diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/calendar/base/backend/icaljs/calRecurrenceRule.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/calendar/base/backend/icaljs/calRecurrenceRule.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/calendar/base/backend/icaljs/calRecurrenceRule.js 2015-01-25 22:23:55.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/calendar/base/backend/icaljs/calRecurrenceRule.js 2015-02-03 14:31:10.000000000 +0000 @@ -138,12 +138,46 @@ set weekStart(val) this.innerObject.wkst = val + 1, getComponent: function(aType, aCount) { - return this.innerObject.getComponent(aType, aCount) - .map(ICAL.Recur.icalDayToNumericDay); + let values = this.innerObject.getComponent(aType); + if (aType == "BYDAY") { + // BYDAY values are alphanumeric: SU, MO, TU, etc.. + for (let i = 0; i < values.length; i++) { + let match = /^([+-])?(5[0-3]|[1-4][0-9]|[1-9])?(SU|MO|TU|WE|TH|FR|SA)$/.exec(values[i]); + if (!match) { + cal.ERROR("Malformed BYDAY rule\n" + cal.STACK(10)); + return []; + } + values[i] = ICAL.Recur.icalDayToNumericDay(match[3]); + if (match[2]) { + // match[2] is the week number for this value. + values[i] += 8 * match[2]; + } + if (match[1] == '-') { + // Week numbers are counted back from the end of the period. + values[i] *= -1; + } + } + } + + if (aCount) aCount.value = values.length; + return values; }, setComponent: function(aType, aCount, aValues) { - let values = aValues.map(ICAL.Recur.numericDayToIcalDay); + let values = aValues; + if (aType == "BYDAY") { + // BYDAY values are alphanumeric: SU, MO, TU, etc.. + for (let i = 0; i < values.length; i++) { + let absValue = Math.abs(values[i]); + if (absValue > 7) { + let ordinal = Math.trunc(values[i] / 8); + let day = ICAL.Recur.numericDayToIcalDay(absValue % 8); + values[i] = ordinal + day; + } else { + values[i] = ICAL.Recur.numericDayToIcalDay(values[i]); + } + } + } this.innerObject.setComponent(aType, values); } }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/calendar/base/content/preferences/alarms.xul thunderbird-trunk-38.0~a1~hg20150202r17443.227207/calendar/base/content/preferences/alarms.xul --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/calendar/base/content/preferences/alarms.xul 2015-01-25 22:23:55.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/calendar/base/content/preferences/alarms.xul 2015-02-03 14:31:10.000000000 +0000 @@ -63,33 +63,45 @@ - - - - - - - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/aboutTabCrashed.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/aboutTabCrashed.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/aboutTabCrashed.css 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/aboutTabCrashed.css 2015-02-03 14:33:13.000000000 +0000 @@ -0,0 +1,8 @@ +/* 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/. */ + +html:not(.crashDumpSubmitted) #reportSent, +html:not(.crashDumpAvailable) #report-box { + display: none; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/aboutTabCrashed.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/aboutTabCrashed.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/aboutTabCrashed.js 2015-01-25 22:24:22.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/aboutTabCrashed.js 2015-02-03 14:33:13.000000000 +0000 @@ -12,21 +12,35 @@ document.title = parseQueryString(); -addEventListener("DOMContentLoaded", () => { - let tryAgain = document.getElementById("tryAgain"); - let sendCrashReport = document.getElementById("checkSendReport"); - - tryAgain.addEventListener("click", () => { - let event = new CustomEvent("AboutTabCrashedTryAgain", { - bubbles: true, - detail: { - sendCrashReport: sendCrashReport.checked, - }, - }); +function shouldSendReport() { + if (!document.documentElement.classList.contains("crashDumpAvailable")) + return false; + return document.getElementById("sendReport").checked; +} - document.dispatchEvent(event); +function sendEvent(message) { + let event = new CustomEvent("AboutTabCrashedMessage", { + bubbles: true, + detail: { + message, + sendCrashReport: shouldSendReport(), + }, }); -}); + + document.dispatchEvent(event); +} + +function closeTab() { + sendEvent("closeTab"); +} + +function restoreTab() { + sendEvent("restoreTab"); +} + +function restoreAll() { + sendEvent("restoreAll"); +} // Error pages are loaded as LOAD_BACKGROUND, so they don't get load events. var event = new CustomEvent("AboutTabCrashedLoad", {bubbles:true}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/aboutTabCrashed.xhtml thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/aboutTabCrashed.xhtml --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/aboutTabCrashed.xhtml 2015-01-25 22:24:22.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/aboutTabCrashed.xhtml 2015-02-03 14:33:13.000000000 +0000 @@ -12,12 +12,11 @@ %globalDTD; - - %browserDTD; %brandDTD; - + + %tabCrashedDTD; ]> @@ -25,6 +24,8 @@ + @@ -36,12 +37,19 @@

&tabCrashed.message;

- - + +
+

&tabCrashed.reportSent;

+
- + + +
diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/browser.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/browser.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/browser.css 2015-01-25 22:24:22.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/browser.css 2015-02-03 14:33:14.000000000 +0000 @@ -1012,8 +1012,8 @@ } /* Combobox dropdown renderer */ -#ContentSelectDropdown { - -moz-binding: url("chrome://global/content/bindings/popup.xml#popup-scrollbars"); +#ContentSelectDropdown > menupopup { + max-height: 350px; } .contentSelectDropdown-optgroup { @@ -1192,6 +1192,10 @@ max-width: 10em; } +#main-window[customizing=true] #PanelUI-update-status { + display: none; +} + /* UI Tour */ @keyframes uitour-wobble { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/browser-data-submission-info-bar.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/browser-data-submission-info-bar.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/browser-data-submission-info-bar.js 2015-01-25 22:24:22.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/browser-data-submission-info-bar.js 2015-02-03 14:33:14.000000000 +0000 @@ -25,13 +25,11 @@ }, init: function() { - window.addEventListener("unload", function onUnload() { - window.removeEventListener("unload", onUnload, false); - + window.addEventListener("unload", () => { for (let o of this._OBSERVERS) { Services.obs.removeObserver(this, o); } - }.bind(this), false); + }, false); for (let o of this._OBSERVERS) { Services.obs.addObserver(this, o, true); @@ -61,10 +59,10 @@ label: gNavigatorBundle.getString("dataReportingNotification.button.label"), accessKey: gNavigatorBundle.getString("dataReportingNotification.button.accessKey"), popup: null, - callback: function () { + callback: () => { this._actionTaken = true; window.openAdvancedPreferences("dataChoicesTab"); - }.bind(this), + }, }]; this._log.info("Creating data reporting policy notification."); @@ -74,11 +72,11 @@ null, this._notificationBox.PRIORITY_INFO_HIGH, buttons, - function onEvent(event) { + event => { if (event == "removed") { Services.obs.notifyObservers(null, "datareporting:notify-data-policy:close", null); } - }.bind(this) + } ); // It is important to defer calling onUserNotifyComplete() until we're // actually sure the notification was displayed. If we ever called diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/browser-gestureSupport.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/browser-gestureSupport.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/browser-gestureSupport.js 2015-01-25 22:24:22.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/browser-gestureSupport.js 2015-02-03 14:33:14.000000000 +0000 @@ -2,8 +2,6 @@ # 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/. -Cu.import("resource://gre/modules/TelemetryStopwatch.jsm", this); - // Simple gestures support // // As per bug #412486, web content must not be allowed to receive any @@ -36,8 +34,9 @@ let addRemove = aAddListener ? window.addEventListener : window.removeEventListener; - gestureEvents.forEach(function (event) addRemove("Moz" + event, this, true), - this); + for (let event of gestureEvents) { + addRemove("Moz" + event, this, true); + } }, /** @@ -285,10 +284,10 @@ // "meta" is preferred over "ctrl" when both buttons are pressed (and a // command for both don't exist) let keyCombos = []; - ["shift", "alt", "ctrl", "meta"].forEach(function (key) { + for (let key of ["shift", "alt", "ctrl", "meta"]) { if (aEvent[key + "Key"]) keyCombos.push(key); - }); + } // Try each combination of key presses in decreasing order for commands for (let subCombo of this._power(keyCombos)) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/browser.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/browser.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/browser.js 2015-01-25 22:24:22.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/browser.js 2015-02-03 14:33:14.000000000 +0000 @@ -271,6 +271,12 @@ return new tmp.PageMenuParent(); }); +function* browserWindows() { + let windows = Services.wm.getEnumerator("navigator:browser"); + while (windows.hasMoreElements()) + yield windows.getNext(); +} + /** * We can avoid adding multiple load event listeners and save some time by adding * one listener that calls all real handlers. @@ -1116,7 +1122,7 @@ #endif }, false, true); - gBrowser.addEventListener("AboutTabCrashedTryAgain", function(event) { + gBrowser.addEventListener("AboutTabCrashedMessage", function(event) { let ownerDoc = event.originalTarget; if (!ownerDoc.documentURI.startsWith("about:tabcrashed")) { @@ -1134,8 +1140,23 @@ TabCrashReporter.submitCrashReport(browser); } #endif + let tab = gBrowser.getTabForBrowser(browser); - SessionStore.reviveCrashedTab(tab); + switch (event.detail.message) { + case "closeTab": + gBrowser.removeTab(tab, { animate: true }); + break; + case "restoreTab": + SessionStore.reviveCrashedTab(tab); + break; + case "restoreAll": + for (let browserWin of browserWindows()) { + for (let tab of window.gBrowser.tabs) { + SessionStore.reviveCrashedTab(tab); + } + } + break; + } }, false, true); let uriToLoad = this._getUriToLoad(); @@ -3760,13 +3781,14 @@ let item = document.createElement("menuitem"); let entry = sessionHistory.getEntryAtIndex(j, false); let uri = entry.URI.spec; + let uriCopy = BrowserUtils.makeURI(uri); item.setAttribute("uri", uri); item.setAttribute("label", entry.title || uri); item.setAttribute("index", j); if (j != index) { - PlacesUtils.favicons.getFaviconURLForPage(entry.URI, function (aURI) { + PlacesUtils.favicons.getFaviconURLForPage(uriCopy, function (aURI) { if (aURI) { let iconURL = PlacesUtils.favicons.getFaviconLinkForIcon(aURI).spec; iconURL = PlacesUtils.getImageURLForResolution(window, iconURL); @@ -3872,7 +3894,10 @@ } if (options && options.remote) { - let omtcEnabled = gPrefService.getBoolPref("layers.offmainthreadcomposition.enabled"); + // If we're using remote tabs by default, then OMTC will be force-enabled, + // despite the preference returning as false. + let omtcEnabled = gPrefService.getBoolPref("layers.offmainthreadcomposition.enabled") + || Services.appinfo.browserTabsRemoteAutostart; if (!omtcEnabled) { alert("To use out-of-process tabs, you must set the layers.offmainthreadcomposition.enabled preference and restart. Opening a normal window instead."); } else { @@ -4761,18 +4786,22 @@ else aWhere = gPrefService.getIntPref("browser.link.open_newwindow"); } + let isPrivate = PrivateBrowsingUtils.isWindowPrivate(aOpener || window); switch (aWhere) { case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW : // FIXME: Bug 408379. So how come this doesn't send the // referrer like the other loads do? var url = aURI ? aURI.spec : "about:blank"; + let features = "all,dialog=no"; + if (isPrivate) { + features += ",private"; + } // Pass all params to openDialog to ensure that "url" isn't passed through // loadOneOrMoreURIs, which splits based on "|" - newWindow = openDialog(getBrowserURL(), "_blank", "all,dialog=no", url, null, null, null); + newWindow = openDialog(getBrowserURL(), "_blank", features, url, null, null, null); break; case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB : let referrer = aOpener ? makeURI(aOpener.location.href) : null; - let isPrivate = PrivateBrowsingUtils.isWindowPrivate(aOpener || window); let browser = this._openURIInNewTab(aURI, referrer, isPrivate, isExternal); if (browser) newWindow = browser.contentWindow; @@ -6463,11 +6492,9 @@ return gBrowser.warnAboutClosingTabs(gBrowser.closingTabsEnum.ALL); // Figure out if there's at least one other browser window around. - let e = Services.wm.getEnumerator("navigator:browser"); let otherPBWindowExists = false; let nonPopupPresent = false; - while (e.hasMoreElements()) { - let win = e.getNext(); + for (let win of browserWindows()) { if (!win.closed && win != window) { if (isPBWindow && PrivateBrowsingUtils.isWindowPrivate(win)) otherPBWindowExists = true; @@ -7566,9 +7593,7 @@ if (isBrowserWindow && switchIfURIInWindow(window)) return true; - let winEnum = Services.wm.getEnumerator("navigator:browser"); - while (winEnum.hasMoreElements()) { - let browserWin = winEnum.getNext(); + for (let browserWin of browserWindows()) { // Skip closed (but not yet destroyed) windows, // and the current window (which was checked earlier). if (browserWin.closed || browserWin == window) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/browser-thumbnails.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/browser-thumbnails.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/browser-thumbnails.js 2015-01-25 22:24:22.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/browser-thumbnails.js 2015-02-03 14:33:14.000000000 +0000 @@ -4,8 +4,6 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #endif -Cu.import("resource://gre/modules/NewTabUtils.jsm"); - /** * Keeps thumbnails of open web pages up-to-date. */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/browser.xul thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/browser.xul --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/browser.xul 2015-01-25 22:24:22.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/browser.xul 2015-02-03 14:33:14.000000000 +0000 @@ -146,8 +146,13 @@
{ + // Do nothing if there is no article, or if the content window has been destroyed. + if (article === null || content === null) { + return; + } + // The loaded page may have changed while we were parsing the document. // Make sure we've got the current one. let currentURL = Services.io.newURI(content.document.documentURI, null, null).specIgnoringRef; - - // Do nothing if there's no article or the page in this tab has changed. - if (article == null || (article.url != currentURL)) { + if (article.url !== currentURL) { return; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/nsContextMenu.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/nsContextMenu.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/nsContextMenu.js 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/nsContextMenu.js 2015-02-03 14:33:14.000000000 +0000 @@ -596,6 +596,7 @@ // gContextMenuContentData instead. if (this.isRemote) { this.browser = gContextMenuContentData.browser; + this.principal = gContextMenuContentData.principal; } else { editFlags = SpellCheckHelper.isEditable(this.target, window); this.browser = this.target.ownerDocument.defaultView @@ -603,6 +604,7 @@ .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIDocShell) .chromeEventHandler; + this.principal = this.target.ownerDocument.nodePrincipal; } this.onSocial = !!this.browser.getAttribute("origin"); @@ -834,18 +836,6 @@ this.linkProtocol == "snews" ); }, - _unremotePrincipal: function(aRemotePrincipal) { - if (this.isRemote) { - return Cc["@mozilla.org/scriptsecuritymanager;1"] - .getService(Ci.nsIScriptSecurityManager) - .getAppCodebasePrincipal(aRemotePrincipal.URI, - aRemotePrincipal.appId, - aRemotePrincipal.isInBrowserElement); - } - - return aRemotePrincipal; - }, - _isSpellCheckEnabled: function(aNode) { // We can always force-enable spellchecking on textboxes if (this.isTargetATextBox(aNode)) { @@ -875,14 +865,14 @@ // Open linked-to URL in a new window. openLink : function () { var doc = this.target.ownerDocument; - urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal)); + urlSecurityCheck(this.linkURL, this.principal); openLinkIn(this.linkURL, "window", this._openLinkInParameters(doc)); }, // Open linked-to URL in a new private window. openLinkInPrivateWindow : function () { var doc = this.target.ownerDocument; - urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal)); + urlSecurityCheck(this.linkURL, this.principal); openLinkIn(this.linkURL, "window", this._openLinkInParameters(doc, { private: true })); }, @@ -890,7 +880,7 @@ // Open linked-to URL in a new tab. openLinkInTab: function() { var doc = this.target.ownerDocument; - urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal)); + urlSecurityCheck(this.linkURL, this.principal); var referrerURI = doc.documentURIObject; // if the mixedContentChannel is present and the referring URI passes @@ -917,7 +907,7 @@ // open URL in current tab openLinkInCurrent: function() { var doc = this.target.ownerDocument; - urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal)); + urlSecurityCheck(this.linkURL, this.principal); openLinkIn(this.linkURL, "current", this._openLinkInParameters(doc)); }, @@ -1125,8 +1115,7 @@ return; var doc = this.target.ownerDocument; - urlSecurityCheck(this.target.currentURI.spec, - this._unremotePrincipal(doc.nodePrincipal)); + urlSecurityCheck(this.target.currentURI.spec, this.principal); // Confirm since it's annoying if you hit this accidentally. const kDesktopBackgroundURL = @@ -1303,7 +1292,7 @@ linkText = this.focusedWindow.getSelection().toString().trim(); else linkText = this.linkText(); - urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal)); + urlSecurityCheck(this.linkURL, this.principal); this.saveHelper(this.linkURL, linkText, null, true, doc); }, @@ -1323,14 +1312,12 @@ true, false, doc.documentURIObject, doc); } else if (this.onImage) { - urlSecurityCheck(this.mediaURL, - this._unremotePrincipal(doc.nodePrincipal)); + urlSecurityCheck(this.mediaURL, this.principal); saveImageURL(this.mediaURL, null, "SaveImageTitle", false, false, doc.documentURIObject, doc); } else if (this.onVideo || this.onAudio) { - urlSecurityCheck(this.mediaURL, - this._unremotePrincipal(doc.nodePrincipal)); + urlSecurityCheck(this.mediaURL, this.principal); var dialogTitle = this.onVideo ? "SaveVideoTitle" : "SaveAudioTitle"; this.saveHelper(this.mediaURL, null, dialogTitle, false, doc); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/sanitize.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/sanitize.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/sanitize.js 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/sanitize.js 2015-02-03 14:33:14.000000000 +0000 @@ -17,6 +17,8 @@ "resource://gre/modules/Task.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon", "resource:///modules/DownloadsCommon.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch", + "resource://gre/modules/TelemetryStopwatch.jsm"); function Sanitizer() {} Sanitizer.prototype = { @@ -92,6 +94,8 @@ return deferred.promise; } + TelemetryStopwatch.start("FX_SANITIZE_TOTAL"); + // Cache the range of times to clear if (this.ignoreTimespan) var range = null; // If we ignore timespan, clear everything @@ -101,6 +105,7 @@ let itemCount = Object.keys(itemsToClear).length; let onItemComplete = function() { if (!--itemCount) { + TelemetryStopwatch.finish("FX_SANITIZE_TOTAL"); seenError ? deferred.reject() : deferred.resolve(); } }; @@ -146,6 +151,8 @@ cache: { clear: function () { + TelemetryStopwatch.start("FX_SANITIZE_CACHE"); + var cache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]. getService(Ci.nsICacheStorageService); try { @@ -159,6 +166,8 @@ try { imageCache.clearCache(false); // true=chrome, false=content } catch(er) {} + + TelemetryStopwatch.finish("FX_SANITIZE_CACHE"); }, get canClear() @@ -170,6 +179,8 @@ cookies: { clear: function () { + TelemetryStopwatch.start("FX_SANITIZE_COOKIES"); + var cookieMgr = Components.classes["@mozilla.org/cookiemanager;1"] .getService(Ci.nsICookieManager); if (this.range) { @@ -217,6 +228,8 @@ } } } + + TelemetryStopwatch.finish("FX_SANITIZE_COOKIES"); }, get canClear() @@ -228,8 +241,10 @@ offlineApps: { clear: function () { + TelemetryStopwatch.start("FX_SANITIZE_OFFLINEAPPS"); Components.utils.import("resource:///modules/offlineAppCache.jsm"); OfflineAppCacheHelper.clear(); + TelemetryStopwatch.finish("FX_SANITIZE_OFFLINEAPPS"); }, get canClear() @@ -241,6 +256,8 @@ history: { clear: function () { + TelemetryStopwatch.start("FX_SANITIZE_HISTORY"); + if (this.range) PlacesUtils.history.removeVisitsByTimeframe(this.range[0], this.range[1]); else @@ -259,6 +276,8 @@ .getService(Components.interfaces.nsINetworkPredictor); predictor.reset(); } catch (e) { } + + TelemetryStopwatch.finish("FX_SANITIZE_HISTORY"); }, get canClear() @@ -272,6 +291,8 @@ formdata: { clear: function () { + TelemetryStopwatch.start("FX_SANITIZE_FORMDATA"); + // Clear undo history of all searchBars var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'] .getService(Components.interfaces.nsIWindowMediator); @@ -296,6 +317,8 @@ [ change.firstUsedStart, change.firstUsedEnd ] = this.range; } FormHistory.update(change); + + TelemetryStopwatch.finish("FX_SANITIZE_FORMDATA"); }, canClear : function(aCallback, aArg) @@ -342,6 +365,7 @@ downloads: { clear: function () { + TelemetryStopwatch.start("FX_SANITIZE_DOWNLOADS"); Task.spawn(function () { let filterByTime = null; if (this.range) { @@ -355,7 +379,11 @@ // Clear all completed/cancelled downloads let list = yield Downloads.getList(Downloads.ALL); list.removeFinished(filterByTime); - }.bind(this)).then(null, Components.utils.reportError); + TelemetryStopwatch.finish("FX_SANITIZE_DOWNLOADS"); + }.bind(this)).then(null, error => { + TelemetryStopwatch.finish("FX_SANITIZE_DOWNLOADS"); + Components.utils.reportError(error); + }); }, canClear : function(aCallback, aArg) @@ -368,10 +396,12 @@ passwords: { clear: function () { + TelemetryStopwatch.start("FX_SANITIZE_PASSWORDS"); var pwmgr = Components.classes["@mozilla.org/login-manager;1"] .getService(Components.interfaces.nsILoginManager); // Passwords are timeless, and don't respect the timeSpan setting pwmgr.removeAllLogins(); + TelemetryStopwatch.finish("FX_SANITIZE_PASSWORDS"); }, get canClear() @@ -386,6 +416,8 @@ sessions: { clear: function () { + TelemetryStopwatch.start("FX_SANITIZE_SESSIONS"); + // clear all auth tokens var sdr = Components.classes["@mozilla.org/security/sdr;1"] .getService(Components.interfaces.nsISecretDecoderRing); @@ -395,6 +427,8 @@ var os = Components.classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); os.notifyObservers(null, "net:clear-active-logins", null); + + TelemetryStopwatch.finish("FX_SANITIZE_SESSIONS"); }, get canClear() @@ -406,6 +440,8 @@ siteSettings: { clear: function () { + TelemetryStopwatch.start("FX_SANITIZE_SITESETTINGS"); + // Clear site-specific permissions like "Allow this site to open popups" // we ignore the "end" range and hope it is now() - none of the // interfaces used here support a true range anyway. @@ -444,6 +480,8 @@ var sss = Cc["@mozilla.org/ssservice;1"] .getService(Ci.nsISiteSecurityService); sss.clearAll(); + + TelemetryStopwatch.finish("FX_SANITIZE_SITESETTINGS"); }, get canClear() @@ -516,6 +554,8 @@ // If/once we get here, we should actually be able to close all windows. + TelemetryStopwatch.start("FX_SANITIZE_OPENWINDOWS"); + // First create a new window. We do this first so that on non-mac, we don't // accidentally close the app by closing all the windows. let handler = Cc["@mozilla.org/browser/clh;1"].getService(Ci.nsIBrowserHandler); @@ -555,8 +595,10 @@ #endif newWindowOpened = true; // If we're the last thing to happen, invoke callback. - if (numWindowsClosing == 0) + if (numWindowsClosing == 0) { + TelemetryStopwatch.finish("FX_SANITIZE_OPENWINDOWS"); aCallback(); + } } let numWindowsClosing = windowList.length; @@ -565,8 +607,10 @@ if (numWindowsClosing == 0) { Services.obs.removeObserver(onWindowClosed, "xul-window-destroyed"); // If we're the last thing to happen, invoke callback. - if (newWindowOpened) + if (newWindowOpened) { + TelemetryStopwatch.finish("FX_SANITIZE_OPENWINDOWS"); aCallback(); + } } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/tabbrowser.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/tabbrowser.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/tabbrowser.css 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/tabbrowser.css 2015-02-03 14:33:14.000000000 +0000 @@ -51,9 +51,10 @@ } } -.tab-icon-image:not([src]):not([pinned]), +.tab-icon-image:not([src]):not([pinned]):not([crashed]), .tab-throbber:not([busy]), -.tab-throbber[busy] + .tab-icon-image { +.tab-icon-image[busy], +.tab-icon-overlay[busy] { display: none; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/tabbrowser.xml thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/tabbrowser.xml --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/tabbrowser.xml 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/tabbrowser.xml 2015-02-03 14:33:14.000000000 +0000 @@ -30,7 +30,7 @@ + xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,selectmenulist"/> @@ -664,9 +664,13 @@ // We need to add 2 because loadURIWithFlags may have // cancelled a pending load which would have cleared // its anchor scroll detection temporary increment. - if (aWebProgress.isTopLevel) + if (aWebProgress.isTopLevel) { this.mBrowser.userTypedClear += 2; + // If the browser is loading it must not be crashed anymore + this.mTab.removeAttribute("crashed"); + } + if (this._shouldShowProgress(aRequest)) { if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) { this.mTab.setAttribute("busy", "true"); @@ -1484,6 +1488,10 @@ if (aShouldBeRemote) { tab.setAttribute("remote", "true"); + // Switching the browser to be remote will connect to a new child + // process so the browser can no longer be considered to be + // crashed. + tab.removeAttribute("crashed"); } else { tab.removeAttribute("remote"); aBrowser.messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: tab.pinned }) @@ -1600,8 +1608,8 @@ if (!isPreloadBrowser && this.hasAttribute("autocompletepopup")) b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup")); - if (this.hasAttribute("selectpopup")) - b.setAttribute("selectpopup", this.getAttribute("selectpopup")); + if (this.hasAttribute("selectmenulist")) + b.setAttribute("selectmenulist", this.getAttribute("selectmenulist")); b.setAttribute("autoscrollpopup", this._autoScrollPopup.id); @@ -3171,6 +3179,7 @@ browser: browser, editFlags: aMessage.data.editFlags, spellInfo: spellInfo, + principal: aMessage.data.principal, customMenuItems: aMessage.data.customMenuItems, addonInfo: aMessage.data.addonInfo }; let popup = browser.ownerDocument.getElementById("contentAreaContextMenu"); @@ -3578,6 +3587,7 @@ browser.docShell.displayLoadError(Cr.NS_ERROR_CONTENT_CRASHED, uri, null); browser.removeAttribute("crashedPageTitle"); let tab = this.getTabForBrowser(browser); + tab.setAttribute("crashed", true); this.setIcon(tab, icon); ]]> @@ -4979,11 +4989,14 @@ class="tab-throbber" role="presentation" layer="true" /> - + { resolveDialogPromise = resolve; }); + document.getAnonymousElementByAttribute(firstTab, "anonid", "close-button").click(); + yield firstDialogShownPromise; + info("Got initial dialog, now trying again"); + expectingDialog = true; + wantToClose = true; + resolveDialogPromise = null; + document.getAnonymousElementByAttribute(firstTab, "anonid", "close-button").click(); + yield windowClosedPromise; + ok(!expectingDialog, "There should have been a dialog."); + ok(newWin.closed, "Window should be closed."); }); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/browser_devices_get_user_media_about_urls.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/browser_devices_get_user_media_about_urls.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/browser_devices_get_user_media_about_urls.js 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/browser_devices_get_user_media_about_urls.js 2015-02-03 14:33:14.000000000 +0000 @@ -22,6 +22,37 @@ var gTab; +// Taken from dom/media/tests/mochitest/head.js +function isMacOSX10_6orOlder() { + var is106orOlder = false; + + if (navigator.platform.indexOf("Mac") == 0) { + var version = Cc["@mozilla.org/system-info;1"] + .getService(Ci.nsIPropertyBag2) + .getProperty("version"); + // the next line is correct: Mac OS 10.6 corresponds to Darwin version 10.x ! + // Mac OS 10.7 is Darwin version 11.x. the |version| string we've got here + // is the Darwin version. + is106orOlder = (parseFloat(version) < 11.0); + } + return is106orOlder; +} + +// Screensharing is disabled on older platforms (WinXP and Mac 10.6). +function isOldPlatform() { + const isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1; + if (isMacOSX10_6orOlder() || isWinXP) { + info(true, "Screensharing disabled for OSX10.6 and WinXP"); + return true; + } + return false; +} + +// Linux prompts aren't working for screensharing. +function isLinux() { + return navigator.platform.indexOf("Linux") != -1; +} + var gObservedTopics = {}; function observer(aSubject, aTopic, aData) { if (!(aTopic in gObservedTopics)) @@ -97,16 +128,41 @@ gObservedTopics = {}; } +function promiseMessage(aMessage, aAction) { + let deferred = Promise.defer(); + + content.addEventListener("message", function messageListener(event) { + content.removeEventListener("message", messageListener); + is(event.data, aMessage, "received " + aMessage); + if (event.data == aMessage) + deferred.resolve(); + else + deferred.reject(); + }); + + if (aAction) + aAction(); + + return deferred.promise; +} + function getMediaCaptureState() { let hasVideo = {}; let hasAudio = {}; - MediaManagerService.mediaCaptureWindowState(content, hasVideo, hasAudio); + let hasScreenShare = {}; + let hasWindowShare = {}; + MediaManagerService.mediaCaptureWindowState(content, hasVideo, hasAudio, + hasScreenShare, hasWindowShare); if (hasVideo.value && hasAudio.value) return "CameraAndMicrophone"; if (hasVideo.value) return "Camera"; if (hasAudio.value) return "Microphone"; + if (hasScreenShare) + return "Screen"; + if (hasWindowShare) + return "Window"; return "none"; } @@ -173,6 +229,7 @@ Services.prefs.setCharPref(PREF_LOOP_CSP, originalLoopCsp); }); +const permissionError = "error: PermissionDeniedError: The user did not grant permission for the operation."; let gTests = [ @@ -208,6 +265,46 @@ }, { + desc: "getUserMedia about:loopconversation should prompt for window sharing", + run: function checkShareScreenLoop() { + if (isOldPlatform() || isLinux()) { + return; + } + + Services.prefs.setCharPref(PREF_LOOP_CSP, "default-src 'unsafe-inline'"); + + let classID = Cc["@mozilla.org/uuid-generator;1"] + .getService(Ci.nsIUUIDGenerator).generateUUID(); + registrar.registerFactory(classID, "", + "@mozilla.org/network/protocol/about;1?what=loopconversation", + factory); + + yield loadPage("about:loopconversation"); + + yield promiseObserverCalled("getUserMedia:request", () => { + info("requesting screen"); + content.wrappedJSObject.requestDevice(false, true, "window"); + }); + // Wait for the devices to actually be captured and running before + // proceeding. + yield promisePopupNotification("webRTC-shareDevices"); + + isnot(getMediaCaptureState(), "Window", + "expected camera and microphone not to be shared"); + + yield promiseMessage(permissionError, () => { + PopupNotifications.panel.firstChild.button.click(); + }); + + expectObserverCalled("getUserMedia:response:deny"); + expectObserverCalled("recording-window-ended"); + + registrar.unregisterFactory(classID, factory); + Services.prefs.setCharPref(PREF_LOOP_CSP, originalLoopCsp); + } +}, + +{ desc: "getUserMedia about:evil should prompt", run: function checkAudioVideoNonLoop() { let classID = Cc["@mozilla.org/uuid-generator;1"] @@ -236,6 +333,8 @@ waitForExplicitFinish(); Services.prefs.setBoolPref(PREF_PERMISSION_FAKE, true); + // Ensure this is always true + Services.prefs.setBoolPref("media.getusermedia.screensharing.enabled", true); gTab = gBrowser.addTab(); gBrowser.selectedTab = gTab; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/browser_devices_get_user_media_in_frame.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/browser_devices_get_user_media_in_frame.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/browser_devices_get_user_media_in_frame.js 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/browser_devices_get_user_media_in_frame.js 2015-02-03 14:33:14.000000000 +0000 @@ -319,21 +319,14 @@ yield checkSharingUI({video: true, audio: true}); info("reloading the frame"); - let deferred = Promise.defer(); - let browser = gBrowser.selectedBrowser; - browser.addEventListener("load", function onload() { - browser.removeEventListener("load", onload, true); - deferred.resolve(); - }, true); - global.location.reload(); - yield deferred.promise; + yield promiseObserverCalled("recording-device-events", + () => { global.location.reload(); }); yield promiseNoPopupNotification("webRTC-sharingDevices"); - if (gObservedTopics["recording-device-events"] == 2) { + if (gObservedTopics["recording-device-events"] == 1) { todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719"); - --gObservedTopics["recording-device-events"]; + gObservedTopics["recording-device-events"] = 0; } - expectObserverCalled("recording-device-events"); expectObserverCalled("recording-window-ended"); expectNoObserverCalled(); yield checkNotSharing(); @@ -352,18 +345,11 @@ checkDeviceSelectors(true, true); info("reloading the frame"); - let deferred = Promise.defer(); - let browser = gBrowser.selectedBrowser; - browser.addEventListener("load", function onload() { - browser.removeEventListener("load", onload, true); - deferred.resolve(); - }, true); - global.location.reload(); - yield deferred.promise; + yield promiseObserverCalled("recording-window-ended", + () => { global.location.reload(); }); yield promiseNoPopupNotification("webRTC-shareDevices"); - expectObserverCalled("recording-window-ended"); expectNoObserverCalled(); yield checkNotSharing(); } @@ -413,22 +399,15 @@ expectNoObserverCalled(); info("reloading the second frame"); - let deferred = Promise.defer(); - let browser = gBrowser.selectedBrowser; - browser.addEventListener("load", function onload() { - browser.removeEventListener("load", onload, true); - deferred.resolve(); - }, true); - g2.location.reload(); - yield deferred.promise; + yield promiseObserverCalled("recording-device-events", + () => { g2.location.reload(); }); yield checkSharingUI({video: false, audio: true}); expectObserverCalled("recording-window-ended"); - if (gObservedTopics["recording-device-events"] == 2) { + if (gObservedTopics["recording-device-events"] == 1) { todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719"); - --gObservedTopics["recording-device-events"]; + gObservedTopics["recording-device-events"] = 0; } - expectObserverCalled("recording-device-events"); expectNoObserverCalled(); yield closeStream(g1); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/browser_devices_get_user_media.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/browser_devices_get_user_media.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/browser_devices_get_user_media.js 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/browser_devices_get_user_media.js 2015-02-03 14:33:14.000000000 +0000 @@ -482,21 +482,14 @@ yield promiseNotificationShown(PopupNotifications.getNotification("webRTC-sharingDevices")); info("reloading the web page"); - let deferred = Promise.defer(); - let browser = gBrowser.selectedBrowser; - browser.addEventListener("load", function onload() { - browser.removeEventListener("load", onload, true); - deferred.resolve(); - }, true); - content.location.reload(); - yield deferred.promise; + yield promiseObserverCalled("recording-device-events", + () => { content.location.reload(); }); yield promiseNoPopupNotification("webRTC-sharingDevices"); - if (gObservedTopics["recording-device-events"] == 2) { + if (gObservedTopics["recording-device-events"] == 1) { todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719"); - --gObservedTopics["recording-device-events"]; + gObservedTopics["recording-device-events"] = 0; } - expectObserverCalled("recording-device-events"); expectObserverCalled("recording-window-ended"); expectNoObserverCalled(); yield checkNotSharing(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/browser.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/browser.ini --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/browser.ini 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/browser.ini 2015-02-03 14:33:14.000000000 +0000 @@ -217,7 +217,7 @@ [browser_bug565575.js] skip-if = e10s [browser_bug565667.js] -run-if = toolkit == "cocoa" +skip-if = toolkit != "cocoa" [browser_bug567306.js] skip-if = e10s # Bug XXX - Needs some massaging to run in e10s [browser_bug575561.js] @@ -297,7 +297,7 @@ [browser_ctrlTab.js] [browser_customize_popupNotification.js] [browser_datareporting_notification.js] -run-if = datareporting +skip-if = !datareporting [browser_devedition.js] [browser_devices_get_user_media.js] skip-if = buildapp == 'mulet' || (os == "linux" && debug) || e10s # linux: bug 976544; e10s: bug 1071623 @@ -487,3 +487,6 @@ [browser_windowactivation.js] [browser_contextmenu_childprocess.js] [browser_bug963945.js] +[browser_readerMode.js] +support-files = + readerModeArticle.html diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/browser_readerMode.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/browser_readerMode.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/browser_readerMode.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/browser_readerMode.js 2015-02-03 14:33:14.000000000 +0000 @@ -0,0 +1,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/. */ + +// Test that the reader mode button appears and works properly on reader-able content. + +const PREF = "reader.parse-on-load.enabled"; + +const TEST_PATH = "http://example.com/browser/browser/base/content/test/general/"; + +let readerButton = document.getElementById("reader-mode-button"); + +add_task(function* () { + registerCleanupFunction(function() { + Services.prefs.clearUserPref(PREF); + while (gBrowser.tabs.length > 1) { + gBrowser.removeCurrentTab(); + } + }); + + // Enable the reader mode button. + Services.prefs.setBoolPref(PREF, true); + + let tab = gBrowser.selectedTab = gBrowser.addTab(); + is_element_hidden(readerButton, "Reader mode button is not present on a new tab"); + + // Point tab to a test page that is reader-able. + let url = TEST_PATH + "readerModeArticle.html"; + yield promiseTabLoadEvent(tab, url); + yield promiseWaitForCondition(() => !readerButton.hidden); + is_element_visible(readerButton, "Reader mode button is present on a reader-able page"); + + readerButton.click(); + yield promiseTabLoadEvent(tab); + + ok(gBrowser.selectedBrowser.currentURI.spec.startsWith("about:reader"), "about:reader loaded after clicking reader mode button"); + is_element_visible(readerButton, "Reader mode button is present on about:reader"); + + readerButton.click(); + yield promiseTabLoadEvent(tab); + is(gBrowser.selectedBrowser.currentURI.spec, url, "Original page loaded after clicking active reader mode button"); + + // Load a new tab that is NOT reader-able. + let newTab = gBrowser.selectedTab = gBrowser.addTab(); + yield promiseTabLoadEvent(newTab, TEST_PATH + "download_page.html"); + yield promiseWaitForCondition(() => readerButton.hidden); + is_element_hidden(readerButton, "Reader mode button is not present on a non-reader-able page"); + + // Switch back to the original tab to make sure reader mode button is still visible. + gBrowser.removeCurrentTab(); + yield promiseWaitForCondition(() => !readerButton.hidden); + is_element_visible(readerButton, "Reader mode button is present on a reader-able page"); +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/browser_remoteTroubleshoot.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/browser_remoteTroubleshoot.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/browser_remoteTroubleshoot.js 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/browser_remoteTroubleshoot.js 2015-02-03 14:33:14.000000000 +0000 @@ -54,6 +54,23 @@ Assert.ok(got.message.extensions, "should have extensions"); Assert.ok(got.message.graphics, "should have graphics"); + // Check we have channel and build ID info: + Assert.equal(got.message.application.buildID, Services.appinfo.appBuildID, + "should have correct build ID"); + + let updateChannel = null; + try { + updateChannel = Cu.import("resource://gre/modules/UpdateChannel.jsm", {}).UpdateChannel.get(); + } catch (ex) {} + if (!updateChannel) { + Assert.ok(!('updateChannel' in got.message.application), + "should not have update channel where not available."); + } else { + Assert.equal(got.message.application.updateChannel, updateChannel, + "should have correct update channel."); + } + + // And check some keys we know we decline to return. Assert.ok(!got.message.modifiedPreferences, "should not have a modifiedPreferences key"); Assert.ok(!got.message.crashes, "should not have crash info"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/browser_searchSuggestionUI.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/browser_searchSuggestionUI.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/browser_searchSuggestionUI.js 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/browser_searchSuggestionUI.js 2015-02-03 14:33:14.000000000 +0000 @@ -192,8 +192,6 @@ let state = yield msg("startComposition", { data: "" }); checkState(state, "", [], -1); - state = yield msg("updateComposition", { data: "x" }); - checkState(state, "", [], -1); state = yield msg("changeComposition", { data: "x", waitForSuggestions: true }); checkState(state, "x", ["xfoo", "xbar"], -1); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/get_user_media.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/get_user_media.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/get_user_media.html 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/get_user_media.html 2015-02-03 14:33:14.000000000 +0000 @@ -24,11 +24,17 @@ var gStream; -function requestDevice(aAudio, aVideo) { +function requestDevice(aAudio, aVideo, aShare) { var opts = {video: aVideo, audio: aAudio}; - if (useFakeStreams) { + if (aShare) { + opts.video = { + mozMediaSource: aShare, + mediaSource: aShare + } + } else if (useFakeStreams) { opts.fake = true; } + window.navigator.mozGetUserMedia(opts, function(stream) { gStream = stream; message("ok"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/readerModeArticle.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/readerModeArticle.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/readerModeArticle.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/readerModeArticle.html 2015-02-03 14:33:14.000000000 +0000 @@ -0,0 +1,16 @@ + + + +Article title + + + +
Site header
+
+

Article title

+

by Jane Doe

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est.

+

Vivamus fermentum semper porta. Nunc diam velit, adipiscing ut tristique vitae, sagittis vel odio. Maecenas convallis ullamcorper ultricies. Curabitur ornare, ligula semper consectetur sagittis, nisi diam iaculis velit, id fringilla sem nunc vel mi. Nam dictum, odio nec pretium volutpat, arcu ante placerat erat, non tristique elit urna et turpis. Quisque mi metus, ornare sit amet fermentum et, tincidunt et orci. Fusce eget orci a orci congue vestibulum. Ut dolor diam, elementum et vestibulum eu, porttitor vel elit. Curabitur venenatis pulvinar tellus gravida ornare. Sed et erat faucibus nunc euismod ultricies ut id justo. Nullam cursus suscipit nisi, et ultrices justo sodales nec. Fusce venenatis facilisis lectus ac semper. Aliquam at massa ipsum. Quisque bibendum purus convallis nulla ultrices ultricies. Nullam aliquam, mi eu aliquam tincidunt, purus velit laoreet tortor, viverra pretium nisi quam vitae mi. Fusce vel volutpat elit. Nam sagittis nisi dui.

+
+ + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/searchSuggestionUI.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/searchSuggestionUI.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/test/general/searchSuggestionUI.js 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/test/general/searchSuggestionUI.js 2015-02-03 14:33:14.000000000 +0000 @@ -28,14 +28,7 @@ }, startComposition: function (arg) { - let data = typeof(arg) == "string" ? arg : arg.data; - content.synthesizeComposition({ type: "compositionstart", data: data }); - ack(); - }, - - updateComposition: function (arg) { - let data = typeof(arg) == "string" ? arg : arg.data; - content.synthesizeComposition({ type: "compositionupdate", data: data }); + content.synthesizeComposition({ type: "compositionstart", data: "" }); ack(); }, @@ -45,7 +38,7 @@ composition: { string: data, clauses: [ - { length: data.length, attr: content.COMPOSITION_ATTR_RAWINPUT } + { length: data.length, attr: content.COMPOSITION_ATTR_RAW_CLAUSE } ] }, caret: { start: data.length, length: 0 } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/urlbarBindings.xml thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/urlbarBindings.xml --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/content/urlbarBindings.xml 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/content/urlbarBindings.xml 2015-02-03 14:33:14.000000000 +0000 @@ -1173,7 +1173,7 @@ // Ensure the panel is wide enough to fit at least 3 engines. minWidth = Math.max(minWidth, ENGINE_WIDTH * 3); } - panel.setAttribute("style", "min-width: " + minWidth + "px"); + panel.style.minWidth = minWidth + "px"; if (!engines.length) return; @@ -1268,11 +1268,14 @@ onSuccess: function(engine) { event.target.hidePopup(); BrowserSearch.searchBar.openSuggestionsPanel(); + }, + onError: function(errorCode) { + Components.utils.reportError("Error adding search engine: " + errorCode); } } Services.search.addEngine(target.getAttribute("uri"), Ci.nsISearchEngine.DATA_XML, - target.getAttribute("src"), false, + target.getAttribute("image"), false, installCallback); } ]]> diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/jar.mn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/jar.mn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/base/jar.mn 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/base/jar.mn 2015-02-03 14:33:14.000000000 +0000 @@ -69,6 +69,7 @@ content/browser/aboutRobots-widget-left.png (content/aboutRobots-widget-left.png) content/browser/aboutSocialError.xhtml (content/aboutSocialError.xhtml) content/browser/aboutProviderDirectory.xhtml (content/aboutProviderDirectory.xhtml) + content/browser/aboutTabCrashed.css (content/aboutTabCrashed.css) content/browser/aboutTabCrashed.js (content/aboutTabCrashed.js) content/browser/aboutTabCrashed.xhtml (content/aboutTabCrashed.xhtml) * content/browser/browser.css (content/browser.css) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/branding/aurora/Makefile.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/branding/aurora/Makefile.in --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/branding/aurora/Makefile.in 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/branding/aurora/Makefile.in 2015-02-03 14:33:14.000000000 +0000 @@ -4,8 +4,6 @@ include $(topsrcdir)/config/config.mk -PREF_JS_EXPORTS = $(srcdir)/pref/firefox-branding.js - # On Windows only do this step for browser, skip for metro. ifeq ($(MOZ_WIDGET_TOOLKIT) $(DIST_SUBDIR),windows browser) BRANDING_FILES := \ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/branding/aurora/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/branding/aurora/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/branding/aurora/moz.build 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/branding/aurora/moz.build 2015-02-03 14:33:15.000000000 +0000 @@ -8,3 +8,8 @@ DIST_SUBDIR = 'browser' export('DIST_SUBDIR') + +JS_PREFERENCE_FILES += [ + 'pref/firefox-branding.js', +] + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/branding/nightly/Makefile.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/branding/nightly/Makefile.in --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/branding/nightly/Makefile.in 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/branding/nightly/Makefile.in 2015-02-03 14:33:15.000000000 +0000 @@ -4,8 +4,6 @@ include $(topsrcdir)/config/config.mk -PREF_JS_EXPORTS = $(srcdir)/pref/firefox-branding.js - # On Windows only do this step for browser, skip for metro. ifeq ($(MOZ_WIDGET_TOOLKIT) $(DIST_SUBDIR),windows browser) BRANDING_FILES := \ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/branding/nightly/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/branding/nightly/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/branding/nightly/moz.build 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/branding/nightly/moz.build 2015-02-03 14:33:15.000000000 +0000 @@ -8,3 +8,8 @@ DIST_SUBDIR = 'browser' export('DIST_SUBDIR') + +JS_PREFERENCE_FILES += [ + 'pref/firefox-branding.js', +] + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/branding/official/Makefile.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/branding/official/Makefile.in --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/branding/official/Makefile.in 2015-01-25 22:24:23.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/branding/official/Makefile.in 2015-02-03 14:33:15.000000000 +0000 @@ -4,8 +4,6 @@ include $(topsrcdir)/config/config.mk -PREF_JS_EXPORTS = $(srcdir)/pref/firefox-branding.js - # On Windows only do this step for browser, skip for metro. ifeq ($(MOZ_WIDGET_TOOLKIT) $(DIST_SUBDIR),windows browser) BRANDING_FILES := \ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/branding/official/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/branding/official/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/branding/official/moz.build 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/branding/official/moz.build 2015-02-03 14:33:16.000000000 +0000 @@ -8,3 +8,8 @@ DIST_SUBDIR = 'browser' export('DIST_SUBDIR') + +JS_PREFERENCE_FILES += [ + 'pref/firefox-branding.js', +] + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/branding/unofficial/Makefile.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/branding/unofficial/Makefile.in --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/branding/unofficial/Makefile.in 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/branding/unofficial/Makefile.in 2015-02-03 14:33:16.000000000 +0000 @@ -4,8 +4,6 @@ include $(topsrcdir)/config/config.mk -PREF_JS_EXPORTS = $(srcdir)/pref/firefox-branding.js - # On Windows only do this step for browser, skip for metro. ifeq ($(MOZ_WIDGET_TOOLKIT) $(DIST_SUBDIR),windows browser) BRANDING_FILES := \ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/branding/unofficial/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/branding/unofficial/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/branding/unofficial/moz.build 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/branding/unofficial/moz.build 2015-02-03 14:33:16.000000000 +0000 @@ -8,3 +8,8 @@ DIST_SUBDIR = 'browser' export('DIST_SUBDIR') + +JS_PREFERENCE_FILES += [ + 'pref/firefox-branding.js', +] + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/customizableui/CustomizableUI.jsm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/customizableui/CustomizableUI.jsm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/customizableui/CustomizableUI.jsm 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/customizableui/CustomizableUI.jsm 2015-02-03 14:33:16.000000000 +0000 @@ -51,13 +51,6 @@ ]; /** - * The method name to use for ES6 iteration. If Symbols are enabled in - * this build, use Symbol.iterator; otherwise "@@iterator". - */ -const JS_HAS_SYMBOLS = typeof Symbol === "function"; -const kIteratorSymbol = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator"; - -/** * The current version. We can use this to auto-add new default widgets as necessary. * (would be const but isn't because of testing purposes) */ @@ -2710,7 +2703,7 @@ * for (let window of CustomizableUI.windows) { ... } */ windows: { - *[kIteratorSymbol]() { + *[Symbol.iterator]() { for (let [window,] of gBuildWindows) yield window; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/feeds/FeedConverter.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/feeds/FeedConverter.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/feeds/FeedConverter.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/feeds/FeedConverter.js 2015-02-03 14:33:16.000000000 +0000 @@ -235,6 +235,11 @@ getService(Ci.nsIIOService); var chromeChannel; + // handling a redirect, hence forwarding the loadInfo from the old channel + // to the newchannel. + var oldChannel = this._request.QueryInterface(Ci.nsIChannel); + var loadInfo = oldChannel.loadInfo; + // If there was no automatic handler, or this was a podcast, // photostream or some other kind of application, show the preview page // if the parser returned a document. @@ -246,12 +251,12 @@ // Now load the actual XUL document. var aboutFeedsURI = ios.newURI("about:feeds", null, null); - chromeChannel = ios.newChannelFromURI(aboutFeedsURI, null); + chromeChannel = ios.newChannelFromURIWithLoadInfo(aboutFeedsURI, loadInfo); chromeChannel.originalURI = result.uri; chromeChannel.owner = Services.scriptSecurityManager.getNoAppCodebasePrincipal(aboutFeedsURI); } else { - chromeChannel = ios.newChannelFromURI(result.uri, null); + chromeChannel = ios.newChannelFromURIWithLoadInfo(result.uri, loadInfo); } chromeChannel.loadGroup = this._request.loadGroup; @@ -546,7 +551,12 @@ newChannel: function GPH_newChannel(aUri) { var inner = aUri.QueryInterface(Ci.nsINestedURI).innerURI; var channel = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService).newChannelFromURI(inner, null); + getService(Ci.nsIIOService).newChannelFromURI2(inner, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); if (channel instanceof Components.interfaces.nsIHttpChannel) // Set this so we know this is supposed to be a feed channel.setRequestHeader("X-Moz-Is-Feed", "1", false); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/feeds/FeedWriter.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/feeds/FeedWriter.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/feeds/FeedWriter.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/feeds/FeedWriter.js 2015-02-03 14:33:16.000000000 +0000 @@ -1128,9 +1128,19 @@ getInterface(Ci.nsIWebNavigation). QueryInterface(Ci.nsIDocShell).currentDocumentChannel; + var nullPrincipal = Cc["@mozilla.org/nullprincipal;1"]. + createInstance(Ci.nsIPrincipal); + var resolvedURI = Cc["@mozilla.org/network/io-service;1"]. getService(Ci.nsIIOService). - newChannel("about:feeds", null, null).URI; + newChannel2("about:feeds", + null, + null, + null, // aLoadingNode + nullPrincipal, + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER).URI; if (resolvedURI.equals(chan.URI)) return chan.originalURI; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/conversation.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/conversation.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/conversation.html 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/conversation.html 2015-02-03 14:33:16.000000000 +0000 @@ -28,7 +28,6 @@ - @@ -41,6 +40,7 @@ + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/js/conversation.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/js/conversation.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/js/conversation.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/js/conversation.js 2015-02-03 14:33:16.000000000 +0000 @@ -42,7 +42,8 @@ conversationStore: React.PropTypes.instanceOf(loop.store.ConversationStore) .isRequired, dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, - roomStore: React.PropTypes.instanceOf(loop.store.RoomStore) + roomStore: React.PropTypes.instanceOf(loop.store.RoomStore), + mozLoop: React.PropTypes.object.isRequired, }, getInitialState: function() { @@ -78,6 +79,7 @@ case "room": { return (React.createElement(DesktopRoomConversationView, { dispatcher: this.props.dispatcher, + mozLoop: this.props.mozLoop, roomStore: this.props.roomStore} )); } @@ -108,7 +110,9 @@ callback(null, navigator.mozLoop.getLoopPref("ot.guid")); }, set: function(guid, callback) { - navigator.mozLoop.setLoopPref("ot.guid", guid); + // See nsIPrefBranch + const PREF_STRING = 32; + navigator.mozLoop.setLoopPref("ot.guid", guid, PREF_STRING); callback(null); } }); @@ -187,7 +191,8 @@ client: client, conversation: conversation, dispatcher: dispatcher, - sdk: window.OT} + sdk: window.OT, + mozLoop: navigator.mozLoop} ), document.querySelector('#main')); dispatcher.dispatch(new sharedActions.GetWindowData({ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/js/conversation.jsx thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/js/conversation.jsx --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/js/conversation.jsx 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/js/conversation.jsx 2015-02-03 14:33:16.000000000 +0000 @@ -42,7 +42,8 @@ conversationStore: React.PropTypes.instanceOf(loop.store.ConversationStore) .isRequired, dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, - roomStore: React.PropTypes.instanceOf(loop.store.RoomStore) + roomStore: React.PropTypes.instanceOf(loop.store.RoomStore), + mozLoop: React.PropTypes.object.isRequired, }, getInitialState: function() { @@ -78,6 +79,7 @@ case "room": { return (); } @@ -108,7 +110,9 @@ callback(null, navigator.mozLoop.getLoopPref("ot.guid")); }, set: function(guid, callback) { - navigator.mozLoop.setLoopPref("ot.guid", guid); + // See nsIPrefBranch + const PREF_STRING = 32; + navigator.mozLoop.setLoopPref("ot.guid", guid, PREF_STRING); callback(null); } }); @@ -188,6 +192,7 @@ conversation={conversation} dispatcher={dispatcher} sdk={window.OT} + mozLoop={navigator.mozLoop} />, document.querySelector('#main')); dispatcher.dispatch(new sharedActions.GetWindowData({ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/js/conversationViews.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/js/conversationViews.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/js/conversationViews.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/js/conversationViews.js 2015-02-03 14:33:16.000000000 +0000 @@ -898,7 +898,7 @@ React.createElement("div", {className: "conversation"}, React.createElement("div", {className: "media nested"}, React.createElement("div", {className: "video_wrapper remote_wrapper"}, - React.createElement("div", {className: "video_inner remote"}) + React.createElement("div", {className: "video_inner remote remote-stream"}) ), React.createElement("div", {className: localStreamClasses}) ), diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/js/conversationViews.jsx thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/js/conversationViews.jsx --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/js/conversationViews.jsx 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/js/conversationViews.jsx 2015-02-03 14:33:16.000000000 +0000 @@ -898,7 +898,7 @@
-
+
diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/js/panel.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/js/panel.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/js/panel.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/js/panel.js 2015-02-03 14:33:16.000000000 +0000 @@ -67,6 +67,7 @@ React.createElement("li", {className: cx({selected: isSelected}), key: i, "data-tab-name": tabName, + title: mozL10n.get(tabName + "_tab_button_tooltip"), onClick: this.handleSelectTab}) ); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/js/panel.jsx thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/js/panel.jsx --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/js/panel.jsx 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/js/panel.jsx 2015-02-03 14:33:16.000000000 +0000 @@ -67,6 +67,7 @@
  • ); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/js/roomViews.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/js/roomViews.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/js/roomViews.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/js/roomViews.js 2015-02-03 14:33:16.000000000 +0000 @@ -14,6 +14,7 @@ var sharedActions = loop.shared.actions; var sharedMixins = loop.shared.mixins; var ROOM_STATES = loop.store.ROOM_STATES; + var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES; var sharedViews = loop.shared.views; /** @@ -169,7 +170,8 @@ ], propTypes: { - dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired + dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, + mozLoop: React.PropTypes.object.isRequired, }, _renderInvitationOverlay: function() { @@ -193,6 +195,7 @@ publishVideo: !this.state.videoMuted }), getLocalElementFunc: this._getElement.bind(this, ".local"), + getScreenShareElementFunc: this._getElement.bind(this, ".screen"), getRemoteElementFunc: this._getElement.bind(this, ".remote") })); } @@ -238,6 +241,11 @@ "room-preview": this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS }); + var screenShareData = { + state: this.state.screenSharingState, + visible: this.props.mozLoop.getLoopPref("screenshare.enabled") + }; + switch(this.state.roomState) { case ROOM_STATES.FAILED: case ROOM_STATES.FULL: { @@ -266,15 +274,18 @@ React.createElement("div", {className: "conversation room-conversation"}, React.createElement("div", {className: "media nested"}, React.createElement("div", {className: "video_wrapper remote_wrapper"}, - React.createElement("div", {className: "video_inner remote"}) + React.createElement("div", {className: "video_inner remote remote-stream"}) ), - React.createElement("div", {className: localStreamClasses}) + React.createElement("div", {className: localStreamClasses}), + React.createElement("div", {className: "screen hide"}) ), React.createElement(sharedViews.ConversationToolbar, { + dispatcher: this.props.dispatcher, video: {enabled: !this.state.videoMuted, visible: true}, audio: {enabled: !this.state.audioMuted, visible: true}, publishStream: this.publishStream, - hangup: this.leaveRoom}) + hangup: this.leaveRoom, + screenShare: screenShareData}) ) ) ) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/js/roomViews.jsx thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/js/roomViews.jsx --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/js/roomViews.jsx 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/js/roomViews.jsx 2015-02-03 14:33:16.000000000 +0000 @@ -14,6 +14,7 @@ var sharedActions = loop.shared.actions; var sharedMixins = loop.shared.mixins; var ROOM_STATES = loop.store.ROOM_STATES; + var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES; var sharedViews = loop.shared.views; /** @@ -169,7 +170,8 @@ ], propTypes: { - dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired + dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, + mozLoop: React.PropTypes.object.isRequired, }, _renderInvitationOverlay: function() { @@ -193,6 +195,7 @@ publishVideo: !this.state.videoMuted }), getLocalElementFunc: this._getElement.bind(this, ".local"), + getScreenShareElementFunc: this._getElement.bind(this, ".screen"), getRemoteElementFunc: this._getElement.bind(this, ".remote") })); } @@ -238,6 +241,11 @@ "room-preview": this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS }); + var screenShareData = { + state: this.state.screenSharingState, + visible: this.props.mozLoop.getLoopPref("screenshare.enabled") + }; + switch(this.state.roomState) { case ROOM_STATES.FAILED: case ROOM_STATES.FULL: { @@ -266,15 +274,18 @@
    -
    +
    +
    + hangup={this.leaveRoom} + screenShare={screenShareData} />
  • diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/libs/l10n.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/libs/l10n.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/libs/l10n.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/libs/l10n.js 2015-02-03 14:33:16.000000000 +0000 @@ -40,7 +40,6 @@ function translateString(key, args, fallback) { if (args && args.num) { var num = args && args.num; - delete args.num; } var data = getL10nData(key, num); if (!data && fallback) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/css/conversation.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/css/conversation.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/css/conversation.css 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/css/conversation.css 2015-02-03 14:33:16.000000000 +0000 @@ -3,11 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* Shared conversation window styles */ -.standalone .video-layout-wrapper, -.conversation .media video { - background-color: #444; -} - .conversation { position: relative; } @@ -48,6 +43,11 @@ margin-right: 16px; } +.btn-screen-share-entry { + float: right !important; + border-left: 1px solid #5a5a5a; +} + .conversation-toolbar-btn-box { border-right: 1px solid #5a5a5a; } @@ -152,11 +152,11 @@ } /* Common media control buttons behavior */ -.conversation-toolbar .media-control { +.conversation-toolbar .transparent-button { background-color: transparent; opacity: 1; } -.conversation-toolbar .media-control:hover { +.conversation-toolbar .transparent-button:hover { background-color: rgba(255,255,255,.35); opacity: 1; } @@ -197,6 +197,21 @@ } } +/* Screen share button */ +.btn-screen-share { + /* XXX Replace this with the real button: bug 1126286 */ + background-image: url(../img/video-inverse-14x14.png); +} + +.btn-screen-share.active { + background-color: #6CB23E; + opacity: 1; +} + +.btn-screen-share.disabled { + /* XXX Add css here for disabled state: bug 1126286 */ +} + .fx-embedded .remote_wrapper { position: absolute; top: 0px; @@ -216,7 +231,7 @@ /* Side by side video elements */ -.conversation .media.side-by-side .remote { +.conversation .media.side-by-side .remote-stream { width: 50%; float: left; } @@ -494,7 +509,7 @@ max-height: none; } -.conversation .media.nested .remote { +.conversation .media.nested .remote-stream { display: inline-block; position: absolute; /* workaround for lack of object-fit; see bug 1020445 */ width: 100%; @@ -508,10 +523,11 @@ * XXX this approach is fragile because it makes assumptions * about the generated OT markup, any change will break it */ -.local-stream.local-stream-audio, -.standalone .OT_subscriber .OT_video-poster, -.fx-embedded .OT_video-container .OT_video-poster, -.local-stream-audio .OT_publisher .OT_video-poster { + +/* + * For any audio-only streams, we want to display our own background + */ +.OT_audio-only .OT_widget-container .OT_video-poster { background-image: url("../img/audio-call-avatar.svg"); background-repeat: no-repeat; background-color: #4BA6E7; @@ -520,6 +536,22 @@ } /* + * Audio-only. For local streams, cancel out the SDK's opacity of 0.25. + * For remote streams we leave them shaded, as otherwise its too bright. + */ +.local-stream-audio .OT_publisher .OT_video-poster { + opacity: 1 +} + +/* + * In audio-only mode, don't display the video element, doing so interferes + * with the background opacity of the video-poster element. + */ +.OT_audio-only .OT_widget-container .OT_video-element { + display: none; +} + +/* * Ensure that the publisher (i.e. local) video is never cropped, so that it's * not possible for someone to be presented with a picture that displays * (for example) a person from the neck up, even though the camera is capturing @@ -537,7 +569,7 @@ * Another less ugly possibility would be to work with Ted Mielczarek to use * the fake camera drivers he has for Linux. */ -.room-conversation .OT_publisher .OT_video-container { +.room-conversation .OT_publisher .OT_widget-container { height: 100% !important; width: 100% !important; top: 0 !important; @@ -545,12 +577,12 @@ background-color: transparent; /* avoid visually obvious letterboxing */ } -.room-conversation .OT_publisher .OT_video-container video { +.room-conversation .OT_publisher .OT_widget-container video { background-color: transparent; /* avoid visually obvious letterboxing */ } -.fx-embedded .room-conversation .room-preview .OT_publisher .OT_video-container, -.fx-embedded .room-conversation .room-preview .OT_publisher .OT_video-container video { +.fx-embedded .room-conversation .room-preview .OT_publisher .OT_widget-container, +.fx-embedded .room-conversation .room-preview .OT_publisher .OT_widget-container video { /* Desktop conversation window room preview local stream actually wants a black background */ background-color: #000; @@ -673,7 +705,6 @@ } .standalone { - max-width: 1000px; margin: 0 auto; } } @@ -905,11 +936,6 @@ width: 75%; } -.standalone .room-conversation .local-stream { - width: 33%; - height: 26.5%; -} - .standalone .room-conversation .conversation-toolbar { background: #000; border: none; @@ -945,11 +971,6 @@ .standalone .room-conversation .video_wrapper.remote_wrapper { width: 100%; } - .standalone .room-conversation .local-stream { - /* Assumes 4:3 aspect ratio */ - width: 180px; - height: 135px; - } .standalone .conversation-toolbar { height: 38px; padding: 8px; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/js/actions.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/js/actions.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/js/actions.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/js/actions.js 2015-02-03 14:33:16.000000000 +0000 @@ -160,6 +160,9 @@ publisherConfig: Object, // The local stream element getLocalElementFunc: Function, + // The screen share element; optional until all conversation + // types support it. + // getScreenShareElementFunc: Function, // The remote stream element getRemoteElementFunc: Function }), @@ -177,6 +180,15 @@ }), /** + * Used for notifying that the dimensions of a stream just changed. Also + * dispatched when a stream connects for the first time. + */ + VideoDimensionsChanged: Action.define("videoDimensionsChanged", { + videoType: String, + dimensions: Object + }), + + /** * Used to mute or unmute a stream */ SetMute: Action.define("setMute", { @@ -187,6 +199,33 @@ }), /** + * Used to start a screen share. + */ + StartScreenShare: Action.define("startScreenShare", { + }), + + /** + * Used to end a screen share. + */ + EndScreenShare: Action.define("endScreenShare", { + }), + + /** + * Used to notifiy that screen sharing is active or not. + */ + ScreenSharingState: Action.define("screenSharingState", { + // One of loop.shared.utils.SCREEN_SHARE_STATES. + state: String + }), + + /** + * Used to notify that a shared screen is being received (or not). + */ + ReceivingScreenShare: Action.define("receivingScreenShare", { + receiving: Boolean + }), + + /** * Creates a new room. * XXX: should move to some roomActions module - refs bug 1079284 */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/js/activeRoomStore.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/js/activeRoomStore.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/js/activeRoomStore.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/js/activeRoomStore.js 2015-02-03 14:33:16.000000000 +0000 @@ -12,12 +12,14 @@ var sharedActions = loop.shared.actions; var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS; + var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES; // Error numbers taken from // https://github.com/mozilla-services/loop-server/blob/master/loop/errno.json var REST_ERRNOS = loop.shared.utils.REST_ERRNOS; var ROOM_STATES = loop.store.ROOM_STATES; + /** * Active room store. * @@ -68,7 +70,11 @@ // session. 'Used' means at least one call has been placed // with it. Entering and leaving the room without seeing // anyone is not considered as 'used' - used: false + used: false, + localVideoDimensions: {}, + remoteVideoDimensions: {}, + screenSharingState: SCREEN_SHARE_STATES.INACTIVE, + receivingScreenShare: false }; }, @@ -115,11 +121,14 @@ "connectedToSdkServers", "connectionFailure", "setMute", + "screenSharingState", + "receivingScreenShare", "remotePeerDisconnected", "remotePeerConnected", "windowUnload", "leaveRoom", - "feedbackComplete" + "feedbackComplete", + "videoDimensionsChanged" ]); }, @@ -367,6 +376,20 @@ }, /** + * Used to note the current screensharing state. + */ + screenSharingState: function(actionData) { + this.setStoreState({screenSharingState: actionData.state}); + }, + + /** + * Used to note the current state of receiving screenshare data. + */ + receivingScreenShare: function(actionData) { + this.setStoreState({receivingScreenShare: actionData.receiving}); + }, + + /** * Handles recording when a remote peer has connected to the servers. */ remotePeerConnected: function() { @@ -477,6 +500,23 @@ // Note, that we want some values, such as the windowId, so we don't // do a full reset here. this.setStoreState(this.getInitialStoreState()); + }, + + /** + * Handles a change in dimensions of a video stream and updates the store data + * with the new dimensions of a local or remote stream. + * + * @param {sharedActions.VideoDimensionsChanged} actionData + */ + videoDimensionsChanged: function(actionData) { + // NOTE: in the future, when multiple remote video streams are supported, + // we'll need to make this support multiple remotes as well. Good + // starting point for video tiling. + var storeProp = (actionData.isLocal ? "local" : "remote") + "VideoDimensions"; + var nextState = {}; + nextState[storeProp] = this.getStoreState()[storeProp]; + nextState[storeProp][actionData.videoType] = actionData.dimensions; + this.setStoreState(nextState); } }); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/js/fxOSActiveRoomStore.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/js/fxOSActiveRoomStore.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/js/fxOSActiveRoomStore.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/js/fxOSActiveRoomStore.js 2015-02-03 14:33:16.000000000 +0000 @@ -32,7 +32,9 @@ roomState: ROOM_STATES.INIT, audioMuted: false, videoMuted: false, - failureReason: undefined + failureReason: undefined, + localVideoDimensions: {}, + remoteVideoDimensions: {} }; }, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/js/mixins.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/js/mixins.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/js/mixins.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/js/mixins.js 2015-02-03 14:33:16.000000000 +0000 @@ -157,29 +157,224 @@ * elements and handling updates of the media containers. */ var MediaSetupMixin = { + _videoDimensionsCache: { + local: {}, + remote: {} + }, + componentDidMount: function() { - rootObject.addEventListener('orientationchange', this.updateVideoContainer); - rootObject.addEventListener('resize', this.updateVideoContainer); + rootObject.addEventListener("orientationchange", this.updateVideoContainer); + rootObject.addEventListener("resize", this.updateVideoContainer); }, componentWillUnmount: function() { - rootObject.removeEventListener('orientationchange', this.updateVideoContainer); - rootObject.removeEventListener('resize', this.updateVideoContainer); + rootObject.removeEventListener("orientationchange", this.updateVideoContainer); + rootObject.removeEventListener("resize", this.updateVideoContainer); + }, + + /** + * Whenever the dimensions change of a video stream, this function is called + * by `updateVideoDimensions` to store the new values and notifies the callee + * if the dimensions changed compared to the currently stored values. + * + * @param {String} which Type of video stream. May be 'local' or 'remote' + * @param {Object} newDimensions Object containing 'width' and 'height' properties + * @return {Boolean} `true` when the dimensions have changed, + * `false` if not + */ + _updateDimensionsCache: function(which, newDimensions) { + var cache = this._videoDimensionsCache[which]; + var cacheKeys = Object.keys(cache); + var changed = false; + Object.keys(newDimensions).forEach(function(videoType) { + if (cacheKeys.indexOf(videoType) === -1) { + cache[videoType] = newDimensions[videoType]; + cache[videoType].aspectRatio = this.getAspectRatio(cache[videoType]); + changed = true; + return; + } + if (cache[videoType].width !== newDimensions[videoType].width) { + cache[videoType].width = newDimensions[videoType].width; + changed = true; + } + if (cache[videoType].height !== newDimensions[videoType].height) { + cache[videoType].height = newDimensions[videoType].height; + changed = true; + } + if (changed) { + cache[videoType].aspectRatio = this.getAspectRatio(cache[videoType]); + } + }, this); + return changed; + }, + + /** + * Whenever the dimensions change of a video stream, this function is called + * to process these changes and possibly trigger an update to the video + * container elements. + * + * @param {Object} localVideoDimensions Object containing 'width' and 'height' + * properties grouped by stream name + * @param {Object} remoteVideoDimensions Object containing 'width' and 'height' + * properties grouped by stream name + */ + updateVideoDimensions: function(localVideoDimensions, remoteVideoDimensions) { + var localChanged = this._updateDimensionsCache("local", localVideoDimensions); + var remoteChanged = this._updateDimensionsCache("remote", remoteVideoDimensions); + if (localChanged || remoteChanged) { + this.updateVideoContainer(); + } + }, + + /** + * Get the aspect ratio of a width/ height pair, which should be the dimensions + * of a stream. The returned object is an aspect ratio indexed by 1; the leading + * size has a value smaller than 1 and the slave size has a value of 1. + * this is exactly the same as notations like 4:3 and 16:9, which are merely + * human-readable forms of their fractional counterparts. 4:3 === 1:0.75 and + * 16:9 === 1:0.5625. + * So we're using the aspect ratios in their original form, because that's + * easier to do calculus with. + * + * Example: + * A stream with dimensions `{ width: 640, height: 480 }` yields an indexed + * aspect ratio of `{ width: 1, height: 0.75 }`. This means that the 'height' + * will determine the value of 'width' when the stream is stretched or shrunk + * to fit inside its container element at the maximum size. + * + * @param {Object} dimensions Object containing 'width' and 'height' properties + * @return {Object} Contains the indexed aspect ratio for 'width' + * and 'height' assigned to the corresponding + * properties. + */ + getAspectRatio: function(dimensions) { + if (dimensions.width === dimensions.height) { + return {width: 1, height: 1}; + } + var denominator = Math.max(dimensions.width, dimensions.height); + return { + width: dimensions.width / denominator, + height: dimensions.height / denominator + }; + }, + + /** + * Retrieve the dimensions of the active remote video stream. This assumes + * that if screens are being shared, the remote camera stream is hidden. + * Example output: + * { + * width: 680, + * height: 480, + * streamWidth: 640, + * streamHeight: 480, + * offsetX: 20, + * offsetY: 0 + * } + * + * Note: This expects a class on the element that has the name "remote" or the + * same name as the possible video types (currently only "screen"). + * Note: Once we support multiple remote video streams, this function will + * need to be updated. + * @return {Object} contains the remote stream dimension properties of its + * container node, the stream itself and offset of the stream + * relative to its container node in pixels. + */ + getRemoteVideoDimensions: function() { + var remoteVideoDimensions; + + Object.keys(this._videoDimensionsCache.remote).forEach(function(videoType) { + var node = this._getElement("." + (videoType === "camera" ? "remote" : videoType)); + var width = node.offsetWidth; + // If the width > 0 then we record its real size by taking its aspect + // ratio in account. Due to the 'contain' fit-mode, the stream will be + // centered inside the video element. + // We'll need to deal with more than one remote video stream whenever + // that becomes something we need to support. + if (width) { + remoteVideoDimensions = { + width: width, + height: node.offsetHeight + }; + var ratio = this._videoDimensionsCache.remote[videoType].aspectRatio; + var leadingAxis = Math.min(ratio.width, ratio.height) === ratio.width ? + "width" : "height"; + var slaveSize = remoteVideoDimensions[leadingAxis] + + (remoteVideoDimensions[leadingAxis] * (1 - ratio[leadingAxis])); + remoteVideoDimensions.streamWidth = leadingAxis === "width" ? + remoteVideoDimensions.width : slaveSize; + remoteVideoDimensions.streamHeight = leadingAxis === "height" ? + remoteVideoDimensions.height: slaveSize; + } + }, this); + + // Supply some sensible defaults for the remoteVideoDimensions if no remote + // stream is connected (yet). + if (!remoteVideoDimensions) { + var node = this._getElement(".remote"); + var width = node.offsetWidth; + var height = node.offsetHeight; + remoteVideoDimensions = { + width: width, + height: height, + streamWidth: width, + streamHeight: height + }; + } + + // Calculate the size of each individual letter- or pillarbox for convenience. + remoteVideoDimensions.offsetX = remoteVideoDimensions.width - + remoteVideoDimensions.streamWidth; + if (remoteVideoDimensions.offsetX > 0) { + remoteVideoDimensions.offsetX /= 2; + } + remoteVideoDimensions.offsetY = remoteVideoDimensions.height - + remoteVideoDimensions.streamHeight; + if (remoteVideoDimensions.offsetY > 0) { + remoteVideoDimensions.offsetY /= 2; + } + + return remoteVideoDimensions; }, /** * Used to update the video container whenever the orientation or size of the * display area changes. + * + * Buffer the calls to this function to make sure we don't overflow the stack + * with update calls when many 'resize' event are fired, to prevent blocking + * the event loop. */ updateVideoContainer: function() { - var localStreamParent = this._getElement('.local .OT_publisher'); - var remoteStreamParent = this._getElement('.remote .OT_subscriber'); - if (localStreamParent) { - localStreamParent.style.width = "100%"; - } - if (remoteStreamParent) { - remoteStreamParent.style.height = "100%"; + if (this._bufferedUpdateVideo) { + rootObject.clearTimeout(this._bufferedUpdateVideo); + this._bufferedUpdateVideo = null; } + + this._bufferedUpdateVideo = rootObject.setTimeout(function() { + this._bufferedUpdateVideo = null; + var localStreamParent = this._getElement(".local .OT_publisher"); + var remoteStreamParent = this._getElement(".remote .OT_subscriber"); + var screenShareStreamParent = this._getElement('.screen .OT_subscriber'); + if (localStreamParent) { + localStreamParent.style.width = "100%"; + } + if (remoteStreamParent) { + remoteStreamParent.style.height = "100%"; + } + if (screenShareStreamParent) { + screenShareStreamParent.style.height = "100%"; + } + + // Update the position and dimensions of the containers of local video + // streams, if necessary. The consumer of this mixin should implement the + // actual updating mechanism. + Object.keys(this._videoDimensionsCache.local).forEach(function(videoType) { + var ratio = this._videoDimensionsCache.local[videoType].aspectRatio; + if (videoType == "camera" && this.updateLocalCameraPosition) { + this.updateLocalCameraPosition(ratio); + } + }, this); + }.bind(this), 0); }, /** @@ -198,16 +393,11 @@ // @see https://bugzilla.mozilla.org/show_bug.cgi?id=1020445 return { insertMode: "append", + fitMode: "contain", width: "100%", height: "100%", publishVideo: options.publishVideo, - style: { - audioLevelDisplayMode: "off", - bugDisplayMode: "off", - buttonDisplayMode: "off", - nameDisplayMode: "off", - videoDisabledDisplayMode: "off" - } + showControls: false, }; }, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/js/otSdkDriver.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/js/otSdkDriver.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/js/otSdkDriver.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/js/otSdkDriver.js 2015-02-03 14:33:16.000000000 +0000 @@ -9,6 +9,8 @@ var sharedActions = loop.shared.actions; var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS; + var STREAM_PROPERTIES = loop.shared.utils.STREAM_PROPERTIES; + var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES; /** * This is a wrapper for the OT sdk. It is used to translate the SDK events into @@ -29,12 +31,22 @@ this.dispatcher.register(this, [ "setupStreamElements", - "setMute" + "setMute", + "startScreenShare", + "endScreenShare" ]); }; OTSdkDriver.prototype = { /** + * Clones the publisher config into a new object, as the sdk modifies the + * properties object. + */ + _getCopyPublisherConfig: function() { + return _.extend({}, this.publisherConfig); + }, + + /** * Handles the setupStreamElements action. Saves the required data and * kicks off the initialising of the publisher. * @@ -43,6 +55,7 @@ */ setupStreamElements: function(actionData) { this.getLocalElement = actionData.getLocalElementFunc; + this.getScreenShareElementFunc = actionData.getScreenShareElementFunc; this.getRemoteElement = actionData.getRemoteElementFunc; this.publisherConfig = actionData.publisherConfig; @@ -50,7 +63,8 @@ // the initial connect of the session. This saves time when setting up // the media. this.publisher = this.sdk.initPublisher(this.getLocalElement(), - this.publisherConfig); + this._getCopyPublisherConfig()); + this.publisher.on("streamCreated", this._onLocalStreamCreated.bind(this)); this.publisher.on("accessAllowed", this._onPublishComplete.bind(this)); this.publisher.on("accessDenied", this._onPublishDenied.bind(this)); this.publisher.on("accessDialogOpened", @@ -73,6 +87,41 @@ }, /** + * Initiates a screen sharing publisher. + */ + startScreenShare: function() { + this.dispatcher.dispatch(new sharedActions.ScreenSharingState({ + state: SCREEN_SHARE_STATES.PENDING + })); + + var config = this._getCopyPublisherConfig(); + // This is temporary until we get a sharing type selector + config.videoSource = "window"; + + this.screenshare = this.sdk.initPublisher(this.getScreenShareElementFunc(), + config); + this.screenshare.on("accessAllowed", this._onScreenShareGranted.bind(this)); + this.screenshare.on("accessDenied", this._onScreenShareDenied.bind(this)); + }, + + /** + * Ends an active screenshare session. + */ + endScreenShare: function() { + if (!this.screenshare) { + return; + } + + this.session.unpublish(this.screenshare); + this.screenshare.off("accessAllowed accessDenied"); + this.screenshare.destroy(); + delete this.screenshare; + this.dispatcher.dispatch(new sharedActions.ScreenSharingState({ + state: SCREEN_SHARE_STATES.INACTIVE + })); + }, + + /** * Connects a session for the SDK, listening to the required events. * * sessionData items: @@ -87,10 +136,12 @@ this.session.on("connectionCreated", this._onConnectionCreated.bind(this)); this.session.on("streamCreated", this._onRemoteStreamCreated.bind(this)); + this.session.on("streamDestroyed", this._onRemoteStreamDestroyed.bind(this)); this.session.on("connectionDestroyed", this._onConnectionDestroyed.bind(this)); this.session.on("sessionDisconnected", this._onSessionDisconnected.bind(this)); + this.session.on("streamPropertyChanged", this._onStreamPropertyChanged.bind(this)); // This starts the actual session connection. this.session.connect(sessionData.apiKey, sessionData.sessionToken, @@ -101,13 +152,16 @@ * Disconnects the sdk session. */ disconnectSession: function() { + this.endScreenShare(); + if (this.session) { - this.session.off("streamCreated connectionDestroyed sessionDisconnected"); + this.session.off("streamCreated streamDestroyed connectionDestroyed " + + "sessionDisconnected streamPropertyChanged"); this.session.disconnect(); delete this.session; } if (this.publisher) { - this.publisher.off("accessAllowed accessDenied accessDialogOpened"); + this.publisher.off("accessAllowed accessDenied accessDialogOpened streamCreated"); this.publisher.destroy(); delete this.publisher; } @@ -228,14 +282,53 @@ }, /** + * Handles when a remote screen share is created, subscribing to + * the stream, and notifying the stores that a share is being + * received. + * + * @param {Stream} stream The SDK Stream: + * https://tokbox.com/opentok/libraries/client/js/reference/Stream.html + */ + _handleRemoteScreenShareCreated: function(stream) { + if (!this.getScreenShareElementFunc) { + return; + } + + // Let the stores know first so they can update the display. + this.dispatcher.dispatch(new sharedActions.ReceivingScreenShare({ + receiving: true + })); + + var remoteElement = this.getScreenShareElementFunc(); + + this.session.subscribe(stream, + remoteElement, this._getCopyPublisherConfig()); + }, + + /** * Handles the event when the remote stream is created. * * @param {StreamEvent} event The event details: * https://tokbox.com/opentok/libraries/client/js/reference/StreamEvent.html */ _onRemoteStreamCreated: function(event) { + if (event.stream[STREAM_PROPERTIES.HAS_VIDEO]) { + this.dispatcher.dispatch(new sharedActions.VideoDimensionsChanged({ + isLocal: false, + videoType: event.stream.videoType, + dimensions: event.stream[STREAM_PROPERTIES.VIDEO_DIMENSIONS] + })); + } + + if (event.stream.videoType === "screen") { + this._handleRemoteScreenShareCreated(event.stream); + return; + } + + var remoteElement = this.getRemoteElement(); + this.session.subscribe(event.stream, - this.getRemoteElement(), this.publisherConfig); + remoteElement, this._getCopyPublisherConfig()); this._subscribedRemoteStream = true; if (this._checkAllStreamsConnected()) { @@ -244,6 +337,41 @@ }, /** + * Handles the event when the local stream is created. + * + * @param {StreamEvent} event The event details: + * https://tokbox.com/opentok/libraries/client/js/reference/StreamEvent.html + */ + _onLocalStreamCreated: function(event) { + if (event.stream[STREAM_PROPERTIES.HAS_VIDEO]) { + this.dispatcher.dispatch(new sharedActions.VideoDimensionsChanged({ + isLocal: true, + videoType: event.stream.videoType, + dimensions: event.stream[STREAM_PROPERTIES.VIDEO_DIMENSIONS] + })); + } + }, + + + /** + * Handles the event when the remote stream is destroyed. + * + * @param {StreamEvent} event The event details: + * https://tokbox.com/opentok/libraries/client/js/reference/StreamEvent.html + */ + _onRemoteStreamDestroyed: function(event) { + if (event.stream.videoType !== "screen") { + return; + } + + // All we need to do is notify the store we're no longer receiving, + // the sdk should do the rest. + this.dispatcher.dispatch(new sharedActions.ReceivingScreenShare({ + receiving: false + })); + }, + + /** * Called from the sdk when the media access dialog is opened. * Prevents the default action, to prevent the SDK's "allow access" * dialog from being shown. @@ -283,6 +411,19 @@ }, /** + * Handles publishing of property changes to a stream. + */ + _onStreamPropertyChanged: function(event) { + if (event.changedProperty == STREAM_PROPERTIES.VIDEO_DIMENSIONS) { + this.dispatcher.dispatch(new sharedActions.VideoDimensionsChanged({ + isLocal: event.stream.connection.id == this.session.connection.id, + videoType: event.stream.videoType, + dimensions: event.stream[STREAM_PROPERTIES.VIDEO_DIMENSIONS] + })); + } + }, + + /** * Publishes the local stream if the session is connected * and the publisher is ready. */ @@ -306,6 +447,25 @@ _checkAllStreamsConnected: function() { return this._publishedLocalStream && this._subscribedRemoteStream; + }, + + /** + * Called when a screenshare is complete, publishes it to the session. + */ + _onScreenShareGranted: function() { + this.session.publish(this.screenshare); + this.dispatcher.dispatch(new sharedActions.ScreenSharingState({ + state: SCREEN_SHARE_STATES.ACTIVE + })); + }, + + /** + * Called when a screenshare is denied. Notifies the other stores. + */ + _onScreenShareDenied: function() { + this.dispatcher.dispatch(new sharedActions.ScreenSharingState({ + state: SCREEN_SHARE_STATES.INACTIVE + })); } }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/js/utils.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/js/utils.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/js/utils.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/js/utils.js 2015-02-03 14:33:16.000000000 +0000 @@ -42,6 +42,19 @@ UNKNOWN: "reason-unknown" }; + var STREAM_PROPERTIES = { + VIDEO_DIMENSIONS: "videoDimensions", + HAS_AUDIO: "hasAudio", + HAS_VIDEO: "hasVideo" + }; + + var SCREEN_SHARE_STATES = { + INACTIVE: "ss-inactive", + // Pending is when the user is being prompted, aka gUM in progress. + PENDING: "ss-pending", + ACTIVE: "ss-active" + }; + /** * Format a given date into an l10n-friendly string. * @@ -138,6 +151,8 @@ FAILURE_DETAILS: FAILURE_DETAILS, REST_ERRNOS: REST_ERRNOS, WEBSOCKET_REASONS: WEBSOCKET_REASONS, + STREAM_PROPERTIES: STREAM_PROPERTIES, + SCREEN_SHARE_STATES: SCREEN_SHARE_STATES, Helper: Helper, composeCallUrlEmail: composeCallUrlEmail, formatDate: formatDate, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/js/views.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/js/views.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/js/views.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/js/views.js 2015-02-03 14:33:16.000000000 +0000 @@ -8,11 +8,13 @@ /* global loop:true, React */ var loop = loop || {}; loop.shared = loop.shared || {}; -loop.shared.views = (function(_, OT, l10n) { +loop.shared.views = (function(_, l10n) { "use strict"; + var sharedActions = loop.shared.actions; var sharedModels = loop.shared.models; var sharedMixins = loop.shared.mixins; + var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES; /** * Media control button. @@ -46,6 +48,7 @@ var classesObj = { "btn": true, "media-control": true, + "transparent-button": true, "local-media": this.props.scope === "local", "muted": !this.props.enabled, "hide": !this.props.visible @@ -73,6 +76,60 @@ }); /** + * Screen sharing control button. + * + * Required props: + * - {loop.Dispatcher} dispatcher The dispatcher instance + * - {Boolean} visible Set to true to display the button + * - {String} state One of the screen sharing states, see + * loop.shared.utils.SCREEN_SHARE_STATES + */ + var ScreenShareControlButton = React.createClass({displayName: "ScreenShareControlButton", + propTypes: { + dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, + visible: React.PropTypes.bool.isRequired, + state: React.PropTypes.string.isRequired, + }, + + handleClick: function() { + if (this.props.state === SCREEN_SHARE_STATES.ACTIVE) { + this.props.dispatcher.dispatch( + new sharedActions.EndScreenShare({})); + } else { + this.props.dispatcher.dispatch( + new sharedActions.StartScreenShare({})); + } + }, + + _getTitle: function() { + var prefix = this.props.state === SCREEN_SHARE_STATES.ACTIVE ? + "active" : "inactive"; + + return l10n.get(prefix + "_screenshare_button_title"); + }, + + render: function() { + if (!this.props.visible) { + return null; + } + + var screenShareClasses = React.addons.classSet({ + "btn": true, + "btn-screen-share": true, + "transparent-button": true, + "active": this.props.state === SCREEN_SHARE_STATES.ACTIVE, + "disabled": this.props.state === SCREEN_SHARE_STATES.PENDING + }); + + return ( + React.createElement("button", {className: screenShareClasses, + onClick: this.handleClick, + title: this._getTitle()}) + ); + } + }); + + /** * Conversation controls. */ var ConversationToolbar = React.createClass({displayName: "ConversationToolbar", @@ -80,13 +137,16 @@ return { video: {enabled: true, visible: true}, audio: {enabled: true, visible: true}, + screenShare: {state: SCREEN_SHARE_STATES.INACTIVE, visible: false}, enableHangup: true }; }, propTypes: { + dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, video: React.PropTypes.object.isRequired, audio: React.PropTypes.object.isRequired, + screenShare: React.PropTypes.object, hangup: React.PropTypes.func.isRequired, publishStream: React.PropTypes.func.isRequired, hangupButtonLabel: React.PropTypes.string, @@ -110,7 +170,6 @@ }, render: function() { - var cx = React.addons.classSet; return ( React.createElement("ul", {className: "conversation-toolbar"}, React.createElement("li", {className: "conversation-toolbar-btn-box btn-hangup-entry"}, @@ -131,6 +190,11 @@ enabled: this.props.audio.enabled, visible: this.props.audio.visible, scope: "local", type: "audio"}) + ), + React.createElement("li", {className: "conversation-toolbar-btn-box btn-screen-share-entry"}, + React.createElement(ScreenShareControlButton, {dispatcher: this.props.dispatcher, + visible: this.props.screenShare.visible, + state: this.props.screenShare.state}) ) ) ); @@ -294,7 +358,7 @@ React.createElement("div", {className: "conversation"}, React.createElement("div", {className: "media nested"}, React.createElement("div", {className: "video_wrapper remote_wrapper"}, - React.createElement("div", {className: "video_inner remote"}) + React.createElement("div", {className: "video_inner remote remote-stream"}) ), React.createElement("div", {className: localStreamClasses}) ), @@ -461,6 +525,7 @@ ConversationView: ConversationView, ConversationToolbar: ConversationToolbar, MediaControlButton: MediaControlButton, + ScreenShareControlButton: ScreenShareControlButton, NotificationListView: NotificationListView }; -})(_, window.OT, navigator.mozL10n || document.mozL10n); +})(_, navigator.mozL10n || document.mozL10n); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/js/views.jsx thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/js/views.jsx --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/js/views.jsx 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/js/views.jsx 2015-02-03 14:33:16.000000000 +0000 @@ -8,11 +8,13 @@ /* global loop:true, React */ var loop = loop || {}; loop.shared = loop.shared || {}; -loop.shared.views = (function(_, OT, l10n) { +loop.shared.views = (function(_, l10n) { "use strict"; + var sharedActions = loop.shared.actions; var sharedModels = loop.shared.models; var sharedMixins = loop.shared.mixins; + var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES; /** * Media control button. @@ -46,6 +48,7 @@ var classesObj = { "btn": true, "media-control": true, + "transparent-button": true, "local-media": this.props.scope === "local", "muted": !this.props.enabled, "hide": !this.props.visible @@ -73,6 +76,60 @@ }); /** + * Screen sharing control button. + * + * Required props: + * - {loop.Dispatcher} dispatcher The dispatcher instance + * - {Boolean} visible Set to true to display the button + * - {String} state One of the screen sharing states, see + * loop.shared.utils.SCREEN_SHARE_STATES + */ + var ScreenShareControlButton = React.createClass({ + propTypes: { + dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, + visible: React.PropTypes.bool.isRequired, + state: React.PropTypes.string.isRequired, + }, + + handleClick: function() { + if (this.props.state === SCREEN_SHARE_STATES.ACTIVE) { + this.props.dispatcher.dispatch( + new sharedActions.EndScreenShare({})); + } else { + this.props.dispatcher.dispatch( + new sharedActions.StartScreenShare({})); + } + }, + + _getTitle: function() { + var prefix = this.props.state === SCREEN_SHARE_STATES.ACTIVE ? + "active" : "inactive"; + + return l10n.get(prefix + "_screenshare_button_title"); + }, + + render: function() { + if (!this.props.visible) { + return null; + } + + var screenShareClasses = React.addons.classSet({ + "btn": true, + "btn-screen-share": true, + "transparent-button": true, + "active": this.props.state === SCREEN_SHARE_STATES.ACTIVE, + "disabled": this.props.state === SCREEN_SHARE_STATES.PENDING + }); + + return ( + + ); + } + }); + + /** * Conversation controls. */ var ConversationToolbar = React.createClass({ @@ -80,13 +137,16 @@ return { video: {enabled: true, visible: true}, audio: {enabled: true, visible: true}, + screenShare: {state: SCREEN_SHARE_STATES.INACTIVE, visible: false}, enableHangup: true }; }, propTypes: { + dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, video: React.PropTypes.object.isRequired, audio: React.PropTypes.object.isRequired, + screenShare: React.PropTypes.object, hangup: React.PropTypes.func.isRequired, publishStream: React.PropTypes.func.isRequired, hangupButtonLabel: React.PropTypes.string, @@ -110,7 +170,6 @@ }, render: function() { - var cx = React.addons.classSet; return (
    • @@ -132,6 +191,11 @@ visible={this.props.audio.visible} scope="local" type="audio" />
    • +
    • + +
    ); } @@ -294,7 +358,7 @@
    -
    +
    @@ -461,6 +525,7 @@ ConversationView: ConversationView, ConversationToolbar: ConversationToolbar, MediaControlButton: MediaControlButton, + ScreenShareControlButton: ScreenShareControlButton, NotificationListView: NotificationListView }; -})(_, window.OT, navigator.mozL10n || document.mozL10n); +})(_, navigator.mozL10n || document.mozL10n); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/libs/sdk-content/css/ot.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/libs/sdk-content/css/ot.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/libs/sdk-content/css/ot.css 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/libs/sdk-content/css/ot.css 2015-02-03 14:33:16.000000000 +0000 @@ -11,16 +11,15 @@ /* Root OT object, this is where our CSS reset happens */ .OT_root, .OT_root * { - color: #ffffff; - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font-family: Arial, Helvetica, sans-serif; - vertical-align: baseline; + color: #ffffff; + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font-family: Arial, Helvetica, sans-serif; + vertical-align: baseline; } - /** * Specific Element Reset */ @@ -31,10 +30,10 @@ .OT_root h4, .OT_root h5, .OT_root h6 { - color: #ffffff; - font-family: Arial, Helvetica, sans-serif; - font-size: 100%; - font-weight: bold; + color: #ffffff; + font-family: Arial, Helvetica, sans-serif; + font-size: 100%; + font-weight: bold; } .OT_root header { @@ -62,11 +61,11 @@ } .OT_root strong { - font-weight: bold; + font-weight: bold; } .OT_root em { - font-style: italic; + font-style: italic; } .OT_root a, @@ -74,260 +73,131 @@ .OT_root a:visited, .OT_root a:hover, .OT_root a:active { - font-family: Arial, Helvetica, sans-serif; + font-family: Arial, Helvetica, sans-serif; } - .OT_root ul, .OT_root ol { - margin: 1em 1em 1em 2em; + margin: 1em 1em 1em 2em; } .OT_root ol { - list-style: decimal outside; + list-style: decimal outside; } .OT_root ul { - list-style: disc outside; + list-style: disc outside; } .OT_root dl { - margin: 4px; -} - .OT_root dl dt, - .OT_root dl dd { - float: left; - margin: 0; - padding: 0; - } - - .OT_root dl dt { - clear: left; - text-align: right; - width: 50px; - } - .OT_root dl dd { - margin-left: 10px; - } - -.OT_root img { - border: 0 none; + margin: 4px; } - -/* Modal dialog styles */ - -/* Modal dialog styles */ - -.OT_dialog { - border: none; - border-radius: 0; - top: 50%; - left: 50%; - position: absolute; - position: fixed; +.OT_root dl dt, +.OT_root dl dd { + float: left; + margin: 0; padding: 0; - background-color: #363636; - color: #fff; - z-index: 9999; - box-shadow: 2px 4px 6px #999; - font-family: 'Didact Gothic', sans-serif; } -.OT_dialog-blackout { - position: absolute; - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - background-color: #363636; +.OT_root dl dt { + clear: left; + text-align: right; + width: 50px; } -.OT_dialog-blackout .OT_dialog { - box-shadow: 0 0 0 transparent; +.OT_root dl dd { + margin-left: 10px; } -.OT_dialog * { - font-family: 'Didact Gothic', sans-serif; -} - -.OT_dialog-plugin-prompt { - margin-left: -350px; - margin-top: -127px; - width: 650px; - height: 254px; -} - -.OT_dialog-plugin-reinstall { - margin-left: -271px; - margin-top: -107px; - width: 542px; - height: 214px; +.OT_root img { + border: 0 none; } -.OT_dialog-plugin-upgrading { - margin-left: -267px; - margin-top: -94px; - width: 514px; - height: 188px; -} +/* Modal dialog styles */ -.OT_dialog-plugin-upgraded { - margin-left: -300px; - margin-top: -100px; - width: 600px; - height: 200px; -} +/* Modal dialog styles */ -.OT_dialog-allow-deny-chrome-first { - margin-left: -227px; - margin-top: -122px; - width: 453px; - height: 244px; +.OT_dialog-centering { + display: table; + width: 100%; + height: 100%; } -.OT_dialog-allow-deny-chrome-pre-denied { - margin-left: -263px; - margin-top: -135px; - width: 526px; - height: 270px; +.OT_dialog-centering-child { + display: table-cell; + vertical-align: middle; } -.OT_dialog-allow-deny-chrome-now-denied { - margin-left: -120px; - margin-top: -85px; - width: 256px; - height: 170px; -} +.OT_dialog { + position: relative; -.OT_dialog-allow-deny-firefox-denied { - margin-left: -160px; - margin-top: -105px; - width: 320px; - height: 190px; -} + box-sizing: border-box; + max-width: 576px; + margin-right: auto; + margin-left: auto; + padding: 36px; + text-align: center; /* centers all the inline content */ -.OT_dialog-allow-deny-firefox-maybe-denied { - margin-left: -281px; - margin-top: -126px; - width: 562px; - height: 252px; + background-color: #363636; + color: #fff; + box-shadow: 2px 4px 6px #999; + font-family: 'Didact Gothic', sans-serif; + font-size: 13px; + line-height: 1.4; } -.OT_dialog-allow-highlight-chrome { - display: inline-block; - margin-top: 20px; - width: 227px; - height: 94px; - background-image: url(../images/rtc/access-prompt-chrome.png); +.OT_dialog * { + font-family: inherit; + box-sizing: inherit; } .OT_closeButton { color: #999999; cursor: pointer; font-size: 32px; - line-height: 30px; + line-height: 36px; position: absolute; - right: 15px; + right: 18px; top: 0; } .OT_dialog-messages { - position: absolute; - top: 32px; - left: 32px; - right: 32px; text-align: center; } -.OT_dialog-allow-deny-firefox-maybe-denied .OT_dialog-messages { - top: 45px; -} - - .OT_dialog-messages-main { + margin-bottom: 36px; + line-height: 36px; + font-weight: 300; - font-size: 18pt; - line-height: 24px; + font-size: 24px; } .OT_dialog-messages-minor { - font-weight: 300; - margin-top: 12px; + margin-bottom: 18px; + font-size: 13px; line-height: 18px; color: #A4A4A4; } -.OT_dialog-allow-deny-firefox-maybe-denied .OT_dialog-messages-minor { - margin-top: 4px; -} - .OT_dialog-messages-minor strong { - font-weight: 300; color: #ffffff; } -.OT_dialog-hidden { - display: none; -} - -.OT_dialog-single-button { - position: absolute; - bottom: 41px; - left: 50%; - margin-left: -97px; - height: 47px; - width: 193px; -} - - -.OT_dialog-single-button-wide { - bottom: 35px; - height: 140px; - left: 5px; - position: absolute; - right: 0; -} - .OT_dialog-single-button-with-title { - margin: 0 auto; - padding-left: 30px; - padding-right: 30px; - width: 270px; - } - - -.OT_dialog-button-pair { - position: absolute; - bottom: 45px; - left: 5px; - right: 0; - height: 94px; -} - -.OT_dialog-button-with-title { - padding-left: 30px; - padding-right: 30px; - width: 260px; - float: left; -} - -.OT_dialog-button-pair-seperator { - border-right: 1px solid #555555; - height: 112px; - width: 1px; - float: left; +.OT_dialog-actions-card { + display: inline-block; } .OT_dialog-button-title { + margin-bottom: 18px; + line-height: 18px; + font-weight: 300; text-align: center; - margin-bottom: 15px; font-size: 14px; - line-height: 150%; color: #999999; } - .OT_dialog-button-title label { color: #999999; } @@ -345,13 +215,13 @@ } .OT_dialog-button { - font-weight: 100; - display: block; - line-height: 50px; - height: 47px; + display: inline-block; + + margin-bottom: 18px; + padding: 0 1em; + background-color: #1CA3DC; text-align: center; - font-size: 16pt; cursor: pointer; } @@ -364,174 +234,100 @@ opacity: 0.5; } -.OT_dialog-button.OT_dialog-button-large { - line-height: 60px; - height: 58px; +.OT_dialog-button-large { + line-height: 36px; + padding-top: 9px; + padding-bottom: 9px; + + font-weight: 100; + font-size: 24px; } -.OT_dialog-button.OT_dialog-button-small { +.OT_dialog-button-small { + line-height: 18px; + padding-top: 9px; + padding-bottom: 9px; + background-color: #444444; color: #999999; - font-size: 12pt; - height: 40px; - line-height: 40px; - margin: 20px auto 0 auto; - width: 86px; + font-size: 16px; } .OT_dialog-progress-bar { + display: inline-block; /* prevents margin collapse */ + width: 100%; + margin-top: 5px; + margin-bottom: 41px; + border: 1px solid #4E4E4E; height: 8px; } .OT_dialog-progress-bar-fill { + height: 100%; + background-color: #29A4DA; - height: 10px; - margin-top: -1px; - margin-left: -1px; - margin-right: -1px; } .OT_dialog-plugin-upgrading .OT_dialog-plugin-upgrade-percentage { - font-size: 36pt; - font-weight: 100; -} - -.OT_dialog-plugin-upgrading .OT_dialog-progress-bar { - margin-top: 25px; - margin-bottom: 25px; -} - -.OT_dialog-3steps { - margin-top: 24px; -} - -.OT_dialog-allow-deny-firefox-maybe-denied .OT_dialog-3steps { - margin-top: 21px; -} + line-height: 54px; -.OT_dialog-3steps-step { - float: left; - -moz-box-sizing: border-box; - box-sizing: border-box; - width: 33%; - height: 140px; - padding: 0 10px; - color: #A4A4A4; -} - -.OT_dialog-3steps-seperator { - float: left; - -moz-box-sizing: border-box; - box-sizing: border-box; - margin-top: 10px; - width: 1px; - height: 68px; - background-color: #555555; -} - -.OT_dialog-allow-deny-chrome-pre-denied .OT_dialog-3steps-seperator { - margin-top: 16px; -} - -.OT_dialog-3steps-step-num { - font-size: 20px; - background-color: #2AA3D8; - border-radius: 20px; - line-height: 33px; - height: 33px; - width: 33px; - margin: 0 auto 17px; -} - -.OT_dialog-allow-deny-chrome-pre-denied .OT_dialog-3steps-step-num { - margin-bottom: 10px; -} - -.OT_dialog-allow-camera-icon { - background-color: #000; - width: 113px; - height: 48px; - margin: 10px auto 0; - background-image: url(../images/rtc/access-predenied-chrome.png); + font-size: 48px; + font-weight: 100; } -/* Publisher Deny Helpers */ +/* Helpers */ -.OT_publisher-denied-firefox { - background-color: #fff; +.OT_centered { + position: fixed; + left: 50%; + top: 50%; + margin: 0; } -.OT_publisher-denied-firefox p { - width: 232px; - height: 103px; - top: 50%; - left: 50%; - display: block; - position: absolute; - margin-top: -52px; - margin-left: -116px; - background-image: url(../images/rtc/access-denied-firefox.png); - background-position: 50% 0; - background-repeat: no-repeat; +.OT_dialog-hidden { + display: none; } -.OT_publisher-denied-firefox span { +.OT_dialog-button-block { display: block; - position: absolute; - bottom: 0; - right: 0; - left: 0; - margin: 0 auto; - width: 232px; - height: 31px; - background-image: url(../images/rtc/access-denied-copy-firefox.png); - text-indent: 100%; - white-space: nowrap; - overflow: hidden; } -.OT_centered { - position: fixed; - left: 50%; - top: 50%; - margin: 0; +.OT_dialog-no-natural-margin { + margin-bottom: 0; } /* Publisher and Subscriber styles */ .OT_publisher, .OT_subscriber { - position: relative; - min-width: 48px; - min-height: 48px; + position: relative; + min-width: 48px; + min-height: 48px; } -.OT_publisher video, -.OT_subscriber video, -.OT_publisher object, -.OT_subscriber object { - width: 100%; - height: 100%; -} +.OT_publisher .OT_video-element, +.OT_subscriber .OT_video-element { + display: block; + position: absolute; + width: 100%; -.OT_publisher object, -.OT_subscriber object { + transform-origin: 0 0; } /* Styles that are applied when the video element should be mirrored */ -.OT_publisher.OT_mirrored video{ - -webkit-transform: scale(-1, 1); - -moz-transform:scale(-1,1); +.OT_publisher.OT_mirrored .OT_video-element { + transform: scale(-1, 1); + transform-origin: 50% 50%; } .OT_subscriber_error { - background-color: #000; - color: #fff; - text-align: center; + background-color: #000; + color: #fff; + text-align: center; } .OT_subscriber_error > p { - padding: 20px; + padding: 20px; } /* The publisher/subscriber name/mute background */ @@ -545,67 +341,67 @@ .OT_subscriber .OT_archiving-status, .OT_publihser .OT_archiving-light-box, .OT_subscriber .OT_archiving-light-box { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - -ms-box-sizing: border-box; - box-sizing: border-box; - top: 0; - left: 0; - right: 0; - display: block; - height: 34px; - position: absolute; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + top: 0; + left: 0; + right: 0; + display: block; + height: 34px; + position: absolute; } .OT_publisher .OT_bar, .OT_subscriber .OT_bar { - background: rgba(0, 0, 0, 0.4); + background: rgba(0, 0, 0, 0.4); } .OT_publisher .OT_edge-bar-item, .OT_subscriber .OT_edge-bar-item { - z-index: 1; /* required to get audio level meter underneath */ + z-index: 1; /* required to get audio level meter underneath */ } /* The publisher/subscriber name panel/archiving status bar */ .OT_publisher .OT_name, .OT_subscriber .OT_name { - background-color: transparent; - color: #ffffff; - font-size: 15px; - line-height: 34px; - font-weight: normal; - padding: 0 4px 0 36px; + background-color: transparent; + color: #ffffff; + font-size: 15px; + line-height: 34px; + font-weight: normal; + padding: 0 4px 0 36px; } .OT_publisher .OT_archiving-status, .OT_subscriber .OT_archiving-status { - background: rgba(0, 0, 0, 0.4); - top: auto; - bottom: 0; - left: 34px; - padding: 0 4px; - color: rgba(255,255,255,0.8); - font-size: 15px; - line-height: 34px; - font-weight: normal; + background: rgba(0, 0, 0, 0.4); + top: auto; + bottom: 0; + left: 34px; + padding: 0 4px; + color: rgba(255, 255, 255, 0.8); + font-size: 15px; + line-height: 34px; + font-weight: normal; } .OT_micro .OT_archiving-status, .OT_micro:hover .OT_archiving-status, .OT_mini .OT_archiving-status, .OT_mini:hover .OT_archiving-status { - display: none; + display: none; } .OT_publisher .OT_archiving-light-box, .OT_subscriber .OT_archiving-light-box { - background: rgba(0, 0, 0, 0.4); - top: auto; - bottom: 0; - right: auto; - width: 34px; - height: 34px; + background: rgba(0, 0, 0, 0.4); + top: auto; + bottom: 0; + right: auto; + width: 34px; + height: 34px; } .OT_archiving-light { @@ -622,6 +418,7 @@ -moz-box-shadow: 0 0 5px 1px #575757; box-shadow: 0 0 5px 1px #575757; } + .OT_archiving-light.OT_active { background-color: #970d13; -webkit-animation: OT_pulse 1.3s ease-in; @@ -631,6 +428,7 @@ -moz-animation-iteration-count: infinite; -webkit-animation-iteration-count: infinite; } + @-moz-keyframes OT_pulse { 0% { -webkit-box-shadow: 0 0 0px 0px #c70019; @@ -662,6 +460,7 @@ box-shadow: 0 0 0px 0px #c70019; } } + @-webkit-keyframes OT_pulse { 0% { -webkit-box-shadow: 0 0 0px 0px #c70019; @@ -693,6 +492,7 @@ box-shadow: 0 0 0px 0px #c70019; } } + @-o-keyframes OT_pulse { 0% { -webkit-box-shadow: 0 0 0px 0px #c70019; @@ -724,6 +524,7 @@ box-shadow: 0 0 0px 0px #c70019; } } + @-ms-keyframes OT_pulse { 0% { -webkit-box-shadow: 0 0 0px 0px #c70019; @@ -755,6 +556,7 @@ box-shadow: 0 0 0px 0px #c70019; } } + @-webkit-keyframes OT_pulse { 0% { -webkit-box-shadow: 0 0 0px 0px #c70019; @@ -790,34 +592,27 @@ .OT_mini .OT_bar, .OT_bar.OT_mode-mini, .OT_bar.OT_mode-mini-auto { - bottom: 0; - height: auto; + bottom: 0; + height: auto; } .OT_mini .OT_name.OT_mode-off, .OT_mini .OT_name.OT_mode-on, .OT_mini .OT_name.OT_mode-auto, .OT_mini:hover .OT_name.OT_mode-auto { - display: none; + display: none; } .OT_publisher .OT_name, .OT_subscriber .OT_name { - left: 24px; + left: 10px; right: 37px; height: 34px; -} - -.OT_publisher .OT_name-no-bug, -.OT_subscriber .OT_name-no-bug { - left: 10px; padding-left: 0; } .OT_publisher .OT_mute, -.OT_subscriber .OT_mute, -.OT_publisher .OT_opentok, -.OT_subscriber .OT_opentok { +.OT_subscriber .OT_mute { border: none; cursor: pointer; display: block; @@ -828,76 +623,46 @@ background-repeat: no-repeat; } -.OT_publisher .OT_opentok, -.OT_subscriber .OT_opentok { - background: url(../images/rtc/buttons.png) 0 -32px no-repeat; - cursor: default; - height: 18px; - left: 8px; - line-height: 18px; - top: 8px; - width: 16px; -} - -.OT_micro .OT_opentok { - display: none !important; -} - .OT_publisher .OT_mute, .OT_subscriber .OT_mute { - right: 0; - top: 0; - border-left: 1px solid rgba(255, 255, 255, 0.2); - height: 36px; - width: 37px; + right: 0; + top: 0; + border-left: 1px solid rgba(255, 255, 255, 0.2); + height: 36px; + width: 37px; } .OT_mini .OT_mute, -.OT_mute.OT_mode-mini, -.OT_mute.OT_mode-mini-auto { - top: 50%; - left: 50%; - right: auto; - margin-top: -18px; - margin-left: -18.5px; - border-left: none; +.OT_publisher.OT_mini .OT_mute.OT_mode-auto.OT_mode-on-hold, +.OT_subscriber.OT_mini .OT_mute.OT_mode-auto.OT_mode-on-hold { + top: 50%; + left: 50%; + right: auto; + margin-top: -18px; + margin-left: -18.5px; + border-left: none; } .OT_publisher .OT_mute { - background-image: url(../images/rtc/mic-on.png); - background-position: 9px 5px; + background-image: url(../images/rtc/mic-on.png); + background-position: 9px 5px; } .OT_publisher .OT_mute.OT_active { - background-image: url(../images/rtc/mic-off.png); - background-position: 9px 4px; + background-image: url(../images/rtc/mic-off.png); + background-position: 9px 4px; } .OT_subscriber .OT_mute { - background-image: url(../images/rtc/speaker-on.png); - background-position: 8px 7px; + background-image: url(../images/rtc/speaker-on.png); + background-position: 8px 7px; } .OT_subscriber .OT_mute.OT_active { - background-image: url(../images/rtc/speaker-off.png); - background-position: 7px 7px; + background-image: url(../images/rtc/speaker-off.png); + background-position: 7px 7px; } -/* Disabling this for now - see https://jira.tokbox.com/browse/OPENTOK-8870 -.OT_publisher .OT_opentok:hover:after, -.OT_subscriber .OT_opentok:hover:after { - content: 'tokbox'; - color: #fff; - font-weight: bold; - font-size: 14px; - letter-spacing: -1px; - top: 20px; - opacity: 0.5; - position: absolute; - text-indent: 0; - top: 0; -}*/ - /** * Styles for display modes * @@ -908,17 +673,9 @@ /* Default display mode transitions for various chrome elements */ .OT_publisher .OT_edge-bar-item, .OT_subscriber .OT_edge-bar-item { - -ms-transition-property: top, bottom, opacity; - -ms-transition-duration: 0.5s; - -moz-transition-property: top, bottom, opacity; - -moz-transition-duration: 0.5s; - -webkit-transition-property: top, bottom, opacity; - -webkit-transition-duration: 0.5s; - -o-transition-property: top, bottom, opacity; - -o-transition-duration: 0.5s; - transition-property: top, bottom, opacity; - transition-duration: 0.5s; - transition-timing-function: ease-in; + transition-property: top, bottom, opacity; + transition-duration: 0.5s; + transition-timing-function: ease-in; } .OT_publisher .OT_edge-bar-item.OT_mode-off, @@ -927,14 +684,14 @@ .OT_subscriber .OT_edge-bar-item.OT_mode-auto, .OT_publisher .OT_edge-bar-item.OT_mode-mini-auto, .OT_subscriber .OT_edge-bar-item.OT_mode-mini-auto { - top: -25px; - opacity: 0; + top: -25px; + opacity: 0; } .OT_mini .OT_mute.OT_mode-auto, .OT_publisher .OT_mute.OT_mode-mini-auto, .OT_subscriber .OT_mute.OT_mode-mini-auto { - top: 50%; + top: 50%; } .OT_publisher .OT_edge-bar-item.OT_edge-bottom.OT_mode-off, @@ -943,8 +700,8 @@ .OT_subscriber .OT_edge-bar-item.OT_edge-bottom.OT_mode-auto, .OT_publisher .OT_edge-bar-item.OT_edge-bottom.OT_mode-mini-auto, .OT_subscriber .OT_edge-bar-item.OT_edge-bottom.OT_mode-mini-auto { - top: auto; - bottom: -25px; + top: auto; + bottom: -25px; } .OT_publisher .OT_edge-bar-item.OT_mode-on, @@ -955,177 +712,181 @@ .OT_subscriber:hover .OT_edge-bar-item.OT_mode-auto, .OT_publisher:hover .OT_edge-bar-item.OT_mode-mini-auto, .OT_subscriber:hover .OT_edge-bar-item.OT_mode-mini-auto { - top: 0; - opacity: 1; + top: 0; + opacity: 1; } .OT_mini .OT_mute.OT_mode-on, .OT_mini:hover .OT_mute.OT_mode-auto, .OT_mute.OT_mode-mini, -.OT_root:hover .OT_mute.OT_mode-mini-auto { - top: 50%; +.OT_root:hover .OT_mute.OT_mode-mini-auto { + top: 50%; } .OT_publisher .OT_edge-bar-item.OT_edge-bottom.OT_mode-on, .OT_subscriber .OT_edge-bar-item.OT_edge-bottom.OT_mode-on, .OT_publisher:hover .OT_edge-bar-item.OT_edge-bottom.OT_mode-auto, .OT_subscriber:hover .OT_edge-bar-item.OT_edge-bottom.OT_mode-auto { - top: auto; - bottom: 0; - opacity: 1; -} - -.OT_publisher .OT_opentok.OT_mode-off, -.OT_publisher .OT_opentok.OT_mode-auto, -.OT_subscriber .OT_opentok.OT_mode-off, -.OT_subscriber .OT_opentok.OT_mode-auto { - top: -17px; -} - -.OT_publisher .OT_opentok.OT_mode-on, -.OT_publisher .OT_opentok.OT_mode-auto.OT_mode-on-hold, -.OT_publisher:hover .OT_opentok.OT_mode-auto, -.OT_subscriber .OT_opentok.OT_mode-on, -.OT_subscriber .OT_opentok.OT_mode-auto.OT_mode-on-hold, -.OT_subscriber:hover .OT_opentok.OT_mode-auto { - top: 8px; + top: auto; + bottom: 0; + opacity: 1; } /* Contains the video element, used to fix video letter-boxing */ -.OT_video-container { - position: absolute; - background-color: #000000; - overflow: hidden; +.OT_widget-container { + position: absolute; + background-color: #000000; + overflow: hidden; } .OT_hidden-audio { - position: absolute !important; - height: 1px !important; - width: 1px !important; + position: absolute !important; + height: 1px !important; + width: 1px !important; } /* Load animation */ .OT_root .OT_video-loading { - background: url('../images/rtc/loader.gif') no-repeat; - display:none; - position: absolute; - width: 32px; - height: 32px; - left: 50%; - top: 50%; - margin-left: -16px; - margin-top: -16px; + background: url('../images/rtc/loader.gif') no-repeat; + display: none; + position: absolute; + width: 32px; + height: 32px; + left: 50%; + top: 50%; + margin-left: -16px; + margin-top: -16px; } .OT_publisher.OT_loading .OT_video-loading, .OT_subscriber.OT_loading .OT_video-loading { - display: block; + display: block; } -.OT_publisher.OT_loading video, -.OT_subscriber.OT_loading video, -.OT_publisher.OT_loading object, -.OT_subscriber.OT_loading object { - display: none; +.OT_publisher.OT_loading .OT_video-element, +.OT_subscriber.OT_loading .OT_video-element { + /*display: none;*/ } +.OT_video-centering { + display: table; + width: 100%; + height: 100%; +} + +.OT_video-container { + display: table-cell; + vertical-align: middle; +} .OT_video-poster { - width: 100%; - height: 100%; - display: none; + position: absolute; + z-index: 1; + width: 100%; + height: 100%; + display: none; - opacity: .25; - background-size: auto 76%; - background-repeat: no-repeat; - background-position: center bottom; - background-image: url(../images/rtc/audioonly-silhouette.svg); + opacity: .25; + + background-repeat: no-repeat; + background-image: url(../images/rtc/audioonly-silhouette.svg); +} + + +.OT_fit-mode-cover .OT_video-poster { + background-size: auto 76%; + background-position: center bottom; +} + +.OT_fit-mode-contain .OT_video-poster { + background-size: contain; + background-position: center; } .OT_audio-level-meter { - position: absolute; - width: 25%; - max-width: 224px; - min-width: 21px; - top: 0; - right: 0; - overflow: hidden; + position: absolute; + width: 25%; + max-width: 224px; + min-width: 21px; + top: 0; + right: 0; + overflow: hidden; } .OT_audio-level-meter:before { - /* makes the height of the container equals its width */ - content: ''; - display: block; - padding-top: 100%; + /* makes the height of the container equals its width */ + content: ''; + display: block; + padding-top: 100%; } .OT_audio-level-meter__bar { - position: absolute; - width: 192%; /* meter value can overflow of 8% */ - height: 192%; - top: -96% /* half of the size */; - right: -96%; - border-radius: 50%; + position: absolute; + width: 192%; /* meter value can overflow of 8% */ + height: 192%; + top: -96% /* half of the size */; + right: -96%; + border-radius: 50%; - background-color: rgba(0, 0, 0, .8); + background-color: rgba(0, 0, 0, .8); } .OT_audio-level-meter__audio-only-img { - position: absolute; - top: 22%; - right: 15%; - width: 40%; + position: absolute; + top: 22%; + right: 15%; + width: 40%; - opacity: .7; + opacity: .7; - background: url(../images/rtc/audioonly-headset.svg) no-repeat center; + background: url(../images/rtc/audioonly-headset.svg) no-repeat center; } .OT_audio-level-meter__audio-only-img:before { - /* makes the height of the container equals its width */ - content: ''; - display: block; - padding-top: 100%; + /* makes the height of the container equals its width */ + content: ''; + display: block; + padding-top: 100%; } .OT_audio-level-meter__value { - position: absolute; - border-radius: 50%; - background-image: radial-gradient(circle, rgba(151,206,0,1) 0%, rgba(151,206,0,0) 100%); + position: absolute; + border-radius: 50%; + background-image: radial-gradient(circle, rgba(151, 206, 0, 1) 0%, rgba(151, 206, 0, 0) 100%); } -.OT_audio-level-meter { +.OT_audio-level-meter.OT_mode-off { display: none; } .OT_audio-level-meter.OT_mode-on, .OT_audio-only .OT_audio-level-meter.OT_mode-auto { - display: block; + display: block; } .OT_video-disabled-indicator { - opacity: 1; - border: none; - display: none; - position: absolute; - background-color: transparent; - background-repeat: no-repeat; - background-position:bottom right; - top: 0; - left: 0; - bottom: 3px; - right: 3px; + opacity: 1; + border: none; + display: none; + position: absolute; + background-color: transparent; + background-repeat: no-repeat; + background-position: bottom right; + top: 0; + left: 0; + bottom: 3px; + right: 3px; } .OT_video-disabled { - background-image: url(../images/rtc/video-disabled.png); + background-image: url(../images/rtc/video-disabled.png); } .OT_video-disabled-warning { - background-image: url(../images/rtc/video-disabled-warning.png); + background-image: url(../images/rtc/video-disabled-warning.png); } .OT_video-disabled-indicator.OT_active { - display: block; + display: block; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/libs/sdk-content/js/dynamic_config.min.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/libs/sdk-content/js/dynamic_config.min.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/libs/sdk-content/js/dynamic_config.min.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/libs/sdk-content/js/dynamic_config.min.js 2015-02-03 14:33:16.000000000 +0000 @@ -1,7 +1,7 @@ -/* - - Copyright (c) 2014 TokBox, Inc. - Released under the MIT license - http://opensource.org/licenses/MIT -*/ +/** + * @license + * Copyright (c) 2014 TokBox, Inc. + * Released under the MIT license + * http://opensource.org/licenses/MIT + */ !function(){TB.Config.replaceWith({global:{exceptionLogging:{enabled:!0,messageLimitPerPartner:100},iceServers:{enabled:!1},instrumentation:{enabled:!1,debugging:!1},tokshow:{textchat:!0}},partners:{change878:{instrumentation:{enabled:!0,debugging:!0}}}})}(TB); \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/libs/sdk.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/libs/sdk.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/content/shared/libs/sdk.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/content/shared/libs/sdk.js 2015-02-03 14:33:16.000000000 +0000 @@ -1,66 +1,30 @@ /** - * @license OpenTok JavaScript Library v2.2.9.1 + * @license OpenTok JavaScript Library v2.4.0 54ae164 HEAD * http://www.tokbox.com/ * * Copyright (c) 2014 TokBox, Inc. * Released under the MIT license * http://opensource.org/licenses/MIT * - * Date: September 08 10:17:05 2014 + * Date: January 08 08:54:40 2015 */ -(function(window) { - if (!window.OT) window.OT = {}; - OT.properties = { - version: 'v2.2.9.1', // The current version (eg. v2.0.4) (This is replaced by gradle) - build: '72b534e', // The current build hash (This is replaced by gradle) - - // Whether or not to turn on debug logging by default - debug: 'false', - // The URL of the tokbox website - websiteURL: 'http://www.tokbox.com', - - // The URL of the CDN - cdnURL: 'http://static.opentok.com', - // The URL to use for logging - loggingURL: 'http://hlg.tokbox.com/prod', - // The anvil API URL - apiURL: 'http://anvil.opentok.com', - - // What protocol to use when connecting to the rumor web socket - messagingProtocol: 'wss', - // What port to use when connection to the rumor web socket - messagingPort: 443, - - // If this environment supports SSL - supportSSL: 'true', - // The CDN to use if we're using SSL - cdnURLSSL: 'https://static.opentok.com', - // The URL to use for logging - loggingURLSSL: 'https://hlg.tokbox.com/prod', - // The anvil API URL to use if we're using SSL - apiURLSSL: 'https://anvil.opentok.com', - - minimumVersion: { - firefox: parseFloat('29'), - chrome: parseFloat('34') - } - }; +!(function(window) { + +!(function(window, OTHelpers, undefined) { -})(window); /** - * @license Common JS Helpers on OpenTok 0.2.0 3fa583f master + * @license Common JS Helpers on OpenTok 0.2.0 ef06638 2014Q4-2.2 * http://www.tokbox.com/ * - * Copyright (c) 2014 TokBox, Inc. - * Released under the MIT license - * http://opensource.org/licenses/MIT + * Copyright (c) 2015 TokBox, Inc. * - * Date: August 08 12:31:42 2014 + * Date: January 08 08:54:29 2015 * */ + // OT Helper Methods // // helpers.js <- the root file @@ -68,182 +32,359 @@ // (i.e. video, dom, etc) // // @example Getting a DOM element by it's id -// var element = OTHelpers('domId'); +// var element = OTHelpers('#domId'); // // /*jshint browser:true, smarttabs:true*/ -!(function(window, undefined) { +// Short cuts to commonly accessed prototypes +var prototypeSlice = Array.prototype.slice; - var OTHelpers = function(domId) { - return document.getElementById(domId); - }; +// RegEx to detect CSS Id selectors +var detectIdSelectors = /^#([\w-]*)$/; - var previousOTHelpers = window.OTHelpers; +// The top-level namespace, also performs basic DOMElement selecting. +// +// @example Get the DOM element with the id of 'domId' +// OTHelpers('#domId') +// +// @example Get all video elements +// OTHelpers('video') +// +// @example Get all elements with the class name of 'foo' +// OTHelpers('.foo') +// +// @example Get all elements with the class name of 'foo', +// and do something with the first. +// var collection = OTHelpers('.foo'); +// console.log(collection.first); +// +// +// The second argument is the context, that is document or parent Element, to +// select from. +// +// @example Get a video element within the element with the id of 'domId' +// OTHelpers('video', OTHelpers('#domId')) +// +// +// +// OTHelpers will accept any of the following and return a collection: +// OTHelpers() +// OTHelpers('css selector', optionalParentNode) +// OTHelpers(DomNode) +// OTHelpers([array of DomNode]) +// +// The collection is a ElementCollection object, see the ElementCollection docs for usage info. +// +var OTHelpers = function(selector, context) { + var results = []; - window.OTHelpers = OTHelpers; + if (typeof(selector) === 'string') { + var idSelector = detectIdSelectors.exec(selector); + context = context || document; + + if (idSelector && idSelector[1]) { + var element = context.getElementById(idSelector[1]); + if (element) results.push(element); + } + else { + results = context.querySelectorAll(selector); + } + } + else if (selector.nodeType || window.XMLHttpRequest && selector instanceof XMLHttpRequest) { + // allow OTHelpers(DOMNode) and OTHelpers(xmlHttpRequest) + results = [selector]; + context = selector; + } + else if (OTHelpers.isArray(selector)) { + results = selector.slice(); + context = null; + } - // A guard to detect when IE has performed cleans on unload - window.___othelpers = true; + return new ElementCollection(results, context); +}; - OTHelpers.keys = Object.keys || function(object) { - var keys = [], hasOwnProperty = Object.prototype.hasOwnProperty; - for(var key in object) { - if(hasOwnProperty.call(object, key)) { - keys.push(key); - } - } - return keys; - }; +// alias $ to OTHelpers for internal use. This is not exposed outside of OTHelpers. +var $ = OTHelpers; - var _each = Array.prototype.forEach || function(iter, ctx) { - for(var idx = 0, count = this.length || 0; idx < count; ++idx) { - if(idx in this) { - iter.call(ctx, this[idx], idx); - } - } - }; +// A helper for converting a NodeList to a JS Array +var nodeListToArray = function nodeListToArray (nodes) { + if ($.env.name !== 'IE' || $.env.version > 9) { + return prototypeSlice.call(nodes); + } - OTHelpers.forEach = function(array, iter, ctx) { - return _each.call(array, iter, ctx); - }; + // IE 9 and below call use Array.prototype.slice.call against + // a NodeList, hence the following + var array = []; - var _map = Array.prototype.map || function(iter, ctx) { - var collect = []; - _each.call(this, function(item, idx) { - collect.push(iter.call(ctx, item, idx)); - }); - return collect; - }; + for (var i=0, num=nodes.length; i= length) { - return -1; +var _map = Array.prototype.map || function(iter, ctx) { + var collect = []; + _each.call(this, function(item, idx) { + collect.push(iter.call(ctx, item, idx)); + }); + return collect; +}; + +OTHelpers.map = function(array, iter) { + return _map.call(array, iter); +}; + +var _filter = Array.prototype.filter || function(iter, ctx) { + var collect = []; + _each.call(this, function(item, idx) { + if(iter.call(ctx, item, idx)) { + collect.push(item); } + }); + return collect; +}; + +OTHelpers.filter = function(array, iter, ctx) { + return _filter.call(array, iter, ctx); +}; - if (pivot < 0) { - pivot = length - Math.abs(pivot); +var _some = Array.prototype.some || function(iter, ctx) { + var any = false; + for(var idx = 0, count = this.length || 0; idx < count; ++idx) { + if(idx in this) { + if(iter.call(ctx, this[idx], idx)) { + any = true; + break; + } } + } + return any; +}; - for (i = pivot; i < length; i++) { - if (this[i] === searchElement) { - return i; +OTHelpers.find = function(array, iter, ctx) { + if (!$.isFunction(iter)) { + throw new TypeError('iter must be a function'); + } + + var any = void 0; + for(var idx = 0, count = array.length || 0; idx < count; ++idx) { + if(idx in array) { + if(iter.call(ctx, array[idx], idx)) { + any = array[idx]; + break; } } + } + return any; +}; + +OTHelpers.findIndex = function(array, iter, ctx) { + if (!$.isFunction(iter)) { + throw new TypeError('iter must be a function'); + } + + for (var i = 0, count = array.length || 0; i < count; ++i) { + if (i in array && iter.call(ctx, array[i], i, array)) { + return i; + } + } + + return -1; +}; + + +OTHelpers.some = function(array, iter, ctx) { + return _some.call(array, iter, ctx); +}; + +var _indexOf = Array.prototype.indexOf || function(searchElement, fromIndex) { + var i, + pivot = (fromIndex) ? fromIndex : 0, + length; + + if (!this) { + throw new TypeError(); + } + + length = this.length; + + if (length === 0 || pivot >= length) { return -1; - }; + } - OTHelpers.arrayIndexOf = function(array, searchElement, fromIndex) { - return _indexOf.call(array, searchElement, fromIndex); - }; + if (pivot < 0) { + pivot = length - Math.abs(pivot); + } - var _bind = Function.prototype.bind || function() { - var args = Array.prototype.slice.call(arguments), - ctx = args.shift(), - fn = this; - return function() { - return fn.apply(ctx, args.concat(Array.prototype.slice.call(arguments))); - }; - }; + for (i = pivot; i < length; i++) { + if (this[i] === searchElement) { + return i; + } + } + return -1; +}; - OTHelpers.bind = function() { - var args = Array.prototype.slice.call(arguments), - fn = args.shift(); - return _bind.apply(fn, args); - }; +OTHelpers.arrayIndexOf = function(array, searchElement, fromIndex) { + return _indexOf.call(array, searchElement, fromIndex); +}; - var _trim = String.prototype.trim || function() { - return this.replace(/^\s+|\s+$/g, ''); +var _bind = Function.prototype.bind || function() { + var args = prototypeSlice.call(arguments), + ctx = args.shift(), + fn = this; + return function() { + return fn.apply(ctx, args.concat(prototypeSlice.call(arguments))); }; +}; - OTHelpers.trim = function(str) { - return _trim.call(str); - }; +OTHelpers.bind = function() { + var args = prototypeSlice.call(arguments), + fn = args.shift(); + return _bind.apply(fn, args); +}; + +var _trim = String.prototype.trim || function() { + return this.replace(/^\s+|\s+$/g, ''); +}; +OTHelpers.trim = function(str) { + return _trim.call(str); +}; + +OTHelpers.noConflict = function() { OTHelpers.noConflict = function() { - OTHelpers.noConflict = function() { - return OTHelpers; - }; - window.OTHelpers = previousOTHelpers; return OTHelpers; }; + window.OTHelpers = previousOTHelpers; + return OTHelpers; +}; - OTHelpers.isNone = function(obj) { - return obj === undefined || obj === null; - }; +OTHelpers.isNone = function(obj) { + return obj === void 0 || obj === null; +}; - OTHelpers.isObject = function(obj) { - return obj === Object(obj); - }; +OTHelpers.isObject = function(obj) { + return obj === Object(obj); +}; - OTHelpers.isFunction = function(obj) { - return !!obj && (obj.toString().indexOf('()') !== -1 || - Object.prototype.toString.call(obj) === '[object Function]'); +OTHelpers.isFunction = function(obj) { + return !!obj && (obj.toString().indexOf('()') !== -1 || + Object.prototype.toString.call(obj) === '[object Function]'); +}; + +OTHelpers.isArray = OTHelpers.isFunction(Array.isArray) && Array.isArray || + function (vArg) { + return Object.prototype.toString.call(vArg) === '[object Array]'; }; - OTHelpers.isArray = OTHelpers.isFunction(Array.isArray) && Array.isArray || - function (vArg) { - return Object.prototype.toString.call(vArg) === '[object Array]'; - }; +OTHelpers.isEmpty = function(obj) { + if (obj === null || obj === void 0) return true; + if (OTHelpers.isArray(obj) || typeof(obj) === 'string') return obj.length === 0; - OTHelpers.isEmpty = function(obj) { - if (obj === null || obj === undefined) return true; - if (OTHelpers.isArray(obj) || typeof(obj) === 'string') return obj.length === 0; - - // Objects without enumerable owned properties are empty. - for (var key in obj) { - if (obj.hasOwnProperty(key)) return false; - } + // Objects without enumerable owned properties are empty. + for (var key in obj) { + if (obj.hasOwnProperty(key)) return false; + } - return true; - }; + return true; +}; // Extend a target object with the properties from one or // more source objects @@ -251,18 +392,18 @@ // @example: // dest = OTHelpers.extend(dest, source1, source2, source3); // - OTHelpers.extend = function(/* dest, source1[, source2, ..., , sourceN]*/) { - var sources = Array.prototype.slice.call(arguments), - dest = sources.shift(); - - OTHelpers.forEach(sources, function(source) { - for (var key in source) { - dest[key] = source[key]; - } - }); +OTHelpers.extend = function(/* dest, source1[, source2, ..., , sourceN]*/) { + var sources = prototypeSlice.call(arguments), + dest = sources.shift(); + + OTHelpers.forEach(sources, function(source) { + for (var key in source) { + dest[key] = source[key]; + } + }); - return dest; - }; + return dest; +}; // Ensures that the target object contains certain defaults. // @@ -271,28 +412,28 @@ // loading: true // loading by default // }); // - OTHelpers.defaults = function(/* dest, defaults1[, defaults2, ..., , defaultsN]*/) { - var sources = Array.prototype.slice.call(arguments), - dest = sources.shift(); - - OTHelpers.forEach(sources, function(source) { - for (var key in source) { - if (dest[key] === void 0) dest[key] = source[key]; - } - }); +OTHelpers.defaults = function(/* dest, defaults1[, defaults2, ..., , defaultsN]*/) { + var sources = prototypeSlice.call(arguments), + dest = sources.shift(); + + OTHelpers.forEach(sources, function(source) { + for (var key in source) { + if (dest[key] === void 0) dest[key] = source[key]; + } + }); - return dest; - }; + return dest; +}; - OTHelpers.clone = function(obj) { - if (!OTHelpers.isObject(obj)) return obj; - return OTHelpers.isArray(obj) ? obj.slice() : OTHelpers.extend({}, obj); - }; +OTHelpers.clone = function(obj) { + if (!OTHelpers.isObject(obj)) return obj; + return OTHelpers.isArray(obj) ? obj.slice() : OTHelpers.extend({}, obj); +}; // Handy do nothing function - OTHelpers.noop = function() {}; +OTHelpers.noop = function() {}; // Returns the number of millisceonds since the the UNIX epoch, this is functionally @@ -300,408 +441,171 @@ // // Where available, we use 'performance.now' which is more accurate and reliable, // otherwise we default to new Date().getTime(). - OTHelpers.now = (function() { - var performance = window.performance || {}, - navigationStart, - now = performance.now || - performance.mozNow || - performance.msNow || - performance.oNow || - performance.webkitNow; - - if (now) { - now = OTHelpers.bind(now, performance); - navigationStart = performance.timing.navigationStart; - - return function() { return navigationStart + now(); }; - } else { - return function() { return new Date().getTime(); }; - } - })(); +OTHelpers.now = (function() { + var performance = window.performance || {}, + navigationStart, + now = performance.now || + performance.mozNow || + performance.msNow || + performance.oNow || + performance.webkitNow; + + if (now) { + now = OTHelpers.bind(now, performance); + navigationStart = performance.timing.navigationStart; + + return function() { return navigationStart + now(); }; + } else { + return function() { return new Date().getTime(); }; + } +})(); - var _browser = function() { - var userAgent = window.navigator.userAgent.toLowerCase(), - appName = window.navigator.appName, - navigatorVendor, - browser = 'unknown', - version = -1; +OTHelpers.canDefineProperty = true; - if (userAgent.indexOf('opera') > -1 || userAgent.indexOf('opr') > -1) { - browser = 'Opera'; +try { + Object.defineProperty({}, 'x', {}); +} catch (err) { + OTHelpers.canDefineProperty = false; +} - if (/opr\/([0-9]{1,}[\.0-9]{0,})/.exec(userAgent) !== null) { - version = parseFloat( RegExp.$1 ); - } +// A helper for defining a number of getters at once. +// +// @example: from inside an object +// OTHelpers.defineGetters(this, { +// apiKey: function() { return _apiKey; }, +// token: function() { return _token; }, +// connected: function() { return this.is('connected'); }, +// capabilities: function() { return _socket.capabilities; }, +// sessionId: function() { return _sessionId; }, +// id: function() { return _sessionId; } +// }); +// +OTHelpers.defineGetters = function(self, getters, enumerable) { + var propsDefinition = {}; - } else if (userAgent.indexOf('firefox') > -1) { - browser = 'Firefox'; + if (enumerable === void 0) enumerable = false; - if (/firefox\/([0-9]{1,}[\.0-9]{0,})/.exec(userAgent) !== null) { - version = parseFloat( RegExp.$1 ); - } + for (var key in getters) { + propsDefinition[key] = { + get: getters[key], + enumerable: enumerable + }; + } - } else if (appName === 'Microsoft Internet Explorer') { - // IE 10 and below - browser = 'IE'; + OTHelpers.defineProperties(self, propsDefinition); +}; - if (/msie ([0-9]{1,}[\.0-9]{0,})/.exec(userAgent) !== null) { - version = parseFloat( RegExp.$1 ); +var generatePropertyFunction = function(object, getter, setter) { + if(getter && !setter) { + return function() { + return getter.call(object); + }; + } else if(getter && setter) { + return function(value) { + if(value !== void 0) { + setter.call(object, value); + } + return getter.call(object); + }; + } else { + return function(value) { + if(value !== void 0) { + setter.call(object, value); } + }; + } +}; - } else if (appName === 'Netscape' && userAgent.indexOf('trident') > -1) { - // IE 11+ +OTHelpers.defineProperties = function(object, getterSetters) { + for (var key in getterSetters) { + object[key] = generatePropertyFunction(object, getterSetters[key].get, + getterSetters[key].set); + } +}; - browser = 'IE'; - if (/trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/.exec(userAgent) !== null) { - version = parseFloat( RegExp.$1 ); - } +// Polyfill Object.create for IE8 +// +// See https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create +// +if (!Object.create) { + Object.create = function (o) { + if (arguments.length > 1) { + throw new Error('Object.create implementation only accepts the first parameter.'); + } + function F() {} + F.prototype = o; + return new F(); + }; +} - } else if (userAgent.indexOf('chrome') > -1) { - browser = 'Chrome'; +// These next bits are included from Underscore.js. The original copyright +// notice is below. +// +// http://underscorejs.org +// (c) 2009-2011 Jeremy Ashkenas, DocumentCloud Inc. +// (c) 2011-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// Underscore may be freely distributed under the MIT license. - if (/chrome\/([0-9]{1,}[\.0-9]{0,})/.exec(userAgent) !== null) { - version = parseFloat( RegExp.$1 ); - } +// Invert the keys and values of an object. The values must be serializable. +OTHelpers.invert = function(obj) { + var result = {}; + for (var key in obj) if (obj.hasOwnProperty(key)) result[obj[key]] = key; + return result; +}; - } else if ((navigatorVendor = window.navigator.vendor) && - navigatorVendor.toLowerCase().indexOf('apple') > -1) { - browser = 'Safari'; +/*jshint browser:true, smarttabs:true*/ - if (/version\/([0-9]{1,}[\.0-9]{0,})/.exec(userAgent) !== null) { - version = parseFloat( RegExp.$1 ); - } - } +// tb_require('../../helpers.js') - return { - browser: browser, - version: version, - iframeNeedsLoad: userAgent.indexOf('webkit') < 0 - }; - }(); +OTHelpers.statable = function(self, possibleStates, initialState, stateChanged, + stateChangedFailed) { + var previousState, + currentState = self.currentState = initialState; + + var setState = function(state) { + if (currentState !== state) { + if (OTHelpers.arrayIndexOf(possibleStates, state) === -1) { + if (stateChangedFailed && OTHelpers.isFunction(stateChangedFailed)) { + stateChangedFailed('invalidState', state); + } + return; + } - OTHelpers.browser = function() { - return _browser.browser; + self.previousState = previousState = currentState; + self.currentState = currentState = state; + if (stateChanged && OTHelpers.isFunction(stateChanged)) stateChanged(state, previousState); + } }; - OTHelpers.browserVersion = function() { - return _browser; - }; + // Returns a number of states and returns true if the current state + // is any of them. + // + // @example + // if (this.is('connecting', 'connected')) { + // // do some stuff + // } + // + self.is = function (/* state0:String, state1:String, ..., stateN:String */) { + return OTHelpers.arrayIndexOf(arguments, currentState) !== -1; + }; - OTHelpers.canDefineProperty = true; - try { - Object.defineProperty({}, 'x', {}); - } catch (err) { - OTHelpers.canDefineProperty = false; - } - -// A helper for defining a number of getters at once. -// -// @example: from inside an object -// OTHelpers.defineGetters(this, { -// apiKey: function() { return _apiKey; }, -// token: function() { return _token; }, -// connected: function() { return this.is('connected'); }, -// capabilities: function() { return _socket.capabilities; }, -// sessionId: function() { return _sessionId; }, -// id: function() { return _sessionId; } -// }); -// - OTHelpers.defineGetters = function(self, getters, enumerable) { - var propsDefinition = {}; - - if (enumerable === void 0) enumerable = false; - - for (var key in getters) { - propsDefinition[key] = { - get: getters[key], - enumerable: enumerable - }; - } - - OTHelpers.defineProperties(self, propsDefinition); - }; - - var generatePropertyFunction = function(object, getter, setter) { - if(getter && !setter) { - return function() { - return getter.call(object); - }; - } else if(getter && setter) { - return function(value) { - if(value !== void 0) { - setter.call(object, value); - } - return getter.call(object); - }; - } else { - return function(value) { - if(value !== void 0) { - setter.call(object, value); - } - }; - } - }; - - OTHelpers.defineProperties = function(object, getterSetters) { - for (var key in getterSetters) { - object[key] = generatePropertyFunction(object, getterSetters[key].get, - getterSetters[key].set); - } - }; - - -// Polyfill Object.create for IE8 -// -// See https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create -// - if (!Object.create) { - Object.create = function (o) { - if (arguments.length > 1) { - throw new Error('Object.create implementation only accepts the first parameter.'); - } - function F() {} - F.prototype = o; - return new F(); - }; - } - - OTHelpers.setCookie = function(key, value) { - try { - localStorage.setItem(key, value); - } catch (err) { - // Store in browser cookie - var date = new Date(); - date.setTime(date.getTime()+(365*24*60*60*1000)); - var expires = '; expires=' + date.toGMTString(); - document.cookie = key + '=' + value + expires + '; path=/'; - } - }; - - OTHelpers.getCookie = function(key) { - var value; - - try { - value = localStorage.getItem('opentok_client_id'); - return value; - } catch (err) { - // Check browser cookies - var nameEQ = key + '='; - var ca = document.cookie.split(';'); - for(var i=0;i < ca.length;i++) { - var c = ca[i]; - while (c.charAt(0) === ' ') { - c = c.substring(1,c.length); - } - if (c.indexOf(nameEQ) === 0) { - value = c.substring(nameEQ.length,c.length); - } - } - - if (value) { - return value; - } - } - - return null; - }; - - -// These next bits are included from Underscore.js. The original copyright -// notice is below. -// -// http://underscorejs.org -// (c) 2009-2011 Jeremy Ashkenas, DocumentCloud Inc. -// (c) 2011-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors -// Underscore may be freely distributed under the MIT license. - - // Invert the keys and values of an object. The values must be serializable. - OTHelpers.invert = function(obj) { - var result = {}; - for (var key in obj) if (obj.hasOwnProperty(key)) result[obj[key]] = key; - return result; - }; - - - // List of HTML entities for escaping. - var entityMap = { - escape: { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - '\'': ''', - '/': '/' - } - }; - - entityMap.unescape = OTHelpers.invert(entityMap.escape); - - // Regexes containing the keys and values listed immediately above. - var entityRegexes = { - escape: new RegExp('[' + OTHelpers.keys(entityMap.escape).join('') + ']', 'g'), - unescape: new RegExp('(' + OTHelpers.keys(entityMap.unescape).join('|') + ')', 'g') - }; - - // Functions for escaping and unescaping strings to/from HTML interpolation. - OTHelpers.forEach(['escape', 'unescape'], function(method) { - OTHelpers[method] = function(string) { - if (string === null || string === undefined) return ''; - return ('' + string).replace(entityRegexes[method], function(match) { - return entityMap[method][match]; - }); - }; - }); - -// By default, Underscore uses ERB-style template delimiters, change the -// following template settings to use alternative delimiters. - OTHelpers.templateSettings = { - evaluate : /<%([\s\S]+?)%>/g, - interpolate : /<%=([\s\S]+?)%>/g, - escape : /<%-([\s\S]+?)%>/g - }; - -// When customizing `templateSettings`, if you don't want to define an -// interpolation, evaluation or escaping regex, we need one that is -// guaranteed not to match. - var noMatch = /(.)^/; - -// Certain characters need to be escaped so that they can be put into a -// string literal. - var escapes = { - '\'': '\'', - '\\': '\\', - '\r': 'r', - '\n': 'n', - '\t': 't', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; - - var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; - -// JavaScript micro-templating, similar to John Resig's implementation. -// Underscore templating handles arbitrary delimiters, preserves whitespace, -// and correctly escapes quotes within interpolated code. - OTHelpers.template = function(text, data, settings) { - var render; - settings = OTHelpers.defaults({}, settings, OTHelpers.templateSettings); - - // Combine delimiters into one regular expression via alternation. - var matcher = new RegExp([ - (settings.escape || noMatch).source, - (settings.interpolate || noMatch).source, - (settings.evaluate || noMatch).source - ].join('|') + '|$', 'g'); - - // Compile the template source, escaping string literals appropriately. - var index = 0; - var source = '__p+=\''; - text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { - source += text.slice(index, offset) - .replace(escaper, function(match) { return '\\' + escapes[match]; }); - - if (escape) { - source += '\'+\n((__t=(' + escape + '))==null?\'\':OTHelpers.escape(__t))+\n\''; - } - if (interpolate) { - source += '\'+\n((__t=(' + interpolate + '))==null?\'\':__t)+\n\''; - } - if (evaluate) { - source += '\';\n' + evaluate + '\n__p+=\''; - } - index = offset + match.length; - return match; - }); - source += '\';\n'; - - // If a variable is not specified, place data values in local scope. - if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; - - source = 'var __t,__p=\'\',__j=Array.prototype.join,' + - 'print=function(){__p+=__j.call(arguments,\'\');};\n' + - source + 'return __p;\n'; - - try { - // evil is necessary for the new Function line - /*jshint evil:true */ - render = new Function(settings.variable || 'obj', source); - } catch (e) { - e.source = source; - throw e; - } - - if (data) return render(data); - var template = function(data) { - return render.call(this, data); - }; - - // Provide the compiled function source as a convenience for precompilation. - template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; - - return template; - }; - -})(window); - -/*jshint browser:true, smarttabs:true*/ - -// tb_require('../../helpers.js') - -(function(window, OTHelpers, undefined) { - - OTHelpers.statable = function(self, possibleStates, initialState, stateChanged, - stateChangedFailed) { - var previousState, - currentState = self.currentState = initialState; - - var setState = function(state) { - if (currentState !== state) { - if (OTHelpers.arrayIndexOf(possibleStates, state) === -1) { - if (stateChangedFailed && OTHelpers.isFunction(stateChangedFailed)) { - stateChangedFailed('invalidState', state); - } - return; - } - - self.previousState = previousState = currentState; - self.currentState = currentState = state; - if (stateChanged && OTHelpers.isFunction(stateChanged)) stateChanged(state, previousState); - } - }; - - - // Returns a number of states and returns true if the current state - // is any of them. - // - // @example - // if (this.is('connecting', 'connected')) { - // // do some stuff - // } - // - self.is = function (/* state0:String, state1:String, ..., stateN:String */) { - return OTHelpers.arrayIndexOf(arguments, currentState) !== -1; - }; - - - // Returns a number of states and returns true if the current state - // is none of them. - // - // @example - // if (this.isNot('connecting', 'connected')) { - // // do some stuff - // } - // - self.isNot = function (/* state0:String, state1:String, ..., stateN:String */) { - return OTHelpers.arrayIndexOf(arguments, currentState) === -1; - }; - - return setState; + // Returns a number of states and returns true if the current state + // is none of them. + // + // @example + // if (this.isNot('connecting', 'connected')) { + // // do some stuff + // } + // + self.isNot = function (/* state0:String, state1:String, ..., stateN:String */) { + return OTHelpers.arrayIndexOf(arguments, currentState) === -1; }; -})(window, window.OTHelpers); + return setState; +}; /*! * This is a modified version of Robert Kieffer awesome uuid.js library. @@ -720,7 +624,7 @@ /*global crypto:true, Uint32Array:true, Buffer:true */ /*jshint browser:true, smarttabs:true*/ -(function(window, OTHelpers, undefined) { +(function() { // Unique ID creation requires a high quality random # generator, but // Math.random() does not guarantee "cryptographic quality". So we feature @@ -843,3758 +747,3952 @@ OTHelpers.uuid = uuid; -}(window, window.OTHelpers)); -/*jshint browser:true, smarttabs:true*/ +}()); +/*jshint browser:true, smarttabs:true */ // tb_require('../helpers.js') -(function(window, OTHelpers, undefined) { - OTHelpers.useLogHelpers = function(on){ +var getErrorLocation; - // Log levels for OTLog.setLogLevel - on.DEBUG = 5; - on.LOG = 4; - on.INFO = 3; - on.WARN = 2; - on.ERROR = 1; - on.NONE = 0; - - var _logLevel = on.NONE, - _logs = [], - _canApplyConsole = true; +// Properties that we'll acknowledge from the JS Error object +var safeErrorProps = [ + 'description', + 'fileName', + 'lineNumber', + 'message', + 'name', + 'number', + 'stack' +]; - try { - Function.prototype.bind.call(window.console.log, window.console); - } catch (err) { - _canApplyConsole = false; + +// OTHelpers.Error +// +// A construct to contain error information that also helps with extracting error +// context, such as stack trace. +// +// @constructor +// @memberof OTHelpers +// @method Error +// +// @param {String} message +// Optional. The error message +// +// @param {Object} props +// Optional. A dictionary of properties containing extra Error info. +// +// +// @example Create a simple error with juts a custom message +// var error = new OTHelpers.Error('Something Broke!'); +// error.message === 'Something Broke!'; +// +// @example Create an Error with a message and a name +// var error = new OTHelpers.Error('Something Broke!', 'FooError'); +// error.message === 'Something Broke!'; +// error.name === 'FooError'; +// +// @example Create an Error with a message, name, and custom properties +// var error = new OTHelpers.Error('Something Broke!', 'FooError', { +// foo: 'bar', +// listOfImportantThings: [1,2,3,4] +// }); +// error.message === 'Something Broke!'; +// error.name === 'FooError'; +// error.foo === 'bar'; +// error.listOfImportantThings == [1,2,3,4]; +// +// @example Create an Error from a Javascript Error +// var error = new OTHelpers.Error(domSyntaxError); +// error.message === domSyntaxError.message; +// error.name === domSyntaxError.name === 'SyntaxError'; +// // ...continues for each properties of domSyntaxError +// +// @references +// * https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi +// * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack +// * http://www.w3.org/TR/dom/#interface-domerror +// +// +// @todo +// * update usage in OTMedia +// * replace error handling in OT.js +// * normalise stack behaviour under Chrome/Node/Safari with other browsers +// * unit test for stack parsing +// +// +OTHelpers.Error = function (message, name, props) { + switch (arguments.length) { + case 1: + if ($.isObject(message)) { + props = message; + name = void 0; + message = void 0; + } + // Otherwise it's the message + break; + + case 2: + if ($.isObject(name)) { + props = name; + name = void 0; } + // Otherwise name is actually the name - // Some objects can't be logged in the console, mostly these are certain - // types of native objects that are exposed to JS. This is only really a - // problem with IE, hence only the IE version does anything. - var makeLogArgumentsSafe = function(args) { return args; }; + break; + } - if (OTHelpers.browser() === 'IE') { - makeLogArgumentsSafe = function(args) { - return [toDebugString(Array.prototype.slice.apply(args))]; - }; + if ( props instanceof Error) { + // Special handling of this due to Chrome weirdness. It seems that + // properties of the Error object, and it's children, are not + // enumerable in Chrome? + for (var i = 0, num = safeErrorProps.length; i < num; ++i) { + this[safeErrorProps[i]] = props[safeErrorProps[i]]; + } + } + else if ( $.isObject(props)) { + // Use an custom properties that are provided + for (var key in props) { + if (props.hasOwnProperty(key)) { + this[key] = props[key]; + } } + } - // Generates a logging method for a particular method and log level. - // - // Attempts to handle the following cases: - // * the desired log method doesn't exist, call fallback (if available) instead - // * the console functionality isn't available because the developer tools (in IE) - // aren't open, call fallback (if available) - // * attempt to deal with weird IE hosted logging methods as best we can. - // - function generateLoggingMethod(method, level, fallback) { - return function() { - if (on.shouldLog(level)) { - var cons = window.console, - args = makeLogArgumentsSafe(arguments); - - // In IE, window.console may not exist if the developer tools aren't open - // This also means that cons and cons[method] can appear at any moment - // hence why we retest this every time. - if (cons && cons[method]) { - // the desired console method isn't a real object, which means - // that we can't use apply on it. We force it to be a real object - // using Function.bind, assuming that's available. - if (cons[method].apply || _canApplyConsole) { - if (!cons[method].apply) { - cons[method] = Function.prototype.bind.call(cons[method], cons); - } + // If any of the fundamental properties are missing then try and + // extract them. + if ( !(this.fileName && this.lineNumber && this.columnNumber && this.stack) ) { + var err = getErrorLocation(); - cons[method].apply(cons, args); - } - else { - // This isn't the same result as the above, but it's better - // than nothing. - cons[method](args); - } - } - else if (fallback) { - fallback.apply(on, args); + if (!this.fileName && err.fileName) { + this.fileName = err.fileName; + } - // Skip appendToLogs, we delegate entirely to the fallback - return; - } + if (!this.lineNumber && err.lineNumber) { + this.lineNumber = err.lineNumber; + } - appendToLogs(method, makeLogArgumentsSafe(arguments)); - } - }; + if (!this.columnNumber && err.columnNumber) { + this.columnNumber = err.columnNumber; } - on.log = generateLoggingMethod('log', on.LOG); + if (!this.stack && err.stack) { + this.stack = err.stack; + } + } - // Generate debug, info, warn, and error logging methods, these all fallback to on.log - on.debug = generateLoggingMethod('debug', on.DEBUG, on.log); - on.info = generateLoggingMethod('info', on.INFO, on.log); - on.warn = generateLoggingMethod('warn', on.WARN, on.log); - on.error = generateLoggingMethod('error', on.ERROR, on.log); + if (!this.message && message) this.message = message; + if (!this.name && name) this.name = name; +}; +OTHelpers.Error.prototype.toString = +OTHelpers.Error.prototype.valueOf = function() { + var locationDetails = ''; + if (this.fileName) locationDetails += ' ' + this.fileName; + if (this.lineNumber) { + locationDetails += ' ' + this.lineNumber; + if (this.columnNumber) locationDetails += ':' + this.columnNumber; + } - on.setLogLevel = function(level) { - _logLevel = typeof(level) === 'number' ? level : 0; - on.debug('TB.setLogLevel(' + _logLevel + ')'); - return _logLevel; - }; + return '<' + (this.name ? this.name + ' ' : '') + this.message + locationDetails + '>'; +}; - on.getLogs = function() { - return _logs; - }; - // Determine if the level is visible given the current logLevel. - on.shouldLog = function(level) { - return _logLevel >= level; +// Normalise err.stack so that it is the same format as the other browsers +// We skip the first two frames so that we don't capture getErrorLocation() and +// the callee. +// +// Used by Environments that support the StackTrace API. (Chrome, Node, Opera) +// +var prepareStackTrace = function prepareStackTrace (_, stack){ + return $.map(stack.slice(2), function(frame) { + var _f = { + fileName: frame.getFileName(), + linenumber: frame.getLineNumber(), + columnNumber: frame.getColumnNumber() }; - // Format the current time nicely for logging. Returns the current - // local time. - function formatDateStamp() { - var now = new Date(); - return now.toLocaleTimeString() + now.getMilliseconds(); - } + if (frame.getFunctionName()) _f.functionName = frame.getFunctionName(); + if (frame.getMethodName()) _f.methodName = frame.getMethodName(); + if (frame.getThis()) _f.self = frame.getThis(); + + return _f; + }); +}; + - function toJson(object) { +// Black magic to retrieve error location info for various environments +getErrorLocation = function getErrorLocation () { + var info = {}, + callstack, + errLocation, + err; + + switch ($.env.name) { + case 'Firefox': + case 'Safari': + case 'IE': + + if ($.env.name === 'IE') { + err = new Error(); + } + else { try { - return JSON.stringify(object); - } catch(e) { - return object.toString(); + window.call.js.is.explody; } + catch(e) { err = e; } } - function toDebugString(object) { - var components = []; + callstack = err.stack.split('\n'); - if (typeof(object) === 'undefined') { - // noop - } - else if (object === null) { - components.push('NULL'); - } - else if (OTHelpers.isArray(object)) { - for (var i=0; i 3) info.columnNumber = parseInt(errLocation[4], 10); + } + break; + + case 'Chrome': + case 'Node': + case 'Opera': + var currentPST = Error.prepareStackTrace; + Error.prepareStackTrace = prepareStackTrace; + err = new Error(); + info.stack = err.stack; + Error.prepareStackTrace = currentPST; + + var topFrame = info.stack[0]; + info.lineNumber = topFrame.lineNumber; + info.columnNumber = topFrame.columnNumber; + info.fileName = topFrame.fileName; + if (topFrame.functionName) info.functionName = topFrame.functionName; + if (topFrame.methodName) info.methodName = topFrame.methodName; + if (topFrame.self) info.self = topFrame.self; + break; + + default: + err = new Error(); + if (err.stack) info.stack = err.stack.split('\n'); + break; + } - OTHelpers.useLogHelpers(OTHelpers); - OTHelpers.setLogLevel(OTHelpers.ERROR); + if (err.message) info.message = err.message; + return info; +}; -})(window, window.OTHelpers); /*jshint browser:true, smarttabs:true*/ +/* global process */ // tb_require('../helpers.js') -// DOM helpers -(function(window, OTHelpers, undefined) { - // Helper function for adding event listeners to dom elements. - // WARNING: This doesn't preserve event types, your handler could - // be getting all kinds of different parameters depending on the browser. - // You also may have different scopes depending on the browser and bubbling - // and cancelable are not supported. - OTHelpers.on = function(element, eventName, handler) { - if (element.addEventListener) { - element.addEventListener(eventName, handler, false); - } else if (element.attachEvent) { - element.attachEvent('on' + eventName, handler); - } else { - var oldHandler = element['on'+eventName]; - element['on'+eventName] = function() { - handler.apply(this, arguments); - if (oldHandler) oldHandler.apply(this, arguments); - }; - } - return element; - }; +// OTHelpers.env +// +// Contains information about the current environment. +// * **OTHelpers.env.name** The name of the Environment (Chrome, FF, Node, etc) +// * **OTHelpers.env.version** Usually a Float, except in Node which uses a String +// * **OTHelpers.env.userAgent** The raw user agent +// * **OTHelpers.env.versionGreaterThan** A helper method that returns true if the +// current version is greater than the argument +// +// Example +// if (OTHelpers.env.versionGreaterThan('0.10.30')) { +// // do something +// } +// +(function() { + // @todo make exposing userAgent unnecessary + var version = -1; - // Helper function for removing event listeners from dom elements. - OTHelpers.off = function(element, eventName, handler) { - if (element.removeEventListener) { - element.removeEventListener (eventName, handler,false); + // Returns true if otherVersion is greater than the current environment + // version. + var versionGEThan = function versionGEThan (otherVersion) { + if (otherVersion === version) return true; + + if (typeof(otherVersion) === 'number' && typeof(version) === 'number') { + return otherVersion > version; + } + + // The versions have multiple components (i.e. 0.10.30) and + // must be compared piecewise. + // Note: I'm ignoring the case where one version has multiple + // components and the other doesn't. + var v1 = otherVersion.split('.'), + v2 = version.split('.'), + versionLength = (v1.length > v2.length ? v2 : v1).length; + + for (var i = 0; i < versionLength; ++i) { + if (parseInt(v1[i], 10) > parseInt(v2[i], 10)) { + return true; + } } - else if (element.detachEvent) { - element.detachEvent('on' + eventName, handler); + + // Special case, v1 has extra components but the initial components + // were identical, we assume this means newer but it might also mean + // that someone changed versioning systems. + if (i < v1.length) { + return true; } - }; -})(window, window.OTHelpers); + return false; + }; -/*jshint browser:true, smarttabs:true*/ + var env = function() { + if (typeof(process) !== 'undefined' && + typeof(process.versions) !== 'undefined' && + typeof(process.versions.node) === 'string') { -// tb_require('../helpers.js') -// tb_require('./dom_events.js') + version = process.versions.node; + if (version.substr(1) === 'v') version = version.substr(1); -(function(window, OTHelpers, undefined) { + // Special casing node to avoid gating window.navigator. + // Version will be a string rather than a float. + return { + name: 'Node', + version: version, + userAgent: 'Node ' + version, + iframeNeedsLoad: false, + versionGreaterThan: versionGEThan + }; + } - var _domReady = typeof(document) === 'undefined' || - document.readyState === 'complete' || - (document.readyState === 'interactive' && document.body), + var userAgent = window.navigator.userAgent.toLowerCase(), + appName = window.navigator.appName, + navigatorVendor, + name = 'unknown'; - _loadCallbacks = [], - _unloadCallbacks = [], - _domUnloaded = false, + if (userAgent.indexOf('opera') > -1 || userAgent.indexOf('opr') > -1) { + name = 'Opera'; - onDomReady = function() { - _domReady = true; + if (/opr\/([0-9]{1,}[\.0-9]{0,})/.exec(userAgent) !== null) { + version = parseFloat( RegExp.$1 ); + } - // This is making an assumption about there being only one "window" - // that we care about. - OTHelpers.on(window, 'unload', onDomUnload); + } else if (userAgent.indexOf('firefox') > -1) { + name = 'Firefox'; - OTHelpers.forEach(_loadCallbacks, function(listener) { - listener[0].call(listener[1]); - }); + if (/firefox\/([0-9]{1,}[\.0-9]{0,})/.exec(userAgent) !== null) { + version = parseFloat( RegExp.$1 ); + } - _loadCallbacks = []; - }, + } else if (appName === 'Microsoft Internet Explorer') { + // IE 10 and below + name = 'IE'; - onDomUnload = function() { - _domUnloaded = true; + if (/msie ([0-9]{1,}[\.0-9]{0,})/.exec(userAgent) !== null) { + version = parseFloat( RegExp.$1 ); + } - OTHelpers.forEach(_unloadCallbacks, function(listener) { - listener[0].call(listener[1]); - }); + } else if (appName === 'Netscape' && userAgent.indexOf('trident') > -1) { + // IE 11+ - _unloadCallbacks = []; - }; + name = 'IE'; + + if (/trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/.exec(userAgent) !== null) { + version = parseFloat( RegExp.$1 ); + } + } else if (userAgent.indexOf('chrome') > -1) { + name = 'Chrome'; - OTHelpers.onDOMLoad = function(cb, context) { - if (OTHelpers.isReady()) { - cb.call(context); - return; - } + if (/chrome\/([0-9]{1,}[\.0-9]{0,})/.exec(userAgent) !== null) { + version = parseFloat( RegExp.$1 ); + } - _loadCallbacks.push([cb, context]); - }; + } else if ((navigatorVendor = window.navigator.vendor) && + navigatorVendor.toLowerCase().indexOf('apple') > -1) { + name = 'Safari'; - OTHelpers.onDOMUnload = function(cb, context) { - if (this.isDOMUnloaded()) { - cb.call(context); - return; + if (/version\/([0-9]{1,}[\.0-9]{0,})/.exec(userAgent) !== null) { + version = parseFloat( RegExp.$1 ); + } } - _unloadCallbacks.push([cb, context]); - }; + return { + name: name, + version: version, + userAgent: window.navigator.userAgent, + iframeNeedsLoad: userAgent.indexOf('webkit') < 0, + versionGreaterThan: versionGEThan + }; + }(); - OTHelpers.isReady = function() { - return !_domUnloaded && _domReady; - }; - OTHelpers.isDOMUnloaded = function() { - return _domUnloaded; + OTHelpers.env = env; + + OTHelpers.browser = function() { + return OTHelpers.env.name; }; + OTHelpers.browserVersion = function() { + return OTHelpers.env; + }; - if (_domReady) { - onDomReady(); - } else if(typeof(document) !== 'undefined') { - if (document.addEventListener) { - document.addEventListener('DOMContentLoaded', onDomReady, false); - } else if (document.attachEvent) { - // This is so onLoad works in IE, primarily so we can show the upgrade to Chrome popup - document.attachEvent('onreadystatechange', function() { - if (document.readyState === 'complete') onDomReady(); - }); - } - } +})(); +/*jshint browser:false, smarttabs:true*/ +/* global window, require */ -})(window, window.OTHelpers); +// tb_require('../../helpers.js') +// tb_require('../environment.js') -/*jshint browser:true, smarttabs:true*/ +if (window.OTHelpers.env.name === 'Node') { + var request = require('request'); -// tb_require('../helpers.js') + OTHelpers.request = function(url, options, callback) { + var completion = function(error, response, body) { + var event = {response: response, body: body}; -(function(window, OTHelpers, undefined) { + // We need to detect things that Request considers a success, + // but we consider to be failures. + if (!error && response.statusCode >= 200 && + (response.statusCode < 300 || response.statusCode === 304) ) { + callback(null, event); + } else { + callback(error, event); + } + }; - OTHelpers.castToBoolean = function(value, defaultValue) { - if (value === undefined) return defaultValue; - return value === 'true' || value === true; + if (options.method.toLowerCase() === 'get') { + request.get(url, completion); + } + else { + request.post(url, options.body, completion); + } }; - OTHelpers.roundFloat = function(value, places) { - return Number(value.toFixed(places)); - }; + OTHelpers.getJSON = function(url, options, callback) { + var extendedHeaders = require('underscore').extend( + { + 'Accept': 'application/json' + }, + options.headers || {} + ); -})(window, window.OTHelpers); + request.get({ + url: url, + headers: extendedHeaders, + json: true + }, function(err, response) { + callback(err, response && response.body); + }); + }; +} /*jshint browser:true, smarttabs:true*/ -// tb_require('../helpers.js') +// tb_require('../../helpers.js') +// tb_require('../environment.js') -(function(window, OTHelpers, undefined) { +function formatPostData(data) { //, contentType + // If it's a string, we assume it's properly encoded + if (typeof(data) === 'string') return data; - var capabilities = {}; + var queryString = []; - // Registers a new capability type and a function that will indicate - // whether this client has that capability. - // - // OTHelpers.registerCapability('bundle', function() { - // return OTHelpers.hasCapabilities('webrtc') && - // (OTHelpers.browser() === 'Chrome' || TBPlugin.isInstalled()); - // }); - // - OTHelpers.registerCapability = function(name, callback) { - var _name = name.toLowerCase(); + for (var key in data) { + queryString.push( + encodeURIComponent(key) + '=' + encodeURIComponent(data[key]) + ); + } - if (capabilities.hasOwnProperty(_name)) { - OTHelpers.error('Attempted to register', name, 'capability more than once'); + return queryString.join('&').replace(/\+/g, '%20'); +} + +if (window.OTHelpers.env.name !== 'Node') { + + OTHelpers.xdomainRequest = function(url, options, callback) { + /*global XDomainRequest*/ + var xdr = new XDomainRequest(), + _options = options || {}, + _method = _options.method.toLowerCase(); + + if(!_method) { + callback(new Error('No HTTP method specified in options')); return; } - if (!OTHelpers.isFunction(callback)) { - OTHelpers.error('Attempted to register', name, - 'capability with a callback that isn\' a function'); + _method = _method.toUpperCase(); + + if(!(_method === 'GET' || _method === 'POST')) { + callback(new Error('HTTP method can only be ')); return; } - memoriseCapabilityTest(_name, callback); - }; + function done(err, event) { + xdr.onload = xdr.onerror = xdr.ontimeout = function() {}; + xdr = void 0; + callback(err, event); + } - // Wrap up a capability test in a function that memorises the - // result. - var memoriseCapabilityTest = function (name, callback) { - capabilities[name] = function() { - var result = callback(); - capabilities[name] = function() { - return result; - }; + xdr.onload = function() { + done(null, { + target: { + responseText: xdr.responseText, + headers: { + 'content-type': xdr.contentType + } + } + }); + }; - return result; + xdr.onerror = function() { + done(new Error('XDomainRequest of ' + url + ' failed')); }; - }; - var testCapability = function (name) { - return capabilities[name](); - }; + xdr.ontimeout = function() { + done(new Error('XDomainRequest of ' + url + ' timed out')); + }; + xdr.open(_method, url); + xdr.send(options.body && formatPostData(options.body)); - // Returns true if all of the capability names passed in - // exist and are met. - // - // OTHelpers.hasCapabilities('bundle', 'rtcpMux') - // - OTHelpers.hasCapabilities = function(/* capability1, capability2, ..., capabilityN */) { - var capNames = Array.prototype.slice.call(arguments), - name; + }; - for (var i=0; i= 200 && (status < 300 || status === 304) ) { + callback(null, event); + } else { + callback(event); + } + }); -/*jshint browser:true, smarttabs:true*/ + OTHelpers.on(request, 'error', callback); + } -// tb_require('../helpers.js') -// tb_require('./capabilities.js') + request.open(options.method, url, true); -(function(window, OTHelpers, undefined) { + if (!_options.headers) _options.headers = {}; - // Indicates if the client supports WebSockets. - OTHelpers.registerCapability('websockets', function() { - return 'WebSocket' in window; - }); + for (var name in _options.headers) { + request.setRequestHeader(name, _options.headers[name]); + } -})(window, window.OTHelpers); + request.send(options.body && formatPostData(options.body)); + }; -/*jshint browser:true, smarttabs:true*/ -// tb_require('../helpers.js') -// tb_require('../vendor/uuid.js') -// tb_require('./dom_events.js') + OTHelpers.getJSON = function(url, options, callback) { + options = options || {}; -(function(window, OTHelpers, undefined) { + var done = function(error, event) { + if(error) { + callback(error, event && event.target && event.target.responseText); + } else { + var response; - var _callAsync; + try { + response = JSON.parse(event.target.responseText); + } catch(e) { + // Badly formed JSON + callback(e, event && event.target && event.target.responseText); + return; + } - // Is true if window.postMessage is supported. - // This is not quite as simple as just looking for - // window.postMessage as some older versions of IE - // have a broken implementation of it. - // - var supportsPostMessage = (function () { - if (window.postMessage) { - // Check to see if postMessage fires synchronously, - // if it does, then the implementation of postMessage - // is broken. - var postMessageIsAsynchronous = true; - var oldOnMessage = window.onmessage; - window.onmessage = function() { - postMessageIsAsynchronous = false; - }; - window.postMessage('', '*'); - window.onmessage = oldOnMessage; - return postMessageIsAsynchronous; - } - })(); - - if (supportsPostMessage) { - var timeouts = [], - messageName = 'OTHelpers.' + OTHelpers.uuid.v4() + '.zero-timeout'; - - var removeMessageHandler = function() { - timeouts = []; - - if(window.removeEventListener) { - window.removeEventListener('message', handleMessage); - } else if(window.detachEvent) { - window.detachEvent('onmessage', handleMessage); + callback(null, response, event); } }; - var handleMessage = function(event) { - if (event.source === window && - event.data === messageName) { + if(options.xdomainrequest) { + OTHelpers.xdomainRequest(url, { method: 'GET' }, done); + } else { + var extendedHeaders = OTHelpers.extend({ + 'Accept': 'application/json' + }, options.headers || {}); - if(OTHelpers.isFunction(event.stopPropagation)) { - event.stopPropagation(); - } - event.cancelBubble = true; + OTHelpers.get(url, OTHelpers.extend(options || {}, { + headers: extendedHeaders + }), done); + } - if (!window.___othelpers) { - removeMessageHandler(); - return; - } + }; - if (timeouts.length > 0) { - var args = timeouts.shift(), - fn = args.shift(); +} +/*jshint browser:true, smarttabs:true*/ - fn.apply(null, args); - } - } - }; +// tb_require('../helpers.js') +// tb_require('./environment.js') - // Ensure that we don't receive messages after unload - // Yes, this seems to really happen in IE sometimes, usually - // when iFrames are involved. - OTHelpers.on(window, 'unload', removeMessageHandler); +OTHelpers.useLogHelpers = function(on){ - if(window.addEventListener) { - window.addEventListener('message', handleMessage, true); - } else if(window.attachEvent) { - window.attachEvent('onmessage', handleMessage); - } + // Log levels for OTLog.setLogLevel + on.DEBUG = 5; + on.LOG = 4; + on.INFO = 3; + on.WARN = 2; + on.ERROR = 1; + on.NONE = 0; + + var _logLevel = on.NONE, + _logs = [], + _canApplyConsole = true; - _callAsync = function (/* fn, [arg1, arg2, ..., argN] */) { - timeouts.push(Array.prototype.slice.call(arguments)); - window.postMessage(messageName, '*'); - }; + try { + Function.prototype.bind.call(window.console.log, window.console); + } catch (err) { + _canApplyConsole = false; } - else { - _callAsync = function (/* fn, [arg1, arg2, ..., argN] */) { - var args = Array.prototype.slice.call(arguments), - fn = args.shift(); - setTimeout(function() { - fn.apply(null, args); - }, 0); + // Some objects can't be logged in the console, mostly these are certain + // types of native objects that are exposed to JS. This is only really a + // problem with IE, hence only the IE version does anything. + var makeLogArgumentsSafe = function(args) { return args; }; + + if (OTHelpers.env.name === 'IE') { + makeLogArgumentsSafe = function(args) { + return [toDebugString(prototypeSlice.apply(args))]; }; } - - // Calls the function +fn+ asynchronously with the current execution. - // This is most commonly used to execute something straight after - // the current function. - // - // Any arguments in addition to +fn+ will be passed to +fn+ when it's - // called. - // - // You would use this inplace of setTimeout(fn, 0) type constructs. callAsync - // is preferable as it executes in a much more predictable time window, - // unlike setTimeout which could execute anywhere from 2ms to several thousand - // depending on the browser/context. + // Generates a logging method for a particular method and log level. // - // It does this using window.postMessage, although if postMessage won't - // work it will fallback to setTimeout. + // Attempts to handle the following cases: + // * the desired log method doesn't exist, call fallback (if available) instead + // * the console functionality isn't available because the developer tools (in IE) + // aren't open, call fallback (if available) + // * attempt to deal with weird IE hosted logging methods as best we can. // - OTHelpers.callAsync = _callAsync; + function generateLoggingMethod(method, level, fallback) { + return function() { + if (on.shouldLog(level)) { + var cons = window.console, + args = makeLogArgumentsSafe(arguments); + + // In IE, window.console may not exist if the developer tools aren't open + // This also means that cons and cons[method] can appear at any moment + // hence why we retest this every time. + if (cons && cons[method]) { + // the desired console method isn't a real object, which means + // that we can't use apply on it. We force it to be a real object + // using Function.bind, assuming that's available. + if (cons[method].apply || _canApplyConsole) { + if (!cons[method].apply) { + cons[method] = Function.prototype.bind.call(cons[method], cons); + } + cons[method].apply(cons, args); + } + else { + // This isn't the same result as the above, but it's better + // than nothing. + cons[method](args); + } + } + else if (fallback) { + fallback.apply(on, args); - // Wraps +handler+ in a function that will execute it asynchronously - // so that it doesn't interfere with it's exceution context if it raises - // an exception. - OTHelpers.createAsyncHandler = function(handler) { - return function() { - var args = Array.prototype.slice.call(arguments); + // Skip appendToLogs, we delegate entirely to the fallback + return; + } - OTHelpers.callAsync(function() { - handler.apply(null, args); - }); + appendToLogs(method, makeLogArgumentsSafe(arguments)); + } }; - }; + } -})(window, window.OTHelpers); + on.log = generateLoggingMethod('log', on.LOG); -/*jshint browser:true, smarttabs:true*/ -/*global jasmine:true*/ + // Generate debug, info, warn, and error logging methods, these all fallback to on.log + on.debug = generateLoggingMethod('debug', on.DEBUG, on.log); + on.info = generateLoggingMethod('info', on.INFO, on.log); + on.warn = generateLoggingMethod('warn', on.WARN, on.log); + on.error = generateLoggingMethod('error', on.ERROR, on.log); -// tb_require('../../helpers.js') -// tb_require('../callbacks.js') -(function(window, OTHelpers, undefined) { + on.setLogLevel = function(level) { + _logLevel = typeof(level) === 'number' ? level : 0; + on.debug('TB.setLogLevel(' + _logLevel + ')'); + return _logLevel; + }; -/** -* This base class defines the on, once, and off -* methods of objects that can dispatch events. -* -* @class EventDispatcher -*/ - OTHelpers.eventing = function(self, syncronous) { - var _events = {}; + on.getLogs = function() { + return _logs; + }; + // Determine if the level is visible given the current logLevel. + on.shouldLog = function(level) { + return _logLevel >= level; + }; - // Call the defaultAction, passing args - function executeDefaultAction(defaultAction, args) { - if (!defaultAction) return; + // Format the current time nicely for logging. Returns the current + // local time. + function formatDateStamp() { + var now = new Date(); + return now.toLocaleTimeString() + now.getMilliseconds(); + } - defaultAction.apply(null, args.slice()); + function toJson(object) { + try { + return JSON.stringify(object); + } catch(e) { + return object.toString(); } + } - // Execute each handler in +listeners+ with +args+. - // - // Each handler will be executed async. On completion the defaultAction - // handler will be executed with the args. - // - // @param [Array] listeners - // An array of functions to execute. Each will be passed args. - // - // @param [Array] args - // An array of arguments to execute each function in +listeners+ with. - // - // @param [String] name - // The name of this event. - // - // @param [Function, Null, Undefined] defaultAction - // An optional function to execute after every other handler. This will execute even - // if +listeners+ is empty. +defaultAction+ will be passed args as a normal - // handler would. - // - // @return Undefined - // - function executeListenersAsyncronously(name, args, defaultAction) { - var listeners = _events[name]; - if (!listeners || listeners.length === 0) return; + function toDebugString(object) { + var components = []; - var listenerAcks = listeners.length; + if (typeof(object) === 'undefined') { + // noop + } + else if (object === null) { + components.push('NULL'); + } + else if (OTHelpers.isArray(object)) { + for (var i=0; i Hello OpenTok: asdf - // - // - // @param [String] eventName - // The name of this event. - // - // @param [Array] arguments - // Any additional arguments beyond +eventName+ will be passed to the handlers. - // - // @return this - // - self.trigger = function(eventName) { - if (!_events[eventName] || _events[eventName].length === 0) { - return; - } +/*jshint browser:true, smarttabs:true*/ - var args = Array.prototype.slice.call(arguments); +// tb_require('../helpers.js') +// tb_require('./dom_events.js') - // Remove the eventName arg - args.shift(); +(function() { - executeListeners(eventName, args); + var _domReady = typeof(document) === 'undefined' || + document.readyState === 'complete' || + (document.readyState === 'interactive' && document.body), - return this; - }; + _loadCallbacks = [], + _unloadCallbacks = [], + _domUnloaded = false, - /** - * Adds an event handler function for one or more events. - * - *

    - * The following code adds an event handler for one event: - *

    - * - *
    -    * obj.on("eventName", function (event) {
    -    *     // This is the event handler.
    -    * });
    -    * 
    - * - *

    If you pass in multiple event names and a handler method, the handler is - * registered for each of those events:

    - * - *
    -    * obj.on("eventName1 eventName2",
    -    *        function (event) {
    -    *            // This is the event handler.
    -    *        });
    -    * 
    - * - *

    You can also pass in a third context parameter (which is optional) to - * define the value of this in the handler method:

    - * - *
    obj.on("eventName",
    -    *        function (event) {
    -    *            // This is the event handler.
    -    *        },
    -    *        obj);
    -    * 
    - * - *

    - * The method also supports an alternate syntax, in which the first parameter is an object - * that is a hash map of event names and handler functions and the second parameter (optional) - * is the context for this in each handler: - *

    - *
    -    * obj.on(
    -    *    {
    -    *       eventName1: function (event) {
    -    *               // This is the handler for eventName1.
    -    *           },
    -    *       eventName2:  function (event) {
    -    *               // This is the handler for eventName2.
    -    *           }
    -    *    },
    -    *    obj);
    -    * 
    - * - *

    - * If you do not add a handler for an event, the event is ignored locally. - *

    - * - * @param {String} type The string identifying the type of event. You can specify multiple event - * names in this string, separating them with a space. The event handler will process each of - * the events. - * @param {Function} handler The handler function to process the event. This function takes - * the event object as a parameter. - * @param {Object} context (Optional) Defines the value of this in the event - * handler function. - * - * @returns {EventDispatcher} The EventDispatcher object. - * - * @memberOf EventDispatcher - * @method #on - * @see off() - * @see once() - * @see Events - */ - self.on = function(eventNames, handlerOrContext, context) { - if (typeof(eventNames) === 'string' && handlerOrContext) { - addListeners(eventNames.split(' '), handlerOrContext, context); - } - else { - for (var name in eventNames) { - if (eventNames.hasOwnProperty(name)) { - addListeners([name], eventNames[name], handlerOrContext); + onDomReady = function() { + _domReady = true; + + if (typeof(document) !== 'undefined') { + if ( document.addEventListener ) { + document.removeEventListener('DOMContentLoaded', onDomReady, false); + window.removeEventListener('load', onDomReady, false); + } else { + document.detachEvent('onreadystatechange', onDomReady); + window.detachEvent('onload', onDomReady); } } - } - return this; - }; + // This is making an assumption about there being only one 'window' + // that we care about. + OTHelpers.on(window, 'unload', onDomUnload); - /** - * Removes an event handler or handlers. - * - *

    If you pass in one event name and a handler method, the handler is removed for that - * event:

    - * - *
    obj.off("eventName", eventHandler);
    - * - *

    If you pass in multiple event names and a handler method, the handler is removed for - * those events:

    - * - *
    obj.off("eventName1 eventName2", eventHandler);
    - * - *

    If you pass in an event name (or names) and no handler method, all handlers are - * removed for those events:

    - * - *
    obj.off("event1Name event2Name");
    - * - *

    If you pass in no arguments, all event handlers are removed for all events - * dispatched by the object:

    - * - *
    obj.off();
    - * - *

    - * The method also supports an alternate syntax, in which the first parameter is an object that - * is a hash map of event names and handler functions and the second parameter (optional) is - * the context for this in each handler: - *

    - *
    -    * obj.off(
    -    *    {
    -    *       eventName1: event1Handler,
    -    *       eventName2: event2Handler
    -    *    });
    -    * 
    - * - * @param {String} type (Optional) The string identifying the type of event. You can - * use a space to specify multiple events, as in "accessAllowed accessDenied - * accessDialogClosed". If you pass in no type value (or other arguments), - * all event handlers are removed for the object. - * @param {Function} handler (Optional) The event handler function to remove. The handler - * must be the same function object as was passed into on(). Be careful with - * helpers like bind() that return a new function when called. If you pass in - * no handler, all event handlers are removed for the specified event - * type. - * @param {Object} context (Optional) If you specify a context, the event handler - * is removed for all specified events and handlers that use the specified context. (The - * context must match the context passed into on().) - * - * @returns {Object} The object that dispatched the event. - * - * @memberOf EventDispatcher - * @method #off - * @see on() - * @see once() - * @see Events - */ - self.off = function(eventNames, handlerOrContext, context) { - if (typeof eventNames === 'string') { - if (handlerOrContext && OTHelpers.isFunction(handlerOrContext)) { - removeListeners(eventNames.split(' '), handlerOrContext, context); - } - else { - OTHelpers.forEach(eventNames.split(' '), function(name) { - removeAllListenersNamed(name, handlerOrContext); - }, this); - } - - } else if (!eventNames) { - // remove all bound events - _events = {}; + OTHelpers.forEach(_loadCallbacks, function(listener) { + listener[0].call(listener[1]); + }); - } else { - for (var name in eventNames) { - if (eventNames.hasOwnProperty(name)) { - removeListeners([name], eventNames[name], handlerOrContext); - } - } - } + _loadCallbacks = []; + }, - return this; - }; + onDomUnload = function() { + _domUnloaded = true; + OTHelpers.forEach(_unloadCallbacks, function(listener) { + listener[0].call(listener[1]); + }); - /** - * Adds an event handler function for one or more events. Once the handler is called, - * the specified handler method is removed as a handler for this event. (When you use - * the on() method to add an event handler, the handler is not - * removed when it is called.) The once() method is the equivilent of - * calling the on() - * method and calling off() the first time the handler is invoked. - * - *

    - * The following code adds a one-time event handler for the accessAllowed event: - *

    - * - *
    -    * obj.once("eventName", function (event) {
    -    *    // This is the event handler.
    -    * });
    -    * 
    - * - *

    If you pass in multiple event names and a handler method, the handler is registered - * for each of those events:

    - * - *
    obj.once("eventName1 eventName2"
    -    *          function (event) {
    -    *              // This is the event handler.
    -    *          });
    -    * 
    - * - *

    You can also pass in a third context parameter (which is optional) to define - * the value of - * this in the handler method:

    - * - *
    obj.once("eventName",
    -    *          function (event) {
    -    *              // This is the event handler.
    -    *          },
    -    *          obj);
    -    * 
    - * - *

    - * The method also supports an alternate syntax, in which the first parameter is an object that - * is a hash map of event names and handler functions and the second parameter (optional) is the - * context for this in each handler: - *

    - *
    -    * obj.once(
    -    *    {
    -    *       eventName1: function (event) {
    -    *                  // This is the event handler for eventName1.
    -    *           },
    -    *       eventName2:  function (event) {
    -    *                  // This is the event handler for eventName1.
    -    *           }
    -    *    },
    -    *    obj);
    -    * 
    - * - * @param {String} type The string identifying the type of event. You can specify multiple - * event names in this string, separating them with a space. The event handler will process - * the first occurence of the events. After the first event, the handler is removed (for - * all specified events). - * @param {Function} handler The handler function to process the event. This function takes - * the event object as a parameter. - * @param {Object} context (Optional) Defines the value of this in the event - * handler function. - * - * @returns {Object} The object that dispatched the event. - * - * @memberOf EventDispatcher - * @method #once - * @see on() - * @see off() - * @see Events - */ - self.once = function(eventNames, handler, context) { - var names = eventNames.split(' '), - fun = OTHelpers.bind(function() { - var result = handler.apply(context || null, arguments); - removeListeners(names, handler, context); + _unloadCallbacks = []; + }; - return result; - }, this); - addListeners(names, handler, context, fun); - return this; - }; + OTHelpers.onDOMLoad = function(cb, context) { + if (OTHelpers.isReady()) { + cb.call(context); + return; + } + _loadCallbacks.push([cb, context]); + }; - /** - * Deprecated; use on() or once() instead. - *

    - * This method registers a method as an event listener for a specific event. - *

    - * - *

    - * If a handler is not registered for an event, the event is ignored locally. If the - * event listener function does not exist, the event is ignored locally. - *

    - *

    - * Throws an exception if the listener name is invalid. - *

    - * - * @param {String} type The string identifying the type of event. - * - * @param {Function} listener The function to be invoked when the object dispatches the event. - * - * @param {Object} context (Optional) Defines the value of this in the event - * handler function. - * - * @memberOf EventDispatcher - * @method #addEventListener - * @see on() - * @see once() - * @see Events - */ - // See 'on' for usage. - // @depreciated will become a private helper function in the future. - self.addEventListener = function(eventName, handler, context) { - OTHelpers.warn('The addEventListener() method is deprecated. Use on() or once() instead.'); - addListeners([eventName], handler, context); - }; + OTHelpers.onDOMUnload = function(cb, context) { + if (this.isDOMUnloaded()) { + cb.call(context); + return; + } + _unloadCallbacks.push([cb, context]); + }; - /** - * Deprecated; use on() or once() instead. - *

    - * Removes an event listener for a specific event. - *

    - * - *

    - * Throws an exception if the listener name is invalid. - *

    - * - * @param {String} type The string identifying the type of event. - * - * @param {Function} listener The event listener function to remove. - * - * @param {Object} context (Optional) If you specify a context, the event - * handler is removed for all specified events and event listeners that use the specified - context. (The context must match the context passed into - * addEventListener().) - * - * @memberOf EventDispatcher - * @method #removeEventListener - * @see off() - * @see Events - */ - // See 'off' for usage. - // @depreciated will become a private helper function in the future. - self.removeEventListener = function(eventName, handler, context) { - OTHelpers.warn('The removeEventListener() method is deprecated. Use off() instead.'); - removeListeners([eventName], handler, context); - }; + OTHelpers.isReady = function() { + return !_domUnloaded && _domReady; + }; + OTHelpers.isDOMUnloaded = function() { + return _domUnloaded; + }; + if (_domReady) { + onDomReady(); + } else if(typeof(document) !== 'undefined') { + if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', onDomReady, false); - return self; - }; - - OTHelpers.eventing.Event = function() { - - return function (type, cancelable) { - this.type = type; - this.cancelable = cancelable !== undefined ? cancelable : true; - - var _defaultPrevented = false; - - this.preventDefault = function() { - if (this.cancelable) { - _defaultPrevented = true; - } else { - OTHelpers.warn('Event.preventDefault :: Trying to preventDefault ' + - 'on an Event that isn\'t cancelable'); - } - }; + // fallback + window.addEventListener( 'load', onDomReady, false ); - this.isDefaultPrevented = function() { - return _defaultPrevented; - }; - }; + } else if (document.attachEvent) { + document.attachEvent('onreadystatechange', function() { + if (document.readyState === 'complete') onDomReady(); + }); - }; + // fallback + window.attachEvent( 'onload', onDomReady ); + } + } -})(window, window.OTHelpers); +})(); /*jshint browser:true, smarttabs:true*/ // tb_require('../helpers.js') -// tb_require('./callbacks.js') - -// DOM helpers -(function(window, OTHelpers, undefined) { - - OTHelpers.isElementNode = function(node) { - return node && typeof node === 'object' && node.nodeType === 1; - }; - // Returns true if the client supports element.classList - OTHelpers.supportsClassList = function() { - var hasSupport = (typeof document !== 'undefined') && - ('classList' in document.createElement('a')); +OTHelpers.setCookie = function(key, value) { + try { + localStorage.setItem(key, value); + } catch (err) { + // Store in browser cookie + var date = new Date(); + date.setTime(date.getTime()+(365*24*60*60*1000)); + var expires = '; expires=' + date.toGMTString(); + document.cookie = key + '=' + value + expires + '; path=/'; + } +}; - OTHelpers.supportsClassList = function() { return hasSupport; }; +OTHelpers.getCookie = function(key) { + var value; - return hasSupport; - }; + try { + value = localStorage.getItem(key); + return value; + } catch (err) { + // Check browser cookies + var nameEQ = key + '='; + var ca = document.cookie.split(';'); + for(var i=0;i < ca.length;i++) { + var c = ca[i]; + while (c.charAt(0) === ' ') { + c = c.substring(1,c.length); + } + if (c.indexOf(nameEQ) === 0) { + value = c.substring(nameEQ.length,c.length); + } + } - OTHelpers.removeElement = function(element) { - if (element && element.parentNode) { - element.parentNode.removeChild(element); + if (value) { + return value; } - }; + } - OTHelpers.removeElementById = function(elementId) { - /*jshint newcap:false */ - this.removeElement(OTHelpers(elementId)); - }; + return null; +}; - OTHelpers.removeElementsByType = function(parentElem, type) { - if (!parentElem) return; +// tb_require('../helpers.js') - var elements = parentElem.getElementsByTagName(type); +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ - // elements is a "live" NodesList collection. Meaning that the collection - // itself will be mutated as we remove elements from the DOM. This means - // that "while there are still elements" is safer than "iterate over each - // element" as the collection length and the elements indices will be modified - // with each iteration. - while (elements.length) { - parentElem.removeChild(elements[0]); - } - }; - OTHelpers.emptyElement = function(element) { - while (element.firstChild) { - element.removeChild(element.firstChild); +OTHelpers.Collection = function(idField) { + var _models = [], + _byId = {}, + _idField = idField || 'id'; + + OTHelpers.eventing(this, true); + + var modelProperty = function(model, property) { + if(OTHelpers.isFunction(model[property])) { + return model[property](); + } else { + return model[property]; } - return element; }; - OTHelpers.createElement = function(nodeName, attributes, children, doc) { - var element = (doc || document).createElement(nodeName); - - if (attributes) { - for (var name in attributes) { - if (typeof(attributes[name]) === 'object') { - if (!element[name]) element[name] = {}; + var onModelUpdate = OTHelpers.bind(function onModelUpdate (event) { + this.trigger('update', event); + this.trigger('update:'+event.target.id, event); + }, this), - var subAttrs = attributes[name]; - for (var n in subAttrs) { - element[name][n] = subAttrs[n]; - } - } - else if (name === 'className') { - element.className = attributes[name]; - } - else { - element.setAttribute(name, attributes[name]); - } - } - } + onModelDestroy = OTHelpers.bind(function onModelDestroyed (event) { + this.remove(event.target, event.reason); + }, this); - var setChildren = function(child) { - if(typeof child === 'string') { - element.innerHTML = element.innerHTML + child; - } else { - element.appendChild(child); - } - }; - if(OTHelpers.isArray(children)) { - OTHelpers.forEach(children, setChildren); - } else if(children) { - setChildren(children); - } + this.reset = function() { + // Stop listening on the models, they are no longer our problem + OTHelpers.forEach(_models, function(model) { + model.off('updated', onModelUpdate, this); + model.off('destroyed', onModelDestroy, this); + }, this); - return element; + _models = []; + _byId = {}; }; - OTHelpers.createButton = function(innerHTML, attributes, events) { - var button = OTHelpers.createElement('button', attributes, innerHTML); - - if (events) { - for (var name in events) { - if (events.hasOwnProperty(name)) { - OTHelpers.on(button, name, events[name]); - } + this.destroy = function(reason) { + OTHelpers.forEach(_models, function(model) { + if(model && typeof model.destroy === 'function') { + model.destroy(reason, true); } + }); - button._boundEvents = events; - } - - return button; + this.reset(); + this.off(); }; + this.get = function(id) { return id && _byId[id] !== void 0 ? _models[_byId[id]] : void 0; }; + this.has = function(id) { return id && _byId[id] !== void 0; }; - // Detects when an element is not part of the document flow because - // it or one of it's ancesters has display:none. - OTHelpers.isDisplayNone = function(element) { - if ( (element.offsetWidth === 0 || element.offsetHeight === 0) && - OTHelpers.css(element, 'display') === 'none') return true; + this.toString = function() { return _models.toString(); }; - if (element.parentNode && element.parentNode.style) { - return OTHelpers.isDisplayNone(element.parentNode); + // Return only models filtered by either a dict of properties + // or a filter function. + // + // @example Return all publishers with a streamId of 1 + // OT.publishers.where({streamId: 1}) + // + // @example The same thing but filtering using a filter function + // OT.publishers.where(function(publisher) { + // return publisher.stream.id === 4; + // }); + // + // @example The same thing but filtering using a filter function + // executed with a specific this + // OT.publishers.where(function(publisher) { + // return publisher.stream.id === 4; + // }, self); + // + this.where = function(attrsOrFilterFn, context) { + if (OTHelpers.isFunction(attrsOrFilterFn)) { + return OTHelpers.filter(_models, attrsOrFilterFn, context); } - return false; + return OTHelpers.filter(_models, function(model) { + for (var key in attrsOrFilterFn) { + if(!attrsOrFilterFn.hasOwnProperty(key)) { + continue; + } + if (modelProperty(model, key) !== attrsOrFilterFn[key]) return false; + } + + return true; + }); }; - OTHelpers.findElementWithDisplayNone = function(element) { - if ( (element.offsetWidth === 0 || element.offsetHeight === 0) && - OTHelpers.css(element, 'display') === 'none') return element; + // Similar to where in behaviour, except that it only returns + // the first match. + this.find = function(attrsOrFilterFn, context) { + var filterFn; - if (element.parentNode && element.parentNode.style) { - return OTHelpers.findElementWithDisplayNone(element.parentNode); + if (OTHelpers.isFunction(attrsOrFilterFn)) { + filterFn = attrsOrFilterFn; + } + else { + filterFn = function(model) { + for (var key in attrsOrFilterFn) { + if(!attrsOrFilterFn.hasOwnProperty(key)) { + continue; + } + if (modelProperty(model, key) !== attrsOrFilterFn[key]) return false; + } + + return true; + }; } - return null; - }; + filterFn = OTHelpers.bind(filterFn, context); - function objectHasProperties(obj) { - for (var key in obj) { - if (obj.hasOwnProperty(key)) return true; + for (var i=0; i<_models.length; ++i) { + if (filterFn(_models[i]) === true) return _models[i]; } - return false; - } + return null; + }; - // Allows an +onChange+ callback to be triggered when specific style properties - // of +element+ are notified. The callback accepts a single parameter, which is - // a hash where the keys are the style property that changed and the values are - // an array containing the old and new values ([oldValue, newValue]). - // - // Width and Height changes while the element is display: none will not be - // fired until such time as the element becomes visible again. - // - // This function returns the MutationObserver itself. Once you no longer wish - // to observe the element you should call disconnect on the observer. - // - // Observing changes: - // // observe changings to the width and height of object - // dimensionsObserver = OTHelpers.observeStyleChanges(object, - // ['width', 'height'], function(changeSet) { - // OT.debug("The new width and height are " + - // changeSet.width[1] + ',' + changeSet.height[1]); - // }); - // - // Cleaning up - // // stop observing changes - // dimensionsObserver.disconnect(); - // dimensionsObserver = null; - // - OTHelpers.observeStyleChanges = function(element, stylesToObserve, onChange) { - var oldStyles = {}; - - var getStyle = function getStyle(style) { - switch (style) { - case 'width': - return OTHelpers.width(element); + this.add = function(model) { + var id = modelProperty(model, _idField); - case 'height': - return OTHelpers.height(element); + if (this.has(id)) { + OTHelpers.warn('Model ' + id + ' is already in the collection', _models); + return this; + } - default: - return OTHelpers.css(element); - } - }; + _byId[id] = _models.push(model) - 1; - // get the inital values - OTHelpers.forEach(stylesToObserve, function(style) { - oldStyles[style] = getStyle(style); - }); + model.on('updated', onModelUpdate, this); + model.on('destroyed', onModelDestroy, this); - var observer = new MutationObserver(function(mutations) { - var changeSet = {}; + this.trigger('add', model); + this.trigger('add:'+id, model); - OTHelpers.forEach(mutations, function(mutation) { - if (mutation.attributeName !== 'style') return; + return this; + }; - var isHidden = OTHelpers.isDisplayNone(element); + this.remove = function(model, reason) { + var id = modelProperty(model, _idField); - OTHelpers.forEach(stylesToObserve, function(style) { - if(isHidden && (style === 'width' || style === 'height')) return; + _models.splice(_byId[id], 1); - var newValue = getStyle(style); + // Shuffle everyone down one + for (var i=_byId[id]; i<_models.length; ++i) { + _byId[_models[i][_idField]] = i; + } - if (newValue !== oldStyles[style]) { - changeSet[style] = [oldStyles[style], newValue]; - oldStyles[style] = newValue; - } - }); - }); + delete _byId[id]; - if (objectHasProperties(changeSet)) { - // Do this after so as to help avoid infinite loops of mutations. - OTHelpers.callAsync(function() { - onChange.call(null, changeSet); - }); - } - }); + model.off('updated', onModelUpdate, this); + model.off('destroyed', onModelDestroy, this); - observer.observe(element, { - attributes:true, - attributeFilter: ['style'], - childList:false, - characterData:false, - subtree:false - }); + this.trigger('remove', model, reason); + this.trigger('remove:'+id, model, reason); - return observer; + return this; }; + // Retrigger the add event behaviour for each model. You can also + // select a subset of models to trigger using the same arguments + // as the #where method. + this._triggerAddEvents = function() { + var models = this.where.apply(this, arguments); + OTHelpers.forEach(models, function(model) { + this.trigger('add', model); + this.trigger('add:' + modelProperty(model, _idField), model); + }, this); + }; - // trigger the +onChange+ callback whenever - // 1. +element+ is removed - // 2. or an immediate child of +element+ is removed. - // - // This function returns the MutationObserver itself. Once you no longer wish - // to observe the element you should call disconnect on the observer. - // - // Observing changes: - // // observe changings to the width and height of object - // nodeObserver = OTHelpers.observeNodeOrChildNodeRemoval(object, function(removedNodes) { - // OT.debug("Some child nodes were removed"); - // OTHelpers.forEach(removedNodes, function(node) { - // OT.debug(node); - // }); - // }); - // - // Cleaning up - // // stop observing changes - // nodeObserver.disconnect(); - // nodeObserver = null; - // - OTHelpers.observeNodeOrChildNodeRemoval = function(element, onChange) { - var observer = new MutationObserver(function(mutations) { - var removedNodes = []; - - OTHelpers.forEach(mutations, function(mutation) { - if (mutation.removedNodes.length) { - removedNodes = removedNodes.concat(Array.prototype.slice.call(mutation.removedNodes)); - } - }); + this.length = function() { + return _models.length; + }; +}; - if (removedNodes.length) { - // Do this after so as to help avoid infinite loops of mutations. - OTHelpers.callAsync(function() { - onChange(removedNodes); - }); - } - }); - observer.observe(element, { - attributes:false, - childList:true, - characterData:false, - subtree:true - }); +/*jshint browser:true, smarttabs:true*/ - return observer; - }; +// tb_require('../helpers.js') -})(window, window.OTHelpers); +OTHelpers.castToBoolean = function(value, defaultValue) { + if (value === undefined) return defaultValue; + return value === 'true' || value === true; +}; +OTHelpers.roundFloat = function(value, places) { + return Number(value.toFixed(places)); +}; /*jshint browser:true, smarttabs:true*/ // tb_require('../helpers.js') -// tb_require('./dom.js') - -(function(window, OTHelpers, undefined) { - OTHelpers.Modal = function(options) { +(function() { - OTHelpers.eventing(this, true); + var capabilities = {}; - var callback = arguments[arguments.length - 1]; + // Registers a new capability type and a function that will indicate + // whether this client has that capability. + // + // OTHelpers.registerCapability('bundle', function() { + // return OTHelpers.hasCapabilities('webrtc') && + // (OTHelpers.env.name === 'Chrome' || TBPlugin.isInstalled()); + // }); + // + OTHelpers.registerCapability = function(name, callback) { + var _name = name.toLowerCase(); - if(!OTHelpers.isFunction(callback)) { - throw new Error('OTHelpers.Modal2 must be given a callback'); + if (capabilities.hasOwnProperty(_name)) { + OTHelpers.error('Attempted to register', name, 'capability more than once'); + return; } - if(arguments.length < 2) { - options = {}; + if (!OTHelpers.isFunction(callback)) { + OTHelpers.error('Attempted to register', name, + 'capability with a callback that isn\' a function'); + return; } - var domElement = document.createElement('iframe'); - - domElement.id = options.id || OTHelpers.uuid(); - domElement.style.position = 'absolute'; - domElement.style.position = 'fixed'; - domElement.style.height = '100%'; - domElement.style.width = '100%'; - domElement.style.top = '0px'; - domElement.style.left = '0px'; - domElement.style.right = '0px'; - domElement.style.bottom = '0px'; - domElement.style.zIndex = 1000; - domElement.style.border = '0'; - - try { - domElement.style.backgroundColor = 'rgba(0,0,0,0.2)'; - } catch (err) { - // Old IE browsers don't support rgba and we still want to show the upgrade message - // but we just make the background of the iframe completely transparent. - domElement.style.backgroundColor = 'transparent'; - domElement.setAttribute('allowTransparency', 'true'); - } + memoriseCapabilityTest(_name, callback); + }; - domElement.scrolling = 'no'; - domElement.setAttribute('scrolling', 'no'); - // This is necessary for IE, as it will not inherit it's doctype from - // the parent frame. - var frameContent = '' + - '' + - '' + - ''; + // Wrap up a capability test in a function that memorises the + // result. + var memoriseCapabilityTest = function (name, callback) { + capabilities[name] = function() { + var result = callback(); + capabilities[name] = function() { + return result; + }; - var wrappedCallback = function() { - var doc = domElement.contentDocument || domElement.contentWindow.document; + return result; + }; + }; - if (OTHelpers.browserVersion().iframeNeedsLoad) { - doc.body.style.backgroundColor = 'transparent'; - doc.body.style.border = 'none'; + var testCapability = function (name) { + return capabilities[name](); + }; - if (OTHelpers.browser() !== 'IE') { - // Skip this for IE as we use the bookmarklet workaround - // for THAT browser. - doc.open(); - doc.write(frameContent); - doc.close(); - } - } - callback( - domElement.contentWindow, - doc - ); - }; + // Returns true if all of the capability names passed in + // exist and are met. + // + // OTHelpers.hasCapabilities('bundle', 'rtcpMux') + // + OTHelpers.hasCapabilities = function(/* capability1, capability2, ..., capabilityN */) { + var capNames = prototypeSlice.call(arguments), + name; - document.body.appendChild(domElement); + for (var i=0; i 0) { + var args = timeouts.shift(), + fn = args.shift(); - for (property in currentStyle) { - if (/width|height|margin.|padding.|border.+W/.test(property) && style[property] !== 'auto') { - style[property] = getPixelSize(element, currentStyle, property, fontSize) + 'px'; - } else if (property === 'styleFloat') { - /*jshint -W069 */ - style['float'] = currentStyle[property]; - } else { - style[property] = currentStyle[property]; + fn.apply(null, args); + } } - } + }; - setShortStyleProperty(style, 'margin'); - setShortStyleProperty(style, 'padding'); - setShortStyleProperty(style, 'border'); + // Ensure that we don't receive messages after unload + // Yes, this seems to really happen in IE sometimes, usually + // when iFrames are involved. + OTHelpers.on(window, 'unload', removeMessageHandler); - style.fontSize = fontSize + 'px'; + if(window.addEventListener) { + window.addEventListener('message', handleMessage, true); + } else if(window.attachEvent) { + window.attachEvent('onmessage', handleMessage); + } - return style; + _callAsync = function (/* fn, [arg1, arg2, ..., argN] */) { + timeouts.push(prototypeSlice.call(arguments)); + window.postMessage(messageName, '*'); + }; } + else { + _callAsync = function (/* fn, [arg1, arg2, ..., argN] */) { + var args = prototypeSlice.call(arguments), + fn = args.shift(); - CSSStyleDeclaration.prototype = { - constructor: CSSStyleDeclaration, - getPropertyPriority: function () {}, - getPropertyValue: function ( prop ) { - return this[prop] || ''; - }, - item: function () {}, - removeProperty: function () {}, - setProperty: function () {}, - getPropertyCSSValue: function () {} - }; - - function getComputedStyle(element) { - return new CSSStyleDeclaration(element); + setTimeout(function() { + fn.apply(null, args); + }, 0); + }; } - OTHelpers.getComputedStyle = function(element) { - if(element && - element.ownerDocument && - element.ownerDocument.defaultView && - element.ownerDocument.defaultView.getComputedStyle) { - return element.ownerDocument.defaultView.getComputedStyle(element); - } else { - return getComputedStyle(element); - } - }; + // Calls the function +fn+ asynchronously with the current execution. + // This is most commonly used to execute something straight after + // the current function. + // + // Any arguments in addition to +fn+ will be passed to +fn+ when it's + // called. + // + // You would use this inplace of setTimeout(fn, 0) type constructs. callAsync + // is preferable as it executes in a much more predictable time window, + // unlike setTimeout which could execute anywhere from 2ms to several thousand + // depending on the browser/context. + // + // It does this using window.postMessage, although if postMessage won't + // work it will fallback to setTimeout. + // + OTHelpers.callAsync = _callAsync; -})(window, window.OTHelpers); -// DOM Attribute helpers helpers + // Wraps +handler+ in a function that will execute it asynchronously + // so that it doesn't interfere with it's exceution context if it raises + // an exception. + OTHelpers.createAsyncHandler = function(handler) { + return function() { + var args = prototypeSlice.call(arguments); + + OTHelpers.callAsync(function() { + handler.apply(null, args); + }); + }; + }; + +})(); /*jshint browser:true, smarttabs:true*/ +/*global jasmine:true*/ -// tb_require('../helpers.js') -// tb_require('./dom.js') +// tb_require('../../helpers.js') +// tb_require('../callbacks.js') -(function(window, OTHelpers, undefined) { +/** +* This base class defines the on, once, and off +* methods of objects that can dispatch events. +* +* @class EventDispatcher +*/ +OTHelpers.eventing = function(self, syncronous) { + var _events = {}; - OTHelpers.addClass = function(element, value) { - // Only bother targeting Element nodes, ignore Text Nodes, CDATA, etc - if (element.nodeType !== 1) { - return; - } + // Call the defaultAction, passing args + function executeDefaultAction(defaultAction, args) { + if (!defaultAction) return; - var classNames = OTHelpers.trim(value).split(/\s+/), - i, l; + defaultAction.apply(null, args.slice()); + } - if (OTHelpers.supportsClassList()) { - for (i=0, l=classNames.length; i 0) { - return element.offsetWidth + 'px'; + OTHelpers.forEach(eventNames, OTHelpers.bind(function(name) { + if (_events[name]) { + _events[name] = OTHelpers.filter(_events[name], filterHandlerAndContext); + if (_events[name].length === 0) delete _events[name]; + var removedListener = name + ':removed'; + if (_events[ removedListener]) { + executeListeners(removedListener, [_events[name] ? _events[name].length : 0]); } + } + }, self)); - return OTHelpers.css(element, 'width'); - }, - - _height = function(element) { - if (element.offsetHeight > 0) { - return element.offsetHeight + 'px'; - } + }; - return OTHelpers.css(element, 'height'); - }; + // Execute any listeners bound to the +event+ Event. + // + // Each handler will be executed async. On completion the defaultAction + // handler will be executed with the args. + // + // @param [Event] event + // An Event object. + // + // @param [Function, Null, Undefined] defaultAction + // An optional function to execute after every other handler. This will execute even + // if there are listeners bound to this event. +defaultAction+ will be passed + // args as a normal handler would. + // + // @return this + // + self.dispatchEvent = function(event, defaultAction) { + if (!event.type) { + OTHelpers.error('OTHelpers.Eventing.dispatchEvent: Event has no type'); + OTHelpers.error(event); - OTHelpers.width = function(element, newWidth) { - if (newWidth) { - OTHelpers.css(element, 'width', newWidth); - return this; - } - else { - if (OTHelpers.isDisplayNone(element)) { - // We can't get the width, probably since the element is hidden. - return OTHelpers.makeVisibleAndYield(element, function() { - return _width(element); - }); - } - else { - return _width(element); - } + throw new Error('OTHelpers.Eventing.dispatchEvent: Event has no type'); } - }; - OTHelpers.height = function(element, newHeight) { - if (newHeight) { - OTHelpers.css(element, 'height', newHeight); - return this; + if (!event.target) { + event.target = this; } - else { - if (OTHelpers.isDisplayNone(element)) { - // We can't get the height, probably since the element is hidden. - return OTHelpers.makeVisibleAndYield(element, function() { - return _height(element); - }); - } - else { - return _height(element); - } + + if (!_events[event.type] || _events[event.type].length === 0) { + executeDefaultAction(defaultAction, [event]); + return; } - }; - // Centers +element+ within the window. You can pass through the width and height - // if you know it, if you don't they will be calculated for you. - OTHelpers.centerElement = function(element, width, height) { - if (!width) width = parseInt(OTHelpers.width(element), 10); - if (!height) height = parseInt(OTHelpers.height(element), 10); + executeListeners(event.type, [event], defaultAction); - var marginLeft = -0.5 * width + 'px'; - var marginTop = -0.5 * height + 'px'; - OTHelpers.css(element, 'margin', marginTop + ' 0 0 ' + marginLeft); - OTHelpers.addClass(element, 'OT_centered'); + return this; }; -})(window, window.OTHelpers); - -// CSS helpers helpers + // Execute each handler for the event called +name+. + // + // Each handler will be executed async, and any exceptions that they throw will + // be caught and logged + // + // How to pass these? + // * defaultAction + // + // @example + // foo.on('bar', function(name, message) { + // alert("Hello " + name + ": " + message); + // }); + // + // foo.trigger('OpenTok', 'asdf'); // -> Hello OpenTok: asdf + // + // + // @param [String] eventName + // The name of this event. + // + // @param [Array] arguments + // Any additional arguments beyond +eventName+ will be passed to the handlers. + // + // @return this + // + self.trigger = function(eventName) { + if (!_events[eventName] || _events[eventName].length === 0) { + return; + } -/*jshint browser:true, smarttabs:true*/ + var args = prototypeSlice.call(arguments); -// tb_require('../helpers.js') -// tb_require('./dom.js') -// tb_require('./getcomputedstyle.js') + // Remove the eventName arg + args.shift(); -(function(window, OTHelpers, undefined) { + executeListeners(eventName, args); - var displayStateCache = {}, - defaultDisplays = {}; + return this; + }; - var defaultDisplayValueForElement = function(element) { - if (defaultDisplays[element.ownerDocument] && - defaultDisplays[element.ownerDocument][element.nodeName]) { - return defaultDisplays[element.ownerDocument][element.nodeName]; + /** + * Adds an event handler function for one or more events. + * + *

    + * The following code adds an event handler for one event: + *

    + * + *
    +  * obj.on("eventName", function (event) {
    +  *     // This is the event handler.
    +  * });
    +  * 
    + * + *

    If you pass in multiple event names and a handler method, the handler is + * registered for each of those events:

    + * + *
    +  * obj.on("eventName1 eventName2",
    +  *        function (event) {
    +  *            // This is the event handler.
    +  *        });
    +  * 
    + * + *

    You can also pass in a third context parameter (which is optional) to + * define the value of this in the handler method:

    + * + *
    obj.on("eventName",
    +  *        function (event) {
    +  *            // This is the event handler.
    +  *        },
    +  *        obj);
    +  * 
    + * + *

    + * The method also supports an alternate syntax, in which the first parameter is an object + * that is a hash map of event names and handler functions and the second parameter (optional) + * is the context for this in each handler: + *

    + *
    +  * obj.on(
    +  *    {
    +  *       eventName1: function (event) {
    +  *               // This is the handler for eventName1.
    +  *           },
    +  *       eventName2:  function (event) {
    +  *               // This is the handler for eventName2.
    +  *           }
    +  *    },
    +  *    obj);
    +  * 
    + * + *

    + * If you do not add a handler for an event, the event is ignored locally. + *

    + * + * @param {String} type The string identifying the type of event. You can specify multiple event + * names in this string, separating them with a space. The event handler will process each of + * the events. + * @param {Function} handler The handler function to process the event. This function takes + * the event object as a parameter. + * @param {Object} context (Optional) Defines the value of this in the event + * handler function. + * + * @returns {EventDispatcher} The EventDispatcher object. + * + * @memberOf EventDispatcher + * @method #on + * @see off() + * @see once() + * @see Events + */ + self.on = function(eventNames, handlerOrContext, context) { + if (typeof(eventNames) === 'string' && handlerOrContext) { + addListeners(eventNames.split(' '), handlerOrContext, context); + } + else { + for (var name in eventNames) { + if (eventNames.hasOwnProperty(name)) { + addListeners([name], eventNames[name], handlerOrContext); + } + } } - if (!defaultDisplays[element.ownerDocument]) defaultDisplays[element.ownerDocument] = {}; - - // We need to know what display value to use for this node. The easiest way - // is to actually create a node and read it out. - var testNode = element.ownerDocument.createElement(element.nodeName), - defaultDisplay; + return this; + }; - element.ownerDocument.body.appendChild(testNode); - defaultDisplay = defaultDisplays[element.ownerDocument][element.nodeName] = - OTHelpers.css(testNode, 'display'); + /** + * Removes an event handler or handlers. + * + *

    If you pass in one event name and a handler method, the handler is removed for that + * event:

    + * + *
    obj.off("eventName", eventHandler);
    + * + *

    If you pass in multiple event names and a handler method, the handler is removed for + * those events:

    + * + *
    obj.off("eventName1 eventName2", eventHandler);
    + * + *

    If you pass in an event name (or names) and no handler method, all handlers are + * removed for those events:

    + * + *
    obj.off("event1Name event2Name");
    + * + *

    If you pass in no arguments, all event handlers are removed for all events + * dispatched by the object:

    + * + *
    obj.off();
    + * + *

    + * The method also supports an alternate syntax, in which the first parameter is an object that + * is a hash map of event names and handler functions and the second parameter (optional) is + * the context for this in each handler: + *

    + *
    +  * obj.off(
    +  *    {
    +  *       eventName1: event1Handler,
    +  *       eventName2: event2Handler
    +  *    });
    +  * 
    + * + * @param {String} type (Optional) The string identifying the type of event. You can + * use a space to specify multiple events, as in "accessAllowed accessDenied + * accessDialogClosed". If you pass in no type value (or other arguments), + * all event handlers are removed for the object. + * @param {Function} handler (Optional) The event handler function to remove. The handler + * must be the same function object as was passed into on(). Be careful with + * helpers like bind() that return a new function when called. If you pass in + * no handler, all event handlers are removed for the specified event + * type. + * @param {Object} context (Optional) If you specify a context, the event handler + * is removed for all specified events and handlers that use the specified context. (The + * context must match the context passed into on().) + * + * @returns {Object} The object that dispatched the event. + * + * @memberOf EventDispatcher + * @method #off + * @see on() + * @see once() + * @see Events + */ + self.off = function(eventNames, handlerOrContext, context) { + if (typeof eventNames === 'string') { + if (handlerOrContext && OTHelpers.isFunction(handlerOrContext)) { + removeListeners(eventNames.split(' '), handlerOrContext, context); + } + else { + OTHelpers.forEach(eventNames.split(' '), function(name) { + removeAllListenersNamed(name, handlerOrContext); + }, this); + } - OTHelpers.removeElement(testNode); - testNode = null; + } else if (!eventNames) { + // remove all bound events + _events = {}; - return defaultDisplay; - }; + } else { + for (var name in eventNames) { + if (eventNames.hasOwnProperty(name)) { + removeListeners([name], eventNames[name], handlerOrContext); + } + } + } - var isHidden = function(element) { - var computedStyle = OTHelpers.getComputedStyle(element); - return computedStyle.getPropertyValue('display') === 'none'; + return this; }; - OTHelpers.show = function(element) { - var display = element.style.display; - - if (display === '' || display === 'none') { - element.style.display = displayStateCache[element] || ''; - delete displayStateCache[element]; - } - if (isHidden(element)) { - // It's still hidden so there's probably a stylesheet that declares this - // element as display:none; - displayStateCache[element] = 'none'; + /** + * Adds an event handler function for one or more events. Once the handler is called, + * the specified handler method is removed as a handler for this event. (When you use + * the on() method to add an event handler, the handler is not + * removed when it is called.) The once() method is the equivilent of + * calling the on() + * method and calling off() the first time the handler is invoked. + * + *

    + * The following code adds a one-time event handler for the accessAllowed event: + *

    + * + *
    +  * obj.once("eventName", function (event) {
    +  *    // This is the event handler.
    +  * });
    +  * 
    + * + *

    If you pass in multiple event names and a handler method, the handler is registered + * for each of those events:

    + * + *
    obj.once("eventName1 eventName2"
    +  *          function (event) {
    +  *              // This is the event handler.
    +  *          });
    +  * 
    + * + *

    You can also pass in a third context parameter (which is optional) to define + * the value of + * this in the handler method:

    + * + *
    obj.once("eventName",
    +  *          function (event) {
    +  *              // This is the event handler.
    +  *          },
    +  *          obj);
    +  * 
    + * + *

    + * The method also supports an alternate syntax, in which the first parameter is an object that + * is a hash map of event names and handler functions and the second parameter (optional) is the + * context for this in each handler: + *

    + *
    +  * obj.once(
    +  *    {
    +  *       eventName1: function (event) {
    +  *                  // This is the event handler for eventName1.
    +  *           },
    +  *       eventName2:  function (event) {
    +  *                  // This is the event handler for eventName1.
    +  *           }
    +  *    },
    +  *    obj);
    +  * 
    + * + * @param {String} type The string identifying the type of event. You can specify multiple + * event names in this string, separating them with a space. The event handler will process + * the first occurence of the events. After the first event, the handler is removed (for + * all specified events). + * @param {Function} handler The handler function to process the event. This function takes + * the event object as a parameter. + * @param {Object} context (Optional) Defines the value of this in the event + * handler function. + * + * @returns {Object} The object that dispatched the event. + * + * @memberOf EventDispatcher + * @method #once + * @see on() + * @see off() + * @see Events + */ + self.once = function(eventNames, handler, context) { + var names = eventNames.split(' '), + fun = OTHelpers.bind(function() { + var result = handler.apply(context || null, arguments); + removeListeners(names, handler, context); - element.style.display = defaultDisplayValueForElement(element); - } + return result; + }, this); + addListeners(names, handler, context, fun); return this; }; - OTHelpers.hide = function(element) { - if (element.style.display === 'none') return; - - displayStateCache[element] = element.style.display; - element.style.display = 'none'; - return this; + /** + * Deprecated; use on() or once() instead. + *

    + * This method registers a method as an event listener for a specific event. + *

    + * + *

    + * If a handler is not registered for an event, the event is ignored locally. If the + * event listener function does not exist, the event is ignored locally. + *

    + *

    + * Throws an exception if the listener name is invalid. + *

    + * + * @param {String} type The string identifying the type of event. + * + * @param {Function} listener The function to be invoked when the object dispatches the event. + * + * @param {Object} context (Optional) Defines the value of this in the event + * handler function. + * + * @memberOf EventDispatcher + * @method #addEventListener + * @see on() + * @see once() + * @see Events + */ + // See 'on' for usage. + // @depreciated will become a private helper function in the future. + self.addEventListener = function(eventName, handler, context) { + OTHelpers.warn('The addEventListener() method is deprecated. Use on() or once() instead.'); + addListeners([eventName], handler, context); }; - OTHelpers.css = function(element, nameOrHash, value) { - if (typeof(nameOrHash) !== 'string') { - var style = element.style; - for (var cssName in nameOrHash) { - if (nameOrHash.hasOwnProperty(cssName)) { - style[cssName] = nameOrHash[cssName]; - } - } + /** + * Deprecated; use on() or once() instead. + *

    + * Removes an event listener for a specific event. + *

    + * + *

    + * Throws an exception if the listener name is invalid. + *

    + * + * @param {String} type The string identifying the type of event. + * + * @param {Function} listener The event listener function to remove. + * + * @param {Object} context (Optional) If you specify a context, the event + * handler is removed for all specified events and event listeners that use the specified + context. (The context must match the context passed into + * addEventListener().) + * + * @memberOf EventDispatcher + * @method #removeEventListener + * @see off() + * @see Events + */ + // See 'off' for usage. + // @depreciated will become a private helper function in the future. + self.removeEventListener = function(eventName, handler, context) { + OTHelpers.warn('The removeEventListener() method is deprecated. Use off() instead.'); + removeListeners([eventName], handler, context); + }; - return this; - } else if (value !== undefined) { - element.style[nameOrHash] = value; - return this; + return self; +}; - } else { - // Normalise vendor prefixes from the form MozTranform to -moz-transform - // except for ms extensions, which are weird... +OTHelpers.eventing.Event = function() { + return function (type, cancelable) { + this.type = type; + this.cancelable = cancelable !== undefined ? cancelable : true; - var name = nameOrHash.replace( /([A-Z]|^ms)/g, '-$1' ).toLowerCase(), - computedStyle = OTHelpers.getComputedStyle(element), - currentValue = computedStyle.getPropertyValue(name); + var _defaultPrevented = false; - if (currentValue === '') { - currentValue = element.style[name]; + this.preventDefault = function() { + if (this.cancelable) { + _defaultPrevented = true; + } else { + OTHelpers.warn('Event.preventDefault :: Trying to preventDefault ' + + 'on an Event that isn\'t cancelable'); } + }; - return currentValue; - } + this.isDefaultPrevented = function() { + return _defaultPrevented; + }; }; +}; +/*jshint browser:true, smarttabs:true */ -// Apply +styles+ to +element+ while executing +callback+, restoring the previous -// styles after the callback executes. - OTHelpers.applyCSS = function(element, styles, callback) { - var oldStyles = {}, - name, - ret; - - // Backup the old styles - for (name in styles) { - if (styles.hasOwnProperty(name)) { - // We intentionally read out of style here, instead of using the css - // helper. This is because the css helper uses querySelector and we - // only want to pull values out of the style (domeElement.style) hash. - oldStyles[name] = element.style[name]; +// tb_require('../helpers.js') +// tb_require('./callbacks.js') +// tb_require('./dom_events.js') - OTHelpers.css(element, name, styles[name]); - } - } +OTHelpers.createElement = function(nodeName, attributes, children, doc) { + var element = (doc || document).createElement(nodeName); - ret = callback(); + if (attributes) { + for (var name in attributes) { + if (typeof(attributes[name]) === 'object') { + if (!element[name]) element[name] = {}; - // Restore the old styles - for (name in styles) { - if (styles.hasOwnProperty(name)) { - OTHelpers.css(element, name, oldStyles[name] || ''); + var subAttrs = attributes[name]; + for (var n in subAttrs) { + element[name][n] = subAttrs[n]; + } + } + else if (name === 'className') { + element.className = attributes[name]; + } + else { + element.setAttribute(name, attributes[name]); } } + } - return ret; + var setChildren = function(child) { + if(typeof child === 'string') { + element.innerHTML = element.innerHTML + child; + } else { + element.appendChild(child); + } }; - // Make +element+ visible while executing +callback+. - OTHelpers.makeVisibleAndYield = function(element, callback) { - // find whether it's the element or an ancester that's display none and - // then apply to whichever it is - var targetElement = OTHelpers.findElementWithDisplayNone(element); - if (!targetElement) return; - - return OTHelpers.applyCSS(targetElement, { - display: 'block', - visibility: 'hidden' - }, callback); - }; + if($.isArray(children)) { + $.forEach(children, setChildren); + } else if(children) { + setChildren(children); + } -})(window, window.OTHelpers); + return element; +}; -// AJAX helpers +OTHelpers.createButton = function(innerHTML, attributes, events) { + var button = $.createElement('button', attributes, innerHTML); -/*jshint browser:true, smarttabs:true*/ + if (events) { + for (var name in events) { + if (events.hasOwnProperty(name)) { + $.on(button, name, events[name]); + } + } + + button._boundEvents = events; + } + + return button; +}; +/*jshint browser:true, smarttabs:true */ // tb_require('../helpers.js') +// tb_require('./callbacks.js') -(function(window, OTHelpers, undefined) { +// DOM helpers - var requestAnimationFrame = window.requestAnimationFrame || - window.mozRequestAnimationFrame || - window.webkitRequestAnimationFrame || - window.msRequestAnimationFrame; +ElementCollection.prototype.appendTo = function(parentElement) { + if (!parentElement) throw new Error('appendTo requires a DOMElement to append to.'); - if (requestAnimationFrame) { - requestAnimationFrame = OTHelpers.bind(requestAnimationFrame, window); - } - else { - var lastTime = 0; - var startTime = OTHelpers.now(); + return this.forEach(parentElement.appendChild.bind(parentElement)); +}; - requestAnimationFrame = function(callback){ - var currTime = OTHelpers.now(); - var timeToCall = Math.max(0, 16 - (currTime - lastTime)); - var id = window.setTimeout(function() { callback(currTime - startTime); }, timeToCall); - lastTime = currTime + timeToCall; - return id; - }; - } +ElementCollection.prototype.after = function(prevElement) { + if (!prevElement) throw new Error('after requires a DOMElement to insert after'); - OTHelpers.requestAnimationFrame = requestAnimationFrame; -})(window, window.OTHelpers); -// AJAX helpers + return this.forEach(function(element) { + if (element.parentElement) { + if (prevElement !== element.parentNode.lastChild) { + element.parentElement.before(element, prevElement); + } + else { + element.parentElement.appendChild(element); + } + } + }); +}; -/*jshint browser:true, smarttabs:true*/ +ElementCollection.prototype.before = function(nextElement) { + if (!nextElement) throw new Error('before requires a DOMElement to insert before'); -// tb_require('../helpers.js') + return this.forEach(function(element) { + if (element.parentElement) { + element.parentElement.before(element, nextElement); + } + }); +}; -(function(window, OTHelpers, undefined) { +ElementCollection.prototype.remove = function () { + return this.forEach(function(element) { + if (element.parentNode) { + element.parentNode.removeChild(element); + } + }); +}; - function formatPostData(data) { //, contentType - // If it's a string, we assume it's properly encoded - if (typeof(data) === 'string') return data; +ElementCollection.prototype.empty = function () { + return this.forEach(function(element) { + // elements is a "live" NodesList collection. Meaning that the collection + // itself will be mutated as we remove elements from the DOM. This means + // that "while there are still elements" is safer than "iterate over each + // element" as the collection length and the elements indices will be modified + // with each iteration. + while (element.firstChild) { + element.removeChild(element.firstChild); + } + }); +}; - var queryString = []; - for (var key in data) { - queryString.push( - encodeURIComponent(key) + '=' + encodeURIComponent(data[key]) - ); +// Detects when an element is not part of the document flow because +// it or one of it's ancesters has display:none. +ElementCollection.prototype.isDisplayNone = function() { + return this.some(function(element) { + if ( (element.offsetWidth === 0 || element.offsetHeight === 0) && + $(element).css('display') === 'none') return true; + + if (element.parentNode && element.parentNode.style) { + return $(element.parentNode).isDisplayNone(); } + }); +}; - return queryString.join('&').replace(/\+/g, '%20'); - } +ElementCollection.prototype.findElementWithDisplayNone = function(element) { + return $.findElementWithDisplayNone(element); +}; - OTHelpers.getJSON = function(url, options, callback) { - options = options || {}; - var done = function(error, event) { - if(error) { - callback(error, event && event.target && event.target.responseText); - } else { - var response; - try { - response = JSON.parse(event.target.responseText); - } catch(e) { - // Badly formed JSON - callback(e, event && event.target && event.target.responseText); - return; - } +OTHelpers.isElementNode = function(node) { + return node && typeof node === 'object' && node.nodeType === 1; +}; - callback(null, response, event); - } - }; - if(options.xdomainrequest) { - OTHelpers.xdomainRequest(url, { method: 'GET' }, done); - } else { - var extendedHeaders = OTHelpers.extend({ - 'Accept': 'application/json' - }, options.headers || {}); +// @remove +OTHelpers.removeElement = function(element) { + $(element).remove(); +}; - OTHelpers.get(url, OTHelpers.extend(options || {}, { - headers: extendedHeaders - }), done); - } +// @remove +OTHelpers.removeElementById = function(elementId) { + return $('#'+elementId).remove(); +}; - }; +// @remove +OTHelpers.removeElementsByType = function(parentElem, type) { + return $(type, parentElem).remove(); +}; - OTHelpers.xdomainRequest = function(url, options, callback) { - /*global XDomainRequest*/ - var xdr = new XDomainRequest(), - _options = options || {}, - _method = _options.method; +// @remove +OTHelpers.emptyElement = function(element) { + return $(element).empty(); +}; - if(!_method) { - callback(new Error('No HTTP method specified in options')); - return; - } - _method = _method.toUpperCase(); - if(!(_method === 'GET' || _method === 'POST')) { - callback(new Error('HTTP method can only be ')); - return; - } - function done(err, event) { - xdr.onload = xdr.onerror = xdr.ontimeout = function() {}; - xdr = void 0; - callback(err, event); - } +// @remove +OTHelpers.isDisplayNone = function(element) { + return $(element).isDisplayNone(); +}; - xdr.onload = function() { - done(null, { - target: { - responseText: xdr.responseText, - headers: { - 'content-type': xdr.contentType - } - } - }); - }; +OTHelpers.findElementWithDisplayNone = function(element) { + if ( (element.offsetWidth === 0 || element.offsetHeight === 0) && + $.css(element, 'display') === 'none') return element; - xdr.onerror = function() { - done(new Error('XDomainRequest of ' + url + ' failed')); - }; + if (element.parentNode && element.parentNode.style) { + return $.findElementWithDisplayNone(element.parentNode); + } - xdr.ontimeout = function() { - done(new Error('XDomainRequest of ' + url + ' timed out')); - }; + return null; +}; - xdr.open(_method, url); - xdr.send(options.body && formatPostData(options.body)); - }; +/*jshint browser:true, smarttabs:true*/ - OTHelpers.request = function(url, options, callback) { - var request = new XMLHttpRequest(), - _options = options || {}, - _method = _options.method; +// tb_require('../helpers.js') +// tb_require('./environment.js') +// tb_require('./dom.js') - if(!_method) { - callback(new Error('No HTTP method specified in options')); - return; - } +OTHelpers.Modal = function(options) { - // Setup callbacks to correctly respond to success and error callbacks. This includes - // interpreting the responses HTTP status, which XmlHttpRequest seems to ignore - // by default. - if(callback) { - OTHelpers.on(request, 'load', function(event) { - var status = event.target.status; + OTHelpers.eventing(this, true); - // We need to detect things that XMLHttpRequest considers a success, - // but we consider to be failures. - if ( status >= 200 && status < 300 || status === 304 ) { - callback(null, event); - } else { - callback(event); - } - }); + var callback = arguments[arguments.length - 1]; - OTHelpers.on(request, 'error', callback); - } + if(!OTHelpers.isFunction(callback)) { + throw new Error('OTHelpers.Modal2 must be given a callback'); + } - request.open(options.method, url, true); + if(arguments.length < 2) { + options = {}; + } - if (!_options.headers) _options.headers = {}; + var domElement = document.createElement('iframe'); - for (var name in _options.headers) { - request.setRequestHeader(name, _options.headers[name]); - } + domElement.id = options.id || OTHelpers.uuid(); + domElement.style.position = 'absolute'; + domElement.style.position = 'fixed'; + domElement.style.height = '100%'; + domElement.style.width = '100%'; + domElement.style.top = '0px'; + domElement.style.left = '0px'; + domElement.style.right = '0px'; + domElement.style.bottom = '0px'; + domElement.style.zIndex = 1000; + domElement.style.border = '0'; - request.send(options.body && formatPostData(options.body)); - }; + try { + domElement.style.backgroundColor = 'rgba(0,0,0,0.2)'; + } catch (err) { + // Old IE browsers don't support rgba and we still want to show the upgrade message + // but we just make the background of the iframe completely transparent. + domElement.style.backgroundColor = 'transparent'; + domElement.setAttribute('allowTransparency', 'true'); + } - OTHelpers.get = function(url, options, callback) { - var _options = OTHelpers.extend(options || {}, { - method: 'GET' - }); - OTHelpers.request(url, _options, callback); - }; + domElement.scrolling = 'no'; + domElement.setAttribute('scrolling', 'no'); - OTHelpers.post = function(url, options, callback) { - var _options = OTHelpers.extend(options || {}, { - method: 'POST' - }); + // This is necessary for IE, as it will not inherit it's doctype from + // the parent frame. + var frameContent = '' + + '' + + '' + + ''; - if(_options.xdomainrequest) { - OTHelpers.xdomainRequest(url, _options, callback); - } else { - OTHelpers.request(url, _options, callback); + var wrappedCallback = function() { + var doc = domElement.contentDocument || domElement.contentWindow.document; + + if (OTHelpers.env.iframeNeedsLoad) { + doc.body.style.backgroundColor = 'transparent'; + doc.body.style.border = 'none'; + + if (OTHelpers.env.name !== 'IE') { + // Skip this for IE as we use the bookmarklet workaround + // for THAT browser. + doc.open(); + doc.write(frameContent); + doc.close(); + } } + + callback( + domElement.contentWindow, + doc + ); }; -})(window, window.OTHelpers); -!(function(window) { + document.body.appendChild(domElement); - /* global OTHelpers */ + if(OTHelpers.env.iframeNeedsLoad) { + if (OTHelpers.env.name === 'IE') { + // This works around some issues with IE and document.write. + // Basically this works by slightly abusing the bookmarklet/scriptlet + // functionality that all browsers support. + domElement.contentWindow.contents = frameContent; + /*jshint scripturl:true*/ + domElement.src = 'javascript:window["contents"]'; + /*jshint scripturl:false*/ + } + + OTHelpers.on(domElement, 'load', wrappedCallback); + } else { + setTimeout(wrappedCallback, 0); + } + + this.close = function() { + OTHelpers.removeElement(domElement); + this.trigger('closed'); + this.element = domElement = null; + return this; + }; - if (!window.OT) window.OT = {}; + this.element = domElement; - // Bring OTHelpers in as OT.$ - OT.$ = OTHelpers.noConflict(); +}; - // Allow events to be bound on OT - OT.$.eventing(OT); +/* + * getComputedStyle from + * https://github.com/jonathantneal/Polyfills-for-IE8/blob/master/getComputedStyle.js - // REMOVE THIS POST IE MERGE +// tb_require('../helpers.js') +// tb_require('./dom.js') - OT.$.defineGetters = function(self, getters, enumerable) { - var propsDefinition = {}; +/*jshint strict: false, eqnull: true, browser:true, smarttabs:true*/ - if (enumerable === void 0) enumerable = false; +(function() { - for (var key in getters) { - if(!getters.hasOwnProperty(key)) { - continue; - } - propsDefinition[key] = { - get: getters[key], - enumerable: enumerable - }; - } + /*jshint eqnull: true, browser: true */ - Object.defineProperties(self, propsDefinition); - }; - // STOP REMOVING HERE + function getPixelSize(element, style, property, fontSize) { + var sizeWithSuffix = style[property], + size = parseFloat(sizeWithSuffix), + suffix = sizeWithSuffix.split(/\d/)[0], + rootSize; + + fontSize = fontSize != null ? + fontSize : /%|em/.test(suffix) && element.parentElement ? + getPixelSize(element.parentElement, element.parentElement.currentStyle, 'fontSize', null) : + 16; + rootSize = property === 'fontSize' ? + fontSize : /width/i.test(property) ? element.clientWidth : element.clientHeight; + + return (suffix === 'em') ? + size * fontSize : (suffix === 'in') ? + size * 96 : (suffix === 'pt') ? + size * 96 / 72 : (suffix === '%') ? + size / 100 * rootSize : size; + } - // OT.$.Modal was OT.Modal before the great common-js-helpers move - OT.Modal = OT.$.Modal; + function setShortStyleProperty(style, property) { + var + borderSuffix = property === 'border' ? 'Width' : '', + t = property + 'Top' + borderSuffix, + r = property + 'Right' + borderSuffix, + b = property + 'Bottom' + borderSuffix, + l = property + 'Left' + borderSuffix; - // Add logging methods - OT.$.useLogHelpers(OT); + style[property] = (style[t] === style[r] === style[b] === style[l] ? [style[t]] + : style[t] === style[b] && style[l] === style[r] ? [style[t], style[r]] + : style[l] === style[r] ? [style[t], style[r], style[b]] + : [style[t], style[r], style[b], style[l]]).join(' '); + } - var _debugHeaderLogged = false, - _setLogLevel = OT.setLogLevel; + function CSSStyleDeclaration(element) { + var currentStyle = element.currentStyle, + style = this, + fontSize = getPixelSize(element, currentStyle, 'fontSize', null), + property; - // On the first time log level is set to DEBUG (or higher) show version info. - OT.setLogLevel = function(level) { - // Set OT.$ to the same log level - OT.$.setLogLevel(level); - var retVal = _setLogLevel.call(OT, level); - if (OT.shouldLog(OT.DEBUG) && !_debugHeaderLogged) { - OT.debug('OpenTok JavaScript library ' + OT.properties.version); - OT.debug('Release notes: ' + OT.properties.websiteURL + - '/opentok/webrtc/docs/js/release-notes.html'); - OT.debug('Known issues: ' + OT.properties.websiteURL + - '/opentok/webrtc/docs/js/release-notes.html#knownIssues'); - _debugHeaderLogged = true; + for (property in currentStyle) { + if (/width|height|margin.|padding.|border.+W/.test(property) && style[property] !== 'auto') { + style[property] = getPixelSize(element, currentStyle, property, fontSize) + 'px'; + } else if (property === 'styleFloat') { + /*jshint -W069 */ + style['float'] = currentStyle[property]; + } else { + style[property] = currentStyle[property]; + } } - OT.debug('OT.setLogLevel(' + retVal + ')'); - return retVal; - }; - var debugTrue = OT.properties.debug === 'true' || OT.properties.debug === true; - OT.setLogLevel(debugTrue ? OT.DEBUG : OT.ERROR); + setShortStyleProperty(style, 'margin'); + setShortStyleProperty(style, 'padding'); + setShortStyleProperty(style, 'border'); - OT.$.userAgent = function() { - var userAgent = navigator.userAgent; - if (TBPlugin.isInstalled()) userAgent += '; TBPlugin ' + TBPlugin.version(); - return userAgent; - }; + style.fontSize = fontSize + 'px'; - /** - * Sets the API log level. - *

    - * Calling OT.setLogLevel() sets the log level for runtime log messages that - * are the OpenTok library generates. The default value for the log level is OT.ERROR. - *

    - *

    - * The OpenTok JavaScript library displays log messages in the debugger console (such as - * Firebug), if one exists. - *

    - *

    - * The following example logs the session ID to the console, by calling OT.log(). - * The code also logs an error message when it attempts to publish a stream before the Session - * object dispatches a sessionConnected event. - *

    - *
    -  * OT.setLogLevel(OT.LOG);
    -  * session = OT.initSession(sessionId);
    -  * OT.log(sessionId);
    -  * publisher = OT.initPublisher("publishContainer");
    -  * session.publish(publisher);
    -  * 
    - * - * @param {Number} logLevel The degree of logging desired by the developer: - * - *

    - *

      - *
    • - * OT.NONE — API logging is disabled. - *
    • - *
    • - * OT.ERROR — Logging of errors only. - *
    • - *
    • - * OT.WARN — Logging of warnings and errors. - *
    • - *
    • - * OT.INFO — Logging of other useful information, in addition to - * warnings and errors. - *
    • - *
    • - * OT.LOG — Logging of OT.log() messages, in addition - * to OpenTok info, warning, - * and error messages. - *
    • - *
    • - * OT.DEBUG — Fine-grained logging of all API actions, as well as - * OT.log() messages. - *
    • - *
    - *

    - * - * @name OT.setLogLevel - * @memberof OT - * @function - * @see OT.log() - */ - - /** - * Sends a string to the the debugger console (such as Firebug), if one exists. - * However, the function only logs to the console if you have set the log level - * to OT.LOG or OT.DEBUG, - * by calling OT.setLogLevel(OT.LOG) or OT.setLogLevel(OT.DEBUG). - * - * @param {String} message The string to log. - * - * @name OT.log - * @memberof OT - * @function - * @see OT.setLogLevel() - */ - -})(window); -!(function() { + return style; + } - var addCss = function(document, url, callback) { - var head = document.head || document.getElementsByTagName('head')[0]; - var cssTag = OT.$.createElement('link', { - type: 'text/css', - media: 'screen', - rel: 'stylesheet', - href: url - }); - head.appendChild(cssTag); - OT.$.on(cssTag, 'error', function(error) { - OT.error('Could not load CSS for dialog', url, error && error.message || error); - }); - OT.$.on(cssTag, 'load', callback); + CSSStyleDeclaration.prototype = { + constructor: CSSStyleDeclaration, + getPropertyPriority: function () {}, + getPropertyValue: function ( prop ) { + return this[prop] || ''; + }, + item: function () {}, + removeProperty: function () {}, + setProperty: function () {}, + getPropertyCSSValue: function () {} }; - var addDialogCSS = function(document, urls, callback) { - var allURLs = [ - '//fonts.googleapis.com/css?family=Didact+Gothic', - OT.properties.cssURL - ].concat(urls); - var remainingStylesheets = allURLs.length; - OT.$.forEach(allURLs, function(stylesheetUrl) { - addCss(document, stylesheetUrl, function() { - if(--remainingStylesheets <= 0) { - callback(); - } - }); - }); + function getComputedStyle(element) { + return new CSSStyleDeclaration(element); + } - }; - var templateElement = function(classes, children, tagName) { - var el = OT.$.createElement(tagName || 'div', { 'class': classes }, children, this); - el.on = OT.$.bind(OT.$.on, OT.$, el); - el.off = OT.$.bind(OT.$.off, OT.$, el); - return el; + OTHelpers.getComputedStyle = function(element) { + if(element && + element.ownerDocument && + element.ownerDocument.defaultView && + element.ownerDocument.defaultView.getComputedStyle) { + return element.ownerDocument.defaultView.getComputedStyle(element); + } else { + return getComputedStyle(element); + } }; - var checkBoxElement = function (classes, nameAndId, onChange) { - var checkbox = templateElement.call(this, '', null, 'input').on('change', onChange); +})(); - if (OT.$.browser() === 'IE' && OT.$.browserVersion().version <= 8) { - // Fix for IE8 not triggering the change event - checkbox.on('click', function() { - checkbox.blur(); - checkbox.focus(); - }); - } +/*jshint browser:true, smarttabs:true */ + +// tb_require('../helpers.js') +// tb_require('./callbacks.js') +// tb_require('./dom.js') - checkbox.setAttribute('name', nameAndId); - checkbox.setAttribute('id', nameAndId); - checkbox.setAttribute('type', 'checkbox'); +var observeStyleChanges = function observeStyleChanges (element, stylesToObserve, onChange) { + var oldStyles = {}; - return checkbox; - }; + var getStyle = function getStyle(style) { + switch (style) { + case 'width': + return $(element).width(); - var linkElement = function(children, href, classes) { - var link = templateElement.call(this, classes || '', children, 'a'); - link.setAttribute('href', href); - return link; + case 'height': + return $(element).height(); + + default: + return $(element).css(style); + } }; - OT.Dialogs = {}; + // get the inital values + $.forEach(stylesToObserve, function(style) { + oldStyles[style] = getStyle(style); + }); - OT.Dialogs.AllowDeny = { - Chrome: {}, - Firefox: {} - }; + var observer = new MutationObserver(function(mutations) { + var changeSet = {}; - OT.Dialogs.AllowDeny.Chrome.initialPrompt = function() { - var modal = new OT.$.Modal(function(window, document) { + $.forEach(mutations, function(mutation) { + if (mutation.attributeName !== 'style') return; - var el = OT.$.bind(templateElement, document), - close, root; + var isHidden = $.isDisplayNone(element); - close = el('OT_closeButton', '×') - .on('click', function() { - modal.trigger('closeButtonClicked'); - modal.close(); - }); + $.forEach(stylesToObserve, function(style) { + if(isHidden && (style === 'width' || style === 'height')) return; - root = el('OT_root OT_dialog OT_dialog-allow-deny-chrome-first', [ - close, - el('OT_dialog-messages', [ - el('OT_dialog-messages-main', 'Allow camera and mic access'), - el('OT_dialog-messages-minor', 'Click the Allow button in the upper-right corner ' + - 'of your browser to enable real-time communication.'), - el('OT_dialog-allow-highlight-chrome') - ]) - ]); + var newValue = getStyle(style); - addDialogCSS(document, [], function() { - document.body.appendChild(root); + if (newValue !== oldStyles[style]) { + changeSet[style] = [oldStyles[style], newValue]; + oldStyles[style] = newValue; + } }); - }); - return modal; - }; - OT.Dialogs.AllowDeny.Chrome.previouslyDenied = function(website) { - var modal = new OT.$.Modal(function(window, document) { - - var el = OT.$.bind(templateElement, document), - close, - root; + if (!$.isEmpty(changeSet)) { + // Do this after so as to help avoid infinite loops of mutations. + $.callAsync(function() { + onChange.call(null, changeSet); + }); + } + }); - close = el('OT_closeButton', '×') - .on('click', function() { - modal.trigger('closeButtonClicked'); - modal.close(); - }); + observer.observe(element, { + attributes:true, + attributeFilter: ['style'], + childList:false, + characterData:false, + subtree:false + }); - root = el('OT_root OT_dialog OT_dialog-allow-deny-chrome-pre-denied', [ - close, - el('OT_dialog-messages', [ - el('OT_dialog-messages-main', 'Allow camera and mic access'), - el('OT_dialog-messages-minor', [ - 'To interact with this app, follow these 3 steps:', - el('OT_dialog-3steps', [ - el('OT_dialog-3steps-step', [ - el('OT_dialog-3steps-step-num', '1'), - 'Find this icon in the URL bar and click it', - el('OT_dialog-allow-camera-icon') - ]), - el('OT_dialog-3steps-seperator'), - el('OT_dialog-3steps-step', [ - el('OT_dialog-3steps-step-num', '2'), - 'Select "Ask if ' + website + ' wants to access your camera and mic" ' + - 'and then click Done.' - ]), - el('OT_dialog-3steps-seperator'), - el('OT_dialog-3steps-step', [ - el('OT_dialog-3steps-step-num', '3'), - 'Refresh your browser.' - ]) - ]) - ]) - ]) - ]); + return observer; +}; - addDialogCSS(document, [], function() { - document.body.appendChild(root); - }); +var observeNodeOrChildNodeRemoval = function observeNodeOrChildNodeRemoval (element, onChange) { + var observer = new MutationObserver(function(mutations) { + var removedNodes = []; + $.forEach(mutations, function(mutation) { + if (mutation.removedNodes.length) { + removedNodes = removedNodes.concat(prototypeSlice.call(mutation.removedNodes)); + } }); - return modal; - }; - OT.Dialogs.AllowDeny.Chrome.deniedNow = function() { - var modal = new OT.$.Modal(function(window, document) { + if (removedNodes.length) { + // Do this after so as to help avoid infinite loops of mutations. + $.callAsync(function() { + onChange($(removedNodes)); + }); + } + }); - var el = OT.$.bind(templateElement, document), - root; + observer.observe(element, { + attributes:false, + childList:true, + characterData:false, + subtree:true + }); - root = el('OT_root OT_dialog-blackout', - el('OT_dialog OT_dialog-allow-deny-chrome-now-denied', [ - el('OT_dialog-messages', [ - el('OT_dialog-messages-main ', - el('OT_dialog-allow-camera-icon') - ), - el('OT_dialog-messages-minor', - 'Find & click this icon to allow camera and mic access.' - ) - ]) - ]) - ); + return observer; +}; - addDialogCSS(document, [], function() { - document.body.appendChild(root); - }); +// Allows an +onChange+ callback to be triggered when specific style properties +// of +element+ are notified. The callback accepts a single parameter, which is +// a hash where the keys are the style property that changed and the values are +// an array containing the old and new values ([oldValue, newValue]). +// +// Width and Height changes while the element is display: none will not be +// fired until such time as the element becomes visible again. +// +// This function returns the MutationObserver itself. Once you no longer wish +// to observe the element you should call disconnect on the observer. +// +// Observing changes: +// // observe changings to the width and height of object +// dimensionsObserver = OTHelpers(object).observeStyleChanges(, +// ['width', 'height'], function(changeSet) { +// OT.debug("The new width and height are " + +// changeSet.width[1] + ',' + changeSet.height[1]); +// }); +// +// Cleaning up +// // stop observing changes +// dimensionsObserver.disconnect(); +// dimensionsObserver = null; +// +ElementCollection.prototype.observeStyleChanges = function(stylesToObserve, onChange) { + var observers = []; - }); - return modal; - }; + this.forEach(function(element) { + observers.push( + observeStyleChanges(element, stylesToObserve, onChange) + ); + }); - OT.Dialogs.AllowDeny.Firefox.maybeDenied = function() { - var modal = new OT.$.Modal(function(window, document) { + return observers; +}; - var el = OT.$.bind(templateElement, document), - close, - root; +// trigger the +onChange+ callback whenever +// 1. +element+ is removed +// 2. or an immediate child of +element+ is removed. +// +// This function returns the MutationObserver itself. Once you no longer wish +// to observe the element you should call disconnect on the observer. +// +// Observing changes: +// // observe changings to the width and height of object +// nodeObserver = OTHelpers(object).observeNodeOrChildNodeRemoval(function(removedNodes) { +// OT.debug("Some child nodes were removed"); +// removedNodes.forEach(function(node) { +// OT.debug(node); +// }); +// }); +// +// Cleaning up +// // stop observing changes +// nodeObserver.disconnect(); +// nodeObserver = null; +// +ElementCollection.prototype.observeNodeOrChildNodeRemoval = function(onChange) { + var observers = []; - close = el('OT_closeButton', '×') - .on('click', function() { - modal.trigger('closeButtonClicked'); - modal.close(); - }); + this.forEach(function(element) { + observers.push( + observeNodeOrChildNodeRemoval(element, onChange) + ); + }); - root = el('OT_root OT_dialog OT_dialog-allow-deny-firefox-maybe-denied', [ - close, - el('OT_dialog-messages', [ - el('OT_dialog-messages-main', 'Please allow camera & mic access'), - el('OT_dialog-messages-minor', [ - 'To interact with this app, follow these 3 steps:', - el('OT_dialog-3steps', [ - el('OT_dialog-3steps-step', [ - el('OT_dialog-3steps-step-num', '1'), - 'Reload the page, or click the camera icon ' + - 'in the browser URL bar.' - ]), - el('OT_dialog-3steps-seperator'), - el('OT_dialog-3steps-step', [ - el('OT_dialog-3steps-step-num', '2'), - 'In the menu, select your camera & mic.' - ]), - el('OT_dialog-3steps-seperator'), - el('OT_dialog-3steps-step', [ - el('OT_dialog-3steps-step-num', '3'), - 'Click "Share Selected Devices."' - ]) - ]) - ]) - ]) - ]); + return observers; +}; - addDialogCSS(document, [], function() { - document.body.appendChild(root); - }); - }); - return modal; - }; +// @remove +OTHelpers.observeStyleChanges = function(element, stylesToObserve, onChange) { + return $(element).observeStyleChanges(stylesToObserve, onChange)[0]; +}; - OT.Dialogs.AllowDeny.Firefox.denied = function() { - var modal = new OT.$.Modal(function(window, document) { +// @remove +OTHelpers.observeNodeOrChildNodeRemoval = function(element, onChange) { + return $(element).observeNodeOrChildNodeRemoval(onChange)[0]; +}; - var el = OT.$.bind(templateElement, document), - btn = OT.$.bind(templateElement, document, 'OT_dialog-button OT_dialog-button-large'), - root, - refreshButton; +/*jshint browser:true, smarttabs:true */ - refreshButton = btn('Reload') - .on('click', function() { - modal.trigger('refresh'); - }); +// tb_require('../helpers.js') +// tb_require('./dom.js') +// tb_require('./capabilities.js') - root = el('OT_root OT_dialog-blackout', - el('OT_dialog OT_dialog-allow-deny-firefox-denied', [ - el('OT_dialog-messages', [ - el('OT_dialog-messages-minor', - 'Access to camera and microphone has been denied. ' + - 'Click the button to reload page.' - ) - ]), - el('OT_dialog-single-button', refreshButton) - ]) - ); +// Returns true if the client supports element.classList +OTHelpers.registerCapability('classList', function() { + return (typeof document !== 'undefined') && ('classList' in document.createElement('a')); +}); - addDialogCSS(document, [], function() { - document.body.appendChild(root); - }); - }); +function hasClass (element, className) { + if (!className) return false; - return modal; - }; + if ($.hasCapabilities('classList')) { + return element.classList.contains(className); + } - OT.Dialogs.Plugin = {}; + return element.className.indexOf(className) > -1; +} - OT.Dialogs.Plugin.promptToInstall = function() { - var modal = new OT.$.Modal(function(window, document) { +function toggleClasses (element, classNames) { + if (!classNames || classNames.length === 0) return; - var el = OT.$.bind(templateElement, document), - btn = function(children, size) { - var classes = 'OT_dialog-button ' + - (size ? 'OT_dialog-button-' + size : 'OT_dialog-button-large'), - b = el(classes, children); + // Only bother targeting Element nodes, ignore Text Nodes, CDATA, etc + if (element.nodeType !== 1) { + return; + } - b.enable = function() { - OT.$.removeClass(this, 'OT_dialog-button-disabled'); - return this; - }; + var numClasses = classNames.length, + i = 0; - b.disable = function() { - OT.$.addClass(this, 'OT_dialog-button-disabled'); - return this; - }; + if ($.hasCapabilities('classList')) { + for (; i 99) { - OT.$.css(progressBar, 'width', ''); - progressText.innerHTML = '100%'; - } else if(newProgress < 1) { - OT.$.css(progressBar, 'width', '0%'); - progressText.innerHTML = '0%'; - } else { - OT.$.css(progressBar, 'width', newProgress + '%'); - progressText.innerHTML = newProgress + '%'; - } - } else { - progressValue = newProgress; - } - }; +// @remove +OTHelpers.addClass = function(element, className) { + return $(element).addClass(className); +}; - return modal; - }; +// @remove +OTHelpers.removeClass = function(element, value) { + return $(element).removeClass(value); +}; - OT.Dialogs.Plugin.updateComplete = function(error) { - var modal = new OT.$.Modal(function(window, document) { - var el = OT.$.bind(templateElement, document), - reloadButton, - root; - reloadButton = el('OT_dialog-button', 'Reload').on('click', function() { - modal.trigger('reload'); - }); +/*jshint browser:true, smarttabs:true */ - var msgs; +// tb_require('../helpers.js') +// tb_require('./dom.js') +// tb_require('./capabilities.js') - if(error) { - msgs = ['Update Failed.', error + '' || 'NO ERROR']; - } else { - msgs = ['Update Complete.', - 'The OpenTok plugin has been succesfully updated. ' + - 'Please reload your browser.']; - } - root = el('OT_root OT_dialog OT_dialog-plugin-upgraded', [ - el('OT_dialog-messages', [ - el('OT_dialog-messages-main', msgs[0]), - el('OT_dialog-messages-minor', msgs[1]) - ]), - el('OT_dialog-single-button', reloadButton) - ]); +// Gets or sets the attribute called +name+ for the first element in the collection +ElementCollection.prototype.attr = function (name, value) { + if (OTHelpers.isObject(name)) { + for (var key in name) { + this.first.setAttribute(key, name[key]); + } + } + else if (value === void 0) { + return this.first.getAttribute(name); + } + else { + this.first.setAttribute(name, value); + } - addDialogCSS(document, [], function() { - document.body.appendChild(root); - }); + return this; +}; - }); +// Gets, and optionally sets, the html body of the first element +// in the collection. If the +html+ is provided then the first +// element's html body will be replaced with it. +// +ElementCollection.prototype.html = function (html) { + if (html !== void 0) { + this.first.innerHTML = html; + } - return modal; + return this.first.innerHTML; +}; - }; +// Centers +element+ within the window. You can pass through the width and height +// if you know it, if you don't they will be calculated for you. +ElementCollection.prototype.center = function (width, height) { + var $element; + + this.forEach(function(element) { + $element = $(element); + if (!width) width = parseInt($element.width(), 10); + if (!height) height = parseInt($element.height(), 10); -})(); -!(function(window) { + var marginLeft = -0.5 * width + 'px'; + var marginTop = -0.5 * height + 'px'; - // IMPORTANT This file should be included straight after helpers.js - if (!window.OT) window.OT = {}; + $element.css('margin', marginTop + ' 0 0 ' + marginLeft) + .addClass('OT_centered'); + }); - if (!OT.properties) { - throw new Error('OT.properties does not exist, please ensure that you include a valid ' + - 'properties file.'); - } + return this; +}; - OT.useSSL = function () { - return OT.properties.supportSSL && (window.location.protocol.indexOf('https') >= 0 || - window.location.protocol.indexOf('chrome-extension') >= 0); - }; - // Consumes and overwrites OT.properties. Makes it better and stronger! - OT.properties = function(properties) { - var props = OT.$.clone(properties); +// @remove +// Centers +element+ within the window. You can pass through the width and height +// if you know it, if you don't they will be calculated for you. +OTHelpers.centerElement = function(element, width, height) { + return $(element).center(width, height); +}; - props.debug = properties.debug === 'true' || properties.debug === true; - props.supportSSL = properties.supportSSL === 'true' || properties.supportSSL === true; + /** + * Methods to calculate element widths and heights. + */ +(function() { - if (window.OTProperties) { - // Allow window.OTProperties to override cdnURL, configURL, assetURL and cssURL - if (window.OTProperties.cdnURL) props.cdnURL = window.OTProperties.cdnURL; - if (window.OTProperties.cdnURLSSL) props.cdnURLSSL = window.OTProperties.cdnURLSSL; - if (window.OTProperties.configURL) props.configURL = window.OTProperties.configURL; - if (window.OTProperties.assetURL) props.assetURL = window.OTProperties.assetURL; - if (window.OTProperties.cssURL) props.cssURL = window.OTProperties.cssURL; - } + var _width = function(element) { + if (element.offsetWidth > 0) { + return element.offsetWidth + 'px'; + } - if (!props.assetURL) { - if (OT.useSSL()) { - props.assetURL = props.cdnURLSSL + '/webrtc/' + props.version; - } else { - props.assetURL = props.cdnURL + '/webrtc/' + props.version; + return $(element).css('width'); + }, + + _height = function(element) { + if (element.offsetHeight > 0) { + return element.offsetHeight + 'px'; + } + + return $(element).css('height'); + }; + + ElementCollection.prototype.width = function (newWidth) { + if (newWidth) { + this.css('width', newWidth); + return this; + } + else { + if (this.isDisplayNone()) { + return this.makeVisibleAndYield(function(element) { + return _width(element); + })[0]; + } + else { + return _width(this.get(0)); } } + }; - var isIE89 = OT.$.browser() === 'IE' && OT.$.browserVersion().version <= 9; - if (!(isIE89 && window.location.protocol.indexOf('https') < 0)) { - props.apiURL = props.apiURLSSL; - props.loggingURL = props.loggingURLSSL; + ElementCollection.prototype.height = function (newHeight) { + if (newHeight) { + this.css('height', newHeight); + return this; + } + else { + if (this.isDisplayNone()) { + // We can't get the height, probably since the element is hidden. + return this.makeVisibleAndYield(function(element) { + return _height(element); + })[0]; + } + else { + return _height(this.get(0)); + } } + }; - if (!props.configURL) props.configURL = props.assetURL + '/js/dynamic_config.min.js'; - if (!props.cssURL) props.cssURL = props.assetURL + '/css/ot.min.css'; + // @remove + OTHelpers.width = function(element, newWidth) { + var ret = $(element).width(newWidth); + return newWidth ? OTHelpers : ret; + }; - return props; - }(OT.properties); -})(window); -!(function() { + // @remove + OTHelpers.height = function(element, newHeight) { + var ret = $(element).height(newHeight); + return newHeight ? OTHelpers : ret; + }; -//-------------------------------------- -// JS Dynamic Config -//-------------------------------------- +})(); - OT.Config = (function() { - var _loaded = false, - _global = {}, - _partners = {}, - _script, - _head = document.head || document.getElementsByTagName('head')[0], - _loadTimer, - - _clearTimeout = function() { - if (_loadTimer) { - clearTimeout(_loadTimer); - _loadTimer = null; - } - }, +// CSS helpers helpers - _cleanup = function() { - _clearTimeout(); +/*jshint browser:true, smarttabs:true*/ - if (_script) { - _script.onload = _script.onreadystatechange = null; +// tb_require('../helpers.js') +// tb_require('./dom.js') +// tb_require('./getcomputedstyle.js') - if ( _head && _script.parentNode ) { - _head.removeChild( _script ); - } +(function() { - _script = undefined; - } - }, + var displayStateCache = {}, + defaultDisplays = {}; - _onLoad = function() { - // Only IE and Opera actually support readyState on Script elements. - if (_script.readyState && !/loaded|complete/.test( _script.readyState )) { - // Yeah, we're not ready yet... - return; - } + var defaultDisplayValueForElement = function (element) { + if (defaultDisplays[element.ownerDocument] && + defaultDisplays[element.ownerDocument][element.nodeName]) { + return defaultDisplays[element.ownerDocument][element.nodeName]; + } - _clearTimeout(); + if (!defaultDisplays[element.ownerDocument]) defaultDisplays[element.ownerDocument] = {}; - if (!_loaded) { - // Our config script is loaded but there is not config (as - // replaceWith wasn't called). Something went wrong. Possibly - // the file we loaded wasn't actually a valid config file. - _this._onLoadTimeout(); - } - }, + // We need to know what display value to use for this node. The easiest way + // is to actually create a node and read it out. + var testNode = element.ownerDocument.createElement(element.nodeName), + defaultDisplay; - _getModule = function(moduleName, apiKey) { - if (apiKey && _partners[apiKey] && _partners[apiKey][moduleName]) { - return _partners[apiKey][moduleName]; - } + element.ownerDocument.body.appendChild(testNode); + defaultDisplay = defaultDisplays[element.ownerDocument][element.nodeName] = + $(testNode).css('display'); - return _global[moduleName]; - }, + $(testNode).remove(); + testNode = null; + + return defaultDisplay; + }; - _this; + var isHidden = function (element) { + var computedStyle = $.getComputedStyle(element); + return computedStyle.getPropertyValue('display') === 'none'; + }; - _this = { - // In ms - loadTimeout: 4000, + var setCssProperties = function (element, hash) { + var style = element.style; - load: function(configUrl) { - if (!configUrl) throw new Error('You must pass a valid configUrl to Config.load'); + for (var cssName in hash) { + if (hash.hasOwnProperty(cssName)) { + style[cssName] = hash[cssName]; + } + } + }; - _loaded = false; + var setCssProperty = function (element, name, value) { + element.style[name] = value; + }; - setTimeout(function() { - _script = document.createElement( 'script' ); - _script.async = 'async'; - _script.src = configUrl; - _script.onload = _script.onreadystatechange = OT.$.bind(_onLoad, this); - _head.appendChild(_script); - },1); + var getCssProperty = function (element, unnormalisedName) { + // Normalise vendor prefixes from the form MozTranform to -moz-transform + // except for ms extensions, which are weird... - _loadTimer = setTimeout(function() { - _this._onLoadTimeout(); - }, this.loadTimeout); - }, + var name = unnormalisedName.replace( /([A-Z]|^ms)/g, '-$1' ).toLowerCase(), + computedStyle = $.getComputedStyle(element), + currentValue = computedStyle.getPropertyValue(name); - _onLoadTimeout: function() { - _cleanup(); + if (currentValue === '') { + currentValue = element.style[name]; + } - OT.warn('TB DynamicConfig failed to load in ' + _this.loadTimeout + ' ms'); - this.trigger('dynamicConfigLoadFailed'); - }, + return currentValue; + }; - isLoaded: function() { - return _loaded; - }, + var applyCSS = function(element, styles, callback) { + var oldStyles = {}, + name, + ret; - reset: function() { - _cleanup(); - _loaded = false; - _global = {}; - _partners = {}; - }, + // Backup the old styles + for (name in styles) { + if (styles.hasOwnProperty(name)) { + // We intentionally read out of style here, instead of using the css + // helper. This is because the css helper uses querySelector and we + // only want to pull values out of the style (domeElement.style) hash. + oldStyles[name] = element.style[name]; - // This is public so that the dynamic config file can load itself. - // Using it for other purposes is discouraged, but not forbidden. - replaceWith: function(config) { - _cleanup(); + $(element).css(name, styles[name]); + } + } - if (!config) config = {}; + ret = callback(element); - _global = config.global || {}; - _partners = config.partners || {}; + // Restore the old styles + for (name in styles) { + if (styles.hasOwnProperty(name)) { + $(element).css(name, oldStyles[name] || ''); + } + } - if (!_loaded) _loaded = true; - this.trigger('dynamicConfigChanged'); - }, + return ret; + }; - // @example Get the value that indicates whether exceptionLogging is enabled - // OT.Config.get('exceptionLogging', 'enabled'); - // - // @example Get a key for a specific partner, fallback to the default if there is - // no key for that partner - // OT.Config.get('exceptionLogging', 'enabled', 'apiKey'); - // - get: function(moduleName, key, apiKey) { - var module = _getModule(moduleName, apiKey); - return module ? module[key] : null; + ElementCollection.prototype.show = function() { + return this.forEach(function(element) { + var display = element.style.display; + + if (display === '' || display === 'none') { + element.style.display = displayStateCache[element] || ''; + delete displayStateCache[element]; } - }; - OT.$.eventing(_this); + if (isHidden(element)) { + // It's still hidden so there's probably a stylesheet that declares this + // element as display:none; + displayStateCache[element] = 'none'; - return _this; - })(); + element.style.display = defaultDisplayValueForElement(element); + } + }); + }; -})(window); -/** - * @license TB Plugin 0.4.0.8 72b534e HEAD - * http://www.tokbox.com/ - * - * Copyright (c) 2014 TokBox, Inc. - * Released under the MIT license - * http://opensource.org/licenses/MIT - * - * Date: September 08 10:17:49 2014 - * - */ + ElementCollection.prototype.hide = function() { + return this.forEach(function(element) { + if (element.style.display === 'none') return; -/* jshint globalstrict: true, strict: false, undef: true, unused: false, - trailing: true, browser: true, smarttabs:true */ -/* global scope:true, OT:true */ -/* exported TBPlugin */ + displayStateCache[element] = element.style.display; + element.style.display = 'none'; + }); + }; -/* jshint ignore:start */ -(function(scope) { -/* jshint ignore:end */ + ElementCollection.prototype.css = function(nameOrHash, value) { + if (this.length === 0) return; -// If we've already be setup, bail -if (scope.TBPlugin !== void 0) return; + if (typeof(nameOrHash) !== 'string') { -// TB must exist first, otherwise we can't do anything -if (scope.OT === void 0) return; + return this.forEach(function(element) { + setCssProperties(element, nameOrHash); + }); -// Establish the environment that we're running in -var env = OT.$.browserVersion(), - isSupported = env.browser === 'IE' && env.version >= 8, - pluginReady = false; + } else if (value !== undefined) { -var TBPlugin = { - isSupported: function () { return isSupported; }, - isReady: function() { return pluginReady; } -}; + return this.forEach(function(element) { + setCssProperty(element, nameOrHash, value); + }); + } else { + return getCssProperty(this.first, nameOrHash, value); + } + }; -scope.TBPlugin = TBPlugin; + // Apply +styles+ to +element+ while executing +callback+, restoring the previous + // styles after the callback executes. + ElementCollection.prototype.applyCSS = function (styles, callback) { + var results = []; -// We only support IE, version 10 or above right now -if (!TBPlugin.isSupported()) { - TBPlugin.isInstalled = function isInstalled () { return false; }; - return; -} + this.forEach(function(element) { + results.push(applyCSS(element, styles, callback)); + }); -// tb_require('./header.js') + return results; + }; -/* exported shim */ -// Shims for various missing things from JS -// Applied only after init is called to prevent unnecessary polution -var shim = function shim () { - if (!Function.prototype.bind) { - Function.prototype.bind = function (oThis) { - if (typeof this !== 'function') { - // closest thing possible to the ECMAScript 5 internal IsCallable function - throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); - } + // Make +element+ visible while executing +callback+. + ElementCollection.prototype.makeVisibleAndYield = function (callback) { + var hiddenVisually = { + display: 'block', + visibility: 'hidden' + }, + results = []; - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - FNOP = function () {}, - fBound = function () { - return fToBind.apply(this instanceof FNOP && oThis ? - this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); - }; + this.forEach(function(element) { + // find whether it's the element or an ancestor that's display none and + // then apply to whichever it is + var targetElement = $.findElementWithDisplayNone(element); + if (!targetElement) { + results.push(void 0); + } + else { + results.push( + applyCSS(targetElement, hiddenVisually, callback) + ); + } + }); - FNOP.prototype = this.prototype; - fBound.prototype = new FNOP(); + return results; + }; - return fBound; - }; - } - if(!Array.isArray) { - Array.isArray = function (vArg) { - return Object.prototype.toString.call(vArg) === '[object Array]'; - }; - } + // @remove + OTHelpers.show = function(element) { + return $(element).show(); + }; - if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (searchElement, fromIndex) { - var i, - pivot = (fromIndex) ? fromIndex : 0, - length; + // @remove + OTHelpers.hide = function(element) { + return $(element).hide(); + }; - if (!this) { - throw new TypeError(); - } + // @remove + OTHelpers.css = function(element, nameOrHash, value) { + return $(element).css(nameOrHash, value); + }; - length = this.length; + // @remove + OTHelpers.applyCSS = function(element, styles, callback) { + return $(element).applyCSS(styles, callback); + }; - if (length === 0 || pivot >= length) { - return -1; - } + // @remove + OTHelpers.makeVisibleAndYield = function(element, callback) { + return $(element).makeVisibleAndYield(callback); + }; - if (pivot < 0) { - pivot = length - Math.abs(pivot); - } +})(); - for (i = pivot; i < length; i++) { - if (this[i] === searchElement) { - return i; - } - } - return -1; - }; - } +// tb_require('../helpers.js') - if (!Array.prototype.map) - { - Array.prototype.map = function(fun /*, thisArg */) - { - 'use strict'; +/**@licence + * Copyright (c) 2010 Caolan McMahon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + **/ - if (this === void 0 || this === null) - throw new TypeError(); - var t = Object(this); - var len = t.length >>> 0; - if (typeof fun !== 'function') { - throw new TypeError(); - } +(function() { - var res = new Array(len); - var thisArg = arguments.length >= 2 ? arguments[1] : void 0; - for (var i = 0; i < len; i++) - { - // NOTE: Absolute correctness would demand Object.defineProperty - // be used. But this method is fairly new, and failure is - // possible only if Object.prototype or Array.prototype - // has a property |i| (very unlikely), so use a less-correct - // but more portable alternative. - if (i in t) - res[i] = fun.call(thisArg, t[i], i, t); + OTHelpers.setImmediate = (function() { + if (typeof process === 'undefined' || !(process.nextTick)) { + if (typeof setImmediate === 'function') { + return function (fn) { + // not a direct alias for IE10 compatibility + setImmediate(fn); + }; } + return function (fn) { + setTimeout(fn, 0); + }; + } + if (typeof setImmediate !== 'undefined') { + return setImmediate; + } + return process.nextTick; + })(); - return res; + OTHelpers.iterator = function(tasks) { + var makeCallback = function (index) { + var fn = function () { + if (tasks.length) { + tasks[index].apply(null, arguments); + } + return fn.next(); + }; + fn.next = function () { + return (index < tasks.length - 1) ? makeCallback(index + 1) : null; + }; + return fn; }; - } -}; -// tb_require('./header.js') -// tb_require('./shims.js') + return makeCallback(0); + }; -/* jshint globalstrict: true, strict: false, undef: true, unused: true, - trailing: true, browser: true, smarttabs:true */ -/* global OT:true, TBPlugin:true, pluginInfo:true, debug:true, scope:true, - _document:true */ -/* exported createMediaCaptureController:true, createPeerController:true, - injectObject:true, plugins:true, mediaCaptureObject:true, - removeAllObjects:true, curryCallAsync:true */ + OTHelpers.waterfall = function(array, done) { + done = done || function () {}; + if (array.constructor !== Array) { + return done(new Error('First argument to waterfall must be an array of functions')); + } -var objectTimeouts = {}, - mediaCaptureObject, - plugins = {}; + if (!array.length) { + return done(); + } -var curryCallAsync = function curryCallAsync (fn) { - return function() { - var args = Array.prototype.slice.call(arguments); - args.unshift(fn); - OT.$.callAsync.apply(OT.$, args); + var next = function(iterator) { + return function (err) { + if (err) { + done.apply(null, arguments); + done = function () {}; + } else { + var args = prototypeSlice.call(arguments, 1), + nextFn = iterator.next(); + if (nextFn) { + args.push(next(nextFn)); + } else { + args.push(done); + } + OTHelpers.setImmediate(function() { + iterator.apply(null, args); + }); + } + }; + }; + + next(OTHelpers.iterator(array))(); }; -}; -var generatePluginUuid = function generatePluginUuid () { - return OT.$.uuid().replace(/\-+/g, ''); -}; +})(); +/*jshint browser:true, smarttabs:true*/ -var clearObjectLoadTimeout = function clearObjectLoadTimeout (callbackId) { - if (!callbackId) return; +// tb_require('../helpers.js') - if (objectTimeouts[callbackId]) { - clearTimeout(objectTimeouts[callbackId]); - delete objectTimeouts[callbackId]; +(function() { + + var requestAnimationFrame = window.requestAnimationFrame || + window.mozRequestAnimationFrame || + window.webkitRequestAnimationFrame || + window.msRequestAnimationFrame; + + if (requestAnimationFrame) { + requestAnimationFrame = OTHelpers.bind(requestAnimationFrame, window); } + else { + var lastTime = 0; + var startTime = OTHelpers.now(); - if (scope[callbackId]) { - try { - delete scope[callbackId]; - } catch (err) { - scope[callbackId] = void 0; - } + requestAnimationFrame = function(callback){ + var currTime = OTHelpers.now(); + var timeToCall = Math.max(0, 16 - (currTime - lastTime)); + var id = window.setTimeout(function() { callback(currTime - startTime); }, timeToCall); + lastTime = currTime + timeToCall; + return id; + }; } -}; -var removeObjectFromDom = function removeObjectFromDom (object) { - clearObjectLoadTimeout(object.getAttribute('tb_callbackId')); + OTHelpers.requestAnimationFrame = requestAnimationFrame; +})(); +/*jshint browser:true, smarttabs:true*/ - if (mediaCaptureObject && mediaCaptureObject.id === object.id) { - mediaCaptureObject = null; - } - else if (plugins.hasOwnProperty(object.id)) { - delete plugins[object.id]; - } +// tb_require('../helpers.js') - object.parentNode.removeChild(object); -}; +(function() { -// @todo bind destroy to unload, may need to coordinate with TB -// jshint -W098 -var removeAllObjects = function removeAllObjects () { - if (mediaCaptureObject) mediaCaptureObject.destroy(); + // Singleton interval + var logQueue = [], + queueRunning = false; - for (var id in plugins) { - if (plugins.hasOwnProperty(id)) { - plugins[id].destroy(); - } - } -}; + OTHelpers.Analytics = function(loggingUrl, debugFn) { -// Reference counted wrapper for a plugin object -var PluginObject = function PluginObject (plugin) { - var _plugin = plugin, - _liveObjects = []; + var endPoint = loggingUrl + '/logging/ClientEvent', + endPointQos = loggingUrl + '/logging/ClientQos', - this._ = _plugin; + reportedErrors = {}, - this.addRef = function(ref) { - _liveObjects.push(ref); - return this; - }; + send = function(data, isQos, callback) { + OTHelpers.post((isQos ? endPointQos : endPoint) + '?_=' + OTHelpers.uuid.v4(), { + body: data, + xdomainrequest: ($.env.name === 'IE' && $.env.version < 10), + headers: { + 'Content-Type': 'application/json' + } + }, callback); + }, - this.removeRef = function(ref) { - if (_liveObjects.length === 0) return; + throttledPost = function() { + // Throttle logs so that they only happen 1 at a time + if (!queueRunning && logQueue.length > 0) { + queueRunning = true; + var curr = logQueue[0]; - var index = _liveObjects.indexOf(ref); - if (index !== -1) { - _liveObjects.splice(index, 1); - } + // Remove the current item and send the next log + var processNextItem = function() { + logQueue.shift(); + queueRunning = false; + throttledPost(); + }; - if (_liveObjects.length === 0) { - this.destroy(); - } + if (curr) { + send(curr.data, curr.isQos, function(err) { + if (err) { + var debugMsg = 'Failed to send ClientEvent, moving on to the next item.'; + if (debugFn) { + debugFn(debugMsg); + } else { + console.log(debugMsg); + } + // There was an error, move onto the next item + } else { + curr.onComplete(); + } + setTimeout(processNextItem, 50); + }); + } + } + }, - return this; - }; + post = function(data, onComplete, isQos) { + logQueue.push({ + data: data, + onComplete: onComplete, + isQos: isQos + }); - this.isValid = function() { - return _plugin.valid; - }; + throttledPost(); + }, - // Event Handling Mechanisms + shouldThrottleError = function(code, type, partnerId) { + if (!partnerId) return false; - var eventHandlers = {}; + var errKey = [partnerId, type, code].join('_'), + //msgLimit = DynamicConfig.get('exceptionLogging', 'messageLimitPerPartner', partnerId); + msgLimit = 100; + if (msgLimit === null || msgLimit === undefined) return false; + return (reportedErrors[errKey] || 0) <= msgLimit; + }; - var onCustomEvent = OT.$.bind(curryCallAsync(function onCustomEvent() { - var args = Array.prototype.slice.call(arguments), - name = args.shift(); + // Log an error via ClientEvents. + // + // @param [String] code + // @param [String] type + // @param [String] message + // @param [Hash] details additional error details + // + // @param [Hash] options the options to log the client event with. + // @option options [String] action The name of the Event that we are logging. E.g. + // 'TokShowLoaded'. Required. + // @option options [String] variation Usually used for Split A/B testing, when you + // have multiple variations of the +_action+. + // @option options [String] payload The payload. Required. + // @option options [String] sessionId The active OpenTok session, if there is one + // @option options [String] connectionId The active OpenTok connectionId, if there is one + // @option options [String] partnerId + // @option options [String] guid ... + // @option options [String] streamId ... + // @option options [String] section ... + // @option options [String] clientVersion ... + // + // Reports will be throttled to X reports (see exceptionLogging.messageLimitPerPartner + // from the dynamic config for X) of each error type for each partner. Reports can be + // disabled/enabled globally or on a per partner basis (per partner settings + // take precedence) using exceptionLogging.enabled. + // + this.logError = function(code, type, message, details, options) { + if (!options) options = {}; + var partnerId = options.partnerId; - if (!eventHandlers.hasOwnProperty(name) && eventHandlers[name].length) { - return; - } + if (shouldThrottleError(code, type, partnerId)) { + //OT.log('ClientEvents.error has throttled an error of type ' + type + '.' + + // code + ' for partner ' + (partnerId || 'No Partner Id')); + return; + } - OT.$.forEach(eventHandlers[name], function(handler) { - handler[0].apply(handler[1], args); - }); - }), this); + var errKey = [partnerId, type, code].join('_'), + payload = details ? details : null; + reportedErrors[errKey] = typeof(reportedErrors[errKey]) !== 'undefined' ? + reportedErrors[errKey] + 1 : 1; + this.logEvent(OTHelpers.extend(options, { + action: type + '.' + code, + payload: payload + }), false); + }; - this.on = function (name, callback, context) { - if (!eventHandlers.hasOwnProperty(name)) { - eventHandlers[name] = []; - } + // Log a client event to the analytics backend. + // + // @example Logs a client event called 'foo' + // this.logEvent({ + // action: 'foo', + // payload: 'bar', + // sessionId: sessionId, + // connectionId: connectionId + // }, false) + // + // @param [Hash] data the data to log the client event with. + // @param [Boolean] qos Whether this is a QoS event. + // + this.logEvent = function(data, qos, throttle) { + if (!qos) qos = false; - eventHandlers[name].push([callback, context]); - return this; - }; + if (throttle && !isNaN(throttle)) { + if (Math.random() > throttle) { + return; + } + } - this.off = function (name, callback, context) { - if (!eventHandlers.hasOwnProperty(name) || - eventHandlers[name].length === 0) { - return; - } + // remove properties that have null values: + for (var key in data) { + if (data.hasOwnProperty(key) && data[key] === null) { + delete data[key]; + } + } - OT.$.filter(eventHandlers[name], function(listener) { - return listener[0] === callback && - listener[1] === context; - }); + // TODO: catch error when stringifying an object that has a circular reference + data = JSON.stringify(data); - return this; - }; + var onComplete = function() { + // OT.log('logged: ' + '{action: ' + data['action'] + ', variation: ' + data['variation'] + // + ', payload: ' + data['payload'] + '}'); + }; - this.once = function (name, callback, context) { - var fn = function () { - this.off(name, fn, this); - return callback.apply(context, arguments); + post(data, onComplete, qos); }; - this.on(name, fn, this); - return this; + // Log a client QOS to the analytics backend. + // Log a client QOS to the analytics backend. + // @option options [String] action The name of the Event that we are logging. + // E.g. 'TokShowLoaded'. Required. + // @option options [String] variation Usually used for Split A/B testing, when + // you have multiple variations of the +_action+. + // @option options [String] payload The payload. Required. + // @option options [String] sessionId The active OpenTok session, if there is one + // @option options [String] connectionId The active OpenTok connectionId, if there is one + // @option options [String] partnerId + // @option options [String] guid ... + // @option options [String] streamId ... + // @option options [String] section ... + // @option options [String] clientVersion ... + // + this.logQOS = function(options) { + this.logEvent(options, true); + }; }; +})(); - this.onReady = function(readyCallback) { - if (_plugin.on) { - // If the plugin supports custom events we'll use them - _plugin.on(-1, {customEvent: curryCallAsync(onCustomEvent, this)}); - } +// AJAX helpers - // Only the main plugin has an initialise method - if (_plugin.initialise) { - this.on('ready', OT.$.bind(curryCallAsync(readyCallback), this)); - _plugin.initialise(); - } - else { - readyCallback.call(null); - } - }; +/*jshint browser:true, smarttabs:true*/ - this.destroy = function() { - while (_liveObjects.length) { - _liveObjects.shift().destroy(); - } +// tb_require('../helpers.js') +// tb_require('./ajax/node.js') +// tb_require('./ajax/browser.js') - removeObjectFromDom(_plugin); - _plugin = null; - }; +OTHelpers.get = function(url, options, callback) { + var _options = OTHelpers.extend(options || {}, { + method: 'GET' + }); + OTHelpers.request(url, _options, callback); +}; - this.setStream = function(stream, completion) { - if (completion) { - if (stream.hasVideo()) { - // FIX ME renderingStarted currently doesn't first - // this.once('renderingStarted', completion); - var verifyStream = function() { - if (_plugin.videoWidth > 0) { - // This fires a little too soon. - setTimeout(completion, 200); - } - else { - setTimeout(verifyStream, 500); - } - }; - setTimeout(verifyStream, 500); - } - else { - // TODO Investigate whether there is a good way to detect - // when the audio is ready. Does it even matter? - completion(); - } - } - _plugin.setStream(stream); - }; +OTHelpers.post = function(url, options, callback) { + var _options = OTHelpers.extend(options || {}, { + method: 'POST' + }); + + if(_options.xdomainrequest) { + OTHelpers.xdomainRequest(url, _options, callback); + } else { + OTHelpers.request(url, _options, callback); + } }; -// Stops and cleans up after the plugin object load timeout. -var injectObject = function injectObject (mimeType, isVisible, params, completion) { - var callbackId = 'TBPlugin_loaded_' + generatePluginUuid(); - params.onload = callbackId; - params.userAgent = window.navigator.userAgent.toLowerCase(); - scope[callbackId] = function() { - clearObjectLoadTimeout(callbackId); +})(window, window.OTHelpers); - o.setAttribute('id', 'tb_plugin_' + o.uuid); - o.removeAttribute('tb_callbackId'); - pluginRefCounted.uuid = o.uuid; - pluginRefCounted.id = o.id; +/** + * @license TB Plugin 0.4.0.9 88af499 2014Q4-2.2 + * http://www.tokbox.com/ + * + * Copyright (c) 2015 TokBox, Inc. + * + * Date: January 08 08:54:38 2015 + * + */ + +/* jshint globalstrict: true, strict: false, undef: true, unused: false, + trailing: true, browser: true, smarttabs:true */ +/* global scope:true, OT:true, OTHelpers:true */ +/* exported OTPlugin */ - pluginRefCounted.onReady(function(err) { - if (err) { - OT.error('Error while starting up plugin ' + o.uuid + ': ' + err); - return; - } +/* jshint ignore:start */ +(function(scope) { +/* jshint ignore:end */ - debug('Plugin ' + o.id + ' is loaded'); +// If we've already be setup, bail +if (scope.OTPlugin !== void 0) return; - if (completion && OT.$.isFunction(completion)) { - completion.call(TBPlugin, null, pluginRefCounted); - } - }); - }; - var tmpContainer = document.createElement('div'), - objBits = [], - extraAttributes = ['width="0" height="0"'], - pluginRefCounted, - o; +// TB must exist first, otherwise we can't do anything +// if (scope.OT === void 0) return; - if (isVisible !== true) { - extraAttributes.push('visibility="hidden"'); - } +// Establish the environment that we're running in +// Note: we don't currently support 64bit IE +var isSupported = (OTHelpers.env.name === 'IE' && OTHelpers.env.version >= 8 && + OTHelpers.env.userAgent.indexOf('x64') === -1), + pluginIsReady = false; - objBits.push(''); - for (var name in params) { - if (params.hasOwnProperty(name)) { - objBits.push(''); - } +var OTPlugin = { + isSupported: function () { return isSupported; }, + isReady: function() { return pluginIsReady; }, + meta: { + mimeType: 'application/x-opentokie,version=0.4.0.9', + activeXName: 'TokBox.OpenTokIE.0.4.0.9', + version: '0.4.0.9' } +}; - objBits.push(''); - tmpContainer.innerHTML = objBits.join(''); - _document.body.appendChild(tmpContainer); - function firstElementChild(element) { - if(element.firstElementChild) { - return element.firstElementChild; - } - for(var i = 0, len = element.childNodes.length; i < len; ++i) { - if(element.childNodes[i].nodeType === 1) { - return element.childNodes[i]; - } - } - return null; - } +// Add logging methods +OTHelpers.useLogHelpers(OTPlugin); - o = firstElementChild(tmpContainer); - o.setAttribute('tb_callbackId', callbackId); +scope.OTPlugin = OTPlugin; - pluginRefCounted = new PluginObject(o); +// If this client isn't supported we still make sure that OTPlugin is defined +// and the basic API (isSupported() and isInstalled()) is created. +if (!OTPlugin.isSupported()) { + OTPlugin.isInstalled = function isInstalled () { return false; }; + return; +} - _document.body.appendChild(o); - _document.body.removeChild(tmpContainer); +// tb_require('./header.js') - objectTimeouts[callbackId] = setTimeout(function() { - clearObjectLoadTimeout(callbackId); +/* exported shim */ - completion.call(TBPlugin, 'The object with the mimeType of ' + - mimeType + ' timed out while loading.'); +// Shims for various missing things from JS +// Applied only after init is called to prevent unnecessary polution +var shim = function shim () { + if (!Function.prototype.bind) { + Function.prototype.bind = function (oThis) { + if (typeof this !== 'function') { + // closest thing possible to the ECMAScript 5 internal IsCallable function + throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); + } - _document.body.removeChild(o); - }, 3000); + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + FNOP = function () {}, + fBound = function () { + return fToBind.apply(this instanceof FNOP && oThis ? + this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); + }; - return pluginRefCounted; -}; + FNOP.prototype = this.prototype; + fBound.prototype = new FNOP(); + return fBound; + }; + } -// Creates the Media Capture controller. This exposes selectSources and is -// used in the private API. -// -// Only one Media Capture controller can exist at once, calling this method -// more than once will raise an exception. -// -var createMediaCaptureController = function createMediaCaptureController (completion) { - if (mediaCaptureObject) { - throw new Error('TBPlugin.createMediaCaptureController called multiple times!'); + if(!Array.isArray) { + Array.isArray = function (vArg) { + return Object.prototype.toString.call(vArg) === '[object Array]'; + }; } - mediaCaptureObject = injectObject(pluginInfo.mimeType, false, {windowless: false}, completion); + if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function (searchElement, fromIndex) { + var i, + pivot = (fromIndex) ? fromIndex : 0, + length; - mediaCaptureObject.selectSources = function() { - return this._.selectSources.apply(this._, arguments); - }; + if (!this) { + throw new TypeError(); + } - return mediaCaptureObject; -}; + length = this.length; -// Create an instance of the publisher/subscriber/peerconnection object. -// Many of these can exist at once, but the +id+ of each must be unique -// within a single instance of scope (window or window-like thing). -// -var createPeerController = function createPeerController (completion) { - var o = injectObject(pluginInfo.mimeType, true, {windowless: true}, function(err, plugin) { - if (err) { - completion.call(TBPlugin, err); - return; - } + if (length === 0 || pivot >= length) { + return -1; + } - plugins[plugin.id] = plugin; - completion.call(TBPlugin, null, plugin); - }); + if (pivot < 0) { + pivot = length - Math.abs(pivot); + } - return o; -}; + for (i = pivot; i < length; i++) { + if (this[i] === searchElement) { + return i; + } + } + return -1; + }; + } -// tb_require('./header.js') -// tb_require('./shims.js') -// tb_require('./plugin_object.js') + if (!Array.prototype.map) + { + Array.prototype.map = function(fun /*, thisArg */) + { + 'use strict'; -/* jshint globalstrict: true, strict: false, undef: true, unused: true, - trailing: true, browser: true, smarttabs:true */ -/* global OT:true, debug:true */ -/* exported VideoContainer */ + if (this === void 0 || this === null) + throw new TypeError(); -var VideoContainer = function VideoContainer (plugin, stream) { - this.domElement = plugin._; - this.parentElement = plugin._.parentNode; + var t = Object(this); + var len = t.length >>> 0; + if (typeof fun !== 'function') { + throw new TypeError(); + } - plugin.addRef(this); + var res = new Array(len); + var thisArg = arguments.length >= 2 ? arguments[1] : void 0; + for (var i = 0; i < len; i++) + { + // NOTE: Absolute correctness would demand Object.defineProperty + // be used. But this method is fairly new, and failure is + // possible only if Object.prototype or Array.prototype + // has a property |i| (very unlikely), so use a less-correct + // but more portable alternative. + if (i in t) + res[i] = fun.call(thisArg, t[i], i, t); + } - this.appendTo = function (parentDomElement) { - if (parentDomElement && plugin._.parentNode !== parentDomElement) { - debug('VideoContainer appendTo', parentDomElement); - parentDomElement.appendChild(plugin._); - this.parentElement = parentDomElement; - } - }; + return res; + }; + } +}; +// tb_require('./header.js') +// tb_require('./shims.js') - this.show = function (completion) { - debug('VideoContainer show'); - plugin._.removeAttribute('width'); - plugin._.removeAttribute('height'); - plugin.setStream(stream, completion); - OT.$.show(plugin._); - }; +/* exported RumorSocket */ - this.setWidth = function (width) { - debug('VideoContainer setWidth to ' + width); - plugin._.setAttribute('width', width); - }; +var RumorSocket = function(plugin, server) { + var connected = false, + rumorID; - this.setHeight = function (height) { - debug('VideoContainer setHeight to ' + height); - plugin._.setAttribute('height', height); - }; + var _onOpen, + _onClose; - this.setVolume = function (value) { - // TODO - debug('VideoContainer setVolume not implemented: called with ' + value); - }; - this.getVolume = function () { - // TODO - debug('VideoContainer getVolume not implemented'); - return 0.5; - }; + try { + rumorID = plugin._.RumorInit(server, ''); + } + catch(e) { + OTPlugin.error('Error creating the Rumor Socket: ', e.message); + } - this.getImgData = function () { - return plugin._.getImgData('image/png'); - }; + if(!rumorID) { + throw new Error('Could not initialise OTPlugin rumor connection'); + } - this.getVideoWidth = function () { - return plugin._.videoWidth; - }; + plugin._.SetOnRumorOpen(rumorID, function() { + if (_onOpen && OTHelpers.isFunction(_onOpen)) { + _onOpen.call(null); + } + }); - this.getVideoHeight = function () { - return plugin._.videoHeight; - }; + plugin._.SetOnRumorClose(rumorID, function(code) { + _onClose(code); - this.destroy = function () { - plugin._.setStream(null); + // We're done. Clean up ourselves plugin.removeRef(this); - }; -}; - -// tb_require('./header.js') -// tb_require('./shims.js') -// tb_require('./plugin_object.js') + }); -/* jshint globalstrict: true, strict: false, undef: true, unused: true, - trailing: true, browser: true, smarttabs:true */ -/* global OT:true, TBPlugin:true, pluginInfo:true, ActiveXObject:true, - injectObject:true, curryCallAsync:true */ + var api = { + open: function() { + connected = true; + plugin._.RumorOpen(rumorID); + }, -/* exported AutoUpdater:true */ -var AutoUpdater; + close: function(code, reason) { + if (connected) { + connected = false; + plugin._.RumorClose(rumorID, code, reason); + } + }, -(function() { + destroy: function() { + this.close(); + }, - var autoUpdaterController, - updaterMimeType, // <- cached version, use getInstallerMimeType instead - installedVersion = -1; // <- cached version, use getInstallerMimeType instead - - - var versionGreaterThan = function versionGreaterThan (version1,version2) { - if (version1 === version2) return false; - - var v1 = version1.split('.'), - v2 = version2.split('.'); + send: function(msg) { + plugin._.RumorSend(rumorID, msg.type, msg.toAddress, + JSON.parse(JSON.stringify(msg.headers)), msg.data); + }, - v1 = parseFloat(parseInt(v1.shift(), 10) + '.' + - v1.map(function(vcomp) { return parseInt(vcomp, 10); }).join('')); + onOpen: function(callback) { + _onOpen = callback; + }, - v2 = parseFloat(parseInt(v2.shift(), 10) + '.' + - v2.map(function(vcomp) { return parseInt(vcomp, 10); }).join('')); + onClose: function(callback) { + _onClose = callback; + }, + onError: function(callback) { + plugin._.SetOnRumorError(rumorID, callback); + }, - return v1 > v2; + onMessage: function(callback) { + plugin._.SetOnRumorMessage(rumorID, callback); + } }; + plugin.addRef(api); + return api; +}; - // Work out the full mimeType (including the currently installed version) - // of the installer. - var findMimeTypeAndVersion = function findMimeTypeAndVersion () { - - if (updaterMimeType !== void 0) { - return updaterMimeType; - } - - var activeXControlId = 'TokBox.otiePluginInstaller', - unversionedMimeType = 'application/x-otieplugininstaller', - plugin = navigator.plugins[activeXControlId]; +// tb_require('./header.js') +// tb_require('./shims.js') - installedVersion = -1; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT:true, scope:true, injectObject:true */ +/* exported createMediaCaptureController:true, createPeerController:true, + injectObject:true, plugins:true, mediaCaptureObject:true, + removeAllObjects:true, curryCallAsync:true */ +var objectTimeouts = {}, + mediaCaptureObject, + plugins = {}; - if (plugin) { - // Look through the supported mime-types for the version - // There should only be one mime-type in our use case, and - // if there's more than one they should all have the same - // version. - var numMimeTypes = plugin.length, - extractVersion = new RegExp(unversionedMimeType.replace('-', '\\-') + - ',version=([0-9]+)', 'i'), - mimeType, - bits; +var curryCallAsync = function curryCallAsync (fn) { + return function() { + var args = Array.prototype.slice.call(arguments); + args.unshift(fn); + OTHelpers.callAsync.apply(OTHelpers, args); + }; +}; - for (var i=0; i 0) { + // This fires a little too soon. + setTimeout(completion, 200); + } + else { + setTimeout(verifyStream, 500); + } + }; + + setTimeout(verifyStream, 500); + } + else { + // TODO Investigate whether there is a good way to detect + // when the audio is ready. Does it even matter? + completion(); + } + } + _plugin.setStream(stream); + }; +}; + +// tb_require('./header.js') +// tb_require('./shims.js') +// tb_require('./proxy.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* exported VideoContainer */ + +var VideoContainer = function VideoContainer (plugin, stream) { + this.domElement = plugin._; + this.parentElement = plugin._.parentNode; + + plugin.addRef(this); + + this.appendTo = function (parentDomElement) { + if (parentDomElement && plugin._.parentNode !== parentDomElement) { + OTPlugin.debug('VideoContainer appendTo', parentDomElement); + parentDomElement.appendChild(plugin._); + this.parentElement = parentDomElement; + } + }; + + this.show = function (completion) { + OTPlugin.debug('VideoContainer show'); + plugin._.removeAttribute('width'); + plugin._.removeAttribute('height'); + plugin.setStream(stream, completion); + OTHelpers.show(plugin._); + }; + + this.setWidth = function (width) { + OTPlugin.debug('VideoContainer setWidth to ' + width); + plugin._.setAttribute('width', width); + }; + + this.setHeight = function (height) { + OTPlugin.debug('VideoContainer setHeight to ' + height); + plugin._.setAttribute('height', height); + }; + + this.setVolume = function (value) { + // TODO + OTPlugin.debug('VideoContainer setVolume not implemented: called with ' + value); + }; + + this.getVolume = function () { + // TODO + OTPlugin.debug('VideoContainer getVolume not implemented'); + return 0.5; + }; + + this.getImgData = function () { + return plugin._.getImgData('image/png'); + }; + + this.getVideoWidth = function () { + return plugin._.videoWidth; + }; + + this.getVideoHeight = function () { + return plugin._.videoHeight; + }; + + this.destroy = function () { + plugin._.setStream(null); + plugin.removeRef(this); + }; +}; + +// tb_require('./header.js') +// tb_require('./shims.js') +// tb_require('./proxy.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* exported RTCStatsReport */ + +var RTCStatsReport = function (reports) { + this.forEach = function (callback, context) { + for (var id in reports) { + callback.call(context, reports[id]); + } + }; +}; + +// tb_require('./header.js') +// tb_require('./shims.js') +// tb_require('./proxy.js') +// tb_require('./video_container.js') /* jshint globalstrict: true, strict: false, undef: true, unused: true, trailing: true, browser: true, smarttabs:true */ -/* global OT:true, VideoContainer:true */ +/* global VideoContainer:true */ /* exported MediaStream */ var MediaStreamTrack = function MediaStreamTrack (mediaStreamId, options, plugin) { this.id = options.id; this.kind = options.kind; this.label = options.label; - this.enabled = OT.$.castToBoolean(options.enabled); + this.enabled = OTHelpers.castToBoolean(options.enabled); this.streamId = mediaStreamId; this.setEnabled = function (enabled) { - this.enabled = OT.$.castToBoolean(enabled); + this.enabled = OTHelpers.castToBoolean(enabled); if (this.enabled) { plugin._.enableMediaStreamTrack(mediaStreamId, this.id); @@ -4631,7 +4729,7 @@ var hasTracksOfType = function (type) { var tracks = type === 'video' ? videoTracks : audioTracks; - return OT.$.some(tracks, function(track) { + return OTHelpers.some(tracks, function(track) { return track.enabled; }); }; @@ -4677,7 +4775,7 @@ plugin: plugin, // Get a VideoContainer to render the stream in. - render: OT.$.bind(function() { + render: OTHelpers.bind(function() { return new VideoContainer(plugin, this); }, this) }; @@ -4689,210 +4787,174 @@ return new MediaStream( JSON.parse(json), plugin ); }; -// tb_require('./header.js') + // tb_require('./header.js') // tb_require('./shims.js') +// tb_require('./proxy.js') +// tb_require('./stats.js') -/* global OT:true */ -/* exported PluginRumorSocket */ - -var PluginRumorSocket = function(plugin, server) { - var connected = false, - rumorID; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global MediaStream:true, RTCStatsReport:true */ +/* exported PeerConnection */ - try { - rumorID = plugin._.RumorInit(server, ''); - } - catch(e) { - OT.error('Error creating the Rumor Socket: ', e.message); - } +// Our RTCPeerConnection shim, it should look like a normal PeerConection +// from the outside, but it actually delegates to our plugin. +// +var PeerConnection = function PeerConnection (iceServers, options, plugin, ready) { + var id = OTHelpers.uuid(), + hasLocalDescription = false, + hasRemoteDescription = false, + candidates = [], + inited = false, + deferMethods = [], + events, + _this = this; - if(!rumorID) { - throw new Error('Could not initialise plugin rumor connection'); - } + plugin.addRef(this); - var socket = { - open: function() { - connected = true; - plugin._.RumorOpen(rumorID); - }, + events = { + addstream: [], + removestream: [], + icecandidate: [], + signalingstatechange: [], + iceconnectionstatechange: [] + }; - close: function(code, reason) { - if (!connected) return; - connected = false; + var onAddIceCandidate = function onAddIceCandidate () {/* success */}, - plugin._.RumorClose(rumorID, code, reason); - plugin.removeRef(this); - }, + onAddIceCandidateFailed = function onAddIceCandidateFailed (err) { + OTPlugin.error('Failed to process candidate'); + OTPlugin.error(err); + }, - destroy: function() { - this.close(); - }, + processPendingCandidates = function processPendingCandidates () { + for (var i=0; i ', object); - } - else { - scope.OT.info('TB Plugin - ' + message); - } -}; +var MediaConstraints = function(userConstraints) { + var constraints = OTHelpers.clone(userConstraints); + this.hasVideo = constraints.video !== void 0 && constraints.video !== false; + this.hasAudio = constraints.audio !== void 0 && constraints.audio !== false; -/// Private API + if (constraints.video === true) constraints.video = {}; + if (constraints.audio === true) constraints.audio = {}; -var isDomReady = function isDomReady () { - return (_document.readyState === 'complete' || - (_document.readyState === 'interactive' && _document.body)); - }, + if (this.hasVideo && !constraints.video.mandatory) { + constraints.video.mandatory = {}; + } - onDomReady = function onDomReady () { - var callCompletionHandlers = function(err) { - var callback; + if (this.hasAudio && !constraints.audio.mandatory) { + constraints.audio.mandatory = {}; + } - while ( (callback = readyCallbacks.pop()) && OT.$.isFunction(callback) ) { - callback.call(TBPlugin, err); - } - }; + this.screenSharing = this.hasVideo && + ( constraints.video.mandatory.chromeMediaSource === 'screen' || + constraints.video.mandatory.chromeMediaSource === 'window' ); - AutoUpdater.get(function(err, updater) { - if (err) { - OT.error('Error while loading the AutoUpdater: ' + err); - callCompletionHandlers('Error while loading the AutoUpdater: ' + err); - return; - } + this.audio = constraints.audio; + this.video = constraints.video; - // If the plugin is out of date then we kick off the - // auto update process and then bail out. - if (updater.isOutOfDate()) { - updater.autoUpdate(); - return; - } + this.setVideoSource = function(sourceId) { + if (sourceId !== void 0) constraints.video.mandatory.sourceId = sourceId; + else delete constraints.video; + }; - // Inject the controller object into the page, wait for it to load or timeout... - createMediaCaptureController(function(err) { - if (!err && (mediaCaptureObject && !mediaCaptureObject.isValid())) { - err = 'The TB Plugin failed to load properly'; - } + this.setAudioSource = function(sourceId) { + if (sourceId !== void 0) constraints.audio.mandatory.sourceId = sourceId; + else delete constraints.audio; + }; - pluginReady = true; - callCompletionHandlers(err); + this.toHash = function() { + return constraints; + }; +}; - OT.onUnload(destroy); - }); - }); - }, +// tb_require('./header.js') +// tb_require('./shims.js') +// tb_require('./proxy.js') - waitForDomReady = function waitForDomReady () { - if (isDomReady()) { - onDomReady(); - } - else if (_document.addEventListener) { - _document.addEventListener('DOMContentLoaded', onDomReady, false); - } else if (_document.attachEvent) { - _document.attachEvent('onreadystatechange', function() { - if (_document.readyState === 'complete') onDomReady(); - }); - } - }, +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global PluginProxy:true, scope:true */ +/* exported injectObject, clearObjectLoadTimeout */ - // @todo bind destroy to unload, may need to coordinate with TB - // jshint -W098 - destroy = function destroy () { - removeAllObjects(); - }; +var objectTimeouts = {}; +var lastElementChild = function lastElementChild(parent) { + var node = parent.lastChild; -/// Public API + while(node && node.nodeType !== 1) { + node = node.previousSibling; + } -TBPlugin.isInstalled = function isInstalled () { - if (!this.isSupported()) return false; - return AutoUpdater.isinstalled(); + return node; }; -TBPlugin.version = function version () { - return pluginInfo.version; +var generateCallbackUUID = function generateCallbackUUID () { + return 'OTPlugin_loaded_' + OTHelpers.uuid().replace(/\-+/g, ''); }; -TBPlugin.installedVersion = function installedVersion () { - return AutoUpdater.installedVersion(); -}; -// Returns a URI to the TBPlugin installer that is paired with -// this version of TBPlugin.js. -TBPlugin.pathToInstaller = function pathToInstaller () { - return 'https://s3.amazonaws.com/otplugin.tokbox.com/v' + - pluginInfo.version + '/otiePluginMain.msi'; -}; +var clearObjectLoadTimeout = function clearObjectLoadTimeout (callbackId) { + if (!callbackId) return; -// Trigger +callback+ when the plugin is ready -// -// Most of the public API cannot be called until -// the plugin is ready. -// -TBPlugin.ready = function ready (callback) { - if (TBPlugin.isReady()) { - var err; + if (objectTimeouts[callbackId]) { + clearTimeout(objectTimeouts[callbackId]); + delete objectTimeouts[callbackId]; + } - if (!mediaCaptureObject || !mediaCaptureObject.isValid()) { - err = 'The TB Plugin failed to load properly'; + if (scope[callbackId]) { + try { + delete scope[callbackId]; + } catch (err) { + scope[callbackId] = void 0; } - - callback.call(TBPlugin, err); - } - else { - readyCallbacks.push(callback); } }; -// Helper function for TBPlugin.getUserMedia -var _getUserMedia = function _getUserMedia(mediaConstraints, success, error) { - createPeerController(function(err, plugin) { - if (err) { - error.call(TBPlugin, err); - return; - } +var createPluginProxy = function createPluginProxy (callbackId, mimeType, params, isVisible) { + var objBits = [], + extraAttributes = ['width="0" height="0"'], + plugin; - plugin._.getUserMedia(mediaConstraints.toHash(), function(streamJson) { - success.call(TBPlugin, MediaStream.fromJson(streamJson, plugin)); - }, error); - }); -}; + if (isVisible !== true) { + extraAttributes.push('visibility="hidden"'); + } -// Equivalent to: window.getUserMedia(constraints, success, error); -// -// Except that the constraints won't be identical -TBPlugin.getUserMedia = function getUserMedia (userConstraints, success, error) { - var constraints = new MediaConstraints(userConstraints); + objBits.push(''); - if (constraints.screenSharing) { - _getUserMedia(constraints, success, error); + for (var name in params) { + if (params.hasOwnProperty(name)) { + objBits.push(''); + } } - else { - var sources = []; - if (constraints.hasVideo) sources.push('video'); - if (constraints.hasAudio) sources.push('audio'); - mediaCaptureObject.selectSources(sources, function(captureDevices) { - for (var key in captureDevices) { - if (captureDevices.hasOwnProperty(key)) { - OT.debug(key + ' Capture Device: ' + captureDevices[key]); - } - } + objBits.push(''); - // Use the sources to acquire the hardware and start rendering - constraints.setVideoSource(captureDevices.video); - constraints.setAudioSource(captureDevices.audio); + scope.document.body.insertAdjacentHTML('beforeend', objBits.join('')); + plugin = new PluginProxy(lastElementChild(scope.document.body)); + plugin._.setAttribute('tbCallbackId', callbackId); - _getUserMedia(constraints, success, error); - }, error); - } + return plugin; }; -TBPlugin.initRumorSocket = function(messagingURL, completion) { - TBPlugin.ready(function(error) { - if(error) { - completion(error); - } else { - completion(null, new PluginRumorSocket(mediaCaptureObject, messagingURL)); - } - }); -}; +// Stops and cleans up after the plugin object load timeout. +var injectObject = function injectObject (mimeType, isVisible, params, completion) { + var callbackId = generateCallbackUUID(), + plugin; -// Equivalent to: var pc = new window.RTCPeerConnection(iceServers, options); -// -// Except that it is async and takes a completion handler -TBPlugin.initPeerConnection = function initPeerConnection (iceServers, - options, - localStream, - completion) { + params.onload = callbackId; + params.userAgent = OTHelpers.env.userAgent.toLowerCase(); - var gotPeerObject = function(err, plugin) { - if (err) { - completion.call(TBPlugin, err); - return; + scope[callbackId] = function() { + clearObjectLoadTimeout(callbackId); + + plugin._.setAttribute('id', 'tb_plugin_' + plugin._.uuid); + + if (plugin._.removeAttribute !== void 0) { + plugin._.removeAttribute('tbCallbackId'); + } + else { + // Plugin is some kind of weird object that doesn't have removeAttribute on Safari? + plugin._.tbCallbackId = null; } - debug('Got PeerConnection for ' + plugin.id); - var peerConnection = new PeerConnection(iceServers, options, plugin); + plugin.uuid = plugin._.uuid; + plugin.id = plugin._.id; + + plugin.onReady(function(err) { + if (err) { + OTPlugin.error('Error while starting up plugin ' + plugin.uuid + ': ' + err); + return; + } + + OTPlugin.debug('Plugin ' + plugin.id + ' is loaded'); - completion.call(TBPlugin, null, peerConnection); + if (completion && OTHelpers.isFunction(completion)) { + completion.call(OTPlugin, null, plugin); + } + }); }; - // @fixme this is nasty and brittle. We need some way to use the same Object - // for the PeerConnection that was used for the getUserMedia call (in the case - // of publishers). We don't really have a way of implicitly associating them though. - // Hence, publishers will have to pass through their localStream (if they have one) - // and we will look up the original Object and use that. Otherwise we generate - // a new one. - if (localStream && localStream._.plugin) { - gotPeerObject(null, localStream._.plugin); - } - else { - createPeerController(gotPeerObject); - } -}; + plugin = createPluginProxy(callbackId, mimeType, params, isVisible); -// A RTCSessionDescription like object exposed for native WebRTC compatability -TBPlugin.RTCSessionDescription = function RTCSessionDescription (options) { - this.type = options.type; - this.sdp = options.sdp; -}; + objectTimeouts[callbackId] = setTimeout(function() { + clearObjectLoadTimeout(callbackId); -// A RTCIceCandidate like object exposed for native WebRTC compatability -TBPlugin.RTCIceCandidate = function RTCIceCandidate (options) { - this.sdpMid = options.sdpMid; - this.sdpMLineIndex = parseInt(options.sdpMLineIndex, 10); - this.candidate = options.candidate; + completion.call(OTPlugin, 'The object with the mimeType of ' + + mimeType + ' timed out while loading.'); + + scope.document.body.removeChild(plugin._); + }, 3000); + + return plugin; }; -// Make this available for now -TBPlugin.debug = debug; +// tb_require('./header.js') +// tb_require('./shims.js') +// tb_require('./proxy.js') +// tb_require('./embedding.js') -shim(); +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global injectObject, scope:true */ +/* exported createMediaCaptureController:true, createPeerController:true, + injectObject:true, plugins:true, mediaCaptureObject:true, + removeAllObjects:true */ -waitForDomReady(); +var objectTimeouts = {}, + mediaCaptureObject, + plugins = {}; -// tb_require('./tb_plugin.js') -/* jshint ignore:start */ -})(this); -/* jshint ignore:end */ -!(function() { -/*global OT:true */ +// @todo bind destroy to unload, may need to coordinate with TB +// jshint -W098 +var removeAllObjects = function removeAllObjects () { + if (mediaCaptureObject) mediaCaptureObject.destroy(); - var defaultAspectRatio = 4.0/3.0, - miniWidth = 128, - miniHeight = 128, - microWidth = 64, - microHeight = 64; + for (var id in plugins) { + if (plugins.hasOwnProperty(id)) { + plugins[id].destroy(); + } + } +}; - // This code positions the video element so that we don't get any letterboxing. - // It will take into consideration aspect ratios other than 4/3 but only when - // the video element is first created. If the aspect ratio changes at a later point - // this calculation will become incorrect. - function fixAspectRatio(element, width, height, desiredAspectRatio, rotated) { - - if (TBPlugin.isInstalled()) { - // The plugin will sort out it's own aspect ratio, so we - // only need to tell the container to expand to fit it's parent. - - OT.$.css(element, { - width: '100%', - height: '100%', - left: 0, - top: 0 - }); +// Creates the Media Capture controller. This exposes selectSources and is +// used in the private API. +// +// Only one Media Capture controller can exist at once, calling this method +// more than once will raise an exception. +// +var createMediaCaptureController = function createMediaCaptureController (completion) { + if (mediaCaptureObject) { + throw new Error('OTPlugin.createMediaCaptureController called multiple times!'); + } + + mediaCaptureObject = injectObject(OTPlugin.meta.mimeType, false, {windowless: false}, completion); + + mediaCaptureObject.selectSources = function() { + return this._.selectSources.apply(this._, arguments); + }; + + return mediaCaptureObject; +}; +// Create an instance of the publisher/subscriber/peerconnection object. +// Many of these can exist at once, but the +id+ of each must be unique +// within a single instance of scope (window or window-like thing). +// +var createPeerController = function createPeerController (completion) { + var o = injectObject(OTPlugin.meta.mimeType, true, {windowless: true}, function(err, plugin) { + if (err) { + completion.call(OTPlugin, err); return; } - if (!width) width = parseInt(OT.$.width(element.parentNode), 10); - else width = parseInt(width, 10); + plugins[plugin.id] = plugin; + completion.call(OTPlugin, null, plugin); + }); - if (!height) height = parseInt(OT.$.height(element.parentNode), 10); - else height = parseInt(height, 10); + return o; +}; - if (width === 0 || height === 0) return; +// tb_require('./header.js') +// tb_require('./shims.js') +// tb_require('./proxy.js') - if (!desiredAspectRatio) desiredAspectRatio = defaultAspectRatio; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT:true, OTPlugin:true, ActiveXObject:true, + injectObject:true, curryCallAsync:true */ - var actualRatio = (width + 0.0)/height, - props; +/* exported AutoUpdater:true */ +var AutoUpdater; - props = { - width: '100%', - height: '100%', - left: 0, - top: 0 - }; +(function() { - if (actualRatio > desiredAspectRatio) { - // Width is largest so we blow up the height so we don't have letterboxing - var newHeight = (actualRatio / desiredAspectRatio) * 100; + var autoUpdaterController, + updaterMimeType, // <- cached version, use getInstallerMimeType instead + installedVersion = -1; // <- cached version, use getInstallerMimeType instead - props.height = newHeight + '%'; - props.top = '-' + ((newHeight - 100) / 2) + '%'; - } else if (actualRatio < desiredAspectRatio) { - // Height is largest, blow up the width - var newWidth = (desiredAspectRatio / actualRatio) * 100; + var versionGreaterThan = function versionGreaterThan (version1, version2) { + if (version1 === version2) return false; + if (version1 === -1) return version2; + if (version2 === -1) return version1; - props.width = newWidth + '%'; - props.left = '-' + ((newWidth - 100) / 2) + '%'; + if (version1.indexOf('.') === -1 && version2.indexOf('.') === -1) { + return version1 > version2; } - OT.$.css(element, props); + // The versions have multiple components (i.e. 0.10.30) and + // must be compared piecewise. + // Note: I'm ignoring the case where one version has multiple + // components and the other doesn't. + var v1 = version1.split('.'), + v2 = version2.split('.'), + versionLength = (v1.length > v2.length ? v2 : v1).length; - var video = element.querySelector('video'); - if(video) { - if(rotated) { - var w = element.offsetWidth, - h = element.offsetHeight, - diff = w - h; - props = { - width: h + 'px', - height: w + 'px', - marginTop: -(diff / 2) + 'px', - marginLeft: (diff / 2) + 'px' - }; - OT.$.css(video, props); - } else { - OT.$.css(video, { width: '', height: '', marginTop: '', marginLeft: ''}); + + for (var i = 0; i < versionLength; ++i) { + if (parseInt(v1[i], 10) > parseInt(v2[i], 10)) { + return true; } } - } - - function fixMini(container, width, height) { - var w = parseInt(width, 10), - h = parseInt(height, 10); - if(w < microWidth || h < microHeight) { - OT.$.addClass(container, 'OT_micro'); - } else { - OT.$.removeClass(container, 'OT_micro'); - } - if(w < miniWidth || h < miniHeight) { - OT.$.addClass(container, 'OT_mini'); - } else { - OT.$.removeClass(container, 'OT_mini'); + // Special case, v1 has extra components but the initial components + // were identical, we assume this means newer but it might also mean + // that someone changed versioning systems. + if (i < v1.length) { + return true; } - } - var getOrCreateContainer = function getOrCreateContainer(elementOrDomId, insertMode) { - var container, - domId; + return false; + }; - if (elementOrDomId && elementOrDomId.nodeName) { - // It looks like we were given a DOM element. Grab the id or generate - // one if it doesn't have one. - container = elementOrDomId; - if (!container.getAttribute('id') || container.getAttribute('id').length === 0) { - container.setAttribute('id', 'OT_' + OT.$.uuid()); - } - domId = container.getAttribute('id'); - } else { - // We may have got an id, try and get it's DOM element. - container = OT.$(elementOrDomId); - domId = elementOrDomId || ('OT_' + OT.$.uuid()); - } + // Work out the full mimeType (including the currently installed version) + // of the installer. + var findMimeTypeAndVersion = function findMimeTypeAndVersion () { - if (!container) { - container = OT.$.createElement('div', {id: domId}); - container.style.backgroundColor = '#000000'; - document.body.appendChild(container); - } else { - if(!(insertMode == null || insertMode === 'replace')) { - var placeholder = document.createElement('div'); - placeholder.id = ('OT_' + OT.$.uuid()); - if(insertMode === 'append') { - container.appendChild(placeholder); - container = placeholder; - } else if(insertMode === 'before') { - container.parentNode.insertBefore(placeholder, container); - container = placeholder; - } else if(insertMode === 'after') { - container.parentNode.insertBefore(placeholder, container.nextSibling); - container = placeholder; - } - } else { - OT.$.emptyElement(container); - } + if (updaterMimeType !== void 0) { + return updaterMimeType; } - return container; - }; + var activeXControlId = 'TokBox.otiePluginInstaller', + installPluginName = 'otiePluginInstaller', + unversionedMimeType = 'application/x-otieplugininstaller', + plugin = navigator.plugins[activeXControlId] || navigator.plugins[installPluginName]; - // Creates the standard container that the Subscriber and Publisher use to hold - // their video element and other chrome. - OT.WidgetView = function(targetElement, properties) { - var container = getOrCreateContainer(targetElement, properties && properties.insertMode), - videoContainer = document.createElement('div'), - oldContainerStyles = {}, - dimensionsObserver, - videoElement, - videoObserver, - posterContainer, - loadingContainer, - width, - height, - loading = true, - audioOnly = false; + installedVersion = -1; - if (properties) { - width = properties.width; - height = properties.height; + if (plugin) { + // Look through the supported mime-types for the version + // There should only be one mime-type in our use case, and + // if there's more than one they should all have the same + // version. + var numMimeTypes = plugin.length, + extractVersion = new RegExp(unversionedMimeType.replace('-', '\\-') + + ',version=([0-9a-zA-Z-_.]+)', 'i'), + mimeType, + bits; - if (width) { - if (typeof(width) === 'number') { - width = width + 'px'; - } - } - if (height) { - if (typeof(height) === 'number') { - height = height + 'px'; + for (var i=0; i' + helpMsg + '' : '') + - '

    '; - OT.$.addClass(container, classNames || 'OT_subscriber_error'); - if(container.querySelector('p').offsetHeight > container.offsetHeight) { - container.querySelector('span').style.display = 'none'; - } - }; - }; +OTPlugin.version = function version () { + return OTPlugin.meta.version; +}; -})(window); -// Web OT Helpers -!(function(window) { +OTPlugin.installedVersion = function installedVersion () { + return AutoUpdater.installedVersion(); +}; - var NativeRTCPeerConnection = (window.webkitRTCPeerConnection || - window.mozRTCPeerConnection); +// Returns a URI to the OTPlugin installer that is paired with +// this version of OTPlugin.js. +OTPlugin.pathToInstaller = function pathToInstaller () { + return 'https://s3.amazonaws.com/otplugin.tokbox.com/v' + + OTPlugin.meta.version + '/otiePluginMain.msi'; +}; - if (navigator.webkitGetUserMedia) { - /*global webkitMediaStream, webkitRTCPeerConnection*/ - // Stub for getVideoTracks for Chrome < 26 - if (!webkitMediaStream.prototype.getVideoTracks) { - webkitMediaStream.prototype.getVideoTracks = function() { - return this.videoTracks; - }; - } +// Trigger +callback+ when the plugin is ready +// +// Most of the public API cannot be called until +// the plugin is ready. +// +OTPlugin.ready = function ready (callback) { + if (OTPlugin.isReady()) { + var err; - // Stubs for getAudioTracks for Chrome < 26 - if (!webkitMediaStream.prototype.getAudioTracks) { - webkitMediaStream.prototype.getAudioTracks = function() { - return this.audioTracks; - }; + if (!mediaCaptureObject || !mediaCaptureObject.isValid()) { + err = 'The TB Plugin failed to load properly'; } - if (!webkitRTCPeerConnection.prototype.getLocalStreams) { - webkitRTCPeerConnection.prototype.getLocalStreams = function() { - return this.localStreams; - }; - } + callback.call(OTPlugin, err); + } + else { + registerReadyListener(callback); + } +}; - if (!webkitRTCPeerConnection.prototype.getRemoteStreams) { - webkitRTCPeerConnection.prototype.getRemoteStreams = function() { - return this.remoteStreams; - }; +// Helper function for OTPlugin.getUserMedia +var _getUserMedia = function _getUserMedia(mediaConstraints, success, error) { + createPeerController(function(err, plugin) { + if (err) { + error.call(OTPlugin, err); + return; } - } else if (navigator.mozGetUserMedia) { - // Firefox < 23 doesn't support get Video/Audio tracks, we'll just stub them out for now. - /* global MediaStream */ - if (!MediaStream.prototype.getVideoTracks) { - MediaStream.prototype.getVideoTracks = function() { - return []; - }; - } + plugin._.getUserMedia(mediaConstraints.toHash(), function(streamJson) { + success.call(OTPlugin, MediaStream.fromJson(streamJson, plugin)); + }, error); + }); +}; - if (!MediaStream.prototype.getAudioTracks) { - MediaStream.prototype.getAudioTracks = function() { - return []; - }; - } +// Equivalent to: window.getUserMedia(constraints, success, error); +// +// Except that the constraints won't be identical +OTPlugin.getUserMedia = function getUserMedia (userConstraints, success, error) { + var constraints = new MediaConstraints(userConstraints); - // This won't work as mozRTCPeerConnection is a weird internal Firefox - // object (a wrapped native object I think). - // if (!window.mozRTCPeerConnection.prototype.getLocalStreams) { - // window.mozRTCPeerConnection.prototype.getLocalStreams = function() { - // return this.localStreams; - // }; - // } - - // This won't work as mozRTCPeerConnection is a weird internal Firefox - // object (a wrapped native object I think). - // if (!window.mozRTCPeerConnection.prototype.getRemoteStreams) { - // window.mozRTCPeerConnection.prototype.getRemoteStreams = function() { - // return this.remoteStreams; - // }; - // } - } - - // The setEnabled method on MediaStreamTracks is a TBPlugin - // construct. In this particular instance it's easier to bring - // all the good browsers down to IE's level than bootstrap it up. - if (typeof window.MediaStreamTrack !== 'undefined') { - if (!window.MediaStreamTrack.prototype.setEnabled) { - window.MediaStreamTrack.prototype.setEnabled = function (enabled) { - this.enabled = OT.$.castToBoolean(enabled); - }; - } + if (constraints.screenSharing) { + _getUserMedia(constraints, success, error); + } + else { + var sources = []; + if (constraints.hasVideo) sources.push('video'); + if (constraints.hasAudio) sources.push('audio'); + + mediaCaptureObject.selectSources(sources, function(captureDevices) { + for (var key in captureDevices) { + if (captureDevices.hasOwnProperty(key)) { + OTPlugin.debug(key + ' Capture Device: ' + captureDevices[key]); + } + } + + // Use the sources to acquire the hardware and start rendering + constraints.setVideoSource(captureDevices.video); + constraints.setAudioSource(captureDevices.audio); + + _getUserMedia(constraints, success, error); + }, error); } +}; + +OTPlugin.initRumorSocket = function(messagingURL, completion) { + OTPlugin.ready(function(error) { + if(error) { + completion(error); + } else { + completion(null, new RumorSocket(mediaCaptureObject, messagingURL)); + } + }); +}; + +// Equivalent to: var pc = new window.RTCPeerConnection(iceServers, options); +// +// Except that it is async and takes a completion handler +OTPlugin.initPeerConnection = function initPeerConnection (iceServers, + options, + localStream, + completion) { - OT.$.createPeerConnection = function (config, options, publishersWebRtcStream, completion) { - if (TBPlugin.isInstalled()) { - TBPlugin.initPeerConnection(config, options, - publishersWebRtcStream, completion); + var gotPeerObject = function(err, plugin) { + if (err) { + completion.call(OTPlugin, err); + return; } - else { - var pc; - try { - pc = new NativeRTCPeerConnection(config, options); - } catch(e) { - completion(e.message); + OTPlugin.debug('Got PeerConnection for ' + plugin.id); + PeerConnection.create(iceServers, options, plugin, function(err, peerConnection) { + if (err) { + completion.call(OTPlugin, err); return; } - completion(null, pc); - } + completion.call(OTPlugin, null, peerConnection); + }); }; - // Returns a String representing the supported WebRTC crypto scheme. The possible - // values are SDES_SRTP, DTLS_SRTP, and NONE; - // - // Broadly: - // * Firefox only supports DTLS - // * Older versions of Chrome (<= 24) only support SDES - // * Newer versions of Chrome (>= 25) support DTLS and SDES - // - OT.$.supportedCryptoScheme = function() { - if (!OT.$.hasCapabilities('webrtc')) return 'NONE'; + // @fixme this is nasty and brittle. We need some way to use the same Object + // for the PeerConnection that was used for the getUserMedia call (in the case + // of publishers). We don't really have a way of implicitly associating them though. + // Hence, publishers will have to pass through their localStream (if they have one) + // and we will look up the original Object and use that. Otherwise we generate + // a new one. + if (localStream && localStream._.plugin) { + gotPeerObject(null, localStream._.plugin); + } + else { + createPeerController(gotPeerObject); + } +}; - var chromeVersion = window.navigator.userAgent.toLowerCase().match(/chrome\/([0-9\.]+)/i); - return chromeVersion && parseFloat(chromeVersion[1], 10) < 25 ? 'SDES_SRTP' : 'DTLS_SRTP'; - }; +// A RTCSessionDescription like object exposed for native WebRTC compatability +OTPlugin.RTCSessionDescription = function RTCSessionDescription (options) { + this.type = options.type; + this.sdp = options.sdp; +}; -})(window); -// Web OT Helpers -!(function(window) { +// A RTCIceCandidate like object exposed for native WebRTC compatability +OTPlugin.RTCIceCandidate = function RTCIceCandidate (options) { + this.sdpMid = options.sdpMid; + this.sdpMLineIndex = parseInt(options.sdpMLineIndex, 10); + this.candidate = options.candidate; +}; - /* jshint globalstrict: true, strict: false, undef: true, unused: true, - trailing: true, browser: true, smarttabs:true */ - /* global TBPlugin, OT */ +// tb_require('./api.js') - /// - // Capabilities - // - // Support functions to query browser/client Media capabilities. - // +/* global shim, OTHelpers, onDomReady */ +shim(); - // Indicates whether this client supports the getUserMedia - // API. - // - OT.$.registerCapability('getUserMedia', function() { - return !!(navigator.webkitGetUserMedia || navigator.mozGetUserMedia || TBPlugin.isInstalled()); - }); +OTHelpers.onDOMLoad(onDomReady); +/* jshint ignore:start */ +})(this); +/* jshint ignore:end */ - // TODO Remove all PeerConnection stuff, that belongs to the messaging layer not the Media layer. - // Indicates whether this client supports the PeerConnection - // API. - // - // Chrome Issues: - // * The explicit prototype.addStream check is because webkitRTCPeerConnection was - // partially implemented, but not functional, in Chrome 22. - // - // Firefox Issues: - // * No real support before Firefox 19 - // * Firefox 19 has issues with generating Offers. - // * Firefox 20 doesn't interoperate with Chrome. - // - OT.$.registerCapability('PeerConnection', function() { - var browser = OT.$.browserVersion(); - if (navigator.webkitGetUserMedia) { - return typeof(window.webkitRTCPeerConnection) === 'function' && - !!window.webkitRTCPeerConnection.prototype.addStream; - } else if (navigator.mozGetUserMedia) { - if (typeof(window.mozRTCPeerConnection) === 'function' && browser.version > 20.0) { - try { - new window.mozRTCPeerConnection(); - return true; - } catch (err) { - return false; - } - } - } else { - return TBPlugin.isInstalled(); - } - }); +/* jshint ignore:start */ +!(function(window, OT) { +/* jshint ignore:end */ +// tb_require('./header.js') - // Indicates whether this client supports WebRTC - // - // This is defined as: getUserMedia + PeerConnection + exceeds min browser version - // - OT.$.registerCapability('webrtc', function() { - var browser = OT.$.browserVersion(), - minimumVersions = OT.properties.minimumVersion || {}, - minimumVersion = minimumVersions[browser.browser.toLowerCase()]; - - if(minimumVersion && minimumVersion > browser.version) { - OT.debug('Support for', browser.browser, 'is disabled because we require', - minimumVersion, 'but this is', browser.version); - return false; - } +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +// This is not obvious, so to prevent end-user frustration we'll let them know +// explicitly rather than failing with a bunch of permission errors. We don't +// handle this using an OT Exception as it's really only a development thing. +if (location.protocol === 'file:') { + /*global alert*/ + alert('You cannot test a page using WebRTC through the file system due to browser ' + + 'permissions. You must run it over a web server.'); +} - return OT.$.hasCapabilities('getUserMedia', 'PeerConnection'); - }); +var OT = window.OT || {}; +// Define the APIKEY this is a global parameter which should not change +OT.APIKEY = (function(){ + // Script embed + var scriptSrc = (function(){ + var s = document.getElementsByTagName('script'); + s = s[s.length - 1]; + s = s.getAttribute('src') || s.src; + return s; + })(); - // TODO Remove all transport stuff, that belongs to the messaging layer not the Media layer. - // Indicates if the browser supports bundle - // - // Broadly: - // * Firefox doesn't support bundle - // * Chrome support bundle - // * OT Plugin supports bundle - // - OT.$.registerCapability('bundle', function() { - return OT.$.hasCapabilities('webrtc') && - (OT.$.browser() === 'Chrome' || TBPlugin.isInstalled()); - }); + var m = scriptSrc.match(/[\?\&]apikey=([^&]+)/i); + return m ? m[1] : ''; +})(); - // Indicates if the browser supports rtcp mux - // - // Broadly: - // * Older versions of Firefox (<= 25) don't support rtcp mux - // * Older versions of Firefox (>= 26) support rtcp mux (not tested yet) - // * Chrome support rtcp mux - // * OT Plugin supports rtcp mux - // - OT.$.registerCapability('RTCPMux', function() { - return OT.$.hasCapabilities('webrtc') && - (OT.$.browser() === 'Chrome' || TBPlugin.isInstalled()); - }); +if (!window.OT) window.OT = OT; +if (!window.TB) window.TB = OT; +// tb_require('../js/ot.js') +OT.properties = { + version: 'v2.4.0', // The current version (eg. v2.0.4) (This is replaced by gradle) + build: '54ae164', // The current build hash (This is replaced by gradle) + + // Whether or not to turn on debug logging by default + debug: 'false', + // The URL of the tokbox website + websiteURL: 'http://www.tokbox.com', + + // The URL of the CDN + cdnURL: 'http://static.opentok.com', + // The URL to use for logging + loggingURL: 'http://hlg.tokbox.com/prod', + + // The anvil API URL + apiURL: 'http://anvil.opentok.com', + + // What protocol to use when connecting to the rumor web socket + messagingProtocol: 'wss', + // What port to use when connection to the rumor web socket + messagingPort: 443, + + // If this environment supports SSL + supportSSL: 'true', + // The CDN to use if we're using SSL + cdnURLSSL: 'https://static.opentok.com', + // The URL to use for logging + loggingURLSSL: 'https://hlg.tokbox.com/prod', + + // The anvil API URL to use if we're using SSL + apiURLSSL: 'https://anvil.opentok.com', + + minimumVersion: { + firefox: parseFloat('29'), + chrome: parseFloat('34') + } +}; - // Indicates whether this browser supports the getMediaDevices (getSources) API. - // - OT.$.registerCapability('getMediaDevices', function() { - return OT.$.isFunction(window.MediaStreamTrack) && - OT.$.isFunction(window.MediaStreamTrack.getSources); - }); -})(window); -// Web OT Helpers -!(function() { +// tb_require('../ot.js') +// tb_require('../../conf/properties.js'); - var nativeGetUserMedia, - vendorToW3CErrors, - gumNamesToMessages, - mapVendorErrorName, - parseErrorEvent, - areInvalidConstraints; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - // Handy cross-browser getUserMedia shim. Inspired by some code from Adam Barth - nativeGetUserMedia = (function() { - if (navigator.getUserMedia) { - return OT.$.bind(navigator.getUserMedia, navigator); - } else if (navigator.mozGetUserMedia) { - return OT.$.bind(navigator.mozGetUserMedia, navigator); - } else if (navigator.webkitGetUserMedia) { - return OT.$.bind(navigator.webkitGetUserMedia, navigator); - } else if (TBPlugin.isInstalled()) { - return OT.$.bind(TBPlugin.getUserMedia, TBPlugin); - } - })(); - // Mozilla error strings and the equivalent W3C names. NOT_SUPPORTED_ERROR does not - // exist in the spec right now, so we'll include Mozilla's error description. - // Chrome TrackStartError is triggered when the camera is already used by another app (Windows) - vendorToW3CErrors = { - PERMISSION_DENIED: 'PermissionDeniedError', - NOT_SUPPORTED_ERROR: 'NotSupportedError', - MANDATORY_UNSATISFIED_ERROR: ' ConstraintNotSatisfiedError', - NO_DEVICES_FOUND: 'NoDevicesFoundError', - HARDWARE_UNAVAILABLE: 'HardwareUnavailableError', - TrackStartError: 'HardwareUnavailableError' - }; +// Mount OTHelpers on OT.$ +OT.$ = window.OTHelpers; - gumNamesToMessages = { - PermissionDeniedError: 'End-user denied permission to hardware devices', - PermissionDismissedError: 'End-user dismissed permission to hardware devices', - NotSupportedError: 'A constraint specified is not supported by the browser.', - ConstraintNotSatisfiedError: 'It\'s not possible to satisfy one or more constraints ' + - 'passed into the getUserMedia function', - OverconstrainedError: 'Due to changes in the environment, one or more mandatory ' + - 'constraints can no longer be satisfied.', - NoDevicesFoundError: 'No voice or video input devices are available on this machine.', - HardwareUnavailableError: 'The selected voice or video devices are unavailable. Verify ' + - 'that the chosen devices are not in use by another application.' - }; +// Allow events to be bound on OT +OT.$.eventing(OT); - // Map vendor error strings to names and messages if possible - mapVendorErrorName = function mapVendorErrorName(vendorErrorName, vendorErrors) { - var errorName, errorMessage; +// REMOVE THIS POST IE MERGE - if(vendorErrors.hasOwnProperty(vendorErrorName)) { - errorName = vendorErrors[vendorErrorName]; - } else { - // This doesn't map to a known error from the Media Capture spec, it's - // probably a custom vendor error message. - errorName = vendorErrorName; - } +OT.$.defineGetters = function(self, getters, enumerable) { + var propsDefinition = {}; - if(gumNamesToMessages.hasOwnProperty(errorName)) { - errorMessage = gumNamesToMessages[errorName]; - } else { - errorMessage = 'Unknown Error while getting user media'; + if (enumerable === void 0) enumerable = false; + + for (var key in getters) { + if(!getters.hasOwnProperty(key)) { + continue; } - return { - name: errorName, - message: errorMessage + propsDefinition[key] = { + get: getters[key], + enumerable: enumerable }; - }; + } - // Parse and normalise a getUserMedia error event from Chrome or Mozilla - // @ref http://dev.w3.org/2011/webrtc/editor/getusermedia.html#idl-def-NavigatorUserMediaError - parseErrorEvent = function parseErrorObject(event) { - var error; + Object.defineProperties(self, propsDefinition); +}; - if (OT.$.isObject(event) && event.name) { - error = mapVendorErrorName(event.name, vendorToW3CErrors); - error.constraintName = event.constraintName; - } else if (typeof event === 'string') { - error = mapVendorErrorName(event, vendorToW3CErrors); - } else { - error = { - message: 'Unknown Error type while getting media' - }; - } +// STOP REMOVING HERE - return error; - }; +// OT.$.Modal was OT.Modal before the great common-js-helpers move +OT.Modal = OT.$.Modal; - // Validates a Hash of getUserMedia constraints. Currently we only - // check to see if there is at least one non-false constraint. - areInvalidConstraints = function(constraints) { - if (!constraints || !OT.$.isObject(constraints)) return true; +// Add logging methods +OT.$.useLogHelpers(OT); - for (var key in constraints) { - if(!constraints.hasOwnProperty(key)) { - continue; - } - if (constraints[key]) return false; - } +var _debugHeaderLogged = false, + _setLogLevel = OT.setLogLevel; + +// On the first time log level is set to DEBUG (or higher) show version info. +OT.setLogLevel = function(level) { + // Set OT.$ to the same log level + OT.$.setLogLevel(level); + var retVal = _setLogLevel.call(OT, level); + if (OT.shouldLog(OT.DEBUG) && !_debugHeaderLogged) { + OT.debug('OpenTok JavaScript library ' + OT.properties.version); + OT.debug('Release notes: ' + OT.properties.websiteURL + + '/opentok/webrtc/docs/js/release-notes.html'); + OT.debug('Known issues: ' + OT.properties.websiteURL + + '/opentok/webrtc/docs/js/release-notes.html#knownIssues'); + _debugHeaderLogged = true; + } + OT.debug('OT.setLogLevel(' + retVal + ')'); + return retVal; +}; - return true; - }; +var debugTrue = OT.properties.debug === 'true' || OT.properties.debug === true; +OT.setLogLevel(debugTrue ? OT.DEBUG : OT.ERROR); - // A wrapper for the builtin navigator.getUserMedia. In addition to the usual - // getUserMedia behaviour, this helper method also accepts a accessDialogOpened - // and accessDialogClosed callback. - // - // @memberof OT.$ - // @private - // - // @param {Object} constraints - // A dictionary of constraints to pass to getUserMedia. See - // http://dev.w3.org/2011/webrtc/editor/getusermedia.html#idl-def-MediaStreamConstraints - // in the Media Capture and Streams spec for more info. - // - // @param {function} success - // Called when getUserMedia completes successfully. The callback will be passed a WebRTC - // Stream object. - // - // @param {function} failure - // Called when getUserMedia fails to access a user stream. It will be passed an object - // with a code property representing the error that occurred. - // - // @param {function} accessDialogOpened - // Called when the access allow/deny dialog is opened. - // - // @param {function} accessDialogClosed - // Called when the access allow/deny dialog is closed. - // - // @param {function} accessDenied - // Called when access is denied to the camera/mic. This will be either because - // the user has clicked deny or because a particular origin is permanently denied. - // - OT.$.getUserMedia = function(constraints, success, failure, accessDialogOpened, - accessDialogClosed, accessDenied, customGetUserMedia) { +// Patch the userAgent to ref OTPlugin, if it's installed. +if (OTPlugin && OTPlugin.isInstalled()) { + OT.$.env.userAgent += '; OTPlugin ' + OTPlugin.version(); +} - var getUserMedia = nativeGetUserMedia; +// @todo remove this +OT.$.userAgent = function() { return OT.$.env.userAgent; }; - if(OT.$.isFunction(customGetUserMedia)) { - getUserMedia = customGetUserMedia; - } - // All constraints are false, we don't allow this. This may be valid later - // depending on how/if we integrate data channels. - if (areInvalidConstraints(constraints)) { - OT.error('Couldn\'t get UserMedia: All constraints were false'); - // Using a ugly dummy-code for now. - failure.call(null, { - name: 'NO_VALID_CONSTRAINTS', - message: 'Video and Audio was disabled, you need to enabled at least one' - }); +/** + * Sets the API log level. + *

    + * Calling OT.setLogLevel() sets the log level for runtime log messages that + * are the OpenTok library generates. The default value for the log level is OT.ERROR. + *

    + *

    + * The OpenTok JavaScript library displays log messages in the debugger console (such as + * Firebug), if one exists. + *

    + *

    + * The following example logs the session ID to the console, by calling OT.log(). + * The code also logs an error message when it attempts to publish a stream before the Session + * object dispatches a sessionConnected event. + *

    + *
    +  * OT.setLogLevel(OT.LOG);
    +  * session = OT.initSession(sessionId);
    +  * OT.log(sessionId);
    +  * publisher = OT.initPublisher("publishContainer");
    +  * session.publish(publisher);
    +  * 
    + * + * @param {Number} logLevel The degree of logging desired by the developer: + * + *

    + *

      + *
    • + * OT.NONE — API logging is disabled. + *
    • + *
    • + * OT.ERROR — Logging of errors only. + *
    • + *
    • + * OT.WARN — Logging of warnings and errors. + *
    • + *
    • + * OT.INFO — Logging of other useful information, in addition to + * warnings and errors. + *
    • + *
    • + * OT.LOG — Logging of OT.log() messages, in addition + * to OpenTok info, warning, + * and error messages. + *
    • + *
    • + * OT.DEBUG — Fine-grained logging of all API actions, as well as + * OT.log() messages. + *
    • + *
    + *

    + * + * @name OT.setLogLevel + * @memberof OT + * @function + * @see OT.log() + */ - return; - } +/** + * Sends a string to the the debugger console (such as Firebug), if one exists. + * However, the function only logs to the console if you have set the log level + * to OT.LOG or OT.DEBUG, + * by calling OT.setLogLevel(OT.LOG) or OT.setLogLevel(OT.DEBUG). + * + * @param {String} message The string to log. + * + * @name OT.log + * @memberof OT + * @function + * @see OT.setLogLevel() + */ - var triggerOpenedTimer = null, - displayedPermissionDialog = false, +// tb_require('../../../helpers/helpers.js') - finaliseAccessDialog = function() { - if (triggerOpenedTimer) { - clearTimeout(triggerOpenedTimer); - } +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - if (displayedPermissionDialog && accessDialogClosed) accessDialogClosed(); - }, +// Rumor Messaging for JS +// +// https://tbwiki.tokbox.com/index.php/Rumor_:_Messaging_FrameWork +// +// @todo Rumor { +// Add error codes for all the error cases +// Add Dependability commands +// } + +OT.Rumor = { + MessageType: { + // This is used to subscribe to address/addresses. The address/addresses the + // client specifies here is registered on the server. Once any message is sent to + // that address/addresses, the client receives that message. + SUBSCRIBE: 0, + + // This is used to unsubscribe to address / addresses. Once the client unsubscribe + // to an address, it will stop getting messages sent to that address. + UNSUBSCRIBE: 1, + + // This is used to send messages to arbitrary address/ addresses. Messages can be + // anything and Rumor will not care about what is included. + MESSAGE: 2, + + // This will be the first message that the client sends to the server. It includes + // the uniqueId for that client connection and a disconnect_notify address that will + // be notified once the client disconnects. + CONNECT: 3, + + // This will be the message used by the server to notify an address that a + // client disconnected. + DISCONNECT: 4, + + //Enhancements to support Keepalives + PING: 7, + PONG: 8, + STATUS: 9 + } +}; - triggerOpened = function() { - triggerOpenedTimer = null; - displayedPermissionDialog = true; +// tb_require('../../../helpers/helpers.js') +// tb_require('./rumor.js') - if (accessDialogOpened) accessDialogOpened(); - }, +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT, OTPlugin */ - onStream = function(stream) { - finaliseAccessDialog(); - success.call(null, stream); - }, +!(function() { - onError = function(event) { - finaliseAccessDialog(); - var error = parseErrorEvent(event); + OT.Rumor.PluginSocket = function(messagingURL, events) { - // The error name 'PERMISSION_DENIED' is from an earlier version of the spec - if (error.name === 'PermissionDeniedError' || error.name === 'PermissionDismissedError') { - accessDenied.call(null, error); - } else { - failure.call(null, error); - } - }; + var webSocket, + state = 'initializing'; - try { - getUserMedia(constraints, onStream, onError); - } catch (e) { - OT.error('Couldn\'t get UserMedia: ' + e.toString()); - onError(); - return; - } + OTPlugin.initRumorSocket(messagingURL, OT.$.bind(function(err, rumorSocket) { + if(err) { + state = 'closed'; + events.onClose({ code: 4999 }); + } else if(state === 'initializing') { + webSocket = rumorSocket; - // The 'remember me' functionality of WebRTC only functions over HTTPS, if - // we aren't on HTTPS then we should definitely be displaying the access - // dialog. - // - // If we are on HTTPS, we'll wait 500ms to see if we get a stream - // immediately. If we do then the user had clicked 'remember me'. Otherwise - // we assume that the accessAllowed dialog is visible. - // - // @todo benchmark and see if 500ms is a reasonable number. It seems like - // we should know a lot quicker. - // - if (location.protocol.indexOf('https') === -1) { - // Execute after, this gives the client a chance to bind to the - // accessDialogOpened event. - triggerOpenedTimer = setTimeout(triggerOpened, 100); + webSocket.onOpen(function() { + state = 'open'; + events.onOpen(); + }); + webSocket.onClose(function(error) { + state = 'closed'; /* CLOSED */ + events.onClose({ code: error }); + }); + webSocket.onError(function(error) { + state = 'closed'; /* CLOSED */ + events.onError(error); + /* native websockets seem to do this, so should we */ + events.onClose({ code: error }); + }); - } else { - // wait a second and then trigger accessDialogOpened - triggerOpenedTimer = setTimeout(triggerOpened, 500); - } - }; + webSocket.onMessage(function(type, addresses, headers, payload) { + var msg = new OT.Rumor.Message(type, addresses, headers, payload); + events.onMessage(msg); + }); -})(); -// Web OT Helpers -!(function(window) { + webSocket.open(); + } else { + this.close(); + } + }, this)); - /* jshint globalstrict: true, strict: false, undef: true, unused: true, - trailing: true, browser: true, smarttabs:true */ - /* global OT */ + this.close = function() { + if(state === 'initializing' || state === 'closed') { + state = 'closed'; + return; + } - /// - // Device Helpers - // - // Support functions to enumerating and guerying device info - // + webSocket.close(1000, ''); + }; + + this.send = function(msg) { + if(state === 'open') { + webSocket.send(msg); + } + }; + + this.isClosed = function() { + return state === 'closed'; + }; - var chromeToW3CDeviceKinds = { - audio: 'audioInput', - video: 'videoInput' }; +}(this)); - OT.$.shouldAskForDevices = function(callback) { - var MST = window.MediaStreamTrack; +// tb_require('../../../helpers/helpers.js') - if(MST != null && OT.$.isFunction(MST.getSources)) { - window.MediaStreamTrack.getSources(function(sources) { - var hasAudio = sources.some(function(src) { - return src.kind === 'audio'; - }); - - var hasVideo = sources.some(function(src) { - return src.kind === 'video'; - }); +// https://code.google.com/p/stringencoding/ +// An implementation of http://encoding.spec.whatwg.org/#api +// Modified by TokBox to remove all encoding support except for utf-8 - callback.call(null, { video: hasVideo, audio: hasAudio }); - }); +/** + * @license Copyright 2014 Joshua Bell + * + * 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. + * + * Original source: https://github.com/inexorabletash/text-encoding + ***/ +/*jshint unused:false*/ - } else { - // This environment can't enumerate devices anyway, so we'll memorise this result. - OT.$.shouldAskForDevices = function(callback) { - setTimeout(OT.$.bind(callback, null, { video: true, audio: true })); - }; +(function(global) { + 'use strict'; - OT.$.shouldAskForDevices(callback); - } - }; + if(OT.$.env && OT.$.env.name === 'IE' && OT.$.env.version < 10) { + return; // IE 8 doesn't do websockets. No websockets, no encoding. + } - OT.$.getMediaDevices = function(callback) { - if(OT.$.hasCapabilities('getMediaDevices')) { - window.MediaStreamTrack.getSources(function(sources) { - var filteredSources = OT.$.filter(sources, function(source) { - return chromeToW3CDeviceKinds[source.kind] != null; - }); - callback(void 0, OT.$.map(filteredSources, function(source) { - return { - deviceId: source.id, - label: source.label, - kind: chromeToW3CDeviceKinds[source.kind] - }; - })); - }); - } else { - callback(new Error('This browser does not support getMediaDevices APIs')); - } - }; + if ( (global.TextEncoder !== void 0) && (global.TextDecoder !== void 0)) { + // defer to the native ones + return; + } -})(window); -(function(window) { + /* jshint ignore:start */ - var VideoOrientationTransforms = { - 0: 'rotate(0deg)', - 270: 'rotate(90deg)', - 90: 'rotate(-90deg)', - 180: 'rotate(180deg)' - }; + // + // Utilities + // - OT.VideoOrientation = { - ROTATED_NORMAL: 0, - ROTATED_LEFT: 270, - ROTATED_RIGHT: 90, - ROTATED_UPSIDE_DOWN: 180 - }; + /** + * @param {number} a The number to test. + * @param {number} min The minimum value in the range, inclusive. + * @param {number} max The maximum value in the range, inclusive. + * @return {boolean} True if a >= min and a <= max. + */ + function inRange(a, min, max) { + return min <= a && a <= max; + } - var DefaultAudioVolume = 50; + /** + * @param {number} n The numerator. + * @param {number} d The denominator. + * @return {number} The result of the integer division of n by d. + */ + function div(n, d) { + return Math.floor(n / d); + } - var DEGREE_TO_RADIANS = Math.PI * 2 / 360; // + // Implementation of Encoding specification + // http://dvcs.w3.org/hg/encoding/raw-file/tip/Overview.html // - // var _videoElement = new OT.VideoElement({ - // fallbackText: 'blah' - // }, errorHandler); - // - // _videoElement.bindToStream(webRtcStream, completion); // => VideoElement - // _videoElement.appendTo(DOMElement) // => VideoElement - // - // _videoElement.domElement // => DomNode - // - // _videoElement.imgData // => PNG Data string + // - // _videoElement.orientation = OT.VideoOrientation.ROTATED_LEFT; + // 3. Terminology // - // _videoElement.unbindStream(); - // _videoElement.destroy() // => Completely cleans up and - // removes the video element + // + // 4. Encodings // - OT.VideoElement = function(/* optional */ options/*, optional errorHandler*/) { - var _options = OT.$.defaults( options && !OT.$.isFunction(options) ? options : {}, { - fallbackText: 'Sorry, Web RTC is not available in your browser' - }), - - errorHandler = OT.$.isFunction(arguments[arguments.length-1]) ? - arguments[arguments.length-1] : void 0, - - orientationHandler = OT.$.bind(function(orientation) { - this.trigger('orientationChanged', orientation); - }, this), - - _videoElement = TBPlugin.isInstalled() ? - new PluginVideoElement(_options, errorHandler, orientationHandler) : - new NativeDOMVideoElement(_options, errorHandler, orientationHandler), - _streamBound = false, - _stream, - _preInitialisedVolue; - - OT.$.eventing(this); - - // Public Properties - OT.$.defineProperties(this, { - - domElement: { - get: function() { - return _videoElement.domElement(); - } - }, - videoWidth: { - get: function() { - return _videoElement['video' + (this.isRotated() ? 'Height' : 'Width')](); - } - }, - - videoHeight: { - get: function() { - return _videoElement['video' + (this.isRotated() ? 'Width' : 'Height')](); - } - }, + /** @const */ + var EOF_byte = -1; + /** @const */ + var EOF_code_point = -1; - aspectRatio: { - get: function() { - return (this.videoWidth() + 0.0) / this.videoHeight(); - } - }, + /** + * @constructor + * @param {Uint8Array} bytes Array of bytes that provide the stream. + */ + function ByteInputStream(bytes) { + /** @type {number} */ + var pos = 0; - isRotated: { - get: function() { - return _videoElement.isRotated(); - } - }, + /** @return {number} Get the next byte from the stream. */ + this.get = function() { + return (pos >= bytes.length) ? EOF_byte : Number(bytes[pos]); + }; - orientation: { - get: function() { - return _videoElement.orientation(); - }, - set: function(orientation) { - _videoElement.orientation(orientation); - } - }, + /** @param {number} n Number (positive or negative) by which to + * offset the byte pointer. */ + this.offset = function(n) { + pos += n; + if (pos < 0) { + throw new Error('Seeking past start of the buffer'); + } + if (pos > bytes.length) { + throw new Error('Seeking past EOF'); + } + }; - audioChannelType: { - get: function() { - return _videoElement.audioChannelType(); - }, - set: function(type) { - _videoElement.audioChannelType(type); + /** + * @param {Array.} test Array of bytes to compare against. + * @return {boolean} True if the start of the stream matches the test + * bytes. + */ + this.match = function(test) { + if (test.length > pos + bytes.length) { + return false; + } + var i; + for (i = 0; i < test.length; i += 1) { + if (Number(bytes[pos + i]) !== test[i]) { + return false; } } - }); - - // Public Methods - - this.imgData = function() { - return _videoElement.imgData(); + return true; }; + } - this.appendTo = function(parentDomElement) { - _videoElement.appendTo(parentDomElement); - return this; - }; + /** + * @constructor + * @param {Array.} bytes The array to write bytes into. + */ + function ByteOutputStream(bytes) { + /** @type {number} */ + var pos = 0; - this.bindToStream = function(webRtcStream, completion) { - _streamBound = false; - _stream = webRtcStream; + /** + * @param {...number} var_args The byte or bytes to emit into the stream. + * @return {number} The last byte emitted. + */ + this.emit = function(var_args) { + /** @type {number} */ + var last = EOF_byte; + var i; + for (i = 0; i < arguments.length; ++i) { + last = Number(arguments[i]); + bytes[pos++] = last; + } + return last; + }; + } - _videoElement.bindToStream(webRtcStream, OT.$.bind(function(err) { - if (err) { - completion(err); - return; - } - - _streamBound = true; - - if (_preInitialisedVolue) { - this.setAudioVolume(_preInitialisedVolue); - _preInitialisedVolue = null; + /** + * @constructor + * @param {string} string The source of code units for the stream. + */ + function CodePointInputStream(string) { + /** + * @param {string} string Input string of UTF-16 code units. + * @return {Array.} Code points. + */ + function stringToCodePoints(string) { + /** @type {Array.} */ + var cps = []; + // Based on http://www.w3.org/TR/WebIDL/#idl-DOMString + var i = 0, n = string.length; + while (i < string.length) { + var c = string.charCodeAt(i); + if (!inRange(c, 0xD800, 0xDFFF)) { + cps.push(c); + } else if (inRange(c, 0xDC00, 0xDFFF)) { + cps.push(0xFFFD); + } else { // (inRange(cu, 0xD800, 0xDBFF)) + if (i === n - 1) { + cps.push(0xFFFD); + } else { + var d = string.charCodeAt(i + 1); + if (inRange(d, 0xDC00, 0xDFFF)) { + var a = c & 0x3FF; + var b = d & 0x3FF; + i += 1; + cps.push(0x10000 + (a << 10) + b); + } else { + cps.push(0xFFFD); + } + } } - - completion(null); - }, this)); - - return this; - }; - - this.unbindStream = function() { - if (!_stream) return this; - - _stream = null; - _videoElement.unbindStream(); - return this; - }; - - this.setAudioVolume = function (value) { - if (_streamBound) _videoElement.setAudioVolume( OT.$.roundFloat(value / 100, 2) ); - else _preInitialisedVolue = value; - - return this; - }; - - this.getAudioVolume = function () { - if (_streamBound) return parseInt(_videoElement.getAudioVolume() * 100, 10); - else return _preInitialisedVolue || 50; - }; - - - this.whenTimeIncrements = function (callback, context) { - _videoElement.whenTimeIncrements(callback, context); - return this; - }; - - this.destroy = function () { - // unbind all events so they don't fire after the object is dead - this.off(); - - _videoElement.destroy(); - return void 0; - }; - }; - - var PluginVideoElement = function PluginVideoElement (options, - errorHandler, - orientationChangedHandler) { - var _videoProxy, - _parentDomElement; - - canBeOrientatedMixin(this, - function() { return _videoProxy.domElement; }, - orientationChangedHandler); - - /// Public methods - - this.domElement = function() { - return _videoProxy ? _videoProxy.domElement : void 0; - }; - - this.videoWidth = function() { - return _videoProxy ? _videoProxy.getVideoWidth() : void 0; - }; - - this.videoHeight = function() { - return _videoProxy ? _videoProxy.getVideoHeight() : void 0; - }; - - this.imgData = function() { - return _videoProxy ? _videoProxy.getImgData() : null; - }; - - // Append the Video DOM element to a parent node - this.appendTo = function(parentDomElement) { - _parentDomElement = parentDomElement; - return this; - }; - - // Bind a stream to the video element. - this.bindToStream = function(webRtcStream, completion) { - if (!_parentDomElement) { - completion('The VideoElement must attached to a DOM node before a stream can be bound'); - return; + i += 1; } + return cps; + } - _videoProxy = webRtcStream._.render(); - _videoProxy.appendTo(_parentDomElement); - _videoProxy.show(completion); - - return this; - }; - - // Unbind the currently bound stream from the video element. - this.unbindStream = function() { - // TODO: some way to tell TBPlugin to release that stream and controller + /** @type {number} */ + var pos = 0; + /** @type {Array.} */ + var cps = stringToCodePoints(string); - if (_videoProxy) { - _videoProxy.destroy(); - _parentDomElement = null; - _videoProxy = null; + /** @param {number} n The number of bytes (positive or negative) + * to advance the code point pointer by.*/ + this.offset = function(n) { + pos += n; + if (pos < 0) { + throw new Error('Seeking past start of the buffer'); + } + if (pos > cps.length) { + throw new Error('Seeking past EOF'); } - - return this; }; - this.setAudioVolume = function(value) { - if (_videoProxy) _videoProxy.setVolume(value); - }; - this.getAudioVolume = function() { - // Return the actual volume of the DOM element - if (_videoProxy) return _videoProxy.getVolume(); - return DefaultAudioVolume; + /** @return {number} Get the next code point from the stream. */ + this.get = function() { + if (pos >= cps.length) { + return EOF_code_point; + } + return cps[pos]; }; + } - // see https://wiki.mozilla.org/WebAPI/AudioChannels - // The audioChannelType is not currently supported in the plugin. - this.audioChannelType = function(/* type */) { - return 'unknown'; - }; + /** + * @constructor + */ + function CodePointOutputStream() { + /** @type {string} */ + var string = ''; - this.whenTimeIncrements = function(callback, context) { - // exists for compatibility with NativeVideoElement - OT.$.callAsync(OT.$.bind(callback, context)); + /** @return {string} The accumulated string. */ + this.string = function() { + return string; }; - this.destroy = function() { - this.unbindStream(); - - return void 0; + /** @param {number} c The code point to encode into the stream. */ + this.emit = function(c) { + if (c <= 0xFFFF) { + string += String.fromCharCode(c); + } else { + c -= 0x10000; + string += String.fromCharCode(0xD800 + ((c >> 10) & 0x3ff)); + string += String.fromCharCode(0xDC00 + (c & 0x3ff)); + } }; - }; - + } - var NativeDOMVideoElement = function NativeDOMVideoElement (options, - errorHandler, - orientationChangedHandler) { - var _domElement, - _videoElementMovedWarning = false; + /** + * @constructor + * @param {string} message Description of the error. + */ + function EncodingError(message) { + this.name = 'EncodingError'; + this.message = message; + this.code = 0; + } + EncodingError.prototype = Error.prototype; + /** + * @param {boolean} fatal If true, decoding errors raise an exception. + * @param {number=} opt_code_point Override the standard fallback code point. + * @return {number} The code point to insert on a decoding error. + */ + function decoderError(fatal, opt_code_point) { + if (fatal) { + throw new EncodingError('Decoder error'); + } + return opt_code_point || 0xFFFD; + } - /// Private API - var _onVideoError = OT.$.bind(function(event) { - var reason = 'There was an unexpected problem with the Video Stream: ' + - videoElementErrorCodeToStr(event.target.error.code); - errorHandler(reason, this, 'VideoElement'); - }, this), + /** + * @param {number} code_point The code point that could not be encoded. + */ + function encoderError(code_point) { + throw new EncodingError('The code point ' + code_point + + ' could not be encoded.'); + } - // The video element pauses itself when it's reparented, this is - // unfortunate. This function plays the video again and is triggered - // on the pause event. - _playVideoOnPause = function() { - if(!_videoElementMovedWarning) { - OT.warn('Video element paused, auto-resuming. If you intended to do this, ' + - 'use publishVideo(false) or subscribeToVideo(false) instead.'); + /** + * @param {string} label The encoding label. + * @return {?{name:string,labels:Array.}} + */ + function getEncoding(label) { + label = String(label).trim().toLowerCase(); + if (Object.prototype.hasOwnProperty.call(label_to_encoding, label)) { + return label_to_encoding[label]; + } + return null; + } - _videoElementMovedWarning = true; - } + /** @type {Array.<{encodings: Array.<{name:string,labels:Array.}>, + * heading: string}>} */ + var encodings = [ + { + 'encodings': [ + { + 'labels': [ + 'unicode-1-1-utf-8', + 'utf-8', + 'utf8' + ], + 'name': 'utf-8' + } + ], + 'heading': 'The Encoding' + }, + { + 'encodings': [ + { + 'labels': [ + 'cp864', + 'ibm864' + ], + 'name': 'ibm864' + }, + { + 'labels': [ + 'cp866', + 'ibm866' + ], + 'name': 'ibm866' + }, + { + 'labels': [ + 'csisolatin2', + 'iso-8859-2', + 'iso-ir-101', + 'iso8859-2', + 'iso_8859-2', + 'l2', + 'latin2' + ], + 'name': 'iso-8859-2' + }, + { + 'labels': [ + 'csisolatin3', + 'iso-8859-3', + 'iso_8859-3', + 'iso-ir-109', + 'l3', + 'latin3' + ], + 'name': 'iso-8859-3' + }, + { + 'labels': [ + 'csisolatin4', + 'iso-8859-4', + 'iso_8859-4', + 'iso-ir-110', + 'l4', + 'latin4' + ], + 'name': 'iso-8859-4' + }, + { + 'labels': [ + 'csisolatincyrillic', + 'cyrillic', + 'iso-8859-5', + 'iso_8859-5', + 'iso-ir-144' + ], + 'name': 'iso-8859-5' + }, + { + 'labels': [ + 'arabic', + 'csisolatinarabic', + 'ecma-114', + 'iso-8859-6', + 'iso_8859-6', + 'iso-ir-127' + ], + 'name': 'iso-8859-6' + }, + { + 'labels': [ + 'csisolatingreek', + 'ecma-118', + 'elot_928', + 'greek', + 'greek8', + 'iso-8859-7', + 'iso_8859-7', + 'iso-ir-126' + ], + 'name': 'iso-8859-7' + }, + { + 'labels': [ + 'csisolatinhebrew', + 'hebrew', + 'iso-8859-8', + 'iso-8859-8-i', + 'iso-ir-138', + 'iso_8859-8', + 'visual' + ], + 'name': 'iso-8859-8' + }, + { + 'labels': [ + 'csisolatin6', + 'iso-8859-10', + 'iso-ir-157', + 'iso8859-10', + 'l6', + 'latin6' + ], + 'name': 'iso-8859-10' + }, + { + 'labels': [ + 'iso-8859-13' + ], + 'name': 'iso-8859-13' + }, + { + 'labels': [ + 'iso-8859-14', + 'iso8859-14' + ], + 'name': 'iso-8859-14' + }, + { + 'labels': [ + 'iso-8859-15', + 'iso_8859-15' + ], + 'name': 'iso-8859-15' + }, + { + 'labels': [ + 'iso-8859-16' + ], + 'name': 'iso-8859-16' + }, + { + 'labels': [ + 'koi8-r', + 'koi8_r' + ], + 'name': 'koi8-r' + }, + { + 'labels': [ + 'koi8-u' + ], + 'name': 'koi8-u' + }, + { + 'labels': [ + 'csmacintosh', + 'mac', + 'macintosh', + 'x-mac-roman' + ], + 'name': 'macintosh' + }, + { + 'labels': [ + 'iso-8859-11', + 'tis-620', + 'windows-874' + ], + 'name': 'windows-874' + }, + { + 'labels': [ + 'windows-1250', + 'x-cp1250' + ], + 'name': 'windows-1250' + }, + { + 'labels': [ + 'windows-1251', + 'x-cp1251' + ], + 'name': 'windows-1251' + }, + { + 'labels': [ + 'ascii', + 'ansi_x3.4-1968', + 'csisolatin1', + 'iso-8859-1', + 'iso8859-1', + 'iso_8859-1', + 'l1', + 'latin1', + 'us-ascii', + 'windows-1252' + ], + 'name': 'windows-1252' + }, + { + 'labels': [ + 'cp1253', + 'windows-1253' + ], + 'name': 'windows-1253' + }, + { + 'labels': [ + 'csisolatin5', + 'iso-8859-9', + 'iso-ir-148', + 'l5', + 'latin5', + 'windows-1254' + ], + 'name': 'windows-1254' + }, + { + 'labels': [ + 'cp1255', + 'windows-1255' + ], + 'name': 'windows-1255' + }, + { + 'labels': [ + 'cp1256', + 'windows-1256' + ], + 'name': 'windows-1256' + }, + { + 'labels': [ + 'windows-1257' + ], + 'name': 'windows-1257' + }, + { + 'labels': [ + 'cp1258', + 'windows-1258' + ], + 'name': 'windows-1258' + }, + { + 'labels': [ + 'x-mac-cyrillic', + 'x-mac-ukrainian' + ], + 'name': 'x-mac-cyrillic' + } + ], + 'heading': 'Legacy single-byte encodings' + }, + { + 'encodings': [ + { + 'labels': [ + 'chinese', + 'csgb2312', + 'csiso58gb231280', + 'gb2312', + 'gbk', + 'gb_2312', + 'gb_2312-80', + 'iso-ir-58', + 'x-gbk' + ], + 'name': 'gbk' + }, + { + 'labels': [ + 'gb18030' + ], + 'name': 'gb18030' + }, + { + 'labels': [ + 'hz-gb-2312' + ], + 'name': 'hz-gb-2312' + } + ], + 'heading': 'Legacy multi-byte Chinese (simplified) encodings' + }, + { + 'encodings': [ + { + 'labels': [ + 'big5', + 'big5-hkscs', + 'cn-big5', + 'csbig5', + 'x-x-big5' + ], + 'name': 'big5' + } + ], + 'heading': 'Legacy multi-byte Chinese (traditional) encodings' + }, + { + 'encodings': [ + { + 'labels': [ + 'cseucpkdfmtjapanese', + 'euc-jp', + 'x-euc-jp' + ], + 'name': 'euc-jp' + }, + { + 'labels': [ + 'csiso2022jp', + 'iso-2022-jp' + ], + 'name': 'iso-2022-jp' + }, + { + 'labels': [ + 'csshiftjis', + 'ms_kanji', + 'shift-jis', + 'shift_jis', + 'sjis', + 'windows-31j', + 'x-sjis' + ], + 'name': 'shift_jis' + } + ], + 'heading': 'Legacy multi-byte Japanese encodings' + }, + { + 'encodings': [ + { + 'labels': [ + 'cseuckr', + 'csksc56011987', + 'euc-kr', + 'iso-ir-149', + 'korean', + 'ks_c_5601-1987', + 'ks_c_5601-1989', + 'ksc5601', + 'ksc_5601', + 'windows-949' + ], + 'name': 'euc-kr' + }, + { + 'labels': [ + 'csiso2022kr', + 'iso-2022-kr' + ], + 'name': 'iso-2022-kr' + } + ], + 'heading': 'Legacy multi-byte Korean encodings' + }, + { + 'encodings': [ + { + 'labels': [ + 'utf-16', + 'utf-16le' + ], + 'name': 'utf-16' + }, + { + 'labels': [ + 'utf-16be' + ], + 'name': 'utf-16be' + } + ], + 'heading': 'Legacy utf-16 encodings' + } + ]; + + var name_to_encoding = {}; + var label_to_encoding = {}; + encodings.forEach(function(category) { + category.encodings.forEach(function(encoding) { + name_to_encoding[encoding.name] = encoding; + encoding.labels.forEach(function(label) { + label_to_encoding[label] = encoding; + }); + }); + }); + + // + // 5. Indexes + // + + /** + * @param {number} pointer The |pointer| to search for. + * @param {Array.} index The |index| to search within. + * @return {?number} The code point corresponding to |pointer| in |index|, + * or null if |code point| is not in |index|. + */ + function indexCodePointFor(pointer, index) { + return (index || [])[pointer] || null; + } + + /** + * @param {number} code_point The |code point| to search for. + * @param {Array.} index The |index| to search within. + * @return {?number} The first pointer corresponding to |code point| in + * |index|, or null if |code point| is not in |index|. + */ + function indexPointerFor(code_point, index) { + var pointer = index.indexOf(code_point); + return pointer === -1 ? null : pointer; + } + + /** @type {Object.|Array.>)>} */ + var indexes = global['encoding-indexes'] || {}; + + + // + // 7. The encoding + // + + // 7.1 utf-8 + + /** + * @constructor + * @param {{fatal: boolean}} options + */ + function UTF8Decoder(options) { + var fatal = options.fatal; + var utf8_code_point = 0, + utf8_bytes_needed = 0, + utf8_bytes_seen = 0, + utf8_lower_boundary = 0; + + /** + * @param {ByteInputStream} byte_pointer The byte stream to decode. + * @return {?number} The next code point decoded, or null if not enough + * data exists in the input stream to decode a complete code point. + */ + this.decode = function(byte_pointer) { + var bite = byte_pointer.get(); + if (bite === EOF_byte) { + if (utf8_bytes_needed !== 0) { + return decoderError(fatal); + } + return EOF_code_point; + } + byte_pointer.offset(1); + + if (utf8_bytes_needed === 0) { + if (inRange(bite, 0x00, 0x7F)) { + return bite; + } + if (inRange(bite, 0xC2, 0xDF)) { + utf8_bytes_needed = 1; + utf8_lower_boundary = 0x80; + utf8_code_point = bite - 0xC0; + } else if (inRange(bite, 0xE0, 0xEF)) { + utf8_bytes_needed = 2; + utf8_lower_boundary = 0x800; + utf8_code_point = bite - 0xE0; + } else if (inRange(bite, 0xF0, 0xF4)) { + utf8_bytes_needed = 3; + utf8_lower_boundary = 0x10000; + utf8_code_point = bite - 0xF0; + } else { + return decoderError(fatal); + } + utf8_code_point = utf8_code_point * Math.pow(64, utf8_bytes_needed); + return null; + } + if (!inRange(bite, 0x80, 0xBF)) { + utf8_code_point = 0; + utf8_bytes_needed = 0; + utf8_bytes_seen = 0; + utf8_lower_boundary = 0; + byte_pointer.offset(-1); + return decoderError(fatal); + } + utf8_bytes_seen += 1; + utf8_code_point = utf8_code_point + (bite - 0x80) * + Math.pow(64, utf8_bytes_needed - utf8_bytes_seen); + if (utf8_bytes_seen !== utf8_bytes_needed) { + return null; + } + var code_point = utf8_code_point; + var lower_boundary = utf8_lower_boundary; + utf8_code_point = 0; + utf8_bytes_needed = 0; + utf8_bytes_seen = 0; + utf8_lower_boundary = 0; + if (inRange(code_point, lower_boundary, 0x10FFFF) && + !inRange(code_point, 0xD800, 0xDFFF)) { + return code_point; + } + return decoderError(fatal); + }; + } + + /** + * @constructor + * @param {{fatal: boolean}} options + */ + function UTF8Encoder(options) { + var fatal = options.fatal; + /** + * @param {ByteOutputStream} output_byte_stream Output byte stream. + * @param {CodePointInputStream} code_point_pointer Input stream. + * @return {number} The last byte emitted. + */ + this.encode = function(output_byte_stream, code_point_pointer) { + var code_point = code_point_pointer.get(); + if (code_point === EOF_code_point) { + return EOF_byte; + } + code_point_pointer.offset(1); + if (inRange(code_point, 0xD800, 0xDFFF)) { + return encoderError(code_point); + } + if (inRange(code_point, 0x0000, 0x007f)) { + return output_byte_stream.emit(code_point); + } + var count, offset; + if (inRange(code_point, 0x0080, 0x07FF)) { + count = 1; + offset = 0xC0; + } else if (inRange(code_point, 0x0800, 0xFFFF)) { + count = 2; + offset = 0xE0; + } else if (inRange(code_point, 0x10000, 0x10FFFF)) { + count = 3; + offset = 0xF0; + } + var result = output_byte_stream.emit( + div(code_point, Math.pow(64, count)) + offset); + while (count > 0) { + var temp = div(code_point, Math.pow(64, count - 1)); + result = output_byte_stream.emit(0x80 + (temp % 64)); + count -= 1; + } + return result; + }; + } + + name_to_encoding['utf-8'].getEncoder = function(options) { + return new UTF8Encoder(options); + }; + name_to_encoding['utf-8'].getDecoder = function(options) { + return new UTF8Decoder(options); + }; + + + // NOTE: currently unused + /** + * @param {string} label The encoding label. + * @param {ByteInputStream} input_stream The byte stream to test. + */ + function detectEncoding(label, input_stream) { + if (input_stream.match([0xFF, 0xFE])) { + input_stream.offset(2); + return 'utf-16'; + } + if (input_stream.match([0xFE, 0xFF])) { + input_stream.offset(2); + return 'utf-16be'; + } + if (input_stream.match([0xEF, 0xBB, 0xBF])) { + input_stream.offset(3); + return 'utf-8'; + } + return label; + } + + /** + * @param {string} label The encoding label. + * @param {ByteInputStream} input_stream The byte stream to test. + */ + function consumeBOM(label, input_stream) { + if (input_stream.match([0xFF, 0xFE]) && label === 'utf-16') { + input_stream.offset(2); + return; + } + if (input_stream.match([0xFE, 0xFF]) && label == 'utf-16be') { + input_stream.offset(2); + return; + } + if (input_stream.match([0xEF, 0xBB, 0xBF]) && label == 'utf-8') { + input_stream.offset(3); + return; + } + } + + // + // Implementation of Text Encoding Web API + // + + /** @const */ + var DEFAULT_ENCODING = 'utf-8'; + + /** + * @constructor + * @param {string=} opt_encoding The label of the encoding; + * defaults to 'utf-8'. + * @param {{fatal: boolean}=} options + */ + function TextEncoder(opt_encoding, options) { + if (!this || this === global) { + return new TextEncoder(opt_encoding, options); + } + opt_encoding = opt_encoding ? String(opt_encoding) : DEFAULT_ENCODING; + options = Object(options); + /** @private */ + this._encoding = getEncoding(opt_encoding); + if (this._encoding === null || (this._encoding.name !== 'utf-8' && + this._encoding.name !== 'utf-16' && + this._encoding.name !== 'utf-16be')) + throw new TypeError('Unknown encoding: ' + opt_encoding); + /* @private @type {boolean} */ + this._streaming = false; + /** @private */ + this._encoder = null; + /* @private @type {{fatal: boolean}=} */ + this._options = { fatal: Boolean(options.fatal) }; + + if (Object.defineProperty) { + Object.defineProperty( + this, 'encoding', + { get: function() { return this._encoding.name; } }); + } else { + this.encoding = this._encoding.name; + } + + return this; + } + + TextEncoder.prototype = { + /** + * @param {string=} opt_string The string to encode. + * @param {{stream: boolean}=} options + */ + encode: function encode(opt_string, options) { + opt_string = opt_string ? String(opt_string) : ''; + options = Object(options); + // TODO: any options? + if (!this._streaming) { + this._encoder = this._encoding.getEncoder(this._options); + } + this._streaming = Boolean(options.stream); + + var bytes = []; + var output_stream = new ByteOutputStream(bytes); + var input_stream = new CodePointInputStream(opt_string); + while (input_stream.get() !== EOF_code_point) { + this._encoder.encode(output_stream, input_stream); + } + if (!this._streaming) { + var last_byte; + do { + last_byte = this._encoder.encode(output_stream, input_stream); + } while (last_byte !== EOF_byte); + this._encoder = null; + } + return new Uint8Array(bytes); + } + }; + + + /** + * @constructor + * @param {string=} opt_encoding The label of the encoding; + * defaults to 'utf-8'. + * @param {{fatal: boolean}=} options + */ + function TextDecoder(opt_encoding, options) { + if (!this || this === global) { + return new TextDecoder(opt_encoding, options); + } + opt_encoding = opt_encoding ? String(opt_encoding) : DEFAULT_ENCODING; + options = Object(options); + /** @private */ + this._encoding = getEncoding(opt_encoding); + if (this._encoding === null) + throw new TypeError('Unknown encoding: ' + opt_encoding); + + /* @private @type {boolean} */ + this._streaming = false; + /** @private */ + this._decoder = null; + /* @private @type {{fatal: boolean}=} */ + this._options = { fatal: Boolean(options.fatal) }; + + if (Object.defineProperty) { + Object.defineProperty( + this, 'encoding', + { get: function() { return this._encoding.name; } }); + } else { + this.encoding = this._encoding.name; + } + + return this; + } + + // TODO: Issue if input byte stream is offset by decoder + // TODO: BOM detection will not work if stream header spans multiple calls + // (last N bytes of previous stream may need to be retained?) + TextDecoder.prototype = { + /** + * @param {ArrayBufferView=} opt_view The buffer of bytes to decode. + * @param {{stream: boolean}=} options + */ + decode: function decode(opt_view, options) { + if (opt_view && !('buffer' in opt_view && 'byteOffset' in opt_view && + 'byteLength' in opt_view)) { + throw new TypeError('Expected ArrayBufferView'); + } else if (!opt_view) { + opt_view = new Uint8Array(0); + } + options = Object(options); + + if (!this._streaming) { + this._decoder = this._encoding.getDecoder(this._options); + } + this._streaming = Boolean(options.stream); + + var bytes = new Uint8Array(opt_view.buffer, + opt_view.byteOffset, + opt_view.byteLength); + var input_stream = new ByteInputStream(bytes); + + if (!this._BOMseen) { + // TODO: Don't do this until sufficient bytes are present + this._BOMseen = true; + consumeBOM(this._encoding.name, input_stream); + } + + var output_stream = new CodePointOutputStream(), code_point; + while (input_stream.get() !== EOF_byte) { + code_point = this._decoder.decode(input_stream); + if (code_point !== null && code_point !== EOF_code_point) { + output_stream.emit(code_point); + } + } + if (!this._streaming) { + do { + code_point = this._decoder.decode(input_stream); + if (code_point !== null && code_point !== EOF_code_point) { + output_stream.emit(code_point); + } + } while (code_point !== EOF_code_point && + input_stream.get() != EOF_byte); + this._decoder = null; + } + return output_stream.string(); + } + }; + + global['TextEncoder'] = global['TextEncoder'] || TextEncoder; + global['TextDecoder'] = global['TextDecoder'] || TextDecoder; + + /* jshint ignore:end */ + +}(this)); + +// tb_require('../../../helpers/helpers.js') +// tb_require('./encoding.js') +// tb_require('./rumor.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT, TextEncoder, TextDecoder */ + +// +// +// @references +// * https://tbwiki.tokbox.com/index.php/Rumor_Message_Packet +// * https://tbwiki.tokbox.com/index.php/Rumor_Protocol +// +OT.Rumor.Message = function (type, toAddress, headers, data) { + this.type = type; + this.toAddress = toAddress; + this.headers = headers; + this.data = data; + + this.transactionId = this.headers['TRANSACTION-ID']; + this.status = this.headers.STATUS; + this.isError = !(this.status && this.status[0] === '2'); +}; + +OT.Rumor.Message.prototype.serialize = function () { + var offset = 8, + cBuf = 7, + address = [], + headerKey = [], + headerVal = [], + strArray, + dataView, + i, + j; + + // The number of addresses + cBuf++; + + // Write out the address. + for (i = 0; i < this.toAddress.length; i++) { + /*jshint newcap:false */ + address.push(new TextEncoder('utf-8').encode(this.toAddress[i])); + cBuf += 2; + cBuf += address[i].length; + } + + // The number of parameters + cBuf++; + + // Write out the params + i = 0; + + for (var key in this.headers) { + if(!this.headers.hasOwnProperty(key)) { + continue; + } + headerKey.push(new TextEncoder('utf-8').encode(key)); + headerVal.push(new TextEncoder('utf-8').encode(this.headers[key])); + cBuf += 4; + cBuf += headerKey[i].length; + cBuf += headerVal[i].length; + + i++; + } + + dataView = new TextEncoder('utf-8').encode(this.data); + cBuf += dataView.length; + + // Let's allocate a binary blob of this size + var buffer = new ArrayBuffer(cBuf); + var uint8View = new Uint8Array(buffer, 0, cBuf); + + // We don't include the header in the lenght. + cBuf -= 4; + + // Write out size (in network order) + uint8View[0] = (cBuf & 0xFF000000) >>> 24; + uint8View[1] = (cBuf & 0x00FF0000) >>> 16; + uint8View[2] = (cBuf & 0x0000FF00) >>> 8; + uint8View[3] = (cBuf & 0x000000FF) >>> 0; + + // Write out reserved bytes + uint8View[4] = 0; + uint8View[5] = 0; + + // Write out message type + uint8View[6] = this.type; + uint8View[7] = this.toAddress.length; + + // Now just copy over the encoded values.. + for (i = 0; i < address.length; i++) { + strArray = address[i]; + uint8View[offset++] = strArray.length >> 8 & 0xFF; + uint8View[offset++] = strArray.length >> 0 & 0xFF; + for (j = 0; j < strArray.length; j++) { + uint8View[offset++] = strArray[j]; + } + } + + uint8View[offset++] = headerKey.length; + + // Write out the params + for (i = 0; i < headerKey.length; i++) { + strArray = headerKey[i]; + uint8View[offset++] = strArray.length >> 8 & 0xFF; + uint8View[offset++] = strArray.length >> 0 & 0xFF; + for (j = 0; j < strArray.length; j++) { + uint8View[offset++] = strArray[j]; + } + + strArray = headerVal[i]; + uint8View[offset++] = strArray.length >> 8 & 0xFF; + uint8View[offset++] = strArray.length >> 0 & 0xFF; + for (j = 0; j < strArray.length; j++) { + uint8View[offset++] = strArray[j]; + } + } + + // And finally the data + for (i = 0; i < dataView.length; i++) { + uint8View[offset++] = dataView[i]; + } + + return buffer; +}; + +function toArrayBuffer(buffer) { + var ab = new ArrayBuffer(buffer.length); + var view = new Uint8Array(ab); + for (var i = 0; i < buffer.length; ++i) { + view[i] = buffer[i]; + } + return ab; +} + +OT.Rumor.Message.deserialize = function (buffer) { + + if(typeof Buffer !== 'undefined' && + Buffer.isBuffer(buffer)) { + buffer = toArrayBuffer(buffer); + } + var cBuf = 0, + type, + offset = 8, + uint8View = new Uint8Array(buffer), + strView, + headerlen, + headers, + keyStr, + valStr, + length, + i; + + // Write out size (in network order) + cBuf += uint8View[0] << 24; + cBuf += uint8View[1] << 16; + cBuf += uint8View[2] << 8; + cBuf += uint8View[3] << 0; + + type = uint8View[6]; + var address = []; + + for (i = 0; i < uint8View[7]; i++) { + length = uint8View[offset++] << 8; + length += uint8View[offset++]; + strView = new Uint8Array(buffer, offset, length); + /*jshint newcap:false */ + address[i] = new TextDecoder('utf-8').decode(strView); + offset += length; + } + + headerlen = uint8View[offset++]; + headers = {}; + + for (i = 0; i < headerlen; i++) { + length = uint8View[offset++] << 8; + length += uint8View[offset++]; + strView = new Uint8Array(buffer, offset, length); + keyStr = new TextDecoder('utf-8').decode(strView); + offset += length; + + length = uint8View[offset++] << 8; + length += uint8View[offset++]; + strView = new Uint8Array(buffer, offset, length); + valStr = new TextDecoder('utf-8').decode(strView); + headers[keyStr] = valStr; + offset += length; + } + + var dataView = new Uint8Array(buffer, offset); + var data = new TextDecoder('utf-8').decode(dataView); + + return new OT.Rumor.Message(type, address, headers, data); +}; + + +OT.Rumor.Message.Connect = function (uniqueId, notifyDisconnectAddress) { + var headers = { + uniqueId: uniqueId, + notifyDisconnectAddress: notifyDisconnectAddress + }; + + return new OT.Rumor.Message(OT.Rumor.MessageType.CONNECT, [], headers, ''); +}; + +OT.Rumor.Message.Disconnect = function () { + return new OT.Rumor.Message(OT.Rumor.MessageType.DISCONNECT, [], {}, ''); +}; + +OT.Rumor.Message.Subscribe = function(topics) { + return new OT.Rumor.Message(OT.Rumor.MessageType.SUBSCRIBE, topics, {}, ''); +}; + +OT.Rumor.Message.Unsubscribe = function(topics) { + return new OT.Rumor.Message(OT.Rumor.MessageType.UNSUBSCRIBE, topics, {}, ''); +}; + +OT.Rumor.Message.Publish = function(topics, message, headers) { + return new OT.Rumor.Message(OT.Rumor.MessageType.MESSAGE, topics, headers||{}, message || ''); +}; + +// This message is used to implement keepalives on the persistent +// socket connection between the client and server. Every time the +// client sends a PING to the server, the server will respond with +// a PONG. +OT.Rumor.Message.Ping = function() { + return new OT.Rumor.Message(OT.Rumor.MessageType.PING, [], {}, ''); +}; + +// tb_require('../../../helpers/helpers.js') +// tb_require('./rumor.js') +// tb_require('./message.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ + +!(function() { + + var BUFFER_DRAIN_INTERVAL = 100, + // The total number of times to retest the websocket's send buffer + BUFFER_DRAIN_MAX_RETRIES = 10; + + OT.Rumor.NativeSocket = function(TheWebSocket, messagingURL, events) { + + var webSocket, + disconnectWhenSendBufferIsDrained, + bufferDrainTimeout, // Timer to poll whether th send buffer has been drained + close; + + webSocket = new TheWebSocket(messagingURL); + webSocket.binaryType = 'arraybuffer'; + + webSocket.onopen = events.onOpen; + webSocket.onclose = events.onClose; + webSocket.onerror = events.onError; + + webSocket.onmessage = function(message) { + if (!OT) { + // In IE 10/11, This can apparently be called after + // the page is unloaded and OT is garbage-collected + return; + } + + var msg = OT.Rumor.Message.deserialize(message.data); + events.onMessage(msg); + }; + + // Ensure that the WebSocket send buffer is fully drained before disconnecting + // the socket. If the buffer doesn't drain after a certain length of time + // we give up and close it anyway. + disconnectWhenSendBufferIsDrained = + function disconnectWhenSendBufferIsDrained (bufferDrainRetries) { + if (!webSocket) return; + + if (bufferDrainRetries === void 0) bufferDrainRetries = 0; + if (bufferDrainTimeout) clearTimeout(bufferDrainTimeout); + + if (webSocket.bufferedAmount > 0 && + (bufferDrainRetries + 1) <= BUFFER_DRAIN_MAX_RETRIES) { + bufferDrainTimeout = setTimeout(disconnectWhenSendBufferIsDrained, + BUFFER_DRAIN_INTERVAL, bufferDrainRetries+1); + + } else { + close(); + } + }; + + close = function close() { + webSocket.close(); + }; + + this.close = function(drainBuffer) { + if (drainBuffer) { + disconnectWhenSendBufferIsDrained(); + } else { + close(); + } + }; + + this.send = function(msg) { + webSocket.send(msg.serialize()); + }; + + this.isClosed = function() { + return webSocket.readyState === 3; + }; + + }; + + +}(this)); + +// tb_require('../../../helpers/helpers.js') +// tb_require('./message.js') +// tb_require('./native_socket.js') +// tb_require('./plugin_socket.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ + +var WEB_SOCKET_KEEP_ALIVE_INTERVAL = 9000, + + // Magic Connectivity Timeout Constant: We wait 9*the keep alive interval, + // on the third keep alive we trigger the timeout if we haven't received the + // server pong. + WEB_SOCKET_CONNECTIVITY_TIMEOUT = 5*WEB_SOCKET_KEEP_ALIVE_INTERVAL - 100, + + wsCloseErrorCodes; + +// https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Close_codes +// http://docs.oracle.com/javaee/7/api/javax/websocket/CloseReason.CloseCodes.html +wsCloseErrorCodes = { + 1002: 'The endpoint is terminating the connection due to a protocol error. ' + + '(CLOSE_PROTOCOL_ERROR)', + 1003: 'The connection is being terminated because the endpoint received data of ' + + 'a type it cannot accept (for example, a text-only endpoint received binary data). ' + + '(CLOSE_UNSUPPORTED)', + 1004: 'The endpoint is terminating the connection because a data frame was received ' + + 'that is too large. (CLOSE_TOO_LARGE)', + 1005: 'Indicates that no status code was provided even though one was expected. ' + + '(CLOSE_NO_STATUS)', + 1006: 'Used to indicate that a connection was closed abnormally (that is, with no ' + + 'close frame being sent) when a status code is expected. (CLOSE_ABNORMAL)', + 1007: 'Indicates that an endpoint is terminating the connection because it has received ' + + 'data within a message that was not consistent with the type of the message (e.g., ' + + 'non-UTF-8 [RFC3629] data within a text message)', + 1008: 'Indicates that an endpoint is terminating the connection because it has received a ' + + 'message that violates its policy. This is a generic status code that can be returned ' + + 'when there is no other more suitable status code (e.g., 1003 or 1009) or if there is a ' + + 'need to hide specific details about the policy', + 1009: 'Indicates that an endpoint is terminating the connection because it has received a ' + + 'message that is too big for it to process', + 1011: 'Indicates that a server is terminating the connection because it encountered an ' + + 'unexpected condition that prevented it from fulfilling the request', + + // .... codes in the 4000-4999 range are available for use by applications. + 4001: 'Connectivity loss was detected as it was too long since the socket received the ' + + 'last PONG message' +}; + +OT.Rumor.SocketError = function(code, message) { + this.code = code; + this.message = message; +}; + +// The NativeSocket bit is purely to make testing simpler, it defaults to WebSocket +// so in normal operation you would omit it. +OT.Rumor.Socket = function(messagingURL, notifyDisconnectAddress, NativeSocket) { + + var states = ['disconnected', 'error', 'connected', 'connecting', 'disconnecting'], + webSocket, + id, + onOpen, + onError, + onClose, + onMessage, + connectCallback, + connectTimeout, + lastMessageTimestamp, // The timestamp of the last message received + keepAliveTimer; // Timer for the connectivity checks + + + //// Private API + var stateChanged = function(newState) { + switch (newState) { + case 'disconnected': + case 'error': + webSocket = null; + if (onClose) { + var error; + if(hasLostConnectivity()) { + error = new Error(wsCloseErrorCodes[4001]); + error.code = 4001; + } + onClose(error); + } + break; + } + }, + + setState = OT.$.statable(this, states, 'disconnected', stateChanged), + + validateCallback = function validateCallback (name, callback) { + if (callback === null || !OT.$.isFunction(callback) ) { + throw new Error('The Rumor.Socket ' + name + + ' callback must be a valid function or null'); + } + }, + + error = OT.$.bind(function error (errorMessage) { + OT.error('Rumor.Socket: ' + errorMessage); + + var socketError = new OT.Rumor.SocketError(null, errorMessage || 'Unknown Socket Error'); + + if (connectTimeout) clearTimeout(connectTimeout); + + setState('error'); + + if (this.previousState === 'connecting' && connectCallback) { + connectCallback(socketError, void 0); + connectCallback = null; + } + + if (onError) onError(socketError); + }, this), + + hasLostConnectivity = function hasLostConnectivity () { + if (!lastMessageTimestamp) return false; + + return (OT.$.now() - lastMessageTimestamp) >= WEB_SOCKET_CONNECTIVITY_TIMEOUT; + }, + + sendKeepAlive = OT.$.bind(function() { + if (!this.is('connected')) return; + + if ( hasLostConnectivity() ) { + webSocketDisconnected({code: 4001}); + } + else { + webSocket.send(OT.Rumor.Message.Ping()); + keepAliveTimer = setTimeout(sendKeepAlive, WEB_SOCKET_KEEP_ALIVE_INTERVAL); + } + }, this), + + // Returns true if we think the DOM has been unloaded + // It detects this by looking for the OT global, which + // should always exist until the DOM is cleaned up. + isDOMUnloaded = function isDOMUnloaded () { + return !window.OT; + }; + + + //// Private Event Handlers + var webSocketConnected = OT.$.bind(function webSocketConnected () { + if (connectTimeout) clearTimeout(connectTimeout); + if (this.isNot('connecting')) { + OT.debug('webSocketConnected reached in state other than connecting'); + return; + } + + // Connect to Rumor by registering our connection id and the + // app server address to notify if we disconnect. + // + // We don't need to wait for a reply to this message. + webSocket.send(OT.Rumor.Message.Connect(id, notifyDisconnectAddress)); + + setState('connected'); + if (connectCallback) { + connectCallback(void 0, id); + connectCallback = null; + } + + if (onOpen) onOpen(id); + + keepAliveTimer = setTimeout(function() { + lastMessageTimestamp = OT.$.now(); + sendKeepAlive(); + }, WEB_SOCKET_KEEP_ALIVE_INTERVAL); + }, this), + + webSocketConnectTimedOut = function webSocketConnectTimedOut () { + var webSocketWas = webSocket; + error('Timed out while waiting for the Rumor socket to connect.'); + // This will prevent a socket eventually connecting + // But call it _after_ the error just in case any of + // the callbacks fire synchronously, breaking the error + // handling code. + try { + webSocketWas.close(); + } catch(x) {} + }, + + webSocketError = function webSocketError () {}, + // var errorMessage = 'Unknown Socket Error'; + // @fixme We MUST be able to do better than this! + + // All errors seem to result in disconnecting the socket, the close event + // has a close reason and code which gives some error context. This, + // combined with the fact that the errorEvent argument contains no + // error info at all, means we'll delay triggering the error handlers + // until the socket is closed. + // error(errorMessage); + + webSocketDisconnected = OT.$.bind(function webSocketDisconnected (closeEvent) { + if (connectTimeout) clearTimeout(connectTimeout); + if (keepAliveTimer) clearTimeout(keepAliveTimer); + + if (isDOMUnloaded()) { + // Sometimes we receive the web socket close event after + // the DOM has already been partially or fully unloaded + // if that's the case here then it's not really safe, or + // desirable, to continue. + return; + } + + if (closeEvent.code !== 1000 && closeEvent.code !== 1001) { + var reason = closeEvent.reason || closeEvent.message; + if (!reason && wsCloseErrorCodes.hasOwnProperty(closeEvent.code)) { + reason = wsCloseErrorCodes[closeEvent.code]; + } + + error('Rumor Socket Disconnected: ' + reason); + } + + if (this.isNot('error')) setState('disconnected'); + }, this), + + webSocketReceivedMessage = function webSocketReceivedMessage (msg) { + lastMessageTimestamp = OT.$.now(); + + if (onMessage) { + if (msg.type !== OT.Rumor.MessageType.PONG) { + onMessage(msg); + } + } + }; + + + //// Public API + + this.publish = function (topics, message, headers) { + webSocket.send(OT.Rumor.Message.Publish(topics, message, headers)); + }; + + this.subscribe = function(topics) { + webSocket.send(OT.Rumor.Message.Subscribe(topics)); + }; + + this.unsubscribe = function(topics) { + webSocket.send(OT.Rumor.Message.Unsubscribe(topics)); + }; + + this.connect = function (connectionId, complete) { + if (this.is('connecting', 'connected')) { + complete(new OT.Rumor.SocketError(null, + 'Rumor.Socket cannot connect when it is already connecting or connected.')); + return; + } + + id = connectionId; + connectCallback = complete; + + setState('connecting'); + + var TheWebSocket = NativeSocket || window.WebSocket; + + var events = { + onOpen: webSocketConnected, + onClose: webSocketDisconnected, + onError: webSocketError, + onMessage: webSocketReceivedMessage + }; + + try { + if(typeof TheWebSocket !== 'undefined') { + webSocket = new OT.Rumor.NativeSocket(TheWebSocket, messagingURL, events); + } else { + webSocket = new OT.Rumor.PluginSocket(messagingURL, events); + } + + connectTimeout = setTimeout(webSocketConnectTimedOut, OT.Rumor.Socket.CONNECT_TIMEOUT); + } + catch(e) { + OT.error(e); + + // @todo add an actual error message + error('Could not connect to the Rumor socket, possibly because of a blocked port.'); + } + }; + + this.disconnect = function(drainSocketBuffer) { + if (connectTimeout) clearTimeout(connectTimeout); + if (keepAliveTimer) clearTimeout(keepAliveTimer); + + if (!webSocket) { + if (this.isNot('error')) setState('disconnected'); + return; + } + + if (webSocket.isClosed()) { + if (this.isNot('error')) setState('disconnected'); + } + else { + if (this.is('connected')) { + // Look! We are nice to the rumor server ;-) + webSocket.send(OT.Rumor.Message.Disconnect()); + } + + // Wait until the socket is ready to close + webSocket.close(drainSocketBuffer); + } + }; + + + + OT.$.defineProperties(this, { + id: { + get: function() { return id; } + }, + + onOpen: { + set: function(callback) { + validateCallback('onOpen', callback); + onOpen = callback; + }, + + get: function() { return onOpen; } + }, + + onError: { + set: function(callback) { + validateCallback('onError', callback); + onError = callback; + }, + + get: function() { return onError; } + }, + + onClose: { + set: function(callback) { + validateCallback('onClose', callback); + onClose = callback; + }, + + get: function() { return onClose; } + }, + + onMessage: { + set: function(callback) { + validateCallback('onMessage', callback); + onMessage = callback; + }, + + get: function() { return onMessage; } + } + }); +}; + +// The number of ms to wait for the websocket to connect +OT.Rumor.Socket.CONNECT_TIMEOUT = 15000; + + +// tb_require('../../../helpers/helpers.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ + +// Rumor Messaging for JS +// +// https://tbwiki.tokbox.com/index.php/Raptor_Messages_(Sent_as_a_RumorMessage_payload_in_JSON) +// +// @todo Raptor { +// Look at disconnection cleanup: i.e. subscriber + publisher cleanup +// Add error codes for all the error cases +// Write unit tests for SessionInfo +// Write unit tests for Session +// Make use of the new DestroyedEvent +// Remove dependency on OT.properties +// OT.Capabilities must be part of the Raptor namespace +// Add Dependability commands +// Think about noConflict, or whether we should just use the OT namespace +// Think about how to expose OT.publishers, OT.subscribers, and OT.sessions if messaging was +// being included as a component +// Another solution to the problem of having publishers/subscribers/etc would be to make +// Raptor Socket a separate component from Dispatch (dispatch being more business logic) +// Look at the coupling of OT.sessions to OT.Raptor.Socket +// } +// +// @todo Raptor Docs { +// Document payload formats for incoming messages (what are the payloads for +// STREAM CREATED/MODIFIED for example) +// Document how keepalives work +// Document all the Raptor actions and types +// Document the session connect flow (including error cases) +// } + +OT.Raptor = { + Actions: { + //General + CONNECT: 100, + CREATE: 101, + UPDATE: 102, + DELETE: 103, + STATE: 104, + + //Moderation + FORCE_DISCONNECT: 105, + FORCE_UNPUBLISH: 106, + SIGNAL: 107, + + //Archives + CREATE_ARCHIVE: 108, + CLOSE_ARCHIVE: 109, + START_RECORDING_SESSION: 110, + STOP_RECORDING_SESSION: 111, + START_RECORDING_STREAM: 112, + STOP_RECORDING_STREAM: 113, + LOAD_ARCHIVE: 114, + START_PLAYBACK: 115, + STOP_PLAYBACK: 116, + + //AppState + APPSTATE_PUT: 117, + APPSTATE_DELETE: 118, + + // JSEP + OFFER: 119, + ANSWER: 120, + PRANSWER: 121, + CANDIDATE: 122, + SUBSCRIBE: 123, + UNSUBSCRIBE: 124, + QUERY: 125, + SDP_ANSWER: 126, + + //KeepAlive + PONG: 127, + REGISTER: 128, //Used for registering streams. + + QUALITY_CHANGED: 129 + }, + + Types: { + //RPC + RPC_REQUEST: 100, + RPC_RESPONSE: 101, + + //EVENT + STREAM: 102, + ARCHIVE: 103, + CONNECTION: 104, + APPSTATE: 105, + CONNECTIONCOUNT: 106, + MODERATION: 107, + SIGNAL: 108, + SUBSCRIBER: 110, + + //JSEP Protocol + JSEP: 109 + } +}; + +// tb_require('../../../helpers/helpers.js') +// tb_require('./raptor.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ + +OT.Raptor.serializeMessage = function (message) { + return JSON.stringify(message); +}; + + +// Deserialising a Raptor message mainly means doing a JSON.parse on it. +// We do decorate the final message with a few extra helper properies though. +// +// These include: +// * typeName: A human readable version of the Raptor type. E.g. STREAM instead of 102 +// * actionName: A human readable version of the Raptor action. E.g. CREATE instead of 101 +// * signature: typeName and actionName combined. This is mainly for debugging. E.g. A type +// of 102 and an action of 101 would result in a signature of "STREAM:CREATE" +// +OT.Raptor.deserializeMessage = function (msg) { + if (msg.length === 0) return {}; + + var message = JSON.parse(msg), + bits = message.uri.substr(1).split('/'); + + // Remove the Raptor protocol version + bits.shift(); + if (bits[bits.length-1] === '') bits.pop(); + + message.params = {}; + for (var i=0, numBits=bits.length ; i 6) { + message.resource = bits[bits.length-4] + '_' + bits[bits.length-2]; + } else { + message.resource = bits[bits.length-2]; + } + } + else { + if (bits[bits.length-1] === 'channel' && bits.length > 5) { + message.resource = bits[bits.length-3] + '_' + bits[bits.length-1]; + } else { + message.resource = bits[bits.length-1]; + } + } + + message.signature = message.resource + '#' + message.method; + return message; +}; + +OT.Raptor.unboxFromRumorMessage = function (rumorMessage) { + var message = OT.Raptor.deserializeMessage(rumorMessage.data); + message.transactionId = rumorMessage.transactionId; + message.fromAddress = rumorMessage.headers['X-TB-FROM-ADDRESS']; + + return message; +}; + +OT.Raptor.parseIceServers = function (message) { + try { + return JSON.parse(message.data).content.iceServers; + } catch (e) { + return []; + } +}; + +OT.Raptor.Message = {}; + + +OT.Raptor.Message.offer = function (uri, offerSdp) { + return OT.Raptor.serializeMessage({ + method: 'offer', + uri: uri, + content: { + sdp: offerSdp + } + }); +}; + + +OT.Raptor.Message.connections = {}; + +OT.Raptor.Message.connections.create = function (apiKey, sessionId, connectionId) { + return OT.Raptor.serializeMessage({ + method: 'create', + uri: '/v2/partner/' + apiKey + '/session/' + sessionId + '/connection/' + connectionId, + content: { + userAgent: OT.$.env.userAgent + } + }); +}; + +OT.Raptor.Message.connections.destroy = function (apiKey, sessionId, connectionId) { + return OT.Raptor.serializeMessage({ + method: 'delete', + uri: '/v2/partner/' + apiKey + '/session/' + sessionId + '/connection/' + connectionId, + content: {} + }); +}; + + +OT.Raptor.Message.sessions = {}; + +OT.Raptor.Message.sessions.get = function (apiKey, sessionId) { + return OT.Raptor.serializeMessage({ + method: 'read', + uri: '/v2/partner/' + apiKey + '/session/' + sessionId, + content: {} + }); +}; + + +OT.Raptor.Message.streams = {}; + +OT.Raptor.Message.streams.get = function (apiKey, sessionId, streamId) { + return OT.Raptor.serializeMessage({ + method: 'read', + uri: '/v2/partner/' + apiKey + '/session/' + sessionId + '/stream/' + streamId, + content: {} + }); +}; + +OT.Raptor.Message.streams.channelFromOTChannel = function(channel) { + var raptorChannel = { + id: channel.id, + type: channel.type, + active: channel.active + }; + + if (channel.type === 'video') { + raptorChannel.width = channel.width; + raptorChannel.height = channel.height; + raptorChannel.orientation = channel.orientation; + raptorChannel.frameRate = channel.frameRate; + if (channel.source !== 'default') { + raptorChannel.source = channel.source; + } + raptorChannel.fitMode = channel.fitMode; + } - _domElement.play(); - }; + return raptorChannel; +}; +OT.Raptor.Message.streams.create = function (apiKey, sessionId, streamId, name, + audioFallbackEnabled, channels, minBitrate, maxBitrate) { + var messageContent = { + id: streamId, + name: name, + audioFallbackEnabled: audioFallbackEnabled, + channel: OT.$.map(channels, function(channel) { + return OT.Raptor.Message.streams.channelFromOTChannel(channel); + }) + }; + + if (minBitrate) messageContent.minBitrate = minBitrate; + if (maxBitrate) messageContent.maxBitrate = maxBitrate; + + return OT.Raptor.serializeMessage({ + method: 'create', + uri: '/v2/partner/' + apiKey + '/session/' + sessionId + '/stream/' + streamId, + content: messageContent + }); +}; - _domElement = createNativeVideoElement(options.fallbackText, options.attributes); +OT.Raptor.Message.streams.destroy = function (apiKey, sessionId, streamId) { + return OT.Raptor.serializeMessage({ + method: 'delete', + uri: '/v2/partner/' + apiKey + '/session/' + sessionId + '/stream/' + streamId, + content: {} + }); +}; - _domElement.addEventListener('pause', _playVideoOnPause); - canBeOrientatedMixin(this, function() { return _domElement; }, orientationChangedHandler); +OT.Raptor.Message.streams.answer = function (apiKey, sessionId, streamId, answerSdp) { + return OT.Raptor.serializeMessage({ + method: 'answer', + uri: '/v2/partner/' + apiKey + '/session/' + sessionId + '/stream/' + streamId, + content: { + sdp: answerSdp + } + }); +}; - /// Public methods +OT.Raptor.Message.streams.candidate = function (apiKey, sessionId, streamId, candidate) { + return OT.Raptor.serializeMessage({ + method: 'candidate', + uri: '/v2/partner/' + apiKey + '/session/' + sessionId + '/stream/' + streamId, + content: candidate + }); +}; - this.domElement = function() { - return _domElement; - }; +OT.Raptor.Message.streamChannels = {}; +OT.Raptor.Message.streamChannels.update = + function (apiKey, sessionId, streamId, channelId, attributes) { + return OT.Raptor.serializeMessage({ + method: 'update', + uri: '/v2/partner/' + apiKey + '/session/' + sessionId + '/stream/' + + streamId + '/channel/' + channelId, + content: attributes + }); +}; - this.videoWidth = function() { - return _domElement.videoWidth; - }; - this.videoHeight = function() { - return _domElement.videoHeight; - }; +OT.Raptor.Message.subscribers = {}; - this.imgData = function() { - var canvas = OT.$.createElement('canvas', { - width: _domElement.videoWidth, - height: _domElement.videoHeight, - style: { display: 'none' } - }); +OT.Raptor.Message.subscribers.create = + function (apiKey, sessionId, streamId, subscriberId, connectionId, channelsToSubscribeTo) { + var content = { + id: subscriberId, + connection: connectionId, + keyManagementMethod: OT.$.supportedCryptoScheme(), + bundleSupport: OT.$.hasCapabilities('bundle'), + rtcpMuxSupport: OT.$.hasCapabilities('RTCPMux') + }; + if (channelsToSubscribeTo) content.channel = channelsToSubscribeTo; + + return OT.Raptor.serializeMessage({ + method: 'create', + uri: '/v2/partner/' + apiKey + '/session/' + sessionId + + '/stream/' + streamId + '/subscriber/' + subscriberId, + content: content + }); +}; - document.body.appendChild(canvas); - try { - canvas.getContext('2d').drawImage(_domElement, 0, 0, canvas.width, canvas.height); - } catch(err) { - OT.warn('Cannot get image data yet'); - return null; - } - var imgData = canvas.toDataURL('image/png'); +OT.Raptor.Message.subscribers.destroy = function (apiKey, sessionId, streamId, subscriberId) { + return OT.Raptor.serializeMessage({ + method: 'delete', + uri: '/v2/partner/' + apiKey + '/session/' + sessionId + + '/stream/' + streamId + '/subscriber/' + subscriberId, + content: {} + }); +}; - OT.$.removeElement(canvas); +OT.Raptor.Message.subscribers.update = + function (apiKey, sessionId, streamId, subscriberId, attributes) { + return OT.Raptor.serializeMessage({ + method: 'update', + uri: '/v2/partner/' + apiKey + '/session/' + sessionId + + '/stream/' + streamId + '/subscriber/' + subscriberId, + content: attributes + }); +}; - return OT.$.trim(imgData.replace('data:image/png;base64,', '')); - }; - // Append the Video DOM element to a parent node - this.appendTo = function(parentDomElement) { - parentDomElement.appendChild(_domElement); - return this; - }; +OT.Raptor.Message.subscribers.candidate = + function (apiKey, sessionId, streamId, subscriberId, candidate) { + return OT.Raptor.serializeMessage({ + method: 'candidate', + uri: '/v2/partner/' + apiKey + '/session/' + sessionId + + '/stream/' + streamId + '/subscriber/' + subscriberId, + content: candidate + }); +}; - // Bind a stream to the video element. - this.bindToStream = function(webRtcStream, completion) { - bindStreamToNativeVideoElement(_domElement, webRtcStream, function(err) { - if (err) { - completion(err); - return; - } - _domElement.addEventListener('error', _onVideoError, false); - completion(null); - }); +OT.Raptor.Message.subscribers.answer = + function (apiKey, sessionId, streamId, subscriberId, answerSdp) { + return OT.Raptor.serializeMessage({ + method: 'answer', + uri: '/v2/partner/' + apiKey + '/session/' + sessionId + + '/stream/' + streamId + '/subscriber/' + subscriberId, + content: { + sdp: answerSdp + } + }); +}; - return this; - }; +OT.Raptor.Message.subscriberChannels = {}; - // Unbind the currently bound stream from the video element. - this.unbindStream = function() { - if (_domElement) { - unbindNativeStream(_domElement); - } +OT.Raptor.Message.subscriberChannels.update = + function (apiKey, sessionId, streamId, subscriberId, channelId, attributes) { + return OT.Raptor.serializeMessage({ + method: 'update', + uri: '/v2/partner/' + apiKey + '/session/' + sessionId + + '/stream/' + streamId + '/subscriber/' + subscriberId + '/channel/' + channelId, + content: attributes + }); +}; - return this; - }; - this.setAudioVolume = function(value) { - if (_domElement) _domElement.volume = value; - }; +OT.Raptor.Message.signals = {}; - this.getAudioVolume = function() { - // Return the actual volume of the DOM element - if (_domElement) return _domElement.volume; - return DefaultAudioVolume; - }; +OT.Raptor.Message.signals.create = function (apiKey, sessionId, toAddress, type, data) { + var content = {}; + if (type !== void 0) content.type = type; + if (data !== void 0) content.data = data; + + return OT.Raptor.serializeMessage({ + method: 'signal', + uri: '/v2/partner/' + apiKey + '/session/' + sessionId + + (toAddress !== void 0 ? '/connection/' + toAddress : '') + '/signal/' + OT.$.uuid(), + content: content + }); +}; - // see https://wiki.mozilla.org/WebAPI/AudioChannels - // The audioChannelType is currently only available in Firefox. This property returns - // "unknown" in other browser. The related HTML tag attribute is "mozaudiochannel" - this.audioChannelType = function(type) { - if (type !== void 0) { - _domElement.mozAudioChannelType = type; - } +// tb_require('../../../helpers/helpers.js') +// tb_require('./message.js') - if ('mozAudioChannelType' in _domElement) { - return _domElement.mozAudioChannelType; - } else { - return 'unknown'; - } - }; - this.whenTimeIncrements = function(callback, context) { - if(_domElement) { - var lastTime, handler; - handler = OT.$.bind(function() { - if(!lastTime || lastTime >= _domElement.currentTime) { - lastTime = _domElement.currentTime; - } else { - _domElement.removeEventListener('timeupdate', handler, false); - callback.call(context, this); - } - }, this); - _domElement.addEventListener('timeupdate', handler, false); - } - }; +!(function() { + /* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ + /* global OT, EventEmitter, util */ - this.destroy = function() { - this.unbindStream(); + // Connect error codes and reasons that Raptor can return. + var connectErrorReasons; - if (_domElement) { - // Unbind this first, otherwise it will trigger when the - // video element is removed from the DOM. - _domElement.removeEventListener('pause', _playVideoOnPause); + connectErrorReasons = { + 409: 'This P2P session already has 2 participants.', + 410: 'The session already has four participants.', + 1004: 'The token passed is invalid.' + }; - OT.$.removeElement(_domElement); - _domElement = null; + + OT.Raptor.Dispatcher = function () { + + if(OT.$.env.name === 'Node') { + EventEmitter.call(this); + } else { + OT.$.eventing(this, true); + this.emit = this.trigger; + } + + this.callbacks = {}; + }; + + if(OT.$.env.name === 'Node') { + util.inherits(OT.Raptor.Dispatcher, EventEmitter); + } + + OT.Raptor.Dispatcher.prototype.registerCallback = function (transactionId, completion) { + this.callbacks[transactionId] = completion; + }; + + OT.Raptor.Dispatcher.prototype.triggerCallback = function (transactionId) { + /*, arg1, arg2, argN-1, argN*/ + if (!transactionId) return; + + var completion = this.callbacks[transactionId]; + + if (completion && OT.$.isFunction(completion)) { + var args = Array.prototype.slice.call(arguments); + args.shift(); + + completion.apply(null, args); + } + + delete this.callbacks[transactionId]; + }; + + OT.Raptor.Dispatcher.prototype.onClose = function(reason) { + this.emit('close', reason); + }; + + + OT.Raptor.Dispatcher.prototype.dispatch = function(rumorMessage) { + // The special casing of STATUS messages is ugly. Need to think about + // how to better integrate this. + + if (rumorMessage.type === OT.Rumor.MessageType.STATUS) { + OT.debug('OT.Raptor.dispatch: STATUS'); + OT.debug(rumorMessage); + + var error; + + if (rumorMessage.isError) { + error = new OT.Error(rumorMessage.status); } - return void 0; - }; + this.triggerCallback(rumorMessage.transactionId, error, rumorMessage); + + return; + } + + var message = OT.Raptor.unboxFromRumorMessage(rumorMessage); + OT.debug('OT.Raptor.dispatch ' + message.signature); + OT.debug(rumorMessage.data); + + switch(message.resource) { + case 'session': + this.dispatchSession(message); + break; + + case 'connection': + this.dispatchConnection(message); + break; + + case 'stream': + this.dispatchStream(message); + break; + + case 'stream_channel': + this.dispatchStreamChannel(message); + break; + + case 'subscriber': + this.dispatchSubscriber(message); + break; + + case 'subscriber_channel': + this.dispatchSubscriberChannel(message); + break; + + case 'signal': + this.dispatchSignal(message); + break; + + case 'archive': + this.dispatchArchive(message); + break; + + default: + OT.warn('OT.Raptor.dispatch: Type ' + message.resource + ' is not currently implemented'); + } }; -/// Private Helper functions + OT.Raptor.Dispatcher.prototype.dispatchSession = function (message) { + switch (message.method) { + case 'read': + this.emit('session#read', message.content, message.transactionId); + break; - // A mixin to create the orientation API implementation on +self+ - // +getDomElementCallback+ is a function that the mixin will call when it wants to - // get the native Dom element for +self+. - // - // +initialOrientation+ sets the initial orientation (shockingly), it's currently unused - // so the initial value is actually undefined. - // - var canBeOrientatedMixin = function canBeOrientatedMixin (self, - getDomElementCallback, - orientationChangedHandler, - initialOrientation) { - var _orientation = initialOrientation; - OT.$.defineProperties(self, { - isRotated: { - get: function() { - return this.orientation() && - (this.orientation().videoOrientation === 270 || - this.orientation().videoOrientation === 90); - } - }, + default: + OT.warn('OT.Raptor.dispatch: ' + message.signature + ' is not currently implemented'); + } + }; + + OT.Raptor.Dispatcher.prototype.dispatchConnection = function (message) { - orientation: { - get: function() { return _orientation; }, - set: function(orientation) { - _orientation = orientation; + switch (message.method) { + case 'created': + this.emit('connection#created', message.content); + break; - var transform = VideoOrientationTransforms[orientation.videoOrientation] || - VideoOrientationTransforms.ROTATED_NORMAL; - switch(OT.$.browser()) { - case 'Chrome': - case 'Safari': - getDomElementCallback().style.webkitTransform = transform; - break; + case 'deleted': + this.emit('connection#deleted', message.params.connection, message.reason); + break; - case 'IE': - if (OT.$.browserVersion().version >= 9) { - getDomElementCallback().style.msTransform = transform; - } - else { - // So this basically defines matrix that represents a rotation - // of a single vector in a 2d basis. - // - // R = [cos(Theta) -sin(Theta)] - // [sin(Theta) cos(Theta)] - // - // Where Theta is the number of radians to rotate by - // - // Then to rotate the vector v: - // v' = Rv - // - // We then use IE8 Matrix filter property, which takes - // a 2x2 rotation matrix, to rotate our DOM element. - // - var radians = orientation.videoOrientation * DEGREE_TO_RADIANS, - element = getDomElementCallback(), - costheta = Math.cos(radians), - sintheta = Math.sin(radians); + default: + OT.warn('OT.Raptor.dispatch: ' + message.signature + ' is not currently implemented'); + } + }; - // element.filters.item(0).M11 = costheta; - // element.filters.item(0).M12 = -sintheta; - // element.filters.item(0).M21 = sintheta; - // element.filters.item(0).M22 = costheta; + OT.Raptor.Dispatcher.prototype.dispatchStream = function (message) { - element.style.filter = 'progid:DXImageTransform.Microsoft.Matrix(' + - 'M11='+costheta+',' + - 'M12='+(-sintheta)+',' + - 'M21='+sintheta+',' + - 'M22='+costheta+',SizingMethod=\'auto expand\')'; - } + switch (message.method) { + case 'created': + this.emit('stream#created', message.content, message.transactionId); + break; + case 'deleted': + this.emit('stream#deleted', message.params.stream, + message.reason); + break; - break; - default: - // The standard version, just Firefox, Opera, and IE > 9 - getDomElementCallback().style.transform = transform; - } + case 'updated': + this.emit('stream#updated', message.params.stream, + message.content); + break; - orientationChangedHandler(_orientation); - } - }, + // The JSEP process + case 'generateoffer': + case 'answer': + case 'pranswer': + case 'offer': + case 'candidate': + this.dispatchJsep(message.method, message); + break; - // see https://wiki.mozilla.org/WebAPI/AudioChannels - // The audioChannelType is currently only available in Firefox. This property returns - // "unknown" in other browser. The related HTML tag attribute is "mozaudiochannel" - audioChannelType: { - get: function() { - if ('mozAudioChannelType' in this.domElement) { - return this.domElement.mozAudioChannelType; - } else { - return 'unknown'; - } - }, - set: function(type) { - if ('mozAudioChannelType' in this.domElement) { - this.domElement.mozAudioChannelType = type; - } - } - } - }); + default: + OT.warn('OT.Raptor.dispatch: ' + message.signature + ' is not currently implemented'); + } }; - function createNativeVideoElement(fallbackText, attributes) { - var videoElement = document.createElement('video'); - videoElement.setAttribute('autoplay', ''); - videoElement.innerHTML = fallbackText; - - if (attributes) { - if (attributes.muted === true) { - delete attributes.muted; - videoElement.muted = 'true'; - } + OT.Raptor.Dispatcher.prototype.dispatchStreamChannel = function (message) { + switch (message.method) { + case 'updated': + this.emit('streamChannel#updated', message.params.stream, + message.params.channel, message.content); + break; - for (var key in attributes) { - if(!attributes.hasOwnProperty(key)) { - continue; - } - videoElement.setAttribute(key, attributes[key]); - } + default: + OT.warn('OT.Raptor.dispatch: ' + message.signature + ' is not currently implemented'); } + }; - return videoElement; - } + // Dispatch JSEP messages + // + // generateoffer: + // Request to generate a offer for another Peer (or Prism). This kicks + // off the JSEP process. + // + // answer: + // generate a response to another peers offer, this contains our constraints + // and requirements. + // + // pranswer: + // a provisional answer, i.e. not the final one. + // + // candidate + // + // + OT.Raptor.Dispatcher.prototype.dispatchJsep = function (method, message) { + this.emit('jsep#' + method, message.params.stream, message.fromAddress, message); + }; - // See http://www.w3.org/TR/2010/WD-html5-20101019/video.html#error-codes - var _videoErrorCodes = {}; + OT.Raptor.Dispatcher.prototype.dispatchSubscriberChannel = function (message) { + switch (message.method) { + case 'updated': + this.emit('subscriberChannel#updated', message.params.stream, + message.params.channel, message.content); + break; - // Checking for window.MediaError for IE compatibility, just so we don't throw - // exceptions when the script is included - if (window.MediaError) { - _videoErrorCodes[window.MediaError.MEDIA_ERR_ABORTED] = 'The fetching process for the media ' + - 'resource was aborted by the user agent at the user\'s request.'; - _videoErrorCodes[window.MediaError.MEDIA_ERR_NETWORK] = 'A network error of some description ' + - 'caused the user agent to stop fetching the media resource, after the resource was ' + - 'established to be usable.'; - _videoErrorCodes[window.MediaError.MEDIA_ERR_DECODE] = 'An error of some description ' + - 'occurred while decoding the media resource, after the resource was established to be ' + - ' usable.'; - _videoErrorCodes[window.MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED] = 'The media resource ' + - 'indicated by the src attribute was not suitable.'; - } - function videoElementErrorCodeToStr(errorCode) { - return _videoErrorCodes[parseInt(errorCode, 10)] || 'An unknown error occurred.'; - } + case 'update': // subscriberId, streamId, content + this.emit('subscriberChannel#update', message.params.subscriber, + message.params.stream, message.content); + break; - function bindStreamToNativeVideoElement(videoElement, webRtcStream, completion) { - var cleanup, - onLoad, - onError, - onStoppedLoading, - timeout; - - // Note: onloadedmetadata doesn't fire in Chrome for audio only crbug.com/110938 - // After version 36 it will fire if the video track is disabled. - var browser = OT.$.browserVersion(), - needsDisabledAudioProtection = browser.browser === 'Chrome' && browser.version < 36; - if (navigator.mozGetUserMedia || !(needsDisabledAudioProtection && - (webRtcStream.getVideoTracks().length > 0 && webRtcStream.getVideoTracks()[0].enabled))) { + default: + OT.warn('OT.Raptor.dispatch: ' + message.signature + ' is not currently implemented'); + } + }; + + OT.Raptor.Dispatcher.prototype.dispatchSubscriber = function (message) { + switch (message.method) { + case 'created': + this.emit('subscriber#created', message.params.stream, message.fromAddress, + message.content.id); + break; - cleanup = function cleanup () { - clearTimeout(timeout); - videoElement.removeEventListener('loadedmetadata', onLoad, false); - videoElement.removeEventListener('error', onError, false); - webRtcStream.onended = null; - }; - onLoad = function onLoad () { - cleanup(); - completion(null); - }; + case 'deleted': + this.dispatchJsep('unsubscribe', message); + this.emit('subscriber#deleted', message.params.stream, + message.fromAddress); + break; - onError = function onError (event) { - cleanup(); - unbindNativeStream(videoElement); - completion('There was an unexpected problem with the Video Stream: ' + - videoElementErrorCodeToStr(event.target.error.code)); - }; - onStoppedLoading = function onStoppedLoading () { - // The stream ended before we fully bound it. Maybe the other end called - // stop on it or something else went wrong. - cleanup(); - unbindNativeStream(videoElement); - completion('Stream ended while trying to bind it to a video element.'); - }; + // The JSEP process + case 'generateoffer': + case 'answer': + case 'pranswer': + case 'offer': + case 'candidate': + this.dispatchJsep(message.method, message); + break; - // Timeout if it takes too long - timeout = setTimeout(OT.$.bind(function() { - if (videoElement.currentTime === 0) { - cleanup(); - completion('The video stream failed to connect. Please notify the site ' + - 'owner if this continues to happen.'); - } else if (webRtcStream.ended === true) { - // The ended event should have fired by here, but support for it isn't - // always so awesome. - onStoppedLoading(); - } else { - OT.warn('Never got the loadedmetadata event but currentTime > 0'); - onLoad(null); - } - }, this), 30000); + default: + OT.warn('OT.Raptor.dispatch: ' + message.signature + ' is not currently implemented'); + } + }; - videoElement.addEventListener('loadedmetadata', onLoad, false); - videoElement.addEventListener('error', onError, false); - webRtcStream.onended = onStoppedLoading; - } else { - OT.$.callAsync(completion, null); + OT.Raptor.Dispatcher.prototype.dispatchSignal = function (message) { + if (message.method !== 'signal') { + OT.warn('OT.Raptor.dispatch: ' + message.signature + ' is not currently implemented'); + return; } + this.emit('signal', message.fromAddress, message.content.type, + message.content.data); + }; - // The official spec way is 'srcObject', we are slowly converging there. - if (videoElement.srcObject !== void 0) { - videoElement.srcObject = webRtcStream; - } else if (videoElement.mozSrcObject !== void 0) { - videoElement.mozSrcObject = webRtcStream; - } else { - videoElement.src = window.URL.createObjectURL(webRtcStream); + OT.Raptor.Dispatcher.prototype.dispatchArchive = function (message) { + switch (message.method) { + case 'created': + this.emit('archive#created', message.content); + break; + + case 'updated': + this.emit('archive#updated', message.params.archive, message.content); + break; } + }; + +}(this)); + +// tb_require('../../../helpers/helpers.js') +// tb_require('./message.js') +// tb_require('./dispatch.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - videoElement.play(); - } +(function(window) { + // @todo hide these + OT.publishers = new OT.$.Collection('guid'); // Publishers are id'd by their guid + OT.subscribers = new OT.$.Collection('widgetId'); // Subscribers are id'd by their widgetId + OT.sessions = new OT.$.Collection(); - function unbindNativeStream(videoElement) { - if (videoElement.srcObject !== void 0) { - videoElement.srcObject = null; - } else if (videoElement.mozSrcObject !== void 0) { - videoElement.mozSrcObject = null; - } else { - window.URL.revokeObjectURL(videoElement.src); - } + function parseStream(dict, session) { + var channel = dict.channel.map(function(channel) { + return new OT.StreamChannel(channel); + }); + + var connectionId = dict.connectionId ? dict.connectionId : dict.connection.id; + + return new OT.Stream( dict.id, + dict.name, + dict.creationTime, + session.connections.get(connectionId), + session, + channel ); } + function parseAndAddStreamToSession(dict, session) { + if (session.streams.has(dict.id)) return; -})(window); -// tb_require('../helpers/helpers.js') + var stream = parseStream(dict, session); + session.streams.add( stream ); -!(function() { - /* jshint globalstrict: true, strict: false, undef: true, unused: true, - trailing: true, browser: true, smarttabs:true */ - /* global OT */ + return stream; + } - var currentGuidStorage, - currentGuid; + function parseArchive(dict) { + return new OT.Archive( dict.id, + dict.name, + dict.status ); + } - var isInvalidStorage = function isInvalidStorage (storageInterface) { - return !(OT.$.isFunction(storageInterface.get) && OT.$.isFunction(storageInterface.set)); - }; + function parseAndAddArchiveToSession(dict, session) { + if (session.archives.has(dict.id)) return; - var getClientGuid = function getClientGuid (completion) { - if (currentGuid) { - completion(null, currentGuid); - return; - } + var archive = parseArchive(dict); + session.archives.add(archive); - // It's the first time that getClientGuid has been called - // in this page lifetime. Attempt to load any existing Guid - // from the storage - currentGuidStorage.get(completion); - }; + return archive; + } - OT.overrideGuidStorage = function (storageInterface) { - if (isInvalidStorage(storageInterface)) { - throw new Error('The storageInterface argument does not seem to be valid, ' + - 'it must implement get and set methods'); - } + var DelayedEventQueue = function DelayedEventQueue (eventDispatcher) { + var queue = []; - if (currentGuidStorage === storageInterface) { - return; - } + this.enqueue = function enqueue (/* arg1, arg2, ..., argN */) { + queue.push( Array.prototype.slice.call(arguments) ); + }; - currentGuidStorage = storageInterface; + this.triggerAll = function triggerAll () { + var event; - // If a client Guid has already been assigned to this client then - // let the new storage know about it so that it's in sync. - if (currentGuid) { - currentGuidStorage.set(currentGuid, function(error) { - if (error) { - OT.error('Failed to send initial Guid value (' + currentGuid + - ') to the newly assigned Guid Storage. The error was: ' + error); - // @todo error - } - }); - } + // Array.prototype.shift is actually pretty inefficient for longer Arrays, + // this is because after the first element is removed it reshuffles every + // remaining element up one (1). This involves way too many allocations and + // deallocations as the queue size increases. + // + // A more efficient version could be written by keeping an index to the current + // 'first' element in the Array and increasing that by one whenever an element + // is removed. The elements that are infront of the index have been 'deleted'. + // Periodically the front of the Array could be spliced off to reclaim the space. + // + // 1. http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.9 + // + // + // TLDR: Array.prototype.shift is O(n), where n is the array length, + // instead of the expected O(1). You can implement your own shift that runs + // in amortised constant time. + // + // @todo benchmark and see if we should actually care about shift's performance + // for our common queue sizes. + // + while( (event = queue.shift()) ) { + eventDispatcher.trigger.apply(eventDispatcher, event); + } + }; }; - if (!OT._) OT._ = {}; - OT._.getClientGuid = function (completion) { - getClientGuid(function(error, guid) { - if (error) { - completion(error); - return; - } + var DelayedSessionEvents = function(dispatcher) { + var eventQueues = {}; - if (!guid) { - // Nothing came back, this client is entirely new. - // generate a new Guid and persist it - guid = OT.$.uuid(); - currentGuidStorage.set(guid, function(error) { - if (error) { - completion(error); - return; - } + this.enqueue = function enqueue (/* key, arg1, arg2, ..., argN */) { + var key = arguments[0]; + var eventArgs = Array.prototype.slice.call(arguments, 1); + if (!eventQueues[key]) { + eventQueues[key] = new DelayedEventQueue(dispatcher); + } + eventQueues[key].enqueue.apply(eventQueues[key], eventArgs); + }; - currentGuid = guid; - }); + this.triggerConnectionCreated = function triggerConnectionCreated (connection) { + if (eventQueues['connectionCreated' + connection.id]) { + eventQueues['connectionCreated' + connection.id].triggerAll(); } - else if (!currentGuid) { - currentGuid = guid; + }; + + this.triggerSessionConnected = function triggerSessionConnected (connections) { + if (eventQueues.sessionConnected) { + eventQueues.sessionConnected.triggerAll(); } - completion(null, currentGuid); - }); + OT.$.forEach(connections, function(connection) { + this.triggerConnectionCreated(connection); + }); + }; }; + var unconnectedStreams = {}; - // Implement our default storage mechanism, which sets/gets a cookie - // called 'opentok_client_id' - OT.overrideGuidStorage({ - get: function(completion) { - completion(null, OT.$.getCookie('opentok_client_id')); - }, + window.OT.SessionDispatcher = function(session) { - set: function(guid, completion) { - OT.$.setCookie('opentok_client_id', guid); - completion(null); - } - }); + var dispatcher = new OT.Raptor.Dispatcher(), + sessionStateReceived = false, + delayedSessionEvents = new DelayedSessionEvents(dispatcher); -})(window); -!(function(window) { + dispatcher.on('close', function(reason) { - // Singleton interval - var logQueue = [], - queueRunning = false; + var connection = session.connection; + if (!connection) { + return; + } - OT.Analytics = function() { + if (connection.destroyedReason()) { + OT.debug('OT.Raptor.Socket: Socket was closed but the connection had already ' + + 'been destroyed. Reason: ' + connection.destroyedReason()); + return; + } - var endPoint = OT.properties.loggingURL + '/logging/ClientEvent', - endPointQos = OT.properties.loggingURL + '/logging/ClientQos', + connection.destroy( reason ); + }); + + // This method adds connections to the session both on a connection#created and + // on a session#read. In the case of session#read sessionRead is set to true and + // we include our own connection. + var addConnection = function (connection, sessionRead) { + connection = OT.Connection.fromHash(connection); + if (sessionRead || session.connection && connection.id !== session.connection.id) { + session.connections.add( connection ); + delayedSessionEvents.triggerConnectionCreated(connection); + } - reportedErrors = {}, + OT.$.forEach(OT.$.keys(unconnectedStreams), function(streamId) { + var stream = unconnectedStreams[streamId]; + if (stream && connection.id === stream.connection.id) { + // dispatch streamCreated event now that the connectionCreated has been dispatched + parseAndAddStreamToSession(stream, session); + delete unconnectedStreams[stream.id]; + + var payload = { + debug: sessionRead ? 'connection came in session#read' : + 'connection came in connection#created', + streamId : stream.id, + }; + session.logEvent('streamCreated', 'warning', payload); + } + }); + + return connection; + }; - // Map of camel-cased keys to underscored - camelCasedKeys, + dispatcher.on('session#read', function(content, transactionId) { - browser = OT.$.browserVersion(), + var state = {}, + connection; - send = function(data, isQos, callback) { - OT.$.post((isQos ? endPointQos : endPoint) + '?_=' + OT.$.uuid.v4(), { - body: data, - xdomainrequest: (browser.browser === 'IE' & browser.version < 10), - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }, callback); - }, + state.streams = []; + state.connections = []; + state.archives = []; - throttledPost = function() { - // Throttle logs so that they only happen 1 at a time - if (!queueRunning && logQueue.length > 0) { - queueRunning = true; - var curr = logQueue[0]; + OT.$.forEach(content.connection, function(connectionParams) { + connection = addConnection(connectionParams, true); + state.connections.push(connection); + }); - // Remove the current item and send the next log - var processNextItem = function() { - logQueue.shift(); - queueRunning = false; - throttledPost(); - }; + OT.$.forEach(content.stream, function(streamParams) { + state.streams.push( parseAndAddStreamToSession(streamParams, session) ); + }); - if (curr) { - send(curr.data, curr.isQos, function(err) { - if(err) { - OT.debug('Failed to send ClientEvent, moving on to the next item.'); - // There was an error, move onto the next item - } else { - curr.onComplete(); - } - setTimeout(processNextItem, 50); - }); - } - } - }, + OT.$.forEach(content.archive || content.archives, function(archiveParams) { + state.archives.push( parseAndAddArchiveToSession(archiveParams, session) ); + }); - post = function(data, onComplete, isQos) { - logQueue.push({ - data: data, - onComplete: onComplete, - isQos: isQos - }); + session._.subscriberMap = {}; - throttledPost(); - }, + dispatcher.triggerCallback(transactionId, null, state); - shouldThrottleError = function(code, type, partnerId) { - if (!partnerId) return false; + sessionStateReceived = true; + delayedSessionEvents.triggerSessionConnected(session.connections); + }); - var errKey = [partnerId, type, code].join('_'), - //msgLimit = DynamicConfig.get('exceptionLogging', 'messageLimitPerPartner', partnerId); - msgLimit = 100; - if (msgLimit === null || msgLimit === undefined) return false; - return (reportedErrors[errKey] || 0) <= msgLimit; - }; + dispatcher.on('connection#created', function(connection) { + addConnection(connection); + }); - camelCasedKeys = { - payloadType: 'payload_type', - partnerId: 'partner_id', - streamId: 'stream_id', - sessionId: 'session_id', - connectionId: 'connection_id', - widgetType: 'widget_type', - widgetId: 'widget_id', - avgAudioBitrate: 'avg_audio_bitrate', - avgVideoBitrate: 'avg_video_bitrate', - localCandidateType: 'local_candidate_type', - remoteCandidateType: 'remote_candidate_type', - transportType: 'transport_type' - }; + dispatcher.on('connection#deleted', function(connection, reason) { + connection = session.connections.get(connection); + connection.destroy(reason); + }); - // Log an error via ClientEvents. - // - // @param [String] code - // @param [String] type - // @param [String] message - // @param [Hash] details additional error details - // - // @param [Hash] options the options to log the client event with. - // @option options [String] action The name of the Event that we are logging. E.g. - // 'TokShowLoaded'. Required. - // @option options [String] variation Usually used for Split A/B testing, when you - // have multiple variations of the +_action+. - // @option options [String] payloadType A text description of the payload. Required. - // @option options [String] payload The payload. Required. - // @option options [String] sessionId The active OpenTok session, if there is one - // @option options [String] connectionId The active OpenTok connectionId, if there is one - // @option options [String] partnerId - // @option options [String] guid ... - // @option options [String] widgetId ... - // @option options [String] streamId ... - // @option options [String] section ... - // @option options [String] build ... - // - // Reports will be throttled to X reports (see exceptionLogging.messageLimitPerPartner - // from the dynamic config for X) of each error type for each partner. Reports can be - // disabled/enabled globally or on a per partner basis (per partner settings - // take precedence) using exceptionLogging.enabled. - // - this.logError = function(code, type, message, details, options) { - if (!options) options = {}; - var partnerId = options.partnerId; + dispatcher.on('stream#created', function(stream, transactionId) { + var connectionId = stream.connectionId ? stream.connectionId : stream.connection.id; + if (session.connections.has(connectionId)) { + stream = parseAndAddStreamToSession(stream, session); + } else { + unconnectedStreams[stream.id] = stream; - if (OT.Config.get('exceptionLogging', 'enabled', partnerId) !== true) { - return; + var payload = { + type : 'eventOrderError -- streamCreated event before connectionCreated', + streamId : stream.id, + }; + session.logEvent('streamCreated', 'warning', payload); } - if (shouldThrottleError(code, type, partnerId)) { - //OT.log('ClientEvents.error has throttled an error of type ' + type + '.' + - // code + ' for partner ' + (partnerId || 'No Partner Id')); + if (stream.publisher) { + stream.publisher.setStream(stream); + } + + dispatcher.triggerCallback(transactionId, null, stream); + }); + + dispatcher.on('stream#deleted', function(streamId, reason) { + var stream = session.streams.get(streamId); + + if (!stream) { + OT.error('OT.Raptor.dispatch: A stream does not exist with the id of ' + + streamId + ', for stream#deleted message!'); + // @todo error return; } - var errKey = [partnerId, type, code].join('_'), + stream.destroy(reason); + }); - payload = this.escapePayload(OT.$.extend(details || {}, { - message: payload, - userAgent: OT.$.userAgent() - })); + dispatcher.on('stream#updated', function(streamId, content) { + var stream = session.streams.get(streamId); + + if (!stream) { + OT.error('OT.Raptor.dispatch: A stream does not exist with the id of ' + + streamId + ', for stream#updated message!'); + // @todo error + return; + } + stream._.update(content); - reportedErrors[errKey] = typeof(reportedErrors[errKey]) !== 'undefined' ? - reportedErrors[errKey] + 1 : 1; + }); - return this.logEvent(OT.$.extend(options, { - action: type + '.' + code, - payloadType: payload[0], - payload: payload[1] - })); - }; + dispatcher.on('streamChannel#updated', function(streamId, channelId, content) { + var stream; + if (!(streamId && (stream = session.streams.get(streamId)))) { + OT.error('OT.Raptor.dispatch: Unable to determine streamId, or the stream does not ' + + 'exist, for streamChannel message!'); + // @todo error + return; + } + stream._.updateChannel(channelId, content); + }); - // Log a client event to the analytics backend. + // Dispatch JSEP messages // - // @example Logs a client event called 'foo' - // OT.ClientEvents.log({ - // action: 'foo', - // payload_type: 'foo's payload', - // payload: 'bar', - // session_id: sessionId, - // connection_id: connectionId - // }) + // generateoffer: + // Request to generate a offer for another Peer (or Prism). This kicks + // off the JSEP process. // - // @param [Hash] options the options to log the client event with. - // @option options [String] action The name of the Event that we are logging. - // E.g. 'TokShowLoaded'. Required. - // @option options [String] variation Usually used for Split A/B testing, when - // you have multiple variations of the +_action+. - // @option options [String] payloadType A text description of the payload. Required. - // @option options [String] payload The payload. Required. - // @option options [String] session_id The active OpenTok session, if there is one - // @option options [String] connection_id The active OpenTok connectionId, if there is one - // @option options [String] partner_id - // @option options [String] guid ... - // @option options [String] widget_id ... - // @option options [String] stream_id ... - // @option options [String] section ... - // @option options [String] build ... + // answer: + // generate a response to another peers offer, this contains our constraints + // and requirements. // - this.logEvent = function(options) { - var partnerId = options.partnerId; + // pranswer: + // a provisional answer, i.e. not the final one. + // + // candidate + // + // + var jsepHandler = function(method, streamId, fromAddress, message) { - if (!options) options = {}; + var fromConnection, + actors; - OT._.getClientGuid(function(error, guid) { - if (error) { - // @todo - return; - } + switch (method) { + // Messages for Subscribers + case 'offer': + actors = []; + var subscriber = OT.subscribers.find({streamId: streamId}); + if (subscriber) actors.push(subscriber); + break; - // Set a bunch of defaults - var data = OT.$.extend({ - 'variation' : '', - 'guid' : guid, - 'widget_id' : '', - 'session_id': '', - 'connection_id': '', - 'stream_id' : '', - 'partner_id' : partnerId, - 'source' : window.location.href, - 'section' : '', - 'build' : '' - }, options), - - onComplete = function(){ - // OT.log('logged: ' + '{action: ' + data['action'] + ', variation: ' + data['variation'] - // + ', payload_type: ' + data['payload_type'] + ', payload: ' + data['payload'] + '}'); - }; - // We camel-case our names, but the ClientEvents backend wants them - // underscored... - for (var key in camelCasedKeys) { - if (camelCasedKeys.hasOwnProperty(key) && data[key]) { - data[camelCasedKeys[key]] = data[key]; - delete data[key]; - } - } + // Messages for Publishers + case 'answer': + case 'pranswer': + case 'generateoffer': + case 'unsubscribe': + actors = OT.publishers.where({streamId: streamId}); + break; - post(data, onComplete, false); - }); - }; - // Log a client QOS to the analytics backend. - // - this.logQOS = function(options) { - var partnerId = options.partnerId; + // Messages for Publishers and Subscribers + case 'candidate': + // send to whichever of your publisher or subscribers are + // subscribing/publishing that stream + actors = OT.publishers.where({streamId: streamId}) + .concat(OT.subscribers.where({streamId: streamId})); + break; - if (!options) options = {}; - OT._.getClientGuid(function(error, guid) { - if (error) { - // @todo + default: + OT.warn('OT.Raptor.dispatch: jsep#' + method + + ' is not currently implemented'); return; - } + } - // Set a bunch of defaults - var data = OT.$.extend({ - 'guid' : guid, - 'widget_id' : '', - 'session_id': '', - 'connection_id': '', - 'stream_id' : '', - 'partner_id' : partnerId, - 'source' : window.location.href, - 'build' : '', - 'duration' : 0 //in milliseconds - }, options), - - onComplete = function(){ - // OT.log('logged: ' + '{action: ' + data['action'] + ', variation: ' + data['variation'] - // + ', payload_type: ' + data['payload_type'] + ', payload: ' + data['payload'] + '}'); - }; + if (actors.length === 0) return; - // We camel-case our names, but the ClientEvents backend wants them - // underscored... - for (var key in camelCasedKeys) { - if (camelCasedKeys.hasOwnProperty(key)) { - if(data[key]) { - data[camelCasedKeys[key]] = data[key]; - } - delete data[key]; - } - } + // This is a bit hacky. We don't have the session in the message so we iterate + // until we find the actor that the message relates to this stream, and then + // we grab the session from it. + fromConnection = actors[0].session.connections.get(fromAddress); + if(!fromConnection && fromAddress.match(/^symphony\./)) { + fromConnection = OT.Connection.fromHash({ + id: fromAddress, + creationTime: Math.floor(OT.$.now()) + }); + + actors[0].session.connections.add(fromConnection); + } else if(!fromConnection) { + OT.warn('OT.Raptor.dispatch: Messsage comes from a connection (' + + fromAddress + ') that we do not know about. The message was ignored.'); + return; + } - post(data, onComplete, true); + OT.$.forEach(actors, function(actor) { + actor.processMessage(method, fromConnection, message); }); }; - // Converts +payload+ to two pipe seperated strings. Doesn't currently handle - // edgecases, e.g. escaping '\\|' will break stuff. - // - // *Note:* It strip any keys that have null values. - this.escapePayload = function(payload) { - var escapedPayload = [], - escapedPayloadDesc = []; - - for (var key in payload) { - if (payload.hasOwnProperty(key) && payload[key] !== null && payload[key] !== undefined) { - escapedPayload.push( payload[key] ? payload[key].toString().replace('|', '\\|') : '' ); - escapedPayloadDesc.push( key.toString().replace('|', '\\|') ); - } + dispatcher.on('jsep#offer', OT.$.bind(jsepHandler, null, 'offer')); + dispatcher.on('jsep#answer', OT.$.bind(jsepHandler, null, 'answer')); + dispatcher.on('jsep#pranswer', OT.$.bind(jsepHandler, null, 'pranswer')); + dispatcher.on('jsep#generateoffer', OT.$.bind(jsepHandler, null, 'generateoffer')); + dispatcher.on('jsep#unsubscribe', OT.$.bind(jsepHandler, null, 'unsubscribe')); + dispatcher.on('jsep#candidate', OT.$.bind(jsepHandler, null, 'candidate')); + + dispatcher.on('subscriberChannel#updated', function(streamId, channelId, content) { + + if (!streamId || !session.streams.has(streamId)) { + OT.error('OT.Raptor.dispatch: Unable to determine streamId, or the stream does not ' + + 'exist, for subscriberChannel#updated message!'); + // @todo error + return; } - return [ - escapedPayloadDesc.join('|'), - escapedPayload.join('|') - ]; - }; - }; + session.streams.get(streamId)._ + .updateChannel(channelId, content); -})(window); -!(function() { + }); - OT.$.registerCapability('audioOutputLevelStat', function() { - return OT.$.browserVersion().browser === 'Chrome'; - }); + dispatcher.on('subscriberChannel#update', function(subscriberId, streamId, content) { - OT.$.registerCapability('webAudioCapableRemoteStream', function() { - return OT.$.browserVersion().browser === 'Firefox'; - }); + if (!streamId || !session.streams.has(streamId)) { + OT.error('OT.Raptor.dispatch: Unable to determine streamId, or the stream does not ' + + 'exist, for subscriberChannel#update message!'); + // @todo error + return; + } - OT.$.registerCapability('getStatsWithSingleParameter', function() { - return OT.$.browserVersion().browser === 'Chrome'; - }); + // Hint to update for congestion control from the Media Server + if (!OT.subscribers.has(subscriberId)) { + OT.error('OT.Raptor.dispatch: Unable to determine subscriberId, or the subscriber ' + + 'does not exist, for subscriberChannel#update message!'); + // @todo error + return; + } - OT.$.registerCapability('webAudio', function() { - return 'AudioContext' in window; - }); + // We assume that an update on a Subscriber channel is to disableVideo + // we may need to be more specific in the future + OT.subscribers.get(subscriberId).disableVideo(content.active); -})(); -!(function(window) { + }); - // This is not obvious, so to prevent end-user frustration we'll let them know - // explicitly rather than failing with a bunch of permission errors. We don't - // handle this using an OT Exception as it's really only a development thing. - if (location.protocol === 'file:') { - /*global alert*/ - alert('You cannot test a page using WebRTC through the file system due to browser ' + - 'permissions. You must run it over a web server.'); - } + dispatcher.on('subscriber#created', function(streamId, fromAddress, subscriberId) { - if (!window.OT) window.OT = {}; + var stream = streamId ? session.streams.get(streamId) : null; - if (!window.URL && window.webkitURL) { - window.URL = window.webkitURL; - } + if (!stream) { + OT.error('OT.Raptor.dispatch: Unable to determine streamId, or the stream does ' + + 'not exist, for subscriber#created message!'); + // @todo error + return; + } - var _analytics = new OT.Analytics(); + session._.subscriberMap[fromAddress + '_' + stream.id] = subscriberId; + }); - var // Global parameters used by upgradeSystemRequirements - _intervalId, - _lastHash = document.location.hash; + dispatcher.on('subscriber#deleted', function(streamId, fromAddress) { + var stream = streamId ? session.streams.get(streamId) : null; + if (!stream) { + OT.error('OT.Raptor.dispatch: Unable to determine streamId, or the stream does ' + + 'not exist, for subscriber#created message!'); + // @todo error + return; + } -/** -* The first step in using the OpenTok API is to call the OT.initSession() -* method. Other methods of the OT object check for system requirements and set up error logging. -* -* @class OT -*/ + delete session._.subscriberMap[fromAddress + '_' + stream.id]; + }); -/** -*

    -* Initializes and returns the local session object for a specified session ID. -*

    -*

    -* You connect to an OpenTok session using the connect() method -* of the Session object returned by the OT.initSession() method. -* Note that calling OT.initSession() does not initiate communications -* with the cloud. It simply initializes the Session object that you can use to -* connect (and to perform other operations once connected). -*

    -* -*

    -* For an example, see Session.connect(). -*

    -* -* @method OT.initSession -* @memberof OT -* @param {String} apiKey Your OpenTok API key (see the -* OpenTok dashboard). -* @param {String} sessionId The session ID identifying the OpenTok session. For more -* information, see Session creation. -* @returns {Session} The session object through which all further interactions with -* the session will occur. -*/ - OT.initSession = function(apiKey, sessionId) { + dispatcher.on('signal', function(fromAddress, signalType, data) { + var fromConnection = session.connections.get(fromAddress); + if (session.connection && fromAddress === session.connection.connectionId) { + if (sessionStateReceived) { + session._.dispatchSignal(fromConnection, signalType, data); + } else { + delayedSessionEvents.enqueue('sessionConnected', + 'signal', fromAddress, signalType, data); + } + } else { + if (session.connections.get(fromAddress)) { + session._.dispatchSignal(fromConnection, signalType, data); + } else { + delayedSessionEvents.enqueue('connectionCreated' + fromAddress, + 'signal', fromAddress, signalType, data); + } + } + }); + + dispatcher.on('archive#created', function(archive) { + parseAndAddArchiveToSession(archive, session); + }); - if(sessionId == null) { - sessionId = apiKey; - apiKey = null; - } + dispatcher.on('archive#updated', function(archiveId, update) { + var archive = session.archives.get(archiveId); + + if (!archive) { + OT.error('OT.Raptor.dispatch: An archive does not exist with the id of ' + + archiveId + ', for archive#updated message!'); + // @todo error + return; + } - var session = OT.sessions.get(sessionId); + archive._.update(update); + }); - if (!session) { - session = new OT.Session(apiKey, sessionId); - OT.sessions.add(session); - } + return dispatcher; - return session; }; -/** -*

    -* Initializes and returns a Publisher object. You can then pass this Publisher -* object to Session.publish() to publish a stream to a session. -*

    -*

    -* Note: If you intend to reuse a Publisher object created using -* OT.initPublisher() to publish to different sessions sequentially, -* call either Session.disconnect() or Session.unpublish(). -* Do not call both. Then call the preventDefault() method of the -* streamDestroyed or sessionDisconnected event object to prevent the -* Publisher object from being removed from the page. -*

    -* -* @param {Object} targetElement (Optional) The DOM element or the id attribute of the -* existing DOM element used to determine the location of the Publisher video in the HTML DOM. See -* the insertMode property of the properties parameter. If you do not -* specify a targetElement, the application appends a new DOM element to the HTML -* body. -* -*

    -* The application throws an error if an element with an ID set to the -* targetElement value does not exist in the HTML DOM. -*

    -* -* @param {Object} properties (Optional) This object contains the following properties (each of which -* are optional): -*

    -*
      -*
    • -* audioSource (String) — The ID of the audio input device (such as a -* microphone) to be used by the publisher. You can obtain a list of available devices, including -* audio input devices, by calling the OT.getDevices() method. Each -* device listed by the method has a unique device ID. If you pass in a device ID that does not -* match an existing audio input device, the call to OT.initPublisher() fails with an -* error (error code 1500, "Unable to Publish") passed to the completion handler function. -*
    • -*
    • -* frameRate (Number) — The desired frame rate, in frames per second, -* of the video. Valid values are 30, 15, 7, and 1. The published stream will use the closest -* value supported on the publishing client. The frame rate can differ slightly from the value -* you set, depending on the browser of the client. And the video will only use the desired -* frame rate if the client configuration supports it. -*

      If the publisher specifies a frame rate, the actual frame rate of the video stream -* is set as the frameRate property of the Stream object, though the actual frame rate -* will vary based on changing network and system conditions. If the developer does not specify a -* frame rate, this property is undefined. -*

      -* For sessions that use the OpenTok Media Router (sessions with -* the media mode -* set to routed, lowering the frame rate or lowering the resolution reduces -* the maximum bandwidth the stream can use. However, in sessions with the media mode set to -* relayed, lowering the frame rate or resolution may not reduce the stream's bandwidth. -*

      -*

      -* You can also restrict the frame rate of a Subscriber's video stream. To restrict the frame rate -* a Subscriber, call the restrictFrameRate() method of the subscriber, passing in -* true. -* (See Subscriber.restrictFrameRate().) -*

      -*
    • -*
    • -* height (Number) — The desired height, in pixels, of the -* displayed Publisher video stream (default: 198). Note: Use the -* height and width properties to set the dimensions -* of the publisher video; do not set the height and width of the DOM element -* (using CSS). -*
    • -*
    • -* insertMode (String) — Specifies how the Publisher object will be -* inserted in the HTML DOM. See the targetElement parameter. This string can -* have the following values: -*
        -*
      • "replace" — The Publisher object replaces contents of the -* targetElement. This is the default.
      • -*
      • "after" — The Publisher object is a new element inserted after -* the targetElement in the HTML DOM. (Both the Publisher and targetElement have the -* same parent element.)
      • -*
      • "before" — The Publisher object is a new element inserted before -* the targetElement in the HTML DOM. (Both the Publisher and targetElement have the same -* parent element.)
      • -*
      • "append" — The Publisher object is a new element added as a child -* of the targetElement. If there are other child elements, the Publisher is appended as -* the last child element of the targetElement.
      • -*
      -*
    • -*
    • -* mirror (Boolean) — Whether the publisher's video image -* is mirrored in the publisher's page<. The default value is true -* (the video image is mirrored). This property does not affect the display -* on other subscribers' web pages. -*
    • -*
    • -* name (String) — The name for this stream. The name appears at -* the bottom of Subscriber videos. The default value is "" (an empty string). Setting -* this to a string longer than 1000 characters results in an runtime exception. -*
    • -*
    • -* publishAudio (Boolean) — Whether to initially publish audio -* for the stream (default: true). This setting applies when you pass -* the Publisher object in a call to the Session.publish() method. -*
    • -*
    • -* publishVideo (Boolean) — Whether to initially publish video -* for the stream (default: true). This setting applies when you pass -* the Publisher object in a call to the Session.publish() method. -*
    • -*
    • -* resolution (String) — The desired resolution of the video. The format -* of the string is "widthxheight", where the width and height are represented in -* pixels. Valid values are "1280x720", "640x480", and -* "320x240". The published video will only use the desired resolution if the -* client configuration supports it. -*

      -* The requested resolution of a video stream is set as the videoDimensions.width and -* videoDimensions.height properties of the Stream object. -*

      -*

      -* The default resolution for a stream (if you do not specify a resolution) is 640x480 pixels. -* If the client system cannot support the resolution you requested, the the stream will use the -* next largest setting supported. -*

      -*

      -* For sessions that use the OpenTok Media Router (sessions with the -* media mode -* set to routed, lowering the frame rate or lowering the resolution reduces the maximum bandwidth -* the stream can use. However, in sessions that have the media mode set to relayed, lowering the -* frame rate or resolution may not reduce the stream's bandwidth. -*

      -*
    • -*
    • -* style (Object) — An object containing properties that define the initial -* appearance of user interface controls of the Publisher. The style object includes -* the following properties: -*
        -*
      • audioLevelDisplayMode (String) — How to display the audio level -* indicator. Possible values are: "auto" (the indicator is displayed when the -* video is disabled), "off" (the indicator is not displayed), and -* "on" (the indicator is always displayed).
      • -* -*
      • backgroundImageURI (String) — A URI for an image to display as -* the background image when a video is not displayed. (A video may not be displayed if -* you call publishVideo(false) on the Publisher object). You can pass an http -* or https URI to a PNG, JPEG, or non-animated GIF file location. You can also use the -* data URI scheme (instead of http or https) and pass in base-64-encrypted -* PNG data, such as that obtained from the -* Publisher.getImgData() method. For example, -* you could set the property to "data:VBORw0KGgoAA...", where the portion of the -* string after "data:" is the result of a call to -* Publisher.getImgData(). If the URL or the image data is invalid, the property -* is ignored (the attempt to set the image fails silently). -*

        -* Note that in Internet Explorer 8 (using the OpenTok Plugin for Internet Explorer), -* you cannot set the backgroundImageURI style to a string larger than 32 kB. -* This is due to an IE 8 limitation on the size of URI strings. Due to this limitation, -* you cannot set the backgroundImageURI style to a string obtained with the -* getImgData() method. -*

      • -* -*
      • buttonDisplayMode (String) — How to display the microphone controls -* Possible values are: "auto" (controls are displayed when the stream is first -* displayed and when the user mouses over the display), "off" (controls are not -* displayed), and "on" (controls are always displayed).
      • -* -*
      • nameDisplayMode (String) — Whether to display the stream name. -* Possible values are: "auto" (the name is displayed when the stream is first -* displayed and when the user mouses over the display), "off" (the name is not -* displayed), and "on" (the name is always displayed).
      • -*
      -*
    • -*
    • -* videoSource (String) — The ID of the video input device (such as a -* camera) to be used by the publisher. You can obtain a list of available devices, including -* video input devices, by calling the OT.getDevices() method. Each -* device listed by the method has a unique device ID. If you pass in a device ID that does not -* match an existing video input device, the call to OT.initPublisher() fails with an -* error (error code 1500, "Unable to Publish") passed to the completion handler function. -*
    • -*
    • -* width (Number) — The desired width, in pixels, of the -* displayed Publisher video stream (default: 264). Note: Use the -* height and width properties to set the dimensions -* of the publisher video; do not set the height and width of the DOM element -* (using CSS). -*
    • -*
    -* @param {Function} completionHandler (Optional) A function to be called when the method succeeds -* or fails in initializing a Publisher object. This function takes one parameter — -* error. On success, the error object is set to null. On -* failure, the error object has two properties: code (an integer) and -* message (a string), which identify the cause of the failure. The method succeeds -* when the user grants access to the camera and microphone. The method fails if the user denies -* access to the camera and microphone. The completionHandler function is called -* before the Publisher dispatches an accessAllowed (success) event or an -* accessDenied (failure) event. -*

    -* The following code adds a completionHandler when calling the -* OT.initPublisher() method: -*

    -*
    -* var publisher = OT.initPublisher('publisher', null, function (error) {
    -*   if (error) {
    -*     console.log(error);
    -*   } else {
    -*     console.log("Publisher initialized.");
    -*   }
    -* });
    -* 
    -* -* @returns {Publisher} The Publisher object. -* @see for audio input - * devices or "videoInput" for video input devices. - *

    - * The deviceId property is a unique ID for the device. You can pass - * the deviceId in as the audioSource or videoSource - * property of the the options parameter of the - * OT.initPublisher() method. - *

    - * The label property identifies the device. The label - * property is set to an empty string if the user has not previously granted access to - * a camera and microphone. In HTTP, the user must have granted access to a camera and - * microphone in the current page (for example, in response to a call to - * OT.initPublisher()). In HTTPS, the user must have previously granted access - * to the camera and microphone in the current page or in a page previously loaded from the - * domain. - * - * - * @see OT.initPublisher() - * @method OT.getDevices - * @memberof OT - */ - OT.getDevices = function(callback) { - OT.$.getMediaDevices(callback); - }; + return Promise.race([ + downloadPromise, + timeout(_httpConfig.duration * 1000) + ]) + .then(function() { + xhr.abort(); + return { + byteDownloaded: loadedLength, + duration: OT.$.now() - startTs + }; + }); + } + function doUpload() { + var payload = new Array(_httpConfig.uploadSize * _httpConfig.uploadCount).join('a'); -/** -* Checks if the system supports OpenTok for WebRTC. -* @return {Number} Whether the system supports OpenTok for WebRTC (1) or not (0). -* @see OT.upgradeSystemRequirements() -* @method OT.checkSystemRequirements -* @memberof OT -*/ - OT.checkSystemRequirements = function() { - OT.debug('OT.checkSystemRequirements()'); + var xhr; + var startTs; + var loadedLength = 0; + var uploadPromise = new Promise(function(resolve, reject) { + xhr = otRequest(_httpConfig.uploadUrl, {method: 'post'}, function(error) { + if (error) { + reject(new OT.$.Error('Connection to the HTTP server failed (' + + error.target.status + ')', 1006)); + } else { + resolve(); + } + }); - // Try native support first, then TBPlugin... - var systemRequirementsMet = OT.$.hasCapabilities('websockets', 'webrtc') || - TBPlugin.isInstalled(); - - systemRequirementsMet = systemRequirementsMet ? - this.HAS_REQUIREMENTS : this.NOT_HAS_REQUIREMENTS; - - OT.checkSystemRequirements = function() { - OT.debug('OT.checkSystemRequirements()'); - return systemRequirementsMet; - }; - - if(systemRequirementsMet === this.NOT_HAS_REQUIREMENTS) { - _analytics.logEvent({ - action: 'checkSystemRequirements', - variation: 'notHasRequirements', - 'payload_type': 'userAgent', - 'partner_id': OT.APIKEY, - payload: OT.$.userAgent() + xhr.upload.addEventListener('loadstart', function() { + startTs = OT.$.now(); + }); + xhr.upload.addEventListener('progress', function(evt) { + loadedLength = evt.loaded; }); - } - return systemRequirementsMet; - }; + xhr.send(payload); + }); + return Promise.race([ + uploadPromise, + timeout(_httpConfig.duration * 1000) + ]) + .then(function() { + xhr.abort(); + return { + byteUploaded: loadedLength, + duration: OT.$.now() - startTs + }; + }); + } -/** -* Displays information about system requirments for OpenTok for WebRTC. This -* information is displayed in an iframe element that fills the browser window. -*

    -* Note: this information is displayed automatically when you call the -* OT.initSession() or the OT.initPublisher() method -* if the client does not support OpenTok for WebRTC. -*

    -* @see OT.checkSystemRequirements() -* @method OT.upgradeSystemRequirements -* @memberof OT -*/ - OT.upgradeSystemRequirements = function(){ - // trigger after the OT environment has loaded - OT.onLoad( function() { - - if(TBPlugin.isSupported()) { - OT.Dialogs.Plugin.promptToInstall().on({ - download: function() { - window.location = TBPlugin.pathToInstaller(); - }, - refresh: function() { - location.reload(); - }, - closed: function() {} - }); - return; - } + return Promise.all([doDownload(), doUpload()]) + .then(function(values) { + var downloadStats = values[0]; + var uploadStats = values[1]; - var id = '_upgradeFlash'; + return { + downloadBandwidth: 1000 * (downloadStats.byteDownloaded * 8) / downloadStats.duration, + uploadBandwidth: 1000 * (uploadStats.byteUploaded * 8) / uploadStats.duration + }; + }); +} - // Load the iframe over the whole page. - document.body.appendChild((function() { - var d = document.createElement('iframe'); - d.id = id; - d.style.position = 'absolute'; - d.style.position = 'fixed'; - d.style.height = '100%'; - d.style.width = '100%'; - d.style.top = '0px'; - d.style.left = '0px'; - d.style.right = '0px'; - d.style.bottom = '0px'; - d.style.zIndex = 1000; - try { - d.style.backgroundColor = 'rgba(0,0,0,0.2)'; - } catch (err) { - // Old IE browsers don't support rgba and we still want to show the upgrade message - // but we just make the background of the iframe completely transparent. - d.style.backgroundColor = 'transparent'; - d.setAttribute('allowTransparency', 'true'); - } - d.setAttribute('frameBorder', '0'); - d.frameBorder = '0'; - d.scrolling = 'no'; - d.setAttribute('scrolling', 'no'); +OT.httpTest = httpTest; - var browser = OT.$.browserVersion(), - minimumBrowserVersion = OT.properties.minimumVersion[browser.browser.toLowerCase()], - isSupportedButOld = minimumBrowserVersion > browser.version; - d.src = OT.properties.assetURL + '/html/upgrade.html#' + - encodeURIComponent(isSupportedButOld ? 'true' : 'false') + ',' + - encodeURIComponent(JSON.stringify(OT.properties.minimumVersion)) + '|' + - encodeURIComponent(document.location.href); +// tb_require('../../helpers/helpers.js') - return d; - })()); +/* exported SDPHelpers */ - // Now we need to listen to the event handler if the user closes this dialog. - // Since this is from an IFRAME within another domain we are going to listen to hash - // changes. The best cross browser solution is to poll for a change in the hashtag. - if (_intervalId) clearInterval(_intervalId); - _intervalId = setInterval(function(){ - var hash = document.location.hash, - re = /^#?\d+&/; - if (hash !== _lastHash && re.test(hash)) { - _lastHash = hash; - if (hash.replace(re, '') === 'close_window'){ - document.body.removeChild(document.getElementById(id)); - document.location.hash = ''; - } - } - }, 100); +// Here are the structure of the rtpmap attribute and the media line, most of the +// complex Regular Expressions in this code are matching against one of these two +// formats: +// * a=rtpmap: / [/] +// * m= / +// +// References: +// * https://tools.ietf.org/html/rfc4566 +// * http://en.wikipedia.org/wiki/Session_Description_Protocol +// +var SDPHelpers = { + // Search through sdpLines to find the Media Line of type +mediaType+. + getMLineIndex: function getMLineIndex(sdpLines, mediaType) { + var targetMLine = 'm=' + mediaType; + + // Find the index of the media line for +type+ + return OT.$.findIndex(sdpLines, function(line) { + if (line.indexOf(targetMLine) !== -1) { + return true; + } + + return false; }); - }; + }, + // Extract the payload types for a give Media Line. + // + getMLinePayloadTypes: function getMLinePayloadTypes (mediaLine, mediaType) { + var mLineSelector = new RegExp('^m=' + mediaType + + ' \\d+(/\\d+)? [a-zA-Z0-9/]+(( [a-zA-Z0-9/]+)+)$', 'i'); + + // Get all payload types that the line supports + var payloadTypes = mediaLine.match(mLineSelector); + if (!payloadTypes || payloadTypes.length < 2) { + // Error, invalid M line? + return []; + } - OT.reportIssue = function(){ - OT.warn('ToDo: haven\'t yet implemented OT.reportIssue'); - }; + return OT.$.trim(payloadTypes[2]).split(' '); + }, - OT.components = {}; - OT.sessions = {}; + removeTypesFromMLine: function removeTypesFromMLine (mediaLine, payloadTypes) { + return OT.$.trim( + mediaLine.replace(new RegExp(' ' + payloadTypes.join(' |'), 'ig') , ' ') + .replace(/\s+/g, ' ') ); + }, - // namespaces - OT.rtc = {}; -// Define the APIKEY this is a global parameter which should not change - OT.APIKEY = (function(){ - // Script embed - var scriptSrc = (function(){ - var s = document.getElementsByTagName('script'); - s = s[s.length - 1]; - s = s.getAttribute('src') || s.src; - return s; - })(); + // Remove all references to a particular encodingName from a particular media type + // + removeMediaEncoding: function removeMediaEncoding (sdp, mediaType, encodingName) { + var sdpLines = sdp.split('\r\n'), + mLineIndex = SDPHelpers.getMLineIndex(sdpLines, mediaType), + mLine = mLineIndex > -1 ? sdpLines[mLineIndex] : void 0, + typesToRemove = [], + payloadTypes, + match; - var m = scriptSrc.match(/[\?\&]apikey=([^&]+)/i); - return m ? m[1] : ''; - })(); + if (mLineIndex === -1) { + // Error, missing M line + return sdpLines.join('\r\n'); + } - OT.HAS_REQUIREMENTS = 1; - OT.NOT_HAS_REQUIREMENTS = 0; + // Get all payload types that the line supports + payloadTypes = SDPHelpers.getMLinePayloadTypes(mLine, mediaType); + if (payloadTypes.length === 0) { + // Error, invalid M line? + return sdpLines.join('\r\n'); + } -/** -* This method is deprecated. Use on() or once() instead. -* -*

    -* Registers a method as an event listener for a specific event. -*

    -* -*

    -* The OT object dispatches one type of event — an exception event. The -* following code adds an event listener for the exception event: -*

    -* -*
    -* OT.addEventListener("exception", exceptionHandler);
    -*
    -* function exceptionHandler(event) {
    -*    alert("exception event. \n  code == " + event.code + "\n  message == " + event.message);
    -* }
    -* 
    -* -*

    -* If a handler is not registered for an event, the event is ignored locally. If the event -* listener function does not exist, the event is ignored locally. -*

    -*

    -* Throws an exception if the listener name is invalid. -*

    -* -* @param {String} type The string identifying the type of event. -* -* @param {Function} listener The function to be invoked when the OT object dispatches the event. -* @see on() -* @see once() -* @memberof OT -* @method addEventListener -*/ + // Find the location of all the rtpmap lines that relate to +encodingName+ + // and any of the supported payload types + var matcher = new RegExp('a=rtpmap:(' + payloadTypes.join('|') + ') ' + + encodingName + '\\/\\d+', 'i'); -/** -* This method is deprecated. Use off() instead. -* -*

    -* Removes an event listener for a specific event. -*

    -* -*

    -* Throws an exception if the listener name is invalid. -*

    -* -* @param {String} type The string identifying the type of event. -* -* @param {Function} listener The event listener function to remove. -* -* @see off() -* @memberof OT -* @method removeEventListener -*/ + sdpLines = OT.$.filter(sdpLines, function(line, index) { + match = line.match(matcher); + if (match === null) return true; + typesToRemove.push(match[1]); -/** -* Adds an event handler function for one or more events. -* -*

    -* The OT object dispatches one type of event — an exception event. The following -* code adds an event -* listener for the exception event: -*

    -* -*
    -* OT.on("exception", function (event) {
    -*   // This is the event handler.
    -* });
    -* 
    -* -*

    You can also pass in a third context parameter (which is optional) to define the -* value of -* this in the handler method:

    -* -*
    -* OT.on("exception",
    -*   function (event) {
    -*     // This is the event handler.
    -*   }),
    -*   session
    -* );
    -* 
    -* -*

    -* If you do not add a handler for an event, the event is ignored locally. -*

    -* -* @param {String} type The string identifying the type of event. -* @param {Function} handler The handler function to process the event. This function takes the event -* object as a parameter. -* @param {Object} context (Optional) Defines the value of this in the event handler -* function. -* -* @memberof OT -* @method on -* @see off() -* @see once() -* @see Events -*/ + if (index < mLineIndex) { + // This removal changed the index of the mline, track it + mLineIndex--; + } -/** -* Adds an event handler function for an event. Once the handler is called, the specified handler -* method is -* removed as a handler for this event. (When you use the OT.on() method to add an event -* handler, the handler -* is not removed when it is called.) The OT.once() method is the equivilent of -* calling the OT.on() -* method and calling OT.off() the first time the handler is invoked. -* -*

    -* The following code adds a one-time event handler for the exception event: -*

    -* -*
    -* OT.once("exception", function (event) {
    -*   console.log(event);
    -* }
    -* 
    -* -*

    You can also pass in a third context parameter (which is optional) to define the -* value of -* this in the handler method:

    -* -*
    -* OT.once("exception",
    -*   function (event) {
    -*     // This is the event handler.
    -*   },
    -*   session
    -* );
    -* 
    -* -*

    -* The method also supports an alternate syntax, in which the first parameter is an object that is a -* hash map of -* event names and handler functions and the second parameter (optional) is the context for this in -* each handler: -*

    -*
    -* OT.once(
    -*   {exeption: function (event) {
    -*     // This is the event handler.
    -*     }
    -*   },
    -*   session
    -* );
    -* 
    -* -* @param {String} type The string identifying the type of event. You can specify multiple event -* names in this string, -* separating them with a space. The event handler will process the first occurence of the events. -* After the first event, -* the handler is removed (for all specified events). -* @param {Function} handler The handler function to process the event. This function takes the event -* object as a parameter. -* @param {Object} context (Optional) Defines the value of this in the event handler -* function. -* -* @memberof OT -* @method once -* @see on() -* @see once() -* @see Events -*/ + // remove this one + return false; + }); + + if (typesToRemove.length > 0 && mLineIndex > -1) { + // Remove all the payload types and we've removed from the media line + sdpLines[mLineIndex] = SDPHelpers.removeTypesFromMLine(mLine, typesToRemove); + } + + return sdpLines.join('\r\n'); + }, + + // Removes all Confort Noise from +sdp+. + // + // See https://jira.tokbox.com/browse/OPENTOK-7176 + // + removeComfortNoise: function removeComfortNoise (sdp) { + return SDPHelpers.removeMediaEncoding(sdp, 'audio', 'CN'); + }, + removeVideoCodec: function removeVideoCodec (sdp, codec) { + return SDPHelpers.removeMediaEncoding(sdp, 'video', codec); + } +}; -/** -* Removes an event handler. -* -*

    Pass in an event name and a handler method, the handler is removed for that event:

    -* -*
    OT.off("exceptionEvent", exceptionEventHandler);
    -* -*

    If you pass in an event name and no handler method, all handlers are removed for that -* events:

    -* -*
    OT.off("exceptionEvent");
    -* -*

    -* The method also supports an alternate syntax, in which the first parameter is an object that is a -* hash map of -* event names and handler functions and the second parameter (optional) is the context for matching -* handlers: -*

    -*
    -* OT.off(
    -*   {
    -*     exceptionEvent: exceptionEventHandler
    -*   },
    -*   this
    -* );
    -* 
    -* -* @param {String} type (Optional) The string identifying the type of event. You can use a space to -* specify multiple events, as in "eventName1 eventName2 eventName3". If you pass in no -* type value (or other arguments), all event handlers are removed for the object. -* @param {Function} handler (Optional) The event handler function to remove. If you pass in no -* handler, all event handlers are removed for the specified event type. -* @param {Object} context (Optional) If you specify a context, the event handler is -* removed for all specified events and handlers that use the specified context. -* -* @memberof OT -* @method off -* @see on() -* @see once() -* @see Events -*/ + + +// tb_require('../../helpers/helpers.js') + +function isVideoStat(stat) { + // Chrome implementation only has this property for RTP video stat + return stat.hasOwnProperty('googFrameWidthReceived') || + stat.hasOwnProperty('googFrameWidthInput') || + stat.mediaType === 'video'; +} + +function isAudioStat(stat) { + // Chrome implementation only has this property for RTP audio stat + return stat.hasOwnProperty('audioInputLevel') || + stat.hasOwnProperty('audioOutputLevel') || + stat.mediaType === 'audio'; +} + +function isInboundStat(stat) { + return stat.hasOwnProperty('bytesReceived'); +} + +function parseStatCategory(stat) { + var statCategory = { + packetsLost: 0, + packetsReceived: 0, + bytesReceived: 0 + }; + + if (stat.hasOwnProperty('packetsReceived')) { + statCategory.packetsReceived = parseInt(stat.packetsReceived, 10); + } + if (stat.hasOwnProperty('packetsLost')) { + statCategory.packetsLost = parseInt(stat.packetsLost, 10); + } + if (stat.hasOwnProperty('bytesReceived')) { + statCategory.bytesReceived += parseInt(stat.bytesReceived, 10); + } + + return statCategory; +} + +function normalizeTimestamp(timestamp) { + if (OT.$.isObject(timestamp) && 'getTime' in timestamp) { + // Chrome as of 39 delivers a "kind of Date" object for timestamps + // we duck check it and get the timestamp + return timestamp.getTime(); + } else { + return timestamp; + } +} + +var getStatsHelpers = {}; +getStatsHelpers.isVideoStat = isVideoStat; +getStatsHelpers.isAudioStat = isAudioStat; +getStatsHelpers.isInboundStat = isInboundStat; +getStatsHelpers.parseStatCategory = parseStatCategory; +getStatsHelpers.normalizeTimestamp = normalizeTimestamp; + +OT.getStatsHelpers = getStatsHelpers; + +// tb_require('../../helpers/helpers.js') /** - * Dispatched by the OT class when the app encounters an exception. - * Note that you set up an event handler for the exception event by calling the - * OT.on() method. * - * @name exception - * @event - * @borrows ExceptionEvent#message as this.message - * @memberof OT - * @see ExceptionEvent + * @returns {function(RTCPeerConnection, + * function(DOMError, Array.<{id: string=, type: string=, timestamp: number}>))} */ +function getStatsAdapter() { - if (!window.OT) window.OT = OT; - if (!window.TB) window.TB = OT; + /// +// Get Stats using the older API. Used by all current versions +// of Chrome. +// + function getStatsOldAPI(peerConnection, completion) { -})(window); -!(function() { + peerConnection.getStats(function(rtcStatsReport) { - OT.Collection = function(idField) { - var _models = [], - _byId = {}, - _idField = idField || 'id'; - - OT.$.eventing(this, true); - - var modelProperty = function(model, property) { - if(OT.$.isFunction(model[property])) { - return model[property](); - } else { - return model[property]; - } - }; + var stats = []; + rtcStatsReport.result().forEach(function(rtcStat) { - var onModelUpdate = OT.$.bind(function onModelUpdate (event) { - this.trigger('update', event); - this.trigger('update:'+event.target.id, event); - }, this), + var stat = {}; - onModelDestroy = OT.$.bind(function onModelDestroyed (event) { - this.remove(event.target, event.reason); - }, this); + rtcStat.names().forEach(function(name) { + stat[name] = rtcStat.stat(name); + }); + // fake the structure of the "new" RTC stat object + stat.id = rtcStat.id; + stat.type = rtcStat.type; + stat.timestamp = rtcStat.timestamp; + stats.push(stat); + }); - this.reset = function() { - // Stop listening on the models, they are no longer our problem - OT.$.forEach(_models, function(model) { - model.off('updated', onModelUpdate, this); - model.off('destroyed', onModelDestroy, this); - }, this); + completion(null, stats); + }); + } - _models = []; - _byId = {}; - }; +/// +// Get Stats using the newer API. +// + function getStatsNewAPI(peerConnection, completion) { - this.destroy = function(reason) { - OT.$.forEach(_models, function(model) { - if(model && typeof model.destroy === 'function') { - model.destroy(reason, true); - } - }); + peerConnection.getStats(null, function(rtcStatsReport) { - this.reset(); - this.off(); - }; + var stats = []; + rtcStatsReport.forEach(function(rtcStats) { + stats.push(rtcStats); + }); - this.get = function(id) { return id && _byId[id] !== void 0 ? _models[_byId[id]] : void 0; }; - this.has = function(id) { return id && _byId[id] !== void 0; }; + completion(null, stats); + }, completion); + } - this.toString = function() { return _models.toString(); }; + if (OT.$.browserVersion().name === 'Firefox' || OTPlugin.isInstalled()) { + return getStatsNewAPI; + } else { + return getStatsOldAPI; + } +} - // Return only models filtered by either a dict of properties - // or a filter function. - // - // @example Return all publishers with a streamId of 1 - // OT.publishers.where({streamId: 1}) - // - // @example The same thing but filtering using a filter function - // OT.publishers.where(function(publisher) { - // return publisher.stream.id === 4; - // }); - // - // @example The same thing but filtering using a filter function - // executed with a specific this - // OT.publishers.where(function(publisher) { - // return publisher.stream.id === 4; - // }, self); - // - this.where = function(attrsOrFilterFn, context) { - if (OT.$.isFunction(attrsOrFilterFn)) return OT.$.filter(_models, attrsOrFilterFn, context); +OT.getStatsAdpater = getStatsAdapter; - return OT.$.filter(_models, function(model) { - for (var key in attrsOrFilterFn) { - if(!attrsOrFilterFn.hasOwnProperty(key)) { - continue; - } - if (modelProperty(model, key) !== attrsOrFilterFn[key]) return false; - } +// tb_require('../../helpers/helpers.js') +// tb_require('../peer_connection/get_stats_adapter.js') +// tb_require('../peer_connection/get_stats_helpers.js') - return true; - }); - }; +/* global OT, Promise */ - // Similar to where in behaviour, except that it only returns - // the first match. - this.find = function(attrsOrFilterFn, context) { - var filterFn; +/** + * @returns {Promise.<{packetLostRation: number, roundTripTime: number}>} + */ +function webrtcTest(config) { - if (OT.$.isFunction(attrsOrFilterFn)) { - filterFn = attrsOrFilterFn; - } - else { - filterFn = function(model) { - for (var key in attrsOrFilterFn) { - if(!attrsOrFilterFn.hasOwnProperty(key)) { - continue; - } - if (modelProperty(model, key) !== attrsOrFilterFn[key]) return false; - } + var _getStats = OT.getStatsAdpater(); + var _mediaConfig = config.mediaConfig; + var _localStream = config.localStream; - return true; - }; - } + // todo copied from peer_connection.js + // Normalise these + var NativeRTCSessionDescription, + NativeRTCIceCandidate; + if (!OTPlugin.isInstalled()) { + // order is very important: 'RTCSessionDescription' defined in Firefox Nighly but useless + NativeRTCSessionDescription = (window.mozRTCSessionDescription || + window.RTCSessionDescription); + NativeRTCIceCandidate = (window.mozRTCIceCandidate || window.RTCIceCandidate); + } + else { + NativeRTCSessionDescription = OTPlugin.RTCSessionDescription; + NativeRTCIceCandidate = OTPlugin.RTCIceCandidate; + } - filterFn = OT.$.bind(filterFn, context); - for (var i=0; i<_models.length; ++i) { - if (filterFn(_models[i]) === true) return _models[i]; - } + function isCandidateRelay(candidate) { + return candidate.candidate.indexOf('relay') !== -1; + } - return null; - }; + /** + * Create video a element attaches it to the body and put it visually outside the body. + * + * @returns {OT.VideoElement} + */ + function createVideoElementForTest() { + var videoElement = new OT.VideoElement({attributes: {muted: true}}); + videoElement.domElement().style.position = 'absolute'; + videoElement.domElement().style.top = '-9999%'; + videoElement.appendTo(document.body); + return videoElement; + } - this.add = function(model) { - var id = modelProperty(model, _idField); + function createPeerConnectionForTest() { + return new Promise(function(resolve, reject) { + OT.$.createPeerConnection({ + iceServers: _mediaConfig.iceServers + }, {}, + null, + function(error, pc) { + if (error) { + reject(new OT.$.Error('createPeerConnection failed', 1600, error)); + } else { + resolve(pc); + } + } + ); + }); + } - if (this.has(id)) { - OT.warn('Model ' + id + ' is already in the collection', _models); - return this; - } + function createOffer(pc) { + return new Promise(function(resolve, reject) { + pc.createOffer(resolve, reject); + }); + } - _byId[id] = _models.push(model) - 1; + function attachMediaStream(videoElement, webRtcStream) { + return new Promise(function(resolve, reject) { + videoElement.bindToStream(webRtcStream, function(error) { + if (error) { + reject(new OT.$.Error('bindToStream failed', 1600, error)); + } else { + resolve(); + } + }); + }); + } - model.on('updated', onModelUpdate, this); - model.on('destroyed', onModelDestroy, this); + function addIceCandidate(pc, candidate) { + return new Promise(function(resolve, reject) { + pc.addIceCandidate(new NativeRTCIceCandidate({ + sdpMLineIndex: candidate.sdpMLineIndex, + candidate: candidate.candidate + }), resolve, reject); + }); + } - this.trigger('add', model); - this.trigger('add:'+id, model); + function setLocalDescription(pc, offer) { + return new Promise(function(resolve, reject) { + pc.setLocalDescription(offer, resolve, function(error) { + reject(new OT.$.Error('setLocalDescription failed', 1600, error)); + }); + }); + } - return this; - }; + function setRemoteDescription(pc, offer) { + return new Promise(function(resolve, reject) { + pc.setRemoteDescription(offer, resolve, function(error) { + reject(new OT.$.Error('setRemoteDescription failed', 1600, error)); + }); + }); + } - this.remove = function(model, reason) { - var id = modelProperty(model, _idField); + function createAnswer(pc) { + return new Promise(function(resolve, reject) { + pc.createAnswer(resolve, function(error) { + reject(new OT.$.Error('createAnswer failed', 1600, error)); + }); + }); + } - _models.splice(_byId[id], 1); + function getStats(pc) { + return new Promise(function(resolve, reject) { + _getStats(pc, function(error, stats) { + if (error) { + reject(new OT.$.Error('geStats failed', 1600, error)); + } else { + resolve(stats); + } + }); + }); + } - // Shuffle everyone down one - for (var i=_byId[id]; i<_models.length; ++i) { - _byId[_models[i][_idField]] = i; + function createOnIceCandidateListener(pc) { + return function(event) { + if (event.candidate && isCandidateRelay(event.candidate)) { + addIceCandidate(pc, event.candidate)['catch'](function() { + OT.warn('An error occurred while adding a ICE candidate during webrtc test'); + }); } + }; + } - delete _byId[id]; + /** + * @returns {Promise.<{packetLostRation: number, roundTripTime: number}>} + */ + function collectPeerConnectionStats(localPc, remotePc) { - model.off('updated', onModelUpdate, this); - model.off('destroyed', onModelDestroy, this); + var SAMPLING_DELAY = 1000; - this.trigger('remove', model, reason); - this.trigger('remove:'+id, model, reason); + return new Promise(function(resolve) { - return this; - }; + var collectionActive = true; - // Used by session connecto fire add events after adding listeners - this._triggerAddEvents = function() { - var models = this.where.apply(this, arguments); - OT.$.forEach(models, function(model) { - this.trigger('add', model); - this.trigger('add:' + modelProperty(model, _idField), model); - }, this); - }; + var statsSamples = { + startTs: OT.$.now(), + packetLostRatioSamplesCount: 0, + packetLostRatio: 0, + roundTripTimeSamplesCount: 0, + roundTripTime: 0, + bytesReceived: 0 + }; - this.length = function() { - return _models.length; - }; - }; + function calculateBandwidth() { + return 1000 * statsSamples.bytesReceived * 8 / (OT.$.now() - statsSamples.startTs); + } -}(this)); -!(function() { + function sample() { - /** - * The Event object defines the basic OpenTok event object that is passed to - * event listeners. Other OpenTok event classes implement the properties and methods of - * the Event object.

    - * - *

    For example, the Stream object dispatches a streamPropertyChanged event when - * the stream's properties are updated. You add a callback for an event using the - * on() method of the Stream object:

    - * - *
    -   * stream.on("streamPropertyChanged", function (event) {
    -   *     alert("Properties changed for stream " + event.target.streamId);
    -   * });
    - * - * @class Event - * @property {Boolean} cancelable Whether the event has a default behavior that is cancelable - * (true) or not (false). You can cancel the default behavior by - * calling the preventDefault() method of the Event object in the callback - * function. (See preventDefault().) - * - * @property {Object} target The object that dispatched the event. - * - * @property {String} type The type of event. - */ - OT.Event = OT.$.eventing.Event(); - /** - * Prevents the default behavior associated with the event from taking place. - * - *

    To see whether an event has a default behavior, check the cancelable property - * of the event object.

    - * - *

    Call the preventDefault() method in the callback function for the event.

    - * - *

    The following events have default behaviors:

    - * - * - * - * @method #preventDefault - * @memberof Event - */ - /** - * Whether the default event behavior has been prevented via a call to - * preventDefault() (true) or not (false). - * See preventDefault(). - * @method #isDefaultPrevented - * @return {Boolean} - * @memberof Event - */ + Promise.all([ + getStats(localPc).then(function(stats) { + OT.$.forEach(stats, function(stat) { + if (OT.getStatsHelpers.isVideoStat(stat)) { + var rtt = null; + + if (stat.hasOwnProperty('googRtt')) { + rtt = parseInt(stat.googRtt, 10); + } else if (stat.hasOwnProperty('mozRtt')) { + rtt = stat.mozRtt; + } - // Event names lookup - OT.Event.names = { - // Activity Status for cams/mics - ACTIVE: 'active', - INACTIVE: 'inactive', - UNKNOWN: 'unknown', + if (rtt !== null && rtt > -1) { + statsSamples.roundTripTimeSamplesCount++; + statsSamples.roundTripTime += rtt; + } + } + }); + }), + + getStats(remotePc).then(function(stats) { + OT.$.forEach(stats, function(stat) { + if (OT.getStatsHelpers.isVideoStat(stat)) { + if (stat.hasOwnProperty('packetsReceived') && + stat.hasOwnProperty('packetsLost')) { + + var packetLost = parseInt(stat.packetsLost, 10); + var packetsReceived = parseInt(stat.packetsReceived, 10); + if (packetLost >= 0 && packetsReceived > 0) { + statsSamples.packetLostRatioSamplesCount++; + statsSamples.packetLostRatio += packetLost * 100 / packetsReceived; + } + } + + if (stat.hasOwnProperty('bytesReceived')) { + statsSamples.bytesReceived += parseInt(stat.bytesReceived, 10); + } + } + }); + }) + ]) + .then(function() { + // wait and trigger another round of collection + setTimeout(function() { + if (collectionActive) { + sample(); + } + }, SAMPLING_DELAY); + }); + } - // Archive types - PER_SESSION: 'perSession', - PER_STREAM: 'perStream', + // start the sampling "loop" + sample(); - // OT Events - EXCEPTION: 'exception', - ISSUE_REPORTED: 'issueReported', + function stopCollectStats() { + collectionActive = false; - // Session Events - SESSION_CONNECTED: 'sessionConnected', - SESSION_DISCONNECTED: 'sessionDisconnected', - STREAM_CREATED: 'streamCreated', - STREAM_DESTROYED: 'streamDestroyed', - CONNECTION_CREATED: 'connectionCreated', - CONNECTION_DESTROYED: 'connectionDestroyed', - SIGNAL: 'signal', - STREAM_PROPERTY_CHANGED: 'streamPropertyChanged', - MICROPHONE_LEVEL_CHANGED: 'microphoneLevelChanged', + var pcStats = { + packetLostRatio: statsSamples.packetLostRatioSamplesCount > 0 ? + statsSamples.packetLostRatio /= statsSamples.packetLostRatioSamplesCount * 100 : null, + roundTripTime: statsSamples.roundTripTimeSamplesCount > 0 ? + statsSamples.roundTripTime /= statsSamples.roundTripTimeSamplesCount : null, + bandwidth: calculateBandwidth() + }; + resolve(pcStats); + } - // Publisher Events - RESIZE: 'resize', - SETTINGS_BUTTON_CLICK: 'settingsButtonClick', - DEVICE_INACTIVE: 'deviceInactive', - INVALID_DEVICE_NAME: 'invalidDeviceName', - ACCESS_ALLOWED: 'accessAllowed', - ACCESS_DENIED: 'accessDenied', - ACCESS_DIALOG_OPENED: 'accessDialogOpened', - ACCESS_DIALOG_CLOSED: 'accessDialogClosed', - ECHO_CANCELLATION_MODE_CHANGED: 'echoCancellationModeChanged', - PUBLISHER_DESTROYED: 'destroyed', + // sample for the nominal delay + // if the bandwidth is bellow the threshold at the end we give an extra time + setTimeout(function() { - // Subscriber Events - SUBSCRIBER_DESTROYED: 'destroyed', + if (calculateBandwidth() < _mediaConfig.thresholdBitsPerSecond) { + // give an extra delay in case it was transient bandwidth problem + setTimeout(stopCollectStats, _mediaConfig.extendedDuration * 1000); + } else { + stopCollectStats(); + } - // DeviceManager Events - DEVICES_DETECTED: 'devicesDetected', + }, _mediaConfig.duration * 1000); + }); + } - // DevicePanel Events - DEVICES_SELECTED: 'devicesSelected', - CLOSE_BUTTON_CLICK: 'closeButtonClick', + return Promise + .all([createPeerConnectionForTest(), createPeerConnectionForTest()]) + .then(function(pcs) { - MICLEVEL : 'microphoneActivityLevel', - MICGAINCHANGED : 'microphoneGainChanged', + var localPc = pcs[0], + remotePc = pcs[1]; - // Environment Loader - ENV_LOADED: 'envLoaded', - ENV_UNLOADED: 'envUnloaded', + var localVideo = createVideoElementForTest(), + remoteVideo = createVideoElementForTest(); - // Audio activity Events - AUDIO_LEVEL_UPDATED: 'audioLevelUpdated' - }; + attachMediaStream(localVideo, _localStream); + localPc.addStream(_localStream); - OT.ExceptionCodes = { - JS_EXCEPTION: 2000, - AUTHENTICATION_ERROR: 1004, - INVALID_SESSION_ID: 1005, - CONNECT_FAILED: 1006, - CONNECT_REJECTED: 1007, - CONNECTION_TIMEOUT: 1008, - NOT_CONNECTED: 1010, - P2P_CONNECTION_FAILED: 1013, - API_RESPONSE_FAILURE: 1014, - UNABLE_TO_PUBLISH: 1500, - UNABLE_TO_SUBSCRIBE: 1501, - UNABLE_TO_FORCE_DISCONNECT: 1520, - UNABLE_TO_FORCE_UNPUBLISH: 1530 - }; + var remoteStream; + remotePc.onaddstream = function(evt) { + remoteStream = evt.stream; + attachMediaStream(remoteVideo, remoteStream); + }; - /** - * The {@link OT} class dispatches exception events when the OpenTok API encounters - * an exception (error). The ExceptionEvent object defines the properties of the event - * object that is dispatched. - * - *

    Note that you set up a callback for the exception event by calling the - * OT.on() method.

    - * - * @class ExceptionEvent - * @property {Number} code The error code. The following is a list of error codes:

    - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
    - * code - * - * - * title - *
    - * 1004 - * - * - * Authentication error - *
    - * 1005 - * - * - * Invalid Session ID - *
    - * 1006 - * - * - * Connect Failed - *
    - * 1007 - * - * - * Connect Rejected - *
    - * 1008 - * - * - * Connect Time-out - *
    - * 1009 - * - * - * Security Error - *
    - * 1010 - * - * - * Not Connected - *
    - * 1011 - * - * - * Invalid Parameter - *
    - * 1013 - * - * Connection Failed - *
    - * 1014 - * - * API Response Failure - *
    - * 1500 - * - * Unable to Publish - *
    - * 1520 - * - * Unable to Force Disconnect - *
    - * 1530 - * - * Unable to Force Unpublish - *
    - * 1535 - * - * Force Unpublish on Invalid Stream - *
    - * 2000 - * - * - * Internal Error - *
    - * 2010 - * - * - * Report Issue Failure - *
    - * - *

    Check the message property for more details about the error.

    - * - * @property {String} message The error message. - * - * @property {Object} target The object that the event pertains to. For an - * exception event, this will be an object other than the OT object - * (such as a Session object or a Publisher object). - * - * @property {String} title The error title. - * @augments Event - */ - OT.ExceptionEvent = function (type, message, title, code, component, target) { - OT.Event.call(this, type); + localPc.onicecandidate = createOnIceCandidateListener(remotePc); + remotePc.onicecandidate = createOnIceCandidateListener(localPc); + + function dispose() { + localVideo.destroy(); + remoteVideo.destroy(); + localPc.close(); + remotePc.close(); + } + + return createOffer(localPc) + .then(function(offer) { + return Promise.all([ + setLocalDescription(localPc, offer), + setRemoteDescription(remotePc, offer) + ]); + }) + .then(function() { + return createAnswer(remotePc); + }) + .then(function(answer) { + return Promise.all([ + setLocalDescription(remotePc, answer), + setRemoteDescription(localPc, answer) + ]); + }) + .then(function() { + return collectPeerConnectionStats(localPc, remotePc); + }) + .then(function(value) { + dispose(); + return value; + }, function(error) { + dispose(); + throw error; + }); + }); +} + +OT.webrtcTest = webrtcTest; + +// tb_require('../../helpers/helpers.js') - this.message = message; - this.title = title; - this.code = code; - this.component = component; - this.target = target; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ + +// Manages N Chrome elements +OT.Chrome = function(properties) { + var _visible = false, + _widgets = {}, + + // Private helper function + _set = function(name, widget) { + widget.parent = this; + widget.appendTo(properties.parent); + + _widgets[name] = widget; + + this[name] = widget; + }; + + if (!properties.parent) { + // @todo raise an exception + return; + } + + OT.$.eventing(this); + + this.destroy = function() { + this.off(); + this.hideWhileLoading(); + + for (var name in _widgets) { + _widgets[name].destroy(); + } }; + this.showAfterLoading = function() { + _visible = true; - OT.IssueReportedEvent = function (type, issueId) { - OT.Event.call(this, type); - this.issueId = issueId; + for (var name in _widgets) { + _widgets[name].showAfterLoading(); + } }; - // Triggered when the JS dynamic config and the DOM have loaded. - OT.EnvLoadedEvent = function (type) { - OT.Event.call(this, type); + this.hideWhileLoading = function() { + _visible = false; + + for (var name in _widgets) { + _widgets[name].hideWhileLoading(); + } }; -/** - * Dispatched by the Session object when a client connects to or disconnects from a {@link Session}. - * For the local client, the Session object dispatches a "sessionConnected" or "sessionDisconnected" - * event, defined by the {@link SessionConnectEvent} and {@link SessionDisconnectEvent} classes. - * - *
    Example
    - * - *

    The following code keeps a running total of the number of connections to a session - * by monitoring the connections property of the sessionConnect, - * connectionCreated and connectionDestroyed events:

    - * - *
    var apiKey = ""; // Replace with your API key. See https://dashboard.tokbox.com/projects
    - * var sessionID = ""; // Replace with your own session ID.
    - *                     // See https://dashboard.tokbox.com/projects
    - * var token = ""; // Replace with a generated token that has been assigned the moderator role.
    - *                 // See https://dashboard.tokbox.com/projects
    - * var connectionCount = 0;
    - *
    - * var session = OT.initSession(apiKey, sessionID);
    - * session.on("connectionCreated", function(event) {
    - *    connectionCount++;
    - *    displayConnectionCount();
    - * });
    - * session.on("connectionDestroyed", function(event) {
    - *    connectionCount--;
    - *    displayConnectionCount();
    - * });
    - * session.connect(token);
    - *
    - * function displayConnectionCount() {
    - *     document.getElementById("connectionCountField").value = connectionCount.toString();
    - * }
    - * - *

    This example assumes that there is an input text field in the HTML DOM - * with the id set to "connectionCountField":

    - * - *
    <input type="text" id="connectionCountField" value="0"></input>
    - * - * - * @property {Connection} connection A Connection objects for the connections that was - * created or deleted. - * - * @property {Array} connections Deprecated. Use the connection property. A - * connectionCreated or connectionDestroyed event is dispatched - * for each connection created and destroyed in the session. - * - * @property {String} reason For a connectionDestroyed event, - * a description of why the connection ended. This property can have two values: - *

    - *
      - *
    • "clientDisconnected" — A client disconnected from the session by calling - * the disconnect() method of the Session object or by closing the browser. - * (See Session.disconnect().)
    • - * - *
    • "forceDisconnected" — A moderator has disconnected the publisher - * from the session, by calling the forceDisconnect() method of the Session - * object. (See Session.forceDisconnect().)
    • - * - *
    • "networkDisconnected" — The network connection terminated abruptly - * (for example, the client lost their internet connection).
    • - *
    - * - *

    Depending on the context, this description may allow the developer to refine - * the course of action they take in response to an event.

    - * - *

    For a connectionCreated event, this string is undefined.

    - * - * @class ConnectionEvent - * @augments Event - */ - var connectionEventPluralDeprecationWarningShown = false; - OT.ConnectionEvent = function (type, connection, reason) { - OT.Event.call(this, type, false); + // Adds the widget to the chrome and to the DOM. Also creates a accessor + // property for it on the chrome. + // + // @example + // chrome.set('foo', new FooWidget()); + // chrome.foo.setDisplayMode('on'); + // + // @example + // chrome.set({ + // foo: new FooWidget(), + // bar: new BarWidget() + // }); + // chrome.foo.setDisplayMode('on'); + // + this.set = function(widgetName, widget) { + if (typeof(widgetName) === 'string' && widget) { + _set.call(this, widgetName, widget); - if (OT.$.canDefineProperty) { - Object.defineProperty(this, 'connections', { - get: function() { - if(!connectionEventPluralDeprecationWarningShown) { - OT.warn('OT.ConnectionEvent connections property is deprecated, ' + - 'use connection instead.'); - connectionEventPluralDeprecationWarningShown = true; - } - return [connection]; - } - }); } else { - this.connections = [connection]; + for (var name in widgetName) { + if (widgetName.hasOwnProperty(name)) { + _set.call(this, name, widgetName[name]); + } + } } + return this; + }; - this.connection = connection; - this.reason = reason; +}; + + +// tb_require('../../../helpers/helpers.js') +// tb_require('../chrome.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ + +if (!OT.Chrome.Behaviour) OT.Chrome.Behaviour = {}; + +// A mixin to encapsulate the basic widget behaviour. This needs a better name, +// it's not actually a widget. It's actually "Behaviour that can be applied to +// an object to make it support the basic Chrome widget workflow"...but that would +// probably been too long a name. +OT.Chrome.Behaviour.Widget = function(widget, options) { + var _options = options || {}, + _mode, + _previousOnMode = 'auto', + _loadingMode; + + // + // @param [String] mode + // 'on', 'off', or 'auto' + // + widget.setDisplayMode = function(mode) { + var newMode = mode || 'auto'; + if (_mode === newMode) return; + + OT.$.removeClass(this.domElement, 'OT_mode-' + _mode); + OT.$.addClass(this.domElement, 'OT_mode-' + newMode); + + if (newMode === 'off') { + _previousOnMode = _mode; + } + _mode = newMode; }; -/** - * StreamEvent is an event that can have the type "streamCreated" or "streamDestroyed". - * These events are dispatched by the Session object when another client starts or - * stops publishing a stream to a {@link Session}. For a local client's stream, the - * Publisher object dispatches the event. - * - *

    Example — streamCreated event dispatched - * by the Session object

    - *

    The following code initializes a session and sets up an event listener for when - * a stream published by another client is created:

    - * - *
    - * session.on("streamCreated", function(event) {
    - *   // streamContainer is a DOM element
    - *   subscriber = session.subscribe(event.stream, targetElement);
    - * }).connect(token);
    - * 
    - * - *

    Example — streamDestroyed event dispatched - * by the Session object

    - * - *

    The following code initializes a session and sets up an event listener for when - * other clients' streams end:

    - * - *
    - * session.on("streamDestroyed", function(event) {
    - *     console.log("Stream " + event.stream.name + " ended. " + event.reason);
    - * }).connect(token);
    - * 
    - * - *

    Example — streamCreated event dispatched - * by a Publisher object

    - *

    The following code publishes a stream and adds an event listener for when the streaming - * starts

    - * - *
    - * var publisher = session.publish(targetElement)
    - *   .on("streamCreated", function(event) {
    - *     console.log("Publisher started streaming.");
    - *   );
    - * 
    - * - *

    Example — streamDestroyed event - * dispatched by a Publisher object

    - * - *

    The following code publishes a stream, and leaves the Publisher in the HTML DOM - * when the streaming stops:

    - * - *
    - * var publisher = session.publish(targetElement)
    - *   .on("streamDestroyed", function(event) {
    - *     event.preventDefault();
    - *     console.log("Publisher stopped streaming.");
    - *   );
    - * 
    - * - * @class StreamEvent - * - * @property {Boolean} cancelable Whether the event has a default behavior that is cancelable - * (true) or not (false). You can cancel the default behavior by calling - * the preventDefault() method of the StreamEvent object in the event listener - * function. The streamDestroyed - * event is cancelable. (See preventDefault().) - * - * @property {String} reason For a streamDestroyed event, - * a description of why the session disconnected. This property can have one of the following - * values: - *

    - *
      - *
    • "clientDisconnected" — A client disconnected from the session by calling - * the disconnect() method of the Session object or by closing the browser. - * (See Session.disconnect().)
    • - * - *
    • "forceDisconnected" — A moderator has disconnected the publisher of the - * stream from the session, by calling the forceDisconnect() method of the Session -* object. (See Session.forceDisconnect().)
    • - * - *
    • "forceUnpublished" — A moderator has forced the publisher of the stream - * to stop publishing the stream, by calling the forceUnpublish() method of the - * Session object. (See Session.forceUnpublish().)
    • - * - *
    • "networkDisconnected" — The network connection terminated abruptly (for - * example, the client lost their internet connection).
    • - * - *
    - * - *

    Depending on the context, this description may allow the developer to refine - * the course of action they take in response to an event.

    - * - *

    For a streamCreated event, this string is undefined.

    - * - * @property {Stream} stream A Stream object corresponding to the stream that was added (in the - * case of a streamCreated event) or deleted (in the case of a - * streamDestroyed event). - * - * @property {Array} streams Deprecated. Use the stream property. A - * streamCreated or streamDestroyed event is dispatched for - * each stream added or destroyed. - * - * @augments Event - */ + widget.getDisplayMode = function() { + return _mode; + }; + + widget.show = function() { + if (_mode !== _previousOnMode) { + this.setDisplayMode(_previousOnMode); + if (_options.onShow) _options.onShow(); + } + return this; + }; + + widget.showAfterLoading = function() { + this.setDisplayMode(_loadingMode); + }; + + widget.hide = function() { + if (_mode !== 'off') { + this.setDisplayMode('off'); + if (_options.onHide) _options.onHide(); + } + return this; + }; + + widget.hideWhileLoading = function() { + _loadingMode = _mode; + this.setDisplayMode('off'); + }; + + widget.destroy = function() { + if (_options.onDestroy) _options.onDestroy(this.domElement); + if (this.domElement) OT.$.removeElement(this.domElement); + + return widget; + }; - var streamEventPluralDeprecationWarningShown = false; - OT.StreamEvent = function (type, stream, reason, cancelable) { - OT.Event.call(this, type, cancelable); + widget.appendTo = function(parent) { + // create the element under parent + this.domElement = OT.$.createElement(_options.nodeName || 'div', + _options.htmlAttributes, + _options.htmlContent); - if (OT.$.canDefineProperty) { - Object.defineProperty(this, 'streams', { - get: function() { - if(!streamEventPluralDeprecationWarningShown) { - OT.warn('OT.StreamEvent streams property is deprecated, use stream instead.'); - streamEventPluralDeprecationWarningShown = true; - } - return [stream]; - } - }); - } else { - this.streams = [stream]; + if (_options.onCreate) _options.onCreate(this.domElement); + + widget.setDisplayMode(_options.mode); + + if (_options.mode === 'auto') { + // if the mode is auto we hold the "on mode" for 2 seconds + // this will let the proper widgets nicely fade away and help discoverability + OT.$.addClass(widget.domElement, 'OT_mode-on-hold'); + setTimeout(function() { + OT.$.removeClass(widget.domElement, 'OT_mode-on-hold'); + }, 2000); } - this.stream = stream; - this.reason = reason; - }; -/** -* Prevents the default behavior associated with the event from taking place. -* -*

    For the streamDestroyed event dispatched by the Session object, -* the default behavior is that all Subscriber objects that are subscribed to the stream are -* unsubscribed and removed from the HTML DOM. Each Subscriber object dispatches a -* destroyed event when the element is removed from the HTML DOM. If you call the -* preventDefault() method in the event listener for the streamDestroyed -* event, the default behavior is prevented and you can clean up Subscriber objects using your -* own code. See -* Session.getSubscribersForStream().

    -*

    -* For the streamDestroyed event dispatched by a Publisher object, the default -* behavior is that the Publisher object is removed from the HTML DOM. The Publisher object -* dispatches a destroyed event when the element is removed from the HTML DOM. -* If you call the preventDefault() method in the event listener for the -* streamDestroyed event, the default behavior is prevented, and you can -* retain the Publisher for reuse or clean it up using your own code. -*

    -*

    To see whether an event has a default behavior, check the cancelable property of -* the event object.

    -* -*

    Call the preventDefault() method in the event listener function for the event.

    -* -* @method #preventDefault -* @memberof StreamEvent -*/ + // add the widget to the parent + parent.appendChild(this.domElement); -/** - * The Session object dispatches SessionConnectEvent object when a session has successfully - * connected in response to a call to the connect() method of the Session object. - *

    - * In version 2.2, the completionHandler of the Session.connect() method - * indicates success or failure in connecting to the session. - * - * @class SessionConnectEvent - * @property {Array} connections Deprecated in version 2.2 (and set to an empty array). In - * version 2.2, listen for the connectionCreated event dispatched by the Session - * object. In version 2.2, the Session object dispatches a connectionCreated event - * for each connection (including your own). This includes connections present when you first - * connect to the session. - * - * @property {Array} streams Deprecated in version 2.2 (and set to an empty array). In version - * 2.2, listen for the streamCreated event dispatched by the Session object. In - * version 2.2, the Session object dispatches a streamCreated event for each stream - * other than those published by your client. This includes streams - * present when you first connect to the session. - * - * @see Session.connect()

    - * @augments Event - */ + return widget; + }; +}; - var sessionConnectedConnectionsDeprecationWarningShown = false; - var sessionConnectedStreamsDeprecationWarningShown = false; - var sessionConnectedArchivesDeprecationWarningShown = false; +// tb_require('../../helpers/helpers.js') +// tb_require('./behaviour/widget.js') - OT.SessionConnectEvent = function (type) { - OT.Event.call(this, type, false); - if (OT.$.canDefineProperty) { - Object.defineProperties(this, { - connections: { - get: function() { - if(!sessionConnectedConnectionsDeprecationWarningShown) { - OT.warn('OT.SessionConnectedEvent no longer includes connections. Listen ' + - 'for connectionCreated events instead.'); - sessionConnectedConnectionsDeprecationWarningShown = true; - } - return []; - } - }, - streams: { - get: function() { - if(!sessionConnectedStreamsDeprecationWarningShown) { - OT.warn('OT.SessionConnectedEvent no longer includes streams. Listen for ' + - 'streamCreated events instead.'); - sessionConnectedConnectionsDeprecationWarningShown = true; - } - return []; - } - }, - archives: { - get: function() { - if(!sessionConnectedArchivesDeprecationWarningShown) { - OT.warn('OT.SessionConnectedEvent no longer includes archives. Listen for ' + - 'archiveStarted events instead.'); - sessionConnectedArchivesDeprecationWarningShown = true; - } - return []; - } - } - }); +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ + +OT.Chrome.VideoDisabledIndicator = function(options) { + var _videoDisabled = false, + _warning = false, + updateClasses; + + updateClasses = OT.$.bind(function(domElement) { + if (_videoDisabled) { + OT.$.addClass(domElement, 'OT_video-disabled'); } else { - this.connections = []; - this.streams = []; - this.archives = []; + OT.$.removeClass(domElement, 'OT_video-disabled'); + } + if(_warning) { + OT.$.addClass(domElement, 'OT_video-disabled-warning'); + } else { + OT.$.removeClass(domElement, 'OT_video-disabled-warning'); } + if ((_videoDisabled || _warning) && + (this.getDisplayMode() === 'auto' || this.getDisplayMode() === 'on')) { + OT.$.addClass(domElement, 'OT_active'); + } else { + OT.$.removeClass(domElement, 'OT_active'); + } + }, this); + + this.disableVideo = function(value) { + _videoDisabled = value; + if(value === true) { + _warning = false; + } + updateClasses(this.domElement); }; -/** - * The Session object dispatches SessionDisconnectEvent object when a session has disconnected. - * This event may be dispatched asynchronously in response to a successful call to the - * disconnect() method of the session object. - * - *

    - * Example - *

    - *

    - * The following code initializes a session and sets up an event listener for when a session is - * disconnected. - *

    - *
    var apiKey = ""; // Replace with your API key. See https://dashboard.tokbox.com/projects
    - *  var sessionID = ""; // Replace with your own session ID.
    - *                      // See https://dashboard.tokbox.com/projects
    - *  var token = ""; // Replace with a generated token that has been assigned the moderator role.
    - *                  // See https://dashboard.tokbox.com/projects
    - *
    - *  var session = OT.initSession(apiKey, sessionID);
    - *  session.on("sessionDisconnected", function(event) {
    - *      alert("The session disconnected. " + event.reason);
    - *  });
    - *  session.connect(token);
    - *  
    - * - * @property {String} reason A description of why the session disconnected. - * This property can have two values: - *

    - *
      - *
    • "clientDisconnected" — A client disconnected from the session by calling - * the disconnect() method of the Session object or by closing the browser. - * ( See Session.disconnect().)
    • - *
    • "forceDisconnected" — A moderator has disconnected you from the session - * by calling the forceDisconnect() method of the Session object. (See - * Session.forceDisconnect().)
    • - *
    • "networkDisconnected" — The network connection terminated abruptly - * (for example, the client lost their internet connection).
    • - *
    - * - * @class SessionDisconnectEvent - * @augments Event - */ - OT.SessionDisconnectEvent = function (type, reason, cancelable) { - OT.Event.call(this, type, cancelable); - this.reason = reason; + this.setWarning = function(value) { + _warning = value; + updateClasses(this.domElement); }; -/** -* Prevents the default behavior associated with the event from taking place. -* -*

    For the sessionDisconnectEvent, the default behavior is that all Subscriber -* objects are unsubscribed and removed from the HTML DOM. Each Subscriber object dispatches a -* destroyed event when the element is removed from the HTML DOM. If you call the -* preventDefault() method in the event listener for the sessionDisconnect -* event, the default behavior is prevented, and you can, optionally, clean up Subscriber objects -* using your own code). -* -*

    To see whether an event has a default behavior, check the cancelable property of -* the event object.

    -* -*

    Call the preventDefault() method in the event listener function for the event.

    -* -* @method #preventDefault -* @memberof SessionDisconnectEvent -*/ + // Mixin common widget behaviour + OT.Chrome.Behaviour.Widget(this, { + mode: options.mode || 'auto', + nodeName: 'div', + htmlAttributes: { + className: 'OT_video-disabled-indicator' + } + }); -/** - * The Session object dispatches a streamPropertyChanged event in the - * following circumstances: - * - *
      - * - *
    • When a publisher starts or stops publishing audio or video. This change causes - * the hasAudio or hasVideo property of the Stream object to - * change. This change results from a call to the publishAudio() or - * publishVideo() methods of the Publish object.
    • - * - *
    • When the videoDimensions property of a stream changes. For more information, - * see Stream.videoDimensions.
    • - * - *
    - * - * @class StreamPropertyChangedEvent - * @property {String} changedProperty The property of the stream that changed. This value - * is either "hasAudio", "hasVideo", or "videoDimensions". - * @property {Object} newValue The new value of the property (after the change). - * @property {Object} oldValue The old value of the property (before the change). - * @property {Stream} stream The Stream object for which a property has changed. - * - * @see Publisher.publishAudio()

    - * @see Publisher.publishVideo()

    - * @see Stream.videoDimensions

    - * @augments Event - */ - OT.StreamPropertyChangedEvent = function (type, stream, changedProperty, oldValue, newValue) { - OT.Event.call(this, type, false); - this.type = type; - this.stream = stream; - this.changedProperty = changedProperty; - this.oldValue = oldValue; - this.newValue = newValue; + var parentSetDisplayMode = OT.$.bind(this.setDisplayMode, this); + this.setDisplayMode = function(mode) { + parentSetDisplayMode(mode); + updateClasses(this.domElement); }; +}; -/** - * Defines event objects for the archiveStarted and archiveStopped events. - * The Session object dispatches these events when an archive recording of the session starts and - * stops. - * - * @property {String} id The archive ID. - * @property {String} name The name of the archive. You can assign an archive a name when you create - * it, using the OpenTok REST API or one of the - * OpenTok server SDKs. - * - * @class ArchiveEvent - * @augments Event - */ - OT.ArchiveEvent = function (type, archive) { - OT.Event.call(this, type, false); - this.type = type; - this.id = archive.id; - this.name = archive.name; - this.status = archive.status; - this.archive = archive; +// tb_require('../../helpers/helpers.js') +// tb_require('./behaviour/widget.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ + +// NamePanel Chrome Widget +// +// mode (String) +// Whether to display the name. Possible values are: "auto" (the name is displayed +// when the stream is first displayed and when the user mouses over the display), +// "off" (the name is not displayed), and "on" (the name is displayed). +// +// displays a name +// can be shown/hidden +// can be destroyed +OT.Chrome.NamePanel = function(options) { + var _name = options.name; + + if (!_name || OT.$.trim(_name).length === '') { + _name = null; + + // THere's no name, just flip the mode off + options.mode = 'off'; + } + + this.setName = OT.$.bind(function(name) { + if (!_name) this.setDisplayMode('auto'); + _name = name; + this.domElement.innerHTML = _name; + }); + + // Mixin common widget behaviour + OT.Chrome.Behaviour.Widget(this, { + mode: options.mode, + nodeName: 'h1', + htmlContent: _name, + htmlAttributes: { + className: 'OT_name OT_edge-bar-item' + } + }); +}; + +// tb_require('../../helpers/helpers.js') +// tb_require('./behaviour/widget.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ + +OT.Chrome.MuteButton = function(options) { + var _onClickCb, + _muted = options.muted || false, + updateClasses, + attachEvents, + detachEvents, + onClick; + + updateClasses = OT.$.bind(function() { + if (_muted) { + OT.$.addClass(this.domElement, 'OT_active'); + } else { + OT.$.removeClass(this.domElement, 'OT_active '); + } + }, this); + + // Private Event Callbacks + attachEvents = function(elem) { + _onClickCb = OT.$.bind(onClick, this); + OT.$.on(elem, 'click', _onClickCb); }; - OT.ArchiveUpdatedEvent = function (stream, key, oldValue, newValue) { - OT.Event.call(this, 'updated', false); - this.target = stream; - this.changedProperty = key; - this.oldValue = oldValue; - this.newValue = newValue; + detachEvents = function(elem) { + _onClickCb = null; + OT.$.off(elem, 'click', _onClickCb); }; -/** - * The Session object dispatches a signal event when the client receives a signal from the session. - * - * @class SignalEvent - * @property {String} type The type assigned to the signal (if there is one). Use the type to - * filter signals received (by adding an event handler for signal:type1 or signal:type2, etc.) - * @property {String} data The data string sent with the signal (if there is one). - * @property {Connection} from The Connection corresponding to the client that sent with the signal. - * - * @see Session.signal()

    - * @see Session events (signal and signal:type)

    - * @augments Event - */ - OT.SignalEvent = function(type, data, from) { - OT.Event.call(this, type ? 'signal:' + type : OT.Event.names.SIGNAL, false); - this.data = data; - this.from = from; + onClick = function() { + _muted = !_muted; + + updateClasses(); + + if (_muted) { + this.parent.trigger('muted', this); + } else { + this.parent.trigger('unmuted', this); + } + + return false; }; - OT.StreamUpdatedEvent = function (stream, key, oldValue, newValue) { - OT.Event.call(this, 'updated', false); - this.target = stream; - this.changedProperty = key; - this.oldValue = oldValue; - this.newValue = newValue; + OT.$.defineProperties(this, { + muted: { + get: function() { return _muted; }, + set: function(muted) { + _muted = muted; + updateClasses(); + } + } + }); + + // Mixin common widget behaviour + var classNames = _muted ? 'OT_edge-bar-item OT_mute OT_active' : 'OT_edge-bar-item OT_mute'; + OT.Chrome.Behaviour.Widget(this, { + mode: options.mode, + nodeName: 'button', + htmlContent: 'Mute', + htmlAttributes: { + className: classNames + }, + onCreate: OT.$.bind(attachEvents, this), + onDestroy: OT.$.bind(detachEvents, this) + }); +}; + +// tb_require('../../helpers/helpers.js') +// tb_require('./behaviour/widget.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ + +// BackingBar Chrome Widget +// +// nameMode (String) +// Whether or not the name panel is being displayed +// Possible values are: "auto" (the name is displayed +// when the stream is first displayed and when the user mouses over the display), +// "off" (the name is not displayed), and "on" (the name is displayed). +// +// muteMode (String) +// Whether or not the mute button is being displayed +// Possible values are: "auto" (the mute button is displayed +// when the stream is first displayed and when the user mouses over the display), +// "off" (the mute button is not displayed), and "on" (the mute button is displayed). +// +// displays a backing bar +// can be shown/hidden +// can be destroyed +OT.Chrome.BackingBar = function(options) { + var _nameMode = options.nameMode, + _muteMode = options.muteMode; + + function getDisplayMode() { + if(_nameMode === 'on' || _muteMode === 'on') { + return 'on'; + } else if(_nameMode === 'mini' || _muteMode === 'mini') { + return 'mini'; + } else if(_nameMode === 'mini-auto' || _muteMode === 'mini-auto') { + return 'mini-auto'; + } else if(_nameMode === 'auto' || _muteMode === 'auto') { + return 'auto'; + } else { + return 'off'; + } + } + + // Mixin common widget behaviour + OT.Chrome.Behaviour.Widget(this, { + mode: getDisplayMode(), + nodeName: 'div', + htmlContent: '', + htmlAttributes: { + className: 'OT_bar OT_edge-bar-item' + } + }); + + this.setNameMode = function(nameMode) { + _nameMode = nameMode; + this.setDisplayMode(getDisplayMode()); }; - OT.DestroyedEvent = function(type, target, reason) { - OT.Event.call(this, type, false); - this.target = target; - this.reason = reason; + this.setMuteMode = function(muteMode) { + _muteMode = muteMode; + this.setDisplayMode(getDisplayMode()); }; -/** - * Defines the event object for the videoDisabled and videoEnabled events - * dispatched by the Subscriber. - * - * @class VideoEnabledChangedEvent - * - * @property {Boolean} cancelable Whether the event has a default behavior that is cancelable - * (true) or not (false). You can cancel the default behavior by - * calling the preventDefault() method of the event object in the callback - * function. (See preventDefault().) - * - * @property {String} reason The reason the video was disabled or enabled. This can be set to one of - * the following values: - * - *
      - * - *
    • "publishVideo" — The publisher started or stopped publishing video, - * by calling publishVideo(true) or publishVideo(false).
    • - * - *
    • "quality" — The OpenTok Media Router starts or stops sending video - * to the subscriber based on stream quality changes. This feature of the OpenTok Media - * Router has a subscriber drop the video stream when connectivity degrades. (The subscriber - * continues to receive the audio stream, if there is one.) - *

      - * If connectivity improves to support video again, the Subscriber object dispatches - * a videoEnabled event, and the Subscriber resumes receiving video. - *

      - * By default, the Subscriber displays a video disabled indicator when a - * videoDisabled event with this reason is dispatched and removes the indicator - * when the videoDisabled event with this reason is dispatched. You can control - * the display of this icon by calling the setStyle() method of the Subscriber, - * setting the videoDisabledDisplayMode property(or you can set the style when - * calling the Session.subscribe() method, setting the style property - * of the properties parameter). - *

      - * This feature is only available in sessions that use the OpenTok Media Router (sessions with - * the media mode - * set to routed), not in sessions with the media mode set to relayed. - *

    • - * - *
    • "subscribeToVideo" — The subscriber started or stopped subscribing to - * video, by calling subscribeToVideo(true) or subscribeToVideo(false). - *
    • - * - *
    - * - * @property {Object} target The object that dispatched the event. - * - * @property {String} type The type of event: "videoDisabled" or - * "videoEnabled". - * - * @see Subscriber videoDisabled event

    - * @see Subscriber videoEnabled event

    - * @augments Event - */ - OT.VideoEnabledChangedEvent = function(type, properties) { - OT.Event.call(this, type, false); - this.reason = properties.reason; +}; + + +// tb_require('../../helpers/helpers.js') +// tb_require('./behaviour/widget.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ + + +OT.Chrome.AudioLevelMeter = function(options) { + + var widget = this, + _meterBarElement, + _voiceOnlyIconElement, + _meterValueElement, + _value, + _maxValue = options.maxValue || 1, + _minValue = options.minValue || 0; + + function onCreate() { + _meterBarElement = OT.$.createElement('div', { + className: 'OT_audio-level-meter__bar' + }, ''); + _meterValueElement = OT.$.createElement('div', { + className: 'OT_audio-level-meter__value' + }, ''); + _voiceOnlyIconElement = OT.$.createElement('div', { + className: 'OT_audio-level-meter__audio-only-img' + }, ''); + + widget.domElement.appendChild(_meterBarElement); + widget.domElement.appendChild(_voiceOnlyIconElement); + widget.domElement.appendChild(_meterValueElement); + } + + function updateView() { + var percentSize = _value * 100 / (_maxValue - _minValue); + _meterValueElement.style.width = _meterValueElement.style.height = 2 * percentSize + '%'; + _meterValueElement.style.top = _meterValueElement.style.right = -percentSize + '%'; + } + + // Mixin common widget behaviour + var widgetOptions = { + mode: options ? options.mode : 'auto', + nodeName: 'div', + htmlAttributes: { + className: 'OT_audio-level-meter' + }, + onCreate: onCreate }; - OT.VideoDisableWarningEvent = function(type/*, properties*/) { - OT.Event.call(this, type, false); + OT.Chrome.Behaviour.Widget(this, widgetOptions); + + // override + var _setDisplayMode = OT.$.bind(widget.setDisplayMode, widget); + widget.setDisplayMode = function(mode) { + _setDisplayMode(mode); + if (mode === 'off') { + if (options.onPassivate) options.onPassivate(); + } else { + if (options.onActivate) options.onActivate(); + } }; -/** - * Dispatched periodically by a Subscriber or Publisher object to indicate the audio - * level. This event is dispatched up to 60 times per second, depending on the browser. - * - * @property {String} audioLevel The audio level, from 0 to 1.0. Adjust this value logarithmically - * for use in adjusting a user interface element, such as a volume meter. Use a moving average - * to smooth the data. - * - * @class AudioLevelUpdatedEvent - * @augments Event - */ - OT.AudioLevelUpdatedEvent = function(audioLevel) { - OT.Event.call(this, OT.Event.names.AUDIO_LEVEL_UPDATED, false); - this.audioLevel = audioLevel; + widget.setValue = function(value) { + _value = value; + updateView(); }; +}; -})(window); -/* jshint ignore:start */ -// https://code.google.com/p/stringencoding/ -// An implementation of http://encoding.spec.whatwg.org/#api +// tb_require('../../helpers/helpers.js') +// tb_require('./behaviour/widget.js') -/** - * @license Copyright 2014 Joshua Bell - * - * 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. - * - * Original source: https://github.com/inexorabletash/text-encoding - ***/ +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ + + +// Archving Chrome Widget +// +// mode (String) +// Whether to display the archving widget. Possible values are: "on" (the status is displayed +// when archiving and briefly when archving ends) and "off" (the status is not displayed) + +// Whether to display the archving widget. Possible values are: "auto" (the name is displayed +// when the status is first displayed and when the user mouses over the display), +// "off" (the name is not displayed), and "on" (the name is displayed). +// +// displays a name +// can be shown/hidden +// can be destroyed +OT.Chrome.Archiving = function(options) { + var _archiving = options.archiving, + _archivingStarted = options.archivingStarted || 'Archiving on', + _archivingEnded = options.archivingEnded || 'Archiving off', + _initialState = true, + _lightBox, + _light, + _text, + _textNode, + renderStageDelayedAction, + renderText, + renderStage; + + renderText = function(text) { + _textNode.nodeValue = text; + _lightBox.setAttribute('title', text); + }; + + renderStage = OT.$.bind(function() { + if(renderStageDelayedAction) { + clearTimeout(renderStageDelayedAction); + renderStageDelayedAction = null; + } + + if(_archiving) { + OT.$.addClass(_light, 'OT_active'); + } else { + OT.$.removeClass(_light, 'OT_active'); + } + + OT.$.removeClass(this.domElement, 'OT_archiving-' + (!_archiving ? 'on' : 'off')); + OT.$.addClass(this.domElement, 'OT_archiving-' + (_archiving ? 'on' : 'off')); + if(options.show && _archiving) { + renderText(_archivingStarted); + OT.$.addClass(_text, 'OT_mode-on'); + OT.$.removeClass(_text, 'OT_mode-auto'); + this.setDisplayMode('on'); + renderStageDelayedAction = setTimeout(function() { + OT.$.addClass(_text, 'OT_mode-auto'); + OT.$.removeClass(_text, 'OT_mode-on'); + }, 5000); + } else if(options.show && !_initialState) { + OT.$.addClass(_text, 'OT_mode-on'); + OT.$.removeClass(_text, 'OT_mode-auto'); + this.setDisplayMode('on'); + renderText(_archivingEnded); + renderStageDelayedAction = setTimeout(OT.$.bind(function() { + this.setDisplayMode('off'); + }, this), 5000); + } else { + this.setDisplayMode('off'); + } + }, this); + + // Mixin common widget behaviour + OT.Chrome.Behaviour.Widget(this, { + mode: _archiving && options.show && 'on' || 'off', + nodeName: 'h1', + htmlAttributes: {className: 'OT_archiving OT_edge-bar-item OT_edge-bottom'}, + onCreate: OT.$.bind(function() { + _lightBox = OT.$.createElement('div', { + className: 'OT_archiving-light-box' + }, ''); + _light = OT.$.createElement('div', { + className: 'OT_archiving-light' + }, ''); + _lightBox.appendChild(_light); + _text = OT.$.createElement('div', { + className: 'OT_archiving-status OT_mode-on OT_edge-bar-item OT_edge-bottom' + }, ''); + _textNode = document.createTextNode(''); + _text.appendChild(_textNode); + this.domElement.appendChild(_lightBox); + this.domElement.appendChild(_text); + renderStage(); + }, this) + }); + + this.setShowArchiveStatus = OT.$.bind(function(show) { + options.show = show; + if(this.domElement) { + renderStage.call(this); + } + }, this); + + this.setArchiving = OT.$.bind(function(status) { + _archiving = status; + _initialState = false; + if(this.domElement) { + renderStage.call(this); + } + }, this); + +}; + +// tb_require('../helpers.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ + +// Web OT Helpers +!(function(window) { + // guard for Node.js + if (window && typeof(navigator) !== 'undefined') { + var NativeRTCPeerConnection = (window.webkitRTCPeerConnection || + window.mozRTCPeerConnection); + + if (navigator.webkitGetUserMedia) { + /*global webkitMediaStream, webkitRTCPeerConnection*/ + // Stub for getVideoTracks for Chrome < 26 + if (!webkitMediaStream.prototype.getVideoTracks) { + webkitMediaStream.prototype.getVideoTracks = function() { + return this.videoTracks; + }; + } + + // Stubs for getAudioTracks for Chrome < 26 + if (!webkitMediaStream.prototype.getAudioTracks) { + webkitMediaStream.prototype.getAudioTracks = function() { + return this.audioTracks; + }; + } + + if (!webkitRTCPeerConnection.prototype.getLocalStreams) { + webkitRTCPeerConnection.prototype.getLocalStreams = function() { + return this.localStreams; + }; + } + + if (!webkitRTCPeerConnection.prototype.getRemoteStreams) { + webkitRTCPeerConnection.prototype.getRemoteStreams = function() { + return this.remoteStreams; + }; + } + + } else if (navigator.mozGetUserMedia) { + // Firefox < 23 doesn't support get Video/Audio tracks, we'll just stub them out for now. + /* global MediaStream */ + if (!MediaStream.prototype.getVideoTracks) { + MediaStream.prototype.getVideoTracks = function() { + return []; + }; + } + + if (!MediaStream.prototype.getAudioTracks) { + MediaStream.prototype.getAudioTracks = function() { + return []; + }; + } + + // This won't work as mozRTCPeerConnection is a weird internal Firefox + // object (a wrapped native object I think). + // if (!window.mozRTCPeerConnection.prototype.getLocalStreams) { + // window.mozRTCPeerConnection.prototype.getLocalStreams = function() { + // return this.localStreams; + // }; + // } + + // This won't work as mozRTCPeerConnection is a weird internal Firefox + // object (a wrapped native object I think). + // if (!window.mozRTCPeerConnection.prototype.getRemoteStreams) { + // window.mozRTCPeerConnection.prototype.getRemoteStreams = function() { + // return this.remoteStreams; + // }; + // } + } + + // The setEnabled method on MediaStreamTracks is a OTPlugin + // construct. In this particular instance it's easier to bring + // all the good browsers down to IE's level than bootstrap it up. + if (typeof window.MediaStreamTrack !== 'undefined') { + if (!window.MediaStreamTrack.prototype.setEnabled) { + window.MediaStreamTrack.prototype.setEnabled = function (enabled) { + this.enabled = OT.$.castToBoolean(enabled); + }; + } + } + + if (!window.URL && window.webkitURL) { + window.URL = window.webkitURL; + } + + OT.$.createPeerConnection = function (config, options, publishersWebRtcStream, completion) { + if (OTPlugin.isInstalled()) { + OTPlugin.initPeerConnection(config, options, + publishersWebRtcStream, completion); + } + else { + var pc; + + try { + pc = new NativeRTCPeerConnection(config, options); + } catch(e) { + completion(e.message); + return; + } + + completion(null, pc); + } + }; + } + + // Returns a String representing the supported WebRTC crypto scheme. The possible + // values are SDES_SRTP, DTLS_SRTP, and NONE; + // + // Broadly: + // * Firefox only supports DTLS + // * Older versions of Chrome (<= 24) only support SDES + // * Newer versions of Chrome (>= 25) support DTLS and SDES + // + OT.$.supportedCryptoScheme = function() { + return OT.$.env.name === 'Chrome' && OT.$.env.version < 25 ? 'SDES_SRTP' : 'DTLS_SRTP'; + }; + +})(window); -(function(global) { - 'use strict'; +// tb_require('../../helpers/helpers.js') +// tb_require('../../helpers/lib/web_rtc.js') - var browser = OT.$.browserVersion(); - if(browser.browser === 'IE' && browser.version < 10) { - return; // IE 8 doesn't do websockets. No websockets, no encoding. - } +/* exported subscribeProcessor */ +/* global SDPHelpers */ - if ( (global.TextEncoder !== void 0) && (global.TextDecoder !== void 0)) { - // defer to the native ones - // @todo is this a good idea? - return; - } +// Attempt to completely process a subscribe message. This will: +// * create an Offer +// * set the new offer as the location description +// +// If there are no issues, the +success+ callback will be executed on completion. +// Errors during any step will result in the +failure+ callback being executed. +// +var subscribeProcessor = function(peerConnection, success, failure) { + var constraints, + generateErrorCallback, + setLocalDescription; + + constraints = { + mandatory: {}, + optional: [] + }, - // - // Utilities - // + generateErrorCallback = function(message, prefix) { + return function(errorReason) { + OT.error(message); + OT.error(errorReason); - /** - * @param {number} a The number to test. - * @param {number} min The minimum value in the range, inclusive. - * @param {number} max The maximum value in the range, inclusive. - * @return {boolean} True if a >= min and a <= max. - */ - function inRange(a, min, max) { - return min <= a && a <= max; - } + if (failure) failure(message, errorReason, prefix); + }; + }; - /** - * @param {number} n The numerator. - * @param {number} d The denominator. - * @return {number} The result of the integer division of n by d. - */ - function div(n, d) { - return Math.floor(n / d); + setLocalDescription = function(offer) { + offer.sdp = SDPHelpers.removeComfortNoise(offer.sdp); + offer.sdp = SDPHelpers.removeVideoCodec(offer.sdp, 'ulpfec'); + offer.sdp = SDPHelpers.removeVideoCodec(offer.sdp, 'red'); + + peerConnection.setLocalDescription( + offer, + + // Success + function() { + success(offer); + }, + + // Failure + generateErrorCallback('Error while setting LocalDescription', 'SetLocalDescription') + ); + }; + + // For interop with FireFox. Disable Data Channel in createOffer. + if (navigator.mozGetUserMedia) { + constraints.mandatory.MozDontOfferDataChannel = true; } + peerConnection.createOffer( + // Success + setLocalDescription, - // - // Implementation of Encoding specification - // http://dvcs.w3.org/hg/encoding/raw-file/tip/Overview.html - // + // Failure + generateErrorCallback('Error while creating Offer', 'CreateOffer'), - // - // 3. Terminology - // + constraints + ); +}; +// tb_require('../../helpers/helpers.js') +// tb_require('../../helpers/lib/web_rtc.js') - // - // 4. Encodings - // +/* exported offerProcessor */ +/* global SDPHelpers */ - /** @const */ var EOF_byte = -1; - /** @const */ var EOF_code_point = -1; +// Attempt to completely process +offer+. This will: +// * set the offer as the remote description +// * create an answer and +// * set the new answer as the location description +// +// If there are no issues, the +success+ callback will be executed on completion. +// Errors during any step will result in the +failure+ callback being executed. +// +var offerProcessor = function(peerConnection, offer, success, failure) { + var generateErrorCallback, + setLocalDescription, + createAnswer; - /** - * @constructor - * @param {Uint8Array} bytes Array of bytes that provide the stream. - */ - function ByteInputStream(bytes) { - /** @type {number} */ - var pos = 0; + generateErrorCallback = function(message, prefix) { + return function(errorReason) { + OT.error(message); + OT.error(errorReason); - /** @return {number} Get the next byte from the stream. */ - this.get = function() { - return (pos >= bytes.length) ? EOF_byte : Number(bytes[pos]); + if (failure) failure(message, errorReason, prefix); }; + }; - /** @param {number} n Number (positive or negative) by which to - * offset the byte pointer. */ - this.offset = function(n) { - pos += n; - if (pos < 0) { - throw new Error('Seeking past start of the buffer'); - } - if (pos > bytes.length) { - throw new Error('Seeking past EOF'); - } - }; + setLocalDescription = function(answer) { + answer.sdp = SDPHelpers.removeComfortNoise(answer.sdp); + answer.sdp = SDPHelpers.removeVideoCodec(answer.sdp, 'ulpfec'); + answer.sdp = SDPHelpers.removeVideoCodec(answer.sdp, 'red'); - /** - * @param {Array.} test Array of bytes to compare against. - * @return {boolean} True if the start of the stream matches the test - * bytes. - */ - this.match = function(test) { - if (test.length > pos + bytes.length) { - return false; - } - var i; - for (i = 0; i < test.length; i += 1) { - if (Number(bytes[pos + i]) !== test[i]) { - return false; - } - } - return true; - }; + peerConnection.setLocalDescription( + answer, + + // Success + function() { + success(answer); + }, + + // Failure + generateErrorCallback('Error while setting LocalDescription', 'SetLocalDescription') + ); + }; + + createAnswer = function() { + peerConnection.createAnswer( + // Success + setLocalDescription, + + // Failure + generateErrorCallback('Error while setting createAnswer', 'CreateAnswer'), + + null, // MediaConstraints + false // createProvisionalAnswer + ); + }; + + // Workaround for a Chrome issue. Add in the SDES crypto line into offers + // from Firefox + if (offer.sdp.indexOf('a=crypto') === -1) { + var cryptoLine = 'a=crypto:1 AES_CM_128_HMAC_SHA1_80 ' + + 'inline:FakeFakeFakeFakeFakeFakeFakeFakeFakeFake\\r\\n'; + + // insert the fake crypto line for every M line + offer.sdp = offer.sdp.replace(/^c=IN(.*)$/gmi, 'c=IN$1\r\n'+cryptoLine); } - /** - * @constructor - * @param {Array.} bytes The array to write bytes into. - */ - function ByteOutputStream(bytes) { - /** @type {number} */ - var pos = 0; + if (offer.sdp.indexOf('a=rtcp-fb') === -1) { + var rtcpFbLine = 'a=rtcp-fb:* ccm fir\r\na=rtcp-fb:* nack '; - /** - * @param {...number} var_args The byte or bytes to emit into the stream. - * @return {number} The last byte emitted. - */ - this.emit = function(var_args) { - /** @type {number} */ - var last = EOF_byte; - var i; - for (i = 0; i < arguments.length; ++i) { - last = Number(arguments[i]); - bytes[pos++] = last; - } - return last; - }; + // insert the fake crypto line for every M line + offer.sdp = offer.sdp.replace(/^m=video(.*)$/gmi, 'm=video$1\r\n'+rtcpFbLine); } - /** - * @constructor - * @param {string} string The source of code units for the stream. - */ - function CodePointInputStream(string) { - /** - * @param {string} string Input string of UTF-16 code units. - * @return {Array.} Code points. - */ - function stringToCodePoints(string) { - /** @type {Array.} */ - var cps = []; - // Based on http://www.w3.org/TR/WebIDL/#idl-DOMString - var i = 0, n = string.length; - while (i < string.length) { - var c = string.charCodeAt(i); - if (!inRange(c, 0xD800, 0xDFFF)) { - cps.push(c); - } else if (inRange(c, 0xDC00, 0xDFFF)) { - cps.push(0xFFFD); - } else { // (inRange(cu, 0xD800, 0xDBFF)) - if (i === n - 1) { - cps.push(0xFFFD); - } else { - var d = string.charCodeAt(i + 1); - if (inRange(d, 0xDC00, 0xDFFF)) { - var a = c & 0x3FF; - var b = d & 0x3FF; - i += 1; - cps.push(0x10000 + (a << 10) + b); - } else { - cps.push(0xFFFD); - } - } - } - i += 1; - } - return cps; - } + peerConnection.setRemoteDescription( + offer, - /** @type {number} */ - var pos = 0; - /** @type {Array.} */ - var cps = stringToCodePoints(string); + // Success + createAnswer, - /** @param {number} n The number of bytes (positive or negative) - * to advance the code point pointer by.*/ - this.offset = function(n) { - pos += n; - if (pos < 0) { - throw new Error('Seeking past start of the buffer'); - } - if (pos > cps.length) { - throw new Error('Seeking past EOF'); - } - }; + // Failure + generateErrorCallback('Error while setting RemoteDescription', 'SetRemoteDescription') + ); +}; +// tb_require('../../helpers/helpers.js') +// tb_require('../../helpers/lib/web_rtc.js') - /** @return {number} Get the next code point from the stream. */ - this.get = function() { - if (pos >= cps.length) { - return EOF_code_point; - } - return cps[pos]; - }; - } +/* exported IceCandidateProcessor */ - /** - * @constructor - */ - function CodePointOutputStream() { - /** @type {string} */ - var string = ''; +// Normalise these +var NativeRTCIceCandidate; - /** @return {string} The accumulated string. */ - this.string = function() { - return string; - }; +if (!OTPlugin.isInstalled()) { + NativeRTCIceCandidate = (window.mozRTCIceCandidate || window.RTCIceCandidate); +} +else { + NativeRTCIceCandidate = OTPlugin.RTCIceCandidate; +} - /** @param {number} c The code point to encode into the stream. */ - this.emit = function(c) { - if (c <= 0xFFFF) { - string += String.fromCharCode(c); - } else { - c -= 0x10000; - string += String.fromCharCode(0xD800 + ((c >> 10) & 0x3ff)); - string += String.fromCharCode(0xDC00 + (c & 0x3ff)); - } - }; - } +// Process incoming Ice Candidates from a remote connection (which have been +// forwarded via iceCandidateForwarder). The Ice Candidates cannot be processed +// until a PeerConnection is available. Once a PeerConnection becomes available +// the pending PeerConnections can be processed by calling processPending. +// +// @example +// +// var iceProcessor = new IceCandidateProcessor(); +// iceProcessor.process(iceMessage1); +// iceProcessor.process(iceMessage2); +// iceProcessor.process(iceMessage3); +// +// iceProcessor.setPeerConnection(peerConnection); +// iceProcessor.processPending(); +// +var IceCandidateProcessor = function() { + var _pendingIceCandidates = [], + _peerConnection = null; - /** - * @constructor - * @param {string} message Description of the error. - */ - function EncodingError(message) { - this.name = 'EncodingError'; - this.message = message; - this.code = 0; - } - EncodingError.prototype = Error.prototype; + this.setPeerConnection = function(peerConnection) { + _peerConnection = peerConnection; + }; - /** - * @param {boolean} fatal If true, decoding errors raise an exception. - * @param {number=} opt_code_point Override the standard fallback code point. - * @return {number} The code point to insert on a decoding error. - */ - function decoderError(fatal, opt_code_point) { - if (fatal) { - throw new EncodingError('Decoder error'); + this.process = function(message) { + var iceCandidate = new NativeRTCIceCandidate(message.content); + + if (_peerConnection) { + _peerConnection.addIceCandidate(iceCandidate); + } else { + _pendingIceCandidates.push(iceCandidate); } - return opt_code_point || 0xFFFD; - } + }; + + this.processPending = function() { + while(_pendingIceCandidates.length) { + _peerConnection.addIceCandidate(_pendingIceCandidates.shift()); + } + }; +}; +// tb_require('../../helpers/helpers.js') +// tb_require('../../helpers/lib/web_rtc.js') + +/* exported connectionStateLogger */ + +// @meta: ping Mike/Eric to let them know that this data is coming and what format it will be +// @meta: what reports would I like around this, what question am I trying to answer? +// +// log the sequence of iceconnectionstates, icegatheringstates, signalingstates +// log until we reach a terminal iceconnectionstate or signalingstate +// send a client event once we have a full sequence +// +// Format of the states: +// [ +// {delta: 1234, iceConnection: 'new', signalingstate: 'stable', iceGathering: 'new'}, +// {delta: 1234, iceConnection: 'new', signalingstate: 'stable', iceGathering: 'new'}, +// {delta: 1234, iceConnection: 'new', signalingstate: 'stable', iceGathering: 'new'}, +// ] +// +// Format of the logged event: +// { +// startTime: 1234, +// finishTime: 5678, +// states: [ +// {delta: 1234, iceConnection: 'new', signalingstate: 'stable', iceGathering: 'new'}, +// {delta: 1234, iceConnection: 'new', signalingstate: 'stable', iceGathering: 'new'}, +// {delta: 1234, iceConnection: 'new', signalingstate: 'stable', iceGathering: 'new'}, +// ] +// } +// +var connectionStateLogger = function(pc) { + var startTime = OT.$.now(), + finishTime, + suceeded, + states = []; - /** - * @param {number} code_point The code point that could not be encoded. - */ - function encoderError(code_point) { - throw new EncodingError('The code point ' + code_point + - ' could not be encoded.'); - } + var trackState = function() { + var now = OT.$.now(), + lastState = states[states.length-1], + state = {delta: finishTime ? now - finishTime : 0}; - /** - * @param {string} label The encoding label. - * @return {?{name:string,labels:Array.}} - */ - function getEncoding(label) { - label = String(label).trim().toLowerCase(); - if (Object.prototype.hasOwnProperty.call(label_to_encoding, label)) { - return label_to_encoding[label]; + if (!lastState || lastState.iceConnection !== pc.iceConnectionState) { + state.iceConnectionState = pc.iceConnectionState; } - return null; - } - /** @type {Array.<{encodings: Array.<{name:string,labels:Array.}>, - * heading: string}>} */ - var encodings = [ - { - 'encodings': [ - { - 'labels': [ - 'unicode-1-1-utf-8', - 'utf-8', - 'utf8' - ], - 'name': 'utf-8' - } - ], - 'heading': 'The Encoding' - }, - { - 'encodings': [ - { - 'labels': [ - 'cp864', - 'ibm864' - ], - 'name': 'ibm864' - }, - { - 'labels': [ - 'cp866', - 'ibm866' - ], - 'name': 'ibm866' - }, - { - 'labels': [ - 'csisolatin2', - 'iso-8859-2', - 'iso-ir-101', - 'iso8859-2', - 'iso_8859-2', - 'l2', - 'latin2' - ], - 'name': 'iso-8859-2' - }, - { - 'labels': [ - 'csisolatin3', - 'iso-8859-3', - 'iso_8859-3', - 'iso-ir-109', - 'l3', - 'latin3' - ], - 'name': 'iso-8859-3' - }, - { - 'labels': [ - 'csisolatin4', - 'iso-8859-4', - 'iso_8859-4', - 'iso-ir-110', - 'l4', - 'latin4' - ], - 'name': 'iso-8859-4' - }, - { - 'labels': [ - 'csisolatincyrillic', - 'cyrillic', - 'iso-8859-5', - 'iso_8859-5', - 'iso-ir-144' - ], - 'name': 'iso-8859-5' - }, - { - 'labels': [ - 'arabic', - 'csisolatinarabic', - 'ecma-114', - 'iso-8859-6', - 'iso_8859-6', - 'iso-ir-127' - ], - 'name': 'iso-8859-6' - }, - { - 'labels': [ - 'csisolatingreek', - 'ecma-118', - 'elot_928', - 'greek', - 'greek8', - 'iso-8859-7', - 'iso_8859-7', - 'iso-ir-126' - ], - 'name': 'iso-8859-7' - }, - { - 'labels': [ - 'csisolatinhebrew', - 'hebrew', - 'iso-8859-8', - 'iso-8859-8-i', - 'iso-ir-138', - 'iso_8859-8', - 'visual' - ], - 'name': 'iso-8859-8' - }, - { - 'labels': [ - 'csisolatin6', - 'iso-8859-10', - 'iso-ir-157', - 'iso8859-10', - 'l6', - 'latin6' - ], - 'name': 'iso-8859-10' - }, - { - 'labels': [ - 'iso-8859-13' - ], - 'name': 'iso-8859-13' - }, - { - 'labels': [ - 'iso-8859-14', - 'iso8859-14' - ], - 'name': 'iso-8859-14' - }, - { - 'labels': [ - 'iso-8859-15', - 'iso_8859-15' - ], - 'name': 'iso-8859-15' - }, - { - 'labels': [ - 'iso-8859-16' - ], - 'name': 'iso-8859-16' - }, - { - 'labels': [ - 'koi8-r', - 'koi8_r' - ], - 'name': 'koi8-r' - }, - { - 'labels': [ - 'koi8-u' - ], - 'name': 'koi8-u' - }, - { - 'labels': [ - 'csmacintosh', - 'mac', - 'macintosh', - 'x-mac-roman' - ], - 'name': 'macintosh' - }, - { - 'labels': [ - 'iso-8859-11', - 'tis-620', - 'windows-874' - ], - 'name': 'windows-874' - }, - { - 'labels': [ - 'windows-1250', - 'x-cp1250' - ], - 'name': 'windows-1250' - }, - { - 'labels': [ - 'windows-1251', - 'x-cp1251' - ], - 'name': 'windows-1251' - }, - { - 'labels': [ - 'ascii', - 'ansi_x3.4-1968', - 'csisolatin1', - 'iso-8859-1', - 'iso8859-1', - 'iso_8859-1', - 'l1', - 'latin1', - 'us-ascii', - 'windows-1252' - ], - 'name': 'windows-1252' - }, - { - 'labels': [ - 'cp1253', - 'windows-1253' - ], - 'name': 'windows-1253' - }, - { - 'labels': [ - 'csisolatin5', - 'iso-8859-9', - 'iso-ir-148', - 'l5', - 'latin5', - 'windows-1254' - ], - 'name': 'windows-1254' - }, - { - 'labels': [ - 'cp1255', - 'windows-1255' - ], - 'name': 'windows-1255' + if (!lastState || lastState.signalingState !== pc.signalingState) { + state.signalingState = pc.signalingState; + } + + if (!lastState || lastState.iceGatheringState !== pc.iceGatheringState) { + state.iceGathering = pc.iceGatheringState; + } + OT.debug(state); + states.push(state); + finishTime = now; + }; + + pc.addEventListener('iceconnectionstatechange', trackState, false); + pc.addEventListener('signalingstatechange', trackState, false); + pc.addEventListener('icegatheringstatechange', trackState, false); + + return { + stop: function () { + pc.removeEventListener('iceconnectionstatechange', trackState, false); + pc.removeEventListener('signalingstatechange', trackState, false); + pc.removeEventListener('icegatheringstatechange', trackState, false); + + // @todo The client logging of these states is not currently used, so it's left todo. + + // @todo analyse final state and decide whether the connection was successful + suceeded = true; + + var payload = { + type: 'PeerConnectionWorkflow', + success: suceeded, + startTime: startTime, + finishTime: finishTime, + states: states + }; + + // @todo send client event + OT.debug(payload); + } + }; +}; + +// tb_require('../../helpers/helpers.js') +// tb_require('../../helpers/lib/web_rtc.js') +// tb_require('./connection_state_logger.js') +// tb_require('./ice_candidate_processor.js') +// tb_require('./subscribe_processor.js') +// tb_require('./offer_processor.js') +// tb_require('./get_stats_adapter.js') + +/* global offerProcessor, subscribeProcessor, connectionStateLogger, IceCandidateProcessor */ + +// Normalise these +var NativeRTCSessionDescription; + +if (!OTPlugin.isInstalled()) { + // order is very important: 'RTCSessionDescription' defined in Firefox Nighly but useless + NativeRTCSessionDescription = (window.mozRTCSessionDescription || + window.RTCSessionDescription); +} +else { + NativeRTCSessionDescription = OTPlugin.RTCSessionDescription; +} + + +// Helper function to forward Ice Candidates via +messageDelegate+ +var iceCandidateForwarder = function(messageDelegate) { + return function(event) { + if (event.candidate) { + messageDelegate(OT.Raptor.Actions.CANDIDATE, event.candidate); + } else { + OT.debug('IceCandidateForwarder: No more ICE candidates.'); + } + }; +}; + +/* + * Negotiates a WebRTC PeerConnection. + * + * Responsible for: + * * offer-answer exchange + * * iceCandidates + * * notification of remote streams being added/removed + * + */ +OT.PeerConnection = function(config) { + var _peerConnection, + _peerConnectionCompletionHandlers = [], + _iceProcessor = new IceCandidateProcessor(), + _getStatsAdapter = OT.getStatsAdpater(), + _stateLogger, + _offer, + _answer, + _state = 'new', + _messageDelegates = []; + + + OT.$.eventing(this); + + // if ice servers doesn't exist Firefox will throw an exception. Chrome + // interprets this as 'Use my default STUN servers' whereas FF reads it + // as 'Don't use STUN at all'. *Grumble* + if (!config.iceServers) config.iceServers = []; + + // Private methods + var delegateMessage = OT.$.bind(function(type, messagePayload, uri) { + if (_messageDelegates.length) { + // We actually only ever send to the first delegate. This is because + // each delegate actually represents a Publisher/Subscriber that + // shares a single PeerConnection. If we sent to all delegates it + // would result in each message being processed multiple times by + // each PeerConnection. + _messageDelegates[0](type, messagePayload, uri); + } + }, this), + + // Create and initialise the PeerConnection object. This deals with + // any differences between the various browser implementations and + // our own OTPlugin version. + // + // +completion+ is the function is call once we've either successfully + // created the PeerConnection or on failure. + // + // +localWebRtcStream+ will be null unless the callee is representing + // a publisher. This is an unfortunate implementation limitation + // of OTPlugin, it's not used for vanilla WebRTC. Hopefully this can + // be tidied up later. + // + createPeerConnection = OT.$.bind(function (completion, localWebRtcStream) { + if (_peerConnection) { + completion.call(null, null, _peerConnection); + return; + } + + _peerConnectionCompletionHandlers.push(completion); + + if (_peerConnectionCompletionHandlers.length > 1) { + // The PeerConnection is already being setup, just wait for + // it to be ready. + return; + } + + var pcConstraints = { + optional: [ + {DtlsSrtpKeyAgreement: true} + ] + }; + + OT.debug('Creating peer connection config "' + JSON.stringify(config) + '".'); + + if (!config.iceServers || config.iceServers.length === 0) { + // This should never happen unless something is misconfigured + OT.error('No ice servers present'); + } + + OT.$.createPeerConnection(config, pcConstraints, localWebRtcStream, + attachEventsToPeerConnection); + }, this), + + // An auxiliary function to createPeerConnection. This binds the various event callbacks + // once the peer connection is created. + // + // +err+ will be non-null if an err occured while creating the PeerConnection + // +pc+ will be the PeerConnection object itself. + // + attachEventsToPeerConnection = OT.$.bind(function(err, pc) { + if (err) { + triggerError('Failed to create PeerConnection, exception: ' + + err.toString(), 'NewPeerConnection'); + + _peerConnectionCompletionHandlers = []; + return; + } + + OT.debug('OT attachEventsToPeerConnection'); + _peerConnection = pc; + _stateLogger = connectionStateLogger(_peerConnection); + + _peerConnection.addEventListener('icecandidate', + iceCandidateForwarder(delegateMessage), false); + _peerConnection.addEventListener('addstream', onRemoteStreamAdded, false); + _peerConnection.addEventListener('removestream', onRemoteStreamRemoved, false); + _peerConnection.addEventListener('signalingstatechange', routeStateChanged, false); + + if (_peerConnection.oniceconnectionstatechange !== void 0) { + var failedStateTimer; + _peerConnection.addEventListener('iceconnectionstatechange', function (event) { + if (event.target.iceConnectionState === 'failed') { + if (failedStateTimer) { + clearTimeout(failedStateTimer); + } + + // We wait 5 seconds and make sure that it's still in the failed state + // before we trigger the error. This is because we sometimes see + // 'failed' and then 'connected' afterwards. + failedStateTimer = setTimeout(function () { + if (event.target.iceConnectionState === 'failed') { + triggerError('The stream was unable to connect due to a network error.' + + ' Make sure your connection isn\'t blocked by a firewall.', 'ICEWorkflow'); + } + }, 5000); + } + }, false); + } + + triggerPeerConnectionCompletion(null); + }, this), + + triggerPeerConnectionCompletion = function () { + while (_peerConnectionCompletionHandlers.length) { + _peerConnectionCompletionHandlers.shift().call(null); + } + }, + + // Clean up the Peer Connection and trigger the close event. + // This function can be called safely multiple times, it will + // only trigger the close event once (per PeerConnection object) + tearDownPeerConnection = function() { + // Our connection is dead, stop processing ICE candidates + if (_iceProcessor) _iceProcessor.setPeerConnection(null); + if (_stateLogger) _stateLogger.stop(); + + qos.stopCollecting(); + + if (_peerConnection !== null) { + if (_peerConnection.destroy) { + // OTPlugin defines a destroy method on PCs. This allows + // the plugin to release any resources that it's holding. + _peerConnection.destroy(); + } + + _peerConnection = null; + this.trigger('close'); + } + }, + + routeStateChanged = OT.$.bind(function() { + var newState = _peerConnection.signalingState; + + if (newState && newState !== _state) { + _state = newState; + OT.debug('PeerConnection.stateChange: ' + _state); + + switch(_state) { + case 'closed': + tearDownPeerConnection.call(this); + break; + } + } + }, this), + + qosCallback = OT.$.bind(function(parsedStats) { + this.trigger('qos', parsedStats); + }, this), + + getRemoteStreams = function() { + var streams; + + if (_peerConnection.getRemoteStreams) { + streams = _peerConnection.getRemoteStreams(); + } else if (_peerConnection.remoteStreams) { + streams = _peerConnection.remoteStreams; + } else { + throw new Error('Invalid Peer Connection object implements no ' + + 'method for retrieving remote streams'); + } + + // Force streams to be an Array, rather than a 'Sequence' object, + // which is browser dependent and does not behaviour like an Array + // in every case. + return Array.prototype.slice.call(streams); + }, + + /// PeerConnection signaling + onRemoteStreamAdded = OT.$.bind(function(event) { + this.trigger('streamAdded', event.stream); + }, this), + + onRemoteStreamRemoved = OT.$.bind(function(event) { + this.trigger('streamRemoved', event.stream); + }, this), + + // ICE Negotiation messages + + + // Relays a SDP payload (+sdp+), that is part of a message of type +messageType+ + // via the registered message delegators + relaySDP = function(messageType, sdp, uri) { + delegateMessage(messageType, sdp, uri); + }, + + + // Process an offer that + processOffer = function(message) { + var offer = new NativeRTCSessionDescription({type: 'offer', sdp: message.content.sdp}), + + // Relays +answer+ Answer + relayAnswer = function(answer) { + _iceProcessor.setPeerConnection(_peerConnection); + _iceProcessor.processPending(); + relaySDP(OT.Raptor.Actions.ANSWER, answer); + + qos.startCollecting(_peerConnection); + }, + + reportError = function(message, errorReason, prefix) { + triggerError('PeerConnection.offerProcessor ' + message + ': ' + + errorReason, prefix); + }; + + createPeerConnection(function() { + offerProcessor( + _peerConnection, + offer, + relayAnswer, + reportError + ); + }); + }, + + processAnswer = function(message) { + if (!message.content.sdp) { + OT.error('PeerConnection.processMessage: Weird answer message, no SDP.'); + return; + } + + _answer = new NativeRTCSessionDescription({type: 'answer', sdp: message.content.sdp}); + + _peerConnection.setRemoteDescription(_answer, + function () { + OT.debug('setRemoteDescription Success'); + }, function (errorReason) { + triggerError('Error while setting RemoteDescription ' + errorReason, + 'SetRemoteDescription'); + }); + + _iceProcessor.setPeerConnection(_peerConnection); + _iceProcessor.processPending(); + + qos.startCollecting(_peerConnection); + }, + + processSubscribe = function(message) { + OT.debug('PeerConnection.processSubscribe: Sending offer to subscriber.'); + + if (!_peerConnection) { + // TODO(rolly) I need to examine whether this can + // actually happen. If it does happen in the short + // term, I want it to be noisy. + throw new Error('PeerConnection broke!'); + } + + createPeerConnection(function() { + subscribeProcessor( + _peerConnection, + + // Success: Relay Offer + function(offer) { + _offer = offer; + relaySDP(OT.Raptor.Actions.OFFER, _offer, message.uri); + }, + + // Failure + function(message, errorReason, prefix) { + triggerError('PeerConnection.subscribeProcessor ' + message + ': ' + + errorReason, prefix); + } + ); + }); + }, + + triggerError = OT.$.bind(function(errorReason, prefix) { + OT.error(errorReason); + this.trigger('error', errorReason, prefix); + }, this); + + this.addLocalStream = function(webRTCStream) { + createPeerConnection(function() { + _peerConnection.addStream(webRTCStream); + }, webRTCStream); + }; + + this.disconnect = function() { + _iceProcessor = null; + + if (_peerConnection && + _peerConnection.signalingState && + _peerConnection.signalingState.toLowerCase() !== 'closed') { + + _peerConnection.close(); + + if (OT.$.env.name === 'Firefox') { + // FF seems to never go into the closed signalingState when the close + // method is called on a PeerConnection. This means that we need to call + // our cleanup code manually. + // + // * https://bugzilla.mozilla.org/show_bug.cgi?id=989936 + // + OT.$.callAsync(OT.$.bind(tearDownPeerConnection, this)); + } + } + + this.off(); + }; + + this.processMessage = function(type, message) { + OT.debug('PeerConnection.processMessage: Received ' + + type + ' from ' + message.fromAddress); + + OT.debug(message); + + switch(type) { + case 'generateoffer': + processSubscribe.call(this, message); + break; + + case 'offer': + processOffer.call(this, message); + break; + + case 'answer': + case 'pranswer': + processAnswer.call(this, message); + break; + + case 'candidate': + _iceProcessor.process(message); + break; + + default: + OT.debug('PeerConnection.processMessage: Received an unexpected message of type ' + + type + ' from ' + message.fromAddress + ': ' + JSON.stringify(message)); + } + + return this; + }; + + this.setIceServers = function (iceServers) { + if (iceServers) { + config.iceServers = iceServers; + } + }; + + this.registerMessageDelegate = function(delegateFn) { + return _messageDelegates.push(delegateFn); + }; + + this.unregisterMessageDelegate = function(delegateFn) { + var index = OT.$.arrayIndexOf(_messageDelegates, delegateFn); + + if ( index !== -1 ) { + _messageDelegates.splice(index, 1); + } + return _messageDelegates.length; + }; + + this.remoteStreams = function() { + return _peerConnection ? getRemoteStreams() : []; + }; + + this.getStats = function(callback) { + createPeerConnection(function() { + _getStatsAdapter(_peerConnection, callback); + }); + }; + + var qos = new OT.PeerConnection.QOS(qosCallback); +}; + +// tb_require('../../helpers/helpers.js') +// tb_require('./peer_connection.js') + +// +// There are three implementations of stats parsing in this file. +// 1. For Chrome: Chrome is currently using an older version of the API +// 2. For OTPlugin: The plugin is using a newer version of the API that +// exists in the latest WebRTC codebase +// 3. For Firefox: FF is using a version that looks a lot closer to the +// current spec. +// +// I've attempted to keep the three implementations from sharing any code, +// accordingly you'll notice a bunch of duplication between the three. +// +// This is acceptable as the goal is to be able to remove each implementation +// as it's no longer needed without any risk of affecting the others. If there +// was shared code between them then each removal would require an audit of +// all the others. +// +// +!(function() { + + /// + // Get Stats using the older API. Used by all current versions + // of Chrome. + // + var parseStatsOldAPI = function parseStatsOldAPI (peerConnection, + prevStats, + currentStats, + completion) { + + /* this parses a result if there it contains the video bitrate */ + var parseVideoStats = function (result) { + if (result.stat('googFrameRateSent')) { + currentStats.videoSentBytes = Number(result.stat('bytesSent')); + currentStats.videoSentPackets = Number(result.stat('packetsSent')); + currentStats.videoSentPacketsLost = Number(result.stat('packetsLost')); + currentStats.videoRtt = Number(result.stat('googRtt')); + currentStats.videoFrameRate = Number(result.stat('googFrameRateInput')); + currentStats.videoWidth = Number(result.stat('googFrameWidthSent')); + currentStats.videoHeight = Number(result.stat('googFrameHeightSent')); + currentStats.videoCodec = result.stat('googCodecName'); + } else if (result.stat('googFrameRateReceived')) { + currentStats.videoRecvBytes = Number(result.stat('bytesReceived')); + currentStats.videoRecvPackets = Number(result.stat('packetsReceived')); + currentStats.videoRecvPacketsLost = Number(result.stat('packetsLost')); + currentStats.videoFrameRate = Number(result.stat('googFrameRateOutput')); + currentStats.videoWidth = Number(result.stat('googFrameWidthReceived')); + currentStats.videoHeight = Number(result.stat('googFrameHeightReceived')); + currentStats.videoCodec = result.stat('googCodecName'); + } + return null; }, - { - 'labels': [ - 'cp1256', - 'windows-1256' - ], - 'name': 'windows-1256' + + parseAudioStats = function (result) { + if (result.stat('audioInputLevel')) { + currentStats.audioSentPackets = Number(result.stat('packetsSent')); + currentStats.audioSentPacketsLost = Number(result.stat('packetsLost')); + currentStats.audioSentBytes = Number(result.stat('bytesSent')); + currentStats.audioCodec = result.stat('googCodecName'); + currentStats.audioRtt = Number(result.stat('googRtt')); + } else if (result.stat('audioOutputLevel')) { + currentStats.audioRecvPackets = Number(result.stat('packetsReceived')); + currentStats.audioRecvPacketsLost = Number(result.stat('packetsLost')); + currentStats.audioRecvBytes = Number(result.stat('bytesReceived')); + currentStats.audioCodec = result.stat('googCodecName'); + } }, - { - 'labels': [ - 'windows-1257' - ], - 'name': 'windows-1257' + + parseStatsReports = function (stats) { + if (stats.result) { + var resultList = stats.result(); + for (var resultIndex = 0; resultIndex < resultList.length; resultIndex++) { + var result = resultList[resultIndex]; + + if (result.stat) { + + if(result.stat('googActiveConnection') === 'true') { + currentStats.localCandidateType = result.stat('googLocalCandidateType'); + currentStats.remoteCandidateType = result.stat('googRemoteCandidateType'); + currentStats.transportType = result.stat('googTransportType'); + } + + parseAudioStats(result); + parseVideoStats(result); + } + } + } + + completion(null, currentStats); + }; + + peerConnection.getStats(parseStatsReports); + }; + + /// + // Get Stats for the OT Plugin, newer than Chromes version, but + // still not in sync with the spec. + // + var parseStatsOTPlugin = function parseStatsOTPlugin (peerConnection, + prevStats, + currentStats, + completion) { + + var onStatsError = function onStatsError (error) { + completion(error); }, - { - 'labels': [ - 'cp1258', - 'windows-1258' - ], - 'name': 'windows-1258' + + /// + // From the Audio Tracks + // * avgAudioBitrate + // * audioBytesTransferred + // + parseAudioStats = function (statsReport) { + var lastBytesSent = prevStats.audioBytesTransferred || 0, + transferDelta; + + if (statsReport.audioInputLevel) { + currentStats.audioSentBytes = Number(statsReport.bytesSent); + currentStats.audioSentPackets = Number(statsReport.packetsSent); + currentStats.audioSentPacketsLost = Number(statsReport.packetsLost); + currentStats.audioRtt = Number(statsReport.googRtt); + currentStats.audioCodec = statsReport.googCodecName; + } + else if (statsReport.audioOutputLevel) { + currentStats.audioBytesTransferred = Number(statsReport.bytesReceived); + currentStats.audioCodec = statsReport.googCodecName; + } + + if (currentStats.audioBytesTransferred) { + transferDelta = currentStats.audioBytesTransferred - lastBytesSent; + currentStats.avgAudioBitrate = Math.round(transferDelta * 8 / currentStats.period); + } }, - { - 'labels': [ - 'x-mac-cyrillic', - 'x-mac-ukrainian' - ], - 'name': 'x-mac-cyrillic' - } - ], - 'heading': 'Legacy single-byte encodings' - }, - { - 'encodings': [ - { - 'labels': [ - 'chinese', - 'csgb2312', - 'csiso58gb231280', - 'gb2312', - 'gbk', - 'gb_2312', - 'gb_2312-80', - 'iso-ir-58', - 'x-gbk' - ], - 'name': 'gbk' + + /// + // From the Video Tracks + // * frameRate + // * avgVideoBitrate + // * videoBytesTransferred + // + parseVideoStats = function (statsReport) { + + var lastBytesSent = prevStats.videoBytesTransferred || 0, + transferDelta; + + if (statsReport.googFrameHeightSent) { + currentStats.videoSentBytes = Number(statsReport.bytesSent); + currentStats.videoSentPackets = Number(statsReport.packetsSent); + currentStats.videoSentPacketsLost = Number(statsReport.packetsLost); + currentStats.videoRtt = Number(statsReport.googRtt); + currentStats.videoCodec = statsReport.googCodecName; + currentStats.videoWidth = Number(statsReport.googFrameWidthSent); + currentStats.videoHeight = Number(statsReport.googFrameHeightSent); + } + else if (statsReport.googFrameHeightReceived) { + currentStats.videoRecvBytes = Number(statsReport.bytesReceived); + currentStats.videoRecvPackets = Number(statsReport.packetsReceived); + currentStats.videoRecvPacketsLost = Number(statsReport.packetsLost); + currentStats.videoRtt = Number(statsReport.googRtt); + currentStats.videoCodec = statsReport.googCodecName; + currentStats.videoWidth = Number(statsReport.googFrameWidthReceived); + currentStats.videoHeight = Number(statsReport.googFrameHeightReceived); + } + + if (currentStats.videoBytesTransferred) { + transferDelta = currentStats.videoBytesTransferred - lastBytesSent; + currentStats.avgVideoBitrate = Math.round(transferDelta * 8 / currentStats.period); + } + + if (statsReport.googFrameRateSent) { + currentStats.videoFrameRate = Number(statsReport.googFrameRateSent); + } else if (statsReport.googFrameRateReceived) { + currentStats.videoFrameRate = Number(statsReport.googFrameRateReceived); + } }, - { - 'labels': [ - 'gb18030' - ], - 'name': 'gb18030' + + isStatsForVideoTrack = function(statsReport) { + return statsReport.googFrameHeightSent !== void 0 || + statsReport.googFrameHeightReceived !== void 0 || + currentStats.videoBytesTransferred !== void 0 || + statsReport.googFrameRateSent !== void 0; }, - { - 'labels': [ - 'hz-gb-2312' - ], - 'name': 'hz-gb-2312' + + isStatsForIceCandidate = function(statsReport) { + return statsReport.googActiveConnection === 'true'; + }; + + peerConnection.getStats(null, function(statsReports) { + statsReports.forEach(function(statsReport) { + if (isStatsForIceCandidate(statsReport)) { + currentStats.localCandidateType = statsReport.googLocalCandidateType; + currentStats.remoteCandidateType = statsReport.googRemoteCandidateType; + currentStats.transportType = statsReport.googTransportType; } - ], - 'heading': 'Legacy multi-byte Chinese (simplified) encodings' - }, - { - 'encodings': [ - { - 'labels': [ - 'big5', - 'big5-hkscs', - 'cn-big5', - 'csbig5', - 'x-x-big5' - ], - 'name': 'big5' + else if (isStatsForVideoTrack(statsReport)) { + parseVideoStats(statsReport); } - ], - 'heading': 'Legacy multi-byte Chinese (traditional) encodings' - }, - { - 'encodings': [ - { - 'labels': [ - 'cseucpkdfmtjapanese', - 'euc-jp', - 'x-euc-jp' - ], - 'name': 'euc-jp' - }, - { - 'labels': [ - 'csiso2022jp', - 'iso-2022-jp' - ], - 'name': 'iso-2022-jp' - }, - { - 'labels': [ - 'csshiftjis', - 'ms_kanji', - 'shift-jis', - 'shift_jis', - 'sjis', - 'windows-31j', - 'x-sjis' - ], - 'name': 'shift_jis' + else { + parseAudioStats(statsReport); } - ], - 'heading': 'Legacy multi-byte Japanese encodings' - }, - { - 'encodings': [ - { - 'labels': [ - 'cseuckr', - 'csksc56011987', - 'euc-kr', - 'iso-ir-149', - 'korean', - 'ks_c_5601-1987', - 'ks_c_5601-1989', - 'ksc5601', - 'ksc_5601', - 'windows-949' - ], - 'name': 'euc-kr' + }); + + completion(null, currentStats); + }, onStatsError); + }; + + + /// + // Get Stats using the newer API. + // + var parseStatsNewAPI = function parseStatsNewAPI (peerConnection, + prevStats, + currentStats, + completion) { + + var onStatsError = function onStatsError (error) { + completion(error); }, - { - 'labels': [ - 'csiso2022kr', - 'iso-2022-kr' - ], - 'name': 'iso-2022-kr' - } - ], - 'heading': 'Legacy multi-byte Korean encodings' - }, - { - 'encodings': [ - { - 'labels': [ - 'utf-16', - 'utf-16le' - ], - 'name': 'utf-16' + + parseAudioStats = function (result) { + if (result.type==='outboundrtp') { + currentStats.audioSentPackets = result.packetsSent; + currentStats.audioSentPacketsLost = result.packetsLost; + currentStats.audioSentBytes = result.bytesSent; + } else if (result.type==='inboundrtp') { + currentStats.audioRecvPackets = result.packetsReceived; + currentStats.audioRecvPacketsLost = result.packetsLost; + currentStats.audioRecvBytes = result.bytesReceived; + } }, - { - 'labels': [ - 'utf-16be' - ], - 'name': 'utf-16be' + + parseVideoStats = function (result) { + if (result.type==='outboundrtp') { + currentStats.videoSentPackets = result.packetsSent; + currentStats.videoSentPacketsLost = result.packetsLost; + currentStats.videoSentBytes = result.bytesSent; + } else if (result.type==='inboundrtp') { + currentStats.videoRecvPackets = result.packetsReceived; + currentStats.videoRecvPacketsLost = result.packetsLost; + currentStats.videoRecvBytes = result.bytesReceived; + } + }; + + peerConnection.getStats(null, function(stats) { + + for (var key in stats) { + if (stats.hasOwnProperty(key) && + (stats[key].type === 'outboundrtp' || stats[key].type === 'inboundrtp')) { + var res = stats[key]; + + if (res.id.indexOf('audio') !== -1) { + parseAudioStats(res); + } else if (res.id.indexOf('video') !== -1) { + parseVideoStats(res); + } + } + } + + completion(null, currentStats); + }, onStatsError); + }; + + + var parseQOS = function (peerConnection, prevStats, currentStats, completion) { + if (OTPlugin.isInstalled()) { + parseQOS = parseStatsOTPlugin; + return parseStatsOTPlugin(peerConnection, prevStats, currentStats, completion); + } + else if (OT.$.env.name === 'Firefox' && OT.$.env.version >= 27) { + parseQOS = parseStatsNewAPI; + return parseStatsNewAPI(peerConnection, prevStats, currentStats, completion); + } + else { + parseQOS = parseStatsOldAPI; + return parseStatsOldAPI(peerConnection, prevStats, currentStats, completion); + } + }; + + OT.PeerConnection.QOS = function (qosCallback) { + var _creationTime = OT.$.now(), + _peerConnection; + + var calculateQOS = OT.$.bind(function calculateQOS (prevStats) { + if (!_peerConnection) { + // We don't have a PeerConnection yet, or we did and + // it's been closed. Either way we're done. + return; + } + + var now = OT.$.now(); + + var currentStats = { + timeStamp: now, + duration: Math.round(now - _creationTime), + period: (now - prevStats.timeStamp) / 1000 + }; + + var onParsedStats = function (err, parsedStats) { + if (err) { + OT.error('Failed to Parse QOS Stats: ' + JSON.stringify(err)); + return; } - ], - 'heading': 'Legacy utf-16 encodings' - } - ]; - var name_to_encoding = {}; - var label_to_encoding = {}; - encodings.forEach(function(category) { - category.encodings.forEach(function(encoding) { - name_to_encoding[encoding.name] = encoding; - encoding.labels.forEach(function(label) { - label_to_encoding[label] = encoding; + qosCallback(parsedStats, prevStats); + + // Recalculate the stats + setTimeout(OT.$.bind(calculateQOS, null, parsedStats), OT.PeerConnection.QOS.INTERVAL); + }; + + parseQOS(_peerConnection, prevStats, currentStats, onParsedStats); + }, this); + + + this.startCollecting = function (peerConnection) { + if (!peerConnection || !peerConnection.getStats) { + // It looks like this browser doesn't support getStats + // Bail. + return; + } + + _peerConnection = peerConnection; + + calculateQOS({ + timeStamp: OT.$.now() }); - }); - }); + }; - // - // 5. Indexes - // + this.stopCollecting = function () { + _peerConnection = null; + }; + }; - /** - * @param {number} pointer The |pointer| to search for. - * @param {Array.} index The |index| to search within. - * @return {?number} The code point corresponding to |pointer| in |index|, - * or null if |code point| is not in |index|. - */ - function indexCodePointFor(pointer, index) { - return (index || [])[pointer] || null; - } + // Recalculate the stats in 30 sec + OT.PeerConnection.QOS.INTERVAL = 30000; +})(); - /** - * @param {number} code_point The |code point| to search for. - * @param {Array.} index The |index| to search within. - * @return {?number} The first pointer corresponding to |code point| in - * |index|, or null if |code point| is not in |index|. - */ - function indexPointerFor(code_point, index) { - var pointer = index.indexOf(code_point); - return pointer === -1 ? null : pointer; - } +// tb_require('../../helpers/helpers.js') +// tb_require('./peer_connection.js') - /** @type {Object.|Array.>)>} */ - var indexes = global['encoding-indexes'] || {}; +OT.PeerConnections = (function() { + var _peerConnections = {}; - /** - * @param {number} pointer The |pointer| to search for in the gb18030 index. - * @return {?number} The code point corresponding to |pointer| in |index|, - * or null if |code point| is not in the gb18030 index. - */ - function indexGB18030CodePointFor(pointer) { - if ((pointer > 39419 && pointer < 189000) || (pointer > 1237575)) { - return null; - } - var /** @type {number} */ offset = 0, - /** @type {number} */ code_point_offset = 0, - /** @type {Array.>} */ index = indexes['gb18030']; - var i; - for (i = 0; i < index.length; ++i) { - var entry = index[i]; - if (entry[0] <= pointer) { - offset = entry[0]; - code_point_offset = entry[1]; - } else { - break; + return { + add: function(remoteConnection, streamId, config) { + var key = remoteConnection.id + '_' + streamId, + ref = _peerConnections[key]; + + if (!ref) { + ref = _peerConnections[key] = { + count: 0, + pc: new OT.PeerConnection(config) + }; + } + + // increase the PCs ref count by 1 + ref.count += 1; + + return ref.pc; + }, + + remove: function(remoteConnection, streamId) { + var key = remoteConnection.id + '_' + streamId, + ref = _peerConnections[key]; + + if (ref) { + ref.count -= 1; + + if (ref.count === 0) { + ref.pc.disconnect(); + delete _peerConnections[key]; + } } } - return code_point_offset + pointer - offset; - } + }; +})(); +// tb_require('../../helpers/helpers.js') +// tb_require('../messaging/raptor/raptor.js') +// tb_require('./peer_connections.js') - /** - * @param {number} code_point The |code point| to locate in the gb18030 index. - * @return {number} The first pointer corresponding to |code point| in the - * gb18030 index. - */ - function indexGB18030PointerFor(code_point) { - var /** @type {number} */ offset = 0, - /** @type {number} */ pointer_offset = 0, - /** @type {Array.>} */ index = indexes['gb18030']; - var i; - for (i = 0; i < index.length; ++i) { - var entry = index[i]; - if (entry[1] <= code_point) { - offset = entry[1]; - pointer_offset = entry[0]; - } else { - break; +/* + * Abstracts PeerConnection related stuff away from OT.Subscriber. + * + * Responsible for: + * * setting up the underlying PeerConnection (delegates to OT.PeerConnections) + * * triggering a connected event when the Peer connection is opened + * * triggering a disconnected event when the Peer connection is closed + * * creating a video element when a stream is added + * * responding to stream removed intelligently + * * providing a destroy method + * * providing a processMessage method + * + * Once the PeerConnection is connected and the video element playing it + * triggers the connected event + * + * Triggers the following events + * * connected + * * disconnected + * * remoteStreamAdded + * * remoteStreamRemoved + * * error + * + */ + +OT.SubscriberPeerConnection = function(remoteConnection, session, stream, + subscriber, properties) { + var _peerConnection, + _destroyed = false, + _hasRelayCandidates = false, + _onPeerClosed, + _onRemoteStreamAdded, + _onRemoteStreamRemoved, + _onPeerError, + _relayMessageToPeer, + _setEnabledOnStreamTracksCurry, + _onQOS; + + // Private + _onPeerClosed = function() { + this.destroy(); + this.trigger('disconnected', this); + }; + + _onRemoteStreamAdded = function(remoteRTCStream) { + this.trigger('remoteStreamAdded', remoteRTCStream, this); + }; + + _onRemoteStreamRemoved = function(remoteRTCStream) { + this.trigger('remoteStreamRemoved', remoteRTCStream, this); + }; + + // Note: All Peer errors are fatal right now. + _onPeerError = function(errorReason, prefix) { + this.trigger('error', null, errorReason, this, prefix); + }; + + _relayMessageToPeer = OT.$.bind(function(type, payload) { + if (!_hasRelayCandidates){ + var extractCandidates = type === OT.Raptor.Actions.CANDIDATE || + type === OT.Raptor.Actions.OFFER || + type === OT.Raptor.Actions.ANSWER || + type === OT.Raptor.Actions.PRANSWER ; + + if (extractCandidates) { + var message = (type === OT.Raptor.Actions.CANDIDATE) ? payload.candidate : payload.sdp; + _hasRelayCandidates = message.indexOf('typ relay') !== -1; } } - return pointer_offset + code_point - offset; - } - // - // 7. The encoding - // + switch(type) { + case OT.Raptor.Actions.ANSWER: + case OT.Raptor.Actions.PRANSWER: + this.trigger('connected'); - // 7.1 utf-8 + session._.jsepAnswerP2p(stream.id, subscriber.widgetId, payload.sdp); + break; - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function UTF8Decoder(options) { - var fatal = options.fatal; - var /** @type {number} */ utf8_code_point = 0, - /** @type {number} */ utf8_bytes_needed = 0, - /** @type {number} */ utf8_bytes_seen = 0, - /** @type {number} */ utf8_lower_boundary = 0; + case OT.Raptor.Actions.OFFER: + session._.jsepOfferP2p(stream.id, subscriber.widgetId, payload.sdp); + break; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite === EOF_byte) { - if (utf8_bytes_needed !== 0) { - return decoderError(fatal); - } - return EOF_code_point; + case OT.Raptor.Actions.CANDIDATE: + session._.jsepCandidateP2p(stream.id, subscriber.widgetId, payload); + break; + } + }, this); + + // Helper method used by subscribeToAudio/subscribeToVideo + _setEnabledOnStreamTracksCurry = function(isVideo) { + var method = 'get' + (isVideo ? 'Video' : 'Audio') + 'Tracks'; + + return function(enabled) { + var remoteStreams = _peerConnection.remoteStreams(), + tracks, + stream; + + if (remoteStreams.length === 0 || !remoteStreams[0][method]) { + // either there is no remote stream or we are in a browser that doesn't + // expose the media tracks (Firefox) + return; } - byte_pointer.offset(1); - if (utf8_bytes_needed === 0) { - if (inRange(bite, 0x00, 0x7F)) { - return bite; - } - if (inRange(bite, 0xC2, 0xDF)) { - utf8_bytes_needed = 1; - utf8_lower_boundary = 0x80; - utf8_code_point = bite - 0xC0; - } else if (inRange(bite, 0xE0, 0xEF)) { - utf8_bytes_needed = 2; - utf8_lower_boundary = 0x800; - utf8_code_point = bite - 0xE0; - } else if (inRange(bite, 0xF0, 0xF4)) { - utf8_bytes_needed = 3; - utf8_lower_boundary = 0x10000; - utf8_code_point = bite - 0xF0; - } else { - return decoderError(fatal); + for (var i=0, num=remoteStreams.length; i 0) { - var temp = div(code_point, Math.pow(64, count - 1)); - result = output_byte_stream.emit(0x80 + (temp % 64)); - count -= 1; + _onQOS = OT.$.bind(function _onQOS (parsedStats, prevStats) { + this.trigger('qos', parsedStats, prevStats); + }, this); + + OT.$.eventing(this); + + // Public + this.destroy = function() { + if (_destroyed) return; + _destroyed = true; + + if (_peerConnection) { + var numDelegates = _peerConnection.unregisterMessageDelegate(_relayMessageToPeer); + + // Only clean up the PeerConnection if there isn't another Subscriber using it + if (numDelegates === 0) { + // Unsubscribe us from the stream, if it hasn't already been destroyed + if (session && session.isConnected() && stream && !stream.destroyed) { + // Notify the server components + session._.subscriberDestroy(stream, subscriber); + } + + // Ref: OPENTOK-2458 disable all audio tracks before removing it. + this.subscribeToAudio(false); } - return result; - }; - } + OT.PeerConnections.remove(remoteConnection, stream.id); + } + _peerConnection = null; + this.off(); + }; - name_to_encoding['utf-8'].getEncoder = function(options) { - return new UTF8Encoder(options); + this.processMessage = function(type, message) { + _peerConnection.processMessage(type, message); }; - name_to_encoding['utf-8'].getDecoder = function(options) { - return new UTF8Decoder(options); + + this.getStats = function(callback) { + _peerConnection.getStats(callback); }; - // - // 8. Legacy single-byte encodings - // + this.subscribeToAudio = _setEnabledOnStreamTracksCurry(false); + this.subscribeToVideo = _setEnabledOnStreamTracksCurry(true); - /** - * @constructor - * @param {Array.} index The encoding index. - * @param {{fatal: boolean}} options - */ - function SingleByteDecoder(index, options) { - var fatal = options.fatal; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite === EOF_byte) { - return EOF_code_point; - } - byte_pointer.offset(1); - if (inRange(bite, 0x00, 0x7F)) { - return bite; - } - var code_point = index[bite - 0x80]; - if (code_point === null) { - return decoderError(fatal); - } - return code_point; - }; - } + this.hasRelayCandidates = function() { + return _hasRelayCandidates; + }; - /** - * @constructor - * @param {Array.} index The encoding index. - * @param {{fatal: boolean}} options - */ - function SingleByteEncoder(index, options) { - var fatal = options.fatal; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if (inRange(code_point, 0x0000, 0x007F)) { - return output_byte_stream.emit(code_point); - } - var pointer = indexPointerFor(code_point, index); - if (pointer === null) { - encoderError(code_point); - } - return output_byte_stream.emit(pointer + 0x80); - }; - } + // Init + this.init = function() { + _peerConnection = OT.PeerConnections.add(remoteConnection, stream.streamId, {}); - (function() { - ['ibm864', 'ibm866', 'iso-8859-2', 'iso-8859-3', 'iso-8859-4', - 'iso-8859-5', 'iso-8859-6', 'iso-8859-7', 'iso-8859-8', 'iso-8859-10', - 'iso-8859-13', 'iso-8859-14', 'iso-8859-15', 'iso-8859-16', 'koi8-r', - 'koi8-u', 'macintosh', 'windows-874', 'windows-1250', 'windows-1251', - 'windows-1252', 'windows-1253', 'windows-1254', 'windows-1255', - 'windows-1256', 'windows-1257', 'windows-1258', 'x-mac-cyrillic' - ].forEach(function(name) { - var encoding = name_to_encoding[name]; - var index = indexes[name]; - encoding.getDecoder = function(options) { - return new SingleByteDecoder(index, options); - }; - encoding.getEncoder = function(options) { - return new SingleByteEncoder(index, options); - }; - }); - }()); + _peerConnection.on({ + close: _onPeerClosed, + streamAdded: _onRemoteStreamAdded, + streamRemoved: _onRemoteStreamRemoved, + error: _onPeerError, + qos: _onQOS + }, this); - // - // 9. Legacy multi-byte Chinese (simplified) encodings - // + var numDelegates = _peerConnection.registerMessageDelegate(_relayMessageToPeer); - // 9.1 gbk + // If there are already remoteStreams, add them immediately + if (_peerConnection.remoteStreams().length > 0) { + OT.$.forEach(_peerConnection.remoteStreams(), _onRemoteStreamAdded, this); + } else if (numDelegates === 1) { + // We only bother with the PeerConnection negotiation if we don't already + // have a remote stream. + + var channelsToSubscribeTo; + + if (properties.subscribeToVideo || properties.subscribeToAudio) { + var audio = stream.getChannelsOfType('audio'), + video = stream.getChannelsOfType('video'); - /** - * @constructor - * @param {boolean} gb18030 True if decoding gb18030, false otherwise. - * @param {{fatal: boolean}} options - */ - function GBKDecoder(gb18030, options) { - var fatal = options.fatal; - var /** @type {number} */ gbk_first = 0x00, - /** @type {number} */ gbk_second = 0x00, - /** @type {number} */ gbk_third = 0x00; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite === EOF_byte && gbk_first === 0x00 && - gbk_second === 0x00 && gbk_third === 0x00) { - return EOF_code_point; - } - if (bite === EOF_byte && - (gbk_first !== 0x00 || gbk_second !== 0x00 || gbk_third !== 0x00)) { - gbk_first = 0x00; - gbk_second = 0x00; - gbk_third = 0x00; - decoderError(fatal); - } - byte_pointer.offset(1); - var code_point; - if (gbk_third !== 0x00) { - code_point = null; - if (inRange(bite, 0x30, 0x39)) { - code_point = indexGB18030CodePointFor( - (((gbk_first - 0x81) * 10 + (gbk_second - 0x30)) * 126 + - (gbk_third - 0x81)) * 10 + bite - 0x30); - } - gbk_first = 0x00; - gbk_second = 0x00; - gbk_third = 0x00; - if (code_point === null) { - byte_pointer.offset(-3); - return decoderError(fatal); - } - return code_point; + channelsToSubscribeTo = OT.$.map(audio, function(channel) { + return { + id: channel.id, + type: channel.type, + active: properties.subscribeToAudio + }; + }).concat(OT.$.map(video, function(channel) { + return { + id: channel.id, + type: channel.type, + active: properties.subscribeToVideo, + restrictFrameRate: properties.restrictFrameRate !== void 0 ? + properties.restrictFrameRate : false + }; + })); } - if (gbk_second !== 0x00) { - if (inRange(bite, 0x81, 0xFE)) { - gbk_third = bite; - return null; - } - byte_pointer.offset(-2); - gbk_first = 0x00; - gbk_second = 0x00; - return decoderError(fatal); + + session._.subscriberCreate(stream, subscriber, channelsToSubscribeTo, + OT.$.bind(function(err, message) { + if (err) { + this.trigger('error', err.message, this, 'Subscribe'); + } + if (_peerConnection) { + _peerConnection.setIceServers(OT.Raptor.parseIceServers(message)); + } + }, this)); + } + }; +}; + +// tb_require('../../helpers/helpers.js') +// tb_require('../messaging/raptor/raptor.js') +// tb_require('./peer_connections.js') + + +/* + * Abstracts PeerConnection related stuff away from OT.Publisher. + * + * Responsible for: + * * setting up the underlying PeerConnection (delegates to OT.PeerConnections) + * * triggering a connected event when the Peer connection is opened + * * triggering a disconnected event when the Peer connection is closed + * * providing a destroy method + * * providing a processMessage method + * + * Once the PeerConnection is connected and the video element playing it triggers + * the connected event + * + * Triggers the following events + * * connected + * * disconnected + */ +OT.PublisherPeerConnection = function(remoteConnection, session, streamId, webRTCStream) { + var _peerConnection, + _hasRelayCandidates = false, + _subscriberId = session._.subscriberMap[remoteConnection.id + '_' + streamId], + _onPeerClosed, + _onPeerError, + _relayMessageToPeer, + _onQOS; + + // Private + _onPeerClosed = function() { + this.destroy(); + this.trigger('disconnected', this); + }; + + // Note: All Peer errors are fatal right now. + _onPeerError = function(errorReason, prefix) { + this.trigger('error', null, errorReason, this, prefix); + this.destroy(); + }; + + _relayMessageToPeer = OT.$.bind(function(type, payload, uri) { + if (!_hasRelayCandidates){ + var extractCandidates = type === OT.Raptor.Actions.CANDIDATE || + type === OT.Raptor.Actions.OFFER || + type === OT.Raptor.Actions.ANSWER || + type === OT.Raptor.Actions.PRANSWER ; + + if (extractCandidates) { + var message = (type === OT.Raptor.Actions.CANDIDATE) ? payload.candidate : payload.sdp; + _hasRelayCandidates = message.indexOf('typ relay') !== -1; } - if (gbk_first !== 0x00) { - if (inRange(bite, 0x30, 0x39) && gb18030) { - gbk_second = bite; - return null; - } - var lead = gbk_first; - var pointer = null; - gbk_first = 0x00; - var offset = bite < 0x7F ? 0x40 : 0x41; - if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFE)) { - pointer = (lead - 0x81) * 190 + (bite - offset); - } - code_point = pointer === null ? null : - indexCodePointFor(pointer, indexes['gbk']); - if (pointer === null) { - byte_pointer.offset(-1); + } + + switch(type) { + case OT.Raptor.Actions.ANSWER: + case OT.Raptor.Actions.PRANSWER: + if (session.sessionInfo.p2pEnabled) { + session._.jsepAnswerP2p(streamId, _subscriberId, payload.sdp); + } else { + session._.jsepAnswer(streamId, payload.sdp); } - if (code_point === null) { - return decoderError(fatal); + + break; + + case OT.Raptor.Actions.OFFER: + this.trigger('connected'); + session._.jsepOffer(uri, payload.sdp); + + break; + + case OT.Raptor.Actions.CANDIDATE: + if (session.sessionInfo.p2pEnabled) { + session._.jsepCandidateP2p(streamId, _subscriberId, payload); + + } else { + session._.jsepCandidate(streamId, payload); } - return code_point; - } - if (inRange(bite, 0x00, 0x7F)) { - return bite; - } - if (bite === 0x80) { - return 0x20AC; - } - if (inRange(bite, 0x81, 0xFE)) { - gbk_first = bite; - return null; - } - return decoderError(fatal); + } + }, this); + + _onQOS = OT.$.bind(function _onQOS (parsedStats, prevStats) { + this.trigger('qos', remoteConnection, parsedStats, prevStats); + }, this); + + OT.$.eventing(this); + + // Public + this.destroy = function() { + // Clean up our PeerConnection + if (_peerConnection) { + _peerConnection.off(); + OT.PeerConnections.remove(remoteConnection, streamId); + } + + _peerConnection = null; + }; + + this.processMessage = function(type, message) { + _peerConnection.processMessage(type, message); + }; + + // Init + this.init = function(iceServers) { + _peerConnection = OT.PeerConnections.add(remoteConnection, streamId, { + iceServers: iceServers + }); + + _peerConnection.on({ + close: _onPeerClosed, + error: _onPeerError, + qos: _onQOS + }, this); + + _peerConnection.registerMessageDelegate(_relayMessageToPeer); + _peerConnection.addLocalStream(webRTCStream); + + this.remoteConnection = function() { + return remoteConnection; }; - } - /** - * @constructor - * @param {boolean} gb18030 True if decoding gb18030, false otherwise. - * @param {{fatal: boolean}} options - */ - function GBKEncoder(gb18030, options) { - var fatal = options.fatal; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if (inRange(code_point, 0x0000, 0x007F)) { - return output_byte_stream.emit(code_point); - } - var pointer = indexPointerFor(code_point, indexes['gbk']); - if (pointer !== null) { - var lead = div(pointer, 190) + 0x81; - var trail = pointer % 190; - var offset = trail < 0x3F ? 0x40 : 0x41; - return output_byte_stream.emit(lead, trail + offset); - } - if (pointer === null && !gb18030) { - return encoderError(code_point); - } - pointer = indexGB18030PointerFor(code_point); - var byte1 = div(div(div(pointer, 10), 126), 10); - pointer = pointer - byte1 * 10 * 126 * 10; - var byte2 = div(div(pointer, 10), 126); - pointer = pointer - byte2 * 10 * 126; - var byte3 = div(pointer, 10); - var byte4 = pointer - byte3 * 10; - return output_byte_stream.emit(byte1 + 0x81, - byte2 + 0x30, - byte3 + 0x81, - byte4 + 0x30); + this.hasRelayCandidates = function() { + return _hasRelayCandidates; }; + + }; +}; + + +// tb_require('../helpers.js') +// tb_require('./web_rtc.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT, OTPlugin */ + +var videoContentResizesMixin = function(self, domElement) { + + var width = domElement.videoWidth, + height = domElement.videoHeight, + stopped = true; + + function actor() { + if (stopped) { + return; + } + if (width !== domElement.videoWidth || height !== domElement.videoHeight) { + self.trigger('videoDimensionsChanged', + { width: width, height: height }, + { width: domElement.videoWidth, height: domElement.videoHeight } + ); + width = domElement.videoWidth; + height = domElement.videoHeight; + } + waiter(); + } + + function waiter() { + self.whenTimeIncrements(function() { + window.requestAnimationFrame(actor); + }); } - name_to_encoding['gbk'].getEncoder = function(options) { - return new GBKEncoder(false, options); + self.startObservingSize = function() { + stopped = false; + waiter(); }; - name_to_encoding['gbk'].getDecoder = function(options) { - return new GBKDecoder(false, options); + + self.stopObservingSize = function() { + stopped = true; }; - // 9.2 gb18030 - name_to_encoding['gb18030'].getEncoder = function(options) { - return new GBKEncoder(true, options); +}; + +(function(window) { + + var VideoOrientationTransforms = { + 0: 'rotate(0deg)', + 270: 'rotate(90deg)', + 90: 'rotate(-90deg)', + 180: 'rotate(180deg)' }; - name_to_encoding['gb18030'].getDecoder = function(options) { - return new GBKDecoder(true, options); + + OT.VideoOrientation = { + ROTATED_NORMAL: 0, + ROTATED_LEFT: 270, + ROTATED_RIGHT: 90, + ROTATED_UPSIDE_DOWN: 180 }; - // 9.3 hz-gb-2312 + var DefaultAudioVolume = 50; + + var DEGREE_TO_RADIANS = Math.PI * 2 / 360; + + // + // + // var _videoElement = new OT.VideoElement({ + // fallbackText: 'blah' + // }, errorHandler); + // + // _videoElement.bindToStream(webRtcStream, completion); // => VideoElement + // _videoElement.appendTo(DOMElement) // => VideoElement + // + // _videoElement.domElement // => DomNode + // + // _videoElement.imgData // => PNG Data string + // + // _videoElement.orientation = OT.VideoOrientation.ROTATED_LEFT; + // + // _videoElement.unbindStream(); + // _videoElement.destroy() // => Completely cleans up and + // removes the video element + // + // + OT.VideoElement = function(/* optional */ options/*, optional errorHandler*/) { + var _options = OT.$.defaults( options && !OT.$.isFunction(options) ? options : {}, { + fallbackText: 'Sorry, Web RTC is not available in your browser' + }), + + errorHandler = OT.$.isFunction(arguments[arguments.length-1]) ? + arguments[arguments.length-1] : void 0, + + orientationHandler = OT.$.bind(function(orientation) { + this.trigger('orientationChanged', orientation); + }, this), + + _videoElement = OTPlugin.isInstalled() ? + new PluginVideoElement(_options, errorHandler, orientationHandler) : + new NativeDOMVideoElement(_options, errorHandler, orientationHandler), + _streamBound = false, + _stream, + _preInitialisedVolue; + + OT.$.eventing(this); + + _videoElement.on('videoDimensionsChanged', OT.$.bind(function(oldValue, newValue) { + this.trigger('videoDimensionsChanged', oldValue, newValue); + }, this)); + + _videoElement.on('mediaStopped', OT.$.bind(function() { + this.trigger('mediaStopped'); + }, this)); + + // Public Properties + OT.$.defineProperties(this, { - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function HZGB2312Decoder(options) { - var fatal = options.fatal; - var /** @type {boolean} */ hzgb2312 = false, - /** @type {number} */ hzgb2312_lead = 0x00; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite === EOF_byte && hzgb2312_lead === 0x00) { - return EOF_code_point; - } - if (bite === EOF_byte && hzgb2312_lead !== 0x00) { - hzgb2312_lead = 0x00; - return decoderError(fatal); - } - byte_pointer.offset(1); - if (hzgb2312_lead === 0x7E) { - hzgb2312_lead = 0x00; - if (bite === 0x7B) { - hzgb2312 = true; - return null; - } - if (bite === 0x7D) { - hzgb2312 = false; - return null; - } - if (bite === 0x7E) { - return 0x007E; + domElement: { + get: function() { + return _videoElement.domElement(); } - if (bite === 0x0A) { - return null; + }, + + videoWidth: { + get: function() { + return _videoElement['video' + (this.isRotated() ? 'Height' : 'Width')](); } - byte_pointer.offset(-1); - return decoderError(fatal); - } - if (hzgb2312_lead !== 0x00) { - var lead = hzgb2312_lead; - hzgb2312_lead = 0x00; - var code_point = null; - if (inRange(bite, 0x21, 0x7E)) { - code_point = indexCodePointFor((lead - 1) * 190 + - (bite + 0x3F), indexes['gbk']); + }, + + videoHeight: { + get: function() { + return _videoElement['video' + (this.isRotated() ? 'Width' : 'Height')](); } - if (bite === 0x0A) { - hzgb2312 = false; + }, + + aspectRatio: { + get: function() { + return (this.videoWidth() + 0.0) / this.videoHeight(); } - if (code_point === null) { - return decoderError(fatal); + }, + + isRotated: { + get: function() { + return _videoElement.isRotated(); } - return code_point; - } - if (bite === 0x7E) { - hzgb2312_lead = 0x7E; - return null; - } - if (hzgb2312) { - if (inRange(bite, 0x20, 0x7F)) { - hzgb2312_lead = bite; - return null; + }, + + orientation: { + get: function() { + return _videoElement.orientation(); + }, + set: function(orientation) { + _videoElement.orientation(orientation); } - if (bite === 0x0A) { - hzgb2312 = false; + }, + + audioChannelType: { + get: function() { + return _videoElement.audioChannelType(); + }, + set: function(type) { + _videoElement.audioChannelType(type); } - return decoderError(fatal); - } - if (inRange(bite, 0x00, 0x7F)) { - return bite; } - return decoderError(fatal); - }; - } + }); - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function HZGB2312Encoder(options) { - var fatal = options.fatal; - var hzgb2312 = false; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if (inRange(code_point, 0x0000, 0x007F) && hzgb2312) { - code_point_pointer.offset(-1); - hzgb2312 = false; - return output_byte_stream.emit(0x7E, 0x7D); - } - if (code_point === 0x007E) { - return output_byte_stream.emit(0x7E, 0x7E); - } - if (inRange(code_point, 0x0000, 0x007F)) { - return output_byte_stream.emit(code_point); - } - if (!hzgb2312) { - code_point_pointer.offset(-1); - hzgb2312 = true; - return output_byte_stream.emit(0x7E, 0x7B); - } - var pointer = indexPointerFor(code_point, indexes['gbk']); - if (pointer === null) { - return encoderError(code_point); - } - var lead = div(pointer, 190) + 1; - var trail = pointer % 190 - 0x3F; - if (!inRange(lead, 0x21, 0x7E) || !inRange(trail, 0x21, 0x7E)) { - return encoderError(code_point); - } - return output_byte_stream.emit(lead, trail); + // Public Methods + + this.imgData = function() { + return _videoElement.imgData(); }; - } - name_to_encoding['hz-gb-2312'].getEncoder = function(options) { - return new HZGB2312Encoder(options); - }; - name_to_encoding['hz-gb-2312'].getDecoder = function(options) { - return new HZGB2312Decoder(options); - }; + this.appendTo = function(parentDomElement) { + _videoElement.appendTo(parentDomElement); + return this; + }; - // - // 10. Legacy multi-byte Chinese (traditional) encodings - // + this.bindToStream = function(webRtcStream, completion) { + _streamBound = false; + _stream = webRtcStream; - // 10.1 big5 + _videoElement.bindToStream(webRtcStream, OT.$.bind(function(err) { + if (err) { + completion(err); + return; + } - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function Big5Decoder(options) { - var fatal = options.fatal; - var /** @type {number} */ big5_lead = 0x00, - /** @type {?number} */ big5_pending = null; + _streamBound = true; - /** - * @param {ByteInputStream} byte_pointer The byte steram to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - // NOTE: Hack to support emitting two code points - if (big5_pending !== null) { - var pending = big5_pending; - big5_pending = null; - return pending; - } - var bite = byte_pointer.get(); - if (bite === EOF_byte && big5_lead === 0x00) { - return EOF_code_point; - } - if (bite === EOF_byte && big5_lead !== 0x00) { - big5_lead = 0x00; - return decoderError(fatal); - } - byte_pointer.offset(1); - if (big5_lead !== 0x00) { - var lead = big5_lead; - var pointer = null; - big5_lead = 0x00; - var offset = bite < 0x7F ? 0x40 : 0x62; - if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0xA1, 0xFE)) { - pointer = (lead - 0x81) * 157 + (bite - offset); - } - if (pointer === 1133) { - big5_pending = 0x0304; - return 0x00CA; - } - if (pointer === 1135) { - big5_pending = 0x030C; - return 0x00CA; - } - if (pointer === 1164) { - big5_pending = 0x0304; - return 0x00EA; - } - if (pointer === 1166) { - big5_pending = 0x030C; - return 0x00EA; - } - var code_point = (pointer === null) ? null : - indexCodePointFor(pointer, indexes['big5']); - if (pointer === null) { - byte_pointer.offset(-1); - } - if (code_point === null) { - return decoderError(fatal); + if (_preInitialisedVolue) { + this.setAudioVolume(_preInitialisedVolue); + _preInitialisedVolue = null; } - return code_point; - } - if (inRange(bite, 0x00, 0x7F)) { - return bite; - } - if (inRange(bite, 0x81, 0xFE)) { - big5_lead = bite; - return null; - } - return decoderError(fatal); - }; - } - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function Big5Encoder(options) { - var fatal = options.fatal; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if (inRange(code_point, 0x0000, 0x007F)) { - return output_byte_stream.emit(code_point); - } - var pointer = indexPointerFor(code_point, indexes['big5']); - if (pointer === null) { - return encoderError(code_point); - } - var lead = div(pointer, 157) + 0x81; - //if (lead < 0xA1) { - // return encoderError(code_point); - //} - var trail = pointer % 157; - var offset = trail < 0x3F ? 0x40 : 0x62; - return output_byte_stream.emit(lead, trail + offset); - }; - } + _videoElement.on('aspectRatioAvailable', + OT.$.bind(this.trigger, this, 'aspectRatioAvailable')); - name_to_encoding['big5'].getEncoder = function(options) { - return new Big5Encoder(options); - }; - name_to_encoding['big5'].getDecoder = function(options) { - return new Big5Decoder(options); - }; + completion(null); + }, this)); + return this; + }; - // - // 11. Legacy multi-byte Japanese encodings - // + this.unbindStream = function() { + if (!_stream) return this; - // 11.1 euc.jp + _stream = null; + _videoElement.unbindStream(); + return this; + }; - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function EUCJPDecoder(options) { - var fatal = options.fatal; - var /** @type {number} */ eucjp_first = 0x00, - /** @type {number} */ eucjp_second = 0x00; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite === EOF_byte) { - if (eucjp_first === 0x00 && eucjp_second === 0x00) { - return EOF_code_point; - } - eucjp_first = 0x00; - eucjp_second = 0x00; - return decoderError(fatal); - } - byte_pointer.offset(1); + this.setAudioVolume = function (value) { + if (_streamBound) _videoElement.setAudioVolume( OT.$.roundFloat(value / 100, 2) ); + else _preInitialisedVolue = value; - var lead, code_point; - if (eucjp_second !== 0x00) { - lead = eucjp_second; - eucjp_second = 0x00; - code_point = null; - if (inRange(lead, 0xA1, 0xFE) && inRange(bite, 0xA1, 0xFE)) { - code_point = indexCodePointFor((lead - 0xA1) * 94 + bite - 0xA1, - indexes['jis0212']); - } - if (!inRange(bite, 0xA1, 0xFE)) { - byte_pointer.offset(-1); - } - if (code_point === null) { - return decoderError(fatal); - } - return code_point; - } - if (eucjp_first === 0x8E && inRange(bite, 0xA1, 0xDF)) { - eucjp_first = 0x00; - return 0xFF61 + bite - 0xA1; - } - if (eucjp_first === 0x8F && inRange(bite, 0xA1, 0xFE)) { - eucjp_first = 0x00; - eucjp_second = bite; - return null; - } - if (eucjp_first !== 0x00) { - lead = eucjp_first; - eucjp_first = 0x00; - code_point = null; - if (inRange(lead, 0xA1, 0xFE) && inRange(bite, 0xA1, 0xFE)) { - code_point = indexCodePointFor((lead - 0xA1) * 94 + bite - 0xA1, - indexes['jis0208']); - } - if (!inRange(bite, 0xA1, 0xFE)) { - byte_pointer.offset(-1); - } - if (code_point === null) { - return decoderError(fatal); - } - return code_point; - } - if (inRange(bite, 0x00, 0x7F)) { - return bite; - } - if (bite === 0x8E || bite === 0x8F || (inRange(bite, 0xA1, 0xFE))) { - eucjp_first = bite; - return null; - } - return decoderError(fatal); + return this; }; - } - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function EUCJPEncoder(options) { - var fatal = options.fatal; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if (inRange(code_point, 0x0000, 0x007F)) { - return output_byte_stream.emit(code_point); - } - if (code_point === 0x00A5) { - return output_byte_stream.emit(0x5C); - } - if (code_point === 0x203E) { - return output_byte_stream.emit(0x7E); - } - if (inRange(code_point, 0xFF61, 0xFF9F)) { - return output_byte_stream.emit(0x8E, code_point - 0xFF61 + 0xA1); - } + this.getAudioVolume = function () { + if (_streamBound) return parseInt(_videoElement.getAudioVolume() * 100, 10); + else return _preInitialisedVolue || 50; + }; + + + this.whenTimeIncrements = function (callback, context) { + _videoElement.whenTimeIncrements(callback, context); + return this; + }; - var pointer = indexPointerFor(code_point, indexes['jis0208']); - if (pointer === null) { - return encoderError(code_point); - } - var lead = div(pointer, 94) + 0xA1; - var trail = pointer % 94 + 0xA1; - return output_byte_stream.emit(lead, trail); + this.onRatioAvailable = function(callabck) { + _videoElement.onRatioAvailable(callabck) ; + return this; }; - } - name_to_encoding['euc-jp'].getEncoder = function(options) { - return new EUCJPEncoder(options); - }; - name_to_encoding['euc-jp'].getDecoder = function(options) { - return new EUCJPDecoder(options); + this.destroy = function () { + // unbind all events so they don't fire after the object is dead + this.off(); + + _videoElement.destroy(); + return void 0; + }; }; - // 11.2 iso-2022-jp + var PluginVideoElement = function PluginVideoElement (options, + errorHandler, + orientationChangedHandler) { + var _videoProxy, + _parentDomElement, + _ratioAvailable = false, + _ratioAvailableListeners = []; - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function ISO2022JPDecoder(options) { - var fatal = options.fatal; - /** @enum */ - var state = { - ASCII: 0, - escape_start: 1, - escape_middle: 2, - escape_final: 3, - lead: 4, - trail: 5, - Katakana: 6 - }; - var /** @type {number} */ iso2022jp_state = state.ASCII, - /** @type {boolean} */ iso2022jp_jis0212 = false, - /** @type {number} */ iso2022jp_lead = 0x00; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite !== EOF_byte) { - byte_pointer.offset(1); - } - switch (iso2022jp_state) { - default: - case state.ASCII: - if (bite === 0x1B) { - iso2022jp_state = state.escape_start; - return null; - } - if (inRange(bite, 0x00, 0x7F)) { - return bite; - } - if (bite === EOF_byte) { - return EOF_code_point; - } - return decoderError(fatal); + OT.$.eventing(this); - case state.escape_start: - if (bite === 0x24 || bite === 0x28) { - iso2022jp_lead = bite; - iso2022jp_state = state.escape_middle; - return null; - } - if (bite !== EOF_byte) { - byte_pointer.offset(-1); - } - iso2022jp_state = state.ASCII; - return decoderError(fatal); + canBeOrientatedMixin(this, + function() { return _videoProxy.domElement; }, + orientationChangedHandler); - case state.escape_middle: - var lead = iso2022jp_lead; - iso2022jp_lead = 0x00; - if (lead === 0x24 && (bite === 0x40 || bite === 0x42)) { - iso2022jp_jis0212 = false; - iso2022jp_state = state.lead; - return null; - } - if (lead === 0x24 && bite === 0x28) { - iso2022jp_state = state.escape_final; - return null; - } - if (lead === 0x28 && (bite === 0x42 || bite === 0x4A)) { - iso2022jp_state = state.ASCII; - return null; - } - if (lead === 0x28 && bite === 0x49) { - iso2022jp_state = state.Katakana; - return null; - } - if (bite === EOF_byte) { - byte_pointer.offset(-1); - } else { - byte_pointer.offset(-2); - } - iso2022jp_state = state.ASCII; - return decoderError(fatal); + /// Public methods - case state.escape_final: - if (bite === 0x44) { - iso2022jp_jis0212 = true; - iso2022jp_state = state.lead; - return null; - } - if (bite === EOF_byte) { - byte_pointer.offset(-2); - } else { - byte_pointer.offset(-3); - } - iso2022jp_state = state.ASCII; - return decoderError(fatal); + this.domElement = function() { + return _videoProxy ? _videoProxy.domElement : void 0; + }; - case state.lead: - if (bite === 0x0A) { - iso2022jp_state = state.ASCII; - return decoderError(fatal, 0x000A); - } - if (bite === 0x1B) { - iso2022jp_state = state.escape_start; - return null; - } - if (bite === EOF_byte) { - return EOF_code_point; - } - iso2022jp_lead = bite; - iso2022jp_state = state.trail; - return null; + this.videoWidth = function() { + return _videoProxy ? _videoProxy.getVideoWidth() : void 0; + }; - case state.trail: - iso2022jp_state = state.lead; - if (bite === EOF_byte) { - return decoderError(fatal); - } - var code_point = null; - var pointer = (iso2022jp_lead - 0x21) * 94 + bite - 0x21; - if (inRange(iso2022jp_lead, 0x21, 0x7E) && - inRange(bite, 0x21, 0x7E)) { - code_point = (iso2022jp_jis0212 === false) ? - indexCodePointFor(pointer, indexes['jis0208']) : - indexCodePointFor(pointer, indexes['jis0212']); - } - if (code_point === null) { - return decoderError(fatal); - } - return code_point; - - case state.Katakana: - if (bite === 0x1B) { - iso2022jp_state = state.escape_start; - return null; - } - if (inRange(bite, 0x21, 0x5F)) { - return 0xFF61 + bite - 0x21; - } - if (bite === EOF_byte) { - return EOF_code_point; - } - return decoderError(fatal); - } + this.videoHeight = function() { + return _videoProxy ? _videoProxy.getVideoHeight() : void 0; }; - } - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function ISO2022JPEncoder(options) { - var fatal = options.fatal; - /** @enum */ - var state = { - ASCII: 0, - lead: 1, - Katakana: 2 + this.imgData = function() { + return _videoProxy ? _videoProxy.getImgData() : null; }; - var /** @type {number} */ iso2022jp_state = state.ASCII; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if ((inRange(code_point, 0x0000, 0x007F) || - code_point === 0x00A5 || code_point === 0x203E) && - iso2022jp_state !== state.ASCII) { - code_point_pointer.offset(-1); - iso2022jp_state = state.ASCII; - return output_byte_stream.emit(0x1B, 0x28, 0x42); - } - if (inRange(code_point, 0x0000, 0x007F)) { - return output_byte_stream.emit(code_point); - } - if (code_point === 0x00A5) { - return output_byte_stream.emit(0x5C); - } - if (code_point === 0x203E) { - return output_byte_stream.emit(0x7E); - } - if (inRange(code_point, 0xFF61, 0xFF9F) && - iso2022jp_state !== state.Katakana) { - code_point_pointer.offset(-1); - iso2022jp_state = state.Katakana; - return output_byte_stream.emit(0x1B, 0x28, 0x49); - } - if (inRange(code_point, 0xFF61, 0xFF9F)) { - return output_byte_stream.emit(code_point - 0xFF61 - 0x21); - } - if (iso2022jp_state !== state.lead) { - code_point_pointer.offset(-1); - iso2022jp_state = state.lead; - return output_byte_stream.emit(0x1B, 0x24, 0x42); - } - var pointer = indexPointerFor(code_point, indexes['jis0208']); - if (pointer === null) { - return encoderError(code_point); - } - var lead = div(pointer, 94) + 0x21; - var trail = pointer % 94 + 0x21; - return output_byte_stream.emit(lead, trail); + + // Append the Video DOM element to a parent node + this.appendTo = function(parentDomElement) { + _parentDomElement = parentDomElement; + return this; }; - } - name_to_encoding['iso-2022-jp'].getEncoder = function(options) { - return new ISO2022JPEncoder(options); - }; - name_to_encoding['iso-2022-jp'].getDecoder = function(options) { - return new ISO2022JPDecoder(options); - }; + // Bind a stream to the video element. + this.bindToStream = function(webRtcStream, completion) { + if (!_parentDomElement) { + completion('The VideoElement must attached to a DOM node before a stream can be bound'); + return; + } - // 11.3 shift_jis + _videoProxy = webRtcStream._.render(); + _videoProxy.appendTo(_parentDomElement); + _videoProxy.show(function(error) { - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function ShiftJISDecoder(options) { - var fatal = options.fatal; - var /** @type {number} */ shiftjis_lead = 0x00; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite === EOF_byte && shiftjis_lead === 0x00) { - return EOF_code_point; - } - if (bite === EOF_byte && shiftjis_lead !== 0x00) { - shiftjis_lead = 0x00; - return decoderError(fatal); - } - byte_pointer.offset(1); - if (shiftjis_lead !== 0x00) { - var lead = shiftjis_lead; - shiftjis_lead = 0x00; - if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFC)) { - var offset = (bite < 0x7F) ? 0x40 : 0x41; - var lead_offset = (lead < 0xA0) ? 0x81 : 0xC1; - var code_point = indexCodePointFor((lead - lead_offset) * 188 + - bite - offset, indexes['jis0208']); - if (code_point === null) { - return decoderError(fatal); + if (!error) { + _ratioAvailable = true; + var listener; + while ((listener = _ratioAvailableListeners.shift())) { + listener(); } - return code_point; } - byte_pointer.offset(-1); - return decoderError(fatal); - } - if (inRange(bite, 0x00, 0x80)) { - return bite; - } - if (inRange(bite, 0xA1, 0xDF)) { - return 0xFF61 + bite - 0xA1; - } - if (inRange(bite, 0x81, 0x9F) || inRange(bite, 0xE0, 0xFC)) { - shiftjis_lead = bite; - return null; - } - return decoderError(fatal); + + completion(error); + }); + + return this; }; - } - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function ShiftJISEncoder(options) { - var fatal = options.fatal; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if (inRange(code_point, 0x0000, 0x0080)) { - return output_byte_stream.emit(code_point); - } - if (code_point === 0x00A5) { - return output_byte_stream.emit(0x5C); - } - if (code_point === 0x203E) { - return output_byte_stream.emit(0x7E); - } - if (inRange(code_point, 0xFF61, 0xFF9F)) { - return output_byte_stream.emit(code_point - 0xFF61 + 0xA1); + // Unbind the currently bound stream from the video element. + this.unbindStream = function() { + // TODO: some way to tell OTPlugin to release that stream and controller + + if (_videoProxy) { + _videoProxy.destroy(); + _parentDomElement = null; + _videoProxy = null; } - var pointer = indexPointerFor(code_point, indexes['jis0208']); - if (pointer === null) { - return encoderError(code_point); + + return this; + }; + + this.setAudioVolume = function(value) { + if (_videoProxy) _videoProxy.setVolume(value); + }; + + this.getAudioVolume = function() { + // Return the actual volume of the DOM element + if (_videoProxy) return _videoProxy.getVolume(); + return DefaultAudioVolume; + }; + + // see https://wiki.mozilla.org/WebAPI/AudioChannels + // The audioChannelType is not currently supported in the plugin. + this.audioChannelType = function(/* type */) { + return 'unknown'; + }; + + this.whenTimeIncrements = function(callback, context) { + // exists for compatibility with NativeVideoElement + OT.$.callAsync(OT.$.bind(callback, context)); + }; + + this.onRatioAvailable = function(callback) { + if(_ratioAvailable) { + callback(); + } else { + _ratioAvailableListeners.push(callback); } - var lead = div(pointer, 188); - var lead_offset = lead < 0x1F ? 0x81 : 0xC1; - var trail = pointer % 188; - var offset = trail < 0x3F ? 0x40 : 0x41; - return output_byte_stream.emit(lead + lead_offset, trail + offset); }; - } - name_to_encoding['shift_jis'].getEncoder = function(options) { - return new ShiftJISEncoder(options); - }; - name_to_encoding['shift_jis'].getDecoder = function(options) { - return new ShiftJISDecoder(options); + this.destroy = function() { + this.unbindStream(); + + return void 0; + }; }; - // - // 12. Legacy multi-byte Korean encodings - // - // 12.1 euc-kr + var NativeDOMVideoElement = function NativeDOMVideoElement (options, + errorHandler, + orientationChangedHandler) { + var _domElement, + _videoElementMovedWarning = false; - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function EUCKRDecoder(options) { - var fatal = options.fatal; - var /** @type {number} */ euckr_lead = 0x00; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite === EOF_byte && euckr_lead === 0) { - return EOF_code_point; - } - if (bite === EOF_byte && euckr_lead !== 0) { - euckr_lead = 0x00; - return decoderError(fatal); - } - byte_pointer.offset(1); - if (euckr_lead !== 0x00) { - var lead = euckr_lead; - var pointer = null; - euckr_lead = 0x00; + OT.$.eventing(this); + + /// Private API + var _onVideoError = OT.$.bind(function(event) { + var reason = 'There was an unexpected problem with the Video Stream: ' + + videoElementErrorCodeToStr(event.target.error.code); + errorHandler(reason, this, 'VideoElement'); + }, this), + + // The video element pauses itself when it's reparented, this is + // unfortunate. This function plays the video again and is triggered + // on the pause event. + _playVideoOnPause = function() { + if(!_videoElementMovedWarning) { + OT.warn('Video element paused, auto-resuming. If you intended to do this, ' + + 'use publishVideo(false) or subscribeToVideo(false) instead.'); - if (inRange(lead, 0x81, 0xC6)) { - var temp = (26 + 26 + 126) * (lead - 0x81); - if (inRange(bite, 0x41, 0x5A)) { - pointer = temp + bite - 0x41; - } else if (inRange(bite, 0x61, 0x7A)) { - pointer = temp + 26 + bite - 0x61; - } else if (inRange(bite, 0x81, 0xFE)) { - pointer = temp + 26 + 26 + bite - 0x81; + _videoElementMovedWarning = true; } - } - if (inRange(lead, 0xC7, 0xFD) && inRange(bite, 0xA1, 0xFE)) { - pointer = (26 + 26 + 126) * (0xC7 - 0x81) + (lead - 0xC7) * 94 + - (bite - 0xA1); - } + _domElement.play(); + }; - var code_point = (pointer === null) ? null : - indexCodePointFor(pointer, indexes['euc-kr']); - if (pointer === null) { - byte_pointer.offset(-1); - } - if (code_point === null) { - return decoderError(fatal); + + _domElement = createNativeVideoElement(options.fallbackText, options.attributes); + + // dirty but it is the only way right now to get the aspect ratio in FF + // any other event is triggered too early + var ratioAvailable = false; + var ratioAvailableListeners = []; + _domElement.addEventListener('timeupdate', function timeupdateHandler() { + var aspectRatio = _domElement.videoWidth / _domElement.videoHeight; + if (!isNaN(aspectRatio)) { + _domElement.removeEventListener('timeupdate', timeupdateHandler); + ratioAvailable = true; + var listener; + while ((listener = ratioAvailableListeners.shift())) { + listener(); } - return code_point; } + }); - if (inRange(bite, 0x00, 0x7F)) { - return bite; - } + _domElement.addEventListener('pause', _playVideoOnPause); - if (inRange(bite, 0x81, 0xFD)) { - euckr_lead = bite; - return null; - } + videoContentResizesMixin(this, _domElement); - return decoderError(fatal); + canBeOrientatedMixin(this, function() { return _domElement; }, orientationChangedHandler); + + /// Public methods + + this.domElement = function() { + return _domElement; }; - } - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function EUCKREncoder(options) { - var fatal = options.fatal; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if (inRange(code_point, 0x0000, 0x007F)) { - return output_byte_stream.emit(code_point); - } - var pointer = indexPointerFor(code_point, indexes['euc-kr']); - if (pointer === null) { - return encoderError(code_point); - } - var lead, trail; - if (pointer < ((26 + 26 + 126) * (0xC7 - 0x81))) { - lead = div(pointer, (26 + 26 + 126)) + 0x81; - trail = pointer % (26 + 26 + 126); - var offset = trail < 26 ? 0x41 : trail < 26 + 26 ? 0x47 : 0x4D; - return output_byte_stream.emit(lead, trail + offset); - } - pointer = pointer - (26 + 26 + 126) * (0xC7 - 0x81); - lead = div(pointer, 94) + 0xC7; - trail = pointer % 94 + 0xA1; - return output_byte_stream.emit(lead, trail); + this.videoWidth = function() { + return _domElement.videoWidth; }; - } - name_to_encoding['euc-kr'].getEncoder = function(options) { - return new EUCKREncoder(options); - }; - name_to_encoding['euc-kr'].getDecoder = function(options) { - return new EUCKRDecoder(options); - }; + this.videoHeight = function() { + return _domElement.videoHeight; + }; - // 12.2 iso-2022-kr + this.imgData = function() { + var canvas = OT.$.createElement('canvas', { + width: _domElement.videoWidth, + height: _domElement.videoHeight, + style: { display: 'none' } + }); - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function ISO2022KRDecoder(options) { - var fatal = options.fatal; - /** @enum */ - var state = { - ASCII: 0, - escape_start: 1, - escape_middle: 2, - escape_end: 3, - lead: 4, - trail: 5 - }; - var /** @type {number} */ iso2022kr_state = state.ASCII, - /** @type {number} */ iso2022kr_lead = 0x00; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite !== EOF_byte) { - byte_pointer.offset(1); - } - switch (iso2022kr_state) { - default: - case state.ASCII: - if (bite === 0x0E) { - iso2022kr_state = state.lead; - return null; - } - if (bite === 0x0F) { - return null; - } - if (bite === 0x1B) { - iso2022kr_state = state.escape_start; - return null; - } - if (inRange(bite, 0x00, 0x7F)) { - return bite; - } - if (bite === EOF_byte) { - return EOF_code_point; - } - return decoderError(fatal); - case state.escape_start: - if (bite === 0x24) { - iso2022kr_state = state.escape_middle; - return null; - } - if (bite !== EOF_byte) { - byte_pointer.offset(-1); - } - iso2022kr_state = state.ASCII; - return decoderError(fatal); - case state.escape_middle: - if (bite === 0x29) { - iso2022kr_state = state.escape_end; - return null; - } - if (bite === EOF_byte) { - byte_pointer.offset(-1); - } else { - byte_pointer.offset(-2); - } - iso2022kr_state = state.ASCII; - return decoderError(fatal); - case state.escape_end: - if (bite === 0x43) { - iso2022kr_state = state.ASCII; - return null; - } - if (bite === EOF_byte) { - byte_pointer.offset(-2); - } else { - byte_pointer.offset(-3); - } - iso2022kr_state = state.ASCII; - return decoderError(fatal); - case state.lead: - if (bite === 0x0A) { - iso2022kr_state = state.ASCII; - return decoderError(fatal, 0x000A); - } - if (bite === 0x0E) { - return null; - } - if (bite === 0x0F) { - iso2022kr_state = state.ASCII; - return null; - } - if (bite === EOF_byte) { - return EOF_code_point; - } - iso2022kr_lead = bite; - iso2022kr_state = state.trail; - return null; - case state.trail: - iso2022kr_state = state.lead; - if (bite === EOF_byte) { - return decoderError(fatal); - } - var code_point = null; - if (inRange(iso2022kr_lead, 0x21, 0x46) && - inRange(bite, 0x21, 0x7E)) { - code_point = indexCodePointFor((26 + 26 + 126) * - (iso2022kr_lead - 1) + - 26 + 26 + bite - 1, - indexes['euc-kr']); - } else if (inRange(iso2022kr_lead, 0x47, 0x7E) && - inRange(bite, 0x21, 0x7E)) { - code_point = indexCodePointFor((26 + 26 + 126) * (0xC7 - 0x81) + - (iso2022kr_lead - 0x47) * 94 + - (bite - 0x21), - indexes['euc-kr']); - } - if (code_point !== null) { - return code_point; - } - return decoderError(fatal); + document.body.appendChild(canvas); + try { + canvas.getContext('2d').drawImage(_domElement, 0, 0, canvas.width, canvas.height); + } catch(err) { + OT.warn('Cannot get image data yet'); + return null; } - }; - } + var imgData = canvas.toDataURL('image/png'); - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function ISO2022KREncoder(options) { - var fatal = options.fatal; - /** @enum */ - var state = { - ASCII: 0, - lead: 1 + OT.$.removeElement(canvas); + + return OT.$.trim(imgData.replace('data:image/png;base64,', '')); }; - var /** @type {boolean} */ iso2022kr_initialization = false, - /** @type {number} */ iso2022kr_state = state.ASCII; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - if (!iso2022kr_initialization) { - iso2022kr_initialization = true; - output_byte_stream.emit(0x1B, 0x24, 0x29, 0x43); - } - code_point_pointer.offset(1); - if (inRange(code_point, 0x0000, 0x007F) && - iso2022kr_state !== state.ASCII) { - code_point_pointer.offset(-1); - iso2022kr_state = state.ASCII; - return output_byte_stream.emit(0x0F); - } - if (inRange(code_point, 0x0000, 0x007F)) { - return output_byte_stream.emit(code_point); - } - if (iso2022kr_state !== state.lead) { - code_point_pointer.offset(-1); - iso2022kr_state = state.lead; - return output_byte_stream.emit(0x0E); - } - var pointer = indexPointerFor(code_point, indexes['euc-kr']); - if (pointer === null) { - return encoderError(code_point); - } - var lead, trail; - if (pointer < (26 + 26 + 126) * (0xC7 - 0x81)) { - lead = div(pointer, (26 + 26 + 126)) + 1; - trail = pointer % (26 + 26 + 126) - 26 - 26 + 1; - if (!inRange(lead, 0x21, 0x46) || !inRange(trail, 0x21, 0x7E)) { - return encoderError(code_point); - } - return output_byte_stream.emit(lead, trail); - } - pointer = pointer - (26 + 26 + 126) * (0xC7 - 0x81); - lead = div(pointer, 94) + 0x47; - trail = pointer % 94 + 0x21; - if (!inRange(lead, 0x47, 0x7E) || !inRange(trail, 0x21, 0x7E)) { - return encoderError(code_point); - } - return output_byte_stream.emit(lead, trail); + + // Append the Video DOM element to a parent node + this.appendTo = function(parentDomElement) { + parentDomElement.appendChild(_domElement); + return this; }; - } - name_to_encoding['iso-2022-kr'].getEncoder = function(options) { - return new ISO2022KREncoder(options); - }; - name_to_encoding['iso-2022-kr'].getDecoder = function(options) { - return new ISO2022KRDecoder(options); - }; + // Bind a stream to the video element. + this.bindToStream = function(webRtcStream, completion) { + var _this = this; + bindStreamToNativeVideoElement(_domElement, webRtcStream, function(err) { + if (err) { + completion(err); + return; + } + _this.startObservingSize(); - // - // 13. Legacy utf-16 encodings - // + webRtcStream.onended = function() { + _this.trigger('mediaStopped', this); + }; - // 13.1 utf-16 - /** - * @constructor - * @param {boolean} utf16_be True if big-endian, false if little-endian. - * @param {{fatal: boolean}} options - */ - function UTF16Decoder(utf16_be, options) { - var fatal = options.fatal; - var /** @type {?number} */ utf16_lead_byte = null, - /** @type {?number} */ utf16_lead_surrogate = null; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite === EOF_byte && utf16_lead_byte === null && - utf16_lead_surrogate === null) { - return EOF_code_point; - } - if (bite === EOF_byte && (utf16_lead_byte !== null || - utf16_lead_surrogate !== null)) { - return decoderError(fatal); - } - byte_pointer.offset(1); - if (utf16_lead_byte === null) { - utf16_lead_byte = bite; - return null; - } - var code_point; - if (utf16_be) { - code_point = (utf16_lead_byte << 8) + bite; - } else { - code_point = (bite << 8) + utf16_lead_byte; - } - utf16_lead_byte = null; - if (utf16_lead_surrogate !== null) { - var lead_surrogate = utf16_lead_surrogate; - utf16_lead_surrogate = null; - if (inRange(code_point, 0xDC00, 0xDFFF)) { - return 0x10000 + (lead_surrogate - 0xD800) * 0x400 + - (code_point - 0xDC00); - } - byte_pointer.offset(-2); - return decoderError(fatal); - } - if (inRange(code_point, 0xD800, 0xDBFF)) { - utf16_lead_surrogate = code_point; - return null; - } - if (inRange(code_point, 0xDC00, 0xDFFF)) { - return decoderError(fatal); - } - return code_point; - }; - } + _domElement.addEventListener('error', _onVideoError, false); + completion(null); + }); - /** - * @constructor - * @param {boolean} utf16_be True if big-endian, false if little-endian. - * @param {{fatal: boolean}} options - */ - function UTF16Encoder(utf16_be, options) { - var fatal = options.fatal; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - function convert_to_bytes(code_unit) { - var byte1 = code_unit >> 8; - var byte2 = code_unit & 0x00FF; - if (utf16_be) { - return output_byte_stream.emit(byte1, byte2); - } - return output_byte_stream.emit(byte2, byte1); - } - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if (inRange(code_point, 0xD800, 0xDFFF)) { - encoderError(code_point); - } - if (code_point <= 0xFFFF) { - return convert_to_bytes(code_point); - } - var lead = div((code_point - 0x10000), 0x400) + 0xD800; - var trail = ((code_point - 0x10000) % 0x400) + 0xDC00; - convert_to_bytes(lead); - return convert_to_bytes(trail); + return this; }; - } - name_to_encoding['utf-16'].getEncoder = function(options) { - return new UTF16Encoder(false, options); - }; - name_to_encoding['utf-16'].getDecoder = function(options) { - return new UTF16Decoder(false, options); - }; - // 13.2 utf-16be - name_to_encoding['utf-16be'].getEncoder = function(options) { - return new UTF16Encoder(true, options); - }; - name_to_encoding['utf-16be'].getDecoder = function(options) { - return new UTF16Decoder(true, options); - }; + // Unbind the currently bound stream from the video element. + this.unbindStream = function() { + if (_domElement) { + unbindNativeStream(_domElement); + } + this.stopObservingSize(); - // NOTE: currently unused - /** - * @param {string} label The encoding label. - * @param {ByteInputStream} input_stream The byte stream to test. - */ - function detectEncoding(label, input_stream) { - if (input_stream.match([0xFF, 0xFE])) { - input_stream.offset(2); - return 'utf-16'; - } - if (input_stream.match([0xFE, 0xFF])) { - input_stream.offset(2); - return 'utf-16be'; - } - if (input_stream.match([0xEF, 0xBB, 0xBF])) { - input_stream.offset(3); - return 'utf-8'; - } - return label; - } + return this; + }; - /** - * @param {string} label The encoding label. - * @param {ByteInputStream} input_stream The byte stream to test. - */ - function consumeBOM(label, input_stream) { - if (input_stream.match([0xFF, 0xFE]) && label === 'utf-16') { - input_stream.offset(2); - return; - } - if (input_stream.match([0xFE, 0xFF]) && label == 'utf-16be') { - input_stream.offset(2); - return; - } - if (input_stream.match([0xEF, 0xBB, 0xBF]) && label == 'utf-8') { - input_stream.offset(3); - return; - } - } + this.setAudioVolume = function(value) { + if (_domElement) _domElement.volume = value; + }; - // - // Implementation of Text Encoding Web API - // + this.getAudioVolume = function() { + // Return the actual volume of the DOM element + if (_domElement) return _domElement.volume; + return DefaultAudioVolume; + }; + + // see https://wiki.mozilla.org/WebAPI/AudioChannels + // The audioChannelType is currently only available in Firefox. This property returns + // "unknown" in other browser. The related HTML tag attribute is "mozaudiochannel" + this.audioChannelType = function(type) { + if (type !== void 0) { + _domElement.mozAudioChannelType = type; + } - /** @const */ var DEFAULT_ENCODING = 'utf-8'; + if ('mozAudioChannelType' in _domElement) { + return _domElement.mozAudioChannelType; + } else { + return 'unknown'; + } + }; - /** - * @constructor - * @param {string=} opt_encoding The label of the encoding; - * defaults to 'utf-8'. - * @param {{fatal: boolean}=} options - */ - function TextEncoder(opt_encoding, options) { - if (!this || this === global) { - return new TextEncoder(opt_encoding, options); - } - opt_encoding = opt_encoding ? String(opt_encoding) : DEFAULT_ENCODING; - options = Object(options); - /** @private */ - this._encoding = getEncoding(opt_encoding); - if (this._encoding === null || (this._encoding.name !== 'utf-8' && - this._encoding.name !== 'utf-16' && - this._encoding.name !== 'utf-16be')) - throw new TypeError('Unknown encoding: ' + opt_encoding); - /* @private @type {boolean} */ - this._streaming = false; - /** @private */ - this._encoder = null; - /* @private @type {{fatal: boolean}=} */ - this._options = { fatal: Boolean(options.fatal) }; + this.whenTimeIncrements = function(callback, context) { + if (_domElement) { + var lastTime, handler; + handler = OT.$.bind(function() { + if (_domElement) { + if (!lastTime || lastTime >= _domElement.currentTime) { + lastTime = _domElement.currentTime; + } else { + _domElement.removeEventListener('timeupdate', handler, false); + callback.call(context, this); + } + } + }, this); + _domElement.addEventListener('timeupdate', handler, false); + } + }; - if (Object.defineProperty) { - Object.defineProperty( - this, 'encoding', - { get: function() { return this._encoding.name; } }); - } else { - this.encoding = this._encoding.name; - } + this.destroy = function() { + this.unbindStream(); - return this; - } + if (_domElement) { + // Unbind this first, otherwise it will trigger when the + // video element is removed from the DOM. + _domElement.removeEventListener('pause', _playVideoOnPause); - TextEncoder.prototype = { - /** - * @param {string=} opt_string The string to encode. - * @param {{stream: boolean}=} options - */ - encode: function encode(opt_string, options) { - opt_string = opt_string ? String(opt_string) : ''; - options = Object(options); - // TODO: any options? - if (!this._streaming) { - this._encoder = this._encoding.getEncoder(this._options); + OT.$.removeElement(_domElement); + _domElement = null; } - this._streaming = Boolean(options.stream); - var bytes = []; - var output_stream = new ByteOutputStream(bytes); - var input_stream = new CodePointInputStream(opt_string); - while (input_stream.get() !== EOF_code_point) { - this._encoder.encode(output_stream, input_stream); - } - if (!this._streaming) { - var last_byte; - do { - last_byte = this._encoder.encode(output_stream, input_stream); - } while (last_byte !== EOF_byte); - this._encoder = null; + return void 0; + }; + + this.onRatioAvailable = function(callback) { + if(ratioAvailable) { + callback(); + } else { + ratioAvailableListeners.push(callback); } - return new Uint8Array(bytes); - } + }; }; +/// Private Helper functions - /** - * @constructor - * @param {string=} opt_encoding The label of the encoding; - * defaults to 'utf-8'. - * @param {{fatal: boolean}=} options - */ - function TextDecoder(opt_encoding, options) { - if (!this || this === global) { - return new TextDecoder(opt_encoding, options); - } - opt_encoding = opt_encoding ? String(opt_encoding) : DEFAULT_ENCODING; - options = Object(options); - /** @private */ - this._encoding = getEncoding(opt_encoding); - if (this._encoding === null) - throw new TypeError('Unknown encoding: ' + opt_encoding); + // A mixin to create the orientation API implementation on +self+ + // +getDomElementCallback+ is a function that the mixin will call when it wants to + // get the native Dom element for +self+. + // + // +initialOrientation+ sets the initial orientation (shockingly), it's currently unused + // so the initial value is actually undefined. + // + var canBeOrientatedMixin = function canBeOrientatedMixin (self, + getDomElementCallback, + orientationChangedHandler, + initialOrientation) { + var _orientation = initialOrientation; - /* @private @type {boolean} */ - this._streaming = false; - /** @private */ - this._decoder = null; - /* @private @type {{fatal: boolean}=} */ - this._options = { fatal: Boolean(options.fatal) }; + OT.$.defineProperties(self, { + isRotated: { + get: function() { + return this.orientation() && + (this.orientation().videoOrientation === 270 || + this.orientation().videoOrientation === 90); + } + }, - if (Object.defineProperty) { - Object.defineProperty( - this, 'encoding', - { get: function() { return this._encoding.name; } }); - } else { - this.encoding = this._encoding.name; - } + orientation: { + get: function() { return _orientation; }, + set: function(orientation) { + _orientation = orientation; - return this; - } + var transform = VideoOrientationTransforms[orientation.videoOrientation] || + VideoOrientationTransforms.ROTATED_NORMAL; - // TODO: Issue if input byte stream is offset by decoder - // TODO: BOM detection will not work if stream header spans multiple calls - // (last N bytes of previous stream may need to be retained?) - TextDecoder.prototype = { - /** - * @param {ArrayBufferView=} opt_view The buffer of bytes to decode. - * @param {{stream: boolean}=} options - */ - decode: function decode(opt_view, options) { - if (opt_view && !('buffer' in opt_view && 'byteOffset' in opt_view && - 'byteLength' in opt_view)) { - throw new TypeError('Expected ArrayBufferView'); - } else if (!opt_view) { - opt_view = new Uint8Array(0); - } - options = Object(options); + switch(OT.$.env.name) { + case 'Chrome': + case 'Safari': + getDomElementCallback().style.webkitTransform = transform; + break; - if (!this._streaming) { - this._decoder = this._encoding.getDecoder(this._options); - } - this._streaming = Boolean(options.stream); + case 'IE': + if (OT.$.env.version >= 9) { + getDomElementCallback().style.msTransform = transform; + } + else { + // So this basically defines matrix that represents a rotation + // of a single vector in a 2d basis. + // + // R = [cos(Theta) -sin(Theta)] + // [sin(Theta) cos(Theta)] + // + // Where Theta is the number of radians to rotate by + // + // Then to rotate the vector v: + // v' = Rv + // + // We then use IE8 Matrix filter property, which takes + // a 2x2 rotation matrix, to rotate our DOM element. + // + var radians = orientation.videoOrientation * DEGREE_TO_RADIANS, + element = getDomElementCallback(), + costheta = Math.cos(radians), + sintheta = Math.sin(radians); - var bytes = new Uint8Array(opt_view.buffer, - opt_view.byteOffset, - opt_view.byteLength); - var input_stream = new ByteInputStream(bytes); + // element.filters.item(0).M11 = costheta; + // element.filters.item(0).M12 = -sintheta; + // element.filters.item(0).M21 = sintheta; + // element.filters.item(0).M22 = costheta; - if (!this._BOMseen) { - // TODO: Don't do this until sufficient bytes are present - this._BOMseen = true; - consumeBOM(this._encoding.name, input_stream); - } + element.style.filter = 'progid:DXImageTransform.Microsoft.Matrix(' + + 'M11='+costheta+',' + + 'M12='+(-sintheta)+',' + + 'M21='+sintheta+',' + + 'M22='+costheta+',SizingMethod=\'auto expand\')'; + } + + + break; + + default: + // The standard version, just Firefox, Opera, and IE > 9 + getDomElementCallback().style.transform = transform; + } + + orientationChangedHandler(_orientation); - var output_stream = new CodePointOutputStream(), code_point; - while (input_stream.get() !== EOF_byte) { - code_point = this._decoder.decode(input_stream); - if (code_point !== null && code_point !== EOF_code_point) { - output_stream.emit(code_point); } - } - if (!this._streaming) { - do { - code_point = this._decoder.decode(input_stream); - if (code_point !== null && code_point !== EOF_code_point) { - output_stream.emit(code_point); + }, + + // see https://wiki.mozilla.org/WebAPI/AudioChannels + // The audioChannelType is currently only available in Firefox. This property returns + // "unknown" in other browser. The related HTML tag attribute is "mozaudiochannel" + audioChannelType: { + get: function() { + if ('mozAudioChannelType' in this.domElement) { + return this.domElement.mozAudioChannelType; + } else { + return 'unknown'; + } + }, + set: function(type) { + if ('mozAudioChannelType' in this.domElement) { + this.domElement.mozAudioChannelType = type; } - } while (code_point !== EOF_code_point && - input_stream.get() != EOF_byte); - this._decoder = null; + } } - return output_stream.string(); - } + }); }; - global['TextEncoder'] = global['TextEncoder'] || TextEncoder; - global['TextDecoder'] = global['TextDecoder'] || TextDecoder; -}(this)); -/* jshint ignore:end */ -!(function() { + function createNativeVideoElement(fallbackText, attributes) { + var videoElement = document.createElement('video'); + videoElement.setAttribute('autoplay', ''); + videoElement.innerHTML = fallbackText; - // Rumor Messaging for JS - // - // https://tbwiki.tokbox.com/index.php/Rumor_:_Messaging_FrameWork - // - // @todo Rumor { - // Add error codes for all the error cases - // Add Dependability commands - // } + if (attributes) { + if (attributes.muted === true) { + delete attributes.muted; + videoElement.muted = 'true'; + } - OT.Rumor = { - MessageType: { - // This is used to subscribe to address/addresses. The address/addresses the - // client specifies here is registered on the server. Once any message is sent to - // that address/addresses, the client receives that message. - SUBSCRIBE: 0, - - // This is used to unsubscribe to address / addresses. Once the client unsubscribe - // to an address, it will stop getting messages sent to that address. - UNSUBSCRIBE: 1, - - // This is used to send messages to arbitrary address/ addresses. Messages can be - // anything and Rumor will not care about what is included. - MESSAGE: 2, - - // This will be the first message that the client sends to the server. It includes - // the uniqueId for that client connection and a disconnect_notify address that will - // be notified once the client disconnects. - CONNECT: 3, - - // This will be the message used by the server to notify an address that a - // client disconnected. - DISCONNECT: 4, - - //Enhancements to support Keepalives - PING: 7, - PONG: 8, - STATUS: 9 + for (var key in attributes) { + if(!attributes.hasOwnProperty(key)) { + continue; + } + videoElement.setAttribute(key, attributes[key]); + } } - }; - -}(this)); -!(function(OT) { - - var WEB_SOCKET_KEEP_ALIVE_INTERVAL = 9000, - // Magic Connectivity Timeout Constant: We wait 9*the keep alive interval, - // on the third keep alive we trigger the timeout if we haven't received the - // server pong. - WEB_SOCKET_CONNECTIVITY_TIMEOUT = 5*WEB_SOCKET_KEEP_ALIVE_INTERVAL - 100, - - wsCloseErrorCodes; + return videoElement; + } + // See http://www.w3.org/TR/2010/WD-html5-20101019/video.html#error-codes + var _videoErrorCodes = {}; - // https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Close_codes - // http://docs.oracle.com/javaee/7/api/javax/websocket/CloseReason.CloseCodes.html - wsCloseErrorCodes = { - 1002: 'The endpoint is terminating the connection due to a protocol error. ' + - '(CLOSE_PROTOCOL_ERROR)', - 1003: 'The connection is being terminated because the endpoint received data of ' + - 'a type it cannot accept (for example, a text-only endpoint received binary data). ' + - '(CLOSE_UNSUPPORTED)', - 1004: 'The endpoint is terminating the connection because a data frame was received ' + - 'that is too large. (CLOSE_TOO_LARGE)', - 1005: 'Indicates that no status code was provided even though one was expected. ' + - '(CLOSE_NO_STATUS)', - 1006: 'Used to indicate that a connection was closed abnormally (that is, with no ' + - 'close frame being sent) when a status code is expected. (CLOSE_ABNORMAL)', - 1007: 'Indicates that an endpoint is terminating the connection because it has received ' + - 'data within a message that was not consistent with the type of the message (e.g., ' + - 'non-UTF-8 [RFC3629] data within a text message)', - 1008: 'Indicates that an endpoint is terminating the connection because it has received a ' + - 'message that violates its policy. This is a generic status code that can be returned ' + - 'when there is no other more suitable status code (e.g., 1003 or 1009) or if there is a ' + - 'need to hide specific details about the policy', - 1009: 'Indicates that an endpoint is terminating the connection because it has received a ' + - 'message that is too big for it to process', - 1011: 'Indicates that a server is terminating the connection because it encountered an ' + - 'unexpected condition that prevented it from fulfilling the request', - - // .... codes in the 4000-4999 range are available for use by applications. - 4001: 'Connectivity loss was detected as it was too long since the socket received the ' + - 'last PONG message' - }; + // Checking for window.MediaError for IE compatibility, just so we don't throw + // exceptions when the script is included + if (window.MediaError) { + _videoErrorCodes[window.MediaError.MEDIA_ERR_ABORTED] = 'The fetching process for the media ' + + 'resource was aborted by the user agent at the user\'s request.'; + _videoErrorCodes[window.MediaError.MEDIA_ERR_NETWORK] = 'A network error of some description ' + + 'caused the user agent to stop fetching the media resource, after the resource was ' + + 'established to be usable.'; + _videoErrorCodes[window.MediaError.MEDIA_ERR_DECODE] = 'An error of some description ' + + 'occurred while decoding the media resource, after the resource was established to be ' + + ' usable.'; + _videoErrorCodes[window.MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED] = 'The media resource ' + + 'indicated by the src attribute was not suitable.'; + } - OT.Rumor.SocketError = function(code, message) { - this.code = code; - this.message = message; - }; + function videoElementErrorCodeToStr(errorCode) { + return _videoErrorCodes[parseInt(errorCode, 10)] || 'An unknown error occurred.'; + } - // The NativeSocket bit is purely to make testing simpler, it defaults to WebSocket - // so in normal operation you would omit it. - OT.Rumor.Socket = function(messagingURL, notifyDisconnectAddress, NativeSocket) { - - var states = ['disconnected', 'error', 'connected', 'connecting', 'disconnecting'], - webSocket, - id, - onOpen, - onError, - onClose, - onMessage, - connectCallback, - connectTimeout, - lastMessageTimestamp, // The timestamp of the last message received - keepAliveTimer; // Timer for the connectivity checks - - - //// Private API - var stateChanged = function(newState) { - switch (newState) { - case 'disconnected': - case 'error': - webSocket = null; - if (onClose) { - var error; - if(hasLostConnectivity()) { - error = new Error(wsCloseErrorCodes[4001]); - error.code = 4001; - } - onClose(error); - } - break; - } + function bindStreamToNativeVideoElement(videoElement, webRtcStream, completion) { + var timeout, + minVideoTracksForTimeUpdate = OT.$.env.name === 'Chrome' ? 1 : 0, + loadedEvent = webRtcStream.getVideoTracks().length > minVideoTracksForTimeUpdate ? + 'timeupdate' : 'loadedmetadata'; + + var cleanup = function cleanup () { + clearTimeout(timeout); + videoElement.removeEventListener(loadedEvent, onLoad, false); + videoElement.removeEventListener('error', onError, false); + webRtcStream.onended = null; }, - setState = OT.$.statable(this, states, 'disconnected', stateChanged), + onLoad = function onLoad () { + cleanup(); + completion(null); + }, - validateCallback = function validateCallback (name, callback) { - if (callback === null || !OT.$.isFunction(callback) ) { - throw new Error('The Rumor.Socket ' + name + - ' callback must be a valid function or null'); - } + onError = function onError (event) { + cleanup(); + unbindNativeStream(videoElement); + completion('There was an unexpected problem with the Video Stream: ' + + videoElementErrorCodeToStr(event.target.error.code)); }, - error = OT.$.bind(function error (errorMessage) { - OT.error('Rumor.Socket: ' + errorMessage); + onStoppedLoading = function onStoppedLoading () { + // The stream ended before we fully bound it. Maybe the other end called + // stop on it or something else went wrong. + cleanup(); + unbindNativeStream(videoElement); + completion('Stream ended while trying to bind it to a video element.'); + }; - var socketError = new OT.Rumor.SocketError(null, errorMessage || 'Unknown Socket Error'); + videoElement.addEventListener(loadedEvent, onLoad, false); + videoElement.addEventListener('error', onError, false); + webRtcStream.onended = onStoppedLoading; - if (connectTimeout) clearTimeout(connectTimeout); + // The official spec way is 'srcObject', we are slowly converging there. + if (videoElement.srcObject !== void 0) { + videoElement.srcObject = webRtcStream; + } else if (videoElement.mozSrcObject !== void 0) { + videoElement.mozSrcObject = webRtcStream; + } else { + videoElement.src = window.URL.createObjectURL(webRtcStream); + } + } - setState('error'); - if (this.previousState === 'connecting' && connectCallback) { - connectCallback(socketError, null); - connectCallback = null; - } + function unbindNativeStream(videoElement) { + if (videoElement.srcObject !== void 0) { + videoElement.srcObject = null; + } else if (videoElement.mozSrcObject !== void 0) { + videoElement.mozSrcObject = null; + } else { + window.URL.revokeObjectURL(videoElement.src); + } + } - if (onError) onError(socketError); - }, this), - hasLostConnectivity = function hasLostConnectivity () { - if (!lastMessageTimestamp) return false; +})(window); + +// tb_require('../helpers.js') +// tb_require('./video_element.js') - return (OT.$.now() - lastMessageTimestamp) >= WEB_SOCKET_CONNECTIVITY_TIMEOUT; - }, +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - sendKeepAlive = OT.$.bind(function() { - if (!this.is('connected')) return; +!(function() { +/*global OT:true */ - if ( hasLostConnectivity() ) { - webSocketDisconnected({code: 4001}); - } - else { - webSocket.send(OT.Rumor.Message.Ping()); - keepAliveTimer = setTimeout(sendKeepAlive, WEB_SOCKET_KEEP_ALIVE_INTERVAL); - } - }, this), + var defaultAspectRatio = 4.0/3.0, + miniWidth = 128, + miniHeight = 128, + microWidth = 64, + microHeight = 64; - // Returns true if we think the DOM has been unloaded - // It detects this by looking for the OT global, which - // should always exist until the DOM is cleaned up. - isDOMUnloaded = function isDOMUnloaded () { - return !window.OT; - }; + /** + * Sets the video element size so by preserving the intrinsic aspect ratio of the element content + * but altering the width and height so that the video completely covers the container. + * + * @param {Element} element the container of the video element + * @param {number} containerWidth + * @param {number} containerHeight + * @param {number} intrinsicRatio the aspect ratio of the video media + * @param {boolean} rotated + */ + function fixFitModeCover(element, containerWidth, containerHeight, intrinsicRatio, rotated) { + var $video = OT.$('.OT_video-element', element); - //// Private Event Handlers - var webSocketConnected = OT.$.bind(function webSocketConnected () { - if (connectTimeout) clearTimeout(connectTimeout); - if (this.isNot('connecting')) { - OT.debug('webSocketConnected reached in state other than connecting'); - return; - } + if ($video.length > 0) { - // Connect to Rumor by registering our connection id and the - // app server address to notify if we disconnect. - // - // We don't need to wait for a reply to this message. - webSocket.send(OT.Rumor.Message.Connect(id, notifyDisconnectAddress)); + var cssProps = {left: '', top: ''}; - setState('connected'); - if (connectCallback) { - connectCallback(null, id); - connectCallback = null; - } + if (OTPlugin.isInstalled()) { + cssProps.width = '100%'; + cssProps.height = '100%'; + } else { + intrinsicRatio = intrinsicRatio || defaultAspectRatio; + intrinsicRatio = rotated ? 1 / intrinsicRatio : intrinsicRatio; - if (onOpen) onOpen(id); + var containerRatio = containerWidth / containerHeight; - keepAliveTimer = setTimeout(function() { - lastMessageTimestamp = OT.$.now(); - sendKeepAlive(); - }, WEB_SOCKET_KEEP_ALIVE_INTERVAL); - }, this), + var enforcedVideoWidth, + enforcedVideoHeight; - webSocketConnectTimedOut = function webSocketConnectTimedOut () { - var webSocketWas = webSocket; - error('Timed out while waiting for the Rumor socket to connect.'); - // This will prevent a socket eventually connecting - // But call it _after_ the error just in case any of - // the callbacks fire synchronously, breaking the error - // handling code. - try { - webSocketWas.close(); - } catch(x) {} - }, + if (rotated) { + // in case of rotation same code works for both kind of ration + enforcedVideoHeight = containerWidth; + enforcedVideoWidth = enforcedVideoHeight * intrinsicRatio; + + cssProps.width = enforcedVideoWidth + 'px'; + cssProps.height = enforcedVideoHeight + 'px'; + cssProps.top = (enforcedVideoWidth + containerHeight) / 2 + 'px'; + } else { + if (intrinsicRatio < containerRatio) { + // the container is wider than the video -> we will crop the height of the video + enforcedVideoWidth = containerWidth; + enforcedVideoHeight = enforcedVideoWidth / intrinsicRatio; + + cssProps.width = enforcedVideoWidth + 'px'; + cssProps.height = enforcedVideoHeight + 'px'; + cssProps.top = (-enforcedVideoHeight + containerHeight) / 2 + 'px'; + } else { + enforcedVideoHeight = containerHeight; + enforcedVideoWidth = enforcedVideoHeight * intrinsicRatio; - webSocketError = function webSocketError () {}, - // var errorMessage = 'Unknown Socket Error'; - // @fixme We MUST be able to do better than this! - - // All errors seem to result in disconnecting the socket, the close event - // has a close reason and code which gives some error context. This, - // combined with the fact that the errorEvent argument contains no - // error info at all, means we'll delay triggering the error handlers - // until the socket is closed. - // error(errorMessage); - - webSocketDisconnected = OT.$.bind(function webSocketDisconnected (closeEvent) { - if (connectTimeout) clearTimeout(connectTimeout); - if (keepAliveTimer) clearTimeout(keepAliveTimer); - - if (isDOMUnloaded()) { - // Sometimes we receive the web socket close event after - // the DOM has already been partially or fully unloaded - // if that's the case here then it's not really safe, or - // desirable, to continue. - return; + cssProps.width = enforcedVideoWidth + 'px'; + cssProps.height = enforcedVideoHeight + 'px'; + cssProps.left = (-enforcedVideoWidth + containerWidth) / 2 + 'px'; } + } + } - if (closeEvent.code !== 1000 && closeEvent.code !== 1001) { - var reason = closeEvent.reason || closeEvent.message; - if (!reason && wsCloseErrorCodes.hasOwnProperty(closeEvent.code)) { - reason = wsCloseErrorCodes[closeEvent.code]; - } + $video.css(cssProps); + } + } - error('Rumor Socket Disconnected: ' + reason); - } + /** + * Sets the video element size so that the video is entirely visible inside the container. + * + * @param {Element} element the container of the video element + * @param {number} containerWidth + * @param {number} containerHeight + * @param {number} intrinsicRatio the aspect ratio of the video media + * @param {boolean} rotated + */ + function fixFitModeContain(element, containerWidth, containerHeight, intrinsicRatio, rotated) { - if (this.isNot('error')) setState('disconnected'); - }, this), + var $video = OT.$('.OT_video-element', element); - webSocketReceivedMessage = function webSocketReceivedMessage (msg) { - lastMessageTimestamp = OT.$.now(); + if ($video.length > 0) { - if (onMessage) { - if (msg.type !== OT.Rumor.MessageType.PONG) { - onMessage(msg); - } - } - }; + var cssProps = {left: '', top: ''}; - //// Public API + if (OTPlugin.isInstalled()) { + intrinsicRatio = intrinsicRatio || defaultAspectRatio; - this.publish = function (topics, message, headers) { - webSocket.send(OT.Rumor.Message.Publish(topics, message, headers)); - }; + var containerRatio = containerWidth / containerHeight; - this.subscribe = function(topics) { - webSocket.send(OT.Rumor.Message.Subscribe(topics)); - }; + var enforcedVideoWidth, + enforcedVideoHeight; - this.unsubscribe = function(topics) { - webSocket.send(OT.Rumor.Message.Unsubscribe(topics)); - }; + if (intrinsicRatio < containerRatio) { + enforcedVideoHeight = containerHeight; + enforcedVideoWidth = containerHeight * intrinsicRatio; - this.connect = function (connectionId, complete) { - if (this.is('connecting', 'connected')) { - complete(new OT.Rumor.SocketError(null, - 'Rumor.Socket cannot connect when it is already connecting or connected.')); - return; - } + cssProps.width = enforcedVideoWidth + 'px'; + cssProps.height = enforcedVideoHeight + 'px'; + cssProps.left = (containerWidth - enforcedVideoWidth) / 2 + 'px'; + } else { + enforcedVideoWidth = containerWidth; + enforcedVideoHeight = enforcedVideoWidth / intrinsicRatio; - id = connectionId; - connectCallback = complete; + cssProps.width = enforcedVideoWidth + 'px'; + cssProps.height = enforcedVideoHeight + 'px'; + cssProps.top = (containerHeight - enforcedVideoHeight) / 2 + 'px'; + } + } else { + if (rotated) { + cssProps.width = containerHeight + 'px'; + cssProps.height = containerWidth + 'px'; + cssProps.top = containerHeight + 'px'; + } else { + cssProps.width = '100%'; + cssProps.height = '100%'; + } + } - setState('connecting'); + $video.css(cssProps); + } + } - var TheWebSocket = NativeSocket || window.WebSocket; + function fixMini(container, width, height) { + var w = parseInt(width, 10), + h = parseInt(height, 10); - var events = { - onOpen: webSocketConnected, - onClose: webSocketDisconnected, - onError: webSocketError, - onMessage: webSocketReceivedMessage - }; + if(w < microWidth || h < microHeight) { + OT.$.addClass(container, 'OT_micro'); + } else { + OT.$.removeClass(container, 'OT_micro'); + } + if(w < miniWidth || h < miniHeight) { + OT.$.addClass(container, 'OT_mini'); + } else { + OT.$.removeClass(container, 'OT_mini'); + } + } - try { - if(typeof TheWebSocket !== 'undefined') { - webSocket = new OT.Rumor.NativeSocket(TheWebSocket, messagingURL, events); - } else { - webSocket = new OT.Rumor.PluginSocket(messagingURL, events); - } + var getOrCreateContainer = function getOrCreateContainer(elementOrDomId, insertMode) { + var container, + domId; - connectTimeout = setTimeout(webSocketConnectTimedOut, OT.Rumor.Socket.CONNECT_TIMEOUT); + if (elementOrDomId && elementOrDomId.nodeName) { + // It looks like we were given a DOM element. Grab the id or generate + // one if it doesn't have one. + container = elementOrDomId; + if (!container.getAttribute('id') || container.getAttribute('id').length === 0) { + container.setAttribute('id', 'OT_' + OT.$.uuid()); } - catch(e) { - OT.error(e); - // @todo add an actual error message - error('Could not connect to the Rumor socket, possibly because of a blocked port.'); + domId = container.getAttribute('id'); + } else { + // We may have got an id, try and get it's DOM element. + container = OT.$('#' + elementOrDomId).get(0); + domId = elementOrDomId || ('OT_' + OT.$.uuid()); + } + + if (!container) { + container = OT.$.createElement('div', {id: domId}); + container.style.backgroundColor = '#000000'; + document.body.appendChild(container); + } else { + if(!(insertMode == null || insertMode === 'replace')) { + var placeholder = document.createElement('div'); + placeholder.id = ('OT_' + OT.$.uuid()); + if(insertMode === 'append') { + container.appendChild(placeholder); + container = placeholder; + } else if(insertMode === 'before') { + container.parentNode.insertBefore(placeholder, container); + container = placeholder; + } else if(insertMode === 'after') { + container.parentNode.insertBefore(placeholder, container.nextSibling); + container = placeholder; + } + } else { + OT.$.emptyElement(container); } - }; + } - this.disconnect = function(drainSocketBuffer) { - if (connectTimeout) clearTimeout(connectTimeout); - if (keepAliveTimer) clearTimeout(keepAliveTimer); + return container; + }; - if (!webSocket) { - if (this.isNot('error')) setState('disconnected'); - return; - } + // Creates the standard container that the Subscriber and Publisher use to hold + // their video element and other chrome. + OT.WidgetView = function(targetElement, properties) { - if (webSocket.isClosed()) { - if (this.isNot('error')) setState('disconnected'); - } - else { - if (this.is('connected')) { - // Look! We are nice to the rumor server ;-) - webSocket.send(OT.Rumor.Message.Disconnect()); - } + var widgetView = {}; + + var container = getOrCreateContainer(targetElement, properties && properties.insertMode), + widgetContainer = document.createElement('div'), + oldContainerStyles = {}, + dimensionsObserver, + videoElement, + videoObserver, + posterContainer, + loadingContainer, + width, + height, + loading = true, + audioOnly = false, + fitMode = 'cover', + fixFitMode = fixFitModeCover; + + OT.$.eventing(widgetView); + + if (properties) { + width = properties.width; + height = properties.height; - // Wait until the socket is ready to close - webSocket.close(drainSocketBuffer); + if (width) { + if (typeof(width) === 'number') { + width = width + 'px'; + } } - }; + if (height) { + if (typeof(height) === 'number') { + height = height + 'px'; + } + } + container.style.width = width ? width : '264px'; + container.style.height = height ? height : '198px'; + container.style.overflow = 'hidden'; + fixMini(container, width || '264px', height || '198px'); - OT.$.defineProperties(this, { - id: { - get: function() { return id; } - }, + if (properties.mirror === undefined || properties.mirror) { + OT.$.addClass(container, 'OT_mirrored'); + } - onOpen: { - set: function(callback) { - validateCallback('onOpen', callback); - onOpen = callback; - }, + if (properties.fitMode === 'contain') { + fitMode = 'contain'; + fixFitMode = fixFitModeContain; + } else if (properties.fitMode !== 'cover') { + OT.warn('Invalid fit value "' + properties.fitMode + '" passed. ' + + 'Only "contain" and "cover" can be used.'); + } + } - get: function() { return onOpen; } - }, + if (properties.classNames) OT.$.addClass(container, properties.classNames); - onError: { - set: function(callback) { - validateCallback('onError', callback); - onError = callback; - }, + OT.$(container).addClass('OT_loading OT_fit-mode-' + fitMode); - get: function() { return onError; } - }, + OT.$.addClass(widgetContainer, 'OT_widget-container'); + widgetContainer.style.width = '100%'; //container.style.width; + widgetContainer.style.height = '100%'; // container.style.height; + container.appendChild(widgetContainer); - onClose: { - set: function(callback) { - validateCallback('onClose', callback); - onClose = callback; - }, + loadingContainer = document.createElement('div'); + OT.$.addClass(loadingContainer, 'OT_video-loading'); + widgetContainer.appendChild(loadingContainer); - get: function() { return onClose; } - }, + posterContainer = document.createElement('div'); + OT.$.addClass(posterContainer, 'OT_video-poster'); + widgetContainer.appendChild(posterContainer); - onMessage: { - set: function(callback) { - validateCallback('onMessage', callback); - onMessage = callback; - }, + oldContainerStyles.width = container.offsetWidth; + oldContainerStyles.height = container.offsetHeight; - get: function() { return onMessage; } - } - }); - }; + if (!OTPlugin.isInstalled()) { + // Observe changes to the width and height and update the aspect ratio + dimensionsObserver = OT.$.observeStyleChanges(container, ['width', 'height'], + function(changeSet) { + var width = changeSet.width ? changeSet.width[1] : container.offsetWidth, + height = changeSet.height ? changeSet.height[1] : container.offsetHeight; - // The number of ms to wait for the websocket to connect - OT.Rumor.Socket.CONNECT_TIMEOUT = 15000; + fixMini(container, width, height); -}(window.OT, this)); -!(function() { + if (videoElement) { + fixFitMode(widgetContainer, width, height, videoElement.aspectRatio(), + videoElement.isRotated()); + } + }); - var BUFFER_DRAIN_INTERVAL = 100, - // The total number of times to retest the websocket's send buffer - BUFFER_DRAIN_MAX_RETRIES = 10; - OT.Rumor.NativeSocket = function(TheWebSocket, messagingURL, events) { + // @todo observe if the video container or the video element get removed + // if they do we should do some cleanup + videoObserver = OT.$.observeNodeOrChildNodeRemoval(container, function(removedNodes) { + if (!videoElement) return; - var webSocket, - disconnectWhenSendBufferIsDrained, - bufferDrainTimeout, // Timer to poll whether th send buffer has been drained - close; + // This assumes a video element being removed is the main video element. This may + // not be the case. + var videoRemoved = OT.$.some(removedNodes, function(node) { + return node === widgetContainer || node.nodeName === 'VIDEO'; + }); - webSocket = new TheWebSocket(messagingURL); - webSocket.binaryType = 'arraybuffer'; + if (videoRemoved) { + videoElement.destroy(); + videoElement = null; + } - webSocket.onopen = events.onOpen; - webSocket.onclose = events.onClose; - webSocket.onerror = events.onError; + if (widgetContainer) { + OT.$.removeElement(widgetContainer); + widgetContainer = null; + } - webSocket.onmessage = function(message) { - if (!OT) { - // In IE 10/11, This can apparently be called after - // the page is unloaded and OT is garbage-collected - return; - } + if (dimensionsObserver) { + dimensionsObserver.disconnect(); + dimensionsObserver = null; + } - var msg = OT.Rumor.Message.deserialize(message.data); - events.onMessage(msg); - }; + if (videoObserver) { + videoObserver.disconnect(); + videoObserver = null; + } + }); + } - // Ensure that the WebSocket send buffer is fully drained before disconnecting - // the socket. If the buffer doesn't drain after a certain length of time - // we give up and close it anyway. - disconnectWhenSendBufferIsDrained = - function disconnectWhenSendBufferIsDrained (bufferDrainRetries) { - if (!webSocket) return; + widgetView.destroy = function() { + if (dimensionsObserver) { + dimensionsObserver.disconnect(); + dimensionsObserver = null; + } - if (bufferDrainRetries === void 0) bufferDrainRetries = 0; - if (bufferDrainTimeout) clearTimeout(bufferDrainTimeout); + if (videoObserver) { + videoObserver.disconnect(); + videoObserver = null; + } - if (webSocket.bufferedAmount > 0 && - (bufferDrainRetries + 1) <= BUFFER_DRAIN_MAX_RETRIES) { - bufferDrainTimeout = setTimeout(disconnectWhenSendBufferIsDrained, - BUFFER_DRAIN_INTERVAL, bufferDrainRetries+1); + if (videoElement) { + videoElement.destroy(); + videoElement = null; + } - } else { - close(); + if (container) { + OT.$.removeElement(container); + container = null; } }; - close = function close() { - webSocket.close(); + widgetView.setBackgroundImageURI = function(bgImgURI) { + if (bgImgURI.substr(0, 5) !== 'http:' && bgImgURI.substr(0, 6) !== 'https:') { + if (bgImgURI.substr(0, 22) !== 'data:image/png;base64,') { + bgImgURI = 'data:image/png;base64,' + bgImgURI; + } + } + OT.$.css(posterContainer, 'backgroundImage', 'url(' + bgImgURI + ')'); + OT.$.css(posterContainer, 'backgroundSize', 'contain'); + OT.$.css(posterContainer, 'opacity', '1.0'); }; - this.close = function(drainBuffer) { - if (drainBuffer) { - disconnectWhenSendBufferIsDrained(); - } else { - close(); + if (properties && properties.style && properties.style.backgroundImageURI) { + widgetView.setBackgroundImageURI(properties.style.backgroundImageURI); + } + + widgetView.bindVideo = function(webRTCStream, options, completion) { + // remove the old video element if it exists + // @todo this might not be safe, publishers/subscribers use this as well... + if (videoElement) { + videoElement.destroy(); + videoElement = null; } - }; - this.send = function(msg) { - webSocket.send(msg.serialize()); - }; + var onError = options && options.error ? options.error : void 0; + delete options.error; - this.isClosed = function() { - return webSocket.readyState === 3; - }; + var video = new OT.VideoElement({attributes: options}, onError); - }; + // Initialize the audio volume + if (options.audioVolume) video.setAudioVolume(options.audioVolume); + // makes the incoming audio streams take priority (will impact only FF OS for now) + video.audioChannelType('telephony'); -}(this)); -!(function() { + video.appendTo(widgetContainer).bindToStream(webRTCStream, function(err) { + if (err) { + video.destroy(); + completion(err); + return; + } - OT.Rumor.PluginSocket = function(messagingURL, events) { + videoElement = video; - var webSocket, - state = 'initializing'; + // clear inline height value we used to init plugin rendering + OT.$.css(video.domElement(), 'height', ''); - TBPlugin.initRumorSocket(messagingURL, OT.$.bind(function(err, rumorSocket) { - if(err) { - state = 'closed'; - events.onClose({ code: 4999 }); - } else if(state === 'initializing') { - webSocket = rumorSocket; + var fixFitModePartial = function() { + fixFitMode(widgetContainer, container.offsetWidth, container.offsetHeight, + video.aspectRatio(), video.isRotated()); + }; - webSocket.onOpen(function() { - state = 'open'; - events.onOpen(); - }); - webSocket.onClose(function(error) { - state = 'closed'; /* CLOSED */ - events.onClose({ code: error }); - }); - webSocket.onError(function(error) { - state = 'closed'; /* CLOSED */ - events.onError(error); - /* native websockets seem to do this, so should we */ - events.onClose({ code: error }); + video.on({ + orientationChanged: fixFitModePartial, + videoDimensionsChanged: function(oldValue, newValue) { + fixFitModePartial(); + widgetView.trigger('videoDimensionsChanged', oldValue, newValue); + }, + mediaStopped: function() { + widgetView.trigger('mediaStopped'); + } }); - webSocket.onMessage(function(type, addresses, headers, payload) { - var msg = new OT.Rumor.Message(type, addresses, headers, payload); - events.onMessage(msg); - }); + video.onRatioAvailable(fixFitModePartial); - webSocket.open(); - } else { - this.close(); - } - }, this)); + completion(null, video); + }); - this.close = function() { - if(state === 'initializing' || state === 'closed') { - state = 'closed'; - return; - } + OT.$.addClass(video.domElement(), 'OT_video-element'); - webSocket.close(1000, ''); - }; + // plugin needs a minimum of height to be rendered and kicked off + // we will reset that once the video is bound to the stream + OT.$.css(video.domElement(), 'height', '1px'); - this.send = function(msg) { - if(state === 'open') { - webSocket.send(msg); - } + return video; }; - this.isClosed = function() { - return state === 'closed'; - }; + OT.$.defineProperties(widgetView, { - }; + video: { + get: function() { + return videoElement; + } + }, -}(this)); -!(function() { + showPoster: { + get: function() { + return !OT.$.isDisplayNone(posterContainer); + }, + set: function(newValue) { + if(newValue) { + OT.$.show(posterContainer); + } else { + OT.$.hide(posterContainer); + } + } + }, - /*global TextEncoder, TextDecoder */ + poster: { + get: function() { + return OT.$.css(posterContainer, 'backgroundImage'); + }, + set: function(src) { + OT.$.css(posterContainer, 'backgroundImage', 'url(' + src + ')'); + } + }, - // - // - // @references - // * https://tbwiki.tokbox.com/index.php/Rumor_Message_Packet - // * https://tbwiki.tokbox.com/index.php/Rumor_Protocol - // - OT.Rumor.Message = function (type, toAddress, headers, data) { - this.type = type; - this.toAddress = toAddress; - this.headers = headers; - this.data = data; + loading: { + get: function() { return loading; }, + set: function(l) { + loading = l; - this.transactionId = this.headers['TRANSACTION-ID']; - this.status = this.headers.STATUS; - this.isError = !(this.status && this.status[0] === '2'); - }; - - OT.Rumor.Message.prototype.serialize = function () { - var offset = 8, - cBuf = 7, - address = [], - headerKey = [], - headerVal = [], - strArray, - dataView, - i, - j; - - // The number of addresses - cBuf++; - - // Write out the address. - for (i = 0; i < this.toAddress.length; i++) { - /*jshint newcap:false */ - address.push(new TextEncoder('utf-8').encode(this.toAddress[i])); - cBuf += 2; - cBuf += address[i].length; - } + if (loading) { + OT.$.addClass(container, 'OT_loading'); + } else { + OT.$.removeClass(container, 'OT_loading'); + } + } + }, + + audioOnly: { + get: function() { return audioOnly; }, + set: function(a) { + audioOnly = a; + + if (audioOnly) { + OT.$.addClass(container, 'OT_audio-only'); + } else { + OT.$.removeClass(container, 'OT_audio-only'); + } + } + }, + + domId: { + get: function() { return container.getAttribute('id'); } + } - // The number of parameters - cBuf++; + }); - // Write out the params - i = 0; + widgetView.domElement = container; - for (var key in this.headers) { - if(!this.headers.hasOwnProperty(key)) { - continue; + widgetView.addError = function(errorMsg, helpMsg, classNames) { + container.innerHTML = '

    ' + errorMsg + + (helpMsg ? ' ' + helpMsg + '' : '') + + '

    '; + OT.$.addClass(container, classNames || 'OT_subscriber_error'); + if(container.querySelector('p').offsetHeight > container.offsetHeight) { + container.querySelector('span').style.display = 'none'; } - headerKey.push(new TextEncoder('utf-8').encode(key)); - headerVal.push(new TextEncoder('utf-8').encode(this.headers[key])); - cBuf += 4; - cBuf += headerKey[i].length; - cBuf += headerVal[i].length; - - i++; - } + }; - dataView = new TextEncoder('utf-8').encode(this.data); - cBuf += dataView.length; + return widgetView; + }; - // Let's allocate a binary blob of this size - var buffer = new ArrayBuffer(cBuf); - var uint8View = new Uint8Array(buffer, 0, cBuf); +})(window); - // We don't include the header in the lenght. - cBuf -= 4; +// tb_require('../helpers.js') - // Write out size (in network order) - uint8View[0] = (cBuf & 0xFF000000) >>> 24; - uint8View[1] = (cBuf & 0x00FF0000) >>> 16; - uint8View[2] = (cBuf & 0x0000FF00) >>> 8; - uint8View[3] = (cBuf & 0x000000FF) >>> 0; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - // Write out reserved bytes - uint8View[4] = 0; - uint8View[5] = 0; +if (!OT.properties) { + throw new Error('OT.properties does not exist, please ensure that you include a valid ' + + 'properties file.'); +} - // Write out message type - uint8View[6] = this.type; - uint8View[7] = this.toAddress.length; +OT.useSSL = function () { + return OT.properties.supportSSL && (window.location.protocol.indexOf('https') >= 0 || + window.location.protocol.indexOf('chrome-extension') >= 0); +}; - // Now just copy over the encoded values.. - for (i = 0; i < address.length; i++) { - strArray = address[i]; - uint8View[offset++] = strArray.length >> 8 & 0xFF; - uint8View[offset++] = strArray.length >> 0 & 0xFF; - for (j = 0; j < strArray.length; j++) { - uint8View[offset++] = strArray[j]; - } +// Consumes and overwrites OT.properties. Makes it better and stronger! +OT.properties = function(properties) { + var props = OT.$.clone(properties); + + props.debug = properties.debug === 'true' || properties.debug === true; + props.supportSSL = properties.supportSSL === 'true' || properties.supportSSL === true; + + if (window.OTProperties) { + // Allow window.OTProperties to override cdnURL, configURL, assetURL and cssURL + if (window.OTProperties.cdnURL) props.cdnURL = window.OTProperties.cdnURL; + if (window.OTProperties.cdnURLSSL) props.cdnURLSSL = window.OTProperties.cdnURLSSL; + if (window.OTProperties.configURL) props.configURL = window.OTProperties.configURL; + if (window.OTProperties.assetURL) props.assetURL = window.OTProperties.assetURL; + if (window.OTProperties.cssURL) props.cssURL = window.OTProperties.cssURL; + } + + if (!props.assetURL) { + if (OT.useSSL()) { + props.assetURL = props.cdnURLSSL + '/webrtc/' + props.version; + } else { + props.assetURL = props.cdnURL + '/webrtc/' + props.version; } + } - uint8View[offset++] = headerKey.length; + var isIE89 = OT.$.env.name === 'IE' && OT.$.env.version <= 9; + if (!(isIE89 && window.location.protocol.indexOf('https') < 0)) { + props.apiURL = props.apiURLSSL; + props.loggingURL = props.loggingURLSSL; + } - // Write out the params - for (i = 0; i < headerKey.length; i++) { - strArray = headerKey[i]; - uint8View[offset++] = strArray.length >> 8 & 0xFF; - uint8View[offset++] = strArray.length >> 0 & 0xFF; - for (j = 0; j < strArray.length; j++) { - uint8View[offset++] = strArray[j]; - } + if (!props.configURL) props.configURL = props.assetURL + '/js/dynamic_config.min.js'; + if (!props.cssURL) props.cssURL = props.assetURL + '/css/TB.min.css'; - strArray = headerVal[i]; - uint8View[offset++] = strArray.length >> 8 & 0xFF; - uint8View[offset++] = strArray.length >> 0 & 0xFF; - for (j = 0; j < strArray.length; j++) { - uint8View[offset++] = strArray[j]; - } - } + return props; +}(OT.properties); - // And finally the data - for (i = 0; i < dataView.length; i++) { - uint8View[offset++] = dataView[i]; - } +// tb_require('../helpers.js') - return buffer; - }; +!(function() { + /* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ + /* global OT */ - function toArrayBuffer(buffer) { - var ab = new ArrayBuffer(buffer.length); - var view = new Uint8Array(ab); - for (var i = 0; i < buffer.length; ++i) { - view[i] = buffer[i]; - } - return ab; - } + var currentGuidStorage, + currentGuid; - OT.Rumor.Message.deserialize = function (buffer) { + var isInvalidStorage = function isInvalidStorage (storageInterface) { + return !(OT.$.isFunction(storageInterface.get) && OT.$.isFunction(storageInterface.set)); + }; - if(typeof Buffer !== 'undefined' && - Buffer.isBuffer(buffer)) { - buffer = toArrayBuffer(buffer); + var getClientGuid = function getClientGuid (completion) { + if (currentGuid) { + completion(null, currentGuid); + return; } - var cBuf = 0, - type, - offset = 8, - uint8View = new Uint8Array(buffer), - strView, - headerlen, - headers, - keyStr, - valStr, - length, - i; - // Write out size (in network order) - cBuf += uint8View[0] << 24; - cBuf += uint8View[1] << 16; - cBuf += uint8View[2] << 8; - cBuf += uint8View[3] << 0; - - type = uint8View[6]; - var address = []; + // It's the first time that getClientGuid has been called + // in this page lifetime. Attempt to load any existing Guid + // from the storage + currentGuidStorage.get(completion); + }; - for (i = 0; i < uint8View[7]; i++) { - length = uint8View[offset++] << 8; - length += uint8View[offset++]; - strView = new Uint8Array(buffer, offset, length); - /*jshint newcap:false */ - address[i] = new TextDecoder('utf-8').decode(strView); - offset += length; + /* + * Sets the methods for storing and retrieving client GUIDs persistently + * across sessions. By default, OpenTok.js attempts to use browser cookies to + * store GUIDs. + *

    + * Pass in an object that has a get() method and + * a set() method. + *

    + * The get() method must take one parameter: the callback + * method to invoke. The callback method is passed two parameters — + * the first parameter is an error object or null if the call is successful; + * and the second parameter is the GUID (a string) if successful. + *

    + * The set() method must include two parameters: the GUID to set + * (a string) and the callback method to invoke. The callback method is + * passed an error object on error, or it is passed no parameter if the call is + * successful. + *

    + * Here is an example: + *

    + *

    +  * var ComplexStorage = function() {
    +  *   this.set = function(guid, completion) {
    +  *     AwesomeBackendService.set(guid, function(response) {
    +  *       completion(response.error || null);
    +  *     });
    +  *   };
    +  *   this.get = function(completion) {
    +  *     AwesomeBackendService.get(function(response, guid) {
    +  *       completion(response.error || null, guid);
    +  *     });
    +  *   };
    +  * };
    +  *
    +  * OT.overrideGuidStorage(new ComplexStorage());
    +  * 
    + */ + OT.overrideGuidStorage = function (storageInterface) { + if (isInvalidStorage(storageInterface)) { + throw new Error('The storageInterface argument does not seem to be valid, ' + + 'it must implement get and set methods'); } - headerlen = uint8View[offset++]; - headers = {}; - - for (i = 0; i < headerlen; i++) { - length = uint8View[offset++] << 8; - length += uint8View[offset++]; - strView = new Uint8Array(buffer, offset, length); - keyStr = new TextDecoder('utf-8').decode(strView); - offset += length; - - length = uint8View[offset++] << 8; - length += uint8View[offset++]; - strView = new Uint8Array(buffer, offset, length); - valStr = new TextDecoder('utf-8').decode(strView); - headers[keyStr] = valStr; - offset += length; + if (currentGuidStorage === storageInterface) { + return; } - var dataView = new Uint8Array(buffer, offset); - var data = new TextDecoder('utf-8').decode(dataView); + currentGuidStorage = storageInterface; - return new OT.Rumor.Message(type, address, headers, data); + // If a client Guid has already been assigned to this client then + // let the new storage know about it so that it's in sync. + if (currentGuid) { + currentGuidStorage.set(currentGuid, function(error) { + if (error) { + OT.error('Failed to send initial Guid value (' + currentGuid + + ') to the newly assigned Guid Storage. The error was: ' + error); + // @todo error + } + }); + } }; + if (!OT._) OT._ = {}; + OT._.getClientGuid = function (completion) { + getClientGuid(function(error, guid) { + if (error) { + completion(error); + return; + } - OT.Rumor.Message.Connect = function (uniqueId, notifyDisconnectAddress) { - var headers = { - uniqueId: uniqueId, - notifyDisconnectAddress: notifyDisconnectAddress - }; + if (!guid) { + // Nothing came back, this client is entirely new. + // generate a new Guid and persist it + guid = OT.$.uuid(); + currentGuidStorage.set(guid, function(error) { + if (error) { + completion(error); + return; + } - return new OT.Rumor.Message(OT.Rumor.MessageType.CONNECT, [], headers, ''); - }; + currentGuid = guid; + }); + } + else if (!currentGuid) { + currentGuid = guid; + } - OT.Rumor.Message.Disconnect = function () { - return new OT.Rumor.Message(OT.Rumor.MessageType.DISCONNECT, [], {}, ''); + completion(null, currentGuid); + }); }; - OT.Rumor.Message.Subscribe = function(topics) { - return new OT.Rumor.Message(OT.Rumor.MessageType.SUBSCRIBE, topics, {}, ''); - }; - OT.Rumor.Message.Unsubscribe = function(topics) { - return new OT.Rumor.Message(OT.Rumor.MessageType.UNSUBSCRIBE, topics, {}, ''); - }; + // Implement our default storage mechanism, which sets/gets a cookie + // called 'opentok_client_id' + OT.overrideGuidStorage({ + get: function(completion) { + completion(null, OT.$.getCookie('opentok_client_id')); + }, - OT.Rumor.Message.Publish = function(topics, message, headers) { - return new OT.Rumor.Message(OT.Rumor.MessageType.MESSAGE, topics, headers||{}, message || ''); - }; + set: function(guid, completion) { + OT.$.setCookie('opentok_client_id', guid); + completion(null); + } + }); - // This message is used to implement keepalives on the persistent - // socket connection between the client and server. Every time the - // client sends a PING to the server, the server will respond with - // a PONG. - OT.Rumor.Message.Ping = function() { - return new OT.Rumor.Message(OT.Rumor.MessageType.PING, [], {}, ''); - }; +})(window); -}(this)); -!(function() { +// tb_require('../helpers.js') +// tb_require('./web_rtc.js') - // Rumor Messaging for JS - // - // https://tbwiki.tokbox.com/index.php/Raptor_Messages_(Sent_as_a_RumorMessage_payload_in_JSON) - // - // @todo Raptor { - // Look at disconnection cleanup: i.e. subscriber + publisher cleanup - // Add error codes for all the error cases - // Write unit tests for SessionInfo - // Write unit tests for Session - // Make use of the new DestroyedEvent - // Remove dependency on OT.properties - // OT.Capabilities must be part of the Raptor namespace - // Add Dependability commands - // Think about noConflict, or whether we should just use the OT namespace - // Think about how to expose OT.publishers, OT.subscribers, and OT.sessions if messaging was - // being included as a component - // Another solution to the problem of having publishers/subscribers/etc would be to make - // Raptor Socket a separate component from Dispatch (dispatch being more business logic) - // Look at the coupling of OT.sessions to OT.Raptor.Socket - // } - // - // @todo Raptor Docs { - // Document payload formats for incoming messages (what are the payloads for - // STREAM CREATED/MODIFIED for example) - // Document how keepalives work - // Document all the Raptor actions and types - // Document the session connect flow (including error cases) - // } +// Web OT Helpers +!(function() { + /* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ + /* global OT */ - OT.Raptor = { - Actions: { - //General - CONNECT: 100, - CREATE: 101, - UPDATE: 102, - DELETE: 103, - STATE: 104, - - //Moderation - FORCE_DISCONNECT: 105, - FORCE_UNPUBLISH: 106, - SIGNAL: 107, - - //Archives - CREATE_ARCHIVE: 108, - CLOSE_ARCHIVE: 109, - START_RECORDING_SESSION: 110, - STOP_RECORDING_SESSION: 111, - START_RECORDING_STREAM: 112, - STOP_RECORDING_STREAM: 113, - LOAD_ARCHIVE: 114, - START_PLAYBACK: 115, - STOP_PLAYBACK: 116, - - //AppState - APPSTATE_PUT: 117, - APPSTATE_DELETE: 118, - - // JSEP - OFFER: 119, - ANSWER: 120, - PRANSWER: 121, - CANDIDATE: 122, - SUBSCRIBE: 123, - UNSUBSCRIBE: 124, - QUERY: 125, - SDP_ANSWER: 126, - - //KeepAlive - PONG: 127, - REGISTER: 128, //Used for registering streams. + var nativeGetUserMedia, + vendorToW3CErrors, + gumNamesToMessages, + mapVendorErrorName, + parseErrorEvent, + areInvalidConstraints; - QUALITY_CHANGED: 129 - }, + // Handy cross-browser getUserMedia shim. Inspired by some code from Adam Barth + nativeGetUserMedia = (function() { + if (navigator.getUserMedia) { + return OT.$.bind(navigator.getUserMedia, navigator); + } else if (navigator.mozGetUserMedia) { + return OT.$.bind(navigator.mozGetUserMedia, navigator); + } else if (navigator.webkitGetUserMedia) { + return OT.$.bind(navigator.webkitGetUserMedia, navigator); + } else if (OTPlugin.isInstalled()) { + return OT.$.bind(OTPlugin.getUserMedia, OTPlugin); + } + })(); - Types: { - //RPC - RPC_REQUEST: 100, - RPC_RESPONSE: 101, - - //EVENT - STREAM: 102, - ARCHIVE: 103, - CONNECTION: 104, - APPSTATE: 105, - CONNECTIONCOUNT: 106, - MODERATION: 107, - SIGNAL: 108, - SUBSCRIBER: 110, + // Mozilla error strings and the equivalent W3C names. NOT_SUPPORTED_ERROR does not + // exist in the spec right now, so we'll include Mozilla's error description. + // Chrome TrackStartError is triggered when the camera is already used by another app (Windows) + vendorToW3CErrors = { + PERMISSION_DENIED: 'PermissionDeniedError', + NOT_SUPPORTED_ERROR: 'NotSupportedError', + MANDATORY_UNSATISFIED_ERROR: ' ConstraintNotSatisfiedError', + NO_DEVICES_FOUND: 'NoDevicesFoundError', + HARDWARE_UNAVAILABLE: 'HardwareUnavailableError', + TrackStartError: 'HardwareUnavailableError' + }; - //JSEP Protocol - JSEP: 109 - } + gumNamesToMessages = { + PermissionDeniedError: 'End-user denied permission to hardware devices', + PermissionDismissedError: 'End-user dismissed permission to hardware devices', + NotSupportedError: 'A constraint specified is not supported by the browser.', + ConstraintNotSatisfiedError: 'It\'s not possible to satisfy one or more constraints ' + + 'passed into the getUserMedia function', + OverconstrainedError: 'Due to changes in the environment, one or more mandatory ' + + 'constraints can no longer be satisfied.', + NoDevicesFoundError: 'No voice or video input devices are available on this machine.', + HardwareUnavailableError: 'The selected voice or video devices are unavailable. Verify ' + + 'that the chosen devices are not in use by another application.' }; -}(this)); -!(function() { + // Map vendor error strings to names and messages if possible + mapVendorErrorName = function mapVendorErrorName(vendorErrorName, vendorErrors) { + var errorName, errorMessage; + + if(vendorErrors.hasOwnProperty(vendorErrorName)) { + errorName = vendorErrors[vendorErrorName]; + } else { + // This doesn't map to a known error from the Media Capture spec, it's + // probably a custom vendor error message. + errorName = vendorErrorName; + } + if(gumNamesToMessages.hasOwnProperty(errorName)) { + errorMessage = gumNamesToMessages[errorName]; + } else { + errorMessage = 'Unknown Error while getting user media'; + } - OT.Raptor.serializeMessage = function (message) { - return JSON.stringify(message); + return { + name: errorName, + message: errorMessage + }; }; + // Parse and normalise a getUserMedia error event from Chrome or Mozilla + // @ref http://dev.w3.org/2011/webrtc/editor/getusermedia.html#idl-def-NavigatorUserMediaError + parseErrorEvent = function parseErrorObject(event) { + var error; - // Deserialising a Raptor message mainly means doing a JSON.parse on it. - // We do decorate the final message with a few extra helper properies though. - // - // These include: - // * typeName: A human readable version of the Raptor type. E.g. STREAM instead of 102 - // * actionName: A human readable version of the Raptor action. E.g. CREATE instead of 101 - // * signature: typeName and actionName combined. This is mainly for debugging. E.g. A type - // of 102 and an action of 101 would result in a signature of "STREAM:CREATE" - // - OT.Raptor.deserializeMessage = function (msg) { - if (msg.length === 0) return {}; - - var message = JSON.parse(msg), - bits = message.uri.substr(1).split('/'); + if (OT.$.isObject(event) && event.name) { + error = mapVendorErrorName(event.name, vendorToW3CErrors); + error.constraintName = event.constraintName; + } else if (typeof event === 'string') { + error = mapVendorErrorName(event, vendorToW3CErrors); + } else { + error = { + message: 'Unknown Error type while getting media' + }; + } - // Remove the Raptor protocol version - bits.shift(); - if (bits[bits.length-1] === '') bits.pop(); + return error; + }; - message.params = {}; - for (var i=0, numBits=bits.length ; i 6) { - message.resource = bits[bits.length-4] + '_' + bits[bits.length-2]; - } else { - message.resource = bits[bits.length-2]; - } - } - else { - if (bits[bits.length-1] === 'channel' && bits.length > 5) { - message.resource = bits[bits.length-3] + '_' + bits[bits.length-1]; - } else { - message.resource = bits[bits.length-1]; + for (var key in constraints) { + if(!constraints.hasOwnProperty(key)) { + continue; } + if (constraints[key]) return false; } - message.signature = message.resource + '#' + message.method; - return message; + return true; }; - OT.Raptor.unboxFromRumorMessage = function (rumorMessage) { - var message = OT.Raptor.deserializeMessage(rumorMessage.data); - message.transactionId = rumorMessage.transactionId; - message.fromAddress = rumorMessage.headers['X-TB-FROM-ADDRESS']; - return message; - }; + // A wrapper for the builtin navigator.getUserMedia. In addition to the usual + // getUserMedia behaviour, this helper method also accepts a accessDialogOpened + // and accessDialogClosed callback. + // + // @memberof OT.$ + // @private + // + // @param {Object} constraints + // A dictionary of constraints to pass to getUserMedia. See + // http://dev.w3.org/2011/webrtc/editor/getusermedia.html#idl-def-MediaStreamConstraints + // in the Media Capture and Streams spec for more info. + // + // @param {function} success + // Called when getUserMedia completes successfully. The callback will be passed a WebRTC + // Stream object. + // + // @param {function} failure + // Called when getUserMedia fails to access a user stream. It will be passed an object + // with a code property representing the error that occurred. + // + // @param {function} accessDialogOpened + // Called when the access allow/deny dialog is opened. + // + // @param {function} accessDialogClosed + // Called when the access allow/deny dialog is closed. + // + // @param {function} accessDenied + // Called when access is denied to the camera/mic. This will be either because + // the user has clicked deny or because a particular origin is permanently denied. + // + OT.$.getUserMedia = function(constraints, success, failure, accessDialogOpened, + accessDialogClosed, accessDenied, customGetUserMedia) { + + var getUserMedia = nativeGetUserMedia; - OT.Raptor.parseIceServers = function (message) { - try { - return JSON.parse(message.data).content.iceServers; - } catch (e) { - return []; + if(OT.$.isFunction(customGetUserMedia)) { + getUserMedia = customGetUserMedia; } - }; - - OT.Raptor.Message = {}; - - OT.Raptor.Message.connections = {}; - - OT.Raptor.Message.connections.create = function (apiKey, sessionId, connectionId) { - return OT.Raptor.serializeMessage({ - method: 'create', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId + '/connection/' + connectionId, - content: { - userAgent: OT.$.userAgent() - } - }); - }; + // All constraints are false, we don't allow this. This may be valid later + // depending on how/if we integrate data channels. + if (areInvalidConstraints(constraints)) { + OT.error('Couldn\'t get UserMedia: All constraints were false'); + // Using a ugly dummy-code for now. + failure.call(null, { + name: 'NO_VALID_CONSTRAINTS', + message: 'Video and Audio was disabled, you need to enabled at least one' + }); - OT.Raptor.Message.connections.destroy = function (apiKey, sessionId, connectionId) { - return OT.Raptor.serializeMessage({ - method: 'delete', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId + '/connection/' + connectionId, - content: {} - }); - }; + return; + } + var triggerOpenedTimer = null, + displayedPermissionDialog = false, - OT.Raptor.Message.sessions = {}; + finaliseAccessDialog = function() { + if (triggerOpenedTimer) { + clearTimeout(triggerOpenedTimer); + } - OT.Raptor.Message.sessions.get = function (apiKey, sessionId) { - return OT.Raptor.serializeMessage({ - method: 'read', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId, - content: {} - }); - }; + if (displayedPermissionDialog && accessDialogClosed) accessDialogClosed(); + }, + triggerOpened = function() { + triggerOpenedTimer = null; + displayedPermissionDialog = true; - OT.Raptor.Message.streams = {}; + if (accessDialogOpened) accessDialogOpened(); + }, - OT.Raptor.Message.streams.get = function (apiKey, sessionId, streamId) { - return OT.Raptor.serializeMessage({ - method: 'read', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId + '/stream/' + streamId, - content: {} - }); - }; + onStream = function(stream) { + finaliseAccessDialog(); + success.call(null, stream); + }, - OT.Raptor.Message.streams.create = function (apiKey, sessionId, streamId, name, videoOrientation, - videoWidth, videoHeight, hasAudio, hasVideo, frameRate, minBitrate, maxBitrate) { - var channels = []; + onError = function(event) { + finaliseAccessDialog(); + var error = parseErrorEvent(event); - if (hasAudio !== void 0) { - channels.push({ - id: 'audio1', - type: 'audio', - active: hasAudio - }); - } + // The error name 'PERMISSION_DENIED' is from an earlier version of the spec + if (error.name === 'PermissionDeniedError' || error.name === 'PermissionDismissedError') { + accessDenied.call(null, error); + } else { + failure.call(null, error); + } + }; - if (hasVideo !== void 0) { - var channel = { - id: 'video1', - type: 'video', - active: hasVideo, - width: videoWidth, - height: videoHeight, - orientation: videoOrientation - }; - if (frameRate) channel.frameRate = frameRate; - channels.push(channel); + try { + getUserMedia(constraints, onStream, onError); + } catch (e) { + OT.error('Couldn\'t get UserMedia: ' + e.toString()); + onError(); + return; } - var messageContent = { - id: streamId, - name: name, - channel: channels - }; - - if (minBitrate) messageContent.minBitrate = minBitrate; - if (maxBitrate) messageContent.maxBitrate = maxBitrate; - - return OT.Raptor.serializeMessage({ - method: 'create', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId + '/stream/' + streamId, - content: messageContent - }); - }; - - OT.Raptor.Message.streams.destroy = function (apiKey, sessionId, streamId) { - return OT.Raptor.serializeMessage({ - method: 'delete', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId + '/stream/' + streamId, - content: {} - }); - }; - - OT.Raptor.Message.streams.offer = function (apiKey, sessionId, streamId, offerSdp) { - return OT.Raptor.serializeMessage({ - method: 'offer', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId + '/stream/' + streamId, - content: { - sdp: offerSdp - } - }); - }; + // The 'remember me' functionality of WebRTC only functions over HTTPS, if + // we aren't on HTTPS then we should definitely be displaying the access + // dialog. + // + // If we are on HTTPS, we'll wait 500ms to see if we get a stream + // immediately. If we do then the user had clicked 'remember me'. Otherwise + // we assume that the accessAllowed dialog is visible. + // + // @todo benchmark and see if 500ms is a reasonable number. It seems like + // we should know a lot quicker. + // + if (location.protocol.indexOf('https') === -1) { + // Execute after, this gives the client a chance to bind to the + // accessDialogOpened event. + triggerOpenedTimer = setTimeout(triggerOpened, 100); - OT.Raptor.Message.streams.answer = function (apiKey, sessionId, streamId, answerSdp) { - return OT.Raptor.serializeMessage({ - method: 'answer', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId + '/stream/' + streamId, - content: { - sdp: answerSdp - } - }); + } else { + // wait a second and then trigger accessDialogOpened + triggerOpenedTimer = setTimeout(triggerOpened, 500); + } }; - OT.Raptor.Message.streams.candidate = function (apiKey, sessionId, streamId, candidate) { - return OT.Raptor.serializeMessage({ - method: 'candidate', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId + '/stream/' + streamId, - content: candidate - }); - }; +})(); - OT.Raptor.Message.streamChannels = {}; - OT.Raptor.Message.streamChannels.update = - function (apiKey, sessionId, streamId, channelId, attributes) { - return OT.Raptor.serializeMessage({ - method: 'update', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId + '/stream/' + - streamId + '/channel/' + channelId, - content: attributes - }); - }; +// tb_require('../helpers.js') +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - OT.Raptor.Message.subscribers = {}; +!(function() { - OT.Raptor.Message.subscribers.create = - function (apiKey, sessionId, streamId, subscriberId, connectionId, channelsToSubscribeTo) { - var content = { - id: subscriberId, - connection: connectionId, - keyManagementMethod: OT.$.supportedCryptoScheme(), - bundleSupport: OT.$.hasCapabilities('bundle'), - rtcpMuxSupport: OT.$.hasCapabilities('RTCPMux') + var adjustModal = function(callback) { + return function setFullHeightDocument(window, document) { + // required in IE8 + document.querySelector('html').style.height = document.body.style.height = '100%'; + callback(window, document); }; - if (channelsToSubscribeTo) content.channel = channelsToSubscribeTo; - - return OT.Raptor.serializeMessage({ - method: 'create', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId + - '/stream/' + streamId + '/subscriber/' + subscriberId, - content: content - }); - }; - - OT.Raptor.Message.subscribers.destroy = function (apiKey, sessionId, streamId, subscriberId) { - return OT.Raptor.serializeMessage({ - method: 'delete', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId + - '/stream/' + streamId + '/subscriber/' + subscriberId, - content: {} - }); }; - OT.Raptor.Message.subscribers.update = - function (apiKey, sessionId, streamId, subscriberId, attributes) { - return OT.Raptor.serializeMessage({ - method: 'update', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId + - '/stream/' + streamId + '/subscriber/' + subscriberId, - content: attributes + var addCss = function(document, url, callback) { + var head = document.head || document.getElementsByTagName('head')[0]; + var cssTag = OT.$.createElement('link', { + type: 'text/css', + media: 'screen', + rel: 'stylesheet', + href: url }); - }; - - - OT.Raptor.Message.subscribers.candidate = - function (apiKey, sessionId, streamId, subscriberId, candidate) { - return OT.Raptor.serializeMessage({ - method: 'candidate', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId + - '/stream/' + streamId + '/subscriber/' + subscriberId, - content: candidate + head.appendChild(cssTag); + OT.$.on(cssTag, 'error', function(error) { + OT.error('Could not load CSS for dialog', url, error && error.message || error); }); + OT.$.on(cssTag, 'load', callback); }; - OT.Raptor.Message.subscribers.offer = - function (apiKey, sessionId, streamId, subscriberId, offerSdp) { - return OT.Raptor.serializeMessage({ - method: 'offer', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId + - '/stream/' + streamId + '/subscriber/' + subscriberId, - content: { - sdp: offerSdp - } + var addDialogCSS = function(document, urls, callback) { + var allURLs = [ + '//fonts.googleapis.com/css?family=Didact+Gothic', + OT.properties.cssURL + ].concat(urls); + var remainingStylesheets = allURLs.length; + OT.$.forEach(allURLs, function(stylesheetUrl) { + addCss(document, stylesheetUrl, function() { + if(--remainingStylesheets <= 0) { + callback(); + } + }); }); - }; - OT.Raptor.Message.subscribers.answer = - function (apiKey, sessionId, streamId, subscriberId, answerSdp) { - return OT.Raptor.serializeMessage({ - method: 'answer', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId + - '/stream/' + streamId + '/subscriber/' + subscriberId, - content: { - sdp: answerSdp - } - }); }; - - OT.Raptor.Message.subscriberChannels = {}; - - OT.Raptor.Message.subscriberChannels.update = - function (apiKey, sessionId, streamId, subscriberId, channelId, attributes) { - return OT.Raptor.serializeMessage({ - method: 'update', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId + - '/stream/' + streamId + '/subscriber/' + subscriberId + '/channel/' + channelId, - content: attributes - }); + var templateElement = function(classes, children, tagName) { + var el = OT.$.createElement(tagName || 'div', { 'class': classes }, children, this); + el.on = OT.$.bind(OT.$.on, OT.$, el); + el.off = OT.$.bind(OT.$.off, OT.$, el); + return el; }; + var checkBoxElement = function (classes, nameAndId, onChange) { + var checkbox = templateElement.call(this, '', null, 'input'); + checkbox = OT.$(checkbox).on('change', onChange); - OT.Raptor.Message.signals = {}; - - OT.Raptor.Message.signals.create = function (apiKey, sessionId, toAddress, type, data) { - var content = {}; - if (type !== void 0) content.type = type; - if (data !== void 0) content.data = data; + if (OT.$.env.name === 'IE' && OT.$.env.version <= 8) { + // Fix for IE8 not triggering the change event + checkbox.on('click', function() { + checkbox.first.blur(); + checkbox.first.focus(); + }); + } - return OT.Raptor.serializeMessage({ - method: 'signal', - uri: '/v2/partner/' + apiKey + '/session/' + sessionId + - (toAddress !== void 0 ? '/connection/' + toAddress : '') + '/signal/' + OT.$.uuid(), - content: content + checkbox.attr({ + name: nameAndId, + id: nameAndId, + type: 'checkbox' }); - }; - -}(this)); -!(function() { - - var MAX_SIGNAL_DATA_LENGTH = 8192, - MAX_SIGNAL_TYPE_LENGTH = 128; - - // - // Error Codes: - // 413 - Type too long - // 400 - Type is invalid - // 413 - Data too long - // 400 - Data is invalid (can't be parsed as JSON) - // 429 - Rate limit exceeded - // 500 - Websocket connection is down - // 404 - To connection does not exist - // 400 - To is invalid - // - OT.Signal = function(sessionId, fromConnectionId, options) { - var isInvalidType = function(type) { - // Our format matches the unreserved characters from the URI RFC: - // http://www.ietf.org/rfc/rfc3986 - return !/^[a-zA-Z0-9\-\._~]+$/.exec(type); - }, - - validateTo = function(toAddress) { - if (!toAddress) { - return { - code: 400, - reason: 'The signal type was null or an empty String. Either set it to a non-empty ' + - 'String value or omit it' - }; - } - - if ( !(toAddress instanceof OT.Connection || toAddress instanceof OT.Session) ) { - return { - code: 400, - reason: 'The To field was invalid' - }; - } - - return null; - }, - - validateType = function(type) { - var error = null; - - if (type === null || type === void 0) { - error = { - code: 400, - reason: 'The signal type was null or undefined. Either set it to a String value or ' + - 'omit it' - }; - } - else if (type.length > MAX_SIGNAL_TYPE_LENGTH) { - error = { - code: 413, - reason: 'The signal type was too long, the maximum length of it is ' + - MAX_SIGNAL_TYPE_LENGTH + ' characters' - }; - } - else if ( isInvalidType(type) ) { - error = { - code: 400, - reason: 'The signal type was invalid, it can only contain letters, ' + - 'numbers, \'-\', \'_\', and \'~\'.' - }; - } - - return error; - }, - validateData = function(data) { - var error = null; - if (data === null || data === void 0) { - error = { - code: 400, - reason: 'The signal data was null or undefined. Either set it to a String value or ' + - 'omit it' - }; - } - else { - try { - if (JSON.stringify(data).length > MAX_SIGNAL_DATA_LENGTH) { - error = { - code: 413, - reason: 'The data field was too long, the maximum size of it is ' + - MAX_SIGNAL_DATA_LENGTH + ' characters' - }; - } - } - catch(e) { - error = {code: 400, reason: 'The data field was not valid JSON'}; - } - } + return checkbox.first; + }; - return error; - }; + var linkElement = function(children, href, classes) { + var link = templateElement.call(this, classes || '', children, 'a'); + link.setAttribute('href', href); + return link; + }; + OT.Dialogs = {}; - this.toRaptorMessage = function() { - var to = this.to; + OT.Dialogs.Plugin = {}; - if (to && typeof(to) !== 'string') { - to = to.id; - } + OT.Dialogs.Plugin.promptToInstall = function() { + var modal = new OT.$.Modal(adjustModal(function(window, document) { - return OT.Raptor.Message.signals.create(OT.APIKEY, sessionId, to, this.type, this.data); - }; + var el = OT.$.bind(templateElement, document), + btn = function(children, size) { + var classes = 'OT_dialog-button ' + + (size ? 'OT_dialog-button-' + size : 'OT_dialog-button-large'), + b = el(classes, children); - this.toHash = function() { - return options; - }; + b.enable = function() { + OT.$.removeClass(this, 'OT_dialog-button-disabled'); + return this; + }; + + b.disable = function() { + OT.$.addClass(this, 'OT_dialog-button-disabled'); + return this; + }; + return b; + }, + downloadButton = btn('Download plugin'), + cancelButton = btn('cancel', 'small'), + refreshButton = btn('Refresh browser'), + acceptEULA, + checkbox, + close, + root; - this.error = null; + OT.$.addClass(cancelButton, 'OT_dialog-no-natural-margin OT_dialog-button-block'); + OT.$.addClass(refreshButton, 'OT_dialog-no-natural-margin'); - if (options) { - if (options.hasOwnProperty('data')) { - this.data = OT.$.clone(options.data); - this.error = validateData(this.data); + function onDownload() { + modal.trigger('download'); + setTimeout(function() { + root.querySelector('.OT_dialog-messages-main').innerHTML = + 'Plugin installation successful'; + var sections = root.querySelectorAll('.OT_dialog-section'); + OT.$.addClass(sections[0], 'OT_dialog-hidden'); + OT.$.removeClass(sections[1], 'OT_dialog-hidden'); + }, 3000); } - if (options.hasOwnProperty('to')) { - this.to = options.to; - - if (!this.error) { - this.error = validateTo(this.to); - } + function onRefresh() { + modal.trigger('refresh'); } - if (options.hasOwnProperty('type')) { - if (!this.error) { - this.error = validateType(options.type); + function onToggleEULA() { + if (checkbox.checked) { + enableButtons(); + } + else { + disableButtons(); } - this.type = options.type; } - } - - this.valid = this.error === null; - }; - -}(this)); -!(function() { - function SignalError(code, reason) { - this.code = code; - this.reason = reason; - } + function enableButtons() { + downloadButton.enable(); + downloadButton.on('click', onDownload); - // The Dispatcher bit is purely to make testing simpler, it defaults to a new OT.Raptor.Dispatcher - // so in normal operation you would omit it. - OT.Raptor.Socket = function(widgetId, messagingSocketUrl, symphonyUrl, dispatcher) { - var _states = ['disconnected', 'connecting', 'connected', 'error', 'disconnecting'], - _sessionId, - _token, - _rumor, - _dispatcher, - _completion; + refreshButton.enable(); + refreshButton.on('click', onRefresh); + } + function disableButtons() { + downloadButton.disable(); + downloadButton.off('click', onDownload); - //// Private API - var setState = OT.$.statable(this, _states, 'disconnected'), + refreshButton.disable(); + refreshButton.off('click', onRefresh); + } - onConnectComplete = function onConnectComplete(error) { - if (error) { - setState('error'); - } - else { - setState('connected'); - } + downloadButton.disable(); + refreshButton.disable(); - _completion.apply(null, arguments); - }, + cancelButton.on('click', function() { + modal.trigger('cancelButtonClicked'); + modal.close(); + }); - onClose = OT.$.bind(function onClose (err) { - var reason = this.is('disconnecting') ? 'clientDisconnected' : 'networkDisconnected'; + close = el('OT_closeButton', '×') + .on('click', function() { + modal.trigger('closeButtonClicked'); + modal.close(); + }).first; - if(err && err.code === 4001) { - reason = 'networkTimedout'; - } + var protocol = (window.location.protocol.indexOf('https') >= 0 ? 'https' : 'http'); + acceptEULA = linkElement.call(document, + 'end-user license agreement', + protocol + '://tokbox.com/support/ie-eula'); - setState('disconnected'); + checkbox = checkBoxElement.call(document, null, 'acceptEULA', onToggleEULA); - _dispatcher.onClose(reason); - }, this), + root = el('OT_dialog-centering', [ + el('OT_dialog-centering-child', [ + el('OT_root OT_dialog OT_dialog-plugin-prompt', [ + close, + el('OT_dialog-messages', [ + el('OT_dialog-messages-main', 'This app requires real-time communication') + ]), + el('OT_dialog-section', [ + el('OT_dialog-single-button-with-title', [ + el('OT_dialog-button-title', [ + checkbox, + (function() { + var x = el('', 'accept', 'label'); + x.setAttribute('for', checkbox.id); + x.style.margin = '0 5px'; + return x; + })(), + acceptEULA + ]), + el('OT_dialog-actions-card', [ + downloadButton, + cancelButton + ]) + ]) + ]), + el('OT_dialog-section OT_dialog-hidden', [ + el('OT_dialog-button-title', [ + 'You can now enjoy webRTC enabled video via Internet Explorer.' + ]), + refreshButton + ]) + ]) + ]) + ]); - onError = function onError () {}; - // @todo what does having an error mean? Are they always fatal? Are we disconnected now? + addDialogCSS(document, [], function() { + document.body.appendChild(root); + }); + })); + return modal; + }; - //// Public API + OT.Dialogs.Plugin.promptToReinstall = function() { + var modal = new OT.$.Modal(adjustModal(function(window, document) { - this.connect = function (token, sessionInfo, completion) { - if (!this.is('disconnected', 'error')) { - OT.warn('Cannot connect the Raptor Socket as it is currently connected. You should ' + - 'disconnect first.'); - return; - } + var el = OT.$.bind(templateElement, document), + close, + okayButton, + root; - setState('connecting'); - _sessionId = sessionInfo.sessionId; - _token = token; - _completion = completion; - - var connectionId = OT.$.uuid(), - rumorChannel = '/v2/partner/' + OT.APIKEY + '/session/' + _sessionId; - - _rumor = new OT.Rumor.Socket(messagingSocketUrl, symphonyUrl); - _rumor.onClose(onClose); - _rumor.onMessage(OT.$.bind(_dispatcher.dispatch, _dispatcher)); + close = el('OT_closeButton', '×') + .on('click', function() { + modal.trigger('closeButtonClicked'); + modal.close(); + }).first; + + okayButton = + el('OT_dialog-button OT_dialog-button-large OT_dialog-no-natural-margin', 'Okay') + .on('click', function() { + modal.trigger('okay'); + }).first; + + root = el('OT_dialog-centering', [ + el('OT_dialog-centering-child', [ + el('OT_ROOT OT_dialog OT_dialog-plugin-reinstall', [ + close, + el('OT_dialog-messages', [ + el('OT_dialog-messages-main', 'Reinstall Opentok Plugin'), + el('OT_dialog-messages-minor', 'Uh oh! Try reinstalling the OpenTok plugin again ' + + 'to enable real-time video communication for Internet Explorer.') + ]), + el('OT_dialog-section', [ + el('OT_dialog-single-button', okayButton) + ]) + ]) + ]) + ]); - _rumor.connect(connectionId, OT.$.bind(function(error) { - if (error) { - error.message = 'WebSocketConnection:' + error.code + ':' + error.message; - onConnectComplete(error); - return; - } + addDialogCSS(document, [], function() { + document.body.appendChild(root); + }); - // we do this here to avoid getting connect errors twice - _rumor.onError(onError); + })); - OT.debug('Raptor Socket connected. Subscribing to ' + - rumorChannel + ' on ' + messagingSocketUrl); + return modal; + }; - _rumor.subscribe([rumorChannel]); + OT.Dialogs.Plugin.updateInProgress = function() { - //connect to session - var connectMessage = OT.Raptor.Message.connections.create(OT.APIKEY, - _sessionId, _rumor.id()); - this.publish(connectMessage, {'X-TB-TOKEN-AUTH': _token}, OT.$.bind(function(error) { - if (error) { - error.message = 'ConnectToSession:' + error.code + - ':Received error response to connection create message.'; - onConnectComplete(error); - return; - } + var progressBar, + progressText, + progressValue = 0; - this.publish( OT.Raptor.Message.sessions.get(OT.APIKEY, _sessionId), - function (error) { - if (error) { - error.message = 'GetSessionState:' + error.code + - ':Received error response to session read'; - } - onConnectComplete.apply(null, arguments); - }); - }, this)); - }, this)); - }; + var modal = new OT.$.Modal(adjustModal(function(window, document) { + var el = OT.$.bind(templateElement, document), + root; - this.disconnect = function (drainSocketBuffer) { - if (this.is('disconnected')) return; + progressText = el('OT_dialog-plugin-upgrade-percentage', '0%', 'strong'); - setState('disconnecting'); - _rumor.disconnect(drainSocketBuffer); - }; + progressBar = el('OT_dialog-progress-bar-fill'); - // Publishs +message+ to the Symphony app server. - // - // The completion handler is optional, as is the headers - // dict, but if you provide the completion handler it must - // be the last argument. - // - this.publish = function (message, headers, completion) { - if (_rumor.isNot('connected')) { - OT.error('OT.Raptor.Socket: cannot publish until the socket is connected.' + message); - return; - } + root = el('OT_dialog-centering', [ + el('OT_dialog-centering-child', [ + el('OT_ROOT OT_dialog OT_dialog-plugin-upgrading', [ + el('OT_dialog-messages', [ + el('OT_dialog-messages-main', [ + 'One moment please... ', + progressText + ]), + el('OT_dialog-progress-bar', progressBar), + el('OT_dialog-messages-minor OT_dialog-no-natural-margin', + 'Please wait while the OpenTok plugin is updated') + ]) + ]) + ]) + ]); - var transactionId = OT.$.uuid(), - _headers = {}, - _completion; - - // Work out if which of the optional arguments (headers, completion) - // have been provided. - if (headers) { - if (OT.$.isFunction(headers)) { - _headers = {}; - _completion = headers; + addDialogCSS(document, [], function() { + document.body.appendChild(root); + if(progressValue != null) { + modal.setUpdateProgress(progressValue); } - else { - _headers = headers; + }); + })); + + modal.setUpdateProgress = function(newProgress) { + if(progressBar && progressText) { + if(newProgress > 99) { + OT.$.css(progressBar, 'width', ''); + progressText.innerHTML = '100%'; + } else if(newProgress < 1) { + OT.$.css(progressBar, 'width', '0%'); + progressText.innerHTML = '0%'; + } else { + OT.$.css(progressBar, 'width', newProgress + '%'); + progressText.innerHTML = newProgress + '%'; } + } else { + progressValue = newProgress; } - if (!_completion && completion && OT.$.isFunction(completion)) _completion = completion; + }; + return modal; + }; - if (_completion) _dispatcher.registerCallback(transactionId, _completion); + OT.Dialogs.Plugin.updateComplete = function(error) { + var modal = new OT.$.Modal(adjustModal(function(window, document) { + var el = OT.$.bind(templateElement, document), + reloadButton, + root; - OT.debug('OT.Raptor.Socket Publish (ID:' + transactionId + ') '); - OT.debug(message); + reloadButton = + el('OT_dialog-button OT_dialog-button-large OT_dialog-no-natural-margin', 'Reload') + .on('click', function() { + modal.trigger('reload'); + }).first; - _rumor.publish([symphonyUrl], message, OT.$.extend(_headers, { - 'Content-Type': 'application/x-raptor+v2', - 'TRANSACTION-ID': transactionId, - 'X-TB-FROM-ADDRESS': _rumor.id() - })); + var msgs; - return transactionId; - }; + if(error) { + msgs = ['Update Failed.', error + '' || 'NO ERROR']; + } else { + msgs = ['Update Complete.', + 'The OpenTok plugin has been succesfully updated. ' + + 'Please reload your browser.']; + } - // Register a new stream against _sessionId - this.streamCreate = function(name, orientation, encodedWidth, encodedHeight, - hasAudio, hasVideo, frameRate, minBitrate, maxBitrate, completion) { - var streamId = OT.$.uuid(), - message = OT.Raptor.Message.streams.create( OT.APIKEY, - _sessionId, - streamId, - name, - orientation, - encodedWidth, - encodedHeight, - hasAudio, - hasVideo, - frameRate, - minBitrate, - maxBitrate); + root = el('OT_dialog-centering', [ + el('OT_dialog-centering-child', [ + el('OT_root OT_dialog OT_dialog-plugin-upgraded', [ + el('OT_dialog-messages', [ + el('OT_dialog-messages-main', msgs[0]), + el('OT_dialog-messages-minor', msgs[1]) + ]), + el('OT_dialog-single-button', reloadButton) + ]) + ]) + ]); - this.publish(message, function(error, message) { - completion(error, streamId, message); + addDialogCSS(document, [], function() { + document.body.appendChild(root); }); - }; - this.streamDestroy = function(streamId) { - this.publish( OT.Raptor.Message.streams.destroy(OT.APIKEY, _sessionId, streamId) ); - }; - - this.streamChannelUpdate = function(streamId, channelId, attributes) { - this.publish( OT.Raptor.Message.streamChannels.update(OT.APIKEY, _sessionId, - streamId, channelId, attributes) ); - }; - - this.subscriberCreate = function(streamId, subscriberId, channelsToSubscribeTo, completion) { - this.publish( OT.Raptor.Message.subscribers.create(OT.APIKEY, _sessionId, - streamId, subscriberId, _rumor.id(), channelsToSubscribeTo), completion ); - }; - - this.subscriberDestroy = function(streamId, subscriberId) { - this.publish( OT.Raptor.Message.subscribers.destroy(OT.APIKEY, _sessionId, - streamId, subscriberId) ); - }; - - this.subscriberUpdate = function(streamId, subscriberId, attributes) { - this.publish( OT.Raptor.Message.subscribers.update(OT.APIKEY, _sessionId, - streamId, subscriberId, attributes) ); - }; + })); - this.subscriberChannelUpdate = function(streamId, subscriberId, channelId, attributes) { - this.publish( OT.Raptor.Message.subscriberChannels.update(OT.APIKEY, _sessionId, - streamId, subscriberId, channelId, attributes) ); - }; + return modal; - this.forceDisconnect = function(connectionIdToDisconnect, completion) { - this.publish( OT.Raptor.Message.connections.destroy(OT.APIKEY, _sessionId, - connectionIdToDisconnect), completion ); - }; + }; - this.forceUnpublish = function(streamIdToUnpublish, completion) { - this.publish( OT.Raptor.Message.streams.destroy(OT.APIKEY, _sessionId, - streamIdToUnpublish), completion ); - }; - this.jsepCandidate = function(streamId, candidate) { - this.publish( - OT.Raptor.Message.streams.candidate(OT.APIKEY, _sessionId, streamId, candidate) - ); - }; +})(); - this.jsepCandidateP2p = function(streamId, subscriberId, candidate) { - this.publish( - OT.Raptor.Message.subscribers.candidate(OT.APIKEY, _sessionId, streamId, - subscriberId, candidate) - ); - }; +// tb_require('../helpers.js') +// tb_require('./web_rtc.js') - this.jsepOffer = function(streamId, offerSdp) { - this.publish( OT.Raptor.Message.streams.offer(OT.APIKEY, _sessionId, streamId, offerSdp) ); - }; +// Web OT Helpers +!(function(window) { - this.jsepOfferP2p = function(streamId, subscriberId, offerSdp) { - this.publish( OT.Raptor.Message.subscribers.offer(OT.APIKEY, _sessionId, streamId, - subscriberId, offerSdp) ); - }; + /* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ + /* global OT */ - this.jsepAnswer = function(streamId, answerSdp) { - this.publish( OT.Raptor.Message.streams.answer(OT.APIKEY, _sessionId, streamId, answerSdp) ); - }; + /// + // Device Helpers + // + // Support functions to enumerating and guerying device info + // - this.jsepAnswerP2p = function(streamId, subscriberId, answerSdp) { - this.publish( OT.Raptor.Message.subscribers.answer(OT.APIKEY, _sessionId, streamId, - subscriberId, answerSdp) ); - }; + var chromeToW3CDeviceKinds = { + audio: 'audioInput', + video: 'videoInput' + }; - this.signal = function(options, completion) { - var signal = new OT.Signal(_sessionId, _rumor.id(), options || {}); - if (!signal.valid) { - if (completion && OT.$.isFunction(completion)) { - completion( new SignalError(signal.error.code, signal.error.reason), signal.toHash() ); - } + OT.$.shouldAskForDevices = function(callback) { + var MST = window.MediaStreamTrack; - return; - } + if(MST != null && OT.$.isFunction(MST.getSources)) { + window.MediaStreamTrack.getSources(function(sources) { + var hasAudio = sources.some(function(src) { + return src.kind === 'audio'; + }); - this.publish( signal.toRaptorMessage(), function(err) { - var error; - if (err) error = new SignalError(err.code, err.message); + var hasVideo = sources.some(function(src) { + return src.kind === 'video'; + }); - if (completion && OT.$.isFunction(completion)) completion(error, signal.toHash()); + callback.call(null, { video: hasVideo, audio: hasAudio }); }); - }; - this.id = function() { - return _rumor && _rumor.id(); - }; + } else { + // This environment can't enumerate devices anyway, so we'll memorise this result. + OT.$.shouldAskForDevices = function(callback) { + setTimeout(OT.$.bind(callback, null, { video: true, audio: true })); + }; - if(dispatcher == null) { - dispatcher = new OT.Raptor.Dispatcher(); + OT.$.shouldAskForDevices(callback); } - _dispatcher = dispatcher; - }; - -}(this)); -!(function() { - /*global EventEmitter, util*/ - - // Connect error codes and reasons that Raptor can return. - var connectErrorReasons; - - connectErrorReasons = { - 409: 'This P2P session already has 2 participants.', - 410: 'The session already has four participants.', - 1004: 'The token passed is invalid.' }; - OT.Raptor.Dispatcher = function () { - - if(OT.isNodeModule) { - EventEmitter.call(this); + OT.$.getMediaDevices = function(callback) { + if(OT.$.hasCapabilities('getMediaDevices')) { + window.MediaStreamTrack.getSources(function(sources) { + var filteredSources = OT.$.filter(sources, function(source) { + return chromeToW3CDeviceKinds[source.kind] != null; + }); + callback(void 0, OT.$.map(filteredSources, function(source) { + return { + deviceId: source.id, + label: source.label, + kind: chromeToW3CDeviceKinds[source.kind] + }; + })); + }); } else { - OT.$.eventing(this, true); - this.emit = this.trigger; + callback(new Error('This browser does not support getMediaDevices APIs')); } - - this.callbacks = {}; }; - if(OT.isNodeModule) { - util.inherits(OT.Raptor.Dispatcher, EventEmitter); - } +})(window); +// tb_require('../helpers.js') - OT.Raptor.Dispatcher.prototype.registerCallback = function (transactionId, completion) { - this.callbacks[transactionId] = completion; - }; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* exported loadCSS */ - OT.Raptor.Dispatcher.prototype.triggerCallback = function (transactionId) { - /*, arg1, arg2, argN-1, argN*/ - if (!transactionId) return; +var loadCSS = function loadCSS(cssURL) { + var style = document.createElement('link'); + style.type = 'text/css'; + style.media = 'screen'; + style.rel = 'stylesheet'; + style.href = cssURL; + var head = document.head || document.getElementsByTagName('head')[0]; + head.appendChild(style); +}; - var completion = this.callbacks[transactionId]; +// tb_require('../helpers.js') +// tb_require('./properties.js') - if (completion && OT.$.isFunction(completion)) { - var args = Array.prototype.slice.call(arguments); - args.shift(); +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - completion.apply(null, args); - } +//-------------------------------------- +// JS Dynamic Config +//-------------------------------------- - delete this.callbacks[transactionId]; - }; +OT.Config = (function() { + var _loaded = false, + _global = {}, + _partners = {}, + _script, + _head = document.head || document.getElementsByTagName('head')[0], + _loadTimer, + + _clearTimeout = function() { + if (_loadTimer) { + clearTimeout(_loadTimer); + _loadTimer = null; + } + }, - OT.Raptor.Dispatcher.prototype.onClose = function(reason) { - this.emit('close', reason); - }; + _cleanup = function() { + _clearTimeout(); + if (_script) { + _script.onload = _script.onreadystatechange = null; - OT.Raptor.Dispatcher.prototype.dispatch = function(rumorMessage) { - // The special casing of STATUS messages is ugly. Need to think about - // how to better integrate this. + if ( _head && _script.parentNode ) { + _head.removeChild( _script ); + } - if (rumorMessage.type === OT.Rumor.MessageType.STATUS) { - OT.debug('OT.Raptor.dispatch: STATUS'); - OT.debug(rumorMessage); + _script = undefined; + } + }, - var error; + _onLoad = function() { + // Only IE and Opera actually support readyState on Script elements. + if (_script.readyState && !/loaded|complete/.test( _script.readyState )) { + // Yeah, we're not ready yet... + return; + } - if (rumorMessage.isError) { - error = new OT.Error(rumorMessage.status); - } + _clearTimeout(); - this.triggerCallback(rumorMessage.transactionId, error, rumorMessage); + if (!_loaded) { + // Our config script is loaded but there is not config (as + // replaceWith wasn't called). Something went wrong. Possibly + // the file we loaded wasn't actually a valid config file. + _this._onLoadTimeout(); + } + }, - return; - } + _onLoadError = function(/* event */) { + _cleanup(); - var message = OT.Raptor.unboxFromRumorMessage(rumorMessage); - OT.debug('OT.Raptor.dispatch ' + message.signature); - OT.debug(rumorMessage.data); + OT.warn('TB DynamicConfig failed to load due to an error'); + this.trigger('dynamicConfigLoadFailed'); + }, - switch(message.resource) { - case 'session': - this.dispatchSession(message); - break; + _getModule = function(moduleName, apiKey) { + if (apiKey && _partners[apiKey] && _partners[apiKey][moduleName]) { + return _partners[apiKey][moduleName]; + } - case 'connection': - this.dispatchConnection(message); - break; + return _global[moduleName]; + }, - case 'stream': - this.dispatchStream(message); - break; + _this; - case 'stream_channel': - this.dispatchStreamChannel(message); - break; + _this = { + // In ms + loadTimeout: 4000, - case 'subscriber': - this.dispatchSubscriber(message); - break; + _onLoadTimeout: function() { + _cleanup(); - case 'subscriber_channel': - this.dispatchSubscriberChannel(message); - break; + OT.warn('TB DynamicConfig failed to load in ' + _this.loadTimeout + ' ms'); + this.trigger('dynamicConfigLoadFailed'); + }, - case 'signal': - this.dispatchSignal(message); - break; + load: function(configUrl) { + if (!configUrl) throw new Error('You must pass a valid configUrl to Config.load'); - case 'archive': - this.dispatchArchive(message); - break; + _loaded = false; - default: - OT.warn('OT.Raptor.dispatch: Type ' + message.resource + ' is not currently implemented'); - } - }; + setTimeout(function() { + _script = document.createElement( 'script' ); + _script.async = 'async'; + _script.src = configUrl; + _script.onload = _script.onreadystatechange = OT.$.bind(_onLoad, _this); + _script.onerror = OT.$.bind(_onLoadError, _this); + _head.appendChild(_script); + },1); + + _loadTimer = setTimeout(function() { + _this._onLoadTimeout(); + }, this.loadTimeout); + }, - OT.Raptor.Dispatcher.prototype.dispatchSession = function (message) { - switch (message.method) { - case 'read': - this.emit('session#read', message.content, message.transactionId); - break; + isLoaded: function() { + return _loaded; + }, - default: - OT.warn('OT.Raptor.dispatch: ' + message.signature + ' is not currently implemented'); - } - }; + reset: function() { + this.off(); + _cleanup(); + _loaded = false; + _global = {}; + _partners = {}; + }, - OT.Raptor.Dispatcher.prototype.dispatchConnection = function (message) { + // This is public so that the dynamic config file can load itself. + // Using it for other purposes is discouraged, but not forbidden. + replaceWith: function(config) { + _cleanup(); - switch (message.method) { - case 'created': - this.emit('connection#created', message.content); - break; + if (!config) config = {}; + _global = config.global || {}; + _partners = config.partners || {}; - case 'deleted': - this.emit('connection#deleted', message.params.connection, message.reason); - break; + if (!_loaded) _loaded = true; + this.trigger('dynamicConfigChanged'); + }, - default: - OT.warn('OT.Raptor.dispatch: ' + message.signature + ' is not currently implemented'); + // @example Get the value that indicates whether exceptionLogging is enabled + // OT.Config.get('exceptionLogging', 'enabled'); + // + // @example Get a key for a specific partner, fallback to the default if there is + // no key for that partner + // OT.Config.get('exceptionLogging', 'enabled', 'apiKey'); + // + get: function(moduleName, key, apiKey) { + var module = _getModule(moduleName, apiKey); + return module ? module[key] : null; } }; - OT.Raptor.Dispatcher.prototype.dispatchStream = function (message) { - - switch (message.method) { - case 'created': - this.emit('stream#created', message.content, message.transactionId); - break; + OT.$.eventing(_this); - case 'deleted': - this.emit('stream#deleted', message.params.stream, - message.reason); - break; + return _this; +})(); +// tb_require('../helpers.js') - case 'updated': - this.emit('stream#updated', message.params.stream, - message.content); - break; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OTPlugin, OT */ +/// +// Capabilities +// +// Support functions to query browser/client Media capabilities. +// - // The JSEP process - case 'generateoffer': - case 'answer': - case 'pranswer': - case 'offer': - case 'candidate': - this.dispatchJsep(message.method, message); - break; - default: - OT.warn('OT.Raptor.dispatch: ' + message.signature + ' is not currently implemented'); - } - }; +// Indicates whether this client supports the getUserMedia +// API. +// +OT.$.registerCapability('getUserMedia', function() { + if (OT.$.env === 'Node') return false; + return !!(navigator.webkitGetUserMedia || + navigator.mozGetUserMedia || + OTPlugin.isInstalled()); +}); - OT.Raptor.Dispatcher.prototype.dispatchStreamChannel = function (message) { - switch (message.method) { - case 'updated': - this.emit('streamChannel#updated', message.params.stream, - message.params.channel, message.content); - break; - default: - OT.warn('OT.Raptor.dispatch: ' + message.signature + ' is not currently implemented'); - } - }; - // Dispatch JSEP messages - // - // generateoffer: - // Request to generate a offer for another Peer (or Prism). This kicks - // off the JSEP process. - // - // answer: - // generate a response to another peers offer, this contains our constraints - // and requirements. - // - // pranswer: - // a provisional answer, i.e. not the final one. - // - // candidate - // - // - OT.Raptor.Dispatcher.prototype.dispatchJsep = function (method, message) { - this.emit('jsep#' + method, message.params.stream, message.fromAddress, message); - }; +// TODO Remove all PeerConnection stuff, that belongs to the messaging layer not the Media layer. +// Indicates whether this client supports the PeerConnection +// API. +// +// Chrome Issues: +// * The explicit prototype.addStream check is because webkitRTCPeerConnection was +// partially implemented, but not functional, in Chrome 22. +// +// Firefox Issues: +// * No real support before Firefox 19 +// * Firefox 19 has issues with generating Offers. +// * Firefox 20 doesn't interoperate with Chrome. +// +OT.$.registerCapability('PeerConnection', function() { + if (OT.$.env === 'Node') { + return false; + } + else if (typeof(window.webkitRTCPeerConnection) === 'function' && + !!window.webkitRTCPeerConnection.prototype.addStream) { + return true; + } else if (typeof(window.mozRTCPeerConnection) === 'function' && OT.$.env.version > 20.0) { + return true; + } else { + return OTPlugin.isInstalled(); + } +}); - OT.Raptor.Dispatcher.prototype.dispatchSubscriberChannel = function (message) { - switch (message.method) { - case 'updated': - this.emit('subscriberChannel#updated', message.params.stream, - message.params.channel, message.content); - break; +// Indicates whether this client supports WebRTC +// +// This is defined as: getUserMedia + PeerConnection + exceeds min browser version +// +OT.$.registerCapability('webrtc', function() { + if (OT.properties) { + var minimumVersions = OT.properties.minimumVersion || {}, + minimumVersion = minimumVersions[OT.$.env.name.toLowerCase()]; + + if(minimumVersion && OT.$.env.versionGreaterThan(minimumVersion)) { + OT.debug('Support for', OT.$.env.name, 'is disabled because we require', + minimumVersion, 'but this is', OT.$.env.version); + return false; + } + } - case 'update': // subscriberId, streamId, content - this.emit('subscriberChannel#update', message.params.subscriber, - message.params.stream, message.content); - break; + if (OT.$.env === 'Node') { + // Node works, even though it doesn't have getUserMedia + return true; + } + return OT.$.hasCapabilities('getUserMedia', 'PeerConnection'); +}); - default: - OT.warn('OT.Raptor.dispatch: ' + message.signature + ' is not currently implemented'); - } - }; - OT.Raptor.Dispatcher.prototype.dispatchSubscriber = function (message) { - switch (message.method) { - case 'created': - this.emit('subscriber#created', message.params.stream, message.fromAddress, - message.content.id); - break; +// TODO Remove all transport stuff, that belongs to the messaging layer not the Media layer. +// Indicates if the browser supports bundle +// +// Broadly: +// * Firefox doesn't support bundle +// * Chrome support bundle +// * OT Plugin supports bundle +// * We assume NodeJs supports bundle (e.g. 'you're on your own' mode) +// +OT.$.registerCapability('bundle', function() { + return OT.$.hasCapabilities('webrtc') && + (OT.$.env.name === 'Chrome' || + OT.$.env.name === 'Node' || + OTPlugin.isInstalled()); +}); +// Indicates if the browser supports RTCP Mux +// +// Broadly: +// * Older versions of Firefox (<= 25) don't support RTCP Mux +// * Older versions of Firefox (>= 26) support RTCP Mux (not tested yet) +// * Chrome support RTCP Mux +// * OT Plugin supports RTCP Mux +// * We assume NodeJs supports RTCP Mux (e.g. 'you're on your own' mode) +// +OT.$.registerCapability('RTCPMux', function() { + return OT.$.hasCapabilities('webrtc') && + (OT.$.env.name === 'Chrome' || + OT.$.env.name === 'Node' || + OTPlugin.isInstalled()); +}); - case 'deleted': - this.dispatchJsep('unsubscribe', message); - this.emit('subscriber#deleted', message.params.stream, - message.fromAddress); - break; - // The JSEP process - case 'generateoffer': - case 'answer': - case 'pranswer': - case 'offer': - case 'candidate': - this.dispatchJsep(message.method, message); - break; +// Indicates whether this browser supports the getMediaDevices (getSources) API. +// +OT.$.registerCapability('getMediaDevices', function() { + return OT.$.isFunction(window.MediaStreamTrack) && + OT.$.isFunction(window.MediaStreamTrack.getSources); +}); + + +OT.$.registerCapability('audioOutputLevelStat', function() { + return OT.$.env.name === 'Chrome' || OT.$.env.name === 'IE'; +}); + +OT.$.registerCapability('webAudioCapableRemoteStream', function() { + return OT.$.env.name === 'Firefox'; +}); + +OT.$.registerCapability('webAudio', function() { + return 'AudioContext' in window; +}); - default: - OT.warn('OT.Raptor.dispatch: ' + message.signature + ' is not currently implemented'); - } - }; +// tb_require('../helpers.js') +// tb_require('./config.js') - OT.Raptor.Dispatcher.prototype.dispatchSignal = function (message) { - if (message.method !== 'signal') { - OT.warn('OT.Raptor.dispatch: ' + message.signature + ' is not currently implemented'); +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ + + +OT.Analytics = function(loggingUrl) { + + var LOG_VERSION = '1'; + var _analytics = new OT.$.Analytics(loggingUrl, OT.debug, OT._.getClientGuid); + + this.logError = function(code, type, message, details, options) { + if (!options) options = {}; + var partnerId = options.partnerId; + + if (OT.Config.get('exceptionLogging', 'enabled', partnerId) !== true) { return; } - this.emit('signal', message.fromAddress, message.content.type, - message.content.data); - }; - OT.Raptor.Dispatcher.prototype.dispatchArchive = function (message) { - switch (message.method) { - case 'created': - this.emit('archive#created', message.content); - break; - - case 'updated': - this.emit('archive#updated', message.params.archive, message.content); - break; - } + OT._.getClientGuid(function(error, guid) { + if (error) { + // @todo + return; + } + var data = OT.$.extend({ + // TODO: OT.properties.version only gives '2.2', not '2.2.9.3'. + 'clientVersion' : 'js-' + OT.properties.version.replace('v', ''), + 'guid' : guid, + 'partnerId' : partnerId, + 'source' : window.location.href, + 'logVersion' : LOG_VERSION, + 'clientSystemTime' : new Date().getTime() + }, options); + _analytics.logError(code, type, message, details, data); + }); + }; -}(this)); -(function(window) { + this.logEvent = function(options, throttle) { + var partnerId = options.partnerId; - // @todo hide these - OT.publishers = new OT.Collection('guid'); // Publishers are id'd by their guid - OT.subscribers = new OT.Collection('widgetId'); // Subscribers are id'd by their widgetId - OT.sessions = new OT.Collection(); + if (!options) options = {}; - function parseStream(dict, session) { - var channel = dict.channel.map(function(channel) { - return new OT.StreamChannel(channel); - }); + OT._.getClientGuid(function(error, guid) { + if (error) { + // @todo + return; + } - var connectionId = dict.connectionId ? dict.connectionId : dict.connection.id; + // Set a bunch of defaults + var data = OT.$.extend({ + // TODO: OT.properties.version only gives '2.2', not '2.2.9.3'. + 'clientVersion' : 'js-' + OT.properties.version.replace('v', ''), + 'guid' : guid, + 'partnerId' : partnerId, + 'source' : window.location.href, + 'logVersion' : LOG_VERSION, + 'clientSystemTime' : new Date().getTime() + }, options); + _analytics.logEvent(data, false, throttle); + }); + }; - return new OT.Stream( dict.id, - dict.name, - dict.creationTime, - session.connections.get(connectionId), - session, - channel ); - } + this.logQOS = function(options) { + var partnerId = options.partnerId; - function parseAndAddStreamToSession(dict, session) { - if (session.streams.has(dict.id)) return; + if (!options) options = {}; - var stream = parseStream(dict, session); - session.streams.add( stream ); + OT._.getClientGuid(function(error, guid) { + if (error) { + // @todo + return; + } - return stream; - } + // Set a bunch of defaults + var data = OT.$.extend({ + // TODO: OT.properties.version only gives '2.2', not '2.2.9.3'. + 'clientVersion' : 'js-' + OT.properties.version.replace('v', ''), + 'guid' : guid, + 'partnerId' : partnerId, + 'source' : window.location.href, + 'logVersion' : LOG_VERSION, + 'clientSystemTime' : new Date().getTime(), + 'duration' : 0 //in milliseconds + }, options); - function parseArchive(dict) { - return new OT.Archive( dict.id, - dict.name, - dict.status ); - } + _analytics.logQOS(data); + }); + }; +}; - function parseAndAddArchiveToSession(dict, session) { - if (session.archives.has(dict.id)) return; +// tb_require('../helpers.js') +// tb_require('./config.js') +// tb_require('./analytics.js') - var archive = parseArchive(dict); - session.archives.add(archive); +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - return archive; - } +OT.ConnectivityAttemptPinger = function (options) { + var _state = 'Initial', + _previousState, + states = ['Initial', 'Attempt', 'Success', 'Failure'], + pingTimer, // Timer for the Attempting pings + PING_INTERVAL = 5000, + PING_COUNT_TOTAL = 6, + pingCount; + + //// Private API + var stateChanged = function(newState) { + _state = newState; + var invalidSequence = false; + switch (_state) { + case 'Attempt': + if (_previousState !== 'Initial') { + invalidSequence = true; + } + startAttemptPings(); + break; + case 'Success': + if (_previousState !== 'Attempt') { + invalidSequence = true; + } + stopAttemptPings(); + break; + case 'Failure': + if (_previousState !== 'Attempt') { + invalidSequence = true; + } + stopAttemptPings(); + break; + } + if (invalidSequence) { + var data = options ? OT.$.clone(options) : {}; + data.action = 'Internal Error'; + data.variation = 'Non-fatal'; + data.payload = { + debug: 'Invalid sequence: ' + options.action + ' ' + + _previousState + ' -> ' + _state + }; + OT.analytics.logEvent(data); + } + }, - var sessionRead; - var sessionReadQueue = []; + setState = OT.$.statable(this, states, 'Failure', stateChanged), - function sessionReadQueuePush(type, args) { - var triggerArgs = ['signal']; - triggerArgs.push.apply(triggerArgs, args); - sessionReadQueue.push(triggerArgs); - } + startAttemptPings = function() { + pingCount = 0; + pingTimer = setInterval(function() { + if (pingCount < PING_COUNT_TOTAL) { + var data = OT.$.extend(options, {variation: 'Attempting'}); + OT.analytics.logEvent(data); + } else { + stopAttemptPings(); + } + pingCount++; + }, PING_INTERVAL); + }, - window.OT.SessionDispatcher = function(session) { + stopAttemptPings = function() { + clearInterval(pingTimer); + }; - var dispatcher = new OT.Raptor.Dispatcher(); + this.setVariation = function(variation) { + _previousState = _state; + setState(variation); - dispatcher.on('close', function(reason) { + // We could change the ConnectivityAttemptPinger to a ConnectivityAttemptLogger + // that also logs events in addition to logging the ping ('Attempting') events. + // + // var payload = OT.$.extend(options, {variation:variation}); + // OT.analytics.logEvent(payload); + }; - var connection = session.connection; + this.stop = function() { + stopAttemptPings(); + }; +}; - if (!connection) { - return; - } +// tb_require('../helpers/helpers.js') - if (connection.destroyedReason()) { - OT.debug('OT.Raptor.Socket: Socket was closed but the connection had already ' + - 'been destroyed. Reason: ' + connection.destroyedReason()); - return; - } +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - connection.destroy( reason ); +/* Stylable Notes + * Some bits are controlled by multiple flags, i.e. buttonDisplayMode and nameDisplayMode. + * When there are multiple flags how is the final setting chosen? + * When some style bits are set updates will need to be pushed through to the Chrome + */ - }); +// Mixes the StylableComponent behaviour into the +self+ object. It will +// also set the default styles to +initialStyles+. +// +// @note This Mixin is dependent on OT.Eventing. +// +// +// @example +// +// function SomeObject { +// OT.StylableComponent(this, { +// name: 'SomeObject', +// foo: 'bar' +// }); +// } +// +// var obj = new SomeObject(); +// obj.getStyle('foo'); // => 'bar' +// obj.setStyle('foo', 'baz') +// obj.getStyle('foo'); // => 'baz' +// obj.getStyle(); // => {name: 'SomeObject', foo: 'baz'} +// +OT.StylableComponent = function(self, initalStyles, showControls, logSetStyleWithPayload) { + if (!self.trigger) { + throw new Error('OT.StylableComponent is dependent on the mixin OT.$.eventing. ' + + 'Ensure that this is included in the object before StylableComponent.'); + } - dispatcher.on('session#read', function(content, transactionId) { + var _readOnly = false; + + // Broadcast style changes as the styleValueChanged event + var onStyleChange = function(key, value, oldValue) { + if (oldValue) { + self.trigger('styleValueChanged', key, value, oldValue); + } else { + self.trigger('styleValueChanged', key, value); + } + }; - var state = {}, - connection; + if(showControls === false) { + initalStyles = { + buttonDisplayMode: 'off', + nameDisplayMode: 'off', + audioLevelDisplayMode: 'off' + }; - state.streams = []; - state.connections = []; - state.archives = []; + _readOnly = true; + logSetStyleWithPayload({ + showControls: false + }); + } - OT.$.forEach(content.connection, function(connectionParams) { - connection = OT.Connection.fromHash(connectionParams); - state.connections.push(connection); - session.connections.add(connection); - }); + var _style = new Style(initalStyles, onStyleChange); - OT.$.forEach(content.stream, function(streamParams) { - state.streams.push( parseAndAddStreamToSession(streamParams, session) ); - }); +/** + * Returns an object that has the properties that define the current user interface controls of + * the Publisher. You can modify the properties of this object and pass the object to the + * setStyle() method of thePublisher object. (See the documentation for + * setStyle() to see the styles that define this object.) + * @return {Object} The object that defines the styles of the Publisher. + * @see setStyle() + * @method #getStyle + * @memberOf Publisher + */ - OT.$.forEach(content.archive || content.archives, function(archiveParams) { - state.archives.push( parseAndAddArchiveToSession(archiveParams, session) ); - }); +/** + * Returns an object that has the properties that define the current user interface controls of + * the Subscriber. You can modify the properties of this object and pass the object to the + * setStyle() method of the Subscriber object. (See the documentation for + * setStyle() to see the styles that define this object.) + * @return {Object} The object that defines the styles of the Subscriber. + * @see setStyle() + * @method #getStyle + * @memberOf Subscriber + */ + // If +key+ is falsly then all styles will be returned. + self.getStyle = function(key) { + return _style.get(key); + }; - session._.subscriberMap = {}; +/** + * Sets properties that define the appearance of some user interface controls of the Publisher. + * + *

    You can either pass one parameter or two parameters to this method.

    + * + *

    If you pass one parameter, style, it is an object that has the following + * properties: + * + *

      + *
    • audioLevelDisplayMode (String) — How to display the audio level + * indicator. Possible values are: "auto" (the indicator is displayed when the + * video is disabled), "off" (the indicator is not displayed), and + * "on" (the indicator is always displayed).
    • + * + *
    • backgroundImageURI (String) — A URI for an image to display as + * the background image when a video is not displayed. (A video may not be displayed if + * you call publishVideo(false) on the Publisher object). You can pass an http + * or https URI to a PNG, JPEG, or non-animated GIF file location. You can also use the + * data URI scheme (instead of http or https) and pass in base-64-encrypted + * PNG data, such as that obtained from the + * Publisher.getImgData() method. For example, + * you could set the property to "data:VBORw0KGgoAA...", where the portion of + * the string after "data:" is the result of a call to + * Publisher.getImgData(). If the URL or the image data is invalid, the + * property is ignored (the attempt to set the image fails silently). + *

      + * Note that in Internet Explorer 8 (using the OpenTok Plugin for Internet Explorer), + * you cannot set the backgroundImageURI style to a string larger than + * 32 kB. This is due to an IE 8 limitation on the size of URI strings. Due to this + * limitation, you cannot set the backgroundImageURI style to a string obtained + * with the getImgData() method. + *

    • + * + *
    • buttonDisplayMode (String) — How to display the microphone + * controls. Possible values are: "auto" (controls are displayed when the + * stream is first displayed and when the user mouses over the display), "off" + * (controls are not displayed), and "on" (controls are always displayed).
    • + * + *
    • nameDisplayMode (String) — Whether to display the stream name. + * Possible values are: "auto" (the name is displayed when the stream is first + * displayed and when the user mouses over the display), "off" (the name is not + * displayed), and "on" (the name is always displayed).
    • + *
    + *

    + * + *

    For example, the following code passes one parameter to the method:

    + * + *
    myPublisher.setStyle({nameDisplayMode: "off"});
    + * + *

    If you pass two parameters, style and value, they are + * key-value pair that define one property of the display style. For example, the following + * code passes two parameter values to the method:

    + * + *
    myPublisher.setStyle("nameDisplayMode", "off");
    + * + *

    You can set the initial settings when you call the Session.publish() + * or OT.initPublisher() method. Pass a style property as part of the + * properties parameter of the method.

    + * + *

    The OT object dispatches an exception event if you pass in an invalid style + * to the method. The code property of the ExceptionEvent object is set to 1011.

    + * + * @param {Object} style Either an object containing properties that define the style, or a + * String defining this single style property to set. + * @param {String} value The value to set for the style passed in. Pass a value + * for this parameter only if the value of the style parameter is a String.

    + * + * @see getStyle() + * @return {Publisher} The Publisher object + * @see setStyle() + * + * @see Session.publish() + * @see OT.initPublisher() + * @method #setStyle + * @memberOf Publisher + */ - dispatcher.triggerCallback(transactionId, null, state); +/** + * Sets properties that define the appearance of some user interface controls of the Subscriber. + * + *

    You can either pass one parameter or two parameters to this method.

    + * + *

    If you pass one parameter, style, it is an object that has the following + * properties: + * + *

      + *
    • audioLevelDisplayMode (String) — How to display the audio level + * indicator. Possible values are: "auto" (the indicator is displayed when the + * video is disabled), "off" (the indicator is not displayed), and + * "on" (the indicator is always displayed).
    • + * + *
    • backgroundImageURI (String) — A URI for an image to display as + * the background image when a video is not displayed. (A video may not be displayed if + * you call subscribeToVideo(false) on the Publisher object). You can pass an + * http or https URI to a PNG, JPEG, or non-animated GIF file location. You can also use the + * data URI scheme (instead of http or https) and pass in base-64-encrypted + * PNG data, such as that obtained from the + * Subscriber.getImgData() method. For example, + * you could set the property to "data:VBORw0KGgoAA...", where the portion of + * the string after "data:" is the result of a call to + * Publisher.getImgData(). If the URL or the image data is invalid, the + * property is ignored (the attempt to set the image fails silently). + *

      + * Note that in Internet Explorer 8 (using the OpenTok Plugin for Internet Explorer), + * you cannot set the backgroundImageURI style to a string larger than + * 32 kB. This is due to an IE 8 limitation on the size of URI strings. Due to this + * limitation, you cannot set the backgroundImageURI style to a string obtained + * with the getImgData() method. + *

    • + * + *
    • buttonDisplayMode (String) — How to display the speaker + * controls. Possible values are: "auto" (controls are displayed when the + * stream is first displayed and when the user mouses over the display), "off" + * (controls are not displayed), and "on" (controls are always displayed).
    • + * + *
    • nameDisplayMode (String) — Whether to display the stream name. + * Possible values are: "auto" (the name is displayed when the stream is first + * displayed and when the user mouses over the display), "off" (the name is not + * displayed), and "on" (the name is always displayed).
    • + * + *
    • videoDisabledDisplayMode (String) — Whether to display the video + * disabled indicator and video disabled warning icons for a Subscriber. These icons + * indicate that the video has been disabled (or is in risk of being disabled for + * the warning icon) due to poor stream quality. Possible values are: "auto" + * (the icons are automatically when the displayed video is disabled or in risk of being + * disabled due to poor stream quality), "off" (do not display the icons), and + * "on" (display the icons).
    • + *
    + *

    + * + *

    For example, the following code passes one parameter to the method:

    + * + *
    mySubscriber.setStyle({nameDisplayMode: "off"});
    + * + *

    If you pass two parameters, style and value, they are key-value + * pair that define one property of the display style. For example, the following code passes + * two parameter values to the method:

    + * + *
    mySubscriber.setStyle("nameDisplayMode", "off");
    + * + *

    You can set the initial settings when you call the Session.subscribe() method. + * Pass a style property as part of the properties parameter of the + * method.

    + * + *

    The OT object dispatches an exception event if you pass in an invalid style + * to the method. The code property of the ExceptionEvent object is set to 1011.

    + * + * @param {Object} style Either an object containing properties that define the style, or a + * String defining this single style property to set. + * @param {String} value The value to set for the style passed in. Pass a value + * for this parameter only if the value of the style parameter is a String.

    + * + * @returns {Subscriber} The Subscriber object. + * + * @see getStyle() + * @see setStyle() + * + * @see Session.subscribe() + * @method #setStyle + * @memberOf Subscriber + */ - sessionRead = true; - for (var i = 0; i < sessionReadQueue.length; ++i) { - dispatcher.trigger.apply(dispatcher, sessionReadQueue[i]); + if(_readOnly) { + self.setStyle = function() { + OT.warn('Calling setStyle() has no effect because the' + + 'showControls option was set to false'); + return this; + }; + } else { + self.setStyle = function(keyOrStyleHash, value, silent) { + var logPayload = {}; + if (typeof(keyOrStyleHash) !== 'string') { + _style.setAll(keyOrStyleHash, silent); + logPayload = keyOrStyleHash; + } else { + _style.set(keyOrStyleHash, value); + logPayload[keyOrStyleHash] = value; } - sessionReadQueue = []; + if (logSetStyleWithPayload) logSetStyleWithPayload(logPayload); + return this; + }; + } +}; - }); - dispatcher.on('connection#created', function(connection) { - connection = OT.Connection.fromHash(connection); - if (session.connection && connection.id !== session.connection.id) { - session.connections.add( connection ); - } - }); +/*jshint latedef:false */ +var Style = function(initalStyles, onStyleChange) { +/*jshint latedef:true */ + var _style = {}, + _COMPONENT_STYLES, + _validStyleValues, + isValidStyle, + castValue; + + + _COMPONENT_STYLES = [ + 'showMicButton', + 'showSpeakerButton', + 'nameDisplayMode', + 'buttonDisplayMode', + 'backgroundImageURI', + 'audioLevelDisplayMode' + ]; - dispatcher.on('connection#deleted', function(connection, reason) { - connection = session.connections.get(connection); - connection.destroy(reason); - }); + _validStyleValues = { + buttonDisplayMode: ['auto', 'mini', 'mini-auto', 'off', 'on'], + nameDisplayMode: ['auto', 'off', 'on'], + audioLevelDisplayMode: ['auto', 'off', 'on'], + showSettingsButton: [true, false], + showMicButton: [true, false], + backgroundImageURI: null, + showControlBar: [true, false], + showArchiveStatus: [true, false], + videoDisabledDisplayMode: ['auto', 'off', 'on'] + }; + + // Validates the style +key+ and also whether +value+ is valid for +key+ + isValidStyle = function(key, value) { + return key === 'backgroundImageURI' || + (_validStyleValues.hasOwnProperty(key) && + OT.$.arrayIndexOf(_validStyleValues[key], value) !== -1 ); + }; + + castValue = function(value) { + switch(value) { + case 'true': + return true; + case 'false': + return false; + default: + return value; + } + }; - dispatcher.on('stream#created', function(stream, transactionId) { - stream = parseAndAddStreamToSession(stream, session); + // Returns a shallow copy of the styles. + this.getAll = function() { + var style = OT.$.clone(_style); - if (stream.publisher) { - stream.publisher.setStream(stream); + for (var key in style) { + if(!style.hasOwnProperty(key)) { + continue; } + if (OT.$.arrayIndexOf(_COMPONENT_STYLES, key) < 0) { - dispatcher.triggerCallback(transactionId, null, stream); - }); + // Strip unnecessary properties out, should this happen on Set? + delete style[key]; + } + } - dispatcher.on('stream#deleted', function(streamId, reason) { - var stream = session.streams.get(streamId); + return style; + }; - if (!stream) { - OT.error('OT.Raptor.dispatch: A stream does not exist with the id of ' + - streamId + ', for stream#deleted message!'); - // @todo error - return; - } + this.get = function(key) { + if (key) { + return _style[key]; + } - stream.destroy(reason); - }); + // We haven't been asked for any specific key, just return the lot + return this.getAll(); + }; - dispatcher.on('stream#updated', function(streamId, content) { - var stream = session.streams.get(streamId); + // *note:* this will not trigger onStyleChange if +silent+ is truthy + this.setAll = function(newStyles, silent) { + var oldValue, newValue; - if (!stream) { - OT.error('OT.Raptor.dispatch: A stream does not exist with the id of ' + - streamId + ', for stream#updated message!'); - // @todo error - return; + for (var key in newStyles) { + if(!newStyles.hasOwnProperty(key)) { + continue; } + newValue = castValue(newStyles[key]); - stream._.update(content); + if (isValidStyle(key, newValue)) { + oldValue = _style[key]; - }); + if (newValue !== oldValue) { + _style[key] = newValue; + if (!silent) onStyleChange(key, newValue, oldValue); + } - dispatcher.on('streamChannel#updated', function(streamId, channelId, content) { - var stream; - if (!(streamId && (stream = session.streams.get(streamId)))) { - OT.error('OT.Raptor.dispatch: Unable to determine streamId, or the stream does not ' + - 'exist, for streamChannel message!'); - // @todo error - return; + } else { + OT.warn('Style.setAll::Invalid style property passed ' + key + ' : ' + newValue); } - stream._.updateChannel(channelId, content); - }); - - // Dispatch JSEP messages - // - // generateoffer: - // Request to generate a offer for another Peer (or Prism). This kicks - // off the JSEP process. - // - // answer: - // generate a response to another peers offer, this contains our constraints - // and requirements. - // - // pranswer: - // a provisional answer, i.e. not the final one. - // - // candidate - // - // - var jsepHandler = function(method, streamId, fromAddress, message) { + } - var fromConnection, - actors; + return this; + }; - switch (method) { - // Messages for Subscribers - case 'offer': - actors = []; - var subscriber = OT.subscribers.find({streamId: streamId}); - if (subscriber) actors.push(subscriber); - break; + this.set = function(key, value) { + OT.debug('setStyle: ' + key.toString()); + var newValue = castValue(value), + oldValue; - // Messages for Publishers - case 'answer': - case 'pranswer': - case 'generateoffer': - case 'unsubscribe': - actors = OT.publishers.where({streamId: streamId}); - break; + if (!isValidStyle(key, newValue)) { + OT.warn('Style.set::Invalid style property passed ' + key + ' : ' + newValue); + return this; + } + oldValue = _style[key]; + if (newValue !== oldValue) { + _style[key] = newValue; - // Messages for Publishers and Subscribers - case 'candidate': - // send to whichever of your publisher or subscribers are - // subscribing/publishing that stream - actors = OT.publishers.where({streamId: streamId}) - .concat(OT.subscribers.where({streamId: streamId})); - break; + onStyleChange(key, value, oldValue); + } + return this; + }; - default: - OT.warn('OT.Raptor.dispatch: jsep#' + method + - ' is not currently implemented'); - return; - } + if (initalStyles) this.setAll(initalStyles, true); +}; - if (actors.length === 0) return; +// tb_require('../helpers/helpers.js') - // This is a bit hacky. We don't have the session in the message so we iterate - // until we find the actor that the message relates to this stream, and then - // we grab the session from it. - fromConnection = actors[0].session.connections.get(fromAddress); - if(!fromConnection && fromAddress.match(/^symphony\./)) { - fromConnection = OT.Connection.fromHash({ - id: fromAddress, - creationTime: Math.floor(OT.$.now()) - }); +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - actors[0].session.connections.add(fromConnection); - } else if(!fromConnection) { - OT.warn('OT.Raptor.dispatch: Messsage comes from a connection (' + - fromAddress + ') that we do not know about. The message was ignored.'); - return; - } - OT.$.forEach(actors, function(actor) { - actor.processMessage(method, fromConnection, message); - }); - }; +// A Factory method for generating simple state machine classes. +// +// @usage +// var StateMachine = OT.generateSimpleStateMachine('start', ['start', 'middle', 'end', { +// start: ['middle'], +// middle: ['end'], +// end: ['start'] +// }]); +// +// var states = new StateMachine(); +// state.current; // <-- start +// state.set('middle'); +// +OT.generateSimpleStateMachine = function(initialState, states, transitions) { + var validStates = states.slice(), + validTransitions = OT.$.clone(transitions); - dispatcher.on('jsep#offer', OT.$.bind(jsepHandler, null, 'offer')); - dispatcher.on('jsep#answer', OT.$.bind(jsepHandler, null, 'answer')); - dispatcher.on('jsep#pranswer', OT.$.bind(jsepHandler, null, 'pranswer')); - dispatcher.on('jsep#generateoffer', OT.$.bind(jsepHandler, null, 'generateoffer')); - dispatcher.on('jsep#unsubscribe', OT.$.bind(jsepHandler, null, 'unsubscribe')); - dispatcher.on('jsep#candidate', OT.$.bind(jsepHandler, null, 'candidate')); + var isValidState = function (state) { + return OT.$.arrayIndexOf(validStates, state) !== -1; + }; - dispatcher.on('subscriberChannel#updated', function(streamId, channelId, content) { + var isValidTransition = function(fromState, toState) { + return validTransitions[fromState] && + OT.$.arrayIndexOf(validTransitions[fromState], toState) !== -1; + }; - if (!streamId || !session.streams.has(streamId)) { - OT.error('OT.Raptor.dispatch: Unable to determine streamId, or the stream does not ' + - 'exist, for subscriberChannel#updated message!'); - // @todo error - return; - } + return function(stateChangeFailed) { + var currentState = initialState, + previousState = null; - session.streams.get(streamId)._ - .updateChannel(channelId, content); + this.current = currentState; - }); + function signalChangeFailed(message, newState) { + stateChangeFailed({ + message: message, + newState: newState, + currentState: currentState, + previousState: previousState + }); + } - dispatcher.on('subscriberChannel#update', function(subscriberId, streamId, content) { + // Validates +newState+. If it's invalid it triggers stateChangeFailed and returns false. + function handleInvalidStateChanges(newState) { + if (!isValidState(newState)) { + signalChangeFailed('\'' + newState + '\' is not a valid state', newState); - if (!streamId || !session.streams.has(streamId)) { - OT.error('OT.Raptor.dispatch: Unable to determine streamId, or the stream does not ' + - 'exist, for subscriberChannel#update message!'); - // @todo error - return; + return false; } - // Hint to update for congestion control from the Media Server - if (!OT.subscribers.has(subscriberId)) { - OT.error('OT.Raptor.dispatch: Unable to determine subscriberId, or the subscriber ' + - 'does not exist, for subscriberChannel#update message!'); - // @todo error - return; - } + if (!isValidTransition(currentState, newState)) { + signalChangeFailed('\'' + currentState + '\' cannot transition to \'' + + newState + '\'', newState); - // We assume that an update on a Subscriber channel is to disableVideo - // we may need to be more specific in the future - OT.subscribers.get(subscriberId).disableVideo(content.active); + return false; + } - }); + return true; + } - dispatcher.on('subscriber#created', function(streamId, fromAddress, subscriberId) { - var stream = streamId ? session.streams.get(streamId) : null; + this.set = function(newState) { + if (!handleInvalidStateChanges(newState)) return; + previousState = currentState; + this.current = currentState = newState; + }; - if (!stream) { - OT.error('OT.Raptor.dispatch: Unable to determine streamId, or the stream does ' + - 'not exist, for subscriber#created message!'); - // @todo error - return; - } + }; +}; - session._.subscriberMap[fromAddress + '_' + stream.id] = subscriberId; - }); +// tb_require('../helpers/helpers.js') +// tb_require('./state_machine.js') - dispatcher.on('subscriber#deleted', function(streamId, fromAddress) { - var stream = streamId ? session.streams.get(streamId) : null; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - if (!stream) { - OT.error('OT.Raptor.dispatch: Unable to determine streamId, or the stream does ' + - 'not exist, for subscriber#created message!'); - // @todo error - return; - } +!(function() { - delete session._.subscriberMap[fromAddress + '_' + stream.id]; - }); +// Models a Subscriber's subscribing State +// +// Valid States: +// NotSubscribing (the initial state +// Init (basic setup of DOM +// ConnectingToPeer (Failure Cases -> No Route, Bad Offer, Bad Answer +// BindingRemoteStream (Failure Cases -> Anything to do with the media being +// (invalid, the media never plays +// Subscribing (this is 'onLoad' +// Failed (terminal state, with a reason that maps to one of the +// (failure cases above +// Destroyed (The subscriber has been cleaned up, terminal state +// +// +// Valid Transitions: +// NotSubscribing -> +// Init +// +// Init -> +// ConnectingToPeer +// | BindingRemoteStream (if we are subscribing to ourselves and we alreay +// (have a stream +// | NotSubscribing (destroy() +// +// ConnectingToPeer -> +// BindingRemoteStream +// | NotSubscribing +// | Failed +// | NotSubscribing (destroy() +// +// BindingRemoteStream -> +// Subscribing +// | Failed +// | NotSubscribing (destroy() +// +// Subscribing -> +// NotSubscribing (unsubscribe +// | Failed (probably a peer connection failure after we began +// (subscribing +// +// Failed -> +// Destroyed +// +// Destroyed -> (terminal state) +// +// +// @example +// var state = new SubscribingState(function(change) { +// console.log(change.message); +// }); +// +// state.set('Init'); +// state.current; -> 'Init' +// +// state.set('Subscribing'); -> triggers stateChangeFailed and logs out the error message +// +// + var validStates, + validTransitions, + initialState = 'NotSubscribing'; - dispatcher.on('signal', function(fromAddress, signalType, data) { - if (sessionRead) { - var fromConnection = session.connections.get(fromAddress); - session._.dispatchSignal(fromConnection, signalType, data); - } else { - if (!sessionRead) { - sessionReadQueuePush('signal', arguments); - } - } - }); + validStates = [ + 'NotSubscribing', 'Init', 'ConnectingToPeer', + 'BindingRemoteStream', 'Subscribing', 'Failed', + 'Destroyed' + ]; - dispatcher.on('archive#created', function(archive) { - parseAndAddArchiveToSession(archive, session); - }); + validTransitions = { + NotSubscribing: ['NotSubscribing', 'Init', 'Destroyed'], + Init: ['NotSubscribing', 'ConnectingToPeer', 'BindingRemoteStream', 'Destroyed'], + ConnectingToPeer: ['NotSubscribing', 'BindingRemoteStream', 'Failed', 'Destroyed'], + BindingRemoteStream: ['NotSubscribing', 'Subscribing', 'Failed', 'Destroyed'], + Subscribing: ['NotSubscribing', 'Failed', 'Destroyed'], + Failed: ['Destroyed'], + Destroyed: [] + }; - dispatcher.on('archive#updated', function(archiveId, update) { - var archive = session.archives.get(archiveId); + OT.SubscribingState = OT.generateSimpleStateMachine(initialState, validStates, validTransitions); - if (!archive) { - OT.error('OT.Raptor.dispatch: An archive does not exist with the id of ' + - archiveId + ', for archive#updated message!'); - // @todo error - return; - } + OT.SubscribingState.prototype.isDestroyed = function() { + return this.current === 'Destroyed'; + }; - archive._.update(update); - }); + OT.SubscribingState.prototype.isFailed = function() { + return this.current === 'Failed'; + }; - return dispatcher; + OT.SubscribingState.prototype.isSubscribing = function() { + return this.current === 'Subscribing'; + }; + OT.SubscribingState.prototype.isAttemptingToSubscribe = function() { + return OT.$.arrayIndexOf( + [ 'Init', 'ConnectingToPeer', 'BindingRemoteStream' ], + this.current + ) !== -1; }; })(window); -!(function() { - - // Helper to synchronise several startup tasks and then dispatch a unified - // 'envLoaded' event. - // - // This depends on: - // * OT - // * OT.Config - // - function EnvironmentLoader() { - var _configReady = false, - - // If the plugin is installed, then we should wait for it to - // be ready as well. - _pluginSupported = TBPlugin.isSupported(), - _pluginLoadAttemptComplete = _pluginSupported ? TBPlugin.isReady() : true, - - isReady = function() { - return !OT.$.isDOMUnloaded() && OT.$.isReady() && - _configReady && _pluginLoadAttemptComplete; - }, - onLoaded = function() { - if (isReady()) { - OT.dispatchEvent(new OT.EnvLoadedEvent(OT.Event.names.ENV_LOADED)); - } - }, +// tb_require('../helpers/helpers.js') +// tb_require('./state_machine.js') +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - onDomReady = function() { - OT.$.onDOMUnload(onDomUnload); +!(function() { - // The Dynamic Config won't load until the DOM is ready - OT.Config.load(OT.properties.configURL); +// Models a Publisher's publishing State +// +// Valid States: +// NotPublishing +// GetUserMedia +// BindingMedia +// MediaBound +// PublishingToSession +// Publishing +// Failed +// Destroyed +// +// +// Valid Transitions: +// NotPublishing -> +// GetUserMedia +// +// GetUserMedia -> +// BindingMedia +// | Failed (Failure Reasons -> stream error, constraints, +// (permission denied +// | NotPublishing (destroy() +// +// +// BindingMedia -> +// MediaBound +// | Failed (Failure Reasons -> Anything to do with the media +// (being invalid, the media never plays +// | NotPublishing (destroy() +// +// MediaBound -> +// PublishingToSession (MediaBound could transition to PublishingToSession +// (if a stand-alone publish is bound to a session +// | Failed (Failure Reasons -> media issues with a stand-alone publisher +// | NotPublishing (destroy() +// +// PublishingToSession +// Publishing +// | Failed (Failure Reasons -> timeout while waiting for ack of +// (stream registered. We do not do this right now +// | NotPublishing (destroy() +// +// +// Publishing -> +// NotPublishing (Unpublish +// | Failed (Failure Reasons -> loss of network, media error, anything +// (that causes *all* Peer Connections to fail (less than all +// (failing is just an error, all is failure) +// | NotPublishing (destroy() +// +// Failed -> +// Destroyed +// +// Destroyed -> (Terminal state +// +// - onLoaded(); - }, + var validStates = [ + 'NotPublishing', 'GetUserMedia', 'BindingMedia', 'MediaBound', + 'PublishingToSession', 'Publishing', 'Failed', + 'Destroyed' + ], - onDomUnload = function() { - // Disconnect the session first, this will prevent the plugin - // from locking up during browser unload. - // if (_pluginSupported) { - // var sessions = OT.sessions.where(); - // for (var i=0; icompletionHandler parameter: - * - * - * - *

    - * The completionHandler parameter is a function that is called when the call to - * the asynchronous method succeeds or fails. If the asynchronous call fails, the completion - * handler function is passes an error object (defined by the Error class). The code - * and message properties of the error object provide details about the error. - * - * @property {Number} code The error code, defining the error. - * - *

    - * In the event of an error, the code value of the error parameter can - * have one of the following values: - *

    - * - *

    Errors when calling Session.connect():

    - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
    codeDescription
    1004Authentication error. Check the error message for details. This error can result if you - * in an expired token when trying to connect to a session. It can also occur if you pass - * in an invalid token or API key. Make sure that you are generating the token using the - * current version of one of the - * OpenTok server SDKs.
    1005Invalid Session ID. Make sure you generate the session ID using the current version of - * one of the OpenTok server - * SDKs.
    1006Connect Failed. Unable to connect to the session. You may want to have the client check - * the network connection.
    - * - *

    Errors when calling Session.forceDisconnect():

    - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
    - * code - * Description
    1010The client is not connected to the OpenTok session. Check that client connects - * successfully and has not disconnected before calling forceDisconnect().
    1520Unable to force disconnect. The client's token does not have the role set to moderator. - * Once the client has connected to the session, the capabilities property of - * the Session object lists the client's capabilities.
    - * - *

    Errors when calling Session.forceUnpublish():

    - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
    codeDescription
    1010The client is not connected to the OpenTok session. Check that client connects - * successfully and has not disconnected before calling forceUnpublish().
    1530Unable to force unpublish. The client's token does not have the role set to moderator. - * Once the client has connected to the session, the capabilities property of - * the Session object lists the client's capabilities.
    1535Force Unpublish on an invalid stream. Make sure that the stream has not left the - * session before you call the forceUnpublish() method.
    - * - *

    Errors when calling Session.publish():

    - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
    codeDescription
    1010The client is not connected to the OpenTok session. Check that the client connects - * successfully before trying to publish. And check that the client has not disconnected - * before trying to publish.
    1500Unable to Publish. The client's token does not have the role set to to publish or - * moderator. Once the client has connected to the session, the capabilities - * property of the Session object lists the client's capabilities.
    1601Internal error -- WebRTC publisher error. Try republishing or reconnecting to the - * session.
    - * - *

    Errors when calling Session.signal():

    - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
    codeDescription
    400One of the signal properties — data, type, or to — - * is invalid. Or the data cannot be parsed as JSON.
    404 The to connection does not exist.
    413 The type string exceeds the maximum length (128 bytes), - * or the data string exceeds the maximum size (8 kB).
    500The client is not connected to the OpenTok session. Check that the client connects - * successfully before trying to signal. And check that the client has not disconnected before - * trying to publish.
    - * - *

    Errors when calling Session.subscribe():

    - * - * - * - * - * - * - * - * - * - * - * - * - * - *
    - * Errors when calling Session.subscribe(): - *
    - * code - * Description
    1600Internal error -- WebRTC subscriber error. Try resubscribing to the stream or - * reconnecting to the session.
    - * - *

    Errors when calling TB.initPublisher():

    - * - * - * - * - * - * - * - * - * - * - *
    codeDescription
    1004Authentication error. Check the error message for details. This error can result if you - * pass in an expired token when trying to connect to a session. It can also occur if you - * pass in an invalid token or API key. Make sure that you are generating the token using - * the current version of one of the - * OpenTok server SDKs.
    - * - *

    General errors that can occur when calling any method:

    - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
    codeDescription
    1011Invalid Parameter. Check that you have passed valid parameter values into the method - * call.
    2000Internal Error. Try reconnecting to the OpenTok session and trying the action again.
    - * - * @property {String} message The message string provides details about the error. - * - * @class Error - * @augments Event - */ - OT.Error = function(code, message) { - this.code = code; - this.message = message; - }; +// tb_require('../helpers/helpers.js') - var errorsCodesToTitle = { - 1004: 'Authentication error', - 1005: 'Invalid Session ID', - 1006: 'Connect Failed', - 1007: 'Connect Rejected', - 1008: 'Connect Time-out', - 1009: 'Security Error', - 1010: 'Not Connected', - 1011: 'Invalid Parameter', - 1012: 'Peer-to-peer Stream Play Failed', - 1013: 'Connection Failed', - 1014: 'API Response Failure', - 1500: 'Unable to Publish', - 1520: 'Unable to Force Disconnect', - 1530: 'Unable to Force Unpublish', - 2000: 'Internal Error', - 2001: 'Embed Failed', - 4000: 'WebSocket Connection Failed', - 4001: 'WebSocket Network Disconnected' +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ + +/* + * Executes the provided callback thanks to window.setInterval. + * + * @param {function()} callback + * @param {number} frequency how many times per second we want to execute the callback + * @constructor + */ +OT.IntervalRunner = function(callback, frequency) { + var _callback = callback, + _frequency = frequency, + _intervalId = null; + + this.start = function() { + _intervalId = window.setInterval(_callback, 1000 / _frequency); }; - var analytics; + this.stop = function() { + window.clearInterval(_intervalId); + _intervalId = null; + }; +}; - function _exceptionHandler(component, msg, errorCode, context) { - var title = errorsCodesToTitle[errorCode], - contextCopy = context ? OT.$.clone(context) : {}; +// tb_require('../helpers/helpers.js') - OT.error('OT.exception :: title: ' + title + ' (' + errorCode + ') msg: ' + msg); +!(function() { + /* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ + /* global OT */ - if (!contextCopy.partnerId) contextCopy.partnerId = OT.APIKEY; + /** + * The Event object defines the basic OpenTok event object that is passed to + * event listeners. Other OpenTok event classes implement the properties and methods of + * the Event object.

    + * + *

    For example, the Stream object dispatches a streamPropertyChanged event when + * the stream's properties are updated. You add a callback for an event using the + * on() method of the Stream object:

    + * + *
    +   * stream.on("streamPropertyChanged", function (event) {
    +   *     alert("Properties changed for stream " + event.target.streamId);
    +   * });
    + * + * @class Event + * @property {Boolean} cancelable Whether the event has a default behavior that is cancelable + * (true) or not (false). You can cancel the default behavior by + * calling the preventDefault() method of the Event object in the callback + * function. (See preventDefault().) + * + * @property {Object} target The object that dispatched the event. + * + * @property {String} type The type of event. + */ + OT.Event = OT.$.eventing.Event(); + /** + * Prevents the default behavior associated with the event from taking place. + * + *

    To see whether an event has a default behavior, check the cancelable property + * of the event object.

    + * + *

    Call the preventDefault() method in the callback function for the event.

    + * + *

    The following events have default behaviors:

    + * + * + * + * @method #preventDefault + * @memberof Event + */ + /** + * Whether the default event behavior has been prevented via a call to + * preventDefault() (true) or not (false). + * See preventDefault(). + * @method #isDefaultPrevented + * @return {Boolean} + * @memberof Event + */ - try { - if (!analytics) analytics = new OT.Analytics(); - analytics.logError(errorCode, 'tb.exception', title, {details:msg}, contextCopy); + // Event names lookup + OT.Event.names = { + // Activity Status for cams/mics + ACTIVE: 'active', + INACTIVE: 'inactive', + UNKNOWN: 'unknown', - OT.dispatchEvent( - new OT.ExceptionEvent(OT.Event.names.EXCEPTION, msg, title, errorCode, component, component) - ); - } catch(err) { - OT.error('OT.exception :: Failed to dispatch exception - ' + err.toString()); - // Don't throw an error because this is asynchronous - // don't do an exceptionHandler because that would be recursive - } - } + // Archive types + PER_SESSION: 'perSession', + PER_STREAM: 'perStream', -// @todo redo this when we have time to tidy up -// -// @example -// -// OT.handleJsException("Descriptive error message", 2000, { -// session: session, -// target: stream|publisher|subscriber|session|etc -// }); -// - OT.handleJsException = function(errorMsg, code, options) { - options = options || {}; + // OT Events + EXCEPTION: 'exception', + ISSUE_REPORTED: 'issueReported', - var context, - session = options.session; + // Session Events + SESSION_CONNECTED: 'sessionConnected', + SESSION_DISCONNECTED: 'sessionDisconnected', + STREAM_CREATED: 'streamCreated', + STREAM_DESTROYED: 'streamDestroyed', + CONNECTION_CREATED: 'connectionCreated', + CONNECTION_DESTROYED: 'connectionDestroyed', + SIGNAL: 'signal', + STREAM_PROPERTY_CHANGED: 'streamPropertyChanged', + MICROPHONE_LEVEL_CHANGED: 'microphoneLevelChanged', - if (session) { - context = { - sessionId: session.sessionId - }; - if (session.isConnected()) context.connectionId = session.connection.connectionId; - if (!options.target) options.target = session; + // Publisher Events + RESIZE: 'resize', + SETTINGS_BUTTON_CLICK: 'settingsButtonClick', + DEVICE_INACTIVE: 'deviceInactive', + INVALID_DEVICE_NAME: 'invalidDeviceName', + ACCESS_ALLOWED: 'accessAllowed', + ACCESS_DENIED: 'accessDenied', + ACCESS_DIALOG_OPENED: 'accessDialogOpened', + ACCESS_DIALOG_CLOSED: 'accessDialogClosed', + ECHO_CANCELLATION_MODE_CHANGED: 'echoCancellationModeChanged', + MEDIA_STOPPED: 'mediaStopped', + PUBLISHER_DESTROYED: 'destroyed', - } else if (options.sessionId) { - context = { - sessionId: options.sessionId - }; + // Subscriber Events + SUBSCRIBER_DESTROYED: 'destroyed', - if (!options.target) options.target = null; - } + // DeviceManager Events + DEVICES_DETECTED: 'devicesDetected', - _exceptionHandler(options.target, errorMsg, code, context); - }; + // DevicePanel Events + DEVICES_SELECTED: 'devicesSelected', + CLOSE_BUTTON_CLICK: 'closeButtonClick', - // This is a placeholder until error handling can be rewritten - OT.dispatchError = function (code, message, completionHandler, session) { - OT.error(code, message); + MICLEVEL : 'microphoneActivityLevel', + MICGAINCHANGED : 'microphoneGainChanged', - if (completionHandler && OT.$.isFunction(completionHandler)) { - completionHandler.call(null, new OT.Error(code, message)); - } + // Environment Loader + ENV_LOADED: 'envLoaded', + ENV_UNLOADED: 'envUnloaded', - OT.handleJsException(message, code, { - session: session - }); + // Audio activity Events + AUDIO_LEVEL_UPDATED: 'audioLevelUpdated' }; -})(window); -!(function() { + OT.ExceptionCodes = { + JS_EXCEPTION: 2000, + AUTHENTICATION_ERROR: 1004, + INVALID_SESSION_ID: 1005, + CONNECT_FAILED: 1006, + CONNECT_REJECTED: 1007, + CONNECTION_TIMEOUT: 1008, + NOT_CONNECTED: 1010, + P2P_CONNECTION_FAILED: 1013, + API_RESPONSE_FAILURE: 1014, + TERMS_OF_SERVICE_FAILURE: 1026, + UNABLE_TO_PUBLISH: 1500, + UNABLE_TO_SUBSCRIBE: 1501, + UNABLE_TO_FORCE_DISCONNECT: 1520, + UNABLE_TO_FORCE_UNPUBLISH: 1530 + }; - OT.ConnectionCapabilities = function(capabilitiesHash) { - // Private helper methods - var castCapabilities = function(capabilitiesHash) { - capabilitiesHash.supportsWebRTC = OT.$.castToBoolean(capabilitiesHash.supportsWebRTC); - return capabilitiesHash; - }; + /** + * The {@link OT} class dispatches exception events when the OpenTok API encounters + * an exception (error). The ExceptionEvent object defines the properties of the event + * object that is dispatched. + * + *

    Note that you set up a callback for the exception event by calling the + * OT.on() method.

    + * + * @class ExceptionEvent + * @property {Number} code The error code. The following is a list of error codes:

    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    + * code + * + * + * title + *
    + * 1004 + * + * + * Authentication error + *
    + * 1005 + * + * + * Invalid Session ID + *
    + * 1006 + * + * + * Connect Failed + *
    + * 1007 + * + * + * Connect Rejected + *
    + * 1008 + * + * + * Connect Time-out + *
    + * 1009 + * + * + * Security Error + *
    + * 1010 + * + * + * Not Connected + *
    + * 1011 + * + * + * Invalid Parameter + *
    + * 1013 + * + * Connection Failed + *
    + * 1014 + * + * API Response Failure + *
    + * 1026 + * + * Terms of Service Violation: Export Compliance + *
    + * 1500 + * + * Unable to Publish + *
    + * 1520 + * + * Unable to Force Disconnect + *
    + * 1530 + * + * Unable to Force Unpublish + *
    + * 1535 + * + * Force Unpublish on Invalid Stream + *
    + * 2000 + * + * + * Internal Error + *
    + * 2010 + * + * + * Report Issue Failure + *
    + * + *

    Check the message property for more details about the error.

    + * + * @property {String} message The error message. + * + * @property {Object} target The object that the event pertains to. For an + * exception event, this will be an object other than the OT object + * (such as a Session object or a Publisher object). + * + * @property {String} title The error title. + * @augments Event + */ + OT.ExceptionEvent = function (type, message, title, code, component, target) { + OT.Event.call(this, type); - // Private data - var _caps = castCapabilities(capabilitiesHash); - this.supportsWebRTC = _caps.supportsWebRTC; + this.message = message; + this.title = title; + this.code = code; + this.component = component; + this.target = target; }; -})(window); -!(function() { - - /** - * The Connection object represents a connection to an OpenTok session. Each client that connects - * to a session has a unique connection, with a unique connection ID (represented by the - * id property of the Connection object for the client). - *

    - * The Session object has a connection property that is a Connection object. - * It represents the local client's connection. (A client only has a connection once the - * client has successfully called the connect() method of the {@link Session} - * object.) - *

    - * The Session object dispatches a connectionCreated event when each client - * (including your own) connects to a session (and for clients that are present in the - * session when you connect). The connectionCreated event object has a - * connection property, which is a Connection object corresponding to the client - * the event pertains to. - *

    - * The Stream object has a connection property that is a Connection object. - * It represents the connection of the client that is publishing the stream. - * - * @class Connection - * @property {String} connectionId The ID of this connection. - * @property {Number} creationTime The timestamp for the creation of the connection. This - * value is calculated in milliseconds. - * You can convert this value to a Date object by calling new Date(creationTime), - * where creationTime - * is the creationTime property of the Connection object. - * @property {String} data A string containing metadata describing the - * connection. When you generate a user token string pass the connection data string to the - * generate_token() method of our - * server-side libraries. You can also generate a token - * and define connection data on the - * Dashboard page. - */ - OT.Connection = function(id, creationTime, data, capabilitiesHash, permissionsHash) { - var destroyedReason; - - this.id = this.connectionId = id; - this.creationTime = creationTime ? Number(creationTime) : null; - this.data = data; - this.capabilities = new OT.ConnectionCapabilities(capabilitiesHash); - this.permissions = new OT.Capabilities(permissionsHash); - this.quality = null; - - OT.$.eventing(this); - - this.destroy = OT.$.bind(function(reason, quiet) { - destroyedReason = reason || 'clientDisconnected'; - - if (quiet !== true) { - this.dispatchEvent( - new OT.DestroyedEvent( - 'destroyed', // This should be OT.Event.names.CONNECTION_DESTROYED, but - // the value of that is currently shared with Session - this, - destroyedReason - ) - ); - } - }, this); - - this.destroyed = function() { - return destroyedReason !== void 0; - }; - - this.destroyedReason = function() { - return destroyedReason; - }; - - }; - OT.Connection.fromHash = function(hash) { - return new OT.Connection(hash.id, - hash.creationTime, - hash.data, - OT.$.extend(hash.capablities || {}, { supportsWebRTC: true }), - hash.permissions || [] ); + OT.IssueReportedEvent = function (type, issueId) { + OT.Event.call(this, type); + this.issueId = issueId; }; -})(window); -!(function() { - - // id: String | mandatory | immutable - // type: String {video/audio/data/...} | mandatory | immutable - // active: Boolean | mandatory | mutable - // orientation: Integer? | optional | mutable - // frameRate: Float | optional | mutable - // height: Integer | optional | mutable - // width: Integer | optional | mutable - OT.StreamChannel = function(options) { - this.id = options.id; - this.type = options.type; - this.active = OT.$.castToBoolean(options.active); - this.orientation = options.orientation || OT.VideoOrientation.ROTATED_NORMAL; - if (options.frameRate) this.frameRate = parseFloat(options.frameRate, 10); - this.width = parseInt(options.width, 10); - this.height = parseInt(options.height, 10); - - OT.$.eventing(this, true); - - // Returns true if a property was updated. - this.update = function(attributes) { - var videoDimensions = {}, - oldVideoDimensions = {}; - - for (var key in attributes) { - if(!attributes.hasOwnProperty(key)) { - continue; - } - // we shouldn't really read this before we know the key is valid - var oldValue = this[key]; - - switch(key) { - case 'active': - this.active = OT.$.castToBoolean(attributes[key]); - break; - - case 'disableWarning': - this.disableWarning = OT.$.castToBoolean(attributes[key]); - break; - - case 'frameRate': - this.frameRate = parseFloat(attributes[key], 10); - break; - - case 'width': - case 'height': - this[key] = parseInt(attributes[key], 10); - - videoDimensions[key] = this[key]; - oldVideoDimensions[key] = oldValue; - break; - - case 'orientation': - this[key] = attributes[key]; - - videoDimensions[key] = this[key]; - oldVideoDimensions[key] = oldValue; - break; - - default: - OT.warn('Tried to update unknown key ' + key + ' on ' + this.type + - ' channel ' + this.id); - return; - } - - this.trigger('update', this, key, oldValue, this[key]); - } - - if (OT.$.keys(videoDimensions).length) { - // To make things easier for the public API, we broadcast videoDimensions changes, - // which is an aggregate of width, height, and orientation changes. - this.trigger('update', this, 'videoDimensions', oldVideoDimensions, videoDimensions); - } - - return true; - }; + // Triggered when the JS dynamic config and the DOM have loaded. + OT.EnvLoadedEvent = function (type) { + OT.Event.call(this, type); }; -})(window); -!(function() { - - var validPropertyNames = ['name', 'archiving']; /** - * Specifies a stream. A stream is a representation of a published stream in a session. When a - * client calls the Session.publish() method, a new stream is - * created. Properties of the Stream object provide information about the stream. + * Defines connectionCreated and connectionDestroyed events dispatched by + * the {@link Session} object. + *

    + * The Session object dispatches a connectionCreated event when a client (including + * your own) connects to a Session. It also dispatches a connectionCreated event for + * every client in the session when you first connect. (when your local client connects, the Session + * object also dispatches a sessionConnected event, defined by the + * {@link SessionConnectEvent} class.) + *

    + * While you are connected to the session, the Session object dispatches a + * connectionDestroyed event when another client disconnects from the Session. + * (When you disconnect, the Session object also dispatches a sessionDisconnected + * event, defined by the {@link SessionDisconnectEvent} class.) * - *

    When a stream is added to a session, the Session object dispatches a - * streamCreatedEvent. When a stream is destroyed, the Session object dispatches a - * streamDestroyed event. The StreamEvent object, which defines these event objects, - * has a stream property, which is an array of Stream object. For details and a code - * example, see {@link StreamEvent}.

    + *
    Example
    * - *

    When a connection to a session is made, the Session object dispatches a - * sessionConnected event, defined by the SessionConnectEvent object. The - * SessionConnectEvent object has a streams property, which is an array of Stream - * objects pertaining to the streams in the session at that time. For details and a code example, - * see {@link SessionConnectEvent}.

    + *

    The following code keeps a running total of the number of connections to a session + * by monitoring the connections property of the sessionConnect, + * connectionCreated and connectionDestroyed events:

    * - * @class Stream - * @property {Connection} connection The Connection object corresponding - * to the connection that is publishing the stream. You can compare this to to the - * connection property of the Session object to see if the stream is being published - * by the local web page. + *
    var apiKey = ""; // Replace with your API key. See https://dashboard.tokbox.com/projects
    + * var sessionID = ""; // Replace with your own session ID.
    + *                     // See https://dashboard.tokbox.com/projects
    + * var token = ""; // Replace with a generated token that has been assigned the moderator role.
    + *                 // See https://dashboard.tokbox.com/projects
    + * var connectionCount = 0;
      *
    - * @property {Number} creationTime The timestamp for the creation
    - * of the stream. This value is calculated in milliseconds. You can convert this value to a
    - * Date object by calling new Date(creationTime), where creationTime is
    - * the creationTime property of the Stream object.
    + * var session = OT.initSession(apiKey, sessionID);
    + * session.on("connectionCreated", function(event) {
    + *    connectionCount++;
    + *    displayConnectionCount();
    + * });
    + * session.on("connectionDestroyed", function(event) {
    + *    connectionCount--;
    + *    displayConnectionCount();
    + * });
    + * session.connect(token);
      *
    - * @property {Number} frameRate The frame rate of the video stream. This property is only set if the
    - * publisher of the stream specifies a frame rate when calling the OT.initPublisher()
    - * method; otherwise, this property is undefined.
    + * function displayConnectionCount() {
    + *     document.getElementById("connectionCountField").value = connectionCount.toString();
    + * }
    + * + *

    This example assumes that there is an input text field in the HTML DOM + * with the id set to "connectionCountField":

    + * + *
    <input type="text" id="connectionCountField" value="0"></input>
    + * + * + * @property {Connection} connection A Connection objects for the connections that was + * created or deleted. + * + * @property {Array} connections Deprecated. Use the connection property. A + * connectionCreated or connectionDestroyed event is dispatched + * for each connection created and destroyed in the session. + * + * @property {String} reason For a connectionDestroyed event, + * a description of why the connection ended. This property can have two values: + *

    + *
      + *
    • "clientDisconnected" — A client disconnected from the session by calling + * the disconnect() method of the Session object or by closing the browser. + * (See Session.disconnect().)
    • * - * @property {Boolean} hasAudio Whether the stream has audio. This property can change if the - * publisher turns on or off audio (by calling - * Publisher.publishAudio()). When this occurs, the - * {@link Session} object dispatches a streamPropertyChanged event (see - * {@link StreamPropertyChangedEvent}.) + *
    • "forceDisconnected" — A moderator has disconnected the publisher + * from the session, by calling the forceDisconnect() method of the Session + * object. (See Session.forceDisconnect().)
    • * - * @property {Boolean} hasVideo Whether the stream has video. This property can change if the - * publisher turns on or off video (by calling - * Publisher.publishVideo()). When this occurs, the - * {@link Session} object dispatches a streamPropertyChanged event (see - * {@link StreamPropertyChangedEvent}.) + *
    • "networkDisconnected" — The network connection terminated abruptly + * (for example, the client lost their internet connection).
    • + *
    * - * @property {String} name The name of the stream. Publishers can specify a name when publishing - * a stream (using the publish() method of the publisher's Session object). + *

    Depending on the context, this description may allow the developer to refine + * the course of action they take in response to an event.

    * - * @property {String} streamId The unique ID of the stream. + *

    For a connectionCreated event, this string is undefined.

    * - * @property {Object} videoDimensions This object has two properties: width and - * height. Both are numbers. The width property is the width of the - * encoded stream; the height property is the height of the encoded stream. (These - * are independent of the actual width of Publisher and Subscriber objects corresponding to the - * stream.) This property can change if a stream - * published from an iOS device resizes, based on a change in the device orientation. When this - * occurs, the {@link Session} object dispatches a streamPropertyChanged event (see - * {@link StreamPropertyChangedEvent}.) + * @class ConnectionEvent + * @augments Event */ + var connectionEventPluralDeprecationWarningShown = false; + OT.ConnectionEvent = function (type, connection, reason) { + OT.Event.call(this, type, false); - - OT.Stream = function(id, name, creationTime, connection, session, channel) { - var destroyedReason; - - this.id = this.streamId = id; - this.name = name; - this.creationTime = Number(creationTime); - - this.connection = connection; - this.channel = channel; - this.publisher = OT.publishers.find({streamId: this.id}); - - OT.$.eventing(this); - - var onChannelUpdate = OT.$.bind(function(channel, key, oldValue, newValue) { - var _key = key; - - switch(_key) { - case 'active': - _key = channel.type === 'audio' ? 'hasAudio' : 'hasVideo'; - this[_key] = newValue; - break; - - case 'disableWarning': - _key = channel.type === 'audio' ? 'audioDisableWarning': 'videoDisableWarning'; - this[_key] = newValue; - if (!this[channel.type === 'audio' ? 'hasAudio' : 'hasVideo']) { - return; // Do NOT event in this case. + if (OT.$.canDefineProperty) { + Object.defineProperty(this, 'connections', { + get: function() { + if(!connectionEventPluralDeprecationWarningShown) { + OT.warn('OT.ConnectionEvent connections property is deprecated, ' + + 'use connection instead.'); + connectionEventPluralDeprecationWarningShown = true; } - break; - - case 'orientation': - case 'width': - case 'height': - this.videoDimensions = { - width: channel.width, - height: channel.height, - orientation: channel.orientation - }; - - // We dispatch this via the videoDimensions key instead - return; - } - - this.dispatchEvent( new OT.StreamUpdatedEvent(this, _key, oldValue, newValue) ); - }, this); - - var associatedWidget = OT.$.bind(function() { - if(this.publisher) { - return this.publisher; - } else { - return OT.subscribers.find(function(subscriber) { - return subscriber.stream.id === this.id && - subscriber.session.id === session.id; - }); - } - }, this); - - // Returns all channels that have a type of +type+. - this.getChannelsOfType = function (type) { - return OT.$.filter(this.channel, function(channel) { - return channel.type === type; + return [connection]; + } }); - }; - - this.getChannel = function (id) { - for (var i=0; iExample — streamCreated event dispatched + * by the Session object + *

    The following code initializes a session and sets up an event listener for when + * a stream published by another client is created:

    + * + *
    + * session.on("streamCreated", function(event) {
    + *   // streamContainer is a DOM element
    + *   subscriber = session.subscribe(event.stream, targetElement);
    + * }).connect(token);
    + * 
    + * + *

    Example — streamDestroyed event dispatched + * by the Session object

    + * + *

    The following code initializes a session and sets up an event listener for when + * other clients' streams end:

    + * + *
    + * session.on("streamDestroyed", function(event) {
    + *     console.log("Stream " + event.stream.name + " ended. " + event.reason);
    + * }).connect(token);
    + * 
    + * + *

    Example — streamCreated event dispatched + * by a Publisher object

    + *

    The following code publishes a stream and adds an event listener for when the streaming + * starts

    + * + *
    + * var publisher = session.publish(targetElement)
    + *   .on("streamCreated", function(event) {
    + *     console.log("Publisher started streaming.");
    + *   );
    + * 
    + * + *

    Example — streamDestroyed event + * dispatched by a Publisher object

    + * + *

    The following code publishes a stream, and leaves the Publisher in the HTML DOM + * when the streaming stops:

    + * + *
    + * var publisher = session.publish(targetElement)
    + *   .on("streamDestroyed", function(event) {
    + *     event.preventDefault();
    + *     console.log("Publisher stopped streaming.");
    + *   );
    + * 
    + * + * @class StreamEvent + * + * @property {Boolean} cancelable Whether the event has a default behavior that is cancelable + * (true) or not (false). You can cancel the default behavior by calling + * the preventDefault() method of the StreamEvent object in the event listener + * function. The streamDestroyed + * event is cancelable. (See preventDefault().) + * + * @property {String} reason For a streamDestroyed event, + * a description of why the session disconnected. This property can have one of the following + * values: + *

    + *
      + *
    • "clientDisconnected" — A client disconnected from the session by calling + * the disconnect() method of the Session object or by closing the browser. + * (See Session.disconnect().)
    • + * + *
    • "forceDisconnected" — A moderator has disconnected the publisher of the + * stream from the session, by calling the forceDisconnect() method of the Session +* object. (See Session.forceDisconnect().)
    • + * + *
    • "forceUnpublished" — A moderator has forced the publisher of the stream + * to stop publishing the stream, by calling the forceUnpublish() method of the + * Session object. (See Session.forceUnpublish().)
    • + * + *
    • "mediaStopped" — The user publishing the stream has stopped sharing the + * screen. This value is only used in screen-sharing video streams.
    • + * + *
    • "networkDisconnected" — The network connection terminated abruptly (for + * example, the client lost their internet connection).
    • + * + *
    + * + *

    Depending on the context, this description may allow the developer to refine + * the course of action they take in response to an event.

    + * + *

    For a streamCreated event, this string is undefined.

    + * + * @property {Stream} stream A Stream object corresponding to the stream that was added (in the + * case of a streamCreated event) or deleted (in the case of a + * streamDestroyed event). + * + * @property {Array} streams Deprecated. Use the stream property. A + * streamCreated or streamDestroyed event is dispatched for + * each stream added or destroyed. + * + * @augments Event + */ - switch(key) { - case 'name': - this[key] = newValue; - break; + var streamEventPluralDeprecationWarningShown = false; + OT.StreamEvent = function (type, stream, reason, cancelable) { + OT.Event.call(this, type, cancelable); - case 'archiving': - var widget = associatedWidget(); - if(widget) { - widget._.archivingStatus(newValue); + if (OT.$.canDefineProperty) { + Object.defineProperty(this, 'streams', { + get: function() { + if(!streamEventPluralDeprecationWarningShown) { + OT.warn('OT.StreamEvent streams property is deprecated, use stream instead.'); + streamEventPluralDeprecationWarningShown = true; } - this[key] = newValue; - break; - } - - var event = new OT.StreamUpdatedEvent(this, key, oldValue, newValue); - this.dispatchEvent(event); - }, this); - - // Mass update, called by Raptor.Dispatcher - this._.update = OT.$.bind(function(attributes) { - for (var key in attributes) { - if(!attributes.hasOwnProperty(key)) { - continue; + return [stream]; } - this._.updateProperty(key, attributes[key]); - } - }, this); + }); + } else { + this.streams = [stream]; + } - this._.updateChannel = OT.$.bind(function(channelId, attributes) { - this.getChannel(channelId).update(attributes); - }, this); + this.stream = stream; + this.reason = reason; }; -})(window); -!(function() { - - - OT.Archive = function(id, name, status) { - this.id = id; - this.name = name; - this.status = status; +/** +* Prevents the default behavior associated with the event from taking place. +* +*

    For the streamDestroyed event dispatched by the Session object, +* the default behavior is that all Subscriber objects that are subscribed to the stream are +* unsubscribed and removed from the HTML DOM. Each Subscriber object dispatches a +* destroyed event when the element is removed from the HTML DOM. If you call the +* preventDefault() method in the event listener for the streamDestroyed +* event, the default behavior is prevented and you can clean up Subscriber objects using your +* own code. See +* Session.getSubscribersForStream().

    +*

    +* For the streamDestroyed event dispatched by a Publisher object, the default +* behavior is that the Publisher object is removed from the HTML DOM. The Publisher object +* dispatches a destroyed event when the element is removed from the HTML DOM. +* If you call the preventDefault() method in the event listener for the +* streamDestroyed event, the default behavior is prevented, and you can +* retain the Publisher for reuse or clean it up using your own code. +*

    +*

    To see whether an event has a default behavior, check the cancelable property of +* the event object.

    +* +*

    Call the preventDefault() method in the event listener function for the event.

    +* +* @method #preventDefault +* @memberof StreamEvent +*/ - this._ = {}; +/** + * The Session object dispatches SessionConnectEvent object when a session has successfully + * connected in response to a call to the connect() method of the Session object. + *

    + * In version 2.2, the completionHandler of the Session.connect() method + * indicates success or failure in connecting to the session. + * + * @class SessionConnectEvent + * @property {Array} connections Deprecated in version 2.2 (and set to an empty array). In + * version 2.2, listen for the connectionCreated event dispatched by the Session + * object. In version 2.2, the Session object dispatches a connectionCreated event + * for each connection (including your own). This includes connections present when you first + * connect to the session. + * + * @property {Array} streams Deprecated in version 2.2 (and set to an empty array). In version + * 2.2, listen for the streamCreated event dispatched by the Session object. In + * version 2.2, the Session object dispatches a streamCreated event for each stream + * other than those published by your client. This includes streams + * present when you first connect to the session. + * + * @see Session.connect()

    + * @augments Event + */ - OT.$.eventing(this); + var sessionConnectedConnectionsDeprecationWarningShown = false; + var sessionConnectedStreamsDeprecationWarningShown = false; + var sessionConnectedArchivesDeprecationWarningShown = false; - // Mass update, called by Raptor.Dispatcher - this._.update = OT.$.bind(function (attributes) { - for (var key in attributes) { - if(!attributes.hasOwnProperty(key)) { - continue; + OT.SessionConnectEvent = function (type) { + OT.Event.call(this, type, false); + if (OT.$.canDefineProperty) { + Object.defineProperties(this, { + connections: { + get: function() { + if(!sessionConnectedConnectionsDeprecationWarningShown) { + OT.warn('OT.SessionConnectedEvent no longer includes connections. Listen ' + + 'for connectionCreated events instead.'); + sessionConnectedConnectionsDeprecationWarningShown = true; + } + return []; + } + }, + streams: { + get: function() { + if(!sessionConnectedStreamsDeprecationWarningShown) { + OT.warn('OT.SessionConnectedEvent no longer includes streams. Listen for ' + + 'streamCreated events instead.'); + sessionConnectedConnectionsDeprecationWarningShown = true; + } + return []; + } + }, + archives: { + get: function() { + if(!sessionConnectedArchivesDeprecationWarningShown) { + OT.warn('OT.SessionConnectedEvent no longer includes archives. Listen for ' + + 'archiveStarted events instead.'); + sessionConnectedArchivesDeprecationWarningShown = true; + } + return []; + } } - var oldValue = this[key]; - this[key] = attributes[key]; - - var event = new OT.ArchiveUpdatedEvent(this, key, oldValue, this[key]); - this.dispatchEvent(event); - } - }, this); - - this.destroy = function() {}; + }); + } else { + this.connections = []; + this.streams = []; + this.archives = []; + } + }; +/** + * The Session object dispatches SessionDisconnectEvent object when a session has disconnected. + * This event may be dispatched asynchronously in response to a successful call to the + * disconnect() method of the session object. + * + *

    + * Example + *

    + *

    + * The following code initializes a session and sets up an event listener for when a session is + * disconnected. + *

    + *
    var apiKey = ""; // Replace with your API key. See https://dashboard.tokbox.com/projects
    + *  var sessionID = ""; // Replace with your own session ID.
    + *                      // See https://dashboard.tokbox.com/projects
    + *  var token = ""; // Replace with a generated token that has been assigned the moderator role.
    + *                  // See https://dashboard.tokbox.com/projects
    + *
    + *  var session = OT.initSession(apiKey, sessionID);
    + *  session.on("sessionDisconnected", function(event) {
    + *      alert("The session disconnected. " + event.reason);
    + *  });
    + *  session.connect(token);
    + *  
    + * + * @property {String} reason A description of why the session disconnected. + * This property can have two values: + *

    + *
      + *
    • "clientDisconnected" — A client disconnected from the session by calling + * the disconnect() method of the Session object or by closing the browser. + * ( See Session.disconnect().)
    • + *
    • "forceDisconnected" — A moderator has disconnected you from the session + * by calling the forceDisconnect() method of the Session object. (See + * Session.forceDisconnect().)
    • + *
    • "networkDisconnected" — The network connection terminated abruptly + * (for example, the client lost their internet connection).
    • + *
    + * + * @class SessionDisconnectEvent + * @augments Event + */ + OT.SessionDisconnectEvent = function (type, reason, cancelable) { + OT.Event.call(this, type, cancelable); + this.reason = reason; }; -})(window); -!(function() { - - - /* - * A RTCPeerConnection.getStats based audio level sampler. - * - * It uses the the getStats method to get the audioOutputLevel. - * This implementation expects the single parameter version of the getStats method. - * - * Currently the audioOutputLevel stats is only supported in Chrome. - * - * @param {OT.SubscriberPeerConnection} peerConnection the peer connection to use to get the stats - * @constructor - */ - OT.GetStatsAudioLevelSampler = function(peerConnection) { - - if (!OT.$.hasCapabilities('audioOutputLevelStat', 'getStatsWithSingleParameter')) { - throw new Error('The current platform does not provide the required capabilities'); - } - - var _peerConnection = peerConnection, - _statsProperty = 'audioOutputLevel'; - - /* - * Acquires the audio level. - * - * @param {function(?number)} done a callback to be called with the acquired value in the - * [0, 1] range when available or null if no value could be acquired - */ - this.sample = function(done) { - _peerConnection.getStatsWithSingleParameter(function(statsReport) { - var results = statsReport.result(); - - for (var i = 0; i < results.length; i++) { - var result = results[i]; - if (result.local) { - var audioOutputLevel = parseFloat(result.local.stat(_statsProperty)); - if (!isNaN(audioOutputLevel)) { - // the mex value delivered by getStats for audio levels is 2^15 - done(audioOutputLevel / 32768); - return; - } - } - } +/** +* Prevents the default behavior associated with the event from taking place. +* +*

    For the sessionDisconnectEvent, the default behavior is that all Subscriber +* objects are unsubscribed and removed from the HTML DOM. Each Subscriber object dispatches a +* destroyed event when the element is removed from the HTML DOM. If you call the +* preventDefault() method in the event listener for the sessionDisconnect +* event, the default behavior is prevented, and you can, optionally, clean up Subscriber objects +* using your own code). +* +*

    To see whether an event has a default behavior, check the cancelable property of +* the event object.

    +* +*

    Call the preventDefault() method in the event listener function for the event.

    +* +* @method #preventDefault +* @memberof SessionDisconnectEvent +*/ - done(null); - }); - }; +/** + * The Session object dispatches a streamPropertyChanged event in the + * following circumstances: + * + *
      + *
    • A stream has started or stopped publishing audio or video (see + * Publisher.publishAudio() and + * Publisher.publishVideo()). + * This change results from a call to the publishAudio() or + * publishVideo() methods of the Publish object. Note that a + * subscriber's video can be disabled or enabled for reasons other than the + * publisher disabling or enabling it. A Subscriber object dispatches + * videoDisabled and videoEnabled events in all + * conditions that cause the subscriber's stream to be disabled or enabled. + *
    • + *
    • The videoDimensions property of the Stream object has + * changed (see Stream.videoDimensions). + *
    • + *
    • The videoType property of the Stream object has changed. + * This can happen in a stream published by a mobile device. (See + * Stream.videoType.) + *
    • + *
    + * + * @class StreamPropertyChangedEvent + * @property {String} changedProperty The property of the stream that changed. This value + * is either "hasAudio", "hasVideo", or "videoDimensions". + * @property {Object} newValue The new value of the property (after the change). + * @property {Object} oldValue The old value of the property (before the change). + * @property {Stream} stream The Stream object for which a property has changed. + * + * @see Publisher.publishAudio()

    + * @see Publisher.publishVideo()

    + * @see Stream.videoDimensions

    + * @augments Event + */ + OT.StreamPropertyChangedEvent = function (type, stream, changedProperty, oldValue, newValue) { + OT.Event.call(this, type, false); + this.type = type; + this.stream = stream; + this.changedProperty = changedProperty; + this.oldValue = oldValue; + this.newValue = newValue; }; - - /* - * An AudioContext based audio level sampler. It returns the maximum value in the - * last 1024 samples. - * - * It is worth noting that the remote MediaStream audio analysis is currently only - * available in FF. - * - * This implementation gracefully handles the case where the MediaStream has not - * been set yet by returning a null value until the stream is set. It is up to the - * call site to decide what to do with this value (most likely ignore it and retry later). - * - * @constructor - * @param {AudioContext} audioContext an audio context instance to get an analyser node - */ - OT.AnalyserAudioLevelSampler = function(audioContext) { - - var _sampler = this, - _analyser = null, - _timeDomainData = null; - - var _getAnalyser = function(stream) { - var sourceNode = audioContext.createMediaStreamSource(stream); - var analyser = audioContext.createAnalyser(); - sourceNode.connect(analyser); - return analyser; - }; - - this.webOTStream = null; - - this.sample = function(done) { - - if (!_analyser && _sampler.webOTStream) { - _analyser = _getAnalyser(_sampler.webOTStream); - _timeDomainData = new Uint8Array(_analyser.frequencyBinCount); - } - - if (_analyser) { - _analyser.getByteTimeDomainData(_timeDomainData); - - // varies from 0 to 255 - var max = 0; - for (var idx = 0; idx < _timeDomainData.length; idx++) { - max = Math.max(max, Math.abs(_timeDomainData[idx] - 128)); - } - - // normalize the collected level to match the range delivered by - // the getStats' audioOutputLevel - done(max / 128); - } else { - done(null); - } - }; + OT.VideoDimensionsChangedEvent = function (target, oldValue, newValue) { + OT.Event.call(this, 'videoDimensionsChanged', false); + this.type = 'videoDimensionsChanged'; + this.target = target; + this.oldValue = oldValue; + this.newValue = newValue; }; - /* - * Transforms a raw audio level to produce a "smoother" animation when using displaying the - * audio level. This transformer is state-full because it needs to keep the previous average - * value of the signal for filtering. - * - * It applies a low pass filter to get rid of level jumps and apply a log scale. - * - * @constructor - */ - OT.AudioLevelTransformer = function() { - - var _averageAudioLevel = null; - - /* - * - * @param {number} audioLevel a level in the [0,1] range - * @returns {number} a level in the [0,1] range transformed - */ - this.transform = function(audioLevel) { - if (_averageAudioLevel === null || audioLevel >= _averageAudioLevel) { - _averageAudioLevel = audioLevel; - } else { - // a simple low pass filter with a smoothing of 70 - _averageAudioLevel = audioLevel * 0.3 + _averageAudioLevel * 0.7; - } - - // 1.5 scaling to map -30-0 dBm range to [0,1] - var logScaled = (Math.log(_averageAudioLevel) / Math.LN10) / 1.5 + 1; - - return Math.min(Math.max(logScaled, 0), 1); - }; +/** + * Defines event objects for the archiveStarted and archiveStopped events. + * The Session object dispatches these events when an archive recording of the session starts and + * stops. + * + * @property {String} id The archive ID. + * @property {String} name The name of the archive. You can assign an archive a name when you create + * it, using the OpenTok REST API or one of the + * OpenTok server SDKs. + * + * @class ArchiveEvent + * @augments Event + */ + OT.ArchiveEvent = function (type, archive) { + OT.Event.call(this, type, false); + this.type = type; + this.id = archive.id; + this.name = archive.name; + this.status = archive.status; + this.archive = archive; }; -})(window); -!(function() { - - /* - * Executes the provided callback thanks to window.setInterval. - * - * @param {function()} callback - * @param {number} frequency how many times per second we want to execute the callback - * @constructor - */ - OT.IntervalRunner = function(callback, frequency) { - var _callback = callback, - _frequency = frequency, - _intervalId = null; - - this.start = function() { - _intervalId = window.setInterval(_callback, 1000 / _frequency); - }; - - this.stop = function() { - window.clearInterval(_intervalId); - _intervalId = null; - }; + OT.ArchiveUpdatedEvent = function (stream, key, oldValue, newValue) { + OT.Event.call(this, 'updated', false); + this.target = stream; + this.changedProperty = key; + this.oldValue = oldValue; + this.newValue = newValue; }; -})(window); -!(function(window) { - - // Normalise these - var NativeRTCSessionDescription, - NativeRTCIceCandidate; - - if (!TBPlugin.isInstalled()) { - // order is very important: 'RTCSessionDescription' defined in Firefox Nighly but useless - NativeRTCSessionDescription = (window.mozRTCSessionDescription || - window.RTCSessionDescription); - NativeRTCIceCandidate = (window.mozRTCIceCandidate || window.RTCIceCandidate); - } - else { - NativeRTCSessionDescription = TBPlugin.RTCSessionDescription; - NativeRTCIceCandidate = TBPlugin.RTCIceCandidate; - } - - // Helper function to forward Ice Candidates via +messageDelegate+ - var iceCandidateForwarder = function(messageDelegate) { - return function(event) { - if (event.candidate) { - messageDelegate(OT.Raptor.Actions.CANDIDATE, event.candidate); - } else { - OT.debug('IceCandidateForwarder: No more ICE candidates.'); - } - }; +/** + * The Session object dispatches a signal event when the client receives a signal from the session. + * + * @class SignalEvent + * @property {String} type The type assigned to the signal (if there is one). Use the type to + * filter signals received (by adding an event handler for signal:type1 or signal:type2, etc.) + * @property {String} data The data string sent with the signal (if there is one). + * @property {Connection} from The Connection corresponding to the client that sent with the signal. + * + * @see Session.signal()

    + * @see Session events (signal and signal:type)

    + * @augments Event + */ + OT.SignalEvent = function(type, data, from) { + OT.Event.call(this, type ? 'signal:' + type : OT.Event.names.SIGNAL, false); + this.data = data; + this.from = from; }; + OT.StreamUpdatedEvent = function (stream, key, oldValue, newValue) { + OT.Event.call(this, 'updated', false); + this.target = stream; + this.changedProperty = key; + this.oldValue = oldValue; + this.newValue = newValue; + }; - // Process incoming Ice Candidates from a remote connection (which have been - // forwarded via iceCandidateForwarder). The Ice Candidates cannot be processed - // until a PeerConnection is available. Once a PeerConnection becomes available - // the pending PeerConnections can be processed by calling processPending. - // - // @example - // - // var iceProcessor = new IceCandidateProcessor(); - // iceProcessor.process(iceMessage1); - // iceProcessor.process(iceMessage2); - // iceProcessor.process(iceMessage3); - // - // iceProcessor.setPeerConnection(peerConnection); - // iceProcessor.processPending(); - // - var IceCandidateProcessor = function() { - var _pendingIceCandidates = [], - _peerConnection = null; - - this.setPeerConnection = function(peerConnection) { - _peerConnection = peerConnection; - }; - - this.process = function(message) { - var iceCandidate = new NativeRTCIceCandidate(message.content); + OT.DestroyedEvent = function(type, target, reason) { + OT.Event.call(this, type, false); + this.target = target; + this.reason = reason; + }; - if (_peerConnection) { - _peerConnection.addIceCandidate(iceCandidate); - } else { - _pendingIceCandidates.push(iceCandidate); - } - }; +/** + * Defines the event object for the videoDisabled and videoEnabled events + * dispatched by the Subscriber. + * + * @class VideoEnabledChangedEvent + * + * @property {Boolean} cancelable Whether the event has a default behavior that is cancelable + * (true) or not (false). You can cancel the default behavior by + * calling the preventDefault() method of the event object in the callback + * function. (See preventDefault().) + * + * @property {String} reason The reason the video was disabled or enabled. This can be set to one of + * the following values: + * + *
      + * + *
    • "publishVideo" — The publisher started or stopped publishing video, + * by calling publishVideo(true) or publishVideo(false).
    • + * + *
    • "quality" — The OpenTok Media Router starts or stops sending video + * to the subscriber based on stream quality changes. This feature of the OpenTok Media + * Router has a subscriber drop the video stream when connectivity degrades. (The subscriber + * continues to receive the audio stream, if there is one.) + *

      + * If connectivity improves to support video again, the Subscriber object dispatches + * a videoEnabled event, and the Subscriber resumes receiving video. + *

      + * By default, the Subscriber displays a video disabled indicator when a + * videoDisabled event with this reason is dispatched and removes the indicator + * when the videoDisabled event with this reason is dispatched. You can control + * the display of this icon by calling the setStyle() method of the Subscriber, + * setting the videoDisabledDisplayMode property(or you can set the style when + * calling the Session.subscribe() method, setting the style property + * of the properties parameter). + *

      + * This feature is only available in sessions that use the OpenTok Media Router (sessions with + * the media mode + * set to routed), not in sessions with the media mode set to relayed. + *

    • + * + *
    • "subscribeToVideo" — The subscriber started or stopped subscribing to + * video, by calling subscribeToVideo(true) or subscribeToVideo(false). + *
    • + * + *
    + * + * @property {Object} target The object that dispatched the event. + * + * @property {String} type The type of event: "videoDisabled" or + * "videoEnabled". + * + * @see Subscriber videoDisabled event

    + * @see Subscriber videoEnabled event

    + * @augments Event + */ + OT.VideoEnabledChangedEvent = function(type, properties) { + OT.Event.call(this, type, false); + this.reason = properties.reason; + }; - this.processPending = function() { - while(_pendingIceCandidates.length) { - _peerConnection.addIceCandidate(_pendingIceCandidates.shift()); - } - }; + OT.VideoDisableWarningEvent = function(type/*, properties*/) { + OT.Event.call(this, type, false); }; - // Removes all Confort Noise from +sdp+. - // - // See https://jira.tokbox.com/browse/OPENTOK-7176 - // - var removeComfortNoise = function removeComfortNoise (sdp) { - // a=rtpmap: / [/] - var matcher = /a=rtpmap:(\d+) CN\/\d+/i, - payloadTypes = [], - audioMediaLineIndex, - sdpLines, - match; +/** + * Dispatched periodically by a Subscriber or Publisher object to indicate the audio + * level. This event is dispatched up to 60 times per second, depending on the browser. + * + * @property {String} audioLevel The audio level, from 0 to 1.0. Adjust this value logarithmically + * for use in adjusting a user interface element, such as a volume meter. Use a moving average + * to smooth the data. + * + * @class AudioLevelUpdatedEvent + * @augments Event + */ + OT.AudioLevelUpdatedEvent = function(audioLevel) { + OT.Event.call(this, OT.Event.names.AUDIO_LEVEL_UPDATED, false); + this.audioLevel = audioLevel; + }; - // Icky code. This filter operation has two side effects in addition - // to doing the actual filtering: - // 1. extract all the payload types from the rtpmap CN lines - // 2. find the index of the audio media line - // - sdpLines = OT.$.filter(sdp.split('\r\n'), function(line, index) { - if (line.indexOf('m=audio') !== -1) audioMediaLineIndex = index; + OT.MediaStoppedEvent = function(target) { + OT.Event.call(this, OT.Event.names.MEDIA_STOPPED, true); + this.target = target; + }; - match = line.match(matcher); - if (match !== null) { - payloadTypes.push(match[1]); +})(window); - // remove this line as it contains CN - return false; - } +// tb_require('../../helpers/helpers.js') +// tb_require('../events.js') - return true; - }); +var screenSharingExtensionByKind = {}, + screenSharingExtensionClasses = {}; - if (payloadTypes.length && audioMediaLineIndex) { - // Remove all CN payload types from the audio media line. - sdpLines[audioMediaLineIndex] = sdpLines[audioMediaLineIndex].replace( - new RegExp(payloadTypes.join('|'), 'ig') , '').replace(/\s+/g, ' '); - } +OT.registerScreenSharingExtensionHelper = function(kind, helper) { + screenSharingExtensionClasses[kind] = helper; + if (helper.autoRegisters && helper.isSupportedInThisBrowser) { + OT.registerScreenSharingExtension(kind); + } +}; - return sdpLines.join('\r\n'); - }; +/** + * Register a Chrome extension for screen-sharing support. + *

    + * Use the OT.checkScreenSharingCapability() method to check if an extension is + * required, registered, and installed. + *

    + * The OpenTok + * screensharing-extensions + * sample includes code for creating a Chrome extension for screen-sharing support. + * + * @param {String} kind Set this parameter to "chrome". Currently, you can only + * register a screen-sharing extension for Chrome. + * + * @see OT.initPublisher() + * @see OT.checkScreenSharingCapability() + * @method OT.registerScreenSharingExtension + * @memberof OT + */ - var removeVideoCodec = function removeVideoCodec (sdp, codec) { - var matcher = new RegExp('a=rtpmap:(\\d+) ' + codec + '\\/\\d+', 'i'), - payloadTypes = [], - videoMediaLineIndex, - sdpLines, - match; +OT.registerScreenSharingExtension = function(kind) { + var initArgs = Array.prototype.slice.call(arguments, 1); - sdpLines = OT.$.filter(sdp.split('\r\n'), function(line, index) { - if (line.indexOf('m=video') !== -1) videoMediaLineIndex = index; + if (screenSharingExtensionClasses[kind] == null) { + throw new Error('Unsupported kind passed to OT.registerScreenSharingExtension'); + } - match = line.match(matcher); - if (match !== null) { - payloadTypes.push(match[1]); + var x = screenSharingExtensionClasses[kind] + .register.apply(screenSharingExtensionClasses[kind], initArgs); + screenSharingExtensionByKind[kind] = x; +}; - // remove this line as it contains the codec - return false; - } +var screenSharingPickHelper = function() { - return true; - }); + var foundClass = OT.$.find(OT.$.keys(screenSharingExtensionClasses), function(cls) { + return screenSharingExtensionClasses[cls].isSupportedInThisBrowser; + }); - if (payloadTypes.length && videoMediaLineIndex) { - sdpLines[videoMediaLineIndex] = sdpLines[videoMediaLineIndex].replace( - new RegExp(payloadTypes.join('|'), 'ig') , '').replace(/\s+/g, ' '); - } + if (foundClass === void 0) { + return {}; + } - return sdpLines.join('\r\n'); + return { + name: foundClass, + proto: screenSharingExtensionClasses[foundClass], + instance: screenSharingExtensionByKind[foundClass] }; - // Attempt to completely process +offer+. This will: - // * set the offer as the remote description - // * create an answer and - // * set the new answer as the location description - // - // If there are no issues, the +success+ callback will be executed on completion. - // Errors during any step will result in the +failure+ callback being executed. - // - var offerProcessor = function(peerConnection, offer, success, failure) { - var generateErrorCallback, - setLocalDescription, - createAnswer; - - generateErrorCallback = function(message, prefix) { - return function(errorReason) { - OT.error(message); - OT.error(errorReason); - - if (failure) failure(message, errorReason, prefix); - }; - }; - - setLocalDescription = function(answer) { - answer.sdp = removeComfortNoise(answer.sdp); - answer.sdp = removeVideoCodec(answer.sdp, 'ulpfec'); - answer.sdp = removeVideoCodec(answer.sdp, 'red'); - - peerConnection.setLocalDescription( - answer, +}; - // Success - function() { - success(answer); - }, +OT.pickScreenSharingHelper = function() { + return screenSharingPickHelper(); +}; - // Failure - generateErrorCallback('Error while setting LocalDescription', 'SetLocalDescription') - ); - }; +/** + * Checks for screen sharing support on the client browser. The object passed to the callback + * function defines whether screen sharing is supported as well as whether an extension is + * required, installed, and registered (if needed). + *

    + *

    + * OT.checkScreenSharingCapability(function(response) {
    + *   if (!response.supported || response.extensionRegistered === false) {
    + *     // This browser does not support screen sharing
    + *   } else if(response.extensionInstalled === false) {
    + *     // Prompt to install the extension
    + *   } else {
    + *     // Screen sharing is available.
    + *   }
    + * });
    + * 
    + * + * @param {function} callback The callback invoked with the support options object passed as + * the parameter. This object has the following properties: + *

    + *

      + *
    • + * supported (Boolean) — Set to true if screen sharing is supported in the + * browser. Check the extensionRequired property to see if the browser requires + * an extension for screen sharing. + *
    • + *
    • + * extensionRequired (String) — Set to "chrome" on Chrome, + * which requires a screen sharing extension to be installed. Otherwise, this property is + * undefined. + *
    • + *
    • + * extensionRegistered (Boolean) — On Chrome, this property is set to + * true if a screen-sharing extension is registered; otherwise it is set to + * false. If the extension type does not require registration (as in the + * case of of the OpenTok plugin for Internet Explorer), this property is set to + * true. In other browsers (which do not require an extension), this property + * is undefined. Use the OT.registerScreenSharingExtension() method to register + * an extension in Chrome. + *
    • + *
    • + * extensionInstalled (Boolean) — If an extension is required, this is set + * to true if the extension installed (and registered, if needed); otherwise it + * is set to false. If an extension is not required (for example on FireFox), + * this property is undefined. + *
    • + *
    + * + * @see OT.initPublisher() + * @see OT.registerScreenSharingExtension() + * @method OT.checkScreenSharingCapability + * @memberof OT + */ +OT.checkScreenSharingCapability = function(callback) { - createAnswer = function() { - peerConnection.createAnswer( - // Success - setLocalDescription, + var response = { + supported: false, + extensionRequired: void 0, + extensionRegistered: void 0, + extensionInstalled: void 0, + supportedSources: {} + }; - // Failure - generateErrorCallback('Error while setting createAnswer', 'CreateAnswer'), + // find a supported browser - null, // MediaConstraints - false // createProvisionalAnswer - ); - }; + var helper = screenSharingPickHelper(); - // Workaround for a Chrome issue. Add in the SDES crypto line into offers - // from Firefox - if (offer.sdp.indexOf('a=crypto') === -1) { - var cryptoLine = 'a=crypto:1 AES_CM_128_HMAC_SHA1_80 ' + - 'inline:FakeFakeFakeFakeFakeFakeFakeFakeFakeFake\\r\\n'; + if (helper.name === void 0) { + setTimeout(callback.bind(null, response)); + return; + } - // insert the fake crypto line for every M line - offer.sdp = offer.sdp.replace(/^c=IN(.*)$/gmi, 'c=IN$1\r\n'+cryptoLine); - } + response.supported = true; + response.extensionRequired = helper.proto.extensionRequired ? helper.name : void 0; - if (offer.sdp.indexOf('a=rtcp-fb') === -1) { - var rtcpFbLine = 'a=rtcp-fb:* ccm fir\r\na=rtcp-fb:* nack '; + response.supportedSources = { + screen: helper.proto.sources.screen, + application: helper.proto.sources.application, + window: helper.proto.sources.window + }; - // insert the fake crypto line for every M line - offer.sdp = offer.sdp.replace(/^m=video(.*)$/gmi, 'm=video$1\r\n'+rtcpFbLine); + if (!helper.instance) { + response.extensionRegistered = false; + if (response.extensionRequired) { + response.extensionInstalled = false; } + setTimeout(callback.bind(null, response)); + return; + } - peerConnection.setRemoteDescription( - offer, - - // Success - createAnswer, + response.extensionRegistered = response.extensionRequired ? true : void 0; + helper.instance.isInstalled(function(installed) { + response.extensionInstalled = response.extensionRequired ? installed : void 0; + callback(response); + }); + +}; - // Failure - generateErrorCallback('Error while setting RemoteDescription', 'SetRemoteDescription') - ); +// tb_require('./register.js') - }; +OT.registerScreenSharingExtensionHelper('firefox', { + isSupportedInThisBrowser: OT.$.env.name === 'Firefox', + autoRegisters: true, + extensionRequired: false, + getConstraintsShowsPermissionUI: false, + sources: { + screen: true, + application: OT.$.env.name === 'Firefox' && OT.$.env.version >= 34, + window: OT.$.env.name === 'Firefox' && OT.$.env.version >= 34 + }, + register: function() { + return { + isInstalled: function(callback) { + callback(true); + }, + getConstraints: function(source, constraints, callback) { + constraints.video = { + mediaSource: source + }; + callback(void 0, constraints); + } + }; + } +}); - // Attempt to completely process a subscribe message. This will: - // * create an Offer - // * set the new offer as the location description - // - // If there are no issues, the +success+ callback will be executed on completion. - // Errors during any step will result in the +failure+ callback being executed. - // - var suscribeProcessor = function(peerConnection, success, failure) { - var constraints, - generateErrorCallback, - setLocalDescription; - - constraints = { - mandatory: {}, - optional: [] - }, +OT.registerScreenSharingExtensionHelper('chrome', { + isSupportedInThisBrowser: !!navigator.webkitGetUserMedia && typeof chrome !== 'undefined', + autoRegisters: false, + extensionRequired: true, + getConstraintsShowsPermissionUI: true, + sources: { + screen: true, + application: false, + window: false + }, + register: function (extensionID) { + if(!extensionID) { + throw new Error('initChromeScreenSharingExtensionHelper: extensionID is required.'); + } - generateErrorCallback = function(message, prefix) { - return function(errorReason) { - OT.error(message); - OT.error(errorReason); + var isChrome = !!navigator.webkitGetUserMedia && typeof chrome !== 'undefined', + callbackRegistry = {}, + isInstalled = void 0; + + var prefix = 'com.tokbox.screenSharing.' + extensionID; + var request = function(method, payload) { + var res = { payload: payload, from: 'jsapi' }; + res[prefix] = method; + return res; + }; - if (failure) failure(message, errorReason, prefix); + var addCallback = function(fn, timeToWait) { + var requestId = OT.$.uuid(), + timeout; + callbackRegistry[requestId] = function() { + clearTimeout(timeout); + timeout = null; + fn.apply(null, arguments); }; + if(timeToWait) { + timeout = setTimeout(function() { + delete callbackRegistry[requestId]; + fn(new Error('Timeout waiting for response to request.')); + }, timeToWait); + } + return requestId; }; - setLocalDescription = function(offer) { - offer.sdp = removeComfortNoise(offer.sdp); - offer.sdp = removeVideoCodec(offer.sdp, 'ulpfec'); - offer.sdp = removeVideoCodec(offer.sdp, 'red'); + var isAvailable = function(callback) { + if(!callback) { + throw new Error('isAvailable: callback is required.'); + } - peerConnection.setLocalDescription( - offer, + if(!isChrome) { + setTimeout(callback.bind(null, false)); + } - // Success - function() { - success(offer); - }, + if(isInstalled !== void 0) { + setTimeout(callback.bind(null, isInstalled)); + } else { + var requestId = addCallback(function(error, event) { + if(isInstalled !== true) { + isInstalled = (event === 'extensionLoaded'); + } + callback(isInstalled); + }, 2000); + var post = request('isExtensionInstalled', { requestId: requestId }); + window.postMessage(post, '*'); + } + }; - // Failure - generateErrorCallback('Error while setting LocalDescription', 'SetLocalDescription') - ); + var getConstraints = function(source, constraints, callback) { + if(!callback) { + throw new Error('getSourceId: callback is required'); + } + isAvailable(function(isInstalled) { + if(isInstalled) { + var requestId = addCallback(function(error, event, payload) { + if(event === 'permissionDenied') { + callback(new Error('PermissionDeniedError')); + } else { + if (!constraints.video) { + constraints.video = {}; + } + if (!constraints.video.mandatory) { + constraints.video.mandatory = {}; + } + constraints.video.mandatory.chromeMediaSource = 'desktop'; + constraints.video.mandatory.chromeMediaSourceId = payload.sourceId; + callback(void 0, constraints); + } + }); + window.postMessage(request('getSourceId', { requestId: requestId, source: source }), '*'); + } else { + callback(new Error('Extension is not installed')); + } + }); }; - // For interop with FireFox. Disable Data Channel in createOffer. - if (navigator.mozGetUserMedia) { - constraints.mandatory.MozDontOfferDataChannel = true; - } + window.addEventListener('message', function(event) { - peerConnection.createOffer( - // Success - setLocalDescription, + if (event.origin !== window.location.origin) { + return; + } - // Failure - generateErrorCallback('Error while creating Offer', 'CreateOffer'), + if(!(event.data != null && typeof event.data === 'object')) { + return; + } - constraints - ); - }; + if(event.data.from !== 'extension') { + return; + } - /* - * Negotiates a WebRTC PeerConnection. - * - * Responsible for: - * * offer-answer exchange - * * iceCandidates - * * notification of remote streams being added/removed - * - */ - OT.PeerConnection = function(config) { - var _peerConnection, - _peerConnectionCompletionHandlers = [], - _iceProcessor = new IceCandidateProcessor(), - _offer, - _answer, - _state = 'new', - _messageDelegates = []; + var method = event.data[prefix], + payload = event.data.payload; + if(payload && payload.requestId) { + var callback = callbackRegistry[payload.requestId]; + delete callbackRegistry[payload.requestId]; + if(callback) { + callback(null, method, payload); + } + } - OT.$.eventing(this); + if(method === 'extensionLoaded') { + isInstalled = true; + } + }); - // if ice servers doesn't exist Firefox will throw an exception. Chrome - // interprets this as 'Use my default STUN servers' whereas FF reads it - // as 'Don't use STUN at all'. *Grumble* - if (!config.iceServers) config.iceServers = []; - - // Private methods - var delegateMessage = OT.$.bind(function(type, messagePayload) { - if (_messageDelegates.length) { - // We actually only ever send to the first delegate. This is because - // each delegate actually represents a Publisher/Subscriber that - // shares a single PeerConnection. If we sent to all delegates it - // would result in each message being processed multiple times by - // each PeerConnection. - _messageDelegates[0](type, messagePayload); - } - }, this), + return { + isInstalled: isAvailable, + getConstraints: getConstraints + }; + } +}); - // Create and initialise the PeerConnection object. This deals with - // any differences between the various browser implementations and - // our own TBPlugin version. - // - // +completion+ is the function is call once we've either successfully - // created the PeerConnection or on failure. - // - // +localWebRtcStream+ will be null unless the callee is representing - // a publisher. This is an unfortunate implementation limitation - // of TBPlugin, it's not used for vanilla WebRTC. Hopefully this can - // be tidied up later. - // - createPeerConnection = OT.$.bind(function (completion, localWebRtcStream) { - if (_peerConnection) { - completion.call(null, null, _peerConnection); - return; - } +// tb_require('../helpers/helpers.js') +// tb_require('../helpers/lib/properties.js') +// tb_require('../helpers/lib/video_element.js') +// tb_require('./events.js') - _peerConnectionCompletionHandlers.push(completion); +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - if (_peerConnectionCompletionHandlers.length > 1) { - // The PeerConnection is already being setup, just wait for - // it to be ready. - return; - } - var pcConstraints = { - optional: [ - {DtlsSrtpKeyAgreement: true} - ] - }; +// id: String | mandatory | immutable +// type: String {video/audio/data/...} | mandatory | immutable +// active: Boolean | mandatory | mutable +// orientation: Integer? | optional | mutable +// frameRate: Float | optional | mutable +// height: Integer | optional | mutable +// width: Integer | optional | mutable +OT.StreamChannel = function(options) { + this.id = options.id; + this.type = options.type; + this.active = OT.$.castToBoolean(options.active); + this.orientation = options.orientation || OT.VideoOrientation.ROTATED_NORMAL; + if (options.frameRate) this.frameRate = parseFloat(options.frameRate, 10); + this.width = parseInt(options.width, 10); + this.height = parseInt(options.height, 10); + + // The defaults are used for incoming streams from pre 2015Q1 release clients. + this.source = options.source || 'camera'; + this.fitMode = options.fitMode || 'cover'; + + OT.$.eventing(this, true); + + // Returns true if a property was updated. + this.update = function(attributes) { + var videoDimensions = {}, + oldVideoDimensions = {}; - OT.debug('Creating peer connection config "' + JSON.stringify(config) + '".'); + for (var key in attributes) { + if(!attributes.hasOwnProperty(key)) { + continue; + } - if (!config.iceServers || config.iceServers.length === 0) { - // This should never happen unless something is misconfigured - OT.error('No ice servers present'); - } + // we shouldn't really read this before we know the key is valid + var oldValue = this[key]; - OT.$.createPeerConnection(config, pcConstraints, localWebRtcStream, - OT.$.bind(attachEventsToPeerConnection, this)); - }, this), + switch(key) { + case 'active': + this.active = OT.$.castToBoolean(attributes[key]); + break; - // An auxiliary function to createPeerConnection. This binds the various event callbacks - // once the peer connection is created. - // - // +err+ will be non-null if an err occured while creating the PeerConnection - // +pc+ will be the PeerConnection object itself. - // - attachEventsToPeerConnection = OT.$.bind(function(err, pc) { - if (err) { - triggerError('Failed to create PeerConnection, exception: ' + - err.toString(), 'NewPeerConnection'); + case 'disableWarning': + this.disableWarning = OT.$.castToBoolean(attributes[key]); + break; - _peerConnectionCompletionHandlers = []; - return; - } + case 'frameRate': + this.frameRate = parseFloat(attributes[key], 10); + break; - OT.debug('OT attachEventsToPeerConnection'); - _peerConnection = pc; + case 'width': + case 'height': + this[key] = parseInt(attributes[key], 10); - _peerConnection.onicecandidate = iceCandidateForwarder(delegateMessage); - _peerConnection.onaddstream = OT.$.bind(onRemoteStreamAdded, this); - _peerConnection.onremovestream = OT.$.bind(onRemoteStreamRemoved, this); - - if (_peerConnection.onsignalingstatechange !== undefined) { - _peerConnection.onsignalingstatechange = OT.$.bind(routeStateChanged, this); - } else if (_peerConnection.onstatechange !== undefined) { - _peerConnection.onstatechange = OT.$.bind(routeStateChanged, this); - } - - if (_peerConnection.oniceconnectionstatechange !== undefined) { - var failedStateTimer; - _peerConnection.oniceconnectionstatechange = function (event) { - if (event.target.iceConnectionState === 'failed') { - if (failedStateTimer) { - clearTimeout(failedStateTimer); - } - // We wait 5 seconds and make sure that it's still in the failed state - // before we trigger the error. This is because we sometimes see - // 'failed' and then 'connected' afterwards. - setTimeout(function () { - if (event.target.iceConnectionState === 'failed') { - triggerError('The stream was unable to connect due to a network error.' + - ' Make sure your connection isn\'t blocked by a firewall.', 'ICEWorkflow'); - } - }, 5000); - } - }; - } + videoDimensions[key] = this[key]; + oldVideoDimensions[key] = oldValue; + break; - triggerPeerConnectionCompletion(null); - }, this), + case 'orientation': + this[key] = attributes[key]; - triggerPeerConnectionCompletion = function () { - while (_peerConnectionCompletionHandlers.length) { - _peerConnectionCompletionHandlers.shift().call(null); - } - }, + videoDimensions[key] = this[key]; + oldVideoDimensions[key] = oldValue; + break; - // Clean up the Peer Connection and trigger the close event. - // This function can be called safely multiple times, it will - // only trigger the close event once (per PeerConnection object) - tearDownPeerConnection = function() { - // Our connection is dead, stop processing ICE candidates - if (_iceProcessor) _iceProcessor.setPeerConnection(null); - - qos.stopCollecting(); - - if (_peerConnection !== null) { - if (_peerConnection.destroy) { - // OTPlugin defines a destroy method on PCs. This allows - // the plugin to release any resources that it's holding. - _peerConnection.destroy(); - } + case 'fitMode': + this[key] = attributes[key]; + break; - _peerConnection = null; - this.trigger('close'); - } - }, + case 'source': + this[key] = attributes[key]; + break; - routeStateChanged = function(event) { - var newState; + default: + OT.warn('Tried to update unknown key ' + key + ' on ' + this.type + + ' channel ' + this.id); + return; + } - if (typeof(event) === 'string') { - // The newest version of the API - newState = event; - - } else if (event.target && event.target.signalingState) { - // The slightly older version - newState = event.target.signalingState; + this.trigger('update', this, key, oldValue, this[key]); + } - } else { - // At least six months old version. Positively ancient, yeah? - newState = event.target.readyState; - } + if (OT.$.keys(videoDimensions).length) { + // To make things easier for the public API, we broadcast videoDimensions changes, + // which is an aggregate of width, height, and orientation changes. + this.trigger('update', this, 'videoDimensions', oldVideoDimensions, videoDimensions); + } - if (newState && newState.toLowerCase() !== _state) { - _state = newState.toLowerCase(); - OT.debug('PeerConnection.stateChange: ' + _state); - - switch(_state) { - case 'closed': - tearDownPeerConnection.call(this); - break; - } - } - }, + return true; + }; +}; - qosCallback = OT.$.bind(function(parsedStats) { - this.trigger('qos', parsedStats); - }, this), +// tb_require('../helpers/helpers.js') +// tb_require('../helpers/lib/properties.js') +// tb_require('./events.js') +// tb_require('./stream_channel.js') - getRemoteStreams = function() { - var streams; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - if (_peerConnection.getRemoteStreams) { - streams = _peerConnection.getRemoteStreams(); - } else if (_peerConnection.remoteStreams) { - streams = _peerConnection.remoteStreams; - } else { - throw new Error('Invalid Peer Connection object implements no ' + - 'method for retrieving remote streams'); - } +!(function() { - // Force streams to be an Array, rather than a 'Sequence' object, - // which is browser dependent and does not behaviour like an Array - // in every case. - return Array.prototype.slice.call(streams); - }, + var validPropertyNames = ['name', 'archiving']; - /// PeerConnection signaling - onRemoteStreamAdded = function(event) { - this.trigger('streamAdded', event.stream); - }, +/** + * Specifies a stream. A stream is a representation of a published stream in a session. When a + * client calls the Session.publish() method, a new stream is + * created. Properties of the Stream object provide information about the stream. + * + *

    When a stream is added to a session, the Session object dispatches a + * streamCreatedEvent. When a stream is destroyed, the Session object dispatches a + * streamDestroyed event. The StreamEvent object, which defines these event objects, + * has a stream property, which is an array of Stream object. For details and a code + * example, see {@link StreamEvent}.

    + * + *

    When a connection to a session is made, the Session object dispatches a + * sessionConnected event, defined by the SessionConnectEvent object. The + * SessionConnectEvent object has a streams property, which is an array of Stream + * objects pertaining to the streams in the session at that time. For details and a code example, + * see {@link SessionConnectEvent}.

    + * + * @class Stream + * @property {Connection} connection The Connection object corresponding + * to the connection that is publishing the stream. You can compare this to to the + * connection property of the Session object to see if the stream is being published + * by the local web page. + * + * @property {Number} creationTime The timestamp for the creation + * of the stream. This value is calculated in milliseconds. You can convert this value to a + * Date object by calling new Date(creationTime), where creationTime is + * the creationTime property of the Stream object. + * + * @property {Number} frameRate The frame rate of the video stream. This property is only set if the + * publisher of the stream specifies a frame rate when calling the OT.initPublisher() + * method; otherwise, this property is undefined. + * + * @property {Boolean} hasAudio Whether the stream has audio. This property can change if the + * publisher turns on or off audio (by calling + * Publisher.publishAudio()). When this occurs, the + * {@link Session} object dispatches a streamPropertyChanged event (see + * {@link StreamPropertyChangedEvent}). + * + * @property {Boolean} hasVideo Whether the stream has video. This property can change if the + * publisher turns on or off video (by calling + * Publisher.publishVideo()). When this occurs, the + * {@link Session} object dispatches a streamPropertyChanged event (see + * {@link StreamPropertyChangedEvent}). + * + * @property {String} name The name of the stream. Publishers can specify a name when publishing + * a stream (using the publish() method of the publisher's Session object). + * + * @property {String} streamId The unique ID of the stream. + * + * @property {Object} videoDimensions This object has two properties: width and + * height. Both are numbers. The width property is the width of the + * encoded stream; the height property is the height of the encoded stream. (These + * are independent of the actual width of Publisher and Subscriber objects corresponding to the + * stream.) This property can change if a stream published from a mobile device resizes, based on + * a change in the device orientation. It can also occur if the video source is a screen-sharing + * window and the user publishing the stream resizes the window. When the video dimensions change, + * the {@link Session} object dispatches a streamPropertyChanged event + * (see {@link StreamPropertyChangedEvent}). + * + * @property {String} videoType The type of video — either "camera" or + * "screen". A "screen" video uses screen sharing on the publisher + * as the video source; for other videos, this property is set to "camera". + * This property can change if a stream published from a mobile device changes from a + * camera to a screen-sharing video type. When the video type changes, the {@link Session} object + * dispatches a streamPropertyChanged event (see {@link StreamPropertyChangedEvent}). + */ - onRemoteStreamRemoved = function(event) { - this.trigger('streamRemoved', event.stream); - }, - // ICE Negotiation messages + OT.Stream = function(id, name, creationTime, connection, session, channel) { + var destroyedReason; + this.id = this.streamId = id; + this.name = name; + this.creationTime = Number(creationTime); - // Relays a SDP payload (+sdp+), that is part of a message of type +messageType+ - // via the registered message delegators - relaySDP = function(messageType, sdp) { - delegateMessage(messageType, sdp); - }, + this.connection = connection; + this.channel = channel; + this.publisher = OT.publishers.find({streamId: this.id}); + OT.$.eventing(this); - // Process an offer that - processOffer = function(message) { - var offer = new NativeRTCSessionDescription({type: 'offer', sdp: message.content.sdp}), - - // Relays +answer+ Answer - relayAnswer = function(answer) { - _iceProcessor.setPeerConnection(_peerConnection); - _iceProcessor.processPending(); - relaySDP(OT.Raptor.Actions.ANSWER, answer); - - qos.startCollecting(_peerConnection); - }, - - reportError = function(message, errorReason, prefix) { - triggerError('PeerConnection.offerProcessor ' + message + ': ' + - errorReason, prefix); - }; + var onChannelUpdate = OT.$.bind(function(channel, key, oldValue, newValue) { + var _key = key; - createPeerConnection(function() { - offerProcessor( - _peerConnection, - offer, - relayAnswer, - reportError - ); - }); - }, + switch(_key) { + case 'active': + _key = channel.type === 'audio' ? 'hasAudio' : 'hasVideo'; + this[_key] = newValue; + break; - processAnswer = function(message) { - if (!message.content.sdp) { - OT.error('PeerConnection.processMessage: Weird answer message, no SDP.'); - return; + case 'disableWarning': + _key = channel.type === 'audio' ? 'audioDisableWarning': 'videoDisableWarning'; + this[_key] = newValue; + if (!this[channel.type === 'audio' ? 'hasAudio' : 'hasVideo']) { + return; // Do NOT event in this case. } - - _answer = new NativeRTCSessionDescription({type: 'answer', sdp: message.content.sdp}); - - _peerConnection.setRemoteDescription(_answer, - function () { - OT.debug('setRemoteDescription Success'); - }, function (errorReason) { - triggerError('Error while setting RemoteDescription ' + errorReason, - 'SetRemoteDescription'); - }); - - _iceProcessor.setPeerConnection(_peerConnection); - _iceProcessor.processPending(); - - qos.startCollecting(_peerConnection); - }, - - processSubscribe = function() { - OT.debug('PeerConnection.processSubscribe: Sending offer to subscriber.'); - - if (!_peerConnection) { - // TODO(rolly) I need to examine whether this can - // actually happen. If it does happen in the short - // term, I want it to be noisy. - throw new Error('PeerConnection broke!'); - } - - createPeerConnection(function() { - suscribeProcessor( - _peerConnection, - - // Success: Relay Offer - function(offer) { - _offer = offer; - relaySDP(OT.Raptor.Actions.OFFER, _offer); - }, - - // Failure - function(message, errorReason, prefix) { - triggerError('PeerConnection.suscribeProcessor ' + message + ': ' + - errorReason, prefix); - } - ); - }); - }, - - triggerError = OT.$.bind(function(errorReason, prefix) { - OT.error(errorReason); - this.trigger('error', errorReason, prefix); - }, this); - - this.addLocalStream = function(webRTCStream) { - createPeerConnection(function() { - _peerConnection.addStream(webRTCStream); - }, webRTCStream); - }; - - this.disconnect = function() { - _iceProcessor = null; - - if (_peerConnection) { - var currentState = (_peerConnection.signalingState || _peerConnection.readyState); - if (currentState && currentState.toLowerCase() !== 'closed') _peerConnection.close(); - - // In theory, calling close on the PeerConnection should trigger a statechange - // event with 'close'. For some reason I'm not seeing this in FF, hence we're - // calling it manually below - tearDownPeerConnection.call(this); - } - - this.off(); - }; - - this.processMessage = function(type, message) { - OT.debug('PeerConnection.processMessage: Received ' + - type + ' from ' + message.fromAddress); - - OT.debug(message); - - switch(type) { - case 'generateoffer': - processSubscribe.call(this, message); break; - case 'offer': - processOffer.call(this, message); + case 'fitMode': + _key = 'defaultFitMode'; + this[_key] = newValue; break; - case 'answer': - case 'pranswer': - processAnswer.call(this, message); + case 'source': + _key = channel.type === 'audio' ? 'audioType' : 'videoType'; + this[_key] = newValue; break; - case 'candidate': - _iceProcessor.process(message); - break; + case 'orientation': + case 'width': + case 'height': + this.videoDimensions = { + width: channel.width, + height: channel.height, + orientation: channel.orientation + }; - default: - OT.debug('PeerConnection.processMessage: Received an unexpected message of type ' + - type + ' from ' + message.fromAddress + ': ' + JSON.stringify(message)); + // We dispatch this via the videoDimensions key instead + return; } - return this; - }; + this.dispatchEvent( new OT.StreamUpdatedEvent(this, _key, oldValue, newValue) ); + }, this); - this.setIceServers = function (iceServers) { - if (iceServers) { - config.iceServers = iceServers; + var associatedWidget = OT.$.bind(function() { + if(this.publisher) { + return this.publisher; + } else { + return OT.subscribers.find(function(subscriber) { + return subscriber.stream.id === this.id && + subscriber.session.id === session.id; + }); } - }; + }, this); - this.registerMessageDelegate = function(delegateFn) { - return _messageDelegates.push(delegateFn); + // Returns all channels that have a type of +type+. + this.getChannelsOfType = function (type) { + return OT.$.filter(this.channel, function(channel) { + return channel.type === type; + }); }; - this.unregisterMessageDelegate = function(delegateFn) { - var index = OT.$.arrayIndexOf(_messageDelegates, delegateFn); - - if ( index !== -1 ) { - _messageDelegates.splice(index, 1); + this.getChannel = function (id) { + for (var i=0; i= 27.0) { - parseQOS = parseStatsNewAPI; - return parseStatsNewAPI(peerConnection, prevStats, currentStats, completion); + if(OT.$.env.name === 'IE' && OT.$.env.version < 10) { + sessionInfoURL = sessionInfoURL + '&format=json&token=' + encodeURIComponent(session.token) + + '&version=1&cache=' + OT.$.uuid(); + options = { + xdomainrequest: true + }; } else { - parseQOS = parseStatsOldAPI; - return parseStatsOldAPI(peerConnection, prevStats, currentStats, completion); + options = { + headers: { + 'X-TB-TOKEN-AUTH': session.token, + 'X-TB-VERSION': 1 + } + }; } + session.logEvent('SessionInfo', 'Attempt', {messagingServer: OT.properties.apiURL}); + + OT.$.getJSON(sessionInfoURL, options, function(error, sessionInfo) { + if(error) { + var responseText = sessionInfo; + onGetErrorCallback(session, onFailure, + new OT.Error(error.target && error.target.status || error.code, error.message || + 'Could not connect to the OpenTok API Server.'), responseText); + } else { + validateRawSessionInfo(sessionInfo); + } + }); }; - OT.PeerConnection.QOS = function (qosCallback) { - var _creationTime = OT.$.now(), - _peerConnection; + var messageServerToClientErrorCodes = {}; + messageServerToClientErrorCodes['404'] = OT.ExceptionCodes.INVALID_SESSION_ID; + messageServerToClientErrorCodes['409'] = OT.ExceptionCodes.TERMS_OF_SERVICE_FAILURE; + messageServerToClientErrorCodes['400'] = OT.ExceptionCodes.INVALID_SESSION_ID; + messageServerToClientErrorCodes['403'] = OT.ExceptionCodes.AUTHENTICATION_ERROR; - var calculateQOS = OT.$.bind(function calculateQOS (prevStats) { - if (!_peerConnection) { - // We don't have a PeerConnection yet, or we did and - // it's been closed. Either way we're done. - return; + // Return the error in +jsonDocument+, if there is one. Otherwise it will return + // false. + parseErrorFromJSONDocument = function(jsonDocument) { + if(OT.$.isArray(jsonDocument)) { + + var errors = OT.$.filter(jsonDocument, function(node) { + return node.error != null; + }); + + var numErrorNodes = errors.length; + if(numErrorNodes === 0) { + return false; + } + + var errorCode = errors[0].error.code; + if (messageServerToClientErrorCodes[errorCode.toString()]) { + errorCode = messageServerToClientErrorCodes[errorCode]; } - var now = OT.$.now(); + return { + code: errorCode, + message: errors[0].error.errorMessage && errors[0].error.errorMessage.message + }; + } else { + return { + code: null, + message: 'Unknown error: getSessionInfo JSON response was badly formed' + }; + } + }; + + onGetResponseCallback = function(session, onSuccess, rawSessionInfo) { + session.logEvent('SessionInfo', 'Success', {messagingServer: OT.properties.apiURL}); - var currentStats = { - timeStamp: now, - duration: Math.round(now - _creationTime), - deltaSecs: (now - prevStats.timeStamp) / 1000 - }; + onSuccess( new OT.SessionInfo(rawSessionInfo) ); + }; - var onParsedStats = function (err, parsedStats) { - if (err) { - OT.error('Failed to Parse QOS Stats: ' + JSON.stringify(err)); - return; - } + onGetErrorCallback = function(session, onFailure, error, responseText) { + var payload = { + reason:'GetSessionInfo', + code: (error.code || 'No code'), + message: error.message + ':' + + (responseText || 'Empty responseText from API server') + }; + session.logConnectivityEvent('Failure', payload); - qosCallback(parsedStats, prevStats); + onFailure(error, session); + }; - // Recalculate the stats - setTimeout(OT.$.bind(calculateQOS, null, parsedStats), OT.PeerConnection.QOS.INTERVAL); - }; +})(window); - parseQOS(_peerConnection, prevStats, currentStats, onParsedStats); - }, this); +// tb_require('../helpers/helpers.js') +// tb_require('../helpers/lib/properties.js') +!(function() { + /* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ + /* global OT */ - this.startCollecting = function (peerConnection) { - if (!peerConnection || !peerConnection.getStats) { - // It looks like this browser doesn't support getStats - // Bail. - return; - } + /** + * The Error class is used to define the error object passed into completion handlers. + * Each of the following methods, which execute asynchronously, includes a + * completionHandler parameter: + * + * + * + *

    + * The completionHandler parameter is a function that is called when the call to + * the asynchronous method succeeds or fails. If the asynchronous call fails, the completion + * handler function is passes an error object (defined by the Error class). The code + * and message properties of the error object provide details about the error. + * + * @property {Number} code The error code, defining the error. + * + *

    + * In the event of an error, the code value of the error parameter can + * have one of the following values: + *

    + * + *

    Errors when calling Session.connect():

    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    codeDescription
    1004Authentication error. Check the error message for details. This error can result if you + * in an expired token when trying to connect to a session. It can also occur if you pass + * in an invalid token or API key. Make sure that you are generating the token using the + * current version of one of the + * OpenTok server SDKs.
    1005Invalid Session ID. Make sure you generate the session ID using the current version of + * one of the OpenTok server + * SDKs.
    1006Connect Failed. Unable to connect to the session. You may want to have the client check + * the network connection.
    + * + *

    Errors when calling Session.forceDisconnect():

    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    + * code + * Description
    1010The client is not connected to the OpenTok session. Check that client connects + * successfully and has not disconnected before calling forceDisconnect().
    1520Unable to force disconnect. The client's token does not have the role set to moderator. + * Once the client has connected to the session, the capabilities property of + * the Session object lists the client's capabilities.
    + * + *

    Errors when calling Session.forceUnpublish():

    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    codeDescription
    1010The client is not connected to the OpenTok session. Check that client connects + * successfully and has not disconnected before calling forceUnpublish().
    1530Unable to force unpublish. The client's token does not have the role set to moderator. + * Once the client has connected to the session, the capabilities property of + * the Session object lists the client's capabilities.
    1535Force Unpublish on an invalid stream. Make sure that the stream has not left the + * session before you call the forceUnpublish() method.
    + * + *

    Errors when calling Session.publish():

    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    codeDescription
    1010The client is not connected to the OpenTok session. Check that the client connects + * successfully before trying to publish. And check that the client has not disconnected + * before trying to publish.
    1500Unable to Publish. The client's token does not have the role set to to publish or + * moderator. Once the client has connected to the session, the capabilities + * property of the Session object lists the client's capabilities.
    1601Internal error -- WebRTC publisher error. Try republishing or reconnecting to the + * session.
    + * + *

    Errors when calling Session.signal():

    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    codeDescription
    400One of the signal properties — data, type, or to — + * is invalid. Or the data cannot be parsed as JSON.
    404 The to connection does not exist.
    413 The type string exceeds the maximum length (128 bytes), + * or the data string exceeds the maximum size (8 kB).
    500The client is not connected to the OpenTok session. Check that the client connects + * successfully before trying to signal. And check that the client has not disconnected before + * trying to publish.
    + * + *

    Errors when calling Session.subscribe():

    + * + * + * + * + * + * + * + * + * + * + *
    + * code + * Description
    1600Internal error -- WebRTC subscriber error. Try resubscribing to the stream or + * reconnecting to the session.
    + * + *

    Errors when calling OT.initPublisher():

    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    codeDescription
    1004Authentication error. Check the error message for details. This error can result if you + * pass in an expired token when trying to connect to a session. It can also occur if you + * pass in an invalid token or API key. Make sure that you are generating the token using + * the current version of one of the + * OpenTok server SDKs.
    1550Screen sharing is not supported (and you set the videoSource property + * of the options parameter of OT.initPublisher() to + * "screen"). Before calling OT.initPublisher(), you can call + * OT.checkScreenSharingCapability() + * to check if screen sharing is supported.
    1551A screen sharing extension needs to be registered but it is not. This error can occur + * when you set the videoSource property of the options parameter + * of OT.initPublisher() to "screen". Before calling + * OT.initPublisher(), you can call + * OT.checkScreenSharingCapability() + * to check if screen sharing requires an extension to be registered.
    1552A screen sharing extension is required, but it is not installed. This error can occur + * when you set the videoSource property of the options parameter + * of OT.initPublisher() to "screen". Before calling + * OT.initPublisher(), you can call + * OT.checkScreenSharingCapability() + * to check if screen sharing requires an extension to be installed.
    + * + *

    General errors that can occur when calling any method:

    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    codeDescription
    1011Invalid Parameter. Check that you have passed valid parameter values into the method + * call.
    2000Internal Error. Try reconnecting to the OpenTok session and trying the action again.
    + * + * @property {String} message The message string provides details about the error. + * + * @class Error + * @augments Event + */ + OT.Error = function(code, message) { + this.code = code; + this.message = message; + }; - _peerConnection = peerConnection; + var errorsCodesToTitle = { + 1004: 'Authentication error', + 1005: 'Invalid Session ID', + 1006: 'Connect Failed', + 1007: 'Connect Rejected', + 1008: 'Connect Time-out', + 1009: 'Security Error', + 1010: 'Not Connected', + 1011: 'Invalid Parameter', + 1012: 'Peer-to-peer Stream Play Failed', + 1013: 'Connection Failed', + 1014: 'API Response Failure', + 1015: 'Session connected, cannot test network', + 1021: 'Request Timeout', + 1026: 'Terms of Service Violation: Export Compliance', + 1500: 'Unable to Publish', + 1503: 'No TURN server found', + 1520: 'Unable to Force Disconnect', + 1530: 'Unable to Force Unpublish', + 1553: 'ICEWorkflow failed', + 1600: 'createOffer, createAnswer, setLocalDescription, setRemoteDescription', + 2000: 'Internal Error', + 2001: 'Unexpected HTTP error codes (f.e. 500)', + 4000: 'WebSocket Connection Failed', + 4001: 'WebSocket Network Disconnected' + }; - calculateQOS({ - timeStamp: OT.$.now() - }); - }; + function _exceptionHandler(component, msg, errorCode, context) { + var title = errorsCodesToTitle[errorCode], + contextCopy = context ? OT.$.clone(context) : {}; - this.stopCollecting = function () { - _peerConnection = null; - }; - }; + OT.error('OT.exception :: title: ' + title + ' (' + errorCode + ') msg: ' + msg); - // Recalculate the stats in 30 sec - OT.PeerConnection.QOS.INTERVAL = 30000; -})(); -!(function() { + if (!contextCopy.partnerId) contextCopy.partnerId = OT.APIKEY; - var _peerConnections = {}; + try { + OT.analytics.logError(errorCode, 'tb.exception', title, {details:msg}, contextCopy); - OT.PeerConnections = { - add: function(remoteConnection, streamId, config) { - var key = remoteConnection.id + '_' + streamId, - ref = _peerConnections[key]; + OT.dispatchEvent( + new OT.ExceptionEvent(OT.Event.names.EXCEPTION, msg, title, errorCode, component, component) + ); + } catch(err) { + OT.error('OT.exception :: Failed to dispatch exception - ' + err.toString()); + // Don't throw an error because this is asynchronous + // don't do an exceptionHandler because that would be recursive + } + } - if (!ref) { - ref = _peerConnections[key] = { - count: 0, - pc: new OT.PeerConnection(config) - }; - } +// @todo redo this when we have time to tidy up +// +// @example +// +// OT.handleJsException("Descriptive error message", 2000, { +// session: session, +// target: stream|publisher|subscriber|session|etc +// }); +// + OT.handleJsException = function(errorMsg, code, options) { + options = options || {}; - // increase the PCs ref count by 1 - ref.count += 1; + var context, + session = options.session; - return ref.pc; - }, + if (session) { + context = { + sessionId: session.sessionId + }; - remove: function(remoteConnection, streamId) { - var key = remoteConnection.id + '_' + streamId, - ref = _peerConnections[key]; + if (session.isConnected()) context.connectionId = session.connection.connectionId; + if (!options.target) options.target = session; - if (ref) { - ref.count -= 1; + } else if (options.sessionId) { + context = { + sessionId: options.sessionId + }; - if (ref.count === 0) { - ref.pc.disconnect(); - delete _peerConnections[key]; - } - } + if (!options.target) options.target = null; } - }; - -})(window); -!(function() { - /* - * Abstracts PeerConnection related stuff away from OT.Publisher. - * - * Responsible for: - * * setting up the underlying PeerConnection (delegates to OT.PeerConnections) - * * triggering a connected event when the Peer connection is opened - * * triggering a disconnected event when the Peer connection is closed - * * providing a destroy method - * * providing a processMessage method - * - * Once the PeerConnection is connected and the video element playing it triggers - * the connected event - * - * Triggers the following events - * * connected - * * disconnected - */ - OT.PublisherPeerConnection = function(remoteConnection, session, streamId, webRTCStream) { - var _peerConnection, - _hasRelayCandidates = false, - _subscriberId = session._.subscriberMap[remoteConnection.id + '_' + streamId], - _onPeerClosed, - _onPeerError, - _relayMessageToPeer, - _onQOS; + _exceptionHandler(options.target, errorMsg, code, context); + }; - // Private - _onPeerClosed = function() { - this.destroy(); - this.trigger('disconnected', this); - }; + // This is a placeholder until error handling can be rewritten + OT.dispatchError = function (code, message, completionHandler, session) { + OT.error(code, message); - // Note: All Peer errors are fatal right now. - _onPeerError = function(errorReason, prefix) { - this.trigger('error', null, errorReason, this, prefix); - this.destroy(); - }; + if (completionHandler && OT.$.isFunction(completionHandler)) { + completionHandler.call(null, new OT.Error(code, message)); + } - _relayMessageToPeer = OT.$.bind(function(type, payload) { - if (!_hasRelayCandidates){ - var extractCandidates = type === OT.Raptor.Actions.CANDIDATE || - type === OT.Raptor.Actions.OFFER || - type === OT.Raptor.Actions.ANSWER || - type === OT.Raptor.Actions.PRANSWER ; - - if (extractCandidates) { - var message = (type === OT.Raptor.Actions.CANDIDATE) ? payload.candidate : payload.sdp; - _hasRelayCandidates = message.indexOf('typ relay') !== -1; - } - } + OT.handleJsException(message, code, { + session: session + }); + }; - switch(type) { - case OT.Raptor.Actions.ANSWER: - case OT.Raptor.Actions.PRANSWER: - if (session.sessionInfo.p2pEnabled) { - session._.jsepAnswerP2p(streamId, _subscriberId, payload.sdp); - } else { - session._.jsepAnswer(streamId, payload.sdp); - } +})(window); - break; +// tb_require('../helpers/helpers.js') +// tb_require('../helpers/lib/config.js') +// tb_require('./events.js') - case OT.Raptor.Actions.OFFER: - this.trigger('connected'); +!(function() { - if (session.sessionInfo.p2pEnabled) { - session._.jsepOfferP2p(streamId, _subscriberId, payload.sdp); + /* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ + /* global OT */ - } else { - session._.jsepOffer(streamId, payload.sdp); - } + // Helper to synchronise several startup tasks and then dispatch a unified + // 'envLoaded' event. + // + // This depends on: + // * OT + // * OT.Config + // + function EnvironmentLoader() { + var _configReady = false, - break; + // If the plugin is installed, then we should wait for it to + // be ready as well. + _pluginSupported = OTPlugin.isSupported(), + _pluginLoadAttemptComplete = _pluginSupported ? OTPlugin.isReady() : true, - case OT.Raptor.Actions.CANDIDATE: - if (session.sessionInfo.p2pEnabled) { - session._.jsepCandidateP2p(streamId, _subscriberId, payload); + isReady = function() { + return !OT.$.isDOMUnloaded() && OT.$.isReady() && + _configReady && _pluginLoadAttemptComplete; + }, - } else { - session._.jsepCandidate(streamId, payload); + onLoaded = function() { + if (isReady()) { + OT.dispatchEvent(new OT.EnvLoadedEvent(OT.Event.names.ENV_LOADED)); } - } - }, this); - - _onQOS = OT.$.bind(function _onQOS (parsedStats, prevStats) { - this.trigger('qos', remoteConnection, parsedStats, prevStats); - }, this); - - OT.$.eventing(this); - - // Public - this.destroy = function() { - // Clean up our PeerConnection - if (_peerConnection) { - _peerConnection.off(); - OT.PeerConnections.remove(remoteConnection, streamId); - } - - _peerConnection = null; - }; + }, - this.processMessage = function(type, message) { - _peerConnection.processMessage(type, message); - }; - // Init - this.init = function(iceServers) { - _peerConnection = OT.PeerConnections.add(remoteConnection, streamId, { - iceServers: iceServers - }); + onDomReady = function() { + OT.$.onDOMUnload(onDomUnload); - _peerConnection.on({ - close: _onPeerClosed, - error: _onPeerError, - qos: _onQOS - }, this); + // The Dynamic Config won't load until the DOM is ready + OT.Config.load(OT.properties.configURL); - _peerConnection.registerMessageDelegate(_relayMessageToPeer); - _peerConnection.addLocalStream(webRTCStream); + onLoaded(); + }, - this.remoteConnection = function() { - return remoteConnection; - }; + onDomUnload = function() { + OT.dispatchEvent(new OT.EnvLoadedEvent(OT.Event.names.ENV_UNLOADED)); + }, - this.hasRelayCandidates = function() { - return _hasRelayCandidates; - }; + onPluginReady = function(err) { + // We mark the plugin as ready so as not to stall the environment + // loader. In this case though, OTPlugin is not supported. + _pluginLoadAttemptComplete = true; - }; - }; + if (err) { + OT.debug('TB Plugin failed to load or was not installed'); + } -})(window); -!(function() { + onLoaded(); + }, - /* - * Abstracts PeerConnection related stuff away from OT.Subscriber. - * - * Responsible for: - * * setting up the underlying PeerConnection (delegates to OT.PeerConnections) - * * triggering a connected event when the Peer connection is opened - * * triggering a disconnected event when the Peer connection is closed - * * creating a video element when a stream is added - * * responding to stream removed intelligently - * * providing a destroy method - * * providing a processMessage method - * - * Once the PeerConnection is connected and the video element playing it - * triggers the connected event - * - * Triggers the following events - * * connected - * * disconnected - * * remoteStreamAdded - * * remoteStreamRemoved - * * error - * - */ + configLoaded = function() { + _configReady = true; + OT.Config.off('dynamicConfigChanged', configLoaded); + OT.Config.off('dynamicConfigLoadFailed', configLoadFailed); - OT.SubscriberPeerConnection = function(remoteConnection, session, stream, - subscriber, properties) { - var _peerConnection, - _destroyed = false, - _hasRelayCandidates = false, - _onPeerClosed, - _onRemoteStreamAdded, - _onRemoteStreamRemoved, - _onPeerError, - _relayMessageToPeer, - _setEnabledOnStreamTracksCurry, - _onQOS; + onLoaded(); + }, - // Private - _onPeerClosed = function() { - this.destroy(); - this.trigger('disconnected', this); - }; + configLoadFailed = function() { + configLoaded(); + }; - _onRemoteStreamAdded = function(remoteRTCStream) { - this.trigger('remoteStreamAdded', remoteRTCStream, this); - }; - _onRemoteStreamRemoved = function(remoteRTCStream) { - this.trigger('remoteStreamRemoved', remoteRTCStream, this); - }; + OT.Config.on('dynamicConfigChanged', configLoaded); + OT.Config.on('dynamicConfigLoadFailed', configLoadFailed); - // Note: All Peer errors are fatal right now. - _onPeerError = function(errorReason, prefix) { - this.trigger('error', errorReason, this, prefix); - }; + OT.$.onDOMLoad(onDomReady); - _relayMessageToPeer = OT.$.bind(function(type, payload) { - if (!_hasRelayCandidates){ - var extractCandidates = type === OT.Raptor.Actions.CANDIDATE || - type === OT.Raptor.Actions.OFFER || - type === OT.Raptor.Actions.ANSWER || - type === OT.Raptor.Actions.PRANSWER ; + // If the plugin should work on this platform then + // see if it loads. + if (_pluginSupported) OTPlugin.ready(onPluginReady); - if (extractCandidates) { - var message = (type === OT.Raptor.Actions.CANDIDATE) ? payload.candidate : payload.sdp; - _hasRelayCandidates = message.indexOf('typ relay') !== -1; - } + this.onLoad = function(cb, context) { + if (isReady()) { + cb.call(context); + return; } - switch(type) { - case OT.Raptor.Actions.ANSWER: - case OT.Raptor.Actions.PRANSWER: - this.trigger('connected'); - - session._.jsepAnswerP2p(stream.id, subscriber.widgetId, payload.sdp); - break; - - case OT.Raptor.Actions.OFFER: - session._.jsepOfferP2p(stream.id, subscriber.widgetId, payload.sdp); - break; + OT.on(OT.Event.names.ENV_LOADED, cb, context); + }; - case OT.Raptor.Actions.CANDIDATE: - session._.jsepCandidateP2p(stream.id, subscriber.widgetId, payload); - break; + this.onUnload = function(cb, context) { + if (this.isUnloaded()) { + cb.call(context); + return; } - }, this); - - // Helper method used by subscribeToAudio/subscribeToVideo - _setEnabledOnStreamTracksCurry = function(isVideo) { - var method = 'get' + (isVideo ? 'Video' : 'Audio') + 'Tracks'; - - return function(enabled) { - var remoteStreams = _peerConnection.remoteStreams(), - tracks, - stream; - - if (remoteStreams.length === 0 || !remoteStreams[0][method]) { - // either there is no remote stream or we are in a browser that doesn't - // expose the media tracks (Firefox) - return; - } - for (var i=0, num=remoteStreams.length; iOT.upgradeSystemRequirements() + * @method OT.checkSystemRequirements + * @memberof OT + */ +OT.checkSystemRequirements = function() { + OT.debug('OT.checkSystemRequirements()'); - // If there are already remoteStreams, add them immediately - if (_peerConnection.remoteStreams().length > 0) { - OT.$.forEach(_peerConnection.remoteStreams(), _onRemoteStreamAdded, this); - } else if (numDelegates === 1) { - // We only bother with the PeerConnection negotiation if we don't already - // have a remote stream. - - var channelsToSubscribeTo; - - if (properties.subscribeToVideo || properties.subscribeToAudio) { - var audio = stream.getChannelsOfType('audio'), - video = stream.getChannelsOfType('video'); - - channelsToSubscribeTo = OT.$.map(audio, function(channel) { - return { - id: channel.id, - type: channel.type, - active: properties.subscribeToAudio - }; - }).concat(OT.$.map(video, function(channel) { - return { - id: channel.id, - type: channel.type, - active: properties.subscribeToVideo, - restrictFrameRate: properties.restrictFrameRate !== void 0 ? - properties.restrictFrameRate : false - }; - })); - } + // Try native support first, then OTPlugin... + var systemRequirementsMet = OT.$.hasCapabilities('websockets', 'webrtc') || + OTPlugin.isInstalled(); - session._.subscriberCreate(stream, subscriber, channelsToSubscribeTo, - OT.$.bind(function(err, message) { - if (err) { - this.trigger('error', err.message, this, 'Subscribe'); - } - _peerConnection.setIceServers(OT.Raptor.parseIceServers(message)); - }, this)); - } - }; + systemRequirementsMet = systemRequirementsMet ? + this.HAS_REQUIREMENTS : this.NOT_HAS_REQUIREMENTS; - this.getStatsWithSingleParameter = function(callback) { - if(_peerConnection) { - _peerConnection.getStatsWithSingleParameter(callback); - } - }; + OT.checkSystemRequirements = function() { + OT.debug('OT.checkSystemRequirements()'); + return systemRequirementsMet; }; -})(window); -!(function() { - -// Manages N Chrome elements - OT.Chrome = function(properties) { - var _visible = false, - _widgets = {}, - - // Private helper function - _set = function(name, widget) { - widget.parent = this; - widget.appendTo(properties.parent); + if(systemRequirementsMet === this.NOT_HAS_REQUIREMENTS) { + OT.analytics.logEvent({ + action: 'checkSystemRequirements', + variation: 'notHasRequirements', + partnerId: OT.APIKEY, + payload: {userAgent: OT.$.env.userAgent} + }); + } - _widgets[name] = widget; + return systemRequirementsMet; +}; - this[name] = widget; - }; - if (!properties.parent) { - // @todo raise an exception +/** + * Displays information about system requirments for OpenTok for WebRTC. This + * information is displayed in an iframe element that fills the browser window. + *

    + * Note: this information is displayed automatically when you call the + * OT.initSession() or the OT.initPublisher() method + * if the client does not support OpenTok for WebRTC. + *

    + * @see OT.checkSystemRequirements() + * @method OT.upgradeSystemRequirements + * @memberof OT + */ +OT.upgradeSystemRequirements = function(){ + // trigger after the OT environment has loaded + OT.onLoad( function() { + + if(OTPlugin.isSupported()) { + OT.Dialogs.Plugin.promptToInstall().on({ + download: function() { + window.location = OTPlugin.pathToInstaller(); + }, + refresh: function() { + location.reload(); + }, + closed: function() {} + }); return; } - OT.$.eventing(this); - - this.destroy = function() { - this.off(); - this.hide(); + var id = '_upgradeFlash'; - for (var name in _widgets) { - _widgets[name].destroy(); + // Load the iframe over the whole page. + document.body.appendChild((function() { + var d = document.createElement('iframe'); + d.id = id; + d.style.position = 'absolute'; + d.style.position = 'fixed'; + d.style.height = '100%'; + d.style.width = '100%'; + d.style.top = '0px'; + d.style.left = '0px'; + d.style.right = '0px'; + d.style.bottom = '0px'; + d.style.zIndex = 1000; + try { + d.style.backgroundColor = 'rgba(0,0,0,0.2)'; + } catch (err) { + // Old IE browsers don't support rgba and we still want to show the upgrade message + // but we just make the background of the iframe completely transparent. + d.style.backgroundColor = 'transparent'; + d.setAttribute('allowTransparency', 'true'); + } + d.setAttribute('frameBorder', '0'); + d.frameBorder = '0'; + d.scrolling = 'no'; + d.setAttribute('scrolling', 'no'); + + var minimumBrowserVersion = OT.properties.minimumVersion[OT.$.env.name.toLowerCase()], + isSupportedButOld = minimumBrowserVersion > OT.$.env.version; + d.src = OT.properties.assetURL + '/html/upgrade.html#' + + encodeURIComponent(isSupportedButOld ? 'true' : 'false') + ',' + + encodeURIComponent(JSON.stringify(OT.properties.minimumVersion)) + '|' + + encodeURIComponent(document.location.href); + + return d; + })()); + + // Now we need to listen to the event handler if the user closes this dialog. + // Since this is from an IFRAME within another domain we are going to listen to hash + // changes. The best cross browser solution is to poll for a change in the hashtag. + if (_intervalId) clearInterval(_intervalId); + _intervalId = setInterval(function(){ + var hash = document.location.hash, + re = /^#?\d+&/; + if (hash !== _lastHash && re.test(hash)) { + _lastHash = hash; + if (hash.replace(re, '') === 'close_window'){ + document.body.removeChild(document.getElementById(id)); + document.location.hash = ''; + } } - }; + }, 100); + }); +}; +// tb_require('../helpers/helpers.js') - this.show = function() { - _visible = true; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - for (var name in _widgets) { - _widgets[name].show(); - } - }; +OT.ConnectionCapabilities = function(capabilitiesHash) { + // Private helper methods + var castCapabilities = function(capabilitiesHash) { + capabilitiesHash.supportsWebRTC = OT.$.castToBoolean(capabilitiesHash.supportsWebRTC); + return capabilitiesHash; + }; - this.hide = function() { - _visible = false; + // Private data + var _caps = castCapabilities(capabilitiesHash); + this.supportsWebRTC = _caps.supportsWebRTC; +}; - for (var name in _widgets) { - _widgets[name].hide(); - } - }; +// tb_require('../helpers/helpers.js') +// tb_require('../helpers/lib/properties.js') +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - // Adds the widget to the chrome and to the DOM. Also creates a accessor - // property for it on the chrome. - // - // @example - // chrome.set('foo', new FooWidget()); - // chrome.foo.setDisplayMode('on'); - // - // @example - // chrome.set({ - // foo: new FooWidget(), - // bar: new BarWidget() - // }); - // chrome.foo.setDisplayMode('on'); - // - this.set = function(widgetName, widget) { - if (typeof(widgetName) === 'string' && widget) { - _set.call(this, widgetName, widget); - } else { - for (var name in widgetName) { - if (widgetName.hasOwnProperty(name)) { - _set.call(this, name, widgetName[name]); - } - } - } - return this; - }; +/** + * A class defining properties of the capabilities property of a + * Session object. See Session.capabilities. + *

    + * All Capabilities properties are undefined until you have connected to a session + * and the Session object has dispatched the sessionConnected event. + *

    + * For more information on token roles, see the + * generate_token() + * method of the OpenTok server-side libraries. + * + * @class Capabilities + * + * @property {Number} forceDisconnect Specifies whether you can call + * the Session.forceDisconnect() method (1) or not (0). To call the + * Session.forceDisconnect() method, + * the user must have a token that is assigned the role of moderator. + * @property {Number} forceUnpublish Specifies whether you can call + * the Session.forceUnpublish() method (1) or not (0). To call the + * Session.forceUnpublish() method, the user must have a token that + * is assigned the role of moderator. + * @property {Number} publish Specifies whether you can publish to the session (1) or not (0). + * The ability to publish is based on a few factors. To publish, the user must have a token that + * is assigned a role that supports publishing. There must be a connected camera and microphone. + * @property {Number} subscribe Specifies whether you can subscribe to streams + * in the session (1) or not (0). Currently, this capability is available for all users on all + * platforms. + */ +OT.Capabilities = function(permissions) { + this.publish = OT.$.arrayIndexOf(permissions, 'publish') !== -1 ? 1 : 0; + this.subscribe = OT.$.arrayIndexOf(permissions, 'subscribe') !== -1 ? 1 : 0; + this.forceUnpublish = OT.$.arrayIndexOf(permissions, 'forceunpublish') !== -1 ? 1 : 0; + this.forceDisconnect = OT.$.arrayIndexOf(permissions, 'forcedisconnect') !== -1 ? 1 : 0; + this.supportsWebRTC = OT.$.hasCapabilities('webrtc') ? 1 : 0; + this.permittedTo = function(action) { + return this.hasOwnProperty(action) && this[action] === 1; }; +}; -})(window); -!(function() { +// tb_require('../helpers/helpers.js') +// tb_require('../helpers/lib/properties.js') +// tb_require('./events.js') +// tb_require('./capabilities.js') +// tb_require('./connection_capabilities.js') - if (!OT.Chrome.Behaviour) OT.Chrome.Behaviour = {}; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - // A mixin to encapsulate the basic widget behaviour. This needs a better name, - // it's not actually a widget. It's actually "Behaviour that can be applied to - // an object to make it support the basic Chrome widget workflow"...but that would - // probably been too long a name. - OT.Chrome.Behaviour.Widget = function(widget, options) { - var _options = options || {}, - _mode, - _previousMode; +/** + * The Connection object represents a connection to an OpenTok session. Each client that connects + * to a session has a unique connection, with a unique connection ID (represented by the + * id property of the Connection object for the client). + *

    + * The Session object has a connection property that is a Connection object. + * It represents the local client's connection. (A client only has a connection once the + * client has successfully called the connect() method of the {@link Session} + * object.) + *

    + * The Session object dispatches a connectionCreated event when each client (including + * your own) connects to a session (and for clients that are present in the session when you + * connect). The connectionCreated event object has a connection + * property, which is a Connection object corresponding to the client the event pertains to. + *

    + * The Stream object has a connection property that is a Connection object. + * It represents the connection of the client that is publishing the stream. + * + * @class Connection + * @property {String} connectionId The ID of this connection. + * @property {Number} creationTime The timestamp for the creation of the connection. This + * value is calculated in milliseconds. + * You can convert this value to a Date object by calling new Date(creationTime), + * where creationTime + * is the creationTime property of the Connection object. + * @property {String} data A string containing metadata describing the + * connection. When you generate a user token string pass the connection data string to the + * generate_token() method of our + * server-side libraries. You can also generate a token + * and define connection data on the + * Dashboard page. + */ +OT.Connection = function(id, creationTime, data, capabilitiesHash, permissionsHash) { + var destroyedReason; - // - // @param [String] mode - // 'on', 'off', or 'auto' - // - widget.setDisplayMode = function(mode) { - var newMode = mode || 'auto'; - if (_mode === newMode) return; + this.id = this.connectionId = id; + this.creationTime = creationTime ? Number(creationTime) : null; + this.data = data; + this.capabilities = new OT.ConnectionCapabilities(capabilitiesHash); + this.permissions = new OT.Capabilities(permissionsHash); + this.quality = null; - OT.$.removeClass(this.domElement, 'OT_mode-' + _mode); - OT.$.addClass(this.domElement, 'OT_mode-' + newMode); + OT.$.eventing(this); - _previousMode = _mode; - _mode = newMode; - }; + this.destroy = OT.$.bind(function(reason, quiet) { + destroyedReason = reason || 'clientDisconnected'; - widget.show = function() { - this.setDisplayMode(_previousMode); - if (_options.onShow) _options.onShow(); + if (quiet !== true) { + this.dispatchEvent( + new OT.DestroyedEvent( + 'destroyed', // This should be OT.Event.names.CONNECTION_DESTROYED, but + // the value of that is currently shared with Session + this, + destroyedReason + ) + ); + } + }, this); - return this; - }; + this.destroyed = function() { + return destroyedReason !== void 0; + }; - widget.hide = function() { - this.setDisplayMode('off'); - if (_options.onHide) _options.onHide(); + this.destroyedReason = function() { + return destroyedReason; + }; - return this; - }; +}; + +OT.Connection.fromHash = function(hash) { + return new OT.Connection(hash.id, + hash.creationTime, + hash.data, + OT.$.extend(hash.capablities || {}, { supportsWebRTC: true }), + hash.permissions || [] ); +}; - widget.destroy = function() { - if (_options.onDestroy) _options.onDestroy(this.domElement); - if (this.domElement) OT.$.removeElement(this.domElement); - return widget; - }; +// tb_require('../../../helpers/helpers.js') +// tb_require('./message.js') +// tb_require('../../connection.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - widget.appendTo = function(parent) { - // create the element under parent - this.domElement = OT.$.createElement(_options.nodeName || 'div', - _options.htmlAttributes, - _options.htmlContent); +!(function() { - if (_options.onCreate) _options.onCreate(this.domElement); + var MAX_SIGNAL_DATA_LENGTH = 8192, + MAX_SIGNAL_TYPE_LENGTH = 128; - widget.setDisplayMode(_options.mode); + // + // Error Codes: + // 413 - Type too long + // 400 - Type is invalid + // 413 - Data too long + // 400 - Data is invalid (can't be parsed as JSON) + // 429 - Rate limit exceeded + // 500 - Websocket connection is down + // 404 - To connection does not exist + // 400 - To is invalid + // + OT.Signal = function(sessionId, fromConnectionId, options) { + var isInvalidType = function(type) { + // Our format matches the unreserved characters from the URI RFC: + // http://www.ietf.org/rfc/rfc3986 + return !/^[a-zA-Z0-9\-\._~]+$/.exec(type); + }, - if (_options.mode === 'auto') { - // if the mode is auto we hold the "on mode" for 2 seconds - // this will let the proper widgets nicely fade away and help discoverability - OT.$.addClass(widget.domElement, 'OT_mode-on-hold'); - setTimeout(function() { - OT.$.removeClass(widget.domElement, 'OT_mode-on-hold'); - }, 2000); - } + validateTo = function(toAddress) { + if (!toAddress) { + return { + code: 400, + reason: 'The signal to field was invalid. Either set it to a OT.Connection, ' + + 'OT.Session, or omit it entirely' + }; + } + if ( !(toAddress instanceof OT.Connection || toAddress instanceof OT.Session) ) { + return { + code: 400, + reason: 'The To field was invalid' + }; + } - // add the widget to the parent - parent.appendChild(this.domElement); + return null; + }, - return widget; - }; - }; + validateType = function(type) { + var error = null; -})(window); -!(function() { + if (type === null || type === void 0) { + error = { + code: 400, + reason: 'The signal type was null or undefined. Either set it to a String value or ' + + 'omit it' + }; + } + else if (type.length > MAX_SIGNAL_TYPE_LENGTH) { + error = { + code: 413, + reason: 'The signal type was too long, the maximum length of it is ' + + MAX_SIGNAL_TYPE_LENGTH + ' characters' + }; + } + else if ( isInvalidType(type) ) { + error = { + code: 400, + reason: 'The signal type was invalid, it can only contain letters, ' + + 'numbers, \'-\', \'_\', and \'~\'.' + }; + } - // BackingBar Chrome Widget - // - // nameMode (String) - // Whether or not the name panel is being displayed - // Possible values are: "auto" (the name is displayed - // when the stream is first displayed and when the user mouses over the display), - // "off" (the name is not displayed), and "on" (the name is displayed). - // - // muteMode (String) - // Whether or not the mute button is being displayed - // Possible values are: "auto" (the mute button is displayed - // when the stream is first displayed and when the user mouses over the display), - // "off" (the mute button is not displayed), and "on" (the mute button is displayed). - // - // displays a backing bar - // can be shown/hidden - // can be destroyed - OT.Chrome.BackingBar = function(options) { - var _nameMode = options.nameMode, - _muteMode = options.muteMode; - - function getDisplayMode() { - if(_nameMode === 'on' || _muteMode === 'on') { - return 'on'; - } else if(_nameMode === 'mini' || _muteMode === 'mini') { - return 'mini'; - } else if(_nameMode === 'mini-auto' || _muteMode === 'mini-auto') { - return 'mini-auto'; - } else if(_nameMode === 'auto' || _muteMode === 'auto') { - return 'auto'; - } else { - return 'off'; - } - } + return error; + }, + + validateData = function(data) { + var error = null; + if (data === null || data === void 0) { + error = { + code: 400, + reason: 'The signal data was null or undefined. Either set it to a String value or ' + + 'omit it' + }; + } + else { + try { + if (JSON.stringify(data).length > MAX_SIGNAL_DATA_LENGTH) { + error = { + code: 413, + reason: 'The data field was too long, the maximum size of it is ' + + MAX_SIGNAL_DATA_LENGTH + ' characters' + }; + } + } + catch(e) { + error = {code: 400, reason: 'The data field was not valid JSON'}; + } + } + + return error; + }; + + + this.toRaptorMessage = function() { + var to = this.to; - // Mixin common widget behaviour - OT.Chrome.Behaviour.Widget(this, { - mode: getDisplayMode(), - nodeName: 'div', - htmlContent: '', - htmlAttributes: { - className: 'OT_bar OT_edge-bar-item' + if (to && typeof(to) !== 'string') { + to = to.id; } - }); - this.setNameMode = function(nameMode) { - _nameMode = nameMode; - this.setDisplayMode(getDisplayMode()); + return OT.Raptor.Message.signals.create(OT.APIKEY, sessionId, to, this.type, this.data); }; - this.setMuteMode = function(muteMode) { - _muteMode = muteMode; - this.setDisplayMode(getDisplayMode()); + this.toHash = function() { + return options; }; - }; - -})(window); -!(function() { - - // NamePanel Chrome Widget - // - // mode (String) - // Whether to display the name. Possible values are: "auto" (the name is displayed - // when the stream is first displayed and when the user mouses over the display), - // "off" (the name is not displayed), and "on" (the name is displayed). - // - // displays a name - // can be shown/hidden - // can be destroyed - OT.Chrome.NamePanel = function(options) { - var _name = options.name, - _bugMode = options.bugMode; - if (!_name || OT.$.trim(_name).length === '') { - _name = null; + this.error = null; - // THere's no name, just flip the mode off - options.mode = 'off'; - } + if (options) { + if (options.hasOwnProperty('data')) { + this.data = OT.$.clone(options.data); + this.error = validateData(this.data); + } - this.setName = OT.$.bind(function(name) { - if (!_name) this.setDisplayMode('auto'); - _name = name; - this.domElement.innerHTML = _name; - }); + if (options.hasOwnProperty('to')) { + this.to = options.to; - this.setBugMode = OT.$.bind(function(bugMode) { - _bugMode = bugMode; - if(bugMode === 'off') { - OT.$.addClass(this.domElement, 'OT_name-no-bug'); - } else { - OT.$.removeClass(this.domElement, 'OT_name-no-bug'); + if (!this.error) { + this.error = validateTo(this.to); + } } - }, this); - // Mixin common widget behaviour - OT.Chrome.Behaviour.Widget(this, { - mode: options.mode, - nodeName: 'h1', - htmlContent: _name, - htmlAttributes: { - className: 'OT_name OT_edge-bar-item' - }, - onCreate: OT.$.bind(function() { - this.setBugMode(_bugMode); - }, this) - }); + if (options.hasOwnProperty('type')) { + if (!this.error) { + this.error = validateType(options.type); + } + this.type = options.type; + } + } + this.valid = this.error === null; }; -})(window); -!(function() { +}(this)); - OT.Chrome.MuteButton = function(options) { - var _onClickCb, - _muted = options.muted || false, - updateClasses, - attachEvents, - detachEvents, - onClick; - - updateClasses = OT.$.bind(function() { - if (_muted) { - OT.$.addClass(this.domElement, 'OT_active'); - } else { - OT.$.removeClass(this.domElement, 'OT_active '); - } - }, this); +// tb_require('../../../helpers/helpers.js') +// tb_require('../rumor/rumor.js') +// tb_require('./message.js') +// tb_require('./dispatch.js') +// tb_require('./signal.js') - // Private Event Callbacks - attachEvents = function(elem) { - _onClickCb = OT.$.bind(onClick, this); - OT.$.on(elem, 'click', _onClickCb); - }; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - detachEvents = function(elem) { - _onClickCb = null; - OT.$.off(elem, 'click', _onClickCb); - }; +function SignalError(code, message) { + this.code = code; + this.message = message; - onClick = function() { - _muted = !_muted; + // Undocumented. Left in for backwards compatibility: + this.reason = message; +} - updateClasses(); +// The Dispatcher bit is purely to make testing simpler, it defaults to a new OT.Raptor.Dispatcher +// so in normal operation you would omit it. +OT.Raptor.Socket = function(connectionId, widgetId, messagingSocketUrl, symphonyUrl, dispatcher) { + var _states = ['disconnected', 'connecting', 'connected', 'error', 'disconnecting'], + _sessionId, + _token, + _rumor, + _dispatcher, + _completion; - if (_muted) { - this.parent.trigger('muted', this); - } else { - this.parent.trigger('unmuted', this); - } - return false; - }; + //// Private API + var setState = OT.$.statable(this, _states, 'disconnected'), - OT.$.defineProperties(this, { - muted: { - get: function() { return _muted; }, - set: function(muted) { - _muted = muted; - updateClasses(); + onConnectComplete = function onConnectComplete(error) { + if (error) { + setState('error'); + } + else { + setState('connected'); } - } - }); - // Mixin common widget behaviour - var classNames = _muted ? 'OT_edge-bar-item OT_mute OT_active' : 'OT_edge-bar-item OT_mute'; - OT.Chrome.Behaviour.Widget(this, { - mode: options.mode, - nodeName: 'button', - htmlContent: 'Mute', - htmlAttributes: { - className: classNames + _completion.apply(null, arguments); }, - onCreate: OT.$.bind(attachEvents, this), - onDestroy: OT.$.bind(detachEvents, this) - }); - }; + onClose = OT.$.bind(function onClose (err) { + var reason = this.is('disconnecting') ? 'clientDisconnected' : 'networkDisconnected'; -})(window); -!(function() { - - OT.Chrome.OpenTokButton = function(options) { + if(err && err.code === 4001) { + reason = 'networkTimedout'; + } - // Mixin common widget behaviour - OT.Chrome.Behaviour.Widget(this, { - mode: options ? options.mode : null, - nodeName: 'span', - htmlContent: 'OpenTok', - htmlAttributes: { - className: 'OT_opentok OT_edge-bar-item' - } - }); + setState('disconnected'); - }; + _dispatcher.onClose(reason); + }, this), -})(window); -!(function() { + onError = function onError () {}; + // @todo what does having an error mean? Are they always fatal? Are we disconnected now? - // Archving Chrome Widget - // - // mode (String) - // Whether to display the archving widget. Possible values are: "on" (the status is displayed - // when archiving and briefly when archving ends) and "off" (the status is not displayed) - - // Whether to display the archving widget. Possible values are: "auto" (the name is displayed - // when the status is first displayed and when the user mouses over the display), - // "off" (the name is not displayed), and "on" (the name is displayed). - // - // displays a name - // can be shown/hidden - // can be destroyed - OT.Chrome.Archiving = function(options) { - var _archiving = options.archiving, - _archivingStarted = options.archivingStarted || 'Archiving on', - _archivingEnded = options.archivingEnded || 'Archiving off', - _initialState = true, - _lightBox, - _light, - _text, - _textNode, - renderStageDelayedAction, - renderText, - renderStage; - - renderText = function(text) { - _textNode.nodeValue = text; - _lightBox.setAttribute('title', text); - }; - - renderStage = OT.$.bind(function() { - if(renderStageDelayedAction) { - clearTimeout(renderStageDelayedAction); - renderStageDelayedAction = null; - } - if(_archiving) { - OT.$.addClass(_light, 'OT_active'); - } else { - OT.$.removeClass(_light, 'OT_active'); - } + //// Public API - OT.$.removeClass(this.domElement, 'OT_archiving-' + (!_archiving ? 'on' : 'off')); - OT.$.addClass(this.domElement, 'OT_archiving-' + (_archiving ? 'on' : 'off')); - if(options.show && _archiving) { - renderText(_archivingStarted); - OT.$.addClass(_text, 'OT_mode-on'); - OT.$.removeClass(_text, 'OT_mode-auto'); - this.setDisplayMode('on'); - renderStageDelayedAction = setTimeout(function() { - OT.$.addClass(_text, 'OT_mode-auto'); - OT.$.removeClass(_text, 'OT_mode-on'); - }, 5000); - } else if(options.show && !_initialState) { - OT.$.addClass(_text, 'OT_mode-on'); - OT.$.removeClass(_text, 'OT_mode-auto'); - this.setDisplayMode('on'); - renderText(_archivingEnded); - renderStageDelayedAction = setTimeout(OT.$.bind(function() { - this.setDisplayMode('off'); - }, this), 5000); - } else { - this.setDisplayMode('off'); - } - }, this); + this.connect = function (token, sessionInfo, completion) { + if (!this.is('disconnected', 'error')) { + OT.warn('Cannot connect the Raptor Socket as it is currently connected. You should ' + + 'disconnect first.'); + return; + } - // Mixin common widget behaviour - OT.Chrome.Behaviour.Widget(this, { - mode: _archiving && options.show && 'on' || 'off', - nodeName: 'h1', - htmlAttributes: {className: 'OT_archiving OT_edge-bar-item OT_edge-bottom'}, - onCreate: OT.$.bind(function() { - _lightBox = OT.$.createElement('div', { - className: 'OT_archiving-light-box' - }, ''); - _light = OT.$.createElement('div', { - className: 'OT_archiving-light' - }, ''); - _lightBox.appendChild(_light); - _text = OT.$.createElement('div', { - className: 'OT_archiving-status OT_mode-on OT_edge-bar-item OT_edge-bottom' - }, ''); - _textNode = document.createTextNode(''); - _text.appendChild(_textNode); - this.domElement.appendChild(_lightBox); - this.domElement.appendChild(_text); - renderStage(); - }, this) - }); - - this.setShowArchiveStatus = OT.$.bind(function(show) { - options.show = show; - if(this.domElement) { - renderStage.call(this); - } - }, this); + setState('connecting'); + _sessionId = sessionInfo.sessionId; + _token = token; + _completion = completion; + + var rumorChannel = '/v2/partner/' + OT.APIKEY + '/session/' + _sessionId; + + _rumor = new OT.Rumor.Socket(messagingSocketUrl, symphonyUrl); + _rumor.onClose(onClose); + _rumor.onMessage(OT.$.bind(_dispatcher.dispatch, _dispatcher)); - this.setArchiving = OT.$.bind(function(status) { - _archiving = status; - _initialState = false; - if(this.domElement) { - renderStage.call(this); + _rumor.connect(connectionId, OT.$.bind(function(error) { + if (error) { + onConnectComplete({ + reason: 'WebSocketConnection', + code: error.code, + message: error.message + }); + return; } - }, this); - }; + // we do this here to avoid getting connect errors twice + _rumor.onError(onError); -})(window); -!(function() { + OT.debug('Raptor Socket connected. Subscribing to ' + + rumorChannel + ' on ' + messagingSocketUrl); - OT.Chrome.AudioLevelMeter = function(options) { + _rumor.subscribe([rumorChannel]); - var widget = this, - _meterBarElement, - _voiceOnlyIconElement, - _meterValueElement, - _value, - _maxValue = options.maxValue || 1, - _minValue = options.minValue || 0; - - // Mixin common widget behaviour - OT.Chrome.Behaviour.Widget(this, { - mode: options ? options.mode : 'auto', - nodeName: 'div', - htmlAttributes: { - className: 'OT_audio-level-meter' - }, - onCreate: function() { - _meterBarElement = OT.$.createElement('div', { - className: 'OT_audio-level-meter__bar' - }, ''); - _meterValueElement = OT.$.createElement('div', { - className: 'OT_audio-level-meter__value' - }, ''); - _voiceOnlyIconElement = OT.$.createElement('div', { - className: 'OT_audio-level-meter__audio-only-img' - }, ''); - - widget.domElement.appendChild(_meterBarElement); - widget.domElement.appendChild(_voiceOnlyIconElement); - widget.domElement.appendChild(_meterValueElement); - } - }); + //connect to session + var connectMessage = OT.Raptor.Message.connections.create(OT.APIKEY, + _sessionId, _rumor.id()); + this.publish(connectMessage, {'X-TB-TOKEN-AUTH': _token}, OT.$.bind(function(error) { + if (error) { + error.message = 'ConnectToSession:' + error.code + + ':Received error response to connection create message.'; + var payload = { + reason: 'ConnectToSession', + code: error.code, + message: 'Received error response to connection create message.' + }; + var event = { + action: 'Connect', + variation: 'Failure', + payload: payload, + sessionId: _sessionId, + partnerId: OT.APIKEY, + connectionId: connectionId + }; + OT.analytics.logEvent(event); + onConnectComplete(payload); + return; + } - function updateView() { - var percentSize = _value * 100 / (_maxValue - _minValue); - _meterValueElement.style.width = _meterValueElement.style.height = 2 * percentSize + '%'; - _meterValueElement.style.top = _meterValueElement.style.right = -percentSize + '%'; - } - - widget.setValue = function(value) { - _value = value; - updateView(); - }; + this.publish( OT.Raptor.Message.sessions.get(OT.APIKEY, _sessionId), + function (error) { + if (error) { + var payload = { + reason: 'GetSessionState', + code: error.code, + message: 'Received error response to session read' + }; + var event = { + action: 'Connect', + variation: 'Failure', + payload: payload, + sessionId: _sessionId, + partnerId: OT.APIKEY, + connectionId: connectionId + }; + OT.analytics.logEvent(event); + onConnectComplete(payload); + } else { + onConnectComplete.apply(null, arguments); + } + }); + }, this)); + }, this)); }; -})(window); -!(function() { - OT.Chrome.VideoDisabledIndicator = function(options) { - var _mode, - _videoDisabled = false, - _warning = false, - updateClasses; - - _mode = options.mode || 'auto'; - updateClasses = function(domElement) { - if (_videoDisabled) { - OT.$.addClass(domElement, 'OT_video-disabled'); - } else { - OT.$.removeClass(domElement, 'OT_video-disabled'); - } - if(_warning) { - OT.$.addClass(domElement, 'OT_video-disabled-warning'); - } else { - OT.$.removeClass(domElement, 'OT_video-disabled-warning'); - } - if ((_videoDisabled || _warning) && (_mode === 'auto' || _mode === 'on')) { - OT.$.addClass(domElement, 'OT_active'); - } else { - OT.$.removeClass(domElement, 'OT_active'); - } - }; - - this.disableVideo = function(value) { - _videoDisabled = value; - if(value === true) { - _warning = false; - } - updateClasses(this.domElement); - }; - - this.setWarning = function(value) { - _warning = value; - updateClasses(this.domElement); - }; - // Mixin common widget behaviour - OT.Chrome.Behaviour.Widget(this, { - mode: _mode, - nodeName: 'div', - htmlAttributes: { - className: 'OT_video-disabled-indicator' - } - }); + this.disconnect = function (drainSocketBuffer) { + if (this.is('disconnected')) return; - this.setDisplayMode = function(mode) { - _mode = mode; - updateClasses(this.domElement); - }; + setState('disconnecting'); + _rumor.disconnect(drainSocketBuffer); }; -})(window); -(function() { -/* Stylable Notes - * RTC doesn't need to wait until anything is loaded - * Some bits are controlled by multiple flags, i.e. buttonDisplayMode and nameDisplayMode. - * When there are multiple flags how is the final setting chosen? - * When some style bits are set updates will need to be pushed through to the Chrome - */ - // Mixes the StylableComponent behaviour into the +self+ object. It will - // also set the default styles to +initialStyles+. - // - // @note This Mixin is dependent on OT.Eventing. - // + // Publishs +message+ to the Symphony app server. // - // @example + // The completion handler is optional, as is the headers + // dict, but if you provide the completion handler it must + // be the last argument. // - // function SomeObject { - // OT.StylableComponent(this, { - // name: 'SomeObject', - // foo: 'bar' - // }); - // } - // - // var obj = new SomeObject(); - // obj.getStyle('foo'); // => 'bar' - // obj.setStyle('foo', 'baz') - // obj.getStyle('foo'); // => 'baz' - // obj.getStyle(); // => {name: 'SomeObject', foo: 'baz'} - // - OT.StylableComponent = function(self, initalStyles) { - if (!self.trigger) { - throw new Error('OT.StylableComponent is dependent on the mixin OT.$.eventing. ' + - 'Ensure that this is included in the object before StylableComponent.'); - } - - // Broadcast style changes as the styleValueChanged event - var onStyleChange = function(key, value, oldValue) { - if (oldValue) { - self.trigger('styleValueChanged', key, value, oldValue); - } else { - self.trigger('styleValueChanged', key, value); + this.publish = function (message, headers, completion) { + if (_rumor.isNot('connected')) { + OT.error('OT.Raptor.Socket: cannot publish until the socket is connected.' + message); + return; + } + + var transactionId = OT.$.uuid(), + _headers = {}, + _completion; + + // Work out if which of the optional arguments (headers, completion) + // have been provided. + if (headers) { + if (OT.$.isFunction(headers)) { + _headers = {}; + _completion = headers; } - }; + else { + _headers = headers; + } + } + if (!_completion && completion && OT.$.isFunction(completion)) _completion = completion; - var _style = new Style(initalStyles, onStyleChange); - /** - * Returns an object that has the properties that define the current user interface controls of - * the Publisher. You can modify the properties of this object and pass the object to the - * setStyle() method of thePublisher object. (See the documentation for - * setStyle() to see the styles that define this object.) - * @return {Object} The object that defines the styles of the Publisher. - * @see setStyle() - * @method #getStyle - * @memberOf Publisher - */ + if (_completion) _dispatcher.registerCallback(transactionId, _completion); - /** - * Returns an object that has the properties that define the current user interface controls of - * the Subscriber. You can modify the properties of this object and pass the object to the - * setStyle() method of the Subscriber object. (See the documentation for - * setStyle() to see the styles that define this object.) - * @return {Object} The object that defines the styles of the Subscriber. - * @see setStyle() - * @method #getStyle - * @memberOf Subscriber - */ - // If +key+ is falsly then all styles will be returned. - self.getStyle = function(key) { - return _style.get(key); - }; + OT.debug('OT.Raptor.Socket Publish (ID:' + transactionId + ') '); + OT.debug(message); - /** - * Sets properties that define the appearance of some user interface controls of the Publisher. - * - *

    You can either pass one parameter or two parameters to this method.

    - * - *

    If you pass one parameter, style, it is an object that has the following - * properties: - * - *

      - *
    • audioLevelDisplayMode (String) — How to display the audio level - * indicator. Possible values are: "auto" (the indicator is displayed when the - * video is disabled), "off" (the indicator is not displayed), and - * "on" (the indicator is always displayed).
    • - * - *
    • backgroundImageURI (String) — A URI for an image to display as - * the background image when a video is not displayed. (A video may not be displayed if - * you call publishVideo(false) on the Publisher object). You can pass an http - * or https URI to a PNG, JPEG, or non-animated GIF file location. You can also use the - * data URI scheme (instead of http or https) and pass in base-64-encrypted - * PNG data, such as that obtained from the - * Publisher.getImgData() method. For example, - * you could set the property to "data:VBORw0KGgoAA...", where the portion of - * the string after "data:" is the result of a call to - * Publisher.getImgData(). If the URL or the image data is invalid, the - * property is ignored (the attempt to set the image fails silently). - *

      - * Note that in Internet Explorer 8 (using the OpenTok Plugin for Internet Explorer), - * you cannot set the backgroundImageURI style to a string larger than - * 32 kB. This is due to an IE 8 limitation on the size of URI strings. Due to this - * limitation, you cannot set the backgroundImageURI style to a string obtained - * with the getImgData() method. - *

    • - * - *
    • buttonDisplayMode (String) — How to display the microphone - * controls. Possible values are: "auto" (controls are displayed when the - * stream is first displayed and when the user mouses over the display), "off" - * (controls are not displayed), and "on" (controls are always displayed).
    • - * - *
    • nameDisplayMode (String) — Whether to display the stream name. - * Possible values are: "auto" (the name is displayed when the stream is first - * displayed and when the user mouses over the display), "off" (the name is not - * displayed), and "on" (the name is always displayed).
    • - *
    - *

    - * - *

    For example, the following code passes one parameter to the method:

    - * - *
    myPublisher.setStyle({nameDisplayMode: "off"});
    - * - *

    If you pass two parameters, style and value, they are - * key-value pair that define one property of the display style. For example, the following - * code passes two parameter values to the method:

    - * - *
    myPublisher.setStyle("nameDisplayMode", "off");
    - * - *

    You can set the initial settings when you call the Session.publish() - * or OT.initPublisher() method. Pass a style property as part of the - * properties parameter of the method.

    - * - *

    The OT object dispatches an exception event if you pass in an invalid style - * to the method. The code property of the ExceptionEvent object is set to 1011.

    - * - * @param {Object} style Either an object containing properties that define the style, or a - * String defining this single style property to set. - * @param {String} value The value to set for the style passed in. Pass a value - * for this parameter only if the value of the style parameter is a String.

    - * - * @see getStyle() - * @return {Publisher} The Publisher object - * @see setStyle() - * - * @see Session.publish() - * @see OT.initPublisher() - * @method #setStyle - * @memberOf Publisher - */ + _rumor.publish([symphonyUrl], message, OT.$.extend(_headers, { + 'Content-Type': 'application/x-raptor+v2', + 'TRANSACTION-ID': transactionId, + 'X-TB-FROM-ADDRESS': _rumor.id() + })); - /** - * Sets properties that define the appearance of some user interface controls of the Subscriber. - * - *

    You can either pass one parameter or two parameters to this method.

    - * - *

    If you pass one parameter, style, it is an object that has the following - * properties: - * - *

      - *
    • audioLevelDisplayMode (String) — How to display the audio level - * indicator. Possible values are: "auto" (the indicator is displayed when the - * video is disabled), "off" (the indicator is not displayed), and - * "on" (the indicator is always displayed).
    • - * - *
    • backgroundImageURI (String) — A URI for an image to display as - * the background image when a video is not displayed. (A video may not be displayed if - * you call subscribeToVideo(false) on the Publisher object). You can pass an - * http or https URI to a PNG, JPEG, or non-animated GIF file location. You can also use the - * data URI scheme (instead of http or https) and pass in base-64-encrypted - * PNG data, such as that obtained from the - * Subscriber.getImgData() method. For example, - * you could set the property to "data:VBORw0KGgoAA...", where the portion of - * the string after "data:" is the result of a call to - * Publisher.getImgData(). If the URL or the image data is invalid, the - * property is ignored (the attempt to set the image fails silently). - *

      - * Note that in Internet Explorer 8 (using the OpenTok Plugin for Internet Explorer), - * you cannot set the backgroundImageURI style to a string larger than - * 32 kB. This is due to an IE 8 limitation on the size of URI strings. Due to this - * limitation, you cannot set the backgroundImageURI style to a string obtained - * with the getImgData() method. - *

    • - * - *
    • buttonDisplayMode (String) — How to display the speaker - * controls. Possible values are: "auto" (controls are displayed when the - * stream is first displayed and when the user mouses over the display), "off" - * (controls are not displayed), and "on" (controls are always displayed).
    • - * - *
    • nameDisplayMode (String) — Whether to display the stream name. - * Possible values are: "auto" (the name is displayed when the stream is first - * displayed and when the user mouses over the display), "off" (the name is not - * displayed), and "on" (the name is always displayed).
    • - * - *
    • videoDisabledDisplayMode (String) — Whether to display the video - * disabled indicator and video disabled warning icons for a Subscriber. These icons - * indicate that the video has been disabled (or is in risk of being disabled for - * the warning icon) due to poor stream quality. Possible values are: "auto" - * (the icons are automatically when the displayed video is disabled or in risk of being - * disabled due to poor stream quality), "off" (do not display the icons), and - * "on" (display the icons).
    • - *
    - *

    - * - *

    For example, the following code passes one parameter to the method:

    - * - *
    mySubscriber.setStyle({nameDisplayMode: "off"});
    - * - *

    If you pass two parameters, style and value, they are key-value - * pair that define one property of the display style. For example, the following code passes - * two parameter values to the method:

    - * - *
    mySubscriber.setStyle("nameDisplayMode", "off");
    - * - *

    You can set the initial settings when you call the Session.subscribe() method. - * Pass a style property as part of the properties parameter of the - * method.

    - * - *

    The OT object dispatches an exception event if you pass in an invalid style - * to the method. The code property of the ExceptionEvent object is set to 1011.

    - * - * @param {Object} style Either an object containing properties that define the style, or a - * String defining this single style property to set. - * @param {String} value The value to set for the style passed in. Pass a value - * for this parameter only if the value of the style parameter is a String.

    - * - * @returns {Subscriber} The Subscriber object. - * - * @see getStyle() - * @see setStyle() - * - * @see Session.subscribe() - * @method #setStyle - * @memberOf Subscriber - */ - self.setStyle = function(keyOrStyleHash, value, silent) { - if (typeof(keyOrStyleHash) !== 'string') { - _style.setAll(keyOrStyleHash, silent); - } else { - _style.set(keyOrStyleHash, value); - } - return this; - }; + return transactionId; }; - var Style = function(initalStyles, onStyleChange) { - var _style = {}, - _COMPONENT_STYLES, - _validStyleValues, - isValidStyle, - castValue; - - _COMPONENT_STYLES = [ - 'showMicButton', - 'showSpeakerButton', - 'nameDisplayMode', - 'buttonDisplayMode', - 'backgroundImageURI', - 'bugDisplayMode' - ]; - - _validStyleValues = { - buttonDisplayMode: ['auto', 'mini', 'mini-auto', 'off', 'on'], - nameDisplayMode: ['auto', 'off', 'on'], - bugDisplayMode: ['auto', 'off', 'on'], - audioLevelDisplayMode: ['auto', 'off', 'on'], - showSettingsButton: [true, false], - showMicButton: [true, false], - backgroundImageURI: null, - showControlBar: [true, false], - showArchiveStatus: [true, false], - videoDisabledDisplayMode: ['auto', 'off', 'on'] - }; - + // Register a new stream against _sessionId + this.streamCreate = function(name, audioFallbackEnabled, channels, minBitrate, maxBitrate, + completion) { + var streamId = OT.$.uuid(), + message = OT.Raptor.Message.streams.create( OT.APIKEY, + _sessionId, + streamId, + name, + audioFallbackEnabled, + channels, + minBitrate, + maxBitrate); - // Validates the style +key+ and also whether +value+ is valid for +key+ - isValidStyle = function(key, value) { - return key === 'backgroundImageURI' || - (_validStyleValues.hasOwnProperty(key) && - OT.$.arrayIndexOf(_validStyleValues[key], value) !== -1 ); - }; - - castValue = function(value) { - switch(value) { - case 'true': - return true; - case 'false': - return false; - default: - return value; - } - }; + this.publish(message, function(error, message) { + completion(error, streamId, message); + }); + }; - // Returns a shallow copy of the styles. - this.getAll = function() { - var style = OT.$.clone(_style); + this.streamDestroy = function(streamId) { + this.publish( OT.Raptor.Message.streams.destroy(OT.APIKEY, _sessionId, streamId) ); + }; - for (var key in style) { - if(!style.hasOwnProperty(key)) { - continue; - } - if (OT.$.arrayIndexOf(_COMPONENT_STYLES, key) < 0) { + this.streamChannelUpdate = function(streamId, channelId, attributes) { + this.publish( OT.Raptor.Message.streamChannels.update(OT.APIKEY, _sessionId, + streamId, channelId, attributes) ); + }; - // Strip unnecessary properties out, should this happen on Set? - delete style[key]; - } - } + this.subscriberCreate = function(streamId, subscriberId, channelsToSubscribeTo, completion) { + this.publish( OT.Raptor.Message.subscribers.create(OT.APIKEY, _sessionId, + streamId, subscriberId, _rumor.id(), channelsToSubscribeTo), completion ); + }; - return style; - }; + this.subscriberDestroy = function(streamId, subscriberId) { + this.publish( OT.Raptor.Message.subscribers.destroy(OT.APIKEY, _sessionId, + streamId, subscriberId) ); + }; - this.get = function(key) { - if (key) { - return _style[key]; - } + this.subscriberUpdate = function(streamId, subscriberId, attributes) { + this.publish( OT.Raptor.Message.subscribers.update(OT.APIKEY, _sessionId, + streamId, subscriberId, attributes) ); + }; - // We haven't been asked for any specific key, just return the lot - return this.getAll(); - }; + this.subscriberChannelUpdate = function(streamId, subscriberId, channelId, attributes) { + this.publish( OT.Raptor.Message.subscriberChannels.update(OT.APIKEY, _sessionId, + streamId, subscriberId, channelId, attributes) ); + }; - // *note:* this will not trigger onStyleChange if +silent+ is truthy - this.setAll = function(newStyles, silent) { - var oldValue, newValue; + this.forceDisconnect = function(connectionIdToDisconnect, completion) { + this.publish( OT.Raptor.Message.connections.destroy(OT.APIKEY, _sessionId, + connectionIdToDisconnect), completion ); + }; - for (var key in newStyles) { - if(!newStyles.hasOwnProperty(key)) { - continue; - } - newValue = castValue(newStyles[key]); + this.forceUnpublish = function(streamIdToUnpublish, completion) { + this.publish( OT.Raptor.Message.streams.destroy(OT.APIKEY, _sessionId, + streamIdToUnpublish), completion ); + }; - if (isValidStyle(key, newValue)) { - oldValue = _style[key]; + this.jsepCandidate = function(streamId, candidate) { + this.publish( + OT.Raptor.Message.streams.candidate(OT.APIKEY, _sessionId, streamId, candidate) + ); + }; - if (newValue !== oldValue) { - _style[key] = newValue; - if (!silent) onStyleChange(key, newValue, oldValue); - } + this.jsepCandidateP2p = function(streamId, subscriberId, candidate) { + this.publish( + OT.Raptor.Message.subscribers.candidate(OT.APIKEY, _sessionId, streamId, + subscriberId, candidate) + ); + }; - } else { - OT.warn('Style.setAll::Invalid style property passed ' + key + ' : ' + newValue); - } - } + this.jsepOffer = function(uri, offerSdp) { + this.publish( OT.Raptor.Message.offer(uri, offerSdp) ); + }; - return this; - }; + this.jsepAnswer = function(streamId, answerSdp) { + this.publish( OT.Raptor.Message.streams.answer(OT.APIKEY, _sessionId, streamId, answerSdp) ); + }; - this.set = function(key, value) { - OT.debug('setStyle: ' + key.toString()); + this.jsepAnswerP2p = function(streamId, subscriberId, answerSdp) { + this.publish( OT.Raptor.Message.subscribers.answer(OT.APIKEY, _sessionId, streamId, + subscriberId, answerSdp) ); + }; - var newValue = castValue(value), - oldValue; + this.signal = function(options, completion, logEventFn) { + var signal = new OT.Signal(_sessionId, _rumor.id(), options || {}); - if (!isValidStyle(key, newValue)) { - OT.warn('Style.set::Invalid style property passed ' + key + ' : ' + newValue); - return this; + if (!signal.valid) { + if (completion && OT.$.isFunction(completion)) { + completion( new SignalError(signal.error.code, signal.error.reason), signal.toHash() ); } - oldValue = _style[key]; - if (newValue !== oldValue) { - _style[key] = newValue; + return; + } - onStyleChange(key, value, oldValue); + this.publish( signal.toRaptorMessage(), function(err) { + var error; + if (err) { + error = new SignalError(err.code, err.message); + } else { + var typeStr = signal.data? typeof(signal.data) : null; + logEventFn('signal', 'send', {type: typeStr}); } - return this; - }; - + if (completion && OT.$.isFunction(completion)) completion(error, signal.toHash()); + }); + }; - if (initalStyles) this.setAll(initalStyles, true); + this.id = function() { + return _rumor && _rumor.id(); }; -})(window); -!(function() { + if(dispatcher == null) { + dispatcher = new OT.Raptor.Dispatcher(); + } + _dispatcher = dispatcher; +}; + +// tb_require('../helpers/helpers.js') +// tb_require('../helpers/lib/capabilities.js') +// tb_require('./peer_connection/publisher_peer_connection.js') +// tb_require('./peer_connection/subscriber_peer_connection.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ /* - * A Publishers Microphone. + * A RTCPeerConnection.getStats based audio level sampler. * - * TODO - * * bind to changes in mute/unmute/volume/etc and respond to them + * It uses the the getStats method to get the audioOutputLevel. + * This implementation expects the single parameter version of the getStats method. + * + * Currently the audioOutputLevel stats is only supported in Chrome. + * + * @param {OT.SubscriberPeerConnection} peerConnection the peer connection to use to get the stats + * @constructor */ - OT.Microphone = function(webRTCStream, muted) { - var _muted; - - OT.$.defineProperties(this, { - muted: { - get: function() { - return _muted; - }, - set: function(muted) { - if (_muted === muted) return; +OT.GetStatsAudioLevelSampler = function(peerConnection) { - _muted = muted; + if (!OT.$.hasCapabilities('audioOutputLevelStat')) { + throw new Error('The current platform does not provide the required capabilities'); + } - var audioTracks = webRTCStream.getAudioTracks(); + var _peerConnection = peerConnection, + _statsProperty = 'audioOutputLevel'; - for (var i=0, num=audioTracks.length; inull if no value could be acquired + */ + this.sample = function(done) { + _peerConnection.getStats(function(error, stats) { + if (!error) { + for (var idx = 0; idx < stats.length; idx++) { + var stat = stats[idx]; + var audioOutputLevel = parseFloat(stat[_statsProperty]); + if (!isNaN(audioOutputLevel)) { + // the mex value delivered by getStats for audio levels is 2^15 + done(audioOutputLevel / 32768); + return; } } } - }); - - // Set the initial value - if (muted !== undefined) { - this.muted(muted === true); - - } else if (webRTCStream.getAudioTracks().length) { - this.muted(!webRTCStream.getAudioTracks()[0].enabled); - - } else { - this.muted(false); - } + done(null); + }); }; +}; -})(window); -!(function(window, OT) { - - // A Factory method for generating simple state machine classes. - // - // @usage - // var StateMachine = OT.generateSimpleStateMachine('start', ['start', 'middle', 'end', { - // start: ['middle'], - // middle: ['end'], - // end: ['start'] - // }]); - // - // var states = new StateMachine(); - // state.current; // <-- start - // state.set('middle'); - // - OT.generateSimpleStateMachine = function(initialState, states, transitions) { - var validStates = states.slice(), - validTransitions = OT.$.clone(transitions); - - var isValidState = function (state) { - return OT.$.arrayIndexOf(validStates, state) !== -1; - }; - - var isValidTransition = function(fromState, toState) { - return validTransitions[fromState] && - OT.$.arrayIndexOf(validTransitions[fromState], toState) !== -1; - }; - return function(stateChangeFailed) { - var currentState = initialState, - previousState = null; +/* + * An AudioContext based audio level sampler. It returns the maximum value in the + * last 1024 samples. + * + * It is worth noting that the remote MediaStream audio analysis is currently only + * available in FF. + * + * This implementation gracefully handles the case where the MediaStream has not + * been set yet by returning a null value until the stream is set. It is up to the + * call site to decide what to do with this value (most likely ignore it and retry later). + * + * @constructor + * @param {AudioContext} audioContext an audio context instance to get an analyser node + */ +OT.AnalyserAudioLevelSampler = function(audioContext) { - this.current = currentState; + var _sampler = this, + _analyser = null, + _timeDomainData = null; - function signalChangeFailed(message, newState) { - stateChangeFailed({ - message: message, - newState: newState, - currentState: currentState, - previousState: previousState - }); - } + var _getAnalyser = function(stream) { + var sourceNode = audioContext.createMediaStreamSource(stream); + var analyser = audioContext.createAnalyser(); + sourceNode.connect(analyser); + return analyser; + }; - // Validates +newState+. If it's invalid it triggers stateChangeFailed and returns false. - function handleInvalidStateChanges(newState) { - if (!isValidState(newState)) { - signalChangeFailed('\'' + newState + '\' is not a valid state', newState); + this.webRTCStream = null; - return false; - } + this.sample = function(done) { - if (!isValidTransition(currentState, newState)) { - signalChangeFailed('\'' + currentState + '\' cannot transition to \'' + - newState + '\'', newState); + if (!_analyser && _sampler.webRTCStream) { + _analyser = _getAnalyser(_sampler.webRTCStream); + _timeDomainData = new Uint8Array(_analyser.frequencyBinCount); + } - return false; - } + if (_analyser) { + _analyser.getByteTimeDomainData(_timeDomainData); - return true; + // varies from 0 to 255 + var max = 0; + for (var idx = 0; idx < _timeDomainData.length; idx++) { + max = Math.max(max, Math.abs(_timeDomainData[idx] - 128)); } - - this.set = function(newState) { - if (!handleInvalidStateChanges(newState)) return; - previousState = currentState; - this.current = currentState = newState; - }; - - }; + // normalize the collected level to match the range delivered by + // the getStats' audioOutputLevel + done(max / 128); + } else { + done(null); + } }; +}; -})(window, window.OT); -!(function() { +/* + * Transforms a raw audio level to produce a "smoother" animation when using displaying the + * audio level. This transformer is state-full because it needs to keep the previous average + * value of the signal for filtering. + * + * It applies a low pass filter to get rid of level jumps and apply a log scale. + * + * @constructor + */ +OT.AudioLevelTransformer = function() { -// Models a Subscriber's subscribing State -// -// Valid States: -// NotSubscribing (the initial state -// Init (basic setup of DOM -// ConnectingToPeer (Failure Cases -> No Route, Bad Offer, Bad Answer -// BindingRemoteStream (Failure Cases -> Anything to do with the media being -// (invalid, the media never plays -// Subscribing (this is 'onLoad' -// Failed (terminal state, with a reason that maps to one of the -// (failure cases above -// Destroyed (The subscriber has been cleaned up, terminal state -// -// -// Valid Transitions: -// NotSubscribing -> -// Init -// -// Init -> -// ConnectingToPeer -// | BindingRemoteStream (if we are subscribing to ourselves and we alreay -// (have a stream -// | NotSubscribing (destroy() -// -// ConnectingToPeer -> -// BindingRemoteStream -// | NotSubscribing -// | Failed -// | NotSubscribing (destroy() -// -// BindingRemoteStream -> -// Subscribing -// | Failed -// | NotSubscribing (destroy() -// -// Subscribing -> -// NotSubscribing (unsubscribe -// | Failed (probably a peer connection failure after we began -// (subscribing -// -// Failed -> -// Destroyed -// -// Destroyed -> (terminal state) -// -// -// @example -// var state = new SubscribingState(function(change) { -// console.log(change.message); -// }); -// -// state.set('Init'); -// state.current; -> 'Init' -// -// state.set('Subscribing'); -> triggers stateChangeFailed and logs out the error message -// -// - var validStates, - validTransitions, - initialState = 'NotSubscribing'; + var _averageAudioLevel = null; - validStates = [ - 'NotSubscribing', 'Init', 'ConnectingToPeer', - 'BindingRemoteStream', 'Subscribing', 'Failed', - 'Destroyed' - ]; + /* + * + * @param {number} audioLevel a level in the [0,1] range + * @returns {number} a level in the [0,1] range transformed + */ + this.transform = function(audioLevel) { + if (_averageAudioLevel === null || audioLevel >= _averageAudioLevel) { + _averageAudioLevel = audioLevel; + } else { + // a simple low pass filter with a smoothing of 70 + _averageAudioLevel = audioLevel * 0.3 + _averageAudioLevel * 0.7; + } - validTransitions = { - NotSubscribing: ['NotSubscribing', 'Init', 'Destroyed'], - Init: ['NotSubscribing', 'ConnectingToPeer', 'BindingRemoteStream', 'Destroyed'], - ConnectingToPeer: ['NotSubscribing', 'BindingRemoteStream', 'Failed', 'Destroyed'], - BindingRemoteStream: ['NotSubscribing', 'Subscribing', 'Failed', 'Destroyed'], - Subscribing: ['NotSubscribing', 'Failed', 'Destroyed'], - Failed: ['Destroyed'], - Destroyed: [] + // 1.5 scaling to map -30-0 dBm range to [0,1] + var logScaled = (Math.log(_averageAudioLevel) / Math.LN10) / 1.5 + 1; + + return Math.min(Math.max(logScaled, 0), 1); }; +}; - OT.SubscribingState = OT.generateSimpleStateMachine(initialState, validStates, validTransitions); +// tb_require('../helpers/helpers.js') - OT.SubscribingState.prototype.isDestroyed = function() { - return this.current === 'Destroyed'; - }; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - OT.SubscribingState.prototype.isFailed = function() { - return this.current === 'Failed'; +/* + * Lazy instantiates an audio context and always return the same instance on following calls + * + * @returns {AudioContext} + */ +OT.audioContext = function() { + var context = new window.AudioContext(); + OT.audioContext = function() { + return context; }; + return context; +}; - OT.SubscribingState.prototype.isSubscribing = function() { - return this.current === 'Subscribing'; - }; +// tb_require('../helpers/helpers.js') +// tb_require('./events.js') - OT.SubscribingState.prototype.isAttemptingToSubscribe = function() { - return OT.$.arrayIndexOf( - [ 'Init', 'ConnectingToPeer', 'BindingRemoteStream' ], - this.current - ) !== -1; - }; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ -})(window); -!(function() { +OT.Archive = function(id, name, status) { + this.id = id; + this.name = name; + this.status = status; -// Models a Publisher's publishing State -// -// Valid States: -// NotPublishing -// GetUserMedia -// BindingMedia -// MediaBound -// PublishingToSession -// Publishing -// Failed -// Destroyed -// -// -// Valid Transitions: -// NotPublishing -> -// GetUserMedia -// -// GetUserMedia -> -// BindingMedia -// | Failed (Failure Reasons -> stream error, constraints, -// (permission denied -// | NotPublishing (destroy() -// -// -// BindingMedia -> -// MediaBound -// | Failed (Failure Reasons -> Anything to do with the media -// (being invalid, the media never plays -// | NotPublishing (destroy() -// -// MediaBound -> -// PublishingToSession (MediaBound could transition to PublishingToSession -// (if a stand-alone publish is bound to a session -// | Failed (Failure Reasons -> media issues with a stand-alone publisher -// | NotPublishing (destroy() -// -// PublishingToSession -// Publishing -// | Failed (Failure Reasons -> timeout while waiting for ack of -// (stream registered. We do not do this right now -// | NotPublishing (destroy() -// -// -// Publishing -> -// NotPublishing (Unpublish -// | Failed (Failure Reasons -> loss of network, media error, anything -// (that causes *all* Peer Connections to fail (less than all -// (failing is just an error, all is failure) -// | NotPublishing (destroy() -// -// Failed -> -// Destroyed -// -// Destroyed -> (Terminal state -// -// + this._ = {}; - var validStates = [ - 'NotPublishing', 'GetUserMedia', 'BindingMedia', 'MediaBound', - 'PublishingToSession', 'Publishing', 'Failed', - 'Destroyed' - ], + OT.$.eventing(this); - validTransitions = { - NotPublishing: ['NotPublishing', 'GetUserMedia', 'Destroyed'], - GetUserMedia: ['BindingMedia', 'Failed', 'NotPublishing', 'Destroyed'], - BindingMedia: ['MediaBound', 'Failed', 'NotPublishing', 'Destroyed'], - MediaBound: ['NotPublishing', 'PublishingToSession', 'Failed', 'Destroyed'], - PublishingToSession: ['NotPublishing', 'Publishing', 'Failed', 'Destroyed'], - Publishing: ['NotPublishing', 'MediaBound', 'Failed', 'Destroyed'], - Failed: ['Destroyed'], - Destroyed: [] - }, + // Mass update, called by Raptor.Dispatcher + this._.update = OT.$.bind(function (attributes) { + for (var key in attributes) { + if(!attributes.hasOwnProperty(key)) { + continue; + } + var oldValue = this[key]; + this[key] = attributes[key]; - initialState = 'NotPublishing'; + var event = new OT.ArchiveUpdatedEvent(this, key, oldValue, this[key]); + this.dispatchEvent(event); + } + }, this); - OT.PublishingState = OT.generateSimpleStateMachine(initialState, validStates, validTransitions); + this.destroy = function() {}; - OT.PublishingState.prototype.isDestroyed = function() { - return this.current === 'Destroyed'; - }; +}; - OT.PublishingState.prototype.isAttemptingToPublish = function() { - return OT.$.arrayIndexOf( - [ 'GetUserMedia', 'BindingMedia', 'MediaBound', 'PublishingToSession' ], - this.current) !== -1; - }; +// tb_require('../helpers/helpers.js') +// tb_require('../helpers/lib/properties.js') +// tb_require('../helpers/lib/analytics.js') - OT.PublishingState.prototype.isPublishing = function() { - return this.current === 'Publishing'; - }; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ -})(window); -!(function() { +OT.analytics = new OT.Analytics(OT.properties.loggingURL); +// tb_require('../helpers/helpers.js') +// tb_require('../helpers/lib/widget_view.js') +// tb_require('./analytics.js') +// tb_require('./events.js') +// tb_require('./system_requirements.js') +// tb_require('./stylable_component.js') +// tb_require('./stream.js') +// tb_require('./connection.js') +// tb_require('./subscribing_state.js') +// tb_require('./environment_loader.js') +// tb_require('./audio_level_samplers.js') +// tb_require('./audio_context.js') +// tb_require('./chrome/chrome.js') +// tb_require('./chrome/backing_bar.js') +// tb_require('./chrome/name_panel.js') +// tb_require('./chrome/mute_button.js') +// tb_require('./chrome/archiving.js') +// tb_require('./chrome/audio_level_meter.js') +// tb_require('./peer_connection/subscriber_peer_connection.js') +// tb_require('./peer_connection/get_stats_adapter.js') +// tb_require('./peer_connection/get_stats_helpers.js') - // The default constraints - var defaultConstraints = { - audio: true, - video: true - }; + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ /** - * The Publisher object provides the mechanism through which control of the - * published stream is accomplished. Calling the OT.initPublisher() method - * creates a Publisher object.

    - * - *

    The following code instantiates a session, and publishes an audio-video stream - * upon connection to the session:

    - * - *
    - *  var apiKey = ""; // Replace with your API key. See https://dashboard.tokbox.com/projects
    - *  var sessionID = ""; // Replace with your own session ID.
    - *                      // See https://dashboard.tokbox.com/projects
    - *  var token = ""; // Replace with a generated token that has been assigned the moderator role.
    - *                  // See https://dashboard.tokbox.com/projects
    - *
    - *  var session = OT.initSession(apiKey, sessionID);
    - *  session.on("sessionConnected", function(event) {
    - *    // This example assumes that a DOM element with the ID 'publisherElement' exists
    - *    var publisherProperties = {width: 400, height:300, name:"Bob's stream"};
    - *    publisher = OT.initPublisher('publisherElement', publisherProperties);
    - *    session.publish(publisher);
    - *  });
    - *  session.connect(token);
    - *  
    - * - *

    This example creates a Publisher object and adds its video to a DOM element - * with the ID publisherElement by calling the OT.initPublisher() - * method. It then publishes a stream to the session by calling - * the publish() method of the Session object.

    - * - * @property {Boolean} accessAllowed Whether the user has granted access to the camera - * and microphone. The Publisher object dispatches an accessAllowed event when - * the user grants access. The Publisher object dispatches an accessDenied event - * when the user denies access. - * @property {Element} element The HTML DOM element containing the Publisher. - * @property {String} id The DOM ID of the Publisher. - * @property {Stream} stream The {@link Stream} object corresponding the the stream of - * the Publisher. - * @property {Session} session The {@link Session} to which the Publisher belongs. + * The Subscriber object is a representation of the local video element that is playing back + * a remote stream. The Subscriber object includes methods that let you disable and enable + * local audio playback for the subscribed stream. The subscribe() method of the + * {@link Session} object returns a Subscriber object. * - * @see OT.initPublisher - * @see Session.publish() + * @property {Element} element The HTML DOM element containing the Subscriber. + * @property {String} id The DOM ID of the Subscriber. + * @property {Stream} stream The stream to which you are subscribing. * - * @class Publisher + * @class Subscriber * @augments EventDispatcher */ - OT.Publisher = function() { - // Check that the client meets the minimum requirements, if they don't the upgrade - // flow will be triggered. - if (!OT.checkSystemRequirements()) { - OT.upgradeSystemRequirements(); - return; - } +OT.Subscriber = function(targetElement, options, completionHandler) { + var _widgetId = OT.$.uuid(), + _domId = targetElement || _widgetId, + _container, + _streamContainer, + _chrome, + _audioLevelMeter, + _fromConnectionId, + _peerConnection, + _session = options.session, + _stream = options.stream, + _subscribeStartTime, + _startConnectingTime, + _properties, + _audioVolume = 100, + _state, + _prevStats, + _lastSubscribeToVideoReason, + _audioLevelCapable = OT.$.hasCapabilities('audioOutputLevelStat') || + OT.$.hasCapabilities('webAudioCapableRemoteStream'), + _audioLevelSampler, + _audioLevelRunner, + _frameRateRestricted = false, + _connectivityAttemptPinger, + _subscriber = this; + + _properties = OT.$.defaults({}, options, { + showControls: true, + fitMode: _stream.defaultFitMode || 'cover' + }); - var _guid = OT.Publisher.nextId(), - _domId, - _container, - _targetElement, - _stream, - _streamId, - _webRTCStream, - _session, - _peerConnections = {}, - _loaded = false, - _publishProperties, - _publishStartTime, - _microphone, - _chrome, - _audioLevelMeter, - _analytics = new OT.Analytics(), - _validResolutions, - _validFrameRates = [ 1, 7, 15, 30 ], - _prevStats, - _state, - _iceServers, - _audioLevelCapable = OT.$.hasCapabilities('webAudio'), - _audioLevelSampler; - - _validResolutions = { - '320x240': {width: 320, height: 240}, - '640x480': {width: 640, height: 480}, - '1280x720': {width: 1280, height: 720} - }; + this.id = _domId; + this.widgetId = _widgetId; + this.session = _session; + this.stream = _stream = _properties.stream; + this.streamId = _stream.id; + + _prevStats = { + timeStamp: OT.$.now() + }; - _prevStats = { - 'timeStamp' : OT.$.now() - }; + if (!_session) { + OT.handleJsException('Subscriber must be passed a session option', 2000, { + session: _session, + target: this + }); - OT.$.eventing(this); + return; + } - if(_audioLevelCapable) { - _audioLevelSampler = new OT.AnalyserAudioLevelSampler(new window.AudioContext()); + OT.$.eventing(this, false); - var publisher = this; - var audioLevelRunner = new OT.IntervalRunner(function() { - _audioLevelSampler.sample(function(audioInputLevel) { - OT.$.requestAnimationFrame(function() { - publisher.dispatchEvent( - new OT.AudioLevelUpdatedEvent(audioInputLevel)); - }); - }); - }, 60); + if (typeof completionHandler === 'function') { + this.once('subscribeComplete', completionHandler); + } - this.on({ - 'audioLevelUpdated:added': function(count) { - if (count === 1) { - audioLevelRunner.start(); - } - }, - 'audioLevelUpdated:removed': function(count) { - if (count === 0) { - audioLevelRunner.stop(); - } + if(_audioLevelCapable) { + this.on({ + 'audioLevelUpdated:added': function(count) { + if (count === 1 && _audioLevelRunner) { + _audioLevelRunner.start(); } - }); - } - - OT.StylableComponent(this, { - showArchiveStatus: true, - nameDisplayMode: 'auto', - buttonDisplayMode: 'auto', - bugDisplayMode: 'auto', - audioLevelDisplayMode: 'auto', - backgroundImageURI: null + }, + 'audioLevelUpdated:removed': function(count) { + if (count === 0 && _audioLevelRunner) { + _audioLevelRunner.stop(); + } + } }); + } - /// Private Methods - var logAnalyticsEvent = function(action, variation, payloadType, payload) { - _analytics.logEvent({ - action: action, - variation: variation, - 'payload_type': payloadType, - payload: payload, - 'session_id': _session ? _session.sessionId : null, - 'connection_id': _session && - _session.isConnected() ? _session.connection.connectionId : null, - 'partner_id': _session ? _session.apiKey : OT.APIKEY, - streamId: _stream ? _stream.id : null, - 'widget_id': _guid, - 'widget_type': 'Publisher' - }); - }, + var logAnalyticsEvent = function(action, variation, payload, throttle) { + var args = [{ + action: action, + variation: variation, + payload: payload, + streamId: _stream ? _stream.id : null, + sessionId: _session ? _session.sessionId : null, + connectionId: _session && _session.isConnected() ? + _session.connection.connectionId : null, + partnerId: _session && _session.isConnected() ? _session.sessionInfo.partnerId : null, + subscriberId: _widgetId, + }]; + if (throttle) args.push(throttle); + OT.analytics.logEvent.apply(OT.analytics, args); + }, - recordQOS = OT.$.bind(function(connection, parsedStats) { - if(!_state.isPublishing()) { - return; - } - var QoSBlob = { - 'widget_type': 'Publisher', - 'stream_type': 'WebRTC', + logConnectivityEvent = function(variation, payload) { + if (variation === 'Attempt' || !_connectivityAttemptPinger) { + _connectivityAttemptPinger = new OT.ConnectivityAttemptPinger({ + action: 'Subscribe', sessionId: _session ? _session.sessionId : null, connectionId: _session && _session.isConnected() ? _session.connection.connectionId : null, - partnerId: _session ? _session.apiKey : OT.APIKEY, - streamId: _stream ? _stream.id : null, - width: _container ? OT.$.width(_container.domElement) : undefined, - height: _container ? OT.$.height(_container.domElement) : undefined, - widgetId: _guid, - version: OT.properties.version, - 'media_server_name': _session ? _session.sessionInfo.messagingServer : null, - p2pFlag: _session ? _session.sessionInfo.p2pEnabled : false, - duration: _publishStartTime ? new Date().getTime() - _publishStartTime.getTime() : 0, - 'remote_connection_id': connection.id - }; + partnerId: _session.isConnected() ? _session.sessionInfo.partnerId : null, + streamId: _stream ? _stream.id : null + }); + } + _connectivityAttemptPinger.setVariation(variation); + logAnalyticsEvent('Subscribe', variation, payload); + }, - _analytics.logQOS( OT.$.extend(QoSBlob, parsedStats) ); - this.trigger('qos', parsedStats); - }, this), + recordQOS = OT.$.bind(function(parsedStats) { + var QoSBlob = { + streamType : 'WebRTC', + width: _container ? Number(OT.$.width(_container.domElement).replace('px', '')) : null, + height: _container ? Number(OT.$.height(_container.domElement).replace('px', '')) : null, + sessionId: _session ? _session.sessionId : null, + connectionId: _session ? _session.connection.connectionId : null, + mediaServerName: _session ? _session.sessionInfo.messagingServer : null, + p2pFlag: _session ? _session.sessionInfo.p2pEnabled : false, + partnerId: _session ? _session.apiKey : null, + streamId: _stream.id, + subscriberId: _widgetId, + version: OT.properties.version, + duration: parseInt(OT.$.now() - _subscribeStartTime, 10), + remoteConnectionId: _stream.connection.connectionId + }; - /// Private Events + OT.analytics.logQOS( OT.$.extend(QoSBlob, parsedStats) ); + this.trigger('qos', parsedStats); + }, this), - stateChangeFailed = function(changeFailed) { - OT.error('Publisher State Change Failed: ', changeFailed.message); - OT.debug(changeFailed); - }, - onLoaded = function() { - if (_state.isDestroyed()) { - // The publisher was destroyed before loading finished - return; - } + stateChangeFailed = function(changeFailed) { + OT.error('Subscriber State Change Failed: ', changeFailed.message); + OT.debug(changeFailed); + }, - OT.debug('OT.Publisher.onLoaded'); + onLoaded = function() { + if (_state.isSubscribing() || !_streamContainer) return; - _state.set('MediaBound'); + OT.debug('OT.Subscriber.onLoaded'); - // If we have a session and we haven't created the stream yet then - // wait until that is complete before hiding the loading spinner - _container.loading(this.session ? !_stream : false); + _state.set('Subscribing'); + _subscribeStartTime = OT.$.now(); - _loaded = true; + var payload = { + pcc: parseInt(_subscribeStartTime - _startConnectingTime, 10), + hasRelayCandidates: _peerConnection && _peerConnection.hasRelayCandidates() + }; + logAnalyticsEvent('createPeerConnection', 'Success', payload); - _createChrome.call(this); + _container.loading(false); + _chrome.showAfterLoading(); - this.trigger('initSuccess'); - this.trigger('loaded', this); - }, + if(_frameRateRestricted) { + _stream.setRestrictFrameRate(true); + } - onLoadFailure = function(reason) { - logAnalyticsEvent('publish', 'Failure', 'reason', - 'Publisher PeerConnection Error: ' + reason); + if(_audioLevelMeter && _subscriber.getStyle('audioLevelDisplayMode') === 'auto') { + _audioLevelMeter[_container.audioOnly() ? 'show' : 'hide'](); + } - _state.set('Failed'); - this.trigger('publishComplete', new OT.Error(OT.ExceptionCodes.P2P_CONNECTION_FAILED, - 'Publisher PeerConnection Error: ' + reason)); + this.trigger('subscribeComplete', null, this); + this.trigger('loaded', this); - OT.handleJsException('Publisher PeerConnection Error: ' + reason, - OT.ExceptionCodes.P2P_CONNECTION_FAILED, { - session: _session, - target: this - }); - }, + logConnectivityEvent('Success', {streamId: _stream.id}); + }, - onStreamAvailable = function(webOTStream) { - OT.debug('OT.Publisher.onStreamAvailable'); + onDisconnected = function() { + OT.debug('OT.Subscriber has been disconnected from the Publisher\'s PeerConnection'); - _state.set('BindingMedia'); + if (_state.isAttemptingToSubscribe()) { + // subscribing error + _state.set('Failed'); + this.trigger('subscribeComplete', new OT.Error(null, 'ClientDisconnected')); - cleanupLocalStream(); - _webRTCStream = webOTStream; + } else if (_state.isSubscribing()) { + _state.set('Failed'); - _microphone = new OT.Microphone(_webRTCStream, !_publishProperties.publishAudio); - this.publishVideo(_publishProperties.publishVideo && - _webRTCStream.getVideoTracks().length > 0); + // we were disconnected after we were already subscribing + // probably do nothing? + } - this.accessAllowed = true; - this.dispatchEvent( - new OT.Event(OT.Event.names.ACCESS_ALLOWED, false) - ); + this.disconnect(); + }, - var videoContainerOptions = { - muted: true, - error: OT.$.bind(onVideoError, this) + onPeerConnectionFailure = OT.$.bind(function(code, reason, peerConnection, prefix) { + var payload; + if (_state.isAttemptingToSubscribe()) { + // We weren't subscribing yet so this was a failure in setting + // up the PeerConnection or receiving the initial stream. + payload = { + reason: prefix ? prefix : 'PeerConnectionError', + message: 'Subscriber PeerConnection Error: ' + reason, + hasRelayCandidates: _peerConnection && _peerConnection.hasRelayCandidates() }; + logAnalyticsEvent('createPeerConnection', 'Failure', payload); - _targetElement = _container.bindVideo(_webRTCStream, - videoContainerOptions, - OT.$.bind(function(err) { - if (err) { - onLoadFailure.call(this, err); - return; - } + _state.set('Failed'); + this.trigger('subscribeComplete', new OT.Error(null, reason)); + + } else if (_state.isSubscribing()) { + // we were disconnected after we were already subscribing + _state.set('Failed'); + this.trigger('error', reason); + } + + this.disconnect(); - onLoaded.call(this); - }, this)); + payload = { + reason: prefix ? prefix : 'PeerConnectionError', + message: 'Subscriber PeerConnection Error: ' + reason, + code: OT.ExceptionCodes.P2P_CONNECTION_FAILED + }; + logConnectivityEvent('Failure', payload); - if(_audioLevelSampler) { - _audioLevelSampler.webOTStream = webOTStream; + OT.handleJsException('Subscriber PeerConnection Error: ' + reason, + OT.ExceptionCodes.P2P_CONNECTION_FAILED, { + session: _session, + target: this } + ); + _showError.call(this, reason); + }, this), - }, + onRemoteStreamAdded = function(webRTCStream) { + OT.debug('OT.Subscriber.onRemoteStreamAdded'); - onStreamAvailableError = function(error) { - OT.error('OT.Publisher.onStreamAvailableError ' + error.name + ': ' + error.message); + _state.set('BindingRemoteStream'); - _state.set('Failed'); - this.trigger('publishComplete', new OT.Error(OT.ExceptionCodes.UNABLE_TO_PUBLISH, - error.message)); + // Disable the audio/video, if needed + this.subscribeToAudio(_properties.subscribeToAudio); - if (_container) _container.destroy(); + _lastSubscribeToVideoReason = 'loading'; + this.subscribeToVideo(_properties.subscribeToVideo, 'loading'); - logAnalyticsEvent('publish', 'Failure', 'reason', - 'GetUserMedia:Publisher failed to access camera/mic: ' + error.message); + var videoContainerOptions = { + error: onPeerConnectionFailure, + audioVolume: _audioVolume + }; - OT.handleJsException('Publisher failed to access camera/mic: ' + error.message, - OT.ExceptionCodes.UNABLE_TO_PUBLISH, { - session: _session, - target: this + // This is a workaround for a bug in Chrome where a track disabled on + // the remote end doesn't fire loadedmetadata causing the subscriber to timeout + // https://jira.tokbox.com/browse/OPENTOK-15605 + // Also https://jira.tokbox.com/browse/OPENTOK-16425 + var tracks, + reenableVideoTrack = false; + if (!_stream.hasVideo && OT.$.env.name === 'Chrome' && OT.$.env.version >= 35) { + tracks = webRTCStream.getVideoTracks(); + if(tracks.length > 0) { + tracks[0].enabled = false; + reenableVideoTrack = tracks[0]; + } + } + + _streamContainer = _container.bindVideo(webRTCStream, + videoContainerOptions, + OT.$.bind(function(err) { + if (err) { + onPeerConnectionFailure(null, err.message || err, _peerConnection, 'VideoElement'); + return; + } + + // Continues workaround for https://jira.tokbox.com/browse/OPENTOK-15605 + // Also https://jira.tokbox.com/browse/OPENTOK-16425] + if (reenableVideoTrack != null && _properties.subscribeToVideo) { + reenableVideoTrack.enabled = true; + } + + _streamContainer.orientation({ + width: _stream.videoDimensions.width, + height: _stream.videoDimensions.height, + videoOrientation: _stream.videoDimensions.orientation }); - }, - // The user has clicked the 'deny' button the the allow access dialog - // (or it's set to always deny) - onAccessDenied = function(error) { - OT.error('OT.Publisher.onStreamAvailableError Permission Denied'); + onLoaded.call(this, null); + }, this)); - _state.set('Failed'); - this.trigger('publishComplete', new OT.Error(OT.ExceptionCodes.UNABLE_TO_PUBLISH, - 'Publisher Access Denied: Permission Denied' + - (error.message ? ': ' + error.message : ''))); + if (OT.$.hasCapabilities('webAudioCapableRemoteStream') && _audioLevelSampler && + webRTCStream.getAudioTracks().length > 0) { + _audioLevelSampler.webRTCStream = webRTCStream; + } - logAnalyticsEvent('publish', 'Failure', 'reason', - 'GetUserMedia:Publisher Access Denied: Permission Denied'); + logAnalyticsEvent('createPeerConnection', 'StreamAdded'); + this.trigger('streamAdded', this); + }, - var browser = OT.$.browserVersion(); + onRemoteStreamRemoved = function(webRTCStream) { + OT.debug('OT.Subscriber.onStreamRemoved'); - var event = new OT.Event(OT.Event.names.ACCESS_DENIED), - defaultAction = function() { - if(!event.isDefaultPrevented()) { - if(browser.browser === 'Chrome') { - if (_container) { - _container.addError('', null, 'OT_publisher-denied-chrome'); - } - if(!accessDialogWasOpened) { - OT.Dialogs.AllowDeny.Chrome.previouslyDenied(window.location.hostname); - } else { - OT.Dialogs.AllowDeny.Chrome.deniedNow(); - } - } else if(browser.browser === 'Firefox') { - if(_container) { - _container.addError('', 'Click the reload button in the URL bar to change ' + - 'camera & mic settings.', 'OT_publisher-denied-firefox'); - } - OT.Dialogs.AllowDeny.Firefox.denied().on({ - refresh: function() { - window.location.reload(); - } - }); - } - } - }; + if (_streamContainer.stream === webRTCStream) { + _streamContainer.destroy(); + _streamContainer = null; + } - this.dispatchEvent(event, defaultAction); - }, - accessDialogPrompt, - accessDialogChromeTimeout, - accessDialogFirefoxTimeout, - accessDialogWasOpened = false, - - onAccessDialogOpened = function() { - - accessDialogWasOpened = true; - - logAnalyticsEvent('accessDialog', 'Opened', '', ''); - - var browser = OT.$.browserVersion(); - - this.dispatchEvent( - new OT.Event(OT.Event.names.ACCESS_DIALOG_OPENED, true), - function(event) { - if(!event.isDefaultPrevented()) { - if(browser.browser === 'Chrome') { - accessDialogChromeTimeout = setTimeout(function() { - accessDialogChromeTimeout = null; - logAnalyticsEvent('allowDenyHelpers', 'show', 'version', 'Chrome'); - accessDialogPrompt = OT.Dialogs.AllowDeny.Chrome.initialPrompt(); - accessDialogPrompt.on('closeButtonClicked', function() { - logAnalyticsEvent('allowDenyHelpers', 'dismissed', 'version', 'Chrome'); - }); - }, 5000); - } else if(browser.browser === 'Firefox') { - accessDialogFirefoxTimeout = setTimeout(function() { - accessDialogFirefoxTimeout = null; - logAnalyticsEvent('allowDenyHelpers', 'show', 'version', 'Firefox'); - accessDialogPrompt = OT.Dialogs.AllowDeny.Firefox.maybeDenied(); - accessDialogPrompt.on('closeButtonClicked', function() { - logAnalyticsEvent('allowDenyHelpers', 'dismissed', 'version', 'Firefox'); - }); - }, 7000); - } - } else { - logAnalyticsEvent('allowDenyHelpers', 'developerPrevented', '', ''); - } - } - ); - }, + this.trigger('streamRemoved', this); + }, - onAccessDialogClosed = function() { - logAnalyticsEvent('accessDialog', 'Closed', '', ''); + streamDestroyed = function () { + this.disconnect(); + }, - if(accessDialogChromeTimeout) { - clearTimeout(accessDialogChromeTimeout); - logAnalyticsEvent('allowDenyHelpers', 'notShown', 'version', 'Chrome'); - accessDialogChromeTimeout = null; - } + streamUpdated = function(event) { - if(accessDialogFirefoxTimeout) { - clearTimeout(accessDialogFirefoxTimeout); - logAnalyticsEvent('allowDenyHelpers', 'notShown', 'version', 'Firefox'); - accessDialogFirefoxTimeout = null; - } + switch(event.changedProperty) { + case 'videoDimensions': + if (!_streamContainer) { + // Ignore videoEmension updates before streamContainer is created OPENTOK-17253 + break; + } + _streamContainer.orientation({ + width: event.newValue.width, + height: event.newValue.height, + videoOrientation: event.newValue.orientation + }); - if(accessDialogPrompt) { - accessDialogPrompt.close(); - var browser = OT.$.browserVersion(); - logAnalyticsEvent('allowDenyHelpers', 'closed', 'version', browser.browser); - accessDialogPrompt = null; - } + this.dispatchEvent(new OT.VideoDimensionsChangedEvent( + this, event.oldValue, event.newValue + )); - this.dispatchEvent( - new OT.Event(OT.Event.names.ACCESS_DIALOG_CLOSED, false) - ); - }, + break; - onVideoError = function(errorCode, errorReason) { - OT.error('OT.Publisher.onVideoError'); + case 'videoDisableWarning': + _chrome.videoDisabledIndicator.setWarning(event.newValue); + this.dispatchEvent(new OT.VideoDisableWarningEvent( + event.newValue ? 'videoDisableWarning' : 'videoDisableWarningLifted' + )); + break; - var message = errorReason + (errorCode ? ' (' + errorCode + ')' : ''); - logAnalyticsEvent('stream', null, 'reason', - 'Publisher while playing stream: ' + message); + case 'hasVideo': - _state.set('Failed'); + setAudioOnly(!(_stream.hasVideo && _properties.subscribeToVideo)); - if (_state.isAttemptingToPublish()) { - this.trigger('publishComplete', new OT.Error(OT.ExceptionCodes.UNABLE_TO_PUBLISH, - message)); - } else { - this.trigger('error', message); - } + this.dispatchEvent(new OT.VideoEnabledChangedEvent( + _stream.hasVideo ? 'videoEnabled' : 'videoDisabled', { + reason: 'publishVideo' + })); + break; - OT.handleJsException('Publisher error playing stream: ' + message, - OT.ExceptionCodes.UNABLE_TO_PUBLISH, { - session: _session, - target: this - }); - }, + case 'hasAudio': + // noop + } + }, - onPeerDisconnected = function(peerConnection) { - OT.debug('OT.Subscriber has been disconnected from the Publisher\'s PeerConnection'); + /// Chrome - this.cleanupSubscriber(peerConnection.remoteConnection().id); - }, + // If mode is false, then that is the mode. If mode is true then we'll + // definitely display the button, but we'll defer the model to the + // Publishers buttonDisplayMode style property. + chromeButtonMode = function(mode) { + if (mode === false) return 'off'; - onPeerConnectionFailure = function(code, reason, peerConnection, prefix) { - logAnalyticsEvent('publish', 'Failure', 'reason|hasRelayCandidates', - (prefix ? prefix : '') + [':Publisher PeerConnection with connection ' + - (peerConnection && peerConnection.remoteConnection && - peerConnection.remoteConnection().id) + ' failed: ' + - reason, peerConnection.hasRelayCandidates() - ].join('|')); + var defaultMode = this.getStyle('buttonDisplayMode'); - OT.handleJsException('Publisher PeerConnection Error: ' + reason, - OT.ExceptionCodes.UNABLE_TO_PUBLISH, { - session: _session, - target: this - }); + // The default model is false, but it's overridden by +mode+ being true + if (defaultMode === false) return 'on'; - // We don't call cleanupSubscriber as it also logs a - // disconnected analytics event, which we don't want in this - // instance. The duplication is crufty though and should - // be tidied up. + // defaultMode is either true or auto. + return defaultMode; + }, - delete _peerConnections[peerConnection.remoteConnection().id]; - }, + updateChromeForStyleChange = function(key, value/*, oldValue*/) { + if (!_chrome) return; + + switch(key) { + case 'nameDisplayMode': + _chrome.name.setDisplayMode(value); + _chrome.backingBar.setNameMode(value); + break; + + case 'videoDisabledDisplayMode': + _chrome.videoDisabledIndicator.setDisplayMode(value); + break; - /// Private Helpers + case 'showArchiveStatus': + _chrome.archive.setShowArchiveStatus(value); + break; - // Assigns +stream+ to this publisher. The publisher listens - // for a bunch of events on the stream so it can respond to - // changes. - assignStream = OT.$.bind(function(stream) { - this.stream = _stream = stream; - _stream.on('destroyed', this.disconnect, this); + case 'buttonDisplayMode': + _chrome.muteButton.setDisplayMode(value); + _chrome.backingBar.setMuteMode(value); + break; - _state.set('Publishing'); - _container.loading(!_loaded); - _publishStartTime = new Date(); + case 'audioLevelDisplayMode': + _chrome.audioLevel.setDisplayMode(value); + break; - this.trigger('publishComplete', null, this); + case 'bugDisplayMode': + // bugDisplayMode can't be updated but is used by some partners - this.dispatchEvent(new OT.StreamEvent('streamCreated', stream, null, false)); + case 'backgroundImageURI': + _container.setBackgroundImageURI(value); + } + }, - logAnalyticsEvent('publish', 'Success', 'streamType:streamId', 'WebRTC:' + _streamId); - }, this), + _createChrome = function() { - // Clean up our LocalMediaStream - cleanupLocalStream = function() { - if (_webRTCStream) { - // Stop revokes our access cam and mic access for this instance - // of localMediaStream. - _webRTCStream.stop(); - _webRTCStream = null; - } - }, + var widgets = { + backingBar: new OT.Chrome.BackingBar({ + nameMode: !_properties.name ? 'off' : this.getStyle('nameDisplayMode'), + muteMode: chromeButtonMode.call(this, this.getStyle('showMuteButton')) + }), - createPeerConnectionForRemote = function(remoteConnection) { - var peerConnection = _peerConnections[remoteConnection.id]; + name: new OT.Chrome.NamePanel({ + name: _properties.name, + mode: this.getStyle('nameDisplayMode') + }), - if (!peerConnection) { - var startConnectingTime = OT.$.now(); + muteButton: new OT.Chrome.MuteButton({ + muted: _properties.muted, + mode: chromeButtonMode.call(this, this.getStyle('showMuteButton')) + }), - logAnalyticsEvent('createPeerConnection', 'Attempt', '', ''); + archive: new OT.Chrome.Archiving({ + show: this.getStyle('showArchiveStatus'), + archiving: false + }) + }; - // Cleanup our subscriber when they disconnect - remoteConnection.on('destroyed', - OT.$.bind(this.cleanupSubscriber, this, remoteConnection.id)); - - peerConnection = _peerConnections[remoteConnection.id] = new OT.PublisherPeerConnection( - remoteConnection, - _session, - _streamId, - _webRTCStream - ); - - peerConnection.on({ - connected: function() { - logAnalyticsEvent('createPeerConnection', 'Success', 'pcc|hasRelayCandidates', [ - parseInt(OT.$.now() - startConnectingTime, 10), - peerConnection.hasRelayCandidates() - ].join('|')); - }, - disconnected: onPeerDisconnected, - error: onPeerConnectionFailure, - qos: recordQOS - }, this); + if (_audioLevelCapable) { + var audioLevelTransformer = new OT.AudioLevelTransformer(); - peerConnection.init(_iceServers); - } + var audioLevelUpdatedHandler = function(evt) { + _audioLevelMeter.setValue(audioLevelTransformer.transform(evt.audioLevel)); + }; - return peerConnection; - }, + _audioLevelMeter = new OT.Chrome.AudioLevelMeter({ + mode: this.getStyle('audioLevelDisplayMode'), + onActivate: function() { + _subscriber.on('audioLevelUpdated', audioLevelUpdatedHandler); + }, + onPassivate: function() { + _subscriber.off('audioLevelUpdated', audioLevelUpdatedHandler); + } + }); - /// Chrome + widgets.audioLevel = _audioLevelMeter; + } - // If mode is false, then that is the mode. If mode is true then we'll - // definitely display the button, but we'll defer the model to the - // Publishers buttonDisplayMode style property. - chromeButtonMode = function(mode) { - if (mode === false) return 'off'; + widgets.videoDisabledIndicator = new OT.Chrome.VideoDisabledIndicator({ + mode: this.getStyle('videoDisabledDisplayMode') + }); - var defaultMode = this.getStyle('buttonDisplayMode'); + _chrome = new OT.Chrome({ + parent: _container.domElement + }).set(widgets).on({ + muted: function() { + muteAudio.call(this, true); + }, - // The default model is false, but it's overridden by +mode+ being true - if (defaultMode === false) return 'on'; + unmuted: function() { + muteAudio.call(this, false); + } + }, this); - // defaultMode is either true or auto. - return defaultMode; - }, + // Hide the chrome until we explicitly show it + _chrome.hideWhileLoading(); + }, - updateChromeForStyleChange = function(key, value) { - if (!_chrome) return; + _showError = function() { + // Display the error message inside the container, assuming it's + // been created by now. + if (_container) { + _container.addError( + 'The stream was unable to connect due to a network error.', + 'Make sure your connection isn\'t blocked by a firewall.' + ); + } + }; + + OT.StylableComponent(this, { + nameDisplayMode: 'auto', + buttonDisplayMode: 'auto', + audioLevelDisplayMode: 'auto', + videoDisabledDisplayMode: 'auto', + backgroundImageURI: null, + showArchiveStatus: true, + showMicButton: true + }, _properties.showControls, function (payload) { + logAnalyticsEvent('SetStyle', 'Subscriber', payload, 0.1); + }); - switch(key) { - case 'nameDisplayMode': - _chrome.name.setDisplayMode(value); - _chrome.backingBar.setNameMode(value); - break; + var setAudioOnly = function(audioOnly) { + if (_container) { + _container.audioOnly(audioOnly); + _container.showPoster(audioOnly); + } - case 'showArchiveStatus': - logAnalyticsEvent('showArchiveStatus', 'styleChange', 'mode', value ? 'on': 'off'); - _chrome.archive.setShowArchiveStatus(value); - break; + if (_audioLevelMeter && _subscriber.getStyle('audioLevelDisplayMode') === 'auto') { + _audioLevelMeter[audioOnly ? 'show' : 'hide'](); + } + }; - case 'buttonDisplayMode': - _chrome.muteButton.setDisplayMode(value); - _chrome.backingBar.setMuteMode(value); - break; + // logs an analytics event for getStats every 100 calls + var notifyGetStatsCalled = (function() { + var callCount = 0; + return function throttlingNotifyGetStatsCalled() { + if (callCount % 100 === 0) { + logAnalyticsEvent('getStats', 'Called'); + } + callCount++; + }; + })(); - case 'audioLevelDisplayMode': - _chrome.audioLevel.setDisplayMode(value); - break; + this.destroy = function(reason, quiet) { + if (_state.isDestroyed()) return; - case 'bugDisplayMode': - // bugDisplayMode can't be updated but is used by some partners + if(reason === 'streamDestroyed') { + if (_state.isAttemptingToSubscribe()) { + // We weren't subscribing yet so the stream was destroyed before we setup + // the PeerConnection or receiving the initial stream. + this.trigger('subscribeComplete', new OT.Error(null, 'InvalidStreamID')); + } + } - case 'backgroundImageURI': - _container.setBackgroundImageURI(value); - } - }, + _state.set('Destroyed'); - _createChrome = function() { + if(_audioLevelRunner) { + _audioLevelRunner.stop(); + } - if(this.getStyle('bugDisplayMode') === 'off') { - logAnalyticsEvent('bugDisplayMode', 'createChrome', 'mode', 'off'); - } - if(!this.getStyle('showArchiveStatus')) { - logAnalyticsEvent('showArchiveStatus', 'createChrome', 'mode', 'off'); - } + this.disconnect(); - var widgets = { - backingBar: new OT.Chrome.BackingBar({ - nameMode: !_publishProperties.name ? 'off' : this.getStyle('nameDisplayMode'), - muteMode: chromeButtonMode.call(this, this.getStyle('buttonDisplayMode')) - }), - - name: new OT.Chrome.NamePanel({ - name: _publishProperties.name, - mode: this.getStyle('nameDisplayMode'), - bugMode: this.getStyle('bugDisplayMode') - }), - - muteButton: new OT.Chrome.MuteButton({ - muted: _publishProperties.publishAudio === false, - mode: chromeButtonMode.call(this, this.getStyle('buttonDisplayMode')) - }), - - opentokButton: new OT.Chrome.OpenTokButton({ - mode: this.getStyle('bugDisplayMode') - }), - - archive: new OT.Chrome.Archiving({ - show: this.getStyle('showArchiveStatus'), - archiving: false - }) - }; + if (_chrome) { + _chrome.destroy(); + _chrome = null; + } - if(_audioLevelCapable) { - _audioLevelMeter = new OT.Chrome.AudioLevelMeter({ - mode: this.getStyle('audioLevelDisplayMode') - }); + if (_container) { + _container.destroy(); + _container = null; + this.element = null; + } - var audioLevelTransformer = new OT.AudioLevelTransformer(); - this.on('audioLevelUpdated', function(evt) { - _audioLevelMeter.setValue(audioLevelTransformer.transform(evt.audioLevel)); - }); + if (_stream && !_stream.destroyed) { + logAnalyticsEvent('unsubscribe', null, {streamId: _stream.id}); + } - widgets.audioLevel = _audioLevelMeter; - } + this.id = _domId = null; + this.stream = _stream = null; + this.streamId = null; - _chrome = new OT.Chrome({ - parent: _container.domElement - }).set(widgets).on({ - muted: OT.$.bind(this.publishAudio, this, false), - unmuted: OT.$.bind(this.publishAudio, this, true) - }); - }, + this.session =_session = null; + _properties = null; - reset = OT.$.bind(function() { - if (_chrome) { - _chrome.destroy(); - _chrome = null; - } + if (quiet !== true) { + this.dispatchEvent( + new OT.DestroyedEvent( + OT.Event.names.SUBSCRIBER_DESTROYED, + this, + reason + ), + OT.$.bind(this.off, this) + ); + } - this.disconnect(); + return this; + }; - _microphone = null; + this.disconnect = function() { + if (!_state.isDestroyed() && !_state.isFailed()) { + // If we are already in the destroyed state then disconnect + // has been called after (or from within) destroy. + _state.set('NotSubscribing'); + } - if (_targetElement) { - _targetElement.destroy(); - _targetElement = null; - } + if (_streamContainer) { + _streamContainer.destroy(); + _streamContainer = null; + } - cleanupLocalStream(); + if (_peerConnection) { + _peerConnection.destroy(); + _peerConnection = null; - if (_container) { - _container.destroy(); - _container = null; - } + logAnalyticsEvent('disconnect', 'PeerConnection', {streamId: _stream.id}); + } + }; - if (_session) { - this._.unpublishFromSession(_session, 'reset'); - } + this.processMessage = function(type, fromConnection, message) { + OT.debug('OT.Subscriber.processMessage: Received ' + type + ' message from ' + + fromConnection.id); + OT.debug(message); - this.id = _domId = null; - this.stream = _stream = null; - _loaded = false; + if (_fromConnectionId !== fromConnection.id) { + _fromConnectionId = fromConnection.id; + } - this.session = _session = null; + if (_peerConnection) { + _peerConnection.processMessage(type, message); + } + }; - if (!_state.isDestroyed()) _state.set('NotPublishing'); - }, this); + this.disableVideo = function(active) { + if (!active) { + OT.warn('Due to high packet loss and low bandwidth, video has been disabled'); + } else { + if (_lastSubscribeToVideoReason === 'auto') { + OT.info('Video has been re-enabled'); + _chrome.videoDisabledIndicator.disableVideo(false); + } else { + OT.info('Video was not re-enabled because it was manually disabled'); + return; + } + } + this.subscribeToVideo(active, 'auto'); + if(!active) { + _chrome.videoDisabledIndicator.disableVideo(true); + } + var payload = active ? {videoEnabled: true} : {videoDisabled: true}; + logAnalyticsEvent('updateQuality', 'video', payload); + }; - this.publish = function(targetElement, properties) { - OT.debug('OT.Publisher: publish'); + /** + * Return the base-64-encoded string of PNG data representing the Subscriber video. + * + *

    You can use the string as the value for a data URL scheme passed to the src parameter of + * an image file, as in the following:

    + * + *
    +   *  var imgData = subscriber.getImgData();
    +   *
    +   *  var img = document.createElement("img");
    +   *  img.setAttribute("src", "data:image/png;base64," + imgData);
    +   *  var imgWin = window.open("about:blank", "Screenshot");
    +   *  imgWin.document.write("<body></body>");
    +   *  imgWin.document.body.appendChild(img);
    +   *  
    + * @method #getImgData + * @memberOf Subscriber + * @return {String} The base-64 encoded string. Returns an empty string if there is no video. + */ + this.getImgData = function() { + if (!this.isSubscribing()) { + OT.error('OT.Subscriber.getImgData: Cannot getImgData before the Subscriber ' + + 'is subscribing.'); + return null; + } - if ( _state.isAttemptingToPublish() || _state.isPublishing() ) reset(); - _state.set('GetUserMedia'); + return _streamContainer.imgData(); + }; - _publishProperties = OT.$.defaults(properties || {}, { - publishAudio : true, - publishVideo : true, - mirror: true - }); + this.getStats = function(callback) { + if (!_peerConnection) { + callback(new OT.$.Error('Subscriber is not connected cannot getStats', 1015)); + return; + } - if (!_publishProperties.constraints) { - _publishProperties.constraints = OT.$.clone(defaultConstraints); + notifyGetStatsCalled(); - if(_publishProperties.audioSource === null || _publishProperties.audioSource === false) { - _publishProperties.constraints.audio = false; - _publishProperties.publishAudio = false; - } else { - if(typeof _publishProperties.audioSource === 'object') { - if(_publishProperties.audioSource.deviceId != null) { - _publishProperties.audioSource = _publishProperties.audioSource.deviceId; - } else { - OT.warn('Invalid audioSource passed to Publisher. Expected either a device ID'); - } - } + _peerConnection.getStats(function(error, stats) { + if (error) { + callback(error); + return; + } - if (_publishProperties.audioSource) { - if (typeof _publishProperties.constraints.audio !== 'object') { - _publishProperties.constraints.audio = {}; - } - if (!_publishProperties.constraints.audio.mandatory) { - _publishProperties.constraints.audio.mandatory = {}; - } - if (!_publishProperties.constraints.audio.optional) { - _publishProperties.constraints.audio.optional = []; - } - _publishProperties.constraints.audio.mandatory.sourceId = - _publishProperties.audioSource; + var otStats = { + timestamp: 0 + }; + + OT.$.forEach(stats, function(stat) { + if (OT.getStatsHelpers.isInboundStat(stat)) { + var video = OT.getStatsHelpers.isVideoStat(stat); + var audio = OT.getStatsHelpers.isAudioStat(stat); + + // it is safe to override the timestamp of one by another + // if they are from the same getStats "batch" video and audio ts have the same value + if (audio || video) { + otStats.timestamp = OT.getStatsHelpers.normalizeTimestamp(stat.timestamp); + } + if (video) { + otStats.video = OT.getStatsHelpers.parseStatCategory(stat); + } else if (audio) { + otStats.audio = OT.getStatsHelpers.parseStatCategory(stat); } } + }); - if(_publishProperties.videoSource === null || _publishProperties.videoSource === false) { - _publishProperties.constraints.video = false; - _publishProperties.publishVideo = false; - } else { + callback(null, otStats); + }); + }; - if(typeof _publishProperties.videoSource === 'object') { - if(_publishProperties.videoSource.deviceId != null) { - _publishProperties.videoSource = _publishProperties.videoSource.deviceId; - } else { - OT.warn('Invalid videoSource passed to Publisher. Expected either a device ID'); - } - } + /** + * Sets the audio volume, between 0 and 100, of the Subscriber. + * + *

    You can set the initial volume when you call the Session.subscribe() + * method. Pass a audioVolume property of the properties parameter + * of the method.

    + * + * @param {Number} value The audio volume, between 0 and 100. + * + * @return {Subscriber} The Subscriber object. This lets you chain method calls, as in the + * following: + * + *
    mySubscriber.setAudioVolume(50).setStyle(newStyle);
    + * + * @see getAudioVolume() + * @see Session.subscribe() + * @method #setAudioVolume + * @memberOf Subscriber + */ + this.setAudioVolume = function(value) { + value = parseInt(value, 10); + if (isNaN(value)) { + OT.error('OT.Subscriber.setAudioVolume: value should be an integer between 0 and 100'); + return this; + } + _audioVolume = Math.max(0, Math.min(100, value)); + if (_audioVolume !== value) { + OT.warn('OT.Subscriber.setAudioVolume: value should be an integer between 0 and 100'); + } + if(_properties.muted && _audioVolume > 0) { + _properties.premuteVolume = value; + muteAudio.call(this, false); + } + if (_streamContainer) { + _streamContainer.setAudioVolume(_audioVolume); + } + return this; + }; - if (_publishProperties.videoSource) { - if (typeof _publishProperties.constraints.video !== 'object') { - _publishProperties.constraints.video = {}; - } - if (!_publishProperties.constraints.video.mandatory) { - _publishProperties.constraints.video.mandatory = {}; - } - if (!_publishProperties.constraints.video.optional) { - _publishProperties.constraints.video.optional = []; - } - _publishProperties.constraints.video.mandatory.sourceId = - _publishProperties.videoSource; - } + /** + * Returns the audio volume, between 0 and 100, of the Subscriber. + * + *

    Generally you use this method in conjunction with the setAudioVolume() + * method.

    + * + * @return {Number} The audio volume, between 0 and 100, of the Subscriber. + * @see setAudioVolume() + * @method #getAudioVolume + * @memberOf Subscriber + */ + this.getAudioVolume = function() { + if(_properties.muted) { + return 0; + } + if (_streamContainer) return _streamContainer.getAudioVolume(); + else return _audioVolume; + }; - if (_publishProperties.resolution) { - if (_publishProperties.resolution !== void 0 && - !_validResolutions.hasOwnProperty(_publishProperties.resolution)) { - OT.warn('Invalid resolution passed to the Publisher. Got: ' + - _publishProperties.resolution + ' expecting one of "' + - OT.$.keys(_validResolutions).join('","') + '"'); - } else { - _publishProperties.videoDimensions = _validResolutions[_publishProperties.resolution]; - if (typeof _publishProperties.constraints.video !== 'object') { - _publishProperties.constraints.video = {}; - } - if (!_publishProperties.constraints.video.mandatory) { - _publishProperties.constraints.video.mandatory = {}; - } - if (!_publishProperties.constraints.video.optional) { - _publishProperties.constraints.video.optional = []; - } - _publishProperties.constraints.video.optional = - _publishProperties.constraints.video.optional.concat([ - {minWidth: _publishProperties.videoDimensions.width}, - {maxWidth: _publishProperties.videoDimensions.width}, - {minHeight: _publishProperties.videoDimensions.height}, - {maxHeight: _publishProperties.videoDimensions.height} - ]); - } - } + /** + * Toggles audio on and off. Starts subscribing to audio (if it is available and currently + * not being subscribed to) when the value is true; stops + * subscribing to audio (if it is currently being subscribed to) when the value + * is false. + *

    + * Note: This method only affects the local playback of audio. It has no impact on the + * audio for other connections subscribing to the same stream. If the Publsher is not + * publishing audio, enabling the Subscriber audio will have no practical effect. + *

    + * + * @param {Boolean} value Whether to start subscribing to audio (true) or not + * (false). + * + * @return {Subscriber} The Subscriber object. This lets you chain method calls, as in the + * following: + * + *
    mySubscriber.subscribeToAudio(true).subscribeToVideo(false);
    + * + * @see subscribeToVideo() + * @see Session.subscribe() + * @see StreamPropertyChangedEvent + * + * @method #subscribeToAudio + * @memberOf Subscriber + */ + this.subscribeToAudio = function(pValue) { + var value = OT.$.castToBoolean(pValue, true); - if (_publishProperties.frameRate !== void 0 && - OT.$.arrayIndexOf(_validFrameRates, _publishProperties.frameRate) === -1) { - OT.warn('Invalid frameRate passed to the publisher got: ' + - _publishProperties.frameRate + ' expecting one of ' + _validFrameRates.join(',')); - delete _publishProperties.frameRate; - } else if (_publishProperties.frameRate) { - if (typeof _publishProperties.constraints.video !== 'object') { - _publishProperties.constraints.video = {}; - } - if (!_publishProperties.constraints.video.mandatory) { - _publishProperties.constraints.video.mandatory = {}; - } - if (!_publishProperties.constraints.video.optional) { - _publishProperties.constraints.video.optional = []; - } - _publishProperties.constraints.video.optional = - _publishProperties.constraints.video.optional.concat([ - { minFrameRate: _publishProperties.frameRate }, - { maxFrameRate: _publishProperties.frameRate } - ]); - } - } + if (_peerConnection) { + _peerConnection.subscribeToAudio(value && !_properties.subscribeMute); - } else { - OT.warn('You have passed your own constraints not using ours'); + if (_session && _stream && value !== _properties.subscribeToAudio) { + _stream.setChannelActiveState('audio', value && !_properties.subscribeMute); } + } + _properties.subscribeToAudio = value; - if (_publishProperties.style) { - this.setStyle(_publishProperties.style, null, true); - } + return this; + }; - if (_publishProperties.name) { - _publishProperties.name = _publishProperties.name.toString(); - } + var muteAudio = function(_mute) { + _chrome.muteButton.muted(_mute); - _publishProperties.classNames = 'OT_root OT_publisher'; + if(_mute === _properties.mute) { + return; + } + if(OT.$.env.name === 'Chrome' || OTPlugin.isInstalled()) { + _properties.subscribeMute = _properties.muted = _mute; + this.subscribeToAudio(_properties.subscribeToAudio); + } else { + if(_mute) { + _properties.premuteVolume = this.getAudioVolume(); + _properties.muted = true; + this.setAudioVolume(0); + } else if(_properties.premuteVolume || _properties.audioVolume) { + _properties.muted = false; + this.setAudioVolume(_properties.premuteVolume || _properties.audioVolume); + } + } + _properties.mute = _properties.mute; + }; + + var reasonMap = { + auto: 'quality', + publishVideo: 'publishVideo', + subscribeToVideo: 'subscribeToVideo' + }; - // Defer actually creating the publisher DOM nodes until we know - // the DOM is actually loaded. - OT.onLoad(function() { - _container = new OT.WidgetView(targetElement, _publishProperties); - this.id = _domId = _container.domId(); - this.element = _container.domElement; - OT.$.shouldAskForDevices(OT.$.bind(function(devices) { - if(!devices.video) { - OT.warn('Setting video constraint to false, there are no video sources'); - _publishProperties.constraints.video = false; - } - if(!devices.audio) { - OT.warn('Setting audio constraint to false, there are no audio sources'); - _publishProperties.constraints.audio = false; - } - OT.$.getUserMedia( - _publishProperties.constraints, - OT.$.bind(onStreamAvailable, this), - OT.$.bind(onStreamAvailableError, this), - OT.$.bind(onAccessDialogOpened, this), - OT.$.bind(onAccessDialogClosed, this), - OT.$.bind(onAccessDenied, this) - ); - }, this)); + /** + * Toggles video on and off. Starts subscribing to video (if it is available and + * currently not being subscribed to) when the value is true; + * stops subscribing to video (if it is currently being subscribed to) when the + * value is false. + *

    + * Note: This method only affects the local playback of video. It has no impact on + * the video for other connections subscribing to the same stream. If the Publsher is not + * publishing video, enabling the Subscriber video will have no practical video. + *

    + * + * @param {Boolean} value Whether to start subscribing to video (true) or not + * (false). + * + * @return {Subscriber} The Subscriber object. This lets you chain method calls, as in the + * following: + * + *
    mySubscriber.subscribeToVideo(true).subscribeToAudio(false);
    + * + * @see subscribeToAudio() + * @see Session.subscribe() + * @see StreamPropertyChangedEvent + * + * @method #subscribeToVideo + * @memberOf Subscriber + */ + this.subscribeToVideo = function(pValue, reason) { + var value = OT.$.castToBoolean(pValue, true); + + setAudioOnly(!(value && _stream.hasVideo)); + if ( value && _container && _container.video()) { + _container.loading(value); + _container.video().whenTimeIncrements(function() { + _container.loading(false); }, this); + } - return this; - }; + if (_chrome && _chrome.videoDisabledIndicator) { + _chrome.videoDisabledIndicator.disableVideo(false); + } - /** - * Starts publishing audio (if it is currently not being published) - * when the value is true; stops publishing audio - * (if it is currently being published) when the value is false. - * - * @param {Boolean} value Whether to start publishing audio (true) - * or not (false). - * - * @see OT.initPublisher() - * @see Stream.hasAudio - * @see StreamPropertyChangedEvent - * @method #publishAudio - * @memberOf Publisher - */ - this.publishAudio = function(value) { - _publishProperties.publishAudio = value; + if (_peerConnection) { + _peerConnection.subscribeToVideo(value); - if (_microphone) { - _microphone.muted(!value); + if (_session && _stream && (value !== _properties.subscribeToVideo || + reason !== _lastSubscribeToVideoReason)) { + _stream.setChannelActiveState('video', value, reason); } + } - if (_chrome) { - _chrome.muteButton.muted(!value); - } + _properties.subscribeToVideo = value; + _lastSubscribeToVideoReason = reason; - if (_session && _stream) { - _stream.setChannelActiveState('audio', value); - } + if (reason !== 'loading') { + this.dispatchEvent(new OT.VideoEnabledChangedEvent( + value ? 'videoEnabled' : 'videoDisabled', + { + reason: reasonMap[reason] || 'subscribeToVideo' + } + )); + } - return this; - }; + return this; + }; + this.isSubscribing = function() { + return _state.isSubscribing(); + }; - /** - * Starts publishing video (if it is currently not being published) - * when the value is true; stops publishing video - * (if it is currently being published) when the value is false. - * - * @param {Boolean} value Whether to start publishing video (true) - * or not (false). - * - * @see OT.initPublisher() - * @see Stream.hasVideo - * @see StreamPropertyChangedEvent - * @method #publishVideo - * @memberOf Publisher - */ - this.publishVideo = function(value) { - var oldValue = _publishProperties.publishVideo; - _publishProperties.publishVideo = value; + this.isWebRTC = true; - if (_session && _stream && _publishProperties.publishVideo !== oldValue) { - _stream.setChannelActiveState('video', value); - } + this.isLoading = function() { + return _container && _container.loading(); + }; - // We currently do this event if the value of publishVideo has not changed - // This is because the state of the video tracks enabled flag may not match - // the value of publishVideo at this point. This will be tidied up shortly. - if (_webRTCStream) { - var videoTracks = _webRTCStream.getVideoTracks(); - for (var i=0, num=videoTracks.length; itrue. When you pass in false, the frame rate of the video stream + * is not restricted. + *

    + * When the frame rate is restricted, the Subscriber video frame will update once or less per + * second. + *

    + * This feature is only available in sessions that use the OpenTok Media Router (sessions with + * the media mode + * set to routed), not in sessions with the media mode set to relayed. In relayed sessions, + * calling this method has no effect. + *

    + * Restricting the subscriber frame rate has the following benefits: + *

      + *
    • It reduces CPU usage.
    • + *
    • It reduces the network bandwidth consumed.
    • + *
    • It lets you subscribe to more streams simultaneously.
    • + *
    + *

    + * Reducing a subscriber's frame rate has no effect on the frame rate of the video in + * other clients. + * + * @param {Boolean} value Whether to restrict the Subscriber's video frame rate + * (true) or not (false). + * + * @return {Subscriber} The Subscriber object. This lets you chain method calls, as in the + * following: + * + *

    mySubscriber.restrictFrameRate(false).subscribeToAudio(true);
    + * + * @method #restrictFrameRate + * @memberOf Subscriber + */ + this.restrictFrameRate = function(val) { + OT.debug('OT.Subscriber.restrictFrameRate(' + val + ')'); - /** - * Deletes the Publisher object and removes it from the HTML DOM. - *

    - * The Publisher object dispatches a destroyed event when the DOM - * element is removed. - *

    - * @method #destroy - * @memberOf Publisher - * @return {Publisher} The Publisher. - */ - this.destroy = function(/* unused */ reason, quiet) { - if (_state.isDestroyed()) return; - _state.set('Destroyed'); + logAnalyticsEvent('restrictFrameRate', val.toString(), {streamId: _stream.id}); - reset(); + if (_session.sessionInfo.p2pEnabled) { + OT.warn('OT.Subscriber.restrictFrameRate: Cannot restrictFrameRate on a P2P session'); + } - if (quiet !== true) { - this.dispatchEvent( - new OT.DestroyedEvent( - OT.Event.names.PUBLISHER_DESTROYED, - this, - reason - ), - OT.$.bind(this.off,this) - ); - } + if (typeof val !== 'boolean') { + OT.error('OT.Subscriber.restrictFrameRate: expected a boolean value got a ' + typeof val); + } else { + _frameRateRestricted = val; + _stream.setRestrictFrameRate(val); + } + return this; + }; - return this; - }; + this.on('styleValueChanged', updateChromeForStyleChange, this); - /** - * @methodOf Publisher - * @private - */ - this.disconnect = function() { - // Close the connection to each of our subscribers - for (var fromConnectionId in _peerConnections) { - this.cleanupSubscriber(fromConnectionId); + this._ = { + archivingStatus: function(status) { + if(_chrome) { + _chrome.archive.setArchiving(status); } - }; + } + }; - this.cleanupSubscriber = function(fromConnectionId) { - var pc = _peerConnections[fromConnectionId]; + _state = new OT.SubscribingState(stateChangeFailed); - if (pc) { - pc.destroy(); - delete _peerConnections[fromConnectionId]; + OT.debug('OT.Subscriber: subscribe to ' + _stream.id); - logAnalyticsEvent('disconnect', 'PeerConnection', - 'subscriberConnection', fromConnectionId); - } - }; + _state.set('Init'); + if (!_stream) { + // @todo error + OT.error('OT.Subscriber: No stream parameter.'); + return false; + } - this.processMessage = function(type, fromConnection, message) { - OT.debug('OT.Publisher.processMessage: Received ' + type + ' from ' + fromConnection.id); - OT.debug(message); + _stream.on({ + updated: streamUpdated, + destroyed: streamDestroyed + }, this); - switch (type) { - case 'unsubscribe': - this.cleanupSubscriber(message.content.connection.id); - break; + _fromConnectionId = _stream.connection.id; + _properties.name = _properties.name || _stream.name; + _properties.classNames = 'OT_root OT_subscriber'; - default: - var peerConnection = createPeerConnectionForRemote.call(this, fromConnection); - peerConnection.processMessage(type, message); - } - }; + if (_properties.style) { + this.setStyle(_properties.style, null, true); + } + if (_properties.audioVolume) { + this.setAudioVolume(_properties.audioVolume); + } - /** - * Returns the base-64-encoded string of PNG data representing the Publisher video. - * - *

    You can use the string as the value for a data URL scheme passed to the src parameter of - * an image file, as in the following:

    - * - *
    -    *  var imgData = publisher.getImgData();
    -    *
    -    *  var img = document.createElement("img");
    -    *  img.setAttribute("src", "data:image/png;base64," + imgData);
    -    *  var imgWin = window.open("about:blank", "Screenshot");
    -    *  imgWin.document.write("<body></body>");
    -    *  imgWin.document.body.appendChild(img);
    -    * 
    - * - * @method #getImgData - * @memberOf Publisher - * @return {String} The base-64 encoded string. Returns an empty string if there is no video. - */ - - this.getImgData = function() { - if (!_loaded) { - OT.error('OT.Publisher.getImgData: Cannot getImgData before the Publisher is publishing.'); + _properties.subscribeToAudio = OT.$.castToBoolean(_properties.subscribeToAudio, true); + _properties.subscribeToVideo = OT.$.castToBoolean(_properties.subscribeToVideo, true); - return null; - } + _container = new OT.WidgetView(targetElement, _properties); + this.id = _domId = _container.domId(); + this.element = _container.domElement; - return _targetElement.imgData(); - }; + _createChrome.call(this); + _startConnectingTime = OT.$.now(); - // API Compatibility layer for Flash Publisher, this could do with some tidyup. - this._ = { - publishToSession: OT.$.bind(function(session) { - // Add session property to Publisher - this.session = _session = session; + if (_stream.connection.id !== _session.connection.id) { + logAnalyticsEvent('createPeerConnection', 'Attempt', ''); - var createStream = function() { + _state.set('ConnectingToPeer'); - var streamWidth, - streamHeight; + _peerConnection = new OT.SubscriberPeerConnection(_stream.connection, _session, + _stream, this, _properties); - // Bail if this.session is gone, it means we were unpublished - // before createStream could finish. - if (!_session) return; + _peerConnection.on({ + disconnected: onDisconnected, + error: onPeerConnectionFailure, + remoteStreamAdded: onRemoteStreamAdded, + remoteStreamRemoved: onRemoteStreamRemoved, + qos: recordQOS + }, this); - _state.set('PublishingToSession'); + // initialize the peer connection AFTER we've added the event listeners + _peerConnection.init(); - var onStreamRegistered = OT.$.bind(function(err, streamId, message) { - if (err) { - // @todo we should respect err.code here and translate it to the local - // client equivalent. - logAnalyticsEvent('publish', 'Failure', 'reason', - 'Publish:' + OT.ExceptionCodes.UNABLE_TO_PUBLISH + ':' + err.message); - if (_state.isAttemptingToPublish()) { - this.trigger('publishComplete', new OT.Error(OT.ExceptionCodes.UNABLE_TO_PUBLISH, - err.message)); - } - return; - } + if (OT.$.hasCapabilities('audioOutputLevelStat')) { + _audioLevelSampler = new OT.GetStatsAudioLevelSampler(_peerConnection, 'out'); + } else if (OT.$.hasCapabilities('webAudioCapableRemoteStream')) { + _audioLevelSampler = new OT.AnalyserAudioLevelSampler(OT.audioContext()); + } + + if(_audioLevelSampler) { + var subscriber = this; + // sample with interval to minimise disturbance on animation loop but dispatch the + // event with RAF since the main purpose is animation of a meter + _audioLevelRunner = new OT.IntervalRunner(function() { + _audioLevelSampler.sample(function(audioOutputLevel) { + if (audioOutputLevel !== null) { + OT.$.requestAnimationFrame(function() { + subscriber.dispatchEvent( + new OT.AudioLevelUpdatedEvent(audioOutputLevel)); + }); + } + }); + }, 60); + } + } else { + logAnalyticsEvent('createPeerConnection', 'Attempt', ''); - this.streamId = _streamId = streamId; - _iceServers = OT.Raptor.parseIceServers(message); - }, this); + var publisher = _session.getPublisherForStream(_stream); + if(!(publisher && publisher._.webRtcStream())) { + this.trigger('subscribeComplete', new OT.Error(null, 'InvalidStreamID')); + return this; + } + + // Subscribe to yourself edge-case + onRemoteStreamAdded.call(this, publisher._.webRtcStream()); + } + + logConnectivityEvent('Attempt', {streamId: _stream.id}); + + + /** + * Dispatched periodically to indicate the subscriber's audio level. The event is dispatched + * up to 60 times per second, depending on the browser. The audioLevel property + * of the event is audio level, from 0 to 1.0. See {@link AudioLevelUpdatedEvent} for more + * information. + *

    + * The following example adjusts the value of a meter element that shows volume of the + * subscriber. Note that the audio level is adjusted logarithmically and a moving average + * is applied: + *

    + * var movingAvg = null;
    + * subscriber.on('audioLevelUpdated', function(event) {
    + *   if (movingAvg === null || movingAvg <= event.audioLevel) {
    + *     movingAvg = event.audioLevel;
    + *   } else {
    + *     movingAvg = 0.7 * movingAvg + 0.3 * event.audioLevel;
    + *   }
    + *
    + *   // 1.5 scaling to map the -30 - 0 dBm range to [0,1]
    + *   var logLevel = (Math.log(movingAvg) / Math.LN10) / 1.5 + 1;
    + *   logLevel = Math.min(Math.max(logLevel, 0), 1);
    + *   document.getElementById('subscriberMeter').value = logLevel;
    + * });
    + * 
    + *

    This example shows the algorithm used by the default audio level indicator displayed + * in an audio-only Subscriber. + * + * @name audioLevelUpdated + * @event + * @memberof Subscriber + * @see AudioLevelUpdatedEvent + */ + +/** +* Dispatched when the video for the subscriber is disabled. +*

    +* The reason property defines the reason the video was disabled. This can be set to +* one of the following values: +*

    +* +*

      +* +*
    • "publishVideo" — The publisher stopped publishing video by calling +* publishVideo(false).
    • +* +*
    • "quality" — The OpenTok Media Router stopped sending video +* to the subscriber based on stream quality changes. This feature of the OpenTok Media +* Router has a subscriber drop the video stream when connectivity degrades. (The subscriber +* continues to receive the audio stream, if there is one.) +*

      +* Before sending this event, when the Subscriber's stream quality deteriorates to a level +* that is low enough that the video stream is at risk of being disabled, the Subscriber +* dispatches a videoDisableWarning event. +*

      +* If connectivity improves to support video again, the Subscriber object dispatches +* a videoEnabled event, and the Subscriber resumes receiving video. +*

      +* By default, the Subscriber displays a video disabled indicator when a +* videoDisabled event with this reason is dispatched and removes the indicator +* when the videoDisabled event with this reason is dispatched. You can control +* the display of this icon by calling the setStyle() method of the Subscriber, +* setting the videoDisabledDisplayMode property(or you can set the style when +* calling the Session.subscribe() method, setting the style property +* of the properties parameter). +*

      +* This feature is only available in sessions that use the OpenTok Media Router (sessions with +* the media mode +* set to routed), not in sessions with the media mode set to relayed. +*

      +* You can disable this audio-only fallback feature, by setting the +* audioFallbackEnabled property to false in the options you pass +* into the OT.initPublisher() method on the publishing client. (See +* OT.initPublisher().) +*

    • +* +*
    • "subscribeToVideo" — The subscriber started or stopped subscribing to +* video, by calling subscribeToVideo(false). +*
    • +* +*
    +* +* @see VideoEnabledChangedEvent +* @see event:videoDisableWarning +* @see event:videoEnabled +* @name videoDisabled +* @event +* @memberof Subscriber +*/ - // We set the streamWidth and streamHeight to be the minimum of the requested - // resolution and the actual resolution. - if (_publishProperties.videoDimensions) { - streamWidth = Math.min(_publishProperties.videoDimensions.width, - _targetElement.videoWidth() || 640); - streamHeight = Math.min(_publishProperties.videoDimensions.height, - _targetElement.videoHeight() || 480); - } else { - streamWidth = _targetElement.videoWidth() || 640; - streamHeight = _targetElement.videoHeight() || 480; - } +/** +* Dispatched when the OpenTok Media Router determines that the stream quality has degraded +* and the video will be disabled if the quality degrades more. If the quality degrades further, +* the Subscriber disables the video and dispatches a videoDisabled event. +*

    +* By default, the Subscriber displays a video disabled warning indicator when this event +* is dispatched (and the video is disabled). You can control the display of this icon by +* calling the setStyle() method and setting the +* videoDisabledDisplayMode property (or you can set the style when calling +* the Session.subscribe() method and setting the style property +* of the properties parameter). +*

    +* This feature is only available in sessions that use the OpenTok Media Router (sessions with +* the media mode +* set to routed), not in sessions with the media mode set to relayed. +* +* @see Event +* @see event:videoDisabled +* @see event:videoDisableWarningLifted +* @name videoDisableWarning +* @event +* @memberof Subscriber +*/ - session._.streamCreate( - _publishProperties && _publishProperties.name ? _publishProperties.name : '', - OT.VideoOrientation.ROTATED_NORMAL, - streamWidth, - streamHeight, - _publishProperties.publishAudio, - _publishProperties.publishVideo, - _publishProperties.frameRate, - onStreamRegistered - ); - }; +/** +* Dispatched when the OpenTok Media Router determines that the stream quality has improved +* to the point at which the video being disabled is not an immediate risk. This event is +* dispatched after the Subscriber object dispatches a videoDisableWarning event. +*

    +* This feature is only available in sessions that use the OpenTok Media Router (sessions with +* the media mode +* set to routed), not in sessions with the media mode set to relayed. +* +* @see Event +* @see event:videoDisabled +* @see event:videoDisableWarning +* @name videoDisableWarningLifted +* @event +* @memberof Subscriber +*/ - if (_loaded) createStream.call(this); - else this.on('initSuccess', createStream, this); +/** +* Dispatched when the OpenTok Media Router resumes sending video to the subscriber +* after video was previously disabled. +*

    +* The reason property defines the reason the video was enabled. This can be set to +* one of the following values: +*

    +* +*

      +* +*
    • "publishVideo" — The publisher started publishing video by calling +* publishVideo(true).
    • +* +*
    • "quality" — The OpenTok Media Router resumed sending video +* to the subscriber based on stream quality changes. This feature of the OpenTok Media +* Router has a subscriber drop the video stream when connectivity degrades and then resume +* the video stream if the stream quality improves. +*

      +* This feature is only available in sessions that use the OpenTok Media Router (sessions with +* the media mode +* set to routed), not in sessions with the media mode set to relayed. +*

    • +* +*
    • "subscribeToVideo" — The subscriber started or stopped subscribing to +* video, by calling subscribeToVideo(false). +*
    • +* +*
    +* +*

    +* To prevent video from resuming, in the videoEnabled event listener, +* call subscribeToVideo(false) on the Subscriber object. +* +* @see VideoEnabledChangedEvent +* @see event:videoDisabled +* @name videoEnabled +* @event +* @memberof Subscriber +*/ - logAnalyticsEvent('publish', 'Attempt', 'streamType', 'WebRTC'); +/** +* Dispatched when the Subscriber element is removed from the HTML DOM. When this event is +* dispatched, you may choose to adjust or remove HTML DOM elements related to the subscriber. +* @see Event +* @name destroyed +* @event +* @memberof Subscriber +*/ - return this; - }, this), +/** +* Dispatched when the video dimensions of the video change. This can occur when the +* stream.videoType property is set to "screen" (for a screen-sharing +* video stream), and the user resizes the window being captured. It can also occur if the video +* is being published by a mobile device and the user rotates the device (causing the camera +* orientation to change). +* @name videoDimensionsChanged +* @event +* @memberof Subscriber +*/ - unpublishFromSession: OT.$.bind(function(session, reason) { - if (!_session || session.id !== _session.id) { - OT.warn('The publisher ' + _guid + ' is trying to unpublish from a session ' + - session.id + ' it is not attached to (it is attached to ' + - (_session && _session.id || 'no session') + ')'); - return this; - } +}; - if (session.isConnected() && this.stream) { - session._.streamDestroy(this.stream.id); - } +// tb_require('../helpers/helpers.js') +// tb_require('../helpers/lib/config.js') +// tb_require('./analytics.js') +// tb_require('./events.js') +// tb_require('./error_handling.js') +// tb_require('./system_requirements.js') +// tb_require('./stream.js') +// tb_require('./connection.js') +// tb_require('./environment_loader.js') +// tb_require('./session_info.js') +// tb_require('./messaging/raptor/raptor.js') +// tb_require('./messaging/raptor/session-dispatcher.js') +// tb_require('./qos_testing/webrtc_test.js') +// tb_require('./qos_testing/http_test.js') - // Disconnect immediately, rather than wait for the WebSocket to - // reply to our destroyStream message. - this.disconnect(); - this.session = _session = null; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT, Promise */ - // We're back to being a stand-alone publisher again. - if (!_state.isDestroyed()) _state.set('MediaBound'); - logAnalyticsEvent('unpublish', 'Success', 'sessionId', session.id); +/** + * The Session object returned by the OT.initSession() method provides access to + * much of the OpenTok functionality. + * + * @class Session + * @augments EventDispatcher + * + * @property {Capabilities} capabilities A {@link Capabilities} object that includes information + * about the capabilities of the client. All properties of the capabilities object + * are undefined until you have connected to a session and the Session object has dispatched the + * sessionConnected event. + * @property {Connection} connection The {@link Connection} object for this session. The + * connection property is only available once the Session object dispatches the sessionConnected + * event. The Session object asynchronously dispatches a sessionConnected event in response + * to a successful call to the connect() method. See: connect and + * {@link Connection}. + * @property {String} sessionId The session ID for this session. You pass this value into the + * OT.initSession() method when you create the Session object. (Note: a Session + * object is not connected to the OpenTok server until you call the connect() method of the + * object and the object dispatches a connected event. See {@link OT.initSession} and + * {@link connect}). + * For more information on sessions and session IDs, see + * Session creation. + */ +OT.Session = function(apiKey, sessionId) { + OT.$.eventing(this); - this._.streamDestroyed(reason); + // Check that the client meets the minimum requirements, if they don't the upgrade + // flow will be triggered. + if (!OT.checkSystemRequirements()) { + OT.upgradeSystemRequirements(); + return; + } - return this; - }, this), + if(sessionId == null) { + sessionId = apiKey; + apiKey = null; + } + + this.id = this.sessionId = sessionId; + + var _initialConnection = true, + _apiKey = apiKey, + _token, + _session = this, + _sessionId = sessionId, + _socket, + _widgetId = OT.$.uuid(), + _connectionId = OT.$.uuid(), + sessionConnectFailed, + sessionDisconnectedHandler, + connectionCreatedHandler, + connectionDestroyedHandler, + streamCreatedHandler, + streamPropertyModifiedHandler, + streamDestroyedHandler, + archiveCreatedHandler, + archiveDestroyedHandler, + archiveUpdatedHandler, + init, + reset, + disconnectComponents, + destroyPublishers, + destroySubscribers, + connectMessenger, + getSessionInfo, + onSessionInfoResponse, + permittedTo, + _connectivityAttemptPinger, + dispatchError; - streamDestroyed: OT.$.bind(function(reason) { - if(OT.$.arrayIndexOf(['reset'], reason) < 0) { - var event = new OT.StreamEvent('streamDestroyed', _stream, reason, true); - var defaultAction = OT.$.bind(function() { - if(!event.isDefaultPrevented()) { - this.destroy(); - } - }, this); - this.dispatchEvent(event, defaultAction); - } - }, this), - archivingStatus: OT.$.bind(function(status) { - if(_chrome) { - _chrome.archive.setArchiving(status); - } + var setState = OT.$.statable(this, [ + 'disconnected', 'connecting', 'connected', 'disconnecting' + ], 'disconnected'); + + this.connection = null; + this.connections = new OT.$.Collection(); + this.streams = new OT.$.Collection(); + this.archives = new OT.$.Collection(); - return status; - }, this), - webRtcStream: function() { - return _webRTCStream; - } - }; +//-------------------------------------- +// MESSAGE HANDLERS +//-------------------------------------- - this.detectDevices = function() { - OT.warn('Fixme: Haven\'t implemented detectDevices'); - }; +// The duplication of this and sessionConnectionFailed will go away when +// session and messenger are refactored + sessionConnectFailed = function(reason, code) { + setState('disconnected'); - this.detectMicActivity = function() { - OT.warn('Fixme: Haven\'t implemented detectMicActivity'); - }; + OT.error(reason); - this.getEchoCancellationMode = function() { - OT.warn('Fixme: Haven\'t implemented getEchoCancellationMode'); - return 'fullDuplex'; - }; + this.trigger('sessionConnectFailed', + new OT.Error(code || OT.ExceptionCodes.CONNECT_FAILED, reason)); - this.setMicrophoneGain = function() { - OT.warn('Fixme: Haven\'t implemented setMicrophoneGain'); - }; + OT.handleJsException(reason, code || OT.ExceptionCodes.CONNECT_FAILED, { + session: this + }); + }; - this.getMicrophoneGain = function() { - OT.warn('Fixme: Haven\'t implemented getMicrophoneGain'); - return 0.5; - }; + sessionDisconnectedHandler = function(event) { + var reason = event.reason; + if(reason === 'networkTimedout') { + reason = 'networkDisconnected'; + this.logEvent('Connect', 'TimeOutDisconnect', {reason: event.reason}); + } else { + this.logEvent('Connect', 'Disconnected', {reason: event.reason}); + } - this.setCamera = function() { - OT.warn('Fixme: Haven\'t implemented setCamera'); - }; + var publicEvent = new OT.SessionDisconnectEvent('sessionDisconnected', reason); - this.setMicrophone = function() { - OT.warn('Fixme: Haven\'t implemented setMicrophone'); - }; + reset(); + disconnectComponents.call(this, reason); + var defaultAction = OT.$.bind(function() { + // Publishers handle preventDefault'ing themselves + destroyPublishers.call(this, publicEvent.reason); + // Subscriers don't, destroy 'em if needed + if (!publicEvent.isDefaultPrevented()) destroySubscribers.call(this, publicEvent.reason); + }, this); - // Platform methods: + this.dispatchEvent(publicEvent, defaultAction); + }; - this.guid = function() { - return _guid; - }; + connectionCreatedHandler = function(connection) { + // We don't broadcast events for the symphony connection + if (connection.id.match(/^symphony\./)) return; + + this.dispatchEvent(new OT.ConnectionEvent( + OT.Event.names.CONNECTION_CREATED, + connection + )); + }; + + connectionDestroyedHandler = function(connection, reason) { + // We don't broadcast events for the symphony connection + if (connection.id.match(/^symphony\./)) return; + + // Don't delete the connection if it's ours. This only happens when + // we're about to receive a session disconnected and session disconnected + // will also clean up our connection. + if (connection.id === _socket.id()) return; + + this.dispatchEvent( + new OT.ConnectionEvent( + OT.Event.names.CONNECTION_DESTROYED, + connection, + reason + ) + ); + }; - this.videoElement = function() { - return _targetElement.domElement(); - }; + streamCreatedHandler = function(stream) { + if(stream.connection.id !== this.connection.id) { + this.dispatchEvent(new OT.StreamEvent( + OT.Event.names.STREAM_CREATED, + stream, + null, + false + )); + } + }; - this.setStream = assignStream; + streamPropertyModifiedHandler = function(event) { + var stream = event.target, + propertyName = event.changedProperty, + newValue = event.newValue; - this.isWebRTC = true; + if (propertyName === 'videoDisableWarning' || propertyName === 'audioDisableWarning') { + return; // These are not public properties, skip top level event for them. + } - this.isLoading = function() { - return _container && _container.loading(); - }; + if (propertyName === 'orientation') { + propertyName = 'videoDimensions'; + newValue = {width: newValue.width, height: newValue.height}; + } - this.videoWidth = function() { - return _targetElement.videoWidth(); - }; + this.dispatchEvent(new OT.StreamPropertyChangedEvent( + OT.Event.names.STREAM_PROPERTY_CHANGED, + stream, + propertyName, + event.oldValue, + newValue + )); + }; - this.videoHeight = function() { - return _targetElement.videoHeight(); - }; + streamDestroyedHandler = function(stream, reason) { - // Make read-only: element, guid, _.webRtcStream + // if the stream is one of ours we delegate handling + // to the publisher itself. + if(stream.connection.id === this.connection.id) { + OT.$.forEach(OT.publishers.where({ streamId: stream.id }), OT.$.bind(function(publisher) { + publisher._.unpublishFromSession(this, reason); + }, this)); + return; + } - this.on('styleValueChanged', updateChromeForStyleChange, this); - _state = new OT.PublishingState(stateChangeFailed); + var event = new OT.StreamEvent('streamDestroyed', stream, reason, true); - this.accessAllowed = false; - - /** - * Dispatched when the user has clicked the Allow button, granting the - * app access to the camera and microphone. The Publisher object has an - * accessAllowed property which indicates whether the user - * has granted access to the camera and microphone. - * @see Event - * @name accessAllowed - * @event - * @memberof Publisher - */ - - /** - * Dispatched when the user has clicked the Deny button, preventing the - * app from having access to the camera and microphone. - *

    - * The default behavior of this event is to display a user interface element - * in the Publisher object, indicating that that user has denied access to - * the camera and microphone. Call the preventDefault() method - * method of the event object in the event listener to prevent this message - * from being displayed. - * @see Event - * @name accessDenied - * @event - * @memberof Publisher - */ - - /** - * Dispatched when the Allow/Deny dialog box is opened. (This is the dialog box in which - * the user can grant the app access to the camera and microphone.) - *

    - * The default behavior of this event is to display a message in the browser that instructs - * the user how to enable the camera and microphone. Call the preventDefault() - * method of the event object in the event listener to prevent this message from being displayed. - * @see Event - * @name accessDialogOpened - * @event - * @memberof Publisher - */ - - /** - * Dispatched when the Allow/Deny box is closed. (This is the dialog box in which the - * user can grant the app access to the camera and microphone.) - * @see Event - * @name accessDialogClosed - * @event - * @memberof Publisher - */ + var defaultAction = OT.$.bind(function() { + if (!event.isDefaultPrevented()) { + // If we are subscribed to any of the streams we should unsubscribe + OT.$.forEach(OT.subscribers.where({streamId: stream.id}), function(subscriber) { + if (subscriber.session.id === this.id) { + if(subscriber.stream) { + subscriber.destroy('streamDestroyed'); + } + } + }, this); + } else { + // @TODO Add a one time warning that this no longer cleans up the publisher + } + }, this); - /** - * Dispatched periodically to indicate the publisher's audio level. The event is dispatched - * up to 60 times per second, depending on the browser. The audioLevel property - * of the event is audio level, from 0 to 1.0. See {@link AudioLevelUpdatedEvent} for more - * information. - *

    - * The following example adjusts the value of a meter element that shows volume of the - * publisher. Note that the audio level is adjusted logarithmically and a moving average - * is applied: - *

    - *

    -    * var movingAvg = null;
    -    * publisher.on('audioLevelUpdated', function(event) {
    -    *   if (movingAvg === null || movingAvg <= event.audioLevel) {
    -    *     movingAvg = event.audioLevel;
    -    *   } else {
    -    *     movingAvg = 0.7 * movingAvg + 0.3 * event.audioLevel;
    -    *   }
    -    *
    -    *   // 1.5 scaling to map the -30 - 0 dBm range to [0,1]
    -    *   var logLevel = (Math.log(movingAvg) / Math.LN10) / 1.5 + 1;
    -    *   logLevel = Math.min(Math.max(logLevel, 0), 1);
    -    *   document.getElementById('publisherMeter').value = logLevel;
    -    * });
    -    * 
    - *

    This example shows the algorithm used by the default audio level indicator displayed - * in an audio-only Publisher. - * - * @name audioLevelUpdated - * @event - * @memberof Publisher - * @see AudioLevelUpdatedEvent - */ - - /** - * The publisher has started streaming to the session. - * @name streamCreated - * @event - * @memberof Publisher - * @see StreamEvent - * @see Session.publish() - */ - - /** - * The publisher has stopped streaming to the session. The default behavior is that - * the Publisher object is removed from the HTML DOM). The Publisher object dispatches a - * destroyed event when the element is removed from the HTML DOM. If you call the - * preventDefault() method of the event object in the event listener, the default - * behavior is prevented, and you can, optionally, retain the Publisher for reuse or clean it up - * using your own code. - * @name streamDestroyed - * @event - * @memberof Publisher - * @see StreamEvent - */ - - /** - * Dispatched when the Publisher element is removed from the HTML DOM. When this event - * is dispatched, you may choose to adjust or remove HTML DOM elements related to the publisher. - * @name destroyed - * @event - * @memberof Publisher - */ + this.dispatchEvent(event, defaultAction); }; - // Helper function to generate unique publisher ids - OT.Publisher.nextId = OT.$.uuid; + archiveCreatedHandler = function(archive) { + this.dispatchEvent(new OT.ArchiveEvent('archiveStarted', archive)); + }; -})(window); -!(function() { + archiveDestroyedHandler = function(archive) { + this.dispatchEvent(new OT.ArchiveEvent('archiveDestroyed', archive)); + }; -/** - * The Subscriber object is a representation of the local video element that is playing back - * a remote stream. The Subscriber object includes methods that let you disable and enable - * local audio playback for the subscribed stream. The subscribe() method of the - * {@link Session} object returns a Subscriber object. - * - * @property {Element} element The HTML DOM element containing the Subscriber. - * @property {String} id The DOM ID of the Subscriber. - * @property {Stream} stream The stream to which you are subscribing. - * - * @class Subscriber - * @augments EventDispatcher - */ - OT.Subscriber = function(targetElement, options) { - var _widgetId = OT.$.uuid(), - _domId = targetElement || _widgetId, - _container, - _streamContainer, - _chrome, - _audioLevelMeter, - _stream, - _fromConnectionId, - _peerConnection, - _session = options.session, - _subscribeStartTime, - _startConnectingTime, - _properties = OT.$.clone(options), - _analytics = new OT.Analytics(), - _audioVolume = 100, - _state, - _prevStats, - _lastSubscribeToVideoReason, - _audioLevelCapable = OT.$.hasCapabilities('audioOutputLevelStat') || - OT.$.hasCapabilities('webAudioCapableRemoteStream'), - _audioLevelSampler, - _audioLevelRunner, - _frameRateRestricted = false; - - this.id = _domId; - this.widgetId = _widgetId; - this.session = _session; - - _prevStats = { - timeStamp: OT.$.now() - }; - - if (!_session) { - OT.handleJsException('Subscriber must be passed a session option', 2000, { - session: _session, - target: this - }); + archiveUpdatedHandler = function(event) { + var archive = event.target, + propertyName = event.changedProperty, + newValue = event.newValue; - return; + if(propertyName === 'status' && newValue === 'stopped') { + this.dispatchEvent(new OT.ArchiveEvent('archiveStopped', archive)); + } else { + this.dispatchEvent(new OT.ArchiveEvent('archiveUpdated', archive)); } + }; - OT.$.eventing(this, false); - - if(_audioLevelCapable) { - this.on({ - 'audioLevelUpdated:added': function(count) { - if (count === 1 && _audioLevelRunner) { - _audioLevelRunner.start(); - } - }, - 'audioLevelUpdated:removed': function(count) { - if (count === 0 && _audioLevelRunner) { - _audioLevelRunner.stop(); - } - } - }); - } + init = function() { + _session.token = _token = null; + setState('disconnected'); + _session.connection = null; + _session.capabilities = new OT.Capabilities([]); + _session.connections.destroy(); + _session.streams.destroy(); + _session.archives.destroy(); + }; - OT.StylableComponent(this, { - nameDisplayMode: 'auto', - buttonDisplayMode: 'auto', - audioLevelDisplayMode: 'auto', - videoDisabledIndicatorDisplayMode: 'auto', - backgroundImageURI: null, - showArchiveStatus: true, - showMicButton: true, - bugDisplayMode: 'auto' - }); - - var logAnalyticsEvent = function(action, variation, payloadType, payload) { - /* jshint camelcase:false*/ - _analytics.logEvent({ - action: action, - variation: variation, - payload_type: payloadType, - payload: payload, - stream_id: _stream ? _stream.id : null, - session_id: _session ? _session.sessionId : null, - connection_id: _session && _session.isConnected() ? - _session.connection.connectionId : null, - partner_id: _session && _session.isConnected() ? _session.sessionInfo.partnerId : null, - widget_id: _widgetId, - widget_type: 'Subscriber' - }); - }, + // Put ourselves into a pristine state + reset = function() { + // reset connection id now so that following calls to testNetwork and connect will share + // the same new session id. We need to reset here because testNetwork might be call after + // and it is always called before the session is connected + // on initial connection we don't reset + _connectionId = OT.$.uuid(); + init(); + }; - recordQOS = OT.$.bind(function(parsedStats) { - if(_state.isSubscribing() && _session && _session.isConnected()) { - /*jshint camelcase:false */ - var QoSBlob = { - widget_type: 'Subscriber', - stream_type : 'WebRTC', - width: _container ? OT.$.width(_container.domElement) : undefined, - height: _container ? OT.$.height(_container.domElement) : undefined, - session_id: _session ? _session.sessionId : null, - connectionId: _session ? _session.connection.connectionId : null, - media_server_name: _session ? _session.sessionInfo.messagingServer : null, - p2pFlag: _session ? _session.sessionInfo.p2pEnabled : false, - partner_id: _session ? _session.apiKey : null, - stream_id: _stream.id, - widget_id: _widgetId, - version: OT.properties.version, - duration: parseInt(OT.$.now() - _subscribeStartTime, 10), - remote_connection_id: _stream.connection.connectionId - }; + disconnectComponents = function(reason) { + OT.$.forEach(OT.publishers.where({session: this}), function(publisher) { + publisher.disconnect(reason); + }); - _analytics.logQOS( OT.$.extend(QoSBlob, parsedStats) ); - this.trigger('qos', parsedStats); - } - }, this), + OT.$.forEach(OT.subscribers.where({session: this}), function(subscriber) { + subscriber.disconnect(); + }); + }; + destroyPublishers = function(reason) { + OT.$.forEach(OT.publishers.where({session: this}), function(publisher) { + publisher._.streamDestroyed(reason); + }); + }; - stateChangeFailed = function(changeFailed) { - OT.error('Subscriber State Change Failed: ', changeFailed.message); - OT.debug(changeFailed); - }, + destroySubscribers = function(reason) { + OT.$.forEach(OT.subscribers.where({session: this}), function(subscriber) { + subscriber.destroy(reason); + }); + }; - onLoaded = function() { - if (_state.isSubscribing() || !_streamContainer) return; + connectMessenger = function() { + OT.debug('OT.Session: connecting to Raptor'); - OT.debug('OT.Subscriber.onLoaded'); + var socketUrl = this.sessionInfo.messagingURL, + symphonyUrl = OT.properties.symphonyAddresss || this.sessionInfo.symphonyAddress; - _state.set('Subscribing'); - _subscribeStartTime = OT.$.now(); + _socket = new OT.Raptor.Socket(_connectionId, _widgetId, socketUrl, symphonyUrl, + OT.SessionDispatcher(this)); - logAnalyticsEvent('createPeerConnection', 'Success', 'pcc|hasRelayCandidates', [ - parseInt(_subscribeStartTime - _startConnectingTime, 10), - _peerConnection && _peerConnection.hasRelayCandidates() - ].join('|')); - _container.loading(false); + _socket.connect(_token, this.sessionInfo, OT.$.bind(function(error, sessionState) { + if (error) { + _socket = void 0; + this.logConnectivityEvent('Failure', error); - _createChrome.call(this); - if(_frameRateRestricted) { - _stream.setRestrictFrameRate(true); - } + sessionConnectFailed.call(this, error.message, error.code); + return; + } - this.trigger('subscribeComplete', null, this); - this.trigger('loaded', this); + OT.debug('OT.Session: Received session state from Raptor', sessionState); - logAnalyticsEvent('subscribe', 'Success', 'streamId', _stream.id); - }, + this.connection = this.connections.get(_socket.id()); + if(this.connection) { + this.capabilities = this.connection.permissions; + } - onDisconnected = function() { - OT.debug('OT.Subscriber has been disconnected from the Publisher\'s PeerConnection'); + setState('connected'); - if (_state.isAttemptingToSubscribe()) { - // subscribing error - _state.set('Failed'); - this.trigger('subscribeComplete', new OT.Error(null, 'ClientDisconnected')); + this.logConnectivityEvent('Success', null, {connectionId: this.connection.id}); - } else if (_state.isSubscribing()) { - _state.set('Failed'); + // Listen for our own connection's destroyed event so we know when we've been disconnected. + this.connection.on('destroyed', sessionDisconnectedHandler, this); - // we were disconnected after we were already subscribing - // probably do nothing? - } + // Listen for connection updates + this.connections.on({ + add: connectionCreatedHandler, + remove: connectionDestroyedHandler + }, this); - this.disconnect(); - }, + // Listen for stream updates + this.streams.on({ + add: streamCreatedHandler, + remove: streamDestroyedHandler, + update: streamPropertyModifiedHandler + }, this); - onPeerConnectionFailure = OT.$.bind(function(reason, peerConnection, prefix) { - if (_state.isAttemptingToSubscribe()) { - // We weren't subscribing yet so this was a failure in setting - // up the PeerConnection or receiving the initial stream. - logAnalyticsEvent('createPeerConnection', 'Failure', 'reason|hasRelayCandidates', [ - 'Subscriber PeerConnection Error: ' + reason, - _peerConnection && _peerConnection.hasRelayCandidates() - ].join('|')); - - _state.set('Failed'); - this.trigger('subscribeComplete', new OT.Error(null, reason)); - - } else if (_state.isSubscribing()) { - // we were disconnected after we were already subscribing - _state.set('Failed'); - this.trigger('error', reason); - } - - this.disconnect(); - - logAnalyticsEvent('subscribe', 'Failure', 'reason', - (prefix ? prefix : '') + ':Subscriber PeerConnection Error: ' + reason); - - OT.handleJsException('Subscriber PeerConnection Error: ' + reason, - OT.ExceptionCodes.P2P_CONNECTION_FAILED, { - session: _session, - target: this - } - ); - _showError.call(this, reason); - }, this), + this.archives.on({ + add: archiveCreatedHandler, + remove: archiveDestroyedHandler, + update: archiveUpdatedHandler + }, this); - onRemoteStreamAdded = function(webOTStream) { - OT.debug('OT.Subscriber.onRemoteStreamAdded'); + this.dispatchEvent( + new OT.SessionConnectEvent(OT.Event.names.SESSION_CONNECTED), OT.$.bind(function() { + this.connections._triggerAddEvents(); // { id: this.connection.id } + this.streams._triggerAddEvents(); // { id: this.stream.id } + this.archives._triggerAddEvents(); + }, this) + ); - _state.set('BindingRemoteStream'); + }, this)); + }; - // Disable the audio/video, if needed - this.subscribeToAudio(_properties.subscribeToAudio); + getSessionInfo = function() { + if (this.is('connecting')) { + OT.SessionInfo.get( + this, + OT.$.bind(onSessionInfoResponse, this), + OT.$.bind(function(error) { + sessionConnectFailed.call(this, error.message + + (error.code ? ' (' + error.code + ')' : ''), error.code); + }, this) + ); + } + }; - _lastSubscribeToVideoReason = 'loading'; - this.subscribeToVideo(_properties.subscribeToVideo, 'loading'); + onSessionInfoResponse = function(sessionInfo) { + if (this.is('connecting')) { + var overrides = OT.properties.sessionInfoOverrides; + this.sessionInfo = sessionInfo; + if (overrides != null && typeof overrides === 'object') { + this.sessionInfo = OT.$.defaults(overrides, this.sessionInfo); + } + if (this.sessionInfo.partnerId && this.sessionInfo.partnerId !== _apiKey) { + this.apiKey = _apiKey = this.sessionInfo.partnerId; + + var reason = 'Authentication Error: The API key does not match the token or session.'; + + var payload = { + code: OT.ExceptionCodes.AUTHENTICATION_ERROR, + message: reason + }; + this.logEvent('Failure', 'SessionInfo', payload); - var videoContainerOptions = { - error: onPeerConnectionFailure, - audioVolume: _audioVolume - }; + sessionConnectFailed.call(this, reason, OT.ExceptionCodes.AUTHENTICATION_ERROR); + } else { + connectMessenger.call(this); + } + } + }; - // This is a workaround for a bug in Chrome where a track disabled on - // the remote end doesn't fire loadedmetadata causing the subscriber to timeout - // https://jira.tokbox.com/browse/OPENTOK-15605 - var browser = OT.$.browserVersion(), - tracks, - reenableVideoTrack = false; - if (!_stream.hasVideo && browser.browser === 'Chrome' && browser.version >= 35) { - tracks = webOTStream.getVideoTracks(); - if(tracks.length > 0) { - tracks[0].enabled = false; - reenableVideoTrack = tracks[0]; - } - } + // Check whether we have permissions to perform the action. + permittedTo = OT.$.bind(function(action) { + return this.capabilities.permittedTo(action); + }, this); - _streamContainer = _container.bindVideo(webOTStream, - videoContainerOptions, - OT.$.bind(function(err) { - if (err) { - onPeerConnectionFailure(err.message || err, _peerConnection, 'VideoElement'); - return; - } + dispatchError = OT.$.bind(function(code, message, completionHandler) { + OT.dispatchError(code, message, completionHandler, this); + }, this); - // Continues workaround for https://jira.tokbox.com/browse/OPENTOK-15605 - if (reenableVideoTrack != null && _properties.subscribeToVideo) { - reenableVideoTrack.enabled = true; - } + this.logEvent = function(action, variation, payload, options) { + var event = { + action: action, + variation: variation, + payload: payload, + sessionId: _sessionId, + partnerId: _apiKey + }; - _streamContainer.orientation({ - width: _stream.videoDimensions.width, - height: _stream.videoDimensions.height, - videoOrientation: _stream.videoDimensions.orientation - }); + event.connectionId = _connectionId; - onLoaded.call(this, null); - }, this)); + if (options) event = OT.$.extend(options, event); + OT.analytics.logEvent(event); + }; - if (OT.$.hasCapabilities('webAudioCapableRemoteStream') && _audioLevelSampler) { - _audioLevelSampler.webOTStream = webOTStream; - } + /** + * @typedef {Object} Stats + * @property {number} bytesSentPerSecond + * @property {number} bytesReceivedPerSecond + * @property {number} packetLossRatio + * @property {number} rtt + */ - logAnalyticsEvent('createPeerConnection', 'StreamAdded', '', ''); - this.trigger('streamAdded', this); + function getTestNetworkConfig(token) { + return new Promise(function(resolve, reject) { + OT.$.getJSON( + [OT.properties.apiURL, '/v2/partner/', _apiKey, '/session/', _sessionId, '/connection/', + _connectionId, '/testNetworkConfig'].join(''), + { + headers: {'X-TB-TOKEN-AUTH': token} }, - - onRemoteStreamRemoved = function(webOTStream) { - OT.debug('OT.Subscriber.onStreamRemoved'); - - if (_streamContainer.stream === webOTStream) { - _streamContainer.destroy(); - _streamContainer = null; + function(errorEvent, response) { + if (errorEvent) { + var error = JSON.parse(errorEvent.target.responseText); + if (error.code === -1) { + reject(new OT.$.Error('Unexpected HTTP error codes ' + + errorEvent.target.status, '2001')); + } else if (error.code === 10001 || error.code === 10002) { + reject(new OT.$.Error(error.message, '1004')); + } else { + reject(new OT.$.Error(error.message, error.code)); + } + } else { + resolve(response); } + }); + }); + } + + /** + * @param {string} token + * @param {OT.Publisher} publisher + * @param {function(?OT.$.Error, Stats=)} callback + */ + this.testNetwork = function(token, publisher, callback) { + // intercept call to callback to log the result + var origCallback = callback; + callback = function loggingCallback(error, stats) { + if (error) { + _session.logEvent('testNetwork', 'Failure', { + failureCode: error.name || error.message || 'unknown' + }); + } else { + _session.logEvent('testNetwork', 'Success', stats); + } + origCallback(error, stats); + }; - this.trigger('streamRemoved', this); - }, + _session.logEvent('testNetwork', 'Attempt', {}); - streamDestroyed = function () { - this.disconnect(); - }, + if(this.isConnected()) { + callback(new OT.$.Error('Session connected, cannot test network', 1015)); + return; + } - streamUpdated = function(event) { + var webRtcStreamPromise = new Promise( + function(resolve, reject) { + var webRtcStream = publisher._.webRtcStream(); + if (webRtcStream) { + resolve(webRtcStream); + } else { - switch(event.changedProperty) { - case 'videoDimensions': - _streamContainer.orientation({ - width: event.newValue.width, - height: event.newValue.height, - videoOrientation: event.newValue.orientation - }); - break; + var onAccessAllowed = function() { + unbind(); + resolve(publisher._.webRtcStream()); + }; - case 'videoDisableWarning': - _chrome.videoDisabledIndicator.setWarning(event.newValue); - this.dispatchEvent(new OT.VideoDisableWarningEvent( - event.newValue ? 'videoDisableWarning' : 'videoDisableWarningLifted' - )); - break; + var onPublishComplete = function(error) { + if (error) { + unbind(); + reject(error); + } + }; - case 'hasVideo': - if(_container) { - var audioOnly = !(_stream.hasVideo && _properties.subscribeToVideo); - _container.audioOnly(audioOnly); - _container.showPoster(audioOnly); - } - this.dispatchEvent(new OT.VideoEnabledChangedEvent( - _stream.hasVideo ? 'videoEnabled' : 'videoDisabled', { - reason: 'publishVideo' - })); - break; + var unbind = function() { + publisher.off('publishComplete', onPublishComplete); + publisher.off('accessAllowed', onAccessAllowed); + }; - case 'hasAudio': - // noop - } - }, + publisher.on('publishComplete', onPublishComplete); + publisher.on('accessAllowed', onAccessAllowed); - /// Chrome + } + }); - // If mode is false, then that is the mode. If mode is true then we'll - // definitely display the button, but we'll defer the model to the - // Publishers buttonDisplayMode style property. - chromeButtonMode = function(mode) { - if (mode === false) return 'off'; + var testConfig; + var webrtcStats; + Promise.all([getTestNetworkConfig(token), webRtcStreamPromise]) + .then(function(values) { + var webRtcStream = values[1]; + testConfig = values[0]; + return OT.webrtcTest({mediaConfig: testConfig.media, localStream: webRtcStream}); + }) + .then(function(stats) { + OT.debug('Received stats from webrtcTest: ', stats); + if (stats.bandwidth < testConfig.media.thresholdBitsPerSecond) { + return Promise.reject(new OT.$.Error('The detect bandwidth form the WebRTC stage of ' + + 'the test was not sufficient to run the HTTP stage of the test', 1553)); + } + + webrtcStats = stats; + }) + .then(function() { + return OT.httpTest({httpConfig: testConfig.http}); + }) + .then(function(httpStats) { + var stats = { + uploadBitsPerSecond: httpStats.uploadBandwidth, + downloadBitsPerSecond: httpStats.downloadBandwidth, + packetLossRatio: webrtcStats.packetLostRatio, + roundTripTimeMilliseconds: webrtcStats.roundTripTime + }; + callback(null, stats); + // IE8 (ES3 JS engine) requires bracket notation for "catch" keyword + })['catch'](function(error) { + callback(error); + }); + }; - var defaultMode = this.getStyle('buttonDisplayMode'); + this.logConnectivityEvent = function(variation, payload, options) { + if (variation === 'Attempt' || !_connectivityAttemptPinger) { + var pingerOptions = { + action: 'Connect', + sessionId: _sessionId, + partnerId: _apiKey + }; + if (this.connection && this.connection.id) { + pingerOptions = event.connectionId = this.connection.id; + } else if (_connectionId) { + pingerOptions.connectionId = _connectionId; + } + _connectivityAttemptPinger = new OT.ConnectivityAttemptPinger(pingerOptions); + } + _connectivityAttemptPinger.setVariation(variation); + this.logEvent('Connect', variation, payload, options); + }; - // The default model is false, but it's overridden by +mode+ being true - if (defaultMode === false) return 'on'; +/** +* Connects to an OpenTok session. +*

    +* Upon a successful connection, the completion handler (the second parameter of the method) is +* invoked without an error object passed in. (If there is an error connecting, the completion +* handler is invoked with an error object.) Make sure that you have successfully connected to the +* session before calling other methods of the Session object. +*

    +*

    +* The Session object dispatches a connectionCreated event when any client +* (including your own) connects to to the session. +*

    +* +*
    +* Example +*
    +*

    +* The following code initializes a session and sets up an event listener for when the session +* connects: +*

    +*
    +*  var apiKey = ""; // Replace with your API key. See https://dashboard.tokbox.com/projects
    +*  var sessionID = ""; // Replace with your own session ID.
    +*                      // See https://dashboard.tokbox.com/projects
    +*  var token = ""; // Replace with a generated token that has been assigned the moderator role.
    +*                  // See https://dashboard.tokbox.com/projects
    +*
    +*  var session = OT.initSession(apiKey, sessionID);
    +*  session.on("sessionConnected", function(sessionConnectEvent) {
    +*      //
    +*  });
    +*  session.connect(token);
    +*  
    +*

    +*

    +* In this example, the sessionConnectHandler() function is passed an event +* object of type {@link SessionConnectEvent}. +*

    +* +*
    +* Events dispatched: +*
    +* +*

    +* exception (ExceptionEvent) — Dispatched +* by the OT class locally in the event of an error. +*

    +*

    +* connectionCreated (ConnectionEvent) — +* Dispatched by the Session object on all clients connected to the session. +*

    +*

    +* sessionConnected (SessionConnectEvent) +* — Dispatched locally by the Session object when the connection is established. +*

    +* +* @param {String} token The session token. You generate a session token using our +* server-side libraries or the +* Dashboard page. For more information, see +* Connection token creation. +* +* @param {Function} completionHandler (Optional) A function to be called when the call to the +* connect() method succeeds or fails. This function takes one parameter — +* error (see the Error object). +* On success, the completionHandler function is not passed any +* arguments. On error, the function is passed an error object parameter +* (see the Error object). The +* error object has two properties: code (an integer) and +* message (a string), which identify the cause of the failure. The following +* code adds a completionHandler when calling the connect() method: +*
    +* session.connect(token, function (error) {
    +*   if (error) {
    +*       console.log(error.message);
    +*   } else {
    +*     console.log("Connected to session.");
    +*   }
    +* });
    +* 
    +*

    +* Note that upon connecting to the session, the Session object dispatches a +* sessionConnected event in addition to calling the completionHandler. +* The SessionConnectEvent object, which defines the sessionConnected event, +* includes connections and streams properties, which +* list the connections and streams in the session when you connect. +*

    +* +* @see SessionConnectEvent +* @method #connect +* @memberOf Session +*/ + this.connect = function(token) { - // defaultMode is either true or auto. - return defaultMode; - }, + if(apiKey == null && arguments.length > 1 && + (typeof arguments[0] === 'string' || typeof arguments[0] === 'number') && + typeof arguments[1] === 'string') { + _apiKey = token.toString(); + token = arguments[1]; + } - updateChromeForStyleChange = function(key, value/*, oldValue*/) { - if (!_chrome) return; + // The completion handler is always the last argument. + var completionHandler = arguments[arguments.length - 1]; - switch(key) { - case 'nameDisplayMode': - _chrome.name.setDisplayMode(value); - _chrome.backingBar.setNameMode(value); - break; + if (this.is('connecting', 'connected')) { + OT.warn('OT.Session: Cannot connect, the session is already ' + this.state); + return this; + } - case 'videoDisabledDisplayMode': - _chrome.videoDisabledIndicator.setDisplayMode(value); - break; + init(); + setState('connecting'); + this.token = _token = !OT.$.isFunction(token) && token; + + // Get a new widget ID when reconnecting. + if (_initialConnection) { + _initialConnection = false; + } else { + _widgetId = OT.$.uuid(); + } - case 'showArchiveStatus': - _chrome.archive.setShowArchiveStatus(value); - break; + if (completionHandler && OT.$.isFunction(completionHandler)) { + this.once('sessionConnected', OT.$.bind(completionHandler, null, null)); + this.once('sessionConnectFailed', completionHandler); + } - case 'buttonDisplayMode': - _chrome.muteButton.setDisplayMode(value); - _chrome.backingBar.setMuteMode(value); - break; + if(_apiKey == null || OT.$.isFunction(_apiKey)) { + setTimeout(OT.$.bind( + sessionConnectFailed, + this, + 'API Key is undefined. You must pass an API Key to initSession.', + OT.ExceptionCodes.AUTHENTICATION_ERROR + )); - case 'audioLevelDisplayMode': - _chrome.audioLevel.setDisplayMode(value); - break; + return this; + } - case 'bugDisplayMode': - // bugDisplayMode can't be updated but is used by some partners + if (!_sessionId) { + setTimeout(OT.$.bind( + sessionConnectFailed, + this, + 'SessionID is undefined. You must pass a sessionID to initSession.', + OT.ExceptionCodes.INVALID_SESSION_ID + )); - case 'backgroundImageURI': - _container.setBackgroundImageURI(value); - } - }, + return this; + } - _createChrome = function() { - - if(this.getStyle('bugDisplayMode') === 'off') { - logAnalyticsEvent('bugDisplayMode', 'createChrome', 'mode', 'off'); - } + this.apiKey = _apiKey = _apiKey.toString(); - var widgets = { - backingBar: new OT.Chrome.BackingBar({ - nameMode: !_properties.name ? 'off' : this.getStyle('nameDisplayMode'), - muteMode: chromeButtonMode.call(this, this.getStyle('showMuteButton')) - }), - - name: new OT.Chrome.NamePanel({ - name: _properties.name, - mode: this.getStyle('nameDisplayMode'), - bugMode: this.getStyle('bugDisplayMode') - }), - - muteButton: new OT.Chrome.MuteButton({ - muted: _properties.muted, - mode: chromeButtonMode.call(this, this.getStyle('showMuteButton')) - }), - - opentokButton: new OT.Chrome.OpenTokButton({ - mode: this.getStyle('bugDisplayMode') - }), - - archive: new OT.Chrome.Archiving({ - show: this.getStyle('showArchiveStatus'), - archiving: false - }) - }; + // Ugly hack, make sure OT.APIKEY is set + if (OT.APIKEY.length === 0) { + OT.APIKEY = _apiKey; + } - if(_audioLevelCapable) { - _audioLevelMeter = new OT.Chrome.AudioLevelMeter({ - mode: this.getStyle('audioLevelDisplayMode') - }); + this.logConnectivityEvent('Attempt'); - var audioLevelTransformer = new OT.AudioLevelTransformer(); - this.on('audioLevelUpdated', function(evt) { - _audioLevelMeter.setValue(audioLevelTransformer.transform(evt.audioLevel)); - }); + getSessionInfo.call(this); + return this; + }; - widgets.audioLevel = _audioLevelMeter; - } +/** +* Disconnects from the OpenTok session. +* +*

    +* Calling the disconnect() method ends your connection with the session. In the +* course of terminating your connection, it also ceases publishing any stream(s) you were +* publishing. +*

    +*

    +* Session objects on remote clients dispatch streamDestroyed events for any +* stream you were publishing. The Session object dispatches a sessionDisconnected +* event locally. The Session objects on remote clients dispatch connectionDestroyed +* events, letting other connections know you have left the session. The +* {@link SessionDisconnectEvent} and {@link StreamEvent} objects that define the +* sessionDisconnect and connectionDestroyed events each have a +* reason property. The reason property lets the developer determine +* whether the connection is being terminated voluntarily and whether any streams are being +* destroyed as a byproduct of the underlying connection's voluntary destruction. +*

    +*

    +* If the session is not currently connected, calling this method causes a warning to be logged. +* See OT.setLogLevel(). +*

    +* +*

    +* Note: If you intend to reuse a Publisher object created using +* OT.initPublisher() to publish to different sessions sequentially, call either +* Session.disconnect() or Session.unpublish(). Do not call both. +* Then call the preventDefault() method of the streamDestroyed or +* sessionDisconnected event object to prevent the Publisher object from being +* removed from the page. Be sure to call preventDefault() only if the +* connection.connectionId property of the Stream object in the event matches the +* connection.connectionId property of your Session object (to ensure that you are +* preventing the default behavior for your published streams, not for other streams that you +* subscribe to). +*

    +* +*
    +* Events dispatched: +*
    +*

    +* sessionDisconnected +* (SessionDisconnectEvent) +* — Dispatched locally when the connection is disconnected. +*

    +*

    +* connectionDestroyed (ConnectionEvent) — +* Dispatched on other clients, along with the streamDestroyed event (as warranted). +*

    +* +*

    +* streamDestroyed (StreamEvent) — +* Dispatched on other clients if streams are lost as a result of the session disconnecting. +*

    +* +* @method #disconnect +* @memberOf Session +*/ + var disconnect = OT.$.bind(function disconnect(drainSocketBuffer) { + if (_socket && _socket.isNot('disconnected')) { + if (_socket.isNot('disconnecting')) { + setState('disconnecting'); + _socket.disconnect(drainSocketBuffer); + } + } + else { + reset(); + } + }, this); - widgets.videoDisabledIndicator = new OT.Chrome.VideoDisabledIndicator({ - mode: this.getStyle('videoDisabledDisplayMode') - }); + this.disconnect = function(drainSocketBuffer) { + disconnect(drainSocketBuffer !== void 0 ? drainSocketBuffer : true); + }; - _chrome = new OT.Chrome({ - parent: _container.domElement - }).set(widgets).on({ - muted: function() { - muteAudio.call(this, true); - }, + this.destroy = function(reason) { + this.streams.destroy(); + this.connections.destroy(); + this.archives.destroy(); + disconnect(reason !== 'unloaded'); + }; - unmuted: function() { - muteAudio.call(this, false); - } - }, this); +/** +* The publish() method starts publishing an audio-video stream to the session. +* The audio-video stream is captured from a local microphone and webcam. Upon successful +* publishing, the Session objects on all connected clients dispatch the +* streamCreated event. +*

    +* +* +*

    You pass a Publisher object as the one parameter of the method. You can initialize a +* Publisher object by calling the OT.initPublisher() +* method. Before calling Session.publish(). +*

    +* +*

    This method takes an alternate form: publish([targetElement:String, +* properties:Object]):Publisher — In this form, you do not pass a Publisher +* object into the function. Instead, you pass in a targetElement (the ID of the +* DOM element that the Publisher will replace) and a properties object that +* defines options for the Publisher (see OT.initPublisher().) +* The method returns a new Publisher object, which starts sending an audio-video stream to the +* session. The remainder of this documentation describes the form that takes a single Publisher +* object as a parameter. +* +*

    +* A local display of the published stream is created on the web page by replacing +* the specified element in the DOM with a streaming video display. The video stream +* is automatically mirrored horizontally so that users see themselves and movement +* in their stream in a natural way. If the width and height of the display do not match +* the 4:3 aspect ratio of the video signal, the video stream is cropped to fit the +* display. +*

    +* +*

    +* If calling this method creates a new Publisher object and the OpenTok library does not +* have access to the camera or microphone, the web page alerts the user to grant access +* to the camera and microphone. +*

    +* +*

    +* The OT object dispatches an exception event if the user's role does not +* include permissions required to publish. For example, if the user's role is set to subscriber, +* then they cannot publish. You define a user's role when you create the user token using the +* generate_token() method of the +* OpenTok server-side +* libraries or the Dashboard page. +* You pass the token string as a parameter of the connect() method of the Session +* object. See ExceptionEvent and +* OT.on(). +*

    +*

    +* The application throws an error if the session is not connected. +*

    +* +*
    Events dispatched:
    +*

    +* exception (ExceptionEvent) — Dispatched +* by the OT object. This can occur when user's role does not allow publishing (the +* code property of event object is set to 1500); it can also occur if the c +* onnection fails to connect (the code property of event object is set to 1013). +* WebRTC is a peer-to-peer protocol, and it is possible that connections will fail to connect. +* The most common cause for failure is a firewall that the protocol cannot traverse. +*

    +*

    +* streamCreated (StreamEvent) — +* The stream has been published. The Session object dispatches this on all clients +* subscribed to the stream, as well as on the publisher's client. +*

    +* +*
    Example
    +* +*

    +* The following example publishes a video once the session connects: +*

    +*
    +* var sessionId = ""; // Replace with your own session ID.
    +*                     // See https://dashboard.tokbox.com/projects
    +* var token = ""; // Replace with a generated token that has been assigned the moderator role.
    +*                 // See https://dashboard.tokbox.com/projects
    +* var session = OT.initSession(apiKey, sessionID);
    +* session.on("sessionConnected", function (event) {
    +*     var publisherOptions = {width: 400, height:300, name:"Bob's stream"};
    +*     // This assumes that there is a DOM element with the ID 'publisher':
    +*     publisher = OT.initPublisher('publisher', publisherOptions);
    +*     session.publish(publisher);
    +* });
    +* session.connect(token);
    +* 
    +* +* @param {Publisher} publisher A Publisher object, which you initialize by calling the +* OT.initPublisher() method. +* +* @param {Function} completionHandler (Optional) A function to be called when the call to the +* publish() method succeeds or fails. This function takes one parameter — +* error. On success, the completionHandler function is not passed any +* arguments. On error, the function is passed an error object parameter +* (see the Error object). The +* error object has two properties: code (an integer) and +* message (a string), which identify the cause of the failure. Calling +* publish() fails if the role assigned to your token is not "publisher" or +* "moderator"; in this case error.code is set to 1500. Calling +* publish() also fails the client fails to connect; in this case +* error.code is set to 1013. The following code adds a +* completionHandler when calling the publish() method: +*
    +* session.publish(publisher, null, function (error) {
    +*   if (error) {
    +*     console.log(error.message);
    +*   } else {
    +*     console.log("Publishing a stream.");
    +*   }
    +* });
    +* 
    +* +* @returns The Publisher object for this stream. +* +* @method #publish +* @memberOf Session +*/ + this.publish = function(publisher, properties, completionHandler) { + if(typeof publisher === 'function') { + completionHandler = publisher; + publisher = undefined; + } + if(typeof properties === 'function') { + completionHandler = properties; + properties = undefined; + } + if (this.isNot('connected')) { + OT.analytics.logError(1010, 'OT.exception', + 'We need to be connected before you can publish', null, { + action: 'Publish', + variation: 'Failure', + payload: { + reason:'unconnected', + code: OT.ExceptionCodes.NOT_CONNECTED, + message: 'We need to be connected before you can publish' }, + sessionId: _sessionId, + partnerId: _apiKey, + }); - _showError = function() { - // Display the error message inside the container, assuming it's - // been created by now. - if (_container) { - _container.addError( - 'The stream was unable to connect due to a network error.', - 'Make sure your connection isn\'t blocked by a firewall.' - ); - } - }; + if (completionHandler && OT.$.isFunction(completionHandler)) { + dispatchError(OT.ExceptionCodes.NOT_CONNECTED, + 'We need to be connected before you can publish', completionHandler); + } + return null; + } - this.subscribe = function(stream) { - OT.debug('OT.Subscriber: subscribe to ' + stream.id); + if (!permittedTo('publish')) { + var errorMessage = 'This token does not allow publishing. The role must be at least ' + + '`publisher` to enable this functionality'; + var payload = { + reason: 'permission', + code: OT.ExceptionCodes.UNABLE_TO_PUBLISH, + message: errorMessage + }; + this.logEvent('publish', 'Failure', payload); + dispatchError(OT.ExceptionCodes.UNABLE_TO_PUBLISH, errorMessage, completionHandler); + return null; + } - if (_state.isSubscribing()) { - // @todo error - OT.error('OT.Subscriber.Subscribe: Cannot subscribe, already subscribing.'); - return false; + // If the user has passed in an ID of a element then we create a new publisher. + if (!publisher || typeof(publisher)==='string' || OT.$.isElementNode(publisher)) { + // Initiate a new Publisher with the new session credentials + publisher = OT.initPublisher(publisher, properties); + + } else if (publisher instanceof OT.Publisher){ + + // If the publisher already has a session attached to it we can + if ('session' in publisher && publisher.session && 'sessionId' in publisher.session) { + // send a warning message that we can't publish again. + if( publisher.session.sessionId === this.sessionId){ + OT.warn('Cannot publish ' + publisher.guid() + ' again to ' + + this.sessionId + '. Please call session.unpublish(publisher) first.'); + } else { + OT.warn('Cannot publish ' + publisher.guid() + ' publisher already attached to ' + + publisher.session.sessionId+ '. Please call session.unpublish(publisher) first.'); + } } - _state.set('Init'); + } else { + dispatchError(OT.ExceptionCodes.UNABLE_TO_PUBLISH, + 'Session.publish :: First parameter passed in is neither a ' + + 'string nor an instance of the Publisher', + completionHandler); + return; + } - if (!stream) { - // @todo error - OT.error('OT.Subscriber: No stream parameter.'); - return false; + publisher.once('publishComplete', function(err) { + if (err) { + dispatchError(OT.ExceptionCodes.UNABLE_TO_PUBLISH, + 'Session.publish :: ' + err.message, + completionHandler); + return; } - if (_stream) { - // @todo error - OT.error('OT.Subscriber: Already subscribed'); - return false; + if (completionHandler && OT.$.isFunction(completionHandler)) { + completionHandler.apply(null, arguments); } + }); - this.stream = _stream = stream; - this.streamId = _stream.id; - _stream.on({ - updated: streamUpdated, - destroyed: streamDestroyed - }, this); + // Add publisher reference to the session + publisher._.publishToSession(this); - _fromConnectionId = stream.connection.id; - _properties.name = _properties.name || _stream.name; - _properties.classNames = 'OT_root OT_subscriber'; + // return the embed publisher + return publisher; + }; - if (_properties.style) { - this.setStyle(_properties.style, null, true); - } - if (_properties.audioVolume) { - this.setAudioVolume(_properties.audioVolume); - } +/** +* Ceases publishing the specified publisher's audio-video stream +* to the session. By default, the local representation of the audio-video stream is +* removed from the web page. Upon successful termination, the Session object on every +* connected web page dispatches +* a streamDestroyed event. +*

    +* +*

    +* To prevent the Publisher from being removed from the DOM, add an event listener for the +* streamDestroyed event dispatched by the Publisher object and call the +* preventDefault() method of the event object. +*

    +* +*

    +* Note: If you intend to reuse a Publisher object created using +* OT.initPublisher() to publish to different sessions sequentially, call +* either Session.disconnect() or Session.unpublish(). Do not call +* both. Then call the preventDefault() method of the streamDestroyed +* or sessionDisconnected event object to prevent the Publisher object from being +* removed from the page. Be sure to call preventDefault() only if the +* connection.connectionId property of the Stream object in the event matches the +* connection.connectionId property of your Session object (to ensure that you are +* preventing the default behavior for your published streams, not for other streams that you +* subscribe to). +*

    +* +*
    Events dispatched:
    +* +*

    +* streamDestroyed (StreamEvent) — +* The stream associated with the Publisher has been destroyed. Dispatched on by the +* Publisher on on the Publisher's browser. Dispatched by the Session object on +* all other connections subscribing to the publisher's stream. +*

    +* +*
    Example
    +* +* The following example publishes a stream to a session and adds a Disconnect link to the +* web page. Clicking this link causes the stream to stop being published. +* +*
    +* <script>
    +*     var apiKey = ""; // Replace with your API key. See https://dashboard.tokbox.com/projects
    +*     var sessionID = ""; // Replace with your own session ID.
    +*                      // See https://dashboard.tokbox.com/projects
    +*     var token = "Replace with the TokBox token string provided to you."
    +*     var session = OT.initSession(apiKey, sessionID);
    +*     session.on("sessionConnected", function sessionConnectHandler(event) {
    +*         // This assumes that there is a DOM element with the ID 'publisher':
    +*         publisher = OT.initPublisher('publisher');
    +*         session.publish(publisher);
    +*     });
    +*     session.connect(token);
    +*     var publisher;
    +*
    +*     function unpublish() {
    +*         session.unpublish(publisher);
    +*     }
    +* </script>
    +*
    +* <body>
    +*
    +*     <div id="publisherContainer/>
    +*     <br/>
    +*
    +*     <a href="javascript:unpublish()">Stop Publishing</a>
    +*
    +* </body>
    +*
    +* 
    +* +* @see publish() +* +* @see streamDestroyed event +* +* @param {Publisher} publisher The Publisher object to stop streaming. +* +* @method #unpublish +* @memberOf Session +*/ + this.unpublish = function(publisher) { + if (!publisher) { + OT.error('OT.Session.unpublish: publisher parameter missing.'); + return; + } - _properties.subscribeToAudio = OT.$.castToBoolean(_properties.subscribeToAudio, true); - _properties.subscribeToVideo = OT.$.castToBoolean(_properties.subscribeToVideo, true); + // Unpublish the localMedia publisher + publisher._.unpublishFromSession(this, 'unpublished'); + }; - _container = new OT.WidgetView(targetElement, _properties); - this.id = _domId = _container.domId(); - this.element = _container.domElement; - _startConnectingTime = OT.$.now(); +/** +* Subscribes to a stream that is available to the session. You can get an array of +* available streams from the streams property of the sessionConnected +* and streamCreated events (see +* SessionConnectEvent and +* StreamEvent). +*

    +*

    +* The subscribed stream is displayed on the local web page by replacing the specified element +* in the DOM with a streaming video display. If the width and height of the display do not +* match the 4:3 aspect ratio of the video signal, the video stream is cropped to fit +* the display. If the stream lacks a video component, a blank screen with an audio indicator +* is displayed in place of the video stream. +*

    +* +*

    +* The application throws an error if the session is not connected or if the +* targetElement does not exist in the HTML DOM. +*

    +* +*
    Example
    +* +* The following code subscribes to other clients' streams: +* +*
    +* var apiKey = ""; // Replace with your API key. See https://dashboard.tokbox.com/projects
    +* var sessionID = ""; // Replace with your own session ID.
    +*                     // See https://dashboard.tokbox.com/projects
    +*
    +* var session = OT.initSession(apiKey, sessionID);
    +* session.on("streamCreated", function(event) {
    +*   subscriber = session.subscribe(event.stream, targetElement);
    +* });
    +* session.connect(token);
    +* 
    +* +* @param {Stream} stream The Stream object representing the stream to which we are trying to +* subscribe. +* +* @param {Object} targetElement (Optional) The DOM element or the id attribute of +* the existing DOM element used to determine the location of the Subscriber video in the HTML +* DOM. See the insertMode property of the properties parameter. If +* you do not specify a targetElement, the application appends a new DOM element +* to the HTML body. +* +* @param {Object} properties This is an object that contains the following properties: +*
      +*
    • audioVolume (Number) — The desired audio volume, between 0 and +* 100, when the Subscriber is first opened (default: 50). After you subscribe to the +* stream, you can adjust the volume by calling the +* setAudioVolume() method of the +* Subscriber object. This volume setting affects local playback only; it does not affect +* the stream's volume on other clients.
    • +* +*
    • +* fitMode (String) — Determines how the video is displayed if the its +* dimensions do not match those of the DOM element. You can set this property to one of +* the following values: +*

      +*

        +*
      • +* "cover" — The video is cropped if its dimensions do not match +* those of the DOM element. This is the default setting for screen-sharing videos +* (for Stream objects with the videoType property set to +* "screen"). +*
      • +*
      • +* "contain" — The video is letter-boxed if its dimensions do not +* match those of the DOM element. This is the default setting for videos that have a +* camera as the source (for Stream objects with the videoType property +* set to "camera"). +*
      • +*
      +*
    • +* +*
    • height (Number) — The desired height, in pixels, of the +* displayed Subscriber video stream (default: 198). Note: Use the +* height and width properties to set the dimensions +* of the Subscriber video; do not set the height and width of the DOM element +* (using CSS).
    • +* +*
    • +* insertMode (String) — Specifies how the Subscriber object will +* be inserted in the HTML DOM. See the targetElement parameter. This +* string can have the following values: +*
        +*
      • "replace" — The Subscriber object replaces contents of the +* targetElement. This is the default.
      • +*
      • "after" — The Subscriber object is a new element inserted +* after the targetElement in the HTML DOM. (Both the Subscriber and targetElement +* have the same parent element.)
      • +*
      • "before" — The Subscriber object is a new element inserted +* before the targetElement in the HTML DOM. (Both the Subsciber and targetElement +* have the same parent element.)
      • +*
      • "append" — The Subscriber object is a new element added as a +* child of the targetElement. If there are other child elements, the Subscriber is +* appended as the last child element of the targetElement.
      • +*
      +*
    • +* +*
    • +* style (Object) — An object containing properties that define the initial +* appearance of user interface controls of the Subscriber. The style object +* includes the following properties: +*
        +*
      • audioLevelDisplayMode (String) — How to display the audio level +* indicator. Possible values are: "auto" (the indicator is displayed when the +* video is disabled), "off" (the indicator is not displayed), and +* "on" (the indicator is always displayed).
      • +* +*
      • backgroundImageURI (String) — A URI for an image to display as +* the background image when a video is not displayed. (A video may not be displayed if +* you call subscribeToVideo(false) on the Subscriber object). You can pass an +* http or https URI to a PNG, JPEG, or non-animated GIF file location. You can also use the +* data URI scheme (instead of http or https) and pass in base-64-encrypted +* PNG data, such as that obtained from the +* Subscriber.getImgData() method. For example, +* you could set the property to "data:VBORw0KGgoAA...", where the portion of +* the string after "data:" is the result of a call to +* Subscriber.getImgData(). If the URL or the image data is invalid, the +* property is ignored (the attempt to set the image fails silently). +*

        +* Note that in Internet Explorer 8 (using the OpenTok Plugin for Internet Explorer), +* you cannot set the backgroundImageURI style to a string larger than +* 32 kB. This is due to an IE 8 limitation on the size of URI strings. Due to this +* limitation, you cannot set the backgroundImageURI style to a string obtained +* with the getImgData() method. +*

      • +* +*
      • buttonDisplayMode (String) — How to display the speaker controls +* Possible values are: "auto" (controls are displayed when the stream is first +* displayed and when the user mouses over the display), "off" (controls are not +* displayed), and "on" (controls are always displayed).
      • +* +*
      • nameDisplayMode (String) — Whether to display the stream name. +* Possible values are: "auto" (the name is displayed when the stream is first +* displayed and when the user mouses over the display), "off" (the name is not +* displayed), and "on" (the name is always displayed).
      • +* +*
      • videoDisabledDisplayMode (String) — Whether to display the video +* disabled indicator and video disabled warning icons for a Subscriber. These icons +* indicate that the video has been disabled (or is in risk of being disabled for +* the warning icon) due to poor stream quality. This style only applies to the Subscriber +* object. Possible values are: "auto" (the icons are automatically when the +* displayed video is disabled or in risk of being disabled due to poor stream quality), +* "off" (do not display the icons), and "on" (display the +* icons). The default setting is "auto"
      • +*
      +*
    • +* +*
    • subscribeToAudio (Boolean) — Whether to initially subscribe to audio +* (if available) for the stream (default: true).
    • +* +*
    • subscribeToVideo (Boolean) — Whether to initially subscribe to video +* (if available) for the stream (default: true).
    • +* +*
    • width (Number) — The desired width, in pixels, of the +* displayed Subscriber video stream (default: 264). Note: Use the +* height and width properties to set the dimensions +* of the Subscriber video; do not set the height and width of the DOM element +* (using CSS).
    • +* +*
    +* +* @param {Function} completionHandler (Optional) A function to be called when the call to the +* subscribe() method succeeds or fails. This function takes one parameter — +* error. On success, the completionHandler function is not passed any +* arguments. On error, the function is passed an error object, defined by the +* Error class, has two properties: code (an integer) and +* message (a string), which identify the cause of the failure. The following +* code adds a completionHandler when calling the subscribe() method: +*
    +* session.subscribe(stream, "subscriber", null, function (error) {
    +*   if (error) {
    +*     console.log(error.message);
    +*   } else {
    +*     console.log("Subscribed to stream: " + stream.id);
    +*   }
    +* });
    +* 
    +* +* @signature subscribe(stream, targetElement, properties, completionHandler) +* @returns {Subscriber} The Subscriber object for this stream. Stream control functions +* are exposed through the Subscriber object. +* @method #subscribe +* @memberOf Session +*/ + this.subscribe = function(stream, targetElement, properties, completionHandler) { - if (_stream.connection.id !== _session.connection.id) { - logAnalyticsEvent('createPeerConnection', 'Attempt', '', ''); + if (!this.connection || !this.connection.connectionId) { + dispatchError(OT.ExceptionCodes.UNABLE_TO_SUBSCRIBE, + 'Session.subscribe :: Connection required to subscribe', + completionHandler); + return; + } - _state.set('ConnectingToPeer'); + if (!stream) { + dispatchError(OT.ExceptionCodes.UNABLE_TO_SUBSCRIBE, + 'Session.subscribe :: stream cannot be null', + completionHandler); + return; + } - _peerConnection = new OT.SubscriberPeerConnection(_stream.connection, _session, - _stream, this, _properties); + if (!stream.hasOwnProperty('streamId')) { + dispatchError(OT.ExceptionCodes.UNABLE_TO_SUBSCRIBE, + 'Session.subscribe :: invalid stream object', + completionHandler); + return; + } - _peerConnection.on({ - disconnected: onDisconnected, - error: onPeerConnectionFailure, - remoteStreamAdded: onRemoteStreamAdded, - remoteStreamRemoved: onRemoteStreamRemoved, - qos: recordQOS - }, this); + if(typeof targetElement === 'function') { + completionHandler = targetElement; + targetElement = undefined; + properties = undefined; + } - // initialize the peer connection AFTER we've added the event listeners - _peerConnection.init(); + if(typeof properties === 'function') { + completionHandler = properties; + properties = undefined; + } - if (OT.$.hasCapabilities('audioOutputLevelStat')) { - _audioLevelSampler = new OT.GetStatsAudioLevelSampler(_peerConnection, 'out'); - } else if (OT.$.hasCapabilities('webAudioCapableRemoteStream')) { - _audioLevelSampler = new OT.AnalyserAudioLevelSampler(new window.AudioContext()); - } - - if(_audioLevelSampler) { - var subscriber = this; - // sample with interval to minimise disturbance on animation loop but dispatch the - // event with RAF since the main purpose is animation of a meter - _audioLevelRunner = new OT.IntervalRunner(function() { - _audioLevelSampler.sample(function(audioOutputLevel) { - if (audioOutputLevel !== null) { - OT.$.requestAnimationFrame(function() { - subscriber.dispatchEvent( - new OT.AudioLevelUpdatedEvent(audioOutputLevel)); - }); - } - }); - }, 60); - } - } else { - logAnalyticsEvent('createPeerConnection', 'Attempt', '', ''); + var subscriber = new OT.Subscriber(targetElement, OT.$.extend(properties || {}, { + stream: stream, + session: this + }), function(err) { - var publisher = _session.getPublisherForStream(_stream); - if(!(publisher && publisher._.webRtcStream())) { - this.trigger('subscribeComplete', new OT.Error(null, 'InvalidStreamID')); - return this; - } + if (err) { + dispatchError(OT.ExceptionCodes.UNABLE_TO_SUBSCRIBE, + 'Session.subscribe :: ' + err.message, + completionHandler); - // Subscribe to yourself edge-case - onRemoteStreamAdded.call(this, publisher._.webRtcStream()); + } else if (completionHandler && OT.$.isFunction(completionHandler)) { + completionHandler.apply(null, arguments); } - logAnalyticsEvent('subscribe', 'Attempt', 'streamId', _stream.id); + }); - return this; - }; + OT.subscribers.add(subscriber); - this.destroy = function(reason, quiet) { - if (_state.isDestroyed()) return; + return subscriber; - if(reason === 'streamDestroyed') { - if (_state.isAttemptingToSubscribe()) { - // We weren't subscribing yet so the stream was destroyed before we setup - // the PeerConnection or receiving the initial stream. - this.trigger('subscribeComplete', new OT.Error(null, 'InvalidStreamID')); - } - } + }; - _state.set('Destroyed'); +/** +* Stops subscribing to a stream in the session. the display of the audio-video stream is +* removed from the local web page. +* +*
    Example
    +*

    +* The following code subscribes to other clients' streams. For each stream, the code also +* adds an Unsubscribe link. +*

    +*
    +* var apiKey = ""; // Replace with your API key. See https://dashboard.tokbox.com/projects
    +* var sessionID = ""; // Replace with your own session ID.
    +*                     // See https://dashboard.tokbox.com/projects
    +* var streams = [];
    +*
    +* var session = OT.initSession(apiKey, sessionID);
    +* session.on("streamCreated", function(event) {
    +*     var stream = event.stream;
    +*     displayStream(stream);
    +* });
    +* session.connect(token);
    +*
    +* function displayStream(stream) {
    +*     var div = document.createElement('div');
    +*     div.setAttribute('id', 'stream' + stream.streamId);
    +*
    +*     var subscriber = session.subscribe(stream, div);
    +*     subscribers.push(subscriber);
    +*
    +*     var aLink = document.createElement('a');
    +*     aLink.setAttribute('href', 'javascript: unsubscribe("' + subscriber.id + '")');
    +*     aLink.innerHTML = "Unsubscribe";
    +*
    +*     var streamsContainer = document.getElementById('streamsContainer');
    +*     streamsContainer.appendChild(div);
    +*     streamsContainer.appendChild(aLink);
    +*
    +*     streams = event.streams;
    +* }
    +*
    +* function unsubscribe(subscriberId) {
    +*     console.log("unsubscribe called");
    +*     for (var i = 0; i < subscribers.length; i++) {
    +*         var subscriber = subscribers[i];
    +*         if (subscriber.id == subscriberId) {
    +*             session.unsubscribe(subscriber);
    +*         }
    +*     }
    +* }
    +* 
    +* +* @param {Subscriber} subscriber The Subscriber object to unsubcribe. +* +* @see subscribe() +* +* @method #unsubscribe +* @memberOf Session +*/ + this.unsubscribe = function(subscriber) { + if (!subscriber) { + var errorMsg = 'OT.Session.unsubscribe: subscriber cannot be null'; + OT.error(errorMsg); + throw new Error(errorMsg); + } - if(_audioLevelRunner) { - _audioLevelRunner.stop(); - } + if (!subscriber.stream) { + OT.warn('OT.Session.unsubscribe:: tried to unsubscribe a subscriber that had no stream'); + return false; + } - this.disconnect(); + OT.debug('OT.Session.unsubscribe: subscriber ' + subscriber.id); - if (_chrome) { - _chrome.destroy(); - _chrome = null; - } + subscriber.destroy(); - if (_container) { - _container.destroy(); - _container = null; - this.element = null; - } + return true; + }; - if (_stream && !_stream.destroyed) { - logAnalyticsEvent('unsubscribe', null, 'streamId', _stream.id); - } +/** +* Returns an array of local Subscriber objects for a given stream. +* +* @param {Stream} stream The stream for which you want to find subscribers. +* +* @returns {Array} An array of {@link Subscriber} objects for the specified stream. +* +* @see unsubscribe() +* @see Subscriber +* @see StreamEvent +* @method #getSubscribersForStream +* @memberOf Session +*/ + this.getSubscribersForStream = function(stream) { + return OT.subscribers.where({streamId: stream.id}); + }; - this.id = _domId = null; - this.stream = _stream = null; - this.streamId = null; +/** +* Returns the local Publisher object for a given stream. +* +* @param {Stream} stream The stream for which you want to find the Publisher. +* +* @returns {Publisher} A Publisher object for the specified stream. Returns +* null if there is no local Publisher object +* for the specified stream. +* +* @see forceUnpublish() +* @see Subscriber +* @see StreamEvent +* +* @method #getPublisherForStream +* @memberOf Session +*/ + this.getPublisherForStream = function(stream) { + var streamId, + errorMsg; + + if (typeof stream === 'string') { + streamId = stream; + } else if (typeof stream === 'object' && stream && stream.hasOwnProperty('id')) { + streamId = stream.id; + } else { + errorMsg = 'Session.getPublisherForStream :: Invalid stream type'; + OT.error(errorMsg); + throw new Error(errorMsg); + } - this.session =_session = null; - _properties = null; + return OT.publishers.where({streamId: streamId})[0]; + }; - if (quiet !== true) { - this.dispatchEvent( - new OT.DestroyedEvent( - OT.Event.names.SUBSCRIBER_DESTROYED, - this, - reason - ), - OT.$.bind(this.off, this) - ); - } + // Private Session API: for internal OT use only + this._ = { + jsepCandidateP2p: function(streamId, subscriberId, candidate) { + return _socket.jsepCandidateP2p(streamId, subscriberId, candidate); + }, - return this; - }; + jsepCandidate: function(streamId, candidate) { + return _socket.jsepCandidate(streamId, candidate); + }, - this.disconnect = function() { - if (!_state.isDestroyed() && !_state.isFailed()) { - // If we are already in the destroyed state then disconnect - // has been called after (or from within) destroy. - _state.set('NotSubscribing'); - } + jsepOffer: function(streamId, offerSdp) { + return _socket.jsepOffer(streamId, offerSdp); + }, - if (_streamContainer) { - _streamContainer.destroy(); - _streamContainer = null; - } + jsepOfferP2p: function(streamId, subscriberId, offerSdp) { + return _socket.jsepOfferP2p(streamId, subscriberId, offerSdp); + }, - if (_peerConnection) { - _peerConnection.destroy(); - _peerConnection = null; + jsepAnswer: function(streamId, answerSdp) { + return _socket.jsepAnswer(streamId, answerSdp); + }, - logAnalyticsEvent('disconnect', 'PeerConnection', 'streamId', _stream.id); - } - }; + jsepAnswerP2p: function(streamId, subscriberId, answerSdp) { + return _socket.jsepAnswerP2p(streamId, subscriberId, answerSdp); + }, - this.processMessage = function(type, fromConnection, message) { - OT.debug('OT.Subscriber.processMessage: Received ' + type + ' message from ' + - fromConnection.id); - OT.debug(message); + // session.on("signal", function(SignalEvent)) + // session.on("signal:{type}", function(SignalEvent)) + dispatchSignal: OT.$.bind(function(fromConnection, type, data) { + var event = new OT.SignalEvent(type, data, fromConnection); + event.target = this; + + // signal a "signal" event + // NOTE: trigger doesn't support defaultAction, and therefore preventDefault. + this.trigger(OT.Event.names.SIGNAL, event); + + // signal an "signal:{type}" event" if there was a custom type + if (type) this.dispatchEvent(event); + }, this), + + subscriberCreate: function(stream, subscriber, channelsToSubscribeTo, completion) { + return _socket.subscriberCreate(stream.id, subscriber.widgetId, + channelsToSubscribeTo, completion); + }, - if (_fromConnectionId !== fromConnection.id) { - _fromConnectionId = fromConnection.id; - } + subscriberDestroy: function(stream, subscriber) { + return _socket.subscriberDestroy(stream.id, subscriber.widgetId); + }, - if (_peerConnection) { - _peerConnection.processMessage(type, message); - } - }; + subscriberUpdate: function(stream, subscriber, attributes) { + return _socket.subscriberUpdate(stream.id, subscriber.widgetId, attributes); + }, - this.disableVideo = function(active) { - if (!active) { - OT.warn('Due to high packet loss and low bandwidth, video has been disabled'); - } else { - if (_lastSubscribeToVideoReason === 'auto') { - OT.info('Video has been re-enabled'); - _chrome.videoDisabledIndicator.disableVideo(false); - } else { - OT.info('Video was not re-enabled because it was manually disabled'); - return; - } - } - this.subscribeToVideo(active, 'auto'); - if(!active) { - _chrome.videoDisabledIndicator.disableVideo(true); - } - logAnalyticsEvent('updateQuality', 'video', active ? 'videoEnabled' : 'videoDisabled', true); - }; + subscriberChannelUpdate: function(stream, subscriber, channel, attributes) { + return _socket.subscriberChannelUpdate(stream.id, subscriber.widgetId, channel.id, + attributes); + }, - /** - * Return the base-64-encoded string of PNG data representing the Subscriber video. - * - *

    You can use the string as the value for a data URL scheme passed to the src parameter of - * an image file, as in the following:

    - * - *
    -     *  var imgData = subscriber.getImgData();
    -     *
    -     *  var img = document.createElement("img");
    -     *  img.setAttribute("src", "data:image/png;base64," + imgData);
    -     *  var imgWin = window.open("about:blank", "Screenshot");
    -     *  imgWin.document.write("<body></body>");
    -     *  imgWin.document.body.appendChild(img);
    -     *  
    - * @method #getImgData - * @memberOf Subscriber - * @return {String} The base-64 encoded string. Returns an empty string if there is no video. - */ - this.getImgData = function() { - if (!this.isSubscribing()) { - OT.error('OT.Subscriber.getImgData: Cannot getImgData before the Subscriber ' + - 'is subscribing.'); - return null; - } + streamCreate: function(name, audioFallbackEnabled, channels, completion) { + _socket.streamCreate( + name, + audioFallbackEnabled, + channels, + OT.Config.get('bitrates', 'min', OT.APIKEY), + OT.Config.get('bitrates', 'max', OT.APIKEY), + completion + ); + }, - return _streamContainer.imgData(); - }; + streamDestroy: function(streamId) { + _socket.streamDestroy(streamId); + }, - /** - * Sets the audio volume, between 0 and 100, of the Subscriber. - * - *

    You can set the initial volume when you call the Session.subscribe() - * method. Pass a audioVolume property of the properties parameter - * of the method.

    - * - * @param {Number} value The audio volume, between 0 and 100. - * - * @return {Subscriber} The Subscriber object. This lets you chain method calls, as in the - * following: - * - *
    mySubscriber.setAudioVolume(50).setStyle(newStyle);
    - * - * @see getAudioVolume() - * @see Session.subscribe() - * @method #setAudioVolume - * @memberOf Subscriber - */ - this.setAudioVolume = function(value) { - value = parseInt(value, 10); - if (isNaN(value)) { - OT.error('OT.Subscriber.setAudioVolume: value should be an integer between 0 and 100'); - return this; - } - _audioVolume = Math.max(0, Math.min(100, value)); - if (_audioVolume !== value) { - OT.warn('OT.Subscriber.setAudioVolume: value should be an integer between 0 and 100'); - } - if(_properties.muted && _audioVolume > 0) { - _properties.premuteVolume = value; - muteAudio.call(this, false); - } - if (_streamContainer) { - _streamContainer.setAudioVolume(_audioVolume); - } - return this; - }; + streamChannelUpdate: function(stream, channel, attributes) { + _socket.streamChannelUpdate(stream.id, channel.id, attributes); + } + }; - /** - * Returns the audio volume, between 0 and 100, of the Subscriber. - * - *

    Generally you use this method in conjunction with the setAudioVolume() - * method.

    - * - * @return {Number} The audio volume, between 0 and 100, of the Subscriber. - * @see setAudioVolume() - * @method #getAudioVolume - * @memberOf Subscriber - */ - this.getAudioVolume = function() { - if(_properties.muted) { - return 0; - } - if (_streamContainer) return _streamContainer.getAudioVolume(); - else return _audioVolume; - }; - /** - * Toggles audio on and off. Starts subscribing to audio (if it is available and currently - * not being subscribed to) when the value is true; stops - * subscribing to audio (if it is currently being subscribed to) when the value - * is false. - *

    - * Note: This method only affects the local playback of audio. It has no impact on the - * audio for other connections subscribing to the same stream. If the Publsher is not - * publishing audio, enabling the Subscriber audio will have no practical effect. - *

    - * - * @param {Boolean} value Whether to start subscribing to audio (true) or not - * (false). - * - * @return {Subscriber} The Subscriber object. This lets you chain method calls, as in the - * following: - * - *
    mySubscriber.subscribeToAudio(true).subscribeToVideo(false);
    - * - * @see subscribeToVideo() - * @see Session.subscribe() - * @see StreamPropertyChangedEvent - * - * @method #subscribeToAudio - * @memberOf Subscriber - */ - this.subscribeToAudio = function(pValue) { - var value = OT.$.castToBoolean(pValue, true); - - if (_peerConnection) { - _peerConnection.subscribeToAudio(value && !_properties.subscribeMute); +/** +* Sends a signal to each client or a specified client in the session. Specify a +* to property of the signal parameter to limit the signal to +* be sent to a specific client; otherwise the signal is sent to each client connected to +* the session. +*

    +* The following example sends a signal of type "foo" with a specified data payload ("hello") +* to all clients connected to the session: +*

    +* session.signal({
    +*     type: "foo",
    +*     data: "hello"
    +*   },
    +*   function(error) {
    +*     if (error) {
    +*       console.log("signal error: " + error.message);
    +*     } else {
    +*       console.log("signal sent");
    +*     }
    +*   }
    +* );
    +* 
    +*

    +* Calling this method without specifying a recipient client (by setting the to +* property of the signal parameter) results in multiple signals sent (one to each +* client in the session). For information on charges for signaling, see the +* OpenTok pricing page. +*

    +* The following example sends a signal of type "foo" with a data payload ("hello") to a +* specific client connected to the session: +*

    +* session.signal({
    +*     type: "foo",
    +*     to: recipientConnection; // a Connection object
    +*     data: "hello"
    +*   },
    +*   function(error) {
    +*     if (error) {
    +*       console.log("signal error: " + error.message);
    +*     } else {
    +*       console.log("signal sent");
    +*     }
    +*   }
    +* );
    +* 
    +*

    +* Add an event handler for the signal event to listen for all signals sent in +* the session. Add an event handler for the signal:type event to listen for +* signals of a specified type only (replace type, in signal:type, +* with the type of signal to listen for). The Session object dispatches these events. (See +* events.) +* +* @param {Object} signal An object that contains the following properties defining the signal: +*

      +*
    • data — (String) The data to send. The limit to the length of data +* string is 8kB. Do not set the data string to null or +* undefined.
    • +*
    • to — (Connection) A Connection +* object corresponding to the client that the message is to be sent to. If you do not +* specify this property, the signal is sent to all clients connected to the session.
    • +*
    • type — (String) The type of the signal. You can use the type to +* filter signals when setting an event handler for the signal:type event +* (where you replace type with the type string). The maximum length of the +* type string is 128 characters, and it must contain only letters (A-Z and a-z), +* numbers (0-9), '-', '_', and '~'.
    • +* +*
    +* +*

    Each property is optional. If you set none of the properties, you will send a signal +* with no data or type to each client connected to the session.

    +* +* @param {Function} completionHandler A function that is called when sending the signal +* succeeds or fails. This function takes one parameter — error. +* On success, the completionHandler function is not passed any +* arguments. On error, the function is passed an error object, defined by the +* Error class. The error object has the following +* properties: +* +*
      +*
    • code — (Number) An error code, which can be one of the following: +* +* +* +* +* +* +* +* +* +* +* +* +* +*
      400 One of the signal properties — data, type, or to — +* is invalid.
      404 The client specified by the to property is not connected to +* the session.
      413 The type string exceeds the maximum length (128 bytes), +* or the data string exceeds the maximum size (8 kB).
      500 You are not connected to the OpenTok session.
      +*
    • +*
    • message — (String) A description of the error.
    • +*
    +* +*

    Note that the completionHandler success result (error == null) +* indicates that the options passed into the Session.signal() method are valid +* and the signal was sent. It does not indicate that the signal was successfully +* received by any of the intended recipients. +* +* @method #signal +* @memberOf Session +* @see signal and signal:type events +*/ + this.signal = function(options, completion) { + var _options = options, + _completion = completion; + + if (OT.$.isFunction(_options)) { + _completion = _options; + _options = null; + } - if (_session && _stream && value !== _properties.subscribeToAudio) { - _stream.setChannelActiveState('audio', value && !_properties.subscribeMute); - } - } + if (this.isNot('connected')) { + var notConnectedErrorMsg = 'Unable to send signal - you are not connected to the session.'; + dispatchError(500, notConnectedErrorMsg, _completion); + return; + } - _properties.subscribeToAudio = value; + _socket.signal(_options, _completion, this.logEvent); + if (options && options.data && (typeof(options.data) !== 'string')) { + OT.warn('Signaling of anything other than Strings is deprecated. ' + + 'Please update the data property to be a string.'); + } + }; - return this; - }; +/** +* Forces a remote connection to leave the session. +* +*

    +* The forceDisconnect() method is normally used as a moderation tool +* to remove users from an ongoing session. +*

    +*

    +* When a connection is terminated using the forceDisconnect(), +* sessionDisconnected, connectionDestroyed and +* streamDestroyed events are dispatched in the same way as they +* would be if the connection had terminated itself using the disconnect() +* method. However, the reason property of a {@link ConnectionEvent} or +* {@link StreamEvent} object specifies "forceDisconnected" as the reason +* for the destruction of the connection and stream(s). +*

    +*

    +* While you can use the forceDisconnect() method to terminate your own connection, +* calling the disconnect() method is simpler. +*

    +*

    +* The OT object dispatches an exception event if the user's role +* does not include permissions required to force other users to disconnect. +* You define a user's role when you create the user token using the +* generate_token() method using +* OpenTok +* server-side libraries or the +* Dashboard page. +* See ExceptionEvent and OT.on(). +*

    +*

    +* The application throws an error if the session is not connected. +*

    +* +*
    Events dispatched:
    +* +*

    +* connectionDestroyed (ConnectionEvent) — +* On clients other than which had the connection terminated. +*

    +*

    +* exception (ExceptionEvent) — +* The user's role does not allow forcing other user's to disconnect (event.code = +* 1530), +* or the specified stream is not publishing to the session (event.code = 1535). +*

    +*

    +* sessionDisconnected +* (SessionDisconnectEvent) — +* On the client which has the connection terminated. +*

    +*

    +* streamDestroyed (StreamEvent) — +* If streams are stopped as a result of the connection ending. +*

    +* +* @param {Connection} connection The connection to be disconnected from the session. +* This value can either be a Connection object or a connection +* ID (which can be obtained from the connectionId property of the Connection object). +* +* @param {Function} completionHandler (Optional) A function to be called when the call to the +* forceDiscononnect() method succeeds or fails. This function takes one parameter +* — error. On success, the completionHandler function is +* not passed any arguments. On error, the function is passed an error object +* parameter. The error object, defined by the Error +* class, has two properties: code (an integer) +* and message (a string), which identify the cause of the failure. +* Calling forceDisconnect() fails if the role assigned to your +* token is not "moderator"; in this case error.code is set to 1520. The following +* code adds a completionHandler when calling the forceDisconnect() +* method: +*
    +* session.forceDisconnect(connection, function (error) {
    +*   if (error) {
    +*       console.log(error);
    +*     } else {
    +*       console.log("Connection forced to disconnect: " + connection.id);
    +*     }
    +*   });
    +* 
    +* +* @method #forceDisconnect +* @memberOf Session +*/ - var muteAudio = function(_mute) { - _chrome.muteButton.muted(_mute); + this.forceDisconnect = function(connectionOrConnectionId, completionHandler) { + if (this.isNot('connected')) { + var notConnectedErrorMsg = 'Cannot call forceDisconnect(). You are not ' + + 'connected to the session.'; + dispatchError(OT.ExceptionCodes.NOT_CONNECTED, notConnectedErrorMsg, completionHandler); + return; + } - if(_mute === _properties.mute) { - return; - } - if(OT.$.browser() === 'Chrome' || TBPlugin.isInstalled()) { - _properties.subscribeMute = _properties.muted = _mute; - this.subscribeToAudio(_properties.subscribeToAudio); - } else { - if(_mute) { - _properties.premuteVolume = this.getAudioVolume(); - _properties.muted = true; - this.setAudioVolume(0); - } else if(_properties.premuteVolume || _properties.audioVolume) { - _properties.muted = false; - this.setAudioVolume(_properties.premuteVolume || _properties.audioVolume); - } - } - _properties.mute = _properties.mute; - }; - - var reasonMap = { - auto: 'quality', - publishVideo: 'publishVideo', - subscribeToVideo: 'subscribeToVideo' - }; + var notPermittedErrorMsg = 'This token does not allow forceDisconnect. ' + + 'The role must be at least `moderator` to enable this functionality'; + if (permittedTo('forceDisconnect')) { + var connectionId = typeof connectionOrConnectionId === 'string' ? + connectionOrConnectionId : connectionOrConnectionId.id; - /** - * Toggles video on and off. Starts subscribing to video (if it is available and - * currently not being subscribed to) when the value is true; - * stops subscribing to video (if it is currently being subscribed to) when the - * value is false. - *

    - * Note: This method only affects the local playback of video. It has no impact on - * the video for other connections subscribing to the same stream. If the Publsher is not - * publishing video, enabling the Subscriber video will have no practical video. - *

    - * - * @param {Boolean} value Whether to start subscribing to video (true) or not - * (false). - * - * @return {Subscriber} The Subscriber object. This lets you chain method calls, as in the - * following: - * - *
    mySubscriber.subscribeToVideo(true).subscribeToAudio(false);
    - * - * @see subscribeToAudio() - * @see Session.subscribe() - * @see StreamPropertyChangedEvent - * - * @method #subscribeToVideo - * @memberOf Subscriber - */ - this.subscribeToVideo = function(pValue, reason) { - var value = OT.$.castToBoolean(pValue, true); - - if(_container) { - var audioOnly = !(value && _stream.hasVideo); - _container.audioOnly(audioOnly); - _container.showPoster(audioOnly); - if(value && _container.video()) { - _container.loading(value); - _container.video().whenTimeIncrements(function(){ - _container.loading(false); - }, this); + _socket.forceDisconnect(connectionId, function(err) { + if (err) { + dispatchError(OT.ExceptionCodes.UNABLE_TO_FORCE_DISCONNECT, + notPermittedErrorMsg, completionHandler); + + } else if (completionHandler && OT.$.isFunction(completionHandler)) { + completionHandler.apply(null, arguments); } - } + }); + } else { + // if this throws an error the handleJsException won't occur + dispatchError(OT.ExceptionCodes.UNABLE_TO_FORCE_DISCONNECT, + notPermittedErrorMsg, completionHandler); + } + }; - if (_chrome && _chrome.videoDisabledIndicator) { - _chrome.videoDisabledIndicator.disableVideo(false); - } +/** +* Forces the publisher of the specified stream to stop publishing the stream. +* +*

    +* Calling this method causes the Session object to dispatch a streamDestroyed +* event on all clients that are subscribed to the stream (including the client that is +* publishing the stream). The reason property of the StreamEvent object is +* set to "forceUnpublished". +*

    +*

    +* The OT object dispatches an exception event if the user's role +* does not include permissions required to force other users to unpublish. +* You define a user's role when you create the user token using the generate_token() +* method using the +* OpenTok +* server-side libraries or the Dashboard +* page. +* You pass the token string as a parameter of the connect() method of the Session +* object. See ExceptionEvent and +* OT.on(). +*

    +* +*
    Events dispatched:
    +* +*

    +* exception (ExceptionEvent) — +* The user's role does not allow forcing other users to unpublish. +*

    +*

    +* streamDestroyed (StreamEvent) — +* The stream has been unpublished. The Session object dispatches this on all clients +* subscribed to the stream, as well as on the publisher's client. +*

    +* +* @param {Stream} stream The stream to be unpublished. +* +* @param {Function} completionHandler (Optional) A function to be called when the call to the +* forceUnpublish() method succeeds or fails. This function takes one parameter +* — error. On success, the completionHandler function is +* not passed any arguments. On error, the function is passed an error object +* parameter. The error object, defined by the Error +* class, has two properties: code (an integer) +* and message (a string), which identify the cause of the failure. Calling +* forceUnpublish() fails if the role assigned to your token is not "moderator"; +* in this case error.code is set to 1530. The following code adds a +* completionHandler when calling the forceUnpublish() method: +*
    +* session.forceUnpublish(stream, function (error) {
    +*   if (error) {
    +*       console.log(error);
    +*     } else {
    +*       console.log("Connection forced to disconnect: " + connection.id);
    +*     }
    +*   });
    +* 
    +* +* @method #forceUnpublish +* @memberOf Session +*/ + this.forceUnpublish = function(streamOrStreamId, completionHandler) { + if (this.isNot('connected')) { + var notConnectedErrorMsg = 'Cannot call forceUnpublish(). You are not ' + + 'connected to the session.'; + dispatchError(OT.ExceptionCodes.NOT_CONNECTED, notConnectedErrorMsg, completionHandler); + return; + } + + var notPermittedErrorMsg = 'This token does not allow forceUnpublish. ' + + 'The role must be at least `moderator` to enable this functionality'; - if (_peerConnection) { - _peerConnection.subscribeToVideo(value); + if (permittedTo('forceUnpublish')) { + var stream = typeof streamOrStreamId === 'string' ? + this.streams.get(streamOrStreamId) : streamOrStreamId; - if (_session && _stream && (value !== _properties.subscribeToVideo || - reason !== _lastSubscribeToVideoReason)) { - _stream.setChannelActiveState('video', value, reason); + _socket.forceUnpublish(stream.id, function(err) { + if (err) { + dispatchError(OT.ExceptionCodes.UNABLE_TO_FORCE_UNPUBLISH, + notPermittedErrorMsg, completionHandler); + } else if (completionHandler && OT.$.isFunction(completionHandler)) { + completionHandler.apply(null, arguments); } - } + }); + } else { + // if this throws an error the handleJsException won't occur + dispatchError(OT.ExceptionCodes.UNABLE_TO_FORCE_UNPUBLISH, + notPermittedErrorMsg, completionHandler); + } + }; - _properties.subscribeToVideo = value; - _lastSubscribeToVideoReason = reason; + this.getStateManager = function() { + OT.warn('Fixme: Have not implemented session.getStateManager'); + }; - if (reason !== 'loading') { - this.dispatchEvent(new OT.VideoEnabledChangedEvent( - value ? 'videoEnabled' : 'videoDisabled', - { - reason: reasonMap[reason] || 'subscribeToVideo' - } - )); - } + this.isConnected = function() { + return this.is('connected'); + }; - return this; - }; + this.capabilities = new OT.Capabilities([]); - this.isSubscribing = function() { - return _state.isSubscribing(); - }; +/** + * Dispatched when an archive recording of the session starts. + * + * @name archiveStarted + * @event + * @memberof Session + * @see ArchiveEvent + * @see Archiving overview. + */ - this.isWebRTC = true; +/** + * Dispatched when an archive recording of the session stops. + * + * @name archiveStopped + * @event + * @memberof Session + * @see ArchiveEvent + * @see Archiving overview. + */ - this.isLoading = function() { - return _container && _container.loading(); - }; +/** + * Dispatched when a new client (including your own) has connected to the session, and for + * every client in the session when you first connect. (The Session object also dispatches + * a sessionConnected event when your local client connects.) + * + * @name connectionCreated + * @event + * @memberof Session + * @see ConnectionEvent + * @see OT.initSession() + */ - this.videoWidth = function() { - return _streamContainer.videoWidth(); - }; +/** + * A client, other than your own, has disconnected from the session. + * @name connectionDestroyed + * @event + * @memberof Session + * @see ConnectionEvent + */ - this.videoHeight = function() { - return _streamContainer.videoHeight(); - }; +/** + * The page has connected to an OpenTok session. This event is dispatched asynchronously + * in response to a successful call to the connect() method of a Session + * object. Before calling the connect() method, initialize the session by + * calling the OT.initSession() method. For a code example and more details, + * see Session.connect(). + * @name sessionConnected + * @event + * @memberof Session + * @see SessionConnectEvent + * @see Session.connect() + * @see OT.initSession() + */ - /** - * Restricts the frame rate of the Subscriber's video stream, when you pass in - * true. When you pass in false, the frame rate of the video stream - * is not restricted. - *

    - * When the frame rate is restricted, the Subscriber video frame will update once or less per - * second. - *

    - * This feature is only available in sessions that use the OpenTok Media Router (sessions with - * the media mode - * set to routed), not in sessions with the media mode set to relayed. In relayed sessions, - * calling this method has no effect. - *

    - * Restricting the subscriber frame rate has the following benefits: - *

      - *
    • It reduces CPU usage.
    • - *
    • It reduces the network bandwidth consumed.
    • - *
    • It lets you subscribe to more streams simultaneously.
    • - *
    - *

    - * Reducing a subscriber's frame rate has no effect on the frame rate of the video in - * other clients. - * - * @param {Boolean} value Whether to restrict the Subscriber's video frame rate - * (true) or not (false). - * - * @return {Subscriber} The Subscriber object. This lets you chain method calls, as in the - * following: - * - *

    mySubscriber.restrictFrameRate(false).subscribeToAudio(true);
    - * - * @method #restrictFrameRate - * @memberOf Subscriber - */ - this.restrictFrameRate = function(val) { - OT.debug('OT.Subscriber.restrictFrameRate(' + val + ')'); +/** + * The client has disconnected from the session. This event may be dispatched asynchronously + * in response to a successful call to the disconnect() method of the Session object. + * The event may also be disptached if a session connection is lost inadvertantly, as in the case + * of a lost network connection. + *

    + * The default behavior is that all Subscriber objects are unsubscribed and removed from the + * HTML DOM. Each Subscriber object dispatches a destroyed event when the element is + * removed from the HTML DOM. If you call the preventDefault() method in the event + * listener for the sessionDisconnect event, the default behavior is prevented, and + * you can, optionally, clean up Subscriber objects using your own code. +* + * @name sessionDisconnected + * @event + * @memberof Session + * @see Session.disconnect() + * @see Session.forceDisconnect() + * @see SessionDisconnectEvent + */ - logAnalyticsEvent('restrictFrameRate', val.toString(), 'streamId', _stream.id); +/** + * A new stream, published by another client, has been created on this session. For streams + * published by your own client, the Publisher object dispatches a streamCreated + * event. For a code example and more details, see {@link StreamEvent}. + * @name streamCreated + * @event + * @memberof Session + * @see StreamEvent + * @see Session.publish() + */ - if (_session.sessionInfo.p2pEnabled) { - OT.warn('OT.Subscriber.restrictFrameRate: Cannot restrictFrameRate on a P2P session'); - } +/** + * A stream from another client has stopped publishing to the session. + *

    + * The default behavior is that all Subscriber objects that are subscribed to the stream are + * unsubscribed and removed from the HTML DOM. Each Subscriber object dispatches a + * destroyed event when the element is removed from the HTML DOM. If you call the + * preventDefault() method in the event listener for the + * streamDestroyed event, the default behavior is prevented and you can clean up + * Subscriber objects using your own code. See + * Session.getSubscribersForStream(). + *

    + * For streams published by your own client, the Publisher object dispatches a + * streamDestroyed event. + *

    + * For a code example and more details, see {@link StreamEvent}. + * @name streamDestroyed + * @event + * @memberof Session + * @see StreamEvent + */ - if (typeof val !== 'boolean') { - OT.error('OT.Subscriber.restrictFrameRate: expected a boolean value got a ' + typeof val); - } else { - _frameRateRestricted = val; - _stream.setRestrictFrameRate(val); - } - return this; - }; +/** + * Defines an event dispatched when property of a stream has changed. This can happen in + * in the following conditions: + *

    + *

      + *
    • A stream has started or stopped publishing audio or video (see + * Publisher.publishAudio() and + * Publisher.publishVideo()). Note + * that a subscriber's video can be disabled or enabled for reasons other than + * the publisher disabling or enabling it. A Subscriber object dispatches + * videoDisabled and videoEnabled events in all + * conditions that cause the subscriber's stream to be disabled or enabled. + *
    • + *
    • The videoDimensions property of the Stream object has + * changed (see Stream.videoDimensions). + *
    • + *
    • The videoType property of the Stream object has changed. + * This can happen in a stream published by a mobile device. (See + * Stream.videoType.) + *
    • + *
    + * + * @name streamPropertyChanged + * @event + * @memberof Session + * @see StreamPropertyChangedEvent + * @see Publisher.publishAudio() + * @see Publisher.publishVideo() + * @see Stream.hasAudio + * @see Stream.hasVideo + * @see Stream.videoDimensions + * @see Subscriber videoDisabled event + * @see Subscriber videoEnabled event + */ - this.on('styleValueChanged', updateChromeForStyleChange, this); +/** + * A signal was received from the session. The SignalEvent + * class defines this event object. It includes the following properties: + *
      + *
    • data — (String) The data string sent with the signal (if there + * is one).
    • + *
    • from — (Connection) The Connection + * corresponding to the client that sent with the signal.
    • + *
    • type — (String) The type assigned to the signal (if there is + * one).
    • + *
    + *

    + * You can register to receive all signals sent in the session, by adding an event handler + * for the signal event. For example, the following code adds an event handler + * to process all signals sent in the session: + *

    + * session.on("signal", function(event) {
    + *   console.log("Signal sent from connection: " + event.from.id);
    + *   console.log("Signal data: " + event.data);
    + * });
    + * 
    + *

    You can register for signals of a specfied type by adding an event handler for the + * signal:type event (replacing type with the actual type string + * to filter on). + * + * @name signal + * @event + * @memberof Session + * @see Session.signal() + * @see SignalEvent + * @see signal:type event + */ - this._ = { - archivingStatus: function(status) { - if(_chrome) { - _chrome.archive.setArchiving(status); - } - } - }; +/** + * A signal of the specified type was received from the session. The + * SignalEvent class defines this event object. + * It includes the following properties: + *

      + *
    • data — (String) The data string sent with the signal.
    • + *
    • from — (Connection) The Connection + * corresponding to the client that sent with the signal.
    • + *
    • type — (String) The type assigned to the signal (if there is one). + *
    • + *
    + *

    + * You can register for signals of a specfied type by adding an event handler for the + * signal:type event (replacing type with the actual type string + * to filter on). For example, the following code adds an event handler for signals of + * type "foo": + *

    + * session.on("signal:foo", function(event) {
    + *   console.log("foo signal sent from connection " + event.from.id);
    + *   console.log("Signal data: " + event.data);
    + * });
    + * 
    + *

    + * You can register to receive all signals sent in the session, by adding an event + * handler for the signal event. + * + * @name signal:type + * @event + * @memberof Session + * @see Session.signal() + * @see SignalEvent + * @see signal event + */ +}; - _state = new OT.SubscribingState(stateChangeFailed); +// tb_require('../helpers/helpers.js') +// tb_require('../helpers/lib/get_user_media.js') +// tb_require('../helpers/lib/widget_view.js') +// tb_require('./analytics.js') +// tb_require('./events.js') +// tb_require('./system_requirements.js') +// tb_require('./stylable_component.js') +// tb_require('./stream.js') +// tb_require('./connection.js') +// tb_require('./publishing_state.js') +// tb_require('./environment_loader.js') +// tb_require('./audio_context.js') +// tb_require('./chrome/chrome.js') +// tb_require('./chrome/backing_bar.js') +// tb_require('./chrome/name_panel.js') +// tb_require('./chrome/mute_button.js') +// tb_require('./chrome/archiving.js') +// tb_require('./chrome/audio_level_meter.js') +// tb_require('./peer_connection/publisher_peer_connection.js') +// tb_require('./screensharing/register.js') - /** - * Dispatched periodically to indicate the subscriber's audio level. The event is dispatched - * up to 60 times per second, depending on the browser. The audioLevel property - * of the event is audio level, from 0 to 1.0. See {@link AudioLevelUpdatedEvent} for more - * information. - *

    - * The following example adjusts the value of a meter element that shows volume of the - * subscriber. Note that the audio level is adjusted logarithmically and a moving average - * is applied: - *

    -   * var movingAvg = null;
    -   * subscriber.on('audioLevelUpdated', function(event) {
    -   *   if (movingAvg === null || movingAvg <= event.audioLevel) {
    -   *     movingAvg = event.audioLevel;
    -   *   } else {
    -   *     movingAvg = 0.7 * movingAvg + 0.3 * event.audioLevel;
    -   *   }
    -   *
    -   *   // 1.5 scaling to map the -30 - 0 dBm range to [0,1]
    -   *   var logLevel = (Math.log(movingAvg) / Math.LN10) / 1.5 + 1;
    -   *   logLevel = Math.min(Math.max(logLevel, 0), 1);
    -   *   document.getElementById('subscriberMeter').value = logLevel;
    -   * });
    -   * 
    - *

    This example shows the algorithm used by the default audio level indicator displayed - * in an audio-only Subscriber. - * - * @name audioLevelUpdated - * @event - * @memberof Subscriber - * @see AudioLevelUpdatedEvent - */ +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ - /** - * Dispatched when the video for the subscriber is disabled. - *

    - * The reason property defines the reason the video was disabled. This can be set to - * one of the following values: - *

    - * - *

      - * - *
    • "publishVideo" — The publisher stopped publishing video by calling - * publishVideo(false).
    • - * - *
    • "quality" — The OpenTok Media Router stopped sending video - * to the subscriber based on stream quality changes. This feature of the OpenTok Media - * Router has a subscriber drop the video stream when connectivity degrades. (The subscriber - * continues to receive the audio stream, if there is one.) - *

      - * Before sending this event, when the Subscriber's stream quality deteriorates to a level - * that is low enough that the video stream is at risk of being disabled, the Subscriber - * dispatches a videoDisableWarning event. - *

      - * If connectivity improves to support video again, the Subscriber object dispatches - * a videoEnabled event, and the Subscriber resumes receiving video. - *

      - * By default, the Subscriber displays a video disabled indicator when a - * videoDisabled event with this reason is dispatched and removes the indicator - * when the videoDisabled event with this reason is dispatched. You can control - * the display of this icon by calling the setStyle() method of the Subscriber, - * setting the videoDisabledDisplayMode property(or you can set the style when - * calling the Session.subscribe() method, setting the style property - * of the properties parameter). - *

      - * This feature is only available in sessions that use the OpenTok Media Router (sessions with - * the media mode - * set to routed), not in sessions with the media mode set to relayed. - *

    • - * - *
    • "subscribeToVideo" — The subscriber started or stopped subscribing to - * video, by calling subscribeToVideo(false). - *
    • - * - *
    - * - * @see VideoEnabledChangedEvent - * @see event:videoDisableWarning - * @see event:videoEnabled - * @name videoDisabled - * @event - * @memberof Subscriber - */ - - /** - * Dispatched when the OpenTok Media Router determines that the stream quality has degraded - * and the video will be disabled if the quality degrades more. If the quality degrades further, - * the Subscriber disables the video and dispatches a videoDisabled event. - *

    - * By default, the Subscriber displays a video disabled warning indicator when this event - * is dispatched (and the video is disabled). You can control the display of this icon by - * calling the setStyle() method and setting the - * videoDisabledDisplayMode property (or you can set the style when calling - * the Session.subscribe() method and setting the style property - * of the properties parameter). - *

    - * This feature is only available in sessions that use the OpenTok Media Router (sessions with - * the media mode - * set to routed), not in sessions with the media mode set to relayed. - * - * @see Event - * @see event:videoDisabled - * @see event:videoDisableWarningLifted - * @name videoDisableWarning - * @event - * @memberof Subscriber - */ - - /** - * Dispatched when the OpenTok Media Router determines that the stream quality has improved - * to the point at which the video being disabled is not an immediate risk. This event is - * dispatched after the Subscriber object dispatches a videoDisableWarning event. - *

    - * This feature is only available in sessions that use the OpenTok Media Router (sessions with - * the media mode - * set to routed), not in sessions with the media mode set to relayed. - * - * @see Event - * @see event:videoDisabled - * @see event:videoDisableWarning - * @name videoDisableWarningLifted - * @event - * @memberof Subscriber - */ - - /** - * Dispatched when the OpenTok Media Router resumes sending video to the subscriber - * after video was previously disabled. - *

    - * The reason property defines the reason the video was enabled. This can be set to - * one of the following values: - *

    - * - *

      - * - *
    • "publishVideo" — The publisher started publishing video by calling - * publishVideo(true).
    • - * - *
    • "quality" — The OpenTok Media Router resumed sending video - * to the subscriber based on stream quality changes. This feature of the OpenTok Media - * Router has a subscriber drop the video stream when connectivity degrades and then resume - * the video stream if the stream quality improves. - *

      - * This feature is only available in sessions that use the OpenTok Media Router (sessions with - * the media mode - * set to routed), not in sessions with the media mode set to relayed. - *

    • - * - *
    • "subscribeToVideo" — The subscriber started or stopped subscribing to - * video, by calling subscribeToVideo(false). - *
    • - * - *
    - * - *

    - * To prevent video from resuming, in the videoEnabled event listener, - * call subscribeToVideo(false) on the Subscriber object. - * - * @see VideoEnabledChangedEvent - * @see event:videoDisabled - * @name videoEnabled - * @event - * @memberof Subscriber - */ - - /** - * Dispatched when the Subscriber element is removed from the HTML DOM. When this event is - * dispatched, you may choose to adjust or remove HTML DOM elements related to the subscriber. - * @see Event - * @name destroyed - * @event - * @memberof Subscriber - */ - }; +// The default constraints +var defaultConstraints = { + audio: true, + video: true +}; -})(window); -!(function() { +/** + * The Publisher object provides the mechanism through which control of the + * published stream is accomplished. Calling the OT.initPublisher() method + * creates a Publisher object.

    + * + *

    The following code instantiates a session, and publishes an audio-video stream + * upon connection to the session:

    + * + *
    + *  var apiKey = ""; // Replace with your API key. See https://dashboard.tokbox.com/projects
    + *  var sessionID = ""; // Replace with your own session ID.
    + *                      // See https://dashboard.tokbox.com/projects
    + *  var token = ""; // Replace with a generated token that has been assigned the moderator role.
    + *                  // See https://dashboard.tokbox.com/projects
    + *
    + *  var session = OT.initSession(apiKey, sessionID);
    + *  session.on("sessionConnected", function(event) {
    + *    // This example assumes that a DOM element with the ID 'publisherElement' exists
    + *    var publisherProperties = {width: 400, height:300, name:"Bob's stream"};
    + *    publisher = OT.initPublisher('publisherElement', publisherProperties);
    + *    session.publish(publisher);
    + *  });
    + *  session.connect(token);
    + *  
    + * + *

    This example creates a Publisher object and adds its video to a DOM element + * with the ID publisherElement by calling the OT.initPublisher() + * method. It then publishes a stream to the session by calling + * the publish() method of the Session object.

    + * + * @property {Boolean} accessAllowed Whether the user has granted access to the camera + * and microphone. The Publisher object dispatches an accessAllowed event when + * the user grants access. The Publisher object dispatches an accessDenied event + * when the user denies access. + * @property {Element} element The HTML DOM element containing the Publisher. + * @property {String} id The DOM ID of the Publisher. + * @property {Stream} stream The {@link Stream} object corresponding the the stream of + * the Publisher. + * @property {Session} session The {@link Session} to which the Publisher belongs. + * + * @see OT.initPublisher + * @see Session.publish() + * + * @class Publisher + * @augments EventDispatcher + */ +OT.Publisher = function(options) { + // Check that the client meets the minimum requirements, if they don't the upgrade + // flow will be triggered. + if (!OT.checkSystemRequirements()) { + OT.upgradeSystemRequirements(); + return; + } - var parseErrorFromJSONDocument, - onGetResponseCallback, - onGetErrorCallback; + var _guid = OT.Publisher.nextId(), + _domId, + _widgetView, + _targetElement, + _stream, + _streamId, + _webRTCStream, + _session, + _peerConnections = {}, + _loaded = false, + _publishStartTime, + _microphone, + _chrome, + _audioLevelMeter, + _properties, + _validResolutions, + _validFrameRates = [ 1, 7, 15, 30 ], + _prevStats, + _state, + _iceServers, + _audioLevelCapable = OT.$.hasCapabilities('webAudio'), + _audioLevelSampler, + _isScreenSharing = options && ( + options.videoSource === 'screen' || + options.videoSource === 'window' || + options.videoSource === 'tab' || + options.videoSource === 'application' + ), + _connectivityAttemptPinger, + _publisher = this; + + _properties = OT.$.defaults(options || {}, { + publishAudio: _isScreenSharing ? false : true, + publishVideo: true, + mirror: _isScreenSharing ? false : true, + showControls: true, + fitMode: _isScreenSharing ? 'contain' : 'cover', + audioFallbackEnabled: _isScreenSharing ? false : true, + maxResolution: _isScreenSharing ? { width: 1920, height: 1920 } : undefined + }); - OT.SessionInfo = function(jsonDocument) { - var sessionJSON = jsonDocument[0]; + _validResolutions = { + '320x240': {width: 320, height: 240}, + '640x480': {width: 640, height: 480}, + '1280x720': {width: 1280, height: 720} + }; + + if (_isScreenSharing) { + if (window.location.protocol !== 'https:') { + OT.warn('Screen Sharing typically requires pages to be loadever over HTTPS - unless this ' + + 'browser is configured locally to allow non-SSL pages, permission will be denied ' + + 'without user input.'); + } + } - OT.log('SessionInfo Response:'); - OT.log(jsonDocument); + _prevStats = { + 'timeStamp' : OT.$.now() + }; - /*jshint camelcase:false*/ + OT.$.eventing(this); - this.sessionId = sessionJSON.session_id; - this.partnerId = sessionJSON.partner_id; - this.sessionStatus = sessionJSON.session_status; + if(!_isScreenSharing && _audioLevelCapable) { + _audioLevelSampler = new OT.AnalyserAudioLevelSampler(OT.audioContext()); - this.messagingServer = sessionJSON.messaging_server_url; + var audioLevelRunner = new OT.IntervalRunner(function() { + _audioLevelSampler.sample(function(audioInputLevel) { + OT.$.requestAnimationFrame(function() { + _publisher.dispatchEvent( + new OT.AudioLevelUpdatedEvent(audioInputLevel)); + }); + }); + }, 60); - this.messagingURL = sessionJSON.messaging_url; - this.symphonyAddress = sessionJSON.symphony_address; + this.on({ + 'audioLevelUpdated:added': function(count) { + if (count === 1) { + audioLevelRunner.start(); + } + }, + 'audioLevelUpdated:removed': function(count) { + if (count === 0) { + audioLevelRunner.stop(); + } + } + }); + } - this.p2pEnabled = !!(sessionJSON.properties && - sessionJSON.properties.p2p && - sessionJSON.properties.p2p.preference && - sessionJSON.properties.p2p.preference.value === 'enabled'); - }; + /// Private Methods + var logAnalyticsEvent = function(action, variation, payload, throttle) { + OT.analytics.logEvent({ + action: action, + variation: variation, + payload: payload, + 'sessionId': _session ? _session.sessionId : null, + 'connectionId': _session && + _session.isConnected() ? _session.connection.connectionId : null, + 'partnerId': _session ? _session.apiKey : OT.APIKEY, + streamId: _stream ? _stream.id : null + }, throttle); + }, - // Retrieves Session Info for +session+. The SessionInfo object will be passed - // to the +onSuccess+ callback. The +onFailure+ callback will be passed an error - // object and the DOMEvent that relates to the error. - OT.SessionInfo.get = function(session, onSuccess, onFailure) { - var sessionInfoURL = OT.properties.apiURL + '/session/' + session.id + '?extended=true', + logConnectivityEvent = function(variation, payload) { + if (variation === 'Attempt' || !_connectivityAttemptPinger) { + _connectivityAttemptPinger = new OT.ConnectivityAttemptPinger({ + action: 'Publish', + 'sessionId': _session ? _session.sessionId : null, + 'connectionId': _session && + _session.isConnected() ? _session.connection.connectionId : null, + 'partnerId': _session ? _session.apiKey : OT.APIKEY, + streamId: _stream ? _stream.id : null + }); + } + if (variation === 'Failure' && payload.reason !== 'Non-fatal') { + // We don't want to log an invalid sequence in this case because it was a + // non-fatal failure + _connectivityAttemptPinger.setVariation(variation); + } + logAnalyticsEvent('Publish', variation, payload); + }, - browser = OT.$.browserVersion(), + recordQOS = OT.$.bind(function(connection, parsedStats) { + var QoSBlob = { + streamType: 'WebRTC', + sessionId: _session ? _session.sessionId : null, + connectionId: _session && _session.isConnected() ? + _session.connection.connectionId : null, + partnerId: _session ? _session.apiKey : OT.APIKEY, + streamId: _stream ? _stream.id : null, + width: _widgetView ? Number(OT.$.width(_widgetView.domElement).replace('px', '')) + : undefined, + height: _widgetView ? Number(OT.$.height(_widgetView.domElement).replace('px', '')) + : undefined, + version: OT.properties.version, + mediaServerName: _session ? _session.sessionInfo.messagingServer : null, + p2pFlag: _session ? _session.sessionInfo.p2pEnabled : false, + duration: _publishStartTime ? new Date().getTime() - _publishStartTime.getTime() : 0, + remoteConnectionId: connection.id + }; + OT.analytics.logQOS( OT.$.extend(QoSBlob, parsedStats) ); + this.trigger('qos', parsedStats); + }, this), - startTime = OT.$.now(), + /// Private Events - options, + stateChangeFailed = function(changeFailed) { + OT.error('Publisher State Change Failed: ', changeFailed.message); + OT.debug(changeFailed); + }, - validateRawSessionInfo = function(sessionInfo) { - session.logEvent('Instrumentation', null, 'gsi', OT.$.now() - startTime); - var error = parseErrorFromJSONDocument(sessionInfo); - if (error === false) { - onGetResponseCallback(session, onSuccess, sessionInfo); - } else { - onGetErrorCallback(session, onFailure, error, JSON.stringify(sessionInfo)); - } - }; + onLoaded = OT.$.bind(function() { + if (_state.isDestroyed()) { + // The publisher was destroyed before loading finished + return; + } + OT.debug('OT.Publisher.onLoaded'); - if(browser.browser === 'IE' && browser.version < 10) { - sessionInfoURL = sessionInfoURL + '&format=json&token=' + encodeURIComponent(session.token) + - '&version=1&cache=' + OT.$.uuid(); - options = { - xdomainrequest: true - }; - } - else { - options = { - headers: { - 'X-TB-TOKEN-AUTH': session.token, - 'X-TB-VERSION': 1 - } - }; - } + _state.set('MediaBound'); - session.logEvent('getSessionInfo', 'Attempt', 'api_url', OT.properties.apiURL); + // If we have a session and we haven't created the stream yet then + // wait until that is complete before hiding the loading spinner + _widgetView.loading(this.session ? !_stream : false); - OT.$.getJSON(sessionInfoURL, options, function(error, sessionInfo) { - if(error) { - var responseText = sessionInfo; - onGetErrorCallback(session, onFailure, - new OT.Error(error.target && error.target.status || error.code, error.message || - 'Could not connect to the OpenTok API Server.'), responseText); - } else { - validateRawSessionInfo(sessionInfo); - } - }); - }; + _loaded = true; - var messageServerToClientErrorCodes = {}; - messageServerToClientErrorCodes['404'] = OT.ExceptionCodes.INVALID_SESSION_ID; - messageServerToClientErrorCodes['409'] = OT.ExceptionCodes.INVALID_SESSION_ID; - messageServerToClientErrorCodes['400'] = OT.ExceptionCodes.INVALID_SESSION_ID; - messageServerToClientErrorCodes['403'] = OT.ExceptionCodes.AUTHENTICATION_ERROR; + createChrome.call(this); - // Return the error in +jsonDocument+, if there is one. Otherwise it will return - // false. - parseErrorFromJSONDocument = function(jsonDocument) { - if(OT.$.isArray(jsonDocument)) { + this.trigger('initSuccess'); + this.trigger('loaded', this); + }, this), - var errors = OT.$.filter(jsonDocument, function(node) { - return node.error != null; - }); + onLoadFailure = OT.$.bind(function(reason) { + var errorCode = OT.ExceptionCodes.P2P_CONNECTION_FAILED; + var payload = { + reason: 'Publisher PeerConnection Error: ', + code: errorCode, + message: reason + }; + logConnectivityEvent('Failure', payload); - var numErrorNodes = errors.length; - if(numErrorNodes === 0) { - return false; - } + _state.set('Failed'); + this.trigger('publishComplete', new OT.Error(errorCode, + 'Publisher PeerConnection Error: ' + reason)); + + OT.handleJsException('Publisher PeerConnection Error: ' + reason, + OT.ExceptionCodes.P2P_CONNECTION_FAILED, { + session: _session, + target: this + }); + }, this), - var errorCode = errors[0].error.code; - if (messageServerToClientErrorCodes[errorCode.toString()]) { - errorCode = messageServerToClientErrorCodes[errorCode]; - } + onStreamAvailable = OT.$.bind(function(webOTStream) { + OT.debug('OT.Publisher.onStreamAvailable'); - return { - code: errorCode, - message: errors[0].error.errorMessage && errors[0].error.errorMessage.message - }; - } else { - return { - code: null, - message: 'Unknown error: getSessionInfo JSON response was badly formed' - }; - } - }; + _state.set('BindingMedia'); - onGetResponseCallback = function(session, onSuccess, rawSessionInfo) { - session.logEvent('getSessionInfo', 'Success', 'api_url', OT.properties.apiURL); + cleanupLocalStream(); + _webRTCStream = webOTStream; - onSuccess( new OT.SessionInfo(rawSessionInfo) ); - }; + _microphone = new OT.Microphone(_webRTCStream, !_properties.publishAudio); + this.publishVideo(_properties.publishVideo && + _webRTCStream.getVideoTracks().length > 0); - onGetErrorCallback = function(session, onFailure, error, responseText) { - session.logEvent('Connect', 'Failure', 'errorMessage', - 'GetSessionInfo:' + (error.code || 'No code') + ':' + error.message + ':' + - (responseText || 'Empty responseText from API server')); - onFailure(error, session); - }; + this.accessAllowed = true; + this.dispatchEvent(new OT.Event(OT.Event.names.ACCESS_ALLOWED, false)); -})(window); -!(function() { - /** - * A class defining properties of the capabilities property of a - * Session object. See Session.capabilities. - *

    - * All Capabilities properties are undefined until you have connected to a session - * and the Session object has dispatched the sessionConnected event. - *

    - * For more information on token roles, see the - * generate_token() - * method of the OpenTok server-side libraries. - * - * @class Capabilities - * - * @property {Number} forceDisconnect Specifies whether you can call - * the Session.forceDisconnect() method (1) or not (0). To call the - * Session.forceDisconnect() method, - * the user must have a token that is assigned the role of moderator. - * @property {Number} forceUnpublish Specifies whether you can call - * the Session.forceUnpublish() method (1) or not (0). To call the - * Session.forceUnpublish() method, the user must have a token that - * is assigned the role of moderator. - * @property {Number} publish Specifies whether you can publish to the session (1) or not (0). - * The ability to publish is based on a few factors. To publish, the user must have a token that - * is assigned a role that supports publishing. There must be a connected camera and microphone. - * @property {Number} subscribe Specifies whether you can subscribe to streams - * in the session (1) or not (0). Currently, this capability is available for all users on all - * platforms. - */ - OT.Capabilities = function(permissions) { - this.publish = OT.$.arrayIndexOf(permissions, 'publish') !== -1 ? 1 : 0; - this.subscribe = OT.$.arrayIndexOf(permissions, 'subscribe') !== -1 ? 1 : 0; - this.forceUnpublish = OT.$.arrayIndexOf(permissions, 'forceunpublish') !== -1 ? 1 : 0; - this.forceDisconnect = OT.$.arrayIndexOf(permissions, 'forcedisconnect') !== -1 ? 1 : 0; - this.supportsWebRTC = OT.$.hasCapabilities('webrtc') ? 1 : 0; + var videoContainerOptions = { + muted: true, + error: onVideoError + }; - this.permittedTo = function(action) { - return this.hasOwnProperty(action) && this[action] === 1; - }; - }; + _targetElement = _widgetView.bindVideo(_webRTCStream, + videoContainerOptions, + function(err) { + if (err) { + onLoadFailure(err); + return; + } -})(window); -!(function(window) { + onLoaded(); + }); + if(_audioLevelSampler && _webRTCStream.getAudioTracks().length > 0) { + _audioLevelSampler.webRTCStream = _webRTCStream; + } -/** - * The Session object returned by the OT.initSession() method provides access to - * much of the OpenTok functionality. - * - * @class Session - * @augments EventDispatcher - * - * @property {Capabilities} capabilities A {@link Capabilities} object that includes information - * about the capabilities of the client. All properties of the capabilities object - * are undefined until you have connected to a session and the Session object has dispatched the - * sessionConnected event. - * @property {Connection} connection The {@link Connection} object for this session. The - * connection property is only available once the Session object dispatches the sessionConnected - * event. The Session object asynchronously dispatches a sessionConnected event in response - * to a successful call to the connect() method. See: connect and - * {@link Connection}. - * @property {String} sessionId The session ID for this session. You pass this value into the - * OT.initSession() method when you create the Session object. (Note: a Session - * object is not connected to the OpenTok server until you call the connect() method of the - * object and the object dispatches a connected event. See {@link OT.initSession} and - * {@link connect}). - * For more information on sessions and session IDs, see - * Session creation. - */ - OT.Session = function(apiKey, sessionId) { - OT.$.eventing(this); + }, this), - // Check that the client meets the minimum requirements, if they don't the upgrade - // flow will be triggered. - if (!OT.checkSystemRequirements()) { - OT.upgradeSystemRequirements(); - return; - } + onStreamAvailableError = OT.$.bind(function(error) { + OT.error('OT.Publisher.onStreamAvailableError ' + error.name + ': ' + error.message); - if(sessionId == null) { - sessionId = apiKey; - apiKey = null; - } + _state.set('Failed'); + this.trigger('publishComplete', new OT.Error(OT.ExceptionCodes.UNABLE_TO_PUBLISH, + error.message)); + + if (_widgetView) _widgetView.destroy(); + + var payload = { + reason: 'GetUserMedia', + code: OT.ExceptionCodes.UNABLE_TO_PUBLISH, + message: 'Publisher failed to access camera/mic: ' + error.message + }; + logConnectivityEvent('Failure', payload); - this.id = this.sessionId = sessionId; - - var _initialConnection = true, - _apiKey = apiKey, - _token, - _sessionId = sessionId, - _socket, - _widgetId = OT.$.uuid(), - _connectionId, - _analytics = new OT.Analytics(), - sessionConnectFailed, - sessionDisconnectedHandler, - connectionCreatedHandler, - connectionDestroyedHandler, - streamCreatedHandler, - streamPropertyModifiedHandler, - streamDestroyedHandler, - archiveCreatedHandler, - archiveDestroyedHandler, - archiveUpdatedHandler, - reset, - disconnectComponents, - destroyPublishers, - destroySubscribers, - connectMessenger, - getSessionInfo, - onSessionInfoResponse, - permittedTo, - dispatchError; + OT.handleJsException(payload.reason, + payload.code, { + session: _session, + target: this + }); + }, this), + onScreenSharingError = OT.$.bind(function(error) { + OT.error('OT.Publisher.onScreenSharingError ' + error.message); + _state.set('Failed'); + + this.trigger('publishComplete', new OT.Error(OT.ExceptionCodes.UNABLE_TO_PUBLISH, + 'Screensharing: ' + error.message)); + + var payload = { + reason: 'ScreenSharing', + message:error.message + }; + logConnectivityEvent('Failure', payload); + }, this), + // The user has clicked the 'deny' button the the allow access dialog + // (or it's set to always deny) + onAccessDenied = OT.$.bind(function(error) { + OT.error('OT.Publisher.onStreamAvailableError Permission Denied'); + + _state.set('Failed'); + var errorMessage = 'Publisher Access Denied: Permission Denied' + + (error.message ? ': ' + error.message : ''); + var errorCode = OT.ExceptionCodes.UNABLE_TO_PUBLISH; + this.trigger('publishComplete', new OT.Error(errorCode, errorMessage)); + + var payload = { + reason: 'GetUserMedia', + code: errorCode, + message: errorMessage + }; + logConnectivityEvent('Failure', payload); - var setState = OT.$.statable(this, [ - 'disconnected', 'connecting', 'connected', 'disconnecting' - ], 'disconnected'); - - this.connection = null; - this.connections = new OT.Collection(); - this.streams = new OT.Collection(); - this.archives = new OT.Collection(); - - - //-------------------------------------- - // MESSAGE HANDLERS - //-------------------------------------- - - // The duplication of this and sessionConnectionFailed will go away when - // session and messenger are refactored - sessionConnectFailed = function(reason, code) { - setState('disconnected'); + this.dispatchEvent(new OT.Event(OT.Event.names.ACCESS_DENIED)); + }, this), - OT.error(reason); + onAccessDialogOpened = OT.$.bind(function() { + logAnalyticsEvent('accessDialog', 'Opened'); - this.trigger('sessionConnectFailed', - new OT.Error(code || OT.ExceptionCodes.CONNECT_FAILED, reason)); + this.dispatchEvent(new OT.Event(OT.Event.names.ACCESS_DIALOG_OPENED, true)); + }, this), - OT.handleJsException(reason, code || OT.ExceptionCodes.CONNECT_FAILED, { - session: this - }); - }; + onAccessDialogClosed = OT.$.bind(function() { + logAnalyticsEvent('accessDialog', 'Closed'); - sessionDisconnectedHandler = function(event) { - var reason = event.reason; - if(reason === 'networkTimedout') { - reason = 'networkDisconnected'; - this.logEvent('Connect', 'TimeOutDisconnect', 'reason', event.reason); - } else { - this.logEvent('Connect', 'Disconnected', 'reason', event.reason); - } + this.dispatchEvent( new OT.Event(OT.Event.names.ACCESS_DIALOG_CLOSED, false)); + }, this), - var publicEvent = new OT.SessionDisconnectEvent('sessionDisconnected', reason); + onVideoError = OT.$.bind(function(errorCode, errorReason) { + OT.error('OT.Publisher.onVideoError'); - reset.call(this); - disconnectComponents.call(this, reason); + var message = errorReason + (errorCode ? ' (' + errorCode + ')' : ''); + logAnalyticsEvent('stream', null, {reason:'Publisher while playing stream: ' + message}); - var defaultAction = OT.$.bind(function() { - // Publishers handle preventDefault'ing themselves - destroyPublishers.call(this, publicEvent.reason); - // Subscriers don't, destroy 'em if needed - if (!publicEvent.isDefaultPrevented()) destroySubscribers.call(this, publicEvent.reason); - }, this); + _state.set('Failed'); - this.dispatchEvent(publicEvent, defaultAction); - }; + if (_state.isAttemptingToPublish()) { + this.trigger('publishComplete', new OT.Error(OT.ExceptionCodes.UNABLE_TO_PUBLISH, + message)); + } else { + this.trigger('error', message); + } - connectionCreatedHandler = function(connection) { - // We don't broadcast events for the symphony connection - if (connection.id.match(/^symphony\./)) return; + OT.handleJsException('Publisher error playing stream: ' + message, + OT.ExceptionCodes.UNABLE_TO_PUBLISH, { + session: _session, + target: this + }); + }, this), - this.dispatchEvent(new OT.ConnectionEvent( - OT.Event.names.CONNECTION_CREATED, - connection - )); - }; + onPeerDisconnected = OT.$.bind(function(peerConnection) { + OT.debug('OT.Subscriber has been disconnected from the Publisher\'s PeerConnection'); - connectionDestroyedHandler = function(connection, reason) { - // We don't broadcast events for the symphony connection - if (connection.id.match(/^symphony\./)) return; - - // Don't delete the connection if it's ours. This only happens when - // we're about to receive a session disconnected and session disconnected - // will also clean up our connection. - if (connection.id === _socket.id()) return; + this.cleanupSubscriber(peerConnection.remoteConnection().id); + }, this), - this.dispatchEvent( - new OT.ConnectionEvent( - OT.Event.names.CONNECTION_DESTROYED, - connection, - reason - ) - ); - }; + onPeerConnectionFailure = OT.$.bind(function(code, reason, peerConnection, prefix) { + var payload = { + reason: prefix ? prefix : 'PeerConnectionError', + code: OT.ExceptionCodes.UNABLE_TO_PUBLISH, + message: (prefix ? prefix : '') + ':Publisher PeerConnection with connection ' + + (peerConnection && peerConnection.remoteConnection && + peerConnection.remoteConnection().id) + ' failed: ' + reason, + hasRelayCandidates: peerConnection.hasRelayCandidates() + }; + if (_state.isPublishing()) { + // We're already publishing so this is a Non-fatal failure, must be p2p and one of our + // peerconnections failed + payload.reason = 'Non-fatal'; + } + logConnectivityEvent('Failure', payload); + + OT.handleJsException('Publisher PeerConnection Error: ' + reason, + OT.ExceptionCodes.UNABLE_TO_PUBLISH, { + session: _session, + target: this + }); - streamCreatedHandler = function(stream) { - if(stream.connection.id !== this.connection.id) { - this.dispatchEvent(new OT.StreamEvent( - OT.Event.names.STREAM_CREATED, - stream, - null, - false - )); - } - }; + // We don't call cleanupSubscriber as it also logs a + // disconnected analytics event, which we don't want in this + // instance. The duplication is crufty though and should + // be tidied up. - streamPropertyModifiedHandler = function(event) { - var stream = event.target, - propertyName = event.changedProperty, - newValue = event.newValue; + delete _peerConnections[peerConnection.remoteConnection().id]; + }, this), - if (propertyName === 'videoDisableWarning' || propertyName === 'audioDisableWarning') { - return; // These are not public properties, skip top level event for them. - } + /// Private Helpers - if (propertyName === 'orientation') { - propertyName = 'videoDimensions'; - newValue = {width: newValue.width, height: newValue.height}; - } + // Assigns +stream+ to this publisher. The publisher listens + // for a bunch of events on the stream so it can respond to + // changes. + assignStream = OT.$.bind(function(stream) { + this.stream = _stream = stream; + _stream.on('destroyed', this.disconnect, this); - this.dispatchEvent(new OT.StreamPropertyChangedEvent( - OT.Event.names.STREAM_PROPERTY_CHANGED, - stream, - propertyName, - event.oldValue, - newValue - )); - }; + _state.set('Publishing'); + _widgetView.loading(!_loaded); + _publishStartTime = new Date(); - streamDestroyedHandler = function(stream, reason) { + this.trigger('publishComplete', null, this); - // if the stream is one of ours we delegate handling - // to the publisher itself. - if(stream.connection.id === this.connection.id) { - OT.$.forEach(OT.publishers.where({ streamId: stream.id }), OT.$.bind(function(publisher) { - publisher._.unpublishFromSession(this, reason); - }, this)); - return; - } + this.dispatchEvent(new OT.StreamEvent('streamCreated', stream, null, false)); - var event = new OT.StreamEvent('streamDestroyed', stream, reason, true); + var payload = { + streamType: 'WebRTC', + }; + logConnectivityEvent('Success', payload); + }, this), - var defaultAction = OT.$.bind(function() { - if (!event.isDefaultPrevented()) { - // If we are subscribed to any of the streams we should unsubscribe - OT.$.forEach(OT.subscribers.where({streamId: stream.id}), function(subscriber) { - if (subscriber.session.id === this.id) { - if(subscriber.stream) { - subscriber.destroy('streamDestroyed'); - } - } - }, this); - } else { - // @TODO Add a one time warning that this no longer cleans up the publisher + // Clean up our LocalMediaStream + cleanupLocalStream = function() { + if (_webRTCStream) { + // Stop revokes our access cam and mic access for this instance + // of localMediaStream. + _webRTCStream.stop(); + _webRTCStream = null; } - }, this); + }, - this.dispatchEvent(event, defaultAction); - }; + createPeerConnectionForRemote = OT.$.bind(function(remoteConnection) { + var peerConnection = _peerConnections[remoteConnection.id]; - archiveCreatedHandler = function(archive) { - this.dispatchEvent(new OT.ArchiveEvent('archiveStarted', archive)); - }; + if (!peerConnection) { + var startConnectingTime = OT.$.now(); - archiveDestroyedHandler = function(archive) { - this.dispatchEvent(new OT.ArchiveEvent('archiveDestroyed', archive)); - }; + logAnalyticsEvent('createPeerConnection', 'Attempt'); - archiveUpdatedHandler = function(event) { - var archive = event.target, - propertyName = event.changedProperty, - newValue = event.newValue; + // Cleanup our subscriber when they disconnect + remoteConnection.on('destroyed', + OT.$.bind(this.cleanupSubscriber, this, remoteConnection.id)); + + peerConnection = _peerConnections[remoteConnection.id] = new OT.PublisherPeerConnection( + remoteConnection, + _session, + _streamId, + _webRTCStream + ); - if(propertyName === 'status' && newValue === 'stopped') { - this.dispatchEvent(new OT.ArchiveEvent('archiveStopped', archive)); - } else { - this.dispatchEvent(new OT.ArchiveEvent('archiveUpdated', archive)); - } - }; + peerConnection.on({ + connected: function() { + var payload = { + pcc: parseInt(OT.$.now() - startConnectingTime, 10), + hasRelayCandidates: peerConnection.hasRelayCandidates() + }; + logAnalyticsEvent('createPeerConnection', 'Success', payload); + }, + disconnected: onPeerDisconnected, + error: onPeerConnectionFailure, + qos: recordQOS + }, this); - // Put ourselves into a pristine state - reset = function() { - this.token = _token = null; - setState('disconnected'); - this.connection = null; - this.capabilities = new OT.Capabilities([]); - this.connections.destroy(); - this.streams.destroy(); - this.archives.destroy(); - }; + peerConnection.init(_iceServers); + } - disconnectComponents = function(reason) { - OT.$.forEach(OT.publishers.where({session: this}), function(publisher) { - publisher.disconnect(reason); - }); + return peerConnection; + }, this), - OT.$.forEach(OT.subscribers.where({session: this}), function(subscriber) { - subscriber.disconnect(); - }); - }; + /// Chrome - destroyPublishers = function(reason) { - OT.$.forEach(OT.publishers.where({session: this}), function(publisher) { - publisher._.streamDestroyed(reason); - }); - }; + // If mode is false, then that is the mode. If mode is true then we'll + // definitely display the button, but we'll defer the model to the + // Publishers buttonDisplayMode style property. + chromeButtonMode = function(mode) { + if (mode === false) return 'off'; - destroySubscribers = function(reason) { - OT.$.forEach(OT.subscribers.where({session: this}), function(subscriber) { - subscriber.destroy(reason); - }); - }; + var defaultMode = this.getStyle('buttonDisplayMode'); - connectMessenger = function() { - OT.debug('OT.Session: connecting to Raptor'); + // The default model is false, but it's overridden by +mode+ being true + if (defaultMode === false) return 'on'; - var socketUrl = this.sessionInfo.messagingURL, - symphonyUrl = OT.properties.symphonyAddresss || this.sessionInfo.symphonyAddress; + // defaultMode is either true or auto. + return defaultMode; + }, - _socket = new OT.Raptor.Socket(_widgetId, socketUrl, symphonyUrl, - OT.SessionDispatcher(this)); + updateChromeForStyleChange = function(key, value) { + if (!_chrome) return; - var analyticsPayload = [ - socketUrl, OT.$.userAgent(), OT.properties.version, - window.externalHost ? 'yes' : 'no' - ]; + switch(key) { + case 'nameDisplayMode': + _chrome.name.setDisplayMode(value); + _chrome.backingBar.setNameMode(value); + break; - _socket.connect(_token, this.sessionInfo, OT.$.bind(function(error, sessionState) { - if (error) { - _socket = void 0; - analyticsPayload.splice(0,0,error.message); - this.logEvent('Connect', 'Failure', - 'reason|webSocketServerUrl|userAgent|sdkVersion|chromeFrame', - analyticsPayload.map(function(e) { return e.replace('|', '\\|'); }).join('|')); + case 'showArchiveStatus': + logAnalyticsEvent('showArchiveStatus', 'styleChange', {mode: value ? 'on': 'off'}); + _chrome.archive.setShowArchiveStatus(value); + break; - sessionConnectFailed.call(this, error.message, error.code); - return; + case 'buttonDisplayMode': + _chrome.muteButton.setDisplayMode(value); + _chrome.backingBar.setMuteMode(value); + break; + + case 'audioLevelDisplayMode': + _chrome.audioLevel.setDisplayMode(value); + break; + + case 'backgroundImageURI': + _widgetView.setBackgroundImageURI(value); } + }, - OT.debug('OT.Session: Received session state from Raptor', sessionState); + createChrome = function() { - this.connection = this.connections.get(_socket.id()); - if(this.connection) { - this.capabilities = this.connection.permissions; + if(!this.getStyle('showArchiveStatus')) { + logAnalyticsEvent('showArchiveStatus', 'createChrome', {mode: 'off'}); } - setState('connected'); + var widgets = { + backingBar: new OT.Chrome.BackingBar({ + nameMode: !_properties.name ? 'off' : this.getStyle('nameDisplayMode'), + muteMode: chromeButtonMode.call(this, this.getStyle('buttonDisplayMode')) + }), - this.logEvent('Connect', 'Success', - 'webSocketServerUrl|userAgent|sdkVersion|chromeFrame', - OT.$.map(analyticsPayload, function(e) { - return e.replace('|', '\\|'); - }).join('|'), {connectionId: this.connection.id}); - - // Listen for our own connection's destroyed event so we know when we've been disconnected. - this.connection.on('destroyed', sessionDisconnectedHandler, this); - - // Listen for connection updates - this.connections.on({ - add: connectionCreatedHandler, - remove: connectionDestroyedHandler - }, this); + name: new OT.Chrome.NamePanel({ + name: _properties.name, + mode: this.getStyle('nameDisplayMode') + }), - // Listen for stream updates - this.streams.on({ - add: streamCreatedHandler, - remove: streamDestroyedHandler, - update: streamPropertyModifiedHandler - }, this); + muteButton: new OT.Chrome.MuteButton({ + muted: _properties.publishAudio === false, + mode: chromeButtonMode.call(this, this.getStyle('buttonDisplayMode')) + }), - this.archives.on({ - add: archiveCreatedHandler, - remove: archiveDestroyedHandler, - update: archiveUpdatedHandler - }, this); + archive: new OT.Chrome.Archiving({ + show: this.getStyle('showArchiveStatus'), + archiving: false + }) + }; - this.dispatchEvent( - new OT.SessionConnectEvent(OT.Event.names.SESSION_CONNECTED), OT.$.bind(function() { - this.connections._triggerAddEvents(); // { id: this.connection.id } - this.streams._triggerAddEvents(); // { id: this.stream.id } - this.archives._triggerAddEvents(); - }, this) - ); + if (_audioLevelCapable) { + var audioLevelTransformer = new OT.AudioLevelTransformer(); - }, this)); - }; + var audioLevelUpdatedHandler = function(evt) { + _audioLevelMeter.setValue(audioLevelTransformer.transform(evt.audioLevel)); + }; - getSessionInfo = function() { - if (this.is('connecting')) { - OT.SessionInfo.get( - this, - OT.$.bind(onSessionInfoResponse, this), - OT.$.bind(function(error) { - sessionConnectFailed.call(this, error.message + - (error.code ? ' (' + error.code + ')' : ''), error.code); - }, this) - ); - } - }; + _audioLevelMeter = new OT.Chrome.AudioLevelMeter({ + mode: this.getStyle('audioLevelDisplayMode'), + onActivate: function() { + _publisher.on('audioLevelUpdated', audioLevelUpdatedHandler); + }, + onPassivate: function() { + _publisher.off('audioLevelUpdated', audioLevelUpdatedHandler); + } + }); - onSessionInfoResponse = function(sessionInfo) { - if (this.is('connecting')) { - var overrides = OT.properties.sessionInfoOverrides; - this.sessionInfo = sessionInfo; - if (overrides != null && typeof overrides === 'object') { - this.sessionInfo = OT.$.defaults(overrides, this.sessionInfo); - console.log('is', this.sessionInfo); - } - if (this.sessionInfo.partnerId && this.sessionInfo.partnerId !== _apiKey) { - this.apiKey = _apiKey = this.sessionInfo.partnerId; + widgets.audioLevel = _audioLevelMeter; + } - var reason = 'Authentication Error: The API key does not match the token or session.'; + _chrome = new OT.Chrome({ + parent: _widgetView.domElement + }).set(widgets).on({ + muted: OT.$.bind(this.publishAudio, this, false), + unmuted: OT.$.bind(this.publishAudio, this, true) + }); - this.logEvent('Connect', 'Failure', 'reason', 'GetSessionInfo:' + - OT.ExceptionCodes.AUTHENTICATION_ERROR + ':' + reason); + if(_audioLevelMeter && this.getStyle('audioLevelDisplayMode') === 'auto') { + _audioLevelMeter[_widgetView.audioOnly() ? 'show' : 'hide'](); + } + }, - sessionConnectFailed.call(this, reason, OT.ExceptionCodes.AUTHENTICATION_ERROR); - } else { - connectMessenger.call(this); + reset = OT.$.bind(function() { + if (_chrome) { + _chrome.destroy(); + _chrome = null; } - } - }; - // Check whether we have permissions to perform the action. - permittedTo = OT.$.bind(function(action) { - return this.capabilities.permittedTo(action); - }, this); + this.disconnect(); - dispatchError = OT.$.bind(function(code, message, completionHandler) { - OT.dispatchError(code, message, completionHandler, this); - }, this); + _microphone = null; - this.logEvent = function(action, variation, payloadType, payload, options) { - /* jshint camelcase:false */ - var event = { - action: action, - variation: variation, - payload_type: payloadType, - payload: payload, - session_id: _sessionId, - partner_id: _apiKey, - widget_id: _widgetId, - widget_type: 'Controller' - }; - if (this.connection && this.connection.id) _connectionId = event.connection_id = - this.connection.id; - else if (_connectionId) event.connection_id = _connectionId; + if (_targetElement) { + _targetElement.destroy(); + _targetElement = null; + } - if (options) event = OT.$.extend(options, event); - _analytics.logEvent(event); - }; + cleanupLocalStream(); - /** - * Connects to an OpenTok session. - *

    - * Upon a successful connection, the completion handler (the second parameter of the method) is - * invoked without an error object passed in. (If there is an error connecting, the completion - * handler is invoked with an error object.) Make sure that you have successfully connected to the - * session before calling other methods of the Session object. - *

    - *

    - * The Session object dispatches a connectionCreated event when any client - * (including your own) connects to to the session. - *

    - * - *
    - * Example - *
    - *

    - * The following code initializes a session and sets up an event listener for when the session - * connects: - *

    - *
    - *  var apiKey = ""; // Replace with your API key. See https://dashboard.tokbox.com/projects
    - *  var sessionID = ""; // Replace with your own session ID.
    - *                      // See https://dashboard.tokbox.com/projects
    - *  var token = ""; // Replace with a generated token that has been assigned the moderator role.
    - *                  // See https://dashboard.tokbox.com/projects
    - *
    - *  var session = OT.initSession(apiKey, sessionID);
    - *  session.on("sessionConnected", function(sessionConnectEvent) {
    - *      //
    - *  });
    - *  session.connect(token);
    - *  
    - *

    - *

    - * In this example, the sessionConnectHandler() function is passed an event - * object of type {@link SessionConnectEvent}. - *

    - * - *
    - * Events dispatched: - *
    - * - *

    - * exception (ExceptionEvent) — Dispatched - * by the OT class locally in the event of an error. - *

    - *

    - * connectionCreated (ConnectionEvent) — - * Dispatched by the Session object on all clients connected to the session. - *

    - *

    - * sessionConnected (SessionConnectEvent) - * — Dispatched locally by the Session object when the connection is established. - *

    - * - * @param {String} token The session token. You generate a session token using our - * server-side libraries or the - * Dashboard page. For more information, see - * Connection token creation. - * - * @param {Function} completionHandler (Optional) A function to be called when the call to the - * connect() method succeeds or fails. This function takes one parameter — - * error (see the Error object). - * On success, the completionHandler function is not passed any - * arguments. On error, the function is passed an error object parameter - * (see the Error object). The - * error object has two properties: code (an integer) and - * message (a string), which identify the cause of the failure. The following - * code adds a completionHandler when calling the connect() method: - *
    -  * session.connect(token, function (error) {
    -  *   if (error) {
    -  *       console.log(error.message);
    -  *   } else {
    -  *     console.log("Connected to session.");
    -  *   }
    -  * });
    -  * 
    - *

    - * Note that upon connecting to the session, the Session object dispatches a - * sessionConnected event in addition to calling the completionHandler. - * The SessionConnectEvent object, which defines the sessionConnected event, - * includes connections and streams properties, which - * list the connections and streams in the session when you connect. - *

    - * - * @see SessionConnectEvent - * @method #connect - * @memberOf Session - */ - this.connect = function(token) { + if (_widgetView) { + _widgetView.destroy(); + _widgetView = null; + } + + if (_session) { + this._.unpublishFromSession(_session, 'reset'); + } + + this.id = _domId = null; + this.stream = _stream = null; + _loaded = false; + + this.session = _session = null; + + if (!_state.isDestroyed()) _state.set('NotPublishing'); + }, this); + + OT.StylableComponent(this, { + showArchiveStatus: true, + nameDisplayMode: 'auto', + buttonDisplayMode: 'auto', + audioLevelDisplayMode: _isScreenSharing ? 'off' : 'auto', + backgroundImageURI: null + }, _properties.showControls, function (payload) { + logAnalyticsEvent('SetStyle', 'Publisher', payload, 0.1); + }); + + var setAudioOnly = function(audioOnly) { + if (_widgetView) { + _widgetView.audioOnly(audioOnly); + _widgetView.showPoster(audioOnly); + } + + if (_audioLevelMeter && _publisher.getStyle('audioLevelDisplayMode') === 'auto') { + _audioLevelMeter[audioOnly ? 'show' : 'hide'](); + } + }; + + this.publish = function(targetElement) { + OT.debug('OT.Publisher: publish'); + + if ( _state.isAttemptingToPublish() || _state.isPublishing() ) reset(); + _state.set('GetUserMedia'); + + if (!_properties.constraints) { + _properties.constraints = OT.$.clone(defaultConstraints); - if(apiKey == null && arguments.length > 1 && - (typeof arguments[0] === 'string' || typeof arguments[0] === 'number') && - typeof arguments[1] === 'string') { - _apiKey = token.toString(); - token = arguments[1]; + if (_isScreenSharing) { + if (_properties.audioSource != null) { + OT.warn('Invalid audioSource passed to Publisher - when using screen sharing no ' + + 'audioSource may be used'); + } + _properties.audioSource = null; } - // The completion handler is always the last argument. - var completionHandler = arguments[arguments.length - 1]; + if(_properties.audioSource === null || _properties.audioSource === false) { + _properties.constraints.audio = false; + _properties.publishAudio = false; + } else { + if(typeof _properties.audioSource === 'object') { + if(_properties.audioSource.deviceId != null) { + _properties.audioSource = _properties.audioSource.deviceId; + } else { + OT.warn('Invalid audioSource passed to Publisher. Expected either a device ID'); + } + } - if (this.is('connecting', 'connected')) { - OT.warn('OT.Session: Cannot connect, the session is already ' + this.state); - return this; + if (_properties.audioSource) { + if (typeof _properties.constraints.audio !== 'object') { + _properties.constraints.audio = {}; + } + if (!_properties.constraints.audio.mandatory) { + _properties.constraints.audio.mandatory = {}; + } + if (!_properties.constraints.audio.optional) { + _properties.constraints.audio.optional = []; + } + _properties.constraints.audio.mandatory.sourceId = + _properties.audioSource; + } } - reset.call(this); - setState('connecting'); - this.token = _token = !OT.$.isFunction(token) && token; - - // Get a new widget ID when reconnecting. - if (_initialConnection) { - _initialConnection = false; - } else { - _widgetId = OT.$.uuid(); - } + if(_properties.videoSource === null || _properties.videoSource === false) { + _properties.constraints.video = false; + _properties.publishVideo = false; + } else { + + if(typeof _properties.videoSource === 'object' && + _properties.videoSource.deviceId == null) { + OT.warn('Invalid videoSource passed to Publisher. Expected either a device ' + + 'ID or device.'); + _properties.videoSource = null; + } + + var _setupVideoDefaults = function() { + if (typeof _properties.constraints.video !== 'object') { + _properties.constraints.video = {}; + } + if (!_properties.constraints.video.mandatory) { + _properties.constraints.video.mandatory = {}; + } + if (!_properties.constraints.video.optional) { + _properties.constraints.video.optional = []; + } + }; + + if (_properties.videoSource) { + _setupVideoDefaults(); - if (completionHandler && OT.$.isFunction(completionHandler)) { - this.once('sessionConnected', OT.$.bind(completionHandler, null, null)); - this.once('sessionConnectFailed', completionHandler); - } + var mandatory = _properties.constraints.video.mandatory; + + if(_isScreenSharing) { + // this is handled by the extension helpers + } else if(_properties.videoSource.deviceId != null) { + mandatory.sourceId = _properties.videoSource.deviceId; + } else { + mandatory.sourceId = _properties.videoSource; + } + } - if(_apiKey == null || OT.$.isFunction(_apiKey)) { - setTimeout(OT.$.bind( - sessionConnectFailed, - this, - 'API Key is undefined. You must pass an API Key to initSession.', - OT.ExceptionCodes.AUTHENTICATION_ERROR - )); + if (_properties.resolution) { + if (!_validResolutions.hasOwnProperty(_properties.resolution)) { + OT.warn('Invalid resolution passed to the Publisher. Got: ' + + _properties.resolution + ' expecting one of "' + + OT.$.keys(_validResolutions).join('","') + '"'); + } else { + _properties.videoDimensions = _validResolutions[_properties.resolution]; + _setupVideoDefaults(); + if (OT.$.env.name === 'Chrome') { + _properties.constraints.video.optional = + _properties.constraints.video.optional.concat([ + {minWidth: _properties.videoDimensions.width}, + {maxWidth: _properties.videoDimensions.width}, + {minHeight: _properties.videoDimensions.height}, + {maxHeight: _properties.videoDimensions.height} + ]); + } else { + // This is not supported + } + } + } - return this; - } + if (_properties.maxResolution) { + _setupVideoDefaults(); + if (_properties.maxResolution.width > 1920) { + OT.warn('Invalid maxResolution passed to the Publisher. maxResolution.width must ' + + 'be less than or equal to 1920'); + _properties.maxResolution.width = 1920; + } + if (_properties.maxResolution.height > 1920) { + OT.warn('Invalid maxResolution passed to the Publisher. maxResolution.height must ' + + 'be less than or equal to 1920'); + _properties.maxResolution.height = 1920; + } + + _properties.videoDimensions = _properties.maxResolution; + + if (OT.$.env.name === 'Chrome') { + _setupVideoDefaults(); + _properties.constraints.video.mandatory.maxWidth = + _properties.videoDimensions.width; + _properties.constraints.video.mandatory.maxHeight = + _properties.videoDimensions.height; + } else { + // This is not suppoted + } + } - if (!_sessionId) { - setTimeout(OT.$.bind( - sessionConnectFailed, - this, - 'SessionID is undefined. You must pass a sessionID to initSession.', - OT.ExceptionCodes.INVALID_SESSION_ID - )); + if (_properties.frameRate !== void 0 && + OT.$.arrayIndexOf(_validFrameRates, _properties.frameRate) === -1) { + OT.warn('Invalid frameRate passed to the publisher got: ' + + _properties.frameRate + ' expecting one of ' + _validFrameRates.join(',')); + delete _properties.frameRate; + } else if (_properties.frameRate) { + _setupVideoDefaults(); + _properties.constraints.video.optional = + _properties.constraints.video.optional.concat([ + { minFrameRate: _properties.frameRate }, + { maxFrameRate: _properties.frameRate } + ]); + } - return this; } - this.apiKey = _apiKey = _apiKey.toString(); + } else { + OT.warn('You have passed your own constraints not using ours'); + } - // Ugly hack, make sure OT.APIKEY is set - if (OT.APIKEY.length === 0) { - OT.APIKEY = _apiKey; - } + if (_properties.style) { + this.setStyle(_properties.style, null, true); + } - var analyticsPayload = [ - OT.$.userAgent(), OT.properties.version, - window.externalHost ? 'yes' : 'no' - ]; - this.logEvent( 'Connect', 'Attempt', - 'userAgent|sdkVersion|chromeFrame', - analyticsPayload.map(function(e) { return e.replace('|', '\\|'); }).join('|') - ); + if (_properties.name) { + _properties.name = _properties.name.toString(); + } - getSessionInfo.call(this); - return this; - }; + _properties.classNames = 'OT_root OT_publisher'; - /** - * Disconnects from the OpenTok session. - * - *

    - * Calling the disconnect() method ends your connection with the session. In the - * course of terminating your connection, it also ceases publishing any stream(s) you were - * publishing. - *

    - *

    - * Session objects on remote clients dispatch streamDestroyed events for any - * stream you were publishing. The Session object dispatches a sessionDisconnected - * event locally. The Session objects on remote clients dispatch connectionDestroyed - * events, letting other connections know you have left the session. The - * {@link SessionDisconnectEvent} and {@link StreamEvent} objects that define the - * sessionDisconnect and connectionDestroyed events each have a - * reason property. The reason property lets the developer determine - * whether the connection is being terminated voluntarily and whether any streams are being - * destroyed as a byproduct of the underlying connection's voluntary destruction. - *

    - *

    - * If the session is not currently connected, calling this method causes a warning to be logged. - * See OT.setLogLevel(). - *

    - * - *

    - * Note: If you intend to reuse a Publisher object created using - * OT.initPublisher() to publish to different sessions sequentially, call either - * Session.disconnect() or Session.unpublish(). Do not call both. - * Then call the preventDefault() method of the streamDestroyed or - * sessionDisconnected event object to prevent the Publisher object from being - * removed from the page. Be sure to call preventDefault() only if the - * connection.connectionId property of the Stream object in the event matches the - * connection.connectionId property of your Session object (to ensure that you are - * preventing the default behavior for your published streams, not for other streams that you - * subscribe to). - *

    - * - *
    - * Events dispatched: - *
    - *

    - * sessionDisconnected - * (SessionDisconnectEvent) - * — Dispatched locally when the connection is disconnected. - *

    - *

    - * connectionDestroyed (ConnectionEvent) — - * Dispatched on other clients, along with the streamDestroyed event (as warranted). - *

    - * - *

    - * streamDestroyed (StreamEvent) — - * Dispatched on other clients if streams are lost as a result of the session disconnecting. - *

    - * - * @method #disconnect - * @memberOf Session - */ - var disconnect = OT.$.bind(function disconnect(drainSocketBuffer) { - if (_socket && _socket.isNot('disconnected')) { - if (_socket.isNot('disconnecting')) { - setState('disconnecting'); - _socket.disconnect(drainSocketBuffer); - } - } - else { - reset.call(this); - } - }, this); + // Defer actually creating the publisher DOM nodes until we know + // the DOM is actually loaded. + OT.onLoad(function() { + _widgetView = new OT.WidgetView(targetElement, _properties); + _publisher.id = _domId = _widgetView.domId(); + _publisher.element = _widgetView.domElement; - this.disconnect = function(drainSocketBuffer) { - disconnect(drainSocketBuffer !== void 0 ? drainSocketBuffer : true); - }; + _widgetView.on('videoDimensionsChanged', function(oldValue, newValue) { + if (_stream) { + _stream.setVideoDimensions(newValue.width, newValue.height); + } + _publisher.dispatchEvent( + new OT.VideoDimensionsChangedEvent(_publisher, oldValue, newValue) + ); + }); - this.destroy = function(reason) { - this.streams.destroy(); - this.connections.destroy(); - this.archives.destroy(); - disconnect(reason !== 'unloaded'); - }; + _widgetView.on('mediaStopped', function() { + var event = new OT.MediaStoppedEvent(_publisher); - /** - * The publish() method starts publishing an audio-video stream to the session. - * The audio-video stream is captured from a local microphone and webcam. Upon successful - * publishing, the Session objects on all connected clients dispatch the - * streamCreated event. - *

    - * - * - *

    You pass a Publisher object as the one parameter of the method. You can initialize a - * Publisher object by calling the OT.initPublisher() - * method. Before calling Session.publish(). - *

    - * - *

    This method takes an alternate form: publish([targetElement:String, - * properties:Object]):Publisher — In this form, you do not pass a Publisher - * object into the function. Instead, you pass in a targetElement (the ID of the - * DOM element that the Publisher will replace) and a properties object that - * defines options for the Publisher (see OT.initPublisher().) - * The method returns a new Publisher object, which starts sending an audio-video stream to the - * session. The remainder of this documentation describes the form that takes a single Publisher - * object as a parameter. - * - *

    - * A local display of the published stream is created on the web page by replacing - * the specified element in the DOM with a streaming video display. The video stream - * is automatically mirrored horizontally so that users see themselves and movement - * in their stream in a natural way. If the width and height of the display do not match - * the 4:3 aspect ratio of the video signal, the video stream is cropped to fit the - * display. - *

    - * - *

    - * If calling this method creates a new Publisher object and the OpenTok library does not - * have access to the camera or microphone, the web page alerts the user to grant access - * to the camera and microphone. - *

    - * - *

    - * The OT object dispatches an exception event if the user's role does not - * include permissions required to publish. For example, if the user's role is set to subscriber, - * then they cannot publish. You define a user's role when you create the user token using the - * generate_token() method of the - * OpenTok server-side - * libraries or the Dashboard page. - * You pass the token string as a parameter of the connect() method of the Session - * object. See ExceptionEvent and - * OT.on(). - *

    - *

    - * The application throws an error if the session is not connected. - *

    - * - *
    Events dispatched:
    - *

    - * exception (ExceptionEvent) — Dispatched - * by the OT object. This can occur when user's role does not allow publishing (the - * code property of event object is set to 1500); it can also occur if the c - * onnection fails to connect (the code property of event object is set to 1013). - * WebRTC is a peer-to-peer protocol, and it is possible that connections will fail to connect. - * The most common cause for failure is a firewall that the protocol cannot traverse. - *

    - *

    - * streamCreated (StreamEvent) — - * The stream has been published. The Session object dispatches this on all clients - * subscribed to the stream, as well as on the publisher's client. - *

    - * - *
    Example
    - * - *

    - * The following example publishes a video once the session connects: - *

    - *
    -  * var sessionId = ""; // Replace with your own session ID.
    -  *                     // See https://dashboard.tokbox.com/projects
    -  * var token = ""; // Replace with a generated token that has been assigned the moderator role.
    -  *                 // See https://dashboard.tokbox.com/projects
    -  * var session = OT.initSession(apiKey, sessionID);
    -  * session.on("sessionConnected", function (event) {
    -  *     var publisherOptions = {width: 400, height:300, name:"Bob's stream"};
    -  *     // This assumes that there is a DOM element with the ID 'publisher':
    -  *     publisher = OT.initPublisher('publisher', publisherOptions);
    -  *     session.publish(publisher);
    -  * });
    -  * session.connect(token);
    -  * 
    - * - * @param {Publisher} publisher A Publisher object, which you initialize by calling the - * OT.initPublisher() method. - * - * @param {Function} completionHandler (Optional) A function to be called when the call to the - * publish() method succeeds or fails. This function takes one parameter — - * error. On success, the completionHandler function is not passed any - * arguments. On error, the function is passed an error object parameter - * (see the Error object). The - * error object has two properties: code (an integer) and - * message (a string), which identify the cause of the failure. Calling - * publish() fails if the role assigned to your token is not "publisher" or - * "moderator"; in this case error.code is set to 1500. Calling - * publish() also fails the client fails to connect; in this case - * error.code is set to 1013. The following code adds a - * completionHandler when calling the publish() method: - *
    -  * session.publish(publisher, null, function (error) {
    -  *   if (error) {
    -  *     console.log(error.message);
    -  *   } else {
    -  *     console.log("Publishing a stream.");
    -  *   }
    -  * });
    -  * 
    - * - * @returns The Publisher object for this stream. - * - * @method #publish - * @memberOf Session - */ - this.publish = function(publisher, properties, completionHandler) { - if(typeof publisher === 'function') { - completionHandler = publisher; - publisher = undefined; - } - if(typeof properties === 'function') { - completionHandler = properties; - properties = undefined; - } - if (this.isNot('connected')) { - /*jshint camelcase:false*/ - _analytics.logError(1010, 'OT.exception', - 'We need to be connected before you can publish', null, { - action: 'publish', - variation: 'Failure', - payload_type: 'reason', - payload: 'We need to be connected before you can publish', - session_id: _sessionId, - partner_id: _apiKey, - widgetId: _widgetId, - widget_type: 'Controller' + _publisher.dispatchEvent(event, function() { + if(!event.isDefaultPrevented()) { + if (_session) { + _publisher._.unpublishFromSession(_session, 'mediaStopped'); + } else { + _publisher.destroy('mediaStopped'); + } + } }); + }); - if (completionHandler && OT.$.isFunction(completionHandler)) { - dispatchError(OT.ExceptionCodes.NOT_CONNECTED, - 'We need to be connected before you can publish', completionHandler); - } + OT.$.waterfall([ + function(cb) { + if (_isScreenSharing) { + OT.checkScreenSharingCapability(function(response) { + if (!response.supported) { + onScreenSharingError( + new Error('Screen Sharing is not supported in this browser') + ); + } else if (response.extensionRegistered === false) { + onScreenSharingError( + new Error('Screen Sharing suppor in this browser requires an extension, but ' + + 'one has not been registered.') + ); + } else if (response.extensionInstalled === false) { + onScreenSharingError( + new Error('Screen Sharing suppor in this browser requires an extension, but ' + + 'the extension is not installed.') + ); + } else { - return null; - } + var helper = OT.pickScreenSharingHelper(); - if (!permittedTo('publish')) { - this.logEvent('publish', 'Failure', 'reason', - 'This token does not allow publishing. The role must be at least `publisher` ' + - 'to enable this functionality'); - dispatchError(OT.ExceptionCodes.UNABLE_TO_PUBLISH, - 'This token does not allow publishing. The role must be at least `publisher` ' + - 'to enable this functionality', completionHandler); - return null; - } + if (helper.proto.getConstraintsShowsPermissionUI) { + onAccessDialogOpened(); + } - // If the user has passed in an ID of a element then we create a new publisher. - if (!publisher || typeof(publisher)==='string' || OT.$.isElementNode(publisher)) { - // Initiate a new Publisher with the new session credentials - publisher = OT.initPublisher(publisher, properties); - - } else if (publisher instanceof OT.Publisher){ - - // If the publisher already has a session attached to it we can - if ('session' in publisher && publisher.session && 'sessionId' in publisher.session) { - // send a warning message that we can't publish again. - if( publisher.session.sessionId === this.sessionId){ - OT.warn('Cannot publish ' + publisher.guid() + ' again to ' + - this.sessionId + '. Please call session.unpublish(publisher) first.'); + helper.instance.getConstraints(options.videoSource, _properties.constraints, + function(err, constraints) { + if (helper.proto.getConstraintsShowsPermissionUI) { + onAccessDialogClosed(); + } + if (err) { + if (err.message === 'PermissionDeniedError') { + onAccessDenied(err); + } else { + onScreenSharingError(err); + } + } else { + _properties.constraints = constraints; + cb(); + } + }); + } + }); } else { - OT.warn('Cannot publish ' + publisher.guid() + ' publisher already attached to ' + - publisher.session.sessionId+ '. Please call session.unpublish(publisher) first.'); + OT.$.shouldAskForDevices(function(devices) { + if(!devices.video) { + OT.warn('Setting video constraint to false, there are no video sources'); + _properties.constraints.video = false; + } + if(!devices.audio) { + OT.warn('Setting audio constraint to false, there are no audio sources'); + _properties.constraints.audio = false; + } + cb(); + }); } - } + }, - } else { - dispatchError(OT.ExceptionCodes.UNABLE_TO_PUBLISH, - 'Session.publish :: First parameter passed in is neither a ' + - 'string nor an instance of the Publisher', - completionHandler); - return; - } + function() { - publisher.once('publishComplete', function(err) { - if (err) { - dispatchError(OT.ExceptionCodes.UNABLE_TO_PUBLISH, - 'Session.publish :: ' + err.message, - completionHandler); - return; - } + if (_state.isDestroyed()) { + return; + } - if (completionHandler && OT.$.isFunction(completionHandler)) { - completionHandler.apply(null, arguments); + OT.$.getUserMedia( + _properties.constraints, + onStreamAvailable, + onStreamAvailableError, + onAccessDialogOpened, + onAccessDialogClosed, + onAccessDenied + ); } - }); - // Add publisher reference to the session - publisher._.publishToSession(this); + ]); - // return the embed publisher - return publisher; - }; + }, this); - /** - * Ceases publishing the specified publisher's audio-video stream - * to the session. By default, the local representation of the audio-video stream is - * removed from the web page. Upon successful termination, the Session object on every - * connected web page dispatches - * a streamDestroyed event. - *

    - * - *

    - * To prevent the Publisher from being removed from the DOM, add an event listener for the - * streamDestroyed event dispatched by the Publisher object and call the - * preventDefault() method of the event object. - *

    - * - *

    - * Note: If you intend to reuse a Publisher object created using - * OT.initPublisher() to publish to different sessions sequentially, call - * either Session.disconnect() or Session.unpublish(). Do not call - * both. Then call the preventDefault() method of the streamDestroyed - * or sessionDisconnected event object to prevent the Publisher object from being - * removed from the page. Be sure to call preventDefault() only if the - * connection.connectionId property of the Stream object in the event matches the - * connection.connectionId property of your Session object (to ensure that you are - * preventing the default behavior for your published streams, not for other streams that you - * subscribe to). - *

    - * - *
    Events dispatched:
    - * - *

    - * streamDestroyed (StreamEvent) — - * The stream associated with the Publisher has been destroyed. Dispatched on by the - * Publisher on on the Publisher's browser. Dispatched by the Session object on - * all other connections subscribing to the publisher's stream. - *

    - * - *
    Example
    - * - * The following example publishes a stream to a session and adds a Disconnect link to the - * web page. Clicking this link causes the stream to stop being published. - * - *
    -  * <script>
    -  *     var apiKey = ""; // Replace with your API key. See https://dashboard.tokbox.com/projects
    -  *     var sessionID = ""; // Replace with your own session ID.
    -  *                      // See https://dashboard.tokbox.com/projects
    -  *     var token = "Replace with the TokBox token string provided to you."
    -  *     var session = OT.initSession(apiKey, sessionID);
    -  *     session.on("sessionConnected", function sessionConnectHandler(event) {
    -  *         // This assumes that there is a DOM element with the ID 'publisher':
    -  *         publisher = OT.initPublisher('publisher');
    -  *         session.publish(publisher);
    -  *     });
    -  *     session.connect(token);
    -  *     var publisher;
    -  *
    -  *     function unpublish() {
    -  *         session.unpublish(publisher);
    -  *     }
    -  * </script>
    -  *
    -  * <body>
    -  *
    -  *     <div id="publisherContainer/>
    -  *     <br/>
    -  *
    -  *     <a href="javascript:unpublish()">Stop Publishing</a>
    -  *
    -  * </body>
    -  *
    -  * 
    - * - * @see publish() - * - * @see streamDestroyed event - * - * @param {Publisher} publisher The Publisher object to stop streaming. - * - * @method #unpublish - * @memberOf Session - */ - this.unpublish = function(publisher) { - if (!publisher) { - OT.error('OT.Session.unpublish: publisher parameter missing.'); - return; + return this; + }; + +/** +* Starts publishing audio (if it is currently not being published) +* when the value is true; stops publishing audio +* (if it is currently being published) when the value is false. +* +* @param {Boolean} value Whether to start publishing audio (true) +* or not (false). +* +* @see OT.initPublisher() +* @see Stream.hasAudio +* @see StreamPropertyChangedEvent +* @method #publishAudio +* @memberOf Publisher +*/ + this.publishAudio = function(value) { + _properties.publishAudio = value; + + if (_microphone) { + _microphone.muted(!value); + } + + if (_chrome) { + _chrome.muteButton.muted(!value); + } + + if (_session && _stream) { + _stream.setChannelActiveState('audio', value); + } + + return this; + }; + + +/** +* Starts publishing video (if it is currently not being published) +* when the value is true; stops publishing video +* (if it is currently being published) when the value is false. +* +* @param {Boolean} value Whether to start publishing video (true) +* or not (false). +* +* @see OT.initPublisher() +* @see Stream.hasVideo +* @see StreamPropertyChangedEvent +* @method #publishVideo +* @memberOf Publisher +*/ + this.publishVideo = function(value) { + var oldValue = _properties.publishVideo; + _properties.publishVideo = value; + + if (_session && _stream && _properties.publishVideo !== oldValue) { + _stream.setChannelActiveState('video', value); + } + + // We currently do this event if the value of publishVideo has not changed + // This is because the state of the video tracks enabled flag may not match + // the value of publishVideo at this point. This will be tidied up shortly. + if (_webRTCStream) { + var videoTracks = _webRTCStream.getVideoTracks(); + for (var i=0, num=videoTracks.length; istreams property of the sessionConnected - * and streamCreated events (see - * SessionConnectEvent and - * StreamEvent). - *

    - *

    - * The subscribed stream is displayed on the local web page by replacing the specified element - * in the DOM with a streaming video display. If the width and height of the display do not - * match the 4:3 aspect ratio of the video signal, the video stream is cropped to fit - * the display. If the stream lacks a video component, a blank screen with an audio indicator - * is displayed in place of the video stream. - *

    - * + /** + * Deletes the Publisher object and removes it from the HTML DOM. *

    - * The application throws an error if the session is not connected or if the - * targetElement does not exist in the HTML DOM. + * The Publisher object dispatches a destroyed event when the DOM + * element is removed. *

    - * - *
    Example
    - * - * The following code subscribes to other clients' streams: - * - *
    -  * var apiKey = ""; // Replace with your API key. See https://dashboard.tokbox.com/projects
    -  * var sessionID = ""; // Replace with your own session ID.
    -  *                     // See https://dashboard.tokbox.com/projects
    -  *
    -  * var session = OT.initSession(apiKey, sessionID);
    -  * session.on("streamCreated", function(event) {
    -  *   subscriber = session.subscribe(event.stream, targetElement);
    -  * });
    -  * session.connect(token);
    -  * 
    - * - * @param {Stream} stream The Stream object representing the stream to which we are trying to - * subscribe. - * - * @param {Object} targetElement (Optional) The DOM element or the id attribute of - * the existing DOM element used to determine the location of the Subscriber video in the HTML - * DOM. See the insertMode property of the properties parameter. If - * you do not specify a targetElement, the application appends a new DOM element - * to the HTML body. - * - * @param {Object} properties This is an object that contains the following properties: - *
      - *
    • audioVolume (Number) — The desired audio volume, between 0 and - * 100, when the Subscriber is first opened (default: 50). After you subscribe to the - * stream, you can adjust the volume by calling the - * setAudioVolume() method of the - * Subscriber object. This volume setting affects local playback only; it does not affect - * the stream's volume on other clients.
    • - * - *
    • height (Number) — The desired height, in pixels, of the - * displayed Subscriber video stream (default: 198). Note: Use the - * height and width properties to set the dimensions - * of the Subscriber video; do not set the height and width of the DOM element - * (using CSS).
    • - * - *
    • - * insertMode (String) — Specifies how the Subscriber object will - * be inserted in the HTML DOM. See the targetElement parameter. This - * string can have the following values: - *
        - *
      • "replace" — The Subscriber object replaces contents of the - * targetElement. This is the default.
      • - *
      • "after" — The Subscriber object is a new element inserted - * after the targetElement in the HTML DOM. (Both the Subscriber and targetElement - * have the same parent element.)
      • - *
      • "before" — The Subscriber object is a new element inserted - * before the targetElement in the HTML DOM. (Both the Subsciber and targetElement - * have the same parent element.)
      • - *
      • "append" — The Subscriber object is a new element added as a - * child of the targetElement. If there are other child elements, the Subscriber is - * appended as the last child element of the targetElement.
      • - *
      - *
    • - * - *
    • - * style (Object) — An object containing properties that define the initial - * appearance of user interface controls of the Subscriber. The style object - * includes the following properties: - *
        - *
      • audioLevelDisplayMode (String) — How to display the audio level - * indicator. Possible values are: "auto" (the indicator is displayed when the - * video is disabled), "off" (the indicator is not displayed), and - * "on" (the indicator is always displayed).
      • - * - *
      • backgroundImageURI (String) — A URI for an image to display as - * the background image when a video is not displayed. (A video may not be displayed if - * you call subscribeToVideo(false) on the Subscriber object). You can pass an - * http or https URI to a PNG, JPEG, or non-animated GIF file location. You can also use the - * data URI scheme (instead of http or https) and pass in base-64-encrypted - * PNG data, such as that obtained from the - * Subscriber.getImgData() method. For example, - * you could set the property to "data:VBORw0KGgoAA...", where the portion of - * the string after "data:" is the result of a call to - * Subscriber.getImgData(). If the URL or the image data is invalid, the - * property is ignored (the attempt to set the image fails silently). - *

        - * Note that in Internet Explorer 8 (using the OpenTok Plugin for Internet Explorer), - * you cannot set the backgroundImageURI style to a string larger than - * 32 kB. This is due to an IE 8 limitation on the size of URI strings. Due to this - * limitation, you cannot set the backgroundImageURI style to a string obtained - * with the getImgData() method. - *

      • - * - *
      • buttonDisplayMode (String) — How to display the speaker controls - * Possible values are: "auto" (controls are displayed when the stream is first - * displayed and when the user mouses over the display), "off" (controls are not - * displayed), and "on" (controls are always displayed).
      • - * - *
      • nameDisplayMode (String) — Whether to display the stream name. - * Possible values are: "auto" (the name is displayed when the stream is first - * displayed and when the user mouses over the display), "off" (the name is not - * displayed), and "on" (the name is always displayed).
      • - * - *
      • videoDisabledDisplayMode (String) — Whether to display the video - * disabled indicator and video disabled warning icons for a Subscriber. These icons - * indicate that the video has been disabled (or is in risk of being disabled for - * the warning icon) due to poor stream quality. This style only applies to the Subscriber - * object. Possible values are: "auto" (the icons are automatically when the - * displayed video is disabled or in risk of being disabled due to poor stream quality), - * "off" (do not display the icons), and "on" (display the - * icons). The default setting is "auto"
      • - *
      - *
    • - * - *
    • subscribeToAudio (Boolean) — Whether to initially subscribe to audio - * (if available) for the stream (default: true).
    • - * - *
    • subscribeToVideo (Boolean) — Whether to initially subscribe to video - * (if available) for the stream (default: true).
    • - * - *
    • width (Number) — The desired width, in pixels, of the - * displayed Subscriber video stream (default: 264). Note: Use the - * height and width properties to set the dimensions - * of the Subscriber video; do not set the height and width of the DOM element - * (using CSS).
    • - * - *
    - * - * @param {Function} completionHandler (Optional) A function to be called when the call to the - * subscribe() method succeeds or fails. This function takes one parameter — - * error. On success, the completionHandler function is not passed any - * arguments. On error, the function is passed an error object, defined by the - * Error class, has two properties: code (an integer) and - * message (a string), which identify the cause of the failure. The following - * code adds a completionHandler when calling the subscribe() method: - *
    -  * session.subscribe(stream, "subscriber", null, function (error) {
    -  *   if (error) {
    -  *     console.log(error.message);
    -  *   } else {
    -  *     console.log("Subscribed to stream: " + stream.id);
    -  *   }
    -  * });
    -  * 
    - * - * @signature subscribe(stream, targetElement, properties, completionHandler) - * @returns {Subscriber} The Subscriber object for this stream. Stream control functions - * are exposed through the Subscriber object. - * @method #subscribe - * @memberOf Session + * @method #destroy + * @memberOf Publisher + * @return {Publisher} The Publisher. */ - this.subscribe = function(stream, targetElement, properties, completionHandler) { + this.destroy = function(/* unused */ reason, quiet) { + if (_state.isDestroyed()) return; + _state.set('Destroyed'); - if (!this.connection || !this.connection.connectionId) { - dispatchError(OT.ExceptionCodes.UNABLE_TO_SUBSCRIBE, - 'Session.subscribe :: Connection required to subscribe', - completionHandler); - return; - } + reset(); - if (!stream) { - dispatchError(OT.ExceptionCodes.UNABLE_TO_SUBSCRIBE, - 'Session.subscribe :: stream cannot be null', - completionHandler); - return; - } + if (quiet !== true) { + this.dispatchEvent( + new OT.DestroyedEvent( + OT.Event.names.PUBLISHER_DESTROYED, + this, + reason + ), + OT.$.bind(this.off, this) + ); + } - if (!stream.hasOwnProperty('streamId')) { - dispatchError(OT.ExceptionCodes.UNABLE_TO_SUBSCRIBE, - 'Session.subscribe :: invalid stream object', - completionHandler); - return; - } + return this; + }; - if(typeof targetElement === 'function') { - completionHandler = targetElement; - targetElement = undefined; - } + /** + * @methodOf Publisher + * @private + */ + this.disconnect = function() { + // Close the connection to each of our subscribers + for (var fromConnectionId in _peerConnections) { + this.cleanupSubscriber(fromConnectionId); + } + }; - if(typeof properties === 'function') { - completionHandler = properties; - properties = undefined; - } + this.cleanupSubscriber = function(fromConnectionId) { + var pc = _peerConnections[fromConnectionId]; - var subscriber = new OT.Subscriber(targetElement, OT.$.extend(properties || {}, { - session: this - })); + if (pc) { + pc.destroy(); + delete _peerConnections[fromConnectionId]; - subscriber.once('subscribeComplete', function(err) { - if (err) { - dispatchError(OT.ExceptionCodes.UNABLE_TO_SUBSCRIBE, - 'Session.subscribe :: ' + err.message, - completionHandler); + logAnalyticsEvent('disconnect', 'PeerConnection', + {subscriberConnection: fromConnectionId}); + } + }; - return; - } - if (completionHandler && OT.$.isFunction(completionHandler)) { - completionHandler.apply(null, arguments); - } - }); + this.processMessage = function(type, fromConnection, message) { + OT.debug('OT.Publisher.processMessage: Received ' + type + ' from ' + fromConnection.id); + OT.debug(message); - OT.subscribers.add(subscriber); - subscriber.subscribe(stream); + switch (type) { + case 'unsubscribe': + this.cleanupSubscriber(message.content.connection.id); + break; - return subscriber; - }; + default: + var peerConnection = createPeerConnectionForRemote(fromConnection); + peerConnection.processMessage(type, message); + } + }; - /** - * Stops subscribing to a stream in the session. the display of the audio-video stream is - * removed from the local web page. + /** + * Returns the base-64-encoded string of PNG data representing the Publisher video. + * + *

    You can use the string as the value for a data URL scheme passed to the src parameter of + * an image file, as in the following:

    * - *
    Example
    - *

    - * The following code subscribes to other clients' streams. For each stream, the code also - * adds an Unsubscribe link. - *

    *
    -  * var apiKey = ""; // Replace with your API key. See https://dashboard.tokbox.com/projects
    -  * var sessionID = ""; // Replace with your own session ID.
    -  *                     // See https://dashboard.tokbox.com/projects
    -  * var streams = [];
    -  *
    -  * var session = OT.initSession(apiKey, sessionID);
    -  * session.on("streamCreated", function(event) {
    -  *     var stream = event.stream;
    -  *     displayStream(stream);
    -  * });
    -  * session.connect(token);
    +  *  var imgData = publisher.getImgData();
       *
    -  * function displayStream(stream) {
    -  *     var div = document.createElement('div');
    -  *     div.setAttribute('id', 'stream' + stream.streamId);
    -  *
    -  *     var subscriber = session.subscribe(stream, div);
    -  *     subscribers.push(subscriber);
    -  *
    -  *     var aLink = document.createElement('a');
    -  *     aLink.setAttribute('href', 'javascript: unsubscribe("' + subscriber.id + '")');
    -  *     aLink.innerHTML = "Unsubscribe";
    -  *
    -  *     var streamsContainer = document.getElementById('streamsContainer');
    -  *     streamsContainer.appendChild(div);
    -  *     streamsContainer.appendChild(aLink);
    -  *
    -  *     streams = event.streams;
    -  * }
    -  *
    -  * function unsubscribe(subscriberId) {
    -  *     console.log("unsubscribe called");
    -  *     for (var i = 0; i < subscribers.length; i++) {
    -  *         var subscriber = subscribers[i];
    -  *         if (subscriber.id == subscriberId) {
    -  *             session.unsubscribe(subscriber);
    -  *         }
    -  *     }
    -  * }
    +  *  var img = document.createElement("img");
    +  *  img.setAttribute("src", "data:image/png;base64," + imgData);
    +  *  var imgWin = window.open("about:blank", "Screenshot");
    +  *  imgWin.document.write("<body></body>");
    +  *  imgWin.document.body.appendChild(img);
       * 
    * - * @param {Subscriber} subscriber The Subscriber object to unsubcribe. - * - * @see subscribe() - * - * @method #unsubscribe - * @memberOf Session + * @method #getImgData + * @memberOf Publisher + * @return {String} The base-64 encoded string. Returns an empty string if there is no video. */ - this.unsubscribe = function(subscriber) { - if (!subscriber) { - var errorMsg = 'OT.Session.unsubscribe: subscriber cannot be null'; - OT.error(errorMsg); - throw new Error(errorMsg); - } - if (!subscriber.stream) { - OT.warn('OT.Session.unsubscribe:: tried to unsubscribe a subscriber that had no stream'); - return false; - } + this.getImgData = function() { + if (!_loaded) { + OT.error('OT.Publisher.getImgData: Cannot getImgData before the Publisher is publishing.'); - OT.debug('OT.Session.unsubscribe: subscriber ' + subscriber.id); + return null; + } - subscriber.destroy(); + return _targetElement.imgData(); + }; - return true; - }; - /** - * Returns an array of local Subscriber objects for a given stream. - * - * @param {Stream} stream The stream for which you want to find subscribers. - * - * @returns {Array} An array of {@link Subscriber} objects for the specified stream. - * - * @see unsubscribe() - * @see Subscriber - * @see StreamEvent - * @method #getSubscribersForStream - * @memberOf Session - */ - this.getSubscribersForStream = function(stream) { - return OT.subscribers.where({streamId: stream.id}); - }; + // API Compatibility layer for Flash Publisher, this could do with some tidyup. + this._ = { + publishToSession: OT.$.bind(function(session) { + // Add session property to Publisher + this.session = _session = session; - /** - * Returns the local Publisher object for a given stream. - * - * @param {Stream} stream The stream for which you want to find the Publisher. - * - * @returns {Publisher} A Publisher object for the specified stream. Returns - * null if there is no local Publisher object - * for the specified stream. - * - * @see forceUnpublish() - * @see Subscriber - * @see StreamEvent - * - * @method #getPublisherForStream - * @memberOf Session - */ - this.getPublisherForStream = function(stream) { - var streamId, - errorMsg; - - if (typeof stream === 'string') { - streamId = stream; - } else if (typeof stream === 'object' && stream && stream.hasOwnProperty('id')) { - streamId = stream.id; - } else { - errorMsg = 'Session.getPublisherForStream :: Invalid stream type'; - OT.error(errorMsg); - throw new Error(errorMsg); - } + var createStream = function() { - return OT.publishers.where({streamId: streamId})[0]; - }; + var streamWidth, + streamHeight; - // Private Session API: for internal OT use only - this._ = { - jsepCandidateP2p: function(streamId, subscriberId, candidate) { - return _socket.jsepCandidateP2p(streamId, subscriberId, candidate); - }, + // Bail if this.session is gone, it means we were unpublished + // before createStream could finish. + if (!_session) return; - jsepCandidate: function(streamId, candidate) { - return _socket.jsepCandidate(streamId, candidate); - }, + _state.set('PublishingToSession'); - jsepOffer: function(streamId, offerSdp) { - return _socket.jsepOffer(streamId, offerSdp); - }, + var onStreamRegistered = OT.$.bind(function(err, streamId, message) { + if (err) { + // @todo we should respect err.code here and translate it to the local + // client equivalent. + var errorCode = OT.ExceptionCodes.UNABLE_TO_PUBLISH; + var payload = { + reason: 'Publish', + code: errorCode, + message: err.message + }; + logConnectivityEvent('Failure', payload); + if (_state.isAttemptingToPublish()) { + this.trigger('publishComplete', new OT.Error(errorCode, err.message)); + } + return; + } - jsepOfferP2p: function(streamId, subscriberId, offerSdp) { - return _socket.jsepOfferP2p(streamId, subscriberId, offerSdp); - }, + this.streamId = _streamId = streamId; + _iceServers = OT.Raptor.parseIceServers(message); + }, this); - jsepAnswer: function(streamId, answerSdp) { - return _socket.jsepAnswer(streamId, answerSdp); - }, + // We set the streamWidth and streamHeight to be the minimum of the requested + // resolution and the actual resolution. + if (_properties.videoDimensions) { + streamWidth = Math.min(_properties.videoDimensions.width, + _targetElement.videoWidth() || 640); + streamHeight = Math.min(_properties.videoDimensions.height, + _targetElement.videoHeight() || 480); + } else { + streamWidth = _targetElement.videoWidth() || 640; + streamHeight = _targetElement.videoHeight() || 480; + } - jsepAnswerP2p: function(streamId, subscriberId, answerSdp) { - return _socket.jsepAnswerP2p(streamId, subscriberId, answerSdp); - }, + var streamChannels = []; - // session.on("signal", function(SignalEvent)) - // session.on("signal:{type}", function(SignalEvent)) - dispatchSignal: OT.$.bind(function(fromConnection, type, data) { - var event = new OT.SignalEvent(type, data, fromConnection); - event.target = this; - - // signal a "signal" event - // NOTE: trigger doesn't support defaultAction, and therefore preventDefault. - this.trigger(OT.Event.names.SIGNAL, event); + if (!(_properties.videoSource === null || _properties.videoSource === false)) { + streamChannels.push(new OT.StreamChannel({ + id: 'video1', + type: 'video', + active: _properties.publishVideo, + orientation: OT.VideoOrientation.ROTATED_NORMAL, + frameRate: _properties.frameRate, + width: streamWidth, + height: streamHeight, + source: _isScreenSharing ? 'screen' : 'camera', + fitMode: _properties.fitMode + })); + } - // signal an "signal:{type}" event" if there was a custom type - if (type) this.dispatchEvent(event); - }, this), + if (!(_properties.audioSource === null || _properties.audioSource === false)) { + streamChannels.push(new OT.StreamChannel({ + id: 'audio1', + type: 'audio', + active: _properties.publishAudio + })); + } - subscriberCreate: function(stream, subscriber, channelsToSubscribeTo, completion) { - return _socket.subscriberCreate(stream.id, subscriber.widgetId, - channelsToSubscribeTo, completion); - }, + session._.streamCreate(_properties.name || '', _properties.audioFallbackEnabled, + streamChannels, onStreamRegistered); - subscriberDestroy: function(stream, subscriber) { - return _socket.subscriberDestroy(stream.id, subscriber.widgetId); - }, + }; - subscriberUpdate: function(stream, subscriber, attributes) { - return _socket.subscriberUpdate(stream.id, subscriber.widgetId, attributes); - }, + if (_loaded) createStream.call(this); + else this.on('initSuccess', createStream, this); - subscriberChannelUpdate: function(stream, subscriber, channel, attributes) { - return _socket.subscriberChannelUpdate(stream.id, subscriber.widgetId, channel.id, - attributes); - }, + logConnectivityEvent('Attempt', {streamType: 'WebRTC'}); - streamCreate: OT.$.bind(function(name, orientation, encodedWidth, encodedHeight, - hasAudio, hasVideo, - frameRate, completion) { - - _socket.streamCreate( - name, - orientation, - encodedWidth, - encodedHeight, - hasAudio, - hasVideo, - frameRate, - OT.Config.get('bitrates', 'min', OT.APIKEY), - OT.Config.get('bitrates', 'max', OT.APIKEY), - completion - ); - }, this), + return this; + }, this), - streamDestroy: function(streamId) { - _socket.streamDestroy(streamId); - }, + unpublishFromSession: OT.$.bind(function(session, reason) { + if (!_session || session.id !== _session.id) { + OT.warn('The publisher ' + _guid + ' is trying to unpublish from a session ' + + session.id + ' it is not attached to (it is attached to ' + + (_session && _session.id || 'no session') + ')'); + return this; + } - streamChannelUpdate: function(stream, channel, attributes) { - _socket.streamChannelUpdate(stream.id, channel.id, attributes); + if (session.isConnected() && this.stream) { + session._.streamDestroy(this.stream.id); } - }; + // Disconnect immediately, rather than wait for the WebSocket to + // reply to our destroyStream message. + this.disconnect(); + this.session = _session = null; - /** - * Sends a signal to each client or a specified client in the session. Specify a - * to property of the signal parameter to limit the signal to - * be sent to a specific client; otherwise the signal is sent to each client connected to - * the session. - *

    - * The following example sends a signal of type "foo" with a specified data payload ("hello") - * to all clients connected to the session: - *

    -  * session.signal({
    -  *     type: "foo",
    -  *     data: "hello"
    -  *   },
    -  *   function(error) {
    -  *     if (error) {
    -  *       console.log("signal error: " + error.message);
    -  *     } else {
    -  *       console.log("signal sent");
    -  *     }
    -  *   }
    -  * );
    -  * 
    - *

    - * Calling this method without specifying a recipient client (by setting the to - * property of the signal parameter) results in multiple signals sent (one to each - * client in the session). For information on charges for signaling, see the - * OpenTok pricing page. - *

    - * The following example sends a signal of type "foo" with a data payload ("hello") to a - * specific client connected to the session: - *

    -  * session.signal({
    -  *     type: "foo",
    -  *     to: recipientConnection; // a Connection object
    -  *     data: "hello"
    -  *   },
    -  *   function(error) {
    -  *     if (error) {
    -  *       console.log("signal error: " + error.message);
    -  *     } else {
    -  *       console.log("signal sent");
    -  *     }
    -  *   }
    -  * );
    -  * 
    - *

    - * Add an event handler for the signal event to listen for all signals sent in - * the session. Add an event handler for the signal:type event to listen for - * signals of a specified type only (replace type, in signal:type, - * with the type of signal to listen for). The Session object dispatches these events. (See - * events.) - * - * @param {Object} signal An object that contains the following properties defining the signal: - *

      - *
    • data — (String) The data to send. The limit to the length of data - * string is 8kB. Do not set the data string to null or - * undefined.
    • - *
    • to — (Connection) A Connection - * object corresponding to the client that the message is to be sent to. If you do not - * specify this property, the signal is sent to all clients connected to the session.
    • - *
    • type — (String) The type of the signal. You can use the type to - * filter signals when setting an event handler for the signal:type event - * (where you replace type with the type string). The maximum length of the - * type string is 128 characters, and it must contain only letters (A-Z and a-z), - * numbers (0-9), '-', '_', and '~'.
    • - * - *
    - * - *

    Each property is optional. If you set none of the properties, you will send a signal - * with no data or type to each client connected to the session.

    - * - * @param {Function} completionHandler A function that is called when sending the signal - * succeeds or fails. This function takes one parameter — error. - * On success, the completionHandler function is not passed any - * arguments. On error, the function is passed an error object, defined by the - * Error class. The error object has the following - * properties: - * - *
      - *
    • code — (Number) An error code, which can be one of the following: - * - * - * - * - * - * - * - * - * - * - * - * - * - *
      400 One of the signal properties — data, type, or to — - * is invalid.
      404 The client specified by the to property is not connected to - * the session.
      413 The type string exceeds the maximum length (128 bytes), - * or the data string exceeds the maximum size (8 kB).
      500 You are not connected to the OpenTok session.
      - *
    • - *
    • message — (String) A description of the error.
    • - *
    - * - *

    Note that the completionHandler success result (error == null) - * indicates that the options passed into the Session.signal() method are valid - * and the signal was sent. It does not indicate that the signal was successfully - * received by any of the intended recipients. - * - * @method #signal - * @memberOf Session - * @see signal and signal:type events - */ - this.signal = function(options, completion) { - var _options = options, - _completion = completion; - - if (OT.$.isFunction(_options)) { - _completion = _options; - _options = null; - } + // We're back to being a stand-alone publisher again. + if (!_state.isDestroyed()) _state.set('MediaBound'); - if (this.isNot('connected')) { - var notConnectedErrorMsg = 'Unable to send signal - you are not connected to the session.'; - dispatchError(500, notConnectedErrorMsg, _completion); - return; + if(_connectivityAttemptPinger) { + _connectivityAttemptPinger.stop(); } + logAnalyticsEvent('unpublish', 'Success', {sessionId: session.id}); - _socket.signal(_options, _completion); - if (options && options.data && (typeof(options.data) !== 'string')) { - OT.warn('Signaling of anything other than Strings is deprecated. ' + - 'Please update the data property to be a string.'); + this._.streamDestroyed(reason); + + return this; + }, this), + + streamDestroyed: OT.$.bind(function(reason) { + if(OT.$.arrayIndexOf(['reset'], reason) < 0) { + var event = new OT.StreamEvent('streamDestroyed', _stream, reason, true); + var defaultAction = OT.$.bind(function() { + if(!event.isDefaultPrevented()) { + this.destroy(); + } + }, this); + this.dispatchEvent(event, defaultAction); } - this.logEvent('signal', 'send', 'type', - (options && options.data) ? typeof(options.data) : 'null'); - }; + }, this), - /** - * Forces a remote connection to leave the session. - * - *

    - * The forceDisconnect() method is normally used as a moderation tool - * to remove users from an ongoing session. - *

    - *

    - * When a connection is terminated using the forceDisconnect(), - * sessionDisconnected, connectionDestroyed and - * streamDestroyed events are dispatched in the same way as they - * would be if the connection had terminated itself using the disconnect() - * method. However, the reason property of a {@link ConnectionEvent} or - * {@link StreamEvent} object specifies "forceDisconnected" as the reason - * for the destruction of the connection and stream(s). - *

    - *

    - * While you can use the forceDisconnect() method to terminate your own connection, - * calling the disconnect() method is simpler. - *

    - *

    - * The OT object dispatches an exception event if the user's role - * does not include permissions required to force other users to disconnect. - * You define a user's role when you create the user token using the - * generate_token() method using - * OpenTok - * server-side libraries or the - * Dashboard page. - * See ExceptionEvent and OT.on(). - *

    - *

    - * The application throws an error if the session is not connected. - *

    - * - *
    Events dispatched:
    - * - *

    - * connectionDestroyed (ConnectionEvent) — - * On clients other than which had the connection terminated. - *

    - *

    - * exception (ExceptionEvent) — - * The user's role does not allow forcing other user's to disconnect (event.code = - * 1530), - * or the specified stream is not publishing to the session (event.code = 1535). - *

    - *

    - * sessionDisconnected - * (SessionDisconnectEvent) — - * On the client which has the connection terminated. - *

    - *

    - * streamDestroyed (StreamEvent) — - * If streams are stopped as a result of the connection ending. - *

    - * - * @param {Connection} connection The connection to be disconnected from the session. - * This value can either be a Connection object or a connection - * ID (which can be obtained from the connectionId property of the Connection object). - * - * @param {Function} completionHandler (Optional) A function to be called when the call to the - * forceDiscononnect() method succeeds or fails. This function takes one parameter - * — error. On success, the completionHandler function is - * not passed any arguments. On error, the function is passed an error object - * parameter. The error object, defined by the Error - * class, has two properties: code (an integer) - * and message (a string), which identify the cause of the failure. - * Calling forceDisconnect() fails if the role assigned to your - * token is not "moderator"; in this case error.code is set to 1520. The following - * code adds a completionHandler when calling the forceDisconnect() - * method: - *
    -  * session.forceDisconnect(connection, function (error) {
    -  *   if (error) {
    -  *       console.log(error);
    -  *     } else {
    -  *       console.log("Connection forced to disconnect: " + connection.id);
    -  *     }
    -  *   });
    -  * 
    - * - * @method #forceDisconnect - * @memberOf Session - */ - this.forceDisconnect = function(connectionOrConnectionId, completionHandler) { - if (this.isNot('connected')) { - var notConnectedErrorMsg = 'Cannot call forceDisconnect(). You are not ' + - 'connected to the session.'; - dispatchError(OT.ExceptionCodes.NOT_CONNECTED, notConnectedErrorMsg, completionHandler); - return; + archivingStatus: OT.$.bind(function(status) { + if(_chrome) { + _chrome.archive.setArchiving(status); } - var notPermittedErrorMsg = 'This token does not allow forceDisconnect. ' + - 'The role must be at least `moderator` to enable this functionality'; + return status; + }, this), - if (permittedTo('forceDisconnect')) { - var connectionId = typeof connectionOrConnectionId === 'string' ? - connectionOrConnectionId : connectionOrConnectionId.id; + webRtcStream: function() { + return _webRTCStream; + } + }; - _socket.forceDisconnect(connectionId, function(err) { - if (err) { - dispatchError(OT.ExceptionCodes.UNABLE_TO_FORCE_DISCONNECT, - notPermittedErrorMsg, completionHandler); + this.detectDevices = function() { + OT.warn('Fixme: Haven\'t implemented detectDevices'); + }; - } else if (completionHandler && OT.$.isFunction(completionHandler)) { - completionHandler.apply(null, arguments); - } - }); - } else { - // if this throws an error the handleJsException won't occur - dispatchError(OT.ExceptionCodes.UNABLE_TO_FORCE_DISCONNECT, - notPermittedErrorMsg, completionHandler); - } - }; + this.detectMicActivity = function() { + OT.warn('Fixme: Haven\'t implemented detectMicActivity'); + }; - /** - * Forces the publisher of the specified stream to stop publishing the stream. - * - *

    - * Calling this method causes the Session object to dispatch a streamDestroyed - * event on all clients that are subscribed to the stream (including the client that is - * publishing the stream). The reason property of the StreamEvent object is - * set to "forceUnpublished". - *

    - *

    - * The OT object dispatches an exception event if the user's role - * does not include permissions required to force other users to unpublish. - * You define a user's role when you create the user token using the generate_token() - * method using the - * OpenTok - * server-side libraries or the Dashboard - * page. - * You pass the token string as a parameter of the connect() method of the Session - * object. See ExceptionEvent and - * OT.on(). - *

    - * - *
    Events dispatched:
    - * - *

    - * exception (ExceptionEvent) — - * The user's role does not allow forcing other users to unpublish. - *

    - *

    - * streamDestroyed (StreamEvent) — - * The stream has been unpublished. The Session object dispatches this on all clients - * subscribed to the stream, as well as on the publisher's client. - *

    - * - * @param {Stream} stream The stream to be unpublished. - * - * @param {Function} completionHandler (Optional) A function to be called when the call to the - * forceUnpublish() method succeeds or fails. This function takes one parameter - * — error. On success, the completionHandler function is - * not passed any arguments. On error, the function is passed an error object - * parameter. The error object, defined by the Error - * class, has two properties: code (an integer) - * and message (a string), which identify the cause of the failure. Calling - * forceUnpublish() fails if the role assigned to your token is not "moderator"; - * in this case error.code is set to 1530. The following code adds a - * completionHandler when calling the forceUnpublish() method: - *
    -  * session.forceUnpublish(stream, function (error) {
    -  *   if (error) {
    -  *       console.log(error);
    -  *     } else {
    -  *       console.log("Connection forced to disconnect: " + connection.id);
    -  *     }
    -  *   });
    -  * 
    - * - * @method #forceUnpublish - * @memberOf Session - */ - this.forceUnpublish = function(streamOrStreamId, completionHandler) { - if (this.isNot('connected')) { - var notConnectedErrorMsg = 'Cannot call forceUnpublish(). You are not ' + - 'connected to the session.'; - dispatchError(OT.ExceptionCodes.NOT_CONNECTED, notConnectedErrorMsg, completionHandler); - return; - } + this.getEchoCancellationMode = function() { + OT.warn('Fixme: Haven\'t implemented getEchoCancellationMode'); + return 'fullDuplex'; + }; - var notPermittedErrorMsg = 'This token does not allow forceUnpublish. ' + - 'The role must be at least `moderator` to enable this functionality'; + this.setMicrophoneGain = function() { + OT.warn('Fixme: Haven\'t implemented setMicrophoneGain'); + }; - if (permittedTo('forceUnpublish')) { - var stream = typeof streamOrStreamId === 'string' ? - this.streams.get(streamOrStreamId) : streamOrStreamId; + this.getMicrophoneGain = function() { + OT.warn('Fixme: Haven\'t implemented getMicrophoneGain'); + return 0.5; + }; - _socket.forceUnpublish(stream.id, function(err) { - if (err) { - dispatchError(OT.ExceptionCodes.UNABLE_TO_FORCE_UNPUBLISH, - notPermittedErrorMsg, completionHandler); - } else if (completionHandler && OT.$.isFunction(completionHandler)) { - completionHandler.apply(null, arguments); - } - }); - } else { - // if this throws an error the handleJsException won't occur - dispatchError(OT.ExceptionCodes.UNABLE_TO_FORCE_UNPUBLISH, - notPermittedErrorMsg, completionHandler); - } - }; + this.setCamera = function() { + OT.warn('Fixme: Haven\'t implemented setCamera'); + }; - this.getStateManager = function() { - OT.warn('Fixme: Have not implemented session.getStateManager'); - }; + this.setMicrophone = function() { + OT.warn('Fixme: Haven\'t implemented setMicrophone'); + }; - this.isConnected = function() { - return this.is('connected'); - }; - this.capabilities = new OT.Capabilities([]); + // Platform methods: - /** - * Dispatched when an archive recording of the session starts. - * - * @name archiveStarted - * @event - * @memberof Session - * @see ArchiveEvent - * @see Archiving overview. - */ + this.guid = function() { + return _guid; + }; - /** - * Dispatched when an archive recording of the session stops. - * - * @name archiveStopped - * @event - * @memberof Session - * @see ArchiveEvent - * @see Archiving overview. - */ + this.videoElement = function() { + return _targetElement.domElement(); + }; - /** - * A new client (including your own) has connected to the session. - * @name connectionCreated - * @event - * @memberof Session - * @see ConnectionEvent - * @see OT.initSession() - */ + this.setStream = assignStream; - /** - * A client, other than your own, has disconnected from the session. - * @name connectionDestroyed - * @event - * @memberof Session - * @see ConnectionEvent - */ + this.isWebRTC = true; - /** - * The page has connected to an OpenTok session. This event is dispatched asynchronously - * in response to a successful call to the connect() method of a Session - * object. Before calling the connect() method, initialize the session by - * calling the OT.initSession() method. For a code example and more details, - * see Session.connect(). - * @name sessionConnected - * @event - * @memberof Session - * @see SessionConnectEvent - * @see Session.connect() - * @see OT.initSession() - */ + this.isLoading = function() { + return _widgetView && _widgetView.loading(); + }; - /** - * The client has disconnected from the session. This event may be dispatched asynchronously - * in response to a successful call to the disconnect() method of the Session object. - * The event may also be disptached if a session connection is lost inadvertantly, as in the case - * of a lost network connection. - *

    - * The default behavior is that all Subscriber objects are unsubscribed and removed from the - * HTML DOM. Each Subscriber object dispatches a destroyed event when the element is - * removed from the HTML DOM. If you call the preventDefault() method in the event - * listener for the sessionDisconnect event, the default behavior is prevented, and - * you can, optionally, clean up Subscriber objects using your own code. -* - * @name sessionDisconnected - * @event - * @memberof Session - * @see Session.disconnect() - * @see Session.forceDisconnect() - * @see SessionDisconnectEvent - */ + this.videoWidth = function() { + return _targetElement.videoWidth(); + }; + + this.videoHeight = function() { + return _targetElement.videoHeight(); + }; + + // Make read-only: element, guid, _.webRtcStream + + this.on('styleValueChanged', updateChromeForStyleChange, this); + _state = new OT.PublishingState(stateChangeFailed); + + this.accessAllowed = false; + +/** +* Dispatched when the user has clicked the Allow button, granting the +* app access to the camera and microphone. The Publisher object has an +* accessAllowed property which indicates whether the user +* has granted access to the camera and microphone. +* @see Event +* @name accessAllowed +* @event +* @memberof Publisher +*/ + +/** +* Dispatched when the user has clicked the Deny button, preventing the +* app from having access to the camera and microphone. +* @see Event +* @name accessDenied +* @event +* @memberof Publisher +*/ + +/** +* Dispatched when the Allow/Deny dialog box is opened. (This is the dialog box in which +* the user can grant the app access to the camera and microphone.) +* @see Event +* @name accessDialogOpened +* @event +* @memberof Publisher +*/ + +/** +* Dispatched when the Allow/Deny box is closed. (This is the dialog box in which the +* user can grant the app access to the camera and microphone.) +* @see Event +* @name accessDialogClosed +* @event +* @memberof Publisher +*/ /** - * A new stream, published by another client, has been created on this session. For streams - * published by your own client, the Publisher object dispatches a streamCreated - * event. For a code example and more details, see {@link StreamEvent}. - * @name streamCreated - * @event - * @memberof Session - * @see StreamEvent - * @see Session.publish() - */ + * Dispatched periodically to indicate the publisher's audio level. The event is dispatched + * up to 60 times per second, depending on the browser. The audioLevel property + * of the event is audio level, from 0 to 1.0. See {@link AudioLevelUpdatedEvent} for more + * information. + *

    + * The following example adjusts the value of a meter element that shows volume of the + * publisher. Note that the audio level is adjusted logarithmically and a moving average + * is applied: + *

    + *

    +  * var movingAvg = null;
    +  * publisher.on('audioLevelUpdated', function(event) {
    +  *   if (movingAvg === null || movingAvg <= event.audioLevel) {
    +  *     movingAvg = event.audioLevel;
    +  *   } else {
    +  *     movingAvg = 0.7 * movingAvg + 0.3 * event.audioLevel;
    +  *   }
    +  *
    +  *   // 1.5 scaling to map the -30 - 0 dBm range to [0,1]
    +  *   var logLevel = (Math.log(movingAvg) / Math.LN10) / 1.5 + 1;
    +  *   logLevel = Math.min(Math.max(logLevel, 0), 1);
    +  *   document.getElementById('publisherMeter').value = logLevel;
    +  * });
    +  * 
    + *

    This example shows the algorithm used by the default audio level indicator displayed + * in an audio-only Publisher. + * + * @name audioLevelUpdated + * @event + * @memberof Publisher + * @see AudioLevelUpdatedEvent + */ - /** - * A stream from another client has stopped publishing to the session. - *

    - * The default behavior is that all Subscriber objects that are subscribed to the stream are - * unsubscribed and removed from the HTML DOM. Each Subscriber object dispatches a - * destroyed event when the element is removed from the HTML DOM. If you call the - * preventDefault() method in the event listener for the - * streamDestroyed event, the default behavior is prevented and you can clean up - * Subscriber objects using your own code. See - * Session.getSubscribersForStream(). - *

    - * For streams published by your own client, the Publisher object dispatches a - * streamDestroyed event. - *

    - * For a code example and more details, see {@link StreamEvent}. - * @name streamDestroyed - * @event - * @memberof Session - * @see StreamEvent - */ - - /** - * A stream has started or stopped publishing audio or video (see - * Publisher.publishAudio() and - * Publisher.publishVideo()); or the - * videoDimensions property of the Stream - * object has changed (see Stream.videoDimensions). - *

    - * Note that a subscriber's video can be disabled or enabled for reasons other than the - * publisher disabling or enabling it. A Subscriber object dispatches videoDisabled - * and videoEnabled events in all conditions that cause the subscriber's stream - * to be disabled or enabled. - * - * @name streamPropertyChanged - * @event - * @memberof Session - * @see StreamPropertyChangedEvent - * @see Publisher.publishAudio() - * @see Publisher.publishVideo() - * @see Stream.hasAudio - * @see Stream.hasVideo - * @see Stream.videoDimensions - * @see Subscriber videoDisabled event - * @see Subscriber videoEnabled event - */ - - /** - * A signal was received from the session. The SignalEvent - * class defines this event object. It includes the following properties: - *

      - *
    • data — (String) The data string sent with the signal (if there - * is one).
    • - *
    • from — (Connection) The Connection - * corresponding to the client that sent with the signal.
    • - *
    • type — (String) The type assigned to the signal (if there is - * one).
    • - *
    - *

    - * You can register to receive all signals sent in the session, by adding an event handler - * for the signal event. For example, the following code adds an event handler - * to process all signals sent in the session: - *

    -	 * session.on("signal", function(event) {
    -	 *   console.log("Signal sent from connection: " + event.from.id);
    -	 *   console.log("Signal data: " + event.data);
    -	 * });
    -	 * 
    - *

    You can register for signals of a specfied type by adding an event handler for the - * signal:type event (replacing type with the actual type string - * to filter on). - * - * @name signal - * @event - * @memberof Session - * @see Session.signal() - * @see SignalEvent - * @see signal:type event - */ - - /** - * A signal of the specified type was received from the session. The - * SignalEvent class defines this event object. - * It includes the following properties: - *

      - *
    • data — (String) The data string sent with the signal.
    • - *
    • from — (Connection) The Connection - * corresponding to the client that sent with the signal.
    • - *
    • type — (String) The type assigned to the signal (if there is one). - *
    • - *
    - *

    - * You can register for signals of a specfied type by adding an event handler for the - * signal:type event (replacing type with the actual type string - * to filter on). For example, the following code adds an event handler for signals of - * type "foo": - *

    -	 * session.on("signal:foo", function(event) {
    -	 *   console.log("foo signal sent from connection " + event.from.id);
    -	 *   console.log("Signal data: " + event.data);
    -	 * });
    -	 * 
    - *

    - * You can register to receive all signals sent in the session, by adding an event - * handler for the signal event. - * - * @name signal:type - * @event - * @memberof Session - * @see Session.signal() - * @see SignalEvent - * @see signal event - */ - }; +/** + * The publisher has started streaming to the session. + * @name streamCreated + * @event + * @memberof Publisher + * @see StreamEvent + * @see Session.publish() + */ -})(window); -(function() { +/** + * The publisher has stopped streaming to the session. The default behavior is that + * the Publisher object is removed from the HTML DOM). The Publisher object dispatches a + * destroyed event when the element is removed from the HTML DOM. If you call the + * preventDefault() method of the event object in the event listener, the default + * behavior is prevented, and you can, optionally, retain the Publisher for reuse or clean it up + * using your own code. + * @name streamDestroyed + * @event + * @memberof Publisher + * @see StreamEvent + */ - var txt = function(text) { - return document.createTextNode(text); - }; +/** +* Dispatched when the Publisher element is removed from the HTML DOM. When this event +* is dispatched, you may choose to adjust or remove HTML DOM elements related to the publisher. +* @name destroyed +* @event +* @memberof Publisher +*/ - var el = function(attr, children, tagName) { - var el = OT.$.createElement(tagName || 'div', attr, children); - el.on = OT.$.bind(OT.$.on, OT.$, el); - return el; - }; +/** +* Dispatched when the video dimensions of the video change. This can only occur in when the +* stream.videoType property is set to "screen" (for a screen-sharing +* video stream), and the user resizes the window being captured. +* @name videoDimensionsChanged +* @event +* @memberof Publisher +*/ + +/** + * The user has stopped screen-sharing for the published stream. This event is only dispatched + * for screen-sharing video streams. + * @name mediaStopped + * @event + * @memberof Publisher + * @see StreamEvent + */ +}; + +// Helper function to generate unique publisher ids +OT.Publisher.nextId = OT.$.uuid; + +// tb_require('../../conf/properties.js') +// tb_require('../ot.js') +// tb_require('./session.js') +// tb_require('./publisher.js') + +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global OT */ + + +/** +* The first step in using the OpenTok API is to call the OT.initSession() +* method. Other methods of the OT object check for system requirements and set up error logging. +* +* @class OT +*/ - function DevicePickerController(opts) { - var destroyExistingPublisher, - publisher, - devicesById; +/** +*

    +* Initializes and returns the local session object for a specified session ID. +*

    +*

    +* You connect to an OpenTok session using the connect() method +* of the Session object returned by the OT.initSession() method. +* Note that calling OT.initSession() does not initiate communications +* with the cloud. It simply initializes the Session object that you can use to +* connect (and to perform other operations once connected). +*

    +* +*

    +* For an example, see Session.connect(). +*

    +* +* @method OT.initSession +* @memberof OT +* @param {String} apiKey Your OpenTok API key (see the +* OpenTok dashboard). +* @param {String} sessionId The session ID identifying the OpenTok session. For more +* information, see Session creation. +* @returns {Session} The session object through which all further interactions with +* the session will occur. +*/ +OT.initSession = function(apiKey, sessionId) { - this.change = OT.$.bind(function() { - destroyExistingPublisher(); + if(sessionId == null) { + sessionId = apiKey; + apiKey = null; + } - var settings; + var session = OT.sessions.get(sessionId); - this.pickedDevice = devicesById[opts.selectTag.value]; + if (!session) { + session = new OT.Session(apiKey, sessionId); + OT.sessions.add(session); + } - if(!this.pickedDevice) { - console.log('No device for', opts.mode, opts.selectTag.value); - return; - } + return session; +}; - settings = { - insertMode: 'append', - name: this.pickedDevice.label, - audioSource: null, - videoSource: null, - width: 220, - height: 165 - }; +/** +*

    +* Initializes and returns a Publisher object. You can then pass this Publisher +* object to Session.publish() to publish a stream to a session. +*

    +*

    +* Note: If you intend to reuse a Publisher object created using +* OT.initPublisher() to publish to different sessions sequentially, +* call either Session.disconnect() or Session.unpublish(). +* Do not call both. Then call the preventDefault() method of the +* streamDestroyed or sessionDisconnected event object to prevent the +* Publisher object from being removed from the page. +*

    +* +* @param {Object} targetElement (Optional) The DOM element or the id attribute of the +* existing DOM element used to determine the location of the Publisher video in the HTML DOM. See +* the insertMode property of the properties parameter. If you do not +* specify a targetElement, the application appends a new DOM element to the HTML +* body. +* +*

    +* The application throws an error if an element with an ID set to the +* targetElement value does not exist in the HTML DOM. +*

    +* +* @param {Object} properties (Optional) This object contains the following properties (each of which +* are optional): +*

    +*
      +*
    • +* audioFallbackEnabled (String) — Whether the stream will use the +* audio-fallback feature (true) or not (false). The audio-fallback +* feature is available in sessions that use the the OpenTok Media Router. With the audio-fallback +* feature enabled (the default), when the server determines that a stream's quality has degraded +* significantly for a specific subscriber, it disables the video in that subscriber in order to +* preserve audio quality. For streams that use a camera as a video source, the default setting is +* true (the audio-fallback feature is enabled). The default setting is +* false (the audio-fallback feature is disabled) for screen-sharing streams, which +* have the videoSource set to "screen" in the +* OT.initPublisher() options. For more information, see the Subscriber +* videoDisabled event and +* the OpenTok Media +* Router and media modes. +*
    • +*
    • +* audioSource (String) — The ID of the audio input device (such as a +* microphone) to be used by the publisher. You can obtain a list of available devices, including +* audio input devices, by calling the OT.getDevices() method. Each +* device listed by the method has a unique device ID. If you pass in a device ID that does not +* match an existing audio input device, the call to OT.initPublisher() fails with an +* error (error code 1500, "Unable to Publish") passed to the completion handler function. +*

      +* If you set this property to null or false, the browser does not +* request access to the microphone, and no audio is published. +*

      +*
    • +*
    • +* fitMode (String) — Determines how the video is displayed if the its +* dimensions do not match those of the DOM element. You can set this property to one of the +* following values: +*

      +*

        +*
      • +* "cover" — The video is cropped if its dimensions do not match those of +* the DOM element. This is the default setting for screen-sharing videos. +*
      • +*
      • +* "contain" — The video is letter-boxed if its dimensions do not match +* those of the DOM element. This is the default setting for videos publishing a camera feed. +*
      • +*
      +*
    • +*
    • +* frameRate (Number) — The desired frame rate, in frames per second, +* of the video. Valid values are 30, 15, 7, and 1. The published stream will use the closest +* value supported on the publishing client. The frame rate can differ slightly from the value +* you set, depending on the browser of the client. And the video will only use the desired +* frame rate if the client configuration supports it. +*

      If the publisher specifies a frame rate, the actual frame rate of the video stream +* is set as the frameRate property of the Stream object, though the actual frame rate +* will vary based on changing network and system conditions. If the developer does not specify a +* frame rate, this property is undefined. +*

      +* For sessions that use the OpenTok Media Router (sessions with +* the media mode +* set to routed, lowering the frame rate or lowering the resolution reduces +* the maximum bandwidth the stream can use. However, in sessions with the media mode set to +* relayed, lowering the frame rate or resolution may not reduce the stream's bandwidth. +*

      +*

      +* You can also restrict the frame rate of a Subscriber's video stream. To restrict the frame rate +* a Subscriber, call the restrictFrameRate() method of the subscriber, passing in +* true. +* (See Subscriber.restrictFrameRate().) +*

      +*
    • +*
    • +* height (Number) — The desired height, in pixels, of the +* displayed Publisher video stream (default: 198). Note: Use the +* height and width properties to set the dimensions +* of the publisher video; do not set the height and width of the DOM element +* (using CSS). +*
    • +*
    • +* insertMode (String) — Specifies how the Publisher object will be +* inserted in the HTML DOM. See the targetElement parameter. This string can +* have the following values: +*
        +*
      • "replace" — The Publisher object replaces contents of the +* targetElement. This is the default.
      • +*
      • "after" — The Publisher object is a new element inserted after +* the targetElement in the HTML DOM. (Both the Publisher and targetElement have the +* same parent element.)
      • +*
      • "before" — The Publisher object is a new element inserted before +* the targetElement in the HTML DOM. (Both the Publisher and targetElement have the same +* parent element.)
      • +*
      • "append" — The Publisher object is a new element added as a child +* of the targetElement. If there are other child elements, the Publisher is appended as +* the last child element of the targetElement.
      • +*
      +*
    • +*
    • +* maxResolution (Object) — Sets the maximum resoultion to stream. +* This setting only applies to when the videoSource property is set to +* "screen" (when the publisher is screen-sharing). The resolution of the +* stream will match the captured screen region unless the region is greater than the +* maxResolution setting. Set this to an object that has two properties: +* width and height (both numbers). The maximum value for each of +* the width and height properties is 1920, and the minimum value +* is 10. +*
    • +*
    • +* mirror (Boolean) — Whether the publisher's video image +* is mirrored in the publisher's page. The default value is true +* (the video image is mirrored), except when the videoSource property is set +* to "screen" (in which case the default is false). This property +* does not affect the display on subscribers' views of the video. +*
    • +*
    • +* name (String) — The name for this stream. The name appears at +* the bottom of Subscriber videos. The default value is "" (an empty string). Setting +* this to a string longer than 1000 characters results in an runtime exception. +*
    • +*
    • +* publishAudio (Boolean) — Whether to initially publish audio +* for the stream (default: true). This setting applies when you pass +* the Publisher object in a call to the Session.publish() method. +*
    • +*
    • +* publishVideo (Boolean) — Whether to initially publish video +* for the stream (default: true). This setting applies when you pass +* the Publisher object in a call to the Session.publish() method. +*
    • +*
    • +* resolution (String) — The desired resolution of the video. The format +* of the string is "widthxheight", where the width and height are represented in +* pixels. Valid values are "1280x720", "640x480", and +* "320x240". The published video will only use the desired resolution if the +* client configuration supports it. +*

      +* The requested resolution of a video stream is set as the videoDimensions.width and +* videoDimensions.height properties of the Stream object. +*

      +*

      +* The default resolution for a stream (if you do not specify a resolution) is 640x480 pixels. +* If the client system cannot support the resolution you requested, the the stream will use the +* next largest setting supported. +*

      +*

      +* For sessions that use the OpenTok Media Router (sessions with the +* media mode +* set to routed, lowering the frame rate or lowering the resolution reduces the maximum bandwidth +* the stream can use. However, in sessions that have the media mode set to relayed, lowering the +* frame rate or resolution may not reduce the stream's bandwidth. +*

      +*
    • +*
    • +* style (Object) — An object containing properties that define the initial +* appearance of user interface controls of the Publisher. The style object includes +* the following properties: +*
        +*
      • audioLevelDisplayMode (String) — How to display the audio level +* indicator. Possible values are: "auto" (the indicator is displayed when the +* video is disabled), "off" (the indicator is not displayed), and +* "on" (the indicator is always displayed).
      • +* +*
      • backgroundImageURI (String) — A URI for an image to display as +* the background image when a video is not displayed. (A video may not be displayed if +* you call publishVideo(false) on the Publisher object). You can pass an http +* or https URI to a PNG, JPEG, or non-animated GIF file location. You can also use the +* data URI scheme (instead of http or https) and pass in base-64-encrypted +* PNG data, such as that obtained from the +* Publisher.getImgData() method. For example, +* you could set the property to "data:VBORw0KGgoAA...", where the portion of the +* string after "data:" is the result of a call to +* Publisher.getImgData(). If the URL or the image data is invalid, the property +* is ignored (the attempt to set the image fails silently). +*

        +* Note that in Internet Explorer 8 (using the OpenTok Plugin for Internet Explorer), +* you cannot set the backgroundImageURI style to a string larger than 32 kB. +* This is due to an IE 8 limitation on the size of URI strings. Due to this limitation, +* you cannot set the backgroundImageURI style to a string obtained with the +* getImgData() method. +*

      • +* +*
      • buttonDisplayMode (String) — How to display the microphone controls +* Possible values are: "auto" (controls are displayed when the stream is first +* displayed and when the user mouses over the display), "off" (controls are not +* displayed), and "on" (controls are always displayed).
      • +* +*
      • nameDisplayMode (String) — Whether to display the stream name. +* Possible values are: "auto" (the name is displayed when the stream is first +* displayed and when the user mouses over the display), "off" (the name is not +* displayed), and "on" (the name is always displayed).
      • +*
      +*
    • +*
    • +* videoSource (String) — The ID of the video input device (such as a +* camera) to be used by the publisher. You can obtain a list of available devices, including +* video input devices, by calling the OT.getDevices() method. Each +* device listed by the method has a unique device ID. If you pass in a device ID that does not +* match an existing video input device, the call to OT.initPublisher() fails with an +* error (error code 1500, "Unable to Publish") passed to the completion handler function. +*

      +* If you set this property to null or false, the browser does not +* request access to the camera, and no video is published. In a voice-only call, set this +* property to null or false for each Publisher. +*

      +*

      +* Set this property to "screen" to publish a screen-sharing stream. Call +* OT.checkScreenSharingCapability() to check +* if screen sharing is supported. When you set the videoSource property to +* "screen", the following are default values for other properties: +* audioFallbackEnabled == false, +* maxResolution == {width: 1920, height: 1920}, mirror == false, +* scaleMode == "fit". Also, the default scaleMode setting for +* subscribers to the stream is "fit". +*

    • +*
    • +* width (Number) — The desired width, in pixels, of the +* displayed Publisher video stream (default: 264). Note: Use the +* height and width properties to set the dimensions +* of the publisher video; do not set the height and width of the DOM element +* (using CSS). +*
    • +*
    +* @param {Function} completionHandler (Optional) A function to be called when the method succeeds +* or fails in initializing a Publisher object. This function takes one parameter — +* error. On success, the error object is set to null. On +* failure, the error object has two properties: code (an integer) and +* message (a string), which identify the cause of the failure. The method succeeds +* when the user grants access to the camera and microphone. The method fails if the user denies +* access to the camera and microphone. The completionHandler function is called +* before the Publisher dispatches an accessAllowed (success) event or an +* accessDenied (failure) event. +*

    +* The following code adds a completionHandler when calling the +* OT.initPublisher() method: +*

    +*
    +* var publisher = OT.initPublisher('publisher', null, function (error) {
    +*   if (error) {
    +*     console.log(error);
    +*   } else {
    +*     console.log("Publisher initialized.");
    +*   }
    +* });
    +* 
    +* +* @returns {Publisher} The Publisher object. +* @see for audio input + * devices or "videoInput" for video input devices. + *

    + * The deviceId property is a unique ID for the device. You can pass + * the deviceId in as the audioSource or videoSource + * property of the the options parameter of the + * OT.initPublisher() method. + *

    + * The label property identifies the device. The label + * property is set to an empty string if the user has not previously granted access to + * a camera and microphone. In HTTP, the user must have granted access to a camera and + * microphone in the current page (for example, in response to a call to + * OT.initPublisher()). In HTTPS, the user must have previously granted access + * to the camera and microphone in the current page or in a page previously loaded from the + * domain. + * + * + * @see OT.initPublisher() + * @method OT.getDevices + * @memberof OT + */ +OT.getDevices = function(callback) { + OT.$.getMediaDevices(callback); +}; - this.videoSource = function() { - return camera && camera.pickedDevice; - }; - this.destroy = OT.$.bind(function() { - if(this.is('destroyed')) { - return; - } - if(camera) { - camera.cleanup(); - } - if(microphone) { - microphone.cleanup(); - } - if(this.is('chooseDevices')) { - targetElement.parentNode.removeChild(targetElement); - } - setState('destroyed'); - }, this); - if(targetElement == null) { - callback(new Error('You must provide a targetElement')); - return; - } +OT.reportIssue = function(){ + OT.warn('ToDo: haven\'t yet implemented OT.reportIssue'); +}; - if(!OT.$.hasCapabilities('getMediaDevices')) { - callback(new Error('This browser does not support getMediaDevices APIs')); - return; - } +OT.components = {}; - var camSelector, - camPreview, - micSelector, - micPreview, - container; - - camSelector = el({ style: 'width: 100%' }, '', 'select'); - camPreview = el({ - style: 'background-color: #000; margin-left: 100px; width: 220px; height: 165px;' - }, ''), - micSelector = el({ style: 'width: 100%' }, '', 'select'), - micPreview = el({ - style: 'background-color: #000; margin-left: 100px; width: 220px; height: 165px;' - }, ''); - container = el({ - id: 'OT_' + OT.$.uuid(), - style: 'border: 1px solid #000; padding: 10px; width: 320px;' - }, [ - el({ style: 'padding: 0 0 10px; overflow: auto; text-align: right; ' }, [ - el({ style: 'float: left; width: 90px; padding-right: 10px; line-height: 160%;' }, - 'Camera'), - el({ style: 'margin-left: 100px; ' }, camSelector), - camPreview - ]), - el({ style: 'overflow: auto; text-align: right;' }, [ - el({ style: 'float: left; width: 90px; padding-right: 10px; line-height: 160%;' }, - 'Microphone'), - el({ style: 'margin-left: 100px; ' }, micSelector), - micPreview - ]) - ]); - - camera = new DevicePickerController({ - selectTag: camSelector, - previewTag: camPreview, - mode: 'videoSource' - }); - - microphone = new DevicePickerController({ - selectTag: micSelector, - previewTag: micPreview, - mode: 'audioSource' - }); +/** + * This method is deprecated. Use on() or once() instead. + * + *

    + * Registers a method as an event listener for a specific event. + *

    + * + *

    + * The OT object dispatches one type of event — an exception event. The + * following code adds an event listener for the exception event: + *

    + * + *
    + * OT.addEventListener("exception", exceptionHandler);
    + *
    + * function exceptionHandler(event) {
    + *    alert("exception event. \n  code == " + event.code + "\n  message == " + event.message);
    + * }
    + * 
    + * + *

    + * If a handler is not registered for an event, the event is ignored locally. If the event + * listener function does not exist, the event is ignored locally. + *

    + *

    + * Throws an exception if the listener name is invalid. + *

    + * + * @param {String} type The string identifying the type of event. + * + * @param {Function} listener The function to be invoked when the OT object dispatches the event. + * @see on() + * @see once() + * @memberof OT + * @method addEventListener + * + */ - camera.setLoading(); - microphone.setLoading(); +/** + * This method is deprecated. Use off() instead. + * + *

    + * Removes an event listener for a specific event. + *

    + * + *

    + * Throws an exception if the listener name is invalid. + *

    + * + * @param {String} type The string identifying the type of event. + * + * @param {Function} listener The event listener function to remove. + * + * @see off() + * @memberof OT + * @method removeEventListener + */ - OT.getDevices(OT.$.bind(function(error, devices) { - if (error) { - callback(error); - return; - } - if(this.is('destroyed')) { - return; // They destroyed us before we got the devices, bail. - } +/** +* Adds an event handler function for one or more events. +* +*

    +* The OT object dispatches one type of event — an exception event. The following +* code adds an event +* listener for the exception event: +*

    +* +*
    +* OT.on("exception", function (event) {
    +*   // This is the event handler.
    +* });
    +* 
    +* +*

    You can also pass in a third context parameter (which is optional) to define the +* value of +* this in the handler method:

    +* +*
    +* OT.on("exception",
    +*   function (event) {
    +*     // This is the event handler.
    +*   }),
    +*   session
    +* );
    +* 
    +* +*

    +* If you do not add a handler for an event, the event is ignored locally. +*

    +* +* @param {String} type The string identifying the type of event. +* @param {Function} handler The handler function to process the event. This function takes the event +* object as a parameter. +* @param {Object} context (Optional) Defines the value of this in the event handler +* function. +* +* @memberof OT +* @method on +* @see off() +* @see once() +* @see Events +*/ - setupDOM(); +/** +* Adds an event handler function for an event. Once the handler is called, the specified handler +* method is +* removed as a handler for this event. (When you use the OT.on() method to add an event +* handler, the handler +* is not removed when it is called.) The OT.once() method is the equivilent of +* calling the OT.on() +* method and calling OT.off() the first time the handler is invoked. +* +*

    +* The following code adds a one-time event handler for the exception event: +*

    +* +*
    +* OT.once("exception", function (event) {
    +*   console.log(event);
    +* }
    +* 
    +* +*

    You can also pass in a third context parameter (which is optional) to define the +* value of +* this in the handler method:

    +* +*
    +* OT.once("exception",
    +*   function (event) {
    +*     // This is the event handler.
    +*   },
    +*   session
    +* );
    +* 
    +* +*

    +* The method also supports an alternate syntax, in which the first parameter is an object that is a +* hash map of +* event names and handler functions and the second parameter (optional) is the context for this in +* each handler: +*

    +*
    +* OT.once(
    +*   {exeption: function (event) {
    +*     // This is the event handler.
    +*     }
    +*   },
    +*   session
    +* );
    +* 
    +* +* @param {String} type The string identifying the type of event. You can specify multiple event +* names in this string, +* separating them with a space. The event handler will process the first occurence of the events. +* After the first event, +* the handler is removed (for all specified events). +* @param {Function} handler The handler function to process the event. This function takes the event +* object as a parameter. +* @param {Object} context (Optional) Defines the value of this in the event handler +* function. +* +* @memberof OT +* @method once +* @see on() +* @see once() +* @see Events +*/ - camera.setDeviceList(devices.filter(function(device) { - return device.kind === 'videoinput'; - })); - microphone.setDeviceList(devices.filter(function(device) { - return device.kind === 'audioinput'; - })); +/** + * Removes an event handler. + * + *

    Pass in an event name and a handler method, the handler is removed for that event:

    + * + *
    OT.off("exceptionEvent", exceptionEventHandler);
    + * + *

    If you pass in an event name and no handler method, all handlers are removed for that + * events:

    + * + *
    OT.off("exceptionEvent");
    + * + *

    + * The method also supports an alternate syntax, in which the first parameter is an object that is a + * hash map of + * event names and handler functions and the second parameter (optional) is the context for matching + * handlers: + *

    + *
    + * OT.off(
    + *   {
    + *     exceptionEvent: exceptionEventHandler
    + *   },
    + *   this
    + * );
    + * 
    + * + * @param {String} type (Optional) The string identifying the type of event. You can use a space to + * specify multiple events, as in "eventName1 eventName2 eventName3". If you pass in no + * type value (or other arguments), all event handlers are removed for the object. + * @param {Function} handler (Optional) The event handler function to remove. If you pass in no + * handler, all event handlers are removed for the specified event type. + * @param {Object} context (Optional) If you specify a context, the event handler is + * removed for all specified events and handlers that use the specified context. + * + * @memberof OT + * @method off + * @see on() + * @see once() + * @see Events + */ - setState('chooseDevices'); +/** + * Dispatched by the OT class when the app encounters an exception. + * Note that you set up an event handler for the exception event by calling the + * OT.on() method. + * + * @name exception + * @event + * @borrows ExceptionEvent#message as this.message + * @memberof OT + * @see ExceptionEvent + */ - }, this)); - setupDOM = function() { - var insertMode = options.insertMode; - if(!(insertMode == null || insertMode === 'replace')) { - if(insertMode === 'append') { - targetElement.appendChild(container); - targetElement = container; - } else if(insertMode === 'before') { - targetElement.parentNode.insertBefore(container, targetElement); - targetElement = container; - } else if(insertMode === 'after') { - targetElement.parentNode.insertBefore(container, targetElement.nextSibling); - targetElement = container; - } - } else { - OT.$.emptyElement(targetElement); - if(targetElement.getAttribute('id') == null) { - targetElement.setAttribute('id', container.getAttribute('id')); - } - for(var key in container.style) { - targetElement.style[key] = container.style[key]; - } - while(container.childNodes.length > 0) { - targetElement.appendChild(container.firstChild); - } - } - }; +// tb_require('./helpers/lib/css_loader.js') +// tb_require('./ot/system_requirements.js') +// tb_require('./ot/session.js') +// tb_require('./ot/publisher.js') +// tb_require('./ot/subscriber.js') +// tb_require('./ot/archive.js') +// tb_require('./ot/connection.js') +// tb_require('./ot/stream.js') +// We want this to be included at the end, just before footer.js - }; +/* jshint globalstrict: true, strict: false, undef: true, unused: true, + trailing: true, browser: true, smarttabs:true */ +/* global loadCSS, define */ - OT.initHardwareSetup = function(targetElement, options, callback) { - return new OT.HardwareSetup(targetElement, options, callback); - }; +// Tidy up everything on unload +OT.onUnload(function() { + OT.publishers.destroy(); + OT.subscribers.destroy(); + OT.sessions.destroy('unloaded'); +}); -})(); -!(function() { - var style = document.createElement('link'); - style.type = 'text/css'; - style.media = 'screen'; - style.rel = 'stylesheet'; - style.href = OT.properties.cssURL; - var head = document.head || document.getElementsByTagName('head')[0]; - head.appendChild(style); -})(window); -!(function(){ -/*global define*/ +loadCSS(OT.properties.cssURL); // Register as a named AMD module, since TokBox could be concatenated with other // files that may use define, but not via a proper concatenation script that @@ -22424,8 +24115,13 @@ // way to register. Uppercase TB is used because AMD module names are // derived from file names, and OpenTok is normally delivered in an uppercase // file name. - if (typeof define === 'function' && define.amd) { - define( 'TB', [], function () { return TB; } ); - } +if (typeof define === 'function' && define.amd) { + define( 'TB', [], function () { return TB; } ); +} +// tb_require('./postscript.js') -})(window); +/* jshint ignore:start */ +})(window, window.OT); +/* jshint ignore:end */ + +})(window || exports); \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/README.txt thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/README.txt --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/README.txt 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/README.txt 2015-02-03 14:33:16.000000000 +0000 @@ -12,6 +12,25 @@ (from content/shared/) with the desktop implementation. See the README.md file in the standalone/ directory for how to run the server locally. +Working with React JSX files +============================ + +Our views use [React](http://facebook.github.io/react/) written in JSX files +and transpiled to JS before we commit. You need to install the JSX compiler +using npm in order to compile the .jsx files into regular .js ones: + + npm install -g react-tools + +Once installed, run build-jsx with the --watch option from +browser/components/loop, eg.: + + cd browser/components/loop + ./build-jsx --watch + +build-jsx can also be do a one-time compile pass instead of watching if +the --watch argument is omitted. Be sure to commit any transpiled files +at the same time as changes to their sources. + Hacking ======= @@ -22,6 +41,19 @@ from the top level before requesting review on a patch. +Front-End Unit Tests +==================== +The unit tests for Loop reside in three directories: + +- test/desktop-local +- test/shared +- test/standalone + +You can run these as part of the run-all-loop-tests.sh command above, or you can run these individually in Firefox. To run them individually, start the standalone client (see standalone/README.md) and load: + + http://localhost:3000/test/ + + Functional Tests ================ These are currently a work in progress, but it's already possible to run a test @@ -36,21 +68,10 @@ as well. -Working with React JSX files -============================ - -Our views use [React](http://facebook.github.io/react/) written in JSX files -and transpiled to JS before we commit. You need to install the JSX compiler -using npm in order to compile the .jsx files into regular .js ones: - - npm install -g react-tools - -Once installed, run build-jsx with the --watch option from -browser/components/loop, eg.: +UI-Showcase +=========== +This is a tool giving the layouts for all the frontend views of Loop, allowing debugging and testing of css layouts and local component behavior. - cd browser/components/loop - ./build-jsx --watch +To access it, start the standalone client (see standalone/README.md) and load: -build-jsx can also be do a one-time compile pass instead of watching if -the --watch argument is omitted. Be sure to commit any transpiled files -at the same time as changes to their sources. + http://localhost:3000/ui/ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/standalone/content/index.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/standalone/content/index.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/standalone/content/index.html 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/standalone/content/index.html 2015-02-03 14:33:16.000000000 +0000 @@ -101,7 +101,6 @@ - @@ -113,6 +112,7 @@ + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/standalone/content/js/standaloneRoomViews.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/standalone/content/js/standaloneRoomViews.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/standalone/content/js/standaloneRoomViews.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/standalone/content/js/standaloneRoomViews.js 2015-02-03 14:33:16.000000000 +0000 @@ -224,7 +224,9 @@ * @private */ _onActiveRoomStateChanged: function() { - this.setState(this.props.activeRoomStore.getStoreState()); + var state = this.props.activeRoomStore.getStoreState(); + this.updateVideoDimensions(state.localVideoDimensions, state.remoteVideoDimensions); + this.setState(state); }, componentDidMount: function() { @@ -249,7 +251,8 @@ this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({ publisherConfig: this.getDefaultPublisherConfig({publishVideo: true}), getLocalElementFunc: this._getElement.bind(this, ".local"), - getRemoteElementFunc: this._getElement.bind(this, ".remote") + getRemoteElementFunc: this._getElement.bind(this, ".remote"), + getScreenShareElementFunc: this._getElement.bind(this, ".screen") })); } @@ -284,6 +287,41 @@ }, /** + * Specifically updates the local camera stream size and position, depending + * on the size and position of the remote video stream. + * This method gets called from `updateVideoContainer`, which is defined in + * the `MediaSetupMixin`. + * + * @param {Object} ratio Aspect ratio of the local camera stream + */ + updateLocalCameraPosition: function(ratio) { + var node = this._getElement(".local"); + var parent = node.offsetParent || this._getElement(".media"); + // The local camera view should be a sixth of the size of its offset parent + // and positioned to overlap with the remote stream at a quarter of its width. + var parentWidth = parent.offsetWidth; + var targetWidth = parentWidth / 6; + + node.style.right = "auto"; + if (window.matchMedia && window.matchMedia("screen and (max-width:640px)").matches) { + targetWidth = 180; + node.style.left = "auto"; + } else { + // Now position the local camera view correctly with respect to the remote + // video stream. + var remoteVideoDimensions = this.getRemoteVideoDimensions(); + var offsetX = (remoteVideoDimensions.streamWidth + remoteVideoDimensions.offsetX); + // The horizontal offset of the stream, and the width of the resulting + // pillarbox, is determined by the height exponent of the aspect ratio. + // Therefore we multiply the width of the local camera view by the height + // ratio. + node.style.left = (offsetX - ((targetWidth * ratio.height) / 4)) + "px"; + } + node.style.width = (targetWidth * ratio.width) + "px"; + node.style.height = (targetWidth * ratio.height) + "px"; + }, + + /** * Checks if current room is active. * * @return {Boolean} @@ -302,6 +340,19 @@ "local-stream-audio": this.state.videoMuted }); + var remoteStreamClasses = React.addons.classSet({ + "video_inner": true, + "remote": true, + "remote-stream": true, + hide: this.state.receivingScreenShare + }); + + var screenShareStreamClasses = React.addons.classSet({ + "screen": true, + "remote-stream": true, + hide: !this.state.receivingScreenShare + }); + return ( React.createElement("div", {className: "room-conversation-wrapper"}, React.createElement("div", {className: "beta-logo"}), @@ -320,11 +371,13 @@ mozL10n.get("self_view_hidden_message") ), React.createElement("div", {className: "video_wrapper remote_wrapper"}, - React.createElement("div", {className: "video_inner remote"}) + React.createElement("div", {className: remoteStreamClasses}), + React.createElement("div", {className: screenShareStreamClasses}) ), React.createElement("div", {className: localStreamClasses}) ), React.createElement(sharedViews.ConversationToolbar, { + dispatcher: this.props.dispatcher, video: {enabled: !this.state.videoMuted, visible: this._roomIsActive()}, audio: {enabled: !this.state.audioMuted, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx 2015-02-03 14:33:16.000000000 +0000 @@ -224,7 +224,9 @@ * @private */ _onActiveRoomStateChanged: function() { - this.setState(this.props.activeRoomStore.getStoreState()); + var state = this.props.activeRoomStore.getStoreState(); + this.updateVideoDimensions(state.localVideoDimensions, state.remoteVideoDimensions); + this.setState(state); }, componentDidMount: function() { @@ -249,7 +251,8 @@ this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({ publisherConfig: this.getDefaultPublisherConfig({publishVideo: true}), getLocalElementFunc: this._getElement.bind(this, ".local"), - getRemoteElementFunc: this._getElement.bind(this, ".remote") + getRemoteElementFunc: this._getElement.bind(this, ".remote"), + getScreenShareElementFunc: this._getElement.bind(this, ".screen") })); } @@ -284,6 +287,41 @@ }, /** + * Specifically updates the local camera stream size and position, depending + * on the size and position of the remote video stream. + * This method gets called from `updateVideoContainer`, which is defined in + * the `MediaSetupMixin`. + * + * @param {Object} ratio Aspect ratio of the local camera stream + */ + updateLocalCameraPosition: function(ratio) { + var node = this._getElement(".local"); + var parent = node.offsetParent || this._getElement(".media"); + // The local camera view should be a sixth of the size of its offset parent + // and positioned to overlap with the remote stream at a quarter of its width. + var parentWidth = parent.offsetWidth; + var targetWidth = parentWidth / 6; + + node.style.right = "auto"; + if (window.matchMedia && window.matchMedia("screen and (max-width:640px)").matches) { + targetWidth = 180; + node.style.left = "auto"; + } else { + // Now position the local camera view correctly with respect to the remote + // video stream. + var remoteVideoDimensions = this.getRemoteVideoDimensions(); + var offsetX = (remoteVideoDimensions.streamWidth + remoteVideoDimensions.offsetX); + // The horizontal offset of the stream, and the width of the resulting + // pillarbox, is determined by the height exponent of the aspect ratio. + // Therefore we multiply the width of the local camera view by the height + // ratio. + node.style.left = (offsetX - ((targetWidth * ratio.height) / 4)) + "px"; + } + node.style.width = (targetWidth * ratio.width) + "px"; + node.style.height = (targetWidth * ratio.height) + "px"; + }, + + /** * Checks if current room is active. * * @return {Boolean} @@ -302,6 +340,19 @@ "local-stream-audio": this.state.videoMuted }); + var remoteStreamClasses = React.addons.classSet({ + "video_inner": true, + "remote": true, + "remote-stream": true, + hide: this.state.receivingScreenShare + }); + + var screenShareStreamClasses = React.addons.classSet({ + "screen": true, + "remote-stream": true, + hide: !this.state.receivingScreenShare + }); + return (
    @@ -320,11 +371,13 @@ {mozL10n.get("self_view_hidden_message")}
    -
    +
    +
    - @@ -56,6 +55,7 @@ + @@ -73,6 +73,7 @@ + - @@ -56,6 +55,7 @@ + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/test/shared/mixins_test.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/test/shared/mixins_test.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/test/shared/mixins_test.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/test/shared/mixins_test.js 2015-02-03 14:33:17.000000000 +0000 @@ -204,8 +204,16 @@ } }); + sandbox.useFakeTimers(); + rootObject = { events: {}, + setTimeout: function(func, timeout) { + return setTimeout(func, timeout); + }, + clearTimeout: function(timer) { + return clearTimeout(timer); + }, addEventListener: function(eventName, listener) { this.events[eventName] = listener; }, @@ -228,13 +236,15 @@ }); describe("Events", function() { - var localElement, remoteElement; + var localElement, remoteElement, screenShareElement; beforeEach(function() { sandbox.stub(view, "getDOMNode").returns({ querySelector: function(classSelector) { if (classSelector.contains("local")) { - return localElement; + return localElement; + } else if (classSelector.contains("screen")) { + return screenShareElement; } return remoteElement; } @@ -244,45 +254,134 @@ describe("resize", function() { it("should update the width on the local stream element", function() { localElement = { + offsetWidth: 100, + offsetHeight: 100, style: { width: "0%" } }; rootObject.events.resize(); + sandbox.clock.tick(10); expect(localElement.style.width).eql("100%"); }); it("should update the height on the remote stream element", function() { remoteElement = { + offsetWidth: 100, + offsetHeight: 100, style: { height: "0%" } }; rootObject.events.resize(); + sandbox.clock.tick(10); expect(remoteElement.style.height).eql("100%"); }); + + it("should update the height on the screen share stream element", function() { + screenShareElement = { + offsetWidth: 100, + offsetHeight: 100, + style: { height: "0%" } + }; + + rootObject.events.resize(); + sandbox.clock.tick(10); + + expect(screenShareElement.style.height).eql("100%"); + }); }); describe("orientationchange", function() { it("should update the width on the local stream element", function() { localElement = { + offsetWidth: 100, + offsetHeight: 100, style: { width: "0%" } }; rootObject.events.orientationchange(); + sandbox.clock.tick(10); expect(localElement.style.width).eql("100%"); }); it("should update the height on the remote stream element", function() { remoteElement = { + offsetWidth: 100, + offsetHeight: 100, style: { height: "0%" } }; rootObject.events.orientationchange(); + sandbox.clock.tick(10); expect(remoteElement.style.height).eql("100%"); }); + + it("should update the height on the screen share stream element", function() { + screenShareElement = { + offsetWidth: 100, + offsetHeight: 100, + style: { height: "0%" } + }; + + rootObject.events.orientationchange(); + sandbox.clock.tick(10); + + expect(screenShareElement.style.height).eql("100%"); + }); + }); + + + describe("Video stream dimensions", function() { + var localVideoDimensions = { + camera: { + width: 640, + height: 480 + } + }; + var remoteVideoDimensions = { + camera: { + width: 420, + height: 138 + } + }; + + beforeEach(function() { + view.updateVideoDimensions(localVideoDimensions, remoteVideoDimensions); + }); + + it("should register video dimension updates correctly", function() { + expect(view._videoDimensionsCache.local.camera.width) + .eql(localVideoDimensions.camera.width); + expect(view._videoDimensionsCache.local.camera.height) + .eql(localVideoDimensions.camera.height); + expect(view._videoDimensionsCache.local.camera.aspectRatio.width).eql(1); + expect(view._videoDimensionsCache.local.camera.aspectRatio.height).eql(0.75); + expect(view._videoDimensionsCache.remote.camera.width) + .eql(remoteVideoDimensions.camera.width); + expect(view._videoDimensionsCache.remote.camera.height) + .eql(remoteVideoDimensions.camera.height); + expect(view._videoDimensionsCache.remote.camera.aspectRatio.width).eql(1); + expect(view._videoDimensionsCache.remote.camera.aspectRatio.height) + .eql(0.32857142857142857); + }); + + it("should fetch remote video stream dimensions correctly", function() { + remoteElement = { + offsetWidth: 600, + offsetHeight: 320 + }; + + var remoteVideoDimensions = view.getRemoteVideoDimensions(); + expect(remoteVideoDimensions.width).eql(remoteElement.offsetWidth); + expect(remoteVideoDimensions.height).eql(remoteElement.offsetHeight); + expect(remoteVideoDimensions.streamWidth).eql(534.8571428571429); + expect(remoteVideoDimensions.streamHeight).eql(remoteElement.offsetHeight); + expect(remoteVideoDimensions.offsetX).eql(32.571428571428555); + expect(remoteVideoDimensions.offsetY).eql(0); + }); }); }); }); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/test/shared/otSdkDriver_test.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/test/shared/otSdkDriver_test.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/test/shared/otSdkDriver_test.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/test/shared/otSdkDriver_test.js 2015-02-03 14:33:17.000000000 +0000 @@ -8,15 +8,19 @@ var sharedActions = loop.shared.actions; var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS; + var STREAM_PROPERTIES = loop.shared.utils.STREAM_PROPERTIES; + var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES; var sandbox; var dispatcher, driver, publisher, sdk, session, sessionData; - var fakeLocalElement, fakeRemoteElement, publisherConfig, fakeEvent; + var fakeLocalElement, fakeRemoteElement, fakeScreenElement; + var publisherConfig, fakeEvent; beforeEach(function() { sandbox = sinon.sandbox.create(); fakeLocalElement = {fake: 1}; fakeRemoteElement = {fake: 2}; + fakeScreenElement = {fake: 3}; fakeEvent = { preventDefault: sinon.stub() }; @@ -34,6 +38,7 @@ connect: sinon.stub(), disconnect: sinon.stub(), publish: sinon.stub(), + unpublish: sinon.stub(), subscribe: sinon.stub(), forceDisconnect: sinon.stub() }, Backbone.Events); @@ -118,6 +123,74 @@ }); }); + describe("#startScreenShare", function() { + var fakeElement; + + beforeEach(function() { + sandbox.stub(dispatcher, "dispatch"); + + fakeElement = { + className: "fakeVideo" + }; + + driver.getScreenShareElementFunc = function() { + return fakeElement; + }; + }); + + it("should dispatch a `ScreenSharingState` action", function() { + driver.startScreenShare(new sharedActions.StartScreenShare()); + + sinon.assert.calledOnce(dispatcher.dispatch); + sinon.assert.calledWithExactly(dispatcher.dispatch, + new sharedActions.ScreenSharingState({ + state: SCREEN_SHARE_STATES.PENDING + })); + }); + + it("should initialize a publisher", function() { + driver.startScreenShare(new sharedActions.StartScreenShare()); + + sinon.assert.calledOnce(sdk.initPublisher); + sinon.assert.calledWithMatch(sdk.initPublisher, + fakeElement, {videoSource: "window"}); + }); + }); + + describe("#endScreenShare", function() { + beforeEach(function() { + driver.getScreenShareElementFunc = function() {}; + + driver.startScreenShare(new sharedActions.StartScreenShare()); + + sandbox.stub(dispatcher, "dispatch"); + + driver.session = session; + }); + + it("should unpublish the share", function() { + driver.endScreenShare(new sharedActions.EndScreenShare()); + + sinon.assert.calledOnce(session.unpublish); + }); + + it("should destroy the share", function() { + driver.endScreenShare(new sharedActions.EndScreenShare()); + + sinon.assert.calledOnce(publisher.destroy); + }); + + it("should dispatch a `ScreenSharingState` action", function() { + driver.endScreenShare(new sharedActions.EndScreenShare()); + + sinon.assert.calledOnce(dispatcher.dispatch); + sinon.assert.calledWithExactly(dispatcher.dispatch, + new sharedActions.ScreenSharingState({ + state: SCREEN_SHARE_STATES.INACTIVE + })); + }); + }); + describe("#connectSession", function() { it("should initialise a new session", function() { driver.connectSession(sessionData); @@ -213,12 +286,13 @@ }); }); - describe("Events", function() { + describe("Events (general media)", function() { beforeEach(function() { driver.connectSession(sessionData); dispatcher.dispatch(new sharedActions.SetupStreamElements({ getLocalElementFunc: function() {return fakeLocalElement;}, + getScreenShareElementFunc: function() {return fakeScreenElement;}, getRemoteElementFunc: function() {return fakeRemoteElement;}, publisherConfig: publisherConfig })); @@ -282,16 +356,50 @@ }); }); - describe("streamCreated", function() { + describe("streamCreated (publisher/local)", function() { + it("should dispatch a VideoDimensionsChanged action", function() { + var fakeStream = { + hasVideo: true, + videoType: "camera", + videoDimensions: {width: 1, height: 2} + }; + + publisher.trigger("streamCreated", {stream: fakeStream}); + + sinon.assert.calledOnce(dispatcher.dispatch); + sinon.assert.calledWithExactly(dispatcher.dispatch, + new sharedActions.VideoDimensionsChanged({ + isLocal: true, + videoType: "camera", + dimensions: {width: 1, height: 2} + })); + }); + }); + + describe("streamCreated (session/remote)", function() { var fakeStream; beforeEach(function() { fakeStream = { - fakeStream: 3 + hasVideo: true, + videoType: "camera", + videoDimensions: {width: 1, height: 2} }; }); - it("should subscribe to the stream", function() { + it("should dispatch a VideoDimensionsChanged action", function() { + session.trigger("streamCreated", {stream: fakeStream}); + + sinon.assert.calledOnce(dispatcher.dispatch); + sinon.assert.calledWithExactly(dispatcher.dispatch, + new sharedActions.VideoDimensionsChanged({ + isLocal: false, + videoType: "camera", + dimensions: {width: 1, height: 2} + })); + }); + + it("should subscribe to a camera stream", function() { session.trigger("streamCreated", {stream: fakeStream}); sinon.assert.calledOnce(session.subscribe); @@ -299,15 +407,123 @@ fakeStream, fakeRemoteElement, publisherConfig); }); + it("should subscribe to a screen sharing stream", function() { + fakeStream.videoType = "screen"; + + session.trigger("streamCreated", {stream: fakeStream}); + + sinon.assert.calledOnce(session.subscribe); + sinon.assert.calledWithExactly(session.subscribe, + fakeStream, fakeScreenElement, publisherConfig); + }); + it("should dispach a mediaConnected action if both streams are up", function() { driver._publishedLocalStream = true; session.trigger("streamCreated", {stream: fakeStream}); - sinon.assert.calledOnce(dispatcher.dispatch); + // Called twice due to the VideoDimensionsChanged above. + sinon.assert.calledTwice(dispatcher.dispatch); sinon.assert.calledWithMatch(dispatcher.dispatch, sinon.match.hasOwn("name", "mediaConnected")); }); + + it("should not dispatch a mediaConnected action for screen sharing streams", + function() { + driver._publishedLocalStream = true; + fakeStream.videoType = "screen"; + + session.trigger("streamCreated", {stream: fakeStream}); + + sinon.assert.neverCalledWithMatch(dispatcher.dispatch, + sinon.match.hasOwn("name", "mediaConnected")); + }); + + it("should not dispatch a ReceivingScreenShare action for camera streams", + function() { + session.trigger("streamCreated", {stream: fakeStream}); + + sinon.assert.neverCalledWithMatch(dispatcher.dispatch, + new sharedActions.ReceivingScreenShare({receiving: true})); + }); + + it("should dispatch a ReceivingScreenShare action for screen sharing streams", + function() { + fakeStream.videoType = "screen"; + + session.trigger("streamCreated", {stream: fakeStream}); + + // Called twice due to the VideoDimensionsChanged above. + sinon.assert.calledTwice(dispatcher.dispatch); + sinon.assert.calledWithMatch(dispatcher.dispatch, + new sharedActions.ReceivingScreenShare({receiving: true})); + }); + }); + + describe("streamDestroyed", function() { + var fakeStream; + + beforeEach(function() { + fakeStream = { + videoType: "screen" + }; + }); + + it("should dispatch a ReceivingScreenShare action", function() { + session.trigger("streamDestroyed", {stream: fakeStream}); + + sinon.assert.calledOnce(dispatcher.dispatch); + sinon.assert.calledWithExactly(dispatcher.dispatch, + new sharedActions.ReceivingScreenShare({ + receiving: false + })); + }); + + it("should not dispatch an action if the videoType is camera", function() { + fakeStream.videoType = "camera"; + + session.trigger("streamDestroyed", {stream: fakeStream}); + + sinon.assert.notCalled(dispatcher.dispatch); + }); + }); + + describe("streamPropertyChanged", function() { + var fakeStream = { + connection: { id: "fake" }, + videoType: "screen", + videoDimensions: { + width: 320, + height: 160 + } + }; + + it("should not dispatch a VideoDimensionsChanged action for other properties", function() { + session.trigger("streamPropertyChanged", { + stream: fakeStream, + changedProperty: STREAM_PROPERTIES.HAS_AUDIO + }); + session.trigger("streamPropertyChanged", { + stream: fakeStream, + changedProperty: STREAM_PROPERTIES.HAS_VIDEO + }); + + sinon.assert.notCalled(dispatcher.dispatch); + }); + + it("should dispatch a VideoDimensionsChanged action", function() { + session.connection = { + id: "localUser" + }; + session.trigger("streamPropertyChanged", { + stream: fakeStream, + changedProperty: STREAM_PROPERTIES.VIDEO_DIMENSIONS + }); + + sinon.assert.calledOnce(dispatcher.dispatch); + sinon.assert.calledWithMatch(dispatcher.dispatch, + sinon.match.hasOwn("name", "videoDimensionsChanged")); + }); }); describe("connectionCreated", function() { @@ -388,4 +604,46 @@ }); }); }); + + describe("Events (screenshare)", function() { + beforeEach(function() { + driver.connectSession(sessionData); + + driver.getScreenShareElementFunc = function() {}; + + driver.startScreenShare(new sharedActions.StartScreenShare()); + + sandbox.stub(dispatcher, "dispatch"); + }); + + describe("accessAllowed", function() { + it("should publish the stream", function() { + publisher.trigger("accessAllowed", fakeEvent); + + sinon.assert.calledOnce(session.publish); + }); + + it("should dispatch a `ScreenSharingState` action", function() { + publisher.trigger("accessAllowed", fakeEvent); + + sinon.assert.calledOnce(dispatcher.dispatch); + sinon.assert.calledWithExactly(dispatcher.dispatch, + new sharedActions.ScreenSharingState({ + state: SCREEN_SHARE_STATES.ACTIVE + })); + }); + }); + + describe("accessDenied", function() { + it("should dispatch a `ScreenShareState` action", function() { + publisher.trigger("accessDenied", fakeEvent); + + sinon.assert.calledOnce(dispatcher.dispatch); + sinon.assert.calledWithExactly(dispatcher.dispatch, + new sharedActions.ScreenSharingState({ + state: SCREEN_SHARE_STATES.INACTIVE + })); + }); + }); + }); }); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/test/shared/views_test.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/test/shared/views_test.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/test/shared/views_test.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/test/shared/views_test.js 2015-02-03 14:33:17.000000000 +0000 @@ -12,10 +12,11 @@ describe("loop.shared.views", function() { "use strict"; - var sharedModels = loop.shared.models, - sharedViews = loop.shared.views, - getReactElementByClass = TestUtils.findRenderedDOMComponentWithClass, - sandbox, fakeAudioXHR; + var sharedModels = loop.shared.models; + var sharedViews = loop.shared.views; + var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES; + var getReactElementByClass = TestUtils.findRenderedDOMComponentWithClass; + var sandbox, fakeAudioXHR, dispatcher; beforeEach(function() { sandbox = sinon.sandbox.create(); @@ -23,6 +24,10 @@ sandbox.stub(l10n, "get", function(x) { return "translated:" + x; }); + + dispatcher = new loop.Dispatcher(); + sandbox.stub(dispatcher, "dispatch"); + fakeAudioXHR = { open: sinon.spy(), send: function() {}, @@ -92,6 +97,76 @@ }); }); + describe("ScreenShareControlButton", function() { + it("should render a visible share button", function() { + var comp = TestUtils.renderIntoDocument( + React.createElement(sharedViews.ScreenShareControlButton, { + dispatcher: dispatcher, + visible: true, + state: SCREEN_SHARE_STATES.INACTIVE + })); + + expect(comp.getDOMNode().classList.contains("active")).eql(false); + expect(comp.getDOMNode().classList.contains("disabled")).eql(false); + }); + + it("should render a disabled share button when share is pending", function() { + var comp = TestUtils.renderIntoDocument( + React.createElement(sharedViews.ScreenShareControlButton, { + dispatcher: dispatcher, + visible: true, + state: SCREEN_SHARE_STATES.PENDING + })); + + expect(comp.getDOMNode().classList.contains("active")).eql(false); + expect(comp.getDOMNode().classList.contains("disabled")).eql(true); + }); + + it("should render an active share button", function() { + var comp = TestUtils.renderIntoDocument( + React.createElement(sharedViews.ScreenShareControlButton, { + dispatcher: dispatcher, + visible: true, + state: SCREEN_SHARE_STATES.ACTIVE + })); + + expect(comp.getDOMNode().classList.contains("active")).eql(true); + expect(comp.getDOMNode().classList.contains("disabled")).eql(false); + }); + + it("should dispatch a StartScreenShare action on click when the state is not active", + function() { + var comp = TestUtils.renderIntoDocument( + React.createElement(sharedViews.ScreenShareControlButton, { + dispatcher: dispatcher, + visible: true, + state: SCREEN_SHARE_STATES.INACTIVE + })); + + TestUtils.Simulate.click(comp.getDOMNode()); + + sinon.assert.calledOnce(dispatcher.dispatch); + sinon.assert.calledWithExactly(dispatcher.dispatch, + new sharedActions.StartScreenShare({})); + }); + + it("should dispatch a EndScreenShare action on click when the state is active", + function() { + var comp = TestUtils.renderIntoDocument( + React.createElement(sharedViews.ScreenShareControlButton, { + dispatcher: dispatcher, + visible: true, + state: SCREEN_SHARE_STATES.ACTIVE + })); + + TestUtils.Simulate.click(comp.getDOMNode()); + + sinon.assert.calledOnce(dispatcher.dispatch); + sinon.assert.calledWithExactly(dispatcher.dispatch, + new sharedActions.EndScreenShare({})); + }); + }); + describe("ConversationToolbar", function() { var hangup, publishStream; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/test/standalone/index.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/test/standalone/index.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/test/standalone/index.html 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/test/standalone/index.html 2015-02-03 14:33:17.000000000 +0000 @@ -41,7 +41,6 @@ - @@ -52,6 +51,7 @@ + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/ui/fake-mozLoop.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/ui/fake-mozLoop.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/ui/fake-mozLoop.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/ui/fake-mozLoop.js 2015-02-03 14:33:17.000000000 +0000 @@ -54,6 +54,7 @@ switch(pref) { // Ensure we skip FTE completely. case "gettingStarted.seen": + case "screenshare.enabled": return true; } }, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/ui/index.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/ui/index.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/ui/index.html 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/ui/index.html 2015-02-03 14:33:17.000000000 +0000 @@ -37,7 +37,6 @@ - @@ -48,6 +47,7 @@ + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/ui/ui-showcase.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/ui/ui-showcase.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/ui/ui-showcase.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/ui/ui-showcase.js 2015-02-03 14:33:17.000000000 +0000 @@ -567,6 +567,7 @@ React.createElement(DesktopRoomConversationView, { roomStore: roomStore, dispatcher: dispatcher, + mozLoop: navigator.mozLoop, roomState: ROOM_STATES.INIT}) ) ), @@ -577,6 +578,7 @@ React.createElement(DesktopRoomConversationView, { roomStore: roomStore, dispatcher: dispatcher, + mozLoop: navigator.mozLoop, roomState: ROOM_STATES.HAS_PARTICIPANTS}) ) ) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/ui/ui-showcase.jsx thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/ui/ui-showcase.jsx --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/loop/ui/ui-showcase.jsx 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/loop/ui/ui-showcase.jsx 2015-02-03 14:33:17.000000000 +0000 @@ -567,6 +567,7 @@
    @@ -577,6 +578,7 @@
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/nsBrowserGlue.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/nsBrowserGlue.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/nsBrowserGlue.js 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/nsBrowserGlue.js 2015-02-03 14:33:17.000000000 +0000 @@ -581,9 +581,6 @@ SignInToWebsiteUX.init(); } #endif -#ifdef NIGHTLY_BUILD - ShumwayUtils.init(); -#endif webrtcUI.init(); AboutHome.init(); SessionStore.init(); @@ -756,6 +753,12 @@ // With older versions of the extension installed, this load will fail // passively. aWindow.messageManager.loadFrameScript("resource://pdf.js/pdfjschildbootstrap.js", true); +#ifdef NIGHTLY_BUILD + // Registering Shumway bootstrap script the child processes. + aWindow.messageManager.loadFrameScript("chrome://shumway/content/bootstrap-content.js", true); + // Initializing Shumway (shall be run after child script registration). + ShumwayUtils.init(); +#endif #ifdef XP_WIN // For windows seven, initialize the jump list module. const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1"; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/browser/browser_475045.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/browser/browser_475045.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/browser/browser_475045.js 2015-01-25 22:24:25.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/browser/browser_475045.js 2015-02-03 14:33:18.000000000 +0000 @@ -7,15 +7,15 @@ getService(Ci.mozIJSSubScriptLoader); this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils); -function test() { +add_task(function* test() { // Make sure the bookmarks bar is visible and restore its state on cleanup. let toolbar = document.getElementById("PersonalToolbar"); ok(toolbar, "PersonalToolbar should not be null"); if (toolbar.collapsed) { - setToolbarVisibility(toolbar, true); + yield promiseSetToolbarVisibility(toolbar, true); registerCleanupFunction(function() { - setToolbarVisibility(toolbar, false); + return promiseSetToolbarVisibility(toolbar, false); }); } @@ -62,4 +62,4 @@ simulateDragDrop(effect, mimeType); }); }); -} +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/browser/browser_555547.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/browser/browser_555547.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/browser/browser_555547.js 2015-01-25 22:24:25.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/browser/browser_555547.js 2015-02-03 14:33:18.000000000 +0000 @@ -2,7 +2,7 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ -function test() { +add_task(function* test() { waitForExplicitFinish(); let sidebarBox = document.getElementById("sidebar-box"); @@ -12,7 +12,7 @@ let toolbar = document.getElementById("PersonalToolbar"); let wasCollapsed = toolbar.collapsed; if (wasCollapsed) - setToolbarVisibility(toolbar, true); + yield promiseSetToolbarVisibility(toolbar, true); let sidebar = document.getElementById("sidebar"); sidebar.addEventListener("load", function() { @@ -50,10 +50,10 @@ toggleSidebar(); if (wasCollapsed) - setToolbarVisibility(toolbar, false); + yield promiseSetToolbarVisibility(toolbar, false); finish(); }); }, true); toggleSidebar("viewBookmarksSidebar", true); -} +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/browser/browser_drag_bookmarks_on_toolbar.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/browser/browser_drag_bookmarks_on_toolbar.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/browser/browser_drag_bookmarks_on_toolbar.js 2015-01-25 22:24:25.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/browser/browser_drag_bookmarks_on_toolbar.js 2015-02-03 14:33:18.000000000 +0000 @@ -138,16 +138,6 @@ return [wrappedNode]; } -function afterToolbarTransition(callback) { - function listener(event) { - if (event.propertyName == "max-height") { - toolbar.removeEventListener("transitionend", listener); - callback(); - } - } - toolbar.addEventListener("transitionend", listener); -} - var gTests = [ //------------------------------------------------------------------------------ @@ -245,8 +235,7 @@ else { // Collapse the personal toolbar if needed. if (wasCollapsed) { - setToolbarVisibility(toolbar, false); - afterToolbarTransition(finish); + promiseSetToolbarVisibility(toolbar, false).then(finish); } else { finish(); } @@ -261,8 +250,7 @@ // Uncollapse the personal toolbar if needed. if (wasCollapsed) { - setToolbarVisibility(toolbar, true); - afterToolbarTransition(nextTest); + promiseSetToolbarVisibility(toolbar, true).then(nextTest); } else { nextTest(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/browser/browser.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/browser/browser.ini --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/browser/browser.ini 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/browser/browser.ini 2015-02-03 14:33:18.000000000 +0000 @@ -48,7 +48,7 @@ [browser_toolbar_migration.js] [browser_library_batch_delete.js] [browser_555547.js] -skip-if = e10s || (os == "win" && !debug) # Bug 1115276 +skip-if = e10s [browser_416459_cut.js] skip-if = e10s # Bug ?????? - clipboard operations don't seem to work in this test? [browser_library_downloads.js] diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/browser/browser_views_liveupdate.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/browser/browser_views_liveupdate.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/browser/browser_views_liveupdate.js 2015-01-25 22:24:25.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/browser/browser_views_liveupdate.js 2015-02-03 14:33:18.000000000 +0000 @@ -10,12 +10,17 @@ let wasCollapsed = toolbar.collapsed; function test() { - // Uncollapse the personal toolbar if needed. - if (wasCollapsed) - setToolbarVisibility(toolbar, true); - waitForExplicitFinish(); + // Uncollapse the personal toolbar if needed. + if (wasCollapsed) { + promiseSetToolbarVisibility(toolbar, true).then(openBookmarksSidebar); + } else { + openBookmarksSidebar(); + } +} + +function openBookmarksSidebar() { // Sanity checks. ok(PlacesUtils, "PlacesUtils in context"); ok(PlacesUIUtils, "PlacesUIUtils in context"); @@ -169,10 +174,11 @@ toggleSidebar("viewBookmarksSidebar", false); // Collapse the personal toolbar if needed. - if (wasCollapsed) - setToolbarVisibility(toolbar, false); - - finish(); + if (wasCollapsed) { + promiseSetToolbarVisibility(toolbar, false).then(finish); + } else { + finish(); + } } /** diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/browser/head.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/browser/head.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/browser/head.js 2015-01-25 22:24:25.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/browser/head.js 2015-02-03 14:33:18.000000000 +0000 @@ -272,42 +272,6 @@ return deferred.promise; } -/** - * Waits for all pending async statements on the default connection. - * - * @return {Promise} - * @resolves When all pending async statements finished. - * @rejects Never. - * - * @note The result is achieved by asynchronously executing a query requiring - * a write lock. Since all statements on the same connection are - * serialized, the end of this write operation means that all writes are - * complete. Note that WAL makes so that writers don't block readers, but - * this is a problem only across different connections. - */ -function promiseAsyncUpdates() -{ - let deferred = Promise.defer(); - - let db = DBConn(); - let begin = db.createAsyncStatement("BEGIN EXCLUSIVE"); - begin.executeAsync(); - begin.finalize(); - - let commit = db.createAsyncStatement("COMMIT"); - commit.executeAsync({ - handleResult: function () {}, - handleError: function () {}, - handleCompletion: function(aReason) - { - deferred.resolve(); - } - }); - commit.finalize(); - - return deferred.promise; -} - function promiseBookmarksNotification(notification, conditionFn) { info(`Waiting for ${notification}`); return new Promise((resolve, reject) => { @@ -359,3 +323,69 @@ }, 2000); }); } + +/** + * Makes the specified toolbar visible or invisible and returns a Promise object + * that is resolved when the toolbar has completed any animations associated + * with hiding or showing the toolbar. + * + * Note that this code assumes that changes to a toolbar's visibility trigger + * a transition on the max-height property of the toolbar element. + * Changes to this styling could cause the returned Promise object to be + * resolved too early or not at all. + * + * @param aToolbar + * The toolbar to update. + * @param aVisible + * True to make the toolbar visible, false to make it hidden. + * + * @return {Promise} + * @resolves Any animation associated with updating the toolbar's visibility has + * finished. + * @rejects Never. + */ +function promiseSetToolbarVisibility(aToolbar, aVisible, aCallback) { + return new Promise((resolve, reject) => { + function listener(event) { + if (event.propertyName == "max-height") { + aToolbar.removeEventListener("transitionend", listener); + resolve(); + } + } + + let transitionProperties = + window.getComputedStyle(aToolbar).transitionProperty.split(", "); + if (isToolbarVisible(aToolbar) != aVisible && + transitionProperties.some( + prop => prop == "max-height" || prop == "all" + )) { + // Just because max-height is a transitionable property doesn't mean + // a transition will be triggered, but it's more likely. + aToolbar.addEventListener("transitionend", listener); + setToolbarVisibility(aToolbar, aVisible); + return; + } + + // No animation to wait for + setToolbarVisibility(aToolbar, aVisible); + resolve(); + }); +} + +/** + * Helper function to determine if the given toolbar is in the visible + * state according to its autohide/collapsed attribute. + * + * @aToolbar The toolbar to query. + * + * @returns True if the relevant attribute on |aToolbar| indicates it is + * visible, false otherwise. + */ +function isToolbarVisible(aToolbar) { + let hidingAttribute = aToolbar.getAttribute("type") == "menubar" + ? "autohide" + : "collapsed"; + let hidingValue = aToolbar.getAttribute(hidingAttribute).toLowerCase(); + // Check for both collapsed="true" and collapsed="collapsed" + return hidingValue !== "true" && hidingValue !== hidingAttribute; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/unit/test_421483.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/unit/test_421483.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/unit/test_421483.js 2015-01-25 22:24:25.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/unit/test_421483.js 2015-02-03 14:33:18.000000000 +0000 @@ -24,7 +24,7 @@ let smartBookmarkItemIds = PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO); do_check_eq(smartBookmarkItemIds.length, 0); - do_log_info("check that pref has not been bumped up"); + do_print("check that pref has not been bumped up"); do_check_eq(Services.prefs.getIntPref("browser.places.smartBookmarksVersion"), -1); }); @@ -34,7 +34,7 @@ let smartBookmarkItemIds = PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO); do_check_neq(smartBookmarkItemIds.length, 0); - do_log_info("check that pref has been bumped up"); + do_print("check that pref has been bumped up"); do_check_true(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0); }); @@ -42,14 +42,14 @@ let smartBookmarkItemIds = PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO); let smartBookmarksCount = smartBookmarkItemIds.length; - do_log_info("remove one smart bookmark and restore"); + do_print("remove one smart bookmark and restore"); PlacesUtils.bookmarks.removeItem(smartBookmarkItemIds[0]); Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0); gluesvc.ensurePlacesDefaultQueriesInitialized(); smartBookmarkItemIds = PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO); do_check_eq(smartBookmarkItemIds.length, smartBookmarksCount); - do_log_info("check that pref has been bumped up"); + do_print("check that pref has been bumped up"); do_check_true(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0); }); @@ -57,7 +57,7 @@ let smartBookmarkItemIds = PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO); let smartBookmarksCount = smartBookmarkItemIds.length; - do_log_info("smart bookmark should be restored in place"); + do_print("smart bookmark should be restored in place"); let parent = PlacesUtils.bookmarks.getFolderIdForItem(smartBookmarkItemIds[0]); let oldTitle = PlacesUtils.bookmarks.getItemTitle(smartBookmarkItemIds[0]); // create a subfolder and move inside it @@ -76,6 +76,6 @@ do_check_eq(smartBookmarkItemIds.length, smartBookmarksCount); do_check_eq(PlacesUtils.bookmarks.getFolderIdForItem(smartBookmarkItemIds[0]), newParent); do_check_eq(PlacesUtils.bookmarks.getItemTitle(smartBookmarkItemIds[0]), oldTitle); - do_log_info("check that pref has been bumped up"); + do_print("check that pref has been bumped up"); do_check_true(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0); }); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/unit/test_browserGlue_prefs.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/unit/test_browserGlue_prefs.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/unit/test_browserGlue_prefs.js 2015-01-25 22:24:25.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/unit/test_browserGlue_prefs.js 2015-02-03 14:33:18.000000000 +0000 @@ -22,7 +22,7 @@ Services.obs.removeObserver(waitImport, "bookmarks-restore-success"); // Delay to test eventual smart bookmarks creation. do_execute_soon(function () { - promiseAsyncUpdates().then(aCallback); + PlacesTestUtils.promiseAsyncUpdates().then(aCallback); }); }, "bookmarks-restore-success", false); } @@ -63,7 +63,7 @@ function test_import() { - do_log_info("Import from bookmarks.html if importBookmarksHTML is true."); + do_print("Import from bookmarks.html if importBookmarksHTML is true."); remove_all_bookmarks(); // Sanity check: we should not have any bookmark on the toolbar. @@ -86,7 +86,7 @@ run_next_test(); }); // Force nsBrowserGlue::_initPlaces(). - do_log_info("Simulate Places init"); + do_print("Simulate Places init"); bg.QueryInterface(Ci.nsIObserver).observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_FORCE_PLACES_INIT); @@ -94,8 +94,8 @@ function test_import_noSmartBookmarks() { - do_log_info("import from bookmarks.html, but don't create smart bookmarks \ - if they are disabled"); + do_print("import from bookmarks.html, but don't create smart bookmarks \ + if they are disabled"); remove_all_bookmarks(); // Sanity check: we should not have any bookmark on the toolbar. @@ -119,7 +119,7 @@ run_next_test(); }); // Force nsBrowserGlue::_initPlaces(). - do_log_info("Simulate Places init"); + do_print("Simulate Places init"); bg.QueryInterface(Ci.nsIObserver).observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_FORCE_PLACES_INIT); @@ -127,8 +127,8 @@ function test_import_autoExport_updatedSmartBookmarks() { - do_log_info("Import from bookmarks.html, but don't create smart bookmarks \ - if autoExportHTML is true and they are at latest version"); + do_print("Import from bookmarks.html, but don't create smart bookmarks \ + if autoExportHTML is true and they are at latest version"); remove_all_bookmarks(); // Sanity check: we should not have any bookmark on the toolbar. @@ -154,7 +154,7 @@ run_next_test(); }); // Force nsBrowserGlue::_initPlaces() - do_log_info("Simulate Places init"); + do_print("Simulate Places init"); bg.QueryInterface(Ci.nsIObserver).observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_FORCE_PLACES_INIT); @@ -162,8 +162,8 @@ function test_import_autoExport_oldSmartBookmarks() { - do_log_info("Import from bookmarks.html, and create smart bookmarks if \ - autoExportHTML is true and they are not at latest version."); + do_print("Import from bookmarks.html, and create smart bookmarks if \ + autoExportHTML is true and they are not at latest version."); remove_all_bookmarks(); // Sanity check: we should not have any bookmark on the toolbar. @@ -190,7 +190,7 @@ run_next_test(); }); // Force nsBrowserGlue::_initPlaces() - do_log_info("Simulate Places init"); + do_print("Simulate Places init"); bg.QueryInterface(Ci.nsIObserver).observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_FORCE_PLACES_INIT); @@ -198,8 +198,8 @@ function test_restore() { - do_log_info("restore from default bookmarks.html if \ - restore_default_bookmarks is true."); + do_print("restore from default bookmarks.html if \ + restore_default_bookmarks is true."); remove_all_bookmarks(); // Sanity check: we should not have any bookmark on the toolbar. @@ -222,7 +222,7 @@ run_next_test(); }); // Force nsBrowserGlue::_initPlaces() - do_log_info("Simulate Places init"); + do_print("Simulate Places init"); bg.QueryInterface(Ci.nsIObserver).observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_FORCE_PLACES_INIT); @@ -231,8 +231,8 @@ function test_restore_import() { - do_log_info("setting both importBookmarksHTML and \ - restore_default_bookmarks should restore defaults."); + do_print("setting both importBookmarksHTML and \ + restore_default_bookmarks should restore defaults."); remove_all_bookmarks(); // Sanity check: we should not have any bookmark on the toolbar. @@ -257,7 +257,7 @@ run_next_test(); }); // Force nsBrowserGlue::_initPlaces() - do_log_info("Simulate Places init"); + do_print("Simulate Places init"); bg.QueryInterface(Ci.nsIObserver).observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_FORCE_PLACES_INIT); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/unit/test_browserGlue_smartBookmarks.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/unit/test_browserGlue_smartBookmarks.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/unit/test_browserGlue_smartBookmarks.js 2015-01-25 22:24:25.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/unit/test_browserGlue_smartBookmarks.js 2015-02-03 14:33:18.000000000 +0000 @@ -345,7 +345,7 @@ Services.obs.removeObserver(waitImport, "bookmarks-restore-success"); // Delay to test eventual smart bookmarks creation. do_execute_soon(function () { - promiseAsyncUpdates().then(aCallback); + PlacesTestUtils.promiseAsyncUpdates().then(aCallback); }); }, "bookmarks-restore-success", false); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/unit/test_browserGlue_urlbar_defaultbehavior_migration.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/unit/test_browserGlue_urlbar_defaultbehavior_migration.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/places/tests/unit/test_browserGlue_urlbar_defaultbehavior_migration.js 2015-01-25 22:24:25.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/places/tests/unit/test_browserGlue_urlbar_defaultbehavior_migration.js 2015-02-03 14:33:18.000000000 +0000 @@ -38,7 +38,7 @@ }; add_task(function*() { - do_log_info("Migrate default.behavior = 0"); + do_print("Migrate default.behavior = 0"); setupBehaviorAndMigrate(0); Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"), @@ -52,7 +52,7 @@ }); add_task(function*() { - do_log_info("Migrate default.behavior = 1"); + do_print("Migrate default.behavior = 1"); setupBehaviorAndMigrate(1); Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"), @@ -66,7 +66,7 @@ }); add_task(function*() { - do_log_info("Migrate default.behavior = 2"); + do_print("Migrate default.behavior = 2"); setupBehaviorAndMigrate(2); Assert.equal(gGetBoolPref("browser.urlbar.suggest.history"), false, @@ -80,7 +80,7 @@ }); add_task(function*() { - do_log_info("Migrate default.behavior = 3"); + do_print("Migrate default.behavior = 3"); setupBehaviorAndMigrate(3); Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"), @@ -94,7 +94,7 @@ }); add_task(function*() { - do_log_info("Migrate default.behavior = 19"); + do_print("Migrate default.behavior = 19"); setupBehaviorAndMigrate(19); Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"), @@ -108,7 +108,7 @@ }); add_task(function*() { - do_log_info("Migrate default.behavior = 33"); + do_print("Migrate default.behavior = 33"); setupBehaviorAndMigrate(33); Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"), @@ -122,7 +122,7 @@ }); add_task(function*() { - do_log_info("Migrate default.behavior = 129"); + do_print("Migrate default.behavior = 129"); setupBehaviorAndMigrate(129); Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"), @@ -136,7 +136,7 @@ }); add_task(function*() { - do_log_info("Migrate default.behavior = 0, autocomplete.enabled = false"); + do_print("Migrate default.behavior = 0, autocomplete.enabled = false"); setupBehaviorAndMigrate(0, false); Assert.equal(gGetBoolPref("browser.urlbar.suggest.history"), false, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/preferences/advanced.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/preferences/advanced.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/preferences/advanced.js 2015-01-25 22:24:25.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/preferences/advanced.js 2015-02-03 14:33:18.000000000 +0000 @@ -31,6 +31,12 @@ } #ifdef MOZ_UPDATER + let onUnload = function () { + window.removeEventListener("unload", onUnload, false); + Services.prefs.removeObserver("app.update.", this); + }.bind(this); + window.addEventListener("unload", onUnload, false); + Services.prefs.addObserver("app.update.", this, false); this.updateReadPrefs(); #endif this.updateOfflineApps(); @@ -822,5 +828,15 @@ document.documentElement.openWindow("mozilla:devicemanager", "chrome://pippki/content/device_manager.xul", "", null); - } + }, + +#ifdef MOZ_UPDATER + observe: function (aSubject, aTopic, aData) { + switch(aTopic) { + case "nsPref:changed": + this.updateReadPrefs(); + break; + } + }, +#endif }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/preferences/content.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/preferences/content.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/preferences/content.js 2015-01-25 22:24:25.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/preferences/content.js 2015-02-03 14:33:18.000000000 +0000 @@ -4,12 +4,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gContentPane = { - - /** - * Initializes the fonts dropdowns displayed in this pane. - */ init: function () { + // Initializes the fonts dropdowns displayed in this pane. this._rebuildFonts(); var menulist = document.getElementById("defaultFont"); if (menulist.selectedIndex == -1) { @@ -23,6 +20,12 @@ let row = document.getElementById("translationBox"); row.removeAttribute("hidden"); } + + let drmInfoURL = + Services.urlFormatter.formatURLPref("app.support.baseURL") + "drm-content"; + document.getElementById("playDRMContentLink").setAttribute("href", drmInfoURL); + document.getElementById("playDRMContentRow").hidden = + !Services.prefs.getBoolPref("browser.eme.ui.enabled"); }, // UTILITY FUNCTIONS diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/preferences/content.xul thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/preferences/content.xul --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/components/preferences/content.xul 2015-01-25 22:24:25.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/components/preferences/content.xul 2015-02-03 14:33:18.000000000 +0000 @@ -22,6 +22,9 @@ + + + @@ -50,6 +53,15 @@ + + + + + + + - &runtimedetails_privilegesWarning; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/devtools/webide/content/webide.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/devtools/webide/content/webide.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/devtools/webide/content/webide.js 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/devtools/webide/content/webide.js 2015-02-03 14:33:22.000000000 +0000 @@ -640,7 +640,9 @@ } Task.spawn(function() { - if (project.type == "runtimeApp") { + // Do not force opening apps that are already running, as they may have + // some activity being opened and don't want to dismiss them. + if (project.type == "runtimeApp" && !AppManager.isProjectRunning()) { yield UI.busyUntil(AppManager.launchRuntimeApp(), "running app"); } yield UI.createToolbox(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/devtools/webide/Makefile.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/devtools/webide/Makefile.in --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/devtools/webide/Makefile.in 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/devtools/webide/Makefile.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -PREF_JS_EXPORTS = $(srcdir)/webide-prefs.js diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/devtools/webide/modules/app-manager.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/devtools/webide/modules/app-manager.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/devtools/webide/modules/app-manager.js 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/devtools/webide/modules/app-manager.js 2015-02-03 14:33:22.000000000 +0000 @@ -121,11 +121,11 @@ this._appsFront = front; this._listTabsResponse = response; this.update("list-tabs-response"); - return front.fetchIcons(); }) .then(() => { this.checkIfProjectIsRunning(); this.update("runtime-apps-found"); + front.fetchIcons(); }); } else { this._listTabsResponse = response; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/devtools/webide/modules/runtimes.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/devtools/webide/modules/runtimes.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/devtools/webide/modules/runtimes.js 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/devtools/webide/modules/runtimes.js 2015-02-03 14:33:22.000000000 +0000 @@ -447,9 +447,8 @@ if (!service) { return promise.reject(new Error("Can't find device: " + this.name)); } - connection.host = service.host; - connection.port = service.port; - connection.encryption = service.encryption; + connection.advertisement = service; + // TODO: Customize client authentication UX connection.connect(); return promise.resolve(); }, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/devtools/webide/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/devtools/webide/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/devtools/webide/moz.build 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/devtools/webide/moz.build 2015-02-03 14:33:22.000000000 +0000 @@ -23,3 +23,8 @@ 'modules/tab-store.js', 'modules/utils.js' ] + +JS_PREFERENCE_FILES += [ + 'webide-prefs.js', +] + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/devtools/webide/themes/runtimedetails.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/devtools/webide/themes/runtimedetails.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/devtools/webide/themes/runtimedetails.css 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/devtools/webide/themes/runtimedetails.css 2015-02-03 14:33:22.000000000 +0000 @@ -11,6 +11,11 @@ padding-left: 6px; } +#devtools-check > a { + color: #4C9ED9; + cursor: pointer; +} + .action { display: inline; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/devtools/webide/themes/webide.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/devtools/webide/themes/webide.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/devtools/webide/themes/webide.css 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/devtools/webide/themes/webide.css 2015-02-03 14:33:22.000000000 +0000 @@ -295,8 +295,10 @@ /* Toolbox */ #notificationbox[toolboxfullscreen] > .devtools-horizontal-splitter, -#notificationbox[toolboxfullscreen] > #deck { - display: none; +#notificationbox[toolboxfullscreen] > #deck, +#notificationbox[toolboxfullscreen] > #deck > iframe { + min-height: 0; + max-height: 0; } #notificationbox[toolboxfullscreen] > #toolbox { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/experiments/Experiments.jsm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/experiments/Experiments.jsm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/experiments/Experiments.jsm 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/experiments/Experiments.jsm 2015-02-03 14:33:22.000000000 +0000 @@ -26,8 +26,8 @@ "resource://gre/modules/AddonManager.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate", "resource://gre/modules/AddonManager.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "TelemetryPing", - "resource://gre/modules/TelemetryPing.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySession", + "resource://gre/modules/TelemetrySession.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "TelemetryLog", "resource://gre/modules/TelemetryLog.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils", @@ -323,7 +323,7 @@ }, telemetryPayload: function () { - return TelemetryPing.getPayload(); + return TelemetrySession.getPayload(); }, /** diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/experiments/test/xpcshell/test_conditions.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/experiments/test/xpcshell/test_conditions.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/experiments/test/xpcshell/test_conditions.js 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/experiments/test/xpcshell/test_conditions.js 2015-02-03 14:33:22.000000000 +0000 @@ -5,6 +5,7 @@ Cu.import("resource:///modules/experiments/Experiments.jsm"); +Cu.import("resource://gre/modules/TelemetrySession.jsm", this); const FILE_MANIFEST = "experiments.manifest"; const SEC_IN_ONE_DAY = 24 * 60 * 60; @@ -51,6 +52,7 @@ add_task(function* test_setup() { createAppInfo(); gProfileDir = do_get_profile(); + yield TelemetrySession.setup(); gPolicy = new Experiments.Policy(); gReporter = yield getReporter("json_payload_simple"); @@ -308,3 +310,7 @@ } } }); + +add_task(function* test_shutdown() { + yield TelemetrySession.shutdown(); +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/experiments/test/xpcshell/test_telemetry.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/experiments/test/xpcshell/test_telemetry.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/experiments/test/xpcshell/test_telemetry.js 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/experiments/test/xpcshell/test_telemetry.js 2015-02-03 14:33:22.000000000 +0000 @@ -103,8 +103,8 @@ // Test basic starting and stopping of experiments. add_task(function* test_telemetryBasics() { - // Check TelemetryLog instead of TelemetryPing.getPayload().log because - // TelemetryPing gets Experiments.instance() and side-effects log entries. + // Check TelemetryLog instead of TelemetrySession.getPayload().log because + // TelemetrySession gets Experiments.instance() and side-effects log entries. const OBSERVER_TOPIC = "experiments-changed"; let observerFireCount = 0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/pdfjs/content/build/pdf.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/pdfjs/content/build/pdf.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/pdfjs/content/build/pdf.js 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/pdfjs/content/build/pdf.js 2015-02-03 14:33:22.000000000 +0000 @@ -22,8 +22,8 @@ (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '1.0.1040'; -PDFJS.build = '997096f'; +PDFJS.version = '1.0.1130'; +PDFJS.build = 'e4f0ae2'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it @@ -341,6 +341,7 @@ case 'https': case 'ftp': case 'mailto': + case 'tel': return true; default: return false; @@ -470,6 +471,8 @@ function bytesToString(bytes) { + assert(bytes !== null && typeof bytes === 'object' && + bytes.length !== undefined, 'Invalid argument for bytesToString'); var length = bytes.length; var MAX_ARGUMENT_COUNT = 8192; if (length < MAX_ARGUMENT_COUNT) { @@ -485,6 +488,7 @@ } function stringToBytes(str) { + assert(typeof str === 'string', 'Invalid argument for stringToBytes'); var length = str.length; var bytes = new Uint8Array(length); for (var i = 0; i < length; ++i) { @@ -1374,6 +1378,9 @@ * Disable pre-fetching of PDF file data. When range requests are enabled PDF.js * will automatically keep fetching more data even if it isn't needed to display * the current page. This default behavior can be disabled. + * + * NOTE: It is also necessary to disable streaming, see above, + * in order for disabling of pre-fetching to work correctly. * @var {boolean} */ PDFJS.disableAutoFetch = (PDFJS.disableAutoFetch === undefined ? @@ -1437,7 +1444,9 @@ * * @typedef {Object} DocumentInitParameters * @property {string} url - The URL of the PDF. - * @property {TypedArray} data - A typed array with PDF data. + * @property {TypedArray|Array|string} data - Binary PDF data. Use typed arrays + * (Uint8Array) to improve the memory usage. If PDF data is BASE64-encoded, + * use atob() to convert it to a binary string first. * @property {Object} httpHeaders - Basic authentication headers. * @property {boolean} withCredentials - Indicates whether or not cross-site * Access-Control requests should be made using credentials such as cookies @@ -1446,6 +1455,9 @@ * @property {TypedArray} initialData - A typed array with the first portion or * all of the pdf data. Used by the extension since some data is already * loaded before the switch to range requests. + * @property {number} length - The PDF file length. It's used for progress + * reports and range requests operations. + * @property {PDFDataRangeTransport} range */ /** @@ -1462,69 +1474,227 @@ * is used, which means it must follow the same origin rules that any XHR does * e.g. No cross domain requests without CORS. * - * @param {string|TypedArray|DocumentInitParameters} source Can be a url to - * where a PDF is located, a typed array (Uint8Array) already populated with - * data or parameter object. + * @param {string|TypedArray|DocumentInitParameters|PDFDataRangeTransport} src + * Can be a url to where a PDF is located, a typed array (Uint8Array) + * already populated with data or parameter object. * - * @param {Object} pdfDataRangeTransport is optional. It is used if you want - * to manually serve range requests for data in the PDF. See viewer.js for - * an example of pdfDataRangeTransport's interface. + * @param {PDFDataRangeTransport} pdfDataRangeTransport (deprecated) It is used + * if you want to manually serve range requests for data in the PDF. * - * @param {function} passwordCallback is optional. It is used to request a + * @param {function} passwordCallback (deprecated) It is used to request a * password if wrong or no password was provided. The callback receives two * parameters: function that needs to be called with new password and reason * (see {PasswordResponses}). * - * @param {function} progressCallback is optional. It is used to be able to + * @param {function} progressCallback (deprecated) It is used to be able to * monitor the loading progress of the PDF file (necessary to implement e.g. * a loading bar). The callback receives an {Object} with the properties: * {number} loaded and {number} total. * - * @return {Promise} A promise that is resolved with {@link PDFDocumentProxy} - * object. + * @return {PDFDocumentLoadingTask} */ -PDFJS.getDocument = function getDocument(source, +PDFJS.getDocument = function getDocument(src, pdfDataRangeTransport, passwordCallback, progressCallback) { - var workerInitializedCapability, workerReadyCapability, transport; + var task = new PDFDocumentLoadingTask(); - if (typeof source === 'string') { - source = { url: source }; - } else if (isArrayBuffer(source)) { - source = { data: source }; - } else if (typeof source !== 'object') { - error('Invalid parameter in getDocument, need either Uint8Array, ' + - 'string or a parameter object'); - } + // Support of the obsolete arguments (for compatibility with API v1.0) + if (pdfDataRangeTransport) { + if (!(pdfDataRangeTransport instanceof PDFDataRangeTransport)) { + // Not a PDFDataRangeTransport instance, trying to add missing properties. + pdfDataRangeTransport = Object.create(pdfDataRangeTransport); + pdfDataRangeTransport.length = src.length; + pdfDataRangeTransport.initialData = src.initialData; + } + src = Object.create(src); + src.range = pdfDataRangeTransport; + } + task.onPassword = passwordCallback || null; + task.onProgress = progressCallback || null; + + var workerInitializedCapability, transport; + var source; + if (typeof src === 'string') { + source = { url: src }; + } else if (isArrayBuffer(src)) { + source = { data: src }; + } else if (src instanceof PDFDataRangeTransport) { + source = { range: src }; + } else { + if (typeof src !== 'object') { + error('Invalid parameter in getDocument, need either Uint8Array, ' + + 'string or a parameter object'); + } + if (!src.url && !src.data && !src.range) { + error('Invalid parameter object: need either .data, .range or .url'); + } - if (!source.url && !source.data) { - error('Invalid parameter array, need either .data or .url'); + source = src; } - // copy/use all keys as is except 'url' -- full path is required var params = {}; for (var key in source) { if (key === 'url' && typeof window !== 'undefined') { + // The full path is required in the 'url' field. params[key] = combineUrl(window.location.href, source[key]); continue; + } else if (key === 'range') { + continue; + } else if (key === 'data' && !(source[key] instanceof Uint8Array)) { + // Converting string or array-like data to Uint8Array. + var pdfBytes = source[key]; + if (typeof pdfBytes === 'string') { + params[key] = stringToBytes(pdfBytes); + } else if (typeof pdfBytes === 'object' && pdfBytes !== null && + !isNaN(pdfBytes.length)) { + params[key] = new Uint8Array(pdfBytes); + } else { + error('Invalid PDF binary data: either typed array, string or ' + + 'array-like object is expected in the data property.'); + } + continue; } params[key] = source[key]; } workerInitializedCapability = createPromiseCapability(); - workerReadyCapability = createPromiseCapability(); - transport = new WorkerTransport(workerInitializedCapability, - workerReadyCapability, pdfDataRangeTransport, - progressCallback); + transport = new WorkerTransport(workerInitializedCapability, source.range); workerInitializedCapability.promise.then(function transportInitialized() { - transport.passwordCallback = passwordCallback; - transport.fetchDocument(params); + transport.fetchDocument(task, params); }); - return workerReadyCapability.promise; + + return task; }; /** + * PDF document loading operation. + * @class + */ +var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() { + /** @constructs PDFDocumentLoadingTask */ + function PDFDocumentLoadingTask() { + this._capability = createPromiseCapability(); + + /** + * Callback to request a password if wrong or no password was provided. + * The callback receives two parameters: function that needs to be called + * with new password and reason (see {PasswordResponses}). + */ + this.onPassword = null; + + /** + * Callback to be able to monitor the loading progress of the PDF file + * (necessary to implement e.g. a loading bar). The callback receives + * an {Object} with the properties: {number} loaded and {number} total. + */ + this.onProgress = null; + } + + PDFDocumentLoadingTask.prototype = + /** @lends PDFDocumentLoadingTask.prototype */ { + /** + * @return {Promise} + */ + get promise() { + return this._capability.promise; + }, + + // TODO add cancel or abort method + + /** + * Registers callbacks to indicate the document loading completion. + * + * @param {function} onFulfilled The callback for the loading completion. + * @param {function} onRejected The callback for the loading failure. + * @return {Promise} A promise that is resolved after the onFulfilled or + * onRejected callback. + */ + then: function PDFDocumentLoadingTask_then(onFulfilled, onRejected) { + return this.promise.then.apply(this.promise, arguments); + } + }; + + return PDFDocumentLoadingTask; +})(); + +/** + * Abstract class to support range requests file loading. + * @class + */ +var PDFDataRangeTransport = (function pdfDataRangeTransportClosure() { + /** + * @constructs PDFDataRangeTransport + * @param {number} length + * @param {Uint8Array} initialData + */ + function PDFDataRangeTransport(length, initialData) { + this.length = length; + this.initialData = initialData; + + this._rangeListeners = []; + this._progressListeners = []; + this._progressiveReadListeners = []; + this._readyCapability = createPromiseCapability(); + } + PDFDataRangeTransport.prototype = + /** @lends PDFDataRangeTransport.prototype */ { + addRangeListener: + function PDFDataRangeTransport_addRangeListener(listener) { + this._rangeListeners.push(listener); + }, + + addProgressListener: + function PDFDataRangeTransport_addProgressListener(listener) { + this._progressListeners.push(listener); + }, + + addProgressiveReadListener: + function PDFDataRangeTransport_addProgressiveReadListener(listener) { + this._progressiveReadListeners.push(listener); + }, + + onDataRange: function PDFDataRangeTransport_onDataRange(begin, chunk) { + var listeners = this._rangeListeners; + for (var i = 0, n = listeners.length; i < n; ++i) { + listeners[i](begin, chunk); + } + }, + + onDataProgress: function PDFDataRangeTransport_onDataProgress(loaded) { + this._readyCapability.promise.then(function () { + var listeners = this._progressListeners; + for (var i = 0, n = listeners.length; i < n; ++i) { + listeners[i](loaded); + } + }.bind(this)); + }, + + onDataProgressiveRead: + function PDFDataRangeTransport_onDataProgress(chunk) { + this._readyCapability.promise.then(function () { + var listeners = this._progressiveReadListeners; + for (var i = 0, n = listeners.length; i < n; ++i) { + listeners[i](chunk); + } + }.bind(this)); + }, + + transportReady: function PDFDataRangeTransport_transportReady() { + this._readyCapability.resolve(); + }, + + requestDataRange: + function PDFDataRangeTransport_requestDataRange(begin, end) { + throw new Error('Abstract method PDFDataRangeTransport.requestDataRange'); + } + }; + return PDFDataRangeTransport; +})(); + +PDFJS.PDFDataRangeTransport = PDFDataRangeTransport; + +/** * Proxy to a PDFDocument in the worker thread. Also, contains commonly used * properties that can be read synchronously. * @class @@ -1639,7 +1809,7 @@ return this.transport.downloadInfoCapability.promise; }, /** - * @returns {Promise} A promise this is resolved with current stats about + * @return {Promise} A promise this is resolved with current stats about * document structures (see {@link PDFDocumentStats}). */ getStats: function PDFDocumentProxy_getStats() { @@ -1703,7 +1873,7 @@ * (default value is 'display'). * @property {Object} imageLayer - (optional) An object that has beginLayout, * endLayout and appendImage functions. - * @property {function} continueCallback - (optional) A function that will be + * @property {function} continueCallback - (deprecated) A function that will be * called each time the rendering is paused. To continue * rendering call the function that is the first argument * to the callback. @@ -1836,7 +2006,12 @@ intentState.renderTasks = []; } intentState.renderTasks.push(internalRenderTask); - var renderTask = new RenderTask(internalRenderTask); + var renderTask = internalRenderTask.task; + + // Obsolete parameter support + if (params.continueCallback) { + renderTask.onContinue = params.continueCallback; + } var self = this; intentState.displayReadyCapability.promise.then( @@ -2001,19 +2176,16 @@ * @ignore */ var WorkerTransport = (function WorkerTransportClosure() { - function WorkerTransport(workerInitializedCapability, workerReadyCapability, - pdfDataRangeTransport, progressCallback) { + function WorkerTransport(workerInitializedCapability, pdfDataRangeTransport) { this.pdfDataRangeTransport = pdfDataRangeTransport; - this.workerInitializedCapability = workerInitializedCapability; - this.workerReadyCapability = workerReadyCapability; - this.progressCallback = progressCallback; this.commonObjs = new PDFObjects(); + this.loadingTask = null; + this.pageCache = []; this.pagePromises = []; this.downloadInfoCapability = createPromiseCapability(); - this.passwordCallback = null; // If worker support isn't disabled explicit and the browser has worker // support, create a new web worker and test if it/the browser fullfills @@ -2152,48 +2324,50 @@ this.numPages = data.pdfInfo.numPages; var pdfDocument = new PDFDocumentProxy(pdfInfo, this); this.pdfDocument = pdfDocument; - this.workerReadyCapability.resolve(pdfDocument); + this.loadingTask._capability.resolve(pdfDocument); }, this); messageHandler.on('NeedPassword', function transportNeedPassword(exception) { - if (this.passwordCallback) { - return this.passwordCallback(updatePassword, - PasswordResponses.NEED_PASSWORD); + var loadingTask = this.loadingTask; + if (loadingTask.onPassword) { + return loadingTask.onPassword(updatePassword, + PasswordResponses.NEED_PASSWORD); } - this.workerReadyCapability.reject( + loadingTask._capability.reject( new PasswordException(exception.message, exception.code)); }, this); messageHandler.on('IncorrectPassword', function transportIncorrectPassword(exception) { - if (this.passwordCallback) { - return this.passwordCallback(updatePassword, - PasswordResponses.INCORRECT_PASSWORD); + var loadingTask = this.loadingTask; + if (loadingTask.onPassword) { + return loadingTask.onPassword(updatePassword, + PasswordResponses.INCORRECT_PASSWORD); } - this.workerReadyCapability.reject( + loadingTask._capability.reject( new PasswordException(exception.message, exception.code)); }, this); messageHandler.on('InvalidPDF', function transportInvalidPDF(exception) { - this.workerReadyCapability.reject( + this.loadingTask._capability.reject( new InvalidPDFException(exception.message)); }, this); messageHandler.on('MissingPDF', function transportMissingPDF(exception) { - this.workerReadyCapability.reject( + this.loadingTask._capability.reject( new MissingPDFException(exception.message)); }, this); messageHandler.on('UnexpectedResponse', function transportUnexpectedResponse(exception) { - this.workerReadyCapability.reject( + this.loadingTask._capability.reject( new UnexpectedResponseException(exception.message, exception.status)); }, this); messageHandler.on('UnknownError', function transportUnknownError(exception) { - this.workerReadyCapability.reject( + this.loadingTask._capability.reject( new UnknownErrorException(exception.message, exception.details)); }, this); @@ -2288,8 +2462,9 @@ }, this); messageHandler.on('DocProgress', function transportDocProgress(data) { - if (this.progressCallback) { - this.progressCallback({ + var loadingTask = this.loadingTask; + if (loadingTask.onProgress) { + loadingTask.onProgress({ loaded: data.loaded, total: data.total }); @@ -2349,10 +2524,16 @@ }); }, - fetchDocument: function WorkerTransport_fetchDocument(source) { + fetchDocument: function WorkerTransport_fetchDocument(loadingTask, source) { + this.loadingTask = loadingTask; + source.disableAutoFetch = PDFJS.disableAutoFetch; source.disableStream = PDFJS.disableStream; source.chunkedViewerLoading = !!this.pdfDataRangeTransport; + if (this.pdfDataRangeTransport) { + source.length = this.pdfDataRangeTransport.length; + source.initialData = this.pdfDataRangeTransport.initialData; + } this.messageHandler.send('GetDocRequest', { source: source, disableRange: PDFJS.disableRange, @@ -2563,26 +2744,37 @@ */ var RenderTask = (function RenderTaskClosure() { function RenderTask(internalRenderTask) { - this.internalRenderTask = internalRenderTask; + this._internalRenderTask = internalRenderTask; + /** - * Promise for rendering task completion. - * @type {Promise} + * Callback for incremental rendering -- a function that will be called + * each time the rendering is paused. To continue rendering call the + * function that is the first argument to the callback. + * @type {function} */ - this.promise = this.internalRenderTask.capability.promise; + this.onContinue = null; } RenderTask.prototype = /** @lends RenderTask.prototype */ { /** + * Promise for rendering task completion. + * @return {Promise} + */ + get promise() { + return this._internalRenderTask.capability.promise; + }, + + /** * Cancels the rendering task. If the task is currently rendering it will * not be cancelled until graphics pauses with a timeout. The promise that * this object extends will resolved when cancelled. */ cancel: function RenderTask_cancel() { - this.internalRenderTask.cancel(); + this._internalRenderTask.cancel(); }, /** - * Registers callback to indicate the rendering task completion. + * Registers callbacks to indicate the rendering task completion. * * @param {function} onFulfilled The callback for the rendering completion. * @param {function} onRejected The callback for the rendering failure. @@ -2590,7 +2782,7 @@ * onRejected callback. */ then: function RenderTask_then(onFulfilled, onRejected) { - return this.promise.then(onFulfilled, onRejected); + return this.promise.then.apply(this.promise, arguments); } }; @@ -2617,6 +2809,7 @@ this.graphicsReady = false; this.cancelled = false; this.capability = createPromiseCapability(); + this.task = new RenderTask(this); // caching this-bound methods this._continueBound = this._continue.bind(this); this._scheduleNextBound = this._scheduleNext.bind(this); @@ -2679,8 +2872,8 @@ if (this.cancelled) { return; } - if (this.params.continueCallback) { - this.params.continueCallback(this._scheduleNextBound); + if (this.task.onContinue) { + this.task.onContinue.call(this.task, this._scheduleNextBound); } else { this._scheduleNext(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/pdfjs/content/build/pdf.worker.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/pdfjs/content/build/pdf.worker.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/pdfjs/content/build/pdf.worker.js 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/pdfjs/content/build/pdf.worker.js 2015-02-03 14:33:22.000000000 +0000 @@ -22,8 +22,8 @@ (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '1.0.1040'; -PDFJS.build = '997096f'; +PDFJS.version = '1.0.1130'; +PDFJS.build = 'e4f0ae2'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it @@ -341,6 +341,7 @@ case 'https': case 'ftp': case 'mailto': + case 'tel': return true; default: return false; @@ -470,6 +471,8 @@ function bytesToString(bytes) { + assert(bytes !== null && typeof bytes === 'object' && + bytes.length !== undefined, 'Invalid argument for bytesToString'); var length = bytes.length; var MAX_ARGUMENT_COUNT = 8192; if (length < MAX_ARGUMENT_COUNT) { @@ -485,6 +488,7 @@ } function stringToBytes(str) { + assert(typeof str === 'string', 'Invalid argument for stringToBytes'); var length = str.length; var bytes = new Uint8Array(length); for (var i = 0; i < length; ++i) { @@ -1449,6 +1453,9 @@ getUint16: function ChunkedStream_getUint16() { var b0 = this.getByte(); var b1 = this.getByte(); + if (b0 === -1 || b1 === -1) { + return -1; + } return (b0 << 8) + b1; }, @@ -14056,7 +14063,7 @@ '63731': 0x23A9, // braceleftbt (0xF8F3) '63740': 0x23AB, // bracerighttp (0xF8FC) '63741': 0x23AC, // bracerightmid (0xF8FD) - '63742': 0x23AD, // bracerightmid (0xF8FE) + '63742': 0x23AD, // bracerightbt (0xF8FE) '63726': 0x23A1, // bracketlefttp (0xF8EE) '63727': 0x23A2, // bracketleftex (0xF8EF) '63728': 0x23A3, // bracketleftbt (0xF8F0) @@ -15996,7 +16003,7 @@ // to be used with the canvas.font. var fontName = name.replace(/[,_]/g, '-'); var isStandardFont = !!stdFontMap[fontName] || - (nonStdFontMap[fontName] && !!stdFontMap[nonStdFontMap[fontName]]); + !!(nonStdFontMap[fontName] && stdFontMap[nonStdFontMap[fontName]]); fontName = stdFontMap[fontName] || nonStdFontMap[fontName] || fontName; this.bold = (fontName.search(/bold/gi) !== -1); @@ -29596,6 +29603,102 @@ return ((stream.pos - 4) - startPos); }, /** + * Find the EOI (end-of-image) marker 0xFFD9 of the stream. + * @returns {number} The inline stream length. + */ + findDCTDecodeInlineStreamEnd: + function Parser_findDCTDecodeInlineStreamEnd(stream) { + var startPos = stream.pos, foundEOI = false, b, markerLength, length; + while ((b = stream.getByte()) !== -1) { + if (b !== 0xFF) { // Not a valid marker. + continue; + } + switch (stream.getByte()) { + case 0x00: // Byte stuffing. + // 0xFF00 appears to be a very common byte sequence in JPEG images. + break; + + case 0xFF: // Fill byte. + // Avoid skipping a valid marker, resetting the stream position. + stream.skip(-1); + break; + + case 0xD9: // EOI + foundEOI = true; + break; + + case 0xC0: // SOF0 + case 0xC1: // SOF1 + case 0xC2: // SOF2 + case 0xC3: // SOF3 + + case 0xC5: // SOF5 + case 0xC6: // SOF6 + case 0xC7: // SOF7 + + case 0xC9: // SOF9 + case 0xCA: // SOF10 + case 0xCB: // SOF11 + + case 0xCD: // SOF13 + case 0xCE: // SOF14 + case 0xCF: // SOF15 + + case 0xC4: // DHT + case 0xCC: // DAC + + case 0xDA: // SOS + case 0xDB: // DQT + case 0xDC: // DNL + case 0xDD: // DRI + case 0xDE: // DHP + case 0xDF: // EXP + + case 0xE0: // APP0 + case 0xE1: // APP1 + case 0xE2: // APP2 + case 0xE3: // APP3 + case 0xE4: // APP4 + case 0xE5: // APP5 + case 0xE6: // APP6 + case 0xE7: // APP7 + case 0xE8: // APP8 + case 0xE9: // APP9 + case 0xEA: // APP10 + case 0xEB: // APP11 + case 0xEC: // APP12 + case 0xED: // APP13 + case 0xEE: // APP14 + case 0xEF: // APP15 + + case 0xFE: // COM + // The marker should be followed by the length of the segment. + markerLength = stream.getUint16(); + if (markerLength > 2) { + // |markerLength| contains the byte length of the marker segment, + // including its own length (2 bytes) and excluding the marker. + stream.skip(markerLength - 2); // Jump to the next marker. + } else { + // The marker length is invalid, resetting the stream position. + stream.skip(-2); + } + break; + } + if (foundEOI) { + break; + } + } + length = stream.pos - startPos; + if (b === -1) { + warn('Inline DCTDecode image stream: ' + + 'EOI marker not found, searching for /EI/ instead.'); + stream.skip(-length); // Reset the stream position. + return this.findDefaultInlineStreamEnd(stream); + } + this.inlineStreamSkipEI(stream); + return length; + }, + /** * Find the EOD (end-of-data) marker '~>' (i.e. TILDE + GT) of the stream. * @returns {number} The inline stream length. */ @@ -29686,7 +29789,9 @@ // Parse image stream. var startPos = stream.pos, length, i, ii; - if (filterName === 'ASCII85Decide' || filterName === 'A85') { + if (filterName === 'DCTDecode' || filterName === 'DCT') { + length = this.findDCTDecodeInlineStreamEnd(stream); + } else if (filterName === 'ASCII85Decide' || filterName === 'A85') { length = this.findASCII85DecodeInlineStreamEnd(stream); } else if (filterName === 'ASCIIHexDecode' || filterName === 'AHx') { length = this.findASCIIHexDecodeInlineStreamEnd(stream); @@ -30613,6 +30718,9 @@ getUint16: function Stream_getUint16() { var b0 = this.getByte(); var b1 = this.getByte(); + if (b0 === -1 || b1 === -1) { + return -1; + } return (b0 << 8) + b1; }, getInt32: function Stream_getInt32() { @@ -30740,6 +30848,9 @@ getUint16: function DecodeStream_getUint16() { var b0 = this.getByte(); var b1 = this.getByte(); + if (b0 === -1 || b1 === -1) { + return -1; + } return (b0 << 8) + b1; }, getInt32: function DecodeStream_getInt32() { @@ -34839,11 +34950,6 @@ context.QCC = []; context.COC = []; break; - case 0xFF55: // Tile-part lengths, main header (TLM) - var Ltlm = readUint16(data, position); // Marker segment length - // Skip tile length markers - position += Ltlm; - break; case 0xFF5C: // Quantization default (QCD) length = readUint16(data, position); var qcd = {}; @@ -35033,6 +35139,9 @@ length = tile.dataEnd - position; parseTilePackets(context, data, position, length); break; + case 0xFF55: // Tile-part lengths, main header (TLM) + case 0xFF57: // Packet length, main header (PLM) + case 0xFF58: // Packet length, tile-part header (PLT) case 0xFF64: // Comment (COM) length = readUint16(data, position); // skipping content @@ -35373,7 +35482,7 @@ r = 0; c = 0; p = 0; - + this.nextPacket = function JpxImage_nextPacket() { // Section B.12.1.3 Resolution-position-component-layer for (; r <= maxDecompositionLevelsCount; r++) { @@ -35457,7 +35566,7 @@ var componentsCount = siz.Csiz; var precinctsSizes = getPrecinctSizesInImageScale(tile); var l = 0, r = 0, c = 0, px = 0, py = 0; - + this.nextPacket = function JpxImage_nextPacket() { // Section B.12.1.5 Component-position-resolution-layer for (; c < componentsCount; ++c) { @@ -37030,10 +37139,9 @@ // At each pixel: Clear contextLabel pixels that are shifted // out of the context, then add new ones. - // If j + n is out of range at the right image border, then - // the undefined value of bitmap[i - 2][j + n] is shifted to 0 contextLabel = ((contextLabel & OLD_PIXEL_MASK) << 1) | - (row2[j + 3] << 11) | (row1[j + 4] << 4) | pixel; + (j + 3 < width ? row2[j + 3] << 11 : 0) | + (j + 4 < width ? row1[j + 4] << 4 : 0) | pixel; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/pdfjs/content/PdfStreamConverter.jsm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/pdfjs/content/PdfStreamConverter.jsm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/pdfjs/content/PdfStreamConverter.jsm 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/pdfjs/content/PdfStreamConverter.jsm 2015-02-03 14:33:22.000000000 +0000 @@ -349,7 +349,11 @@ return (!!prefBrowser && prefGfx); }, supportsDocumentColors: function() { - return getBoolPref('browser.display.use_document_colors', true); + if (getIntPref('browser.display.document_color_use', 0) === 2 || + !getBoolPref('browser.display.use_document_colors', true)) { + return false; + } + return true; }, reportTelemetry: function (data) { var probeInfo = JSON.parse(data); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/pdfjs/content/web/viewer.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/pdfjs/content/web/viewer.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/pdfjs/content/web/viewer.css 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/pdfjs/content/web/viewer.css 2015-02-03 14:33:22.000000000 +0000 @@ -20,6 +20,7 @@ right: 0; bottom: 0; overflow: hidden; + opacity: 0.2; } .textLayer > div { @@ -55,6 +56,9 @@ background-color: rgb(0, 100, 0); } +.textLayer ::selection { background: rgb(0,0,255); } +.textLayer ::-moz-selection { background: rgb(0,0,255); } + .pdfViewer .canvasWrapper { overflow: hidden; } @@ -1153,9 +1157,13 @@ margin-bottom: 10px; } +#thumbnailView > a:last-of-type > .thumbnail:not([data-loaded]) { + margin-bottom: 9px; +} + .thumbnail:not([data-loaded]) { border: 1px dashed rgba(255, 255, 255, 0.5); - margin-bottom: 10px; + margin: -1px -1px 4px -1px; } .thumbnailImage { @@ -1164,6 +1172,8 @@ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3); opacity: 0.8; z-index: 99; + background-color: white; + background-clip: content-box; } .thumbnailSelectionRing { @@ -1294,19 +1304,12 @@ cursor: default; } - /* TODO: file FF bug to support ::-moz-selection:window-inactive so we can override the opaque grey background when the window is inactive; see https://bugzilla.mozilla.org/show_bug.cgi?id=706209 */ ::selection { background: rgba(0,0,255,0.3); } ::-moz-selection { background: rgba(0,0,255,0.3); } -.textLayer ::selection { background: rgb(0,0,255); } -.textLayer ::-moz-selection { background: rgb(0,0,255); } -.textLayer { - opacity: 0.2; -} - #errorWrapper { background: none repeat scroll 0 0 #FF5555; color: white; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/pdfjs/content/web/viewer.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/pdfjs/content/web/viewer.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/pdfjs/content/web/viewer.js 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/pdfjs/content/web/viewer.js 2015-02-03 14:33:22.000000000 +0000 @@ -16,10 +16,10 @@ */ /* globals PDFJS, PDFBug, FirefoxCom, Stats, Cache, ProgressBar, DownloadManager, getFileName, scrollIntoView, getPDFFileNameFromURL, - PDFHistory, Preferences, SidebarView, ViewHistory, PageView, + PDFHistory, Preferences, SidebarView, ViewHistory, Stats, PDFThumbnailViewer, URL, noContextMenuHandler, SecondaryToolbar, PasswordPrompt, PresentationMode, HandTool, Promise, - DocumentProperties, DocumentOutlineView, DocumentAttachmentsView, + DocumentProperties, PDFOutlineView, PDFAttachmentView, OverlayManager, PDFFindController, PDFFindBar, getVisibleElements, watchScroll, PDFViewer, PDFRenderingQueue, PresentationModeState, RenderingStates, DEFAULT_SCALE, UNKNOWN_SCALE, @@ -51,7 +51,6 @@ var MAX_AUTO_SCALE = 1.25; var SCROLLBAR_PADDING = 40; var VERTICAL_PADDING = 5; -var DEFAULT_CACHE_SIZE = 10; // optimised CSS custom property getter/setter var CustomStyle = (function CustomStyleClosure() { @@ -379,26 +378,6 @@ return ProgressBar; })(); -var Cache = function cacheCache(size) { - var data = []; - this.push = function cachePush(view) { - var i = data.indexOf(view); - if (i >= 0) { - data.splice(i, 1); - } - data.push(view); - if (data.length > size) { - data.shift().destroy(); - } - }; - this.resize = function (newSize) { - size = newSize; - while (data.length > size) { - data.shift().destroy(); - } - }; -}; - var DEFAULT_PREFERENCES = { @@ -931,6 +910,9 @@ FIND_PENDING: 3 }; +var FIND_SCROLL_OFFSET_TOP = -50; +var FIND_SCROLL_OFFSET_LEFT = -400; + /** * Provides "search" or "find" functionality for the PDF. * This object actually performs the search for a given string. @@ -1104,8 +1086,6 @@ }, updatePage: function PDFFindController_updatePage(index) { - var page = this.pdfViewer.getPageView(index); - if (this.selected.pageIdx === index) { // If the page is selected, scroll the page into view, which triggers // rendering the page, which adds the textLayer. Once the textLayer is @@ -1113,6 +1093,7 @@ this.pdfViewer.scrollPageIntoView(index + 1); } + var page = this.pdfViewer.getPageView(index); if (page.textLayer) { page.textLayer.updateMatches(); } @@ -1216,6 +1197,26 @@ } }, + /** + * The method is called back from the text layer when match presentation + * is updated. + * @param {number} pageIndex - page index. + * @param {number} index - match index. + * @param {Array} elements - text layer div elements array. + * @param {number} beginIdx - start index of the div array for the match. + * @param {number} endIdx - end index of the div array for the match. + */ + updateMatchPosition: function PDFFindController_updateMatchPosition( + pageIndex, index, elements, beginIdx, endIdx) { + if (this.selected.matchIdx === index && + this.selected.pageIdx === pageIndex) { + scrollIntoView(elements[beginIdx], { + top: FIND_SCROLL_OFFSET_TOP, + left: FIND_SCROLL_OFFSET_LEFT + }); + } + }, + nextPageMatch: function PDFFindController_nextPageMatch() { if (this.resumePageIdx !== null) { console.error('There can only be one pending page.'); @@ -2672,6 +2673,7 @@ }; var IGNORE_CURRENT_POSITION_ON_ZOOM = false; +var DEFAULT_CACHE_SIZE = 10; var CLEANUP_TIMEOUT = 30000; @@ -2822,7 +2824,10 @@ break; case RenderingStates.INITIAL: this.highestPriorityPage = view.renderingId; - view.draw(this.renderHighestPriority.bind(this)); + var continueRendering = function () { + this.renderHighestPriority(); + }.bind(this); + view.draw().then(continueRendering, continueRendering); break; } return true; @@ -2833,601 +2838,525 @@ })(); +var TEXT_LAYER_RENDER_DELAY = 200; // ms + /** - * @constructor - * @param {HTMLDivElement} container - The viewer element. - * @param {number} id - The page unique ID (normally its number). - * @param {number} scale - The page scale display. - * @param {PageViewport} defaultViewport - The page viewport. - * @param {IPDFLinkService} linkService - The navigation/linking service. - * @param {PDFRenderingQueue} renderingQueue - The rendering queue object. - * @param {Cache} cache - The page cache. - * @param {PDFPageSource} pageSource - * @param {PDFViewer} viewer - * - * @implements {IRenderableView} + * @typedef {Object} PDFPageViewOptions + * @property {HTMLDivElement} container - The viewer element. + * @property {number} id - The page unique ID (normally its number). + * @property {number} scale - The page scale display. + * @property {PageViewport} defaultViewport - The page viewport. + * @property {PDFRenderingQueue} renderingQueue - The rendering queue object. + * @property {IPDFTextLayerFactory} textLayerFactory + * @property {IPDFAnnotationsLayerFactory} annotationsLayerFactory */ -var PageView = function pageView(container, id, scale, defaultViewport, - linkService, renderingQueue, cache, - pageSource, viewer) { - this.id = id; - this.renderingId = 'page' + id; - - this.rotation = 0; - this.scale = scale || 1.0; - this.viewport = defaultViewport; - this.pdfPageRotate = defaultViewport.rotation; - this.hasRestrictedScaling = false; - - this.linkService = linkService; - this.renderingQueue = renderingQueue; - this.cache = cache; - this.pageSource = pageSource; - this.viewer = viewer; - - this.renderingState = RenderingStates.INITIAL; - this.resume = null; - - this.textLayer = null; - - this.zoomLayer = null; - - this.annotationLayer = null; - - var anchor = document.createElement('a'); - anchor.name = '' + this.id; - - var div = this.el = document.createElement('div'); - div.id = 'pageContainer' + this.id; - div.className = 'page'; - div.style.width = Math.floor(this.viewport.width) + 'px'; - div.style.height = Math.floor(this.viewport.height) + 'px'; - - container.appendChild(anchor); - container.appendChild(div); - - this.setPdfPage = function pageViewSetPdfPage(pdfPage) { - this.pdfPage = pdfPage; - this.pdfPageRotate = pdfPage.rotate; - var totalRotation = (this.rotation + this.pdfPageRotate) % 360; - this.viewport = pdfPage.getViewport(this.scale * CSS_UNITS, totalRotation); - this.stats = pdfPage.stats; - this.reset(); - }; - this.destroy = function pageViewDestroy() { - this.zoomLayer = null; - this.reset(); - if (this.pdfPage) { - this.pdfPage.destroy(); - } - }; +/** + * @class + * @implements {IRenderableView} + */ +var PDFPageView = (function PDFPageViewClosure() { + /** + * @constructs PDFPageView + * @param {PDFPageViewOptions} options + */ + function PDFPageView(options) { + var container = options.container; + var id = options.id; + var scale = options.scale; + var defaultViewport = options.defaultViewport; + var renderingQueue = options.renderingQueue; + var textLayerFactory = options.textLayerFactory; + var annotationsLayerFactory = options.annotationsLayerFactory; + + this.id = id; + this.renderingId = 'page' + id; + + this.rotation = 0; + this.scale = scale || 1.0; + this.viewport = defaultViewport; + this.pdfPageRotate = defaultViewport.rotation; + this.hasRestrictedScaling = false; + + this.renderingQueue = renderingQueue; + this.textLayerFactory = textLayerFactory; + this.annotationsLayerFactory = annotationsLayerFactory; - this.reset = function pageViewReset(keepAnnotations) { - if (this.renderTask) { - this.renderTask.cancel(); - } - this.resume = null; this.renderingState = RenderingStates.INITIAL; + this.resume = null; - div.style.width = Math.floor(this.viewport.width) + 'px'; - div.style.height = Math.floor(this.viewport.height) + 'px'; + this.onBeforeDraw = null; + this.onAfterDraw = null; - var childNodes = div.childNodes; - for (var i = div.childNodes.length - 1; i >= 0; i--) { - var node = childNodes[i]; - if ((this.zoomLayer && this.zoomLayer === node) || - (keepAnnotations && this.annotationLayer === node)) { - continue; - } - div.removeChild(node); - } - div.removeAttribute('data-loaded'); + this.textLayer = null; - if (keepAnnotations) { - if (this.annotationLayer) { - // Hide annotationLayer until all elements are resized - // so they are not displayed on the already-resized page - this.annotationLayer.setAttribute('hidden', 'true'); - } - } else { - this.annotationLayer = null; - } + this.zoomLayer = null; - if (this.canvas) { - // Zeroing the width and height causes Firefox to release graphics - // resources immediately, which can greatly reduce memory consumption. - this.canvas.width = 0; - this.canvas.height = 0; - delete this.canvas; - } + this.annotationLayer = null; - this.loadingIconDiv = document.createElement('div'); - this.loadingIconDiv.className = 'loadingIcon'; - div.appendChild(this.loadingIconDiv); - }; + var div = document.createElement('div'); + div.id = 'pageContainer' + this.id; + div.className = 'page'; + div.style.width = Math.floor(this.viewport.width) + 'px'; + div.style.height = Math.floor(this.viewport.height) + 'px'; + this.el = div; // TODO replace 'el' property usage + this.div = div; - this.update = function pageViewUpdate(scale, rotation) { - this.scale = scale || this.scale; + container.appendChild(div); + } - if (typeof rotation !== 'undefined') { - this.rotation = rotation; - } + PDFPageView.prototype = { + setPdfPage: function PDFPageView_setPdfPage(pdfPage) { + this.pdfPage = pdfPage; + this.pdfPageRotate = pdfPage.rotate; + var totalRotation = (this.rotation + this.pdfPageRotate) % 360; + this.viewport = pdfPage.getViewport(this.scale * CSS_UNITS, + totalRotation); + this.stats = pdfPage.stats; + this.reset(); + }, - var totalRotation = (this.rotation + this.pdfPageRotate) % 360; - this.viewport = this.viewport.clone({ - scale: this.scale * CSS_UNITS, - rotation: totalRotation - }); + destroy: function PDFPageView_destroy() { + this.zoomLayer = null; + this.reset(); + if (this.pdfPage) { + this.pdfPage.destroy(); + } + }, - var isScalingRestricted = false; - if (this.canvas && PDFJS.maxCanvasPixels > 0) { - var ctx = this.canvas.getContext('2d'); - var outputScale = getOutputScale(ctx); - var pixelsInViewport = this.viewport.width * this.viewport.height; - var maxScale = Math.sqrt(PDFJS.maxCanvasPixels / pixelsInViewport); - if (((Math.floor(this.viewport.width) * outputScale.sx) | 0) * - ((Math.floor(this.viewport.height) * outputScale.sy) | 0) > - PDFJS.maxCanvasPixels) { - isScalingRestricted = true; + reset: function PDFPageView_reset(keepAnnotations) { + if (this.renderTask) { + this.renderTask.cancel(); } - } + this.resume = null; + this.renderingState = RenderingStates.INITIAL; - if (this.canvas && - (PDFJS.useOnlyCssZoom || - (this.hasRestrictedScaling && isScalingRestricted))) { - this.cssTransform(this.canvas, true); - return; - } else if (this.canvas && !this.zoomLayer) { - this.zoomLayer = this.canvas.parentNode; - this.zoomLayer.style.position = 'absolute'; - } - if (this.zoomLayer) { - this.cssTransform(this.zoomLayer.firstChild); - } - this.reset(true); - }; + var div = this.div; + div.style.width = Math.floor(this.viewport.width) + 'px'; + div.style.height = Math.floor(this.viewport.height) + 'px'; - this.cssTransform = function pageCssTransform(canvas, redrawAnnotations) { - // Scale canvas, canvas wrapper, and page container. - var width = this.viewport.width; - var height = this.viewport.height; - canvas.style.width = canvas.parentNode.style.width = div.style.width = - Math.floor(width) + 'px'; - canvas.style.height = canvas.parentNode.style.height = div.style.height = - Math.floor(height) + 'px'; - // The canvas may have been originally rotated, so rotate relative to that. - var relativeRotation = this.viewport.rotation - canvas._viewport.rotation; - var absRotation = Math.abs(relativeRotation); - var scaleX = 1, scaleY = 1; - if (absRotation === 90 || absRotation === 270) { - // Scale x and y because of the rotation. - scaleX = height / width; - scaleY = width / height; - } - var cssTransform = 'rotate(' + relativeRotation + 'deg) ' + - 'scale(' + scaleX + ',' + scaleY + ')'; - CustomStyle.setProp('transform', canvas, cssTransform); - - if (this.textLayer) { - // Rotating the text layer is more complicated since the divs inside the - // the text layer are rotated. - // TODO: This could probably be simplified by drawing the text layer in - // one orientation then rotating overall. - var textLayerViewport = this.textLayer.viewport; - var textRelativeRotation = this.viewport.rotation - - textLayerViewport.rotation; - var textAbsRotation = Math.abs(textRelativeRotation); - var scale = width / textLayerViewport.width; - if (textAbsRotation === 90 || textAbsRotation === 270) { - scale = width / textLayerViewport.height; - } - var textLayerDiv = this.textLayer.textLayerDiv; - var transX, transY; - switch (textAbsRotation) { - case 0: - transX = transY = 0; - break; - case 90: - transX = 0; - transY = '-' + textLayerDiv.style.height; - break; - case 180: - transX = '-' + textLayerDiv.style.width; - transY = '-' + textLayerDiv.style.height; - break; - case 270: - transX = '-' + textLayerDiv.style.width; - transY = 0; - break; - default: - console.error('Bad rotation value.'); - break; + var childNodes = div.childNodes; + var currentZoomLayer = this.zoomLayer || null; + var currentAnnotationNode = (keepAnnotations && this.annotationLayer && + this.annotationLayer.div) || null; + for (var i = childNodes.length - 1; i >= 0; i--) { + var node = childNodes[i]; + if (currentZoomLayer === node || currentAnnotationNode === node) { + continue; + } + div.removeChild(node); } - CustomStyle.setProp('transform', textLayerDiv, - 'rotate(' + textAbsRotation + 'deg) ' + - 'scale(' + scale + ', ' + scale + ') ' + - 'translate(' + transX + ', ' + transY + ')'); - CustomStyle.setProp('transformOrigin', textLayerDiv, '0% 0%'); - } + div.removeAttribute('data-loaded'); - if (redrawAnnotations && this.annotationLayer) { - setupAnnotations(div, this.pdfPage, this.viewport); - } - }; + if (keepAnnotations) { + if (this.annotationLayer) { + // Hide annotationLayer until all elements are resized + // so they are not displayed on the already-resized page + this.annotationLayer.hide(); + } + } else { + this.annotationLayer = null; + } - Object.defineProperty(this, 'width', { - get: function PageView_getWidth() { - return this.viewport.width; - }, - enumerable: true - }); + if (this.canvas) { + // Zeroing the width and height causes Firefox to release graphics + // resources immediately, which can greatly reduce memory consumption. + this.canvas.width = 0; + this.canvas.height = 0; + delete this.canvas; + } - Object.defineProperty(this, 'height', { - get: function PageView_getHeight() { - return this.viewport.height; + this.loadingIconDiv = document.createElement('div'); + this.loadingIconDiv.className = 'loadingIcon'; + div.appendChild(this.loadingIconDiv); }, - enumerable: true - }); - var self = this; + update: function PDFPageView_update(scale, rotation) { + this.scale = scale || this.scale; - function setupAnnotations(pageDiv, pdfPage, viewport) { - - function bindLink(link, dest) { - link.href = linkService.getDestinationHash(dest); - link.onclick = function pageViewSetupLinksOnclick() { - if (dest) { - linkService.navigateTo(dest); - } - return false; - }; - if (dest) { - link.className = 'internalLink'; + if (typeof rotation !== 'undefined') { + this.rotation = rotation; } - } - function bindNamedAction(link, action) { - link.href = linkService.getAnchorUrl(''); - link.onclick = function pageViewSetupNamedActionOnClick() { - linkService.executeNamedAction(action); - return false; - }; - link.className = 'internalLink'; - } + var totalRotation = (this.rotation + this.pdfPageRotate) % 360; + this.viewport = this.viewport.clone({ + scale: this.scale * CSS_UNITS, + rotation: totalRotation + }); - pdfPage.getAnnotations().then(function(annotationsData) { - viewport = viewport.clone({ dontFlip: true }); - var transform = viewport.transform; - var transformStr = 'matrix(' + transform.join(',') + ')'; - var data, element, i, ii; - - if (self.annotationLayer) { - // If an annotationLayer already exists, refresh its children's - // transformation matrices - for (i = 0, ii = annotationsData.length; i < ii; i++) { - data = annotationsData[i]; - element = self.annotationLayer.querySelector( - '[data-annotation-id="' + data.id + '"]'); - if (element) { - CustomStyle.setProp('transform', element, transformStr); - } + var isScalingRestricted = false; + if (this.canvas && PDFJS.maxCanvasPixels > 0) { + var ctx = this.canvas.getContext('2d'); + var outputScale = getOutputScale(ctx); + var pixelsInViewport = this.viewport.width * this.viewport.height; + var maxScale = Math.sqrt(PDFJS.maxCanvasPixels / pixelsInViewport); + if (((Math.floor(this.viewport.width) * outputScale.sx) | 0) * + ((Math.floor(this.viewport.height) * outputScale.sy) | 0) > + PDFJS.maxCanvasPixels) { + isScalingRestricted = true; } - // See this.reset() - self.annotationLayer.removeAttribute('hidden'); - } else { - for (i = 0, ii = annotationsData.length; i < ii; i++) { - data = annotationsData[i]; - if (!data || !data.hasHtml) { - continue; - } + } - element = PDFJS.AnnotationUtils.getHtmlElement(data, - pdfPage.commonObjs); - element.setAttribute('data-annotation-id', data.id); - mozL10n.translate(element); - - var rect = data.rect; - var view = pdfPage.view; - rect = PDFJS.Util.normalizeRect([ - rect[0], - view[3] - rect[1] + view[1], - rect[2], - view[3] - rect[3] + view[1] - ]); - element.style.left = rect[0] + 'px'; - element.style.top = rect[1] + 'px'; - element.style.position = 'absolute'; - - CustomStyle.setProp('transform', element, transformStr); - var transformOriginStr = -rect[0] + 'px ' + -rect[1] + 'px'; - CustomStyle.setProp('transformOrigin', element, transformOriginStr); - - if (data.subtype === 'Link' && !data.url) { - var link = element.getElementsByTagName('a')[0]; - if (link) { - if (data.action) { - bindNamedAction(link, data.action); - } else { - bindLink(link, ('dest' in data) ? data.dest : null); - } - } - } + if (this.canvas && + (PDFJS.useOnlyCssZoom || + (this.hasRestrictedScaling && isScalingRestricted))) { + this.cssTransform(this.canvas, true); + return; + } else if (this.canvas && !this.zoomLayer) { + this.zoomLayer = this.canvas.parentNode; + this.zoomLayer.style.position = 'absolute'; + } + if (this.zoomLayer) { + this.cssTransform(this.zoomLayer.firstChild); + } + this.reset(true); + }, - if (!self.annotationLayer) { - var annotationLayerDiv = document.createElement('div'); - annotationLayerDiv.className = 'annotationLayer'; - pageDiv.appendChild(annotationLayerDiv); - self.annotationLayer = annotationLayerDiv; - } + /** + * Called when moved in the parent's container. + */ + updatePosition: function PDFPageView_updatePosition() { + if (this.textLayer) { + this.textLayer.render(TEXT_LAYER_RENDER_DELAY); + } + }, - self.annotationLayer.appendChild(element); + cssTransform: function PDFPageView_transform(canvas, redrawAnnotations) { + // Scale canvas, canvas wrapper, and page container. + var width = this.viewport.width; + var height = this.viewport.height; + var div = this.div; + canvas.style.width = canvas.parentNode.style.width = div.style.width = + Math.floor(width) + 'px'; + canvas.style.height = canvas.parentNode.style.height = div.style.height = + Math.floor(height) + 'px'; + // The canvas may have been originally rotated, rotate relative to that. + var relativeRotation = this.viewport.rotation - canvas._viewport.rotation; + var absRotation = Math.abs(relativeRotation); + var scaleX = 1, scaleY = 1; + if (absRotation === 90 || absRotation === 270) { + // Scale x and y because of the rotation. + scaleX = height / width; + scaleY = width / height; + } + var cssTransform = 'rotate(' + relativeRotation + 'deg) ' + + 'scale(' + scaleX + ',' + scaleY + ')'; + CustomStyle.setProp('transform', canvas, cssTransform); + + if (this.textLayer) { + // Rotating the text layer is more complicated since the divs inside the + // the text layer are rotated. + // TODO: This could probably be simplified by drawing the text layer in + // one orientation then rotating overall. + var textLayerViewport = this.textLayer.viewport; + var textRelativeRotation = this.viewport.rotation - + textLayerViewport.rotation; + var textAbsRotation = Math.abs(textRelativeRotation); + var scale = width / textLayerViewport.width; + if (textAbsRotation === 90 || textAbsRotation === 270) { + scale = width / textLayerViewport.height; + } + var textLayerDiv = this.textLayer.textLayerDiv; + var transX, transY; + switch (textAbsRotation) { + case 0: + transX = transY = 0; + break; + case 90: + transX = 0; + transY = '-' + textLayerDiv.style.height; + break; + case 180: + transX = '-' + textLayerDiv.style.width; + transY = '-' + textLayerDiv.style.height; + break; + case 270: + transX = '-' + textLayerDiv.style.width; + transY = 0; + break; + default: + console.error('Bad rotation value.'); + break; } + CustomStyle.setProp('transform', textLayerDiv, + 'rotate(' + textAbsRotation + 'deg) ' + + 'scale(' + scale + ', ' + scale + ') ' + + 'translate(' + transX + ', ' + transY + ')'); + CustomStyle.setProp('transformOrigin', textLayerDiv, '0% 0%'); } - }); - } - - this.getPagePoint = function pageViewGetPagePoint(x, y) { - return this.viewport.convertToPdfPoint(x, y); - }; - - this.draw = function pageviewDraw(callback) { - var pdfPage = this.pdfPage; - if (this.pagePdfPromise) { - return; - } - if (!pdfPage) { - var promise = this.pageSource.getPage(); - promise.then(function(pdfPage) { - delete this.pagePdfPromise; - this.setPdfPage(pdfPage); - this.draw(callback); - }.bind(this)); - this.pagePdfPromise = promise; - return; - } - - if (this.renderingState !== RenderingStates.INITIAL) { - console.error('Must be in new state before drawing'); - } - - this.renderingState = RenderingStates.RUNNING; + if (redrawAnnotations && this.annotationLayer) { + this.annotationLayer.setupAnnotations(this.viewport); + } + }, - var viewport = this.viewport; - // Wrap the canvas so if it has a css transform for highdpi the overflow - // will be hidden in FF. - var canvasWrapper = document.createElement('div'); - canvasWrapper.style.width = div.style.width; - canvasWrapper.style.height = div.style.height; - canvasWrapper.classList.add('canvasWrapper'); + get width() { + return this.viewport.width; + }, - var canvas = document.createElement('canvas'); - canvas.id = 'page' + this.id; - canvasWrapper.appendChild(canvas); - if (this.annotationLayer) { - // annotationLayer needs to stay on top - div.insertBefore(canvasWrapper, this.annotationLayer); - } else { - div.appendChild(canvasWrapper); - } - this.canvas = canvas; + get height() { + return this.viewport.height; + }, - var ctx = canvas.getContext('2d'); - var outputScale = getOutputScale(ctx); + getPagePoint: function PDFPageView_getPagePoint(x, y) { + return this.viewport.convertToPdfPoint(x, y); + }, - if (PDFJS.useOnlyCssZoom) { - var actualSizeViewport = viewport.clone({ scale: CSS_UNITS }); - // Use a scale that will make the canvas be the original intended size - // of the page. - outputScale.sx *= actualSizeViewport.width / viewport.width; - outputScale.sy *= actualSizeViewport.height / viewport.height; - outputScale.scaled = true; - } - - if (PDFJS.maxCanvasPixels > 0) { - var pixelsInViewport = viewport.width * viewport.height; - var maxScale = Math.sqrt(PDFJS.maxCanvasPixels / pixelsInViewport); - if (outputScale.sx > maxScale || outputScale.sy > maxScale) { - outputScale.sx = maxScale; - outputScale.sy = maxScale; - outputScale.scaled = true; - this.hasRestrictedScaling = true; - } else { - this.hasRestrictedScaling = false; - } - } + draw: function PDFPageView_draw() { + if (this.renderingState !== RenderingStates.INITIAL) { + console.error('Must be in new state before drawing'); + } + + this.renderingState = RenderingStates.RUNNING; + + var pdfPage = this.pdfPage; + var viewport = this.viewport; + var div = this.div; + // Wrap the canvas so if it has a css transform for highdpi the overflow + // will be hidden in FF. + var canvasWrapper = document.createElement('div'); + canvasWrapper.style.width = div.style.width; + canvasWrapper.style.height = div.style.height; + canvasWrapper.classList.add('canvasWrapper'); - canvas.width = (Math.floor(viewport.width) * outputScale.sx) | 0; - canvas.height = (Math.floor(viewport.height) * outputScale.sy) | 0; - canvas.style.width = Math.floor(viewport.width) + 'px'; - canvas.style.height = Math.floor(viewport.height) + 'px'; - // Add the viewport so it's known what it was originally drawn with. - canvas._viewport = viewport; - - var textLayerDiv = null; - var textLayer = null; - if (!PDFJS.disableTextLayer) { - textLayerDiv = document.createElement('div'); - textLayerDiv.className = 'textLayer'; - textLayerDiv.style.width = canvas.style.width; - textLayerDiv.style.height = canvas.style.height; + var canvas = document.createElement('canvas'); + canvas.id = 'page' + this.id; + canvasWrapper.appendChild(canvas); if (this.annotationLayer) { // annotationLayer needs to stay on top - div.insertBefore(textLayerDiv, this.annotationLayer); + div.insertBefore(canvasWrapper, this.annotationLayer.div); } else { - div.appendChild(textLayerDiv); + div.appendChild(canvasWrapper); } + this.canvas = canvas; - textLayer = this.viewer.createTextLayerBuilder(textLayerDiv, this.id - 1, - this.viewport); - } - this.textLayer = textLayer; - - // TODO(mack): use data attributes to store these - ctx._scaleX = outputScale.sx; - ctx._scaleY = outputScale.sy; - if (outputScale.scaled) { - ctx.scale(outputScale.sx, outputScale.sy); - } - - // Rendering area + var ctx = canvas.getContext('2d'); + var outputScale = getOutputScale(ctx); - var self = this; - function pageViewDrawCallback(error) { - // The renderTask may have been replaced by a new one, so only remove the - // reference to the renderTask if it matches the one that is triggering - // this callback. - if (renderTask === self.renderTask) { - self.renderTask = null; + if (PDFJS.useOnlyCssZoom) { + var actualSizeViewport = viewport.clone({ scale: CSS_UNITS }); + // Use a scale that will make the canvas be the original intended size + // of the page. + outputScale.sx *= actualSizeViewport.width / viewport.width; + outputScale.sy *= actualSizeViewport.height / viewport.height; + outputScale.scaled = true; } - if (error === 'cancelled') { - return; + if (PDFJS.maxCanvasPixels > 0) { + var pixelsInViewport = viewport.width * viewport.height; + var maxScale = Math.sqrt(PDFJS.maxCanvasPixels / pixelsInViewport); + if (outputScale.sx > maxScale || outputScale.sy > maxScale) { + outputScale.sx = maxScale; + outputScale.sy = maxScale; + outputScale.scaled = true; + this.hasRestrictedScaling = true; + } else { + this.hasRestrictedScaling = false; + } } - self.renderingState = RenderingStates.FINISHED; - - if (self.loadingIconDiv) { - div.removeChild(self.loadingIconDiv); - delete self.loadingIconDiv; - } + canvas.width = (Math.floor(viewport.width) * outputScale.sx) | 0; + canvas.height = (Math.floor(viewport.height) * outputScale.sy) | 0; + canvas.style.width = Math.floor(viewport.width) + 'px'; + canvas.style.height = Math.floor(viewport.height) + 'px'; + // Add the viewport so it's known what it was originally drawn with. + canvas._viewport = viewport; + + var textLayerDiv = null; + var textLayer = null; + if (this.textLayerFactory) { + textLayerDiv = document.createElement('div'); + textLayerDiv.className = 'textLayer'; + textLayerDiv.style.width = canvas.style.width; + textLayerDiv.style.height = canvas.style.height; + if (this.annotationLayer) { + // annotationLayer needs to stay on top + div.insertBefore(textLayerDiv, this.annotationLayer.div); + } else { + div.appendChild(textLayerDiv); + } - if (self.zoomLayer) { - div.removeChild(self.zoomLayer); - self.zoomLayer = null; + textLayer = this.textLayerFactory.createTextLayerBuilder(textLayerDiv, + this.id - 1, + this.viewport); } + this.textLayer = textLayer; - self.error = error; - self.stats = pdfPage.stats; - self.updateStats(); - if (self.onAfterDraw) { - self.onAfterDraw(); + // TODO(mack): use data attributes to store these + ctx._scaleX = outputScale.sx; + ctx._scaleY = outputScale.sy; + if (outputScale.scaled) { + ctx.scale(outputScale.sx, outputScale.sy); } - var event = document.createEvent('CustomEvent'); - event.initCustomEvent('pagerender', true, true, { - pageNumber: pdfPage.pageNumber + var resolveRenderPromise, rejectRenderPromise; + var promise = new Promise(function (resolve, reject) { + resolveRenderPromise = resolve; + rejectRenderPromise = reject; }); - div.dispatchEvent(event); - callback(); - } + // Rendering area - var renderContext = { - canvasContext: ctx, - viewport: this.viewport, - // intent: 'default', // === 'display' - continueCallback: function pdfViewcContinueCallback(cont) { - if (!self.renderingQueue.isHighestPriority(self)) { - self.renderingState = RenderingStates.PAUSED; - self.resume = function resumeCallback() { - self.renderingState = RenderingStates.RUNNING; - cont(); - }; + var self = this; + function pageViewDrawCallback(error) { + // The renderTask may have been replaced by a new one, so only remove + // the reference to the renderTask if it matches the one that is + // triggering this callback. + if (renderTask === self.renderTask) { + self.renderTask = null; + } + + if (error === 'cancelled') { + rejectRenderPromise(error); return; } - cont(); - } - }; - var renderTask = this.renderTask = this.pdfPage.render(renderContext); - this.renderTask.promise.then( - function pdfPageRenderCallback() { - pageViewDrawCallback(null); - if (textLayer) { - self.pdfPage.getTextContent().then( - function textContentResolved(textContent) { - textLayer.setTextContent(textContent); - } - ); + self.renderingState = RenderingStates.FINISHED; + + if (self.loadingIconDiv) { + div.removeChild(self.loadingIconDiv); + delete self.loadingIconDiv; } - }, - function pdfPageRenderError(error) { - pageViewDrawCallback(error); - } - ); - setupAnnotations(div, pdfPage, this.viewport); - div.setAttribute('data-loaded', true); + if (self.zoomLayer) { + div.removeChild(self.zoomLayer); + self.zoomLayer = null; + } - // Add the page to the cache at the start of drawing. That way it can be - // evicted from the cache and destroyed even if we pause its rendering. - cache.push(this); - }; + self.error = error; + self.stats = pdfPage.stats; + if (self.onAfterDraw) { + self.onAfterDraw(); + } + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('pagerendered', true, true, { + pageNumber: self.id + }); + div.dispatchEvent(event); - this.beforePrint = function pageViewBeforePrint() { - var pdfPage = this.pdfPage; + if (!error) { + resolveRenderPromise(undefined); + } else { + rejectRenderPromise(error); + } + } - var viewport = pdfPage.getViewport(1); - // Use the same hack we use for high dpi displays for printing to get better - // output until bug 811002 is fixed in FF. - var PRINT_OUTPUT_SCALE = 2; - var canvas = document.createElement('canvas'); - canvas.width = Math.floor(viewport.width) * PRINT_OUTPUT_SCALE; - canvas.height = Math.floor(viewport.height) * PRINT_OUTPUT_SCALE; - canvas.style.width = (PRINT_OUTPUT_SCALE * viewport.width) + 'pt'; - canvas.style.height = (PRINT_OUTPUT_SCALE * viewport.height) + 'pt'; - var cssScale = 'scale(' + (1 / PRINT_OUTPUT_SCALE) + ', ' + - (1 / PRINT_OUTPUT_SCALE) + ')'; - CustomStyle.setProp('transform' , canvas, cssScale); - CustomStyle.setProp('transformOrigin' , canvas, '0% 0%'); - - var printContainer = document.getElementById('printContainer'); - var canvasWrapper = document.createElement('div'); - canvasWrapper.style.width = viewport.width + 'pt'; - canvasWrapper.style.height = viewport.height + 'pt'; - canvasWrapper.appendChild(canvas); - printContainer.appendChild(canvasWrapper); - - canvas.mozPrintCallback = function(obj) { - var ctx = obj.context; - - ctx.save(); - ctx.fillStyle = 'rgb(255, 255, 255)'; - ctx.fillRect(0, 0, canvas.width, canvas.height); - ctx.restore(); - ctx.scale(PRINT_OUTPUT_SCALE, PRINT_OUTPUT_SCALE); + var renderContinueCallback = null; + if (this.renderingQueue) { + renderContinueCallback = function renderContinueCallback(cont) { + if (!self.renderingQueue.isHighestPriority(self)) { + self.renderingState = RenderingStates.PAUSED; + self.resume = function resumeCallback() { + self.renderingState = RenderingStates.RUNNING; + cont(); + }; + return; + } + cont(); + }; + } var renderContext = { canvasContext: ctx, - viewport: viewport, - intent: 'print' + viewport: this.viewport, + // intent: 'default', // === 'display' + continueCallback: renderContinueCallback }; + var renderTask = this.renderTask = this.pdfPage.render(renderContext); - pdfPage.render(renderContext).promise.then(function() { - // Tell the printEngine that rendering this canvas/page has finished. - obj.done(); - }, function(error) { - console.error(error); - // Tell the printEngine that rendering this canvas/page has failed. - // This will make the print proces stop. - if ('abort' in obj) { - obj.abort(); - } else { - obj.done(); + this.renderTask.promise.then( + function pdfPageRenderCallback() { + pageViewDrawCallback(null); + if (textLayer) { + self.pdfPage.getTextContent().then( + function textContentResolved(textContent) { + textLayer.setTextContent(textContent); + textLayer.render(TEXT_LAYER_RENDER_DELAY); + } + ); + } + }, + function pdfPageRenderError(error) { + pageViewDrawCallback(error); } - }); - }; - }; + ); - this.updateStats = function pageViewUpdateStats() { - if (!this.stats) { - return; - } + if (this.annotationsLayerFactory) { + if (!this.annotationLayer) { + this.annotationLayer = this.annotationsLayerFactory. + createAnnotationsLayerBuilder(div, this.pdfPage); + } + this.annotationLayer.setupAnnotations(this.viewport); + } + div.setAttribute('data-loaded', true); - if (PDFJS.pdfBug && Stats.enabled) { - var stats = this.stats; - Stats.add(this.id, stats); - } + if (self.onBeforeDraw) { + self.onBeforeDraw(); + } + return promise; + }, + + beforePrint: function PDFPageView_beforePrint() { + var pdfPage = this.pdfPage; + + var viewport = pdfPage.getViewport(1); + // Use the same hack we use for high dpi displays for printing to get + // better output until bug 811002 is fixed in FF. + var PRINT_OUTPUT_SCALE = 2; + var canvas = document.createElement('canvas'); + canvas.width = Math.floor(viewport.width) * PRINT_OUTPUT_SCALE; + canvas.height = Math.floor(viewport.height) * PRINT_OUTPUT_SCALE; + canvas.style.width = (PRINT_OUTPUT_SCALE * viewport.width) + 'pt'; + canvas.style.height = (PRINT_OUTPUT_SCALE * viewport.height) + 'pt'; + var cssScale = 'scale(' + (1 / PRINT_OUTPUT_SCALE) + ', ' + + (1 / PRINT_OUTPUT_SCALE) + ')'; + CustomStyle.setProp('transform' , canvas, cssScale); + CustomStyle.setProp('transformOrigin' , canvas, '0% 0%'); + + var printContainer = document.getElementById('printContainer'); + var canvasWrapper = document.createElement('div'); + canvasWrapper.style.width = viewport.width + 'pt'; + canvasWrapper.style.height = viewport.height + 'pt'; + canvasWrapper.appendChild(canvas); + printContainer.appendChild(canvasWrapper); + + canvas.mozPrintCallback = function(obj) { + var ctx = obj.context; + + ctx.save(); + ctx.fillStyle = 'rgb(255, 255, 255)'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.restore(); + ctx.scale(PRINT_OUTPUT_SCALE, PRINT_OUTPUT_SCALE); + + var renderContext = { + canvasContext: ctx, + viewport: viewport, + intent: 'print' + }; + + pdfPage.render(renderContext).promise.then(function() { + // Tell the printEngine that rendering this canvas/page has finished. + obj.done(); + }, function(error) { + console.error(error); + // Tell the printEngine that rendering this canvas/page has failed. + // This will make the print proces stop. + if ('abort' in obj) { + obj.abort(); + } else { + obj.done(); + } + }); + }; + }, }; -}; + + return PDFPageView; +})(); -var FIND_SCROLL_OFFSET_TOP = -50; -var FIND_SCROLL_OFFSET_LEFT = -400; var MAX_TEXT_DIVS_TO_RENDER = 100000; -var RENDER_DELAY = 200; // ms var NonWhitespaceRegexp = /\S/; @@ -3440,9 +3369,6 @@ * @property {HTMLDivElement} textLayerDiv - The text layer container. * @property {number} pageIndex - The page index. * @property {PageViewport} viewport - The viewport of the text layer. - * @property {ILastScrollSource} lastScrollSource - The object that records when - * last time scroll happened. - * @property {boolean} isViewerInPresentationMode * @property {PDFFindController} findController */ @@ -3456,18 +3382,27 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() { function TextLayerBuilder(options) { this.textLayerDiv = options.textLayerDiv; - this.layoutDone = false; + this.renderingDone = false; this.divContentDone = false; this.pageIdx = options.pageIndex; + this.pageNumber = this.pageIdx + 1; this.matches = []; - this.lastScrollSource = options.lastScrollSource || null; this.viewport = options.viewport; - this.isViewerInPresentationMode = options.isViewerInPresentationMode; this.textDivs = []; this.findController = options.findController || null; } TextLayerBuilder.prototype = { + _finishRendering: function TextLayerBuilder_finishRendering() { + this.renderingDone = true; + + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('textlayerrendered', true, true, { + pageNumber: this.pageNumber + }); + this.textLayerDiv.dispatchEvent(event); + }, + renderLayer: function TextLayerBuilder_renderLayer() { var textLayerFrag = document.createDocumentFragment(); var textDivs = this.textDivs; @@ -3478,6 +3413,7 @@ // No point in rendering many divs as it would make the browser // unusable even after the divs are rendered. if (textDivsLength > MAX_TEXT_DIVS_TO_RENDER) { + this._finishRendering(); return; } @@ -3521,27 +3457,33 @@ } this.textLayerDiv.appendChild(textLayerFrag); - this.renderingDone = true; + this._finishRendering(); this.updateMatches(); }, - setupRenderLayoutTimer: - function TextLayerBuilder_setupRenderLayoutTimer() { - // Schedule renderLayout() if the user has been scrolling, - // otherwise run it right away. - var self = this; - var lastScroll = (this.lastScrollSource === null ? - 0 : this.lastScrollSource.lastScroll); + /** + * Renders the text layer. + * @param {number} timeout (optional) if specified, the rendering waits + * for specified amount of ms. + */ + render: function TextLayerBuilder_render(timeout) { + if (!this.divContentDone || this.renderingDone) { + return; + } + + if (this.renderTimer) { + clearTimeout(this.renderTimer); + this.renderTimer = null; + } - if (Date.now() - lastScroll > RENDER_DELAY) { // Render right away + if (!timeout) { // Render right away this.renderLayer(); } else { // Schedule - if (this.renderTimer) { - clearTimeout(this.renderTimer); - } + var self = this; this.renderTimer = setTimeout(function() { - self.setupRenderLayoutTimer(); - }, RENDER_DELAY); + self.renderLayer(); + self.renderTimer = null; + }, timeout); } }, @@ -3611,7 +3553,6 @@ this.appendText(textItems[i], textContent.styles); } this.divContentDone = true; - this.setupRenderLayoutTimer(); }, convertMatches: function TextLayerBuilder_convertMatches(matches) { @@ -3673,8 +3614,9 @@ var bidiTexts = this.textContent.items; var textDivs = this.textDivs; var prevEnd = null; + var pageIdx = this.pageIdx; var isSelectedPage = (this.findController === null ? - false : (this.pageIdx === this.findController.selected.pageIdx)); + false : (pageIdx === this.findController.selected.pageIdx)); var selectedMatchIdx = (this.findController === null ? -1 : this.findController.selected.matchIdx); var highlightAll = (this.findController === null ? @@ -3720,10 +3662,9 @@ var isSelected = (isSelectedPage && i === selectedMatchIdx); var highlightSuffix = (isSelected ? ' selected' : ''); - if (isSelected && !this.isViewerInPresentationMode) { - scrollIntoView(textDivs[begin.divIdx], - { top: FIND_SCROLL_OFFSET_TOP, - left: FIND_SCROLL_OFFSET_LEFT }); + if (this.findController) { + this.findController.updateMatchPosition(pageIdx, i, textDivs, + begin.divIdx, end.divIdx); } // Match inside new div. @@ -3795,23 +3736,222 @@ return TextLayerBuilder; })(); - /** - * @typedef {Object} PDFViewerOptions - * @property {HTMLDivElement} container - The container for the viewer element. - * @property {HTMLDivElement} viewer - (optional) The viewer element. - * @property {IPDFLinkService} linkService - The navigation/linking service. - * @property {PDFRenderingQueue} renderingQueue - (optional) The rendering - * queue object. + * @constructor + * @implements IPDFTextLayerFactory */ - +function DefaultTextLayerFactory() {} +DefaultTextLayerFactory.prototype = { + /** + * @param {HTMLDivElement} textLayerDiv + * @param {number} pageIndex + * @param {PageViewport} viewport + * @returns {TextLayerBuilder} + */ + createTextLayerBuilder: function (textLayerDiv, pageIndex, viewport) { + return new TextLayerBuilder({ + textLayerDiv: textLayerDiv, + pageIndex: pageIndex, + viewport: viewport + }); + } +}; + + +/** + * @typedef {Object} AnnotationsLayerBuilderOptions + * @property {HTMLDivElement} pageDiv + * @property {PDFPage} pdfPage + * @property {IPDFLinkService} linkService + */ + +/** + * @class + */ +var AnnotationsLayerBuilder = (function AnnotationsLayerBuilderClosure() { + /** + * @param {AnnotationsLayerBuilderOptions} options + * @constructs AnnotationsLayerBuilder + */ + function AnnotationsLayerBuilder(options) { + this.pageDiv = options.pageDiv; + this.pdfPage = options.pdfPage; + this.linkService = options.linkService; + + this.div = null; + } + AnnotationsLayerBuilder.prototype = + /** @lends AnnotationsLayerBuilder.prototype */ { + + /** + * @param {PageViewport} viewport + */ + setupAnnotations: + function AnnotationsLayerBuilder_setupAnnotations(viewport) { + function bindLink(link, dest) { + link.href = linkService.getDestinationHash(dest); + link.onclick = function annotationsLayerBuilderLinksOnclick() { + if (dest) { + linkService.navigateTo(dest); + } + return false; + }; + if (dest) { + link.className = 'internalLink'; + } + } + + function bindNamedAction(link, action) { + link.href = linkService.getAnchorUrl(''); + link.onclick = function annotationsLayerBuilderNamedActionOnClick() { + linkService.executeNamedAction(action); + return false; + }; + link.className = 'internalLink'; + } + + var linkService = this.linkService; + var pdfPage = this.pdfPage; + var self = this; + + pdfPage.getAnnotations().then(function (annotationsData) { + viewport = viewport.clone({ dontFlip: true }); + var transform = viewport.transform; + var transformStr = 'matrix(' + transform.join(',') + ')'; + var data, element, i, ii; + + if (self.div) { + // If an annotationLayer already exists, refresh its children's + // transformation matrices + for (i = 0, ii = annotationsData.length; i < ii; i++) { + data = annotationsData[i]; + element = self.div.querySelector( + '[data-annotation-id="' + data.id + '"]'); + if (element) { + CustomStyle.setProp('transform', element, transformStr); + } + } + // See PDFPageView.reset() + self.div.removeAttribute('hidden'); + } else { + for (i = 0, ii = annotationsData.length; i < ii; i++) { + data = annotationsData[i]; + if (!data || !data.hasHtml) { + continue; + } + + element = PDFJS.AnnotationUtils.getHtmlElement(data, + pdfPage.commonObjs); + element.setAttribute('data-annotation-id', data.id); + if (typeof mozL10n !== 'undefined') { + mozL10n.translate(element); + } + + var rect = data.rect; + var view = pdfPage.view; + rect = PDFJS.Util.normalizeRect([ + rect[0], + view[3] - rect[1] + view[1], + rect[2], + view[3] - rect[3] + view[1] + ]); + element.style.left = rect[0] + 'px'; + element.style.top = rect[1] + 'px'; + element.style.position = 'absolute'; + + CustomStyle.setProp('transform', element, transformStr); + var transformOriginStr = -rect[0] + 'px ' + -rect[1] + 'px'; + CustomStyle.setProp('transformOrigin', element, transformOriginStr); + + if (data.subtype === 'Link' && !data.url) { + var link = element.getElementsByTagName('a')[0]; + if (link) { + if (data.action) { + bindNamedAction(link, data.action); + } else { + bindLink(link, ('dest' in data) ? data.dest : null); + } + } + } + + if (!self.div) { + var annotationLayerDiv = document.createElement('div'); + annotationLayerDiv.className = 'annotationLayer'; + self.pageDiv.appendChild(annotationLayerDiv); + self.div = annotationLayerDiv; + } + + self.div.appendChild(element); + } + } + }); + }, + + hide: function () { + if (!this.div) { + return; + } + this.div.setAttribute('hidden', 'true'); + } + }; + return AnnotationsLayerBuilder; +})(); + +/** + * @constructor + * @implements IPDFAnnotationsLayerFactory + */ +function DefaultAnnotationsLayerFactory() {} +DefaultAnnotationsLayerFactory.prototype = { + /** + * @param {HTMLDivElement} pageDiv + * @param {PDFPage} pdfPage + * @returns {AnnotationsLayerBuilder} + */ + createAnnotationsLayerBuilder: function (pageDiv, pdfPage) { + return new AnnotationsLayerBuilder({ + pageDiv: pageDiv, + pdfPage: pdfPage + }); + } +}; + + +/** + * @typedef {Object} PDFViewerOptions + * @property {HTMLDivElement} container - The container for the viewer element. + * @property {HTMLDivElement} viewer - (optional) The viewer element. + * @property {IPDFLinkService} linkService - The navigation/linking service. + * @property {PDFRenderingQueue} renderingQueue - (optional) The rendering + * queue object. + */ + /** * Simple viewer control to display PDF content/pages. * @class - * @implements {ILastScrollSource} * @implements {IRenderableView} */ var PDFViewer = (function pdfViewer() { + function PDFPageViewBuffer(size) { + var data = []; + this.push = function cachePush(view) { + var i = data.indexOf(view); + if (i >= 0) { + data.splice(i, 1); + } + data.push(view); + if (data.length > size) { + data.shift().destroy(); + } + }; + this.resize = function (newSize) { + size = newSize; + while (data.length > size) { + data.shift().destroy(); + } + }; + } + /** * @constructs PDFViewer * @param {PDFViewerOptions} options @@ -3831,7 +3971,6 @@ } this.scroll = watchScroll(this.container, this._scrollUpdate.bind(this)); - this.lastScroll = 0; this.updateInProgress = false; this.presentationModeState = PresentationModeState.UNKNOWN; this._resetView(); @@ -3867,7 +4006,6 @@ return; } - this.pages[val - 1].updateStats(); event.previousPageNumber = this._currentPageNumber; this._currentPageNumber = val; event.pageNumber = val; @@ -3973,18 +4111,19 @@ }); this.onePageRendered = onePageRendered; - var bindOnAfterDraw = function (pageView) { + var bindOnAfterAndBeforeDraw = function (pageView) { + pageView.onBeforeDraw = function pdfViewLoadOnBeforeDraw() { + // Add the page to the buffer at the start of drawing. That way it can + // be evicted from the buffer and destroyed even if we pause its + // rendering. + self._buffer.push(this); + }; // when page is painted, using the image as thumbnail base pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() { if (!isOnePageRenderedResolved) { isOnePageRenderedResolved = true; resolveOnePageRendered(); } - var event = document.createEvent('CustomEvent'); - event.initCustomEvent('pagerendered', true, true, { - pageNumber: pageView.id - }); - self.container.dispatchEvent(event); }; }; @@ -3997,12 +4136,20 @@ var scale = this._currentScale || 1.0; var viewport = pdfPage.getViewport(scale * CSS_UNITS); for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) { - var pageSource = new PDFPageSource(pdfDocument, pageNum); - var pageView = new PageView(this.viewer, pageNum, scale, - viewport.clone(), this.linkService, - this.renderingQueue, this.cache, - pageSource, this); - bindOnAfterDraw(pageView); + var textLayerFactory = null; + if (!PDFJS.disableTextLayer) { + textLayerFactory = this; + } + var pageView = new PDFPageView({ + container: this.viewer, + id: pageNum, + scale: scale, + defaultViewport: viewport.clone(), + renderingQueue: this.renderingQueue, + textLayerFactory: textLayerFactory, + annotationsLayerFactory: this + }); + bindOnAfterAndBeforeDraw(pageView); this.pages.push(pageView); } @@ -4043,13 +4190,14 @@ }, _resetView: function () { - this.cache = new Cache(DEFAULT_CACHE_SIZE); this.pages = []; this._currentPageNumber = 1; this._currentScale = UNKNOWN_SCALE; this._currentScaleValue = null; + this._buffer = new PDFPageViewBuffer(DEFAULT_CACHE_SIZE); this.location = null; this._pagesRotation = 0; + this._pagesRequests = []; var container = this.viewer; while (container.hasChildNodes()) { @@ -4058,12 +4206,13 @@ }, _scrollUpdate: function () { - this.lastScroll = Date.now(); - if (this.pagesCount === 0) { return; } this.update(); + for (var i = 0, ii = this.pages.length; i < ii; i++) { + this.pages[i].updatePosition(); + } }, _setScaleUpdatePages: function pdfViewer_setScaleUpdatePages( @@ -4160,7 +4309,6 @@ scrollPageIntoView: function PDFViewer_scrollPageIntoView(pageNumber, dest) { var pageView = this.pages[pageNumber - 1]; - var pageViewDiv = pageView.el; if (this.presentationModeState === PresentationModeState.FULLSCREEN) { @@ -4174,7 +4322,7 @@ this._setScale(this.currentScaleValue, true); } if (!dest) { - scrollIntoView(pageViewDiv); + scrollIntoView(pageView.div); return; } @@ -4237,7 +4385,7 @@ } if (scale === 'page-fit' && !dest[4]) { - scrollIntoView(pageViewDiv); + scrollIntoView(pageView.div); return; } @@ -4248,7 +4396,7 @@ var left = Math.min(boundingRect[0][0], boundingRect[1][0]); var top = Math.min(boundingRect[0][1], boundingRect[1][1]); - scrollIntoView(pageViewDiv, { left: left, top: top }); + scrollIntoView(pageView.div, { left: left, top: top }); }, _updateLocation: function (firstPage) { @@ -4290,7 +4438,7 @@ var suggestedCacheSize = Math.max(DEFAULT_CACHE_SIZE, 2 * visiblePages.length + 1); - this.cache.resize(suggestedCacheSize); + this._buffer.resize(suggestedCacheSize); this.renderingQueue.renderHighestPriority(visible); @@ -4366,13 +4514,38 @@ } }, + /** + * @param {PDFPageView} pageView + * @returns {PDFPage} + * @private + */ + _ensurePdfPageLoaded: function (pageView) { + if (pageView.pdfPage) { + return Promise.resolve(pageView.pdfPage); + } + var pageNumber = pageView.id; + if (this._pagesRequests[pageNumber]) { + return this._pagesRequests[pageNumber]; + } + var promise = this.pdfDocument.getPage(pageNumber).then( + function (pdfPage) { + pageView.setPdfPage(pdfPage); + this._pagesRequests[pageNumber] = null; + return pdfPage; + }.bind(this)); + this._pagesRequests[pageNumber] = promise; + return promise; + }, + forceRendering: function (currentlyVisiblePages) { var visiblePages = currentlyVisiblePages || this._getVisiblePages(); var pageView = this.renderingQueue.getHighestPriority(visiblePages, this.pages, this.scroll.down); if (pageView) { - this.renderingQueue.renderView(pageView); + this._ensurePdfPageLoaded(pageView).then(function () { + this.renderingQueue.renderView(pageView); + }.bind(this)); return true; } return false; @@ -4385,9 +4558,9 @@ }, /** - * @param textLayerDiv {HTMLDivElement} - * @param pageIndex {number} - * @param viewport {PageViewport} + * @param {HTMLDivElement} textLayerDiv + * @param {number} pageIndex + * @param {PageViewport} viewport * @returns {TextLayerBuilder} */ createTextLayerBuilder: function (textLayerDiv, pageIndex, viewport) { @@ -4397,9 +4570,20 @@ textLayerDiv: textLayerDiv, pageIndex: pageIndex, viewport: viewport, - lastScrollSource: this, - isViewerInPresentationMode: isViewerInPresentationMode, - findController: this.findController + findController: isViewerInPresentationMode ? null : this.findController + }); + }, + + /** + * @param {HTMLDivElement} pageDiv + * @param {PDFPage} pdfPage + * @returns {AnnotationsLayerBuilder} + */ + createAnnotationsLayerBuilder: function (pageDiv, pdfPage) { + return new AnnotationsLayerBuilder({ + pageDiv: pageDiv, + pdfPage: pdfPage, + linkService: this.linkService }); }, @@ -4458,1761 +4642,1869 @@ return SimpleLinkService; })(); + +var THUMBNAIL_SCROLL_MARGIN = -19; + + +var THUMBNAIL_WIDTH = 98; // px +var THUMBNAIL_CANVAS_BORDER_WIDTH = 1; // px + +/** + * @typedef {Object} PDFThumbnailViewOptions + * @property {HTMLDivElement} container - The viewer element. + * @property {number} id - The thumbnail's unique ID (normally its number). + * @property {PageViewport} defaultViewport - The page viewport. + * @property {IPDFLinkService} linkService - The navigation/linking service. + * @property {PDFRenderingQueue} renderingQueue - The rendering queue object. + */ + /** - * PDFPage object source. * @class + * @implements {IRenderableView} */ -var PDFPageSource = (function PDFPageSourceClosure() { +var PDFThumbnailView = (function PDFThumbnailViewClosure() { + function getTempCanvas(width, height) { + var tempCanvas = PDFThumbnailView.tempImageCache; + if (!tempCanvas) { + tempCanvas = document.createElement('canvas'); + PDFThumbnailView.tempImageCache = tempCanvas; + } + tempCanvas.width = width; + tempCanvas.height = height; + + // Since this is a temporary canvas, we need to fill the canvas with a white + // background ourselves. |_getPageDrawContext| uses CSS rules for this. + var ctx = tempCanvas.getContext('2d'); + ctx.save(); + ctx.fillStyle = 'rgb(255, 255, 255)'; + ctx.fillRect(0, 0, width, height); + ctx.restore(); + return tempCanvas; + } + /** - * @constructs - * @param {PDFDocument} pdfDocument - * @param {number} pageNumber - * @constructor + * @constructs PDFThumbnailView + * @param {PDFThumbnailViewOptions} options */ - function PDFPageSource(pdfDocument, pageNumber) { - this.pdfDocument = pdfDocument; - this.pageNumber = pageNumber; - } + function PDFThumbnailView(options) { + var container = options.container; + var id = options.id; + var defaultViewport = options.defaultViewport; + var linkService = options.linkService; + var renderingQueue = options.renderingQueue; + + this.id = id; + this.renderingId = 'thumbnail' + id; + + this.pdfPage = null; + this.rotation = 0; + this.viewport = defaultViewport; + this.pdfPageRotate = defaultViewport.rotation; + + this.linkService = linkService; + this.renderingQueue = renderingQueue; + + this.hasImage = false; + this.resume = null; + this.renderingState = RenderingStates.INITIAL; + + this.pageWidth = this.viewport.width; + this.pageHeight = this.viewport.height; + this.pageRatio = this.pageWidth / this.pageHeight; + + this.canvasWidth = THUMBNAIL_WIDTH; + this.canvasHeight = (this.canvasWidth / this.pageRatio) | 0; + this.scale = this.canvasWidth / this.pageWidth; + + var anchor = document.createElement('a'); + anchor.href = linkService.getAnchorUrl('#page=' + id); + anchor.title = mozL10n.get('thumb_page_title', {page: id}, 'Page {{page}}'); + anchor.onclick = function stopNavigation() { + linkService.page = id; + return false; + }; + + var div = document.createElement('div'); + div.id = 'thumbnailContainer' + id; + div.className = 'thumbnail'; + this.el = div; // TODO: replace 'el' property usage. + this.div = div; + + if (id === 1) { + // Highlight the thumbnail of the first page when no page number is + // specified (or exists in cache) when the document is loaded. + div.classList.add('selected'); + } + + var ring = document.createElement('div'); + ring.className = 'thumbnailSelectionRing'; + var borderAdjustment = 2 * THUMBNAIL_CANVAS_BORDER_WIDTH; + ring.style.width = this.canvasWidth + borderAdjustment + 'px'; + ring.style.height = this.canvasHeight + borderAdjustment + 'px'; + this.ring = ring; + + div.appendChild(ring); + anchor.appendChild(div); + container.appendChild(anchor); + } + + PDFThumbnailView.prototype = { + setPdfPage: function PDFThumbnailView_setPdfPage(pdfPage) { + this.pdfPage = pdfPage; + this.pdfPageRotate = pdfPage.rotate; + var totalRotation = (this.rotation + this.pdfPageRotate) % 360; + this.viewport = pdfPage.getViewport(1, totalRotation); + this.reset(); + }, + + reset: function PDFThumbnailView_reset() { + if (this.renderTask) { + this.renderTask.cancel(); + } + this.hasImage = false; + this.resume = null; + this.renderingState = RenderingStates.INITIAL; + + this.pageWidth = this.viewport.width; + this.pageHeight = this.viewport.height; + this.pageRatio = this.pageWidth / this.pageHeight; + + this.canvasHeight = (this.canvasWidth / this.pageRatio) | 0; + this.scale = (this.canvasWidth / this.pageWidth); + + this.div.removeAttribute('data-loaded'); + var ring = this.ring; + var childNodes = ring.childNodes; + for (var i = childNodes.length - 1; i >= 0; i--) { + ring.removeChild(childNodes[i]); + } + var borderAdjustment = 2 * THUMBNAIL_CANVAS_BORDER_WIDTH; + ring.style.width = this.canvasWidth + borderAdjustment + 'px'; + ring.style.height = this.canvasHeight + borderAdjustment + 'px'; + + if (this.canvas) { + // Zeroing the width and height causes Firefox to release graphics + // resources immediately, which can greatly reduce memory consumption. + this.canvas.width = 0; + this.canvas.height = 0; + delete this.canvas; + } + }, + + update: function PDFThumbnailView_update(rotation) { + if (typeof rotation !== 'undefined') { + this.rotation = rotation; + } + var totalRotation = (this.rotation + this.pdfPageRotate) % 360; + this.viewport = this.viewport.clone({ + scale: 1, + rotation: totalRotation + }); + this.reset(); + }, - PDFPageSource.prototype = /** @lends PDFPageSource.prototype */ { /** - * @returns {Promise} + * @private */ - getPage: function () { - return this.pdfDocument.getPage(this.pageNumber); - } - }; + _getPageDrawContext: function PDFThumbnailView_getPageDrawContext() { + var canvas = document.createElement('canvas'); + canvas.id = this.renderingId; - return PDFPageSource; -})(); + canvas.width = this.canvasWidth; + canvas.height = this.canvasHeight; + canvas.className = 'thumbnailImage'; + canvas.setAttribute('aria-label', mozL10n.get('thumb_page_canvas', + {page: this.id}, 'Thumbnail of Page {{page}}')); + this.canvas = canvas; + this.div.setAttribute('data-loaded', true); + this.ring.appendChild(canvas); -var PDFViewerApplication = { - initialBookmark: document.location.hash.substring(1), - initialized: false, - fellback: false, - pdfDocument: null, - sidebarOpen: false, - printing: false, - /** @type {PDFViewer} */ - pdfViewer: null, - /** @type {PDFThumbnailViewer} */ - pdfThumbnailViewer: null, - /** @type {PDFRenderingQueue} */ - pdfRenderingQueue: null, - pageRotation: 0, - updateScaleControls: true, - isInitialViewSet: false, - animationStartedPromise: null, - mouseScrollTimeStamp: 0, - mouseScrollDelta: 0, - preferenceSidebarViewOnLoad: SidebarView.NONE, - preferencePdfBugEnabled: false, - isViewerEmbedded: (window.parent !== window), - url: '', + return canvas.getContext('2d'); + }, - // called once when the document is loaded - initialize: function pdfViewInitialize() { - var pdfRenderingQueue = new PDFRenderingQueue(); - pdfRenderingQueue.onIdle = this.cleanup.bind(this); - this.pdfRenderingQueue = pdfRenderingQueue; + draw: function PDFThumbnailView_draw() { + if (this.renderingState !== RenderingStates.INITIAL) { + console.error('Must be in new state before drawing'); + } + if (this.hasImage) { + return Promise.resolve(undefined); + } + this.hasImage = true; + this.renderingState = RenderingStates.RUNNING; - var container = document.getElementById('viewerContainer'); - var viewer = document.getElementById('viewer'); - this.pdfViewer = new PDFViewer({ - container: container, - viewer: viewer, - renderingQueue: pdfRenderingQueue, - linkService: this - }); - pdfRenderingQueue.setViewer(this.pdfViewer); + var resolveRenderPromise, rejectRenderPromise; + var promise = new Promise(function (resolve, reject) { + resolveRenderPromise = resolve; + rejectRenderPromise = reject; + }); - var thumbnailContainer = document.getElementById('thumbnailView'); - this.pdfThumbnailViewer = new PDFThumbnailViewer({ - container: thumbnailContainer, - renderingQueue: pdfRenderingQueue, - linkService: this - }); - pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer); + var self = this; + function thumbnailDrawCallback(error) { + // The renderTask may have been replaced by a new one, so only remove + // the reference to the renderTask if it matches the one that is + // triggering this callback. + if (renderTask === self.renderTask) { + self.renderTask = null; + } + if (error === 'cancelled') { + rejectRenderPromise(error); + return; + } + self.renderingState = RenderingStates.FINISHED; - Preferences.initialize(); + if (!error) { + resolveRenderPromise(undefined); + } else { + rejectRenderPromise(error); + } + } - this.findController = new PDFFindController({ - pdfViewer: this.pdfViewer, - integratedFind: this.supportsIntegratedFind - }); - this.pdfViewer.setFindController(this.findController); + var ctx = this._getPageDrawContext(); + var drawViewport = this.viewport.clone({ scale: this.scale }); + var renderContinueCallback = function renderContinueCallback(cont) { + if (!self.renderingQueue.isHighestPriority(self)) { + self.renderingState = RenderingStates.PAUSED; + self.resume = function resumeCallback() { + self.renderingState = RenderingStates.RUNNING; + cont(); + }; + return; + } + cont(); + }; - this.findBar = new PDFFindBar({ - bar: document.getElementById('findbar'), - toggleButton: document.getElementById('viewFind'), - findField: document.getElementById('findInput'), - highlightAllCheckbox: document.getElementById('findHighlightAll'), - caseSensitiveCheckbox: document.getElementById('findMatchCase'), - findMsg: document.getElementById('findMsg'), - findStatusIcon: document.getElementById('findStatusIcon'), - findPreviousButton: document.getElementById('findPrevious'), - findNextButton: document.getElementById('findNext'), - findController: this.findController - }); + var renderContext = { + canvasContext: ctx, + viewport: drawViewport, + continueCallback: renderContinueCallback + }; + var renderTask = this.renderTask = this.pdfPage.render(renderContext); - this.findController.setFindBar(this.findBar); + renderTask.promise.then( + function pdfPageRenderCallback() { + thumbnailDrawCallback(null); + }, + function pdfPageRenderError(error) { + thumbnailDrawCallback(error); + } + ); + return promise; + }, - HandTool.initialize({ - container: container, - toggleHandTool: document.getElementById('toggleHandTool') - }); + setImage: function PDFThumbnailView_setImage(pageView) { + var img = pageView.canvas; + if (this.hasImage || !img) { + return; + } + if (!this.pdfPage) { + this.setPdfPage(pageView.pdfPage); + } + this.hasImage = true; + this.renderingState = RenderingStates.FINISHED; - SecondaryToolbar.initialize({ - toolbar: document.getElementById('secondaryToolbar'), - presentationMode: PresentationMode, - toggleButton: document.getElementById('secondaryToolbarToggle'), - presentationModeButton: - document.getElementById('secondaryPresentationMode'), - openFile: document.getElementById('secondaryOpenFile'), - print: document.getElementById('secondaryPrint'), - download: document.getElementById('secondaryDownload'), - viewBookmark: document.getElementById('secondaryViewBookmark'), - firstPage: document.getElementById('firstPage'), - lastPage: document.getElementById('lastPage'), - pageRotateCw: document.getElementById('pageRotateCw'), - pageRotateCcw: document.getElementById('pageRotateCcw'), - documentProperties: DocumentProperties, - documentPropertiesButton: document.getElementById('documentProperties') - }); - - PresentationMode.initialize({ - container: container, - secondaryToolbar: SecondaryToolbar, - firstPage: document.getElementById('contextFirstPage'), - lastPage: document.getElementById('contextLastPage'), - pageRotateCw: document.getElementById('contextPageRotateCw'), - pageRotateCcw: document.getElementById('contextPageRotateCcw') - }); - - PasswordPrompt.initialize({ - overlayName: 'passwordOverlay', - passwordField: document.getElementById('password'), - passwordText: document.getElementById('passwordText'), - passwordSubmit: document.getElementById('passwordSubmit'), - passwordCancel: document.getElementById('passwordCancel') - }); - - DocumentProperties.initialize({ - overlayName: 'documentPropertiesOverlay', - closeButton: document.getElementById('documentPropertiesClose'), - fileNameField: document.getElementById('fileNameField'), - fileSizeField: document.getElementById('fileSizeField'), - titleField: document.getElementById('titleField'), - authorField: document.getElementById('authorField'), - subjectField: document.getElementById('subjectField'), - keywordsField: document.getElementById('keywordsField'), - creationDateField: document.getElementById('creationDateField'), - modificationDateField: document.getElementById('modificationDateField'), - creatorField: document.getElementById('creatorField'), - producerField: document.getElementById('producerField'), - versionField: document.getElementById('versionField'), - pageCountField: document.getElementById('pageCountField') - }); - - var self = this; - var initializedPromise = Promise.all([ - Preferences.get('enableWebGL').then(function resolved(value) { - PDFJS.disableWebGL = !value; - }), - Preferences.get('sidebarViewOnLoad').then(function resolved(value) { - self.preferenceSidebarViewOnLoad = value; - }), - Preferences.get('pdfBugEnabled').then(function resolved(value) { - self.preferencePdfBugEnabled = value; - }), - Preferences.get('disableTextLayer').then(function resolved(value) { - if (PDFJS.disableTextLayer === true) { - return; - } - PDFJS.disableTextLayer = value; - }), - Preferences.get('disableRange').then(function resolved(value) { - if (PDFJS.disableRange === true) { - return; - } - PDFJS.disableRange = value; - }), - Preferences.get('disableAutoFetch').then(function resolved(value) { - PDFJS.disableAutoFetch = value; - }), - Preferences.get('disableFontFace').then(function resolved(value) { - if (PDFJS.disableFontFace === true) { - return; - } - PDFJS.disableFontFace = value; - }), - Preferences.get('useOnlyCssZoom').then(function resolved(value) { - PDFJS.useOnlyCssZoom = value; - }) - // TODO move more preferences and other async stuff here - ]).catch(function (reason) { }); - - return initializedPromise.then(function () { - PDFViewerApplication.initialized = true; - }); - }, - - zoomIn: function pdfViewZoomIn(ticks) { - var newScale = this.pdfViewer.currentScale; - do { - newScale = (newScale * DEFAULT_SCALE_DELTA).toFixed(2); - newScale = Math.ceil(newScale * 10) / 10; - newScale = Math.min(MAX_SCALE, newScale); - } while (--ticks && newScale < MAX_SCALE); - this.setScale(newScale, true); - }, - - zoomOut: function pdfViewZoomOut(ticks) { - var newScale = this.pdfViewer.currentScale; - do { - newScale = (newScale / DEFAULT_SCALE_DELTA).toFixed(2); - newScale = Math.floor(newScale * 10) / 10; - newScale = Math.max(MIN_SCALE, newScale); - } while (--ticks && newScale > MIN_SCALE); - this.setScale(newScale, true); - }, - - get currentScaleValue() { - return this.pdfViewer.currentScaleValue; - }, - - get pagesCount() { - return this.pdfDocument.numPages; - }, - - set page(val) { - this.pdfViewer.currentPageNumber = val; - }, - - get page() { - return this.pdfViewer.currentPageNumber; - }, - - get supportsPrinting() { - var canvas = document.createElement('canvas'); - var value = 'mozPrintCallback' in canvas; - // shadow - Object.defineProperty(this, 'supportsPrinting', { value: value, - enumerable: true, - configurable: true, - writable: false }); - return value; - }, - - get supportsFullscreen() { - var doc = document.documentElement; - var support = doc.requestFullscreen || doc.mozRequestFullScreen || - doc.webkitRequestFullScreen || doc.msRequestFullscreen; - - if (document.fullscreenEnabled === false || - document.mozFullScreenEnabled === false || - document.webkitFullscreenEnabled === false || - document.msFullscreenEnabled === false) { - support = false; - } - - Object.defineProperty(this, 'supportsFullscreen', { value: support, - enumerable: true, - configurable: true, - writable: false }); - return support; - }, - - get supportsIntegratedFind() { - var support = false; - support = FirefoxCom.requestSync('supportsIntegratedFind'); - Object.defineProperty(this, 'supportsIntegratedFind', { value: support, - enumerable: true, - configurable: true, - writable: false }); - return support; - }, - - get supportsDocumentFonts() { - var support = true; - support = FirefoxCom.requestSync('supportsDocumentFonts'); - Object.defineProperty(this, 'supportsDocumentFonts', { value: support, - enumerable: true, - configurable: true, - writable: false }); - return support; - }, - - get supportsDocumentColors() { - var support = true; - support = FirefoxCom.requestSync('supportsDocumentColors'); - Object.defineProperty(this, 'supportsDocumentColors', { value: support, - enumerable: true, - configurable: true, - writable: false }); - return support; - }, + var ctx = this._getPageDrawContext(); + var canvas = ctx.canvas; - get loadingBar() { - var bar = new ProgressBar('#loadingBar', {}); - Object.defineProperty(this, 'loadingBar', { value: bar, - enumerable: true, - configurable: true, - writable: false }); - return bar; - }, - - initPassiveLoading: function pdfViewInitPassiveLoading() { - var pdfDataRangeTransportReadyResolve; - var pdfDataRangeTransportReady = new Promise(function (resolve) { - pdfDataRangeTransportReadyResolve = resolve; - }); - var pdfDataRangeTransport = { - rangeListeners: [], - progressListeners: [], - progressiveReadListeners: [], - ready: pdfDataRangeTransportReady, - - addRangeListener: function PdfDataRangeTransport_addRangeListener( - listener) { - this.rangeListeners.push(listener); - }, - - addProgressListener: function PdfDataRangeTransport_addProgressListener( - listener) { - this.progressListeners.push(listener); - }, - - addProgressiveReadListener: - function PdfDataRangeTransport_addProgressiveReadListener(listener) { - this.progressiveReadListeners.push(listener); - }, - - onDataRange: function PdfDataRangeTransport_onDataRange(begin, chunk) { - var listeners = this.rangeListeners; - for (var i = 0, n = listeners.length; i < n; ++i) { - listeners[i](begin, chunk); - } - }, - - onDataProgress: function PdfDataRangeTransport_onDataProgress(loaded) { - this.ready.then(function () { - var listeners = this.progressListeners; - for (var i = 0, n = listeners.length; i < n; ++i) { - listeners[i](loaded); - } - }.bind(this)); - }, - - onDataProgressiveRead: - function PdfDataRangeTransport_onDataProgress(chunk) { - this.ready.then(function () { - var listeners = this.progressiveReadListeners; - for (var i = 0, n = listeners.length; i < n; ++i) { - listeners[i](chunk); - } - }.bind(this)); - }, - - transportReady: function PdfDataRangeTransport_transportReady() { - pdfDataRangeTransportReadyResolve(); - }, - - requestDataRange: function PdfDataRangeTransport_requestDataRange( - begin, end) { - FirefoxCom.request('requestDataRange', { begin: begin, end: end }); - } - }; - - window.addEventListener('message', function windowMessage(e) { - if (e.source !== null) { - // The message MUST originate from Chrome code. - console.warn('Rejected untrusted message from ' + e.origin); + if (img.width <= 2 * canvas.width) { + ctx.drawImage(img, 0, 0, img.width, img.height, + 0, 0, canvas.width, canvas.height); return; } - var args = e.data; + // drawImage does an awful job of rescaling the image, doing it gradually. + var MAX_NUM_SCALING_STEPS = 3; + var reducedWidth = canvas.width << MAX_NUM_SCALING_STEPS; + var reducedHeight = canvas.height << MAX_NUM_SCALING_STEPS; + var reducedImage = getTempCanvas(reducedWidth, reducedHeight); + var reducedImageCtx = reducedImage.getContext('2d'); - if (typeof args !== 'object' || !('pdfjsLoadAction' in args)) { - return; + while (reducedWidth > img.width || reducedHeight > img.height) { + reducedWidth >>= 1; + reducedHeight >>= 1; } - switch (args.pdfjsLoadAction) { - case 'supportsRangedLoading': - PDFViewerApplication.open(args.pdfUrl, 0, undefined, - pdfDataRangeTransport, { - length: args.length, - initialData: args.data - }); - break; - case 'range': - pdfDataRangeTransport.onDataRange(args.begin, args.chunk); - break; - case 'rangeProgress': - pdfDataRangeTransport.onDataProgress(args.loaded); - break; - case 'progressiveRead': - pdfDataRangeTransport.onDataProgressiveRead(args.chunk); - break; - case 'progress': - PDFViewerApplication.progress(args.loaded / args.total); - break; - case 'complete': - if (!args.data) { - PDFViewerApplication.error(mozL10n.get('loading_error', null, - 'An error occurred while loading the PDF.'), e); - break; - } - PDFViewerApplication.open(args.data, 0); - break; + reducedImageCtx.drawImage(img, 0, 0, img.width, img.height, + 0, 0, reducedWidth, reducedHeight); + while (reducedWidth > 2 * canvas.width) { + reducedImageCtx.drawImage(reducedImage, + 0, 0, reducedWidth, reducedHeight, + 0, 0, reducedWidth >> 1, reducedHeight >> 1); + reducedWidth >>= 1; + reducedHeight >>= 1; } - }); - FirefoxCom.requestSync('initPassiveLoading', null); - }, - - setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) { - this.url = url; - try { - this.setTitle(decodeURIComponent(getFileName(url)) || url); - } catch (e) { - // decodeURIComponent may throw URIError, - // fall back to using the unprocessed url in that case - this.setTitle(url); + ctx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, + 0, 0, canvas.width, canvas.height); } - }, - - setTitle: function pdfViewSetTitle(title) { - document.title = title; - }, - - close: function pdfViewClose() { - var errorWrapper = document.getElementById('errorWrapper'); - errorWrapper.setAttribute('hidden', 'true'); + }; - if (!this.pdfDocument) { - return; - } + return PDFThumbnailView; +})(); - this.pdfDocument.destroy(); - this.pdfDocument = null; +PDFThumbnailView.tempImageCache = null; - this.pdfThumbnailViewer.setDocument(null); - this.pdfViewer.setDocument(null); - if (typeof PDFBug !== 'undefined') { - PDFBug.cleanup(); - } - }, +/** + * @typedef {Object} PDFThumbnailViewerOptions + * @property {HTMLDivElement} container - The container for the thumbnail + * elements. + * @property {IPDFLinkService} linkService - The navigation/linking service. + * @property {PDFRenderingQueue} renderingQueue - The rendering queue object. + */ - // TODO(mack): This function signature should really be pdfViewOpen(url, args) - open: function pdfViewOpen(file, scale, password, - pdfDataRangeTransport, args) { - if (this.pdfDocument) { - // Reload the preferences if a document was previously opened. - Preferences.reload(); - } - this.close(); +/** + * Simple viewer control to display thumbnails for pages. + * @class + * @implements {IRenderableView} + */ +var PDFThumbnailViewer = (function PDFThumbnailViewerClosure() { + /** + * @constructs PDFThumbnailViewer + * @param {PDFThumbnailViewerOptions} options + */ + function PDFThumbnailViewer(options) { + this.container = options.container; + this.renderingQueue = options.renderingQueue; + this.linkService = options.linkService; - var parameters = {password: password}; - if (typeof file === 'string') { // URL - this.setTitleUsingUrl(file); - parameters.url = file; - } else if (file && 'byteLength' in file) { // ArrayBuffer - parameters.data = file; - } else if (file.url && file.originalUrl) { - this.setTitleUsingUrl(file.originalUrl); - parameters.url = file.url; - } - if (args) { - for (var prop in args) { - parameters[prop] = args[prop]; - } - } + this.scroll = watchScroll(this.container, this._scrollUpdated.bind(this)); + this._resetView(); + } - var self = this; - self.loading = true; - self.downloadComplete = false; + PDFThumbnailViewer.prototype = { + /** + * @private + */ + _scrollUpdated: function PDFThumbnailViewer_scrollUpdated() { + this.renderingQueue.renderHighestPriority(); + }, - var passwordNeeded = function passwordNeeded(updatePassword, reason) { - PasswordPrompt.updatePassword = updatePassword; - PasswordPrompt.reason = reason; - PasswordPrompt.open(); - }; + getThumbnail: function PDFThumbnailViewer_getThumbnail(index) { + return this.thumbnails[index]; + }, - function getDocumentProgress(progressData) { - self.progress(progressData.loaded / progressData.total); - } + /** + * @private + */ + _getVisibleThumbs: function PDFThumbnailViewer_getVisibleThumbs() { + return getVisibleElements(this.container, this.thumbnails); + }, - PDFJS.getDocument(parameters, pdfDataRangeTransport, passwordNeeded, - getDocumentProgress).then( - function getDocumentCallback(pdfDocument) { - self.load(pdfDocument, scale); - self.loading = false; - }, - function getDocumentError(exception) { - var message = exception && exception.message; - var loadingErrorMessage = mozL10n.get('loading_error', null, - 'An error occurred while loading the PDF.'); + scrollThumbnailIntoView: + function PDFThumbnailViewer_scrollThumbnailIntoView(page) { + var selected = document.querySelector('.thumbnail.selected'); + if (selected) { + selected.classList.remove('selected'); + } + var thumbnail = document.getElementById('thumbnailContainer' + page); + thumbnail.classList.add('selected'); + var visibleThumbs = this._getVisibleThumbs(); + var numVisibleThumbs = visibleThumbs.views.length; - if (exception instanceof PDFJS.InvalidPDFException) { - // change error message also for other builds - loadingErrorMessage = mozL10n.get('invalid_file_error', null, - 'Invalid or corrupted PDF file.'); - } else if (exception instanceof PDFJS.MissingPDFException) { - // special message for missing PDF's - loadingErrorMessage = mozL10n.get('missing_file_error', null, - 'Missing PDF file.'); - } else if (exception instanceof PDFJS.UnexpectedResponseException) { - loadingErrorMessage = mozL10n.get('unexpected_response_error', null, - 'Unexpected server response.'); + // If the thumbnail isn't currently visible, scroll it into view. + if (numVisibleThumbs > 0) { + var first = visibleThumbs.first.id; + // Account for only one thumbnail being visible. + var last = (numVisibleThumbs > 1 ? visibleThumbs.last.id : first); + if (page <= first || page >= last) { + scrollIntoView(thumbnail, { top: THUMBNAIL_SCROLL_MARGIN }); } - - var moreInfo = { - message: message - }; - self.error(loadingErrorMessage, moreInfo); - self.loading = false; } - ); + }, - if (args && args.length) { - DocumentProperties.setFileSize(args.length); - } - }, + get pagesRotation() { + return this._pagesRotation; + }, - download: function pdfViewDownload() { - function downloadByUrl() { - downloadManager.downloadUrl(url, filename); - } + set pagesRotation(rotation) { + this._pagesRotation = rotation; + for (var i = 0, l = this.thumbnails.length; i < l; i++) { + var thumb = this.thumbnails[i]; + thumb.update(rotation); + } + }, - var url = this.url.split('#')[0]; - var filename = getPDFFileNameFromURL(url); - var downloadManager = new DownloadManager(); - downloadManager.onerror = function (err) { - // This error won't really be helpful because it's likely the - // fallback won't work either (or is already open). - PDFViewerApplication.error('PDF failed to download.'); - }; + cleanup: function PDFThumbnailViewer_cleanup() { + var tempCanvas = PDFThumbnailView.tempImageCache; + if (tempCanvas) { + // Zeroing the width and height causes Firefox to release graphics + // resources immediately, which can greatly reduce memory consumption. + tempCanvas.width = 0; + tempCanvas.height = 0; + } + PDFThumbnailView.tempImageCache = null; + }, - if (!this.pdfDocument) { // the PDF is not ready yet - downloadByUrl(); - return; - } + /** + * @private + */ + _resetView: function PDFThumbnailViewer_resetView() { + this.thumbnails = []; + this._pagesRotation = 0; + this._pagesRequests = []; + }, - if (!this.downloadComplete) { // the PDF is still downloading - downloadByUrl(); - return; - } + setDocument: function PDFThumbnailViewer_setDocument(pdfDocument) { + if (this.pdfDocument) { + // cleanup of the elements and views + var thumbsView = this.container; + while (thumbsView.hasChildNodes()) { + thumbsView.removeChild(thumbsView.lastChild); + } + this._resetView(); + } - this.pdfDocument.getData().then( - function getDataSuccess(data) { - var blob = PDFJS.createBlob(data, 'application/pdf'); - downloadManager.download(blob, url, filename); - }, - downloadByUrl // Error occurred try downloading with just the url. - ).then(null, downloadByUrl); - }, + this.pdfDocument = pdfDocument; + if (!pdfDocument) { + return Promise.resolve(); + } - fallback: function pdfViewFallback(featureId) { - // Only trigger the fallback once so we don't spam the user with messages - // for one PDF. - if (this.fellback) - return; - this.fellback = true; - var url = this.url.split('#')[0]; - FirefoxCom.request('fallback', { featureId: featureId, url: url }, - function response(download) { - if (!download) { - return; + return pdfDocument.getPage(1).then(function (firstPage) { + var pagesCount = pdfDocument.numPages; + var viewport = firstPage.getViewport(1.0); + for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) { + var thumbnail = new PDFThumbnailView({ + container: this.container, + id: pageNum, + defaultViewport: viewport.clone(), + linkService: this.linkService, + renderingQueue: this.renderingQueue + }); + this.thumbnails.push(thumbnail); } - PDFViewerApplication.download(); - }); - }, + }.bind(this)); + }, - navigateTo: function pdfViewNavigateTo(dest) { - var destString = ''; - var self = this; + /** + * @param {PDFPageView} pageView + * @returns {PDFPage} + * @private + */ + _ensurePdfPageLoaded: + function PDFThumbnailViewer_ensurePdfPageLoaded(thumbView) { + if (thumbView.pdfPage) { + return Promise.resolve(thumbView.pdfPage); + } + var pageNumber = thumbView.id; + if (this._pagesRequests[pageNumber]) { + return this._pagesRequests[pageNumber]; + } + var promise = this.pdfDocument.getPage(pageNumber).then( + function (pdfPage) { + thumbView.setPdfPage(pdfPage); + this._pagesRequests[pageNumber] = null; + return pdfPage; + }.bind(this)); + this._pagesRequests[pageNumber] = promise; + return promise; + }, - var goToDestination = function(destRef) { - self.pendingRefStr = null; - // dest array looks like that: - var pageNumber = destRef instanceof Object ? - self.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] : - (destRef + 1); - if (pageNumber) { - if (pageNumber > self.pagesCount) { - pageNumber = self.pagesCount; - } - self.pdfViewer.scrollPageIntoView(pageNumber, dest); + ensureThumbnailVisible: + function PDFThumbnailViewer_ensureThumbnailVisible(page) { + // Ensure that the thumbnail of the current page is visible + // when switching from another view. + scrollIntoView(document.getElementById('thumbnailContainer' + page)); + }, - // Update the browsing history. - PDFHistory.push({ dest: dest, hash: destString, page: pageNumber }); - } else { - self.pdfDocument.getPageIndex(destRef).then(function (pageIndex) { - var pageNum = pageIndex + 1; - self.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] = pageNum; - goToDestination(destRef); - }); + forceRendering: function () { + var visibleThumbs = this._getVisibleThumbs(); + var thumbView = this.renderingQueue.getHighestPriority(visibleThumbs, + this.thumbnails, + this.scroll.down); + if (thumbView) { + this._ensurePdfPageLoaded(thumbView).then(function () { + this.renderingQueue.renderView(thumbView); + }.bind(this)); + return true; } - }; - - var destinationPromise; - if (typeof dest === 'string') { - destString = dest; - destinationPromise = this.pdfDocument.getDestination(dest); - } else { - destinationPromise = Promise.resolve(dest); + return false; } - destinationPromise.then(function(destination) { - dest = destination; - if (!(destination instanceof Array)) { - return; // invalid destination - } - goToDestination(destination[0]); - }); - }, - - executeNamedAction: function pdfViewExecuteNamedAction(action) { - // See PDF reference, table 8.45 - Named action - switch (action) { - case 'GoToPage': - document.getElementById('pageNumber').focus(); - break; + }; - case 'GoBack': - PDFHistory.back(); - break; + return PDFThumbnailViewer; +})(); - case 'GoForward': - PDFHistory.forward(); - break; - case 'Find': - if (!this.supportsIntegratedFind) { - this.findBar.toggle(); - } - break; +/** + * @typedef {Object} PDFOutlineViewOptions + * @property {HTMLDivElement} container - The viewer element. + * @property {Array} outline - An array of outline objects. + * @property {IPDFLinkService} linkService - The navigation/linking service. + */ - case 'NextPage': - this.page++; - break; +/** + * @class + */ +var PDFOutlineView = (function PDFOutlineViewClosure() { + /** + * @constructs PDFOutlineView + * @param {PDFOutlineViewOptions} options + */ + function PDFOutlineView(options) { + this.container = options.container; + this.outline = options.outline; + this.linkService = options.linkService; + } - case 'PrevPage': - this.page--; - break; + PDFOutlineView.prototype = { + reset: function PDFOutlineView_reset() { + var container = this.container; + while (container.firstChild) { + container.removeChild(container.firstChild); + } + }, - case 'LastPage': - this.page = this.pagesCount; - break; + /** + * @private + */ + _bindLink: function PDFOutlineView_bindLink(element, item) { + var linkService = this.linkService; + element.href = linkService.getDestinationHash(item.dest); + element.onclick = function goToDestination(e) { + linkService.navigateTo(item.dest); + return false; + }; + }, - case 'FirstPage': - this.page = 1; - break; + render: function PDFOutlineView_render() { + var outline = this.outline; - default: - break; // No action according to spec - } - }, + this.reset(); - getDestinationHash: function pdfViewGetDestinationHash(dest) { - if (typeof dest === 'string') { - return this.getAnchorUrl('#' + escape(dest)); - } - if (dest instanceof Array) { - var destRef = dest[0]; // see navigateTo method for dest format - var pageNumber = destRef instanceof Object ? - this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] : - (destRef + 1); - if (pageNumber) { - var pdfOpenParams = this.getAnchorUrl('#page=' + pageNumber); - var destKind = dest[1]; - if (typeof destKind === 'object' && 'name' in destKind && - destKind.name === 'XYZ') { - var scale = (dest[4] || this.currentScaleValue); - var scaleNumber = parseFloat(scale); - if (scaleNumber) { - scale = scaleNumber * 100; - } - pdfOpenParams += '&zoom=' + scale; - if (dest[2] || dest[3]) { - pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0); + if (!outline) { + return; + } + + var queue = [{ parent: this.container, items: this.outline }]; + while (queue.length > 0) { + var levelData = queue.shift(); + for (var i = 0, len = levelData.items.length; i < len; i++) { + var item = levelData.items[i]; + var div = document.createElement('div'); + div.className = 'outlineItem'; + var element = document.createElement('a'); + this._bindLink(element, item); + element.textContent = item.title; + div.appendChild(element); + + if (item.items.length > 0) { + var itemsDiv = document.createElement('div'); + itemsDiv.className = 'outlineItems'; + div.appendChild(itemsDiv); + queue.push({ parent: itemsDiv, items: item.items }); } + + levelData.parent.appendChild(div); } - return pdfOpenParams; } } - return ''; - }, + }; - /** - * Prefix the full url on anchor links to make sure that links are resolved - * relative to the current URL instead of the one defined in . - * @param {String} anchor The anchor hash, including the #. - */ - getAnchorUrl: function getAnchorUrl(anchor) { - return this.url.split('#')[0] + anchor; - }, + return PDFOutlineView; +})(); + + +/** + * @typedef {Object} PDFAttachmentViewOptions + * @property {HTMLDivElement} container - The viewer element. + * @property {Array} attachments - An array of attachment objects. + * @property {DownloadManager} downloadManager - The download manager. + */ +/** + * @class + */ +var PDFAttachmentView = (function PDFAttachmentViewClosure() { /** - * Show the error box. - * @param {String} message A message that is human readable. - * @param {Object} moreInfo (optional) Further information about the error - * that is more technical. Should have a 'message' - * and optionally a 'stack' property. + * @constructs PDFAttachmentView + * @param {PDFAttachmentViewOptions} options */ - error: function pdfViewError(message, moreInfo) { - var moreInfoText = mozL10n.get('error_version_info', - {version: PDFJS.version || '?', build: PDFJS.build || '?'}, - 'PDF.js v{{version}} (build: {{build}})') + '\n'; - if (moreInfo) { - moreInfoText += - mozL10n.get('error_message', {message: moreInfo.message}, - 'Message: {{message}}'); - if (moreInfo.stack) { - moreInfoText += '\n' + - mozL10n.get('error_stack', {stack: moreInfo.stack}, - 'Stack: {{stack}}'); - } else { - if (moreInfo.filename) { - moreInfoText += '\n' + - mozL10n.get('error_file', {file: moreInfo.filename}, - 'File: {{file}}'); - } - if (moreInfo.lineNumber) { - moreInfoText += '\n' + - mozL10n.get('error_line', {line: moreInfo.lineNumber}, - 'Line: {{line}}'); - } + function PDFAttachmentView(options) { + this.container = options.container; + this.attachments = options.attachments; + this.downloadManager = options.downloadManager; + } + + PDFAttachmentView.prototype = { + reset: function PDFAttachmentView_reset() { + var container = this.container; + while (container.firstChild) { + container.removeChild(container.firstChild); } - } + }, - console.error(message + '\n' + moreInfoText); - this.fallback(); - }, + /** + * @private + */ + _bindLink: function PDFAttachmentView_bindLink(button, content, filename) { + button.onclick = function downloadFile(e) { + this.downloadManager.downloadData(content, filename, ''); + return false; + }.bind(this); + }, - progress: function pdfViewProgress(level) { - var percent = Math.round(level * 100); - // When we transition from full request to range requests, it's possible - // that we discard some of the loaded data. This can cause the loading - // bar to move backwards. So prevent this by only updating the bar if it - // increases. - if (percent > this.loadingBar.percent || isNaN(percent)) { - this.loadingBar.percent = percent; + render: function PDFAttachmentView_render() { + var attachments = this.attachments; - // When disableAutoFetch is enabled, it's not uncommon for the entire file - // to never be fetched (depends on e.g. the file structure). In this case - // the loading bar will not be completely filled, nor will it be hidden. - // To prevent displaying a partially filled loading bar permanently, we - // hide it when no data has been loaded during a certain amount of time. - if (PDFJS.disableAutoFetch && percent) { - if (this.disableAutoFetchLoadingBarTimeout) { - clearTimeout(this.disableAutoFetchLoadingBarTimeout); - this.disableAutoFetchLoadingBarTimeout = null; - } - this.loadingBar.show(); + this.reset(); - this.disableAutoFetchLoadingBarTimeout = setTimeout(function () { - this.loadingBar.hide(); - this.disableAutoFetchLoadingBarTimeout = null; - }.bind(this), DISABLE_AUTO_FETCH_LOADING_BAR_TIMEOUT); + if (!attachments) { + return; + } + + var names = Object.keys(attachments).sort(function(a, b) { + return a.toLowerCase().localeCompare(b.toLowerCase()); + }); + for (var i = 0, len = names.length; i < len; i++) { + var item = attachments[names[i]]; + var filename = getFileName(item.filename); + var div = document.createElement('div'); + div.className = 'attachmentsItem'; + var button = document.createElement('button'); + this._bindLink(button, item.content, filename); + button.textContent = filename; + div.appendChild(button); + this.container.appendChild(div); } } - }, + }; - load: function pdfViewLoad(pdfDocument, scale) { - var self = this; - scale = scale || UNKNOWN_SCALE; + return PDFAttachmentView; +})(); - this.findController.reset(); - this.pdfDocument = pdfDocument; +var PDFViewerApplication = { + initialBookmark: document.location.hash.substring(1), + initialized: false, + fellback: false, + pdfDocument: null, + sidebarOpen: false, + printing: false, + /** @type {PDFViewer} */ + pdfViewer: null, + /** @type {PDFThumbnailViewer} */ + pdfThumbnailViewer: null, + /** @type {PDFRenderingQueue} */ + pdfRenderingQueue: null, + pageRotation: 0, + updateScaleControls: true, + isInitialViewSet: false, + animationStartedPromise: null, + mouseScrollTimeStamp: 0, + mouseScrollDelta: 0, + preferenceSidebarViewOnLoad: SidebarView.NONE, + preferencePdfBugEnabled: false, + isViewerEmbedded: (window.parent !== window), + url: '', - DocumentProperties.url = this.url; - DocumentProperties.pdfDocument = pdfDocument; - DocumentProperties.resolveDataAvailable(); + // called once when the document is loaded + initialize: function pdfViewInitialize() { + var pdfRenderingQueue = new PDFRenderingQueue(); + pdfRenderingQueue.onIdle = this.cleanup.bind(this); + this.pdfRenderingQueue = pdfRenderingQueue; + + var container = document.getElementById('viewerContainer'); + var viewer = document.getElementById('viewer'); + this.pdfViewer = new PDFViewer({ + container: container, + viewer: viewer, + renderingQueue: pdfRenderingQueue, + linkService: this + }); + pdfRenderingQueue.setViewer(this.pdfViewer); + + var thumbnailContainer = document.getElementById('thumbnailView'); + this.pdfThumbnailViewer = new PDFThumbnailViewer({ + container: thumbnailContainer, + renderingQueue: pdfRenderingQueue, + linkService: this + }); + pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer); + + Preferences.initialize(); + + this.findController = new PDFFindController({ + pdfViewer: this.pdfViewer, + integratedFind: this.supportsIntegratedFind + }); + this.pdfViewer.setFindController(this.findController); + + this.findBar = new PDFFindBar({ + bar: document.getElementById('findbar'), + toggleButton: document.getElementById('viewFind'), + findField: document.getElementById('findInput'), + highlightAllCheckbox: document.getElementById('findHighlightAll'), + caseSensitiveCheckbox: document.getElementById('findMatchCase'), + findMsg: document.getElementById('findMsg'), + findStatusIcon: document.getElementById('findStatusIcon'), + findPreviousButton: document.getElementById('findPrevious'), + findNextButton: document.getElementById('findNext'), + findController: this.findController + }); + + this.findController.setFindBar(this.findBar); + + HandTool.initialize({ + container: container, + toggleHandTool: document.getElementById('toggleHandTool') + }); + + SecondaryToolbar.initialize({ + toolbar: document.getElementById('secondaryToolbar'), + presentationMode: PresentationMode, + toggleButton: document.getElementById('secondaryToolbarToggle'), + presentationModeButton: + document.getElementById('secondaryPresentationMode'), + openFile: document.getElementById('secondaryOpenFile'), + print: document.getElementById('secondaryPrint'), + download: document.getElementById('secondaryDownload'), + viewBookmark: document.getElementById('secondaryViewBookmark'), + firstPage: document.getElementById('firstPage'), + lastPage: document.getElementById('lastPage'), + pageRotateCw: document.getElementById('pageRotateCw'), + pageRotateCcw: document.getElementById('pageRotateCcw'), + documentProperties: DocumentProperties, + documentPropertiesButton: document.getElementById('documentProperties') + }); + + PresentationMode.initialize({ + container: container, + secondaryToolbar: SecondaryToolbar, + firstPage: document.getElementById('contextFirstPage'), + lastPage: document.getElementById('contextLastPage'), + pageRotateCw: document.getElementById('contextPageRotateCw'), + pageRotateCcw: document.getElementById('contextPageRotateCcw') + }); + + PasswordPrompt.initialize({ + overlayName: 'passwordOverlay', + passwordField: document.getElementById('password'), + passwordText: document.getElementById('passwordText'), + passwordSubmit: document.getElementById('passwordSubmit'), + passwordCancel: document.getElementById('passwordCancel') + }); + + DocumentProperties.initialize({ + overlayName: 'documentPropertiesOverlay', + closeButton: document.getElementById('documentPropertiesClose'), + fileNameField: document.getElementById('fileNameField'), + fileSizeField: document.getElementById('fileSizeField'), + titleField: document.getElementById('titleField'), + authorField: document.getElementById('authorField'), + subjectField: document.getElementById('subjectField'), + keywordsField: document.getElementById('keywordsField'), + creationDateField: document.getElementById('creationDateField'), + modificationDateField: document.getElementById('modificationDateField'), + creatorField: document.getElementById('creatorField'), + producerField: document.getElementById('producerField'), + versionField: document.getElementById('versionField'), + pageCountField: document.getElementById('pageCountField') + }); + + var self = this; + var initializedPromise = Promise.all([ + Preferences.get('enableWebGL').then(function resolved(value) { + PDFJS.disableWebGL = !value; + }), + Preferences.get('sidebarViewOnLoad').then(function resolved(value) { + self.preferenceSidebarViewOnLoad = value; + }), + Preferences.get('pdfBugEnabled').then(function resolved(value) { + self.preferencePdfBugEnabled = value; + }), + Preferences.get('disableTextLayer').then(function resolved(value) { + if (PDFJS.disableTextLayer === true) { + return; + } + PDFJS.disableTextLayer = value; + }), + Preferences.get('disableRange').then(function resolved(value) { + if (PDFJS.disableRange === true) { + return; + } + PDFJS.disableRange = value; + }), + Preferences.get('disableAutoFetch').then(function resolved(value) { + PDFJS.disableAutoFetch = value; + }), + Preferences.get('disableFontFace').then(function resolved(value) { + if (PDFJS.disableFontFace === true) { + return; + } + PDFJS.disableFontFace = value; + }), + Preferences.get('useOnlyCssZoom').then(function resolved(value) { + PDFJS.useOnlyCssZoom = value; + }) + // TODO move more preferences and other async stuff here + ]).catch(function (reason) { }); - var downloadedPromise = pdfDocument.getDownloadInfo().then(function() { - self.downloadComplete = true; - self.loadingBar.hide(); + return initializedPromise.then(function () { + PDFViewerApplication.initialized = true; }); + }, - var pagesCount = pdfDocument.numPages; - document.getElementById('numPages').textContent = - mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}'); - document.getElementById('pageNumber').max = pagesCount; - - var id = this.documentFingerprint = pdfDocument.fingerprint; - var store = this.store = new ViewHistory(id); - - var pdfViewer = this.pdfViewer; - pdfViewer.currentScale = scale; - pdfViewer.setDocument(pdfDocument); - var firstPagePromise = pdfViewer.firstPagePromise; - var pagesPromise = pdfViewer.pagesPromise; - var onePageRendered = pdfViewer.onePageRendered; - - this.pageRotation = 0; - this.isInitialViewSet = false; - this.pagesRefMap = pdfViewer.pagesRefMap; - - this.pdfThumbnailViewer.setDocument(pdfDocument); + zoomIn: function pdfViewZoomIn(ticks) { + var newScale = this.pdfViewer.currentScale; + do { + newScale = (newScale * DEFAULT_SCALE_DELTA).toFixed(2); + newScale = Math.ceil(newScale * 10) / 10; + newScale = Math.min(MAX_SCALE, newScale); + } while (--ticks && newScale < MAX_SCALE); + this.setScale(newScale, true); + }, - firstPagePromise.then(function(pdfPage) { - downloadedPromise.then(function () { - var event = document.createEvent('CustomEvent'); - event.initCustomEvent('documentload', true, true, {}); - window.dispatchEvent(event); - }); + zoomOut: function pdfViewZoomOut(ticks) { + var newScale = this.pdfViewer.currentScale; + do { + newScale = (newScale / DEFAULT_SCALE_DELTA).toFixed(2); + newScale = Math.floor(newScale * 10) / 10; + newScale = Math.max(MIN_SCALE, newScale); + } while (--ticks && newScale > MIN_SCALE); + this.setScale(newScale, true); + }, - self.loadingBar.setWidth(document.getElementById('viewer')); + get currentScaleValue() { + return this.pdfViewer.currentScaleValue; + }, - self.findController.resolveFirstPage(); + get pagesCount() { + return this.pdfDocument.numPages; + }, - if (!PDFJS.disableHistory && !self.isViewerEmbedded) { - // The browsing history is only enabled when the viewer is standalone, - // i.e. not when it is embedded in a web page. - PDFHistory.initialize(self.documentFingerprint, self); - } - }); + set page(val) { + this.pdfViewer.currentPageNumber = val; + }, - // Fetch the necessary preference values. - var showPreviousViewOnLoad; - var showPreviousViewOnLoadPromise = - Preferences.get('showPreviousViewOnLoad').then(function (prefValue) { - showPreviousViewOnLoad = prefValue; - }); - var defaultZoomValue; - var defaultZoomValuePromise = - Preferences.get('defaultZoomValue').then(function (prefValue) { - defaultZoomValue = prefValue; - }); + get page() { + return this.pdfViewer.currentPageNumber; + }, - var storePromise = store.initializedPromise; - Promise.all([firstPagePromise, storePromise, showPreviousViewOnLoadPromise, - defaultZoomValuePromise]).then(function resolved() { - var storedHash = null; - if (showPreviousViewOnLoad && store.get('exists', false)) { - var pageNum = store.get('page', '1'); - var zoom = defaultZoomValue || - store.get('zoom', self.pdfViewer.currentScale); - var left = store.get('scrollLeft', '0'); - var top = store.get('scrollTop', '0'); + get supportsPrinting() { + var canvas = document.createElement('canvas'); + var value = 'mozPrintCallback' in canvas; + // shadow + Object.defineProperty(this, 'supportsPrinting', { value: value, + enumerable: true, + configurable: true, + writable: false }); + return value; + }, - storedHash = 'page=' + pageNum + '&zoom=' + zoom + ',' + - left + ',' + top; - } else if (defaultZoomValue) { - storedHash = 'page=1&zoom=' + defaultZoomValue; - } - self.setInitialView(storedHash, scale); + get supportsFullscreen() { + var doc = document.documentElement; + var support = doc.requestFullscreen || doc.mozRequestFullScreen || + doc.webkitRequestFullScreen || doc.msRequestFullscreen; - // Make all navigation keys work on document load, - // unless the viewer is embedded in a web page. - if (!self.isViewerEmbedded) { - self.pdfViewer.focus(); - self.pdfViewer.blur(); - } - }, function rejected(reason) { - console.error(reason); + if (document.fullscreenEnabled === false || + document.mozFullScreenEnabled === false || + document.webkitFullscreenEnabled === false || + document.msFullscreenEnabled === false) { + support = false; + } - firstPagePromise.then(function () { - self.setInitialView(null, scale); - }); - }); + Object.defineProperty(this, 'supportsFullscreen', { value: support, + enumerable: true, + configurable: true, + writable: false }); + return support; + }, - pagesPromise.then(function() { - if (self.supportsPrinting) { - pdfDocument.getJavaScript().then(function(javaScript) { - if (javaScript.length) { - console.warn('Warning: JavaScript is not supported'); - self.fallback(PDFJS.UNSUPPORTED_FEATURES.javaScript); - } - // Hack to support auto printing. - var regex = /\bprint\s*\(/g; - for (var i = 0, ii = javaScript.length; i < ii; i++) { - var js = javaScript[i]; - if (js && regex.test(js)) { - setTimeout(function() { - window.print(); - }); - return; - } - } - }); - } - }); + get supportsIntegratedFind() { + var support = false; + support = FirefoxCom.requestSync('supportsIntegratedFind'); + Object.defineProperty(this, 'supportsIntegratedFind', { value: support, + enumerable: true, + configurable: true, + writable: false }); + return support; + }, - // outline depends on pagesRefMap - var promises = [pagesPromise, this.animationStartedPromise]; - Promise.all(promises).then(function() { - pdfDocument.getOutline().then(function(outline) { - var outlineView = document.getElementById('outlineView'); - self.outline = new DocumentOutlineView({ - outline: outline, - outlineView: outlineView, - linkService: self - }); - document.getElementById('viewOutline').disabled = !outline; + get supportsDocumentFonts() { + var support = true; + support = FirefoxCom.requestSync('supportsDocumentFonts'); + Object.defineProperty(this, 'supportsDocumentFonts', { value: support, + enumerable: true, + configurable: true, + writable: false }); + return support; + }, - if (!outline && !outlineView.classList.contains('hidden')) { - self.switchSidebarView('thumbs'); - } - if (outline && - self.preferenceSidebarViewOnLoad === SidebarView.OUTLINE) { - self.switchSidebarView('outline', true); - } - }); - pdfDocument.getAttachments().then(function(attachments) { - var attachmentsView = document.getElementById('attachmentsView'); - self.attachments = new DocumentAttachmentsView({ - attachments: attachments, - attachmentsView: attachmentsView - }); - document.getElementById('viewAttachments').disabled = !attachments; + get supportsDocumentColors() { + var support = true; + support = FirefoxCom.requestSync('supportsDocumentColors'); + Object.defineProperty(this, 'supportsDocumentColors', { value: support, + enumerable: true, + configurable: true, + writable: false }); + return support; + }, - if (!attachments && !attachmentsView.classList.contains('hidden')) { - self.switchSidebarView('thumbs'); - } - if (attachments && - self.preferenceSidebarViewOnLoad === SidebarView.ATTACHMENTS) { - self.switchSidebarView('attachments', true); - } - }); - }); + get loadingBar() { + var bar = new ProgressBar('#loadingBar', {}); + Object.defineProperty(this, 'loadingBar', { value: bar, + enumerable: true, + configurable: true, + writable: false }); + return bar; + }, - if (self.preferenceSidebarViewOnLoad === SidebarView.THUMBS) { - Promise.all([firstPagePromise, onePageRendered]).then(function () { - self.switchSidebarView('thumbs', true); - }); + initPassiveLoading: function pdfViewInitPassiveLoading() { + function FirefoxComDataRangeTransport(length, initialData) { + PDFJS.PDFDataRangeTransport.call(this, length, initialData); } + FirefoxComDataRangeTransport.prototype = + Object.create(PDFJS.PDFDataRangeTransport.prototype); + FirefoxComDataRangeTransport.prototype.requestDataRange = + function FirefoxComDataRangeTransport_requestDataRange(begin, end) { + FirefoxCom.request('requestDataRange', { begin: begin, end: end }); + }; - pdfDocument.getMetadata().then(function(data) { - var info = data.info, metadata = data.metadata; - self.documentInfo = info; - self.metadata = metadata; - - // Provides some basic debug information - console.log('PDF ' + pdfDocument.fingerprint + ' [' + - info.PDFFormatVersion + ' ' + (info.Producer || '-').trim() + - ' / ' + (info.Creator || '-').trim() + ']' + - ' (PDF.js: ' + (PDFJS.version || '-') + - (!PDFJS.disableWebGL ? ' [WebGL]' : '') + ')'); - - var pdfTitle; - if (metadata && metadata.has('dc:title')) { - var title = metadata.get('dc:title'); - // Ghostscript sometimes return 'Untitled', sets the title to 'Untitled' - if (title !== 'Untitled') { - pdfTitle = title; - } - } - - if (!pdfTitle && info && info['Title']) { - pdfTitle = info['Title']; - } + var pdfDataRangeTransport; - if (pdfTitle) { - self.setTitle(pdfTitle + ' - ' + document.title); + window.addEventListener('message', function windowMessage(e) { + if (e.source !== null) { + // The message MUST originate from Chrome code. + console.warn('Rejected untrusted message from ' + e.origin); + return; } + var args = e.data; - if (info.IsAcroFormPresent) { - console.warn('Warning: AcroForm/XFA is not supported'); - self.fallback(PDFJS.UNSUPPORTED_FEATURES.forms); + if (typeof args !== 'object' || !('pdfjsLoadAction' in args)) { + return; } + switch (args.pdfjsLoadAction) { + case 'supportsRangedLoading': + pdfDataRangeTransport = + new FirefoxComDataRangeTransport(args.length, args.data); - var versionId = String(info.PDFFormatVersion).slice(-1) | 0; - var generatorId = 0; - var KNOWN_GENERATORS = ["acrobat distiller", "acrobat pdfwritter", - "adobe livecycle", "adobe pdf library", "adobe photoshop", "ghostscript", - "tcpdf", "cairo", "dvipdfm", "dvips", "pdftex", "pdfkit", "itext", - "prince", "quarkxpress", "mac os x", "microsoft", "openoffice", "oracle", - "luradocument", "pdf-xchange", "antenna house", "aspose.cells", "fpdf"]; - var generatorId = 0; - if (info.Producer) { - KNOWN_GENERATORS.some(function (generator, s, i) { - if (generator.indexOf(s) < 0) { - return false; + PDFViewerApplication.open(args.pdfUrl, 0, undefined, + pdfDataRangeTransport); + + if (args.length) { + DocumentProperties.setFileSize(args.length); } - generatorId = i + 1; - return true; - }.bind(null, info.Producer.toLowerCase())); + break; + case 'range': + pdfDataRangeTransport.onDataRange(args.begin, args.chunk); + break; + case 'rangeProgress': + pdfDataRangeTransport.onDataProgress(args.loaded); + break; + case 'progressiveRead': + pdfDataRangeTransport.onDataProgressiveRead(args.chunk); + break; + case 'progress': + PDFViewerApplication.progress(args.loaded / args.total); + break; + case 'complete': + if (!args.data) { + PDFViewerApplication.error(mozL10n.get('loading_error', null, + 'An error occurred while loading the PDF.'), e); + break; + } + PDFViewerApplication.open(args.data, 0); + break; } - var formType = !info.IsAcroFormPresent ? null : info.IsXFAPresent ? - 'xfa' : 'acroform'; - FirefoxCom.request('reportTelemetry', JSON.stringify({ - type: 'documentInfo', - version: versionId, - generator: generatorId, - formType: formType - })); }); + FirefoxCom.requestSync('initPassiveLoading', null); }, - setInitialView: function pdfViewSetInitialView(storedHash, scale) { - this.isInitialViewSet = true; + setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) { + this.url = url; + try { + this.setTitle(decodeURIComponent(getFileName(url)) || url); + } catch (e) { + // decodeURIComponent may throw URIError, + // fall back to using the unprocessed url in that case + this.setTitle(url); + } + }, - // When opening a new file (when one is already loaded in the viewer): - // Reset 'currentPageNumber', since otherwise the page's scale will be wrong - // if 'currentPageNumber' is larger than the number of pages in the file. - document.getElementById('pageNumber').value = - this.pdfViewer.currentPageNumber = 1; + setTitle: function pdfViewSetTitle(title) { + document.title = title; + }, - if (PDFHistory.initialDestination) { - this.navigateTo(PDFHistory.initialDestination); - PDFHistory.initialDestination = null; - } else if (this.initialBookmark) { - this.setHash(this.initialBookmark); - PDFHistory.push({ hash: this.initialBookmark }, !!this.initialBookmark); - this.initialBookmark = null; - } else if (storedHash) { - this.setHash(storedHash); - } else if (scale) { - this.setScale(scale, true); - this.page = 1; + close: function pdfViewClose() { + var errorWrapper = document.getElementById('errorWrapper'); + errorWrapper.setAttribute('hidden', 'true'); + + if (!this.pdfDocument) { + return; } - if (this.pdfViewer.currentScale === UNKNOWN_SCALE) { - // Scale was not initialized: invalid bookmark or scale was not specified. - // Setting the default one. - this.setScale(DEFAULT_SCALE, true); + this.pdfDocument.destroy(); + this.pdfDocument = null; + + this.pdfThumbnailViewer.setDocument(null); + this.pdfViewer.setDocument(null); + + if (typeof PDFBug !== 'undefined') { + PDFBug.cleanup(); } }, - cleanup: function pdfViewCleanup() { - this.pdfViewer.cleanup(); - this.pdfThumbnailViewer.cleanup(); - this.pdfDocument.cleanup(); - }, + // TODO(mack): This function signature should really be pdfViewOpen(url, args) + open: function pdfViewOpen(file, scale, password, + pdfDataRangeTransport, args) { + if (this.pdfDocument) { + // Reload the preferences if a document was previously opened. + Preferences.reload(); + } + this.close(); - forceRendering: function pdfViewForceRendering() { - this.pdfRenderingQueue.printing = this.printing; - this.pdfRenderingQueue.isThumbnailViewEnabled = this.sidebarOpen; - this.pdfRenderingQueue.renderHighestPriority(); + var parameters = {password: password}; + if (typeof file === 'string') { // URL + this.setTitleUsingUrl(file); + parameters.url = file; + } else if (file && 'byteLength' in file) { // ArrayBuffer + parameters.data = file; + } else if (file.url && file.originalUrl) { + this.setTitleUsingUrl(file.originalUrl); + parameters.url = file.url; + } + if (args) { + for (var prop in args) { + parameters[prop] = args[prop]; + } + } + + var self = this; + self.loading = true; + self.downloadComplete = false; + + var passwordNeeded = function passwordNeeded(updatePassword, reason) { + PasswordPrompt.updatePassword = updatePassword; + PasswordPrompt.reason = reason; + PasswordPrompt.open(); + }; + + function getDocumentProgress(progressData) { + self.progress(progressData.loaded / progressData.total); + } + + PDFJS.getDocument(parameters, pdfDataRangeTransport, passwordNeeded, + getDocumentProgress).then( + function getDocumentCallback(pdfDocument) { + self.load(pdfDocument, scale); + self.loading = false; + }, + function getDocumentError(exception) { + var message = exception && exception.message; + var loadingErrorMessage = mozL10n.get('loading_error', null, + 'An error occurred while loading the PDF.'); + + if (exception instanceof PDFJS.InvalidPDFException) { + // change error message also for other builds + loadingErrorMessage = mozL10n.get('invalid_file_error', null, + 'Invalid or corrupted PDF file.'); + } else if (exception instanceof PDFJS.MissingPDFException) { + // special message for missing PDF's + loadingErrorMessage = mozL10n.get('missing_file_error', null, + 'Missing PDF file.'); + } else if (exception instanceof PDFJS.UnexpectedResponseException) { + loadingErrorMessage = mozL10n.get('unexpected_response_error', null, + 'Unexpected server response.'); + } + + var moreInfo = { + message: message + }; + self.error(loadingErrorMessage, moreInfo); + self.loading = false; + } + ); + + if (args && args.length) { + DocumentProperties.setFileSize(args.length); + } }, - setHash: function pdfViewSetHash(hash) { - if (!this.isInitialViewSet) { - this.initialBookmark = hash; - return; + download: function pdfViewDownload() { + function downloadByUrl() { + downloadManager.downloadUrl(url, filename); } - var validFitZoomValues = ['Fit','FitB','FitH','FitBH', - 'FitV','FitBV','FitR']; + var url = this.url.split('#')[0]; + var filename = getPDFFileNameFromURL(url); + var downloadManager = new DownloadManager(); + downloadManager.onerror = function (err) { + // This error won't really be helpful because it's likely the + // fallback won't work either (or is already open). + PDFViewerApplication.error('PDF failed to download.'); + }; - if (!hash) { + if (!this.pdfDocument) { // the PDF is not ready yet + downloadByUrl(); return; } - if (hash.indexOf('=') >= 0) { - var params = this.parseQueryString(hash); - // borrowing syntax from "Parameters for Opening PDF Files" - if ('nameddest' in params) { - PDFHistory.updateNextHashParam(params.nameddest); - this.navigateTo(params.nameddest); - return; - } - var pageNumber, dest; - if ('page' in params) { - pageNumber = (params.page | 0) || 1; - } - if ('zoom' in params) { - var zoomArgs = params.zoom.split(','); // scale,left,top - // building destination array + if (!this.downloadComplete) { // the PDF is still downloading + downloadByUrl(); + return; + } - // If the zoom value, it has to get divided by 100. If it is a string, - // it should stay as it is. - var zoomArg = zoomArgs[0]; - var zoomArgNumber = parseFloat(zoomArg); - var destName = 'XYZ'; - if (zoomArgNumber) { - zoomArg = zoomArgNumber / 100; - } else if (validFitZoomValues.indexOf(zoomArg) >= 0) { - destName = zoomArg; + this.pdfDocument.getData().then( + function getDataSuccess(data) { + var blob = PDFJS.createBlob(data, 'application/pdf'); + downloadManager.download(blob, url, filename); + }, + downloadByUrl // Error occurred try downloading with just the url. + ).then(null, downloadByUrl); + }, + + fallback: function pdfViewFallback(featureId) { + // Only trigger the fallback once so we don't spam the user with messages + // for one PDF. + if (this.fellback) + return; + this.fellback = true; + var url = this.url.split('#')[0]; + FirefoxCom.request('fallback', { featureId: featureId, url: url }, + function response(download) { + if (!download) { + return; } - dest = [null, { name: destName }, - zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null, - zoomArgs.length > 2 ? (zoomArgs[2] | 0) : null, - zoomArg]; - } - if (dest) { - this.pdfViewer.scrollPageIntoView(pageNumber || this.page, dest); - } else if (pageNumber) { - this.page = pageNumber; // simple page - } - if ('pagemode' in params) { - if (params.pagemode === 'thumbs' || params.pagemode === 'bookmarks' || - params.pagemode === 'attachments') { - this.switchSidebarView((params.pagemode === 'bookmarks' ? - 'outline' : params.pagemode), true); - } else if (params.pagemode === 'none' && this.sidebarOpen) { - document.getElementById('sidebarToggle').click(); + PDFViewerApplication.download(); + }); + }, + + navigateTo: function pdfViewNavigateTo(dest) { + var destString = ''; + var self = this; + + var goToDestination = function(destRef) { + self.pendingRefStr = null; + // dest array looks like that: + var pageNumber = destRef instanceof Object ? + self.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] : + (destRef + 1); + if (pageNumber) { + if (pageNumber > self.pagesCount) { + pageNumber = self.pagesCount; } + self.pdfViewer.scrollPageIntoView(pageNumber, dest); + + // Update the browsing history. + PDFHistory.push({ dest: dest, hash: destString, page: pageNumber }); + } else { + self.pdfDocument.getPageIndex(destRef).then(function (pageIndex) { + var pageNum = pageIndex + 1; + self.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] = pageNum; + goToDestination(destRef); + }); } - } else if (/^\d+$/.test(hash)) { // page number - this.page = hash; - } else { // named destination - PDFHistory.updateNextHashParam(unescape(hash)); - this.navigateTo(unescape(hash)); - } - }, + }; - switchSidebarView: function pdfViewSwitchSidebarView(view, openSidebar) { - if (openSidebar && !this.sidebarOpen) { - document.getElementById('sidebarToggle').click(); + var destinationPromise; + if (typeof dest === 'string') { + destString = dest; + destinationPromise = this.pdfDocument.getDestination(dest); + } else { + destinationPromise = Promise.resolve(dest); } - var thumbsView = document.getElementById('thumbnailView'); - var outlineView = document.getElementById('outlineView'); - var attachmentsView = document.getElementById('attachmentsView'); - - var thumbsButton = document.getElementById('viewThumbnail'); - var outlineButton = document.getElementById('viewOutline'); - var attachmentsButton = document.getElementById('viewAttachments'); + destinationPromise.then(function(destination) { + dest = destination; + if (!(destination instanceof Array)) { + return; // invalid destination + } + goToDestination(destination[0]); + }); + }, - switch (view) { - case 'thumbs': - var wasAnotherViewVisible = thumbsView.classList.contains('hidden'); + executeNamedAction: function pdfViewExecuteNamedAction(action) { + // See PDF reference, table 8.45 - Named action + switch (action) { + case 'GoToPage': + document.getElementById('pageNumber').focus(); + break; - thumbsButton.classList.add('toggled'); - outlineButton.classList.remove('toggled'); - attachmentsButton.classList.remove('toggled'); - thumbsView.classList.remove('hidden'); - outlineView.classList.add('hidden'); - attachmentsView.classList.add('hidden'); + case 'GoBack': + PDFHistory.back(); + break; - this.forceRendering(); + case 'GoForward': + PDFHistory.forward(); + break; - if (wasAnotherViewVisible) { - this.pdfThumbnailViewer.ensureThumbnailVisible(this.page); + case 'Find': + if (!this.supportsIntegratedFind) { + this.findBar.toggle(); } break; - case 'outline': - thumbsButton.classList.remove('toggled'); - outlineButton.classList.add('toggled'); - attachmentsButton.classList.remove('toggled'); - thumbsView.classList.add('hidden'); - outlineView.classList.remove('hidden'); - attachmentsView.classList.add('hidden'); + case 'NextPage': + this.page++; + break; - if (outlineButton.getAttribute('disabled')) { - return; - } + case 'PrevPage': + this.page--; break; - case 'attachments': - thumbsButton.classList.remove('toggled'); - outlineButton.classList.remove('toggled'); - attachmentsButton.classList.add('toggled'); - thumbsView.classList.add('hidden'); - outlineView.classList.add('hidden'); - attachmentsView.classList.remove('hidden'); + case 'LastPage': + this.page = this.pagesCount; + break; - if (attachmentsButton.getAttribute('disabled')) { - return; - } + case 'FirstPage': + this.page = 1; break; + + default: + break; // No action according to spec } }, - // Helper function to parse query string (e.g. ?param1=value&parm2=...). - parseQueryString: function pdfViewParseQueryString(query) { - var parts = query.split('&'); - var params = {}; - for (var i = 0, ii = parts.length; i < ii; ++i) { - var param = parts[i].split('='); - var key = param[0].toLowerCase(); - var value = param.length > 1 ? param[1] : null; - params[decodeURIComponent(key)] = decodeURIComponent(value); + getDestinationHash: function pdfViewGetDestinationHash(dest) { + if (typeof dest === 'string') { + return this.getAnchorUrl('#' + escape(dest)); } - return params; + if (dest instanceof Array) { + var destRef = dest[0]; // see navigateTo method for dest format + var pageNumber = destRef instanceof Object ? + this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] : + (destRef + 1); + if (pageNumber) { + var pdfOpenParams = this.getAnchorUrl('#page=' + pageNumber); + var destKind = dest[1]; + if (typeof destKind === 'object' && 'name' in destKind && + destKind.name === 'XYZ') { + var scale = (dest[4] || this.currentScaleValue); + var scaleNumber = parseFloat(scale); + if (scaleNumber) { + scale = scaleNumber * 100; + } + pdfOpenParams += '&zoom=' + scale; + if (dest[2] || dest[3]) { + pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0); + } + } + return pdfOpenParams; + } + } + return ''; }, - beforePrint: function pdfViewSetupBeforePrint() { - if (!this.supportsPrinting) { - var printMessage = mozL10n.get('printing_not_supported', null, - 'Warning: Printing is not fully supported by this browser.'); - this.error(printMessage); - return; + /** + * Prefix the full url on anchor links to make sure that links are resolved + * relative to the current URL instead of the one defined in . + * @param {String} anchor The anchor hash, including the #. + */ + getAnchorUrl: function getAnchorUrl(anchor) { + return this.url.split('#')[0] + anchor; + }, + + /** + * Show the error box. + * @param {String} message A message that is human readable. + * @param {Object} moreInfo (optional) Further information about the error + * that is more technical. Should have a 'message' + * and optionally a 'stack' property. + */ + error: function pdfViewError(message, moreInfo) { + var moreInfoText = mozL10n.get('error_version_info', + {version: PDFJS.version || '?', build: PDFJS.build || '?'}, + 'PDF.js v{{version}} (build: {{build}})') + '\n'; + if (moreInfo) { + moreInfoText += + mozL10n.get('error_message', {message: moreInfo.message}, + 'Message: {{message}}'); + if (moreInfo.stack) { + moreInfoText += '\n' + + mozL10n.get('error_stack', {stack: moreInfo.stack}, + 'Stack: {{stack}}'); + } else { + if (moreInfo.filename) { + moreInfoText += '\n' + + mozL10n.get('error_file', {file: moreInfo.filename}, + 'File: {{file}}'); + } + if (moreInfo.lineNumber) { + moreInfoText += '\n' + + mozL10n.get('error_line', {line: moreInfo.lineNumber}, + 'Line: {{line}}'); + } + } } - var alertNotReady = false; - var i, ii; - if (!this.pagesCount) { - alertNotReady = true; - } else { - for (i = 0, ii = this.pagesCount; i < ii; ++i) { - if (!this.pdfViewer.getPageView(i).pdfPage) { - alertNotReady = true; - break; + console.error(message + '\n' + moreInfoText); + this.fallback(); + }, + + progress: function pdfViewProgress(level) { + var percent = Math.round(level * 100); + // When we transition from full request to range requests, it's possible + // that we discard some of the loaded data. This can cause the loading + // bar to move backwards. So prevent this by only updating the bar if it + // increases. + if (percent > this.loadingBar.percent || isNaN(percent)) { + this.loadingBar.percent = percent; + + // When disableAutoFetch is enabled, it's not uncommon for the entire file + // to never be fetched (depends on e.g. the file structure). In this case + // the loading bar will not be completely filled, nor will it be hidden. + // To prevent displaying a partially filled loading bar permanently, we + // hide it when no data has been loaded during a certain amount of time. + if (PDFJS.disableAutoFetch && percent) { + if (this.disableAutoFetchLoadingBarTimeout) { + clearTimeout(this.disableAutoFetchLoadingBarTimeout); + this.disableAutoFetchLoadingBarTimeout = null; } + this.loadingBar.show(); + + this.disableAutoFetchLoadingBarTimeout = setTimeout(function () { + this.loadingBar.hide(); + this.disableAutoFetchLoadingBarTimeout = null; + }.bind(this), DISABLE_AUTO_FETCH_LOADING_BAR_TIMEOUT); } } - if (alertNotReady) { - var notReadyMessage = mozL10n.get('printing_not_ready', null, - 'Warning: The PDF is not fully loaded for printing.'); - window.alert(notReadyMessage); - return; - } + }, - this.printing = true; - this.forceRendering(); + load: function pdfViewLoad(pdfDocument, scale) { + var self = this; + scale = scale || UNKNOWN_SCALE; - var body = document.querySelector('body'); - body.setAttribute('data-mozPrintCallback', true); - for (i = 0, ii = this.pagesCount; i < ii; ++i) { - this.pdfViewer.getPageView(i).beforePrint(); - } + this.findController.reset(); - FirefoxCom.request('reportTelemetry', JSON.stringify({ - type: 'print' - })); - }, + this.pdfDocument = pdfDocument; - afterPrint: function pdfViewSetupAfterPrint() { - var div = document.getElementById('printContainer'); - while (div.hasChildNodes()) { - div.removeChild(div.lastChild); - } + DocumentProperties.url = this.url; + DocumentProperties.pdfDocument = pdfDocument; + DocumentProperties.resolveDataAvailable(); - this.printing = false; - this.forceRendering(); - }, + var downloadedPromise = pdfDocument.getDownloadInfo().then(function() { + self.downloadComplete = true; + self.loadingBar.hide(); + }); - setScale: function (value, resetAutoSettings) { - this.updateScaleControls = !!resetAutoSettings; - this.pdfViewer.currentScaleValue = value; - this.updateScaleControls = true; - }, + var pagesCount = pdfDocument.numPages; + document.getElementById('numPages').textContent = + mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}'); + document.getElementById('pageNumber').max = pagesCount; - rotatePages: function pdfViewRotatePages(delta) { - var pageNumber = this.page; + var id = this.documentFingerprint = pdfDocument.fingerprint; + var store = this.store = new ViewHistory(id); - this.pageRotation = (this.pageRotation + 360 + delta) % 360; - this.pdfViewer.pagesRotation = this.pageRotation; - this.pdfThumbnailViewer.pagesRotation = this.pageRotation; + var pdfViewer = this.pdfViewer; + pdfViewer.currentScale = scale; + pdfViewer.setDocument(pdfDocument); + var firstPagePromise = pdfViewer.firstPagePromise; + var pagesPromise = pdfViewer.pagesPromise; + var onePageRendered = pdfViewer.onePageRendered; - this.forceRendering(); + this.pageRotation = 0; + this.isInitialViewSet = false; + this.pagesRefMap = pdfViewer.pagesRefMap; - this.pdfViewer.scrollPageIntoView(pageNumber); - }, + this.pdfThumbnailViewer.setDocument(pdfDocument); - /** - * This function flips the page in presentation mode if the user scrolls up - * or down with large enough motion and prevents page flipping too often. - * - * @this {PDFView} - * @param {number} mouseScrollDelta The delta value from the mouse event. - */ - mouseScroll: function pdfViewMouseScroll(mouseScrollDelta) { - var MOUSE_SCROLL_COOLDOWN_TIME = 50; + firstPagePromise.then(function(pdfPage) { + downloadedPromise.then(function () { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('documentload', true, true, {}); + window.dispatchEvent(event); + }); - var currentTime = (new Date()).getTime(); - var storedTime = this.mouseScrollTimeStamp; + self.loadingBar.setWidth(document.getElementById('viewer')); - // In case one page has already been flipped there is a cooldown time - // which has to expire before next page can be scrolled on to. - if (currentTime > storedTime && - currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) { - return; - } + self.findController.resolveFirstPage(); - // In case the user decides to scroll to the opposite direction than before - // clear the accumulated delta. - if ((this.mouseScrollDelta > 0 && mouseScrollDelta < 0) || - (this.mouseScrollDelta < 0 && mouseScrollDelta > 0)) { - this.clearMouseScrollState(); - } + if (!PDFJS.disableHistory && !self.isViewerEmbedded) { + // The browsing history is only enabled when the viewer is standalone, + // i.e. not when it is embedded in a web page. + PDFHistory.initialize(self.documentFingerprint, self); + } + }); - this.mouseScrollDelta += mouseScrollDelta; + // Fetch the necessary preference values. + var showPreviousViewOnLoad; + var showPreviousViewOnLoadPromise = + Preferences.get('showPreviousViewOnLoad').then(function (prefValue) { + showPreviousViewOnLoad = prefValue; + }); + var defaultZoomValue; + var defaultZoomValuePromise = + Preferences.get('defaultZoomValue').then(function (prefValue) { + defaultZoomValue = prefValue; + }); - var PAGE_FLIP_THRESHOLD = 120; - if (Math.abs(this.mouseScrollDelta) >= PAGE_FLIP_THRESHOLD) { + var storePromise = store.initializedPromise; + Promise.all([firstPagePromise, storePromise, showPreviousViewOnLoadPromise, + defaultZoomValuePromise]).then(function resolved() { + var storedHash = null; + if (showPreviousViewOnLoad && store.get('exists', false)) { + var pageNum = store.get('page', '1'); + var zoom = defaultZoomValue || + store.get('zoom', self.pdfViewer.currentScale); + var left = store.get('scrollLeft', '0'); + var top = store.get('scrollTop', '0'); - var PageFlipDirection = { - UP: -1, - DOWN: 1 - }; + storedHash = 'page=' + pageNum + '&zoom=' + zoom + ',' + + left + ',' + top; + } else if (defaultZoomValue) { + storedHash = 'page=1&zoom=' + defaultZoomValue; + } + self.setInitialView(storedHash, scale); - // In presentation mode scroll one page at a time. - var pageFlipDirection = (this.mouseScrollDelta > 0) ? - PageFlipDirection.UP : - PageFlipDirection.DOWN; - this.clearMouseScrollState(); - var currentPage = this.page; + // Make all navigation keys work on document load, + // unless the viewer is embedded in a web page. + if (!self.isViewerEmbedded) { + self.pdfViewer.focus(); + self.pdfViewer.blur(); + } + }, function rejected(reason) { + console.error(reason); - // In case we are already on the first or the last page there is no need - // to do anything. - if ((currentPage === 1 && pageFlipDirection === PageFlipDirection.UP) || - (currentPage === this.pagesCount && - pageFlipDirection === PageFlipDirection.DOWN)) { - return; + firstPagePromise.then(function () { + self.setInitialView(null, scale); + }); + }); + + pagesPromise.then(function() { + if (self.supportsPrinting) { + pdfDocument.getJavaScript().then(function(javaScript) { + if (javaScript.length) { + console.warn('Warning: JavaScript is not supported'); + self.fallback(PDFJS.UNSUPPORTED_FEATURES.javaScript); + } + // Hack to support auto printing. + var regex = /\bprint\s*\(/g; + for (var i = 0, ii = javaScript.length; i < ii; i++) { + var js = javaScript[i]; + if (js && regex.test(js)) { + setTimeout(function() { + window.print(); + }); + return; + } + } + }); } + }); - this.page += pageFlipDirection; - this.mouseScrollTimeStamp = currentTime; - } - }, + // outline depends on pagesRefMap + var promises = [pagesPromise, this.animationStartedPromise]; + Promise.all(promises).then(function() { + pdfDocument.getOutline().then(function(outline) { + var container = document.getElementById('outlineView'); + self.outline = new PDFOutlineView({ + container: container, + outline: outline, + linkService: self + }); + self.outline.render(); + document.getElementById('viewOutline').disabled = !outline; - /** - * This function clears the member attributes used with mouse scrolling in - * presentation mode. - * - * @this {PDFView} - */ - clearMouseScrollState: function pdfViewClearMouseScrollState() { - this.mouseScrollTimeStamp = 0; - this.mouseScrollDelta = 0; - } -}; + if (!outline && !container.classList.contains('hidden')) { + self.switchSidebarView('thumbs'); + } + if (outline && + self.preferenceSidebarViewOnLoad === SidebarView.OUTLINE) { + self.switchSidebarView('outline', true); + } + }); + pdfDocument.getAttachments().then(function(attachments) { + var container = document.getElementById('attachmentsView'); + self.attachments = new PDFAttachmentView({ + container: container, + attachments: attachments, + downloadManager: new DownloadManager() + }); + self.attachments.render(); + document.getElementById('viewAttachments').disabled = !attachments; + if (!attachments && !container.classList.contains('hidden')) { + self.switchSidebarView('thumbs'); + } + if (attachments && + self.preferenceSidebarViewOnLoad === SidebarView.ATTACHMENTS) { + self.switchSidebarView('attachments', true); + } + }); + }); -var THUMBNAIL_SCROLL_MARGIN = -19; + if (self.preferenceSidebarViewOnLoad === SidebarView.THUMBS) { + Promise.all([firstPagePromise, onePageRendered]).then(function () { + self.switchSidebarView('thumbs', true); + }); + } -/** - * @constructor - * @param container - * @param id - * @param defaultViewport - * @param linkService - * @param renderingQueue - * @param pageSource - * - * @implements {IRenderableView} - */ -var ThumbnailView = function thumbnailView(container, id, defaultViewport, - linkService, renderingQueue, - pageSource) { - var anchor = document.createElement('a'); - anchor.href = linkService.getAnchorUrl('#page=' + id); - anchor.title = mozL10n.get('thumb_page_title', {page: id}, 'Page {{page}}'); - anchor.onclick = function stopNavigation() { - linkService.page = id; - return false; - }; + pdfDocument.getMetadata().then(function(data) { + var info = data.info, metadata = data.metadata; + self.documentInfo = info; + self.metadata = metadata; + + // Provides some basic debug information + console.log('PDF ' + pdfDocument.fingerprint + ' [' + + info.PDFFormatVersion + ' ' + (info.Producer || '-').trim() + + ' / ' + (info.Creator || '-').trim() + ']' + + ' (PDF.js: ' + (PDFJS.version || '-') + + (!PDFJS.disableWebGL ? ' [WebGL]' : '') + ')'); - this.pdfPage = undefined; - this.viewport = defaultViewport; - this.pdfPageRotate = defaultViewport.rotation; - - this.rotation = 0; - this.pageWidth = this.viewport.width; - this.pageHeight = this.viewport.height; - this.pageRatio = this.pageWidth / this.pageHeight; - this.id = id; - this.renderingId = 'thumbnail' + id; - - this.canvasWidth = 98; - this.canvasHeight = this.canvasWidth / this.pageWidth * this.pageHeight; - this.scale = (this.canvasWidth / this.pageWidth); - - var div = this.el = document.createElement('div'); - div.id = 'thumbnailContainer' + id; - div.className = 'thumbnail'; - - if (id === 1) { - // Highlight the thumbnail of the first page when no page number is - // specified (or exists in cache) when the document is loaded. - div.classList.add('selected'); - } - - var ring = document.createElement('div'); - ring.className = 'thumbnailSelectionRing'; - ring.style.width = this.canvasWidth + 'px'; - ring.style.height = this.canvasHeight + 'px'; - - div.appendChild(ring); - anchor.appendChild(div); - container.appendChild(anchor); - - this.hasImage = false; - this.renderingState = RenderingStates.INITIAL; - this.renderingQueue = renderingQueue; - this.pageSource = pageSource; - - this.setPdfPage = function thumbnailViewSetPdfPage(pdfPage) { - this.pdfPage = pdfPage; - this.pdfPageRotate = pdfPage.rotate; - var totalRotation = (this.rotation + this.pdfPageRotate) % 360; - this.viewport = pdfPage.getViewport(1, totalRotation); - this.update(); - }; + var pdfTitle; + if (metadata && metadata.has('dc:title')) { + var title = metadata.get('dc:title'); + // Ghostscript sometimes return 'Untitled', sets the title to 'Untitled' + if (title !== 'Untitled') { + pdfTitle = title; + } + } - this.update = function thumbnailViewUpdate(rotation) { - if (rotation !== undefined) { - this.rotation = rotation; - } - var totalRotation = (this.rotation + this.pdfPageRotate) % 360; - this.viewport = this.viewport.clone({ - scale: 1, - rotation: totalRotation - }); - this.pageWidth = this.viewport.width; - this.pageHeight = this.viewport.height; - this.pageRatio = this.pageWidth / this.pageHeight; + if (!pdfTitle && info && info['Title']) { + pdfTitle = info['Title']; + } - this.canvasHeight = this.canvasWidth / this.pageWidth * this.pageHeight; - this.scale = (this.canvasWidth / this.pageWidth); + if (pdfTitle) { + self.setTitle(pdfTitle + ' - ' + document.title); + } - div.removeAttribute('data-loaded'); - ring.textContent = ''; - ring.style.width = this.canvasWidth + 'px'; - ring.style.height = this.canvasHeight + 'px'; + if (info.IsAcroFormPresent) { + console.warn('Warning: AcroForm/XFA is not supported'); + self.fallback(PDFJS.UNSUPPORTED_FEATURES.forms); + } - this.hasImage = false; - this.renderingState = RenderingStates.INITIAL; - this.resume = null; - }; + var versionId = String(info.PDFFormatVersion).slice(-1) | 0; + var generatorId = 0; + var KNOWN_GENERATORS = ["acrobat distiller", "acrobat pdfwritter", + "adobe livecycle", "adobe pdf library", "adobe photoshop", "ghostscript", + "tcpdf", "cairo", "dvipdfm", "dvips", "pdftex", "pdfkit", "itext", + "prince", "quarkxpress", "mac os x", "microsoft", "openoffice", "oracle", + "luradocument", "pdf-xchange", "antenna house", "aspose.cells", "fpdf"]; + var generatorId = 0; + if (info.Producer) { + KNOWN_GENERATORS.some(function (generator, s, i) { + if (generator.indexOf(s) < 0) { + return false; + } + generatorId = i + 1; + return true; + }.bind(null, info.Producer.toLowerCase())); + } + var formType = !info.IsAcroFormPresent ? null : info.IsXFAPresent ? + 'xfa' : 'acroform'; + FirefoxCom.request('reportTelemetry', JSON.stringify({ + type: 'documentInfo', + version: versionId, + generator: generatorId, + formType: formType + })); + }); + }, - this.getPageDrawContext = function thumbnailViewGetPageDrawContext() { - var canvas = document.createElement('canvas'); - canvas.id = 'thumbnail' + id; + setInitialView: function pdfViewSetInitialView(storedHash, scale) { + this.isInitialViewSet = true; - canvas.width = this.canvasWidth; - canvas.height = this.canvasHeight; - canvas.className = 'thumbnailImage'; - canvas.setAttribute('aria-label', mozL10n.get('thumb_page_canvas', - {page: id}, 'Thumbnail of Page {{page}}')); + // When opening a new file (when one is already loaded in the viewer): + // Reset 'currentPageNumber', since otherwise the page's scale will be wrong + // if 'currentPageNumber' is larger than the number of pages in the file. + document.getElementById('pageNumber').value = + this.pdfViewer.currentPageNumber = 1; - div.setAttribute('data-loaded', true); + if (PDFHistory.initialDestination) { + this.navigateTo(PDFHistory.initialDestination); + PDFHistory.initialDestination = null; + } else if (this.initialBookmark) { + this.setHash(this.initialBookmark); + PDFHistory.push({ hash: this.initialBookmark }, !!this.initialBookmark); + this.initialBookmark = null; + } else if (storedHash) { + this.setHash(storedHash); + } else if (scale) { + this.setScale(scale, true); + this.page = 1; + } - ring.appendChild(canvas); + if (this.pdfViewer.currentScale === UNKNOWN_SCALE) { + // Scale was not initialized: invalid bookmark or scale was not specified. + // Setting the default one. + this.setScale(DEFAULT_SCALE, true); + } + }, - var ctx = canvas.getContext('2d'); - ctx.save(); - ctx.fillStyle = 'rgb(255, 255, 255)'; - ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight); - ctx.restore(); - return ctx; - }; + cleanup: function pdfViewCleanup() { + this.pdfViewer.cleanup(); + this.pdfThumbnailViewer.cleanup(); + this.pdfDocument.cleanup(); + }, - this.drawingRequired = function thumbnailViewDrawingRequired() { - return !this.hasImage; - }; + forceRendering: function pdfViewForceRendering() { + this.pdfRenderingQueue.printing = this.printing; + this.pdfRenderingQueue.isThumbnailViewEnabled = this.sidebarOpen; + this.pdfRenderingQueue.renderHighestPriority(); + }, - this.draw = function thumbnailViewDraw(callback) { - if (!this.pdfPage) { - var promise = this.pageSource.getPage(this.id); - promise.then(function(pdfPage) { - this.setPdfPage(pdfPage); - this.draw(callback); - }.bind(this)); + setHash: function pdfViewSetHash(hash) { + if (!this.isInitialViewSet) { + this.initialBookmark = hash; return; } - if (this.renderingState !== RenderingStates.INITIAL) { - console.error('Must be in new state before drawing'); - } + var validFitZoomValues = ['Fit','FitB','FitH','FitBH', + 'FitV','FitBV','FitR']; - this.renderingState = RenderingStates.RUNNING; - if (this.hasImage) { - callback(); + if (!hash) { return; } - var self = this; - var ctx = this.getPageDrawContext(); - var drawViewport = this.viewport.clone({ scale: this.scale }); - var renderContext = { - canvasContext: ctx, - viewport: drawViewport, - continueCallback: function(cont) { - if (!self.renderingQueue.isHighestPriority(self)) { - self.renderingState = RenderingStates.PAUSED; - self.resume = function() { - self.renderingState = RenderingStates.RUNNING; - cont(); - }; - return; - } - cont(); + if (hash.indexOf('=') >= 0) { + var params = this.parseQueryString(hash); + // borrowing syntax from "Parameters for Opening PDF Files" + if ('nameddest' in params) { + PDFHistory.updateNextHashParam(params.nameddest); + this.navigateTo(params.nameddest); + return; } - }; - this.pdfPage.render(renderContext).promise.then( - function pdfPageRenderCallback() { - self.renderingState = RenderingStates.FINISHED; - callback(); - }, - function pdfPageRenderError(error) { - self.renderingState = RenderingStates.FINISHED; - callback(); + var pageNumber, dest; + if ('page' in params) { + pageNumber = (params.page | 0) || 1; } - ); - this.hasImage = true; - }; - - function getTempCanvas(width, height) { - var tempCanvas = ThumbnailView.tempImageCache; - if (!tempCanvas) { - tempCanvas = document.createElement('canvas'); - ThumbnailView.tempImageCache = tempCanvas; - } - tempCanvas.width = width; - tempCanvas.height = height; - return tempCanvas; - } - - this.setImage = function thumbnailViewSetImage(img) { - if (!this.pdfPage) { - var promise = this.pageSource.getPage(); - promise.then(function(pdfPage) { - this.setPdfPage(pdfPage); - this.setImage(img); - }.bind(this)); - return; - } - if (this.hasImage || !img) { - return; - } - this.renderingState = RenderingStates.FINISHED; - var ctx = this.getPageDrawContext(); + if ('zoom' in params) { + var zoomArgs = params.zoom.split(','); // scale,left,top + // building destination array - var reducedImage = img; - var reducedWidth = img.width; - var reducedHeight = img.height; - - // drawImage does an awful job of rescaling the image, doing it gradually - var MAX_SCALE_FACTOR = 2.0; - if (Math.max(img.width / ctx.canvas.width, - img.height / ctx.canvas.height) > MAX_SCALE_FACTOR) { - reducedWidth >>= 1; - reducedHeight >>= 1; - reducedImage = getTempCanvas(reducedWidth, reducedHeight); - var reducedImageCtx = reducedImage.getContext('2d'); - reducedImageCtx.drawImage(img, 0, 0, img.width, img.height, - 0, 0, reducedWidth, reducedHeight); - while (Math.max(reducedWidth / ctx.canvas.width, - reducedHeight / ctx.canvas.height) > MAX_SCALE_FACTOR) { - reducedImageCtx.drawImage(reducedImage, - 0, 0, reducedWidth, reducedHeight, - 0, 0, reducedWidth >> 1, reducedHeight >> 1); - reducedWidth >>= 1; - reducedHeight >>= 1; + // If the zoom value, it has to get divided by 100. If it is a string, + // it should stay as it is. + var zoomArg = zoomArgs[0]; + var zoomArgNumber = parseFloat(zoomArg); + var destName = 'XYZ'; + if (zoomArgNumber) { + zoomArg = zoomArgNumber / 100; + } else if (validFitZoomValues.indexOf(zoomArg) >= 0) { + destName = zoomArg; + } + dest = [null, { name: destName }, + zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null, + zoomArgs.length > 2 ? (zoomArgs[2] | 0) : null, + zoomArg]; + } + if (dest) { + this.pdfViewer.scrollPageIntoView(pageNumber || this.page, dest); + } else if (pageNumber) { + this.page = pageNumber; // simple page } + if ('pagemode' in params) { + if (params.pagemode === 'thumbs' || params.pagemode === 'bookmarks' || + params.pagemode === 'attachments') { + this.switchSidebarView((params.pagemode === 'bookmarks' ? + 'outline' : params.pagemode), true); + } else if (params.pagemode === 'none' && this.sidebarOpen) { + document.getElementById('sidebarToggle').click(); + } + } + } else if (/^\d+$/.test(hash)) { // page number + this.page = hash; + } else { // named destination + PDFHistory.updateNextHashParam(unescape(hash)); + this.navigateTo(unescape(hash)); } + }, - ctx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, - 0, 0, ctx.canvas.width, ctx.canvas.height); - - this.hasImage = true; - }; -}; - -ThumbnailView.tempImageCache = null; - -/** - * @typedef {Object} PDFThumbnailViewerOptions - * @property {HTMLDivElement} container - The container for the thumbs elements. - * @property {IPDFLinkService} linkService - The navigation/linking service. - * @property {PDFRenderingQueue} renderingQueue - The rendering queue object. - */ - -/** - * Simple viewer control to display thumbs for pages. - * @class - */ -var PDFThumbnailViewer = (function pdfThumbnailViewer() { - /** - * @constructs - * @param {PDFThumbnailViewerOptions} options - */ - function PDFThumbnailViewer(options) { - this.container = options.container; - this.renderingQueue = options.renderingQueue; - this.linkService = options.linkService; + switchSidebarView: function pdfViewSwitchSidebarView(view, openSidebar) { + if (openSidebar && !this.sidebarOpen) { + document.getElementById('sidebarToggle').click(); + } + var thumbsView = document.getElementById('thumbnailView'); + var outlineView = document.getElementById('outlineView'); + var attachmentsView = document.getElementById('attachmentsView'); - this.scroll = watchScroll(this.container, this._scrollUpdated.bind(this)); - this._resetView(); - } + var thumbsButton = document.getElementById('viewThumbnail'); + var outlineButton = document.getElementById('viewOutline'); + var attachmentsButton = document.getElementById('viewAttachments'); - PDFThumbnailViewer.prototype = { - _scrollUpdated: function PDFThumbnailViewer_scrollUpdated() { - this.renderingQueue.renderHighestPriority(); - }, + switch (view) { + case 'thumbs': + var wasAnotherViewVisible = thumbsView.classList.contains('hidden'); - getThumbnail: function PDFThumbnailViewer_getThumbnail(index) { - return this.thumbnails[index]; - }, + thumbsButton.classList.add('toggled'); + outlineButton.classList.remove('toggled'); + attachmentsButton.classList.remove('toggled'); + thumbsView.classList.remove('hidden'); + outlineView.classList.add('hidden'); + attachmentsView.classList.add('hidden'); - _getVisibleThumbs: function PDFThumbnailViewer_getVisibleThumbs() { - return getVisibleElements(this.container, this.thumbnails); - }, + this.forceRendering(); - scrollThumbnailIntoView: function (page) { - var selected = document.querySelector('.thumbnail.selected'); - if (selected) { - selected.classList.remove('selected'); - } - var thumbnail = document.getElementById('thumbnailContainer' + page); - thumbnail.classList.add('selected'); - var visibleThumbs = this._getVisibleThumbs(); - var numVisibleThumbs = visibleThumbs.views.length; + if (wasAnotherViewVisible) { + this.pdfThumbnailViewer.ensureThumbnailVisible(this.page); + } + break; - // If the thumbnail isn't currently visible, scroll it into view. - if (numVisibleThumbs > 0) { - var first = visibleThumbs.first.id; - // Account for only one thumbnail being visible. - var last = (numVisibleThumbs > 1 ? visibleThumbs.last.id : first); - if (page <= first || page >= last) { - scrollIntoView(thumbnail, { top: THUMBNAIL_SCROLL_MARGIN }); + case 'outline': + thumbsButton.classList.remove('toggled'); + outlineButton.classList.add('toggled'); + attachmentsButton.classList.remove('toggled'); + thumbsView.classList.add('hidden'); + outlineView.classList.remove('hidden'); + attachmentsView.classList.add('hidden'); + + if (outlineButton.getAttribute('disabled')) { + return; } - } - }, + break; - get pagesRotation() { - return this._pagesRotation; - }, + case 'attachments': + thumbsButton.classList.remove('toggled'); + outlineButton.classList.remove('toggled'); + attachmentsButton.classList.add('toggled'); + thumbsView.classList.add('hidden'); + outlineView.classList.add('hidden'); + attachmentsView.classList.remove('hidden'); - set pagesRotation(rotation) { - this._pagesRotation = rotation; - for (var i = 0, l = this.thumbnails.length; i < l; i++) { - var thumb = this.thumbnails[i]; - thumb.update(rotation); - } - }, + if (attachmentsButton.getAttribute('disabled')) { + return; + } + break; + } + }, - cleanup: function PDFThumbnailViewer_cleanup() { - ThumbnailView.tempImageCache = null; - }, + // Helper function to parse query string (e.g. ?param1=value&parm2=...). + parseQueryString: function pdfViewParseQueryString(query) { + var parts = query.split('&'); + var params = {}; + for (var i = 0, ii = parts.length; i < ii; ++i) { + var param = parts[i].split('='); + var key = param[0].toLowerCase(); + var value = param.length > 1 ? param[1] : null; + params[decodeURIComponent(key)] = decodeURIComponent(value); + } + return params; + }, - _resetView: function () { - this.thumbnails = []; - this._pagesRotation = 0; - }, + beforePrint: function pdfViewSetupBeforePrint() { + if (!this.supportsPrinting) { + var printMessage = mozL10n.get('printing_not_supported', null, + 'Warning: Printing is not fully supported by this browser.'); + this.error(printMessage); + return; + } - setDocument: function (pdfDocument) { - if (this.pdfDocument) { - // cleanup of the elements and views - var thumbsView = this.container; - while (thumbsView.hasChildNodes()) { - thumbsView.removeChild(thumbsView.lastChild); + var alertNotReady = false; + var i, ii; + if (!this.pagesCount) { + alertNotReady = true; + } else { + for (i = 0, ii = this.pagesCount; i < ii; ++i) { + if (!this.pdfViewer.getPageView(i).pdfPage) { + alertNotReady = true; + break; } - this._resetView(); } + } + if (alertNotReady) { + var notReadyMessage = mozL10n.get('printing_not_ready', null, + 'Warning: The PDF is not fully loaded for printing.'); + window.alert(notReadyMessage); + return; + } - this.pdfDocument = pdfDocument; - if (!pdfDocument) { - return Promise.resolve(); - } + this.printing = true; + this.forceRendering(); - return pdfDocument.getPage(1).then(function (firstPage) { - var pagesCount = pdfDocument.numPages; - var viewport = firstPage.getViewport(1.0); - for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) { - var pageSource = new PDFPageSource(pdfDocument, pageNum); - var thumbnail = new ThumbnailView(this.container, pageNum, - viewport.clone(), this.linkService, - this.renderingQueue, pageSource); - this.thumbnails.push(thumbnail); - } - }.bind(this)); - }, + var body = document.querySelector('body'); + body.setAttribute('data-mozPrintCallback', true); + for (i = 0, ii = this.pagesCount; i < ii; ++i) { + this.pdfViewer.getPageView(i).beforePrint(); + } - ensureThumbnailVisible: - function PDFThumbnailViewer_ensureThumbnailVisible(page) { - // Ensure that the thumbnail of the current page is visible - // when switching from another view. - scrollIntoView(document.getElementById('thumbnailContainer' + page)); - }, + FirefoxCom.request('reportTelemetry', JSON.stringify({ + type: 'print' + })); + }, - forceRendering: function () { - var visibleThumbs = this._getVisibleThumbs(); - var thumbView = this.renderingQueue.getHighestPriority(visibleThumbs, - this.thumbnails, - this.scroll.down); - if (thumbView) { - this.renderingQueue.renderView(thumbView); - return true; - } - return false; + afterPrint: function pdfViewSetupAfterPrint() { + var div = document.getElementById('printContainer'); + while (div.hasChildNodes()) { + div.removeChild(div.lastChild); } - }; - return PDFThumbnailViewer; -})(); + this.printing = false; + this.forceRendering(); + }, + setScale: function (value, resetAutoSettings) { + this.updateScaleControls = !!resetAutoSettings; + this.pdfViewer.currentScaleValue = value; + this.updateScaleControls = true; + }, -var DocumentOutlineView = function documentOutlineView(options) { - var outline = options.outline; - var outlineView = options.outlineView; - while (outlineView.firstChild) { - outlineView.removeChild(outlineView.firstChild); - } + rotatePages: function pdfViewRotatePages(delta) { + var pageNumber = this.page; + this.pageRotation = (this.pageRotation + 360 + delta) % 360; + this.pdfViewer.pagesRotation = this.pageRotation; + this.pdfThumbnailViewer.pagesRotation = this.pageRotation; - if (!outline) { - return; - } + this.forceRendering(); - var linkService = options.linkService; + this.pdfViewer.scrollPageIntoView(pageNumber); + }, - function bindItemLink(domObj, item) { - domObj.href = linkService.getDestinationHash(item.dest); - domObj.onclick = function documentOutlineViewOnclick(e) { - linkService.navigateTo(item.dest); - return false; - }; - } + /** + * This function flips the page in presentation mode if the user scrolls up + * or down with large enough motion and prevents page flipping too often. + * + * @this {PDFView} + * @param {number} mouseScrollDelta The delta value from the mouse event. + */ + mouseScroll: function pdfViewMouseScroll(mouseScrollDelta) { + var MOUSE_SCROLL_COOLDOWN_TIME = 50; - var queue = [{parent: outlineView, items: outline}]; - while (queue.length > 0) { - var levelData = queue.shift(); - var i, n = levelData.items.length; - for (i = 0; i < n; i++) { - var item = levelData.items[i]; - var div = document.createElement('div'); - div.className = 'outlineItem'; - var a = document.createElement('a'); - bindItemLink(a, item); - a.textContent = item.title; - div.appendChild(a); - - if (item.items.length > 0) { - var itemsDiv = document.createElement('div'); - itemsDiv.className = 'outlineItems'; - div.appendChild(itemsDiv); - queue.push({parent: itemsDiv, items: item.items}); - } + var currentTime = (new Date()).getTime(); + var storedTime = this.mouseScrollTimeStamp; - levelData.parent.appendChild(div); + // In case one page has already been flipped there is a cooldown time + // which has to expire before next page can be scrolled on to. + if (currentTime > storedTime && + currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) { + return; } - } -}; + // In case the user decides to scroll to the opposite direction than before + // clear the accumulated delta. + if ((this.mouseScrollDelta > 0 && mouseScrollDelta < 0) || + (this.mouseScrollDelta < 0 && mouseScrollDelta > 0)) { + this.clearMouseScrollState(); + } -var DocumentAttachmentsView = function documentAttachmentsView(options) { - var attachments = options.attachments; - var attachmentsView = options.attachmentsView; - while (attachmentsView.firstChild) { - attachmentsView.removeChild(attachmentsView.firstChild); - } + this.mouseScrollDelta += mouseScrollDelta; - if (!attachments) { - return; - } + var PAGE_FLIP_THRESHOLD = 120; + if (Math.abs(this.mouseScrollDelta) >= PAGE_FLIP_THRESHOLD) { - function bindItemLink(domObj, item) { - domObj.onclick = function documentAttachmentsViewOnclick(e) { - var downloadManager = new DownloadManager(); - downloadManager.downloadData(item.content, getFileName(item.filename), - ''); - return false; - }; - } + var PageFlipDirection = { + UP: -1, + DOWN: 1 + }; - var names = Object.keys(attachments).sort(function(a,b) { - return a.toLowerCase().localeCompare(b.toLowerCase()); - }); - for (var i = 0, ii = names.length; i < ii; i++) { - var item = attachments[names[i]]; - var div = document.createElement('div'); - div.className = 'attachmentsItem'; - var button = document.createElement('button'); - bindItemLink(button, item); - button.textContent = getFileName(item.filename); - div.appendChild(button); - attachmentsView.appendChild(div); + // In presentation mode scroll one page at a time. + var pageFlipDirection = (this.mouseScrollDelta > 0) ? + PageFlipDirection.UP : + PageFlipDirection.DOWN; + this.clearMouseScrollState(); + var currentPage = this.page; + + // In case we are already on the first or the last page there is no need + // to do anything. + if ((currentPage === 1 && pageFlipDirection === PageFlipDirection.UP) || + (currentPage === this.pagesCount && + pageFlipDirection === PageFlipDirection.DOWN)) { + return; + } + + this.page += pageFlipDirection; + this.mouseScrollTimeStamp = currentTime; + } + }, + + /** + * This function clears the member attributes used with mouse scrolling in + * presentation mode. + * + * @this {PDFView} + */ + clearMouseScrollState: function pdfViewClearMouseScrollState() { + this.mouseScrollTimeStamp = 0; + this.mouseScrollDelta = 0; } }; - function webViewerLoad(evt) { PDFViewerApplication.initialize().then(webViewerInitialized); } @@ -6409,20 +6701,15 @@ document.addEventListener('DOMContentLoaded', webViewerLoad, true); document.addEventListener('pagerendered', function (e) { - var pageIndex = e.detail.pageNumber - 1; + var pageNumber = e.detail.pageNumber; + var pageIndex = pageNumber - 1; var pageView = PDFViewerApplication.pdfViewer.getPageView(pageIndex); var thumbnailView = PDFViewerApplication.pdfThumbnailViewer. getThumbnail(pageIndex); - thumbnailView.setImage(pageView.canvas); + thumbnailView.setImage(pageView); - if (pageView.textLayer && pageView.textLayer.textDivs && - pageView.textLayer.textDivs.length > 0 && - !PDFViewerApplication.supportsDocumentColors) { - console.error(mozL10n.get('document_colors_disabled', null, - 'PDF documents are not allowed to use their own colors: ' + - '\'Allow pages to choose their own colors\' ' + - 'is deactivated in the browser.')); - PDFViewerApplication.fallback(); + if (PDFJS.pdfBug && Stats.enabled && pageView.stats) { + Stats.add(pageNumber, pageView.stats); } if (pageView.error) { @@ -6443,12 +6730,27 @@ // If the page is still visible when it has finished rendering, // ensure that the page number input loading indicator is hidden. - if ((pageIndex + 1) === PDFViewerApplication.page) { + if (pageNumber === PDFViewerApplication.page) { var pageNumberInput = document.getElementById('pageNumber'); pageNumberInput.classList.remove(PAGE_NUMBER_LOADING_INDICATOR); } }, true); +document.addEventListener('textlayerrendered', function (e) { + var pageIndex = e.detail.pageNumber - 1; + var pageView = PDFViewerApplication.pdfViewer.getPageView(pageIndex); + + if (pageView.textLayer && pageView.textLayer.textDivs && + pageView.textLayer.textDivs.length > 0 && + !PDFViewerApplication.supportsDocumentColors) { + console.error(mozL10n.get('document_colors_disabled', null, + 'PDF documents are not allowed to use their own colors: ' + + '\'Allow pages to choose their own colors\' ' + + 'is deactivated in the browser.')); + PDFViewerApplication.fallback(); + } +}, true); + window.addEventListener('presentationmodechanged', function (e) { var active = e.detail.active; var switchInProgress = e.detail.switchInProgress; @@ -6608,6 +6910,14 @@ document.getElementById('firstPage').disabled = (page <= 1); document.getElementById('lastPage').disabled = (page >= numPages); + // we need to update stats + if (PDFJS.pdfBug && Stats.enabled) { + var pageView = PDFViewerApplication.pdfViewer.getPageView(page - 1); + if (pageView.stats) { + Stats.add(page, pageView.stats); + } + } + // checking if the this.page was called from the updateViewarea function if (evt.updateInProgress) { return; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/pdfjs/README.mozilla thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/pdfjs/README.mozilla --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/pdfjs/README.mozilla 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/pdfjs/README.mozilla 2015-02-03 14:33:22.000000000 +0000 @@ -1,4 +1,4 @@ This is the pdf.js project output, https://github.com/mozilla/pdf.js -Current extension version is: 1.0.1040 +Current extension version is: 1.0.1130 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/chrome/bootstrap-content.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/chrome/bootstrap-content.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/chrome/bootstrap-content.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/chrome/bootstrap-content.js 2015-02-03 14:33:22.000000000 +0000 @@ -0,0 +1,80 @@ +/* + * Copyright 2014 Mozilla Foundation + * + * 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. + */ + +'use strict'; + +(function contentScriptClosure() { + const Cc = Components.classes; + const Ci = Components.interfaces; + const Cm = Components.manager; + const Cu = Components.utils; + const Cr = Components.results; + + // we need to use closure here -- we are running in the global context + Cu.import('resource://gre/modules/Services.jsm'); + + var isRemote = Services.appinfo.processType === + Services.appinfo.PROCESS_TYPE_CONTENT; + var isStarted = false; + + function startup() { + if (isStarted) { + return; + } + + isStarted = true; + Cu.import('resource://shumway/ShumwayBootstrapUtils.jsm'); + ShumwayBootstrapUtils.register(); + } + + function shutdown() { + if (!isStarted) { + return; + } + + isStarted = false; + ShumwayBootstrapUtils.unregister(); + Cu.unload('resource://shumway/ShumwayBootstrapUtils.jsm'); + } + + + function updateSettings() { + let mm = Cc["@mozilla.org/childprocessmessagemanager;1"] + .getService(Ci.nsISyncMessageSender); + var results = mm.sendSyncMessage('Shumway:Chrome:isEnabled'); + var isEnabled = results.some(function (item) { + return item; + }); + + if (isEnabled) { + startup(); + } else { + shutdown(); + } + } + + if (isRemote) { + addMessageListener('Shumway:Child:refreshSettings', updateSettings); + updateSettings(); + + addMessageListener('Shumway:Child:shutdown', function shutdownListener(e) { + removeMessageListener('Shumway:Child:refreshSettings', updateSettings); + removeMessageListener('Shumway:Child:shutdown', shutdownListener); + + shutdown(); + }); + } +})(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/chrome/content.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/chrome/content.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/chrome/content.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/chrome/content.js 2015-02-03 14:33:22.000000000 +0000 @@ -0,0 +1,98 @@ +/* + * Copyright 2014 Mozilla Foundation + * + * 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. + */ + +Components.utils.import('resource://gre/modules/Services.jsm'); +Components.utils.import('chrome://shumway/content/SpecialInflate.jsm'); +Components.utils.import('chrome://shumway/content/RtmpUtils.jsm'); + +var externalInterfaceWrapper = { + callback: function (call) { + if (!shumwayComAdapter.onExternalCallback) { + return undefined; + } + return shumwayComAdapter.onExternalCallback( + Components.utils.cloneInto(JSON.parse(call), content)); + } +}; + +// The object allows resending of external interface, clipboard and other +// control messages between unprivileged content and ShumwayStreamConverter. +var shumwayComAdapter; + +function sendMessage(action, data, sync, callbackCookie) { + var detail = {action: action, data: data, sync: sync}; + if (callbackCookie !== undefined) { + detail.callback = true; + detail.cookie = callbackCookie; + } + if (!sync) { + sendAsyncMessage('Shumway:message', detail); + return; + } + var result = sendSyncMessage('Shumway:message', detail); + return Components.utils.cloneInto(result, content); +} + +addMessageListener('Shumway:init', function (message) { + sendAsyncMessage('Shumway:running', {}, { + externalInterface: externalInterfaceWrapper + }); + + // Exposing ShumwayCom object/adapter to the unprivileged content -- setting + // up Xray wrappers. + shumwayComAdapter = Components.utils.createObjectIn(content, {defineAs: 'ShumwayCom'}); + Components.utils.exportFunction(sendMessage, shumwayComAdapter, {defineAs: 'sendMessage'}); + Object.defineProperties(shumwayComAdapter, { + onLoadFileCallback: { value: null, writable: true }, + onExternalCallback: { value: null, writable: true }, + onMessageCallback: { value: null, writable: true } + }); + Components.utils.makeObjectPropsNormal(shumwayComAdapter); + + // Exposing createSpecialInflate function for DEFLATE stream decoding using + // Gecko API. + if (SpecialInflateUtils.isSpecialInflateEnabled) { + Components.utils.exportFunction(function () { + return SpecialInflateUtils.createWrappedSpecialInflate(content); + }, content, {defineAs: 'createSpecialInflate'}); + } + + if (RtmpUtils.isRtmpEnabled) { + Components.utils.exportFunction(function (params) { + return RtmpUtils.createSocket(content, params); + }, content, {defineAs: 'createRtmpSocket'}); + Components.utils.exportFunction(function () { + return RtmpUtils.createXHR(content); + }, content, {defineAs: 'createRtmpXHR'}); + } + + content.wrappedJSObject.runViewer(); +}); + +addMessageListener('Shumway:loadFile', function (message) { + if (!shumwayComAdapter.onLoadFileCallback) { + return; + } + shumwayComAdapter.onLoadFileCallback(Components.utils.cloneInto(message.data, content)); +}); + +addMessageListener('Shumway:messageCallback', function (message) { + if (!shumwayComAdapter.onMessageCallback) { + return; + } + shumwayComAdapter.onMessageCallback(message.data.cookie, + Components.utils.cloneInto(message.data.response, content)); +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/chrome/RtmpUtils.jsm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/chrome/RtmpUtils.jsm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/chrome/RtmpUtils.jsm 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/chrome/RtmpUtils.jsm 2015-02-03 14:33:22.000000000 +0000 @@ -0,0 +1,192 @@ +/* + * Copyright 2014 Mozilla Foundation + * + * 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. + */ + +var EXPORTED_SYMBOLS = ['RtmpUtils']; + +Components.utils.import('resource://gre/modules/Services.jsm'); + +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; +var Cr = Components.results; + +var RtmpUtils = { + get isRtmpEnabled() { + try { + return Services.prefs.getBoolPref('shumway.rtmp.enabled'); + } catch (ex) { + return false; + } + }, + + createSocket: function (sandbox, params) { + var host = params.host, port = params.port, ssl = params.ssl; + + var baseSocket = Cc["@mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket); + var socket = baseSocket.open(host, port, {useSecureTransport: ssl, binaryType: 'arraybuffer'}); + if (!socket) { + return null; + } + + var wrapperOnOpen = null, wrapperOnData = null, wrapperOnDrain = null; + var wrapperOnError = null, wrapperOnClose = null; + socket.onopen = function () { + if (wrapperOnOpen) { + wrapperOnOpen.call(wrapper, new sandbox.Object()); + } + }; + socket.ondata = function (e) { + if (wrapperOnData) { + var wrappedE = new sandbox.Object(); + wrappedE.data = Components.utils.cloneInto(e.data, sandbox); + wrapperOnData.call(wrapper, wrappedE); + } + }; + socket.ondrain = function () { + if (wrapperOnDrain) { + wrapperOnDrain.call(wrapper, new sandbox.Object()); + } + }; + socket.onerror = function (e) { + if (wrapperOnError) { + var wrappedE = new sandbox.Object(); + wrappedE.data = Components.utils.cloneInto(e.data, sandbox); + wrapperOnError.call(wrapper, wrappedE); + } + }; + socket.onclose = function () { + if (wrapperOnClose) { + wrapperOnClose.call(wrapper, new sandbox.Object()); + } + }; + + var wrapper = new sandbox.Object(); + var waived = Components.utils.waiveXrays(wrapper); + Object.defineProperties(waived, { + onopen: { + get: function () { return wrapperOnOpen; }, + set: function (value) { wrapperOnOpen = value; }, + enumerable: true + }, + ondata: { + get: function () { return wrapperOnData; }, + set: function (value) { wrapperOnData = value; }, + enumerable: true + }, + ondrain: { + get: function () { return wrapperOnDrain; }, + set: function (value) { wrapperOnDrain = value; }, + enumerable: true + }, + onerror: { + get: function () { return wrapperOnError; }, + set: function (value) { wrapperOnError = value; }, + enumerable: true + }, + onclose: { + get: function () { return wrapperOnClose; }, + set: function (value) { wrapperOnClose = value; }, + enumerable: true + }, + + send: { + value: function (buffer, offset, count) { + return socket.send(buffer, offset, count); + } + }, + + close: { + value: function () { + socket.close(); + } + } + }); + return wrapper; + }, + + createXHR: function (sandbox) { + var xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(Ci.nsIXMLHttpRequest); + var wrapperOnLoad = null, wrapperOnError = null; + xhr.onload = function () { + if (wrapperOnLoad) { + wrapperOnLoad.call(wrapper, new sandbox.Object()); + } + }; + xhr.onerror = function () { + if (wrappedOnError) { + wrappedOnError.call(wrapper, new sandbox.Object()); + } + }; + + var wrapper = new sandbox.Object(); + var waived = Components.utils.waiveXrays(wrapper); + Object.defineProperties(waived, { + status: { + get: function () { return xhr.status; }, + enumerable: true + }, + response: { + get: function () { return Components.utils.cloneInto(xhr.response, sandbox); }, + enumerable: true + }, + responseType: { + get: function () { return xhr.responseType; }, + set: function (value) { + if (value !== 'arraybuffer') { + throw new Error('Invalid responseType.'); + } + }, + enumerable: true + }, + onload: { + get: function () { return wrapperOnLoad; }, + set: function (value) { wrapperOnLoad = value; }, + enumerable: true + }, + onerror: { + get: function () { return wrapperOnError; }, + set: function (value) { wrapperOnError = value; }, + enumerable: true + }, + open: { + value: function (method, path, async) { + if (method !== 'POST' || !path || (async !== undefined && !async)) { + throw new Error('invalid open() arguments'); + } + // TODO check path + xhr.open('POST', path, true); + xhr.responseType = 'arraybuffer'; + xhr.setRequestHeader('Content-Type', 'application/x-fcs'); + } + }, + setRequestHeader: { + value: function (header, value) { + if (header !== 'Content-Type' || value !== 'application/x-fcs') { + throw new Error('invalid setRequestHeader() arguments'); + } + } + }, + + send: { + value: function (data) { + xhr.send(data); + } + } + }); + return wrapper; + } +}; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/chrome/SpecialInflate.jsm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/chrome/SpecialInflate.jsm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/chrome/SpecialInflate.jsm 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/chrome/SpecialInflate.jsm 2015-02-03 14:33:22.000000000 +0000 @@ -0,0 +1,132 @@ +/* + * Copyright 2014 Mozilla Foundation + * + * 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. + */ + +var EXPORTED_SYMBOLS = ['SpecialInflate', 'SpecialInflateUtils']; + +Components.utils.import('resource://gre/modules/Services.jsm'); + +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; +var Cr = Components.results; + +function SimpleStreamListener() { + this.binaryStream = Cc['@mozilla.org/binaryinputstream;1'] + .createInstance(Ci.nsIBinaryInputStream); + this.onData = null; + this.buffer = null; +} +SimpleStreamListener.prototype = { + QueryInterface: function (iid) { + if (iid.equals(Ci.nsIStreamListener) || + iid.equals(Ci.nsIRequestObserver) || + iid.equals(Ci.nsISupports)) + return this; + throw Cr.NS_ERROR_NO_INTERFACE; + }, + onStartRequest: function (aRequest, aContext) { + return Cr.NS_OK; + }, + onStopRequest: function (aRequest, aContext, sStatusCode) { + return Cr.NS_OK; + }, + onDataAvailable: function (aRequest, aContext, aInputStream, aOffset, aCount) { + this.binaryStream.setInputStream(aInputStream); + if (!this.buffer || aCount > this.buffer.byteLength) { + this.buffer = new ArrayBuffer(aCount); + } + this.binaryStream.readArrayBuffer(aCount, this.buffer); + this.onData(new Uint8Array(this.buffer, 0, aCount)); + return Cr.NS_OK; + } +}; + +function SpecialInflate() { + var listener = new SimpleStreamListener(); + listener.onData = function (data) { + this.onData(data); + }.bind(this); + + var converterService = Cc["@mozilla.org/streamConverters;1"].getService(Ci.nsIStreamConverterService); + var converter = converterService.asyncConvertData("deflate", "uncompressed", listener, null); + converter.onStartRequest(null, null); + this.converter = converter; + + var binaryStream = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream); + var pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe); + pipe.init(true, true, 0, 0xFFFFFFFF, null); + binaryStream.setOutputStream(pipe.outputStream); + this.binaryStream = binaryStream; + + this.pipeInputStream = pipe.inputStream; + + this.onData = null; +} +SpecialInflate.prototype = { + push: function (data) { + this.binaryStream.writeByteArray(data, data.length); + this.converter.onDataAvailable(null, null, this.pipeInputStream, 0, data.length); + }, + close: function () { + this.binaryStream.close(); + this.converter.onStopRequest(null, null, Cr.NS_OK); + } +}; + +var SpecialInflateUtils = { + get isSpecialInflateEnabled() { + try { + return Services.prefs.getBoolPref('shumway.specialInflate'); + } catch (ex) { + return false; // TODO true; + } + }, + + createWrappedSpecialInflate: function (sandbox) { + var wrapped = new SpecialInflate(); + var wrapperOnData = null; + wrapped.onData = function(data) { + if (wrapperOnData) { + wrapperOnData.call(wrapper, Components.utils.cloneInto(data, sandbox)); + } + }; + // We will return object created in the sandbox/content, with some exposed + // properties/methods, so we can send data between wrapped object and + // and sandbox/content. + var wrapper = new sandbox.Object(); + var waived = Components.utils.waiveXrays(wrapper); + Object.defineProperties(waived, { + onData: { + get: function () { return wrapperOnData; }, + set: function (value) { wrapperOnData = value; }, + enumerable: true + }, + push: { + value: function (data) { + // Uint8Array is expected in the data parameter. + // SpecialInflate.push() fails with other argument types. + return wrapped.push(data); + } + }, + close: { + value: function () { + return wrapped.close(); + } + } + }); + return wrapper; + } +}; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/chrome/viewer.wrapper.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/chrome/viewer.wrapper.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/chrome/viewer.wrapper.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/chrome/viewer.wrapper.html 2015-02-03 14:33:22.000000000 +0000 @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/chrome/viewerWrapper.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/chrome/viewerWrapper.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/chrome/viewerWrapper.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/chrome/viewerWrapper.js 2015-02-03 14:33:22.000000000 +0000 @@ -0,0 +1,145 @@ +/* + * Copyright 2014 Mozilla Foundation + * + * 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. + */ + +window.notifyShumwayMessage = function (detail) { }; +window.onExternalCallback = null; +window.onMessageCallback = null; +window.onLoadFileCallback = null; + +var viewer = document.getElementById('viewer'), onLoaded; +var promise = new Promise(function (resolve) { + onLoaded = resolve; +}); +viewer.addEventListener('load', function () { + onLoaded(false); +}); +viewer.addEventListener('mozbrowserloadend', function () { + onLoaded(true); +}); + +Components.utils.import('chrome://shumway/content/SpecialInflate.jsm'); +Components.utils.import('chrome://shumway/content/RtmpUtils.jsm'); + +function runViewer() { + function handler() { + function sendMessage(action, data, sync, callbackCookie) { + var detail = {action: action, data: data, sync: sync}; + if (callbackCookie !== undefined) { + detail.callback = true; + detail.cookie = callbackCookie; + } + var result = window.notifyShumwayMessage(detail); + return Components.utils.cloneInto(result, childWindow); + } + + var childWindow = viewer.contentWindow.wrappedJSObject; + + // Exposing ShumwayCom object/adapter to the unprivileged content -- setting + // up Xray wrappers. This allows resending of external interface, clipboard + // and other control messages between unprivileged content and + // ShumwayStreamConverter. + var shumwayComAdapter = Components.utils.createObjectIn(childWindow, {defineAs: 'ShumwayCom'}); + Components.utils.exportFunction(sendMessage, shumwayComAdapter, {defineAs: 'sendMessage'}); + Object.defineProperties(shumwayComAdapter, { + onLoadFileCallback: { value: null, writable: true }, + onExternalCallback: { value: null, writable: true }, + onMessageCallback: { value: null, writable: true } + }); + Components.utils.makeObjectPropsNormal(shumwayComAdapter); + + // Exposing createSpecialInflate function for DEFLATE stream decoding using + // Gecko API. + if (SpecialInflateUtils.isSpecialInflateEnabled) { + Components.utils.exportFunction(function () { + return SpecialInflateUtils.createWrappedSpecialInflate(childWindow); + }, childWindow, {defineAs: 'createSpecialInflate'}); + } + + if (RtmpUtils.isRtmpEnabled) { + Components.utils.exportFunction(function (params) { + return RtmpUtils.createSocket(childWindow, params); + }, childWindow, {defineAs: 'createRtmpSocket'}); + Components.utils.exportFunction(function () { + return RtmpUtils.createXHR(childWindow); + }, childWindow, {defineAs: 'createRtmpXHR'}); + } + + window.onExternalCallback = function (call) { + return shumwayComAdapter.onExternalCallback(Components.utils.cloneInto(call, childWindow)); + }; + + window.onMessageCallback = function (response) { + shumwayComAdapter.onMessageCallback(Components.utils.cloneInto(response, childWindow)); + }; + + window.onLoadFileCallback = function (args) { + shumwayComAdapter.onLoadFileCallback(Components.utils.cloneInto(args, childWindow)); + }; + + childWindow.runViewer(); + } + + function handlerOOP() { + var frameLoader = viewer.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader; + var messageManager = frameLoader.messageManager; + messageManager.loadFrameScript('chrome://shumway/content/content.js', false); + + var externalInterface; + + messageManager.addMessageListener('Shumway:running', function (message) { + externalInterface = message.objects.externalInterface; + }); + + messageManager.addMessageListener('Shumway:message', function (message) { + var detail = { + action: message.data.action, + data: message.data.data, + sync: message.data.sync + }; + if (message.data.callback) { + detail.callback = true; + detail.cookie = message.data.cookie; + } + + return window.notifyShumwayMessage(detail); + }); + + window.onExternalCallback = function (call) { + return externalInterface.callback(JSON.stringify(call)); + }; + + window.onMessageCallback = function (response) { + messageManager.sendAsyncMessage('Shumway:messageCallback', { + cookie: response.cookie, + response: response.response + }); + }; + + window.onLoadFileCallback = function (args) { + messageManager.sendAsyncMessage('Shumway:loadFile', args); + }; + + messageManager.sendAsyncMessage('Shumway:init', {}); + } + + promise.then(function (oop) { + if (oop) { + handlerOOP(); + } else { + handler(); + } + }); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/chrome.manifest thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/chrome.manifest --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/chrome.manifest 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/chrome.manifest 2015-02-03 14:33:22.000000000 +0000 @@ -1 +1,2 @@ +content shumway chrome/ resource shumway content/ Binary files /tmp/RADeF8d7XE/thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/avm2/generated/avm1lib/avm1lib.abc and /tmp/KkXw4lxydX/thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/avm2/generated/avm1lib/avm1lib.abc differ Binary files /tmp/RADeF8d7XE/thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/avm2/generated/builtin/builtin.abc and /tmp/KkXw4lxydX/thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/avm2/generated/builtin/builtin.abc differ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/blurh.frag thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/blurh.frag --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/blurh.frag 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/blurh.frag 1970-01-01 00:00:00.000000000 +0000 @@ -1,127 +0,0 @@ -/* -precision mediump float; - -varying vec4 vColor; -uniform sampler2D uSampler; -varying vec2 vCoordinate; - -out vec4 FragmentColor; - -uniform float offset[5] = float[]( 0.0, 1.0, 2.0, 3.0, 4.0 ); -uniform float weight[5] = float[]( 0.2270270270, 0.1945945946, 0.1216216216, - 0.0540540541, 0.0162162162 ); - -void main(void) -{ - FragmentColor = texture2D( uSampler, vec2(vCoordinate) * weight[0]; - for (int i=1; i<5; i++) { - FragmentColor += - texture2D( uSampler, ( vec2(gl_FragCoord)+vec2(0.0, offset[i]) )/1024.0 ) - * weight[i]; - FragmentColor += - texture2D( uSampler, ( vec2(gl_FragCoord)-vec2(0.0, offset[i]) )/1024.0 ) - * weight[i]; - } -} -*/ - -/* -precision mediump float; - -varying vec4 vColor; -uniform sampler2D uSampler; -varying vec2 vCoordinate; - -void main() { - const int sampleRadius = 16; - const int samples = sampleRadius * 2 + 1; - float dy = 1.0 / 512.0; - vec4 sample = vec4(0, 0, 0, 0); - for (int i = -sampleRadius; i <= sampleRadius; i++) { - sample += texture2D(uSampler, vCoordinate + vec2(0, float(i) * dy)); - } - gl_FragColor = sample / float(samples); - // gl_FragColor = texture2D(uSampler, vCoordinate); -} -*/ - -precision mediump float; - -varying vec4 vColor; -uniform sampler2D uSampler; -varying vec2 vCoordinate; - -void main() { - vec4 sum = vec4(0.0); - float blur = 1.0 / 512.0 * 1.0; - sum += texture2D(uSampler, vec2(vCoordinate.x - 4.0 * blur, vCoordinate.y)) * 0.05; - sum += texture2D(uSampler, vec2(vCoordinate.x - 3.0 * blur, vCoordinate.y)) * 0.09; - sum += texture2D(uSampler, vec2(vCoordinate.x - 2.0 * blur, vCoordinate.y)) * 0.12; - sum += texture2D(uSampler, vec2(vCoordinate.x - blur, vCoordinate.y)) * 0.15; - sum += texture2D(uSampler, vec2(vCoordinate.x, vCoordinate.y)) * 0.16; - sum += texture2D(uSampler, vec2(vCoordinate.x + blur, vCoordinate.y)) * 0.15; - sum += texture2D(uSampler, vec2(vCoordinate.x + 2.0 * blur, vCoordinate.y)) * 0.12; - sum += texture2D(uSampler, vec2(vCoordinate.x + 3.0 * blur, vCoordinate.y)) * 0.09; - sum += texture2D(uSampler, vec2(vCoordinate.x + 4.0 * blur, vCoordinate.y)) * 0.05; - gl_FragColor = sum; - // gl_FragColor = texture2D(uSampler, vCoordinate); -} - -/* -precision mediump float; - -varying vec4 vColor; -uniform sampler2D uSampler; -varying vec2 vCoordinate; - -void main() { - vec4 sum = vec4(0.0); - float blur = 0.1; - sum += texture2D(uSampler, vec2(vCoordinate.x - 4.0 * blur, vCoordinate.y)) * 0.05; - sum += texture2D(uSampler, vec2(vCoordinate.x - 3.0 * blur, vCoordinate.y)) * 0.09; - sum += texture2D(uSampler, vec2(vCoordinate.x - 2.0 * blur, vCoordinate.y)) * 0.12; - sum += texture2D(uSampler, vec2(vCoordinate.x - blur, vCoordinate.y)) * 0.15; - sum += texture2D(uSampler, vec2(vCoordinate.x, vCoordinate.y)) * 0.16; - sum += texture2D(uSampler, vec2(vCoordinate.x + blur, vCoordinate.y)) * 0.15; - sum += texture2D(uSampler, vec2(vCoordinate.x + 2.0 * blur, vCoordinate.y)) * 0.12; - sum += texture2D(uSampler, vec2(vCoordinate.x + 3.0 * blur, vCoordinate.y)) * 0.09; - sum += texture2D(uSampler, vec2(vCoordinate.x + 4.0 * blur, vCoordinate.y)) * 0.05; - gl_FragColor = sum; - // gl_FragColor = texture2D(uSampler, vCoordinate); -} -*/ - -/* -precision mediump float; - -varying vec4 vColor; -uniform sampler2D uSampler; -varying vec2 vCoordinate; - -void main() { - gl_FragColor = texture2D(uSampler, vCoordinate); -} - -*/ - -/* -precision mediump float; -varying vec2 vCoordinate; -varying float vColor; -uniform float blur; -uniform sampler2D uSampler; -void main(void) { -vec4 sum = vec4(0.0); -sum += texture2D(uSampler, vec2(vCoordinate.x - 4.0*blur, vCoordinate.y)) * 0.05; -sum += texture2D(uSampler, vec2(vCoordinate.x - 3.0*blur, vCoordinate.y)) * 0.09; -sum += texture2D(uSampler, vec2(vCoordinate.x - 2.0*blur, vCoordinate.y)) * 0.12; -sum += texture2D(uSampler, vec2(vCoordinate.x - blur, vCoordinate.y)) * 0.15; -sum += texture2D(uSampler, vec2(vCoordinate.x, vCoordinate.y)) * 0.16; -sum += texture2D(uSampler, vec2(vCoordinate.x + blur, vCoordinate.y)) * 0.15; -sum += texture2D(uSampler, vec2(vCoordinate.x + 2.0*blur, vCoordinate.y)) * 0.12; -sum += texture2D(uSampler, vec2(vCoordinate.x + 3.0*blur, vCoordinate.y)) * 0.09; -sum += texture2D(uSampler, vec2(vCoordinate.x + 4.0*blur, vCoordinate.y)) * 0.05; -gl_FragColor = sum; -} - -*/ \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/blurv.frag thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/blurv.frag --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/blurv.frag 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/blurv.frag 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -precision mediump float; - -varying vec4 vColor; -uniform sampler2D uSampler; -varying vec2 vCoordinate; - -void main() { - const int sampleRadius = 8; - const int samples = sampleRadius * 2 + 1; - float dx = 0.01; - vec4 sample = vec4(0, 0, 0, 0); - for (int i = -sampleRadius; i <= sampleRadius; i++) { - sample += texture2D(uSampler, vCoordinate + vec2(0, float(i) * dy)); - } - gl_FragColor = sample / float(samples); -} \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/canvas.vert thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/canvas.vert --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/canvas.vert 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/canvas.vert 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -uniform vec2 uResolution; -uniform mat3 uTransformMatrix; -uniform float uZ; - -attribute vec2 aPosition; -attribute vec4 aColor; -attribute vec2 aCoordinate; - -varying vec4 vColor; -varying vec2 vCoordinate; - -void main() { - vec2 position = ((uTransformMatrix * vec3(aPosition, 1.0)).xy / uResolution) * 2.0 - 1.0; - position *= vec2(1.0, -1.0); - // position *= vec2(40.0, -4.0); - gl_Position = vec4(vec3(position, uZ), 1.0); - vColor = aColor; - vCoordinate = aCoordinate; -} \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/curve.frag thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/curve.frag --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/curve.frag 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/curve.frag 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -precision mediump float; - -uniform vec4 uColor; -varying vec2 vTextureCoordinate; - -void main() { - gl_FragColor = uColor; - gl_FragColor = vec4(vTextureCoordinate.x, vTextureCoordinate.y, 0, 0.5); - - float u = vTextureCoordinate.x; - float v = vTextureCoordinate.y; - float r = u * u - v; - if (r < 0.0) { - gl_FragColor = vec4(1, 0, 0, 1); - } else { - gl_FragColor = vec4(1, 0, 0, 0.2); - } -} \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/solid-fill.frag thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/solid-fill.frag --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/solid-fill.frag 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/solid-fill.frag 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -precision mediump float; - -varying vec4 vColor; - -void main() { - gl_FragColor = vColor; -} \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/solid-fill.vert thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/solid-fill.vert --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/solid-fill.vert 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/solid-fill.vert 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -uniform vec2 uResolution; -uniform mat3 uTransformMatrix; -uniform float uZ; - -attribute vec2 aPosition; -attribute vec4 aColor; - -varying vec4 vColor; - -void main() { - vec2 position = ((uTransformMatrix * vec3(aPosition, 1.0)).xy / uResolution) * 2.0 - 1.0; - position *= vec2(1.0, -1.0); - gl_Position = vec4(vec3(position, uZ), 1.0); - vColor = aColor; -} \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/texture-fill.frag thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/texture-fill.frag --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/texture-fill.frag 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/texture-fill.frag 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -precision mediump float; - -varying vec4 vColor; -uniform sampler2D uSampler; -varying vec2 vCoordinate; - -void main() { - // gl_FragColor = vColor; - // gl_FragColor = vec4(vTextureCoordinate.x, vTextureCoordinate.y, 0, 0.5); - // gl_FragColor = gl_FragColor; // + texture2D(uSampler, vCoordinate); - gl_FragColor = texture2D(uSampler, vCoordinate); -} \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/texture-fill.vert thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/texture-fill.vert --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/texture-fill.vert 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/gfx/gl/shaders/texture-fill.vert 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -uniform vec2 uResolution; -uniform mat3 uTransformMatrix; -uniform float uZ; - -attribute vec2 aPosition; -attribute vec4 aColor; -attribute vec2 aCoordinate; - -varying vec4 vColor; -varying vec2 vCoordinate; - -void main() { - vec2 position = ((uTransformMatrix * vec3(aPosition, 1.0)).xy / uResolution) * 2.0 - 1.0; - position *= vec2(1.0, -1.0); - // position *= vec2(40.0, -4.0); - gl_Position = vec4(vec3(position, uZ), 1.0); - vColor = aColor; - vCoordinate = aCoordinate; -} \ No newline at end of file Binary files /tmp/RADeF8d7XE/thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/playerglobal/playerglobal.abcs and /tmp/KkXw4lxydX/thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/playerglobal/playerglobal.abcs differ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/playerglobal/playerglobal.json thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/playerglobal/playerglobal.json --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/playerglobal/playerglobal.json 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/playerglobal/playerglobal.json 2015-02-03 14:33:22.000000000 +0000 @@ -117,14 +117,14 @@ "flash.display:BitmapData" ], "offset": 5303, - "length": 1471 + "length": 1445 }, { "name": "flash/display/BitmapDataChannel", "defs": [ "flash.display:BitmapDataChannel" ], - "offset": 6774, + "offset": 6748, "length": 274 }, { @@ -132,7 +132,7 @@ "defs": [ "flash.display:BitmapEncodingColorSpace" ], - "offset": 7048, + "offset": 7022, "length": 355 }, { @@ -140,15 +140,15 @@ "defs": [ "flash.display:BlendMode" ], - "offset": 7403, - "length": 624 + "offset": 7377, + "length": 595 }, { "name": "flash/display/CapsStyle", "defs": [ "flash.display:CapsStyle" ], - "offset": 8027, + "offset": 7972, "length": 256 }, { @@ -156,7 +156,7 @@ "defs": [ "flash.display:ColorCorrection" ], - "offset": 8283, + "offset": 8228, "length": 262 }, { @@ -164,7 +164,7 @@ "defs": [ "flash.display:ColorCorrectionSupport" ], - "offset": 8545, + "offset": 8490, "length": 314 }, { @@ -172,15 +172,15 @@ "defs": [ "flash.display:DisplayObject" ], - "offset": 8859, - "length": 1500 + "offset": 8804, + "length": 1466 }, { "name": "flash/display/DisplayObjectContainer", "defs": [ "flash.display:DisplayObjectContainer" ], - "offset": 10359, + "offset": 10270, "length": 898 }, { @@ -188,7 +188,7 @@ "defs": [ "flash.display:FocusDirection" ], - "offset": 11257, + "offset": 11168, "length": 262 }, { @@ -196,7 +196,7 @@ "defs": [ "flash.display:FrameLabel" ], - "offset": 11519, + "offset": 11430, "length": 278 }, { @@ -204,15 +204,15 @@ "defs": [ "flash.display:Graphics" ], - "offset": 11797, - "length": 1028 + "offset": 11708, + "length": 954 }, { "name": "flash/display/GradientType", "defs": [ "flash.display:GradientType" ], - "offset": 12825, + "offset": 12662, "length": 239 }, { @@ -220,7 +220,7 @@ "defs": [ "flash.display:GraphicsBitmapFill" ], - "offset": 13064, + "offset": 12901, "length": 346 }, { @@ -228,7 +228,7 @@ "defs": [ "flash.display:GraphicsEndFill" ], - "offset": 13410, + "offset": 13247, "length": 215 }, { @@ -236,7 +236,7 @@ "defs": [ "flash.display:GraphicsGradientFill" ], - "offset": 13625, + "offset": 13462, "length": 697 }, { @@ -244,7 +244,7 @@ "defs": [ "flash.display:GraphicsPath" ], - "offset": 14322, + "offset": 14159, "length": 1258 }, { @@ -252,7 +252,7 @@ "defs": [ "flash.display:GraphicsPathCommand" ], - "offset": 15580, + "offset": 15417, "length": 368 }, { @@ -260,23 +260,15 @@ "defs": [ "flash.display:GraphicsPathWinding" ], - "offset": 15948, + "offset": 15785, "length": 259 }, { - "name": "flash/display/GraphicsShaderFill", - "defs": [ - "flash.display:GraphicsShaderFill" - ], - "offset": 16207, - "length": 299 - }, - { "name": "flash/display/GraphicsSolidFill", "defs": [ "flash.display:GraphicsSolidFill" ], - "offset": 16506, + "offset": 16044, "length": 283 }, { @@ -284,7 +276,7 @@ "defs": [ "flash.display:GraphicsStroke" ], - "offset": 16789, + "offset": 16327, "length": 1542 }, { @@ -292,7 +284,7 @@ "defs": [ "flash.display:GraphicsTrianglePath" ], - "offset": 18331, + "offset": 17869, "length": 680 }, { @@ -300,7 +292,7 @@ "defs": [ "flash.display:IBitmapDrawable" ], - "offset": 19011, + "offset": 18549, "length": 105 }, { @@ -308,7 +300,7 @@ "defs": [ "flash.display:IDrawCommand" ], - "offset": 19116, + "offset": 18654, "length": 102 }, { @@ -316,7 +308,7 @@ "defs": [ "flash.display:IGraphicsData" ], - "offset": 19218, + "offset": 18756, "length": 103 }, { @@ -324,7 +316,7 @@ "defs": [ "flash.display:IGraphicsFill" ], - "offset": 19321, + "offset": 18859, "length": 103 }, { @@ -332,7 +324,7 @@ "defs": [ "flash.display:IGraphicsPath" ], - "offset": 19424, + "offset": 18962, "length": 103 }, { @@ -340,7 +332,7 @@ "defs": [ "flash.display:IGraphicsStroke" ], - "offset": 19527, + "offset": 19065, "length": 105 }, { @@ -348,7 +340,7 @@ "defs": [ "flash.display:InteractiveObject" ], - "offset": 19632, + "offset": 19170, "length": 764 }, { @@ -356,7 +348,7 @@ "defs": [ "flash.display:InterpolationMethod" ], - "offset": 20396, + "offset": 19934, "length": 254 }, { @@ -364,7 +356,7 @@ "defs": [ "flash.display:JointStyle" ], - "offset": 20650, + "offset": 20188, "length": 258 }, { @@ -372,7 +364,7 @@ "defs": [ "flash.display:JPEGEncoderOptions" ], - "offset": 20908, + "offset": 20446, "length": 216 }, { @@ -380,7 +372,7 @@ "defs": [ "flash.display:JPEGXREncoderOptions" ], - "offset": 21124, + "offset": 20662, "length": 295 }, { @@ -388,7 +380,7 @@ "defs": [ "flash.display:LineScaleMode" ], - "offset": 21419, + "offset": 20957, "length": 307 }, { @@ -396,23 +388,23 @@ "defs": [ "flash.display:Loader" ], - "offset": 21726, - "length": 1320 + "offset": 21264, + "length": 756 }, { "name": "flash/display/LoaderInfo", "defs": [ "flash.display:LoaderInfo" ], - "offset": 23046, - "length": 1227 + "offset": 22020, + "length": 992 }, { "name": "flash/display/MorphShape", "defs": [ "flash.display:MorphShape" ], - "offset": 24273, + "offset": 23012, "length": 263 }, { @@ -420,31 +412,31 @@ "defs": [ "flash.display:MovieClip" ], - "offset": 24536, - "length": 871 + "offset": 23275, + "length": 845 }, { "name": "flash/display/NativeMenu", "defs": [ "flash.display:NativeMenu" ], - "offset": 25407, - "length": 207 + "offset": 24120, + "length": 226 }, { "name": "flash/display/NativeMenuItem", "defs": [ "flash.display:NativeMenuItem" ], - "offset": 25614, - "length": 279 + "offset": 24346, + "length": 285 }, { "name": "flash/display/PixelSnapping", "defs": [ "flash.display:PixelSnapping" ], - "offset": 25893, + "offset": 24631, "length": 264 }, { @@ -452,7 +444,7 @@ "defs": [ "flash.display:PNGEncoderOptions" ], - "offset": 26157, + "offset": 24895, "length": 224 }, { @@ -460,71 +452,15 @@ "defs": [ "flash.display:Scene" ], - "offset": 26381, + "offset": 25119, "length": 255 }, { - "name": "flash/display/Shader", - "defs": [ - "flash.display:Shader" - ], - "offset": 26636, - "length": 368 - }, - { - "name": "flash/display/ShaderData", - "defs": [ - "flash.display:ShaderData" - ], - "offset": 27004, - "length": 195 - }, - { - "name": "flash/display/ShaderInput", - "defs": [ - "flash.display:ShaderInput" - ], - "offset": 27199, - "length": 334 - }, - { - "name": "flash/display/ShaderJob", - "defs": [ - "flash.display:ShaderJob" - ], - "offset": 27533, - "length": 492 - }, - { - "name": "flash/display/ShaderParameter", - "defs": [ - "flash.display:ShaderParameter" - ], - "offset": 28025, - "length": 308 - }, - { - "name": "flash/display/ShaderParameterType", - "defs": [ - "flash.display:ShaderParameterType" - ], - "offset": 28333, - "length": 618 - }, - { - "name": "flash/display/ShaderPrecision", - "defs": [ - "flash.display:ShaderPrecision" - ], - "offset": 28951, - "length": 237 - }, - { "name": "flash/display/Shape", "defs": [ "flash.display:Shape" ], - "offset": 29188, + "offset": 25374, "length": 266 }, { @@ -532,15 +468,15 @@ "defs": [ "flash.display:SimpleButton" ], - "offset": 29454, - "length": 682 + "offset": 25640, + "length": 596 }, { "name": "flash/display/SpreadMethod", "defs": [ "flash.display:SpreadMethod" ], - "offset": 30136, + "offset": 26236, "length": 264 }, { @@ -548,7 +484,7 @@ "defs": [ "flash.display:Sprite" ], - "offset": 30400, + "offset": 26500, "length": 670 }, { @@ -556,15 +492,15 @@ "defs": [ "flash.display:Stage" ], - "offset": 31070, - "length": 3649 + "offset": 27170, + "length": 2409 }, { "name": "flash/display/Stage3D", "defs": [ "flash.display:Stage3D" ], - "offset": 34719, + "offset": 29579, "length": 443 }, { @@ -572,7 +508,7 @@ "defs": [ "flash.display:StageAlign" ], - "offset": 35162, + "offset": 30022, "length": 383 }, { @@ -580,7 +516,7 @@ "defs": [ "flash.display:StageDisplayState" ], - "offset": 35545, + "offset": 30405, "length": 319 }, { @@ -588,7 +524,7 @@ "defs": [ "flash.display:StageQuality" ], - "offset": 35864, + "offset": 30724, "length": 429 }, { @@ -596,7 +532,7 @@ "defs": [ "flash.display:StageScaleMode" ], - "offset": 36293, + "offset": 31153, "length": 317 }, { @@ -604,7 +540,7 @@ "defs": [ "flash.display:SWFVersion" ], - "offset": 36610, + "offset": 31470, "length": 454 }, { @@ -612,7 +548,7 @@ "defs": [ "flash.display:TriangleCulling" ], - "offset": 37064, + "offset": 31924, "length": 278 }, { @@ -620,7 +556,7 @@ "defs": [ "flash.external:ExternalInterface" ], - "offset": 37342, + "offset": 32202, "length": 3261 }, { @@ -628,7 +564,7 @@ "defs": [ "flash.events:AccelerometerEvent" ], - "offset": 40603, + "offset": 35463, "length": 817 }, { @@ -636,7 +572,7 @@ "defs": [ "flash.events:ActivityEvent" ], - "offset": 41420, + "offset": 36280, "length": 505 }, { @@ -644,7 +580,7 @@ "defs": [ "flash.events:AsyncErrorEvent" ], - "offset": 41925, + "offset": 36785, "length": 522 }, { @@ -652,7 +588,7 @@ "defs": [ "flash.events:ContextMenuEvent" ], - "offset": 42447, + "offset": 37307, "length": 829 }, { @@ -660,7 +596,7 @@ "defs": [ "flash.events:DataEvent" ], - "offset": 43276, + "offset": 38136, "length": 546 }, { @@ -668,15 +604,15 @@ "defs": [ "flash.events:ErrorEvent" ], - "offset": 43822, - "length": 494 + "offset": 38682, + "length": 388 }, { "name": "flash/events/Event", "defs": [ "flash.events:Event" ], - "offset": 44316, + "offset": 39070, "length": 2036 }, { @@ -684,15 +620,15 @@ "defs": [ "flash.events:EventDispatcher" ], - "offset": 46352, - "length": 649 + "offset": 41106, + "length": 463 }, { "name": "flash/events/EventPhase", "defs": [ "flash.events:EventPhase" ], - "offset": 47001, + "offset": 41569, "length": 262 }, { @@ -700,7 +636,7 @@ "defs": [ "flash.events:FocusEvent" ], - "offset": 47263, + "offset": 41831, "length": 980 }, { @@ -708,7 +644,7 @@ "defs": [ "flash.events:FullScreenEvent" ], - "offset": 48243, + "offset": 42811, "length": 643 }, { @@ -716,7 +652,7 @@ "defs": [ "flash.events:GameInputEvent" ], - "offset": 48886, + "offset": 43454, "length": 390 }, { @@ -724,7 +660,7 @@ "defs": [ "flash.events:GeolocationEvent" ], - "offset": 49276, + "offset": 43844, "length": 1137 }, { @@ -732,7 +668,7 @@ "defs": [ "flash.events:GestureEvent" ], - "offset": 50413, + "offset": 44981, "length": 669 }, { @@ -740,7 +676,7 @@ "defs": [ "flash.events:GesturePhase" ], - "offset": 51082, + "offset": 45650, "length": 281 }, { @@ -748,15 +684,15 @@ "defs": [ "flash.events:HTTPStatusEvent" ], - "offset": 51363, - "length": 746 + "offset": 45931, + "length": 534 }, { "name": "flash/events/IEventDispatcher", "defs": [ "flash.events:IEventDispatcher" ], - "offset": 52109, + "offset": 46465, "length": 353 }, { @@ -764,7 +700,7 @@ "defs": [ "flash.events:IMEEvent" ], - "offset": 52462, + "offset": 46818, "length": 632 }, { @@ -772,23 +708,23 @@ "defs": [ "flash.events:IOErrorEvent" ], - "offset": 53094, - "length": 605 + "offset": 47450, + "length": 495 }, { "name": "flash/events/KeyboardEvent", "defs": [ "flash.events:KeyboardEvent" ], - "offset": 53699, - "length": 1044 + "offset": 47945, + "length": 617 }, { "name": "flash/events/MouseEvent", "defs": [ "flash.events:MouseEvent" ], - "offset": 54743, + "offset": 48562, "length": 1503 }, { @@ -796,7 +732,7 @@ "defs": [ "flash.events:NetDataEvent" ], - "offset": 56246, + "offset": 50065, "length": 565 }, { @@ -804,7 +740,7 @@ "defs": [ "flash.events:NetFilterEvent" ], - "offset": 56811, + "offset": 50630, "length": 454 }, { @@ -812,7 +748,7 @@ "defs": [ "flash.events:NetMonitorEvent" ], - "offset": 57265, + "offset": 51084, "length": 516 }, { @@ -820,15 +756,15 @@ "defs": [ "flash.events:NetStatusEvent" ], - "offset": 57781, - "length": 498 + "offset": 51600, + "length": 385 }, { "name": "flash/events/OutputProgressEvent", "defs": [ "flash.events:OutputProgressEvent" ], - "offset": 58279, + "offset": 51985, "length": 638 }, { @@ -836,7 +772,7 @@ "defs": [ "flash.events:PressAndTapGestureEvent" ], - "offset": 58917, + "offset": 52623, "length": 944 }, { @@ -844,7 +780,7 @@ "defs": [ "flash.events:ProgressEvent" ], - "offset": 59861, + "offset": 53567, "length": 460 }, { @@ -852,7 +788,7 @@ "defs": [ "flash.events:SampleDataEvent" ], - "offset": 60321, + "offset": 54027, "length": 639 }, { @@ -860,23 +796,15 @@ "defs": [ "flash.events:SecurityErrorEvent" ], - "offset": 60960, + "offset": 54666, "length": 511 }, { - "name": "flash/events/ShaderEvent", - "defs": [ - "flash.events:ShaderEvent" - ], - "offset": 61471, - "length": 742 - }, - { "name": "flash/events/SoftKeyboardEvent", "defs": [ "flash.events:SoftKeyboardEvent" ], - "offset": 62213, + "offset": 55177, "length": 793 }, { @@ -884,7 +812,7 @@ "defs": [ "flash.events:SoftKeyboardTrigger" ], - "offset": 63006, + "offset": 55970, "length": 287 }, { @@ -892,7 +820,7 @@ "defs": [ "flash.events:StageVideoAvailabilityEvent" ], - "offset": 63293, + "offset": 56257, "length": 377 }, { @@ -900,7 +828,7 @@ "defs": [ "flash.events:StageVideoEvent" ], - "offset": 63670, + "offset": 56634, "length": 535 }, { @@ -908,7 +836,7 @@ "defs": [ "flash.events:StatusEvent" ], - "offset": 64205, + "offset": 57169, "length": 578 }, { @@ -916,7 +844,7 @@ "defs": [ "flash.events:SyncEvent" ], - "offset": 64783, + "offset": 57747, "length": 466 }, { @@ -924,15 +852,15 @@ "defs": [ "flash.events:TextEvent" ], - "offset": 65249, - "length": 569 + "offset": 58213, + "length": 381 }, { "name": "flash/events/ThrottleEvent", "defs": [ "flash.events:ThrottleEvent" ], - "offset": 65818, + "offset": 58594, "length": 553 }, { @@ -940,7 +868,7 @@ "defs": [ "flash.events:ThrottleType" ], - "offset": 66371, + "offset": 59147, "length": 268 }, { @@ -948,23 +876,23 @@ "defs": [ "flash.events:TimerEvent" ], - "offset": 66639, - "length": 505 + "offset": 59415, + "length": 389 }, { "name": "flash/events/TouchEvent", "defs": [ "flash.events:TouchEvent" ], - "offset": 67144, - "length": 2322 + "offset": 59804, + "length": 1501 }, { "name": "flash/events/TransformGestureEvent", "defs": [ "flash.events:TransformGestureEvent" ], - "offset": 69466, + "offset": 61305, "length": 1154 }, { @@ -972,7 +900,7 @@ "defs": [ "flash.events:UncaughtErrorEvent" ], - "offset": 70620, + "offset": 62459, "length": 525 }, { @@ -980,15 +908,15 @@ "defs": [ "flash.events:UncaughtErrorEvents" ], - "offset": 71145, - "length": 208 + "offset": 62984, + "length": 194 }, { "name": "flash/events/VideoEvent", "defs": [ "flash.events:VideoEvent" ], - "offset": 71353, + "offset": 63178, "length": 461 }, { @@ -996,7 +924,7 @@ "defs": [ "flash.geom:ColorTransform" ], - "offset": 71814, + "offset": 63639, "length": 586 }, { @@ -1004,7 +932,7 @@ "defs": [ "flash.geom:Matrix" ], - "offset": 72400, + "offset": 64225, "length": 814 }, { @@ -1012,7 +940,7 @@ "defs": [ "flash.geom:Matrix3D" ], - "offset": 73214, + "offset": 65039, "length": 1092 }, { @@ -1020,7 +948,7 @@ "defs": [ "flash.geom:Orientation3D" ], - "offset": 74306, + "offset": 66131, "length": 290 }, { @@ -1028,7 +956,7 @@ "defs": [ "flash.geom:PerspectiveProjection" ], - "offset": 74596, + "offset": 66421, "length": 389 }, { @@ -1036,7 +964,7 @@ "defs": [ "flash.geom:Point" ], - "offset": 74985, + "offset": 66810, "length": 516 }, { @@ -1044,7 +972,7 @@ "defs": [ "flash.geom:Rectangle" ], - "offset": 75501, + "offset": 67326, "length": 889 }, { @@ -1052,7 +980,7 @@ "defs": [ "flash.geom:Transform" ], - "offset": 76390, + "offset": 68215, "length": 553 }, { @@ -1060,7 +988,7 @@ "defs": [ "flash.geom:Utils3D" ], - "offset": 76943, + "offset": 68768, "length": 342 }, { @@ -1068,7 +996,7 @@ "defs": [ "flash.geom:Vector3D" ], - "offset": 77285, + "offset": 69110, "length": 830 }, { @@ -1076,7 +1004,7 @@ "defs": [ "flash.filters:BevelFilter" ], - "offset": 78115, + "offset": 69940, "length": 685 }, { @@ -1084,7 +1012,7 @@ "defs": [ "flash.filters:BitmapFilter" ], - "offset": 78800, + "offset": 70625, "length": 209 }, { @@ -1092,7 +1020,7 @@ "defs": [ "flash.filters:BitmapFilterQuality" ], - "offset": 79009, + "offset": 70834, "length": 256 }, { @@ -1100,7 +1028,7 @@ "defs": [ "flash.filters:BitmapFilterType" ], - "offset": 79265, + "offset": 71090, "length": 268 }, { @@ -1108,7 +1036,7 @@ "defs": [ "flash.filters:BlurFilter" ], - "offset": 79533, + "offset": 71358, "length": 342 }, { @@ -1116,7 +1044,7 @@ "defs": [ "flash.filters:ColorMatrixFilter" ], - "offset": 79875, + "offset": 71700, "length": 294 }, { @@ -1124,7 +1052,7 @@ "defs": [ "flash.filters:ConvolutionFilter" ], - "offset": 80169, + "offset": 71994, "length": 570 }, { @@ -1132,7 +1060,7 @@ "defs": [ "flash.filters:DisplacementMapFilter" ], - "offset": 80739, + "offset": 72564, "length": 632 }, { @@ -1140,7 +1068,7 @@ "defs": [ "flash.filters:DisplacementMapFilterMode" ], - "offset": 81371, + "offset": 73196, "length": 315 }, { @@ -1148,7 +1076,7 @@ "defs": [ "flash.filters:DropShadowFilter" ], - "offset": 81686, + "offset": 73511, "length": 627 }, { @@ -1156,7 +1084,7 @@ "defs": [ "flash.filters:GlowFilter" ], - "offset": 82313, + "offset": 74138, "length": 517 }, { @@ -1164,7 +1092,7 @@ "defs": [ "flash.filters:GradientBevelFilter" ], - "offset": 82830, + "offset": 74655, "length": 649 }, { @@ -1172,23 +1100,15 @@ "defs": [ "flash.filters:GradientGlowFilter" ], - "offset": 83479, + "offset": 75304, "length": 646 }, { - "name": "flash/filters/ShaderFilter", - "defs": [ - "flash.filters:ShaderFilter" - ], - "offset": 84125, - "length": 626 - }, - { "name": "flash/globalization/Collator", "defs": [ "flash.globalization:Collator" ], - "offset": 84751, + "offset": 75950, "length": 692 }, { @@ -1196,7 +1116,7 @@ "defs": [ "flash.globalization:CollatorMode" ], - "offset": 85443, + "offset": 76642, "length": 257 }, { @@ -1204,7 +1124,7 @@ "defs": [ "flash.globalization:CurrencyFormatter" ], - "offset": 85700, + "offset": 76899, "length": 1104 }, { @@ -1212,7 +1132,7 @@ "defs": [ "flash.globalization:CurrencyParseResult" ], - "offset": 86804, + "offset": 78003, "length": 350 }, { @@ -1220,7 +1140,7 @@ "defs": [ "flash.globalization:DateTimeFormatter" ], - "offset": 87154, + "offset": 78353, "length": 830 }, { @@ -1228,7 +1148,7 @@ "defs": [ "flash.globalization:DateTimeNameContext" ], - "offset": 87984, + "offset": 79183, "length": 273 }, { @@ -1236,7 +1156,7 @@ "defs": [ "flash.globalization:DateTimeNameStyle" ], - "offset": 88257, + "offset": 79456, "length": 330 }, { @@ -1244,7 +1164,7 @@ "defs": [ "flash.globalization:DateTimeStyle" ], - "offset": 88587, + "offset": 79786, "length": 330 }, { @@ -1252,7 +1172,7 @@ "defs": [ "flash.globalization:LastOperationStatus" ], - "offset": 88917, + "offset": 80116, "length": 1104 }, { @@ -1260,7 +1180,7 @@ "defs": [ "flash.globalization:LocaleID" ], - "offset": 90021, + "offset": 81220, "length": 560 }, { @@ -1268,7 +1188,7 @@ "defs": [ "flash.globalization:NationalDigitsType" ], - "offset": 90581, + "offset": 81780, "length": 1055 }, { @@ -1276,7 +1196,7 @@ "defs": [ "flash.globalization:NumberFormatter" ], - "offset": 91636, + "offset": 82835, "length": 946 }, { @@ -1284,7 +1204,7 @@ "defs": [ "flash.globalization:NumberParseResult" ], - "offset": 92582, + "offset": 83781, "length": 367 }, { @@ -1292,7 +1212,7 @@ "defs": [ "flash.globalization:StringTools" ], - "offset": 92949, + "offset": 84148, "length": 460 }, { @@ -1300,7 +1220,7 @@ "defs": [ "flash.media:AudioDecoder" ], - "offset": 93409, + "offset": 84608, "length": 448 }, { @@ -1308,7 +1228,7 @@ "defs": [ "flash.media:Camera" ], - "offset": 93857, + "offset": 85056, "length": 1029 }, { @@ -1316,7 +1236,7 @@ "defs": [ "flash.media:H264Level" ], - "offset": 94886, + "offset": 86085, "length": 613 }, { @@ -1324,7 +1244,7 @@ "defs": [ "flash.media:H264Profile" ], - "offset": 95499, + "offset": 86698, "length": 233 }, { @@ -1332,7 +1252,7 @@ "defs": [ "flash.media:H264VideoStreamSettings" ], - "offset": 95732, + "offset": 86931, "length": 1137 }, { @@ -1340,7 +1260,7 @@ "defs": [ "flash.media:ID3Info" ], - "offset": 96869, + "offset": 88068, "length": 300 }, { @@ -1348,7 +1268,7 @@ "defs": [ "flash.media:Microphone" ], - "offset": 97169, + "offset": 88368, "length": 986 }, { @@ -1356,7 +1276,7 @@ "defs": [ "flash.media:MicrophoneEnhancedMode" ], - "offset": 98155, + "offset": 89354, "length": 367 }, { @@ -1364,7 +1284,7 @@ "defs": [ "flash.media:MicrophoneEnhancedOptions" ], - "offset": 98522, + "offset": 89721, "length": 604 }, { @@ -1372,15 +1292,15 @@ "defs": [ "flash.media:Sound" ], - "offset": 99126, - "length": 837 + "offset": 90325, + "length": 726 }, { "name": "flash/media/SoundChannel", "defs": [ "flash.media:SoundChannel" ], - "offset": 99963, + "offset": 91051, "length": 390 }, { @@ -1388,7 +1308,7 @@ "defs": [ "flash.media:SoundCodec" ], - "offset": 100353, + "offset": 91441, "length": 287 }, { @@ -1396,7 +1316,7 @@ "defs": [ "flash.media:SoundLoaderContext" ], - "offset": 100640, + "offset": 91728, "length": 261 }, { @@ -1404,7 +1324,7 @@ "defs": [ "flash.media:SoundMixer" ], - "offset": 100901, + "offset": 91989, "length": 513 }, { @@ -1412,7 +1332,7 @@ "defs": [ "flash.media:SoundTransform" ], - "offset": 101414, + "offset": 92502, "length": 422 }, { @@ -1420,7 +1340,7 @@ "defs": [ "flash.media:StageVideo" ], - "offset": 101836, + "offset": 92924, "length": 570 }, { @@ -1428,7 +1348,7 @@ "defs": [ "flash.media:StageVideoAvailability" ], - "offset": 102406, + "offset": 93494, "length": 271 }, { @@ -1436,7 +1356,7 @@ "defs": [ "flash.media:Video" ], - "offset": 102677, + "offset": 93765, "length": 494 }, { @@ -1444,7 +1364,7 @@ "defs": [ "flash.media:VideoCodec" ], - "offset": 103171, + "offset": 94259, "length": 256 }, { @@ -1452,7 +1372,7 @@ "defs": [ "flash.media:VideoStatus" ], - "offset": 103427, + "offset": 94515, "length": 286 }, { @@ -1460,7 +1380,7 @@ "defs": [ "flash.media:VideoStreamSettings" ], - "offset": 103713, + "offset": 94801, "length": 651 }, { @@ -1468,7 +1388,7 @@ "defs": [ "flash.profiler:Telemetry" ], - "offset": 104364, + "offset": 95452, "length": 422 }, { @@ -1476,7 +1396,7 @@ "defs": [ "flash.printing:PrintJob" ], - "offset": 104786, + "offset": 95874, "length": 711 }, { @@ -1484,7 +1404,7 @@ "defs": [ "flash.printing:PrintJobOptions" ], - "offset": 105497, + "offset": 96585, "length": 216 }, { @@ -1492,7 +1412,7 @@ "defs": [ "flash.printing:PrintJobOrientation" ], - "offset": 105713, + "offset": 96801, "length": 265 }, { @@ -1500,7 +1420,7 @@ "defs": [ "flash.security:CertificateStatus" ], - "offset": 105978, + "offset": 97066, "length": 533 }, { @@ -1508,7 +1428,7 @@ "defs": [ "flash.security:X500DistinguishedName" ], - "offset": 106511, + "offset": 97599, "length": 427 }, { @@ -1516,7 +1436,7 @@ "defs": [ "flash.security:X509Certificate" ], - "offset": 106938, + "offset": 98026, "length": 635 }, { @@ -1524,7 +1444,7 @@ "defs": [ "flash.sampler:ClassFactory" ], - "offset": 107573, + "offset": 98661, "length": 285 }, { @@ -1532,7 +1452,7 @@ "defs": [ "flash.sampler:DeleteObjectSample" ], - "offset": 107858, + "offset": 98946, "length": 233 }, { @@ -1540,7 +1460,7 @@ "defs": [ "flash.sampler:NewObjectSample" ], - "offset": 108091, + "offset": 99179, "length": 308 }, { @@ -1548,7 +1468,7 @@ "defs": [ "flash.sampler:Sample" ], - "offset": 108399, + "offset": 99487, "length": 205 }, { @@ -1556,7 +1476,7 @@ "defs": [ "flash.sampler:StackFrame" ], - "offset": 108604, + "offset": 99692, "length": 310 }, { @@ -1564,7 +1484,7 @@ "defs": [ "flash.system:ApplicationDomain" ], - "offset": 108914, + "offset": 100002, "length": 498 }, { @@ -1572,7 +1492,7 @@ "defs": [ "flash.system:ApplicationInstaller" ], - "offset": 109412, + "offset": 100500, "length": 426 }, { @@ -1580,7 +1500,7 @@ "defs": [ "flash.system:AuthorizedFeatures" ], - "offset": 109838, + "offset": 100926, "length": 456 }, { @@ -1588,7 +1508,7 @@ "defs": [ "flash.system:AuthorizedFeaturesLoader" ], - "offset": 110294, + "offset": 101382, "length": 398 }, { @@ -1596,7 +1516,7 @@ "defs": [ "flash.system:Capabilities" ], - "offset": 110692, + "offset": 101780, "length": 1133 }, { @@ -1604,7 +1524,7 @@ "defs": [ "flash.system:DomainMemoryWithStage3D" ], - "offset": 111825, + "offset": 102913, "length": 191 }, { @@ -1613,7 +1533,7 @@ "flash.system:FSCommand", "flash.system:fscommand" ], - "offset": 112016, + "offset": 103104, "length": 285 }, { @@ -1621,7 +1541,7 @@ "defs": [ "flash.system:ImageDecodingPolicy" ], - "offset": 112301, + "offset": 103389, "length": 257 }, { @@ -1629,15 +1549,15 @@ "defs": [ "flash.system:IME" ], - "offset": 112558, - "length": 507 + "offset": 103646, + "length": 469 }, { "name": "flash/system/IMEConversionMode", "defs": [ "flash.system:IMEConversionMode" ], - "offset": 113065, + "offset": 104115, "length": 432 }, { @@ -1645,7 +1565,7 @@ "defs": [ "flash.system:JPEGLoaderContext" ], - "offset": 113497, + "offset": 104547, "length": 309 }, { @@ -1653,15 +1573,15 @@ "defs": [ "flash.system:LoaderContext" ], - "offset": 113806, - "length": 515 + "offset": 104856, + "length": 480 }, { "name": "flash/system/MessageChannel", "defs": [ "flash.system:MessageChannel" ], - "offset": 114321, + "offset": 105336, "length": 584 }, { @@ -1669,7 +1589,7 @@ "defs": [ "flash.system:MessageChannelState" ], - "offset": 114905, + "offset": 105920, "length": 278 }, { @@ -1677,7 +1597,7 @@ "defs": [ "flash.system:Security" ], - "offset": 115183, + "offset": 106198, "length": 773 }, { @@ -1685,15 +1605,15 @@ "defs": [ "flash.system:SecurityDomain" ], - "offset": 115956, - "length": 265 + "offset": 106971, + "length": 221 }, { "name": "flash/system/SecurityPanel", "defs": [ "flash.system:SecurityPanel" ], - "offset": 116221, + "offset": 107192, "length": 430 }, { @@ -1701,15 +1621,15 @@ "defs": [ "flash.system:System" ], - "offset": 116651, - "length": 629 + "offset": 107622, + "length": 602 }, { "name": "flash/system/SystemUpdater", "defs": [ "flash.system:SystemUpdater" ], - "offset": 117280, + "offset": 108224, "length": 322 }, { @@ -1717,7 +1637,7 @@ "defs": [ "flash.system:SystemUpdaterType" ], - "offset": 117602, + "offset": 108546, "length": 241 }, { @@ -1725,7 +1645,7 @@ "defs": [ "flash.system:TouchscreenType" ], - "offset": 117843, + "offset": 108787, "length": 268 }, { @@ -1733,7 +1653,7 @@ "defs": [ "flash.trace:Trace" ], - "offset": 118111, + "offset": 109055, "length": 488 }, { @@ -1741,31 +1661,31 @@ "defs": [ "flash.ui:ContextMenu" ], - "offset": 118599, - "length": 1036 + "offset": 109543, + "length": 636 }, { "name": "flash/ui/ContextMenuBuiltInItems", "defs": [ "flash.ui:ContextMenuBuiltInItems" ], - "offset": 119635, - "length": 871 + "offset": 110179, + "length": 467 }, { "name": "flash/ui/ContextMenuClipboardItems", "defs": [ "flash.ui:ContextMenuClipboardItems" ], - "offset": 120506, - "length": 587 + "offset": 110646, + "length": 380 }, { "name": "flash/ui/ContextMenuItem", "defs": [ "flash.ui:ContextMenuItem" ], - "offset": 121093, + "offset": 111026, "length": 458 }, { @@ -1773,7 +1693,7 @@ "defs": [ "flash.ui:GameInput" ], - "offset": 121551, + "offset": 111484, "length": 333 }, { @@ -1781,7 +1701,7 @@ "defs": [ "flash.ui:GameInputControl" ], - "offset": 121884, + "offset": 111817, "length": 458 }, { @@ -1789,7 +1709,7 @@ "defs": [ "flash.ui:GameInputControlType" ], - "offset": 122342, + "offset": 112275, "length": 389 }, { @@ -1797,7 +1717,7 @@ "defs": [ "flash.ui:GameInputDevice" ], - "offset": 122731, + "offset": 112664, "length": 623 }, { @@ -1805,7 +1725,7 @@ "defs": [ "flash.ui:GameInputFinger" ], - "offset": 123354, + "offset": 113287, "length": 291 }, { @@ -1813,7 +1733,7 @@ "defs": [ "flash.ui:GameInputHand" ], - "offset": 123645, + "offset": 113578, "length": 256 }, { @@ -1821,7 +1741,7 @@ "defs": [ "flash.ui:Keyboard" ], - "offset": 123901, + "offset": 113834, "length": 9336 }, { @@ -1829,7 +1749,7 @@ "defs": [ "flash.ui:KeyboardType" ], - "offset": 133237, + "offset": 123170, "length": 266 }, { @@ -1837,7 +1757,7 @@ "defs": [ "flash.ui:KeyLocation" ], - "offset": 133503, + "offset": 123436, "length": 273 }, { @@ -1845,15 +1765,15 @@ "defs": [ "flash.ui:Mouse" ], - "offset": 133776, - "length": 397 + "offset": 123709, + "length": 383 }, { "name": "flash/ui/MouseCursor", "defs": [ "flash.ui:MouseCursor" ], - "offset": 134173, + "offset": 124092, "length": 302 }, { @@ -1861,7 +1781,7 @@ "defs": [ "flash.ui:MouseCursorData" ], - "offset": 134475, + "offset": 124394, "length": 352 }, { @@ -1869,7 +1789,7 @@ "defs": [ "flash.ui:Multitouch" ], - "offset": 134827, + "offset": 124746, "length": 435 }, { @@ -1877,7 +1797,7 @@ "defs": [ "flash.ui:MultitouchInputMode" ], - "offset": 135262, + "offset": 125181, "length": 279 }, { @@ -1885,7 +1805,7 @@ "defs": [ "flash.sensors:Accelerometer" ], - "offset": 135541, + "offset": 125460, "length": 357 }, { @@ -1893,7 +1813,7 @@ "defs": [ "flash.sensors:Geolocation" ], - "offset": 135898, + "offset": 125817, "length": 351 }, { @@ -1901,7 +1821,7 @@ "defs": [ "flash.display3D:Context3D" ], - "offset": 136249, + "offset": 126168, "length": 2088 }, { @@ -1909,7 +1829,7 @@ "defs": [ "flash.display3D:Context3DBlendFactor" ], - "offset": 138337, + "offset": 128256, "length": 681 }, { @@ -1917,7 +1837,7 @@ "defs": [ "flash.display3D:Context3DClearMask" ], - "offset": 139018, + "offset": 128937, "length": 292 }, { @@ -1925,7 +1845,7 @@ "defs": [ "flash.display3D:Context3DProfile" ], - "offset": 139310, + "offset": 129229, "length": 282 }, { @@ -1933,7 +1853,7 @@ "defs": [ "flash.display3D:Context3DProgramType" ], - "offset": 139592, + "offset": 129511, "length": 263 }, { @@ -1941,7 +1861,7 @@ "defs": [ "flash.display3D:Context3DRenderMode" ], - "offset": 139855, + "offset": 129774, "length": 257 }, { @@ -1949,7 +1869,7 @@ "defs": [ "flash.display3D:Context3DCompareMode" ], - "offset": 140112, + "offset": 130031, "length": 452 }, { @@ -1957,7 +1877,7 @@ "defs": [ "flash.display3D:Context3DStencilAction" ], - "offset": 140564, + "offset": 130483, "length": 499 }, { @@ -1965,7 +1885,7 @@ "defs": [ "flash.display3D:Context3DTextureFormat" ], - "offset": 141063, + "offset": 130982, "length": 315 }, { @@ -1973,7 +1893,7 @@ "defs": [ "flash.display3D:Context3DTriangleFace" ], - "offset": 141378, + "offset": 131297, "length": 323 }, { @@ -1981,7 +1901,7 @@ "defs": [ "flash.display3D:Context3DVertexBufferFormat" ], - "offset": 141701, + "offset": 131620, "length": 365 }, { @@ -1989,7 +1909,7 @@ "defs": [ "flash.display3D:IndexBuffer3D" ], - "offset": 142066, + "offset": 131985, "length": 364 }, { @@ -1997,7 +1917,7 @@ "defs": [ "flash.display3D:Program3D" ], - "offset": 142430, + "offset": 132349, "length": 275 }, { @@ -2005,7 +1925,7 @@ "defs": [ "flash.display3D:VertexBuffer3D" ], - "offset": 142705, + "offset": 132624, "length": 367 }, { @@ -2013,7 +1933,7 @@ "defs": [ "flash.display3D.textures:CubeTexture" ], - "offset": 143072, + "offset": 132991, "length": 501 }, { @@ -2021,7 +1941,7 @@ "defs": [ "flash.display3D.textures:Texture" ], - "offset": 143573, + "offset": 133492, "length": 487 }, { @@ -2029,7 +1949,7 @@ "defs": [ "flash.display3D.textures:TextureBase" ], - "offset": 144060, + "offset": 133979, "length": 292 }, { @@ -2037,7 +1957,7 @@ "defs": [ "flash.net:DynamicPropertyOutput" ], - "offset": 144352, + "offset": 134271, "length": 308 }, { @@ -2045,7 +1965,7 @@ "defs": [ "flash.net:FileFilter" ], - "offset": 144660, + "offset": 134579, "length": 318 }, { @@ -2053,7 +1973,7 @@ "defs": [ "flash.net:FileReference" ], - "offset": 144978, + "offset": 134897, "length": 666 }, { @@ -2061,7 +1981,7 @@ "defs": [ "flash.net:FileReferenceList" ], - "offset": 145644, + "offset": 135563, "length": 315 }, { @@ -2069,7 +1989,7 @@ "defs": [ "flash.net:GroupSpecifier" ], - "offset": 145959, + "offset": 135878, "length": 1492 }, { @@ -2077,7 +1997,7 @@ "defs": [ "flash.net:IDynamicPropertyOutput" ], - "offset": 147451, + "offset": 137370, "length": 197 }, { @@ -2085,7 +2005,7 @@ "defs": [ "flash.net:IDynamicPropertyWriter" ], - "offset": 147648, + "offset": 137567, "length": 225 }, { @@ -2093,7 +2013,7 @@ "defs": [ "flash.net:LocalConnection" ], - "offset": 147873, + "offset": 137792, "length": 495 }, { @@ -2101,7 +2021,7 @@ "defs": [ "flash.net:NetConnection" ], - "offset": 148368, + "offset": 138287, "length": 906 }, { @@ -2109,7 +2029,7 @@ "defs": [ "flash.net:NetGroup" ], - "offset": 149274, + "offset": 139193, "length": 1299 }, { @@ -2117,7 +2037,7 @@ "defs": [ "flash.net:NetGroupInfo" ], - "offset": 150573, + "offset": 140492, "length": 768 }, { @@ -2125,7 +2045,7 @@ "defs": [ "flash.net:NetGroupReceiveMode" ], - "offset": 151341, + "offset": 141260, "length": 245 }, { @@ -2133,7 +2053,7 @@ "defs": [ "flash.net:NetGroupReplicationStrategy" ], - "offset": 151586, + "offset": 141505, "length": 283 }, { @@ -2141,7 +2061,7 @@ "defs": [ "flash.net:NetGroupSendMode" ], - "offset": 151869, + "offset": 141788, "length": 273 }, { @@ -2149,7 +2069,7 @@ "defs": [ "flash.net:NetGroupSendResult" ], - "offset": 152142, + "offset": 142061, "length": 270 }, { @@ -2157,7 +2077,7 @@ "defs": [ "flash.net:NetMonitor" ], - "offset": 152412, + "offset": 142331, "length": 279 }, { @@ -2165,15 +2085,15 @@ "defs": [ "flash.net:NetStream" ], - "offset": 152691, - "length": 3064 + "offset": 142610, + "length": 3025 }, { "name": "flash/net/NetStreamAppendBytesAction", "defs": [ "flash.net:NetStreamAppendBytesAction" ], - "offset": 155755, + "offset": 145635, "length": 315 }, { @@ -2181,7 +2101,7 @@ "defs": [ "flash.net:NetStreamMulticastInfo" ], - "offset": 156070, + "offset": 145950, "length": 2712 }, { @@ -2189,7 +2109,7 @@ "defs": [ "flash.net:NetStreamInfo" ], - "offset": 158782, + "offset": 148662, "length": 1958 }, { @@ -2197,7 +2117,7 @@ "defs": [ "flash.net:NetStreamPlayOptions" ], - "offset": 160740, + "offset": 150620, "length": 355 }, { @@ -2205,7 +2125,7 @@ "defs": [ "flash.net:NetStreamPlayTransitions" ], - "offset": 161095, + "offset": 150975, "length": 406 }, { @@ -2213,7 +2133,7 @@ "defs": [ "flash.net:ObjectEncoding" ], - "offset": 161501, + "offset": 151381, "length": 345 }, { @@ -2221,7 +2141,7 @@ "defs": [ "flash.net:Responder" ], - "offset": 161846, + "offset": 151726, "length": 239 }, { @@ -2229,7 +2149,7 @@ "defs": [ "flash.net:SecureSocket" ], - "offset": 162085, + "offset": 151965, "length": 566 }, { @@ -2237,7 +2157,7 @@ "defs": [ "flash.net:SharedObject" ], - "offset": 162651, + "offset": 152531, "length": 1108 }, { @@ -2245,7 +2165,7 @@ "defs": [ "flash.net:SharedObjectFlushStatus" ], - "offset": 163759, + "offset": 153639, "length": 257 }, { @@ -2253,15 +2173,15 @@ "defs": [ "flash.net:URLLoader" ], - "offset": 164016, - "length": 1501 + "offset": 153896, + "length": 435 }, { "name": "flash/net/Socket", "defs": [ "flash.net:Socket" ], - "offset": 165517, + "offset": 154331, "length": 1282 }, { @@ -2269,7 +2189,7 @@ "defs": [ "flash.net:URLLoaderDataFormat" ], - "offset": 166799, + "offset": 155613, "length": 276 }, { @@ -2277,15 +2197,15 @@ "defs": [ "flash.net:URLRequest" ], - "offset": 167075, - "length": 402 + "offset": 155889, + "length": 379 }, { "name": "flash/net/URLRequestHeader", "defs": [ "flash.net:URLRequestHeader" ], - "offset": 167477, + "offset": 156268, "length": 223 }, { @@ -2293,7 +2213,7 @@ "defs": [ "flash.net:URLStream" ], - "offset": 167700, + "offset": 156491, "length": 908 }, { @@ -2301,7 +2221,7 @@ "defs": [ "flash.net:URLRequestMethod" ], - "offset": 168608, + "offset": 157399, "length": 304 }, { @@ -2309,15 +2229,15 @@ "defs": [ "flash.net:URLVariables" ], - "offset": 168912, - "length": 972 + "offset": 157703, + "length": 245 }, { "name": "flash/net/XMLSocket", "defs": [ "flash.net:XMLSocket" ], - "offset": 169884, + "offset": 157948, "length": 480 }, { @@ -2325,7 +2245,7 @@ "defs": [ "flash.text:AntiAliasType" ], - "offset": 170364, + "offset": 158428, "length": 239 }, { @@ -2333,7 +2253,7 @@ "defs": [ "flash.text:CSMSettings" ], - "offset": 170603, + "offset": 158667, "length": 248 }, { @@ -2341,7 +2261,7 @@ "defs": [ "flash.text:Font" ], - "offset": 170851, + "offset": 158915, "length": 361 }, { @@ -2349,7 +2269,7 @@ "defs": [ "flash.text:FontStyle" ], - "offset": 171212, + "offset": 159276, "length": 292 }, { @@ -2357,7 +2277,7 @@ "defs": [ "flash.text:FontType" ], - "offset": 171504, + "offset": 159568, "length": 269 }, { @@ -2365,7 +2285,7 @@ "defs": [ "flash.text:GridFitType" ], - "offset": 171773, + "offset": 159837, "length": 258 }, { @@ -2373,7 +2293,7 @@ "defs": [ "flash.text:StaticText" ], - "offset": 172031, + "offset": 160095, "length": 299 }, { @@ -2381,15 +2301,15 @@ "defs": [ "flash.text:StyleSheet" ], - "offset": 172330, - "length": 523 + "offset": 160394, + "length": 386 }, { "name": "flash/text/TextColorType", "defs": [ "flash.text:TextColorType" ], - "offset": 172853, + "offset": 160780, "length": 241 }, { @@ -2397,7 +2317,7 @@ "defs": [ "flash.text:TextDisplayMode" ], - "offset": 173094, + "offset": 161021, "length": 258 }, { @@ -2405,15 +2325,15 @@ "defs": [ "flash.text:TextField" ], - "offset": 173352, - "length": 2507 + "offset": 161279, + "length": 2296 }, { "name": "flash/text/TextFieldAutoSize", "defs": [ "flash.text:TextFieldAutoSize" ], - "offset": 175859, + "offset": 163575, "length": 291 }, { @@ -2421,7 +2341,7 @@ "defs": [ "flash.text:TextFieldType" ], - "offset": 176150, + "offset": 163866, "length": 235 }, { @@ -2429,7 +2349,7 @@ "defs": [ "flash.text:TextFormat" ], - "offset": 176385, + "offset": 164101, "length": 778 }, { @@ -2437,7 +2357,7 @@ "defs": [ "flash.text:TextFormatAlign" ], - "offset": 177163, + "offset": 164879, "length": 343 }, { @@ -2445,7 +2365,7 @@ "defs": [ "flash.text:TextFormatDisplay" ], - "offset": 177506, + "offset": 165222, "length": 241 }, { @@ -2453,7 +2373,7 @@ "defs": [ "flash.text:TextInteractionMode" ], - "offset": 177747, + "offset": 165463, "length": 253 }, { @@ -2461,7 +2381,7 @@ "defs": [ "flash.text:TextLineMetrics" ], - "offset": 178000, + "offset": 165716, "length": 300 }, { @@ -2469,7 +2389,7 @@ "defs": [ "flash.text:TextRenderer" ], - "offset": 178300, + "offset": 166016, "length": 373 }, { @@ -2477,7 +2397,7 @@ "defs": [ "flash.text:TextRun" ], - "offset": 178673, + "offset": 166389, "length": 294 }, { @@ -2485,7 +2405,7 @@ "defs": [ "flash.text:TextSnapshot" ], - "offset": 178967, + "offset": 166683, "length": 513 }, { @@ -2493,7 +2413,7 @@ "defs": [ "flash.text.engine:BreakOpportunity" ], - "offset": 179480, + "offset": 167196, "length": 293 }, { @@ -2501,7 +2421,7 @@ "defs": [ "flash.text.engine:CFFHinting" ], - "offset": 179773, + "offset": 167489, "length": 256 }, { @@ -2509,7 +2429,7 @@ "defs": [ "flash.text.engine:ContentElement" ], - "offset": 180029, + "offset": 167745, "length": 624 }, { @@ -2517,7 +2437,7 @@ "defs": [ "flash.text.engine:DigitCase" ], - "offset": 180653, + "offset": 168369, "length": 277 }, { @@ -2525,7 +2445,7 @@ "defs": [ "flash.text.engine:DigitWidth" ], - "offset": 180930, + "offset": 168646, "length": 288 }, { @@ -2533,7 +2453,7 @@ "defs": [ "flash.text.engine:EastAsianJustifier" ], - "offset": 181218, + "offset": 168934, "length": 495 }, { @@ -2541,7 +2461,7 @@ "defs": [ "flash.text.engine:ElementFormat" ], - "offset": 181713, + "offset": 169429, "length": 1204 }, { @@ -2549,7 +2469,7 @@ "defs": [ "flash.text.engine:FontDescription" ], - "offset": 182917, + "offset": 170633, "length": 672 }, { @@ -2557,7 +2477,7 @@ "defs": [ "flash.text.engine:FontLookup" ], - "offset": 183589, + "offset": 171305, "length": 254 }, { @@ -2565,7 +2485,7 @@ "defs": [ "flash.text.engine:FontMetrics" ], - "offset": 183843, + "offset": 171559, "length": 512 }, { @@ -2573,7 +2493,7 @@ "defs": [ "flash.text.engine:FontPosture" ], - "offset": 184355, + "offset": 172071, "length": 245 }, { @@ -2581,7 +2501,7 @@ "defs": [ "flash.text.engine:FontWeight" ], - "offset": 184600, + "offset": 172316, "length": 239 }, { @@ -2589,7 +2509,7 @@ "defs": [ "flash.text.engine:GraphicElement" ], - "offset": 184839, + "offset": 172555, "length": 497 }, { @@ -2597,7 +2517,7 @@ "defs": [ "flash.text.engine:GroupElement" ], - "offset": 185336, + "offset": 173052, "length": 714 }, { @@ -2605,7 +2525,7 @@ "defs": [ "flash.text.engine:JustificationStyle" ], - "offset": 186050, + "offset": 173766, "length": 356 }, { @@ -2613,7 +2533,7 @@ "defs": [ "flash.text.engine:Kerning" ], - "offset": 186406, + "offset": 174122, "length": 248 }, { @@ -2621,7 +2541,7 @@ "defs": [ "flash.text.engine:LigatureLevel" ], - "offset": 186654, + "offset": 174370, "length": 338 }, { @@ -2629,7 +2549,7 @@ "defs": [ "flash.text.engine:LineJustification" ], - "offset": 186992, + "offset": 174708, "length": 388 }, { @@ -2637,7 +2557,7 @@ "defs": [ "flash.text.engine:RenderingMode" ], - "offset": 187380, + "offset": 175096, "length": 243 }, { @@ -2645,7 +2565,7 @@ "defs": [ "flash.text.engine:SpaceJustifier" ], - "offset": 187623, + "offset": 175339, "length": 527 }, { @@ -2653,7 +2573,7 @@ "defs": [ "flash.text.engine:TabAlignment" ], - "offset": 188150, + "offset": 175866, "length": 299 }, { @@ -2661,7 +2581,7 @@ "defs": [ "flash.text.engine:TabStop" ], - "offset": 188449, + "offset": 176165, "length": 357 }, { @@ -2669,7 +2589,7 @@ "defs": [ "flash.text.engine:TextBaseline" ], - "offset": 188806, + "offset": 176522, "length": 483 }, { @@ -2677,7 +2597,7 @@ "defs": [ "flash.text.engine:TextBlock" ], - "offset": 189289, + "offset": 177005, "length": 2184 }, { @@ -2685,7 +2605,7 @@ "defs": [ "flash.text.engine:TextElement" ], - "offset": 191473, + "offset": 179189, "length": 383 }, { @@ -2693,7 +2613,7 @@ "defs": [ "flash.text.engine:TextJustifier" ], - "offset": 191856, + "offset": 179572, "length": 774 }, { @@ -2701,7 +2621,7 @@ "defs": [ "flash.text.engine:TextLine" ], - "offset": 192630, + "offset": 180346, "length": 2027 }, { @@ -2709,7 +2629,7 @@ "defs": [ "flash.text.engine:TextLineCreationResult" ], - "offset": 194657, + "offset": 182373, "length": 360 }, { @@ -2717,7 +2637,7 @@ "defs": [ "flash.text.engine:TextLineMirrorRegion" ], - "offset": 195017, + "offset": 182733, "length": 451 }, { @@ -2725,7 +2645,7 @@ "defs": [ "flash.text.engine:TextLineValidity" ], - "offset": 195468, + "offset": 183184, "length": 332 }, { @@ -2733,7 +2653,7 @@ "defs": [ "flash.text.engine:TextRotation" ], - "offset": 195800, + "offset": 183516, "length": 352 }, { @@ -2741,7 +2661,7 @@ "defs": [ "flash.text.engine:TypographicCase" ], - "offset": 196152, + "offset": 183868, "length": 436 }, { @@ -2749,7 +2669,7 @@ "defs": [ "flash.text.ime:CompositionAttributeRange" ], - "offset": 196588, + "offset": 184304, "length": 315 }, { @@ -2757,7 +2677,7 @@ "defs": [ "flash.text.ime:IIMEClient" ], - "offset": 196903, + "offset": 184619, "length": 525 }, { @@ -2768,7 +2688,7 @@ "flash.net:registerClassAlias", "flash.net:getClassByAlias" ], - "offset": 197428, + "offset": 185144, "length": 343 }, { @@ -2783,15 +2703,15 @@ "flash.utils:escapeMultiByte", "flash.utils:unescapeMultiByte" ], - "offset": 197771, - "length": 606 + "offset": 185487, + "length": 545 }, { "name": "flash/utils/Endian", "defs": [ "flash.utils:Endian" ], - "offset": 198377, + "offset": 186032, "length": 243 }, { @@ -2799,32 +2719,16 @@ "defs": [ "flash.utils:IExternalizable" ], - "offset": 198620, + "offset": 186275, "length": 223 }, { - "name": "flash/utils/ObjectInput", - "defs": [ - "flash.utils:ObjectInput" - ], - "offset": 198843, - "length": 706 - }, - { - "name": "flash/utils/ObjectOutput", - "defs": [ - "flash.utils:ObjectOutput" - ], - "offset": 199549, - "length": 648 - }, - { "name": "flash/utils/Timer", "defs": [ "flash.utils:Timer" ], - "offset": 200197, - "length": 973 + "offset": 186498, + "length": 400 }, { "name": "flash/utils/SetIntervalTimer", @@ -2835,7 +2739,7 @@ "flash.utils:clearInterval", "flash.utils:clearTimeout" ], - "offset": 201170, + "offset": 186898, "length": 995 } ] \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/ShumwayBootstrapUtils.jsm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/ShumwayBootstrapUtils.jsm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/ShumwayBootstrapUtils.jsm 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/ShumwayBootstrapUtils.jsm 2015-02-03 14:33:22.000000000 +0000 @@ -0,0 +1,95 @@ +/* + * Copyright 2014 Mozilla Foundation + * + * 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. + */ + +'use strict'; + +var EXPORTED_SYMBOLS = ['ShumwayBootstrapUtils']; + +const PREF_PREFIX = 'shumway.'; +const PREF_IGNORE_CTP = PREF_PREFIX + 'ignoreCTP'; +const SWF_CONTENT_TYPE = 'application/x-shockwave-flash'; + +let Cc = Components.classes; +let Ci = Components.interfaces; +let Cm = Components.manager; +let Cu = Components.utils; + +Cu.import('resource://gre/modules/XPCOMUtils.jsm'); +Cu.import('resource://gre/modules/Services.jsm'); + +Cu.import('resource://shumway/ShumwayStreamConverter.jsm'); + +let Ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); +let registerOverlayPreview = 'registerPlayPreviewMimeType' in Ph; + +function getBoolPref(pref, def) { + try { + return Services.prefs.getBoolPref(pref); + } catch (ex) { + return def; + } +} + +function log(str) { + dump(str + '\n'); +} + +// Register/unregister a constructor as a factory. +function Factory() {} +Factory.prototype = { + register: function register(targetConstructor) { + var proto = targetConstructor.prototype; + this._classID = proto.classID; + + var factory = XPCOMUtils._getFactory(targetConstructor); + this._factory = factory; + + var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); + registrar.registerFactory(proto.classID, proto.classDescription, + proto.contractID, factory); + }, + + unregister: function unregister() { + var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); + registrar.unregisterFactory(this._classID, this._factory); + } +}; + +let converterFactory = new Factory(); +let overlayConverterFactory = new Factory(); + +var ShumwayBootstrapUtils = { + register: function () { + // Register the components. + converterFactory.register(ShumwayStreamConverter); + overlayConverterFactory.register(ShumwayStreamOverlayConverter); + + if (registerOverlayPreview) { + var ignoreCTP = getBoolPref(PREF_IGNORE_CTP, true); + Ph.registerPlayPreviewMimeType(SWF_CONTENT_TYPE, ignoreCTP); + } + }, + + unregister: function () { + // Remove the contract/component. + converterFactory.unregister(); + overlayConverterFactory.unregister(); + + if (registerOverlayPreview) { + Ph.unregisterPlayPreviewMimeType(SWF_CONTENT_TYPE); + } + } +}; \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/shumway.gfx.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/shumway.gfx.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/shumway.gfx.js 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/shumway.gfx.js 2015-02-03 14:33:23.000000000 +0000 @@ -1,2215 +1,2291 @@ +/* + + Copyright 2014 Mozilla Foundation + + 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. +*/ console.time("Load Shared Dependencies"); var jsGlobal = function() { - return this || (0,eval)("this"); -}(), inBrowser = "undefined" != typeof console; + return this || (0,eval)("this//# sourceURL=jsGlobal-getter"); +}(), inBrowser = "undefined" !== typeof window && "document" in window && "plugins" in window.document, inFirefox = "undefined" !== typeof navigator && 0 <= navigator.userAgent.indexOf("Firefox"); +function dumpLine(g) { + "undefined" !== typeof dump && dump(g + "\n"); +} jsGlobal.performance || (jsGlobal.performance = {}); jsGlobal.performance.now || (jsGlobal.performance.now = "undefined" !== typeof dateNow ? dateNow : Date.now); -function log(d) { - for (var m = 0;m < arguments.length - 1;m++) { - } - jsGlobal.print.apply(jsGlobal, arguments); -} -function warn(d) { - for (var m = 0;m < arguments.length - 1;m++) { - } - inBrowser ? console.warn.apply(console, arguments) : jsGlobal.print(Shumway.IndentingWriter.RED + d + Shumway.IndentingWriter.ENDC); -} -var Shumway; -(function(d) { - function m(b) { - return(b | 0) === b; +var START_TIME = performance.now(), Shumway; +(function(g) { + function m(a) { + return(a | 0) === a; } - function s(b) { - return "object" === typeof b || "function" === typeof b; + function e(a) { + return "object" === typeof a || "function" === typeof a; } - function e(b) { - return String(Number(b)) === b; + function c(a) { + return String(Number(a)) === a; } - function t(b) { + function w(a) { var h = 0; - if ("number" === typeof b) { - return h = b | 0, b === h && 0 <= h ? !0 : b >>> 0 === b; + if ("number" === typeof a) { + return h = a | 0, a === h && 0 <= h ? !0 : a >>> 0 === a; } - if ("string" !== typeof b) { + if ("string" !== typeof a) { return!1; } - var a = b.length; - if (0 === a) { + var f = a.length; + if (0 === f) { return!1; } - if ("0" === b) { + if ("0" === a) { return!0; } - if (a > d.UINT32_CHAR_BUFFER_LENGTH) { + if (f > g.UINT32_CHAR_BUFFER_LENGTH) { return!1; } - var n = 0, h = b.charCodeAt(n++) - 48; + var d = 0, h = a.charCodeAt(d++) - 48; if (1 > h || 9 < h) { return!1; } - for (var f = 0, q = 0;n < a;) { - q = b.charCodeAt(n++) - 48; - if (0 > q || 9 < q) { + for (var q = 0, x = 0;d < f;) { + x = a.charCodeAt(d++) - 48; + if (0 > x || 9 < x) { return!1; } - f = h; - h = 10 * h + q; + q = h; + h = 10 * h + x; } - return f < d.UINT32_MAX_DIV_10 || f === d.UINT32_MAX_DIV_10 && q <= d.UINT32_MAX_MOD_10 ? !0 : !1; + return q < g.UINT32_MAX_DIV_10 || q === g.UINT32_MAX_DIV_10 && x <= g.UINT32_MAX_MOD_10 ? !0 : !1; } - (function(b) { - b[b._0 = 48] = "_0"; - b[b._1 = 49] = "_1"; - b[b._2 = 50] = "_2"; - b[b._3 = 51] = "_3"; - b[b._4 = 52] = "_4"; - b[b._5 = 53] = "_5"; - b[b._6 = 54] = "_6"; - b[b._7 = 55] = "_7"; - b[b._8 = 56] = "_8"; - b[b._9 = 57] = "_9"; - })(d.CharacterCodes || (d.CharacterCodes = {})); - d.UINT32_CHAR_BUFFER_LENGTH = 10; - d.UINT32_MAX = 4294967295; - d.UINT32_MAX_DIV_10 = 429496729; - d.UINT32_MAX_MOD_10 = 5; - d.isString = function(b) { - return "string" === typeof b; + (function(a) { + a[a._0 = 48] = "_0"; + a[a._1 = 49] = "_1"; + a[a._2 = 50] = "_2"; + a[a._3 = 51] = "_3"; + a[a._4 = 52] = "_4"; + a[a._5 = 53] = "_5"; + a[a._6 = 54] = "_6"; + a[a._7 = 55] = "_7"; + a[a._8 = 56] = "_8"; + a[a._9 = 57] = "_9"; + })(g.CharacterCodes || (g.CharacterCodes = {})); + g.UINT32_CHAR_BUFFER_LENGTH = 10; + g.UINT32_MAX = 4294967295; + g.UINT32_MAX_DIV_10 = 429496729; + g.UINT32_MAX_MOD_10 = 5; + g.isString = function(a) { + return "string" === typeof a; }; - d.isFunction = function(b) { - return "function" === typeof b; + g.isFunction = function(a) { + return "function" === typeof a; }; - d.isNumber = function(b) { - return "number" === typeof b; + g.isNumber = function(a) { + return "number" === typeof a; }; - d.isInteger = m; - d.isArray = function(b) { - return b instanceof Array; + g.isInteger = m; + g.isArray = function(a) { + return a instanceof Array; }; - d.isNumberOrString = function(b) { - return "number" === typeof b || "string" === typeof b; + g.isNumberOrString = function(a) { + return "number" === typeof a || "string" === typeof a; }; - d.isObject = s; - d.toNumber = function(b) { - return+b; + g.isObject = e; + g.toNumber = function(a) { + return+a; }; - d.isNumericString = e; - d.isNumeric = function(b) { - if ("number" === typeof b) { + g.isNumericString = c; + g.isNumeric = function(a) { + if ("number" === typeof a) { return!0; } - if ("string" === typeof b) { - var h = b.charCodeAt(0); - return 65 <= h && 90 >= h || 97 <= h && 122 >= h || 36 === h || 95 === h ? !1 : t(b) || e(b); + if ("string" === typeof a) { + var h = a.charCodeAt(0); + return 65 <= h && 90 >= h || 97 <= h && 122 >= h || 36 === h || 95 === h ? !1 : w(a) || c(a); } return!1; }; - d.isIndex = t; - d.isNullOrUndefined = function(b) { - return void 0 == b; + g.isIndex = w; + g.isNullOrUndefined = function(a) { + return void 0 == a; }; - (function(b) { - b.backtrace = function() { - try { - throw Error(); - } catch (b) { - return b.stack ? b.stack.split("\n").slice(2).join("\n") : ""; - } + var k; + (function(a) { + a.error = function(f) { + console.error(f); + throw Error(f); }; - b.error = function(h) { - inBrowser ? warn(h) : warn(h + "\n\nStack Trace:\n" + b.backtrace()); - throw Error(h); + a.assert = function(f, d) { + void 0 === d && (d = "assertion failed"); + "" === f && (f = !0); + if (!f) { + if ("undefined" !== typeof console && "assert" in console) { + throw console.assert(!1, d), Error(d); + } + a.error(d.toString()); + } }; - b.assert = function(h, n) { - "undefined" === typeof n && (n = "assertion failed"); - "" === h && (h = !0); - h || b.error(n.toString()); + a.assertUnreachable = function(f) { + throw Error("Reached unreachable location " + Error().stack.split("\n")[1] + f); }; - b.assertUnreachable = function(b) { - throw Error("Reached unreachable location " + Error().stack.split("\n")[1] + b); + a.assertNotImplemented = function(f, d) { + f || a.error("notImplemented: " + d); }; - b.assertNotImplemented = function(h, n) { - h || b.error("notImplemented: " + n); + a.warning = function(f, d, q) { + console.warn.apply(console, arguments); }; - b.warning = function(b) { - warn(b); + a.notUsed = function(f) { + a.assert(!1, "Not Used " + f); }; - b.notUsed = function(h) { - b.assert(!1, "Not Used " + h); + a.notImplemented = function(f) { + a.assert(!1, "Not Implemented " + f); }; - b.notImplemented = function(h) { - log("release: false"); - b.assert(!1, "Not Implemented " + h); + a.dummyConstructor = function(f) { + a.assert(!1, "Dummy Constructor: " + f); }; - b.abstractMethod = function(h) { - b.assert(!1, "Abstract Method " + h); + a.abstractMethod = function(f) { + a.assert(!1, "Abstract Method " + f); }; var h = {}; - b.somewhatImplemented = function(a) { - h[a] || (h[a] = !0, b.warning("somewhatImplemented: " + a)); + a.somewhatImplemented = function(f) { + h[f] || (h[f] = !0, a.warning("somewhatImplemented: " + f)); }; - b.unexpected = function(h) { - b.assert(!1, "Unexpected: " + h); + a.unexpected = function(f) { + a.assert(!1, "Unexpected: " + f); }; - b.untested = function(h) { - b.warning("Congratulations, you've found a code path for which we haven't found a test case. Please submit the test case: " + h); + a.unexpectedCase = function(f) { + a.assert(!1, "Unexpected Case: " + f); }; - })(d.Debug || (d.Debug = {})); - var k = d.Debug; - d.getTicks = function() { + })(k = g.Debug || (g.Debug = {})); + g.getTicks = function() { return performance.now(); }; - (function(b) { - function h(f, b) { - for (var n = 0, h = f.length;n < h;n++) { - if (f[n] === b) { - return n; + var b; + (function(a) { + function h(d, f) { + for (var a = 0, h = d.length;a < h;a++) { + if (d[a] === f) { + return a; } } - f.push(b); - return f.length - 1; + d.push(f); + return d.length - 1; } - var a = d.Debug.assert; - b.popManyInto = function(f, b, n) { - a(f.length >= b); - for (var h = b - 1;0 <= h;h--) { - n[h] = f.pop(); - } - n.length = b; - }; - b.popMany = function(f, b) { - a(f.length >= b); - var n = f.length - b, h = f.slice(n, this.length); - f.splice(n, b); - return h; + var f = g.Debug.assert; + a.popManyInto = function(d, a, h) { + f(d.length >= a); + for (var b = a - 1;0 <= b;b--) { + h[b] = d.pop(); + } + h.length = a; + }; + a.popMany = function(d, a) { + f(d.length >= a); + var h = d.length - a, b = d.slice(h, this.length); + d.length = h; + return b; }; - b.popManyIntoVoid = function(f, b) { - a(f.length >= b); - f.length -= b; + a.popManyIntoVoid = function(d, a) { + f(d.length >= a); + d.length -= a; }; - b.pushMany = function(f, b) { - for (var n = 0;n < b.length;n++) { - f.push(b[n]); + a.pushMany = function(d, f) { + for (var a = 0;a < f.length;a++) { + d.push(f[a]); } }; - b.top = function(f) { - return f.length && f[f.length - 1]; + a.top = function(d) { + return d.length && d[d.length - 1]; }; - b.last = function(f) { - return f.length && f[f.length - 1]; + a.last = function(d) { + return d.length && d[d.length - 1]; }; - b.peek = function(f) { - a(0 < f.length); - return f[f.length - 1]; + a.peek = function(d) { + f(0 < d.length); + return d[d.length - 1]; }; - b.indexOf = function(f, b) { - for (var n = 0, h = f.length;n < h;n++) { - if (f[n] === b) { - return n; + a.indexOf = function(d, f) { + for (var a = 0, h = d.length;a < h;a++) { + if (d[a] === f) { + return a; } } return-1; }; - b.pushUnique = h; - b.unique = function(f) { - for (var b = [], n = 0;n < f.length;n++) { - h(b, f[n]); + a.equals = function(d, f) { + if (d.length !== f.length) { + return!1; } - return b; + for (var a = 0;a < d.length;a++) { + if (d[a] !== f[a]) { + return!1; + } + } + return!0; }; - b.copyFrom = function(f, n) { - f.length = 0; - b.pushMany(f, n); - }; - b.ensureTypedArrayCapacity = function(f, b) { - if (f.length < b) { - var n = f; - f = new f.constructor(d.IntegerUtilities.nearestPowerOfTwo(b)); - f.set(n, 0); + a.pushUnique = h; + a.unique = function(d) { + for (var f = [], a = 0;a < d.length;a++) { + h(f, d[a]); } return f; }; - var n = function() { - function f(f) { - "undefined" === typeof f && (f = 16); + a.copyFrom = function(d, f) { + d.length = 0; + a.pushMany(d, f); + }; + a.ensureTypedArrayCapacity = function(d, f) { + if (d.length < f) { + var a = d; + d = new d.constructor(g.IntegerUtilities.nearestPowerOfTwo(f)); + d.set(a, 0); + } + return d; + }; + var d = function() { + function d(f) { + void 0 === f && (f = 16); this._f32 = this._i32 = this._u16 = this._u8 = null; this._offset = 0; this.ensureCapacity(f); } - f.prototype.reset = function() { + d.prototype.reset = function() { this._offset = 0; }; - Object.defineProperty(f.prototype, "offset", {get:function() { + Object.defineProperty(d.prototype, "offset", {get:function() { return this._offset; }, enumerable:!0, configurable:!0}); - f.prototype.getIndex = function(f) { - a(1 === f || 2 === f || 4 === f || 8 === f || 16 === f); - f = this._offset / f; - a((f | 0) === f); - return f; + d.prototype.getIndex = function(d) { + f(1 === d || 2 === d || 4 === d || 8 === d || 16 === d); + d = this._offset / d; + f((d | 0) === d); + return d; }; - f.prototype.ensureAdditionalCapacity = function() { + d.prototype.ensureAdditionalCapacity = function() { this.ensureCapacity(this._offset + 68); }; - f.prototype.ensureCapacity = function(f) { + d.prototype.ensureCapacity = function(d) { if (!this._u8) { - this._u8 = new Uint8Array(f); + this._u8 = new Uint8Array(d); } else { - if (this._u8.length > f) { + if (this._u8.length > d) { return; } } - var b = 2 * this._u8.length; - b < f && (b = f); - f = new Uint8Array(b); - f.set(this._u8, 0); - this._u8 = f; - this._u16 = new Uint16Array(f.buffer); - this._i32 = new Int32Array(f.buffer); - this._f32 = new Float32Array(f.buffer); + var f = 2 * this._u8.length; + f < d && (f = d); + d = new Uint8Array(f); + d.set(this._u8, 0); + this._u8 = d; + this._u16 = new Uint16Array(d.buffer); + this._i32 = new Int32Array(d.buffer); + this._f32 = new Float32Array(d.buffer); }; - f.prototype.writeInt = function(f) { - a(0 === (this._offset & 3)); + d.prototype.writeInt = function(d) { + f(0 === (this._offset & 3)); this.ensureCapacity(this._offset + 4); - this.writeIntUnsafe(f); + this.writeIntUnsafe(d); }; - f.prototype.writeIntAt = function(f, b) { - a(0 <= b && b <= this._offset); - a(0 === (b & 3)); - this.ensureCapacity(b + 4); - this._i32[b >> 2] = f; + d.prototype.writeIntAt = function(d, q) { + f(0 <= q && q <= this._offset); + f(0 === (q & 3)); + this.ensureCapacity(q + 4); + this._i32[q >> 2] = d; }; - f.prototype.writeIntUnsafe = function(f) { - this._i32[this._offset >> 2] = f; + d.prototype.writeIntUnsafe = function(d) { + this._i32[this._offset >> 2] = d; this._offset += 4; }; - f.prototype.writeFloat = function(f) { - a(0 === (this._offset & 3)); + d.prototype.writeFloat = function(d) { + f(0 === (this._offset & 3)); this.ensureCapacity(this._offset + 4); - this.writeFloatUnsafe(f); + this.writeFloatUnsafe(d); }; - f.prototype.writeFloatUnsafe = function(f) { - this._f32[this._offset >> 2] = f; + d.prototype.writeFloatUnsafe = function(d) { + this._f32[this._offset >> 2] = d; this._offset += 4; }; - f.prototype.write4Floats = function(f, b, n, h) { - a(0 === (this._offset & 3)); + d.prototype.write4Floats = function(d, q, a, h) { + f(0 === (this._offset & 3)); this.ensureCapacity(this._offset + 16); - this.write4FloatsUnsafe(f, b, n, h); + this.write4FloatsUnsafe(d, q, a, h); }; - f.prototype.write4FloatsUnsafe = function(f, b, n, h) { - var a = this._offset >> 2; - this._f32[a + 0] = f; - this._f32[a + 1] = b; - this._f32[a + 2] = n; - this._f32[a + 3] = h; + d.prototype.write4FloatsUnsafe = function(d, f, q, a) { + var h = this._offset >> 2; + this._f32[h + 0] = d; + this._f32[h + 1] = f; + this._f32[h + 2] = q; + this._f32[h + 3] = a; this._offset += 16; }; - f.prototype.write6Floats = function(f, b, n, h, c, g) { - a(0 === (this._offset & 3)); + d.prototype.write6Floats = function(d, q, a, h, b, u) { + f(0 === (this._offset & 3)); this.ensureCapacity(this._offset + 24); - this.write6FloatsUnsafe(f, b, n, h, c, g); + this.write6FloatsUnsafe(d, q, a, h, b, u); }; - f.prototype.write6FloatsUnsafe = function(f, b, n, h, a, c) { - var g = this._offset >> 2; - this._f32[g + 0] = f; - this._f32[g + 1] = b; - this._f32[g + 2] = n; - this._f32[g + 3] = h; - this._f32[g + 4] = a; - this._f32[g + 5] = c; + d.prototype.write6FloatsUnsafe = function(d, f, q, a, h, b) { + var u = this._offset >> 2; + this._f32[u + 0] = d; + this._f32[u + 1] = f; + this._f32[u + 2] = q; + this._f32[u + 3] = a; + this._f32[u + 4] = h; + this._f32[u + 5] = b; this._offset += 24; }; - f.prototype.subF32View = function() { + d.prototype.subF32View = function() { return this._f32.subarray(0, this._offset >> 2); }; - f.prototype.subI32View = function() { + d.prototype.subI32View = function() { return this._i32.subarray(0, this._offset >> 2); }; - f.prototype.subU16View = function() { + d.prototype.subU16View = function() { return this._u16.subarray(0, this._offset >> 1); }; - f.prototype.subU8View = function() { + d.prototype.subU8View = function() { return this._u8.subarray(0, this._offset); }; - f.prototype.hashWords = function(f, b, n) { - b = this._i32; - for (var h = 0;h < n;h++) { - f = (31 * f | 0) + b[h] | 0; + d.prototype.hashWords = function(d, f, q) { + f = this._i32; + for (var a = 0;a < q;a++) { + d = (31 * d | 0) + f[a] | 0; } - return f; + return d; }; - f.prototype.reserve = function(f) { - f = f + 3 & -4; - this.ensureCapacity(this._offset + f); - this._offset += f; + d.prototype.reserve = function(d) { + d = d + 3 & -4; + this.ensureCapacity(this._offset + d); + this._offset += d; }; - return f; + return d; }(); - b.ArrayWriter = n; - })(d.ArrayUtilities || (d.ArrayUtilities = {})); - var a = d.ArrayUtilities, c = function() { - function b(b) { - this._u8 = new Uint8Array(b); - this._u16 = new Uint16Array(b); - this._i32 = new Int32Array(b); - this._f32 = new Float32Array(b); + a.ArrayWriter = d; + })(b = g.ArrayUtilities || (g.ArrayUtilities = {})); + var a = function() { + function a(h) { + this._u8 = new Uint8Array(h); + this._u16 = new Uint16Array(h); + this._i32 = new Int32Array(h); + this._f32 = new Float32Array(h); this._offset = 0; } - Object.defineProperty(b.prototype, "offset", {get:function() { + Object.defineProperty(a.prototype, "offset", {get:function() { return this._offset; }, enumerable:!0, configurable:!0}); - b.prototype.isEmpty = function() { + a.prototype.isEmpty = function() { return this._offset === this._u8.length; }; - b.prototype.readInt = function() { + a.prototype.readInt = function() { k.assert(0 === (this._offset & 3)); k.assert(this._offset <= this._u8.length - 4); - var b = this._i32[this._offset >> 2]; + var a = this._i32[this._offset >> 2]; this._offset += 4; - return b; + return a; }; - b.prototype.readFloat = function() { + a.prototype.readFloat = function() { k.assert(0 === (this._offset & 3)); k.assert(this._offset <= this._u8.length - 4); - var b = this._f32[this._offset >> 2]; + var a = this._f32[this._offset >> 2]; this._offset += 4; - return b; + return a; }; - return b; + return a; }(); - d.ArrayReader = c; - (function(b) { - function h(b, f) { - return Object.prototype.hasOwnProperty.call(b, f); + g.ArrayReader = a; + (function(a) { + function h(d, f) { + return Object.prototype.hasOwnProperty.call(d, f); } - function a(b, f) { - for (var q in f) { - h(f, q) && (b[q] = f[q]); + function f(d, f) { + for (var a in f) { + h(f, a) && (d[a] = f[a]); } } - b.boxValue = function(b) { - return void 0 == b || s(b) ? b : Object(b); + a.boxValue = function(d) { + return void 0 == d || e(d) ? d : Object(d); }; - b.toKeyValueArray = function(b) { - var f = Object.prototype.hasOwnProperty, h = [], a; - for (a in b) { - f.call(b, a) && h.push([a, b[a]]); + a.toKeyValueArray = function(d) { + var f = Object.prototype.hasOwnProperty, a = [], h; + for (h in d) { + f.call(d, h) && a.push([h, d[h]]); } - return h; - }; - b.isPrototypeWriteable = function(b) { - return Object.getOwnPropertyDescriptor(b, "prototype").writable; - }; - b.hasOwnProperty = h; - b.propertyIsEnumerable = function(b, f) { - return Object.prototype.propertyIsEnumerable.call(b, f); + return a; }; - b.getOwnPropertyDescriptor = function(b, f) { - return Object.getOwnPropertyDescriptor(b, f); + a.isPrototypeWriteable = function(d) { + return Object.getOwnPropertyDescriptor(d, "prototype").writable; }; - b.hasOwnGetter = function(b, f) { - var h = Object.getOwnPropertyDescriptor(b, f); - return!(!h || !h.get); + a.hasOwnProperty = h; + a.propertyIsEnumerable = function(d, f) { + return Object.prototype.propertyIsEnumerable.call(d, f); }; - b.getOwnGetter = function(b, f) { - var h = Object.getOwnPropertyDescriptor(b, f); - return h ? h.get : null; + a.getOwnPropertyDescriptor = function(d, f) { + return Object.getOwnPropertyDescriptor(d, f); }; - b.hasOwnSetter = function(b, f) { - var h = Object.getOwnPropertyDescriptor(b, f); - return!(!h || !h.set); + a.hasOwnGetter = function(d, f) { + var a = Object.getOwnPropertyDescriptor(d, f); + return!(!a || !a.get); }; - b.createObject = function(b) { - return Object.create(b); + a.getOwnGetter = function(d, f) { + var a = Object.getOwnPropertyDescriptor(d, f); + return a ? a.get : null; }; - b.createEmptyObject = function() { - return Object.create(null); + a.hasOwnSetter = function(d, f) { + var a = Object.getOwnPropertyDescriptor(d, f); + return!(!a || !a.set); }; - b.createMap = function() { + a.createMap = function() { return Object.create(null); }; - b.createArrayMap = function() { + a.createArrayMap = function() { return[]; }; - b.defineReadOnlyProperty = function(b, f, h) { - Object.defineProperty(b, f, {value:h, writable:!1, configurable:!0, enumerable:!1}); + a.defineReadOnlyProperty = function(d, f, a) { + Object.defineProperty(d, f, {value:a, writable:!1, configurable:!0, enumerable:!1}); }; - b.getOwnPropertyDescriptors = function(h) { - for (var f = b.createMap(), q = Object.getOwnPropertyNames(h), a = 0;a < q.length;a++) { - f[q[a]] = Object.getOwnPropertyDescriptor(h, q[a]); + a.getOwnPropertyDescriptors = function(d) { + for (var f = a.createMap(), h = Object.getOwnPropertyNames(d), s = 0;s < h.length;s++) { + f[h[s]] = Object.getOwnPropertyDescriptor(d, h[s]); } return f; }; - b.cloneObject = function(b) { - var f = Object.create(Object.getPrototypeOf(b)); - a(f, b); - return f; + a.cloneObject = function(d) { + var q = Object.create(Object.getPrototypeOf(d)); + f(q, d); + return q; }; - b.copyProperties = function(b, f) { - for (var h in f) { - b[h] = f[h]; + a.copyProperties = function(d, f) { + for (var a in f) { + d[a] = f[a]; } }; - b.copyOwnProperties = a; - b.copyOwnPropertyDescriptors = function(b, f, q) { - "undefined" === typeof q && (q = !0); - for (var a in f) { - if (h(f, a)) { - var c = Object.getOwnPropertyDescriptor(f, a); - if (q || !h(b, a)) { - k.assert(c); + a.copyOwnProperties = f; + a.copyOwnPropertyDescriptors = function(d, f, a) { + void 0 === a && (a = !0); + for (var s in f) { + if (h(f, s)) { + var b = Object.getOwnPropertyDescriptor(f, s); + if (a || !h(d, s)) { + k.assert(b); try { - Object.defineProperty(b, a, c); - } catch (g) { + Object.defineProperty(d, s, b); + } catch (u) { } } } } }; - b.getLatestGetterOrSetterPropertyDescriptor = function(b, f) { - for (var h = {};b;) { - var a = Object.getOwnPropertyDescriptor(b, f); - a && (h.get = h.get || a.get, h.set = h.set || a.set); - if (h.get && h.set) { + a.getLatestGetterOrSetterPropertyDescriptor = function(d, f) { + for (var a = {};d;) { + var h = Object.getOwnPropertyDescriptor(d, f); + h && (a.get = a.get || h.get, a.set = a.set || h.set); + if (a.get && a.set) { break; } - b = Object.getPrototypeOf(b); + d = Object.getPrototypeOf(d); } - return h; - }; - b.defineNonEnumerableGetterOrSetter = function(h, f, q, a) { - var c = b.getLatestGetterOrSetterPropertyDescriptor(h, f); - c.configurable = !0; - c.enumerable = !1; - a ? c.get = q : c.set = q; - Object.defineProperty(h, f, c); - }; - b.defineNonEnumerableGetter = function(b, f, h) { - Object.defineProperty(b, f, {get:h, configurable:!0, enumerable:!1}); - }; - b.defineNonEnumerableSetter = function(b, f, h) { - Object.defineProperty(b, f, {set:h, configurable:!0, enumerable:!1}); - }; - b.defineNonEnumerableProperty = function(b, f, h) { - Object.defineProperty(b, f, {value:h, writable:!0, configurable:!0, enumerable:!1}); - }; - b.defineNonEnumerableForwardingProperty = function(b, f, h) { - Object.defineProperty(b, f, {get:g.makeForwardingGetter(h), set:g.makeForwardingSetter(h), writable:!0, configurable:!0, enumerable:!1}); + return a; }; - b.defineNewNonEnumerableProperty = function(h, f, a) { - k.assert(!Object.prototype.hasOwnProperty.call(h, f), "Property: " + f + " already exits."); - b.defineNonEnumerableProperty(h, f, a); + a.defineNonEnumerableGetterOrSetter = function(d, f, h, s) { + var b = a.getLatestGetterOrSetterPropertyDescriptor(d, f); + b.configurable = !0; + b.enumerable = !1; + s ? b.get = h : b.set = h; + Object.defineProperty(d, f, b); + }; + a.defineNonEnumerableGetter = function(d, f, a) { + Object.defineProperty(d, f, {get:a, configurable:!0, enumerable:!1}); + }; + a.defineNonEnumerableSetter = function(d, f, a) { + Object.defineProperty(d, f, {set:a, configurable:!0, enumerable:!1}); + }; + a.defineNonEnumerableProperty = function(d, f, a) { + Object.defineProperty(d, f, {value:a, writable:!0, configurable:!0, enumerable:!1}); + }; + a.defineNonEnumerableForwardingProperty = function(d, f, a) { + Object.defineProperty(d, f, {get:n.makeForwardingGetter(a), set:n.makeForwardingSetter(a), writable:!0, configurable:!0, enumerable:!1}); + }; + a.defineNewNonEnumerableProperty = function(d, f, h) { + k.assert(!Object.prototype.hasOwnProperty.call(d, f), "Property: " + f + " already exits."); + a.defineNonEnumerableProperty(d, f, h); + }; + a.createPublicAliases = function(d, f) { + for (var a = {value:null, writable:!0, configurable:!0, enumerable:!1}, h = 0;h < f.length;h++) { + var b = f[h]; + a.value = d[b]; + Object.defineProperty(d, "$Bg" + b, a); + } }; - })(d.ObjectUtilities || (d.ObjectUtilities = {})); - (function(b) { - b.makeForwardingGetter = function(b) { - return new Function('return this["' + b + '"]'); + })(g.ObjectUtilities || (g.ObjectUtilities = {})); + var n; + (function(a) { + a.makeForwardingGetter = function(a) { + return new Function('return this["' + a + '"]//# sourceURL=fwd-get-' + a + ".as"); }; - b.makeForwardingSetter = function(b) { - return new Function("value", 'this["' + b + '"] = value;'); + a.makeForwardingSetter = function(a) { + return new Function("value", 'this["' + a + '"] = value;//# sourceURL=fwd-set-' + a + ".as"); }; - b.bindSafely = function(b, a) { - k.assert(!b.boundTo && a); - var n = b.bind(a); - n.boundTo = a; - return n; + a.bindSafely = function(a, f) { + function d() { + return a.apply(f, arguments); + } + k.assert(!a.boundTo); + k.assert(f); + d.boundTo = f; + return d; }; - })(d.FunctionUtilities || (d.FunctionUtilities = {})); - var g = d.FunctionUtilities; - (function(b) { - function h(f) { - return "string" === typeof f ? '"' + f + '"' : "number" === typeof f || "boolean" === typeof f ? String(f) : f instanceof Array ? "[] " + f.length : typeof f; + })(n = g.FunctionUtilities || (g.FunctionUtilities = {})); + (function(a) { + function h(d) { + return "string" === typeof d ? '"' + d + '"' : "number" === typeof d || "boolean" === typeof d ? String(d) : d instanceof Array ? "[] " + d.length : typeof d; } - var a = d.Debug.assert; - b.repeatString = function(f, b) { - for (var h = "", n = 0;n < b;n++) { - h += f; + var f = g.Debug.assert; + a.repeatString = function(d, f) { + for (var q = "", a = 0;a < f;a++) { + q += d; } - return h; - }; - b.memorySizeToString = function(f) { - f |= 0; - return 1024 > f ? f + " B" : 1048576 > f ? (f / 1024).toFixed(2) + "KB" : (f / 1048576).toFixed(2) + "MB"; + return q; }; - b.toSafeString = h; - b.toSafeArrayString = function(f) { - for (var b = [], n = 0;n < f.length;n++) { - b.push(h(f[n])); - } - return b.join(", "); - }; - b.utf8decode = function(f) { - for (var b = new Uint8Array(4 * f.length), h = 0, n = 0, a = f.length;n < a;n++) { - var q = f.charCodeAt(n); - if (127 >= q) { - b[h++] = q; + a.memorySizeToString = function(d) { + d |= 0; + return 1024 > d ? d + " B" : 1048576 > d ? (d / 1024).toFixed(2) + "KB" : (d / 1048576).toFixed(2) + "MB"; + }; + a.toSafeString = h; + a.toSafeArrayString = function(d) { + for (var f = [], q = 0;q < d.length;q++) { + f.push(h(d[q])); + } + return f.join(", "); + }; + a.utf8decode = function(d) { + for (var f = new Uint8Array(4 * d.length), q = 0, a = 0, h = d.length;a < h;a++) { + var x = d.charCodeAt(a); + if (127 >= x) { + f[q++] = x; } else { - if (55296 <= q && 56319 >= q) { - var c = f.charCodeAt(n + 1); - 56320 <= c && 57343 >= c && (q = ((q & 1023) << 10) + (c & 1023) + 65536, ++n); + if (55296 <= x && 56319 >= x) { + var b = d.charCodeAt(a + 1); + 56320 <= b && 57343 >= b && (x = ((x & 1023) << 10) + (b & 1023) + 65536, ++a); } - 0 !== (q & 4292870144) ? (b[h++] = 248 | q >>> 24 & 3, b[h++] = 128 | q >>> 18 & 63, b[h++] = 128 | q >>> 12 & 63, b[h++] = 128 | q >>> 6 & 63) : 0 !== (q & 4294901760) ? (b[h++] = 240 | q >>> 18 & 7, b[h++] = 128 | q >>> 12 & 63, b[h++] = 128 | q >>> 6 & 63) : 0 !== (q & 4294965248) ? (b[h++] = 224 | q >>> 12 & 15, b[h++] = 128 | q >>> 6 & 63) : b[h++] = 192 | q >>> 6 & 31; - b[h++] = 128 | q & 63; + 0 !== (x & 4292870144) ? (f[q++] = 248 | x >>> 24 & 3, f[q++] = 128 | x >>> 18 & 63, f[q++] = 128 | x >>> 12 & 63, f[q++] = 128 | x >>> 6 & 63) : 0 !== (x & 4294901760) ? (f[q++] = 240 | x >>> 18 & 7, f[q++] = 128 | x >>> 12 & 63, f[q++] = 128 | x >>> 6 & 63) : 0 !== (x & 4294965248) ? (f[q++] = 224 | x >>> 12 & 15, f[q++] = 128 | x >>> 6 & 63) : f[q++] = 192 | x >>> 6 & 31; + f[q++] = 128 | x & 63; } } - return b.subarray(0, h); + return f.subarray(0, q); }; - b.utf8encode = function(f) { - for (var b = 0, h = "";b < f.length;) { - var n = f[b++] & 255; - if (127 >= n) { - h += String.fromCharCode(n); + a.utf8encode = function(d) { + for (var f = 0, q = "";f < d.length;) { + var a = d[f++] & 255; + if (127 >= a) { + q += String.fromCharCode(a); } else { - var q = 192, a = 5; + var h = 192, x = 5; do { - if ((n & (q >> 1 | 128)) === q) { + if ((a & (h >> 1 | 128)) === h) { break; } - q = q >> 1 | 128; - --a; - } while (0 <= a); - if (0 >= a) { - h += String.fromCharCode(n); + h = h >> 1 | 128; + --x; + } while (0 <= x); + if (0 >= x) { + q += String.fromCharCode(a); } else { - for (var n = n & (1 << a) - 1, q = !1, c = 5;c >= a;--c) { - var g = f[b++]; - if (128 != (g & 192)) { - q = !0; + for (var a = a & (1 << x) - 1, h = !1, b = 5;b >= x;--b) { + var s = d[f++]; + if (128 != (s & 192)) { + h = !0; break; } - n = n << 6 | g & 63; + a = a << 6 | s & 63; } - if (q) { - for (a = b - (7 - c);a < b;++a) { - h += String.fromCharCode(f[a] & 255); + if (h) { + for (x = f - (7 - b);x < f;++x) { + q += String.fromCharCode(d[x] & 255); } } else { - h = 65536 <= n ? h + String.fromCharCode(n - 65536 >> 10 & 1023 | 55296, n & 1023 | 56320) : h + String.fromCharCode(n); + q = 65536 <= a ? q + String.fromCharCode(a - 65536 >> 10 & 1023 | 55296, a & 1023 | 56320) : q + String.fromCharCode(a); } } } } - return h; + return q; }; - b.base64ArrayBuffer = function(f) { - var b = ""; - f = new Uint8Array(f); - for (var h = f.byteLength, n = h % 3, h = h - n, q, a, c, g, p = 0;p < h;p += 3) { - g = f[p] << 16 | f[p + 1] << 8 | f[p + 2], q = (g & 16515072) >> 18, a = (g & 258048) >> 12, c = (g & 4032) >> 6, g &= 63, b += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[q] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[a] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[g]; + a.base64ArrayBuffer = function(d) { + var f = ""; + d = new Uint8Array(d); + for (var q = d.byteLength, a = q % 3, q = q - a, h, x, b, s, u = 0;u < q;u += 3) { + s = d[u] << 16 | d[u + 1] << 8 | d[u + 2], h = (s & 16515072) >> 18, x = (s & 258048) >> 12, b = (s & 4032) >> 6, s &= 63, f += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[h] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[x] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[b] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[s]; } - 1 == n ? (g = f[h], b += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(g & 252) >> 2] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(g & 3) << 4] + "==") : 2 == n && (g = f[h] << 8 | f[h + 1], b += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(g & 64512) >> 10] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(g & 1008) >> 4] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(g & 15) << + 1 == a ? (s = d[q], f += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(s & 252) >> 2] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(s & 3) << 4] + "==") : 2 == a && (s = d[q] << 8 | d[q + 1], f += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(s & 64512) >> 10] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(s & 1008) >> 4] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(s & 15) << 2] + "="); - return b; - }; - b.escapeString = function(f) { - void 0 !== f && (f = f.replace(/[^\w$]/gi, "$"), /^\d/.test(f) && (f = "$" + f)); return f; }; - b.fromCharCodeArray = function(f) { - for (var b = "", h = 0;h < f.length;h += 16384) { - var n = Math.min(f.length - h, 16384), b = b + String.fromCharCode.apply(null, f.subarray(h, h + n)) - } - return b; + a.escapeString = function(d) { + void 0 !== d && (d = d.replace(/[^\w$]/gi, "$"), /^\d/.test(d) && (d = "$" + d)); + return d; }; - b.variableLengthEncodeInt32 = function(f) { - var h = 32 - Math.clz32(f); - a(32 >= h, h); - for (var n = Math.ceil(h / 6), q = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_"[n], c = n - 1;0 <= c;c--) { - q += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_"[f >> 6 * c & 63]; + a.fromCharCodeArray = function(d) { + for (var f = "", q = 0;q < d.length;q += 16384) { + var a = Math.min(d.length - q, 16384), f = f + String.fromCharCode.apply(null, d.subarray(q, q + a)) } - a(b.variableLengthDecodeInt32(q) === f, f + " : " + q + " - " + n + " bits: " + h); - return q; - }; - b.toEncoding = function(f) { - return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_"[f]; + return f; }; - b.fromEncoding = function(f) { - f = f.charCodeAt(0); - if (65 <= f && 90 >= f) { - return f - 65; + a.variableLengthEncodeInt32 = function(d) { + var q = 32 - Math.clz32(d); + f(32 >= q, q); + for (var h = Math.ceil(q / 6), x = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_"[h], b = h - 1;0 <= b;b--) { + x += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_"[d >> 6 * b & 63]; + } + f(a.variableLengthDecodeInt32(x) === d, d + " : " + x + " - " + h + " bits: " + q); + return x; + }; + a.toEncoding = function(d) { + return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_"[d]; + }; + a.fromEncoding = function(d) { + d = d.charCodeAt(0); + if (65 <= d && 90 >= d) { + return d - 65; } - if (97 <= f && 122 >= f) { - return f - 71; + if (97 <= d && 122 >= d) { + return d - 71; } - if (48 <= f && 57 >= f) { - return f + 4; + if (48 <= d && 57 >= d) { + return d + 4; } - if (36 === f) { + if (36 === d) { return 62; } - if (95 === f) { + if (95 === d) { return 63; } - a(!1, "Invalid Encoding"); + f(!1, "Invalid Encoding"); }; - b.variableLengthDecodeInt32 = function(f) { - for (var h = b.fromEncoding(f[0]), n = 0, q = 0;q < h;q++) { - var a = 6 * (h - q - 1), n = n | b.fromEncoding(f[1 + q]) << a + a.variableLengthDecodeInt32 = function(d) { + for (var f = a.fromEncoding(d[0]), q = 0, h = 0;h < f;h++) { + var x = 6 * (f - h - 1), q = q | a.fromEncoding(d[1 + h]) << x } - return n; + return q; }; - b.trimMiddle = function(f, b) { - if (f.length <= b) { - return f; + a.trimMiddle = function(d, f) { + if (d.length <= f) { + return d; } - var h = b >> 1, n = b - h - 1; - return f.substr(0, h) + "\u2026" + f.substr(f.length - n, n); + var q = f >> 1, a = f - q - 1; + return d.substr(0, q) + "\u2026" + d.substr(d.length - a, a); }; - b.multiple = function(f, b) { - for (var h = "", n = 0;n < b;n++) { - h += f; - } - return h; - }; - b.indexOfAny = function(f, b, h) { - for (var n = f.length, q = 0;q < b.length;q++) { - var a = f.indexOf(b[q], h); - 0 <= a && (n = Math.min(n, a)); - } - return n === f.length ? -1 : n; - }; - var n = Array(3), f = Array(4), q = Array(5), c = Array(6), g = Array(7), p = Array(8), r = Array(9); - b.concat3 = function(f, b, h) { - n[0] = f; - n[1] = b; - n[2] = h; - return n.join(""); + a.multiple = function(d, f) { + for (var q = "", a = 0;a < f;a++) { + q += d; + } + return q; }; - b.concat4 = function(b, h, n, q) { - f[0] = b; - f[1] = h; - f[2] = n; - f[3] = q; - return f.join(""); - }; - b.concat5 = function(f, b, h, n, a) { - q[0] = f; - q[1] = b; - q[2] = h; - q[3] = n; - q[4] = a; + a.indexOfAny = function(d, f, q) { + for (var a = d.length, h = 0;h < f.length;h++) { + var x = d.indexOf(f[h], q); + 0 <= x && (a = Math.min(a, x)); + } + return a === d.length ? -1 : a; + }; + var d = Array(3), q = Array(4), x = Array(5), b = Array(6), n = Array(7), p = Array(8), r = Array(9); + a.concat3 = function(f, q, a) { + d[0] = f; + d[1] = q; + d[2] = a; + return d.join(""); + }; + a.concat4 = function(d, f, a, h) { + q[0] = d; + q[1] = f; + q[2] = a; + q[3] = h; return q.join(""); }; - b.concat6 = function(f, b, h, n, q, a) { - c[0] = f; - c[1] = b; - c[2] = h; - c[3] = n; - c[4] = q; - c[5] = a; - return c.join(""); - }; - b.concat7 = function(f, b, h, n, q, a, c) { - g[0] = f; - g[1] = b; - g[2] = h; - g[3] = n; - g[4] = q; - g[5] = a; - g[6] = c; - return g.join(""); - }; - b.concat8 = function(f, b, h, n, q, a, c, g) { - p[0] = f; - p[1] = b; - p[2] = h; - p[3] = n; - p[4] = q; - p[5] = a; - p[6] = c; - p[7] = g; + a.concat5 = function(d, f, q, a, h) { + x[0] = d; + x[1] = f; + x[2] = q; + x[3] = a; + x[4] = h; + return x.join(""); + }; + a.concat6 = function(d, f, q, a, h, x) { + b[0] = d; + b[1] = f; + b[2] = q; + b[3] = a; + b[4] = h; + b[5] = x; + return b.join(""); + }; + a.concat7 = function(d, f, q, a, h, x, b) { + n[0] = d; + n[1] = f; + n[2] = q; + n[3] = a; + n[4] = h; + n[5] = x; + n[6] = b; + return n.join(""); + }; + a.concat8 = function(d, f, q, a, h, x, b, s) { + p[0] = d; + p[1] = f; + p[2] = q; + p[3] = a; + p[4] = h; + p[5] = x; + p[6] = b; + p[7] = s; return p.join(""); }; - b.concat9 = function(f, b, h, n, q, a, c, g, p) { - r[0] = f; - r[1] = b; - r[2] = h; - r[3] = n; - r[4] = q; - r[5] = a; - r[6] = c; - r[7] = g; - r[8] = p; + a.concat9 = function(d, f, q, a, h, x, b, s, u) { + r[0] = d; + r[1] = f; + r[2] = q; + r[3] = a; + r[4] = h; + r[5] = x; + r[6] = b; + r[7] = s; + r[8] = u; return r.join(""); }; - })(d.StringUtilities || (d.StringUtilities = {})); - (function(b) { - var h = new Uint8Array([7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]), a = new Int32Array([-680876936, -389564586, 606105819, -1044525330, -176418897, 1200080426, -1473231341, -45705983, 1770035416, -1958414417, -42063, -1990404162, 1804603682, -40341101, -1502002290, 1236535329, -165796510, -1069501632, + })(g.StringUtilities || (g.StringUtilities = {})); + (function(a) { + var h = new Uint8Array([7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]), f = new Int32Array([-680876936, -389564586, 606105819, -1044525330, -176418897, 1200080426, -1473231341, -45705983, 1770035416, -1958414417, -42063, -1990404162, 1804603682, -40341101, -1502002290, 1236535329, -165796510, -1069501632, 643717713, -373897302, -701558691, 38016083, -660478335, -405537848, 568446438, -1019803690, -187363961, 1163531501, -1444681467, -51403784, 1735328473, -1926607734, -378558, -2022574463, 1839030562, -35309556, -1530992060, 1272893353, -155497632, -1094730640, 681279174, -358537222, -722521979, 76029189, -640364487, -421815835, 530742520, -995338651, -198630844, 1126891415, -1416354905, -57434055, 1700485571, -1894986606, -1051523, -2054922799, 1873313359, -30611744, -1560198380, 1309151649, -145523070, -1120210379, 718787259, -343485551]); - b.hashBytesTo32BitsMD5 = function(b, f, q) { - var c = 1732584193, g = -271733879, p = -1732584194, r = 271733878, l = q + 72 & -64, k = new Uint8Array(l), v; - for (v = 0;v < q;++v) { - k[v] = b[f++]; - } - k[v++] = 128; - for (b = l - 8;v < b;) { - k[v++] = 0; - } - k[v++] = q << 3 & 255; - k[v++] = q >> 5 & 255; - k[v++] = q >> 13 & 255; - k[v++] = q >> 21 & 255; - k[v++] = q >>> 29 & 255; - k[v++] = 0; - k[v++] = 0; - k[v++] = 0; - b = new Int32Array(16); - for (v = 0;v < l;) { - for (q = 0;16 > q;++q, v += 4) { - b[q] = k[v] | k[v + 1] << 8 | k[v + 2] << 16 | k[v + 3] << 24; - } - var e = c; - f = g; - var u = p, d = r, t, m; - for (q = 0;64 > q;++q) { - 16 > q ? (t = f & u | ~f & d, m = q) : 32 > q ? (t = d & f | ~d & u, m = 5 * q + 1 & 15) : 48 > q ? (t = f ^ u ^ d, m = 3 * q + 5 & 15) : (t = u ^ (f | ~d), m = 7 * q & 15); - var s = d, e = e + t + a[q] + b[m] | 0; - t = h[q]; - d = u; - u = f; - f = f + (e << t | e >>> 32 - t) | 0; - e = s; - } - c = c + e | 0; - g = g + f | 0; - p = p + u | 0; - r = r + d | 0; + a.hashBytesTo32BitsMD5 = function(d, q, a) { + var b = 1732584193, u = -271733879, n = -1732584194, p = 271733878, r = a + 72 & -64, c = new Uint8Array(r), k; + for (k = 0;k < a;++k) { + c[k] = d[q++]; + } + c[k++] = 128; + for (d = r - 8;k < d;) { + c[k++] = 0; + } + c[k++] = a << 3 & 255; + c[k++] = a >> 5 & 255; + c[k++] = a >> 13 & 255; + c[k++] = a >> 21 & 255; + c[k++] = a >>> 29 & 255; + c[k++] = 0; + c[k++] = 0; + c[k++] = 0; + d = new Int32Array(16); + for (k = 0;k < r;) { + for (a = 0;16 > a;++a, k += 4) { + d[a] = c[k] | c[k + 1] << 8 | c[k + 2] << 16 | c[k + 3] << 24; + } + var y = b; + q = u; + var l = n, t = p, v, e; + for (a = 0;64 > a;++a) { + 16 > a ? (v = q & l | ~q & t, e = a) : 32 > a ? (v = t & q | ~t & l, e = 5 * a + 1 & 15) : 48 > a ? (v = q ^ l ^ t, e = 3 * a + 5 & 15) : (v = l ^ (q | ~t), e = 7 * a & 15); + var g = t, y = y + v + f[a] + d[e] | 0; + v = h[a]; + t = l; + l = q; + q = q + (y << v | y >>> 32 - v) | 0; + y = g; + } + b = b + y | 0; + u = u + q | 0; + n = n + l | 0; + p = p + t | 0; } - return c; + return b; }; - b.hashBytesTo32BitsAdler = function(b, f, h) { - var a = 1, c = 0; - for (h = f + h;f < h;++f) { - a = (a + (b[f] & 255)) % 65521, c = (c + a) % 65521; + a.hashBytesTo32BitsAdler = function(d, f, a) { + var h = 1, b = 0; + for (a = f + a;f < a;++f) { + h = (h + (d[f] & 255)) % 65521, b = (b + h) % 65521; } - return c << 16 | a; + return b << 16 | h; }; - })(d.HashUtilities || (d.HashUtilities = {})); + })(g.HashUtilities || (g.HashUtilities = {})); var p = function() { - function b() { + function a() { } - b.seed = function(h) { - b._state[0] = h; - b._state[1] = h; - }; - b.next = function() { - var b = this._state, a = Math.imul(18273, b[0] & 65535) + (b[0] >>> 16) | 0; - b[0] = a; - var n = Math.imul(36969, b[1] & 65535) + (b[1] >>> 16) | 0; - b[1] = n; - b = (a << 16) + (n & 65535) | 0; - return 2.3283064365386963E-10 * (0 > b ? b + 4294967296 : b); + a.seed = function(h) { + a._state[0] = h; + a._state[1] = h; + }; + a.next = function() { + var a = this._state, f = Math.imul(18273, a[0] & 65535) + (a[0] >>> 16) | 0; + a[0] = f; + var d = Math.imul(36969, a[1] & 65535) + (a[1] >>> 16) | 0; + a[1] = d; + a = (f << 16) + (d & 65535) | 0; + return 2.3283064365386963E-10 * (0 > a ? a + 4294967296 : a); }; - b._state = new Uint32Array([57005, 48879]); - return b; + a._state = new Uint32Array([57005, 48879]); + return a; }(); - d.Random = p; + g.Random = p; Math.random = function() { return p.next(); }; (function() { - function b() { + function a() { this.id = "$weakmap" + h++; } if ("function" !== typeof jsGlobal.WeakMap) { var h = 0; - b.prototype = {has:function(b) { - return b.hasOwnProperty(this.id); - }, get:function(b, h) { - return b.hasOwnProperty(this.id) ? b[this.id] : h; - }, set:function(b, h) { - Object.defineProperty(b, this.id, {value:h, enumerable:!1, configurable:!0}); + a.prototype = {has:function(f) { + return f.hasOwnProperty(this.id); + }, get:function(f, d) { + return f.hasOwnProperty(this.id) ? f[this.id] : d; + }, set:function(f, d) { + Object.defineProperty(f, this.id, {value:d, enumerable:!1, configurable:!0}); }}; - jsGlobal.WeakMap = b; + jsGlobal.WeakMap = a; } })(); - c = function() { - function b() { + a = function() { + function a() { "undefined" !== typeof netscape && netscape.security.PrivilegeManager ? this._map = new WeakMap : this._list = []; } - b.prototype.clear = function() { + a.prototype.clear = function() { this._map ? this._map.clear() : this._list.length = 0; }; - b.prototype.push = function(b) { - this._map ? this._map.set(b, null) : this._list.push(b); + a.prototype.push = function(a) { + this._map ? (k.assert(!this._map.has(a)), this._map.set(a, null)) : (k.assert(-1 === this._list.indexOf(a)), this._list.push(a)); }; - b.prototype.forEach = function(b) { + a.prototype.remove = function(a) { + this._map ? (k.assert(this._map.has(a)), this._map.delete(a)) : (k.assert(-1 < this._list.indexOf(a)), this._list[this._list.indexOf(a)] = null, k.assert(-1 === this._list.indexOf(a))); + }; + a.prototype.forEach = function(a) { if (this._map) { - "undefined" !== typeof netscape && netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"), Components.utils.nondeterministicGetWeakMapKeys(this._map).forEach(function(f) { - 0 !== f._referenceCount && b(f); + "undefined" !== typeof netscape && netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"), Components.utils.nondeterministicGetWeakMapKeys(this._map).forEach(function(d) { + 0 !== d._referenceCount && a(d); }); } else { - for (var a = this._list, n = 0, f = 0;f < a.length;f++) { - var q = a[f]; - 0 === q._referenceCount ? n++ : b(q); + for (var f = this._list, d = 0, q = 0;q < f.length;q++) { + var x = f[q]; + x && (0 === x._referenceCount ? (f[q] = null, d++) : a(x)); } - if (16 < n && n > a.length >> 2) { - n = []; - for (f = 0;f < a.length;f++) { - 0 < a[f]._referenceCount && n.push(a[f]); + if (16 < d && d > f.length >> 2) { + d = []; + for (q = 0;q < f.length;q++) { + (x = f[q]) && 0 < x._referenceCount && d.push(x); } - this._list = n; + this._list = d; } } }; - Object.defineProperty(b.prototype, "length", {get:function() { + Object.defineProperty(a.prototype, "length", {get:function() { return this._map ? -1 : this._list.length; }, enumerable:!0, configurable:!0}); - return b; + return a; }(); - d.WeakList = c; - (function(b) { - b.pow2 = function(b) { - return b === (b | 0) ? 0 > b ? 1 / (1 << -b) : 1 << b : Math.pow(2, b); + g.WeakList = a; + var y; + (function(a) { + a.pow2 = function(a) { + return a === (a | 0) ? 0 > a ? 1 / (1 << -a) : 1 << a : Math.pow(2, a); }; - b.clamp = function(b, a, n) { - return Math.max(a, Math.min(n, b)); + a.clamp = function(a, f, d) { + return Math.max(f, Math.min(d, a)); }; - b.roundHalfEven = function(b) { - if (.5 === Math.abs(b % 1)) { - var a = Math.floor(b); - return 0 === a % 2 ? a : Math.ceil(b); + a.roundHalfEven = function(a) { + if (.5 === Math.abs(a % 1)) { + var f = Math.floor(a); + return 0 === f % 2 ? f : Math.ceil(a); } - return Math.round(b); + return Math.round(a); }; - b.epsilonEquals = function(b, a) { - return 1E-7 > Math.abs(b - a); + a.epsilonEquals = function(a, f) { + return 1E-7 > Math.abs(a - f); }; - })(d.NumberUtilities || (d.NumberUtilities = {})); - (function(b) { - b[b.MaxU16 = 65535] = "MaxU16"; - b[b.MaxI16 = 32767] = "MaxI16"; - b[b.MinI16 = -32768] = "MinI16"; - })(d.Numbers || (d.Numbers = {})); - (function(b) { - function h(b) { - return 256 * b << 16 >> 16; + })(y = g.NumberUtilities || (g.NumberUtilities = {})); + (function(a) { + a[a.MaxU16 = 65535] = "MaxU16"; + a[a.MaxI16 = 32767] = "MaxI16"; + a[a.MinI16 = -32768] = "MinI16"; + })(g.Numbers || (g.Numbers = {})); + var v; + (function(a) { + function h(d) { + return 256 * d << 16 >> 16; } - var a = new ArrayBuffer(8); - b.i8 = new Int8Array(a); - b.u8 = new Uint8Array(a); - b.i32 = new Int32Array(a); - b.f32 = new Float32Array(a); - b.f64 = new Float64Array(a); - b.nativeLittleEndian = 1 === (new Int8Array((new Int32Array([1])).buffer))[0]; - b.floatToInt32 = function(h) { - b.f32[0] = h; - return b.i32[0]; - }; - b.int32ToFloat = function(h) { - b.i32[0] = h; - return b.f32[0]; - }; - b.swap16 = function(b) { - return(b & 255) << 8 | b >> 8 & 255; - }; - b.swap32 = function(b) { - return(b & 255) << 24 | (b & 65280) << 8 | b >> 8 & 65280 | b >> 24 & 255; - }; - b.toS8U8 = h; - b.fromS8U8 = function(b) { - return b / 256; - }; - b.clampS8U8 = function(b) { - return h(b) / 256; - }; - b.toS16 = function(b) { - return b << 16 >> 16; - }; - b.bitCount = function(b) { - b -= b >> 1 & 1431655765; - b = (b & 858993459) + (b >> 2 & 858993459); - return 16843009 * (b + (b >> 4) & 252645135) >> 24; - }; - b.ones = function(b) { - b -= b >> 1 & 1431655765; - b = (b & 858993459) + (b >> 2 & 858993459); - return 16843009 * (b + (b >> 4) & 252645135) >> 24; - }; - b.trailingZeros = function(h) { - return b.ones((h & -h) - 1); - }; - b.getFlags = function(b, f) { - var h = ""; - for (b = 0;b < f.length;b++) { - b & 1 << b && (h += f[b] + " "); - } - return 0 === h.length ? "" : h.trim(); - }; - b.isPowerOfTwo = function(b) { - return b && 0 === (b & b - 1); - }; - b.roundToMultipleOfFour = function(b) { - return b + 3 & -4; - }; - b.nearestPowerOfTwo = function(b) { - b--; - b |= b >> 1; - b |= b >> 2; - b |= b >> 4; - b |= b >> 8; - b |= b >> 16; - b++; - return b; + var f = new ArrayBuffer(8); + a.i8 = new Int8Array(f); + a.u8 = new Uint8Array(f); + a.i32 = new Int32Array(f); + a.f32 = new Float32Array(f); + a.f64 = new Float64Array(f); + a.nativeLittleEndian = 1 === (new Int8Array((new Int32Array([1])).buffer))[0]; + a.floatToInt32 = function(d) { + a.f32[0] = d; + return a.i32[0]; + }; + a.int32ToFloat = function(d) { + a.i32[0] = d; + return a.f32[0]; + }; + a.swap16 = function(d) { + return(d & 255) << 8 | d >> 8 & 255; + }; + a.swap32 = function(d) { + return(d & 255) << 24 | (d & 65280) << 8 | d >> 8 & 65280 | d >> 24 & 255; + }; + a.toS8U8 = h; + a.fromS8U8 = function(d) { + return d / 256; + }; + a.clampS8U8 = function(d) { + return h(d) / 256; + }; + a.toS16 = function(d) { + return d << 16 >> 16; + }; + a.bitCount = function(d) { + d -= d >> 1 & 1431655765; + d = (d & 858993459) + (d >> 2 & 858993459); + return 16843009 * (d + (d >> 4) & 252645135) >> 24; + }; + a.ones = function(d) { + d -= d >> 1 & 1431655765; + d = (d & 858993459) + (d >> 2 & 858993459); + return 16843009 * (d + (d >> 4) & 252645135) >> 24; + }; + a.trailingZeros = function(d) { + return a.ones((d & -d) - 1); + }; + a.getFlags = function(d, f) { + var a = ""; + for (d = 0;d < f.length;d++) { + d & 1 << d && (a += f[d] + " "); + } + return 0 === a.length ? "" : a.trim(); + }; + a.isPowerOfTwo = function(d) { + return d && 0 === (d & d - 1); + }; + a.roundToMultipleOfFour = function(d) { + return d + 3 & -4; + }; + a.nearestPowerOfTwo = function(d) { + d--; + d |= d >> 1; + d |= d >> 2; + d |= d >> 4; + d |= d >> 8; + d |= d >> 16; + d++; + return d; }; - b.roundToMultipleOfPowerOfTwo = function(b, f) { - var h = (1 << f) - 1; - return b + h & ~h; - }; - Math.imul || (Math.imul = function(b, f) { - var h = b & 65535, a = f & 65535; - return h * a + ((b >>> 16 & 65535) * a + h * (f >>> 16 & 65535) << 16 >>> 0) | 0; + a.roundToMultipleOfPowerOfTwo = function(d, f) { + var a = (1 << f) - 1; + return d + a & ~a; + }; + Math.imul || (Math.imul = function(d, f) { + var a = d & 65535, h = f & 65535; + return a * h + ((d >>> 16 & 65535) * h + a * (f >>> 16 & 65535) << 16 >>> 0) | 0; }); - Math.clz32 || (Math.clz32 = function(h) { - h |= h >> 1; - h |= h >> 2; - h |= h >> 4; - h |= h >> 8; - return 32 - b.ones(h | h >> 16); + Math.clz32 || (Math.clz32 = function(d) { + d |= d >> 1; + d |= d >> 2; + d |= d >> 4; + d |= d >> 8; + return 32 - a.ones(d | d >> 16); }); - })(d.IntegerUtilities || (d.IntegerUtilities = {})); - var v = d.IntegerUtilities; - (function(b) { - function h(b, h, f, a, c, g) { - return(f - b) * (g - h) - (a - h) * (c - b); + })(v = g.IntegerUtilities || (g.IntegerUtilities = {})); + (function(a) { + function h(f, d, a, h, b, u) { + return(a - f) * (u - d) - (h - d) * (b - f); } - b.pointInPolygon = function(b, h, f) { - for (var a = 0, c = f.length - 2, g = 0;g < c;g += 2) { - var p = f[g + 0], r = f[g + 1], l = f[g + 2], k = f[g + 3]; - (r <= h && k > h || r > h && k <= h) && b < p + (h - r) / (k - r) * (l - p) && a++; + a.pointInPolygon = function(f, d, a) { + for (var h = 0, b = a.length - 2, u = 0;u < b;u += 2) { + var n = a[u + 0], p = a[u + 1], r = a[u + 2], c = a[u + 3]; + (p <= d && c > d || p > d && c <= d) && f < n + (d - p) / (c - p) * (r - n) && h++; } - return 1 === (a & 1); + return 1 === (h & 1); }; - b.signedArea = h; - b.counterClockwise = function(b, a, f, q, c, g) { - return 0 < h(b, a, f, q, c, g); + a.signedArea = h; + a.counterClockwise = function(f, d, a, b, s, u) { + return 0 < h(f, d, a, b, s, u); }; - b.clockwise = function(b, a, f, q, c, g) { - return 0 > h(b, a, f, q, c, g); + a.clockwise = function(f, d, a, b, s, u) { + return 0 > h(f, d, a, b, s, u); }; - b.pointInPolygonInt32 = function(b, h, f) { - b |= 0; - h |= 0; - for (var a = 0, c = f.length - 2, g = 0;g < c;g += 2) { - var p = f[g + 0], r = f[g + 1], l = f[g + 2], k = f[g + 3]; - (r <= h && k > h || r > h && k <= h) && b < p + (h - r) / (k - r) * (l - p) && a++; + a.pointInPolygonInt32 = function(f, d, a) { + f |= 0; + d |= 0; + for (var h = 0, b = a.length - 2, u = 0;u < b;u += 2) { + var n = a[u + 0], p = a[u + 1], r = a[u + 2], c = a[u + 3]; + (p <= d && c > d || p > d && c <= d) && f < n + (d - p) / (c - p) * (r - n) && h++; } - return 1 === (a & 1); + return 1 === (h & 1); }; - })(d.GeometricUtilities || (d.GeometricUtilities = {})); - (function(b) { - b[b.Error = 1] = "Error"; - b[b.Warn = 2] = "Warn"; - b[b.Debug = 4] = "Debug"; - b[b.Log = 8] = "Log"; - b[b.Info = 16] = "Info"; - b[b.All = 31] = "All"; - })(d.LogLevel || (d.LogLevel = {})); - c = function() { - function b(h, a) { - "undefined" === typeof h && (h = !1); + })(g.GeometricUtilities || (g.GeometricUtilities = {})); + (function(a) { + a[a.Error = 1] = "Error"; + a[a.Warn = 2] = "Warn"; + a[a.Debug = 4] = "Debug"; + a[a.Log = 8] = "Log"; + a[a.Info = 16] = "Info"; + a[a.All = 31] = "All"; + })(g.LogLevel || (g.LogLevel = {})); + a = function() { + function a(h, f) { + void 0 === h && (h = !1); this._tab = " "; this._padding = ""; this._suppressOutput = h; - this._out = a || b._consoleOut; - this._outNoNewline = a || b._consoleOutNoNewline; + this._out = f || a._consoleOut; + this._outNoNewline = f || a._consoleOutNoNewline; } - b.prototype.write = function(b, a) { - "undefined" === typeof b && (b = ""); - "undefined" === typeof a && (a = !1); - this._suppressOutput || this._outNoNewline((a ? this._padding : "") + b); - }; - b.prototype.writeLn = function(b) { - "undefined" === typeof b && (b = ""); - this._suppressOutput || this._out(this._padding + b); - }; - b.prototype.writeTimeLn = function(b) { - "undefined" === typeof b && (b = ""); - this._suppressOutput || this._out(this._padding + performance.now().toFixed(2) + " " + b); - }; - b.prototype.writeComment = function(b) { - b = b.split("\n"); - if (1 === b.length) { - this.writeLn("// " + b[0]); + a.prototype.write = function(a, f) { + void 0 === a && (a = ""); + void 0 === f && (f = !1); + this._suppressOutput || this._outNoNewline((f ? this._padding : "") + a); + }; + a.prototype.writeLn = function(a) { + void 0 === a && (a = ""); + this._suppressOutput || this._out(this._padding + a); + }; + a.prototype.writeObject = function(a, f) { + void 0 === a && (a = ""); + this._suppressOutput || this._out(this._padding + a, f); + }; + a.prototype.writeTimeLn = function(a) { + void 0 === a && (a = ""); + this._suppressOutput || this._out(this._padding + performance.now().toFixed(2) + " " + a); + }; + a.prototype.writeComment = function(a) { + a = a.split("\n"); + if (1 === a.length) { + this.writeLn("// " + a[0]); } else { this.writeLn("/**"); - for (var a = 0;a < b.length;a++) { - this.writeLn(" * " + b[a]); + for (var f = 0;f < a.length;f++) { + this.writeLn(" * " + a[f]); } this.writeLn(" */"); } }; - b.prototype.writeLns = function(b) { - b = b.split("\n"); - for (var a = 0;a < b.length;a++) { - this.writeLn(b[a]); + a.prototype.writeLns = function(a) { + a = a.split("\n"); + for (var f = 0;f < a.length;f++) { + this.writeLn(a[f]); } }; - b.prototype.errorLn = function(h) { - b.logLevel & 1 && this.boldRedLn(h); + a.prototype.errorLn = function(h) { + a.logLevel & 1 && this.boldRedLn(h); }; - b.prototype.warnLn = function(h) { - b.logLevel & 2 && this.yellowLn(h); + a.prototype.warnLn = function(h) { + a.logLevel & 2 && this.yellowLn(h); }; - b.prototype.debugLn = function(h) { - b.logLevel & 4 && this.purpleLn(h); + a.prototype.debugLn = function(h) { + a.logLevel & 4 && this.purpleLn(h); }; - b.prototype.logLn = function(h) { - b.logLevel & 8 && this.writeLn(h); + a.prototype.logLn = function(h) { + a.logLevel & 8 && this.writeLn(h); }; - b.prototype.infoLn = function(h) { - b.logLevel & 16 && this.writeLn(h); + a.prototype.infoLn = function(h) { + a.logLevel & 16 && this.writeLn(h); }; - b.prototype.yellowLn = function(h) { - this.colorLn(b.YELLOW, h); + a.prototype.yellowLn = function(h) { + this.colorLn(a.YELLOW, h); }; - b.prototype.greenLn = function(h) { - this.colorLn(b.GREEN, h); + a.prototype.greenLn = function(h) { + this.colorLn(a.GREEN, h); }; - b.prototype.boldRedLn = function(h) { - this.colorLn(b.BOLD_RED, h); + a.prototype.boldRedLn = function(h) { + this.colorLn(a.BOLD_RED, h); }; - b.prototype.redLn = function(h) { - this.colorLn(b.RED, h); + a.prototype.redLn = function(h) { + this.colorLn(a.RED, h); }; - b.prototype.purpleLn = function(h) { - this.colorLn(b.PURPLE, h); + a.prototype.purpleLn = function(h) { + this.colorLn(a.PURPLE, h); }; - b.prototype.colorLn = function(h, a) { - this._suppressOutput || (inBrowser ? this._out(this._padding + a) : this._out(this._padding + h + a + b.ENDC)); + a.prototype.colorLn = function(h, f) { + this._suppressOutput || (inBrowser ? this._out(this._padding + f) : this._out(this._padding + h + f + a.ENDC)); }; - b.prototype.redLns = function(h) { - this.colorLns(b.RED, h); + a.prototype.redLns = function(h) { + this.colorLns(a.RED, h); }; - b.prototype.colorLns = function(b, a) { - for (var n = a.split("\n"), f = 0;f < n.length;f++) { - this.colorLn(b, n[f]); + a.prototype.colorLns = function(a, f) { + for (var d = f.split("\n"), q = 0;q < d.length;q++) { + this.colorLn(a, d[q]); } }; - b.prototype.enter = function(b) { - this._suppressOutput || this._out(this._padding + b); + a.prototype.enter = function(a) { + this._suppressOutput || this._out(this._padding + a); this.indent(); }; - b.prototype.leaveAndEnter = function(b) { - this.leave(b); + a.prototype.leaveAndEnter = function(a) { + this.leave(a); this.indent(); }; - b.prototype.leave = function(b) { + a.prototype.leave = function(a) { this.outdent(); - this._suppressOutput || this._out(this._padding + b); + !this._suppressOutput && a && this._out(this._padding + a); }; - b.prototype.indent = function() { + a.prototype.indent = function() { this._padding += this._tab; }; - b.prototype.outdent = function() { + a.prototype.outdent = function() { 0 < this._padding.length && (this._padding = this._padding.substring(0, this._padding.length - this._tab.length)); }; - b.prototype.writeArray = function(b, a, n) { - "undefined" === typeof a && (a = !1); - "undefined" === typeof n && (n = !1); - a = a || !1; - for (var f = 0, q = b.length;f < q;f++) { - var c = ""; - a && (c = null === b[f] ? "null" : void 0 === b[f] ? "undefined" : b[f].constructor.name, c += " "); - var g = n ? "" : ("" + f).padRight(" ", 4); - this.writeLn(g + c + b[f]); - } - }; - b.PURPLE = "\u001b[94m"; - b.YELLOW = "\u001b[93m"; - b.GREEN = "\u001b[92m"; - b.RED = "\u001b[91m"; - b.BOLD_RED = "\u001b[1;91m"; - b.ENDC = "\u001b[0m"; - b.logLevel = 31; - b._consoleOut = inBrowser ? console.info.bind(console) : print; - b._consoleOutNoNewline = inBrowser ? console.info.bind(console) : putstr; - return b; + a.prototype.writeArray = function(a, f, d) { + void 0 === f && (f = !1); + void 0 === d && (d = !1); + f = f || !1; + for (var q = 0, b = a.length;q < b;q++) { + var s = ""; + f && (s = null === a[q] ? "null" : void 0 === a[q] ? "undefined" : a[q].constructor.name, s += " "); + var n = d ? "" : ("" + q).padRight(" ", 4); + this.writeLn(n + s + a[q]); + } + }; + a.PURPLE = "\u001b[94m"; + a.YELLOW = "\u001b[93m"; + a.GREEN = "\u001b[92m"; + a.RED = "\u001b[91m"; + a.BOLD_RED = "\u001b[1;91m"; + a.ENDC = "\u001b[0m"; + a.logLevel = 31; + a._consoleOut = console.info.bind(console); + a._consoleOutNoNewline = console.info.bind(console); + return a; }(); - d.IndentingWriter = c; - var u = function() { - return function(b, h) { - this.value = b; + g.IndentingWriter = a; + var l = function() { + return function(a, h) { + this.value = a; this.next = h; }; - }(), c = function() { - function b(b) { - k.assert(b); - this._compare = b; + }(), a = function() { + function a(h) { + k.assert(h); + this._compare = h; this._head = null; this._length = 0; } - b.prototype.push = function(b) { - k.assert(void 0 !== b); + a.prototype.push = function(a) { + k.assert(void 0 !== a); this._length++; if (this._head) { - var a = this._head, n = null; - b = new u(b, null); - for (var f = this._compare;a;) { - if (0 < f(a.value, b.value)) { - n ? (b.next = a, n.next = b) : (b.next = this._head, this._head = b); + var f = this._head, d = null; + a = new l(a, null); + for (var q = this._compare;f;) { + if (0 < q(f.value, a.value)) { + d ? (a.next = f, d.next = a) : (a.next = this._head, this._head = a); return; } - n = a; - a = a.next; + d = f; + f = f.next; } - n.next = b; + d.next = a; } else { - this._head = new u(b, null); + this._head = new l(a, null); } }; - b.prototype.forEach = function(h) { - for (var a = this._head, n = null;a;) { - var f = h(a.value); - if (f === b.RETURN) { + a.prototype.forEach = function(h) { + for (var f = this._head, d = null;f;) { + var q = h(f.value); + if (q === a.RETURN) { break; } else { - f === b.DELETE ? a = n ? n.next = a.next : this._head = this._head.next : (n = a, a = a.next); + q === a.DELETE ? f = d ? d.next = f.next : this._head = this._head.next : (d = f, f = f.next); } } }; - b.prototype.isEmpty = function() { + a.prototype.isEmpty = function() { return!this._head; }; - b.prototype.pop = function() { + a.prototype.pop = function() { if (this._head) { this._length--; - var b = this._head; + var a = this._head; this._head = this._head.next; - return b.value; + return a.value; } }; - b.prototype.contains = function(b) { - for (var a = this._head;a;) { - if (a.value === b) { + a.prototype.contains = function(a) { + for (var f = this._head;f;) { + if (f.value === a) { return!0; } - a = a.next; + f = f.next; } return!1; }; - b.prototype.toString = function() { - for (var b = "[", a = this._head;a;) { - b += a.value.toString(), (a = a.next) && (b += ","); + a.prototype.toString = function() { + for (var a = "[", f = this._head;f;) { + a += f.value.toString(), (f = f.next) && (a += ","); } - return b + "]"; + return a + "]"; }; - b.RETURN = 1; - b.DELETE = 2; - return b; + a.RETURN = 1; + a.DELETE = 2; + return a; }(); - d.SortedList = c; - c = function() { - function b(b, a) { - "undefined" === typeof a && (a = 12); + g.SortedList = a; + a = function() { + function a(h, f) { + void 0 === f && (f = 12); this.start = this.index = 0; - this._size = 1 << a; + this._size = 1 << f; this._mask = this._size - 1; - this.array = new b(this._size); + this.array = new h(this._size); } - b.prototype.get = function(b) { - return this.array[b]; + a.prototype.get = function(a) { + return this.array[a]; }; - b.prototype.forEachInReverse = function(b) { + a.prototype.forEachInReverse = function(a) { if (!this.isEmpty()) { - for (var a = 0 === this.index ? this._size - 1 : this.index - 1, n = this.start - 1 & this._mask;a !== n && !b(this.array[a], a);) { - a = 0 === a ? this._size - 1 : a - 1; + for (var f = 0 === this.index ? this._size - 1 : this.index - 1, d = this.start - 1 & this._mask;f !== d && !a(this.array[f], f);) { + f = 0 === f ? this._size - 1 : f - 1; } } }; - b.prototype.write = function(b) { - this.array[this.index] = b; + a.prototype.write = function(a) { + this.array[this.index] = a; this.index = this.index + 1 & this._mask; this.index === this.start && (this.start = this.start + 1 & this._mask); }; - b.prototype.isFull = function() { + a.prototype.isFull = function() { return(this.index + 1 & this._mask) === this.start; }; - b.prototype.isEmpty = function() { + a.prototype.isEmpty = function() { return this.index === this.start; }; - b.prototype.reset = function() { + a.prototype.reset = function() { this.start = this.index = 0; }; - return b; + return a; }(); - d.CircularBuffer = c; - (function(b) { - function h(f) { - return f + (b.BITS_PER_WORD - 1) >> b.ADDRESS_BITS_PER_WORD << b.ADDRESS_BITS_PER_WORD; + g.CircularBuffer = a; + (function(a) { + function h(d) { + return d + (a.BITS_PER_WORD - 1) >> a.ADDRESS_BITS_PER_WORD << a.ADDRESS_BITS_PER_WORD; } - function a(f, b) { - f = f || "1"; - b = b || "0"; - for (var h = "", q = 0;q < length;q++) { - h += this.get(q) ? f : b; - } - return h; - } - function n(f) { - for (var b = [], h = 0;h < length;h++) { - this.get(h) && b.push(f ? f[h] : h); - } - return b.join(", "); - } - var f = d.Debug.assert; - b.ADDRESS_BITS_PER_WORD = 5; - b.BITS_PER_WORD = 1 << b.ADDRESS_BITS_PER_WORD; - b.BIT_INDEX_MASK = b.BITS_PER_WORD - 1; - var q = function() { - function a(f) { + function f(d, a) { + d = d || "1"; + a = a || "0"; + for (var f = "", q = 0;q < length;q++) { + f += this.get(q) ? d : a; + } + return f; + } + function d(d) { + for (var a = [], f = 0;f < length;f++) { + this.get(f) && a.push(d ? d[f] : f); + } + return a.join(", "); + } + var q = g.Debug.assert; + a.ADDRESS_BITS_PER_WORD = 5; + a.BITS_PER_WORD = 1 << a.ADDRESS_BITS_PER_WORD; + a.BIT_INDEX_MASK = a.BITS_PER_WORD - 1; + var b = function() { + function d(f) { this.size = h(f); this.dirty = this.count = 0; this.length = f; - this.bits = new Uint32Array(this.size >> b.ADDRESS_BITS_PER_WORD); + this.bits = new Uint32Array(this.size >> a.ADDRESS_BITS_PER_WORD); } - a.prototype.recount = function() { + d.prototype.recount = function() { if (this.dirty) { - for (var f = this.bits, b = 0, h = 0, a = f.length;h < a;h++) { - var q = f[h], q = q - (q >> 1 & 1431655765), q = (q & 858993459) + (q >> 2 & 858993459), b = b + (16843009 * (q + (q >> 4) & 252645135) >> 24) + for (var d = this.bits, a = 0, f = 0, q = d.length;f < q;f++) { + var h = d[f], h = h - (h >> 1 & 1431655765), h = (h & 858993459) + (h >> 2 & 858993459), a = a + (16843009 * (h + (h >> 4) & 252645135) >> 24) } - this.count = b; + this.count = a; this.dirty = 0; } }; - a.prototype.set = function(f) { - var h = f >> b.ADDRESS_BITS_PER_WORD, a = this.bits[h]; - f = a | 1 << (f & b.BIT_INDEX_MASK); - this.bits[h] = f; - this.dirty |= a ^ f; - }; - a.prototype.setAll = function() { - for (var f = this.bits, b = 0, h = f.length;b < h;b++) { - f[b] = 4294967295; + d.prototype.set = function(d) { + var f = d >> a.ADDRESS_BITS_PER_WORD, q = this.bits[f]; + d = q | 1 << (d & a.BIT_INDEX_MASK); + this.bits[f] = d; + this.dirty |= q ^ d; + }; + d.prototype.setAll = function() { + for (var d = this.bits, a = 0, f = d.length;a < f;a++) { + d[a] = 4294967295; } this.count = this.size; this.dirty = 0; }; - a.prototype.assign = function(f) { - this.count = f.count; - this.dirty = f.dirty; - this.size = f.size; - for (var b = 0, h = this.bits.length;b < h;b++) { - this.bits[b] = f.bits[b]; - } - }; - a.prototype.clear = function(f) { - var h = f >> b.ADDRESS_BITS_PER_WORD, a = this.bits[h]; - f = a & ~(1 << (f & b.BIT_INDEX_MASK)); - this.bits[h] = f; - this.dirty |= a ^ f; - }; - a.prototype.get = function(f) { - return 0 !== (this.bits[f >> b.ADDRESS_BITS_PER_WORD] & 1 << (f & b.BIT_INDEX_MASK)); - }; - a.prototype.clearAll = function() { - for (var f = this.bits, b = 0, h = f.length;b < h;b++) { - f[b] = 0; + d.prototype.assign = function(d) { + this.count = d.count; + this.dirty = d.dirty; + this.size = d.size; + for (var a = 0, f = this.bits.length;a < f;a++) { + this.bits[a] = d.bits[a]; + } + }; + d.prototype.clear = function(d) { + var f = d >> a.ADDRESS_BITS_PER_WORD, q = this.bits[f]; + d = q & ~(1 << (d & a.BIT_INDEX_MASK)); + this.bits[f] = d; + this.dirty |= q ^ d; + }; + d.prototype.get = function(d) { + return 0 !== (this.bits[d >> a.ADDRESS_BITS_PER_WORD] & 1 << (d & a.BIT_INDEX_MASK)); + }; + d.prototype.clearAll = function() { + for (var d = this.bits, a = 0, f = d.length;a < f;a++) { + d[a] = 0; } this.dirty = this.count = 0; }; - a.prototype._union = function(f) { - var b = this.dirty, h = this.bits; - f = f.bits; - for (var a = 0, q = h.length;a < q;a++) { - var n = h[a], c = n | f[a]; - h[a] = c; - b |= n ^ c; - } - this.dirty = b; - }; - a.prototype.intersect = function(f) { - var b = this.dirty, h = this.bits; - f = f.bits; - for (var a = 0, q = h.length;a < q;a++) { - var n = h[a], c = n & f[a]; - h[a] = c; - b |= n ^ c; - } - this.dirty = b; - }; - a.prototype.subtract = function(f) { - var b = this.dirty, h = this.bits; - f = f.bits; - for (var a = 0, q = h.length;a < q;a++) { - var n = h[a], c = n & ~f[a]; - h[a] = c; - b |= n ^ c; - } - this.dirty = b; - }; - a.prototype.negate = function() { - for (var f = this.dirty, b = this.bits, h = 0, a = b.length;h < a;h++) { - var q = b[h], n = ~q; - b[h] = n; - f |= q ^ n; - } - this.dirty = f; - }; - a.prototype.forEach = function(h) { - f(h); - for (var a = this.bits, q = 0, n = a.length;q < n;q++) { - var c = a[q]; - if (c) { - for (var g = 0;g < b.BITS_PER_WORD;g++) { - c & 1 << g && h(q * b.BITS_PER_WORD + g); + d.prototype._union = function(d) { + var a = this.dirty, f = this.bits; + d = d.bits; + for (var q = 0, h = f.length;q < h;q++) { + var b = f[q], x = b | d[q]; + f[q] = x; + a |= b ^ x; + } + this.dirty = a; + }; + d.prototype.intersect = function(d) { + var a = this.dirty, f = this.bits; + d = d.bits; + for (var q = 0, h = f.length;q < h;q++) { + var b = f[q], x = b & d[q]; + f[q] = x; + a |= b ^ x; + } + this.dirty = a; + }; + d.prototype.subtract = function(d) { + var a = this.dirty, f = this.bits; + d = d.bits; + for (var q = 0, h = f.length;q < h;q++) { + var b = f[q], x = b & ~d[q]; + f[q] = x; + a |= b ^ x; + } + this.dirty = a; + }; + d.prototype.negate = function() { + for (var d = this.dirty, a = this.bits, f = 0, q = a.length;f < q;f++) { + var h = a[f], b = ~h; + a[f] = b; + d |= h ^ b; + } + this.dirty = d; + }; + d.prototype.forEach = function(d) { + q(d); + for (var f = this.bits, h = 0, b = f.length;h < b;h++) { + var x = f[h]; + if (x) { + for (var s = 0;s < a.BITS_PER_WORD;s++) { + x & 1 << s && d(h * a.BITS_PER_WORD + s); } } } }; - a.prototype.toArray = function() { - for (var f = [], h = this.bits, a = 0, q = h.length;a < q;a++) { - var n = h[a]; - if (n) { - for (var c = 0;c < b.BITS_PER_WORD;c++) { - n & 1 << c && f.push(a * b.BITS_PER_WORD + c); + d.prototype.toArray = function() { + for (var d = [], f = this.bits, q = 0, h = f.length;q < h;q++) { + var b = f[q]; + if (b) { + for (var x = 0;x < a.BITS_PER_WORD;x++) { + b & 1 << x && d.push(q * a.BITS_PER_WORD + x); } } } - return f; + return d; }; - a.prototype.equals = function(f) { - if (this.size !== f.size) { + d.prototype.equals = function(d) { + if (this.size !== d.size) { return!1; } - var b = this.bits; - f = f.bits; - for (var h = 0, a = b.length;h < a;h++) { - if (b[h] !== f[h]) { + var a = this.bits; + d = d.bits; + for (var f = 0, q = a.length;f < q;f++) { + if (a[f] !== d[f]) { return!1; } } return!0; }; - a.prototype.contains = function(f) { - if (this.size !== f.size) { + d.prototype.contains = function(d) { + if (this.size !== d.size) { return!1; } - var b = this.bits; - f = f.bits; - for (var h = 0, a = b.length;h < a;h++) { - if ((b[h] | f[h]) !== b[h]) { + var a = this.bits; + d = d.bits; + for (var f = 0, q = a.length;f < q;f++) { + if ((a[f] | d[f]) !== a[f]) { return!1; } } return!0; }; - a.prototype.isEmpty = function() { + d.prototype.isEmpty = function() { this.recount(); return 0 === this.count; }; - a.prototype.clone = function() { - var f = new a(this.length); - f._union(this); - return f; + d.prototype.clone = function() { + var a = new d(this.length); + a._union(this); + return a; }; - return a; + return d; }(); - b.Uint32ArrayBitSet = q; - var c = function() { - function a(f) { + a.Uint32ArrayBitSet = b; + var s = function() { + function d(a) { this.dirty = this.count = 0; - this.size = h(f); + this.size = h(a); this.bits = 0; this.singleWord = !0; - this.length = f; + this.length = a; } - a.prototype.recount = function() { + d.prototype.recount = function() { if (this.dirty) { - var f = this.bits, f = f - (f >> 1 & 1431655765), f = (f & 858993459) + (f >> 2 & 858993459); - this.count = 0 + (16843009 * (f + (f >> 4) & 252645135) >> 24); + var d = this.bits, d = d - (d >> 1 & 1431655765), d = (d & 858993459) + (d >> 2 & 858993459); + this.count = 0 + (16843009 * (d + (d >> 4) & 252645135) >> 24); this.dirty = 0; } }; - a.prototype.set = function(f) { - var a = this.bits; - this.bits = f = a | 1 << (f & b.BIT_INDEX_MASK); - this.dirty |= a ^ f; + d.prototype.set = function(d) { + var f = this.bits; + this.bits = d = f | 1 << (d & a.BIT_INDEX_MASK); + this.dirty |= f ^ d; }; - a.prototype.setAll = function() { + d.prototype.setAll = function() { this.bits = 4294967295; this.count = this.size; this.dirty = 0; }; - a.prototype.assign = function(f) { - this.count = f.count; - this.dirty = f.dirty; - this.size = f.size; - this.bits = f.bits; + d.prototype.assign = function(d) { + this.count = d.count; + this.dirty = d.dirty; + this.size = d.size; + this.bits = d.bits; + }; + d.prototype.clear = function(d) { + var f = this.bits; + this.bits = d = f & ~(1 << (d & a.BIT_INDEX_MASK)); + this.dirty |= f ^ d; }; - a.prototype.clear = function(f) { - var a = this.bits; - this.bits = f = a & ~(1 << (f & b.BIT_INDEX_MASK)); - this.dirty |= a ^ f; - }; - a.prototype.get = function(f) { - return 0 !== (this.bits & 1 << (f & b.BIT_INDEX_MASK)); + d.prototype.get = function(d) { + return 0 !== (this.bits & 1 << (d & a.BIT_INDEX_MASK)); }; - a.prototype.clearAll = function() { + d.prototype.clearAll = function() { this.dirty = this.count = this.bits = 0; }; - a.prototype._union = function(f) { - var b = this.bits; - this.bits = f = b | f.bits; - this.dirty = b ^ f; - }; - a.prototype.intersect = function(f) { - var b = this.bits; - this.bits = f = b & f.bits; - this.dirty = b ^ f; - }; - a.prototype.subtract = function(f) { - var b = this.bits; - this.bits = f = b & ~f.bits; - this.dirty = b ^ f; - }; - a.prototype.negate = function() { - var f = this.bits, b = ~f; - this.bits = b; - this.dirty = f ^ b; + d.prototype._union = function(d) { + var a = this.bits; + this.bits = d = a | d.bits; + this.dirty = a ^ d; }; - a.prototype.forEach = function(a) { - f(a); - var h = this.bits; - if (h) { - for (var q = 0;q < b.BITS_PER_WORD;q++) { - h & 1 << q && a(q); + d.prototype.intersect = function(d) { + var a = this.bits; + this.bits = d = a & d.bits; + this.dirty = a ^ d; + }; + d.prototype.subtract = function(d) { + var a = this.bits; + this.bits = d = a & ~d.bits; + this.dirty = a ^ d; + }; + d.prototype.negate = function() { + var d = this.bits, a = ~d; + this.bits = a; + this.dirty = d ^ a; + }; + d.prototype.forEach = function(d) { + q(d); + var f = this.bits; + if (f) { + for (var h = 0;h < a.BITS_PER_WORD;h++) { + f & 1 << h && d(h); } } }; - a.prototype.toArray = function() { - var f = [], a = this.bits; - if (a) { - for (var h = 0;h < b.BITS_PER_WORD;h++) { - a & 1 << h && f.push(h); + d.prototype.toArray = function() { + var d = [], f = this.bits; + if (f) { + for (var q = 0;q < a.BITS_PER_WORD;q++) { + f & 1 << q && d.push(q); } } - return f; + return d; }; - a.prototype.equals = function(f) { - return this.bits === f.bits; + d.prototype.equals = function(d) { + return this.bits === d.bits; }; - a.prototype.contains = function(f) { - var b = this.bits; - return(b | f.bits) === b; + d.prototype.contains = function(d) { + var a = this.bits; + return(a | d.bits) === a; }; - a.prototype.isEmpty = function() { + d.prototype.isEmpty = function() { this.recount(); return 0 === this.count; }; - a.prototype.clone = function() { - var f = new a(this.length); - f._union(this); - return f; + d.prototype.clone = function() { + var a = new d(this.length); + a._union(this); + return a; }; - return a; + return d; }(); - b.Uint32BitSet = c; - c.prototype.toString = n; - c.prototype.toBitString = a; - q.prototype.toString = n; - q.prototype.toBitString = a; - b.BitSetFunctor = function(f) { - var a = 1 === h(f) >> b.ADDRESS_BITS_PER_WORD ? c : q; + a.Uint32BitSet = s; + s.prototype.toString = d; + s.prototype.toBitString = f; + b.prototype.toString = d; + b.prototype.toBitString = f; + a.BitSetFunctor = function(d) { + var f = 1 === h(d) >> a.ADDRESS_BITS_PER_WORD ? s : b; return function() { - return new a(f); + return new f(d); }; }; - })(d.BitSets || (d.BitSets = {})); - c = function() { - function b() { - } - b.randomStyle = function() { - b._randomStyleCache || (b._randomStyleCache = "#ff5e3a #ff9500 #ffdb4c #87fc70 #52edc7 #1ad6fd #c644fc #ef4db6 #4a4a4a #dbddde #ff3b30 #ff9500 #ffcc00 #4cd964 #34aadc #007aff #5856d6 #ff2d55 #8e8e93 #c7c7cc #5ad427 #c86edf #d1eefc #e0f8d8 #fb2b69 #f7f7f7 #1d77ef #d6cec3 #55efcb #ff4981 #ffd3e0 #f7f7f7 #ff1300 #1f1f21 #bdbec2 #ff3a2d".split(" ")); - return b._randomStyleCache[b._nextStyle++ % b._randomStyleCache.length]; - }; - b.contrastStyle = function(b) { - b = parseInt(b.substr(1), 16); - return 128 <= (299 * (b >> 16) + 587 * (b >> 8 & 255) + 114 * (b & 255)) / 1E3 ? "#000000" : "#ffffff"; - }; - b.reset = function() { - b._nextStyle = 0; - }; - b.TabToolbar = "#252c33"; - b.Toolbars = "#343c45"; - b.HighlightBlue = "#1d4f73"; - b.LightText = "#f5f7fa"; - b.ForegroundText = "#b6babf"; - b.Black = "#000000"; - b.VeryDark = "#14171a"; - b.Dark = "#181d20"; - b.Light = "#a9bacb"; - b.Grey = "#8fa1b2"; - b.DarkGrey = "#5f7387"; - b.Blue = "#46afe3"; - b.Purple = "#6b7abb"; - b.Pink = "#df80ff"; - b.Red = "#eb5368"; - b.Orange = "#d96629"; - b.LightOrange = "#d99b28"; - b.Green = "#70bf53"; - b.BlueGrey = "#5e88b0"; - b._nextStyle = 0; - return b; + })(g.BitSets || (g.BitSets = {})); + a = function() { + function a() { + } + a.randomStyle = function() { + a._randomStyleCache || (a._randomStyleCache = "#ff5e3a #ff9500 #ffdb4c #87fc70 #52edc7 #1ad6fd #c644fc #ef4db6 #4a4a4a #dbddde #ff3b30 #ff9500 #ffcc00 #4cd964 #34aadc #007aff #5856d6 #ff2d55 #8e8e93 #c7c7cc #5ad427 #c86edf #d1eefc #e0f8d8 #fb2b69 #f7f7f7 #1d77ef #d6cec3 #55efcb #ff4981 #ffd3e0 #f7f7f7 #ff1300 #1f1f21 #bdbec2 #ff3a2d".split(" ")); + return a._randomStyleCache[a._nextStyle++ % a._randomStyleCache.length]; + }; + a.gradientColor = function(h) { + return a._gradient[a._gradient.length * y.clamp(h, 0, 1) | 0]; + }; + a.contrastStyle = function(a) { + a = parseInt(a.substr(1), 16); + return 128 <= (299 * (a >> 16) + 587 * (a >> 8 & 255) + 114 * (a & 255)) / 1E3 ? "#000000" : "#ffffff"; + }; + a.reset = function() { + a._nextStyle = 0; + }; + a.TabToolbar = "#252c33"; + a.Toolbars = "#343c45"; + a.HighlightBlue = "#1d4f73"; + a.LightText = "#f5f7fa"; + a.ForegroundText = "#b6babf"; + a.Black = "#000000"; + a.VeryDark = "#14171a"; + a.Dark = "#181d20"; + a.Light = "#a9bacb"; + a.Grey = "#8fa1b2"; + a.DarkGrey = "#5f7387"; + a.Blue = "#46afe3"; + a.Purple = "#6b7abb"; + a.Pink = "#df80ff"; + a.Red = "#eb5368"; + a.Orange = "#d96629"; + a.LightOrange = "#d99b28"; + a.Green = "#70bf53"; + a.BlueGrey = "#5e88b0"; + a._nextStyle = 0; + a._gradient = "#FF0000 #FF1100 #FF2300 #FF3400 #FF4600 #FF5700 #FF6900 #FF7B00 #FF8C00 #FF9E00 #FFAF00 #FFC100 #FFD300 #FFE400 #FFF600 #F7FF00 #E5FF00 #D4FF00 #C2FF00 #B0FF00 #9FFF00 #8DFF00 #7CFF00 #6AFF00 #58FF00 #47FF00 #35FF00 #24FF00 #12FF00 #00FF00".split(" "); + return a; }(); - d.ColorStyle = c; - c = function() { - function b(b, a, n, f) { - this.xMin = b | 0; - this.yMin = a | 0; - this.xMax = n | 0; - this.yMax = f | 0; - } - b.FromUntyped = function(a) { - return new b(a.xMin, a.yMin, a.xMax, a.yMax); - }; - b.FromRectangle = function(a) { - return new b(20 * a.x | 0, 20 * a.y | 0, 20 * (a.x + a.width) | 0, 20 * (a.y + a.height) | 0); - }; - b.prototype.setElements = function(b, a, n, f) { - this.xMin = b; - this.yMin = a; - this.xMax = n; - this.yMax = f; - }; - b.prototype.copyFrom = function(b) { - this.setElements(b.xMin, b.yMin, b.xMax, b.yMax); - }; - b.prototype.contains = function(b, a) { - return b < this.xMin !== b < this.xMax && a < this.yMin !== a < this.yMax; - }; - b.prototype.unionInPlace = function(b) { - this.xMin = Math.min(this.xMin, b.xMin); - this.yMin = Math.min(this.yMin, b.yMin); - this.xMax = Math.max(this.xMax, b.xMax); - this.yMax = Math.max(this.yMax, b.yMax); - }; - b.prototype.extendByPoint = function(b, a) { - this.extendByX(b); - this.extendByY(a); + g.ColorStyle = a; + a = function() { + function a(h, f, d, q) { + this.xMin = h | 0; + this.yMin = f | 0; + this.xMax = d | 0; + this.yMax = q | 0; + } + a.FromUntyped = function(h) { + return new a(h.xMin, h.yMin, h.xMax, h.yMax); + }; + a.FromRectangle = function(h) { + return new a(20 * h.x | 0, 20 * h.y | 0, 20 * (h.x + h.width) | 0, 20 * (h.y + h.height) | 0); + }; + a.prototype.setElements = function(a, f, d, q) { + this.xMin = a; + this.yMin = f; + this.xMax = d; + this.yMax = q; + }; + a.prototype.copyFrom = function(a) { + this.setElements(a.xMin, a.yMin, a.xMax, a.yMax); + }; + a.prototype.contains = function(a, f) { + return a < this.xMin !== a < this.xMax && f < this.yMin !== f < this.yMax; + }; + a.prototype.unionInPlace = function(a) { + a.isEmpty() || (this.extendByPoint(a.xMin, a.yMin), this.extendByPoint(a.xMax, a.yMax)); + }; + a.prototype.extendByPoint = function(a, f) { + this.extendByX(a); + this.extendByY(f); }; - b.prototype.extendByX = function(b) { - 134217728 === this.xMin ? this.xMin = this.xMax = b : (this.xMin = Math.min(this.xMin, b), this.xMax = Math.max(this.xMax, b)); + a.prototype.extendByX = function(a) { + 134217728 === this.xMin ? this.xMin = this.xMax = a : (this.xMin = Math.min(this.xMin, a), this.xMax = Math.max(this.xMax, a)); }; - b.prototype.extendByY = function(b) { - 134217728 === this.yMin ? this.yMin = this.yMax = b : (this.yMin = Math.min(this.yMin, b), this.yMax = Math.max(this.yMax, b)); + a.prototype.extendByY = function(a) { + 134217728 === this.yMin ? this.yMin = this.yMax = a : (this.yMin = Math.min(this.yMin, a), this.yMax = Math.max(this.yMax, a)); }; - b.prototype.intersects = function(b) { - return this.contains(b.xMin, b.yMin) || this.contains(b.xMax, b.yMax); + a.prototype.intersects = function(a) { + return this.contains(a.xMin, a.yMin) || this.contains(a.xMax, a.yMax); }; - b.prototype.isEmpty = function() { + a.prototype.isEmpty = function() { return this.xMax <= this.xMin || this.yMax <= this.yMin; }; - Object.defineProperty(b.prototype, "width", {get:function() { + Object.defineProperty(a.prototype, "width", {get:function() { return this.xMax - this.xMin; - }, set:function(b) { - this.xMax = this.xMin + b; + }, set:function(a) { + this.xMax = this.xMin + a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "height", {get:function() { + Object.defineProperty(a.prototype, "height", {get:function() { return this.yMax - this.yMin; - }, set:function(b) { - this.yMax = this.yMin + b; + }, set:function(a) { + this.yMax = this.yMin + a; }, enumerable:!0, configurable:!0}); - b.prototype.getBaseWidth = function(b) { - return Math.abs(Math.cos(b)) * (this.xMax - this.xMin) + Math.abs(Math.sin(b)) * (this.yMax - this.yMin); + a.prototype.getBaseWidth = function(a) { + return Math.abs(Math.cos(a)) * (this.xMax - this.xMin) + Math.abs(Math.sin(a)) * (this.yMax - this.yMin); }; - b.prototype.getBaseHeight = function(b) { - return Math.abs(Math.sin(b)) * (this.xMax - this.xMin) + Math.abs(Math.cos(b)) * (this.yMax - this.yMin); + a.prototype.getBaseHeight = function(a) { + return Math.abs(Math.sin(a)) * (this.xMax - this.xMin) + Math.abs(Math.cos(a)) * (this.yMax - this.yMin); }; - b.prototype.setEmpty = function() { + a.prototype.setEmpty = function() { this.xMin = this.yMin = this.xMax = this.yMax = 0; }; - b.prototype.setToSentinels = function() { + a.prototype.setToSentinels = function() { this.xMin = this.yMin = this.xMax = this.yMax = 134217728; }; - b.prototype.clone = function() { - return new b(this.xMin, this.yMin, this.xMax, this.yMax); + a.prototype.clone = function() { + return new a(this.xMin, this.yMin, this.xMax, this.yMax); }; - b.prototype.toString = function() { + a.prototype.toString = function() { return "{ xMin: " + this.xMin + ", xMin: " + this.yMin + ", xMax: " + this.xMax + ", xMax: " + this.yMax + " }"; }; - return b; + return a; }(); - d.Bounds = c; - c = function() { - function b(b, a, n, f) { - k.assert(m(b)); - k.assert(m(a)); - k.assert(m(n)); + g.Bounds = a; + a = function() { + function a(h, f, d, q) { + k.assert(m(h)); k.assert(m(f)); - this._xMin = b | 0; - this._yMin = a | 0; - this._xMax = n | 0; - this._yMax = f | 0; - } - b.FromUntyped = function(a) { - return new b(a.xMin, a.yMin, a.xMax, a.yMax); - }; - b.FromRectangle = function(a) { - return new b(20 * a.x | 0, 20 * a.y | 0, 20 * (a.x + a.width) | 0, 20 * (a.y + a.height) | 0); - }; - b.prototype.setElements = function(b, a, n, f) { - this.xMin = b; - this.yMin = a; - this.xMax = n; - this.yMax = f; - }; - b.prototype.copyFrom = function(b) { - this.setElements(b.xMin, b.yMin, b.xMax, b.yMax); + k.assert(m(d)); + k.assert(m(q)); + this._xMin = h | 0; + this._yMin = f | 0; + this._xMax = d | 0; + this._yMax = q | 0; + } + a.FromUntyped = function(h) { + return new a(h.xMin, h.yMin, h.xMax, h.yMax); + }; + a.FromRectangle = function(h) { + return new a(20 * h.x | 0, 20 * h.y | 0, 20 * (h.x + h.width) | 0, 20 * (h.y + h.height) | 0); + }; + a.prototype.setElements = function(a, f, d, q) { + this.xMin = a; + this.yMin = f; + this.xMax = d; + this.yMax = q; + }; + a.prototype.copyFrom = function(a) { + this.setElements(a.xMin, a.yMin, a.xMax, a.yMax); + }; + a.prototype.contains = function(a, f) { + return a < this.xMin !== a < this.xMax && f < this.yMin !== f < this.yMax; + }; + a.prototype.unionInPlace = function(a) { + a.isEmpty() || (this.extendByPoint(a.xMin, a.yMin), this.extendByPoint(a.xMax, a.yMax)); + }; + a.prototype.extendByPoint = function(a, f) { + this.extendByX(a); + this.extendByY(f); }; - b.prototype.contains = function(b, a) { - return b < this.xMin !== b < this.xMax && a < this.yMin !== a < this.yMax; + a.prototype.extendByX = function(a) { + 134217728 === this.xMin ? this.xMin = this.xMax = a : (this.xMin = Math.min(this.xMin, a), this.xMax = Math.max(this.xMax, a)); }; - b.prototype.unionWith = function(b) { - this._xMin = Math.min(this._xMin, b._xMin); - this._yMin = Math.min(this._yMin, b._yMin); - this._xMax = Math.max(this._xMax, b._xMax); - this._yMax = Math.max(this._yMax, b._yMax); + a.prototype.extendByY = function(a) { + 134217728 === this.yMin ? this.yMin = this.yMax = a : (this.yMin = Math.min(this.yMin, a), this.yMax = Math.max(this.yMax, a)); }; - b.prototype.extendByPoint = function(b, a) { - this.extendByX(b); - this.extendByY(a); + a.prototype.intersects = function(a) { + return this.contains(a._xMin, a._yMin) || this.contains(a._xMax, a._yMax); }; - b.prototype.extendByX = function(b) { - 134217728 === this.xMin ? this.xMin = this.xMax = b : (this.xMin = Math.min(this.xMin, b), this.xMax = Math.max(this.xMax, b)); - }; - b.prototype.extendByY = function(b) { - 134217728 === this.yMin ? this.yMin = this.yMax = b : (this.yMin = Math.min(this.yMin, b), this.yMax = Math.max(this.yMax, b)); - }; - b.prototype.intersects = function(b) { - return this.contains(b._xMin, b._yMin) || this.contains(b._xMax, b._yMax); - }; - b.prototype.isEmpty = function() { + a.prototype.isEmpty = function() { return this._xMax <= this._xMin || this._yMax <= this._yMin; }; - Object.defineProperty(b.prototype, "xMin", {get:function() { + Object.defineProperty(a.prototype, "xMin", {get:function() { return this._xMin; - }, set:function(b) { - k.assert(m(b)); - this._xMin = b; + }, set:function(a) { + k.assert(m(a)); + this._xMin = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "yMin", {get:function() { + Object.defineProperty(a.prototype, "yMin", {get:function() { return this._yMin; - }, set:function(b) { - k.assert(m(b)); - this._yMin = b | 0; + }, set:function(a) { + k.assert(m(a)); + this._yMin = a | 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "xMax", {get:function() { + Object.defineProperty(a.prototype, "xMax", {get:function() { return this._xMax; - }, set:function(b) { - k.assert(m(b)); - this._xMax = b | 0; + }, set:function(a) { + k.assert(m(a)); + this._xMax = a | 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "width", {get:function() { + Object.defineProperty(a.prototype, "width", {get:function() { return this._xMax - this._xMin; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "yMax", {get:function() { + Object.defineProperty(a.prototype, "yMax", {get:function() { return this._yMax; - }, set:function(b) { - k.assert(m(b)); - this._yMax = b | 0; + }, set:function(a) { + k.assert(m(a)); + this._yMax = a | 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "height", {get:function() { + Object.defineProperty(a.prototype, "height", {get:function() { return this._yMax - this._yMin; }, enumerable:!0, configurable:!0}); - b.prototype.getBaseWidth = function(b) { - return Math.abs(Math.cos(b)) * (this._xMax - this._xMin) + Math.abs(Math.sin(b)) * (this._yMax - this._yMin); + a.prototype.getBaseWidth = function(a) { + return Math.abs(Math.cos(a)) * (this._xMax - this._xMin) + Math.abs(Math.sin(a)) * (this._yMax - this._yMin); }; - b.prototype.getBaseHeight = function(b) { - return Math.abs(Math.sin(b)) * (this._xMax - this._xMin) + Math.abs(Math.cos(b)) * (this._yMax - this._yMin); + a.prototype.getBaseHeight = function(a) { + return Math.abs(Math.sin(a)) * (this._xMax - this._xMin) + Math.abs(Math.cos(a)) * (this._yMax - this._yMin); }; - b.prototype.setEmpty = function() { + a.prototype.setEmpty = function() { this._xMin = this._yMin = this._xMax = this._yMax = 0; }; - b.prototype.clone = function() { - return new b(this.xMin, this.yMin, this.xMax, this.yMax); + a.prototype.clone = function() { + return new a(this.xMin, this.yMin, this.xMax, this.yMax); }; - b.prototype.toString = function() { + a.prototype.toString = function() { return "{ xMin: " + this._xMin + ", xMin: " + this._yMin + ", xMax: " + this._xMax + ", yMax: " + this._yMax + " }"; }; - b.prototype.assertValid = function() { + a.prototype.assertValid = function() { }; - return b; + return a; }(); - d.DebugBounds = c; - c = function() { - function b(b, a, n, f) { - this.r = b; - this.g = a; - this.b = n; - this.a = f; + g.DebugBounds = a; + a = function() { + function a(h, f, d, q) { + this.r = h; + this.g = f; + this.b = d; + this.a = q; } - b.FromARGB = function(a) { - return new b((a >> 16 & 255) / 255, (a >> 8 & 255) / 255, (a >> 0 & 255) / 255, (a >> 24 & 255) / 255); + a.FromARGB = function(h) { + return new a((h >> 16 & 255) / 255, (h >> 8 & 255) / 255, (h >> 0 & 255) / 255, (h >> 24 & 255) / 255); }; - b.FromRGBA = function(a) { - return b.FromARGB(l.RGBAToARGB(a)); + a.FromRGBA = function(h) { + return a.FromARGB(t.RGBAToARGB(h)); }; - b.prototype.toRGBA = function() { + a.prototype.toRGBA = function() { return 255 * this.r << 24 | 255 * this.g << 16 | 255 * this.b << 8 | 255 * this.a; }; - b.prototype.toCSSStyle = function() { - return l.rgbaToCSSStyle(this.toRGBA()); + a.prototype.toCSSStyle = function() { + return t.rgbaToCSSStyle(this.toRGBA()); }; - b.prototype.set = function(b) { - this.r = b.r; - this.g = b.g; - this.b = b.b; - this.a = b.a; - }; - b.randomColor = function(a) { - "undefined" === typeof a && (a = 1); - return new b(Math.random(), Math.random(), Math.random(), a); - }; - b.parseColor = function(a) { - b.colorCache || (b.colorCache = Object.create(null)); - if (b.colorCache[a]) { - return b.colorCache[a]; - } - var c = document.createElement("span"); - document.body.appendChild(c); - c.style.backgroundColor = a; - var n = getComputedStyle(c).backgroundColor; - document.body.removeChild(c); - (c = /^rgb\((\d+), (\d+), (\d+)\)$/.exec(n)) || (c = /^rgba\((\d+), (\d+), (\d+), ([\d.]+)\)$/.exec(n)); - n = new b(0, 0, 0, 0); - n.r = parseFloat(c[1]) / 255; - n.g = parseFloat(c[2]) / 255; - n.b = parseFloat(c[3]) / 255; - n.a = c[4] ? parseFloat(c[4]) / 255 : 1; - return b.colorCache[a] = n; - }; - b.Red = new b(1, 0, 0, 1); - b.Green = new b(0, 1, 0, 1); - b.Blue = new b(0, 0, 1, 1); - b.None = new b(0, 0, 0, 0); - b.White = new b(1, 1, 1, 1); - b.Black = new b(0, 0, 0, 1); - b.colorCache = {}; - return b; + a.prototype.set = function(a) { + this.r = a.r; + this.g = a.g; + this.b = a.b; + this.a = a.a; + }; + a.randomColor = function() { + var h = .4; + void 0 === h && (h = 1); + return new a(Math.random(), Math.random(), Math.random(), h); + }; + a.parseColor = function(h) { + a.colorCache || (a.colorCache = Object.create(null)); + if (a.colorCache[h]) { + return a.colorCache[h]; + } + var f = document.createElement("span"); + document.body.appendChild(f); + f.style.backgroundColor = h; + var d = getComputedStyle(f).backgroundColor; + document.body.removeChild(f); + (f = /^rgb\((\d+), (\d+), (\d+)\)$/.exec(d)) || (f = /^rgba\((\d+), (\d+), (\d+), ([\d.]+)\)$/.exec(d)); + d = new a(0, 0, 0, 0); + d.r = parseFloat(f[1]) / 255; + d.g = parseFloat(f[2]) / 255; + d.b = parseFloat(f[3]) / 255; + d.a = f[4] ? parseFloat(f[4]) / 255 : 1; + return a.colorCache[h] = d; + }; + a.Red = new a(1, 0, 0, 1); + a.Green = new a(0, 1, 0, 1); + a.Blue = new a(0, 0, 1, 1); + a.None = new a(0, 0, 0, 0); + a.White = new a(1, 1, 1, 1); + a.Black = new a(0, 0, 0, 1); + a.colorCache = {}; + return a; }(); - d.Color = c; - (function(b) { - function a(f) { - var b, h, n = f >> 24 & 255; - h = (Math.imul(f >> 16 & 255, n) + 127) / 255 | 0; - b = (Math.imul(f >> 8 & 255, n) + 127) / 255 | 0; - f = (Math.imul(f >> 0 & 255, n) + 127) / 255 | 0; - return n << 24 | h << 16 | b << 8 | f; - } - b.RGBAToARGB = function(f) { - return f >> 8 & 16777215 | (f & 255) << 24; - }; - b.ARGBToRGBA = function(f) { - return f << 8 | f >> 24 & 255; - }; - b.rgbaToCSSStyle = function(f) { - return d.StringUtilities.concat9("rgba(", f >> 24 & 255, ",", f >> 16 & 255, ",", f >> 8 & 255, ",", (f & 255) / 255, ")"); - }; - b.cssStyleToRGBA = function(f) { - if ("#" === f[0]) { - if (7 === f.length) { - return parseInt(f.substring(1), 16) << 8 | 255; + g.Color = a; + var t; + (function(a) { + function h(a) { + var d, f, h = a >> 24 & 255; + f = (Math.imul(a >> 16 & 255, h) + 127) / 255 | 0; + d = (Math.imul(a >> 8 & 255, h) + 127) / 255 | 0; + a = (Math.imul(a >> 0 & 255, h) + 127) / 255 | 0; + return h << 24 | f << 16 | d << 8 | a; + } + a.RGBAToARGB = function(a) { + return a >> 8 & 16777215 | (a & 255) << 24; + }; + a.ARGBToRGBA = function(a) { + return a << 8 | a >> 24 & 255; + }; + a.rgbaToCSSStyle = function(a) { + return g.StringUtilities.concat9("rgba(", a >> 24 & 255, ",", a >> 16 & 255, ",", a >> 8 & 255, ",", (a & 255) / 255, ")"); + }; + a.cssStyleToRGBA = function(a) { + if ("#" === a[0]) { + if (7 === a.length) { + return parseInt(a.substring(1), 16) << 8 | 255; } } else { - if ("r" === f[0]) { - return f = f.substring(5, f.length - 1).split(","), (parseInt(f[0]) & 255) << 24 | (parseInt(f[1]) & 255) << 16 | (parseInt(f[2]) & 255) << 8 | 255 * parseFloat(f[3]) & 255; + if ("r" === a[0]) { + return a = a.substring(5, a.length - 1).split(","), (parseInt(a[0]) & 255) << 24 | (parseInt(a[1]) & 255) << 16 | (parseInt(a[2]) & 255) << 8 | 255 * parseFloat(a[3]) & 255; } } return 4278190335; }; - b.hexToRGB = function(f) { - return parseInt(f.slice(1), 16); + a.hexToRGB = function(a) { + return parseInt(a.slice(1), 16); }; - b.rgbToHex = function(f) { - return "#" + ("000000" + f.toString(16)).slice(-6); + a.rgbToHex = function(a) { + return "#" + ("000000" + (a >>> 0).toString(16)).slice(-6); }; - b.isValidHexColor = function(f) { - return/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(f); + a.isValidHexColor = function(a) { + return/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(a); }; - b.clampByte = function(f) { - return Math.max(0, Math.min(255, f)); - }; - b.unpremultiplyARGB = function(f) { - var b, a, h = f >> 24 & 255; - a = Math.imul(255, f >> 16 & 255) / h & 255; - b = Math.imul(255, f >> 8 & 255) / h & 255; - f = Math.imul(255, f >> 0 & 255) / h & 255; - return h << 24 | a << 16 | b << 8 | f; + a.clampByte = function(a) { + return Math.max(0, Math.min(255, a)); }; - b.premultiplyARGB = a; - var c; - b.ensureUnpremultiplyTable = function() { - if (!c) { - c = new Uint8Array(65536); - for (var f = 0;256 > f;f++) { - for (var b = 0;256 > b;b++) { - c[(b << 8) + f] = Math.imul(255, f) / b; + a.unpremultiplyARGB = function(a) { + var d, f, h = a >> 24 & 255; + f = Math.imul(255, a >> 16 & 255) / h & 255; + d = Math.imul(255, a >> 8 & 255) / h & 255; + a = Math.imul(255, a >> 0 & 255) / h & 255; + return h << 24 | f << 16 | d << 8 | a; + }; + a.premultiplyARGB = h; + var f; + a.ensureUnpremultiplyTable = function() { + if (!f) { + f = new Uint8Array(65536); + for (var a = 0;256 > a;a++) { + for (var d = 0;256 > d;d++) { + f[(d << 8) + a] = Math.imul(255, a) / d; } } } }; - b.tableLookupUnpremultiplyARGB = function(f) { - f |= 0; - var b = f >> 24 & 255; - if (0 === b) { + a.tableLookupUnpremultiplyARGB = function(a) { + a |= 0; + var d = a >> 24 & 255; + if (0 === d) { return 0; } - if (255 === b) { - return f; + if (255 === d) { + return a; } - var a, h, n = b << 8, g = c; - h = g[n + (f >> 16 & 255)]; - a = g[n + (f >> 8 & 255)]; - f = g[n + (f >> 0 & 255)]; - return b << 24 | h << 16 | a << 8 | f; - }; - b.blendPremultipliedBGRA = function(f, b) { - var a, h; - h = 256 - (b & 255); - a = Math.imul(f & 16711935, h) >> 8; - h = Math.imul(f >> 8 & 16711935, h) >> 8; - return((b >> 8 & 16711935) + h & 16711935) << 8 | (b & 16711935) + a & 16711935; - }; - var n = v.swap32; - b.convertImage = function(f, b, g, p) { - g !== p && k.assert(g.buffer !== p.buffer, "Can't handle overlapping views."); - var l = g.length; - if (f === b) { - if (g !== p) { - for (f = 0;f < l;f++) { - p[f] = g[f]; + var h, b, n = d << 8, p = f; + b = p[n + (a >> 16 & 255)]; + h = p[n + (a >> 8 & 255)]; + a = p[n + (a >> 0 & 255)]; + return d << 24 | b << 16 | h << 8 | a; + }; + a.blendPremultipliedBGRA = function(a, d) { + var f, h; + h = 256 - (d & 255); + f = Math.imul(a & 16711935, h) >> 8; + h = Math.imul(a >> 8 & 16711935, h) >> 8; + return((d >> 8 & 16711935) + h & 16711935) << 8 | (d & 16711935) + f & 16711935; + }; + var d = v.swap32; + a.convertImage = function(a, b, s, n) { + s !== n && k.assert(s.buffer !== n.buffer, "Can't handle overlapping views."); + var p = s.length; + if (a === b) { + if (s !== n) { + for (a = 0;a < p;a++) { + n[a] = s[a]; } } } else { - if (1 === f && 3 === b) { - for (d.ColorUtilities.ensureUnpremultiplyTable(), f = 0;f < l;f++) { - var v = g[f]; - b = v & 255; + if (1 === a && 3 === b) { + for (g.ColorUtilities.ensureUnpremultiplyTable(), a = 0;a < p;a++) { + var c = s[a]; + b = c & 255; if (0 === b) { - p[f] = 0; + n[a] = 0; } else { if (255 === b) { - p[f] = (v & 255) << 24 | v >> 8 & 16777215; + n[a] = 4278190080 | c >> 8 & 16777215; } else { - var e = v >> 24 & 255, u = v >> 16 & 255, v = v >> 8 & 255, t = b << 8, m = c, v = m[t + v], u = m[t + u], e = m[t + e]; - p[f] = b << 24 | e << 16 | u << 8 | v; + var u = c >> 24 & 255, l = c >> 16 & 255, c = c >> 8 & 255, y = b << 8, t = f, c = t[y + c], l = t[y + l], u = t[y + u]; + n[a] = b << 24 | u << 16 | l << 8 | c; } } } } else { - if (2 === f && 3 === b) { - for (f = 0;f < l;f++) { - p[f] = n(g[f]); + if (2 === a && 3 === b) { + for (a = 0;a < p;a++) { + n[a] = d(s[a]); } } else { - if (3 === f && 1 === b) { - for (f = 0;f < l;f++) { - b = g[f], p[f] = n(a(b & 4278255360 | b >> 16 & 255 | (b & 255) << 16)); + if (3 === a && 1 === b) { + for (a = 0;a < p;a++) { + b = s[a], n[a] = d(h(b & 4278255360 | b >> 16 & 255 | (b & 255) << 16)); } } else { - for (k.somewhatImplemented("Image Format Conversion: " + r[f] + " -> " + r[b]), f = 0;f < l;f++) { - p[f] = g[f]; + for (k.somewhatImplemented("Image Format Conversion: " + r[a] + " -> " + r[b]), a = 0;a < p;a++) { + n[a] = s[a]; } } } } } }; - })(d.ColorUtilities || (d.ColorUtilities = {})); - var l = d.ColorUtilities, c = function() { - function b(b) { - "undefined" === typeof b && (b = 32); + })(t = g.ColorUtilities || (g.ColorUtilities = {})); + a = function() { + function a(h) { + void 0 === h && (h = 32); this._list = []; - this._maxSize = b; + this._maxSize = h; } - b.prototype.acquire = function(a) { - if (b._enabled) { - for (var c = this._list, n = 0;n < c.length;n++) { - var f = c[n]; - if (f.byteLength >= a) { - return c.splice(n, 1), f; + a.prototype.acquire = function(h) { + if (a._enabled) { + for (var f = this._list, d = 0;d < f.length;d++) { + var q = f[d]; + if (q.byteLength >= h) { + return f.splice(d, 1), q; } } } - return new ArrayBuffer(a); + return new ArrayBuffer(h); }; - b.prototype.release = function(h) { - if (b._enabled) { - var c = this._list; - k.assert(0 > a.indexOf(c, h)); - c.length === this._maxSize && c.shift(); - c.push(h); + a.prototype.release = function(h) { + if (a._enabled) { + var f = this._list; + k.assert(0 > b.indexOf(f, h)); + f.length === this._maxSize && f.shift(); + f.push(h); } }; - b.prototype.ensureUint8ArrayLength = function(b, a) { - if (b.length >= a) { - return b; + a.prototype.ensureUint8ArrayLength = function(a, f) { + if (a.length >= f) { + return a; } - var c = Math.max(b.length + a, (3 * b.length >> 1) + 1), c = new Uint8Array(this.acquire(c), 0, c); - c.set(b); - this.release(b.buffer); - return c; + var d = Math.max(a.length + f, (3 * a.length >> 1) + 1), d = new Uint8Array(this.acquire(d), 0, d); + d.set(a); + this.release(a.buffer); + return d; }; - b.prototype.ensureFloat64ArrayLength = function(b, a) { - if (b.length >= a) { - return b; + a.prototype.ensureFloat64ArrayLength = function(a, f) { + if (a.length >= f) { + return a; } - var c = Math.max(b.length + a, (3 * b.length >> 1) + 1), c = new Float64Array(this.acquire(c * Float64Array.BYTES_PER_ELEMENT), 0, c); - c.set(b); - this.release(b.buffer); - return c; + var d = Math.max(a.length + f, (3 * a.length >> 1) + 1), d = new Float64Array(this.acquire(d * Float64Array.BYTES_PER_ELEMENT), 0, d); + d.set(a); + this.release(a.buffer); + return d; }; - b._enabled = !0; - return b; + a._enabled = !0; + return a; }(); - d.ArrayBufferPool = c; - (function(b) { - (function(b) { - b[b.EXTERNAL_INTERFACE_FEATURE = 1] = "EXTERNAL_INTERFACE_FEATURE"; - b[b.CLIPBOARD_FEATURE = 2] = "CLIPBOARD_FEATURE"; - b[b.SHAREDOBJECT_FEATURE = 3] = "SHAREDOBJECT_FEATURE"; - b[b.VIDEO_FEATURE = 4] = "VIDEO_FEATURE"; - b[b.SOUND_FEATURE = 5] = "SOUND_FEATURE"; - b[b.NETCONNECTION_FEATURE = 6] = "NETCONNECTION_FEATURE"; - })(b.Feature || (b.Feature = {})); - (function(b) { - b[b.AVM1_ERROR = 1] = "AVM1_ERROR"; - b[b.AVM2_ERROR = 2] = "AVM2_ERROR"; - })(b.ErrorTypes || (b.ErrorTypes = {})); - b.instance; - })(d.Telemetry || (d.Telemetry = {})); - (function(b) { - b.instance; - })(d.FileLoadingService || (d.FileLoadingService = {})); - (function(b) { - b.instance = {enabled:!1, initJS:function(b) { - }, registerCallback:function(b) { - }, unregisterCallback:function(b) { - }, eval:function(b) { - }, call:function(b) { + g.ArrayBufferPool = a; + (function(a) { + (function(a) { + a[a.EXTERNAL_INTERFACE_FEATURE = 1] = "EXTERNAL_INTERFACE_FEATURE"; + a[a.CLIPBOARD_FEATURE = 2] = "CLIPBOARD_FEATURE"; + a[a.SHAREDOBJECT_FEATURE = 3] = "SHAREDOBJECT_FEATURE"; + a[a.VIDEO_FEATURE = 4] = "VIDEO_FEATURE"; + a[a.SOUND_FEATURE = 5] = "SOUND_FEATURE"; + a[a.NETCONNECTION_FEATURE = 6] = "NETCONNECTION_FEATURE"; + })(a.Feature || (a.Feature = {})); + (function(a) { + a[a.AVM1_ERROR = 1] = "AVM1_ERROR"; + a[a.AVM2_ERROR = 2] = "AVM2_ERROR"; + })(a.ErrorTypes || (a.ErrorTypes = {})); + a.instance; + })(g.Telemetry || (g.Telemetry = {})); + (function(a) { + a.instance; + })(g.FileLoadingService || (g.FileLoadingService = {})); + g.registerCSSFont = function(a, b, f) { + if (inBrowser) { + var d = document.head; + d.insertBefore(document.createElement("style"), d.firstChild); + d = document.styleSheets[0]; + b = "@font-face{font-family:swffont" + a + ";src:url(data:font/opentype;base64," + g.StringUtilities.base64ArrayBuffer(b) + ")}"; + d.insertRule(b, d.cssRules.length); + f && (f = document.createElement("div"), f.style.fontFamily = "swffont" + a, f.innerHTML = "hello", document.body.appendChild(f), document.body.removeChild(f)); + } else { + k.warning("Cannot register CSS font outside the browser"); + } + }; + (function(a) { + a.instance = {enabled:!1, initJS:function(a) { + }, registerCallback:function(a) { + }, unregisterCallback:function(a) { + }, eval:function(a) { + }, call:function(a) { }, getId:function() { return null; }}; - })(d.ExternalInterfaceService || (d.ExternalInterfaceService = {})); - c = function() { - function b() { + })(g.ExternalInterfaceService || (g.ExternalInterfaceService = {})); + a = function() { + function a() { } - b.prototype.setClipboard = function(b) { + a.prototype.setClipboard = function(a) { k.abstractMethod("public ClipboardService::setClipboard"); }; - b.instance = null; - return b; + a.instance = null; + return a; }(); - d.ClipboardService = c; - c = function() { - function b() { + g.ClipboardService = a; + a = function() { + function a() { this._queues = {}; } - b.prototype.register = function(b, a) { - k.assert(b); + a.prototype.register = function(a, f) { k.assert(a); - var c = this._queues[b]; - if (c) { - if (-1 < c.indexOf(a)) { + k.assert(f); + var d = this._queues[a]; + if (d) { + if (-1 < d.indexOf(f)) { return; } } else { - c = this._queues[b] = []; + d = this._queues[a] = []; } - c.push(a); + d.push(f); }; - b.prototype.unregister = function(b, a) { - k.assert(b); + a.prototype.unregister = function(a, f) { k.assert(a); - var c = this._queues[b]; - if (c) { - var f = c.indexOf(a); - -1 !== f && c.splice(f, 1); - 0 === c.length && (this._queues[b] = null); - } - }; - b.prototype.notify = function(b, a) { - var c = this._queues[b]; - if (c) { - c = c.slice(); - a = Array.prototype.slice.call(arguments, 0); - for (var f = 0;f < c.length;f++) { - c[f].apply(null, a); + k.assert(f); + var d = this._queues[a]; + if (d) { + var q = d.indexOf(f); + -1 !== q && d.splice(q, 1); + 0 === d.length && (this._queues[a] = null); + } + }; + a.prototype.notify = function(a, f) { + var d = this._queues[a]; + if (d) { + d = d.slice(); + f = Array.prototype.slice.call(arguments, 0); + for (var q = 0;q < d.length;q++) { + d[q].apply(null, f); } } }; - b.prototype.notify1 = function(b, a) { - var c = this._queues[b]; - if (c) { - for (var c = c.slice(), f = 0;f < c.length;f++) { - (0,c[f])(b, a); + a.prototype.notify1 = function(a, f) { + var d = this._queues[a]; + if (d) { + for (var d = d.slice(), q = 0;q < d.length;q++) { + (0,d[q])(a, f); } } }; - return b; + return a; }(); - d.Callback = c; - (function(b) { - b[b.None = 0] = "None"; - b[b.PremultipliedAlphaARGB = 1] = "PremultipliedAlphaARGB"; - b[b.StraightAlphaARGB = 2] = "StraightAlphaARGB"; - b[b.StraightAlphaRGBA = 3] = "StraightAlphaRGBA"; - b[b.JPEG = 4] = "JPEG"; - b[b.PNG = 5] = "PNG"; - b[b.GIF = 6] = "GIF"; - })(d.ImageType || (d.ImageType = {})); - var r = d.ImageType; - d.PromiseWrapper = function() { - return function() { - this.promise = new Promise(function(b, a) { - this.resolve = b; - this.reject = a; + g.Callback = a; + (function(a) { + a[a.None = 0] = "None"; + a[a.PremultipliedAlphaARGB = 1] = "PremultipliedAlphaARGB"; + a[a.StraightAlphaARGB = 2] = "StraightAlphaARGB"; + a[a.StraightAlphaRGBA = 3] = "StraightAlphaRGBA"; + a[a.JPEG = 4] = "JPEG"; + a[a.PNG = 5] = "PNG"; + a[a.GIF = 6] = "GIF"; + })(g.ImageType || (g.ImageType = {})); + var r = g.ImageType; + g.getMIMETypeForImageType = function(a) { + switch(a) { + case 4: + return "image/jpeg"; + case 5: + return "image/png"; + case 6: + return "image/gif"; + default: + return "text/plain"; + } + }; + (function(a) { + a.toCSSCursor = function(a) { + switch(a) { + case 0: + return "auto"; + case 2: + return "pointer"; + case 3: + return "grab"; + case 4: + return "text"; + default: + return "default"; + } + }; + })(g.UI || (g.UI = {})); + a = function() { + function a() { + this.promise = new Promise(function(a, f) { + this.resolve = a; + this.reject = f; }.bind(this)); + } + a.prototype.then = function(a, f) { + return this.promise.then(a, f); }; + return a; }(); + g.PromiseWrapper = a; })(Shumway || (Shumway = {})); (function() { - function d(b) { - if ("function" !== typeof b) { + function g(a) { + if ("function" !== typeof a) { throw new TypeError("Invalid deferred constructor"); } - var f = u(); - b = new b(f); - var a = f.resolve; - if ("function" !== typeof a) { + var f = l(); + a = new a(f); + var b = f.resolve; + if ("function" !== typeof b) { throw new TypeError("Invalid resolve construction function"); } f = f.reject; if ("function" !== typeof f) { throw new TypeError("Invalid reject construction function"); } - return{promise:b, resolve:a, reject:f}; + return{promise:a, resolve:b, reject:f}; } - function m(b, f) { - if ("object" !== typeof b || null === b) { + function m(a, f) { + if ("object" !== typeof a || null === a) { return!1; } try { - var a = b.then; - if ("function" !== typeof a) { + var b = a.then; + if ("function" !== typeof b) { return!1; } - a.call(b, f.resolve, f.reject); - } catch (c) { - a = f.reject, a(c); + b.call(a, f.resolve, f.reject); + } catch (h) { + b = f.reject, b(h); } return!0; } - function s(b) { - return "object" === typeof b && null !== b && "undefined" !== typeof b.promiseStatus; + function e(a) { + return "object" === typeof a && null !== a && "undefined" !== typeof a.promiseStatus; } - function e(b, f) { - if ("unresolved" === b.promiseStatus) { - var a = b.rejectReactions; - b.result = f; - b.resolveReactions = void 0; - b.rejectReactions = void 0; - b.promiseStatus = "has-rejection"; - t(a, f); + function c(a, f) { + if ("unresolved" === a.promiseStatus) { + var b = a.rejectReactions; + a.result = f; + a.resolveReactions = void 0; + a.rejectReactions = void 0; + a.promiseStatus = "has-rejection"; + w(b, f); } } - function t(b, f) { - for (var a = 0;a < b.length;a++) { - k({reaction:b[a], argument:f}); + function w(a, f) { + for (var b = 0;b < a.length;b++) { + k({reaction:a[b], argument:f}); } } - function k(b) { - 0 === w.length && setTimeout(a, 0); - w.push(b); + function k(d) { + 0 === f.length && setTimeout(a, 0); + f.push(d); + } + function b(a, f) { + var b = a.deferred, h = a.handler, n, p; + try { + n = h(f); + } catch (c) { + return b = b.reject, b(c); + } + if (n === b.promise) { + return b = b.reject, b(new TypeError("Self resolution")); + } + try { + if (p = m(n, b), !p) { + var r = b.resolve; + return r(n); + } + } catch (k) { + return b = b.reject, b(k); + } } function a() { - for (;0 < w.length;) { - var a = w[0]; + for (;0 < f.length;) { + var a = f[0]; try { - a: { - var f = a.reaction, c = f.deferred, h = f.handler, g = void 0, p = void 0; - try { - g = h(a.argument); - } catch (r) { - var l = c.reject; - l(r); - break a; - } - if (g === c.promise) { - l = c.reject, l(new TypeError("Self resolution")); - } else { - try { - if (p = m(g, c), !p) { - var k = c.resolve; - k(g); - } - } catch (v) { - l = c.reject, l(v); - } - } - } - } catch (e) { - if ("function" === typeof b.onerror) { - b.onerror(e); + b(a.reaction, a.argument); + } catch (q) { + if ("function" === typeof u.onerror) { + u.onerror(q); } } - w.shift(); + f.shift(); } } - function c(b) { - throw b; + function n(a) { + throw a; } - function g(b) { - return b; + function p(a) { + return a; } - function p(b) { + function y(a) { return function(f) { - e(b, f); + c(a, f); }; } - function v(b) { + function v(a) { return function(f) { - if ("unresolved" === b.promiseStatus) { - var a = b.resolveReactions; - b.result = f; - b.resolveReactions = void 0; - b.rejectReactions = void 0; - b.promiseStatus = "has-resolution"; - t(a, f); + if ("unresolved" === a.promiseStatus) { + var b = a.resolveReactions; + a.result = f; + a.resolveReactions = void 0; + a.rejectReactions = void 0; + a.promiseStatus = "has-resolution"; + w(b, f); } }; } - function u() { - function b(f, a) { - b.resolve = f; - b.reject = a; + function l() { + function a(f, b) { + a.resolve = f; + a.reject = b; } - return b; + return a; } - function l(b, f, a) { - return function(c) { - if (c === b) { - return a(new TypeError("Self resolution")); - } - var h = b.promiseConstructor; - if (s(c) && c.promiseConstructor === h) { - return c.then(f, a); + function t(a, f, b) { + return function(h) { + if (h === a) { + return b(new TypeError("Self resolution")); } - h = d(h); - return m(c, h) ? h.promise.then(f, a) : f(c); + var n = a.promiseConstructor; + if (e(h) && h.promiseConstructor === n) { + return h.then(f, b); + } + n = g(n); + return m(h, n) ? n.promise.then(f, b) : f(h); }; } - function r(b, f, a, c) { - return function(h) { - f[b] = h; - c.countdown--; - 0 === c.countdown && a.resolve(f); + function r(a, f, b, h) { + return function(n) { + f[a] = n; + h.countdown--; + 0 === h.countdown && b.resolve(f); }; } - function b(a) { + function u(a) { if ("function" !== typeof a) { throw new TypeError("resolver is not a function"); } @@ -2220,276 +2296,276 @@ this.resolveReactions = []; this.rejectReactions = []; this.result = void 0; - var f = v(this), c = p(this); + var f = v(this), b = y(this); try { - a(f, c); + a(f, b); } catch (h) { - e(this, h); + c(this, h); } - this.promiseConstructor = b; + this.promiseConstructor = u; return this; } var h = Function("return this")(); if (h.Promise) { - "function" !== typeof h.Promise.all && (h.Promise.all = function(b) { - var f = 0, a = [], c, g, p = new h.Promise(function(b, f) { - c = b; - g = f; + "function" !== typeof h.Promise.all && (h.Promise.all = function(a) { + var f = 0, b = [], s, n, p = new h.Promise(function(a, d) { + s = a; + n = d; }); - b.forEach(function(b, h) { + a.forEach(function(a, d) { f++; - b.then(function(b) { - a[h] = b; + a.then(function(a) { + b[d] = a; f--; - 0 === f && c(a); - }, g); + 0 === f && s(b); + }, n); }); - 0 === f && c(a); + 0 === f && s(b); return p; - }), "function" !== typeof h.Promise.resolve && (h.Promise.resolve = function(b) { + }), "function" !== typeof h.Promise.resolve && (h.Promise.resolve = function(a) { return new h.Promise(function(f) { - f(b); + f(a); }); }); } else { - var w = []; - b.all = function(b) { - var f = d(this), a = [], c = {countdown:0}, h = 0; - b.forEach(function(b) { - this.cast(b).then(r(h, a, f, c), f.reject); - h++; - c.countdown++; + var f = []; + u.all = function(a) { + var f = g(this), b = [], h = {countdown:0}, n = 0; + a.forEach(function(a) { + this.cast(a).then(r(n, b, f, h), f.reject); + n++; + h.countdown++; }, this); - 0 === h && f.resolve(a); + 0 === n && f.resolve(b); return f.promise; }; - b.cast = function(b) { - if (s(b)) { - return b; + u.cast = function(a) { + if (e(a)) { + return a; } - var f = d(this); - f.resolve(b); + var f = g(this); + f.resolve(a); return f.promise; }; - b.reject = function(b) { - var f = d(this); - f.reject(b); + u.reject = function(a) { + var f = g(this); + f.reject(a); return f.promise; }; - b.resolve = function(b) { - var f = d(this); - f.resolve(b); + u.resolve = function(a) { + var f = g(this); + f.resolve(a); return f.promise; }; - b.prototype = {"catch":function(b) { - this.then(void 0, b); - }, then:function(b, f) { - if (!s(this)) { + u.prototype = {"catch":function(a) { + this.then(void 0, a); + }, then:function(a, f) { + if (!e(this)) { throw new TypeError("this is not a Promises"); } - var a = d(this.promiseConstructor), h = "function" === typeof f ? f : c, p = {deferred:a, handler:l(this, "function" === typeof b ? b : g, h)}, h = {deferred:a, handler:h}; + var b = g(this.promiseConstructor), h = "function" === typeof f ? f : n, c = {deferred:b, handler:t(this, "function" === typeof a ? a : p, h)}, h = {deferred:b, handler:h}; switch(this.promiseStatus) { case "unresolved": - this.resolveReactions.push(p); + this.resolveReactions.push(c); this.rejectReactions.push(h); break; case "has-resolution": - k({reaction:p, argument:this.result}); + k({reaction:c, argument:this.result}); break; case "has-rejection": k({reaction:h, argument:this.result}); } - return a.promise; + return b.promise; }}; - h.Promise = b; + h.Promise = u; } })(); "undefined" !== typeof exports && (exports.Shumway = Shumway); (function() { - function d(d, s, e) { - d[s] || Object.defineProperty(d, s, {value:e, writable:!0, configurable:!0, enumerable:!1}); + function g(g, e, c) { + g[e] || Object.defineProperty(g, e, {value:c, writable:!0, configurable:!0, enumerable:!1}); } - d(String.prototype, "padRight", function(d, s) { - var e = this, t = e.replace(/\033\[[0-9]*m/g, "").length; - if (!d || t >= s) { - return e; + g(String.prototype, "padRight", function(g, e) { + var c = this, w = c.replace(/\033\[[0-9]*m/g, "").length; + if (!g || w >= e) { + return c; } - for (var t = (s - t) / d.length, k = 0;k < t;k++) { - e += d; + for (var w = (e - w) / g.length, k = 0;k < w;k++) { + c += g; } - return e; + return c; }); - d(String.prototype, "padLeft", function(d, s) { - var e = this, t = e.length; - if (!d || t >= s) { - return e; + g(String.prototype, "padLeft", function(g, e) { + var c = this, w = c.length; + if (!g || w >= e) { + return c; } - for (var t = (s - t) / d.length, k = 0;k < t;k++) { - e = d + e; + for (var w = (e - w) / g.length, k = 0;k < w;k++) { + c = g + c; } - return e; + return c; }); - d(String.prototype, "trim", function() { + g(String.prototype, "trim", function() { return this.replace(/^\s+|\s+$/g, ""); }); - d(String.prototype, "endsWith", function(d) { - return-1 !== this.indexOf(d, this.length - d.length); + g(String.prototype, "endsWith", function(g) { + return-1 !== this.indexOf(g, this.length - g.length); }); - d(Array.prototype, "replace", function(d, s) { - if (d === s) { + g(Array.prototype, "replace", function(g, e) { + if (g === e) { return 0; } - for (var e = 0, t = 0;t < this.length;t++) { - this[t] === d && (this[t] = s, e++); + for (var c = 0, w = 0;w < this.length;w++) { + this[w] === g && (this[w] = e, c++); } - return e; + return c; }); })(); -(function(d) { +(function(g) { (function(m) { - var s = d.isObject, e = d.Debug.assert, t = function() { - function a(c, g, k, l) { - this.shortName = c; - this.longName = g; - this.type = k; - l = l || {}; - this.positional = l.positional; - this.parseFn = l.parse; - this.value = l.defaultValue; + var e = g.isObject, c = g.Debug.assert, w = function() { + function a(b, n, c, k) { + this.shortName = b; + this.longName = n; + this.type = c; + k = k || {}; + this.positional = k.positional; + this.parseFn = k.parse; + this.value = k.defaultValue; } a.prototype.parse = function(a) { - "boolean" === this.type ? (e("boolean" === typeof a), this.value = a) : "number" === this.type ? (e(!isNaN(a), a + " is not a number"), this.value = parseInt(a, 10)) : this.value = a; + "boolean" === this.type ? (c("boolean" === typeof a), this.value = a) : "number" === this.type ? (c(!isNaN(a), a + " is not a number"), this.value = parseInt(a, 10)) : this.value = a; this.parseFn && this.parseFn(this.value); }; return a; }(); - m.Argument = t; + m.Argument = w; var k = function() { - function g() { + function n() { this.args = []; } - g.prototype.addArgument = function(a, c, g, l) { - a = new t(a, c, g, l); + n.prototype.addArgument = function(a, b, n, c) { + a = new w(a, b, n, c); this.args.push(a); return a; }; - g.prototype.addBoundOption = function(a) { - this.args.push(new t(a.shortName, a.longName, a.type, {parse:function(c) { - a.value = c; + n.prototype.addBoundOption = function(a) { + this.args.push(new w(a.shortName, a.longName, a.type, {parse:function(b) { + a.value = b; }})); }; - g.prototype.addBoundOptionSet = function(g) { + n.prototype.addBoundOptionSet = function(n) { var k = this; - g.options.forEach(function(g) { - g instanceof a ? k.addBoundOptionSet(g) : (e(g instanceof c), k.addBoundOption(g)); + n.options.forEach(function(n) { + n instanceof b ? k.addBoundOptionSet(n) : (c(n instanceof a), k.addBoundOption(n)); }); }; - g.prototype.getUsage = function() { + n.prototype.getUsage = function() { var a = ""; - this.args.forEach(function(c) { - a = c.positional ? a + c.longName : a + ("[-" + c.shortName + "|--" + c.longName + ("boolean" === c.type ? "" : " " + c.type[0].toUpperCase()) + "]"); + this.args.forEach(function(b) { + a = b.positional ? a + b.longName : a + ("[-" + b.shortName + "|--" + b.longName + ("boolean" === b.type ? "" : " " + b.type[0].toUpperCase()) + "]"); a += " "; }); return a; }; - g.prototype.parse = function(a) { - var c = {}, g = []; - this.args.forEach(function(b) { - b.positional ? g.push(b) : (c["-" + b.shortName] = b, c["--" + b.longName] = b); + n.prototype.parse = function(a) { + var b = {}, n = []; + this.args.forEach(function(a) { + a.positional ? n.push(a) : (b["-" + a.shortName] = a, b["--" + a.longName] = a); }); - for (var l = [];a.length;) { - var r = a.shift(), b = null, h = r; - if ("--" == r) { - l = l.concat(a); + for (var k = [];a.length;) { + var t = a.shift(), r = null, u = t; + if ("--" == t) { + k = k.concat(a); break; } else { - if ("-" == r.slice(0, 1) || "--" == r.slice(0, 2)) { - b = c[r]; - if (!b) { + if ("-" == t.slice(0, 1) || "--" == t.slice(0, 2)) { + r = b[t]; + if (!r) { continue; } - "boolean" !== b.type ? (h = a.shift(), e("-" !== h && "--" !== h, "Argument " + r + " must have a value.")) : h = !0; + "boolean" !== r.type ? (u = a.shift(), c("-" !== u && "--" !== u, "Argument " + t + " must have a value.")) : u = !0; } else { - g.length ? b = g.shift() : l.push(h); + n.length ? r = n.shift() : k.push(u); } } - b && b.parse(h); + r && r.parse(u); } - e(0 === g.length, "Missing positional arguments."); - return l; + c(0 === n.length, "Missing positional arguments."); + return k; }; - return g; + return n; }(); m.ArgumentParser = k; - var a = function() { - function a(c, g) { - "undefined" === typeof g && (g = null); + var b = function() { + function a(b, n) { + void 0 === n && (n = null); this.open = !1; - this.name = c; - this.settings = g || {}; + this.name = b; + this.settings = n || {}; this.options = []; } - a.prototype.register = function(c) { - if (c instanceof a) { - for (var k = 0;k < this.options.length;k++) { - var e = this.options[k]; - if (e instanceof a && e.name === c.name) { - return e; + a.prototype.register = function(b) { + if (b instanceof a) { + for (var c = 0;c < this.options.length;c++) { + var k = this.options[c]; + if (k instanceof a && k.name === b.name) { + return k; } } } - this.options.push(c); + this.options.push(b); if (this.settings) { - if (c instanceof a) { - k = this.settings[c.name], s(k) && (c.settings = k.settings, c.open = k.open); + if (b instanceof a) { + c = this.settings[b.name], e(c) && (b.settings = c.settings, b.open = c.open); } else { - if ("undefined" !== typeof this.settings[c.longName]) { - switch(c.type) { + if ("undefined" !== typeof this.settings[b.longName]) { + switch(b.type) { case "boolean": - c.value = !!this.settings[c.longName]; + b.value = !!this.settings[b.longName]; break; case "number": - c.value = +this.settings[c.longName]; + b.value = +this.settings[b.longName]; break; default: - c.value = this.settings[c.longName]; + b.value = this.settings[b.longName]; } } } } - return c; + return b; }; a.prototype.trace = function(a) { a.enter(this.name + " {"); - this.options.forEach(function(c) { - c.trace(a); + this.options.forEach(function(b) { + b.trace(a); }); a.leave("}"); }; a.prototype.getSettings = function() { - var c = {}; - this.options.forEach(function(k) { - k instanceof a ? c[k.name] = {settings:k.getSettings(), open:k.open} : c[k.longName] = k.value; + var b = {}; + this.options.forEach(function(c) { + c instanceof a ? b[c.name] = {settings:c.getSettings(), open:c.open} : b[c.longName] = c.value; }); - return c; + return b; }; - a.prototype.setSettings = function(c) { - c && this.options.forEach(function(k) { - k instanceof a ? k.name in c && k.setSettings(c[k.name].settings) : k.longName in c && (k.value = c[k.longName]); + a.prototype.setSettings = function(b) { + b && this.options.forEach(function(c) { + c instanceof a ? c.name in b && c.setSettings(b[c.name].settings) : c.longName in b && (c.value = b[c.longName]); }); }; return a; }(); - m.OptionSet = a; - var c = function() { - function a(c, g, k, l, r, b) { - "undefined" === typeof b && (b = null); - this.longName = g; - this.shortName = c; - this.type = k; - this.value = this.defaultValue = l; - this.description = r; - this.config = b; + m.OptionSet = b; + var a = function() { + function a(b, n, c, k, t, r) { + void 0 === r && (r = null); + this.longName = n; + this.shortName = b; + this.type = c; + this.value = this.defaultValue = k; + this.description = t; + this.config = r; } a.prototype.parse = function(a) { this.value = a; @@ -2499,298 +2575,348 @@ }; return a; }(); - m.Option = c; - })(d.Options || (d.Options = {})); + m.Option = a; + })(g.Options || (g.Options = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - function s() { + function e() { try { return "undefined" !== typeof window && "localStorage" in window && null !== window.localStorage; - } catch (e) { + } catch (c) { return!1; } } - function e(e) { - "undefined" === typeof e && (e = m.ROOT); + function c(c) { + void 0 === c && (c = m.ROOT); var k = {}; - if (s() && (e = window.localStorage[e])) { + if (e() && (c = window.localStorage[c])) { try { - k = JSON.parse(e); - } catch (a) { + k = JSON.parse(c); + } catch (b) { } } return k; } m.ROOT = "Shumway Options"; - m.shumwayOptions = new d.Options.OptionSet(m.ROOT, e()); - m.isStorageSupported = s; - m.load = e; - m.save = function(e, k) { - "undefined" === typeof e && (e = null); - "undefined" === typeof k && (k = m.ROOT); - if (s()) { + m.shumwayOptions = new g.Options.OptionSet(m.ROOT, c()); + m.isStorageSupported = e; + m.load = c; + m.save = function(c, k) { + void 0 === c && (c = null); + void 0 === k && (k = m.ROOT); + if (e()) { try { - window.localStorage[k] = JSON.stringify(e ? e : m.shumwayOptions.getSettings()); - } catch (a) { + window.localStorage[k] = JSON.stringify(c ? c : m.shumwayOptions.getSettings()); + } catch (b) { } } }; - m.setSettings = function(e) { - m.shumwayOptions.setSettings(e); + m.setSettings = function(c) { + m.shumwayOptions.setSettings(c); }; - m.getSettings = function(e) { + m.getSettings = function(c) { return m.shumwayOptions.getSettings(); }; - })(d.Settings || (d.Settings = {})); + })(g.Settings || (g.Settings = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - var s = function() { - function e(e, k) { - this._parent = e; - this._timers = d.ObjectUtilities.createMap(); + var e = function() { + function c(c, k) { + this._parent = c; + this._timers = g.ObjectUtilities.createMap(); this._name = k; this._count = this._total = this._last = this._begin = 0; } - e.time = function(d, k) { - e.start(d); + c.time = function(e, k) { + c.start(e); k(); - e.stop(); + c.stop(); }; - e.start = function(d) { - e._top = e._top._timers[d] || (e._top._timers[d] = new e(e._top, d)); - e._top.start(); - d = e._flat._timers[d] || (e._flat._timers[d] = new e(e._flat, d)); - d.start(); - e._flatStack.push(d); - }; - e.stop = function() { - e._top.stop(); - e._top = e._top._parent; - e._flatStack.pop().stop(); - }; - e.stopStart = function(d) { - e.stop(); - e.start(d); + c.start = function(e) { + c._top = c._top._timers[e] || (c._top._timers[e] = new c(c._top, e)); + c._top.start(); + e = c._flat._timers[e] || (c._flat._timers[e] = new c(c._flat, e)); + e.start(); + c._flatStack.push(e); + }; + c.stop = function() { + c._top.stop(); + c._top = c._top._parent; + c._flatStack.pop().stop(); + }; + c.stopStart = function(e) { + c.stop(); + c.start(e); }; - e.prototype.start = function() { - this._begin = d.getTicks(); + c.prototype.start = function() { + this._begin = g.getTicks(); }; - e.prototype.stop = function() { - this._last = d.getTicks() - this._begin; + c.prototype.stop = function() { + this._last = g.getTicks() - this._begin; this._total += this._last; this._count += 1; }; - e.prototype.toJSON = function() { + c.prototype.toJSON = function() { return{name:this._name, total:this._total, timers:this._timers}; }; - e.prototype.trace = function(e) { - e.enter(this._name + ": " + this._total.toFixed(2) + " ms, count: " + this._count + ", average: " + (this._total / this._count).toFixed(2) + " ms"); + c.prototype.trace = function(c) { + c.enter(this._name + ": " + this._total.toFixed(2) + " ms, count: " + this._count + ", average: " + (this._total / this._count).toFixed(2) + " ms"); for (var k in this._timers) { - this._timers[k].trace(e); + this._timers[k].trace(c); } - e.outdent(); + c.outdent(); }; - e.trace = function(d) { - e._base.trace(d); - e._flat.trace(d); - }; - e._base = new e(null, "Total"); - e._top = e._base; - e._flat = new e(null, "Flat"); - e._flatStack = []; - return e; + c.trace = function(e) { + c._base.trace(e); + c._flat.trace(e); + }; + c._base = new c(null, "Total"); + c._top = c._base; + c._flat = new c(null, "Flat"); + c._flatStack = []; + return c; }(); - m.Timer = s; - s = function() { - function e(e) { - this._enabled = e; + m.Timer = e; + e = function() { + function c(c) { + this._enabled = c; this.clear(); } - Object.defineProperty(e.prototype, "counts", {get:function() { + Object.defineProperty(c.prototype, "counts", {get:function() { return this._counts; }, enumerable:!0, configurable:!0}); - e.prototype.setEnabled = function(e) { - this._enabled = e; + c.prototype.setEnabled = function(c) { + this._enabled = c; }; - e.prototype.clear = function() { - this._counts = d.ObjectUtilities.createMap(); - this._times = d.ObjectUtilities.createMap(); + c.prototype.clear = function() { + this._counts = g.ObjectUtilities.createMap(); + this._times = g.ObjectUtilities.createMap(); }; - e.prototype.toJSON = function() { + c.prototype.toJSON = function() { return{counts:this._counts, times:this._times}; }; - e.prototype.count = function(e, k, a) { - "undefined" === typeof k && (k = 1); - "undefined" === typeof a && (a = 0); + c.prototype.count = function(c, k, b) { + void 0 === k && (k = 1); + void 0 === b && (b = 0); if (this._enabled) { - return void 0 === this._counts[e] && (this._counts[e] = 0, this._times[e] = 0), this._counts[e] += k, this._times[e] += a, this._counts[e]; + return void 0 === this._counts[c] && (this._counts[c] = 0, this._times[c] = 0), this._counts[c] += k, this._times[c] += b, this._counts[c]; } }; - e.prototype.trace = function(e) { + c.prototype.trace = function(c) { for (var k in this._counts) { - e.writeLn(k + ": " + this._counts[k]); + c.writeLn(k + ": " + this._counts[k]); } }; - e.prototype._pairToString = function(e, k) { - var a = k[0], c = k[1], g = e[a], a = a + ": " + c; - g && (a += ", " + g.toFixed(4), 1 < c && (a += " (" + (g / c).toFixed(4) + ")")); - return a; + c.prototype._pairToString = function(c, k) { + var b = k[0], a = k[1], n = c[b], b = b + ": " + a; + n && (b += ", " + n.toFixed(4), 1 < a && (b += " (" + (n / a).toFixed(4) + ")")); + return b; }; - e.prototype.toStringSorted = function() { - var e = this, k = this._times, a = [], c; - for (c in this._counts) { - a.push([c, this._counts[c]]); + c.prototype.toStringSorted = function() { + var c = this, k = this._times, b = [], a; + for (a in this._counts) { + b.push([a, this._counts[a]]); } - a.sort(function(a, c) { - return c[1] - a[1]; + b.sort(function(a, b) { + return b[1] - a[1]; }); - return a.map(function(a) { - return e._pairToString(k, a); + return b.map(function(a) { + return c._pairToString(k, a); }).join(", "); }; - e.prototype.traceSorted = function(e, k) { - "undefined" === typeof k && (k = !1); - var a = this, c = this._times, g = [], p; + c.prototype.traceSorted = function(c, k) { + void 0 === k && (k = !1); + var b = this, a = this._times, n = [], p; for (p in this._counts) { - g.push([p, this._counts[p]]); + n.push([p, this._counts[p]]); } - g.sort(function(a, c) { - return c[1] - a[1]; + n.sort(function(a, b) { + return b[1] - a[1]; }); - k ? e.writeLn(g.map(function(g) { - return a._pairToString(c, g); - }).join(", ")) : g.forEach(function(g) { - e.writeLn(a._pairToString(c, g)); + k ? c.writeLn(n.map(function(n) { + return b._pairToString(a, n); + }).join(", ")) : n.forEach(function(n) { + c.writeLn(b._pairToString(a, n)); }); }; - e.instance = new e(!0); - return e; + c.instance = new c(!0); + return c; }(); - m.Counter = s; - s = function() { - function e(e) { - this._samples = new Float64Array(e); + m.Counter = e; + e = function() { + function c(c) { + this._samples = new Float64Array(c); this._index = this._count = 0; } - e.prototype.push = function(e) { + c.prototype.push = function(c) { this._count < this._samples.length && this._count++; this._index++; - this._samples[this._index % this._samples.length] = e; + this._samples[this._index % this._samples.length] = c; }; - e.prototype.average = function() { - for (var e = 0, k = 0;k < this._count;k++) { - e += this._samples[k]; + c.prototype.average = function() { + for (var c = 0, k = 0;k < this._count;k++) { + c += this._samples[k]; } - return e / this._count; + return c / this._count; }; - return e; + return c; }(); - m.Average = s; - })(d.Metrics || (d.Metrics = {})); + m.Average = e; + })(g.Metrics || (g.Metrics = {})); })(Shumway || (Shumway = {})); -(function(d) { - (function(d) { - function s(b) { - for (var a = Math.max.apply(null, b), c = b.length, g = 1 << a, f = new Uint32Array(g), q = a << 16 | 65535, p = 0;p < g;p++) { - f[p] = q; +var __extends = this.__extends || function(g, m) { + function e() { + this.constructor = g; + } + for (var c in m) { + m.hasOwnProperty(c) && (g[c] = m[c]); + } + e.prototype = m.prototype; + g.prototype = new e; +}; +(function(g) { + (function(m) { + function e(a) { + for (var f = Math.max.apply(null, a), d = a.length, q = 1 << f, b = new Uint32Array(q), s = f << 16 | 65535, n = 0;n < q;n++) { + b[n] = s; } - for (var q = 0, p = 1, r = 2;p <= a;q <<= 1, ++p, r <<= 1) { - for (var l = 0;l < c;++l) { - if (b[l] === p) { - for (var k = 0, e = 0;e < p;++e) { - k = 2 * k + (q >> e & 1); + for (var s = 0, n = 1, c = 2;n <= f;s <<= 1, ++n, c <<= 1) { + for (var p = 0;p < d;++p) { + if (a[p] === n) { + for (var r = 0, k = 0;k < n;++k) { + r = 2 * r + (s >> k & 1); } - for (e = k;e < g;e += r) { - f[e] = p << 16 | l; + for (k = r;k < q;k += c) { + b[k] = n << 16 | p; } - ++q; + ++s; } } } - return{codes:f, maxBits:a}; + return{codes:b, maxBits:f}; } - var e; - (function(b) { - b[b.INIT = 0] = "INIT"; - b[b.BLOCK_0 = 1] = "BLOCK_0"; - b[b.BLOCK_1 = 2] = "BLOCK_1"; - b[b.BLOCK_2_PRE = 3] = "BLOCK_2_PRE"; - b[b.BLOCK_2 = 4] = "BLOCK_2"; - b[b.DONE = 5] = "DONE"; - b[b.ERROR = 6] = "ERROR"; - b[b.VERIFY_HEADER = 7] = "VERIFY_HEADER"; - })(e || (e = {})); - e = function() { - function b(b) { - this._buffer = new Uint8Array(1024); + var c; + (function(a) { + a[a.INIT = 0] = "INIT"; + a[a.BLOCK_0 = 1] = "BLOCK_0"; + a[a.BLOCK_1 = 2] = "BLOCK_1"; + a[a.BLOCK_2_PRE = 3] = "BLOCK_2_PRE"; + a[a.BLOCK_2 = 4] = "BLOCK_2"; + a[a.DONE = 5] = "DONE"; + a[a.ERROR = 6] = "ERROR"; + a[a.VERIFY_HEADER = 7] = "VERIFY_HEADER"; + })(c || (c = {})); + c = function() { + function a(f) { + this._error = null; + } + Object.defineProperty(a.prototype, "error", {get:function() { + return this._error; + }, enumerable:!0, configurable:!0}); + a.prototype.push = function(a) { + g.Debug.abstractMethod("Inflate.push"); + }; + a.prototype.close = function() { + }; + a.create = function(a) { + return "undefined" !== typeof SpecialInflate ? new t(a) : new w(a); + }; + a.prototype._processZLibHeader = function(a, d, q) { + if (d + 2 > q) { + return 0; + } + a = a[d] << 8 | a[d + 1]; + d = null; + 2048 !== (a & 3840) ? d = "inflate: unknown compression method" : 0 !== a % 31 ? d = "inflate: bad FCHECK" : 0 !== (a & 32) && (d = "inflate: FDICT bit set"); + return d ? (this._error = d, -1) : 2; + }; + a.inflate = function(f, d, q) { + var b = new Uint8Array(d), s = 0; + d = a.create(q); + d.onData = function(a) { + b.set(a, s); + s += a.length; + }; + d.push(f); + d.close(); + return b; + }; + return a; + }(); + m.Inflate = c; + var w = function(h) { + function f(d) { + h.call(this, d); + this._buffer = null; this._bitLength = this._bitBuffer = this._bufferPosition = this._bufferSize = 0; this._window = new Uint8Array(65794); this._windowPosition = 0; - this._state = b ? 7 : 0; + this._state = d ? 7 : 0; this._isFinalBlock = !1; this._distanceTable = this._literalTable = null; this._block0Read = 0; this._block2State = null; this._copyState = {state:0, len:0, lenBits:0, dist:0, distBits:0}; this._error = void 0; - if (!u) { - t = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]); - k = new Uint16Array(30); + if (!l) { + k = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]); + b = new Uint16Array(30); a = new Uint8Array(30); - for (var r = b = 0, n = 1;30 > b;++b) { - k[b] = n, n += 1 << (a[b] = ~~((r += 2 < b ? 1 : 0) / 2)); + for (var f = d = 0, x = 1;30 > d;++d) { + b[d] = x, x += 1 << (a[d] = ~~((f += 2 < d ? 1 : 0) / 2)); } - var f = new Uint8Array(288); - for (b = 0;32 > b;++b) { - f[b] = 5; + var s = new Uint8Array(288); + for (d = 0;32 > d;++d) { + s[d] = 5; } - c = s(f.subarray(0, 32)); - g = new Uint16Array(29); - p = new Uint8Array(29); - r = b = 0; - for (n = 3;29 > b;++b) { - g[b] = n - (28 == b ? 1 : 0), n += 1 << (p[b] = ~~((r += 4 < b ? 1 : 0) / 4 % 6)); + n = e(s.subarray(0, 32)); + p = new Uint16Array(29); + y = new Uint8Array(29); + f = d = 0; + for (x = 3;29 > d;++d) { + p[d] = x - (28 == d ? 1 : 0), x += 1 << (y[d] = ~~((f += 4 < d ? 1 : 0) / 4 % 6)); } - for (b = 0;288 > b;++b) { - f[b] = 144 > b || 279 < b ? 8 : 256 > b ? 9 : 7; + for (d = 0;288 > d;++d) { + s[d] = 144 > d || 279 < d ? 8 : 256 > d ? 9 : 7; } - v = s(f); - u = !0; + v = e(s); + l = !0; } } - Object.defineProperty(b.prototype, "error", {get:function() { + __extends(f, h); + Object.defineProperty(f.prototype, "error", {get:function() { return this._error; }, enumerable:!0, configurable:!0}); - b.prototype.push = function(b) { - if (this._buffer.length < this._bufferSize + b.length) { - var a = new Uint8Array(this._bufferSize + b.length); - a.set(this._buffer); - this._buffer = a; + f.prototype.push = function(a) { + if (!this._buffer || this._buffer.length < this._bufferSize + a.length) { + var f = new Uint8Array(this._bufferSize + a.length); + this._buffer && f.set(this._buffer); + this._buffer = f; } - this._buffer.set(b, this._bufferSize); - this._bufferSize += b.length; + this._buffer.set(a, this._bufferSize); + this._bufferSize += a.length; this._bufferPosition = 0; - b = !1; + a = !1; do { - a = this._windowPosition; - if (0 === this._state && (b = this._decodeInitState())) { + f = this._windowPosition; + if (0 === this._state && (a = this._decodeInitState())) { break; } switch(this._state) { case 1: - b = this._decodeBlock0(); + a = this._decodeBlock0(); break; case 3: - if (b = this._decodeBlock2Pre()) { + if (a = this._decodeBlock2Pre()) { break; } ; case 2: ; case 4: - b = this._decodeBlock(); + a = this._decodeBlock(); break; case 6: ; @@ -2798,362 +2924,374 @@ this._bufferPosition = this._bufferSize; break; case 7: - b = this._verifyZlibHeader(); + var b = this._processZLibHeader(this._buffer, this._bufferPosition, this._bufferSize); + 0 < b ? (this._bufferPosition += b, this._state = 0) : 0 === b ? a = !0 : this._state = 6; } - if (0 < this._windowPosition - a) { - this.onData(this._window.subarray(a, this._windowPosition)); + if (0 < this._windowPosition - f) { + this.onData(this._window.subarray(f, this._windowPosition)); } - 65536 <= this._windowPosition && (this._window.set(this._window.subarray(this._windowPosition - 32768, this._windowPosition)), this._windowPosition = 32768); - } while (!b && this._bufferPosition < this._bufferSize); - this._bufferPosition < this._bufferSize ? (this._buffer.set(this._buffer.subarray(this._bufferPosition, this._bufferSize)), this._bufferSize -= this._bufferPosition) : this._bufferSize = 0; - }; - b.prototype._verifyZlibHeader = function() { - var b = this._bufferPosition; - if (b + 2 > this._bufferSize) { - return!0; - } - var a = this._buffer, a = a[b] << 8 | a[b + 1]; - this._bufferPosition = b + 2; - b = null; - 2048 !== (a & 3840) ? b = "inflate: unknown compression method" : 0 !== a % 31 ? b = "inflate: bad FCHECK" : 0 !== (a & 32) && (b = "inflate: FDICT bit set"); - b ? (this._error = b, this._state = 6) : this._state = 0; + 65536 <= this._windowPosition && ("copyWithin" in this._buffer ? this._window.copyWithin(0, this._windowPosition - 32768, this._windowPosition) : this._window.set(this._window.subarray(this._windowPosition - 32768, this._windowPosition)), this._windowPosition = 32768); + } while (!a && this._bufferPosition < this._bufferSize); + this._bufferPosition < this._bufferSize ? ("copyWithin" in this._buffer ? this._buffer.copyWithin(0, this._bufferPosition, this._bufferSize) : this._buffer.set(this._buffer.subarray(this._bufferPosition, this._bufferSize)), this._bufferSize -= this._bufferPosition) : this._bufferSize = 0; }; - b.prototype._decodeInitState = function() { + f.prototype._decodeInitState = function() { if (this._isFinalBlock) { return this._state = 5, !1; } - var b = this._buffer, a = this._bufferSize, g = this._bitBuffer, f = this._bitLength, q = this._bufferPosition; - if (3 > (a - q << 3) + f) { + var a = this._buffer, f = this._bufferSize, b = this._bitBuffer, h = this._bitLength, c = this._bufferPosition; + if (3 > (f - c << 3) + h) { return!0; } - 3 > f && (g |= b[q++] << f, f += 8); - var p = g & 7, g = g >> 3, f = f - 3; + 3 > h && (b |= a[c++] << h, h += 8); + var p = b & 7, b = b >> 3, h = h - 3; switch(p >> 1) { case 0: - f = g = 0; - if (4 > a - q) { + h = b = 0; + if (4 > f - c) { return!0; } - var r = b[q] | b[q + 1] << 8, b = b[q + 2] | b[q + 3] << 8, q = q + 4; - if (65535 !== (r ^ b)) { + var r = a[c] | a[c + 1] << 8, a = a[c + 2] | a[c + 3] << 8, c = c + 4; + if (65535 !== (r ^ a)) { this._error = "inflate: invalid block 0 length"; - b = 6; + a = 6; break; } - 0 === r ? b = 0 : (this._block0Read = r, b = 1); + 0 === r ? a = 0 : (this._block0Read = r, a = 1); break; case 1: - b = 2; + a = 2; this._literalTable = v; - this._distanceTable = c; + this._distanceTable = n; break; case 2: - if (26 > (a - q << 3) + f) { + if (26 > (f - c << 3) + h) { return!0; } - for (;14 > f;) { - g |= b[q++] << f, f += 8; + for (;14 > h;) { + b |= a[c++] << h, h += 8; } - r = (g >> 10 & 15) + 4; - if ((a - q << 3) + f < 14 + 3 * r) { + r = (b >> 10 & 15) + 4; + if ((f - c << 3) + h < 14 + 3 * r) { return!0; } - for (var a = {numLiteralCodes:(g & 31) + 257, numDistanceCodes:(g >> 5 & 31) + 1, codeLengthTable:void 0, bitLengths:void 0, codesRead:0, dupBits:0}, g = g >> 14, f = f - 14, l = new Uint8Array(19), k = 0;k < r;++k) { - 3 > f && (g |= b[q++] << f, f += 8), l[t[k]] = g & 7, g >>= 3, f -= 3; + for (var f = {numLiteralCodes:(b & 31) + 257, numDistanceCodes:(b >> 5 & 31) + 1, codeLengthTable:void 0, bitLengths:void 0, codesRead:0, dupBits:0}, b = b >> 14, h = h - 14, t = new Uint8Array(19), l = 0;l < r;++l) { + 3 > h && (b |= a[c++] << h, h += 8), t[k[l]] = b & 7, b >>= 3, h -= 3; } - for (;19 > k;k++) { - l[t[k]] = 0; + for (;19 > l;l++) { + t[k[l]] = 0; } - a.bitLengths = new Uint8Array(a.numLiteralCodes + a.numDistanceCodes); - a.codeLengthTable = s(l); - this._block2State = a; - b = 3; + f.bitLengths = new Uint8Array(f.numLiteralCodes + f.numDistanceCodes); + f.codeLengthTable = e(t); + this._block2State = f; + a = 3; break; default: return this._error = "inflate: unsupported block type", !1; } this._isFinalBlock = !!(p & 1); - this._state = b; - this._bufferPosition = q; - this._bitBuffer = g; - this._bitLength = f; + this._state = a; + this._bufferPosition = c; + this._bitBuffer = b; + this._bitLength = h; return!1; }; - b.prototype._decodeBlock0 = function() { - var b = this._bufferPosition, a = this._windowPosition, c = this._block0Read, f = 65794 - a, q = this._bufferSize - b, g = q < c, p = Math.min(f, q, c); - this._window.set(this._buffer.subarray(b, b + p), a); - this._windowPosition = a + p; - this._bufferPosition = b + p; - this._block0Read = c - p; - return c === p ? (this._state = 0, !1) : g && f < q; - }; - b.prototype._readBits = function(b) { - var a = this._bitBuffer, c = this._bitLength; - if (b > c) { - var f = this._bufferPosition, q = this._bufferSize; + f.prototype._decodeBlock0 = function() { + var a = this._bufferPosition, f = this._windowPosition, b = this._block0Read, h = 65794 - f, n = this._bufferSize - a, c = n < b, p = Math.min(h, n, b); + this._window.set(this._buffer.subarray(a, a + p), f); + this._windowPosition = f + p; + this._bufferPosition = a + p; + this._block0Read = b - p; + return b === p ? (this._state = 0, !1) : c && h < n; + }; + f.prototype._readBits = function(a) { + var f = this._bitBuffer, b = this._bitLength; + if (a > b) { + var h = this._bufferPosition, n = this._bufferSize; do { - if (f >= q) { - return this._bufferPosition = f, this._bitBuffer = a, this._bitLength = c, -1; + if (h >= n) { + return this._bufferPosition = h, this._bitBuffer = f, this._bitLength = b, -1; } - a |= this._buffer[f++] << c; - c += 8; - } while (b > c); - this._bufferPosition = f; - } - this._bitBuffer = a >> b; - this._bitLength = c - b; - return a & (1 << b) - 1; - }; - b.prototype._readCode = function(b) { - var a = this._bitBuffer, c = this._bitLength, f = b.maxBits; - if (f > c) { - var q = this._bufferPosition, g = this._bufferSize; + f |= this._buffer[h++] << b; + b += 8; + } while (a > b); + this._bufferPosition = h; + } + this._bitBuffer = f >> a; + this._bitLength = b - a; + return f & (1 << a) - 1; + }; + f.prototype._readCode = function(a) { + var f = this._bitBuffer, b = this._bitLength, h = a.maxBits; + if (h > b) { + var n = this._bufferPosition, c = this._bufferSize; do { - if (q >= g) { - return this._bufferPosition = q, this._bitBuffer = a, this._bitLength = c, -1; + if (n >= c) { + return this._bufferPosition = n, this._bitBuffer = f, this._bitLength = b, -1; } - a |= this._buffer[q++] << c; - c += 8; - } while (f > c); - this._bufferPosition = q; - } - b = b.codes[a & (1 << f) - 1]; - f = b >> 16; - if (b & 32768) { + f |= this._buffer[n++] << b; + b += 8; + } while (h > b); + this._bufferPosition = n; + } + a = a.codes[f & (1 << h) - 1]; + h = a >> 16; + if (a & 32768) { return this._error = "inflate: invalid encoding", this._state = 6, -1; } - this._bitBuffer = a >> f; - this._bitLength = c - f; - return b & 65535; - }; - b.prototype._decodeBlock2Pre = function() { - var b = this._block2State, a = b.numLiteralCodes + b.numDistanceCodes, c = b.bitLengths, f = b.codesRead, q = 0 < f ? c[f - 1] : 0, g = b.codeLengthTable, p; - if (0 < b.dupBits) { - p = this._readBits(b.dupBits); + this._bitBuffer = f >> h; + this._bitLength = b - h; + return a & 65535; + }; + f.prototype._decodeBlock2Pre = function() { + var a = this._block2State, f = a.numLiteralCodes + a.numDistanceCodes, b = a.bitLengths, h = a.codesRead, n = 0 < h ? b[h - 1] : 0, c = a.codeLengthTable, p; + if (0 < a.dupBits) { + p = this._readBits(a.dupBits); if (0 > p) { return!0; } for (;p--;) { - c[f++] = q; + b[h++] = n; } - b.dupBits = 0; + a.dupBits = 0; } - for (;f < a;) { - var r = this._readCode(g); + for (;h < f;) { + var r = this._readCode(c); if (0 > r) { - return b.codesRead = f, !0; + return a.codesRead = h, !0; } if (16 > r) { - c[f++] = q = r; + b[h++] = n = r; } else { - var l; + var k; switch(r) { case 16: - l = 2; + k = 2; p = 3; - r = q; + r = n; break; case 17: - p = l = 3; + p = k = 3; r = 0; break; case 18: - l = 7, p = 11, r = 0; + k = 7, p = 11, r = 0; } for (;p--;) { - c[f++] = r; + b[h++] = r; } - p = this._readBits(l); + p = this._readBits(k); if (0 > p) { - return b.codesRead = f, b.dupBits = l, !0; + return a.codesRead = h, a.dupBits = k, !0; } for (;p--;) { - c[f++] = r; + b[h++] = r; } - q = r; + n = r; } } - this._literalTable = s(c.subarray(0, b.numLiteralCodes)); - this._distanceTable = s(c.subarray(b.numLiteralCodes)); + this._literalTable = e(b.subarray(0, a.numLiteralCodes)); + this._distanceTable = e(b.subarray(a.numLiteralCodes)); this._state = 4; this._block2State = null; return!1; }; - b.prototype._decodeBlock = function() { - var b = this._literalTable, c = this._distanceTable, n = this._window, f = this._windowPosition, q = this._copyState, r, l, e, v; - if (0 !== q.state) { - switch(q.state) { + f.prototype._decodeBlock = function() { + var d = this._literalTable, f = this._distanceTable, h = this._window, n = this._windowPosition, c = this._copyState, r, k, t, l; + if (0 !== c.state) { + switch(c.state) { case 1: - if (0 > (r = this._readBits(q.lenBits))) { + if (0 > (r = this._readBits(c.lenBits))) { return!0; } - q.len += r; - q.state = 2; + c.len += r; + c.state = 2; case 2: - if (0 > (r = this._readCode(c))) { + if (0 > (r = this._readCode(f))) { return!0; } - q.distBits = a[r]; - q.dist = k[r]; - q.state = 3; + c.distBits = a[r]; + c.dist = b[r]; + c.state = 3; case 3: r = 0; - if (0 < q.distBits && 0 > (r = this._readBits(q.distBits))) { + if (0 < c.distBits && 0 > (r = this._readBits(c.distBits))) { return!0; } - v = q.dist + r; - l = q.len; - for (r = f - v;l--;) { - n[f++] = n[r++]; - } - q.state = 0; - if (65536 <= f) { - return this._windowPosition = f, !1; + l = c.dist + r; + k = c.len; + for (r = n - l;k--;) { + h[n++] = h[r++]; + } + c.state = 0; + if (65536 <= n) { + return this._windowPosition = n, !1; } break; } } do { - r = this._readCode(b); + r = this._readCode(d); if (0 > r) { - return this._windowPosition = f, !0; + return this._windowPosition = n, !0; } if (256 > r) { - n[f++] = r; + h[n++] = r; } else { if (256 < r) { - this._windowPosition = f; + this._windowPosition = n; r -= 257; - e = p[r]; - l = g[r]; - r = 0 === e ? 0 : this._readBits(e); + t = y[r]; + k = p[r]; + r = 0 === t ? 0 : this._readBits(t); if (0 > r) { - return q.state = 1, q.len = l, q.lenBits = e, !0; + return c.state = 1, c.len = k, c.lenBits = t, !0; } - l += r; - r = this._readCode(c); + k += r; + r = this._readCode(f); if (0 > r) { - return q.state = 2, q.len = l, !0; + return c.state = 2, c.len = k, !0; } - e = a[r]; - v = k[r]; - r = 0 === e ? 0 : this._readBits(e); + t = a[r]; + l = b[r]; + r = 0 === t ? 0 : this._readBits(t); if (0 > r) { - return q.state = 3, q.len = l, q.dist = v, q.distBits = e, !0; + return c.state = 3, c.len = k, c.dist = l, c.distBits = t, !0; } - v += r; - for (r = f - v;l--;) { - n[f++] = n[r++]; + l += r; + for (r = n - l;k--;) { + h[n++] = h[r++]; } } else { this._state = 0; break; } } - } while (65536 > f); - this._windowPosition = f; + } while (65536 > n); + this._windowPosition = n; return!1; }; - b.inflate = function(a, c, g) { - var f = new Uint8Array(c), q = 0; - c = new b(g); - c.onData = function(b) { - f.set(b, q); - q += b.length; - }; - c.push(a); - return f; + return f; + }(c), k, b, a, n, p, y, v, l = !1, t = function(a) { + function f(d) { + a.call(this, d); + this._verifyHeader = d; + this._specialInflate = new SpecialInflate; + this._specialInflate.onData = function(a) { + this.onData(a); + }.bind(this); + } + __extends(f, a); + f.prototype.push = function(a) { + if (this._verifyHeader) { + var f; + this._buffer ? (f = new Uint8Array(this._buffer.length + a.length), f.set(this._buffer), f.set(a, this._buffer.length), this._buffer = null) : f = new Uint8Array(a); + var b = this._processZLibHeader(f, 0, f.length); + if (0 === b) { + this._buffer = f; + return; + } + this._verifyHeader = !0; + 0 < b && (a = f.subarray(b)); + } + this._error || this._specialInflate.push(a); }; - return b; - }(); - d.Inflate = e; - var t, k, a, c, g, p, v, u = !1, l; - (function(b) { - b[b.WRITE = 0] = "WRITE"; - b[b.DONE = 1] = "DONE"; - b[b.ZLIB_HEADER = 2] = "ZLIB_HEADER"; - })(l || (l = {})); - var r = function() { - function b() { + f.prototype.close = function() { + this._specialInflate && (this._specialInflate.close(), this._specialInflate = null); + }; + return f; + }(c), r; + (function(a) { + a[a.WRITE = 0] = "WRITE"; + a[a.DONE = 1] = "DONE"; + a[a.ZLIB_HEADER = 2] = "ZLIB_HEADER"; + })(r || (r = {})); + var u = function() { + function a() { this.a = 1; this.b = 0; } - b.prototype.update = function(b, a, c) { - for (var f = this.a, q = this.b;a < c;++a) { - f = (f + (b[a] & 255)) % 65521, q = (q + f) % 65521; + a.prototype.update = function(a, d, q) { + for (var b = this.a, h = this.b;d < q;++d) { + b = (b + (a[d] & 255)) % 65521, h = (h + b) % 65521; } - this.a = f; - this.b = q; + this.a = b; + this.b = h; }; - b.prototype.getChecksum = function() { + a.prototype.getChecksum = function() { return this.b << 16 | this.a; }; - return b; + return a; }(); - d.Adler32 = r; - l = function() { - function b(b) { - this._state = (this._writeZlibHeader = b) ? 2 : 0; - this._adler32 = b ? new r : null; + m.Adler32 = u; + r = function() { + function a(f) { + this._state = (this._writeZlibHeader = f) ? 2 : 0; + this._adler32 = f ? new u : null; } - b.prototype.push = function(b) { + a.prototype.push = function(a) { 2 === this._state && (this.onData(new Uint8Array([120, 156])), this._state = 0); - for (var a = b.length, c = new Uint8Array(a + 5 * Math.ceil(a / 65535)), f = 0, q = 0;65535 < a;) { - c.set(new Uint8Array([0, 255, 255, 0, 0]), f), f += 5, c.set(b.subarray(q, q + 65535), f), q += 65535, f += 65535, a -= 65535; + for (var d = a.length, q = new Uint8Array(d + 5 * Math.ceil(d / 65535)), b = 0, h = 0;65535 < d;) { + q.set(new Uint8Array([0, 255, 255, 0, 0]), b), b += 5, q.set(a.subarray(h, h + 65535), b), h += 65535, b += 65535, d -= 65535; } - c.set(new Uint8Array([0, a & 255, a >> 8 & 255, ~a & 255, ~a >> 8 & 255]), f); - c.set(b.subarray(q, a), f + 5); - this.onData(c); - this._adler32 && this._adler32.update(b, 0, a); + q.set(new Uint8Array([0, d & 255, d >> 8 & 255, ~d & 255, ~d >> 8 & 255]), b); + q.set(a.subarray(h, d), b + 5); + this.onData(q); + this._adler32 && this._adler32.update(a, 0, d); }; - b.prototype.finish = function() { + a.prototype.finish = function() { this._state = 1; this.onData(new Uint8Array([1, 0, 0, 255, 255])); if (this._adler32) { - var b = this._adler32.getChecksum(); - this.onData(new Uint8Array([b & 255, b >> 8 & 255, b >> 16 & 255, b >>> 24 & 255])); + var a = this._adler32.getChecksum(); + this.onData(new Uint8Array([a & 255, a >> 8 & 255, a >> 16 & 255, a >>> 24 & 255])); } }; - return b; + return a; }(); - d.Deflate = l; - })(d.ArrayUtilities || (d.ArrayUtilities = {})); + m.Deflate = r; + })(g.ArrayUtilities || (g.ArrayUtilities = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - function s() { - t("throwEOFError"); + function e(b, n) { + b !== a(b, 0, n) && throwError("RangeError", Errors.ParamRangeError); } - function e(a) { + function c(a) { return "string" === typeof a ? a : void 0 == a ? null : a + ""; } - var t = d.Debug.notImplemented, k = d.StringUtilities.utf8decode, a = d.StringUtilities.utf8encode, c = d.NumberUtilities.clamp, g = function() { - return function(a, c, b) { + var w = g.Debug.notImplemented, k = g.StringUtilities.utf8decode, b = g.StringUtilities.utf8encode, a = g.NumberUtilities.clamp, n = function() { + return function(a, b, n) { this.buffer = a; - this.length = c; - this.littleEndian = b; + this.length = b; + this.littleEndian = n; }; }(); - m.PlainObjectDataBuffer = g; - for (var p = new Uint32Array(33), v = 1, u = 0;32 >= v;v++) { - p[v] = u = u << 1 | 1; + m.PlainObjectDataBuffer = n; + for (var p = new Uint32Array(33), y = 1, v = 0;32 >= y;y++) { + p[y] = v = v << 1 | 1; } - v = function() { + var l; + (function(a) { + a[a.U8 = 1] = "U8"; + a[a.I32 = 2] = "I32"; + a[a.F32 = 4] = "F32"; + })(l || (l = {})); + y = function() { function l(a) { - "undefined" === typeof a && (a = l.INITIAL_SIZE); - this._buffer || (this._buffer = new ArrayBuffer(a), this._position = this._length = 0, this._updateViews(), this._littleEndian = l._nativeLittleEndian, this._bitLength = this._bitBuffer = 0); + void 0 === a && (a = l.INITIAL_SIZE); + this._buffer || (this._buffer = new ArrayBuffer(a), this._position = this._length = 0, this._resetViews(), this._littleEndian = l._nativeLittleEndian, this._bitLength = this._bitBuffer = 0); } l.FromArrayBuffer = function(a, b) { - "undefined" === typeof b && (b = -1); - var c = Object.create(l.prototype); - c._buffer = a; - c._length = -1 === b ? a.byteLength : b; - c._position = 0; - c._updateViews(); - c._littleEndian = l._nativeLittleEndian; - c._bitBuffer = 0; - c._bitLength = 0; - return c; + void 0 === b && (b = -1); + var h = Object.create(l.prototype); + h._buffer = a; + h._length = -1 === b ? a.byteLength : b; + h._position = 0; + h._resetViews(); + h._littleEndian = l._nativeLittleEndian; + h._bitBuffer = 0; + h._bitLength = 0; + return h; }; l.FromPlainObject = function(a) { var b = l.FromArrayBuffer(a.buffer, a.length); @@ -3161,11 +3299,14 @@ return b; }; l.prototype.toPlainObject = function() { - return new g(this._buffer, this._length, this._littleEndian); + return new n(this._buffer, this._length, this._littleEndian); }; - l.prototype._updateViews = function() { + l.prototype._resetViews = function() { this._u8 = new Uint8Array(this._buffer); - 0 === (this._buffer.byteLength & 3) && (this._i32 = new Int32Array(this._buffer), this._f32 = new Float32Array(this._buffer)); + this._f32 = this._i32 = null; + }; + l.prototype._requestViews = function(a) { + 0 === (this._buffer.byteLength & 3) && (null === this._i32 && a & 2 && (this._i32 = new Int32Array(this._buffer)), null === this._f32 && a & 4 && (this._f32 = new Float32Array(this._buffer))); }; l.prototype.getBytes = function() { return new Uint8Array(this._buffer, 0, this._length); @@ -3173,14 +3314,14 @@ l.prototype._ensureCapacity = function(a) { var b = this._buffer; if (b.byteLength < a) { - for (var c = Math.max(b.byteLength, 1);c < a;) { - c *= 2; + for (var h = Math.max(b.byteLength, 1);h < a;) { + h *= 2; } - a = l._arrayBufferPool.acquire(c); - c = this._u8; + a = l._arrayBufferPool.acquire(h); + h = this._u8; this._buffer = a; - this._updateViews(); - this._u8.set(c); + this._resetViews(); + this._u8.set(h); l._arrayBufferPool.release(b); } }; @@ -3194,19 +3335,19 @@ return this.readUnsignedByte() << 24 >> 24; }; l.prototype.readUnsignedByte = function() { - this._position + 1 > this._length && s(); + this._position + 1 > this._length && throwError("EOFError", Errors.EOFError); return this._u8[this._position++]; }; l.prototype.readBytes = function(a, b) { - var c = 0; - "undefined" === typeof c && (c = 0); - "undefined" === typeof b && (b = 0); - var g = this._position; - c || (c = 0); - b || (b = this._length - g); - g + b > this._length && s(); - a.length < c + b && (a._ensureCapacity(c + b), a.length = c + b); - a._u8.set(new Uint8Array(this._buffer, g, b), c); + var h = 0; + void 0 === h && (h = 0); + void 0 === b && (b = 0); + var f = this._position; + h || (h = 0); + b || (b = this._length - f); + f + b > this._length && throwError("EOFError", Errors.EOFError); + a.length < h + b && (a._ensureCapacity(h + b), a.length = h + b); + a._u8.set(new Uint8Array(this._buffer, f, b), h); this._position += b; }; l.prototype.readShort = function() { @@ -3214,39 +3355,40 @@ }; l.prototype.readUnsignedShort = function() { var a = this._u8, b = this._position; - b + 2 > this._length && s(); - var c = a[b + 0], a = a[b + 1]; + b + 2 > this._length && throwError("EOFError", Errors.EOFError); + var h = a[b + 0], a = a[b + 1]; this._position = b + 2; - return this._littleEndian ? a << 8 | c : c << 8 | a; + return this._littleEndian ? a << 8 | h : h << 8 | a; }; l.prototype.readInt = function() { var a = this._u8, b = this._position; - b + 4 > this._length && s(); - var c = a[b + 0], g = a[b + 1], n = a[b + 2], a = a[b + 3]; + b + 4 > this._length && throwError("EOFError", Errors.EOFError); + var h = a[b + 0], f = a[b + 1], d = a[b + 2], a = a[b + 3]; this._position = b + 4; - return this._littleEndian ? a << 24 | n << 16 | g << 8 | c : c << 24 | g << 16 | n << 8 | a; + return this._littleEndian ? a << 24 | d << 16 | f << 8 | h : h << 24 | f << 16 | d << 8 | a; }; l.prototype.readUnsignedInt = function() { return this.readInt() >>> 0; }; l.prototype.readFloat = function() { var a = this._position; - a + 4 > this._length && s(); + a + 4 > this._length && throwError("EOFError", Errors.EOFError); this._position = a + 4; + this._requestViews(4); if (this._littleEndian && 0 === (a & 3) && this._f32) { return this._f32[a >> 2]; } - var b = this._u8, c = d.IntegerUtilities.u8; - this._littleEndian ? (c[0] = b[a + 0], c[1] = b[a + 1], c[2] = b[a + 2], c[3] = b[a + 3]) : (c[3] = b[a + 0], c[2] = b[a + 1], c[1] = b[a + 2], c[0] = b[a + 3]); - return d.IntegerUtilities.f32[0]; + var b = this._u8, h = g.IntegerUtilities.u8; + this._littleEndian ? (h[0] = b[a + 0], h[1] = b[a + 1], h[2] = b[a + 2], h[3] = b[a + 3]) : (h[3] = b[a + 0], h[2] = b[a + 1], h[1] = b[a + 2], h[0] = b[a + 3]); + return g.IntegerUtilities.f32[0]; }; l.prototype.readDouble = function() { var a = this._u8, b = this._position; - b + 8 > this._length && s(); - var c = d.IntegerUtilities.u8; - this._littleEndian ? (c[0] = a[b + 0], c[1] = a[b + 1], c[2] = a[b + 2], c[3] = a[b + 3], c[4] = a[b + 4], c[5] = a[b + 5], c[6] = a[b + 6], c[7] = a[b + 7]) : (c[0] = a[b + 7], c[1] = a[b + 6], c[2] = a[b + 5], c[3] = a[b + 4], c[4] = a[b + 3], c[5] = a[b + 2], c[6] = a[b + 1], c[7] = a[b + 0]); + b + 8 > this._length && throwError("EOFError", Errors.EOFError); + var h = g.IntegerUtilities.u8; + this._littleEndian ? (h[0] = a[b + 0], h[1] = a[b + 1], h[2] = a[b + 2], h[3] = a[b + 3], h[4] = a[b + 4], h[5] = a[b + 5], h[6] = a[b + 6], h[7] = a[b + 7]) : (h[0] = a[b + 7], h[1] = a[b + 6], h[2] = a[b + 5], h[3] = a[b + 4], h[4] = a[b + 3], h[5] = a[b + 2], h[6] = a[b + 1], h[7] = a[b + 0]); this._position = b + 8; - return d.IntegerUtilities.f64[0]; + return g.IntegerUtilities.f64[0]; }; l.prototype.writeBoolean = function(a) { this.writeByte(a ? 1 : 0); @@ -3270,16 +3412,16 @@ this._position = b; b > this._length && (this._length = b); }; - l.prototype.writeBytes = function(a, b, g) { - "undefined" === typeof b && (b = 0); - "undefined" === typeof g && (g = 0); + l.prototype.writeBytes = function(a, b, h) { + void 0 === b && (b = 0); + void 0 === h && (h = 0); + g.isNullOrUndefined(a) && throwError("TypeError", Errors.NullPointerError, "bytes"); 2 > arguments.length && (b = 0); - 3 > arguments.length && (g = 0); - b !== c(b, 0, a.length) && t("throwRangeError"); - var p = b + g; - p !== c(p, 0, a.length) && t("throwRangeError"); - 0 === g && (g = a.length - b); - this.writeRawBytes(new Int8Array(a._buffer, b, g)); + 3 > arguments.length && (h = 0); + e(b, a.length); + e(b + h, a.length); + 0 === h && (h = a.length - b); + this.writeRawBytes(new Int8Array(a._buffer, b, h)); }; l.prototype.writeShort = function(a) { this.writeUnsignedShort(a); @@ -3287,60 +3429,73 @@ l.prototype.writeUnsignedShort = function(a) { var b = this._position; this._ensureCapacity(b + 2); - var c = this._u8; - this._littleEndian ? (c[b + 0] = a, c[b + 1] = a >> 8) : (c[b + 0] = a >> 8, c[b + 1] = a); + var h = this._u8; + this._littleEndian ? (h[b + 0] = a, h[b + 1] = a >> 8) : (h[b + 0] = a >> 8, h[b + 1] = a); this._position = b += 2; b > this._length && (this._length = b); }; l.prototype.writeInt = function(a) { this.writeUnsignedInt(a); }; - l.prototype.write4Ints = function(a, b, c, g) { - this.write4UnsignedInts(a, b, c, g); + l.prototype.write2Ints = function(a, b) { + this.write2UnsignedInts(a, b); + }; + l.prototype.write4Ints = function(a, b, h, f) { + this.write4UnsignedInts(a, b, h, f); }; l.prototype.writeUnsignedInt = function(a) { var b = this._position; this._ensureCapacity(b + 4); + this._requestViews(2); if (this._littleEndian === l._nativeLittleEndian && 0 === (b & 3) && this._i32) { this._i32[b >> 2] = a; } else { - var c = this._u8; - this._littleEndian ? (c[b + 0] = a, c[b + 1] = a >> 8, c[b + 2] = a >> 16, c[b + 3] = a >> 24) : (c[b + 0] = a >> 24, c[b + 1] = a >> 16, c[b + 2] = a >> 8, c[b + 3] = a); + var h = this._u8; + this._littleEndian ? (h[b + 0] = a, h[b + 1] = a >> 8, h[b + 2] = a >> 16, h[b + 3] = a >> 24) : (h[b + 0] = a >> 24, h[b + 1] = a >> 16, h[b + 2] = a >> 8, h[b + 3] = a); } this._position = b += 4; b > this._length && (this._length = b); }; - l.prototype.write4UnsignedInts = function(a, b, c, g) { - var n = this._position; - this._ensureCapacity(n + 16); - this._littleEndian === l._nativeLittleEndian && 0 === (n & 3) && this._i32 ? (this._i32[(n >> 2) + 0] = a, this._i32[(n >> 2) + 1] = b, this._i32[(n >> 2) + 2] = c, this._i32[(n >> 2) + 3] = g, this._position = n += 16, n > this._length && (this._length = n)) : (this.writeUnsignedInt(a), this.writeUnsignedInt(b), this.writeUnsignedInt(c), this.writeUnsignedInt(g)); + l.prototype.write2UnsignedInts = function(a, b) { + var h = this._position; + this._ensureCapacity(h + 8); + this._requestViews(2); + this._littleEndian === l._nativeLittleEndian && 0 === (h & 3) && this._i32 ? (this._i32[(h >> 2) + 0] = a, this._i32[(h >> 2) + 1] = b, this._position = h += 8, h > this._length && (this._length = h)) : (this.writeUnsignedInt(a), this.writeUnsignedInt(b)); + }; + l.prototype.write4UnsignedInts = function(a, b, h, f) { + var d = this._position; + this._ensureCapacity(d + 16); + this._requestViews(2); + this._littleEndian === l._nativeLittleEndian && 0 === (d & 3) && this._i32 ? (this._i32[(d >> 2) + 0] = a, this._i32[(d >> 2) + 1] = b, this._i32[(d >> 2) + 2] = h, this._i32[(d >> 2) + 3] = f, this._position = d += 16, d > this._length && (this._length = d)) : (this.writeUnsignedInt(a), this.writeUnsignedInt(b), this.writeUnsignedInt(h), this.writeUnsignedInt(f)); }; l.prototype.writeFloat = function(a) { var b = this._position; this._ensureCapacity(b + 4); + this._requestViews(4); if (this._littleEndian === l._nativeLittleEndian && 0 === (b & 3) && this._f32) { this._f32[b >> 2] = a; } else { - var c = this._u8; - d.IntegerUtilities.f32[0] = a; - a = d.IntegerUtilities.u8; - this._littleEndian ? (c[b + 0] = a[0], c[b + 1] = a[1], c[b + 2] = a[2], c[b + 3] = a[3]) : (c[b + 0] = a[3], c[b + 1] = a[2], c[b + 2] = a[1], c[b + 3] = a[0]); + var h = this._u8; + g.IntegerUtilities.f32[0] = a; + a = g.IntegerUtilities.u8; + this._littleEndian ? (h[b + 0] = a[0], h[b + 1] = a[1], h[b + 2] = a[2], h[b + 3] = a[3]) : (h[b + 0] = a[3], h[b + 1] = a[2], h[b + 2] = a[1], h[b + 3] = a[0]); } this._position = b += 4; b > this._length && (this._length = b); }; - l.prototype.write6Floats = function(a, b, c, g, n, f) { - var q = this._position; - this._ensureCapacity(q + 24); - this._littleEndian === l._nativeLittleEndian && 0 === (q & 3) && this._f32 ? (this._f32[(q >> 2) + 0] = a, this._f32[(q >> 2) + 1] = b, this._f32[(q >> 2) + 2] = c, this._f32[(q >> 2) + 3] = g, this._f32[(q >> 2) + 4] = n, this._f32[(q >> 2) + 5] = f, this._position = q += 24, q > this._length && (this._length = q)) : (this.writeFloat(a), this.writeFloat(b), this.writeFloat(c), this.writeFloat(g), this.writeFloat(n), this.writeFloat(f)); + l.prototype.write6Floats = function(a, b, h, f, d, q) { + var n = this._position; + this._ensureCapacity(n + 24); + this._requestViews(4); + this._littleEndian === l._nativeLittleEndian && 0 === (n & 3) && this._f32 ? (this._f32[(n >> 2) + 0] = a, this._f32[(n >> 2) + 1] = b, this._f32[(n >> 2) + 2] = h, this._f32[(n >> 2) + 3] = f, this._f32[(n >> 2) + 4] = d, this._f32[(n >> 2) + 5] = q, this._position = n += 24, n > this._length && (this._length = n)) : (this.writeFloat(a), this.writeFloat(b), this.writeFloat(h), this.writeFloat(f), this.writeFloat(d), this.writeFloat(q)); }; l.prototype.writeDouble = function(a) { var b = this._position; this._ensureCapacity(b + 8); - var c = this._u8; - d.IntegerUtilities.f64[0] = a; - a = d.IntegerUtilities.u8; - this._littleEndian ? (c[b + 0] = a[0], c[b + 1] = a[1], c[b + 2] = a[2], c[b + 3] = a[3], c[b + 4] = a[4], c[b + 5] = a[5], c[b + 6] = a[6], c[b + 7] = a[7]) : (c[b + 0] = a[7], c[b + 1] = a[6], c[b + 2] = a[5], c[b + 3] = a[4], c[b + 4] = a[3], c[b + 5] = a[2], c[b + 6] = a[1], c[b + 7] = a[0]); + var h = this._u8; + g.IntegerUtilities.f64[0] = a; + a = g.IntegerUtilities.u8; + this._littleEndian ? (h[b + 0] = a[0], h[b + 1] = a[1], h[b + 2] = a[2], h[b + 3] = a[3], h[b + 4] = a[4], h[b + 5] = a[5], h[b + 6] = a[6], h[b + 7] = a[7]) : (h[b + 0] = a[7], h[b + 1] = a[6], h[b + 2] = a[5], h[b + 3] = a[4], h[b + 4] = a[3], h[b + 5] = a[2], h[b + 6] = a[1], h[b + 7] = a[0]); this._position = b += 8; b > this._length && (this._length = b); }; @@ -3348,33 +3503,33 @@ return new Int8Array(this._buffer, 0, this._length); }; l.prototype.writeUTF = function(a) { - a = e(a); + a = c(a); a = k(a); this.writeShort(a.length); this.writeRawBytes(a); }; l.prototype.writeUTFBytes = function(a) { - a = e(a); + a = c(a); a = k(a); this.writeRawBytes(a); }; l.prototype.readUTF = function() { return this.readUTFBytes(this.readShort()); }; - l.prototype.readUTFBytes = function(c) { - c >>>= 0; - var b = this._position; - b + c > this._length && s(); - this._position += c; - return a(new Int8Array(this._buffer, b, c)); + l.prototype.readUTFBytes = function(a) { + a >>>= 0; + var n = this._position; + n + a > this._length && throwError("EOFError", Errors.EOFError); + this._position += a; + return b(new Int8Array(this._buffer, n, a)); }; Object.defineProperty(l.prototype, "length", {get:function() { return this._length; - }, set:function(a) { - a >>>= 0; - a > this._buffer.byteLength && this._ensureCapacity(a); - this._length = a; - this._position = c(this._position, 0, this._length); + }, set:function(b) { + b >>>= 0; + b > this._buffer.byteLength && this._ensureCapacity(b); + this._length = b; + this._position = a(this._position, 0, this._length); }, enumerable:!0, configurable:!0}); Object.defineProperty(l.prototype, "bytesAvailable", {get:function() { return this._length - this._position; @@ -3391,6 +3546,7 @@ return this._u8; }, enumerable:!0, configurable:!0}); Object.defineProperty(l.prototype, "ints", {get:function() { + this._requestViews(2); return this._i32; }, enumerable:!0, configurable:!0}); Object.defineProperty(l.prototype, "objectEncoding", {get:function() { @@ -3401,20 +3557,20 @@ Object.defineProperty(l.prototype, "endian", {get:function() { return this._littleEndian ? "littleEndian" : "bigEndian"; }, set:function(a) { - a = e(a); + a = c(a); this._littleEndian = "auto" === a ? l._nativeLittleEndian : "littleEndian" === a; }, enumerable:!0, configurable:!0}); l.prototype.toString = function() { - return a(new Int8Array(this._buffer, 0, this._length)); + return b(new Int8Array(this._buffer, 0, this._length)); }; - l.prototype.toBlob = function() { - return new Blob([new Int8Array(this._buffer, this._position, this._length)]); + l.prototype.toBlob = function(a) { + return new Blob([new Int8Array(this._buffer, this._position, this._length)], {type:a}); }; l.prototype.writeMultiByte = function(a, b) { - t("packageInternal flash.utils.ObjectOutput::writeMultiByte"); + w("packageInternal flash.utils.ObjectOutput::writeMultiByte"); }; l.prototype.readMultiByte = function(a, b) { - t("packageInternal flash.utils.ObjectInput::readMultiByte"); + w("packageInternal flash.utils.ObjectInput::readMultiByte"); }; l.prototype.getValue = function(a) { a |= 0; @@ -3422,10 +3578,10 @@ }; l.prototype.setValue = function(a, b) { a |= 0; - var c = a + 1; - this._ensureCapacity(c); + var h = a + 1; + this._ensureCapacity(h); this._u8[a] = b; - c > this._length && (this._length = c); + h > this._length && (this._length = h); }; l.prototype.readFixed = function() { return this.readInt() / 65536; @@ -3434,8 +3590,8 @@ return this.readShort() / 256; }; l.prototype.readFloat16 = function() { - var a = this.readUnsignedShort(), b = a >> 15 ? -1 : 1, c = (a & 31744) >> 10, a = a & 1023; - return c ? 31 === c ? a ? NaN : Infinity * b : b * Math.pow(2, c - 15) * (1 + a / 1024) : a / 1024 * Math.pow(2, -14) * b; + var a = this.readUnsignedShort(), b = a >> 15 ? -1 : 1, h = (a & 31744) >> 10, a = a & 1023; + return h ? 31 === h ? a ? NaN : Infinity * b : b * Math.pow(2, h - 15) * (1 + a / 1024) : a / 1024 * Math.pow(2, -14) * b; }; l.prototype.readEncodedU32 = function() { var a = this.readUnsignedByte(); @@ -3457,36 +3613,36 @@ return this.readUnsignedBits(a) << 32 - a >> 32 - a; }; l.prototype.readUnsignedBits = function(a) { - for (var b = this._bitBuffer, c = this._bitLength;a > c;) { - b = b << 8 | this.readUnsignedByte(), c += 8; + for (var b = this._bitBuffer, h = this._bitLength;a > h;) { + b = b << 8 | this.readUnsignedByte(), h += 8; } - c -= a; - a = b >>> c & p[a]; + h -= a; + a = b >>> h & p[a]; this._bitBuffer = b; - this._bitLength = c; + this._bitLength = h; return a; }; l.prototype.readFixedBits = function(a) { return this.readBits(a) / 65536; }; - l.prototype.readString = function(c) { - var b = this._position; - if (c) { - b + c > this._length && s(), this._position += c; + l.prototype.readString = function(a) { + var n = this._position; + if (a) { + n + a > this._length && throwError("EOFError", Errors.EOFError), this._position += a; } else { - c = 0; - for (var g = b;g < this._length && this._u8[g];g++) { - c++; + a = 0; + for (var h = n;h < this._length && this._u8[h];h++) { + a++; } - this._position += c + 1; + this._position += a + 1; } - return a(new Int8Array(this._buffer, b, c)); + return b(new Int8Array(this._buffer, n, a)); }; l.prototype.align = function() { this._bitLength = this._bitBuffer = 0; }; l.prototype._compress = function(a) { - a = e(a); + a = c(a); switch(a) { case "zlib": a = new m.Deflate(!0); @@ -3507,13 +3663,13 @@ this._position = 0; }; l.prototype._uncompress = function(a) { - a = e(a); + a = c(a); switch(a) { case "zlib": - a = new m.Inflate(!0); + a = m.Inflate.create(!0); break; case "deflate": - a = new m.Inflate(!1); + a = m.Inflate.create(!1); break; default: return; @@ -3521,7 +3677,8 @@ var b = new l; a.onData = b.writeRawBytes.bind(b); a.push(this._u8.subarray(0, this._length)); - a.error && t("throwCompressedDataError"); + a.error && throwError("IOError", Errors.CompressedDataError); + a.close(); this._ensureCapacity(b._u8.length); this._u8.set(b._u8); this.length = b.length; @@ -3529,189 +3686,209 @@ }; l._nativeLittleEndian = 1 === (new Int8Array((new Int32Array([1])).buffer))[0]; l.INITIAL_SIZE = 128; - l._arrayBufferPool = new d.ArrayBufferPool; + l._arrayBufferPool = new g.ArrayBufferPool; return l; }(); - m.DataBuffer = v; - })(d.ArrayUtilities || (d.ArrayUtilities = {})); + m.DataBuffer = y; + })(g.ArrayUtilities || (g.ArrayUtilities = {})); })(Shumway || (Shumway = {})); -(function(d) { - var m = d.ArrayUtilities.DataBuffer, s = d.ArrayUtilities.ensureTypedArrayCapacity, e = d.Debug.assert; - (function(a) { - a[a.BeginSolidFill = 1] = "BeginSolidFill"; - a[a.BeginGradientFill = 2] = "BeginGradientFill"; - a[a.BeginBitmapFill = 3] = "BeginBitmapFill"; - a[a.EndFill = 4] = "EndFill"; - a[a.LineStyleSolid = 5] = "LineStyleSolid"; - a[a.LineStyleGradient = 6] = "LineStyleGradient"; - a[a.LineStyleBitmap = 7] = "LineStyleBitmap"; - a[a.LineEnd = 8] = "LineEnd"; - a[a.MoveTo = 9] = "MoveTo"; - a[a.LineTo = 10] = "LineTo"; - a[a.CurveTo = 11] = "CurveTo"; - a[a.CubicCurveTo = 12] = "CubicCurveTo"; - })(d.PathCommand || (d.PathCommand = {})); - (function(a) { - a[a.Linear = 16] = "Linear"; - a[a.Radial = 18] = "Radial"; - })(d.GradientType || (d.GradientType = {})); - (function(a) { - a[a.Pad = 0] = "Pad"; - a[a.Reflect = 1] = "Reflect"; - a[a.Repeat = 2] = "Repeat"; - })(d.GradientSpreadMethod || (d.GradientSpreadMethod = {})); - (function(a) { - a[a.RGB = 0] = "RGB"; - a[a.LinearRGB = 1] = "LinearRGB"; - })(d.GradientInterpolationMethod || (d.GradientInterpolationMethod = {})); - (function(a) { - a[a.None = 0] = "None"; - a[a.Normal = 1] = "Normal"; - a[a.Vertical = 2] = "Vertical"; - a[a.Horizontal = 3] = "Horizontal"; - })(d.LineScaleMode || (d.LineScaleMode = {})); - var t = function() { - return function(a, c, g, p, k, e, l, r, b) { - this.commands = a; - this.commandsPosition = c; - this.coordinates = g; - this.coordinatesPosition = p; - this.morphCoordinates = k; +(function(g) { + var m = g.ArrayUtilities.DataBuffer, e = g.ArrayUtilities.ensureTypedArrayCapacity, c = g.Debug.assert; + (function(b) { + b[b.BeginSolidFill = 1] = "BeginSolidFill"; + b[b.BeginGradientFill = 2] = "BeginGradientFill"; + b[b.BeginBitmapFill = 3] = "BeginBitmapFill"; + b[b.EndFill = 4] = "EndFill"; + b[b.LineStyleSolid = 5] = "LineStyleSolid"; + b[b.LineStyleGradient = 6] = "LineStyleGradient"; + b[b.LineStyleBitmap = 7] = "LineStyleBitmap"; + b[b.LineEnd = 8] = "LineEnd"; + b[b.MoveTo = 9] = "MoveTo"; + b[b.LineTo = 10] = "LineTo"; + b[b.CurveTo = 11] = "CurveTo"; + b[b.CubicCurveTo = 12] = "CubicCurveTo"; + })(g.PathCommand || (g.PathCommand = {})); + (function(b) { + b[b.Linear = 16] = "Linear"; + b[b.Radial = 18] = "Radial"; + })(g.GradientType || (g.GradientType = {})); + (function(b) { + b[b.Pad = 0] = "Pad"; + b[b.Reflect = 1] = "Reflect"; + b[b.Repeat = 2] = "Repeat"; + })(g.GradientSpreadMethod || (g.GradientSpreadMethod = {})); + (function(b) { + b[b.RGB = 0] = "RGB"; + b[b.LinearRGB = 1] = "LinearRGB"; + })(g.GradientInterpolationMethod || (g.GradientInterpolationMethod = {})); + (function(b) { + b[b.None = 0] = "None"; + b[b.Normal = 1] = "Normal"; + b[b.Vertical = 2] = "Vertical"; + b[b.Horizontal = 3] = "Horizontal"; + })(g.LineScaleMode || (g.LineScaleMode = {})); + var w = function() { + return function(b, a, n, c, k, e, l, g, r, m, h) { + this.commands = b; + this.commandsPosition = a; + this.coordinates = n; + this.morphCoordinates = c; + this.coordinatesPosition = k; this.styles = e; this.stylesLength = l; - this.hasFills = r; - this.hasLines = b; + this.morphStyles = g; + this.morphStylesLength = r; + this.hasFills = m; + this.hasLines = h; }; }(); - d.PlainObjectShapeData = t; + g.PlainObjectShapeData = w; var k; - (function(a) { - a[a.Commands = 32] = "Commands"; - a[a.Coordinates = 128] = "Coordinates"; - a[a.Styles = 16] = "Styles"; + (function(b) { + b[b.Commands = 32] = "Commands"; + b[b.Coordinates = 128] = "Coordinates"; + b[b.Styles = 16] = "Styles"; })(k || (k = {})); k = function() { - function a(a) { - "undefined" === typeof a && (a = !0); + function b(a) { + void 0 === a && (a = !0); a && this.clear(); } - a.FromPlainObject = function(c) { - var g = new a(!1); - g.commands = c.commands; - g.coordinates = c.coordinates; - g.morphCoordinates = c.morphCoordinates; - g.commandsPosition = c.commandsPosition; - g.coordinatesPosition = c.coordinatesPosition; - g.styles = m.FromArrayBuffer(c.styles, c.stylesLength); - g.styles.endian = "auto"; - g.hasFills = c.hasFills; - g.hasLines = c.hasLines; - return g; + b.FromPlainObject = function(a) { + var n = new b(!1); + n.commands = a.commands; + n.coordinates = a.coordinates; + n.morphCoordinates = a.morphCoordinates; + n.commandsPosition = a.commandsPosition; + n.coordinatesPosition = a.coordinatesPosition; + n.styles = m.FromArrayBuffer(a.styles, a.stylesLength); + n.styles.endian = "auto"; + a.morphStyles && (n.morphStyles = m.FromArrayBuffer(a.morphStyles, a.morphStylesLength), n.morphStyles.endian = "auto"); + n.hasFills = a.hasFills; + n.hasLines = a.hasLines; + return n; }; - a.prototype.moveTo = function(a, g) { + b.prototype.moveTo = function(a, b) { this.ensurePathCapacities(1, 2); this.commands[this.commandsPosition++] = 9; this.coordinates[this.coordinatesPosition++] = a; - this.coordinates[this.coordinatesPosition++] = g; + this.coordinates[this.coordinatesPosition++] = b; }; - a.prototype.lineTo = function(a, g) { + b.prototype.lineTo = function(a, b) { this.ensurePathCapacities(1, 2); this.commands[this.commandsPosition++] = 10; this.coordinates[this.coordinatesPosition++] = a; - this.coordinates[this.coordinatesPosition++] = g; + this.coordinates[this.coordinatesPosition++] = b; }; - a.prototype.curveTo = function(a, g, p, k) { + b.prototype.curveTo = function(a, b, c, k) { this.ensurePathCapacities(1, 4); this.commands[this.commandsPosition++] = 11; this.coordinates[this.coordinatesPosition++] = a; - this.coordinates[this.coordinatesPosition++] = g; - this.coordinates[this.coordinatesPosition++] = p; + this.coordinates[this.coordinatesPosition++] = b; + this.coordinates[this.coordinatesPosition++] = c; this.coordinates[this.coordinatesPosition++] = k; }; - a.prototype.cubicCurveTo = function(a, g, p, k, e, l) { + b.prototype.cubicCurveTo = function(a, b, c, k, e, l) { this.ensurePathCapacities(1, 6); this.commands[this.commandsPosition++] = 12; this.coordinates[this.coordinatesPosition++] = a; - this.coordinates[this.coordinatesPosition++] = g; - this.coordinates[this.coordinatesPosition++] = p; + this.coordinates[this.coordinatesPosition++] = b; + this.coordinates[this.coordinatesPosition++] = c; this.coordinates[this.coordinatesPosition++] = k; this.coordinates[this.coordinatesPosition++] = e; this.coordinates[this.coordinatesPosition++] = l; }; - a.prototype.beginFill = function(a) { + b.prototype.beginFill = function(a) { this.ensurePathCapacities(1, 0); this.commands[this.commandsPosition++] = 1; this.styles.writeUnsignedInt(a); this.hasFills = !0; }; - a.prototype.endFill = function() { + b.prototype.writeMorphFill = function(a) { + this.morphStyles.writeUnsignedInt(a); + }; + b.prototype.endFill = function() { this.ensurePathCapacities(1, 0); this.commands[this.commandsPosition++] = 4; }; - a.prototype.endLine = function() { + b.prototype.endLine = function() { this.ensurePathCapacities(1, 0); this.commands[this.commandsPosition++] = 8; }; - a.prototype.lineStyle = function(a, g, p, k, d, l, r) { - e(a === (a | 0), 0 <= a && 5100 >= a); + b.prototype.lineStyle = function(a, b, p, k, e, l, g) { + c(a === (a | 0), 0 <= a && 5100 >= a); this.ensurePathCapacities(2, 0); this.commands[this.commandsPosition++] = 5; this.coordinates[this.coordinatesPosition++] = a; a = this.styles; - a.writeUnsignedInt(g); + a.writeUnsignedInt(b); a.writeBoolean(p); a.writeUnsignedByte(k); - a.writeUnsignedByte(d); + a.writeUnsignedByte(e); a.writeUnsignedByte(l); - a.writeUnsignedByte(r); + a.writeUnsignedByte(g); this.hasLines = !0; }; - a.prototype.beginBitmap = function(a, g, p, k, d) { - e(3 === a || 7 === a); + b.prototype.writeMorphLineStyle = function(a, b) { + this.morphCoordinates[this.coordinatesPosition - 1] = a; + this.morphStyles.writeUnsignedInt(b); + }; + b.prototype.beginBitmap = function(a, b, p, k, e) { + c(3 === a || 7 === a); this.ensurePathCapacities(1, 0); this.commands[this.commandsPosition++] = a; a = this.styles; - a.writeUnsignedInt(g); - this._writeStyleMatrix(p); + a.writeUnsignedInt(b); + this._writeStyleMatrix(p, !1); a.writeBoolean(k); - a.writeBoolean(d); + a.writeBoolean(e); this.hasFills = !0; }; - a.prototype.beginGradient = function(a, g, p, k, d, l, r, b) { - e(2 === a || 6 === a); + b.prototype.writeMorphBitmap = function(a) { + this._writeStyleMatrix(a, !0); + }; + b.prototype.beginGradient = function(a, b, p, k, e, l, g, r) { + c(2 === a || 6 === a); this.ensurePathCapacities(1, 0); this.commands[this.commandsPosition++] = a; a = this.styles; a.writeUnsignedByte(k); - e(b === (b | 0)); - a.writeShort(b); - this._writeStyleMatrix(d); - k = g.length; + c(r === (r | 0)); + a.writeShort(r); + this._writeStyleMatrix(e, !1); + k = b.length; a.writeByte(k); - for (d = 0;d < k;d++) { - a.writeUnsignedByte(p[d]), a.writeUnsignedInt(g[d]); + for (e = 0;e < k;e++) { + a.writeUnsignedByte(p[e]), a.writeUnsignedInt(b[e]); } a.writeUnsignedByte(l); - a.writeUnsignedByte(r); + a.writeUnsignedByte(g); this.hasFills = !0; }; - a.prototype.writeCommandAndCoordinates = function(a, g, p) { + b.prototype.writeMorphGradient = function(a, b, c) { + this._writeStyleMatrix(c, !0); + c = this.morphStyles; + for (var k = 0;k < a.length;k++) { + c.writeUnsignedByte(b[k]), c.writeUnsignedInt(a[k]); + } + }; + b.prototype.writeCommandAndCoordinates = function(a, b, c) { this.ensurePathCapacities(1, 2); this.commands[this.commandsPosition++] = a; - this.coordinates[this.coordinatesPosition++] = g; - this.coordinates[this.coordinatesPosition++] = p; + this.coordinates[this.coordinatesPosition++] = b; + this.coordinates[this.coordinatesPosition++] = c; }; - a.prototype.writeCoordinates = function(a, g) { + b.prototype.writeCoordinates = function(a, b) { this.ensurePathCapacities(0, 2); this.coordinates[this.coordinatesPosition++] = a; - this.coordinates[this.coordinatesPosition++] = g; + this.coordinates[this.coordinatesPosition++] = b; }; - a.prototype.writeMorphCoordinates = function(a, g) { - this.morphCoordinates = s(this.morphCoordinates, this.coordinatesPosition); + b.prototype.writeMorphCoordinates = function(a, b) { + this.morphCoordinates = e(this.morphCoordinates, this.coordinatesPosition); this.morphCoordinates[this.coordinatesPosition - 2] = a; - this.morphCoordinates[this.coordinatesPosition - 1] = g; + this.morphCoordinates[this.coordinatesPosition - 1] = b; }; - a.prototype.clear = function() { + b.prototype.clear = function() { this.commandsPosition = this.coordinatesPosition = 0; this.commands = new Uint8Array(32); this.coordinates = new Int32Array(128); @@ -3719,780 +3896,922 @@ this.styles.endian = "auto"; this.hasFills = this.hasLines = !1; }; - a.prototype.isEmpty = function() { + b.prototype.isEmpty = function() { return 0 === this.commandsPosition; }; - a.prototype.clone = function() { - var c = new a(!1); - c.commands = new Uint8Array(this.commands); - c.commandsPosition = this.commandsPosition; - c.coordinates = new Int32Array(this.coordinates); - c.coordinatesPosition = this.coordinatesPosition; - c.styles = new m(this.styles.length); - c.styles.writeRawBytes(this.styles.bytes); - c.hasFills = this.hasFills; - c.hasLines = this.hasLines; - return c; + b.prototype.clone = function() { + var a = new b(!1); + a.commands = new Uint8Array(this.commands); + a.commandsPosition = this.commandsPosition; + a.coordinates = new Int32Array(this.coordinates); + a.coordinatesPosition = this.coordinatesPosition; + a.styles = new m(this.styles.length); + a.styles.writeRawBytes(this.styles.bytes); + this.morphStyles && (a.morphStyles = new m(this.morphStyles.length), a.morphStyles.writeRawBytes(this.morphStyles.bytes)); + a.hasFills = this.hasFills; + a.hasLines = this.hasLines; + return a; }; - a.prototype.toPlainObject = function() { - return new t(this.commands, this.commandsPosition, this.coordinates, this.coordinatesPosition, this.morphCoordinates, this.styles.buffer, this.styles.length, this.hasFills, this.hasLines); + b.prototype.toPlainObject = function() { + return new w(this.commands, this.commandsPosition, this.coordinates, this.morphCoordinates, this.coordinatesPosition, this.styles.buffer, this.styles.length, this.morphStyles && this.morphStyles.buffer, this.morphStyles ? this.morphStyles.length : 0, this.hasFills, this.hasLines); }; - Object.defineProperty(a.prototype, "buffers", {get:function() { + Object.defineProperty(b.prototype, "buffers", {get:function() { var a = [this.commands.buffer, this.coordinates.buffer, this.styles.buffer]; this.morphCoordinates && a.push(this.morphCoordinates.buffer); + this.morphStyles && a.push(this.morphStyles.buffer); return a; }, enumerable:!0, configurable:!0}); - a.prototype._writeStyleMatrix = function(a) { - var g = this.styles; - g.writeFloat(a.a); - g.writeFloat(a.b); - g.writeFloat(a.c); - g.writeFloat(a.d); - g.writeFloat(a.tx); - g.writeFloat(a.ty); - }; - a.prototype.ensurePathCapacities = function(a, g) { - this.commands = s(this.commands, this.commandsPosition + a); - this.coordinates = s(this.coordinates, this.coordinatesPosition + g); + b.prototype._writeStyleMatrix = function(a, b) { + (b ? this.morphStyles : this.styles).write6Floats(a.a, a.b, a.c, a.d, a.tx, a.ty); }; - return a; + b.prototype.ensurePathCapacities = function(a, b) { + this.commands = e(this.commands, this.commandsPosition + a); + this.coordinates = e(this.coordinates, this.coordinatesPosition + b); + }; + return b; }(); - d.ShapeData = k; + g.ShapeData = k; })(Shumway || (Shumway = {})); -(function(d) { - (function(d) { - (function(d) { - (function(e) { - e[e.CODE_END = 0] = "CODE_END"; - e[e.CODE_SHOW_FRAME = 1] = "CODE_SHOW_FRAME"; - e[e.CODE_DEFINE_SHAPE = 2] = "CODE_DEFINE_SHAPE"; - e[e.CODE_FREE_CHARACTER = 3] = "CODE_FREE_CHARACTER"; - e[e.CODE_PLACE_OBJECT = 4] = "CODE_PLACE_OBJECT"; - e[e.CODE_REMOVE_OBJECT = 5] = "CODE_REMOVE_OBJECT"; - e[e.CODE_DEFINE_BITS = 6] = "CODE_DEFINE_BITS"; - e[e.CODE_DEFINE_BUTTON = 7] = "CODE_DEFINE_BUTTON"; - e[e.CODE_JPEG_TABLES = 8] = "CODE_JPEG_TABLES"; - e[e.CODE_SET_BACKGROUND_COLOR = 9] = "CODE_SET_BACKGROUND_COLOR"; - e[e.CODE_DEFINE_FONT = 10] = "CODE_DEFINE_FONT"; - e[e.CODE_DEFINE_TEXT = 11] = "CODE_DEFINE_TEXT"; - e[e.CODE_DO_ACTION = 12] = "CODE_DO_ACTION"; - e[e.CODE_DEFINE_FONT_INFO = 13] = "CODE_DEFINE_FONT_INFO"; - e[e.CODE_DEFINE_SOUND = 14] = "CODE_DEFINE_SOUND"; - e[e.CODE_START_SOUND = 15] = "CODE_START_SOUND"; - e[e.CODE_STOP_SOUND = 16] = "CODE_STOP_SOUND"; - e[e.CODE_DEFINE_BUTTON_SOUND = 17] = "CODE_DEFINE_BUTTON_SOUND"; - e[e.CODE_SOUND_STREAM_HEAD = 18] = "CODE_SOUND_STREAM_HEAD"; - e[e.CODE_SOUND_STREAM_BLOCK = 19] = "CODE_SOUND_STREAM_BLOCK"; - e[e.CODE_DEFINE_BITS_LOSSLESS = 20] = "CODE_DEFINE_BITS_LOSSLESS"; - e[e.CODE_DEFINE_BITS_JPEG2 = 21] = "CODE_DEFINE_BITS_JPEG2"; - e[e.CODE_DEFINE_SHAPE2 = 22] = "CODE_DEFINE_SHAPE2"; - e[e.CODE_DEFINE_BUTTON_CXFORM = 23] = "CODE_DEFINE_BUTTON_CXFORM"; - e[e.CODE_PROTECT = 24] = "CODE_PROTECT"; - e[e.CODE_PATHS_ARE_POSTSCRIPT = 25] = "CODE_PATHS_ARE_POSTSCRIPT"; - e[e.CODE_PLACE_OBJECT2 = 26] = "CODE_PLACE_OBJECT2"; - e[e.CODE_REMOVE_OBJECT2 = 28] = "CODE_REMOVE_OBJECT2"; - e[e.CODE_SYNC_FRAME = 29] = "CODE_SYNC_FRAME"; - e[e.CODE_FREE_ALL = 31] = "CODE_FREE_ALL"; - e[e.CODE_DEFINE_SHAPE3 = 32] = "CODE_DEFINE_SHAPE3"; - e[e.CODE_DEFINE_TEXT2 = 33] = "CODE_DEFINE_TEXT2"; - e[e.CODE_DEFINE_BUTTON2 = 34] = "CODE_DEFINE_BUTTON2"; - e[e.CODE_DEFINE_BITS_JPEG3 = 35] = "CODE_DEFINE_BITS_JPEG3"; - e[e.CODE_DEFINE_BITS_LOSSLESS2 = 36] = "CODE_DEFINE_BITS_LOSSLESS2"; - e[e.CODE_DEFINE_EDIT_TEXT = 37] = "CODE_DEFINE_EDIT_TEXT"; - e[e.CODE_DEFINE_VIDEO = 38] = "CODE_DEFINE_VIDEO"; - e[e.CODE_DEFINE_SPRITE = 39] = "CODE_DEFINE_SPRITE"; - e[e.CODE_NAME_CHARACTER = 40] = "CODE_NAME_CHARACTER"; - e[e.CODE_PRODUCT_INFO = 41] = "CODE_PRODUCT_INFO"; - e[e.CODE_DEFINE_TEXT_FORMAT = 42] = "CODE_DEFINE_TEXT_FORMAT"; - e[e.CODE_FRAME_LABEL = 43] = "CODE_FRAME_LABEL"; - e[e.CODE_DEFINE_BEHAVIOUR = 44] = "CODE_DEFINE_BEHAVIOUR"; - e[e.CODE_SOUND_STREAM_HEAD2 = 45] = "CODE_SOUND_STREAM_HEAD2"; - e[e.CODE_DEFINE_MORPH_SHAPE = 46] = "CODE_DEFINE_MORPH_SHAPE"; - e[e.CODE_FRAME_TAG = 47] = "CODE_FRAME_TAG"; - e[e.CODE_DEFINE_FONT2 = 48] = "CODE_DEFINE_FONT2"; - e[e.CODE_GEN_COMMAND = 49] = "CODE_GEN_COMMAND"; - e[e.CODE_DEFINE_COMMAND_OBJ = 50] = "CODE_DEFINE_COMMAND_OBJ"; - e[e.CODE_CHARACTER_SET = 51] = "CODE_CHARACTER_SET"; - e[e.CODE_FONT_REF = 52] = "CODE_FONT_REF"; - e[e.CODE_DEFINE_FUNCTION = 53] = "CODE_DEFINE_FUNCTION"; - e[e.CODE_PLACE_FUNCTION = 54] = "CODE_PLACE_FUNCTION"; - e[e.CODE_GEN_TAG_OBJECTS = 55] = "CODE_GEN_TAG_OBJECTS"; - e[e.CODE_EXPORT_ASSETS = 56] = "CODE_EXPORT_ASSETS"; - e[e.CODE_IMPORT_ASSETS = 57] = "CODE_IMPORT_ASSETS"; - e[e.CODE_ENABLE_DEBUGGER = 58] = "CODE_ENABLE_DEBUGGER"; - e[e.CODE_DO_INIT_ACTION = 59] = "CODE_DO_INIT_ACTION"; - e[e.CODE_DEFINE_VIDEO_STREAM = 60] = "CODE_DEFINE_VIDEO_STREAM"; - e[e.CODE_VIDEO_FRAME = 61] = "CODE_VIDEO_FRAME"; - e[e.CODE_DEFINE_FONT_INFO2 = 62] = "CODE_DEFINE_FONT_INFO2"; - e[e.CODE_DEBUG_ID = 63] = "CODE_DEBUG_ID"; - e[e.CODE_ENABLE_DEBUGGER2 = 64] = "CODE_ENABLE_DEBUGGER2"; - e[e.CODE_SCRIPT_LIMITS = 65] = "CODE_SCRIPT_LIMITS"; - e[e.CODE_SET_TAB_INDEX = 66] = "CODE_SET_TAB_INDEX"; - e[e.CODE_FILE_ATTRIBUTES = 69] = "CODE_FILE_ATTRIBUTES"; - e[e.CODE_PLACE_OBJECT3 = 70] = "CODE_PLACE_OBJECT3"; - e[e.CODE_IMPORT_ASSETS2 = 71] = "CODE_IMPORT_ASSETS2"; - e[e.CODE_DO_ABC_ = 72] = "CODE_DO_ABC_"; - e[e.CODE_DEFINE_FONT_ALIGN_ZONES = 73] = "CODE_DEFINE_FONT_ALIGN_ZONES"; - e[e.CODE_CSM_TEXT_SETTINGS = 74] = "CODE_CSM_TEXT_SETTINGS"; - e[e.CODE_DEFINE_FONT3 = 75] = "CODE_DEFINE_FONT3"; - e[e.CODE_SYMBOL_CLASS = 76] = "CODE_SYMBOL_CLASS"; - e[e.CODE_METADATA = 77] = "CODE_METADATA"; - e[e.CODE_DEFINE_SCALING_GRID = 78] = "CODE_DEFINE_SCALING_GRID"; - e[e.CODE_DO_ABC = 82] = "CODE_DO_ABC"; - e[e.CODE_DEFINE_SHAPE4 = 83] = "CODE_DEFINE_SHAPE4"; - e[e.CODE_DEFINE_MORPH_SHAPE2 = 84] = "CODE_DEFINE_MORPH_SHAPE2"; - e[e.CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA = 86] = "CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA"; - e[e.CODE_DEFINE_BINARY_DATA = 87] = "CODE_DEFINE_BINARY_DATA"; - e[e.CODE_DEFINE_FONT_NAME = 88] = "CODE_DEFINE_FONT_NAME"; - e[e.CODE_START_SOUND2 = 89] = "CODE_START_SOUND2"; - e[e.CODE_DEFINE_BITS_JPEG4 = 90] = "CODE_DEFINE_BITS_JPEG4"; - e[e.CODE_DEFINE_FONT4 = 91] = "CODE_DEFINE_FONT4"; - })(d.SwfTag || (d.SwfTag = {})); - (function(e) { - e[e.Reserved = 2048] = "Reserved"; - e[e.OpaqueBackground = 1024] = "OpaqueBackground"; - e[e.HasVisible = 512] = "HasVisible"; - e[e.HasImage = 256] = "HasImage"; - e[e.HasClassName = 2048] = "HasClassName"; - e[e.HasCacheAsBitmap = 1024] = "HasCacheAsBitmap"; - e[e.HasBlendMode = 512] = "HasBlendMode"; - e[e.HasFilterList = 256] = "HasFilterList"; - e[e.HasClipActions = 128] = "HasClipActions"; - e[e.HasClipDepth = 64] = "HasClipDepth"; - e[e.HasName = 32] = "HasName"; - e[e.HasRatio = 16] = "HasRatio"; - e[e.HasColorTransform = 8] = "HasColorTransform"; - e[e.HasMatrix = 4] = "HasMatrix"; - e[e.HasCharacter = 2] = "HasCharacter"; - e[e.Move = 1] = "Move"; - })(d.PlaceObjectFlags || (d.PlaceObjectFlags = {})); - })(d.Parser || (d.Parser = {})); - })(d.SWF || (d.SWF = {})); -})(Shumway || (Shumway = {})); -(function(d) { - var m = d.Debug.unexpected, s = function() { - function e(e, k, a, c) { - this.url = e; +(function(g) { + (function(g) { + (function(e) { + (function(c) { + c[c.CODE_END = 0] = "CODE_END"; + c[c.CODE_SHOW_FRAME = 1] = "CODE_SHOW_FRAME"; + c[c.CODE_DEFINE_SHAPE = 2] = "CODE_DEFINE_SHAPE"; + c[c.CODE_FREE_CHARACTER = 3] = "CODE_FREE_CHARACTER"; + c[c.CODE_PLACE_OBJECT = 4] = "CODE_PLACE_OBJECT"; + c[c.CODE_REMOVE_OBJECT = 5] = "CODE_REMOVE_OBJECT"; + c[c.CODE_DEFINE_BITS = 6] = "CODE_DEFINE_BITS"; + c[c.CODE_DEFINE_BUTTON = 7] = "CODE_DEFINE_BUTTON"; + c[c.CODE_JPEG_TABLES = 8] = "CODE_JPEG_TABLES"; + c[c.CODE_SET_BACKGROUND_COLOR = 9] = "CODE_SET_BACKGROUND_COLOR"; + c[c.CODE_DEFINE_FONT = 10] = "CODE_DEFINE_FONT"; + c[c.CODE_DEFINE_TEXT = 11] = "CODE_DEFINE_TEXT"; + c[c.CODE_DO_ACTION = 12] = "CODE_DO_ACTION"; + c[c.CODE_DEFINE_FONT_INFO = 13] = "CODE_DEFINE_FONT_INFO"; + c[c.CODE_DEFINE_SOUND = 14] = "CODE_DEFINE_SOUND"; + c[c.CODE_START_SOUND = 15] = "CODE_START_SOUND"; + c[c.CODE_STOP_SOUND = 16] = "CODE_STOP_SOUND"; + c[c.CODE_DEFINE_BUTTON_SOUND = 17] = "CODE_DEFINE_BUTTON_SOUND"; + c[c.CODE_SOUND_STREAM_HEAD = 18] = "CODE_SOUND_STREAM_HEAD"; + c[c.CODE_SOUND_STREAM_BLOCK = 19] = "CODE_SOUND_STREAM_BLOCK"; + c[c.CODE_DEFINE_BITS_LOSSLESS = 20] = "CODE_DEFINE_BITS_LOSSLESS"; + c[c.CODE_DEFINE_BITS_JPEG2 = 21] = "CODE_DEFINE_BITS_JPEG2"; + c[c.CODE_DEFINE_SHAPE2 = 22] = "CODE_DEFINE_SHAPE2"; + c[c.CODE_DEFINE_BUTTON_CXFORM = 23] = "CODE_DEFINE_BUTTON_CXFORM"; + c[c.CODE_PROTECT = 24] = "CODE_PROTECT"; + c[c.CODE_PATHS_ARE_POSTSCRIPT = 25] = "CODE_PATHS_ARE_POSTSCRIPT"; + c[c.CODE_PLACE_OBJECT2 = 26] = "CODE_PLACE_OBJECT2"; + c[c.CODE_REMOVE_OBJECT2 = 28] = "CODE_REMOVE_OBJECT2"; + c[c.CODE_SYNC_FRAME = 29] = "CODE_SYNC_FRAME"; + c[c.CODE_FREE_ALL = 31] = "CODE_FREE_ALL"; + c[c.CODE_DEFINE_SHAPE3 = 32] = "CODE_DEFINE_SHAPE3"; + c[c.CODE_DEFINE_TEXT2 = 33] = "CODE_DEFINE_TEXT2"; + c[c.CODE_DEFINE_BUTTON2 = 34] = "CODE_DEFINE_BUTTON2"; + c[c.CODE_DEFINE_BITS_JPEG3 = 35] = "CODE_DEFINE_BITS_JPEG3"; + c[c.CODE_DEFINE_BITS_LOSSLESS2 = 36] = "CODE_DEFINE_BITS_LOSSLESS2"; + c[c.CODE_DEFINE_EDIT_TEXT = 37] = "CODE_DEFINE_EDIT_TEXT"; + c[c.CODE_DEFINE_VIDEO = 38] = "CODE_DEFINE_VIDEO"; + c[c.CODE_DEFINE_SPRITE = 39] = "CODE_DEFINE_SPRITE"; + c[c.CODE_NAME_CHARACTER = 40] = "CODE_NAME_CHARACTER"; + c[c.CODE_PRODUCT_INFO = 41] = "CODE_PRODUCT_INFO"; + c[c.CODE_DEFINE_TEXT_FORMAT = 42] = "CODE_DEFINE_TEXT_FORMAT"; + c[c.CODE_FRAME_LABEL = 43] = "CODE_FRAME_LABEL"; + c[c.CODE_DEFINE_BEHAVIOUR = 44] = "CODE_DEFINE_BEHAVIOUR"; + c[c.CODE_SOUND_STREAM_HEAD2 = 45] = "CODE_SOUND_STREAM_HEAD2"; + c[c.CODE_DEFINE_MORPH_SHAPE = 46] = "CODE_DEFINE_MORPH_SHAPE"; + c[c.CODE_GENERATE_FRAME = 47] = "CODE_GENERATE_FRAME"; + c[c.CODE_DEFINE_FONT2 = 48] = "CODE_DEFINE_FONT2"; + c[c.CODE_GEN_COMMAND = 49] = "CODE_GEN_COMMAND"; + c[c.CODE_DEFINE_COMMAND_OBJECT = 50] = "CODE_DEFINE_COMMAND_OBJECT"; + c[c.CODE_CHARACTER_SET = 51] = "CODE_CHARACTER_SET"; + c[c.CODE_EXTERNAL_FONT = 52] = "CODE_EXTERNAL_FONT"; + c[c.CODE_DEFINE_FUNCTION = 53] = "CODE_DEFINE_FUNCTION"; + c[c.CODE_PLACE_FUNCTION = 54] = "CODE_PLACE_FUNCTION"; + c[c.CODE_GEN_TAG_OBJECTS = 55] = "CODE_GEN_TAG_OBJECTS"; + c[c.CODE_EXPORT_ASSETS = 56] = "CODE_EXPORT_ASSETS"; + c[c.CODE_IMPORT_ASSETS = 57] = "CODE_IMPORT_ASSETS"; + c[c.CODE_ENABLE_DEBUGGER = 58] = "CODE_ENABLE_DEBUGGER"; + c[c.CODE_DO_INIT_ACTION = 59] = "CODE_DO_INIT_ACTION"; + c[c.CODE_DEFINE_VIDEO_STREAM = 60] = "CODE_DEFINE_VIDEO_STREAM"; + c[c.CODE_VIDEO_FRAME = 61] = "CODE_VIDEO_FRAME"; + c[c.CODE_DEFINE_FONT_INFO2 = 62] = "CODE_DEFINE_FONT_INFO2"; + c[c.CODE_DEBUG_ID = 63] = "CODE_DEBUG_ID"; + c[c.CODE_ENABLE_DEBUGGER2 = 64] = "CODE_ENABLE_DEBUGGER2"; + c[c.CODE_SCRIPT_LIMITS = 65] = "CODE_SCRIPT_LIMITS"; + c[c.CODE_SET_TAB_INDEX = 66] = "CODE_SET_TAB_INDEX"; + c[c.CODE_FILE_ATTRIBUTES = 69] = "CODE_FILE_ATTRIBUTES"; + c[c.CODE_PLACE_OBJECT3 = 70] = "CODE_PLACE_OBJECT3"; + c[c.CODE_IMPORT_ASSETS2 = 71] = "CODE_IMPORT_ASSETS2"; + c[c.CODE_DO_ABC_DEFINE = 72] = "CODE_DO_ABC_DEFINE"; + c[c.CODE_DEFINE_FONT_ALIGN_ZONES = 73] = "CODE_DEFINE_FONT_ALIGN_ZONES"; + c[c.CODE_CSM_TEXT_SETTINGS = 74] = "CODE_CSM_TEXT_SETTINGS"; + c[c.CODE_DEFINE_FONT3 = 75] = "CODE_DEFINE_FONT3"; + c[c.CODE_SYMBOL_CLASS = 76] = "CODE_SYMBOL_CLASS"; + c[c.CODE_METADATA = 77] = "CODE_METADATA"; + c[c.CODE_DEFINE_SCALING_GRID = 78] = "CODE_DEFINE_SCALING_GRID"; + c[c.CODE_DO_ABC = 82] = "CODE_DO_ABC"; + c[c.CODE_DEFINE_SHAPE4 = 83] = "CODE_DEFINE_SHAPE4"; + c[c.CODE_DEFINE_MORPH_SHAPE2 = 84] = "CODE_DEFINE_MORPH_SHAPE2"; + c[c.CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA = 86] = "CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA"; + c[c.CODE_DEFINE_BINARY_DATA = 87] = "CODE_DEFINE_BINARY_DATA"; + c[c.CODE_DEFINE_FONT_NAME = 88] = "CODE_DEFINE_FONT_NAME"; + c[c.CODE_START_SOUND2 = 89] = "CODE_START_SOUND2"; + c[c.CODE_DEFINE_BITS_JPEG4 = 90] = "CODE_DEFINE_BITS_JPEG4"; + c[c.CODE_DEFINE_FONT4 = 91] = "CODE_DEFINE_FONT4"; + })(e.SwfTag || (e.SwfTag = {})); + (function(c) { + c[c.CODE_DEFINE_SHAPE = 2] = "CODE_DEFINE_SHAPE"; + c[c.CODE_DEFINE_BITS = 6] = "CODE_DEFINE_BITS"; + c[c.CODE_DEFINE_BUTTON = 7] = "CODE_DEFINE_BUTTON"; + c[c.CODE_DEFINE_FONT = 10] = "CODE_DEFINE_FONT"; + c[c.CODE_DEFINE_TEXT = 11] = "CODE_DEFINE_TEXT"; + c[c.CODE_DEFINE_SOUND = 14] = "CODE_DEFINE_SOUND"; + c[c.CODE_DEFINE_BITS_LOSSLESS = 20] = "CODE_DEFINE_BITS_LOSSLESS"; + c[c.CODE_DEFINE_BITS_JPEG2 = 21] = "CODE_DEFINE_BITS_JPEG2"; + c[c.CODE_DEFINE_SHAPE2 = 22] = "CODE_DEFINE_SHAPE2"; + c[c.CODE_DEFINE_SHAPE3 = 32] = "CODE_DEFINE_SHAPE3"; + c[c.CODE_DEFINE_TEXT2 = 33] = "CODE_DEFINE_TEXT2"; + c[c.CODE_DEFINE_BUTTON2 = 34] = "CODE_DEFINE_BUTTON2"; + c[c.CODE_DEFINE_BITS_JPEG3 = 35] = "CODE_DEFINE_BITS_JPEG3"; + c[c.CODE_DEFINE_BITS_LOSSLESS2 = 36] = "CODE_DEFINE_BITS_LOSSLESS2"; + c[c.CODE_DEFINE_EDIT_TEXT = 37] = "CODE_DEFINE_EDIT_TEXT"; + c[c.CODE_DEFINE_SPRITE = 39] = "CODE_DEFINE_SPRITE"; + c[c.CODE_DEFINE_MORPH_SHAPE = 46] = "CODE_DEFINE_MORPH_SHAPE"; + c[c.CODE_DEFINE_FONT2 = 48] = "CODE_DEFINE_FONT2"; + c[c.CODE_DEFINE_VIDEO_STREAM = 60] = "CODE_DEFINE_VIDEO_STREAM"; + c[c.CODE_DEFINE_FONT3 = 75] = "CODE_DEFINE_FONT3"; + c[c.CODE_DEFINE_SHAPE4 = 83] = "CODE_DEFINE_SHAPE4"; + c[c.CODE_DEFINE_MORPH_SHAPE2 = 84] = "CODE_DEFINE_MORPH_SHAPE2"; + c[c.CODE_DEFINE_BINARY_DATA = 87] = "CODE_DEFINE_BINARY_DATA"; + c[c.CODE_DEFINE_BITS_JPEG4 = 90] = "CODE_DEFINE_BITS_JPEG4"; + c[c.CODE_DEFINE_FONT4 = 91] = "CODE_DEFINE_FONT4"; + })(e.DefinitionTags || (e.DefinitionTags = {})); + (function(c) { + c[c.CODE_DEFINE_BITS = 6] = "CODE_DEFINE_BITS"; + c[c.CODE_DEFINE_BITS_JPEG2 = 21] = "CODE_DEFINE_BITS_JPEG2"; + c[c.CODE_DEFINE_BITS_JPEG3 = 35] = "CODE_DEFINE_BITS_JPEG3"; + c[c.CODE_DEFINE_BITS_JPEG4 = 90] = "CODE_DEFINE_BITS_JPEG4"; + })(e.ImageDefinitionTags || (e.ImageDefinitionTags = {})); + (function(c) { + c[c.CODE_DEFINE_FONT = 10] = "CODE_DEFINE_FONT"; + c[c.CODE_DEFINE_FONT2 = 48] = "CODE_DEFINE_FONT2"; + c[c.CODE_DEFINE_FONT3 = 75] = "CODE_DEFINE_FONT3"; + c[c.CODE_DEFINE_FONT4 = 91] = "CODE_DEFINE_FONT4"; + })(e.FontDefinitionTags || (e.FontDefinitionTags = {})); + (function(c) { + c[c.CODE_PLACE_OBJECT = 4] = "CODE_PLACE_OBJECT"; + c[c.CODE_PLACE_OBJECT2 = 26] = "CODE_PLACE_OBJECT2"; + c[c.CODE_PLACE_OBJECT3 = 70] = "CODE_PLACE_OBJECT3"; + c[c.CODE_REMOVE_OBJECT = 5] = "CODE_REMOVE_OBJECT"; + c[c.CODE_REMOVE_OBJECT2 = 28] = "CODE_REMOVE_OBJECT2"; + c[c.CODE_START_SOUND = 15] = "CODE_START_SOUND"; + c[c.CODE_START_SOUND2 = 89] = "CODE_START_SOUND2"; + c[c.CODE_VIDEO_FRAME = 61] = "CODE_VIDEO_FRAME"; + })(e.ControlTags || (e.ControlTags = {})); + (function(c) { + c[c.Move = 1] = "Move"; + c[c.HasCharacter = 2] = "HasCharacter"; + c[c.HasMatrix = 4] = "HasMatrix"; + c[c.HasColorTransform = 8] = "HasColorTransform"; + c[c.HasRatio = 16] = "HasRatio"; + c[c.HasName = 32] = "HasName"; + c[c.HasClipDepth = 64] = "HasClipDepth"; + c[c.HasClipActions = 128] = "HasClipActions"; + c[c.HasFilterList = 256] = "HasFilterList"; + c[c.HasBlendMode = 512] = "HasBlendMode"; + c[c.HasCacheAsBitmap = 1024] = "HasCacheAsBitmap"; + c[c.HasClassName = 2048] = "HasClassName"; + c[c.HasImage = 4096] = "HasImage"; + c[c.HasVisible = 8192] = "HasVisible"; + c[c.OpaqueBackground = 16384] = "OpaqueBackground"; + c[c.Reserved = 32768] = "Reserved"; + })(e.PlaceObjectFlags || (e.PlaceObjectFlags = {})); + (function(c) { + c[c.Load = 1] = "Load"; + c[c.EnterFrame = 2] = "EnterFrame"; + c[c.Unload = 4] = "Unload"; + c[c.MouseMove = 8] = "MouseMove"; + c[c.MouseDown = 16] = "MouseDown"; + c[c.MouseUp = 32] = "MouseUp"; + c[c.KeyDown = 64] = "KeyDown"; + c[c.KeyUp = 128] = "KeyUp"; + c[c.Data = 256] = "Data"; + c[c.Initialize = 512] = "Initialize"; + c[c.Press = 1024] = "Press"; + c[c.Release = 2048] = "Release"; + c[c.ReleaseOutside = 4096] = "ReleaseOutside"; + c[c.RollOver = 8192] = "RollOver"; + c[c.RollOut = 16384] = "RollOut"; + c[c.DragOver = 32768] = "DragOver"; + c[c.DragOut = 65536] = "DragOut"; + c[c.KeyPress = 131072] = "KeyPress"; + c[c.Construct = 262144] = "Construct"; + })(e.AVM1ClipEvents || (e.AVM1ClipEvents = {})); + })(g.Parser || (g.Parser = {})); + })(g.SWF || (g.SWF = {})); +})(Shumway || (Shumway = {})); +(function(g) { + var m = g.Debug.unexpected, e = function() { + function c(c, k, b, a) { + this.url = c; this.method = k; - this.mimeType = a; - this.data = c; + this.mimeType = b; + this.data = a; } - e.prototype.readAll = function(e, k) { - var a = this.url, c = new XMLHttpRequest({mozSystem:!0}); - c.open(this.method || "GET", this.url, !0); - c.responseType = "arraybuffer"; - e && (c.onprogress = function(a) { - e(c.response, a.loaded, a.total); + c.prototype.readAll = function(c, k) { + var b = this.url, a = new XMLHttpRequest({mozSystem:!0}); + a.open(this.method || "GET", this.url, !0); + a.responseType = "arraybuffer"; + c && (a.onprogress = function(b) { + c(a.response, b.loaded, b.total); }); - c.onreadystatechange = function(g) { - 4 === c.readyState && (200 !== c.status && 0 !== c.status || null === c.response ? (m("Path: " + a + " not found."), k(null, c.statusText)) : k(c.response)); + a.onreadystatechange = function(n) { + 4 === a.readyState && (200 !== a.status && 0 !== a.status || null === a.response ? (m("Path: " + b + " not found."), k(null, a.statusText)) : k(a.response)); }; - this.mimeType && c.setRequestHeader("Content-Type", this.mimeType); - c.send(this.data || null); + this.mimeType && a.setRequestHeader("Content-Type", this.mimeType); + a.send(this.data || null); }; - e.prototype.readAsync = function(e, k, a, c, g) { - var p = new XMLHttpRequest({mozSystem:!0}), d = this.url, u = 0, l = 0; - p.open(this.method || "GET", d, !0); + c.prototype.readChunked = function(c, k, b, a, n, p) { + if (0 >= c) { + this.readAsync(k, b, a, n, p); + } else { + var e = 0, v = new Uint8Array(c), l = 0, g; + this.readAsync(function(a, b) { + g = b.total; + for (var h = a.length, f = 0;e + h >= c;) { + var d = c - e; + v.set(a.subarray(f, f + d), e); + f += d; + h -= d; + l += c; + k(v, {loaded:l, total:g}); + e = 0; + } + v.set(a.subarray(f), e); + e += h; + }, b, a, function() { + 0 < e && (l += e, k(v.subarray(0, e), {loaded:l, total:g}), e = 0); + n && n(); + }, p); + } + }; + c.prototype.readAsync = function(c, k, b, a, n) { + var p = new XMLHttpRequest({mozSystem:!0}), e = this.url, g = 0, l = 0; + p.open(this.method || "GET", e, !0); p.responseType = "moz-chunked-arraybuffer"; - var r = "moz-chunked-arraybuffer" !== p.responseType; - r && (p.responseType = "arraybuffer"); - p.onprogress = function(b) { - r || (u = b.loaded, l = b.total, e(new Uint8Array(p.response), {loaded:u, total:l})); + var t = "moz-chunked-arraybuffer" !== p.responseType; + t && (p.responseType = "arraybuffer"); + p.onprogress = function(a) { + t || (g = a.loaded, l = a.total, c(new Uint8Array(p.response), {loaded:g, total:l})); }; p.onreadystatechange = function(b) { - 2 === p.readyState && g && g(d, p.status, p.getAllResponseHeaders()); - 4 === p.readyState && (200 !== p.status && 0 !== p.status || null === p.response && (0 === l || u !== l) ? k(p.statusText) : (r && (b = p.response, e(new Uint8Array(b), {loaded:0, total:b.byteLength})), c && c())); + 2 === p.readyState && n && n(e, p.status, p.getAllResponseHeaders()); + 4 === p.readyState && (200 !== p.status && 0 !== p.status || null === p.response && (0 === l || g !== l) ? k(p.statusText) : (t && (b = p.response, c(new Uint8Array(b), {loaded:0, total:b.byteLength})), a && a())); }; this.mimeType && p.setRequestHeader("Content-Type", this.mimeType); p.send(this.data || null); - a && a(); + b && b(); }; - return e; + return c; }(); - d.BinaryFileReader = s; + g.BinaryFileReader = e; })(Shumway || (Shumway = {})); -(function(d) { - (function(d) { - (function(d) { - d[d.Objects = 0] = "Objects"; - d[d.References = 1] = "References"; - })(d.RemotingPhase || (d.RemotingPhase = {})); - (function(d) { - d[d.HasMatrix = 1] = "HasMatrix"; - d[d.HasBounds = 2] = "HasBounds"; - d[d.HasChildren = 4] = "HasChildren"; - d[d.HasColorTransform = 8] = "HasColorTransform"; - d[d.HasClipRect = 16] = "HasClipRect"; - d[d.HasMiscellaneousProperties = 32] = "HasMiscellaneousProperties"; - d[d.HasMask = 64] = "HasMask"; - d[d.HasClip = 128] = "HasClip"; - })(d.MessageBits || (d.MessageBits = {})); - (function(d) { - d[d.None = 0] = "None"; - d[d.Asset = 134217728] = "Asset"; - })(d.IDMask || (d.IDMask = {})); - (function(d) { - d[d.EOF = 0] = "EOF"; - d[d.UpdateFrame = 100] = "UpdateFrame"; - d[d.UpdateGraphics = 101] = "UpdateGraphics"; - d[d.UpdateBitmapData = 102] = "UpdateBitmapData"; - d[d.UpdateTextContent = 103] = "UpdateTextContent"; - d[d.UpdateStage = 104] = "UpdateStage"; - d[d.UpdateNetStream = 105] = "UpdateNetStream"; - d[d.RequestBitmapData = 106] = "RequestBitmapData"; - d[d.DecodeImage = 107] = "DecodeImage"; - d[d.DecodeImageResponse = 108] = "DecodeImageResponse"; - d[d.RegisterFont = 200] = "RegisterFont"; - d[d.DrawToBitmap = 201] = "DrawToBitmap"; - d[d.MouseEvent = 300] = "MouseEvent"; - d[d.KeyboardEvent = 301] = "KeyboardEvent"; - d[d.FocusEvent = 302] = "FocusEvent"; - })(d.MessageTag || (d.MessageTag = {})); - (function(d) { - d[d.Identity = 0] = "Identity"; - d[d.AlphaMultiplierOnly = 1] = "AlphaMultiplierOnly"; - d[d.All = 2] = "All"; - })(d.ColorTransformEncoding || (d.ColorTransformEncoding = {})); - d.MouseEventNames = ["click", "dblclick", "mousedown", "mousemove", "mouseup"]; - d.KeyboardEventNames = ["keydown", "keypress", "keyup"]; - (function(d) { - d[d.CtrlKey = 1] = "CtrlKey"; - d[d.AltKey = 2] = "AltKey"; - d[d.ShiftKey = 4] = "ShiftKey"; - })(d.KeyboardEventFlags || (d.KeyboardEventFlags = {})); - (function(d) { - d[d.DocumentHidden = 0] = "DocumentHidden"; - d[d.DocumentVisible = 1] = "DocumentVisible"; - d[d.WindowBlur = 2] = "WindowBlur"; - d[d.WindowFocus = 3] = "WindowFocus"; - })(d.FocusEventType || (d.FocusEventType = {})); - })(d.Remoting || (d.Remoting = {})); -})(Shumway || (Shumway = {})); -(function(d) { - (function(d) { - (function(d) { - var e = function() { - function k() { +(function(g) { + (function(g) { + (function(e) { + e[e.Objects = 0] = "Objects"; + e[e.References = 1] = "References"; + })(g.RemotingPhase || (g.RemotingPhase = {})); + (function(e) { + e[e.HasMatrix = 1] = "HasMatrix"; + e[e.HasBounds = 2] = "HasBounds"; + e[e.HasChildren = 4] = "HasChildren"; + e[e.HasColorTransform = 8] = "HasColorTransform"; + e[e.HasClipRect = 16] = "HasClipRect"; + e[e.HasMiscellaneousProperties = 32] = "HasMiscellaneousProperties"; + e[e.HasMask = 64] = "HasMask"; + e[e.HasClip = 128] = "HasClip"; + })(g.MessageBits || (g.MessageBits = {})); + (function(e) { + e[e.None = 0] = "None"; + e[e.Asset = 134217728] = "Asset"; + })(g.IDMask || (g.IDMask = {})); + (function(e) { + e[e.EOF = 0] = "EOF"; + e[e.UpdateFrame = 100] = "UpdateFrame"; + e[e.UpdateGraphics = 101] = "UpdateGraphics"; + e[e.UpdateBitmapData = 102] = "UpdateBitmapData"; + e[e.UpdateTextContent = 103] = "UpdateTextContent"; + e[e.UpdateStage = 104] = "UpdateStage"; + e[e.UpdateNetStream = 105] = "UpdateNetStream"; + e[e.RequestBitmapData = 106] = "RequestBitmapData"; + e[e.DrawToBitmap = 200] = "DrawToBitmap"; + e[e.MouseEvent = 300] = "MouseEvent"; + e[e.KeyboardEvent = 301] = "KeyboardEvent"; + e[e.FocusEvent = 302] = "FocusEvent"; + })(g.MessageTag || (g.MessageTag = {})); + (function(e) { + e[e.Blur = 0] = "Blur"; + e[e.DropShadow = 1] = "DropShadow"; + })(g.FilterType || (g.FilterType = {})); + (function(e) { + e[e.Identity = 0] = "Identity"; + e[e.AlphaMultiplierOnly = 1] = "AlphaMultiplierOnly"; + e[e.All = 2] = "All"; + })(g.ColorTransformEncoding || (g.ColorTransformEncoding = {})); + (function(e) { + e[e.Initialized = 0] = "Initialized"; + e[e.PlayStart = 1] = "PlayStart"; + e[e.PlayStop = 2] = "PlayStop"; + e[e.BufferFull = 3] = "BufferFull"; + e[e.Progress = 4] = "Progress"; + e[e.BufferEmpty = 5] = "BufferEmpty"; + e[e.Error = 6] = "Error"; + e[e.Metadata = 7] = "Metadata"; + e[e.Seeking = 8] = "Seeking"; + })(g.VideoPlaybackEvent || (g.VideoPlaybackEvent = {})); + (function(e) { + e[e.Init = 1] = "Init"; + e[e.Pause = 2] = "Pause"; + e[e.Seek = 3] = "Seek"; + e[e.GetTime = 4] = "GetTime"; + e[e.GetBufferLength = 5] = "GetBufferLength"; + e[e.SetSoundLevels = 6] = "SetSoundLevels"; + e[e.GetBytesLoaded = 7] = "GetBytesLoaded"; + e[e.GetBytesTotal = 8] = "GetBytesTotal"; + })(g.VideoControlEvent || (g.VideoControlEvent = {})); + (function(e) { + e[e.ShowAll = 0] = "ShowAll"; + e[e.ExactFit = 1] = "ExactFit"; + e[e.NoBorder = 2] = "NoBorder"; + e[e.NoScale = 4] = "NoScale"; + })(g.StageScaleMode || (g.StageScaleMode = {})); + (function(e) { + e[e.None = 0] = "None"; + e[e.Top = 1] = "Top"; + e[e.Bottom = 2] = "Bottom"; + e[e.Left = 4] = "Left"; + e[e.Right = 8] = "Right"; + e[e.TopLeft = e.Top | e.Left] = "TopLeft"; + e[e.BottomLeft = e.Bottom | e.Left] = "BottomLeft"; + e[e.BottomRight = e.Bottom | e.Right] = "BottomRight"; + e[e.TopRight = e.Top | e.Right] = "TopRight"; + })(g.StageAlignFlags || (g.StageAlignFlags = {})); + g.MouseEventNames = "click dblclick mousedown mousemove mouseup mouseover mouseout".split(" "); + g.KeyboardEventNames = ["keydown", "keypress", "keyup"]; + (function(e) { + e[e.CtrlKey = 1] = "CtrlKey"; + e[e.AltKey = 2] = "AltKey"; + e[e.ShiftKey = 4] = "ShiftKey"; + })(g.KeyboardEventFlags || (g.KeyboardEventFlags = {})); + (function(e) { + e[e.DocumentHidden = 0] = "DocumentHidden"; + e[e.DocumentVisible = 1] = "DocumentVisible"; + e[e.WindowBlur = 2] = "WindowBlur"; + e[e.WindowFocus = 3] = "WindowFocus"; + })(g.FocusEventType || (g.FocusEventType = {})); + })(g.Remoting || (g.Remoting = {})); +})(Shumway || (Shumway = {})); +var throwError, Errors; +(function(g) { + (function(g) { + (function(e) { + var c = function() { + function c() { } - k.toRGBA = function(a, c, g, p) { - "undefined" === typeof p && (p = 1); - return "rgba(" + a + "," + c + "," + g + "," + p + ")"; + c.toRGBA = function(b, a, n, c) { + void 0 === c && (c = 1); + return "rgba(" + b + "," + a + "," + n + "," + c + ")"; }; - return k; + return c; }(); - d.UI = e; - var m = function() { + e.UI = c; + var g = function() { function k() { } - k.prototype.tabToolbar = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(37, 44, 51, a); - }; - k.prototype.toolbars = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(52, 60, 69, a); - }; - k.prototype.selectionBackground = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(29, 79, 115, a); - }; - k.prototype.selectionText = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(245, 247, 250, a); - }; - k.prototype.splitters = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(0, 0, 0, a); - }; - k.prototype.bodyBackground = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(17, 19, 21, a); - }; - k.prototype.sidebarBackground = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(24, 29, 32, a); - }; - k.prototype.attentionBackground = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(161, 134, 80, a); - }; - k.prototype.bodyText = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(143, 161, 178, a); - }; - k.prototype.foregroundTextGrey = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(182, 186, 191, a); - }; - k.prototype.contentTextHighContrast = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(169, 186, 203, a); - }; - k.prototype.contentTextGrey = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(143, 161, 178, a); - }; - k.prototype.contentTextDarkGrey = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(95, 115, 135, a); - }; - k.prototype.blueHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(70, 175, 227, a); - }; - k.prototype.purpleHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(107, 122, 187, a); - }; - k.prototype.pinkHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(223, 128, 255, a); - }; - k.prototype.redHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(235, 83, 104, a); - }; - k.prototype.orangeHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(217, 102, 41, a); - }; - k.prototype.lightOrangeHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(217, 155, 40, a); - }; - k.prototype.greenHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(112, 191, 83, a); - }; - k.prototype.blueGreyHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(94, 136, 176, a); + k.prototype.tabToolbar = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(37, 44, 51, b); + }; + k.prototype.toolbars = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(52, 60, 69, b); + }; + k.prototype.selectionBackground = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(29, 79, 115, b); + }; + k.prototype.selectionText = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(245, 247, 250, b); + }; + k.prototype.splitters = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(0, 0, 0, b); + }; + k.prototype.bodyBackground = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(17, 19, 21, b); + }; + k.prototype.sidebarBackground = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(24, 29, 32, b); + }; + k.prototype.attentionBackground = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(161, 134, 80, b); + }; + k.prototype.bodyText = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(143, 161, 178, b); + }; + k.prototype.foregroundTextGrey = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(182, 186, 191, b); + }; + k.prototype.contentTextHighContrast = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(169, 186, 203, b); + }; + k.prototype.contentTextGrey = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(143, 161, 178, b); + }; + k.prototype.contentTextDarkGrey = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(95, 115, 135, b); + }; + k.prototype.blueHighlight = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(70, 175, 227, b); + }; + k.prototype.purpleHighlight = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(107, 122, 187, b); + }; + k.prototype.pinkHighlight = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(223, 128, 255, b); + }; + k.prototype.redHighlight = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(235, 83, 104, b); + }; + k.prototype.orangeHighlight = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(217, 102, 41, b); + }; + k.prototype.lightOrangeHighlight = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(217, 155, 40, b); + }; + k.prototype.greenHighlight = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(112, 191, 83, b); + }; + k.prototype.blueGreyHighlight = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(94, 136, 176, b); }; return k; }(); - d.UIThemeDark = m; - m = function() { + e.UIThemeDark = g; + g = function() { function k() { } - k.prototype.tabToolbar = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(235, 236, 237, a); - }; - k.prototype.toolbars = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(240, 241, 242, a); - }; - k.prototype.selectionBackground = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(76, 158, 217, a); - }; - k.prototype.selectionText = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(245, 247, 250, a); - }; - k.prototype.splitters = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(170, 170, 170, a); - }; - k.prototype.bodyBackground = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(252, 252, 252, a); - }; - k.prototype.sidebarBackground = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(247, 247, 247, a); - }; - k.prototype.attentionBackground = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(161, 134, 80, a); - }; - k.prototype.bodyText = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(24, 25, 26, a); - }; - k.prototype.foregroundTextGrey = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(88, 89, 89, a); - }; - k.prototype.contentTextHighContrast = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(41, 46, 51, a); - }; - k.prototype.contentTextGrey = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(143, 161, 178, a); - }; - k.prototype.contentTextDarkGrey = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(102, 115, 128, a); - }; - k.prototype.blueHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(0, 136, 204, a); - }; - k.prototype.purpleHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(91, 95, 255, a); - }; - k.prototype.pinkHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(184, 46, 229, a); - }; - k.prototype.redHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(237, 38, 85, a); - }; - k.prototype.orangeHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(241, 60, 0, a); - }; - k.prototype.lightOrangeHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(217, 126, 0, a); - }; - k.prototype.greenHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(44, 187, 15, a); - }; - k.prototype.blueGreyHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return e.toRGBA(95, 136, 176, a); + k.prototype.tabToolbar = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(235, 236, 237, b); + }; + k.prototype.toolbars = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(240, 241, 242, b); + }; + k.prototype.selectionBackground = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(76, 158, 217, b); + }; + k.prototype.selectionText = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(245, 247, 250, b); + }; + k.prototype.splitters = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(170, 170, 170, b); + }; + k.prototype.bodyBackground = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(252, 252, 252, b); + }; + k.prototype.sidebarBackground = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(247, 247, 247, b); + }; + k.prototype.attentionBackground = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(161, 134, 80, b); + }; + k.prototype.bodyText = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(24, 25, 26, b); + }; + k.prototype.foregroundTextGrey = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(88, 89, 89, b); + }; + k.prototype.contentTextHighContrast = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(41, 46, 51, b); + }; + k.prototype.contentTextGrey = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(143, 161, 178, b); + }; + k.prototype.contentTextDarkGrey = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(102, 115, 128, b); + }; + k.prototype.blueHighlight = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(0, 136, 204, b); + }; + k.prototype.purpleHighlight = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(91, 95, 255, b); + }; + k.prototype.pinkHighlight = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(184, 46, 229, b); + }; + k.prototype.redHighlight = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(237, 38, 85, b); + }; + k.prototype.orangeHighlight = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(241, 60, 0, b); + }; + k.prototype.lightOrangeHighlight = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(217, 126, 0, b); + }; + k.prototype.greenHighlight = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(44, 187, 15, b); + }; + k.prototype.blueGreyHighlight = function(b) { + void 0 === b && (b = 1); + return c.toRGBA(95, 136, 176, b); }; return k; }(); - d.UIThemeLight = m; - })(d.Theme || (d.Theme = {})); - })(d.Tools || (d.Tools = {})); -})(Shumway || (Shumway = {})); -(function(d) { - (function(d) { - (function(d) { - var e = function() { - function d(k) { + e.UIThemeLight = g; + })(g.Theme || (g.Theme = {})); + })(g.Tools || (g.Tools = {})); +})(Shumway || (Shumway = {})); +(function(g) { + (function(g) { + (function(e) { + var c = function() { + function c(k) { this._buffers = k || []; this._snapshots = []; this._maxDepth = 0; } - d.prototype.addBuffer = function(d) { - this._buffers.push(d); + c.prototype.addBuffer = function(c) { + this._buffers.push(c); }; - d.prototype.getSnapshotAt = function(d) { - return this._snapshots[d]; + c.prototype.getSnapshotAt = function(c) { + return this._snapshots[c]; }; - Object.defineProperty(d.prototype, "hasSnapshots", {get:function() { + Object.defineProperty(c.prototype, "hasSnapshots", {get:function() { return 0 < this.snapshotCount; }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "snapshotCount", {get:function() { + Object.defineProperty(c.prototype, "snapshotCount", {get:function() { return this._snapshots.length; }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "startTime", {get:function() { + Object.defineProperty(c.prototype, "startTime", {get:function() { return this._startTime; }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "endTime", {get:function() { + Object.defineProperty(c.prototype, "endTime", {get:function() { return this._endTime; }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "totalTime", {get:function() { + Object.defineProperty(c.prototype, "totalTime", {get:function() { return this.endTime - this.startTime; }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "windowStart", {get:function() { + Object.defineProperty(c.prototype, "windowStart", {get:function() { return this._windowStart; }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "windowEnd", {get:function() { + Object.defineProperty(c.prototype, "windowEnd", {get:function() { return this._windowEnd; }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "windowLength", {get:function() { + Object.defineProperty(c.prototype, "windowLength", {get:function() { return this.windowEnd - this.windowStart; }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "maxDepth", {get:function() { + Object.defineProperty(c.prototype, "maxDepth", {get:function() { return this._maxDepth; }, enumerable:!0, configurable:!0}); - d.prototype.forEachSnapshot = function(d) { - for (var a = 0, c = this.snapshotCount;a < c;a++) { - d(this._snapshots[a], a); + c.prototype.forEachSnapshot = function(c) { + for (var b = 0, a = this.snapshotCount;b < a;b++) { + c(this._snapshots[b], b); } }; - d.prototype.createSnapshots = function() { - var d = Number.MAX_VALUE, a = Number.MIN_VALUE, c = 0; + c.prototype.createSnapshots = function() { + var c = Number.MAX_VALUE, b = Number.MIN_VALUE, a = 0; for (this._snapshots = [];0 < this._buffers.length;) { - var g = this._buffers.shift().createSnapshot(); - g && (d > g.startTime && (d = g.startTime), a < g.endTime && (a = g.endTime), c < g.maxDepth && (c = g.maxDepth), this._snapshots.push(g)); + var n = this._buffers.shift().createSnapshot(); + n && (c > n.startTime && (c = n.startTime), b < n.endTime && (b = n.endTime), a < n.maxDepth && (a = n.maxDepth), this._snapshots.push(n)); } - this._startTime = d; - this._endTime = a; - this._windowStart = d; - this._windowEnd = a; - this._maxDepth = c; - }; - d.prototype.setWindow = function(d, a) { - if (d > a) { - var c = d; - d = a; - a = c; - } - c = Math.min(a - d, this.totalTime); - d < this._startTime ? (d = this._startTime, a = this._startTime + c) : a > this._endTime && (d = this._endTime - c, a = this._endTime); - this._windowStart = d; - this._windowEnd = a; + this._startTime = c; + this._endTime = b; + this._windowStart = c; + this._windowEnd = b; + this._maxDepth = a; + }; + c.prototype.setWindow = function(c, b) { + if (c > b) { + var a = c; + c = b; + b = a; + } + a = Math.min(b - c, this.totalTime); + c < this._startTime ? (c = this._startTime, b = this._startTime + a) : b > this._endTime && (c = this._endTime - a, b = this._endTime); + this._windowStart = c; + this._windowEnd = b; }; - d.prototype.moveWindowTo = function(d) { - this.setWindow(d - this.windowLength / 2, d + this.windowLength / 2); + c.prototype.moveWindowTo = function(c) { + this.setWindow(c - this.windowLength / 2, c + this.windowLength / 2); }; - return d; + return c; }(); - d.Profile = e; - })(d.Profiler || (d.Profiler = {})); - })(d.Tools || (d.Tools = {})); -})(Shumway || (Shumway = {})); -var __extends = this.__extends || function(d, m) { - function s() { - this.constructor = d; + e.Profile = c; + })(g.Profiler || (g.Profiler = {})); + })(g.Tools || (g.Tools = {})); +})(Shumway || (Shumway = {})); +__extends = this.__extends || function(g, m) { + function e() { + this.constructor = g; } - for (var e in m) { - m.hasOwnProperty(e) && (d[e] = m[e]); + for (var c in m) { + m.hasOwnProperty(c) && (g[c] = m[c]); } - s.prototype = m.prototype; - d.prototype = new s; + e.prototype = m.prototype; + g.prototype = new e; }; -(function(d) { - (function(d) { - (function(d) { - var e = function() { - return function(d) { - this.kind = d; +(function(g) { + (function(g) { + (function(e) { + var c = function() { + return function(c) { + this.kind = c; this.totalTime = this.selfTime = this.count = 0; }; }(); - d.TimelineFrameStatistics = e; - var m = function() { - function d(a, c, g, p, e, k) { - this.parent = a; - this.kind = c; - this.startData = g; + e.TimelineFrameStatistics = c; + var g = function() { + function k(b, a, c, p, k, e) { + this.parent = b; + this.kind = a; + this.startData = c; this.endData = p; - this.startTime = e; - this.endTime = k; + this.startTime = k; + this.endTime = e; this.maxDepth = 0; } - Object.defineProperty(d.prototype, "totalTime", {get:function() { + Object.defineProperty(k.prototype, "totalTime", {get:function() { return this.endTime - this.startTime; }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "selfTime", {get:function() { - var a = this.totalTime; + Object.defineProperty(k.prototype, "selfTime", {get:function() { + var b = this.totalTime; if (this.children) { - for (var c = 0, g = this.children.length;c < g;c++) { - var p = this.children[c], a = a - (p.endTime - p.startTime) + for (var a = 0, c = this.children.length;a < c;a++) { + var p = this.children[a], b = b - (p.endTime - p.startTime) } } - return a; + return b; }, enumerable:!0, configurable:!0}); - d.prototype.getChildIndex = function(a) { - for (var c = this.children, g = 0;g < c.length;g++) { - if (c[g].endTime > a) { - return g; + k.prototype.getChildIndex = function(b) { + for (var a = this.children, c = 0;c < a.length;c++) { + if (a[c].endTime > b) { + return c; } } return 0; }; - d.prototype.getChildRange = function(a, c) { - if (this.children && a <= this.endTime && c >= this.startTime && c >= a) { - var g = this._getNearestChild(a), p = this._getNearestChildReverse(c); - if (g <= p) { - return a = this.children[g].startTime, c = this.children[p].endTime, {startIndex:g, endIndex:p, startTime:a, endTime:c, totalTime:c - a}; + k.prototype.getChildRange = function(b, a) { + if (this.children && b <= this.endTime && a >= this.startTime && a >= b) { + var c = this._getNearestChild(b), p = this._getNearestChildReverse(a); + if (c <= p) { + return b = this.children[c].startTime, a = this.children[p].endTime, {startIndex:c, endIndex:p, startTime:b, endTime:a, totalTime:a - b}; } } return null; }; - d.prototype._getNearestChild = function(a) { - var c = this.children; - if (c && c.length) { - if (a <= c[0].endTime) { + k.prototype._getNearestChild = function(b) { + var a = this.children; + if (a && a.length) { + if (b <= a[0].endTime) { return 0; } - for (var g, p = 0, d = c.length - 1;d > p;) { - g = (p + d) / 2 | 0; - var e = c[g]; - if (a >= e.startTime && a <= e.endTime) { - return g; + for (var c, p = 0, k = a.length - 1;k > p;) { + c = (p + k) / 2 | 0; + var e = a[c]; + if (b >= e.startTime && b <= e.endTime) { + return c; } - a > e.endTime ? p = g + 1 : d = g; + b > e.endTime ? p = c + 1 : k = c; } - return Math.ceil((p + d) / 2); + return Math.ceil((p + k) / 2); } return 0; }; - d.prototype._getNearestChildReverse = function(a) { - var c = this.children; - if (c && c.length) { - var g = c.length - 1; - if (a >= c[g].startTime) { - return g; - } - for (var p, d = 0;g > d;) { - p = Math.ceil((d + g) / 2); - var e = c[p]; - if (a >= e.startTime && a <= e.endTime) { + k.prototype._getNearestChildReverse = function(b) { + var a = this.children; + if (a && a.length) { + var c = a.length - 1; + if (b >= a[c].startTime) { + return c; + } + for (var p, k = 0;c > k;) { + p = Math.ceil((k + c) / 2); + var e = a[p]; + if (b >= e.startTime && b <= e.endTime) { return p; } - a > e.endTime ? d = p : g = p - 1; + b > e.endTime ? k = p : c = p - 1; } - return(d + g) / 2 | 0; + return(k + c) / 2 | 0; } return 0; }; - d.prototype.query = function(a) { - if (a < this.startTime || a > this.endTime) { + k.prototype.query = function(b) { + if (b < this.startTime || b > this.endTime) { return null; } - var c = this.children; - if (c && 0 < c.length) { - for (var g, p = 0, d = c.length - 1;d > p;) { - var e = (p + d) / 2 | 0; - g = c[e]; - if (a >= g.startTime && a <= g.endTime) { - return g.query(a); - } - a > g.endTime ? p = e + 1 : d = e; - } - g = c[d]; - if (a >= g.startTime && a <= g.endTime) { - return g.query(a); + var a = this.children; + if (a && 0 < a.length) { + for (var c, p = 0, k = a.length - 1;k > p;) { + var e = (p + k) / 2 | 0; + c = a[e]; + if (b >= c.startTime && b <= c.endTime) { + return c.query(b); + } + b > c.endTime ? p = e + 1 : k = e; + } + c = a[k]; + if (b >= c.startTime && b <= c.endTime) { + return c.query(b); } } return this; }; - d.prototype.queryNext = function(a) { - for (var c = this;a > c.endTime;) { - if (c.parent) { - c = c.parent; + k.prototype.queryNext = function(b) { + for (var a = this;b > a.endTime;) { + if (a.parent) { + a = a.parent; } else { break; } } - return c.query(a); + return a.query(b); }; - d.prototype.getDepth = function() { - for (var a = 0, c = this;c;) { - a++, c = c.parent; + k.prototype.getDepth = function() { + for (var b = 0, a = this;a;) { + b++, a = a.parent; } - return a; + return b; }; - d.prototype.calculateStatistics = function() { - function a(g) { - if (g.kind) { - var p = c[g.kind.id] || (c[g.kind.id] = new e(g.kind)); + k.prototype.calculateStatistics = function() { + function b(n) { + if (n.kind) { + var p = a[n.kind.id] || (a[n.kind.id] = new c(n.kind)); p.count++; - p.selfTime += g.selfTime; - p.totalTime += g.totalTime; + p.selfTime += n.selfTime; + p.totalTime += n.totalTime; } - g.children && g.children.forEach(a); + n.children && n.children.forEach(b); } - var c = this.statistics = []; - a(this); + var a = this.statistics = []; + b(this); }; - return d; + k.prototype.trace = function(b) { + var a = (this.kind ? this.kind.name + ": " : "Profile: ") + (this.endTime - this.startTime).toFixed(2); + if (this.children && this.children.length) { + b.enter(a); + for (a = 0;a < this.children.length;a++) { + this.children[a].trace(b); + } + b.outdent(); + } else { + b.writeLn(a); + } + }; + return k; }(); - d.TimelineFrame = m; - m = function(d) { - function a(a) { - d.call(this, null, null, null, null, NaN, NaN); + e.TimelineFrame = g; + g = function(c) { + function b(a) { + c.call(this, null, null, null, null, NaN, NaN); this.name = a; } - __extends(a, d); - return a; - }(m); - d.TimelineBufferSnapshot = m; - })(d.Profiler || (d.Profiler = {})); - })(d.Tools || (d.Tools = {})); + __extends(b, c); + return b; + }(g); + e.TimelineBufferSnapshot = g; + })(g.Profiler || (g.Profiler = {})); + })(g.Tools || (g.Tools = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - (function(m) { - var e = d.ObjectUtilities.createEmptyObject, t = function() { - function k(a, c) { - "undefined" === typeof a && (a = ""); - this.name = a || ""; - this._startTime = d.isNullOrUndefined(c) ? performance.now() : c; + (function(e) { + var c = function() { + function c(k, b) { + void 0 === k && (k = ""); + this.name = k || ""; + this._startTime = g.isNullOrUndefined(b) ? jsGlobal.START_TIME : b; } - k.prototype.getKind = function(a) { - return this._kinds[a]; + c.prototype.getKind = function(c) { + return this._kinds[c]; }; - Object.defineProperty(k.prototype, "kinds", {get:function() { + Object.defineProperty(c.prototype, "kinds", {get:function() { return this._kinds.concat(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(k.prototype, "depth", {get:function() { + Object.defineProperty(c.prototype, "depth", {get:function() { return this._depth; }, enumerable:!0, configurable:!0}); - k.prototype._initialize = function() { + c.prototype._initialize = function() { this._depth = 0; this._stack = []; this._data = []; this._kinds = []; - this._kindNameMap = e(); - this._marks = new d.CircularBuffer(Int32Array, 20); - this._times = new d.CircularBuffer(Float64Array, 20); - }; - k.prototype._getKindId = function(a) { - var c = k.MAX_KINDID; - if (void 0 === this._kindNameMap[a]) { - if (c = this._kinds.length, c < k.MAX_KINDID) { - var g = {id:c, name:a, visible:!0}; - this._kinds.push(g); - this._kindNameMap[a] = g; + this._kindNameMap = Object.create(null); + this._marks = new g.CircularBuffer(Int32Array, 20); + this._times = new g.CircularBuffer(Float64Array, 20); + }; + c.prototype._getKindId = function(k) { + var b = c.MAX_KINDID; + if (void 0 === this._kindNameMap[k]) { + if (b = this._kinds.length, b < c.MAX_KINDID) { + var a = {id:b, name:k, visible:!0}; + this._kinds.push(a); + this._kindNameMap[k] = a; } else { - c = k.MAX_KINDID; + b = c.MAX_KINDID; } } else { - c = this._kindNameMap[a].id; + b = this._kindNameMap[k].id; } - return c; + return b; }; - k.prototype._getMark = function(a, c, g) { - var p = k.MAX_DATAID; - d.isNullOrUndefined(g) || c === k.MAX_KINDID || (p = this._data.length, p < k.MAX_DATAID ? this._data.push(g) : p = k.MAX_DATAID); - return a | p << 16 | c; + c.prototype._getMark = function(k, b, a) { + var n = c.MAX_DATAID; + g.isNullOrUndefined(a) || b === c.MAX_KINDID || (n = this._data.length, n < c.MAX_DATAID ? this._data.push(a) : n = c.MAX_DATAID); + return k | n << 16 | b; }; - k.prototype.enter = function(a, c, g) { - g = (d.isNullOrUndefined(g) ? performance.now() : g) - this._startTime; + c.prototype.enter = function(k, b, a) { + a = (g.isNullOrUndefined(a) ? performance.now() : a) - this._startTime; this._marks || this._initialize(); this._depth++; - a = this._getKindId(a); - this._marks.write(this._getMark(k.ENTER, a, c)); - this._times.write(g); - this._stack.push(a); - }; - k.prototype.leave = function(a, c, g) { - g = (d.isNullOrUndefined(g) ? performance.now() : g) - this._startTime; - var p = this._stack.pop(); - a && (p = this._getKindId(a)); - this._marks.write(this._getMark(k.LEAVE, p, c)); - this._times.write(g); + k = this._getKindId(k); + this._marks.write(this._getMark(c.ENTER, k, b)); + this._times.write(a); + this._stack.push(k); + }; + c.prototype.leave = function(k, b, a) { + a = (g.isNullOrUndefined(a) ? performance.now() : a) - this._startTime; + var n = this._stack.pop(); + k && (n = this._getKindId(k)); + this._marks.write(this._getMark(c.LEAVE, n, b)); + this._times.write(a); this._depth--; }; - k.prototype.count = function(a, c, g) { + c.prototype.count = function(c, b, a) { }; - k.prototype.createSnapshot = function() { - var a; - "undefined" === typeof a && (a = Number.MAX_VALUE); + c.prototype.createSnapshot = function() { + var k; + void 0 === k && (k = Number.MAX_VALUE); if (!this._marks) { return null; } - var c = this._times, g = this._kinds, p = this._data, e = new m.TimelineBufferSnapshot(this.name), u = [e], l = 0; + var b = this._times, a = this._kinds, n = this._data, p = new e.TimelineBufferSnapshot(this.name), y = [p], v = 0; this._marks || this._initialize(); - this._marks.forEachInReverse(function(e, b) { - var h = p[e >>> 16 & k.MAX_DATAID], v = g[e & k.MAX_KINDID]; - if (d.isNullOrUndefined(v) || v.visible) { - var n = e & 2147483648, f = c.get(b), q = u.length; - if (n === k.LEAVE) { - if (1 === q && (l++, l > a)) { + this._marks.forEachInReverse(function(p, t) { + var r = n[p >>> 16 & c.MAX_DATAID], u = a[p & c.MAX_KINDID]; + if (g.isNullOrUndefined(u) || u.visible) { + var h = p & 2147483648, f = b.get(t), d = y.length; + if (h === c.LEAVE) { + if (1 === d && (v++, v > k)) { return!0; } - u.push(new m.TimelineFrame(u[q - 1], v, null, h, NaN, f)); + y.push(new e.TimelineFrame(y[d - 1], u, null, r, NaN, f)); } else { - if (n === k.ENTER) { - if (v = u.pop(), n = u[u.length - 1]) { - for (n.children ? n.children.unshift(v) : n.children = [v], n = u.length, v.depth = n, v.startData = h, v.startTime = f;v;) { - if (v.maxDepth < n) { - v.maxDepth = n, v = v.parent; + if (h === c.ENTER) { + if (u = y.pop(), h = y[y.length - 1]) { + for (h.children ? h.children.unshift(u) : h.children = [u], h = y.length, u.depth = h, u.startData = r, u.startTime = f;u;) { + if (u.maxDepth < h) { + u.maxDepth = h, u = u.parent; } else { break; } @@ -4504,133 +4823,133 @@ } } }); - e.children && e.children.length && (e.startTime = e.children[0].startTime, e.endTime = e.children[e.children.length - 1].endTime); - return e; + p.children && p.children.length && (p.startTime = p.children[0].startTime, p.endTime = p.children[p.children.length - 1].endTime); + return p; }; - k.prototype.reset = function(a) { - this._startTime = d.isNullOrUndefined(a) ? performance.now() : a; + c.prototype.reset = function(c) { + this._startTime = g.isNullOrUndefined(c) ? performance.now() : c; this._marks ? (this._depth = 0, this._data = [], this._marks.reset(), this._times.reset()) : this._initialize(); }; - k.FromFirefoxProfile = function(a, c) { - for (var g = a.profile.threads[0].samples, p = new k(c, g[0].time), d = [], e, l = 0;l < g.length;l++) { - e = g[l]; - var r = e.time, b = e.frames, h = 0; - for (e = Math.min(b.length, d.length);h < e && b[h].location === d[h].location;) { - h++; - } - for (var w = d.length - h, n = 0;n < w;n++) { - e = d.pop(), p.leave(e.location, null, r); + c.FromFirefoxProfile = function(k, b) { + for (var a = k.profile.threads[0].samples, n = new c(b, a[0].time), p = [], e, g = 0;g < a.length;g++) { + e = a[g]; + var l = e.time, t = e.frames, r = 0; + for (e = Math.min(t.length, p.length);r < e && t[r].location === p[r].location;) { + r++; + } + for (var u = p.length - r, h = 0;h < u;h++) { + e = p.pop(), n.leave(e.location, null, l); } - for (;h < b.length;) { - e = b[h++], p.enter(e.location, null, r); + for (;r < t.length;) { + e = t[r++], n.enter(e.location, null, l); } - d = b; + p = t; } - for (;e = d.pop();) { - p.leave(e.location, null, r); + for (;e = p.pop();) { + n.leave(e.location, null, l); } - return p; + return n; }; - k.FromChromeProfile = function(a, c) { - var g = a.timestamps, p = a.samples, d = new k(c, g[0] / 1E3), e = [], l = {}, r; - k._resolveIds(a.head, l); - for (var b = 0;b < g.length;b++) { - var h = g[b] / 1E3, w = []; - for (r = l[p[b]];r;) { - w.unshift(r), r = r.parent; + c.FromChromeProfile = function(k, b) { + var a = k.timestamps, n = k.samples, p = new c(b, a[0] / 1E3), e = [], g = {}, l; + c._resolveIds(k.head, g); + for (var t = 0;t < a.length;t++) { + var r = a[t] / 1E3, u = []; + for (l = g[n[t]];l;) { + u.unshift(l), l = l.parent; } - var n = 0; - for (r = Math.min(w.length, e.length);n < r && w[n] === e[n];) { - n++; + var h = 0; + for (l = Math.min(u.length, e.length);h < l && u[h] === e[h];) { + h++; } - for (var f = e.length - n, q = 0;q < f;q++) { - r = e.pop(), d.leave(r.functionName, null, h); + for (var f = e.length - h, d = 0;d < f;d++) { + l = e.pop(), p.leave(l.functionName, null, r); } - for (;n < w.length;) { - r = w[n++], d.enter(r.functionName, null, h); + for (;h < u.length;) { + l = u[h++], p.enter(l.functionName, null, r); } - e = w; + e = u; } - for (;r = e.pop();) { - d.leave(r.functionName, null, h); + for (;l = e.pop();) { + p.leave(l.functionName, null, r); } - return d; + return p; }; - k._resolveIds = function(a, c) { - c[a.id] = a; - if (a.children) { - for (var g = 0;g < a.children.length;g++) { - a.children[g].parent = a, k._resolveIds(a.children[g], c); + c._resolveIds = function(k, b) { + b[k.id] = k; + if (k.children) { + for (var a = 0;a < k.children.length;a++) { + k.children[a].parent = k, c._resolveIds(k.children[a], b); } } }; - k.ENTER = 0; - k.LEAVE = -2147483648; - k.MAX_KINDID = 65535; - k.MAX_DATAID = 32767; - return k; + c.ENTER = 0; + c.LEAVE = -2147483648; + c.MAX_KINDID = 65535; + c.MAX_DATAID = 32767; + return c; }(); - m.TimelineBuffer = t; + e.TimelineBuffer = c; })(m.Profiler || (m.Profiler = {})); - })(d.Tools || (d.Tools = {})); + })(g.Tools || (g.Tools = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - (function(s) { - (function(d) { - d[d.DARK = 0] = "DARK"; - d[d.LIGHT = 1] = "LIGHT"; - })(s.UIThemeType || (s.UIThemeType = {})); - var e = function() { - function e(d, a) { - "undefined" === typeof a && (a = 0); - this._container = d; + (function(e) { + (function(c) { + c[c.DARK = 0] = "DARK"; + c[c.LIGHT = 1] = "LIGHT"; + })(e.UIThemeType || (e.UIThemeType = {})); + var c = function() { + function c(k, b) { + void 0 === b && (b = 0); + this._container = k; this._headers = []; this._charts = []; this._profiles = []; this._activeProfile = null; - this.themeType = a; + this.themeType = b; this._tooltip = this._createTooltip(); } - e.prototype.createProfile = function(d, a) { - "undefined" === typeof a && (a = !0); - var c = new s.Profile(d); - c.createSnapshots(); - this._profiles.push(c); - a && this.activateProfile(c); - return c; + c.prototype.createProfile = function(c, b) { + void 0 === b && (b = !0); + var a = new e.Profile(c); + a.createSnapshots(); + this._profiles.push(a); + b && this.activateProfile(a); + return a; }; - e.prototype.activateProfile = function(d) { + c.prototype.activateProfile = function(c) { this.deactivateProfile(); - this._activeProfile = d; + this._activeProfile = c; this._createViews(); this._initializeViews(); }; - e.prototype.activateProfileAt = function(d) { - this.activateProfile(this.getProfileAt(d)); + c.prototype.activateProfileAt = function(c) { + this.activateProfile(this.getProfileAt(c)); }; - e.prototype.deactivateProfile = function() { + c.prototype.deactivateProfile = function() { this._activeProfile && (this._destroyViews(), this._activeProfile = null); }; - e.prototype.resize = function() { + c.prototype.resize = function() { this._onResize(); }; - e.prototype.getProfileAt = function(d) { - return this._profiles[d]; + c.prototype.getProfileAt = function(c) { + return this._profiles[c]; }; - Object.defineProperty(e.prototype, "activeProfile", {get:function() { + Object.defineProperty(c.prototype, "activeProfile", {get:function() { return this._activeProfile; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "profileCount", {get:function() { + Object.defineProperty(c.prototype, "profileCount", {get:function() { return this._profiles.length; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "container", {get:function() { + Object.defineProperty(c.prototype, "container", {get:function() { return this._container; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "themeType", {get:function() { + Object.defineProperty(c.prototype, "themeType", {get:function() { return this._themeType; - }, set:function(d) { - switch(d) { + }, set:function(c) { + switch(c) { case 0: this._theme = new m.Theme.UIThemeDark; break; @@ -4638,25 +4957,25 @@ this._theme = new m.Theme.UIThemeLight; } }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "theme", {get:function() { + Object.defineProperty(c.prototype, "theme", {get:function() { return this._theme; }, enumerable:!0, configurable:!0}); - e.prototype.getSnapshotAt = function(d) { - return this._activeProfile.getSnapshotAt(d); + c.prototype.getSnapshotAt = function(c) { + return this._activeProfile.getSnapshotAt(c); }; - e.prototype._createViews = function() { + c.prototype._createViews = function() { if (this._activeProfile) { - var d = this; - this._overviewHeader = new s.FlameChartHeader(this, 0); - this._overview = new s.FlameChartOverview(this, 0); - this._activeProfile.forEachSnapshot(function(a, c) { - d._headers.push(new s.FlameChartHeader(d, 1)); - d._charts.push(new s.FlameChart(d, a)); + var c = this; + this._overviewHeader = new e.FlameChartHeader(this, 0); + this._overview = new e.FlameChartOverview(this, 0); + this._activeProfile.forEachSnapshot(function(b, a) { + c._headers.push(new e.FlameChartHeader(c, 1)); + c._charts.push(new e.FlameChart(c, b)); }); window.addEventListener("resize", this._onResize.bind(this)); } }; - e.prototype._destroyViews = function() { + c.prototype._destroyViews = function() { if (this._activeProfile) { this._overviewHeader.destroy(); for (this._overview.destroy();this._headers.length;) { @@ -4668,163 +4987,163 @@ window.removeEventListener("resize", this._onResize.bind(this)); } }; - e.prototype._initializeViews = function() { + c.prototype._initializeViews = function() { if (this._activeProfile) { - var d = this, a = this._activeProfile.startTime, c = this._activeProfile.endTime; - this._overviewHeader.initialize(a, c); - this._overview.initialize(a, c); - this._activeProfile.forEachSnapshot(function(g, p) { - d._headers[p].initialize(a, c); - d._charts[p].initialize(a, c); + var c = this, b = this._activeProfile.startTime, a = this._activeProfile.endTime; + this._overviewHeader.initialize(b, a); + this._overview.initialize(b, a); + this._activeProfile.forEachSnapshot(function(n, p) { + c._headers[p].initialize(b, a); + c._charts[p].initialize(b, a); }); } }; - e.prototype._onResize = function() { + c.prototype._onResize = function() { if (this._activeProfile) { - var d = this, a = this._container.offsetWidth; - this._overviewHeader.setSize(a); - this._overview.setSize(a); - this._activeProfile.forEachSnapshot(function(c, g) { - d._headers[g].setSize(a); - d._charts[g].setSize(a); + var c = this, b = this._container.offsetWidth; + this._overviewHeader.setSize(b); + this._overview.setSize(b); + this._activeProfile.forEachSnapshot(function(a, n) { + c._headers[n].setSize(b); + c._charts[n].setSize(b); }); } }; - e.prototype._updateViews = function() { + c.prototype._updateViews = function() { if (this._activeProfile) { - var d = this, a = this._activeProfile.windowStart, c = this._activeProfile.windowEnd; - this._overviewHeader.setWindow(a, c); - this._overview.setWindow(a, c); - this._activeProfile.forEachSnapshot(function(g, p) { - d._headers[p].setWindow(a, c); - d._charts[p].setWindow(a, c); + var c = this, b = this._activeProfile.windowStart, a = this._activeProfile.windowEnd; + this._overviewHeader.setWindow(b, a); + this._overview.setWindow(b, a); + this._activeProfile.forEachSnapshot(function(n, p) { + c._headers[p].setWindow(b, a); + c._charts[p].setWindow(b, a); }); } }; - e.prototype._drawViews = function() { + c.prototype._drawViews = function() { }; - e.prototype._createTooltip = function() { - var d = document.createElement("div"); - d.classList.add("profiler-tooltip"); - d.style.display = "none"; - this._container.insertBefore(d, this._container.firstChild); - return d; + c.prototype._createTooltip = function() { + var c = document.createElement("div"); + c.classList.add("profiler-tooltip"); + c.style.display = "none"; + this._container.insertBefore(c, this._container.firstChild); + return c; }; - e.prototype.setWindow = function(d, a) { - this._activeProfile.setWindow(d, a); + c.prototype.setWindow = function(c, b) { + this._activeProfile.setWindow(c, b); this._updateViews(); }; - e.prototype.moveWindowTo = function(d) { - this._activeProfile.moveWindowTo(d); + c.prototype.moveWindowTo = function(c) { + this._activeProfile.moveWindowTo(c); this._updateViews(); }; - e.prototype.showTooltip = function(d, a, c, g) { + c.prototype.showTooltip = function(c, b, a, n) { this.removeTooltipContent(); - this._tooltip.appendChild(this.createTooltipContent(d, a)); + this._tooltip.appendChild(this.createTooltipContent(c, b)); this._tooltip.style.display = "block"; var p = this._tooltip.firstChild; - a = p.clientWidth; + b = p.clientWidth; p = p.clientHeight; - c += c + a >= d.canvas.clientWidth - 50 ? -(a + 20) : 25; - g += d.canvas.offsetTop - p / 2; - this._tooltip.style.left = c + "px"; - this._tooltip.style.top = g + "px"; + a += a + b >= c.canvas.clientWidth - 50 ? -(b + 20) : 25; + n += c.canvas.offsetTop - p / 2; + this._tooltip.style.left = a + "px"; + this._tooltip.style.top = n + "px"; }; - e.prototype.hideTooltip = function() { + c.prototype.hideTooltip = function() { this._tooltip.style.display = "none"; }; - e.prototype.createTooltipContent = function(d, a) { - var c = Math.round(1E5 * a.totalTime) / 1E5, g = Math.round(1E5 * a.selfTime) / 1E5, p = Math.round(1E4 * a.selfTime / a.totalTime) / 100, e = document.createElement("div"), u = document.createElement("h1"); - u.textContent = a.kind.name; - e.appendChild(u); - u = document.createElement("p"); - u.textContent = "Total: " + c + " ms"; - e.appendChild(u); - c = document.createElement("p"); - c.textContent = "Self: " + g + " ms (" + p + "%)"; - e.appendChild(c); - if (g = d.getStatistics(a.kind)) { - p = document.createElement("p"), p.textContent = "Count: " + g.count, e.appendChild(p), p = Math.round(1E5 * g.totalTime) / 1E5, c = document.createElement("p"), c.textContent = "All Total: " + p + " ms", e.appendChild(c), g = Math.round(1E5 * g.selfTime) / 1E5, p = document.createElement("p"), p.textContent = "All Self: " + g + " ms", e.appendChild(p); + c.prototype.createTooltipContent = function(c, b) { + var a = Math.round(1E5 * b.totalTime) / 1E5, n = Math.round(1E5 * b.selfTime) / 1E5, p = Math.round(1E4 * b.selfTime / b.totalTime) / 100, e = document.createElement("div"), g = document.createElement("h1"); + g.textContent = b.kind.name; + e.appendChild(g); + g = document.createElement("p"); + g.textContent = "Total: " + a + " ms"; + e.appendChild(g); + a = document.createElement("p"); + a.textContent = "Self: " + n + " ms (" + p + "%)"; + e.appendChild(a); + if (n = c.getStatistics(b.kind)) { + p = document.createElement("p"), p.textContent = "Count: " + n.count, e.appendChild(p), p = Math.round(1E5 * n.totalTime) / 1E5, a = document.createElement("p"), a.textContent = "All Total: " + p + " ms", e.appendChild(a), n = Math.round(1E5 * n.selfTime) / 1E5, p = document.createElement("p"), p.textContent = "All Self: " + n + " ms", e.appendChild(p); } - this.appendDataElements(e, a.startData); - this.appendDataElements(e, a.endData); + this.appendDataElements(e, b.startData); + this.appendDataElements(e, b.endData); return e; }; - e.prototype.appendDataElements = function(e, a) { - if (!d.isNullOrUndefined(a)) { - e.appendChild(document.createElement("hr")); - var c; - if (d.isObject(a)) { - for (var g in a) { - c = document.createElement("p"), c.textContent = g + ": " + a[g], e.appendChild(c); + c.prototype.appendDataElements = function(c, b) { + if (!g.isNullOrUndefined(b)) { + c.appendChild(document.createElement("hr")); + var a; + if (g.isObject(b)) { + for (var n in b) { + a = document.createElement("p"), a.textContent = n + ": " + b[n], c.appendChild(a); } } else { - c = document.createElement("p"), c.textContent = a.toString(), e.appendChild(c); + a = document.createElement("p"), a.textContent = b.toString(), c.appendChild(a); } } }; - e.prototype.removeTooltipContent = function() { - for (var d = this._tooltip;d.firstChild;) { - d.removeChild(d.firstChild); + c.prototype.removeTooltipContent = function() { + for (var c = this._tooltip;c.firstChild;) { + c.removeChild(c.firstChild); } }; - return e; + return c; }(); - s.Controller = e; + e.Controller = c; })(m.Profiler || (m.Profiler = {})); - })(d.Tools || (d.Tools = {})); + })(g.Tools || (g.Tools = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - (function(m) { - var e = d.NumberUtilities.clamp, t = function() { - function a(a) { + (function(e) { + var c = g.NumberUtilities.clamp, m = function() { + function b(a) { this.value = a; } - a.prototype.toString = function() { + b.prototype.toString = function() { return this.value; }; - a.AUTO = new a("auto"); - a.DEFAULT = new a("default"); - a.NONE = new a("none"); - a.HELP = new a("help"); - a.POINTER = new a("pointer"); - a.PROGRESS = new a("progress"); - a.WAIT = new a("wait"); - a.CELL = new a("cell"); - a.CROSSHAIR = new a("crosshair"); - a.TEXT = new a("text"); - a.ALIAS = new a("alias"); - a.COPY = new a("copy"); - a.MOVE = new a("move"); - a.NO_DROP = new a("no-drop"); - a.NOT_ALLOWED = new a("not-allowed"); - a.ALL_SCROLL = new a("all-scroll"); - a.COL_RESIZE = new a("col-resize"); - a.ROW_RESIZE = new a("row-resize"); - a.N_RESIZE = new a("n-resize"); - a.E_RESIZE = new a("e-resize"); - a.S_RESIZE = new a("s-resize"); - a.W_RESIZE = new a("w-resize"); - a.NE_RESIZE = new a("ne-resize"); - a.NW_RESIZE = new a("nw-resize"); - a.SE_RESIZE = new a("se-resize"); - a.SW_RESIZE = new a("sw-resize"); - a.EW_RESIZE = new a("ew-resize"); - a.NS_RESIZE = new a("ns-resize"); - a.NESW_RESIZE = new a("nesw-resize"); - a.NWSE_RESIZE = new a("nwse-resize"); - a.ZOOM_IN = new a("zoom-in"); - a.ZOOM_OUT = new a("zoom-out"); - a.GRAB = new a("grab"); - a.GRABBING = new a("grabbing"); - return a; + b.AUTO = new b("auto"); + b.DEFAULT = new b("default"); + b.NONE = new b("none"); + b.HELP = new b("help"); + b.POINTER = new b("pointer"); + b.PROGRESS = new b("progress"); + b.WAIT = new b("wait"); + b.CELL = new b("cell"); + b.CROSSHAIR = new b("crosshair"); + b.TEXT = new b("text"); + b.ALIAS = new b("alias"); + b.COPY = new b("copy"); + b.MOVE = new b("move"); + b.NO_DROP = new b("no-drop"); + b.NOT_ALLOWED = new b("not-allowed"); + b.ALL_SCROLL = new b("all-scroll"); + b.COL_RESIZE = new b("col-resize"); + b.ROW_RESIZE = new b("row-resize"); + b.N_RESIZE = new b("n-resize"); + b.E_RESIZE = new b("e-resize"); + b.S_RESIZE = new b("s-resize"); + b.W_RESIZE = new b("w-resize"); + b.NE_RESIZE = new b("ne-resize"); + b.NW_RESIZE = new b("nw-resize"); + b.SE_RESIZE = new b("se-resize"); + b.SW_RESIZE = new b("sw-resize"); + b.EW_RESIZE = new b("ew-resize"); + b.NS_RESIZE = new b("ns-resize"); + b.NESW_RESIZE = new b("nesw-resize"); + b.NWSE_RESIZE = new b("nwse-resize"); + b.ZOOM_IN = new b("zoom-in"); + b.ZOOM_OUT = new b("zoom-out"); + b.GRAB = new b("grab"); + b.GRABBING = new b("grabbing"); + return b; }(); - m.MouseCursor = t; + e.MouseCursor = m; var k = function() { - function a(a, g) { + function b(a, b) { this._target = a; - this._eventTarget = g; + this._eventTarget = b; this._wheelDisabled = !1; this._boundOnMouseDown = this._onMouseDown.bind(this); this._boundOnMouseUp = this._onMouseUp.bind(this); @@ -4833,12 +5152,12 @@ this._boundOnMouseMove = this._onMouseMove.bind(this); this._boundOnMouseWheel = this._onMouseWheel.bind(this); this._boundOnDrag = this._onDrag.bind(this); - g.addEventListener("mousedown", this._boundOnMouseDown, !1); - g.addEventListener("mouseover", this._boundOnMouseOver, !1); - g.addEventListener("mouseout", this._boundOnMouseOut, !1); - g.addEventListener("onwheel" in document ? "wheel" : "mousewheel", this._boundOnMouseWheel, !1); + b.addEventListener("mousedown", this._boundOnMouseDown, !1); + b.addEventListener("mouseover", this._boundOnMouseOver, !1); + b.addEventListener("mouseout", this._boundOnMouseOut, !1); + b.addEventListener("onwheel" in document ? "wheel" : "mousewheel", this._boundOnMouseWheel, !1); } - a.prototype.destroy = function() { + b.prototype.destroy = function() { var a = this._eventTarget; a.removeEventListener("mousedown", this._boundOnMouseDown); a.removeEventListener("mouseover", this._boundOnMouseOver); @@ -4849,38 +5168,38 @@ this._killHoverCheck(); this._target = this._eventTarget = null; }; - a.prototype.updateCursor = function(c) { - if (!a._cursorOwner || a._cursorOwner === this._target) { - var g = this._eventTarget.parentElement; - a._cursor !== c && (a._cursor = c, ["", "-moz-", "-webkit-"].forEach(function(a) { - g.style.cursor = a + c; + b.prototype.updateCursor = function(a) { + if (!b._cursorOwner || b._cursorOwner === this._target) { + var c = this._eventTarget.parentElement; + b._cursor !== a && (b._cursor = a, ["", "-moz-", "-webkit-"].forEach(function(b) { + c.style.cursor = b + a; })); - a._cursorOwner = a._cursor === t.DEFAULT ? null : this._target; + b._cursorOwner = b._cursor === m.DEFAULT ? null : this._target; } }; - a.prototype._onMouseDown = function(a) { + b.prototype._onMouseDown = function(a) { this._killHoverCheck(); if (0 === a.button) { - var g = this._getTargetMousePos(a, a.target); - this._dragInfo = {start:g, current:g, delta:{x:0, y:0}, hasMoved:!1, originalTarget:a.target}; + var b = this._getTargetMousePos(a, a.target); + this._dragInfo = {start:b, current:b, delta:{x:0, y:0}, hasMoved:!1, originalTarget:a.target}; window.addEventListener("mousemove", this._boundOnDrag, !1); window.addEventListener("mouseup", this._boundOnMouseUp, !1); - this._target.onMouseDown(g.x, g.y); + this._target.onMouseDown(b.x, b.y); } }; - a.prototype._onDrag = function(a) { - var g = this._dragInfo; - a = this._getTargetMousePos(a, g.originalTarget); - var p = {x:a.x - g.start.x, y:a.y - g.start.y}; - g.current = a; - g.delta = p; - g.hasMoved = !0; - this._target.onDrag(g.start.x, g.start.y, a.x, a.y, p.x, p.y); + b.prototype._onDrag = function(a) { + var b = this._dragInfo; + a = this._getTargetMousePos(a, b.originalTarget); + var c = {x:a.x - b.start.x, y:a.y - b.start.y}; + b.current = a; + b.delta = c; + b.hasMoved = !0; + this._target.onDrag(b.start.x, b.start.y, a.x, a.y, c.x, c.y); }; - a.prototype._onMouseUp = function(a) { + b.prototype._onMouseUp = function(a) { window.removeEventListener("mousemove", this._boundOnDrag); window.removeEventListener("mouseup", this._boundOnMouseUp); - var g = this; + var b = this; a = this._dragInfo; if (a.hasMoved) { this._target.onDragEnd(a.start.x, a.start.y, a.current.x, a.current.y, a.delta.x, a.delta.y); @@ -4890,43 +5209,43 @@ this._dragInfo = null; this._wheelDisabled = !0; setTimeout(function() { - g._wheelDisabled = !1; + b._wheelDisabled = !1; }, 500); }; - a.prototype._onMouseOver = function(a) { + b.prototype._onMouseOver = function(a) { a.target.addEventListener("mousemove", this._boundOnMouseMove, !1); if (!this._dragInfo) { - var g = this._getTargetMousePos(a, a.target); - this._target.onMouseOver(g.x, g.y); + var b = this._getTargetMousePos(a, a.target); + this._target.onMouseOver(b.x, b.y); this._startHoverCheck(a); } }; - a.prototype._onMouseOut = function(a) { + b.prototype._onMouseOut = function(a) { a.target.removeEventListener("mousemove", this._boundOnMouseMove, !1); if (!this._dragInfo) { this._target.onMouseOut(); } this._killHoverCheck(); }; - a.prototype._onMouseMove = function(a) { + b.prototype._onMouseMove = function(a) { if (!this._dragInfo) { - var g = this._getTargetMousePos(a, a.target); - this._target.onMouseMove(g.x, g.y); + var b = this._getTargetMousePos(a, a.target); + this._target.onMouseMove(b.x, b.y); this._killHoverCheck(); this._startHoverCheck(a); } }; - a.prototype._onMouseWheel = function(a) { + b.prototype._onMouseWheel = function(a) { if (!(a.altKey || a.metaKey || a.ctrlKey || a.shiftKey || (a.preventDefault(), this._dragInfo || this._wheelDisabled))) { - var g = this._getTargetMousePos(a, a.target); - a = e("undefined" !== typeof a.deltaY ? a.deltaY / 16 : -a.wheelDelta / 40, -1, 1); - this._target.onMouseWheel(g.x, g.y, Math.pow(1.2, a) - 1); + var b = this._getTargetMousePos(a, a.target); + a = c("undefined" !== typeof a.deltaY ? a.deltaY / 16 : -a.wheelDelta / 40, -1, 1); + this._target.onMouseWheel(b.x, b.y, Math.pow(1.2, a) - 1); } }; - a.prototype._startHoverCheck = function(c) { - this._hoverInfo = {isHovering:!1, timeoutHandle:setTimeout(this._onMouseMoveIdleHandler.bind(this), a.HOVER_TIMEOUT), pos:this._getTargetMousePos(c, c.target)}; + b.prototype._startHoverCheck = function(a) { + this._hoverInfo = {isHovering:!1, timeoutHandle:setTimeout(this._onMouseMoveIdleHandler.bind(this), b.HOVER_TIMEOUT), pos:this._getTargetMousePos(a, a.target)}; }; - a.prototype._killHoverCheck = function() { + b.prototype._killHoverCheck = function() { if (this._hoverInfo) { clearTimeout(this._hoverInfo.timeoutHandle); if (this._hoverInfo.isHovering) { @@ -4935,559 +5254,559 @@ this._hoverInfo = null; } }; - a.prototype._onMouseMoveIdleHandler = function() { + b.prototype._onMouseMoveIdleHandler = function() { var a = this._hoverInfo; a.isHovering = !0; this._target.onHoverStart(a.pos.x, a.pos.y); }; - a.prototype._getTargetMousePos = function(a, g) { - var p = g.getBoundingClientRect(); - return{x:a.clientX - p.left, y:a.clientY - p.top}; + b.prototype._getTargetMousePos = function(a, b) { + var c = b.getBoundingClientRect(); + return{x:a.clientX - c.left, y:a.clientY - c.top}; }; - a.HOVER_TIMEOUT = 500; - a._cursor = t.DEFAULT; - return a; + b.HOVER_TIMEOUT = 500; + b._cursor = m.DEFAULT; + return b; }(); - m.MouseController = k; + e.MouseController = k; })(m.Profiler || (m.Profiler = {})); - })(d.Tools || (d.Tools = {})); + })(g.Tools || (g.Tools = {})); })(Shumway || (Shumway = {})); -(function(d) { - (function(d) { - (function(d) { - (function(d) { - d[d.NONE = 0] = "NONE"; - d[d.WINDOW = 1] = "WINDOW"; - d[d.HANDLE_LEFT = 2] = "HANDLE_LEFT"; - d[d.HANDLE_RIGHT = 3] = "HANDLE_RIGHT"; - d[d.HANDLE_BOTH = 4] = "HANDLE_BOTH"; - })(d.FlameChartDragTarget || (d.FlameChartDragTarget = {})); - var e = function() { - function e(k) { +(function(g) { + (function(g) { + (function(e) { + (function(c) { + c[c.NONE = 0] = "NONE"; + c[c.WINDOW = 1] = "WINDOW"; + c[c.HANDLE_LEFT = 2] = "HANDLE_LEFT"; + c[c.HANDLE_RIGHT = 3] = "HANDLE_RIGHT"; + c[c.HANDLE_BOTH = 4] = "HANDLE_BOTH"; + })(e.FlameChartDragTarget || (e.FlameChartDragTarget = {})); + var c = function() { + function c(k) { this._controller = k; this._initialized = !1; this._canvas = document.createElement("canvas"); this._context = this._canvas.getContext("2d"); - this._mouseController = new d.MouseController(this, this._canvas); + this._mouseController = new e.MouseController(this, this._canvas); k = k.container; k.appendChild(this._canvas); k = k.getBoundingClientRect(); this.setSize(k.width); } - Object.defineProperty(e.prototype, "canvas", {get:function() { + Object.defineProperty(c.prototype, "canvas", {get:function() { return this._canvas; }, enumerable:!0, configurable:!0}); - e.prototype.setSize = function(d, a) { - "undefined" === typeof a && (a = 20); - this._width = d; - this._height = a; + c.prototype.setSize = function(c, b) { + void 0 === b && (b = 20); + this._width = c; + this._height = b; this._resetCanvas(); this.draw(); }; - e.prototype.initialize = function(d, a) { + c.prototype.initialize = function(c, b) { this._initialized = !0; - this.setRange(d, a); - this.setWindow(d, a, !1); + this.setRange(c, b); + this.setWindow(c, b, !1); this.draw(); }; - e.prototype.setWindow = function(d, a, c) { - "undefined" === typeof c && (c = !0); - this._windowStart = d; - this._windowEnd = a; - !c || this.draw(); - }; - e.prototype.setRange = function(d, a) { - var c = !1; - "undefined" === typeof c && (c = !0); - this._rangeStart = d; - this._rangeEnd = a; - !c || this.draw(); + c.prototype.setWindow = function(c, b, a) { + void 0 === a && (a = !0); + this._windowStart = c; + this._windowEnd = b; + !a || this.draw(); + }; + c.prototype.setRange = function(c, b) { + var a = !1; + void 0 === a && (a = !0); + this._rangeStart = c; + this._rangeEnd = b; + !a || this.draw(); }; - e.prototype.destroy = function() { + c.prototype.destroy = function() { this._mouseController.destroy(); this._mouseController = null; this._controller.container.removeChild(this._canvas); this._controller = null; }; - e.prototype._resetCanvas = function() { - var d = window.devicePixelRatio, a = this._canvas; - a.width = this._width * d; - a.height = this._height * d; - a.style.width = this._width + "px"; - a.style.height = this._height + "px"; + c.prototype._resetCanvas = function() { + var c = window.devicePixelRatio, b = this._canvas; + b.width = this._width * c; + b.height = this._height * c; + b.style.width = this._width + "px"; + b.style.height = this._height + "px"; }; - e.prototype.draw = function() { + c.prototype.draw = function() { }; - e.prototype._almostEq = function(d, a) { - var c; - "undefined" === typeof c && (c = 10); - return Math.abs(d - a) < 1 / Math.pow(10, c); + c.prototype._almostEq = function(c, b) { + var a; + void 0 === a && (a = 10); + return Math.abs(c - b) < 1 / Math.pow(10, a); }; - e.prototype._windowEqRange = function() { + c.prototype._windowEqRange = function() { return this._almostEq(this._windowStart, this._rangeStart) && this._almostEq(this._windowEnd, this._rangeEnd); }; - e.prototype._decimalPlaces = function(d) { - return(+d).toFixed(10).replace(/^-?\d*\.?|0+$/g, "").length; + c.prototype._decimalPlaces = function(c) { + return(+c).toFixed(10).replace(/^-?\d*\.?|0+$/g, "").length; }; - e.prototype._toPixelsRelative = function(d) { + c.prototype._toPixelsRelative = function(c) { return 0; }; - e.prototype._toPixels = function(d) { + c.prototype._toPixels = function(c) { return 0; }; - e.prototype._toTimeRelative = function(d) { + c.prototype._toTimeRelative = function(c) { return 0; }; - e.prototype._toTime = function(d) { + c.prototype._toTime = function(c) { return 0; }; - e.prototype.onMouseWheel = function(d, a, c) { - d = this._toTime(d); - a = this._windowStart; - var g = this._windowEnd, p = g - a; - c = Math.max((e.MIN_WINDOW_LEN - p) / p, c); - this._controller.setWindow(a + (a - d) * c, g + (g - d) * c); + c.prototype.onMouseWheel = function(e, b, a) { + e = this._toTime(e); + b = this._windowStart; + var n = this._windowEnd, p = n - b; + a = Math.max((c.MIN_WINDOW_LEN - p) / p, a); + this._controller.setWindow(b + (b - e) * a, n + (n - e) * a); this.onHoverEnd(); }; - e.prototype.onMouseDown = function(d, a) { + c.prototype.onMouseDown = function(c, b) { }; - e.prototype.onMouseMove = function(d, a) { + c.prototype.onMouseMove = function(c, b) { }; - e.prototype.onMouseOver = function(d, a) { + c.prototype.onMouseOver = function(c, b) { }; - e.prototype.onMouseOut = function() { + c.prototype.onMouseOut = function() { }; - e.prototype.onDrag = function(d, a, c, g, p, e) { + c.prototype.onDrag = function(c, b, a, n, p, e) { }; - e.prototype.onDragEnd = function(d, a, c, g, p, e) { + c.prototype.onDragEnd = function(c, b, a, n, p, e) { }; - e.prototype.onClick = function(d, a) { + c.prototype.onClick = function(c, b) { }; - e.prototype.onHoverStart = function(d, a) { + c.prototype.onHoverStart = function(c, b) { }; - e.prototype.onHoverEnd = function() { + c.prototype.onHoverEnd = function() { }; - e.DRAGHANDLE_WIDTH = 4; - e.MIN_WINDOW_LEN = .1; - return e; + c.DRAGHANDLE_WIDTH = 4; + c.MIN_WINDOW_LEN = .1; + return c; }(); - d.FlameChartBase = e; - })(d.Profiler || (d.Profiler = {})); - })(d.Tools || (d.Tools = {})); + e.FlameChartBase = c; + })(g.Profiler || (g.Profiler = {})); + })(g.Tools || (g.Tools = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - (function(m) { - var e = d.StringUtilities.trimMiddle, t = d.ObjectUtilities.createEmptyObject, k = function(a) { - function c(c, d) { - a.call(this, c); + (function(e) { + var c = g.StringUtilities.trimMiddle, m = function(k) { + function b(a, b) { + k.call(this, a); this._textWidth = {}; this._minFrameWidthInPixels = 1; - this._snapshot = d; - this._kindStyle = t(); + this._snapshot = b; + this._kindStyle = Object.create(null); } - __extends(c, a); - c.prototype.setSize = function(c, d) { - a.prototype.setSize.call(this, c, d || this._initialized ? 12.5 * this._maxDepth : 100); + __extends(b, k); + b.prototype.setSize = function(a, b) { + k.prototype.setSize.call(this, a, b || this._initialized ? 12.5 * this._maxDepth : 100); }; - c.prototype.initialize = function(a, c) { + b.prototype.initialize = function(a, b) { this._initialized = !0; this._maxDepth = this._snapshot.maxDepth; - this.setRange(a, c); - this.setWindow(a, c, !1); + this.setRange(a, b); + this.setWindow(a, b, !1); this.setSize(this._width, 12.5 * this._maxDepth); }; - c.prototype.destroy = function() { - a.prototype.destroy.call(this); + b.prototype.destroy = function() { + k.prototype.destroy.call(this); this._snapshot = null; }; - c.prototype.draw = function() { - var a = this._context, c = window.devicePixelRatio; - d.ColorStyle.reset(); + b.prototype.draw = function() { + var a = this._context, b = window.devicePixelRatio; + g.ColorStyle.reset(); a.save(); - a.scale(c, c); + a.scale(b, b); a.fillStyle = this._controller.theme.bodyBackground(1); a.fillRect(0, 0, this._width, this._height); this._initialized && this._drawChildren(this._snapshot); a.restore(); }; - c.prototype._drawChildren = function(a, c) { - "undefined" === typeof c && (c = 0); - var d = a.getChildRange(this._windowStart, this._windowEnd); - if (d) { - for (var e = d.startIndex;e <= d.endIndex;e++) { - var l = a.children[e]; - this._drawFrame(l, c) && this._drawChildren(l, c + 1); + b.prototype._drawChildren = function(a, b) { + void 0 === b && (b = 0); + var c = a.getChildRange(this._windowStart, this._windowEnd); + if (c) { + for (var e = c.startIndex;e <= c.endIndex;e++) { + var k = a.children[e]; + this._drawFrame(k, b) && this._drawChildren(k, b + 1); } } }; - c.prototype._drawFrame = function(a, c) { - var e = this._context, k = this._toPixels(a.startTime), l = this._toPixels(a.endTime), r = l - k; - if (r <= this._minFrameWidthInPixels) { - return e.fillStyle = this._controller.theme.tabToolbar(1), e.fillRect(k, 12.5 * c, this._minFrameWidthInPixels, 12 + 12.5 * (a.maxDepth - a.depth)), !1; - } - 0 > k && (l = r + k, k = 0); - var l = l - k, b = this._kindStyle[a.kind.id]; - b || (b = d.ColorStyle.randomStyle(), b = this._kindStyle[a.kind.id] = {bgColor:b, textColor:d.ColorStyle.contrastStyle(b)}); - e.save(); - e.fillStyle = b.bgColor; - e.fillRect(k, 12.5 * c, l, 12); - 12 < r && (r = a.kind.name) && r.length && (r = this._prepareText(e, r, l - 4), r.length && (e.fillStyle = b.textColor, e.textBaseline = "bottom", e.fillText(r, k + 2, 12.5 * (c + 1) - 1))); - e.restore(); + b.prototype._drawFrame = function(a, b) { + var c = this._context, e = this._toPixels(a.startTime), k = this._toPixels(a.endTime), l = k - e; + if (l <= this._minFrameWidthInPixels) { + return c.fillStyle = this._controller.theme.tabToolbar(1), c.fillRect(e, 12.5 * b, this._minFrameWidthInPixels, 12 + 12.5 * (a.maxDepth - a.depth)), !1; + } + 0 > e && (k = l + e, e = 0); + var k = k - e, t = this._kindStyle[a.kind.id]; + t || (t = g.ColorStyle.randomStyle(), t = this._kindStyle[a.kind.id] = {bgColor:t, textColor:g.ColorStyle.contrastStyle(t)}); + c.save(); + c.fillStyle = t.bgColor; + c.fillRect(e, 12.5 * b, k, 12); + 12 < l && (l = a.kind.name) && l.length && (l = this._prepareText(c, l, k - 4), l.length && (c.fillStyle = t.textColor, c.textBaseline = "bottom", c.fillText(l, e + 2, 12.5 * (b + 1) - 1))); + c.restore(); return!0; }; - c.prototype._prepareText = function(a, c, d) { - var k = this._measureWidth(a, c); - if (d > k) { - return c; - } - for (var k = 3, l = c.length;k < l;) { - var r = k + l >> 1; - this._measureWidth(a, e(c, r)) < d ? k = r + 1 : l = r; - } - c = e(c, l - 1); - k = this._measureWidth(a, c); - return k <= d ? c : ""; - }; - c.prototype._measureWidth = function(a, c) { - var d = this._textWidth[c]; - d || (d = a.measureText(c).width, this._textWidth[c] = d); - return d; + b.prototype._prepareText = function(a, b, p) { + var e = this._measureWidth(a, b); + if (p > e) { + return b; + } + for (var e = 3, k = b.length;e < k;) { + var l = e + k >> 1; + this._measureWidth(a, c(b, l)) < p ? e = l + 1 : k = l; + } + b = c(b, k - 1); + e = this._measureWidth(a, b); + return e <= p ? b : ""; + }; + b.prototype._measureWidth = function(a, b) { + var c = this._textWidth[b]; + c || (c = a.measureText(b).width, this._textWidth[b] = c); + return c; }; - c.prototype._toPixelsRelative = function(a) { + b.prototype._toPixelsRelative = function(a) { return a * this._width / (this._windowEnd - this._windowStart); }; - c.prototype._toPixels = function(a) { + b.prototype._toPixels = function(a) { return this._toPixelsRelative(a - this._windowStart); }; - c.prototype._toTimeRelative = function(a) { + b.prototype._toTimeRelative = function(a) { return a * (this._windowEnd - this._windowStart) / this._width; }; - c.prototype._toTime = function(a) { + b.prototype._toTime = function(a) { return this._toTimeRelative(a) + this._windowStart; }; - c.prototype._getFrameAtPosition = function(a, c) { - var d = 1 + c / 12.5 | 0, e = this._snapshot.query(this._toTime(a)); - if (e && e.depth >= d) { - for (;e && e.depth > d;) { + b.prototype._getFrameAtPosition = function(a, b) { + var c = 1 + b / 12.5 | 0, e = this._snapshot.query(this._toTime(a)); + if (e && e.depth >= c) { + for (;e && e.depth > c;) { e = e.parent; } return e; } return null; }; - c.prototype.onMouseDown = function(a, c) { - this._windowEqRange() || (this._mouseController.updateCursor(m.MouseCursor.ALL_SCROLL), this._dragInfo = {windowStartInitial:this._windowStart, windowEndInitial:this._windowEnd, target:1}); + b.prototype.onMouseDown = function(a, b) { + this._windowEqRange() || (this._mouseController.updateCursor(e.MouseCursor.ALL_SCROLL), this._dragInfo = {windowStartInitial:this._windowStart, windowEndInitial:this._windowEnd, target:1}); }; - c.prototype.onMouseMove = function(a, c) { + b.prototype.onMouseMove = function(a, b) { }; - c.prototype.onMouseOver = function(a, c) { + b.prototype.onMouseOver = function(a, b) { }; - c.prototype.onMouseOut = function() { + b.prototype.onMouseOut = function() { }; - c.prototype.onDrag = function(a, c, d, e, l, r) { + b.prototype.onDrag = function(a, b, c, e, k, l) { if (a = this._dragInfo) { - l = this._toTimeRelative(-l), this._controller.setWindow(a.windowStartInitial + l, a.windowEndInitial + l); + k = this._toTimeRelative(-k), this._controller.setWindow(a.windowStartInitial + k, a.windowEndInitial + k); } }; - c.prototype.onDragEnd = function(a, c, d, e, l, r) { + b.prototype.onDragEnd = function(a, b, c, k, g, l) { this._dragInfo = null; - this._mouseController.updateCursor(m.MouseCursor.DEFAULT); + this._mouseController.updateCursor(e.MouseCursor.DEFAULT); }; - c.prototype.onClick = function(a, c) { + b.prototype.onClick = function(a, b) { this._dragInfo = null; - this._mouseController.updateCursor(m.MouseCursor.DEFAULT); + this._mouseController.updateCursor(e.MouseCursor.DEFAULT); }; - c.prototype.onHoverStart = function(a, c) { - var d = this._getFrameAtPosition(a, c); - d && (this._hoveredFrame = d, this._controller.showTooltip(this, d, a, c)); + b.prototype.onHoverStart = function(a, b) { + var c = this._getFrameAtPosition(a, b); + c && (this._hoveredFrame = c, this._controller.showTooltip(this, c, a, b)); }; - c.prototype.onHoverEnd = function() { + b.prototype.onHoverEnd = function() { this._hoveredFrame && (this._hoveredFrame = null, this._controller.hideTooltip()); }; - c.prototype.getStatistics = function(a) { - var c = this._snapshot; - c.statistics || c.calculateStatistics(); - return c.statistics[a.id]; + b.prototype.getStatistics = function(a) { + var b = this._snapshot; + b.statistics || b.calculateStatistics(); + return b.statistics[a.id]; }; - return c; - }(m.FlameChartBase); - m.FlameChart = k; + return b; + }(e.FlameChartBase); + e.FlameChart = m; })(m.Profiler || (m.Profiler = {})); - })(d.Tools || (d.Tools = {})); + })(g.Tools || (g.Tools = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - (function(m) { - var e = d.NumberUtilities.clamp; - (function(d) { - d[d.OVERLAY = 0] = "OVERLAY"; - d[d.STACK = 1] = "STACK"; - d[d.UNION = 2] = "UNION"; - })(m.FlameChartOverviewMode || (m.FlameChartOverviewMode = {})); - var t = function(d) { - function a(a, g) { - "undefined" === typeof g && (g = 1); - this._mode = g; + (function(e) { + var c = g.NumberUtilities.clamp; + (function(c) { + c[c.OVERLAY = 0] = "OVERLAY"; + c[c.STACK = 1] = "STACK"; + c[c.UNION = 2] = "UNION"; + })(e.FlameChartOverviewMode || (e.FlameChartOverviewMode = {})); + var m = function(k) { + function b(a, b) { + void 0 === b && (b = 1); + this._mode = b; this._overviewCanvasDirty = !0; this._overviewCanvas = document.createElement("canvas"); this._overviewContext = this._overviewCanvas.getContext("2d"); - d.call(this, a); + k.call(this, a); } - __extends(a, d); - a.prototype.setSize = function(a, g) { - d.prototype.setSize.call(this, a, g || 64); + __extends(b, k); + b.prototype.setSize = function(a, b) { + k.prototype.setSize.call(this, a, b || 64); }; - Object.defineProperty(a.prototype, "mode", {set:function(a) { + Object.defineProperty(b.prototype, "mode", {set:function(a) { this._mode = a; this.draw(); }, enumerable:!0, configurable:!0}); - a.prototype._resetCanvas = function() { - d.prototype._resetCanvas.call(this); + b.prototype._resetCanvas = function() { + k.prototype._resetCanvas.call(this); this._overviewCanvas.width = this._canvas.width; this._overviewCanvas.height = this._canvas.height; this._overviewCanvasDirty = !0; }; - a.prototype.draw = function() { - var a = this._context, g = window.devicePixelRatio, d = this._width, e = this._height; + b.prototype.draw = function() { + var a = this._context, b = window.devicePixelRatio, c = this._width, e = this._height; a.save(); - a.scale(g, g); + a.scale(b, b); a.fillStyle = this._controller.theme.bodyBackground(1); - a.fillRect(0, 0, d, e); + a.fillRect(0, 0, c, e); a.restore(); this._initialized && (this._overviewCanvasDirty && (this._drawChart(), this._overviewCanvasDirty = !1), a.drawImage(this._overviewCanvas, 0, 0), this._drawSelection()); }; - a.prototype._drawSelection = function() { - var a = this._context, g = this._height, d = window.devicePixelRatio, e = this._selection ? this._selection.left : this._toPixels(this._windowStart), k = this._selection ? this._selection.right : this._toPixels(this._windowEnd), l = this._controller.theme; + b.prototype._drawSelection = function() { + var a = this._context, b = this._height, c = window.devicePixelRatio, e = this._selection ? this._selection.left : this._toPixels(this._windowStart), k = this._selection ? this._selection.right : this._toPixels(this._windowEnd), l = this._controller.theme; a.save(); - a.scale(d, d); - this._selection ? (a.fillStyle = l.selectionText(.15), a.fillRect(e, 1, k - e, g - 1), a.fillStyle = "rgba(133, 0, 0, 1)", a.fillRect(e + .5, 0, k - e - 1, 4), a.fillRect(e + .5, g - 4, k - e - 1, 4)) : (a.fillStyle = l.bodyBackground(.4), a.fillRect(0, 1, e, g - 1), a.fillRect(k, 1, this._width, g - 1)); + a.scale(c, c); + this._selection ? (a.fillStyle = l.selectionText(.15), a.fillRect(e, 1, k - e, b - 1), a.fillStyle = "rgba(133, 0, 0, 1)", a.fillRect(e + .5, 0, k - e - 1, 4), a.fillRect(e + .5, b - 4, k - e - 1, 4)) : (a.fillStyle = l.bodyBackground(.4), a.fillRect(0, 1, e, b - 1), a.fillRect(k, 1, this._width, b - 1)); a.beginPath(); a.moveTo(e, 0); - a.lineTo(e, g); + a.lineTo(e, b); a.moveTo(k, 0); - a.lineTo(k, g); + a.lineTo(k, b); a.lineWidth = .5; a.strokeStyle = l.foregroundTextGrey(1); a.stroke(); - g = Math.abs((this._selection ? this._toTime(this._selection.right) : this._windowEnd) - (this._selection ? this._toTime(this._selection.left) : this._windowStart)); + b = Math.abs((this._selection ? this._toTime(this._selection.right) : this._windowEnd) - (this._selection ? this._toTime(this._selection.left) : this._windowStart)); a.fillStyle = l.selectionText(.5); a.font = "8px sans-serif"; a.textBaseline = "alphabetic"; a.textAlign = "end"; - a.fillText(g.toFixed(2), Math.min(e, k) - 4, 10); - a.fillText((g / 60).toFixed(2), Math.min(e, k) - 4, 20); + a.fillText(b.toFixed(2), Math.min(e, k) - 4, 10); + a.fillText((b / 60).toFixed(2), Math.min(e, k) - 4, 20); a.restore(); }; - a.prototype._drawChart = function() { - var a = window.devicePixelRatio, g = this._height, d = this._controller.activeProfile, e = 4 * this._width, k = d.totalTime / e, l = this._overviewContext, r = this._controller.theme.blueHighlight(1); + b.prototype._drawChart = function() { + var a = window.devicePixelRatio, b = this._height, c = this._controller.activeProfile, e = 4 * this._width, k = c.totalTime / e, l = this._overviewContext, g = this._controller.theme.blueHighlight(1); l.save(); - l.translate(0, a * g); - var b = -a * g / (d.maxDepth - 1); - l.scale(a / 4, b); - l.clearRect(0, 0, e, d.maxDepth - 1); - 1 == this._mode && l.scale(1, 1 / d.snapshotCount); - for (var h = 0, m = d.snapshotCount;h < m;h++) { - var n = d.getSnapshotAt(h); - if (n) { - var f = null, q = 0; + l.translate(0, a * b); + var r = -a * b / (c.maxDepth - 1); + l.scale(a / 4, r); + l.clearRect(0, 0, e, c.maxDepth - 1); + 1 == this._mode && l.scale(1, 1 / c.snapshotCount); + for (var m = 0, h = c.snapshotCount;m < h;m++) { + var f = c.getSnapshotAt(m); + if (f) { + var d = null, q = 0; l.beginPath(); l.moveTo(0, 0); - for (var U = 0;U < e;U++) { - q = d.startTime + U * k, q = (f = f ? f.queryNext(q) : n.query(q)) ? f.getDepth() - 1 : 0, l.lineTo(U, q); + for (var x = 0;x < e;x++) { + q = c.startTime + x * k, q = (d = d ? d.queryNext(q) : f.query(q)) ? d.getDepth() - 1 : 0, l.lineTo(x, q); } - l.lineTo(U, 0); - l.fillStyle = r; + l.lineTo(x, 0); + l.fillStyle = g; l.fill(); - 1 == this._mode && l.translate(0, -g * a / b); + 1 == this._mode && l.translate(0, -b * a / r); } } l.restore(); }; - a.prototype._toPixelsRelative = function(a) { + b.prototype._toPixelsRelative = function(a) { return a * this._width / (this._rangeEnd - this._rangeStart); }; - a.prototype._toPixels = function(a) { + b.prototype._toPixels = function(a) { return this._toPixelsRelative(a - this._rangeStart); }; - a.prototype._toTimeRelative = function(a) { + b.prototype._toTimeRelative = function(a) { return a * (this._rangeEnd - this._rangeStart) / this._width; }; - a.prototype._toTime = function(a) { + b.prototype._toTime = function(a) { return this._toTimeRelative(a) + this._rangeStart; }; - a.prototype._getDragTargetUnderCursor = function(a, g) { - if (0 <= g && g < this._height) { - var d = this._toPixels(this._windowStart), e = this._toPixels(this._windowEnd), k = 2 + m.FlameChartBase.DRAGHANDLE_WIDTH / 2, l = a >= d - k && a <= d + k, r = a >= e - k && a <= e + k; - if (l && r) { + b.prototype._getDragTargetUnderCursor = function(a, b) { + if (0 <= b && b < this._height) { + var c = this._toPixels(this._windowStart), k = this._toPixels(this._windowEnd), g = 2 + e.FlameChartBase.DRAGHANDLE_WIDTH / 2, l = a >= c - g && a <= c + g, t = a >= k - g && a <= k + g; + if (l && t) { return 4; } if (l) { return 2; } - if (r) { + if (t) { return 3; } - if (!this._windowEqRange() && a > d + k && a < e - k) { + if (!this._windowEqRange() && a > c + g && a < k - g) { return 1; } } return 0; }; - a.prototype.onMouseDown = function(a, g) { - var d = this._getDragTargetUnderCursor(a, g); - 0 === d ? (this._selection = {left:a, right:a}, this.draw()) : (1 === d && this._mouseController.updateCursor(m.MouseCursor.GRABBING), this._dragInfo = {windowStartInitial:this._windowStart, windowEndInitial:this._windowEnd, target:d}); - }; - a.prototype.onMouseMove = function(a, g) { - var d = m.MouseCursor.DEFAULT, e = this._getDragTargetUnderCursor(a, g); - 0 === e || this._selection || (d = 1 === e ? m.MouseCursor.GRAB : m.MouseCursor.EW_RESIZE); - this._mouseController.updateCursor(d); + b.prototype.onMouseDown = function(a, b) { + var c = this._getDragTargetUnderCursor(a, b); + 0 === c ? (this._selection = {left:a, right:a}, this.draw()) : (1 === c && this._mouseController.updateCursor(e.MouseCursor.GRABBING), this._dragInfo = {windowStartInitial:this._windowStart, windowEndInitial:this._windowEnd, target:c}); + }; + b.prototype.onMouseMove = function(a, b) { + var c = e.MouseCursor.DEFAULT, k = this._getDragTargetUnderCursor(a, b); + 0 === k || this._selection || (c = 1 === k ? e.MouseCursor.GRAB : e.MouseCursor.EW_RESIZE); + this._mouseController.updateCursor(c); }; - a.prototype.onMouseOver = function(a, g) { - this.onMouseMove(a, g); + b.prototype.onMouseOver = function(a, b) { + this.onMouseMove(a, b); }; - a.prototype.onMouseOut = function() { - this._mouseController.updateCursor(m.MouseCursor.DEFAULT); + b.prototype.onMouseOut = function() { + this._mouseController.updateCursor(e.MouseCursor.DEFAULT); }; - a.prototype.onDrag = function(a, g, d, k, u, l) { + b.prototype.onDrag = function(a, b, p, k, g, l) { if (this._selection) { - this._selection = {left:a, right:e(d, 0, this._width - 1)}, this.draw(); + this._selection = {left:a, right:c(p, 0, this._width - 1)}, this.draw(); } else { a = this._dragInfo; if (4 === a.target) { - if (0 !== u) { - a.target = 0 > u ? 2 : 3; + if (0 !== g) { + a.target = 0 > g ? 2 : 3; } else { return; } } - g = this._windowStart; - d = this._windowEnd; - u = this._toTimeRelative(u); + b = this._windowStart; + p = this._windowEnd; + g = this._toTimeRelative(g); switch(a.target) { case 1: - g = a.windowStartInitial + u; - d = a.windowEndInitial + u; + b = a.windowStartInitial + g; + p = a.windowEndInitial + g; break; case 2: - g = e(a.windowStartInitial + u, this._rangeStart, d - m.FlameChartBase.MIN_WINDOW_LEN); + b = c(a.windowStartInitial + g, this._rangeStart, p - e.FlameChartBase.MIN_WINDOW_LEN); break; case 3: - d = e(a.windowEndInitial + u, g + m.FlameChartBase.MIN_WINDOW_LEN, this._rangeEnd); + p = c(a.windowEndInitial + g, b + e.FlameChartBase.MIN_WINDOW_LEN, this._rangeEnd); break; default: return; } - this._controller.setWindow(g, d); + this._controller.setWindow(b, p); } }; - a.prototype.onDragEnd = function(a, g, d, e, k, l) { - this._selection && (this._selection = null, this._controller.setWindow(this._toTime(a), this._toTime(d))); + b.prototype.onDragEnd = function(a, b, c, e, k, l) { + this._selection && (this._selection = null, this._controller.setWindow(this._toTime(a), this._toTime(c))); this._dragInfo = null; - this.onMouseMove(d, e); + this.onMouseMove(c, e); }; - a.prototype.onClick = function(a, g) { + b.prototype.onClick = function(a, b) { this._selection = this._dragInfo = null; - this._windowEqRange() || (0 === this._getDragTargetUnderCursor(a, g) && this._controller.moveWindowTo(this._toTime(a)), this.onMouseMove(a, g)); + this._windowEqRange() || (0 === this._getDragTargetUnderCursor(a, b) && this._controller.moveWindowTo(this._toTime(a)), this.onMouseMove(a, b)); this.draw(); }; - a.prototype.onHoverStart = function(a, g) { + b.prototype.onHoverStart = function(a, b) { }; - a.prototype.onHoverEnd = function() { + b.prototype.onHoverEnd = function() { }; - return a; - }(m.FlameChartBase); - m.FlameChartOverview = t; + return b; + }(e.FlameChartBase); + e.FlameChartOverview = m; })(m.Profiler || (m.Profiler = {})); - })(d.Tools || (d.Tools = {})); + })(g.Tools || (g.Tools = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - (function(m) { - var e = d.NumberUtilities.clamp; - (function(d) { - d[d.OVERVIEW = 0] = "OVERVIEW"; - d[d.CHART = 1] = "CHART"; - })(m.FlameChartHeaderType || (m.FlameChartHeaderType = {})); - var t = function(d) { - function a(a, g) { - this._type = g; - d.call(this, a); - } - __extends(a, d); - a.prototype.draw = function() { - var a = this._context, g = window.devicePixelRatio, d = this._width, e = this._height; + (function(e) { + var c = g.NumberUtilities.clamp; + (function(c) { + c[c.OVERVIEW = 0] = "OVERVIEW"; + c[c.CHART = 1] = "CHART"; + })(e.FlameChartHeaderType || (e.FlameChartHeaderType = {})); + var m = function(k) { + function b(a, b) { + this._type = b; + k.call(this, a); + } + __extends(b, k); + b.prototype.draw = function() { + var a = this._context, b = window.devicePixelRatio, c = this._width, e = this._height; a.save(); - a.scale(g, g); + a.scale(b, b); a.fillStyle = this._controller.theme.tabToolbar(1); - a.fillRect(0, 0, d, e); - this._initialized && (0 == this._type ? (g = this._toPixels(this._windowStart), d = this._toPixels(this._windowEnd), a.fillStyle = this._controller.theme.bodyBackground(1), a.fillRect(g, 0, d - g, e), this._drawLabels(this._rangeStart, this._rangeEnd), this._drawDragHandle(g), this._drawDragHandle(d)) : this._drawLabels(this._windowStart, this._windowEnd)); + a.fillRect(0, 0, c, e); + this._initialized && (0 == this._type ? (b = this._toPixels(this._windowStart), c = this._toPixels(this._windowEnd), a.fillStyle = this._controller.theme.bodyBackground(1), a.fillRect(b, 0, c - b, e), this._drawLabels(this._rangeStart, this._rangeEnd), this._drawDragHandle(b), this._drawDragHandle(c)) : this._drawLabels(this._windowStart, this._windowEnd)); a.restore(); }; - a.prototype._drawLabels = function(c, g) { - var d = this._context, e = this._calculateTickInterval(c, g), k = Math.ceil(c / e) * e, l = 500 <= e, r = l ? 1E3 : 1, b = this._decimalPlaces(e / r), l = l ? "s" : "ms", h = this._toPixels(k), m = this._height / 2, n = this._controller.theme; - d.lineWidth = 1; - d.strokeStyle = n.contentTextDarkGrey(.5); - d.fillStyle = n.contentTextDarkGrey(1); - d.textAlign = "right"; - d.textBaseline = "middle"; - d.font = "11px sans-serif"; - for (n = this._width + a.TICK_MAX_WIDTH;h < n;) { - d.fillText((k / r).toFixed(b) + " " + l, h - 7, m + 1), d.beginPath(), d.moveTo(h, 0), d.lineTo(h, this._height + 1), d.closePath(), d.stroke(), k += e, h = this._toPixels(k); - } - }; - a.prototype._calculateTickInterval = function(c, g) { - var d = (g - c) / (this._width / a.TICK_MAX_WIDTH), e = Math.pow(10, Math.floor(Math.log(d) / Math.LN10)), d = d / e; - return 5 < d ? 10 * e : 2 < d ? 5 * e : 1 < d ? 2 * e : e; - }; - a.prototype._drawDragHandle = function(a) { - var g = this._context; - g.lineWidth = 2; - g.strokeStyle = this._controller.theme.bodyBackground(1); - g.fillStyle = this._controller.theme.foregroundTextGrey(.7); - this._drawRoundedRect(g, a - m.FlameChartBase.DRAGHANDLE_WIDTH / 2, m.FlameChartBase.DRAGHANDLE_WIDTH, this._height - 2); + b.prototype._drawLabels = function(a, c) { + var p = this._context, e = this._calculateTickInterval(a, c), k = Math.ceil(a / e) * e, l = 500 <= e, g = l ? 1E3 : 1, r = this._decimalPlaces(e / g), l = l ? "s" : "ms", m = this._toPixels(k), h = this._height / 2, f = this._controller.theme; + p.lineWidth = 1; + p.strokeStyle = f.contentTextDarkGrey(.5); + p.fillStyle = f.contentTextDarkGrey(1); + p.textAlign = "right"; + p.textBaseline = "middle"; + p.font = "11px sans-serif"; + for (f = this._width + b.TICK_MAX_WIDTH;m < f;) { + p.fillText((k / g).toFixed(r) + " " + l, m - 7, h + 1), p.beginPath(), p.moveTo(m, 0), p.lineTo(m, this._height + 1), p.closePath(), p.stroke(), k += e, m = this._toPixels(k); + } + }; + b.prototype._calculateTickInterval = function(a, c) { + var e = (c - a) / (this._width / b.TICK_MAX_WIDTH), k = Math.pow(10, Math.floor(Math.log(e) / Math.LN10)), e = e / k; + return 5 < e ? 10 * k : 2 < e ? 5 * k : 1 < e ? 2 * k : k; + }; + b.prototype._drawDragHandle = function(a) { + var b = this._context; + b.lineWidth = 2; + b.strokeStyle = this._controller.theme.bodyBackground(1); + b.fillStyle = this._controller.theme.foregroundTextGrey(.7); + this._drawRoundedRect(b, a - e.FlameChartBase.DRAGHANDLE_WIDTH / 2, e.FlameChartBase.DRAGHANDLE_WIDTH, this._height - 2); }; - a.prototype._drawRoundedRect = function(a, g, d, e) { + b.prototype._drawRoundedRect = function(a, b, c, e) { var k, l = !0; - "undefined" === typeof l && (l = !0); - "undefined" === typeof k && (k = !0); + void 0 === l && (l = !0); + void 0 === k && (k = !0); a.beginPath(); - a.moveTo(g + 2, 1); - a.lineTo(g + d - 2, 1); - a.quadraticCurveTo(g + d, 1, g + d, 3); - a.lineTo(g + d, 1 + e - 2); - a.quadraticCurveTo(g + d, 1 + e, g + d - 2, 1 + e); - a.lineTo(g + 2, 1 + e); - a.quadraticCurveTo(g, 1 + e, g, 1 + e - 2); - a.lineTo(g, 3); - a.quadraticCurveTo(g, 1, g + 2, 1); + a.moveTo(b + 2, 1); + a.lineTo(b + c - 2, 1); + a.quadraticCurveTo(b + c, 1, b + c, 3); + a.lineTo(b + c, 1 + e - 2); + a.quadraticCurveTo(b + c, 1 + e, b + c - 2, 1 + e); + a.lineTo(b + 2, 1 + e); + a.quadraticCurveTo(b, 1 + e, b, 1 + e - 2); + a.lineTo(b, 3); + a.quadraticCurveTo(b, 1, b + 2, 1); a.closePath(); l && a.stroke(); k && a.fill(); }; - a.prototype._toPixelsRelative = function(a) { + b.prototype._toPixelsRelative = function(a) { return a * this._width / (0 === this._type ? this._rangeEnd - this._rangeStart : this._windowEnd - this._windowStart); }; - a.prototype._toPixels = function(a) { + b.prototype._toPixels = function(a) { return this._toPixelsRelative(a - (0 === this._type ? this._rangeStart : this._windowStart)); }; - a.prototype._toTimeRelative = function(a) { + b.prototype._toTimeRelative = function(a) { return a * (0 === this._type ? this._rangeEnd - this._rangeStart : this._windowEnd - this._windowStart) / this._width; }; - a.prototype._toTime = function(a) { + b.prototype._toTime = function(a) { return this._toTimeRelative(a) + (0 === this._type ? this._rangeStart : this._windowStart); }; - a.prototype._getDragTargetUnderCursor = function(a, g) { - if (0 <= g && g < this._height) { + b.prototype._getDragTargetUnderCursor = function(a, b) { + if (0 <= b && b < this._height) { if (0 === this._type) { - var d = this._toPixels(this._windowStart), e = this._toPixels(this._windowEnd), k = 2 + m.FlameChartBase.DRAGHANDLE_WIDTH / 2, d = a >= d - k && a <= d + k, e = a >= e - k && a <= e + k; - if (d && e) { + var c = this._toPixels(this._windowStart), k = this._toPixels(this._windowEnd), g = 2 + e.FlameChartBase.DRAGHANDLE_WIDTH / 2, c = a >= c - g && a <= c + g, k = a >= k - g && a <= k + g; + if (c && k) { return 4; } - if (d) { + if (c) { return 2; } - if (e) { + if (k) { return 3; } } @@ -5497,134 +5816,134 @@ } return 0; }; - a.prototype.onMouseDown = function(a, g) { - var d = this._getDragTargetUnderCursor(a, g); - 1 === d && this._mouseController.updateCursor(m.MouseCursor.GRABBING); - this._dragInfo = {windowStartInitial:this._windowStart, windowEndInitial:this._windowEnd, target:d}; - }; - a.prototype.onMouseMove = function(a, g) { - var d = m.MouseCursor.DEFAULT, e = this._getDragTargetUnderCursor(a, g); - 0 !== e && (1 !== e ? d = m.MouseCursor.EW_RESIZE : 1 !== e || this._windowEqRange() || (d = m.MouseCursor.GRAB)); - this._mouseController.updateCursor(d); + b.prototype.onMouseDown = function(a, b) { + var c = this._getDragTargetUnderCursor(a, b); + 1 === c && this._mouseController.updateCursor(e.MouseCursor.GRABBING); + this._dragInfo = {windowStartInitial:this._windowStart, windowEndInitial:this._windowEnd, target:c}; + }; + b.prototype.onMouseMove = function(a, b) { + var c = e.MouseCursor.DEFAULT, k = this._getDragTargetUnderCursor(a, b); + 0 !== k && (1 !== k ? c = e.MouseCursor.EW_RESIZE : 1 !== k || this._windowEqRange() || (c = e.MouseCursor.GRAB)); + this._mouseController.updateCursor(c); }; - a.prototype.onMouseOver = function(a, g) { - this.onMouseMove(a, g); + b.prototype.onMouseOver = function(a, b) { + this.onMouseMove(a, b); }; - a.prototype.onMouseOut = function() { - this._mouseController.updateCursor(m.MouseCursor.DEFAULT); + b.prototype.onMouseOut = function() { + this._mouseController.updateCursor(e.MouseCursor.DEFAULT); }; - a.prototype.onDrag = function(a, g, d, k, u, l) { + b.prototype.onDrag = function(a, b, p, k, g, l) { a = this._dragInfo; if (4 === a.target) { - if (0 !== u) { - a.target = 0 > u ? 2 : 3; + if (0 !== g) { + a.target = 0 > g ? 2 : 3; } else { return; } } - g = this._windowStart; - d = this._windowEnd; - u = this._toTimeRelative(u); + b = this._windowStart; + p = this._windowEnd; + g = this._toTimeRelative(g); switch(a.target) { case 1: - d = 0 === this._type ? 1 : -1; - g = a.windowStartInitial + d * u; - d = a.windowEndInitial + d * u; + p = 0 === this._type ? 1 : -1; + b = a.windowStartInitial + p * g; + p = a.windowEndInitial + p * g; break; case 2: - g = e(a.windowStartInitial + u, this._rangeStart, d - m.FlameChartBase.MIN_WINDOW_LEN); + b = c(a.windowStartInitial + g, this._rangeStart, p - e.FlameChartBase.MIN_WINDOW_LEN); break; case 3: - d = e(a.windowEndInitial + u, g + m.FlameChartBase.MIN_WINDOW_LEN, this._rangeEnd); + p = c(a.windowEndInitial + g, b + e.FlameChartBase.MIN_WINDOW_LEN, this._rangeEnd); break; default: return; } - this._controller.setWindow(g, d); + this._controller.setWindow(b, p); }; - a.prototype.onDragEnd = function(a, g, d, e, k, l) { + b.prototype.onDragEnd = function(a, b, c, e, k, l) { this._dragInfo = null; - this.onMouseMove(d, e); + this.onMouseMove(c, e); }; - a.prototype.onClick = function(a, g) { - 1 === this._dragInfo.target && this._mouseController.updateCursor(m.MouseCursor.GRAB); + b.prototype.onClick = function(a, b) { + 1 === this._dragInfo.target && this._mouseController.updateCursor(e.MouseCursor.GRAB); }; - a.prototype.onHoverStart = function(a, g) { + b.prototype.onHoverStart = function(a, b) { }; - a.prototype.onHoverEnd = function() { + b.prototype.onHoverEnd = function() { }; - a.TICK_MAX_WIDTH = 75; - return a; - }(m.FlameChartBase); - m.FlameChartHeader = t; + b.TICK_MAX_WIDTH = 75; + return b; + }(e.FlameChartBase); + e.FlameChartHeader = m; })(m.Profiler || (m.Profiler = {})); - })(d.Tools || (d.Tools = {})); + })(g.Tools || (g.Tools = {})); })(Shumway || (Shumway = {})); -(function(d) { - (function(d) { - (function(d) { - (function(d) { - var m = function() { - function a(a, g, d, e, k) { +(function(g) { + (function(g) { + (function(e) { + (function(c) { + var e = function() { + function b(a, b, c, e, k) { this.pageLoaded = a; - this.threadsTotal = g; - this.threadsLoaded = d; + this.threadsTotal = b; + this.threadsLoaded = c; this.threadFilesTotal = e; this.threadFilesLoaded = k; } - a.prototype.toString = function() { - return "[" + ["pageLoaded", "threadsTotal", "threadsLoaded", "threadFilesTotal", "threadFilesLoaded"].map(function(a, g, d) { + b.prototype.toString = function() { + return "[" + ["pageLoaded", "threadsTotal", "threadsLoaded", "threadFilesTotal", "threadFilesLoaded"].map(function(a, b, c) { return a + ":" + this[a]; }, this).join(", ") + "]"; }; - return a; + return b; }(); - d.TraceLoggerProgressInfo = m; + c.TraceLoggerProgressInfo = e; var k = function() { - function a(a) { + function b(a) { this._baseUrl = a; this._threads = []; this._progressInfo = null; } - a.prototype.loadPage = function(a, g, d) { + b.prototype.loadPage = function(a, b, c) { this._threads = []; - this._pageLoadCallback = g; - this._pageLoadProgressCallback = d; - this._progressInfo = new m(!1, 0, 0, 0, 0); + this._pageLoadCallback = b; + this._pageLoadProgressCallback = c; + this._progressInfo = new e(!1, 0, 0, 0, 0); this._loadData([a], this._onLoadPage.bind(this)); }; - Object.defineProperty(a.prototype, "buffers", {get:function() { - for (var a = [], g = 0, d = this._threads.length;g < d;g++) { - a.push(this._threads[g].buffer); + Object.defineProperty(b.prototype, "buffers", {get:function() { + for (var a = [], b = 0, c = this._threads.length;b < c;b++) { + a.push(this._threads[b].buffer); } return a; }, enumerable:!0, configurable:!0}); - a.prototype._onProgress = function() { + b.prototype._onProgress = function() { this._pageLoadProgressCallback && this._pageLoadProgressCallback.call(this, this._progressInfo); }; - a.prototype._onLoadPage = function(a) { + b.prototype._onLoadPage = function(a) { if (a && 1 == a.length) { - var g = this, p = 0; + var b = this, e = 0; a = a[0]; var k = a.length; this._threads = Array(k); this._progressInfo.pageLoaded = !0; this._progressInfo.threadsTotal = k; - for (var m = 0;m < a.length;m++) { - var l = a[m], r = [l.dict, l.tree]; - l.corrections && r.push(l.corrections); - this._progressInfo.threadFilesTotal += r.length; - this._loadData(r, function(a) { - return function(c) { - c && (c = new d.Thread(c), c.buffer.name = "Thread " + a, g._threads[a] = c); - p++; - g._progressInfo.threadsLoaded++; - g._onProgress(); - p === k && g._pageLoadCallback.call(g, null, g._threads); + for (var g = 0;g < a.length;g++) { + var l = a[g], t = [l.dict, l.tree]; + l.corrections && t.push(l.corrections); + this._progressInfo.threadFilesTotal += t.length; + this._loadData(t, function(a) { + return function(l) { + l && (l = new c.Thread(l), l.buffer.name = "Thread " + a, b._threads[a] = l); + e++; + b._progressInfo.threadsLoaded++; + b._onProgress(); + e === k && b._pageLoadCallback.call(b, null, b._threads); }; - }(m), function(a) { - g._progressInfo.threadFilesLoaded++; - g._onProgress(); + }(g), function(a) { + b._progressInfo.threadFilesLoaded++; + b._onProgress(); }); } this._onProgress(); @@ -5632,87 +5951,87 @@ this._pageLoadCallback.call(this, "Error loading page.", null); } }; - a.prototype._loadData = function(a, g, d) { - var e = 0, k = 0, l = a.length, r = []; - r.length = l; - for (var b = 0;b < l;b++) { - var h = this._baseUrl + a[b], m = new XMLHttpRequest, n = /\.tl$/i.test(h) ? "arraybuffer" : "json"; - m.open("GET", h, !0); - m.responseType = n; - m.onload = function(a, b) { - return function(c) { - if ("json" === b) { - if (c = this.response, "string" === typeof c) { + b.prototype._loadData = function(a, b, c) { + var e = 0, k = 0, l = a.length, g = []; + g.length = l; + for (var r = 0;r < l;r++) { + var m = this._baseUrl + a[r], h = new XMLHttpRequest, f = /\.tl$/i.test(m) ? "arraybuffer" : "json"; + h.open("GET", m, !0); + h.responseType = f; + h.onload = function(a, f) { + return function(h) { + if ("json" === f) { + if (h = this.response, "string" === typeof h) { try { - c = JSON.parse(c), r[a] = c; - } catch (n) { + h = JSON.parse(h), g[a] = h; + } catch (s) { k++; } } else { - r[a] = c; + g[a] = h; } } else { - r[a] = this.response; + g[a] = this.response; } ++e; - d && d(e); - e === l && g(r); + c && c(e); + e === l && b(g); }; - }(b, n); - m.send(); + }(r, f); + h.send(); } }; - a.colors = "#0044ff #8c4b00 #cc5c33 #ff80c4 #ffbfd9 #ff8800 #8c5e00 #adcc33 #b380ff #bfd9ff #ffaa00 #8c0038 #bf8f30 #f780ff #cc99c9 #aaff00 #000073 #452699 #cc8166 #cca799 #000066 #992626 #cc6666 #ccc299 #ff6600 #526600 #992663 #cc6681 #99ccc2 #ff0066 #520066 #269973 #61994d #739699 #ffcc00 #006629 #269199 #94994d #738299 #ff0000 #590000 #234d8c #8c6246 #7d7399 #ee00ff #00474d #8c2385 #8c7546 #7c8c69 #eeff00 #4d003d #662e1a #62468c #8c6969 #6600ff #4c2900 #1a6657 #8c464f #8c6981 #44ff00 #401100 #1a2466 #663355 #567365 #d90074 #403300 #101d40 #59562d #66614d #cc0000 #002b40 #234010 #4c2626 #4d5e66 #00a3cc #400011 #231040 #4c3626 #464359 #0000bf #331b00 #80e6ff #311a33 #4d3939 #a69b00 #003329 #80ffb2 #331a20 #40303d #00a658 #40ffd9 #ffc480 #ffe1bf #332b26 #8c2500 #9933cc #80fff6 #ffbfbf #303326 #005e8c #33cc47 #b2ff80 #c8bfff #263332 #00708c #cc33ad #ffe680 #f2ffbf #262a33 #388c00 #335ccc #8091ff #bfffd9".split(" "); - return a; + b.colors = "#0044ff #8c4b00 #cc5c33 #ff80c4 #ffbfd9 #ff8800 #8c5e00 #adcc33 #b380ff #bfd9ff #ffaa00 #8c0038 #bf8f30 #f780ff #cc99c9 #aaff00 #000073 #452699 #cc8166 #cca799 #000066 #992626 #cc6666 #ccc299 #ff6600 #526600 #992663 #cc6681 #99ccc2 #ff0066 #520066 #269973 #61994d #739699 #ffcc00 #006629 #269199 #94994d #738299 #ff0000 #590000 #234d8c #8c6246 #7d7399 #ee00ff #00474d #8c2385 #8c7546 #7c8c69 #eeff00 #4d003d #662e1a #62468c #8c6969 #6600ff #4c2900 #1a6657 #8c464f #8c6981 #44ff00 #401100 #1a2466 #663355 #567365 #d90074 #403300 #101d40 #59562d #66614d #cc0000 #002b40 #234010 #4c2626 #4d5e66 #00a3cc #400011 #231040 #4c3626 #464359 #0000bf #331b00 #80e6ff #311a33 #4d3939 #a69b00 #003329 #80ffb2 #331a20 #40303d #00a658 #40ffd9 #ffc480 #ffe1bf #332b26 #8c2500 #9933cc #80fff6 #ffbfbf #303326 #005e8c #33cc47 #b2ff80 #c8bfff #263332 #00708c #cc33ad #ffe680 #f2ffbf #262a33 #388c00 #335ccc #8091ff #bfffd9".split(" "); + return b; }(); - d.TraceLogger = k; - })(d.TraceLogger || (d.TraceLogger = {})); - })(d.Profiler || (d.Profiler = {})); - })(d.Tools || (d.Tools = {})); -})(Shumway || (Shumway = {})); -(function(d) { - (function(d) { - (function(d) { - (function(e) { - var m; - (function(d) { - d[d.START_HI = 0] = "START_HI"; - d[d.START_LO = 4] = "START_LO"; - d[d.STOP_HI = 8] = "STOP_HI"; - d[d.STOP_LO = 12] = "STOP_LO"; - d[d.TEXTID = 16] = "TEXTID"; - d[d.NEXTID = 20] = "NEXTID"; - })(m || (m = {})); - m = function() { - function e(a) { - 2 <= a.length && (this._text = a[0], this._data = new DataView(a[1]), this._buffer = new d.TimelineBuffer, this._walkTree(0)); + c.TraceLogger = k; + })(e.TraceLogger || (e.TraceLogger = {})); + })(g.Profiler || (g.Profiler = {})); + })(g.Tools || (g.Tools = {})); +})(Shumway || (Shumway = {})); +(function(g) { + (function(g) { + (function(e) { + (function(c) { + var g; + (function(c) { + c[c.START_HI = 0] = "START_HI"; + c[c.START_LO = 4] = "START_LO"; + c[c.STOP_HI = 8] = "STOP_HI"; + c[c.STOP_LO = 12] = "STOP_LO"; + c[c.TEXTID = 16] = "TEXTID"; + c[c.NEXTID = 20] = "NEXTID"; + })(g || (g = {})); + g = function() { + function c(b) { + 2 <= b.length && (this._text = b[0], this._data = new DataView(b[1]), this._buffer = new e.TimelineBuffer, this._walkTree(0)); } - Object.defineProperty(e.prototype, "buffer", {get:function() { + Object.defineProperty(c.prototype, "buffer", {get:function() { return this._buffer; }, enumerable:!0, configurable:!0}); - e.prototype._walkTree = function(a) { - var c = this._data, g = this._buffer; + c.prototype._walkTree = function(b) { + var a = this._data, e = this._buffer; do { - var d = a * e.ITEM_SIZE, m = 4294967295 * c.getUint32(d + 0) + c.getUint32(d + 4), u = 4294967295 * c.getUint32(d + 8) + c.getUint32(d + 12), l = c.getUint32(d + 16), d = c.getUint32(d + 20), r = 1 === (l & 1), l = l >>> 1, l = this._text[l]; - g.enter(l, null, m / 1E6); - r && this._walkTree(a + 1); - g.leave(l, null, u / 1E6); - a = d; - } while (0 !== a); + var g = b * c.ITEM_SIZE, m = 4294967295 * a.getUint32(g + 0) + a.getUint32(g + 4), v = 4294967295 * a.getUint32(g + 8) + a.getUint32(g + 12), l = a.getUint32(g + 16), g = a.getUint32(g + 20), t = 1 === (l & 1), l = l >>> 1, l = this._text[l]; + e.enter(l, null, m / 1E6); + t && this._walkTree(b + 1); + e.leave(l, null, v / 1E6); + b = g; + } while (0 !== b); }; - e.ITEM_SIZE = 24; - return e; + c.ITEM_SIZE = 24; + return c; }(); - e.Thread = m; - })(d.TraceLogger || (d.TraceLogger = {})); - })(d.Profiler || (d.Profiler = {})); - })(d.Tools || (d.Tools = {})); + c.Thread = g; + })(e.TraceLogger || (e.TraceLogger = {})); + })(g.Profiler || (g.Profiler = {})); + })(g.Tools || (g.Tools = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - (function(m) { - var e = d.NumberUtilities.clamp, t = function() { - function a() { + (function(e) { + var c = g.NumberUtilities.clamp, m = function() { + function b() { this.length = 0; this.lines = []; this.format = []; @@ -5720,27 +6039,27 @@ this.repeat = []; this.length = 0; } - a.prototype.append = function(a, d) { - var e = this.lines; - 0 < e.length && e[e.length - 1] === a ? this.repeat[e.length - 1]++ : (this.lines.push(a), this.repeat.push(1), this.format.push(d ? {backgroundFillStyle:d} : void 0), this.time.push(performance.now()), this.length++); + b.prototype.append = function(a, b) { + var c = this.lines; + 0 < c.length && c[c.length - 1] === a ? this.repeat[c.length - 1]++ : (this.lines.push(a), this.repeat.push(1), this.format.push(b ? {backgroundFillStyle:b} : void 0), this.time.push(performance.now()), this.length++); }; - a.prototype.get = function(a) { + b.prototype.get = function(a) { return this.lines[a]; }; - a.prototype.getFormat = function(a) { + b.prototype.getFormat = function(a) { return this.format[a]; }; - a.prototype.getTime = function(a) { + b.prototype.getTime = function(a) { return this.time[a]; }; - a.prototype.getRepeat = function(a) { + b.prototype.getRepeat = function(a) { return this.repeat[a]; }; - return a; + return b; }(); - m.Buffer = t; + e.Buffer = m; var k = function() { - function a(a) { + function b(a) { this.lineColor = "#2A2A2A"; this.alternateLineColor = "#262626"; this.textColor = "#FFFFFF"; @@ -5762,63 +6081,63 @@ this._resizeHandler(); this.textMarginBottom = this.textMarginLeft = 4; this.refreshFrequency = 0; - this.buffer = new t; + this.buffer = new m; a.addEventListener("keydown", function(a) { - var c = 0; + var s = 0; switch(a.keyCode) { - case f: + case d: this.showLineNumbers = !this.showLineNumbers; break; case q: this.showLineTime = !this.showLineTime; break; case l: - c = -1; + s = -1; break; - case r: - c = 1; + case k: + s = 1; break; - case d: - c = -this.pageLineCount; + case b: + s = -this.pageLineCount; break; - case e: - c = this.pageLineCount; + case c: + s = this.pageLineCount; break; - case k: - c = -this.lineIndex; + case e: + s = -this.lineIndex; break; - case m: - c = this.buffer.length - this.lineIndex; + case g: + s = this.buffer.length - this.lineIndex; break; - case b: + case r: this.columnIndex -= a.metaKey ? 10 : 1; 0 > this.columnIndex && (this.columnIndex = 0); a.preventDefault(); break; - case h: + case u: this.columnIndex += a.metaKey ? 10 : 1; a.preventDefault(); break; - case w: + case h: a.metaKey && (this.selection = {start:0, end:this.buffer.length}, a.preventDefault()); break; - case n: + case f: if (a.metaKey) { - var I = ""; + var O = ""; if (this.selection) { - for (var B = this.selection.start;B <= this.selection.end;B++) { - I += this.buffer.get(B) + "\n"; + for (var T = this.selection.start;T <= this.selection.end;T++) { + O += this.buffer.get(T) + "\n"; } } else { - I = this.buffer.get(this.lineIndex); + O = this.buffer.get(this.lineIndex); } - alert(I); + alert(O); } ; } - a.metaKey && (c *= this.pageLineCount); - c && (this.scroll(c), a.preventDefault()); - c && a.shiftKey ? this.selection ? this.lineIndex > this.selection.start ? this.selection.end = this.lineIndex : this.selection.start = this.lineIndex : 0 < c ? this.selection = {start:this.lineIndex - c, end:this.lineIndex} : 0 > c && (this.selection = {start:this.lineIndex, end:this.lineIndex - c}) : c && (this.selection = null); + a.metaKey && (s *= this.pageLineCount); + s && (this.scroll(s), a.preventDefault()); + s && a.shiftKey ? this.selection ? this.lineIndex > this.selection.start ? this.selection.end = this.lineIndex : this.selection.start = this.lineIndex : 0 < s ? this.selection = {start:this.lineIndex - s, end:this.lineIndex} : 0 > s && (this.selection = {start:this.lineIndex, end:this.lineIndex - s}) : s && (this.selection = null); this.paint(); }.bind(this), !1); a.addEventListener("focus", function(a) { @@ -5827,193 +6146,216 @@ a.addEventListener("blur", function(a) { this.hasFocus = !1; }.bind(this), !1); - var d = 33, e = 34, k = 36, m = 35, l = 38, r = 40, b = 37, h = 39, w = 65, n = 67, f = 78, q = 84; + var b = 33, c = 34, e = 36, g = 35, l = 38, k = 40, r = 37, u = 39, h = 65, f = 67, d = 78, q = 84; } - a.prototype.resize = function() { + b.prototype.resize = function() { this._resizeHandler(); }; - a.prototype._resizeHandler = function() { - var a = this.canvas.parentElement, d = a.clientWidth, a = a.clientHeight - 1, e = window.devicePixelRatio || 1; - 1 !== e ? (this.ratio = e / 1, this.canvas.width = d * this.ratio, this.canvas.height = a * this.ratio, this.canvas.style.width = d + "px", this.canvas.style.height = a + "px") : (this.ratio = 1, this.canvas.width = d, this.canvas.height = a); + b.prototype._resizeHandler = function() { + var a = this.canvas.parentElement, b = a.clientWidth, a = a.clientHeight - 1, c = window.devicePixelRatio || 1; + 1 !== c ? (this.ratio = c / 1, this.canvas.width = b * this.ratio, this.canvas.height = a * this.ratio, this.canvas.style.width = b + "px", this.canvas.style.height = a + "px") : (this.ratio = 1, this.canvas.width = b, this.canvas.height = a); this.pageLineCount = Math.floor(this.canvas.height / this.lineHeight); }; - a.prototype.gotoLine = function(a) { - this.lineIndex = e(a, 0, this.buffer.length - 1); + b.prototype.gotoLine = function(a) { + this.lineIndex = c(a, 0, this.buffer.length - 1); }; - a.prototype.scrollIntoView = function() { + b.prototype.scrollIntoView = function() { this.lineIndex < this.pageIndex ? this.pageIndex = this.lineIndex : this.lineIndex >= this.pageIndex + this.pageLineCount && (this.pageIndex = this.lineIndex - this.pageLineCount + 1); }; - a.prototype.scroll = function(a) { + b.prototype.scroll = function(a) { this.gotoLine(this.lineIndex + a); this.scrollIntoView(); }; - a.prototype.paint = function() { + b.prototype.paint = function() { var a = this.pageLineCount; this.pageIndex + a > this.buffer.length && (a = this.buffer.length - this.pageIndex); - var d = this.textMarginLeft, e = d + (this.showLineNumbers ? 5 * (String(this.buffer.length).length + 2) : 0), k = e + (this.showLineTime ? 40 : 10), m = k + 25; + var b = this.textMarginLeft, c = b + (this.showLineNumbers ? 5 * (String(this.buffer.length).length + 2) : 0), e = c + (this.showLineTime ? 40 : 10), g = e + 25; this.context.font = this.fontSize + 'px Consolas, "Liberation Mono", Courier, monospace'; this.context.setTransform(this.ratio, 0, 0, this.ratio, 0, 0); - for (var l = this.canvas.width, r = this.lineHeight, b = 0;b < a;b++) { - var h = b * this.lineHeight, w = this.pageIndex + b, n = this.buffer.get(w), f = this.buffer.getFormat(w), q = this.buffer.getRepeat(w), U = 1 < w ? this.buffer.getTime(w) - this.buffer.getTime(0) : 0; - this.context.fillStyle = w % 2 ? this.lineColor : this.alternateLineColor; - f && f.backgroundFillStyle && (this.context.fillStyle = f.backgroundFillStyle); - this.context.fillRect(0, h, l, r); + for (var l = this.canvas.width, k = this.lineHeight, r = 0;r < a;r++) { + var m = r * this.lineHeight, h = this.pageIndex + r, f = this.buffer.get(h), d = this.buffer.getFormat(h), q = this.buffer.getRepeat(h), x = 1 < h ? this.buffer.getTime(h) - this.buffer.getTime(0) : 0; + this.context.fillStyle = h % 2 ? this.lineColor : this.alternateLineColor; + d && d.backgroundFillStyle && (this.context.fillStyle = d.backgroundFillStyle); + this.context.fillRect(0, m, l, k); this.context.fillStyle = this.selectionTextColor; this.context.fillStyle = this.textColor; - this.selection && w >= this.selection.start && w <= this.selection.end && (this.context.fillStyle = this.selectionColor, this.context.fillRect(0, h, l, r), this.context.fillStyle = this.selectionTextColor); - this.hasFocus && w === this.lineIndex && (this.context.fillStyle = this.selectionColor, this.context.fillRect(0, h, l, r), this.context.fillStyle = this.selectionTextColor); - 0 < this.columnIndex && (n = n.substring(this.columnIndex)); - h = (b + 1) * this.lineHeight - this.textMarginBottom; - this.showLineNumbers && this.context.fillText(String(w), d, h); - this.showLineTime && this.context.fillText(U.toFixed(1).padLeft(" ", 6), e, h); - 1 < q && this.context.fillText(String(q).padLeft(" ", 3), k, h); - this.context.fillText(n, m, h); - } - }; - a.prototype.refreshEvery = function(a) { - function d() { - e.paint(); - e.refreshFrequency && setTimeout(d, e.refreshFrequency); + this.selection && h >= this.selection.start && h <= this.selection.end && (this.context.fillStyle = this.selectionColor, this.context.fillRect(0, m, l, k), this.context.fillStyle = this.selectionTextColor); + this.hasFocus && h === this.lineIndex && (this.context.fillStyle = this.selectionColor, this.context.fillRect(0, m, l, k), this.context.fillStyle = this.selectionTextColor); + 0 < this.columnIndex && (f = f.substring(this.columnIndex)); + m = (r + 1) * this.lineHeight - this.textMarginBottom; + this.showLineNumbers && this.context.fillText(String(h), b, m); + this.showLineTime && this.context.fillText(x.toFixed(1).padLeft(" ", 6), c, m); + 1 < q && this.context.fillText(String(q).padLeft(" ", 3), e, m); + this.context.fillText(f, g, m); + } + }; + b.prototype.refreshEvery = function(a) { + function b() { + c.paint(); + c.refreshFrequency && setTimeout(b, c.refreshFrequency); } - var e = this; + var c = this; this.refreshFrequency = a; - e.refreshFrequency && setTimeout(d, e.refreshFrequency); + c.refreshFrequency && setTimeout(b, c.refreshFrequency); }; - a.prototype.isScrolledToBottom = function() { + b.prototype.isScrolledToBottom = function() { return this.lineIndex === this.buffer.length - 1; }; - return a; + return b; }(); - m.Terminal = k; + e.Terminal = k; })(m.Terminal || (m.Terminal = {})); - })(d.Tools || (d.Tools = {})); + })(g.Tools || (g.Tools = {})); })(Shumway || (Shumway = {})); -(function(d) { - (function(d) { - (function(d) { - var e = function() { - function d(e) { +(function(g) { + (function(g) { + (function(e) { + var c = function() { + function c(e) { this._lastWeightedTime = this._lastTime = this._index = 0; this._gradient = "#FF0000 #FF1100 #FF2300 #FF3400 #FF4600 #FF5700 #FF6900 #FF7B00 #FF8C00 #FF9E00 #FFAF00 #FFC100 #FFD300 #FFE400 #FFF600 #F7FF00 #E5FF00 #D4FF00 #C2FF00 #B0FF00 #9FFF00 #8DFF00 #7CFF00 #6AFF00 #58FF00 #47FF00 #35FF00 #24FF00 #12FF00 #00FF00".split(" "); - this._canvas = e; - this._context = e.getContext("2d"); - window.addEventListener("resize", this._resizeHandler.bind(this), !1); - this._resizeHandler(); + this._container = e; + this._canvas = document.createElement("canvas"); + this._container.appendChild(this._canvas); + this._context = this._canvas.getContext("2d"); + this._listenForContainerSizeChanges(); } - d.prototype._resizeHandler = function() { - var d = this._canvas.parentElement, a = d.clientWidth, d = d.clientHeight - 1, c = window.devicePixelRatio || 1; - 1 !== c ? (this._ratio = c / 1, this._canvas.width = a * this._ratio, this._canvas.height = d * this._ratio, this._canvas.style.width = a + "px", this._canvas.style.height = d + "px") : (this._ratio = 1, this._canvas.width = a, this._canvas.height = d); + c.prototype._listenForContainerSizeChanges = function() { + var c = this._containerWidth, b = this._containerHeight; + this._onContainerSizeChanged(); + var a = this; + setInterval(function() { + if (c !== a._containerWidth || b !== a._containerHeight) { + a._onContainerSizeChanged(), c = a._containerWidth, b = a._containerHeight; + } + }, 10); + }; + c.prototype._onContainerSizeChanged = function() { + var c = this._containerWidth, b = this._containerHeight, a = window.devicePixelRatio || 1; + 1 !== a ? (this._ratio = a / 1, this._canvas.width = c * this._ratio, this._canvas.height = b * this._ratio, this._canvas.style.width = c + "px", this._canvas.style.height = b + "px") : (this._ratio = 1, this._canvas.width = c, this._canvas.height = b); }; - d.prototype.tickAndRender = function(d) { - "undefined" === typeof d && (d = !1); + Object.defineProperty(c.prototype, "_containerWidth", {get:function() { + return this._container.clientWidth; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "_containerHeight", {get:function() { + return this._container.clientHeight; + }, enumerable:!0, configurable:!0}); + c.prototype.tickAndRender = function(c, b) { + void 0 === c && (c = !1); + void 0 === b && (b = 0); if (0 === this._lastTime) { this._lastTime = performance.now(); } else { - var a = (performance.now() - this._lastTime) * (1 - .9) + .9 * this._lastWeightedTime, c = this._context, g = 2 * this._ratio, e = (this._canvas.width - 20) / (g + 1) | 0, m = this._index++; - this._index > e && (this._index = 0); - c.globalAlpha = 1; - c.fillStyle = "black"; - c.fillRect(20 + m * (g + 1), 0, 4 * g, this._canvas.height); - e = 1E3 / 60 / a; - c.fillStyle = this._gradient[e * (this._gradient.length - 1) | 0]; - c.globalAlpha = d ? .5 : 1; - c.fillRect(20 + m * (g + 1), 0, g, this._canvas.height * e | 0); - 0 === m % 16 && (c.globalAlpha = 1, c.fillStyle = "black", c.fillRect(0, 0, 20, this._canvas.height), c.fillStyle = "white", c.font = "10px Arial", c.fillText((1E3 / a).toFixed(0), 2, 8)); + var a = 1 * (performance.now() - this._lastTime) + 0 * this._lastWeightedTime, e = this._context, g = 2 * this._ratio, m = 30 * this._ratio, v = performance; + v.memory && (m += 30 * this._ratio); + var l = (this._canvas.width - m) / (g + 1) | 0, t = this._index++; + this._index > l && (this._index = 0); + l = this._canvas.height; + e.globalAlpha = 1; + e.fillStyle = "black"; + e.fillRect(m + t * (g + 1), 0, 4 * g, this._canvas.height); + var r = Math.min(1E3 / 60 / a, 1); + e.fillStyle = "#00FF00"; + e.globalAlpha = c ? .5 : 1; + r = l / 2 * r | 0; + e.fillRect(m + t * (g + 1), l - r, g, r); + b && (r = Math.min(1E3 / 240 / b, 1), e.fillStyle = "#FF6347", r = l / 2 * r | 0, e.fillRect(m + t * (g + 1), l / 2 - r, g, r)); + 0 === t % 16 && (e.globalAlpha = 1, e.fillStyle = "black", e.fillRect(0, 0, m, this._canvas.height), e.fillStyle = "white", e.font = 8 * this._ratio + "px Arial", e.textBaseline = "middle", g = (1E3 / a).toFixed(0), b && (g += " " + b.toFixed(0)), v.memory && (g += " " + (v.memory.usedJSHeapSize / 1024 / 1024).toFixed(2)), e.fillText(g, 2 * this._ratio, this._containerHeight / 2 * this._ratio)); this._lastTime = performance.now(); this._lastWeightedTime = a; } }; - return d; + return c; }(); - d.FPS = e; - })(d.Mini || (d.Mini = {})); - })(d.Tools || (d.Tools = {})); + e.FPS = c; + })(g.Mini || (g.Mini = {})); + })(g.Tools || (g.Tools = {})); })(Shumway || (Shumway = {})); console.timeEnd("Load Shared Dependencies"); console.time("Load GFX Dependencies"); -(function(d) { +(function(g) { (function(m) { - function s(a, b, f) { - return p && f ? "string" === typeof b ? (a = d.ColorUtilities.cssStyleToRGBA(b), d.ColorUtilities.rgbaToCSSStyle(f.transformRGBA(a))) : b instanceof CanvasGradient && b._template ? b._template.createCanvasGradient(a, f) : b : b; + function e(a, d, f) { + return p && f ? "string" === typeof d ? (a = g.ColorUtilities.cssStyleToRGBA(d), g.ColorUtilities.rgbaToCSSStyle(f.transformRGBA(a))) : d instanceof CanvasGradient && d._template ? d._template.createCanvasGradient(a, f) : d : d; } - var e = d.Debug.assert, t = d.NumberUtilities.clamp; + var c = g.Debug.assert, w = g.NumberUtilities.clamp; (function(a) { a[a.None = 0] = "None"; a[a.Brief = 1] = "Brief"; a[a.Verbose = 2] = "Verbose"; })(m.TraceLevel || (m.TraceLevel = {})); - var k = d.Metrics.Counter.instance; - m.frameCounter = new d.Metrics.Counter(!0); + var k = g.Metrics.Counter.instance; + m.frameCounter = new g.Metrics.Counter(!0); m.traceLevel = 2; m.writer = null; m.frameCount = function(a) { k.count(a); m.frameCounter.count(a); }; - m.timelineBuffer = new d.Tools.Profiler.TimelineBuffer("GFX"); - m.enterTimeline = function(a, b) { + m.timelineBuffer = new g.Tools.Profiler.TimelineBuffer("GFX"); + m.enterTimeline = function(a, d) { }; - m.leaveTimeline = function(a, b) { + m.leaveTimeline = function(a, d) { }; - var a = null, c = null, g = null, p = !0; - p && "undefined" !== typeof CanvasRenderingContext2D && (a = CanvasGradient.prototype.addColorStop, c = CanvasRenderingContext2D.prototype.createLinearGradient, g = CanvasRenderingContext2D.prototype.createRadialGradient, CanvasRenderingContext2D.prototype.createLinearGradient = function(a, b, f, c) { - return(new u(a, b, f, c)).createCanvasGradient(this, null); - }, CanvasRenderingContext2D.prototype.createRadialGradient = function(a, b, f, c, d, q) { - return(new l(a, b, f, c, d, q)).createCanvasGradient(this, null); - }, CanvasGradient.prototype.addColorStop = function(b, f) { - a.call(this, b, f); - this._template.addColorStop(b, f); + var b = null, a = null, n = null, p = !0; + p && "undefined" !== typeof CanvasRenderingContext2D && (b = CanvasGradient.prototype.addColorStop, a = CanvasRenderingContext2D.prototype.createLinearGradient, n = CanvasRenderingContext2D.prototype.createRadialGradient, CanvasRenderingContext2D.prototype.createLinearGradient = function(a, d, f, b) { + return(new v(a, d, f, b)).createCanvasGradient(this, null); + }, CanvasRenderingContext2D.prototype.createRadialGradient = function(a, d, f, b, c, q) { + return(new l(a, d, f, b, c, q)).createCanvasGradient(this, null); + }, CanvasGradient.prototype.addColorStop = function(a, d) { + b.call(this, a, d); + this._template.addColorStop(a, d); }); - var v = function() { - return function(a, b) { + var y = function() { + return function(a, d) { this.offset = a; - this.color = b; + this.color = d; }; - }(), u = function() { - function b(a, f, c, d) { + }(), v = function() { + function d(a, f, b, c) { this.x0 = a; this.y0 = f; - this.x1 = c; - this.y1 = d; + this.x1 = b; + this.y1 = c; this.colorStops = []; } - b.prototype.addColorStop = function(a, b) { - this.colorStops.push(new v(a, b)); + d.prototype.addColorStop = function(a, d) { + this.colorStops.push(new y(a, d)); }; - b.prototype.createCanvasGradient = function(b, f) { - for (var d = c.call(b, this.x0, this.y0, this.x1, this.y1), q = this.colorStops, g = 0;g < q.length;g++) { - var n = q[g], e = n.offset, n = n.color, n = f ? s(b, n, f) : n; - a.call(d, e, n); + d.prototype.createCanvasGradient = function(d, f) { + for (var c = a.call(d, this.x0, this.y0, this.x1, this.y1), q = this.colorStops, h = 0;h < q.length;h++) { + var s = q[h], x = s.offset, s = s.color, s = f ? e(d, s, f) : s; + b.call(c, x, s); } - d._template = this; - d._transform = this._transform; - return d; + c._template = this; + c._transform = this._transform; + return c; }; - return b; + return d; }(), l = function() { - function b(a, f, c, d, q, g) { - this.x0 = a; + function a(d, f, b, c, q, h) { + this.x0 = d; this.y0 = f; - this.r0 = c; - this.x1 = d; + this.r0 = b; + this.x1 = c; this.y1 = q; - this.r1 = g; + this.r1 = h; this.colorStops = []; } - b.prototype.addColorStop = function(a, b) { - this.colorStops.push(new v(a, b)); + a.prototype.addColorStop = function(a, d) { + this.colorStops.push(new y(a, d)); }; - b.prototype.createCanvasGradient = function(b, f) { - for (var c = g.call(b, this.x0, this.y0, this.r0, this.x1, this.y1, this.r1), d = this.colorStops, q = 0;q < d.length;q++) { - var n = d[q], e = n.offset, n = n.color, n = f ? s(b, n, f) : n; - a.call(c, e, n); + a.prototype.createCanvasGradient = function(a, d) { + for (var f = n.call(a, this.x0, this.y0, this.r0, this.x1, this.y1, this.r1), c = this.colorStops, q = 0;q < c.length;q++) { + var h = c[q], s = h.offset, h = h.color, h = d ? e(a, h, d) : h; + b.call(f, s, h); } - c._template = this; - c._transform = this._transform; - return c; + f._template = this; + f._transform = this._transform; + return f; }; - return b; - }(), r; + return a; + }(), t; (function(a) { a[a.ClosePath = 1] = "ClosePath"; a[a.MoveTo = 2] = "MoveTo"; @@ -6026,391 +6368,403 @@ a[a.Save = 9] = "Save"; a[a.Restore = 10] = "Restore"; a[a.Transform = 11] = "Transform"; - })(r || (r = {})); - var b = function() { - function a(b) { + })(t || (t = {})); + var r = function() { + function a(d) { this._commands = new Uint8Array(a._arrayBufferPool.acquire(8), 0, 8); this._commandPosition = 0; this._data = new Float64Array(a._arrayBufferPool.acquire(8 * Float64Array.BYTES_PER_ELEMENT), 0, 8); this._dataPosition = 0; - b instanceof a && this.addPath(b); + d instanceof a && this.addPath(d); } - a._apply = function(a, b) { - var f = a._commands, c = a._data, d = 0, q = 0; - b.beginPath(); - for (var g = a._commandPosition;d < g;) { - switch(f[d++]) { + a._apply = function(a, d) { + var f = a._commands, b = a._data, c = 0, q = 0; + d.beginPath(); + for (var h = a._commandPosition;c < h;) { + switch(f[c++]) { case 1: - b.closePath(); + d.closePath(); break; case 2: - b.moveTo(c[q++], c[q++]); + d.moveTo(b[q++], b[q++]); break; case 3: - b.lineTo(c[q++], c[q++]); + d.lineTo(b[q++], b[q++]); break; case 4: - b.quadraticCurveTo(c[q++], c[q++], c[q++], c[q++]); + d.quadraticCurveTo(b[q++], b[q++], b[q++], b[q++]); break; case 5: - b.bezierCurveTo(c[q++], c[q++], c[q++], c[q++], c[q++], c[q++]); + d.bezierCurveTo(b[q++], b[q++], b[q++], b[q++], b[q++], b[q++]); break; case 6: - b.arcTo(c[q++], c[q++], c[q++], c[q++], c[q++]); + d.arcTo(b[q++], b[q++], b[q++], b[q++], b[q++]); break; case 7: - b.rect(c[q++], c[q++], c[q++], c[q++]); + d.rect(b[q++], b[q++], b[q++], b[q++]); break; case 8: - b.arc(c[q++], c[q++], c[q++], c[q++], c[q++], !!c[q++]); + d.arc(b[q++], b[q++], b[q++], b[q++], b[q++], !!b[q++]); break; case 9: - b.save(); + d.save(); break; case 10: - b.restore(); + d.restore(); break; case 11: - b.transform(c[q++], c[q++], c[q++], c[q++], c[q++], c[q++]); + d.transform(b[q++], b[q++], b[q++], b[q++], b[q++], b[q++]); } } }; - a.prototype._ensureCommandCapacity = function(b) { - this._commands = a._arrayBufferPool.ensureUint8ArrayLength(this._commands, b); + a.prototype._ensureCommandCapacity = function(d) { + this._commands = a._arrayBufferPool.ensureUint8ArrayLength(this._commands, d); }; - a.prototype._ensureDataCapacity = function(b) { - this._data = a._arrayBufferPool.ensureFloat64ArrayLength(this._data, b); + a.prototype._ensureDataCapacity = function(d) { + this._data = a._arrayBufferPool.ensureFloat64ArrayLength(this._data, d); }; a.prototype._writeCommand = function(a) { this._commandPosition >= this._commands.length && this._ensureCommandCapacity(this._commandPosition + 1); this._commands[this._commandPosition++] = a; }; - a.prototype._writeData = function(a, b, f, c, q, d) { - var g = arguments.length; - e(6 >= g && (0 === g % 2 || 5 === g)); - this._dataPosition + g >= this._data.length && this._ensureDataCapacity(this._dataPosition + g); - var n = this._data, h = this._dataPosition; - n[h] = a; - n[h + 1] = b; - 2 < g && (n[h + 2] = f, n[h + 3] = c, 4 < g && (n[h + 4] = q, 6 === g && (n[h + 5] = d))); - this._dataPosition += g; + a.prototype._writeData = function(a, d, f, b, q, h) { + var s = arguments.length; + c(6 >= s && (0 === s % 2 || 5 === s)); + this._dataPosition + s >= this._data.length && this._ensureDataCapacity(this._dataPosition + s); + var x = this._data, e = this._dataPosition; + x[e] = a; + x[e + 1] = d; + 2 < s && (x[e + 2] = f, x[e + 3] = b, 4 < s && (x[e + 4] = q, 6 === s && (x[e + 5] = h))); + this._dataPosition += s; }; a.prototype.closePath = function() { this._writeCommand(1); }; - a.prototype.moveTo = function(a, b) { + a.prototype.moveTo = function(a, d) { this._writeCommand(2); - this._writeData(a, b); + this._writeData(a, d); }; - a.prototype.lineTo = function(a, b) { + a.prototype.lineTo = function(a, d) { this._writeCommand(3); - this._writeData(a, b); + this._writeData(a, d); }; - a.prototype.quadraticCurveTo = function(a, b, f, c) { + a.prototype.quadraticCurveTo = function(a, d, f, b) { this._writeCommand(4); - this._writeData(a, b, f, c); + this._writeData(a, d, f, b); }; - a.prototype.bezierCurveTo = function(a, b, f, c, q, d) { + a.prototype.bezierCurveTo = function(a, d, f, b, c, q) { this._writeCommand(5); - this._writeData(a, b, f, c, q, d); + this._writeData(a, d, f, b, c, q); }; - a.prototype.arcTo = function(a, b, f, c, q) { + a.prototype.arcTo = function(a, d, f, b, c) { this._writeCommand(6); - this._writeData(a, b, f, c, q); + this._writeData(a, d, f, b, c); }; - a.prototype.rect = function(a, b, f, c) { + a.prototype.rect = function(a, d, f, b) { this._writeCommand(7); - this._writeData(a, b, f, c); + this._writeData(a, d, f, b); }; - a.prototype.arc = function(a, b, f, c, q, d) { + a.prototype.arc = function(a, d, f, b, c, q) { this._writeCommand(8); - this._writeData(a, b, f, c, q, +d); + this._writeData(a, d, f, b, c, +q); }; - a.prototype.addPath = function(a, b) { - b && (this._writeCommand(9), this._writeCommand(11), this._writeData(b.a, b.b, b.c, b.d, b.e, b.f)); + a.prototype.addPath = function(a, d) { + d && (this._writeCommand(9), this._writeCommand(11), this._writeData(d.a, d.b, d.c, d.d, d.e, d.f)); var f = this._commandPosition + a._commandPosition; f >= this._commands.length && this._ensureCommandCapacity(f); - for (var c = this._commands, q = a._commands, d = this._commandPosition, g = 0;d < f;d++) { - c[d] = q[g++]; + for (var b = this._commands, c = a._commands, q = this._commandPosition, h = 0;q < f;q++) { + b[q] = c[h++]; } this._commandPosition = f; f = this._dataPosition + a._dataPosition; f >= this._data.length && this._ensureDataCapacity(f); - c = this._data; - q = a._data; - d = this._dataPosition; - for (g = 0;d < f;d++) { - c[d] = q[g++]; + b = this._data; + c = a._data; + q = this._dataPosition; + for (h = 0;q < f;q++) { + b[q] = c[h++]; } this._dataPosition = f; - b && this._writeCommand(10); + d && this._writeCommand(10); }; - a._arrayBufferPool = new d.ArrayBufferPool; + a._arrayBufferPool = new g.ArrayBufferPool; return a; }(); - m.Path = b; + m.Path = r; if ("undefined" !== typeof CanvasRenderingContext2D && ("undefined" === typeof Path2D || !Path2D.prototype.addPath)) { - var h = CanvasRenderingContext2D.prototype.fill; - CanvasRenderingContext2D.prototype.fill = function(a, f) { - arguments.length && (a instanceof b ? b._apply(a, this) : f = a); - f ? h.call(this, f) : h.call(this); + var u = CanvasRenderingContext2D.prototype.fill; + CanvasRenderingContext2D.prototype.fill = function(a, d) { + arguments.length && (a instanceof r ? r._apply(a, this) : d = a); + d ? u.call(this, d) : u.call(this); + }; + var h = CanvasRenderingContext2D.prototype.stroke; + CanvasRenderingContext2D.prototype.stroke = function(a, d) { + arguments.length && (a instanceof r ? r._apply(a, this) : d = a); + d ? h.call(this, d) : h.call(this); + }; + var f = CanvasRenderingContext2D.prototype.clip; + CanvasRenderingContext2D.prototype.clip = function(a, d) { + arguments.length && (a instanceof r ? r._apply(a, this) : d = a); + d ? f.call(this, d) : f.call(this); }; - var w = CanvasRenderingContext2D.prototype.stroke; - CanvasRenderingContext2D.prototype.stroke = function(a, f) { - arguments.length && (a instanceof b ? b._apply(a, this) : f = a); - f ? w.call(this, f) : w.call(this); - }; - var n = CanvasRenderingContext2D.prototype.clip; - CanvasRenderingContext2D.prototype.clip = function(a, f) { - arguments.length && (a instanceof b ? b._apply(a, this) : f = a); - f ? n.call(this, f) : n.call(this); - }; - window.Path2D = b; + window.Path2D = r; } if ("undefined" !== typeof CanvasPattern && Path2D.prototype.addPath) { - r = function(a) { + t = function(a) { this._transform = a; this._template && (this._template._transform = a); }; - CanvasPattern.prototype.setTransform || (CanvasPattern.prototype.setTransform = r); - CanvasGradient.prototype.setTransform || (CanvasGradient.prototype.setTransform = r); - var f = CanvasRenderingContext2D.prototype.fill, q = CanvasRenderingContext2D.prototype.stroke; - CanvasRenderingContext2D.prototype.fill = function(a, b) { - var c = !!this.fillStyle._transform; - if ((this.fillStyle instanceof CanvasPattern || this.fillStyle instanceof CanvasGradient) && c && a instanceof Path2D) { - var c = this.fillStyle._transform, q; + CanvasPattern.prototype.setTransform || (CanvasPattern.prototype.setTransform = t); + CanvasGradient.prototype.setTransform || (CanvasGradient.prototype.setTransform = t); + var d = CanvasRenderingContext2D.prototype.fill, q = CanvasRenderingContext2D.prototype.stroke; + CanvasRenderingContext2D.prototype.fill = function(a, f) { + var b = !!this.fillStyle._transform; + if ((this.fillStyle instanceof CanvasPattern || this.fillStyle instanceof CanvasGradient) && b && a instanceof Path2D) { + var b = this.fillStyle._transform, c; try { - q = c.inverse(); - } catch (d) { - q = c = m.Geometry.Matrix.createIdentitySVGMatrix(); + c = b.inverse(); + } catch (q) { + c = b = m.Geometry.Matrix.createIdentitySVGMatrix(); } + this.transform(b.a, b.b, b.c, b.d, b.e, b.f); + b = new Path2D; + b.addPath(a, c); + d.call(this, b, f); this.transform(c.a, c.b, c.c, c.d, c.e, c.f); - c = new Path2D; - c.addPath(a, q); - f.call(this, c, b); - this.transform(q.a, q.b, q.c, q.d, q.e, q.f); } else { - 0 === arguments.length ? f.call(this) : 1 === arguments.length ? f.call(this, a) : 2 === arguments.length && f.call(this, a, b); + 0 === arguments.length ? d.call(this) : 1 === arguments.length ? d.call(this, a) : 2 === arguments.length && d.call(this, a, f); } }; CanvasRenderingContext2D.prototype.stroke = function(a) { - var b = !!this.strokeStyle._transform; - if ((this.strokeStyle instanceof CanvasPattern || this.strokeStyle instanceof CanvasGradient) && b && a instanceof Path2D) { - var f = this.strokeStyle._transform, b = f.inverse(); + var d = !!this.strokeStyle._transform; + if ((this.strokeStyle instanceof CanvasPattern || this.strokeStyle instanceof CanvasGradient) && d && a instanceof Path2D) { + var f = this.strokeStyle._transform, d = f.inverse(); this.transform(f.a, f.b, f.c, f.d, f.e, f.f); f = new Path2D; - f.addPath(a, b); - var c = this.lineWidth; - this.lineWidth *= (b.a + b.d) / 2; + f.addPath(a, d); + var b = this.lineWidth; + this.lineWidth *= (d.a + d.d) / 2; q.call(this, f); - this.transform(b.a, b.b, b.c, b.d, b.e, b.f); - this.lineWidth = c; + this.transform(d.a, d.b, d.c, d.d, d.e, d.f); + this.lineWidth = b; } else { 0 === arguments.length ? q.call(this) : 1 === arguments.length && q.call(this, a); } }; } "undefined" !== typeof CanvasRenderingContext2D && function() { - function a(b) { - return b.a; - } - function b(a) { - return a.d; + function a() { + c(this.mozCurrentTransform); + return m.Geometry.Matrix.createSVGMatrixFromArray(this.mozCurrentTransform); } - CanvasRenderingContext2D.prototype.flashStroke = function(f, c) { - var q = this.currentTransform; - if (!q) { - if (q = this.mozCurrentTransform) { - q = m.Geometry.Matrix.createSVGMatrixFromArray(q); - } else { - this.stroke(f); - return; + CanvasRenderingContext2D.prototype.flashStroke = function(a, d) { + var f = this.currentTransform; + if (f) { + var b = new Path2D; + b.addPath(a, f); + var c = this.lineWidth; + this.setTransform(1, 0, 0, 1, 0, 0); + switch(d) { + case 1: + this.lineWidth = w(c * (g.getScaleX(f) + g.getScaleY(f)) / 2, 1, 1024); + break; + case 2: + this.lineWidth = w(c * g.getScaleY(f), 1, 1024); + break; + case 3: + this.lineWidth = w(c * g.getScaleX(f), 1, 1024); } + this.stroke(b); + this.setTransform(f.a, f.b, f.c, f.d, f.e, f.f); + this.lineWidth = c; + } else { + this.stroke(a); } - var d = new Path2D; - d.addPath(f, q); - var g = this.lineWidth; - this.setTransform(1, 0, 0, 1, 0, 0); - switch(c) { - case 1: - this.lineWidth = t(g * (a(q) + b(q)) / 2, 1, 1024); - break; - case 2: - this.lineWidth = t(g * b(q), 1, 1024); - break; - case 3: - this.lineWidth = t(g * a(q), 1, 1024); - } - this.stroke(d); - this.setTransform(q.a, q.b, q.c, q.d, q.e, q.f); - this.lineWidth = g; }; + !("currentTransform" in CanvasRenderingContext2D.prototype) && "mozCurrentTransform" in CanvasRenderingContext2D.prototype && Object.defineProperty(CanvasRenderingContext2D.prototype, "currentTransform", {get:a}); }(); if ("undefined" !== typeof CanvasRenderingContext2D && void 0 === CanvasRenderingContext2D.prototype.globalColorMatrix) { - var U = CanvasRenderingContext2D.prototype.fill, L = CanvasRenderingContext2D.prototype.stroke, I = CanvasRenderingContext2D.prototype.fillText, B = CanvasRenderingContext2D.prototype.strokeText; + var x = CanvasRenderingContext2D.prototype.fill, s = CanvasRenderingContext2D.prototype.stroke, O = CanvasRenderingContext2D.prototype.fillText, T = CanvasRenderingContext2D.prototype.strokeText; Object.defineProperty(CanvasRenderingContext2D.prototype, "globalColorMatrix", {get:function() { return this._globalColorMatrix ? this._globalColorMatrix.clone() : null; }, set:function(a) { a ? this._globalColorMatrix ? this._globalColorMatrix.set(a) : this._globalColorMatrix = a.clone() : this._globalColorMatrix = null; }, enumerable:!0, configurable:!0}); - CanvasRenderingContext2D.prototype.fill = function(a, b) { + CanvasRenderingContext2D.prototype.fill = function(a, d) { var f = null; - this._globalColorMatrix && (f = this.fillStyle, this.fillStyle = s(this, this.fillStyle, this._globalColorMatrix)); - 0 === arguments.length ? U.call(this) : 1 === arguments.length ? U.call(this, a) : 2 === arguments.length && U.call(this, a, b); + this._globalColorMatrix && (f = this.fillStyle, this.fillStyle = e(this, this.fillStyle, this._globalColorMatrix)); + 0 === arguments.length ? x.call(this) : 1 === arguments.length ? x.call(this, a) : 2 === arguments.length && x.call(this, a, d); f && (this.fillStyle = f); }; - CanvasRenderingContext2D.prototype.stroke = function(a, b) { + CanvasRenderingContext2D.prototype.stroke = function(a, d) { var f = null; - this._globalColorMatrix && (f = this.strokeStyle, this.strokeStyle = s(this, this.strokeStyle, this._globalColorMatrix)); - 0 === arguments.length ? L.call(this) : 1 === arguments.length && L.call(this, a); + this._globalColorMatrix && (f = this.strokeStyle, this.strokeStyle = e(this, this.strokeStyle, this._globalColorMatrix)); + 0 === arguments.length ? s.call(this) : 1 === arguments.length && s.call(this, a); f && (this.strokeStyle = f); }; - CanvasRenderingContext2D.prototype.fillText = function(a, b, f, c) { - var q = null; - this._globalColorMatrix && (q = this.fillStyle, this.fillStyle = s(this, this.fillStyle, this._globalColorMatrix)); - 3 === arguments.length ? I.call(this, a, b, f) : 4 === arguments.length ? I.call(this, a, b, f, c) : d.Debug.unexpected(); - q && (this.fillStyle = q); - }; - CanvasRenderingContext2D.prototype.strokeText = function(a, b, f, c) { - var q = null; - this._globalColorMatrix && (q = this.strokeStyle, this.strokeStyle = s(this, this.strokeStyle, this._globalColorMatrix)); - 3 === arguments.length ? B.call(this, a, b, f) : 4 === arguments.length ? B.call(this, a, b, f, c) : d.Debug.unexpected(); - q && (this.strokeStyle = q); - }; - } - })(d.GFX || (d.GFX = {})); -})(Shumway || (Shumway = {})); -(function(d) { - var m = d.Debug.assert, s = function() { - function d() { + CanvasRenderingContext2D.prototype.fillText = function(a, d, f, b) { + var c = null; + this._globalColorMatrix && (c = this.fillStyle, this.fillStyle = e(this, this.fillStyle, this._globalColorMatrix)); + 3 === arguments.length ? O.call(this, a, d, f) : 4 === arguments.length ? O.call(this, a, d, f, b) : g.Debug.unexpected(); + c && (this.fillStyle = c); + }; + CanvasRenderingContext2D.prototype.strokeText = function(a, d, f, b) { + var c = null; + this._globalColorMatrix && (c = this.strokeStyle, this.strokeStyle = e(this, this.strokeStyle, this._globalColorMatrix)); + 3 === arguments.length ? T.call(this, a, d, f) : 4 === arguments.length ? T.call(this, a, d, f, b) : g.Debug.unexpected(); + c && (this.strokeStyle = c); + }; + } + })(g.GFX || (g.GFX = {})); +})(Shumway || (Shumway = {})); +(function(g) { + (function(g) { + g.ScreenShot = function() { + return function(e, c, g) { + this.dataURL = e; + this.w = c; + this.h = g; + }; + }(); + })(g.GFX || (g.GFX = {})); +})(Shumway || (Shumway = {})); +(function(g) { + var m = g.Debug.assert, e = function() { + function c() { this._count = 0; this._head = this._tail = null; } - Object.defineProperty(d.prototype, "count", {get:function() { + Object.defineProperty(c.prototype, "count", {get:function() { return this._count; }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "head", {get:function() { + Object.defineProperty(c.prototype, "head", {get:function() { return this._head; }, enumerable:!0, configurable:!0}); - d.prototype._unshift = function(d) { - m(!d.next && !d.previous); - 0 === this._count ? this._head = this._tail = d : (d.next = this._head, this._head = d.next.previous = d); + c.prototype._unshift = function(c) { + m(!c.next && !c.previous); + 0 === this._count ? this._head = this._tail = c : (c.next = this._head, this._head = c.next.previous = c); this._count++; }; - d.prototype._remove = function(d) { + c.prototype._remove = function(c) { m(0 < this._count); - d === this._head && d === this._tail ? this._head = this._tail = null : d === this._head ? (this._head = d.next, this._head.previous = null) : d == this._tail ? (this._tail = d.previous, this._tail.next = null) : (d.previous.next = d.next, d.next.previous = d.previous); - d.previous = d.next = null; + c === this._head && c === this._tail ? this._head = this._tail = null : c === this._head ? (this._head = c.next, this._head.previous = null) : c == this._tail ? (this._tail = c.previous, this._tail.next = null) : (c.previous.next = c.next, c.next.previous = c.previous); + c.previous = c.next = null; this._count--; }; - d.prototype.use = function(d) { - this._head !== d && ((d.next || d.previous || this._tail === d) && this._remove(d), this._unshift(d)); + c.prototype.use = function(c) { + this._head !== c && ((c.next || c.previous || this._tail === c) && this._remove(c), this._unshift(c)); }; - d.prototype.pop = function() { + c.prototype.pop = function() { if (!this._tail) { return null; } - var d = this._tail; - this._remove(d); - return d; + var c = this._tail; + this._remove(c); + return c; }; - d.prototype.visit = function(d, e) { - "undefined" === typeof e && (e = !0); - for (var a = e ? this._head : this._tail;a && d(a);) { - a = e ? a.next : a.previous; + c.prototype.visit = function(c, e) { + void 0 === e && (e = !0); + for (var b = e ? this._head : this._tail;b && c(b);) { + b = e ? b.next : b.previous; } }; - return d; + return c; }(); - d.LRUList = s; + g.LRUList = e; + g.getScaleX = function(c) { + return c.a; + }; + g.getScaleY = function(c) { + return c.d; + }; })(Shumway || (Shumway = {})); -var Shumway$$inline_46 = Shumway || (Shumway = {}), GFX$$inline_47 = Shumway$$inline_46.GFX || (Shumway$$inline_46.GFX = {}), Option$$inline_48 = Shumway$$inline_46.Options.Option, OptionSet$$inline_49 = Shumway$$inline_46.Options.OptionSet, shumwayOptions$$inline_50 = Shumway$$inline_46.Settings.shumwayOptions, rendererOptions$$inline_51 = shumwayOptions$$inline_50.register(new OptionSet$$inline_49("Renderer Options")); -GFX$$inline_47.imageUpdateOption = rendererOptions$$inline_51.register(new Option$$inline_48("", "imageUpdate", "boolean", !0, "Enable image updating.")); -GFX$$inline_47.imageConvertOption = rendererOptions$$inline_51.register(new Option$$inline_48("", "imageConvert", "boolean", !0, "Enable image conversion.")); -GFX$$inline_47.stageOptions = shumwayOptions$$inline_50.register(new OptionSet$$inline_49("Stage Renderer Options")); -GFX$$inline_47.forcePaint = GFX$$inline_47.stageOptions.register(new Option$$inline_48("", "forcePaint", "boolean", !1, "Force repainting.")); -GFX$$inline_47.ignoreViewport = GFX$$inline_47.stageOptions.register(new Option$$inline_48("", "ignoreViewport", "boolean", !1, "Cull elements outside of the viewport.")); -GFX$$inline_47.viewportLoupeDiameter = GFX$$inline_47.stageOptions.register(new Option$$inline_48("", "viewportLoupeDiameter", "number", 256, "Size of the viewport loupe.", {range:{min:1, max:1024, step:1}})); -GFX$$inline_47.disableClipping = GFX$$inline_47.stageOptions.register(new Option$$inline_48("", "disableClipping", "boolean", !1, "Disable clipping.")); -GFX$$inline_47.debugClipping = GFX$$inline_47.stageOptions.register(new Option$$inline_48("", "debugClipping", "boolean", !1, "Disable clipping.")); -GFX$$inline_47.backend = GFX$$inline_47.stageOptions.register(new Option$$inline_48("", "backend", "number", 0, "Backends", {choices:{Canvas2D:0, WebGL:1, Both:2}})); -GFX$$inline_47.hud = GFX$$inline_47.stageOptions.register(new Option$$inline_48("", "hud", "boolean", !1, "Enable HUD.")); -var webGLOptions$$inline_52 = GFX$$inline_47.stageOptions.register(new OptionSet$$inline_49("WebGL Options")); -GFX$$inline_47.perspectiveCamera = webGLOptions$$inline_52.register(new Option$$inline_48("", "pc", "boolean", !1, "Use perspective camera.")); -GFX$$inline_47.perspectiveCameraFOV = webGLOptions$$inline_52.register(new Option$$inline_48("", "pcFOV", "number", 60, "Perspective Camera FOV.")); -GFX$$inline_47.perspectiveCameraDistance = webGLOptions$$inline_52.register(new Option$$inline_48("", "pcDistance", "number", 2, "Perspective Camera Distance.")); -GFX$$inline_47.perspectiveCameraAngle = webGLOptions$$inline_52.register(new Option$$inline_48("", "pcAngle", "number", 0, "Perspective Camera Angle.")); -GFX$$inline_47.perspectiveCameraAngleRotate = webGLOptions$$inline_52.register(new Option$$inline_48("", "pcRotate", "boolean", !1, "Rotate Use perspective camera.")); -GFX$$inline_47.perspectiveCameraSpacing = webGLOptions$$inline_52.register(new Option$$inline_48("", "pcSpacing", "number", .01, "Element Spacing.")); -GFX$$inline_47.perspectiveCameraSpacingInflate = webGLOptions$$inline_52.register(new Option$$inline_48("", "pcInflate", "boolean", !1, "Rotate Use perspective camera.")); -GFX$$inline_47.drawTiles = webGLOptions$$inline_52.register(new Option$$inline_48("", "drawTiles", "boolean", !1, "Draw WebGL Tiles")); -GFX$$inline_47.drawSurfaces = webGLOptions$$inline_52.register(new Option$$inline_48("", "drawSurfaces", "boolean", !1, "Draw WebGL Surfaces.")); -GFX$$inline_47.drawSurface = webGLOptions$$inline_52.register(new Option$$inline_48("", "drawSurface", "number", -1, "Draw WebGL Surface #")); -GFX$$inline_47.drawElements = webGLOptions$$inline_52.register(new Option$$inline_48("", "drawElements", "boolean", !0, "Actually call gl.drawElements. This is useful to test if the GPU is the bottleneck.")); -GFX$$inline_47.disableSurfaceUploads = webGLOptions$$inline_52.register(new Option$$inline_48("", "disableSurfaceUploads", "boolean", !1, "Disable surface uploads.")); -GFX$$inline_47.premultipliedAlpha = webGLOptions$$inline_52.register(new Option$$inline_48("", "premultipliedAlpha", "boolean", !1, "Set the premultipliedAlpha flag on getContext().")); -GFX$$inline_47.unpackPremultiplyAlpha = webGLOptions$$inline_52.register(new Option$$inline_48("", "unpackPremultiplyAlpha", "boolean", !0, "Use UNPACK_PREMULTIPLY_ALPHA_WEBGL in pixelStorei.")); -var factorChoices$$inline_53 = {ZERO:0, ONE:1, SRC_COLOR:768, ONE_MINUS_SRC_COLOR:769, DST_COLOR:774, ONE_MINUS_DST_COLOR:775, SRC_ALPHA:770, ONE_MINUS_SRC_ALPHA:771, DST_ALPHA:772, ONE_MINUS_DST_ALPHA:773, SRC_ALPHA_SATURATE:776, CONSTANT_COLOR:32769, ONE_MINUS_CONSTANT_COLOR:32770, CONSTANT_ALPHA:32771, ONE_MINUS_CONSTANT_ALPHA:32772}; -GFX$$inline_47.sourceBlendFactor = webGLOptions$$inline_52.register(new Option$$inline_48("", "sourceBlendFactor", "number", factorChoices$$inline_53.ONE, "", {choices:factorChoices$$inline_53})); -GFX$$inline_47.destinationBlendFactor = webGLOptions$$inline_52.register(new Option$$inline_48("", "destinationBlendFactor", "number", factorChoices$$inline_53.ONE_MINUS_SRC_ALPHA, "", {choices:factorChoices$$inline_53})); -var canvas2DOptions$$inline_54 = GFX$$inline_47.stageOptions.register(new OptionSet$$inline_49("Canvas2D Options")); -GFX$$inline_47.clipDirtyRegions = canvas2DOptions$$inline_54.register(new Option$$inline_48("", "clipDirtyRegions", "boolean", !1, "Clip dirty regions.")); -GFX$$inline_47.clipCanvas = canvas2DOptions$$inline_54.register(new Option$$inline_48("", "clipCanvas", "boolean", !1, "Clip Regions.")); -GFX$$inline_47.cull = canvas2DOptions$$inline_54.register(new Option$$inline_48("", "cull", "boolean", !1, "Enable culling.")); -GFX$$inline_47.compositeMask = canvas2DOptions$$inline_54.register(new Option$$inline_48("", "compositeMask", "boolean", !1, "Composite Mask.")); -GFX$$inline_47.snapToDevicePixels = canvas2DOptions$$inline_54.register(new Option$$inline_48("", "snapToDevicePixels", "boolean", !1, "")); -GFX$$inline_47.imageSmoothing = canvas2DOptions$$inline_54.register(new Option$$inline_48("", "imageSmoothing", "boolean", !1, "")); -GFX$$inline_47.blending = canvas2DOptions$$inline_54.register(new Option$$inline_48("", "blending", "boolean", !0, "")); -GFX$$inline_47.cacheShapes = canvas2DOptions$$inline_54.register(new Option$$inline_48("", "cacheShapes", "boolean", !1, "")); -GFX$$inline_47.cacheShapesMaxSize = canvas2DOptions$$inline_54.register(new Option$$inline_48("", "cacheShapesMaxSize", "number", 256, "", {range:{min:1, max:1024, step:1}})); -GFX$$inline_47.cacheShapesThreshold = canvas2DOptions$$inline_54.register(new Option$$inline_48("", "cacheShapesThreshold", "number", 256, "", {range:{min:1, max:1024, step:1}})); -var GFX$$inline_55 = Shumway$$inline_46.GFX; -(function(d) { +var Shumway$$inline_24 = Shumway || (Shumway = {}), GFX$$inline_25 = Shumway$$inline_24.GFX || (Shumway$$inline_24.GFX = {}), Option$$inline_26 = Shumway$$inline_24.Options.Option, OptionSet$$inline_27 = Shumway$$inline_24.Options.OptionSet, shumwayOptions$$inline_28 = Shumway$$inline_24.Settings.shumwayOptions, rendererOptions$$inline_29 = shumwayOptions$$inline_28.register(new OptionSet$$inline_27("Renderer Options")); +GFX$$inline_25.imageUpdateOption = rendererOptions$$inline_29.register(new Option$$inline_26("", "imageUpdate", "boolean", !0, "Enable image updating.")); +GFX$$inline_25.imageConvertOption = rendererOptions$$inline_29.register(new Option$$inline_26("", "imageConvert", "boolean", !0, "Enable image conversion.")); +GFX$$inline_25.stageOptions = shumwayOptions$$inline_28.register(new OptionSet$$inline_27("Stage Renderer Options")); +GFX$$inline_25.forcePaint = GFX$$inline_25.stageOptions.register(new Option$$inline_26("", "forcePaint", "boolean", !1, "Force repainting.")); +GFX$$inline_25.ignoreViewport = GFX$$inline_25.stageOptions.register(new Option$$inline_26("", "ignoreViewport", "boolean", !1, "Cull elements outside of the viewport.")); +GFX$$inline_25.viewportLoupeDiameter = GFX$$inline_25.stageOptions.register(new Option$$inline_26("", "viewportLoupeDiameter", "number", 256, "Size of the viewport loupe.", {range:{min:1, max:1024, step:1}})); +GFX$$inline_25.disableClipping = GFX$$inline_25.stageOptions.register(new Option$$inline_26("", "disableClipping", "boolean", !1, "Disable clipping.")); +GFX$$inline_25.debugClipping = GFX$$inline_25.stageOptions.register(new Option$$inline_26("", "debugClipping", "boolean", !1, "Disable clipping.")); +GFX$$inline_25.hud = GFX$$inline_25.stageOptions.register(new Option$$inline_26("", "hud", "boolean", !1, "Enable HUD.")); +var webGLOptions$$inline_30 = GFX$$inline_25.stageOptions.register(new OptionSet$$inline_27("WebGL Options")); +GFX$$inline_25.perspectiveCamera = webGLOptions$$inline_30.register(new Option$$inline_26("", "pc", "boolean", !1, "Use perspective camera.")); +GFX$$inline_25.perspectiveCameraFOV = webGLOptions$$inline_30.register(new Option$$inline_26("", "pcFOV", "number", 60, "Perspective Camera FOV.")); +GFX$$inline_25.perspectiveCameraDistance = webGLOptions$$inline_30.register(new Option$$inline_26("", "pcDistance", "number", 2, "Perspective Camera Distance.")); +GFX$$inline_25.perspectiveCameraAngle = webGLOptions$$inline_30.register(new Option$$inline_26("", "pcAngle", "number", 0, "Perspective Camera Angle.")); +GFX$$inline_25.perspectiveCameraAngleRotate = webGLOptions$$inline_30.register(new Option$$inline_26("", "pcRotate", "boolean", !1, "Rotate Use perspective camera.")); +GFX$$inline_25.perspectiveCameraSpacing = webGLOptions$$inline_30.register(new Option$$inline_26("", "pcSpacing", "number", .01, "Element Spacing.")); +GFX$$inline_25.perspectiveCameraSpacingInflate = webGLOptions$$inline_30.register(new Option$$inline_26("", "pcInflate", "boolean", !1, "Rotate Use perspective camera.")); +GFX$$inline_25.drawTiles = webGLOptions$$inline_30.register(new Option$$inline_26("", "drawTiles", "boolean", !1, "Draw WebGL Tiles")); +GFX$$inline_25.drawSurfaces = webGLOptions$$inline_30.register(new Option$$inline_26("", "drawSurfaces", "boolean", !1, "Draw WebGL Surfaces.")); +GFX$$inline_25.drawSurface = webGLOptions$$inline_30.register(new Option$$inline_26("", "drawSurface", "number", -1, "Draw WebGL Surface #")); +GFX$$inline_25.drawElements = webGLOptions$$inline_30.register(new Option$$inline_26("", "drawElements", "boolean", !0, "Actually call gl.drawElements. This is useful to test if the GPU is the bottleneck.")); +GFX$$inline_25.disableSurfaceUploads = webGLOptions$$inline_30.register(new Option$$inline_26("", "disableSurfaceUploads", "boolean", !1, "Disable surface uploads.")); +GFX$$inline_25.premultipliedAlpha = webGLOptions$$inline_30.register(new Option$$inline_26("", "premultipliedAlpha", "boolean", !1, "Set the premultipliedAlpha flag on getContext().")); +GFX$$inline_25.unpackPremultiplyAlpha = webGLOptions$$inline_30.register(new Option$$inline_26("", "unpackPremultiplyAlpha", "boolean", !0, "Use UNPACK_PREMULTIPLY_ALPHA_WEBGL in pixelStorei.")); +var factorChoices$$inline_31 = {ZERO:0, ONE:1, SRC_COLOR:768, ONE_MINUS_SRC_COLOR:769, DST_COLOR:774, ONE_MINUS_DST_COLOR:775, SRC_ALPHA:770, ONE_MINUS_SRC_ALPHA:771, DST_ALPHA:772, ONE_MINUS_DST_ALPHA:773, SRC_ALPHA_SATURATE:776, CONSTANT_COLOR:32769, ONE_MINUS_CONSTANT_COLOR:32770, CONSTANT_ALPHA:32771, ONE_MINUS_CONSTANT_ALPHA:32772}; +GFX$$inline_25.sourceBlendFactor = webGLOptions$$inline_30.register(new Option$$inline_26("", "sourceBlendFactor", "number", factorChoices$$inline_31.ONE, "", {choices:factorChoices$$inline_31})); +GFX$$inline_25.destinationBlendFactor = webGLOptions$$inline_30.register(new Option$$inline_26("", "destinationBlendFactor", "number", factorChoices$$inline_31.ONE_MINUS_SRC_ALPHA, "", {choices:factorChoices$$inline_31})); +var canvas2DOptions$$inline_32 = GFX$$inline_25.stageOptions.register(new OptionSet$$inline_27("Canvas2D Options")); +GFX$$inline_25.clipDirtyRegions = canvas2DOptions$$inline_32.register(new Option$$inline_26("", "clipDirtyRegions", "boolean", !1, "Clip dirty regions.")); +GFX$$inline_25.clipCanvas = canvas2DOptions$$inline_32.register(new Option$$inline_26("", "clipCanvas", "boolean", !1, "Clip Regions.")); +GFX$$inline_25.cull = canvas2DOptions$$inline_32.register(new Option$$inline_26("", "cull", "boolean", !1, "Enable culling.")); +GFX$$inline_25.snapToDevicePixels = canvas2DOptions$$inline_32.register(new Option$$inline_26("", "snapToDevicePixels", "boolean", !1, "")); +GFX$$inline_25.imageSmoothing = canvas2DOptions$$inline_32.register(new Option$$inline_26("", "imageSmoothing", "boolean", !1, "")); +GFX$$inline_25.masking = canvas2DOptions$$inline_32.register(new Option$$inline_26("", "masking", "boolean", !0, "Composite Mask.")); +GFX$$inline_25.blending = canvas2DOptions$$inline_32.register(new Option$$inline_26("", "blending", "boolean", !0, "")); +GFX$$inline_25.debugLayers = canvas2DOptions$$inline_32.register(new Option$$inline_26("", "debugLayers", "boolean", !1, "")); +GFX$$inline_25.filters = canvas2DOptions$$inline_32.register(new Option$$inline_26("", "filters", "boolean", !1, "")); +GFX$$inline_25.cacheShapes = canvas2DOptions$$inline_32.register(new Option$$inline_26("", "cacheShapes", "boolean", !0, "")); +GFX$$inline_25.cacheShapesMaxSize = canvas2DOptions$$inline_32.register(new Option$$inline_26("", "cacheShapesMaxSize", "number", 256, "", {range:{min:1, max:1024, step:1}})); +GFX$$inline_25.cacheShapesThreshold = canvas2DOptions$$inline_32.register(new Option$$inline_26("", "cacheShapesThreshold", "number", 256, "", {range:{min:1, max:1024, step:1}})); +(function(g) { (function(m) { - (function(s) { - function e(a, b, c, d) { - var g = 1 - d; - return a * g * g + 2 * b * g * d + c * d * d; - } - function t(a, b, c, d, g) { - var e = g * g, h = 1 - g, l = h * h; - return a * h * l + 3 * b * g * l + 3 * c * h * e + d * g * e; + (function(e) { + function c(a, f, d, b) { + var c = 1 - b; + return a * c * c + 2 * f * c * b + d * b * b; + } + function w(a, f, d, b, c) { + var s = c * c, e = 1 - c, l = e * e; + return a * e * l + 3 * f * c * l + 3 * d * e * s + b * c * s; } - var k = d.NumberUtilities.clamp, a = d.NumberUtilities.pow2, c = d.NumberUtilities.epsilonEquals, g = d.Debug.assert; - s.radianToDegrees = function(a) { + var k = g.NumberUtilities.clamp, b = g.NumberUtilities.pow2, a = g.NumberUtilities.epsilonEquals, n = g.Debug.assert; + e.radianToDegrees = function(a) { return 180 * a / Math.PI; }; - s.degreesToRadian = function(a) { + e.degreesToRadian = function(a) { return a * Math.PI / 180; }; - s.quadraticBezier = e; - s.quadraticBezierExtreme = function(a, b, c) { - var d = (a - b) / (a - 2 * b + c); - return 0 > d ? a : 1 < d ? c : e(a, b, c, d); - }; - s.cubicBezier = t; - s.cubicBezierExtremes = function(a, b, c, d) { - var g = b - a, e; - e = 2 * (c - b); - var h = d - c; - g + h === e && (h *= 1.0001); - var l = 2 * g - e, p = e - 2 * g, p = Math.sqrt(p * p - 4 * g * (g - e + h)); - e = 2 * (g - e + h); - g = (l + p) / e; - l = (l - p) / e; - p = []; - 0 <= g && 1 >= g && p.push(t(a, b, c, d, g)); - 0 <= l && 1 >= l && p.push(t(a, b, c, d, l)); - return p; + e.quadraticBezier = c; + e.quadraticBezierExtreme = function(a, f, d) { + var b = (a - f) / (a - 2 * f + d); + return 0 > b ? a : 1 < b ? d : c(a, f, d, b); + }; + e.cubicBezier = w; + e.cubicBezierExtremes = function(a, f, d, b) { + var c = f - a, s; + s = 2 * (d - f); + var e = b - d; + c + e === s && (e *= 1.0001); + var l = 2 * c - s, g = s - 2 * c, g = Math.sqrt(g * g - 4 * c * (c - s + e)); + s = 2 * (c - s + e); + c = (l + g) / s; + l = (l - g) / s; + g = []; + 0 <= c && 1 >= c && g.push(w(a, f, d, b, c)); + 0 <= l && 1 >= l && g.push(w(a, f, d, b, l)); + return g; }; var p = function() { - function a(b, c) { - this.x = b; - this.y = c; + function a(f, d) { + this.x = f; + this.y = d; } - a.prototype.setElements = function(a, b) { + a.prototype.setElements = function(a, d) { this.x = a; - this.y = b; + this.y = d; return this; }; a.prototype.set = function(a) { @@ -6440,40 +6794,41 @@ a.prototype.clone = function() { return new a(this.x, this.y); }; - a.prototype.toString = function() { - return "{x: " + this.x + ", y: " + this.y + "}"; - }; - a.prototype.inTriangle = function(a, b, c) { - var d = a.y * c.x - a.x * c.y + (c.y - a.y) * this.x + (a.x - c.x) * this.y, g = a.x * b.y - a.y * b.x + (a.y - b.y) * this.x + (b.x - a.x) * this.y; - if (0 > d != 0 > g) { + a.prototype.toString = function(a) { + void 0 === a && (a = 2); + return "{x: " + this.x.toFixed(a) + ", y: " + this.y.toFixed(a) + "}"; + }; + a.prototype.inTriangle = function(a, d, b) { + var c = a.y * b.x - a.x * b.y + (b.y - a.y) * this.x + (a.x - b.x) * this.y, h = a.x * d.y - a.y * d.x + (a.y - d.y) * this.x + (d.x - a.x) * this.y; + if (0 > c != 0 > h) { return!1; } - a = -b.y * c.x + a.y * (c.x - b.x) + a.x * (b.y - c.y) + b.x * c.y; - 0 > a && (d = -d, g = -g, a = -a); - return 0 < d && 0 < g && d + g < a; + a = -d.y * b.x + a.y * (b.x - d.x) + a.x * (d.y - b.y) + d.x * b.y; + 0 > a && (c = -c, h = -h, a = -a); + return 0 < c && 0 < h && c + h < a; }; a.createEmpty = function() { return new a(0, 0); }; a.createEmptyPoints = function(b) { - for (var c = [], d = 0;d < b;d++) { - c.push(new a(0, 0)); + for (var d = [], c = 0;c < b;c++) { + d.push(new a(0, 0)); } - return c; + return d; }; return a; }(); - s.Point = p; - var v = function() { - function a(b, c, d) { + e.Point = p; + var y = function() { + function a(b, d, c) { this.x = b; - this.y = c; - this.z = d; + this.y = d; + this.z = c; } - a.prototype.setElements = function(a, b, c) { + a.prototype.setElements = function(a, d, b) { this.x = a; - this.y = b; - this.z = c; + this.y = d; + this.z = b; return this; }; a.prototype.set = function(a) { @@ -6486,10 +6841,10 @@ return this.x * a.x + this.y * a.y + this.z * a.z; }; a.prototype.cross = function(a) { - var b = this.z * a.x - this.x * a.z, c = this.x * a.y - this.y * a.x; + var d = this.z * a.x - this.x * a.z, b = this.x * a.y - this.y * a.x; this.x = this.y * a.z - this.z * a.y; - this.y = b; - this.z = c; + this.y = d; + this.z = b; return this; }; a.prototype.squaredLength = function() { @@ -6515,30 +6870,32 @@ a.prototype.clone = function() { return new a(this.x, this.y, this.z); }; - a.prototype.toString = function() { - return "{x: " + this.x + ", y: " + this.y + ", z: " + this.z + "}"; + a.prototype.toString = function(a) { + void 0 === a && (a = 2); + return "{x: " + this.x.toFixed(a) + ", y: " + this.y.toFixed(a) + ", z: " + this.z.toFixed(a) + "}"; }; a.createEmpty = function() { return new a(0, 0, 0); }; a.createEmptyPoints = function(b) { - for (var c = [], d = 0;d < b;d++) { - c.push(new a(0, 0, 0)); + for (var d = [], c = 0;c < b;c++) { + d.push(new a(0, 0, 0)); } - return c; + return d; }; return a; }(); - s.Point3D = v; - var u = function() { - function a(b, c, d, g) { - this.setElements(b, c, d, g); + e.Point3D = y; + var v = function() { + function a(b, d, c, x) { + this.setElements(b, d, c, x); + a.allocationCount++; } - a.prototype.setElements = function(a, b, c, d) { + a.prototype.setElements = function(a, d, b, c) { this.x = a; - this.y = b; - this.w = c; - this.h = d; + this.y = d; + this.w = b; + this.h = c; }; a.prototype.set = function(a) { this.x = a.x; @@ -6547,15 +6904,15 @@ this.h = a.h; }; a.prototype.contains = function(a) { - var b = a.x + a.w, c = a.y + a.h, d = this.x + this.w, g = this.y + this.h; - return a.x >= this.x && a.x < d && a.y >= this.y && a.y < g && b > this.x && b <= d && c > this.y && c <= g; + var d = a.x + a.w, b = a.y + a.h, c = this.x + this.w, h = this.y + this.h; + return a.x >= this.x && a.x < c && a.y >= this.y && a.y < h && d > this.x && d <= c && b > this.y && b <= h; }; a.prototype.containsPoint = function(a) { return a.x >= this.x && a.x < this.x + this.w && a.y >= this.y && a.y < this.y + this.h; }; a.prototype.isContained = function(a) { - for (var b = 0;b < a.length;b++) { - if (a[b].contains(this)) { + for (var d = 0;d < a.length;d++) { + if (a[d].contains(this)) { return!0; } } @@ -6571,93 +6928,104 @@ if (this.isEmpty()) { this.set(a); } else { - var b = this.x, c = this.y; - this.x > a.x && (b = a.x); - this.y > a.y && (c = a.y); - var d = this.x + this.w; - d < a.x + a.w && (d = a.x + a.w); - var g = this.y + this.h; - g < a.y + a.h && (g = a.y + a.h); - this.x = b; - this.y = c; - this.w = d - b; - this.h = g - c; + if (!a.isEmpty()) { + var d = this.x, b = this.y; + this.x > a.x && (d = a.x); + this.y > a.y && (b = a.y); + var c = this.x + this.w; + c < a.x + a.w && (c = a.x + a.w); + var h = this.y + this.h; + h < a.y + a.h && (h = a.y + a.h); + this.x = d; + this.y = b; + this.w = c - d; + this.h = h - b; + } } }; a.prototype.isEmpty = function() { return 0 >= this.w || 0 >= this.h; }; a.prototype.setEmpty = function() { - this.h = this.w = 0; + this.h = this.w = this.y = this.x = 0; }; a.prototype.intersect = function(b) { - var c = a.createEmpty(); + var d = a.createEmpty(); if (this.isEmpty() || b.isEmpty()) { - return c.setEmpty(), c; + return d.setEmpty(), d; } - c.x = Math.max(this.x, b.x); - c.y = Math.max(this.y, b.y); - c.w = Math.min(this.x + this.w, b.x + b.w) - c.x; - c.h = Math.min(this.y + this.h, b.y + b.h) - c.y; - c.isEmpty() && c.setEmpty(); - this.set(c); + d.x = Math.max(this.x, b.x); + d.y = Math.max(this.y, b.y); + d.w = Math.min(this.x + this.w, b.x + b.w) - d.x; + d.h = Math.min(this.y + this.h, b.y + b.h) - d.y; + d.isEmpty() && d.setEmpty(); + this.set(d); }; a.prototype.intersects = function(a) { if (this.isEmpty() || a.isEmpty()) { return!1; } - var b = Math.max(this.x, a.x), c = Math.max(this.y, a.y), b = Math.min(this.x + this.w, a.x + a.w) - b; - a = Math.min(this.y + this.h, a.y + a.h) - c; - return!(0 >= b || 0 >= a); - }; - a.prototype.intersectsTransformedAABB = function(b, c) { - var d = a._temporary; - d.set(b); - c.transformRectangleAABB(d); - return this.intersects(d); + var d = Math.max(this.x, a.x), b = Math.max(this.y, a.y), d = Math.min(this.x + this.w, a.x + a.w) - d; + a = Math.min(this.y + this.h, a.y + a.h) - b; + return!(0 >= d || 0 >= a); + }; + a.prototype.intersectsTransformedAABB = function(b, d) { + var c = a._temporary; + c.set(b); + d.transformRectangleAABB(c); + return this.intersects(c); }; - a.prototype.intersectsTranslated = function(a, b, c) { + a.prototype.intersectsTranslated = function(a, d, b) { if (this.isEmpty() || a.isEmpty()) { return!1; } - var d = Math.max(this.x, a.x + b), g = Math.max(this.y, a.y + c); - b = Math.min(this.x + this.w, a.x + b + a.w) - d; - a = Math.min(this.y + this.h, a.y + c + a.h) - g; - return!(0 >= b || 0 >= a); + var c = Math.max(this.x, a.x + d), h = Math.max(this.y, a.y + b); + d = Math.min(this.x + this.w, a.x + d + a.w) - c; + a = Math.min(this.y + this.h, a.y + b + a.h) - h; + return!(0 >= d || 0 >= a); }; a.prototype.area = function() { return this.w * this.h; }; a.prototype.clone = function() { - return new a(this.x, this.y, this.w, this.h); + var b = a.allocate(); + b.set(this); + return b; + }; + a.allocate = function() { + var b = a._dirtyStack; + return b.length ? b.pop() : new a(12345, 67890, 12345, 67890); + }; + a.prototype.free = function() { + a._dirtyStack.push(this); }; a.prototype.snap = function() { - var a = Math.ceil(this.x + this.w), b = Math.ceil(this.y + this.h); + var a = Math.ceil(this.x + this.w), d = Math.ceil(this.y + this.h); this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.w = a - this.x; - this.h = b - this.y; + this.h = d - this.y; return this; }; - a.prototype.scale = function(a, b) { + a.prototype.scale = function(a, d) { this.x *= a; - this.y *= b; + this.y *= d; this.w *= a; - this.h *= b; + this.h *= d; return this; }; - a.prototype.offset = function(a, b) { + a.prototype.offset = function(a, d) { this.x += a; - this.y += b; + this.y += d; return this; }; - a.prototype.resize = function(a, b) { + a.prototype.resize = function(a, d) { this.w += a; - this.h += b; + this.h += d; return this; }; - a.prototype.expand = function(a, b) { - this.offset(-a, -b).resize(2 * a, 2 * b); + a.prototype.expand = function(a, d) { + this.offset(-a, -d).resize(2 * a, 2 * d); return this; }; a.prototype.getCenter = function() { @@ -6666,17 +7034,23 @@ a.prototype.getAbsoluteBounds = function() { return new a(0, 0, this.w, this.h); }; - a.prototype.toString = function() { - return "{" + this.x + ", " + this.y + ", " + this.w + ", " + this.h + "}"; + a.prototype.toString = function(a) { + void 0 === a && (a = 2); + return "{" + this.x.toFixed(a) + ", " + this.y.toFixed(a) + ", " + this.w.toFixed(a) + ", " + this.h.toFixed(a) + "}"; }; a.createEmpty = function() { - return new a(0, 0, 0, 0); + var b = a.allocate(); + b.setEmpty(); + return b; }; a.createSquare = function() { return new a(-512, -512, 1024, 1024); }; a.createMaxI16 = function() { - return new a(d.Numbers.MinI16, d.Numbers.MinI16, 65535, 65535); + return new a(-32768, -32768, 65535, 65535); + }; + a.prototype.setMaxI16 = function() { + this.setElements(-32768, -32768, 65535, 65535); }; a.prototype.getCorners = function(a) { a[0].x = this.x; @@ -6688,10 +7062,12 @@ a[3].x = this.x; a[3].y = this.y + this.h; }; - a._temporary = a.createEmpty(); + a.allocationCount = 0; + a._temporary = new a(0, 0, 0, 0); + a._dirtyStack = []; return a; }(); - s.Rectangle = u; + e.Rectangle = v; var l = function() { function a(b) { this.corners = b.map(function(a) { @@ -6699,33 +7075,33 @@ }); this.axes = [b[1].clone().sub(b[0]), b[3].clone().sub(b[0])]; this.origins = []; - for (var c = 0;2 > c;c++) { - this.axes[c].mul(1 / this.axes[c].squaredLength()), this.origins.push(b[0].dot(this.axes[c])); + for (var d = 0;2 > d;d++) { + this.axes[d].mul(1 / this.axes[d].squaredLength()), this.origins.push(b[0].dot(this.axes[d])); } } a.prototype.getBounds = function() { return a.getBounds(this.corners); }; a.getBounds = function(a) { - for (var b = new p(Number.MAX_VALUE, Number.MAX_VALUE), c = new p(Number.MIN_VALUE, Number.MIN_VALUE), d = 0;4 > d;d++) { - var g = a[d].x, e = a[d].y; - b.x = Math.min(b.x, g); - b.y = Math.min(b.y, e); - c.x = Math.max(c.x, g); - c.y = Math.max(c.y, e); + for (var d = new p(Number.MAX_VALUE, Number.MAX_VALUE), b = new p(Number.MIN_VALUE, Number.MIN_VALUE), c = 0;4 > c;c++) { + var h = a[c].x, e = a[c].y; + d.x = Math.min(d.x, h); + d.y = Math.min(d.y, e); + b.x = Math.max(b.x, h); + b.y = Math.max(b.y, e); } - return new u(b.x, b.y, c.x - b.x, c.y - b.y); + return new v(d.x, d.y, b.x - d.x, b.y - d.y); }; a.prototype.intersects = function(a) { return this.intersectsOneWay(a) && a.intersectsOneWay(this); }; a.prototype.intersectsOneWay = function(a) { - for (var b = 0;2 > b;b++) { - for (var c = 0;4 > c;c++) { - var d = a.corners[c].dot(this.axes[b]), g, e; - 0 === c ? e = g = d : d < g ? g = d : d > e && (e = d); + for (var d = 0;2 > d;d++) { + for (var b = 0;4 > b;b++) { + var c = a.corners[b].dot(this.axes[d]), h, e; + 0 === b ? e = h = c : c < h ? h = c : c > e && (e = c); } - if (g > 1 + this.origins[b] || e < this.origins[b]) { + if (h > 1 + this.origins[d] || e < this.origins[d]) { return!1; } } @@ -6733,173 +7109,228 @@ }; return a; }(); - s.OBB = l; - var r = function() { - function a(b, c, d, g, e, h) { + e.OBB = l; + (function(a) { + a[a.Unknown = 0] = "Unknown"; + a[a.Identity = 1] = "Identity"; + a[a.Translation = 2] = "Translation"; + })(e.MatrixType || (e.MatrixType = {})); + var t = function() { + function b(a, d, c, x, s, e) { this._data = new Float64Array(6); - this.setElements(b, c, d, g, e, h); + this._type = 0; + this.setElements(a, d, c, x, s, e); + b.allocationCount++; } - Object.defineProperty(a.prototype, "a", {get:function() { + Object.defineProperty(b.prototype, "a", {get:function() { return this._data[0]; }, set:function(a) { this._data[0] = a; + this._type = 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "b", {get:function() { + Object.defineProperty(b.prototype, "b", {get:function() { return this._data[1]; }, set:function(a) { this._data[1] = a; + this._type = 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "c", {get:function() { + Object.defineProperty(b.prototype, "c", {get:function() { return this._data[2]; }, set:function(a) { this._data[2] = a; + this._type = 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "d", {get:function() { + Object.defineProperty(b.prototype, "d", {get:function() { return this._data[3]; }, set:function(a) { this._data[3] = a; + this._type = 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "tx", {get:function() { + Object.defineProperty(b.prototype, "tx", {get:function() { return this._data[4]; }, set:function(a) { this._data[4] = a; + 1 === this._type && (this._type = 2); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "ty", {get:function() { + Object.defineProperty(b.prototype, "ty", {get:function() { return this._data[5]; }, set:function(a) { this._data[5] = a; + 1 === this._type && (this._type = 2); }, enumerable:!0, configurable:!0}); - a.prototype.setElements = function(a, b, c, d, g, e) { - var h = this._data; - h[0] = a; - h[1] = b; - h[2] = c; - h[3] = d; - h[4] = g; - h[5] = e; - }; - a.prototype.set = function(a) { - var b = this._data; - a = a._data; - b[0] = a[0]; - b[1] = a[1]; - b[2] = a[2]; - b[3] = a[3]; - b[4] = a[4]; - b[5] = a[5]; + b.prototype.setElements = function(a, d, b, c, h, e) { + var l = this._data; + l[0] = a; + l[1] = d; + l[2] = b; + l[3] = c; + l[4] = h; + l[5] = e; + this._type = 0; + }; + b.prototype.set = function(a) { + var d = this._data, b = a._data; + d[0] = b[0]; + d[1] = b[1]; + d[2] = b[2]; + d[3] = b[3]; + d[4] = b[4]; + d[5] = b[5]; + this._type = a._type; }; - a.prototype.emptyArea = function(a) { + b.prototype.emptyArea = function(a) { a = this._data; return 0 === a[0] || 0 === a[3] ? !0 : !1; }; - a.prototype.infiniteArea = function(a) { + b.prototype.infiniteArea = function(a) { a = this._data; return Infinity === Math.abs(a[0]) || Infinity === Math.abs(a[3]) ? !0 : !1; }; - a.prototype.isEqual = function(a) { - var b = this._data; + b.prototype.isEqual = function(a) { + if (1 === this._type && 1 === a._type) { + return!0; + } + var d = this._data; a = a._data; - return b[0] === a[0] && b[1] === a[1] && b[2] === a[2] && b[3] === a[3] && b[4] === a[4] && b[5] === a[5]; + return d[0] === a[0] && d[1] === a[1] && d[2] === a[2] && d[3] === a[3] && d[4] === a[4] && d[5] === a[5]; }; - a.prototype.clone = function() { - var b = this._data; - return new a(b[0], b[1], b[2], b[3], b[4], b[5]); + b.prototype.clone = function() { + var a = b.allocate(); + a.set(this); + return a; }; - a.prototype.transform = function(a, b, c, d, g, e) { - var h = this._data, n = h[0], l = h[1], p = h[2], r = h[3], k = h[4], m = h[5]; - h[0] = n * a + p * b; - h[1] = l * a + r * b; - h[2] = n * c + p * d; - h[3] = l * c + r * d; - h[4] = n * g + p * e + k; - h[5] = l * g + r * e + m; + b.allocate = function() { + var a = b._dirtyStack; + return a.length ? a.pop() : new b(12345, 12345, 12345, 12345, 12345, 12345); + }; + b.prototype.free = function() { + b._dirtyStack.push(this); + }; + b.prototype.transform = function(a, d, b, c, h, e) { + var l = this._data, g = l[0], n = l[1], k = l[2], p = l[3], r = l[4], t = l[5]; + l[0] = g * a + k * d; + l[1] = n * a + p * d; + l[2] = g * b + k * c; + l[3] = n * b + p * c; + l[4] = g * h + k * e + r; + l[5] = n * h + p * e + t; + this._type = 0; return this; }; - a.prototype.transformRectangle = function(a, b) { - var c = this._data, d = c[0], g = c[1], e = c[2], h = c[3], n = c[4], c = c[5], l = a.x, p = a.y, r = a.w, k = a.h; - b[0].x = d * l + e * p + n; - b[0].y = g * l + h * p + c; - b[1].x = d * (l + r) + e * p + n; - b[1].y = g * (l + r) + h * p + c; - b[2].x = d * (l + r) + e * (p + k) + n; - b[2].y = g * (l + r) + h * (p + k) + c; - b[3].x = d * l + e * (p + k) + n; - b[3].y = g * l + h * (p + k) + c; + b.prototype.transformRectangle = function(a, d) { + n(4 === d.length); + var b = this._data, c = b[0], h = b[1], e = b[2], l = b[3], g = b[4], b = b[5], k = a.x, p = a.y, r = a.w, t = a.h; + d[0].x = c * k + e * p + g; + d[0].y = h * k + l * p + b; + d[1].x = c * (k + r) + e * p + g; + d[1].y = h * (k + r) + l * p + b; + d[2].x = c * (k + r) + e * (p + t) + g; + d[2].y = h * (k + r) + l * (p + t) + b; + d[3].x = c * k + e * (p + t) + g; + d[3].y = h * k + l * (p + t) + b; }; - a.prototype.isTranslationOnly = function() { - var a = this._data; - return 1 === a[0] && 0 === a[1] && 0 === a[2] && 1 === a[3] || c(a[0], 1) && c(a[1], 0) && c(a[2], 0) && c(a[3], 1) ? !0 : !1; + b.prototype.isTranslationOnly = function() { + if (2 === this._type) { + return!0; + } + var b = this._data; + return 1 === b[0] && 0 === b[1] && 0 === b[2] && 1 === b[3] || a(b[0], 1) && a(b[1], 0) && a(b[2], 0) && a(b[3], 1) ? (this._type = 2, !0) : !1; + }; + b.prototype.transformRectangleAABB = function(a) { + var d = this._data; + if (1 !== this._type) { + if (2 === this._type) { + a.x += d[4], a.y += d[5]; + } else { + var b = d[0], c = d[1], h = d[2], e = d[3], l = d[4], g = d[5], n = a.x, k = a.y, p = a.w, r = a.h, d = b * n + h * k + l, t = c * n + e * k + g, m = b * (n + p) + h * k + l, v = c * (n + p) + e * k + g, u = b * (n + p) + h * (k + r) + l, p = c * (n + p) + e * (k + r) + g, b = b * n + h * (k + r) + l, c = c * n + e * (k + r) + g, e = 0; + d > m && (e = d, d = m, m = e); + u > b && (e = u, u = b, b = e); + a.x = d < u ? d : u; + a.w = (m > b ? m : b) - a.x; + t > v && (e = t, t = v, v = e); + p > c && (e = p, p = c, c = e); + a.y = t < p ? t : p; + a.h = (v > c ? v : c) - a.y; + } + } }; - a.prototype.transformRectangleAABB = function(a) { - var b = this._data, c = b[0], d = b[1], g = b[2], e = b[3], h = b[4], n = b[5], l = a.x, p = a.y, r = a.w, k = a.h, b = c * l + g * p + h, m = d * l + e * p + n, w = c * (l + r) + g * p + h, v = d * (l + r) + e * p + n, u = c * (l + r) + g * (p + k) + h, r = d * (l + r) + e * (p + k) + n, c = c * l + g * (p + k) + h, d = d * l + e * (p + k) + n, e = 0; - b > w && (e = b, b = w, w = e); - u > c && (e = u, u = c, c = e); - a.x = b < u ? b : u; - a.w = (w > c ? w : c) - a.x; - m > v && (e = m, m = v, v = e); - r > d && (e = r, r = d, d = e); - a.y = m < r ? m : r; - a.h = (v > d ? v : d) - a.y; - }; - a.prototype.scale = function(a, b) { - var c = this._data; - c[0] *= a; - c[1] *= b; - c[2] *= a; - c[3] *= b; - c[4] *= a; - c[5] *= b; + b.prototype.scale = function(a, d) { + var b = this._data; + b[0] *= a; + b[1] *= d; + b[2] *= a; + b[3] *= d; + b[4] *= a; + b[5] *= d; + this._type = 0; return this; }; - a.prototype.scaleClone = function(a, b) { - return 1 === a && 1 === b ? this : this.clone().scale(a, b); + b.prototype.scaleClone = function(a, d) { + return 1 === a && 1 === d ? this : this.clone().scale(a, d); }; - a.prototype.rotate = function(a) { - var b = this._data, c = b[0], d = b[1], g = b[2], e = b[3], h = b[4], n = b[5], l = Math.cos(a); + b.prototype.rotate = function(a) { + var d = this._data, b = d[0], c = d[1], h = d[2], e = d[3], l = d[4], g = d[5], n = Math.cos(a); a = Math.sin(a); - b[0] = l * c - a * d; - b[1] = a * c + l * d; - b[2] = l * g - a * e; - b[3] = a * g + l * e; - b[4] = l * h - a * n; - b[5] = a * h + l * n; + d[0] = n * b - a * c; + d[1] = a * b + n * c; + d[2] = n * h - a * e; + d[3] = a * h + n * e; + d[4] = n * l - a * g; + d[5] = a * l + n * g; + this._type = 0; return this; }; - a.prototype.concat = function(a) { - var b = this._data; + b.prototype.concat = function(a) { + if (1 === a._type) { + return this; + } + var d = this._data; a = a._data; - var c = b[0] * a[0], d = 0, g = 0, e = b[3] * a[3], h = b[4] * a[0] + a[4], n = b[5] * a[3] + a[5]; - if (0 !== b[1] || 0 !== b[2] || 0 !== a[1] || 0 !== a[2]) { - c += b[1] * a[2], e += b[2] * a[1], d += b[0] * a[1] + b[1] * a[3], g += b[2] * a[0] + b[3] * a[2], h += b[5] * a[2], n += b[4] * a[1]; - } - b[0] = c; - b[1] = d; - b[2] = g; - b[3] = e; - b[4] = h; - b[5] = n; + var b = d[0] * a[0], c = 0, h = 0, e = d[3] * a[3], l = d[4] * a[0] + a[4], g = d[5] * a[3] + a[5]; + if (0 !== d[1] || 0 !== d[2] || 0 !== a[1] || 0 !== a[2]) { + b += d[1] * a[2], e += d[2] * a[1], c += d[0] * a[1] + d[1] * a[3], h += d[2] * a[0] + d[3] * a[2], l += d[5] * a[2], g += d[4] * a[1]; + } + d[0] = b; + d[1] = c; + d[2] = h; + d[3] = e; + d[4] = l; + d[5] = g; + this._type = 0; + return this; + }; + b.prototype.concatClone = function(a) { + return this.clone().concat(a); + }; + b.prototype.preMultiply = function(a) { + var d = this._data, b = a._data; + if (2 === a._type && this._type & 3) { + d[4] += b[4], d[5] += b[5], this._type = 2; + } else { + if (1 !== a._type) { + a = b[0] * d[0]; + var c = 0, h = 0, e = b[3] * d[3], l = b[4] * d[0] + d[4], g = b[5] * d[3] + d[5]; + if (0 !== b[1] || 0 !== b[2] || 0 !== d[1] || 0 !== d[2]) { + a += b[1] * d[2], e += b[2] * d[1], c += b[0] * d[1] + b[1] * d[3], h += b[2] * d[0] + b[3] * d[2], l += b[5] * d[2], g += b[4] * d[1]; + } + d[0] = a; + d[1] = c; + d[2] = h; + d[3] = e; + d[4] = l; + d[5] = g; + this._type = 0; + } + } }; - a.prototype.preMultiply = function(a) { + b.prototype.translate = function(a, d) { var b = this._data; - a = a._data; - var c = a[0] * b[0], d = 0, g = 0, e = a[3] * b[3], h = a[4] * b[0] + b[4], n = a[5] * b[3] + b[5]; - if (0 !== a[1] || 0 !== a[2] || 0 !== b[1] || 0 !== b[2]) { - c += a[1] * b[2], e += a[2] * b[1], d += a[0] * b[1] + a[1] * b[3], g += a[2] * b[0] + a[3] * b[2], h += a[5] * b[2], n += a[4] * b[1]; - } - b[0] = c; - b[1] = d; - b[2] = g; - b[3] = e; - b[4] = h; - b[5] = n; - }; - a.prototype.translate = function(a, b) { - var c = this._data; - c[4] += a; - c[5] += b; + b[4] += a; + b[5] += d; + 1 === this._type && (this._type = 2); return this; }; - a.prototype.setIdentity = function() { + b.prototype.setIdentity = function() { var a = this._data; a[0] = 1; a[1] = 0; @@ -6907,215 +7338,253 @@ a[3] = 1; a[4] = 0; a[5] = 0; + this._type = 1; }; - a.prototype.isIdentity = function() { + b.prototype.isIdentity = function() { + if (1 === this._type) { + return!0; + } var a = this._data; return 1 === a[0] && 0 === a[1] && 0 === a[2] && 1 === a[3] && 0 === a[4] && 0 === a[5]; }; - a.prototype.transformPoint = function(a) { - var b = this._data, c = a.x, d = a.y; - a.x = b[0] * c + b[2] * d + b[4]; - a.y = b[1] * c + b[3] * d + b[5]; + b.prototype.transformPoint = function(a) { + if (1 !== this._type) { + var d = this._data, b = a.x, c = a.y; + a.x = d[0] * b + d[2] * c + d[4]; + a.y = d[1] * b + d[3] * c + d[5]; + } }; - a.prototype.transformPoints = function(a) { - for (var b = 0;b < a.length;b++) { - this.transformPoint(a[b]); + b.prototype.transformPoints = function(a) { + if (1 !== this._type) { + for (var d = 0;d < a.length;d++) { + this.transformPoint(a[d]); + } } }; - a.prototype.deltaTransformPoint = function(a) { - var b = this._data, c = a.x, d = a.y; - a.x = b[0] * c + b[2] * d; - a.y = b[1] * c + b[3] * d; - }; - a.prototype.inverse = function(a) { - var b = this._data, c = a._data, d = b[1], g = b[2], e = b[4], h = b[5]; - if (0 === d && 0 === g) { - var n = c[0] = 1 / b[0], b = c[3] = 1 / b[3]; - c[1] = 0; - c[2] = 0; - c[4] = -n * e; - c[5] = -b * h; + b.prototype.deltaTransformPoint = function(a) { + if (1 !== this._type) { + var d = this._data, b = a.x, c = a.y; + a.x = d[0] * b + d[2] * c; + a.y = d[1] * b + d[3] * c; + } + }; + b.prototype.inverse = function(a) { + var d = this._data, b = a._data; + if (1 === this._type) { + a.setIdentity(); } else { - var n = b[0], b = b[3], l = n * b - d * g; - 0 === l ? a.setIdentity() : (l = 1 / l, c[0] = b * l, d = c[1] = -d * l, g = c[2] = -g * l, b = c[3] = n * l, c[4] = -(c[0] * e + g * h), c[5] = -(d * e + b * h)); + if (2 === this._type) { + b[0] = 1, b[1] = 0, b[2] = 0, b[3] = 1, b[4] = -d[4], b[5] = -d[5], a._type = 2; + } else { + var c = d[1], h = d[2], e = d[4], l = d[5]; + if (0 === c && 0 === h) { + var g = b[0] = 1 / d[0], d = b[3] = 1 / d[3]; + b[1] = 0; + b[2] = 0; + b[4] = -g * e; + b[5] = -d * l; + } else { + var g = d[0], d = d[3], n = g * d - c * h; + if (0 === n) { + a.setIdentity(); + return; + } + n = 1 / n; + b[0] = d * n; + c = b[1] = -c * n; + h = b[2] = -h * n; + d = b[3] = g * n; + b[4] = -(b[0] * e + h * l); + b[5] = -(c * e + d * l); + } + a._type = 0; + } } }; - a.prototype.getTranslateX = function() { + b.prototype.getTranslateX = function() { return this._data[4]; }; - a.prototype.getTranslateY = function() { + b.prototype.getTranslateY = function() { return this._data[4]; }; - a.prototype.getScaleX = function() { + b.prototype.getScaleX = function() { var a = this._data; if (1 === a[0] && 0 === a[1]) { return 1; } - var b = Math.sqrt(a[0] * a[0] + a[1] * a[1]); - return 0 < a[0] ? b : -b; + var d = Math.sqrt(a[0] * a[0] + a[1] * a[1]); + return 0 < a[0] ? d : -d; }; - a.prototype.getScaleY = function() { + b.prototype.getScaleY = function() { var a = this._data; if (0 === a[2] && 1 === a[3]) { return 1; } - var b = Math.sqrt(a[2] * a[2] + a[3] * a[3]); - return 0 < a[3] ? b : -b; + var d = Math.sqrt(a[2] * a[2] + a[3] * a[3]); + return 0 < a[3] ? d : -d; + }; + b.prototype.getScale = function() { + return(this.getScaleX() + this.getScaleY()) / 2; }; - a.prototype.getAbsoluteScaleX = function() { + b.prototype.getAbsoluteScaleX = function() { return Math.abs(this.getScaleX()); }; - a.prototype.getAbsoluteScaleY = function() { + b.prototype.getAbsoluteScaleY = function() { return Math.abs(this.getScaleY()); }; - a.prototype.getRotation = function() { + b.prototype.getRotation = function() { var a = this._data; return 180 * Math.atan(a[1] / a[0]) / Math.PI; }; - a.prototype.isScaleOrRotation = function() { + b.prototype.isScaleOrRotation = function() { var a = this._data; return.01 > Math.abs(a[0] * a[2] + a[1] * a[3]); }; - a.prototype.toString = function() { - var a = this._data; - return "{" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ", " + a[4] + ", " + a[5] + "}"; + b.prototype.toString = function(a) { + void 0 === a && (a = 2); + var d = this._data; + return "{" + d[0].toFixed(a) + ", " + d[1].toFixed(a) + ", " + d[2].toFixed(a) + ", " + d[3].toFixed(a) + ", " + d[4].toFixed(a) + ", " + d[5].toFixed(a) + "}"; }; - a.prototype.toWebGLMatrix = function() { + b.prototype.toWebGLMatrix = function() { var a = this._data; return new Float32Array([a[0], a[1], 0, a[2], a[3], 0, a[4], a[5], 1]); }; - a.prototype.toCSSTransform = function() { + b.prototype.toCSSTransform = function() { var a = this._data; return "matrix(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ", " + a[4] + ", " + a[5] + ")"; }; - a.createIdentity = function() { - return new a(1, 0, 0, 1, 0, 0); + b.createIdentity = function() { + var a = b.allocate(); + a.setIdentity(); + return a; }; - a.prototype.toSVGMatrix = function() { - var b = this._data, c = a._svg.createSVGMatrix(); - c.a = b[0]; - c.b = b[1]; - c.c = b[2]; - c.d = b[3]; - c.e = b[4]; - c.f = b[5]; - return c; + b.prototype.toSVGMatrix = function() { + var a = this._data, d = b._svg.createSVGMatrix(); + d.a = a[0]; + d.b = a[1]; + d.c = a[2]; + d.d = a[3]; + d.e = a[4]; + d.f = a[5]; + return d; }; - a.prototype.snap = function() { + b.prototype.snap = function() { var a = this._data; - return this.isTranslationOnly() ? (a[0] = 1, a[1] = 0, a[2] = 0, a[3] = 1, a[4] = Math.round(a[4]), a[5] = Math.round(a[5]), !0) : !1; + return this.isTranslationOnly() ? (a[0] = 1, a[1] = 0, a[2] = 0, a[3] = 1, a[4] = Math.round(a[4]), a[5] = Math.round(a[5]), this._type = 2, !0) : !1; }; - a.createIdentitySVGMatrix = function() { - return a._svg.createSVGMatrix(); + b.createIdentitySVGMatrix = function() { + return b._svg.createSVGMatrix(); }; - a.createSVGMatrixFromArray = function(b) { - var c = a._svg.createSVGMatrix(); - c.a = b[0]; - c.b = b[1]; - c.c = b[2]; - c.d = b[3]; - c.e = b[4]; - c.f = b[5]; - return c; + b.createSVGMatrixFromArray = function(a) { + var d = b._svg.createSVGMatrix(); + d.a = a[0]; + d.b = a[1]; + d.c = a[2]; + d.d = a[3]; + d.e = a[4]; + d.f = a[5]; + return d; }; - a._svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - a.multiply = function(a, b) { - var c = b._data; - a.transform(c[0], c[1], c[2], c[3], c[4], c[5]); + b.allocationCount = 0; + b._dirtyStack = []; + b._svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + b.multiply = function(a, d) { + var b = d._data; + a.transform(b[0], b[1], b[2], b[3], b[4], b[5]); }; - return a; + return b; }(); - s.Matrix = r; - r = function() { + e.Matrix = t; + t = function() { function a(b) { this._m = new Float32Array(b); } a.prototype.asWebGLMatrix = function() { return this._m; }; - a.createCameraLookAt = function(b, c, d) { - c = b.clone().sub(c).normalize(); - d = d.clone().cross(c).normalize(); - var g = c.clone().cross(d); - return new a([d.x, d.y, d.z, 0, g.x, g.y, g.z, 0, c.x, c.y, c.z, 0, b.x, b.y, b.z, 1]); - }; - a.createLookAt = function(b, c, d) { - c = b.clone().sub(c).normalize(); - d = d.clone().cross(c).normalize(); - var g = c.clone().cross(d); - return new a([d.x, g.x, c.x, 0, g.x, g.y, c.y, 0, c.x, g.z, c.z, 0, -d.dot(b), -g.dot(b), -c.dot(b), 1]); + a.createCameraLookAt = function(b, d, c) { + d = b.clone().sub(d).normalize(); + c = c.clone().cross(d).normalize(); + var e = d.clone().cross(c); + return new a([c.x, c.y, c.z, 0, e.x, e.y, e.z, 0, d.x, d.y, d.z, 0, b.x, b.y, b.z, 1]); + }; + a.createLookAt = function(b, d, c) { + d = b.clone().sub(d).normalize(); + c = c.clone().cross(d).normalize(); + var e = d.clone().cross(c); + return new a([c.x, e.x, d.x, 0, e.x, e.y, d.y, 0, d.x, e.z, d.z, 0, -c.dot(b), -e.dot(b), -d.dot(b), 1]); }; a.prototype.mul = function(a) { a = [a.x, a.y, a.z, 0]; - for (var b = this._m, c = [], d = 0;4 > d;d++) { - c[d] = 0; - for (var g = 4 * d, e = 0;4 > e;e++) { - c[d] += b[g + e] * a[e]; + for (var b = this._m, c = [], h = 0;4 > h;h++) { + c[h] = 0; + for (var s = 4 * h, e = 0;4 > e;e++) { + c[h] += b[s + e] * a[e]; } } - return new v(c[0], c[1], c[2]); + return new y(c[0], c[1], c[2]); }; - a.create2DProjection = function(b, c, d) { - return new a([2 / b, 0, 0, 0, 0, -2 / c, 0, 0, 0, 0, 2 / d, 0, -1, 1, 0, 1]); + a.create2DProjection = function(b, d, c) { + return new a([2 / b, 0, 0, 0, 0, -2 / d, 0, 0, 0, 0, 2 / c, 0, -1, 1, 0, 1]); }; a.createPerspective = function(b) { b = Math.tan(.5 * Math.PI - .5 * b); - var c = 1 / -4999.9; - return new a([b / 1, 0, 0, 0, 0, b, 0, 0, 0, 0, 5000.1 * c, -1, 0, 0, 1E3 * c, 0]); + var d = 1 / -4999.9; + return new a([b / 1, 0, 0, 0, 0, b, 0, 0, 0, 0, 5000.1 * d, -1, 0, 0, 1E3 * d, 0]); }; a.createIdentity = function() { return a.createTranslation(0, 0); }; - a.createTranslation = function(b, c) { - return new a([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, b, c, 0, 1]); + a.createTranslation = function(b, d) { + return new a([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, b, d, 0, 1]); }; a.createXRotation = function(b) { - var c = Math.cos(b); + var d = Math.cos(b); b = Math.sin(b); - return new a([1, 0, 0, 0, 0, c, b, 0, 0, -b, c, 0, 0, 0, 0, 1]); + return new a([1, 0, 0, 0, 0, d, b, 0, 0, -b, d, 0, 0, 0, 0, 1]); }; a.createYRotation = function(b) { - var c = Math.cos(b); + var d = Math.cos(b); b = Math.sin(b); - return new a([c, 0, -b, 0, 0, 1, 0, 0, b, 0, c, 0, 0, 0, 0, 1]); + return new a([d, 0, -b, 0, 0, 1, 0, 0, b, 0, d, 0, 0, 0, 0, 1]); }; a.createZRotation = function(b) { - var c = Math.cos(b); + var d = Math.cos(b); b = Math.sin(b); - return new a([c, b, 0, 0, -b, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + return new a([d, b, 0, 0, -b, d, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); }; - a.createScale = function(b, c, d) { - return new a([b, 0, 0, 0, 0, c, 0, 0, 0, 0, d, 0, 0, 0, 0, 1]); + a.createScale = function(b, d, c) { + return new a([b, 0, 0, 0, 0, d, 0, 0, 0, 0, c, 0, 0, 0, 0, 1]); }; - a.createMultiply = function(b, c) { - var d = b._m, g = c._m, e = d[0], h = d[1], l = d[2], p = d[3], r = d[4], k = d[5], m = d[6], w = d[7], v = d[8], u = d[9], s = d[10], t = d[11], y = d[12], A = d[13], D = d[14], d = d[15], J = g[0], H = g[1], K = g[2], M = g[3], N = g[4], O = g[5], P = g[6], Q = g[7], R = g[8], S = g[9], T = g[10], W = g[11], X = g[12], Y = g[13], Z = g[14], g = g[15]; - return new a([e * J + h * N + l * R + p * X, e * H + h * O + l * S + p * Y, e * K + h * P + l * T + p * Z, e * M + h * Q + l * W + p * g, r * J + k * N + m * R + w * X, r * H + k * O + m * S + w * Y, r * K + k * P + m * T + w * Z, r * M + k * Q + m * W + w * g, v * J + u * N + s * R + t * X, v * H + u * O + s * S + t * Y, v * K + u * P + s * T + t * Z, v * M + u * Q + s * W + t * g, y * J + A * N + D * R + d * X, y * H + A * O + D * S + d * Y, y * K + A * P + D * T + d * Z, y * M + A * - Q + D * W + d * g]); + a.createMultiply = function(b, d) { + var c = b._m, e = d._m, s = c[0], l = c[1], g = c[2], n = c[3], k = c[4], p = c[5], r = c[6], t = c[7], m = c[8], v = c[9], u = c[10], y = c[11], w = c[12], A = c[13], C = c[14], c = c[15], D = e[0], z = e[1], E = e[2], G = e[3], F = e[4], H = e[5], I = e[6], J = e[7], K = e[8], L = e[9], M = e[10], N = e[11], Q = e[12], R = e[13], S = e[14], e = e[15]; + return new a([s * D + l * F + g * K + n * Q, s * z + l * H + g * L + n * R, s * E + l * I + g * M + n * S, s * G + l * J + g * N + n * e, k * D + p * F + r * K + t * Q, k * z + p * H + r * L + t * R, k * E + p * I + r * M + t * S, k * G + p * J + r * N + t * e, m * D + v * F + u * K + y * Q, m * z + v * H + u * L + y * R, m * E + v * I + u * M + y * S, m * G + v * J + u * N + y * e, w * D + A * F + C * K + c * Q, w * z + A * H + C * L + c * R, w * E + A * I + C * M + c * S, w * G + A * + J + C * N + c * e]); }; a.createInverse = function(b) { - var c = b._m; - b = c[0]; - var d = c[1], g = c[2], e = c[3], h = c[4], l = c[5], p = c[6], r = c[7], k = c[8], m = c[9], w = c[10], v = c[11], u = c[12], s = c[13], t = c[14], c = c[15], y = w * c, A = t * v, D = p * c, J = t * r, H = p * v, K = w * r, M = g * c, N = t * e, O = g * v, P = w * e, Q = g * r, R = p * e, S = k * s, T = u * m, W = h * s, X = u * l, Y = h * m, Z = k * l, aa = b * s, ba = u * d, ca = b * m, da = k * d, ea = b * l, fa = h * d, ha = y * l + J * m + H * s - (A * l + D * m + K * s), ia = A * - d + M * m + P * s - (y * d + N * m + O * s), s = D * d + N * l + Q * s - (J * d + M * l + R * s), d = K * d + O * l + R * m - (H * d + P * l + Q * m), l = 1 / (b * ha + h * ia + k * s + u * d); - return new a([l * ha, l * ia, l * s, l * d, l * (A * h + D * k + K * u - (y * h + J * k + H * u)), l * (y * b + N * k + O * u - (A * b + M * k + P * u)), l * (J * b + M * h + R * u - (D * b + N * h + Q * u)), l * (H * b + P * h + Q * k - (K * b + O * h + R * k)), l * (S * r + X * v + Y * c - (T * r + W * v + Z * c)), l * (T * e + aa * v + da * c - (S * e + ba * v + ca * c)), l * (W * e + ba * r + ea * c - (X * e + aa * r + fa * c)), l * (Z * e + ca * r + fa * v - (Y * e + da * r + ea * - v)), l * (W * w + Z * t + T * p - (Y * t + S * p + X * w)), l * (ca * t + S * g + ba * w - (aa * w + da * t + T * g)), l * (aa * p + fa * t + X * g - (ea * t + W * g + ba * p)), l * (ea * w + Y * g + da * p - (ca * p + fa * w + Z * g))]); + var d = b._m; + b = d[0]; + var c = d[1], e = d[2], s = d[3], l = d[4], g = d[5], n = d[6], k = d[7], p = d[8], r = d[9], t = d[10], m = d[11], v = d[12], u = d[13], y = d[14], d = d[15], w = t * d, A = y * m, C = n * d, D = y * k, z = n * m, E = t * k, G = e * d, F = y * s, H = e * m, I = t * s, J = e * k, K = n * s, L = p * u, M = v * r, N = l * u, Q = v * g, R = l * r, S = p * g, Y = b * u, Z = v * c, $ = b * r, aa = p * c, ba = b * g, ca = l * c, da = w * g + D * r + z * u - (A * g + C * r + E * u), ea = A * c + + G * r + I * u - (w * c + F * r + H * u), u = C * c + F * g + J * u - (D * c + G * g + K * u), c = E * c + H * g + K * r - (z * c + I * g + J * r), g = 1 / (b * da + l * ea + p * u + v * c); + return new a([g * da, g * ea, g * u, g * c, g * (A * l + C * p + E * v - (w * l + D * p + z * v)), g * (w * b + F * p + H * v - (A * b + G * p + I * v)), g * (D * b + G * l + K * v - (C * b + F * l + J * v)), g * (z * b + I * l + J * p - (E * b + H * l + K * p)), g * (L * k + Q * m + R * d - (M * k + N * m + S * d)), g * (M * s + Y * m + aa * d - (L * s + Z * m + $ * d)), g * (N * s + Z * k + ba * d - (Q * s + Y * k + ca * d)), g * (S * s + $ * k + ca * m - (R * s + aa * k + ba * m)), g * + (N * t + S * y + M * n - (R * y + L * n + Q * t)), g * ($ * y + L * e + Z * t - (Y * t + aa * y + M * e)), g * (Y * n + ca * y + Q * e - (ba * y + N * e + Z * n)), g * (ba * t + R * e + aa * n - ($ * n + ca * t + S * e))]); }; return a; }(); - s.Matrix3D = r; - r = function() { - function a(b, c, d) { - "undefined" === typeof d && (d = 7); - var g = this.size = 1 << d; - this.sizeInBits = d; + e.Matrix3D = t; + t = function() { + function a(b, d, c) { + void 0 === c && (c = 7); + var e = this.size = 1 << c; + this.sizeInBits = c; this.w = b; - this.h = c; - this.c = Math.ceil(b / g); - this.r = Math.ceil(c / g); + this.h = d; + this.c = Math.ceil(b / e); + this.r = Math.ceil(d / e); this.grid = []; for (b = 0;b < this.r;b++) { - for (this.grid.push([]), c = 0;c < this.c;c++) { - this.grid[b][c] = new a.Cell(new u(c * g, b * g, g, g)); + for (this.grid.push([]), d = 0;d < this.c;d++) { + this.grid[b][d] = new a.Cell(new v(d * e, b * e, e, e)); } } } @@ -7127,22 +7596,22 @@ } }; a.prototype.getBounds = function() { - return new u(0, 0, this.w, this.h); + return new v(0, 0, this.w, this.h); }; a.prototype.addDirtyRectangle = function(a) { var b = a.x >> this.sizeInBits, c = a.y >> this.sizeInBits; if (!(b >= this.c || c >= this.r)) { 0 > b && (b = 0); 0 > c && (c = 0); - var d = this.grid[c][b]; + var h = this.grid[c][b]; a = a.clone(); a.snap(); - if (d.region.contains(a)) { - d.bounds.isEmpty() ? d.bounds.set(a) : d.bounds.contains(a) || d.bounds.union(a); + if (h.region.contains(a)) { + h.bounds.isEmpty() ? h.bounds.set(a) : h.bounds.contains(a) || h.bounds.union(a); } else { - for (var g = Math.min(this.c, Math.ceil((a.x + a.w) / this.size)) - b, e = Math.min(this.r, Math.ceil((a.y + a.h) / this.size)) - c, h = 0;h < g;h++) { - for (var n = 0;n < e;n++) { - d = this.grid[c + n][b + h], d = d.region.clone(), d.intersect(a), d.isEmpty() || this.addDirtyRectangle(d); + for (var e = Math.min(this.c, Math.ceil((a.x + a.w) / this.size)) - b, l = Math.min(this.r, Math.ceil((a.y + a.h) / this.size)) - c, g = 0;g < e;g++) { + for (var n = 0;n < l;n++) { + h = this.grid[c + n][b + g], h = h.region.clone(), h.intersect(a), h.isEmpty() || this.addDirtyRectangle(h); } } } @@ -7164,8 +7633,8 @@ return 0; } for (var b = 0, c = 0;c < this.r;c++) { - for (var d = 0;d < this.c;d++) { - b += this.grid[c][d].region.area(); + for (var h = 0;h < this.c;h++) { + b += this.grid[c][h].region.area(); } } return b / a; @@ -7176,32 +7645,32 @@ } if (b && b.drawGrid) { a.strokeStyle = "white"; - for (var d = 0;d < this.r;d++) { - for (var g = 0;g < this.c;g++) { - var e = this.grid[d][g]; + for (var h = 0;h < this.r;h++) { + for (var e = 0;e < this.c;e++) { + var g = this.grid[h][e]; a.beginPath(); - c(e.region); + c(g.region); a.closePath(); a.stroke(); } } } a.strokeStyle = "#E0F8D8"; - for (d = 0;d < this.r;d++) { - for (g = 0;g < this.c;g++) { - e = this.grid[d][g], a.beginPath(), c(e.bounds), a.closePath(), a.stroke(); + for (h = 0;h < this.r;h++) { + for (e = 0;e < this.c;e++) { + g = this.grid[h][e], a.beginPath(), c(g.bounds), a.closePath(), a.stroke(); } } }; - a.tmpRectangle = u.createEmpty(); + a.tmpRectangle = v.createEmpty(); return a; }(); - s.DirtyRegion = r; + e.DirtyRegion = t; (function(a) { var b = function() { function a(b) { this.region = b; - this.bounds = u.createEmpty(); + this.bounds = v.createEmpty(); } a.prototype.clear = function() { this.bounds.setEmpty(); @@ -7209,14 +7678,14 @@ return a; }(); a.Cell = b; - })(s.DirtyRegion || (s.DirtyRegion = {})); - var r = s.DirtyRegion, b = function() { - function a(b, c, d, g, e, h) { + })(t = e.DirtyRegion || (e.DirtyRegion = {})); + var r = function() { + function a(b, d, c, h, e, g) { this.index = b; - this.x = c; - this.y = d; - this.scale = h; - this.bounds = new u(c * g, d * e, g, e); + this.x = d; + this.y = c; + this.scale = g; + this.bounds = new v(d * h, c * e, h, e); } a.prototype.getOBB = function() { if (this._obb) { @@ -7228,21 +7697,21 @@ a.corners = p.createEmptyPoints(4); return a; }(); - s.Tile = b; - var h = function() { - function a(c, d, e, h, n) { - this.tileW = e; + e.Tile = r; + var u = function() { + function a(b, d, c, h, e) { + this.tileW = c; this.tileH = h; - this.scale = n; - this.w = c; + this.scale = e; + this.w = b; this.h = d; this.rows = Math.ceil(d / h); - this.columns = Math.ceil(c / e); - g(2048 > this.rows && 2048 > this.columns); + this.columns = Math.ceil(b / c); + n(2048 > this.rows && 2048 > this.columns); this.tiles = []; - for (d = c = 0;d < this.rows;d++) { - for (var l = 0;l < this.columns;l++) { - this.tiles.push(new b(c++, l, d, e, h, n)); + for (d = b = 0;d < this.rows;d++) { + for (var g = 0;g < this.columns;g++) { + this.tiles.push(new r(b++, g, d, c, h, e)); } } } @@ -7256,238 +7725,197 @@ var c = this.columns * this.rows; return 40 > c && b.isScaleOrRotation() ? this.getFewTiles(a, b, 10 < c) : this.getManyTiles(a, b); }; - a.prototype.getFewTiles = function(b, c, d) { - "undefined" === typeof d && (d = !0); - if (c.isTranslationOnly() && 1 === this.tiles.length) { - return this.tiles[0].bounds.intersectsTranslated(b, c.tx, c.ty) ? [this.tiles[0]] : []; - } - c.transformRectangle(b, a._points); - var g; - b = new u(0, 0, this.w, this.h); - d && (g = new l(a._points)); + a.prototype.getFewTiles = function(b, d, c) { + void 0 === c && (c = !0); + if (d.isTranslationOnly() && 1 === this.tiles.length) { + return this.tiles[0].bounds.intersectsTranslated(b, d.tx, d.ty) ? [this.tiles[0]] : []; + } + d.transformRectangle(b, a._points); + var e; + b = new v(0, 0, this.w, this.h); + c && (e = new l(a._points)); b.intersect(l.getBounds(a._points)); if (b.isEmpty()) { return[]; } - var e = b.x / this.tileW | 0; - c = b.y / this.tileH | 0; - var h = Math.ceil((b.x + b.w) / this.tileW) | 0, p = Math.ceil((b.y + b.h) / this.tileH) | 0, e = k(e, 0, this.columns), h = k(h, 0, this.columns); - c = k(c, 0, this.rows); - for (var p = k(p, 0, this.rows), r = [];e < h;e++) { - for (var m = c;m < p;m++) { - var w = this.tiles[m * this.columns + e]; - w.bounds.intersects(b) && (d ? w.getOBB().intersects(g) : 1) && r.push(w); - } - } - return r; - }; - a.prototype.getManyTiles = function(b, c) { - function d(a, b, c) { - return(a - b.x) * (c.y - b.y) / (c.x - b.x) + b.y; - } - function g(a, b, c, f, d) { - if (!(0 > c || c >= b.columns)) { - for (f = k(f, 0, b.rows), d = k(d + 1, 0, b.rows);f < d;f++) { - a.push(b.tiles[f * b.columns + c]); - } + var s = b.x / this.tileW | 0; + d = b.y / this.tileH | 0; + var g = Math.ceil((b.x + b.w) / this.tileW) | 0, n = Math.ceil((b.y + b.h) / this.tileH) | 0, s = k(s, 0, this.columns), g = k(g, 0, this.columns); + d = k(d, 0, this.rows); + for (var n = k(n, 0, this.rows), p = [];s < g;s++) { + for (var r = d;r < n;r++) { + var t = this.tiles[r * this.columns + s]; + t.bounds.intersects(b) && (c ? t.getOBB().intersects(e) : 1) && p.push(t); } } - var e = a._points; - c.transformRectangle(b, e); - for (var h = e[0].x < e[1].x ? 0 : 1, l = e[2].x < e[3].x ? 2 : 3, l = e[h].x < e[l].x ? h : l, h = [], p = 0;5 > p;p++, l++) { - h.push(e[l % 4]); - } - (h[1].x - h[0].x) * (h[3].y - h[0].y) < (h[1].y - h[0].y) * (h[3].x - h[0].x) && (e = h[1], h[1] = h[3], h[3] = e); - var e = [], r, m, l = Math.floor(h[0].x / this.tileW), p = (l + 1) * this.tileW; - if (h[2].x < p) { - r = Math.min(h[0].y, h[1].y, h[2].y, h[3].y); - m = Math.max(h[0].y, h[1].y, h[2].y, h[3].y); - var w = Math.floor(r / this.tileH), v = Math.floor(m / this.tileH); - g(e, this, l, w, v); - return e; + return p; + }; + a.prototype.getManyTiles = function(b, d) { + function c(a, b, d) { + return(a - b.x) * (d.y - b.y) / (d.x - b.x) + b.y; + } + function e(a, b, d, c, f) { + if (!(0 > d || d >= b.columns)) { + for (c = k(c, 0, b.rows), f = k(f + 1, 0, b.rows);c < f;c++) { + a.push(b.tiles[c * b.columns + d]); + } + } } - var u = 0, s = 4, t = !1; - if (h[0].x === h[1].x || h[0].x === h[3].x) { - h[0].x === h[1].x ? (t = !0, u++) : s--, r = d(p, h[u], h[u + 1]), m = d(p, h[s], h[s - 1]), w = Math.floor(h[u].y / this.tileH), v = Math.floor(h[s].y / this.tileH), g(e, this, l, w, v), l++; + var s = a._points; + d.transformRectangle(b, s); + for (var g = s[0].x < s[1].x ? 0 : 1, l = s[2].x < s[3].x ? 2 : 3, l = s[g].x < s[l].x ? g : l, g = [], n = 0;5 > n;n++, l++) { + g.push(s[l % 4]); + } + (g[1].x - g[0].x) * (g[3].y - g[0].y) < (g[1].y - g[0].y) * (g[3].x - g[0].x) && (s = g[1], g[1] = g[3], g[3] = s); + var s = [], p, r, l = Math.floor(g[0].x / this.tileW), n = (l + 1) * this.tileW; + if (g[2].x < n) { + p = Math.min(g[0].y, g[1].y, g[2].y, g[3].y); + r = Math.max(g[0].y, g[1].y, g[2].y, g[3].y); + var t = Math.floor(p / this.tileH), m = Math.floor(r / this.tileH); + e(s, this, l, t, m); + return s; + } + var v = 0, u = 4, y = !1; + if (g[0].x === g[1].x || g[0].x === g[3].x) { + g[0].x === g[1].x ? (y = !0, v++) : u--, p = c(n, g[v], g[v + 1]), r = c(n, g[u], g[u - 1]), t = Math.floor(g[v].y / this.tileH), m = Math.floor(g[u].y / this.tileH), e(s, this, l, t, m), l++; } do { - var F, y, A, D; - h[u + 1].x < p ? (F = h[u + 1].y, A = !0) : (F = d(p, h[u], h[u + 1]), A = !1); - h[s - 1].x < p ? (y = h[s - 1].y, D = !0) : (y = d(p, h[s], h[s - 1]), D = !1); - w = Math.floor((h[u].y < h[u + 1].y ? r : F) / this.tileH); - v = Math.floor((h[s].y > h[s - 1].y ? m : y) / this.tileH); - g(e, this, l, w, v); - if (A && t) { + var w, B, A, C; + g[v + 1].x < n ? (w = g[v + 1].y, A = !0) : (w = c(n, g[v], g[v + 1]), A = !1); + g[u - 1].x < n ? (B = g[u - 1].y, C = !0) : (B = c(n, g[u], g[u - 1]), C = !1); + t = Math.floor((g[v].y < g[v + 1].y ? p : w) / this.tileH); + m = Math.floor((g[u].y > g[u - 1].y ? r : B) / this.tileH); + e(s, this, l, t, m); + if (A && y) { break; } - A ? (t = !0, u++, r = d(p, h[u], h[u + 1])) : r = F; - D ? (s--, m = d(p, h[s], h[s - 1])) : m = y; + A ? (y = !0, v++, p = c(n, g[v], g[v + 1])) : p = w; + C ? (u--, r = c(n, g[u], g[u - 1])) : r = B; l++; - p = (l + 1) * this.tileW; - } while (u < s); - return e; + n = (l + 1) * this.tileW; + } while (v < u); + return s; }; a._points = p.createEmptyPoints(4); return a; }(); - s.TileCache = h; - r = function() { - function b(a, c, d) { + e.TileCache = u; + t = function() { + function a(b, d, c) { this._cacheLevels = []; - this._source = a; - this._tileSize = c; - this._minUntiledSize = d; - } - b.prototype._getTilesAtScale = function(b, c, d) { - var e = Math.max(c.getAbsoluteScaleX(), c.getAbsoluteScaleY()), n = 0; - 1 !== e && (n = k(Math.round(Math.log(1 / e) / Math.LN2), -5, 3)); - e = a(n); - if (this._source.hasFlags(1)) { + this._source = b; + this._tileSize = d; + this._minUntiledSize = c; + } + a.prototype._getTilesAtScale = function(a, d, c) { + var h = Math.max(d.getAbsoluteScaleX(), d.getAbsoluteScaleY()), e = 0; + 1 !== h && (e = k(Math.round(Math.log(1 / h) / Math.LN2), -5, 3)); + h = b(e); + if (this._source.hasFlags(1048576)) { for (;;) { - e = a(n); - if (d.contains(this._source.getBounds().getAbsoluteBounds().clone().scale(e, e))) { + h = b(e); + if (c.contains(this._source.getBounds().getAbsoluteBounds().clone().scale(h, h))) { break; } - n--; - g(-5 <= n); + e--; + n(-5 <= e); } } - this._source.hasFlags(4) || (n = k(n, -5, 0)); - e = a(n); - d = 5 + n; - n = this._cacheLevels[d]; - if (!n) { - var n = this._source.getBounds().getAbsoluteBounds().clone().scale(e, e), l, p; - this._source.hasFlags(1) || !this._source.hasFlags(8) || Math.max(n.w, n.h) <= this._minUntiledSize ? (l = n.w, p = n.h) : l = p = this._tileSize; - n = this._cacheLevels[d] = new h(n.w, n.h, l, p, e); - } - return n.getTiles(b, c.scaleClone(e, e)); - }; - b.prototype.fetchTiles = function(a, b, c, d) { - var g = new u(0, 0, c.canvas.width, c.canvas.height); - a = this._getTilesAtScale(a, b, g); - var h; + this._source.hasFlags(2097152) || (e = k(e, -5, 0)); + h = b(e); + c = 5 + e; + e = this._cacheLevels[c]; + if (!e) { + var e = this._source.getBounds().getAbsoluteBounds().clone().scale(h, h), g, l; + this._source.hasFlags(1048576) || !this._source.hasFlags(4194304) || Math.max(e.w, e.h) <= this._minUntiledSize ? (g = e.w, l = e.h) : g = l = this._tileSize; + e = this._cacheLevels[c] = new u(e.w, e.h, g, l, h); + } + return e.getTiles(a, d.scaleClone(h, h)); + }; + a.prototype.fetchTiles = function(a, b, c, h) { + var e = new v(0, 0, c.canvas.width, c.canvas.height); + a = this._getTilesAtScale(a, b, e); + var g; b = this._source; - for (var e = 0;e < a.length;e++) { - var n = a[e]; - n.cachedSurfaceRegion && n.cachedSurfaceRegion.surface && !b.hasFlags(3) || (h || (h = []), h.push(n)); + for (var l = 0;l < a.length;l++) { + var n = a[l]; + n.cachedSurfaceRegion && n.cachedSurfaceRegion.surface && !b.hasFlags(1048592) || (g || (g = []), g.push(n)); } - h && this._cacheTiles(c, h, d, g); - b.removeFlags(2); + g && this._cacheTiles(c, g, h, e); + b.removeFlags(16); return a; }; - b.prototype._getTileBounds = function(a) { - for (var b = u.createEmpty(), c = 0;c < a.length;c++) { + a.prototype._getTileBounds = function(a) { + for (var b = v.createEmpty(), c = 0;c < a.length;c++) { b.union(a[c].bounds); } return b; }; - b.prototype._cacheTiles = function(a, b, c, d, h) { - "undefined" === typeof h && (h = 4); - g(0 < h, "Infinite recursion is likely."); - var e = this._getTileBounds(b); + a.prototype._cacheTiles = function(a, b, c, h, e) { + void 0 === e && (e = 4); + n(0 < e, "Infinite recursion is likely."); + var g = this._getTileBounds(b); a.save(); a.setTransform(1, 0, 0, 1, 0, 0); - a.clearRect(0, 0, d.w, d.h); - a.translate(-e.x, -e.y); + a.clearRect(0, 0, h.w, h.h); + a.translate(-g.x, -g.y); a.scale(b[0].scale, b[0].scale); - var n = this._source.getBounds(); - a.translate(-n.x, -n.y); - 2 <= m.traceLevel && m.writer && m.writer.writeLn("Rendering Tiles: " + e); - this._source.render(a); + var l = this._source.getBounds(); + a.translate(-l.x, -l.y); + 2 <= m.traceLevel && m.writer && m.writer.writeLn("Rendering Tiles: " + g); + this._source.render(a, 0); a.restore(); - for (var n = null, l = 0;l < b.length;l++) { - var p = b[l], r = p.bounds.clone(); - r.x -= e.x; - r.y -= e.y; - d.contains(r) || (n || (n = []), n.push(p)); + for (var l = null, k = 0;k < b.length;k++) { + var p = b[k], r = p.bounds.clone(); + r.x -= g.x; + r.y -= g.y; + h.contains(r) || (l || (l = []), l.push(p)); p.cachedSurfaceRegion = c(p.cachedSurfaceRegion, a, r); } - n && (2 <= n.length ? (b = n.slice(0, n.length / 2 | 0), e = n.slice(b.length), this._cacheTiles(a, b, c, d, h - 1), this._cacheTiles(a, e, c, d, h - 1)) : this._cacheTiles(a, n, c, d, h - 1)); - }; - return b; - }(); - s.RenderableTileCache = r; - var w = function() { - return function(a, b) { - this.surfaceRegion = a; - this.scale = b; - }; - }(); - s.MipMapLevel = w; - r = function() { - function b(a, c, d) { - this._source = a; - this._levels = []; - this._surfaceRegionAllocator = c; - this._size = d; - } - b.prototype.render = function(a) { - }; - b.prototype.getLevel = function(b) { - b = Math.max(b.getAbsoluteScaleX(), b.getAbsoluteScaleY()); - var c = 0; - 1 !== b && (c = k(Math.round(Math.log(b) / Math.LN2), -5, 3)); - this._source.hasFlags(4) || (c = k(c, -5, 0)); - b = a(c); - var d = 5 + c, g = this._levels[d]; - if (!g) { - c = this._source.getBounds().clone(); - c.scale(b, b); - c.snap(); - var g = this._surfaceRegionAllocator.allocate(c.w, c.h), h = g.region, g = this._levels[d] = new w(g, b), d = g.surfaceRegion.surface.context; - d.save(); - d.beginPath(); - d.rect(h.x, h.y, h.w, h.h); - d.clip(); - d.setTransform(b, 0, 0, b, h.x - c.x, h.y - c.y); - this._source.render(d); - d.restore(); - } - return g; + l && (2 <= l.length ? (b = l.slice(0, l.length / 2 | 0), g = l.slice(b.length), this._cacheTiles(a, b, c, h, e - 1), this._cacheTiles(a, g, c, h, e - 1)) : this._cacheTiles(a, l, c, h, e - 1)); }; - return b; + return a; }(); - s.MipMap = r; + e.RenderableTileCache = t; })(m.Geometry || (m.Geometry = {})); - })(d.GFX || (d.GFX = {})); + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); -__extends = this.__extends || function(d, m) { - function s() { - this.constructor = d; +__extends = this.__extends || function(g, m) { + function e() { + this.constructor = g; } - for (var e in m) { - m.hasOwnProperty(e) && (d[e] = m[e]); + for (var c in m) { + m.hasOwnProperty(c) && (g[c] = m[c]); } - s.prototype = m.prototype; - d.prototype = new s; + e.prototype = m.prototype; + g.prototype = new e; }; -(function(d) { +(function(g) { (function(m) { - var s = d.IntegerUtilities.roundToMultipleOfPowerOfTwo, e = d.Debug.assert, t = m.Geometry.Rectangle; - (function(d) { - var a = function(a) { + var e = g.IntegerUtilities.roundToMultipleOfPowerOfTwo, c = g.Debug.assert, w = m.Geometry.Rectangle; + (function(g) { + var b = function(a) { function b() { a.apply(this, arguments); } __extends(b, a); return b; }(m.Geometry.Rectangle); - d.Region = a; - var c = function() { + g.Region = b; + var a = function() { function a(b, c) { - this._root = new g(0, 0, b | 0, c | 0, !1); + this._root = new n(0, 0, b | 0, c | 0, !1); } - a.prototype.allocate = function(a, c) { + a.prototype.allocate = function(a, b) { a = Math.ceil(a); - c = Math.ceil(c); - e(0 < a && 0 < c); - var d = this._root.insert(a, c); - d && (d.allocator = this, d.allocated = !0); - return d; + b = Math.ceil(b); + c(0 < a && 0 < b); + var h = this._root.insert(a, b); + h && (h.allocator = this, h.allocated = !0); + return h; }; a.prototype.free = function(a) { - e(a.allocator === this); + c(a.allocator === this); a.clear(); a.allocated = !1; }; @@ -7495,649 +7923,740 @@ a.MAX_DEPTH = 256; return a; }(); - d.CompactAllocator = c; - var g = function(a) { - function b(b, c, d, f, g) { - a.call(this, b, c, d, f); + g.CompactAllocator = a; + var n = function(b) { + function c(a, h, f, d, q) { + b.call(this, a, h, f, d); this._children = null; - this._horizontal = g; + this._horizontal = q; this.allocated = !1; } - __extends(b, a); - b.prototype.clear = function() { + __extends(c, b); + c.prototype.clear = function() { this._children = null; this.allocated = !1; }; - b.prototype.insert = function(a, b) { + c.prototype.insert = function(a, b) { return this._insert(a, b, 0); }; - b.prototype._insert = function(a, d, g) { - if (!(g > c.MAX_DEPTH || this.allocated || this.w < a || this.h < d)) { + c.prototype._insert = function(b, h, f) { + if (!(f > a.MAX_DEPTH || this.allocated || this.w < b || this.h < h)) { if (this._children) { - var f; - if ((f = this._children[0]._insert(a, d, g + 1)) || (f = this._children[1]._insert(a, d, g + 1))) { - return f; + var d; + if ((d = this._children[0]._insert(b, h, f + 1)) || (d = this._children[1]._insert(b, h, f + 1))) { + return d; } } else { - return f = !this._horizontal, c.RANDOM_ORIENTATION && (f = .5 <= Math.random()), this._children = this._horizontal ? [new b(this.x, this.y, this.w, d, !1), new b(this.x, this.y + d, this.w, this.h - d, f)] : [new b(this.x, this.y, a, this.h, !0), new b(this.x + a, this.y, this.w - a, this.h, f)], f = this._children[0], f.w === a && f.h === d ? (f.allocated = !0, f) : this._insert(a, d, g + 1); + return d = !this._horizontal, a.RANDOM_ORIENTATION && (d = .5 <= Math.random()), this._children = this._horizontal ? [new c(this.x, this.y, this.w, h, !1), new c(this.x, this.y + h, this.w, this.h - h, d)] : [new c(this.x, this.y, b, this.h, !0), new c(this.x + b, this.y, this.w - b, this.h, d)], d = this._children[0], d.w === b && d.h === h ? (d.allocated = !0, d) : this._insert(b, h, f + 1); } } }; - return b; - }(d.Region), p = function() { - function a(b, c, d, g) { - this._columns = b / d | 0; - this._rows = c / g | 0; - this._sizeW = d; - this._sizeH = g; + return c; + }(g.Region), p = function() { + function a(b, c, h, f) { + this._columns = b / h | 0; + this._rows = c / f | 0; + this._sizeW = h; + this._sizeH = f; this._freeList = []; this._index = 0; this._total = this._columns * this._rows; } - a.prototype.allocate = function(a, c) { + a.prototype.allocate = function(a, b) { a = Math.ceil(a); - c = Math.ceil(c); - e(0 < a && 0 < c); - var d = this._sizeW, g = this._sizeH; - if (a > d || c > g) { + b = Math.ceil(b); + c(0 < a && 0 < b); + var h = this._sizeW, f = this._sizeH; + if (a > h || b > f) { return null; } - var f = this._freeList, l = this._index; - return 0 < f.length ? (d = f.pop(), e(!1 === d.allocated), d.allocated = !0, d) : l < this._total ? (f = l / this._columns | 0, d = new v((l - f * this._columns) * d, f * g, a, c), d.index = l, d.allocator = this, d.allocated = !0, this._index++, d) : null; + var d = this._freeList, q = this._index; + return 0 < d.length ? (h = d.pop(), c(!1 === h.allocated), h.w = a, h.h = b, h.allocated = !0, h) : q < this._total ? (d = q / this._columns | 0, h = new y((q - d * this._columns) * h, d * f, a, b), h.index = q, h.allocator = this, h.allocated = !0, this._index++, h) : null; }; a.prototype.free = function(a) { - e(a.allocator === this); + c(a.allocator === this); a.allocated = !1; this._freeList.push(a); }; return a; }(); - d.GridAllocator = p; - var v = function(a) { - function b(b, c, d, f) { - a.call(this, b, c, d, f); + g.GridAllocator = p; + var y = function(a) { + function b(c, h, f, d) { + a.call(this, c, h, f, d); this.index = -1; } __extends(b, a); return b; - }(d.Region); - d.GridCell = v; - var u = function() { + }(g.Region); + g.GridCell = y; + var v = function() { return function(a, b, c) { this.size = a; this.region = b; this.allocator = c; }; }(), l = function(a) { - function b(b, c, d, f, g) { - a.call(this, b, c, d, f); - this.region = g; + function b(c, h, f, d, q) { + a.call(this, c, h, f, d); + this.region = q; } __extends(b, a); return b; - }(d.Region); - d.BucketCell = l; - a = function() { - function a(b, c) { - e(0 < b && 0 < c); + }(g.Region); + g.BucketCell = l; + b = function() { + function a(b, e) { + c(0 < b && 0 < e); this._buckets = []; this._w = b | 0; - this._h = c | 0; + this._h = e | 0; this._filled = 0; } - a.prototype.allocate = function(a, c) { + a.prototype.allocate = function(a, b) { a = Math.ceil(a); - c = Math.ceil(c); - e(0 < a && 0 < c); - var d = Math.max(a, c); - if (a > this._w || c > this._h) { + b = Math.ceil(b); + c(0 < a && 0 < b); + var h = Math.max(a, b); + if (a > this._w || b > this._h) { return null; } - var g = null, f, q = this._buckets; + var f = null, d, q = this._buckets; do { - for (var r = 0;r < q.length && !(q[r].size >= d && (f = q[r], g = f.allocator.allocate(a, c)));r++) { + for (var g = 0;g < q.length && !(q[g].size >= h && (d = q[g], f = d.allocator.allocate(a, b)));g++) { } - if (!g) { - var k = this._h - this._filled; - if (k < c) { + if (!f) { + var s = this._h - this._filled; + if (s < b) { return null; } - var r = s(d, 2), m = 2 * r; - m > k && (m = k); - if (m < r) { + var g = e(h, 8), n = 2 * g; + n > s && (n = s); + if (n < g) { return null; } - k = new t(0, this._filled, this._w, m); - this._buckets.push(new u(r, k, new p(k.w, k.h, r, r))); - this._filled += m; + s = new w(0, this._filled, this._w, n); + this._buckets.push(new v(g, s, new p(s.w, s.h, g, g))); + this._filled += n; } - } while (!g); - return new l(f.region.x + g.x, f.region.y + g.y, g.w, g.h, g); + } while (!f); + return new l(d.region.x + f.x, d.region.y + f.y, f.w, f.h, f); }; a.prototype.free = function(a) { a.region.allocator.free(a.region); }; return a; }(); - d.BucketAllocator = a; + g.BucketAllocator = b; })(m.RegionAllocator || (m.RegionAllocator = {})); - (function(d) { - var a = function() { - function a(c) { - this._createSurface = c; + (function(c) { + var b = function() { + function a(a) { + this._createSurface = a; this._surfaces = []; } Object.defineProperty(a.prototype, "surfaces", {get:function() { return this._surfaces; }, enumerable:!0, configurable:!0}); - a.prototype._createNewSurface = function(a, c) { - var d = this._createSurface(a, c); - this._surfaces.push(d); - return d; + a.prototype._createNewSurface = function(a, b) { + var c = this._createSurface(a, b); + this._surfaces.push(c); + return c; }; a.prototype.addSurface = function(a) { this._surfaces.push(a); }; - a.prototype.allocate = function(a, c) { - for (var d = 0;d < this._surfaces.length;d++) { - var e = this._surfaces[d].allocate(a, c); + a.prototype.allocate = function(a, b) { + for (var c = 0;c < this._surfaces.length;c++) { + var e = this._surfaces[c].allocate(a, b); if (e) { return e; } } - return this._createNewSurface(a, c).allocate(a, c); + return this._createNewSurface(a, b).allocate(a, b); }; a.prototype.free = function(a) { }; return a; }(); - d.SimpleAllocator = a; + c.SimpleAllocator = b; })(m.SurfaceRegionAllocator || (m.SurfaceRegionAllocator = {})); - })(d.GFX || (d.GFX = {})); + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - var s = m.Geometry.Point, e = m.Geometry.Matrix, t = d.Debug.assert, k = d.Debug.unexpected; - (function(a) { - a[a.None = 0] = "None"; - a[a.Upward = 1] = "Upward"; - a[a.Downward = 2] = "Downward"; - })(m.Direction || (m.Direction = {})); - (function(a) { - a[a.Never = 0] = "Never"; - a[a.Always = 1] = "Always"; - a[a.Auto = 2] = "Auto"; - })(m.PixelSnapping || (m.PixelSnapping = {})); + var e = m.Geometry.Rectangle, c = m.Geometry.Matrix, w = m.Geometry.DirtyRegion, k = g.Debug.assert; (function(a) { - a[a.Never = 0] = "Never"; - a[a.Always = 1] = "Always"; - })(m.Smoothing || (m.Smoothing = {})); + a[a.Normal = 1] = "Normal"; + a[a.Layer = 2] = "Layer"; + a[a.Multiply = 3] = "Multiply"; + a[a.Screen = 4] = "Screen"; + a[a.Lighten = 5] = "Lighten"; + a[a.Darken = 6] = "Darken"; + a[a.Difference = 7] = "Difference"; + a[a.Add = 8] = "Add"; + a[a.Subtract = 9] = "Subtract"; + a[a.Invert = 10] = "Invert"; + a[a.Alpha = 11] = "Alpha"; + a[a.Erase = 12] = "Erase"; + a[a.Overlay = 13] = "Overlay"; + a[a.HardLight = 14] = "HardLight"; + })(m.BlendMode || (m.BlendMode = {})); + var b = m.BlendMode; (function(a) { - a[a.Empty = 0] = "Empty"; - a[a.Dirty = 1] = "Dirty"; - a[a.IsMask = 2] = "IsMask"; - a[a.IgnoreMask = 8] = "IgnoreMask"; - a[a.IgnoreQuery = 16] = "IgnoreQuery"; - a[a.InvalidBounds = 32] = "InvalidBounds"; - a[a.InvalidConcatenatedMatrix = 64] = "InvalidConcatenatedMatrix"; - a[a.InvalidInvertedConcatenatedMatrix = 128] = "InvalidInvertedConcatenatedMatrix"; - a[a.InvalidConcatenatedColorMatrix = 256] = "InvalidConcatenatedColorMatrix"; - a[a.InvalidPaint = 512] = "InvalidPaint"; - a[a.EnterClip = 4096] = "EnterClip"; - a[a.LeaveClip = 8192] = "LeaveClip"; - a[a.Visible = 16384] = "Visible"; + a[a.None = 0] = "None"; + a[a.BoundsAutoCompute = 2] = "BoundsAutoCompute"; + a[a.IsMask = 4] = "IsMask"; + a[a.Dirty = 16] = "Dirty"; + a[a.InvalidBounds = 256] = "InvalidBounds"; + a[a.InvalidConcatenatedMatrix = 512] = "InvalidConcatenatedMatrix"; + a[a.InvalidInvertedConcatenatedMatrix = 1024] = "InvalidInvertedConcatenatedMatrix"; + a[a.InvalidConcatenatedColorMatrix = 2048] = "InvalidConcatenatedColorMatrix"; + a[a.UpOnAddedOrRemoved = a.InvalidBounds | a.Dirty] = "UpOnAddedOrRemoved"; + a[a.UpOnMoved = a.InvalidBounds | a.Dirty] = "UpOnMoved"; + a[a.DownOnAddedOrRemoved = a.InvalidConcatenatedMatrix | a.InvalidInvertedConcatenatedMatrix | a.InvalidConcatenatedColorMatrix] = "DownOnAddedOrRemoved"; + a[a.DownOnMoved = a.InvalidConcatenatedMatrix | a.InvalidInvertedConcatenatedMatrix | a.InvalidConcatenatedColorMatrix] = "DownOnMoved"; + a[a.UpOnColorMatrixChanged = a.Dirty] = "UpOnColorMatrixChanged"; + a[a.DownOnColorMatrixChanged = a.InvalidConcatenatedColorMatrix] = "DownOnColorMatrixChanged"; + a[a.Visible = 65536] = "Visible"; + a[a.UpOnInvalidate = a.InvalidBounds | a.Dirty] = "UpOnInvalidate"; + a[a.Default = a.BoundsAutoCompute | a.InvalidBounds | a.InvalidConcatenatedMatrix | a.InvalidInvertedConcatenatedMatrix | a.Visible] = "Default"; + a[a.CacheAsBitmap = 131072] = "CacheAsBitmap"; + a[a.PixelSnapping = 262144] = "PixelSnapping"; + a[a.ImageSmoothing = 524288] = "ImageSmoothing"; + a[a.Dynamic = 1048576] = "Dynamic"; + a[a.Scalable = 2097152] = "Scalable"; + a[a.Tileable = 4194304] = "Tileable"; a[a.Transparent = 32768] = "Transparent"; - })(m.FrameFlags || (m.FrameFlags = {})); + })(m.NodeFlags || (m.NodeFlags = {})); + var a = m.NodeFlags; + (function(a) { + a[a.Node = 1] = "Node"; + a[a.Shape = 3] = "Shape"; + a[a.Group = 5] = "Group"; + a[a.Stage = 13] = "Stage"; + a[a.Renderable = 33] = "Renderable"; + })(m.NodeType || (m.NodeType = {})); + var n = m.NodeType; (function(a) { a[a.None = 0] = "None"; - a[a.AllowMatrixWrite = 1] = "AllowMatrixWrite"; - a[a.AllowColorMatrixWrite = 2] = "AllowColorMatrixWrite"; - a[a.AllowBlendModeWrite = 4] = "AllowBlendModeWrite"; - a[a.AllowFiltersWrite = 8] = "AllowFiltersWrite"; - a[a.AllowMaskWrite = 16] = "AllowMaskWrite"; - a[a.AllowChildrenWrite = 32] = "AllowChildrenWrite"; - a[a.AllowClipWrite = 64] = "AllowClipWrite"; - a[a.AllowAllWrite = a.AllowMatrixWrite | a.AllowColorMatrixWrite | a.AllowBlendModeWrite | a.AllowFiltersWrite | a.AllowMaskWrite | a.AllowChildrenWrite | a.AllowClipWrite] = "AllowAllWrite"; - })(m.FrameCapabilityFlags || (m.FrameCapabilityFlags = {})); - var a = m.FrameCapabilityFlags, c = function() { - function c() { - this._id = c._nextID++; - this._flags = 17376; - this._capability = a.AllowAllWrite; - this._parent = null; - this._clip = -1; - this._blendMode = 1; - this._filters = []; - this._mask = null; - this._matrix = e.createIdentity(); - this._concatenatedMatrix = e.createIdentity(); - this._invertedConcatenatedMatrix = null; - this._colorMatrix = m.ColorMatrix.createIdentity(); - this._concatenatedColorMatrix = m.ColorMatrix.createIdentity(); - this._pixelSnapping = this._smoothing = 0; + a[a.OnStageBoundsChanged = 1] = "OnStageBoundsChanged"; + })(m.NodeEventType || (m.NodeEventType = {})); + var p = function() { + function a() { } - c._getAncestors = function(a, d) { - "undefined" === typeof d && (d = null); - var e = c._path; - for (e.length = 0;a && a !== d;) { - t(a !== a._parent), e.push(a), a = a._parent; + a.prototype.visitNode = function(a, b) { + }; + a.prototype.visitShape = function(a, b) { + this.visitNode(a, b); + }; + a.prototype.visitGroup = function(a, b) { + this.visitNode(a, b); + for (var c = a.getChildren(), f = 0;f < c.length;f++) { + c[f].visit(this, b); } - t(a === d, "Last ancestor is not an ancestor."); - return e; }; - Object.defineProperty(c.prototype, "parent", {get:function() { - return this._parent; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "id", {get:function() { + a.prototype.visitStage = function(a, b) { + this.visitGroup(a, b); + }; + a.prototype.visitRenderable = function(a, b) { + this.visitNode(a, b); + }; + return a; + }(); + m.NodeVisitor = p; + var y = function() { + return function() { + }; + }(); + m.State = y; + var v = function(a) { + function b() { + a.call(this); + this.matrix = c.createIdentity(); + } + __extends(b, a); + b.prototype.transform = function(a) { + var b = this.clone(); + b.matrix.preMultiply(a.getMatrix()); + return b; + }; + b.allocate = function() { + var a = b._dirtyStack, c = null; + a.length && (c = a.pop()); + return c; + }; + b.prototype.clone = function() { + var a = b.allocate(); + a || (a = new b); + a.set(this); + return a; + }; + b.prototype.set = function(a) { + this.matrix.set(a.matrix); + }; + b.prototype.free = function() { + b._dirtyStack.push(this); + }; + b._dirtyStack = []; + return b; + }(y); + m.MatrixState = v; + var l = function(a) { + function b() { + a.apply(this, arguments); + this.isDirty = !0; + } + __extends(b, a); + b.prototype.start = function(a, b) { + this._dirtyRegion = b; + var d = new v; + d.matrix.setIdentity(); + a.visit(this, d); + d.free(); + }; + b.prototype.visitGroup = function(a, b) { + var d = a.getChildren(); + this.visitNode(a, b); + for (var c = 0;c < d.length;c++) { + var f = d[c], h = b.transform(f.getTransform()); + f.visit(this, h); + h.free(); + } + }; + b.prototype.visitNode = function(a, b) { + a.hasFlags(16) && (this.isDirty = !0); + a.toggleFlags(16, !1); + }; + return b; + }(p); + m.DirtyNodeVisitor = l; + y = function(a) { + function b(d) { + a.call(this); + this.writer = d; + } + __extends(b, a); + b.prototype.visitNode = function(a, b) { + }; + b.prototype.visitShape = function(a, b) { + this.writer.writeLn(a.toString()); + this.visitNode(a, b); + }; + b.prototype.visitGroup = function(a, b) { + this.visitNode(a, b); + var d = a.getChildren(); + this.writer.enter(a.toString() + " " + d.length); + for (var c = 0;c < d.length;c++) { + d[c].visit(this, b); + } + this.writer.outdent(); + }; + b.prototype.visitStage = function(a, b) { + this.visitGroup(a, b); + }; + return b; + }(p); + m.TracingNodeVisitor = y; + var t = function() { + function b() { + this._clip = -1; + this._eventListeners = null; + this._id = b._nextId++; + this._type = 1; + this._index = -1; + this._parent = null; + this.reset(); + } + Object.defineProperty(b.prototype, "id", {get:function() { return this._id; }, enumerable:!0, configurable:!0}); - c.prototype._setFlags = function(a) { - this._flags |= a; + b.prototype._dispatchEvent = function(a) { + if (this._eventListeners) { + for (var b = this._eventListeners, c = 0;c < b.length;c++) { + var f = b[c]; + f.type === a && f.listener(this, a); + } + } }; - c.prototype._removeFlags = function(a) { - this._flags &= ~a; + b.prototype.addEventListener = function(a, b) { + this._eventListeners || (this._eventListeners = []); + this._eventListeners.push({type:a, listener:b}); }; - c.prototype._hasFlags = function(a) { - return(this._flags & a) === a; + Object.defineProperty(b.prototype, "properties", {get:function() { + return this._properties || (this._properties = {}); + }, enumerable:!0, configurable:!0}); + b.prototype.reset = function() { + this._flags = a.Default; + this._properties = this._transform = this._layer = this._bounds = null; }; - c.prototype._toggleFlags = function(a, c) { - this._flags = c ? this._flags | a : this._flags & ~a; + Object.defineProperty(b.prototype, "clip", {get:function() { + return this._clip; + }, set:function(a) { + this._clip = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(b.prototype, "parent", {get:function() { + return this._parent; + }, enumerable:!0, configurable:!0}); + b.prototype.getTransformedBounds = function(a) { + var b = this.getBounds(!0); + if (a !== this && !b.isEmpty()) { + var c = this.getTransform().getConcatenatedMatrix(); + a ? (a = a.getTransform().getInvertedConcatenatedMatrix(), a.preMultiply(c), a.transformRectangleAABB(b), a.free()) : c.transformRectangleAABB(b); + } + return b; }; - c.prototype._hasAnyFlags = function(a) { - return!!(this._flags & a); + b.prototype._markCurrentBoundsAsDirtyRegion = function() { }; - c.prototype._findClosestAncestor = function(a) { - for (var c = this;c;) { - if (!1 === c._hasFlags(a)) { - return c; + b.prototype.getStage = function(a) { + void 0 === a && (a = !0); + for (var b = this._parent;b;) { + if (b.isType(13)) { + var c = b; + if (a) { + if (c.dirtyRegion) { + return c; + } + } else { + return c; + } } - t(c !== c._parent); - c = c._parent; + b = b._parent; } return null; }; - c.prototype._isAncestor = function(a) { + b.prototype.getChildren = function(a) { + throw g.Debug.abstractMethod("Node::getChildren"); + }; + b.prototype.getBounds = function(a) { + throw g.Debug.abstractMethod("Node::getBounds"); + }; + b.prototype.setBounds = function(a) { + k(!(this._flags & 2)); + (this._bounds || (this._bounds = e.createEmpty())).set(a); + this.removeFlags(256); + }; + b.prototype.clone = function() { + throw g.Debug.abstractMethod("Node::clone"); + }; + b.prototype.setFlags = function(a) { + this._flags |= a; + }; + b.prototype.hasFlags = function(a) { + return(this._flags & a) === a; + }; + b.prototype.hasAnyFlags = function(a) { + return!!(this._flags & a); + }; + b.prototype.removeFlags = function(a) { + this._flags &= ~a; + }; + b.prototype.toggleFlags = function(a, b) { + this._flags = b ? this._flags | a : this._flags & ~a; + }; + b.prototype._propagateFlagsUp = function(a) { + if (0 !== a && !this.hasFlags(a)) { + this.hasFlags(2) || (a &= -257); + this.setFlags(a); + var b = this._parent; + b && b._propagateFlagsUp(a); + } + }; + b.prototype._propagateFlagsDown = function(a) { + throw g.Debug.abstractMethod("Node::_propagateFlagsDown"); + }; + b.prototype.isAncestor = function(a) { for (;a;) { if (a === this) { return!0; } - t(a !== a._parent); + k(a !== a._parent); a = a._parent; } return!1; }; - c.prototype.setCapability = function(a, c, d) { - "undefined" === typeof c && (c = !0); - "undefined" === typeof d && (d = 0); - this._capability = c ? this._capability | a : this._capability & ~a; - if (1 === d && this._parent) { - this._parent.setCapability(a, c, d); - } else { - if (2 === d && this instanceof m.FrameContainer) { - for (var g = this._children, e = 0;e < g.length;e++) { - g[e].setCapability(a, c, d); - } + b._getAncestors = function(a, c) { + var h = b._path; + for (h.length = 0;a && a !== c;) { + k(a !== a._parent), h.push(a), a = a._parent; + } + k(a === c, "Last ancestor is not an ancestor."); + return h; + }; + b.prototype._findClosestAncestor = function() { + for (var a = this;a;) { + if (!1 === a.hasFlags(512)) { + return a; } + k(a !== a._parent); + a = a._parent; } + return null; }; - c.prototype.removeCapability = function(a) { - this.setCapability(a, !1); + b.prototype.isType = function(a) { + return this._type === a; }; - c.prototype.hasCapability = function() { - return this._capability & 1; + b.prototype.isTypeOf = function(a) { + return(this._type & a) === a; }; - c.prototype.checkCapability = function(c) { - this._capability & c || k("Frame doesn't have capability: " + a[c]); + b.prototype.isLeaf = function() { + return this.isType(33) || this.isType(3); }; - c.prototype._propagateFlagsUp = function(a) { - if (!this._hasFlags(a)) { - this._setFlags(a); - var c = this._parent; - c && c._propagateFlagsUp(a); + b.prototype.isLinear = function() { + if (this.isLeaf()) { + return!0; } + if (this.isType(5)) { + var a = this._children; + if (1 === a.length && a[0].isLinear()) { + return!0; + } + } + return!1; }; - c.prototype._propagateFlagsDown = function(a) { - this._setFlags(a); + b.prototype.getTransformMatrix = function() { + var a; + void 0 === a && (a = !1); + return this.getTransform().getMatrix(a); }; - c.prototype._invalidatePosition = function() { - this._propagateFlagsDown(192); - this._parent && this._parent._invalidateBounds(); - this._invalidateParentPaint(); + b.prototype.getTransform = function() { + null === this._transform && (this._transform = new u(this)); + return this._transform; + }; + b.prototype.getLayer = function() { + null === this._layer && (this._layer = new h(this)); + return this._layer; }; - c.prototype.invalidatePaint = function() { - this._propagateFlagsUp(512); + b.prototype.visit = function(a, b) { + switch(this._type) { + case 1: + a.visitNode(this, b); + break; + case 5: + a.visitGroup(this, b); + break; + case 13: + a.visitStage(this, b); + break; + case 3: + a.visitShape(this, b); + break; + case 33: + a.visitRenderable(this, b); + break; + default: + g.Debug.unexpectedCase(); + } }; - c.prototype._invalidateParentPaint = function() { - this._parent && this._parent._propagateFlagsUp(512); + b.prototype.invalidate = function() { + this._propagateFlagsUp(a.UpOnInvalidate); }; - c.prototype._invalidateBounds = function() { - this._propagateFlagsUp(32); + b.prototype.toString = function(a) { + void 0 === a && (a = !1); + var b = n[this._type] + " " + this._id; + a && (b += " " + this._bounds.toString()); + return b; }; - Object.defineProperty(c.prototype, "properties", {get:function() { - return this._properties || (this._properties = Object.create(null)); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "x", {get:function() { - return this._matrix.tx; - }, set:function(a) { - this.checkCapability(1); - this._matrix.tx = a; - this._invalidatePosition(); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "y", {get:function() { - return this._matrix.ty; - }, set:function(a) { - this.checkCapability(1); - this._matrix.ty = a; - this._invalidatePosition(); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "matrix", {get:function() { - return this._matrix; - }, set:function(a) { - this.checkCapability(1); - this._matrix.set(a); - this._invalidatePosition(); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "blendMode", {get:function() { - return this._blendMode; - }, set:function(a) { - a |= 0; - this.checkCapability(4); - this._blendMode = a; - this._invalidateParentPaint(); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "filters", {get:function() { - return this._filters; - }, set:function(a) { - this.checkCapability(8); - this._filters = a; - this._invalidateParentPaint(); + b._path = []; + b._nextId = 0; + return b; + }(); + m.Node = t; + var r = function(b) { + function d() { + b.call(this); + this._type = 5; + this._children = []; + } + __extends(d, b); + d.prototype.getChildren = function(a) { + void 0 === a && (a = !1); + return a ? this._children.slice(0) : this._children; + }; + d.prototype.childAt = function(a) { + k(0 <= a && a < this._children.length); + return this._children[a]; + }; + Object.defineProperty(d.prototype, "child", {get:function() { + k(1 === this._children.length); + return this._children[0]; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "colorMatrix", {get:function() { - return this._colorMatrix; - }, set:function(a) { - this.checkCapability(2); - this._colorMatrix.set(a); - this._propagateFlagsDown(256); - this._invalidateParentPaint(); + Object.defineProperty(d.prototype, "groupChild", {get:function() { + k(1 === this._children.length); + k(this._children[0] instanceof d); + return this._children[0]; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "mask", {get:function() { - return this._mask; - }, set:function(a) { - this.checkCapability(16); - this._mask && this._mask !== a && this._mask._removeFlags(2); - if (this._mask = a) { - this._mask._setFlags(2), this._mask.invalidate(); + d.prototype.addChild = function(b) { + k(b); + k(!b.isAncestor(this)); + b._parent && b._parent.removeChildAt(b._index); + b._parent = this; + b._index = this._children.length; + this._children.push(b); + this._propagateFlagsUp(a.UpOnAddedOrRemoved); + b._propagateFlagsDown(a.DownOnAddedOrRemoved); + }; + d.prototype.removeChildAt = function(b) { + k(0 <= b && b < this._children.length); + var d = this._children[b]; + k(b === d._index); + this._children.splice(b, 1); + d._index = -1; + d._parent = null; + this._propagateFlagsUp(a.UpOnAddedOrRemoved); + d._propagateFlagsDown(a.DownOnAddedOrRemoved); + }; + d.prototype.clearChildren = function() { + for (var b = 0;b < this._children.length;b++) { + var d = this._children[b]; + d && (d._index = -1, d._parent = null, d._propagateFlagsDown(a.DownOnAddedOrRemoved)); } - this.invalidate(); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "clip", {get:function() { - return this._clip; - }, set:function(a) { - this.checkCapability(64); - this._clip = a; - }, enumerable:!0, configurable:!0}); - c.prototype.getBounds = function() { - t(!1, "Override this."); - return null; + this._children.length = 0; + this._propagateFlagsUp(a.UpOnAddedOrRemoved); }; - c.prototype.gatherPreviousDirtyRegions = function() { - var a = this.stage; - a.trackDirtyRegions && this.visit(function(c) { - if (c instanceof m.FrameContainer) { - return 0; + d.prototype._propagateFlagsDown = function(a) { + if (!this.hasFlags(a)) { + this.setFlags(a); + for (var b = this._children, d = 0;d < b.length;d++) { + b[d]._propagateFlagsDown(a); } - c._previouslyRenderedAABB && a.dirtyRegion.addDirtyRectangle(c._previouslyRenderedAABB); - return 0; - }); - }; - c.prototype.getConcatenatedColorMatrix = function() { - if (!this._parent) { - return this._colorMatrix; } - if (this._hasFlags(256)) { - for (var a = this._findClosestAncestor(256), d = c._getAncestors(this, a), e = a ? a._concatenatedColorMatrix.clone() : m.ColorMatrix.createIdentity(), l = d.length - 1;0 <= l;l--) { - a = d[l], t(a._hasFlags(256)), e.multiply(a._colorMatrix), a._concatenatedColorMatrix.set(e), a._removeFlags(256); + }; + d.prototype.getBounds = function(a) { + void 0 === a && (a = !1); + var b = this._bounds || (this._bounds = e.createEmpty()); + if (this.hasFlags(256)) { + b.setEmpty(); + for (var d = this._children, c = e.allocate(), f = 0;f < d.length;f++) { + var h = d[f]; + c.set(h.getBounds()); + h.getTransformMatrix().transformRectangleAABB(c); + b.union(c); } + c.free(); + this.removeFlags(256); } - return this._concatenatedColorMatrix; + return a ? b.clone() : b; }; - c.prototype.getConcatenatedAlpha = function(a) { - "undefined" === typeof a && (a = null); - for (var c = this, d = 1;c && c !== a;) { - d *= c._colorMatrix.alphaMultiplier, c = c._parent; - } - return d; + return d; + }(t); + m.Group = r; + var u = function() { + function b(a) { + this._node = a; + this._matrix = c.createIdentity(); + this._colorMatrix = m.ColorMatrix.createIdentity(); + this._concatenatedMatrix = c.createIdentity(); + this._invertedConcatenatedMatrix = c.createIdentity(); + this._concatenatedColorMatrix = m.ColorMatrix.createIdentity(); + } + b.prototype.setMatrix = function(b) { + this._matrix.isEqual(b) || (this._matrix.set(b), this._node._propagateFlagsUp(a.UpOnMoved), this._node._propagateFlagsDown(a.DownOnMoved)); }; - Object.defineProperty(c.prototype, "stage", {get:function() { - for (var a = this;a._parent;) { - a = a._parent; - } - return a instanceof m.Stage ? a : null; - }, enumerable:!0, configurable:!0}); - c.prototype.getConcatenatedMatrix = function() { - if (this._hasFlags(64)) { - for (var a = this._findClosestAncestor(64), d = c._getAncestors(this, a), k = a ? a._concatenatedMatrix.clone() : e.createIdentity(), l = d.length - 1;0 <= l;l--) { - a = d[l], t(a._hasFlags(64)), k.preMultiply(a._matrix), a._concatenatedMatrix.set(k), a._removeFlags(64); - } - } - return this._concatenatedMatrix; - }; - c.prototype._getInvertedConcatenatedMatrix = function() { - this._hasFlags(128) && (this._invertedConcatenatedMatrix || (this._invertedConcatenatedMatrix = e.createIdentity()), this._invertedConcatenatedMatrix.set(this.getConcatenatedMatrix()), this._invertedConcatenatedMatrix.inverse(this._invertedConcatenatedMatrix), this._removeFlags(128)); - return this._invertedConcatenatedMatrix; - }; - c.prototype.invalidate = function() { - this._setFlags(1); - }; - c.prototype.visit = function(a, c, d, g) { - "undefined" === typeof d && (d = 0); - "undefined" === typeof g && (g = 0); - var e, b, h = g & 8; - e = [this]; - var k, n = !!c; - n && (k = [c.clone()]); - for (var f = [d];0 < e.length;) { - b = e.pop(); - n && (c = k.pop()); - d = f.pop() | b._flags; - var q = a(b, c, d); - if (0 === q) { - if (b instanceof m.FrameContainer) { - var s = b._children.length; - if (g & 16 && !m.disableClipping.value) { - for (var q = b.gatherLeaveClipEvents(), L = s - 1;0 <= L;L--) { - if (q && q[L]) { - for (;q[L].length;) { - if (s = q[L].shift(), e.push(s), f.push(8192), n) { - var I = c.clone(); - I.preMultiply(s.matrix); - k.push(I); - } - } - } - var B = b._children[L]; - t(B); - e.push(B); - n && (I = c.clone(), I.preMultiply(B.matrix), k.push(I)); - 0 <= B.clip ? f.push(d | 4096) : f.push(d); - } - } else { - for (L = 0;L < s;L++) { - if (B = b._children[h ? L : s - 1 - L]) { - e.push(B), n && (I = c.clone(), I.preMultiply(B.matrix), k.push(I)), f.push(d); - } - } - } - } - } else { - if (1 === q) { - break; - } - } - } + b.prototype.setColorMatrix = function(b) { + this._colorMatrix.set(b); + this._node._propagateFlagsUp(a.UpOnColorMatrixChanged); + this._node._propagateFlagsDown(a.DownOnColorMatrixChanged); + }; + b.prototype.getMatrix = function(a) { + void 0 === a && (a = !1); + return a ? this._matrix.clone() : this._matrix; }; - c.prototype.getDepth = function() { - for (var a = 0, c = this;c._parent;) { - a++, c = c._parent; - } - return a; + b.prototype.hasColorMatrix = function() { + return null !== this._colorMatrix; }; - Object.defineProperty(c.prototype, "smoothing", {get:function() { - return this._smoothing; + b.prototype.getColorMatrix = function() { + var a; + void 0 === a && (a = !1); + null === this._colorMatrix && (this._colorMatrix = m.ColorMatrix.createIdentity()); + return a ? this._colorMatrix.clone() : this._colorMatrix; + }; + b.prototype.getConcatenatedMatrix = function(a) { + void 0 === a && (a = !1); + if (this._node.hasFlags(512)) { + for (var b = this._node._findClosestAncestor(), f = t._getAncestors(this._node, b), h = b ? b.getTransform()._concatenatedMatrix.clone() : c.createIdentity(), e = f.length - 1;0 <= e;e--) { + var b = f[e], g = b.getTransform(); + k(b.hasFlags(512)); + h.preMultiply(g._matrix); + g._concatenatedMatrix.set(h); + b.removeFlags(512); + } + } + return a ? this._concatenatedMatrix.clone() : this._concatenatedMatrix; + }; + b.prototype.getInvertedConcatenatedMatrix = function() { + var a = !0; + void 0 === a && (a = !1); + this._node.hasFlags(1024) && (this.getConcatenatedMatrix().inverse(this._invertedConcatenatedMatrix), this._node.removeFlags(1024)); + return a ? this._invertedConcatenatedMatrix.clone() : this._invertedConcatenatedMatrix; + }; + b.prototype.toString = function() { + return this._matrix.toString(); + }; + return b; + }(); + m.Transform = u; + var h = function() { + function a(b) { + this._node = b; + this._mask = null; + this._blendMode = 1; + } + Object.defineProperty(a.prototype, "filters", {get:function() { + return this._filters; }, set:function(a) { - this._smoothing = a; - this.invalidate(); + this._filters = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "pixelSnapping", {get:function() { - return this._pixelSnapping; + Object.defineProperty(a.prototype, "blendMode", {get:function() { + return this._blendMode; }, set:function(a) { - this._pixelSnapping = a; - this.invalidate(); + this._blendMode = a; }, enumerable:!0, configurable:!0}); - c.prototype.queryFramesByPoint = function(a) { - var c = !0, d = !0; - "undefined" === typeof c && (c = !1); - "undefined" === typeof d && (d = !1); - var g = e.createIdentity(), r = s.createEmpty(), b = []; - this.visit(function(e, k, n) { - if (n & 16) { - return 2; - } - k.inverse(g); - r.set(a); - g.transformPoint(r); - if (e.getBounds().containsPoint(r)) { - if (e instanceof m.FrameContainer) { - if (d && (b.push(e), !c)) { - return 1; - } - } else { - if (b.push(e), !c) { - return 1; - } - } - return 0; - } - return 2; - }, e.createIdentity(), 0); - b.reverse(); - return b; + Object.defineProperty(a.prototype, "mask", {get:function() { + return this._mask; + }, set:function(a) { + this._mask && this._mask !== a && this._mask.removeFlags(4); + (this._mask = a) && this._mask.setFlags(4); + }, enumerable:!0, configurable:!0}); + a.prototype.toString = function() { + return b[this._blendMode]; }; - c._path = []; - c._nextID = 0; - return c; + return a; }(); - m.Frame = c; - })(d.GFX || (d.GFX = {})); -})(Shumway || (Shumway = {})); -(function(d) { - (function(m) { - var s = m.Geometry.Rectangle, e = d.Debug.assert, t = function(d) { - function a() { - d.call(this); - this._children = []; + m.Layer = h; + y = function(a) { + function b(d) { + a.call(this); + k(d); + this._source = d; + this._type = 3; + this.ratio = 0; } - __extends(a, d); - a.prototype.addChild = function(a) { - this.checkCapability(32); - a && (e(a !== this), a._parent = this, a._invalidatePosition()); - this._children.push(a); - }; - a.prototype.addChildAt = function(a, d) { - this.checkCapability(32); - e(0 <= d && d <= this._children.length); - d === this._children.length ? this._children.push(a) : this._children.splice(d, 0, a); - a && (e(a !== this), a._parent = this, a._invalidatePosition()); - return a; - }; - a.prototype.removeChild = function(a) { - this.checkCapability(32); - a._parent === this && (a = this._children.indexOf(a), this.removeChildAt(a)); - }; - a.prototype.removeChildAt = function(a) { - this.checkCapability(32); - e(0 <= a && a < this._children.length); - if (a = this._children.splice(a, 1)[0]) { - a._parent = null, a._invalidatePosition(); - } - }; - a.prototype.clearChildren = function() { - this.checkCapability(32); - for (var a = 0;a < this._children.length;a++) { - var d = this._children[a]; - d && d._invalidatePosition(); - } - this._children.length = 0; + __extends(b, a); + b.prototype.getBounds = function(a) { + void 0 === a && (a = !1); + var b = this._bounds || (this._bounds = e.createEmpty()); + this.hasFlags(256) && (b.set(this._source.getBounds()), this.removeFlags(256)); + return a ? b.clone() : b; }; - a.prototype._propagateFlagsDown = function(a) { - if (!this._hasFlags(a)) { - this._setFlags(a); - for (var d = this._children, e = 0;e < d.length;e++) { - d[e]._propagateFlagsDown(a); - } - } + Object.defineProperty(b.prototype, "source", {get:function() { + return this._source; + }, enumerable:!0, configurable:!0}); + b.prototype._propagateFlagsDown = function(a) { + this.setFlags(a); }; - a.prototype.getBounds = function() { - if (!this._hasFlags(32)) { - return this._bounds; - } - for (var a = s.createEmpty(), d = 0;d < this._children.length;d++) { - var e = this._children[d], k = e.getBounds().clone(); - e.matrix.transformRectangleAABB(k); - a.union(k); - } - this._bounds = a; - this._removeFlags(32); - return a; + b.prototype.getChildren = function(a) { + return[this._source]; }; - a.prototype.gatherLeaveClipEvents = function() { - for (var a = this._children.length, d = null, e = 0;e < a;e++) { - var k = this._children[e]; - if (0 <= k.clip) { - var m = e + k.clip, d = d || []; - d[m] || (d[m] = []); - d[m].push(k); - } - } - return d; - }; - return a; - }(m.Frame); - m.FrameContainer = t; - })(d.GFX || (d.GFX = {})); -})(Shumway || (Shumway = {})); -(function(d) { - (function(m) { - var s = m.Geometry.Rectangle, e = m.Geometry.DirtyRegion, t = d.Debug.assert; - (function(a) { - a[a.Normal = 1] = "Normal"; - a[a.Layer = 2] = "Layer"; - a[a.Multiply = 3] = "Multiply"; - a[a.Screen = 4] = "Screen"; - a[a.Lighten = 5] = "Lighten"; - a[a.Darken = 6] = "Darken"; - a[a.Difference = 7] = "Difference"; - a[a.Add = 8] = "Add"; - a[a.Subtract = 9] = "Subtract"; - a[a.Invert = 10] = "Invert"; - a[a.Alpha = 11] = "Alpha"; - a[a.Erase = 12] = "Erase"; - a[a.Overlay = 13] = "Overlay"; - a[a.HardLight = 14] = "HardLight"; - })(m.BlendMode || (m.BlendMode = {})); - (function(a) { - a[a.None = 0] = "None"; - a[a.Continue = 0] = "Continue"; - a[a.Stop = 1] = "Stop"; - a[a.Skip = 2] = "Skip"; - a[a.FrontToBack = 8] = "FrontToBack"; - a[a.Clips = 16] = "Clips"; - })(m.VisitorFlags || (m.VisitorFlags = {})); - m.StageRendererOptions = function() { + return b; + }(t); + m.Shape = y; + m.RendererOptions = function() { return function() { this.debug = !1; this.paintRenderable = !0; - this.paintViewport = this.paintFlashing = this.paintBounds = !1; + this.paintViewport = this.paintFlashing = this.paintDirtyRegion = this.paintBounds = !1; + this.clear = !0; }; }(); (function(a) { @@ -8147,269 +8666,410 @@ a[a.DOM = 3] = "DOM"; a[a.SVG = 4] = "SVG"; })(m.Backend || (m.Backend = {})); - var k = function() { - function a(a, d, e) { - this._canvas = a; - this._stage = d; - this._options = e; - this._viewport = s.createSquare(); + p = function(a) { + function b(d, c, h) { + a.call(this); + this._container = d; + this._stage = c; + this._options = h; + this._viewport = e.createSquare(); + this._devicePixelRatio = 1; } - Object.defineProperty(a.prototype, "viewport", {set:function(a) { + __extends(b, a); + Object.defineProperty(b.prototype, "viewport", {set:function(a) { this._viewport.set(a); }, enumerable:!0, configurable:!0}); - a.prototype.render = function() { - }; - a.prototype.resize = function() { - }; - return a; - }(); - m.StageRenderer = k; - k = function(a) { - function c(c, d, k) { - "undefined" === typeof k && (k = !1); - a.call(this); - this.w = c; - this.h = d; - this.dirtyRegion = new e(c, d); - this.trackDirtyRegions = k; - this._setFlags(1); - } - __extends(c, a); - c.prototype.readyToRender = function() { - var a; - "undefined" === typeof a && (a = !0); - if (!this._hasFlags(512)) { - return!1; - } - a && this.visit(function(a) { - return a._hasFlags(512) ? (a._toggleFlags(512, !1), 0) : 2; - }); - return!0; + b.prototype.render = function() { + throw g.Debug.abstractMethod("Renderer::render"); }; - c.prototype.gatherMarkedDirtyRegions = function(a) { - var c = this; - this.visit(function(a, d, e) { - a._removeFlags(1); - if (a instanceof m.FrameContainer) { - return 0; - } - e & 1 && (e = a.getBounds().clone(), d.transformRectangleAABB(e), c.dirtyRegion.addDirtyRectangle(e), a._previouslyRenderedAABB && c.dirtyRegion.addDirtyRectangle(a._previouslyRenderedAABB)); - return 0; - }, a, 0); - }; - c.prototype.gatherFrames = function() { - var a = []; - this.visit(function(c, d) { - c instanceof m.FrameContainer || a.push(c); - return 0; - }, this.matrix); - return a; + b.prototype.resize = function() { + throw g.Debug.abstractMethod("Renderer::resize"); }; - c.prototype.gatherLayers = function() { - var a = [], c; - this.visit(function(d, e) { - if (d instanceof m.FrameContainer) { - return 0; - } - var l = d.getBounds().clone(); - e.transformRectangleAABB(l); - d._hasFlags(1) ? (c && a.push(c), a.push(l.clone()), c = null) : c ? c.union(l) : c = l.clone(); - return 0; - }, this.matrix); - c && a.push(c); - return a; + b.prototype.screenShot = function(a, b) { + throw g.Debug.abstractMethod("Renderer::screenShot"); }; - return c; - }(m.FrameContainer); - m.Stage = k; - k = function(a) { - function c(c, e) { + return b; + }(p); + m.Renderer = p; + p = function(a) { + function b(c, h, g) { + void 0 === g && (g = !1); a.call(this); - this.color = d.Color.None; - this._bounds = new s(0, 0, c, e); + this._dirtyVisitor = new l; + this._flags &= -3; + this._type = 13; + this._scaleMode = b.DEFAULT_SCALE; + this._align = b.DEFAULT_ALIGN; + this._content = new r; + this._content._flags &= -3; + this.addChild(this._content); + this.setFlags(16); + this.setBounds(new e(0, 0, c, h)); + g ? (this._dirtyRegion = new w(c, h), this._dirtyRegion.addDirtyRectangle(new e(0, 0, c, h))) : this._dirtyRegion = null; + this._updateContentMatrix(); } - __extends(c, a); - c.prototype.setBounds = function(a) { - this._bounds.set(a); + __extends(b, a); + Object.defineProperty(b.prototype, "dirtyRegion", {get:function() { + return this._dirtyRegion; + }, enumerable:!0, configurable:!0}); + b.prototype.setBounds = function(b) { + a.prototype.setBounds.call(this, b); + this._updateContentMatrix(); + this._dispatchEvent(1); + this._dirtyRegion && (this._dirtyRegion = new w(b.w, b.h), this._dirtyRegion.addDirtyRectangle(b)); }; - c.prototype.getBounds = function() { - return this._bounds; + Object.defineProperty(b.prototype, "content", {get:function() { + return this._content; + }, enumerable:!0, configurable:!0}); + b.prototype.readyToRender = function() { + this._dirtyVisitor.isDirty = !1; + this._dirtyVisitor.start(this, this._dirtyRegion); + return this._dirtyVisitor.isDirty ? !0 : !1; }; - return c; - }(m.FrameContainer); - m.ClipRectangle = k; - k = function(a) { - function c(c) { - a.call(this); - t(c); - this._source = c; - } - __extends(c, a); - Object.defineProperty(c.prototype, "source", {get:function() { - return this._source; + Object.defineProperty(b.prototype, "align", {get:function() { + return this._align; + }, set:function(a) { + this._align = a; + this._updateContentMatrix(); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(b.prototype, "scaleMode", {get:function() { + return this._scaleMode; + }, set:function(a) { + this._scaleMode = a; + this._updateContentMatrix(); }, enumerable:!0, configurable:!0}); - c.prototype.getBounds = function() { - return this.source.getBounds(); + b.prototype._updateContentMatrix = function() { + if (this._scaleMode === b.DEFAULT_SCALE && this._align === b.DEFAULT_ALIGN) { + this._content.getTransform().setMatrix(new c(1, 0, 0, 1, 0, 0)); + } else { + var a = this.getBounds(), f = this._content.getBounds(), h = a.w / f.w, e = a.h / f.h; + switch(this._scaleMode) { + case 2: + h = e = Math.max(h, e); + break; + case 4: + h = e = 1; + break; + case 1: + break; + default: + h = e = Math.min(h, e); + } + var g; + g = this._align & 4 ? 0 : this._align & 8 ? a.w - f.w * h : (a.w - f.w * h) / 2; + a = this._align & 1 ? 0 : this._align & 2 ? a.h - f.h * e : (a.h - f.h * e) / 2; + this._content.getTransform().setMatrix(new c(h, 0, 0, e, g, a)); + } }; - return c; - }(m.Frame); - m.Shape = k; - })(d.GFX || (d.GFX = {})); + b.DEFAULT_SCALE = 4; + b.DEFAULT_ALIGN = 5; + return b; + }(r); + m.Stage = p; + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - var s = m.Geometry.Rectangle, e = m.Geometry.Matrix, t = d.Debug.assertUnreachable, k = d.Debug.assert, a = d.Debug.unexpected, c = d.ArrayUtilities.indexOf; - (function(a) { - a[a.None = 0] = "None"; - a[a.Dynamic = 1] = "Dynamic"; - a[a.Dirty = 2] = "Dirty"; - a[a.Scalable = 4] = "Scalable"; - a[a.Tileable = 8] = "Tileable"; - a[a.Loading = 16] = "Loading"; - })(m.RenderableFlags || (m.RenderableFlags = {})); - var g = function() { - function a(b) { - this._flags = 0; - this.properties = {}; - this._frameReferrers = []; - this._renderableReferrers = []; - this._bounds = b.clone(); + function e(a, b, c) { + return a + (b - a) * c; + } + function c(a, b, c) { + return e(a >> 24 & 255, b >> 24 & 255, c) << 24 | e(a >> 16 & 255, b >> 16 & 255, c) << 16 | e(a >> 8 & 255, b >> 8 & 255, c) << 8 | e(a & 255, b & 255, c); + } + var w = m.Geometry.Point, k = m.Geometry.Rectangle, b = m.Geometry.Matrix, a = g.Debug.assertUnreachable, n = g.Debug.assert, p = g.ArrayUtilities.indexOf, y = function(a) { + function b() { + a.call(this); + this._parents = []; + this._renderableParents = []; + this._invalidateEventListeners = null; + this._flags &= -3; + this._type = 33; } - a.prototype.setFlags = function(a) { - this._flags |= a; - }; - a.prototype.hasFlags = function(a) { - return(this._flags & a) === a; - }; - a.prototype.removeFlags = function(a) { - this._flags &= ~a; + __extends(b, a); + b.prototype.addParent = function(a) { + n(a); + var b = p(this._parents, a); + n(0 > b); + this._parents.push(a); + }; + b.prototype.addRenderableParent = function(a) { + n(a); + var b = p(this._renderableParents, a); + n(0 > b); + this._renderableParents.push(a); + }; + b.prototype.wrap = function() { + for (var a, b = this._parents, d = 0;d < b.length;d++) { + if (a = b[d], !a._parent) { + return a; + } + } + a = new m.Shape(this); + this.addParent(a); + return a; }; - a.prototype.addFrameReferrer = function(a) { - c(this._frameReferrers, a); - this._frameReferrers.push(a); - }; - a.prototype.addRenderableReferrer = function(a) { - c(this._renderableReferrers, a); - this._renderableReferrers.push(a); - }; - a.prototype.invalidatePaint = function() { - this.setFlags(2); - for (var a = this._frameReferrers, b = 0;b < a.length;b++) { - a[b].invalidatePaint(); + b.prototype.invalidate = function() { + this.setFlags(16); + for (var a = this._parents, b = 0;b < a.length;b++) { + a[b].invalidate(); } - a = this._renderableReferrers; + a = this._renderableParents; for (b = 0;b < a.length;b++) { - a[b].invalidatePaint(); + a[b].invalidate(); + } + if (a = this._invalidateEventListeners) { + for (b = 0;b < a.length;b++) { + a[b](this); + } } }; - a.prototype.getBounds = function() { - return this._bounds; + b.prototype.addInvalidateEventListener = function(a) { + this._invalidateEventListeners || (this._invalidateEventListeners = []); + var b = p(this._invalidateEventListeners, a); + n(0 > b); + this._invalidateEventListeners.push(a); }; - a.prototype.render = function(a, b, c) { + b.prototype.getBounds = function(a) { + void 0 === a && (a = !1); + return a ? this._bounds.clone() : this._bounds; }; - return a; - }(); - m.Renderable = g; - var p = function(a) { - function b(c, d) { - a.call(this, c); - this.render = d; + b.prototype.getChildren = function(a) { + return null; + }; + b.prototype._propagateFlagsUp = function(a) { + if (0 !== a && !this.hasFlags(a)) { + for (var b = 0;b < this._parents.length;b++) { + this._parents[b]._propagateFlagsUp(a); + } + } + }; + b.prototype.render = function(a, b, d, c, f) { + }; + return b; + }(m.Node); + m.Renderable = y; + var v = function(a) { + function b(d, c) { + a.call(this); + this.setBounds(d); + this.render = c; } __extends(b, a); return b; - }(g); - m.CustomRenderable = p; - p = function(a) { - function b(c, d) { - a.call(this, d); - this._flags = 3; - this._lastCurrentTime = 0; - this._video = document.createElement("video"); - this._video.src = c; - this._video.loop = !0; - this._video.play(); + }(y); + m.CustomRenderable = v; + (function(a) { + a[a.Idle = 1] = "Idle"; + a[a.Playing = 2] = "Playing"; + a[a.Paused = 3] = "Paused"; + })(m.RenderableVideoState || (m.RenderableVideoState = {})); + v = function(a) { + function b(c, h) { + a.call(this); + this._flags = 1048592; + this._lastPausedTime = this._lastTimeInvalidated = 0; + this._seekHappens = !1; + this._isDOMElement = !0; + this.setBounds(new k(0, 0, 1, 1)); + this._assetId = c; + this._eventSerializer = h; + var e = document.createElement("video"), g = this._handleVideoEvent.bind(this); + e.preload = "metadata"; + e.addEventListener("play", g); + e.addEventListener("ended", g); + e.addEventListener("loadeddata", g); + e.addEventListener("progress", g); + e.addEventListener("waiting", g); + e.addEventListener("loadedmetadata", g); + e.addEventListener("error", g); + e.addEventListener("seeking", g); + this._video = e; + this._videoEventHandler = g; b._renderableVideos.push(this); + "undefined" !== typeof registerInspectorAsset && registerInspectorAsset(-1, -1, this); + this._state = 1; } __extends(b, a); - b.prototype.invalidatePaintCheck = function() { - this._lastCurrentTime !== this._video.currentTime && this.invalidatePaint(); - this._lastCurrentTime = this._video.currentTime; + Object.defineProperty(b.prototype, "video", {get:function() { + return this._video; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(b.prototype, "state", {get:function() { + return this._state; + }, enumerable:!0, configurable:!0}); + b.prototype.play = function() { + this._video.play(); + this._state = 2; + }; + b.prototype.pause = function() { + this._video.pause(); + this._state = 3; + }; + b.prototype._handleVideoEvent = function(a) { + var b = null, d = this._video; + switch(a.type) { + case "play": + a = 1; + break; + case "ended": + a = 2; + break; + case "loadeddata": + a = 3; + break; + case "progress": + a = 4; + break; + case "waiting": + a = 5; + break; + case "loadedmetadata": + a = 7; + b = {videoWidth:d.videoWidth, videoHeight:d.videoHeight, duration:d.duration}; + break; + case "error": + a = 6; + b = {code:d.error.code}; + break; + case "seeking": + a = 8; + this._seekHappens = !0; + break; + default: + return; + } + this._notifyNetStream(a, b); + }; + b.prototype._notifyNetStream = function(a, b) { + this._eventSerializer.sendVideoPlaybackEvent(this._assetId, a, b); + }; + b.prototype.processControlRequest = function(a, b) { + var d = this._video; + switch(a) { + case 1: + d.src = b.url; + this._notifyNetStream(0, null); + break; + case 2: + d && (b.paused && !d.paused ? (this._lastPausedTime = isNaN(b.time) ? d.currentTime : d.currentTime = b.time, this.pause()) : !b.paused && d.paused && (this.play(), isNaN(b.time) || this._lastPausedTime === b.time || (d.currentTime = b.time), this._seekHappens && (this._seekHappens = !1, this._notifyNetStream(3, null)))); + break; + case 3: + d && (d.currentTime = b.time); + break; + case 4: + return d ? d.currentTime : 0; + case 5: + return d ? d.duration : 0; + case 6: + d && (d.volume = b.volume); + break; + case 7: + if (!d) { + return 0; + } + var c = -1; + if (d.buffered) { + for (var f = 0;f < d.buffered.length;f++) { + c = Math.max(c, d.buffered.end(f)); + } + } else { + c = d.duration; + } + return Math.round(500 * c); + case 8: + return d ? Math.round(500 * d.duration) : 0; + } }; - b.invalidateVideos = function() { + b.prototype.checkForUpdate = function() { + this._lastTimeInvalidated !== this._video.currentTime && (this._isDOMElement || this.invalidate()); + this._lastTimeInvalidated = this._video.currentTime; + }; + b.checkForVideoUpdates = function() { for (var a = b._renderableVideos, c = 0;c < a.length;c++) { - a[c].invalidatePaintCheck(); + a[c].checkForUpdate(); } }; - b.prototype.render = function(a, b) { - this._video && a.drawImage(this._video, 0, 0); + b.prototype.render = function(a, b, d) { + (b = this._video) && 0 < b.videoWidth && a.drawImage(b, 0, 0, b.videoWidth, b.videoHeight, 0, 0, this._bounds.w, this._bounds.h); }; b._renderableVideos = []; return b; - }(g); - m.RenderableVideo = p; - p = function(b) { - function c(a, d) { - b.call(this, d); - this._flags = 3; + }(y); + m.RenderableVideo = v; + v = function(a) { + function b(d, c) { + a.call(this); + this._flags = 1048592; this.properties = {}; - this._canvas = a; - this._context = this._canvas.getContext("2d"); - this._imageData = this._context.createImageData(this._bounds.w, this._bounds.h); + this.setBounds(c); + d instanceof HTMLCanvasElement ? this._initializeSourceCanvas(d) : this._sourceImage = d; } - __extends(c, b); - c.FromDataBuffer = function(a, b, d) { - var e = document.createElement("canvas"); - e.width = d.w; - e.height = d.h; - d = new c(e, d); - d.updateFromDataBuffer(a, b); - return d; + __extends(b, a); + b.FromDataBuffer = function(a, c, f) { + var h = document.createElement("canvas"); + h.width = f.w; + h.height = f.h; + f = new b(h, f); + f.updateFromDataBuffer(a, c); + return f; }; - c.FromFrame = function(a, b, d, e, g) { - var h = document.createElement("canvas"), l = a.getBounds(); - h.width = l.w; - h.height = l.h; - h = new c(h, l); - h.drawFrame(a, b, d, e, g); - return h; + b.FromNode = function(a, c, f, h, e) { + var g = document.createElement("canvas"), l = a.getBounds(); + g.width = l.w; + g.height = l.h; + g = new b(g, l); + g.drawNode(a, c, f, h, e); + return g; + }; + b.FromImage = function(a) { + return new b(a, new k(0, 0, -1, -1)); }; - c.prototype.updateFromDataBuffer = function(b, c) { + b.prototype.updateFromDataBuffer = function(a, b) { if (m.imageUpdateOption.value) { - if (4 === b || 5 === b || 6 === b) { - var e = this; - e.setFlags(16); - var g = new Image; - g.src = URL.createObjectURL(c.toBlob()); - g.onload = function() { - e._context.drawImage(g, 0, 0); - e.removeFlags(16); - e.invalidatePaint(); - }; - g.onerror = function() { - a("Image loading error: " + d.ImageType[b]); - }; + var d = b.buffer; + if (4 === a || 5 === a || 6 === a) { + g.Debug.assertUnreachable("Mustn't encounter un-decoded images here"); } else { - m.imageConvertOption.value && d.ColorUtilities.convertImage(b, 3, new Int32Array(c.buffer), new Int32Array(this._imageData.data.buffer)), this._context.putImageData(this._imageData, 0, 0); + var c = this._bounds, f = this._imageData; + f && f.width === c.w && f.height === c.h || (f = this._imageData = this._context.createImageData(c.w, c.h)); + m.imageConvertOption.value && (d = new Int32Array(d), c = new Int32Array(f.data.buffer), g.ColorUtilities.convertImage(a, 3, d, c)); + this._ensureSourceCanvas(); + this._context.putImageData(f, 0, 0); } - this.invalidatePaint(); + this.invalidate(); } }; - c.prototype.readImageData = function(a) { - var b = this._context.getImageData(0, 0, this._canvas.width, this._canvas.height).data; - a.writeRawBytes(b); + b.prototype.readImageData = function(a) { + a.writeRawBytes(this.imageData.data); }; - c.prototype.render = function(a, b) { - this._canvas ? a.drawImage(this._canvas, 0, 0) : this._renderFallback(a); + b.prototype.render = function(a, b, d) { + this.renderSource ? a.drawImage(this.renderSource, 0, 0) : this._renderFallback(a); }; - c.prototype.drawFrame = function(a, b, c, d, e) { - c = m.Canvas2D; - d = this.getBounds(); - var g = new c.Canvas2DStageRendererOptions; - g.cacheShapes = !0; - (new c.Canvas2DStageRenderer(this._canvas, null, g)).renderFrame(a, e || d, b); + b.prototype.drawNode = function(a, b, d, c, f) { + d = m.Canvas2D; + c = this.getBounds(); + (new d.Canvas2DRenderer(this._canvas, null)).renderNode(a, f || c, b); + }; + b.prototype._initializeSourceCanvas = function(a) { + this._canvas = a; + this._context = this._canvas.getContext("2d"); }; - c.prototype._renderFallback = function(a) { - this.fillStyle || (this.fillStyle = d.ColorStyle.randomStyle()); + b.prototype._ensureSourceCanvas = function() { + if (!this._canvas) { + var a = document.createElement("canvas"), b = this._bounds; + a.width = b.w; + a.height = b.h; + this._initializeSourceCanvas(a); + } + }; + Object.defineProperty(b.prototype, "imageData", {get:function() { + this._canvas || (n(this._sourceImage), this._ensureSourceCanvas(), this._context.drawImage(this._sourceImage, 0, 0), this._sourceImage = null); + return this._context.getImageData(0, 0, this._bounds.w, this._bounds.h); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(b.prototype, "renderSource", {get:function() { + return this._canvas || this._sourceImage; + }, enumerable:!0, configurable:!0}); + b.prototype._renderFallback = function(a) { + this.fillStyle || (this.fillStyle = g.ColorStyle.randomStyle()); var b = this._bounds; a.save(); a.beginPath(); @@ -8418,458 +9078,635 @@ a.fillRect(b.x, b.y, b.w, b.h); a.restore(); }; - return c; - }(g); - m.RenderableBitmap = p; - var v; + return b; + }(y); + m.RenderableBitmap = v; (function(a) { a[a.Fill = 0] = "Fill"; a[a.Stroke = 1] = "Stroke"; a[a.StrokeFill = 2] = "StrokeFill"; - })(v || (v = {})); - var u = function() { - return function(a, b, c, d) { + })(m.PathType || (m.PathType = {})); + var l = function() { + return function(a, b, c, h) { this.type = a; this.style = b; this.smoothImage = c; - this.strokeProperties = d; + this.strokeProperties = h; this.path = new Path2D; - k(1 === a === !!d); + n(1 === a === !!h); }; - }(), l = function() { - return function(a, b, c, d, e) { + }(); + m.StyledPath = l; + var t = function() { + return function(a, b, c, h, e) { this.thickness = a; this.scaleMode = b; this.capsStyle = c; - this.jointsStyle = d; + this.jointsStyle = h; this.miterLimit = e; }; }(); - v = function(a) { - function b(c, d, e, g) { - a.call(this, g); - this._flags = 14; + m.StrokeProperties = t; + var r = function(c) { + function d(a, b, d, h) { + c.call(this); + this._flags = 6291472; this.properties = {}; - this._id = c; - this._pathData = d; - this._textures = e; - e.length && this.setFlags(1); - } - __extends(b, a); - b.prototype.update = function(a, b, c) { - this._bounds = c; + this.setBounds(h); + this._id = a; + this._pathData = b; + this._textures = d; + d.length && this.setFlags(1048576); + } + __extends(d, c); + d.prototype.update = function(a, b, d) { + this.setBounds(d); this._pathData = a; this._paths = null; this._textures = b; + this.setFlags(1048576); + this.invalidate(); }; - b.prototype.getBounds = function() { - return this._bounds; - }; - b.prototype.render = function(a, b, c) { - "undefined" === typeof c && (c = !1); + d.prototype.render = function(a, b, d, c, f) { + void 0 === c && (c = !1); + void 0 === f && (f = !1); a.fillStyle = a.strokeStyle = "transparent"; - var d = this._textures; - for (b = 0;b < d.length;b++) { - if (d[b].hasFlags(16)) { - return; + b = this._deserializePaths(this._pathData, a, b); + n(b); + for (d = 0;d < b.length;d++) { + var h = b[d]; + a.mozImageSmoothingEnabled = a.msImageSmoothingEnabled = a.imageSmoothingEnabled = h.smoothImage; + if (0 === h.type) { + a.fillStyle = f ? "#FF4981" : h.style, c ? a.clip(h.path, "evenodd") : a.fill(h.path, "evenodd"), a.fillStyle = "transparent"; + } else { + if (!c && !f) { + a.strokeStyle = h.style; + var e = 1; + h.strokeProperties && (e = h.strokeProperties.scaleMode, a.lineWidth = h.strokeProperties.thickness, a.lineCap = h.strokeProperties.capsStyle, a.lineJoin = h.strokeProperties.jointsStyle, a.miterLimit = h.strokeProperties.miterLimit); + var g = a.lineWidth; + (g = 1 === g || 3 === g) && a.translate(.5, .5); + a.flashStroke(h.path, e); + g && a.translate(-.5, -.5); + a.strokeStyle = "transparent"; + } + } + } + }; + d.prototype._deserializePaths = function(b, c, f) { + n(b ? !this._paths : this._paths); + if (this._paths) { + return this._paths; + } + f = this._paths = []; + for (var h = null, e = null, l = 0, k = 0, p, r, v = !1, m = 0, y = 0, u = b.commands, w = b.coordinates, B = b.styles, A = B.position = 0, C = b.commandsPosition, D = 0;D < C;D++) { + switch(p = u[D], p) { + case 9: + n(A <= b.coordinatesPosition - 2); + v && h && (h.lineTo(m, y), e && e.lineTo(m, y)); + v = !0; + l = m = w[A++] / 20; + k = y = w[A++] / 20; + h && h.moveTo(l, k); + e && e.moveTo(l, k); + break; + case 10: + n(A <= b.coordinatesPosition - 2); + l = w[A++] / 20; + k = w[A++] / 20; + h && h.lineTo(l, k); + e && e.lineTo(l, k); + break; + case 11: + n(A <= b.coordinatesPosition - 4); + p = w[A++] / 20; + r = w[A++] / 20; + l = w[A++] / 20; + k = w[A++] / 20; + h && h.quadraticCurveTo(p, r, l, k); + e && e.quadraticCurveTo(p, r, l, k); + break; + case 12: + n(A <= b.coordinatesPosition - 6); + p = w[A++] / 20; + r = w[A++] / 20; + var z = w[A++] / 20, E = w[A++] / 20, l = w[A++] / 20, k = w[A++] / 20; + h && h.bezierCurveTo(p, r, z, E, l, k); + e && e.bezierCurveTo(p, r, z, E, l, k); + break; + case 1: + n(4 <= B.bytesAvailable); + h = this._createPath(0, g.ColorUtilities.rgbaToCSSStyle(B.readUnsignedInt()), !1, null, l, k); + break; + case 3: + p = this._readBitmap(B, c); + h = this._createPath(0, p.style, p.smoothImage, null, l, k); + break; + case 2: + h = this._createPath(0, this._readGradient(B, c), !1, null, l, k); + break; + case 4: + h = null; + break; + case 5: + e = g.ColorUtilities.rgbaToCSSStyle(B.readUnsignedInt()); + B.position += 1; + p = B.readByte(); + r = d.LINE_CAPS_STYLES[B.readByte()]; + z = d.LINE_JOINTS_STYLES[B.readByte()]; + p = new t(w[A++] / 20, p, r, z, B.readByte()); + e = this._createPath(1, e, !1, p, l, k); + break; + case 6: + e = this._createPath(2, this._readGradient(B, c), !1, null, l, k); + break; + case 7: + p = this._readBitmap(B, c); + e = this._createPath(2, p.style, p.smoothImage, null, l, k); + break; + case 8: + e = null; + break; + default: + a("Invalid command " + p + " encountered at index" + D + " of " + C); } } - (b = this._pathData) && this._deserializePaths(b, a); - d = this._paths; - k(d); - for (b = 0;b < d.length;b++) { - var e = d[b]; - a.mozImageSmoothingEnabled = a.msImageSmoothingEnabled = a.imageSmoothingEnabled = e.smoothImage; - if (0 === e.type) { - a.fillStyle = e.style, c ? a.clip(e.path, "evenodd") : a.fill(e.path, "evenodd"), a.fillStyle = "transparent"; - } else { - if (!c) { - a.strokeStyle = e.style; - var g = 1; - e.strokeProperties && (g = e.strokeProperties.scaleMode, a.lineWidth = e.strokeProperties.thickness, a.lineCap = e.strokeProperties.capsStyle, a.lineJoin = e.strokeProperties.jointsStyle, a.miterLimit = e.strokeProperties.miterLimit); - var h = a.lineWidth; - (h = 1 === h || 3 === h) && a.translate(.5, .5); - a.flashStroke(e.path, g); - h && a.translate(-.5, -.5); - a.strokeStyle = "transparent"; - } - } + n(0 === B.bytesAvailable); + n(D === C); + n(A === b.coordinatesPosition); + v && h && (h.lineTo(m, y), e && e.lineTo(m, y)); + this._pathData = null; + return f; + }; + d.prototype._createPath = function(a, b, d, c, f, h) { + a = new l(a, b, d, c); + this._paths.push(a); + a.path.moveTo(f, h); + return a.path; + }; + d.prototype._readMatrix = function(a) { + return new b(a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat()); + }; + d.prototype._readGradient = function(a, b) { + n(34 <= a.bytesAvailable); + var d = a.readUnsignedByte(), c = 2 * a.readShort() / 255; + n(-1 <= c && 1 >= c); + var f = this._readMatrix(a), d = 16 === d ? b.createLinearGradient(-1, 0, 1, 0) : b.createRadialGradient(c, 0, 0, 0, 0, 1); + d.setTransform && d.setTransform(f.toSVGMatrix()); + f = a.readUnsignedByte(); + for (c = 0;c < f;c++) { + var h = a.readUnsignedByte() / 255, e = g.ColorUtilities.rgbaToCSSStyle(a.readUnsignedInt()); + d.addColorStop(h, e); } + a.position += 2; + return d; + }; + d.prototype._readBitmap = function(a, b) { + n(30 <= a.bytesAvailable); + var d = a.readUnsignedInt(), c = this._readMatrix(a), f = a.readBoolean() ? "repeat" : "no-repeat", h = a.readBoolean(); + (d = this._textures[d]) ? (f = b.createPattern(d.renderSource, f), f.setTransform(c.toSVGMatrix())) : f = null; + return{style:f, smoothImage:h}; + }; + d.prototype._renderFallback = function(a) { + this.fillStyle || (this.fillStyle = g.ColorStyle.randomStyle()); + var b = this._bounds; + a.save(); + a.beginPath(); + a.lineWidth = 2; + a.fillStyle = this.fillStyle; + a.fillRect(b.x, b.y, b.w, b.h); + a.restore(); }; - b.prototype._deserializePaths = function(a, c) { - k(!this._paths); - this._paths = []; - for (var e = null, g = null, h = 0, r = 0, p, m, u = !1, v = 0, s = 0, ga = a.commands, E = a.coordinates, x = a.styles, z = x.position = 0, G = a.commandsPosition, F = 0;F < G;F++) { - switch(p = ga[F], p) { + d.LINE_CAPS_STYLES = ["round", "butt", "square"]; + d.LINE_JOINTS_STYLES = ["round", "bevel", "miter"]; + return d; + }(y); + m.RenderableShape = r; + v = function(f) { + function d() { + f.apply(this, arguments); + this._flags = 7340048; + this._morphPaths = Object.create(null); + } + __extends(d, f); + d.prototype._deserializePaths = function(b, d, f) { + if (this._morphPaths[f]) { + return this._morphPaths[f]; + } + var h = this._morphPaths[f] = [], l = null, k = null, p = 0, v = 0, m, y, u = !1, w = 0, P = 0, U = b.commands, B = b.coordinates, A = b.morphCoordinates, C = b.styles, D = b.morphStyles; + C.position = 0; + for (var z = D.position = 0, E = b.commandsPosition, G = 0;G < E;G++) { + switch(m = U[G], m) { case 9: - k(z <= a.coordinatesPosition - 2); - u && e && (e.lineTo(v, s), g && g.lineTo(v, s)); + n(z <= b.coordinatesPosition - 2); + u && l && (l.lineTo(w, P), k && k.lineTo(w, P)); u = !0; - h = v = E[z++] / 20; - r = s = E[z++] / 20; - e && e.moveTo(h, r); - g && g.moveTo(h, r); + p = w = e(B[z], A[z++], f) / 20; + v = P = e(B[z], A[z++], f) / 20; + l && l.moveTo(p, v); + k && k.moveTo(p, v); break; case 10: - k(z <= a.coordinatesPosition - 2); - h = E[z++] / 20; - r = E[z++] / 20; - e && e.lineTo(h, r); - g && g.lineTo(h, r); + n(z <= b.coordinatesPosition - 2); + p = e(B[z], A[z++], f) / 20; + v = e(B[z], A[z++], f) / 20; + l && l.lineTo(p, v); + k && k.lineTo(p, v); break; case 11: - k(z <= a.coordinatesPosition - 4); - p = E[z++] / 20; - m = E[z++] / 20; - h = E[z++] / 20; - r = E[z++] / 20; - e && e.quadraticCurveTo(p, m, h, r); - g && g.quadraticCurveTo(p, m, h, r); + n(z <= b.coordinatesPosition - 4); + m = e(B[z], A[z++], f) / 20; + y = e(B[z], A[z++], f) / 20; + p = e(B[z], A[z++], f) / 20; + v = e(B[z], A[z++], f) / 20; + l && l.quadraticCurveTo(m, y, p, v); + k && k.quadraticCurveTo(m, y, p, v); break; case 12: - k(z <= a.coordinatesPosition - 6); - p = E[z++] / 20; - m = E[z++] / 20; - var y = E[z++] / 20, A = E[z++] / 20, h = E[z++] / 20, r = E[z++] / 20; - e && e.bezierCurveTo(p, m, y, A, h, r); - g && g.bezierCurveTo(p, m, y, A, h, r); + n(z <= b.coordinatesPosition - 6); + m = e(B[z], A[z++], f) / 20; + y = e(B[z], A[z++], f) / 20; + var F = e(B[z], A[z++], f) / 20, H = e(B[z], A[z++], f) / 20, p = e(B[z], A[z++], f) / 20, v = e(B[z], A[z++], f) / 20; + l && l.bezierCurveTo(m, y, F, H, p, v); + k && k.bezierCurveTo(m, y, F, H, p, v); break; case 1: - k(4 <= x.bytesAvailable); - e = this._createPath(0, d.ColorUtilities.rgbaToCSSStyle(x.readUnsignedInt()), !1, null, h, r); + n(4 <= C.bytesAvailable); + l = this._createMorphPath(0, f, g.ColorUtilities.rgbaToCSSStyle(c(C.readUnsignedInt(), D.readUnsignedInt(), f)), !1, null, p, v); break; case 3: - p = this._readBitmap(x, c); - e = this._createPath(0, p.style, p.smoothImage, null, h, r); + m = this._readMorphBitmap(C, D, f, d); + l = this._createMorphPath(0, f, m.style, m.smoothImage, null, p, v); break; case 2: - e = this._createPath(0, this._readGradient(x, c), !1, null, h, r); + m = this._readMorphGradient(C, D, f, d); + l = this._createMorphPath(0, f, m, !1, null, p, v); break; case 4: - e = null; + l = null; break; case 5: - g = d.ColorUtilities.rgbaToCSSStyle(x.readUnsignedInt()); - x.position += 1; - p = x.readByte(); - m = b.LINE_CAPS_STYLES[x.readByte()]; - y = b.LINE_JOINTS_STYLES[x.readByte()]; - p = new l(E[z++] / 20, p, m, y, x.readByte()); - g = this._createPath(1, g, !1, p, h, r); + m = e(B[z], A[z++], f) / 20; + k = g.ColorUtilities.rgbaToCSSStyle(c(C.readUnsignedInt(), D.readUnsignedInt(), f)); + C.position += 1; + y = C.readByte(); + F = r.LINE_CAPS_STYLES[C.readByte()]; + H = r.LINE_JOINTS_STYLES[C.readByte()]; + m = new t(m, y, F, H, C.readByte()); + k = this._createMorphPath(1, f, k, !1, m, p, v); break; case 6: - g = this._createPath(2, this._readGradient(x, c), !1, null, h, r); + m = this._readMorphGradient(C, D, f, d); + k = this._createMorphPath(2, f, m, !1, null, p, v); break; case 7: - p = this._readBitmap(x, c); - g = this._createPath(2, p.style, p.smoothImage, null, h, r); + m = this._readMorphBitmap(C, D, f, d); + k = this._createMorphPath(2, f, m.style, m.smoothImage, null, p, v); break; case 8: - g = null; + k = null; break; default: - t("Invalid command " + p + " encountered at index" + F + " of " + G); + a("Invalid command " + m + " encountered at index" + G + " of " + E); } } - k(0 === x.bytesAvailable); - k(F === G); - k(z === a.coordinatesPosition); - u && e && (e.lineTo(v, s), g && g.lineTo(v, s)); - this._pathData = null; + n(0 === C.bytesAvailable); + n(G === E); + n(z === b.coordinatesPosition); + u && l && (l.lineTo(w, P), k && k.lineTo(w, P)); + return h; }; - b.prototype._createPath = function(a, b, c, d, e, g) { - a = new u(a, b, c, d); - this._paths.push(a); - a.path.moveTo(e, g); + d.prototype._createMorphPath = function(a, b, d, c, f, h, e) { + a = new l(a, d, c, f); + this._morphPaths[b].push(a); + a.path.moveTo(h, e); return a.path; }; - b.prototype._readMatrix = function(a) { - return new e(a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat()); + d.prototype._readMorphMatrix = function(a, d, c) { + return new b(e(a.readFloat(), d.readFloat(), c), e(a.readFloat(), d.readFloat(), c), e(a.readFloat(), d.readFloat(), c), e(a.readFloat(), d.readFloat(), c), e(a.readFloat(), d.readFloat(), c), e(a.readFloat(), d.readFloat(), c)); }; - b.prototype._readGradient = function(a, b) { - k(34 <= a.bytesAvailable); - var c = a.readUnsignedByte(), e = 2 * a.readShort() / 255; - k(-1 <= e && 1 >= e); - var g = this._readMatrix(a), c = 16 === c ? b.createLinearGradient(-1, 0, 1, 0) : b.createRadialGradient(e, 0, 0, 0, 0, 1); - c.setTransform && c.setTransform(g.toSVGMatrix()); - g = a.readUnsignedByte(); - for (e = 0;e < g;e++) { - var h = a.readUnsignedByte() / 255, l = d.ColorUtilities.rgbaToCSSStyle(a.readUnsignedInt()); - c.addColorStop(h, l); + d.prototype._readMorphGradient = function(a, b, d, f) { + n(34 <= a.bytesAvailable); + var h = a.readUnsignedByte(), l = 2 * a.readShort() / 255; + n(-1 <= l && 1 >= l); + var k = this._readMorphMatrix(a, b, d); + f = 16 === h ? f.createLinearGradient(-1, 0, 1, 0) : f.createRadialGradient(l, 0, 0, 0, 0, 1); + f.setTransform && f.setTransform(k.toSVGMatrix()); + k = a.readUnsignedByte(); + for (h = 0;h < k;h++) { + var l = e(a.readUnsignedByte() / 255, b.readUnsignedByte() / 255, d), p = c(a.readUnsignedInt(), b.readUnsignedInt(), d), p = g.ColorUtilities.rgbaToCSSStyle(p); + f.addColorStop(l, p); } a.position += 2; - return c; - }; - b.prototype._readBitmap = function(a, b) { - k(30 <= a.bytesAvailable); - var c = a.readUnsignedInt(), d = this._readMatrix(a), e = a.readBoolean() ? "repeat" : "no-repeat", g = a.readBoolean(), c = this._textures[c]; - k(c._canvas); - e = b.createPattern(c._canvas, e); - e.setTransform(d.toSVGMatrix()); - return{style:e, smoothImage:g}; + return f; }; - b.prototype._renderFallback = function(a) { - this.fillStyle || (this.fillStyle = d.ColorStyle.randomStyle()); - var b = this._bounds; - a.save(); - a.beginPath(); - a.lineWidth = 2; - a.fillStyle = this.fillStyle; - a.fillRect(b.x, b.y, b.w, b.h); - a.restore(); + d.prototype._readMorphBitmap = function(a, b, d, c) { + n(30 <= a.bytesAvailable); + var f = a.readUnsignedInt(); + b = this._readMorphMatrix(a, b, d); + d = a.readBoolean() ? "repeat" : "no-repeat"; + a = a.readBoolean(); + f = this._textures[f]; + n(f._canvas); + c = c.createPattern(f._canvas, d); + c.setTransform(b.toSVGMatrix()); + return{style:c, smoothImage:a}; }; - b.LINE_CAPS_STYLES = ["round", "butt", "square"]; - b.LINE_JOINTS_STYLES = ["round", "bevel", "miter"]; - return b; - }(g); - m.RenderableShape = v; - var r = function() { + return d; + }(r); + m.RenderableMorphShape = v; + var u = function() { function a() { this.align = this.leading = this.descent = this.ascent = this.width = this.y = this.x = 0; this.runs = []; } - a.prototype.addRun = function(c, d, f, e) { - if (f) { - a._measureContext.font = c; - var g = a._measureContext.measureText(f).width | 0; - this.runs.push(new b(c, d, f, g, e)); - this.width += g; - } - }; - a.prototype.wrap = function(c) { - var d = [this], f = this.runs, e = this; - e.width = 0; - e.runs = []; - for (var g = a._measureContext, l = 0;l < f.length;l++) { - var r = f[l], k = r.text; - r.text = ""; - r.width = 0; - g.font = r.font; - for (var p = c, m = k.split(/[\s.-]/), u = 0, v = 0;v < m.length;v++) { - var s = m[v], t = k.substr(u, s.length + 1), x = g.measureText(t).width | 0; - if (x > p) { + a.prototype.addRun = function(b, c, e, g) { + if (e) { + a._measureContext.font = b; + var l = a._measureContext.measureText(e).width | 0; + this.runs.push(new h(b, c, e, l, g)); + this.width += l; + } + }; + a.prototype.wrap = function(b) { + var c = [this], e = this.runs, g = this; + g.width = 0; + g.runs = []; + for (var l = a._measureContext, n = 0;n < e.length;n++) { + var k = e[n], p = k.text; + k.text = ""; + k.width = 0; + l.font = k.font; + for (var r = b, t = p.split(/[\s.-]/), m = 0, v = 0;v < t.length;v++) { + var y = t[v], u = p.substr(m, y.length + 1), w = l.measureText(u).width | 0; + if (w > r) { do { - if (e.runs.push(r), e.width += r.width, r = new b(r.font, r.fillStyle, "", 0, r.underline), p = new a, p.y = e.y + e.descent + e.leading + e.ascent | 0, p.ascent = e.ascent, p.descent = e.descent, p.leading = e.leading, p.align = e.align, d.push(p), e = p, p = c - x, 0 > p) { - var x = t.length, z, G; + if (k.text && (g.runs.push(k), g.width += k.width, k = new h(k.font, k.fillStyle, "", 0, k.underline), r = new a, r.y = g.y + g.descent + g.leading + g.ascent | 0, r.ascent = g.ascent, r.descent = g.descent, r.leading = g.leading, r.align = g.align, c.push(r), g = r), r = b - w, 0 > r) { + var w = u.length, B, A; do { - x--, z = t.substr(0, x), G = g.measureText(z).width | 0; - } while (G > c); - r.text = z; - r.width = G; - t = t.substr(x); - x = g.measureText(t).width | 0; + w--; + if (1 > w) { + throw Error("Shall never happen: bad maxWidth?"); + } + B = u.substr(0, w); + A = l.measureText(B).width | 0; + } while (A > b); + k.text = B; + k.width = A; + u = u.substr(w); + w = l.measureText(u).width | 0; } - } while (0 > p); + } while (0 > r); } else { - p -= x; + r -= w; } - r.text += t; - r.width += x; - u += s.length + 1; + k.text += u; + k.width += w; + m += y.length + 1; } - e.runs.push(r); - e.width += r.width; + g.runs.push(k); + g.width += k.width; } - return d; + return c; }; a._measureContext = document.createElement("canvas").getContext("2d"); return a; }(); - m.TextLine = r; - var b = function() { - return function(a, b, c, d, e) { - "undefined" === typeof a && (a = ""); - "undefined" === typeof b && (b = ""); - "undefined" === typeof c && (c = ""); - "undefined" === typeof d && (d = 0); - "undefined" === typeof e && (e = !1); + m.TextLine = u; + var h = function() { + return function(a, b, c, h, e) { + void 0 === a && (a = ""); + void 0 === b && (b = ""); + void 0 === c && (c = ""); + void 0 === h && (h = 0); + void 0 === e && (e = !1); this.font = a; this.fillStyle = b; this.text = c; - this.width = d; + this.width = h; this.underline = e; }; }(); - m.TextRun = b; + m.TextRun = h; v = function(a) { - function b(c) { - a.call(this, c); - this._flags = 3; + function d(d) { + a.call(this); + this._flags = 1048592; this.properties = {}; - this._textBounds = c.clone(); + this._textBounds = d.clone(); this._textRunData = null; this._plainText = ""; this._borderColor = this._backgroundColor = 0; - this._matrix = e.createIdentity(); + this._matrix = b.createIdentity(); this._coords = null; - this.textRect = c.clone(); + this._scrollV = 1; + this._scrollH = 0; + this.textRect = d.clone(); this.lines = []; + this.setBounds(d); } - __extends(b, a); - b.prototype.setBounds = function(a) { - this._bounds.set(a); - this._textBounds.set(a); - this.textRect.setElements(a.x + 2, a.y + 2, a.x - 2, a.x - 2); + __extends(d, a); + d.prototype.setBounds = function(b) { + a.prototype.setBounds.call(this, b); + this._textBounds.set(b); + this.textRect.setElements(b.x + 2, b.y + 2, b.w - 2, b.h - 2); }; - b.prototype.setContent = function(a, b, c, d) { + d.prototype.setContent = function(a, b, d, c) { this._textRunData = b; this._plainText = a; - this._matrix.set(c); - this._coords = d; + this._matrix.set(d); + this._coords = c; this.lines = []; }; - b.prototype.setStyle = function(a, b) { + d.prototype.setStyle = function(a, b, d, c) { this._backgroundColor = a; this._borderColor = b; + this._scrollV = d; + this._scrollH = c; }; - b.prototype.reflow = function(a, b) { - var c = this._textRunData; - if (c) { - for (var e = this._bounds, g = e.w - 4, h = this._plainText, l = this.lines, p = new r, k = 0, m = 0, u = 0, v = 0, s = 0, w = -1;c.position < c.length;) { - var t = c.readInt(), G = c.readInt(), F = c.readInt(), y = c.readInt(), y = y ? "swffont" + y : c.readUTF(), A = c.readInt(), D = c.readInt(), J = c.readInt(); - A > u && (u = A); - D > v && (v = D); - J > s && (s = J); - A = c.readBoolean(); - D = ""; - c.readBoolean() && (D += "italic"); - A && (D += " bold"); - F = D + " " + F + "px " + y; - y = c.readInt(); - y = d.ColorUtilities.rgbToHex(y); - A = c.readInt(); - -1 === w && (w = A); - c.readBoolean(); - c.readInt(); - c.readInt(); - c.readInt(); - c.readInt(); - c.readInt(); - for (var A = c.readBoolean(), H = "", D = !1;!D;t++) { - D = t >= G - 1; - J = h[t]; - if ("\r" !== J && "\n" !== J && (H += J, t < h.length - 1)) { + d.prototype.reflow = function(a, b) { + var d = this._textRunData; + if (d) { + for (var c = this._bounds, f = c.w - 4, h = this._plainText, e = this.lines, l = new u, k = 0, n = 0, p = 0, r = 0, t = 0, m = -1;d.position < d.length;) { + var v = d.readInt(), y = d.readInt(), w = d.readInt(), D = d.readUTF(), z = d.readInt(), E = d.readInt(), G = d.readInt(); + z > p && (p = z); + E > r && (r = E); + G > t && (t = G); + z = d.readBoolean(); + E = ""; + d.readBoolean() && (E += "italic "); + z && (E += "bold "); + w = E + w + "px " + D; + D = d.readInt(); + D = g.ColorUtilities.rgbToHex(D); + z = d.readInt(); + -1 === m && (m = z); + d.readBoolean(); + d.readInt(); + d.readInt(); + d.readInt(); + d.readInt(); + d.readInt(); + for (var z = d.readBoolean(), F = "", E = !1;!E;v++) { + E = v >= y - 1; + G = h[v]; + if ("\r" !== G && "\n" !== G && (F += G, v < h.length - 1)) { continue; } - p.addRun(F, y, H, A); - if (p.runs.length) { - k += u; - p.y = k | 0; - k += v + s; - p.ascent = u; - p.descent = v; - p.leading = s; - p.align = w; - if (b && p.width > g) { - for (p = p.wrap(g), H = 0;H < p.length;H++) { - var K = p[H], k = K.y + K.descent + K.leading; - l.push(K); - K.width > m && (m = K.width); + l.addRun(w, D, F, z); + if (l.runs.length) { + e.length && (k += t); + k += p; + l.y = k | 0; + k += r; + l.ascent = p; + l.descent = r; + l.leading = t; + l.align = m; + if (b && l.width > f) { + for (l = l.wrap(f), F = 0;F < l.length;F++) { + var H = l[F], k = H.y + H.descent + H.leading; + e.push(H); + H.width > n && (n = H.width); } } else { - l.push(p), p.width > m && (m = p.width); + e.push(l), l.width > n && (n = l.width); } - p = new r; + l = new u; } else { - k += u + v + s; + k += p + r + t; } - H = ""; - if (D) { - s = v = u = 0; - w = -1; + F = ""; + if (E) { + t = r = p = 0; + m = -1; break; } - "\r" === J && "\n" === h[t + 1] && t++; + "\r" === G && "\n" === h[v + 1] && v++; } - p.addRun(F, y, H, A); + l.addRun(w, D, F, z); } - c = this.textRect; - c.w = m; - c.h = k; + d = h[h.length - 1]; + "\r" !== d && "\n" !== d || e.push(l); + d = this.textRect; + d.w = n; + d.h = k; if (a) { if (!b) { - g = m; - m = e.w; + f = n; + n = c.w; switch(a) { case 1: - c.x = m - (g + 4) >> 1; + d.x = n - (f + 4) >> 1; break; case 3: - c.x = m - (g + 4); + d.x = n - (f + 4); } - this._textBounds.setElements(c.x - 2, c.y - 2, c.w + 4, c.h + 4); + this._textBounds.setElements(d.x - 2, d.y - 2, d.w + 4, d.h + 4); } - e.h = k + 4; + c.h = k + 4; } else { - this._textBounds = e; + this._textBounds = c; } - for (t = 0;t < l.length;t++) { - if (e = l[t], e.width < g) { - switch(e.align) { + for (v = 0;v < e.length;v++) { + if (c = e[v], c.width < f) { + switch(c.align) { case 1: - e.x = g - e.width | 0; + c.x = f - c.width | 0; break; case 2: - e.x = (g - e.width) / 2 | 0; + c.x = (f - c.width) / 2 | 0; } } } - this.invalidatePaint(); + this.invalidate(); } }; - b.prototype.getBounds = function() { - return this._bounds; + d.roundBoundPoints = function(a) { + n(a === d.absoluteBoundPoints); + for (var b = 0;b < a.length;b++) { + var c = a[b]; + c.x = Math.floor(c.x + .1) + .5; + c.y = Math.floor(c.y + .1) + .5; + } }; - b.prototype.render = function(a) { + d.prototype.render = function(a) { a.save(); - var b = this._textBounds; - this._backgroundColor && (a.fillStyle = d.ColorUtilities.rgbaToCSSStyle(this._backgroundColor), a.fillRect(b.x, b.y, b.w, b.h)); - this._borderColor && (a.strokeStyle = d.ColorUtilities.rgbaToCSSStyle(this._borderColor), a.lineCap = "square", a.lineWidth = 1, a.strokeRect(b.x, b.y, b.w, b.h)); + var c = this._textBounds; + this._backgroundColor && (a.fillStyle = g.ColorUtilities.rgbaToCSSStyle(this._backgroundColor), a.fillRect(c.x, c.y, c.w, c.h)); + if (this._borderColor) { + a.strokeStyle = g.ColorUtilities.rgbaToCSSStyle(this._borderColor); + a.lineCap = "square"; + a.lineWidth = 1; + var f = d.absoluteBoundPoints, h = a.currentTransform; + h ? (c = c.clone(), (new b(h.a, h.b, h.c, h.d, h.e, h.f)).transformRectangle(c, f), a.setTransform(1, 0, 0, 1, 0, 0)) : (f[0].x = c.x, f[0].y = c.y, f[1].x = c.x + c.w, f[1].y = c.y, f[2].x = c.x + c.w, f[2].y = c.y + c.h, f[3].x = c.x, f[3].y = c.y + c.h); + d.roundBoundPoints(f); + c = new Path2D; + c.moveTo(f[0].x, f[0].y); + c.lineTo(f[1].x, f[1].y); + c.lineTo(f[2].x, f[2].y); + c.lineTo(f[3].x, f[3].y); + c.lineTo(f[0].x, f[0].y); + a.stroke(c); + h && a.setTransform(h.a, h.b, h.c, h.d, h.e, h.f); + } this._coords ? this._renderChars(a) : this._renderLines(a); a.restore(); }; - b.prototype._renderChars = function(a) { + d.prototype._renderChars = function(a) { if (this._matrix) { var b = this._matrix; a.transform(b.a, b.b, b.c, b.d, b.tx, b.ty); } - for (var b = this.lines, c = this._coords, d = c.position = 0;d < b.length;d++) { - for (var e = b[d].runs, g = 0;g < e.length;g++) { - var h = e[g]; - a.font = h.font; - a.fillStyle = h.fillStyle; - for (var h = h.text, l = 0;l < h.length;l++) { - var r = c.readInt() / 20, p = c.readInt() / 20; - a.fillText(h[l], r, p); + for (var b = this.lines, d = this._coords, c = d.position = 0;c < b.length;c++) { + for (var f = b[c].runs, h = 0;h < f.length;h++) { + var e = f[h]; + a.font = e.font; + a.fillStyle = e.fillStyle; + for (var e = e.text, g = 0;g < e.length;g++) { + var l = d.readInt() / 20, k = d.readInt() / 20; + a.fillText(e[g], l, k); } } } }; - b.prototype._renderLines = function(a) { + d.prototype._renderLines = function(a) { var b = this._textBounds; a.beginPath(); - a.rect(b.x, b.y, b.w, b.h); + a.rect(b.x + 2, b.y + 2, b.w - 4, b.h - 4); a.clip(); - a.translate(b.x + 2, b.y + 2); - for (var b = this.lines, c = 0;c < b.length;c++) { - for (var d = b[c], e = d.x, g = d.y, h = d.runs, l = 0;l < h.length;l++) { - var r = h[l]; - a.font = r.font; - a.fillStyle = r.fillStyle; - r.underline && a.fillRect(e, g + d.descent / 2 | 0, r.width, 1); - a.fillText(r.text, e, g); - e += r.width; + a.translate(b.x - this._scrollH + 2, b.y + 2); + for (var d = this.lines, c = this._scrollV, f = 0, h = 0;h < d.length;h++) { + var e = d[h], g = e.x, l = e.y; + if (h + 1 < c) { + f = l + e.descent + e.leading; + } else { + l -= f; + if (h + 1 - c && l > b.h) { + break; + } + for (var k = e.runs, n = 0;n < k.length;n++) { + var p = k[n]; + a.font = p.font; + a.fillStyle = p.fillStyle; + p.underline && a.fillRect(g, l + e.descent / 2 | 0, p.width, 1); + a.textAlign = "left"; + a.textBaseline = "alphabetic"; + a.fillText(p.text, g, l); + g += p.width; + } } } }; - return b; - }(g); + d.absoluteBoundPoints = [new w(0, 0), new w(0, 0), new w(0, 0), new w(0, 0)]; + return d; + }(y); m.RenderableText = v; - v = function(a) { - function b(c, d) { - a.call(this, new s(0, 0, c, d)); - this._flags = 5; + y = function(a) { + function b(d, c) { + a.call(this); + this._flags = 3145728; this.properties = {}; + this.setBounds(new k(0, 0, d, c)); } __extends(b, a); Object.defineProperty(b.prototype, "text", {get:function() { @@ -8877,7 +9714,7 @@ }, set:function(a) { this._text = a; }, enumerable:!0, configurable:!0}); - b.prototype.render = function(a, b) { + b.prototype.render = function(a, b, d) { a.save(); a.textBaseline = "top"; a.fillStyle = "white"; @@ -8885,309 +9722,248 @@ a.restore(); }; return b; - }(g); - m.Label = v; - g = function(a) { - function b() { - a.call(this, s.createMaxI16()); - this._flags = 14; - this.properties = {}; - } - __extends(b, a); - b.prototype.render = function(a, b) { - function c(b) { - for (var d = Math.ceil((e.x + e.w) / b) * b, f = Math.floor(e.x / b) * b;f < d;f += b) { - a.moveTo(f + .5, e.y), a.lineTo(f + .5, e.y + e.h); - } - d = Math.ceil((e.y + e.h) / b) * b; - for (f = Math.floor(e.y / b) * b;f < d;f += b) { - a.moveTo(e.x, f + .5), a.lineTo(e.x + e.w, f + .5); - } - } - a.save(); - var e = b || this.getBounds(); - a.fillStyle = d.ColorStyle.VeryDark; - a.fillRect(e.x, e.y, e.w, e.h); - a.beginPath(); - c(100); - a.lineWidth = 1; - a.strokeStyle = d.ColorStyle.Dark; - a.stroke(); - a.beginPath(); - c(500); - a.lineWidth = 1; - a.strokeStyle = d.ColorStyle.TabToolbar; - a.stroke(); - a.beginPath(); - c(1E3); - a.lineWidth = 3; - a.strokeStyle = d.ColorStyle.Toolbars; - a.stroke(); - a.lineWidth = 3; - a.beginPath(); - a.moveTo(-1048576, .5); - a.lineTo(1048576, .5); - a.moveTo(.5, -1048576); - a.lineTo(.5, 1048576); - a.strokeStyle = d.ColorStyle.Orange; - a.stroke(); - a.restore(); - }; - return b; - }(g); - m.Grid = g; - })(d.GFX || (d.GFX = {})); + }(y); + m.Label = y; + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - var s = d.ColorUtilities.clampByte, e = d.Debug.assert, t = function() { + var e = g.ColorUtilities.clampByte, c = g.Debug.assert, w = function() { return function() { }; }(); - m.Filter = t; - var k = function(a) { - function c(c, d, e) { - a.call(this); - this.blurX = c; - this.blurY = d; + m.Filter = w; + var k = function(b) { + function a(a, c, e) { + b.call(this); + this.blurX = a; + this.blurY = c; this.quality = e; } - __extends(c, a); - return c; - }(t); + __extends(a, b); + return a; + }(w); m.BlurFilter = k; - k = function(a) { - function c(c, d, e, k, l, r, b, h, m, n, f) { - a.call(this); - this.alpha = c; - this.angle = d; + k = function(b) { + function a(a, c, e, g, l, k, r, m, h, f, d) { + b.call(this); + this.alpha = a; + this.angle = c; this.blurX = e; - this.blurY = k; + this.blurY = g; this.color = l; - this.distance = r; - this.hideObject = b; - this.inner = h; - this.knockout = m; - this.quality = n; - this.strength = f; + this.distance = k; + this.hideObject = r; + this.inner = m; + this.knockout = h; + this.quality = f; + this.strength = d; } - __extends(c, a); - return c; - }(t); + __extends(a, b); + return a; + }(w); m.DropshadowFilter = k; - t = function(a) { - function c(c, d, e, k, l, r, b, h) { - a.call(this); - this.alpha = c; - this.blurX = d; + w = function(b) { + function a(a, c, e, g, l, k, r, m) { + b.call(this); + this.alpha = a; + this.blurX = c; this.blurY = e; - this.color = k; + this.color = g; this.inner = l; - this.knockout = r; - this.quality = b; - this.strength = h; + this.knockout = k; + this.quality = r; + this.strength = m; } - __extends(c, a); - return c; - }(t); - m.GlowFilter = t; - t = function() { - function a(a) { - e(20 === a.length); - this._m = new Float32Array(a); - } - a.prototype.clone = function() { - return new a(this._m); - }; - a.prototype.set = function(a) { - this._m.set(a._m); - }; - a.prototype.toWebGLMatrix = function() { - return new Float32Array(this._m); - }; - a.prototype.asWebGLMatrix = function() { - return this._m.subarray(0, 16); - }; - a.prototype.asWebGLVector = function() { - return this._m.subarray(16, 20); - }; - a.prototype.getColorMatrix = function() { - var a = new Float32Array(20), d = this._m; - a[0] = d[0]; - a[1] = d[4]; - a[2] = d[8]; - a[3] = d[12]; - a[4] = 255 * d[16]; - a[5] = d[1]; - a[6] = d[5]; - a[7] = d[9]; - a[8] = d[13]; - a[9] = 255 * d[17]; - a[10] = d[2]; - a[11] = d[6]; - a[12] = d[10]; - a[13] = d[14]; - a[14] = 255 * d[18]; - a[15] = d[3]; - a[16] = d[7]; - a[17] = d[11]; - a[18] = d[15]; - a[19] = 255 * d[19]; + __extends(a, b); + return a; + }(w); + m.GlowFilter = w; + (function(b) { + b[b.Unknown = 0] = "Unknown"; + b[b.Identity = 1] = "Identity"; + })(m.ColorMatrixType || (m.ColorMatrixType = {})); + w = function() { + function b(a) { + c(20 === a.length); + this._data = new Float32Array(a); + this._type = 0; + } + b.prototype.clone = function() { + var a = new b(this._data); + a._type = this._type; return a; }; - a.prototype.getColorTransform = function() { - var a = new Float32Array(8), d = this._m; - a[0] = d[0]; - a[1] = d[5]; - a[2] = d[10]; - a[3] = d[15]; - a[4] = 255 * d[16]; - a[5] = 255 * d[17]; - a[6] = 255 * d[18]; - a[7] = 255 * d[19]; - return a; + b.prototype.set = function(a) { + this._data.set(a._data); + this._type = a._type; }; - a.prototype.isIdentity = function() { - var a = this._m; + b.prototype.toWebGLMatrix = function() { + return new Float32Array(this._data); + }; + b.prototype.asWebGLMatrix = function() { + return this._data.subarray(0, 16); + }; + b.prototype.asWebGLVector = function() { + return this._data.subarray(16, 20); + }; + b.prototype.isIdentity = function() { + if (this._type & 1) { + return!0; + } + var a = this._data; return 1 == a[0] && 0 == a[1] && 0 == a[2] && 0 == a[3] && 0 == a[4] && 1 == a[5] && 0 == a[6] && 0 == a[7] && 0 == a[8] && 0 == a[9] && 1 == a[10] && 0 == a[11] && 0 == a[12] && 0 == a[13] && 0 == a[14] && 1 == a[15] && 0 == a[16] && 0 == a[17] && 0 == a[18] && 0 == a[19]; }; - a.createIdentity = function() { - return new a([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0]); + b.createIdentity = function() { + var a = new b([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0]); + a._type = 1; + return a; }; - a.prototype.setMultipliersAndOffsets = function(a, d, e, k, m, l, r, b) { - for (var h = this._m, s = 0;s < h.length;s++) { - h[s] = 0; - } - h[0] = a; - h[5] = d; - h[10] = e; - h[15] = k; - h[16] = m; - h[17] = l; - h[18] = r; - h[19] = b; - }; - a.prototype.transformRGBA = function(a) { - var d = a >> 24 & 255, e = a >> 16 & 255, k = a >> 8 & 255, m = a & 255, l = this._m; - a = s(d * l[0] + e * l[1] + k * l[2] + m * l[3] + l[16]); - var r = s(d * l[4] + e * l[5] + k * l[6] + m * l[7] + l[17]), b = s(d * l[8] + e * l[9] + k * l[10] + m * l[11] + l[18]), d = s(d * l[12] + e * l[13] + k * l[14] + m * l[15] + l[19]); - return a << 24 | r << 16 | b << 8 | d; - }; - a.prototype.multiply = function(a) { - var d = this._m, e = a._m; - a = d[0]; - var k = d[1], m = d[2], l = d[3], r = d[4], b = d[5], h = d[6], s = d[7], n = d[8], f = d[9], q = d[10], t = d[11], L = d[12], I = d[13], B = d[14], V = d[15], $ = d[16], C = d[17], ja = d[18], ga = d[19], E = e[0], x = e[1], z = e[2], G = e[3], F = e[4], y = e[5], A = e[6], D = e[7], J = e[8], H = e[9], K = e[10], M = e[11], N = e[12], O = e[13], P = e[14], Q = e[15], R = e[16], S = e[17], T = e[18], e = e[19]; - d[0] = a * E + r * x + n * z + L * G; - d[1] = k * E + b * x + f * z + I * G; - d[2] = m * E + h * x + q * z + B * G; - d[3] = l * E + s * x + t * z + V * G; - d[4] = a * F + r * y + n * A + L * D; - d[5] = k * F + b * y + f * A + I * D; - d[6] = m * F + h * y + q * A + B * D; - d[7] = l * F + s * y + t * A + V * D; - d[8] = a * J + r * H + n * K + L * M; - d[9] = k * J + b * H + f * K + I * M; - d[10] = m * J + h * H + q * K + B * M; - d[11] = l * J + s * H + t * K + V * M; - d[12] = a * N + r * O + n * P + L * Q; - d[13] = k * N + b * O + f * P + I * Q; - d[14] = m * N + h * O + q * P + B * Q; - d[15] = l * N + s * O + t * P + V * Q; - d[16] = a * R + r * S + n * T + L * e + $; - d[17] = k * R + b * S + f * T + I * e + C; - d[18] = m * R + h * S + q * T + B * e + ja; - d[19] = l * R + s * S + t * T + V * e + ga; + b.prototype.setMultipliersAndOffsets = function(a, b, c, e, g, l, k, r) { + for (var m = this._data, h = 0;h < m.length;h++) { + m[h] = 0; + } + m[0] = a; + m[5] = b; + m[10] = c; + m[15] = e; + m[16] = g / 255; + m[17] = l / 255; + m[18] = k / 255; + m[19] = r / 255; + this._type = 0; + }; + b.prototype.transformRGBA = function(a) { + var b = a >> 24 & 255, c = a >> 16 & 255, g = a >> 8 & 255, k = a & 255, l = this._data; + a = e(b * l[0] + c * l[1] + g * l[2] + k * l[3] + 255 * l[16]); + var t = e(b * l[4] + c * l[5] + g * l[6] + k * l[7] + 255 * l[17]), r = e(b * l[8] + c * l[9] + g * l[10] + k * l[11] + 255 * l[18]), b = e(b * l[12] + c * l[13] + g * l[14] + k * l[15] + 255 * l[19]); + return a << 24 | t << 16 | r << 8 | b; + }; + b.prototype.multiply = function(a) { + if (!(a._type & 1)) { + var b = this._data, c = a._data; + a = b[0]; + var e = b[1], g = b[2], l = b[3], k = b[4], r = b[5], m = b[6], h = b[7], f = b[8], d = b[9], q = b[10], x = b[11], s = b[12], O = b[13], w = b[14], V = b[15], fa = b[16], ga = b[17], ha = b[18], ia = b[19], W = c[0], X = c[1], P = c[2], U = c[3], B = c[4], A = c[5], C = c[6], D = c[7], z = c[8], E = c[9], G = c[10], F = c[11], H = c[12], I = c[13], J = c[14], K = c[15], L = c[16], M = c[17], N = c[18], c = c[19]; + b[0] = a * W + k * X + f * P + s * U; + b[1] = e * W + r * X + d * P + O * U; + b[2] = g * W + m * X + q * P + w * U; + b[3] = l * W + h * X + x * P + V * U; + b[4] = a * B + k * A + f * C + s * D; + b[5] = e * B + r * A + d * C + O * D; + b[6] = g * B + m * A + q * C + w * D; + b[7] = l * B + h * A + x * C + V * D; + b[8] = a * z + k * E + f * G + s * F; + b[9] = e * z + r * E + d * G + O * F; + b[10] = g * z + m * E + q * G + w * F; + b[11] = l * z + h * E + x * G + V * F; + b[12] = a * H + k * I + f * J + s * K; + b[13] = e * H + r * I + d * J + O * K; + b[14] = g * H + m * I + q * J + w * K; + b[15] = l * H + h * I + x * J + V * K; + b[16] = a * L + k * M + f * N + s * c + fa; + b[17] = e * L + r * M + d * N + O * c + ga; + b[18] = g * L + m * M + q * N + w * c + ha; + b[19] = l * L + h * M + x * N + V * c + ia; + this._type = 0; + } }; - Object.defineProperty(a.prototype, "alphaMultiplier", {get:function() { - return this._m[15]; + Object.defineProperty(b.prototype, "alphaMultiplier", {get:function() { + return this._data[15]; }, enumerable:!0, configurable:!0}); - a.prototype.hasOnlyAlphaMultiplier = function() { - var a = this._m; + b.prototype.hasOnlyAlphaMultiplier = function() { + var a = this._data; return 1 == a[0] && 0 == a[1] && 0 == a[2] && 0 == a[3] && 0 == a[4] && 1 == a[5] && 0 == a[6] && 0 == a[7] && 0 == a[8] && 0 == a[9] && 1 == a[10] && 0 == a[11] && 0 == a[12] && 0 == a[13] && 0 == a[14] && 0 == a[16] && 0 == a[17] && 0 == a[18] && 0 == a[19]; }; - a.prototype.equals = function(a) { + b.prototype.equals = function(a) { if (!a) { return!1; } - var d = this._m; - a = a._m; - for (var e = 0;20 > e;e++) { - if (.001 < Math.abs(d[e] - a[e])) { + if (this._type === a._type && 1 === this._type) { + return!0; + } + var b = this._data; + a = a._data; + for (var c = 0;20 > c;c++) { + if (.001 < Math.abs(b[c] - a[c])) { return!1; } } return!0; }; - return a; + b.prototype.toSVGFilterMatrix = function() { + var a = this._data; + return[a[0], a[4], a[8], a[12], a[16], a[1], a[5], a[9], a[13], a[17], a[2], a[6], a[10], a[14], a[18], a[3], a[7], a[11], a[15], a[19]].join(" "); + }; + return b; }(); - m.ColorMatrix = t; - })(d.GFX || (d.GFX = {})); + m.ColorMatrix = w; + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - (function(s) { - function e(a, c) { - return-1 !== a.indexOf(c, this.length - c.length); - } - var t = m.Geometry.Point3D, k = m.Geometry.Matrix3D, a = m.Geometry.degreesToRadian, c = d.Debug.assert, g = d.Debug.unexpected, p = d.Debug.notImplemented; - s.SHADER_ROOT = "shaders/"; - var v = function() { - function u(a, e) { - this._fillColor = d.Color.Red; - this._surfaceRegionCache = new d.LRUList; + (function(e) { + function c(a, b) { + return-1 !== a.indexOf(b, this.length - b.length); + } + var w = m.Geometry.Point3D, k = m.Geometry.Matrix3D, b = m.Geometry.degreesToRadian, a = g.Debug.assert, n = g.Debug.unexpected, p = g.Debug.notImplemented; + e.SHADER_ROOT = "shaders/"; + var y = function() { + function v(b, c) { + this._fillColor = g.Color.Red; + this._surfaceRegionCache = new g.LRUList; this.modelViewProjectionMatrix = k.createIdentity(); - this._canvas = a; - this._options = e; - this.gl = a.getContext("experimental-webgl", {preserveDrawingBuffer:!1, antialias:!0, stencil:!0, premultipliedAlpha:!1}); - c(this.gl, "Cannot create WebGL context."); + this._canvas = b; + this._options = c; + this.gl = b.getContext("experimental-webgl", {preserveDrawingBuffer:!1, antialias:!0, stencil:!0, premultipliedAlpha:!1}); + a(this.gl, "Cannot create WebGL context."); this._programCache = Object.create(null); this._resize(); - this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, e.unpackPremultiplyAlpha ? this.gl.ONE : this.gl.ZERO); - this._backgroundColor = d.Color.Black; - this._geometry = new s.WebGLGeometry(this); - this._tmpVertices = s.Vertex.createEmptyVertices(s.Vertex, 64); - this._maxSurfaces = e.maxSurfaces; - this._maxSurfaceSize = e.maxSurfaceSize; + this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, c.unpackPremultiplyAlpha ? this.gl.ONE : this.gl.ZERO); + this._backgroundColor = g.Color.Black; + this._geometry = new e.WebGLGeometry(this); + this._tmpVertices = e.Vertex.createEmptyVertices(e.Vertex, 64); + this._maxSurfaces = c.maxSurfaces; + this._maxSurfaceSize = c.maxSurfaceSize; this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA); this.gl.enable(this.gl.BLEND); this.modelViewProjectionMatrix = k.create2DProjection(this._w, this._h, 2E3); - var b = this; + var n = this; this._surfaceRegionAllocator = new m.SurfaceRegionAllocator.SimpleAllocator(function() { - var a = b._createTexture(); - return new s.WebGLSurface(1024, 1024, a); + var a = n._createTexture(); + return new e.WebGLSurface(1024, 1024, a); }); } - Object.defineProperty(u.prototype, "surfaces", {get:function() { + Object.defineProperty(v.prototype, "surfaces", {get:function() { return this._surfaceRegionAllocator.surfaces; }, enumerable:!0, configurable:!0}); - Object.defineProperty(u.prototype, "fillStyle", {set:function(a) { - this._fillColor.set(d.Color.parseColor(a)); + Object.defineProperty(v.prototype, "fillStyle", {set:function(a) { + this._fillColor.set(g.Color.parseColor(a)); }, enumerable:!0, configurable:!0}); - u.prototype.setBlendMode = function(a) { - var c = this.gl; + v.prototype.setBlendMode = function(a) { + var b = this.gl; switch(a) { case 8: - c.blendFunc(c.SRC_ALPHA, c.DST_ALPHA); + b.blendFunc(b.SRC_ALPHA, b.DST_ALPHA); break; case 3: - c.blendFunc(c.DST_COLOR, c.ONE_MINUS_SRC_ALPHA); + b.blendFunc(b.DST_COLOR, b.ONE_MINUS_SRC_ALPHA); break; case 4: - c.blendFunc(c.SRC_ALPHA, c.ONE); + b.blendFunc(b.SRC_ALPHA, b.ONE); break; case 2: ; case 1: - c.blendFunc(c.ONE, c.ONE_MINUS_SRC_ALPHA); + b.blendFunc(b.ONE, b.ONE_MINUS_SRC_ALPHA); break; default: p("Blend Mode: " + a); } }; - u.prototype.setBlendOptions = function() { + v.prototype.setBlendOptions = function() { this.gl.blendFunc(this._options.sourceBlendFactor, this._options.destinationBlendFactor); }; - u.glSupportedBlendMode = function(a) { + v.glSupportedBlendMode = function(a) { switch(a) { case 8: ; @@ -9201,151 +9977,151 @@ return!1; } }; - u.prototype.create2DProjectionMatrix = function() { + v.prototype.create2DProjectionMatrix = function() { return k.create2DProjection(this._w, this._h, -this._w); }; - u.prototype.createPerspectiveMatrix = function(c, d, b) { - b = a(b); - d = k.createPerspective(a(d)); - var e = new t(0, 1, 0), g = new t(0, 0, 0); - c = new t(0, 0, c); - c = k.createCameraLookAt(c, g, e); - c = k.createInverse(c); - e = k.createIdentity(); - e = k.createMultiply(e, k.createTranslation(-this._w / 2, -this._h / 2)); - e = k.createMultiply(e, k.createScale(1 / this._w, -1 / this._h, .01)); - e = k.createMultiply(e, k.createYRotation(b)); - e = k.createMultiply(e, c); - return e = k.createMultiply(e, d); + v.prototype.createPerspectiveMatrix = function(a, c, e) { + e = b(e); + c = k.createPerspective(b(c)); + var g = new w(0, 1, 0), h = new w(0, 0, 0); + a = new w(0, 0, a); + a = k.createCameraLookAt(a, h, g); + a = k.createInverse(a); + g = k.createIdentity(); + g = k.createMultiply(g, k.createTranslation(-this._w / 2, -this._h / 2)); + g = k.createMultiply(g, k.createScale(1 / this._w, -1 / this._h, .01)); + g = k.createMultiply(g, k.createYRotation(e)); + g = k.createMultiply(g, a); + return g = k.createMultiply(g, c); }; - u.prototype.discardCachedImages = function() { + v.prototype.discardCachedImages = function() { 2 <= m.traceLevel && m.writer && m.writer.writeLn("Discard Cache"); - for (var a = this._surfaceRegionCache.count / 2 | 0, c = 0;c < a;c++) { - var b = this._surfaceRegionCache.pop(); - 2 <= m.traceLevel && m.writer && m.writer.writeLn("Discard: " + b); - b.texture.atlas.remove(b.region); - b.texture = null; + for (var a = this._surfaceRegionCache.count / 2 | 0, b = 0;b < a;b++) { + var c = this._surfaceRegionCache.pop(); + 2 <= m.traceLevel && m.writer && m.writer.writeLn("Discard: " + c); + c.texture.atlas.remove(c.region); + c.texture = null; } }; - u.prototype.cacheImage = function(a) { - var c = this.allocateSurfaceRegion(a.width, a.height); - 2 <= m.traceLevel && m.writer && m.writer.writeLn("Uploading Image: @ " + c.region); - this._surfaceRegionCache.use(c); - this.updateSurfaceRegion(a, c); - return c; + v.prototype.cacheImage = function(a) { + var b = this.allocateSurfaceRegion(a.width, a.height); + 2 <= m.traceLevel && m.writer && m.writer.writeLn("Uploading Image: @ " + b.region); + this._surfaceRegionCache.use(b); + this.updateSurfaceRegion(a, b); + return b; }; - u.prototype.allocateSurfaceRegion = function(a, c) { - return this._surfaceRegionAllocator.allocate(a, c); + v.prototype.allocateSurfaceRegion = function(a, b) { + return this._surfaceRegionAllocator.allocate(a, b); }; - u.prototype.updateSurfaceRegion = function(a, c) { - var b = this.gl; - b.bindTexture(b.TEXTURE_2D, c.surface.texture); - b.texSubImage2D(b.TEXTURE_2D, 0, c.region.x, c.region.y, b.RGBA, b.UNSIGNED_BYTE, a); + v.prototype.updateSurfaceRegion = function(a, b) { + var c = this.gl; + c.bindTexture(c.TEXTURE_2D, b.surface.texture); + c.texSubImage2D(c.TEXTURE_2D, 0, b.region.x, b.region.y, c.RGBA, c.UNSIGNED_BYTE, a); }; - u.prototype._resize = function() { + v.prototype._resize = function() { var a = this.gl; this._w = this._canvas.width; this._h = this._canvas.height; a.viewport(0, 0, this._w, this._h); - for (var c in this._programCache) { - this._initializeProgram(this._programCache[c]); + for (var b in this._programCache) { + this._initializeProgram(this._programCache[b]); } }; - u.prototype._initializeProgram = function(a) { + v.prototype._initializeProgram = function(a) { this.gl.useProgram(a); }; - u.prototype._createShaderFromFile = function(a) { - var d = s.SHADER_ROOT + a, b = this.gl; - a = new XMLHttpRequest; - a.open("GET", d, !1); - a.send(); - c(200 === a.status || 0 === a.status, "File : " + d + " not found."); - if (e(d, ".vert")) { - d = b.VERTEX_SHADER; + v.prototype._createShaderFromFile = function(b) { + var g = e.SHADER_ROOT + b, k = this.gl; + b = new XMLHttpRequest; + b.open("GET", g, !1); + b.send(); + a(200 === b.status || 0 === b.status, "File : " + g + " not found."); + if (c(g, ".vert")) { + g = k.VERTEX_SHADER; } else { - if (e(d, ".frag")) { - d = b.FRAGMENT_SHADER; + if (c(g, ".frag")) { + g = k.FRAGMENT_SHADER; } else { throw "Shader Type: not supported."; } } - return this._createShader(d, a.responseText); + return this._createShader(g, b.responseText); }; - u.prototype.createProgramFromFiles = function() { + v.prototype.createProgramFromFiles = function() { var a = this._programCache["combined.vert-combined.frag"]; a || (a = this._createProgram([this._createShaderFromFile("combined.vert"), this._createShaderFromFile("combined.frag")]), this._queryProgramAttributesAndUniforms(a), this._initializeProgram(a), this._programCache["combined.vert-combined.frag"] = a); return a; }; - u.prototype._createProgram = function(a) { - var c = this.gl, b = c.createProgram(); + v.prototype._createProgram = function(a) { + var b = this.gl, c = b.createProgram(); a.forEach(function(a) { - c.attachShader(b, a); + b.attachShader(c, a); }); - c.linkProgram(b); - c.getProgramParameter(b, c.LINK_STATUS) || (g("Cannot link program: " + c.getProgramInfoLog(b)), c.deleteProgram(b)); - return b; + b.linkProgram(c); + b.getProgramParameter(c, b.LINK_STATUS) || (n("Cannot link program: " + b.getProgramInfoLog(c)), b.deleteProgram(c)); + return c; }; - u.prototype._createShader = function(a, c) { - var b = this.gl, d = b.createShader(a); - b.shaderSource(d, c); - b.compileShader(d); - return b.getShaderParameter(d, b.COMPILE_STATUS) ? d : (g("Cannot compile shader: " + b.getShaderInfoLog(d)), b.deleteShader(d), null); - }; - u.prototype._createTexture = function() { - var a = this.gl, c = a.createTexture(); - a.bindTexture(a.TEXTURE_2D, c); + v.prototype._createShader = function(a, b) { + var c = this.gl, e = c.createShader(a); + c.shaderSource(e, b); + c.compileShader(e); + return c.getShaderParameter(e, c.COMPILE_STATUS) ? e : (n("Cannot compile shader: " + c.getShaderInfoLog(e)), c.deleteShader(e), null); + }; + v.prototype._createTexture = function() { + var a = this.gl, b = a.createTexture(); + a.bindTexture(a.TEXTURE_2D, b); a.texParameteri(a.TEXTURE_2D, a.TEXTURE_WRAP_S, a.CLAMP_TO_EDGE); a.texParameteri(a.TEXTURE_2D, a.TEXTURE_WRAP_T, a.CLAMP_TO_EDGE); a.texParameteri(a.TEXTURE_2D, a.TEXTURE_MIN_FILTER, a.LINEAR); a.texParameteri(a.TEXTURE_2D, a.TEXTURE_MAG_FILTER, a.LINEAR); a.texImage2D(a.TEXTURE_2D, 0, a.RGBA, 1024, 1024, 0, a.RGBA, a.UNSIGNED_BYTE, null); - return c; - }; - u.prototype._createFramebuffer = function(a) { - var c = this.gl, b = c.createFramebuffer(); - c.bindFramebuffer(c.FRAMEBUFFER, b); - c.framebufferTexture2D(c.FRAMEBUFFER, c.COLOR_ATTACHMENT0, c.TEXTURE_2D, a, 0); - c.bindFramebuffer(c.FRAMEBUFFER, null); return b; }; - u.prototype._queryProgramAttributesAndUniforms = function(a) { + v.prototype._createFramebuffer = function(a) { + var b = this.gl, c = b.createFramebuffer(); + b.bindFramebuffer(b.FRAMEBUFFER, c); + b.framebufferTexture2D(b.FRAMEBUFFER, b.COLOR_ATTACHMENT0, b.TEXTURE_2D, a, 0); + b.bindFramebuffer(b.FRAMEBUFFER, null); + return c; + }; + v.prototype._queryProgramAttributesAndUniforms = function(a) { a.uniforms = {}; a.attributes = {}; - for (var c = this.gl, b = 0, d = c.getProgramParameter(a, c.ACTIVE_ATTRIBUTES);b < d;b++) { - var e = c.getActiveAttrib(a, b); - a.attributes[e.name] = e; - e.location = c.getAttribLocation(a, e.name); + for (var b = this.gl, c = 0, e = b.getProgramParameter(a, b.ACTIVE_ATTRIBUTES);c < e;c++) { + var h = b.getActiveAttrib(a, c); + a.attributes[h.name] = h; + h.location = b.getAttribLocation(a, h.name); } - b = 0; - for (d = c.getProgramParameter(a, c.ACTIVE_UNIFORMS);b < d;b++) { - e = c.getActiveUniform(a, b), a.uniforms[e.name] = e, e.location = c.getUniformLocation(a, e.name); + c = 0; + for (e = b.getProgramParameter(a, b.ACTIVE_UNIFORMS);c < e;c++) { + h = b.getActiveUniform(a, c), a.uniforms[h.name] = h, h.location = b.getUniformLocation(a, h.name); } }; - Object.defineProperty(u.prototype, "target", {set:function(a) { - var c = this.gl; - a ? (c.viewport(0, 0, a.w, a.h), c.bindFramebuffer(c.FRAMEBUFFER, a.framebuffer)) : (c.viewport(0, 0, this._w, this._h), c.bindFramebuffer(c.FRAMEBUFFER, null)); + Object.defineProperty(v.prototype, "target", {set:function(a) { + var b = this.gl; + a ? (b.viewport(0, 0, a.w, a.h), b.bindFramebuffer(b.FRAMEBUFFER, a.framebuffer)) : (b.viewport(0, 0, this._w, this._h), b.bindFramebuffer(b.FRAMEBUFFER, null)); }, enumerable:!0, configurable:!0}); - u.prototype.clear = function(a) { + v.prototype.clear = function(a) { a = this.gl; a.clearColor(0, 0, 0, 0); a.clear(a.COLOR_BUFFER_BIT); }; - u.prototype.clearTextureRegion = function(a, c) { - "undefined" === typeof c && (c = d.Color.None); - var b = this.gl, e = a.region; + v.prototype.clearTextureRegion = function(a, b) { + void 0 === b && (b = g.Color.None); + var c = this.gl, e = a.region; this.target = a.surface; - b.enable(b.SCISSOR_TEST); - b.scissor(e.x, e.y, e.w, e.h); - b.clearColor(c.r, c.g, c.b, c.a); - b.clear(b.COLOR_BUFFER_BIT | b.DEPTH_BUFFER_BIT); - b.disable(b.SCISSOR_TEST); + c.enable(c.SCISSOR_TEST); + c.scissor(e.x, e.y, e.w, e.h); + c.clearColor(b.r, b.g, b.b, b.a); + c.clear(c.COLOR_BUFFER_BIT | c.DEPTH_BUFFER_BIT); + c.disable(c.SCISSOR_TEST); }; - u.prototype.sizeOf = function(a) { - var c = this.gl; + v.prototype.sizeOf = function(a) { + var b = this.gl; switch(a) { - case c.UNSIGNED_BYTE: + case b.UNSIGNED_BYTE: return 1; - case c.UNSIGNED_SHORT: + case b.UNSIGNED_SHORT: return 2; case this.gl.INT: ; @@ -9355,235 +10131,235 @@ p(a); } }; - u.MAX_SURFACES = 8; - return u; + v.MAX_SURFACES = 8; + return v; }(); - s.WebGLContext = v; + e.WebGLContext = y; })(m.WebGL || (m.WebGL = {})); - })(d.GFX || (d.GFX = {})); + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); -__extends = this.__extends || function(d, m) { - function s() { - this.constructor = d; +__extends = this.__extends || function(g, m) { + function e() { + this.constructor = g; } - for (var e in m) { - m.hasOwnProperty(e) && (d[e] = m[e]); + for (var c in m) { + m.hasOwnProperty(c) && (g[c] = m[c]); } - s.prototype = m.prototype; - d.prototype = new s; + e.prototype = m.prototype; + g.prototype = new e; }; -(function(d) { +(function(g) { (function(m) { - (function(s) { - var e = d.Debug.assert, t = function(a) { - function c() { - a.apply(this, arguments); + (function(e) { + var c = g.Debug.assert, w = function(b) { + function a() { + b.apply(this, arguments); } - __extends(c, a); - c.prototype.ensureVertexCapacity = function(a) { - e(0 === (this._offset & 3)); + __extends(a, b); + a.prototype.ensureVertexCapacity = function(a) { + c(0 === (this._offset & 3)); this.ensureCapacity(this._offset + 8 * a); }; - c.prototype.writeVertex = function(a, c) { - e(0 === (this._offset & 3)); + a.prototype.writeVertex = function(a, b) { + c(0 === (this._offset & 3)); this.ensureCapacity(this._offset + 8); - this.writeVertexUnsafe(a, c); + this.writeVertexUnsafe(a, b); }; - c.prototype.writeVertexUnsafe = function(a, c) { - var d = this._offset >> 2; - this._f32[d] = a; - this._f32[d + 1] = c; + a.prototype.writeVertexUnsafe = function(a, b) { + var c = this._offset >> 2; + this._f32[c] = a; + this._f32[c + 1] = b; this._offset += 8; }; - c.prototype.writeVertex3D = function(a, c, d) { - e(0 === (this._offset & 3)); + a.prototype.writeVertex3D = function(a, b, e) { + c(0 === (this._offset & 3)); this.ensureCapacity(this._offset + 12); - this.writeVertex3DUnsafe(a, c, d); + this.writeVertex3DUnsafe(a, b, e); }; - c.prototype.writeVertex3DUnsafe = function(a, c, d) { + a.prototype.writeVertex3DUnsafe = function(a, b, c) { var e = this._offset >> 2; this._f32[e] = a; - this._f32[e + 1] = c; - this._f32[e + 2] = d; + this._f32[e + 1] = b; + this._f32[e + 2] = c; this._offset += 12; }; - c.prototype.writeTriangleElements = function(a, c, d) { - e(0 === (this._offset & 1)); + a.prototype.writeTriangleElements = function(a, b, e) { + c(0 === (this._offset & 1)); this.ensureCapacity(this._offset + 6); - var k = this._offset >> 1; - this._u16[k] = a; - this._u16[k + 1] = c; - this._u16[k + 2] = d; + var g = this._offset >> 1; + this._u16[g] = a; + this._u16[g + 1] = b; + this._u16[g + 2] = e; this._offset += 6; }; - c.prototype.ensureColorCapacity = function(a) { - e(0 === (this._offset & 2)); + a.prototype.ensureColorCapacity = function(a) { + c(0 === (this._offset & 2)); this.ensureCapacity(this._offset + 16 * a); }; - c.prototype.writeColorFloats = function(a, c, d, k) { - e(0 === (this._offset & 2)); + a.prototype.writeColorFloats = function(a, b, e, g) { + c(0 === (this._offset & 2)); this.ensureCapacity(this._offset + 16); - this.writeColorFloatsUnsafe(a, c, d, k); + this.writeColorFloatsUnsafe(a, b, e, g); }; - c.prototype.writeColorFloatsUnsafe = function(a, c, d, e) { - var l = this._offset >> 2; - this._f32[l] = a; - this._f32[l + 1] = c; - this._f32[l + 2] = d; - this._f32[l + 3] = e; + a.prototype.writeColorFloatsUnsafe = function(a, b, c, e) { + var g = this._offset >> 2; + this._f32[g] = a; + this._f32[g + 1] = b; + this._f32[g + 2] = c; + this._f32[g + 3] = e; this._offset += 16; }; - c.prototype.writeColor = function() { - var a = Math.random(), c = Math.random(), d = Math.random(), k = Math.random() / 2; - e(0 === (this._offset & 3)); + a.prototype.writeColor = function() { + var a = Math.random(), b = Math.random(), e = Math.random(), g = Math.random() / 2; + c(0 === (this._offset & 3)); this.ensureCapacity(this._offset + 4); - this._i32[this._offset >> 2] = k << 24 | d << 16 | c << 8 | a; + this._i32[this._offset >> 2] = g << 24 | e << 16 | b << 8 | a; this._offset += 4; }; - c.prototype.writeColorUnsafe = function(a, c, d, e) { - this._i32[this._offset >> 2] = e << 24 | d << 16 | c << 8 | a; + a.prototype.writeColorUnsafe = function(a, b, c, e) { + this._i32[this._offset >> 2] = e << 24 | c << 16 | b << 8 | a; this._offset += 4; }; - c.prototype.writeRandomColor = function() { + a.prototype.writeRandomColor = function() { this.writeColor(); }; - return c; - }(d.ArrayUtilities.ArrayWriter); - s.BufferWriter = t; - s.WebGLAttribute = function() { - return function(a, c, d, e) { - "undefined" === typeof e && (e = !1); - this.name = a; - this.size = c; - this.type = d; + return a; + }(g.ArrayUtilities.ArrayWriter); + e.BufferWriter = w; + e.WebGLAttribute = function() { + return function(b, a, c, e) { + void 0 === e && (e = !1); + this.name = b; + this.size = a; + this.type = c; this.normalized = e; }; }(); var k = function() { - function a(a) { + function b(a) { this.size = 0; this.attributes = a; } - a.prototype.initialize = function(a) { - for (var d = 0, e = 0;e < this.attributes.length;e++) { - this.attributes[e].offset = d, d += a.sizeOf(this.attributes[e].type) * this.attributes[e].size; + b.prototype.initialize = function(a) { + for (var b = 0, c = 0;c < this.attributes.length;c++) { + this.attributes[c].offset = b, b += a.sizeOf(this.attributes[c].type) * this.attributes[c].size; } - this.size = d; + this.size = b; }; - return a; + return b; }(); - s.WebGLAttributeList = k; + e.WebGLAttributeList = k; k = function() { - function a(a) { + function b(a) { this._elementOffset = this.triangleCount = 0; this.context = a; - this.array = new t(8); + this.array = new w(8); this.buffer = a.gl.createBuffer(); - this.elementArray = new t(8); + this.elementArray = new w(8); this.elementBuffer = a.gl.createBuffer(); } - Object.defineProperty(a.prototype, "elementOffset", {get:function() { + Object.defineProperty(b.prototype, "elementOffset", {get:function() { return this._elementOffset; }, enumerable:!0, configurable:!0}); - a.prototype.addQuad = function() { + b.prototype.addQuad = function() { var a = this._elementOffset; this.elementArray.writeTriangleElements(a, a + 1, a + 2); this.elementArray.writeTriangleElements(a, a + 2, a + 3); this.triangleCount += 2; this._elementOffset += 4; }; - a.prototype.resetElementOffset = function() { + b.prototype.resetElementOffset = function() { this._elementOffset = 0; }; - a.prototype.reset = function() { + b.prototype.reset = function() { this.array.reset(); this.elementArray.reset(); this.resetElementOffset(); this.triangleCount = 0; }; - a.prototype.uploadBuffers = function() { + b.prototype.uploadBuffers = function() { var a = this.context.gl; a.bindBuffer(a.ARRAY_BUFFER, this.buffer); a.bufferData(a.ARRAY_BUFFER, this.array.subU8View(), a.DYNAMIC_DRAW); a.bindBuffer(a.ELEMENT_ARRAY_BUFFER, this.elementBuffer); a.bufferData(a.ELEMENT_ARRAY_BUFFER, this.elementArray.subU8View(), a.DYNAMIC_DRAW); }; - return a; + return b; }(); - s.WebGLGeometry = k; - k = function(a) { - function c(c, d, e) { - a.call(this, c, d, e); - } - __extends(c, a); - c.createEmptyVertices = function(a, c) { - for (var d = [], e = 0;e < c;e++) { - d.push(new a(0, 0, 0)); + e.WebGLGeometry = k; + k = function(b) { + function a(a, c, e) { + b.call(this, a, c, e); + } + __extends(a, b); + a.createEmptyVertices = function(a, b) { + for (var c = [], e = 0;e < b;e++) { + c.push(new a(0, 0, 0)); } - return d; + return c; }; - return c; + return a; }(m.Geometry.Point3D); - s.Vertex = k; - (function(a) { - a[a.ZERO = 0] = "ZERO"; - a[a.ONE = 1] = "ONE"; - a[a.SRC_COLOR = 768] = "SRC_COLOR"; - a[a.ONE_MINUS_SRC_COLOR = 769] = "ONE_MINUS_SRC_COLOR"; - a[a.DST_COLOR = 774] = "DST_COLOR"; - a[a.ONE_MINUS_DST_COLOR = 775] = "ONE_MINUS_DST_COLOR"; - a[a.SRC_ALPHA = 770] = "SRC_ALPHA"; - a[a.ONE_MINUS_SRC_ALPHA = 771] = "ONE_MINUS_SRC_ALPHA"; - a[a.DST_ALPHA = 772] = "DST_ALPHA"; - a[a.ONE_MINUS_DST_ALPHA = 773] = "ONE_MINUS_DST_ALPHA"; - a[a.SRC_ALPHA_SATURATE = 776] = "SRC_ALPHA_SATURATE"; - a[a.CONSTANT_COLOR = 32769] = "CONSTANT_COLOR"; - a[a.ONE_MINUS_CONSTANT_COLOR = 32770] = "ONE_MINUS_CONSTANT_COLOR"; - a[a.CONSTANT_ALPHA = 32771] = "CONSTANT_ALPHA"; - a[a.ONE_MINUS_CONSTANT_ALPHA = 32772] = "ONE_MINUS_CONSTANT_ALPHA"; - })(s.WebGLBlendFactor || (s.WebGLBlendFactor = {})); + e.Vertex = k; + (function(b) { + b[b.ZERO = 0] = "ZERO"; + b[b.ONE = 1] = "ONE"; + b[b.SRC_COLOR = 768] = "SRC_COLOR"; + b[b.ONE_MINUS_SRC_COLOR = 769] = "ONE_MINUS_SRC_COLOR"; + b[b.DST_COLOR = 774] = "DST_COLOR"; + b[b.ONE_MINUS_DST_COLOR = 775] = "ONE_MINUS_DST_COLOR"; + b[b.SRC_ALPHA = 770] = "SRC_ALPHA"; + b[b.ONE_MINUS_SRC_ALPHA = 771] = "ONE_MINUS_SRC_ALPHA"; + b[b.DST_ALPHA = 772] = "DST_ALPHA"; + b[b.ONE_MINUS_DST_ALPHA = 773] = "ONE_MINUS_DST_ALPHA"; + b[b.SRC_ALPHA_SATURATE = 776] = "SRC_ALPHA_SATURATE"; + b[b.CONSTANT_COLOR = 32769] = "CONSTANT_COLOR"; + b[b.ONE_MINUS_CONSTANT_COLOR = 32770] = "ONE_MINUS_CONSTANT_COLOR"; + b[b.CONSTANT_ALPHA = 32771] = "CONSTANT_ALPHA"; + b[b.ONE_MINUS_CONSTANT_ALPHA = 32772] = "ONE_MINUS_CONSTANT_ALPHA"; + })(e.WebGLBlendFactor || (e.WebGLBlendFactor = {})); })(m.WebGL || (m.WebGL = {})); - })(d.GFX || (d.GFX = {})); + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); -(function(d) { - (function(d) { - (function(s) { - var e = function() { - function e(a, c, g) { - this.texture = g; - this.w = a; - this.h = c; - this._regionAllocator = new d.RegionAllocator.CompactAllocator(this.w, this.h); +(function(g) { + (function(g) { + (function(e) { + var c = function() { + function c(b, a, e) { + this.texture = e; + this.w = b; + this.h = a; + this._regionAllocator = new g.RegionAllocator.CompactAllocator(this.w, this.h); } - e.prototype.allocate = function(a, c) { - var d = this._regionAllocator.allocate(a, c); - return d ? new t(this, d) : null; + c.prototype.allocate = function(b, a) { + var c = this._regionAllocator.allocate(b, a); + return c ? new w(this, c) : null; }; - e.prototype.free = function(a) { - this._regionAllocator.free(a.region); + c.prototype.free = function(b) { + this._regionAllocator.free(b.region); }; - return e; + return c; }(); - s.WebGLSurface = e; - var t = function() { - return function(d, a) { - this.surface = d; - this.region = a; + e.WebGLSurface = c; + var w = function() { + return function(c, b) { + this.surface = c; + this.region = b; this.next = this.previous = null; }; }(); - s.WebGLSurfaceRegion = t; - })(d.WebGL || (d.WebGL = {})); - })(d.GFX || (d.GFX = {})); + e.WebGLSurfaceRegion = w; + })(g.WebGL || (g.WebGL = {})); + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - (function(s) { - var e = d.Color; - s.TILE_SIZE = 256; - s.MIN_UNTILED_SIZE = 256; - var t = m.Geometry.Matrix, k = m.Geometry.Rectangle, a = m.Geometry.RenderableTileCache, c = d.GFX.Shape, g = d.GFX.ColorMatrix, p = d.Debug.unexpected, v = function(a) { - function c() { + (function(e) { + var c = g.Color; + e.TILE_SIZE = 256; + e.MIN_UNTILED_SIZE = 256; + var w = m.Geometry.Matrix, k = m.Geometry.Rectangle, b = function(a) { + function b() { a.apply(this, arguments); this.maxSurfaces = 8; this.maxSurfaceSize = 4096; @@ -9598,20 +10374,20 @@ this.sourceBlendFactor = 1; this.destinationBlendFactor = 771; } - __extends(c, a); - return c; - }(m.StageRendererOptions); - s.WebGLStageRendererOptions = v; - var u = function(d) { - function r(a, c, e) { - "undefined" === typeof e && (e = new v); - d.call(this, a, c, e); - this._tmpVertices = s.Vertex.createEmptyVertices(s.Vertex, 64); + __extends(b, a); + return b; + }(m.RendererOptions); + e.WebGLRendererOptions = b; + var a = function(a) { + function g(c, k, l) { + void 0 === l && (l = new b); + a.call(this, c, k, l); + this._tmpVertices = e.Vertex.createEmptyVertices(e.Vertex, 64); this._cachedTiles = []; - a = this._context = new s.WebGLContext(this._canvas, e); + c = this._context = new e.WebGLContext(this._canvas, l); this._updateSize(); - this._brush = new s.WebGLCombinedBrush(a, new s.WebGLGeometry(a)); - this._stencilBrush = new s.WebGLCombinedBrush(a, new s.WebGLGeometry(a)); + this._brush = new e.WebGLCombinedBrush(c, new e.WebGLGeometry(c)); + this._stencilBrush = new e.WebGLCombinedBrush(c, new e.WebGLGeometry(c)); this._scratchCanvas = document.createElement("canvas"); this._scratchCanvas.width = this._scratchCanvas.height = 2048; this._scratchCanvasContext = this._scratchCanvas.getContext("2d", {willReadFrequently:!0}); @@ -9621,161 +10397,105 @@ this._uploadCanvas = document.createElement("canvas"); this._uploadCanvas.width = this._uploadCanvas.height = 0; this._uploadCanvasContext = this._uploadCanvas.getContext("2d", {willReadFrequently:!0}); - e.showTemporaryCanvases && (document.getElementById("temporaryCanvasPanelContainer").appendChild(this._uploadCanvas), document.getElementById("temporaryCanvasPanelContainer").appendChild(this._scratchCanvas)); + l.showTemporaryCanvases && (document.getElementById("temporaryCanvasPanelContainer").appendChild(this._uploadCanvas), document.getElementById("temporaryCanvasPanelContainer").appendChild(this._scratchCanvas)); this._clipStack = []; } - __extends(r, d); - r.prototype.resize = function() { + __extends(g, a); + g.prototype.resize = function() { this._updateSize(); this.render(); }; - r.prototype._updateSize = function() { + g.prototype._updateSize = function() { this._viewport = new k(0, 0, this._canvas.width, this._canvas.height); this._context._resize(); }; - r.prototype._cacheImageCallback = function(a, c, d) { - var e = d.w, f = d.h, g = d.x; - d = d.y; + g.prototype._cacheImageCallback = function(a, b, c) { + var e = c.w, g = c.h, k = c.x; + c = c.y; this._uploadCanvas.width = e + 2; - this._uploadCanvas.height = f + 2; - this._uploadCanvasContext.drawImage(c.canvas, g, d, e, f, 1, 1, e, f); - this._uploadCanvasContext.drawImage(c.canvas, g, d, e, 1, 1, 0, e, 1); - this._uploadCanvasContext.drawImage(c.canvas, g, d + f - 1, e, 1, 1, f + 1, e, 1); - this._uploadCanvasContext.drawImage(c.canvas, g, d, 1, f, 0, 1, 1, f); - this._uploadCanvasContext.drawImage(c.canvas, g + e - 1, d, 1, f, e + 1, 1, 1, f); + this._uploadCanvas.height = g + 2; + this._uploadCanvasContext.drawImage(b.canvas, k, c, e, g, 1, 1, e, g); + this._uploadCanvasContext.drawImage(b.canvas, k, c, e, 1, 1, 0, e, 1); + this._uploadCanvasContext.drawImage(b.canvas, k, c + g - 1, e, 1, 1, g + 1, e, 1); + this._uploadCanvasContext.drawImage(b.canvas, k, c, 1, g, 0, 1, 1, g); + this._uploadCanvasContext.drawImage(b.canvas, k + e - 1, c, 1, g, e + 1, 1, 1, g); return a && a.surface ? (this._options.disableSurfaceUploads || this._context.updateSurfaceRegion(this._uploadCanvas, a), a) : this._context.cacheImage(this._uploadCanvas); }; - r.prototype._enterClip = function(a, c, d, e) { - d.flush(); - var f = this._context.gl; - 0 === this._clipStack.length && (f.enable(f.STENCIL_TEST), f.clear(f.STENCIL_BUFFER_BIT), f.stencilFunc(f.ALWAYS, 1, 1)); + g.prototype._enterClip = function(a, b, c, e) { + c.flush(); + b = this._context.gl; + 0 === this._clipStack.length && (b.enable(b.STENCIL_TEST), b.clear(b.STENCIL_BUFFER_BIT), b.stencilFunc(b.ALWAYS, 1, 1)); this._clipStack.push(a); - f.colorMask(!1, !1, !1, !1); - f.stencilOp(f.KEEP, f.KEEP, f.INCR); - this._renderFrame(a, c, d, e, 0); - d.flush(); - f.colorMask(!0, !0, !0, !0); - f.stencilFunc(f.NOTEQUAL, 0, this._clipStack.length); - f.stencilOp(f.KEEP, f.KEEP, f.KEEP); - }; - r.prototype._leaveClip = function(a, c, d, e) { - d.flush(); - var f = this._context.gl; + b.colorMask(!1, !1, !1, !1); + b.stencilOp(b.KEEP, b.KEEP, b.INCR); + c.flush(); + b.colorMask(!0, !0, !0, !0); + b.stencilFunc(b.NOTEQUAL, 0, this._clipStack.length); + b.stencilOp(b.KEEP, b.KEEP, b.KEEP); + }; + g.prototype._leaveClip = function(a, b, c, e) { + c.flush(); + b = this._context.gl; if (a = this._clipStack.pop()) { - f.colorMask(!1, !1, !1, !1), f.stencilOp(f.KEEP, f.KEEP, f.DECR), this._renderFrame(a, c, d, e, 0), d.flush(), f.colorMask(!0, !0, !0, !0), f.stencilFunc(f.NOTEQUAL, 0, this._clipStack.length), f.stencilOp(f.KEEP, f.KEEP, f.KEEP); + b.colorMask(!1, !1, !1, !1), b.stencilOp(b.KEEP, b.KEEP, b.DECR), c.flush(), b.colorMask(!0, !0, !0, !0), b.stencilFunc(b.NOTEQUAL, 0, this._clipStack.length), b.stencilOp(b.KEEP, b.KEEP, b.KEEP); } - 0 === this._clipStack.length && f.disable(f.STENCIL_TEST); + 0 === this._clipStack.length && b.disable(b.STENCIL_TEST); }; - r.prototype._renderFrame = function(b, d, l, n, f) { - "undefined" === typeof f && (f = 0); - var q = this, r = this._options, u = this._context, v = this._cacheImageCallback.bind(this), B = t.createIdentity(), V = g.createIdentity(), $ = t.createIdentity(); - b.visit(function(d, g, h) { - f += r.frameSpacing; - var E = d.getBounds(); - if (h & 4096) { - q._enterClip(d, g, l, n); - } else { - if (h & 8192) { - q._leaveClip(d, g, l, n); - } else { - if (!n.intersectsTransformedAABB(E, g)) { - return 2; - } - h = d.getConcatenatedAlpha(b); - r.ignoreColorMatrix || (V = d.getConcatenatedColorMatrix()); - if (d instanceof m.FrameContainer) { - if (d instanceof m.ClipRectangle || r.paintBounds) { - d.color || (d.color = e.randomColor(.3)), l.fillRectangle(E, d.color, g, f); - } - } else { - if (d instanceof c) { - if (1 !== d.blendMode) { - return 2; - } - E = d.source.getBounds(); - if (!E.isEmpty()) { - var x = d.source, z = x.properties.tileCache; - z || (z = x.properties.tileCache = new a(x, s.TILE_SIZE, s.MIN_UNTILED_SIZE)); - x = t.createIdentity().translate(E.x, E.y); - x.concat(g); - x.inverse($); - z = z.fetchTiles(n, $, q._scratchCanvasContext, v); - for (x = 0;x < z.length;x++) { - var G = z[x]; - B.setIdentity(); - B.translate(G.bounds.x, G.bounds.y); - B.scale(1 / G.scale, 1 / G.scale); - B.translate(E.x, E.y); - B.concat(g); - var F = G.cachedSurfaceRegion; - F && F.surface && u._surfaceRegionCache.use(F); - var y = new e(1, 1, 1, h); - r.paintFlashing && (y = e.randomColor(1)); - l.drawImage(F, new k(0, 0, G.bounds.w, G.bounds.h), y, V, B, f, d.blendMode) || p(); - r.drawTiles && (F = G.bounds.clone(), G.color || (G.color = e.randomColor(.4)), l.fillRectangle(new k(0, 0, F.w, F.h), G.color, B, f)); - } - } - } - } - return 0; - } - } - }, d, 0, 16); + g.prototype._renderFrame = function(a, b, c, e) { }; - r.prototype._renderSurfaces = function(a) { - var c = this._options, d = this._context, g = this._viewport; - if (c.drawSurfaces) { - var f = d.surfaces, d = t.createIdentity(); - if (0 <= c.drawSurface && c.drawSurface < f.length) { - for (var c = f[c.drawSurface | 0], f = new k(0, 0, c.w, c.h), l = f.clone();l.w > g.w;) { - l.scale(.5, .5); + g.prototype._renderSurfaces = function(a) { + var b = this._options, g = this._context, n = this._viewport; + if (b.drawSurfaces) { + var p = g.surfaces, g = w.createIdentity(); + if (0 <= b.drawSurface && b.drawSurface < p.length) { + for (var b = p[b.drawSurface | 0], p = new k(0, 0, b.w, b.h), m = p.clone();m.w > n.w;) { + m.scale(.5, .5); } - a.drawImage(new s.WebGLSurfaceRegion(c, f), l, e.White, null, d, .2); + a.drawImage(new e.WebGLSurfaceRegion(b, p), m, c.White, null, g, .2); } else { - l = g.w / 5; - l > g.h / f.length && (l = g.h / f.length); - a.fillRectangle(new k(g.w - l, 0, l, g.h), new e(0, 0, 0, .5), d, .1); - for (var r = 0;r < f.length;r++) { - var c = f[r], p = new k(g.w - l, r * l, l, l); - a.drawImage(new s.WebGLSurfaceRegion(c, new k(0, 0, c.w, c.h)), p, e.White, null, d, .2); + m = n.w / 5; + m > n.h / p.length && (m = n.h / p.length); + a.fillRectangle(new k(n.w - m, 0, m, n.h), new c(0, 0, 0, .5), g, .1); + for (var h = 0;h < p.length;h++) { + var b = p[h], f = new k(n.w - m, h * m, m, m); + a.drawImage(new e.WebGLSurfaceRegion(b, new k(0, 0, b.w, b.h)), f, c.White, null, g, .2); } } a.flush(); } }; - r.prototype.render = function() { - var a = this._stage, c = this._options, d = this._context.gl; - this._context.modelViewProjectionMatrix = c.perspectiveCamera ? this._context.createPerspectiveMatrix(c.perspectiveCameraDistance + (c.animateZoom ? .8 * Math.sin(Date.now() / 3E3) : 0), c.perspectiveCameraFOV, c.perspectiveCameraAngle) : this._context.create2DProjectionMatrix(); - var g = this._brush; - d.clearColor(0, 0, 0, 0); - d.clear(d.COLOR_BUFFER_BIT | d.DEPTH_BUFFER_BIT); - g.reset(); - d = this._viewport; - this._renderFrame(a, a.matrix, g, d, 0); - g.flush(); - c.paintViewport && (g.fillRectangle(d, new e(.5, 0, 0, .25), t.createIdentity(), 0), g.flush()); - this._renderSurfaces(g); - }; - return r; - }(m.StageRenderer); - s.WebGLStageRenderer = u; + g.prototype.render = function() { + var a = this._options, b = this._context.gl; + this._context.modelViewProjectionMatrix = a.perspectiveCamera ? this._context.createPerspectiveMatrix(a.perspectiveCameraDistance + (a.animateZoom ? .8 * Math.sin(Date.now() / 3E3) : 0), a.perspectiveCameraFOV, a.perspectiveCameraAngle) : this._context.create2DProjectionMatrix(); + var e = this._brush; + b.clearColor(0, 0, 0, 0); + b.clear(b.COLOR_BUFFER_BIT | b.DEPTH_BUFFER_BIT); + e.reset(); + b = this._viewport; + e.flush(); + a.paintViewport && (e.fillRectangle(b, new c(.5, 0, 0, .25), w.createIdentity(), 0), e.flush()); + this._renderSurfaces(e); + }; + return g; + }(m.Renderer); + e.WebGLRenderer = a; })(m.WebGL || (m.WebGL = {})); - })(d.GFX || (d.GFX = {})); + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - (function(s) { - var e = d.Color, t = m.Geometry.Point, k = m.Geometry.Matrix3D, a = function() { - function a(c, d, e) { + (function(e) { + var c = g.Color, w = m.Geometry.Point, k = m.Geometry.Matrix3D, b = function() { + function a(b, c, e) { this._target = e; - this._context = c; - this._geometry = d; + this._context = b; + this._geometry = c; } a.prototype.reset = function() { - d.Debug.abstractMethod("reset"); + g.Debug.abstractMethod("reset"); }; a.prototype.flush = function() { - d.Debug.abstractMethod("flush"); + g.Debug.abstractMethod("flush"); }; Object.defineProperty(a.prototype, "target", {get:function() { return this._target; @@ -9785,26 +10505,26 @@ }, enumerable:!0, configurable:!0}); return a; }(); - s.WebGLBrush = a; + e.WebGLBrush = b; (function(a) { a[a.FillColor = 0] = "FillColor"; a[a.FillTexture = 1] = "FillTexture"; a[a.FillTextureWithColorMatrix = 2] = "FillTextureWithColorMatrix"; - })(s.WebGLCombinedBrushKind || (s.WebGLCombinedBrushKind = {})); - var c = function(a) { - function c(d, k, l) { - a.call(this, d, k, l); + })(e.WebGLCombinedBrushKind || (e.WebGLCombinedBrushKind = {})); + var a = function(a) { + function b(e, g, k) { + a.call(this, e, g, k); this.kind = 0; - this.color = new e(0, 0, 0, 0); + this.color = new c(0, 0, 0, 0); this.sampler = 0; - this.coordinate = new t(0, 0); + this.coordinate = new w(0, 0); } - __extends(c, a); - c.initializeAttributeList = function(a) { - var d = a.gl; - c.attributeList || (c.attributeList = new s.WebGLAttributeList([new s.WebGLAttribute("aPosition", 3, d.FLOAT), new s.WebGLAttribute("aCoordinate", 2, d.FLOAT), new s.WebGLAttribute("aColor", 4, d.UNSIGNED_BYTE, !0), new s.WebGLAttribute("aKind", 1, d.FLOAT), new s.WebGLAttribute("aSampler", 1, d.FLOAT)]), c.attributeList.initialize(a)); + __extends(b, a); + b.initializeAttributeList = function(a) { + var c = a.gl; + b.attributeList || (b.attributeList = new e.WebGLAttributeList([new e.WebGLAttribute("aPosition", 3, c.FLOAT), new e.WebGLAttribute("aCoordinate", 2, c.FLOAT), new e.WebGLAttribute("aColor", 4, c.UNSIGNED_BYTE, !0), new e.WebGLAttribute("aKind", 1, c.FLOAT), new e.WebGLAttribute("aSampler", 1, c.FLOAT)]), b.attributeList.initialize(a)); }; - c.prototype.writeTo = function(a) { + b.prototype.writeTo = function(a) { a = a.array; a.ensureAdditionalCapacity(); a.writeVertex3DUnsafe(this.x, this.y, this.z); @@ -9813,134 +10533,134 @@ a.writeFloatUnsafe(this.kind); a.writeFloatUnsafe(this.sampler); }; - return c; - }(s.Vertex); - s.WebGLCombinedBrushVertex = c; - a = function(a) { - function d(e, k, l) { - "undefined" === typeof l && (l = null); - a.call(this, e, k, l); + return b; + }(e.Vertex); + e.WebGLCombinedBrushVertex = a; + b = function(b) { + function c(e, g, k) { + void 0 === k && (k = null); + b.call(this, e, g, k); this._blendMode = 1; this._program = e.createProgramFromFiles(); this._surfaces = []; - c.initializeAttributeList(this._context); + a.initializeAttributeList(this._context); } - __extends(d, a); - d.prototype.reset = function() { + __extends(c, b); + c.prototype.reset = function() { this._surfaces = []; this._geometry.reset(); }; - d.prototype.drawImage = function(a, c, e, g, b, h, k) { - "undefined" === typeof h && (h = 0); - "undefined" === typeof k && (k = 1); + c.prototype.drawImage = function(a, b, e, g, k, n, h) { + void 0 === n && (n = 0); + void 0 === h && (h = 1); if (!a || !a.surface) { return!0; } - c = c.clone(); + b = b.clone(); this._colorMatrix && (g && this._colorMatrix.equals(g) || this.flush()); this._colorMatrix = g; - this._blendMode !== k && (this.flush(), this._blendMode = k); - k = this._surfaces.indexOf(a.surface); - 0 > k && (8 === this._surfaces.length && this.flush(), this._surfaces.push(a.surface), k = this._surfaces.length - 1); - var n = d._tmpVertices, f = a.region.clone(); - f.offset(1, 1).resize(-2, -2); - f.scale(1 / a.surface.w, 1 / a.surface.h); - b.transformRectangle(c, n); + this._blendMode !== h && (this.flush(), this._blendMode = h); + h = this._surfaces.indexOf(a.surface); + 0 > h && (8 === this._surfaces.length && this.flush(), this._surfaces.push(a.surface), h = this._surfaces.length - 1); + var f = c._tmpVertices, d = a.region.clone(); + d.offset(1, 1).resize(-2, -2); + d.scale(1 / a.surface.w, 1 / a.surface.h); + k.transformRectangle(b, f); for (a = 0;4 > a;a++) { - n[a].z = h; + f[a].z = n; } - n[0].coordinate.x = f.x; - n[0].coordinate.y = f.y; - n[1].coordinate.x = f.x + f.w; - n[1].coordinate.y = f.y; - n[2].coordinate.x = f.x + f.w; - n[2].coordinate.y = f.y + f.h; - n[3].coordinate.x = f.x; - n[3].coordinate.y = f.y + f.h; + f[0].coordinate.x = d.x; + f[0].coordinate.y = d.y; + f[1].coordinate.x = d.x + d.w; + f[1].coordinate.y = d.y; + f[2].coordinate.x = d.x + d.w; + f[2].coordinate.y = d.y + d.h; + f[3].coordinate.x = d.x; + f[3].coordinate.y = d.y + d.h; for (a = 0;4 > a;a++) { - h = d._tmpVertices[a], h.kind = g ? 2 : 1, h.color.set(e), h.sampler = k, h.writeTo(this._geometry); + n = c._tmpVertices[a], n.kind = g ? 2 : 1, n.color.set(e), n.sampler = h, n.writeTo(this._geometry); } this._geometry.addQuad(); return!0; }; - d.prototype.fillRectangle = function(a, c, e, g) { - "undefined" === typeof g && (g = 0); - e.transformRectangle(a, d._tmpVertices); + c.prototype.fillRectangle = function(a, b, e, g) { + void 0 === g && (g = 0); + e.transformRectangle(a, c._tmpVertices); for (a = 0;4 > a;a++) { - e = d._tmpVertices[a], e.kind = 0, e.color.set(c), e.z = g, e.writeTo(this._geometry); + e = c._tmpVertices[a], e.kind = 0, e.color.set(b), e.z = g, e.writeTo(this._geometry); } this._geometry.addQuad(); }; - d.prototype.flush = function() { - var a = this._geometry, d = this._program, e = this._context.gl, g; - a.uploadBuffers(); - e.useProgram(d); + c.prototype.flush = function() { + var b = this._geometry, c = this._program, e = this._context.gl, g; + b.uploadBuffers(); + e.useProgram(c); this._target ? (g = k.create2DProjection(this._target.w, this._target.h, 2E3), g = k.createMultiply(g, k.createScale(1, -1, 1))) : g = this._context.modelViewProjectionMatrix; - e.uniformMatrix4fv(d.uniforms.uTransformMatrix3D.location, !1, g.asWebGLMatrix()); - this._colorMatrix && (e.uniformMatrix4fv(d.uniforms.uColorMatrix.location, !1, this._colorMatrix.asWebGLMatrix()), e.uniform4fv(d.uniforms.uColorVector.location, this._colorMatrix.asWebGLVector())); + e.uniformMatrix4fv(c.uniforms.uTransformMatrix3D.location, !1, g.asWebGLMatrix()); + this._colorMatrix && (e.uniformMatrix4fv(c.uniforms.uColorMatrix.location, !1, this._colorMatrix.asWebGLMatrix()), e.uniform4fv(c.uniforms.uColorVector.location, this._colorMatrix.asWebGLVector())); for (g = 0;g < this._surfaces.length;g++) { e.activeTexture(e.TEXTURE0 + g), e.bindTexture(e.TEXTURE_2D, this._surfaces[g].texture); } - e.uniform1iv(d.uniforms["uSampler[0]"].location, [0, 1, 2, 3, 4, 5, 6, 7]); - e.bindBuffer(e.ARRAY_BUFFER, a.buffer); - var b = c.attributeList.size, h = c.attributeList.attributes; - for (g = 0;g < h.length;g++) { - var p = h[g], n = d.attributes[p.name].location; - e.enableVertexAttribArray(n); - e.vertexAttribPointer(n, p.size, p.type, p.normalized, b, p.offset); + e.uniform1iv(c.uniforms["uSampler[0]"].location, [0, 1, 2, 3, 4, 5, 6, 7]); + e.bindBuffer(e.ARRAY_BUFFER, b.buffer); + var n = a.attributeList.size, p = a.attributeList.attributes; + for (g = 0;g < p.length;g++) { + var h = p[g], f = c.attributes[h.name].location; + e.enableVertexAttribArray(f); + e.vertexAttribPointer(f, h.size, h.type, h.normalized, n, h.offset); } this._context.setBlendOptions(); this._context.target = this._target; - e.bindBuffer(e.ELEMENT_ARRAY_BUFFER, a.elementBuffer); - e.drawElements(e.TRIANGLES, 3 * a.triangleCount, e.UNSIGNED_SHORT, 0); + e.bindBuffer(e.ELEMENT_ARRAY_BUFFER, b.elementBuffer); + e.drawElements(e.TRIANGLES, 3 * b.triangleCount, e.UNSIGNED_SHORT, 0); this.reset(); }; - d._tmpVertices = s.Vertex.createEmptyVertices(c, 4); - d._depth = 1; - return d; - }(a); - s.WebGLCombinedBrush = a; + c._tmpVertices = e.Vertex.createEmptyVertices(a, 4); + c._depth = 1; + return c; + }(b); + e.WebGLCombinedBrush = b; })(m.WebGL || (m.WebGL = {})); - })(d.GFX || (d.GFX = {})); + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - (function(s) { - function e() { + (function(e) { + function c() { void 0 === this.stackDepth && (this.stackDepth = 0); void 0 === this.clipStack ? this.clipStack = [0] : this.clipStack.push(0); this.stackDepth++; - v.call(this); + y.call(this); } - function t() { + function w() { this.stackDepth--; this.clipStack.pop(); - b.call(this); + r.call(this); } function k() { p(!this.buildingClippingRegionDepth); l.apply(this, arguments); } - function a() { + function b() { p(m.debugClipping.value || !this.buildingClippingRegionDepth); - r.apply(this, arguments); + t.apply(this, arguments); } - function c() { - h.call(this); + function a() { + u.call(this); } - function g() { + function n() { void 0 === this.clipStack && (this.clipStack = [0]); this.clipStack[this.clipStack.length - 1]++; - m.debugClipping.value ? (this.strokeStyle = d.ColorStyle.Pink, this.stroke.apply(this, arguments)) : u.apply(this, arguments); + m.debugClipping.value ? (this.strokeStyle = g.ColorStyle.Pink, this.stroke.apply(this, arguments)) : v.apply(this, arguments); } - var p = d.Debug.assert, v = CanvasRenderingContext2D.prototype.save, u = CanvasRenderingContext2D.prototype.clip, l = CanvasRenderingContext2D.prototype.fill, r = CanvasRenderingContext2D.prototype.stroke, b = CanvasRenderingContext2D.prototype.restore, h = CanvasRenderingContext2D.prototype.beginPath; - s.notifyReleaseChanged = function() { - CanvasRenderingContext2D.prototype.save = e; - CanvasRenderingContext2D.prototype.clip = g; + var p = g.Debug.assert, y = CanvasRenderingContext2D.prototype.save, v = CanvasRenderingContext2D.prototype.clip, l = CanvasRenderingContext2D.prototype.fill, t = CanvasRenderingContext2D.prototype.stroke, r = CanvasRenderingContext2D.prototype.restore, u = CanvasRenderingContext2D.prototype.beginPath; + e.notifyReleaseChanged = function() { + CanvasRenderingContext2D.prototype.save = c; + CanvasRenderingContext2D.prototype.clip = n; CanvasRenderingContext2D.prototype.fill = k; - CanvasRenderingContext2D.prototype.stroke = a; - CanvasRenderingContext2D.prototype.restore = t; - CanvasRenderingContext2D.prototype.beginPath = c; + CanvasRenderingContext2D.prototype.stroke = b; + CanvasRenderingContext2D.prototype.restore = w; + CanvasRenderingContext2D.prototype.beginPath = a; }; CanvasRenderingContext2D.prototype.enterBuildingClippingRegion = function() { this.buildingClippingRegionDepth || (this.buildingClippingRegionDepth = 0); @@ -9950,273 +10670,737 @@ this.buildingClippingRegionDepth--; }; })(m.Canvas2D || (m.Canvas2D = {})); - })(d.GFX || (d.GFX = {})); + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); -(function(d) { - (function(d) { - (function(d) { - var e = function() { - return function(d, a) { - this.surface = d; - this.region = a; +(function(g) { + (function(m) { + (function(e) { + function c(a) { + var b = "source-over"; + switch(a) { + case 1: + ; + case 2: + break; + case 3: + b = "multiply"; + break; + case 8: + ; + case 4: + b = "screen"; + break; + case 5: + b = "lighten"; + break; + case 6: + b = "darken"; + break; + case 7: + b = "difference"; + break; + case 13: + b = "overlay"; + break; + case 14: + b = "hard-light"; + break; + case 11: + b = "destination-in"; + break; + case 12: + b = "destination-out"; + break; + default: + g.Debug.somewhatImplemented("Blend Mode: " + m.BlendMode[a]); + } + return b; + } + var w = g.NumberUtilities.clamp; + navigator.userAgent.indexOf("Firefox"); + var k = function() { + function a() { + } + a._prepareSVGFilters = function() { + if (!a._svgBlurFilter) { + var b = document.createElementNS("http://www.w3.org/2000/svg", "svg"), c = document.createElementNS("http://www.w3.org/2000/svg", "defs"), e = document.createElementNS("http://www.w3.org/2000/svg", "filter"); + e.setAttribute("id", "svgBlurFilter"); + var g = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur"); + g.setAttribute("stdDeviation", "0 0"); + e.appendChild(g); + c.appendChild(e); + a._svgBlurFilter = g; + e = document.createElementNS("http://www.w3.org/2000/svg", "filter"); + e.setAttribute("id", "svgDropShadowFilter"); + g = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur"); + g.setAttribute("in", "SourceAlpha"); + g.setAttribute("stdDeviation", "3"); + e.appendChild(g); + a._svgDropshadowFilterBlur = g; + g = document.createElementNS("http://www.w3.org/2000/svg", "feOffset"); + g.setAttribute("dx", "0"); + g.setAttribute("dy", "0"); + g.setAttribute("result", "offsetblur"); + e.appendChild(g); + a._svgDropshadowFilterOffset = g; + g = document.createElementNS("http://www.w3.org/2000/svg", "feFlood"); + g.setAttribute("flood-color", "rgba(0,0,0,1)"); + e.appendChild(g); + a._svgDropshadowFilterFlood = g; + g = document.createElementNS("http://www.w3.org/2000/svg", "feComposite"); + g.setAttribute("in2", "offsetblur"); + g.setAttribute("operator", "in"); + e.appendChild(g); + var g = document.createElementNS("http://www.w3.org/2000/svg", "feMerge"), k = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode"); + g.appendChild(k); + k = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode"); + k.setAttribute("in", "SourceGraphic"); + g.appendChild(k); + e.appendChild(g); + c.appendChild(e); + e = document.createElementNS("http://www.w3.org/2000/svg", "filter"); + e.setAttribute("id", "svgColorMatrixFilter"); + g = document.createElementNS("http://www.w3.org/2000/svg", "feColorMatrix"); + g.setAttribute("color-interpolation-filters", "sRGB"); + g.setAttribute("in", "SourceGraphic"); + g.setAttribute("type", "matrix"); + e.appendChild(g); + c.appendChild(e); + a._svgColorMatrixFilter = g; + b.appendChild(c); + document.documentElement.appendChild(b); + } + }; + a._applyColorMatrixFilter = function(b, c) { + a._prepareSVGFilters(); + a._svgColorMatrixFilter.setAttribute("values", c.toSVGFilterMatrix()); + b.filter = "url(#svgColorMatrixFilter)"; + }; + a._applyFilters = function(b, c, e) { + function k(a) { + var c = b / 2; + switch(a) { + case 0: + return 0; + case 1: + return c / 2.7; + case 2: + return c / 1.28; + default: + return c; + } + } + a._prepareSVGFilters(); + a._removeFilters(c); + for (var l = 0;l < e.length;l++) { + var t = e[l]; + if (t instanceof m.BlurFilter) { + var r = t, t = k(r.quality); + a._svgBlurFilter.setAttribute("stdDeviation", r.blurX * t + " " + r.blurY * t); + c.filter = "url(#svgBlurFilter)"; + } else { + t instanceof m.DropshadowFilter && (r = t, t = k(r.quality), a._svgDropshadowFilterBlur.setAttribute("stdDeviation", r.blurX * t + " " + r.blurY * t), a._svgDropshadowFilterOffset.setAttribute("dx", String(Math.cos(r.angle * Math.PI / 180) * r.distance * b)), a._svgDropshadowFilterOffset.setAttribute("dy", String(Math.sin(r.angle * Math.PI / 180) * r.distance * b)), a._svgDropshadowFilterFlood.setAttribute("flood-color", g.ColorUtilities.rgbaToCSSStyle(r.color << 8 | Math.round(255 * + r.alpha))), c.filter = "url(#svgDropShadowFilter)"); + } + } + }; + a._removeFilters = function(a) { + a.filter = "none"; + }; + a._applyColorMatrix = function(b, c) { + a._removeFilters(b); + c.isIdentity() ? (b.globalAlpha = 1, b.globalColorMatrix = null) : c.hasOnlyAlphaMultiplier() ? (b.globalAlpha = w(c.alphaMultiplier, 0, 1), b.globalColorMatrix = null) : (b.globalAlpha = 1, a._svgFiltersAreSupported ? (a._applyColorMatrixFilter(b, c), b.globalColorMatrix = null) : b.globalColorMatrix = c); + }; + a._svgFiltersAreSupported = !!Object.getOwnPropertyDescriptor(CanvasRenderingContext2D.prototype, "filter"); + return a; + }(); + e.Filters = k; + var b = function() { + function a(a, b, c, e) { + this.surface = a; + this.region = b; + this.w = c; + this.h = e; + } + a.prototype.free = function() { + this.surface.free(this); + }; + a._ensureCopyCanvasSize = function(b, c) { + var e; + if (a._copyCanvasContext) { + if (e = a._copyCanvasContext.canvas, e.width < b || e.height < c) { + e.width = g.IntegerUtilities.nearestPowerOfTwo(b), e.height = g.IntegerUtilities.nearestPowerOfTwo(c); + } + } else { + e = document.createElement("canvas"), "undefined" !== typeof registerScratchCanvas && registerScratchCanvas(e), e.width = 512, e.height = 512, a._copyCanvasContext = e.getContext("2d"); + } + }; + a.prototype.draw = function(b, e, g, k, l, m) { + this.context.setTransform(1, 0, 0, 1, 0, 0); + var r, u = 0, h = 0; + b.context.canvas === this.context.canvas ? (a._ensureCopyCanvasSize(k, l), r = a._copyCanvasContext, r.clearRect(0, 0, k, l), r.drawImage(b.surface.canvas, b.region.x, b.region.y, k, l, 0, 0, k, l), r = r.canvas, h = u = 0) : (r = b.surface.canvas, u = b.region.x, h = b.region.y); + a: { + switch(m) { + case 11: + b = !0; + break a; + default: + b = !1; + } + } + b && (this.context.save(), this.context.beginPath(), this.context.rect(e, g, k, l), this.context.clip()); + this.context.globalCompositeOperation = c(m); + this.context.drawImage(r, u, h, k, l, e, g, k, l); + this.context.globalCompositeOperation = c(1); + b && this.context.restore(); + }; + Object.defineProperty(a.prototype, "context", {get:function() { + return this.surface.context; + }, enumerable:!0, configurable:!0}); + a.prototype.resetTransform = function() { + this.surface.context.setTransform(1, 0, 0, 1, 0, 0); }; + a.prototype.reset = function() { + var a = this.surface.context; + a.setTransform(1, 0, 0, 1, 0, 0); + a.fillStyle = null; + a.strokeStyle = null; + a.globalAlpha = 1; + a.globalColorMatrix = null; + a.globalCompositeOperation = c(1); + }; + a.prototype.fill = function(a) { + var b = this.surface.context, c = this.region; + b.fillStyle = a; + b.fillRect(c.x, c.y, c.w, c.h); + }; + a.prototype.clear = function(a) { + var b = this.surface.context, c = this.region; + a || (a = c); + b.clearRect(a.x, a.y, a.w, a.h); + }; + return a; }(); - d.Canvas2DSurfaceRegion = e; - var m = function() { - function d(a, c) { + e.Canvas2DSurfaceRegion = b; + k = function() { + function a(a, b) { this.canvas = a; this.context = a.getContext("2d"); this.w = a.width; this.h = a.height; - this._regionAllocator = c; + this._regionAllocator = b; } - d.prototype.allocate = function(a, c) { - var d = this._regionAllocator.allocate(a, c); - return d ? new e(this, d) : null; + a.prototype.allocate = function(a, c) { + var e = this._regionAllocator.allocate(a, c); + return e ? new b(this, e, a, c) : null; }; - d.prototype.free = function(a) { + a.prototype.free = function(a) { this._regionAllocator.free(a.region); }; - return d; + return a; }(); - d.Canvas2DSurface = m; - })(d.Canvas2D || (d.Canvas2D = {})); - })(d.GFX || (d.GFX = {})); + e.Canvas2DSurface = k; + })(m.Canvas2D || (m.Canvas2D = {})); + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - (function(s) { - var e = d.GFX.Geometry.Rectangle, t = d.GFX.Geometry.MipMap; + (function(e) { + var c = g.Debug.assert, w = g.GFX.Geometry.Rectangle, k = g.GFX.Geometry.Point, b = g.GFX.Geometry.Matrix, a = g.NumberUtilities.clamp, n = g.NumberUtilities.pow2, p = new g.IndentingWriter(!1, dumpLine), y = function() { + return function(a, b) { + this.surfaceRegion = a; + this.scale = b; + }; + }(); + e.MipMapLevel = y; + var v = function() { + function b(a, c, e, h) { + this._node = c; + this._levels = []; + this._surfaceRegionAllocator = e; + this._size = h; + this._renderer = a; + } + b.prototype.getLevel = function(b) { + b = Math.max(b.getAbsoluteScaleX(), b.getAbsoluteScaleY()); + var c = 0; + 1 !== b && (c = a(Math.round(Math.log(b) / Math.LN2), -5, 3)); + this._node.hasFlags(2097152) || (c = a(c, -5, 0)); + b = n(c); + var e = 5 + c, c = this._levels[e]; + if (!c) { + var h = this._node.getBounds().clone(); + h.scale(b, b); + h.snap(); + var g = this._surfaceRegionAllocator.allocate(h.w, h.h), k = g.region, c = this._levels[e] = new y(g, b), e = new t(g); + e.clip.set(k); + e.matrix.setElements(b, 0, 0, b, k.x - h.x, k.y - h.y); + e.flags |= 64; + this._renderer.renderNodeWithState(this._node, e); + e.free(); + } + return c; + }; + return b; + }(); + e.MipMap = v; (function(a) { a[a.NonZero = 0] = "NonZero"; a[a.EvenOdd = 1] = "EvenOdd"; - })(s.FillRule || (s.FillRule = {})); - var k = function(a) { - function c() { + })(e.FillRule || (e.FillRule = {})); + var l = function(a) { + function b() { a.apply(this, arguments); this.blending = this.imageSmoothing = this.snapToDevicePixels = !0; + this.debugLayers = !1; + this.filters = this.masking = !0; this.cacheShapes = !1; this.cacheShapesMaxSize = 256; this.cacheShapesThreshold = 16; this.alpha = !1; } + __extends(b, a); + return b; + }(m.RendererOptions); + e.Canvas2DRendererOptions = l; + (function(a) { + a[a.None = 0] = "None"; + a[a.IgnoreNextLayer = 1] = "IgnoreNextLayer"; + a[a.RenderMask = 2] = "RenderMask"; + a[a.IgnoreMask = 4] = "IgnoreMask"; + a[a.PaintStencil = 8] = "PaintStencil"; + a[a.PaintClip = 16] = "PaintClip"; + a[a.IgnoreRenderable = 32] = "IgnoreRenderable"; + a[a.IgnoreNextRenderWithCache = 64] = "IgnoreNextRenderWithCache"; + a[a.CacheShapes = 256] = "CacheShapes"; + a[a.PaintFlashing = 512] = "PaintFlashing"; + a[a.PaintBounds = 1024] = "PaintBounds"; + a[a.PaintDirtyRegion = 2048] = "PaintDirtyRegion"; + a[a.ImageSmoothing = 4096] = "ImageSmoothing"; + a[a.PixelSnapping = 8192] = "PixelSnapping"; + })(e.RenderFlags || (e.RenderFlags = {})); + w.createMaxI16(); + var t = function(a) { + function c(d) { + a.call(this); + this.clip = w.createEmpty(); + this.clipList = []; + this.flags = 0; + this.target = null; + this.matrix = b.createIdentity(); + this.colorMatrix = m.ColorMatrix.createIdentity(); + c.allocationCount++; + this.target = d; + } __extends(c, a); + c.prototype.set = function(a) { + this.clip.set(a.clip); + this.target = a.target; + this.matrix.set(a.matrix); + this.colorMatrix.set(a.colorMatrix); + this.flags = a.flags; + g.ArrayUtilities.copyFrom(this.clipList, a.clipList); + }; + c.prototype.clone = function() { + var a = c.allocate(); + a || (a = new c(this.target)); + a.set(this); + return a; + }; + c.allocate = function() { + var a = c._dirtyStack, b = null; + a.length && (b = a.pop()); + return b; + }; + c.prototype.free = function() { + c._dirtyStack.push(this); + }; + c.prototype.transform = function(a) { + var b = this.clone(); + b.matrix.preMultiply(a.getMatrix()); + a.hasColorMatrix() && b.colorMatrix.multiply(a.getColorMatrix()); + return b; + }; + c.prototype.hasFlags = function(a) { + return(this.flags & a) === a; + }; + c.prototype.removeFlags = function(a) { + this.flags &= ~a; + }; + c.prototype.toggleFlags = function(a, b) { + this.flags = b ? this.flags | a : this.flags & ~a; + }; + c.allocationCount = 0; + c._dirtyStack = []; return c; - }(m.StageRendererOptions); - s.Canvas2DStageRendererOptions = k; - var a = function() { - return function(a, c, d) { - "undefined" === typeof c && (c = !1); - "undefined" === typeof d && (d = null); - this.options = a; - this.clipRegion = c; - this.ignoreMask = d; - }; - }(); - s.Canvas2DStageRendererState = a; - var c = e.createMaxI16(), g = function(g) { - function v(a, c, d) { - "undefined" === typeof d && (d = new k); - g.call(this, a, c, d); - c = this.context = a.getContext("2d", {alpha:d.alpha}); - this._viewport = new e(0, 0, a.width, a.height); - this._fillRule = "nonzero"; - c.fillRule = c.mozFillRule = this._fillRule; - v._prepareSurfaceAllocators(); - } - __extends(v, g); - v._prepareSurfaceAllocators = function() { - v._initializedCaches || (v._surfaceCache = new m.SurfaceRegionAllocator.SimpleAllocator(function(a, c) { - var d = document.createElement("canvas"); - "undefined" !== typeof registerScratchCanvas && registerScratchCanvas(d); - var b = Math.max(1024, a), e = Math.max(1024, c); - d.width = b; - d.height = e; - var g = null, g = 1024 <= a || 1024 <= c ? new m.RegionAllocator.GridAllocator(b, e, b, e) : new m.RegionAllocator.BucketAllocator(b, e); - return new s.Canvas2DSurface(d, g); - }), v._shapeCache = new m.SurfaceRegionAllocator.SimpleAllocator(function(a, c) { - var d = document.createElement("canvas"); - "undefined" !== typeof registerScratchCanvas && registerScratchCanvas(d); - d.width = 1024; - d.height = 1024; - var b = b = new m.RegionAllocator.CompactAllocator(1024, 1024); - return new s.Canvas2DSurface(d, b); - }), v._initializedCaches = !0); - }; - v.prototype.resize = function() { - var a = this._canvas, c = this.context; - this._viewport = new e(0, 0, a.width, a.height); - c.fillRule = c.mozFillRule = this._fillRule; - this.render(); + }(m.State); + e.RenderState = t; + var r = function() { + function a() { + this.culledNodes = this.groups = this.shapes = this._count = 0; + } + a.prototype.enter = function(a) { + this._count++; + p && (p.enter("> Frame: " + this._count), this._enterTime = performance.now(), this.culledNodes = this.groups = this.shapes = 0); }; - v.prototype.render = function() { - var a = this._stage, c = this.context; - c.setTransform(1, 0, 0, 1, 0, 0); - c.save(); - var d = this._options; - c.globalAlpha = 1; - var b = this._viewport; - this.renderFrame(a, b, a.matrix, !0); - a.trackDirtyRegions && a.dirtyRegion.clear(); - c.restore(); - d && d.paintViewport && (c.beginPath(), c.rect(b.x, b.y, b.w, b.h), c.strokeStyle = "#FF4981", c.stroke()); + a.prototype.leave = function() { + p && (p.writeLn("Shapes: " + this.shapes + ", Groups: " + this.groups + ", Culled Nodes: " + this.culledNodes), p.writeLn("Elapsed: " + (performance.now() - this._enterTime).toFixed(2)), p.writeLn("Rectangle: " + w.allocationCount + ", Matrix: " + b.allocationCount + ", State: " + t.allocationCount), p.leave("<")); }; - v.prototype.renderFrame = function(c, d, e, b) { - "undefined" === typeof b && (b = !1); - var g = this.context; - g.save(); - this._options.paintViewport || (g.beginPath(), g.rect(d.x, d.y, d.w, d.h), g.clip()); - b && g.clearRect(d.x, d.y, d.w, d.h); - this._renderFrame(g, c, e, d, new a(this._options)); - g.restore(); - }; - v.prototype._renderToSurfaceRegion = function(c, d, g) { - var b = c.getBounds().clone(); - d.transformRectangleAABB(b); - b.snap(); - var h = b.x, k = b.y, n = b.clone(); - n.intersect(g); - n.snap(); - h += n.x - b.x; - k += n.y - b.y; - g = v._surfaceCache.allocate(n.w, n.h); - var b = g.region, b = new e(b.x, b.y, n.w, n.h), f = g.surface.context; - f.setTransform(1, 0, 0, 1, 0, 0); - f.clearRect(b.x, b.y, b.w, b.h); - d = d.clone(); - d.translate(b.x - h, b.y - k); - f.save(); - f.beginPath(); - f.rect(b.x, b.y, b.w, b.h); - f.clip(); - this._renderFrame(f, c, d, b, new a(this._options)); - f.restore(); - return{surfaceRegion:g, surfaceRegionBounds:b, clippedBounds:n}; - }; - v.prototype._renderShape = function(a, c, e, b, g) { - b = c.getBounds(); - if (!b.isEmpty() && g.options.paintRenderable) { - c = c.source; - var k = c.properties.renderCount || 0, n = g.options.cacheShapesMaxSize, f = Math.max(e.getAbsoluteScaleX(), e.getAbsoluteScaleY()); - !g.clipRegion && !c.hasFlags(1) && g.options.cacheShapes && k > g.options.cacheShapesThreshold && b.w * f <= n && b.h * f <= n ? ((k = c.properties.mipMap) || (k = c.properties.mipMap = new t(c, v._shapeCache, n)), e = k.getLevel(e), c = e.surfaceRegion, n = c.region, e && a.drawImage(c.surface.canvas, n.x, n.y, n.w, n.h, b.x, b.y, b.w, b.h), g.options.paintFlashing && (a.fillStyle = d.ColorStyle.Green, a.globalAlpha = .5, a.fillRect(b.x, b.y, b.w, b.h))) : (c.properties.renderCount = - ++k, c.render(a, null, g.clipRegion), g.options.paintFlashing && (a.fillStyle = d.ColorStyle.randomStyle(), a.globalAlpha = .1, a.fillRect(b.x, b.y, b.w, b.h))); + return a; + }(); + e.FrameInfo = r; + var u = function(a) { + function f(b, c, e) { + void 0 === e && (e = new l); + a.call(this, b, c, e); + this._visited = 0; + this._frameInfo = new r; + this._fontSize = 0; + this._layers = []; + if (b instanceof HTMLCanvasElement) { + var g = b; + this._viewport = new w(0, 0, g.width, g.height); + this._target = this._createTarget(g); + } else { + this._addLayer("Background Layer"); + e = this._addLayer("Canvas Layer"); + g = document.createElement("canvas"); + e.appendChild(g); + this._viewport = new w(0, 0, b.scrollWidth, b.scrollHeight); + var k = this; + c.addEventListener(1, function() { + k._onStageBoundsChanged(g); + }); + this._onStageBoundsChanged(g); } + f._prepareSurfaceAllocators(); + } + __extends(f, a); + f.prototype._addLayer = function(a) { + a = document.createElement("div"); + a.style.position = "absolute"; + a.style.overflow = "hidden"; + a.style.width = "100%"; + a.style.height = "100%"; + this._container.appendChild(a); + this._layers.push(a); + return a; }; - v.prototype._renderFrame = function(e, g, k, b, h, p) { - "undefined" === typeof p && (p = !1); - var n = this; - g.visit(function(f, k, r) { - if (p && g === f) { - return 0; - } - if (!f._hasFlags(16384)) { - return 2; + Object.defineProperty(f.prototype, "_backgroundVideoLayer", {get:function() { + return this._layers[0]; + }, enumerable:!0, configurable:!0}); + f.prototype._createTarget = function(a) { + return new e.Canvas2DSurfaceRegion(new e.Canvas2DSurface(a), new m.RegionAllocator.Region(0, 0, a.width, a.height), a.width, a.height); + }; + f.prototype._onStageBoundsChanged = function(a) { + var b = this._stage.getBounds(!0); + b.snap(); + for (var c = this._devicePixelRatio = window.devicePixelRatio || 1, e = b.w / c + "px", c = b.h / c + "px", f = 0;f < this._layers.length;f++) { + var g = this._layers[f]; + g.style.width = e; + g.style.height = c; + } + a.width = b.w; + a.height = b.h; + a.style.position = "absolute"; + a.style.width = e; + a.style.height = c; + this._target = this._createTarget(a); + this._fontSize = 10 * this._devicePixelRatio; + }; + f._prepareSurfaceAllocators = function() { + f._initializedCaches || (f._surfaceCache = new m.SurfaceRegionAllocator.SimpleAllocator(function(a, b) { + var c = document.createElement("canvas"); + "undefined" !== typeof registerScratchCanvas && registerScratchCanvas(c); + var f = Math.max(1024, a), g = Math.max(1024, b); + c.width = f; + c.height = g; + var h = null, h = 512 <= a || 512 <= b ? new m.RegionAllocator.GridAllocator(f, g, f, g) : new m.RegionAllocator.BucketAllocator(f, g); + return new e.Canvas2DSurface(c, h); + }), f._shapeCache = new m.SurfaceRegionAllocator.SimpleAllocator(function(a, b) { + var c = document.createElement("canvas"); + "undefined" !== typeof registerScratchCanvas && registerScratchCanvas(c); + c.width = 1024; + c.height = 1024; + var f = f = new m.RegionAllocator.CompactAllocator(1024, 1024); + return new e.Canvas2DSurface(c, f); + }), f._initializedCaches = !0); + }; + f.prototype.render = function() { + var a = this._stage, b = this._target, c = this._options, e = this._viewport; + b.reset(); + b.context.save(); + b.context.beginPath(); + b.context.rect(e.x, e.y, e.w, e.h); + b.context.clip(); + this._renderStageToTarget(b, a, e); + b.reset(); + c.paintViewport && (b.context.beginPath(), b.context.rect(e.x, e.y, e.w, e.h), b.context.strokeStyle = "#FF4981", b.context.lineWidth = 2, b.context.stroke()); + b.context.restore(); + }; + f.prototype.renderNode = function(a, b, c) { + var e = new t(this._target); + e.clip.set(b); + e.flags = 256; + e.matrix.set(c); + a.visit(this, e); + e.free(); + }; + f.prototype.renderNodeWithState = function(a, b) { + a.visit(this, b); + }; + f.prototype._renderWithCache = function(a, b) { + var c = b.matrix, e = a.getBounds(); + if (e.isEmpty()) { + return!1; + } + var g = this._options.cacheShapesMaxSize, h = Math.max(c.getAbsoluteScaleX(), c.getAbsoluteScaleY()), k = !!(b.flags & 16), l = !!(b.flags & 8); + if (b.hasFlags(256)) { + if (l || k || !b.colorMatrix.isIdentity() || a.hasFlags(1048576) || 100 < this._options.cacheShapesThreshold || e.w * h > g || e.h * h > g) { + return!1; } - var s = f.getBounds(); - if (h.ignoreMask !== f && f.mask && !h.clipRegion) { - return e.save(), n._renderFrame(e, f.mask, f.mask.getConcatenatedMatrix(), b, new a(h.options, !0)), n._renderFrame(e, f, k, b, new a(h.options, !1, f)), e.restore(), 2; + (h = a.properties.mipMap) || (h = a.properties.mipMap = new v(this, a, f._shapeCache, g)); + k = h.getLevel(c); + g = k.surfaceRegion; + h = g.region; + return k ? (k = b.target.context, k.imageSmoothingEnabled = k.mozImageSmoothingEnabled = !0, k.setTransform(c.a, c.b, c.c, c.d, c.tx, c.ty), k.drawImage(g.surface.canvas, h.x, h.y, h.w, h.h, e.x, e.y, e.w, e.h), !0) : !1; + } + }; + f.prototype._intersectsClipList = function(a, b) { + var c = a.getBounds(!0), e = !1; + b.matrix.transformRectangleAABB(c); + b.clip.intersects(c) && (e = !0); + var f = b.clipList; + if (e && f.length) { + for (var e = !1, g = 0;g < f.length;g++) { + if (c.intersects(f[g])) { + e = !0; + break; + } } - if (r & 4096) { - e.save(), e.enterBuildingClippingRegion(), n._renderFrame(e, f, k, c, new a(h.options, !0)), e.leaveBuildingClippingRegion(); - } else { - if (r & 8192) { - e.restore(); - } else { - if (!b.intersectsTransformedAABB(s, k)) { - return 2; - } - (1 === f.pixelSnapping || h.options.snapToDevicePixels) && k.snap(); - e.imageSmoothingEnabled = 1 === f.smoothing || h.options.imageSmoothing; - e.setTransform(k.a, k.b, k.c, k.d, k.tx, k.ty); - var t = f.getConcatenatedColorMatrix(); - t.isIdentity() ? (e.globalAlpha = 1, e.globalColorMatrix = null) : t.hasOnlyAlphaMultiplier() ? (e.globalAlpha = t.alphaMultiplier, e.globalColorMatrix = null) : (e.globalAlpha = 1, e.globalColorMatrix = t); - if (r & 2 && !h.clipRegion) { - return 2; - } - r = f.getBounds().clone(); - k.transformRectangleAABB(r); - r.snap(); - if (f !== g && h.options.blending && (e.globalCompositeOperation = n._getCompositeOperation(f.blendMode), 1 !== f.blendMode)) { - return s = n._renderToSurfaceRegion(f, k, b), f = s.surfaceRegion, k = s.surfaceRegionBounds, s = s.clippedBounds, e.setTransform(1, 0, 0, 1, 0, 0), e.drawImage(f.surface.canvas, k.x, k.y, k.w, k.h, s.x, s.y, k.w, k.h), f.surface.free(f), 2; - } - if (f instanceof m.Shape) { - f._previouslyRenderedAABB = r, n._renderShape(e, f, k, b, h); - } else { - if (f instanceof m.ClipRectangle) { - return e.save(), e.beginPath(), e.rect(s.x, s.y, s.w, s.h), e.clip(), r.intersect(b), f._hasFlags(32768) || (e.fillStyle = f.color.toCSSStyle(), e.fillRect(s.x, s.y, s.w, s.h)), n._renderFrame(e, f, k, r, h, !0), e.restore(), 2; + } + c.free(); + return e; + }; + f.prototype.visitGroup = function(a, b) { + this._frameInfo.groups++; + a.getBounds(); + if ((!a.hasFlags(4) || b.flags & 4) && a.hasFlags(65536)) { + if (b.flags & 1 || 1 === a.getLayer().blendMode && !a.getLayer().mask || !this._options.blending) { + if (this._intersectsClipList(a, b)) { + for (var c = null, e = a.getChildren(), f = 0;f < e.length;f++) { + var g = e[f], h = b.transform(g.getTransform()); + h.toggleFlags(4096, g.hasFlags(524288)); + if (0 <= g.clip) { + c = c || new Uint8Array(e.length); + c[g.clip + f]++; + var k = h.clone(); + b.target.context.save(); + k.flags |= 16; + g.visit(this, k); + k.free(); + } else { + g.visit(this, h); + } + if (c && 0 < c[f]) { + for (;c[f]--;) { + b.target.context.restore(); + } } - h.options.paintBounds && f instanceof m.FrameContainer && (s = f.getBounds().clone(), e.strokeStyle = d.ColorStyle.LightOrange, e.strokeRect(s.x, s.y, s.w, s.h)); + h.free(); } - return 0; + } else { + this._frameInfo.culledNodes++; } + } else { + b = b.clone(), b.flags |= 1, this._renderLayer(a, b), b.free(); } - }, k, 0, 16); + this._renderDebugInfo(a, b); + } }; - v.prototype._getCompositeOperation = function(a) { - var c = "source-over"; - switch(a) { - case 3: - c = "multiply"; - break; - case 4: - c = "screen"; - break; - case 5: - c = "lighten"; - break; - case 6: - c = "darken"; - break; - case 7: - c = "difference"; - break; - case 13: - c = "overlay"; - break; - case 14: - c = "hard-light"; + f.prototype._renderDebugInfo = function(a, b) { + if (b.flags & 1024) { + var c = b.target.context, e = a.getBounds(!0), h = a.properties.style; + h || (h = a.properties.style = g.Color.randomColor().toCSSStyle()); + c.strokeStyle = h; + b.matrix.transformRectangleAABB(e); + c.setTransform(1, 0, 0, 1, 0, 0); + e.free(); + e = a.getBounds(); + h = f._debugPoints; + b.matrix.transformRectangle(e, h); + c.lineWidth = 1; + c.beginPath(); + c.moveTo(h[0].x, h[0].y); + c.lineTo(h[1].x, h[1].y); + c.lineTo(h[2].x, h[2].y); + c.lineTo(h[3].x, h[3].y); + c.lineTo(h[0].x, h[0].y); + c.stroke(); + } + }; + f.prototype.visitStage = function(a, b) { + var c = b.target.context, e = a.getBounds(!0); + b.matrix.transformRectangleAABB(e); + e.intersect(b.clip); + b.target.reset(); + b = b.clone(); + this._options.clear && b.target.clear(b.clip); + a.hasFlags(32768) || !a.color || b.flags & 32 || (this._container.style.backgroundColor = a.color.toCSSStyle()); + this.visitGroup(a, b); + a.dirtyRegion && (c.restore(), b.target.reset(), c.globalAlpha = .4, b.hasFlags(2048) && a.dirtyRegion.render(b.target.context), a.dirtyRegion.clear()); + b.free(); + }; + f.prototype.visitShape = function(a, b) { + if (this._intersectsClipList(a, b)) { + var c = b.matrix; + b.flags & 8192 && (c = c.clone(), c.snap()); + var f = b.target.context; + e.Filters._applyColorMatrix(f, b.colorMatrix); + a.source instanceof m.RenderableVideo ? this.visitRenderableVideo(a.source, b) : 0 < f.globalAlpha && this.visitRenderable(a.source, b, a.ratio); + b.flags & 8192 && c.free(); + } + }; + f.prototype.visitRenderableVideo = function(a, b) { + if (a.video && a.video.videoWidth) { + var c = this._devicePixelRatio, e = b.matrix.clone(); + e.scale(1 / c, 1 / c); + var c = a.getBounds(), f = g.GFX.Geometry.Matrix.createIdentity(); + f.scale(c.w / a.video.videoWidth, c.h / a.video.videoHeight); + e.preMultiply(f); + f.free(); + c = e.toCSSTransform(); + a.video.style.transformOrigin = "0 0"; + a.video.style.transform = c; + this._backgroundVideoLayer !== a.video.parentElement && (this._backgroundVideoLayer.appendChild(a.video), 1 === a.state && a.play()); + e.free(); + } + }; + f.prototype.visitRenderable = function(a, b, c) { + var e = a.getBounds(), f = b.matrix, h = b.target.context, k = !!(b.flags & 16), l = !!(b.flags & 8), p = !!(b.flags & 512); + if (!(e.isEmpty() || b.flags & 32)) { + if (b.hasFlags(64)) { + b.removeFlags(64); + } else { + if (this._renderWithCache(a, b)) { + return; + } + } + h.setTransform(f.a, f.b, f.c, f.d, f.tx, f.ty); + var n = 0; + p && (n = performance.now()); + this._frameInfo.shapes++; + h.imageSmoothingEnabled = h.mozImageSmoothingEnabled = b.hasFlags(4096); + b = a.properties.renderCount || 0; + Math.max(f.getAbsoluteScaleX(), f.getAbsoluteScaleY()); + a.properties.renderCount = ++b; + a.render(h, c, null, k, l); + p && (a = performance.now() - n, h.fillStyle = g.ColorStyle.gradientColor(.1 / a), h.globalAlpha = .3 + .1 * Math.random(), h.fillRect(e.x, e.y, e.w, e.h)); + } + }; + f.prototype._renderLayer = function(a, b) { + var c = a.getLayer(), e = c.mask; + if (e) { + this._renderWithMask(a, e, c.blendMode, !a.hasFlags(131072) || !e.hasFlags(131072), b); + } else { + var e = w.allocate(), f = this._renderToTemporarySurface(a, b, e); + f && (b.target.draw(f, e.x, e.y, e.w, e.h, c.blendMode), f.free()); + e.free(); + } + }; + f.prototype._renderWithMask = function(a, b, c, e, f) { + var g = b.getTransform().getConcatenatedMatrix(!0); + b.parent || (g = g.scale(this._devicePixelRatio, this._devicePixelRatio)); + var h = a.getBounds().clone(); + f.matrix.transformRectangleAABB(h); + h.snap(); + if (!h.isEmpty()) { + var k = b.getBounds().clone(); + g.transformRectangleAABB(k); + k.snap(); + if (!k.isEmpty()) { + var l = f.clip.clone(); + l.intersect(h); + l.intersect(k); + l.snap(); + l.isEmpty() || (h = f.clone(), h.clip.set(l), a = this._renderToTemporarySurface(a, h, w.createEmpty()), h.free(), h = f.clone(), h.clip.set(l), h.matrix = g, h.flags |= 4, e && (h.flags |= 8), b = this._renderToTemporarySurface(b, h, w.createEmpty()), h.free(), a.draw(b, 0, 0, l.w, l.h, 11), f.target.draw(a, l.x, l.y, l.w, l.h, c), b.free(), a.free()); + } + } + }; + f.prototype._renderStageToTarget = function(a, c, e) { + w.allocationCount = b.allocationCount = t.allocationCount = 0; + a = new t(a); + a.clip.set(e); + this._options.paintRenderable || (a.flags |= 32); + this._options.paintBounds && (a.flags |= 1024); + this._options.paintDirtyRegion && (a.flags |= 2048); + this._options.paintFlashing && (a.flags |= 512); + this._options.cacheShapes && (a.flags |= 256); + this._options.imageSmoothing && (a.flags |= 4096); + this._options.snapToDevicePixels && (a.flags |= 8192); + this._frameInfo.enter(a); + c.visit(this, a); + this._frameInfo.leave(); + }; + f.prototype._renderToTemporarySurface = function(a, b, c) { + var e = b.matrix, f = a.getBounds().clone(); + e.transformRectangleAABB(f); + f.snap(); + c.set(f); + c.intersect(b.clip); + c.snap(); + if (c.isEmpty()) { + return null; } + var f = this._allocateSurface(c.w, c.h), h = f.region, h = new w(h.x, h.y, c.w, c.h); + f.context.setTransform(1, 0, 0, 1, 0, 0); + f.clear(); + e = e.clone(); + e.translate(h.x - c.x, h.y - c.y); + f.context.save(); + b = b.clone(); + b.target = f; + b.matrix = e; + b.clip.set(h); + a.visit(this, b); + b.free(); + f.context.restore(); + return f; + }; + f.prototype._allocateSurface = function(a, b) { + var c = f._surfaceCache.allocate(a, b); + c.fill("#FF4981"); return c; }; - v._initializedCaches = !1; - return v; - }(m.StageRenderer); - s.Canvas2DStageRenderer = g; + f.prototype.screenShot = function(a, b) { + if (b) { + var e = this._stage.content.groupChild.child; + c(e instanceof m.Stage); + a = e.content.getBounds(!0); + e.content.getTransform().getConcatenatedMatrix().transformRectangleAABB(a); + a.intersect(this._viewport); + } + a || (a = new w(0, 0, this._target.w, this._target.h)); + e = document.createElement("canvas"); + e.width = a.w; + e.height = a.h; + var f = e.getContext("2d"); + f.fillStyle = this._container.style.backgroundColor; + f.fillRect(0, 0, a.w, a.h); + f.drawImage(this._target.context.canvas, a.x, a.y, a.w, a.h, 0, 0, a.w, a.h); + return new m.ScreenShot(e.toDataURL("image/png"), a.w, a.h); + }; + f._initializedCaches = !1; + f._debugPoints = k.createEmptyPoints(4); + return f; + }(m.Renderer); + e.Canvas2DRenderer = u; })(m.Canvas2D || (m.Canvas2D = {})); - })(d.GFX || (d.GFX = {})); -})(Shumway || (Shumway = {})); -(function(d) { - (function(d) { - var s = function() { - function e(d, e) { - this.container = d; - this.pixelRatio = e; - } - e.prototype.render = function(e, k) { - var a = 1 / this.pixelRatio, c = this; - e.visit(function(e, k) { - k = k.clone(); - k.scale(a, a); - if (e instanceof d.Shape) { - var s = c.getDIV(e); - s.style.transform = s.style.webkitTransform = k.toCSSTransform(); - } - return 0; - }, e.matrix); - }; - e.prototype.getDIV = function(d) { - var e = d.properties, a = e.div; - a || (a = e.div = document.createElement("div"), a.style.width = d.w + "px", a.style.height = d.h + "px", a.style.position = "absolute", e = document.createElement("canvas"), e.width = d.w, e.height = d.h, d.source.render(e.getContext("2d")), a.appendChild(e), a.style.transformOrigin = a.style.webkitTransformOrigin = "0px 0px", a.appendChild(e), this.container.appendChild(a)); - return a; - }; - return e; - }(); - d.DOMStageRenderer = s; - })(d.GFX || (d.GFX = {})); + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - var s = m.Geometry.Point, e = m.Geometry.Matrix, t = m.Geometry.Rectangle, k = d.GFX.WebGL.WebGLStageRenderer, a = d.GFX.WebGL.WebGLStageRendererOptions, c = d.Tools.Mini.FPS, g = function() { + var e = g.Debug.assert, c = m.Geometry.Point, w = m.Geometry.Matrix, k = m.Geometry.Rectangle, b = g.Tools.Mini.FPS, a = function() { function a() { } a.prototype.onMouseUp = function(a, b) { @@ -10245,403 +11429,454 @@ }; return a; }(); - m.State = g; - var p = function(a) { - function c() { + m.UIState = a; + var n = function(a) { + function b() { a.apply(this, arguments); this._keyCodes = []; } - __extends(c, a); - c.prototype.onMouseDown = function(a, c) { - c.altKey && (a.state = new u(a.worldView, a.getMousePosition(c, null), a.worldView.matrix.clone())); + __extends(b, a); + b.prototype.onMouseDown = function(a, b) { + b.altKey && (a.state = new y(a.worldView, a.getMousePosition(b, null), a.worldView.getTransform().getMatrix(!0))); }; - c.prototype.onMouseClick = function(a, c) { + b.prototype.onMouseClick = function(a, b) { }; - c.prototype.onKeyDown = function(a, c) { - this._keyCodes[c.keyCode] = !0; + b.prototype.onKeyDown = function(a, b) { + this._keyCodes[b.keyCode] = !0; }; - c.prototype.onKeyUp = function(a, c) { - this._keyCodes[c.keyCode] = !1; + b.prototype.onKeyUp = function(a, b) { + this._keyCodes[b.keyCode] = !1; }; - return c; - }(g), v = function(a) { - function c() { + return b; + }(a), p = function(a) { + function b() { a.apply(this, arguments); this._keyCodes = []; this._paused = !1; - this._mousePosition = new s(0, 0); + this._mousePosition = new c(0, 0); } - __extends(c, a); - c.prototype.onMouseMove = function(a, c) { - this._mousePosition = a.getMousePosition(c, null); + __extends(b, a); + b.prototype.onMouseMove = function(a, b) { + this._mousePosition = a.getMousePosition(b, null); this._update(a); }; - c.prototype.onMouseDown = function(a, c) { + b.prototype.onMouseDown = function(a, b) { }; - c.prototype.onMouseClick = function(a, c) { + b.prototype.onMouseClick = function(a, b) { }; - c.prototype.onMouseWheel = function(a, c) { - var d = "DOMMouseScroll" === c.type ? -c.detail : c.wheelDelta / 40; - if (c.altKey) { - c.preventDefault(); - var e = a.getMousePosition(c, null), f = a.worldView.matrix.clone(), d = 1 + d / 1E3; + b.prototype.onMouseWheel = function(a, b) { + var c = "DOMMouseScroll" === b.type ? -b.detail : b.wheelDelta / 40; + if (b.altKey) { + b.preventDefault(); + var e = a.getMousePosition(b, null), f = a.worldView.getTransform().getMatrix(!0), c = 1 + c / 1E3; f.translate(-e.x, -e.y); - f.scale(d, d); + f.scale(c, c); f.translate(e.x, e.y); - a.worldView.matrix = f; + a.worldView.getTransform().setMatrix(f); } }; - c.prototype.onKeyPress = function(a, c) { - if (112 === c.keyCode || "p" === c.key) { - this._paused = !this._paused; + b.prototype.onKeyPress = function(a, b) { + if (b.altKey) { + var c = b.keyCode || b.which; + console.info("onKeyPress Code: " + c); + switch(c) { + case 248: + this._paused = !this._paused; + b.preventDefault(); + break; + case 223: + a.toggleOption("paintRenderable"); + b.preventDefault(); + break; + case 8730: + a.toggleOption("paintViewport"); + b.preventDefault(); + break; + case 8747: + a.toggleOption("paintBounds"); + b.preventDefault(); + break; + case 8706: + a.toggleOption("paintDirtyRegion"); + b.preventDefault(); + break; + case 231: + a.toggleOption("clear"); + b.preventDefault(); + break; + case 402: + a.toggleOption("paintFlashing"), b.preventDefault(); + } + this._update(a); } - this._keyCodes[83] && a.toggleOption("paintRenderable"); - this._keyCodes[86] && a.toggleOption("paintViewport"); - this._keyCodes[66] && a.toggleOption("paintBounds"); - this._keyCodes[70] && a.toggleOption("paintFlashing"); - this._update(a); }; - c.prototype.onKeyDown = function(a, c) { - this._keyCodes[c.keyCode] = !0; + b.prototype.onKeyDown = function(a, b) { + this._keyCodes[b.keyCode] = !0; this._update(a); }; - c.prototype.onKeyUp = function(a, c) { - this._keyCodes[c.keyCode] = !1; + b.prototype.onKeyUp = function(a, b) { + this._keyCodes[b.keyCode] = !1; this._update(a); }; - c.prototype._update = function(a) { + b.prototype._update = function(a) { a.paused = this._paused; if (a.getOption()) { - var c = m.viewportLoupeDiameter.value, d = m.viewportLoupeDiameter.value; - a.viewport = new t(this._mousePosition.x - c / 2, this._mousePosition.y - d / 2, c, d); + var b = m.viewportLoupeDiameter.value, c = m.viewportLoupeDiameter.value; + a.viewport = new k(this._mousePosition.x - b / 2, this._mousePosition.y - c / 2, b, c); } else { a.viewport = null; } }; - return c; - }(g); + return b; + }(a); (function(a) { - function c() { + function b() { a.apply(this, arguments); this._startTime = Date.now(); } - __extends(c, a); - c.prototype.onMouseMove = function(a, c) { + __extends(b, a); + b.prototype.onMouseMove = function(a, b) { if (!(10 > Date.now() - this._startTime)) { - var d = a.queryFrameUnderMouse(c); - d && d.hasCapability() && (a.state = new u(d, a.getMousePosition(c, null), d.matrix.clone())); + var c = a._world; + c && (a.state = new y(c, a.getMousePosition(b, null), c.getTransform().getMatrix(!0))); } }; - c.prototype.onMouseUp = function(a, c) { - a.state = new p; - a.selectFrameUnderMouse(c); + b.prototype.onMouseUp = function(a, b) { + a.state = new n; + a.selectNodeUnderMouse(b); }; - return c; - })(g); - var u = function(a) { - function c(b, d, e) { + return b; + })(a); + var y = function(a) { + function b(c, e, g) { a.call(this); - this._target = b; - this._startPosition = d; - this._startMatrix = e; - } - __extends(c, a); - c.prototype.onMouseMove = function(a, c) { - c.preventDefault(); - var d = a.getMousePosition(c, null); - d.sub(this._startPosition); - this._target.matrix = this._startMatrix.clone().translate(d.x, d.y); + this._target = c; + this._startPosition = e; + this._startMatrix = g; + } + __extends(b, a); + b.prototype.onMouseMove = function(a, b) { + b.preventDefault(); + var c = a.getMousePosition(b, null); + c.sub(this._startPosition); + this._target.getTransform().setMatrix(this._startMatrix.clone().translate(c.x, c.y)); a.state = this; }; - c.prototype.onMouseUp = function(a, c) { - a.state = new p; + b.prototype.onMouseUp = function(a, b) { + a.state = new n; }; - return c; - }(g), g = function() { - function g(e, b, h, l) { - function n() { - var a = document.createElement("canvas"); - a.style.backgroundColor = $; - e.appendChild(a); - u.push(a); - var b = new m.Canvas2D.Canvas2DStageRendererOptions; - b.alpha = V; - t.push(b); - B.push(new m.Canvas2D.Canvas2DStageRenderer(a, s, b)); - } - function f() { - var b = document.createElement("canvas"); - b.style.backgroundColor = $; - e.appendChild(b); - u.push(b); - var c = new a; - t.push(c); - B.push(new k(b, s, c)); - } - function q(a) { - C.getMousePosition(a, C._world); - C._state.onMouseWheel(C, a); - C._persistentState.onMouseWheel(C, a); - } - "undefined" === typeof h && (h = !1); - "undefined" === typeof l && (l = void 0); - this._state = new p; - this._persistentState = new v; + return b; + }(a), a = function() { + function a(c, k, r) { + function v(a) { + d._state.onMouseWheel(d, a); + d._persistentState.onMouseWheel(d, a); + } + void 0 === k && (k = !1); + void 0 === r && (r = void 0); + this._state = new n; + this._persistentState = new p; this.paused = !1; this.viewport = null; - this._selectedFrames = []; - this._eventListeners = d.ObjectUtilities.createEmptyObject(); - var s = this._stage = new m.Stage(128, 128, !0); - this._worldView = new m.FrameContainer; - this._worldViewOverlay = new m.FrameContainer; - this._world = new m.FrameContainer; - this._stage.addChild(this._worldView); + this._selectedNodes = []; + this._eventListeners = Object.create(null); + this._fullScreen = !1; + e(c && 0 === c.children.length, "Easel container must be empty."); + this._container = c; + this._stage = new m.Stage(512, 512, !0); + this._worldView = this._stage.content; + this._world = new m.Group; this._worldView.addChild(this._world); - this._worldView.addChild(this._worldViewOverlay); - this._disableHidpi = h; - m.hud.value ? (h = document.createElement("div"), h.style.position = "absolute", h.style.bottom = "0", h.style.width = "100%", h.style.height = "16px", this._fpsCanvas = document.createElement("canvas"), h.appendChild(this._fpsCanvas), e.appendChild(h), this._fps = new c(this._fpsCanvas)) : this._fps = null; - window.addEventListener("resize", this._deferredResizeHandler.bind(this), !1); - var t = this._options = [], u = this._canvases = [], B = this._renderers = [], V = 0 === l; - this.transparent = V; - var $ = void 0 === l ? "#14171a" : 0 === l ? "transparent" : d.ColorUtilities.rgbaToCSSStyle(l); - switch(b) { - case 0: - n(); - break; - case 1: - f(); - break; - case 2: - n(), f(); + this._disableHiDPI = k; + k = document.createElement("div"); + k.style.position = "absolute"; + k.style.width = "100%"; + k.style.height = "100%"; + c.appendChild(k); + if (m.hud.value) { + var h = document.createElement("div"); + h.style.position = "absolute"; + h.style.width = "100%"; + h.style.height = "100%"; + h.style.pointerEvents = "none"; + var f = document.createElement("div"); + f.style.position = "absolute"; + f.style.width = "100%"; + f.style.height = "20px"; + f.style.pointerEvents = "none"; + h.appendChild(f); + c.appendChild(h); + this._fps = new b(f); + } else { + this._fps = null; } - this._resizeHandler(); + this.transparent = h = 0 === r; + void 0 === r || 0 === r || g.ColorUtilities.rgbaToCSSStyle(r); + this._options = new m.Canvas2D.Canvas2DRendererOptions; + this._options.alpha = h; + this._renderer = new m.Canvas2D.Canvas2DRenderer(k, this._stage, this._options); + this._listenForContainerSizeChanges(); this._onMouseUp = this._onMouseUp.bind(this); this._onMouseDown = this._onMouseDown.bind(this); this._onMouseMove = this._onMouseMove.bind(this); - var C = this; + var d = this; window.addEventListener("mouseup", function(a) { - C._state.onMouseUp(C, a); - C._render(); + d._state.onMouseUp(d, a); + d._render(); }, !1); window.addEventListener("mousemove", function(a) { - C.getMousePosition(a, C._world); - C._state.onMouseMove(C, a); - C._persistentState.onMouseMove(C, a); + d._state.onMouseMove(d, a); + d._persistentState.onMouseMove(d, a); }, !1); - window.addEventListener("DOMMouseScroll", q); - window.addEventListener("mousewheel", q); - u.forEach(function(a) { - return a.addEventListener("mousedown", function(a) { - C._state.onMouseDown(C, a); - }, !1); + window.addEventListener("DOMMouseScroll", v); + window.addEventListener("mousewheel", v); + c.addEventListener("mousedown", function(a) { + d._state.onMouseDown(d, a); }); window.addEventListener("keydown", function(a) { - C._state.onKeyDown(C, a); - C._persistentState.onKeyDown(C, a); + d._state.onKeyDown(d, a); + if (d._state !== d._persistentState) { + d._persistentState.onKeyDown(d, a); + } }, !1); window.addEventListener("keypress", function(a) { - C._state.onKeyPress(C, a); - C._persistentState.onKeyPress(C, a); + d._state.onKeyPress(d, a); + if (d._state !== d._persistentState) { + d._persistentState.onKeyPress(d, a); + } }, !1); window.addEventListener("keyup", function(a) { - C._state.onKeyUp(C, a); - C._persistentState.onKeyUp(C, a); + d._state.onKeyUp(d, a); + if (d._state !== d._persistentState) { + d._persistentState.onKeyUp(d, a); + } }, !1); this._enterRenderLoop(); } - g.prototype.addEventListener = function(a, b) { + a.prototype._listenForContainerSizeChanges = function() { + var a = this._containerWidth, b = this._containerHeight; + this._onContainerSizeChanged(); + var c = this; + setInterval(function() { + if (a !== c._containerWidth || b !== c._containerHeight) { + c._onContainerSizeChanged(), a = c._containerWidth, b = c._containerHeight; + } + }, 10); + }; + a.prototype._onContainerSizeChanged = function() { + var a = this.getRatio(), b = Math.ceil(this._containerWidth * a), c = Math.ceil(this._containerHeight * a); + this._stage.setBounds(new k(0, 0, b, c)); + this._stage.content.setBounds(new k(0, 0, b, c)); + this._worldView.getTransform().setMatrix(new w(a, 0, 0, a, 0, 0)); + this._dispatchEvent("resize"); + }; + a.prototype.addEventListener = function(a, b) { this._eventListeners[a] || (this._eventListeners[a] = []); this._eventListeners[a].push(b); }; - g.prototype._dispatchEvent = function() { - var a = this._eventListeners.render; - if (a) { + a.prototype._dispatchEvent = function(a) { + if (a = this._eventListeners[a]) { for (var b = 0;b < a.length;b++) { a[b](); } } }; - g.prototype._enterRenderLoop = function() { + a.prototype._enterRenderLoop = function() { var a = this; - requestAnimationFrame(function h() { + requestAnimationFrame(function r() { a.render(); - requestAnimationFrame(h); + requestAnimationFrame(r); }); }; - Object.defineProperty(g.prototype, "state", {set:function(a) { + Object.defineProperty(a.prototype, "state", {set:function(a) { this._state = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "cursor", {set:function(a) { - this._canvases.forEach(function(b) { - return b.style.cursor = a; - }); + Object.defineProperty(a.prototype, "cursor", {set:function(a) { + this._container.style.cursor = a; }, enumerable:!0, configurable:!0}); - g.prototype._render = function() { - var a = (this._stage.readyToRender() || m.forcePaint.value) && !this.paused; + a.prototype._render = function() { + m.RenderableVideo.checkForVideoUpdates(); + var a = (this._stage.readyToRender() || m.forcePaint.value) && !this.paused, b = 0; if (a) { - for (var b = 0;b < this._renderers.length;b++) { - var c = this._renderers[b]; - c.viewport = this.viewport ? this.viewport : new t(0, 0, this._canvases[b].width, this._canvases[b].height); - this._dispatchEvent(); - c.render(); - } + var c = this._renderer; + c.viewport = this.viewport ? this.viewport : this._stage.getBounds(); + this._dispatchEvent("render"); + b = performance.now(); + c.render(); + b = performance.now() - b; } - this._fps && this._fps.tickAndRender(!a); + this._fps && this._fps.tickAndRender(!a, b); }; - g.prototype.render = function() { + a.prototype.render = function() { this._render(); }; - Object.defineProperty(g.prototype, "world", {get:function() { + Object.defineProperty(a.prototype, "world", {get:function() { return this._world; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "worldView", {get:function() { + Object.defineProperty(a.prototype, "worldView", {get:function() { return this._worldView; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "worldOverlay", {get:function() { - return this._worldViewOverlay; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "stage", {get:function() { + Object.defineProperty(a.prototype, "stage", {get:function() { return this._stage; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "options", {get:function() { - return this._options[0]; + Object.defineProperty(a.prototype, "options", {get:function() { + return this._options; }, enumerable:!0, configurable:!0}); - g.prototype.toggleOption = function(a) { - for (var b = 0;b < this._options.length;b++) { - var c = this._options[b]; - c[a] = !c[a]; - } + a.prototype.getDisplayParameters = function() { + return{stageWidth:this._containerWidth, stageHeight:this._containerHeight, pixelRatio:this.getRatio(), screenWidth:window.screen ? window.screen.width : 640, screenHeight:window.screen ? window.screen.height : 480}; }; - g.prototype.getOption = function() { - return this._options[0].paintViewport; + a.prototype.toggleOption = function(a) { + var b = this._options; + b[a] = !b[a]; }; - g.prototype._deferredResizeHandler = function() { - clearTimeout(this._deferredResizeHandlerTimeout); - this._deferredResizeHandlerTimeout = setTimeout(this._resizeHandler.bind(this), 1E3); + a.prototype.getOption = function() { + return this._options.paintViewport; }; - g.prototype._resizeHandler = function() { + a.prototype.getRatio = function() { var a = window.devicePixelRatio || 1, b = 1; - 1 === a || this._disableHidpi || (b = a / 1); - for (a = 0;a < this._canvases.length;a++) { - var c = this._canvases[a], d = c.parentElement, g = d.clientWidth, d = d.clientHeight / this._canvases.length; - 1 < b ? (c.width = g * b, c.height = d * b, c.style.width = g + "px", c.style.height = d + "px") : (c.width = g, c.height = d); - this._stage.w = c.width; - this._stage.h = c.height; - this._renderers[a].resize(); - } - this._stage.matrix.set(new e(b, 0, 0, b, 0, 0)); - }; - g.prototype.resize = function() { - this._resizeHandler(); - }; - g.prototype.queryFrameUnderMouse = function(a) { - a = this.stage.queryFramesByPoint(this.getMousePosition(a, null)); - return 0 < a.length ? a[0] : null; + 1 === a || this._disableHiDPI || (b = a / 1); + return b; + }; + Object.defineProperty(a.prototype, "_containerWidth", {get:function() { + return this._container.clientWidth; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(a.prototype, "_containerHeight", {get:function() { + return this._container.clientHeight; + }, enumerable:!0, configurable:!0}); + a.prototype.queryNodeUnderMouse = function(a) { + return this._world; }; - g.prototype.selectFrameUnderMouse = function(a) { - (a = this.queryFrameUnderMouse(a)) && a.hasCapability() ? this._selectedFrames.push(a) : this._selectedFrames = []; + a.prototype.selectNodeUnderMouse = function(a) { + (a = this._world) && this._selectedNodes.push(a); this._render(); }; - g.prototype.getMousePosition = function(a, b) { - var c = this._canvases[0], d = c.getBoundingClientRect(), c = new s(c.width / d.width * (a.clientX - d.left), c.height / d.height * (a.clientY - d.top)); + a.prototype.getMousePosition = function(a, b) { + var e = this._container, g = e.getBoundingClientRect(), h = this.getRatio(), e = new c(e.scrollWidth / g.width * (a.clientX - g.left) * h, e.scrollHeight / g.height * (a.clientY - g.top) * h); if (!b) { - return c; + return e; } - d = e.createIdentity(); - b.getConcatenatedMatrix().inverse(d); - d.transformPoint(c); - return c; + g = w.createIdentity(); + b.getTransform().getConcatenatedMatrix().inverse(g); + g.transformPoint(e); + return e; }; - g.prototype.getMouseWorldPosition = function(a) { + a.prototype.getMouseWorldPosition = function(a) { return this.getMousePosition(a, this._world); }; - g.prototype._onMouseDown = function(a) { - this._renderers.forEach(function(a) { - return a.render(); - }); + a.prototype._onMouseDown = function(a) { + }; + a.prototype._onMouseUp = function(a) { }; - g.prototype._onMouseUp = function(a) { + a.prototype._onMouseMove = function(a) { }; - g.prototype._onMouseMove = function(a) { + a.prototype.screenShot = function(a, b) { + return this._renderer.screenShot(a, b); }; - return g; + return a; }(); - m.Easel = g; - })(d.GFX || (d.GFX = {})); + m.Easel = a; + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - var s = d.GFX.Geometry.Rectangle, e = d.GFX.Geometry.Matrix; - (function(a) { - a[a.Simple = 0] = "Simple"; + var e = g.GFX.Geometry.Matrix; + (function(c) { + c[c.Simple = 0] = "Simple"; })(m.Layout || (m.Layout = {})); - var t = function(a) { - function c() { - a.apply(this, arguments); + var c = function(c) { + function b() { + c.apply(this, arguments); this.layout = 0; } - __extends(c, a); - return c; - }(m.StageRendererOptions); - m.TreeStageRendererOptions = t; - var k = function(a) { - function c(c, d, e) { - "undefined" === typeof e && (e = new t); - a.call(this, c, d, e); - this.context = c.getContext("2d"); - this._viewport = new s(0, 0, c.width, c.height); - } - __extends(c, a); - c.prototype.render = function() { - var a = this.context; + __extends(b, c); + return b; + }(m.RendererOptions); + m.TreeRendererOptions = c; + var w = function(g) { + function b(a, b, e) { + void 0 === e && (e = new c); + g.call(this, a, b, e); + this._canvas = document.createElement("canvas"); + this._container.appendChild(this._canvas); + this._context = this._canvas.getContext("2d"); + this._listenForContainerSizeChanges(); + } + __extends(b, g); + b.prototype._listenForContainerSizeChanges = function() { + var a = this._containerWidth, b = this._containerHeight; + this._onContainerSizeChanged(); + var c = this; + setInterval(function() { + if (a !== c._containerWidth || b !== c._containerHeight) { + c._onContainerSizeChanged(), a = c._containerWidth, b = c._containerHeight; + } + }, 10); + }; + b.prototype._getRatio = function() { + var a = window.devicePixelRatio || 1, b = 1; + 1 !== a && (b = a / 1); + return b; + }; + b.prototype._onContainerSizeChanged = function() { + var a = this._getRatio(), b = Math.ceil(this._containerWidth * a), c = Math.ceil(this._containerHeight * a), e = this._canvas; + 0 < a ? (e.width = b * a, e.height = c * a, e.style.width = b + "px", e.style.height = c + "px") : (e.width = b, e.height = c); + }; + Object.defineProperty(b.prototype, "_containerWidth", {get:function() { + return this._container.clientWidth; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(b.prototype, "_containerHeight", {get:function() { + return this._container.clientHeight; + }, enumerable:!0, configurable:!0}); + b.prototype.render = function() { + var a = this._context; a.save(); - a.clearRect(0, 0, this._stage.w, this._stage.h); + a.clearRect(0, 0, this._canvas.width, this._canvas.height); a.scale(1, 1); - 0 === this._options.layout && this._renderFrameSimple(this.context, this._stage, e.createIdentity(), this._viewport); + 0 === this._options.layout && this._renderNodeSimple(this._context, this._stage, e.createIdentity()); a.restore(); }; - c.clearContext = function(a, c) { - a.clearRect(c.x, c.y, c.w, c.h); - }; - c.prototype._renderFrameSimple = function(a, c, d, e) { - function k(c) { - var d = c instanceof m.FrameContainer; - a.fillStyle = c._hasFlags(512) ? "red" : c._hasFlags(64) ? "blue" : "white"; - var e = d ? 2 : 6; - a.fillRect(b, h, e, 2); - if (d) { - b += e + 2; - n = Math.max(n, b + 6); - c = c._children; + b.prototype._renderNodeSimple = function(a, b, c) { + function e(b) { + var c = b.getChildren(); + a.fillStyle = b.hasFlags(16) ? "red" : "white"; + var d = String(b.id); + b instanceof m.RenderableText ? d = "T" + d : b instanceof m.RenderableShape ? d = "S" + d : b instanceof m.RenderableBitmap ? d = "B" + d : b instanceof m.RenderableVideo && (d = "V" + d); + b instanceof m.Renderable && (d = d + " [" + b._parents.length + "]"); + b = a.measureText(d).width; + a.fillText(d, k, t); + if (c) { + k += b + 4; + u = Math.max(u, k + 20); for (d = 0;d < c.length;d++) { - k(c[d]), d < c.length - 1 && (h += 3, h > r._canvas.height && (a.fillStyle = "gray", a.fillRect(n + 4, 0, 2, r._canvas.height), b = b - s + n + 8, s = n + 8, h = 0, a.fillStyle = "white")); + e(c[d]), d < c.length - 1 && (t += 18, t > g._canvas.height && (a.fillStyle = "gray", k = k - r + u + 8, r = u + 8, t = 0, a.fillStyle = "white")); } - b -= e + 2; + k -= b + 4; } } - var r = this; + var g = this; a.save(); + a.font = "16px Arial"; a.fillStyle = "white"; - var b = 0, h = 0, s = 0, n = 0; - k(c); + var k = 0, t = 0, r = 0, u = 0; + e(b); a.restore(); }; - return c; - }(m.StageRenderer); - m.TreeStageRenderer = k; - })(d.GFX || (d.GFX = {})); + return b; + }(m.Renderer); + m.TreeRenderer = w; + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - (function(m) { - var e = d.GFX.Shape, t = d.GFX.RenderableShape, k = d.GFX.RenderableBitmap, a = d.GFX.RenderableVideo, c = d.GFX.RenderableText, g = d.GFX.ColorMatrix, p = d.GFX.FrameContainer, v = d.GFX.ClipRectangle, u = d.ShapeData, l = d.ArrayUtilities.DataBuffer, r = d.GFX.Geometry.Matrix, b = d.GFX.Geometry.Rectangle, h = d.Debug.assert, w = function() { + (function(e) { + var c = g.GFX.BlurFilter, w = g.GFX.DropshadowFilter, k = g.GFX.Shape, b = g.GFX.Group, a = g.GFX.RenderableShape, n = g.GFX.RenderableMorphShape, p = g.GFX.RenderableBitmap, y = g.GFX.RenderableVideo, v = g.GFX.RenderableText, l = g.GFX.ColorMatrix, t = g.ShapeData, r = g.ArrayUtilities.DataBuffer, u = g.GFX.Stage, h = g.GFX.Geometry.Matrix, f = g.GFX.Geometry.Rectangle, d = g.Debug.assert, q = function() { function a() { } a.prototype.writeMouseEvent = function(a, b) { var c = this.output; c.writeInt(300); - var e = d.Remoting.MouseEventNames.indexOf(a.type); - c.writeInt(e); + var d = g.Remoting.MouseEventNames.indexOf(a.type); + c.writeInt(d); c.writeFloat(b.x); c.writeFloat(b.y); c.writeInt(a.buttons); @@ -10650,7 +11885,7 @@ a.prototype.writeKeyboardEvent = function(a) { var b = this.output; b.writeInt(301); - var c = d.Remoting.KeyboardEventNames.indexOf(a.type); + var c = g.Remoting.KeyboardEventNames.indexOf(a.type); b.writeInt(c); b.writeInt(a.keyCode); b.writeInt(a.charCode); @@ -10662,28 +11897,26 @@ b.writeInt(302); b.writeInt(a); }; - a.prototype.writeDecodeImageResponse = function(a, b, c, d, e) { - var g = this.output; - g.writeInt(108); - g.writeInt(a); - g.writeInt(b); - this._writeAsset(c); - g.writeInt(d); - g.writeInt(e); - }; - a.prototype._writeAsset = function(a) { - this.output.writeInt(this.outputAssets.length); - this.outputAssets.push(a); - }; return a; }(); - m.GFXChannelSerializer = w; - w = function() { + e.GFXChannelSerializer = q; + q = function() { function a(b, c, d) { - this.root = new v(128, 128); - d && this.root._setFlags(32768); - c.addChild(this.root); - this._frames = []; + function e(a) { + a = a.getBounds(!0); + var c = b.easel.getRatio(); + a.scale(1 / c, 1 / c); + a.snap(); + f.setBounds(a); + } + var f = this.stage = new u(128, 512); + "undefined" !== typeof registerInspectorStage && registerInspectorStage(f); + e(b.stage); + b.stage.addEventListener(1, e); + b.content = f.content; + d && this.stage.setFlags(32768); + c.addChild(this.stage); + this._nodes = []; this._assets = []; this._easelHost = b; this._canvas = document.createElement("canvas"); @@ -10691,15 +11924,16 @@ } a.prototype._registerAsset = function(a, b, c) { "undefined" !== typeof registerInspectorAsset && registerInspectorAsset(a, b, c); + this._assets[a] && console.warn("Asset already exists: " + a + ". old:", this._assets[a], "new: " + c); this._assets[a] = c; }; - a.prototype._makeFrame = function(a) { + a.prototype._makeNode = function(a) { if (-1 === a) { return null; } var b = null; - a & 134217728 ? (a &= -134217729, b = new e(this._assets[a]), this._assets[a].addFrameReferrer(b)) : b = this._frames[a]; - h(b, "Frame " + b + " of " + a + " has not been sent yet."); + a & 134217728 ? (a &= -134217729, b = this._assets[a].wrap()) : b = this._nodes[a]; + d(b, "Node " + b + " of " + a + " has not been sent yet."); return b; }; a.prototype._getAsset = function(a) { @@ -10714,27 +11948,41 @@ a.prototype._getTextAsset = function(a) { return this._assets[a]; }; - a.prototype._decodeImage = function(a, b) { - var c = new Image, d = this; - c.src = URL.createObjectURL(new Blob([a])); - c.onload = function() { - d._canvas.width = c.width; - d._canvas.height = c.height; - d._context.drawImage(c, 0, 0); - b(d._context.getImageData(0, 0, c.width, c.height)); + a.prototype.registerFont = function(a, b, c) { + g.registerCSSFont(a, b.data, !inFirefox); + inFirefox ? c(null) : window.setTimeout(c, 400); + }; + a.prototype.registerImage = function(a, b, c, d) { + this._registerAsset(a, b, this._decodeImage(c.dataType, c.data, d)); + }; + a.prototype.registerVideo = function(a) { + this._registerAsset(a, 0, new y(a, this)); + }; + a.prototype._decodeImage = function(a, b, c) { + var e = new Image, h = p.FromImage(e); + e.src = URL.createObjectURL(new Blob([b], {type:g.getMIMETypeForImageType(a)})); + e.onload = function() { + d(!h.parent); + h.setBounds(new f(0, 0, e.width, e.height)); + h.invalidate(); + c({width:e.width, height:e.height}); }; - c.onerror = function() { - b(null); + e.onerror = function() { + c(null); }; + return h; + }; + a.prototype.sendVideoPlaybackEvent = function(a, b, c) { + this._easelHost.sendVideoPlaybackEvent(a, b, c); }; return a; }(); - m.GFXChannelDeserializerContext = w; - w = function() { + e.GFXChannelDeserializerContext = q; + q = function() { function e() { } e.prototype.read = function() { - for (var a = 0, b = this.input, c = 0, d = 0, e = 0, g = 0, k = 0, l = 0, m = 0, p = 0, n = 0;0 < b.bytesAvailable;) { + for (var a = 0, b = this.input, c = 0, e = 0, f = 0, g = 0, h = 0, k = 0, l = 0, p = 0;0 < b.bytesAvailable;) { switch(a = b.readInt(), a) { case 0: return; @@ -10743,11 +11991,11 @@ this._readUpdateGraphics(); break; case 102: - d++; + e++; this._readUpdateBitmapData(); break; case 103: - e++; + f++; this._readUpdateTextContent(); break; case 100: @@ -10755,31 +12003,23 @@ this._readUpdateFrame(); break; case 104: - k++; + h++; this._readUpdateStage(); break; case 105: - l++; + k++; this._readUpdateNetStream(); break; case 200: - m++; - this._readRegisterFont(); - break; - case 201: - p++; + l++; this._readDrawToBitmap(); break; case 106: p++; this._readRequestBitmapData(); break; - case 107: - n++; - this._readDecodeImage(); - break; default: - h(!1, "Unknown MessageReader tag: " + a); + d(!1, "Unknown MessageReader tag: " + a); } } }; @@ -10794,17 +12034,17 @@ return b; }; e.prototype._readColorMatrix = function() { - var a = this.input, b = e._temporaryReadColorMatrix, c = 1, d = 1, g = 1, h = 1, k = 0, l = 0, m = 0, p = 0; + var a = this.input, b = e._temporaryReadColorMatrix, c = 1, d = 1, f = 1, g = 1, h = 0, k = 0, l = 0, p = 0; switch(a.readInt()) { case 0: return e._temporaryReadColorMatrixIdentity; case 1: - h = a.readFloat(); + g = a.readFloat(); break; case 2: - c = a.readFloat(), d = a.readFloat(), g = a.readFloat(), h = a.readFloat(), k = a.readInt(), l = a.readInt(), m = a.readInt(), p = a.readInt(); + c = a.readFloat(), d = a.readFloat(), f = a.readFloat(), g = a.readFloat(), h = a.readInt(), k = a.readInt(), l = a.readInt(), p = a.readInt(); } - b.setMultipliersAndOffsets(c, d, g, h, k, l, m, p); + b.setMultipliersAndOffsets(c, d, f, g, h, k, l, p); return b; }; e.prototype._readAsset = function() { @@ -10813,36 +12053,36 @@ return b; }; e.prototype._readUpdateGraphics = function() { - for (var a = this.input, b = this.context, c = a.readInt(), d = a.readInt(), e = b._getAsset(c), g = this._readRectangle(), h = u.FromPlainObject(this._readAsset()), k = a.readInt(), l = [], m = 0;m < k;m++) { - var p = a.readInt(); - l.push(b._getBitmapAsset(p)); + for (var b = this.input, c = this.context, d = b.readInt(), e = b.readInt(), f = c._getAsset(d), g = this._readRectangle(), h = t.FromPlainObject(this._readAsset()), k = b.readInt(), l = [], p = 0;p < k;p++) { + var m = b.readInt(); + l.push(c._getBitmapAsset(m)); } - if (e) { - e.update(h, l, g); + if (f) { + f.update(h, l, g); } else { - a = new t(c, h, l, g); - for (m = 0;m < l.length;m++) { - l[m].addRenderableReferrer(a); + b = h.morphCoordinates ? new n(d, h, l, g) : new a(d, h, l, g); + for (p = 0;p < l.length;p++) { + l[p] && l[p].addRenderableParent(b); } - b._registerAsset(c, d, a); + c._registerAsset(d, e, b); } }; e.prototype._readUpdateBitmapData = function() { - var a = this.input, b = this.context, c = a.readInt(), d = a.readInt(), e = b._getBitmapAsset(c), g = this._readRectangle(), a = a.readInt(), h = l.FromPlainObject(this._readAsset()); - e ? e.updateFromDataBuffer(a, h) : (e = k.FromDataBuffer(a, h, g), b._registerAsset(c, d, e)); + var a = this.input, b = this.context, c = a.readInt(), d = a.readInt(), e = b._getBitmapAsset(c), f = this._readRectangle(), a = a.readInt(), g = r.FromPlainObject(this._readAsset()); + e ? e.updateFromDataBuffer(a, g) : (e = p.FromDataBuffer(a, g, f), b._registerAsset(c, d, e)); }; e.prototype._readUpdateTextContent = function() { - var a = this.input, b = this.context, d = a.readInt(), e = a.readInt(), g = b._getTextAsset(d), h = this._readRectangle(), k = this._readMatrix(), m = a.readInt(), p = a.readInt(), n = a.readInt(), r = a.readBoolean(), s = this._readAsset(), t = l.FromPlainObject(this._readAsset()), u = null, v = a.readInt(); - v && (u = new l(4 * v), a.readBytes(u, 4 * v)); - g ? (g.setBounds(h), g.setContent(s, t, k, u), g.setStyle(m, p), g.reflow(n, r)) : (g = new c(h), g.setContent(s, t, k, u), g.setStyle(m, p), g.reflow(n, r), b._registerAsset(d, e, g)); + var a = this.input, b = this.context, c = a.readInt(), d = a.readInt(), e = b._getTextAsset(c), f = this._readRectangle(), g = this._readMatrix(), h = a.readInt(), k = a.readInt(), l = a.readInt(), p = a.readBoolean(), n = a.readInt(), m = a.readInt(), q = this._readAsset(), t = r.FromPlainObject(this._readAsset()), u = null, w = a.readInt(); + w && (u = new r(4 * w), a.readBytes(u, 4 * w)); + e ? (e.setBounds(f), e.setContent(q, t, g, u), e.setStyle(h, k, n, m), e.reflow(l, p)) : (e = new v(f), e.setContent(q, t, g, u), e.setStyle(h, k, n, m), e.reflow(l, p), b._registerAsset(c, d, e)); if (this.output) { - for (a = g.textRect, this.output.writeInt(20 * a.w), this.output.writeInt(20 * a.h), this.output.writeInt(20 * a.x), g = g.lines, a = g.length, this.output.writeInt(a), b = 0;b < a;b++) { - this._writeLineMetrics(g[b]); + for (a = e.textRect, this.output.writeInt(20 * a.w), this.output.writeInt(20 * a.h), this.output.writeInt(20 * a.x), e = e.lines, a = e.length, this.output.writeInt(a), b = 0;b < a;b++) { + this._writeLineMetrics(e[b]); } } }; e.prototype._writeLineMetrics = function(a) { - h(this.output); + d(this.output); this.output.writeInt(a.x); this.output.writeInt(a.width); this.output.writeInt(a.ascent); @@ -10851,183 +12091,239 @@ }; e.prototype._readUpdateStage = function() { var a = this.context, b = this.input.readInt(); - a._frames[b] || (a._frames[b] = a.root); + a._nodes[b] || (a._nodes[b] = a.stage.content); var b = this.input.readInt(), c = this._readRectangle(); - a.root.setBounds(c); - a.root.color = d.Color.FromARGB(b); + a.stage.content.setBounds(c); + a.stage.color = g.Color.FromARGB(b); + a.stage.align = this.input.readInt(); + a.stage.scaleMode = this.input.readInt(); + b = this.input.readInt(); + this.input.readInt(); + c = this.input.readInt(); + a._easelHost.cursor = g.UI.toCSSCursor(c); + a._easelHost.fullscreen = 0 === b || 1 === b; }; e.prototype._readUpdateNetStream = function() { - var c = this.context, d = this.input.readInt(), e = c._getVideoAsset(d), g = this.input.readUTF(); - e || (e = new a(g, new b(0, 0, 960, 480)), c._registerAsset(d, 0, e)); + var a = this.context, b = this.input.readInt(), c = a._getVideoAsset(b), d = this._readRectangle(); + c || (a.registerVideo(b), c = a._getVideoAsset(b)); + c.setBounds(d); }; - e.prototype._readUpdateFrame = function() { - var a = this.input, b = this.context, c = a.readInt(), d = b._frames[c]; - d || (d = b._frames[c] = new p); - var e = a.readInt(); - e & 1 && (d.matrix = this._readMatrix()); - e & 8 && (d.colorMatrix = this._readColorMatrix()); - e & 64 && (d.mask = b._makeFrame(a.readInt())); - e & 128 && (d.clip = a.readInt()); - e & 32 && (d.blendMode = a.readInt(), d._toggleFlags(16384, a.readBoolean()), d.pixelSnapping = a.readInt(), d.smoothing = a.readInt()); - if (e & 4) { - e = a.readInt(); - d.clearChildren(); - for (var g = 0;g < e;g++) { - var k = a.readInt(), l = b._makeFrame(k); - h(l, "Child " + k + " of " + c + " has not been sent yet."); - d.addChild(l); + e.prototype._readFilters = function(a) { + var b = this.input, d = b.readInt(), e = []; + if (d) { + for (var f = 0;f < d;f++) { + var h = b.readInt(); + switch(h) { + case 0: + e.push(new c(b.readFloat(), b.readFloat(), b.readInt())); + break; + case 1: + e.push(new w(b.readFloat(), b.readFloat(), b.readFloat(), b.readFloat(), b.readInt(), b.readFloat(), b.readBoolean(), b.readBoolean(), b.readBoolean(), b.readInt(), b.readFloat())); + break; + default: + g.Debug.somewhatImplemented(m.FilterType[h]); + } } + a.getLayer().filters = e; } }; - e.prototype._readRegisterFont = function() { - var a = this.input, b = a.readInt(); - a.readBoolean(); - a.readBoolean(); - var a = this._readAsset(), c = document.head; - c.insertBefore(document.createElement("style"), c.firstChild); - c = document.styleSheets[0]; - c.insertRule("@font-face{font-family:swffont" + b + ";src:url(data:font/opentype;base64," + d.StringUtilities.base64ArrayBuffer(a.buffer) + ")}", c.cssRules.length); + e.prototype._readUpdateFrame = function() { + var a = this.input, c = this.context, e = a.readInt(), f = 0, g = c._nodes[e]; + g || (g = c._nodes[e] = new b); + var h = a.readInt(); + h & 1 && g.getTransform().setMatrix(this._readMatrix()); + h & 8 && g.getTransform().setColorMatrix(this._readColorMatrix()); + if (h & 64) { + var l = a.readInt(); + 0 <= l && (g.getLayer().mask = c._makeNode(l)); + } + h & 128 && (g.clip = a.readInt()); + h & 32 && (f = a.readInt() / 65535, d(0 <= f && 1 >= f), l = a.readInt(), 1 !== l && (g.getLayer().blendMode = l), this._readFilters(g), g.toggleFlags(65536, a.readBoolean()), g.toggleFlags(131072, a.readBoolean()), g.toggleFlags(262144, !!a.readInt()), g.toggleFlags(524288, !!a.readInt())); + if (h & 4) { + h = a.readInt(); + l = g; + l.clearChildren(); + for (var p = 0;p < h;p++) { + var n = a.readInt(), m = c._makeNode(n); + d(m, "Child " + n + " of " + e + " has not been sent yet."); + l.addChild(m); + } + } + f && (m = g.getChildren()[0], m instanceof k && (m.ratio = f)); }; e.prototype._readDrawToBitmap = function() { - var a = this.input, b = this.context, c = a.readInt(), d = a.readInt(), e = a.readInt(), g, h, l; - g = e & 1 ? this._readMatrix().clone() : r.createIdentity(); - e & 8 && (h = this._readColorMatrix()); - e & 16 && (l = this._readRectangle()); + var a = this.input, b = this.context, c = a.readInt(), d = a.readInt(), e = a.readInt(), f, g, k; + f = e & 1 ? this._readMatrix().clone() : h.createIdentity(); + e & 8 && (g = this._readColorMatrix()); + e & 16 && (k = this._readRectangle()); e = a.readInt(); a.readBoolean(); a = b._getBitmapAsset(c); - d = b._makeFrame(d); - a ? a.drawFrame(d, g, h, e, l) : b._registerAsset(c, -1, k.FromFrame(d, g, h, e, l)); + d = b._makeNode(d); + a ? a.drawNode(d, f, g, e, k) : b._registerAsset(c, -1, p.FromNode(d, f, g, e, k)); }; e.prototype._readRequestBitmapData = function() { var a = this.output, b = this.context, c = this.input.readInt(); b._getBitmapAsset(c).readImageData(a); }; - e.prototype._readDecodeImage = function() { - var a = this.input, b = a.readInt(); - a.readInt(); - var a = this._readAsset(), c = this; - this.context._decodeImage(a, function(a) { - var e = new l, f = new d.Remoting.GFX.GFXChannelSerializer, g = []; - f.output = e; - f.outputAssets = g; - a ? f.writeDecodeImageResponse(b, 3, a.data, a.width, a.height) : f.writeDecodeImageResponse(b, 0, null, 0, 0); - c.context._easelHost.onSendUpdates(e, g); - }); - }; - e._temporaryReadMatrix = r.createIdentity(); - e._temporaryReadRectangle = b.createEmpty(); - e._temporaryReadColorMatrix = g.createIdentity(); - e._temporaryReadColorMatrixIdentity = g.createIdentity(); + e._temporaryReadMatrix = h.createIdentity(); + e._temporaryReadRectangle = f.createEmpty(); + e._temporaryReadColorMatrix = l.createIdentity(); + e._temporaryReadColorMatrixIdentity = l.createIdentity(); return e; }(); - m.GFXChannelDeserializer = w; + e.GFXChannelDeserializer = q; })(m.GFX || (m.GFX = {})); - })(d.Remoting || (d.Remoting = {})); + })(g.Remoting || (g.Remoting = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - var s = d.GFX.Geometry.Point, e = d.ArrayUtilities.DataBuffer, t = function() { - function k(a) { - this._easel = a; - var c = a.transparent; - this._frameContainer = a.world; - this._context = new d.Remoting.GFX.GFXChannelDeserializerContext(this, this._frameContainer, c); + var e = g.GFX.Geometry.Point, c = g.ArrayUtilities.DataBuffer, w = function() { + function k(b) { + this._easel = b; + var a = b.transparent; + this._group = b.world; + this._content = null; + this._fullscreen = !1; + this._context = new g.Remoting.GFX.GFXChannelDeserializerContext(this, this._group, a); this._addEventListeners(); } - k.prototype.onSendUpdates = function(a, c) { + k.prototype.onSendUpdates = function(b, a) { throw Error("This method is abstract"); }; + Object.defineProperty(k.prototype, "easel", {get:function() { + return this._easel; + }, enumerable:!0, configurable:!0}); Object.defineProperty(k.prototype, "stage", {get:function() { return this._easel.stage; }, enumerable:!0, configurable:!0}); - k.prototype._mouseEventListener = function(a) { - var c = this._easel.getMouseWorldPosition(a), c = new s(c.x, c.y), g = new e, k = new d.Remoting.GFX.GFXChannelSerializer; - k.output = g; - k.writeMouseEvent(a, c); - this.onSendUpdates(g, []); - }; - k.prototype._keyboardEventListener = function(a) { - var c = new e, g = new d.Remoting.GFX.GFXChannelSerializer; - g.output = c; - g.writeKeyboardEvent(a); - this.onSendUpdates(c, []); + Object.defineProperty(k.prototype, "content", {set:function(b) { + this._content = b; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "cursor", {set:function(b) { + this._easel.cursor = b; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "fullscreen", {set:function(b) { + if (this._fullscreen !== b) { + this._fullscreen = b; + var a = window.FirefoxCom; + a && a.request("setFullscreen", b, null); + } + }, enumerable:!0, configurable:!0}); + k.prototype._mouseEventListener = function(b) { + var a = this._easel.getMousePosition(b, this._content), a = new e(a.x, a.y), k = new c, p = new g.Remoting.GFX.GFXChannelSerializer; + p.output = k; + p.writeMouseEvent(b, a); + this.onSendUpdates(k, []); + }; + k.prototype._keyboardEventListener = function(b) { + var a = new c, e = new g.Remoting.GFX.GFXChannelSerializer; + e.output = a; + e.writeKeyboardEvent(b); + this.onSendUpdates(a, []); }; k.prototype._addEventListeners = function() { - for (var a = this._mouseEventListener.bind(this), c = this._keyboardEventListener.bind(this), d = k._mouseEvents, e = 0;e < d.length;e++) { - window.addEventListener(d[e], a); + for (var b = this._mouseEventListener.bind(this), a = this._keyboardEventListener.bind(this), c = k._mouseEvents, e = 0;e < c.length;e++) { + window.addEventListener(c[e], b); } - a = k._keyboardEvents; - for (e = 0;e < a.length;e++) { - window.addEventListener(a[e], c); + b = k._keyboardEvents; + for (e = 0;e < b.length;e++) { + window.addEventListener(b[e], a); } this._addFocusEventListeners(); + this._easel.addEventListener("resize", this._resizeEventListener.bind(this)); }; - k.prototype._sendFocusEvent = function(a) { - var c = new e, g = new d.Remoting.GFX.GFXChannelSerializer; - g.output = c; - g.writeFocusEvent(a); - this.onSendUpdates(c, []); + k.prototype._sendFocusEvent = function(b) { + var a = new c, e = new g.Remoting.GFX.GFXChannelSerializer; + e.output = a; + e.writeFocusEvent(b); + this.onSendUpdates(a, []); }; k.prototype._addFocusEventListeners = function() { - var a = this; - document.addEventListener("visibilitychange", function(c) { - a._sendFocusEvent(document.hidden ? 0 : 1); + var b = this; + document.addEventListener("visibilitychange", function(a) { + b._sendFocusEvent(document.hidden ? 0 : 1); }); - window.addEventListener("focus", function(c) { - a._sendFocusEvent(3); + window.addEventListener("focus", function(a) { + b._sendFocusEvent(3); }); - window.addEventListener("blur", function(c) { - a._sendFocusEvent(2); + window.addEventListener("blur", function(a) { + b._sendFocusEvent(2); }); }; - k.prototype.processUpdates = function(a, c, e) { - "undefined" === typeof e && (e = null); - var k = new d.Remoting.GFX.GFXChannelDeserializer; - k.input = a; - k.inputAssets = c; - k.output = e; - k.context = this._context; - k.read(); - }; - k.prototype.processExternalCommand = function(a) { - if ("isEnabled" === a.action) { - a.result = !1; + k.prototype._resizeEventListener = function() { + this.onDisplayParameters(this._easel.getDisplayParameters()); + }; + k.prototype.onDisplayParameters = function(b) { + throw Error("This method is abstract"); + }; + k.prototype.processUpdates = function(b, a, c) { + void 0 === c && (c = null); + var e = new g.Remoting.GFX.GFXChannelDeserializer; + e.input = b; + e.inputAssets = a; + e.output = c; + e.context = this._context; + e.read(); + }; + k.prototype.processExternalCommand = function(b) { + if ("isEnabled" === b.action) { + b.result = !1; } else { throw Error("This command is not supported"); } }; - k.prototype.processFSCommand = function(a, c) { + k.prototype.processVideoControl = function(b, a, c) { + var e = this._context, g = e._getVideoAsset(b); + if (!g) { + if (1 != a) { + return; + } + e.registerVideo(b); + g = e._getVideoAsset(b); + } + return g.processControlRequest(a, c); + }; + k.prototype.processRegisterFontOrImage = function(b, a, c, e, k) { + "font" === c ? this._context.registerFont(b, e, k) : (g.Debug.assert("image" === c), this._context.registerImage(b, a, e, k)); + }; + k.prototype.processFSCommand = function(b, a) { }; k.prototype.processFrame = function() { }; - k.prototype.onExernalCallback = function(a) { + k.prototype.onExernalCallback = function(b) { throw Error("This method is abstract"); }; - k.prototype.sendExernalCallback = function(a, c) { - var d = {functionName:a, args:c}; - this.onExernalCallback(d); - if (d.error) { - throw Error(d.error); + k.prototype.sendExernalCallback = function(b, a) { + var c = {functionName:b, args:a}; + this.onExernalCallback(c); + if (c.error) { + throw Error(c.error); } - return d.result; + return c.result; + }; + k.prototype.onVideoPlaybackEvent = function(b, a, c) { + throw Error("This method is abstract"); }; - k._mouseEvents = d.Remoting.MouseEventNames; - k._keyboardEvents = d.Remoting.KeyboardEventNames; + k.prototype.sendVideoPlaybackEvent = function(b, a, c) { + this.onVideoPlaybackEvent(b, a, c); + }; + k._mouseEvents = g.Remoting.MouseEventNames; + k._keyboardEvents = g.Remoting.KeyboardEventNames; return k; }(); - m.EaselHost = t; - })(d.GFX || (d.GFX = {})); + m.EaselHost = w; + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - (function(s) { - var e = d.ArrayUtilities.DataBuffer, t = d.CircularBuffer, k = d.Tools.Profiler.TimelineBuffer, a = function(a) { - function d(e, g, k) { - a.call(this, e); + (function(e) { + var c = g.ArrayUtilities.DataBuffer, w = g.CircularBuffer, k = g.Tools.Profiler.TimelineBuffer, b = function(a) { + function b(c, e, g) { + a.call(this, c); this._timelineRequests = Object.create(null); - this._playerWindow = g; - this._window = k; + this._playerWindow = e; + this._window = g; this._window.addEventListener("message", function(a) { this.onWindowMessage(a.data); }.bind(this)); @@ -11035,107 +12331,130 @@ this.onWindowMessage(a.detail, !1); }.bind(this)); } - __extends(d, a); - d.prototype.onSendUpdates = function(a, c) { - var d = a.getBytes(); - this._playerWindow.postMessage({type:"gfx", updates:d, assets:c}, "*", [d.buffer]); - }; - d.prototype.onExernalCallback = function(a) { - var c = this._playerWindow.document.createEvent("CustomEvent"); - c.initCustomEvent("syncmessage", !1, !1, {type:"externalCallback", request:a}); - this._playerWindow.dispatchEvent(c); - }; - d.prototype.requestTimeline = function(a, c) { - return new Promise(function(d) { - this._timelineRequests[a] = d; - this._playerWindow.postMessage({type:"timeline", cmd:c, request:a}, "*"); + __extends(b, a); + b.prototype.onSendUpdates = function(a, b) { + var c = a.getBytes(); + this._playerWindow.postMessage({type:"gfx", updates:c, assets:b}, "*", [c.buffer]); + }; + b.prototype.onExernalCallback = function(a) { + var b = this._playerWindow.document.createEvent("CustomEvent"); + b.initCustomEvent("syncmessage", !1, !1, {type:"externalCallback", request:a}); + this._playerWindow.dispatchEvent(b); + }; + b.prototype.onDisplayParameters = function(a) { + this._playerWindow.postMessage({type:"displayParameters", params:a}, "*"); + }; + b.prototype.onVideoPlaybackEvent = function(a, b, c) { + var e = this._playerWindow.document.createEvent("CustomEvent"); + e.initCustomEvent("syncmessage", !1, !1, {type:"videoPlayback", id:a, eventType:b, data:c}); + this._playerWindow.dispatchEvent(e); + }; + b.prototype.requestTimeline = function(a, b) { + return new Promise(function(c) { + this._timelineRequests[a] = c; + this._playerWindow.postMessage({type:"timeline", cmd:b, request:a}, "*"); }.bind(this)); }; - d.prototype.onWindowMessage = function(a, c) { - "undefined" === typeof c && (c = !0); + b.prototype.onWindowMessage = function(a, b) { + void 0 === b && (b = !0); if ("object" === typeof a && null !== a) { if ("player" === a.type) { - var d = e.FromArrayBuffer(a.updates.buffer); - if (c) { - this.processUpdates(d, a.assets); + var e = c.FromArrayBuffer(a.updates.buffer); + if (b) { + this.processUpdates(e, a.assets); } else { - var g = new e; - this.processUpdates(d, a.assets, g); + var g = new c; + this.processUpdates(e, a.assets, g); a.result = g.toPlainObject(); } } else { - "frame" !== a.type && ("external" === a.type ? this.processExternalCommand(a.request) : "fscommand" !== a.type && "timelineResponse" === a.type && a.timeline && (a.timeline.__proto__ = k.prototype, a.timeline._marks.__proto__ = t.prototype, a.timeline._times.__proto__ = t.prototype, this._timelineRequests[a.request](a.timeline))); + "frame" !== a.type && ("external" === a.type ? this.processExternalCommand(a.request) : "videoControl" === a.type ? a.result = this.processVideoControl(a.id, a.eventType, a.data) : "registerFontOrImage" === a.type ? this.processRegisterFontOrImage(a.syncId, a.symbolId, a.assetType, a.data, a.resolve) : "fscommand" !== a.type && "timelineResponse" === a.type && a.timeline && (a.timeline.__proto__ = k.prototype, a.timeline._marks.__proto__ = w.prototype, a.timeline._times.__proto__ = + w.prototype, this._timelineRequests[a.request](a.timeline))); } } }; - return d; + return b; }(m.EaselHost); - s.WindowEaselHost = a; + e.WindowEaselHost = b; })(m.Window || (m.Window = {})); - })(d.GFX || (d.GFX = {})); + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); -(function(d) { +(function(g) { (function(m) { - (function(s) { - var e = d.ArrayUtilities.DataBuffer, t = function(k) { - function a(a) { - k.call(this, a); - this._worker = d.Player.Test.FakeSyncWorker.instance; + (function(e) { + var c = g.ArrayUtilities.DataBuffer, w = function(e) { + function b(a) { + e.call(this, a); + this._worker = g.Player.Test.FakeSyncWorker.instance; this._worker.addEventListener("message", this._onWorkerMessage.bind(this)); this._worker.addEventListener("syncmessage", this._onSyncWorkerMessage.bind(this)); } - __extends(a, k); - a.prototype.onSendUpdates = function(a, d) { - var e = a.getBytes(); - this._worker.postMessage({type:"gfx", updates:e, assets:d}, [e.buffer]); + __extends(b, e); + b.prototype.onSendUpdates = function(a, b) { + var c = a.getBytes(); + this._worker.postMessage({type:"gfx", updates:c, assets:b}, [c.buffer]); }; - a.prototype.onExernalCallback = function(a) { + b.prototype.onExernalCallback = function(a) { this._worker.postSyncMessage({type:"externalCallback", request:a}); }; - a.prototype.requestTimeline = function(a, e) { - var k; + b.prototype.onDisplayParameters = function(a) { + this._worker.postMessage({type:"displayParameters", params:a}); + }; + b.prototype.onVideoPlaybackEvent = function(a, b, c) { + this._worker.postMessage({type:"videoPlayback", id:a, eventType:b, data:c}); + }; + b.prototype.requestTimeline = function(a, b) { + var c; switch(a) { case "AVM2": - k = d.AVM2.timelineBuffer; + c = g.AVM2.timelineBuffer; break; case "Player": - k = d.Player.timelineBuffer; + c = g.Player.timelineBuffer; break; case "SWF": - k = d.SWF.timelineBuffer; + c = g.SWF.timelineBuffer; } - "clear" === e && k && k.reset(); - return Promise.resolve(k); + "clear" === b && c && c.reset(); + return Promise.resolve(c); }; - a.prototype._onWorkerMessage = function(a, d) { - "undefined" === typeof d && (d = !0); - var k = a.data; - if ("object" === typeof k && null !== k) { - switch(k.type) { + b.prototype._onWorkerMessage = function(a, b) { + void 0 === b && (b = !0); + var e = a.data; + if ("object" === typeof e && null !== e) { + switch(e.type) { case "player": - var m = e.FromArrayBuffer(k.updates.buffer); - if (d) { - this.processUpdates(m, k.assets); + var g = c.FromArrayBuffer(e.updates.buffer); + if (b) { + this.processUpdates(g, e.assets); } else { - var s = new e; - this.processUpdates(m, k.assets, s); - a.result = s.toPlainObject(); + var k = new c; + this.processUpdates(g, e.assets, k); + a.result = k.toPlainObject(); a.handled = !0; } break; case "external": - a.result = this.processExternalCommand(k.command), a.handled = !0; + a.result = this.processExternalCommand(e.command); + a.handled = !0; + break; + case "videoControl": + a.result = this.processVideoControl(e.id, e.eventType, e.data); + a.handled = !0; + break; + case "registerFontOrImage": + this.processRegisterFontOrImage(e.syncId, e.symbolId, e.assetType, e.data, e.resolve), a.handled = !0; } } }; - a.prototype._onSyncWorkerMessage = function(a) { + b.prototype._onSyncWorkerMessage = function(a) { return this._onWorkerMessage(a, !1); }; - return a; + return b; }(m.EaselHost); - s.TestEaselHost = t; + e.TestEaselHost = w; })(m.Test || (m.Test = {})); - })(d.GFX || (d.GFX = {})); + })(g.GFX || (g.GFX = {})); })(Shumway || (Shumway = {})); console.timeEnd("Load GFX Dependencies"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/shumway.parser.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/shumway.parser.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/shumway.parser.js 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/shumway.parser.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,8812 +0,0 @@ -console.time("Load Parser Dependencies"); -var jsGlobal = function() { - return this || (0,eval)("this"); -}(), inBrowser = "undefined" != typeof console; -jsGlobal.performance || (jsGlobal.performance = {}); -jsGlobal.performance.now || (jsGlobal.performance.now = "undefined" !== typeof dateNow ? dateNow : Date.now); -function log(f) { - for (var u = 0;u < arguments.length - 1;u++) { - } - jsGlobal.print.apply(jsGlobal, arguments); -} -function warn(f) { - for (var u = 0;u < arguments.length - 1;u++) { - } - inBrowser ? console.warn.apply(console, arguments) : jsGlobal.print(Shumway.IndentingWriter.RED + f + Shumway.IndentingWriter.ENDC); -} -var Shumway; -(function(f) { - function u(b) { - return(b | 0) === b; - } - function c(b) { - return "object" === typeof b || "function" === typeof b; - } - function t(b) { - return String(Number(b)) === b; - } - function m(b) { - var g = 0; - if ("number" === typeof b) { - return g = b | 0, b === g && 0 <= g ? !0 : b >>> 0 === b; - } - if ("string" !== typeof b) { - return!1; - } - var a = b.length; - if (0 === a) { - return!1; - } - if ("0" === b) { - return!0; - } - if (a > f.UINT32_CHAR_BUFFER_LENGTH) { - return!1; - } - var l = 0, g = b.charCodeAt(l++) - 48; - if (1 > g || 9 < g) { - return!1; - } - for (var s = 0, d = 0;l < a;) { - d = b.charCodeAt(l++) - 48; - if (0 > d || 9 < d) { - return!1; - } - s = g; - g = 10 * g + d; - } - return s < f.UINT32_MAX_DIV_10 || s === f.UINT32_MAX_DIV_10 && d <= f.UINT32_MAX_MOD_10 ? !0 : !1; - } - (function(b) { - b[b._0 = 48] = "_0"; - b[b._1 = 49] = "_1"; - b[b._2 = 50] = "_2"; - b[b._3 = 51] = "_3"; - b[b._4 = 52] = "_4"; - b[b._5 = 53] = "_5"; - b[b._6 = 54] = "_6"; - b[b._7 = 55] = "_7"; - b[b._8 = 56] = "_8"; - b[b._9 = 57] = "_9"; - })(f.CharacterCodes || (f.CharacterCodes = {})); - f.UINT32_CHAR_BUFFER_LENGTH = 10; - f.UINT32_MAX = 4294967295; - f.UINT32_MAX_DIV_10 = 429496729; - f.UINT32_MAX_MOD_10 = 5; - f.isString = function(b) { - return "string" === typeof b; - }; - f.isFunction = function(b) { - return "function" === typeof b; - }; - f.isNumber = function(b) { - return "number" === typeof b; - }; - f.isInteger = u; - f.isArray = function(b) { - return b instanceof Array; - }; - f.isNumberOrString = function(b) { - return "number" === typeof b || "string" === typeof b; - }; - f.isObject = c; - f.toNumber = function(b) { - return+b; - }; - f.isNumericString = t; - f.isNumeric = function(b) { - if ("number" === typeof b) { - return!0; - } - if ("string" === typeof b) { - var g = b.charCodeAt(0); - return 65 <= g && 90 >= g || 97 <= g && 122 >= g || 36 === g || 95 === g ? !1 : m(b) || t(b); - } - return!1; - }; - f.isIndex = m; - f.isNullOrUndefined = function(b) { - return void 0 == b; - }; - (function(b) { - b.backtrace = function() { - try { - throw Error(); - } catch (b) { - return b.stack ? b.stack.split("\n").slice(2).join("\n") : ""; - } - }; - b.error = function(g) { - inBrowser ? warn(g) : warn(g + "\n\nStack Trace:\n" + b.backtrace()); - throw Error(g); - }; - b.assert = function(g, l) { - "undefined" === typeof l && (l = "assertion failed"); - "" === g && (g = !0); - g || b.error(l.toString()); - }; - b.assertUnreachable = function(b) { - throw Error("Reached unreachable location " + Error().stack.split("\n")[1] + b); - }; - b.assertNotImplemented = function(g, l) { - g || b.error("notImplemented: " + l); - }; - b.warning = function(b) { - warn(b); - }; - b.notUsed = function(g) { - b.assert(!1, "Not Used " + g); - }; - b.notImplemented = function(g) { - log("release: false"); - b.assert(!1, "Not Implemented " + g); - }; - b.abstractMethod = function(g) { - b.assert(!1, "Abstract Method " + g); - }; - var g = {}; - b.somewhatImplemented = function(a) { - g[a] || (g[a] = !0, b.warning("somewhatImplemented: " + a)); - }; - b.unexpected = function(g) { - b.assert(!1, "Unexpected: " + g); - }; - b.untested = function(g) { - b.warning("Congratulations, you've found a code path for which we haven't found a test case. Please submit the test case: " + g); - }; - })(f.Debug || (f.Debug = {})); - var r = f.Debug; - f.getTicks = function() { - return performance.now(); - }; - (function(b) { - function g(b, g) { - for (var l = 0, a = b.length;l < a;l++) { - if (b[l] === g) { - return l; - } - } - b.push(g); - return b.length - 1; - } - var a = f.Debug.assert; - b.popManyInto = function(b, g, l) { - a(b.length >= g); - for (var d = g - 1;0 <= d;d--) { - l[d] = b.pop(); - } - l.length = g; - }; - b.popMany = function(b, g) { - a(b.length >= g); - var l = b.length - g, d = b.slice(l, this.length); - b.splice(l, g); - return d; - }; - b.popManyIntoVoid = function(b, g) { - a(b.length >= g); - b.length -= g; - }; - b.pushMany = function(b, g) { - for (var l = 0;l < g.length;l++) { - b.push(g[l]); - } - }; - b.top = function(b) { - return b.length && b[b.length - 1]; - }; - b.last = function(b) { - return b.length && b[b.length - 1]; - }; - b.peek = function(b) { - a(0 < b.length); - return b[b.length - 1]; - }; - b.indexOf = function(b, g) { - for (var l = 0, a = b.length;l < a;l++) { - if (b[l] === g) { - return l; - } - } - return-1; - }; - b.pushUnique = g; - b.unique = function(b) { - for (var l = [], a = 0;a < b.length;a++) { - g(l, b[a]); - } - return l; - }; - b.copyFrom = function(g, l) { - g.length = 0; - b.pushMany(g, l); - }; - b.ensureTypedArrayCapacity = function(b, g) { - if (b.length < g) { - var l = b; - b = new b.constructor(f.IntegerUtilities.nearestPowerOfTwo(g)); - b.set(l, 0); - } - return b; - }; - var l = function() { - function b(g) { - "undefined" === typeof g && (g = 16); - this._f32 = this._i32 = this._u16 = this._u8 = null; - this._offset = 0; - this.ensureCapacity(g); - } - b.prototype.reset = function() { - this._offset = 0; - }; - Object.defineProperty(b.prototype, "offset", {get:function() { - return this._offset; - }, enumerable:!0, configurable:!0}); - b.prototype.getIndex = function(b) { - a(1 === b || 2 === b || 4 === b || 8 === b || 16 === b); - b = this._offset / b; - a((b | 0) === b); - return b; - }; - b.prototype.ensureAdditionalCapacity = function(b) { - this.ensureCapacity(this._offset + b); - }; - b.prototype.ensureCapacity = function(b) { - if (!this._u8) { - this._u8 = new Uint8Array(b); - } else { - if (this._u8.length > b) { - return; - } - } - var g = 2 * this._u8.length; - g < b && (g = b); - b = new Uint8Array(g); - b.set(this._u8, 0); - this._u8 = b; - this._u16 = new Uint16Array(b.buffer); - this._i32 = new Int32Array(b.buffer); - this._f32 = new Float32Array(b.buffer); - }; - b.prototype.writeInt = function(b) { - a(0 === (this._offset & 3)); - this.ensureCapacity(this._offset + 4); - this.writeIntUnsafe(b); - }; - b.prototype.writeIntAt = function(b, g) { - a(0 <= g && g <= this._offset); - a(0 === (g & 3)); - this.ensureCapacity(g + 4); - this._i32[g >> 2] = b; - }; - b.prototype.writeIntUnsafe = function(b) { - this._i32[this._offset >> 2] = b; - this._offset += 4; - }; - b.prototype.writeFloat = function(b) { - a(0 === (this._offset & 3)); - this.ensureCapacity(this._offset + 4); - this.writeFloatUnsafe(b); - }; - b.prototype.writeFloatUnsafe = function(b) { - this._f32[this._offset >> 2] = b; - this._offset += 4; - }; - b.prototype.write4Floats = function(b, g, l, s) { - a(0 === (this._offset & 3)); - this.ensureCapacity(this._offset + 16); - this.write4FloatsUnsafe(b, g, l, s); - }; - b.prototype.write4FloatsUnsafe = function(b, g, l, s) { - var a = this._offset >> 2; - this._f32[a + 0] = b; - this._f32[a + 1] = g; - this._f32[a + 2] = l; - this._f32[a + 3] = s; - this._offset += 16; - }; - b.prototype.write6Floats = function(b, g, l, s, d, e) { - a(0 === (this._offset & 3)); - this.ensureCapacity(this._offset + 24); - this.write6FloatsUnsafe(b, g, l, s, d, e); - }; - b.prototype.write6FloatsUnsafe = function(b, g, l, s, a, d) { - var e = this._offset >> 2; - this._f32[e + 0] = b; - this._f32[e + 1] = g; - this._f32[e + 2] = l; - this._f32[e + 3] = s; - this._f32[e + 4] = a; - this._f32[e + 5] = d; - this._offset += 24; - }; - b.prototype.subF32View = function() { - return this._f32.subarray(0, this._offset >> 2); - }; - b.prototype.subI32View = function() { - return this._i32.subarray(0, this._offset >> 2); - }; - b.prototype.subU16View = function() { - return this._u16.subarray(0, this._offset >> 1); - }; - b.prototype.subU8View = function() { - return this._u8.subarray(0, this._offset); - }; - b.prototype.hashWords = function(b, g, l) { - g = this._i32; - for (var s = 0;s < l;s++) { - b = (31 * b | 0) + g[s] | 0; - } - return b; - }; - b.prototype.reserve = function(b) { - b = b + 3 & -4; - this.ensureCapacity(this._offset + b); - this._offset += b; - }; - return b; - }(); - b.ArrayWriter = l; - })(f.ArrayUtilities || (f.ArrayUtilities = {})); - var a = f.ArrayUtilities, d = function() { - function b(b) { - this._u8 = new Uint8Array(b); - this._u16 = new Uint16Array(b); - this._i32 = new Int32Array(b); - this._f32 = new Float32Array(b); - this._offset = 0; - } - Object.defineProperty(b.prototype, "offset", {get:function() { - return this._offset; - }, enumerable:!0, configurable:!0}); - b.prototype.isEmpty = function() { - return this._offset === this._u8.length; - }; - b.prototype.readInt = function() { - r.assert(0 === (this._offset & 3)); - r.assert(this._offset <= this._u8.length - 4); - var b = this._i32[this._offset >> 2]; - this._offset += 4; - return b; - }; - b.prototype.readFloat = function() { - r.assert(0 === (this._offset & 3)); - r.assert(this._offset <= this._u8.length - 4); - var b = this._f32[this._offset >> 2]; - this._offset += 4; - return b; - }; - return b; - }(); - f.ArrayReader = d; - (function(b) { - function g(b, g) { - return Object.prototype.hasOwnProperty.call(b, g); - } - function a(b, s) { - for (var d in s) { - g(s, d) && (b[d] = s[d]); - } - } - b.boxValue = function(b) { - return void 0 == b || c(b) ? b : Object(b); - }; - b.toKeyValueArray = function(b) { - var g = Object.prototype.hasOwnProperty, a = [], d; - for (d in b) { - g.call(b, d) && a.push([d, b[d]]); - } - return a; - }; - b.isPrototypeWriteable = function(b) { - return Object.getOwnPropertyDescriptor(b, "prototype").writable; - }; - b.hasOwnProperty = g; - b.propertyIsEnumerable = function(b, g) { - return Object.prototype.propertyIsEnumerable.call(b, g); - }; - b.getOwnPropertyDescriptor = function(b, g) { - return Object.getOwnPropertyDescriptor(b, g); - }; - b.hasOwnGetter = function(b, g) { - var a = Object.getOwnPropertyDescriptor(b, g); - return!(!a || !a.get); - }; - b.getOwnGetter = function(b, g) { - var a = Object.getOwnPropertyDescriptor(b, g); - return a ? a.get : null; - }; - b.hasOwnSetter = function(b, g) { - var a = Object.getOwnPropertyDescriptor(b, g); - return!(!a || !a.set); - }; - b.createObject = function(b) { - return Object.create(b); - }; - b.createEmptyObject = function() { - return Object.create(null); - }; - b.createMap = function() { - return Object.create(null); - }; - b.createArrayMap = function() { - return[]; - }; - b.defineReadOnlyProperty = function(b, g, a) { - Object.defineProperty(b, g, {value:a, writable:!1, configurable:!0, enumerable:!1}); - }; - b.getOwnPropertyDescriptors = function(g) { - for (var s = b.createMap(), a = Object.getOwnPropertyNames(g), d = 0;d < a.length;d++) { - s[a[d]] = Object.getOwnPropertyDescriptor(g, a[d]); - } - return s; - }; - b.cloneObject = function(b) { - var g = Object.create(Object.getPrototypeOf(b)); - a(g, b); - return g; - }; - b.copyProperties = function(b, g) { - for (var a in g) { - b[a] = g[a]; - } - }; - b.copyOwnProperties = a; - b.copyOwnPropertyDescriptors = function(b, a, d) { - "undefined" === typeof d && (d = !0); - for (var e in a) { - if (g(a, e)) { - var h = Object.getOwnPropertyDescriptor(a, e); - if (d || !g(b, e)) { - r.assert(h); - try { - Object.defineProperty(b, e, h); - } catch (c) { - } - } - } - } - }; - b.getLatestGetterOrSetterPropertyDescriptor = function(b, g) { - for (var a = {};b;) { - var d = Object.getOwnPropertyDescriptor(b, g); - d && (a.get = a.get || d.get, a.set = a.set || d.set); - if (a.get && a.set) { - break; - } - b = Object.getPrototypeOf(b); - } - return a; - }; - b.defineNonEnumerableGetterOrSetter = function(g, a, d, e) { - var h = b.getLatestGetterOrSetterPropertyDescriptor(g, a); - h.configurable = !0; - h.enumerable = !1; - e ? h.get = d : h.set = d; - Object.defineProperty(g, a, h); - }; - b.defineNonEnumerableGetter = function(b, g, a) { - Object.defineProperty(b, g, {get:a, configurable:!0, enumerable:!1}); - }; - b.defineNonEnumerableSetter = function(b, g, a) { - Object.defineProperty(b, g, {set:a, configurable:!0, enumerable:!1}); - }; - b.defineNonEnumerableProperty = function(b, g, a) { - Object.defineProperty(b, g, {value:a, writable:!0, configurable:!0, enumerable:!1}); - }; - b.defineNonEnumerableForwardingProperty = function(b, g, a) { - Object.defineProperty(b, g, {get:p.makeForwardingGetter(a), set:p.makeForwardingSetter(a), writable:!0, configurable:!0, enumerable:!1}); - }; - b.defineNewNonEnumerableProperty = function(g, a, d) { - r.assert(!Object.prototype.hasOwnProperty.call(g, a), "Property: " + a + " already exits."); - b.defineNonEnumerableProperty(g, a, d); - }; - })(f.ObjectUtilities || (f.ObjectUtilities = {})); - (function(b) { - b.makeForwardingGetter = function(b) { - return new Function('return this["' + b + '"]'); - }; - b.makeForwardingSetter = function(b) { - return new Function("value", 'this["' + b + '"] = value;'); - }; - b.bindSafely = function(b, a) { - r.assert(!b.boundTo && a); - var d = b.bind(a); - d.boundTo = a; - return d; - }; - })(f.FunctionUtilities || (f.FunctionUtilities = {})); - var p = f.FunctionUtilities; - (function(b) { - function g(b) { - return "string" === typeof b ? '"' + b + '"' : "number" === typeof b || "boolean" === typeof b ? String(b) : b instanceof Array ? "[] " + b.length : typeof b; - } - var a = f.Debug.assert; - b.repeatString = function(b, g) { - for (var a = "", d = 0;d < g;d++) { - a += b; - } - return a; - }; - b.memorySizeToString = function(b) { - b |= 0; - return 1024 > b ? b + " B" : 1048576 > b ? (b / 1024).toFixed(2) + "KB" : (b / 1048576).toFixed(2) + "MB"; - }; - b.toSafeString = g; - b.toSafeArrayString = function(b) { - for (var a = [], d = 0;d < b.length;d++) { - a.push(g(b[d])); - } - return a.join(", "); - }; - b.utf8decode = function(b) { - for (var g = new Uint8Array(4 * b.length), a = 0, d = 0, s = b.length;d < s;d++) { - var l = b.charCodeAt(d); - if (127 >= l) { - g[a++] = l; - } else { - if (55296 <= l && 56319 >= l) { - var e = b.charCodeAt(d + 1); - 56320 <= e && 57343 >= e && (l = ((l & 1023) << 10) + (e & 1023) + 65536, ++d); - } - 0 !== (l & 4292870144) ? (g[a++] = 248 | l >>> 24 & 3, g[a++] = 128 | l >>> 18 & 63, g[a++] = 128 | l >>> 12 & 63, g[a++] = 128 | l >>> 6 & 63) : 0 !== (l & 4294901760) ? (g[a++] = 240 | l >>> 18 & 7, g[a++] = 128 | l >>> 12 & 63, g[a++] = 128 | l >>> 6 & 63) : 0 !== (l & 4294965248) ? (g[a++] = 224 | l >>> 12 & 15, g[a++] = 128 | l >>> 6 & 63) : g[a++] = 192 | l >>> 6 & 31; - g[a++] = 128 | l & 63; - } - } - return g.subarray(0, a); - }; - b.utf8encode = function(b) { - for (var g = 0, a = "";g < b.length;) { - var d = b[g++] & 255; - if (127 >= d) { - a += String.fromCharCode(d); - } else { - var s = 192, l = 5; - do { - if ((d & (s >> 1 | 128)) === s) { - break; - } - s = s >> 1 | 128; - --l; - } while (0 <= l); - if (0 >= l) { - a += String.fromCharCode(d); - } else { - for (var d = d & (1 << l) - 1, s = !1, e = 5;e >= l;--e) { - var h = b[g++]; - if (128 != (h & 192)) { - s = !0; - break; - } - d = d << 6 | h & 63; - } - if (s) { - for (l = g - (7 - e);l < g;++l) { - a += String.fromCharCode(b[l] & 255); - } - } else { - a = 65536 <= d ? a + String.fromCharCode(d - 65536 >> 10 & 1023 | 55296, d & 1023 | 56320) : a + String.fromCharCode(d); - } - } - } - } - return a; - }; - b.base64ArrayBuffer = function(b) { - var g = ""; - b = new Uint8Array(b); - for (var a = b.byteLength, d = a % 3, a = a - d, s, l, e, h, c = 0;c < a;c += 3) { - h = b[c] << 16 | b[c + 1] << 8 | b[c + 2], s = (h & 16515072) >> 18, l = (h & 258048) >> 12, e = (h & 4032) >> 6, h &= 63, g += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[s] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[e] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[h]; - } - 1 == d ? (h = b[a], g += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(h & 252) >> 2] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(h & 3) << 4] + "==") : 2 == d && (h = b[a] << 8 | b[a + 1], g += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(h & 64512) >> 10] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(h & 1008) >> 4] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(h & 15) << - 2] + "="); - return g; - }; - b.escapeString = function(b) { - void 0 !== b && (b = b.replace(/[^\w$]/gi, "$"), /^\d/.test(b) && (b = "$" + b)); - return b; - }; - b.fromCharCodeArray = function(b) { - for (var g = "", a = 0;a < b.length;a += 16384) { - var d = Math.min(b.length - a, 16384), g = g + String.fromCharCode.apply(null, b.subarray(a, a + d)) - } - return g; - }; - b.variableLengthEncodeInt32 = function(g) { - var d = 32 - Math.clz32(g); - a(32 >= d, d); - for (var s = Math.ceil(d / 6), l = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_"[s], e = s - 1;0 <= e;e--) { - l += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_"[g >> 6 * e & 63]; - } - a(b.variableLengthDecodeInt32(l) === g, g + " : " + l + " - " + s + " bits: " + d); - return l; - }; - b.toEncoding = function(b) { - return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_"[b]; - }; - b.fromEncoding = function(b) { - b = b.charCodeAt(0); - if (65 <= b && 90 >= b) { - return b - 65; - } - if (97 <= b && 122 >= b) { - return b - 71; - } - if (48 <= b && 57 >= b) { - return b + 4; - } - if (36 === b) { - return 62; - } - if (95 === b) { - return 63; - } - a(!1, "Invalid Encoding"); - }; - b.variableLengthDecodeInt32 = function(g) { - for (var a = b.fromEncoding(g[0]), d = 0, l = 0;l < a;l++) { - var s = 6 * (a - l - 1), d = d | b.fromEncoding(g[1 + l]) << s - } - return d; - }; - b.trimMiddle = function(b, g) { - if (b.length <= g) { - return b; - } - var a = g >> 1, d = g - a - 1; - return b.substr(0, a) + "\u2026" + b.substr(b.length - d, d); - }; - b.multiple = function(b, g) { - for (var a = "", d = 0;d < g;d++) { - a += b; - } - return a; - }; - b.indexOfAny = function(b, g, a) { - for (var d = b.length, l = 0;l < g.length;l++) { - var s = b.indexOf(g[l], a); - 0 <= s && (d = Math.min(d, s)); - } - return d === b.length ? -1 : d; - }; - var d = Array(3), s = Array(4), e = Array(5), h = Array(6), c = Array(7), k = Array(8), p = Array(9); - b.concat3 = function(b, g, a) { - d[0] = b; - d[1] = g; - d[2] = a; - return d.join(""); - }; - b.concat4 = function(b, g, a, d) { - s[0] = b; - s[1] = g; - s[2] = a; - s[3] = d; - return s.join(""); - }; - b.concat5 = function(b, g, a, d, l) { - e[0] = b; - e[1] = g; - e[2] = a; - e[3] = d; - e[4] = l; - return e.join(""); - }; - b.concat6 = function(b, g, a, d, l, s) { - h[0] = b; - h[1] = g; - h[2] = a; - h[3] = d; - h[4] = l; - h[5] = s; - return h.join(""); - }; - b.concat7 = function(b, g, a, d, l, s, e) { - c[0] = b; - c[1] = g; - c[2] = a; - c[3] = d; - c[4] = l; - c[5] = s; - c[6] = e; - return c.join(""); - }; - b.concat8 = function(b, g, a, d, l, s, e, h) { - k[0] = b; - k[1] = g; - k[2] = a; - k[3] = d; - k[4] = l; - k[5] = s; - k[6] = e; - k[7] = h; - return k.join(""); - }; - b.concat9 = function(b, g, a, d, l, s, e, h, c) { - p[0] = b; - p[1] = g; - p[2] = a; - p[3] = d; - p[4] = l; - p[5] = s; - p[6] = e; - p[7] = h; - p[8] = c; - return p.join(""); - }; - })(f.StringUtilities || (f.StringUtilities = {})); - (function(b) { - var g = new Uint8Array([7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]), a = new Int32Array([-680876936, -389564586, 606105819, -1044525330, -176418897, 1200080426, -1473231341, -45705983, 1770035416, -1958414417, -42063, -1990404162, 1804603682, -40341101, -1502002290, 1236535329, -165796510, -1069501632, - 643717713, -373897302, -701558691, 38016083, -660478335, -405537848, 568446438, -1019803690, -187363961, 1163531501, -1444681467, -51403784, 1735328473, -1926607734, -378558, -2022574463, 1839030562, -35309556, -1530992060, 1272893353, -155497632, -1094730640, 681279174, -358537222, -722521979, 76029189, -640364487, -421815835, 530742520, -995338651, -198630844, 1126891415, -1416354905, -57434055, 1700485571, -1894986606, -1051523, -2054922799, 1873313359, -30611744, -1560198380, 1309151649, - -145523070, -1120210379, 718787259, -343485551]); - b.hashBytesTo32BitsMD5 = function(b, d, e) { - var h = 1732584193, c = -271733879, k = -1732584194, p = 271733878, n = e + 72 & -64, q = new Uint8Array(n), z; - for (z = 0;z < e;++z) { - q[z] = b[d++]; - } - q[z++] = 128; - for (b = n - 8;z < b;) { - q[z++] = 0; - } - q[z++] = e << 3 & 255; - q[z++] = e >> 5 & 255; - q[z++] = e >> 13 & 255; - q[z++] = e >> 21 & 255; - q[z++] = e >>> 29 & 255; - q[z++] = 0; - q[z++] = 0; - q[z++] = 0; - b = new Int32Array(16); - for (z = 0;z < n;) { - for (e = 0;16 > e;++e, z += 4) { - b[e] = q[z] | q[z + 1] << 8 | q[z + 2] << 16 | q[z + 3] << 24; - } - var v = h; - d = c; - var x = k, t = p, r, m; - for (e = 0;64 > e;++e) { - 16 > e ? (r = d & x | ~d & t, m = e) : 32 > e ? (r = t & d | ~t & x, m = 5 * e + 1 & 15) : 48 > e ? (r = d ^ x ^ t, m = 3 * e + 5 & 15) : (r = x ^ (d | ~t), m = 7 * e & 15); - var f = t, v = v + r + a[e] + b[m] | 0; - r = g[e]; - t = x; - x = d; - d = d + (v << r | v >>> 32 - r) | 0; - v = f; - } - h = h + v | 0; - c = c + d | 0; - k = k + x | 0; - p = p + t | 0; - } - return h; - }; - b.hashBytesTo32BitsAdler = function(b, g, a) { - var d = 1, e = 0; - for (a = g + a;g < a;++g) { - d = (d + (b[g] & 255)) % 65521, e = (e + d) % 65521; - } - return e << 16 | d; - }; - })(f.HashUtilities || (f.HashUtilities = {})); - var e = function() { - function b() { - } - b.seed = function(g) { - b._state[0] = g; - b._state[1] = g; - }; - b.next = function() { - var b = this._state, a = Math.imul(18273, b[0] & 65535) + (b[0] >>> 16) | 0; - b[0] = a; - var d = Math.imul(36969, b[1] & 65535) + (b[1] >>> 16) | 0; - b[1] = d; - b = (a << 16) + (d & 65535) | 0; - return 2.3283064365386963E-10 * (0 > b ? b + 4294967296 : b); - }; - b._state = new Uint32Array([57005, 48879]); - return b; - }(); - f.Random = e; - Math.random = function() { - return e.next(); - }; - (function() { - function b() { - this.id = "$weakmap" + g++; - } - if ("function" !== typeof jsGlobal.WeakMap) { - var g = 0; - b.prototype = {has:function(b) { - return b.hasOwnProperty(this.id); - }, get:function(b, g) { - return b.hasOwnProperty(this.id) ? b[this.id] : g; - }, set:function(b, g) { - Object.defineProperty(b, this.id, {value:g, enumerable:!1, configurable:!0}); - }}; - jsGlobal.WeakMap = b; - } - })(); - d = function() { - function b() { - "undefined" !== typeof netscape && netscape.security.PrivilegeManager ? this._map = new WeakMap : this._list = []; - } - b.prototype.clear = function() { - this._map ? this._map.clear() : this._list.length = 0; - }; - b.prototype.push = function(b) { - this._map ? this._map.set(b, null) : this._list.push(b); - }; - b.prototype.forEach = function(b) { - if (this._map) { - "undefined" !== typeof netscape && netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"), Components.utils.nondeterministicGetWeakMapKeys(this._map).forEach(function(a) { - 0 !== a._referenceCount && b(a); - }); - } else { - for (var a = this._list, d = 0, s = 0;s < a.length;s++) { - var e = a[s]; - 0 === e._referenceCount ? d++ : b(e); - } - if (16 < d && d > a.length >> 2) { - d = []; - for (s = 0;s < a.length;s++) { - 0 < a[s]._referenceCount && d.push(a[s]); - } - this._list = d; - } - } - }; - Object.defineProperty(b.prototype, "length", {get:function() { - return this._map ? -1 : this._list.length; - }, enumerable:!0, configurable:!0}); - return b; - }(); - f.WeakList = d; - (function(b) { - b.pow2 = function(b) { - return b === (b | 0) ? 0 > b ? 1 / (1 << -b) : 1 << b : Math.pow(2, b); - }; - b.clamp = function(b, a, d) { - return Math.max(a, Math.min(d, b)); - }; - b.roundHalfEven = function(b) { - if (.5 === Math.abs(b % 1)) { - var a = Math.floor(b); - return 0 === a % 2 ? a : Math.ceil(b); - } - return Math.round(b); - }; - b.epsilonEquals = function(b, a) { - return 1E-7 > Math.abs(b - a); - }; - })(f.NumberUtilities || (f.NumberUtilities = {})); - (function(b) { - b[b.MaxU16 = 65535] = "MaxU16"; - b[b.MaxI16 = 32767] = "MaxI16"; - b[b.MinI16 = -32768] = "MinI16"; - })(f.Numbers || (f.Numbers = {})); - (function(b) { - function g(b) { - return 256 * b << 16 >> 16; - } - var a = new ArrayBuffer(8); - b.i8 = new Int8Array(a); - b.u8 = new Uint8Array(a); - b.i32 = new Int32Array(a); - b.f32 = new Float32Array(a); - b.f64 = new Float64Array(a); - b.nativeLittleEndian = 1 === (new Int8Array((new Int32Array([1])).buffer))[0]; - b.floatToInt32 = function(g) { - b.f32[0] = g; - return b.i32[0]; - }; - b.int32ToFloat = function(g) { - b.i32[0] = g; - return b.f32[0]; - }; - b.swap16 = function(b) { - return(b & 255) << 8 | b >> 8 & 255; - }; - b.swap32 = function(b) { - return(b & 255) << 24 | (b & 65280) << 8 | b >> 8 & 65280 | b >> 24 & 255; - }; - b.toS8U8 = g; - b.fromS8U8 = function(b) { - return b / 256; - }; - b.clampS8U8 = function(b) { - return g(b) / 256; - }; - b.toS16 = function(b) { - return b << 16 >> 16; - }; - b.bitCount = function(b) { - b -= b >> 1 & 1431655765; - b = (b & 858993459) + (b >> 2 & 858993459); - return 16843009 * (b + (b >> 4) & 252645135) >> 24; - }; - b.ones = function(b) { - b -= b >> 1 & 1431655765; - b = (b & 858993459) + (b >> 2 & 858993459); - return 16843009 * (b + (b >> 4) & 252645135) >> 24; - }; - b.trailingZeros = function(g) { - return b.ones((g & -g) - 1); - }; - b.getFlags = function(b, g) { - var a = ""; - for (b = 0;b < g.length;b++) { - b & 1 << b && (a += g[b] + " "); - } - return 0 === a.length ? "" : a.trim(); - }; - b.isPowerOfTwo = function(b) { - return b && 0 === (b & b - 1); - }; - b.roundToMultipleOfFour = function(b) { - return b + 3 & -4; - }; - b.nearestPowerOfTwo = function(b) { - b--; - b |= b >> 1; - b |= b >> 2; - b |= b >> 4; - b |= b >> 8; - b |= b >> 16; - b++; - return b; - }; - b.roundToMultipleOfPowerOfTwo = function(b, g) { - var a = (1 << g) - 1; - return b + a & ~a; - }; - Math.imul || (Math.imul = function(b, g) { - var a = b & 65535, d = g & 65535; - return a * d + ((b >>> 16 & 65535) * d + a * (g >>> 16 & 65535) << 16 >>> 0) | 0; - }); - Math.clz32 || (Math.clz32 = function(g) { - g |= g >> 1; - g |= g >> 2; - g |= g >> 4; - g |= g >> 8; - return 32 - b.ones(g | g >> 16); - }); - })(f.IntegerUtilities || (f.IntegerUtilities = {})); - var h = f.IntegerUtilities; - (function(b) { - function g(b, g, a, d, e, h) { - return(a - b) * (h - g) - (d - g) * (e - b); - } - b.pointInPolygon = function(b, g, a) { - for (var d = 0, e = a.length - 2, h = 0;h < e;h += 2) { - var c = a[h + 0], k = a[h + 1], p = a[h + 2], q = a[h + 3]; - (k <= g && q > g || k > g && q <= g) && b < c + (g - k) / (q - k) * (p - c) && d++; - } - return 1 === (d & 1); - }; - b.signedArea = g; - b.counterClockwise = function(b, a, d, e, h, c) { - return 0 < g(b, a, d, e, h, c); - }; - b.clockwise = function(b, a, d, e, h, c) { - return 0 > g(b, a, d, e, h, c); - }; - b.pointInPolygonInt32 = function(b, g, a) { - b |= 0; - g |= 0; - for (var d = 0, e = a.length - 2, h = 0;h < e;h += 2) { - var c = a[h + 0], k = a[h + 1], p = a[h + 2], q = a[h + 3]; - (k <= g && q > g || k > g && q <= g) && b < c + (g - k) / (q - k) * (p - c) && d++; - } - return 1 === (d & 1); - }; - })(f.GeometricUtilities || (f.GeometricUtilities = {})); - (function(b) { - b[b.Error = 1] = "Error"; - b[b.Warn = 2] = "Warn"; - b[b.Debug = 4] = "Debug"; - b[b.Log = 8] = "Log"; - b[b.Info = 16] = "Info"; - b[b.All = 31] = "All"; - })(f.LogLevel || (f.LogLevel = {})); - d = function() { - function b(g, a) { - "undefined" === typeof g && (g = !1); - this._tab = " "; - this._padding = ""; - this._suppressOutput = g; - this._out = a || b._consoleOut; - this._outNoNewline = a || b._consoleOutNoNewline; - } - b.prototype.write = function(b, a) { - "undefined" === typeof b && (b = ""); - "undefined" === typeof a && (a = !1); - this._suppressOutput || this._outNoNewline((a ? this._padding : "") + b); - }; - b.prototype.writeLn = function(b) { - "undefined" === typeof b && (b = ""); - this._suppressOutput || this._out(this._padding + b); - }; - b.prototype.writeTimeLn = function(b) { - "undefined" === typeof b && (b = ""); - this._suppressOutput || this._out(this._padding + performance.now().toFixed(2) + " " + b); - }; - b.prototype.writeComment = function(b) { - b = b.split("\n"); - if (1 === b.length) { - this.writeLn("// " + b[0]); - } else { - this.writeLn("/**"); - for (var a = 0;a < b.length;a++) { - this.writeLn(" * " + b[a]); - } - this.writeLn(" */"); - } - }; - b.prototype.writeLns = function(b) { - b = b.split("\n"); - for (var a = 0;a < b.length;a++) { - this.writeLn(b[a]); - } - }; - b.prototype.errorLn = function(g) { - b.logLevel & 1 && this.boldRedLn(g); - }; - b.prototype.warnLn = function(g) { - b.logLevel & 2 && this.yellowLn(g); - }; - b.prototype.debugLn = function(g) { - b.logLevel & 4 && this.purpleLn(g); - }; - b.prototype.logLn = function(g) { - b.logLevel & 8 && this.writeLn(g); - }; - b.prototype.infoLn = function(g) { - b.logLevel & 16 && this.writeLn(g); - }; - b.prototype.yellowLn = function(g) { - this.colorLn(b.YELLOW, g); - }; - b.prototype.greenLn = function(g) { - this.colorLn(b.GREEN, g); - }; - b.prototype.boldRedLn = function(g) { - this.colorLn(b.BOLD_RED, g); - }; - b.prototype.redLn = function(g) { - this.colorLn(b.RED, g); - }; - b.prototype.purpleLn = function(g) { - this.colorLn(b.PURPLE, g); - }; - b.prototype.colorLn = function(g, a) { - this._suppressOutput || (inBrowser ? this._out(this._padding + a) : this._out(this._padding + g + a + b.ENDC)); - }; - b.prototype.redLns = function(g) { - this.colorLns(b.RED, g); - }; - b.prototype.colorLns = function(b, a) { - for (var d = a.split("\n"), e = 0;e < d.length;e++) { - this.colorLn(b, d[e]); - } - }; - b.prototype.enter = function(b) { - this._suppressOutput || this._out(this._padding + b); - this.indent(); - }; - b.prototype.leaveAndEnter = function(b) { - this.leave(b); - this.indent(); - }; - b.prototype.leave = function(b) { - this.outdent(); - this._suppressOutput || this._out(this._padding + b); - }; - b.prototype.indent = function() { - this._padding += this._tab; - }; - b.prototype.outdent = function() { - 0 < this._padding.length && (this._padding = this._padding.substring(0, this._padding.length - this._tab.length)); - }; - b.prototype.writeArray = function(b, a, d) { - "undefined" === typeof a && (a = !1); - "undefined" === typeof d && (d = !1); - a = a || !1; - for (var e = 0, h = b.length;e < h;e++) { - var c = ""; - a && (c = null === b[e] ? "null" : void 0 === b[e] ? "undefined" : b[e].constructor.name, c += " "); - var k = d ? "" : ("" + e).padRight(" ", 4); - this.writeLn(k + c + b[e]); - } - }; - b.PURPLE = "\u001b[94m"; - b.YELLOW = "\u001b[93m"; - b.GREEN = "\u001b[92m"; - b.RED = "\u001b[91m"; - b.BOLD_RED = "\u001b[1;91m"; - b.ENDC = "\u001b[0m"; - b.logLevel = 31; - b._consoleOut = inBrowser ? console.info.bind(console) : print; - b._consoleOutNoNewline = inBrowser ? console.info.bind(console) : putstr; - return b; - }(); - f.IndentingWriter = d; - var n = function() { - return function(b, g) { - this.value = b; - this.next = g; - }; - }(), d = function() { - function b(b) { - r.assert(b); - this._compare = b; - this._head = null; - this._length = 0; - } - b.prototype.push = function(b) { - r.assert(void 0 !== b); - this._length++; - if (this._head) { - var a = this._head, d = null; - b = new n(b, null); - for (var e = this._compare;a;) { - if (0 < e(a.value, b.value)) { - d ? (b.next = a, d.next = b) : (b.next = this._head, this._head = b); - return; - } - d = a; - a = a.next; - } - d.next = b; - } else { - this._head = new n(b, null); - } - }; - b.prototype.forEach = function(g) { - for (var a = this._head, d = null;a;) { - var e = g(a.value); - if (e === b.RETURN) { - break; - } else { - e === b.DELETE ? a = d ? d.next = a.next : this._head = this._head.next : (d = a, a = a.next); - } - } - }; - b.prototype.isEmpty = function() { - return!this._head; - }; - b.prototype.pop = function() { - if (this._head) { - this._length--; - var b = this._head; - this._head = this._head.next; - return b.value; - } - }; - b.prototype.contains = function(b) { - for (var a = this._head;a;) { - if (a.value === b) { - return!0; - } - a = a.next; - } - return!1; - }; - b.prototype.toString = function() { - for (var b = "[", a = this._head;a;) { - b += a.value.toString(), (a = a.next) && (b += ","); - } - return b + "]"; - }; - b.RETURN = 1; - b.DELETE = 2; - return b; - }(); - f.SortedList = d; - d = function() { - function b(b, a) { - "undefined" === typeof a && (a = 12); - this.start = this.index = 0; - this._size = 1 << a; - this._mask = this._size - 1; - this.array = new b(this._size); - } - b.prototype.get = function(b) { - return this.array[b]; - }; - b.prototype.forEachInReverse = function(b) { - if (!this.isEmpty()) { - for (var a = 0 === this.index ? this._size - 1 : this.index - 1, d = this.start - 1 & this._mask;a !== d && !b(this.array[a], a);) { - a = 0 === a ? this._size - 1 : a - 1; - } - } - }; - b.prototype.write = function(b) { - this.array[this.index] = b; - this.index = this.index + 1 & this._mask; - this.index === this.start && (this.start = this.start + 1 & this._mask); - }; - b.prototype.isFull = function() { - return(this.index + 1 & this._mask) === this.start; - }; - b.prototype.isEmpty = function() { - return this.index === this.start; - }; - b.prototype.reset = function() { - this.start = this.index = 0; - }; - return b; - }(); - f.CircularBuffer = d; - (function(b) { - function g(g) { - return g + (b.BITS_PER_WORD - 1) >> b.ADDRESS_BITS_PER_WORD << b.ADDRESS_BITS_PER_WORD; - } - function a(b, g) { - b = b || "1"; - g = g || "0"; - for (var d = "", e = 0;e < length;e++) { - d += this.get(e) ? b : g; - } - return d; - } - function d(b) { - for (var g = [], a = 0;a < length;a++) { - this.get(a) && g.push(b ? b[a] : a); - } - return g.join(", "); - } - var e = f.Debug.assert; - b.ADDRESS_BITS_PER_WORD = 5; - b.BITS_PER_WORD = 1 << b.ADDRESS_BITS_PER_WORD; - b.BIT_INDEX_MASK = b.BITS_PER_WORD - 1; - var h = function() { - function a(d) { - this.size = g(d); - this.dirty = this.count = 0; - this.length = d; - this.bits = new Uint32Array(this.size >> b.ADDRESS_BITS_PER_WORD); - } - a.prototype.recount = function() { - if (this.dirty) { - for (var b = this.bits, g = 0, a = 0, d = b.length;a < d;a++) { - var e = b[a], e = e - (e >> 1 & 1431655765), e = (e & 858993459) + (e >> 2 & 858993459), g = g + (16843009 * (e + (e >> 4) & 252645135) >> 24) - } - this.count = g; - this.dirty = 0; - } - }; - a.prototype.set = function(g) { - var a = g >> b.ADDRESS_BITS_PER_WORD, d = this.bits[a]; - g = d | 1 << (g & b.BIT_INDEX_MASK); - this.bits[a] = g; - this.dirty |= d ^ g; - }; - a.prototype.setAll = function() { - for (var b = this.bits, g = 0, a = b.length;g < a;g++) { - b[g] = 4294967295; - } - this.count = this.size; - this.dirty = 0; - }; - a.prototype.assign = function(b) { - this.count = b.count; - this.dirty = b.dirty; - this.size = b.size; - for (var g = 0, a = this.bits.length;g < a;g++) { - this.bits[g] = b.bits[g]; - } - }; - a.prototype.clear = function(g) { - var a = g >> b.ADDRESS_BITS_PER_WORD, d = this.bits[a]; - g = d & ~(1 << (g & b.BIT_INDEX_MASK)); - this.bits[a] = g; - this.dirty |= d ^ g; - }; - a.prototype.get = function(g) { - return 0 !== (this.bits[g >> b.ADDRESS_BITS_PER_WORD] & 1 << (g & b.BIT_INDEX_MASK)); - }; - a.prototype.clearAll = function() { - for (var b = this.bits, g = 0, a = b.length;g < a;g++) { - b[g] = 0; - } - this.dirty = this.count = 0; - }; - a.prototype._union = function(b) { - var g = this.dirty, a = this.bits; - b = b.bits; - for (var d = 0, e = a.length;d < e;d++) { - var s = a[d], h = s | b[d]; - a[d] = h; - g |= s ^ h; - } - this.dirty = g; - }; - a.prototype.intersect = function(b) { - var g = this.dirty, a = this.bits; - b = b.bits; - for (var d = 0, e = a.length;d < e;d++) { - var s = a[d], h = s & b[d]; - a[d] = h; - g |= s ^ h; - } - this.dirty = g; - }; - a.prototype.subtract = function(b) { - var g = this.dirty, a = this.bits; - b = b.bits; - for (var d = 0, e = a.length;d < e;d++) { - var s = a[d], h = s & ~b[d]; - a[d] = h; - g |= s ^ h; - } - this.dirty = g; - }; - a.prototype.negate = function() { - for (var b = this.dirty, g = this.bits, a = 0, d = g.length;a < d;a++) { - var e = g[a], s = ~e; - g[a] = s; - b |= e ^ s; - } - this.dirty = b; - }; - a.prototype.forEach = function(g) { - e(g); - for (var a = this.bits, d = 0, h = a.length;d < h;d++) { - var c = a[d]; - if (c) { - for (var l = 0;l < b.BITS_PER_WORD;l++) { - c & 1 << l && g(d * b.BITS_PER_WORD + l); - } - } - } - }; - a.prototype.toArray = function() { - for (var g = [], a = this.bits, d = 0, e = a.length;d < e;d++) { - var s = a[d]; - if (s) { - for (var h = 0;h < b.BITS_PER_WORD;h++) { - s & 1 << h && g.push(d * b.BITS_PER_WORD + h); - } - } - } - return g; - }; - a.prototype.equals = function(b) { - if (this.size !== b.size) { - return!1; - } - var g = this.bits; - b = b.bits; - for (var a = 0, d = g.length;a < d;a++) { - if (g[a] !== b[a]) { - return!1; - } - } - return!0; - }; - a.prototype.contains = function(b) { - if (this.size !== b.size) { - return!1; - } - var g = this.bits; - b = b.bits; - for (var a = 0, d = g.length;a < d;a++) { - if ((g[a] | b[a]) !== g[a]) { - return!1; - } - } - return!0; - }; - a.prototype.isEmpty = function() { - this.recount(); - return 0 === this.count; - }; - a.prototype.clone = function() { - var b = new a(this.length); - b._union(this); - return b; - }; - return a; - }(); - b.Uint32ArrayBitSet = h; - var c = function() { - function a(b) { - this.dirty = this.count = 0; - this.size = g(b); - this.bits = 0; - this.singleWord = !0; - this.length = b; - } - a.prototype.recount = function() { - if (this.dirty) { - var b = this.bits, b = b - (b >> 1 & 1431655765), b = (b & 858993459) + (b >> 2 & 858993459); - this.count = 0 + (16843009 * (b + (b >> 4) & 252645135) >> 24); - this.dirty = 0; - } - }; - a.prototype.set = function(a) { - var g = this.bits; - this.bits = a = g | 1 << (a & b.BIT_INDEX_MASK); - this.dirty |= g ^ a; - }; - a.prototype.setAll = function() { - this.bits = 4294967295; - this.count = this.size; - this.dirty = 0; - }; - a.prototype.assign = function(b) { - this.count = b.count; - this.dirty = b.dirty; - this.size = b.size; - this.bits = b.bits; - }; - a.prototype.clear = function(a) { - var g = this.bits; - this.bits = a = g & ~(1 << (a & b.BIT_INDEX_MASK)); - this.dirty |= g ^ a; - }; - a.prototype.get = function(a) { - return 0 !== (this.bits & 1 << (a & b.BIT_INDEX_MASK)); - }; - a.prototype.clearAll = function() { - this.dirty = this.count = this.bits = 0; - }; - a.prototype._union = function(b) { - var a = this.bits; - this.bits = b = a | b.bits; - this.dirty = a ^ b; - }; - a.prototype.intersect = function(b) { - var a = this.bits; - this.bits = b = a & b.bits; - this.dirty = a ^ b; - }; - a.prototype.subtract = function(b) { - var a = this.bits; - this.bits = b = a & ~b.bits; - this.dirty = a ^ b; - }; - a.prototype.negate = function() { - var b = this.bits, a = ~b; - this.bits = a; - this.dirty = b ^ a; - }; - a.prototype.forEach = function(a) { - e(a); - var g = this.bits; - if (g) { - for (var d = 0;d < b.BITS_PER_WORD;d++) { - g & 1 << d && a(d); - } - } - }; - a.prototype.toArray = function() { - var a = [], g = this.bits; - if (g) { - for (var d = 0;d < b.BITS_PER_WORD;d++) { - g & 1 << d && a.push(d); - } - } - return a; - }; - a.prototype.equals = function(b) { - return this.bits === b.bits; - }; - a.prototype.contains = function(b) { - var a = this.bits; - return(a | b.bits) === a; - }; - a.prototype.isEmpty = function() { - this.recount(); - return 0 === this.count; - }; - a.prototype.clone = function() { - var b = new a(this.length); - b._union(this); - return b; - }; - return a; - }(); - b.Uint32BitSet = c; - c.prototype.toString = d; - c.prototype.toBitString = a; - h.prototype.toString = d; - h.prototype.toBitString = a; - b.BitSetFunctor = function(a) { - var d = 1 === g(a) >> b.ADDRESS_BITS_PER_WORD ? c : h; - return function() { - return new d(a); - }; - }; - })(f.BitSets || (f.BitSets = {})); - d = function() { - function b() { - } - b.randomStyle = function() { - b._randomStyleCache || (b._randomStyleCache = "#ff5e3a #ff9500 #ffdb4c #87fc70 #52edc7 #1ad6fd #c644fc #ef4db6 #4a4a4a #dbddde #ff3b30 #ff9500 #ffcc00 #4cd964 #34aadc #007aff #5856d6 #ff2d55 #8e8e93 #c7c7cc #5ad427 #c86edf #d1eefc #e0f8d8 #fb2b69 #f7f7f7 #1d77ef #d6cec3 #55efcb #ff4981 #ffd3e0 #f7f7f7 #ff1300 #1f1f21 #bdbec2 #ff3a2d".split(" ")); - return b._randomStyleCache[b._nextStyle++ % b._randomStyleCache.length]; - }; - b.contrastStyle = function(b) { - b = parseInt(b.substr(1), 16); - return 128 <= (299 * (b >> 16) + 587 * (b >> 8 & 255) + 114 * (b & 255)) / 1E3 ? "#000000" : "#ffffff"; - }; - b.reset = function() { - b._nextStyle = 0; - }; - b.TabToolbar = "#252c33"; - b.Toolbars = "#343c45"; - b.HighlightBlue = "#1d4f73"; - b.LightText = "#f5f7fa"; - b.ForegroundText = "#b6babf"; - b.Black = "#000000"; - b.VeryDark = "#14171a"; - b.Dark = "#181d20"; - b.Light = "#a9bacb"; - b.Grey = "#8fa1b2"; - b.DarkGrey = "#5f7387"; - b.Blue = "#46afe3"; - b.Purple = "#6b7abb"; - b.Pink = "#df80ff"; - b.Red = "#eb5368"; - b.Orange = "#d96629"; - b.LightOrange = "#d99b28"; - b.Green = "#70bf53"; - b.BlueGrey = "#5e88b0"; - b._nextStyle = 0; - return b; - }(); - f.ColorStyle = d; - d = function() { - function b(b, a, d, e) { - this.xMin = b | 0; - this.yMin = a | 0; - this.xMax = d | 0; - this.yMax = e | 0; - } - b.FromUntyped = function(a) { - return new b(a.xMin, a.yMin, a.xMax, a.yMax); - }; - b.FromRectangle = function(a) { - return new b(20 * a.x | 0, 20 * a.y | 0, 20 * (a.x + a.width) | 0, 20 * (a.y + a.height) | 0); - }; - b.prototype.setElements = function(b, a, d, e) { - this.xMin = b; - this.yMin = a; - this.xMax = d; - this.yMax = e; - }; - b.prototype.copyFrom = function(b) { - this.setElements(b.xMin, b.yMin, b.xMax, b.yMax); - }; - b.prototype.contains = function(b, a) { - return b < this.xMin !== b < this.xMax && a < this.yMin !== a < this.yMax; - }; - b.prototype.unionInPlace = function(b) { - this.xMin = Math.min(this.xMin, b.xMin); - this.yMin = Math.min(this.yMin, b.yMin); - this.xMax = Math.max(this.xMax, b.xMax); - this.yMax = Math.max(this.yMax, b.yMax); - }; - b.prototype.extendByPoint = function(b, a) { - this.extendByX(b); - this.extendByY(a); - }; - b.prototype.extendByX = function(b) { - 134217728 === this.xMin ? this.xMin = this.xMax = b : (this.xMin = Math.min(this.xMin, b), this.xMax = Math.max(this.xMax, b)); - }; - b.prototype.extendByY = function(b) { - 134217728 === this.yMin ? this.yMin = this.yMax = b : (this.yMin = Math.min(this.yMin, b), this.yMax = Math.max(this.yMax, b)); - }; - b.prototype.intersects = function(b) { - return this.contains(b.xMin, b.yMin) || this.contains(b.xMax, b.yMax); - }; - b.prototype.isEmpty = function() { - return this.xMax <= this.xMin || this.yMax <= this.yMin; - }; - Object.defineProperty(b.prototype, "width", {get:function() { - return this.xMax - this.xMin; - }, set:function(b) { - this.xMax = this.xMin + b; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "height", {get:function() { - return this.yMax - this.yMin; - }, set:function(b) { - this.yMax = this.yMin + b; - }, enumerable:!0, configurable:!0}); - b.prototype.getBaseWidth = function(b) { - return Math.abs(Math.cos(b)) * (this.xMax - this.xMin) + Math.abs(Math.sin(b)) * (this.yMax - this.yMin); - }; - b.prototype.getBaseHeight = function(b) { - return Math.abs(Math.sin(b)) * (this.xMax - this.xMin) + Math.abs(Math.cos(b)) * (this.yMax - this.yMin); - }; - b.prototype.setEmpty = function() { - this.xMin = this.yMin = this.xMax = this.yMax = 0; - }; - b.prototype.setToSentinels = function() { - this.xMin = this.yMin = this.xMax = this.yMax = 134217728; - }; - b.prototype.clone = function() { - return new b(this.xMin, this.yMin, this.xMax, this.yMax); - }; - b.prototype.toString = function() { - return "{ xMin: " + this.xMin + ", xMin: " + this.yMin + ", xMax: " + this.xMax + ", xMax: " + this.yMax + " }"; - }; - return b; - }(); - f.Bounds = d; - d = function() { - function b(b, a, d, e) { - r.assert(u(b)); - r.assert(u(a)); - r.assert(u(d)); - r.assert(u(e)); - this._xMin = b | 0; - this._yMin = a | 0; - this._xMax = d | 0; - this._yMax = e | 0; - } - b.FromUntyped = function(a) { - return new b(a.xMin, a.yMin, a.xMax, a.yMax); - }; - b.FromRectangle = function(a) { - return new b(20 * a.x | 0, 20 * a.y | 0, 20 * (a.x + a.width) | 0, 20 * (a.y + a.height) | 0); - }; - b.prototype.setElements = function(b, a, d, e) { - this.xMin = b; - this.yMin = a; - this.xMax = d; - this.yMax = e; - }; - b.prototype.copyFrom = function(b) { - this.setElements(b.xMin, b.yMin, b.xMax, b.yMax); - }; - b.prototype.contains = function(b, a) { - return b < this.xMin !== b < this.xMax && a < this.yMin !== a < this.yMax; - }; - b.prototype.unionWith = function(b) { - this._xMin = Math.min(this._xMin, b._xMin); - this._yMin = Math.min(this._yMin, b._yMin); - this._xMax = Math.max(this._xMax, b._xMax); - this._yMax = Math.max(this._yMax, b._yMax); - }; - b.prototype.extendByPoint = function(b, a) { - this.extendByX(b); - this.extendByY(a); - }; - b.prototype.extendByX = function(b) { - 134217728 === this.xMin ? this.xMin = this.xMax = b : (this.xMin = Math.min(this.xMin, b), this.xMax = Math.max(this.xMax, b)); - }; - b.prototype.extendByY = function(b) { - 134217728 === this.yMin ? this.yMin = this.yMax = b : (this.yMin = Math.min(this.yMin, b), this.yMax = Math.max(this.yMax, b)); - }; - b.prototype.intersects = function(b) { - return this.contains(b._xMin, b._yMin) || this.contains(b._xMax, b._yMax); - }; - b.prototype.isEmpty = function() { - return this._xMax <= this._xMin || this._yMax <= this._yMin; - }; - Object.defineProperty(b.prototype, "xMin", {get:function() { - return this._xMin; - }, set:function(b) { - r.assert(u(b)); - this._xMin = b; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "yMin", {get:function() { - return this._yMin; - }, set:function(b) { - r.assert(u(b)); - this._yMin = b | 0; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "xMax", {get:function() { - return this._xMax; - }, set:function(b) { - r.assert(u(b)); - this._xMax = b | 0; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "width", {get:function() { - return this._xMax - this._xMin; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "yMax", {get:function() { - return this._yMax; - }, set:function(b) { - r.assert(u(b)); - this._yMax = b | 0; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "height", {get:function() { - return this._yMax - this._yMin; - }, enumerable:!0, configurable:!0}); - b.prototype.getBaseWidth = function(b) { - return Math.abs(Math.cos(b)) * (this._xMax - this._xMin) + Math.abs(Math.sin(b)) * (this._yMax - this._yMin); - }; - b.prototype.getBaseHeight = function(b) { - return Math.abs(Math.sin(b)) * (this._xMax - this._xMin) + Math.abs(Math.cos(b)) * (this._yMax - this._yMin); - }; - b.prototype.setEmpty = function() { - this._xMin = this._yMin = this._xMax = this._yMax = 0; - }; - b.prototype.clone = function() { - return new b(this.xMin, this.yMin, this.xMax, this.yMax); - }; - b.prototype.toString = function() { - return "{ xMin: " + this._xMin + ", xMin: " + this._yMin + ", xMax: " + this._xMax + ", yMax: " + this._yMax + " }"; - }; - b.prototype.assertValid = function() { - }; - return b; - }(); - f.DebugBounds = d; - d = function() { - function b(b, a, d, e) { - this.r = b; - this.g = a; - this.b = d; - this.a = e; - } - b.FromARGB = function(a) { - return new b((a >> 16 & 255) / 255, (a >> 8 & 255) / 255, (a >> 0 & 255) / 255, (a >> 24 & 255) / 255); - }; - b.FromRGBA = function(a) { - return b.FromARGB(q.RGBAToARGB(a)); - }; - b.prototype.toRGBA = function() { - return 255 * this.r << 24 | 255 * this.g << 16 | 255 * this.b << 8 | 255 * this.a; - }; - b.prototype.toCSSStyle = function() { - return q.rgbaToCSSStyle(this.toRGBA()); - }; - b.prototype.set = function(b) { - this.r = b.r; - this.g = b.g; - this.b = b.b; - this.a = b.a; - }; - b.randomColor = function(a) { - "undefined" === typeof a && (a = 1); - return new b(Math.random(), Math.random(), Math.random(), a); - }; - b.parseColor = function(a) { - b.colorCache || (b.colorCache = Object.create(null)); - if (b.colorCache[a]) { - return b.colorCache[a]; - } - var d = document.createElement("span"); - document.body.appendChild(d); - d.style.backgroundColor = a; - var e = getComputedStyle(d).backgroundColor; - document.body.removeChild(d); - (d = /^rgb\((\d+), (\d+), (\d+)\)$/.exec(e)) || (d = /^rgba\((\d+), (\d+), (\d+), ([\d.]+)\)$/.exec(e)); - e = new b(0, 0, 0, 0); - e.r = parseFloat(d[1]) / 255; - e.g = parseFloat(d[2]) / 255; - e.b = parseFloat(d[3]) / 255; - e.a = d[4] ? parseFloat(d[4]) / 255 : 1; - return b.colorCache[a] = e; - }; - b.Red = new b(1, 0, 0, 1); - b.Green = new b(0, 1, 0, 1); - b.Blue = new b(0, 0, 1, 1); - b.None = new b(0, 0, 0, 0); - b.White = new b(1, 1, 1, 1); - b.Black = new b(0, 0, 0, 1); - b.colorCache = {}; - return b; - }(); - f.Color = d; - (function(b) { - function a(b) { - var d, g, e = b >> 24 & 255; - g = (Math.imul(b >> 16 & 255, e) + 127) / 255 | 0; - d = (Math.imul(b >> 8 & 255, e) + 127) / 255 | 0; - b = (Math.imul(b >> 0 & 255, e) + 127) / 255 | 0; - return e << 24 | g << 16 | d << 8 | b; - } - b.RGBAToARGB = function(b) { - return b >> 8 & 16777215 | (b & 255) << 24; - }; - b.ARGBToRGBA = function(b) { - return b << 8 | b >> 24 & 255; - }; - b.rgbaToCSSStyle = function(b) { - return f.StringUtilities.concat9("rgba(", b >> 24 & 255, ",", b >> 16 & 255, ",", b >> 8 & 255, ",", (b & 255) / 255, ")"); - }; - b.cssStyleToRGBA = function(b) { - if ("#" === b[0]) { - if (7 === b.length) { - return parseInt(b.substring(1), 16) << 8 | 255; - } - } else { - if ("r" === b[0]) { - return b = b.substring(5, b.length - 1).split(","), (parseInt(b[0]) & 255) << 24 | (parseInt(b[1]) & 255) << 16 | (parseInt(b[2]) & 255) << 8 | 255 * parseFloat(b[3]) & 255; - } - } - return 4278190335; - }; - b.hexToRGB = function(b) { - return parseInt(b.slice(1), 16); - }; - b.rgbToHex = function(b) { - return "#" + ("000000" + b.toString(16)).slice(-6); - }; - b.isValidHexColor = function(b) { - return/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(b); - }; - b.clampByte = function(b) { - return Math.max(0, Math.min(255, b)); - }; - b.unpremultiplyARGB = function(b) { - var a, d, g = b >> 24 & 255; - d = Math.imul(255, b >> 16 & 255) / g & 255; - a = Math.imul(255, b >> 8 & 255) / g & 255; - b = Math.imul(255, b >> 0 & 255) / g & 255; - return g << 24 | d << 16 | a << 8 | b; - }; - b.premultiplyARGB = a; - var d; - b.ensureUnpremultiplyTable = function() { - if (!d) { - d = new Uint8Array(65536); - for (var b = 0;256 > b;b++) { - for (var a = 0;256 > a;a++) { - d[(a << 8) + b] = Math.imul(255, b) / a; - } - } - } - }; - b.tableLookupUnpremultiplyARGB = function(b) { - b |= 0; - var a = b >> 24 & 255; - if (0 === a) { - return 0; - } - if (255 === a) { - return b; - } - var g, e, h = a << 8, c = d; - e = c[h + (b >> 16 & 255)]; - g = c[h + (b >> 8 & 255)]; - b = c[h + (b >> 0 & 255)]; - return a << 24 | e << 16 | g << 8 | b; - }; - b.blendPremultipliedBGRA = function(b, a) { - var d, g; - g = 256 - (a & 255); - d = Math.imul(b & 16711935, g) >> 8; - g = Math.imul(b >> 8 & 16711935, g) >> 8; - return((a >> 8 & 16711935) + g & 16711935) << 8 | (a & 16711935) + d & 16711935; - }; - var e = h.swap32; - b.convertImage = function(b, h, c, p) { - c !== p && r.assert(c.buffer !== p.buffer, "Can't handle overlapping views."); - var q = c.length; - if (b === h) { - if (c !== p) { - for (b = 0;b < q;b++) { - p[b] = c[b]; - } - } - } else { - if (1 === b && 3 === h) { - for (f.ColorUtilities.ensureUnpremultiplyTable(), b = 0;b < q;b++) { - var n = c[b]; - h = n & 255; - if (0 === h) { - p[b] = 0; - } else { - if (255 === h) { - p[b] = (n & 255) << 24 | n >> 8 & 16777215; - } else { - var t = n >> 24 & 255, m = n >> 16 & 255, n = n >> 8 & 255, z = h << 8, v = d, n = v[z + n], m = v[z + m], t = v[z + t]; - p[b] = h << 24 | t << 16 | m << 8 | n; - } - } - } - } else { - if (2 === b && 3 === h) { - for (b = 0;b < q;b++) { - p[b] = e(c[b]); - } - } else { - if (3 === b && 1 === h) { - for (b = 0;b < q;b++) { - h = c[b], p[b] = e(a(h & 4278255360 | h >> 16 & 255 | (h & 255) << 16)); - } - } else { - for (r.somewhatImplemented("Image Format Conversion: " + k[b] + " -> " + k[h]), b = 0;b < q;b++) { - p[b] = c[b]; - } - } - } - } - } - }; - })(f.ColorUtilities || (f.ColorUtilities = {})); - var q = f.ColorUtilities, d = function() { - function b(b) { - "undefined" === typeof b && (b = 32); - this._list = []; - this._maxSize = b; - } - b.prototype.acquire = function(a) { - if (b._enabled) { - for (var d = this._list, e = 0;e < d.length;e++) { - var h = d[e]; - if (h.byteLength >= a) { - return d.splice(e, 1), h; - } - } - } - return new ArrayBuffer(a); - }; - b.prototype.release = function(d) { - if (b._enabled) { - var e = this._list; - r.assert(0 > a.indexOf(e, d)); - e.length === this._maxSize && e.shift(); - e.push(d); - } - }; - b.prototype.ensureUint8ArrayLength = function(b, a) { - if (b.length >= a) { - return b; - } - var d = Math.max(b.length + a, (3 * b.length >> 1) + 1), d = new Uint8Array(this.acquire(d), 0, d); - d.set(b); - this.release(b.buffer); - return d; - }; - b.prototype.ensureFloat64ArrayLength = function(b, a) { - if (b.length >= a) { - return b; - } - var d = Math.max(b.length + a, (3 * b.length >> 1) + 1), d = new Float64Array(this.acquire(d * Float64Array.BYTES_PER_ELEMENT), 0, d); - d.set(b); - this.release(b.buffer); - return d; - }; - b._enabled = !0; - return b; - }(); - f.ArrayBufferPool = d; - (function(b) { - (function(b) { - b[b.EXTERNAL_INTERFACE_FEATURE = 1] = "EXTERNAL_INTERFACE_FEATURE"; - b[b.CLIPBOARD_FEATURE = 2] = "CLIPBOARD_FEATURE"; - b[b.SHAREDOBJECT_FEATURE = 3] = "SHAREDOBJECT_FEATURE"; - b[b.VIDEO_FEATURE = 4] = "VIDEO_FEATURE"; - b[b.SOUND_FEATURE = 5] = "SOUND_FEATURE"; - b[b.NETCONNECTION_FEATURE = 6] = "NETCONNECTION_FEATURE"; - })(b.Feature || (b.Feature = {})); - (function(b) { - b[b.AVM1_ERROR = 1] = "AVM1_ERROR"; - b[b.AVM2_ERROR = 2] = "AVM2_ERROR"; - })(b.ErrorTypes || (b.ErrorTypes = {})); - b.instance; - })(f.Telemetry || (f.Telemetry = {})); - (function(b) { - b.instance; - })(f.FileLoadingService || (f.FileLoadingService = {})); - (function(b) { - b.instance = {enabled:!1, initJS:function(b) { - }, registerCallback:function(b) { - }, unregisterCallback:function(b) { - }, eval:function(b) { - }, call:function(b) { - }, getId:function() { - return null; - }}; - })(f.ExternalInterfaceService || (f.ExternalInterfaceService = {})); - d = function() { - function b() { - } - b.prototype.setClipboard = function(b) { - r.abstractMethod("public ClipboardService::setClipboard"); - }; - b.instance = null; - return b; - }(); - f.ClipboardService = d; - d = function() { - function b() { - this._queues = {}; - } - b.prototype.register = function(b, a) { - r.assert(b); - r.assert(a); - var d = this._queues[b]; - if (d) { - if (-1 < d.indexOf(a)) { - return; - } - } else { - d = this._queues[b] = []; - } - d.push(a); - }; - b.prototype.unregister = function(b, a) { - r.assert(b); - r.assert(a); - var d = this._queues[b]; - if (d) { - var e = d.indexOf(a); - -1 !== e && d.splice(e, 1); - 0 === d.length && (this._queues[b] = null); - } - }; - b.prototype.notify = function(b, a) { - var d = this._queues[b]; - if (d) { - d = d.slice(); - a = Array.prototype.slice.call(arguments, 0); - for (var e = 0;e < d.length;e++) { - d[e].apply(null, a); - } - } - }; - b.prototype.notify1 = function(b, a) { - var d = this._queues[b]; - if (d) { - for (var d = d.slice(), e = 0;e < d.length;e++) { - (0,d[e])(b, a); - } - } - }; - return b; - }(); - f.Callback = d; - (function(b) { - b[b.None = 0] = "None"; - b[b.PremultipliedAlphaARGB = 1] = "PremultipliedAlphaARGB"; - b[b.StraightAlphaARGB = 2] = "StraightAlphaARGB"; - b[b.StraightAlphaRGBA = 3] = "StraightAlphaRGBA"; - b[b.JPEG = 4] = "JPEG"; - b[b.PNG = 5] = "PNG"; - b[b.GIF = 6] = "GIF"; - })(f.ImageType || (f.ImageType = {})); - var k = f.ImageType; - f.PromiseWrapper = function() { - return function() { - this.promise = new Promise(function(b, a) { - this.resolve = b; - this.reject = a; - }.bind(this)); - }; - }(); -})(Shumway || (Shumway = {})); -(function() { - function f(b) { - if ("function" !== typeof b) { - throw new TypeError("Invalid deferred constructor"); - } - var a = n(); - b = new b(a); - var d = a.resolve; - if ("function" !== typeof d) { - throw new TypeError("Invalid resolve construction function"); - } - a = a.reject; - if ("function" !== typeof a) { - throw new TypeError("Invalid reject construction function"); - } - return{promise:b, resolve:d, reject:a}; - } - function u(b, a) { - if ("object" !== typeof b || null === b) { - return!1; - } - try { - var d = b.then; - if ("function" !== typeof d) { - return!1; - } - d.call(b, a.resolve, a.reject); - } catch (g) { - d = a.reject, d(g); - } - return!0; - } - function c(b) { - return "object" === typeof b && null !== b && "undefined" !== typeof b.promiseStatus; - } - function t(b, a) { - if ("unresolved" === b.promiseStatus) { - var d = b.rejectReactions; - b.result = a; - b.resolveReactions = void 0; - b.rejectReactions = void 0; - b.promiseStatus = "has-rejection"; - m(d, a); - } - } - function m(b, a) { - for (var d = 0;d < b.length;d++) { - r({reaction:b[d], argument:a}); - } - } - function r(b) { - 0 === V.length && setTimeout(a, 0); - V.push(b); - } - function a() { - for (;0 < V.length;) { - var a = V[0]; - try { - a: { - var d = a.reaction, g = d.deferred, e = d.handler, h = void 0, c = void 0; - try { - h = e(a.argument); - } catch (k) { - var p = g.reject; - p(k); - break a; - } - if (h === g.promise) { - p = g.reject, p(new TypeError("Self resolution")); - } else { - try { - if (c = u(h, g), !c) { - var q = g.resolve; - q(h); - } - } catch (n) { - p = g.reject, p(n); - } - } - } - } catch (v) { - if ("function" === typeof b.onerror) { - b.onerror(v); - } - } - V.shift(); - } - } - function d(b) { - throw b; - } - function p(b) { - return b; - } - function e(b) { - return function(a) { - t(b, a); - }; - } - function h(b) { - return function(a) { - if ("unresolved" === b.promiseStatus) { - var d = b.resolveReactions; - b.result = a; - b.resolveReactions = void 0; - b.rejectReactions = void 0; - b.promiseStatus = "has-resolution"; - m(d, a); - } - }; - } - function n() { - function b(a, d) { - b.resolve = a; - b.reject = d; - } - return b; - } - function q(b, a, d) { - return function(g) { - if (g === b) { - return d(new TypeError("Self resolution")); - } - var e = b.promiseConstructor; - if (c(g) && g.promiseConstructor === e) { - return g.then(a, d); - } - e = f(e); - return u(g, e) ? e.promise.then(a, d) : a(g); - }; - } - function k(b, a, d, g) { - return function(e) { - a[b] = e; - g.countdown--; - 0 === g.countdown && d.resolve(a); - }; - } - function b(a) { - if ("function" !== typeof a) { - throw new TypeError("resolver is not a function"); - } - if ("object" !== typeof this) { - throw new TypeError("Promise to initialize is not an object"); - } - this.promiseStatus = "unresolved"; - this.resolveReactions = []; - this.rejectReactions = []; - this.result = void 0; - var d = h(this), g = e(this); - try { - a(d, g); - } catch (c) { - t(this, c); - } - this.promiseConstructor = b; - return this; - } - var g = Function("return this")(); - if (g.Promise) { - "function" !== typeof g.Promise.all && (g.Promise.all = function(b) { - var a = 0, d = [], e, h, c = new g.Promise(function(b, a) { - e = b; - h = a; - }); - b.forEach(function(b, g) { - a++; - b.then(function(b) { - d[g] = b; - a--; - 0 === a && e(d); - }, h); - }); - 0 === a && e(d); - return c; - }), "function" !== typeof g.Promise.resolve && (g.Promise.resolve = function(b) { - return new g.Promise(function(a) { - a(b); - }); - }); - } else { - var V = []; - b.all = function(b) { - var a = f(this), d = [], g = {countdown:0}, e = 0; - b.forEach(function(b) { - this.cast(b).then(k(e, d, a, g), a.reject); - e++; - g.countdown++; - }, this); - 0 === e && a.resolve(d); - return a.promise; - }; - b.cast = function(b) { - if (c(b)) { - return b; - } - var a = f(this); - a.resolve(b); - return a.promise; - }; - b.reject = function(b) { - var a = f(this); - a.reject(b); - return a.promise; - }; - b.resolve = function(b) { - var a = f(this); - a.resolve(b); - return a.promise; - }; - b.prototype = {"catch":function(b) { - this.then(void 0, b); - }, then:function(b, a) { - if (!c(this)) { - throw new TypeError("this is not a Promises"); - } - var g = f(this.promiseConstructor), e = "function" === typeof a ? a : d, h = {deferred:g, handler:q(this, "function" === typeof b ? b : p, e)}, e = {deferred:g, handler:e}; - switch(this.promiseStatus) { - case "unresolved": - this.resolveReactions.push(h); - this.rejectReactions.push(e); - break; - case "has-resolution": - r({reaction:h, argument:this.result}); - break; - case "has-rejection": - r({reaction:e, argument:this.result}); - } - return g.promise; - }}; - g.Promise = b; - } -})(); -"undefined" !== typeof exports && (exports.Shumway = Shumway); -(function() { - function f(f, c, t) { - f[c] || Object.defineProperty(f, c, {value:t, writable:!0, configurable:!0, enumerable:!1}); - } - f(String.prototype, "padRight", function(f, c) { - var t = this, m = t.replace(/\033\[[0-9]*m/g, "").length; - if (!f || m >= c) { - return t; - } - for (var m = (c - m) / f.length, r = 0;r < m;r++) { - t += f; - } - return t; - }); - f(String.prototype, "padLeft", function(f, c) { - var t = this, m = t.length; - if (!f || m >= c) { - return t; - } - for (var m = (c - m) / f.length, r = 0;r < m;r++) { - t = f + t; - } - return t; - }); - f(String.prototype, "trim", function() { - return this.replace(/^\s+|\s+$/g, ""); - }); - f(String.prototype, "endsWith", function(f) { - return-1 !== this.indexOf(f, this.length - f.length); - }); - f(Array.prototype, "replace", function(f, c) { - if (f === c) { - return 0; - } - for (var t = 0, m = 0;m < this.length;m++) { - this[m] === f && (this[m] = c, t++); - } - return t; - }); -})(); -(function(f) { - (function(u) { - var c = f.isObject, t = f.Debug.assert, m = function() { - function a(d, h, c, p) { - this.shortName = d; - this.longName = h; - this.type = c; - p = p || {}; - this.positional = p.positional; - this.parseFn = p.parse; - this.value = p.defaultValue; - } - a.prototype.parse = function(a) { - "boolean" === this.type ? (t("boolean" === typeof a), this.value = a) : "number" === this.type ? (t(!isNaN(a), a + " is not a number"), this.value = parseInt(a, 10)) : this.value = a; - this.parseFn && this.parseFn(this.value); - }; - return a; - }(); - u.Argument = m; - var r = function() { - function c() { - this.args = []; - } - c.prototype.addArgument = function(a, d, c, p) { - a = new m(a, d, c, p); - this.args.push(a); - return a; - }; - c.prototype.addBoundOption = function(a) { - this.args.push(new m(a.shortName, a.longName, a.type, {parse:function(d) { - a.value = d; - }})); - }; - c.prototype.addBoundOptionSet = function(e) { - var h = this; - e.options.forEach(function(e) { - e instanceof a ? h.addBoundOptionSet(e) : (t(e instanceof d), h.addBoundOption(e)); - }); - }; - c.prototype.getUsage = function() { - var a = ""; - this.args.forEach(function(d) { - a = d.positional ? a + d.longName : a + ("[-" + d.shortName + "|--" + d.longName + ("boolean" === d.type ? "" : " " + d.type[0].toUpperCase()) + "]"); - a += " "; - }); - return a; - }; - c.prototype.parse = function(a) { - var d = {}, c = []; - this.args.forEach(function(b) { - b.positional ? c.push(b) : (d["-" + b.shortName] = b, d["--" + b.longName] = b); - }); - for (var p = [];a.length;) { - var k = a.shift(), b = null, g = k; - if ("--" == k) { - p = p.concat(a); - break; - } else { - if ("-" == k.slice(0, 1) || "--" == k.slice(0, 2)) { - b = d[k]; - if (!b) { - continue; - } - "boolean" !== b.type ? (g = a.shift(), t("-" !== g && "--" !== g, "Argument " + k + " must have a value.")) : g = !0; - } else { - c.length ? b = c.shift() : p.push(g); - } - } - b && b.parse(g); - } - t(0 === c.length, "Missing positional arguments."); - return p; - }; - return c; - }(); - u.ArgumentParser = r; - var a = function() { - function a(d, h) { - "undefined" === typeof h && (h = null); - this.open = !1; - this.name = d; - this.settings = h || {}; - this.options = []; - } - a.prototype.register = function(d) { - if (d instanceof a) { - for (var h = 0;h < this.options.length;h++) { - var t = this.options[h]; - if (t instanceof a && t.name === d.name) { - return t; - } - } - } - this.options.push(d); - if (this.settings) { - if (d instanceof a) { - h = this.settings[d.name], c(h) && (d.settings = h.settings, d.open = h.open); - } else { - if ("undefined" !== typeof this.settings[d.longName]) { - switch(d.type) { - case "boolean": - d.value = !!this.settings[d.longName]; - break; - case "number": - d.value = +this.settings[d.longName]; - break; - default: - d.value = this.settings[d.longName]; - } - } - } - } - return d; - }; - a.prototype.trace = function(a) { - a.enter(this.name + " {"); - this.options.forEach(function(d) { - d.trace(a); - }); - a.leave("}"); - }; - a.prototype.getSettings = function() { - var d = {}; - this.options.forEach(function(h) { - h instanceof a ? d[h.name] = {settings:h.getSettings(), open:h.open} : d[h.longName] = h.value; - }); - return d; - }; - a.prototype.setSettings = function(d) { - d && this.options.forEach(function(h) { - h instanceof a ? h.name in d && h.setSettings(d[h.name].settings) : h.longName in d && (h.value = d[h.longName]); - }); - }; - return a; - }(); - u.OptionSet = a; - var d = function() { - function a(d, h, c, p, k, b) { - "undefined" === typeof b && (b = null); - this.longName = h; - this.shortName = d; - this.type = c; - this.value = this.defaultValue = p; - this.description = k; - this.config = b; - } - a.prototype.parse = function(a) { - this.value = a; - }; - a.prototype.trace = function(a) { - a.writeLn(("-" + this.shortName + "|--" + this.longName).padRight(" ", 30) + " = " + this.type + " " + this.value + " [" + this.defaultValue + "] (" + this.description + ")"); - }; - return a; - }(); - u.Option = d; - })(f.Options || (f.Options = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - function c() { - try { - return "undefined" !== typeof window && "localStorage" in window && null !== window.localStorage; - } catch (c) { - return!1; - } - } - function t(t) { - "undefined" === typeof t && (t = u.ROOT); - var r = {}; - if (c() && (t = window.localStorage[t])) { - try { - r = JSON.parse(t); - } catch (a) { - } - } - return r; - } - u.ROOT = "Shumway Options"; - u.shumwayOptions = new f.Options.OptionSet(u.ROOT, t()); - u.isStorageSupported = c; - u.load = t; - u.save = function(t, r) { - "undefined" === typeof t && (t = null); - "undefined" === typeof r && (r = u.ROOT); - if (c()) { - try { - window.localStorage[r] = JSON.stringify(t ? t : u.shumwayOptions.getSettings()); - } catch (a) { - } - } - }; - u.setSettings = function(c) { - u.shumwayOptions.setSettings(c); - }; - u.getSettings = function(c) { - return u.shumwayOptions.getSettings(); - }; - })(f.Settings || (f.Settings = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - var c = function() { - function c(t, r) { - this._parent = t; - this._timers = f.ObjectUtilities.createMap(); - this._name = r; - this._count = this._total = this._last = this._begin = 0; - } - c.time = function(m, r) { - c.start(m); - r(); - c.stop(); - }; - c.start = function(m) { - c._top = c._top._timers[m] || (c._top._timers[m] = new c(c._top, m)); - c._top.start(); - m = c._flat._timers[m] || (c._flat._timers[m] = new c(c._flat, m)); - m.start(); - c._flatStack.push(m); - }; - c.stop = function() { - c._top.stop(); - c._top = c._top._parent; - c._flatStack.pop().stop(); - }; - c.stopStart = function(m) { - c.stop(); - c.start(m); - }; - c.prototype.start = function() { - this._begin = f.getTicks(); - }; - c.prototype.stop = function() { - this._last = f.getTicks() - this._begin; - this._total += this._last; - this._count += 1; - }; - c.prototype.toJSON = function() { - return{name:this._name, total:this._total, timers:this._timers}; - }; - c.prototype.trace = function(c) { - c.enter(this._name + ": " + this._total.toFixed(2) + " ms, count: " + this._count + ", average: " + (this._total / this._count).toFixed(2) + " ms"); - for (var t in this._timers) { - this._timers[t].trace(c); - } - c.outdent(); - }; - c.trace = function(m) { - c._base.trace(m); - c._flat.trace(m); - }; - c._base = new c(null, "Total"); - c._top = c._base; - c._flat = new c(null, "Flat"); - c._flatStack = []; - return c; - }(); - u.Timer = c; - c = function() { - function c(t) { - this._enabled = t; - this.clear(); - } - Object.defineProperty(c.prototype, "counts", {get:function() { - return this._counts; - }, enumerable:!0, configurable:!0}); - c.prototype.setEnabled = function(c) { - this._enabled = c; - }; - c.prototype.clear = function() { - this._counts = f.ObjectUtilities.createMap(); - this._times = f.ObjectUtilities.createMap(); - }; - c.prototype.toJSON = function() { - return{counts:this._counts, times:this._times}; - }; - c.prototype.count = function(c, r, a) { - "undefined" === typeof r && (r = 1); - "undefined" === typeof a && (a = 0); - if (this._enabled) { - return void 0 === this._counts[c] && (this._counts[c] = 0, this._times[c] = 0), this._counts[c] += r, this._times[c] += a, this._counts[c]; - } - }; - c.prototype.trace = function(c) { - for (var r in this._counts) { - c.writeLn(r + ": " + this._counts[r]); - } - }; - c.prototype._pairToString = function(c, r) { - var a = r[0], d = r[1], p = c[a], a = a + ": " + d; - p && (a += ", " + p.toFixed(4), 1 < d && (a += " (" + (p / d).toFixed(4) + ")")); - return a; - }; - c.prototype.toStringSorted = function() { - var c = this, r = this._times, a = [], d; - for (d in this._counts) { - a.push([d, this._counts[d]]); - } - a.sort(function(a, d) { - return d[1] - a[1]; - }); - return a.map(function(a) { - return c._pairToString(r, a); - }).join(", "); - }; - c.prototype.traceSorted = function(c, r) { - "undefined" === typeof r && (r = !1); - var a = this, d = this._times, p = [], e; - for (e in this._counts) { - p.push([e, this._counts[e]]); - } - p.sort(function(a, d) { - return d[1] - a[1]; - }); - r ? c.writeLn(p.map(function(e) { - return a._pairToString(d, e); - }).join(", ")) : p.forEach(function(e) { - c.writeLn(a._pairToString(d, e)); - }); - }; - c.instance = new c(!0); - return c; - }(); - u.Counter = c; - c = function() { - function c(t) { - this._samples = new Float64Array(t); - this._index = this._count = 0; - } - c.prototype.push = function(c) { - this._count < this._samples.length && this._count++; - this._index++; - this._samples[this._index % this._samples.length] = c; - }; - c.prototype.average = function() { - for (var c = 0, r = 0;r < this._count;r++) { - c += this._samples[r]; - } - return c / this._count; - }; - return c; - }(); - u.Average = c; - })(f.Metrics || (f.Metrics = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(f) { - function c(b) { - for (var a = Math.max.apply(null, b), d = b.length, e = 1 << a, c = new Uint32Array(e), h = a << 16 | 65535, k = 0;k < e;k++) { - c[k] = h; - } - for (var h = 0, k = 1, p = 2;k <= a;h <<= 1, ++k, p <<= 1) { - for (var q = 0;q < d;++q) { - if (b[q] === k) { - for (var n = 0, r = 0;r < k;++r) { - n = 2 * n + (h >> r & 1); - } - for (r = n;r < e;r += p) { - c[r] = k << 16 | q; - } - ++h; - } - } - } - return{codes:c, maxBits:a}; - } - var t; - (function(b) { - b[b.INIT = 0] = "INIT"; - b[b.BLOCK_0 = 1] = "BLOCK_0"; - b[b.BLOCK_1 = 2] = "BLOCK_1"; - b[b.BLOCK_2_PRE = 3] = "BLOCK_2_PRE"; - b[b.BLOCK_2 = 4] = "BLOCK_2"; - b[b.DONE = 5] = "DONE"; - b[b.ERROR = 6] = "ERROR"; - b[b.VERIFY_HEADER = 7] = "VERIFY_HEADER"; - })(t || (t = {})); - t = function() { - function b(b) { - this._buffer = new Uint8Array(1024); - this._bitLength = this._bitBuffer = this._bufferPosition = this._bufferSize = 0; - this._window = new Uint8Array(65794); - this._windowPosition = 0; - this._state = b ? 7 : 0; - this._isFinalBlock = !1; - this._distanceTable = this._literalTable = null; - this._block0Read = 0; - this._block2State = null; - this._copyState = {state:0, len:0, lenBits:0, dist:0, distBits:0}; - this._error = void 0; - if (!n) { - m = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]); - r = new Uint16Array(30); - a = new Uint8Array(30); - for (var k = b = 0, l = 1;30 > b;++b) { - r[b] = l, l += 1 << (a[b] = ~~((k += 2 < b ? 1 : 0) / 2)); - } - var s = new Uint8Array(288); - for (b = 0;32 > b;++b) { - s[b] = 5; - } - d = c(s.subarray(0, 32)); - p = new Uint16Array(29); - e = new Uint8Array(29); - k = b = 0; - for (l = 3;29 > b;++b) { - p[b] = l - (28 == b ? 1 : 0), l += 1 << (e[b] = ~~((k += 4 < b ? 1 : 0) / 4 % 6)); - } - for (b = 0;288 > b;++b) { - s[b] = 144 > b || 279 < b ? 8 : 256 > b ? 9 : 7; - } - h = c(s); - n = !0; - } - } - Object.defineProperty(b.prototype, "error", {get:function() { - return this._error; - }, enumerable:!0, configurable:!0}); - b.prototype.push = function(b) { - if (this._buffer.length < this._bufferSize + b.length) { - var a = new Uint8Array(this._bufferSize + b.length); - a.set(this._buffer); - this._buffer = a; - } - this._buffer.set(b, this._bufferSize); - this._bufferSize += b.length; - this._bufferPosition = 0; - b = !1; - do { - a = this._windowPosition; - if (0 === this._state && (b = this._decodeInitState())) { - break; - } - switch(this._state) { - case 1: - b = this._decodeBlock0(); - break; - case 3: - if (b = this._decodeBlock2Pre()) { - break; - } - ; - case 2: - ; - case 4: - b = this._decodeBlock(); - break; - case 6: - ; - case 5: - this._bufferPosition = this._bufferSize; - break; - case 7: - b = this._verifyZlibHeader(); - } - if (0 < this._windowPosition - a) { - this.onData(this._window.subarray(a, this._windowPosition)); - } - 65536 <= this._windowPosition && (this._window.set(this._window.subarray(this._windowPosition - 32768, this._windowPosition)), this._windowPosition = 32768); - } while (!b && this._bufferPosition < this._bufferSize); - this._bufferPosition < this._bufferSize ? (this._buffer.set(this._buffer.subarray(this._bufferPosition, this._bufferSize)), this._bufferSize -= this._bufferPosition) : this._bufferSize = 0; - }; - b.prototype._verifyZlibHeader = function() { - var b = this._bufferPosition; - if (b + 2 > this._bufferSize) { - return!0; - } - var a = this._buffer, a = a[b] << 8 | a[b + 1]; - this._bufferPosition = b + 2; - b = null; - 2048 !== (a & 3840) ? b = "inflate: unknown compression method" : 0 !== a % 31 ? b = "inflate: bad FCHECK" : 0 !== (a & 32) && (b = "inflate: FDICT bit set"); - b ? (this._error = b, this._state = 6) : this._state = 0; - }; - b.prototype._decodeInitState = function() { - if (this._isFinalBlock) { - return this._state = 5, !1; - } - var b = this._buffer, a = this._bufferSize, e = this._bitBuffer, k = this._bitLength, p = this._bufferPosition; - if (3 > (a - p << 3) + k) { - return!0; - } - 3 > k && (e |= b[p++] << k, k += 8); - var q = e & 7, e = e >> 3, k = k - 3; - switch(q >> 1) { - case 0: - k = e = 0; - if (4 > a - p) { - return!0; - } - var n = b[p] | b[p + 1] << 8, b = b[p + 2] | b[p + 3] << 8, p = p + 4; - if (65535 !== (n ^ b)) { - this._error = "inflate: invalid block 0 length"; - b = 6; - break; - } - 0 === n ? b = 0 : (this._block0Read = n, b = 1); - break; - case 1: - b = 2; - this._literalTable = h; - this._distanceTable = d; - break; - case 2: - if (26 > (a - p << 3) + k) { - return!0; - } - for (;14 > k;) { - e |= b[p++] << k, k += 8; - } - n = (e >> 10 & 15) + 4; - if ((a - p << 3) + k < 14 + 3 * n) { - return!0; - } - for (var a = {numLiteralCodes:(e & 31) + 257, numDistanceCodes:(e >> 5 & 31) + 1, codeLengthTable:void 0, bitLengths:void 0, codesRead:0, dupBits:0}, e = e >> 14, k = k - 14, r = new Uint8Array(19), t = 0;t < n;++t) { - 3 > k && (e |= b[p++] << k, k += 8), r[m[t]] = e & 7, e >>= 3, k -= 3; - } - for (;19 > t;t++) { - r[m[t]] = 0; - } - a.bitLengths = new Uint8Array(a.numLiteralCodes + a.numDistanceCodes); - a.codeLengthTable = c(r); - this._block2State = a; - b = 3; - break; - default: - return this._error = "inflate: unsupported block type", !1; - } - this._isFinalBlock = !!(q & 1); - this._state = b; - this._bufferPosition = p; - this._bitBuffer = e; - this._bitLength = k; - return!1; - }; - b.prototype._decodeBlock0 = function() { - var b = this._bufferPosition, a = this._windowPosition, d = this._block0Read, e = 65794 - a, c = this._bufferSize - b, h = c < d, k = Math.min(e, c, d); - this._window.set(this._buffer.subarray(b, b + k), a); - this._windowPosition = a + k; - this._bufferPosition = b + k; - this._block0Read = d - k; - return d === k ? (this._state = 0, !1) : h && e < c; - }; - b.prototype._readBits = function(b) { - var a = this._bitBuffer, d = this._bitLength; - if (b > d) { - var e = this._bufferPosition, c = this._bufferSize; - do { - if (e >= c) { - return this._bufferPosition = e, this._bitBuffer = a, this._bitLength = d, -1; - } - a |= this._buffer[e++] << d; - d += 8; - } while (b > d); - this._bufferPosition = e; - } - this._bitBuffer = a >> b; - this._bitLength = d - b; - return a & (1 << b) - 1; - }; - b.prototype._readCode = function(b) { - var a = this._bitBuffer, d = this._bitLength, e = b.maxBits; - if (e > d) { - var c = this._bufferPosition, h = this._bufferSize; - do { - if (c >= h) { - return this._bufferPosition = c, this._bitBuffer = a, this._bitLength = d, -1; - } - a |= this._buffer[c++] << d; - d += 8; - } while (e > d); - this._bufferPosition = c; - } - b = b.codes[a & (1 << e) - 1]; - e = b >> 16; - if (b & 32768) { - return this._error = "inflate: invalid encoding", this._state = 6, -1; - } - this._bitBuffer = a >> e; - this._bitLength = d - e; - return b & 65535; - }; - b.prototype._decodeBlock2Pre = function() { - var b = this._block2State, a = b.numLiteralCodes + b.numDistanceCodes, d = b.bitLengths, e = b.codesRead, h = 0 < e ? d[e - 1] : 0, k = b.codeLengthTable, p; - if (0 < b.dupBits) { - p = this._readBits(b.dupBits); - if (0 > p) { - return!0; - } - for (;p--;) { - d[e++] = h; - } - b.dupBits = 0; - } - for (;e < a;) { - var q = this._readCode(k); - if (0 > q) { - return b.codesRead = e, !0; - } - if (16 > q) { - d[e++] = h = q; - } else { - var n; - switch(q) { - case 16: - n = 2; - p = 3; - q = h; - break; - case 17: - p = n = 3; - q = 0; - break; - case 18: - n = 7, p = 11, q = 0; - } - for (;p--;) { - d[e++] = q; - } - p = this._readBits(n); - if (0 > p) { - return b.codesRead = e, b.dupBits = n, !0; - } - for (;p--;) { - d[e++] = q; - } - h = q; - } - } - this._literalTable = c(d.subarray(0, b.numLiteralCodes)); - this._distanceTable = c(d.subarray(b.numLiteralCodes)); - this._state = 4; - this._block2State = null; - return!1; - }; - b.prototype._decodeBlock = function() { - var b = this._literalTable, d = this._distanceTable, c = this._window, h = this._windowPosition, k = this._copyState, q, n, t, f; - if (0 !== k.state) { - switch(k.state) { - case 1: - if (0 > (q = this._readBits(k.lenBits))) { - return!0; - } - k.len += q; - k.state = 2; - case 2: - if (0 > (q = this._readCode(d))) { - return!0; - } - k.distBits = a[q]; - k.dist = r[q]; - k.state = 3; - case 3: - q = 0; - if (0 < k.distBits && 0 > (q = this._readBits(k.distBits))) { - return!0; - } - f = k.dist + q; - n = k.len; - for (q = h - f;n--;) { - c[h++] = c[q++]; - } - k.state = 0; - if (65536 <= h) { - return this._windowPosition = h, !1; - } - break; - } - } - do { - q = this._readCode(b); - if (0 > q) { - return this._windowPosition = h, !0; - } - if (256 > q) { - c[h++] = q; - } else { - if (256 < q) { - this._windowPosition = h; - q -= 257; - t = e[q]; - n = p[q]; - q = 0 === t ? 0 : this._readBits(t); - if (0 > q) { - return k.state = 1, k.len = n, k.lenBits = t, !0; - } - n += q; - q = this._readCode(d); - if (0 > q) { - return k.state = 2, k.len = n, !0; - } - t = a[q]; - f = r[q]; - q = 0 === t ? 0 : this._readBits(t); - if (0 > q) { - return k.state = 3, k.len = n, k.dist = f, k.distBits = t, !0; - } - f += q; - for (q = h - f;n--;) { - c[h++] = c[q++]; - } - } else { - this._state = 0; - break; - } - } - } while (65536 > h); - this._windowPosition = h; - return!1; - }; - b.inflate = function(a, d) { - var e = new Uint8Array(d), c = 0, h = new b(!0); - h.onData = function(b) { - e.set(b, c); - c += b.length; - }; - h.push(a); - return e; - }; - return b; - }(); - f.Inflate = t; - var m, r, a, d, p, e, h, n = !1, q; - (function(b) { - b[b.WRITE = 0] = "WRITE"; - b[b.DONE = 1] = "DONE"; - b[b.ZLIB_HEADER = 2] = "ZLIB_HEADER"; - })(q || (q = {})); - var k = function() { - function b() { - this.a = 1; - this.b = 0; - } - b.prototype.update = function(b, a, d) { - for (var e = this.a, c = this.b;a < d;++a) { - e = (e + (b[a] & 255)) % 65521, c = (c + e) % 65521; - } - this.a = e; - this.b = c; - }; - b.prototype.getChecksum = function() { - return this.b << 16 | this.a; - }; - return b; - }(); - f.Adler32 = k; - q = function() { - function b(b) { - this._state = (this._writeZlibHeader = b) ? 2 : 0; - this._adler32 = b ? new k : null; - } - b.prototype.push = function(b) { - 2 === this._state && (this.onData(new Uint8Array([120, 156])), this._state = 0); - for (var a = b.length, d = new Uint8Array(a + 5 * Math.ceil(a / 65535)), e = 0, c = 0;65535 < a;) { - d.set(new Uint8Array([0, 255, 255, 0, 0]), e), e += 5, d.set(b.subarray(c, c + 65535), e), c += 65535, e += 65535, a -= 65535; - } - d.set(new Uint8Array([0, a & 255, a >> 8 & 255, ~a & 255, ~a >> 8 & 255]), e); - d.set(b.subarray(c, a), e + 5); - this.onData(d); - this._adler32 && this._adler32.update(b, 0, a); - }; - b.prototype.finish = function() { - this._state = 1; - this.onData(new Uint8Array([1, 0, 0, 255, 255])); - if (this._adler32) { - var b = this._adler32.getChecksum(); - this.onData(new Uint8Array([b & 255, b >> 8 & 255, b >> 16 & 255, b >>> 24 & 255])); - } - }; - return b; - }(); - f.Deflate = q; - })(f.ArrayUtilities || (f.ArrayUtilities = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - function c() { - m("throwEOFError"); - } - function t(a) { - return "string" === typeof a ? a : void 0 == a ? null : a + ""; - } - var m = f.Debug.notImplemented, r = f.StringUtilities.utf8decode, a = f.StringUtilities.utf8encode, d = f.NumberUtilities.clamp, p = function() { - return function(a, d, b) { - this.buffer = a; - this.length = d; - this.littleEndian = b; - }; - }(); - u.PlainObjectDataBuffer = p; - for (var e = new Uint32Array(33), h = 1, n = 0;32 >= h;h++) { - e[h] = n = n << 1 | 1; - } - h = function() { - function h(a) { - "undefined" === typeof a && (a = h.INITIAL_SIZE); - this._buffer || (this._buffer = new ArrayBuffer(a), this._position = this._length = 0, this._updateViews(), this._littleEndian = h._nativeLittleEndian, this._bitLength = this._bitBuffer = 0); - } - h.FromArrayBuffer = function(a, b) { - "undefined" === typeof b && (b = -1); - var d = Object.create(h.prototype); - d._buffer = a; - d._length = -1 === b ? a.byteLength : b; - d._position = 0; - d._updateViews(); - d._littleEndian = h._nativeLittleEndian; - d._bitBuffer = 0; - d._bitLength = 0; - return d; - }; - h.FromPlainObject = function(a) { - var b = h.FromArrayBuffer(a.buffer, a.length); - b._littleEndian = a.littleEndian; - return b; - }; - h.prototype.toPlainObject = function() { - return new p(this._buffer, this._length, this._littleEndian); - }; - h.prototype._updateViews = function() { - this._u8 = new Uint8Array(this._buffer); - 0 === (this._buffer.byteLength & 3) && (this._i32 = new Int32Array(this._buffer), this._f32 = new Float32Array(this._buffer)); - }; - h.prototype.getBytes = function() { - return new Uint8Array(this._buffer, 0, this._length); - }; - h.prototype._ensureCapacity = function(a) { - var b = this._buffer; - if (b.byteLength < a) { - for (var d = Math.max(b.byteLength, 1);d < a;) { - d *= 2; - } - a = h._arrayBufferPool.acquire(d); - d = this._u8; - this._buffer = a; - this._updateViews(); - this._u8.set(d); - h._arrayBufferPool.release(b); - } - }; - h.prototype.clear = function() { - this._position = this._length = 0; - }; - h.prototype.readBoolean = function() { - return 0 !== this.readUnsignedByte(); - }; - h.prototype.readByte = function() { - return this.readUnsignedByte() << 24 >> 24; - }; - h.prototype.readUnsignedByte = function() { - this._position + 1 > this._length && c(); - return this._u8[this._position++]; - }; - h.prototype.readBytes = function(a, b, d) { - "undefined" === typeof b && (b = 0); - "undefined" === typeof d && (d = 0); - var e = this._position; - b || (b = 0); - d || (d = this._length - e); - e + d > this._length && c(); - a.length < b + d && (a._ensureCapacity(b + d), a.length = b + d); - a._u8.set(new Uint8Array(this._buffer, e, d), b); - this._position += d; - }; - h.prototype.readShort = function() { - return this.readUnsignedShort() << 16 >> 16; - }; - h.prototype.readUnsignedShort = function() { - var a = this._u8, b = this._position; - b + 2 > this._length && c(); - var d = a[b + 0], a = a[b + 1]; - this._position = b + 2; - return this._littleEndian ? a << 8 | d : d << 8 | a; - }; - h.prototype.readInt = function() { - var a = this._u8, b = this._position; - b + 4 > this._length && c(); - var d = a[b + 0], e = a[b + 1], h = a[b + 2], a = a[b + 3]; - this._position = b + 4; - return this._littleEndian ? a << 24 | h << 16 | e << 8 | d : d << 24 | e << 16 | h << 8 | a; - }; - h.prototype.readUnsignedInt = function() { - return this.readInt() >>> 0; - }; - h.prototype.readFloat = function() { - var a = this._position; - a + 4 > this._length && c(); - this._position = a + 4; - if (this._littleEndian && 0 === (a & 3) && this._f32) { - return this._f32[a >> 2]; - } - var b = this._u8, d = f.IntegerUtilities.u8; - this._littleEndian ? (d[0] = b[a + 0], d[1] = b[a + 1], d[2] = b[a + 2], d[3] = b[a + 3]) : (d[3] = b[a + 0], d[2] = b[a + 1], d[1] = b[a + 2], d[0] = b[a + 3]); - return f.IntegerUtilities.f32[0]; - }; - h.prototype.readDouble = function() { - var a = this._u8, b = this._position; - b + 8 > this._length && c(); - var d = f.IntegerUtilities.u8; - this._littleEndian ? (d[0] = a[b + 0], d[1] = a[b + 1], d[2] = a[b + 2], d[3] = a[b + 3], d[4] = a[b + 4], d[5] = a[b + 5], d[6] = a[b + 6], d[7] = a[b + 7]) : (d[0] = a[b + 7], d[1] = a[b + 6], d[2] = a[b + 5], d[3] = a[b + 4], d[4] = a[b + 3], d[5] = a[b + 2], d[6] = a[b + 1], d[7] = a[b + 0]); - this._position = b + 8; - return f.IntegerUtilities.f64[0]; - }; - h.prototype.writeBoolean = function(a) { - this.writeByte(a ? 1 : 0); - }; - h.prototype.writeByte = function(a) { - var b = this._position + 1; - this._ensureCapacity(b); - this._u8[this._position++] = a; - b > this._length && (this._length = b); - }; - h.prototype.writeUnsignedByte = function(a) { - var b = this._position + 1; - this._ensureCapacity(b); - this._u8[this._position++] = a; - b > this._length && (this._length = b); - }; - h.prototype.writeRawBytes = function(a) { - var b = this._position + a.length; - this._ensureCapacity(b); - this._u8.set(a, this._position); - this._position = b; - b > this._length && (this._length = b); - }; - h.prototype.writeBytes = function(a, b, e) { - "undefined" === typeof b && (b = 0); - "undefined" === typeof e && (e = 0); - 2 > arguments.length && (b = 0); - 3 > arguments.length && (e = 0); - b !== d(b, 0, a.length) && m("throwRangeError"); - var h = b + e; - h !== d(h, 0, a.length) && m("throwRangeError"); - 0 === e && (e = a.length - b); - this.writeRawBytes(new Int8Array(a._buffer, b, e)); - }; - h.prototype.writeShort = function(a) { - this.writeUnsignedShort(a); - }; - h.prototype.writeUnsignedShort = function(a) { - var b = this._position; - this._ensureCapacity(b + 2); - var d = this._u8; - this._littleEndian ? (d[b + 0] = a, d[b + 1] = a >> 8) : (d[b + 0] = a >> 8, d[b + 1] = a); - this._position = b += 2; - b > this._length && (this._length = b); - }; - h.prototype.writeInt = function(a) { - this.writeUnsignedInt(a); - }; - h.prototype.write4Ints = function(a, b, d, e) { - this.write4UnsignedInts(a, b, d, e); - }; - h.prototype.writeUnsignedInt = function(a) { - var b = this._position; - this._ensureCapacity(b + 4); - if (this._littleEndian === h._nativeLittleEndian && 0 === (b & 3) && this._i32) { - this._i32[b >> 2] = a; - } else { - var d = this._u8; - this._littleEndian ? (d[b + 0] = a, d[b + 1] = a >> 8, d[b + 2] = a >> 16, d[b + 3] = a >> 24) : (d[b + 0] = a >> 24, d[b + 1] = a >> 16, d[b + 2] = a >> 8, d[b + 3] = a); - } - this._position = b += 4; - b > this._length && (this._length = b); - }; - h.prototype.write4UnsignedInts = function(a, b, d, e) { - var c = this._position; - this._ensureCapacity(c + 16); - this._littleEndian === h._nativeLittleEndian && 0 === (c & 3) && this._i32 ? (this._i32[(c >> 2) + 0] = a, this._i32[(c >> 2) + 1] = b, this._i32[(c >> 2) + 2] = d, this._i32[(c >> 2) + 3] = e, this._position = c += 16, c > this._length && (this._length = c)) : (this.writeUnsignedInt(a), this.writeUnsignedInt(b), this.writeUnsignedInt(d), this.writeUnsignedInt(e)); - }; - h.prototype.writeFloat = function(a) { - var b = this._position; - this._ensureCapacity(b + 4); - if (this._littleEndian === h._nativeLittleEndian && 0 === (b & 3) && this._f32) { - this._f32[b >> 2] = a; - } else { - var d = this._u8; - f.IntegerUtilities.f32[0] = a; - a = f.IntegerUtilities.u8; - this._littleEndian ? (d[b + 0] = a[0], d[b + 1] = a[1], d[b + 2] = a[2], d[b + 3] = a[3]) : (d[b + 0] = a[3], d[b + 1] = a[2], d[b + 2] = a[1], d[b + 3] = a[0]); - } - this._position = b += 4; - b > this._length && (this._length = b); - }; - h.prototype.write6Floats = function(a, b, d, e, c, p) { - var n = this._position; - this._ensureCapacity(n + 24); - this._littleEndian === h._nativeLittleEndian && 0 === (n & 3) && this._f32 ? (this._f32[(n >> 2) + 0] = a, this._f32[(n >> 2) + 1] = b, this._f32[(n >> 2) + 2] = d, this._f32[(n >> 2) + 3] = e, this._f32[(n >> 2) + 4] = c, this._f32[(n >> 2) + 5] = p, this._position = n += 24, n > this._length && (this._length = n)) : (this.writeFloat(a), this.writeFloat(b), this.writeFloat(d), this.writeFloat(e), this.writeFloat(c), this.writeFloat(p)); - }; - h.prototype.writeDouble = function(a) { - var b = this._position; - this._ensureCapacity(b + 8); - var d = this._u8; - f.IntegerUtilities.f64[0] = a; - a = f.IntegerUtilities.u8; - this._littleEndian ? (d[b + 0] = a[0], d[b + 1] = a[1], d[b + 2] = a[2], d[b + 3] = a[3], d[b + 4] = a[4], d[b + 5] = a[5], d[b + 6] = a[6], d[b + 7] = a[7]) : (d[b + 0] = a[7], d[b + 1] = a[6], d[b + 2] = a[5], d[b + 3] = a[4], d[b + 4] = a[3], d[b + 5] = a[2], d[b + 6] = a[1], d[b + 7] = a[0]); - this._position = b += 8; - b > this._length && (this._length = b); - }; - h.prototype.readRawBytes = function() { - return new Int8Array(this._buffer, 0, this._length); - }; - h.prototype.writeUTF = function(a) { - a = t(a); - a = r(a); - this.writeShort(a.length); - this.writeRawBytes(a); - }; - h.prototype.writeUTFBytes = function(a) { - a = t(a); - a = r(a); - this.writeRawBytes(a); - }; - h.prototype.readUTF = function() { - return this.readUTFBytes(this.readShort()); - }; - h.prototype.readUTFBytes = function(d) { - d >>>= 0; - var b = this._position; - b + d > this._length && c(); - this._position += d; - return a(new Int8Array(this._buffer, b, d)); - }; - Object.defineProperty(h.prototype, "length", {get:function() { - return this._length; - }, set:function(a) { - a >>>= 0; - a > this._buffer.byteLength && this._ensureCapacity(a); - this._length = a; - this._position = d(this._position, 0, this._length); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(h.prototype, "bytesAvailable", {get:function() { - return this._length - this._position; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(h.prototype, "position", {get:function() { - return this._position; - }, set:function(a) { - this._position = a >>> 0; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(h.prototype, "buffer", {get:function() { - return this._buffer; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(h.prototype, "bytes", {get:function() { - return this._u8; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(h.prototype, "ints", {get:function() { - return this._i32; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(h.prototype, "objectEncoding", {get:function() { - return this._objectEncoding; - }, set:function(a) { - this._objectEncoding = a >>> 0; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(h.prototype, "endian", {get:function() { - return this._littleEndian ? "littleEndian" : "bigEndian"; - }, set:function(a) { - a = t(a); - this._littleEndian = "auto" === a ? h._nativeLittleEndian : "littleEndian" === a; - }, enumerable:!0, configurable:!0}); - h.prototype.toString = function() { - return a(new Int8Array(this._buffer, 0, this._length)); - }; - h.prototype.toBlob = function() { - return new Blob([new Int8Array(this._buffer, this._position, this._length)]); - }; - h.prototype.writeMultiByte = function(a, b) { - m("packageInternal flash.utils.ObjectOutput::writeMultiByte"); - }; - h.prototype.readMultiByte = function(a, b) { - m("packageInternal flash.utils.ObjectInput::readMultiByte"); - }; - h.prototype.getValue = function(a) { - a |= 0; - return a >= this._length ? void 0 : this._u8[a]; - }; - h.prototype.setValue = function(a, b) { - a |= 0; - var d = a + 1; - this._ensureCapacity(d); - this._u8[a] = b; - d > this._length && (this._length = d); - }; - h.prototype.readFixed = function() { - return this.readInt() / 65536; - }; - h.prototype.readFixed8 = function() { - return this.readShort() / 256; - }; - h.prototype.readFloat16 = function() { - var a = this.readUnsignedShort(), b = a >> 15 ? -1 : 1, d = (a & 31744) >> 10, a = a & 1023; - return d ? 31 === d ? a ? NaN : Infinity * b : b * Math.pow(2, d - 15) * (1 + a / 1024) : a / 1024 * Math.pow(2, -14) * b; - }; - h.prototype.readEncodedU32 = function() { - var a = this.readUnsignedByte(); - if (!(a & 128)) { - return a; - } - a = a & 127 | this.readUnsignedByte() << 7; - if (!(a & 16384)) { - return a; - } - a = a & 16383 | this.readUnsignedByte() << 14; - if (!(a & 2097152)) { - return a; - } - a = a & 2097151 | this.readUnsignedByte() << 21; - return a & 268435456 ? a & 268435455 | this.readUnsignedByte() << 28 : a; - }; - h.prototype.readBits = function(a) { - return this.readUnsignedBits(a) << 32 - a >> 32 - a; - }; - h.prototype.readUnsignedBits = function(a) { - for (var b = this._bitBuffer, d = this._bitLength;a > d;) { - b = b << 8 | this.readUnsignedByte(), d += 8; - } - d -= a; - a = b >>> d & e[a]; - this._bitBuffer = b; - this._bitLength = d; - return a; - }; - h.prototype.readFixedBits = function(a) { - return this.readBits(a) / 65536; - }; - h.prototype.readString = function(d) { - var b = this._position; - if (d) { - b + d > this._length && c(), this._position += d; - } else { - d = 0; - for (var e = b;e < this._length && this._u8[e];e++) { - d++; - } - this._position += d + 1; - } - return a(new Int8Array(this._buffer, b, d)); - }; - h.prototype.align = function() { - this._bitLength = this._bitBuffer = 0; - }; - h.prototype._compress = function(a) { - a = t(a); - switch(a) { - case "zlib": - a = new u.Deflate(!0); - break; - case "deflate": - a = new u.Deflate(!1); - break; - default: - return; - } - var b = new h; - a.onData = b.writeRawBytes.bind(b); - a.push(this._u8.subarray(0, this._length)); - a.finish(); - this._ensureCapacity(b._u8.length); - this._u8.set(b._u8); - this.length = b.length; - this._position = 0; - }; - h.prototype._uncompress = function(a) { - a = t(a); - switch(a) { - case "zlib": - a = new u.Inflate(!0); - break; - case "deflate": - a = new u.Inflate(!1); - break; - default: - return; - } - var b = new h; - a.onData = b.writeRawBytes.bind(b); - a.push(this._u8.subarray(0, this._length)); - a.error && m("throwCompressedDataError"); - this._ensureCapacity(b._u8.length); - this._u8.set(b._u8); - this.length = b.length; - this._position = 0; - }; - h._nativeLittleEndian = 1 === (new Int8Array((new Int32Array([1])).buffer))[0]; - h.INITIAL_SIZE = 128; - h._arrayBufferPool = new f.ArrayBufferPool; - return h; - }(); - u.DataBuffer = h; - })(f.ArrayUtilities || (f.ArrayUtilities = {})); -})(Shumway || (Shumway = {})); -(function(f) { - var u = f.ArrayUtilities.DataBuffer, c = f.ArrayUtilities.ensureTypedArrayCapacity, t = f.Debug.assert; - (function(a) { - a[a.BeginSolidFill = 1] = "BeginSolidFill"; - a[a.BeginGradientFill = 2] = "BeginGradientFill"; - a[a.BeginBitmapFill = 3] = "BeginBitmapFill"; - a[a.EndFill = 4] = "EndFill"; - a[a.LineStyleSolid = 5] = "LineStyleSolid"; - a[a.LineStyleGradient = 6] = "LineStyleGradient"; - a[a.LineStyleBitmap = 7] = "LineStyleBitmap"; - a[a.LineEnd = 8] = "LineEnd"; - a[a.MoveTo = 9] = "MoveTo"; - a[a.LineTo = 10] = "LineTo"; - a[a.CurveTo = 11] = "CurveTo"; - a[a.CubicCurveTo = 12] = "CubicCurveTo"; - })(f.PathCommand || (f.PathCommand = {})); - (function(a) { - a[a.Linear = 16] = "Linear"; - a[a.Radial = 18] = "Radial"; - })(f.GradientType || (f.GradientType = {})); - (function(a) { - a[a.Pad = 0] = "Pad"; - a[a.Reflect = 1] = "Reflect"; - a[a.Repeat = 2] = "Repeat"; - })(f.GradientSpreadMethod || (f.GradientSpreadMethod = {})); - (function(a) { - a[a.RGB = 0] = "RGB"; - a[a.LinearRGB = 1] = "LinearRGB"; - })(f.GradientInterpolationMethod || (f.GradientInterpolationMethod = {})); - (function(a) { - a[a.None = 0] = "None"; - a[a.Normal = 1] = "Normal"; - a[a.Vertical = 2] = "Vertical"; - a[a.Horizontal = 3] = "Horizontal"; - })(f.LineScaleMode || (f.LineScaleMode = {})); - var m = function() { - return function(a, d, c, e, h, n, r, t, b) { - this.commands = a; - this.commandsPosition = d; - this.coordinates = c; - this.coordinatesPosition = e; - this.morphCoordinates = h; - this.styles = n; - this.stylesLength = r; - this.hasFills = t; - this.hasLines = b; - }; - }(); - f.PlainObjectShapeData = m; - var r; - (function(a) { - a[a.Commands = 32] = "Commands"; - a[a.Coordinates = 128] = "Coordinates"; - a[a.Styles = 16] = "Styles"; - })(r || (r = {})); - r = function() { - function a(a) { - "undefined" === typeof a && (a = !0); - a && this.clear(); - } - a.FromPlainObject = function(d) { - var c = new a(!1); - c.commands = d.commands; - c.coordinates = d.coordinates; - c.morphCoordinates = d.morphCoordinates; - c.commandsPosition = d.commandsPosition; - c.coordinatesPosition = d.coordinatesPosition; - c.styles = u.FromArrayBuffer(d.styles, d.stylesLength); - c.styles.endian = "auto"; - c.hasFills = d.hasFills; - c.hasLines = d.hasLines; - return c; - }; - a.prototype.moveTo = function(a, c) { - this.ensurePathCapacities(1, 2); - this.commands[this.commandsPosition++] = 9; - this.coordinates[this.coordinatesPosition++] = a; - this.coordinates[this.coordinatesPosition++] = c; - }; - a.prototype.lineTo = function(a, c) { - this.ensurePathCapacities(1, 2); - this.commands[this.commandsPosition++] = 10; - this.coordinates[this.coordinatesPosition++] = a; - this.coordinates[this.coordinatesPosition++] = c; - }; - a.prototype.curveTo = function(a, c, e, h) { - this.ensurePathCapacities(1, 4); - this.commands[this.commandsPosition++] = 11; - this.coordinates[this.coordinatesPosition++] = a; - this.coordinates[this.coordinatesPosition++] = c; - this.coordinates[this.coordinatesPosition++] = e; - this.coordinates[this.coordinatesPosition++] = h; - }; - a.prototype.cubicCurveTo = function(a, c, e, h, n, r) { - this.ensurePathCapacities(1, 6); - this.commands[this.commandsPosition++] = 12; - this.coordinates[this.coordinatesPosition++] = a; - this.coordinates[this.coordinatesPosition++] = c; - this.coordinates[this.coordinatesPosition++] = e; - this.coordinates[this.coordinatesPosition++] = h; - this.coordinates[this.coordinatesPosition++] = n; - this.coordinates[this.coordinatesPosition++] = r; - }; - a.prototype.beginFill = function(a) { - this.ensurePathCapacities(1, 0); - this.commands[this.commandsPosition++] = 1; - this.styles.writeUnsignedInt(a); - this.hasFills = !0; - }; - a.prototype.endFill = function() { - this.ensurePathCapacities(1, 0); - this.commands[this.commandsPosition++] = 4; - }; - a.prototype.endLine = function() { - this.ensurePathCapacities(1, 0); - this.commands[this.commandsPosition++] = 8; - }; - a.prototype.lineStyle = function(a, c, e, h, n, r, k) { - t(a === (a | 0), 0 <= a && 5100 >= a); - this.ensurePathCapacities(2, 0); - this.commands[this.commandsPosition++] = 5; - this.coordinates[this.coordinatesPosition++] = a; - a = this.styles; - a.writeUnsignedInt(c); - a.writeBoolean(e); - a.writeUnsignedByte(h); - a.writeUnsignedByte(n); - a.writeUnsignedByte(r); - a.writeUnsignedByte(k); - this.hasLines = !0; - }; - a.prototype.beginBitmap = function(a, c, e, h, n) { - t(3 === a || 7 === a); - this.ensurePathCapacities(1, 0); - this.commands[this.commandsPosition++] = a; - a = this.styles; - a.writeUnsignedInt(c); - this._writeStyleMatrix(e); - a.writeBoolean(h); - a.writeBoolean(n); - this.hasFills = !0; - }; - a.prototype.beginGradient = function(a, c, e, h, n, r, k, b) { - t(2 === a || 6 === a); - this.ensurePathCapacities(1, 0); - this.commands[this.commandsPosition++] = a; - a = this.styles; - a.writeUnsignedByte(h); - t(b === (b | 0)); - a.writeShort(b); - this._writeStyleMatrix(n); - h = c.length; - a.writeByte(h); - for (n = 0;n < h;n++) { - a.writeUnsignedByte(e[n]), a.writeUnsignedInt(c[n]); - } - a.writeUnsignedByte(r); - a.writeUnsignedByte(k); - this.hasFills = !0; - }; - a.prototype.writeCommandAndCoordinates = function(a, c, e) { - this.ensurePathCapacities(1, 2); - this.commands[this.commandsPosition++] = a; - this.coordinates[this.coordinatesPosition++] = c; - this.coordinates[this.coordinatesPosition++] = e; - }; - a.prototype.writeCoordinates = function(a, c) { - this.ensurePathCapacities(0, 2); - this.coordinates[this.coordinatesPosition++] = a; - this.coordinates[this.coordinatesPosition++] = c; - }; - a.prototype.writeMorphCoordinates = function(a, p) { - this.morphCoordinates = c(this.morphCoordinates, this.coordinatesPosition); - this.morphCoordinates[this.coordinatesPosition - 2] = a; - this.morphCoordinates[this.coordinatesPosition - 1] = p; - }; - a.prototype.clear = function() { - this.commandsPosition = this.coordinatesPosition = 0; - this.commands = new Uint8Array(32); - this.coordinates = new Int32Array(128); - this.styles = new u(16); - this.styles.endian = "auto"; - this.hasFills = this.hasLines = !1; - }; - a.prototype.isEmpty = function() { - return 0 === this.commandsPosition; - }; - a.prototype.clone = function() { - var d = new a(!1); - d.commands = new Uint8Array(this.commands); - d.commandsPosition = this.commandsPosition; - d.coordinates = new Int32Array(this.coordinates); - d.coordinatesPosition = this.coordinatesPosition; - d.styles = new u(this.styles.length); - d.styles.writeRawBytes(this.styles.bytes); - d.hasFills = this.hasFills; - d.hasLines = this.hasLines; - return d; - }; - a.prototype.toPlainObject = function() { - return new m(this.commands, this.commandsPosition, this.coordinates, this.coordinatesPosition, this.morphCoordinates, this.styles.buffer, this.styles.length, this.hasFills, this.hasLines); - }; - Object.defineProperty(a.prototype, "buffers", {get:function() { - var a = [this.commands.buffer, this.coordinates.buffer, this.styles.buffer]; - this.morphCoordinates && a.push(this.morphCoordinates.buffer); - return a; - }, enumerable:!0, configurable:!0}); - a.prototype._writeStyleMatrix = function(a) { - var c = this.styles; - c.writeFloat(a.a); - c.writeFloat(a.b); - c.writeFloat(a.c); - c.writeFloat(a.d); - c.writeFloat(a.tx); - c.writeFloat(a.ty); - }; - a.prototype.ensurePathCapacities = function(a, p) { - this.commands = c(this.commands, this.commandsPosition + a); - this.coordinates = c(this.coordinates, this.coordinatesPosition + p); - }; - return a; - }(); - f.ShapeData = r; -})(Shumway || (Shumway = {})); -(function(f) { - (function(f) { - (function(c) { - (function(c) { - c[c.CODE_END = 0] = "CODE_END"; - c[c.CODE_SHOW_FRAME = 1] = "CODE_SHOW_FRAME"; - c[c.CODE_DEFINE_SHAPE = 2] = "CODE_DEFINE_SHAPE"; - c[c.CODE_FREE_CHARACTER = 3] = "CODE_FREE_CHARACTER"; - c[c.CODE_PLACE_OBJECT = 4] = "CODE_PLACE_OBJECT"; - c[c.CODE_REMOVE_OBJECT = 5] = "CODE_REMOVE_OBJECT"; - c[c.CODE_DEFINE_BITS = 6] = "CODE_DEFINE_BITS"; - c[c.CODE_DEFINE_BUTTON = 7] = "CODE_DEFINE_BUTTON"; - c[c.CODE_JPEG_TABLES = 8] = "CODE_JPEG_TABLES"; - c[c.CODE_SET_BACKGROUND_COLOR = 9] = "CODE_SET_BACKGROUND_COLOR"; - c[c.CODE_DEFINE_FONT = 10] = "CODE_DEFINE_FONT"; - c[c.CODE_DEFINE_TEXT = 11] = "CODE_DEFINE_TEXT"; - c[c.CODE_DO_ACTION = 12] = "CODE_DO_ACTION"; - c[c.CODE_DEFINE_FONT_INFO = 13] = "CODE_DEFINE_FONT_INFO"; - c[c.CODE_DEFINE_SOUND = 14] = "CODE_DEFINE_SOUND"; - c[c.CODE_START_SOUND = 15] = "CODE_START_SOUND"; - c[c.CODE_STOP_SOUND = 16] = "CODE_STOP_SOUND"; - c[c.CODE_DEFINE_BUTTON_SOUND = 17] = "CODE_DEFINE_BUTTON_SOUND"; - c[c.CODE_SOUND_STREAM_HEAD = 18] = "CODE_SOUND_STREAM_HEAD"; - c[c.CODE_SOUND_STREAM_BLOCK = 19] = "CODE_SOUND_STREAM_BLOCK"; - c[c.CODE_DEFINE_BITS_LOSSLESS = 20] = "CODE_DEFINE_BITS_LOSSLESS"; - c[c.CODE_DEFINE_BITS_JPEG2 = 21] = "CODE_DEFINE_BITS_JPEG2"; - c[c.CODE_DEFINE_SHAPE2 = 22] = "CODE_DEFINE_SHAPE2"; - c[c.CODE_DEFINE_BUTTON_CXFORM = 23] = "CODE_DEFINE_BUTTON_CXFORM"; - c[c.CODE_PROTECT = 24] = "CODE_PROTECT"; - c[c.CODE_PATHS_ARE_POSTSCRIPT = 25] = "CODE_PATHS_ARE_POSTSCRIPT"; - c[c.CODE_PLACE_OBJECT2 = 26] = "CODE_PLACE_OBJECT2"; - c[c.CODE_REMOVE_OBJECT2 = 28] = "CODE_REMOVE_OBJECT2"; - c[c.CODE_SYNC_FRAME = 29] = "CODE_SYNC_FRAME"; - c[c.CODE_FREE_ALL = 31] = "CODE_FREE_ALL"; - c[c.CODE_DEFINE_SHAPE3 = 32] = "CODE_DEFINE_SHAPE3"; - c[c.CODE_DEFINE_TEXT2 = 33] = "CODE_DEFINE_TEXT2"; - c[c.CODE_DEFINE_BUTTON2 = 34] = "CODE_DEFINE_BUTTON2"; - c[c.CODE_DEFINE_BITS_JPEG3 = 35] = "CODE_DEFINE_BITS_JPEG3"; - c[c.CODE_DEFINE_BITS_LOSSLESS2 = 36] = "CODE_DEFINE_BITS_LOSSLESS2"; - c[c.CODE_DEFINE_EDIT_TEXT = 37] = "CODE_DEFINE_EDIT_TEXT"; - c[c.CODE_DEFINE_VIDEO = 38] = "CODE_DEFINE_VIDEO"; - c[c.CODE_DEFINE_SPRITE = 39] = "CODE_DEFINE_SPRITE"; - c[c.CODE_NAME_CHARACTER = 40] = "CODE_NAME_CHARACTER"; - c[c.CODE_PRODUCT_INFO = 41] = "CODE_PRODUCT_INFO"; - c[c.CODE_DEFINE_TEXT_FORMAT = 42] = "CODE_DEFINE_TEXT_FORMAT"; - c[c.CODE_FRAME_LABEL = 43] = "CODE_FRAME_LABEL"; - c[c.CODE_DEFINE_BEHAVIOUR = 44] = "CODE_DEFINE_BEHAVIOUR"; - c[c.CODE_SOUND_STREAM_HEAD2 = 45] = "CODE_SOUND_STREAM_HEAD2"; - c[c.CODE_DEFINE_MORPH_SHAPE = 46] = "CODE_DEFINE_MORPH_SHAPE"; - c[c.CODE_FRAME_TAG = 47] = "CODE_FRAME_TAG"; - c[c.CODE_DEFINE_FONT2 = 48] = "CODE_DEFINE_FONT2"; - c[c.CODE_GEN_COMMAND = 49] = "CODE_GEN_COMMAND"; - c[c.CODE_DEFINE_COMMAND_OBJ = 50] = "CODE_DEFINE_COMMAND_OBJ"; - c[c.CODE_CHARACTER_SET = 51] = "CODE_CHARACTER_SET"; - c[c.CODE_FONT_REF = 52] = "CODE_FONT_REF"; - c[c.CODE_DEFINE_FUNCTION = 53] = "CODE_DEFINE_FUNCTION"; - c[c.CODE_PLACE_FUNCTION = 54] = "CODE_PLACE_FUNCTION"; - c[c.CODE_GEN_TAG_OBJECTS = 55] = "CODE_GEN_TAG_OBJECTS"; - c[c.CODE_EXPORT_ASSETS = 56] = "CODE_EXPORT_ASSETS"; - c[c.CODE_IMPORT_ASSETS = 57] = "CODE_IMPORT_ASSETS"; - c[c.CODE_ENABLE_DEBUGGER = 58] = "CODE_ENABLE_DEBUGGER"; - c[c.CODE_DO_INIT_ACTION = 59] = "CODE_DO_INIT_ACTION"; - c[c.CODE_DEFINE_VIDEO_STREAM = 60] = "CODE_DEFINE_VIDEO_STREAM"; - c[c.CODE_VIDEO_FRAME = 61] = "CODE_VIDEO_FRAME"; - c[c.CODE_DEFINE_FONT_INFO2 = 62] = "CODE_DEFINE_FONT_INFO2"; - c[c.CODE_DEBUG_ID = 63] = "CODE_DEBUG_ID"; - c[c.CODE_ENABLE_DEBUGGER2 = 64] = "CODE_ENABLE_DEBUGGER2"; - c[c.CODE_SCRIPT_LIMITS = 65] = "CODE_SCRIPT_LIMITS"; - c[c.CODE_SET_TAB_INDEX = 66] = "CODE_SET_TAB_INDEX"; - c[c.CODE_FILE_ATTRIBUTES = 69] = "CODE_FILE_ATTRIBUTES"; - c[c.CODE_PLACE_OBJECT3 = 70] = "CODE_PLACE_OBJECT3"; - c[c.CODE_IMPORT_ASSETS2 = 71] = "CODE_IMPORT_ASSETS2"; - c[c.CODE_DO_ABC_ = 72] = "CODE_DO_ABC_"; - c[c.CODE_DEFINE_FONT_ALIGN_ZONES = 73] = "CODE_DEFINE_FONT_ALIGN_ZONES"; - c[c.CODE_CSM_TEXT_SETTINGS = 74] = "CODE_CSM_TEXT_SETTINGS"; - c[c.CODE_DEFINE_FONT3 = 75] = "CODE_DEFINE_FONT3"; - c[c.CODE_SYMBOL_CLASS = 76] = "CODE_SYMBOL_CLASS"; - c[c.CODE_METADATA = 77] = "CODE_METADATA"; - c[c.CODE_DEFINE_SCALING_GRID = 78] = "CODE_DEFINE_SCALING_GRID"; - c[c.CODE_DO_ABC = 82] = "CODE_DO_ABC"; - c[c.CODE_DEFINE_SHAPE4 = 83] = "CODE_DEFINE_SHAPE4"; - c[c.CODE_DEFINE_MORPH_SHAPE2 = 84] = "CODE_DEFINE_MORPH_SHAPE2"; - c[c.CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA = 86] = "CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA"; - c[c.CODE_DEFINE_BINARY_DATA = 87] = "CODE_DEFINE_BINARY_DATA"; - c[c.CODE_DEFINE_FONT_NAME = 88] = "CODE_DEFINE_FONT_NAME"; - c[c.CODE_START_SOUND2 = 89] = "CODE_START_SOUND2"; - c[c.CODE_DEFINE_BITS_JPEG4 = 90] = "CODE_DEFINE_BITS_JPEG4"; - c[c.CODE_DEFINE_FONT4 = 91] = "CODE_DEFINE_FONT4"; - })(c.SwfTag || (c.SwfTag = {})); - (function(c) { - c[c.Reserved = 2048] = "Reserved"; - c[c.OpaqueBackground = 1024] = "OpaqueBackground"; - c[c.HasVisible = 512] = "HasVisible"; - c[c.HasImage = 256] = "HasImage"; - c[c.HasClassName = 2048] = "HasClassName"; - c[c.HasCacheAsBitmap = 1024] = "HasCacheAsBitmap"; - c[c.HasBlendMode = 512] = "HasBlendMode"; - c[c.HasFilterList = 256] = "HasFilterList"; - c[c.HasClipActions = 128] = "HasClipActions"; - c[c.HasClipDepth = 64] = "HasClipDepth"; - c[c.HasName = 32] = "HasName"; - c[c.HasRatio = 16] = "HasRatio"; - c[c.HasColorTransform = 8] = "HasColorTransform"; - c[c.HasMatrix = 4] = "HasMatrix"; - c[c.HasCharacter = 2] = "HasCharacter"; - c[c.Move = 1] = "Move"; - })(c.PlaceObjectFlags || (c.PlaceObjectFlags = {})); - })(f.Parser || (f.Parser = {})); - })(f.SWF || (f.SWF = {})); -})(Shumway || (Shumway = {})); -(function(f) { - var u = f.Debug.unexpected, c = function() { - function c(f, r, a, d) { - this.url = f; - this.method = r; - this.mimeType = a; - this.data = d; - } - c.prototype.readAll = function(c, r) { - var a = this.url, d = new XMLHttpRequest({mozSystem:!0}); - d.open(this.method || "GET", this.url, !0); - d.responseType = "arraybuffer"; - c && (d.onprogress = function(a) { - c(d.response, a.loaded, a.total); - }); - d.onreadystatechange = function(c) { - 4 === d.readyState && (200 !== d.status && 0 !== d.status || null === d.response ? (u("Path: " + a + " not found."), r(null, d.statusText)) : r(d.response)); - }; - this.mimeType && d.setRequestHeader("Content-Type", this.mimeType); - d.send(this.data || null); - }; - c.prototype.readAsync = function(c, r, a, d, p) { - var e = new XMLHttpRequest({mozSystem:!0}), h = this.url, n = 0, q = 0; - e.open(this.method || "GET", h, !0); - e.responseType = "moz-chunked-arraybuffer"; - var k = "moz-chunked-arraybuffer" !== e.responseType; - k && (e.responseType = "arraybuffer"); - e.onprogress = function(b) { - k || (n = b.loaded, q = b.total, c(new Uint8Array(e.response), {loaded:n, total:q})); - }; - e.onreadystatechange = function(b) { - 2 === e.readyState && p && p(h, e.status, e.getAllResponseHeaders()); - 4 === e.readyState && (200 !== e.status && 0 !== e.status || null === e.response && (0 === q || n !== q) ? r(e.statusText) : (k && (b = e.response, c(new Uint8Array(b), {loaded:0, total:b.byteLength})), d && d())); - }; - this.mimeType && e.setRequestHeader("Content-Type", this.mimeType); - e.send(this.data || null); - a && a(); - }; - return c; - }(); - f.BinaryFileReader = c; -})(Shumway || (Shumway = {})); -(function(f) { - (function(f) { - (function(c) { - c[c.Objects = 0] = "Objects"; - c[c.References = 1] = "References"; - })(f.RemotingPhase || (f.RemotingPhase = {})); - (function(c) { - c[c.HasMatrix = 1] = "HasMatrix"; - c[c.HasBounds = 2] = "HasBounds"; - c[c.HasChildren = 4] = "HasChildren"; - c[c.HasColorTransform = 8] = "HasColorTransform"; - c[c.HasClipRect = 16] = "HasClipRect"; - c[c.HasMiscellaneousProperties = 32] = "HasMiscellaneousProperties"; - c[c.HasMask = 64] = "HasMask"; - c[c.HasClip = 128] = "HasClip"; - })(f.MessageBits || (f.MessageBits = {})); - (function(c) { - c[c.None = 0] = "None"; - c[c.Asset = 134217728] = "Asset"; - })(f.IDMask || (f.IDMask = {})); - (function(c) { - c[c.EOF = 0] = "EOF"; - c[c.UpdateFrame = 100] = "UpdateFrame"; - c[c.UpdateGraphics = 101] = "UpdateGraphics"; - c[c.UpdateBitmapData = 102] = "UpdateBitmapData"; - c[c.UpdateTextContent = 103] = "UpdateTextContent"; - c[c.UpdateStage = 104] = "UpdateStage"; - c[c.UpdateNetStream = 105] = "UpdateNetStream"; - c[c.RequestBitmapData = 106] = "RequestBitmapData"; - c[c.DecodeImage = 107] = "DecodeImage"; - c[c.DecodeImageResponse = 108] = "DecodeImageResponse"; - c[c.RegisterFont = 200] = "RegisterFont"; - c[c.DrawToBitmap = 201] = "DrawToBitmap"; - c[c.MouseEvent = 300] = "MouseEvent"; - c[c.KeyboardEvent = 301] = "KeyboardEvent"; - c[c.FocusEvent = 302] = "FocusEvent"; - })(f.MessageTag || (f.MessageTag = {})); - (function(c) { - c[c.Identity = 0] = "Identity"; - c[c.AlphaMultiplierOnly = 1] = "AlphaMultiplierOnly"; - c[c.All = 2] = "All"; - })(f.ColorTransformEncoding || (f.ColorTransformEncoding = {})); - f.MouseEventNames = ["click", "dblclick", "mousedown", "mousemove", "mouseup"]; - f.KeyboardEventNames = ["keydown", "keypress", "keyup"]; - (function(c) { - c[c.CtrlKey = 1] = "CtrlKey"; - c[c.AltKey = 2] = "AltKey"; - c[c.ShiftKey = 4] = "ShiftKey"; - })(f.KeyboardEventFlags || (f.KeyboardEventFlags = {})); - (function(c) { - c[c.DocumentHidden = 0] = "DocumentHidden"; - c[c.DocumentVisible = 1] = "DocumentVisible"; - c[c.WindowBlur = 2] = "WindowBlur"; - c[c.WindowFocus = 3] = "WindowFocus"; - })(f.FocusEventType || (f.FocusEventType = {})); - })(f.Remoting || (f.Remoting = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(f) { - (function(c) { - var f = function() { - function c() { - } - c.toRGBA = function(a, d, c, e) { - "undefined" === typeof e && (e = 1); - return "rgba(" + a + "," + d + "," + c + "," + e + ")"; - }; - return c; - }(); - c.UI = f; - var m = function() { - function c() { - } - c.prototype.tabToolbar = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(37, 44, 51, a); - }; - c.prototype.toolbars = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(52, 60, 69, a); - }; - c.prototype.selectionBackground = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(29, 79, 115, a); - }; - c.prototype.selectionText = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(245, 247, 250, a); - }; - c.prototype.splitters = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(0, 0, 0, a); - }; - c.prototype.bodyBackground = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(17, 19, 21, a); - }; - c.prototype.sidebarBackground = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(24, 29, 32, a); - }; - c.prototype.attentionBackground = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(161, 134, 80, a); - }; - c.prototype.bodyText = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(143, 161, 178, a); - }; - c.prototype.foregroundTextGrey = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(182, 186, 191, a); - }; - c.prototype.contentTextHighContrast = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(169, 186, 203, a); - }; - c.prototype.contentTextGrey = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(143, 161, 178, a); - }; - c.prototype.contentTextDarkGrey = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(95, 115, 135, a); - }; - c.prototype.blueHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(70, 175, 227, a); - }; - c.prototype.purpleHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(107, 122, 187, a); - }; - c.prototype.pinkHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(223, 128, 255, a); - }; - c.prototype.redHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(235, 83, 104, a); - }; - c.prototype.orangeHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(217, 102, 41, a); - }; - c.prototype.lightOrangeHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(217, 155, 40, a); - }; - c.prototype.greenHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(112, 191, 83, a); - }; - c.prototype.blueGreyHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(94, 136, 176, a); - }; - return c; - }(); - c.UIThemeDark = m; - m = function() { - function c() { - } - c.prototype.tabToolbar = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(235, 236, 237, a); - }; - c.prototype.toolbars = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(240, 241, 242, a); - }; - c.prototype.selectionBackground = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(76, 158, 217, a); - }; - c.prototype.selectionText = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(245, 247, 250, a); - }; - c.prototype.splitters = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(170, 170, 170, a); - }; - c.prototype.bodyBackground = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(252, 252, 252, a); - }; - c.prototype.sidebarBackground = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(247, 247, 247, a); - }; - c.prototype.attentionBackground = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(161, 134, 80, a); - }; - c.prototype.bodyText = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(24, 25, 26, a); - }; - c.prototype.foregroundTextGrey = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(88, 89, 89, a); - }; - c.prototype.contentTextHighContrast = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(41, 46, 51, a); - }; - c.prototype.contentTextGrey = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(143, 161, 178, a); - }; - c.prototype.contentTextDarkGrey = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(102, 115, 128, a); - }; - c.prototype.blueHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(0, 136, 204, a); - }; - c.prototype.purpleHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(91, 95, 255, a); - }; - c.prototype.pinkHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(184, 46, 229, a); - }; - c.prototype.redHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(237, 38, 85, a); - }; - c.prototype.orangeHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(241, 60, 0, a); - }; - c.prototype.lightOrangeHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(217, 126, 0, a); - }; - c.prototype.greenHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(44, 187, 15, a); - }; - c.prototype.blueGreyHighlight = function(a) { - "undefined" === typeof a && (a = 1); - return f.toRGBA(95, 136, 176, a); - }; - return c; - }(); - c.UIThemeLight = m; - })(f.Theme || (f.Theme = {})); - })(f.Tools || (f.Tools = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(f) { - (function(c) { - var f = function() { - function c(f) { - this._buffers = f || []; - this._snapshots = []; - this._maxDepth = 0; - } - c.prototype.addBuffer = function(c) { - this._buffers.push(c); - }; - c.prototype.getSnapshotAt = function(c) { - return this._snapshots[c]; - }; - Object.defineProperty(c.prototype, "hasSnapshots", {get:function() { - return 0 < this.snapshotCount; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "snapshotCount", {get:function() { - return this._snapshots.length; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "startTime", {get:function() { - return this._startTime; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "endTime", {get:function() { - return this._endTime; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "totalTime", {get:function() { - return this.endTime - this.startTime; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "windowStart", {get:function() { - return this._windowStart; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "windowEnd", {get:function() { - return this._windowEnd; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "windowLength", {get:function() { - return this.windowEnd - this.windowStart; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "maxDepth", {get:function() { - return this._maxDepth; - }, enumerable:!0, configurable:!0}); - c.prototype.forEachSnapshot = function(c) { - for (var a = 0, d = this.snapshotCount;a < d;a++) { - c(this._snapshots[a], a); - } - }; - c.prototype.createSnapshots = function() { - var c = Number.MAX_VALUE, a = Number.MIN_VALUE, d = 0; - for (this._snapshots = [];0 < this._buffers.length;) { - var p = this._buffers.shift().createSnapshot(); - p && (c > p.startTime && (c = p.startTime), a < p.endTime && (a = p.endTime), d < p.maxDepth && (d = p.maxDepth), this._snapshots.push(p)); - } - this._startTime = c; - this._endTime = a; - this._windowStart = c; - this._windowEnd = a; - this._maxDepth = d; - }; - c.prototype.setWindow = function(c, a) { - if (c > a) { - var d = c; - c = a; - a = d; - } - d = Math.min(a - c, this.totalTime); - c < this._startTime ? (c = this._startTime, a = this._startTime + d) : a > this._endTime && (c = this._endTime - d, a = this._endTime); - this._windowStart = c; - this._windowEnd = a; - }; - c.prototype.moveWindowTo = function(c) { - this.setWindow(c - this.windowLength / 2, c + this.windowLength / 2); - }; - return c; - }(); - c.Profile = f; - })(f.Profiler || (f.Profiler = {})); - })(f.Tools || (f.Tools = {})); -})(Shumway || (Shumway = {})); -var __extends = this.__extends || function(f, u) { - function c() { - this.constructor = f; - } - for (var t in u) { - u.hasOwnProperty(t) && (f[t] = u[t]); - } - c.prototype = u.prototype; - f.prototype = new c; -}; -(function(f) { - (function(f) { - (function(c) { - var f = function() { - return function(c) { - this.kind = c; - this.totalTime = this.selfTime = this.count = 0; - }; - }(); - c.TimelineFrameStatistics = f; - var m = function() { - function c(a, d, p, e, h, n) { - this.parent = a; - this.kind = d; - this.startData = p; - this.endData = e; - this.startTime = h; - this.endTime = n; - this.maxDepth = 0; - } - Object.defineProperty(c.prototype, "totalTime", {get:function() { - return this.endTime - this.startTime; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "selfTime", {get:function() { - var a = this.totalTime; - if (this.children) { - for (var d = 0, c = this.children.length;d < c;d++) { - var e = this.children[d], a = a - (e.endTime - e.startTime) - } - } - return a; - }, enumerable:!0, configurable:!0}); - c.prototype.getChildIndex = function(a) { - for (var d = this.children, c = 0;c < d.length;c++) { - if (d[c].endTime > a) { - return c; - } - } - return 0; - }; - c.prototype.getChildRange = function(a, d) { - if (this.children && a <= this.endTime && d >= this.startTime && d >= a) { - var c = this._getNearestChild(a), e = this._getNearestChildReverse(d); - if (c <= e) { - return a = this.children[c].startTime, d = this.children[e].endTime, {startIndex:c, endIndex:e, startTime:a, endTime:d, totalTime:d - a}; - } - } - return null; - }; - c.prototype._getNearestChild = function(a) { - var d = this.children; - if (d && d.length) { - if (a <= d[0].endTime) { - return 0; - } - for (var c, e = 0, h = d.length - 1;h > e;) { - c = (e + h) / 2 | 0; - var n = d[c]; - if (a >= n.startTime && a <= n.endTime) { - return c; - } - a > n.endTime ? e = c + 1 : h = c; - } - return Math.ceil((e + h) / 2); - } - return 0; - }; - c.prototype._getNearestChildReverse = function(a) { - var d = this.children; - if (d && d.length) { - var c = d.length - 1; - if (a >= d[c].startTime) { - return c; - } - for (var e, h = 0;c > h;) { - e = Math.ceil((h + c) / 2); - var n = d[e]; - if (a >= n.startTime && a <= n.endTime) { - return e; - } - a > n.endTime ? h = e : c = e - 1; - } - return(h + c) / 2 | 0; - } - return 0; - }; - c.prototype.query = function(a) { - if (a < this.startTime || a > this.endTime) { - return null; - } - var d = this.children; - if (d && 0 < d.length) { - for (var c, e = 0, h = d.length - 1;h > e;) { - var n = (e + h) / 2 | 0; - c = d[n]; - if (a >= c.startTime && a <= c.endTime) { - return c.query(a); - } - a > c.endTime ? e = n + 1 : h = n; - } - c = d[h]; - if (a >= c.startTime && a <= c.endTime) { - return c.query(a); - } - } - return this; - }; - c.prototype.queryNext = function(a) { - for (var d = this;a > d.endTime;) { - if (d.parent) { - d = d.parent; - } else { - break; - } - } - return d.query(a); - }; - c.prototype.getDepth = function() { - for (var a = 0, d = this;d;) { - a++, d = d.parent; - } - return a; - }; - c.prototype.calculateStatistics = function() { - function a(c) { - if (c.kind) { - var e = d[c.kind.id] || (d[c.kind.id] = new f(c.kind)); - e.count++; - e.selfTime += c.selfTime; - e.totalTime += c.totalTime; - } - c.children && c.children.forEach(a); - } - var d = this.statistics = []; - a(this); - }; - return c; - }(); - c.TimelineFrame = m; - m = function(c) { - function a(a) { - c.call(this, null, null, null, null, NaN, NaN); - this.name = a; - } - __extends(a, c); - return a; - }(m); - c.TimelineBufferSnapshot = m; - })(f.Profiler || (f.Profiler = {})); - })(f.Tools || (f.Tools = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - (function(c) { - var t = f.ObjectUtilities.createEmptyObject, m = function() { - function r(a, d) { - "undefined" === typeof a && (a = ""); - this.name = a || ""; - this._startTime = f.isNullOrUndefined(d) ? performance.now() : d; - } - r.prototype.getKind = function(a) { - return this._kinds[a]; - }; - Object.defineProperty(r.prototype, "kinds", {get:function() { - return this._kinds.concat(); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(r.prototype, "depth", {get:function() { - return this._depth; - }, enumerable:!0, configurable:!0}); - r.prototype._initialize = function() { - this._depth = 0; - this._stack = []; - this._data = []; - this._kinds = []; - this._kindNameMap = t(); - this._marks = new f.CircularBuffer(Int32Array, 20); - this._times = new f.CircularBuffer(Float64Array, 20); - }; - r.prototype._getKindId = function(a) { - var d = r.MAX_KINDID; - if (void 0 === this._kindNameMap[a]) { - if (d = this._kinds.length, d < r.MAX_KINDID) { - var c = {id:d, name:a, visible:!0}; - this._kinds.push(c); - this._kindNameMap[a] = c; - } else { - d = r.MAX_KINDID; - } - } else { - d = this._kindNameMap[a].id; - } - return d; - }; - r.prototype._getMark = function(a, d, c) { - var e = r.MAX_DATAID; - f.isNullOrUndefined(c) || d === r.MAX_KINDID || (e = this._data.length, e < r.MAX_DATAID ? this._data.push(c) : e = r.MAX_DATAID); - return a | e << 16 | d; - }; - r.prototype.enter = function(a, d, c) { - c = (f.isNullOrUndefined(c) ? performance.now() : c) - this._startTime; - this._marks || this._initialize(); - this._depth++; - a = this._getKindId(a); - this._marks.write(this._getMark(r.ENTER, a, d)); - this._times.write(c); - this._stack.push(a); - }; - r.prototype.leave = function(a, d, c) { - c = (f.isNullOrUndefined(c) ? performance.now() : c) - this._startTime; - var e = this._stack.pop(); - a && (e = this._getKindId(a)); - this._marks.write(this._getMark(r.LEAVE, e, d)); - this._times.write(c); - this._depth--; - }; - r.prototype.count = function(a, d, c) { - }; - r.prototype.createSnapshot = function() { - var a; - "undefined" === typeof a && (a = Number.MAX_VALUE); - if (!this._marks) { - return null; - } - var d = this._times, p = this._kinds, e = this._data, h = new c.TimelineBufferSnapshot(this.name), n = [h], q = 0; - this._marks || this._initialize(); - this._marks.forEachInReverse(function(h, b) { - var g = e[h >>> 16 & r.MAX_DATAID], m = p[h & r.MAX_KINDID]; - if (f.isNullOrUndefined(m) || m.visible) { - var l = h & 2147483648, s = d.get(b), A = n.length; - if (l === r.LEAVE) { - if (1 === A && (q++, q > a)) { - return!0; - } - n.push(new c.TimelineFrame(n[A - 1], m, null, g, NaN, s)); - } else { - if (l === r.ENTER) { - if (m = n.pop(), l = n[n.length - 1]) { - for (l.children ? l.children.unshift(m) : l.children = [m], l = n.length, m.depth = l, m.startData = g, m.startTime = s;m;) { - if (m.maxDepth < l) { - m.maxDepth = l, m = m.parent; - } else { - break; - } - } - } else { - return!0; - } - } - } - } - }); - h.children && h.children.length && (h.startTime = h.children[0].startTime, h.endTime = h.children[h.children.length - 1].endTime); - return h; - }; - r.prototype.reset = function(a) { - this._startTime = f.isNullOrUndefined(a) ? performance.now() : a; - this._marks ? (this._depth = 0, this._data = [], this._marks.reset(), this._times.reset()) : this._initialize(); - }; - r.FromFirefoxProfile = function(a, d) { - for (var c = a.profile.threads[0].samples, e = new r(d, c[0].time), h = [], n, f = 0;f < c.length;f++) { - n = c[f]; - var k = n.time, b = n.frames, g = 0; - for (n = Math.min(b.length, h.length);g < n && b[g].location === h[g].location;) { - g++; - } - for (var m = h.length - g, l = 0;l < m;l++) { - n = h.pop(), e.leave(n.location, null, k); - } - for (;g < b.length;) { - n = b[g++], e.enter(n.location, null, k); - } - h = b; - } - for (;n = h.pop();) { - e.leave(n.location, null, k); - } - return e; - }; - r.FromChromeProfile = function(a, d) { - var c = a.timestamps, e = a.samples, h = new r(d, c[0] / 1E3), n = [], f = {}, k; - r._resolveIds(a.head, f); - for (var b = 0;b < c.length;b++) { - var g = c[b] / 1E3, m = []; - for (k = f[e[b]];k;) { - m.unshift(k), k = k.parent; - } - var l = 0; - for (k = Math.min(m.length, n.length);l < k && m[l] === n[l];) { - l++; - } - for (var s = n.length - l, A = 0;A < s;A++) { - k = n.pop(), h.leave(k.functionName, null, g); - } - for (;l < m.length;) { - k = m[l++], h.enter(k.functionName, null, g); - } - n = m; - } - for (;k = n.pop();) { - h.leave(k.functionName, null, g); - } - return h; - }; - r._resolveIds = function(a, c) { - c[a.id] = a; - if (a.children) { - for (var p = 0;p < a.children.length;p++) { - a.children[p].parent = a, r._resolveIds(a.children[p], c); - } - } - }; - r.ENTER = 0; - r.LEAVE = -2147483648; - r.MAX_KINDID = 65535; - r.MAX_DATAID = 32767; - return r; - }(); - c.TimelineBuffer = m; - })(u.Profiler || (u.Profiler = {})); - })(f.Tools || (f.Tools = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - (function(c) { - (function(c) { - c[c.DARK = 0] = "DARK"; - c[c.LIGHT = 1] = "LIGHT"; - })(c.UIThemeType || (c.UIThemeType = {})); - var t = function() { - function m(c, a) { - "undefined" === typeof a && (a = 0); - this._container = c; - this._headers = []; - this._charts = []; - this._profiles = []; - this._activeProfile = null; - this.themeType = a; - this._tooltip = this._createTooltip(); - } - m.prototype.createProfile = function(f, a) { - "undefined" === typeof a && (a = !0); - var d = new c.Profile(f); - d.createSnapshots(); - this._profiles.push(d); - a && this.activateProfile(d); - return d; - }; - m.prototype.activateProfile = function(c) { - this.deactivateProfile(); - this._activeProfile = c; - this._createViews(); - this._initializeViews(); - }; - m.prototype.activateProfileAt = function(c) { - this.activateProfile(this.getProfileAt(c)); - }; - m.prototype.deactivateProfile = function() { - this._activeProfile && (this._destroyViews(), this._activeProfile = null); - }; - m.prototype.resize = function() { - this._onResize(); - }; - m.prototype.getProfileAt = function(c) { - return this._profiles[c]; - }; - Object.defineProperty(m.prototype, "activeProfile", {get:function() { - return this._activeProfile; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(m.prototype, "profileCount", {get:function() { - return this._profiles.length; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(m.prototype, "container", {get:function() { - return this._container; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(m.prototype, "themeType", {get:function() { - return this._themeType; - }, set:function(c) { - switch(c) { - case 0: - this._theme = new u.Theme.UIThemeDark; - break; - case 1: - this._theme = new u.Theme.UIThemeLight; - } - }, enumerable:!0, configurable:!0}); - Object.defineProperty(m.prototype, "theme", {get:function() { - return this._theme; - }, enumerable:!0, configurable:!0}); - m.prototype.getSnapshotAt = function(c) { - return this._activeProfile.getSnapshotAt(c); - }; - m.prototype._createViews = function() { - if (this._activeProfile) { - var f = this; - this._overviewHeader = new c.FlameChartHeader(this, 0); - this._overview = new c.FlameChartOverview(this, 0); - this._activeProfile.forEachSnapshot(function(a, d) { - f._headers.push(new c.FlameChartHeader(f, 1)); - f._charts.push(new c.FlameChart(f, a)); - }); - window.addEventListener("resize", this._onResize.bind(this)); - } - }; - m.prototype._destroyViews = function() { - if (this._activeProfile) { - this._overviewHeader.destroy(); - for (this._overview.destroy();this._headers.length;) { - this._headers.pop().destroy(); - } - for (;this._charts.length;) { - this._charts.pop().destroy(); - } - window.removeEventListener("resize", this._onResize.bind(this)); - } - }; - m.prototype._initializeViews = function() { - if (this._activeProfile) { - var c = this, a = this._activeProfile.startTime, d = this._activeProfile.endTime; - this._overviewHeader.initialize(a, d); - this._overview.initialize(a, d); - this._activeProfile.forEachSnapshot(function(p, e) { - c._headers[e].initialize(a, d); - c._charts[e].initialize(a, d); - }); - } - }; - m.prototype._onResize = function() { - if (this._activeProfile) { - var c = this, a = this._container.offsetWidth; - this._overviewHeader.setSize(a); - this._overview.setSize(a); - this._activeProfile.forEachSnapshot(function(d, p) { - c._headers[p].setSize(a); - c._charts[p].setSize(a); - }); - } - }; - m.prototype._updateViews = function() { - if (this._activeProfile) { - var c = this, a = this._activeProfile.windowStart, d = this._activeProfile.windowEnd; - this._overviewHeader.setWindow(a, d); - this._overview.setWindow(a, d); - this._activeProfile.forEachSnapshot(function(p, e) { - c._headers[e].setWindow(a, d); - c._charts[e].setWindow(a, d); - }); - } - }; - m.prototype._drawViews = function() { - }; - m.prototype._createTooltip = function() { - var c = document.createElement("div"); - c.classList.add("profiler-tooltip"); - c.style.display = "none"; - this._container.insertBefore(c, this._container.firstChild); - return c; - }; - m.prototype.setWindow = function(c, a) { - this._activeProfile.setWindow(c, a); - this._updateViews(); - }; - m.prototype.moveWindowTo = function(c) { - this._activeProfile.moveWindowTo(c); - this._updateViews(); - }; - m.prototype.showTooltip = function(c, a, d, p) { - this.removeTooltipContent(); - this._tooltip.appendChild(this.createTooltipContent(c, a)); - this._tooltip.style.display = "block"; - var e = this._tooltip.firstChild; - a = e.clientWidth; - e = e.clientHeight; - d += d + a >= c.canvas.clientWidth - 50 ? -(a + 20) : 25; - p += c.canvas.offsetTop - e / 2; - this._tooltip.style.left = d + "px"; - this._tooltip.style.top = p + "px"; - }; - m.prototype.hideTooltip = function() { - this._tooltip.style.display = "none"; - }; - m.prototype.createTooltipContent = function(c, a) { - var d = Math.round(1E5 * a.totalTime) / 1E5, p = Math.round(1E5 * a.selfTime) / 1E5, e = Math.round(1E4 * a.selfTime / a.totalTime) / 100, h = document.createElement("div"), n = document.createElement("h1"); - n.textContent = a.kind.name; - h.appendChild(n); - n = document.createElement("p"); - n.textContent = "Total: " + d + " ms"; - h.appendChild(n); - d = document.createElement("p"); - d.textContent = "Self: " + p + " ms (" + e + "%)"; - h.appendChild(d); - if (p = c.getStatistics(a.kind)) { - e = document.createElement("p"), e.textContent = "Count: " + p.count, h.appendChild(e), e = Math.round(1E5 * p.totalTime) / 1E5, d = document.createElement("p"), d.textContent = "All Total: " + e + " ms", h.appendChild(d), p = Math.round(1E5 * p.selfTime) / 1E5, e = document.createElement("p"), e.textContent = "All Self: " + p + " ms", h.appendChild(e); - } - this.appendDataElements(h, a.startData); - this.appendDataElements(h, a.endData); - return h; - }; - m.prototype.appendDataElements = function(c, a) { - if (!f.isNullOrUndefined(a)) { - c.appendChild(document.createElement("hr")); - var d; - if (f.isObject(a)) { - for (var p in a) { - d = document.createElement("p"), d.textContent = p + ": " + a[p], c.appendChild(d); - } - } else { - d = document.createElement("p"), d.textContent = a.toString(), c.appendChild(d); - } - } - }; - m.prototype.removeTooltipContent = function() { - for (var c = this._tooltip;c.firstChild;) { - c.removeChild(c.firstChild); - } - }; - return m; - }(); - c.Controller = t; - })(u.Profiler || (u.Profiler = {})); - })(f.Tools || (f.Tools = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - (function(c) { - var t = f.NumberUtilities.clamp, m = function() { - function a(a) { - this.value = a; - } - a.prototype.toString = function() { - return this.value; - }; - a.AUTO = new a("auto"); - a.DEFAULT = new a("default"); - a.NONE = new a("none"); - a.HELP = new a("help"); - a.POINTER = new a("pointer"); - a.PROGRESS = new a("progress"); - a.WAIT = new a("wait"); - a.CELL = new a("cell"); - a.CROSSHAIR = new a("crosshair"); - a.TEXT = new a("text"); - a.ALIAS = new a("alias"); - a.COPY = new a("copy"); - a.MOVE = new a("move"); - a.NO_DROP = new a("no-drop"); - a.NOT_ALLOWED = new a("not-allowed"); - a.ALL_SCROLL = new a("all-scroll"); - a.COL_RESIZE = new a("col-resize"); - a.ROW_RESIZE = new a("row-resize"); - a.N_RESIZE = new a("n-resize"); - a.E_RESIZE = new a("e-resize"); - a.S_RESIZE = new a("s-resize"); - a.W_RESIZE = new a("w-resize"); - a.NE_RESIZE = new a("ne-resize"); - a.NW_RESIZE = new a("nw-resize"); - a.SE_RESIZE = new a("se-resize"); - a.SW_RESIZE = new a("sw-resize"); - a.EW_RESIZE = new a("ew-resize"); - a.NS_RESIZE = new a("ns-resize"); - a.NESW_RESIZE = new a("nesw-resize"); - a.NWSE_RESIZE = new a("nwse-resize"); - a.ZOOM_IN = new a("zoom-in"); - a.ZOOM_OUT = new a("zoom-out"); - a.GRAB = new a("grab"); - a.GRABBING = new a("grabbing"); - return a; - }(); - c.MouseCursor = m; - var r = function() { - function a(a, c) { - this._target = a; - this._eventTarget = c; - this._wheelDisabled = !1; - this._boundOnMouseDown = this._onMouseDown.bind(this); - this._boundOnMouseUp = this._onMouseUp.bind(this); - this._boundOnMouseOver = this._onMouseOver.bind(this); - this._boundOnMouseOut = this._onMouseOut.bind(this); - this._boundOnMouseMove = this._onMouseMove.bind(this); - this._boundOnMouseWheel = this._onMouseWheel.bind(this); - this._boundOnDrag = this._onDrag.bind(this); - c.addEventListener("mousedown", this._boundOnMouseDown, !1); - c.addEventListener("mouseover", this._boundOnMouseOver, !1); - c.addEventListener("mouseout", this._boundOnMouseOut, !1); - c.addEventListener("onwheel" in document ? "wheel" : "mousewheel", this._boundOnMouseWheel, !1); - } - a.prototype.destroy = function() { - var a = this._eventTarget; - a.removeEventListener("mousedown", this._boundOnMouseDown); - a.removeEventListener("mouseover", this._boundOnMouseOver); - a.removeEventListener("mouseout", this._boundOnMouseOut); - a.removeEventListener("onwheel" in document ? "wheel" : "mousewheel", this._boundOnMouseWheel); - window.removeEventListener("mousemove", this._boundOnDrag); - window.removeEventListener("mouseup", this._boundOnMouseUp); - this._killHoverCheck(); - this._target = this._eventTarget = null; - }; - a.prototype.updateCursor = function(c) { - if (!a._cursorOwner || a._cursorOwner === this._target) { - var p = this._eventTarget.parentElement; - a._cursor !== c && (a._cursor = c, ["", "-moz-", "-webkit-"].forEach(function(a) { - p.style.cursor = a + c; - })); - a._cursorOwner = a._cursor === m.DEFAULT ? null : this._target; - } - }; - a.prototype._onMouseDown = function(a) { - this._killHoverCheck(); - if (0 === a.button) { - var c = this._getTargetMousePos(a, a.target); - this._dragInfo = {start:c, current:c, delta:{x:0, y:0}, hasMoved:!1, originalTarget:a.target}; - window.addEventListener("mousemove", this._boundOnDrag, !1); - window.addEventListener("mouseup", this._boundOnMouseUp, !1); - this._target.onMouseDown(c.x, c.y); - } - }; - a.prototype._onDrag = function(a) { - var c = this._dragInfo; - a = this._getTargetMousePos(a, c.originalTarget); - var e = {x:a.x - c.start.x, y:a.y - c.start.y}; - c.current = a; - c.delta = e; - c.hasMoved = !0; - this._target.onDrag(c.start.x, c.start.y, a.x, a.y, e.x, e.y); - }; - a.prototype._onMouseUp = function(a) { - window.removeEventListener("mousemove", this._boundOnDrag); - window.removeEventListener("mouseup", this._boundOnMouseUp); - var c = this; - a = this._dragInfo; - if (a.hasMoved) { - this._target.onDragEnd(a.start.x, a.start.y, a.current.x, a.current.y, a.delta.x, a.delta.y); - } else { - this._target.onClick(a.current.x, a.current.y); - } - this._dragInfo = null; - this._wheelDisabled = !0; - setTimeout(function() { - c._wheelDisabled = !1; - }, 500); - }; - a.prototype._onMouseOver = function(a) { - a.target.addEventListener("mousemove", this._boundOnMouseMove, !1); - if (!this._dragInfo) { - var c = this._getTargetMousePos(a, a.target); - this._target.onMouseOver(c.x, c.y); - this._startHoverCheck(a); - } - }; - a.prototype._onMouseOut = function(a) { - a.target.removeEventListener("mousemove", this._boundOnMouseMove, !1); - if (!this._dragInfo) { - this._target.onMouseOut(); - } - this._killHoverCheck(); - }; - a.prototype._onMouseMove = function(a) { - if (!this._dragInfo) { - var c = this._getTargetMousePos(a, a.target); - this._target.onMouseMove(c.x, c.y); - this._killHoverCheck(); - this._startHoverCheck(a); - } - }; - a.prototype._onMouseWheel = function(a) { - if (!(a.altKey || a.metaKey || a.ctrlKey || a.shiftKey || (a.preventDefault(), this._dragInfo || this._wheelDisabled))) { - var c = this._getTargetMousePos(a, a.target); - a = t("undefined" !== typeof a.deltaY ? a.deltaY / 16 : -a.wheelDelta / 40, -1, 1); - this._target.onMouseWheel(c.x, c.y, Math.pow(1.2, a) - 1); - } - }; - a.prototype._startHoverCheck = function(c) { - this._hoverInfo = {isHovering:!1, timeoutHandle:setTimeout(this._onMouseMoveIdleHandler.bind(this), a.HOVER_TIMEOUT), pos:this._getTargetMousePos(c, c.target)}; - }; - a.prototype._killHoverCheck = function() { - if (this._hoverInfo) { - clearTimeout(this._hoverInfo.timeoutHandle); - if (this._hoverInfo.isHovering) { - this._target.onHoverEnd(); - } - this._hoverInfo = null; - } - }; - a.prototype._onMouseMoveIdleHandler = function() { - var a = this._hoverInfo; - a.isHovering = !0; - this._target.onHoverStart(a.pos.x, a.pos.y); - }; - a.prototype._getTargetMousePos = function(a, c) { - var e = c.getBoundingClientRect(); - return{x:a.clientX - e.left, y:a.clientY - e.top}; - }; - a.HOVER_TIMEOUT = 500; - a._cursor = m.DEFAULT; - return a; - }(); - c.MouseController = r; - })(u.Profiler || (u.Profiler = {})); - })(f.Tools || (f.Tools = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(f) { - (function(c) { - (function(c) { - c[c.NONE = 0] = "NONE"; - c[c.WINDOW = 1] = "WINDOW"; - c[c.HANDLE_LEFT = 2] = "HANDLE_LEFT"; - c[c.HANDLE_RIGHT = 3] = "HANDLE_RIGHT"; - c[c.HANDLE_BOTH = 4] = "HANDLE_BOTH"; - })(c.FlameChartDragTarget || (c.FlameChartDragTarget = {})); - var f = function() { - function f(r) { - this._controller = r; - this._initialized = !1; - this._canvas = document.createElement("canvas"); - this._context = this._canvas.getContext("2d"); - this._mouseController = new c.MouseController(this, this._canvas); - r = r.container; - r.appendChild(this._canvas); - r = r.getBoundingClientRect(); - this.setSize(r.width); - } - Object.defineProperty(f.prototype, "canvas", {get:function() { - return this._canvas; - }, enumerable:!0, configurable:!0}); - f.prototype.setSize = function(c, a) { - "undefined" === typeof a && (a = 20); - this._width = c; - this._height = a; - this._resetCanvas(); - this.draw(); - }; - f.prototype.initialize = function(c, a) { - this._initialized = !0; - this.setRange(c, a); - this.setWindow(c, a, !1); - this.draw(); - }; - f.prototype.setWindow = function(c, a, d) { - "undefined" === typeof d && (d = !0); - this._windowStart = c; - this._windowEnd = a; - !d || this.draw(); - }; - f.prototype.setRange = function(c, a) { - var d = !1; - "undefined" === typeof d && (d = !0); - this._rangeStart = c; - this._rangeEnd = a; - !d || this.draw(); - }; - f.prototype.destroy = function() { - this._mouseController.destroy(); - this._mouseController = null; - this._controller.container.removeChild(this._canvas); - this._controller = null; - }; - f.prototype._resetCanvas = function() { - var c = window.devicePixelRatio, a = this._canvas; - a.width = this._width * c; - a.height = this._height * c; - a.style.width = this._width + "px"; - a.style.height = this._height + "px"; - }; - f.prototype.draw = function() { - }; - f.prototype._almostEq = function(c, a) { - var d; - "undefined" === typeof d && (d = 10); - return Math.abs(c - a) < 1 / Math.pow(10, d); - }; - f.prototype._windowEqRange = function() { - return this._almostEq(this._windowStart, this._rangeStart) && this._almostEq(this._windowEnd, this._rangeEnd); - }; - f.prototype._decimalPlaces = function(c) { - return(+c).toFixed(10).replace(/^-?\d*\.?|0+$/g, "").length; - }; - f.prototype._toPixelsRelative = function(c) { - return 0; - }; - f.prototype._toPixels = function(c) { - return 0; - }; - f.prototype._toTimeRelative = function(c) { - return 0; - }; - f.prototype._toTime = function(c) { - return 0; - }; - f.prototype.onMouseWheel = function(c, a, d) { - c = this._toTime(c); - a = this._windowStart; - var p = this._windowEnd, e = p - a; - d = Math.max((f.MIN_WINDOW_LEN - e) / e, d); - this._controller.setWindow(a + (a - c) * d, p + (p - c) * d); - this.onHoverEnd(); - }; - f.prototype.onMouseDown = function(c, a) { - }; - f.prototype.onMouseMove = function(c, a) { - }; - f.prototype.onMouseOver = function(c, a) { - }; - f.prototype.onMouseOut = function() { - }; - f.prototype.onDrag = function(c, a, d, p, e, h) { - }; - f.prototype.onDragEnd = function(c, a, d, p, e, h) { - }; - f.prototype.onClick = function(c, a) { - }; - f.prototype.onHoverStart = function(c, a) { - }; - f.prototype.onHoverEnd = function() { - }; - f.DRAGHANDLE_WIDTH = 4; - f.MIN_WINDOW_LEN = .1; - return f; - }(); - c.FlameChartBase = f; - })(f.Profiler || (f.Profiler = {})); - })(f.Tools || (f.Tools = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - (function(c) { - var t = f.StringUtilities.trimMiddle, m = f.ObjectUtilities.createEmptyObject, r = function(a) { - function d(c, d) { - a.call(this, c); - this._textWidth = {}; - this._minFrameWidthInPixels = 1; - this._snapshot = d; - this._kindStyle = m(); - } - __extends(d, a); - d.prototype.setSize = function(c, d) { - a.prototype.setSize.call(this, c, d || this._initialized ? 12.5 * this._maxDepth : 100); - }; - d.prototype.initialize = function(a, c) { - this._initialized = !0; - this._maxDepth = this._snapshot.maxDepth; - this.setRange(a, c); - this.setWindow(a, c, !1); - this.setSize(this._width, 12.5 * this._maxDepth); - }; - d.prototype.destroy = function() { - a.prototype.destroy.call(this); - this._snapshot = null; - }; - d.prototype.draw = function() { - var a = this._context, c = window.devicePixelRatio; - f.ColorStyle.reset(); - a.save(); - a.scale(c, c); - a.fillStyle = this._controller.theme.bodyBackground(1); - a.fillRect(0, 0, this._width, this._height); - this._initialized && this._drawChildren(this._snapshot); - a.restore(); - }; - d.prototype._drawChildren = function(a, c) { - "undefined" === typeof c && (c = 0); - var d = a.getChildRange(this._windowStart, this._windowEnd); - if (d) { - for (var n = d.startIndex;n <= d.endIndex;n++) { - var f = a.children[n]; - this._drawFrame(f, c) && this._drawChildren(f, c + 1); - } - } - }; - d.prototype._drawFrame = function(a, c) { - var d = this._context, n = this._toPixels(a.startTime), q = this._toPixels(a.endTime), k = q - n; - if (k <= this._minFrameWidthInPixels) { - return d.fillStyle = this._controller.theme.tabToolbar(1), d.fillRect(n, 12.5 * c, this._minFrameWidthInPixels, 12 + 12.5 * (a.maxDepth - a.depth)), !1; - } - 0 > n && (q = k + n, n = 0); - var q = q - n, b = this._kindStyle[a.kind.id]; - b || (b = f.ColorStyle.randomStyle(), b = this._kindStyle[a.kind.id] = {bgColor:b, textColor:f.ColorStyle.contrastStyle(b)}); - d.save(); - d.fillStyle = b.bgColor; - d.fillRect(n, 12.5 * c, q, 12); - 12 < k && (k = a.kind.name) && k.length && (k = this._prepareText(d, k, q - 4), k.length && (d.fillStyle = b.textColor, d.textBaseline = "bottom", d.fillText(k, n + 2, 12.5 * (c + 1) - 1))); - d.restore(); - return!0; - }; - d.prototype._prepareText = function(a, c, d) { - var n = this._measureWidth(a, c); - if (d > n) { - return c; - } - for (var n = 3, f = c.length;n < f;) { - var k = n + f >> 1; - this._measureWidth(a, t(c, k)) < d ? n = k + 1 : f = k; - } - c = t(c, f - 1); - n = this._measureWidth(a, c); - return n <= d ? c : ""; - }; - d.prototype._measureWidth = function(a, c) { - var d = this._textWidth[c]; - d || (d = a.measureText(c).width, this._textWidth[c] = d); - return d; - }; - d.prototype._toPixelsRelative = function(a) { - return a * this._width / (this._windowEnd - this._windowStart); - }; - d.prototype._toPixels = function(a) { - return this._toPixelsRelative(a - this._windowStart); - }; - d.prototype._toTimeRelative = function(a) { - return a * (this._windowEnd - this._windowStart) / this._width; - }; - d.prototype._toTime = function(a) { - return this._toTimeRelative(a) + this._windowStart; - }; - d.prototype._getFrameAtPosition = function(a, c) { - var d = 1 + c / 12.5 | 0, n = this._snapshot.query(this._toTime(a)); - if (n && n.depth >= d) { - for (;n && n.depth > d;) { - n = n.parent; - } - return n; - } - return null; - }; - d.prototype.onMouseDown = function(a, d) { - this._windowEqRange() || (this._mouseController.updateCursor(c.MouseCursor.ALL_SCROLL), this._dragInfo = {windowStartInitial:this._windowStart, windowEndInitial:this._windowEnd, target:1}); - }; - d.prototype.onMouseMove = function(a, c) { - }; - d.prototype.onMouseOver = function(a, c) { - }; - d.prototype.onMouseOut = function() { - }; - d.prototype.onDrag = function(a, c, d, n, f, k) { - if (a = this._dragInfo) { - f = this._toTimeRelative(-f), this._controller.setWindow(a.windowStartInitial + f, a.windowEndInitial + f); - } - }; - d.prototype.onDragEnd = function(a, d, h, n, f, k) { - this._dragInfo = null; - this._mouseController.updateCursor(c.MouseCursor.DEFAULT); - }; - d.prototype.onClick = function(a, d) { - this._dragInfo = null; - this._mouseController.updateCursor(c.MouseCursor.DEFAULT); - }; - d.prototype.onHoverStart = function(a, c) { - var d = this._getFrameAtPosition(a, c); - d && (this._hoveredFrame = d, this._controller.showTooltip(this, d, a, c)); - }; - d.prototype.onHoverEnd = function() { - this._hoveredFrame && (this._hoveredFrame = null, this._controller.hideTooltip()); - }; - d.prototype.getStatistics = function(a) { - var c = this._snapshot; - c.statistics || c.calculateStatistics(); - return c.statistics[a.id]; - }; - return d; - }(c.FlameChartBase); - c.FlameChart = r; - })(u.Profiler || (u.Profiler = {})); - })(f.Tools || (f.Tools = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - (function(c) { - var t = f.NumberUtilities.clamp; - (function(c) { - c[c.OVERLAY = 0] = "OVERLAY"; - c[c.STACK = 1] = "STACK"; - c[c.UNION = 2] = "UNION"; - })(c.FlameChartOverviewMode || (c.FlameChartOverviewMode = {})); - var m = function(f) { - function a(a, c) { - "undefined" === typeof c && (c = 1); - this._mode = c; - this._overviewCanvasDirty = !0; - this._overviewCanvas = document.createElement("canvas"); - this._overviewContext = this._overviewCanvas.getContext("2d"); - f.call(this, a); - } - __extends(a, f); - a.prototype.setSize = function(a, c) { - f.prototype.setSize.call(this, a, c || 64); - }; - Object.defineProperty(a.prototype, "mode", {set:function(a) { - this._mode = a; - this.draw(); - }, enumerable:!0, configurable:!0}); - a.prototype._resetCanvas = function() { - f.prototype._resetCanvas.call(this); - this._overviewCanvas.width = this._canvas.width; - this._overviewCanvas.height = this._canvas.height; - this._overviewCanvasDirty = !0; - }; - a.prototype.draw = function() { - var a = this._context, c = window.devicePixelRatio, e = this._width, h = this._height; - a.save(); - a.scale(c, c); - a.fillStyle = this._controller.theme.bodyBackground(1); - a.fillRect(0, 0, e, h); - a.restore(); - this._initialized && (this._overviewCanvasDirty && (this._drawChart(), this._overviewCanvasDirty = !1), a.drawImage(this._overviewCanvas, 0, 0), this._drawSelection()); - }; - a.prototype._drawSelection = function() { - var a = this._context, c = this._height, e = window.devicePixelRatio, h = this._selection ? this._selection.left : this._toPixels(this._windowStart), f = this._selection ? this._selection.right : this._toPixels(this._windowEnd), q = this._controller.theme; - a.save(); - a.scale(e, e); - this._selection ? (a.fillStyle = q.selectionText(.15), a.fillRect(h, 1, f - h, c - 1), a.fillStyle = "rgba(133, 0, 0, 1)", a.fillRect(h + .5, 0, f - h - 1, 4), a.fillRect(h + .5, c - 4, f - h - 1, 4)) : (a.fillStyle = q.bodyBackground(.4), a.fillRect(0, 1, h, c - 1), a.fillRect(f, 1, this._width, c - 1)); - a.beginPath(); - a.moveTo(h, 0); - a.lineTo(h, c); - a.moveTo(f, 0); - a.lineTo(f, c); - a.lineWidth = .5; - a.strokeStyle = q.foregroundTextGrey(1); - a.stroke(); - c = Math.abs((this._selection ? this._toTime(this._selection.right) : this._windowEnd) - (this._selection ? this._toTime(this._selection.left) : this._windowStart)); - a.fillStyle = q.selectionText(.5); - a.font = "8px sans-serif"; - a.textBaseline = "alphabetic"; - a.textAlign = "end"; - a.fillText(c.toFixed(2), Math.min(h, f) - 4, 10); - a.fillText((c / 60).toFixed(2), Math.min(h, f) - 4, 20); - a.restore(); - }; - a.prototype._drawChart = function() { - var a = window.devicePixelRatio, c = this._height, e = this._controller.activeProfile, h = 4 * this._width, f = e.totalTime / h, q = this._overviewContext, k = this._controller.theme.blueHighlight(1); - q.save(); - q.translate(0, a * c); - var b = -a * c / (e.maxDepth - 1); - q.scale(a / 4, b); - q.clearRect(0, 0, h, e.maxDepth - 1); - 1 == this._mode && q.scale(1, 1 / e.snapshotCount); - for (var g = 0, r = e.snapshotCount;g < r;g++) { - var l = e.getSnapshotAt(g); - if (l) { - var s = null, A = 0; - q.beginPath(); - q.moveTo(0, 0); - for (var m = 0;m < h;m++) { - A = e.startTime + m * f, A = (s = s ? s.queryNext(A) : l.query(A)) ? s.getDepth() - 1 : 0, q.lineTo(m, A); - } - q.lineTo(m, 0); - q.fillStyle = k; - q.fill(); - 1 == this._mode && q.translate(0, -c * a / b); - } - } - q.restore(); - }; - a.prototype._toPixelsRelative = function(a) { - return a * this._width / (this._rangeEnd - this._rangeStart); - }; - a.prototype._toPixels = function(a) { - return this._toPixelsRelative(a - this._rangeStart); - }; - a.prototype._toTimeRelative = function(a) { - return a * (this._rangeEnd - this._rangeStart) / this._width; - }; - a.prototype._toTime = function(a) { - return this._toTimeRelative(a) + this._rangeStart; - }; - a.prototype._getDragTargetUnderCursor = function(a, f) { - if (0 <= f && f < this._height) { - var e = this._toPixels(this._windowStart), h = this._toPixels(this._windowEnd), n = 2 + c.FlameChartBase.DRAGHANDLE_WIDTH / 2, q = a >= e - n && a <= e + n, k = a >= h - n && a <= h + n; - if (q && k) { - return 4; - } - if (q) { - return 2; - } - if (k) { - return 3; - } - if (!this._windowEqRange() && a > e + n && a < h - n) { - return 1; - } - } - return 0; - }; - a.prototype.onMouseDown = function(a, f) { - var e = this._getDragTargetUnderCursor(a, f); - 0 === e ? (this._selection = {left:a, right:a}, this.draw()) : (1 === e && this._mouseController.updateCursor(c.MouseCursor.GRABBING), this._dragInfo = {windowStartInitial:this._windowStart, windowEndInitial:this._windowEnd, target:e}); - }; - a.prototype.onMouseMove = function(a, f) { - var e = c.MouseCursor.DEFAULT, h = this._getDragTargetUnderCursor(a, f); - 0 === h || this._selection || (e = 1 === h ? c.MouseCursor.GRAB : c.MouseCursor.EW_RESIZE); - this._mouseController.updateCursor(e); - }; - a.prototype.onMouseOver = function(a, c) { - this.onMouseMove(a, c); - }; - a.prototype.onMouseOut = function() { - this._mouseController.updateCursor(c.MouseCursor.DEFAULT); - }; - a.prototype.onDrag = function(a, f, e, h, n, q) { - if (this._selection) { - this._selection = {left:a, right:t(e, 0, this._width - 1)}, this.draw(); - } else { - a = this._dragInfo; - if (4 === a.target) { - if (0 !== n) { - a.target = 0 > n ? 2 : 3; - } else { - return; - } - } - f = this._windowStart; - e = this._windowEnd; - n = this._toTimeRelative(n); - switch(a.target) { - case 1: - f = a.windowStartInitial + n; - e = a.windowEndInitial + n; - break; - case 2: - f = t(a.windowStartInitial + n, this._rangeStart, e - c.FlameChartBase.MIN_WINDOW_LEN); - break; - case 3: - e = t(a.windowEndInitial + n, f + c.FlameChartBase.MIN_WINDOW_LEN, this._rangeEnd); - break; - default: - return; - } - this._controller.setWindow(f, e); - } - }; - a.prototype.onDragEnd = function(a, c, e, h, f, q) { - this._selection && (this._selection = null, this._controller.setWindow(this._toTime(a), this._toTime(e))); - this._dragInfo = null; - this.onMouseMove(e, h); - }; - a.prototype.onClick = function(a, c) { - this._selection = this._dragInfo = null; - this._windowEqRange() || (0 === this._getDragTargetUnderCursor(a, c) && this._controller.moveWindowTo(this._toTime(a)), this.onMouseMove(a, c)); - this.draw(); - }; - a.prototype.onHoverStart = function(a, c) { - }; - a.prototype.onHoverEnd = function() { - }; - return a; - }(c.FlameChartBase); - c.FlameChartOverview = m; - })(u.Profiler || (u.Profiler = {})); - })(f.Tools || (f.Tools = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - (function(c) { - var t = f.NumberUtilities.clamp; - (function(c) { - c[c.OVERVIEW = 0] = "OVERVIEW"; - c[c.CHART = 1] = "CHART"; - })(c.FlameChartHeaderType || (c.FlameChartHeaderType = {})); - var m = function(f) { - function a(a, c) { - this._type = c; - f.call(this, a); - } - __extends(a, f); - a.prototype.draw = function() { - var a = this._context, c = window.devicePixelRatio, e = this._width, h = this._height; - a.save(); - a.scale(c, c); - a.fillStyle = this._controller.theme.tabToolbar(1); - a.fillRect(0, 0, e, h); - this._initialized && (0 == this._type ? (c = this._toPixels(this._windowStart), e = this._toPixels(this._windowEnd), a.fillStyle = this._controller.theme.bodyBackground(1), a.fillRect(c, 0, e - c, h), this._drawLabels(this._rangeStart, this._rangeEnd), this._drawDragHandle(c), this._drawDragHandle(e)) : this._drawLabels(this._windowStart, this._windowEnd)); - a.restore(); - }; - a.prototype._drawLabels = function(c, f) { - var e = this._context, h = this._calculateTickInterval(c, f), n = Math.ceil(c / h) * h, q = 500 <= h, k = q ? 1E3 : 1, b = this._decimalPlaces(h / k), q = q ? "s" : "ms", g = this._toPixels(n), m = this._height / 2, l = this._controller.theme; - e.lineWidth = 1; - e.strokeStyle = l.contentTextDarkGrey(.5); - e.fillStyle = l.contentTextDarkGrey(1); - e.textAlign = "right"; - e.textBaseline = "middle"; - e.font = "11px sans-serif"; - for (l = this._width + a.TICK_MAX_WIDTH;g < l;) { - e.fillText((n / k).toFixed(b) + " " + q, g - 7, m + 1), e.beginPath(), e.moveTo(g, 0), e.lineTo(g, this._height + 1), e.closePath(), e.stroke(), n += h, g = this._toPixels(n); - } - }; - a.prototype._calculateTickInterval = function(c, f) { - var e = (f - c) / (this._width / a.TICK_MAX_WIDTH), h = Math.pow(10, Math.floor(Math.log(e) / Math.LN10)), e = e / h; - return 5 < e ? 10 * h : 2 < e ? 5 * h : 1 < e ? 2 * h : h; - }; - a.prototype._drawDragHandle = function(a) { - var f = this._context; - f.lineWidth = 2; - f.strokeStyle = this._controller.theme.bodyBackground(1); - f.fillStyle = this._controller.theme.foregroundTextGrey(.7); - this._drawRoundedRect(f, a - c.FlameChartBase.DRAGHANDLE_WIDTH / 2, c.FlameChartBase.DRAGHANDLE_WIDTH, this._height - 2); - }; - a.prototype._drawRoundedRect = function(a, c, e, h) { - var f, q = !0; - "undefined" === typeof q && (q = !0); - "undefined" === typeof f && (f = !0); - a.beginPath(); - a.moveTo(c + 2, 1); - a.lineTo(c + e - 2, 1); - a.quadraticCurveTo(c + e, 1, c + e, 3); - a.lineTo(c + e, 1 + h - 2); - a.quadraticCurveTo(c + e, 1 + h, c + e - 2, 1 + h); - a.lineTo(c + 2, 1 + h); - a.quadraticCurveTo(c, 1 + h, c, 1 + h - 2); - a.lineTo(c, 3); - a.quadraticCurveTo(c, 1, c + 2, 1); - a.closePath(); - q && a.stroke(); - f && a.fill(); - }; - a.prototype._toPixelsRelative = function(a) { - return a * this._width / (0 === this._type ? this._rangeEnd - this._rangeStart : this._windowEnd - this._windowStart); - }; - a.prototype._toPixels = function(a) { - return this._toPixelsRelative(a - (0 === this._type ? this._rangeStart : this._windowStart)); - }; - a.prototype._toTimeRelative = function(a) { - return a * (0 === this._type ? this._rangeEnd - this._rangeStart : this._windowEnd - this._windowStart) / this._width; - }; - a.prototype._toTime = function(a) { - return this._toTimeRelative(a) + (0 === this._type ? this._rangeStart : this._windowStart); - }; - a.prototype._getDragTargetUnderCursor = function(a, f) { - if (0 <= f && f < this._height) { - if (0 === this._type) { - var e = this._toPixels(this._windowStart), h = this._toPixels(this._windowEnd), n = 2 + c.FlameChartBase.DRAGHANDLE_WIDTH / 2, e = a >= e - n && a <= e + n, h = a >= h - n && a <= h + n; - if (e && h) { - return 4; - } - if (e) { - return 2; - } - if (h) { - return 3; - } - } - if (!this._windowEqRange()) { - return 1; - } - } - return 0; - }; - a.prototype.onMouseDown = function(a, f) { - var e = this._getDragTargetUnderCursor(a, f); - 1 === e && this._mouseController.updateCursor(c.MouseCursor.GRABBING); - this._dragInfo = {windowStartInitial:this._windowStart, windowEndInitial:this._windowEnd, target:e}; - }; - a.prototype.onMouseMove = function(a, f) { - var e = c.MouseCursor.DEFAULT, h = this._getDragTargetUnderCursor(a, f); - 0 !== h && (1 !== h ? e = c.MouseCursor.EW_RESIZE : 1 !== h || this._windowEqRange() || (e = c.MouseCursor.GRAB)); - this._mouseController.updateCursor(e); - }; - a.prototype.onMouseOver = function(a, c) { - this.onMouseMove(a, c); - }; - a.prototype.onMouseOut = function() { - this._mouseController.updateCursor(c.MouseCursor.DEFAULT); - }; - a.prototype.onDrag = function(a, f, e, h, n, q) { - a = this._dragInfo; - if (4 === a.target) { - if (0 !== n) { - a.target = 0 > n ? 2 : 3; - } else { - return; - } - } - f = this._windowStart; - e = this._windowEnd; - n = this._toTimeRelative(n); - switch(a.target) { - case 1: - e = 0 === this._type ? 1 : -1; - f = a.windowStartInitial + e * n; - e = a.windowEndInitial + e * n; - break; - case 2: - f = t(a.windowStartInitial + n, this._rangeStart, e - c.FlameChartBase.MIN_WINDOW_LEN); - break; - case 3: - e = t(a.windowEndInitial + n, f + c.FlameChartBase.MIN_WINDOW_LEN, this._rangeEnd); - break; - default: - return; - } - this._controller.setWindow(f, e); - }; - a.prototype.onDragEnd = function(a, c, e, h, f, q) { - this._dragInfo = null; - this.onMouseMove(e, h); - }; - a.prototype.onClick = function(a, f) { - 1 === this._dragInfo.target && this._mouseController.updateCursor(c.MouseCursor.GRAB); - }; - a.prototype.onHoverStart = function(a, c) { - }; - a.prototype.onHoverEnd = function() { - }; - a.TICK_MAX_WIDTH = 75; - return a; - }(c.FlameChartBase); - c.FlameChartHeader = m; - })(u.Profiler || (u.Profiler = {})); - })(f.Tools || (f.Tools = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(f) { - (function(c) { - (function(c) { - var f = function() { - function a(a, c, e, h, f) { - this.pageLoaded = a; - this.threadsTotal = c; - this.threadsLoaded = e; - this.threadFilesTotal = h; - this.threadFilesLoaded = f; - } - a.prototype.toString = function() { - return "[" + ["pageLoaded", "threadsTotal", "threadsLoaded", "threadFilesTotal", "threadFilesLoaded"].map(function(a, c, e) { - return a + ":" + this[a]; - }, this).join(", ") + "]"; - }; - return a; - }(); - c.TraceLoggerProgressInfo = f; - var r = function() { - function a(a) { - this._baseUrl = a; - this._threads = []; - this._progressInfo = null; - } - a.prototype.loadPage = function(a, c, e) { - this._threads = []; - this._pageLoadCallback = c; - this._pageLoadProgressCallback = e; - this._progressInfo = new f(!1, 0, 0, 0, 0); - this._loadData([a], this._onLoadPage.bind(this)); - }; - Object.defineProperty(a.prototype, "buffers", {get:function() { - for (var a = [], c = 0, e = this._threads.length;c < e;c++) { - a.push(this._threads[c].buffer); - } - return a; - }, enumerable:!0, configurable:!0}); - a.prototype._onProgress = function() { - this._pageLoadProgressCallback && this._pageLoadProgressCallback.call(this, this._progressInfo); - }; - a.prototype._onLoadPage = function(a) { - if (a && 1 == a.length) { - var f = this, e = 0; - a = a[0]; - var h = a.length; - this._threads = Array(h); - this._progressInfo.pageLoaded = !0; - this._progressInfo.threadsTotal = h; - for (var n = 0;n < a.length;n++) { - var q = a[n], k = [q.dict, q.tree]; - q.corrections && k.push(q.corrections); - this._progressInfo.threadFilesTotal += k.length; - this._loadData(k, function(b) { - return function(a) { - a && (a = new c.Thread(a), a.buffer.name = "Thread " + b, f._threads[b] = a); - e++; - f._progressInfo.threadsLoaded++; - f._onProgress(); - e === h && f._pageLoadCallback.call(f, null, f._threads); - }; - }(n), function(a) { - f._progressInfo.threadFilesLoaded++; - f._onProgress(); - }); - } - this._onProgress(); - } else { - this._pageLoadCallback.call(this, "Error loading page.", null); - } - }; - a.prototype._loadData = function(a, c, e) { - var h = 0, f = 0, q = a.length, k = []; - k.length = q; - for (var b = 0;b < q;b++) { - var g = this._baseUrl + a[b], m = new XMLHttpRequest, l = /\.tl$/i.test(g) ? "arraybuffer" : "json"; - m.open("GET", g, !0); - m.responseType = l; - m.onload = function(a, b) { - return function(d) { - if ("json" === b) { - if (d = this.response, "string" === typeof d) { - try { - d = JSON.parse(d), k[a] = d; - } catch (g) { - f++; - } - } else { - k[a] = d; - } - } else { - k[a] = this.response; - } - ++h; - e && e(h); - h === q && c(k); - }; - }(b, l); - m.send(); - } - }; - a.colors = "#0044ff #8c4b00 #cc5c33 #ff80c4 #ffbfd9 #ff8800 #8c5e00 #adcc33 #b380ff #bfd9ff #ffaa00 #8c0038 #bf8f30 #f780ff #cc99c9 #aaff00 #000073 #452699 #cc8166 #cca799 #000066 #992626 #cc6666 #ccc299 #ff6600 #526600 #992663 #cc6681 #99ccc2 #ff0066 #520066 #269973 #61994d #739699 #ffcc00 #006629 #269199 #94994d #738299 #ff0000 #590000 #234d8c #8c6246 #7d7399 #ee00ff #00474d #8c2385 #8c7546 #7c8c69 #eeff00 #4d003d #662e1a #62468c #8c6969 #6600ff #4c2900 #1a6657 #8c464f #8c6981 #44ff00 #401100 #1a2466 #663355 #567365 #d90074 #403300 #101d40 #59562d #66614d #cc0000 #002b40 #234010 #4c2626 #4d5e66 #00a3cc #400011 #231040 #4c3626 #464359 #0000bf #331b00 #80e6ff #311a33 #4d3939 #a69b00 #003329 #80ffb2 #331a20 #40303d #00a658 #40ffd9 #ffc480 #ffe1bf #332b26 #8c2500 #9933cc #80fff6 #ffbfbf #303326 #005e8c #33cc47 #b2ff80 #c8bfff #263332 #00708c #cc33ad #ffe680 #f2ffbf #262a33 #388c00 #335ccc #8091ff #bfffd9".split(" "); - return a; - }(); - c.TraceLogger = r; - })(c.TraceLogger || (c.TraceLogger = {})); - })(f.Profiler || (f.Profiler = {})); - })(f.Tools || (f.Tools = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(f) { - (function(c) { - (function(f) { - var m; - (function(c) { - c[c.START_HI = 0] = "START_HI"; - c[c.START_LO = 4] = "START_LO"; - c[c.STOP_HI = 8] = "STOP_HI"; - c[c.STOP_LO = 12] = "STOP_LO"; - c[c.TEXTID = 16] = "TEXTID"; - c[c.NEXTID = 20] = "NEXTID"; - })(m || (m = {})); - m = function() { - function f(a) { - 2 <= a.length && (this._text = a[0], this._data = new DataView(a[1]), this._buffer = new c.TimelineBuffer, this._walkTree(0)); - } - Object.defineProperty(f.prototype, "buffer", {get:function() { - return this._buffer; - }, enumerable:!0, configurable:!0}); - f.prototype._walkTree = function(a) { - var c = this._data, p = this._buffer; - do { - var e = a * f.ITEM_SIZE, h = 4294967295 * c.getUint32(e + 0) + c.getUint32(e + 4), n = 4294967295 * c.getUint32(e + 8) + c.getUint32(e + 12), q = c.getUint32(e + 16), e = c.getUint32(e + 20), k = 1 === (q & 1), q = q >>> 1, q = this._text[q]; - p.enter(q, null, h / 1E6); - k && this._walkTree(a + 1); - p.leave(q, null, n / 1E6); - a = e; - } while (0 !== a); - }; - f.ITEM_SIZE = 24; - return f; - }(); - f.Thread = m; - })(c.TraceLogger || (c.TraceLogger = {})); - })(f.Profiler || (f.Profiler = {})); - })(f.Tools || (f.Tools = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - (function(c) { - var t = f.NumberUtilities.clamp, m = function() { - function a() { - this.length = 0; - this.lines = []; - this.format = []; - this.time = []; - this.repeat = []; - this.length = 0; - } - a.prototype.append = function(a, c) { - var e = this.lines; - 0 < e.length && e[e.length - 1] === a ? this.repeat[e.length - 1]++ : (this.lines.push(a), this.repeat.push(1), this.format.push(c ? {backgroundFillStyle:c} : void 0), this.time.push(performance.now()), this.length++); - }; - a.prototype.get = function(a) { - return this.lines[a]; - }; - a.prototype.getFormat = function(a) { - return this.format[a]; - }; - a.prototype.getTime = function(a) { - return this.time[a]; - }; - a.prototype.getRepeat = function(a) { - return this.repeat[a]; - }; - return a; - }(); - c.Buffer = m; - var r = function() { - function a(a) { - this.lineColor = "#2A2A2A"; - this.alternateLineColor = "#262626"; - this.textColor = "#FFFFFF"; - this.selectionColor = "#96C9F3"; - this.selectionTextColor = "#000000"; - this.ratio = 1; - this.showLineNumbers = !0; - this.showLineCounter = this.showLineTime = !1; - this.canvas = a; - this.canvas.focus(); - this.context = a.getContext("2d", {original:!0}); - this.context.fillStyle = "#FFFFFF"; - this.fontSize = 10; - this.columnIndex = this.pageIndex = this.lineIndex = 0; - this.selection = null; - this.lineHeight = 15; - this.hasFocus = !1; - window.addEventListener("resize", this._resizeHandler.bind(this), !1); - this._resizeHandler(); - this.textMarginBottom = this.textMarginLeft = 4; - this.refreshFrequency = 0; - this.buffer = new m; - a.addEventListener("keydown", function(a) { - var d = 0; - switch(a.keyCode) { - case s: - this.showLineNumbers = !this.showLineNumbers; - break; - case A: - this.showLineTime = !this.showLineTime; - break; - case q: - d = -1; - break; - case k: - d = 1; - break; - case c: - d = -this.pageLineCount; - break; - case e: - d = this.pageLineCount; - break; - case h: - d = -this.lineIndex; - break; - case f: - d = this.buffer.length - this.lineIndex; - break; - case b: - this.columnIndex -= a.metaKey ? 10 : 1; - 0 > this.columnIndex && (this.columnIndex = 0); - a.preventDefault(); - break; - case g: - this.columnIndex += a.metaKey ? 10 : 1; - a.preventDefault(); - break; - case r: - a.metaKey && (this.selection = {start:0, end:this.buffer.length}, a.preventDefault()); - break; - case l: - if (a.metaKey) { - var m = ""; - if (this.selection) { - for (var t = this.selection.start;t <= this.selection.end;t++) { - m += this.buffer.get(t) + "\n"; - } - } else { - m = this.buffer.get(this.lineIndex); - } - alert(m); - } - ; - } - a.metaKey && (d *= this.pageLineCount); - d && (this.scroll(d), a.preventDefault()); - d && a.shiftKey ? this.selection ? this.lineIndex > this.selection.start ? this.selection.end = this.lineIndex : this.selection.start = this.lineIndex : 0 < d ? this.selection = {start:this.lineIndex - d, end:this.lineIndex} : 0 > d && (this.selection = {start:this.lineIndex, end:this.lineIndex - d}) : d && (this.selection = null); - this.paint(); - }.bind(this), !1); - a.addEventListener("focus", function(a) { - this.hasFocus = !0; - }.bind(this), !1); - a.addEventListener("blur", function(a) { - this.hasFocus = !1; - }.bind(this), !1); - var c = 33, e = 34, h = 36, f = 35, q = 38, k = 40, b = 37, g = 39, r = 65, l = 67, s = 78, A = 84; - } - a.prototype.resize = function() { - this._resizeHandler(); - }; - a.prototype._resizeHandler = function() { - var a = this.canvas.parentElement, c = a.clientWidth, a = a.clientHeight - 1, e = window.devicePixelRatio || 1; - 1 !== e ? (this.ratio = e / 1, this.canvas.width = c * this.ratio, this.canvas.height = a * this.ratio, this.canvas.style.width = c + "px", this.canvas.style.height = a + "px") : (this.ratio = 1, this.canvas.width = c, this.canvas.height = a); - this.pageLineCount = Math.floor(this.canvas.height / this.lineHeight); - }; - a.prototype.gotoLine = function(a) { - this.lineIndex = t(a, 0, this.buffer.length - 1); - }; - a.prototype.scrollIntoView = function() { - this.lineIndex < this.pageIndex ? this.pageIndex = this.lineIndex : this.lineIndex >= this.pageIndex + this.pageLineCount && (this.pageIndex = this.lineIndex - this.pageLineCount + 1); - }; - a.prototype.scroll = function(a) { - this.gotoLine(this.lineIndex + a); - this.scrollIntoView(); - }; - a.prototype.paint = function() { - var a = this.pageLineCount; - this.pageIndex + a > this.buffer.length && (a = this.buffer.length - this.pageIndex); - var c = this.textMarginLeft, e = c + (this.showLineNumbers ? 5 * (String(this.buffer.length).length + 2) : 0), h = e + (this.showLineTime ? 40 : 10), f = h + 25; - this.context.font = this.fontSize + 'px Consolas, "Liberation Mono", Courier, monospace'; - this.context.setTransform(this.ratio, 0, 0, this.ratio, 0, 0); - for (var q = this.canvas.width, k = this.lineHeight, b = 0;b < a;b++) { - var g = b * this.lineHeight, m = this.pageIndex + b, l = this.buffer.get(m), s = this.buffer.getFormat(m), A = this.buffer.getRepeat(m), r = 1 < m ? this.buffer.getTime(m) - this.buffer.getTime(0) : 0; - this.context.fillStyle = m % 2 ? this.lineColor : this.alternateLineColor; - s && s.backgroundFillStyle && (this.context.fillStyle = s.backgroundFillStyle); - this.context.fillRect(0, g, q, k); - this.context.fillStyle = this.selectionTextColor; - this.context.fillStyle = this.textColor; - this.selection && m >= this.selection.start && m <= this.selection.end && (this.context.fillStyle = this.selectionColor, this.context.fillRect(0, g, q, k), this.context.fillStyle = this.selectionTextColor); - this.hasFocus && m === this.lineIndex && (this.context.fillStyle = this.selectionColor, this.context.fillRect(0, g, q, k), this.context.fillStyle = this.selectionTextColor); - 0 < this.columnIndex && (l = l.substring(this.columnIndex)); - g = (b + 1) * this.lineHeight - this.textMarginBottom; - this.showLineNumbers && this.context.fillText(String(m), c, g); - this.showLineTime && this.context.fillText(r.toFixed(1).padLeft(" ", 6), e, g); - 1 < A && this.context.fillText(String(A).padLeft(" ", 3), h, g); - this.context.fillText(l, f, g); - } - }; - a.prototype.refreshEvery = function(a) { - function c() { - e.paint(); - e.refreshFrequency && setTimeout(c, e.refreshFrequency); - } - var e = this; - this.refreshFrequency = a; - e.refreshFrequency && setTimeout(c, e.refreshFrequency); - }; - a.prototype.isScrolledToBottom = function() { - return this.lineIndex === this.buffer.length - 1; - }; - return a; - }(); - c.Terminal = r; - })(u.Terminal || (u.Terminal = {})); - })(f.Tools || (f.Tools = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(f) { - (function(c) { - var f = function() { - function c(f) { - this._lastWeightedTime = this._lastTime = this._index = 0; - this._gradient = "#FF0000 #FF1100 #FF2300 #FF3400 #FF4600 #FF5700 #FF6900 #FF7B00 #FF8C00 #FF9E00 #FFAF00 #FFC100 #FFD300 #FFE400 #FFF600 #F7FF00 #E5FF00 #D4FF00 #C2FF00 #B0FF00 #9FFF00 #8DFF00 #7CFF00 #6AFF00 #58FF00 #47FF00 #35FF00 #24FF00 #12FF00 #00FF00".split(" "); - this._canvas = f; - this._context = f.getContext("2d"); - window.addEventListener("resize", this._resizeHandler.bind(this), !1); - this._resizeHandler(); - } - c.prototype._resizeHandler = function() { - var c = this._canvas.parentElement, a = c.clientWidth, c = c.clientHeight - 1, d = window.devicePixelRatio || 1; - 1 !== d ? (this._ratio = d / 1, this._canvas.width = a * this._ratio, this._canvas.height = c * this._ratio, this._canvas.style.width = a + "px", this._canvas.style.height = c + "px") : (this._ratio = 1, this._canvas.width = a, this._canvas.height = c); - }; - c.prototype.tickAndRender = function(c) { - "undefined" === typeof c && (c = !1); - if (0 === this._lastTime) { - this._lastTime = performance.now(); - } else { - var a = (performance.now() - this._lastTime) * (1 - .9) + .9 * this._lastWeightedTime, d = this._context, f = 2 * this._ratio, e = (this._canvas.width - 20) / (f + 1) | 0, h = this._index++; - this._index > e && (this._index = 0); - d.globalAlpha = 1; - d.fillStyle = "black"; - d.fillRect(20 + h * (f + 1), 0, 4 * f, this._canvas.height); - e = 1E3 / 60 / a; - d.fillStyle = this._gradient[e * (this._gradient.length - 1) | 0]; - d.globalAlpha = c ? .5 : 1; - d.fillRect(20 + h * (f + 1), 0, f, this._canvas.height * e | 0); - 0 === h % 16 && (d.globalAlpha = 1, d.fillStyle = "black", d.fillRect(0, 0, 20, this._canvas.height), d.fillStyle = "white", d.font = "10px Arial", d.fillText((1E3 / a).toFixed(0), 2, 8)); - this._lastTime = performance.now(); - this._lastWeightedTime = a; - } - }; - return c; - }(); - c.FPS = f; - })(f.Mini || (f.Mini = {})); - })(f.Tools || (f.Tools = {})); -})(Shumway || (Shumway = {})); -console.timeEnd("Load Parser Dependencies"); -console.time("Load SWF Parser"); -(function(f) { - (function(u) { - u.timelineBuffer = new f.Tools.Profiler.TimelineBuffer("Parser"); - u.enterTimeline = function(c, f) { - }; - u.leaveTimeline = function(c) { - }; - })(f.SWF || (f.SWF = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - (function(c) { - function t(a, c, d) { - return m(a, c, d) << 32 - d >> 32 - d; - } - function m(c, d, f) { - for (var q = d.bitBuffer, k = d.bitLength;f > k;) { - q = q << 8 | c[d.pos++], k += 8; - } - k -= f; - c = q >>> k & a[f]; - d.bitBuffer = q; - d.bitLength = k; - return c; - } - var r = Math.pow; - c.readSi8 = function(a, c) { - return c.getInt8(c.pos++); - }; - c.readSi16 = function(a, c) { - return c.getInt16(c.pos, c.pos += 2); - }; - c.readSi32 = function(a, c) { - return c.getInt32(c.pos, c.pos += 4); - }; - c.readUi8 = function(a, c) { - return a[c.pos++]; - }; - c.readUi16 = function(a, c) { - return c.getUint16(c.pos, c.pos += 2); - }; - c.readUi32 = function(a, c) { - return c.getUint32(c.pos, c.pos += 4); - }; - c.readFixed = function(a, c) { - return c.getInt32(c.pos, c.pos += 4) / 65536; - }; - c.readFixed8 = function(a, c) { - return c.getInt16(c.pos, c.pos += 2) / 256; - }; - c.readFloat16 = function(a, c) { - var d = c.getUint16(c.pos); - c.pos += 2; - var f = d >> 15 ? -1 : 1, k = (d & 31744) >> 10, d = d & 1023; - return k ? 31 === k ? d ? NaN : Infinity * f : f * r(2, k - 15) * (1 + d / 1024) : f * r(2, -14) * (d / 1024); - }; - c.readFloat = function(a, c) { - return c.getFloat32(c.pos, c.pos += 4); - }; - c.readDouble = function(a, c) { - return c.getFloat64(c.pos, c.pos += 8); - }; - c.readEncodedU32 = function(a, c) { - var d = a[c.pos++]; - if (!(d & 128)) { - return d; - } - d = d & 127 | a[c.pos++] << 7; - if (!(d & 16384)) { - return d; - } - d = d & 16383 | a[c.pos++] << 14; - if (!(d & 2097152)) { - return d; - } - d = d & 2097151 | a[c.pos++] << 21; - return d & 268435456 ? d & 268435455 | a[c.pos++] << 28 : d; - }; - c.readBool = function(a, c) { - return!!a[c.pos++]; - }; - c.align = function(a, c) { - c.align(); - }; - c.readSb = t; - for (var a = new Uint32Array(33), d = 1, p = 0;32 >= d;++d) { - a[d] = p = p << 1 | 1; - } - c.readUb = m; - c.readFb = function(a, c, d) { - return t(a, c, d) / 65536; - }; - c.readString = function(a, c, d) { - var q = c.pos; - if (d) { - a = a.subarray(q, q += d); - } else { - d = 0; - for (var k = q;a[k];k++) { - d++; - } - a = a.subarray(q, q += d); - q++; - } - c.pos = q; - c = f.StringUtilities.utf8encode(a); - 0 <= c.indexOf("\x00") && (c = c.split("\x00").join("")); - return c; - }; - c.readBinary = function(a, c, d, f) { - d || (d = c.end - c.pos); - a = a.subarray(c.pos, c.pos += d); - if (f) { - return a; - } - d = new Uint8Array(d); - d.set(a); - return d; - }; - })(u.Parser || (u.Parser = {})); - })(f.SWF || (f.SWF = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(f) { - (function(c) { - function f(a, b, d, e, h) { - d || (d = {}); - d.id = c.readUi16(a, b); - var s = d.lineBounds = {}; - g(a, b, s, e, h); - if (s = d.isMorph = 46 === h || 84 === h) { - var k = d.lineBoundsMorph = {}; - g(a, b, k, e, h); - } - if (k = d.canHaveStrokes = 83 === h || 84 === h) { - var n = d.fillBounds = {}; - g(a, b, n, e, h); - s && (n = d.fillBoundsMorph = {}, g(a, b, n, e, h)); - d.flags = c.readUi8(a, b); - } - if (s) { - d.offsetMorph = c.readUi32(a, b); - var l = d, q, p, n = y(a, b, l, e, h, s, k), m = n.lineBits, A = n.fillBits, r = l.records = []; - do { - var V = {}, n = B(a, b, V, e, h, s, A, m, k, p); - q = n.eos; - A = n.fillBits; - m = n.lineBits; - p = n.bits; - r.push(V); - } while (!q); - n = w(a, b, l, e, h); - A = n.fillBits; - m = n.lineBits; - l = l.recordsMorph = []; - do { - r = {}, n = B(a, b, r, e, h, s, A, m, k, p), q = n.eos, A = n.fillBits, m = n.lineBits, p = n.bits, l.push(r); - } while (!q); - } else { - A = d; - p = y(a, b, A, e, h, s, k); - n = p.fillBits; - m = p.lineBits; - l = A.records = []; - do { - r = {}, p = B(a, b, r, e, h, s, n, m, k, q), A = p.eos, n = p.fillBits, m = p.lineBits, q = p.bits, l.push(r); - } while (!A); - } - return d; - } - function m(a, b, d, e, h) { - var g; - d || (d = {}); - if (4 < h) { - g = d.flags = 26 < h ? c.readUi16(a, b) : c.readUi8(a, b); - d.depth = c.readUi16(a, b); - g & 2048 && (d.className = c.readString(a, b, 0)); - g & 2 && (d.symbolId = c.readUi16(a, b)); - if (g & 4) { - var f = d.matrix = {}; - s(a, b, f, e, h); - } - g & 8 && (f = d.cxform = {}, A(a, b, f, e, h)); - g & 16 && (d.ratio = c.readUi16(a, b)); - g & 32 && (d.name = c.readString(a, b, 0)); - g & 64 && (d.clipDepth = c.readUi16(a, b)); - if (g & 256) { - for (var n = c.readUi8(a, b), f = d.filters = [];n--;) { - var k = {}; - D(a, b, k, e, h); - f.push(k); - } - } - g & 512 && (d.blendMode = c.readUi8(a, b)); - g & 1024 && (d.bmpCache = c.readUi8(a, b)); - if (g & 128) { - c.readUi16(a, b); - 6 <= e ? c.readUi32(a, b) : c.readUi16(a, b); - h = d.events = []; - do { - var f = {}, n = a, k = b, l = f, q = e, p = 6 <= q ? c.readUi32(n, k) : c.readUi16(n, k), m = l.eoe = !p, w = 0; - l.onKeyUp = p >> 7 & 1; - l.onKeyDown = p >> 6 & 1; - l.onMouseUp = p >> 5 & 1; - l.onMouseDown = p >> 4 & 1; - l.onMouseMove = p >> 3 & 1; - l.onUnload = p >> 2 & 1; - l.onEnterFrame = p >> 1 & 1; - l.onLoad = p & 1; - 6 <= q && (l.onDragOver = p >> 15 & 1, l.onRollOut = p >> 14 & 1, l.onRollOver = p >> 13 & 1, l.onReleaseOutside = p >> 12 & 1, l.onRelease = p >> 11 & 1, l.onPress = p >> 10 & 1, l.onInitialize = p >> 9 & 1, l.onData = p >> 8 & 1, l.onConstruct = 7 <= q ? p >> 18 & 1 : 0, w = l.keyPress = p >> 17 & 1, l.onDragOut = p >> 16 & 1); - m || (q = l.length = c.readUi32(n, k), w && (l.keyCode = c.readUi8(n, k)), l.actionsData = c.readBinary(n, k, q - +w, !1)); - n = m; - h.push(f); - } while (!n); - } - g & 1024 && (e = d, h = c.readUi8(a, b) | c.readUi8(a, b) << 24 | c.readUi8(a, b) << 16 | c.readUi8(a, b) << 8, e.backgroundColor = h); - g & 512 && (d.visibility = c.readUi8(a, b)); - } else { - d.symbolId = c.readUi16(a, b), d.depth = c.readUi16(a, b), d.flags |= 4, g = d.matrix = {}, s(a, b, g, e, h), b.remaining() && (d.flags |= 8, g = d.cxform = {}, A(a, b, g, e, h)); - } - return d; - } - function r(a, b, d, e, h) { - d || (d = {}); - 5 === h && (d.symbolId = c.readUi16(a, b)); - d.depth = c.readUi16(a, b); - return d; - } - function a(a, b, d, e, h) { - d || (d = {}); - d.id = c.readUi16(a, b); - 21 < h ? (e = c.readUi32(a, b), 90 === h && (d.deblock = c.readFixed8(a, b)), e = d.imgData = c.readBinary(a, b, e, !0), d.alphaData = c.readBinary(a, b, 0, !0)) : e = d.imgData = c.readBinary(a, b, 0, !0); - switch(e[0] << 8 | e[1]) { - case 65496: - ; - case 65497: - d.mimeType = "image/jpeg"; - break; - case 35152: - d.mimeType = "image/png"; - break; - case 18249: - d.mimeType = "image/gif"; - break; - default: - d.mimeType = "application/octet-stream"; - } - 6 === h && (d.incomplete = 1); - return d; - } - function d(a, b, d, e, h) { - var g; - d || (d = {}); - d.id = c.readUi16(a, b); - if (7 == h) { - var f = d.characters = []; - do { - var n = {}; - g = a; - var l = b, k = n, q = e, p = h, m = c.readUi8(g, l), w = k.eob = !m; - k.flags = 8 <= q ? (m >> 5 & 1 ? 512 : 0) | (m >> 4 & 1 ? 256 : 0) : 0; - k.stateHitTest = m >> 3 & 1; - k.stateDown = m >> 2 & 1; - k.stateOver = m >> 1 & 1; - k.stateUp = m & 1; - w || (k.symbolId = c.readUi16(g, l), k.depth = c.readUi16(g, l), m = k.matrix = {}, s(g, l, m, q, p), 34 === p && (m = k.cxform = {}, A(g, l, m, q, p)), k.flags & 256 && (k.filterCount = c.readUi8(g, l), m = k.filters = {}, D(g, l, m, q, p)), k.flags & 512 && (k.blendMode = c.readUi8(g, l))); - g = w; - f.push(n); - } while (!g); - d.actionsData = c.readBinary(a, b, 0, !1); - } else { - g = c.readUi8(a, b); - d.trackAsMenu = g >> 7 & 1; - f = c.readUi16(a, b); - n = d.characters = []; - do { - l = {}; - k = c.readUi8(a, b); - g = l.eob = !k; - l.flags = 8 <= e ? (k >> 5 & 1 ? 512 : 0) | (k >> 4 & 1 ? 256 : 0) : 0; - l.stateHitTest = k >> 3 & 1; - l.stateDown = k >> 2 & 1; - l.stateOver = k >> 1 & 1; - l.stateUp = k & 1; - if (!g) { - l.symbolId = c.readUi16(a, b); - l.depth = c.readUi16(a, b); - k = l.matrix = {}; - s(a, b, k, e, h); - 34 === h && (k = l.cxform = {}, A(a, b, k, e, h)); - if (l.flags & 256) { - for (q = c.readUi8(a, b), k = d.filters = [];q--;) { - p = {}, D(a, b, p, e, h), k.push(p); - } - } - l.flags & 512 && (l.blendMode = c.readUi8(a, b)); - } - n.push(l); - } while (!g); - if (f) { - e = d.buttonActions = []; - do { - h = {}, g = a, f = b, n = h, l = c.readUi16(g, f), k = c.readUi16(g, f), n.keyCode = (k & 65024) >> 9, n.stateTransitionFlags = k & 511, n.actionsData = c.readBinary(g, f, (l || 4) - 4, !1), e.push(h); - } while (0 < b.remaining()); - } - } - return d; - } - function p(a, b, d, e, h) { - var f; - d || (d = {}); - d.id = c.readUi16(a, b); - var k = d.bbox = {}; - g(a, b, k, e, h); - k = d.matrix = {}; - s(a, b, k, e, h); - e = d.glyphBits = c.readUi8(a, b); - var k = d.advanceBits = c.readUi8(a, b), n = d.records = []; - do { - var q = {}; - f = a; - var p = b, m = q, A = h, w = e, r = k, B = void 0; - c.align(f, p); - var y = c.readUb(f, p, 8), t = m.eot = !y, B = f, D = p, u = m, G = y, y = u.hasFont = G >> 3 & 1, C = u.hasColor = G >> 2 & 1, K = u.hasMoveY = G >> 1 & 1, G = u.hasMoveX = G & 1; - y && (u.fontId = c.readUi16(B, D)); - C && (u.color = 33 === A ? l(B, D) : V(B, D)); - G && (u.moveX = c.readSi16(B, D)); - K && (u.moveY = c.readSi16(B, D)); - y && (u.fontHeight = c.readUi16(B, D)); - if (!t) { - for (B = c.readUi8(f, p), B = m.glyphCount = B, m = m.entries = [];B--;) { - D = {}, u = f, A = p, y = D, C = r, y.glyphIndex = c.readUb(u, A, w), y.advance = c.readSb(u, A, C), m.push(D); - } - } - f = t; - n.push(q); - } while (!f); - return d; - } - function e(a, b, d, e, h) { - d || (d = {}); - 59 === h && (d.spriteId = c.readUi16(a, b)); - d.actionsData = c.readBinary(a, b, 0, !1); - return d; - } - function h(a, b, d, e, h) { - d || (d = {}); - 15 == h && (d.soundId = c.readUi16(a, b)); - 89 == h && (d.soundClassName = c.readString(a, b, 0)); - e = d.soundInfo = {}; - c.readUb(a, b, 2); - e.stop = c.readUb(a, b, 1); - e.noMultiple = c.readUb(a, b, 1); - h = e.hasEnvelope = c.readUb(a, b, 1); - var g = e.hasLoops = c.readUb(a, b, 1), f = e.hasOutPoint = c.readUb(a, b, 1); - if (e.hasInPoint = c.readUb(a, b, 1)) { - e.inPoint = c.readUi32(a, b); - } - f && (e.outPoint = c.readUi32(a, b)); - g && (e.loopCount = c.readUi16(a, b)); - if (h) { - for (h = e.envelopeCount = c.readUi8(a, b), e = e.envelopes = [];h--;) { - var g = {}, f = a, k = b, s = g; - s.pos44 = c.readUi32(f, k); - s.volumeLeft = c.readUi16(f, k); - s.volumeRight = c.readUi16(f, k); - e.push(g); - } - } - return d; - } - function n(a, b, d, e, h) { - d || (d = {}); - e = c.readUi8(a, b); - d.playbackRate = e >> 2 & 3; - d.playbackSize = e >> 1 & 1; - d.playbackType = e & 1; - e = c.readUi8(a, b); - h = d.streamCompression = e >> 4 & 15; - d.streamRate = e >> 2 & 3; - d.streamSize = e >> 1 & 1; - d.streamType = e & 1; - d.samplesCount = c.readUi32(a, b); - 2 == h && (d.latencySeek = c.readSi16(a, b)); - return d; - } - function q(a, b, d, e, h) { - d || (d = {}); - d.id = c.readUi16(a, b); - e = d.format = c.readUi8(a, b); - d.width = c.readUi16(a, b); - d.height = c.readUi16(a, b); - d.hasAlpha = 36 === h; - 3 === e && (d.colorTableSize = c.readUi8(a, b)); - d.bmpData = c.readBinary(a, b, 0, !1); - return d; - } - function k(a, b, d, e, h) { - d || (d = {}); - d.id = c.readUi16(a, b); - var f = d.hasLayout = c.readUb(a, b, 1); - 5 < e ? d.shiftJis = c.readUb(a, b, 1) : c.readUb(a, b, 1); - d.smallText = c.readUb(a, b, 1); - d.ansi = c.readUb(a, b, 1); - var k = d.wideOffset = c.readUb(a, b, 1), s = d.wide = c.readUb(a, b, 1); - d.italic = c.readUb(a, b, 1); - d.bold = c.readUb(a, b, 1); - 5 < e ? d.language = c.readUi8(a, b) : (c.readUi8(a, b), d.language = 0); - var l = c.readUi8(a, b); - d.name = c.readString(a, b, l); - 75 === h && (d.resolution = 20); - var l = d.glyphCount = c.readUi16(a, b), n = b.pos; - if (k) { - for (var k = d.offsets = [], q = l;q--;) { - k.push(c.readUi32(a, b)); - } - d.mapOffset = c.readUi32(a, b); - } else { - k = d.offsets = []; - for (q = l;q--;) { - k.push(c.readUi16(a, b)); - } - d.mapOffset = c.readUi16(a, b); - } - k = d.glyphs = []; - for (q = l;q--;) { - var p = {}; - 1 === d.offsets[l - q] + n - b.pos ? (c.readUi8(a, b), k.push({records:[{type:0, eos:!0, hasNewStyles:0, hasLineStyle:0, hasFillStyle1:0, hasFillStyle0:0, move:0}]})) : (C(a, b, p, e, h), k.push(p)); - } - if (s) { - for (n = d.codes = [], k = l;k--;) { - n.push(c.readUi16(a, b)); - } - } else { - for (n = d.codes = [], k = l;k--;) { - n.push(c.readUi8(a, b)); - } - } - if (f) { - d.ascent = c.readUi16(a, b); - d.descent = c.readUi16(a, b); - d.leading = c.readSi16(a, b); - f = d.advance = []; - for (n = l;n--;) { - f.push(c.readSi16(a, b)); - } - for (f = d.bbox = [];l--;) { - n = {}, g(a, b, n, e, h), f.push(n); - } - h = c.readUi16(a, b); - for (e = d.kerning = [];h--;) { - l = {}, f = a, n = b, k = l, s ? (k.code1 = c.readUi16(f, n), k.code2 = c.readUi16(f, n)) : (k.code1 = c.readUi8(f, n), k.code2 = c.readUi8(f, n)), k.adjustment = c.readUi16(f, n), e.push(l); - } - } - return d; - } - function b(a, b, d, e, h) { - d || (d = {}); - d.flags = 82 === h ? c.readUi32(a, b) : 0; - d.name = 82 === h ? c.readString(a, b, 0) : ""; - d.data = c.readBinary(a, b, 0, !1); - return d; - } - function g(a, b, d, e, h) { - c.align(a, b); - var g = c.readUb(a, b, 5); - e = c.readSb(a, b, g); - h = c.readSb(a, b, g); - var f = c.readSb(a, b, g), g = c.readSb(a, b, g); - d.xMin = e; - d.xMax = h; - d.yMin = f; - d.yMax = g; - c.align(a, b); - } - function V(a, b) { - return(c.readUi8(a, b) << 24 | c.readUi8(a, b) << 16 | c.readUi8(a, b) << 8 | 255) >>> 0; - } - function l(a, b) { - return c.readUi8(a, b) << 24 | c.readUi8(a, b) << 16 | c.readUi8(a, b) << 8 | c.readUi8(a, b); - } - function s(a, b, d, e, h) { - c.align(a, b); - c.readUb(a, b, 1) ? (h = c.readUb(a, b, 5), d.a = c.readFb(a, b, h), d.d = c.readFb(a, b, h)) : (d.a = 1, d.d = 1); - c.readUb(a, b, 1) ? (h = c.readUb(a, b, 5), d.b = c.readFb(a, b, h), d.c = c.readFb(a, b, h)) : (d.b = 0, d.c = 0); - h = c.readUb(a, b, 5); - e = c.readSb(a, b, h); - h = c.readSb(a, b, h); - d.tx = e; - d.ty = h; - c.align(a, b); - } - function A(a, b, d, e, h) { - c.align(a, b); - e = c.readUb(a, b, 1); - var g = c.readUb(a, b, 1), f = c.readUb(a, b, 4); - g ? (d.redMultiplier = c.readSb(a, b, f), d.greenMultiplier = c.readSb(a, b, f), d.blueMultiplier = c.readSb(a, b, f), d.alphaMultiplier = 4 < h ? c.readSb(a, b, f) : 256) : (d.redMultiplier = 256, d.greenMultiplier = 256, d.blueMultiplier = 256, d.alphaMultiplier = 256); - e ? (d.redOffset = c.readSb(a, b, f), d.greenOffset = c.readSb(a, b, f), d.blueOffset = c.readSb(a, b, f), d.alphaOffset = 4 < h ? c.readSb(a, b, f) : 0) : (d.redOffset = 0, d.greenOffset = 0, d.blueOffset = 0, d.alphaOffset = 0); - c.align(a, b); - } - function B(a, b, d, e, h, g, f, k, l, s) { - var n, q = d.type = c.readUb(a, b, 1), p = c.readUb(a, b, 5); - n = d.eos = !(q || p); - if (q) { - e = s, h = h = h = 0, h = d.isStraight = p >> 4, e = (p & 15) + 2, h ? (h = d.isGeneral = c.readUb(a, b, 1)) ? (d.deltaX = c.readSb(a, b, e), d.deltaY = c.readSb(a, b, e)) : (h = d.isVertical = c.readUb(a, b, 1)) ? d.deltaY = c.readSb(a, b, e) : d.deltaX = c.readSb(a, b, e) : (d.controlDeltaX = c.readSb(a, b, e), d.controlDeltaY = c.readSb(a, b, e), d.anchorDeltaX = c.readSb(a, b, e), d.anchorDeltaY = c.readSb(a, b, e)), a = e; - } else { - var m = 0, A = 0, w = 0, r = 0, B = 0, m = 2 < h ? d.hasNewStyles = p >> 4 : d.hasNewStyles = 0, A = d.hasLineStyle = p >> 3 & 1, w = d.hasFillStyle1 = p >> 2 & 1, r = d.hasFillStyle0 = p >> 1 & 1; - if (B = d.move = p & 1) { - s = c.readUb(a, b, 5), d.moveX = c.readSb(a, b, s), d.moveY = c.readSb(a, b, s); - } - r && (d.fillStyle0 = c.readUb(a, b, f)); - w && (d.fillStyle1 = c.readUb(a, b, f)); - A && (d.lineStyle = c.readUb(a, b, k)); - m && (a = y(a, b, d, e, h, g, l), k = a.lineBits, f = a.fillBits); - a = s; - } - return{type:q, flags:p, eos:n, fillBits:f, lineBits:k, bits:a}; - } - function y(a, b, d, e, h, g, f) { - var k, s = c.readUi8(a, b); - k = 2 < h && 255 === s ? c.readUi16(a, b) : s; - for (s = d.fillStyles = [];k--;) { - var n = {}; - u(a, b, n, e, h, g); - s.push(n); - } - s = c.readUi8(a, b); - k = 2 < h && 255 === s ? c.readUi16(a, b) : s; - for (s = d.lineStyles = [];k--;) { - var n = {}, q = a, p = b, m = n, A = e, r = h, B = g, y = f; - m.width = c.readUi16(q, p); - B && (m.widthMorph = c.readUi16(q, p)); - if (y) { - c.align(q, p); - m.startCapsStyle = c.readUb(q, p, 2); - var y = m.jointStyle = c.readUb(q, p, 2), t = m.hasFill = c.readUb(q, p, 1); - m.noHscale = c.readUb(q, p, 1); - m.noVscale = c.readUb(q, p, 1); - m.pixelHinting = c.readUb(q, p, 1); - c.readUb(q, p, 5); - m.noClose = c.readUb(q, p, 1); - m.endCapsStyle = c.readUb(q, p, 2); - 2 === y && (m.miterLimitFactor = c.readFixed8(q, p)); - t ? (m = m.fillStyle = {}, u(q, p, m, A, r, B)) : (m.color = l(q, p), B && (m.colorMorph = l(q, p))); - } else { - m.color = 22 < r ? l(q, p) : V(q, p), B && (m.colorMorph = l(q, p)); - } - s.push(n); - } - a = w(a, b, d, e, h); - return{fillBits:a.fillBits, lineBits:a.lineBits}; - } - function w(a, b, d, e, h) { - c.align(a, b); - d = c.readUb(a, b, 4); - a = c.readUb(a, b, 4); - return{fillBits:d, lineBits:a}; - } - function u(a, b, d, e, h, g) { - var f = d.type = c.readUi8(a, b); - switch(f) { - case 0: - d.color = 22 < h || g ? l(a, b) : V(a, b); - g && (d.colorMorph = l(a, b)); - break; - case 16: - ; - case 18: - ; - case 19: - var k = d.matrix = {}; - s(a, b, k, e, h); - g && (k = d.matrixMorph = {}, s(a, b, k, e, h)); - 83 === h ? (d.spreadMode = c.readUb(a, b, 2), d.interpolationMode = c.readUb(a, b, 2)) : c.readUb(a, b, 4); - k = d.count = c.readUb(a, b, 4); - for (e = d.records = [];k--;) { - var n = {}, q = a, p = b, m = n, A = h, w = g; - m.ratio = c.readUi8(q, p); - m.color = 22 < A ? l(q, p) : V(q, p); - w && (m.ratioMorph = c.readUi8(q, p), m.colorMorph = l(q, p)); - e.push(n); - } - 19 === f && (d.focalPoint = c.readSi16(a, b), g && (d.focalPointMorph = c.readSi16(a, b))); - break; - case 64: - ; - case 65: - ; - case 66: - ; - case 67: - d.bitmapId = c.readUi16(a, b), k = d.matrix = {}, s(a, b, k, e, h), g && (g = d.matrixMorph = {}, s(a, b, g, e, h)), d.condition = 64 === f || 67 === f; - } - } - function D(a, b, d, e, h) { - e = d.type = c.readUi8(a, b); - switch(e) { - case 0: - ; - case 2: - ; - case 3: - ; - case 4: - ; - case 7: - h = 4 === e || 7 === e ? c.readUi8(a, b) : 1; - for (var g = d.colors = [], f = h;f--;) { - g.push(l(a, b)); - } - 3 === e && (d.hightlightColor = l(a, b)); - if (4 === e || 7 === e) { - for (g = d.ratios = [];h--;) { - g.push(c.readUi8(a, b)); - } - } - d.blurX = c.readFixed(a, b); - d.blurY = c.readFixed(a, b); - 2 !== e && (d.angle = c.readFixed(a, b), d.distance = c.readFixed(a, b)); - d.strength = c.readFixed8(a, b); - d.inner = c.readUb(a, b, 1); - d.knockout = c.readUb(a, b, 1); - d.compositeSource = c.readUb(a, b, 1); - 3 === e || 4 === e || 7 === e ? (d.onTop = c.readUb(a, b, 1), d.quality = c.readUb(a, b, 4)) : d.quality = c.readUb(a, b, 5); - break; - case 1: - d.blurX = c.readFixed(a, b); - d.blurY = c.readFixed(a, b); - d.quality = c.readUb(a, b, 5); - c.readUb(a, b, 3); - break; - case 5: - h = d.matrixX = c.readUi8(a, b); - g = d.matrixY = c.readUi8(a, b); - d.divisor = c.readFloat(a, b); - d.bias = c.readFloat(a, b); - e = d.matrix = []; - for (h *= g;h--;) { - e.push(c.readFloat(a, b)); - } - d.color = l(a, b); - c.readUb(a, b, 6); - d.clamp = c.readUb(a, b, 1); - d.preserveAlpha = c.readUb(a, b, 1); - break; - case 6: - for (d = d.matrix = [], e = 20;e--;) { - d.push(c.readFloat(a, b)); - } - ; - } - } - function C(a, b, c, d, e) { - var h; - h = w(a, b, c, d, e); - var g = h.fillBits; - h = h.lineBits; - var f = c.records = []; - do { - var k = {}; - h = B(a, b, k, d, e, !1, g, h, !1, void 0); - c = h.eos; - g = h.fillBits; - h = h.lineBits; - f.push(k); - } while (!c); - } - c.tagHandler = {0:void 0, 1:void 0, 2:f, 4:m, 5:r, 6:a, 7:d, 8:function(a, b, d, e, h) { - d || (d = {}); - d.id = 0; - d.imgData = c.readBinary(a, b, 0, !1); - d.mimeType = "application/octet-stream"; - return d; - }, 9:function(a, b, c, d, e) { - c || (c = {}); - c.color = V(a, b); - return c; - }, 10:function(a, b, d, e, h) { - d || (d = {}); - d.id = c.readUi16(a, b); - for (var g = c.readUi16(a, b), f = d.glyphCount = g / 2, k = [], s = f - 1;s--;) { - k.push(c.readUi16(a, b)); - } - d.offsets = [g].concat(k); - for (g = d.glyphs = [];f--;) { - k = {}, C(a, b, k, e, h), g.push(k); - } - return d; - }, 11:p, 12:e, 13:void 0, 14:function(a, b, d, e, h) { - d || (d = {}); - d.id = c.readUi16(a, b); - e = c.readUi8(a, b); - d.soundFormat = e >> 4 & 15; - d.soundRate = e >> 2 & 3; - d.soundSize = e >> 1 & 1; - d.soundType = e & 1; - d.samplesCount = c.readUi32(a, b); - d.soundData = c.readBinary(a, b, 0, !1); - return d; - }, 15:h, 17:void 0, 18:n, 19:function(a, b, d, e, h) { - d || (d = {}); - d.data = c.readBinary(a, b, 0, !1); - return d; - }, 20:q, 21:a, 22:f, 23:void 0, 24:void 0, 26:m, 28:r, 32:f, 33:p, 34:d, 35:a, 36:q, 37:function(a, b, d, e, h) { - d || (d = {}); - d.id = c.readUi16(a, b); - var f = d.bbox = {}; - g(a, b, f, e, h); - e = c.readUi16(a, b); - h = d.hasText = e >> 7 & 1; - d.wordWrap = e >> 6 & 1; - d.multiline = e >> 5 & 1; - d.password = e >> 4 & 1; - d.readonly = e >> 3 & 1; - var f = d.hasColor = e >> 2 & 1, k = d.hasMaxLength = e >> 1 & 1, s = d.hasFont = e & 1, n = d.hasFontClass = e >> 15 & 1; - d.autoSize = e >> 14 & 1; - var q = d.hasLayout = e >> 13 & 1; - d.noSelect = e >> 12 & 1; - d.border = e >> 11 & 1; - d.wasStatic = e >> 10 & 1; - d.html = e >> 9 & 1; - d.useOutlines = e >> 8 & 1; - s && (d.fontId = c.readUi16(a, b)); - n && (d.fontClass = c.readString(a, b, 0)); - s && (d.fontHeight = c.readUi16(a, b)); - f && (d.color = l(a, b)); - k && (d.maxLength = c.readUi16(a, b)); - q && (d.align = c.readUi8(a, b), d.leftMargin = c.readUi16(a, b), d.rightMargin = c.readUi16(a, b), d.indent = c.readSi16(a, b), d.leading = c.readSi16(a, b)); - d.variableName = c.readString(a, b, 0); - h && (d.initialText = c.readString(a, b, 0)); - return d; - }, 39:void 0, 43:function(a, b, d, e, h) { - d || (d = {}); - d.name = c.readString(a, b, 0); - return d; - }, 45:n, 46:f, 48:k, 56:function(a, b, d, e, h) { - d || (d = {}); - h = c.readUi16(a, b); - for (e = d.exports = [];h--;) { - var g = {}; - g.symbolId = c.readUi16(a, b); - g.className = c.readString(a, b, 0); - e.push(g); - } - return d; - }, 57:void 0, 58:void 0, 59:e, 60:void 0, 61:void 0, 62:void 0, 64:void 0, 65:void 0, 66:void 0, 69:function(a, b, d, e, h) { - d || (d = {}); - c.readUb(a, b, 1); - d.useDirectBlit = c.readUb(a, b, 1); - d.useGpu = c.readUb(a, b, 1); - d.hasMetadata = c.readUb(a, b, 1); - d.doAbc = c.readUb(a, b, 1); - d.noCrossDomainCaching = c.readUb(a, b, 1); - d.relativeUrls = c.readUb(a, b, 1); - d.network = c.readUb(a, b, 1); - c.readUb(a, b, 24); - return d; - }, 70:m, 71:void 0, 72:b, 73:void 0, 74:void 0, 75:k, 76:function(a, b, d, e, h) { - d || (d = {}); - h = c.readUi16(a, b); - for (e = d.exports = [];h--;) { - var g = {}; - g.symbolId = c.readUi16(a, b); - g.className = c.readString(a, b, 0); - e.push(g); - } - return d; - }, 77:void 0, 78:function(a, b, d, e, h) { - d || (d = {}); - d.symbolId = c.readUi16(a, b); - var f = d.splitter = {}; - g(a, b, f, e, h); - return d; - }, 82:b, 83:f, 84:f, 86:function(a, b, d, e, h) { - d || (d = {}); - h = c.readEncodedU32(a, b); - for (e = d.scenes = [];h--;) { - var g = {}; - g.offset = c.readEncodedU32(a, b); - g.name = c.readString(a, b, 0); - e.push(g); - } - h = c.readEncodedU32(a, b); - for (e = d.labels = [];h--;) { - g = {}, g.frame = c.readEncodedU32(a, b), g.name = c.readString(a, b, 0), e.push(g); - } - return d; - }, 87:function(a, b, d, e, h) { - d || (d = {}); - d.id = c.readUi16(a, b); - c.readUi32(a, b); - d.data = c.readBinary(a, b, 0, !1); - return d; - }, 88:void 0, 89:h, 90:a, 91:function(a, b, d, e, h) { - d || (d = {}); - d.id = c.readUi16(a, b); - c.readUb(a, b, 5); - e = d.hasFontData = c.readUb(a, b, 1); - d.italic = c.readUb(a, b, 1); - d.bold = c.readUb(a, b, 1); - d.name = c.readString(a, b, 0); - e && (d.data = c.readBinary(a, b, 0, !1)); - return d; - }}; - c.readHeader = function(a, b, d, e, h) { - d || (d = {}); - e = d.bbox = {}; - c.align(a, b); - var g = c.readUb(a, b, 5); - h = c.readSb(a, b, g); - var f = c.readSb(a, b, g), k = c.readSb(a, b, g), g = c.readSb(a, b, g); - e.xMin = h; - e.xMax = f; - e.yMin = k; - e.yMax = g; - c.align(a, b); - e = c.readUi8(a, b); - d.frameRate = c.readUi8(a, b) + e / 256; - d.frameCount = c.readUi16(a, b); - return d; - }; - })(f.Parser || (f.Parser = {})); - })(f.SWF || (f.SWF = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - (function(c) { - function t(a, d, n, q, k, b) { - function g() { - d.pos = s; - a._readTag = m; - return!1; - } - var p = a.tags, l = d.bytes, s, m = null; - a._readTag && (m = a._readTag, a._readTag = null); - try { - for (;d.pos < d.end;) { - s = d.pos; - if (d.pos + 2 > d.end) { - return g(); - } - var r = c.readUi16(l, d); - if (!r) { - q = !0; - break; - } - var y = r >> 6, w = r & 63; - if (63 === w) { - if (d.pos + 4 > d.end) { - return g(); - } - w = c.readUi32(l, d); - } - if (m) { - if (1 === y && 1 === m.code) { - m.repeat++; - d.pos += w; - continue; - } - p.push(m); - k && void 0 !== m.id && (a.bytesLoaded = a.bytesTotal * d.pos / d.end | 0, k(a)); - m = null; - } - if (d.pos + w > d.end) { - return g(); - } - var u = d.substream(d.pos, d.pos += w), D = u.bytes, w = {code:y}; - if (39 === y) { - w.type = "sprite", w.id = c.readUi16(D, u), w.frameCount = c.readUi16(D, u), w.tags = [], t(w, u, n, !0, null, null) || f.Debug.error("Invalid SWF tag structure"); - } else { - if (1 === y) { - w.repeat = 1; - } else { - var C = c.tagHandler[y]; - C && C(D, u, w, n, y); - } - } - m = w; - } - m && q || d.pos >= d.end ? (m && (m.finalTag = !0, p.push(m)), k && (a.bytesLoaded = a.bytesTotal, k(a))) : a._readTag = m; - } catch (z) { - throw b && b(z), z; - } - return!0; - } - function m(e) { - function h(a, b, d) { - var h = new Uint8Array(b); - h.set(a); - var f = a.length; - q = {push:function(a, b) { - h.set(a, f); - f += a.length; - }, close:function() { - var a = {}, b; - "image/jpeg" == d ? b = c.parseJpegChunks(a, h) : (c.parsePngHeaders(a, h), b = [h.subarray(0, f)]); - var k = 0, n = 0; - b.forEach(function(a) { - k += a.byteLength; - }); - var q = new Uint8Array(k); - b.forEach(function(a) { - q.set(a, n); - n += a.byteLength; - }); - a.id = 0; - a.data = q; - a.mimeType = d; - a = {command:"image", type:"image", props:a}; - e.oncomplete && e.oncomplete(a); - }}; - e.onimgprogress(b); - } - var n = new a, q = null; - return{push:function(a, b) { - if (null !== q) { - return q.push(a, b); - } - if (!n.push(a, 8)) { - return null; - } - var g = n.getHead(), m = g[0], l = g[1], s = g[2]; - if (87 === l && 83 === s) { - f.Debug.assert(70 === m || 67 === m, "Unsupported compression format: " + (90 === m ? "LZMA" : String(m))), m = 67 === m, g = g[3], l = n.createStream(), l.pos += 4, l = c.readUi32(null, l) - 8, q = new p(g, l, e), m && (q = new d(q)), q.push(n.getTail(8), b); - } else { - var g = !1, A; - 255 === m && 216 === l && 255 === s ? (g = !0, A = "image/jpeg") : 137 === m && 80 === l && 78 === s && (g = !0, A = "image/png"); - g && h(a, b.bytesTotal, A); - } - n = null; - }, close:function() { - if (n) { - var a = {command:"empty", data:n.getBytes()}; - e.oncomplete && e.oncomplete(a); - } - q && q.close && q.close(); - }}; - } - var r = f.ArrayUtilities.Inflate, a = function() { - function a(c) { - "undefined" === typeof c && (c = 16); - this._bufferSize = c; - this._buffer = new Uint8Array(this._bufferSize); - this._pos = 0; - } - a.prototype.push = function(a, c) { - var d = this._pos + a.length; - if (this._bufferSize < d) { - for (var e = this._bufferSize;e < d;) { - e <<= 1; - } - d = new Uint8Array(e); - 0 < this._bufferSize && d.set(this._buffer); - this._buffer = d; - this._bufferSize = e; - } - this._buffer.set(a, this._pos); - this._pos += a.length; - if (c) { - return this._pos >= c; - } - }; - a.prototype.getHead = function() { - return this._buffer.subarray(0, 8); - }; - a.prototype.getTail = function(a) { - return this._buffer.subarray(a, this._pos); - }; - a.prototype.removeHead = function(a) { - a = this.getTail(a); - this._buffer = new Uint8Array(this._bufferSize); - this._buffer.set(a); - this._pos = a.length; - }; - Object.defineProperty(a.prototype, "arrayBuffer", {get:function() { - return this._buffer.buffer; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "length", {get:function() { - return this._pos; - }, enumerable:!0, configurable:!0}); - a.prototype.getBytes = function() { - return this._buffer.subarray(0, this._pos); - }; - a.prototype.createStream = function() { - return new u.Stream(this.arrayBuffer, 0, this.length); - }; - return a; - }(), d = function() { - function a(c) { - this._inflate = new r(!0); - this._inflate.onData = function(a) { - c.push(a, this._progressInfo); - }.bind(this); - } - a.prototype.push = function(a, c) { - this._progressInfo = c; - this._inflate.push(a); - }; - a.prototype.close = function() { - this._inflate = null; - }; - return a; - }(), p = function() { - function d(c, e, f) { - this.swf = {swfVersion:c, parseTime:0, bytesLoaded:void 0, bytesTotal:void 0, fileAttributes:void 0, tags:void 0}; - this._buffer = new a(32768); - this._initialize = !0; - this._totalRead = 0; - this._length = e; - this._options = f; - } - d.prototype.push = function(a, d) { - if (0 !== a.length) { - var e = this.swf, k = e.swfVersion, b = this._buffer, g = this._options, p, l = !1; - d && (e.bytesLoaded = d.bytesLoaded, e.bytesTotal = d.bytesTotal, l = d.bytesLoaded >= d.bytesTotal); - if (this._initialize) { - if (!b.push(a, 27)) { - return; - } - p = b.createStream(); - var s = p.bytes; - c.readHeader(s, p, e, null, null); - if (4420 == c.readUi16(s, p)) { - p.pos + 4 > p.end && f.Debug.error("Not enough data."); - var s = p.substream(p.pos, p.pos += 4), m = {code:69}; - (0,c.tagHandler[69])(s.bytes, s, m, k, 69); - e.fileAttributes = m; - } else { - p.pos -= 2, e.fileAttributes = {}; - } - if (g.onstart) { - g.onstart(e); - } - e.tags = []; - this._initialize = !1; - } else { - b.push(a), p = b.createStream(); - } - s = performance.now(); - t(e, p, k, l, g.onprogress, g.onexception); - e.parseTime += performance.now() - s; - k = p.pos; - b.removeHead(k); - this._totalRead += k; - if (g.oncomplete && e.tags[e.tags.length - 1].finalTag) { - g.oncomplete(e); - } - } - }; - d.prototype.close = function() { - }; - return d; - }(); - c.parseAsync = m; - c.parse = function(a, c) { - "undefined" === typeof c && (c = {}); - var d = m(c), f = new Uint8Array(a); - d.push(f, {bytesLoaded:f.length, bytesTotal:f.length}); - d.close(); - }; - })(u.Parser || (u.Parser = {})); - })(f.SWF || (f.SWF = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - (function(c) { - var t = f.Debug.assert, m = f.Debug.assertUnreachable, r = f.IntegerUtilities.roundToMultipleOfFour, a = f.ArrayUtilities.Inflate; - (function(a) { - a[a.FORMAT_COLORMAPPED = 3] = "FORMAT_COLORMAPPED"; - a[a.FORMAT_15BPP = 4] = "FORMAT_15BPP"; - a[a.FORMAT_24BPP = 5] = "FORMAT_24BPP"; - })(c.BitmapFormat || (c.BitmapFormat = {})); - c.defineBitmap = function(c) { - var p, e = 0; - switch(c.format) { - case 3: - p = c.width; - var e = c.height, h = c.hasAlpha, n = r(p) - p, q = h ? 4 : 3, k = r((c.colorTableSize + 1) * q), b = k + (p + n) * e, g = a.inflate(c.bmpData, b), V = new Uint32Array(p * e), l = 0, s = 0; - if (h) { - for (h = 0;h < e;h++) { - for (var A = 0;A < p;A++) { - var s = g[k++] << 2, B = g[s + 3], y = g[s + 0], w = g[s + 1], s = g[s + 2]; - V[l++] = s << 24 | w << 16 | y << 8 | B; - } - k += n; - } - } else { - for (h = 0;h < e;h++) { - for (A = 0;A < p;A++) { - s = g[k++] * q, B = 255, y = g[s + 0], w = g[s + 1], s = g[s + 2], V[l++] = s << 24 | w << 16 | y << 8 | B; - } - k += n; - } - } - t(k === b, "We should be at the end of the data buffer now."); - t(l === p * e, "Should have filled the entire image."); - p = new Uint8Array(V.buffer); - e = 1; - break; - case 5: - q = c.width; - b = c.height; - n = c.hasAlpha; - p = b * q * 4; - e = a.inflate(c.bmpData, p); - if (n) { - p = e; - } else { - n = new Uint32Array(q * b); - q *= b; - for (g = b = 0;g < q;g++) { - b++, V = e[b++], k = e[b++], l = e[b++], n[g] = l << 24 | k << 16 | V << 8 | 255; - } - t(b === p, "We should be at the end of the data buffer now."); - p = new Uint8Array(n.buffer); - } - e = 1; - break; - case 4: - f.Debug.notImplemented("parse15BPP"); - p = null; - e = 1; - break; - default: - m("invalid bitmap format"); - } - return{type:"image", id:c.id, width:c.width, height:c.height, mimeType:"application/octet-stream", data:p, dataType:e}; - }; - })(u.Parser || (u.Parser = {})); - })(f.SWF || (f.SWF = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - (function(c) { - var t = f.Debug.assert; - c.defineButton = function(c, f) { - for (var a = c.characters, d = {up:[], over:[], down:[], hitTest:[]}, p = 0, e;(e = a[p++]) && !e.eob;) { - var h = f[e.symbolId]; - t(h, "undefined character button"); - h = {symbolId:h.id, depth:e.depth, flags:e.matrix ? 4 : 0, matrix:e.matrix}; - e.stateUp && d.up.push(h); - e.stateOver && d.over.push(h); - e.stateDown && d.down.push(h); - e.stateHitTest && d.hitTest.push(h); - } - return{type:"button", id:c.id, buttonActions:c.buttonActions, states:d}; - }; - })(u.Parser || (u.Parser = {})); - })(f.SWF || (f.SWF = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(f) { - (function(c) { - function f(a) { - for (var c = 0;2 <= a;) { - a /= 2, ++c; - } - return d(2, c); - } - function m(a) { - return n(a >> 8 & 255, a & 255); - } - function r(a) { - return m(a >> 16) + m(a); - } - function a(a) { - for (var c = 0, b = 0, d = 0, e = 0, h = 0;h < a.length;h++) { - var f = a[h]; - if (f) { - for (var f = f.records, n, p = 0, m = 0, w = 0;w < f.length;w++) { - n = f[w]; - if (n.type) { - n.isStraight ? (p += n.deltaX || 0, m += -(n.deltaY || 0)) : (p += n.controlDeltaX, m += -n.controlDeltaY, p += n.anchorDeltaX, m += -n.anchorDeltaY); - } else { - if (n.eos) { - break; - } - n.move && (p = n.moveX, m = -n.moveY); - } - c > p && (c = p); - b > m && (b = m); - d < p && (d = p); - e < m && (e = m); - } - } - } - return 5E3 < Math.max(d - c, e - b); - } - var d = Math.pow, p = Math.min, e = Math.max, h = Math.log, n = String.fromCharCode; - c.defineFont = function(c, d) { - var b = "swf-font-" + c.id, g = c.name || b, n = {type:"font", id:c.id, name:g, bold:1 === c.bold, italic:1 === c.italic, codes:null, metrics:null, data:c.data, originalSize:!1}, l = c.glyphs, s = l ? c.glyphCount = l.length : 0; - if (!s) { - return n; - } - var A = {}, B = [], y = {}, w = [], u, D = !("advance" in c), C = 48 === c.code, z = 75 === c.code; - D && (c.advance = []); - u = Math.max.apply(null, c.codes) || 35; - if (c.codes) { - for (var v = 0;v < c.codes.length;v++) { - var x = c.codes[v]; - if (32 > x || -1 < B.indexOf(x)) { - u++, 8232 == u && (u = 8240), x = u; - } - B.push(x); - y[x] = v; - } - u = B.concat(); - B.sort(function(a, b) { - return a - b; - }); - for (var v = 0, H;void 0 !== (x = B[v++]);) { - var T = x, J = T; - for (H = [v - 1];void 0 !== (x = B[v]) && J + 1 === x;) { - ++J, H.push(v), ++v; - } - w.push([T, J, H]); - } - } else { - H = []; - for (v = 0;v < s;v++) { - x = 57344 + v, B.push(x), y[x] = v, H.push(v); - } - w.push([57344, 57344 + s - 1, H]); - u = B.concat(); - } - var E = c.resolution || 1; - C && a(l) && (E = 20, n.originalSize = !0); - C = Math.ceil(c.ascent / E) || 1024; - H = -Math.ceil(c.descent / E) || 0; - for (var R = c.leading / E || 0, F = A["OS/2"] = "", S = "", U = "", X = "", v = 0, I;I = w[v++];) { - T = I[0], J = I[1], x = I[2][0], F += m(T), S += m(J), U += m(x - T + 1 & 65535), X += m(0); - } - S += "\u00ff\u00ff"; - F += "\u00ff\u00ff"; - U += "\x00\u0001"; - X += "\x00\x00"; - w = w.length + 1; - v = 2 * f(w); - T = 2 * w - v; - v = "\x00\x00" + m(2 * w) + m(v) + m(h(w) / h(2)) + m(T) + S + "\x00\x00" + F + U + X; - A.cmap = "\x00\x00\x00\u0001\x00\u0003\x00\u0001\x00\x00\x00\f\x00\u0004" + m(v.length + 4) + v; - var Q = "\x00\u0001\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\x00", F = "\x00\x00", w = 16, S = 0, X = [], T = [], J = []; - I = []; - for (var v = U = 0, ba = {};void 0 !== (x = B[v++]);) { - for (var O = l[y[x]], Y = O.records, L = 0, M = 0, N = "", ga = "", K = 0, P = [], K = -1, O = 0;O < Y.length;O++) { - N = Y[O]; - if (N.type) { - 0 > K && (K = 0, P[K] = {data:[], commands:[], xMin:0, xMax:0, yMin:0, yMax:0}); - if (N.isStraight) { - P[K].commands.push(2); - var W = (N.deltaX || 0) / E, Z = -(N.deltaY || 0) / E; - } else { - P[K].commands.push(3), W = N.controlDeltaX / E, Z = -N.controlDeltaY / E, L += W, M += Z, P[K].data.push(L, M), W = N.anchorDeltaX / E, Z = -N.anchorDeltaY / E; - } - L += W; - M += Z; - P[K].data.push(L, M); - } else { - if (N.eos) { - break; - } - if (N.move) { - K++; - P[K] = {data:[], commands:[], xMin:0, xMax:0, yMin:0, yMax:0}; - P[K].commands.push(1); - var ea = N.moveX / E, N = -N.moveY / E, W = ea - L, Z = N - M, L = ea, M = N; - P[K].data.push(L, M); - } - } - -1 < K && (P[K].xMin > L && (P[K].xMin = L), P[K].yMin > M && (P[K].yMin = M), P[K].xMax < L && (P[K].xMax = L), P[K].yMax < M && (P[K].yMax = M)); - } - z || P.sort(function(a, b) { - return(b.xMax - b.xMin) * (b.yMax - b.yMin) - (a.xMax - a.xMin) * (a.yMax - a.yMin); - }); - ba[x] = P; - } - for (v = 0;void 0 !== (x = B[v++]);) { - for (var O = l[y[x]], Y = O.records, P = ba[x], ha = 1, K = 0, W = M = N = L = "", x = M = L = 0, Y = -1024, ea = 0, ia = -1024, ga = N = "", K = 0, K = -1, ca = [], ja = [], O = 0;O < P.length;O++) { - ca = ca.concat(P[O].data), ja = ja.concat(P[O].commands); - } - for (var $ = M = L = 0, aa = 0, da = "", P = "", fa = 0, K = 0, ha = 1, ga = "", O = 0;O < ja.length;O++) { - W = ja[O], 1 === W ? (K && (++ha, ga += m(K - 1)), $ = ca[fa++], aa = ca[fa++], W = $ - L, Z = aa - M, N += "\u0001", da += m(W), P += m(Z), L = $, M = aa) : 2 === W ? ($ = ca[fa++], aa = ca[fa++], W = $ - L, Z = aa - M, N += "\u0001", da += m(W), P += m(Z), L = $, M = aa) : 3 === W && ($ = ca[fa++], aa = ca[fa++], W = $ - L, Z = aa - M, N += "\x00", da += m(W), P += m(Z), L = $, M = aa, K++, $ = ca[fa++], aa = ca[fa++], W = $ - L, Z = aa - M, N += "\u0001", da += m(W), P += m(Z), L = - $, M = aa), K++, K > S && (S = K), x > L && (x = L), ea > M && (ea = M), Y < L && (Y = L), ia < M && (ia = M); - } - L = ga += m((K || 1) - 1); - M = da; - W = P; - O || (x = Y = ea = ia = 0, N += "1"); - O = m(ha) + m(x) + m(ea) + m(Y) + m(ia) + L + "\x00\x00" + N + M + W; - O.length & 1 && (O += "\x00"); - Q += O; - F += m(w / 2); - w += O.length; - X.push(x); - T.push(Y); - J.push(ea); - I.push(ia); - ha > U && (U = ha); - K > S && (S = K); - D && c.advance.push((Y - x) * E * 1.3); - } - F += m(w / 2); - A.glyf = Q; - z || (v = Math.min.apply(null, J), 0 > v && (H = H || v)); - A["OS/2"] = "\x00\u0001\x00\x00" + m(c.bold ? 700 : 400) + "\x00\u0005\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ALF " + m((c.italic ? 1 : 0) | (c.bold ? 32 : 0)) + m(B[0]) + m(B[B.length - 1]) + m(C) + m(H) + m(R) + m(C) + m(-H) + "\x00\x00\x00\x00\x00\x00\x00\x00"; - A.head = "\x00\u0001\x00\x00\x00\u0001\x00\x00\x00\x00\x00\x00_\u000f<\u00f5\x00\x0B\u0004\x00\x00\x00\x00\x00" + r(Date.now()) + "\x00\x00\x00\x00" + r(Date.now()) + m(p.apply(null, X)) + m(p.apply(null, J)) + m(e.apply(null, T)) + m(e.apply(null, I)) + m((c.italic ? 2 : 0) | (c.bold ? 1 : 0)) + "\x00\b\x00\u0002\x00\x00\x00\x00"; - B = c.advance; - A.hhea = "\x00\u0001\x00\x00" + m(C) + m(H) + m(R) + m(B ? e.apply(null, B) : 1024) + "\x00\x00\x00\x00\u0003\u00b8\x00\u0001\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + m(s + 1); - l = "\x00\x00\x00\x00"; - for (v = 0;v < s;++v) { - l += m(B ? B[v] / E : 1024) + "\x00\x00"; - } - A.hmtx = l; - if (c.kerning) { - B = c.kerning; - E = B.length; - v = 2 * f(E); - E = "\x00\x00\x00\u0001\x00\x00" + m(14 + 6 * E) + "\x00\u0001" + m(E) + m(v) + m(h(E) / h(2)) + m(2 * E - v); - for (v = 0;N = B[v++];) { - E += m(y[N.code1]) + m(y[N.code2]) + m(N.adjustment); - } - A.kern = E; - } - A.loca = F; - A.maxp = "\x00\u0001\x00\x00" + m(s + 1) + m(S) + m(U) + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; - v = g.replace(/ /g, ""); - g = [c.copyright || "Original licence", g, "Unknown", b, g, "1.0", v, "Unknown", "Unknown", "Unknown"]; - v = g.length; - b = "\x00\x00" + m(v) + m(12 * v + 6); - for (v = w = 0;s = g[v++];) { - b += "\x00\u0001\x00\x00\x00\x00" + m(v - 1) + m(s.length) + m(w), w += s.length; - } - A.name = b + g.join(""); - A.post = "\x00\u0003\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; - g = Object.keys(A); - v = g.length; - s = "\x00\u0001\x00\x00" + m(v) + "\x00\u0080\x00\u0003\x00 "; - y = ""; - w = 16 * v + s.length; - for (v = 0;b = g[v++];) { - B = A[b]; - E = B.length; - for (s += b + "\x00\x00\x00\x00" + r(w) + r(E);E & 3;) { - B += "\x00", ++E; - } - for (y += B;w & 3;) { - ++w; - } - w += E; - } - A = s + y; - C = {ascent:C / 1024, descent:-H / 1024, leading:R / 1024}; - H = new Uint8Array(A.length); - for (v = 0;v < A.length;v++) { - H[v] = A.charCodeAt(v) & 255; - } - n.codes = u; - n.metrics = C; - n.data = H; - return n; - }; - })(f.Parser || (f.Parser = {})); - })(f.SWF || (f.SWF = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - (function(c) { - function t(a, c) { - return a[c] << 8 | a[c + 1]; - } - function m(a, c) { - var f = 0, p = c.length, k = [], b; - do { - for (var g = f;f < p && 255 !== c[f];) { - ++f; - } - for (;f < p && 255 === c[f];) { - ++f; - } - b = c[f++]; - if (218 === b) { - f = p; - } else { - if (217 === b) { - f += 2; - continue; - } else { - if (208 > b || 216 < b) { - var m = t(c, f); - 192 <= b && 195 >= b && (a.height = t(c, f + 3), a.width = t(c, f + 5)); - f += m; - } - } - } - k.push(c.subarray(g, f)); - } while (f < p); - d(a.width && a.height, "bad jpeg image"); - return k; - } - function r(a, c) { - if (73 === c[12] && 72 === c[13] && 68 === c[14] && 82 === c[15]) { - a.width = c[16] << 24 | c[17] << 16 | c[18] << 8 | c[19]; - a.height = c[20] << 24 | c[21] << 16 | c[22] << 8 | c[23]; - var d = c[26]; - a.hasAlpha = 4 === d || 6 === d; - } - } - function a(a) { - for (var c = 0, d = 0;d < a.length;d++) { - c += a[d].length; - } - for (var c = new Uint8Array(c), f = 0, d = 0;d < a.length;d++) { - var k = a[d]; - c.set(k, f); - f += k.length; - } - return c; - } - var d = f.Debug.assert, p = f.ArrayUtilities.Inflate; - c.parseJpegChunks = m; - c.parsePngHeaders = r; - c.defineImage = function(c, h) { - var n = {type:"image", id:c.id, mimeType:c.mimeType}, q = c.imgData; - if ("image/jpeg" === c.mimeType) { - var k = c.alphaData; - if (k) { - var b = new f.JPEG.JpegImage; - b.parse(a(m(n, q))); - d(n.width === b.width); - d(n.height === b.height); - var q = n.width * n.height, k = p.inflate(k, q), g = n.data = new Uint8ClampedArray(4 * q); - b.copyToImageData(n); - for (var b = 0, t = 3;b < q;b++, t += 4) { - g[t] = k[b]; - } - n.mimeType = "application/octet-stream"; - n.dataType = 3; - } else { - q = m(n, q), c.incomplete && (b = h[0], d(b, "missing jpeg tables"), (b = b.data) && b.size && (q[0] = q[0].subarray(2), q.unshift(b.slice(0, b.size - 2)))), n.data = a(q), n.dataType = 4; - } - } else { - r(n, q), n.data = q, n.dataType = 5; - } - return n; - }; - })(u.Parser || (u.Parser = {})); - })(f.SWF || (f.SWF = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - (function(c) { - var t = f.Debug.assert; - c.defineLabel = function(c, f) { - for (var a = c.records, d = c.bbox, p = "", e = [], h = [], n = 12, q = "Times Roman", k = 0, b = 0, g = 0, u = 0, l, s;(l = a[u++]) && !l.eot;) { - l.hasFont && (q = f[l.fontId], t(q, "undefined label font"), s = q.codes, h.push(q.id), n = l.fontHeight, q.originalSize || (n /= 20), q = "swffont" + q.id); - l.hasColor && (k = l.color >>> 8); - l.hasMoveX && (b = l.moveX, b < d.xMin && (d.xMin = b)); - l.hasMoveY && (g = l.moveY, g < d.yMin && (d.yMin = g)); - var A = ""; - l = l.entries; - for (var B = 0, y;y = l[B++];) { - var w = s[y.glyphIndex]; - t(w, "undefined label glyph"); - A += String.fromCharCode(w); - e.push(b, g); - b += y.advance; - } - p += '' + A.replace(/[<>&]/g, function(a) { - return "<" === a ? "<" : ">" === a ? ">" : "&"; - }) + ""; - } - a = {type:"text", id:c.id, fillBounds:d, matrix:c.matrix, tag:{hasText:!0, initialText:p, html:!0, readonly:!0}, coords:e, static:!0, require:null}; - h.length && (a.require = h); - return a; - }; - })(u.Parser || (u.Parser = {})); - })(f.SWF || (f.SWF = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - (function(c) { - function t(a, b, c, d) { - if (a) { - if (b.fill0) { - if (d = d[b.fill0 - 1], b.fill1 || b.line) { - d.addSegment(a.toReversed()); - } else { - a.isReversed = !0; - return; - } - } - b.line && b.fill1 && (d = c[b.line - 1], d.addSegment(a.clone())); - } - } - function m(a, b, c, d) { - if (b) { - a.miterLimit = 2 * (a.miterLimitFactor || 1.5); - if (!a.color && a.hasFill) { - var e = m(a.fillStyle, !1, c, d); - a.type = e.type; - a.transform = e.transform; - a.records = e.records; - a.colors = e.colors; - a.ratios = e.ratios; - a.focalPoint = e.focalPoint; - a.bitmapId = e.bitmapId; - a.bitmapIndex = e.bitmapIndex; - a.repeat = e.repeat; - a.fillStyle = null; - return a; - } - a.type = 0; - } - if (void 0 === a.type || 0 === a.type) { - return a; - } - switch(a.type) { - case 16: - ; - case 18: - ; - case 19: - e = a.records; - b = a.colors = []; - c = a.ratios = []; - for (d = 0;d < e.length;d++) { - var h = e[d]; - b.push(h.color); - c.push(h.ratio); - } - e = 819.2; - break; - case 64: - ; - case 65: - ; - case 66: - ; - case 67: - a.smooth = 66 !== a.type && 67 !== a.type; - a.repeat = 65 !== a.type && 67 !== a.type; - c[a.bitmapId] ? (a.bitmapIndex = d.length, d.push(a.bitmapId), e = .05) : a.bitmapIndex = -1; - break; - default: - q("shape parser encountered invalid fill style"); - } - if (!a.matrix) { - return a.transform = g, a; - } - b = a.matrix; - a.transform = {a:b.a * e, b:b.b * e, c:b.c * e, d:b.d * e, tx:b.tx / 20, ty:b.ty / 20}; - a.matrix = null; - return a; - } - function r(a, b, c, d) { - for (var e = [], g = 0;g < a.length;g++) { - var h = m(a[g], b, c, d); - e[g] = b ? new l(null, h) : new l(h, null); - } - return e; - } - function a(a, b) { - var c = a.noHscale ? a.noVscale ? 0 : 2 : a.noVscale ? 3 : 1, d = h(a.width, 0, 5100) | 0; - b.lineStyle(d, a.color, a.pixelHinting, c, a.endCapsStyle, a.jointStyle, a.miterLimit); - } - var d = f.Bounds, p = f.ArrayUtilities.DataBuffer, e = f.ShapeData, h = f.NumberUtilities.clamp, n = f.Debug.assert, q = f.Debug.assertUnreachable, k = Array.prototype.push, b; - (function(a) { - a[a.Solid = 0] = "Solid"; - a[a.LinearGradient = 16] = "LinearGradient"; - a[a.RadialGradient = 18] = "RadialGradient"; - a[a.FocalRadialGradient = 19] = "FocalRadialGradient"; - a[a.RepeatingBitmap = 64] = "RepeatingBitmap"; - a[a.ClippedBitmap = 65] = "ClippedBitmap"; - a[a.NonsmoothedRepeatingBitmap = 66] = "NonsmoothedRepeatingBitmap"; - a[a.NonsmoothedClippedBitmap = 67] = "NonsmoothedClippedBitmap"; - })(b || (b = {})); - var g = {a:1, b:0, c:0, d:1, tx:0, ty:0}; - c.defineShape = function(a, b) { - for (var c = [], g = r(a.fillStyles, !1, b, c), h = r(a.lineStyles, !0, b, c), f = a.records, p = h, q = a.recordsMorph || null, h = null !== q, z = {fill0:0, fill1:0, line:0}, v = null, x, H, T = f.length - 1, J = 0, E = 0, R = 0, F = 0, S, U = 0, X = 0;U < T;U++) { - var I = f[U], Q; - h && (Q = q[X++]); - if (0 === I.type) { - v && t(v, z, p, g), I.hasNewStyles && (x || (x = []), k.apply(x, g), g = r(I.fillStyles, !1, b, c), k.apply(x, p), p = r(I.lineStyles, !0, b, c), H && (x.push(H), H = null), z = {fill0:0, fill1:0, line:0}), I.hasFillStyle0 && (z.fill0 = I.fillStyle0), I.hasFillStyle1 && (z.fill1 = I.fillStyle1), I.hasLineStyle && (z.line = I.lineStyle), z.fill1 ? S = g[z.fill1 - 1] : z.line ? S = p[z.line - 1] : z.fill0 && (S = g[z.fill0 - 1]), I.move && (J = I.moveX | 0, E = I.moveY | 0), S && (v = u.FromDefaults(h), - S.addSegment(v), h ? (0 === Q.type ? (R = Q.moveX | 0, F = Q.moveY | 0) : (R = J, F = E, X--), v.morphMoveTo(J, E, R, F)) : v.moveTo(J, E)); - } else { - n(1 === I.type); - v || (H || (H = new l(null, m({color:{red:0, green:0, blue:0, alpha:0}, width:20}, !0, b, c))), v = u.FromDefaults(h), H.addSegment(v), h ? v.morphMoveTo(J, E, R, F) : v.moveTo(J, E)); - if (h) { - for (;Q && 0 === Q.type;) { - Q = q[X++]; - } - Q || (Q = I); - } - if (!I.isStraight || h && !Q.isStraight) { - var ba, O, Y; - I.isStraight ? (Y = I.deltaX | 0, I = I.deltaY | 0, ba = J + (Y >> 1), O = E + (I >> 1), J += Y, E += I) : (ba = J + I.controlDeltaX | 0, O = E + I.controlDeltaY | 0, J = ba + I.anchorDeltaX | 0, E = O + I.anchorDeltaY | 0); - v.curveTo(ba, O, J, E); - if (h) { - if (Q.isStraight) { - Y = Q.deltaX | 0, I = Q.deltaY | 0, L = R + (Y >> 1), M = F + (I >> 1), R += Y, F += I; - } else { - var L = R + Q.controlDeltaX | 0, M = F + Q.controlDeltaY | 0, R = L + Q.anchorDeltaX | 0, F = M + Q.anchorDeltaY | 0 - } - v.morphCurveTo(ba, O, J, E, L, M, R, F); - } - } else { - J += I.deltaX | 0, E += I.deltaY | 0, h ? (R += Q.deltaX | 0, F += Q.deltaY | 0, v.morphLineTo(J, E, R, F)) : v.lineTo(J, E); - } - } - } - t(v, z, p, g); - x ? k.apply(x, g) : x = g; - k.apply(x, p); - H && x.push(H); - f = new e; - h && (f.morphCoordinates = new Int32Array(f.coordinates.length)); - for (U = 0;U < x.length;U++) { - x[U].serialize(f); - } - a.lineBoundsMorph && (x = a.lineBounds = d.FromUntyped(a.lineBounds), H = a.lineBoundsMorph, x.extendByPoint(H.xMin, H.yMin), x.extendByPoint(H.xMax, H.yMax), x = a.fillBoundsMorph) && (H = a.fillBounds = a.fillBounds ? d.FromUntyped(a.fillBounds) : null, H.extendByPoint(x.xMin, x.yMin), H.extendByPoint(x.xMax, x.yMax)); - return{type:a.isMorph ? "morphshape" : "shape", id:a.id, fillBounds:a.fillBounds, lineBounds:a.lineBounds, morphFillBounds:a.fillBoundsMorph || null, morphLineBounds:a.lineBoundsMorph || null, shape:f.toPlainObject(), transferables:f.buffers, require:c.length ? c : null}; - }; - var u = function() { - function a(b, c, d, e, h, g) { - this.commands = b; - this.data = c; - this.morphData = d; - this.prev = e; - this.next = h; - this.isReversed = g; - this.id = a._counter++; - } - a.FromDefaults = function(b) { - var c = new p, d = new p; - c.endian = d.endian = "auto"; - var e = null; - b && (e = new p, e.endian = "auto"); - return new a(c, d, e, null, null, !1); - }; - a.prototype.moveTo = function(a, b) { - this.commands.writeUnsignedByte(9); - this.data.writeInt(a); - this.data.writeInt(b); - }; - a.prototype.morphMoveTo = function(a, b, c, d) { - this.moveTo(a, b); - this.morphData.writeInt(c); - this.morphData.writeInt(d); - }; - a.prototype.lineTo = function(a, b) { - this.commands.writeUnsignedByte(10); - this.data.writeInt(a); - this.data.writeInt(b); - }; - a.prototype.morphLineTo = function(a, b, c, d) { - this.lineTo(a, b); - this.morphData.writeInt(c); - this.morphData.writeInt(d); - }; - a.prototype.curveTo = function(a, b, c, d) { - this.commands.writeUnsignedByte(11); - this.data.writeInt(a); - this.data.writeInt(b); - this.data.writeInt(c); - this.data.writeInt(d); - }; - a.prototype.morphCurveTo = function(a, b, c, d, e, h, g, f) { - this.curveTo(a, b, c, d); - this.morphData.writeInt(e); - this.morphData.writeInt(h); - this.morphData.writeInt(g); - this.morphData.writeInt(f); - }; - a.prototype.toReversed = function() { - n(!this.isReversed); - return new a(this.commands, this.data, this.morphData, null, null, !0); - }; - a.prototype.clone = function() { - return new a(this.commands, this.data, this.morphData, null, null, this.isReversed); - }; - a.prototype.storeStartAndEnd = function() { - var a = this.data.ints, b = a[0] + "," + a[1], c = (this.data.length >> 2) - 2, a = a[c] + "," + a[c + 1]; - this.isReversed ? (this.startPoint = a, this.endPoint = b) : (this.startPoint = b, this.endPoint = a); - }; - a.prototype.connectsTo = function(a) { - n(a !== this); - n(this.endPoint); - n(a.startPoint); - return this.endPoint === a.startPoint; - }; - a.prototype.startConnectsTo = function(a) { - n(a !== this); - return this.startPoint === a.startPoint; - }; - a.prototype.flipDirection = function() { - var a = "", a = this.startPoint; - this.startPoint = this.endPoint; - this.endPoint = a; - this.isReversed = !this.isReversed; - }; - a.prototype.serialize = function(a, b) { - if (this.isReversed) { - this._serializeReversed(a, b); - } else { - var c = this.commands.bytes, d = this.data.length >> 2, e = this.morphData ? this.morphData.ints : null, h = this.data.ints; - n(9 === c[0]); - var g = 0; - h[0] === b.x && h[1] === b.y && g++; - for (var f = this.commands.length, k = 2 * g;g < f;g++) { - k = this._writeCommand(c[g], k, h, e, a); - } - n(k === d); - b.x = h[d - 2]; - b.y = h[d - 1]; - } - }; - a.prototype._serializeReversed = function(a, b) { - var c = this.commands.length, d = (this.data.length >> 2) - 2, e = this.commands.bytes; - n(9 === e[0]); - var h = this.data.ints, g = this.morphData ? this.morphData.ints : null; - h[d] === b.x && h[d + 1] === b.y || this._writeCommand(9, d, h, g, a); - if (1 !== c) { - for (;1 < c--;) { - var d = d - 2, f = e[c]; - a.writeCommandAndCoordinates(f, h[d], h[d + 1]); - g && a.writeMorphCoordinates(g[d], g[d + 1]); - 11 === f && (d -= 2, a.writeCoordinates(h[d], h[d + 1]), g && a.writeMorphCoordinates(g[d], g[d + 1])); - } - n(0 === d); - } - b.x = h[0]; - b.y = h[1]; - }; - a.prototype._writeCommand = function(a, b, c, d, e) { - e.writeCommandAndCoordinates(a, c[b++], c[b++]); - d && e.writeMorphCoordinates(d[b - 2], d[b - 1]); - 11 === a && (e.writeCoordinates(c[b++], c[b++]), d && e.writeMorphCoordinates(d[b - 2], d[b - 1])); - return b; - }; - a._counter = 0; - return a; - }(), l = function() { - function b(a, c) { - this.fillStyle = a; - this.lineStyle = c; - this._head = null; - } - b.prototype.addSegment = function(a) { - n(a); - n(null === a.next); - n(null === a.prev); - var b = this._head; - b && (n(a !== b), b.next = a, a.prev = b); - this._head = a; - }; - b.prototype.removeSegment = function(a) { - a.prev && (a.prev.next = a.next); - a.next && (a.next.prev = a.prev); - }; - b.prototype.insertSegment = function(a, b) { - var c = b.prev; - a.prev = c; - a.next = b; - c && (c.next = a); - b.prev = a; - }; - b.prototype.head = function() { - return this._head; - }; - b.prototype.serialize = function(b) { - var c = this.head(); - if (c) { - for (;c;) { - c.storeStartAndEnd(), c = c.prev; - } - for (var d = this.head(), e = d, h = c = null, g = d.prev;d;) { - for (;g;) { - g.startConnectsTo(d) && g.flipDirection(), g.connectsTo(d) ? (g.next !== d && (this.removeSegment(g), this.insertSegment(g, d)), d = g, g = d.prev) : (g.startConnectsTo(e) && g.flipDirection(), e.connectsTo(g) ? (this.removeSegment(g), e.next = g, g = g.prev, e.next.prev = e, e.next.next = null, e = e.next) : g = g.prev); - } - g = d.prev; - c ? (h.next = d, d.prev = h, h = e, h.next = null) : (c = d, h = e); - if (!g) { - break; - } - d = e = g; - g = d.prev; - } - if (this.fillStyle) { - switch(g = this.fillStyle, g.type) { - case 0: - b.beginFill(g.color); - break; - case 16: - ; - case 18: - ; - case 19: - d = 16 === g.type ? 16 : 18; - b.beginGradient(2, g.colors, g.ratios, d, g.transform, g.spreadMethod, g.interpolationMode, g.focalPoint | 0); - break; - case 65: - ; - case 64: - ; - case 67: - ; - case 66: - n(-1 < g.bitmapIndex); - b.beginBitmap(3, g.bitmapIndex, g.transform, g.repeat, g.smooth); - break; - default: - q("Invalid fill style type: " + g.type); - } - } else { - switch(g = this.lineStyle, n(g), g.type) { - case 0: - a(g, b); - break; - case 16: - ; - case 18: - ; - case 19: - d = 16 === g.type ? 16 : 18; - a(g, b); - b.beginGradient(6, g.colors, g.ratios, d, g.transform, g.spreadMethod, g.interpolationMode, g.focalPoint | 0); - break; - case 65: - ; - case 64: - ; - case 67: - ; - case 66: - n(-1 < g.bitmapIndex); - a(g, b); - b.beginBitmap(7, g.bitmapIndex, g.transform, g.repeat, g.smooth); - break; - default: - console.error("Line style type not yet supported: " + g.type); - } - } - d = {x:0, y:0}; - for (g = c;g;) { - g.serialize(b, d), g = g.next; - } - this.fillStyle ? b.endFill() : b.endLine(); - return b; - } - }; - return b; - }(); - })(u.Parser || (u.Parser = {})); - })(f.SWF || (f.SWF = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(f) { - (function(c) { - function f(a, b, c, d, e) { - var k = d >> 3, n = c * b * k, k = c * k, p = a.length + (a.length & 1), q = new ArrayBuffer(h.length + p), m = new Uint8Array(q); - m.set(h); - if (e) { - e = 0; - for (var r = h.length;e < a.length;e += 2, r += 2) { - m[r] = a[e + 1], m[r + 1] = a[e]; - } - } else { - m.set(a, h.length); - } - a = new DataView(q); - a.setUint32(4, p + 36, !0); - a.setUint16(22, c, !0); - a.setUint32(24, b, !0); - a.setUint32(28, n, !0); - a.setUint16(32, k, !0); - a.setUint16(34, d, !0); - a.setUint32(40, p, !0); - return{data:m, mimeType:"audio/wav"}; - } - function m(a, b, c) { - function d(b) { - for (;f < b;) { - h = h << 8 | a[e++], f += 8; - } - f -= b; - return h >>> f & (1 << b) - 1; - } - for (var e = 0, h = 0, f = 0, k = 0, p = d(2), m = n[p];k < b.length;) { - var r = b[k++] = d(16) << 16 >> 16, t, v = d(6), u; - 1 < c && (t = b[k++] = d(16) << 16 >> 16, u = d(6)); - for (var H = 1 << p + 1, T = 0;4095 > T;T++) { - for (var J = d(p + 2), E = q[v], R = 0, F = H >> 1;F;F >>= 1, E >>= 1) { - J & F && (R += E); - } - r += (J & H ? -1 : 1) * (R + E); - b[k++] = r = -32768 > r ? -32768 : 32767 < r ? 32767 : r; - v += m[J & ~H]; - v = 0 > v ? 0 : 88 < v ? 88 : v; - if (1 < c) { - J = d(p + 2); - E = q[u]; - R = 0; - for (F = H >> 1;F;F >>= 1, E >>= 1) { - J & F && (R += E); - } - t += (J & H ? -1 : 1) * (R + E); - b[k++] = t = -32768 > t ? -32768 : 32767 < t ? 32767 : t; - u += m[J & ~H]; - u = 0 > u ? 0 : 88 < u ? 88 : u; - } - } - } - } - function r(a) { - for (var b = new Float32Array(a.length), c = 0;c < b.length;c++) { - b[c] = (a[c] - 128) / 128; - } - this.currentSample += b.length / this.channels; - return{streamId:this.streamId, samplesCount:b.length / this.channels, pcm:b}; - } - function a(a) { - for (var b = new Float32Array(a.length / 2), c = 0, d = 0;c < b.length;c++, d += 2) { - b[c] = (a[d] << 24 | a[d + 1] << 16) / 2147483648; - } - this.currentSample += b.length / this.channels; - return{streamId:this.streamId, samplesCount:b.length / this.channels, pcm:b}; - } - function d(a) { - for (var b = new Float32Array(a.length / 2), c = 0, d = 0;c < b.length;c++, d += 2) { - b[c] = (a[d + 1] << 24 | a[d] << 16) / 2147483648; - } - this.currentSample += b.length / this.channels; - return{streamId:this.streamId, samplesCount:b.length / this.channels, pcm:b}; - } - function p(a) { - var b = a[1] << 8 | a[0], c = a[3] << 8 | a[2]; - this.currentSample += b; - return{streamId:this.streamId, samplesCount:b, data:new Uint8Array(a.subarray(4)), seek:c}; - } - var e = [5512, 11250, 22500, 44100], h = new Uint8Array([82, 73, 70, 70, 0, 0, 0, 0, 87, 65, 86, 69, 102, 109, 116, 32, 16, 0, 0, 0, 1, 0, 2, 0, 68, 172, 0, 0, 16, 177, 2, 0, 4, 0, 16, 0, 100, 97, 116, 97, 0, 0, 0, 0]); - c.defineSound = function(a, b) { - var c = 1 == a.soundType ? 2 : 1, d = a.samplesCount, h = e[a.soundRate], k = a.soundData, n; - switch(a.soundFormat) { - case 0: - n = new Float32Array(d * c); - if (1 == a.soundSize) { - for (var p = d = 0;d < n.length;d++, p += 2) { - n[d] = (k[p] << 24 | k[p + 1] << 16) / 2147483648; - } - k = f(k, h, c, 16, !0); - } else { - for (d = 0;d < n.length;d++) { - n[d] = (k[d] - 128) / 128; - } - k = f(k, h, c, 8, !1); - } - break; - case 3: - n = new Float32Array(d * c); - if (1 == a.soundSize) { - for (p = d = 0;d < n.length;d++, p += 2) { - n[d] = (k[p + 1] << 24 | k[p] << 16) / 2147483648; - } - k = f(k, h, c, 16, !1); - } else { - for (d = 0;d < n.length;d++) { - n[d] = (k[d] - 128) / 128; - } - k = f(k, h, c, 8, !1); - } - break; - case 2: - k = {data:new Uint8Array(k.subarray(2)), mimeType:"audio/mpeg"}; - break; - case 1: - p = new Int16Array(d * c); - m(k, p, c); - n = new Float32Array(d * c); - for (d = 0;d < n.length;d++) { - n[d] = p[d] / 32768; - } - k = f(new Uint8Array(p.buffer), h, c, 16, !(new Uint8Array((new Uint16Array([1])).buffer))[0]); - break; - default: - throw Error("Unsupported audio format: " + a.soundFormat);; - } - c = {type:"sound", id:a.id, sampleRate:h, channels:c, pcm:n, packaged:void 0}; - k && (c.packaged = k); - return c; - }; - var n = [[-1, 2], [-1, -1, 2, 4], [-1, -1, -1, -1, 2, 4, 6, 8], [-1, -1, -1, -1, -1, -1, -1, -1, 1, 2, 4, 6, 8, 10, 13, 16]], q = [7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, - 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767], k = 0, b = function() { - function a(b, c, d) { - this.streamId = k++; - this.samplesCount = b; - this.sampleRate = c; - this.channels = d; - this.format = null; - this.currentSample = 0; - } - Object.defineProperty(a.prototype, "info", {get:function() { - return{samplesCount:this.samplesCount, sampleRate:this.sampleRate, channels:this.channels, format:this.format, streamId:this.streamId}; - }, enumerable:!0, configurable:!0}); - return a; - }(); - c.SwfSoundStream = b; - c.createSoundStream = function(c) { - var h = new b(c.samplesCount, e[c.streamRate], 1 == c.streamType ? 2 : 1); - switch(c.streamCompression) { - case 0: - h.format = "wave"; - h.decode = 1 == c.soundSize ? a : r; - break; - case 3: - h.format = "wave"; - h.decode = 1 == c.soundSize ? d : r; - break; - case 2: - h.format = "mp3"; - h.decode = p; - break; - default: - throw Error("Unsupported audio format: " + c.soundFormat);; - } - return h; - }; - })(f.Parser || (f.Parser = {})); - })(f.SWF || (f.SWF = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - (function(c) { - c.defineText = function(c, m) { - var r = [], a = !1, d = !1; - if (c.hasFont) { - var p = m[c.fontId]; - p ? (r.push(p.id), a = p.bold, d = p.italic) : f.Debug.warning("Font is not defined."); - } - a = {type:"text", id:c.id, fillBounds:c.bbox, variableName:c.variableName, tag:c, bold:a, italic:d, require:void 0}; - r.length && (a.require = r); - return a; - }; - })(u.Parser || (u.Parser = {})); - })(f.SWF || (f.SWF = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(f) { - function c(a, c) { - for (var d = 0, f = [], q, k, b = 16;0 < b && !a[b - 1];) { - b--; - } - f.push({children:[], index:0}); - var g = f[0], m; - for (q = 0;q < b;q++) { - for (k = 0;k < a[q];k++) { - g = f.pop(); - for (g.children[g.index] = c[d];0 < g.index;) { - g = f.pop(); - } - g.index++; - for (f.push(g);f.length <= q;) { - f.push(m = {children:[], index:0}), g.children[g.index] = m.children, g = m; - } - d++; - } - q + 1 < b && (f.push(m = {children:[], index:0}), g.children[g.index] = m.children, g = m); - } - return f[0].children; - } - function t(c, d, h, f, m, k, b, g, r) { - function l() { - if (0 < H) { - return H--, x >> H & 1; - } - x = c[d++]; - if (255 == x) { - var a = c[d++]; - if (a) { - throw "unexpected marker: " + (x << 8 | a).toString(16); - } - } - H = 7; - return x >>> 7; - } - function s(a) { - for (var b;null !== (b = l());) { - a = a[b]; - if ("number" === typeof a) { - return a; - } - if ("object" !== typeof a) { - throw "invalid huffman sequence"; - } - } - return null; - } - function t(a) { - for (var b = 0;0 < a;) { - var c = l(); - if (null === c) { - return; - } - b = b << 1 | c; - a--; - } - return b; - } - function u(a) { - if (1 === a) { - return 1 === l() ? 1 : -1; - } - var b = t(a); - return b >= 1 << a - 1 ? b : b + (-1 << a) + 1; - } - function y(b, c) { - var d = s(b.huffmanTableDC), d = 0 === d ? 0 : u(d); - b.blockData[c] = b.pred += d; - for (d = 1;64 > d;) { - var e = s(b.huffmanTableAC), h = e & 15, e = e >> 4; - if (0 === h) { - if (15 > e) { - break; - } - d += 16; - } else { - d += e, b.blockData[c + a[d]] = u(h), d++; - } - } - } - function w(a, b) { - var c = s(a.huffmanTableDC), c = 0 === c ? 0 : u(c) << r; - a.blockData[b] = a.pred += c; - } - function G(a, b) { - a.blockData[b] |= l() << r; - } - function D(c, d) { - if (0 < T) { - T--; - } else { - for (var e = k;e <= b;) { - var h = s(c.huffmanTableAC), g = h & 15, h = h >> 4; - if (0 === g) { - if (15 > h) { - T = t(h) + (1 << h) - 1; - break; - } - e += 16; - } else { - e += h, c.blockData[d + a[e]] = u(g) * (1 << r), e++; - } - } - } - } - function C(c, d) { - for (var e = k, h = 0, g;e <= b;) { - g = a[e]; - switch(J) { - case 0: - h = s(c.huffmanTableAC); - g = h & 15; - h >>= 4; - if (0 === g) { - 15 > h ? (T = t(h) + (1 << h), J = 4) : (h = 16, J = 1); - } else { - if (1 !== g) { - throw "invalid ACn encoding"; - } - E = u(g); - J = h ? 2 : 3; - } - continue; - case 1: - ; - case 2: - c.blockData[d + g] ? c.blockData[d + g] += l() << r : (h--, 0 === h && (J = 2 == J ? 3 : 0)); - break; - case 3: - c.blockData[d + g] ? c.blockData[d + g] += l() << r : (c.blockData[d + g] = E << r, J = 0); - break; - case 4: - c.blockData[d + g] && (c.blockData[d + g] += l() << r); - } - e++; - } - 4 === J && (T--, 0 === T && (J = 0)); - } - var z = h.mcusPerLine, v = d, x = 0, H = 0, T = 0, J = 0, E, R = f.length, F, S, U, X, I; - g = h.progressive ? 0 === k ? 0 === g ? w : G : 0 === g ? D : C : y; - var Q = 0; - h = 1 == R ? f[0].blocksPerLine * f[0].blocksPerColumn : z * h.mcusPerColumn; - m || (m = h); - for (var ba, O;Q < h;) { - for (S = 0;S < R;S++) { - f[S].pred = 0; - } - T = 0; - if (1 == R) { - for (F = f[0], I = 0;I < m;I++) { - g(F, 64 * ((F.blocksPerLine + 1) * (Q / F.blocksPerLine | 0) + Q % F.blocksPerLine)), Q++; - } - } else { - for (I = 0;I < m;I++) { - for (S = 0;S < R;S++) { - for (F = f[S], ba = F.h, O = F.v, U = 0;U < O;U++) { - for (X = 0;X < ba;X++) { - g(F, 64 * ((F.blocksPerLine + 1) * ((Q / z | 0) * F.v + U) + (Q % z * F.h + X))); - } - } - } - Q++; - } - } - H = 0; - F = c[d] << 8 | c[d + 1]; - if (65280 >= F) { - throw "marker was not found"; - } - if (65488 <= F && 65495 >= F) { - d += 2; - } else { - break; - } - } - return d - v; - } - function m(a, c) { - for (var d = c.blocksPerLine, f = c.blocksPerColumn, m = new Int32Array(64), k = 0;k < f;k++) { - for (var b = 0;b < d;b++) { - for (var g = c, r = 64 * ((c.blocksPerLine + 1) * k + b), l = m, s = g.quantizationTable, t = void 0, u = void 0, y = void 0, w = void 0, G = void 0, D = void 0, C = void 0, z = void 0, v = void 0, x = void 0, x = 0;64 > x;x++) { - l[x] = g.blockData[r + x] * s[x]; - } - for (x = 0;8 > x;++x) { - s = 8 * x, 0 === l[1 + s] && 0 === l[2 + s] && 0 === l[3 + s] && 0 === l[4 + s] && 0 === l[5 + s] && 0 === l[6 + s] && 0 === l[7 + s] ? (v = 5793 * l[0 + s] + 512 >> 10, l[0 + s] = v, l[1 + s] = v, l[2 + s] = v, l[3 + s] = v, l[4 + s] = v, l[5 + s] = v, l[6 + s] = v, l[7 + s] = v) : (t = 5793 * l[0 + s] + 128 >> 8, u = 5793 * l[4 + s] + 128 >> 8, y = l[2 + s], w = l[6 + s], G = 2896 * (l[1 + s] - l[7 + s]) + 128 >> 8, z = 2896 * (l[1 + s] + l[7 + s]) + 128 >> 8, D = l[3 + s] << 4, C = - l[5 + s] << 4, v = t - u + 1 >> 1, t = t + u + 1 >> 1, u = v, v = 3784 * y + 1567 * w + 128 >> 8, y = 1567 * y - 3784 * w + 128 >> 8, w = v, v = G - C + 1 >> 1, G = G + C + 1 >> 1, C = v, v = z + D + 1 >> 1, D = z - D + 1 >> 1, z = v, v = t - w + 1 >> 1, t = t + w + 1 >> 1, w = v, v = u - y + 1 >> 1, u = u + y + 1 >> 1, y = v, v = 2276 * G + 3406 * z + 2048 >> 12, G = 3406 * G - 2276 * z + 2048 >> 12, z = v, v = 799 * D + 4017 * C + 2048 >> 12, D = 4017 * D - 799 * C + 2048 >> 12, C = - v, l[0 + s] = t + z, l[7 + s] = t - z, l[1 + s] = u + C, l[6 + s] = u - C, l[2 + s] = y + D, l[5 + s] = y - D, l[3 + s] = w + G, l[4 + s] = w - G); - } - for (x = 0;8 > x;++x) { - s = x, 0 === l[8 + s] && 0 === l[16 + s] && 0 === l[24 + s] && 0 === l[32 + s] && 0 === l[40 + s] && 0 === l[48 + s] && 0 === l[56 + s] ? (v = 5793 * l[x + 0] + 8192 >> 14, l[0 + s] = v, l[8 + s] = v, l[16 + s] = v, l[24 + s] = v, l[32 + s] = v, l[40 + s] = v, l[48 + s] = v, l[56 + s] = v) : (t = 5793 * l[0 + s] + 2048 >> 12, u = 5793 * l[32 + s] + 2048 >> 12, y = l[16 + s], w = l[48 + s], G = 2896 * (l[8 + s] - l[56 + s]) + 2048 >> 12, z = 2896 * (l[8 + s] + l[56 + s]) + 2048 >> 12, - D = l[24 + s], C = l[40 + s], v = t - u + 1 >> 1, t = t + u + 1 >> 1, u = v, v = 3784 * y + 1567 * w + 2048 >> 12, y = 1567 * y - 3784 * w + 2048 >> 12, w = v, v = G - C + 1 >> 1, G = G + C + 1 >> 1, C = v, v = z + D + 1 >> 1, D = z - D + 1 >> 1, z = v, v = t - w + 1 >> 1, t = t + w + 1 >> 1, w = v, v = u - y + 1 >> 1, u = u + y + 1 >> 1, y = v, v = 2276 * G + 3406 * z + 2048 >> 12, G = 3406 * G - 2276 * z + 2048 >> 12, z = v, v = 799 * D + 4017 * C + 2048 >> 12, D = 4017 * D - 799 * - C + 2048 >> 12, C = v, l[0 + s] = t + z, l[56 + s] = t - z, l[8 + s] = u + C, l[48 + s] = u - C, l[16 + s] = y + D, l[40 + s] = y - D, l[24 + s] = w + G, l[32 + s] = w - G); - } - for (x = 0;64 > x;++x) { - t = r + x, u = l[x], u = -2056 >= u ? 0 : 2024 <= u ? 255 : u + 2056 >> 4, g.blockData[t] = u; - } - } - } - return c.blockData; - } - function r(a) { - return 0 >= a ? 0 : 255 <= a ? 255 : a; - } - var a = new Int32Array([0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63]), d = function() { - function d() { - } - d.prototype.parse = function(d) { - function h() { - var a = d[k] << 8 | d[k + 1]; - k += 2; - return a; - } - function f() { - var a = h(), a = d.subarray(k, k + a - 2); - k += a.length; - return a; - } - function p(a) { - for (var b = Math.ceil(a.samplesPerLine / 8 / a.maxH), c = Math.ceil(a.scanLines / 8 / a.maxV), d = 0;d < a.components.length;d++) { - x = a.components[d]; - var e = Math.ceil(Math.ceil(a.samplesPerLine / 8) * x.h / a.maxH), h = Math.ceil(Math.ceil(a.scanLines / 8) * x.v / a.maxV); - x.blockData = new Int16Array(64 * c * x.v * (b * x.h + 1)); - x.blocksPerLine = e; - x.blocksPerColumn = h; - } - a.mcusPerLine = b; - a.mcusPerColumn = c; - } - var k = 0, b = null, g = null, r, l, s = [], u = [], B = [], y = h(); - if (65496 != y) { - throw "SOI not found"; - } - for (y = h();65497 != y;) { - var w, G; - switch(y) { - case 65504: - ; - case 65505: - ; - case 65506: - ; - case 65507: - ; - case 65508: - ; - case 65509: - ; - case 65510: - ; - case 65511: - ; - case 65512: - ; - case 65513: - ; - case 65514: - ; - case 65515: - ; - case 65516: - ; - case 65517: - ; - case 65518: - ; - case 65519: - ; - case 65534: - w = f(); - 65504 === y && 74 === w[0] && 70 === w[1] && 73 === w[2] && 70 === w[3] && 0 === w[4] && (b = {version:{major:w[5], minor:w[6]}, densityUnits:w[7], xDensity:w[8] << 8 | w[9], yDensity:w[10] << 8 | w[11], thumbWidth:w[12], thumbHeight:w[13], thumbData:w.subarray(14, 14 + 3 * w[12] * w[13])}); - 65518 === y && 65 === w[0] && 100 === w[1] && 111 === w[2] && 98 === w[3] && 101 === w[4] && 0 === w[5] && (g = {version:w[6], flags0:w[7] << 8 | w[8], flags1:w[9] << 8 | w[10], transformCode:w[11]}); - break; - case 65499: - for (var y = h() + k - 2, D;k < y;) { - var C = d[k++], z = new Int32Array(64); - if (0 === C >> 4) { - for (w = 0;64 > w;w++) { - D = a[w], z[D] = d[k++]; - } - } else { - if (1 === C >> 4) { - for (w = 0;64 > w;w++) { - D = a[w], z[D] = h(); - } - } else { - throw "DQT: invalid table spec"; - } - } - s[C & 15] = z; - } - break; - case 65472: - ; - case 65473: - ; - case 65474: - if (r) { - throw "Only single frame JPEGs supported"; - } - h(); - r = {}; - r.extended = 65473 === y; - r.progressive = 65474 === y; - r.precision = d[k++]; - r.scanLines = h(); - r.samplesPerLine = h(); - r.components = []; - r.componentIds = {}; - w = d[k++]; - for (y = z = C = 0;y < w;y++) { - D = d[k]; - G = d[k + 1] >> 4; - var v = d[k + 1] & 15; - C < G && (C = G); - z < v && (z = v); - G = r.components.push({h:G, v:v, quantizationTable:s[d[k + 2]]}); - r.componentIds[D] = G - 1; - k += 3; - } - r.maxH = C; - r.maxV = z; - p(r); - break; - case 65476: - D = h(); - for (y = 2;y < D;) { - C = d[k++]; - z = new Uint8Array(16); - for (w = G = 0;16 > w;w++, k++) { - G += z[w] = d[k]; - } - v = new Uint8Array(G); - for (w = 0;w < G;w++, k++) { - v[w] = d[k]; - } - y += 17 + G; - (0 === C >> 4 ? B : u)[C & 15] = c(z, v); - } - break; - case 65501: - h(); - l = h(); - break; - case 65498: - h(); - D = d[k++]; - w = []; - for (var x, y = 0;y < D;y++) { - C = r.componentIds[d[k++]], x = r.components[C], C = d[k++], x.huffmanTableDC = B[C >> 4], x.huffmanTableAC = u[C & 15], w.push(x); - } - y = d[k++]; - D = d[k++]; - C = d[k++]; - y = t(d, k, r, w, l, y, D, C >> 4, C & 15); - k += y; - break; - default: - if (255 == d[k - 3] && 192 <= d[k - 2] && 254 >= d[k - 2]) { - k -= 3; - break; - } - throw "unknown JPEG marker " + y.toString(16);; - } - y = h(); - } - this.width = r.samplesPerLine; - this.height = r.scanLines; - this.jfif = b; - this.adobe = g; - this.components = []; - for (y = 0;y < r.components.length;y++) { - x = r.components[y], this.components.push({output:m(r, x), scaleX:x.h / r.maxH, scaleY:x.v / r.maxV, blocksPerLine:x.blocksPerLine, blocksPerColumn:x.blocksPerColumn}); - } - this.numComponents = this.components.length; - }; - d.prototype._getLinearizedBlockData = function(a, c) { - var d = this.width / a, f = this.height / c, k, b, g, p, l, m, r = 0, t, u = this.components.length, w = a * c * u, G = new Uint8Array(w), D = new Uint32Array(a); - for (m = 0;m < u;m++) { - k = this.components[m]; - b = k.scaleX * d; - g = k.scaleY * f; - r = m; - t = k.output; - p = k.blocksPerLine + 1 << 3; - for (l = 0;l < a;l++) { - k = 0 | l * b, D[l] = (k & 4294967288) << 3 | k & 7; - } - for (b = 0;b < c;b++) { - for (k = 0 | b * g, k = p * (k & 4294967288) | (k & 7) << 3, l = 0;l < a;l++) { - G[r] = t[k + D[l]], r += u; - } - } - } - if (f = this.decodeTransform) { - for (m = 0;m < w;) { - for (d = k = 0;k < u;k++, m++, d += 2) { - G[m] = (G[m] * f[d] >> 8) + f[d + 1]; - } - } - } - return G; - }; - d.prototype._isColorConversionNeeded = function() { - return this.adobe && this.adobe.transformCode ? !0 : 3 == this.numComponents ? !0 : !1; - }; - d.prototype._convertYccToRgb = function(a) { - for (var c, d, f, k = 0, b = a.length;k < b;k += 3) { - c = a[k], d = a[k + 1], f = a[k + 2], a[k] = r(c - 179.456 + 1.402 * f), a[k + 1] = r(c + 135.459 - .344 * d - .714 * f), a[k + 2] = r(c - 226.816 + 1.772 * d); - } - return a; - }; - d.prototype._convertYcckToRgb = function(a) { - for (var c, d, f, k, b, g, p, l, m, t, u, y, w, G, D = 0, C = 0, z = a.length;C < z;C += 4) { - c = a[C]; - d = a[C + 1]; - f = a[C + 2]; - k = a[C + 3]; - b = d * d; - g = d * f; - p = d * c; - l = d * k; - m = f * f; - t = f * k; - u = f * c; - y = c * c; - w = c * k; - G = k * k; - var v = -122.67195406894 - 6.60635669420364E-5 * b + 4.37130475926232E-4 * g - 5.4080610064599E-5 * p + 4.8449797120281E-4 * l - .154362151871126 * d - 9.57964378445773E-4 * m + 8.17076911346625E-4 * u - .00477271405408747 * t + 1.53380253221734 * f + 9.61250184130688E-4 * y - .00266257332283933 * w + .48357088451265 * c - 3.36197177618394E-4 * G + .484791561490776 * k, x = 107.268039397724 + 2.19927104525741E-5 * b - 6.40992018297945E-4 * g + 6.59397001245577E-4 * p + 4.26105652938837E-4 * - l - .176491792462875 * d - 7.78269941513683E-4 * m + .00130872261408275 * u + 7.70482631801132E-4 * t - .151051492775562 * f + .00126935368114843 * y - .00265090189010898 * w + .25802910206845 * c - 3.18913117588328E-4 * G - .213742400323665 * k; - c = -20.810012546947 - 5.70115196973677E-4 * b - 2.63409051004589E-5 * g + .0020741088115012 * p - .00288260236853442 * l + .814272968359295 * d - 1.53496057440975E-5 * m - 1.32689043961446E-4 * u + 5.60833691242812E-4 * t - .195152027534049 * f + .00174418132927582 * y - .00255243321439347 * w + .116935020465145 * c - 3.43531996510555E-4 * G + .24165260232407 * k; - a[D++] = r(v); - a[D++] = r(x); - a[D++] = r(c); - } - return a; - }; - d.prototype._convertYcckToCmyk = function(a) { - for (var c, d, f, k = 0, b = a.length;k < b;k += 4) { - c = a[k], d = a[k + 1], f = a[k + 2], a[k] = r(434.456 - c - 1.402 * f), a[k + 1] = r(119.541 - c + .344 * d + .714 * f), a[k + 2] = r(481.816 - c - 1.772 * d); - } - return a; - }; - d.prototype._convertCmykToRgb = function(a) { - for (var c, d, f, k, b = 0, g = 1 / 255 / 255, p = 0, l = a.length;p < l;p += 4) { - c = a[p]; - d = a[p + 1]; - f = a[p + 2]; - k = a[p + 3]; - var m = c * (-4.387332384609988 * c + 54.48615194189176 * d + 18.82290502165302 * f + 212.25662451639585 * k - 72734.4411664936) + d * (1.7149763477362134 * d - 5.6096736904047315 * f - 17.873870861415444 * k - 1401.7366389350734) + f * (-2.5217340131683033 * f - 21.248923337353073 * k + 4465.541406466231) - k * (21.86122147463605 * k + 48317.86113160301), r = c * (8.841041422036149 * c + 60.118027045597366 * d + 6.871425592049007 * f + 31.159100130055922 * k - 20220.756542821975) + d * - (-15.310361306967817 * d + 17.575251261109482 * f + 131.35250912493976 * k - 48691.05921601825) + f * (4.444339102852739 * f + 9.8632861493405 * k - 6341.191035517494) - k * (20.737325471181034 * k + 47890.15695978492); - c = c * (.8842522430003296 * c + 8.078677503112928 * d + 30.89978309703729 * f - .23883238689178934 * k - 3616.812083916688) + d * (10.49593273432072 * d + 63.02378494754052 * f + 50.606957656360734 * k - 28620.90484698408) + f * (.03296041114873217 * f + 115.60384449646641 * k - 49363.43385999684) - k * (22.33816807309886 * k + 45932.16563550634); - a[b++] = 0 <= m ? 255 : -16581375 >= m ? 0 : 255 + m * g | 0; - a[b++] = 0 <= r ? 255 : -16581375 >= r ? 0 : 255 + r * g | 0; - a[b++] = 0 <= c ? 255 : -16581375 >= c ? 0 : 255 + c * g | 0; - } - return a; - }; - d.prototype.getData = function(a, c, d) { - if (4 < this.numComponents) { - throw "Unsupported color mode"; - } - a = this._getLinearizedBlockData(a, c); - return 3 === this.numComponents ? this._convertYccToRgb(a) : 4 === this.numComponents ? this._isColorConversionNeeded() ? d ? this._convertYcckToRgb(a) : this._convertYcckToCmyk(a) : this._convertCmykToRgb(a) : a; - }; - d.prototype.copyToImageData = function(a) { - var c = a.width, d = a.height, f = c * d * 4; - a = a.data; - var c = this.getData(c, d, !0), k = d = 0, b, g, p, l; - switch(this.components.length) { - case 1: - for (;k < f;) { - b = c[d++], a[k++] = b, a[k++] = b, a[k++] = b, a[k++] = 255; - } - break; - case 3: - for (;k < f;) { - p = c[d++], l = c[d++], b = c[d++], a[k++] = p, a[k++] = l, a[k++] = b, a[k++] = 255; - } - break; - case 4: - for (;k < f;) { - p = c[d++], l = c[d++], b = c[d++], g = c[d++], p = 255 - r(p * (1 - g / 255) + g), l = 255 - r(l * (1 - g / 255) + g), b = 255 - r(b * (1 - g / 255) + g), a[k++] = p, a[k++] = l, a[k++] = b, a[k++] = 255; - } - break; - default: - throw "Unsupported color mode";; - } - }; - return d; - }(); - f.JpegImage = d; - })(f.JPEG || (f.JPEG = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(f) { - function c() { - this.bitBuffer = this.bitLength = 0; - } - function t(a) { - if (this.pos + a > this.end) { - throw f.StreamNoDataError; - } - } - function m() { - return this.end - this.pos; - } - function r(a, c) { - var f = new d(this.bytes); - f.pos = a; - f.end = c; - return f; - } - function a(a) { - var c = this.bytes, d = this.end + a.length; - if (d > c.length) { - throw "stream buffer overfow"; - } - c.set(a, this.end); - this.end = d; - } - f.StreamNoDataError = {}; - var d = function() { - return function(d, e, f, n) { - void 0 === e && (e = 0); - d.buffer instanceof ArrayBuffer && (e += d.byteOffset, d = d.buffer); - void 0 === f && (f = d.byteLength - e); - void 0 === n && (n = f); - var q = new Uint8Array(d, e, n); - d = new DataView(d, e, n); - d.bytes = q; - d.pos = 0; - d.end = f; - d.bitBuffer = 0; - d.bitLength = 0; - d.align = c; - d.ensure = t; - d.remaining = m; - d.substream = r; - d.push = a; - return d; - }; - }(); - f.Stream = d; - })(f.SWF || (f.SWF = {})); -})(Shumway || (Shumway = {})); -(function(f) { - (function(u) { - function c(a, p, e) { - var h; - switch(a.code) { - case 6: - ; - case 21: - ; - case 35: - ; - case 90: - ; - case 8: - h = f.SWF.Parser.defineImage(a, p); - break; - case 20: - ; - case 36: - h = f.SWF.Parser.defineBitmap(a); - break; - case 7: - ; - case 34: - h = f.SWF.Parser.defineButton(a, p); - break; - case 37: - h = f.SWF.Parser.defineText(a, p); - break; - case 10: - ; - case 48: - ; - case 75: - ; - case 91: - h = f.SWF.Parser.defineFont(a, p); - break; - case 46: - ; - case 84: - ; - case 2: - ; - case 22: - ; - case 32: - ; - case 83: - h = f.SWF.Parser.defineShape(a, p); - break; - case 14: - h = f.SWF.Parser.defineSound(a, p); - break; - case 87: - h = {type:"binary", id:a.id, data:a.data}; - break; - case 39: - for (var n = [], q = {type:"frame"}, k = [], b = a.tags, g = null, t = 0, l = null, s = 0, u = b.length;s < u;s++) { - var B = b[s]; - if ("id" in B) { - h = c(B, p, e), e(h, h.transferables); - } else { - switch(B.code) { - case 12: - g || (g = []); - g.push(t); - g.push(B.actionsData); - break; - case 15: - n.push(B); - break; - case 18: - try { - l = r(B), q.soundStream = l.info; - } catch (y) { - } - break; - case 19: - l && (q.soundStreamBlock = l.decode(B.data)); - break; - case 43: - q.labelName = B.name; - break; - case 4: - ; - case 26: - ; - case 70: - n.push(B); - break; - case 5: - ; - case 28: - n.push(B); - break; - case 1: - t += B.repeat; - q.repeat = B.repeat; - q.commands = n; - k.push(q); - n = []; - q = {type:"frame"}; - break; - default: - f.Debug.warning("Dropped tag during parsing. Code: " + B.code + " (" + m[B.code] + ")"); - } - } - } - 0 === k.length && (q.repeat = 1, q.commands = n, k.push(q)); - h = {type:"sprite", id:a.id, frameCount:a.frameCount, frames:k, frameScripts:g}; - break; - case 11: - ; - case 33: - h = f.SWF.Parser.defineLabel(a, p); - break; - default: - f.Debug.warning("Dropped tag during parsing. Code: " + B.code + " (" + m[B.code] + ")"); - } - if (!h) { - return{command:"error", message:"unknown symbol type: " + a.code}; - } - h.isSymbol = !0; - return p[a.id] = h; - } - function t(a) { - var p = [], e = {}, h = {type:"frame"}, n = 0, q = null, k = 0; - return{onstart:function(b) { - a({command:"init", result:b}); - }, onimgprogress:function(b) { - for (;k <= b;) { - a({command:"progress", result:{bytesLoaded:k, bytesTotal:b, open:!0}}), k += Math.min(b - k || 1024, 1024); - } - }, onprogress:function(b) { - if (65536 <= b.bytesLoaded - k) { - for (;k < b.bytesLoaded;) { - k && a({command:"progress", result:{bytesLoaded:k, bytesTotal:b.bytesTotal}}), k += 65536; - } - } - for (var g = b.tags, t = g.length;n < t;n++) { - var l = g[n]; - if ("id" in l) { - l = c(l, e, a), a(l, l.transferables); - } else { - switch(l.code) { - case 86: - h.sceneData = l; - break; - case 78: - a({isSymbol:!0, id:l.symbolId, updates:{scale9Grid:l.splitter}}); - break; - case 82: - ; - case 72: - a({type:"abc", flags:l.flags, name:l.name, data:l.data}); - break; - case 12: - var s = h.actionBlocks; - s ? s.push(l.actionsData) : h.actionBlocks = [l.actionsData]; - break; - case 59: - (h.initActionBlocks || (h.initActionBlocks = [])).push({spriteId:l.spriteId, actionsData:l.actionsData}); - break; - case 15: - p.push(l); - break; - case 18: - try { - q = r(l), h.soundStream = q.info; - } catch (u) { - } - break; - case 19: - q && (h.soundStreamBlock = q.decode(l.data)); - break; - case 56: - s = h.exports; - h.exports = s ? s.concat(l.exports) : l.exports.slice(0); - break; - case 76: - s = h.symbolClasses; - h.symbolClasses = s ? s.concat(l.exports) : l.exports.slice(0); - break; - case 43: - h.labelName = l.name; - break; - case 4: - ; - case 26: - ; - case 70: - p.push(l); - break; - case 5: - ; - case 28: - p.push(l); - break; - case 9: - h.bgcolor = l.color; - break; - case 1: - h.repeat = l.repeat; - h.commands = p; - h.complete = !!l.finalTag; - a(h); - p = []; - h = {type:"frame"}; - break; - default: - f.Debug.warning("Dropped tag during parsing. Code: " + l.code + " (" + m[l.code] + ")"); - } - } - } - b.bytesLoaded >= b.bytesTotal && a({command:"progress", result:{bytesLoaded:b.bytesLoaded, bytesTotal:b.bytesTotal}}); - }, oncomplete:function(b) { - a(b); - var c; - "number" === typeof b.swfVersion && (c = b.bbox, c = {topic:"parseInfo", parseTime:b.parseTime, bytesTotal:b.bytesTotal, swfVersion:b.swfVersion, frameRate:b.frameRate, width:(c.xMax - c.xMin) / 20, height:(c.yMax - c.yMin) / 20, isAvm2:!!b.fileAttributes.doAbc}); - a({command:"complete", stats:c}); - }, onexception:function(b) { - a({type:"exception", message:b.message, stack:b.stack}); - }}; - } - var m = f.SWF.Parser.SwfTag, r = f.SWF.Parser.createSoundStream, a = function() { - function a(c, d) { - this._subscription = null; - var f = this; - d ? (this._messenger = c, c.onmessage = function(a) { - f.listener(a.data); - }) : this._messenger = {postMessage:function(a) { - f.onmessage({data:a}); - }}; - } - a.prototype.terminate = function() { - this.listener = this._messenger = null; - }; - a.prototype.onmessage = function(a) { - this.listener(a.data); - }; - a.prototype.postMessage = function(a) { - this.listener && this.listener(a); - }; - a.prototype.listener = function(a) { - this._subscription ? this._subscription.callback(a.data, a.progress) : "pipe:" === a ? (this._subscription = {subscribe:function(a) { - this.callback = a; - }}, this.parseLoadedData(this._messenger, this._subscription)) : this.parseLoadedData(this._messenger, a); - }; - a.prototype.parseLoadedData = function(a, c) { - function d(c, b) { - try { - a.postMessage(c, b); - } catch (e) { - if ("DataCloneError" != e) { - throw e; - } - a.postMessage(c); - } - } - if (c instanceof ArrayBuffer) { - f.SWF.Parser.parse(c, t(d)); - } else { - if ("subscribe" in c) { - var m = f.SWF.Parser.parseAsync(t(d)); - c.subscribe(function(a, b) { - a ? m.push(a, b) : m.close(); - }); - } else { - if ("undefined" !== typeof FileReaderSync) { - var q = (new FileReaderSync).readAsArrayBuffer(c); - f.SWF.Parser.parse(q, t(d)); - } else { - q = new FileReader, q.onload = function() { - f.SWF.Parser.parse(this.result, t(d)); - }, q.readAsArrayBuffer(c); - } - } - } - }; - return a; - }(); - u.ResourceLoader = a; - })(f.SWF || (f.SWF = {})); -})(Shumway || (Shumway = {})); -console.timeEnd("Load SWF Parser"); - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/shumway.player.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/shumway.player.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/shumway.player.js 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/shumway.player.js 2015-02-03 14:33:23.000000000 +0000 @@ -1,2218 +1,2290 @@ +/* + + Copyright 2014 Mozilla Foundation + + 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. +*/ console.time("Load Player Dependencies"); console.time("Load Shared Dependencies"); var jsGlobal = function() { - return this || (0,eval)("this"); -}(), inBrowser = "undefined" != typeof console; + return this || (0,eval)("this//# sourceURL=jsGlobal-getter"); +}(), inBrowser = "undefined" !== typeof window && "document" in window && "plugins" in window.document, inFirefox = "undefined" !== typeof navigator && 0 <= navigator.userAgent.indexOf("Firefox"); jsGlobal.performance || (jsGlobal.performance = {}); jsGlobal.performance.now || (jsGlobal.performance.now = "undefined" !== typeof dateNow ? dateNow : Date.now); -function log(b) { - for (var k = 0;k < arguments.length - 1;k++) { +var START_TIME = performance.now(), Shumway; +(function(c) { + function h(d) { + return(d | 0) === d; } - jsGlobal.print.apply(jsGlobal, arguments); -} -function warn(b) { - for (var k = 0;k < arguments.length - 1;k++) { + function a(d) { + return "object" === typeof d || "function" === typeof d; } - inBrowser ? console.warn.apply(console, arguments) : jsGlobal.print(Shumway.IndentingWriter.RED + b + Shumway.IndentingWriter.ENDC); -} -var Shumway; -(function(b) { - function k(a) { - return(a | 0) === a; + function s(d) { + return String(Number(d)) === d; } - function g(a) { - return "object" === typeof a || "function" === typeof a; - } - function f(a) { - return String(Number(a)) === a; - } - function t(a) { - var c = 0; - if ("number" === typeof a) { - return c = a | 0, a === c && 0 <= c ? !0 : a >>> 0 === a; + function v(d) { + var b = 0; + if ("number" === typeof d) { + return b = d | 0, d === b && 0 <= b ? !0 : d >>> 0 === d; } - if ("string" !== typeof a) { + if ("string" !== typeof d) { return!1; } - var e = a.length; - if (0 === e) { + var g = d.length; + if (0 === g) { return!1; } - if ("0" === a) { + if ("0" === d) { return!0; } - if (e > b.UINT32_CHAR_BUFFER_LENGTH) { + if (g > c.UINT32_CHAR_BUFFER_LENGTH) { return!1; } - var r = 0, c = a.charCodeAt(r++) - 48; - if (1 > c || 9 < c) { + var r = 0, b = d.charCodeAt(r++) - 48; + if (1 > b || 9 < b) { return!1; } - for (var h = 0, q = 0;r < e;) { - q = a.charCodeAt(r++) - 48; - if (0 > q || 9 < q) { + for (var w = 0, f = 0;r < g;) { + f = d.charCodeAt(r++) - 48; + if (0 > f || 9 < f) { return!1; } - h = c; - c = 10 * c + q; + w = b; + b = 10 * b + f; } - return h < b.UINT32_MAX_DIV_10 || h === b.UINT32_MAX_DIV_10 && q <= b.UINT32_MAX_MOD_10 ? !0 : !1; + return w < c.UINT32_MAX_DIV_10 || w === c.UINT32_MAX_DIV_10 && f <= c.UINT32_MAX_MOD_10 ? !0 : !1; } - (function(a) { - a[a._0 = 48] = "_0"; - a[a._1 = 49] = "_1"; - a[a._2 = 50] = "_2"; - a[a._3 = 51] = "_3"; - a[a._4 = 52] = "_4"; - a[a._5 = 53] = "_5"; - a[a._6 = 54] = "_6"; - a[a._7 = 55] = "_7"; - a[a._8 = 56] = "_8"; - a[a._9 = 57] = "_9"; - })(b.CharacterCodes || (b.CharacterCodes = {})); - b.UINT32_CHAR_BUFFER_LENGTH = 10; - b.UINT32_MAX = 4294967295; - b.UINT32_MAX_DIV_10 = 429496729; - b.UINT32_MAX_MOD_10 = 5; - b.isString = function(a) { - return "string" === typeof a; + (function(d) { + d[d._0 = 48] = "_0"; + d[d._1 = 49] = "_1"; + d[d._2 = 50] = "_2"; + d[d._3 = 51] = "_3"; + d[d._4 = 52] = "_4"; + d[d._5 = 53] = "_5"; + d[d._6 = 54] = "_6"; + d[d._7 = 55] = "_7"; + d[d._8 = 56] = "_8"; + d[d._9 = 57] = "_9"; + })(c.CharacterCodes || (c.CharacterCodes = {})); + c.UINT32_CHAR_BUFFER_LENGTH = 10; + c.UINT32_MAX = 4294967295; + c.UINT32_MAX_DIV_10 = 429496729; + c.UINT32_MAX_MOD_10 = 5; + c.isString = function(d) { + return "string" === typeof d; }; - b.isFunction = function(a) { - return "function" === typeof a; + c.isFunction = function(d) { + return "function" === typeof d; }; - b.isNumber = function(a) { - return "number" === typeof a; + c.isNumber = function(d) { + return "number" === typeof d; }; - b.isInteger = k; - b.isArray = function(a) { - return a instanceof Array; + c.isInteger = h; + c.isArray = function(d) { + return d instanceof Array; }; - b.isNumberOrString = function(a) { - return "number" === typeof a || "string" === typeof a; + c.isNumberOrString = function(d) { + return "number" === typeof d || "string" === typeof d; }; - b.isObject = g; - b.toNumber = function(a) { - return+a; + c.isObject = a; + c.toNumber = function(d) { + return+d; }; - b.isNumericString = f; - b.isNumeric = function(a) { - if ("number" === typeof a) { + c.isNumericString = s; + c.isNumeric = function(d) { + if ("number" === typeof d) { return!0; } - if ("string" === typeof a) { - var c = a.charCodeAt(0); - return 65 <= c && 90 >= c || 97 <= c && 122 >= c || 36 === c || 95 === c ? !1 : t(a) || f(a); + if ("string" === typeof d) { + var b = d.charCodeAt(0); + return 65 <= b && 90 >= b || 97 <= b && 122 >= b || 36 === b || 95 === b ? !1 : v(d) || s(d); } return!1; }; - b.isIndex = t; - b.isNullOrUndefined = function(a) { - return void 0 == a; + c.isIndex = v; + c.isNullOrUndefined = function(d) { + return void 0 == d; }; - (function(a) { - a.backtrace = function() { - try { - throw Error(); - } catch (a) { - return a.stack ? a.stack.split("\n").slice(2).join("\n") : ""; - } + var p; + (function(d) { + d.error = function(b) { + console.error(b); + throw Error(b); }; - a.error = function(c) { - inBrowser ? warn(c) : warn(c + "\n\nStack Trace:\n" + a.backtrace()); - throw Error(c); + d.assert = function(b, r) { + void 0 === r && (r = "assertion failed"); + "" === b && (b = !0); + if (!b) { + if ("undefined" !== typeof console && "assert" in console) { + throw console.assert(!1, r), Error(r); + } + d.error(r.toString()); + } }; - a.assert = function(c, r) { - "undefined" === typeof r && (r = "assertion failed"); - "" === c && (c = !0); - c || a.error(r.toString()); + d.assertUnreachable = function(b) { + var r = Error().stack.split("\n")[1]; + throw Error("Reached unreachable location " + r + b); }; - a.assertUnreachable = function(a) { - var l = Error().stack.split("\n")[1]; - throw Error("Reached unreachable location " + l + a); + d.assertNotImplemented = function(b, r) { + b || d.error("notImplemented: " + r); }; - a.assertNotImplemented = function(c, r) { - c || a.error("notImplemented: " + r); + d.warning = function(b, r, d) { + console.warn.apply(console, arguments); }; - a.warning = function(a) { - warn(a); + d.notUsed = function(b) { + d.assert(!1, "Not Used " + b); }; - a.notUsed = function(c) { - a.assert(!1, "Not Used " + c); + d.notImplemented = function(b) { + d.assert(!1, "Not Implemented " + b); }; - a.notImplemented = function(c) { - log("release: false"); - a.assert(!1, "Not Implemented " + c); + d.dummyConstructor = function(b) { + d.assert(!1, "Dummy Constructor: " + b); }; - a.abstractMethod = function(c) { - a.assert(!1, "Abstract Method " + c); + d.abstractMethod = function(b) { + d.assert(!1, "Abstract Method " + b); }; - var c = {}; - a.somewhatImplemented = function(e) { - c[e] || (c[e] = !0, a.warning("somewhatImplemented: " + e)); + var b = {}; + d.somewhatImplemented = function(g) { + b[g] || (b[g] = !0, d.warning("somewhatImplemented: " + g)); }; - a.unexpected = function(c) { - a.assert(!1, "Unexpected: " + c); + d.unexpected = function(b) { + d.assert(!1, "Unexpected: " + b); }; - a.untested = function(c) { - a.warning("Congratulations, you've found a code path for which we haven't found a test case. Please submit the test case: " + c); + d.unexpectedCase = function(b) { + d.assert(!1, "Unexpected Case: " + b); }; - })(b.Debug || (b.Debug = {})); - var s = b.Debug; - b.getTicks = function() { + })(p = c.Debug || (c.Debug = {})); + c.getTicks = function() { return performance.now(); }; - (function(a) { - function c(a, l) { - for (var r = 0, e = a.length;r < e;r++) { - if (a[r] === l) { + var u; + (function(d) { + function b(b, g) { + for (var r = 0, d = b.length;r < d;r++) { + if (b[r] === g) { return r; } } - a.push(l); - return a.length - 1; + b.push(g); + return b.length - 1; } - var e = b.Debug.assert; - a.popManyInto = function(a, l, c) { - e(a.length >= l); - for (var r = l - 1;0 <= r;r--) { - c[r] = a.pop(); - } - c.length = l; - }; - a.popMany = function(a, l) { - e(a.length >= l); - var c = a.length - l, r = a.slice(c, this.length); - a.splice(c, l); - return r; + var g = c.Debug.assert; + d.popManyInto = function(b, r, d) { + g(b.length >= r); + for (var f = r - 1;0 <= f;f--) { + d[f] = b.pop(); + } + d.length = r; + }; + d.popMany = function(b, r) { + g(b.length >= r); + var d = b.length - r, f = b.slice(d, this.length); + b.length = d; + return f; }; - a.popManyIntoVoid = function(a, l) { - e(a.length >= l); - a.length -= l; + d.popManyIntoVoid = function(b, r) { + g(b.length >= r); + b.length -= r; }; - a.pushMany = function(a, l) { - for (var c = 0;c < l.length;c++) { - a.push(l[c]); + d.pushMany = function(b, g) { + for (var r = 0;r < g.length;r++) { + b.push(g[r]); } }; - a.top = function(a) { - return a.length && a[a.length - 1]; + d.top = function(b) { + return b.length && b[b.length - 1]; }; - a.last = function(a) { - return a.length && a[a.length - 1]; + d.last = function(b) { + return b.length && b[b.length - 1]; }; - a.peek = function(a) { - e(0 < a.length); - return a[a.length - 1]; + d.peek = function(b) { + g(0 < b.length); + return b[b.length - 1]; }; - a.indexOf = function(a, l) { - for (var c = 0, r = a.length;c < r;c++) { - if (a[c] === l) { - return c; + d.indexOf = function(b, g) { + for (var r = 0, d = b.length;r < d;r++) { + if (b[r] === g) { + return r; } } return-1; }; - a.pushUnique = c; - a.unique = function(a) { - for (var l = [], r = 0;r < a.length;r++) { - c(l, a[r]); + d.equals = function(b, g) { + if (b.length !== g.length) { + return!1; } - return l; + for (var r = 0;r < b.length;r++) { + if (b[r] !== g[r]) { + return!1; + } + } + return!0; }; - a.copyFrom = function(h, c) { - h.length = 0; - a.pushMany(h, c); - }; - a.ensureTypedArrayCapacity = function(a, l) { - if (a.length < l) { - var c = a; - a = new a.constructor(b.IntegerUtilities.nearestPowerOfTwo(l)); - a.set(c, 0); + d.pushUnique = b; + d.unique = function(g) { + for (var r = [], d = 0;d < g.length;d++) { + b(r, g[d]); } - return a; + return r; + }; + d.copyFrom = function(b, g) { + b.length = 0; + d.pushMany(b, g); + }; + d.ensureTypedArrayCapacity = function(b, g) { + if (b.length < g) { + var r = b; + b = new b.constructor(c.IntegerUtilities.nearestPowerOfTwo(g)); + b.set(r, 0); + } + return b; }; var r = function() { - function a(h) { - "undefined" === typeof h && (h = 16); + function b(g) { + void 0 === g && (g = 16); this._f32 = this._i32 = this._u16 = this._u8 = null; this._offset = 0; - this.ensureCapacity(h); + this.ensureCapacity(g); } - a.prototype.reset = function() { + b.prototype.reset = function() { this._offset = 0; }; - Object.defineProperty(a.prototype, "offset", {get:function() { + Object.defineProperty(b.prototype, "offset", {get:function() { return this._offset; }, enumerable:!0, configurable:!0}); - a.prototype.getIndex = function(a) { - e(1 === a || 2 === a || 4 === a || 8 === a || 16 === a); - a = this._offset / a; - e((a | 0) === a); - return a; + b.prototype.getIndex = function(b) { + g(1 === b || 2 === b || 4 === b || 8 === b || 16 === b); + b = this._offset / b; + g((b | 0) === b); + return b; }; - a.prototype.ensureAdditionalCapacity = function(a) { - this.ensureCapacity(this._offset + a); + b.prototype.ensureAdditionalCapacity = function(b) { + this.ensureCapacity(this._offset + b); }; - a.prototype.ensureCapacity = function(a) { + b.prototype.ensureCapacity = function(b) { if (!this._u8) { - this._u8 = new Uint8Array(a); + this._u8 = new Uint8Array(b); } else { - if (this._u8.length > a) { + if (this._u8.length > b) { return; } } - var h = 2 * this._u8.length; - h < a && (h = a); - a = new Uint8Array(h); - a.set(this._u8, 0); - this._u8 = a; - this._u16 = new Uint16Array(a.buffer); - this._i32 = new Int32Array(a.buffer); - this._f32 = new Float32Array(a.buffer); + var g = 2 * this._u8.length; + g < b && (g = b); + b = new Uint8Array(g); + b.set(this._u8, 0); + this._u8 = b; + this._u16 = new Uint16Array(b.buffer); + this._i32 = new Int32Array(b.buffer); + this._f32 = new Float32Array(b.buffer); }; - a.prototype.writeInt = function(a) { - e(0 === (this._offset & 3)); + b.prototype.writeInt = function(b) { + g(0 === (this._offset & 3)); this.ensureCapacity(this._offset + 4); - this.writeIntUnsafe(a); + this.writeIntUnsafe(b); }; - a.prototype.writeIntAt = function(a, h) { - e(0 <= h && h <= this._offset); - e(0 === (h & 3)); - this.ensureCapacity(h + 4); - this._i32[h >> 2] = a; + b.prototype.writeIntAt = function(b, r) { + g(0 <= r && r <= this._offset); + g(0 === (r & 3)); + this.ensureCapacity(r + 4); + this._i32[r >> 2] = b; }; - a.prototype.writeIntUnsafe = function(a) { - this._i32[this._offset >> 2] = a; + b.prototype.writeIntUnsafe = function(b) { + this._i32[this._offset >> 2] = b; this._offset += 4; }; - a.prototype.writeFloat = function(a) { - e(0 === (this._offset & 3)); + b.prototype.writeFloat = function(b) { + g(0 === (this._offset & 3)); this.ensureCapacity(this._offset + 4); - this.writeFloatUnsafe(a); + this.writeFloatUnsafe(b); }; - a.prototype.writeFloatUnsafe = function(a) { - this._f32[this._offset >> 2] = a; + b.prototype.writeFloatUnsafe = function(b) { + this._f32[this._offset >> 2] = b; this._offset += 4; }; - a.prototype.write4Floats = function(a, h, l, c) { - e(0 === (this._offset & 3)); + b.prototype.write4Floats = function(b, r, d, w) { + g(0 === (this._offset & 3)); this.ensureCapacity(this._offset + 16); - this.write4FloatsUnsafe(a, h, l, c); + this.write4FloatsUnsafe(b, r, d, w); }; - a.prototype.write4FloatsUnsafe = function(a, h, l, c) { - var r = this._offset >> 2; - this._f32[r + 0] = a; - this._f32[r + 1] = h; - this._f32[r + 2] = l; - this._f32[r + 3] = c; + b.prototype.write4FloatsUnsafe = function(b, g, r, d) { + var w = this._offset >> 2; + this._f32[w + 0] = b; + this._f32[w + 1] = g; + this._f32[w + 2] = r; + this._f32[w + 3] = d; this._offset += 16; }; - a.prototype.write6Floats = function(a, h, l, c, r, u) { - e(0 === (this._offset & 3)); + b.prototype.write6Floats = function(b, r, d, w, f, k) { + g(0 === (this._offset & 3)); this.ensureCapacity(this._offset + 24); - this.write6FloatsUnsafe(a, h, l, c, r, u); + this.write6FloatsUnsafe(b, r, d, w, f, k); }; - a.prototype.write6FloatsUnsafe = function(a, h, l, c, r, e) { - var u = this._offset >> 2; - this._f32[u + 0] = a; - this._f32[u + 1] = h; - this._f32[u + 2] = l; - this._f32[u + 3] = c; - this._f32[u + 4] = r; - this._f32[u + 5] = e; + b.prototype.write6FloatsUnsafe = function(b, g, r, d, w, f) { + var k = this._offset >> 2; + this._f32[k + 0] = b; + this._f32[k + 1] = g; + this._f32[k + 2] = r; + this._f32[k + 3] = d; + this._f32[k + 4] = w; + this._f32[k + 5] = f; this._offset += 24; }; - a.prototype.subF32View = function() { + b.prototype.subF32View = function() { return this._f32.subarray(0, this._offset >> 2); }; - a.prototype.subI32View = function() { + b.prototype.subI32View = function() { return this._i32.subarray(0, this._offset >> 2); }; - a.prototype.subU16View = function() { + b.prototype.subU16View = function() { return this._u16.subarray(0, this._offset >> 1); }; - a.prototype.subU8View = function() { + b.prototype.subU8View = function() { return this._u8.subarray(0, this._offset); }; - a.prototype.hashWords = function(a, h, l) { - h = this._i32; - for (var c = 0;c < l;c++) { - a = (31 * a | 0) + h[c] | 0; + b.prototype.hashWords = function(b, g, r) { + g = this._i32; + for (var d = 0;d < r;d++) { + b = (31 * b | 0) + g[d] | 0; } - return a; + return b; }; - a.prototype.reserve = function(a) { - a = a + 3 & -4; - this.ensureCapacity(this._offset + a); - this._offset += a; + b.prototype.reserve = function(b) { + b = b + 3 & -4; + this.ensureCapacity(this._offset + b); + this._offset += b; }; - return a; + return b; }(); - a.ArrayWriter = r; - })(b.ArrayUtilities || (b.ArrayUtilities = {})); - var m = b.ArrayUtilities, d = function() { - function a(l) { - this._u8 = new Uint8Array(l); - this._u16 = new Uint16Array(l); - this._i32 = new Int32Array(l); - this._f32 = new Float32Array(l); + d.ArrayWriter = r; + })(u = c.ArrayUtilities || (c.ArrayUtilities = {})); + var l = function() { + function d(b) { + this._u8 = new Uint8Array(b); + this._u16 = new Uint16Array(b); + this._i32 = new Int32Array(b); + this._f32 = new Float32Array(b); this._offset = 0; } - Object.defineProperty(a.prototype, "offset", {get:function() { + Object.defineProperty(d.prototype, "offset", {get:function() { return this._offset; }, enumerable:!0, configurable:!0}); - a.prototype.isEmpty = function() { + d.prototype.isEmpty = function() { return this._offset === this._u8.length; }; - a.prototype.readInt = function() { - s.assert(0 === (this._offset & 3)); - s.assert(this._offset <= this._u8.length - 4); - var a = this._i32[this._offset >> 2]; + d.prototype.readInt = function() { + p.assert(0 === (this._offset & 3)); + p.assert(this._offset <= this._u8.length - 4); + var b = this._i32[this._offset >> 2]; this._offset += 4; - return a; + return b; }; - a.prototype.readFloat = function() { - s.assert(0 === (this._offset & 3)); - s.assert(this._offset <= this._u8.length - 4); - var a = this._f32[this._offset >> 2]; + d.prototype.readFloat = function() { + p.assert(0 === (this._offset & 3)); + p.assert(this._offset <= this._u8.length - 4); + var b = this._f32[this._offset >> 2]; this._offset += 4; - return a; + return b; }; - return a; + return d; }(); - b.ArrayReader = d; - (function(l) { - function c(a, h) { - return Object.prototype.hasOwnProperty.call(a, h); - } - function e(a, h) { - for (var l in h) { - c(h, l) && (a[l] = h[l]); - } - } - l.boxValue = function(a) { - return void 0 == a || g(a) ? a : Object(a); - }; - l.toKeyValueArray = function(a) { - var h = Object.prototype.hasOwnProperty, l = [], c; - for (c in a) { - h.call(a, c) && l.push([c, a[c]]); + c.ArrayReader = l; + (function(d) { + function b(b, g) { + return Object.prototype.hasOwnProperty.call(b, g); + } + function g(g, d) { + for (var f in d) { + b(d, f) && (g[f] = d[f]); + } + } + d.boxValue = function(b) { + return void 0 == b || a(b) ? b : Object(b); + }; + d.toKeyValueArray = function(b) { + var g = Object.prototype.hasOwnProperty, d = [], f; + for (f in b) { + g.call(b, f) && d.push([f, b[f]]); } - return l; - }; - l.isPrototypeWriteable = function(a) { - return Object.getOwnPropertyDescriptor(a, "prototype").writable; - }; - l.hasOwnProperty = c; - l.propertyIsEnumerable = function(a, h) { - return Object.prototype.propertyIsEnumerable.call(a, h); + return d; }; - l.getOwnPropertyDescriptor = function(a, h) { - return Object.getOwnPropertyDescriptor(a, h); + d.isPrototypeWriteable = function(b) { + return Object.getOwnPropertyDescriptor(b, "prototype").writable; }; - l.hasOwnGetter = function(a, h) { - var l = Object.getOwnPropertyDescriptor(a, h); - return!(!l || !l.get); + d.hasOwnProperty = b; + d.propertyIsEnumerable = function(b, g) { + return Object.prototype.propertyIsEnumerable.call(b, g); }; - l.getOwnGetter = function(a, h) { - var l = Object.getOwnPropertyDescriptor(a, h); - return l ? l.get : null; + d.getOwnPropertyDescriptor = function(b, g) { + return Object.getOwnPropertyDescriptor(b, g); }; - l.hasOwnSetter = function(a, h) { - var l = Object.getOwnPropertyDescriptor(a, h); - return!(!l || !l.set); + d.hasOwnGetter = function(b, g) { + var d = Object.getOwnPropertyDescriptor(b, g); + return!(!d || !d.get); }; - l.createObject = function(a) { - return Object.create(a); + d.getOwnGetter = function(b, g) { + var d = Object.getOwnPropertyDescriptor(b, g); + return d ? d.get : null; }; - l.createEmptyObject = function() { - return Object.create(null); + d.hasOwnSetter = function(b, g) { + var d = Object.getOwnPropertyDescriptor(b, g); + return!(!d || !d.set); }; - l.createMap = function() { + d.createMap = function() { return Object.create(null); }; - l.createArrayMap = function() { + d.createArrayMap = function() { return[]; }; - l.defineReadOnlyProperty = function(a, h, l) { - Object.defineProperty(a, h, {value:l, writable:!1, configurable:!0, enumerable:!1}); + d.defineReadOnlyProperty = function(b, g, d) { + Object.defineProperty(b, g, {value:d, writable:!1, configurable:!0, enumerable:!1}); }; - l.getOwnPropertyDescriptors = function(a) { - for (var h = l.createMap(), c = Object.getOwnPropertyNames(a), e = 0;e < c.length;e++) { - h[c[e]] = Object.getOwnPropertyDescriptor(a, c[e]); + d.getOwnPropertyDescriptors = function(b) { + for (var g = d.createMap(), f = Object.getOwnPropertyNames(b), k = 0;k < f.length;k++) { + g[f[k]] = Object.getOwnPropertyDescriptor(b, f[k]); } - return h; + return g; }; - l.cloneObject = function(a) { - var h = Object.create(Object.getPrototypeOf(a)); - e(h, a); - return h; + d.cloneObject = function(b) { + var d = Object.create(Object.getPrototypeOf(b)); + g(d, b); + return d; }; - l.copyProperties = function(a, h) { - for (var l in h) { - a[l] = h[l]; + d.copyProperties = function(b, g) { + for (var d in g) { + b[d] = g[d]; } }; - l.copyOwnProperties = e; - l.copyOwnPropertyDescriptors = function(a, h, l) { - "undefined" === typeof l && (l = !0); - for (var e in h) { - if (c(h, e)) { - var q = Object.getOwnPropertyDescriptor(h, e); - if (l || !c(a, e)) { - s.assert(q); + d.copyOwnProperties = g; + d.copyOwnPropertyDescriptors = function(g, d, f) { + void 0 === f && (f = !0); + for (var k in d) { + if (b(d, k)) { + var e = Object.getOwnPropertyDescriptor(d, k); + if (f || !b(g, k)) { + p.assert(e); try { - Object.defineProperty(a, e, q); - } catch (d) { + Object.defineProperty(g, k, e); + } catch (n) { } } } } }; - l.getLatestGetterOrSetterPropertyDescriptor = function(a, h) { - for (var l = {};a;) { - var c = Object.getOwnPropertyDescriptor(a, h); - c && (l.get = l.get || c.get, l.set = l.set || c.set); - if (l.get && l.set) { + d.getLatestGetterOrSetterPropertyDescriptor = function(b, g) { + for (var d = {};b;) { + var f = Object.getOwnPropertyDescriptor(b, g); + f && (d.get = d.get || f.get, d.set = d.set || f.set); + if (d.get && d.set) { break; } - a = Object.getPrototypeOf(a); + b = Object.getPrototypeOf(b); } - return l; - }; - l.defineNonEnumerableGetterOrSetter = function(a, h, c, e) { - var u = l.getLatestGetterOrSetterPropertyDescriptor(a, h); - u.configurable = !0; - u.enumerable = !1; - e ? u.get = c : u.set = c; - Object.defineProperty(a, h, u); - }; - l.defineNonEnumerableGetter = function(a, h, l) { - Object.defineProperty(a, h, {get:l, configurable:!0, enumerable:!1}); - }; - l.defineNonEnumerableSetter = function(a, h, l) { - Object.defineProperty(a, h, {set:l, configurable:!0, enumerable:!1}); - }; - l.defineNonEnumerableProperty = function(a, h, l) { - Object.defineProperty(a, h, {value:l, writable:!0, configurable:!0, enumerable:!1}); - }; - l.defineNonEnumerableForwardingProperty = function(l, h, c) { - Object.defineProperty(l, h, {get:a.makeForwardingGetter(c), set:a.makeForwardingSetter(c), writable:!0, configurable:!0, enumerable:!1}); - }; - l.defineNewNonEnumerableProperty = function(a, h, c) { - s.assert(!Object.prototype.hasOwnProperty.call(a, h), "Property: " + h + " already exits."); - l.defineNonEnumerableProperty(a, h, c); - }; - })(b.ObjectUtilities || (b.ObjectUtilities = {})); - (function(a) { - a.makeForwardingGetter = function(a) { - return new Function('return this["' + a + '"]'); - }; - a.makeForwardingSetter = function(a) { - return new Function("value", 'this["' + a + '"] = value;'); + return d; }; - a.bindSafely = function(a, l) { - s.assert(!a.boundTo && l); - var c = a.bind(l); - c.boundTo = l; - return c; + d.defineNonEnumerableGetterOrSetter = function(b, g, f, k) { + var e = d.getLatestGetterOrSetterPropertyDescriptor(b, g); + e.configurable = !0; + e.enumerable = !1; + k ? e.get = f : e.set = f; + Object.defineProperty(b, g, e); + }; + d.defineNonEnumerableGetter = function(b, g, d) { + Object.defineProperty(b, g, {get:d, configurable:!0, enumerable:!1}); + }; + d.defineNonEnumerableSetter = function(b, g, d) { + Object.defineProperty(b, g, {set:d, configurable:!0, enumerable:!1}); + }; + d.defineNonEnumerableProperty = function(b, g, d) { + Object.defineProperty(b, g, {value:d, writable:!0, configurable:!0, enumerable:!1}); + }; + d.defineNonEnumerableForwardingProperty = function(b, g, d) { + Object.defineProperty(b, g, {get:e.makeForwardingGetter(d), set:e.makeForwardingSetter(d), writable:!0, configurable:!0, enumerable:!1}); + }; + d.defineNewNonEnumerableProperty = function(b, g, f) { + p.assert(!Object.prototype.hasOwnProperty.call(b, g), "Property: " + g + " already exits."); + d.defineNonEnumerableProperty(b, g, f); + }; + d.createPublicAliases = function(b, g) { + for (var d = {value:null, writable:!0, configurable:!0, enumerable:!1}, f = 0;f < g.length;f++) { + var k = g[f]; + d.value = b[k]; + Object.defineProperty(b, "$Bg" + k, d); + } + }; + })(c.ObjectUtilities || (c.ObjectUtilities = {})); + var e; + (function(d) { + d.makeForwardingGetter = function(b) { + return new Function('return this["' + b + '"]//# sourceURL=fwd-get-' + b + ".as"); + }; + d.makeForwardingSetter = function(b) { + return new Function("value", 'this["' + b + '"] = value;//# sourceURL=fwd-set-' + b + ".as"); + }; + d.bindSafely = function(b, g) { + function d() { + return b.apply(g, arguments); + } + p.assert(!b.boundTo); + p.assert(g); + d.boundTo = g; + return d; }; - })(b.FunctionUtilities || (b.FunctionUtilities = {})); - var a = b.FunctionUtilities; - (function(a) { - function c(a) { - return "string" === typeof a ? '"' + a + '"' : "number" === typeof a || "boolean" === typeof a ? String(a) : a instanceof Array ? "[] " + a.length : typeof a; - } - var e = b.Debug.assert; - a.repeatString = function(a, h) { - for (var l = "", c = 0;c < h;c++) { - l += a; + })(e = c.FunctionUtilities || (c.FunctionUtilities = {})); + (function(d) { + function b(b) { + return "string" === typeof b ? '"' + b + '"' : "number" === typeof b || "boolean" === typeof b ? String(b) : b instanceof Array ? "[] " + b.length : typeof b; + } + var g = c.Debug.assert; + d.repeatString = function(b, g) { + for (var d = "", r = 0;r < g;r++) { + d += b; } - return l; + return d; }; - a.memorySizeToString = function(a) { - a |= 0; - return 1024 > a ? a + " B" : 1048576 > a ? (a / 1024).toFixed(2) + "KB" : (a / 1048576).toFixed(2) + "MB"; - }; - a.toSafeString = c; - a.toSafeArrayString = function(a) { - for (var h = [], l = 0;l < a.length;l++) { - h.push(c(a[l])); - } - return h.join(", "); - }; - a.utf8decode = function(a) { - for (var h = new Uint8Array(4 * a.length), l = 0, c = 0, e = a.length;c < e;c++) { - var r = a.charCodeAt(c); - if (127 >= r) { - h[l++] = r; + d.memorySizeToString = function(b) { + b |= 0; + return 1024 > b ? b + " B" : 1048576 > b ? (b / 1024).toFixed(2) + "KB" : (b / 1048576).toFixed(2) + "MB"; + }; + d.toSafeString = b; + d.toSafeArrayString = function(g) { + for (var d = [], r = 0;r < g.length;r++) { + d.push(b(g[r])); + } + return d.join(", "); + }; + d.utf8decode = function(b) { + for (var g = new Uint8Array(4 * b.length), d = 0, r = 0, w = b.length;r < w;r++) { + var f = b.charCodeAt(r); + if (127 >= f) { + g[d++] = f; } else { - if (55296 <= r && 56319 >= r) { - var u = a.charCodeAt(c + 1); - 56320 <= u && 57343 >= u && (r = ((r & 1023) << 10) + (u & 1023) + 65536, ++c); + if (55296 <= f && 56319 >= f) { + var k = b.charCodeAt(r + 1); + 56320 <= k && 57343 >= k && (f = ((f & 1023) << 10) + (k & 1023) + 65536, ++r); } - 0 !== (r & 4292870144) ? (h[l++] = 248 | r >>> 24 & 3, h[l++] = 128 | r >>> 18 & 63, h[l++] = 128 | r >>> 12 & 63, h[l++] = 128 | r >>> 6 & 63) : 0 !== (r & 4294901760) ? (h[l++] = 240 | r >>> 18 & 7, h[l++] = 128 | r >>> 12 & 63, h[l++] = 128 | r >>> 6 & 63) : 0 !== (r & 4294965248) ? (h[l++] = 224 | r >>> 12 & 15, h[l++] = 128 | r >>> 6 & 63) : h[l++] = 192 | r >>> 6 & 31; - h[l++] = 128 | r & 63; + 0 !== (f & 4292870144) ? (g[d++] = 248 | f >>> 24 & 3, g[d++] = 128 | f >>> 18 & 63, g[d++] = 128 | f >>> 12 & 63, g[d++] = 128 | f >>> 6 & 63) : 0 !== (f & 4294901760) ? (g[d++] = 240 | f >>> 18 & 7, g[d++] = 128 | f >>> 12 & 63, g[d++] = 128 | f >>> 6 & 63) : 0 !== (f & 4294965248) ? (g[d++] = 224 | f >>> 12 & 15, g[d++] = 128 | f >>> 6 & 63) : g[d++] = 192 | f >>> 6 & 31; + g[d++] = 128 | f & 63; } } - return h.subarray(0, l); + return g.subarray(0, d); }; - a.utf8encode = function(a) { - for (var h = 0, l = "";h < a.length;) { - var c = a[h++] & 255; - if (127 >= c) { - l += String.fromCharCode(c); + d.utf8encode = function(b) { + for (var g = 0, d = "";g < b.length;) { + var r = b[g++] & 255; + if (127 >= r) { + d += String.fromCharCode(r); } else { - var e = 192, r = 5; + var f = 192, w = 5; do { - if ((c & (e >> 1 | 128)) === e) { + if ((r & (f >> 1 | 128)) === f) { break; } - e = e >> 1 | 128; - --r; - } while (0 <= r); - if (0 >= r) { - l += String.fromCharCode(c); + f = f >> 1 | 128; + --w; + } while (0 <= w); + if (0 >= w) { + d += String.fromCharCode(r); } else { - for (var c = c & (1 << r) - 1, e = !1, u = 5;u >= r;--u) { - var q = a[h++]; - if (128 != (q & 192)) { - e = !0; + for (var r = r & (1 << w) - 1, f = !1, k = 5;k >= w;--k) { + var e = b[g++]; + if (128 != (e & 192)) { + f = !0; break; } - c = c << 6 | q & 63; + r = r << 6 | e & 63; } - if (e) { - for (r = h - (7 - u);r < h;++r) { - l += String.fromCharCode(a[r] & 255); + if (f) { + for (w = g - (7 - k);w < g;++w) { + d += String.fromCharCode(b[w] & 255); } } else { - l = 65536 <= c ? l + String.fromCharCode(c - 65536 >> 10 & 1023 | 55296, c & 1023 | 56320) : l + String.fromCharCode(c); + d = 65536 <= r ? d + String.fromCharCode(r - 65536 >> 10 & 1023 | 55296, r & 1023 | 56320) : d + String.fromCharCode(r); } } } } - return l; + return d; }; - a.base64ArrayBuffer = function(a) { - var h = ""; - a = new Uint8Array(a); - for (var l = a.byteLength, c = l % 3, l = l - c, e, r, u, q, d = 0;d < l;d += 3) { - q = a[d] << 16 | a[d + 1] << 8 | a[d + 2], e = (q & 16515072) >> 18, r = (q & 258048) >> 12, u = (q & 4032) >> 6, q &= 63, h += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[e] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[r] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[u] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[q]; + d.base64ArrayBuffer = function(b) { + var g = ""; + b = new Uint8Array(b); + for (var d = b.byteLength, r = d % 3, d = d - r, f, w, k, e, n = 0;n < d;n += 3) { + e = b[n] << 16 | b[n + 1] << 8 | b[n + 2], f = (e & 16515072) >> 18, w = (e & 258048) >> 12, k = (e & 4032) >> 6, e &= 63, g += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[f] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[w] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[k] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[e]; } - 1 == c ? (q = a[l], h += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(q & 252) >> 2] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(q & 3) << 4] + "==") : 2 == c && (q = a[l] << 8 | a[l + 1], h += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(q & 64512) >> 10] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(q & 1008) >> 4] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(q & 15) << + 1 == r ? (e = b[d], g += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(e & 252) >> 2] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(e & 3) << 4] + "==") : 2 == r && (e = b[d] << 8 | b[d + 1], g += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(e & 64512) >> 10] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(e & 1008) >> 4] + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(e & 15) << 2] + "="); - return h; + return g; }; - a.escapeString = function(a) { - void 0 !== a && (a = a.replace(/[^\w$]/gi, "$"), /^\d/.test(a) && (a = "$" + a)); - return a; + d.escapeString = function(b) { + void 0 !== b && (b = b.replace(/[^\w$]/gi, "$"), /^\d/.test(b) && (b = "$" + b)); + return b; }; - a.fromCharCodeArray = function(a) { - for (var h = "", l = 0;l < a.length;l += 16384) { - var c = Math.min(a.length - l, 16384), h = h + String.fromCharCode.apply(null, a.subarray(l, l + c)) + d.fromCharCodeArray = function(b) { + for (var g = "", d = 0;d < b.length;d += 16384) { + var r = Math.min(b.length - d, 16384), g = g + String.fromCharCode.apply(null, b.subarray(d, d + r)) } - return h; + return g; }; - a.variableLengthEncodeInt32 = function(h) { - var c = 32 - Math.clz32(h); - e(32 >= c, c); - for (var r = Math.ceil(c / 6), u = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_"[r], q = r - 1;0 <= q;q--) { - u += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_"[h >> 6 * q & 63]; - } - e(a.variableLengthDecodeInt32(u) === h, h + " : " + u + " - " + r + " bits: " + c); - return u; - }; - a.toEncoding = function(a) { - return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_"[a]; - }; - a.fromEncoding = function(a) { - a = a.charCodeAt(0); - if (65 <= a && 90 >= a) { - return a - 65; + d.variableLengthEncodeInt32 = function(b) { + var r = 32 - Math.clz32(b); + g(32 >= r, r); + for (var f = Math.ceil(r / 6), w = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_"[f], k = f - 1;0 <= k;k--) { + w += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_"[b >> 6 * k & 63]; + } + g(d.variableLengthDecodeInt32(w) === b, b + " : " + w + " - " + f + " bits: " + r); + return w; + }; + d.toEncoding = function(b) { + return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_"[b]; + }; + d.fromEncoding = function(b) { + b = b.charCodeAt(0); + if (65 <= b && 90 >= b) { + return b - 65; } - if (97 <= a && 122 >= a) { - return a - 71; + if (97 <= b && 122 >= b) { + return b - 71; } - if (48 <= a && 57 >= a) { - return a + 4; + if (48 <= b && 57 >= b) { + return b + 4; } - if (36 === a) { + if (36 === b) { return 62; } - if (95 === a) { + if (95 === b) { return 63; } - e(!1, "Invalid Encoding"); + g(!1, "Invalid Encoding"); }; - a.variableLengthDecodeInt32 = function(h) { - for (var c = a.fromEncoding(h[0]), e = 0, r = 0;r < c;r++) { - var u = 6 * (c - r - 1), e = e | a.fromEncoding(h[1 + r]) << u + d.variableLengthDecodeInt32 = function(b) { + for (var g = d.fromEncoding(b[0]), r = 0, f = 0;f < g;f++) { + var w = 6 * (g - f - 1), r = r | d.fromEncoding(b[1 + f]) << w } - return e; + return r; }; - a.trimMiddle = function(a, h) { - if (a.length <= h) { - return a; + d.trimMiddle = function(b, g) { + if (b.length <= g) { + return b; } - var l = h >> 1, c = h - l - 1; - return a.substr(0, l) + "\u2026" + a.substr(a.length - c, c); + var d = g >> 1, r = g - d - 1; + return b.substr(0, d) + "\u2026" + b.substr(b.length - r, r); }; - a.multiple = function(a, h) { - for (var l = "", c = 0;c < h;c++) { - l += a; + d.multiple = function(b, g) { + for (var d = "", r = 0;r < g;r++) { + d += b; } - return l; + return d; }; - a.indexOfAny = function(a, h, l) { - for (var c = a.length, e = 0;e < h.length;e++) { - var r = a.indexOf(h[e], l); - 0 <= r && (c = Math.min(c, r)); - } - return c === a.length ? -1 : c; - }; - var r = Array(3), h = Array(4), q = Array(5), d = Array(6), p = Array(7), n = Array(8), m = Array(9); - a.concat3 = function(a, h, l) { - r[0] = a; - r[1] = h; - r[2] = l; + d.indexOfAny = function(b, g, d) { + for (var r = b.length, f = 0;f < g.length;f++) { + var w = b.indexOf(g[f], d); + 0 <= w && (r = Math.min(r, w)); + } + return r === b.length ? -1 : r; + }; + var r = Array(3), w = Array(4), f = Array(5), k = Array(6), e = Array(7), n = Array(8), m = Array(9); + d.concat3 = function(b, g, d) { + r[0] = b; + r[1] = g; + r[2] = d; return r.join(""); }; - a.concat4 = function(a, l, c, e) { - h[0] = a; - h[1] = l; - h[2] = c; - h[3] = e; - return h.join(""); - }; - a.concat5 = function(a, h, l, c, e) { - q[0] = a; - q[1] = h; - q[2] = l; - q[3] = c; - q[4] = e; - return q.join(""); - }; - a.concat6 = function(a, h, l, c, e, r) { - d[0] = a; - d[1] = h; - d[2] = l; - d[3] = c; - d[4] = e; - d[5] = r; - return d.join(""); - }; - a.concat7 = function(a, h, l, c, e, r, u) { - p[0] = a; - p[1] = h; - p[2] = l; - p[3] = c; - p[4] = e; - p[5] = r; - p[6] = u; - return p.join(""); - }; - a.concat8 = function(a, h, l, c, e, r, u, q) { - n[0] = a; - n[1] = h; - n[2] = l; - n[3] = c; - n[4] = e; - n[5] = r; - n[6] = u; - n[7] = q; + d.concat4 = function(b, g, d, r) { + w[0] = b; + w[1] = g; + w[2] = d; + w[3] = r; + return w.join(""); + }; + d.concat5 = function(b, g, d, r, w) { + f[0] = b; + f[1] = g; + f[2] = d; + f[3] = r; + f[4] = w; + return f.join(""); + }; + d.concat6 = function(b, g, d, r, f, w) { + k[0] = b; + k[1] = g; + k[2] = d; + k[3] = r; + k[4] = f; + k[5] = w; + return k.join(""); + }; + d.concat7 = function(b, g, d, r, f, w, k) { + e[0] = b; + e[1] = g; + e[2] = d; + e[3] = r; + e[4] = f; + e[5] = w; + e[6] = k; + return e.join(""); + }; + d.concat8 = function(b, g, d, r, f, w, k, e) { + n[0] = b; + n[1] = g; + n[2] = d; + n[3] = r; + n[4] = f; + n[5] = w; + n[6] = k; + n[7] = e; return n.join(""); }; - a.concat9 = function(a, h, l, c, e, r, u, q, d) { - m[0] = a; - m[1] = h; - m[2] = l; - m[3] = c; - m[4] = e; - m[5] = r; - m[6] = u; - m[7] = q; - m[8] = d; + d.concat9 = function(b, g, d, r, f, w, k, e, n) { + m[0] = b; + m[1] = g; + m[2] = d; + m[3] = r; + m[4] = f; + m[5] = w; + m[6] = k; + m[7] = e; + m[8] = n; return m.join(""); }; - })(b.StringUtilities || (b.StringUtilities = {})); - (function(a) { - var c = new Uint8Array([7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]), e = new Int32Array([-680876936, -389564586, 606105819, -1044525330, -176418897, 1200080426, -1473231341, -45705983, 1770035416, -1958414417, -42063, -1990404162, 1804603682, -40341101, -1502002290, 1236535329, -165796510, -1069501632, + })(c.StringUtilities || (c.StringUtilities = {})); + (function(d) { + var b = new Uint8Array([7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]), g = new Int32Array([-680876936, -389564586, 606105819, -1044525330, -176418897, 1200080426, -1473231341, -45705983, 1770035416, -1958414417, -42063, -1990404162, 1804603682, -40341101, -1502002290, 1236535329, -165796510, -1069501632, 643717713, -373897302, -701558691, 38016083, -660478335, -405537848, 568446438, -1019803690, -187363961, 1163531501, -1444681467, -51403784, 1735328473, -1926607734, -378558, -2022574463, 1839030562, -35309556, -1530992060, 1272893353, -155497632, -1094730640, 681279174, -358537222, -722521979, 76029189, -640364487, -421815835, 530742520, -995338651, -198630844, 1126891415, -1416354905, -57434055, 1700485571, -1894986606, -1051523, -2054922799, 1873313359, -30611744, -1560198380, 1309151649, -145523070, -1120210379, 718787259, -343485551]); - a.hashBytesTo32BitsMD5 = function(a, h, l) { - var q = 1732584193, d = -271733879, p = -1732584194, n = 271733878, b = l + 72 & -64, m = new Uint8Array(b), f; - for (f = 0;f < l;++f) { - m[f] = a[h++]; - } - m[f++] = 128; - for (a = b - 8;f < a;) { - m[f++] = 0; - } - m[f++] = l << 3 & 255; - m[f++] = l >> 5 & 255; - m[f++] = l >> 13 & 255; - m[f++] = l >> 21 & 255; - m[f++] = l >>> 29 & 255; - m[f++] = 0; - m[f++] = 0; - m[f++] = 0; - a = new Int32Array(16); - for (f = 0;f < b;) { - for (l = 0;16 > l;++l, f += 4) { - a[l] = m[f] | m[f + 1] << 8 | m[f + 2] << 16 | m[f + 3] << 24; - } - var g = q; - h = d; - var s = p, k = n, t, z; - for (l = 0;64 > l;++l) { - 16 > l ? (t = h & s | ~h & k, z = l) : 32 > l ? (t = k & h | ~k & s, z = 5 * l + 1 & 15) : 48 > l ? (t = h ^ s ^ k, z = 3 * l + 5 & 15) : (t = s ^ (h | ~k), z = 7 * l & 15); - var P = k, g = g + t + e[l] + a[z] | 0; - t = c[l]; - k = s; - s = h; - h = h + (g << t | g >>> 32 - t) | 0; - g = P; - } - q = q + g | 0; - d = d + h | 0; - p = p + s | 0; - n = n + k | 0; - } - return q; - }; - a.hashBytesTo32BitsAdler = function(a, h, l) { - var c = 1, e = 0; - for (l = h + l;h < l;++h) { - c = (c + (a[h] & 255)) % 65521, e = (e + c) % 65521; + d.hashBytesTo32BitsMD5 = function(d, f, k) { + var e = 1732584193, n = -271733879, m = -1732584194, l = 271733878, q = k + 72 & -64, a = new Uint8Array(q), t; + for (t = 0;t < k;++t) { + a[t] = d[f++]; + } + a[t++] = 128; + for (d = q - 8;t < d;) { + a[t++] = 0; + } + a[t++] = k << 3 & 255; + a[t++] = k >> 5 & 255; + a[t++] = k >> 13 & 255; + a[t++] = k >> 21 & 255; + a[t++] = k >>> 29 & 255; + a[t++] = 0; + a[t++] = 0; + a[t++] = 0; + d = new Int32Array(16); + for (t = 0;t < q;) { + for (k = 0;16 > k;++k, t += 4) { + d[k] = a[t] | a[t + 1] << 8 | a[t + 2] << 16 | a[t + 3] << 24; + } + var c = e; + f = n; + var s = m, u = l, p, h; + for (k = 0;64 > k;++k) { + 16 > k ? (p = f & s | ~f & u, h = k) : 32 > k ? (p = u & f | ~u & s, h = 5 * k + 1 & 15) : 48 > k ? (p = f ^ s ^ u, h = 3 * k + 5 & 15) : (p = s ^ (f | ~u), h = 7 * k & 15); + var v = u, c = c + p + g[k] + d[h] | 0; + p = b[k]; + u = s; + s = f; + f = f + (c << p | c >>> 32 - p) | 0; + c = v; + } + e = e + c | 0; + n = n + f | 0; + m = m + s | 0; + l = l + u | 0; } - return e << 16 | c; + return e; }; - })(b.HashUtilities || (b.HashUtilities = {})); - var c = function() { - function a() { - } - a.seed = function(c) { - a._state[0] = c; - a._state[1] = c; - }; - a.next = function() { - var a = this._state, l = Math.imul(18273, a[0] & 65535) + (a[0] >>> 16) | 0; - a[0] = l; - var c = Math.imul(36969, a[1] & 65535) + (a[1] >>> 16) | 0; - a[1] = c; - a = (l << 16) + (c & 65535) | 0; - return 2.3283064365386963E-10 * (0 > a ? a + 4294967296 : a); + d.hashBytesTo32BitsAdler = function(b, g, d) { + var f = 1, k = 0; + for (d = g + d;g < d;++g) { + f = (f + (b[g] & 255)) % 65521, k = (k + f) % 65521; + } + return k << 16 | f; + }; + })(c.HashUtilities || (c.HashUtilities = {})); + var m = function() { + function d() { + } + d.seed = function(b) { + d._state[0] = b; + d._state[1] = b; + }; + d.next = function() { + var b = this._state, g = Math.imul(18273, b[0] & 65535) + (b[0] >>> 16) | 0; + b[0] = g; + var d = Math.imul(36969, b[1] & 65535) + (b[1] >>> 16) | 0; + b[1] = d; + b = (g << 16) + (d & 65535) | 0; + return 2.3283064365386963E-10 * (0 > b ? b + 4294967296 : b); }; - a._state = new Uint32Array([57005, 48879]); - return a; + d._state = new Uint32Array([57005, 48879]); + return d; }(); - b.Random = c; + c.Random = m; Math.random = function() { - return c.next(); + return m.next(); }; (function() { - function a() { - this.id = "$weakmap" + c++; + function d() { + this.id = "$weakmap" + b++; } if ("function" !== typeof jsGlobal.WeakMap) { - var c = 0; - a.prototype = {has:function(a) { - return a.hasOwnProperty(this.id); - }, get:function(a, l) { - return a.hasOwnProperty(this.id) ? a[this.id] : l; - }, set:function(a, l) { - Object.defineProperty(a, this.id, {value:l, enumerable:!1, configurable:!0}); + var b = 0; + d.prototype = {has:function(b) { + return b.hasOwnProperty(this.id); + }, get:function(b, d) { + return b.hasOwnProperty(this.id) ? b[this.id] : d; + }, set:function(b, d) { + Object.defineProperty(b, this.id, {value:d, enumerable:!1, configurable:!0}); }}; - jsGlobal.WeakMap = a; + jsGlobal.WeakMap = d; } })(); - d = function() { - function a() { + l = function() { + function d() { "undefined" !== typeof netscape && netscape.security.PrivilegeManager ? this._map = new WeakMap : this._list = []; } - a.prototype.clear = function() { + d.prototype.clear = function() { this._map ? this._map.clear() : this._list.length = 0; }; - a.prototype.push = function(a) { - this._map ? this._map.set(a, null) : this._list.push(a); + d.prototype.push = function(b) { + this._map ? (p.assert(!this._map.has(b)), this._map.set(b, null)) : (p.assert(-1 === this._list.indexOf(b)), this._list.push(b)); }; - a.prototype.forEach = function(a) { + d.prototype.remove = function(b) { + this._map ? (p.assert(this._map.has(b)), this._map.delete(b)) : (p.assert(-1 < this._list.indexOf(b)), this._list[this._list.indexOf(b)] = null, p.assert(-1 === this._list.indexOf(b))); + }; + d.prototype.forEach = function(b) { if (this._map) { - "undefined" !== typeof netscape && netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"), Components.utils.nondeterministicGetWeakMapKeys(this._map).forEach(function(h) { - 0 !== h._referenceCount && a(h); + "undefined" !== typeof netscape && netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"), Components.utils.nondeterministicGetWeakMapKeys(this._map).forEach(function(g) { + 0 !== g._referenceCount && b(g); }); } else { - for (var l = this._list, c = 0, h = 0;h < l.length;h++) { - var e = l[h]; - 0 === e._referenceCount ? c++ : a(e); + for (var g = this._list, d = 0, f = 0;f < g.length;f++) { + var k = g[f]; + k && (0 === k._referenceCount ? (g[f] = null, d++) : b(k)); } - if (16 < c && c > l.length >> 2) { - c = []; - for (h = 0;h < l.length;h++) { - 0 < l[h]._referenceCount && c.push(l[h]); + if (16 < d && d > g.length >> 2) { + d = []; + for (f = 0;f < g.length;f++) { + (k = g[f]) && 0 < k._referenceCount && d.push(k); } - this._list = c; + this._list = d; } } }; - Object.defineProperty(a.prototype, "length", {get:function() { + Object.defineProperty(d.prototype, "length", {get:function() { return this._map ? -1 : this._list.length; }, enumerable:!0, configurable:!0}); - return a; + return d; }(); - b.WeakList = d; - (function(a) { - a.pow2 = function(a) { - return a === (a | 0) ? 0 > a ? 1 / (1 << -a) : 1 << a : Math.pow(2, a); + c.WeakList = l; + var t; + (function(d) { + d.pow2 = function(b) { + return b === (b | 0) ? 0 > b ? 1 / (1 << -b) : 1 << b : Math.pow(2, b); + }; + d.clamp = function(b, g, d) { + return Math.max(g, Math.min(d, b)); + }; + d.roundHalfEven = function(b) { + if (.5 === Math.abs(b % 1)) { + var g = Math.floor(b); + return 0 === g % 2 ? g : Math.ceil(b); + } + return Math.round(b); + }; + d.epsilonEquals = function(b, g) { + return 1E-7 > Math.abs(b - g); + }; + })(t = c.NumberUtilities || (c.NumberUtilities = {})); + (function(d) { + d[d.MaxU16 = 65535] = "MaxU16"; + d[d.MaxI16 = 32767] = "MaxI16"; + d[d.MinI16 = -32768] = "MinI16"; + })(c.Numbers || (c.Numbers = {})); + var q; + (function(d) { + function b(b) { + return 256 * b << 16 >> 16; + } + var g = new ArrayBuffer(8); + d.i8 = new Int8Array(g); + d.u8 = new Uint8Array(g); + d.i32 = new Int32Array(g); + d.f32 = new Float32Array(g); + d.f64 = new Float64Array(g); + d.nativeLittleEndian = 1 === (new Int8Array((new Int32Array([1])).buffer))[0]; + d.floatToInt32 = function(b) { + d.f32[0] = b; + return d.i32[0]; + }; + d.int32ToFloat = function(b) { + d.i32[0] = b; + return d.f32[0]; + }; + d.swap16 = function(b) { + return(b & 255) << 8 | b >> 8 & 255; + }; + d.swap32 = function(b) { + return(b & 255) << 24 | (b & 65280) << 8 | b >> 8 & 65280 | b >> 24 & 255; + }; + d.toS8U8 = b; + d.fromS8U8 = function(b) { + return b / 256; + }; + d.clampS8U8 = function(g) { + return b(g) / 256; + }; + d.toS16 = function(b) { + return b << 16 >> 16; + }; + d.bitCount = function(b) { + b -= b >> 1 & 1431655765; + b = (b & 858993459) + (b >> 2 & 858993459); + return 16843009 * (b + (b >> 4) & 252645135) >> 24; + }; + d.ones = function(b) { + b -= b >> 1 & 1431655765; + b = (b & 858993459) + (b >> 2 & 858993459); + return 16843009 * (b + (b >> 4) & 252645135) >> 24; + }; + d.trailingZeros = function(b) { + return d.ones((b & -b) - 1); + }; + d.getFlags = function(b, g) { + var d = ""; + for (b = 0;b < g.length;b++) { + b & 1 << b && (d += g[b] + " "); + } + return 0 === d.length ? "" : d.trim(); }; - a.clamp = function(a, l, c) { - return Math.max(l, Math.min(c, a)); - }; - a.roundHalfEven = function(a) { - if (.5 === Math.abs(a % 1)) { - var l = Math.floor(a); - return 0 === l % 2 ? l : Math.ceil(a); - } - return Math.round(a); + d.isPowerOfTwo = function(b) { + return b && 0 === (b & b - 1); }; - a.epsilonEquals = function(a, l) { - return 1E-7 > Math.abs(a - l); + d.roundToMultipleOfFour = function(b) { + return b + 3 & -4; }; - })(b.NumberUtilities || (b.NumberUtilities = {})); - (function(a) { - a[a.MaxU16 = 65535] = "MaxU16"; - a[a.MaxI16 = 32767] = "MaxI16"; - a[a.MinI16 = -32768] = "MinI16"; - })(b.Numbers || (b.Numbers = {})); - (function(a) { - function c(a) { - return 256 * a << 16 >> 16; - } - var e = new ArrayBuffer(8); - a.i8 = new Int8Array(e); - a.u8 = new Uint8Array(e); - a.i32 = new Int32Array(e); - a.f32 = new Float32Array(e); - a.f64 = new Float64Array(e); - a.nativeLittleEndian = 1 === (new Int8Array((new Int32Array([1])).buffer))[0]; - a.floatToInt32 = function(c) { - a.f32[0] = c; - return a.i32[0]; - }; - a.int32ToFloat = function(c) { - a.i32[0] = c; - return a.f32[0]; - }; - a.swap16 = function(a) { - return(a & 255) << 8 | a >> 8 & 255; - }; - a.swap32 = function(a) { - return(a & 255) << 24 | (a & 65280) << 8 | a >> 8 & 65280 | a >> 24 & 255; - }; - a.toS8U8 = c; - a.fromS8U8 = function(a) { - return a / 256; - }; - a.clampS8U8 = function(a) { - return c(a) / 256; - }; - a.toS16 = function(a) { - return a << 16 >> 16; - }; - a.bitCount = function(a) { - a -= a >> 1 & 1431655765; - a = (a & 858993459) + (a >> 2 & 858993459); - return 16843009 * (a + (a >> 4) & 252645135) >> 24; - }; - a.ones = function(a) { - a -= a >> 1 & 1431655765; - a = (a & 858993459) + (a >> 2 & 858993459); - return 16843009 * (a + (a >> 4) & 252645135) >> 24; - }; - a.trailingZeros = function(c) { - return a.ones((c & -c) - 1); - }; - a.getFlags = function(a, h) { - var c = ""; - for (a = 0;a < h.length;a++) { - a & 1 << a && (c += h[a] + " "); - } - return 0 === c.length ? "" : c.trim(); - }; - a.isPowerOfTwo = function(a) { - return a && 0 === (a & a - 1); - }; - a.roundToMultipleOfFour = function(a) { - return a + 3 & -4; - }; - a.nearestPowerOfTwo = function(a) { - a--; - a |= a >> 1; - a |= a >> 2; - a |= a >> 4; - a |= a >> 8; - a |= a >> 16; - a++; - return a; + d.nearestPowerOfTwo = function(b) { + b--; + b |= b >> 1; + b |= b >> 2; + b |= b >> 4; + b |= b >> 8; + b |= b >> 16; + b++; + return b; }; - a.roundToMultipleOfPowerOfTwo = function(a, h) { - var c = (1 << h) - 1; - return a + c & ~c; - }; - Math.imul || (Math.imul = function(a, h) { - var c = a & 65535, l = h & 65535; - return c * l + ((a >>> 16 & 65535) * l + c * (h >>> 16 & 65535) << 16 >>> 0) | 0; + d.roundToMultipleOfPowerOfTwo = function(b, g) { + var d = (1 << g) - 1; + return b + d & ~d; + }; + Math.imul || (Math.imul = function(b, g) { + var d = b & 65535, f = g & 65535; + return d * f + ((b >>> 16 & 65535) * f + d * (g >>> 16 & 65535) << 16 >>> 0) | 0; }); - Math.clz32 || (Math.clz32 = function(c) { - c |= c >> 1; - c |= c >> 2; - c |= c >> 4; - c |= c >> 8; - return 32 - a.ones(c | c >> 16); + Math.clz32 || (Math.clz32 = function(b) { + b |= b >> 1; + b |= b >> 2; + b |= b >> 4; + b |= b >> 8; + return 32 - d.ones(b | b >> 16); }); - })(b.IntegerUtilities || (b.IntegerUtilities = {})); - var n = b.IntegerUtilities; - (function(a) { - function c(a, l, h, e, u, q) { - return(h - a) * (q - l) - (e - l) * (u - a); - } - a.pointInPolygon = function(a, c, h) { - for (var l = 0, e = h.length - 2, u = 0;u < e;u += 2) { - var q = h[u + 0], d = h[u + 1], p = h[u + 2], n = h[u + 3]; - (d <= c && n > c || d > c && n <= c) && a < q + (c - d) / (n - d) * (p - q) && l++; - } - return 1 === (l & 1); - }; - a.signedArea = c; - a.counterClockwise = function(a, l, h, e, q, d) { - return 0 < c(a, l, h, e, q, d); - }; - a.clockwise = function(a, l, h, e, q, d) { - return 0 > c(a, l, h, e, q, d); - }; - a.pointInPolygonInt32 = function(a, c, h) { - a |= 0; - c |= 0; - for (var l = 0, e = h.length - 2, u = 0;u < e;u += 2) { - var q = h[u + 0], d = h[u + 1], p = h[u + 2], n = h[u + 3]; - (d <= c && n > c || d > c && n <= c) && a < q + (c - d) / (n - d) * (p - q) && l++; - } - return 1 === (l & 1); - }; - })(b.GeometricUtilities || (b.GeometricUtilities = {})); - (function(a) { - a[a.Error = 1] = "Error"; - a[a.Warn = 2] = "Warn"; - a[a.Debug = 4] = "Debug"; - a[a.Log = 8] = "Log"; - a[a.Info = 16] = "Info"; - a[a.All = 31] = "All"; - })(b.LogLevel || (b.LogLevel = {})); - d = function() { - function a(c, e) { - "undefined" === typeof c && (c = !1); + })(q = c.IntegerUtilities || (c.IntegerUtilities = {})); + (function(d) { + function b(b, d, f, k, e, n) { + return(f - b) * (n - d) - (k - d) * (e - b); + } + d.pointInPolygon = function(b, d, f) { + for (var k = 0, e = f.length - 2, n = 0;n < e;n += 2) { + var m = f[n + 0], l = f[n + 1], q = f[n + 2], a = f[n + 3]; + (l <= d && a > d || l > d && a <= d) && b < m + (d - l) / (a - l) * (q - m) && k++; + } + return 1 === (k & 1); + }; + d.signedArea = b; + d.counterClockwise = function(g, d, f, k, e, n) { + return 0 < b(g, d, f, k, e, n); + }; + d.clockwise = function(g, d, f, k, e, n) { + return 0 > b(g, d, f, k, e, n); + }; + d.pointInPolygonInt32 = function(b, d, f) { + b |= 0; + d |= 0; + for (var k = 0, e = f.length - 2, n = 0;n < e;n += 2) { + var m = f[n + 0], l = f[n + 1], q = f[n + 2], a = f[n + 3]; + (l <= d && a > d || l > d && a <= d) && b < m + (d - l) / (a - l) * (q - m) && k++; + } + return 1 === (k & 1); + }; + })(c.GeometricUtilities || (c.GeometricUtilities = {})); + (function(d) { + d[d.Error = 1] = "Error"; + d[d.Warn = 2] = "Warn"; + d[d.Debug = 4] = "Debug"; + d[d.Log = 8] = "Log"; + d[d.Info = 16] = "Info"; + d[d.All = 31] = "All"; + })(c.LogLevel || (c.LogLevel = {})); + l = function() { + function d(b, g) { + void 0 === b && (b = !1); this._tab = " "; this._padding = ""; - this._suppressOutput = c; - this._out = e || a._consoleOut; - this._outNoNewline = e || a._consoleOutNoNewline; - } - a.prototype.write = function(a, c) { - "undefined" === typeof a && (a = ""); - "undefined" === typeof c && (c = !1); - this._suppressOutput || this._outNoNewline((c ? this._padding : "") + a); - }; - a.prototype.writeLn = function(a) { - "undefined" === typeof a && (a = ""); - this._suppressOutput || this._out(this._padding + a); - }; - a.prototype.writeTimeLn = function(a) { - "undefined" === typeof a && (a = ""); - this._suppressOutput || this._out(this._padding + performance.now().toFixed(2) + " " + a); - }; - a.prototype.writeComment = function(a) { - a = a.split("\n"); - if (1 === a.length) { - this.writeLn("// " + a[0]); + this._suppressOutput = b; + this._out = g || d._consoleOut; + this._outNoNewline = g || d._consoleOutNoNewline; + } + d.prototype.write = function(b, g) { + void 0 === b && (b = ""); + void 0 === g && (g = !1); + this._suppressOutput || this._outNoNewline((g ? this._padding : "") + b); + }; + d.prototype.writeLn = function(b) { + void 0 === b && (b = ""); + this._suppressOutput || this._out(this._padding + b); + }; + d.prototype.writeObject = function(b, g) { + void 0 === b && (b = ""); + this._suppressOutput || this._out(this._padding + b, g); + }; + d.prototype.writeTimeLn = function(b) { + void 0 === b && (b = ""); + this._suppressOutput || this._out(this._padding + performance.now().toFixed(2) + " " + b); + }; + d.prototype.writeComment = function(b) { + b = b.split("\n"); + if (1 === b.length) { + this.writeLn("// " + b[0]); } else { this.writeLn("/**"); - for (var c = 0;c < a.length;c++) { - this.writeLn(" * " + a[c]); + for (var g = 0;g < b.length;g++) { + this.writeLn(" * " + b[g]); } this.writeLn(" */"); } }; - a.prototype.writeLns = function(a) { - a = a.split("\n"); - for (var c = 0;c < a.length;c++) { - this.writeLn(a[c]); + d.prototype.writeLns = function(b) { + b = b.split("\n"); + for (var g = 0;g < b.length;g++) { + this.writeLn(b[g]); } }; - a.prototype.errorLn = function(c) { - a.logLevel & 1 && this.boldRedLn(c); + d.prototype.errorLn = function(b) { + d.logLevel & 1 && this.boldRedLn(b); }; - a.prototype.warnLn = function(c) { - a.logLevel & 2 && this.yellowLn(c); + d.prototype.warnLn = function(b) { + d.logLevel & 2 && this.yellowLn(b); }; - a.prototype.debugLn = function(c) { - a.logLevel & 4 && this.purpleLn(c); + d.prototype.debugLn = function(b) { + d.logLevel & 4 && this.purpleLn(b); }; - a.prototype.logLn = function(c) { - a.logLevel & 8 && this.writeLn(c); + d.prototype.logLn = function(b) { + d.logLevel & 8 && this.writeLn(b); }; - a.prototype.infoLn = function(c) { - a.logLevel & 16 && this.writeLn(c); + d.prototype.infoLn = function(b) { + d.logLevel & 16 && this.writeLn(b); }; - a.prototype.yellowLn = function(c) { - this.colorLn(a.YELLOW, c); + d.prototype.yellowLn = function(b) { + this.colorLn(d.YELLOW, b); }; - a.prototype.greenLn = function(c) { - this.colorLn(a.GREEN, c); + d.prototype.greenLn = function(b) { + this.colorLn(d.GREEN, b); }; - a.prototype.boldRedLn = function(c) { - this.colorLn(a.BOLD_RED, c); + d.prototype.boldRedLn = function(b) { + this.colorLn(d.BOLD_RED, b); }; - a.prototype.redLn = function(c) { - this.colorLn(a.RED, c); + d.prototype.redLn = function(b) { + this.colorLn(d.RED, b); }; - a.prototype.purpleLn = function(c) { - this.colorLn(a.PURPLE, c); + d.prototype.purpleLn = function(b) { + this.colorLn(d.PURPLE, b); }; - a.prototype.colorLn = function(c, e) { - this._suppressOutput || (inBrowser ? this._out(this._padding + e) : this._out(this._padding + c + e + a.ENDC)); + d.prototype.colorLn = function(b, g) { + this._suppressOutput || (inBrowser ? this._out(this._padding + g) : this._out(this._padding + b + g + d.ENDC)); }; - a.prototype.redLns = function(c) { - this.colorLns(a.RED, c); + d.prototype.redLns = function(b) { + this.colorLns(d.RED, b); }; - a.prototype.colorLns = function(a, c) { - for (var l = c.split("\n"), h = 0;h < l.length;h++) { - this.colorLn(a, l[h]); + d.prototype.colorLns = function(b, g) { + for (var d = g.split("\n"), f = 0;f < d.length;f++) { + this.colorLn(b, d[f]); } }; - a.prototype.enter = function(a) { - this._suppressOutput || this._out(this._padding + a); + d.prototype.enter = function(b) { + this._suppressOutput || this._out(this._padding + b); this.indent(); }; - a.prototype.leaveAndEnter = function() { + d.prototype.leaveAndEnter = function() { this.leave("Slots"); this.indent(); }; - a.prototype.leave = function(a) { + d.prototype.leave = function(b) { this.outdent(); - this._suppressOutput || this._out(this._padding + a); + !this._suppressOutput && b && this._out(this._padding + b); }; - a.prototype.indent = function() { + d.prototype.indent = function() { this._padding += this._tab; }; - a.prototype.outdent = function() { + d.prototype.outdent = function() { 0 < this._padding.length && (this._padding = this._padding.substring(0, this._padding.length - this._tab.length)); }; - a.prototype.writeArray = function(a, c, l) { - "undefined" === typeof c && (c = !1); - "undefined" === typeof l && (l = !1); - c = c || !1; - for (var h = 0, e = a.length;h < e;h++) { - var q = ""; - c && (q = null === a[h] ? "null" : void 0 === a[h] ? "undefined" : a[h].constructor.name, q += " "); - var d = l ? "" : ("" + h).padRight(" ", 4); - this.writeLn(d + q + a[h]); - } - }; - a.PURPLE = "\u001b[94m"; - a.YELLOW = "\u001b[93m"; - a.GREEN = "\u001b[92m"; - a.RED = "\u001b[91m"; - a.BOLD_RED = "\u001b[1;91m"; - a.ENDC = "\u001b[0m"; - a.logLevel = 31; - a._consoleOut = inBrowser ? console.info.bind(console) : print; - a._consoleOutNoNewline = inBrowser ? console.info.bind(console) : putstr; - return a; + d.prototype.writeArray = function(b, g, d) { + void 0 === g && (g = !1); + void 0 === d && (d = !1); + g = g || !1; + for (var f = 0, k = b.length;f < k;f++) { + var e = ""; + g && (e = null === b[f] ? "null" : void 0 === b[f] ? "undefined" : b[f].constructor.name, e += " "); + var n = d ? "" : ("" + f).padRight(" ", 4); + this.writeLn(n + e + b[f]); + } + }; + d.PURPLE = "\u001b[94m"; + d.YELLOW = "\u001b[93m"; + d.GREEN = "\u001b[92m"; + d.RED = "\u001b[91m"; + d.BOLD_RED = "\u001b[1;91m"; + d.ENDC = "\u001b[0m"; + d.logLevel = 31; + d._consoleOut = console.info.bind(console); + d._consoleOutNoNewline = console.info.bind(console); + return d; }(); - b.IndentingWriter = d; - var p = function() { - return function(a, c) { - this.value = a; - this.next = c; - }; - }(), d = function() { - function a(c) { - s.assert(c); - this._compare = c; + c.IndentingWriter = l; + var n = function() { + return function(d, b) { + this.value = d; + this.next = b; + }; + }(), l = function() { + function d(b) { + p.assert(b); + this._compare = b; this._head = null; this._length = 0; } - a.prototype.push = function(a) { - s.assert(void 0 !== a); + d.prototype.push = function(b) { + p.assert(void 0 !== b); this._length++; if (this._head) { - var c = this._head, l = null; - a = new p(a, null); - for (var h = this._compare;c;) { - if (0 < h(c.value, a.value)) { - l ? (a.next = c, l.next = a) : (a.next = this._head, this._head = a); + var g = this._head, d = null; + b = new n(b, null); + for (var f = this._compare;g;) { + if (0 < f(g.value, b.value)) { + d ? (b.next = g, d.next = b) : (b.next = this._head, this._head = b); return; } - l = c; - c = c.next; + d = g; + g = g.next; } - l.next = a; + d.next = b; } else { - this._head = new p(a, null); + this._head = new n(b, null); } }; - a.prototype.forEach = function(c) { - for (var e = this._head, r = null;e;) { - var h = c(e.value); - if (h === a.RETURN) { + d.prototype.forEach = function(b) { + for (var g = this._head, r = null;g;) { + var f = b(g.value); + if (f === d.RETURN) { break; } else { - h === a.DELETE ? e = r ? r.next = e.next : this._head = this._head.next : (r = e, e = e.next); + f === d.DELETE ? g = r ? r.next = g.next : this._head = this._head.next : (r = g, g = g.next); } } }; - a.prototype.isEmpty = function() { + d.prototype.isEmpty = function() { return!this._head; }; - a.prototype.pop = function() { + d.prototype.pop = function() { if (this._head) { this._length--; - var a = this._head; + var b = this._head; this._head = this._head.next; - return a.value; + return b.value; } }; - a.prototype.contains = function(a) { - for (var c = this._head;c;) { - if (c.value === a) { + d.prototype.contains = function(b) { + for (var g = this._head;g;) { + if (g.value === b) { return!0; } - c = c.next; + g = g.next; } return!1; }; - a.prototype.toString = function() { - for (var a = "[", c = this._head;c;) { - a += c.value.toString(), (c = c.next) && (a += ","); - } - return a + "]"; - }; - a.RETURN = 1; - a.DELETE = 2; - return a; + d.prototype.toString = function() { + for (var b = "[", g = this._head;g;) { + b += g.value.toString(), (g = g.next) && (b += ","); + } + return b + "]"; + }; + d.RETURN = 1; + d.DELETE = 2; + return d; }(); - b.SortedList = d; - d = function() { - function a(c, l) { - "undefined" === typeof l && (l = 12); + c.SortedList = l; + l = function() { + function d(b, g) { + void 0 === g && (g = 12); this.start = this.index = 0; - this._size = 1 << l; + this._size = 1 << g; this._mask = this._size - 1; - this.array = new c(this._size); + this.array = new b(this._size); } - a.prototype.get = function(a) { - return this.array[a]; + d.prototype.get = function(b) { + return this.array[b]; }; - a.prototype.forEachInReverse = function(a) { + d.prototype.forEachInReverse = function(b) { if (!this.isEmpty()) { - for (var c = 0 === this.index ? this._size - 1 : this.index - 1, l = this.start - 1 & this._mask;c !== l && !a(this.array[c], c);) { - c = 0 === c ? this._size - 1 : c - 1; + for (var g = 0 === this.index ? this._size - 1 : this.index - 1, d = this.start - 1 & this._mask;g !== d && !b(this.array[g], g);) { + g = 0 === g ? this._size - 1 : g - 1; } } }; - a.prototype.write = function(a) { - this.array[this.index] = a; + d.prototype.write = function(b) { + this.array[this.index] = b; this.index = this.index + 1 & this._mask; this.index === this.start && (this.start = this.start + 1 & this._mask); }; - a.prototype.isFull = function() { + d.prototype.isFull = function() { return(this.index + 1 & this._mask) === this.start; }; - a.prototype.isEmpty = function() { + d.prototype.isEmpty = function() { return this.index === this.start; }; - a.prototype.reset = function() { + d.prototype.reset = function() { this.start = this.index = 0; }; - return a; + return d; }(); - b.CircularBuffer = d; - (function(a) { - function c(h) { - return h + (a.BITS_PER_WORD - 1) >> a.ADDRESS_BITS_PER_WORD << a.ADDRESS_BITS_PER_WORD; - } - function e(a, h) { - a = a || "1"; - h = h || "0"; - for (var c = "", l = 0;l < length;l++) { - c += this.get(l) ? a : h; - } - return c; - } - function r(a) { - for (var h = [], c = 0;c < length;c++) { - this.get(c) && h.push(a ? a[c] : c); + c.CircularBuffer = l; + (function(d) { + function b(b) { + return b + (d.BITS_PER_WORD - 1) >> d.ADDRESS_BITS_PER_WORD << d.ADDRESS_BITS_PER_WORD; + } + function g(b, g) { + b = b || "1"; + g = g || "0"; + for (var d = "", r = 0;r < length;r++) { + d += this.get(r) ? b : g; } - return h.join(", "); + return d; } - var h = b.Debug.assert; - a.ADDRESS_BITS_PER_WORD = 5; - a.BITS_PER_WORD = 1 << a.ADDRESS_BITS_PER_WORD; - a.BIT_INDEX_MASK = a.BITS_PER_WORD - 1; - var q = function() { - function e(h) { - this.size = c(h); + function r(b) { + for (var g = [], d = 0;d < length;d++) { + this.get(d) && g.push(b ? b[d] : d); + } + return g.join(", "); + } + var f = c.Debug.assert; + d.ADDRESS_BITS_PER_WORD = 5; + d.BITS_PER_WORD = 1 << d.ADDRESS_BITS_PER_WORD; + d.BIT_INDEX_MASK = d.BITS_PER_WORD - 1; + var k = function() { + function g(r) { + this.size = b(r); this.dirty = this.count = 0; - this.length = h; - this.bits = new Uint32Array(this.size >> a.ADDRESS_BITS_PER_WORD); + this.length = r; + this.bits = new Uint32Array(this.size >> d.ADDRESS_BITS_PER_WORD); } - e.prototype.recount = function() { + g.prototype.recount = function() { if (this.dirty) { - for (var a = this.bits, h = 0, c = 0, e = a.length;c < e;c++) { - var l = a[c], l = l - (l >> 1 & 1431655765), l = (l & 858993459) + (l >> 2 & 858993459), h = h + (16843009 * (l + (l >> 4) & 252645135) >> 24) + for (var b = this.bits, g = 0, d = 0, r = b.length;d < r;d++) { + var f = b[d], f = f - (f >> 1 & 1431655765), f = (f & 858993459) + (f >> 2 & 858993459), g = g + (16843009 * (f + (f >> 4) & 252645135) >> 24) } - this.count = h; + this.count = g; this.dirty = 0; } }; - e.prototype.set = function(h) { - var c = h >> a.ADDRESS_BITS_PER_WORD, e = this.bits[c]; - h = e | 1 << (h & a.BIT_INDEX_MASK); - this.bits[c] = h; - this.dirty |= e ^ h; - }; - e.prototype.setAll = function() { - for (var a = this.bits, h = 0, c = a.length;h < c;h++) { - a[h] = 4294967295; + g.prototype.set = function(b) { + var g = b >> d.ADDRESS_BITS_PER_WORD, r = this.bits[g]; + b = r | 1 << (b & d.BIT_INDEX_MASK); + this.bits[g] = b; + this.dirty |= r ^ b; + }; + g.prototype.setAll = function() { + for (var b = this.bits, g = 0, d = b.length;g < d;g++) { + b[g] = 4294967295; } this.count = this.size; this.dirty = 0; }; - e.prototype.assign = function(a) { - this.count = a.count; - this.dirty = a.dirty; - this.size = a.size; - for (var h = 0, c = this.bits.length;h < c;h++) { - this.bits[h] = a.bits[h]; - } - }; - e.prototype.clear = function(h) { - var c = h >> a.ADDRESS_BITS_PER_WORD, e = this.bits[c]; - h = e & ~(1 << (h & a.BIT_INDEX_MASK)); - this.bits[c] = h; - this.dirty |= e ^ h; - }; - e.prototype.get = function(h) { - return 0 !== (this.bits[h >> a.ADDRESS_BITS_PER_WORD] & 1 << (h & a.BIT_INDEX_MASK)); - }; - e.prototype.clearAll = function() { - for (var a = this.bits, h = 0, c = a.length;h < c;h++) { - a[h] = 0; + g.prototype.assign = function(b) { + this.count = b.count; + this.dirty = b.dirty; + this.size = b.size; + for (var g = 0, d = this.bits.length;g < d;g++) { + this.bits[g] = b.bits[g]; + } + }; + g.prototype.clear = function(b) { + var g = b >> d.ADDRESS_BITS_PER_WORD, r = this.bits[g]; + b = r & ~(1 << (b & d.BIT_INDEX_MASK)); + this.bits[g] = b; + this.dirty |= r ^ b; + }; + g.prototype.get = function(b) { + return 0 !== (this.bits[b >> d.ADDRESS_BITS_PER_WORD] & 1 << (b & d.BIT_INDEX_MASK)); + }; + g.prototype.clearAll = function() { + for (var b = this.bits, g = 0, d = b.length;g < d;g++) { + b[g] = 0; } this.dirty = this.count = 0; }; - e.prototype._union = function(a) { - var h = this.dirty, c = this.bits; - a = a.bits; - for (var e = 0, l = c.length;e < l;e++) { - var r = c[e], q = r | a[e]; - c[e] = q; - h |= r ^ q; - } - this.dirty = h; - }; - e.prototype.intersect = function(a) { - var h = this.dirty, c = this.bits; - a = a.bits; - for (var e = 0, l = c.length;e < l;e++) { - var r = c[e], q = r & a[e]; - c[e] = q; - h |= r ^ q; - } - this.dirty = h; - }; - e.prototype.subtract = function(a) { - var h = this.dirty, c = this.bits; - a = a.bits; - for (var e = 0, l = c.length;e < l;e++) { - var r = c[e], q = r & ~a[e]; - c[e] = q; - h |= r ^ q; - } - this.dirty = h; - }; - e.prototype.negate = function() { - for (var a = this.dirty, h = this.bits, c = 0, e = h.length;c < e;c++) { - var l = h[c], r = ~l; - h[c] = r; - a |= l ^ r; - } - this.dirty = a; - }; - e.prototype.forEach = function(c) { - h(c); - for (var e = this.bits, r = 0, q = e.length;r < q;r++) { - var u = e[r]; - if (u) { - for (var d = 0;d < a.BITS_PER_WORD;d++) { - u & 1 << d && c(r * a.BITS_PER_WORD + d); + g.prototype._union = function(b) { + var g = this.dirty, d = this.bits; + b = b.bits; + for (var r = 0, f = d.length;r < f;r++) { + var k = d[r], w = k | b[r]; + d[r] = w; + g |= k ^ w; + } + this.dirty = g; + }; + g.prototype.intersect = function(b) { + var g = this.dirty, d = this.bits; + b = b.bits; + for (var r = 0, f = d.length;r < f;r++) { + var k = d[r], w = k & b[r]; + d[r] = w; + g |= k ^ w; + } + this.dirty = g; + }; + g.prototype.subtract = function(b) { + var g = this.dirty, d = this.bits; + b = b.bits; + for (var r = 0, f = d.length;r < f;r++) { + var k = d[r], w = k & ~b[r]; + d[r] = w; + g |= k ^ w; + } + this.dirty = g; + }; + g.prototype.negate = function() { + for (var b = this.dirty, g = this.bits, d = 0, r = g.length;d < r;d++) { + var f = g[d], k = ~f; + g[d] = k; + b |= f ^ k; + } + this.dirty = b; + }; + g.prototype.forEach = function(b) { + f(b); + for (var g = this.bits, r = 0, k = g.length;r < k;r++) { + var e = g[r]; + if (e) { + for (var n = 0;n < d.BITS_PER_WORD;n++) { + e & 1 << n && b(r * d.BITS_PER_WORD + n); } } } }; - e.prototype.toArray = function() { - for (var h = [], c = this.bits, e = 0, r = c.length;e < r;e++) { - var q = c[e]; - if (q) { - for (var u = 0;u < a.BITS_PER_WORD;u++) { - q & 1 << u && h.push(e * a.BITS_PER_WORD + u); + g.prototype.toArray = function() { + for (var b = [], g = this.bits, r = 0, f = g.length;r < f;r++) { + var k = g[r]; + if (k) { + for (var w = 0;w < d.BITS_PER_WORD;w++) { + k & 1 << w && b.push(r * d.BITS_PER_WORD + w); } } } - return h; + return b; }; - e.prototype.equals = function(a) { - if (this.size !== a.size) { + g.prototype.equals = function(b) { + if (this.size !== b.size) { return!1; } - var h = this.bits; - a = a.bits; - for (var c = 0, e = h.length;c < e;c++) { - if (h[c] !== a[c]) { + var g = this.bits; + b = b.bits; + for (var d = 0, r = g.length;d < r;d++) { + if (g[d] !== b[d]) { return!1; } } return!0; }; - e.prototype.contains = function(a) { - if (this.size !== a.size) { + g.prototype.contains = function(b) { + if (this.size !== b.size) { return!1; } - var h = this.bits; - a = a.bits; - for (var c = 0, e = h.length;c < e;c++) { - if ((h[c] | a[c]) !== h[c]) { + var g = this.bits; + b = b.bits; + for (var d = 0, r = g.length;d < r;d++) { + if ((g[d] | b[d]) !== g[d]) { return!1; } } return!0; }; - e.prototype.isEmpty = function() { + g.prototype.isEmpty = function() { this.recount(); return 0 === this.count; }; - e.prototype.clone = function() { - var a = new e(this.length); - a._union(this); - return a; + g.prototype.clone = function() { + var b = new g(this.length); + b._union(this); + return b; }; - return e; + return g; }(); - a.Uint32ArrayBitSet = q; - var d = function() { - function e(a) { + d.Uint32ArrayBitSet = k; + var e = function() { + function g(d) { this.dirty = this.count = 0; - this.size = c(a); + this.size = b(d); this.bits = 0; this.singleWord = !0; - this.length = a; + this.length = d; } - e.prototype.recount = function() { + g.prototype.recount = function() { if (this.dirty) { - var a = this.bits, a = a - (a >> 1 & 1431655765), a = (a & 858993459) + (a >> 2 & 858993459); - this.count = 0 + (16843009 * (a + (a >> 4) & 252645135) >> 24); + var b = this.bits, b = b - (b >> 1 & 1431655765), b = (b & 858993459) + (b >> 2 & 858993459); + this.count = 0 + (16843009 * (b + (b >> 4) & 252645135) >> 24); this.dirty = 0; } }; - e.prototype.set = function(h) { - var c = this.bits; - this.bits = h = c | 1 << (h & a.BIT_INDEX_MASK); - this.dirty |= c ^ h; + g.prototype.set = function(b) { + var g = this.bits; + this.bits = b = g | 1 << (b & d.BIT_INDEX_MASK); + this.dirty |= g ^ b; }; - e.prototype.setAll = function() { + g.prototype.setAll = function() { this.bits = 4294967295; this.count = this.size; this.dirty = 0; }; - e.prototype.assign = function(a) { - this.count = a.count; - this.dirty = a.dirty; - this.size = a.size; - this.bits = a.bits; - }; - e.prototype.clear = function(h) { - var c = this.bits; - this.bits = h = c & ~(1 << (h & a.BIT_INDEX_MASK)); - this.dirty |= c ^ h; + g.prototype.assign = function(b) { + this.count = b.count; + this.dirty = b.dirty; + this.size = b.size; + this.bits = b.bits; + }; + g.prototype.clear = function(b) { + var g = this.bits; + this.bits = b = g & ~(1 << (b & d.BIT_INDEX_MASK)); + this.dirty |= g ^ b; }; - e.prototype.get = function(h) { - return 0 !== (this.bits & 1 << (h & a.BIT_INDEX_MASK)); + g.prototype.get = function(b) { + return 0 !== (this.bits & 1 << (b & d.BIT_INDEX_MASK)); }; - e.prototype.clearAll = function() { + g.prototype.clearAll = function() { this.dirty = this.count = this.bits = 0; }; - e.prototype._union = function(a) { - var h = this.bits; - this.bits = a = h | a.bits; - this.dirty = h ^ a; - }; - e.prototype.intersect = function(a) { - var h = this.bits; - this.bits = a = h & a.bits; - this.dirty = h ^ a; - }; - e.prototype.subtract = function(a) { - var h = this.bits; - this.bits = a = h & ~a.bits; - this.dirty = h ^ a; - }; - e.prototype.negate = function() { - var a = this.bits, h = ~a; - this.bits = h; - this.dirty = a ^ h; - }; - e.prototype.forEach = function(c) { - h(c); - var e = this.bits; - if (e) { - for (var r = 0;r < a.BITS_PER_WORD;r++) { - e & 1 << r && c(r); + g.prototype._union = function(b) { + var g = this.bits; + this.bits = b = g | b.bits; + this.dirty = g ^ b; + }; + g.prototype.intersect = function(b) { + var g = this.bits; + this.bits = b = g & b.bits; + this.dirty = g ^ b; + }; + g.prototype.subtract = function(b) { + var g = this.bits; + this.bits = b = g & ~b.bits; + this.dirty = g ^ b; + }; + g.prototype.negate = function() { + var b = this.bits, g = ~b; + this.bits = g; + this.dirty = b ^ g; + }; + g.prototype.forEach = function(b) { + f(b); + var g = this.bits; + if (g) { + for (var r = 0;r < d.BITS_PER_WORD;r++) { + g & 1 << r && b(r); } } }; - e.prototype.toArray = function() { - var h = [], c = this.bits; - if (c) { - for (var e = 0;e < a.BITS_PER_WORD;e++) { - c & 1 << e && h.push(e); + g.prototype.toArray = function() { + var b = [], g = this.bits; + if (g) { + for (var r = 0;r < d.BITS_PER_WORD;r++) { + g & 1 << r && b.push(r); } } - return h; + return b; }; - e.prototype.equals = function(a) { - return this.bits === a.bits; + g.prototype.equals = function(b) { + return this.bits === b.bits; }; - e.prototype.contains = function(a) { - var h = this.bits; - return(h | a.bits) === h; + g.prototype.contains = function(b) { + var g = this.bits; + return(g | b.bits) === g; }; - e.prototype.isEmpty = function() { + g.prototype.isEmpty = function() { this.recount(); return 0 === this.count; }; - e.prototype.clone = function() { - var a = new e(this.length); - a._union(this); - return a; + g.prototype.clone = function() { + var b = new g(this.length); + b._union(this); + return b; }; - return e; + return g; }(); - a.Uint32BitSet = d; - d.prototype.toString = r; - d.prototype.toBitString = e; - q.prototype.toString = r; - q.prototype.toBitString = e; - a.BitSetFunctor = function(h) { - var e = 1 === c(h) >> a.ADDRESS_BITS_PER_WORD ? d : q; + d.Uint32BitSet = e; + e.prototype.toString = r; + e.prototype.toBitString = g; + k.prototype.toString = r; + k.prototype.toBitString = g; + d.BitSetFunctor = function(g) { + var r = 1 === b(g) >> d.ADDRESS_BITS_PER_WORD ? e : k; return function() { - return new e(h); + return new r(g); }; }; - })(b.BitSets || (b.BitSets = {})); - d = function() { - function a() { - } - a.randomStyle = function() { - a._randomStyleCache || (a._randomStyleCache = "#ff5e3a #ff9500 #ffdb4c #87fc70 #52edc7 #1ad6fd #c644fc #ef4db6 #4a4a4a #dbddde #ff3b30 #ff9500 #ffcc00 #4cd964 #34aadc #007aff #5856d6 #ff2d55 #8e8e93 #c7c7cc #5ad427 #c86edf #d1eefc #e0f8d8 #fb2b69 #f7f7f7 #1d77ef #d6cec3 #55efcb #ff4981 #ffd3e0 #f7f7f7 #ff1300 #1f1f21 #bdbec2 #ff3a2d".split(" ")); - return a._randomStyleCache[a._nextStyle++ % a._randomStyleCache.length]; - }; - a.contrastStyle = function(a) { - a = parseInt(a.substr(1), 16); - return 128 <= (299 * (a >> 16) + 587 * (a >> 8 & 255) + 114 * (a & 255)) / 1E3 ? "#000000" : "#ffffff"; - }; - a.reset = function() { - a._nextStyle = 0; - }; - a.TabToolbar = "#252c33"; - a.Toolbars = "#343c45"; - a.HighlightBlue = "#1d4f73"; - a.LightText = "#f5f7fa"; - a.ForegroundText = "#b6babf"; - a.Black = "#000000"; - a.VeryDark = "#14171a"; - a.Dark = "#181d20"; - a.Light = "#a9bacb"; - a.Grey = "#8fa1b2"; - a.DarkGrey = "#5f7387"; - a.Blue = "#46afe3"; - a.Purple = "#6b7abb"; - a.Pink = "#df80ff"; - a.Red = "#eb5368"; - a.Orange = "#d96629"; - a.LightOrange = "#d99b28"; - a.Green = "#70bf53"; - a.BlueGrey = "#5e88b0"; - a._nextStyle = 0; - return a; + })(c.BitSets || (c.BitSets = {})); + l = function() { + function d() { + } + d.randomStyle = function() { + d._randomStyleCache || (d._randomStyleCache = "#ff5e3a #ff9500 #ffdb4c #87fc70 #52edc7 #1ad6fd #c644fc #ef4db6 #4a4a4a #dbddde #ff3b30 #ff9500 #ffcc00 #4cd964 #34aadc #007aff #5856d6 #ff2d55 #8e8e93 #c7c7cc #5ad427 #c86edf #d1eefc #e0f8d8 #fb2b69 #f7f7f7 #1d77ef #d6cec3 #55efcb #ff4981 #ffd3e0 #f7f7f7 #ff1300 #1f1f21 #bdbec2 #ff3a2d".split(" ")); + return d._randomStyleCache[d._nextStyle++ % d._randomStyleCache.length]; + }; + d.gradientColor = function(b) { + return d._gradient[d._gradient.length * t.clamp(b, 0, 1) | 0]; + }; + d.contrastStyle = function(b) { + b = parseInt(b.substr(1), 16); + return 128 <= (299 * (b >> 16) + 587 * (b >> 8 & 255) + 114 * (b & 255)) / 1E3 ? "#000000" : "#ffffff"; + }; + d.reset = function() { + d._nextStyle = 0; + }; + d.TabToolbar = "#252c33"; + d.Toolbars = "#343c45"; + d.HighlightBlue = "#1d4f73"; + d.LightText = "#f5f7fa"; + d.ForegroundText = "#b6babf"; + d.Black = "#000000"; + d.VeryDark = "#14171a"; + d.Dark = "#181d20"; + d.Light = "#a9bacb"; + d.Grey = "#8fa1b2"; + d.DarkGrey = "#5f7387"; + d.Blue = "#46afe3"; + d.Purple = "#6b7abb"; + d.Pink = "#df80ff"; + d.Red = "#eb5368"; + d.Orange = "#d96629"; + d.LightOrange = "#d99b28"; + d.Green = "#70bf53"; + d.BlueGrey = "#5e88b0"; + d._nextStyle = 0; + d._gradient = "#FF0000 #FF1100 #FF2300 #FF3400 #FF4600 #FF5700 #FF6900 #FF7B00 #FF8C00 #FF9E00 #FFAF00 #FFC100 #FFD300 #FFE400 #FFF600 #F7FF00 #E5FF00 #D4FF00 #C2FF00 #B0FF00 #9FFF00 #8DFF00 #7CFF00 #6AFF00 #58FF00 #47FF00 #35FF00 #24FF00 #12FF00 #00FF00".split(" "); + return d; }(); - b.ColorStyle = d; - d = function() { - function a(c, e, l, h) { - this.xMin = c | 0; - this.yMin = e | 0; - this.xMax = l | 0; - this.yMax = h | 0; - } - a.FromUntyped = function(c) { - return new a(c.xMin, c.yMin, c.xMax, c.yMax); - }; - a.FromRectangle = function(c) { - return new a(20 * c.x | 0, 20 * c.y | 0, 20 * (c.x + c.width) | 0, 20 * (c.y + c.height) | 0); - }; - a.prototype.setElements = function(a, c, e, h) { - this.xMin = a; - this.yMin = c; - this.xMax = e; - this.yMax = h; - }; - a.prototype.copyFrom = function(a) { - this.setElements(a.xMin, a.yMin, a.xMax, a.yMax); - }; - a.prototype.contains = function(a, c) { - return a < this.xMin !== a < this.xMax && c < this.yMin !== c < this.yMax; - }; - a.prototype.unionInPlace = function(a) { - this.xMin = Math.min(this.xMin, a.xMin); - this.yMin = Math.min(this.yMin, a.yMin); - this.xMax = Math.max(this.xMax, a.xMax); - this.yMax = Math.max(this.yMax, a.yMax); - }; - a.prototype.extendByPoint = function(a, c) { - this.extendByX(a); - this.extendByY(c); - }; - a.prototype.extendByX = function(a) { - 134217728 === this.xMin ? this.xMin = this.xMax = a : (this.xMin = Math.min(this.xMin, a), this.xMax = Math.max(this.xMax, a)); + c.ColorStyle = l; + l = function() { + function d(b, g, d, f) { + this.xMin = b | 0; + this.yMin = g | 0; + this.xMax = d | 0; + this.yMax = f | 0; + } + d.FromUntyped = function(b) { + return new d(b.xMin, b.yMin, b.xMax, b.yMax); + }; + d.FromRectangle = function(b) { + return new d(20 * b.x | 0, 20 * b.y | 0, 20 * (b.x + b.width) | 0, 20 * (b.y + b.height) | 0); + }; + d.prototype.setElements = function(b, g, d, f) { + this.xMin = b; + this.yMin = g; + this.xMax = d; + this.yMax = f; + }; + d.prototype.copyFrom = function(b) { + this.setElements(b.xMin, b.yMin, b.xMax, b.yMax); + }; + d.prototype.contains = function(b, g) { + return b < this.xMin !== b < this.xMax && g < this.yMin !== g < this.yMax; + }; + d.prototype.unionInPlace = function(b) { + b.isEmpty() || (this.extendByPoint(b.xMin, b.yMin), this.extendByPoint(b.xMax, b.yMax)); }; - a.prototype.extendByY = function(a) { - 134217728 === this.yMin ? this.yMin = this.yMax = a : (this.yMin = Math.min(this.yMin, a), this.yMax = Math.max(this.yMax, a)); + d.prototype.extendByPoint = function(b, g) { + this.extendByX(b); + this.extendByY(g); }; - a.prototype.intersects = function(a) { - return this.contains(a.xMin, a.yMin) || this.contains(a.xMax, a.yMax); + d.prototype.extendByX = function(b) { + 134217728 === this.xMin ? this.xMin = this.xMax = b : (this.xMin = Math.min(this.xMin, b), this.xMax = Math.max(this.xMax, b)); }; - a.prototype.isEmpty = function() { + d.prototype.extendByY = function(b) { + 134217728 === this.yMin ? this.yMin = this.yMax = b : (this.yMin = Math.min(this.yMin, b), this.yMax = Math.max(this.yMax, b)); + }; + d.prototype.intersects = function(b) { + return this.contains(b.xMin, b.yMin) || this.contains(b.xMax, b.yMax); + }; + d.prototype.isEmpty = function() { return this.xMax <= this.xMin || this.yMax <= this.yMin; }; - Object.defineProperty(a.prototype, "width", {get:function() { + Object.defineProperty(d.prototype, "width", {get:function() { return this.xMax - this.xMin; - }, set:function(a) { - this.xMax = this.xMin + a; + }, set:function(b) { + this.xMax = this.xMin + b; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "height", {get:function() { + Object.defineProperty(d.prototype, "height", {get:function() { return this.yMax - this.yMin; - }, set:function(a) { - this.yMax = this.yMin + a; + }, set:function(b) { + this.yMax = this.yMin + b; }, enumerable:!0, configurable:!0}); - a.prototype.getBaseWidth = function(a) { - return Math.abs(Math.cos(a)) * (this.xMax - this.xMin) + Math.abs(Math.sin(a)) * (this.yMax - this.yMin); + d.prototype.getBaseWidth = function(b) { + return Math.abs(Math.cos(b)) * (this.xMax - this.xMin) + Math.abs(Math.sin(b)) * (this.yMax - this.yMin); }; - a.prototype.getBaseHeight = function(a) { - return Math.abs(Math.sin(a)) * (this.xMax - this.xMin) + Math.abs(Math.cos(a)) * (this.yMax - this.yMin); + d.prototype.getBaseHeight = function(b) { + return Math.abs(Math.sin(b)) * (this.xMax - this.xMin) + Math.abs(Math.cos(b)) * (this.yMax - this.yMin); }; - a.prototype.setEmpty = function() { + d.prototype.setEmpty = function() { this.xMin = this.yMin = this.xMax = this.yMax = 0; }; - a.prototype.setToSentinels = function() { + d.prototype.setToSentinels = function() { this.xMin = this.yMin = this.xMax = this.yMax = 134217728; }; - a.prototype.clone = function() { - return new a(this.xMin, this.yMin, this.xMax, this.yMax); + d.prototype.clone = function() { + return new d(this.xMin, this.yMin, this.xMax, this.yMax); }; - a.prototype.toString = function() { + d.prototype.toString = function() { return "{ xMin: " + this.xMin + ", xMin: " + this.yMin + ", xMax: " + this.xMax + ", xMax: " + this.yMax + " }"; }; - return a; + return d; }(); - b.Bounds = d; - d = function() { - function a(c, e, l, h) { - s.assert(k(c)); - s.assert(k(e)); - s.assert(k(l)); - s.assert(k(h)); - this._xMin = c | 0; - this._yMin = e | 0; - this._xMax = l | 0; - this._yMax = h | 0; - } - a.FromUntyped = function(c) { - return new a(c.xMin, c.yMin, c.xMax, c.yMax); - }; - a.FromRectangle = function(c) { - return new a(20 * c.x | 0, 20 * c.y | 0, 20 * (c.x + c.width) | 0, 20 * (c.y + c.height) | 0); - }; - a.prototype.setElements = function(a, c, e, h) { - this.xMin = a; - this.yMin = c; - this.xMax = e; - this.yMax = h; - }; - a.prototype.copyFrom = function(a) { - this.setElements(a.xMin, a.yMin, a.xMax, a.yMax); - }; - a.prototype.contains = function(a, c) { - return a < this.xMin !== a < this.xMax && c < this.yMin !== c < this.yMax; - }; - a.prototype.unionWith = function(a) { - this._xMin = Math.min(this._xMin, a._xMin); - this._yMin = Math.min(this._yMin, a._yMin); - this._xMax = Math.max(this._xMax, a._xMax); - this._yMax = Math.max(this._yMax, a._yMax); - }; - a.prototype.extendByPoint = function(a, c) { - this.extendByX(a); - this.extendByY(c); - }; - a.prototype.extendByX = function(a) { - 134217728 === this.xMin ? this.xMin = this.xMax = a : (this.xMin = Math.min(this.xMin, a), this.xMax = Math.max(this.xMax, a)); + c.Bounds = l; + l = function() { + function d(b, g, d, f) { + p.assert(h(b)); + p.assert(h(g)); + p.assert(h(d)); + p.assert(h(f)); + this._xMin = b | 0; + this._yMin = g | 0; + this._xMax = d | 0; + this._yMax = f | 0; + } + d.FromUntyped = function(b) { + return new d(b.xMin, b.yMin, b.xMax, b.yMax); + }; + d.FromRectangle = function(b) { + return new d(20 * b.x | 0, 20 * b.y | 0, 20 * (b.x + b.width) | 0, 20 * (b.y + b.height) | 0); + }; + d.prototype.setElements = function(b, g, d, f) { + this.xMin = b; + this.yMin = g; + this.xMax = d; + this.yMax = f; + }; + d.prototype.copyFrom = function(b) { + this.setElements(b.xMin, b.yMin, b.xMax, b.yMax); + }; + d.prototype.contains = function(b, g) { + return b < this.xMin !== b < this.xMax && g < this.yMin !== g < this.yMax; + }; + d.prototype.unionInPlace = function(b) { + b.isEmpty() || (this.extendByPoint(b.xMin, b.yMin), this.extendByPoint(b.xMax, b.yMax)); + }; + d.prototype.extendByPoint = function(b, g) { + this.extendByX(b); + this.extendByY(g); + }; + d.prototype.extendByX = function(b) { + 134217728 === this.xMin ? this.xMin = this.xMax = b : (this.xMin = Math.min(this.xMin, b), this.xMax = Math.max(this.xMax, b)); }; - a.prototype.extendByY = function(a) { - 134217728 === this.yMin ? this.yMin = this.yMax = a : (this.yMin = Math.min(this.yMin, a), this.yMax = Math.max(this.yMax, a)); + d.prototype.extendByY = function(b) { + 134217728 === this.yMin ? this.yMin = this.yMax = b : (this.yMin = Math.min(this.yMin, b), this.yMax = Math.max(this.yMax, b)); }; - a.prototype.intersects = function(a) { - return this.contains(a._xMin, a._yMin) || this.contains(a._xMax, a._yMax); + d.prototype.intersects = function(b) { + return this.contains(b._xMin, b._yMin) || this.contains(b._xMax, b._yMax); }; - a.prototype.isEmpty = function() { + d.prototype.isEmpty = function() { return this._xMax <= this._xMin || this._yMax <= this._yMin; }; - Object.defineProperty(a.prototype, "xMin", {get:function() { + Object.defineProperty(d.prototype, "xMin", {get:function() { return this._xMin; - }, set:function(a) { - s.assert(k(a)); - this._xMin = a; + }, set:function(b) { + p.assert(h(b)); + this._xMin = b; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "yMin", {get:function() { + Object.defineProperty(d.prototype, "yMin", {get:function() { return this._yMin; - }, set:function(a) { - s.assert(k(a)); - this._yMin = a | 0; + }, set:function(b) { + p.assert(h(b)); + this._yMin = b | 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "xMax", {get:function() { + Object.defineProperty(d.prototype, "xMax", {get:function() { return this._xMax; - }, set:function(a) { - s.assert(k(a)); - this._xMax = a | 0; + }, set:function(b) { + p.assert(h(b)); + this._xMax = b | 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "width", {get:function() { + Object.defineProperty(d.prototype, "width", {get:function() { return this._xMax - this._xMin; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "yMax", {get:function() { + Object.defineProperty(d.prototype, "yMax", {get:function() { return this._yMax; - }, set:function(a) { - s.assert(k(a)); - this._yMax = a | 0; + }, set:function(b) { + p.assert(h(b)); + this._yMax = b | 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "height", {get:function() { + Object.defineProperty(d.prototype, "height", {get:function() { return this._yMax - this._yMin; }, enumerable:!0, configurable:!0}); - a.prototype.getBaseWidth = function(a) { - return Math.abs(Math.cos(a)) * (this._xMax - this._xMin) + Math.abs(Math.sin(a)) * (this._yMax - this._yMin); + d.prototype.getBaseWidth = function(b) { + return Math.abs(Math.cos(b)) * (this._xMax - this._xMin) + Math.abs(Math.sin(b)) * (this._yMax - this._yMin); }; - a.prototype.getBaseHeight = function(a) { - return Math.abs(Math.sin(a)) * (this._xMax - this._xMin) + Math.abs(Math.cos(a)) * (this._yMax - this._yMin); + d.prototype.getBaseHeight = function(b) { + return Math.abs(Math.sin(b)) * (this._xMax - this._xMin) + Math.abs(Math.cos(b)) * (this._yMax - this._yMin); }; - a.prototype.setEmpty = function() { + d.prototype.setEmpty = function() { this._xMin = this._yMin = this._xMax = this._yMax = 0; }; - a.prototype.clone = function() { - return new a(this.xMin, this.yMin, this.xMax, this.yMax); + d.prototype.clone = function() { + return new d(this.xMin, this.yMin, this.xMax, this.yMax); }; - a.prototype.toString = function() { + d.prototype.toString = function() { return "{ xMin: " + this._xMin + ", xMin: " + this._yMin + ", xMax: " + this._xMax + ", yMax: " + this._yMax + " }"; }; - a.prototype.assertValid = function() { + d.prototype.assertValid = function() { }; - return a; + return d; }(); - b.DebugBounds = d; - d = function() { - function a(c, e, l, h) { - this.r = c; - this.g = e; - this.b = l; - this.a = h; + c.DebugBounds = l; + l = function() { + function d(b, g, d, f) { + this.r = b; + this.g = g; + this.b = d; + this.a = f; } - a.FromARGB = function(c) { - return new a((c >> 16 & 255) / 255, (c >> 8 & 255) / 255, (c >> 0 & 255) / 255, (c >> 24 & 255) / 255); + d.FromARGB = function(b) { + return new d((b >> 16 & 255) / 255, (b >> 8 & 255) / 255, (b >> 0 & 255) / 255, (b >> 24 & 255) / 255); }; - a.FromRGBA = function(c) { - return a.FromARGB(e.RGBAToARGB(c)); + d.FromRGBA = function(b) { + return d.FromARGB(k.RGBAToARGB(b)); }; - a.prototype.toRGBA = function() { + d.prototype.toRGBA = function() { return 255 * this.r << 24 | 255 * this.g << 16 | 255 * this.b << 8 | 255 * this.a; }; - a.prototype.toCSSStyle = function() { - return e.rgbaToCSSStyle(this.toRGBA()); + d.prototype.toCSSStyle = function() { + return k.rgbaToCSSStyle(this.toRGBA()); }; - a.prototype.set = function(a) { - this.r = a.r; - this.g = a.g; - this.b = a.b; - this.a = a.a; - }; - a.randomColor = function(c) { - "undefined" === typeof c && (c = 1); - return new a(Math.random(), Math.random(), Math.random(), c); - }; - a.parseColor = function(c) { - a.colorCache || (a.colorCache = Object.create(null)); - if (a.colorCache[c]) { - return a.colorCache[c]; - } - var e = document.createElement("span"); - document.body.appendChild(e); - e.style.backgroundColor = c; - var r = getComputedStyle(e).backgroundColor; - document.body.removeChild(e); - (e = /^rgb\((\d+), (\d+), (\d+)\)$/.exec(r)) || (e = /^rgba\((\d+), (\d+), (\d+), ([\d.]+)\)$/.exec(r)); - r = new a(0, 0, 0, 0); - r.r = parseFloat(e[1]) / 255; - r.g = parseFloat(e[2]) / 255; - r.b = parseFloat(e[3]) / 255; - r.a = e[4] ? parseFloat(e[4]) / 255 : 1; - return a.colorCache[c] = r; - }; - a.Red = new a(1, 0, 0, 1); - a.Green = new a(0, 1, 0, 1); - a.Blue = new a(0, 0, 1, 1); - a.None = new a(0, 0, 0, 0); - a.White = new a(1, 1, 1, 1); - a.Black = new a(0, 0, 0, 1); - a.colorCache = {}; - return a; + d.prototype.set = function(b) { + this.r = b.r; + this.g = b.g; + this.b = b.b; + this.a = b.a; + }; + d.randomColor = function(b) { + void 0 === b && (b = 1); + return new d(Math.random(), Math.random(), Math.random(), b); + }; + d.parseColor = function(b) { + d.colorCache || (d.colorCache = Object.create(null)); + if (d.colorCache[b]) { + return d.colorCache[b]; + } + var g = document.createElement("span"); + document.body.appendChild(g); + g.style.backgroundColor = b; + var r = getComputedStyle(g).backgroundColor; + document.body.removeChild(g); + (g = /^rgb\((\d+), (\d+), (\d+)\)$/.exec(r)) || (g = /^rgba\((\d+), (\d+), (\d+), ([\d.]+)\)$/.exec(r)); + r = new d(0, 0, 0, 0); + r.r = parseFloat(g[1]) / 255; + r.g = parseFloat(g[2]) / 255; + r.b = parseFloat(g[3]) / 255; + r.a = g[4] ? parseFloat(g[4]) / 255 : 1; + return d.colorCache[b] = r; + }; + d.Red = new d(1, 0, 0, 1); + d.Green = new d(0, 1, 0, 1); + d.Blue = new d(0, 0, 1, 1); + d.None = new d(0, 0, 0, 0); + d.White = new d(1, 1, 1, 1); + d.Black = new d(0, 0, 0, 1); + d.colorCache = {}; + return d; }(); - b.Color = d; - (function(a) { - function c(a) { - var e, l, r = a >> 24 & 255; - l = (Math.imul(a >> 16 & 255, r) + 127) / 255 | 0; - e = (Math.imul(a >> 8 & 255, r) + 127) / 255 | 0; - a = (Math.imul(a >> 0 & 255, r) + 127) / 255 | 0; - return r << 24 | l << 16 | e << 8 | a; - } - a.RGBAToARGB = function(a) { - return a >> 8 & 16777215 | (a & 255) << 24; - }; - a.ARGBToRGBA = function(a) { - return a << 8 | a >> 24 & 255; - }; - a.rgbaToCSSStyle = function(a) { - return b.StringUtilities.concat9("rgba(", a >> 24 & 255, ",", a >> 16 & 255, ",", a >> 8 & 255, ",", (a & 255) / 255, ")"); - }; - a.cssStyleToRGBA = function(a) { - if ("#" === a[0]) { - if (7 === a.length) { - return parseInt(a.substring(1), 16) << 8 | 255; + c.Color = l; + var k; + (function(d) { + function b(b) { + var g, d, r = b >> 24 & 255; + d = (Math.imul(b >> 16 & 255, r) + 127) / 255 | 0; + g = (Math.imul(b >> 8 & 255, r) + 127) / 255 | 0; + b = (Math.imul(b >> 0 & 255, r) + 127) / 255 | 0; + return r << 24 | d << 16 | g << 8 | b; + } + d.RGBAToARGB = function(b) { + return b >> 8 & 16777215 | (b & 255) << 24; + }; + d.ARGBToRGBA = function(b) { + return b << 8 | b >> 24 & 255; + }; + d.rgbaToCSSStyle = function(b) { + return c.StringUtilities.concat9("rgba(", b >> 24 & 255, ",", b >> 16 & 255, ",", b >> 8 & 255, ",", (b & 255) / 255, ")"); + }; + d.cssStyleToRGBA = function(b) { + if ("#" === b[0]) { + if (7 === b.length) { + return parseInt(b.substring(1), 16) << 8 | 255; } } else { - if ("r" === a[0]) { - return a = a.substring(5, a.length - 1).split(","), (parseInt(a[0]) & 255) << 24 | (parseInt(a[1]) & 255) << 16 | (parseInt(a[2]) & 255) << 8 | 255 * parseFloat(a[3]) & 255; + if ("r" === b[0]) { + return b = b.substring(5, b.length - 1).split(","), (parseInt(b[0]) & 255) << 24 | (parseInt(b[1]) & 255) << 16 | (parseInt(b[2]) & 255) << 8 | 255 * parseFloat(b[3]) & 255; } } return 4278190335; }; - a.hexToRGB = function(a) { - return parseInt(a.slice(1), 16); + d.hexToRGB = function(b) { + return parseInt(b.slice(1), 16); }; - a.rgbToHex = function(a) { - return "#" + ("000000" + a.toString(16)).slice(-6); + d.rgbToHex = function(b) { + return "#" + ("000000" + (b >>> 0).toString(16)).slice(-6); }; - a.isValidHexColor = function(a) { - return/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(a); + d.isValidHexColor = function(b) { + return/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(b); }; - a.clampByte = function(a) { - return Math.max(0, Math.min(255, a)); - }; - a.unpremultiplyARGB = function(a) { - var c, e, l = a >> 24 & 255; - e = Math.imul(255, a >> 16 & 255) / l & 255; - c = Math.imul(255, a >> 8 & 255) / l & 255; - a = Math.imul(255, a >> 0 & 255) / l & 255; - return l << 24 | e << 16 | c << 8 | a; + d.clampByte = function(b) { + return Math.max(0, Math.min(255, b)); }; - a.premultiplyARGB = c; - var e; - a.ensureUnpremultiplyTable = function() { - if (!e) { - e = new Uint8Array(65536); - for (var a = 0;256 > a;a++) { - for (var c = 0;256 > c;c++) { - e[(c << 8) + a] = Math.imul(255, a) / c; + d.unpremultiplyARGB = function(b) { + var g, d, r = b >> 24 & 255; + d = Math.imul(255, b >> 16 & 255) / r & 255; + g = Math.imul(255, b >> 8 & 255) / r & 255; + b = Math.imul(255, b >> 0 & 255) / r & 255; + return r << 24 | d << 16 | g << 8 | b; + }; + d.premultiplyARGB = b; + var g; + d.ensureUnpremultiplyTable = function() { + if (!g) { + g = new Uint8Array(65536); + for (var b = 0;256 > b;b++) { + for (var d = 0;256 > d;d++) { + g[(d << 8) + b] = Math.imul(255, b) / d; } } } }; - a.tableLookupUnpremultiplyARGB = function(a) { - a |= 0; - var c = a >> 24 & 255; - if (0 === c) { + d.tableLookupUnpremultiplyARGB = function(b) { + b |= 0; + var d = b >> 24 & 255; + if (0 === d) { return 0; } - if (255 === c) { - return a; + if (255 === d) { + return b; } - var l, r, q = c << 8, d = e; - r = d[q + (a >> 16 & 255)]; - l = d[q + (a >> 8 & 255)]; - a = d[q + (a >> 0 & 255)]; - return c << 24 | r << 16 | l << 8 | a; - }; - a.blendPremultipliedBGRA = function(a, c) { - var e, l; - l = 256 - (c & 255); - e = Math.imul(a & 16711935, l) >> 8; - l = Math.imul(a >> 8 & 16711935, l) >> 8; - return((c >> 8 & 16711935) + l & 16711935) << 8 | (c & 16711935) + e & 16711935; - }; - var r = n.swap32; - a.convertImage = function(a, l, d, p) { - d !== p && s.assert(d.buffer !== p.buffer, "Can't handle overlapping views."); - var n = d.length; - if (a === l) { - if (d !== p) { - for (a = 0;a < n;a++) { - p[a] = d[a]; + var r, f, k = d << 8, e = g; + f = e[k + (b >> 16 & 255)]; + r = e[k + (b >> 8 & 255)]; + b = e[k + (b >> 0 & 255)]; + return d << 24 | f << 16 | r << 8 | b; + }; + d.blendPremultipliedBGRA = function(b, g) { + var d, r; + r = 256 - (g & 255); + d = Math.imul(b & 16711935, r) >> 8; + r = Math.imul(b >> 8 & 16711935, r) >> 8; + return((g >> 8 & 16711935) + r & 16711935) << 8 | (g & 16711935) + d & 16711935; + }; + var r = q.swap32; + d.convertImage = function(d, k, e, n) { + e !== n && p.assert(e.buffer !== n.buffer, "Can't handle overlapping views."); + var m = e.length; + if (d === k) { + if (e !== n) { + for (d = 0;d < m;d++) { + n[d] = e[d]; } } } else { - if (1 === a && 3 === l) { - for (b.ColorUtilities.ensureUnpremultiplyTable(), a = 0;a < n;a++) { - var m = d[a]; - l = m & 255; - if (0 === l) { - p[a] = 0; + if (1 === d && 3 === k) { + for (c.ColorUtilities.ensureUnpremultiplyTable(), d = 0;d < m;d++) { + var l = e[d]; + k = l & 255; + if (0 === k) { + n[d] = 0; } else { - if (255 === l) { - p[a] = (m & 255) << 24 | m >> 8 & 16777215; + if (255 === k) { + n[d] = 4278190080 | l >> 8 & 16777215; } else { - var f = m >> 24 & 255, g = m >> 16 & 255, m = m >> 8 & 255, k = l << 8, t = e, m = t[k + m], g = t[k + g], f = t[k + f]; - p[a] = l << 24 | f << 16 | g << 8 | m; + var q = l >> 24 & 255, a = l >> 16 & 255, l = l >> 8 & 255, t = k << 8, s = g, l = s[t + l], a = s[t + a], q = s[t + q]; + n[d] = k << 24 | q << 16 | a << 8 | l; } } } } else { - if (2 === a && 3 === l) { - for (a = 0;a < n;a++) { - p[a] = r(d[a]); + if (2 === d && 3 === k) { + for (d = 0;d < m;d++) { + n[d] = r(e[d]); } } else { - if (3 === a && 1 === l) { - for (a = 0;a < n;a++) { - l = d[a], p[a] = r(c(l & 4278255360 | l >> 16 & 255 | (l & 255) << 16)); + if (3 === d && 1 === k) { + for (d = 0;d < m;d++) { + k = e[d], n[d] = r(b(k & 4278255360 | k >> 16 & 255 | (k & 255) << 16)); } } else { - for (s.somewhatImplemented("Image Format Conversion: " + q[a] + " -> " + q[l]), a = 0;a < n;a++) { - p[a] = d[a]; + for (p.somewhatImplemented("Image Format Conversion: " + f[d] + " -> " + f[k]), d = 0;d < m;d++) { + n[d] = e[d]; } } } } } }; - })(b.ColorUtilities || (b.ColorUtilities = {})); - var e = b.ColorUtilities, d = function() { - function a(c) { - "undefined" === typeof c && (c = 32); + })(k = c.ColorUtilities || (c.ColorUtilities = {})); + l = function() { + function d(b) { + void 0 === b && (b = 32); this._list = []; - this._maxSize = c; + this._maxSize = b; } - a.prototype.acquire = function(c) { - if (a._enabled) { - for (var e = this._list, r = 0;r < e.length;r++) { - var h = e[r]; - if (h.byteLength >= c) { - return e.splice(r, 1), h; + d.prototype.acquire = function(b) { + if (d._enabled) { + for (var g = this._list, r = 0;r < g.length;r++) { + var f = g[r]; + if (f.byteLength >= b) { + return g.splice(r, 1), f; } } } - return new ArrayBuffer(c); + return new ArrayBuffer(b); }; - a.prototype.release = function(c) { - if (a._enabled) { - var e = this._list; - s.assert(0 > m.indexOf(e, c)); - e.length === this._maxSize && e.shift(); - e.push(c); + d.prototype.release = function(b) { + if (d._enabled) { + var g = this._list; + p.assert(0 > u.indexOf(g, b)); + g.length === this._maxSize && g.shift(); + g.push(b); } }; - a.prototype.ensureUint8ArrayLength = function(a, c) { - if (a.length >= c) { - return a; + d.prototype.ensureUint8ArrayLength = function(b, g) { + if (b.length >= g) { + return b; } - var e = Math.max(a.length + c, (3 * a.length >> 1) + 1), e = new Uint8Array(this.acquire(e), 0, e); - e.set(a); - this.release(a.buffer); - return e; + var d = Math.max(b.length + g, (3 * b.length >> 1) + 1), d = new Uint8Array(this.acquire(d), 0, d); + d.set(b); + this.release(b.buffer); + return d; }; - a.prototype.ensureFloat64ArrayLength = function(a, c) { - if (a.length >= c) { - return a; + d.prototype.ensureFloat64ArrayLength = function(b, g) { + if (b.length >= g) { + return b; } - var e = Math.max(a.length + c, (3 * a.length >> 1) + 1), e = new Float64Array(this.acquire(e * Float64Array.BYTES_PER_ELEMENT), 0, e); - e.set(a); - this.release(a.buffer); - return e; + var d = Math.max(b.length + g, (3 * b.length >> 1) + 1), d = new Float64Array(this.acquire(d * Float64Array.BYTES_PER_ELEMENT), 0, d); + d.set(b); + this.release(b.buffer); + return d; }; - a._enabled = !0; - return a; + d._enabled = !0; + return d; }(); - b.ArrayBufferPool = d; - (function(a) { - (function(a) { - a[a.EXTERNAL_INTERFACE_FEATURE = 1] = "EXTERNAL_INTERFACE_FEATURE"; - a[a.CLIPBOARD_FEATURE = 2] = "CLIPBOARD_FEATURE"; - a[a.SHAREDOBJECT_FEATURE = 3] = "SHAREDOBJECT_FEATURE"; - a[a.VIDEO_FEATURE = 4] = "VIDEO_FEATURE"; - a[a.SOUND_FEATURE = 5] = "SOUND_FEATURE"; - a[a.NETCONNECTION_FEATURE = 6] = "NETCONNECTION_FEATURE"; - })(a.Feature || (a.Feature = {})); - (function(a) { - a[a.AVM1_ERROR = 1] = "AVM1_ERROR"; - a[a.AVM2_ERROR = 2] = "AVM2_ERROR"; - })(a.ErrorTypes || (a.ErrorTypes = {})); - a.instance; - })(b.Telemetry || (b.Telemetry = {})); - (function(a) { - a.instance; - })(b.FileLoadingService || (b.FileLoadingService = {})); - (function(a) { - a.instance = {enabled:!1, initJS:function(a) { - }, registerCallback:function(a) { - }, unregisterCallback:function(a) { - }, eval:function(a) { - }, call:function(a) { + c.ArrayBufferPool = l; + (function(d) { + (function(b) { + b[b.EXTERNAL_INTERFACE_FEATURE = 1] = "EXTERNAL_INTERFACE_FEATURE"; + b[b.CLIPBOARD_FEATURE = 2] = "CLIPBOARD_FEATURE"; + b[b.SHAREDOBJECT_FEATURE = 3] = "SHAREDOBJECT_FEATURE"; + b[b.VIDEO_FEATURE = 4] = "VIDEO_FEATURE"; + b[b.SOUND_FEATURE = 5] = "SOUND_FEATURE"; + b[b.NETCONNECTION_FEATURE = 6] = "NETCONNECTION_FEATURE"; + })(d.Feature || (d.Feature = {})); + (function(b) { + b[b.AVM1_ERROR = 1] = "AVM1_ERROR"; + b[b.AVM2_ERROR = 2] = "AVM2_ERROR"; + })(d.ErrorTypes || (d.ErrorTypes = {})); + d.instance; + })(c.Telemetry || (c.Telemetry = {})); + (function(d) { + d.instance; + })(c.FileLoadingService || (c.FileLoadingService = {})); + c.registerCSSFont = function(d, b, g) { + if (inBrowser) { + var r = document.head; + r.insertBefore(document.createElement("style"), r.firstChild); + r = document.styleSheets[0]; + b = "@font-face{font-family:swffont" + d + ";src:url(data:font/opentype;base64," + c.StringUtilities.base64ArrayBuffer(b) + ")}"; + r.insertRule(b, r.cssRules.length); + g && (g = document.createElement("div"), g.style.fontFamily = "swffont" + d, g.innerHTML = "hello", document.body.appendChild(g), document.body.removeChild(g)); + } else { + p.warning("Cannot register CSS font outside the browser"); + } + }; + (function(d) { + d.instance = {enabled:!1, initJS:function(b) { + }, registerCallback:function(b) { + }, unregisterCallback:function(b) { + }, eval:function(b) { + }, call:function(b) { }, getId:function() { return null; }}; - })(b.ExternalInterfaceService || (b.ExternalInterfaceService = {})); - d = function() { - function a() { + })(c.ExternalInterfaceService || (c.ExternalInterfaceService = {})); + l = function() { + function d() { } - a.prototype.setClipboard = function(a) { - s.abstractMethod("public ClipboardService::setClipboard"); + d.prototype.setClipboard = function(b) { + p.abstractMethod("public ClipboardService::setClipboard"); }; - a.instance = null; - return a; + d.instance = null; + return d; }(); - b.ClipboardService = d; - d = function() { - function a() { + c.ClipboardService = l; + l = function() { + function d() { this._queues = {}; } - a.prototype.register = function(a, c) { - s.assert(a); - s.assert(c); - var e = this._queues[a]; - if (e) { - if (-1 < e.indexOf(c)) { + d.prototype.register = function(b, g) { + p.assert(b); + p.assert(g); + var d = this._queues[b]; + if (d) { + if (-1 < d.indexOf(g)) { return; } } else { - e = this._queues[a] = []; + d = this._queues[b] = []; + } + d.push(g); + }; + d.prototype.unregister = function(b, g) { + p.assert(b); + p.assert(g); + var d = this._queues[b]; + if (d) { + var f = d.indexOf(g); + -1 !== f && d.splice(f, 1); + 0 === d.length && (this._queues[b] = null); } - e.push(c); }; - a.prototype.unregister = function(a, c) { - s.assert(a); - s.assert(c); - var e = this._queues[a]; - if (e) { - var h = e.indexOf(c); - -1 !== h && e.splice(h, 1); - 0 === e.length && (this._queues[a] = null); - } - }; - a.prototype.notify = function(a, c) { - var e = this._queues[a]; - if (e) { - e = e.slice(); - c = Array.prototype.slice.call(arguments, 0); - for (var h = 0;h < e.length;h++) { - e[h].apply(null, c); + d.prototype.notify = function(b, g) { + var d = this._queues[b]; + if (d) { + d = d.slice(); + g = Array.prototype.slice.call(arguments, 0); + for (var f = 0;f < d.length;f++) { + d[f].apply(null, g); } } }; - a.prototype.notify1 = function(a, c) { - var e = this._queues[a]; - if (e) { - for (var e = e.slice(), h = 0;h < e.length;h++) { - (0,e[h])(a, c); + d.prototype.notify1 = function(b, g) { + var d = this._queues[b]; + if (d) { + for (var d = d.slice(), f = 0;f < d.length;f++) { + (0,d[f])(b, g); } } }; - return a; + return d; }(); - b.Callback = d; - (function(a) { - a[a.None = 0] = "None"; - a[a.PremultipliedAlphaARGB = 1] = "PremultipliedAlphaARGB"; - a[a.StraightAlphaARGB = 2] = "StraightAlphaARGB"; - a[a.StraightAlphaRGBA = 3] = "StraightAlphaRGBA"; - a[a.JPEG = 4] = "JPEG"; - a[a.PNG = 5] = "PNG"; - a[a.GIF = 6] = "GIF"; - })(b.ImageType || (b.ImageType = {})); - var q = b.ImageType; - b.PromiseWrapper = function() { - return function() { - this.promise = new Promise(function(a, c) { - this.resolve = a; - this.reject = c; + c.Callback = l; + (function(d) { + d[d.None = 0] = "None"; + d[d.PremultipliedAlphaARGB = 1] = "PremultipliedAlphaARGB"; + d[d.StraightAlphaARGB = 2] = "StraightAlphaARGB"; + d[d.StraightAlphaRGBA = 3] = "StraightAlphaRGBA"; + d[d.JPEG = 4] = "JPEG"; + d[d.PNG = 5] = "PNG"; + d[d.GIF = 6] = "GIF"; + })(c.ImageType || (c.ImageType = {})); + var f = c.ImageType; + c.getMIMETypeForImageType = function(d) { + switch(d) { + case 4: + return "image/jpeg"; + case 5: + return "image/png"; + case 6: + return "image/gif"; + default: + return "text/plain"; + } + }; + (function(d) { + d.toCSSCursor = function(b) { + switch(b) { + case 0: + return "auto"; + case 2: + return "pointer"; + case 3: + return "grab"; + case 4: + return "text"; + default: + return "default"; + } + }; + })(c.UI || (c.UI = {})); + l = function() { + function d() { + this.promise = new Promise(function(b, d) { + this.resolve = b; + this.reject = d; }.bind(this)); + } + d.prototype.then = function(b, d) { + return this.promise.then(b, d); }; + return d; }(); + c.PromiseWrapper = l; })(Shumway || (Shumway = {})); (function() { - function b(a) { - if ("function" !== typeof a) { + function c(b) { + if ("function" !== typeof b) { throw new TypeError("Invalid deferred constructor"); } - var c = p(); - a = new a(c); - var e = c.resolve; - if ("function" !== typeof e) { + var d = n(); + b = new b(d); + var g = d.resolve; + if ("function" !== typeof g) { throw new TypeError("Invalid resolve construction function"); } - c = c.reject; - if ("function" !== typeof c) { + d = d.reject; + if ("function" !== typeof d) { throw new TypeError("Invalid reject construction function"); } - return{promise:a, resolve:e, reject:c}; + return{promise:b, resolve:g, reject:d}; } - function k(a, c) { - if ("object" !== typeof a || null === a) { + function h(b, d) { + if ("object" !== typeof b || null === b) { return!1; } try { - var e = a.then; - if ("function" !== typeof e) { + var g = b.then; + if ("function" !== typeof g) { return!1; } - e.call(a, c.resolve, c.reject); - } catch (l) { - e = c.reject, e(l); + g.call(b, d.resolve, d.reject); + } catch (f) { + g = d.reject, g(f); } return!0; } - function g(a) { - return "object" === typeof a && null !== a && "undefined" !== typeof a.promiseStatus; + function a(b) { + return "object" === typeof b && null !== b && "undefined" !== typeof b.promiseStatus; } - function f(a, c) { - if ("unresolved" === a.promiseStatus) { - var e = a.rejectReactions; - a.result = c; - a.resolveReactions = void 0; - a.rejectReactions = void 0; - a.promiseStatus = "has-rejection"; - t(e, c); + function s(b, d) { + if ("unresolved" === b.promiseStatus) { + var g = b.rejectReactions; + b.result = d; + b.resolveReactions = void 0; + b.rejectReactions = void 0; + b.promiseStatus = "has-rejection"; + v(g, d); } } - function t(a, c) { - for (var e = 0;e < a.length;e++) { - s({reaction:a[e], argument:c}); + function v(b, d) { + for (var g = 0;g < b.length;g++) { + p({reaction:b[g], argument:d}); } } - function s(a) { - 0 === w.length && setTimeout(m, 0); - w.push(a); + function p(b) { + 0 === g.length && setTimeout(l, 0); + g.push(b); + } + function u(b, d) { + var g = b.deferred, f = b.handler, k, e; + try { + k = f(d); + } catch (n) { + return g = g.reject, g(n); + } + if (k === g.promise) { + return g = g.reject, g(new TypeError("Self resolution")); + } + try { + if (e = h(k, g), !e) { + var m = g.resolve; + return m(k); + } + } catch (l) { + return g = g.reject, g(l); + } } - function m() { - for (;0 < w.length;) { - var a = w[0]; + function l() { + for (;0 < g.length;) { + var b = g[0]; try { - a: { - var c = a.reaction, e = c.deferred, q = c.handler, d = void 0, p = void 0; - try { - d = q(a.argument); - } catch (n) { - var b = e.reject; - b(n); - break a; - } - if (d === e.promise) { - b = e.reject, b(new TypeError("Self resolution")); - } else { - try { - if (p = k(d, e), !p) { - var m = e.resolve; - m(d); - } - } catch (u) { - b = e.reject, b(u); - } - } - } + u(b.reaction, b.argument); } catch (f) { - if ("function" === typeof l.onerror) { - l.onerror(f); + if ("function" === typeof d.onerror) { + d.onerror(f); } } - w.shift(); + g.shift(); } } - function d(a) { - throw a; + function e(b) { + throw b; } - function a(a) { - return a; + function m(b) { + return b; } - function c(a) { - return function(c) { - f(a, c); + function t(b) { + return function(d) { + s(b, d); }; } - function n(a) { - return function(c) { - if ("unresolved" === a.promiseStatus) { - var e = a.resolveReactions; - a.result = c; - a.resolveReactions = void 0; - a.rejectReactions = void 0; - a.promiseStatus = "has-resolution"; - t(e, c); + function q(b) { + return function(d) { + if ("unresolved" === b.promiseStatus) { + var g = b.resolveReactions; + b.result = d; + b.resolveReactions = void 0; + b.rejectReactions = void 0; + b.promiseStatus = "has-resolution"; + v(g, d); } }; } - function p() { - function a(c, e) { - a.resolve = c; - a.reject = e; + function n() { + function b(d, g) { + b.resolve = d; + b.reject = g; } - return a; + return b; } - function e(a, c, e) { - return function(l) { - if (l === a) { - return e(new TypeError("Self resolution")); - } - var q = a.promiseConstructor; - if (g(l) && l.promiseConstructor === q) { - return l.then(c, e); + function k(b, d, g) { + return function(f) { + if (f === b) { + return g(new TypeError("Self resolution")); + } + var k = b.promiseConstructor; + if (a(f) && f.promiseConstructor === k) { + return f.then(d, g); } - q = b(q); - return k(l, q) ? q.promise.then(c, e) : c(l); + k = c(k); + return h(f, k) ? k.promise.then(d, g) : d(f); }; } - function q(a, c, e, l) { - return function(q) { - c[a] = q; - l.countdown--; - 0 === l.countdown && e.resolve(c); + function f(b, d, g, f) { + return function(k) { + d[b] = k; + f.countdown--; + 0 === f.countdown && g.resolve(d); }; } - function l(a) { - if ("function" !== typeof a) { + function d(b) { + if ("function" !== typeof b) { throw new TypeError("resolver is not a function"); } if ("object" !== typeof this) { @@ -2222,578 +2294,628 @@ this.resolveReactions = []; this.rejectReactions = []; this.result = void 0; - var h = n(this), e = c(this); + var g = q(this), f = t(this); try { - a(h, e); - } catch (q) { - f(this, q); + b(g, f); + } catch (k) { + s(this, k); } - this.promiseConstructor = l; + this.promiseConstructor = d; return this; } - var u = Function("return this")(); - if (u.Promise) { - "function" !== typeof u.Promise.all && (u.Promise.all = function(a) { - var c = 0, e = [], l, q, d = new u.Promise(function(a, c) { - l = a; - q = c; + var b = Function("return this")(); + if (b.Promise) { + "function" !== typeof b.Promise.all && (b.Promise.all = function(d) { + var g = 0, f = [], k, e, n = new b.Promise(function(b, d) { + k = b; + e = d; }); - a.forEach(function(a, d) { - c++; - a.then(function(a) { - e[d] = a; - c--; - 0 === c && l(e); - }, q); + d.forEach(function(b, d) { + g++; + b.then(function(b) { + f[d] = b; + g--; + 0 === g && k(f); + }, e); }); - 0 === c && l(e); - return d; - }), "function" !== typeof u.Promise.resolve && (u.Promise.resolve = function(a) { - return new u.Promise(function(c) { - c(a); + 0 === g && k(f); + return n; + }), "function" !== typeof b.Promise.resolve && (b.Promise.resolve = function(d) { + return new b.Promise(function(b) { + b(d); }); }); } else { - var w = []; - l.all = function(a) { - var c = b(this), e = [], l = {countdown:0}, d = 0; - a.forEach(function(a) { - this.cast(a).then(q(d, e, c, l), c.reject); - d++; - l.countdown++; + var g = []; + d.all = function(b) { + var d = c(this), g = [], k = {countdown:0}, e = 0; + b.forEach(function(b) { + this.cast(b).then(f(e, g, d, k), d.reject); + e++; + k.countdown++; }, this); - 0 === d && c.resolve(e); - return c.promise; + 0 === e && d.resolve(g); + return d.promise; }; - l.cast = function(a) { - if (g(a)) { - return a; + d.cast = function(b) { + if (a(b)) { + return b; } - var c = b(this); - c.resolve(a); - return c.promise; - }; - l.reject = function(a) { - var c = b(this); - c.reject(a); - return c.promise; - }; - l.resolve = function(a) { - var c = b(this); - c.resolve(a); - return c.promise; - }; - l.prototype = {"catch":function(a) { - this.then(void 0, a); - }, then:function(c, h) { - if (!g(this)) { + var d = c(this); + d.resolve(b); + return d.promise; + }; + d.reject = function(b) { + var d = c(this); + d.reject(b); + return d.promise; + }; + d.resolve = function(b) { + var d = c(this); + d.resolve(b); + return d.promise; + }; + d.prototype = {"catch":function(b) { + this.then(void 0, b); + }, then:function(b, d) { + if (!a(this)) { throw new TypeError("this is not a Promises"); } - var l = b(this.promiseConstructor), q = "function" === typeof h ? h : d, p = {deferred:l, handler:e(this, "function" === typeof c ? c : a, q)}, q = {deferred:l, handler:q}; + var g = c(this.promiseConstructor), f = "function" === typeof d ? d : e, n = {deferred:g, handler:k(this, "function" === typeof b ? b : m, f)}, f = {deferred:g, handler:f}; switch(this.promiseStatus) { case "unresolved": - this.resolveReactions.push(p); - this.rejectReactions.push(q); + this.resolveReactions.push(n); + this.rejectReactions.push(f); break; case "has-resolution": - s({reaction:p, argument:this.result}); + p({reaction:n, argument:this.result}); break; case "has-rejection": - s({reaction:q, argument:this.result}); + p({reaction:f, argument:this.result}); } - return l.promise; + return g.promise; }}; - u.Promise = l; + b.Promise = d; } })(); "undefined" !== typeof exports && (exports.Shumway = Shumway); (function() { - function b(b, g, f) { - b[g] || Object.defineProperty(b, g, {value:f, writable:!0, configurable:!0, enumerable:!1}); + function c(c, a, s) { + c[a] || Object.defineProperty(c, a, {value:s, writable:!0, configurable:!0, enumerable:!1}); } - b(String.prototype, "padRight", function(b, g) { - var f = this, t = f.replace(/\033\[[0-9]*m/g, "").length; - if (!b || t >= g) { - return f; + c(String.prototype, "padRight", function(c, a) { + var s = this, v = s.replace(/\033\[[0-9]*m/g, "").length; + if (!c || v >= a) { + return s; } - for (var t = (g - t) / b.length, s = 0;s < t;s++) { - f += b; + for (var v = (a - v) / c.length, p = 0;p < v;p++) { + s += c; } - return f; + return s; }); - b(String.prototype, "padLeft", function(b, g) { - var f = this, t = f.length; - if (!b || t >= g) { - return f; + c(String.prototype, "padLeft", function(c, a) { + var s = this, v = s.length; + if (!c || v >= a) { + return s; } - for (var t = (g - t) / b.length, s = 0;s < t;s++) { - f = b + f; + for (var v = (a - v) / c.length, p = 0;p < v;p++) { + s = c + s; } - return f; + return s; }); - b(String.prototype, "trim", function() { + c(String.prototype, "trim", function() { return this.replace(/^\s+|\s+$/g, ""); }); - b(String.prototype, "endsWith", function(b) { - return-1 !== this.indexOf(b, this.length - b.length); + c(String.prototype, "endsWith", function(c) { + return-1 !== this.indexOf(c, this.length - c.length); }); - b(Array.prototype, "replace", function(b, g) { - if (b === g) { + c(Array.prototype, "replace", function(c, a) { + if (c === a) { return 0; } - for (var f = 0, t = 0;t < this.length;t++) { - this[t] === b && (this[t] = g, f++); + for (var s = 0, v = 0;v < this.length;v++) { + this[v] === c && (this[v] = a, s++); } - return f; + return s; }); })(); -(function(b) { - (function(k) { - var g = b.isObject, f = b.Debug.assert, t = function() { - function a(a, d, p, e) { - this.shortName = a; - this.longName = d; - this.type = p; - e = e || {}; - this.positional = e.positional; - this.parseFn = e.parse; - this.value = e.defaultValue; +(function(c) { + (function(h) { + var a = c.isObject, s = c.Debug.assert, v = function() { + function e(e, l, q, n) { + this.shortName = e; + this.longName = l; + this.type = q; + n = n || {}; + this.positional = n.positional; + this.parseFn = n.parse; + this.value = n.defaultValue; } - a.prototype.parse = function(a) { - "boolean" === this.type ? (f("boolean" === typeof a), this.value = a) : "number" === this.type ? (f(!isNaN(a), a + " is not a number"), this.value = parseInt(a, 10)) : this.value = a; + e.prototype.parse = function(e) { + "boolean" === this.type ? (s("boolean" === typeof e), this.value = e) : "number" === this.type ? (s(!isNaN(e), e + " is not a number"), this.value = parseInt(e, 10)) : this.value = e; this.parseFn && this.parseFn(this.value); }; - return a; + return e; }(); - k.Argument = t; - var s = function() { - function a() { + h.Argument = v; + var p = function() { + function e() { this.args = []; } - a.prototype.addArgument = function(a, d, p, e) { - a = new t(a, d, p, e); - this.args.push(a); - return a; + e.prototype.addArgument = function(e, l, q, n) { + e = new v(e, l, q, n); + this.args.push(e); + return e; }; - a.prototype.addBoundOption = function(a) { - this.args.push(new t(a.shortName, a.longName, a.type, {parse:function(d) { - a.value = d; + e.prototype.addBoundOption = function(e) { + this.args.push(new v(e.shortName, e.longName, e.type, {parse:function(l) { + e.value = l; }})); }; - a.prototype.addBoundOptionSet = function(a) { - var n = this; - a.options.forEach(function(a) { - a instanceof m ? n.addBoundOptionSet(a) : (f(a instanceof d), n.addBoundOption(a)); + e.prototype.addBoundOptionSet = function(e) { + var a = this; + e.options.forEach(function(e) { + e instanceof u ? a.addBoundOptionSet(e) : (s(e instanceof l), a.addBoundOption(e)); }); }; - a.prototype.getUsage = function() { - var a = ""; - this.args.forEach(function(d) { - a = d.positional ? a + d.longName : a + ("[-" + d.shortName + "|--" + d.longName + ("boolean" === d.type ? "" : " " + d.type[0].toUpperCase()) + "]"); - a += " "; + e.prototype.getUsage = function() { + var e = ""; + this.args.forEach(function(l) { + e = l.positional ? e + l.longName : e + ("[-" + l.shortName + "|--" + l.longName + ("boolean" === l.type ? "" : " " + l.type[0].toUpperCase()) + "]"); + e += " "; }); - return a; + return e; }; - a.prototype.parse = function(a) { - var d = {}, p = []; - this.args.forEach(function(a) { - a.positional ? p.push(a) : (d["-" + a.shortName] = a, d["--" + a.longName] = a); + e.prototype.parse = function(e) { + var l = {}, q = []; + this.args.forEach(function(b) { + b.positional ? q.push(b) : (l["-" + b.shortName] = b, l["--" + b.longName] = b); }); - for (var e = [];a.length;) { - var q = a.shift(), l = null, b = q; - if ("--" == q) { - e = e.concat(a); + for (var n = [];e.length;) { + var k = e.shift(), f = null, d = k; + if ("--" == k) { + n = n.concat(e); break; } else { - if ("-" == q.slice(0, 1) || "--" == q.slice(0, 2)) { - l = d[q]; - if (!l) { + if ("-" == k.slice(0, 1) || "--" == k.slice(0, 2)) { + f = l[k]; + if (!f) { continue; } - "boolean" !== l.type ? (b = a.shift(), f("-" !== b && "--" !== b, "Argument " + q + " must have a value.")) : b = !0; + "boolean" !== f.type ? (d = e.shift(), s("-" !== d && "--" !== d, "Argument " + k + " must have a value.")) : d = !0; } else { - p.length ? l = p.shift() : e.push(b); + q.length ? f = q.shift() : n.push(d); } } - l && l.parse(b); + f && f.parse(d); } - f(0 === p.length, "Missing positional arguments."); - return e; + s(0 === q.length, "Missing positional arguments."); + return n; }; - return a; + return e; }(); - k.ArgumentParser = s; - var m = function() { - function a(a, d) { - "undefined" === typeof d && (d = null); + h.ArgumentParser = p; + var u = function() { + function e(e, l) { + void 0 === l && (l = null); this.open = !1; - this.name = a; - this.settings = d || {}; + this.name = e; + this.settings = l || {}; this.options = []; } - a.prototype.register = function(c) { - if (c instanceof a) { - for (var d = 0;d < this.options.length;d++) { - var p = this.options[d]; - if (p instanceof a && p.name === c.name) { - return p; + e.prototype.register = function(m) { + if (m instanceof e) { + for (var l = 0;l < this.options.length;l++) { + var q = this.options[l]; + if (q instanceof e && q.name === m.name) { + return q; } } } - this.options.push(c); + this.options.push(m); if (this.settings) { - if (c instanceof a) { - d = this.settings[c.name], g(d) && (c.settings = d.settings, c.open = d.open); + if (m instanceof e) { + l = this.settings[m.name], a(l) && (m.settings = l.settings, m.open = l.open); } else { - if ("undefined" !== typeof this.settings[c.longName]) { - switch(c.type) { + if ("undefined" !== typeof this.settings[m.longName]) { + switch(m.type) { case "boolean": - c.value = !!this.settings[c.longName]; + m.value = !!this.settings[m.longName]; break; case "number": - c.value = +this.settings[c.longName]; + m.value = +this.settings[m.longName]; break; default: - c.value = this.settings[c.longName]; + m.value = this.settings[m.longName]; } } } } - return c; + return m; }; - a.prototype.trace = function(a) { - a.enter(this.name + " {"); - this.options.forEach(function(d) { - d.trace(a); + e.prototype.trace = function(e) { + e.enter(this.name + " {"); + this.options.forEach(function(l) { + l.trace(e); }); - a.leave("}"); + e.leave("}"); }; - a.prototype.getSettings = function() { - var c = {}; - this.options.forEach(function(d) { - d instanceof a ? c[d.name] = {settings:d.getSettings(), open:d.open} : c[d.longName] = d.value; + e.prototype.getSettings = function() { + var m = {}; + this.options.forEach(function(l) { + l instanceof e ? m[l.name] = {settings:l.getSettings(), open:l.open} : m[l.longName] = l.value; }); - return c; + return m; }; - a.prototype.setSettings = function(c) { - c && this.options.forEach(function(d) { - d instanceof a ? d.name in c && d.setSettings(c[d.name].settings) : d.longName in c && (d.value = c[d.longName]); + e.prototype.setSettings = function(m) { + m && this.options.forEach(function(l) { + l instanceof e ? l.name in m && l.setSettings(m[l.name].settings) : l.longName in m && (l.value = m[l.longName]); }); }; - return a; + return e; }(); - k.OptionSet = m; - var d = function() { - function a(a, d, p, e, q, l) { - "undefined" === typeof l && (l = null); - this.longName = d; - this.shortName = a; - this.type = p; - this.value = this.defaultValue = e; - this.description = q; - this.config = l; + h.OptionSet = u; + var l = function() { + function e(e, l, q, n, k, f) { + void 0 === f && (f = null); + this.longName = l; + this.shortName = e; + this.type = q; + this.value = this.defaultValue = n; + this.description = k; + this.config = f; } - a.prototype.parse = function(a) { - this.value = a; + e.prototype.parse = function(e) { + this.value = e; }; - a.prototype.trace = function(a) { - a.writeLn(("-" + this.shortName + "|--" + this.longName).padRight(" ", 30) + " = " + this.type + " " + this.value + " [" + this.defaultValue + "] (" + this.description + ")"); + e.prototype.trace = function(e) { + e.writeLn(("-" + this.shortName + "|--" + this.longName).padRight(" ", 30) + " = " + this.type + " " + this.value + " [" + this.defaultValue + "] (" + this.description + ")"); }; - return a; + return e; }(); - k.Option = d; - })(b.Options || (b.Options = {})); + h.Option = l; + })(c.Options || (c.Options = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - function g() { +(function(c) { + (function(h) { + function a() { try { return "undefined" !== typeof window && "localStorage" in window && null !== window.localStorage; - } catch (b) { + } catch (a) { return!1; } } - function f(b) { - "undefined" === typeof b && (b = k.ROOT); - var f = {}; - if (g() && (b = window.localStorage[b])) { + function s(c) { + void 0 === c && (c = h.ROOT); + var s = {}; + if (a() && (c = window.localStorage[c])) { try { - f = JSON.parse(b); - } catch (m) { + s = JSON.parse(c); + } catch (u) { } } - return f; + return s; } - k.ROOT = "Shumway Options"; - k.shumwayOptions = new b.Options.OptionSet(k.ROOT, f()); - k.isStorageSupported = g; - k.load = f; - k.save = function(b, f) { - "undefined" === typeof b && (b = null); - "undefined" === typeof f && (f = k.ROOT); - if (g()) { + h.ROOT = "Shumway Options"; + h.shumwayOptions = new c.Options.OptionSet(h.ROOT, s()); + h.isStorageSupported = a; + h.load = s; + h.save = function(c, s) { + void 0 === c && (c = null); + void 0 === s && (s = h.ROOT); + if (a()) { try { - window.localStorage[f] = JSON.stringify(b ? b : k.shumwayOptions.getSettings()); - } catch (m) { + window.localStorage[s] = JSON.stringify(c ? c : h.shumwayOptions.getSettings()); + } catch (u) { } } }; - k.setSettings = function(b) { - k.shumwayOptions.setSettings(b); + h.setSettings = function(a) { + h.shumwayOptions.setSettings(a); }; - k.getSettings = function(b) { - return k.shumwayOptions.getSettings(); + h.getSettings = function(a) { + return h.shumwayOptions.getSettings(); }; - })(b.Settings || (b.Settings = {})); + })(c.Settings || (c.Settings = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - var g = function() { - function f(f, g) { - this._parent = f; - this._timers = b.ObjectUtilities.createMap(); - this._name = g; +(function(c) { + (function(h) { + var a = function() { + function a(s, p) { + this._parent = s; + this._timers = c.ObjectUtilities.createMap(); + this._name = p; this._count = this._total = this._last = this._begin = 0; } - f.time = function(b, g) { - f.start(b); - g(); - f.stop(); - }; - f.start = function(b) { - f._top = f._top._timers[b] || (f._top._timers[b] = new f(f._top, b)); - f._top.start(); - b = f._flat._timers[b] || (f._flat._timers[b] = new f(f._flat, b)); - b.start(); - f._flatStack.push(b); - }; - f.stop = function() { - f._top.stop(); - f._top = f._top._parent; - f._flatStack.pop().stop(); - }; - f.stopStart = function(b) { - f.stop(); - f.start(b); + a.time = function(c, p) { + a.start(c); + p(); + a.stop(); + }; + a.start = function(c) { + a._top = a._top._timers[c] || (a._top._timers[c] = new a(a._top, c)); + a._top.start(); + c = a._flat._timers[c] || (a._flat._timers[c] = new a(a._flat, c)); + c.start(); + a._flatStack.push(c); + }; + a.stop = function() { + a._top.stop(); + a._top = a._top._parent; + a._flatStack.pop().stop(); + }; + a.stopStart = function(c) { + a.stop(); + a.start(c); }; - f.prototype.start = function() { - this._begin = b.getTicks(); + a.prototype.start = function() { + this._begin = c.getTicks(); }; - f.prototype.stop = function() { - this._last = b.getTicks() - this._begin; + a.prototype.stop = function() { + this._last = c.getTicks() - this._begin; this._total += this._last; this._count += 1; }; - f.prototype.toJSON = function() { + a.prototype.toJSON = function() { return{name:this._name, total:this._total, timers:this._timers}; }; - f.prototype.trace = function(b) { - b.enter(this._name + ": " + this._total.toFixed(2) + " ms, count: " + this._count + ", average: " + (this._total / this._count).toFixed(2) + " ms"); - for (var f in this._timers) { - this._timers[f].trace(b); - } - b.outdent(); - }; - f.trace = function(b) { - f._base.trace(b); - f._flat.trace(b); - }; - f._base = new f(null, "Total"); - f._top = f._base; - f._flat = new f(null, "Flat"); - f._flatStack = []; - return f; + a.prototype.trace = function(a) { + a.enter(this._name + ": " + this._total.toFixed(2) + " ms, count: " + this._count + ", average: " + (this._total / this._count).toFixed(2) + " ms"); + for (var c in this._timers) { + this._timers[c].trace(a); + } + a.outdent(); + }; + a.trace = function(c) { + a._base.trace(c); + a._flat.trace(c); + }; + a._base = new a(null, "Total"); + a._top = a._base; + a._flat = new a(null, "Flat"); + a._flatStack = []; + return a; }(); - k.Timer = g; - g = function() { - function f(b) { - this._enabled = b; + h.Timer = a; + a = function() { + function a(c) { + this._enabled = c; this.clear(); } - Object.defineProperty(f.prototype, "counts", {get:function() { + Object.defineProperty(a.prototype, "counts", {get:function() { return this._counts; }, enumerable:!0, configurable:!0}); - f.prototype.setEnabled = function(b) { - this._enabled = b; + a.prototype.setEnabled = function(a) { + this._enabled = a; }; - f.prototype.clear = function() { - this._counts = b.ObjectUtilities.createMap(); - this._times = b.ObjectUtilities.createMap(); + a.prototype.clear = function() { + this._counts = c.ObjectUtilities.createMap(); + this._times = c.ObjectUtilities.createMap(); }; - f.prototype.toJSON = function() { + a.prototype.toJSON = function() { return{counts:this._counts, times:this._times}; }; - f.prototype.count = function(b, f, m) { - "undefined" === typeof f && (f = 1); - "undefined" === typeof m && (m = 0); + a.prototype.count = function(a, c, s) { + void 0 === c && (c = 1); + void 0 === s && (s = 0); if (this._enabled) { - return void 0 === this._counts[b] && (this._counts[b] = 0, this._times[b] = 0), this._counts[b] += f, this._times[b] += m, this._counts[b]; + return void 0 === this._counts[a] && (this._counts[a] = 0, this._times[a] = 0), this._counts[a] += c, this._times[a] += s, this._counts[a]; } }; - f.prototype.trace = function(b) { - for (var f in this._counts) { - b.writeLn(f + ": " + this._counts[f]); + a.prototype.trace = function(a) { + for (var c in this._counts) { + a.writeLn(c + ": " + this._counts[c]); } }; - f.prototype._pairToString = function(b, f) { - var m = f[0], d = f[1], a = b[m], m = m + ": " + d; - a && (m += ", " + a.toFixed(4), 1 < d && (m += " (" + (a / d).toFixed(4) + ")")); - return m; + a.prototype._pairToString = function(a, c) { + var s = c[0], l = c[1], e = a[s], s = s + ": " + l; + e && (s += ", " + e.toFixed(4), 1 < l && (s += " (" + (e / l).toFixed(4) + ")")); + return s; }; - f.prototype.toStringSorted = function() { - var b = this, f = this._times, m = [], d; - for (d in this._counts) { - m.push([d, this._counts[d]]); + a.prototype.toStringSorted = function() { + var a = this, c = this._times, s = [], l; + for (l in this._counts) { + s.push([l, this._counts[l]]); } - m.sort(function(a, c) { - return c[1] - a[1]; + s.sort(function(e, m) { + return m[1] - e[1]; }); - return m.map(function(a) { - return b._pairToString(f, a); + return s.map(function(e) { + return a._pairToString(c, e); }).join(", "); }; - f.prototype.traceSorted = function(b) { - var f = !0; - "undefined" === typeof f && (f = !1); - var m = this, d = this._times, a = [], c; - for (c in this._counts) { - a.push([c, this._counts[c]]); + a.prototype.traceSorted = function(a) { + var c = !0; + void 0 === c && (c = !1); + var s = this, l = this._times, e = [], m; + for (m in this._counts) { + e.push([m, this._counts[m]]); } - a.sort(function(a, c) { - return c[1] - a[1]; + e.sort(function(e, m) { + return m[1] - e[1]; }); - f ? b.writeLn(a.map(function(a) { - return m._pairToString(d, a); - }).join(", ")) : a.forEach(function(a) { - b.writeLn(m._pairToString(d, a)); + c ? a.writeLn(e.map(function(e) { + return s._pairToString(l, e); + }).join(", ")) : e.forEach(function(e) { + a.writeLn(s._pairToString(l, e)); }); }; - f.instance = new f(!0); - return f; + a.instance = new a(!0); + return a; }(); - k.Counter = g; - g = function() { - function b(f) { - this._samples = new Float64Array(f); + h.Counter = a; + a = function() { + function a(c) { + this._samples = new Float64Array(c); this._index = this._count = 0; } - b.prototype.push = function(b) { + a.prototype.push = function(a) { this._count < this._samples.length && this._count++; this._index++; - this._samples[this._index % this._samples.length] = b; + this._samples[this._index % this._samples.length] = a; }; - b.prototype.average = function() { - for (var b = 0, f = 0;f < this._count;f++) { - b += this._samples[f]; + a.prototype.average = function() { + for (var a = 0, c = 0;c < this._count;c++) { + a += this._samples[c]; } - return b / this._count; + return a / this._count; }; - return b; + return a; }(); - k.Average = g; - })(b.Metrics || (b.Metrics = {})); + h.Average = a; + })(c.Metrics || (c.Metrics = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - function g(a) { - for (var c = Math.max.apply(null, a), e = a.length, q = 1 << c, h = new Uint32Array(q), d = c << 16 | 65535, b = 0;b < q;b++) { - h[b] = d; +var __extends = this.__extends || function(c, h) { + function a() { + this.constructor = c; + } + for (var s in h) { + h.hasOwnProperty(s) && (c[s] = h[s]); + } + a.prototype = h.prototype; + c.prototype = new a; +}; +(function(c) { + (function(h) { + function a(b) { + for (var d = Math.max.apply(null, b), f = b.length, k = 1 << d, e = new Uint32Array(k), n = d << 16 | 65535, m = 0;m < k;m++) { + e[m] = n; } - for (var d = 0, b = 1, p = 2;b <= c;d <<= 1, ++b, p <<= 1) { - for (var n = 0;n < e;++n) { - if (a[n] === b) { - for (var m = 0, f = 0;f < b;++f) { - m = 2 * m + (d >> f & 1); + for (var n = 0, m = 1, l = 2;m <= d;n <<= 1, ++m, l <<= 1) { + for (var q = 0;q < f;++q) { + if (b[q] === m) { + for (var a = 0, t = 0;t < m;++t) { + a = 2 * a + (n >> t & 1); } - for (f = m;f < q;f += p) { - h[f] = b << 16 | n; + for (t = a;t < k;t += l) { + e[t] = m << 16 | q; } - ++d; + ++n; } } } - return{codes:h, maxBits:c}; + return{codes:e, maxBits:d}; } - var f; - (function(a) { - a[a.INIT = 0] = "INIT"; - a[a.BLOCK_0 = 1] = "BLOCK_0"; - a[a.BLOCK_1 = 2] = "BLOCK_1"; - a[a.BLOCK_2_PRE = 3] = "BLOCK_2_PRE"; - a[a.BLOCK_2 = 4] = "BLOCK_2"; - a[a.DONE = 5] = "DONE"; - a[a.ERROR = 6] = "ERROR"; - a[a.VERIFY_HEADER = 7] = "VERIFY_HEADER"; - })(f || (f = {})); - f = function() { - function e(l) { - this._buffer = new Uint8Array(1024); + var s; + (function(b) { + b[b.INIT = 0] = "INIT"; + b[b.BLOCK_0 = 1] = "BLOCK_0"; + b[b.BLOCK_1 = 2] = "BLOCK_1"; + b[b.BLOCK_2_PRE = 3] = "BLOCK_2_PRE"; + b[b.BLOCK_2 = 4] = "BLOCK_2"; + b[b.DONE = 5] = "DONE"; + b[b.ERROR = 6] = "ERROR"; + b[b.VERIFY_HEADER = 7] = "VERIFY_HEADER"; + })(s || (s = {})); + s = function() { + function b(b) { + this._error = null; + } + Object.defineProperty(b.prototype, "error", {get:function() { + return this._error; + }, enumerable:!0, configurable:!0}); + b.prototype.push = function(b) { + c.Debug.abstractMethod("Inflate.push"); + }; + b.prototype.close = function() { + }; + b.create = function(b) { + return "undefined" !== typeof SpecialInflate ? new k(b) : new v(b); + }; + b.prototype._processZLibHeader = function(b, d, f) { + if (d + 2 > f) { + return 0; + } + b = b[d] << 8 | b[d + 1]; + d = null; + 2048 !== (b & 3840) ? d = "inflate: unknown compression method" : 0 !== b % 31 ? d = "inflate: bad FCHECK" : 0 !== (b & 32) && (d = "inflate: FDICT bit set"); + return d ? (this._error = d, -1) : 2; + }; + b.inflate = function(d, f, k) { + var e = new Uint8Array(f), n = 0; + f = b.create(k); + f.onData = function(b) { + e.set(b, n); + n += b.length; + }; + f.push(d); + f.close(); + return e; + }; + return b; + }(); + h.Inflate = s; + var v = function(b) { + function d(g) { + b.call(this, g); + this._buffer = null; this._bitLength = this._bitBuffer = this._bufferPosition = this._bufferSize = 0; this._window = new Uint8Array(65794); this._windowPosition = 0; - this._state = l ? 7 : 0; + this._state = g ? 7 : 0; this._isFinalBlock = !1; this._distanceTable = this._literalTable = null; this._block0Read = 0; this._block2State = null; this._copyState = {state:0, len:0, lenBits:0, dist:0, distBits:0}; this._error = void 0; - if (!p) { - t = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]); - s = new Uint16Array(30); - m = new Uint8Array(30); - for (var q = l = 0, r = 1;30 > l;++l) { - s[l] = r, r += 1 << (m[l] = ~~((q += 2 < l ? 1 : 0) / 2)); - } - var h = new Uint8Array(288); - for (l = 0;32 > l;++l) { - h[l] = 5; - } - d = g(h.subarray(0, 32)); - a = new Uint16Array(29); - c = new Uint8Array(29); - q = l = 0; - for (r = 3;29 > l;++l) { - a[l] = r - (28 == l ? 1 : 0), r += 1 << (c[l] = ~~((q += 4 < l ? 1 : 0) / 4 % 6)); + if (!n) { + p = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]); + u = new Uint16Array(30); + l = new Uint8Array(30); + for (var f = g = 0, k = 1;30 > g;++g) { + u[g] = k, k += 1 << (l[g] = ~~((f += 2 < g ? 1 : 0) / 2)); + } + var A = new Uint8Array(288); + for (g = 0;32 > g;++g) { + A[g] = 5; + } + e = a(A.subarray(0, 32)); + m = new Uint16Array(29); + t = new Uint8Array(29); + f = g = 0; + for (k = 3;29 > g;++g) { + m[g] = k - (28 == g ? 1 : 0), k += 1 << (t[g] = ~~((f += 4 < g ? 1 : 0) / 4 % 6)); } - for (l = 0;288 > l;++l) { - h[l] = 144 > l || 279 < l ? 8 : 256 > l ? 9 : 7; + for (g = 0;288 > g;++g) { + A[g] = 144 > g || 279 < g ? 8 : 256 > g ? 9 : 7; } - n = g(h); - p = !0; + q = a(A); + n = !0; } } - Object.defineProperty(e.prototype, "error", {get:function() { + __extends(d, b); + Object.defineProperty(d.prototype, "error", {get:function() { return this._error; }, enumerable:!0, configurable:!0}); - e.prototype.push = function(a) { - if (this._buffer.length < this._bufferSize + a.length) { - var c = new Uint8Array(this._bufferSize + a.length); - c.set(this._buffer); - this._buffer = c; + d.prototype.push = function(b) { + if (!this._buffer || this._buffer.length < this._bufferSize + b.length) { + var d = new Uint8Array(this._bufferSize + b.length); + this._buffer && d.set(this._buffer); + this._buffer = d; } - this._buffer.set(a, this._bufferSize); - this._bufferSize += a.length; + this._buffer.set(b, this._bufferSize); + this._bufferSize += b.length; this._bufferPosition = 0; - a = !1; + b = !1; do { - c = this._windowPosition; - if (0 === this._state && (a = this._decodeInitState())) { + d = this._windowPosition; + if (0 === this._state && (b = this._decodeInitState())) { break; } switch(this._state) { case 1: - a = this._decodeBlock0(); + b = this._decodeBlock0(); break; case 3: - if (a = this._decodeBlock2Pre()) { + if (b = this._decodeBlock2Pre()) { break; } ; case 2: ; case 4: - a = this._decodeBlock(); + b = this._decodeBlock(); break; case 6: ; @@ -2801,1619 +2923,1812 @@ this._bufferPosition = this._bufferSize; break; case 7: - a = this._verifyZlibHeader(); + var g = this._processZLibHeader(this._buffer, this._bufferPosition, this._bufferSize); + 0 < g ? (this._bufferPosition += g, this._state = 0) : 0 === g ? b = !0 : this._state = 6; } - if (0 < this._windowPosition - c) { - this.onData(this._window.subarray(c, this._windowPosition)); + if (0 < this._windowPosition - d) { + this.onData(this._window.subarray(d, this._windowPosition)); } - 65536 <= this._windowPosition && (this._window.set(this._window.subarray(this._windowPosition - 32768, this._windowPosition)), this._windowPosition = 32768); - } while (!a && this._bufferPosition < this._bufferSize); - this._bufferPosition < this._bufferSize ? (this._buffer.set(this._buffer.subarray(this._bufferPosition, this._bufferSize)), this._bufferSize -= this._bufferPosition) : this._bufferSize = 0; - }; - e.prototype._verifyZlibHeader = function() { - var a = this._bufferPosition; - if (a + 2 > this._bufferSize) { - return!0; - } - var c = this._buffer, c = c[a] << 8 | c[a + 1]; - this._bufferPosition = a + 2; - a = null; - 2048 !== (c & 3840) ? a = "inflate: unknown compression method" : 0 !== c % 31 ? a = "inflate: bad FCHECK" : 0 !== (c & 32) && (a = "inflate: FDICT bit set"); - a ? (this._error = a, this._state = 6) : this._state = 0; + 65536 <= this._windowPosition && ("copyWithin" in this._buffer ? this._window.copyWithin(0, this._windowPosition - 32768, this._windowPosition) : this._window.set(this._window.subarray(this._windowPosition - 32768, this._windowPosition)), this._windowPosition = 32768); + } while (!b && this._bufferPosition < this._bufferSize); + this._bufferPosition < this._bufferSize ? ("copyWithin" in this._buffer ? this._buffer.copyWithin(0, this._bufferPosition, this._bufferSize) : this._buffer.set(this._buffer.subarray(this._bufferPosition, this._bufferSize)), this._bufferSize -= this._bufferPosition) : this._bufferSize = 0; }; - e.prototype._decodeInitState = function() { + d.prototype._decodeInitState = function() { if (this._isFinalBlock) { return this._state = 5, !1; } - var a = this._buffer, c = this._bufferSize, e = this._bitBuffer, h = this._bitLength, l = this._bufferPosition; - if (3 > (c - l << 3) + h) { + var b = this._buffer, d = this._bufferSize, g = this._bitBuffer, f = this._bitLength, k = this._bufferPosition; + if (3 > (d - k << 3) + f) { return!0; } - 3 > h && (e |= a[l++] << h, h += 8); - var q = e & 7, e = e >> 3, h = h - 3; - switch(q >> 1) { + 3 > f && (g |= b[k++] << f, f += 8); + var n = g & 7, g = g >> 3, f = f - 3; + switch(n >> 1) { case 0: - h = e = 0; - if (4 > c - l) { + f = g = 0; + if (4 > d - k) { return!0; } - var b = a[l] | a[l + 1] << 8, a = a[l + 2] | a[l + 3] << 8, l = l + 4; - if (65535 !== (b ^ a)) { + var m = b[k] | b[k + 1] << 8, b = b[k + 2] | b[k + 3] << 8, k = k + 4; + if (65535 !== (m ^ b)) { this._error = "inflate: invalid block 0 length"; - a = 6; + b = 6; break; } - 0 === b ? a = 0 : (this._block0Read = b, a = 1); + 0 === m ? b = 0 : (this._block0Read = m, b = 1); break; case 1: - a = 2; - this._literalTable = n; - this._distanceTable = d; + b = 2; + this._literalTable = q; + this._distanceTable = e; break; case 2: - if (26 > (c - l << 3) + h) { + if (26 > (d - k << 3) + f) { return!0; } - for (;14 > h;) { - e |= a[l++] << h, h += 8; + for (;14 > f;) { + g |= b[k++] << f, f += 8; } - b = (e >> 10 & 15) + 4; - if ((c - l << 3) + h < 14 + 3 * b) { + m = (g >> 10 & 15) + 4; + if ((d - k << 3) + f < 14 + 3 * m) { return!0; } - for (var c = {numLiteralCodes:(e & 31) + 257, numDistanceCodes:(e >> 5 & 31) + 1, codeLengthTable:void 0, bitLengths:void 0, codesRead:0, dupBits:0}, e = e >> 14, h = h - 14, p = new Uint8Array(19), m = 0;m < b;++m) { - 3 > h && (e |= a[l++] << h, h += 8), p[t[m]] = e & 7, e >>= 3, h -= 3; + for (var d = {numLiteralCodes:(g & 31) + 257, numDistanceCodes:(g >> 5 & 31) + 1, codeLengthTable:void 0, bitLengths:void 0, codesRead:0, dupBits:0}, g = g >> 14, f = f - 14, l = new Uint8Array(19), t = 0;t < m;++t) { + 3 > f && (g |= b[k++] << f, f += 8), l[p[t]] = g & 7, g >>= 3, f -= 3; } - for (;19 > m;m++) { - p[t[m]] = 0; + for (;19 > t;t++) { + l[p[t]] = 0; } - c.bitLengths = new Uint8Array(c.numLiteralCodes + c.numDistanceCodes); - c.codeLengthTable = g(p); - this._block2State = c; - a = 3; + d.bitLengths = new Uint8Array(d.numLiteralCodes + d.numDistanceCodes); + d.codeLengthTable = a(l); + this._block2State = d; + b = 3; break; default: return this._error = "inflate: unsupported block type", !1; } - this._isFinalBlock = !!(q & 1); - this._state = a; - this._bufferPosition = l; - this._bitBuffer = e; - this._bitLength = h; + this._isFinalBlock = !!(n & 1); + this._state = b; + this._bufferPosition = k; + this._bitBuffer = g; + this._bitLength = f; return!1; }; - e.prototype._decodeBlock0 = function() { - var a = this._bufferPosition, c = this._windowPosition, e = this._block0Read, h = 65794 - c, l = this._bufferSize - a, q = l < e, d = Math.min(h, l, e); - this._window.set(this._buffer.subarray(a, a + d), c); - this._windowPosition = c + d; - this._bufferPosition = a + d; - this._block0Read = e - d; - return e === d ? (this._state = 0, !1) : q && h < l; - }; - e.prototype._readBits = function(a) { - var c = this._bitBuffer, e = this._bitLength; - if (a > e) { - var h = this._bufferPosition, l = this._bufferSize; + d.prototype._decodeBlock0 = function() { + var b = this._bufferPosition, d = this._windowPosition, g = this._block0Read, f = 65794 - d, k = this._bufferSize - b, e = k < g, n = Math.min(f, k, g); + this._window.set(this._buffer.subarray(b, b + n), d); + this._windowPosition = d + n; + this._bufferPosition = b + n; + this._block0Read = g - n; + return g === n ? (this._state = 0, !1) : e && f < k; + }; + d.prototype._readBits = function(b) { + var d = this._bitBuffer, g = this._bitLength; + if (b > g) { + var f = this._bufferPosition, k = this._bufferSize; do { - if (h >= l) { - return this._bufferPosition = h, this._bitBuffer = c, this._bitLength = e, -1; + if (f >= k) { + return this._bufferPosition = f, this._bitBuffer = d, this._bitLength = g, -1; } - c |= this._buffer[h++] << e; - e += 8; - } while (a > e); - this._bufferPosition = h; - } - this._bitBuffer = c >> a; - this._bitLength = e - a; - return c & (1 << a) - 1; - }; - e.prototype._readCode = function(a) { - var c = this._bitBuffer, e = this._bitLength, h = a.maxBits; - if (h > e) { - var l = this._bufferPosition, q = this._bufferSize; + d |= this._buffer[f++] << g; + g += 8; + } while (b > g); + this._bufferPosition = f; + } + this._bitBuffer = d >> b; + this._bitLength = g - b; + return d & (1 << b) - 1; + }; + d.prototype._readCode = function(b) { + var d = this._bitBuffer, g = this._bitLength, f = b.maxBits; + if (f > g) { + var k = this._bufferPosition, e = this._bufferSize; do { - if (l >= q) { - return this._bufferPosition = l, this._bitBuffer = c, this._bitLength = e, -1; + if (k >= e) { + return this._bufferPosition = k, this._bitBuffer = d, this._bitLength = g, -1; } - c |= this._buffer[l++] << e; - e += 8; - } while (h > e); - this._bufferPosition = l; - } - a = a.codes[c & (1 << h) - 1]; - h = a >> 16; - if (a & 32768) { + d |= this._buffer[k++] << g; + g += 8; + } while (f > g); + this._bufferPosition = k; + } + b = b.codes[d & (1 << f) - 1]; + f = b >> 16; + if (b & 32768) { return this._error = "inflate: invalid encoding", this._state = 6, -1; } - this._bitBuffer = c >> h; - this._bitLength = e - h; - return a & 65535; - }; - e.prototype._decodeBlock2Pre = function() { - var a = this._block2State, c = a.numLiteralCodes + a.numDistanceCodes, e = a.bitLengths, h = a.codesRead, l = 0 < h ? e[h - 1] : 0, q = a.codeLengthTable, d; - if (0 < a.dupBits) { - d = this._readBits(a.dupBits); - if (0 > d) { + this._bitBuffer = d >> f; + this._bitLength = g - f; + return b & 65535; + }; + d.prototype._decodeBlock2Pre = function() { + var b = this._block2State, d = b.numLiteralCodes + b.numDistanceCodes, g = b.bitLengths, f = b.codesRead, k = 0 < f ? g[f - 1] : 0, e = b.codeLengthTable, n; + if (0 < b.dupBits) { + n = this._readBits(b.dupBits); + if (0 > n) { return!0; } - for (;d--;) { - e[h++] = l; + for (;n--;) { + g[f++] = k; } - a.dupBits = 0; + b.dupBits = 0; } - for (;h < c;) { - var b = this._readCode(q); - if (0 > b) { - return a.codesRead = h, !0; + for (;f < d;) { + var m = this._readCode(e); + if (0 > m) { + return b.codesRead = f, !0; } - if (16 > b) { - e[h++] = l = b; + if (16 > m) { + g[f++] = k = m; } else { - var p; - switch(b) { + var l; + switch(m) { case 16: - p = 2; - d = 3; - b = l; + l = 2; + n = 3; + m = k; break; case 17: - d = p = 3; - b = 0; + n = l = 3; + m = 0; break; case 18: - p = 7, d = 11, b = 0; + l = 7, n = 11, m = 0; } - for (;d--;) { - e[h++] = b; + for (;n--;) { + g[f++] = m; } - d = this._readBits(p); - if (0 > d) { - return a.codesRead = h, a.dupBits = p, !0; + n = this._readBits(l); + if (0 > n) { + return b.codesRead = f, b.dupBits = l, !0; } - for (;d--;) { - e[h++] = b; + for (;n--;) { + g[f++] = m; } - l = b; + k = m; } } - this._literalTable = g(e.subarray(0, a.numLiteralCodes)); - this._distanceTable = g(e.subarray(a.numLiteralCodes)); + this._literalTable = a(g.subarray(0, b.numLiteralCodes)); + this._distanceTable = a(g.subarray(b.numLiteralCodes)); this._state = 4; this._block2State = null; return!1; }; - e.prototype._decodeBlock = function() { - var e = this._literalTable, l = this._distanceTable, q = this._window, h = this._windowPosition, d = this._copyState, b, p, n, f; - if (0 !== d.state) { - switch(d.state) { + d.prototype._decodeBlock = function() { + var b = this._literalTable, d = this._distanceTable, g = this._window, f = this._windowPosition, k = this._copyState, e, n, q, a; + if (0 !== k.state) { + switch(k.state) { case 1: - if (0 > (b = this._readBits(d.lenBits))) { + if (0 > (e = this._readBits(k.lenBits))) { return!0; } - d.len += b; - d.state = 2; + k.len += e; + k.state = 2; case 2: - if (0 > (b = this._readCode(l))) { + if (0 > (e = this._readCode(d))) { return!0; } - d.distBits = m[b]; - d.dist = s[b]; - d.state = 3; + k.distBits = l[e]; + k.dist = u[e]; + k.state = 3; case 3: - b = 0; - if (0 < d.distBits && 0 > (b = this._readBits(d.distBits))) { + e = 0; + if (0 < k.distBits && 0 > (e = this._readBits(k.distBits))) { return!0; } - f = d.dist + b; - p = d.len; - for (b = h - f;p--;) { - q[h++] = q[b++]; - } - d.state = 0; - if (65536 <= h) { - return this._windowPosition = h, !1; + a = k.dist + e; + n = k.len; + for (e = f - a;n--;) { + g[f++] = g[e++]; + } + k.state = 0; + if (65536 <= f) { + return this._windowPosition = f, !1; } break; } } do { - b = this._readCode(e); - if (0 > b) { - return this._windowPosition = h, !0; - } - if (256 > b) { - q[h++] = b; - } else { - if (256 < b) { - this._windowPosition = h; - b -= 257; - n = c[b]; - p = a[b]; - b = 0 === n ? 0 : this._readBits(n); - if (0 > b) { - return d.state = 1, d.len = p, d.lenBits = n, !0; - } - p += b; - b = this._readCode(l); - if (0 > b) { - return d.state = 2, d.len = p, !0; - } - n = m[b]; - f = s[b]; - b = 0 === n ? 0 : this._readBits(n); - if (0 > b) { - return d.state = 3, d.len = p, d.dist = f, d.distBits = n, !0; - } - f += b; - for (b = h - f;p--;) { - q[h++] = q[b++]; + e = this._readCode(b); + if (0 > e) { + return this._windowPosition = f, !0; + } + if (256 > e) { + g[f++] = e; + } else { + if (256 < e) { + this._windowPosition = f; + e -= 257; + q = t[e]; + n = m[e]; + e = 0 === q ? 0 : this._readBits(q); + if (0 > e) { + return k.state = 1, k.len = n, k.lenBits = q, !0; + } + n += e; + e = this._readCode(d); + if (0 > e) { + return k.state = 2, k.len = n, !0; + } + q = l[e]; + a = u[e]; + e = 0 === q ? 0 : this._readBits(q); + if (0 > e) { + return k.state = 3, k.len = n, k.dist = a, k.distBits = q, !0; + } + a += e; + for (e = f - a;n--;) { + g[f++] = g[e++]; } } else { this._state = 0; break; } } - } while (65536 > h); - this._windowPosition = h; + } while (65536 > f); + this._windowPosition = f; return!1; }; - e.inflate = function(a, c, q) { - var h = new Uint8Array(c), d = 0; - c = new e(q); - c.onData = function(a) { - h.set(a, d); - d += a.length; - }; - c.push(a); - return h; + return d; + }(s), p, u, l, e, m, t, q, n = !1, k = function(b) { + function d(g) { + b.call(this, g); + this._verifyHeader = g; + this._specialInflate = new SpecialInflate; + this._specialInflate.onData = function(b) { + this.onData(b); + }.bind(this); + } + __extends(d, b); + d.prototype.push = function(b) { + if (this._verifyHeader) { + var d; + this._buffer ? (d = new Uint8Array(this._buffer.length + b.length), d.set(this._buffer), d.set(b, this._buffer.length), this._buffer = null) : d = new Uint8Array(b); + var g = this._processZLibHeader(d, 0, d.length); + if (0 === g) { + this._buffer = d; + return; + } + this._verifyHeader = !0; + 0 < g && (b = d.subarray(g)); + } + this._error || this._specialInflate.push(b); }; - return e; - }(); - b.Inflate = f; - var t, s, m, d, a, c, n, p = !1, e; - (function(a) { - a[a.WRITE = 0] = "WRITE"; - a[a.DONE = 1] = "DONE"; - a[a.ZLIB_HEADER = 2] = "ZLIB_HEADER"; - })(e || (e = {})); - var q = function() { - function a() { + d.prototype.close = function() { + this._specialInflate && (this._specialInflate.close(), this._specialInflate = null); + }; + return d; + }(s), f; + (function(b) { + b[b.WRITE = 0] = "WRITE"; + b[b.DONE = 1] = "DONE"; + b[b.ZLIB_HEADER = 2] = "ZLIB_HEADER"; + })(f || (f = {})); + var d = function() { + function b() { this.a = 1; this.b = 0; } - a.prototype.update = function(a, c, e) { - for (var h = this.a, l = this.b;c < e;++c) { - h = (h + (a[c] & 255)) % 65521, l = (l + h) % 65521; + b.prototype.update = function(b, d, f) { + for (var k = this.a, e = this.b;d < f;++d) { + k = (k + (b[d] & 255)) % 65521, e = (e + k) % 65521; } - this.a = h; - this.b = l; + this.a = k; + this.b = e; }; - a.prototype.getChecksum = function() { + b.prototype.getChecksum = function() { return this.b << 16 | this.a; }; - return a; + return b; }(); - b.Adler32 = q; - e = function() { - function a(c) { - this._state = (this._writeZlibHeader = c) ? 2 : 0; - this._adler32 = c ? new q : null; + h.Adler32 = d; + f = function() { + function b(b) { + this._state = (this._writeZlibHeader = b) ? 2 : 0; + this._adler32 = b ? new d : null; } - a.prototype.push = function(a) { + b.prototype.push = function(b) { 2 === this._state && (this.onData(new Uint8Array([120, 156])), this._state = 0); - for (var c = a.length, e = new Uint8Array(c + 5 * Math.ceil(c / 65535)), h = 0, l = 0;65535 < c;) { - e.set(new Uint8Array([0, 255, 255, 0, 0]), h), h += 5, e.set(a.subarray(l, l + 65535), h), l += 65535, h += 65535, c -= 65535; + for (var d = b.length, f = new Uint8Array(d + 5 * Math.ceil(d / 65535)), k = 0, e = 0;65535 < d;) { + f.set(new Uint8Array([0, 255, 255, 0, 0]), k), k += 5, f.set(b.subarray(e, e + 65535), k), e += 65535, k += 65535, d -= 65535; } - e.set(new Uint8Array([0, c & 255, c >> 8 & 255, ~c & 255, ~c >> 8 & 255]), h); - e.set(a.subarray(l, c), h + 5); - this.onData(e); - this._adler32 && this._adler32.update(a, 0, c); + f.set(new Uint8Array([0, d & 255, d >> 8 & 255, ~d & 255, ~d >> 8 & 255]), k); + f.set(b.subarray(e, d), k + 5); + this.onData(f); + this._adler32 && this._adler32.update(b, 0, d); }; - a.prototype.finish = function() { + b.prototype.finish = function() { this._state = 1; this.onData(new Uint8Array([1, 0, 0, 255, 255])); if (this._adler32) { - var a = this._adler32.getChecksum(); - this.onData(new Uint8Array([a & 255, a >> 8 & 255, a >> 16 & 255, a >>> 24 & 255])); + var b = this._adler32.getChecksum(); + this.onData(new Uint8Array([b & 255, b >> 8 & 255, b >> 16 & 255, b >>> 24 & 255])); } }; - return a; + return b; }(); - b.Deflate = e; - })(b.ArrayUtilities || (b.ArrayUtilities = {})); + h.Deflate = f; + })(c.ArrayUtilities || (c.ArrayUtilities = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - function g() { - t("throwEOFError"); - } - function f(a) { - return "string" === typeof a ? a : void 0 == a ? null : a + ""; - } - var t = b.Debug.notImplemented, s = b.StringUtilities.utf8decode, m = b.StringUtilities.utf8encode, d = b.NumberUtilities.clamp, a = function() { - return function(a, c, l) { - this.buffer = a; - this.length = c; - this.littleEndian = l; +(function(c) { + (function(h) { + function a(k, f) { + k !== l(k, 0, f) && throwError("RangeError", Errors.ParamRangeError); + } + function s(k) { + return "string" === typeof k ? k : void 0 == k ? null : k + ""; + } + var v = c.Debug.notImplemented, p = c.StringUtilities.utf8decode, u = c.StringUtilities.utf8encode, l = c.NumberUtilities.clamp, e = function() { + return function(k, f, d) { + this.buffer = k; + this.length = f; + this.littleEndian = d; }; }(); - k.PlainObjectDataBuffer = a; - for (var c = new Uint32Array(33), n = 1, p = 0;32 >= n;n++) { - c[n] = p = p << 1 | 1; + h.PlainObjectDataBuffer = e; + for (var m = new Uint32Array(33), t = 1, q = 0;32 >= t;t++) { + m[t] = q = q << 1 | 1; } - n = function() { - function e(a) { - "undefined" === typeof a && (a = e.INITIAL_SIZE); - this._buffer || (this._buffer = new ArrayBuffer(a), this._position = this._length = 0, this._updateViews(), this._littleEndian = e._nativeLittleEndian, this._bitLength = this._bitBuffer = 0); - } - e.FromArrayBuffer = function(a, c) { - "undefined" === typeof c && (c = -1); - var d = Object.create(e.prototype); - d._buffer = a; - d._length = -1 === c ? a.byteLength : c; - d._position = 0; - d._updateViews(); - d._littleEndian = e._nativeLittleEndian; - d._bitBuffer = 0; - d._bitLength = 0; - return d; + var n; + (function(k) { + k[k.U8 = 1] = "U8"; + k[k.I32 = 2] = "I32"; + k[k.F32 = 4] = "F32"; + })(n || (n = {})); + t = function() { + function k(f) { + void 0 === f && (f = k.INITIAL_SIZE); + this._buffer || (this._buffer = new ArrayBuffer(f), this._position = this._length = 0, this._resetViews(), this._littleEndian = k._nativeLittleEndian, this._bitLength = this._bitBuffer = 0); + } + k.FromArrayBuffer = function(f, d) { + void 0 === d && (d = -1); + var b = Object.create(k.prototype); + b._buffer = f; + b._length = -1 === d ? f.byteLength : d; + b._position = 0; + b._resetViews(); + b._littleEndian = k._nativeLittleEndian; + b._bitBuffer = 0; + b._bitLength = 0; + return b; }; - e.FromPlainObject = function(a) { - var c = e.FromArrayBuffer(a.buffer, a.length); - c._littleEndian = a.littleEndian; - return c; + k.FromPlainObject = function(f) { + var d = k.FromArrayBuffer(f.buffer, f.length); + d._littleEndian = f.littleEndian; + return d; }; - e.prototype.toPlainObject = function() { - return new a(this._buffer, this._length, this._littleEndian); + k.prototype.toPlainObject = function() { + return new e(this._buffer, this._length, this._littleEndian); }; - e.prototype._updateViews = function() { + k.prototype._resetViews = function() { this._u8 = new Uint8Array(this._buffer); - 0 === (this._buffer.byteLength & 3) && (this._i32 = new Int32Array(this._buffer), this._f32 = new Float32Array(this._buffer)); + this._f32 = this._i32 = null; + }; + k.prototype._requestViews = function(f) { + 0 === (this._buffer.byteLength & 3) && (null === this._i32 && f & 2 && (this._i32 = new Int32Array(this._buffer)), null === this._f32 && f & 4 && (this._f32 = new Float32Array(this._buffer))); }; - e.prototype.getBytes = function() { + k.prototype.getBytes = function() { return new Uint8Array(this._buffer, 0, this._length); }; - e.prototype._ensureCapacity = function(a) { - var c = this._buffer; - if (c.byteLength < a) { - for (var d = Math.max(c.byteLength, 1);d < a;) { - d *= 2; - } - a = e._arrayBufferPool.acquire(d); - d = this._u8; - this._buffer = a; - this._updateViews(); - this._u8.set(d); - e._arrayBufferPool.release(c); + k.prototype._ensureCapacity = function(f) { + var d = this._buffer; + if (d.byteLength < f) { + for (var b = Math.max(d.byteLength, 1);b < f;) { + b *= 2; + } + f = k._arrayBufferPool.acquire(b); + b = this._u8; + this._buffer = f; + this._resetViews(); + this._u8.set(b); + k._arrayBufferPool.release(d); } }; - e.prototype.clear = function() { + k.prototype.clear = function() { this._position = this._length = 0; }; - e.prototype.readBoolean = function() { + k.prototype.readBoolean = function() { return 0 !== this.readUnsignedByte(); }; - e.prototype.readByte = function() { + k.prototype.readByte = function() { return this.readUnsignedByte() << 24 >> 24; }; - e.prototype.readUnsignedByte = function() { - this._position + 1 > this._length && g(); + k.prototype.readUnsignedByte = function() { + this._position + 1 > this._length && throwError("EOFError", Errors.EOFError); return this._u8[this._position++]; }; - e.prototype.readBytes = function(a, c, e) { - "undefined" === typeof c && (c = 0); - "undefined" === typeof e && (e = 0); - var d = this._position; - c || (c = 0); - e || (e = this._length - d); - d + e > this._length && g(); - a.length < c + e && (a._ensureCapacity(c + e), a.length = c + e); - a._u8.set(new Uint8Array(this._buffer, d, e), c); - this._position += e; + k.prototype.readBytes = function(f, d, b) { + void 0 === d && (d = 0); + void 0 === b && (b = 0); + var g = this._position; + d || (d = 0); + b || (b = this._length - g); + g + b > this._length && throwError("EOFError", Errors.EOFError); + f.length < d + b && (f._ensureCapacity(d + b), f.length = d + b); + f._u8.set(new Uint8Array(this._buffer, g, b), d); + this._position += b; }; - e.prototype.readShort = function() { + k.prototype.readShort = function() { return this.readUnsignedShort() << 16 >> 16; }; - e.prototype.readUnsignedShort = function() { - var a = this._u8, c = this._position; - c + 2 > this._length && g(); - var e = a[c + 0], a = a[c + 1]; - this._position = c + 2; - return this._littleEndian ? a << 8 | e : e << 8 | a; - }; - e.prototype.readInt = function() { - var a = this._u8, c = this._position; - c + 4 > this._length && g(); - var e = a[c + 0], d = a[c + 1], b = a[c + 2], a = a[c + 3]; - this._position = c + 4; - return this._littleEndian ? a << 24 | b << 16 | d << 8 | e : e << 24 | d << 16 | b << 8 | a; + k.prototype.readUnsignedShort = function() { + var f = this._u8, d = this._position; + d + 2 > this._length && throwError("EOFError", Errors.EOFError); + var b = f[d + 0], f = f[d + 1]; + this._position = d + 2; + return this._littleEndian ? f << 8 | b : b << 8 | f; + }; + k.prototype.readInt = function() { + var f = this._u8, d = this._position; + d + 4 > this._length && throwError("EOFError", Errors.EOFError); + var b = f[d + 0], g = f[d + 1], k = f[d + 2], f = f[d + 3]; + this._position = d + 4; + return this._littleEndian ? f << 24 | k << 16 | g << 8 | b : b << 24 | g << 16 | k << 8 | f; }; - e.prototype.readUnsignedInt = function() { + k.prototype.readUnsignedInt = function() { return this.readInt() >>> 0; }; - e.prototype.readFloat = function() { - var a = this._position; - a + 4 > this._length && g(); - this._position = a + 4; - if (this._littleEndian && 0 === (a & 3) && this._f32) { - return this._f32[a >> 2]; - } - var c = this._u8, e = b.IntegerUtilities.u8; - this._littleEndian ? (e[0] = c[a + 0], e[1] = c[a + 1], e[2] = c[a + 2], e[3] = c[a + 3]) : (e[3] = c[a + 0], e[2] = c[a + 1], e[1] = c[a + 2], e[0] = c[a + 3]); - return b.IntegerUtilities.f32[0]; - }; - e.prototype.readDouble = function() { - var a = this._u8, c = this._position; - c + 8 > this._length && g(); - var e = b.IntegerUtilities.u8; - this._littleEndian ? (e[0] = a[c + 0], e[1] = a[c + 1], e[2] = a[c + 2], e[3] = a[c + 3], e[4] = a[c + 4], e[5] = a[c + 5], e[6] = a[c + 6], e[7] = a[c + 7]) : (e[0] = a[c + 7], e[1] = a[c + 6], e[2] = a[c + 5], e[3] = a[c + 4], e[4] = a[c + 3], e[5] = a[c + 2], e[6] = a[c + 1], e[7] = a[c + 0]); - this._position = c + 8; - return b.IntegerUtilities.f64[0]; - }; - e.prototype.writeBoolean = function(a) { - this.writeByte(a ? 1 : 0); - }; - e.prototype.writeByte = function(a) { - var c = this._position + 1; - this._ensureCapacity(c); - this._u8[this._position++] = a; - c > this._length && (this._length = c); - }; - e.prototype.writeUnsignedByte = function(a) { - var c = this._position + 1; - this._ensureCapacity(c); - this._u8[this._position++] = a; - c > this._length && (this._length = c); - }; - e.prototype.writeRawBytes = function(a) { - var c = this._position + a.length; - this._ensureCapacity(c); - this._u8.set(a, this._position); - this._position = c; - c > this._length && (this._length = c); - }; - e.prototype.writeBytes = function(a, c, e) { - "undefined" === typeof c && (c = 0); - "undefined" === typeof e && (e = 0); - 2 > arguments.length && (c = 0); - 3 > arguments.length && (e = 0); - c !== d(c, 0, a.length) && t("throwRangeError"); - var b = c + e; - b !== d(b, 0, a.length) && t("throwRangeError"); - 0 === e && (e = a.length - c); - this.writeRawBytes(new Int8Array(a._buffer, c, e)); - }; - e.prototype.writeShort = function(a) { - this.writeUnsignedShort(a); - }; - e.prototype.writeUnsignedShort = function(a) { - var c = this._position; - this._ensureCapacity(c + 2); - var e = this._u8; - this._littleEndian ? (e[c + 0] = a, e[c + 1] = a >> 8) : (e[c + 0] = a >> 8, e[c + 1] = a); - this._position = c += 2; - c > this._length && (this._length = c); - }; - e.prototype.writeInt = function(a) { - this.writeUnsignedInt(a); - }; - e.prototype.write4Ints = function(a, c, e, d) { - this.write4UnsignedInts(a, c, e, d); - }; - e.prototype.writeUnsignedInt = function(a) { - var c = this._position; - this._ensureCapacity(c + 4); - if (this._littleEndian === e._nativeLittleEndian && 0 === (c & 3) && this._i32) { - this._i32[c >> 2] = a; + k.prototype.readFloat = function() { + var f = this._position; + f + 4 > this._length && throwError("EOFError", Errors.EOFError); + this._position = f + 4; + this._requestViews(4); + if (this._littleEndian && 0 === (f & 3) && this._f32) { + return this._f32[f >> 2]; + } + var d = this._u8, b = c.IntegerUtilities.u8; + this._littleEndian ? (b[0] = d[f + 0], b[1] = d[f + 1], b[2] = d[f + 2], b[3] = d[f + 3]) : (b[3] = d[f + 0], b[2] = d[f + 1], b[1] = d[f + 2], b[0] = d[f + 3]); + return c.IntegerUtilities.f32[0]; + }; + k.prototype.readDouble = function() { + var f = this._u8, d = this._position; + d + 8 > this._length && throwError("EOFError", Errors.EOFError); + var b = c.IntegerUtilities.u8; + this._littleEndian ? (b[0] = f[d + 0], b[1] = f[d + 1], b[2] = f[d + 2], b[3] = f[d + 3], b[4] = f[d + 4], b[5] = f[d + 5], b[6] = f[d + 6], b[7] = f[d + 7]) : (b[0] = f[d + 7], b[1] = f[d + 6], b[2] = f[d + 5], b[3] = f[d + 4], b[4] = f[d + 3], b[5] = f[d + 2], b[6] = f[d + 1], b[7] = f[d + 0]); + this._position = d + 8; + return c.IntegerUtilities.f64[0]; + }; + k.prototype.writeBoolean = function(f) { + this.writeByte(f ? 1 : 0); + }; + k.prototype.writeByte = function(f) { + var d = this._position + 1; + this._ensureCapacity(d); + this._u8[this._position++] = f; + d > this._length && (this._length = d); + }; + k.prototype.writeUnsignedByte = function(f) { + var d = this._position + 1; + this._ensureCapacity(d); + this._u8[this._position++] = f; + d > this._length && (this._length = d); + }; + k.prototype.writeRawBytes = function(f) { + var d = this._position + f.length; + this._ensureCapacity(d); + this._u8.set(f, this._position); + this._position = d; + d > this._length && (this._length = d); + }; + k.prototype.writeBytes = function(f, d, b) { + void 0 === d && (d = 0); + void 0 === b && (b = 0); + c.isNullOrUndefined(f) && throwError("TypeError", Errors.NullPointerError, "bytes"); + 2 > arguments.length && (d = 0); + 3 > arguments.length && (b = 0); + a(d, f.length); + a(d + b, f.length); + 0 === b && (b = f.length - d); + this.writeRawBytes(new Int8Array(f._buffer, d, b)); + }; + k.prototype.writeShort = function(f) { + this.writeUnsignedShort(f); + }; + k.prototype.writeUnsignedShort = function(f) { + var d = this._position; + this._ensureCapacity(d + 2); + var b = this._u8; + this._littleEndian ? (b[d + 0] = f, b[d + 1] = f >> 8) : (b[d + 0] = f >> 8, b[d + 1] = f); + this._position = d += 2; + d > this._length && (this._length = d); + }; + k.prototype.writeInt = function(f) { + this.writeUnsignedInt(f); + }; + k.prototype.write2Ints = function(f, d) { + this.write2UnsignedInts(f, d); + }; + k.prototype.write4Ints = function(f, d, b, g) { + this.write4UnsignedInts(f, d, b, g); + }; + k.prototype.writeUnsignedInt = function(f) { + var d = this._position; + this._ensureCapacity(d + 4); + this._requestViews(2); + if (this._littleEndian === k._nativeLittleEndian && 0 === (d & 3) && this._i32) { + this._i32[d >> 2] = f; } else { - var d = this._u8; - this._littleEndian ? (d[c + 0] = a, d[c + 1] = a >> 8, d[c + 2] = a >> 16, d[c + 3] = a >> 24) : (d[c + 0] = a >> 24, d[c + 1] = a >> 16, d[c + 2] = a >> 8, d[c + 3] = a); + var b = this._u8; + this._littleEndian ? (b[d + 0] = f, b[d + 1] = f >> 8, b[d + 2] = f >> 16, b[d + 3] = f >> 24) : (b[d + 0] = f >> 24, b[d + 1] = f >> 16, b[d + 2] = f >> 8, b[d + 3] = f); } - this._position = c += 4; - c > this._length && (this._length = c); + this._position = d += 4; + d > this._length && (this._length = d); + }; + k.prototype.write2UnsignedInts = function(f, d) { + var b = this._position; + this._ensureCapacity(b + 8); + this._requestViews(2); + this._littleEndian === k._nativeLittleEndian && 0 === (b & 3) && this._i32 ? (this._i32[(b >> 2) + 0] = f, this._i32[(b >> 2) + 1] = d, this._position = b += 8, b > this._length && (this._length = b)) : (this.writeUnsignedInt(f), this.writeUnsignedInt(d)); + }; + k.prototype.write4UnsignedInts = function(f, d, b, g) { + var r = this._position; + this._ensureCapacity(r + 16); + this._requestViews(2); + this._littleEndian === k._nativeLittleEndian && 0 === (r & 3) && this._i32 ? (this._i32[(r >> 2) + 0] = f, this._i32[(r >> 2) + 1] = d, this._i32[(r >> 2) + 2] = b, this._i32[(r >> 2) + 3] = g, this._position = r += 16, r > this._length && (this._length = r)) : (this.writeUnsignedInt(f), this.writeUnsignedInt(d), this.writeUnsignedInt(b), this.writeUnsignedInt(g)); }; - e.prototype.write4UnsignedInts = function(a, c, d, b) { - var p = this._position; - this._ensureCapacity(p + 16); - this._littleEndian === e._nativeLittleEndian && 0 === (p & 3) && this._i32 ? (this._i32[(p >> 2) + 0] = a, this._i32[(p >> 2) + 1] = c, this._i32[(p >> 2) + 2] = d, this._i32[(p >> 2) + 3] = b, this._position = p += 16, p > this._length && (this._length = p)) : (this.writeUnsignedInt(a), this.writeUnsignedInt(c), this.writeUnsignedInt(d), this.writeUnsignedInt(b)); - }; - e.prototype.writeFloat = function(a) { - var c = this._position; - this._ensureCapacity(c + 4); - if (this._littleEndian === e._nativeLittleEndian && 0 === (c & 3) && this._f32) { - this._f32[c >> 2] = a; + k.prototype.writeFloat = function(f) { + var d = this._position; + this._ensureCapacity(d + 4); + this._requestViews(4); + if (this._littleEndian === k._nativeLittleEndian && 0 === (d & 3) && this._f32) { + this._f32[d >> 2] = f; } else { - var d = this._u8; - b.IntegerUtilities.f32[0] = a; - a = b.IntegerUtilities.u8; - this._littleEndian ? (d[c + 0] = a[0], d[c + 1] = a[1], d[c + 2] = a[2], d[c + 3] = a[3]) : (d[c + 0] = a[3], d[c + 1] = a[2], d[c + 2] = a[1], d[c + 3] = a[0]); + var b = this._u8; + c.IntegerUtilities.f32[0] = f; + f = c.IntegerUtilities.u8; + this._littleEndian ? (b[d + 0] = f[0], b[d + 1] = f[1], b[d + 2] = f[2], b[d + 3] = f[3]) : (b[d + 0] = f[3], b[d + 1] = f[2], b[d + 2] = f[1], b[d + 3] = f[0]); } - this._position = c += 4; - c > this._length && (this._length = c); + this._position = d += 4; + d > this._length && (this._length = d); }; - e.prototype.write6Floats = function(a, c, d, b, p, h) { + k.prototype.write6Floats = function(f, d, b, g, r, e) { var n = this._position; this._ensureCapacity(n + 24); - this._littleEndian === e._nativeLittleEndian && 0 === (n & 3) && this._f32 ? (this._f32[(n >> 2) + 0] = a, this._f32[(n >> 2) + 1] = c, this._f32[(n >> 2) + 2] = d, this._f32[(n >> 2) + 3] = b, this._f32[(n >> 2) + 4] = p, this._f32[(n >> 2) + 5] = h, this._position = n += 24, n > this._length && (this._length = n)) : (this.writeFloat(a), this.writeFloat(c), this.writeFloat(d), this.writeFloat(b), this.writeFloat(p), this.writeFloat(h)); + this._requestViews(4); + this._littleEndian === k._nativeLittleEndian && 0 === (n & 3) && this._f32 ? (this._f32[(n >> 2) + 0] = f, this._f32[(n >> 2) + 1] = d, this._f32[(n >> 2) + 2] = b, this._f32[(n >> 2) + 3] = g, this._f32[(n >> 2) + 4] = r, this._f32[(n >> 2) + 5] = e, this._position = n += 24, n > this._length && (this._length = n)) : (this.writeFloat(f), this.writeFloat(d), this.writeFloat(b), this.writeFloat(g), this.writeFloat(r), this.writeFloat(e)); }; - e.prototype.writeDouble = function(a) { - var c = this._position; - this._ensureCapacity(c + 8); - var e = this._u8; - b.IntegerUtilities.f64[0] = a; - a = b.IntegerUtilities.u8; - this._littleEndian ? (e[c + 0] = a[0], e[c + 1] = a[1], e[c + 2] = a[2], e[c + 3] = a[3], e[c + 4] = a[4], e[c + 5] = a[5], e[c + 6] = a[6], e[c + 7] = a[7]) : (e[c + 0] = a[7], e[c + 1] = a[6], e[c + 2] = a[5], e[c + 3] = a[4], e[c + 4] = a[3], e[c + 5] = a[2], e[c + 6] = a[1], e[c + 7] = a[0]); - this._position = c += 8; - c > this._length && (this._length = c); + k.prototype.writeDouble = function(f) { + var d = this._position; + this._ensureCapacity(d + 8); + var b = this._u8; + c.IntegerUtilities.f64[0] = f; + f = c.IntegerUtilities.u8; + this._littleEndian ? (b[d + 0] = f[0], b[d + 1] = f[1], b[d + 2] = f[2], b[d + 3] = f[3], b[d + 4] = f[4], b[d + 5] = f[5], b[d + 6] = f[6], b[d + 7] = f[7]) : (b[d + 0] = f[7], b[d + 1] = f[6], b[d + 2] = f[5], b[d + 3] = f[4], b[d + 4] = f[3], b[d + 5] = f[2], b[d + 6] = f[1], b[d + 7] = f[0]); + this._position = d += 8; + d > this._length && (this._length = d); }; - e.prototype.readRawBytes = function() { + k.prototype.readRawBytes = function() { return new Int8Array(this._buffer, 0, this._length); }; - e.prototype.writeUTF = function(a) { - a = f(a); - a = s(a); - this.writeShort(a.length); - this.writeRawBytes(a); - }; - e.prototype.writeUTFBytes = function(a) { - a = f(a); - a = s(a); - this.writeRawBytes(a); + k.prototype.writeUTF = function(f) { + f = s(f); + f = p(f); + this.writeShort(f.length); + this.writeRawBytes(f); + }; + k.prototype.writeUTFBytes = function(f) { + f = s(f); + f = p(f); + this.writeRawBytes(f); }; - e.prototype.readUTF = function() { + k.prototype.readUTF = function() { return this.readUTFBytes(this.readShort()); }; - e.prototype.readUTFBytes = function(a) { - a >>>= 0; - var c = this._position; - c + a > this._length && g(); - this._position += a; - return m(new Int8Array(this._buffer, c, a)); + k.prototype.readUTFBytes = function(f) { + f >>>= 0; + var d = this._position; + d + f > this._length && throwError("EOFError", Errors.EOFError); + this._position += f; + return u(new Int8Array(this._buffer, d, f)); }; - Object.defineProperty(e.prototype, "length", {get:function() { + Object.defineProperty(k.prototype, "length", {get:function() { return this._length; - }, set:function(a) { - a >>>= 0; - a > this._buffer.byteLength && this._ensureCapacity(a); - this._length = a; - this._position = d(this._position, 0, this._length); + }, set:function(f) { + f >>>= 0; + f > this._buffer.byteLength && this._ensureCapacity(f); + this._length = f; + this._position = l(this._position, 0, this._length); }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "bytesAvailable", {get:function() { + Object.defineProperty(k.prototype, "bytesAvailable", {get:function() { return this._length - this._position; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "position", {get:function() { + Object.defineProperty(k.prototype, "position", {get:function() { return this._position; - }, set:function(a) { - this._position = a >>> 0; + }, set:function(f) { + this._position = f >>> 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "buffer", {get:function() { + Object.defineProperty(k.prototype, "buffer", {get:function() { return this._buffer; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "bytes", {get:function() { + Object.defineProperty(k.prototype, "bytes", {get:function() { return this._u8; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "ints", {get:function() { + Object.defineProperty(k.prototype, "ints", {get:function() { + this._requestViews(2); return this._i32; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "objectEncoding", {get:function() { + Object.defineProperty(k.prototype, "objectEncoding", {get:function() { return this._objectEncoding; - }, set:function(a) { - this._objectEncoding = a >>> 0; + }, set:function(f) { + this._objectEncoding = f >>> 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "endian", {get:function() { + Object.defineProperty(k.prototype, "endian", {get:function() { return this._littleEndian ? "littleEndian" : "bigEndian"; - }, set:function(a) { - a = f(a); - this._littleEndian = "auto" === a ? e._nativeLittleEndian : "littleEndian" === a; + }, set:function(f) { + f = s(f); + this._littleEndian = "auto" === f ? k._nativeLittleEndian : "littleEndian" === f; }, enumerable:!0, configurable:!0}); - e.prototype.toString = function() { - return m(new Int8Array(this._buffer, 0, this._length)); + k.prototype.toString = function() { + return u(new Int8Array(this._buffer, 0, this._length)); }; - e.prototype.toBlob = function() { - return new Blob([new Int8Array(this._buffer, this._position, this._length)]); + k.prototype.toBlob = function(f) { + return new Blob([new Int8Array(this._buffer, this._position, this._length)], {type:f}); }; - e.prototype.writeMultiByte = function(a, c) { - t("packageInternal flash.utils.ObjectOutput::writeMultiByte"); + k.prototype.writeMultiByte = function(f, d) { + v("packageInternal flash.utils.ObjectOutput::writeMultiByte"); }; - e.prototype.readMultiByte = function(a, c) { - t("packageInternal flash.utils.ObjectInput::readMultiByte"); - }; - e.prototype.getValue = function(a) { - a |= 0; - return a >= this._length ? void 0 : this._u8[a]; - }; - e.prototype.setValue = function(a, c) { - a |= 0; - var e = a + 1; - this._ensureCapacity(e); - this._u8[a] = c; - e > this._length && (this._length = e); + k.prototype.readMultiByte = function(f, d) { + v("packageInternal flash.utils.ObjectInput::readMultiByte"); + }; + k.prototype.getValue = function(f) { + f |= 0; + return f >= this._length ? void 0 : this._u8[f]; + }; + k.prototype.setValue = function(f, d) { + f |= 0; + var b = f + 1; + this._ensureCapacity(b); + this._u8[f] = d; + b > this._length && (this._length = b); }; - e.prototype.readFixed = function() { + k.prototype.readFixed = function() { return this.readInt() / 65536; }; - e.prototype.readFixed8 = function() { + k.prototype.readFixed8 = function() { return this.readShort() / 256; }; - e.prototype.readFloat16 = function() { - var a = this.readUnsignedShort(), c = a >> 15 ? -1 : 1, e = (a & 31744) >> 10, a = a & 1023; - return e ? 31 === e ? a ? NaN : Infinity * c : c * Math.pow(2, e - 15) * (1 + a / 1024) : a / 1024 * Math.pow(2, -14) * c; - }; - e.prototype.readEncodedU32 = function() { - var a = this.readUnsignedByte(); - if (!(a & 128)) { - return a; + k.prototype.readFloat16 = function() { + var f = this.readUnsignedShort(), d = f >> 15 ? -1 : 1, b = (f & 31744) >> 10, f = f & 1023; + return b ? 31 === b ? f ? NaN : Infinity * d : d * Math.pow(2, b - 15) * (1 + f / 1024) : f / 1024 * Math.pow(2, -14) * d; + }; + k.prototype.readEncodedU32 = function() { + var f = this.readUnsignedByte(); + if (!(f & 128)) { + return f; } - a = a & 127 | this.readUnsignedByte() << 7; - if (!(a & 16384)) { - return a; + f = f & 127 | this.readUnsignedByte() << 7; + if (!(f & 16384)) { + return f; } - a = a & 16383 | this.readUnsignedByte() << 14; - if (!(a & 2097152)) { - return a; + f = f & 16383 | this.readUnsignedByte() << 14; + if (!(f & 2097152)) { + return f; } - a = a & 2097151 | this.readUnsignedByte() << 21; - return a & 268435456 ? a & 268435455 | this.readUnsignedByte() << 28 : a; + f = f & 2097151 | this.readUnsignedByte() << 21; + return f & 268435456 ? f & 268435455 | this.readUnsignedByte() << 28 : f; }; - e.prototype.readBits = function(a) { - return this.readUnsignedBits(a) << 32 - a >> 32 - a; + k.prototype.readBits = function(f) { + return this.readUnsignedBits(f) << 32 - f >> 32 - f; }; - e.prototype.readUnsignedBits = function(a) { - for (var e = this._bitBuffer, d = this._bitLength;a > d;) { - e = e << 8 | this.readUnsignedByte(), d += 8; - } - d -= a; - a = e >>> d & c[a]; - this._bitBuffer = e; - this._bitLength = d; - return a; + k.prototype.readUnsignedBits = function(f) { + for (var d = this._bitBuffer, b = this._bitLength;f > b;) { + d = d << 8 | this.readUnsignedByte(), b += 8; + } + b -= f; + f = d >>> b & m[f]; + this._bitBuffer = d; + this._bitLength = b; + return f; }; - e.prototype.readFixedBits = function(a) { - return this.readBits(a) / 65536; + k.prototype.readFixedBits = function(f) { + return this.readBits(f) / 65536; }; - e.prototype.readString = function(a) { - var c = this._position; - if (a) { - c + a > this._length && g(), this._position += a; + k.prototype.readString = function(f) { + var d = this._position; + if (f) { + d + f > this._length && throwError("EOFError", Errors.EOFError), this._position += f; } else { - a = 0; - for (var e = c;e < this._length && this._u8[e];e++) { - a++; + f = 0; + for (var b = d;b < this._length && this._u8[b];b++) { + f++; } - this._position += a + 1; + this._position += f + 1; } - return m(new Int8Array(this._buffer, c, a)); + return u(new Int8Array(this._buffer, d, f)); }; - e.prototype.align = function() { + k.prototype.align = function() { this._bitLength = this._bitBuffer = 0; }; - e.prototype._compress = function(a) { - a = f(a); - switch(a) { + k.prototype._compress = function(f) { + f = s(f); + switch(f) { case "zlib": - a = new k.Deflate(!0); + f = new h.Deflate(!0); break; case "deflate": - a = new k.Deflate(!1); + f = new h.Deflate(!1); break; default: return; } - var c = new e; - a.onData = c.writeRawBytes.bind(c); - a.push(this._u8.subarray(0, this._length)); - a.finish(); - this._ensureCapacity(c._u8.length); - this._u8.set(c._u8); - this.length = c.length; + var d = new k; + f.onData = d.writeRawBytes.bind(d); + f.push(this._u8.subarray(0, this._length)); + f.finish(); + this._ensureCapacity(d._u8.length); + this._u8.set(d._u8); + this.length = d.length; this._position = 0; }; - e.prototype._uncompress = function(a) { - a = f(a); - switch(a) { + k.prototype._uncompress = function(f) { + f = s(f); + switch(f) { case "zlib": - a = new k.Inflate(!0); + f = h.Inflate.create(!0); break; case "deflate": - a = new k.Inflate(!1); + f = h.Inflate.create(!1); break; default: return; } - var c = new e; - a.onData = c.writeRawBytes.bind(c); - a.push(this._u8.subarray(0, this._length)); - a.error && t("throwCompressedDataError"); - this._ensureCapacity(c._u8.length); - this._u8.set(c._u8); - this.length = c.length; + var d = new k; + f.onData = d.writeRawBytes.bind(d); + f.push(this._u8.subarray(0, this._length)); + f.error && throwError("IOError", Errors.CompressedDataError); + f.close(); + this._ensureCapacity(d._u8.length); + this._u8.set(d._u8); + this.length = d.length; this._position = 0; }; - e._nativeLittleEndian = 1 === (new Int8Array((new Int32Array([1])).buffer))[0]; - e.INITIAL_SIZE = 128; - e._arrayBufferPool = new b.ArrayBufferPool; - return e; + k._nativeLittleEndian = 1 === (new Int8Array((new Int32Array([1])).buffer))[0]; + k.INITIAL_SIZE = 128; + k._arrayBufferPool = new c.ArrayBufferPool; + return k; }(); - k.DataBuffer = n; - })(b.ArrayUtilities || (b.ArrayUtilities = {})); + h.DataBuffer = t; + })(c.ArrayUtilities || (c.ArrayUtilities = {})); })(Shumway || (Shumway = {})); -(function(b) { - var k = b.ArrayUtilities.DataBuffer, g = b.ArrayUtilities.ensureTypedArrayCapacity, f = b.Debug.assert; - (function(b) { - b[b.BeginSolidFill = 1] = "BeginSolidFill"; - b[b.BeginGradientFill = 2] = "BeginGradientFill"; - b[b.BeginBitmapFill = 3] = "BeginBitmapFill"; - b[b.EndFill = 4] = "EndFill"; - b[b.LineStyleSolid = 5] = "LineStyleSolid"; - b[b.LineStyleGradient = 6] = "LineStyleGradient"; - b[b.LineStyleBitmap = 7] = "LineStyleBitmap"; - b[b.LineEnd = 8] = "LineEnd"; - b[b.MoveTo = 9] = "MoveTo"; - b[b.LineTo = 10] = "LineTo"; - b[b.CurveTo = 11] = "CurveTo"; - b[b.CubicCurveTo = 12] = "CubicCurveTo"; - })(b.PathCommand || (b.PathCommand = {})); - (function(b) { - b[b.Linear = 16] = "Linear"; - b[b.Radial = 18] = "Radial"; - })(b.GradientType || (b.GradientType = {})); - (function(b) { - b[b.Pad = 0] = "Pad"; - b[b.Reflect = 1] = "Reflect"; - b[b.Repeat = 2] = "Repeat"; - })(b.GradientSpreadMethod || (b.GradientSpreadMethod = {})); - (function(b) { - b[b.RGB = 0] = "RGB"; - b[b.LinearRGB = 1] = "LinearRGB"; - })(b.GradientInterpolationMethod || (b.GradientInterpolationMethod = {})); - (function(b) { - b[b.None = 0] = "None"; - b[b.Normal = 1] = "Normal"; - b[b.Vertical = 2] = "Vertical"; - b[b.Horizontal = 3] = "Horizontal"; - })(b.LineScaleMode || (b.LineScaleMode = {})); - var t = function() { - return function(b, d, a, c, n, p, e, q, l) { - this.commands = b; - this.commandsPosition = d; - this.coordinates = a; - this.coordinatesPosition = c; - this.morphCoordinates = n; - this.styles = p; - this.stylesLength = e; - this.hasFills = q; - this.hasLines = l; +(function(c) { + var h = c.ArrayUtilities.DataBuffer, a = c.ArrayUtilities.ensureTypedArrayCapacity, s = c.Debug.assert; + (function(a) { + a[a.BeginSolidFill = 1] = "BeginSolidFill"; + a[a.BeginGradientFill = 2] = "BeginGradientFill"; + a[a.BeginBitmapFill = 3] = "BeginBitmapFill"; + a[a.EndFill = 4] = "EndFill"; + a[a.LineStyleSolid = 5] = "LineStyleSolid"; + a[a.LineStyleGradient = 6] = "LineStyleGradient"; + a[a.LineStyleBitmap = 7] = "LineStyleBitmap"; + a[a.LineEnd = 8] = "LineEnd"; + a[a.MoveTo = 9] = "MoveTo"; + a[a.LineTo = 10] = "LineTo"; + a[a.CurveTo = 11] = "CurveTo"; + a[a.CubicCurveTo = 12] = "CubicCurveTo"; + })(c.PathCommand || (c.PathCommand = {})); + (function(a) { + a[a.Linear = 16] = "Linear"; + a[a.Radial = 18] = "Radial"; + })(c.GradientType || (c.GradientType = {})); + (function(a) { + a[a.Pad = 0] = "Pad"; + a[a.Reflect = 1] = "Reflect"; + a[a.Repeat = 2] = "Repeat"; + })(c.GradientSpreadMethod || (c.GradientSpreadMethod = {})); + (function(a) { + a[a.RGB = 0] = "RGB"; + a[a.LinearRGB = 1] = "LinearRGB"; + })(c.GradientInterpolationMethod || (c.GradientInterpolationMethod = {})); + (function(a) { + a[a.None = 0] = "None"; + a[a.Normal = 1] = "Normal"; + a[a.Vertical = 2] = "Vertical"; + a[a.Horizontal = 3] = "Horizontal"; + })(c.LineScaleMode || (c.LineScaleMode = {})); + var v = function() { + return function(a, l, e, m, t, q, n, k, f, d, b) { + this.commands = a; + this.commandsPosition = l; + this.coordinates = e; + this.morphCoordinates = m; + this.coordinatesPosition = t; + this.styles = q; + this.stylesLength = n; + this.morphStyles = k; + this.morphStylesLength = f; + this.hasFills = d; + this.hasLines = b; }; }(); - b.PlainObjectShapeData = t; - var s; - (function(b) { - b[b.Commands = 32] = "Commands"; - b[b.Coordinates = 128] = "Coordinates"; - b[b.Styles = 16] = "Styles"; - })(s || (s = {})); - s = function() { - function b(d) { - "undefined" === typeof d && (d = !0); - d && this.clear(); - } - b.FromPlainObject = function(d) { - var a = new b(!1); - a.commands = d.commands; - a.coordinates = d.coordinates; - a.morphCoordinates = d.morphCoordinates; - a.commandsPosition = d.commandsPosition; - a.coordinatesPosition = d.coordinatesPosition; - a.styles = k.FromArrayBuffer(d.styles, d.stylesLength); - a.styles.endian = "auto"; - a.hasFills = d.hasFills; - a.hasLines = d.hasLines; - return a; + c.PlainObjectShapeData = v; + var p; + (function(a) { + a[a.Commands = 32] = "Commands"; + a[a.Coordinates = 128] = "Coordinates"; + a[a.Styles = 16] = "Styles"; + })(p || (p = {})); + p = function() { + function c(l) { + void 0 === l && (l = !0); + l && this.clear(); + } + c.FromPlainObject = function(l) { + var e = new c(!1); + e.commands = l.commands; + e.coordinates = l.coordinates; + e.morphCoordinates = l.morphCoordinates; + e.commandsPosition = l.commandsPosition; + e.coordinatesPosition = l.coordinatesPosition; + e.styles = h.FromArrayBuffer(l.styles, l.stylesLength); + e.styles.endian = "auto"; + l.morphStyles && (e.morphStyles = h.FromArrayBuffer(l.morphStyles, l.morphStylesLength), e.morphStyles.endian = "auto"); + e.hasFills = l.hasFills; + e.hasLines = l.hasLines; + return e; }; - b.prototype.moveTo = function(d, a) { + c.prototype.moveTo = function(l, e) { this.ensurePathCapacities(1, 2); this.commands[this.commandsPosition++] = 9; - this.coordinates[this.coordinatesPosition++] = d; - this.coordinates[this.coordinatesPosition++] = a; + this.coordinates[this.coordinatesPosition++] = l; + this.coordinates[this.coordinatesPosition++] = e; }; - b.prototype.lineTo = function(d, a) { + c.prototype.lineTo = function(l, e) { this.ensurePathCapacities(1, 2); this.commands[this.commandsPosition++] = 10; - this.coordinates[this.coordinatesPosition++] = d; - this.coordinates[this.coordinatesPosition++] = a; + this.coordinates[this.coordinatesPosition++] = l; + this.coordinates[this.coordinatesPosition++] = e; }; - b.prototype.curveTo = function(d, a, c, b) { + c.prototype.curveTo = function(l, e, m, a) { this.ensurePathCapacities(1, 4); this.commands[this.commandsPosition++] = 11; - this.coordinates[this.coordinatesPosition++] = d; + this.coordinates[this.coordinatesPosition++] = l; + this.coordinates[this.coordinatesPosition++] = e; + this.coordinates[this.coordinatesPosition++] = m; this.coordinates[this.coordinatesPosition++] = a; - this.coordinates[this.coordinatesPosition++] = c; - this.coordinates[this.coordinatesPosition++] = b; }; - b.prototype.cubicCurveTo = function(d, a, c, b, p, e) { + c.prototype.cubicCurveTo = function(l, e, m, a, q, n) { this.ensurePathCapacities(1, 6); this.commands[this.commandsPosition++] = 12; - this.coordinates[this.coordinatesPosition++] = d; - this.coordinates[this.coordinatesPosition++] = a; - this.coordinates[this.coordinatesPosition++] = c; - this.coordinates[this.coordinatesPosition++] = b; - this.coordinates[this.coordinatesPosition++] = p; + this.coordinates[this.coordinatesPosition++] = l; this.coordinates[this.coordinatesPosition++] = e; + this.coordinates[this.coordinatesPosition++] = m; + this.coordinates[this.coordinatesPosition++] = a; + this.coordinates[this.coordinatesPosition++] = q; + this.coordinates[this.coordinatesPosition++] = n; }; - b.prototype.beginFill = function(d) { + c.prototype.beginFill = function(l) { this.ensurePathCapacities(1, 0); this.commands[this.commandsPosition++] = 1; - this.styles.writeUnsignedInt(d); + this.styles.writeUnsignedInt(l); this.hasFills = !0; }; - b.prototype.endFill = function() { + c.prototype.writeMorphFill = function(l) { + this.morphStyles.writeUnsignedInt(l); + }; + c.prototype.endFill = function() { this.ensurePathCapacities(1, 0); this.commands[this.commandsPosition++] = 4; }; - b.prototype.endLine = function() { + c.prototype.endLine = function() { this.ensurePathCapacities(1, 0); this.commands[this.commandsPosition++] = 8; }; - b.prototype.lineStyle = function(d, a, c, b, p, e, q) { - f(d === (d | 0), 0 <= d && 5100 >= d); + c.prototype.lineStyle = function(l, e, m, a, q, n, k) { + s(l === (l | 0), 0 <= l && 5100 >= l); this.ensurePathCapacities(2, 0); this.commands[this.commandsPosition++] = 5; - this.coordinates[this.coordinatesPosition++] = d; - d = this.styles; - d.writeUnsignedInt(a); - d.writeBoolean(c); - d.writeUnsignedByte(b); - d.writeUnsignedByte(p); - d.writeUnsignedByte(e); - d.writeUnsignedByte(q); + this.coordinates[this.coordinatesPosition++] = l; + l = this.styles; + l.writeUnsignedInt(e); + l.writeBoolean(m); + l.writeUnsignedByte(a); + l.writeUnsignedByte(q); + l.writeUnsignedByte(n); + l.writeUnsignedByte(k); this.hasLines = !0; }; - b.prototype.beginBitmap = function(d, a, c, b, p) { - f(3 === d || 7 === d); + c.prototype.writeMorphLineStyle = function(l, e) { + this.morphCoordinates[this.coordinatesPosition - 1] = l; + this.morphStyles.writeUnsignedInt(e); + }; + c.prototype.beginBitmap = function(l, e, m, a, q) { + s(3 === l || 7 === l); this.ensurePathCapacities(1, 0); - this.commands[this.commandsPosition++] = d; - d = this.styles; - d.writeUnsignedInt(a); - this._writeStyleMatrix(c); - d.writeBoolean(b); - d.writeBoolean(p); + this.commands[this.commandsPosition++] = l; + l = this.styles; + l.writeUnsignedInt(e); + this._writeStyleMatrix(m, !1); + l.writeBoolean(a); + l.writeBoolean(q); this.hasFills = !0; }; - b.prototype.beginGradient = function(d, a, c, b, p, e, q, l) { - f(2 === d || 6 === d); + c.prototype.writeMorphBitmap = function(l) { + this._writeStyleMatrix(l, !0); + }; + c.prototype.beginGradient = function(l, e, m, a, q, n, k, f) { + s(2 === l || 6 === l); this.ensurePathCapacities(1, 0); - this.commands[this.commandsPosition++] = d; - d = this.styles; - d.writeUnsignedByte(b); - f(l === (l | 0)); - d.writeShort(l); - this._writeStyleMatrix(p); - b = a.length; - d.writeByte(b); - for (p = 0;p < b;p++) { - d.writeUnsignedByte(c[p]), d.writeUnsignedInt(a[p]); + this.commands[this.commandsPosition++] = l; + l = this.styles; + l.writeUnsignedByte(a); + s(f === (f | 0)); + l.writeShort(f); + this._writeStyleMatrix(q, !1); + a = e.length; + l.writeByte(a); + for (q = 0;q < a;q++) { + l.writeUnsignedByte(m[q]), l.writeUnsignedInt(e[q]); } - d.writeUnsignedByte(e); - d.writeUnsignedByte(q); + l.writeUnsignedByte(n); + l.writeUnsignedByte(k); this.hasFills = !0; }; - b.prototype.writeCommandAndCoordinates = function(d, a, c) { + c.prototype.writeMorphGradient = function(l, e, m) { + this._writeStyleMatrix(m, !0); + m = this.morphStyles; + for (var a = 0;a < l.length;a++) { + m.writeUnsignedByte(e[a]), m.writeUnsignedInt(l[a]); + } + }; + c.prototype.writeCommandAndCoordinates = function(l, e, m) { this.ensurePathCapacities(1, 2); - this.commands[this.commandsPosition++] = d; - this.coordinates[this.coordinatesPosition++] = a; - this.coordinates[this.coordinatesPosition++] = c; + this.commands[this.commandsPosition++] = l; + this.coordinates[this.coordinatesPosition++] = e; + this.coordinates[this.coordinatesPosition++] = m; }; - b.prototype.writeCoordinates = function(d, a) { + c.prototype.writeCoordinates = function(l, e) { this.ensurePathCapacities(0, 2); - this.coordinates[this.coordinatesPosition++] = d; - this.coordinates[this.coordinatesPosition++] = a; + this.coordinates[this.coordinatesPosition++] = l; + this.coordinates[this.coordinatesPosition++] = e; }; - b.prototype.writeMorphCoordinates = function(d, a) { - this.morphCoordinates = g(this.morphCoordinates, this.coordinatesPosition); - this.morphCoordinates[this.coordinatesPosition - 2] = d; - this.morphCoordinates[this.coordinatesPosition - 1] = a; + c.prototype.writeMorphCoordinates = function(l, e) { + this.morphCoordinates = a(this.morphCoordinates, this.coordinatesPosition); + this.morphCoordinates[this.coordinatesPosition - 2] = l; + this.morphCoordinates[this.coordinatesPosition - 1] = e; }; - b.prototype.clear = function() { + c.prototype.clear = function() { this.commandsPosition = this.coordinatesPosition = 0; this.commands = new Uint8Array(32); this.coordinates = new Int32Array(128); - this.styles = new k(16); + this.styles = new h(16); this.styles.endian = "auto"; this.hasFills = this.hasLines = !1; }; - b.prototype.isEmpty = function() { + c.prototype.isEmpty = function() { return 0 === this.commandsPosition; }; - b.prototype.clone = function() { - var d = new b(!1); - d.commands = new Uint8Array(this.commands); - d.commandsPosition = this.commandsPosition; - d.coordinates = new Int32Array(this.coordinates); - d.coordinatesPosition = this.coordinatesPosition; - d.styles = new k(this.styles.length); - d.styles.writeRawBytes(this.styles.bytes); - d.hasFills = this.hasFills; - d.hasLines = this.hasLines; - return d; + c.prototype.clone = function() { + var l = new c(!1); + l.commands = new Uint8Array(this.commands); + l.commandsPosition = this.commandsPosition; + l.coordinates = new Int32Array(this.coordinates); + l.coordinatesPosition = this.coordinatesPosition; + l.styles = new h(this.styles.length); + l.styles.writeRawBytes(this.styles.bytes); + this.morphStyles && (l.morphStyles = new h(this.morphStyles.length), l.morphStyles.writeRawBytes(this.morphStyles.bytes)); + l.hasFills = this.hasFills; + l.hasLines = this.hasLines; + return l; }; - b.prototype.toPlainObject = function() { - return new t(this.commands, this.commandsPosition, this.coordinates, this.coordinatesPosition, this.morphCoordinates, this.styles.buffer, this.styles.length, this.hasFills, this.hasLines); + c.prototype.toPlainObject = function() { + return new v(this.commands, this.commandsPosition, this.coordinates, this.morphCoordinates, this.coordinatesPosition, this.styles.buffer, this.styles.length, this.morphStyles && this.morphStyles.buffer, this.morphStyles ? this.morphStyles.length : 0, this.hasFills, this.hasLines); }; - Object.defineProperty(b.prototype, "buffers", {get:function() { - var d = [this.commands.buffer, this.coordinates.buffer, this.styles.buffer]; - this.morphCoordinates && d.push(this.morphCoordinates.buffer); - return d; + Object.defineProperty(c.prototype, "buffers", {get:function() { + var l = [this.commands.buffer, this.coordinates.buffer, this.styles.buffer]; + this.morphCoordinates && l.push(this.morphCoordinates.buffer); + this.morphStyles && l.push(this.morphStyles.buffer); + return l; }, enumerable:!0, configurable:!0}); - b.prototype._writeStyleMatrix = function(d) { - var a = this.styles; - a.writeFloat(d.a); - a.writeFloat(d.b); - a.writeFloat(d.c); - a.writeFloat(d.d); - a.writeFloat(d.tx); - a.writeFloat(d.ty); - }; - b.prototype.ensurePathCapacities = function(d, a) { - this.commands = g(this.commands, this.commandsPosition + d); - this.coordinates = g(this.coordinates, this.coordinatesPosition + a); + c.prototype._writeStyleMatrix = function(l, e) { + (e ? this.morphStyles : this.styles).write6Floats(l.a, l.b, l.c, l.d, l.tx, l.ty); }; - return b; + c.prototype.ensurePathCapacities = function(l, e) { + this.commands = a(this.commands, this.commandsPosition + l); + this.coordinates = a(this.coordinates, this.coordinatesPosition + e); + }; + return c; }(); - b.ShapeData = s; + c.ShapeData = p; })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(b) { - b[b.CODE_END = 0] = "CODE_END"; - b[b.CODE_SHOW_FRAME = 1] = "CODE_SHOW_FRAME"; - b[b.CODE_DEFINE_SHAPE = 2] = "CODE_DEFINE_SHAPE"; - b[b.CODE_FREE_CHARACTER = 3] = "CODE_FREE_CHARACTER"; - b[b.CODE_PLACE_OBJECT = 4] = "CODE_PLACE_OBJECT"; - b[b.CODE_REMOVE_OBJECT = 5] = "CODE_REMOVE_OBJECT"; - b[b.CODE_DEFINE_BITS = 6] = "CODE_DEFINE_BITS"; - b[b.CODE_DEFINE_BUTTON = 7] = "CODE_DEFINE_BUTTON"; - b[b.CODE_JPEG_TABLES = 8] = "CODE_JPEG_TABLES"; - b[b.CODE_SET_BACKGROUND_COLOR = 9] = "CODE_SET_BACKGROUND_COLOR"; - b[b.CODE_DEFINE_FONT = 10] = "CODE_DEFINE_FONT"; - b[b.CODE_DEFINE_TEXT = 11] = "CODE_DEFINE_TEXT"; - b[b.CODE_DO_ACTION = 12] = "CODE_DO_ACTION"; - b[b.CODE_DEFINE_FONT_INFO = 13] = "CODE_DEFINE_FONT_INFO"; - b[b.CODE_DEFINE_SOUND = 14] = "CODE_DEFINE_SOUND"; - b[b.CODE_START_SOUND = 15] = "CODE_START_SOUND"; - b[b.CODE_STOP_SOUND = 16] = "CODE_STOP_SOUND"; - b[b.CODE_DEFINE_BUTTON_SOUND = 17] = "CODE_DEFINE_BUTTON_SOUND"; - b[b.CODE_SOUND_STREAM_HEAD = 18] = "CODE_SOUND_STREAM_HEAD"; - b[b.CODE_SOUND_STREAM_BLOCK = 19] = "CODE_SOUND_STREAM_BLOCK"; - b[b.CODE_DEFINE_BITS_LOSSLESS = 20] = "CODE_DEFINE_BITS_LOSSLESS"; - b[b.CODE_DEFINE_BITS_JPEG2 = 21] = "CODE_DEFINE_BITS_JPEG2"; - b[b.CODE_DEFINE_SHAPE2 = 22] = "CODE_DEFINE_SHAPE2"; - b[b.CODE_DEFINE_BUTTON_CXFORM = 23] = "CODE_DEFINE_BUTTON_CXFORM"; - b[b.CODE_PROTECT = 24] = "CODE_PROTECT"; - b[b.CODE_PATHS_ARE_POSTSCRIPT = 25] = "CODE_PATHS_ARE_POSTSCRIPT"; - b[b.CODE_PLACE_OBJECT2 = 26] = "CODE_PLACE_OBJECT2"; - b[b.CODE_REMOVE_OBJECT2 = 28] = "CODE_REMOVE_OBJECT2"; - b[b.CODE_SYNC_FRAME = 29] = "CODE_SYNC_FRAME"; - b[b.CODE_FREE_ALL = 31] = "CODE_FREE_ALL"; - b[b.CODE_DEFINE_SHAPE3 = 32] = "CODE_DEFINE_SHAPE3"; - b[b.CODE_DEFINE_TEXT2 = 33] = "CODE_DEFINE_TEXT2"; - b[b.CODE_DEFINE_BUTTON2 = 34] = "CODE_DEFINE_BUTTON2"; - b[b.CODE_DEFINE_BITS_JPEG3 = 35] = "CODE_DEFINE_BITS_JPEG3"; - b[b.CODE_DEFINE_BITS_LOSSLESS2 = 36] = "CODE_DEFINE_BITS_LOSSLESS2"; - b[b.CODE_DEFINE_EDIT_TEXT = 37] = "CODE_DEFINE_EDIT_TEXT"; - b[b.CODE_DEFINE_VIDEO = 38] = "CODE_DEFINE_VIDEO"; - b[b.CODE_DEFINE_SPRITE = 39] = "CODE_DEFINE_SPRITE"; - b[b.CODE_NAME_CHARACTER = 40] = "CODE_NAME_CHARACTER"; - b[b.CODE_PRODUCT_INFO = 41] = "CODE_PRODUCT_INFO"; - b[b.CODE_DEFINE_TEXT_FORMAT = 42] = "CODE_DEFINE_TEXT_FORMAT"; - b[b.CODE_FRAME_LABEL = 43] = "CODE_FRAME_LABEL"; - b[b.CODE_DEFINE_BEHAVIOUR = 44] = "CODE_DEFINE_BEHAVIOUR"; - b[b.CODE_SOUND_STREAM_HEAD2 = 45] = "CODE_SOUND_STREAM_HEAD2"; - b[b.CODE_DEFINE_MORPH_SHAPE = 46] = "CODE_DEFINE_MORPH_SHAPE"; - b[b.CODE_FRAME_TAG = 47] = "CODE_FRAME_TAG"; - b[b.CODE_DEFINE_FONT2 = 48] = "CODE_DEFINE_FONT2"; - b[b.CODE_GEN_COMMAND = 49] = "CODE_GEN_COMMAND"; - b[b.CODE_DEFINE_COMMAND_OBJ = 50] = "CODE_DEFINE_COMMAND_OBJ"; - b[b.CODE_CHARACTER_SET = 51] = "CODE_CHARACTER_SET"; - b[b.CODE_FONT_REF = 52] = "CODE_FONT_REF"; - b[b.CODE_DEFINE_FUNCTION = 53] = "CODE_DEFINE_FUNCTION"; - b[b.CODE_PLACE_FUNCTION = 54] = "CODE_PLACE_FUNCTION"; - b[b.CODE_GEN_TAG_OBJECTS = 55] = "CODE_GEN_TAG_OBJECTS"; - b[b.CODE_EXPORT_ASSETS = 56] = "CODE_EXPORT_ASSETS"; - b[b.CODE_IMPORT_ASSETS = 57] = "CODE_IMPORT_ASSETS"; - b[b.CODE_ENABLE_DEBUGGER = 58] = "CODE_ENABLE_DEBUGGER"; - b[b.CODE_DO_INIT_ACTION = 59] = "CODE_DO_INIT_ACTION"; - b[b.CODE_DEFINE_VIDEO_STREAM = 60] = "CODE_DEFINE_VIDEO_STREAM"; - b[b.CODE_VIDEO_FRAME = 61] = "CODE_VIDEO_FRAME"; - b[b.CODE_DEFINE_FONT_INFO2 = 62] = "CODE_DEFINE_FONT_INFO2"; - b[b.CODE_DEBUG_ID = 63] = "CODE_DEBUG_ID"; - b[b.CODE_ENABLE_DEBUGGER2 = 64] = "CODE_ENABLE_DEBUGGER2"; - b[b.CODE_SCRIPT_LIMITS = 65] = "CODE_SCRIPT_LIMITS"; - b[b.CODE_SET_TAB_INDEX = 66] = "CODE_SET_TAB_INDEX"; - b[b.CODE_FILE_ATTRIBUTES = 69] = "CODE_FILE_ATTRIBUTES"; - b[b.CODE_PLACE_OBJECT3 = 70] = "CODE_PLACE_OBJECT3"; - b[b.CODE_IMPORT_ASSETS2 = 71] = "CODE_IMPORT_ASSETS2"; - b[b.CODE_DO_ABC_ = 72] = "CODE_DO_ABC_"; - b[b.CODE_DEFINE_FONT_ALIGN_ZONES = 73] = "CODE_DEFINE_FONT_ALIGN_ZONES"; - b[b.CODE_CSM_TEXT_SETTINGS = 74] = "CODE_CSM_TEXT_SETTINGS"; - b[b.CODE_DEFINE_FONT3 = 75] = "CODE_DEFINE_FONT3"; - b[b.CODE_SYMBOL_CLASS = 76] = "CODE_SYMBOL_CLASS"; - b[b.CODE_METADATA = 77] = "CODE_METADATA"; - b[b.CODE_DEFINE_SCALING_GRID = 78] = "CODE_DEFINE_SCALING_GRID"; - b[b.CODE_DO_ABC = 82] = "CODE_DO_ABC"; - b[b.CODE_DEFINE_SHAPE4 = 83] = "CODE_DEFINE_SHAPE4"; - b[b.CODE_DEFINE_MORPH_SHAPE2 = 84] = "CODE_DEFINE_MORPH_SHAPE2"; - b[b.CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA = 86] = "CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA"; - b[b.CODE_DEFINE_BINARY_DATA = 87] = "CODE_DEFINE_BINARY_DATA"; - b[b.CODE_DEFINE_FONT_NAME = 88] = "CODE_DEFINE_FONT_NAME"; - b[b.CODE_START_SOUND2 = 89] = "CODE_START_SOUND2"; - b[b.CODE_DEFINE_BITS_JPEG4 = 90] = "CODE_DEFINE_BITS_JPEG4"; - b[b.CODE_DEFINE_FONT4 = 91] = "CODE_DEFINE_FONT4"; - })(b.SwfTag || (b.SwfTag = {})); - (function(b) { - b[b.Reserved = 2048] = "Reserved"; - b[b.OpaqueBackground = 1024] = "OpaqueBackground"; - b[b.HasVisible = 512] = "HasVisible"; - b[b.HasImage = 256] = "HasImage"; - b[b.HasClassName = 2048] = "HasClassName"; - b[b.HasCacheAsBitmap = 1024] = "HasCacheAsBitmap"; - b[b.HasBlendMode = 512] = "HasBlendMode"; - b[b.HasFilterList = 256] = "HasFilterList"; - b[b.HasClipActions = 128] = "HasClipActions"; - b[b.HasClipDepth = 64] = "HasClipDepth"; - b[b.HasName = 32] = "HasName"; - b[b.HasRatio = 16] = "HasRatio"; - b[b.HasColorTransform = 8] = "HasColorTransform"; - b[b.HasMatrix = 4] = "HasMatrix"; - b[b.HasCharacter = 2] = "HasCharacter"; - b[b.Move = 1] = "Move"; - })(b.PlaceObjectFlags || (b.PlaceObjectFlags = {})); - })(b.Parser || (b.Parser = {})); - })(b.SWF || (b.SWF = {})); -})(Shumway || (Shumway = {})); -(function(b) { - var k = b.Debug.unexpected, g = function() { - function b(g, f, m, d) { - this.url = g; - this.method = f; - this.mimeType = m; - this.data = d; - } - b.prototype.readAll = function(b) { - var g = this.url, m = new XMLHttpRequest({mozSystem:!0}); - m.open(this.method || "GET", this.url, !0); - m.responseType = "arraybuffer"; - m.onreadystatechange = function(d) { - 4 === m.readyState && (200 !== m.status && 0 !== m.status || null === m.response ? (k("Path: " + g + " not found."), b(null, m.statusText)) : b(m.response)); +(function(c) { + (function(c) { + (function(a) { + (function(a) { + a[a.CODE_END = 0] = "CODE_END"; + a[a.CODE_SHOW_FRAME = 1] = "CODE_SHOW_FRAME"; + a[a.CODE_DEFINE_SHAPE = 2] = "CODE_DEFINE_SHAPE"; + a[a.CODE_FREE_CHARACTER = 3] = "CODE_FREE_CHARACTER"; + a[a.CODE_PLACE_OBJECT = 4] = "CODE_PLACE_OBJECT"; + a[a.CODE_REMOVE_OBJECT = 5] = "CODE_REMOVE_OBJECT"; + a[a.CODE_DEFINE_BITS = 6] = "CODE_DEFINE_BITS"; + a[a.CODE_DEFINE_BUTTON = 7] = "CODE_DEFINE_BUTTON"; + a[a.CODE_JPEG_TABLES = 8] = "CODE_JPEG_TABLES"; + a[a.CODE_SET_BACKGROUND_COLOR = 9] = "CODE_SET_BACKGROUND_COLOR"; + a[a.CODE_DEFINE_FONT = 10] = "CODE_DEFINE_FONT"; + a[a.CODE_DEFINE_TEXT = 11] = "CODE_DEFINE_TEXT"; + a[a.CODE_DO_ACTION = 12] = "CODE_DO_ACTION"; + a[a.CODE_DEFINE_FONT_INFO = 13] = "CODE_DEFINE_FONT_INFO"; + a[a.CODE_DEFINE_SOUND = 14] = "CODE_DEFINE_SOUND"; + a[a.CODE_START_SOUND = 15] = "CODE_START_SOUND"; + a[a.CODE_STOP_SOUND = 16] = "CODE_STOP_SOUND"; + a[a.CODE_DEFINE_BUTTON_SOUND = 17] = "CODE_DEFINE_BUTTON_SOUND"; + a[a.CODE_SOUND_STREAM_HEAD = 18] = "CODE_SOUND_STREAM_HEAD"; + a[a.CODE_SOUND_STREAM_BLOCK = 19] = "CODE_SOUND_STREAM_BLOCK"; + a[a.CODE_DEFINE_BITS_LOSSLESS = 20] = "CODE_DEFINE_BITS_LOSSLESS"; + a[a.CODE_DEFINE_BITS_JPEG2 = 21] = "CODE_DEFINE_BITS_JPEG2"; + a[a.CODE_DEFINE_SHAPE2 = 22] = "CODE_DEFINE_SHAPE2"; + a[a.CODE_DEFINE_BUTTON_CXFORM = 23] = "CODE_DEFINE_BUTTON_CXFORM"; + a[a.CODE_PROTECT = 24] = "CODE_PROTECT"; + a[a.CODE_PATHS_ARE_POSTSCRIPT = 25] = "CODE_PATHS_ARE_POSTSCRIPT"; + a[a.CODE_PLACE_OBJECT2 = 26] = "CODE_PLACE_OBJECT2"; + a[a.CODE_REMOVE_OBJECT2 = 28] = "CODE_REMOVE_OBJECT2"; + a[a.CODE_SYNC_FRAME = 29] = "CODE_SYNC_FRAME"; + a[a.CODE_FREE_ALL = 31] = "CODE_FREE_ALL"; + a[a.CODE_DEFINE_SHAPE3 = 32] = "CODE_DEFINE_SHAPE3"; + a[a.CODE_DEFINE_TEXT2 = 33] = "CODE_DEFINE_TEXT2"; + a[a.CODE_DEFINE_BUTTON2 = 34] = "CODE_DEFINE_BUTTON2"; + a[a.CODE_DEFINE_BITS_JPEG3 = 35] = "CODE_DEFINE_BITS_JPEG3"; + a[a.CODE_DEFINE_BITS_LOSSLESS2 = 36] = "CODE_DEFINE_BITS_LOSSLESS2"; + a[a.CODE_DEFINE_EDIT_TEXT = 37] = "CODE_DEFINE_EDIT_TEXT"; + a[a.CODE_DEFINE_VIDEO = 38] = "CODE_DEFINE_VIDEO"; + a[a.CODE_DEFINE_SPRITE = 39] = "CODE_DEFINE_SPRITE"; + a[a.CODE_NAME_CHARACTER = 40] = "CODE_NAME_CHARACTER"; + a[a.CODE_PRODUCT_INFO = 41] = "CODE_PRODUCT_INFO"; + a[a.CODE_DEFINE_TEXT_FORMAT = 42] = "CODE_DEFINE_TEXT_FORMAT"; + a[a.CODE_FRAME_LABEL = 43] = "CODE_FRAME_LABEL"; + a[a.CODE_DEFINE_BEHAVIOUR = 44] = "CODE_DEFINE_BEHAVIOUR"; + a[a.CODE_SOUND_STREAM_HEAD2 = 45] = "CODE_SOUND_STREAM_HEAD2"; + a[a.CODE_DEFINE_MORPH_SHAPE = 46] = "CODE_DEFINE_MORPH_SHAPE"; + a[a.CODE_GENERATE_FRAME = 47] = "CODE_GENERATE_FRAME"; + a[a.CODE_DEFINE_FONT2 = 48] = "CODE_DEFINE_FONT2"; + a[a.CODE_GEN_COMMAND = 49] = "CODE_GEN_COMMAND"; + a[a.CODE_DEFINE_COMMAND_OBJECT = 50] = "CODE_DEFINE_COMMAND_OBJECT"; + a[a.CODE_CHARACTER_SET = 51] = "CODE_CHARACTER_SET"; + a[a.CODE_EXTERNAL_FONT = 52] = "CODE_EXTERNAL_FONT"; + a[a.CODE_DEFINE_FUNCTION = 53] = "CODE_DEFINE_FUNCTION"; + a[a.CODE_PLACE_FUNCTION = 54] = "CODE_PLACE_FUNCTION"; + a[a.CODE_GEN_TAG_OBJECTS = 55] = "CODE_GEN_TAG_OBJECTS"; + a[a.CODE_EXPORT_ASSETS = 56] = "CODE_EXPORT_ASSETS"; + a[a.CODE_IMPORT_ASSETS = 57] = "CODE_IMPORT_ASSETS"; + a[a.CODE_ENABLE_DEBUGGER = 58] = "CODE_ENABLE_DEBUGGER"; + a[a.CODE_DO_INIT_ACTION = 59] = "CODE_DO_INIT_ACTION"; + a[a.CODE_DEFINE_VIDEO_STREAM = 60] = "CODE_DEFINE_VIDEO_STREAM"; + a[a.CODE_VIDEO_FRAME = 61] = "CODE_VIDEO_FRAME"; + a[a.CODE_DEFINE_FONT_INFO2 = 62] = "CODE_DEFINE_FONT_INFO2"; + a[a.CODE_DEBUG_ID = 63] = "CODE_DEBUG_ID"; + a[a.CODE_ENABLE_DEBUGGER2 = 64] = "CODE_ENABLE_DEBUGGER2"; + a[a.CODE_SCRIPT_LIMITS = 65] = "CODE_SCRIPT_LIMITS"; + a[a.CODE_SET_TAB_INDEX = 66] = "CODE_SET_TAB_INDEX"; + a[a.CODE_FILE_ATTRIBUTES = 69] = "CODE_FILE_ATTRIBUTES"; + a[a.CODE_PLACE_OBJECT3 = 70] = "CODE_PLACE_OBJECT3"; + a[a.CODE_IMPORT_ASSETS2 = 71] = "CODE_IMPORT_ASSETS2"; + a[a.CODE_DO_ABC_DEFINE = 72] = "CODE_DO_ABC_DEFINE"; + a[a.CODE_DEFINE_FONT_ALIGN_ZONES = 73] = "CODE_DEFINE_FONT_ALIGN_ZONES"; + a[a.CODE_CSM_TEXT_SETTINGS = 74] = "CODE_CSM_TEXT_SETTINGS"; + a[a.CODE_DEFINE_FONT3 = 75] = "CODE_DEFINE_FONT3"; + a[a.CODE_SYMBOL_CLASS = 76] = "CODE_SYMBOL_CLASS"; + a[a.CODE_METADATA = 77] = "CODE_METADATA"; + a[a.CODE_DEFINE_SCALING_GRID = 78] = "CODE_DEFINE_SCALING_GRID"; + a[a.CODE_DO_ABC = 82] = "CODE_DO_ABC"; + a[a.CODE_DEFINE_SHAPE4 = 83] = "CODE_DEFINE_SHAPE4"; + a[a.CODE_DEFINE_MORPH_SHAPE2 = 84] = "CODE_DEFINE_MORPH_SHAPE2"; + a[a.CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA = 86] = "CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA"; + a[a.CODE_DEFINE_BINARY_DATA = 87] = "CODE_DEFINE_BINARY_DATA"; + a[a.CODE_DEFINE_FONT_NAME = 88] = "CODE_DEFINE_FONT_NAME"; + a[a.CODE_START_SOUND2 = 89] = "CODE_START_SOUND2"; + a[a.CODE_DEFINE_BITS_JPEG4 = 90] = "CODE_DEFINE_BITS_JPEG4"; + a[a.CODE_DEFINE_FONT4 = 91] = "CODE_DEFINE_FONT4"; + })(a.SwfTag || (a.SwfTag = {})); + (function(a) { + a[a.CODE_DEFINE_SHAPE = 2] = "CODE_DEFINE_SHAPE"; + a[a.CODE_DEFINE_BITS = 6] = "CODE_DEFINE_BITS"; + a[a.CODE_DEFINE_BUTTON = 7] = "CODE_DEFINE_BUTTON"; + a[a.CODE_DEFINE_FONT = 10] = "CODE_DEFINE_FONT"; + a[a.CODE_DEFINE_TEXT = 11] = "CODE_DEFINE_TEXT"; + a[a.CODE_DEFINE_SOUND = 14] = "CODE_DEFINE_SOUND"; + a[a.CODE_DEFINE_BITS_LOSSLESS = 20] = "CODE_DEFINE_BITS_LOSSLESS"; + a[a.CODE_DEFINE_BITS_JPEG2 = 21] = "CODE_DEFINE_BITS_JPEG2"; + a[a.CODE_DEFINE_SHAPE2 = 22] = "CODE_DEFINE_SHAPE2"; + a[a.CODE_DEFINE_SHAPE3 = 32] = "CODE_DEFINE_SHAPE3"; + a[a.CODE_DEFINE_TEXT2 = 33] = "CODE_DEFINE_TEXT2"; + a[a.CODE_DEFINE_BUTTON2 = 34] = "CODE_DEFINE_BUTTON2"; + a[a.CODE_DEFINE_BITS_JPEG3 = 35] = "CODE_DEFINE_BITS_JPEG3"; + a[a.CODE_DEFINE_BITS_LOSSLESS2 = 36] = "CODE_DEFINE_BITS_LOSSLESS2"; + a[a.CODE_DEFINE_EDIT_TEXT = 37] = "CODE_DEFINE_EDIT_TEXT"; + a[a.CODE_DEFINE_SPRITE = 39] = "CODE_DEFINE_SPRITE"; + a[a.CODE_DEFINE_MORPH_SHAPE = 46] = "CODE_DEFINE_MORPH_SHAPE"; + a[a.CODE_DEFINE_FONT2 = 48] = "CODE_DEFINE_FONT2"; + a[a.CODE_DEFINE_VIDEO_STREAM = 60] = "CODE_DEFINE_VIDEO_STREAM"; + a[a.CODE_DEFINE_FONT3 = 75] = "CODE_DEFINE_FONT3"; + a[a.CODE_DEFINE_SHAPE4 = 83] = "CODE_DEFINE_SHAPE4"; + a[a.CODE_DEFINE_MORPH_SHAPE2 = 84] = "CODE_DEFINE_MORPH_SHAPE2"; + a[a.CODE_DEFINE_BINARY_DATA = 87] = "CODE_DEFINE_BINARY_DATA"; + a[a.CODE_DEFINE_BITS_JPEG4 = 90] = "CODE_DEFINE_BITS_JPEG4"; + a[a.CODE_DEFINE_FONT4 = 91] = "CODE_DEFINE_FONT4"; + })(a.DefinitionTags || (a.DefinitionTags = {})); + (function(a) { + a[a.CODE_DEFINE_BITS = 6] = "CODE_DEFINE_BITS"; + a[a.CODE_DEFINE_BITS_JPEG2 = 21] = "CODE_DEFINE_BITS_JPEG2"; + a[a.CODE_DEFINE_BITS_JPEG3 = 35] = "CODE_DEFINE_BITS_JPEG3"; + a[a.CODE_DEFINE_BITS_JPEG4 = 90] = "CODE_DEFINE_BITS_JPEG4"; + })(a.ImageDefinitionTags || (a.ImageDefinitionTags = {})); + (function(a) { + a[a.CODE_DEFINE_FONT = 10] = "CODE_DEFINE_FONT"; + a[a.CODE_DEFINE_FONT2 = 48] = "CODE_DEFINE_FONT2"; + a[a.CODE_DEFINE_FONT3 = 75] = "CODE_DEFINE_FONT3"; + a[a.CODE_DEFINE_FONT4 = 91] = "CODE_DEFINE_FONT4"; + })(a.FontDefinitionTags || (a.FontDefinitionTags = {})); + (function(a) { + a[a.CODE_PLACE_OBJECT = 4] = "CODE_PLACE_OBJECT"; + a[a.CODE_PLACE_OBJECT2 = 26] = "CODE_PLACE_OBJECT2"; + a[a.CODE_PLACE_OBJECT3 = 70] = "CODE_PLACE_OBJECT3"; + a[a.CODE_REMOVE_OBJECT = 5] = "CODE_REMOVE_OBJECT"; + a[a.CODE_REMOVE_OBJECT2 = 28] = "CODE_REMOVE_OBJECT2"; + a[a.CODE_START_SOUND = 15] = "CODE_START_SOUND"; + a[a.CODE_START_SOUND2 = 89] = "CODE_START_SOUND2"; + a[a.CODE_VIDEO_FRAME = 61] = "CODE_VIDEO_FRAME"; + })(a.ControlTags || (a.ControlTags = {})); + (function(a) { + a[a.Move = 1] = "Move"; + a[a.HasCharacter = 2] = "HasCharacter"; + a[a.HasMatrix = 4] = "HasMatrix"; + a[a.HasColorTransform = 8] = "HasColorTransform"; + a[a.HasRatio = 16] = "HasRatio"; + a[a.HasName = 32] = "HasName"; + a[a.HasClipDepth = 64] = "HasClipDepth"; + a[a.HasClipActions = 128] = "HasClipActions"; + a[a.HasFilterList = 256] = "HasFilterList"; + a[a.HasBlendMode = 512] = "HasBlendMode"; + a[a.HasCacheAsBitmap = 1024] = "HasCacheAsBitmap"; + a[a.HasClassName = 2048] = "HasClassName"; + a[a.HasImage = 4096] = "HasImage"; + a[a.HasVisible = 8192] = "HasVisible"; + a[a.OpaqueBackground = 16384] = "OpaqueBackground"; + a[a.Reserved = 32768] = "Reserved"; + })(a.PlaceObjectFlags || (a.PlaceObjectFlags = {})); + (function(a) { + a[a.Load = 1] = "Load"; + a[a.EnterFrame = 2] = "EnterFrame"; + a[a.Unload = 4] = "Unload"; + a[a.MouseMove = 8] = "MouseMove"; + a[a.MouseDown = 16] = "MouseDown"; + a[a.MouseUp = 32] = "MouseUp"; + a[a.KeyDown = 64] = "KeyDown"; + a[a.KeyUp = 128] = "KeyUp"; + a[a.Data = 256] = "Data"; + a[a.Initialize = 512] = "Initialize"; + a[a.Press = 1024] = "Press"; + a[a.Release = 2048] = "Release"; + a[a.ReleaseOutside = 4096] = "ReleaseOutside"; + a[a.RollOver = 8192] = "RollOver"; + a[a.RollOut = 16384] = "RollOut"; + a[a.DragOver = 32768] = "DragOver"; + a[a.DragOut = 65536] = "DragOut"; + a[a.KeyPress = 131072] = "KeyPress"; + a[a.Construct = 262144] = "Construct"; + })(a.AVM1ClipEvents || (a.AVM1ClipEvents = {})); + })(c.Parser || (c.Parser = {})); + })(c.SWF || (c.SWF = {})); +})(Shumway || (Shumway = {})); +(function(c) { + var h = c.Debug.unexpected, a = function() { + function a(c, p, u, l) { + this.url = c; + this.method = p; + this.mimeType = u; + this.data = l; + } + a.prototype.readAll = function(a) { + var c = this.url, u = new XMLHttpRequest({mozSystem:!0}); + u.open(this.method || "GET", this.url, !0); + u.responseType = "arraybuffer"; + u.onreadystatechange = function(l) { + 4 === u.readyState && (200 !== u.status && 0 !== u.status || null === u.response ? (h("Path: " + c + " not found."), a(null, u.statusText)) : a(u.response)); + }; + this.mimeType && u.setRequestHeader("Content-Type", this.mimeType); + u.send(this.data || null); + }; + a.prototype.readChunked = function(a, c, u, l, e, m) { + if (0 >= a) { + this.readAsync(c, u, l, e, m); + } else { + var t = 0, q = new Uint8Array(a), n = 0, k; + this.readAsync(function(f, d) { + k = d.total; + for (var b = f.length, g = 0;t + b >= a;) { + var r = a - t; + q.set(f.subarray(g, g + r), t); + g += r; + b -= r; + n += a; + c(q, {loaded:n, total:k}); + t = 0; + } + q.set(f.subarray(g), t); + t += b; + }, u, l, function() { + 0 < t && (n += t, c(q.subarray(0, t), {loaded:n, total:k}), t = 0); + e && e(); + }, m); + } + }; + a.prototype.readAsync = function(a, c, u, l, e) { + var m = new XMLHttpRequest({mozSystem:!0}), t = this.url, q = 0, n = 0; + m.open(this.method || "GET", t, !0); + m.responseType = "moz-chunked-arraybuffer"; + var k = "moz-chunked-arraybuffer" !== m.responseType; + k && (m.responseType = "arraybuffer"); + m.onprogress = function(f) { + k || (q = f.loaded, n = f.total, a(new Uint8Array(m.response), {loaded:q, total:n})); + }; + m.onreadystatechange = function(f) { + 2 === m.readyState && e && e(t, m.status, m.getAllResponseHeaders()); + 4 === m.readyState && (200 !== m.status && 0 !== m.status || null === m.response && (0 === n || q !== n) ? c(m.statusText) : (k && (f = m.response, a(new Uint8Array(f), {loaded:0, total:f.byteLength})), l && l())); }; this.mimeType && m.setRequestHeader("Content-Type", this.mimeType); m.send(this.data || null); + u && u(); }; - b.prototype.readAsync = function(b, g, m, d, a) { - var c = new XMLHttpRequest({mozSystem:!0}), n = this.url, p = 0, e = 0; - c.open(this.method || "GET", n, !0); - c.responseType = "moz-chunked-arraybuffer"; - var q = "moz-chunked-arraybuffer" !== c.responseType; - q && (c.responseType = "arraybuffer"); - c.onprogress = function(a) { - q || (p = a.loaded, e = a.total, b(new Uint8Array(c.response), {loaded:p, total:e})); - }; - c.onreadystatechange = function(l) { - 2 === c.readyState && a && a(n, c.status, c.getAllResponseHeaders()); - 4 === c.readyState && (200 !== c.status && 0 !== c.status || null === c.response && (0 === e || p !== e) ? g(c.statusText) : (q && (l = c.response, b(new Uint8Array(l), {loaded:0, total:l.byteLength})), d && d())); - }; - this.mimeType && c.setRequestHeader("Content-Type", this.mimeType); - c.send(this.data || null); - m && m(); - }; - return b; + return a; }(); - b.BinaryFileReader = g; -})(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - b[b.Objects = 0] = "Objects"; - b[b.References = 1] = "References"; - })(b.RemotingPhase || (b.RemotingPhase = {})); - (function(b) { - b[b.HasMatrix = 1] = "HasMatrix"; - b[b.HasBounds = 2] = "HasBounds"; - b[b.HasChildren = 4] = "HasChildren"; - b[b.HasColorTransform = 8] = "HasColorTransform"; - b[b.HasClipRect = 16] = "HasClipRect"; - b[b.HasMiscellaneousProperties = 32] = "HasMiscellaneousProperties"; - b[b.HasMask = 64] = "HasMask"; - b[b.HasClip = 128] = "HasClip"; - })(b.MessageBits || (b.MessageBits = {})); - (function(b) { - b[b.None = 0] = "None"; - b[b.Asset = 134217728] = "Asset"; - })(b.IDMask || (b.IDMask = {})); - (function(b) { - b[b.EOF = 0] = "EOF"; - b[b.UpdateFrame = 100] = "UpdateFrame"; - b[b.UpdateGraphics = 101] = "UpdateGraphics"; - b[b.UpdateBitmapData = 102] = "UpdateBitmapData"; - b[b.UpdateTextContent = 103] = "UpdateTextContent"; - b[b.UpdateStage = 104] = "UpdateStage"; - b[b.UpdateNetStream = 105] = "UpdateNetStream"; - b[b.RequestBitmapData = 106] = "RequestBitmapData"; - b[b.DecodeImage = 107] = "DecodeImage"; - b[b.DecodeImageResponse = 108] = "DecodeImageResponse"; - b[b.RegisterFont = 200] = "RegisterFont"; - b[b.DrawToBitmap = 201] = "DrawToBitmap"; - b[b.MouseEvent = 300] = "MouseEvent"; - b[b.KeyboardEvent = 301] = "KeyboardEvent"; - b[b.FocusEvent = 302] = "FocusEvent"; - })(b.MessageTag || (b.MessageTag = {})); - (function(b) { - b[b.Identity = 0] = "Identity"; - b[b.AlphaMultiplierOnly = 1] = "AlphaMultiplierOnly"; - b[b.All = 2] = "All"; - })(b.ColorTransformEncoding || (b.ColorTransformEncoding = {})); - b.MouseEventNames = ["click", "dblclick", "mousedown", "mousemove", "mouseup"]; - b.KeyboardEventNames = ["keydown", "keypress", "keyup"]; - (function(b) { - b[b.CtrlKey = 1] = "CtrlKey"; - b[b.AltKey = 2] = "AltKey"; - b[b.ShiftKey = 4] = "ShiftKey"; - })(b.KeyboardEventFlags || (b.KeyboardEventFlags = {})); - (function(b) { - b[b.DocumentHidden = 0] = "DocumentHidden"; - b[b.DocumentVisible = 1] = "DocumentVisible"; - b[b.WindowBlur = 2] = "WindowBlur"; - b[b.WindowFocus = 3] = "WindowFocus"; - })(b.FocusEventType || (b.FocusEventType = {})); - })(b.Remoting || (b.Remoting = {})); + c.BinaryFileReader = a; })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - var f = function() { - function b() { +(function(c) { + (function(c) { + (function(a) { + a[a.Objects = 0] = "Objects"; + a[a.References = 1] = "References"; + })(c.RemotingPhase || (c.RemotingPhase = {})); + (function(a) { + a[a.HasMatrix = 1] = "HasMatrix"; + a[a.HasBounds = 2] = "HasBounds"; + a[a.HasChildren = 4] = "HasChildren"; + a[a.HasColorTransform = 8] = "HasColorTransform"; + a[a.HasClipRect = 16] = "HasClipRect"; + a[a.HasMiscellaneousProperties = 32] = "HasMiscellaneousProperties"; + a[a.HasMask = 64] = "HasMask"; + a[a.HasClip = 128] = "HasClip"; + })(c.MessageBits || (c.MessageBits = {})); + (function(a) { + a[a.None = 0] = "None"; + a[a.Asset = 134217728] = "Asset"; + })(c.IDMask || (c.IDMask = {})); + (function(a) { + a[a.EOF = 0] = "EOF"; + a[a.UpdateFrame = 100] = "UpdateFrame"; + a[a.UpdateGraphics = 101] = "UpdateGraphics"; + a[a.UpdateBitmapData = 102] = "UpdateBitmapData"; + a[a.UpdateTextContent = 103] = "UpdateTextContent"; + a[a.UpdateStage = 104] = "UpdateStage"; + a[a.UpdateNetStream = 105] = "UpdateNetStream"; + a[a.RequestBitmapData = 106] = "RequestBitmapData"; + a[a.DrawToBitmap = 200] = "DrawToBitmap"; + a[a.MouseEvent = 300] = "MouseEvent"; + a[a.KeyboardEvent = 301] = "KeyboardEvent"; + a[a.FocusEvent = 302] = "FocusEvent"; + })(c.MessageTag || (c.MessageTag = {})); + (function(a) { + a[a.Blur = 0] = "Blur"; + a[a.DropShadow = 1] = "DropShadow"; + })(c.FilterType || (c.FilterType = {})); + (function(a) { + a[a.Identity = 0] = "Identity"; + a[a.AlphaMultiplierOnly = 1] = "AlphaMultiplierOnly"; + a[a.All = 2] = "All"; + })(c.ColorTransformEncoding || (c.ColorTransformEncoding = {})); + (function(a) { + a[a.Initialized = 0] = "Initialized"; + a[a.PlayStart = 1] = "PlayStart"; + a[a.PlayStop = 2] = "PlayStop"; + a[a.BufferFull = 3] = "BufferFull"; + a[a.Progress = 4] = "Progress"; + a[a.BufferEmpty = 5] = "BufferEmpty"; + a[a.Error = 6] = "Error"; + a[a.Metadata = 7] = "Metadata"; + a[a.Seeking = 8] = "Seeking"; + })(c.VideoPlaybackEvent || (c.VideoPlaybackEvent = {})); + (function(a) { + a[a.Init = 1] = "Init"; + a[a.Pause = 2] = "Pause"; + a[a.Seek = 3] = "Seek"; + a[a.GetTime = 4] = "GetTime"; + a[a.GetBufferLength = 5] = "GetBufferLength"; + a[a.SetSoundLevels = 6] = "SetSoundLevels"; + a[a.GetBytesLoaded = 7] = "GetBytesLoaded"; + a[a.GetBytesTotal = 8] = "GetBytesTotal"; + })(c.VideoControlEvent || (c.VideoControlEvent = {})); + (function(a) { + a[a.ShowAll = 0] = "ShowAll"; + a[a.ExactFit = 1] = "ExactFit"; + a[a.NoBorder = 2] = "NoBorder"; + a[a.NoScale = 4] = "NoScale"; + })(c.StageScaleMode || (c.StageScaleMode = {})); + (function(a) { + a[a.None = 0] = "None"; + a[a.Top = 1] = "Top"; + a[a.Bottom = 2] = "Bottom"; + a[a.Left = 4] = "Left"; + a[a.Right = 8] = "Right"; + a[a.TopLeft = a.Top | a.Left] = "TopLeft"; + a[a.BottomLeft = a.Bottom | a.Left] = "BottomLeft"; + a[a.BottomRight = a.Bottom | a.Right] = "BottomRight"; + a[a.TopRight = a.Top | a.Right] = "TopRight"; + })(c.StageAlignFlags || (c.StageAlignFlags = {})); + c.MouseEventNames = "click dblclick mousedown mousemove mouseup mouseover mouseout".split(" "); + c.KeyboardEventNames = ["keydown", "keypress", "keyup"]; + (function(a) { + a[a.CtrlKey = 1] = "CtrlKey"; + a[a.AltKey = 2] = "AltKey"; + a[a.ShiftKey = 4] = "ShiftKey"; + })(c.KeyboardEventFlags || (c.KeyboardEventFlags = {})); + (function(a) { + a[a.DocumentHidden = 0] = "DocumentHidden"; + a[a.DocumentVisible = 1] = "DocumentVisible"; + a[a.WindowBlur = 2] = "WindowBlur"; + a[a.WindowFocus = 3] = "WindowFocus"; + })(c.FocusEventType || (c.FocusEventType = {})); + })(c.Remoting || (c.Remoting = {})); +})(Shumway || (Shumway = {})); +var throwError, Errors; +(function(c) { + (function(c) { + (function(a) { + var c = function() { + function a() { } - b.toRGBA = function(b, d, a, c) { - "undefined" === typeof c && (c = 1); - return "rgba(" + b + "," + d + "," + a + "," + c + ")"; + a.toRGBA = function(a, l, e, m) { + void 0 === m && (m = 1); + return "rgba(" + a + "," + l + "," + e + "," + m + ")"; }; - return b; + return a; }(); - b.UI = f; - var k = function() { - function b() { + a.UI = c; + var h = function() { + function a() { } - b.prototype.tabToolbar = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(37, 44, 51, b); - }; - b.prototype.toolbars = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(52, 60, 69, b); - }; - b.prototype.selectionBackground = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(29, 79, 115, b); - }; - b.prototype.selectionText = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(245, 247, 250, b); - }; - b.prototype.splitters = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(0, 0, 0, b); - }; - b.prototype.bodyBackground = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(17, 19, 21, b); - }; - b.prototype.sidebarBackground = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(24, 29, 32, b); - }; - b.prototype.attentionBackground = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(161, 134, 80, b); - }; - b.prototype.bodyText = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(143, 161, 178, b); - }; - b.prototype.foregroundTextGrey = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(182, 186, 191, b); - }; - b.prototype.contentTextHighContrast = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(169, 186, 203, b); - }; - b.prototype.contentTextGrey = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(143, 161, 178, b); - }; - b.prototype.contentTextDarkGrey = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(95, 115, 135, b); - }; - b.prototype.blueHighlight = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(70, 175, 227, b); - }; - b.prototype.purpleHighlight = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(107, 122, 187, b); - }; - b.prototype.pinkHighlight = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(223, 128, 255, b); - }; - b.prototype.redHighlight = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(235, 83, 104, b); - }; - b.prototype.orangeHighlight = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(217, 102, 41, b); - }; - b.prototype.lightOrangeHighlight = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(217, 155, 40, b); - }; - b.prototype.greenHighlight = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(112, 191, 83, b); - }; - b.prototype.blueGreyHighlight = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(94, 136, 176, b); + a.prototype.tabToolbar = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(37, 44, 51, a); + }; + a.prototype.toolbars = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(52, 60, 69, a); + }; + a.prototype.selectionBackground = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(29, 79, 115, a); + }; + a.prototype.selectionText = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(245, 247, 250, a); + }; + a.prototype.splitters = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(0, 0, 0, a); + }; + a.prototype.bodyBackground = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(17, 19, 21, a); + }; + a.prototype.sidebarBackground = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(24, 29, 32, a); + }; + a.prototype.attentionBackground = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(161, 134, 80, a); + }; + a.prototype.bodyText = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(143, 161, 178, a); + }; + a.prototype.foregroundTextGrey = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(182, 186, 191, a); + }; + a.prototype.contentTextHighContrast = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(169, 186, 203, a); + }; + a.prototype.contentTextGrey = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(143, 161, 178, a); + }; + a.prototype.contentTextDarkGrey = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(95, 115, 135, a); + }; + a.prototype.blueHighlight = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(70, 175, 227, a); + }; + a.prototype.purpleHighlight = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(107, 122, 187, a); + }; + a.prototype.pinkHighlight = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(223, 128, 255, a); + }; + a.prototype.redHighlight = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(235, 83, 104, a); + }; + a.prototype.orangeHighlight = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(217, 102, 41, a); + }; + a.prototype.lightOrangeHighlight = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(217, 155, 40, a); + }; + a.prototype.greenHighlight = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(112, 191, 83, a); + }; + a.prototype.blueGreyHighlight = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(94, 136, 176, a); }; - return b; + return a; }(); - b.UIThemeDark = k; - k = function() { - function b() { + a.UIThemeDark = h; + h = function() { + function a() { } - b.prototype.tabToolbar = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(235, 236, 237, b); - }; - b.prototype.toolbars = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(240, 241, 242, b); - }; - b.prototype.selectionBackground = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(76, 158, 217, b); - }; - b.prototype.selectionText = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(245, 247, 250, b); - }; - b.prototype.splitters = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(170, 170, 170, b); - }; - b.prototype.bodyBackground = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(252, 252, 252, b); - }; - b.prototype.sidebarBackground = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(247, 247, 247, b); - }; - b.prototype.attentionBackground = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(161, 134, 80, b); - }; - b.prototype.bodyText = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(24, 25, 26, b); - }; - b.prototype.foregroundTextGrey = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(88, 89, 89, b); - }; - b.prototype.contentTextHighContrast = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(41, 46, 51, b); - }; - b.prototype.contentTextGrey = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(143, 161, 178, b); - }; - b.prototype.contentTextDarkGrey = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(102, 115, 128, b); - }; - b.prototype.blueHighlight = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(0, 136, 204, b); - }; - b.prototype.purpleHighlight = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(91, 95, 255, b); - }; - b.prototype.pinkHighlight = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(184, 46, 229, b); - }; - b.prototype.redHighlight = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(237, 38, 85, b); - }; - b.prototype.orangeHighlight = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(241, 60, 0, b); - }; - b.prototype.lightOrangeHighlight = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(217, 126, 0, b); - }; - b.prototype.greenHighlight = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(44, 187, 15, b); - }; - b.prototype.blueGreyHighlight = function(b) { - "undefined" === typeof b && (b = 1); - return f.toRGBA(95, 136, 176, b); + a.prototype.tabToolbar = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(235, 236, 237, a); + }; + a.prototype.toolbars = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(240, 241, 242, a); + }; + a.prototype.selectionBackground = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(76, 158, 217, a); + }; + a.prototype.selectionText = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(245, 247, 250, a); + }; + a.prototype.splitters = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(170, 170, 170, a); + }; + a.prototype.bodyBackground = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(252, 252, 252, a); + }; + a.prototype.sidebarBackground = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(247, 247, 247, a); + }; + a.prototype.attentionBackground = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(161, 134, 80, a); + }; + a.prototype.bodyText = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(24, 25, 26, a); + }; + a.prototype.foregroundTextGrey = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(88, 89, 89, a); + }; + a.prototype.contentTextHighContrast = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(41, 46, 51, a); + }; + a.prototype.contentTextGrey = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(143, 161, 178, a); + }; + a.prototype.contentTextDarkGrey = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(102, 115, 128, a); + }; + a.prototype.blueHighlight = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(0, 136, 204, a); + }; + a.prototype.purpleHighlight = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(91, 95, 255, a); + }; + a.prototype.pinkHighlight = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(184, 46, 229, a); + }; + a.prototype.redHighlight = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(237, 38, 85, a); + }; + a.prototype.orangeHighlight = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(241, 60, 0, a); + }; + a.prototype.lightOrangeHighlight = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(217, 126, 0, a); + }; + a.prototype.greenHighlight = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(44, 187, 15, a); + }; + a.prototype.blueGreyHighlight = function(a) { + void 0 === a && (a = 1); + return c.toRGBA(95, 136, 176, a); }; - return b; + return a; }(); - b.UIThemeLight = k; - })(b.Theme || (b.Theme = {})); - })(b.Tools || (b.Tools = {})); + a.UIThemeLight = h; + })(c.Theme || (c.Theme = {})); + })(c.Tools || (c.Tools = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - var f = function() { - function b(f) { - this._buffers = f || []; +(function(c) { + (function(c) { + (function(a) { + var c = function() { + function a(c) { + this._buffers = c || []; this._snapshots = []; this._maxDepth = 0; } - b.prototype.addBuffer = function(b) { - this._buffers.push(b); + a.prototype.addBuffer = function(a) { + this._buffers.push(a); }; - b.prototype.getSnapshotAt = function(b) { - return this._snapshots[b]; + a.prototype.getSnapshotAt = function(a) { + return this._snapshots[a]; }; - Object.defineProperty(b.prototype, "hasSnapshots", {get:function() { + Object.defineProperty(a.prototype, "hasSnapshots", {get:function() { return 0 < this.snapshotCount; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "snapshotCount", {get:function() { + Object.defineProperty(a.prototype, "snapshotCount", {get:function() { return this._snapshots.length; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "startTime", {get:function() { + Object.defineProperty(a.prototype, "startTime", {get:function() { return this._startTime; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "endTime", {get:function() { + Object.defineProperty(a.prototype, "endTime", {get:function() { return this._endTime; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "totalTime", {get:function() { + Object.defineProperty(a.prototype, "totalTime", {get:function() { return this.endTime - this.startTime; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "windowStart", {get:function() { + Object.defineProperty(a.prototype, "windowStart", {get:function() { return this._windowStart; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "windowEnd", {get:function() { + Object.defineProperty(a.prototype, "windowEnd", {get:function() { return this._windowEnd; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "windowLength", {get:function() { + Object.defineProperty(a.prototype, "windowLength", {get:function() { return this.windowEnd - this.windowStart; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "maxDepth", {get:function() { + Object.defineProperty(a.prototype, "maxDepth", {get:function() { return this._maxDepth; }, enumerable:!0, configurable:!0}); - b.prototype.forEachSnapshot = function(b) { - for (var m = 0, d = this.snapshotCount;m < d;m++) { - b(this._snapshots[m], m); + a.prototype.forEachSnapshot = function(a) { + for (var c = 0, l = this.snapshotCount;c < l;c++) { + a(this._snapshots[c], c); } }; - b.prototype.createSnapshots = function() { - var b = Number.MAX_VALUE, m = Number.MIN_VALUE, d = 0; + a.prototype.createSnapshots = function() { + var a = Number.MAX_VALUE, c = Number.MIN_VALUE, l = 0; for (this._snapshots = [];0 < this._buffers.length;) { - var a = this._buffers.shift().createSnapshot(); - a && (b > a.startTime && (b = a.startTime), m < a.endTime && (m = a.endTime), d < a.maxDepth && (d = a.maxDepth), this._snapshots.push(a)); + var e = this._buffers.shift().createSnapshot(); + e && (a > e.startTime && (a = e.startTime), c < e.endTime && (c = e.endTime), l < e.maxDepth && (l = e.maxDepth), this._snapshots.push(e)); } - this._startTime = b; - this._endTime = m; - this._windowStart = b; - this._windowEnd = m; - this._maxDepth = d; - }; - b.prototype.setWindow = function(b, m) { - if (b > m) { - var d = b; - b = m; - m = d; - } - d = Math.min(m - b, this.totalTime); - b < this._startTime ? (b = this._startTime, m = this._startTime + d) : m > this._endTime && (b = this._endTime - d, m = this._endTime); - this._windowStart = b; - this._windowEnd = m; + this._startTime = a; + this._endTime = c; + this._windowStart = a; + this._windowEnd = c; + this._maxDepth = l; + }; + a.prototype.setWindow = function(a, c) { + if (a > c) { + var l = a; + a = c; + c = l; + } + l = Math.min(c - a, this.totalTime); + a < this._startTime ? (a = this._startTime, c = this._startTime + l) : c > this._endTime && (a = this._endTime - l, c = this._endTime); + this._windowStart = a; + this._windowEnd = c; }; - b.prototype.moveWindowTo = function(b) { - this.setWindow(b - this.windowLength / 2, b + this.windowLength / 2); + a.prototype.moveWindowTo = function(a) { + this.setWindow(a - this.windowLength / 2, a + this.windowLength / 2); }; - return b; + return a; }(); - b.Profile = f; - })(b.Profiler || (b.Profiler = {})); - })(b.Tools || (b.Tools = {})); -})(Shumway || (Shumway = {})); -var __extends = this.__extends || function(b, k) { - function g() { - this.constructor = b; + a.Profile = c; + })(c.Profiler || (c.Profiler = {})); + })(c.Tools || (c.Tools = {})); +})(Shumway || (Shumway = {})); +__extends = this.__extends || function(c, h) { + function a() { + this.constructor = c; } - for (var f in k) { - k.hasOwnProperty(f) && (b[f] = k[f]); + for (var s in h) { + h.hasOwnProperty(s) && (c[s] = h[s]); } - g.prototype = k.prototype; - b.prototype = new g; + a.prototype = h.prototype; + c.prototype = new a; }; -(function(b) { - (function(b) { - (function(b) { - var f = function() { - return function(b) { - this.kind = b; +(function(c) { + (function(c) { + (function(a) { + var c = function() { + return function(a) { + this.kind = a; this.totalTime = this.selfTime = this.count = 0; }; }(); - b.TimelineFrameStatistics = f; - var k = function() { - function b(m, d, a, c, n, p) { - this.parent = m; - this.kind = d; - this.startData = a; - this.endData = c; - this.startTime = n; - this.endTime = p; + a.TimelineFrameStatistics = c; + var h = function() { + function a(c, l, e, m, t, q) { + this.parent = c; + this.kind = l; + this.startData = e; + this.endData = m; + this.startTime = t; + this.endTime = q; this.maxDepth = 0; } - Object.defineProperty(b.prototype, "totalTime", {get:function() { + Object.defineProperty(a.prototype, "totalTime", {get:function() { return this.endTime - this.startTime; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "selfTime", {get:function() { - var b = this.totalTime; + Object.defineProperty(a.prototype, "selfTime", {get:function() { + var a = this.totalTime; if (this.children) { - for (var d = 0, a = this.children.length;d < a;d++) { - var c = this.children[d], b = b - (c.endTime - c.startTime) + for (var l = 0, e = this.children.length;l < e;l++) { + var m = this.children[l], a = a - (m.endTime - m.startTime) } } - return b; + return a; }, enumerable:!0, configurable:!0}); - b.prototype.getChildIndex = function(b) { - for (var d = this.children, a = 0;a < d.length;a++) { - if (d[a].endTime > b) { - return a; + a.prototype.getChildIndex = function(a) { + for (var l = this.children, e = 0;e < l.length;e++) { + if (l[e].endTime > a) { + return e; } } return 0; }; - b.prototype.getChildRange = function(b, d) { - if (this.children && b <= this.endTime && d >= this.startTime && d >= b) { - var a = this._getNearestChild(b), c = this._getNearestChildReverse(d); - if (a <= c) { - return b = this.children[a].startTime, d = this.children[c].endTime, {startIndex:a, endIndex:c, startTime:b, endTime:d, totalTime:d - b}; + a.prototype.getChildRange = function(a, l) { + if (this.children && a <= this.endTime && l >= this.startTime && l >= a) { + var e = this._getNearestChild(a), m = this._getNearestChildReverse(l); + if (e <= m) { + return a = this.children[e].startTime, l = this.children[m].endTime, {startIndex:e, endIndex:m, startTime:a, endTime:l, totalTime:l - a}; } } return null; }; - b.prototype._getNearestChild = function(b) { - var d = this.children; - if (d && d.length) { - if (b <= d[0].endTime) { + a.prototype._getNearestChild = function(a) { + var l = this.children; + if (l && l.length) { + if (a <= l[0].endTime) { return 0; } - for (var a, c = 0, n = d.length - 1;n > c;) { - a = (c + n) / 2 | 0; - var p = d[a]; - if (b >= p.startTime && b <= p.endTime) { - return a; + for (var e, m = 0, c = l.length - 1;c > m;) { + e = (m + c) / 2 | 0; + var q = l[e]; + if (a >= q.startTime && a <= q.endTime) { + return e; } - b > p.endTime ? c = a + 1 : n = a; + a > q.endTime ? m = e + 1 : c = e; } - return Math.ceil((c + n) / 2); + return Math.ceil((m + c) / 2); } return 0; }; - b.prototype._getNearestChildReverse = function(b) { - var d = this.children; - if (d && d.length) { - var a = d.length - 1; - if (b >= d[a].startTime) { - return a; + a.prototype._getNearestChildReverse = function(a) { + var l = this.children; + if (l && l.length) { + var e = l.length - 1; + if (a >= l[e].startTime) { + return e; } - for (var c, n = 0;a > n;) { - c = Math.ceil((n + a) / 2); - var p = d[c]; - if (b >= p.startTime && b <= p.endTime) { - return c; + for (var m, c = 0;e > c;) { + m = Math.ceil((c + e) / 2); + var q = l[m]; + if (a >= q.startTime && a <= q.endTime) { + return m; } - b > p.endTime ? n = c : a = c - 1; + a > q.endTime ? c = m : e = m - 1; } - return(n + a) / 2 | 0; + return(c + e) / 2 | 0; } return 0; }; - b.prototype.query = function(b) { - if (b < this.startTime || b > this.endTime) { + a.prototype.query = function(a) { + if (a < this.startTime || a > this.endTime) { return null; } - var d = this.children; - if (d && 0 < d.length) { - for (var a, c = 0, n = d.length - 1;n > c;) { - var p = (c + n) / 2 | 0; - a = d[p]; - if (b >= a.startTime && b <= a.endTime) { - return a.query(b); - } - b > a.endTime ? c = p + 1 : n = p; - } - a = d[n]; - if (b >= a.startTime && b <= a.endTime) { - return a.query(b); + var l = this.children; + if (l && 0 < l.length) { + for (var e, m = 0, c = l.length - 1;c > m;) { + var q = (m + c) / 2 | 0; + e = l[q]; + if (a >= e.startTime && a <= e.endTime) { + return e.query(a); + } + a > e.endTime ? m = q + 1 : c = q; + } + e = l[c]; + if (a >= e.startTime && a <= e.endTime) { + return e.query(a); } } return this; }; - b.prototype.queryNext = function(b) { - for (var d = this;b > d.endTime;) { - if (d.parent) { - d = d.parent; + a.prototype.queryNext = function(a) { + for (var l = this;a > l.endTime;) { + if (l.parent) { + l = l.parent; } else { break; } } - return d.query(b); + return l.query(a); }; - b.prototype.getDepth = function() { - for (var b = 0, d = this;d;) { - b++, d = d.parent; + a.prototype.getDepth = function() { + for (var a = 0, l = this;l;) { + a++, l = l.parent; } - return b; + return a; }; - b.prototype.calculateStatistics = function() { - function b(a) { - if (a.kind) { - var c = d[a.kind.id] || (d[a.kind.id] = new f(a.kind)); - c.count++; - c.selfTime += a.selfTime; - c.totalTime += a.totalTime; + a.prototype.calculateStatistics = function() { + function a(e) { + if (e.kind) { + var m = l[e.kind.id] || (l[e.kind.id] = new c(e.kind)); + m.count++; + m.selfTime += e.selfTime; + m.totalTime += e.totalTime; } - a.children && a.children.forEach(b); + e.children && e.children.forEach(a); } - var d = this.statistics = []; - b(this); + var l = this.statistics = []; + a(this); }; - return b; + a.prototype.trace = function(a) { + var l = (this.kind ? this.kind.name + ": " : "Profile: ") + (this.endTime - this.startTime).toFixed(2); + if (this.children && this.children.length) { + a.enter(l); + for (l = 0;l < this.children.length;l++) { + this.children[l].trace(a); + } + a.outdent(); + } else { + a.writeLn(l); + } + }; + return a; }(); - b.TimelineFrame = k; - k = function(b) { - function m(d) { - b.call(this, null, null, null, null, NaN, NaN); - this.name = d; + a.TimelineFrame = h; + h = function(a) { + function c(l) { + a.call(this, null, null, null, null, NaN, NaN); + this.name = l; } - __extends(m, b); - return m; - }(k); - b.TimelineBufferSnapshot = k; - })(b.Profiler || (b.Profiler = {})); - })(b.Tools || (b.Tools = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - var f = b.ObjectUtilities.createEmptyObject, k = function() { - function s(m, d) { - "undefined" === typeof m && (m = ""); - this.name = m || ""; - this._startTime = b.isNullOrUndefined(d) ? performance.now() : d; + __extends(c, a); + return c; + }(h); + a.TimelineBufferSnapshot = h; + })(c.Profiler || (c.Profiler = {})); + })(c.Tools || (c.Tools = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = function() { + function s(a, h) { + void 0 === a && (a = ""); + this.name = a || ""; + this._startTime = c.isNullOrUndefined(h) ? jsGlobal.START_TIME : h; } - s.prototype.getKind = function(b) { - return this._kinds[b]; + s.prototype.getKind = function(a) { + return this._kinds[a]; }; Object.defineProperty(s.prototype, "kinds", {get:function() { return this._kinds.concat(); @@ -4426,72 +4741,72 @@ this._stack = []; this._data = []; this._kinds = []; - this._kindNameMap = f(); - this._marks = new b.CircularBuffer(Int32Array, 20); - this._times = new b.CircularBuffer(Float64Array, 20); - }; - s.prototype._getKindId = function(b) { - var d = s.MAX_KINDID; - if (void 0 === this._kindNameMap[b]) { - if (d = this._kinds.length, d < s.MAX_KINDID) { - var a = {id:d, name:b, visible:!0}; - this._kinds.push(a); - this._kindNameMap[b] = a; + this._kindNameMap = Object.create(null); + this._marks = new c.CircularBuffer(Int32Array, 20); + this._times = new c.CircularBuffer(Float64Array, 20); + }; + s.prototype._getKindId = function(a) { + var c = s.MAX_KINDID; + if (void 0 === this._kindNameMap[a]) { + if (c = this._kinds.length, c < s.MAX_KINDID) { + var l = {id:c, name:a, visible:!0}; + this._kinds.push(l); + this._kindNameMap[a] = l; } else { - d = s.MAX_KINDID; + c = s.MAX_KINDID; } } else { - d = this._kindNameMap[b].id; + c = this._kindNameMap[a].id; } - return d; + return c; }; - s.prototype._getMark = function(m, d, a) { - var c = s.MAX_DATAID; - b.isNullOrUndefined(a) || d === s.MAX_KINDID || (c = this._data.length, c < s.MAX_DATAID ? this._data.push(a) : c = s.MAX_DATAID); - return m | c << 16 | d; + s.prototype._getMark = function(a, h, l) { + var e = s.MAX_DATAID; + c.isNullOrUndefined(l) || h === s.MAX_KINDID || (e = this._data.length, e < s.MAX_DATAID ? this._data.push(l) : e = s.MAX_DATAID); + return a | e << 16 | h; }; - s.prototype.enter = function(m, d, a) { - a = (b.isNullOrUndefined(a) ? performance.now() : a) - this._startTime; + s.prototype.enter = function(a, h, l) { + l = (c.isNullOrUndefined(l) ? performance.now() : l) - this._startTime; this._marks || this._initialize(); this._depth++; - m = this._getKindId(m); - this._marks.write(this._getMark(s.ENTER, m, d)); - this._times.write(a); - this._stack.push(m); - }; - s.prototype.leave = function(m, d, a) { - a = (b.isNullOrUndefined(a) ? performance.now() : a) - this._startTime; - var c = this._stack.pop(); - m && (c = this._getKindId(m)); - this._marks.write(this._getMark(s.LEAVE, c, d)); - this._times.write(a); + a = this._getKindId(a); + this._marks.write(this._getMark(s.ENTER, a, h)); + this._times.write(l); + this._stack.push(a); + }; + s.prototype.leave = function(a, h, l) { + l = (c.isNullOrUndefined(l) ? performance.now() : l) - this._startTime; + var e = this._stack.pop(); + a && (e = this._getKindId(a)); + this._marks.write(this._getMark(s.LEAVE, e, h)); + this._times.write(l); this._depth--; }; - s.prototype.count = function(b, d, a) { + s.prototype.count = function(a, c, l) { }; s.prototype.createSnapshot = function() { - var m; - "undefined" === typeof m && (m = Number.MAX_VALUE); + var p; + void 0 === p && (p = Number.MAX_VALUE); if (!this._marks) { return null; } - var d = this._times, a = this._kinds, c = this._data, n = new g.TimelineBufferSnapshot(this.name), p = [n], e = 0; + var h = this._times, l = this._kinds, e = this._data, m = new a.TimelineBufferSnapshot(this.name), t = [m], q = 0; this._marks || this._initialize(); - this._marks.forEachInReverse(function(q, l) { - var n = c[q >>> 16 & s.MAX_DATAID], w = a[q & s.MAX_KINDID]; - if (b.isNullOrUndefined(w) || w.visible) { - var r = q & 2147483648, h = d.get(l), x = p.length; - if (r === s.LEAVE) { - if (1 === x && (e++, e > m)) { + this._marks.forEachInReverse(function(n, k) { + var f = e[n >>> 16 & s.MAX_DATAID], d = l[n & s.MAX_KINDID]; + if (c.isNullOrUndefined(d) || d.visible) { + var b = n & 2147483648, g = h.get(k), r = t.length; + if (b === s.LEAVE) { + if (1 === r && (q++, q > p)) { return!0; } - p.push(new g.TimelineFrame(p[x - 1], w, null, n, NaN, h)); + t.push(new a.TimelineFrame(t[r - 1], d, null, f, NaN, g)); } else { - if (r === s.ENTER) { - if (w = p.pop(), r = p[p.length - 1]) { - for (r.children ? r.children.unshift(w) : r.children = [w], r = p.length, w.depth = r, w.startData = n, w.startTime = h;w;) { - if (w.maxDepth < r) { - w.maxDepth = r, w = w.parent; + if (b === s.ENTER) { + if (d = t.pop(), b = t[t.length - 1]) { + for (b.children ? b.children.unshift(d) : b.children = [d], b = t.length, d.depth = b, d.startData = f, d.startTime = g;d;) { + if (d.maxDepth < b) { + d.maxDepth = b, d = d.parent; } else { break; } @@ -4503,63 +4818,63 @@ } } }); - n.children && n.children.length && (n.startTime = n.children[0].startTime, n.endTime = n.children[n.children.length - 1].endTime); - return n; + m.children && m.children.length && (m.startTime = m.children[0].startTime, m.endTime = m.children[m.children.length - 1].endTime); + return m; }; - s.prototype.reset = function(m) { - this._startTime = b.isNullOrUndefined(m) ? performance.now() : m; + s.prototype.reset = function(a) { + this._startTime = c.isNullOrUndefined(a) ? performance.now() : a; this._marks ? (this._depth = 0, this._data = [], this._marks.reset(), this._times.reset()) : this._initialize(); }; - s.FromFirefoxProfile = function(b, d) { - for (var a = b.profile.threads[0].samples, c = new s(d, a[0].time), n = [], p, e = 0;e < a.length;e++) { - p = a[e]; - var q = p.time, l = p.frames, u = 0; - for (p = Math.min(l.length, n.length);u < p && l[u].location === n[u].location;) { - u++; + s.FromFirefoxProfile = function(a, c) { + for (var l = a.profile.threads[0].samples, e = new s(c, l[0].time), m = [], t, q = 0;q < l.length;q++) { + t = l[q]; + var n = t.time, k = t.frames, f = 0; + for (t = Math.min(k.length, m.length);f < t && k[f].location === m[f].location;) { + f++; + } + for (var d = m.length - f, b = 0;b < d;b++) { + t = m.pop(), e.leave(t.location, null, n); } - for (var w = n.length - u, r = 0;r < w;r++) { - p = n.pop(), c.leave(p.location, null, q); + for (;f < k.length;) { + t = k[f++], e.enter(t.location, null, n); } - for (;u < l.length;) { - p = l[u++], c.enter(p.location, null, q); - } - n = l; + m = k; } - for (;p = n.pop();) { - c.leave(p.location, null, q); + for (;t = m.pop();) { + e.leave(t.location, null, n); } - return c; + return e; }; - s.FromChromeProfile = function(b, d) { - var a = b.timestamps, c = b.samples, n = new s(d, a[0] / 1E3), p = [], e = {}, q; - s._resolveIds(b.head, e); - for (var l = 0;l < a.length;l++) { - var u = a[l] / 1E3, w = []; - for (q = e[c[l]];q;) { - w.unshift(q), q = q.parent; + s.FromChromeProfile = function(a, c) { + var l = a.timestamps, e = a.samples, m = new s(c, l[0] / 1E3), t = [], q = {}, n; + s._resolveIds(a.head, q); + for (var k = 0;k < l.length;k++) { + var f = l[k] / 1E3, d = []; + for (n = q[e[k]];n;) { + d.unshift(n), n = n.parent; } - var r = 0; - for (q = Math.min(w.length, p.length);r < q && w[r] === p[r];) { - r++; + var b = 0; + for (n = Math.min(d.length, t.length);b < n && d[b] === t[b];) { + b++; } - for (var h = p.length - r, x = 0;x < h;x++) { - q = p.pop(), n.leave(q.functionName, null, u); + for (var g = t.length - b, r = 0;r < g;r++) { + n = t.pop(), m.leave(n.functionName, null, f); } - for (;r < w.length;) { - q = w[r++], n.enter(q.functionName, null, u); + for (;b < d.length;) { + n = d[b++], m.enter(n.functionName, null, f); } - p = w; + t = d; } - for (;q = p.pop();) { - n.leave(q.functionName, null, u); + for (;n = t.pop();) { + m.leave(n.functionName, null, f); } - return n; + return m; }; - s._resolveIds = function(b, d) { - d[b.id] = b; - if (b.children) { - for (var a = 0;a < b.children.length;a++) { - b.children[a].parent = b, s._resolveIds(b.children[a], d); + s._resolveIds = function(a, c) { + c[a.id] = a; + if (a.children) { + for (var l = 0;l < a.children.length;l++) { + a.children[l].parent = a, s._resolveIds(a.children[l], c); } } }; @@ -4569,93 +4884,93 @@ s.MAX_DATAID = 32767; return s; }(); - g.TimelineBuffer = k; - })(k.Profiler || (k.Profiler = {})); - })(b.Tools || (b.Tools = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(b) { - b[b.DARK = 0] = "DARK"; - b[b.LIGHT = 1] = "LIGHT"; - })(g.UIThemeType || (g.UIThemeType = {})); - var f = function() { - function f(b, m) { - "undefined" === typeof m && (m = 0); - this._container = b; + a.TimelineBuffer = s; + })(h.Profiler || (h.Profiler = {})); + })(c.Tools || (c.Tools = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + a[a.DARK = 0] = "DARK"; + a[a.LIGHT = 1] = "LIGHT"; + })(a.UIThemeType || (a.UIThemeType = {})); + var s = function() { + function s(a, c) { + void 0 === c && (c = 0); + this._container = a; this._headers = []; this._charts = []; this._profiles = []; this._activeProfile = null; - this.themeType = m; + this.themeType = c; this._tooltip = this._createTooltip(); } - f.prototype.createProfile = function(b, m) { - "undefined" === typeof m && (m = !0); - var d = new g.Profile(b); - d.createSnapshots(); - this._profiles.push(d); - m && this.activateProfile(d); - return d; + s.prototype.createProfile = function(c, s) { + void 0 === s && (s = !0); + var l = new a.Profile(c); + l.createSnapshots(); + this._profiles.push(l); + s && this.activateProfile(l); + return l; }; - f.prototype.activateProfile = function(b) { + s.prototype.activateProfile = function(a) { this.deactivateProfile(); - this._activeProfile = b; + this._activeProfile = a; this._createViews(); this._initializeViews(); }; - f.prototype.activateProfileAt = function(b) { - this.activateProfile(this.getProfileAt(b)); + s.prototype.activateProfileAt = function(a) { + this.activateProfile(this.getProfileAt(a)); }; - f.prototype.deactivateProfile = function() { + s.prototype.deactivateProfile = function() { this._activeProfile && (this._destroyViews(), this._activeProfile = null); }; - f.prototype.resize = function() { + s.prototype.resize = function() { this._onResize(); }; - f.prototype.getProfileAt = function(b) { - return this._profiles[b]; + s.prototype.getProfileAt = function(a) { + return this._profiles[a]; }; - Object.defineProperty(f.prototype, "activeProfile", {get:function() { + Object.defineProperty(s.prototype, "activeProfile", {get:function() { return this._activeProfile; }, enumerable:!0, configurable:!0}); - Object.defineProperty(f.prototype, "profileCount", {get:function() { + Object.defineProperty(s.prototype, "profileCount", {get:function() { return this._profiles.length; }, enumerable:!0, configurable:!0}); - Object.defineProperty(f.prototype, "container", {get:function() { + Object.defineProperty(s.prototype, "container", {get:function() { return this._container; }, enumerable:!0, configurable:!0}); - Object.defineProperty(f.prototype, "themeType", {get:function() { + Object.defineProperty(s.prototype, "themeType", {get:function() { return this._themeType; - }, set:function(b) { - switch(b) { + }, set:function(a) { + switch(a) { case 0: - this._theme = new k.Theme.UIThemeDark; + this._theme = new h.Theme.UIThemeDark; break; case 1: - this._theme = new k.Theme.UIThemeLight; + this._theme = new h.Theme.UIThemeLight; } }, enumerable:!0, configurable:!0}); - Object.defineProperty(f.prototype, "theme", {get:function() { + Object.defineProperty(s.prototype, "theme", {get:function() { return this._theme; }, enumerable:!0, configurable:!0}); - f.prototype.getSnapshotAt = function(b) { - return this._activeProfile.getSnapshotAt(b); + s.prototype.getSnapshotAt = function(a) { + return this._activeProfile.getSnapshotAt(a); }; - f.prototype._createViews = function() { + s.prototype._createViews = function() { if (this._activeProfile) { - var b = this; - this._overviewHeader = new g.FlameChartHeader(this, 0); - this._overview = new g.FlameChartOverview(this, 0); - this._activeProfile.forEachSnapshot(function(m, d) { - b._headers.push(new g.FlameChartHeader(b, 1)); - b._charts.push(new g.FlameChart(b, m)); + var c = this; + this._overviewHeader = new a.FlameChartHeader(this, 0); + this._overview = new a.FlameChartOverview(this, 0); + this._activeProfile.forEachSnapshot(function(s, l) { + c._headers.push(new a.FlameChartHeader(c, 1)); + c._charts.push(new a.FlameChart(c, s)); }); window.addEventListener("resize", this._onResize.bind(this)); } }; - f.prototype._destroyViews = function() { + s.prototype._destroyViews = function() { if (this._activeProfile) { this._overviewHeader.destroy(); for (this._overview.destroy();this._headers.length;) { @@ -4667,163 +4982,163 @@ window.removeEventListener("resize", this._onResize.bind(this)); } }; - f.prototype._initializeViews = function() { + s.prototype._initializeViews = function() { if (this._activeProfile) { - var b = this, m = this._activeProfile.startTime, d = this._activeProfile.endTime; - this._overviewHeader.initialize(m, d); - this._overview.initialize(m, d); - this._activeProfile.forEachSnapshot(function(a, c) { - b._headers[c].initialize(m, d); - b._charts[c].initialize(m, d); + var a = this, c = this._activeProfile.startTime, l = this._activeProfile.endTime; + this._overviewHeader.initialize(c, l); + this._overview.initialize(c, l); + this._activeProfile.forEachSnapshot(function(e, m) { + a._headers[m].initialize(c, l); + a._charts[m].initialize(c, l); }); } }; - f.prototype._onResize = function() { + s.prototype._onResize = function() { if (this._activeProfile) { - var b = this, m = this._container.offsetWidth; - this._overviewHeader.setSize(m); - this._overview.setSize(m); - this._activeProfile.forEachSnapshot(function(d, a) { - b._headers[a].setSize(m); - b._charts[a].setSize(m); + var a = this, c = this._container.offsetWidth; + this._overviewHeader.setSize(c); + this._overview.setSize(c); + this._activeProfile.forEachSnapshot(function(l, e) { + a._headers[e].setSize(c); + a._charts[e].setSize(c); }); } }; - f.prototype._updateViews = function() { + s.prototype._updateViews = function() { if (this._activeProfile) { - var b = this, m = this._activeProfile.windowStart, d = this._activeProfile.windowEnd; - this._overviewHeader.setWindow(m, d); - this._overview.setWindow(m, d); - this._activeProfile.forEachSnapshot(function(a, c) { - b._headers[c].setWindow(m, d); - b._charts[c].setWindow(m, d); + var a = this, c = this._activeProfile.windowStart, l = this._activeProfile.windowEnd; + this._overviewHeader.setWindow(c, l); + this._overview.setWindow(c, l); + this._activeProfile.forEachSnapshot(function(e, m) { + a._headers[m].setWindow(c, l); + a._charts[m].setWindow(c, l); }); } }; - f.prototype._drawViews = function() { + s.prototype._drawViews = function() { }; - f.prototype._createTooltip = function() { - var b = document.createElement("div"); - b.classList.add("profiler-tooltip"); - b.style.display = "none"; - this._container.insertBefore(b, this._container.firstChild); - return b; + s.prototype._createTooltip = function() { + var a = document.createElement("div"); + a.classList.add("profiler-tooltip"); + a.style.display = "none"; + this._container.insertBefore(a, this._container.firstChild); + return a; }; - f.prototype.setWindow = function(b, m) { - this._activeProfile.setWindow(b, m); + s.prototype.setWindow = function(a, c) { + this._activeProfile.setWindow(a, c); this._updateViews(); }; - f.prototype.moveWindowTo = function(b) { - this._activeProfile.moveWindowTo(b); + s.prototype.moveWindowTo = function(a) { + this._activeProfile.moveWindowTo(a); this._updateViews(); }; - f.prototype.showTooltip = function(b, m, d, a) { + s.prototype.showTooltip = function(a, c, l, e) { this.removeTooltipContent(); - this._tooltip.appendChild(this.createTooltipContent(b, m)); + this._tooltip.appendChild(this.createTooltipContent(a, c)); this._tooltip.style.display = "block"; - var c = this._tooltip.firstChild; - m = c.clientWidth; - c = c.clientHeight; - d += d + m >= b.canvas.clientWidth - 50 ? -(m + 20) : 25; - a += b.canvas.offsetTop - c / 2; - this._tooltip.style.left = d + "px"; - this._tooltip.style.top = a + "px"; + var m = this._tooltip.firstChild; + c = m.clientWidth; + m = m.clientHeight; + l += l + c >= a.canvas.clientWidth - 50 ? -(c + 20) : 25; + e += a.canvas.offsetTop - m / 2; + this._tooltip.style.left = l + "px"; + this._tooltip.style.top = e + "px"; }; - f.prototype.hideTooltip = function() { + s.prototype.hideTooltip = function() { this._tooltip.style.display = "none"; }; - f.prototype.createTooltipContent = function(b, m) { - var d = Math.round(1E5 * m.totalTime) / 1E5, a = Math.round(1E5 * m.selfTime) / 1E5, c = Math.round(1E4 * m.selfTime / m.totalTime) / 100, n = document.createElement("div"), p = document.createElement("h1"); - p.textContent = m.kind.name; - n.appendChild(p); - p = document.createElement("p"); - p.textContent = "Total: " + d + " ms"; - n.appendChild(p); - d = document.createElement("p"); - d.textContent = "Self: " + a + " ms (" + c + "%)"; - n.appendChild(d); - if (a = b.getStatistics(m.kind)) { - c = document.createElement("p"), c.textContent = "Count: " + a.count, n.appendChild(c), c = Math.round(1E5 * a.totalTime) / 1E5, d = document.createElement("p"), d.textContent = "All Total: " + c + " ms", n.appendChild(d), a = Math.round(1E5 * a.selfTime) / 1E5, c = document.createElement("p"), c.textContent = "All Self: " + a + " ms", n.appendChild(c); + s.prototype.createTooltipContent = function(a, c) { + var l = Math.round(1E5 * c.totalTime) / 1E5, e = Math.round(1E5 * c.selfTime) / 1E5, m = Math.round(1E4 * c.selfTime / c.totalTime) / 100, t = document.createElement("div"), q = document.createElement("h1"); + q.textContent = c.kind.name; + t.appendChild(q); + q = document.createElement("p"); + q.textContent = "Total: " + l + " ms"; + t.appendChild(q); + l = document.createElement("p"); + l.textContent = "Self: " + e + " ms (" + m + "%)"; + t.appendChild(l); + if (e = a.getStatistics(c.kind)) { + m = document.createElement("p"), m.textContent = "Count: " + e.count, t.appendChild(m), m = Math.round(1E5 * e.totalTime) / 1E5, l = document.createElement("p"), l.textContent = "All Total: " + m + " ms", t.appendChild(l), e = Math.round(1E5 * e.selfTime) / 1E5, m = document.createElement("p"), m.textContent = "All Self: " + e + " ms", t.appendChild(m); } - this.appendDataElements(n, m.startData); - this.appendDataElements(n, m.endData); - return n; + this.appendDataElements(t, c.startData); + this.appendDataElements(t, c.endData); + return t; }; - f.prototype.appendDataElements = function(f, m) { - if (!b.isNullOrUndefined(m)) { - f.appendChild(document.createElement("hr")); - var d; - if (b.isObject(m)) { - for (var a in m) { - d = document.createElement("p"), d.textContent = a + ": " + m[a], f.appendChild(d); + s.prototype.appendDataElements = function(a, s) { + if (!c.isNullOrUndefined(s)) { + a.appendChild(document.createElement("hr")); + var l; + if (c.isObject(s)) { + for (var e in s) { + l = document.createElement("p"), l.textContent = e + ": " + s[e], a.appendChild(l); } } else { - d = document.createElement("p"), d.textContent = m.toString(), f.appendChild(d); + l = document.createElement("p"), l.textContent = s.toString(), a.appendChild(l); } } }; - f.prototype.removeTooltipContent = function() { - for (var b = this._tooltip;b.firstChild;) { - b.removeChild(b.firstChild); + s.prototype.removeTooltipContent = function() { + for (var a = this._tooltip;a.firstChild;) { + a.removeChild(a.firstChild); } }; - return f; + return s; }(); - g.Controller = f; - })(k.Profiler || (k.Profiler = {})); - })(b.Tools || (b.Tools = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - var f = b.NumberUtilities.clamp, k = function() { - function b(d) { - this.value = d; + a.Controller = s; + })(h.Profiler || (h.Profiler = {})); + })(c.Tools || (c.Tools = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.NumberUtilities.clamp, h = function() { + function a(l) { + this.value = l; } - b.prototype.toString = function() { + a.prototype.toString = function() { return this.value; }; - b.AUTO = new b("auto"); - b.DEFAULT = new b("default"); - b.NONE = new b("none"); - b.HELP = new b("help"); - b.POINTER = new b("pointer"); - b.PROGRESS = new b("progress"); - b.WAIT = new b("wait"); - b.CELL = new b("cell"); - b.CROSSHAIR = new b("crosshair"); - b.TEXT = new b("text"); - b.ALIAS = new b("alias"); - b.COPY = new b("copy"); - b.MOVE = new b("move"); - b.NO_DROP = new b("no-drop"); - b.NOT_ALLOWED = new b("not-allowed"); - b.ALL_SCROLL = new b("all-scroll"); - b.COL_RESIZE = new b("col-resize"); - b.ROW_RESIZE = new b("row-resize"); - b.N_RESIZE = new b("n-resize"); - b.E_RESIZE = new b("e-resize"); - b.S_RESIZE = new b("s-resize"); - b.W_RESIZE = new b("w-resize"); - b.NE_RESIZE = new b("ne-resize"); - b.NW_RESIZE = new b("nw-resize"); - b.SE_RESIZE = new b("se-resize"); - b.SW_RESIZE = new b("sw-resize"); - b.EW_RESIZE = new b("ew-resize"); - b.NS_RESIZE = new b("ns-resize"); - b.NESW_RESIZE = new b("nesw-resize"); - b.NWSE_RESIZE = new b("nwse-resize"); - b.ZOOM_IN = new b("zoom-in"); - b.ZOOM_OUT = new b("zoom-out"); - b.GRAB = new b("grab"); - b.GRABBING = new b("grabbing"); - return b; + a.AUTO = new a("auto"); + a.DEFAULT = new a("default"); + a.NONE = new a("none"); + a.HELP = new a("help"); + a.POINTER = new a("pointer"); + a.PROGRESS = new a("progress"); + a.WAIT = new a("wait"); + a.CELL = new a("cell"); + a.CROSSHAIR = new a("crosshair"); + a.TEXT = new a("text"); + a.ALIAS = new a("alias"); + a.COPY = new a("copy"); + a.MOVE = new a("move"); + a.NO_DROP = new a("no-drop"); + a.NOT_ALLOWED = new a("not-allowed"); + a.ALL_SCROLL = new a("all-scroll"); + a.COL_RESIZE = new a("col-resize"); + a.ROW_RESIZE = new a("row-resize"); + a.N_RESIZE = new a("n-resize"); + a.E_RESIZE = new a("e-resize"); + a.S_RESIZE = new a("s-resize"); + a.W_RESIZE = new a("w-resize"); + a.NE_RESIZE = new a("ne-resize"); + a.NW_RESIZE = new a("nw-resize"); + a.SE_RESIZE = new a("se-resize"); + a.SW_RESIZE = new a("sw-resize"); + a.EW_RESIZE = new a("ew-resize"); + a.NS_RESIZE = new a("ns-resize"); + a.NESW_RESIZE = new a("nesw-resize"); + a.NWSE_RESIZE = new a("nwse-resize"); + a.ZOOM_IN = new a("zoom-in"); + a.ZOOM_OUT = new a("zoom-out"); + a.GRAB = new a("grab"); + a.GRABBING = new a("grabbing"); + return a; }(); - g.MouseCursor = k; - var s = function() { - function b(d, a) { - this._target = d; - this._eventTarget = a; + a.MouseCursor = h; + var p = function() { + function a(l, e) { + this._target = l; + this._eventTarget = e; this._wheelDisabled = !1; this._boundOnMouseDown = this._onMouseDown.bind(this); this._boundOnMouseUp = this._onMouseUp.bind(this); @@ -4832,100 +5147,100 @@ this._boundOnMouseMove = this._onMouseMove.bind(this); this._boundOnMouseWheel = this._onMouseWheel.bind(this); this._boundOnDrag = this._onDrag.bind(this); - a.addEventListener("mousedown", this._boundOnMouseDown, !1); - a.addEventListener("mouseover", this._boundOnMouseOver, !1); - a.addEventListener("mouseout", this._boundOnMouseOut, !1); - a.addEventListener("onwheel" in document ? "wheel" : "mousewheel", this._boundOnMouseWheel, !1); - } - b.prototype.destroy = function() { - var b = this._eventTarget; - b.removeEventListener("mousedown", this._boundOnMouseDown); - b.removeEventListener("mouseover", this._boundOnMouseOver); - b.removeEventListener("mouseout", this._boundOnMouseOut); - b.removeEventListener("onwheel" in document ? "wheel" : "mousewheel", this._boundOnMouseWheel); + e.addEventListener("mousedown", this._boundOnMouseDown, !1); + e.addEventListener("mouseover", this._boundOnMouseOver, !1); + e.addEventListener("mouseout", this._boundOnMouseOut, !1); + e.addEventListener("onwheel" in document ? "wheel" : "mousewheel", this._boundOnMouseWheel, !1); + } + a.prototype.destroy = function() { + var a = this._eventTarget; + a.removeEventListener("mousedown", this._boundOnMouseDown); + a.removeEventListener("mouseover", this._boundOnMouseOver); + a.removeEventListener("mouseout", this._boundOnMouseOut); + a.removeEventListener("onwheel" in document ? "wheel" : "mousewheel", this._boundOnMouseWheel); window.removeEventListener("mousemove", this._boundOnDrag); window.removeEventListener("mouseup", this._boundOnMouseUp); this._killHoverCheck(); this._target = this._eventTarget = null; }; - b.prototype.updateCursor = function(d) { - if (!b._cursorOwner || b._cursorOwner === this._target) { - var a = this._eventTarget.parentElement; - b._cursor !== d && (b._cursor = d, ["", "-moz-", "-webkit-"].forEach(function(c) { - a.style.cursor = c + d; + a.prototype.updateCursor = function(l) { + if (!a._cursorOwner || a._cursorOwner === this._target) { + var e = this._eventTarget.parentElement; + a._cursor !== l && (a._cursor = l, ["", "-moz-", "-webkit-"].forEach(function(a) { + e.style.cursor = a + l; })); - b._cursorOwner = b._cursor === k.DEFAULT ? null : this._target; + a._cursorOwner = a._cursor === h.DEFAULT ? null : this._target; } }; - b.prototype._onMouseDown = function(b) { + a.prototype._onMouseDown = function(a) { this._killHoverCheck(); - if (0 === b.button) { - var a = this._getTargetMousePos(b, b.target); - this._dragInfo = {start:a, current:a, delta:{x:0, y:0}, hasMoved:!1, originalTarget:b.target}; + if (0 === a.button) { + var e = this._getTargetMousePos(a, a.target); + this._dragInfo = {start:e, current:e, delta:{x:0, y:0}, hasMoved:!1, originalTarget:a.target}; window.addEventListener("mousemove", this._boundOnDrag, !1); window.addEventListener("mouseup", this._boundOnMouseUp, !1); - this._target.onMouseDown(a.x, a.y); + this._target.onMouseDown(e.x, e.y); } }; - b.prototype._onDrag = function(b) { - var a = this._dragInfo; - b = this._getTargetMousePos(b, a.originalTarget); - var c = {x:b.x - a.start.x, y:b.y - a.start.y}; - a.current = b; - a.delta = c; - a.hasMoved = !0; - this._target.onDrag(a.start.x, a.start.y, b.x, b.y, c.x, c.y); + a.prototype._onDrag = function(a) { + var e = this._dragInfo; + a = this._getTargetMousePos(a, e.originalTarget); + var m = {x:a.x - e.start.x, y:a.y - e.start.y}; + e.current = a; + e.delta = m; + e.hasMoved = !0; + this._target.onDrag(e.start.x, e.start.y, a.x, a.y, m.x, m.y); }; - b.prototype._onMouseUp = function(b) { + a.prototype._onMouseUp = function(a) { window.removeEventListener("mousemove", this._boundOnDrag); window.removeEventListener("mouseup", this._boundOnMouseUp); - var a = this; - b = this._dragInfo; - if (b.hasMoved) { - this._target.onDragEnd(b.start.x, b.start.y, b.current.x, b.current.y, b.delta.x, b.delta.y); + var e = this; + a = this._dragInfo; + if (a.hasMoved) { + this._target.onDragEnd(a.start.x, a.start.y, a.current.x, a.current.y, a.delta.x, a.delta.y); } else { - this._target.onClick(b.current.x, b.current.y); + this._target.onClick(a.current.x, a.current.y); } this._dragInfo = null; this._wheelDisabled = !0; setTimeout(function() { - a._wheelDisabled = !1; + e._wheelDisabled = !1; }, 500); }; - b.prototype._onMouseOver = function(b) { - b.target.addEventListener("mousemove", this._boundOnMouseMove, !1); + a.prototype._onMouseOver = function(a) { + a.target.addEventListener("mousemove", this._boundOnMouseMove, !1); if (!this._dragInfo) { - var a = this._getTargetMousePos(b, b.target); - this._target.onMouseOver(a.x, a.y); - this._startHoverCheck(b); + var e = this._getTargetMousePos(a, a.target); + this._target.onMouseOver(e.x, e.y); + this._startHoverCheck(a); } }; - b.prototype._onMouseOut = function(b) { - b.target.removeEventListener("mousemove", this._boundOnMouseMove, !1); + a.prototype._onMouseOut = function(a) { + a.target.removeEventListener("mousemove", this._boundOnMouseMove, !1); if (!this._dragInfo) { this._target.onMouseOut(); } this._killHoverCheck(); }; - b.prototype._onMouseMove = function(b) { + a.prototype._onMouseMove = function(a) { if (!this._dragInfo) { - var a = this._getTargetMousePos(b, b.target); - this._target.onMouseMove(a.x, a.y); + var e = this._getTargetMousePos(a, a.target); + this._target.onMouseMove(e.x, e.y); this._killHoverCheck(); - this._startHoverCheck(b); + this._startHoverCheck(a); } }; - b.prototype._onMouseWheel = function(b) { - if (!(b.altKey || b.metaKey || b.ctrlKey || b.shiftKey || (b.preventDefault(), this._dragInfo || this._wheelDisabled))) { - var a = this._getTargetMousePos(b, b.target); - b = f("undefined" !== typeof b.deltaY ? b.deltaY / 16 : -b.wheelDelta / 40, -1, 1); - this._target.onMouseWheel(a.x, a.y, Math.pow(1.2, b) - 1); + a.prototype._onMouseWheel = function(a) { + if (!(a.altKey || a.metaKey || a.ctrlKey || a.shiftKey || (a.preventDefault(), this._dragInfo || this._wheelDisabled))) { + var e = this._getTargetMousePos(a, a.target); + a = s("undefined" !== typeof a.deltaY ? a.deltaY / 16 : -a.wheelDelta / 40, -1, 1); + this._target.onMouseWheel(e.x, e.y, Math.pow(1.2, a) - 1); } }; - b.prototype._startHoverCheck = function(d) { - this._hoverInfo = {isHovering:!1, timeoutHandle:setTimeout(this._onMouseMoveIdleHandler.bind(this), b.HOVER_TIMEOUT), pos:this._getTargetMousePos(d, d.target)}; + a.prototype._startHoverCheck = function(l) { + this._hoverInfo = {isHovering:!1, timeoutHandle:setTimeout(this._onMouseMoveIdleHandler.bind(this), a.HOVER_TIMEOUT), pos:this._getTargetMousePos(l, l.target)}; }; - b.prototype._killHoverCheck = function() { + a.prototype._killHoverCheck = function() { if (this._hoverInfo) { clearTimeout(this._hoverInfo.timeoutHandle); if (this._hoverInfo.isHovering) { @@ -4934,559 +5249,559 @@ this._hoverInfo = null; } }; - b.prototype._onMouseMoveIdleHandler = function() { - var b = this._hoverInfo; - b.isHovering = !0; - this._target.onHoverStart(b.pos.x, b.pos.y); - }; - b.prototype._getTargetMousePos = function(b, a) { - var c = a.getBoundingClientRect(); - return{x:b.clientX - c.left, y:b.clientY - c.top}; + a.prototype._onMouseMoveIdleHandler = function() { + var a = this._hoverInfo; + a.isHovering = !0; + this._target.onHoverStart(a.pos.x, a.pos.y); + }; + a.prototype._getTargetMousePos = function(a, e) { + var m = e.getBoundingClientRect(); + return{x:a.clientX - m.left, y:a.clientY - m.top}; }; - b.HOVER_TIMEOUT = 500; - b._cursor = k.DEFAULT; - return b; + a.HOVER_TIMEOUT = 500; + a._cursor = h.DEFAULT; + return a; }(); - g.MouseController = s; - })(k.Profiler || (k.Profiler = {})); - })(b.Tools || (b.Tools = {})); + a.MouseController = p; + })(h.Profiler || (h.Profiler = {})); + })(c.Tools || (c.Tools = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(b) { - b[b.NONE = 0] = "NONE"; - b[b.WINDOW = 1] = "WINDOW"; - b[b.HANDLE_LEFT = 2] = "HANDLE_LEFT"; - b[b.HANDLE_RIGHT = 3] = "HANDLE_RIGHT"; - b[b.HANDLE_BOTH = 4] = "HANDLE_BOTH"; - })(b.FlameChartDragTarget || (b.FlameChartDragTarget = {})); - var f = function() { - function f(s) { +(function(c) { + (function(c) { + (function(a) { + (function(a) { + a[a.NONE = 0] = "NONE"; + a[a.WINDOW = 1] = "WINDOW"; + a[a.HANDLE_LEFT = 2] = "HANDLE_LEFT"; + a[a.HANDLE_RIGHT = 3] = "HANDLE_RIGHT"; + a[a.HANDLE_BOTH = 4] = "HANDLE_BOTH"; + })(a.FlameChartDragTarget || (a.FlameChartDragTarget = {})); + var c = function() { + function c(s) { this._controller = s; this._initialized = !1; this._canvas = document.createElement("canvas"); this._context = this._canvas.getContext("2d"); - this._mouseController = new b.MouseController(this, this._canvas); + this._mouseController = new a.MouseController(this, this._canvas); s = s.container; s.appendChild(this._canvas); s = s.getBoundingClientRect(); this.setSize(s.width); } - Object.defineProperty(f.prototype, "canvas", {get:function() { + Object.defineProperty(c.prototype, "canvas", {get:function() { return this._canvas; }, enumerable:!0, configurable:!0}); - f.prototype.setSize = function(b, f) { - "undefined" === typeof f && (f = 20); - this._width = b; - this._height = f; + c.prototype.setSize = function(a, c) { + void 0 === c && (c = 20); + this._width = a; + this._height = c; this._resetCanvas(); this.draw(); }; - f.prototype.initialize = function(b, f) { + c.prototype.initialize = function(a, c) { this._initialized = !0; - this.setRange(b, f); - this.setWindow(b, f, !1); + this.setRange(a, c); + this.setWindow(a, c, !1); this.draw(); }; - f.prototype.setWindow = function(b, f, d) { - "undefined" === typeof d && (d = !0); - this._windowStart = b; - this._windowEnd = f; - !d || this.draw(); - }; - f.prototype.setRange = function(b, f) { - var d = !1; - "undefined" === typeof d && (d = !0); - this._rangeStart = b; - this._rangeEnd = f; - !d || this.draw(); + c.prototype.setWindow = function(a, c, l) { + void 0 === l && (l = !0); + this._windowStart = a; + this._windowEnd = c; + !l || this.draw(); + }; + c.prototype.setRange = function(a, c) { + var l = !1; + void 0 === l && (l = !0); + this._rangeStart = a; + this._rangeEnd = c; + !l || this.draw(); }; - f.prototype.destroy = function() { + c.prototype.destroy = function() { this._mouseController.destroy(); this._mouseController = null; this._controller.container.removeChild(this._canvas); this._controller = null; }; - f.prototype._resetCanvas = function() { - var b = window.devicePixelRatio, f = this._canvas; - f.width = this._width * b; - f.height = this._height * b; - f.style.width = this._width + "px"; - f.style.height = this._height + "px"; + c.prototype._resetCanvas = function() { + var a = window.devicePixelRatio, c = this._canvas; + c.width = this._width * a; + c.height = this._height * a; + c.style.width = this._width + "px"; + c.style.height = this._height + "px"; }; - f.prototype.draw = function() { + c.prototype.draw = function() { }; - f.prototype._almostEq = function(b, f) { - var d; - "undefined" === typeof d && (d = 10); - return Math.abs(b - f) < 1 / Math.pow(10, d); + c.prototype._almostEq = function(a, c) { + var l; + void 0 === l && (l = 10); + return Math.abs(a - c) < 1 / Math.pow(10, l); }; - f.prototype._windowEqRange = function() { + c.prototype._windowEqRange = function() { return this._almostEq(this._windowStart, this._rangeStart) && this._almostEq(this._windowEnd, this._rangeEnd); }; - f.prototype._decimalPlaces = function(b) { - return(+b).toFixed(10).replace(/^-?\d*\.?|0+$/g, "").length; + c.prototype._decimalPlaces = function(a) { + return(+a).toFixed(10).replace(/^-?\d*\.?|0+$/g, "").length; }; - f.prototype._toPixelsRelative = function(b) { + c.prototype._toPixelsRelative = function(a) { return 0; }; - f.prototype._toPixels = function(b) { + c.prototype._toPixels = function(a) { return 0; }; - f.prototype._toTimeRelative = function(b) { + c.prototype._toTimeRelative = function(a) { return 0; }; - f.prototype._toTime = function(b) { + c.prototype._toTime = function(a) { return 0; }; - f.prototype.onMouseWheel = function(b, m, d) { - b = this._toTime(b); - m = this._windowStart; - var a = this._windowEnd, c = a - m; - d = Math.max((f.MIN_WINDOW_LEN - c) / c, d); - this._controller.setWindow(m + (m - b) * d, a + (a - b) * d); + c.prototype.onMouseWheel = function(a, s, l) { + a = this._toTime(a); + s = this._windowStart; + var e = this._windowEnd, m = e - s; + l = Math.max((c.MIN_WINDOW_LEN - m) / m, l); + this._controller.setWindow(s + (s - a) * l, e + (e - a) * l); this.onHoverEnd(); }; - f.prototype.onMouseDown = function(b, f) { + c.prototype.onMouseDown = function(a, c) { }; - f.prototype.onMouseMove = function(b, f) { + c.prototype.onMouseMove = function(a, c) { }; - f.prototype.onMouseOver = function(b, f) { + c.prototype.onMouseOver = function(a, c) { }; - f.prototype.onMouseOut = function() { + c.prototype.onMouseOut = function() { }; - f.prototype.onDrag = function(b, f, d, a, c, n) { + c.prototype.onDrag = function(a, c, l, e, m, t) { }; - f.prototype.onDragEnd = function(b, f, d, a, c, n) { + c.prototype.onDragEnd = function(a, c, l, e, m, t) { }; - f.prototype.onClick = function(b, f) { + c.prototype.onClick = function(a, c) { }; - f.prototype.onHoverStart = function(b, f) { + c.prototype.onHoverStart = function(a, c) { }; - f.prototype.onHoverEnd = function() { + c.prototype.onHoverEnd = function() { }; - f.DRAGHANDLE_WIDTH = 4; - f.MIN_WINDOW_LEN = .1; - return f; + c.DRAGHANDLE_WIDTH = 4; + c.MIN_WINDOW_LEN = .1; + return c; }(); - b.FlameChartBase = f; - })(b.Profiler || (b.Profiler = {})); - })(b.Tools || (b.Tools = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - var f = b.StringUtilities.trimMiddle, k = b.ObjectUtilities.createEmptyObject, s = function(m) { - function d(a, b) { - m.call(this, a); + a.FlameChartBase = c; + })(c.Profiler || (c.Profiler = {})); + })(c.Tools || (c.Tools = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.StringUtilities.trimMiddle, h = function(h) { + function v(a, e) { + h.call(this, a); this._textWidth = {}; this._minFrameWidthInPixels = 1; - this._snapshot = b; - this._kindStyle = k(); + this._snapshot = e; + this._kindStyle = Object.create(null); } - __extends(d, m); - d.prototype.setSize = function(a, b) { - m.prototype.setSize.call(this, a, b || this._initialized ? 12.5 * this._maxDepth : 100); + __extends(v, h); + v.prototype.setSize = function(a, e) { + h.prototype.setSize.call(this, a, e || this._initialized ? 12.5 * this._maxDepth : 100); }; - d.prototype.initialize = function(a, b) { + v.prototype.initialize = function(a, e) { this._initialized = !0; this._maxDepth = this._snapshot.maxDepth; - this.setRange(a, b); - this.setWindow(a, b, !1); + this.setRange(a, e); + this.setWindow(a, e, !1); this.setSize(this._width, 12.5 * this._maxDepth); }; - d.prototype.destroy = function() { - m.prototype.destroy.call(this); + v.prototype.destroy = function() { + h.prototype.destroy.call(this); this._snapshot = null; }; - d.prototype.draw = function() { - var a = this._context, c = window.devicePixelRatio; - b.ColorStyle.reset(); + v.prototype.draw = function() { + var a = this._context, e = window.devicePixelRatio; + c.ColorStyle.reset(); a.save(); - a.scale(c, c); + a.scale(e, e); a.fillStyle = this._controller.theme.bodyBackground(1); a.fillRect(0, 0, this._width, this._height); this._initialized && this._drawChildren(this._snapshot); a.restore(); }; - d.prototype._drawChildren = function(a, b) { - "undefined" === typeof b && (b = 0); - var d = a.getChildRange(this._windowStart, this._windowEnd); - if (d) { - for (var p = d.startIndex;p <= d.endIndex;p++) { - var e = a.children[p]; - this._drawFrame(e, b) && this._drawChildren(e, b + 1); + v.prototype._drawChildren = function(a, e) { + void 0 === e && (e = 0); + var m = a.getChildRange(this._windowStart, this._windowEnd); + if (m) { + for (var c = m.startIndex;c <= m.endIndex;c++) { + var q = a.children[c]; + this._drawFrame(q, e) && this._drawChildren(q, e + 1); } } }; - d.prototype._drawFrame = function(a, c) { - var d = this._context, p = this._toPixels(a.startTime), e = this._toPixels(a.endTime), q = e - p; - if (q <= this._minFrameWidthInPixels) { - return d.fillStyle = this._controller.theme.tabToolbar(1), d.fillRect(p, 12.5 * c, this._minFrameWidthInPixels, 12 + 12.5 * (a.maxDepth - a.depth)), !1; - } - 0 > p && (e = q + p, p = 0); - var e = e - p, l = this._kindStyle[a.kind.id]; - l || (l = b.ColorStyle.randomStyle(), l = this._kindStyle[a.kind.id] = {bgColor:l, textColor:b.ColorStyle.contrastStyle(l)}); - d.save(); - d.fillStyle = l.bgColor; - d.fillRect(p, 12.5 * c, e, 12); - 12 < q && (q = a.kind.name) && q.length && (q = this._prepareText(d, q, e - 4), q.length && (d.fillStyle = l.textColor, d.textBaseline = "bottom", d.fillText(q, p + 2, 12.5 * (c + 1) - 1))); - d.restore(); + v.prototype._drawFrame = function(a, e) { + var m = this._context, t = this._toPixels(a.startTime), q = this._toPixels(a.endTime), n = q - t; + if (n <= this._minFrameWidthInPixels) { + return m.fillStyle = this._controller.theme.tabToolbar(1), m.fillRect(t, 12.5 * e, this._minFrameWidthInPixels, 12 + 12.5 * (a.maxDepth - a.depth)), !1; + } + 0 > t && (q = n + t, t = 0); + var q = q - t, k = this._kindStyle[a.kind.id]; + k || (k = c.ColorStyle.randomStyle(), k = this._kindStyle[a.kind.id] = {bgColor:k, textColor:c.ColorStyle.contrastStyle(k)}); + m.save(); + m.fillStyle = k.bgColor; + m.fillRect(t, 12.5 * e, q, 12); + 12 < n && (n = a.kind.name) && n.length && (n = this._prepareText(m, n, q - 4), n.length && (m.fillStyle = k.textColor, m.textBaseline = "bottom", m.fillText(n, t + 2, 12.5 * (e + 1) - 1))); + m.restore(); return!0; }; - d.prototype._prepareText = function(a, b, d) { - var p = this._measureWidth(a, b); - if (d > p) { - return b; + v.prototype._prepareText = function(a, e, m) { + var c = this._measureWidth(a, e); + if (m > c) { + return e; } - for (var p = 3, e = b.length;p < e;) { - var q = p + e >> 1; - this._measureWidth(a, f(b, q)) < d ? p = q + 1 : e = q; - } - b = f(b, e - 1); - p = this._measureWidth(a, b); - return p <= d ? b : ""; - }; - d.prototype._measureWidth = function(a, b) { - var d = this._textWidth[b]; - d || (d = a.measureText(b).width, this._textWidth[b] = d); - return d; + for (var c = 3, q = e.length;c < q;) { + var n = c + q >> 1; + this._measureWidth(a, s(e, n)) < m ? c = n + 1 : q = n; + } + e = s(e, q - 1); + c = this._measureWidth(a, e); + return c <= m ? e : ""; + }; + v.prototype._measureWidth = function(a, e) { + var m = this._textWidth[e]; + m || (m = a.measureText(e).width, this._textWidth[e] = m); + return m; }; - d.prototype._toPixelsRelative = function(a) { + v.prototype._toPixelsRelative = function(a) { return a * this._width / (this._windowEnd - this._windowStart); }; - d.prototype._toPixels = function(a) { + v.prototype._toPixels = function(a) { return this._toPixelsRelative(a - this._windowStart); }; - d.prototype._toTimeRelative = function(a) { + v.prototype._toTimeRelative = function(a) { return a * (this._windowEnd - this._windowStart) / this._width; }; - d.prototype._toTime = function(a) { + v.prototype._toTime = function(a) { return this._toTimeRelative(a) + this._windowStart; }; - d.prototype._getFrameAtPosition = function(a, b) { - var d = 1 + b / 12.5 | 0, p = this._snapshot.query(this._toTime(a)); - if (p && p.depth >= d) { - for (;p && p.depth > d;) { - p = p.parent; + v.prototype._getFrameAtPosition = function(a, e) { + var m = 1 + e / 12.5 | 0, c = this._snapshot.query(this._toTime(a)); + if (c && c.depth >= m) { + for (;c && c.depth > m;) { + c = c.parent; } - return p; + return c; } return null; }; - d.prototype.onMouseDown = function(a, b) { - this._windowEqRange() || (this._mouseController.updateCursor(g.MouseCursor.ALL_SCROLL), this._dragInfo = {windowStartInitial:this._windowStart, windowEndInitial:this._windowEnd, target:1}); + v.prototype.onMouseDown = function(l, e) { + this._windowEqRange() || (this._mouseController.updateCursor(a.MouseCursor.ALL_SCROLL), this._dragInfo = {windowStartInitial:this._windowStart, windowEndInitial:this._windowEnd, target:1}); }; - d.prototype.onMouseMove = function(a, b) { + v.prototype.onMouseMove = function(a, e) { }; - d.prototype.onMouseOver = function(a, b) { + v.prototype.onMouseOver = function(a, e) { }; - d.prototype.onMouseOut = function() { + v.prototype.onMouseOut = function() { }; - d.prototype.onDrag = function(a, b, d, p, e, q) { + v.prototype.onDrag = function(a, e, m, c, q, n) { if (a = this._dragInfo) { - e = this._toTimeRelative(-e), this._controller.setWindow(a.windowStartInitial + e, a.windowEndInitial + e); + q = this._toTimeRelative(-q), this._controller.setWindow(a.windowStartInitial + q, a.windowEndInitial + q); } }; - d.prototype.onDragEnd = function(a, b, d, p, e, q) { + v.prototype.onDragEnd = function(l, e, m, c, q, n) { this._dragInfo = null; - this._mouseController.updateCursor(g.MouseCursor.DEFAULT); + this._mouseController.updateCursor(a.MouseCursor.DEFAULT); }; - d.prototype.onClick = function(a, b) { + v.prototype.onClick = function(l, e) { this._dragInfo = null; - this._mouseController.updateCursor(g.MouseCursor.DEFAULT); + this._mouseController.updateCursor(a.MouseCursor.DEFAULT); }; - d.prototype.onHoverStart = function(a, b) { - var d = this._getFrameAtPosition(a, b); - d && (this._hoveredFrame = d, this._controller.showTooltip(this, d, a, b)); + v.prototype.onHoverStart = function(a, e) { + var m = this._getFrameAtPosition(a, e); + m && (this._hoveredFrame = m, this._controller.showTooltip(this, m, a, e)); }; - d.prototype.onHoverEnd = function() { + v.prototype.onHoverEnd = function() { this._hoveredFrame && (this._hoveredFrame = null, this._controller.hideTooltip()); }; - d.prototype.getStatistics = function(a) { - var b = this._snapshot; - b.statistics || b.calculateStatistics(); - return b.statistics[a.id]; - }; - return d; - }(g.FlameChartBase); - g.FlameChart = s; - })(k.Profiler || (k.Profiler = {})); - })(b.Tools || (b.Tools = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - var f = b.NumberUtilities.clamp; - (function(b) { - b[b.OVERLAY = 0] = "OVERLAY"; - b[b.STACK = 1] = "STACK"; - b[b.UNION = 2] = "UNION"; - })(g.FlameChartOverviewMode || (g.FlameChartOverviewMode = {})); - var k = function(b) { - function m(d, a) { - "undefined" === typeof a && (a = 1); - this._mode = a; + v.prototype.getStatistics = function(a) { + var e = this._snapshot; + e.statistics || e.calculateStatistics(); + return e.statistics[a.id]; + }; + return v; + }(a.FlameChartBase); + a.FlameChart = h; + })(h.Profiler || (h.Profiler = {})); + })(c.Tools || (c.Tools = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.NumberUtilities.clamp; + (function(a) { + a[a.OVERLAY = 0] = "OVERLAY"; + a[a.STACK = 1] = "STACK"; + a[a.UNION = 2] = "UNION"; + })(a.FlameChartOverviewMode || (a.FlameChartOverviewMode = {})); + var h = function(c) { + function h(a, e) { + void 0 === e && (e = 1); + this._mode = e; this._overviewCanvasDirty = !0; this._overviewCanvas = document.createElement("canvas"); this._overviewContext = this._overviewCanvas.getContext("2d"); - b.call(this, d); + c.call(this, a); } - __extends(m, b); - m.prototype.setSize = function(d, a) { - b.prototype.setSize.call(this, d, a || 64); + __extends(h, c); + h.prototype.setSize = function(a, e) { + c.prototype.setSize.call(this, a, e || 64); }; - Object.defineProperty(m.prototype, "mode", {set:function(b) { - this._mode = b; + Object.defineProperty(h.prototype, "mode", {set:function(a) { + this._mode = a; this.draw(); }, enumerable:!0, configurable:!0}); - m.prototype._resetCanvas = function() { - b.prototype._resetCanvas.call(this); + h.prototype._resetCanvas = function() { + c.prototype._resetCanvas.call(this); this._overviewCanvas.width = this._canvas.width; this._overviewCanvas.height = this._canvas.height; this._overviewCanvasDirty = !0; }; - m.prototype.draw = function() { - var b = this._context, a = window.devicePixelRatio, c = this._width, n = this._height; - b.save(); - b.scale(a, a); - b.fillStyle = this._controller.theme.bodyBackground(1); - b.fillRect(0, 0, c, n); - b.restore(); - this._initialized && (this._overviewCanvasDirty && (this._drawChart(), this._overviewCanvasDirty = !1), b.drawImage(this._overviewCanvas, 0, 0), this._drawSelection()); - }; - m.prototype._drawSelection = function() { - var b = this._context, a = this._height, c = window.devicePixelRatio, n = this._selection ? this._selection.left : this._toPixels(this._windowStart), p = this._selection ? this._selection.right : this._toPixels(this._windowEnd), e = this._controller.theme; - b.save(); - b.scale(c, c); - this._selection ? (b.fillStyle = e.selectionText(.15), b.fillRect(n, 1, p - n, a - 1), b.fillStyle = "rgba(133, 0, 0, 1)", b.fillRect(n + .5, 0, p - n - 1, 4), b.fillRect(n + .5, a - 4, p - n - 1, 4)) : (b.fillStyle = e.bodyBackground(.4), b.fillRect(0, 1, n, a - 1), b.fillRect(p, 1, this._width, a - 1)); - b.beginPath(); - b.moveTo(n, 0); - b.lineTo(n, a); - b.moveTo(p, 0); - b.lineTo(p, a); - b.lineWidth = .5; - b.strokeStyle = e.foregroundTextGrey(1); - b.stroke(); - a = Math.abs((this._selection ? this._toTime(this._selection.right) : this._windowEnd) - (this._selection ? this._toTime(this._selection.left) : this._windowStart)); - b.fillStyle = e.selectionText(.5); - b.font = "8px sans-serif"; - b.textBaseline = "alphabetic"; - b.textAlign = "end"; - b.fillText(a.toFixed(2), Math.min(n, p) - 4, 10); - b.fillText((a / 60).toFixed(2), Math.min(n, p) - 4, 20); - b.restore(); - }; - m.prototype._drawChart = function() { - var b = window.devicePixelRatio, a = this._height, c = this._controller.activeProfile, n = 4 * this._width, p = c.totalTime / n, e = this._overviewContext, q = this._controller.theme.blueHighlight(1); - e.save(); - e.translate(0, b * a); - var l = -b * a / (c.maxDepth - 1); - e.scale(b / 4, l); - e.clearRect(0, 0, n, c.maxDepth - 1); - 1 == this._mode && e.scale(1, 1 / c.snapshotCount); - for (var f = 0, w = c.snapshotCount;f < w;f++) { - var r = c.getSnapshotAt(f); - if (r) { - var h = null, x = 0; - e.beginPath(); - e.moveTo(0, 0); - for (var y = 0;y < n;y++) { - x = c.startTime + y * p, x = (h = h ? h.queryNext(x) : r.query(x)) ? h.getDepth() - 1 : 0, e.lineTo(y, x); - } - e.lineTo(y, 0); - e.fillStyle = q; - e.fill(); - 1 == this._mode && e.translate(0, -a * b / l); - } - } - e.restore(); - }; - m.prototype._toPixelsRelative = function(b) { - return b * this._width / (this._rangeEnd - this._rangeStart); - }; - m.prototype._toPixels = function(b) { - return this._toPixelsRelative(b - this._rangeStart); - }; - m.prototype._toTimeRelative = function(b) { - return b * (this._rangeEnd - this._rangeStart) / this._width; - }; - m.prototype._toTime = function(b) { - return this._toTimeRelative(b) + this._rangeStart; - }; - m.prototype._getDragTargetUnderCursor = function(b, a) { - if (0 <= a && a < this._height) { - var c = this._toPixels(this._windowStart), n = this._toPixels(this._windowEnd), p = 2 + g.FlameChartBase.DRAGHANDLE_WIDTH / 2, e = b >= c - p && b <= c + p, q = b >= n - p && b <= n + p; - if (e && q) { + h.prototype.draw = function() { + var a = this._context, e = window.devicePixelRatio, m = this._width, c = this._height; + a.save(); + a.scale(e, e); + a.fillStyle = this._controller.theme.bodyBackground(1); + a.fillRect(0, 0, m, c); + a.restore(); + this._initialized && (this._overviewCanvasDirty && (this._drawChart(), this._overviewCanvasDirty = !1), a.drawImage(this._overviewCanvas, 0, 0), this._drawSelection()); + }; + h.prototype._drawSelection = function() { + var a = this._context, e = this._height, m = window.devicePixelRatio, c = this._selection ? this._selection.left : this._toPixels(this._windowStart), q = this._selection ? this._selection.right : this._toPixels(this._windowEnd), n = this._controller.theme; + a.save(); + a.scale(m, m); + this._selection ? (a.fillStyle = n.selectionText(.15), a.fillRect(c, 1, q - c, e - 1), a.fillStyle = "rgba(133, 0, 0, 1)", a.fillRect(c + .5, 0, q - c - 1, 4), a.fillRect(c + .5, e - 4, q - c - 1, 4)) : (a.fillStyle = n.bodyBackground(.4), a.fillRect(0, 1, c, e - 1), a.fillRect(q, 1, this._width, e - 1)); + a.beginPath(); + a.moveTo(c, 0); + a.lineTo(c, e); + a.moveTo(q, 0); + a.lineTo(q, e); + a.lineWidth = .5; + a.strokeStyle = n.foregroundTextGrey(1); + a.stroke(); + e = Math.abs((this._selection ? this._toTime(this._selection.right) : this._windowEnd) - (this._selection ? this._toTime(this._selection.left) : this._windowStart)); + a.fillStyle = n.selectionText(.5); + a.font = "8px sans-serif"; + a.textBaseline = "alphabetic"; + a.textAlign = "end"; + a.fillText(e.toFixed(2), Math.min(c, q) - 4, 10); + a.fillText((e / 60).toFixed(2), Math.min(c, q) - 4, 20); + a.restore(); + }; + h.prototype._drawChart = function() { + var a = window.devicePixelRatio, e = this._height, m = this._controller.activeProfile, c = 4 * this._width, q = m.totalTime / c, n = this._overviewContext, k = this._controller.theme.blueHighlight(1); + n.save(); + n.translate(0, a * e); + var f = -a * e / (m.maxDepth - 1); + n.scale(a / 4, f); + n.clearRect(0, 0, c, m.maxDepth - 1); + 1 == this._mode && n.scale(1, 1 / m.snapshotCount); + for (var d = 0, b = m.snapshotCount;d < b;d++) { + var g = m.getSnapshotAt(d); + if (g) { + var r = null, w = 0; + n.beginPath(); + n.moveTo(0, 0); + for (var z = 0;z < c;z++) { + w = m.startTime + z * q, w = (r = r ? r.queryNext(w) : g.query(w)) ? r.getDepth() - 1 : 0, n.lineTo(z, w); + } + n.lineTo(z, 0); + n.fillStyle = k; + n.fill(); + 1 == this._mode && n.translate(0, -e * a / f); + } + } + n.restore(); + }; + h.prototype._toPixelsRelative = function(a) { + return a * this._width / (this._rangeEnd - this._rangeStart); + }; + h.prototype._toPixels = function(a) { + return this._toPixelsRelative(a - this._rangeStart); + }; + h.prototype._toTimeRelative = function(a) { + return a * (this._rangeEnd - this._rangeStart) / this._width; + }; + h.prototype._toTime = function(a) { + return this._toTimeRelative(a) + this._rangeStart; + }; + h.prototype._getDragTargetUnderCursor = function(c, e) { + if (0 <= e && e < this._height) { + var m = this._toPixels(this._windowStart), t = this._toPixels(this._windowEnd), q = 2 + a.FlameChartBase.DRAGHANDLE_WIDTH / 2, n = c >= m - q && c <= m + q, k = c >= t - q && c <= t + q; + if (n && k) { return 4; } - if (e) { + if (n) { return 2; } - if (q) { + if (k) { return 3; } - if (!this._windowEqRange() && b > c + p && b < n - p) { + if (!this._windowEqRange() && c > m + q && c < t - q) { return 1; } } return 0; }; - m.prototype.onMouseDown = function(b, a) { - var c = this._getDragTargetUnderCursor(b, a); - 0 === c ? (this._selection = {left:b, right:b}, this.draw()) : (1 === c && this._mouseController.updateCursor(g.MouseCursor.GRABBING), this._dragInfo = {windowStartInitial:this._windowStart, windowEndInitial:this._windowEnd, target:c}); - }; - m.prototype.onMouseMove = function(b, a) { - var c = g.MouseCursor.DEFAULT, n = this._getDragTargetUnderCursor(b, a); - 0 === n || this._selection || (c = 1 === n ? g.MouseCursor.GRAB : g.MouseCursor.EW_RESIZE); - this._mouseController.updateCursor(c); + h.prototype.onMouseDown = function(c, e) { + var m = this._getDragTargetUnderCursor(c, e); + 0 === m ? (this._selection = {left:c, right:c}, this.draw()) : (1 === m && this._mouseController.updateCursor(a.MouseCursor.GRABBING), this._dragInfo = {windowStartInitial:this._windowStart, windowEndInitial:this._windowEnd, target:m}); + }; + h.prototype.onMouseMove = function(c, e) { + var m = a.MouseCursor.DEFAULT, t = this._getDragTargetUnderCursor(c, e); + 0 === t || this._selection || (m = 1 === t ? a.MouseCursor.GRAB : a.MouseCursor.EW_RESIZE); + this._mouseController.updateCursor(m); }; - m.prototype.onMouseOver = function(b, a) { - this.onMouseMove(b, a); + h.prototype.onMouseOver = function(a, e) { + this.onMouseMove(a, e); }; - m.prototype.onMouseOut = function() { - this._mouseController.updateCursor(g.MouseCursor.DEFAULT); + h.prototype.onMouseOut = function() { + this._mouseController.updateCursor(a.MouseCursor.DEFAULT); }; - m.prototype.onDrag = function(b, a, c, n, p, e) { + h.prototype.onDrag = function(c, e, m, t, q, n) { if (this._selection) { - this._selection = {left:b, right:f(c, 0, this._width - 1)}, this.draw(); + this._selection = {left:c, right:s(m, 0, this._width - 1)}, this.draw(); } else { - b = this._dragInfo; - if (4 === b.target) { - if (0 !== p) { - b.target = 0 > p ? 2 : 3; + c = this._dragInfo; + if (4 === c.target) { + if (0 !== q) { + c.target = 0 > q ? 2 : 3; } else { return; } } - a = this._windowStart; - c = this._windowEnd; - p = this._toTimeRelative(p); - switch(b.target) { + e = this._windowStart; + m = this._windowEnd; + q = this._toTimeRelative(q); + switch(c.target) { case 1: - a = b.windowStartInitial + p; - c = b.windowEndInitial + p; + e = c.windowStartInitial + q; + m = c.windowEndInitial + q; break; case 2: - a = f(b.windowStartInitial + p, this._rangeStart, c - g.FlameChartBase.MIN_WINDOW_LEN); + e = s(c.windowStartInitial + q, this._rangeStart, m - a.FlameChartBase.MIN_WINDOW_LEN); break; case 3: - c = f(b.windowEndInitial + p, a + g.FlameChartBase.MIN_WINDOW_LEN, this._rangeEnd); + m = s(c.windowEndInitial + q, e + a.FlameChartBase.MIN_WINDOW_LEN, this._rangeEnd); break; default: return; } - this._controller.setWindow(a, c); + this._controller.setWindow(e, m); } }; - m.prototype.onDragEnd = function(b, a, c, n, p, e) { - this._selection && (this._selection = null, this._controller.setWindow(this._toTime(b), this._toTime(c))); + h.prototype.onDragEnd = function(a, e, m, c, q, n) { + this._selection && (this._selection = null, this._controller.setWindow(this._toTime(a), this._toTime(m))); this._dragInfo = null; - this.onMouseMove(c, n); + this.onMouseMove(m, c); }; - m.prototype.onClick = function(b, a) { + h.prototype.onClick = function(a, e) { this._selection = this._dragInfo = null; - this._windowEqRange() || (0 === this._getDragTargetUnderCursor(b, a) && this._controller.moveWindowTo(this._toTime(b)), this.onMouseMove(b, a)); + this._windowEqRange() || (0 === this._getDragTargetUnderCursor(a, e) && this._controller.moveWindowTo(this._toTime(a)), this.onMouseMove(a, e)); this.draw(); }; - m.prototype.onHoverStart = function(b, a) { + h.prototype.onHoverStart = function(a, e) { }; - m.prototype.onHoverEnd = function() { + h.prototype.onHoverEnd = function() { }; - return m; - }(g.FlameChartBase); - g.FlameChartOverview = k; - })(k.Profiler || (k.Profiler = {})); - })(b.Tools || (b.Tools = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - var f = b.NumberUtilities.clamp; - (function(b) { - b[b.OVERVIEW = 0] = "OVERVIEW"; - b[b.CHART = 1] = "CHART"; - })(g.FlameChartHeaderType || (g.FlameChartHeaderType = {})); - var k = function(b) { - function m(d, a) { - this._type = a; - b.call(this, d); - } - __extends(m, b); - m.prototype.draw = function() { - var b = this._context, a = window.devicePixelRatio, c = this._width, n = this._height; - b.save(); - b.scale(a, a); - b.fillStyle = this._controller.theme.tabToolbar(1); - b.fillRect(0, 0, c, n); - this._initialized && (0 == this._type ? (a = this._toPixels(this._windowStart), c = this._toPixels(this._windowEnd), b.fillStyle = this._controller.theme.bodyBackground(1), b.fillRect(a, 0, c - a, n), this._drawLabels(this._rangeStart, this._rangeEnd), this._drawDragHandle(a), this._drawDragHandle(c)) : this._drawLabels(this._windowStart, this._windowEnd)); - b.restore(); - }; - m.prototype._drawLabels = function(b, a) { - var c = this._context, n = this._calculateTickInterval(b, a), p = Math.ceil(b / n) * n, e = 500 <= n, q = e ? 1E3 : 1, l = this._decimalPlaces(n / q), e = e ? "s" : "ms", f = this._toPixels(p), w = this._height / 2, r = this._controller.theme; - c.lineWidth = 1; - c.strokeStyle = r.contentTextDarkGrey(.5); - c.fillStyle = r.contentTextDarkGrey(1); - c.textAlign = "right"; - c.textBaseline = "middle"; - c.font = "11px sans-serif"; - for (r = this._width + m.TICK_MAX_WIDTH;f < r;) { - c.fillText((p / q).toFixed(l) + " " + e, f - 7, w + 1), c.beginPath(), c.moveTo(f, 0), c.lineTo(f, this._height + 1), c.closePath(), c.stroke(), p += n, f = this._toPixels(p); - } - }; - m.prototype._calculateTickInterval = function(b, a) { - var c = (a - b) / (this._width / m.TICK_MAX_WIDTH), n = Math.pow(10, Math.floor(Math.log(c) / Math.LN10)), c = c / n; - return 5 < c ? 10 * n : 2 < c ? 5 * n : 1 < c ? 2 * n : n; - }; - m.prototype._drawDragHandle = function(b) { - var a = this._context; - a.lineWidth = 2; - a.strokeStyle = this._controller.theme.bodyBackground(1); - a.fillStyle = this._controller.theme.foregroundTextGrey(.7); - this._drawRoundedRect(a, b - g.FlameChartBase.DRAGHANDLE_WIDTH / 2, g.FlameChartBase.DRAGHANDLE_WIDTH, this._height - 2); - }; - m.prototype._drawRoundedRect = function(b, a, c, n) { - var p, e = !0; - "undefined" === typeof e && (e = !0); - "undefined" === typeof p && (p = !0); - b.beginPath(); - b.moveTo(a + 2, 1); - b.lineTo(a + c - 2, 1); - b.quadraticCurveTo(a + c, 1, a + c, 3); - b.lineTo(a + c, 1 + n - 2); - b.quadraticCurveTo(a + c, 1 + n, a + c - 2, 1 + n); - b.lineTo(a + 2, 1 + n); - b.quadraticCurveTo(a, 1 + n, a, 1 + n - 2); - b.lineTo(a, 3); - b.quadraticCurveTo(a, 1, a + 2, 1); - b.closePath(); - e && b.stroke(); - p && b.fill(); - }; - m.prototype._toPixelsRelative = function(b) { - return b * this._width / (0 === this._type ? this._rangeEnd - this._rangeStart : this._windowEnd - this._windowStart); - }; - m.prototype._toPixels = function(b) { - return this._toPixelsRelative(b - (0 === this._type ? this._rangeStart : this._windowStart)); + return h; + }(a.FlameChartBase); + a.FlameChartOverview = h; + })(h.Profiler || (h.Profiler = {})); + })(c.Tools || (c.Tools = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.NumberUtilities.clamp; + (function(a) { + a[a.OVERVIEW = 0] = "OVERVIEW"; + a[a.CHART = 1] = "CHART"; + })(a.FlameChartHeaderType || (a.FlameChartHeaderType = {})); + var h = function(c) { + function h(a, e) { + this._type = e; + c.call(this, a); + } + __extends(h, c); + h.prototype.draw = function() { + var a = this._context, e = window.devicePixelRatio, m = this._width, c = this._height; + a.save(); + a.scale(e, e); + a.fillStyle = this._controller.theme.tabToolbar(1); + a.fillRect(0, 0, m, c); + this._initialized && (0 == this._type ? (e = this._toPixels(this._windowStart), m = this._toPixels(this._windowEnd), a.fillStyle = this._controller.theme.bodyBackground(1), a.fillRect(e, 0, m - e, c), this._drawLabels(this._rangeStart, this._rangeEnd), this._drawDragHandle(e), this._drawDragHandle(m)) : this._drawLabels(this._windowStart, this._windowEnd)); + a.restore(); + }; + h.prototype._drawLabels = function(a, e) { + var m = this._context, c = this._calculateTickInterval(a, e), q = Math.ceil(a / c) * c, n = 500 <= c, k = n ? 1E3 : 1, f = this._decimalPlaces(c / k), n = n ? "s" : "ms", d = this._toPixels(q), b = this._height / 2, g = this._controller.theme; + m.lineWidth = 1; + m.strokeStyle = g.contentTextDarkGrey(.5); + m.fillStyle = g.contentTextDarkGrey(1); + m.textAlign = "right"; + m.textBaseline = "middle"; + m.font = "11px sans-serif"; + for (g = this._width + h.TICK_MAX_WIDTH;d < g;) { + m.fillText((q / k).toFixed(f) + " " + n, d - 7, b + 1), m.beginPath(), m.moveTo(d, 0), m.lineTo(d, this._height + 1), m.closePath(), m.stroke(), q += c, d = this._toPixels(q); + } + }; + h.prototype._calculateTickInterval = function(a, e) { + var m = (e - a) / (this._width / h.TICK_MAX_WIDTH), c = Math.pow(10, Math.floor(Math.log(m) / Math.LN10)), m = m / c; + return 5 < m ? 10 * c : 2 < m ? 5 * c : 1 < m ? 2 * c : c; + }; + h.prototype._drawDragHandle = function(c) { + var e = this._context; + e.lineWidth = 2; + e.strokeStyle = this._controller.theme.bodyBackground(1); + e.fillStyle = this._controller.theme.foregroundTextGrey(.7); + this._drawRoundedRect(e, c - a.FlameChartBase.DRAGHANDLE_WIDTH / 2, a.FlameChartBase.DRAGHANDLE_WIDTH, this._height - 2); + }; + h.prototype._drawRoundedRect = function(a, e, m, c) { + var q, n = !0; + void 0 === n && (n = !0); + void 0 === q && (q = !0); + a.beginPath(); + a.moveTo(e + 2, 1); + a.lineTo(e + m - 2, 1); + a.quadraticCurveTo(e + m, 1, e + m, 3); + a.lineTo(e + m, 1 + c - 2); + a.quadraticCurveTo(e + m, 1 + c, e + m - 2, 1 + c); + a.lineTo(e + 2, 1 + c); + a.quadraticCurveTo(e, 1 + c, e, 1 + c - 2); + a.lineTo(e, 3); + a.quadraticCurveTo(e, 1, e + 2, 1); + a.closePath(); + n && a.stroke(); + q && a.fill(); + }; + h.prototype._toPixelsRelative = function(a) { + return a * this._width / (0 === this._type ? this._rangeEnd - this._rangeStart : this._windowEnd - this._windowStart); + }; + h.prototype._toPixels = function(a) { + return this._toPixelsRelative(a - (0 === this._type ? this._rangeStart : this._windowStart)); }; - m.prototype._toTimeRelative = function(b) { - return b * (0 === this._type ? this._rangeEnd - this._rangeStart : this._windowEnd - this._windowStart) / this._width; + h.prototype._toTimeRelative = function(a) { + return a * (0 === this._type ? this._rangeEnd - this._rangeStart : this._windowEnd - this._windowStart) / this._width; }; - m.prototype._toTime = function(b) { - return this._toTimeRelative(b) + (0 === this._type ? this._rangeStart : this._windowStart); + h.prototype._toTime = function(a) { + return this._toTimeRelative(a) + (0 === this._type ? this._rangeStart : this._windowStart); }; - m.prototype._getDragTargetUnderCursor = function(b, a) { - if (0 <= a && a < this._height) { + h.prototype._getDragTargetUnderCursor = function(c, e) { + if (0 <= e && e < this._height) { if (0 === this._type) { - var c = this._toPixels(this._windowStart), n = this._toPixels(this._windowEnd), p = 2 + g.FlameChartBase.DRAGHANDLE_WIDTH / 2, c = b >= c - p && b <= c + p, n = b >= n - p && b <= n + p; - if (c && n) { + var m = this._toPixels(this._windowStart), t = this._toPixels(this._windowEnd), q = 2 + a.FlameChartBase.DRAGHANDLE_WIDTH / 2, m = c >= m - q && c <= m + q, t = c >= t - q && c <= t + q; + if (m && t) { return 4; } - if (c) { + if (m) { return 2; } - if (n) { + if (t) { return 3; } } @@ -5496,134 +5811,134 @@ } return 0; }; - m.prototype.onMouseDown = function(b, a) { - var c = this._getDragTargetUnderCursor(b, a); - 1 === c && this._mouseController.updateCursor(g.MouseCursor.GRABBING); - this._dragInfo = {windowStartInitial:this._windowStart, windowEndInitial:this._windowEnd, target:c}; - }; - m.prototype.onMouseMove = function(b, a) { - var c = g.MouseCursor.DEFAULT, n = this._getDragTargetUnderCursor(b, a); - 0 !== n && (1 !== n ? c = g.MouseCursor.EW_RESIZE : 1 !== n || this._windowEqRange() || (c = g.MouseCursor.GRAB)); - this._mouseController.updateCursor(c); - }; - m.prototype.onMouseOver = function(b, a) { - this.onMouseMove(b, a); - }; - m.prototype.onMouseOut = function() { - this._mouseController.updateCursor(g.MouseCursor.DEFAULT); - }; - m.prototype.onDrag = function(b, a, c, n, p, e) { - b = this._dragInfo; - if (4 === b.target) { - if (0 !== p) { - b.target = 0 > p ? 2 : 3; + h.prototype.onMouseDown = function(c, e) { + var m = this._getDragTargetUnderCursor(c, e); + 1 === m && this._mouseController.updateCursor(a.MouseCursor.GRABBING); + this._dragInfo = {windowStartInitial:this._windowStart, windowEndInitial:this._windowEnd, target:m}; + }; + h.prototype.onMouseMove = function(c, e) { + var m = a.MouseCursor.DEFAULT, t = this._getDragTargetUnderCursor(c, e); + 0 !== t && (1 !== t ? m = a.MouseCursor.EW_RESIZE : 1 !== t || this._windowEqRange() || (m = a.MouseCursor.GRAB)); + this._mouseController.updateCursor(m); + }; + h.prototype.onMouseOver = function(a, e) { + this.onMouseMove(a, e); + }; + h.prototype.onMouseOut = function() { + this._mouseController.updateCursor(a.MouseCursor.DEFAULT); + }; + h.prototype.onDrag = function(c, e, m, t, q, n) { + c = this._dragInfo; + if (4 === c.target) { + if (0 !== q) { + c.target = 0 > q ? 2 : 3; } else { return; } } - a = this._windowStart; - c = this._windowEnd; - p = this._toTimeRelative(p); - switch(b.target) { + e = this._windowStart; + m = this._windowEnd; + q = this._toTimeRelative(q); + switch(c.target) { case 1: - c = 0 === this._type ? 1 : -1; - a = b.windowStartInitial + c * p; - c = b.windowEndInitial + c * p; + m = 0 === this._type ? 1 : -1; + e = c.windowStartInitial + m * q; + m = c.windowEndInitial + m * q; break; case 2: - a = f(b.windowStartInitial + p, this._rangeStart, c - g.FlameChartBase.MIN_WINDOW_LEN); + e = s(c.windowStartInitial + q, this._rangeStart, m - a.FlameChartBase.MIN_WINDOW_LEN); break; case 3: - c = f(b.windowEndInitial + p, a + g.FlameChartBase.MIN_WINDOW_LEN, this._rangeEnd); + m = s(c.windowEndInitial + q, e + a.FlameChartBase.MIN_WINDOW_LEN, this._rangeEnd); break; default: return; } - this._controller.setWindow(a, c); + this._controller.setWindow(e, m); }; - m.prototype.onDragEnd = function(b, a, c, n, p, e) { + h.prototype.onDragEnd = function(a, e, m, c, q, n) { this._dragInfo = null; - this.onMouseMove(c, n); + this.onMouseMove(m, c); }; - m.prototype.onClick = function(b, a) { - 1 === this._dragInfo.target && this._mouseController.updateCursor(g.MouseCursor.GRAB); + h.prototype.onClick = function(c, e) { + 1 === this._dragInfo.target && this._mouseController.updateCursor(a.MouseCursor.GRAB); }; - m.prototype.onHoverStart = function(b, a) { + h.prototype.onHoverStart = function(a, e) { }; - m.prototype.onHoverEnd = function() { + h.prototype.onHoverEnd = function() { }; - m.TICK_MAX_WIDTH = 75; - return m; - }(g.FlameChartBase); - g.FlameChartHeader = k; - })(k.Profiler || (k.Profiler = {})); - })(b.Tools || (b.Tools = {})); + h.TICK_MAX_WIDTH = 75; + return h; + }(a.FlameChartBase); + a.FlameChartHeader = h; + })(h.Profiler || (h.Profiler = {})); + })(c.Tools || (c.Tools = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(b) { - var g = function() { - function b(d, a, c, n, p) { - this.pageLoaded = d; - this.threadsTotal = a; - this.threadsLoaded = c; - this.threadFilesTotal = n; - this.threadFilesLoaded = p; +(function(c) { + (function(c) { + (function(a) { + (function(a) { + var c = function() { + function a(c, e, m, t, q) { + this.pageLoaded = c; + this.threadsTotal = e; + this.threadsLoaded = m; + this.threadFilesTotal = t; + this.threadFilesLoaded = q; } - b.prototype.toString = function() { - return "[" + ["pageLoaded", "threadsTotal", "threadsLoaded", "threadFilesTotal", "threadFilesLoaded"].map(function(b, a, c) { - return b + ":" + this[b]; + a.prototype.toString = function() { + return "[" + ["pageLoaded", "threadsTotal", "threadsLoaded", "threadFilesTotal", "threadFilesLoaded"].map(function(a, e, m) { + return a + ":" + this[a]; }, this).join(", ") + "]"; }; - return b; + return a; }(); - b.TraceLoggerProgressInfo = g; - var k = function() { - function m(b) { - this._baseUrl = b; + a.TraceLoggerProgressInfo = c; + var h = function() { + function h(a) { + this._baseUrl = a; this._threads = []; this._progressInfo = null; } - m.prototype.loadPage = function(b, a, c) { + h.prototype.loadPage = function(a, e, m) { this._threads = []; - this._pageLoadCallback = a; - this._pageLoadProgressCallback = c; - this._progressInfo = new g(!1, 0, 0, 0, 0); - this._loadData([b], this._onLoadPage.bind(this)); - }; - Object.defineProperty(m.prototype, "buffers", {get:function() { - for (var b = [], a = 0, c = this._threads.length;a < c;a++) { - b.push(this._threads[a].buffer); + this._pageLoadCallback = e; + this._pageLoadProgressCallback = m; + this._progressInfo = new c(!1, 0, 0, 0, 0); + this._loadData([a], this._onLoadPage.bind(this)); + }; + Object.defineProperty(h.prototype, "buffers", {get:function() { + for (var a = [], e = 0, m = this._threads.length;e < m;e++) { + a.push(this._threads[e].buffer); } - return b; + return a; }, enumerable:!0, configurable:!0}); - m.prototype._onProgress = function() { + h.prototype._onProgress = function() { this._pageLoadProgressCallback && this._pageLoadProgressCallback.call(this, this._progressInfo); }; - m.prototype._onLoadPage = function(d) { - if (d && 1 == d.length) { - var a = this, c = 0; - d = d[0]; - var n = d.length; - this._threads = Array(n); + h.prototype._onLoadPage = function(c) { + if (c && 1 == c.length) { + var e = this, m = 0; + c = c[0]; + var t = c.length; + this._threads = Array(t); this._progressInfo.pageLoaded = !0; - this._progressInfo.threadsTotal = n; - for (var p = 0;p < d.length;p++) { - var e = d[p], q = [e.dict, e.tree]; - e.corrections && q.push(e.corrections); - this._progressInfo.threadFilesTotal += q.length; - this._loadData(q, function(e) { + this._progressInfo.threadsTotal = t; + for (var q = 0;q < c.length;q++) { + var n = c[q], k = [n.dict, n.tree]; + n.corrections && k.push(n.corrections); + this._progressInfo.threadFilesTotal += k.length; + this._loadData(k, function(f) { return function(d) { - d && (d = new b.Thread(d), d.buffer.name = "Thread " + e, a._threads[e] = d); - c++; - a._progressInfo.threadsLoaded++; - a._onProgress(); - c === n && a._pageLoadCallback.call(a, null, a._threads); + d && (d = new a.Thread(d), d.buffer.name = "Thread " + f, e._threads[f] = d); + m++; + e._progressInfo.threadsLoaded++; + e._onProgress(); + m === t && e._pageLoadCallback.call(e, null, e._threads); }; - }(p), function(b) { - a._progressInfo.threadFilesLoaded++; - a._onProgress(); + }(q), function(a) { + e._progressInfo.threadFilesLoaded++; + e._onProgress(); }); } this._onProgress(); @@ -5631,87 +5946,87 @@ this._pageLoadCallback.call(this, "Error loading page.", null); } }; - m.prototype._loadData = function(b, a, c) { - var n = 0, p = 0, e = b.length, q = []; - q.length = e; - for (var l = 0;l < e;l++) { - var f = this._baseUrl + b[l], w = new XMLHttpRequest, r = /\.tl$/i.test(f) ? "arraybuffer" : "json"; - w.open("GET", f, !0); - w.responseType = r; - w.onload = function(b, l) { - return function(d) { - if ("json" === l) { - if (d = this.response, "string" === typeof d) { + h.prototype._loadData = function(a, e, m) { + var c = 0, q = 0, n = a.length, k = []; + k.length = n; + for (var f = 0;f < n;f++) { + var d = this._baseUrl + a[f], b = new XMLHttpRequest, g = /\.tl$/i.test(d) ? "arraybuffer" : "json"; + b.open("GET", d, !0); + b.responseType = g; + b.onload = function(b, d) { + return function(g) { + if ("json" === d) { + if (g = this.response, "string" === typeof g) { try { - d = JSON.parse(d), q[b] = d; - } catch (r) { - p++; + g = JSON.parse(g), k[b] = g; + } catch (a) { + q++; } } else { - q[b] = d; + k[b] = g; } } else { - q[b] = this.response; + k[b] = this.response; } - ++n; - c && c(n); - n === e && a(q); + ++c; + m && m(c); + c === n && e(k); }; - }(l, r); - w.send(); + }(f, g); + b.send(); } }; - m.colors = "#0044ff #8c4b00 #cc5c33 #ff80c4 #ffbfd9 #ff8800 #8c5e00 #adcc33 #b380ff #bfd9ff #ffaa00 #8c0038 #bf8f30 #f780ff #cc99c9 #aaff00 #000073 #452699 #cc8166 #cca799 #000066 #992626 #cc6666 #ccc299 #ff6600 #526600 #992663 #cc6681 #99ccc2 #ff0066 #520066 #269973 #61994d #739699 #ffcc00 #006629 #269199 #94994d #738299 #ff0000 #590000 #234d8c #8c6246 #7d7399 #ee00ff #00474d #8c2385 #8c7546 #7c8c69 #eeff00 #4d003d #662e1a #62468c #8c6969 #6600ff #4c2900 #1a6657 #8c464f #8c6981 #44ff00 #401100 #1a2466 #663355 #567365 #d90074 #403300 #101d40 #59562d #66614d #cc0000 #002b40 #234010 #4c2626 #4d5e66 #00a3cc #400011 #231040 #4c3626 #464359 #0000bf #331b00 #80e6ff #311a33 #4d3939 #a69b00 #003329 #80ffb2 #331a20 #40303d #00a658 #40ffd9 #ffc480 #ffe1bf #332b26 #8c2500 #9933cc #80fff6 #ffbfbf #303326 #005e8c #33cc47 #b2ff80 #c8bfff #263332 #00708c #cc33ad #ffe680 #f2ffbf #262a33 #388c00 #335ccc #8091ff #bfffd9".split(" "); - return m; + h.colors = "#0044ff #8c4b00 #cc5c33 #ff80c4 #ffbfd9 #ff8800 #8c5e00 #adcc33 #b380ff #bfd9ff #ffaa00 #8c0038 #bf8f30 #f780ff #cc99c9 #aaff00 #000073 #452699 #cc8166 #cca799 #000066 #992626 #cc6666 #ccc299 #ff6600 #526600 #992663 #cc6681 #99ccc2 #ff0066 #520066 #269973 #61994d #739699 #ffcc00 #006629 #269199 #94994d #738299 #ff0000 #590000 #234d8c #8c6246 #7d7399 #ee00ff #00474d #8c2385 #8c7546 #7c8c69 #eeff00 #4d003d #662e1a #62468c #8c6969 #6600ff #4c2900 #1a6657 #8c464f #8c6981 #44ff00 #401100 #1a2466 #663355 #567365 #d90074 #403300 #101d40 #59562d #66614d #cc0000 #002b40 #234010 #4c2626 #4d5e66 #00a3cc #400011 #231040 #4c3626 #464359 #0000bf #331b00 #80e6ff #311a33 #4d3939 #a69b00 #003329 #80ffb2 #331a20 #40303d #00a658 #40ffd9 #ffc480 #ffe1bf #332b26 #8c2500 #9933cc #80fff6 #ffbfbf #303326 #005e8c #33cc47 #b2ff80 #c8bfff #263332 #00708c #cc33ad #ffe680 #f2ffbf #262a33 #388c00 #335ccc #8091ff #bfffd9".split(" "); + return h; }(); - b.TraceLogger = k; - })(b.TraceLogger || (b.TraceLogger = {})); - })(b.Profiler || (b.Profiler = {})); - })(b.Tools || (b.Tools = {})); + a.TraceLogger = h; + })(a.TraceLogger || (a.TraceLogger = {})); + })(c.Profiler || (c.Profiler = {})); + })(c.Tools || (c.Tools = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - var k; - (function(b) { - b[b.START_HI = 0] = "START_HI"; - b[b.START_LO = 4] = "START_LO"; - b[b.STOP_HI = 8] = "STOP_HI"; - b[b.STOP_LO = 12] = "STOP_LO"; - b[b.TEXTID = 16] = "TEXTID"; - b[b.NEXTID = 20] = "NEXTID"; - })(k || (k = {})); - k = function() { - function f(m) { - 2 <= m.length && (this._text = m[0], this._data = new DataView(m[1]), this._buffer = new b.TimelineBuffer, this._walkTree(0)); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + var h; + (function(a) { + a[a.START_HI = 0] = "START_HI"; + a[a.START_LO = 4] = "START_LO"; + a[a.STOP_HI = 8] = "STOP_HI"; + a[a.STOP_LO = 12] = "STOP_LO"; + a[a.TEXTID = 16] = "TEXTID"; + a[a.NEXTID = 20] = "NEXTID"; + })(h || (h = {})); + h = function() { + function c(s) { + 2 <= s.length && (this._text = s[0], this._data = new DataView(s[1]), this._buffer = new a.TimelineBuffer, this._walkTree(0)); } - Object.defineProperty(f.prototype, "buffer", {get:function() { + Object.defineProperty(c.prototype, "buffer", {get:function() { return this._buffer; }, enumerable:!0, configurable:!0}); - f.prototype._walkTree = function(b) { - var d = this._data, a = this._buffer; + c.prototype._walkTree = function(a) { + var l = this._data, e = this._buffer; do { - var c = b * f.ITEM_SIZE, n = 4294967295 * d.getUint32(c + 0) + d.getUint32(c + 4), p = 4294967295 * d.getUint32(c + 8) + d.getUint32(c + 12), e = d.getUint32(c + 16), c = d.getUint32(c + 20), q = 1 === (e & 1), e = e >>> 1, e = this._text[e]; - a.enter(e, null, n / 1E6); - q && this._walkTree(b + 1); - a.leave(e, null, p / 1E6); - b = c; - } while (0 !== b); + var m = a * c.ITEM_SIZE, t = 4294967295 * l.getUint32(m + 0) + l.getUint32(m + 4), q = 4294967295 * l.getUint32(m + 8) + l.getUint32(m + 12), n = l.getUint32(m + 16), m = l.getUint32(m + 20), k = 1 === (n & 1), n = n >>> 1, n = this._text[n]; + e.enter(n, null, t / 1E6); + k && this._walkTree(a + 1); + e.leave(n, null, q / 1E6); + a = m; + } while (0 !== a); }; - f.ITEM_SIZE = 24; - return f; + c.ITEM_SIZE = 24; + return c; }(); - f.Thread = k; - })(b.TraceLogger || (b.TraceLogger = {})); - })(b.Profiler || (b.Profiler = {})); - })(b.Tools || (b.Tools = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - var f = b.NumberUtilities.clamp, k = function() { - function b() { + c.Thread = h; + })(a.TraceLogger || (a.TraceLogger = {})); + })(c.Profiler || (c.Profiler = {})); + })(c.Tools || (c.Tools = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.NumberUtilities.clamp, h = function() { + function a() { this.length = 0; this.lines = []; this.format = []; @@ -5719,27 +6034,27 @@ this.repeat = []; this.length = 0; } - b.prototype.append = function(b, a) { - var c = this.lines; - 0 < c.length && c[c.length - 1] === b ? this.repeat[c.length - 1]++ : (this.lines.push(b), this.repeat.push(1), this.format.push(a ? {backgroundFillStyle:a} : void 0), this.time.push(performance.now()), this.length++); + a.prototype.append = function(a, e) { + var m = this.lines; + 0 < m.length && m[m.length - 1] === a ? this.repeat[m.length - 1]++ : (this.lines.push(a), this.repeat.push(1), this.format.push(e ? {backgroundFillStyle:e} : void 0), this.time.push(performance.now()), this.length++); }; - b.prototype.get = function(b) { - return this.lines[b]; + a.prototype.get = function(a) { + return this.lines[a]; }; - b.prototype.getFormat = function(b) { - return this.format[b]; + a.prototype.getFormat = function(a) { + return this.format[a]; }; - b.prototype.getTime = function(b) { - return this.time[b]; + a.prototype.getTime = function(a) { + return this.time[a]; }; - b.prototype.getRepeat = function(b) { - return this.repeat[b]; + a.prototype.getRepeat = function(a) { + return this.repeat[a]; }; - return b; + return a; }(); - g.Buffer = k; - var s = function() { - function b(d) { + a.Buffer = h; + var p = function() { + function a(c) { this.lineColor = "#2A2A2A"; this.alternateLineColor = "#262626"; this.textColor = "#FFFFFF"; @@ -5748,9 +6063,9 @@ this.ratio = 1; this.showLineNumbers = !0; this.showLineCounter = this.showLineTime = !1; - this.canvas = d; + this.canvas = c; this.canvas.focus(); - this.context = d.getContext("2d", {original:!0}); + this.context = c.getContext("2d", {original:!0}); this.context.fillStyle = "#FFFFFF"; this.fontSize = 10; this.columnIndex = this.pageIndex = this.lineIndex = 0; @@ -5761,427 +6076,457 @@ this._resizeHandler(); this.textMarginBottom = this.textMarginLeft = 4; this.refreshFrequency = 0; - this.buffer = new k; - d.addEventListener("keydown", function(b) { - var d = 0; - switch(b.keyCode) { - case h: + this.buffer = new h; + c.addEventListener("keydown", function(a) { + var c = 0; + switch(a.keyCode) { + case r: this.showLineNumbers = !this.showLineNumbers; break; - case x: + case w: this.showLineTime = !this.showLineTime; break; - case e: - d = -1; - break; - case q: - d = 1; + case n: + c = -1; break; - case a: - d = -this.pageLineCount; + case k: + c = 1; break; - case c: - d = this.pageLineCount; + case e: + c = -this.pageLineCount; break; - case n: - d = -this.lineIndex; + case m: + c = this.pageLineCount; break; - case p: - d = this.buffer.length - this.lineIndex; + case t: + c = -this.lineIndex; break; - case l: - this.columnIndex -= b.metaKey ? 10 : 1; - 0 > this.columnIndex && (this.columnIndex = 0); - b.preventDefault(); + case q: + c = this.buffer.length - this.lineIndex; break; case f: - this.columnIndex += b.metaKey ? 10 : 1; - b.preventDefault(); - break; - case w: - b.metaKey && (this.selection = {start:0, end:this.buffer.length}, b.preventDefault()); + this.columnIndex -= a.metaKey ? 10 : 1; + 0 > this.columnIndex && (this.columnIndex = 0); + a.preventDefault(); break; - case r: - if (b.metaKey) { - var g = ""; + case d: + this.columnIndex += a.metaKey ? 10 : 1; + a.preventDefault(); + break; + case b: + a.metaKey && (this.selection = {start:0, end:this.buffer.length}, a.preventDefault()); + break; + case g: + if (a.metaKey) { + var l = ""; if (this.selection) { - for (var m = this.selection.start;m <= this.selection.end;m++) { - g += this.buffer.get(m) + "\n"; + for (var s = this.selection.start;s <= this.selection.end;s++) { + l += this.buffer.get(s) + "\n"; } } else { - g = this.buffer.get(this.lineIndex); + l = this.buffer.get(this.lineIndex); } - alert(g); + alert(l); } ; } - b.metaKey && (d *= this.pageLineCount); - d && (this.scroll(d), b.preventDefault()); - d && b.shiftKey ? this.selection ? this.lineIndex > this.selection.start ? this.selection.end = this.lineIndex : this.selection.start = this.lineIndex : 0 < d ? this.selection = {start:this.lineIndex - d, end:this.lineIndex} : 0 > d && (this.selection = {start:this.lineIndex, end:this.lineIndex - d}) : d && (this.selection = null); + a.metaKey && (c *= this.pageLineCount); + c && (this.scroll(c), a.preventDefault()); + c && a.shiftKey ? this.selection ? this.lineIndex > this.selection.start ? this.selection.end = this.lineIndex : this.selection.start = this.lineIndex : 0 < c ? this.selection = {start:this.lineIndex - c, end:this.lineIndex} : 0 > c && (this.selection = {start:this.lineIndex, end:this.lineIndex - c}) : c && (this.selection = null); this.paint(); }.bind(this), !1); - d.addEventListener("focus", function(a) { + c.addEventListener("focus", function(b) { this.hasFocus = !0; }.bind(this), !1); - d.addEventListener("blur", function(a) { + c.addEventListener("blur", function(b) { this.hasFocus = !1; }.bind(this), !1); - var a = 33, c = 34, n = 36, p = 35, e = 38, q = 40, l = 37, f = 39, w = 65, r = 67, h = 78, x = 84; + var e = 33, m = 34, t = 36, q = 35, n = 38, k = 40, f = 37, d = 39, b = 65, g = 67, r = 78, w = 84; } - b.prototype.resize = function() { + a.prototype.resize = function() { this._resizeHandler(); }; - b.prototype._resizeHandler = function() { - var b = this.canvas.parentElement, a = b.clientWidth, b = b.clientHeight - 1, c = window.devicePixelRatio || 1; - 1 !== c ? (this.ratio = c / 1, this.canvas.width = a * this.ratio, this.canvas.height = b * this.ratio, this.canvas.style.width = a + "px", this.canvas.style.height = b + "px") : (this.ratio = 1, this.canvas.width = a, this.canvas.height = b); + a.prototype._resizeHandler = function() { + var a = this.canvas.parentElement, e = a.clientWidth, a = a.clientHeight - 1, m = window.devicePixelRatio || 1; + 1 !== m ? (this.ratio = m / 1, this.canvas.width = e * this.ratio, this.canvas.height = a * this.ratio, this.canvas.style.width = e + "px", this.canvas.style.height = a + "px") : (this.ratio = 1, this.canvas.width = e, this.canvas.height = a); this.pageLineCount = Math.floor(this.canvas.height / this.lineHeight); }; - b.prototype.gotoLine = function(b) { - this.lineIndex = f(b, 0, this.buffer.length - 1); + a.prototype.gotoLine = function(a) { + this.lineIndex = s(a, 0, this.buffer.length - 1); }; - b.prototype.scrollIntoView = function() { + a.prototype.scrollIntoView = function() { this.lineIndex < this.pageIndex ? this.pageIndex = this.lineIndex : this.lineIndex >= this.pageIndex + this.pageLineCount && (this.pageIndex = this.lineIndex - this.pageLineCount + 1); }; - b.prototype.scroll = function(b) { - this.gotoLine(this.lineIndex + b); + a.prototype.scroll = function(a) { + this.gotoLine(this.lineIndex + a); this.scrollIntoView(); }; - b.prototype.paint = function() { - var b = this.pageLineCount; - this.pageIndex + b > this.buffer.length && (b = this.buffer.length - this.pageIndex); - var a = this.textMarginLeft, c = a + (this.showLineNumbers ? 5 * (String(this.buffer.length).length + 2) : 0), n = c + (this.showLineTime ? 40 : 10), p = n + 25; + a.prototype.paint = function() { + var a = this.pageLineCount; + this.pageIndex + a > this.buffer.length && (a = this.buffer.length - this.pageIndex); + var e = this.textMarginLeft, m = e + (this.showLineNumbers ? 5 * (String(this.buffer.length).length + 2) : 0), c = m + (this.showLineTime ? 40 : 10), q = c + 25; this.context.font = this.fontSize + 'px Consolas, "Liberation Mono", Courier, monospace'; this.context.setTransform(this.ratio, 0, 0, this.ratio, 0, 0); - for (var e = this.canvas.width, q = this.lineHeight, l = 0;l < b;l++) { - var f = l * this.lineHeight, w = this.pageIndex + l, r = this.buffer.get(w), h = this.buffer.getFormat(w), x = this.buffer.getRepeat(w), y = 1 < w ? this.buffer.getTime(w) - this.buffer.getTime(0) : 0; - this.context.fillStyle = w % 2 ? this.lineColor : this.alternateLineColor; - h && h.backgroundFillStyle && (this.context.fillStyle = h.backgroundFillStyle); - this.context.fillRect(0, f, e, q); + for (var n = this.canvas.width, k = this.lineHeight, f = 0;f < a;f++) { + var d = f * this.lineHeight, b = this.pageIndex + f, g = this.buffer.get(b), r = this.buffer.getFormat(b), w = this.buffer.getRepeat(b), z = 1 < b ? this.buffer.getTime(b) - this.buffer.getTime(0) : 0; + this.context.fillStyle = b % 2 ? this.lineColor : this.alternateLineColor; + r && r.backgroundFillStyle && (this.context.fillStyle = r.backgroundFillStyle); + this.context.fillRect(0, d, n, k); this.context.fillStyle = this.selectionTextColor; this.context.fillStyle = this.textColor; - this.selection && w >= this.selection.start && w <= this.selection.end && (this.context.fillStyle = this.selectionColor, this.context.fillRect(0, f, e, q), this.context.fillStyle = this.selectionTextColor); - this.hasFocus && w === this.lineIndex && (this.context.fillStyle = this.selectionColor, this.context.fillRect(0, f, e, q), this.context.fillStyle = this.selectionTextColor); - 0 < this.columnIndex && (r = r.substring(this.columnIndex)); - f = (l + 1) * this.lineHeight - this.textMarginBottom; - this.showLineNumbers && this.context.fillText(String(w), a, f); - this.showLineTime && this.context.fillText(y.toFixed(1).padLeft(" ", 6), c, f); - 1 < x && this.context.fillText(String(x).padLeft(" ", 3), n, f); - this.context.fillText(r, p, f); - } - }; - b.prototype.refreshEvery = function(b) { - function a() { - c.paint(); - c.refreshFrequency && setTimeout(a, c.refreshFrequency); - } - var c = this; - this.refreshFrequency = b; - c.refreshFrequency && setTimeout(a, c.refreshFrequency); + this.selection && b >= this.selection.start && b <= this.selection.end && (this.context.fillStyle = this.selectionColor, this.context.fillRect(0, d, n, k), this.context.fillStyle = this.selectionTextColor); + this.hasFocus && b === this.lineIndex && (this.context.fillStyle = this.selectionColor, this.context.fillRect(0, d, n, k), this.context.fillStyle = this.selectionTextColor); + 0 < this.columnIndex && (g = g.substring(this.columnIndex)); + d = (f + 1) * this.lineHeight - this.textMarginBottom; + this.showLineNumbers && this.context.fillText(String(b), e, d); + this.showLineTime && this.context.fillText(z.toFixed(1).padLeft(" ", 6), m, d); + 1 < w && this.context.fillText(String(w).padLeft(" ", 3), c, d); + this.context.fillText(g, q, d); + } + }; + a.prototype.refreshEvery = function(a) { + function e() { + m.paint(); + m.refreshFrequency && setTimeout(e, m.refreshFrequency); + } + var m = this; + this.refreshFrequency = a; + m.refreshFrequency && setTimeout(e, m.refreshFrequency); }; - b.prototype.isScrolledToBottom = function() { + a.prototype.isScrolledToBottom = function() { return this.lineIndex === this.buffer.length - 1; }; - return b; + return a; }(); - g.Terminal = s; - })(k.Terminal || (k.Terminal = {})); - })(b.Tools || (b.Tools = {})); + a.Terminal = p; + })(h.Terminal || (h.Terminal = {})); + })(c.Tools || (c.Tools = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - var f = function() { - function b(f) { +(function(c) { + (function(c) { + (function(a) { + var c = function() { + function a(c) { this._lastWeightedTime = this._lastTime = this._index = 0; this._gradient = "#FF0000 #FF1100 #FF2300 #FF3400 #FF4600 #FF5700 #FF6900 #FF7B00 #FF8C00 #FF9E00 #FFAF00 #FFC100 #FFD300 #FFE400 #FFF600 #F7FF00 #E5FF00 #D4FF00 #C2FF00 #B0FF00 #9FFF00 #8DFF00 #7CFF00 #6AFF00 #58FF00 #47FF00 #35FF00 #24FF00 #12FF00 #00FF00".split(" "); - this._canvas = f; - this._context = f.getContext("2d"); - window.addEventListener("resize", this._resizeHandler.bind(this), !1); - this._resizeHandler(); + this._container = c; + this._canvas = document.createElement("canvas"); + this._container.appendChild(this._canvas); + this._context = this._canvas.getContext("2d"); + this._listenForContainerSizeChanges(); } - b.prototype._resizeHandler = function() { - var b = this._canvas.parentElement, f = b.clientWidth, b = b.clientHeight - 1, d = window.devicePixelRatio || 1; - 1 !== d ? (this._ratio = d / 1, this._canvas.width = f * this._ratio, this._canvas.height = b * this._ratio, this._canvas.style.width = f + "px", this._canvas.style.height = b + "px") : (this._ratio = 1, this._canvas.width = f, this._canvas.height = b); + a.prototype._listenForContainerSizeChanges = function() { + var a = this._containerWidth, c = this._containerHeight; + this._onContainerSizeChanged(); + var l = this; + setInterval(function() { + if (a !== l._containerWidth || c !== l._containerHeight) { + l._onContainerSizeChanged(), a = l._containerWidth, c = l._containerHeight; + } + }, 10); + }; + a.prototype._onContainerSizeChanged = function() { + var a = this._containerWidth, c = this._containerHeight, l = window.devicePixelRatio || 1; + 1 !== l ? (this._ratio = l / 1, this._canvas.width = a * this._ratio, this._canvas.height = c * this._ratio, this._canvas.style.width = a + "px", this._canvas.style.height = c + "px") : (this._ratio = 1, this._canvas.width = a, this._canvas.height = c); }; - b.prototype.tickAndRender = function(b) { - "undefined" === typeof b && (b = !1); + Object.defineProperty(a.prototype, "_containerWidth", {get:function() { + return this._container.clientWidth; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(a.prototype, "_containerHeight", {get:function() { + return this._container.clientHeight; + }, enumerable:!0, configurable:!0}); + a.prototype.tickAndRender = function(a, c) { + void 0 === a && (a = !1); + void 0 === c && (c = 0); if (0 === this._lastTime) { this._lastTime = performance.now(); } else { - var f = (performance.now() - this._lastTime) * (1 - .9) + .9 * this._lastWeightedTime, d = this._context, a = 2 * this._ratio, c = (this._canvas.width - 20) / (a + 1) | 0, n = this._index++; - this._index > c && (this._index = 0); - d.globalAlpha = 1; - d.fillStyle = "black"; - d.fillRect(20 + n * (a + 1), 0, 4 * a, this._canvas.height); - c = 1E3 / 60 / f; - d.fillStyle = this._gradient[c * (this._gradient.length - 1) | 0]; - d.globalAlpha = b ? .5 : 1; - d.fillRect(20 + n * (a + 1), 0, a, this._canvas.height * c | 0); - 0 === n % 16 && (d.globalAlpha = 1, d.fillStyle = "black", d.fillRect(0, 0, 20, this._canvas.height), d.fillStyle = "white", d.font = "10px Arial", d.fillText((1E3 / f).toFixed(0), 2, 8)); + var l = 1 * (performance.now() - this._lastTime) + 0 * this._lastWeightedTime, e = this._context, m = 2 * this._ratio, t = 30 * this._ratio, q = performance; + q.memory && (t += 30 * this._ratio); + var n = (this._canvas.width - t) / (m + 1) | 0, k = this._index++; + this._index > n && (this._index = 0); + n = this._canvas.height; + e.globalAlpha = 1; + e.fillStyle = "black"; + e.fillRect(t + k * (m + 1), 0, 4 * m, this._canvas.height); + var f = Math.min(1E3 / 60 / l, 1); + e.fillStyle = "#00FF00"; + e.globalAlpha = a ? .5 : 1; + f = n / 2 * f | 0; + e.fillRect(t + k * (m + 1), n - f, m, f); + c && (f = Math.min(1E3 / 240 / c, 1), e.fillStyle = "#FF6347", f = n / 2 * f | 0, e.fillRect(t + k * (m + 1), n / 2 - f, m, f)); + 0 === k % 16 && (e.globalAlpha = 1, e.fillStyle = "black", e.fillRect(0, 0, t, this._canvas.height), e.fillStyle = "white", e.font = 8 * this._ratio + "px Arial", e.textBaseline = "middle", m = (1E3 / l).toFixed(0), c && (m += " " + c.toFixed(0)), q.memory && (m += " " + (q.memory.usedJSHeapSize / 1024 / 1024).toFixed(2)), e.fillText(m, 2 * this._ratio, this._containerHeight / 2 * this._ratio)); this._lastTime = performance.now(); - this._lastWeightedTime = f; + this._lastWeightedTime = l; } }; - return b; + return a; }(); - b.FPS = f; - })(b.Mini || (b.Mini = {})); - })(b.Tools || (b.Tools = {})); + a.FPS = c; + })(c.Mini || (c.Mini = {})); + })(c.Tools || (c.Tools = {})); })(Shumway || (Shumway = {})); console.timeEnd("Load Shared Dependencies"); console.time("Load AVM2 Dependencies"); -(function(b) { - (function(k) { - k.timelineBuffer = new b.Tools.Profiler.TimelineBuffer("AVM2"); - k.counter = new b.Metrics.Counter(!0); - k.countTimeline = function(b, f) { - "undefined" === typeof f && (f = 1); - k.timelineBuffer && k.timelineBuffer.count(b, f); - }; - k.enterTimeline = function(b, f) { - }; - k.leaveTimeline = function(b) { - }; - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - function g(b) { - for (var g = 0;g < arguments.length - 1;g++) { - } - var k = b.message; - Array.prototype.slice.call(arguments, 1).forEach(function(b, d) { - k = k.replace("%" + (d + 1), b); +(function(c) { + (function(h) { + h.timelineBuffer = new c.Tools.Profiler.TimelineBuffer("AVM2"); + h.counter = new c.Metrics.Counter(!0); + h.countTimeline = function(a, c) { + void 0 === c && (c = 1); + h.timelineBuffer && h.timelineBuffer.count(a, c); + }; + h.enterTimeline = function(a, c) { + }; + h.leaveTimeline = function(a) { + }; + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + function a(a) { + for (var c = [], h = 1;h < arguments.length;h++) { + c[h - 1] = arguments[h]; + } + var u = a.message; + c.forEach(function(a, e) { + u = u.replace("%" + (e + 1), a); }); - return "Error #" + b.code + ": " + k; + return "Error #" + a.code + ": " + u; } - k.Errors = {CallOfNonFunctionError:{code:1006, message:"%1 is not a function."}, ConvertNullToObjectError:{code:1009, message:"Cannot access a property or method of a null object reference."}, ConvertUndefinedToObjectError:{code:1010, message:"A term is undefined and has no properties."}, ClassNotFoundError:{code:1014, message:"Class %1 could not be found."}, CheckTypeFailedError:{code:1034, message:"Type Coercion failed: cannot convert %1 to %2."}, WrongArgumentCountError:{code:1063, message:"Argument count mismatch on %1. Expected %2, got %3."}, - XMLMarkupMustBeWellFormed:{code:1088, message:"The markup in the document following the root element must be well-formed."}, OutOfRangeError:{code:1125, message:"The index %1 is out of range %2."}, VectorFixedError:{code:1126, message:"Cannot change the length of a fixed Vector."}, InvalidRangeError:{code:1506, message:"The specified range is invalid."}, NullArgumentError:{code:1507, message:"Argument %1 cannot be null."}, InvalidArgumentError:{code:1508, message:"The value specified for argument %1 is invalid."}, - InvalidParamError:{code:2004, message:"One of the parameters is invalid."}, ParamRangeError:{code:2006, message:"The supplied index is out of bounds."}, NullPointerError:{code:2007, message:"Parameter %1 must be non-null."}, InvalidEnumError:{code:2008, message:"Parameter %1 must be one of the accepted values."}, InvalidBitmapData:{code:2015, message:"Invalid BitmapData."}, CompressedDataError:{code:2058, message:"There was an error decompressing the data."}, TooFewArgumentsError:{code:2001, - message:"Too few arguments were specified; got %1, %2 expected."}, SocketConnectError:{code:2011, message:"Socket connection failed to %1:%2."}, CantAddSelfError:{code:2024, message:"An object cannot be added as a child of itself."}, NotAChildError:{code:2025, message:"The supplied DisplayObject must be a child of the caller."}, ExternalInterfaceNotAvailableError:{code:2067, message:"The ExternalInterface is not available in this container. ExternalInterface requires Internet Explorer ActiveX, Firefox, Mozilla 1.7.5 and greater, or other browsers that support NPRuntime."}, - InvalidStageMethodError:{code:2071, message:"The Stage class does not implement this property or method."}, SceneNotFoundError:{code:2108, message:"Scene %1 was not found."}, FrameLabelNotFoundError:{code:2109, message:"Frame label %1 not found in scene %2."}, CantAddParentError:{code:2150, message:"An object cannot be added as a child to one of it's children (or children's children, etc.)."}, ObjectWithStringsParamError:{code:2196, message:"Parameter %1 must be an Object with only String values."}}; - k.getErrorMessage = function(f) { - if (!b.AVM2.Runtime.debuggerMode.value) { - return "Error #" + f; - } - for (var g in k.Errors) { - if (k.Errors[g].code == f) { - return "Error #" + f + ": " + k.Errors[g].message; - } - } - return "Error #" + f + ": (unknown)"; - }; - k.formatErrorMessage = g; - k.translateErrorMessage = function(f) { - if (f.type) { - switch(f.type) { + h.Errors = {CallOfNonFunctionError:{code:1006, message:"%1 is not a function."}, ConvertNullToObjectError:{code:1009, message:"Cannot access a property or method of a null object reference."}, ConvertUndefinedToObjectError:{code:1010, message:"A term is undefined and has no properties."}, ClassNotFoundError:{code:1014, message:"Class %1 could not be found."}, CheckTypeFailedError:{code:1034, message:"Type Coercion failed: cannot convert %1 to %2."}, WrongArgumentCountError:{code:1063, message:"Argument count mismatch on %1. Expected %2, got %3."}, + ConstWriteError:{code:1074, message:"Illegal write to read-only property %1 on %2."}, XMLOnlyWorksWithOneItemLists:{code:1086, message:"The %1 method only works on lists containing one item."}, XMLAssignmentToIndexedXMLNotAllowed:{code:1087, message:"Assignment to indexed XML is not allowed."}, XMLMarkupMustBeWellFormed:{code:1088, message:"The markup in the document following the root element must be well-formed."}, XMLAssigmentOneItemLists:{code:1089, message:"Assignment to lists with more than one item is not supported."}, + XMLNamespaceWithPrefixAndNoURI:{code:1098, message:"Illegal prefix %1 for no namespace."}, OutOfRangeError:{code:1125, message:"The index %1 is out of range %2."}, VectorFixedError:{code:1126, message:"Cannot change the length of a fixed Vector."}, InvalidRangeError:{code:1506, message:"The specified range is invalid."}, NullArgumentError:{code:1507, message:"Argument %1 cannot be null."}, InvalidArgumentError:{code:1508, message:"The value specified for argument %1 is invalid."}, InvalidParamError:{code:2004, + message:"One of the parameters is invalid."}, ParamRangeError:{code:2006, message:"The supplied index is out of bounds."}, NullPointerError:{code:2007, message:"Parameter %1 must be non-null."}, InvalidEnumError:{code:2008, message:"Parameter %1 must be one of the accepted values."}, CantInstantiateError:{code:2012, message:"%1 class cannot be instantiated."}, InvalidBitmapData:{code:2015, message:"Invalid BitmapData."}, EOFError:{code:2030, message:"End of file was encountered.", fqn:"flash.errors.EOFError"}, + CompressedDataError:{code:2058, message:"There was an error decompressing the data.", fqn:"flash.errors.IOError"}, TooFewArgumentsError:{code:2001, message:"Too few arguments were specified; got %1, %2 expected."}, SocketConnectError:{code:2011, message:"Socket connection failed to %1:%2."}, CantAddSelfError:{code:2024, message:"An object cannot be added as a child of itself."}, NotAChildError:{code:2025, message:"The supplied DisplayObject must be a child of the caller."}, DelayRangeError:{code:2066, + message:"The Timer delay specified is out of range."}, ExternalInterfaceNotAvailableError:{code:2067, message:"The ExternalInterface is not available in this container. ExternalInterface requires Internet Explorer ActiveX, Firefox, Mozilla 1.7.5 and greater, or other browsers that support NPRuntime."}, InvalidLoaderMethodError:{code:2069, message:"The Loader class does not implement this method."}, InvalidStageMethodError:{code:2071, message:"The Stage class does not implement this property or method."}, + LoadingObjectNotSWFError:{code:2098, message:"The loading object is not a .swf file, you cannot request SWF properties from it."}, LoadingObjectNotInitializedError:{code:2099, message:"The loading object is not sufficiently loaded to provide this information."}, DecodeParamError:{code:2101, message:"The String passed to URLVariables.decode() must be a URL-encoded query string containing name/value pairs."}, SceneNotFoundError:{code:2108, message:"Scene %1 was not found."}, FrameLabelNotFoundError:{code:2109, + message:"Frame label %1 not found in scene %2."}, InvalidLoaderInfoMethodError:{code:2118, message:"The LoaderInfo class does not implement this method."}, CantAddParentError:{code:2150, message:"An object cannot be added as a child to one of it's children (or children's children, etc.)."}, ObjectWithStringsParamError:{code:2196, message:"Parameter %1 must be an Object with only String values."}}; + h.getErrorMessage = function(a) { + if (!c.AVM2.Runtime.debuggerMode.value) { + return "Error #" + a; + } + for (var v in h.Errors) { + if (h.Errors[v].code == a) { + return "Error #" + a + ": " + h.Errors[v].message; + } + } + return "Error #" + a + ": (unknown)"; + }; + h.formatErrorMessage = a; + h.translateErrorMessage = function(s) { + if (s.type) { + switch(s.type) { case "undefined_method": - return g(k.Errors.CallOfNonFunctionError, "value"); + return a(h.Errors.CallOfNonFunctionError, "value"); default: - throw b.Debug.notImplemented(f.type);; + throw c.Debug.notImplemented(s.type);; } } else { - return 0 <= f.message.indexOf("is not a function") ? g(k.Errors.CallOfNonFunctionError, "value") : f.message; + return 0 <= s.message.indexOf("is not a function") ? a(h.Errors.CallOfNonFunctionError, "value") : s.message; } }; - })(b.AVM2 || (b.AVM2 = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -var Errors = Shumway.AVM2.Errors, getErrorMessage = Shumway.AVM2.getErrorMessage, formatErrorMessage = Shumway.AVM2.formatErrorMessage, translateErrorMessage = Shumway.AVM2.translateErrorMessage; -(function(b) { - (function(k) { - (function(g) { - var f = null; - "undefined" !== typeof TextDecoder && (f = new TextDecoder); - var k = function() { - function g(b) { - this._bytes = b; - this._view = new DataView(b.buffer, b.byteOffset); +Errors = Shumway.AVM2.Errors; +(function(c) { + (function(h) { + (function(a) { + var s = null; + "undefined" !== typeof TextDecoder && (s = new TextDecoder); + var h = function() { + function a(c) { + this._bytes = c; + this._view = new DataView(c.buffer, c.byteOffset); this._position = 0; } - g._getResultBuffer = function(b) { - if (!g._resultBuffer || g._resultBuffer.length < b) { - g._resultBuffer = new Int32Array(2 * b); + a._getResultBuffer = function(c) { + if (!a._resultBuffer || a._resultBuffer.length < c) { + a._resultBuffer = new Int32Array(2 * c); } - return g._resultBuffer; + return a._resultBuffer; }; - Object.defineProperty(g.prototype, "position", {get:function() { + Object.defineProperty(a.prototype, "position", {get:function() { return this._position; }, enumerable:!0, configurable:!0}); - g.prototype.remaining = function() { + a.prototype.remaining = function() { return this._bytes.length - this._position; }; - g.prototype.seek = function(b) { - this._position = b; + a.prototype.seek = function(a) { + this._position = a; }; - g.prototype.readU8 = function() { + a.prototype.readU8 = function() { return this._bytes[this._position++]; }; - g.prototype.readU8s = function(b) { - var d = new Uint8Array(b); - d.set(this._bytes.subarray(this._position, this._position + b), 0); - this._position += b; - return d; + a.prototype.readU8s = function(a) { + var c = new Uint8Array(a); + c.set(this._bytes.subarray(this._position, this._position + a), 0); + this._position += a; + return c; }; - g.prototype.readS8 = function() { + a.prototype.readS8 = function() { return this._bytes[this._position++] << 24 >> 24; }; - g.prototype.readU32 = function() { + a.prototype.readU32 = function() { return this.readS32() >>> 0; }; - g.prototype.readU30 = function() { + a.prototype.readU30 = function() { return this.readU32(); }; - g.prototype.readU30Unsafe = function() { + a.prototype.readU30Unsafe = function() { return this.readU32(); }; - g.prototype.readS16 = function() { + a.prototype.readS16 = function() { return this.readU30Unsafe() << 16 >> 16; }; - g.prototype.readS32 = function() { - var b = this.readU8(); - b & 128 && (b = b & 127 | this.readU8() << 7, b & 16384 && (b = b & 16383 | this.readU8() << 14, b & 2097152 && (b = b & 2097151 | this.readU8() << 21, b & 268435456 && (b = b & 268435455 | this.readU8() << 28, b &= 4294967295)))); - return b; + a.prototype.readS32 = function() { + var a = this.readU8(); + a & 128 && (a = a & 127 | this.readU8() << 7, a & 16384 && (a = a & 16383 | this.readU8() << 14, a & 2097152 && (a = a & 2097151 | this.readU8() << 21, a & 268435456 && (a = a & 268435455 | this.readU8() << 28, a &= 4294967295)))); + return a; }; - g.prototype.readWord = function() { - var b = this._view.getUint32(this._position, !0); + a.prototype.readWord = function() { + var a = this._view.getUint32(this._position, !0); this._position += 4; - return b; + return a; }; - g.prototype.readS24 = function() { + a.prototype.readS24 = function() { return(this.readU8() | this.readU8() << 8 | this.readU8() << 16) << 8 >> 8; }; - g.prototype.readDouble = function() { - var b = this._view.getFloat64(this._position, !0); + a.prototype.readDouble = function() { + var a = this._view.getFloat64(this._position, !0); this._position += 8; - return b; + return a; }; - g.prototype.readUTFString = function(m) { - if (f) { - var d = this._position; - this._position += m; - return f.decode(this._bytes.subarray(d, d + m)); - } - var d = this._position, a = d + m, c = this._bytes, n = 0; - for (m = g._getResultBuffer(2 * m);d < a;) { - var p = c[d++]; - if (127 >= p) { - m[n++] = p; + a.prototype.readUTFString = function(h) { + if (s) { + var l = this._position; + this._position += h; + return s.decode(this._bytes.subarray(l, l + h)); + } + var l = this._position, e = l + h, m = this._bytes, t = 0; + for (h = a._getResultBuffer(2 * h);l < e;) { + var q = m[l++]; + if (127 >= q) { + h[t++] = q; } else { - if (192 <= p) { - var e = 0; - 224 > p ? e = (p & 31) << 6 | c[d++] & 63 : 240 > p ? e = (p & 15) << 12 | (c[d++] & 63) << 6 | c[d++] & 63 : (e = ((p & 7) << 18 | (c[d++] & 63) << 12 | (c[d++] & 63) << 6 | c[d++] & 63) - 65536, m[n++] = ((e & 1047552) >>> 10) + 55296, e = (e & 1023) + 56320); - m[n++] = e; + if (192 <= q) { + var n = 0; + 224 > q ? n = (q & 31) << 6 | m[l++] & 63 : 240 > q ? n = (q & 15) << 12 | (m[l++] & 63) << 6 | m[l++] & 63 : (n = ((q & 7) << 18 | (m[l++] & 63) << 12 | (m[l++] & 63) << 6 | m[l++] & 63) - 65536, h[t++] = ((n & 1047552) >>> 10) + 55296, n = (n & 1023) + 56320); + h[t++] = n; } } } - this._position = d; - return b.StringUtilities.fromCharCodeArray(m.subarray(0, n)); + this._position = l; + return c.StringUtilities.fromCharCodeArray(h.subarray(0, t)); }; - g._resultBuffer = new Int32Array(256); - return g; + a._resultBuffer = new Int32Array(256); + return a; }(); - g.AbcStream = k; - })(k.ABC || (k.ABC = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -__extends = this.__extends || function(b, k) { - function g() { - this.constructor = b; + a.AbcStream = h; + })(h.ABC || (h.ABC = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +__extends = this.__extends || function(c, h) { + function a() { + this.constructor = c; } - for (var f in k) { - k.hasOwnProperty(f) && (b[f] = k[f]); + for (var s in h) { + h.hasOwnProperty(s) && (c[s] = h[s]); } - g.prototype = k.prototype; - b.prototype = new g; + a.prototype = h.prototype; + c.prototype = new a; }; -(function(b) { - (function(k) { - (function(g) { - var f = b.isString, t = b.isNumeric, s = b.isObject, m = b.Debug.assert, d = b.Debug.notImplemented, a = function() { - return function(a, b, c, e) { - "undefined" === typeof e && (e = !1); - this.name = a; - this.type = b; - this.value = c; - this.optional = e; +(function(c) { + (function(h) { + (function(a) { + var s = c.isString, v = c.isNumeric, p = c.isObject, u = c.Debug.assert, l = c.Debug.notImplemented, e = function() { + return function(b, d, g, a) { + void 0 === a && (a = !1); + this.name = b; + this.type = d; + this.value = g; + this.optional = a; }; }(); - g.Parameter = a; - var c = function() { - function a(b, c, e) { - var h = b.constantPool, l = b.methods, d = b.classes, p = b.metadata; - this.holder = e; - this.name = h.multinames[c.readU30()]; - e = c.readU8(); - this.kind = e & 15; - this.attributes = e >> 4 & 15; - m(r.isQName(this.name), "Name must be a QName: " + this.name + ", kind: " + this.kind); + a.Parameter = e; + var m = function() { + function b(a, f, k) { + var r = a.constantPool, e = a.methods, n = a.classes, c = a.metadata; + this.holder = k; + this.name = r.multinames[f.readU30()]; + k = f.readU8(); + this.kind = k & 15; + this.attributes = k >> 4 & 15; + u(g.isQName(this.name), "Name must be a QName: " + this.name + ", kind: " + this.kind); switch(this.kind) { case 0: ; case 6: - this.slotId = c.readU30(); - this.typeName = h.multinames[c.readU30()]; - b = c.readU30(); + this.slotId = f.readU30(); + this.typeName = r.multinames[f.readU30()]; + a = f.readU30(); this.value = void 0; - 0 !== b && (this.hasDefaultValue = !0, this.value = h.getValue(c.readU8(), b)); + 0 !== a && (this.hasDefaultValue = !0, this.value = r.getValue(f.readU8(), a)); break; case 1: ; case 3: ; case 2: - this.dispId = c.readU30(); - this.methodInfo = l[c.readU30()]; + this.dispId = f.readU30(); + this.methodInfo = e[f.readU30()]; this.methodInfo.name = this.name; - u.attachHolder(this.methodInfo, this.holder); - this.methodInfo.abc = b; + d.attachHolder(this.methodInfo, this.holder); + this.methodInfo.abc = a; break; case 4: - this.slotId = c.readU30(); - m(d, "Classes should be passed down here, I'm guessing whenever classes are being parsed."); - this.classInfo = d[c.readU30()]; + this.slotId = f.readU30(); + u(n, "Classes should be passed down here, I'm guessing whenever classes are being parsed."); + this.classInfo = n[f.readU30()]; break; case 5: - m(!1, "Function encountered in the wild, should not happen"); + u(!1, "Function encountered in the wild, should not happen"); + break; + default: + u(!1, "Unknown trait kind: " + w[this.kind]); } if (this.attributes & 4) { - var q, h = 0; - for (b = c.readU30();h < b;h++) { - l = p[c.readU30()], "__go_to_definition_help" !== l.name && "__go_to_ctor_definition_help" !== l.name && (q || (q = {}), q[l.name] = l); + var m, r = 0; + for (a = f.readU30();r < a;r++) { + e = c[f.readU30()], "__go_to_definition_help" !== e.name && "__go_to_ctor_definition_help" !== e.name && (m || (m = {}), m[e.name] = e); } - q && (this.isClass() && (this.classInfo.metadata = q), this.metadata = q); + m && (this.isClass() && (this.classInfo.metadata = m), this.metadata = m); } } - a.prototype.isSlot = function() { + b.prototype.isSlot = function() { return 0 === this.kind; }; - a.prototype.isConst = function() { + b.prototype.isConst = function() { return 6 === this.kind; }; - a.prototype.isMethod = function() { + b.prototype.isMethod = function() { return 1 === this.kind; }; - a.prototype.isClass = function() { + b.prototype.isClass = function() { return 4 === this.kind; }; - a.prototype.isGetter = function() { + b.prototype.isGetter = function() { return 2 === this.kind; }; - a.prototype.isSetter = function() { + b.prototype.isSetter = function() { return 3 === this.kind; }; - a.prototype.isAccessor = function() { + b.prototype.isAccessor = function() { return this.isGetter() || this.isSetter(); }; - a.prototype.isMethodOrAccessor = function() { + b.prototype.isMethodOrAccessor = function() { return this.isMethod() || this.isGetter() || this.isSetter(); }; - a.prototype.isProtected = function() { - m(r.isQName(this.name)); + b.prototype.isProtected = function() { + u(g.isQName(this.name)); return this.name.namespaces[0].isProtected(); }; - a.prototype.kindName = function() { + b.prototype.kindName = function() { switch(this.kind) { case 0: return "Slot"; @@ -6198,87 +6543,87 @@ case 5: return "Function"; } - b.Debug.unexpected(); + c.Debug.unexpected(); }; - a.prototype.isOverride = function() { + b.prototype.isOverride = function() { return this.attributes & 2; }; - a.prototype.isFinal = function() { + b.prototype.isFinal = function() { return this.attributes & 1; }; - a.prototype.toString = function() { - var a = b.IntegerUtilities.getFlags(this.attributes, ["final", "override", "metadata"]); - a && (a += " "); - a += r.getQualifiedName(this.name); + b.prototype.toString = function() { + var b = c.IntegerUtilities.getFlags(this.attributes, ["final", "override", "metadata"]); + b && (b += " "); + b += g.getQualifiedName(this.name); switch(this.kind) { case 0: ; case 6: - return a + ", typeName: " + this.typeName + ", slotId: " + this.slotId + ", value: " + this.value; + return b + ", typeName: " + this.typeName + ", slotId: " + this.slotId + ", value: " + this.value; case 1: ; case 3: ; case 2: - return a + ", " + this.kindName() + ": " + this.methodInfo.name; + return b + ", " + this.kindName() + ": " + this.methodInfo.name; case 4: - return a + ", slotId: " + this.slotId + ", class: " + this.classInfo; + return b + ", slotId: " + this.slotId + ", class: " + this.classInfo; } }; - a.parseTraits = function(b, c, e) { - for (var h = c.readU30(), l = [], d = 0;d < h;d++) { - l.push(new a(b, c, e)); + b.parseTraits = function(d, a, g) { + for (var f = a.readU30(), k = [], r = 0;r < f;r++) { + k.push(new b(d, a, g)); } - return l; + return k; }; - return a; + return b; }(); - g.Trait = c; - var n = function() { - return function(a, b, c) { - this.abc = a; - this.index = b; - this.hash = a.hash & 65535 | c | b << 19; + a.Trait = m; + var t = function() { + return function(b, d, a) { + this.abc = b; + this.index = d; + this.hash = b.hash & 65535 | a | d << 19; }; }(); - g.Info = n; - var p = function(h) { - function d(b, c, e) { - h.call(this, b, c, 131072); - b = b.constantPool; - c = e.readU30(); - this.returnType = b.multinames[e.readU30()]; + a.Info = t; + var q = function(b) { + function d(a, g, f) { + b.call(this, a, g, 131072); + a = a.constantPool; + g = f.readU30(); + this.returnType = a.multinames[f.readU30()]; this.parameters = []; - for (var l = 0;l < c;l++) { - this.parameters.push(new a(void 0, b.multinames[e.readU30()], void 0)); + for (var k = 0;k < g;k++) { + this.parameters.push(new e(void 0, a.multinames[f.readU30()], void 0)); } - this.debugName = b.strings[e.readU30()]; - this.flags = e.readU8(); - l = 0; + this.debugName = a.strings[f.readU30()]; + this.flags = f.readU8(); + k = 0; if (this.flags & 8) { - for (l = e.readU30(), m(c >= l), l = c - l;l < c;l++) { - var p = e.readU30(); - this.parameters[l].value = b.getValue(e.readU8(), p); - this.parameters[l].optional = !0; + for (k = f.readU30(), u(g >= k), k = g - k;k < g;k++) { + var r = f.readU30(); + this.parameters[k].value = a.getValue(f.readU8(), r); + this.parameters[k].optional = !0; } } if (this.flags & 128) { - for (l = 0;l < c;l++) { - d.parseParameterNames ? this.parameters[l].name = b.strings[e.readU30()] : (e.readU30(), this.parameters[l].name = d._getParameterName(l)); + for (k = 0;k < g;k++) { + d.parseParameterNames ? this.parameters[k].name = a.strings[f.readU30()] : (f.readU30(), this.parameters[k].name = d._getParameterName(k)); } } else { - for (l = 0;l < c;l++) { - this.parameters[l].name = d._getParameterName(l); + for (k = 0;k < g;k++) { + this.parameters[k].name = d._getParameterName(k); } } } - __extends(d, h); - d._getParameterName = function(a) { - return 26 > a ? String.fromCharCode(65 + a) : "P" + (a - 26); + __extends(d, b); + d._getParameterName = function(b) { + return 26 > b ? String.fromCharCode(65 + b) : "P" + (b - 26); }; d.prototype.toString = function() { - var a = b.IntegerUtilities.getFlags(this.flags, "NEED_ARGUMENTS NEED_ACTIVATION NEED_REST HAS_OPTIONAL SET_DXN HAS_PARAM_NAMES".split(" ")); - return(a ? a + " " : "") + this.name; + var b = c.IntegerUtilities.getFlags(this.flags, "NEED_ARGUMENTS NEED_ACTIVATION NEED_REST HAS_OPTIONAL SET_DXN HAS_PARAM_NAMES".split(" ")); + return(b ? b + " " : "") + this.name; }; d.prototype.hasOptional = function() { return!!(this.flags & 8); @@ -6296,441 +6641,440 @@ return!!(this.flags & 32); }; d.prototype.isClassMember = function() { - return this.holder instanceof q; + return this.holder instanceof k; }; d.prototype.isInstanceMember = function() { - return this.holder instanceof e; + return this.holder instanceof n; }; d.prototype.isScriptMember = function() { - return this.holder instanceof l; + return this.holder instanceof f; }; d.prototype.hasSetsDxns = function() { return!!(this.flags & 64); }; - d.parseException = function(a, b) { - var c = a.constantPool.multinames, c = {start:b.readU30(), end:b.readU30(), target:b.readU30(), typeName:c[b.readU30()], varName:c[b.readU30()]}; - m(!c.typeName || !c.typeName.isRuntime()); - m(!c.varName || c.varName.isQName()); - return c; + d.parseException = function(b, d) { + var a = b.constantPool.multinames, a = {start:d.readU30(), end:d.readU30(), target:d.readU30(), typeName:a[d.readU30()], varName:a[d.readU30()]}; + u(!a.typeName || !a.typeName.isRuntime()); + u(!a.varName || a.varName.isQName()); + return a; }; - d.parseBody = function(a, b) { - var e = a.methods, h = b.readU30(), e = e[h]; - e.index = h; - e.hasBody = !0; - m(!e.isNative()); - e.maxStack = b.readU30(); - e.localCount = b.readU30(); - e.initScopeDepth = b.readU30(); - e.maxScopeDepth = b.readU30(); - e.code = b.readU8s(b.readU30()); - for (var h = [], l = b.readU30(), p = 0;p < l;++p) { - h.push(d.parseException(a, b)); + d.parseBody = function(b, a) { + var g = b.methods, f = a.readU30(), g = g[f]; + g.index = f; + g.hasBody = !0; + u(!g.isNative()); + g.maxStack = a.readU30(); + g.localCount = a.readU30(); + g.initScopeDepth = a.readU30(); + g.maxScopeDepth = a.readU30(); + g.code = a.readU8s(a.readU30()); + for (var f = [], k = a.readU30(), r = 0;r < k;++r) { + f.push(d.parseException(b, a)); } - e.exceptions = h; - e.traits = c.parseTraits(a, b, e); + g.exceptions = f; + g.traits = m.parseTraits(b, a, g); }; d.prototype.hasExceptions = function() { return 0 < this.exceptions.length; }; d.parseParameterNames = !1; return d; - }(n); - g.MethodInfo = p; - var e = function(a) { - function e(b, h, l) { - a.call(this, b, h, 65536); - this.runtimeId = e.nextID++; - h = b.constantPool; - var d = b.methods; - this.name = h.multinames[l.readU30()]; - m(r.isQName(this.name)); - this.superName = h.multinames[l.readU30()]; - this.flags = l.readU8(); + }(t); + a.MethodInfo = q; + var n = function(b) { + function a(f, k, r) { + b.call(this, f, k, 65536); + this.runtimeId = a.nextID++; + k = f.constantPool; + var e = f.methods; + this.name = k.multinames[r.readU30()]; + u(g.isQName(this.name)); + this.superName = k.multinames[r.readU30()]; + this.flags = r.readU8(); this.protectedNs = void 0; - this.flags & 8 && (this.protectedNs = h.namespaces[l.readU30()]); - var p = l.readU30(); + this.flags & 8 && (this.protectedNs = k.namespaces[r.readU30()]); + var n = r.readU30(); this.interfaces = []; - for (var q = 0;q < p;q++) { - this.interfaces[q] = h.multinames[l.readU30()]; + for (var w = 0;w < n;w++) { + this.interfaces[w] = k.multinames[r.readU30()]; } - this.init = d[l.readU30()]; + this.init = e[r.readU30()]; this.init.isInstanceInitializer = !0; this.init.name = this.name; - u.attachHolder(this.init, this); - this.traits = c.parseTraits(b, l, this); + d.attachHolder(this.init, this); + this.traits = m.parseTraits(f, r, this); } - __extends(e, a); - e.prototype.toString = function() { - var a = b.IntegerUtilities.getFlags(this.flags & 8, ["sealed", "final", "interface", "protected"]), a = (a ? a + " " : "") + this.name; - this.superName && (a += " extends " + this.superName); - return a; + __extends(a, b); + a.prototype.toString = function() { + var b = c.IntegerUtilities.getFlags(this.flags & 8, ["sealed", "final", "interface", "protected"]), b = (b ? b + " " : "") + this.name; + this.superName && (b += " extends " + this.superName); + return b; }; - e.prototype.isFinal = function() { + a.prototype.isFinal = function() { return!!(this.flags & 2); }; - e.prototype.isSealed = function() { + a.prototype.isSealed = function() { return!!(this.flags & 1); }; - e.prototype.isInterface = function() { + a.prototype.isInterface = function() { return!!(this.flags & 4); }; - e.nextID = 1; - return e; - }(n); - g.InstanceInfo = e; - (function(a) { - a[a.AbcMask = 65535] = "AbcMask"; - a[a.KindMask = 458752] = "KindMask"; - a[a.ClassInfo = 0] = "ClassInfo"; - a[a.InstanceInfo = 65536] = "InstanceInfo"; - a[a.MethodInfo = 131072] = "MethodInfo"; - a[a.ScriptInfo = 196608] = "ScriptInfo"; - a[a.NamespaceSet = 262144] = "NamespaceSet"; - a[a.IndexOffset = 19] = "IndexOffset"; - })(g.Hashes || (g.Hashes = {})); - var q = function(a) { - function b(e, h, l) { - a.call(this, e, h, 0); - this.runtimeId = b.nextID++; - this.init = e.methods[l.readU30()]; + a.nextID = 1; + return a; + }(t); + a.InstanceInfo = n; + (function(b) { + b[b.AbcMask = 65535] = "AbcMask"; + b[b.KindMask = 458752] = "KindMask"; + b[b.ClassInfo = 0] = "ClassInfo"; + b[b.InstanceInfo = 65536] = "InstanceInfo"; + b[b.MethodInfo = 131072] = "MethodInfo"; + b[b.ScriptInfo = 196608] = "ScriptInfo"; + b[b.NamespaceSet = 262144] = "NamespaceSet"; + b[b.IndexOffset = 19] = "IndexOffset"; + })(a.Hashes || (a.Hashes = {})); + var k = function(b) { + function a(g, f, k) { + b.call(this, g, f, 0); + this.runtimeId = a.nextID++; + this.init = g.methods[k.readU30()]; this.init.isClassInitializer = !0; - u.attachHolder(this.init, this); - this.traits = c.parseTraits(e, l, this); - this.instanceInfo = e.instances[h]; + d.attachHolder(this.init, this); + this.traits = m.parseTraits(g, k, this); + this.instanceInfo = g.instances[f]; this.instanceInfo.classInfo = this; - this.defaultValue = b._getDefaultValue(this.instanceInfo.name); + this.defaultValue = a._getDefaultValue(this.instanceInfo.name); } - __extends(b, a); - b._getDefaultValue = function(a) { - return r.getQualifiedName(a) === r.Int || r.getQualifiedName(a) === r.Uint ? 0 : r.getQualifiedName(a) === r.Number ? NaN : r.getQualifiedName(a) === r.Boolean ? !1 : null; + __extends(a, b); + a._getDefaultValue = function(b) { + return g.getQualifiedName(b) === g.Int || g.getQualifiedName(b) === g.Uint ? 0 : g.getQualifiedName(b) === g.Number ? NaN : g.getQualifiedName(b) === g.Boolean ? !1 : null; }; - b.prototype.toString = function() { + a.prototype.toString = function() { return this.instanceInfo.name.toString(); }; - b.nextID = 1; - return b; - }(n); - g.ClassInfo = q; - var l = function(a) { - function b(e, h, l) { - a.call(this, e, h, 196608); - this.runtimeId = q.nextID++; - this.name = e.name + "$script" + h; - this.init = e.methods[l.readU30()]; + a.nextID = 1; + return a; + }(t); + a.ClassInfo = k; + var f = function(b) { + function a(g, f, r) { + b.call(this, g, f, 196608); + this.runtimeId = k.nextID++; + this.name = g.name + "$script" + f; + this.init = g.methods[r.readU30()]; this.init.isScriptInitializer = !0; - u.attachHolder(this.init, this); - this.traits = c.parseTraits(e, l, this); + d.attachHolder(this.init, this); + this.traits = m.parseTraits(g, r, this); } - __extends(b, a); - Object.defineProperty(b.prototype, "entryPoint", {get:function() { + __extends(a, b); + Object.defineProperty(a.prototype, "entryPoint", {get:function() { return this.init; }, enumerable:!0, configurable:!0}); - b.prototype.toString = function() { + a.prototype.toString = function() { return this.name; }; - b.nextID = 1; - return b; - }(n); - g.ScriptInfo = l; - var u = function() { - function a(c, d, r) { - "undefined" === typeof r && (r = 0); - k.enterTimeline("Parse ABC"); - this.name = d; + a.nextID = 1; + return a; + }(t); + a.ScriptInfo = f; + var d = function() { + function b(d, g, e) { + void 0 === e && (e = 0); + h.enterTimeline("Parse ABC"); + this.name = g; this.env = {}; - k.enterTimeline("Adler"); - d = b.HashUtilities.hashBytesTo32BitsAdler(c, 0, c.length); - k.leaveTimeline(); - r ? (this.hash = r, m(r === d)) : this.hash = d; - d = new g.AbcStream(c); - a._checkMagic(d); - k.enterTimeline("Parse constantPool"); - this.constantPool = new x(d, this); - k.leaveTimeline(); - k.enterTimeline("Parse Method Infos"); + h.enterTimeline("Adler"); + g = c.HashUtilities.hashBytesTo32BitsAdler(d, 0, d.length); + h.leaveTimeline(); + e ? (this.hash = e, u(e === g)) : this.hash = g; + g = new a.AbcStream(d); + b._checkMagic(g); + h.enterTimeline("Parse constantPool"); + this.constantPool = new z(g, this); + h.leaveTimeline(); + h.enterTimeline("Parse Method Infos"); this.methods = []; - c = d.readU30(); - for (r = 0;r < c;++r) { - this.methods.push(new p(this, r, d)); + d = g.readU30(); + for (e = 0;e < d;++e) { + this.methods.push(new q(this, e, g)); } - k.leaveTimeline(); - k.enterTimeline("Parse MetaData Infos"); + h.leaveTimeline(); + h.enterTimeline("Parse MetaData Infos"); this.metadata = []; - c = d.readU30(); - for (r = 0;r < c;++r) { - this.metadata.push(new h(this, d)); + d = g.readU30(); + for (e = 0;e < d;++e) { + this.metadata.push(new r(this, g)); } - k.leaveTimeline(); - k.enterTimeline("Parse Instance Infos"); + h.leaveTimeline(); + h.enterTimeline("Parse Instance Infos"); this.instances = []; - c = d.readU30(); - for (r = 0;r < c;++r) { - this.instances.push(new e(this, r, d)); + d = g.readU30(); + for (e = 0;e < d;++e) { + this.instances.push(new n(this, e, g)); } - k.leaveTimeline(); - k.enterTimeline("Parse Class Infos"); + h.leaveTimeline(); + h.enterTimeline("Parse Class Infos"); this.classes = []; - for (r = 0;r < c;++r) { - this.classes.push(new q(this, r, d)); + for (e = 0;e < d;++e) { + this.classes.push(new k(this, e, g)); } - k.leaveTimeline(); - k.enterTimeline("Parse Script Infos"); + h.leaveTimeline(); + h.enterTimeline("Parse Script Infos"); this.scripts = []; - c = d.readU30(); - for (r = 0;r < c;++r) { - this.scripts.push(new l(this, r, d)); - } - k.leaveTimeline(); - k.enterTimeline("Parse Method Body Info"); - c = d.readU30(); - for (r = 0;r < c;++r) { - p.parseBody(this, d); - } - k.leaveTimeline(); - k.leaveTimeline(); - } - a._checkMagic = function(a) { - a = a.readWord(); - if (3014671 > a) { - throw Error("Invalid ABC File (magic = " + Number(a).toString(16) + ")"); + d = g.readU30(); + for (e = 0;e < d;++e) { + this.scripts.push(new f(this, e, g)); + } + h.leaveTimeline(); + h.enterTimeline("Parse Method Body Info"); + d = g.readU30(); + for (e = 0;e < d;++e) { + q.parseBody(this, g); + } + h.leaveTimeline(); + h.leaveTimeline(); + } + b._checkMagic = function(b) { + b = b.readWord(); + if (3014671 > b) { + throw Error("Invalid ABC File (magic = " + Number(b).toString(16) + ")"); } }; - Object.defineProperty(a.prototype, "lastScript", {get:function() { - m(0 < this.scripts.length); + Object.defineProperty(b.prototype, "lastScript", {get:function() { + u(0 < this.scripts.length); return this.scripts[this.scripts.length - 1]; }, enumerable:!0, configurable:!0}); - a.attachHolder = function(a, b) { - m(!a.holder); - a.holder = b; + b.attachHolder = function(b, d) { + u(!b.holder); + b.holder = d; }; - a.prototype.toString = function() { + b.prototype.toString = function() { return this.name; }; - a.prototype.getConstant = function(a) { - m((this.hash & 65535) === (a & 65535)); - var b = a >> 19; - switch(a & 458752) { + b.prototype.getConstant = function(b) { + u((this.hash & 65535) === (b & 65535)); + var d = b >> 19; + switch(b & 458752) { case 0: - return this.classes[b]; + return this.classes[d]; case 65536: - return this.instances[b]; + return this.instances[d]; case 131072: - return this.methods[b]; + return this.methods[d]; case 196608: - return this.scripts[b]; + return this.scripts[d]; case 262144: - return this.constantPool.namespaceSets[b]; + return this.constantPool.namespaceSets[d]; default: - d("Kind"); + l("Kind"); } }; - return a; + return b; }(); - g.AbcFile = u; - var w = function() { - function a(b, c, e, h) { - "undefined" === typeof c && (c = ""); - void 0 === c && (c = ""); - this.kind = b; - this.uri = c; - this.prefix = e; + a.AbcFile = d; + var b = function() { + function b(d, a, g, f) { + void 0 === a && (a = ""); + void 0 === a && (a = ""); + this.kind = d; + this.uri = a; + this.prefix = g; this.qualifiedName = void 0; - this._buildNamespace(h); + this._buildNamespace(f); } - a.prototype._buildNamespace = function(b) { + b.prototype._buildNamespace = function(d) { 22 === this.kind && (this.kind = 8); - this.isPublic() && this.uri ? (b = this.uri.length - 1, this.uri.charCodeAt(b) > a._MIN_API_MARK && (m(!1, "What's this code for?"), this.uri = this.uri.substring(0, b - 1))) : this.isUnique() && (m(void 0 !== b), this.uri = "private " + b); + this.isPublic() && this.uri ? (d = this.uri.length - 1, this.uri.charCodeAt(d) > b._MIN_API_MARK && (u(!1, "What's this code for?"), this.uri = this.uri.substring(0, d - 1))) : this.isUnique() && (u(void 0 !== d), this.uri = "private " + d); 26 === this.kind && (this.uri = "*"); - this.qualifiedName = a._qualifyNamespace(this.kind, this.uri, this.prefix ? this.prefix : ""); + this.qualifiedName = b._qualifyNamespace(this.kind, this.uri, this.prefix ? this.prefix : ""); }; - a._hashNamespace = function(c, e, h) { - var l = new Int32Array(1 + e.length + h.length), d = 0; - l[d++] = c; - var p = a._knownURIs.indexOf(e); - if (0 <= p) { - return c << 2 | p; - } - for (c = 0;c < e.length;c++) { - l[d++] = e.charCodeAt(c); + b._hashNamespace = function(d, a, g) { + var f = b._knownURIs.indexOf(a); + if (0 <= f) { + return d << 2 | f; + } + var f = new Int32Array(1 + a.length + g.length), k = 0; + f[k++] = d; + for (d = 0;d < a.length;d++) { + f[k++] = a.charCodeAt(d); + } + for (d = 0;d < g.length;d++) { + f[k++] = g.charCodeAt(d); + } + return c.HashUtilities.hashBytesTo32BitsMD5(f, 0, k); + }; + b._qualifyNamespace = function(d, a, g) { + var f = d + a, k = b._mangledNamespaceCache[f]; + if (k) { + return k; } - for (c = 0;c < h.length;c++) { - l[d++] = h.charCodeAt(c); + k = c.StringUtilities.variableLengthEncodeInt32(b._hashNamespace(d, a, g)); + b._mangledNamespaceMap[k] = {kind:d, uri:a, prefix:g}; + return b._mangledNamespaceCache[f] = k; + }; + b.fromQualifiedName = function(d) { + var a = c.StringUtilities.fromEncoding(d[0]); + d = b._mangledNamespaceMap[d.substring(0, a + 1)]; + return new b(d.kind, d.uri, d.prefix); + }; + b.kindFromString = function(d) { + for (var a in b._kinds) { + if (b._kinds[a] === d) { + return a; + } } - return b.HashUtilities.hashBytesTo32BitsMD5(l, 0, d); + u(!1, "Cannot find kind " + d); + return NaN; }; - a._qualifyNamespace = function(c, e, h) { - var l = c + e, d = a._mangledNamespaceCache[l]; - if (d) { - return d; - } - d = b.StringUtilities.variableLengthEncodeInt32(a._hashNamespace(c, e, h)); - a._mangledNamespaceMap[d] = {kind:c, uri:e, prefix:h}; - return a._mangledNamespaceCache[l] = d; - }; - a.fromQualifiedName = function(c) { - var e = b.StringUtilities.fromEncoding(c[0]); - c = a._mangledNamespaceMap[c.substring(0, e + 1)]; - return new a(c.kind, c.uri, c.prefix); - }; - a.kindFromString = function(b) { - for (var c in a._kinds) { - if (a._kinds[c] === b) { - return c; - } - } - m(!1, "Cannot find kind " + b); - return NaN; + b.createNamespace = function(d, a) { + void 0 === a && (a = void 0); + return new b(8, d, a); + }; + b.parse = function(d, a, g) { + var f = a.readU8(); + d = d.strings[a.readU30()]; + return new b(f, d, void 0, g); }; - a.createNamespace = function(b, c) { - "undefined" === typeof c && (c = void 0); - return new a(8, b, c); - }; - a.parse = function(b, c, e) { - var h = c.readU8(); - b = b.strings[c.readU30()]; - return new a(h, b, void 0, e); - }; - a.prototype.isPublic = function() { + b.prototype.isPublic = function() { return 8 === this.kind || 22 === this.kind; }; - a.prototype.isProtected = function() { + b.prototype.isProtected = function() { return 24 === this.kind || 26 === this.kind; }; - a.prototype.isPrivate = function() { + b.prototype.isPrivate = function() { return 5 === this.kind; }; - a.prototype.isPackageInternal = function() { + b.prototype.isPackageInternal = function() { return 23 === this.kind; }; - a.prototype.isUnique = function() { + b.prototype.isUnique = function() { return 5 === this.kind && !this.uri; }; - a.prototype.isDynamic = function() { + b.prototype.isDynamic = function() { return this.isPublic() && !this.uri; }; - a.prototype.getURI = function() { + b.prototype.getURI = function() { return this.uri; }; - a.prototype.toString = function() { - return a._kinds[this.kind] + (this.uri ? " " + this.uri : ""); + b.prototype.toString = function() { + return b._kinds[this.kind] + (this.uri ? " " + this.uri : ""); }; - a.prototype.clone = function() { - var b = Object.create(a.prototype); - b.kind = this.kind; - b.uri = this.uri; - b.prefix = this.prefix; - b.qualifiedName = this.qualifiedName; - return b; + b.prototype.clone = function() { + var d = Object.create(b.prototype); + d.kind = this.kind; + d.uri = this.uri; + d.prefix = this.prefix; + d.qualifiedName = this.qualifiedName; + return d; }; - a.prototype.isEqualTo = function(a) { - return this.qualifiedName === a.qualifiedName; + b.prototype.isEqualTo = function(b) { + return this.qualifiedName === b.qualifiedName; }; - a.prototype.inNamespaceSet = function(a) { - for (var b = 0;b < a.length;b++) { - if (a[b].qualifiedName === this.qualifiedName) { + b.prototype.inNamespaceSet = function(b) { + for (var d = 0;d < b.length;d++) { + if (b[d].qualifiedName === this.qualifiedName) { return!0; } } return!1; }; - a.prototype.getAccessModifier = function() { - return a._kinds[this.kind]; + b.prototype.getAccessModifier = function() { + return b._kinds[this.kind]; }; - a.prototype.getQualifiedName = function() { + b.prototype.getQualifiedName = function() { return this.qualifiedName; }; - a.fromSimpleName = function(b) { - if (b in a._simpleNameCache) { - return a._simpleNameCache[b]; - } - var c; - 0 === b.indexOf("[") ? (m("]" === b[b.length - 1]), c = b.substring(1, b.length - 1).split(",")) : c = [b]; - return a._simpleNameCache[b] = c.map(function(b) { - b = b.trim(); - var c; - 0 < b.indexOf(" ") ? (c = b.substring(0, b.indexOf(" ")).trim(), b = b.substring(b.indexOf(" ") + 1).trim()) : (c = a._kinds, b === c[8] || b === c[23] || b === c[5] || b === c[24] || b === c[25] || b === c[26] ? (c = b, b = "") : c = a._publicPrefix); - return new a(a.kindFromString(c), b); + b.fromSimpleName = function(d) { + if (d in b._simpleNameCache) { + return b._simpleNameCache[d]; + } + var a; + 0 === d.indexOf("[") ? (u("]" === d[d.length - 1]), a = d.substring(1, d.length - 1).split(",")) : a = [d]; + return b._simpleNameCache[d] = a.map(function(d) { + d = d.trim(); + var a; + 0 < d.indexOf(" ") ? (a = d.substring(0, d.indexOf(" ")).trim(), d = d.substring(d.indexOf(" ") + 1).trim()) : (a = b._kinds, d === a[8] || d === a[23] || d === a[5] || d === a[24] || d === a[25] || d === a[26] ? (a = d, d = "") : a = b._publicPrefix); + return new b(b.kindFromString(a), d); }); }; - a._publicPrefix = "public"; - a._kinds = function() { - var c = b.ObjectUtilities.createMap(); - c[8] = a._publicPrefix; - c[23] = "packageInternal"; - c[5] = "private"; - c[24] = "protected"; - c[25] = "explicit"; - c[26] = "staticProtected"; - return c; + b._publicPrefix = "public"; + b._kinds = function() { + var d = c.ObjectUtilities.createMap(); + d[8] = b._publicPrefix; + d[23] = "packageInternal"; + d[5] = "private"; + d[24] = "protected"; + d[25] = "explicit"; + d[26] = "staticProtected"; + return d; }(); - a._MIN_API_MARK = 58004; - a._MAX_API_MARK = 63743; - a._knownURIs = [""]; - a._mangledNamespaceCache = b.ObjectUtilities.createMap(); - a._mangledNamespaceMap = b.ObjectUtilities.createMap(); - a.PUBLIC = new a(8); - a.PROTECTED = new a(24); - a.PROXY = new a(8, "http://www.adobe.com/2006/actionscript/flash/proxy"); - a.VECTOR = new a(8, "__AS3__.vec"); - a.VECTOR_PACKAGE = new a(23, "__AS3__.vec"); - a.BUILTIN = new a(5, "builtin.as$0"); - a._simpleNameCache = b.ObjectUtilities.createMap(); - return a; + b._MIN_API_MARK = 58004; + b._MAX_API_MARK = 63743; + b._knownURIs = [""]; + b._mangledNamespaceCache = c.ObjectUtilities.createMap(); + b._mangledNamespaceMap = c.ObjectUtilities.createMap(); + b.PUBLIC = new b(8); + b.PROTECTED = new b(24); + b.PROXY = new b(8, "http://www.adobe.com/2006/actionscript/flash/proxy"); + b.VECTOR = new b(8, "__AS3__.vec"); + b.VECTOR_PACKAGE = new b(23, "__AS3__.vec"); + b.BUILTIN = new b(5, "builtin.as$0"); + b._simpleNameCache = c.ObjectUtilities.createMap(); + return b; }(); - g.Namespace = w; - w.prototype = Object.create(w.prototype); - var r = function() { - function a(b, c, e) { - "undefined" === typeof e && (e = 0); - void 0 !== c && m(null === c || f(c), "Multiname name must be a string. " + c); - this.runtimeId = a._nextID++; + a.Namespace = b; + b.prototype = Object.create(b.prototype); + var g = function() { + function d(b, a, g) { + void 0 === a || u(null === a || s(a), "Multiname name must be a string. " + a); + this.runtimeId = d._nextID++; this.namespaces = b; - this.name = c; - this.flags = e; + this.name = a; + this.flags = g | 0; } - a.parse = function(c, e, h, l, d) { - var p = 0, r = e.readU8(), q, n = [], x = 0; - switch(r) { + d.parse = function(b, a, g, f, k) { + var r = 0, e = a.readU8(), n, w = [], m = 0; + switch(e) { case 7: ; case 13: - (p = e.readU30()) ? n = [c.namespaces[p]] : x &= ~a.RUNTIME_NAME; - (p = e.readU30()) && (q = c.strings[p]); + (r = a.readU30()) ? w = [b.namespaces[r]] : m &= ~d.RUNTIME_NAME; + (r = a.readU30()) && (n = b.strings[r]); break; case 15: ; case 16: - (p = e.readU30()) ? q = c.strings[p] : x &= ~a.RUNTIME_NAME; - x |= a.RUNTIME_NAMESPACE; + (r = a.readU30()) ? n = b.strings[r] : m &= ~d.RUNTIME_NAME; + m |= d.RUNTIME_NAMESPACE; break; case 17: ; case 18: - x |= a.RUNTIME_NAMESPACE; - x |= a.RUNTIME_NAME; + m |= d.RUNTIME_NAMESPACE; + m |= d.RUNTIME_NAME; break; case 9: ; case 14: - (p = e.readU30()) ? q = c.strings[p] : x &= ~a.RUNTIME_NAME; - p = e.readU30(); - m(0 !== p); - n = c.namespaceSets[p]; + (r = a.readU30()) ? n = b.strings[r] : m &= ~d.RUNTIME_NAME; + r = a.readU30(); + u(0 !== r); + w = b.namespaceSets[r]; break; case 27: ; case 28: - x |= a.RUNTIME_NAME; - p = e.readU30(); - m(0 !== p); - n = c.namespaceSets[p]; + m |= d.RUNTIME_NAME; + r = a.readU30(); + u(0 !== r); + w = b.namespaceSets[r]; break; case 29: - return c = e.readU32(), p = e.readU32(), m(1 === p), e = e.readU32(), p = void 0, h[c] && h[e] ? (p = new a(h[c].namespaces, h[c].name, x), p.typeParameter = h[e]) : l.push({index:d, factoryTypeIndex:c, typeParameterIndex:e, flags:x}), p; + return b = a.readU32(), r = a.readU32(), u(1 === r), a = a.readU32(), r = void 0, g[b] && g[a] ? (r = new d(g[b].namespaces, g[b].name, m), r.typeParameter = g[a]) : f.push({index:k, factoryTypeIndex:b, typeParameterIndex:a, flags:m}), r; default: - b.Debug.unexpected(); + c.Debug.unexpected(); } - switch(r) { + switch(e) { case 13: ; case 16: @@ -6740,389 +7084,390 @@ case 14: ; case 28: - x |= a.ATTRIBUTE; + m |= d.ATTRIBUTE; } - return new a(n, q, x); + return new d(w, n, m); }; - a.isMultiname = function(b) { - return "number" === typeof b || "string" === typeof b || b instanceof a || b instanceof Number; + d.isMultiname = function(b) { + return "number" === typeof b || "string" === typeof b || b instanceof d || b instanceof Number; }; - a.needsResolution = function(b) { - return b instanceof a && 1 < b.namespaces.length; + d.needsResolution = function(b) { + return b instanceof d && 1 < b.namespaces.length; }; - a.isQName = function(b) { - return b instanceof a ? b.namespaces && 1 === b.namespaces.length : !0; + d.isQName = function(b) { + return b instanceof d ? b.namespaces && 1 === b.namespaces.length : !0; }; - a.isRuntimeName = function(b) { - return b instanceof a && b.isRuntimeName(); + d.isRuntimeName = function(b) { + return b instanceof d && b.isRuntimeName(); }; - a.isRuntimeNamespace = function(b) { - return b instanceof a && b.isRuntimeNamespace(); + d.isRuntimeNamespace = function(b) { + return b instanceof d && b.isRuntimeNamespace(); }; - a.isRuntime = function(b) { - return b instanceof a && b.isRuntimeName() || b.isRuntimeNamespace(); + d.isRuntime = function(b) { + return b instanceof d && b.isRuntimeName() || b.isRuntimeNamespace(); }; - a.getQualifiedName = function(b) { - m(a.isQName(b)); - if (b instanceof a) { + d.getQualifiedName = function(b) { + u(d.isQName(b)); + if (b instanceof d) { if (void 0 !== b.qualifiedName) { return b.qualifiedName; } - var c = String(b.name); - if (t(c) && b.namespaces[0].isPublic()) { - return b.qualifiedName = c; + var a = String(b.name); + if (v(a) && b.namespaces[0].isPublic()) { + return b.qualifiedName = a; } - b = b.qualifiedName = a.qualifyName(b.namespaces[0], c); + b = b.qualifiedName = d.qualifyName(b.namespaces[0], a); } return b; }; - a.qualifyName = function(a, c) { - return b.StringUtilities.concat3("$", a.qualifiedName, c); + d.qualifyName = function(b, d) { + return c.StringUtilities.concat3("$", b.qualifiedName, d); }; - a.stripPublicQualifier = function(a) { - var b = "$" + w.PUBLIC.qualifiedName; - return 0 !== a.indexOf(b) ? void 0 : a.substring(b.length); + d.stripPublicQualifier = function(d) { + var a = "$" + b.PUBLIC.qualifiedName; + return 0 !== d.indexOf(a) ? void 0 : d.substring(a.length); }; - a.fromQualifiedName = function(b) { - if (b instanceof a) { - return b; + d.fromQualifiedName = function(a) { + if (a instanceof d) { + return a; } - if (t(b)) { - return new a([w.PUBLIC], b); + if (v(a)) { + return new d([b.PUBLIC], a, 0); } - if ("$" === b[0]) { - var c = w.fromQualifiedName(b.substring(1)); - return new a([c], b.substring(1 + c.qualifiedName.length)); + if ("$" === a[0]) { + var g = b.fromQualifiedName(a.substring(1)); + return new d([g], a.substring(1 + g.qualifiedName.length), 0); } }; - a.getNameFromPublicQualifiedName = function(b) { - b = a.fromQualifiedName(b); - m(b.getNamespace().isPublic()); + d.getNameFromPublicQualifiedName = function(b) { + b = d.fromQualifiedName(b); + u(b.getNamespace().isPublic()); return b.name; }; - a.getFullQualifiedName = function(b) { - var c = a.getQualifiedName(b); - b instanceof a && b.typeParameter && (c += "$" + a.getFullQualifiedName(b.typeParameter)); - return c; + d.getFullQualifiedName = function(b) { + var a = d.getQualifiedName(b); + b instanceof d && b.typeParameter && (a += "$" + d.getFullQualifiedName(b.typeParameter)); + return a; }; - a.getPublicQualifiedName = function(c) { - var e; - if ("string" === typeof c && (e = a._publicQualifiedNameCache[c])) { - return e; + d.getPublicQualifiedName = function(a) { + var g; + if ("string" === typeof a && (g = d._publicQualifiedNameCache[a])) { + return g; } - if (t(c)) { - return b.toNumber(c); + if (v(a)) { + return c.toNumber(a); } - if (null !== c && s(c)) { - return c; + if (null !== a && p(a)) { + return a; } - e = a.qualifyName(w.PUBLIC, c); - "string" === typeof c && (a._publicQualifiedNameCache[c] = e); - return e; + g = d.qualifyName(b.PUBLIC, a); + "string" === typeof a && (d._publicQualifiedNameCache[a] = g); + return g; }; - a.isPublicQualifiedName = function(a) { - return "number" === typeof a || t(a) || 1 === a.indexOf(w.PUBLIC.qualifiedName); + d.isPublicQualifiedName = function(d) { + return "number" === typeof d || v(d) || 1 === d.indexOf(b.PUBLIC.qualifiedName); }; - a.getAccessModifier = function(b) { - m(a.isQName(b)); + d.getAccessModifier = function(b) { + u(d.isQName(b)); if ("number" === typeof b || "string" === typeof b || b instanceof Number) { return "public"; } - m(b instanceof a); + u(b instanceof d); return b.namespaces[0].getAccessModifier(); }; - a.isNumeric = function(b) { - return "number" === typeof b ? !0 : "string" === typeof b ? t(b) : !isNaN(parseInt(a.getName(b), 10)); + d.isNumeric = function(b) { + return "number" === typeof b ? !0 : "string" === typeof b ? v(b) : !isNaN(parseInt(d.getName(b), 10)); }; - a.getName = function(b) { - m(b instanceof a); - m(!b.isRuntimeName()); + d.getName = function(b) { + u(b instanceof d); + u(!b.isRuntimeName()); return b.getName(); }; - a.isAnyName = function(a) { - return "object" === typeof a && !a.isRuntimeName() && !a.name; + d.isAnyName = function(b) { + return "object" === typeof b && !b.isRuntimeName() && !b.name; }; - a.fromSimpleName = function(b) { - m(b); - if (b in a._simpleNameCache) { - return a._simpleNameCache[b]; - } - var c, e; - c = b.lastIndexOf("."); - 0 >= c && (c = b.lastIndexOf(" ")); - 0 < c && c < b.length - 1 ? (e = b.substring(c + 1).trim(), c = b.substring(0, c).trim()) : (e = b, c = ""); - return a._simpleNameCache[b] = new a(w.fromSimpleName(c), e); + d.fromSimpleName = function(a) { + u(a); + if (a in d._simpleNameCache) { + return d._simpleNameCache[a]; + } + var g, f; + g = a.lastIndexOf("."); + 0 >= g && (g = a.lastIndexOf(" ")); + 0 < g && g < a.length - 1 ? (f = a.substring(g + 1).trim(), g = a.substring(0, g).trim()) : (f = a, g = ""); + return d._simpleNameCache[a] = new d(b.fromSimpleName(g), f, 0); }; - a.prototype.getQName = function(b) { - m(0 <= b && b < this.namespaces.length); + d.prototype.getQName = function(b) { + u(0 <= b && b < this.namespaces.length); this._qualifiedNameCache || (this._qualifiedNameCache = []); - var c = this._qualifiedNameCache[b]; - c || (c = this._qualifiedNameCache[b] = new a([this.namespaces[b]], this.name, this.flags)); - return c; + var a = this._qualifiedNameCache[b]; + a || (a = this._qualifiedNameCache[b] = new d([this.namespaces[b]], this.name, this.flags)); + return a; }; - a.prototype.hasQName = function(b) { - m(b instanceof a); + d.prototype.hasQName = function(b) { + u(b instanceof d); if (this.name !== b.name) { return!1; } - for (var c = 0;c < this.namespaces.length;c++) { - if (this.namespaces[c].isEqualTo(b.namespaces[0])) { + for (var a = 0;a < this.namespaces.length;a++) { + if (this.namespaces[a].isEqualTo(b.namespaces[0])) { return!0; } } return!1; }; - a.prototype.isAttribute = function() { - return this.flags & a.ATTRIBUTE; + d.prototype.isAttribute = function() { + return!!(this.flags & d.ATTRIBUTE); }; - a.prototype.isAnyName = function() { - return a.isAnyName(this); + d.prototype.isAnyName = function() { + return d.isAnyName(this); }; - a.prototype.isAnyNamespace = function() { + d.prototype.isAnyNamespace = function() { return!this.isRuntimeNamespace() && (0 === this.namespaces.length || this.isAnyName() && 1 !== this.namespaces.length); }; - a.prototype.isRuntimeName = function() { - return!!(this.flags & a.RUNTIME_NAME); + d.prototype.isRuntimeName = function() { + return!!(this.flags & d.RUNTIME_NAME); }; - a.prototype.isRuntimeNamespace = function() { - return!!(this.flags & a.RUNTIME_NAMESPACE); + d.prototype.isRuntimeNamespace = function() { + return!!(this.flags & d.RUNTIME_NAMESPACE); }; - a.prototype.isRuntime = function() { - return!!(this.flags & (a.RUNTIME_NAME | a.RUNTIME_NAMESPACE)); + d.prototype.isRuntime = function() { + return!!(this.flags & (d.RUNTIME_NAME | d.RUNTIME_NAMESPACE)); }; - a.prototype.isQName = function() { + d.prototype.isQName = function() { return 1 === this.namespaces.length && !this.isAnyName(); }; - a.prototype.hasTypeParameter = function() { + d.prototype.hasTypeParameter = function() { return!!this.typeParameter; }; - a.prototype.getName = function() { + d.prototype.getName = function() { return this.name; }; - a.prototype.getOriginalName = function() { - m(this.isQName()); - var a = this.namespaces[0].uri; - a && (a += "."); - return a + this.name; - }; - a.prototype.getNamespace = function() { - m(!this.isRuntimeNamespace()); - m(1 === this.namespaces.length); + d.prototype.getOriginalName = function() { + u(this.isQName()); + var b = this.namespaces[0].uri; + b && (b += "."); + return b + this.name; + }; + d.prototype.getNamespace = function() { + u(!this.isRuntimeNamespace()); + u(1 === this.namespaces.length); return this.namespaces[0]; }; - a.prototype.nameToString = function() { + d.prototype.nameToString = function() { if (this.isAnyName()) { return "*"; } - var a = this.getName(); - return this.isRuntimeName() ? "[]" : a; + var b = this.getName(); + return this.isRuntimeName() ? "[]" : b; }; - a.prototype.hasObjectName = function() { + d.prototype.hasObjectName = function() { return "object" === typeof this.name; }; - a.prototype.toString = function() { - var a = this.isAttribute() ? "@" : ""; + d.prototype.toString = function() { + var b = this.isAttribute() ? "@" : ""; if (this.isAnyNamespace()) { - a += "*::" + this.nameToString(); + b += "*::" + this.nameToString(); } else { if (this.isRuntimeNamespace()) { - a += "[]::" + this.nameToString(); + b += "[]::" + this.nameToString(); } else { if (1 === this.namespaces.length && this.isQName()) { - a += this.namespaces[0].toString() + "::", a += this.nameToString(); + b += this.namespaces[0].toString() + "::", b += this.nameToString(); } else { - for (var a = a + "{", b = 0, c = this.namespaces.length;b < c;b++) { - a += this.namespaces[b].toString(), b + 1 < c && (a += ","); + for (var b = b + "{", d = 0, a = this.namespaces.length;d < a;d++) { + b += this.namespaces[d].toString(), d + 1 < a && (b += ","); } - a += "}::" + this.nameToString(); + b += "}::" + this.nameToString(); } } } - this.hasTypeParameter() && (a += "<" + this.typeParameter.toString() + ">"); - return a; + this.hasTypeParameter() && (b += "<" + this.typeParameter.toString() + ">"); + return b; }; - a.ATTRIBUTE = 1; - a.RUNTIME_NAMESPACE = 2; - a.RUNTIME_NAME = 4; - a._nextID = 0; - a._publicQualifiedNameCache = b.ObjectUtilities.createMap(); - a._simpleNameCache = b.ObjectUtilities.createMap(); - a.Int = a.getPublicQualifiedName("int"); - a.Uint = a.getPublicQualifiedName("uint"); - a.Class = a.getPublicQualifiedName("Class"); - a.Array = a.getPublicQualifiedName("Array"); - a.Object = a.getPublicQualifiedName("Object"); - a.String = a.getPublicQualifiedName("String"); - a.Number = a.getPublicQualifiedName("Number"); - a.Boolean = a.getPublicQualifiedName("Boolean"); - a.Function = a.getPublicQualifiedName("Function"); - a.XML = a.getPublicQualifiedName("XML"); - a.XMLList = a.getPublicQualifiedName("XMLList"); - a.TO_STRING = a.getPublicQualifiedName("toString"); - a.VALUE_OF = a.getPublicQualifiedName("valueOf"); - a.TEMPORARY = new a([], ""); - return a; + d.ATTRIBUTE = 1; + d.RUNTIME_NAMESPACE = 2; + d.RUNTIME_NAME = 4; + d._nextID = 0; + d._publicQualifiedNameCache = c.ObjectUtilities.createMap(); + d._simpleNameCache = c.ObjectUtilities.createMap(); + d.Int = d.getPublicQualifiedName("int"); + d.Uint = d.getPublicQualifiedName("uint"); + d.Class = d.getPublicQualifiedName("Class"); + d.Array = d.getPublicQualifiedName("Array"); + d.Object = d.getPublicQualifiedName("Object"); + d.String = d.getPublicQualifiedName("String"); + d.Number = d.getPublicQualifiedName("Number"); + d.Boolean = d.getPublicQualifiedName("Boolean"); + d.Function = d.getPublicQualifiedName("Function"); + d.XML = d.getPublicQualifiedName("XML"); + d.XMLList = d.getPublicQualifiedName("XMLList"); + d.TO_STRING = d.getPublicQualifiedName("toString"); + d.VALUE_OF = d.getPublicQualifiedName("valueOf"); + d.TEMPORARY = new d([], "", 0); + return d; }(); - g.Multiname = r; - var h = function() { - function a(b, c) { - for (var e = b.constantPool.strings, h = this.name = e[c.readU30()], l = c.readU30(), d = [], p = [], r = 0;r < l;r++) { - d[r] = e[c.readU30()]; + a.Multiname = g; + var r = function() { + function b(d, a) { + for (var g = d.constantPool.strings, f = this.name = g[a.readU30()], k = a.readU30(), r = [], e = [], n = 0;n < k;n++) { + r[n] = g[a.readU30()]; } - for (r = 0;r < l;r++) { - var q = d[r]; - p[r] = {key:q, value:e[c.readU30()]}; - q && "native" === h && (m(!this.hasOwnProperty(q)), this[q] = p[r].value); + for (n = 0;n < k;n++) { + var w = r[n]; + e[n] = {key:w, value:g[a.readU30()]}; + w && "native" === f && (u(!this.hasOwnProperty(w)), this[w] = e[n].value); } - this.value = p; + this.value = e; } - a.prototype.toString = function() { + b.prototype.toString = function() { return "[" + this.name + "]"; }; - return a; + return b; }(); - g.MetaDataInfo = h; - (function(a) { - a[a.Undefined = 0] = "Undefined"; - a[a.Utf8 = 1] = "Utf8"; - a[a.Float = 2] = "Float"; - a[a.Int = 3] = "Int"; - a[a.UInt = 4] = "UInt"; - a[a.PrivateNs = 5] = "PrivateNs"; - a[a.Double = 6] = "Double"; - a[a.QName = 7] = "QName"; - a[a.Namespace = 8] = "Namespace"; - a[a.Multiname = 9] = "Multiname"; - a[a.False = 10] = "False"; - a[a.True = 11] = "True"; - a[a.Null = 12] = "Null"; - a[a.QNameA = 13] = "QNameA"; - a[a.MultinameA = 14] = "MultinameA"; - a[a.RTQName = 15] = "RTQName"; - a[a.RTQNameA = 16] = "RTQNameA"; - a[a.RTQNameL = 17] = "RTQNameL"; - a[a.RTQNameLA = 18] = "RTQNameLA"; - a[a.NameL = 19] = "NameL"; - a[a.NameLA = 20] = "NameLA"; - a[a.NamespaceSet = 21] = "NamespaceSet"; - a[a.PackageNamespace = 22] = "PackageNamespace"; - a[a.PackageInternalNs = 23] = "PackageInternalNs"; - a[a.ProtectedNamespace = 24] = "ProtectedNamespace"; - a[a.ExplicitNamespace = 25] = "ExplicitNamespace"; - a[a.StaticProtectedNs = 26] = "StaticProtectedNs"; - a[a.MultinameL = 27] = "MultinameL"; - a[a.MultinameLA = 28] = "MultinameLA"; - a[a.TypeName = 29] = "TypeName"; - a[a.ClassSealed = 1] = "ClassSealed"; - a[a.ClassFinal = 2] = "ClassFinal"; - a[a.ClassInterface = 4] = "ClassInterface"; - a[a.ClassProtectedNs = 8] = "ClassProtectedNs"; - })(g.CONSTANT || (g.CONSTANT = {})); - (function(a) { - a[a.Arguments = 1] = "Arguments"; - a[a.Activation = 2] = "Activation"; - a[a.Needrest = 4] = "Needrest"; - a[a.HasOptional = 8] = "HasOptional"; - a[a.IgnoreRest = 16] = "IgnoreRest"; - a[a.Native = 32] = "Native"; - a[a.Setsdxns = 64] = "Setsdxns"; - a[a.HasParamNames = 128] = "HasParamNames"; - })(g.METHOD || (g.METHOD = {})); - (function(a) { - a[a.Slot = 0] = "Slot"; - a[a.Method = 1] = "Method"; - a[a.Getter = 2] = "Getter"; - a[a.Setter = 3] = "Setter"; - a[a.Class = 4] = "Class"; - a[a.Function = 5] = "Function"; - a[a.Const = 6] = "Const"; - })(g.TRAIT || (g.TRAIT = {})); - (function(a) { - a[a.Final = 1] = "Final"; - a[a.Override = 2] = "Override"; - a[a.Metadata = 4] = "Metadata"; - })(g.ATTR || (g.ATTR = {})); - (function(a) { - a[a.CASEINSENSITIVE = 1] = "CASEINSENSITIVE"; - a[a.DESCENDING = 2] = "DESCENDING"; - a[a.UNIQUESORT = 4] = "UNIQUESORT"; - a[a.RETURNINDEXEDARRAY = 8] = "RETURNINDEXEDARRAY"; - a[a.NUMERIC = 16] = "NUMERIC"; - })(g.SORT || (g.SORT = {})); - var x = function() { - function a(b, c) { - var e, h = [0]; - e = b.readU30(); - for (var l = 1;l < e;++l) { - h.push(b.readS32()); - } - var d = [0]; - e = b.readU30(); - for (l = 1;l < e;++l) { - d.push(b.readU32()); - } - var p = [NaN]; - e = b.readU30(); - for (l = 1;l < e;++l) { - p.push(b.readDouble()); - } - k.enterTimeline("Parse Strings"); - var q = [""]; - e = b.readU30(); - for (l = 1;l < e;++l) { - q.push(b.readUTFString(b.readU30())); - } - k.leaveTimeline(); - this.ints = h; - this.uints = d; - this.doubles = p; - this.strings = q; - k.enterTimeline("Parse Namespaces"); - h = [void 0]; - e = b.readU30(); - for (l = 1;l < e;++l) { - h.push(w.parse(this, b, c.hash + l)); - } - k.leaveTimeline(); - k.enterTimeline("Parse Namespace Sets"); - d = [void 0]; - e = b.readU30(); - for (l = 1;l < e;++l) { - p = b.readU30(); - q = []; - q.runtimeId = a._nextNamespaceSetID++; - q.hash = c.hash & 65535 | 262144 | l << 19; - for (var n = 0;n < p;++n) { - q.push(h[b.readU30()]); - } - d.push(q); - } - k.leaveTimeline(); - this.namespaces = h; - this.namespaceSets = d; - k.enterTimeline("Parse Multinames"); - h = [void 0]; - d = []; - e = b.readU30(); - for (l = 1;l < e;++l) { - h.push(r.parse(this, b, h, d, l)); + a.MetaDataInfo = r; + (function(b) { + b[b.Undefined = 0] = "Undefined"; + b[b.Utf8 = 1] = "Utf8"; + b[b.Float = 2] = "Float"; + b[b.Int = 3] = "Int"; + b[b.UInt = 4] = "UInt"; + b[b.PrivateNs = 5] = "PrivateNs"; + b[b.Double = 6] = "Double"; + b[b.QName = 7] = "QName"; + b[b.Namespace = 8] = "Namespace"; + b[b.Multiname = 9] = "Multiname"; + b[b.False = 10] = "False"; + b[b.True = 11] = "True"; + b[b.Null = 12] = "Null"; + b[b.QNameA = 13] = "QNameA"; + b[b.MultinameA = 14] = "MultinameA"; + b[b.RTQName = 15] = "RTQName"; + b[b.RTQNameA = 16] = "RTQNameA"; + b[b.RTQNameL = 17] = "RTQNameL"; + b[b.RTQNameLA = 18] = "RTQNameLA"; + b[b.NameL = 19] = "NameL"; + b[b.NameLA = 20] = "NameLA"; + b[b.NamespaceSet = 21] = "NamespaceSet"; + b[b.PackageNamespace = 22] = "PackageNamespace"; + b[b.PackageInternalNs = 23] = "PackageInternalNs"; + b[b.ProtectedNamespace = 24] = "ProtectedNamespace"; + b[b.ExplicitNamespace = 25] = "ExplicitNamespace"; + b[b.StaticProtectedNs = 26] = "StaticProtectedNs"; + b[b.MultinameL = 27] = "MultinameL"; + b[b.MultinameLA = 28] = "MultinameLA"; + b[b.TypeName = 29] = "TypeName"; + b[b.ClassSealed = 1] = "ClassSealed"; + b[b.ClassFinal = 2] = "ClassFinal"; + b[b.ClassInterface = 4] = "ClassInterface"; + b[b.ClassProtectedNs = 8] = "ClassProtectedNs"; + })(a.CONSTANT || (a.CONSTANT = {})); + (function(b) { + b[b.Arguments = 1] = "Arguments"; + b[b.Activation = 2] = "Activation"; + b[b.Needrest = 4] = "Needrest"; + b[b.HasOptional = 8] = "HasOptional"; + b[b.IgnoreRest = 16] = "IgnoreRest"; + b[b.Native = 32] = "Native"; + b[b.Setsdxns = 64] = "Setsdxns"; + b[b.HasParamNames = 128] = "HasParamNames"; + })(a.METHOD || (a.METHOD = {})); + (function(b) { + b[b.Slot = 0] = "Slot"; + b[b.Method = 1] = "Method"; + b[b.Getter = 2] = "Getter"; + b[b.Setter = 3] = "Setter"; + b[b.Class = 4] = "Class"; + b[b.Function = 5] = "Function"; + b[b.Const = 6] = "Const"; + })(a.TRAIT || (a.TRAIT = {})); + var w = a.TRAIT; + (function(b) { + b[b.Final = 1] = "Final"; + b[b.Override = 2] = "Override"; + b[b.Metadata = 4] = "Metadata"; + })(a.ATTR || (a.ATTR = {})); + (function(b) { + b[b.CASEINSENSITIVE = 1] = "CASEINSENSITIVE"; + b[b.DESCENDING = 2] = "DESCENDING"; + b[b.UNIQUESORT = 4] = "UNIQUESORT"; + b[b.RETURNINDEXEDARRAY = 8] = "RETURNINDEXEDARRAY"; + b[b.NUMERIC = 16] = "NUMERIC"; + })(a.SORT || (a.SORT = {})); + var z = function() { + function d(a, f) { + var k, r = [0]; + k = a.readU30(); + for (var e = 1;e < k;++e) { + r.push(a.readS32()); + } + var n = [0]; + k = a.readU30(); + for (e = 1;e < k;++e) { + n.push(a.readU32()); + } + var w = [NaN]; + k = a.readU30(); + for (e = 1;e < k;++e) { + w.push(a.readDouble()); + } + h.enterTimeline("Parse Strings"); + var m = [""]; + k = a.readU30(); + for (e = 1;e < k;++e) { + m.push(a.readUTFString(a.readU30())); + } + h.leaveTimeline(); + this.ints = r; + this.uints = n; + this.doubles = w; + this.strings = m; + h.enterTimeline("Parse Namespaces"); + r = [void 0]; + k = a.readU30(); + for (e = 1;e < k;++e) { + r.push(b.parse(this, a, f.hash + e)); + } + h.leaveTimeline(); + h.enterTimeline("Parse Namespace Sets"); + n = [void 0]; + k = a.readU30(); + for (e = 1;e < k;++e) { + w = a.readU30(); + m = []; + m.runtimeId = d._nextNamespaceSetID++; + m.hash = f.hash & 65535 | 262144 | e << 19; + for (var c = 0;c < w;++c) { + m.push(r[a.readU30()]); + } + n.push(m); + } + h.leaveTimeline(); + this.namespaces = r; + this.namespaceSets = n; + h.enterTimeline("Parse Multinames"); + r = [void 0]; + n = []; + k = a.readU30(); + for (e = 1;e < k;++e) { + r.push(g.parse(this, a, r, n, e)); } - for (l = 0;l < d.length;l++) { - e = d[l], q = h[e.factoryTypeIndex], p = h[e.typeParameterIndex], m(q && p), q = new r(q.namespaces, q.name, e.flags), q.typeParameter = p, h[e.index] = q; + for (e = 0;e < n.length;e++) { + k = n[e], m = r[k.factoryTypeIndex], w = r[k.typeParameterIndex], u(m && w), m = new g(m.namespaces, m.name, k.flags), m.typeParameter = w, r[k.index] = m; } - k.leaveTimeline(); - this.multinames = h; + h.leaveTimeline(); + this.multinames = r; } - a.prototype.getValue = function(a, c) { - switch(a) { + d.prototype.getValue = function(b, d) { + switch(b) { case 3: - return this.ints[c]; + return this.ints[d]; case 4: - return this.uints[c]; + return this.uints[d]; case 6: - return this.doubles[c]; + return this.doubles[d]; case 1: - return this.strings[c]; + return this.strings[d]; case 11: return!0; case 10: @@ -7134,7 +7479,7 @@ case 8: ; case 23: - return this.namespaces[c]; + return this.namespaces[d]; case 7: ; case 14: @@ -7150,183 +7495,183 @@ case 19: ; case 20: - return this.multinames[c]; + return this.multinames[d]; case 2: - b.Debug.warning("TODO: CONSTANT.Float may be deprecated?"); + c.Debug.warning("TODO: CONSTANT.Float may be deprecated?"); break; default: - m(!1, "Not Implemented Kind " + a); + u(!1, "Not Implemented Kind " + b); } }; - a._nextNamespaceSetID = 1; - return a; + d._nextNamespaceSetID = 1; + return d; }(); - g.ConstantPool = x; - })(k.ABC || (k.ABC = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - function f(a, b, c, l) { - "undefined" === typeof l && (l = null); - 0 !== c.length && (a.enter(b + " {"), c.forEach(function(b, c) { - b.trace(a, l); + a.ConstantPool = z; + })(h.ABC || (h.ABC = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + function s(a, e, k, f) { + void 0 === f && (f = null); + 0 !== k.length && (a.enter(e + " {"), k.forEach(function(d, b) { + d.trace(a, f); }), a.leave("}")); } - function k(a, b, c) { - var l; - "undefined" === typeof l && (l = !1); - var n = c.position, f = ""; - null === a.operands ? f = "null" : a.operands.forEach(function(l, h) { - var n = f, u; + function h(a, e, k) { + var f; + void 0 === f && (f = !1); + var d = k.position, b = ""; + null === a.operands ? b = "null" : a.operands.forEach(function(d, f) { + var w = b, m; a: { - u = 0; - switch(l.size) { + m = 0; + switch(d.size) { case "s08": - u = c.readS8(); + m = k.readS8(); break; case "u08": - u = c.readU8(); + m = k.readU8(); break; case "s16": - u = c.readS16(); + m = k.readS16(); break; case "s24": - u = c.readS24(); + m = k.readS24(); break; case "u30": - u = c.readU30(); + m = k.readU30(); break; case "u32": - u = c.readU32(); + m = k.readU32(); break; default: - d(!1); + l(!1); } - var g = ""; - switch(l.type) { + var c = ""; + switch(d.type) { case "": break; case "I": - g = b.constantPool.ints[u]; + c = e.constantPool.ints[m]; break; case "U": - g = b.constantPool.uints[u]; + c = e.constantPool.uints[m]; break; case "D": - g = b.constantPool.doubles[u]; + c = e.constantPool.doubles[m]; break; case "S": - g = b.constantPool.strings[u]; + c = e.constantPool.strings[m]; break; case "N": - g = b.constantPool.namespaces[u]; + c = e.constantPool.namespaces[m]; break; case "CI": - g = b.classes[u]; + c = e.classes[m]; break; case "M": - u = b.constantPool.multinames[u]; + m = e.constantPool.multinames[m]; break a; default: - g = "?"; + c = "?"; } - u = l.name + ":" + u + ("" === g ? "" : " (" + g + ")"); + m = d.name + ":" + m + ("" === c ? "" : " (" + c + ")"); } - f = n + u; - h < a.operands.length - 1 && (f += ", "); + b = w + m; + f < a.operands.length - 1 && (b += ", "); }); - l && c.seek(n); - return f; + f && k.seek(d); + return b; } - function s(a, b) { - var c = new n(a); - b.scripts.forEach(function(a) { - c.traceTraits(a.traits); + function p(a, e) { + var k = new t(a); + e.scripts.forEach(function(a) { + k.traceTraits(a.traits); }); } - function m(a, c) { - var q = new b.Metrics.Counter(!0), l = new b.Metrics.Counter(!0), n = new b.Metrics.Counter(!0), f = new b.Metrics.Counter(!0), r = {}, h = {}, x = {}; - c.classes.forEach(function(a) { - r[a.instanceInfo.name.name] = !0; + function u(e, n) { + var k = new c.Metrics.Counter(!0), f = new c.Metrics.Counter(!0), d = new c.Metrics.Counter(!0), b = new c.Metrics.Counter(!0), g = {}, r = {}, w = {}; + n.classes.forEach(function(b) { + g[b.instanceInfo.name.name] = !0; }); - c.scripts.forEach(function(a) { - a.traits.forEach(function(a) { - if (a.isClass()) { - var b = a.classInfo.instanceInfo.superName ? a.classInfo.instanceInfo.superName.name : "?"; - b in r || l.count(b); - a.classInfo.traits.forEach(function(a) { - a.isMethod() ? h[a.name.name] = !0 : x[a.name.name] = !0; + n.scripts.forEach(function(b) { + b.traits.forEach(function(b) { + if (b.isClass()) { + var d = b.classInfo.instanceInfo.superName ? b.classInfo.instanceInfo.superName.name : "?"; + d in g || f.count(d); + b.classInfo.traits.forEach(function(b) { + b.isMethod() ? r[b.name.name] = !0 : w[b.name.name] = !0; }); - a.classInfo.instanceInfo.traits.forEach(function(a) { - !a.isMethod() || a.attributes & 2 ? x[a.name.name] = !0 : h[a.name.name] = !0; + b.classInfo.instanceInfo.traits.forEach(function(b) { + !b.isMethod() || b.attributes & 2 ? w[b.name.name] = !0 : r[b.name.name] = !0; }); } }); }); - var m = new b.Metrics.Counter(!0); - c.methods.forEach(function(a) { - function l(a) { - var b = 0; - switch(a.size) { + var m = new c.Metrics.Counter(!0); + n.methods.forEach(function(f) { + function e(b) { + var d = 0; + switch(b.size) { case "s08": - b = p.readS8(); + d = q.readS8(); break; case "u08": - b = p.readU8(); + d = q.readU8(); break; case "s16": - b = p.readS16(); + d = q.readS16(); break; case "s24": - b = p.readS24(); + d = q.readS24(); break; case "u30": - b = p.readU30(); + d = q.readU30(); break; case "u32": - b = p.readU32(); + d = q.readU32(); break; default: - d(!1); + l(!1); } - var h = ""; - switch(a.type) { + var a = ""; + switch(b.type) { case "": break; case "I": - h = c.constantPool.ints[b]; + a = n.constantPool.ints[d]; break; case "U": - h = c.constantPool.uints[b]; + a = n.constantPool.uints[d]; break; case "D": - h = c.constantPool.doubles[b]; + a = n.constantPool.doubles[d]; break; case "S": - h = c.constantPool.strings[b]; + a = n.constantPool.strings[d]; break; case "N": - h = c.constantPool.namespaces[b]; + a = n.constantPool.namespaces[d]; break; case "CI": - h = c.classes[b]; + a = n.classes[d]; break; case "M": - h = c.constantPool.multinames[b]; + a = n.constantPool.multinames[d]; break; default: - h = "?"; + a = "?"; } - return h; + return a; } - if (a.code) { - for (var p = new g.AbcStream(a.code);0 < p.remaining();) { - a = p.readU8(); - var k = b.AVM2.opcodeTable[a], s = null; - if (k) { - switch(m.count(k.name), k.operands && (s = k.operands.map(l)), a) { + if (f.code) { + for (var q = new a.AbcStream(f.code);0 < q.remaining();) { + f = q.readU8(); + var t = c.AVM2.opcodeTable[f], s = null; + if (t) { + switch(m.count(t.name), t.operands && (s = t.operands.map(e)), f) { case 65: ; case 67: @@ -7342,289 +7687,288 @@ case 69: ; case 78: - !s[0] || s[0].name in h || n.count(s[0].name); + !s[0] || s[0].name in r || d.count(s[0].name); break; case 74: - !s[0] || s[0].name in r || q.count(s[0].name); + !s[0] || s[0].name in g || k.count(s[0].name); break; case 102: ; case 97: - !s[0] || s[0].name in x || f.count(s[0].name); + !s[0] || s[0].name in w || b.count(s[0].name); } } } } }); - a.writeLn(JSON.stringify({definedClasses:r, definedMethods:h, definedProperties:x, libraryClasses:q.counts, librarySuperClasses:l.counts, libraryMethods:n.counts, libraryProperties:f.counts, operations:m.counts}, null, 2)); + e.writeLn(JSON.stringify({definedClasses:g, definedMethods:r, definedProperties:w, libraryClasses:k.counts, librarySuperClasses:f.counts, libraryMethods:d.counts, libraryProperties:b.counts, operations:m.counts}, null, 2)); } - var d = b.Debug.assert, a = b.Debug.notImplemented, c = new b.Options.Option("f", "filter", "string", "SpciMsmNtu", "[S]ource, constant[p]ool, [c]lasses, [i]nstances, [M]etadata, [s]cripts, [m]ethods, multi[N]ames, S[t]atistics, [u]tf"); - g.AbcFile.prototype.trace = function(a) { - 0 <= c.value.indexOf("p") && this.constantPool.trace(a); - 0 <= c.value.indexOf("N") && a.writeArray(this.constantPool.multinames, null, !0); - 0 <= c.value.indexOf("c") && f(a, "classes", this.classes); - 0 <= c.value.indexOf("i") && f(a, "instances", this.instances); - 0 <= c.value.indexOf("M") && f(a, "metadata", this.metadata); - 0 <= c.value.indexOf("s") && f(a, "scripts", this.scripts); - 0 <= c.value.indexOf("m") && f(a, "methods", this.methods, this); - 0 <= c.value.indexOf("S") && s(a, this); - 0 <= c.value.indexOf("t") && m(a, this); + var l = c.Debug.assert, e = c.Debug.notImplemented, m = new c.Options.Option("f", "filter", "string", "SpciMsmNtu", "[S]ource, constant[p]ool, [c]lasses, [i]nstances, [M]etadata, [s]cripts, [m]ethods, multi[N]ames, S[t]atistics, [u]tf"); + a.AbcFile.prototype.trace = function(a) { + 0 <= m.value.indexOf("p") && this.constantPool.trace(a); + 0 <= m.value.indexOf("N") && a.writeArray(this.constantPool.multinames, null, !0); + 0 <= m.value.indexOf("c") && s(a, "classes", this.classes); + 0 <= m.value.indexOf("i") && s(a, "instances", this.instances); + 0 <= m.value.indexOf("M") && s(a, "metadata", this.metadata); + 0 <= m.value.indexOf("s") && s(a, "scripts", this.scripts); + 0 <= m.value.indexOf("m") && s(a, "methods", this.methods, this); + 0 <= m.value.indexOf("S") && p(a, this); + 0 <= m.value.indexOf("t") && u(a, this); }; - g.ConstantPool.prototype.trace = function(a) { + a.ConstantPool.prototype.trace = function(a) { a.enter("constantPool {"); - for (var b in this) { - "namespaces" === b ? (a.enter("namespaces {"), this.namespaces.forEach(function(b, c) { - a.writeLn(("" + c).padRight(" ", 3) + (b ? b.toString() : "*")); - }), a.leave("}")) : this[b] instanceof Array && (a.enter(b + " " + this[b].length + " {"), a.writeArray(this[b]), a.leave("}")); + for (var e in this) { + "namespaces" === e ? (a.enter("namespaces {"), this.namespaces.forEach(function(k, f) { + a.writeLn(("" + f).padRight(" ", 3) + (k ? k.toString() : "*")); + }), a.leave("}")) : this[e] instanceof Array && (a.enter(e + " " + this[e].length + " {"), a.writeArray(this[e]), a.leave("}")); } a.leave("}"); }; - g.ClassInfo.prototype.trace = function(a) { + a.ClassInfo.prototype.trace = function(a) { a.enter("class " + this + " {"); - f(a, "traits", this.traits); + s(a, "traits", this.traits); a.leave("}"); }; - g.MetaDataInfo.prototype.trace = function(a) { + a.MetaDataInfo.prototype.trace = function(a) { a.enter(this + " {"); - this.value.forEach(function(b) { - a.writeLn((b.key ? b.key + ": " : "") + '"' + b.value + '"'); + this.value.forEach(function(e) { + a.writeLn((e.key ? e.key + ": " : "") + '"' + e.value + '"'); }); a.leave("}"); }; - g.InstanceInfo.prototype.trace = function(a) { + a.InstanceInfo.prototype.trace = function(a) { a.enter("instance " + this + " {"); - f(a, "traits", this.traits); + s(a, "traits", this.traits); a.leave("}"); }; - g.ScriptInfo.prototype.trace = function(a) { + a.ScriptInfo.prototype.trace = function(a) { a.enter("script " + this + " {"); - f(a, "traits", this.traits); + s(a, "traits", this.traits); a.leave("}"); }; - g.Trait.prototype.trace = function(a) { + a.Trait.prototype.trace = function(a) { if (this.metadata) { - for (var b in this.metadata) { - this.metadata.hasOwnProperty(b) && this.metadata[b].trace(a); + for (var e in this.metadata) { + this.metadata.hasOwnProperty(e) && this.metadata[e].trace(a); } } a.writeLn(this); }; - g.MethodInfo.prototype.trace = function(a) { - var c = this.abc; - a.enter("method" + (this.name ? " " + this.name : "") + " {"); - a.writeLn("flags: " + b.IntegerUtilities.getFlags(this.flags, "NEED_ARGUMENTS NEED_ACTIVATION NEED_REST HAS_OPTIONAL NATIVE SET_DXN HAS_PARAM_NAMES".split(" "))); - a.writeLn("parameters: " + this.parameters.map(function(a) { - return(a.type ? g.Multiname.getQualifiedName(a.type) + "::" : "") + a.name; + a.MethodInfo.prototype.trace = function(e) { + var n = this.abc; + e.enter("method" + (this.name ? " " + this.name : "") + " {"); + e.writeLn("flags: " + c.IntegerUtilities.getFlags(this.flags, "NEED_ARGUMENTS NEED_ACTIVATION NEED_REST HAS_OPTIONAL NATIVE SET_DXN HAS_PARAM_NAMES".split(" "))); + e.writeLn("parameters: " + this.parameters.map(function(b) { + return(b.type ? a.Multiname.getQualifiedName(b.type) + "::" : "") + b.name; })); if (this.code) { - var q = new g.AbcStream(this.code); - f(a, "traits", this.traits); - for (a.enter("code {");0 < q.remaining();) { - var l = q.readU8(), n = b.AVM2.opcodeTable[l], w; - w = ("" + q.position).padRight(" ", 6); - switch(l) { + var k = new a.AbcStream(this.code); + s(e, "traits", this.traits); + for (e.enter("code {");0 < k.remaining();) { + var f = k.readU8(), d = c.AVM2.opcodeTable[f], b; + b = ("" + k.position).padRight(" ", 6); + switch(f) { case 27: - w += n.name + ": defaultOffset: " + q.readS24(); - l = q.readU30(); - w += ", caseCount: " + l; - for (n = 0;n < l + 1;n++) { - w += " offset: " + q.readS24(); + b += d.name + ": defaultOffset: " + k.readS24(); + f = k.readU30(); + b += ", caseCount: " + f; + for (d = 0;d < f + 1;d++) { + b += " offset: " + k.readS24(); } - a.writeLn(w); + e.writeLn(b); break; default: - n ? (w += n.name.padRight(" ", 20), n.operands ? (0 < n.operands.length && (w += k(n, c, q)), a.writeLn(w)) : d(!1, "Opcode: " + n.name + " has undefined operands.")) : d(!1, "Opcode: " + l + " is not implemented."); + d ? (b += d.name.padRight(" ", 20), d.operands ? (0 < d.operands.length && (b += h(d, n, k)), e.writeLn(b)) : l(!1, "Opcode: " + d.name + " has undefined operands.")) : l(!1, "Opcode: " + f + " is not implemented."); } } - a.leave("}"); + e.leave("}"); } - a.leave("}"); + e.leave("}"); }; - var n = function() { - function c(a) { + var t = function() { + function m(a) { return void 0 === a ? "undefined" : null === a ? "null" : "string" === typeof a ? '"' + a + '"' : String(a); } - function e(a, b) { - "undefined" === typeof b && (b = !1); - return a.parameters.map(function(a) { - var e = a.name; - b || (a.type && (e += ":" + a.type.getName()), void 0 !== a.value && (e += " = " + c(a.value))); - return e; + function n(a, d) { + void 0 === d && (d = !1); + return a.parameters.map(function(b) { + var a = b.name; + d || (b.type && (a += ":" + b.type.getName()), void 0 !== b.value && (a += " = " + m(b.value))); + return a; }).join(", "); } - function d(a) { + function k(a) { this.writer = a; } - d.prototype = {traceTraits:function(b, d, q) { - var r = this.writer, h = this; - b.forEach(function(b) { - var l; - l = g.Multiname.getAccessModifier(b.name); - var n = b.name.namespaces[0].uri; - n && ("http://adobe.com/AS3/2006/builtin" === n && (n = "AS3"), l = "public" === l ? q === n ? "" : n : l); - d && (l += " static"); - if (b.isSlot() || b.isConst()) { - h.traceMetadata(b.metadata), l = b.isConst() ? l + " const" : l + " var", l += " " + b.name.getName(), b.typeName && (l += ":" + b.typeName.getName()), b.value && (l += " = " + c(b.value)), r.writeLn(l + ";"); + k.prototype = {traceTraits:function(f, d, b) { + var g = this.writer, k = this; + f.forEach(function(f) { + var c; + c = a.Multiname.getAccessModifier(f.name); + var l = f.name.namespaces[0].uri; + l && ("http://adobe.com/AS3/2006/builtin" === l && (l = "AS3"), c = "public" === c ? b === l ? "" : l : c); + d && (c += " static"); + if (f.isSlot() || f.isConst()) { + k.traceMetadata(f.metadata), c = f.isConst() ? c + " const" : c + " var", c += " " + f.name.getName(), f.typeName && (c += ":" + f.typeName.getName()), f.value && (c += " = " + m(f.value)), g.writeLn(c + ";"); } else { - if (b.isMethod() || b.isGetter() || b.isSetter()) { - h.traceMetadata(b.metadata); - n = b.methodInfo; - b.attributes & 2 && (l += " override"); - n.isNative() && (l += " native"); - l = l + " function" + (b.isGetter() ? " get" : b.isSetter() ? " set" : ""); - l += " " + b.name.getName(); - l += "(" + e(n) + ")"; - l += n.returnType ? ":" + n.returnType.getName() : ""; - var f; - b.holder instanceof g.ClassInfo ? (f = b.holder.instanceInfo.name, f.getName()) : b.holder instanceof g.InstanceInfo && (f = b.holder.name, f.getName()); - n.isNative(); - n.isNative() ? r.writeLn(l + ";") : q ? r.writeLn(l + ";") : r.writeLn(l + ' { notImplemented("' + b.name.getName() + '"); }'); + if (f.isMethod() || f.isGetter() || f.isSetter()) { + k.traceMetadata(f.metadata); + l = f.methodInfo; + f.attributes & 2 && (c += " override"); + l.isNative() && (c += " native"); + c = c + " function" + (f.isGetter() ? " get" : f.isSetter() ? " set" : ""); + c += " " + f.name.getName(); + c += "(" + n(l) + ")"; + c += l.returnType ? ":" + l.returnType.getName() : ""; + var t; + f.holder instanceof a.ClassInfo ? (t = f.holder.instanceInfo.name, t.getName()) : f.holder instanceof a.InstanceInfo && (t = f.holder.name, t.getName()); + l.isNative(); + l.isNative() ? g.writeLn(c + ";") : b ? g.writeLn(c + ";") : g.writeLn(c + ' { notImplemented("' + f.name.getName() + '"); }'); } else { - b.isClass() ? (f = b.classInfo.instanceInfo.name, r.enter("package " + f.namespaces[0].uri + " {\n"), h.traceMetadata(b.metadata), h.traceClass(b.classInfo), r.leave("\n}"), h.traceClassStub(b)) : a(b); + f.isClass() ? (t = f.classInfo.instanceInfo.name, g.enter("package " + t.namespaces[0].uri + " {\n"), k.traceMetadata(f.metadata), k.traceClass(f.classInfo), g.leave("\n}"), k.traceClassStub(f)) : e(f); } } }); }, traceClassStub2:function(a) { - function b(a, h) { - "undefined" === typeof h && (h = !1); - var l = []; - a.forEach(function(a, b) { - (a.isMethod() || a.isGetter() || a.isSetter()) && a.methodInfo.isNative() && l.push(a); + function d(d, a) { + void 0 === a && (a = !1); + var g = []; + d.forEach(function(b, d) { + (b.isMethod() || b.isGetter() || b.isSetter()) && b.methodInfo.isNative() && g.push(b); }); - l.forEach(function(a, b) { - var h = a.methodInfo, d = a.name.getName(); - c.writeLn("// " + d + " :: " + (h.parameters.length ? e(h) : "void") + " -> " + (h.returnType ? h.returnType.getName() : "any")); - c.enter((a.isGetter() ? '"get ' + d + '"' : a.isSetter() ? '"set ' + d + '"' : d) + ": function " + d + "(" + e(h, !0) + ") {"); - c.writeLn(' notImplemented("' + p + "." + d + '");'); - c.leave("}" + (b === l.length - 1 ? "" : ",\n")); + g.forEach(function(d, a) { + var f = d.methodInfo, k = d.name.getName(); + b.writeLn("// " + k + " :: " + (f.parameters.length ? n(f) : "void") + " -> " + (f.returnType ? f.returnType.getName() : "any")); + b.enter((d.isGetter() ? '"get ' + k + '"' : d.isSetter() ? '"set ' + k + '"' : k) + ": function " + k + "(" + n(f, !0) + ") {"); + b.writeLn(' notImplemented("' + e + "." + k + '");'); + b.leave("}" + (a === g.length - 1 ? "" : ",\n")); }); } - var c = this.writer, d = a.classInfo, h = d.instanceInfo, p = h.name.getName(); + var b = this.writer, g = a.classInfo, k = g.instanceInfo, e = k.name.getName(); a = a.metadata ? a.metadata.native : null; if (!a) { return!1; } - c.writeLn("Cut and paste the following into `native.js' and edit accordingly"); - c.writeLn("8< --------------------------------------------------------------"); - c.enter("natives." + a.cls + " = function " + a.cls + "(runtime, scope, instanceConstructor, baseClass) {"); - c.writeLn('var c = new Class("' + p + '", instanceConstructor, ApplicationDomain.passthroughCallable(instanceConstructor));'); - c.writeLn("c.extend(baseClass);\n"); - c.enter("c.nativeStatics = {"); - b(d.traits, !0); - c.leave("};\n"); - c.enter("c.nativeMethods = {"); - b(h.traits); - c.leave("};\n"); - c.writeLn("return c;"); - c.leave("};"); - c.writeLn("-------------------------------------------------------------- >8"); + b.writeLn("Cut and paste the following into `native.js' and edit accordingly"); + b.writeLn("8< --------------------------------------------------------------"); + b.enter("natives." + a.cls + " = function " + a.cls + "(runtime, scope, instanceConstructor, baseClass) {"); + b.writeLn('var c = new Class("' + e + '", instanceConstructor, ApplicationDomain.passthroughCallable(instanceConstructor));'); + b.writeLn("c.extend(baseClass);\n"); + b.enter("c.nativeStatics = {"); + d(g.traits, !0); + b.leave("};\n"); + b.enter("c.nativeMethods = {"); + d(k.traits); + b.leave("};\n"); + b.writeLn("return c;"); + b.leave("};"); + b.writeLn("-------------------------------------------------------------- >8"); return!0; }, traceClassStub:function(a) { - function c(a) { - return{properties:a.filter(function(a) { + function d(b) { + return{properties:b.filter(function(b) { return!1; - }), methods:a.filter(function(a) { - return(a.isMethod() || a.isGetter() || a.isSetter()) && !0 === a.methodInfo.isNative(); + }), methods:b.filter(function(b) { + return(b.isMethod() || b.isGetter() || b.isSetter()) && !0 === b.methodInfo.isNative(); })}; } - function d(a, h) { - function l(a, b) { - var c = a.methodInfo, d = a.name.getName(), n = "// (" + (c.parameters.length ? e(c) : "void") + ") -> " + (c.returnType ? c.returnType.getName() : "any"), f = d; - a.isGetter() ? f = "get" : a.isSetter() && (f = "set"); - p.enter(f + ": function " + d + "(" + e(c, !0) + ") { " + n); - p.writeLn('notImplemented("' + q + "." + d + '");'); - h || (a.isGetter() ? p.writeLn("return this._" + d + ";") : a.isSetter() && p.writeLn("this._" + d + " = " + c.parameters[0].name + ";")); - p.leave("}" + (b ? "," : "")); - } - "undefined" === typeof h && (h = !1); - a = c(a); - var n = [], f = b.ObjectUtilities.createEmptyObject(); - a.methods.forEach(function(a, b) { - var c = a.name.getName(); - a.isGetter() || a.isSetter() ? (f[c] || (f[c] = []), f[c].push(a)) : n.push(a); + function b(b, a) { + function f(b, d) { + var k = b.methodInfo, r = b.name.getName(), m = "// (" + (k.parameters.length ? n(k) : "void") + ") -> " + (k.returnType ? k.returnType.getName() : "any"), c = r; + b.isGetter() ? c = "get" : b.isSetter() && (c = "set"); + g.enter(c + ": function " + r + "(" + n(k, !0) + ") { " + m); + g.writeLn('notImplemented("' + e + "." + r + '");'); + a || (b.isGetter() ? g.writeLn("return this._" + r + ";") : b.isSetter() && g.writeLn("this._" + r + " = " + k.parameters[0].name + ";")); + g.leave("}" + (d ? "," : "")); + } + void 0 === a && (a = !1); + b = d(b); + var k = [], r = Object.create(null); + b.methods.forEach(function(b, d) { + var a = b.name.getName(); + b.isGetter() || b.isSetter() ? (r[a] || (r[a] = []), r[a].push(b)) : k.push(b); }); - for (var w = 0;w < n.length;w++) { - l(n[w], w < n.length - 1); + for (var m = 0;m < k.length;m++) { + f(k[m], m < k.length - 1); } - for (var g = b.ObjectUtilities.toKeyValueArray(f), m = 0;m < g.length;m++) { - p.enter(g[m][0] + ": {"); - for (var y = g[m][1], w = 0;w < y.length;w++) { - l(y[w], w < y.length - 1); - } - p.leave("}" + (m < g.length - 1 ? "," : "")); - } - a.properties.forEach(function(b, c) { - var e = b.name.getName(), h = c === a.properties.length - 1; - b.name.getNamespace().isPublic() && p.writeLn(e + ": " + ("'public " + b.name.name + "'") + (h ? "" : ",")); + for (var q = c.ObjectUtilities.toKeyValueArray(r), l = 0;l < q.length;l++) { + g.enter(q[l][0] + ": {"); + for (var t = q[l][1], m = 0;m < t.length;m++) { + f(t[m], m < t.length - 1); + } + g.leave("}" + (l < q.length - 1 ? "," : "")); + } + b.properties.forEach(function(d, a) { + var f = d.name.getName(), k = a === b.properties.length - 1; + d.name.getNamespace().isPublic() && g.writeLn(f + ": " + ("'public " + d.name.name + "'") + (k ? "" : ",")); }); } - var p = this.writer; + var g = this.writer; a = a.classInfo; - var h = a.instanceInfo, q = h.name.getName(); - p.writeLn("Cut and paste the following glue and edit accordingly."); - p.writeLn("Class " + h); - p.writeLn("8< --------------------------------------------------------------"); - var n = h.name.namespaces[0].uri; - p.enter("var " + q + "Definition = (function () {"); - p.enter("return {"); - p.writeLn("// (" + e(h.init, !1) + ")"); - p.writeLn('__class__: "' + n + "." + q + '",'); - p.enter("initialize: function () {"); - p.leave("},"); - p.enter("__glue__: {"); - p.enter("native: {"); - p.enter("static: {"); - d(a.traits, !0); - p.leave("},"); - p.enter("instance: {"); - d(h.traits); - p.leave("}"); - p.leave("},"); - p.enter("script: {"); - p.writeLn("instance: Glue.ALL"); - p.leave("}"); - p.leave("}"); - p.leave("};"); - p.leave("}).call(this);"); - p.writeLn("-------------------------------------------------------------- >8"); + var k = a.instanceInfo, e = k.name.getName(); + g.writeLn("Cut and paste the following glue and edit accordingly."); + g.writeLn("Class " + k); + g.writeLn("8< --------------------------------------------------------------"); + var m = k.name.namespaces[0].uri; + g.enter("var " + e + "Definition = (function () {"); + g.enter("return {"); + g.writeLn("// (" + n(k.init, !1) + ")"); + g.writeLn('__class__: "' + m + "." + e + '",'); + g.enter("initialize: function () {"); + g.leave("},"); + g.enter("__glue__: {"); + g.enter("native: {"); + g.enter("static: {"); + b(a.traits, !0); + g.leave("},"); + g.enter("instance: {"); + b(k.traits); + g.leave("}"); + g.leave("},"); + g.enter("script: {"); + g.writeLn("instance: Glue.ALL"); + g.leave("}"); + g.leave("}"); + g.leave("};"); + g.leave("}).call(this);"); + g.writeLn("-------------------------------------------------------------- >8"); return!0; - }, traceClass:function(a) { - var b = this.writer, c = a.instanceInfo, d = c.name, h = g.Multiname.getAccessModifier(d); - c.isFinal() && (h += " final"); - c.isSealed() || (h += " dynamic"); - h += c.isInterface() ? " interface " : " class "; - h += d.getName(); - c.superName && "Object" !== c.superName.getName() && (h += " extends " + c.superName.getName()); - c.interfaces.length && (h += " implements " + c.interfaces.map(function(a) { - return a.getName(); + }, traceClass:function(f) { + var d = this.writer, b = f.instanceInfo, g = b.name, k = a.Multiname.getAccessModifier(g); + b.isFinal() && (k += " final"); + b.isSealed() || (k += " dynamic"); + k += b.isInterface() ? " interface " : " class "; + k += g.getName(); + b.superName && "Object" !== b.superName.getName() && (k += " extends " + b.superName.getName()); + b.interfaces.length && (k += " implements " + b.interfaces.map(function(b) { + return b.getName(); }).join(", ")); - b.enter(h + " {"); - c.isInterface() || b.writeLn("public function " + d.getName() + "(" + e(c.init) + ") {}"); - var p; - c.isInterface() && (p = d.namespaces[0].uri + ":" + d.name); - this.traceTraits(a.traits, !0, p); - this.traceTraits(c.traits, !1, p); - b.leave("}"); + d.enter(k + " {"); + b.isInterface() || d.writeLn("public function " + g.getName() + "(" + n(b.init) + ") {}"); + var e; + b.isInterface() && (e = g.namespaces[0].uri + ":" + g.name); + this.traceTraits(f.traits, !0, e); + this.traceTraits(b.traits, !1, e); + d.leave("}"); }, traceMetadata:function(a) { - var b = this.writer, c; - for (c in a) { - a.hasOwnProperty(c) && 0 !== c.indexOf("__") && b.writeLn("[" + c + "(" + a[c].value.map(function(a) { - return(a.key ? a.key + "=" : "") + '"' + a.value + '"'; + var d = this.writer, b; + for (b in a) { + a.hasOwnProperty(b) && 0 !== b.indexOf("__") && d.writeLn("[" + b + "(" + a[b].value.map(function(b) { + return(b.key ? b.key + "=" : "") + '"' + b.value + '"'; }).join(", ") + ")]"); } }}; - return d; + return k; }(); - })(k.ABC || (k.ABC = {})); - })(b.AVM2 || (b.AVM2 = {})); + })(h.ABC || (h.ABC = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - var g = b.Debug.assert, f = b.Debug.unexpected, t = b.AVM2.ABC.AbcStream, s = b.ArrayUtilities.top, m = b.ArrayUtilities.peek; +(function(c) { + (function(h) { + var a = c.Debug.assert, s = c.Debug.unexpected, v = c.AVM2.ABC.AbcStream, p = c.ArrayUtilities.top, u = c.ArrayUtilities.peek; (function(a) { a[a.bkpt = 1] = "bkpt"; a[a.nop = 2] = "nop"; - a[a["throw"] = 3] = "throw"; a[a.getsuper = 4] = "getsuper"; a[a.setsuper = 5] = "setsuper"; a[a.dxns = 6] = "dxns"; @@ -7755,7 +8099,6 @@ a[a.inclocal = 146] = "inclocal"; a[a.decrement = 147] = "decrement"; a[a.declocal = 148] = "declocal"; - a[a["typeof"] = 149] = "typeof"; a[a.not = 150] = "not"; a[a.bitnot = 151] = "bitnot"; a[a.add = 160] = "add"; @@ -7775,10 +8118,8 @@ a[a.lessequals = 174] = "lessequals"; a[a.greaterthan = 175] = "greaterthan"; a[a.greaterequals = 176] = "greaterequals"; - a[a["instanceof"] = 177] = "instanceof"; a[a.istype = 178] = "istype"; a[a.istypelate = 179] = "istypelate"; - a[a["in"] = 180] = "in"; a[a.increment_i = 192] = "increment_i"; a[a.decrement_i = 193] = "decrement_i"; a[a.inclocal_i = 194] = "inclocal_i"; @@ -7801,8 +8142,12 @@ a[a.debugfile = 241] = "debugfile"; a[a.bkptline = 242] = "bkptline"; a[a.timestamp = 243] = "timestamp"; - })(k.OP || (k.OP = {})); - k.opcodeTable = [null, {name:"bkpt", canThrow:!1, operands:[]}, {name:"nop", canThrow:!1, operands:[]}, {name:"throw", canThrow:!0, operands:[]}, {name:"getsuper", canThrow:!0, operands:[{name:"index", size:"u30", type:""}]}, {name:"setsuper", canThrow:!0, operands:[{name:"index", size:"u30", type:""}]}, {name:"dxns", canThrow:!0, operands:[{name:"index", size:"u30", type:""}]}, {name:"dxnslate", canThrow:!0, operands:[]}, {name:"kill", canThrow:!1, operands:[{name:"index", size:"u30", type:""}]}, + a[a["throw"] = 3] = "throw"; + a[a["typeof"] = 149] = "typeof"; + a[a["instanceof"] = 177] = "instanceof"; + a[a["in"] = 180] = "in"; + })(h.OP || (h.OP = {})); + h.opcodeTable = [null, {name:"bkpt", canThrow:!1, operands:[]}, {name:"nop", canThrow:!1, operands:[]}, {name:"throw", canThrow:!0, operands:[]}, {name:"getsuper", canThrow:!0, operands:[{name:"index", size:"u30", type:""}]}, {name:"setsuper", canThrow:!0, operands:[{name:"index", size:"u30", type:""}]}, {name:"dxns", canThrow:!0, operands:[{name:"index", size:"u30", type:""}]}, {name:"dxnslate", canThrow:!0, operands:[]}, {name:"kill", canThrow:!1, operands:[{name:"index", size:"u30", type:""}]}, {name:"label", canThrow:!1, operands:[]}, {name:"lf32x4", canThrow:!0, operands:[]}, {name:"sf32x4", canThrow:!0, operands:[]}, {name:"ifnlt", canThrow:!0, operands:[{name:"offset", size:"s24", type:""}]}, {name:"ifnle", canThrow:!0, operands:[{name:"offset", size:"s24", type:""}]}, {name:"ifngt", canThrow:!0, operands:[{name:"offset", size:"s24", type:""}]}, {name:"ifnge", canThrow:!0, operands:[{name:"offset", size:"s24", type:""}]}, {name:"jump", canThrow:!1, operands:[{name:"offset", size:"s24", type:""}]}, {name:"iftrue", canThrow:!1, operands:[{name:"offset", size:"s24", type:""}]}, {name:"iffalse", canThrow:!1, operands:[{name:"offset", size:"s24", type:""}]}, {name:"ifeq", canThrow:!0, operands:[{name:"offset", size:"s24", type:""}]}, {name:"ifne", canThrow:!0, operands:[{name:"offset", size:"s24", type:""}]}, {name:"iflt", canThrow:!0, operands:[{name:"offset", size:"s24", type:""}]}, {name:"ifle", canThrow:!0, operands:[{name:"offset", size:"s24", type:""}]}, {name:"ifgt", canThrow:!0, operands:[{name:"offset", size:"s24", type:""}]}, {name:"ifge", canThrow:!0, operands:[{name:"offset", size:"s24", type:""}]}, {name:"ifstricteq", canThrow:!1, operands:[{name:"offset", size:"s24", type:""}]}, {name:"ifstrictne", canThrow:!1, operands:[{name:"offset", size:"s24", type:""}]}, {name:"lookupswitch", canThrow:!1, operands:null}, {name:"pushwith", canThrow:!1, operands:[]}, {name:"popscope", canThrow:!1, operands:[]}, {name:"nextname", canThrow:!0, operands:[]}, {name:"hasnext", canThrow:!0, @@ -7825,55 +8170,52 @@ operands:[]}, {name:"inclocal_i", canThrow:!0, operands:[{name:"index", size:"u30", type:""}]}, {name:"declocal_i", canThrow:!0, operands:[{name:"index", size:"u30", type:""}]}, {name:"negate_i", canThrow:!0, operands:[]}, {name:"add_i", canThrow:!0, operands:[]}, {name:"subtract_i", canThrow:!0, operands:[]}, {name:"multiply_i", canThrow:!0, operands:[]}, null, null, null, null, null, null, null, null, {name:"getlocal0", canThrow:!1, operands:[]}, {name:"getlocal1", canThrow:!1, operands:[]}, {name:"getlocal2", canThrow:!1, operands:[]}, {name:"getlocal3", canThrow:!1, operands:[]}, {name:"setlocal0", canThrow:!1, operands:[]}, {name:"setlocal1", canThrow:!1, operands:[]}, {name:"setlocal2", canThrow:!1, operands:[]}, {name:"setlocal3", canThrow:!1, operands:[]}, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, {name:"invalid", canThrow:!1, operands:[]}, null, {name:"debug", canThrow:!0, operands:[{name:"debugType", size:"u08", type:""}, {name:"index", size:"u30", type:"S"}, {name:"reg", size:"u08", type:""}, {name:"extra", size:"u30", type:""}]}, {name:"debugline", canThrow:!0, operands:[{name:"lineNumber", size:"u30", type:""}]}, {name:"debugfile", canThrow:!0, operands:[{name:"index", size:"u30", type:"S"}]}, null, null, null, null, null, null, null, null, null, null, null, null, null, null]; - k.opcodeName = function(a) { - return k.opcodeTable[a].name; + h.opcodeName = function(a) { + return h.opcodeTable[a].name; }; - var d = function() { - function a(c) { - var e = c.readU8(); - this.op = e; - this.originalPosition = c.position; - var d = b.AVM2.opcodeTable[e]; - d || f("Unknown Op " + e); + var l = function() { + function a(k) { + var f = k.readU8(); + this.op = f; + this.originalPosition = k.position; + var d = c.AVM2.opcodeTable[f]; + d || s("Unknown Op " + f); this.canThrow = d.canThrow; - var p; - switch(e) { - case 27: - d = c.readS24(); - this.offsets = []; - p = c.readU30() + 1; - for (e = 0;e < p;e++) { - this.offsets.push(c.readS24()); - } - this.offsets.push(d); - break; - default: - for (e = 0, p = d.operands.length;e < p;e++) { - var r = d.operands[e]; - switch(r.size) { - case "u08": - this[r.name] = c.readU8(); - break; - case "s08": - this[r.name] = c.readS8(); - break; - case "s16": - this[r.name] = c.readS16(); - break; - case "s24": - this[r.name] = c.readS24(); - break; - case "u30": - this[r.name] = c.readU30(); - break; - case "u32": - this[r.name] = c.readU32(); - break; - default: - f(); - } + var b; + if (27 === f) { + d = k.readS24(); + this.offsets = []; + b = k.readU30() + 1; + for (f = 0;f < b;f++) { + this.offsets.push(k.readS24()); + } + this.offsets.push(d); + } else { + for (f = 0, b = d.operands.length;f < b;f++) { + var g = d.operands[f]; + switch(g.size) { + case "u08": + this[g.name] = k.readU8(); + break; + case "s08": + this[g.name] = k.readS8(); + break; + case "s16": + this[g.name] = k.readS16(); + break; + case "s24": + this[g.name] = k.readS24(); + break; + case "u30": + this[g.name] = k.readU30(); + break; + case "u32": + this[g.name] = k.readU32(); + break; + default: + s(); } - ; + } } } a.prototype.makeBlockHead = function(a) { @@ -7890,147 +8232,147 @@ this.succs && a.writeLn("#" + this.bid); }; a.prototype.toString = function(a) { - var c = b.AVM2.opcodeTable[this.op], e = c.name.padRight(" ", 20), d, p; + var f = c.AVM2.opcodeTable[this.op], d = f.name.padRight(" ", 20), b, g; if (27 === this.op) { - for (e += "targets:", d = 0, p = this.targets.length;d < p;d++) { - e += (0 < d ? "," : "") + this.targets[d].position; + for (d += "targets:", b = 0, g = this.targets.length;b < g;b++) { + d += (0 < b ? "," : "") + this.targets[b].position; } } else { - for (d = 0, p = c.operands.length;d < p;d++) { - var h = c.operands[d]; - if ("offset" === h.name) { - e += "target:" + this.target.position; + for (b = 0, g = f.operands.length;b < g;b++) { + var e = f.operands[b]; + if ("offset" === e.name) { + d += "target:" + this.target.position; } else { - var e = e + (h.name + ": "), n = this[h.name]; + var d = d + (e.name + ": "), w = this[e.name]; if (a) { - switch(h.type) { + switch(e.type) { case "": - e += n; + d += w; break; case "I": - e += a.constantPool.ints[n]; + d += a.constantPool.ints[w]; break; case "U": - e += a.constantPool.uints[n]; + d += a.constantPool.uints[w]; break; case "D": - e += a.constantPool.doubles[n]; + d += a.constantPool.doubles[w]; break; case "S": - e += a.constantPool.strings[n]; + d += a.constantPool.strings[w]; break; case "N": - e += a.constantPool.namespaces[n]; + d += a.constantPool.namespaces[w]; break; case "CI": - e += a.classes[n]; + d += a.classes[w]; break; case "M": - e += a.constantPool.multinames[n]; + d += a.constantPool.multinames[w]; break; default: - e += "?"; + d += "?"; } } else { - e += n; + d += w; } } - d < p - 1 && (e += ", "); + b < g - 1 && (d += ", "); } } - return e; + return d; }; return a; }(); - k.Bytecode = d; - var a = b.BitSets.BITS_PER_WORD, c = b.BitSets.ADDRESS_BITS_PER_WORD, n = b.BitSets.BIT_INDEX_MASK, p = function(b) { - function d(a, c) { - b.call(this, a); - this.blockById = c; + h.Bytecode = l; + var e = c.BitSets.BITS_PER_WORD, m = c.BitSets.ADDRESS_BITS_PER_WORD, t = c.BitSets.BIT_INDEX_MASK, q = function(c) { + function k(a, d) { + c.call(this, a); + this.blockById = d; } - __extends(d, b); - d.prototype.forEachBlock = function(b) { - g(b); - for (var c = this.blockById, e = this.bits, d = 0, h = e.length;d < h;d++) { - var p = e[d]; - if (p) { - for (var n = 0;n < a;n++) { - p & 1 << n && b(c[d * a + n]); + __extends(k, c); + k.prototype.forEachBlock = function(f) { + a(f); + for (var d = this.blockById, b = this.bits, g = 0, k = b.length;g < k;g++) { + var w = b[g]; + if (w) { + for (var m = 0;m < e;m++) { + w & 1 << m && f(d[g * e + m]); } } } }; - d.prototype.choose = function() { - for (var b = this.blockById, c = this.bits, e = 0, d = c.length;e < d;e++) { - var h = c[e]; - if (h) { - for (var p = 0;p < a;p++) { - if (h & 1 << p) { - return b[e * a + p]; + k.prototype.choose = function() { + for (var a = this.blockById, d = this.bits, b = 0, g = d.length;b < g;b++) { + var k = d[b]; + if (k) { + for (var w = 0;w < e;w++) { + if (k & 1 << w) { + return a[b * e + w]; } } } } }; - d.prototype.members = function() { - for (var b = this.blockById, c = [], e = this.bits, d = 0, h = e.length;d < h;d++) { - var p = e[d]; - if (p) { - for (var n = 0;n < a;n++) { - p & 1 << n && c.push(b[d * a + n]); + k.prototype.members = function() { + for (var a = this.blockById, d = [], b = this.bits, g = 0, k = b.length;g < k;g++) { + var w = b[g]; + if (w) { + for (var m = 0;m < e;m++) { + w & 1 << m && d.push(a[g * e + m]); } } } - return c; + return d; }; - d.prototype.setBlocks = function(a) { - for (var b = this.bits, e = 0, d = a.length;e < d;e++) { - var h = a[e].bid; - b[h >> c] |= 1 << (h & n); + k.prototype.setBlocks = function(a) { + for (var d = this.bits, b = 0, g = a.length;b < g;b++) { + var k = a[b].bid; + d[k >> m] |= 1 << (k & t); } }; - return d; - }(b.BitSets.Uint32ArrayBitSet); - k.BlockSet = p; - p = function() { - function a(b) { - this.methodInfo = b; - this.methodInfo.code && (k.enterTimeline("normalizeBytecode"), this.normalizeBytecode(), k.leaveTimeline()); + return k; + }(c.BitSets.Uint32ArrayBitSet); + h.BlockSet = q; + q = function() { + function e(a) { + this.methodInfo = a; + this.methodInfo.code && (h.enterTimeline("normalizeBytecode"), this.normalizeBytecode(), h.leaveTimeline()); } - a.prototype.makeBlockSetFactory = function(a, c) { - g(!this.boundBlockSet); + e.prototype.makeBlockSetFactory = function(k, f) { + a(!this.boundBlockSet); this.boundBlockSet = function() { - return new b.AVM2.BlockSet(a, c); + return new c.AVM2.BlockSet(k, f); }; }; - a.prototype.accessLocal = function(a) { + e.prototype.accessLocal = function(a) { 0 !== a-- && a < this.methodInfo.parameters.length && (this.methodInfo.parameters[a].isUsed = !0); }; - a.prototype.getInvalidTarget = function(a, b) { - if (a && a[b]) { - return a[b]; - } - var c = Object.create(d.prototype); - c.op = 237; - c.position = b; - a && (a[b] = c); - return c; + e.prototype.getInvalidTarget = function(a, f) { + if (a && a[f]) { + return a[f]; + } + var d = Object.create(l.prototype); + d.op = 237; + d.position = f; + a && (a[f] = d); + return d; }; - a.prototype.normalizeBytecode = function() { - for (var a = [], b = [], c = new t(this.methodInfo.code), e;0 < c.remaining();) { - var p = c.position; - e = new d(c); - switch(e.op) { + e.prototype.normalizeBytecode = function() { + for (var a = [], f = [], d = new v(this.methodInfo.code), b;0 < d.remaining();) { + var g = d.position; + b = new l(d); + switch(b.op) { case 2: ; case 9: - a[p] = b.length; + a[g] = f.length; continue; case 27: this.methodInfo.hasLookupSwitches = !0; - e.targets = []; - for (var h = e.offsets, n = 0, f = h.length;n < f;n++) { - h[n] += p; + b.targets = []; + for (var e = b.offsets, w = 0, m = e.length;w < m;w++) { + e[w] += g; } break; case 16: @@ -8062,7 +8404,7 @@ case 17: ; case 18: - e.offset += c.position; + b.offset += d.position; break; case 208: ; @@ -8071,22 +8413,22 @@ case 210: ; case 211: - this.accessLocal(e.op - 208); + this.accessLocal(b.op - 208); break; case 98: - this.accessLocal(e.index); + this.accessLocal(b.index); } - e.position = b.length; - a[p] = b.length; - b.push(e); + b.position = f.length; + a[g] = f.length; + f.push(b); } - for (var c = {}, g = 0, m = b.length;g < m;g++) { - switch(e = b[g], e.op) { + for (var d = {}, c = 0, q = f.length;c < q;c++) { + switch(b = f[c], b.op) { case 27: - h = e.offsets; - n = 0; - for (f = h.length;n < f;n++) { - p = a[h[n]], e.targets.push(b[p] || this.getInvalidTarget(c, h[n])), h[n] = p; + e = b.offsets; + w = 0; + for (m = e.length;w < m;w++) { + g = a[e[w]], b.targets.push(f[g] || this.getInvalidTarget(d, e[w])), e[w] = g; } break; case 16: @@ -8118,45 +8460,45 @@ case 17: ; case 18: - p = a[e.offset], e.target = b[p] || this.getInvalidTarget(c, e.offset), e.offset = p; + g = a[b.offset], b.target = f[g] || this.getInvalidTarget(d, b.offset), b.offset = g; } } - this.bytecodes = b; - e = this.methodInfo.exceptions; - n = 0; - for (f = e.length;n < f;n++) { - h = e[n], h.start = a[h.start], h.end = a[h.end], h.offset = a[h.target], h.target = b[h.offset], h.target.exception = h; + this.bytecodes = f; + b = this.methodInfo.exceptions; + w = 0; + for (m = b.length;w < m;w++) { + e = b[w], e.start = a[e.start], e.end = a[e.end], e.offset = a[e.target], e.target = f[e.offset], e.target.exception = e; } }; - a.prototype.analyzeControlFlow = function() { - g(this.bytecodes); - k.enterTimeline("analyzeControlFlow"); + e.prototype.analyzeControlFlow = function() { + a(this.bytecodes); + h.enterTimeline("analyzeControlFlow"); this.detectBasicBlocks(); this.normalizeReachableBlocks(); this.computeDominance(); this.analyzedControlFlow = !0; - k.leaveTimeline(); + h.leaveTimeline(); return!0; }; - a.prototype.detectBasicBlocks = function() { - var a = this.bytecodes, b = this.methodInfo.exceptions, c = 0 < b.length, e = {}, d, h, p, n; - n = a[0].makeBlockHead(0); - h = 0; - for (p = a.length - 1;h < p;h++) { - switch(d = a[h], d.op) { + e.prototype.detectBasicBlocks = function() { + var k = this.bytecodes, f = this.methodInfo.exceptions, d = 0 < f.length, b = {}, g, e, w, m; + m = k[0].makeBlockHead(0); + e = 0; + for (w = k.length - 1;e < w;e++) { + switch(g = k[e], g.op) { case 71: ; case 72: ; case 3: - n = a[h + 1].makeBlockHead(n); + m = k[e + 1].makeBlockHead(m); break; case 27: - d = d.targets; - for (var f = 0, m = d.length;f < m;f++) { - n = d[f].makeBlockHead(n); + g = g.targets; + for (var c = 0, q = g.length;c < q;c++) { + m = g[c].makeBlockHead(m); } - n = a[h + 1].makeBlockHead(n); + m = k[e + 1].makeBlockHead(m); break; case 16: ; @@ -8187,20 +8529,20 @@ case 17: ; case 18: - n = d.target.makeBlockHead(n), n = a[h + 1].makeBlockHead(n); + m = g.target.makeBlockHead(m), m = k[e + 1].makeBlockHead(m); } } - d = a[p]; - switch(d.op) { + g = k[w]; + switch(g.op) { case 27: - d = d.targets; - f = 0; - for (m = d.length;f < m;f++) { - n = d[f].makeBlockHead(n); + g = g.targets; + c = 0; + for (q = g.length;c < q;c++) { + m = g[c].makeBlockHead(m); } break; case 16: - n = d.target.makeBlockHead(n); + m = g.target.makeBlockHead(m); break; case 21: ; @@ -8229,23 +8571,23 @@ case 17: ; case 18: - n = d.target.makeBlockHead(n), a[h + 1] = this.getInvalidTarget(null, h + 1), n = a[h + 1].makeBlockHead(n); + m = g.target.makeBlockHead(m), k[e + 1] = this.getInvalidTarget(null, e + 1), m = k[e + 1].makeBlockHead(m); + } + if (d) { + for (c = 0;c < f.length;c++) { + e = f[c], w = k[e.end + 1], m = k[e.start].makeBlockHead(m), w && (m = w.makeBlockHead(m)), m = e.target.makeBlockHead(m); + } } - if (c) { - for (f = 0;f < b.length;f++) { - h = b[f], p = a[h.end + 1], n = a[h.start].makeBlockHead(n), p && (n = p.makeBlockHead(n)), n = h.target.makeBlockHead(n); - } - } - var k = a[0]; - h = 1; - for (p = a.length;h < p;h++) { - if (a[h].succs) { - g(k.succs); - e[k.bid] = k; - d = a[h - 1]; - k.end = d; - var s = a[h]; - switch(d.op) { + var n = k[0]; + e = 1; + for (w = k.length;e < w;e++) { + if (k[e].succs) { + a(n.succs); + b[n.bid] = n; + g = k[e - 1]; + n.end = g; + var l = k[e]; + switch(g.op) { case 71: ; case 72: @@ -8253,13 +8595,13 @@ case 3: break; case 27: - f = 0; - for (m = d.targets.length;f < m;f++) { - k.succs.push(d.targets[f]); + c = 0; + for (q = g.targets.length;c < q;c++) { + n.succs.push(g.targets[c]); } break; case 16: - k.succs.push(d.target); + n.succs.push(g.target); break; case 21: ; @@ -8288,140 +8630,140 @@ case 17: ; case 18: - k.succs.push(d.target); - d.target !== s && k.succs.push(s); + n.succs.push(g.target); + g.target !== l && n.succs.push(l); break; default: - k.succs.push(s); + n.succs.push(l); } - if (c) { - f = k; - m = []; - d = 0; - for (var t = b.length;d < t;d++) { - var K = b[d]; - f.position >= K.start && f.end.position <= K.end && m.push(K.target); - } - d = m; - k.hasCatches = 0 < d.length; - k.succs.push.apply(k.succs, d); + if (d) { + c = n; + q = []; + g = 0; + for (var t = f.length;g < t;g++) { + var s = f[g]; + c.position >= s.start && c.end.position <= s.end && q.push(s.target); + } + g = q; + n.hasCatches = 0 < g.length; + n.succs.push.apply(n.succs, g); } - k = s; + n = l; } } - e[k.bid] = k; - d = a[p - 1]; - switch(d.op) { + b[n.bid] = n; + g = k[w - 1]; + switch(g.op) { case 27: - f = 0; - for (m = d.targets.length;f < m;f++) { - k.succs.push(d.targets[f]); + c = 0; + for (q = g.targets.length;c < q;c++) { + n.succs.push(g.targets[c]); } break; case 16: - k.succs.push(d.target); + n.succs.push(g.target); } - k.end = d; - this.makeBlockSetFactory(n, e); + n.end = g; + this.makeBlockSetFactory(m, b); }; - a.prototype.normalizeReachableBlocks = function() { - var a = this.bytecodes[0]; - g(0 === a.preds.length); - var b = [], c = {}, e = {}, d = [a]; - for (e[a.bid] = !0;a = s(d);) { - if (c[a.bid]) { - if (1 === c[a.bid]) { - c[a.bid] = 2; - b.push(a); - for (var h = a.succs, p = 0, n = h.length;p < n;p++) { - h[p].preds.push(a); + e.prototype.normalizeReachableBlocks = function() { + var k = this.bytecodes[0]; + a(0 === k.preds.length); + var f = [], d = {}, b = {}, g = [k]; + for (b[k.bid] = !0;k = p(g);) { + if (d[k.bid]) { + if (1 === d[k.bid]) { + d[k.bid] = 2; + f.push(k); + for (var e = k.succs, m = 0, c = e.length;m < c;m++) { + e[m].preds.push(k); } } - e[a.bid] = !1; - d.pop(); + b[k.bid] = !1; + g.pop(); } else { - for (c[a.bid] = 1, e[a.bid] = !0, h = a.succs, p = 0, n = h.length;p < n;p++) { - var f = h[p]; - e[f.bid] && (a.spbacks || (a.spbacks = new this.boundBlockSet), a.spbacks.set(f.bid)); - !c[f.bid] && d.push(f); + for (d[k.bid] = 1, b[k.bid] = !0, e = k.succs, m = 0, c = e.length;m < c;m++) { + var q = e[m]; + b[q.bid] && (k.spbacks || (k.spbacks = new this.boundBlockSet), k.spbacks.set(q.bid)); + !d[q.bid] && g.push(q); } } } - this.blocks = b.reverse(); + this.blocks = f.reverse(); }; - a.prototype.computeDominance = function() { - var a = this.blocks, b = a.length, c = Array(b); - c[0] = 0; - for (var e = {}, d = 0;d < b;d++) { - e[a[d].bid] = d; + e.prototype.computeDominance = function() { + var k = this.blocks, f = k.length, d = Array(f); + d[0] = 0; + for (var b = {}, g = 0;g < f;g++) { + b[k[g].bid] = g; } - for (var h = !0;h;) { - for (h = !1, d = 1;d < b;d++) { - var p = a[d].preds, n = p.length, f = e[p[0].bid]; - if (!(f in c)) { - for (var m = 1;m < n && !(f = e[p[m].bid], f in c);m++) { + for (var e = !0;e;) { + for (e = !1, g = 1;g < f;g++) { + var m = k[g].preds, c = m.length, q = b[m[0].bid]; + if (!(q in d)) { + for (var n = 1;n < c && !(q = b[m[n].bid], q in d);n++) { } } - g(f in c); - for (m = 0;m < n;m++) { - var k = e[p[m].bid]; - if (k !== f && k in c) { - for (;k !== f;) { - for (;k > f;) { - k = c[k]; + a(q in d); + for (n = 0;n < c;n++) { + var l = b[m[n].bid]; + if (l !== q && l in d) { + for (;l !== q;) { + for (;l > q;) { + l = d[l]; } - for (;f > k;) { - f = c[f]; + for (;q > l;) { + q = d[q]; } } - f = k; + q = l; } } - c[d] !== f && (c[d] = f, h = !0); + d[g] !== q && (d[g] = q, e = !0); } } - a[0].dominator = a[0]; - for (d = 1;d < b;d++) { - e = a[d], m = a[c[d]], e.dominator = m, m.dominatees.push(e), e.npreds = e.preds.length; + k[0].dominator = k[0]; + for (g = 1;g < f;g++) { + b = k[g], n = k[d[g]], b.dominator = n, n.dominatees.push(b), b.npreds = b.preds.length; } - b = [a[0]]; - for (a[0].level || (a[0].level = 0);e = b.shift();) { - a = e.dominatees; - for (m = 0;m < a.length;m++) { - a[m].level = e.level + 1; + f = [k[0]]; + for (k[0].level || (k[0].level = 0);b = f.shift();) { + k = b.dominatees; + for (n = 0;n < k.length;n++) { + k[n].level = b.level + 1; } - b.push.apply(b, a); + f.push.apply(f, k); } }; - a.prototype.markLoops = function() { + e.prototype.markLoops = function() { function a(b) { - var c = 1, h = {}, e = {}, d = [], p = [], l = [], n = b.level + 1; + var d = 1, g = {}, f = {}, k = [], e = [], r = [], m = b.level + 1; b = [b]; - for (var q, r;q = s(b);) { - if (h[q.bid]) { - if (m(p) === q) { - p.pop(); - var f = []; + for (var w, c;w = p(b);) { + if (g[w.bid]) { + if (u(e) === w) { + e.pop(); + var q = []; do { - r = d.pop(), e[r.bid] = !0, f.push(r); - } while (r !== q); - (1 < f.length || r.spbacks && r.spbacks.get(r.bid)) && l.push(f); + c = k.pop(), f[c.bid] = !0, q.push(c); + } while (c !== w); + (1 < q.length || c.spbacks && c.spbacks.get(c.bid)) && r.push(q); } b.pop(); } else { - h[q.bid] = c++; - d.push(q); - p.push(q); - r = q.succs; - for (var f = 0, x = r.length;f < x;f++) { - if (q = r[f], !(q.level < n)) { - var w = q.bid; - if (!h[w]) { - b.push(q); + g[w.bid] = d++; + k.push(w); + e.push(w); + c = w.succs; + for (var q = 0, n = c.length;q < n;q++) { + if (w = c[q], !(w.level < m)) { + var l = w.bid; + if (!g[l]) { + b.push(w); } else { - if (!e[w]) { - for (;h[m(p).bid] > h[w];) { - p.pop(); + if (!f[l]) { + for (;g[u(e).bid] > g[l];) { + e.pop(); } } } @@ -8429,34 +8771,34 @@ } } } - return l; + return r; } - function b(a, c) { - var h = new e; - h.setBlocks(a); - h.recount(); - this.id = c; - this.body = h; - this.exit = new e; + function f(a, d) { + var g = new b; + g.setBlocks(a); + g.recount(); + this.id = d; + this.body = g; + this.exit = new b; this.save = {}; - this.head = new e; + this.head = new b; this.npreds = 0; this._dirtyLocals = null; } if (!this.analyzedControlFlow && !this.analyzeControlFlow()) { return!1; } - var c = this.bytecodes, e = this.boundBlockSet; - b.prototype.getDirtyLocals = function() { + var d = this.bytecodes, b = this.boundBlockSet; + f.prototype.getDirtyLocals = function() { if (this._dirtyLocals) { return this._dirtyLocals; } - var a = this._dirtyLocals = []; - this.body.members().forEach(function(b) { - var h = b.position; - for (b = b.end.position;h <= b;h++) { - var e = c[h], d = e.op; - switch(d) { + var b = this._dirtyLocals = []; + this.body.members().forEach(function(a) { + var g = a.position; + for (a = a.end.position;g <= a;g++) { + var f = d[g], k = f.op; + switch(k) { case 146: ; case 148: @@ -8466,11 +8808,11 @@ case 194: ; case 195: - a[e.index] = !0; + b[f.index] = !0; break; case 50: - a[e.index] = !0; - a[e.object] = !0; + b[f.index] = !0; + b[f.object] = !0; break; case 212: ; @@ -8479,2540 +8821,3218 @@ case 214: ; case 215: - a[d - 212] = !0; + b[k - 212] = !0; } } }); - return a; + return b; }; - var d = function(a) { - for (var b = new e, c = 0, h = a.length;c < h;c++) { - var d = a[c], p = d.spbacks; - if (p) { - for (var d = d.succs, l = 0, n = d.length;l < n;l++) { - var q = d[l]; - p.get(q.bid) && b.set(q.dominator.bid); + var g = function(a) { + for (var d = new b, g = 0, f = a.length;g < f;g++) { + var k = a[g], e = k.spbacks; + if (e) { + for (var k = k.succs, r = 0, m = k.length;r < m;r++) { + var w = k[r]; + e.get(w.bid) && d.set(w.dominator.bid); } } } - return b.members(); + return d.members(); }(this.blocks); - if (0 >= d.length) { + if (0 >= g.length) { return this.markedLoops = !0; } - for (var d = d.sort(function(a, b) { - return a.level - b.level; - }), h = 0, p = d.length - 1;0 <= p;p--) { - var n = d[p], f = a(n); - if (0 !== f.length) { - for (var g = 0, k = f.length;g < k;g++) { - for (var t = f[g], O = new b(t, h++), K = 0, F = t.length;K < F;K++) { - var J = t[K]; - if (J.level === n.level + 1 && !J.loop) { - J.loop = O; - O.head.set(J.bid); - for (var A = J.preds, B = 0, z = A.length;B < z;B++) { - O.body.get(A[B].bid) && J.npreds--; - } - O.npreds += J.npreds; - } - } - K = 0; - for (F = t.length;K < F;K++) { - J = t[K], J.level === n.level + 1 && (J.npreds = O.npreds); + for (var g = g.sort(function(b, a) { + return b.level - a.level; + }), e = 0, m = g.length - 1;0 <= m;m--) { + var c = g[m], q = a(c); + if (0 !== q.length) { + for (var n = 0, l = q.length;n < l;n++) { + for (var t = q[n], s = new f(t, e++), h = 0, v = t.length;h < v;h++) { + var L = t[h]; + if (L.level === c.level + 1 && !L.loop) { + L.loop = s; + s.head.set(L.bid); + for (var H = L.preds, J = 0, C = H.length;J < C;J++) { + s.body.get(H[J].bid) && L.npreds--; + } + s.npreds += L.npreds; + } + } + h = 0; + for (v = t.length;h < v;h++) { + L = t[h], L.level === c.level + 1 && (L.npreds = s.npreds); } - O.head.recount(); + s.head.recount(); } } } return this.markedLoops = !0; }; - return a; + return e; }(); - k.Analysis = p; - })(b.AVM2 || (b.AVM2 = {})); + h.Analysis = q; + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); var Bytecode = Shumway.AVM2.Bytecode, Analysis = Shumway.AVM2.Analysis; -(function(b) { - (function(k) { - var g = b.Options.Option, f = b.Options.OptionSet, t = b.Settings.shumwayOptions.register(new f("AVM2")); - (function(b) { - var m = t.register(new f("Runtime")); - b.traceExecution = m.register(new g("tx", "traceExecution", "number", 0, "trace script execution", {choices:{off:0, normal:2, verbose:3}})); - b.traceCallExecution = m.register(new g("txc", "traceCallExecution", "number", 0, "trace call execution", {choices:{off:0, normal:1, verbose:2}})); - b.traceFunctions = m.register(new g("t", "traceFunctions", "number", 0, "trace functions", {choices:{off:0, compiled:1, "compiled & abc":2}})); - b.traceClasses = m.register(new g("tc", "traceClasses", "boolean", !1, "trace class creation")); - b.traceDomain = m.register(new g("td", "traceDomain", "boolean", !1, "trace domain property access")); - b.debuggerMode = m.register(new g("db", "debuggerMode", "boolean", !0, "enable debugger mode")); - b.globalMultinameAnalysis = m.register(new g("ga", "globalMultinameAnalysis", "boolean", !1, "Global multiname analysis.")); - b.codeCaching = m.register(new g("cc", "codeCaching", "boolean", !1, "Enable code caching.")); - b.compilerEnableExceptions = m.register(new g("cex", "exceptions", "boolean", !1, "Compile functions with catch blocks.")); - b.compilerMaximumMethodSize = m.register(new g("cmms", "maximumMethodSize", "number", 4096, "Compiler maximum method size.")); - b = b.ExecutionMode || (b.ExecutionMode = {}); - b[b.INTERPRET = 1] = "INTERPRET"; - b[b.COMPILE = 2] = "COMPILE"; - })(k.Runtime || (k.Runtime = {})); - (function(b) { - b.options = t.register(new f("Compiler")); - b.traceLevel = b.options.register(new g("tc4", "tc4", "number", 0, "Compiler Trace Level")); - b.breakFilter = b.options.register(new g("", "break", "string", "", "Set a break point at methods whose qualified name matches this string pattern.")); - b.compileFilter = b.options.register(new g("", "compile", "string", "", "Only compile methods whose qualified name matches this string pattern.")); - b.enableDirtyLocals = b.options.register(new g("dl", "dirtyLocals", "boolean", !0, "Performe dirty local analysis to minimise PHI nodes.")); - })(k.Compiler || (k.Compiler = {})); - (function(b) { - b.options = t.register(new f("Verifier")); - b.enabled = b.options.register(new g("verifier", "verifier", "boolean", !0, "Enable verifier.")); - b.traceLevel = b.options.register(new g("tv", "tv", "number", 0, "Verifier Trace Level")); - })(k.Verifier || (k.Verifier = {})); - })(b.AVM2 || (b.AVM2 = {})); +(function(c) { + (function(h) { + var a = c.Options.Option, s = c.Options.OptionSet, v = c.Settings.shumwayOptions.register(new s("AVM2")); + (function(c) { + var h = v.register(new s("Runtime")); + c.traceExecution = h.register(new a("tx", "traceExecution", "number", 0, "trace script execution", {choices:{off:0, normal:2, verbose:3}})); + c.traceCallExecution = h.register(new a("txc", "traceCallExecution", "number", 0, "trace call execution", {choices:{off:0, normal:1, verbose:2}})); + c.traceFunctions = h.register(new a("t", "traceFunctions", "number", 0, "trace functions", {choices:{off:0, compiled:1, "compiled & abc":2}})); + c.traceClasses = h.register(new a("tc", "traceClasses", "boolean", !1, "trace class creation")); + c.traceDomain = h.register(new a("td", "traceDomain", "boolean", !1, "trace domain property access")); + c.debuggerMode = h.register(new a("db", "debuggerMode", "boolean", !0, "enable debugger mode")); + c.globalMultinameAnalysis = h.register(new a("ga", "globalMultinameAnalysis", "boolean", !1, "Global multiname analysis.")); + c.codeCaching = h.register(new a("cc", "codeCaching", "boolean", !1, "Enable code caching.")); + c.compilerEnableExceptions = h.register(new a("cex", "exceptions", "boolean", !1, "Compile functions with catch blocks.")); + c.compilerMaximumMethodSize = h.register(new a("cmms", "maximumMethodSize", "number", 4096, "Compiler maximum method size.")); + c = c.ExecutionMode || (c.ExecutionMode = {}); + c[c.INTERPRET = 1] = "INTERPRET"; + c[c.COMPILE = 2] = "COMPILE"; + })(h.Runtime || (h.Runtime = {})); + (function(c) { + c.options = v.register(new s("Compiler")); + c.traceLevel = c.options.register(new a("tc4", "tc4", "number", 0, "Compiler Trace Level")); + c.breakFilter = c.options.register(new a("", "break", "string", "", "Set a break point at methods whose qualified name matches this string pattern.")); + c.compileFilter = c.options.register(new a("", "compile", "string", "", "Only compile methods whose qualified name matches this string pattern.")); + c.enableDirtyLocals = c.options.register(new a("dl", "dirtyLocals", "boolean", !0, "Performe dirty local analysis to minimise PHI nodes.")); + })(h.Compiler || (h.Compiler = {})); + (function(c) { + c.options = v.register(new s("Verifier")); + c.enabled = c.options.register(new a("verifier", "verifier", "boolean", !0, "Enable verifier.")); + c.traceLevel = c.options.register(new a("tv", "tv", "number", 0, "Verifier Trace Level")); + })(h.Verifier || (h.Verifier = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); var Namespace = Shumway.AVM2.ABC.Namespace; -(function(b) { - (function(k) { - (function(g) { - function f(a) { - var c = this.resolutionMap[a.runtimeId]; - if (c) { - return c; +(function(c) { + (function(h) { + (function(a) { + function s(b) { + var d = this.resolutionMap[b.runtimeId]; + if (d) { + return d; } - var c = this.resolutionMap[a.runtimeId] = b.ObjectUtilities.createMap(), h = this.bindings, e; - for (e in h.map) { - var d = e, p = h.map[e].trait; - if (p.isGetter() || p.isSetter()) { - d = d.substring(g.Binding.KEY_PREFIX_LENGTH); + var d = this.resolutionMap[b.runtimeId] = c.ObjectUtilities.createMap(), g = this.bindings, f; + for (f in g.map) { + var k = f, e = g.map[f].trait; + if (e.isGetter() || e.isSetter()) { + k = k.substring(a.Binding.KEY_PREFIX_LENGTH); } - d = W.fromQualifiedName(d); - d.getNamespace().inNamespaceSet(a) && (c[d.getName()] = W.getQualifiedName(p.name)); + k = T.fromQualifiedName(k); + k.getNamespace().inNamespaceSet(b) && (d[k.getName()] = T.getQualifiedName(e.name)); } - return c; + return d; } - function t(a, c, h) { - na(c) ? c = String(Q(c)) : "object" === typeof c && (c = String(c)); - return b.isNumeric(c) ? b.toNumber(c) : a ? 1 < a.length ? (a = this.getNamespaceResolutionMap(a)[c]) ? a : W.getPublicQualifiedName(c) : W.qualifyName(a[0], c) : W.getPublicQualifiedName(c); + function v(b, a, d) { + qa(a) ? a = String(S(a)) : "object" === typeof a && (a = String(a)); + return c.isNumeric(a) ? c.toNumber(a) : b ? 1 < b.length ? (b = this.getNamespaceResolutionMap(b)[a]) ? b : T.getPublicQualifiedName(a) : T.qualifyName(b[0], a) : T.getPublicQualifiedName(a); + } + function p(b) { + return this.asGetProperty(void 0, b, 0); + } + function u(b, a, d) { + b = this.resolveMultinameProperty(b, a, d); + return this.asGetNumericProperty && T.isNumeric(b) ? this.asGetNumericProperty(b) : this[b]; } - function s(a) { - return this.asGetProperty(void 0, a, 0); + function l(b) { + pa(c.isString(b)); + return this[b]; } - function m(a, b, c) { - a = this.resolveMultinameProperty(a, b, c); - return this.asGetNumericProperty && W.isNumeric(a) ? this.asGetNumericProperty(a) : this[a]; - } - function d(a) { - la(b.isString(a)); - return this[a]; - } - function a(a, b, c) { - b = b ? null : this; - var h = this.asOpenMethods; - return(b && h && h[a] ? h[a] : this[a]).asApply(b, c); - } - function c(a, b) { - return this.asSetProperty(void 0, a, 0, b); - } - function n(a, b) { - b === W.VALUE_OF ? (ia(a, "original_valueOf", a.valueOf), a.valueOf = g.forwardValueOf) : b === W.TO_STRING && (ia(a, "original_toString", a.toString), a.toString = g.forwardToString); - } - function p(a, b, c, h) { - "object" === typeof b && (b = String(b)); - a = this.resolveMultinameProperty(a, b, c); - if (this.asSetNumericProperty && W.isNumeric(a)) { - return this.asSetNumericProperty(a, h); - } - (b = this.asSlots.byQN[a]) && (b = b.type) && b.coerce && (h = b.coerce(h)); - n(this, a); - this[a] = h; - } - function e(a, b) { - return this.asDefineProperty(void 0, a, 0, b); - } - function q(a, b, c, h) { - "object" === typeof b && (b = String(b)); - a = this.resolveMultinameProperty(a, b, c); - Object.defineProperty(this, a, h); - } - function l(a, b) { - return this.asCallProperty(void 0, a, 0, !1, b); - } - function u(a, b, c, h, e) { - g.traceCallExecution.value && ra.enter("call " + (this.class ? this.class + " " : "") + b + "(" + ya(e) + ") #" + ka.count(b)); - h = h ? null : this; - a = this.resolveMultinameProperty(a, b, c); - this.asGetNumericProperty && W.isNumeric(a) ? a = this.asGetNumericProperty(a) : (b = this.asOpenMethods, a = h && b && b[a] ? b[a] : this[a]); - e = a.asApply(h, e); - 0 < g.traceCallExecution.value && ra.leave("return " + va(e)); - return e; + function e(b, a, d) { + a = a ? null : this; + var g = this.asOpenMethods; + return(a && g && g[b] ? g[b] : this[b]).asApply(a, d); + } + function m(b, a) { + return this.asSetProperty(void 0, b, 0, a); + } + function t(b, d) { + d === T.VALUE_OF ? (ha(b, "original_valueOf", b.valueOf), b.valueOf = a.forwardValueOf) : d === T.TO_STRING && (ha(b, "original_toString", b.toString), b.toString = a.forwardToString); + } + function q(b, a, d, g) { + "object" === typeof a && (a = String(a)); + b = this.resolveMultinameProperty(b, a, d); + if (this.asSetNumericProperty && T.isNumeric(b)) { + return this.asSetNumericProperty(b, g); + } + (a = this.asSlots.byQN[b]) && (a = a.type) && a.coerce && (g = a.coerce(g)); + t(this, b); + this[b] = g; + } + function n(b, a) { + return this.asDefineProperty(void 0, b, 0, a); + } + function k(b, a, d, g) { + "object" === typeof a && (a = String(a)); + b = this.resolveMultinameProperty(b, a, d); + Object.defineProperty(this, b, g); + } + function f(b, a, d) { + "object" === typeof a && (a = String(a)); + b = this.resolveMultinameProperty(b, a, d); + return Object.getOwnPropertyDescriptor(this, b); + } + function d(b, a) { + return this.asCallProperty(void 0, b, 0, !1, a); + } + function b(b, d, g, f, k) { + a.traceCallExecution.value && a.callWriter.enter("call " + (this.class ? this.class + " " : "") + d + "(" + xa(k) + ") #" + ia.count(d)); + f = f ? null : this; + b = this.resolveMultinameProperty(b, d, g); + this.asGetNumericProperty && T.isNumeric(b) ? b = this.asGetNumericProperty(b) : (d = this.asOpenMethods, b = f && d && d[b] ? d[b] : this[b]); + k = b.asApply(f, k); + 0 < a.traceCallExecution.value && a.callWriter.leave("return " + va(k)); + return k; } - function w(a, b, c, h, e) { - g.traceCallExecution.value && ra.enter("call super " + (this.class ? this.class + " " : "") + c + "(" + ya(e) + ") #" + ka.count(c)); - a = a.object.baseClass; - b = a.traitsPrototype.resolveMultinameProperty(b, c, h); - c = a.traitsPrototype.asOpenMethods; - la(c && c[b]); - e = c[b].asApply(this, e); - 0 < g.traceCallExecution.value && ra.leave("return " + va(e)); - return e; + function g(b, d, g, f, k) { + a.traceCallExecution.value && a.callWriter.enter("call super " + (this.class ? this.class + " " : "") + g + "(" + xa(k) + ") #" + ia.count(g)); + b = b.object.baseClass; + d = b.traitsPrototype.resolveMultinameProperty(d, g, f); + g = b.traitsPrototype.asOpenMethods; + pa(g && g[d]); + k = g[d].asApply(this, k); + 0 < a.traceCallExecution.value && a.callWriter.leave("return " + va(k)); + return k; } - function r(a, b, c, h, e) { - g.traceCallExecution.value && ra.enter("set super " + (this.class ? this.class + " " : "") + c + "(" + va(e) + ") #" + ka.count(c)); - a = a.object.baseClass; - var d = a.traitsPrototype.resolveMultinameProperty(b, c, h); - this.asSlots.byQN[d] ? this.asSetProperty(b, c, h, e) : a.traitsPrototype[g.VM_OPEN_SET_METHOD_PREFIX + d].call(this, e); - 0 < g.traceCallExecution.value && ra.leave(""); - } - function h(a, b, c, h) { - g.traceCallExecution.value && ra.enter("get super " + (this.class ? this.class + " " : "") + c + " #" + ka.count(c)); - a = a.object.baseClass; - var e = a.traitsPrototype.resolveMultinameProperty(b, c, h); - b = this.asSlots.byQN[e] ? this.asGetProperty(b, c, h) : a.traitsPrototype[g.VM_OPEN_GET_METHOD_PREFIX + e].call(this); - 0 < g.traceCallExecution.value && ra.leave("return " + va(b)); - return b; + function r(b, d, g, f, k) { + a.traceCallExecution.value && a.callWriter.enter("set super " + (this.class ? this.class + " " : "") + g + "(" + va(k) + ") #" + ia.count(g)); + b = b.object.baseClass; + var e = b.traitsPrototype.resolveMultinameProperty(d, g, f); + this.asSlots.byQN[e] ? this.asSetProperty(d, g, f, k) : b.traitsPrototype[a.VM_OPEN_SET_METHOD_PREFIX + e].call(this, k); + 0 < a.traceCallExecution.value && a.callWriter.leave(""); + } + function w(b, d, g, f) { + a.traceCallExecution.value && a.callWriter.enter("get super " + (this.class ? this.class + " " : "") + g + " #" + ia.count(g)); + b = b.object.baseClass; + var k = b.traitsPrototype.resolveMultinameProperty(d, g, f); + d = this.asSlots.byQN[k] ? this.asGetProperty(d, g, f) : b.traitsPrototype[a.VM_OPEN_GET_METHOD_PREFIX + k].call(this); + 0 < a.traceCallExecution.value && a.callWriter.leave("return " + va(d)); + return d; } - function x(a, b) { - if (a.classInfo) { - var c = W.getQualifiedName(a.classInfo.instanceInfo.name); - if (c === W.String) { - return String.asApply(null, b); + function z(b, a) { + if (b.classInfo) { + var d = T.getQualifiedName(b.classInfo.instanceInfo.name); + if (d === T.String) { + return String.asApply(null, a); } - if (c === W.Boolean) { - return Boolean.asApply(null, b); + if (d === T.Boolean) { + return Boolean.asApply(null, a); } - if (c === W.Number) { - return Number.asApply(null, b); + if (d === T.Number) { + return Number.asApply(null, a); } } - c = a.instanceConstructor; - switch(b.length) { + d = b.instanceConstructor; + switch(a.length) { case 0: - return new c; + return new d; case 1: - return new c(b[0]); + return new d(a[0]); case 2: - return new c(b[0], b[1]); + return new d(a[0], a[1]); case 3: - return new c(b[0], b[1], b[2]); + return new d(a[0], a[1], a[2]); case 4: - return new c(b[0], b[1], b[2], b[3]); + return new d(a[0], a[1], a[2], a[3]); case 5: - return new c(b[0], b[1], b[2], b[3], b[4]); + return new d(a[0], a[1], a[2], a[3], a[4]); case 6: - return new c(b[0], b[1], b[2], b[3], b[4], b[5]); + return new d(a[0], a[1], a[2], a[3], a[4], a[5]); case 7: - return new c(b[0], b[1], b[2], b[3], b[4], b[5], b[6]); + return new d(a[0], a[1], a[2], a[3], a[4], a[5], a[6]); case 8: - return new c(b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]); + return new d(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); case 9: - return new c(b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8]); + return new d(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]); case 10: - return new c(b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9]); + return new d(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]); } - for (var h = [], e = 0;e < b.length;e++) { - h[e + 1] = b[e]; + for (var g = [], f = 0;f < a.length;f++) { + g[f + 1] = a[f]; } - return new (Function.bind.asApply(c, h)); + return new (Function.bind.asApply(d, g)); } - function y(a, b, c, h) { - a = this.asGetProperty(a, b, c); - g.traceCallExecution.value && ra.enter("construct " + b + "(" + ya(h) + ") #" + ka.count(b)); - b = x(a, h); - 0 < g.traceCallExecution.value && ra.leave("return " + va(b)); - return b; + function A(b, d, g, f) { + b = this.asGetProperty(b, d, g); + a.traceCallExecution.value && a.callWriter.enter("construct " + d + "(" + xa(f) + ") #" + ia.count(d)); + d = z(b, f); + 0 < a.traceCallExecution.value && a.callWriter.leave("return " + va(d)); + return d; } - function G(a, b, c) { - return this.resolveMultinameProperty(a, b, c) in this; + function B(b, a, d) { + return this.resolveMultinameProperty(b, a, d) in this; } - function I(a, b, c) { - a = this.resolveMultinameProperty(a, b, c); - return oa(this, a); - } - function C(a, b, c) { - a = this.resolveMultinameProperty(a, b, c); - return pa(this, a); - } - function E(a, b, c) { - return this.asHasTraitProperty(a, b, c) ? !1 : delete this[this.resolveMultinameProperty(a, b, c)]; - } - function O(a, b, c) { - a = this.resolveMultinameProperty(a, b, c); - return 0 <= this.asBindings.indexOf(a); - } - function K(a) { - return this[a]; - } - function F(a, b) { - this[a] = b; - } - function J(a) { - 0 === a && ia(this, "asEnumerableKeys", this.asGetEnumerableKeys()); - for (var b = this.asEnumerableKeys;a < b.length;) { - if (this.asHasProperty(void 0, b[a], 0)) { - return a + 1; + function M(b, a, d) { + b = this.resolveMultinameProperty(b, a, d); + return ra(this, b) || 0 <= this.asBindings.indexOf(b); + } + function N(b, a, d) { + b = this.resolveMultinameProperty(b, a, d); + return wa(this, b); + } + function K(b, a, d) { + return this.asHasTraitProperty(b, a, d) ? !1 : delete this[this.resolveMultinameProperty(b, a, d)]; + } + function y(b, a, d) { + b = this.resolveMultinameProperty(b, a, d); + return 0 <= this.asBindings.indexOf(b); + } + function D(b) { + return this[b]; + } + function L(b, a) { + this[b] = a; + } + function H(b) { + 0 === b && ha(this, "asEnumerableKeys", this.asGetEnumerableKeys()); + for (var a = this.asEnumerableKeys;b < a.length;) { + if (this.asHasProperty(void 0, a[b], 0)) { + return b + 1; } - a++; + b++; } return 0; } - function A(a) { - var b = this.asEnumerableKeys; - la(b && 0 < a && a < b.length + 1); - return b[a - 1]; - } - function B(a) { - return this.asGetPublicProperty(this.asNextName(a)); - } - function z(a) { - if (na(a.object)) { - a.index = 0, a.object = null; + function J(b) { + var a = this.asEnumerableKeys; + pa(a && 0 < b && b < a.length + 1); + return a[b - 1]; + } + function C(b) { + return this.asGetPublicProperty(this.asNextName(b)); + } + function E(b) { + if (qa(b.object)) { + b.index = 0, b.object = null; } else { - var b = sa(a.object), c = b.asNextNameIndex(a.index); - if (0 < c) { - a.index = c, a.object = b; + var a = ya(b.object), d = a.asNextNameIndex(b.index); + if (0 < d) { + b.index = d, b.object = a; } else { for (;;) { - b = Object.getPrototypeOf(b); - if (!b) { - a.index = 0; - a.object = null; + a = Object.getPrototypeOf(a); + if (!a) { + b.index = 0; + b.object = null; break; } - c = b.asNextNameIndex(0); - if (0 < c) { - a.index = c; - a.object = b; + d = a.asNextNameIndex(0); + if (0 < d) { + b.index = d; + b.object = a; break; } } } } } - function P() { + function F() { if (this instanceof String || this instanceof Number) { return[]; } - for (var a = Object.keys(this), c = [], h = 0;h < a.length;h++) { - var e = a[h]; - b.isNumeric(e) ? c.push(e) : (e = W.stripPublicQualifier(e), void 0 !== e && c.push(e)); + for (var b = Object.keys(this), a = [], d = 0;d < b.length;d++) { + var g = b[d]; + c.isNumeric(g) ? a.push(g) : (g = T.stripPublicQualifier(g), void 0 !== g && a.push(g)); } - return c; + return a; } - function D(a, b) { - a || L("TypeError", k.Errors.NullPointerError, b); + function I(b, a) { + b || G("TypeError", h.Errors.NullPointerError, a); } - function L(a, c) { - for (var h = 0;h < arguments.length - 2;h++) { - } - h = b.AVM2.formatErrorMessage.apply(null, Array.prototype.slice.call(arguments, 1)); - M(g.AVM2.currentDomain(), a, h, c.code); + function G(b, d, g, f, k, e) { + d.fqn && (b = d.fqn); + var r = c.AVM2.formatErrorMessage.apply(null, Array.prototype.slice.call(arguments, 1)), m = a.AVM2.currentDomain(), w = d.code; + throw new (m.getClass(b).instanceConstructor)(r, w); } - function M(a, b, c, h) { - throw new (a.getClass(b).instanceConstructor)(c, h); + function Z(b) { + return b instanceof h.AS.ASXML || b instanceof h.AS.ASXMLList || b instanceof h.AS.ASQName || b instanceof h.AS.ASNamespace; } - function V(a, b) { - return a.coerce(b); + function Q(b, a) { + return b.coerce(a); } - function Q(a) { - return "string" === typeof a ? a : void 0 == a ? null : a + ""; + function S(b) { + return "string" === typeof b ? b : void 0 == b ? null : b + ""; } - function U(a) { - return void 0 == a ? null : "string" === typeof a || "number" === typeof a ? a : Object(a); + function O(b) { + return b instanceof Boolean ? b.valueOf() : void 0 == b ? null : "string" === typeof b || "number" === typeof b ? b : Object(b); } - function S(a, b) { - return String(a).localeCompare(String(b)); + function P(b, a) { + return String(b).localeCompare(String(a)); } - function aa(a) { - return a instanceof b.AVM2.AS.ASXML || a instanceof b.AVM2.AS.ASXMLList; + function V(b) { + return b instanceof h.AS.ASXML || b instanceof h.AS.ASXMLList; } - function $(b) { - "Object Number Boolean String Array Date RegExp".split(" ").forEach(function(a) { - wa(b[a].prototype, g.VM_NATIVE_PROTOTYPE_FLAG, !0); + function $(c) { + "Object Number Boolean String Array Date RegExp".split(" ").forEach(function(b) { + Ha(c[b].prototype, a.VM_NATIVE_PROTOTYPE_FLAG, !0); }); - ia(b.Object.prototype, "getNamespaceResolutionMap", f); - ia(b.Object.prototype, "resolveMultinameProperty", t); - ia(b.Object.prototype, "asGetProperty", m); - ia(b.Object.prototype, "asGetPublicProperty", s); - ia(b.Object.prototype, "asGetResolvedStringProperty", d); - ia(b.Object.prototype, "asSetProperty", p); - ia(b.Object.prototype, "asSetPublicProperty", c); - ia(b.Object.prototype, "asDefineProperty", q); - ia(b.Object.prototype, "asDefinePublicProperty", e); - ia(b.Object.prototype, "asCallProperty", u); - ia(b.Object.prototype, "asCallSuper", w); - ia(b.Object.prototype, "asGetSuper", h); - ia(b.Object.prototype, "asSetSuper", r); - ia(b.Object.prototype, "asCallPublicProperty", l); - ia(b.Object.prototype, "asCallResolvedStringProperty", a); - ia(b.Object.prototype, "asConstructProperty", y); - ia(b.Object.prototype, "asHasProperty", G); - ia(b.Object.prototype, "asHasPropertyInternal", G); - ia(b.Object.prototype, "asHasOwnProperty", I); - ia(b.Object.prototype, "asPropertyIsEnumerable", C); - ia(b.Object.prototype, "asHasTraitProperty", O); - ia(b.Object.prototype, "asDeleteProperty", E); - ia(b.Object.prototype, "asHasNext2", z); - ia(b.Object.prototype, "asNextName", A); - ia(b.Object.prototype, "asNextValue", B); - ia(b.Object.prototype, "asNextNameIndex", J); - ia(b.Object.prototype, "asGetEnumerableKeys", P); - ia(b.Function.prototype, "asCall", b.Function.prototype.call); - ia(b.Function.prototype, "asApply", b.Function.prototype.apply); - "Array Object Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array".split(" ").forEach(function(a) { - a in b ? (ia(b[a].prototype, "asGetNumericProperty", K), ia(b[a].prototype, "asSetNumericProperty", F)) : log(a + " was not found in globals"); + ha(c.Object.prototype, "getNamespaceResolutionMap", s); + ha(c.Object.prototype, "resolveMultinameProperty", v); + ha(c.Object.prototype, "asGetProperty", u); + ha(c.Object.prototype, "asGetPublicProperty", p); + ha(c.Object.prototype, "asGetResolvedStringProperty", l); + ha(c.Object.prototype, "asSetProperty", q); + ha(c.Object.prototype, "asSetPublicProperty", m); + ha(c.Object.prototype, "asDefineProperty", k); + ha(c.Object.prototype, "asDefinePublicProperty", n); + ha(c.Object.prototype, "asGetPropertyDescriptor", f); + ha(c.Object.prototype, "asCallProperty", b); + ha(c.Object.prototype, "asCallSuper", g); + ha(c.Object.prototype, "asGetSuper", w); + ha(c.Object.prototype, "asSetSuper", r); + ha(c.Object.prototype, "asCallPublicProperty", d); + ha(c.Object.prototype, "asCallResolvedStringProperty", e); + ha(c.Object.prototype, "asConstructProperty", A); + ha(c.Object.prototype, "asHasProperty", B); + ha(c.Object.prototype, "asHasPropertyInternal", B); + ha(c.Object.prototype, "asHasOwnProperty", M); + ha(c.Object.prototype, "asPropertyIsEnumerable", N); + ha(c.Object.prototype, "asHasTraitProperty", y); + ha(c.Object.prototype, "asDeleteProperty", K); + ha(c.Object.prototype, "asHasNext2", E); + ha(c.Object.prototype, "asNextName", J); + ha(c.Object.prototype, "asNextValue", C); + ha(c.Object.prototype, "asNextNameIndex", H); + ha(c.Object.prototype, "asGetEnumerableKeys", F); + ha(c.Function.prototype, "asCall", c.Function.prototype.call); + ha(c.Function.prototype, "asApply", c.Function.prototype.apply); + "Array Object Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array".split(" ").forEach(function(b) { + b in c ? (ha(c[b].prototype, "asGetNumericProperty", D), ha(c[b].prototype, "asSetNumericProperty", L)) : console.warn(b + " was not found in globals"); }); - b.Array.prototype.asGetProperty = function(a, b, c) { - return "number" === typeof b ? this[b] : m.call(this, a, b, c); + c.Array.prototype.asGetProperty = function(b, a, d) { + return "number" === typeof a ? this[a] : u.call(this, b, a, d); }; - b.Array.prototype.asSetProperty = function(a, b, c, h) { - if ("number" === typeof b) { - this[b] = h; + c.Array.prototype.asSetProperty = function(b, a, d, g) { + if ("number" === typeof a) { + this[a] = g; } else { - return p.call(this, a, b, c, h); + return q.call(this, b, a, d, g); } }; } - function ea(a, b) { - b && (new g.CatchBindings(new g.Scope(null, this), b)).applyTo(a, this); + function W(b, d) { + d && (new a.CatchBindings(new a.Scope(null, this), d)).applyTo(b, this); } - function Z(a, b) { - "undefined" === typeof b && (b = 0); - return Array.prototype.slice.call(a, b); + function x(b, a) { + void 0 === a && (a = 0); + return Array.prototype.slice.call(b, a); } - function v(a) { - return!a.hasBody || a.hasExceptions() && !g.compilerEnableExceptions.value || a.hasSetsDxns() || a.code.length > g.compilerMaximumMethodSize.value ? !1 : !0; + function ea(b) { + return!b.hasBody || b.hasExceptions() && !a.compilerEnableExceptions.value || b.hasSetsDxns() || b.code.length > a.compilerMaximumMethodSize.value ? !1 : !0; } - function X(a) { - return!v(a) || a.isClassInitializer || a.isScriptInitializer ? !1 : !0; + function Y(b) { + return!ea(b) || b.isClassInitializer || b.isScriptInitializer ? !1 : !0; } - function ba(a) { - if (a.hasExceptions()) { + function R(b) { + if (b.hasExceptions()) { return!1; } - a = a.holder; - a instanceof da && (a = a.instanceInfo); - if (a instanceof ja) { - switch(a.name.namespaces[0].uri) { + b = b.holder; + b instanceof fa && (b = b.instanceInfo); + if (b instanceof la) { + switch(b.name.namespaces[0].uri) { case "flash.geom": ; case "flash.events": return!0; } - switch(a.name.getOriginalName()) { + switch(b.name.getOriginalName()) { case "com.google.youtube.model.VideoData": return!0; } } return!1; } - function R(a) { - if (g.codeCaching.value) { - var b = g.CODE_CACHE[a.abc.hash]; - if (b) { - b.isInitialized || (b.isInitialized = !0); - if (b = b.methods[a.index]) { - return log("Linking CC: " + a), k.countTimeline("Code Cache Hit"), b; + function U(b) { + if (a.codeCaching.value) { + var d = a.CODE_CACHE[b.abc.hash]; + if (d) { + d.isInitialized || (d.isInitialized = !0); + if (d = d.methods[b.index]) { + return console.log("Linking CC: " + b), h.countTimeline("Code Cache Hit"), d; } - a.isInstanceInitializer || a.isClassInitializer ? k.countTimeline("Code Cache Query On Initializer") : (k.countTimeline("Code Cache MISS ON OTHER"), warn("Shouldn't MISS: " + a + " " + a.debugName)); - k.countTimeline("Code Cache Miss"); + b.isInstanceInitializer || b.isClassInitializer ? h.countTimeline("Code Cache Query On Initializer") : (h.countTimeline("Code Cache MISS ON OTHER"), console.warn("Shouldn't MISS: " + b + " " + b.debugName)); + h.countTimeline("Code Cache Miss"); } else { - warn("Cannot Find Code Cache For ABC, name: " + a.abc.name + ", hash: " + a.abc.hash), k.countTimeline("Code Cache ABC Miss"); + console.warn("Cannot Find Code Cache For ABC, name: " + b.abc.name + ", hash: " + b.abc.hash), h.countTimeline("Code Cache ABC Miss"); } } } - function H(a, c, h) { - var e = !1, d = a.parameters.map(function(a) { - void 0 !== a.value && (e = !0); - return a.value; + function ba(b, a, d) { + var g = !1, f = b.parameters.map(function(b) { + void 0 !== b.value && (g = !0); + return b.value; }); - h = h ? function(c) { - var h = this === jsGlobal ? c.global.object : this, p = Z(arguments, 1); - e && p.length < d.length && (p = p.concat(d.slice(p.length - d.length))); - return b.AVM2.Interpreter.interpretMethod(h, a, c, p); + d = d ? function(a) { + var d = this === jsGlobal ? a.global.object : this, k = x(arguments, 1); + g && k.length < f.length && (k = k.concat(f.slice(k.length - f.length))); + return c.AVM2.Interpreter.interpretMethod(d, b, a, k); } : function() { - var h = this === jsGlobal ? c.global.object : this, p = Z(arguments); - e && p.length < d.length && (p = p.concat(d.slice(arguments.length - d.length))); - return b.AVM2.Interpreter.interpretMethod(h, a, c, p); + var d = this === jsGlobal ? a.global.object : this, k = x(arguments); + g && k.length < f.length && (k = k.concat(f.slice(arguments.length - f.length))); + return c.AVM2.Interpreter.interpretMethod(d, b, a, k); }; - a.hasSetsDxns() && (h = function(a) { + b.hasSetsDxns() && (d = function(b) { return function() { - var c = b.AVM2.AS.ASXML.defaultNamespace; + var a = c.AVM2.AS.ASXML.defaultNamespace; try { - var h = a.apply(this, arguments); - b.AVM2.AS.ASXML.defaultNamespace = c; - return h; - } catch (e) { - throw b.AVM2.AS.ASXML.defaultNamespace = c, e; + var d = b.apply(this, arguments); + c.AVM2.AS.ASXML.defaultNamespace = a; + return d; + } catch (g) { + throw c.AVM2.AS.ASXML.defaultNamespace = a, g; } }; - }(h)); - h.instanceConstructor = h; - h.debugName = "Interpreter Function #" + Ga++; - return h; + }(d)); + d.instanceConstructor = d; + d.debugName = "Interpreter Function #" + Ia++; + return d; } - function Y(a, c, h, e, d) { - d = R(a); - var p; - d || (p = k.Compiler.compileMethod(a, c, h)); - c = a.name ? W.getQualifiedName(a.name) : "fn" + Aa; - a.holder && (h = "", a.holder instanceof da ? h = "static$" + a.holder.instanceInfo.name.getName() : a.holder instanceof ja ? h = a.holder.name.getName() : a.holder instanceof N && (h = "script"), c = h + "$" + c); - c = b.StringUtilities.escapeString(c); - a.verified && (c += "$V"); - e || (h = b.AVM2.Compiler.breakFilter.value) && 0 <= c.search(h) && (e = !0); - h = p.body; - e && (h = "{ debugger; \n" + h + "}"); - if (!d) { - var l = "function " + c + " (" + p.parameters.join(", ") + ") " + h - } - 1 < g.traceFunctions.value && a.trace(new T, a.abc); - a.debugTrace = function() { - a.trace(new T, a.abc); - }; - 0 < g.traceFunctions.value && log(l); - e = d || (new Function("return " + l))(); - e.debugName = "Compiled Function #" + Ha++; - return e; + function X(b, d, g, f, k) { + k = U(b); + var e; + k || (e = h.Compiler.compileMethod(b, d, g)); + d = b.name ? T.getQualifiedName(b.name) : "fn" + Ca; + b.holder && (g = "", b.holder instanceof fa ? g = "static$" + b.holder.instanceInfo.name.getName() : b.holder instanceof la ? g = b.holder.name.getName() : b.holder instanceof ca && (g = "script"), d = g + "$" + d); + d = c.StringUtilities.escapeString(d); + b.verified && (d += "$V"); + f || (g = c.AVM2.Compiler.breakFilter.value) && 0 <= d.search(g) && (f = !0); + g = e.body; + f && (g = "{ debugger; \n" + g + "}"); + if (!k) { + var r = "function " + d + " (" + e.parameters.join(", ") + ") " + g, r = r + ("//# sourceURL=fun-" + d + ".as") + } + 1 < a.traceFunctions.value && b.trace(new ma, b.abc); + b.debugTrace = function() { + b.trace(new ma, b.abc); + }; + 0 < a.traceFunctions.value && console.log(r); + f = k || (new Function("return " + r))(); + f.debugName = "Compiled Function #" + Ja++; + return f; } - function ga(a, c, h, e) { - la(!a.isNative(), "Method should have a builtin: " + a.name); - if (a.freeMethod) { - return h ? g.bindFreeMethodScope(a, c) : a.freeMethod; + function ga(b, d, g, f, k) { + void 0 === k && (k = !1); + pa(!b.isNative(), "Method should have a builtin: " + b.name); + if (b.freeMethod) { + return g ? a.bindFreeMethodScope(b, d) : b.freeMethod; } - var d; - if (d = g.checkMethodOverrides(a)) { - return la(!h), d; + var e; + if (e = a.checkMethodOverrides(b)) { + return pa(!g), e; + } + if (e = a.checkCommonMethodPatterns(b)) { + return e; } - ca(a); - d = !1; - 1 !== a.abc.applicationDomain.mode && X(a) || ba(a) || (d = !0); - var p = b.AVM2.Compiler.compileFilter.value; - p && a.name && 0 > W.getQualifiedName(a.name).search(p) && (d = !0); - d ? a.freeMethod = H(a, c, h) : (Aa++, a.freeMethod = Y(a, c, h, e, a.isInstanceInitializer)); - a.freeMethod.methodInfo = a; - return h ? g.bindFreeMethodScope(a, c) : a.freeMethod; - } - function ca(a) { - if (!a.analysis) { - a.analysis = new k.Analysis(a); - a.needsActivation() && (a.activationPrototype = new Ba(a), (new g.ActivationBindings(a)).applyTo(a.abc.applicationDomain, a.activationPrototype)); - for (var b = a.exceptions, c = 0, h = b.length;c < h;c++) { - var e = b[c]; - if (e.varName) { - var d = Object.create(ma.prototype); - d.kind = 0; - d.name = e.varName; - d.typeName = e.typeName; - d.holder = a; - e.scopeObject = new ea(a.abc.applicationDomain, d); + ja(b); + 1 !== b.abc.applicationDomain.mode && Y(b) || R(b) || (k = !0); + (e = c.AVM2.Compiler.compileFilter.value) && b.name && 0 > T.getQualifiedName(b.name).search(e) && (k = !0); + k ? b.freeMethod = ba(b, d, g) : (Ca++, b.freeMethod = X(b, d, g, f, b.isInstanceInitializer)); + b.freeMethod.methodInfo = b; + return g ? a.bindFreeMethodScope(b, d) : b.freeMethod; + } + function ja(b) { + if (!b.analysis) { + b.analysis = new h.Analysis(b); + b.needsActivation() && (b.activationPrototype = new Da(b), (new a.ActivationBindings(b)).applyTo(b.abc.applicationDomain, b.activationPrototype)); + for (var d = b.exceptions, g = 0, f = d.length;g < f;g++) { + var k = d[g]; + if (k.varName) { + var e = Object.create(na.prototype); + e.kind = 0; + e.name = k.varName; + e.typeName = k.typeName; + e.holder = b; + k.scopeObject = new W(b.abc.applicationDomain, e); } else { - e.scopeObject = new ea(void 0, void 0); + k.scopeObject = new W(void 0, void 0); } } } } - g.sealConstTraits = !1; - g.useAsAdd = !0; - var ka = new b.Metrics.Counter(!0), W = b.AVM2.ABC.Multiname, ha = b.AVM2.ABC.Namespace, da = b.AVM2.ABC.ClassInfo, ja = b.AVM2.ABC.InstanceInfo, N = b.AVM2.ABC.ScriptInfo, ma = b.AVM2.ABC.Trait, T = b.IndentingWriter, oa = b.ObjectUtilities.hasOwnProperty, pa = b.ObjectUtilities.propertyIsEnumerable, na = b.isNullOrUndefined, fa = b.ObjectUtilities.createEmptyObject, sa = b.ObjectUtilities.boxValue, xa = b.FunctionUtilities.bindSafely, la = b.Debug.assert, qa = b.ObjectUtilities.defineNonEnumerableGetterOrSetter, - ia = b.ObjectUtilities.defineNonEnumerableProperty, wa = b.ObjectUtilities.defineReadOnlyProperty, Ia = b.ObjectUtilities.defineNonEnumerableGetter, va = b.StringUtilities.toSafeString, ya = b.StringUtilities.toSafeArrayString; - g.VM_SLOTS = "asSlots"; - g.VM_LENGTH = "asLength"; - g.VM_BINDINGS = "asBindings"; - g.VM_NATIVE_PROTOTYPE_FLAG = "asIsNative"; - g.VM_OPEN_METHODS = "asOpenMethods"; - g.VM_OPEN_METHOD_PREFIX = "m"; - g.VM_MEMOIZER_PREFIX = "z"; - g.VM_OPEN_SET_METHOD_PREFIX = "s"; - g.VM_OPEN_GET_METHOD_PREFIX = "g"; - g.SAVED_SCOPE_NAME = "$SS"; - g.VM_METHOD_OVERRIDES = fa(); - var Ga = 1, Ha = 1, Aa = 0; - g.isNativePrototype = function(a) { - return Object.prototype.hasOwnProperty.call(a, g.VM_NATIVE_PROTOTYPE_FLAG); - }; - var ra = new T; - g.patch = function(a, c) { - la(b.isFunction(c)); - for (var h = 0;h < a.length;h++) { - var e = a[h]; - e.get ? qa(e.object, e.get, c, !0) : e.set ? qa(e.object, e.set, c, !1) : ia(e.object, e.name, c); - } - }; - g.applyNonMemoizedMethodTrait = function(a, c, h, e, d) { - la(e); - if (c.isMethod()) { - var p = [{object:h, name:a}, {object:h, name:g.VM_OPEN_METHOD_PREFIX + a}]; - c = g.makeTrampoline(c, e, d, p, c.methodInfo.parameters.length, ""); - var l = xa(c, h); - wa(l, g.VM_LENGTH, c.asLength); - wa(l, W.getPublicQualifiedName("prototype"), null); - ia(h, a, l); - ia(h, g.VM_OPEN_METHOD_PREFIX + a, l); - } else { - if (c.isGetter() || c.isSetter()) { - var p = [{object:h}], n = 0; - (l = c.isGetter()) ? p[0].get = a : (p[0].set = a, n = 1); - c = g.makeTrampoline(c, e, d, p, n, ""); - qa(h, a, c, l); - } else { - b.Debug.unexpected(c); + function aa(b, d, g) { + pa(d); + pa(b.isMethod() || b.isGetter() || b.isSetter()); + var f = b.methodInfo, k; + if (f.isNative()) { + if (2 <= a.traceExecution.value && console.log("Retrieving Native For Trait: " + b.holder + " " + b), (d = b.metadata) && d.native ? k = c.AVM2.AS.getNative(d.native.value[0].value) : g && (k = c.AVM2.AS.getMethodOrAccessorNative(b, g)), !k) { + return function(a) { + return function() { + c.Debug.warning("Calling undefined native method: " + b.kindName() + " " + a.holder.name + "::" + T.getQualifiedName(a.name)); + }; + }(f); } - } - }; - g.applyMemoizedMethodTrait = function(a, b, c, h, e) { - la(h, b); - if (b.isMethod()) { - var d = {value:null}, p = c.asOpenMethods, l = [{object:d, name:"value"}, {object:p, name:a}, {object:c, name:g.VM_OPEN_METHOD_PREFIX + a}]; - b = g.makeTrampoline(b, h, e, l, b.methodInfo.parameters.length, String(b.name)); - d.value = b; - p[a] = b; - ia(c, g.VM_OPEN_METHOD_PREFIX + a, b); - Ia(c, a, g.makeMemoizer(a, d)); - n(c, a); } else { - b.isGetter() ? (l = [{object:c, get:a}, {object:c, name:g.VM_OPEN_GET_METHOD_PREFIX + a}], b = g.makeTrampoline(b, h, e, l, 0, String(b.name)), ia(c, g.VM_OPEN_GET_METHOD_PREFIX + a, b), qa(c, a, b, !0)) : b.isSetter() && (l = [{object:c, set:a}, {object:c, name:g.VM_OPEN_SET_METHOD_PREFIX + a}], b = g.makeTrampoline(b, h, e, l, 0, String(b.name)), ia(c, g.VM_OPEN_SET_METHOD_PREFIX + a, b), qa(c, a, b, !1)); + 2 <= a.traceExecution.value && console.log("Creating Function For Trait: " + b.holder + " " + b), k = ga(f, d, !1, !1), pa(k); } - }; - g.getNamespaceResolutionMap = f; - g.resolveMultinameProperty = t; - g.asGetPublicProperty = s; - g.asGetProperty = m; - g.asGetResolvedStringProperty = d; - g.asCallResolvedStringProperty = a; - g.asGetResolvedStringPropertyFallback = function(a) { - a = W.getNameFromPublicQualifiedName(a); - return this.asGetProperty([ha.PUBLIC], a, 0); - }; - g.asSetPublicProperty = c; - g.forwardValueOf = new Function("", "return this." + W.VALUE_OF + ".apply(this, arguments)"); - g.forwardToString = new Function("", "return this." + W.TO_STRING + ".apply(this, arguments)"); - g.asSetProperty = p; - g.asDefinePublicProperty = e; - g.asDefineProperty = q; - g.asCallPublicProperty = l; - g.asCallProperty = u; - g.asCallSuper = w; - g.asSetSuper = r; - g.asGetSuper = h; - g.construct = x; - g.asConstructProperty = y; - g.asHasProperty = G; - g.asHasOwnProperty = I; - g.asPropertyIsEnumerable = C; - g.asDeleteProperty = E; - g.asHasTraitProperty = O; - g.asGetNumericProperty = K; - g.asSetNumericProperty = F; - g.asGetDescendants = function(a, c, h) { - b.Debug.notImplemented("asGetDescendants"); - }; - g.asNextNameIndex = J; - g.asNextName = A; - g.asNextValue = B; - g.asHasNext2 = z; - g.asGetEnumerableKeys = P; - g.asTypeOf = function(a) { - if (a) { - if (a.constructor === String) { + 3 <= a.traceExecution.value && console.log("Made Function: " + T.getQualifiedName(f.name)); + return k; + } + a.sealConstTraits = !1; + a.useAsAdd = !0; + var ia = new c.Metrics.Counter(!0), T = c.AVM2.ABC.Multiname, da = c.AVM2.ABC.Namespace, fa = c.AVM2.ABC.ClassInfo, la = c.AVM2.ABC.InstanceInfo, ca = c.AVM2.ABC.ScriptInfo, na = c.AVM2.ABC.Trait, ma = c.IndentingWriter, ra = c.ObjectUtilities.hasOwnProperty, wa = c.ObjectUtilities.propertyIsEnumerable, qa = c.isNullOrUndefined, ya = c.ObjectUtilities.boxValue, pa = c.Debug.assert, za = c.ObjectUtilities.defineNonEnumerableGetterOrSetter, ha = c.ObjectUtilities.defineNonEnumerableProperty, + Ha = c.ObjectUtilities.defineReadOnlyProperty, Ma = c.ObjectUtilities.defineNonEnumerableGetter, va = c.StringUtilities.toSafeString, xa = c.StringUtilities.toSafeArrayString; + a.VM_SLOTS = "asSlots"; + a.VM_LENGTH = "asLength"; + a.VM_BINDINGS = "asBindings"; + a.VM_NATIVE_PROTOTYPE_FLAG = "asIsNative"; + a.VM_OPEN_METHODS = "asOpenMethods"; + a.VM_OPEN_METHOD_PREFIX = "m"; + a.VM_MEMOIZER_PREFIX = "z"; + a.VM_OPEN_SET_METHOD_PREFIX = "s"; + a.VM_OPEN_GET_METHOD_PREFIX = "g"; + a.SAVED_SCOPE_NAME = "$SS"; + a.VM_METHOD_OVERRIDES = Object.create(null); + var Ia = 1, Ja = 1, Ca = 0; + a.isNativePrototype = function(b) { + return Object.prototype.hasOwnProperty.call(b, a.VM_NATIVE_PROTOTYPE_FLAG); + }; + a.traitsWriter = null; + a.callWriter = new ma; + a.patch = function(b, d) { + pa(c.isFunction(d)); + for (var g = 0;g < b.length;g++) { + var f = b[g]; + if (3 <= a.traceExecution.value) { + var k = "Patching: "; + f.name ? k += f.name : f.get ? k += "get " + f.get : f.set && (k += "set " + f.set); + a.traitsWriter && a.traitsWriter.redLn(k); + } + f.get ? za(f.object, f.get, d, !0) : f.set ? za(f.object, f.set, d, !1) : ha(f.object, f.name, d); + } + }; + a.applyMethodTrait = function(b, d, g, f) { + var k = g.trait; + pa(k); + var e = k.isMethod(), r = k.isGetter(), c = e ? a.VM_OPEN_METHOD_PREFIX : r ? a.VM_OPEN_GET_METHOD_PREFIX : a.VM_OPEN_SET_METHOD_PREFIX, m = k.methodInfo.cachedMethodOrTrampoline, w; + m ? m.isTrampoline && (w = m.patchTargets) : (k.methodInfo.isNative() ? m = aa(k, g.scope, g.natives) : (w = [], m = a.makeTrampoline(k, g.scope, g.natives, w, e ? k.methodInfo.parameters.length : 0, String(k.name))), pa(m), k.methodInfo.cachedMethodOrTrampoline = m); + w && (w.push({object:d, name:c + b}), e ? w.push({object:d.asOpenMethods, name:b}) : (g = {object:d}, r ? g.get = b : g.set = b, w.push(g))); + ha(d, c + b, m); + e ? (d.asOpenMethods[b] = m, f ? (f = k.methodInfo.cachedMemoizer, f || (m = {value:m}, w && w.push({object:m, name:"value"}), f = a.makeMemoizer(b, m), k.methodInfo.cachedMemoizer = f), Ma(d, b, f), t(d, b)) : ha(d, b, m)) : (za(d, b, m, r), f && ha(d, c + b, m)); + }; + a.getNamespaceResolutionMap = s; + a.resolveMultinameProperty = v; + a.asGetPublicProperty = p; + a.asGetProperty = u; + a.asGetResolvedStringProperty = l; + a.asCallResolvedStringProperty = e; + a.asGetResolvedStringPropertyFallback = function(b) { + b = T.getNameFromPublicQualifiedName(b); + return this.asGetProperty([da.PUBLIC], b, 0); + }; + a.asSetPublicProperty = m; + a.forwardValueOf = new Function("", "return this." + T.VALUE_OF + ".apply(this, arguments)//# sourceURL=forward-valueOf.as"); + a.forwardToString = new Function("", "return this." + T.TO_STRING + ".apply(this, arguments)//# sourceURL=forward-toString.as"); + a.asSetProperty = q; + a.asDefinePublicProperty = n; + a.asDefineProperty = k; + a.asGetPropertyDescriptor = f; + a.asCallPublicProperty = d; + a.asCallProperty = b; + a.asCallSuper = g; + a.asSetSuper = r; + a.asGetSuper = w; + a.construct = z; + a.asConstructProperty = A; + a.asHasProperty = B; + a.asHasOwnProperty = M; + a.asPropertyIsEnumerable = N; + a.asDeleteProperty = K; + a.asHasTraitProperty = y; + a.asGetNumericProperty = D; + a.asSetNumericProperty = L; + a.asGetDescendants = function(b, a, d) { + c.Debug.notImplemented("asGetDescendants"); + }; + a.asNextNameIndex = H; + a.asNextName = J; + a.asNextValue = C; + a.asHasNext2 = E; + a.asGetEnumerableKeys = F; + a.asTypeOf = function(b) { + if (b) { + if (b.constructor === String) { return "string"; } - if (a.constructor === Number) { + if (b.constructor === Number) { return "number"; } - if (a.constructor === Boolean) { + if (b.constructor === Boolean) { return "boolean"; } - if (a instanceof b.AVM2.AS.ASXML || a instanceof b.AVM2.AS.ASXMLList) { + if (b instanceof c.AVM2.AS.ASXML || b instanceof c.AVM2.AS.ASXMLList) { return "xml"; } - if (b.AVM2.AS.ASClass.isType(a)) { + if (c.AVM2.AS.ASClass.isType(b)) { return "object"; } } - return typeof a; + return typeof b; }; - g.publicizeProperties = function(a) { - for (var b = Object.keys(a), c = 0;c < b.length;c++) { - var h = b[c]; - if (!W.isPublicQualifiedName(h)) { - var e = a[h]; - a[W.getPublicQualifiedName(h)] = e; - delete a[h]; + a.publicizeProperties = function(b) { + for (var a = Object.keys(b), d = 0;d < a.length;d++) { + var g = a[d]; + if (!T.isPublicQualifiedName(g)) { + var f = b[g]; + b[T.getPublicQualifiedName(g)] = f; + delete b[g]; } } }; - g.asGetSlot = function(a, b) { - return a[a.asSlots.byID[b].name]; + a.asGetSlot = function(b, a) { + return b[b.asSlots.byID[a].name]; }; - g.asSetSlot = function(a, b, c) { - b = a.asSlots.byID[b]; - if (!b.const) { - var h = b.type; - a[b.name] = h && h.coerce ? h.coerce(c) : c; + a.asSetSlot = function(b, a, d) { + a = b.asSlots.byID[a]; + if (!a.const) { + var g = a.type; + b[a.name] = g && g.coerce ? g.coerce(d) : d; } }; - g.asCheckVectorSetNumericProperty = function(a, c, h) { - (0 > a || a > c || a === c && h || !b.isNumeric(a)) && L("RangeError", k.Errors.OutOfRangeError, a, c); + a.asCheckVectorSetNumericProperty = function(b, a, d) { + (0 > b || b > a || b === a && d || !c.isNumeric(b)) && G("RangeError", h.Errors.OutOfRangeError, b, a); }; - g.asCheckVectorGetNumericProperty = function(a, c) { - (0 > a || a >= c || !b.isNumeric(a)) && L("RangeError", k.Errors.OutOfRangeError, a, c); + a.asCheckVectorGetNumericProperty = function(b, a) { + (0 > b || b >= a || !c.isNumeric(b)) && G("RangeError", h.Errors.OutOfRangeError, b, a); }; - g.checkNullParameter = D; - g.checkParameterType = function(a, b, c) { - D(a, b); - c.isType(a) || L("TypeError", k.Errors.CheckTypeFailedError, a, c.classInfo.instanceInfo.name.getOriginalName()); + a.checkNullParameter = I; + a.checkParameterType = function(b, a, d) { + I(b, a); + d.isType(b) || G("TypeError", h.Errors.CheckTypeFailedError, b, d.classInfo.instanceInfo.name.getOriginalName()); }; - g.throwError = L; - g.throwErrorFromVM = M; - g.translateError = function(a, c) { - if (c instanceof Error) { - var h = a.getClass(c.name); - if (h) { - return new h.instanceConstructor(b.AVM2.translateErrorMessage(c)); + a.throwError = G; + a.translateError = function(b, a) { + if (a instanceof Error) { + var d = b.getClass(a.name); + if (d) { + return new d.instanceConstructor(c.AVM2.translateErrorMessage(a)); } - b.Debug.unexpected("Can't translate error: " + c); + c.Debug.unexpected("Can't translate error: " + a); } - return c; + return a; }; - g.asIsInstanceOf = function(a, b) { - return a.isInstanceOf(b); + a.asIsInstanceOf = function(b, a) { + return b.isInstanceOf(a); }; - g.asIsType = function(a, b) { - return a.isType(b); + a.asIsType = function(b, a) { + return b.isType(a); }; - g.asAsType = function(a, b) { - return a.isType(b) ? b : null; + a.asAsType = function(b, a) { + return b.isType(a) ? a : null; }; - g.asCoerceByMultiname = function(a, b, c) { - la(b.isQName()); - switch(W.getQualifiedName(b)) { - case W.Int: - return c | 0; - case W.Uint: - return c >>> 0; - case W.String: - return Q(c); - case W.Number: - return+c; - case W.Boolean: - return!!c; - case W.Object: - return U(c); - } - return V(a.abc.applicationDomain.getType(b), c); - }; - g.asCoerce = V; - g.asCoerceString = Q; - g.asCoerceInt = function(a) { - return a | 0; - }; - g.asCoerceUint = function(a) { - return a >>> 0; - }; - g.asCoerceNumber = function(a) { - return+a; - }; - g.asCoerceBoolean = function(a) { - return!!a; - }; - g.asCoerceObject = U; - g.asDefaultCompareFunction = S; - g.asCompare = function(a, c, h, e) { - b.Debug.assertNotImplemented(!(h & 4), "UNIQUESORT"); - b.Debug.assertNotImplemented(!(h & 8), "RETURNINDEXEDARRAY"); - var d = 0; - e || (e = S); - h & 1 && (a = String(a).toLowerCase(), c = String(c).toLowerCase()); - h & 16 ? (a = b.toNumber(a), c = b.toNumber(c), d = a < c ? -1 : a > c ? 1 : 0) : d = e(a, c); - h & 2 && (d *= -1); - return d; + a.escapeXMLAttribute = function(b) { + return h.AS.escapeAttributeValue(b); + }; + a.escapeXMLElement = function(b) { + return h.AS.escapeElementValue(b); + }; + a.asEquals = function(b, a) { + return Z(b) ? b.equals(a) : Z(a) ? a.equals(b) : b == a; + }; + a.asCoerceByMultiname = function(b, a, d) { + pa(a.isQName()); + switch(T.getQualifiedName(a)) { + case T.Int: + return d | 0; + case T.Uint: + return d >>> 0; + case T.String: + return S(d); + case T.Number: + return+d; + case T.Boolean: + return!!d; + case T.Object: + return O(d); + } + return Q(b.abc.applicationDomain.getType(a), d); + }; + a.asCoerce = Q; + a.asCoerceString = S; + a.asCoerceInt = function(b) { + return b | 0; + }; + a.asCoerceUint = function(b) { + return b >>> 0; + }; + a.asCoerceNumber = function(b) { + return+b; + }; + a.asCoerceBoolean = function(b) { + return!!b; + }; + a.asCoerceObject = O; + a.asDefaultCompareFunction = P; + a.asCompare = function(b, a, d, g) { + c.Debug.assertNotImplemented(!(d & 4), "UNIQUESORT"); + c.Debug.assertNotImplemented(!(d & 8), "RETURNINDEXEDARRAY"); + var f = 0; + g || (g = P); + d & 1 && (b = String(b).toLowerCase(), a = String(a).toLowerCase()); + d & 16 ? (b = c.toNumber(b), a = c.toNumber(a), f = b < a ? -1 : b > a ? 1 : 0) : f = g(b, a); + d & 2 && (f *= -1); + return f; }; - g.asAdd = function(a, b) { - return "string" === typeof a || "string" === typeof b ? String(a) + String(b) : a + b; + a.asAdd = function(b, a) { + return "string" === typeof b || "string" === typeof a ? String(b) + String(a) : V(b) && V(a) ? c.AVM2.AS.ASXMLList.addXML(b, a) : b + a; }; - g.getDescendants = function(a, b) { - if (!aa(a)) { + a.getDescendants = function(b, a) { + if (!V(b)) { throw "Not XML object in getDescendants"; } - return a.descendants(b); + return b.descendants(a); }; - g.checkFilter = function(a) { - if (!a.class || !aa(a)) { + a.checkFilter = function(b) { + if (!b.class || !V(b)) { throw "TypeError operand of childFilter not of XML type"; } - return a; + return b; }; - g.initializeGlobalObject = $; + a.initializeGlobalObject = $; $(jsGlobal); - g.nameInTraits = function(a, b) { - if (a.hasOwnProperty(g.VM_BINDINGS) && a.hasOwnProperty(b)) { + a.nameInTraits = function(b, d) { + if (b.hasOwnProperty(a.VM_BINDINGS) && b.hasOwnProperty(d)) { return!0; } - var c = Object.getPrototypeOf(a); - return c.hasOwnProperty(g.VM_BINDINGS) && c.hasOwnProperty(b); + var g = Object.getPrototypeOf(b); + return g.hasOwnProperty(a.VM_BINDINGS) && g.hasOwnProperty(d); }; - g.CatchScopeObject = ea; + a.CatchScopeObject = W; var ta = function() { - function a(b) { - this.scriptInfo = b; - b.global = this; - this.scriptBindings = new g.ScriptBindings(b, new g.Scope(null, this, !1)); - this.scriptBindings.applyTo(b.abc.applicationDomain, this); - b.loaded = !0; + function b(d) { + this.scriptInfo = d; + d.global = this; + this.scriptBindings = new a.ScriptBindings(d, new a.Scope(null, this, !1)); + this.scriptBindings.applyTo(d.abc.applicationDomain, this); + d.loaded = !0; } - a.prototype.toString = function() { + b.prototype.toString = function() { return "[object global]"; }; - a.prototype.isExecuted = function() { + b.prototype.isExecuted = function() { return this.scriptInfo.executed; }; - a.prototype.isExecuting = function() { + b.prototype.isExecuting = function() { return this.scriptInfo.executing; }; - a.prototype.ensureExecuted = function() { - b.AVM2.Runtime.ensureScriptIsExecuted(this.scriptInfo); + b.prototype.ensureExecuted = function() { + c.AVM2.Runtime.ensureScriptIsExecuted(this.scriptInfo); }; - return a; + return b; }(); - g.Global = ta; - ia(ta.prototype, W.getPublicQualifiedName("toString"), function() { + a.Global = ta; + ha(ta.prototype, T.getPublicQualifiedName("toString"), function() { return this.toString(); }); ta = function() { - function a(b) { + function b(a) { this._isLazyInitializer = !0; - la(!b.asLazyInitializer); - this._target = b; + pa(!a.asLazyInitializer); + this._target = a; this._resolved = null; } - a.create = function(b) { - return b.asLazyInitializer ? b.asLazyInitializer : b.asLazyInitializer = new a(b); + b.create = function(a) { + return a.asLazyInitializer ? a.asLazyInitializer : a.asLazyInitializer = new b(a); }; - a.prototype.resolve = function() { + b.prototype.resolve = function() { if (this._resolved) { return this._resolved; } - if (this._target instanceof N) { - var a = this._target; - g.ensureScriptIsExecuted(a, "Lazy Initializer"); - return this._resolved = a.global; + if (this._target instanceof ca) { + var b = this._target; + a.ensureScriptIsExecuted(b, "Lazy Initializer"); + return this._resolved = b.global; } - if (this._target instanceof da) { - return a = this._target, a.classObject ? this._resolved = a.classObject : this._resolved = a.abc.applicationDomain.getProperty(a.instanceInfo.name, !1, !1); + if (this._target instanceof fa) { + return b = this._target, b.classObject ? this._resolved = b.classObject : this._resolved = b.abc.applicationDomain.getProperty(b.instanceInfo.name, !1, !1); } - b.Debug.notImplemented(String(this._target)); + c.Debug.notImplemented(String(this._target)); }; - return a; + return b; }(); - g.LazyInitializer = ta; - g.forEachPublicProperty = function(a, c, h) { - if (!a.asBindings) { - for (var e in a) { - c.call(h, e, a[e]); + a.LazyInitializer = ta; + a.forEachPublicProperty = function(b, a, d) { + if (!b.asBindings) { + for (var g in b) { + a.call(d, g, b[g]); } } else { - for (e in a) { - if (b.isNumeric(e)) { - c.call(h, e, a[e]); + for (g in b) { + if (c.isNumeric(g)) { + a.call(d, g, b[g]); } else { - if (W.isPublicQualifiedName(e) && 0 > a.asBindings.indexOf(e)) { - var d = W.stripPublicQualifier(e); - c.call(h, d, a[e]); + if (T.isPublicQualifiedName(g) && 0 > b.asBindings.indexOf(g)) { + var f = T.stripPublicQualifier(g); + a.call(d, f, b[g]); } } } } }; - g.wrapJSObject = function(a) { - var b = Object.create(a), c; - for (c in a) { - Object.defineProperty(b, W.getPublicQualifiedName(c), function(a, b) { + a.wrapJSObject = function(b) { + var a = Object.create(b), d; + for (d in b) { + Object.defineProperty(a, T.getPublicQualifiedName(d), function(b, a) { return{get:function() { - return a[b]; - }, set:function(c) { - a[b] = c; + return b[a]; + }, set:function(d) { + b[a] = d; }, enumerable:!0}; - }(a, c)); + }(b, d)); } - return b; + return a; }; - g.asCreateActivation = function(a) { - return Object.create(a.activationPrototype); + a.asCreateActivation = function(b) { + return Object.create(b.activationPrototype); }; ta = function() { - function a() { + function b() { } - a.updateTraits = function(c) { - for (var h = 0;h < c.length;h++) { - var e = c[h], d = e.name.name, e = e.name.getNamespace(); - e.isDynamic() || (a.hasNonDynamicNamespaces[d] = !0, a.wasResolved[d] && b.Debug.notImplemented("We have to the undo the optimization, " + d + " can now bind to " + e)); + b.updateTraits = function(a) { + for (var d = 0;d < a.length;d++) { + var g = a[d], f = g.name.name, g = g.name.getNamespace(); + g.isDynamic() || (b.hasNonDynamicNamespaces[f] = !0, b.wasResolved[f] && c.Debug.notImplemented("We have to the undo the optimization, " + f + " can now bind to " + g)); } }; - a.loadAbc = function(b) { - if (g.globalMultinameAnalysis.value) { - var c = b.scripts, h = b.classes; - b = b.methods; - for (var e = 0;e < c.length;e++) { - a.updateTraits(c[e].traits); + b.loadAbc = function(d) { + if (a.globalMultinameAnalysis.value) { + var g = d.scripts, f = d.classes; + d = d.methods; + for (var k = 0;k < g.length;k++) { + b.updateTraits(g[k].traits); } - for (e = 0;e < h.length;e++) { - a.updateTraits(h[e].traits), a.updateTraits(h[e].instanceInfo.traits); + for (k = 0;k < f.length;k++) { + b.updateTraits(f[k].traits), b.updateTraits(f[k].instanceInfo.traits); } - for (e = 0;e < b.length;e++) { - b[e].traits && a.updateTraits(b[e].traits); + for (k = 0;k < d.length;k++) { + d[k].traits && b.updateTraits(d[k].traits); } } }; - a.resolveMultiname = function(b) { - var c = b.name; - if (!a.hasNonDynamicNamespaces[c]) { - return a.wasResolved[c] = !0, new W([ha.PUBLIC], b.name); + b.resolveMultiname = function(a) { + var d = a.name; + if (!b.hasNonDynamicNamespaces[d]) { + return b.wasResolved[d] = !0, new T([da.PUBLIC], a.name, 0); } }; - a.hasNonDynamicNamespaces = fa(); - a.wasResolved = fa(); - return a; + b.hasNonDynamicNamespaces = Object.create(null); + b.wasResolved = Object.create(null); + return b; }(); - g.GlobalMultinameResolver = ta; - var Ba = function() { - return function(a) { - this.methodInfo = a; + a.GlobalMultinameResolver = ta; + var Da = function() { + return function(b) { + this.methodInfo = b; }; }(); - g.ActivationInfo = Ba; - g.HasNext2Info = function() { - return function(a, b) { - this.object = a; - this.index = b; + a.ActivationInfo = Da; + a.HasNext2Info = function() { + return function(b, a) { + this.object = b; + this.index = a; }; }(); - g.sliceArguments = Z; - g.canCompile = v; - g.shouldCompile = X; - g.forceCompile = ba; - g.CODE_CACHE = fa(); - g.searchCodeCache = R; - g.createInterpretedFunction = H; - g.debugName = function(a) { - return b.isFunction(a) ? a.debugName : a; - }; - g.createCompiledFunction = Y; - g.createFunction = ga; - g.ensureFunctionIsInitialized = ca; - g.getTraitFunction = function(a, c, h) { - la(c); - la(a.isMethod() || a.isGetter() || a.isSetter()); - var e = a.methodInfo, d; - if (e.isNative()) { - if ((c = a.metadata) && c.native ? d = b.AVM2.AS.getNative(c.native.value[0].value) : h && (d = b.AVM2.AS.getMethodOrAccessorNative(a, h)), !d) { - return b.Debug.warning("No native method for: " + a.kindName() + " " + e.holder + "::" + W.getQualifiedName(e.name) + ", make sure you've got the static keyword for static methods."), function(c) { - return function() { - b.Debug.warning("Calling undefined native method: " + a.kindName() + " " + c.holder.name + "::" + W.getQualifiedName(c.name)); - }; - }(e); - } - } else { - 2 <= g.traceExecution.value && log("Creating Function For Trait: " + a.holder + " " + a), d = ga(e, c, !1, !1), la(d); - } - 3 <= g.traceExecution.value && log("Made Function: " + W.getQualifiedName(e.name)); + a.sliceArguments = x; + a.canCompile = ea; + a.shouldCompile = Y; + a.forceCompile = R; + a.CODE_CACHE = Object.create(null); + a.searchCodeCache = U; + a.createInterpretedFunction = ba; + a.debugName = function(b) { + return c.isFunction(b) ? b.debugName : b; + }; + a.createCompiledFunction = X; + a.createFunction = ga; + a.ensureFunctionIsInitialized = ja; + a.getTraitFunction = aa; + a.createClass = function(b, d, g) { + var f = b.instanceInfo, k = b.abc.applicationDomain, e = T.getName(f.name); + h.enterTimeline("createClass", {className:e, classInfo:b}); + a.traceExecution.value && console.log("Creating " + (f.isInterface() ? "Interface" : "Class") + ": " + e + (b.native ? " replaced with native " + b.native.cls : "")); + d = f.isInterface() ? c.AVM2.AS.createInterface(b) : c.AVM2.AS.createClass(b, d, g); + a.traceClasses.value && (k.loadedClasses.push(d), k.traceLoadedClasses()); + if (f.isInterface()) { + return h.leaveTimeline(), d; + } + k.onMessage.notify1("classCreated", d); + b.classObject = d; + a.traceExecution.value && console.log("Running " + (f.isInterface() ? "Interface" : "Class") + ": " + e + " Static Constructor"); + h.enterTimeline("staticInitializer"); + ga(b.init, g, !1, !1, !0).call(d); + h.leaveTimeline(); + a.traceExecution.value && console.log("Done With Static Constructor"); + a.sealConstTraits && this.sealConstantTraits(d, b.traits); + h.leaveTimeline(); return d; }; - g.createClass = function(a, c, h) { - var e = a.instanceInfo, d = a.abc.applicationDomain, p = W.getName(e.name); - k.enterTimeline("createClass", {className:p, classInfo:a}); - g.traceExecution.value && log("Creating " + (e.isInterface() ? "Interface" : "Class") + ": " + p + (a.native ? " replaced with native " + a.native.cls : "")); - c = e.isInterface() ? b.AVM2.AS.createInterface(a) : b.AVM2.AS.createClass(a, c, h); - g.traceClasses.value && (d.loadedClasses.push(c), d.traceLoadedClasses()); - if (e.isInterface()) { - return k.leaveTimeline(), c; - } - d.onMessage.notify1("classCreated", c); - a.classObject = c; - g.traceExecution.value && log("Running " + (e.isInterface() ? "Interface" : "Class") + ": " + p + " Static Constructor"); - k.enterTimeline("staticInitializer"); - ga(a.init, h, !1, !1).call(c); - k.leaveTimeline(); - g.traceExecution.value && log("Done With Static Constructor"); - g.sealConstTraits && this.sealConstantTraits(c, a.traits); - k.leaveTimeline(); - return c; - }; - g.sealConstantTraits = function(a, b) { - for (var c = 0, h = b.length;c < h;c++) { - var e = b[c]; - e.isConst() && (e = W.getQualifiedName(e.name), function(b, c) { - Object.defineProperty(a, b, {configurable:!1, enumerable:!1, get:function() { - return c; + a.sealConstantTraits = function(b, a) { + for (var d = 0, g = a.length;d < g;d++) { + var f = a[d]; + f.isConst() && (f = T.getQualifiedName(f.name), function(a, d) { + Object.defineProperty(b, a, {configurable:!1, enumerable:!1, get:function() { + return d; }, set:function() { - M(g.AVM2.currentDomain(), "ReferenceError", "Illegal write to read-only property " + b + ".", 0); + G("ReferenceError", h.Errors.ConstWriteError, a); }}); - }(e, a[e])); + }(f, b[f])); } }; - g.applyType = function(a, c, h) { - c = c.classInfo.instanceInfo.name.name; - if ("Vector" === c) { - la(1 === h.length); - h = h[0]; - if (!na(h)) { - switch(c = h.classInfo.instanceInfo.name.name.toLowerCase(), c) { + a.applyType = function(b, a, d) { + a = a.classInfo.instanceInfo.name.name; + if ("Vector" === a) { + pa(1 === d.length); + d = d[0]; + if (!qa(d)) { + switch(a = d.classInfo.instanceInfo.name.name.toLowerCase(), a) { case "number": - c = "double"; + a = "double"; case "int": ; case "uint": ; case "double": - return a.abc.applicationDomain.getClass("packageInternal __AS3__.vec.Vector$" + c); + return b.abc.applicationDomain.getClass("packageInternal __AS3__.vec.Vector$" + a); } } - return a.abc.applicationDomain.getClass("packageInternal __AS3__.vec.Vector$object").applyType(h); + return b.abc.applicationDomain.getClass("packageInternal __AS3__.vec.Vector$object").applyType(d); } - b.Debug.notImplemented(c); + c.Debug.notImplemented(a); }; - g.createName = function(a, b) { - return new W(a, b); - }; - })(k.Runtime || (k.Runtime = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -var CC = Shumway.AVM2.Runtime.CODE_CACHE, HasNext2Info = Shumway.AVM2.Runtime.HasNext2Info, asCreateActivation = Shumway.AVM2.Runtime.asCreateActivation, asIsInstanceOf = Shumway.AVM2.Runtime.asIsInstanceOf, asIsType = Shumway.AVM2.Runtime.asIsType, asAsType = Shumway.AVM2.Runtime.asAsType, asTypeOf = Shumway.AVM2.Runtime.asTypeOf, asCoerceByMultiname = Shumway.AVM2.Runtime.asCoerceByMultiname, asCoerce = Shumway.AVM2.Runtime.asCoerce, asCoerceString = Shumway.AVM2.Runtime.asCoerceString, asCoerceInt = -Shumway.AVM2.Runtime.asCoerceInt, asCoerceUint = Shumway.AVM2.Runtime.asCoerceUint, asCoerceNumber = Shumway.AVM2.Runtime.asCoerceNumber, asCoerceBoolean = Shumway.AVM2.Runtime.asCoerceBoolean, asCoerceObject = Shumway.AVM2.Runtime.asCoerceObject, asCompare = Shumway.AVM2.Runtime.asCompare, asAdd = Shumway.AVM2.Runtime.asAdd, applyType = Shumway.AVM2.Runtime.applyType, asGetSlot = Shumway.AVM2.Runtime.asGetSlot, asSetSlot = Shumway.AVM2.Runtime.asSetSlot, asHasNext2 = Shumway.AVM2.Runtime.asHasNext2, -getDescendants = Shumway.AVM2.Runtime.getDescendants, checkFilter = Shumway.AVM2.Runtime.checkFilter, sliceArguments = Shumway.AVM2.Runtime.sliceArguments, createFunction = Shumway.AVM2.Runtime.createFunction, createName = Shumway.AVM2.Runtime.createName; -(function(b) { - (function(k) { - (function(g) { - var f = b.AVM2.ABC.Multiname, t = b.ObjectUtilities.createEmptyObject, s = b.Debug.assert, m = b.ObjectUtilities.boxValue, d = function() { - function a(a, d, p) { - "undefined" === typeof p && (p = !1); - this.parent = a; - this.object = m(d); - s(b.isObject(this.object)); - this.global = a ? a.global : this; - this.isWith = p; - this.cache = t(); - } + a.createName = function(b, a, d) { + void 0 === a && (a = "*"); + return new T(b, a, d); + }; + })(h.Runtime || (h.Runtime = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +throwError = Shumway.AVM2.Runtime.throwError; +var CC = Shumway.AVM2.Runtime.CODE_CACHE, HasNext2Info = Shumway.AVM2.Runtime.HasNext2Info, asCreateActivation = Shumway.AVM2.Runtime.asCreateActivation, asIsInstanceOf = Shumway.AVM2.Runtime.asIsInstanceOf, asIsType = Shumway.AVM2.Runtime.asIsType, asAsType = Shumway.AVM2.Runtime.asAsType, asEquals = Shumway.AVM2.Runtime.asEquals, asTypeOf = Shumway.AVM2.Runtime.asTypeOf, asCoerceByMultiname = Shumway.AVM2.Runtime.asCoerceByMultiname, asCoerce = Shumway.AVM2.Runtime.asCoerce, asCoerceString = Shumway.AVM2.Runtime.asCoerceString, +asCoerceInt = Shumway.AVM2.Runtime.asCoerceInt, asCoerceUint = Shumway.AVM2.Runtime.asCoerceUint, asCoerceNumber = Shumway.AVM2.Runtime.asCoerceNumber, asCoerceBoolean = Shumway.AVM2.Runtime.asCoerceBoolean, asCoerceObject = Shumway.AVM2.Runtime.asCoerceObject, asCompare = Shumway.AVM2.Runtime.asCompare, asAdd = Shumway.AVM2.Runtime.asAdd, applyType = Shumway.AVM2.Runtime.applyType, escapeXMLAttribute = Shumway.AVM2.Runtime.escapeXMLAttribute, escapeXMLElement = Shumway.AVM2.Runtime.escapeXMLElement, +asGetSlot = Shumway.AVM2.Runtime.asGetSlot, asSetSlot = Shumway.AVM2.Runtime.asSetSlot, asHasNext2 = Shumway.AVM2.Runtime.asHasNext2, getDescendants = Shumway.AVM2.Runtime.getDescendants, checkFilter = Shumway.AVM2.Runtime.checkFilter, sliceArguments = Shumway.AVM2.Runtime.sliceArguments, createFunction = Shumway.AVM2.Runtime.createFunction, createName = Shumway.AVM2.Runtime.createName; +(function(c) { + (function(h) { + (function(a) { + var s = c.AVM2.ABC.Multiname, v = c.Debug.assert, p = c.ObjectUtilities.boxValue, u = function() { + function a(e, m, l) { + void 0 === l && (l = !1); + this.parent = e; + this.object = p(m); + v(c.isObject(this.object)); + this.global = e ? e.global : this; + this.isWith = l; + this.cache = Object.create(null); + } a.prototype.findDepth = function(a) { - for (var b = this, d = 0;b;) { - if (b.object === a) { - return d; + for (var c = this, l = 0;c;) { + if (c.object === a) { + return l; } - d++; - b = b.parent; + l++; + c = c.parent; } return-1; }; a.prototype.getScopeObjects = function() { - for (var a = [], b = this;b;) { - a.unshift(b.object), b = b.parent; + for (var a = [], c = this;c;) { + a.unshift(c.object), c = c.parent; } return a; }; - a.prototype.findScopeProperty = function(a, d, p, e, q, l) { - k.countTimeline("findScopeProperty"); - var g, w; - w = a ? 1 < a.length ? a.runtimeId + "$" + d : a[0].qualifiedName + "$" + d : d; - if (!l && (g = this.cache[w])) { - return g; + a.prototype.findScopeProperty = function(a, m, l, q, n, k) { + h.countTimeline("findScopeProperty"); + var f, d; + d = a ? 1 < a.length ? a.runtimeId + "$" + m : a[0].qualifiedName + "$" + m : m; + if (!k && (f = this.cache[d])) { + return f; } - if (this.object.asHasPropertyInternal(a, d, p)) { - return this.isWith ? this.object : this.cache[w] = this.object; + if (this.object.asHasPropertyInternal(a, m, l)) { + return this.isWith ? this.object : this.cache[d] = this.object; } if (this.parent) { - return this.cache[w] = this.parent.findScopeProperty(a, d, p, e, q, l); + return this.cache[d] = this.parent.findScopeProperty(a, m, l, q, n, k); } - if (l) { + if (k) { return null; } - if (g = e.abc.applicationDomain.findDomainProperty(new f(a, d, p), q, !0)) { - return g; + if (f = q.abc.applicationDomain.findDomainProperty(new s(a, m, l), n, !0)) { + return f; } - q && b.Debug.unexpected("Cannot find property " + d); + n && c.Debug.unexpected("Cannot find property " + m); return this.global.object; }; return a; }(); - g.Scope = d; - g.bindFreeMethodScope = function(a, b) { - var d = a.freeMethod; - if (a.lastBoundMethod && a.lastBoundMethod.scope === b) { + a.Scope = u; + a.bindFreeMethodScope = function(a, e) { + var c = a.freeMethod; + if (a.lastBoundMethod && a.lastBoundMethod.scope === e) { return a.lastBoundMethod.boundMethod; } - s(d, "There should already be a cached method."); - var p, e = b.global.object; + v(c, "There should already be a cached method."); + var t, q = e.global.object; if (!a.hasOptional() && !a.needsArguments() && !a.needsRest()) { switch(a.parameters.length) { case 0: - p = function() { - return d.call(this === jsGlobal ? e : this, b); + t = function() { + return c.call(this === jsGlobal ? q : this, e); }; break; case 1: - p = function(a) { - return d.call(this === jsGlobal ? e : this, b, a); + t = function(a) { + return c.call(this === jsGlobal ? q : this, e, a); }; break; case 2: - p = function(a, p) { - return d.call(this === jsGlobal ? e : this, b, a, p); + t = function(a, k) { + return c.call(this === jsGlobal ? q : this, e, a, k); }; break; case 3: - p = function(a, p, f) { - return d.call(this === jsGlobal ? e : this, b, a, p, f); + t = function(a, k, f) { + return c.call(this === jsGlobal ? q : this, e, a, k, f); }; } } - p || (k.countTimeline("Bind Scope - Slow Path"), p = function() { - Array.prototype.unshift.call(arguments, b); - return d.asApply(this === jsGlobal ? b.global.object : this, arguments); + t || (h.countTimeline("Bind Scope - Slow Path"), t = function() { + Array.prototype.unshift.call(arguments, e); + return c.asApply(this === jsGlobal ? e.global.object : this, arguments); }); - p.methodInfo = a; - p.instanceConstructor = p; - a.lastBoundMethod = {scope:b, boundMethod:p}; - return p; + t.methodInfo = a; + t.instanceConstructor = t; + a.lastBoundMethod = {scope:e, boundMethod:t}; + return t; }; - })(k.Runtime || (k.Runtime = {})); - })(b.AVM2 || (b.AVM2 = {})); + })(h.Runtime || (h.Runtime = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); var Scope = Shumway.AVM2.Runtime.Scope; -(function(b) { - (function(k) { - (function(g) { - var f = b.AVM2.ABC.Multiname, t = b.AVM2.ABC.Trait, s = b.ObjectUtilities.hasOwnProperty, m = b.ObjectUtilities.createMap, d = b.ObjectUtilities.cloneObject, a = b.ObjectUtilities.copyProperties, c = b.ObjectUtilities.createEmptyObject, n = b.Debug.assert, p = b.ObjectUtilities.defineNonEnumerableProperty, e = b.ObjectUtilities.defineNonEnumerableGetter, q = b.FunctionUtilities.makeForwardingGetter, l = b.ArrayUtilities.pushUnique, u = function() { - function a(b) { - this.trait = b; +(function(c) { + (function(h) { + (function(a) { + var s = c.AVM2.ABC.Multiname, h = c.AVM2.ABC.Trait, p = c.ObjectUtilities.hasOwnProperty, u = c.ObjectUtilities.createMap, l = c.ObjectUtilities.cloneObject, e = c.ObjectUtilities.copyProperties, m = c.Debug.assert, t = c.ObjectUtilities.defineNonEnumerableProperty, q = c.ObjectUtilities.defineNonEnumerableGetter, n = c.FunctionUtilities.makeForwardingGetter, k = c.ArrayUtilities.pushUnique, f = function() { + function b(a) { + this.trait = a; } - a.getKey = function(b, c) { - var h = b; - c.isGetter() ? h = a.GET_PREFIX + b : c.isSetter() && (h = a.SET_PREFIX + b); - return h; + b.getKey = function(a, d) { + var g = a; + d.isGetter() ? g = b.GET_PREFIX + a : d.isSetter() && (g = b.SET_PREFIX + a); + return g; }; - a.prototype.toString = function() { + b.prototype.toString = function() { return String(this.trait); }; - a.SET_PREFIX = "set "; - a.GET_PREFIX = "get "; - a.KEY_PREFIX_LENGTH = 4; - return a; + b.SET_PREFIX = "set "; + b.GET_PREFIX = "get "; + b.KEY_PREFIX_LENGTH = 4; + return b; }(); - g.Binding = u; - var w = function() { - return function(a, b, c, h) { - this.name = a; - this.isConst = b; - this.type = c; - this.trait = h; + a.Binding = f; + var d = function() { + return function(b, a, d, g) { + this.name = b; + this.isConst = a; + this.type = d; + this.trait = g; }; }(); - g.SlotInfo = w; - var r = function() { + a.SlotInfo = d; + var b = function() { return function() { - this.byID = m(); - this.byQN = m(); + this.byID = u(); + this.byQN = u(); }; }(); - g.SlotInfoMap = r; - var h = function() { - function a() { - this.map = m(); + a.SlotInfoMap = b; + var g = function() { + function g() { + this.map = u(); this.slots = []; this.nextSlotId = 1; } - a.prototype.assignNextSlot = function(a) { - n(a instanceof t); - n(a.isSlot() || a.isConst() || a.isClass()); - a.slotId ? this.nextSlotId = a.slotId + 1 : a.slotId = this.nextSlotId++; - n(!this.slots[a.slotId], "Trait slot already taken."); - this.slots[a.slotId] = a; - }; - a.prototype.trace = function(a) { - a.enter("Bindings"); - for (var b in this.map) { - var c = this.map[b]; - a.writeLn(c.trait.kindName() + ": " + b + " -> " + c); - } - a.leaveAndEnter(); - a.writeArray(this.slots); - a.outdent(); - }; - a.prototype.applyTo = function(a, b, c) { - "undefined" === typeof c && (c = !1); - c || (n(!s(b, g.VM_SLOTS), "Already has VM_SLOTS."), n(!s(b, g.VM_BINDINGS), "Already has VM_BINDINGS."), n(!s(b, g.VM_OPEN_METHODS), "Already has VM_OPEN_METHODS."), p(b, g.VM_SLOTS, new r), p(b, g.VM_BINDINGS, []), p(b, g.VM_OPEN_METHODS, m()), p(b, "bindings", this), p(b, "resolutionMap", [])); - G && G.greenLn("Applying Traits" + (c ? " (Append)" : "")); - for (var h in this.map) { - var d = this.map[h]; - c = d.trait; - var x = f.getQualifiedName(c.name); - if (c.isSlot() || c.isConst() || c.isClass()) { - d = void 0; - if (c.isSlot() || c.isConst()) { - if (c.hasDefaultValue) { - d = c.value; + g.prototype.assignNextSlot = function(b) { + m(b instanceof h); + m(b.isSlot() || b.isConst() || b.isClass()); + b.slotId ? this.nextSlotId = b.slotId + 1 : b.slotId = this.nextSlotId++; + m(!this.slots[b.slotId], "Trait slot already taken."); + this.slots[b.slotId] = b; + }; + g.prototype.trace = function(b) { + b.enter("Bindings"); + for (var a in this.map) { + var d = this.map[a]; + b.writeLn(d.trait.kindName() + ": " + a + " -> " + d); + } + b.leaveAndEnter(); + b.writeArray(this.slots); + b.outdent(); + }; + g.prototype.applyTo = function(g, e, r) { + void 0 === r && (r = !1); + r || (m(!p(e, a.VM_SLOTS), "Already has VM_SLOTS."), m(!p(e, a.VM_BINDINGS), "Already has VM_BINDINGS."), m(!p(e, a.VM_OPEN_METHODS), "Already has VM_OPEN_METHODS."), t(e, a.VM_SLOTS, new b), t(e, a.VM_BINDINGS, []), t(e, a.VM_OPEN_METHODS, u()), t(e, "bindings", this), t(e, "resolutionMap", [])); + z && z.greenLn("Applying Traits" + (r ? " (Append)" : "")); + for (var c in this.map) { + var l = this.map[c]; + r = l.trait; + var A = s.getQualifiedName(r.name); + if (r.isSlot() || r.isConst() || r.isClass()) { + l = void 0; + if (r.isSlot() || r.isConst()) { + if (r.hasDefaultValue) { + l = r.value; } else { - if (c.typeName) { - var I = a.findClassInfo(c.typeName); - I && (d = I.defaultValue); + if (r.typeName) { + var h = g.findClassInfo(r.typeName); + h && (l = h.defaultValue); } } } - h !== x ? (G && G.yellowLn("Binding Trait: " + h + " -> " + x), e(b, h, q(x)), l(b.asBindings, h)) : (G && G.greenLn("Applying Trait " + c.kindName() + ": " + c), p(b, x, d), l(b.asBindings, x), d = new w(x, c.isConst(), c.typeName ? a.getProperty(c.typeName, !1, !1) : null, c), b.asSlots.byID[c.slotId] = d, b.asSlots.byQN[x] = d); + c !== A ? (z && z.yellowLn("Binding Trait: " + c + " -> " + A), q(e, c, n(A)), k(e.asBindings, c)) : (z && z.greenLn("Applying Trait " + r.kindName() + ": " + r), t(e, A, l), k(e.asBindings, A), l = new d(A, r.isConst(), r.typeName ? g.getProperty(r.typeName, !1, !1) : null, r), e.asSlots.byID[r.slotId] = l, e.asSlots.byQN[A] = l); } else { - if (c.isMethod() || c.isGetter() || c.isSetter()) { - if (c.isGetter() || c.isSetter()) { - h = h.substring(u.KEY_PREFIX_LENGTH); - } - h !== x ? G && G.yellowLn("Binding Trait: " + h + " -> " + x) : G && G.greenLn("Applying Trait " + c.kindName() + ": " + c); - l(b.asBindings, h); - this instanceof y ? (k.enterTimeline("applyNonMemoizedMethodTrait"), g.applyNonMemoizedMethodTrait(h, c, b, d.scope, d.natives)) : (k.enterTimeline("applyMemoizedMethodTrait"), g.applyMemoizedMethodTrait(h, c, b, d.scope, d.natives)); - k.leaveTimeline(); + if (r.isMethod() || r.isGetter() || r.isSetter()) { + if (r.isGetter() || r.isSetter()) { + c = c.substring(f.KEY_PREFIX_LENGTH); + } + c !== A ? z && z.yellowLn("Binding Trait: " + c + " -> " + A) : z && z.greenLn("Applying Trait " + r.kindName() + ": " + r); + k(e.asBindings, c); + a.applyMethodTrait(c, e, l, !(this instanceof w)); } } } }; - return a; + return g; }(); - g.Bindings = h; - var x = function(a) { - function b(c) { - a.call(this); - n(c.needsActivation()); - this.methodInfo = c; - c = c.traits; - for (var h = 0;h < c.length;h++) { - var e = c[h]; - n(e.isSlot() || e.isConst(), "Only slot or constant traits are allowed in activation objects."); - var d = f.getQualifiedName(e.name); - this.map[d] = new u(e); - this.assignNextSlot(e); + a.Bindings = g; + var r = function(b) { + function a(d) { + b.call(this); + m(d.needsActivation()); + this.methodInfo = d; + d = d.traits; + for (var g = 0;g < d.length;g++) { + var k = d[g]; + m(k.isSlot() || k.isConst(), "Only slot or constant traits are allowed in activation objects."); + var e = s.getQualifiedName(k.name); + this.map[e] = new f(k); + this.assignNextSlot(k); } } - __extends(b, a); - return b; - }(h); - g.ActivationBindings = x; - x = function(a) { - function b(c, h) { - a.call(this); - var e = f.getQualifiedName(h.name); - this.map[e] = new u(h); - n(h.isSlot(), "Only slot traits are allowed in catch objects."); - this.assignNextSlot(h); + __extends(a, b); + return a; + }(g); + a.ActivationBindings = r; + r = function(b) { + function a(d, g) { + b.call(this); + var k = s.getQualifiedName(g.name); + this.map[k] = new f(g); + m(g.isSlot(), "Only slot traits are allowed in catch objects."); + this.assignNextSlot(g); } - __extends(b, a); - return b; - }(h); - g.CatchBindings = x; - var y = function(a) { - function b(c, h) { - a.call(this); - this.scope = h; - this.scriptInfo = c; - for (var e = c.traits, d = 0;d < e.length;d++) { - var p = e[d], l = f.getQualifiedName(p.name), l = u.getKey(l, p), l = this.map[l] = new u(p); - (p.isSlot() || p.isConst() || p.isClass()) && this.assignNextSlot(p); - p.isClass() && p.metadata && p.metadata.native && (p.classInfo.native = p.metadata.native); - if (p.isMethod() || p.isGetter() || p.isSetter()) { - l.scope = this.scope; + __extends(a, b); + return a; + }(g); + a.CatchBindings = r; + var w = function(b) { + function a(d, g) { + b.call(this); + this.scope = g; + this.scriptInfo = d; + for (var k = d.traits, e = 0;e < k.length;e++) { + var r = k[e], c = s.getQualifiedName(r.name), c = f.getKey(c, r), c = this.map[c] = new f(r); + (r.isSlot() || r.isConst() || r.isClass()) && this.assignNextSlot(r); + r.isClass() && r.metadata && r.metadata.native && (r.classInfo.native = r.metadata.native); + if (r.isMethod() || r.isGetter() || r.isSetter()) { + c.scope = this.scope; } } } - __extends(b, a); - return b; - }(h); - g.ScriptBindings = y; - x = function(a) { - function b(c, h, e) { - a.call(this); - this.scope = h; - this.natives = e; - this.classInfo = c; - c = c.traits; - for (h = 0;h < c.length;h++) { - e = c[h]; - var d = f.getQualifiedName(e.name), d = u.getKey(d, e), d = this.map[d] = new u(e); - (e.isSlot() || e.isConst()) && this.assignNextSlot(e); - if (e.isMethod() || e.isGetter() || e.isSetter()) { - d.scope = this.scope, d.natives = this.natives; + __extends(a, b); + return a; + }(g); + a.ScriptBindings = w; + r = function(b) { + function a(d, g, k) { + b.call(this); + this.scope = g; + this.natives = k; + this.classInfo = d; + d = d.traits; + for (g = 0;g < d.length;g++) { + k = d[g]; + var e = s.getQualifiedName(k.name), e = f.getKey(e, k), e = this.map[e] = new f(k); + (k.isSlot() || k.isConst()) && this.assignNextSlot(k); + if (k.isMethod() || k.isGetter() || k.isSetter()) { + e.scope = this.scope, e.natives = this.natives; } } } - __extends(b, a); - return b; - }(h); - g.ClassBindings = x; - h = function(b) { - function h(a, e, p, l) { + __extends(a, b); + return a; + }(g); + a.ClassBindings = r; + g = function(b) { + function a(d, g, f, k) { b.call(this); - this.scope = p; - this.natives = l; - this.parent = a; - this.instanceInfo = e; - this.implementedInterfaces = a ? d(a.implementedInterfaces) : c(); - a && (this.slots = a.slots.slice(), this.nextSlotId = a.nextSlotId); - this.extend(a); - } - __extends(h, b); - h.prototype.extend = function(b) { - var c = this.instanceInfo, h, e = this.map, d, p, l, q; + this.scope = f; + this.natives = k; + this.parent = d; + this.instanceInfo = g; + this.implementedInterfaces = d ? l(d.implementedInterfaces) : Object.create(null); + d && (this.slots = d.slots.slice(), this.nextSlotId = d.nextSlotId); + this.extend(d); + } + __extends(a, b); + a.prototype.extend = function(b) { + var a = this.instanceInfo, d, g = this.map, k, r, c, w; if (b) { - for (p in b.map) { - d = b.map[p], l = d.trait, e[p] = d, l.isProtected() && (q = f.getQualifiedName(new f([c.protectedNs], l.name.getName())), q = u.getKey(q, l), e[q] = d); - } - } - var r = c.traits; - for (b = 0;b < r.length;b++) { - l = r[b]; - d = f.getQualifiedName(l.name); - p = u.getKey(d, l); - d = new u(l); - h = e; - q = d; - var x = q.trait, w = h[p]; - w ? (n(!w.trait.isFinal(), "Cannot redefine a final trait: " + x), n(x.isOverride() || "length" === x.name.getName(), "Overriding a trait that is not marked for override: " + x)) : n(!x.isOverride(), "Trait marked override must override another trait: " + x); - h[p] = q; - if (l.isProtected()) { - for (h = this.parent;h && h.instanceInfo.protectedNs;) { - q = f.getQualifiedName(new f([h.instanceInfo.protectedNs], l.name.getName())), q = u.getKey(q, l), q in e && (e[q] = d), h = h.parent; - } + for (r in b.map) { + k = b.map[r], c = k.trait, g[r] = k, c.isProtected() && (w = s.getQualifiedName(new s([a.protectedNs], c.name.getName(), 0)), w = f.getKey(w, c), g[w] = k); } - (l.isSlot() || l.isConst()) && this.assignNextSlot(l); - if (l.isMethod() || l.isGetter() || l.isSetter()) { - d.scope = this.scope, d.natives = this.natives; - } - } - l = c.abc.applicationDomain; - d = c.interfaces; - for (b = 0;b < d.length;b++) { - r = l.getProperty(d[b], !0, !0), n(r), a(this.implementedInterfaces, r.interfaceBindings.implementedInterfaces), this.implementedInterfaces[f.getQualifiedName(r.classInfo.instanceInfo.name)] = r; } - for (var g in this.implementedInterfaces) { - r = this.implementedInterfaces[g]; - h = r.interfaceBindings; - for (var m in h.map) { - b = h.map[m], c.isInterface() ? e[m] = b : (d = f.getPublicQualifiedName(b.trait.name.getName()), p = u.getKey(d, b.trait), e[m] = e[p]); + var q = a.traits; + for (b = 0;b < q.length;b++) { + c = q[b]; + k = s.getQualifiedName(c.name); + r = f.getKey(k, c); + k = new f(c); + d = g; + w = k; + var n = w.trait, l = d[r]; + l ? (m(!l.trait.isFinal(), "Cannot redefine a final trait: " + n), m(n.isOverride() || "length" === n.name.getName(), "Overriding a trait that is not marked for override: " + n)) : m(!n.isOverride(), "Trait marked override must override another trait: " + n); + d[r] = w; + if (c.isProtected()) { + for (d = this.parent;d && d.instanceInfo.protectedNs;) { + w = s.getQualifiedName(new s([d.instanceInfo.protectedNs], c.name.getName(), 0)), w = f.getKey(w, c), w in g && (g[w] = k), d = d.parent; + } + } + (c.isSlot() || c.isConst()) && this.assignNextSlot(c); + if (c.isMethod() || c.isGetter() || c.isSetter()) { + k.scope = this.scope, k.natives = this.natives; + } + } + c = a.abc.applicationDomain; + k = a.interfaces; + for (b = 0;b < k.length;b++) { + q = c.getProperty(k[b], !0, !0), m(q), e(this.implementedInterfaces, q.interfaceBindings.implementedInterfaces), this.implementedInterfaces[s.getQualifiedName(q.classInfo.instanceInfo.name)] = q; + } + for (var t in this.implementedInterfaces) { + q = this.implementedInterfaces[t]; + d = q.interfaceBindings; + for (var z in d.map) { + b = d.map[z], a.isInterface() ? g[z] = b : (k = s.getPublicQualifiedName(b.trait.name.getName()), r = f.getKey(k, b.trait), g[z] = g[r]); } } }; - h.prototype.toString = function() { + a.prototype.toString = function() { return this.instanceInfo.toString(); }; - return h; - }(h); - g.InstanceBindings = h; - var G = null; - })(k.Runtime || (k.Runtime = {})); - })(b.AVM2 || (b.AVM2 = {})); + return a; + }(g); + a.InstanceBindings = g; + var z = null; + })(h.Runtime || (h.Runtime = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); var Binding = Shumway.AVM2.Runtime.Binding, Bindings = Shumway.AVM2.Runtime.Bindings, ActivationBindings = Shumway.AVM2.Runtime.ActivationBindings, CatchBindings = Shumway.AVM2.Runtime.CatchBindings, ScriptBindings = Shumway.AVM2.Runtime.ScriptBindings, ClassBindings = Shumway.AVM2.Runtime.ClassBindings, InstanceBindings = Shumway.AVM2.Runtime.InstanceBindings; -(function(b) { - (function(b) { - b.XRegExp = function() { - function b(a, c, h) { - var e; - if (h) { - if (a.__proto__) { - a.__proto__ = q.prototype; +(function(c) { + (function(c) { + c.XRegExp = function() { + function a(b, a, d) { + var g; + if (d) { + if (b.__proto__) { + b.__proto__ = k.prototype; } else { - for (e in q.prototype) { - a[e] = q.prototype[e]; + for (g in k.prototype) { + b[g] = k.prototype[g]; } } } - a.xregexp = {captureNames:c}; - return a; + b.xregexp = {captureNames:a}; + return b; } - function f(a) { - return u.replace.call(a, /([\s\S])(?=[\s\S]*\1)/g, ""); + function c(b) { + return d.replace.call(b, /([\s\S])(?=[\s\S]*\1)/g, ""); } - function k(a, c) { - if (!q.isRegExp(a)) { + function h(b, g) { + if (!k.isRegExp(b)) { throw new TypeError("Type RegExp expected"); } - var h = u.exec.call(/\/([a-z]*)$/i, String(a))[1]; - c = c || {}; - c.add && (h = f(h + c.add)); - c.remove && (h = u.replace.call(h, new RegExp("[" + c.remove + "]+", "g"), "")); - return a = b(new RegExp(a.source, h), a.xregexp && a.xregexp.captureNames ? a.xregexp.captureNames.slice(0) : null, c.addProto); + var f = d.exec.call(/\/([a-z]*)$/i, String(b))[1]; + g = g || {}; + g.add && (f = c(f + g.add)); + g.remove && (f = d.replace.call(f, new RegExp("[" + g.remove + "]+", "g"), "")); + return b = a(new RegExp(b.source, f), b.xregexp && b.xregexp.captureNames ? b.xregexp.captureNames.slice(0) : null, g.addProto); } - function s(a, b) { + function p(b, a) { if (Array.prototype.indexOf) { - return a.indexOf(b); + return b.indexOf(a); } - var c = a.length, h; - for (h = 0;h < c;++h) { - if (a[h] === b) { - return h; + var d = b.length, g; + for (g = 0;g < d;++g) { + if (b[g] === a) { + return g; } } return-1; } - function m(a, b) { - return O.call(a) === "[object " + b + "]"; + function u(b, a) { + return K.call(b) === "[object " + a + "]"; } - function d(a, b, c) { - return u.test.call(-1 < c.indexOf("x") ? /^(?:\s+|#.*|\(\?#[^)]*\))*(?:[?*+]|{\d+(?:,\d*)?})/ : /^(?:\(\?#[^)]*\))*(?:[?*+]|{\d+(?:,\d*)?})/, a.slice(b)); + function l(b, a, g) { + return d.test.call(-1 < g.indexOf("x") ? /^(?:\s+|#.*|\(\?#[^)]*\))*(?:[?*+]|{\d+(?:,\d*)?})/ : /^(?:\(\?#[^)]*\))*(?:[?*+]|{\d+(?:,\d*)?})/, b.slice(a)); } - function a(a, b) { - var c; - if (f(b) !== b) { - throw new SyntaxError("Invalid duplicate regex flag " + b); - } - a = u.replace.call(a, /^\(\?([\w$]+)\)/, function(a, c) { - if (u.test.call(/[gy]/, c)) { - throw new SyntaxError("Cannot use flag g or y in mode modifier " + a); + function e(b, a) { + var g; + if (c(a) !== a) { + throw new SyntaxError("Invalid duplicate regex flag " + a); + } + b = d.replace.call(b, /^\(\?([\w$]+)\)/, function(b, g) { + if (d.test.call(/[gy]/, g)) { + throw new SyntaxError("Cannot use flag g or y in mode modifier " + b); } - b = f(b + c); + a = c(a + g); return ""; }); - for (c = 0;c < b.length;++c) { - if (!E[b.charAt(c)]) { - throw new SyntaxError("Unknown regex flag " + b.charAt(c)); + for (g = 0;g < a.length;++g) { + if (!N[a.charAt(g)]) { + throw new SyntaxError("Unknown regex flag " + a.charAt(g)); } } - return{pattern:a, flags:b}; + return{pattern:b, flags:a}; } - function c(a) { - a = a || {}; - m(a, "String") && (a = q.forEach(a, /[^\s,]+/, function(a) { - this[a] = !0; + function m(b) { + b = b || {}; + u(b, "String") && (b = k.forEach(b, /[^\s,]+/, function(b) { + this[b] = !0; }, {})); - return a; + return b; } - function n(a) { - if (!/^[\w$]$/.test(a)) { + function t(b) { + if (!/^[\w$]$/.test(b)) { throw Error("Flag must be a single character A-Za-z0-9_$"); } - E[a] = !0; + N[b] = !0; } - function p(a) { - RegExp.prototype.exec = (a ? w : u).exec; - RegExp.prototype.test = (a ? w : u).test; - String.prototype.match = (a ? w : u).match; - String.prototype.replace = (a ? w : u).replace; - String.prototype.split = (a ? w : u).split; - l.natives = a; + function q(a) { + RegExp.prototype.exec = (a ? b : d).exec; + RegExp.prototype.test = (a ? b : d).test; + String.prototype.match = (a ? b : d).match; + String.prototype.replace = (a ? b : d).replace; + String.prototype.split = (a ? b : d).split; + f.natives = a; } - function e(a) { - if (null == a) { + function n(b) { + if (null == b) { throw new TypeError("Cannot convert null or undefined to object"); } - return a; + return b; } - var q, l = {astral:!1, natives:!1}, u = {exec:RegExp.prototype.exec, test:RegExp.prototype.test, match:String.prototype.match, replace:String.prototype.replace, split:String.prototype.split}, w = {}, r = {}, h = {}, x = [], y = {"default":/\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9]\d*|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S])|\(\?[:=!]|[?*+]\?|{\d+(?:,\d*)?}\??|[\s\S]/, "class":/\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S])|[\s\S]/}, G = /\$(?:{([\w$]+)}|([\d$&`']))/g, - I = void 0 === u.exec.call(/()??/, "")[1], C = void 0 !== RegExp.prototype.sticky, E = {g:!0, i:!0, m:!0, y:C}, O = {}.toString, K; - q = function(c, e) { - var d = {hasNamedCapture:!1, captureNames:[]}, p = "default", l = "", n = 0, r, f; - if (q.isRegExp(c)) { - if (void 0 !== e) { + var k, f = {astral:!1, natives:!1}, d = {exec:RegExp.prototype.exec, test:RegExp.prototype.test, match:String.prototype.match, replace:String.prototype.replace, split:String.prototype.split}, b = {}, g = {}, r = {}, w = [], z = {"default":/\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9]\d*|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S])|\(\?[:=!]|[?*+]\?|{\d+(?:,\d*)?}\??|[\s\S]/, "class":/\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S])|[\s\S]/}, A = /\$(?:{([\w$]+)}|([\d$&`']))/g, + B = void 0 === d.exec.call(/()??/, "")[1], M = void 0 !== RegExp.prototype.sticky, N = {g:!0, i:!0, m:!0, y:M}, K = {}.toString, y; + k = function(b, g) { + var f = {hasNamedCapture:!1, captureNames:[]}, c = "default", m = "", q = 0, n, l; + if (k.isRegExp(b)) { + if (void 0 !== g) { throw new TypeError("Cannot supply flags when copying a RegExp"); } - return k(c, {addProto:!0}); + return h(b, {addProto:!0}); } - c = void 0 === c ? "" : String(c); - e = void 0 === e ? "" : String(e); - f = c + "***" + e; - if (!h[f]) { - r = a(c, e); - c = r.pattern; - for (e = r.flags;n < c.length;) { + b = void 0 === b ? "" : String(b); + g = void 0 === g ? "" : String(g); + l = b + "***" + g; + if (!r[l]) { + n = e(b, g); + b = n.pattern; + for (g = n.flags;q < b.length;) { do { - r = c; - for (var w = e, m = n, s = p, G = d, I = x.length, E = null, C = void 0, O = void 0;I--;) { - if (O = x[I], (O.scope === s || "all" === O.scope) && (!O.flag || -1 < w.indexOf(O.flag)) && (C = q.exec(r, O.regex, m, "sticky"))) { - E = {matchLength:C[0].length, output:O.handler.call(G, C, s, w), reparse:O.reparse}; + n = b; + for (var t = g, s = q, A = c, p = f, B = w.length, u = null, y = void 0, M = void 0;B--;) { + if (M = w[B], (M.scope === A || "all" === M.scope) && (!M.flag || -1 < t.indexOf(M.flag)) && (y = k.exec(n, M.regex, s, "sticky"))) { + u = {matchLength:y[0].length, output:M.handler.call(p, y, A, t), reparse:M.reparse}; break; } } - (r = E) && r.reparse && (c = c.slice(0, n) + r.output + c.slice(n + r.matchLength)); - } while (r && r.reparse); - r ? (l += r.output, n += r.matchLength || 1) : (r = q.exec(c, y[p], n, "sticky")[0], l += r, n += r.length, "[" === r && "default" === p ? p = "class" : "]" === r && "class" === p && (p = "default")); + (n = u) && n.reparse && (b = b.slice(0, q) + n.output + b.slice(q + n.matchLength)); + } while (n && n.reparse); + n ? (m += n.output, q += n.matchLength || 1) : (n = k.exec(b, z[c], q, "sticky")[0], m += n, q += n.length, "[" === n && "default" === c ? c = "class" : "]" === n && "class" === c && (c = "default")); } - h[f] = {pattern:u.replace.call(l, /\(\?:\)(?=\(\?:\))|^\(\?:\)|\(\?:\)$/g, ""), flags:u.replace.call(e, /[^gimy]+/g, ""), captures:d.hasNamedCapture ? d.captureNames : null}; + r[l] = {pattern:d.replace.call(m, /\(\?:\)(?=\(\?:\))|^\(\?:\)|\(\?:\)$/g, ""), flags:d.replace.call(g, /[^gimy]+/g, ""), captures:f.hasNamedCapture ? f.captureNames : null}; } - f = h[f]; - return b(new RegExp(f.pattern, f.flags), f.captures, !0); + l = r[l]; + return a(new RegExp(l.pattern, l.flags), l.captures, !0); }; - q.prototype = RegExp(); - q.version = "3.0.0-pre"; - q.addToken = function(a, b, c) { - c = c || {}; - var h = c.optionalFlags, e; - c.flag && n(c.flag); - if (h) { - for (h = u.split.call(h, ""), e = 0;e < h.length;++e) { - n(h[e]); + k.prototype = RegExp(); + k.version = "3.0.0-pre"; + k.addToken = function(b, a, g) { + g = g || {}; + var f = g.optionalFlags, e; + g.flag && t(g.flag); + if (f) { + for (f = d.split.call(f, ""), e = 0;e < f.length;++e) { + t(f[e]); } } - x.push({regex:k(a, {add:"g" + (C ? "y" : "")}), handler:b, scope:c.scope || "default", flag:c.flag, reparse:c.reparse}); - q.cache.flush("patterns"); - }; - q.cache = function(a, b) { - var c = a + "***" + (b || ""); - return r[c] || (r[c] = q(a, b)); - }; - q.cache.flush = function(a) { - "patterns" === a ? h = {} : r = {}; - }; - q.escape = function(a) { - return u.replace.call(e(a), /[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); + w.push({regex:h(b, {add:"g" + (M ? "y" : "")}), handler:a, scope:g.scope || "default", flag:g.flag, reparse:g.reparse}); + k.cache.flush("patterns"); }; - q.exec = function(a, b, c, h) { - var e = "g"; - C && (h || b.sticky && !1 !== h) && (e += "y"); - b.xregexp = b.xregexp || {captureNames:null}; - e = b.xregexp[e] || (b.xregexp[e] = k(b, {add:e, remove:!1 === h ? "y" : ""})); - e.lastIndex = c = c || 0; - a = w.exec.call(e, a); - h && a && a.index !== c && (a = null); - b.global && (b.lastIndex = a ? e.lastIndex : 0); + k.cache = function(b, a) { + var d = b + "***" + (a || ""); + return g[d] || (g[d] = k(b, a)); + }; + k.cache.flush = function(b) { + "patterns" === b ? r = {} : g = {}; + }; + k.escape = function(b) { + return d.replace.call(n(b), /[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); + }; + k.exec = function(a, d, g, f) { + var k = "g"; + M && (f || d.sticky && !1 !== f) && (k += "y"); + d.xregexp = d.xregexp || {captureNames:null}; + k = d.xregexp[k] || (d.xregexp[k] = h(d, {add:k, remove:!1 === f ? "y" : ""})); + k.lastIndex = g = g || 0; + a = b.exec.call(k, a); + f && a && a.index !== g && (a = null); + d.global && (d.lastIndex = a ? k.lastIndex : 0); return a; }; - q.forEach = function(a, b, c, h) { - for (var e = 0, d = -1;e = q.exec(a, b, e);) { - c.call(h, e, ++d, a, b), e = e.index + (e[0].length || 1); + k.forEach = function(b, a, d, g) { + for (var f = 0, e = -1;f = k.exec(b, a, f);) { + d.call(g, f, ++e, b, a), f = f.index + (f[0].length || 1); } - return h; + return g; }; - q.globalize = function(a) { - return k(a, {add:"g", addProto:!0}); + k.globalize = function(b) { + return h(b, {add:"g", addProto:!0}); }; - q.install = function() { - var a = {natives:!0}, a = c(a); - !l.astral && a.astral && (q.cache.flush("patterns"), l.astral = !0); - !l.natives && a.natives && p(!0); - }; - q.isInstalled = function(a) { - return!!l[a]; - }; - q.isRegExp = function(a) { - return "[object RegExp]" === O.call(a); - }; - q.match = function(a, b, c) { - var h = b.global && "one" !== c || "all" === c, d = (h ? "g" : "") + (b.sticky ? "y" : ""); - b.xregexp = b.xregexp || {captureNames:null}; - d = b.xregexp[d || "noGY"] || (b.xregexp[d || "noGY"] = k(b, {add:d, remove:"one" === c ? "g" : ""})); - a = u.match.call(e(a), d); - b.global && (b.lastIndex = "one" === c && a ? a.index + a[0].length : 0); - return h ? a || [] : a && a[0]; + k.install = function() { + var b = {natives:!0}, b = m(b); + !f.astral && b.astral && (k.cache.flush("patterns"), f.astral = !0); + !f.natives && b.natives && q(!0); + }; + k.isInstalled = function(b) { + return!!f[b]; + }; + k.isRegExp = function(b) { + return "[object RegExp]" === K.call(b); + }; + k.match = function(b, a, g) { + var f = a.global && "one" !== g || "all" === g, k = (f ? "g" : "") + (a.sticky ? "y" : ""); + a.xregexp = a.xregexp || {captureNames:null}; + k = a.xregexp[k || "noGY"] || (a.xregexp[k || "noGY"] = h(a, {add:k, remove:"one" === g ? "g" : ""})); + b = d.match.call(n(b), k); + a.global && (a.lastIndex = "one" === g && b ? b.index + b[0].length : 0); + return f ? b || [] : b && b[0]; }; - q.matchChain = function(a, b) { - return function B(a, c) { - function h(a) { - if (e.backref) { - if (!(a.hasOwnProperty(e.backref) || +e.backref < a.length)) { - throw new ReferenceError("Backreference to undefined group: " + e.backref); + k.matchChain = function(b, a) { + return function J(b, d) { + function g(b) { + if (f.backref) { + if (!(b.hasOwnProperty(f.backref) || +f.backref < b.length)) { + throw new ReferenceError("Backreference to undefined group: " + f.backref); } - d.push(a[e.backref] || ""); + e.push(b[f.backref] || ""); } else { - d.push(a[0]); + e.push(b[0]); } } - var e = b[c].regex ? b[c] : {regex:b[c]}, d = [], p; - for (p = 0;p < a.length;++p) { - q.forEach(a[p], e.regex, h); + var f = a[d].regex ? a[d] : {regex:a[d]}, e = [], r; + for (r = 0;r < b.length;++r) { + k.forEach(b[r], f.regex, g); } - return c !== b.length - 1 && d.length ? B(d, c + 1) : d; - }([a], 0); + return d !== a.length - 1 && e.length ? J(e, d + 1) : e; + }([b], 0); }; - q.replace = function(a, b, c, h) { - var d = q.isRegExp(b), p = b.global && "one" !== h || "all" === h, l = (p ? "g" : "") + (b.sticky ? "y" : ""), n = b; - d ? (b.xregexp = b.xregexp || {captureNames:null}, n = b.xregexp[l || "noGY"] || (b.xregexp[l || "noGY"] = k(b, {add:l, remove:"one" === h ? "g" : ""}))) : p && (n = new RegExp(q.escape(String(b)), "g")); - a = w.replace.call(e(a), n, c); - d && b.global && (b.lastIndex = 0); + k.replace = function(a, d, g, f) { + var e = k.isRegExp(d), r = d.global && "one" !== f || "all" === f, c = (r ? "g" : "") + (d.sticky ? "y" : ""), m = d; + e ? (d.xregexp = d.xregexp || {captureNames:null}, m = d.xregexp[c || "noGY"] || (d.xregexp[c || "noGY"] = h(d, {add:c, remove:"one" === f ? "g" : ""}))) : r && (m = new RegExp(k.escape(String(d)), "g")); + a = b.replace.call(n(a), m, g); + e && d.global && (d.lastIndex = 0); return a; }; - q.replaceEach = function(a, b) { - var c, h; - for (c = 0;c < b.length;++c) { - h = b[c], a = q.replace(a, h[0], h[1], h[2]); + k.replaceEach = function(b, a) { + var d, g; + for (d = 0;d < a.length;++d) { + g = a[d], b = k.replace(b, g[0], g[1], g[2]); } - return a; - }; - q.split = function(a, b, c) { - return w.split.call(e(a), b, c); + return b; }; - q.test = function(a, b, c, h) { - return!!q.exec(a, b, c, h); + k.split = function(a, d, g) { + return b.split.call(n(a), d, g); }; - q.uninstall = function(a) { - a = c(a); - l.astral && a.astral && (q.cache.flush("patterns"), l.astral = !1); - l.natives && a.natives && p(!1); + k.test = function(b, a, d, g) { + return!!k.exec(b, a, d, g); }; - q.union = function(a, b) { - function c(a, b, h) { - var e = l[d - p]; - if (b) { - if (++d, e) { - return "(?<" + e + ">"; + k.uninstall = function(b) { + b = m(b); + f.astral && b.astral && (k.cache.flush("patterns"), f.astral = !1); + f.natives && b.natives && q(!1); + }; + k.union = function(b, a) { + function g(b, a, d) { + var f = m[r - c]; + if (a) { + if (++r, f) { + return "(?<" + f + ">"; } } else { - if (h) { - return "\\" + (+h + p); + if (d) { + return "\\" + (+d + c); } } - return a; + return b; } - var h = /(\()(?!\?)|\\([1-9]\d*)|\\[\s\S]|\[(?:[^\\\]]|\\[\s\S])*]/g, e = [], d = 0, p, l, n, r; - if (!m(a, "Array") || !a.length) { + var f = /(\()(?!\?)|\\([1-9]\d*)|\\[\s\S]|\[(?:[^\\\]]|\\[\s\S])*]/g, e = [], r = 0, c, m, w, q; + if (!u(b, "Array") || !b.length) { throw new TypeError("Must provide a nonempty array of patterns to merge"); } - for (r = 0;r < a.length;++r) { - n = a[r], q.isRegExp(n) ? (p = d, l = n.xregexp && n.xregexp.captureNames || [], e.push(u.replace.call(q(n.source).source, h, c))) : e.push(q.escape(n)); + for (q = 0;q < b.length;++q) { + w = b[q], k.isRegExp(w) ? (c = r, m = w.xregexp && w.xregexp.captureNames || [], e.push(d.replace.call(k(w.source).source, f, g))) : e.push(k.escape(w)); } - return q(e.join("|"), b); + return k(e.join("|"), a); }; - w.exec = function(a) { - var b = this.lastIndex, c = u.exec.apply(this, arguments), h, e; - if (c) { - !I && 1 < c.length && -1 < s(c, "") && (h = k(this, {remove:"g"}), u.replace.call(String(a).slice(c.index), h, function() { - var a = arguments.length, b; - for (b = 1;b < a - 2;++b) { - void 0 === arguments[b] && (c[b] = void 0); + b.exec = function(b) { + var a = this.lastIndex, g = d.exec.apply(this, arguments), f, k; + if (g) { + !B && 1 < g.length && -1 < p(g, "") && (f = h(this, {remove:"g"}), d.replace.call(String(b).slice(g.index), f, function() { + var b = arguments.length, a; + for (a = 1;a < b - 2;++a) { + void 0 === arguments[a] && (g[a] = void 0); } })); if (this.xregexp && this.xregexp.captureNames) { - for (e = 1;e < c.length;++e) { - (h = this.xregexp.captureNames[e - 1]) && (c[h] = c[e]); + for (k = 1;k < g.length;++k) { + (f = this.xregexp.captureNames[k - 1]) && (g[f] = g[k]); } } - this.global && !c[0].length && this.lastIndex > c.index && (this.lastIndex = c.index); + this.global && !g[0].length && this.lastIndex > g.index && (this.lastIndex = g.index); } - this.global || (this.lastIndex = b); - return c; + this.global || (this.lastIndex = a); + return g; }; - w.test = function(a) { - return!!w.exec.call(this, a); + b.test = function(a) { + return!!b.exec.call(this, a); }; - w.match = function(a) { - var b; - if (!q.isRegExp(a)) { + b.match = function(a) { + var g; + if (!k.isRegExp(a)) { a = new RegExp(a); } else { if (a.global) { - return b = u.match.apply(this, arguments), a.lastIndex = 0, b; + return g = d.match.apply(this, arguments), a.lastIndex = 0, g; } } - return w.exec.call(a, e(this)); + return b.exec.call(a, n(this)); }; - w.replace = function(a, b) { - var c = q.isRegExp(a), h, e, d; - c ? (a.xregexp && (e = a.xregexp.captureNames), h = a.lastIndex) : a += ""; - d = m(b, "Function") ? u.replace.call(String(this), a, function() { - var h = arguments, d; + b.replace = function(b, a) { + var g = k.isRegExp(b), f, e, r; + g ? (b.xregexp && (e = b.xregexp.captureNames), f = b.lastIndex) : b += ""; + r = u(a, "Function") ? d.replace.call(String(this), b, function() { + var d = arguments, f; if (e) { - for (h[0] = new String(h[0]), d = 0;d < e.length;++d) { - e[d] && (h[0][e[d]] = h[d + 1]); + for (d[0] = new String(d[0]), f = 0;f < e.length;++f) { + e[f] && (d[0][e[f]] = d[f + 1]); } } - c && a.global && (a.lastIndex = h[h.length - 2] + h[0].length); - return b.apply(void 0, h); - }) : u.replace.call(null == this ? this : String(this), a, function() { - var a = arguments; - return u.replace.call(String(b), G, function(b, c, h) { - if (c) { - h = +c; - if (h <= a.length - 3) { - return a[h] || ""; + g && b.global && (b.lastIndex = d[d.length - 2] + d[0].length); + return a.apply(void 0, d); + }) : d.replace.call(null == this ? this : String(this), b, function() { + var b = arguments; + return d.replace.call(String(a), A, function(a, d, g) { + if (d) { + g = +d; + if (g <= b.length - 3) { + return b[g] || ""; } - h = e ? s(e, c) : -1; - if (0 > h) { - throw new SyntaxError("Backreference to undefined group " + b); + g = e ? p(e, d) : -1; + if (0 > g) { + throw new SyntaxError("Backreference to undefined group " + a); } - return a[h + 1] || ""; + return b[g + 1] || ""; } - if ("$" === h) { + if ("$" === g) { return "$"; } - if ("&" === h || 0 === +h) { - return a[0]; + if ("&" === g || 0 === +g) { + return b[0]; } - if ("`" === h) { - return a[a.length - 1].slice(0, a[a.length - 2]); + if ("`" === g) { + return b[b.length - 1].slice(0, b[b.length - 2]); } - if ("'" === h) { - return a[a.length - 1].slice(a[a.length - 2] + a[0].length); + if ("'" === g) { + return b[b.length - 1].slice(b[b.length - 2] + b[0].length); } - h = +h; - if (!isNaN(h)) { - if (h > a.length - 3) { - throw new SyntaxError("Backreference to undefined group " + b); + g = +g; + if (!isNaN(g)) { + if (g > b.length - 3) { + throw new SyntaxError("Backreference to undefined group " + a); } - return a[h] || ""; + return b[g] || ""; } - throw new SyntaxError("Invalid token " + b); + throw new SyntaxError("Invalid token " + a); }); }); - c && (a.lastIndex = a.global ? 0 : h); - return d; + g && (b.lastIndex = b.global ? 0 : f); + return r; }; - w.split = function(a, b) { - if (!q.isRegExp(a)) { - return u.split.apply(this, arguments); - } - var c = String(this), h = [], e = a.lastIndex, d = 0, p; - b = (void 0 === b ? -1 : b) >>> 0; - q.forEach(c, a, function(a) { - a.index + a[0].length > d && (h.push(c.slice(d, a.index)), 1 < a.length && a.index < c.length && Array.prototype.push.apply(h, a.slice(1)), p = a[0].length, d = a.index + p); + b.split = function(b, a) { + if (!k.isRegExp(b)) { + return d.split.apply(this, arguments); + } + var g = String(this), f = [], e = b.lastIndex, r = 0, c; + a = (void 0 === a ? -1 : a) >>> 0; + k.forEach(g, b, function(b) { + b.index + b[0].length > r && (f.push(g.slice(r, b.index)), 1 < b.length && b.index < g.length && Array.prototype.push.apply(f, b.slice(1)), c = b[0].length, r = b.index + c); }); - d === c.length ? u.test.call(a, "") && !p || h.push("") : h.push(c.slice(d)); - a.lastIndex = e; - return h.length > b ? h.slice(0, b) : h; - }; - K = q.addToken; - K(/\\([ABCE-RTUVXYZaeg-mopqyz]|c(?![A-Za-z])|u(?![\dA-Fa-f]{4})|x(?![\dA-Fa-f]{2}))/, function(a, b) { - if ("B" === a[1] && "default" === b) { - return a[0]; + r === g.length ? d.test.call(b, "") && !c || f.push("") : f.push(g.slice(r)); + b.lastIndex = e; + return f.length > a ? f.slice(0, a) : f; + }; + y = k.addToken; + y(/\\([ABCE-RTUVXYZaeg-mopqyz]|c(?![A-Za-z])|u(?![\dA-Fa-f]{4})|x(?![\dA-Fa-f]{2}))/, function(b, a) { + if ("B" === b[1] && "default" === a) { + return b[0]; } - throw new SyntaxError("Invalid escape " + a[0]); + throw new SyntaxError("Invalid escape " + b[0]); }, {scope:"all"}); - K(/\[(\^?)]/, function(a) { - return a[1] ? "[\\s\\S]" : "\\b\\B"; + y(/\[(\^?)]/, function(b) { + return b[1] ? "[\\s\\S]" : "\\b\\B"; }); - K(/\(\?#[^)]*\)/, function(a, b, c) { - return d(a.input, a.index + a[0].length, c) ? "" : "(?:)"; + y(/\(\?#[^)]*\)/, function(b, a, d) { + return l(b.input, b.index + b[0].length, d) ? "" : "(?:)"; }); - K(/\s+|#.*/, function(a, b, c) { - return d(a.input, a.index + a[0].length, c) ? "" : "(?:)"; + y(/\s+|#.*/, function(b, a, d) { + return l(b.input, b.index + b[0].length, d) ? "" : "(?:)"; }, {flag:"x"}); - K(/\./, function() { + y(/\./, function() { return "[\\s\\S]"; }, {flag:"s"}); - K(/\\k<([\w$]+)>/, function(a) { - var b = isNaN(a[1]) ? s(this.captureNames, a[1]) + 1 : +a[1], c = a.index + a[0].length; - if (!b || b > this.captureNames.length) { - throw new SyntaxError("Backreference to undefined group " + a[0]); + y(/\\k<([\w$]+)>/, function(b) { + var a = isNaN(b[1]) ? p(this.captureNames, b[1]) + 1 : +b[1], d = b.index + b[0].length; + if (!a || a > this.captureNames.length) { + throw new SyntaxError("Backreference to undefined group " + b[0]); } - return "\\" + b + (c === a.input.length || isNaN(a.input.charAt(c)) ? "" : "(?:)"); + return "\\" + a + (d === b.input.length || isNaN(b.input.charAt(d)) ? "" : "(?:)"); }); - K(/\\(\d+)/, function(a, b) { - if (!("default" === b && /^[1-9]/.test(a[1]) && +a[1] <= this.captureNames.length) && "0" !== a[1]) { - throw new SyntaxError("Cannot use octal escape or backreference to undefined group " + a[0]); + y(/\\(\d+)/, function(b, a) { + if (!("default" === a && /^[1-9]/.test(b[1]) && +b[1] <= this.captureNames.length) && "0" !== b[1]) { + throw new SyntaxError("Cannot use octal escape or backreference to undefined group " + b[0]); } - return a[0]; + return b[0]; }, {scope:"all"}); - K(/\(\?P?<([\w$]+)>/, function(a) { - if (!isNaN(a[1])) { - throw new SyntaxError("Cannot use integer as capture name " + a[0]); + y(/\(\?P?<([\w$]+)>/, function(b) { + if (!isNaN(b[1])) { + throw new SyntaxError("Cannot use integer as capture name " + b[0]); } - if ("length" === a[1] || "__proto__" === a[1]) { - throw new SyntaxError("Cannot use reserved word as capture name " + a[0]); + if ("length" === b[1] || "__proto__" === b[1]) { + throw new SyntaxError("Cannot use reserved word as capture name " + b[0]); } - if (-1 < s(this.captureNames, a[1])) { - throw new SyntaxError("Cannot use same name for multiple groups " + a[0]); + if (-1 < p(this.captureNames, b[1])) { + throw new SyntaxError("Cannot use same name for multiple groups " + b[0]); } - this.captureNames.push(a[1]); + this.captureNames.push(b[1]); this.hasNamedCapture = !0; return "("; }); - K(/\((?!\?)/, function(a, b, c) { - if (-1 < c.indexOf("n")) { + y(/\((?!\?)/, function(b, a, d) { + if (-1 < d.indexOf("n")) { return "(?:"; } this.captureNames.push(null); return "("; }, {optionalFlags:"n"}); - return q; + return k; }(); - })(b.AVM2 || (b.AVM2 = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); Shumway.AVM2.XRegExp.install(); -(function(b) { - (function(k) { - (function(g) { - var f = b.Debug.assertNotImplemented, t = b.Debug.notImplemented, s = b.AVM2.Runtime.throwError, m = b.NumberUtilities.clamp, d = b.AVM2.Runtime.asCheckVectorGetNumericProperty, a = b.AVM2.Runtime.asCheckVectorSetNumericProperty, c = function() { - function c(a, b) { - "undefined" === typeof a && (a = 0); - "undefined" === typeof b && (b = !1); - a >>>= 0; - this._fixed = !!b; - this._buffer = new Int32Array(Math.max(c.INITIAL_CAPACITY, a + c.EXTRA_CAPACITY)); - this._offset = 0; - this._length = a; +(function(c) { + (function(h) { + (function(a) { + function s(b, a) { + g(!da[b], "Native function: " + b + " is already registered."); + da[b] = a; + } + function v(b) { + switch(b) { + case "prototype": + return "native_prototype"; + case "hasOwnProperty": + return "native_hasOwnProperty"; + case "isPrototypeOf": + return "native_isPrototypeOf"; + case "propertyIsEnumerable": + return "native_propertyIsEnumerable"; + default: + return b; + } + } + function p(b) { + for (var a = b.split("."), d = ca, f = 0, k = a.length;f < k;f++) { + d = d && d[a[f]]; + } + d || (d = da[b]); + g(d, "getNative(" + b + ") not found."); + g(0 > la.indexOf(d), "Leaking illegal function."); + return d; + } + var u = c.AVM2.ABC.Multiname, l = c.AVM2.Runtime.Scope, e = c.ObjectUtilities.hasOwnProperty, m = c.ObjectUtilities.hasOwnGetter, t = c.ObjectUtilities.defineNonEnumerableProperty, q = c.isNumber, n = c.isNullOrUndefined, k = c.ObjectUtilities.isPrototypeWriteable, f = c.ObjectUtilities.getOwnPropertyDescriptor, d = c.Debug.notImplemented, b = c.AVM2.Runtime.asCoerceString, g = c.Debug.assert, r = c.AVM2.Runtime.createFunction, w = c.AVM2.Runtime, z = c.ObjectUtilities.boxValue, A = c.AVM2.Runtime.ClassBindings, + B = c.AVM2.Runtime.InstanceBindings, M = c.AVM2.Runtime.asCompare; + (function(b) { + b[b.NONE = 0] = "NONE"; + b[b.OWN_INITIALIZE = 1] = "OWN_INITIALIZE"; + b[b.SUPER_INITIALIZE = 2] = "SUPER_INITIALIZE"; + })(a.InitializationFlags || (a.InitializationFlags = {})); + var N = function() { + function b() { } - c.defaultCompareFunction = function(a, b) { - return String(a).localeCompare(String(b)); + b.morphIntoASClass = function(b) { + this.classInfo = b; + this.__proto__ = y.prototype; }; - c.compare = function(a, e, d, l) { - f(!(d & c.CASEINSENSITIVE), "CASEINSENSITIVE"); - f(!(d & c.UNIQUESORT), "UNIQUESORT"); - f(!(d & c.RETURNINDEXEDARRAY), "RETURNINDEXEDARRAY"); - var g = 0; - l || (l = c.defaultCompareFunction); - d & c.NUMERIC ? (a = b.toNumber(a), e = b.toNumber(e), g = a < e ? -1 : a > e ? 1 : 0) : g = l(a, e); - d & c.DESCENDING && (g *= -1); - return g; + b.create = function(b, a, d) { + y.create(b, a, this.instanceConstructor); }; - c.callable = function(a) { - if (a instanceof c) { - return a; - } - var e = a.asGetProperty(void 0, "length"); - if (void 0 !== e) { - for (var d = new c(e, !1), l = 0;l < e;l++) { - d.asSetNumericProperty(l, a.asGetPublicProperty(l)); - } - return d; - } - b.Debug.unexpected(); + b.initializeFrom = function(b) { + return D.initializeFrom.call(this, b); }; - c.prototype.internalToString = function() { - for (var a = "", b = this._offset, c = b + this._length, d = 0;d < this._buffer.length;d++) { - d === b && (a += "["), d === c && (a += "]"), a += this._buffer[d], d < this._buffer.length - 1 && (a += ","); + b.asCall = function(b) { + for (var a = [], d = 1;d < arguments.length;d++) { + a[d - 1] = arguments[d]; } - this._offset + this._length === this._buffer.length && (a += "]"); - return a + ": offset: " + this._offset + ", length: " + this._length + ", capacity: " + this._buffer.length; + return this.callableConstructor.apply(b, a); }; - c.prototype.toString = function() { - for (var a = "", b = 0;b < this._length;b++) { - a += this._buffer[this._offset + b], b < this._length - 1 && (a += ","); - } - return a; + b.asApply = function(b, a) { + return this.callableConstructor.apply(b, a); }; - c.prototype._view = function() { - return this._buffer.subarray(this._offset, this._offset + this._length); + b.verify = function() { + D.verify.call(this); }; - c.prototype._ensureCapacity = function(a) { - var b = this._offset + a; - b < this._buffer.length || (a <= this._buffer.length ? (b = this._buffer.length - a >> 2, this._buffer.set(this._view(), b), this._offset = b) : (a = (3 * this._buffer.length >> 1) + 1, a < b && (a = b), b = new Int32Array(a), b.set(this._buffer, 0), this._buffer = b)); - }; - c.prototype.concat = function() { - t("Int32Vector.concat"); - }; - c.prototype.every = function(a, b) { - for (var c = 0;c < this._length;c++) { - if (!a.call(b, this._buffer[this._offset + c], c, this)) { - return!1; - } - } - return!0; + b.trace = function(b) { + D.trace.call(this, b); }; - c.prototype.filter = function(a, b) { - for (var d = new c, l = 0;l < this._length;l++) { - a.call(b, this._buffer[this._offset + l], l, this) && d.push(this._buffer[this._offset + l]); - } - return d; + b.getQualifiedClassName = function() { + return D.getQualifiedClassName.call(this); }; - c.prototype.some = function(a, c) { - 2 !== arguments.length ? s("ArgumentError", k.Errors.WrongArgumentCountError) : b.isFunction(a) || s("ArgumentError", k.Errors.CheckTypeFailedError); - for (var d = 0;d < this._length;d++) { - if (a.call(c, this._buffer[this._offset + d], d, this)) { - return!0; + b._setPropertyIsEnumerable = function(b, a, d) { + a = u.getPublicQualifiedName(a); + d = f(b, a); + d.enumerable = !1; + Object.defineProperty(b, a, d); + }; + b._dontEnumPrototype = function(b) { + for (var a in b) { + if (u.isPublicQualifiedName(a)) { + var d = f(b, a); + d.enumerable = !1; + Object.defineProperty(b, a, d); } } + }; + b._init = function() { + this.dynamicPrototype.asSetPublicProperty("hasOwnProperty", b.prototype.native_hasOwnProperty); + this.dynamicPrototype.asSetPublicProperty("propertyIsEnumerable", b.prototype.native_propertyIsEnumerable); + this.dynamicPrototype.asSetPublicProperty("setPropertyIsEnumerable", b.prototype.setPropertyIsEnumerable); + this.dynamicPrototype.asSetPublicProperty("isPrototypeOf", b.prototype.native_isPrototypeOf); + this.dynamicPrototype.asSetPublicProperty("toString", b.prototype.toString); + this.dynamicPrototype.asSetPublicProperty("valueOf", b.prototype.valueOf); + b._dontEnumPrototype(this.dynamicPrototype); + }; + b.prototype.native_isPrototypeOf = function(b) { + d("isPrototypeOf"); return!1; }; - c.prototype.forEach = function(a, b) { - for (var c = 0;c < this._length;c++) { - a.call(b, this._buffer[this._offset + c], c, this); - } + b.prototype.native_hasOwnProperty = function(b) { + return this.asHasOwnProperty(null, b, 0); }; - c.prototype.join = function(a) { - t("Int32Vector.join"); + b.prototype.native_propertyIsEnumerable = function(b) { + return this.asPropertyIsEnumerable(null, b, 0); }; - c.prototype.indexOf = function(a, b) { - t("Int32Vector.indexOf"); + b.prototype.setPropertyIsEnumerable = function(a, d) { + b._setPropertyIsEnumerable(this, a, d); }; - c.prototype.lastIndexOf = function(a, b) { - t("Int32Vector.lastIndexOf"); + b.prototype.toString = function() { + var b = z(this); + return b instanceof y ? c.StringUtilities.concat3("[class ", b.classInfo.instanceInfo.name.name, "]") : c.StringUtilities.concat3("[object ", b.class.classInfo.instanceInfo.name.name, "]"); }; - c.prototype.map = function(a, e) { - b.isFunction(a) || s("ArgumentError", k.Errors.CheckTypeFailedError); - for (var d = new c, l = 0;l < this._length;l++) { - d.push(a.call(e, this._buffer[this._offset + l], l, this)); + b.baseClass = null; + b.instanceConstructor = Object; + b.instanceConstructorNoInitialize = null; + b.initializer = null; + b.initializers = null; + b.classInitializer = null; + b.callableConstructor = b.instanceConstructor; + b.defaultValue = null; + b.initializationFlags = 0; + b.call = Function.prototype.call; + b.apply = Function.prototype.apply; + b.coerce = w.asCoerceObject; + b.defineProperty = Object.defineProperty; + return b; + }(); + a.ASObject = N; + var K = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + a.baseClass = null; + a.classInfo = null; + a.instanceConstructor = null; + a.callableConstructor = null; + a.classBindings = null; + a.instanceBindings = null; + a.staticNatives = null; + a.instanceNatives = null; + a.traitsPrototype = null; + a.dynamicPrototype = null; + a.defaultValue = null; + a.initializationFlags = 0; + return a; + }(N); + a.ASNative = K; + var y = function(b) { + function a(b) { + this.classInfo = b; + this.instanceNatives = this.staticNatives = null; + this.initializationFlags = 0; + this.defaultValue = null; + } + __extends(a, b); + a.configureBuiltinPrototype = function(b, a) { + g(b.instanceConstructor); + b.baseClass = a; + b.dynamicPrototype = b.traitsPrototype = b.instanceConstructor.prototype; + }; + a.configurePrototype = function(b, a) { + b.baseClass = a; + b.dynamicPrototype = Object.create(a.dynamicPrototype); + b.traitsPrototype = Object.create(b.dynamicPrototype); + for (var d = b.traitsPrototype, g = [];b;) { + g.push(b), b = b.baseClass; + } + for (var f = 0;f < g.length;f++) { + var k = [g[f].typeScriptPrototype]; + g[f].instanceNatives && c.ArrayUtilities.pushMany(k, g[f].instanceNatives); + for (var r = 0;r < k.length;r++) { + var m = k[r], w; + for (w in m) { + if (!(0 < f && "toString" === w) && e(m, w) && !e(d, w)) { + var q = Object.getOwnPropertyDescriptor(m, w); + c.Debug.assert(q); + try { + Object.defineProperty(d, w, q); + } catch (n) { + } + } + } + } } - return d; }; - c.prototype.push = function() { - for (var a = 0;a < arguments.length - 0;a++) { - } - this._checkFixed(); - this._ensureCapacity(this._length + arguments.length); - for (a = 0;a < arguments.length;a++) { - this._buffer[this._offset + this._length++] = arguments[a]; - } + a.create = function(b, d, f) { + g(!b.instanceConstructorNoInitialize, "This should not be set yet."); + g(!b.dynamicPrototype && !b.traitsPrototype, "These should not be set yet."); + b.typeScriptPrototype = b.prototype; + b.instanceConstructor && !k(b.instanceConstructor) ? a.configureBuiltinPrototype(b, d) : a.configurePrototype(b, d); + b.instanceConstructor || (b.instanceConstructor = f, b !== f && (b.instanceConstructor.__proto__ = b)); + b.callableConstructor || (b.callableConstructor = b.coerce.bind(b)); + b.instanceConstructorNoInitialize = b.instanceConstructor; + b.instanceConstructor.prototype = b.traitsPrototype; + t(b.instanceConstructor.prototype, "class", b); + t(b.dynamicPrototype, u.getPublicQualifiedName("constructor"), b); + b.protocol && c.ObjectUtilities.copyOwnPropertyDescriptors(b.traitsPrototype, b.protocol); }; - c.prototype.pop = function() { - this._checkFixed(); - if (0 === this._length) { - return c.DEFAULT_VALUE; - } - this._length--; - return this._buffer[this._offset + this._length]; + a.prototype.initializeFrom = function(b) { + var d = Object.create(this.traitsPrototype); + a.runInitializers(d, b); + return d; }; - c.prototype.reverse = function() { - for (var a = this._offset, b = this._offset + this._length - 1, c = this._buffer;a < b;) { - var d = c[a]; - c[a] = c[b]; - c[b] = d; - a++; - b--; + a.runInitializers = function(b, a) { + a = a || b.class.defaultInitializerArgument; + var d = b.class.initializers; + if (d) { + for (var g = 0;g < d.length;g++) { + d[g].call(b, a); + } } }; - c._sort = function(a) { - for (var b = [], c = -1, d = 0, n = a.length - 1, f, r, h, x;;) { - if (100 >= n - d) { - for (r = d + 1;r <= n;r++) { - h = a[r]; - for (f = r - 1;f >= d && a[f] > h;) { - a[f + 1] = a[f--]; + a.configureInitializers = function(b) { + b.baseClass && b.baseClass.initializers && (b.initializers = b.baseClass.initializers.slice(0)); + b.initializer && (b.initializers || (b.initializers = []), b.initializers.push(b.initializer)); + b.initializers && (g(b.instanceConstructorNoInitialize === b.instanceConstructor), b.instanceConstructor = function() { + a.runInitializers(this, void 0); + return b.instanceConstructorNoInitialize.apply(this, arguments); + }, b.instanceConstructor.prototype = b.traitsPrototype, t(b.instanceConstructor.prototype, "class", b), b.instanceConstructor.classInfo = b.classInfo, b.instanceConstructor.__proto__ = b); + }; + a.runClassInitializer = function(b) { + b.classInitializer && b.classInitializer(); + }; + a.linkSymbols = function(b) { + function a(b, f, k) { + for (var e = 0;e < f.length;e++) { + var r = f[e], c; + a: { + c = b; + for (var w = r.name.name, q = 0;q < c.length;q++) { + var n = c[q]; + if (0 <= n.indexOf(w) && ("!" === n[n.length - 1] && (n = n.slice(0, n.length - 1)), w === n)) { + c = !0; + break a; + } } - a[f + 1] = h; - } - if (-1 == c) { - break; + c = !1; } - n = b[c--]; - d = b[c--]; - } else { - x = d + n >> 1; - f = d + 1; - r = n; - h = a[x]; - a[x] = a[f]; - a[f] = h; - a[d] > a[n] && (h = a[d], a[d] = a[n], a[n] = h); - a[f] > a[n] && (h = a[f], a[f] = a[n], a[n] = h); - a[d] > a[f] && (h = a[d], a[d] = a[f], a[f] = h); - for (x = a[f];;) { - do { - f++; - } while (a[f] < x); - do { - r--; - } while (a[r] > x); - if (r < f) { + if (c) { + g(!r.name.getNamespace().isPrivate(), "Why are you linking against private members?"); + if (r.isConst()) { + d("Don't link against const traits."); break; } - h = a[f]; - a[f] = a[r]; - a[r] = h; + c = r.name.name; + w = u.getQualifiedName(r.name); + r.isSlot() ? Object.defineProperty(k, c, {get:new Function("", "return this." + w + "//# sourceURL=get-" + w + ".as"), set:new Function("v", "this." + w + " = v;//# sourceURL=set-" + w + ".as")}) : r.isMethod() ? (g(!k[c], "Symbol should not already exist."), g(k.asOpenMethods[w], "There should be an open method for this symbol."), k[c] = k.asOpenMethods[w]) : r.isGetter() ? (g(m(k, w), "There should be an getter method for this symbol."), Object.defineProperty(k, c, {get:new Function("", + "return this." + w + "//# sourceURL=get-" + w + ".as")})) : d(r); } - a[d + 1] = a[r]; - a[r] = x; - n - f + 1 >= r - d ? (b[++c] = f, b[++c] = n, n = r - 1) : (b[++c] = d, b[++c] = r - 1, d = f); } } - return a; + b.classSymbols && a(b.classSymbols, b.classInfo.traits, b); + b.instanceSymbols && a(b.instanceSymbols, b.classInfo.instanceInfo.traits, b.traitsPrototype); }; - c.prototype._sortNumeric = function(a) { - c._sort(this._view()); - a && this.reverse(); + a.prototype.morphIntoASClass = function(b) { + g(this.classInfo === b); + g(this instanceof a); }; - c.prototype.sort = function() { - if (0 === arguments.length) { - return Array.prototype.sort.call(this._view()); - } - var a, e = 0; - arguments[0] instanceof Function ? a = arguments[0] : b.isNumber(arguments[0]) && (e = arguments[0]); - b.isNumber(arguments[1]) && (e = arguments[1]); - if (e & c.NUMERIC) { - return this._sortNumeric(e & c.DESCENDING); - } - Array.prototype.sort.call(this._view(), function(b, d) { - return c.compare(b, d, e, a); - }); + Object.defineProperty(a.prototype, "native_prototype", {get:function() { + g(this.dynamicPrototype); + return this.dynamicPrototype; + }, enumerable:!0, configurable:!0}); + a.prototype.asCall = function(b, a) { + return this.coerce(a); }; - c.prototype.asGetNumericProperty = function(a) { - d(a, this._length); - return this._buffer[this._offset + a]; + a.prototype.asApply = function(b, a) { + return this.coerce(a[0]); + }; + a.prototype.applyType = function(b) { + debugger; + return null; }; - c.prototype.asSetNumericProperty = function(b, c) { - a(b, this._length, this._fixed); - b === this._length && (this._ensureCapacity(this._length + 1), this._length++); - this._buffer[this._offset + b] = c; + a.prototype.isInstanceOf = function(b) { + return this.isInterface() ? !1 : this.isType(b); }; - c.prototype.shift = function() { - this._checkFixed(); - if (0 === this._length) { - return 0; + a.prototype.isType = function(b) { + if (c.isNullOrUndefined(b)) { + return!1; } - this._length--; - return this._buffer[this._offset++]; + b = z(b); + if (this.isInterface()) { + if (null === b || "object" !== typeof b) { + return!1; + } + g(b.class.implementedInterfaces, "No 'implementedInterfaces' map found on class " + b.class); + var a = u.getQualifiedName(this.classInfo.instanceInfo.name); + return void 0 !== b.class.implementedInterfaces[a]; + } + return this.dynamicPrototype.isPrototypeOf(b); + }; + a.prototype.isSubtypeOf = function(b) { + for (var a = this;a;) { + if (a.traitsPrototype === b.traitsPrototype) { + return!0; + } + a = a.baseClass; + } + return!1; }; - c.prototype._checkFixed = function() { - this._fixed && s("RangeError", k.Errors.VectorFixedError); + a.prototype.coerce = function(b) { + return b; }; - c.prototype._slide = function(a) { - this._buffer.set(this._view(), this._offset + a); - this._offset += a; + a.prototype.isInterface = function() { + return this.classInfo.instanceInfo.isInterface(); }; - c.prototype.unshift = function() { - this._checkFixed(); - if (arguments.length) { - this._ensureCapacity(this._length + arguments.length); - this._slide(arguments.length); - this._offset -= arguments.length; - this._length += arguments.length; - for (var a = 0;a < arguments.length;a++) { - this._buffer[this._offset + a] = arguments[a]; + a.prototype.getQualifiedClassName = function() { + var b = this.classInfo.instanceInfo.name, a = b.namespaces[0].uri; + return a ? a + "::" + b.name : b.name; + }; + a.prototype.verify = function() { + function b(a, d, g) { + for (var f = 0;f < a.length;f++) { + if (d(a[f], g)) { + return!0; + } } + return!1; } - }; - c.prototype.asHasProperty = function(a, e, d) { - if (c.prototype === this || !b.isNumeric(e)) { - return Object.prototype.asHasProperty.call(this, a, e, d); + if (!this.isInterface()) { + var a = [this.classInfo.traits, this.classInfo.instanceInfo.traits], d = [this]; + this.staticNatives && c.ArrayUtilities.pushMany(d, this.staticNatives); + var f = [this.prototype]; + this.instanceNatives && c.ArrayUtilities.pushMany(f, this.instanceNatives); + this === N ? g(!this.baseClass, "ASObject should have no base class.") : (g(this.baseClass, this.classInfo.instanceInfo.name + " has no base class."), g(this.baseClass !== this)); + g(this.traitsPrototype === this.instanceConstructor.prototype, "The traitsPrototype is not set correctly."); + for (var k = 0;k < a.length;k++) { + for (var e = 0 === k, r = 0;r < a[k].length;r++) { + var m = a[k][r], w = v(m.name.name); + if (m.isMethodOrAccessor() && m.methodInfo.isNative()) { + var q = e ? d : f; + m.isMethod() ? b(q, c.ObjectUtilities.hasOwnProperty, w) : m.isGetter() ? b(q, c.ObjectUtilities.hasOwnGetter, w) : m.isSetter() && b(q, c.ObjectUtilities.hasOwnSetter, w); + } + } + } + c.Debug.assert(this.instanceConstructor, "Must have a constructor function."); } - a = b.toNumber(e); - return 0 <= a && a < this._length; }; - Object.defineProperty(c.prototype, "length", {get:function() { - return this._length; - }, set:function(a) { - a >>>= 0; - if (a > this._length) { - this._ensureCapacity(a); - for (var b = this._offset + this._length, d = this._offset + a;b < d;b++) { - this._buffer[b] = c.DEFAULT_VALUE; - } + a.labelObject = function(b) { + if (!b) { + return b; } - this._length = a; + e(b, "labelId") || (b.labelId = a.labelCounter++); + return b instanceof Function ? "Function [#" + b.labelId + "]" : "Object [#" + b.labelId + "]"; + }; + a.prototype.trace = function(b) { + if (this.isInterface()) { + b.enter("Interface: " + this.classInfo), this.interfaceBindings.trace(b); + } else { + b.enter("Class: " + this.classInfo); + b.writeLn("baseClass: " + (this.baseClass ? this.baseClass.classInfo.instanceInfo.name : null)); + this.classBindings.trace(b); + this.instanceBindings.trace(b); + b.enter("Interfaces"); + for (var a in this.implementedInterfaces) { + b.writeLn(this.implementedInterfaces[a].classInfo.toString()); + } + b.leave(); + } + b.leave(); + }; + a.instanceConstructor = a; + a.staticNatives = null; + a.instanceNatives = null; + a.labelCounter = 0; + return a; + }(N); + a.ASClass = y; + var D = y.prototype; + D.call = Function.prototype.call; + D.apply = Function.prototype.apply; + var L = function(b) { + function a() { + } + __extends(a, b); + Object.defineProperty(a.prototype, "native_prototype", {get:function() { + return this.prototype; + }, set:function(b) { + this.prototype = b; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "fixed", {get:function() { - return this._fixed; - }, set:function(a) { - this._fixed = !!a; + Object.defineProperty(a.prototype, "native_length", {get:function() { + return this.hasOwnProperty(w.VM_LENGTH) ? this.asLength : this.length; }, enumerable:!0, configurable:!0}); - c.prototype._spliceHelper = function(a, b, c, d, n) { - b = m(b, 0, d.length - n); - c = m(c, 0, this._length - a); - this._ensureCapacity(this._length - c + b); - var f = this._offset + a + c; - this._buffer.set(this._buffer.subarray(f, f + this._length - a - c), this._offset + a + b); - this._length += b - c; - for (c = 0;c < b;c++) { - this._buffer[this._offset + a + c] = d.asGetNumericProperty(n + c); - } + a.prototype.toString = function() { + return "function Function() {}"; }; - c.prototype.asNextName = function(a) { - return a - 1; + a.baseClass = null; + a.instanceConstructor = Function; + a.staticNatives = [Function]; + a.instanceNatives = [Function.prototype]; + return a; + }(N); + a.ASFunction = L; + var H = function(b) { + function a(b) { + } + __extends(a, b); + a.instanceConstructor = Boolean; + a.callableConstructor = a.instanceConstructor; + a.staticNatives = null; + a.instanceNatives = null; + a.coerce = w.asCoerceBoolean; + return a; + }(N); + a.ASBoolean = H; + H.prototype.toString = Boolean.prototype.toString; + H.prototype.valueOf = Boolean.prototype.valueOf; + var J = function(b) { + function a(b, d) { + var g = c.FunctionUtilities.bindSafely(d, b); + t(this, "call", g.call.bind(g)); + t(this, "apply", g.apply.bind(g)); + } + __extends(a, b); + a.prototype.toString = function() { + return "function Function() {}"; }; - c.prototype.asNextValue = function(a) { - return this._buffer[this._offset + a - 1]; + a.staticNatives = null; + a.instanceNatives = null; + return a.instanceConstructor = a; + }(L); + a.ASMethodClosure = J; + var C = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + a._numberToString = function(b, a) { + return Number(b).toString(a | 0); }; - c.prototype.asNextNameIndex = function(a) { - a += 1; - return a <= this._length ? a : 0; + a._minValue = function() { + return Number.MIN_VALUE; }; - c.prototype.asHasNext2 = function(a) { - a.index = this.asNextNameIndex(a.index); + a.instanceConstructor = Number; + a.callableConstructor = a.instanceConstructor; + a.staticNatives = [Math]; + a.instanceNatives = [Number.prototype]; + a.defaultValue = Number(0); + a.coerce = w.asCoerceNumber; + return a; + }(N); + a.ASNumber = C; + var E = function(b) { + function a(b) { + return Object(Number(b | 0)); + } + __extends(a, b); + a.asCall = function(b, a) { + return a | 0; }; - c.EXTRA_CAPACITY = 4; - c.INITIAL_CAPACITY = 10; - c.DEFAULT_VALUE = 0; - c.CASEINSENSITIVE = 1; - c.DESCENDING = 2; - c.UNIQUESORT = 4; - c.RETURNINDEXEDARRAY = 8; - c.NUMERIC = 16; - return c; - }(); - g.Int32Vector = c; - c.prototype._reverse = c.prototype.reverse; - c.prototype._filter = c.prototype.filter; - c.prototype._map = c.prototype.map; - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - var f = b.Debug.assertNotImplemented, t = b.Debug.notImplemented, s = b.AVM2.Runtime.throwError, m = b.NumberUtilities.clamp, d = b.AVM2.Runtime.asCheckVectorGetNumericProperty, a = b.AVM2.Runtime.asCheckVectorSetNumericProperty, c = function() { - function c(a, b) { - "undefined" === typeof a && (a = 0); - "undefined" === typeof b && (b = !1); - a >>>= 0; - this._fixed = !!b; - this._buffer = new Uint32Array(Math.max(c.INITIAL_CAPACITY, a + c.EXTRA_CAPACITY)); - this._offset = 0; - this._length = a; + a.asApply = function(b, a) { + return a[0] | 0; + }; + a.isInstanceOf = function(b) { + return!1; + }; + a.isType = function(b) { + return q(b) || b instanceof Number ? (b = +b, (b | 0) === b) : !1; + }; + a.instanceConstructor = a; + a.callableConstructor = a.instanceConstructor; + a.staticNatives = [Math]; + a.instanceNatives = [Number.prototype]; + a.defaultValue = 0; + a.coerce = w.asCoerceInt; + return a; + }(N); + a.ASInt = E; + var F = function(b) { + function a(b) { + return Object(Number(b >>> 0)); } - c.defaultCompareFunction = function(a, b) { - return String(a).localeCompare(String(b)); + __extends(a, b); + a.asCall = function(b, a) { + return a >>> 0; }; - c.compare = function(a, e, d, l) { - f(!(d & c.CASEINSENSITIVE), "CASEINSENSITIVE"); - f(!(d & c.UNIQUESORT), "UNIQUESORT"); - f(!(d & c.RETURNINDEXEDARRAY), "RETURNINDEXEDARRAY"); - var g = 0; - l || (l = c.defaultCompareFunction); - d & c.NUMERIC ? (a = b.toNumber(a), e = b.toNumber(e), g = a < e ? -1 : a > e ? 1 : 0) : g = l(a, e); - d & c.DESCENDING && (g *= -1); - return g; + a.asApply = function(b, a) { + return a[0] >>> 0; }; - c.callable = function(a) { - if (a instanceof c) { - return a; + a.isInstanceOf = function(b) { + return!1; + }; + a.isType = function(b) { + return q(b) || b instanceof Number ? (b = +b, b >>> 0 === b) : !1; + }; + a.instanceConstructor = a; + a.callableConstructor = a.instanceConstructor; + a.staticNatives = [Math]; + a.instanceNatives = [Number.prototype]; + a.defaultValue = 0; + a.coerce = w.asCoerceUint; + return a; + }(N); + a.ASUint = F; + var I = function(a) { + function d() { + a.apply(this, arguments); + } + __extends(d, a); + Object.defineProperty(d.prototype, "native_length", {get:function() { + return this.length; + }, enumerable:!0, configurable:!0}); + d.prototype.match = function(b) { + if (void 0 === b || null === b) { + return null; } - var e = a.asGetProperty(void 0, "length"); - if (void 0 !== e) { - for (var d = new c(e, !1), l = 0;l < e;l++) { - d.asSetNumericProperty(l, a.asGetPublicProperty(l)); + if (b instanceof h.XRegExp && b.global) { + for (var a = [], d;d = b.exec(this);) { + a.push(d[0]); } - return d; + return a; } - b.Debug.unexpected(); + b instanceof h.XRegExp || "string" === typeof b || (b = String(b)); + return this.match(b); }; - c.prototype.internalToString = function() { - for (var a = "", b = this._offset, c = b + this._length, d = 0;d < this._buffer.length;d++) { - d === b && (a += "["), d === c && (a += "]"), a += this._buffer[d], d < this._buffer.length - 1 && (a += ","); - } - this._offset + this._length === this._buffer.length && (a += "]"); - return a + ": offset: " + this._offset + ", length: " + this._length + ", capacity: " + this._buffer.length; + d.prototype.search = function(a) { + return a instanceof h.XRegExp ? this.search(a) : this.indexOf(b(a)); + }; + d.prototype.toUpperCase = function() { + var b = String.prototype.toUpperCase.apply(this); + return b = b.replace(/\u039C/g, String.fromCharCode(181)); }; - c.prototype.toString = function() { - for (var a = "", b = 0;b < this._length;b++) { - a += this._buffer[this._offset + b], b < this._length - 1 && (a += ","); + d.prototype.toLocaleUpperCase = function() { + var b = String.prototype.toLocaleUpperCase.apply(this); + return b = b.replace(/\u039C/g, String.fromCharCode(181)); + }; + d.instanceConstructor = String; + d.callableConstructor = d.instanceConstructor; + d.staticNatives = [String]; + d.instanceNatives = [String.prototype]; + d.coerce = w.asCoerceString; + return d; + }(N); + a.ASString = I; + a.arraySort = function(b, a) { + if (0 === a.length) { + return b.sort(); + } + var d, g = 0; + a[0] instanceof Function ? d = a[0] : q(a[0]) && (g = a[0]); + q(a[1]) && (g = a[1]); + b.sort(function(b, a) { + return M(b, a, g, d); + }); + return b; + }; + var G = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + a.prototype.toLocaleString = function() { + for (var b = a.coerce(this), d = "", g = 0, f = b.length;g < f;g++) { + var k = b[g]; + null !== k && void 0 !== k && (d += k.toLocaleString()); + g + 1 < f && (d += ","); } - return a; + return d; }; - c.prototype._view = function() { - return this._buffer.subarray(this._offset, this._offset + this._length); + a.prototype.splice = function() { + return 0 === arguments.length ? void 0 : this.splice.apply(this, arguments); }; - c.prototype._ensureCapacity = function(a) { - var b = this._offset + a; - b < this._buffer.length || (a <= this._buffer.length ? (b = this._buffer.length - a >> 2, this._buffer.set(this._view(), b), this._offset = b) : (a = (3 * this._buffer.length >> 1) + 1, a < b && (a = b), b = new Uint32Array(a), b.set(this._buffer, 0), this._buffer = b)); - }; - c.prototype.concat = function() { - t("Uint32Vector.concat"); - }; - c.prototype.every = function(a, b) { - for (var c = 0;c < this._length;c++) { - if (!a.call(b, this._buffer[this._offset + c], c, this)) { - return!1; - } + a.prototype.every = function(b, a) { + for (var d = 0;d < this.length && !0 === b.call(a, this[d], d, this);d++) { } - return!0; + return!1; }; - c.prototype.filter = function(a, b) { - for (var d = new c, l = 0;l < this._length;l++) { - a.call(b, this._buffer[this._offset + l], l, this) && d.push(this._buffer[this._offset + l]); + a.prototype.filter = function(b, a) { + for (var d = [], g = 0;g < this.length;g++) { + !0 === b.call(a, this[g], g, this) && d.push(this[g]); } return d; }; - c.prototype.some = function(a, c) { - 2 !== arguments.length ? s("ArgumentError", k.Errors.WrongArgumentCountError) : b.isFunction(a) || s("ArgumentError", k.Errors.CheckTypeFailedError); - for (var d = 0;d < this._length;d++) { - if (a.call(c, this._buffer[this._offset + d], d, this)) { - return!0; + a.prototype.sort = function() { + if (0 === arguments.length) { + return this.sort(); + } + var b, a = 0; + arguments[0] instanceof Function ? b = arguments[0] : q(arguments[0]) && (a = arguments[0]); + q(arguments[1]) && (a = arguments[1]); + this.sort(function(d, g) { + return w.asCompare(d, g, a, b); + }); + return this; + }; + a.prototype.sortOn = function(b, d) { + c.isString(b) && (b = [b]); + n(d) ? d = [] : q(d) && (d = [d]); + for (var g = b.length - 1;0 <= g;g--) { + var f = u.getPublicQualifiedName(b[g]); + if (a.CACHE_NUMERIC_COMPARATORS && d[g] & 16) { + var k = "var x = +(a." + f + "), y = +(b." + f + ");", k = d[g] & 2 ? k + "return x < y ? 1 : (x > y ? -1 : 0);" : k + "return x < y ? -1 : (x > y ? 1 : 0);", e = a.numericComparatorCache[k]; + e || (e = a.numericComparatorCache[k] = new Function("a", "b", k)); + this.sort(e); + } else { + this.sort(function(b, a) { + return w.asCompare(b[f], a[f], d[g] | 0); + }); } } - return!1; + return this; + }; + Object.defineProperty(a.prototype, "native_length", {get:function() { + return this.length; + }, set:function(b) { + this.length = b >>> 0; + }, enumerable:!0, configurable:!0}); + a.instanceConstructor = Array; + a.staticNatives = [Array]; + a.instanceNatives = [Array.prototype]; + a.classInitializer = function() { + var b = Array.prototype, d = a.prototype; + t(b, "$Bgjoin", b.join); + t(b, "$BgtoString", b.join); + t(b, "$BgtoLocaleString", d.toLocaleString); + t(b, "$Bgpop", b.pop); + t(b, "$Bgpush", b.push); + t(b, "$Bgreverse", b.reverse); + t(b, "$Bgconcat", b.concat); + t(b, "$Bgsplice", d.splice); + t(b, "$Bgslice", b.slice); + t(b, "$Bgshift", b.shift); + t(b, "$Bgunshift", b.unshift); + t(b, "$BgindexOf", b.indexOf); + t(b, "$BglastIndexOf", b.lastIndexOf); + t(b, "$BgforEach", b.forEach); + t(b, "$Bgmap", b.map); + t(b, "$Bgfilter", b.filter); + t(b, "$Bgsome", b.some); + t(b, "$Bgevery", d.every); + t(b, "$Bgsort", d.sort); + t(b, "$BgsortOn", d.sortOn); + }; + a.CACHE_NUMERIC_COMPARATORS = !0; + a.numericComparatorCache = Object.create(null); + return a; + }(N); + a.ASArray = G; + var Z = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + a.prototype.newThisType = function() { + return new this.class.instanceConstructor; + }; + a.staticNatives = null; + a.instanceNatives = null; + a.instanceConstructor = a; + a.callableConstructor = null; + return a; + }(K); + a.ASVector = Z; + var Q = function(a) { + function d() { + a.apply(this, arguments); + } + __extends(d, a); + d.transformJSValueToAS = function(b, a) { + if ("object" !== typeof b || n(b)) { + return b; + } + for (var g = Object.keys(b), f = Array.isArray(b) ? [] : {}, k = 0;k < g.length;k++) { + var e = b[g[k]]; + a && (e = d.transformJSValueToAS(e, !0)); + f.asSetPublicProperty(g[k], e); + } + return f; }; - c.prototype.forEach = function(a, b) { - for (var c = 0;c < this._length;c++) { - a.call(b, this._buffer[this._offset + c], c, this); + d.transformASValueToJS = function(b, a) { + if ("object" !== typeof b || n(b)) { + return b; + } + for (var g = Object.keys(b), f = Array.isArray(b) ? [] : {}, k = 0;k < g.length;k++) { + var e = g[k], r = e; + c.isNumeric(e) || (r = u.getNameFromPublicQualifiedName(e)); + e = b[e]; + a && (e = d.transformASValueToJS(e, !0)); + f[r] = e; } + return f; + }; + d.parseCore = function(a) { + a = b(a); + return d.transformJSValueToAS(JSON.parse(a), !0); }; - c.prototype.join = function(a) { - t("Uint32Vector.join"); + d.stringifySpecializedToString = function(b, a, g, f) { + return JSON.stringify(d.transformASValueToJS(b, !0), g, f); }; - c.prototype.indexOf = function(a, b) { - t("Uint32Vector.indexOf"); + d.instanceConstructor = d; + d.staticNatives = null; + d.instanceNatives = null; + return d; + }(N); + a.ASJSON = Q; + var S = function(b) { + function a(b, g) { + d("ASError"); + } + __extends(a, b); + a.prototype.getStackTrace = function() { + return null; }; - c.prototype.lastIndexOf = function(a, b) { - t("Uint32Vector.lastIndexOf"); + a.instanceConstructor = null; + a.staticNatives = null; + a.instanceNatives = null; + a.getErrorMessage = c.AVM2.getErrorMessage; + return a; + }(K); + a.ASError = S; + var O = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + return a; + }(S); + a.ASDefinitionError = O; + var P = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + return a; + }(S); + a.ASEvalError = P; + var V = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + return a; + }(S); + a.ASRangeError = V; + var $ = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + return a; + }(S); + a.ASReferenceError = $; + var W = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + return a; + }(S); + a.ASSecurityError = W; + var x = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + return a; + }(S); + a.ASSyntaxError = x; + var ea = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + return a; + }(S); + a.ASTypeError = ea; + var Y = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + return a; + }(S); + a.ASURIError = Y; + var R = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + return a; + }(S); + a.ASVerifyError = R; + var U = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + return a; + }(S); + a.ASUninitializedError = U; + var ba = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + return a; + }(S); + a.ASArgumentError = ba; + var X = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + Object.defineProperty(a.prototype, "native_source", {get:function() { + return this.source; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(a.prototype, "native_global", {get:function() { + return this.global; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(a.prototype, "native_ignoreCase", {get:function() { + return this.ignoreCase; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(a.prototype, "native_multiline", {get:function() { + return this.multiline; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(a.prototype, "native_lastIndex", {get:function() { + return this.lastIndex; + }, set:function(b) { + this.lastIndex = b | 0; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(a.prototype, "native_dotall", {get:function() { + return this.dotall; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(a.prototype, "native_extended", {get:function() { + return this.extended; + }, enumerable:!0, configurable:!0}); + a.prototype.exec = function(b) { + void 0 === b && (b = ""); + var a = RegExp.prototype.exec.apply(this, arguments); + if (!a) { + return a; + } + for (var d = Object.keys(a), g = 0;g < d.length;g++) { + var f = d[g]; + c.isNumeric(f) || void 0 === a[f] && (a[f] = ""); + } + c.AVM2.Runtime.publicizeProperties(a); + return a; }; - c.prototype.map = function(a, e) { - b.isFunction(a) || s("ArgumentError", k.Errors.CheckTypeFailedError); - for (var d = new c, l = 0;l < this._length;l++) { - d.push(a.call(e, this._buffer[this._offset + l], l, this)); + a.instanceConstructor = h.XRegExp; + a.staticNatives = [h.XRegExp]; + a.instanceNatives = [h.XRegExp.prototype]; + return a; + }(N); + a.ASRegExp = X; + var ga = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + a.staticNatives = [Math]; + return a; + }(K); + a.ASMath = ga; + var ja = function(b) { + function a() { + b.apply(this, arguments); + } + __extends(a, b); + a.staticNatives = [Date]; + a.instanceNatives = [Date.prototype]; + a.instanceConstructor = Date; + return a; + }(K); + a.ASDate = ja; + var aa = c.ObjectUtilities.createMap(), ia = !1; + a.initialize = function(b) { + ia || (aa.ObjectClass = N, aa.Class = y, aa.FunctionClass = L, aa.BooleanClass = H, aa.MethodClosureClass = J, aa.NamespaceClass = a.ASNamespace, aa.NumberClass = C, aa.IntClass = E, aa.UIntClass = F, aa.StringClass = I, aa.ArrayClass = G, aa.VectorClass = Z, aa.ObjectVectorClass = a.GenericVector, aa.IntVectorClass = a.Int32Vector, aa.UIntVectorClass = a.Uint32Vector, aa.DoubleVectorClass = a.Float64Vector, aa.JSONClass = Q, aa.XMLClass = a.ASXML, aa.XMLListClass = a.ASXMLList, aa.QNameClass = + a.ASQName, aa.ErrorClass = S, aa.DefinitionErrorClass = O, aa.EvalErrorClass = P, aa.RangeErrorClass = V, aa.ReferenceErrorClass = $, aa.SecurityErrorClass = W, aa.SyntaxErrorClass = x, aa.TypeErrorClass = ea, aa.URIErrorClass = Y, aa.VerifyErrorClass = R, aa.UninitializedErrorClass = U, aa.ArgumentErrorClass = ba, aa.DateClass = ja, aa.MathClass = ga, aa.RegExpClass = X, aa.ProxyClass = a.flash.utils.OriginalProxy, aa.DictionaryClass = a.flash.utils.OriginalDictionary, aa.ByteArrayClass = + a.flash.utils.OriginalByteArray, aa.SystemClass = a.flash.system.OriginalSystem, ia = !0); + }; + var T = c.ObjectUtilities.createMap(), da = c.ObjectUtilities.createMap(); + a.registerNativeClass = function(b, a) { + g(!T[b], "Native class: " + b + " is already registered."); + T[b] = a; + }; + a.registerNativeFunction = s; + a.createInterface = function(b) { + var a = b.instanceInfo; + g(a.isInterface()); + b = new y(b); + b.interfaceBindings = new B(null, a, null, null); + b.verify(); + return b; + }; + var fa = []; + a.createClass = function(b, a, d) { + var f = b.instanceInfo, k = b.abc.applicationDomain, e = b.native, m; + e ? ((m = aa[b.native.cls]) || (m = T[b.native.cls]), m || c.Debug.unexpected("No native class for " + b.native.cls), m.morphIntoASClass(b), fa && fa.push(m)) : m = new y(b); + d = new l(d, null); + d.object = m; + var w = null; + f.init.isNative() ? (g(e), w = m) : w = r(f.init, d, !1, !1); + var q = null, n = null; + e && (q = [m], m.staticNatives && c.ArrayUtilities.pushMany(q, m.staticNatives), n = [m.prototype], m.instanceNatives && c.ArrayUtilities.pushMany(n, m.instanceNatives)); + y.create(m, a, w); + m.verify(); + if ("Class" === b.instanceInfo.name.name) { + for (e = 0;e < fa.length;e++) { + fa[e].__proto__ = y.prototype; + } + fa = null; + } + h.enterTimeline("InstanceBindings"); + m.instanceBindings = new B(a ? a.instanceBindings : null, f, d, n); + m.instanceConstructor && (h.enterTimeline("applyTo"), m.instanceBindings.applyTo(k, m.traitsPrototype), h.leaveTimeline()); + h.leaveTimeline(); + h.enterTimeline("ClassBindings"); + m.classBindings = new A(b, d, q); + h.enterTimeline("applyTo"); + m.classBindings.applyTo(k, m); + m === y ? m.instanceBindings.applyTo(k, N, !0) : y.instanceBindings && y.instanceBindings.applyTo(k, m, !0); + h.leaveTimeline(); + h.leaveTimeline(); + m.implementedInterfaces = m.instanceBindings.implementedInterfaces; + h.enterTimeline("Configure"); + y.configureInitializers(m); + y.linkSymbols(m); + y.runClassInitializer(m); + h.leaveTimeline(); + return m; + }; + var la = [w.forwardValueOf, w.forwardToString]; + a.getMethodOrAccessorNative = function(b, a) { + for (var d = v(u.getName(b.name)), k = 0;k < a.length;k++) { + var r = a[k], m = d; + e(r, "original_" + d) && (m = "original_" + d); + !e(r, d) && e(r, "native_" + d) && (m = "native_" + d); + if (e(r, m)) { + return b.isAccessor() ? (d = f(r, m), d = b.isGetter() ? d.get : d.set) : (g(b.isMethod()), d = r[m]), g(d, "Method or Accessor property exists but it's undefined: " + b), g(0 > la.indexOf(d), "Leaking illegal function."), d; + } + } + c.Debug.warning("No native method for: " + b.kindName() + " " + b.methodInfo.holder + "::" + u.getQualifiedName(b.name) + ", make sure you've got the static keyword for static methods."); + return null; + }; + a.escapeNativeName = v; + var ca; + (function(b) { + function a(b) { + for (var d = {prototype:Object.create(null)}, g = Object.getOwnPropertyNames(b.prototype), f = 0;f < g.length;f++) { + d.prototype[g[f]] = b.prototype[g[f]]; } return d; + } + b.String = jsGlobal.String; + b.Function = jsGlobal.Function; + b.Boolean = jsGlobal.Boolean; + b.Number = jsGlobal.Number; + b.Date = jsGlobal.Date; + b.ASObject = c.AVM2.AS.ASObject; + b.ASFunction = c.AVM2.AS.ASFunction; + b.Original = {Date:a(b.Date), Array:a(Array), String:a(b.String), Number:a(b.Number), Boolean:a(b.Boolean)}; + b.print = function(b, a, d, g, f) { + jsGlobal.print.apply(null, arguments); }; - c.prototype.push = function() { - for (var a = 0;a < arguments.length - 0;a++) { - } - this._checkFixed(); - this._ensureCapacity(this._length + arguments.length); - for (a = 0;a < arguments.length;a++) { - this._buffer[this._offset + this._length++] = arguments[a]; + b.debugBreak = function(b) { + debugger; + }; + b.bugzilla = function(b) { + switch(b) { + case 574600: + return!0; } + return!1; }; - c.prototype.pop = function() { - this._checkFixed(); - if (0 === this._length) { - return c.DEFAULT_VALUE; + b.decodeURI = jsGlobal.decodeURI; + b.decodeURIComponent = jsGlobal.decodeURIComponent; + b.encodeURI = jsGlobal.encodeURI; + b.encodeURIComponent = jsGlobal.encodeURIComponent; + b.isNaN = jsGlobal.isNaN; + b.isFinite = jsGlobal.isFinite; + b.parseInt = jsGlobal.parseInt; + b.parseFloat = jsGlobal.parseFloat; + b.escape = jsGlobal.escape; + b.unescape = jsGlobal.unescape; + b.isXMLName; + b.notImplemented = c.Debug.notImplemented; + b.getQualifiedClassName = function(b) { + if (null === b) { + return "null"; } - this._length--; - return this._buffer[this._offset + this._length]; + if (void 0 === b) { + return "void"; + } + if (E.isType(b)) { + return "int"; + } + b = z(b); + return y.isType(b) ? b.getQualifiedClassName() : b.class.getQualifiedClassName(); }; - c.prototype.reverse = function() { - for (var a = this._offset, b = this._offset + this._length - 1, c = this._buffer;a < b;) { - var d = c[a]; - c[a] = c[b]; - c[b] = d; - a++; - b--; + b.getQualifiedSuperclassName = function(b) { + if (n(b)) { + return "null"; } + b = z(b); + b = y.isType(b) ? b : b.class; + return b.baseClass ? b.baseClass.getQualifiedClassName() : "null"; + }; + b.getDefinitionByName = function(a) { + a = b.String(a).replace("::", "."); + return c.AVM2.Runtime.AVM2.currentDomain().getClass(a, !1) || null; + }; + b.describeType = function(b, a) { + return c.AVM2.AS.describeType(b, a); + }; + b.describeTypeJSON = function(b, a) { + return c.AVM2.AS.describeTypeJSON(b, a); + }; + })(ca = a.Natives || (a.Natives = {})); + a.getNative = p; + s("unsafeJSNative", p); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.Debug.assert, v = c.Debug.assertNotImplemented, p = c.AVM2.Runtime.asCoerceString, u = c.ObjectUtilities.defineNonEnumerableProperty, l = c.AVM2.Runtime.throwError, e = c.AVM2.Runtime.asCheckVectorGetNumericProperty, m = c.AVM2.Runtime.asCheckVectorSetNumericProperty, t = function(q) { + function n(k, f, d) { + void 0 === k && (k = 0); + void 0 === f && (f = !1); + void 0 === d && (d = a.ASObject); + k >>>= 0; + this._fixed = !!f; + this._buffer = Array(k); + this._defaultValue = (this._type = d) ? d.defaultValue : null; + this._fill(k, this._defaultValue); + } + __extends(n, q); + n.prototype.newThisType = function() { + return new n; + }; + n.defaultCompareFunction = function(a, f) { + return String(a).localeCompare(String(f)); + }; + n.compare = function(a, f, d, b) { + v(!(d & n.CASEINSENSITIVE), "CASEINSENSITIVE"); + v(!(d & n.UNIQUESORT), "UNIQUESORT"); + v(!(d & n.RETURNINDEXEDARRAY), "RETURNINDEXEDARRAY"); + var g = 0; + b || (b = n.defaultCompareFunction); + d & n.NUMERIC ? (a = c.toNumber(a), f = c.toNumber(f), g = a < f ? -1 : a > f ? 1 : 0) : g = b(a, f); + d & n.DESCENDING && (g *= -1); + return g; }; - c._sort = function(a) { - for (var b = [], c = -1, d = 0, n = a.length - 1, f, r, h, x;;) { - if (100 >= n - d) { - for (r = d + 1;r <= n;r++) { - h = a[r]; - for (f = r - 1;f >= d && a[f] > h;) { - a[f + 1] = a[f--]; - } - a[f + 1] = h; - } - if (-1 == c) { - break; - } - n = b[c--]; - d = b[c--]; - } else { - x = d + n >> 1; - f = d + 1; - r = n; - h = a[x]; - a[x] = a[f]; - a[f] = h; - a[d] > a[n] && (h = a[d], a[d] = a[n], a[n] = h); - a[f] > a[n] && (h = a[f], a[f] = a[n], a[n] = h); - a[d] > a[f] && (h = a[d], a[d] = a[f], a[f] = h); - for (x = a[f];;) { - do { - f++; - } while (a[f] < x); - do { - r--; - } while (a[r] > x); - if (r < f) { - break; - } - h = a[f]; - a[f] = a[r]; - a[r] = h; + n.applyType = function(k) { + function f(a, b) { + Function.prototype.call.call(n.instanceConstructor, this, a, b, k); + } + f.prototype = n.prototype; + f.instanceConstructor = f; + f.callableConstructor = function(d) { + if (d instanceof a.Int32Vector) { + return d; + } + var b = d.asGetProperty(void 0, "length"); + if (void 0 !== b) { + for (var g = new f(b, !1), k = 0;k < b;k++) { + g.asSetNumericProperty(k, d.asGetPublicProperty(k)); } - a[d + 1] = a[r]; - a[r] = x; - n - f + 1 >= r - d ? (b[++c] = f, b[++c] = n, n = r - 1) : (b[++c] = d, b[++c] = r - 1, d = f); + return g; } + c.Debug.unexpected(); + }; + f.__proto__ = n; + return f; + }; + n.prototype._fill = function(a, f) { + for (var d = 0;d < a;d++) { + this._buffer[0 + d] = f; + } + }; + n.prototype.toString = function() { + for (var a = "", f = 0;f < this._buffer.length;f++) { + a += this._buffer[f], f < this._buffer.length - 1 && (a += ","); } return a; }; - c.prototype._sortNumeric = function(a) { - c._sort(this._view()); - a && this.reverse(); + n.prototype.toLocaleString = function() { + for (var a = "", f = 0;f < this._buffer.length;f++) { + a += this._buffer[f].asCallPublicProperty("toLocaleString"), f < this._buffer.length - 1 && (a += ","); + } + return a; }; - c.prototype.sort = function() { + n.prototype.sort = function(k) { if (0 === arguments.length) { - return Array.prototype.sort.call(this._view()); + return this._buffer.sort(); } - var a, e = 0; - arguments[0] instanceof Function ? a = arguments[0] : b.isNumber(arguments[0]) && (e = arguments[0]); - b.isNumber(arguments[1]) && (e = arguments[1]); - if (e & c.NUMERIC) { - return this._sortNumeric(e & c.DESCENDING); + if (k instanceof Function) { + return this._buffer.sort(k); } - Array.prototype.sort.call(this._view(), function(b, d) { - return c.compare(b, d, e, a); - }); - }; - c.prototype.asGetNumericProperty = function(a) { - d(a, this._length); - return this._buffer[this._offset + a]; - }; - c.prototype.asSetNumericProperty = function(b, c) { - a(b, this._length, this._fixed); - b === this._length && (this._ensureCapacity(this._length + 1), this._length++); - this._buffer[this._offset + b] = c; + var f = k | 0; + v(!(f & a.Int32Vector.UNIQUESORT), "UNIQUESORT"); + v(!(f & a.Int32Vector.RETURNINDEXEDARRAY), "RETURNINDEXEDARRAY"); + return f && n.NUMERIC ? f & n.DESCENDING ? this._buffer.sort(function(a, b) { + return asCoerceNumber(b) - asCoerceNumber(a); + }) : this._buffer.sort(function(a, b) { + return asCoerceNumber(a) - asCoerceNumber(b); + }) : f && n.CASEINSENSITIVE ? f & n.DESCENDING ? this._buffer.sort(function(a, b) { + return p(b).toLowerCase() - p(a).toLowerCase(); + }) : this._buffer.sort(function(a, b) { + return p(a).toLowerCase() - p(b).toLowerCase(); + }) : f & n.DESCENDING ? this._buffer.sort(function(a, b) { + return b - a; + }) : this._buffer.sort(); }; - c.prototype.shift = function() { - this._checkFixed(); - if (0 === this._length) { - return 0; + n.prototype.every = function(a, f) { + for (var d = 0;d < this._buffer.length;d++) { + if (!a.call(f, this.asGetNumericProperty(d), d, this)) { + return!1; + } } - this._length--; - return this._buffer[this._offset++]; - }; - c.prototype._checkFixed = function() { - this._fixed && s("RangeError", k.Errors.VectorFixedError); + return!0; }; - c.prototype._slide = function(a) { - this._buffer.set(this._view(), this._offset + a); - this._offset += a; + n.prototype.filter = function(a, f) { + for (var d = new n(0, !1, this._type), b = 0;b < this._buffer.length;b++) { + a.call(f, this.asGetNumericProperty(b), b, this) && d.push(this.asGetNumericProperty(b)); + } + return d; }; - c.prototype.unshift = function() { - this._checkFixed(); - if (arguments.length) { - this._ensureCapacity(this._length + arguments.length); - this._slide(arguments.length); - this._offset -= arguments.length; - this._length += arguments.length; - for (var a = 0;a < arguments.length;a++) { - this._buffer[this._offset + a] = arguments[a]; + n.prototype.some = function(a, f) { + 2 !== arguments.length ? l("ArgumentError", h.Errors.WrongArgumentCountError) : c.isFunction(a) || l("ArgumentError", h.Errors.CheckTypeFailedError); + for (var d = 0;d < this._buffer.length;d++) { + if (a.call(f, this.asGetNumericProperty(d), d, this)) { + return!0; } } + return!1; }; - c.prototype.asHasProperty = function(a, e, d) { - if (c.prototype === this || !b.isNumeric(e)) { - return Object.prototype.asHasProperty.call(this, a, e, d); + n.prototype.forEach = function(a, f) { + c.isFunction(a) || l("ArgumentError", h.Errors.CheckTypeFailedError); + for (var d = 0;d < this._buffer.length;d++) { + a.call(f, this.asGetNumericProperty(d), d, this); } - a = b.toNumber(e); - return 0 <= a && a < this._length; }; - Object.defineProperty(c.prototype, "length", {get:function() { - return this._length; + n.prototype.join = function(a) { + void 0 === a && (a = ","); + for (var f = this._buffer, d = this._buffer.length, b = "", g = 0;g < d - 1;g++) { + b += f[g] + a; + } + 0 < d && (b += f[d - 1]); + return b; + }; + n.prototype.indexOf = function(a, f) { + void 0 === f && (f = 0); + return this._buffer.indexOf(a, f); + }; + n.prototype.lastIndexOf = function(a, f) { + void 0 === f && (f = 2147483647); + return this._buffer.lastIndexOf(a, f); + }; + n.prototype.map = function(a, f) { + c.isFunction(a) || l("ArgumentError", h.Errors.CheckTypeFailedError); + for (var d = new n(0, !1, this._type), b = 0;b < this._buffer.length;b++) { + d.push(a.call(f, this.asGetNumericProperty(b), b, this)); + } + return d; + }; + n.prototype.push = function(a, f, d, b, g, e, c, m) { + this._checkFixed(); + for (var q = 0;q < arguments.length;q++) { + this._buffer.push(this._coerce(arguments[q])); + } + }; + n.prototype.pop = function() { + this._checkFixed(); + return 0 === this._buffer.length ? void 0 : this._buffer.pop(); + }; + n.prototype.concat = function() { + for (var a = [], f = 0;f < arguments.length;f++) { + a.push(this._coerce(arguments[f])._buffer); + } + return this._buffer.concat.apply(this._buffer, a); + }; + n.prototype.reverse = function() { + this._buffer.reverse(); + return this; + }; + n.prototype._coerce = function(a) { + return this._type ? this._type.coerce(a) : void 0 === a ? null : a; + }; + n.prototype.shift = function() { + this._checkFixed(); + return 0 === this._buffer.length ? void 0 : this._buffer.shift(); + }; + n.prototype.unshift = function() { + if (arguments.length) { + this._checkFixed(); + for (var a = 0;a < arguments.length;a++) { + this._buffer.unshift(this._coerce(arguments[a])); + } + } + }; + n.prototype.slice = function(a, f) { + void 0 === a && (a = 0); + void 0 === f && (f = 2147483647); + var d = this._buffer, b = d.length, g = Math.min(Math.max(a, 0), b), b = Math.min(Math.max(f, g), b), e = new n(b - g, this.fixed, this._type); + e._buffer = d.slice(g, b); + return e; + }; + n.prototype.splice = function(a, f) { + var d = this._buffer, b = d.length, g = Math.min(Math.max(a, 0), b), b = Math.min(Math.max(f, 0), b - g), e = arguments.length - 2; + b !== e && this._checkFixed(); + for (var g = [g, b], c = 2;c < e + 2;c++) { + g[c] = this._coerce(arguments[c]); + } + b = new n(b, this.fixed, this._type); + b._buffer = d.splice.apply(d, g); + return b; + }; + Object.defineProperty(n.prototype, "length", {get:function() { + return this._buffer.length; }, set:function(a) { a >>>= 0; - if (a > this._length) { - this._ensureCapacity(a); - for (var b = this._offset + this._length, d = this._offset + a;b < d;b++) { - this._buffer[b] = c.DEFAULT_VALUE; + if (a > this._buffer.length) { + for (var f = this._buffer.length;f < a;f++) { + this._buffer[f] = this._defaultValue; } + } else { + this._buffer.length = a; } - this._length = a; + s(this._buffer.length === a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "fixed", {get:function() { + Object.defineProperty(n.prototype, "fixed", {get:function() { return this._fixed; }, set:function(a) { this._fixed = !!a; }, enumerable:!0, configurable:!0}); - c.prototype._spliceHelper = function(a, b, c, d, n) { - b = m(b, 0, d.length - n); - c = m(c, 0, this._length - a); - this._ensureCapacity(this._length - c + b); - var f = this._offset + a + c; - this._buffer.set(this._buffer.subarray(f, f + this._length - a - c), this._offset + a + b); - this._length += b - c; - for (c = 0;c < b;c++) { - this._buffer[this._offset + a + c] = d.asGetNumericProperty(n + c); - } + n.prototype._checkFixed = function() { + this._fixed && l("RangeError", h.Errors.VectorFixedError); }; - c.prototype.asNextName = function(a) { + n.prototype.asNextName = function(a) { return a - 1; }; - c.prototype.asNextValue = function(a) { - return this._buffer[this._offset + a - 1]; + n.prototype.asNextValue = function(a) { + return this._buffer[a - 1]; }; - c.prototype.asNextNameIndex = function(a) { + n.prototype.asNextNameIndex = function(a) { a += 1; - return a <= this._length ? a : 0; + return a <= this._buffer.length ? a : 0; + }; + n.prototype.asHasProperty = function(a, f, d) { + if (n.prototype === this || !c.isNumeric(f)) { + return Object.prototype.asHasProperty.call(this, a, f, d); + } + a = c.toNumber(f); + return 0 <= a && a < this._buffer.length; + }; + n.prototype.asGetNumericProperty = function(a) { + e(a, this._buffer.length); + return this._buffer[a]; }; - c.prototype.asHasNext2 = function(a) { + n.prototype.asSetNumericProperty = function(a, f) { + m(a, this._buffer.length, this._fixed); + this._buffer[a] = this._coerce(f); + }; + n.prototype.asHasNext2 = function(a) { a.index = this.asNextNameIndex(a.index); }; - c.EXTRA_CAPACITY = 4; - c.INITIAL_CAPACITY = 10; - c.DEFAULT_VALUE = 0; - c.CASEINSENSITIVE = 1; - c.DESCENDING = 2; - c.UNIQUESORT = 4; - c.RETURNINDEXEDARRAY = 8; - c.NUMERIC = 16; - return c; - }(); - g.Uint32Vector = c; - c.prototype._reverse = c.prototype.reverse; - c.prototype._filter = c.prototype.filter; - c.prototype._map = c.prototype.map; - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - var f = b.Debug.assertNotImplemented, t = b.Debug.notImplemented, s = b.AVM2.Runtime.throwError, m = b.NumberUtilities.clamp, d = b.AVM2.Runtime.asCheckVectorGetNumericProperty, a = b.AVM2.Runtime.asCheckVectorSetNumericProperty, c = function() { - function c(a, b) { - "undefined" === typeof a && (a = 0); - "undefined" === typeof b && (b = !1); + n.CASEINSENSITIVE = 1; + n.DESCENDING = 2; + n.UNIQUESORT = 4; + n.RETURNINDEXEDARRAY = 8; + n.NUMERIC = 16; + n.instanceConstructor = n; + n.staticNatives = [n]; + n.instanceNatives = [n.prototype]; + n.classInitializer = function() { + var a = n.prototype; + u(a, "$Bgjoin", a.join); + u(a, "$BgtoString", a.join); + u(a, "$BgtoLocaleString", a.toLocaleString); + u(a, "$Bgpop", a.pop); + u(a, "$Bgpush", a.push); + u(a, "$Bgreverse", a.reverse); + u(a, "$Bgconcat", a.concat); + u(a, "$Bgsplice", a.splice); + u(a, "$Bgslice", a.slice); + u(a, "$Bgshift", a.shift); + u(a, "$Bgunshift", a.unshift); + u(a, "$BgindexOf", a.indexOf); + u(a, "$BglastIndexOf", a.lastIndexOf); + u(a, "$BgforEach", a.forEach); + u(a, "$Bgmap", a.map); + u(a, "$Bgfilter", a.filter); + u(a, "$Bgsome", a.some); + u(a, "$Bgevery", a.every); + u(a, "$Bgsort", a.sort); + }; + return n; + }(a.ASVector); + a.GenericVector = t; + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.Debug.assertNotImplemented, v = c.ObjectUtilities.defineNonEnumerableProperty, p = c.AVM2.Runtime.throwError, u = c.AVM2.Runtime.asCheckVectorGetNumericProperty, l = c.AVM2.Runtime.asCheckVectorSetNumericProperty, e = function(a) { + function e(a, c) { + void 0 === a && (a = 0); + void 0 === c && (c = !1); a >>>= 0; - this._fixed = !!b; - this._buffer = new Float64Array(Math.max(c.INITIAL_CAPACITY, a + c.EXTRA_CAPACITY)); + this._fixed = !!c; + this._buffer = new Int32Array(Math.max(e.INITIAL_CAPACITY, a + e.EXTRA_CAPACITY)); this._offset = 0; this._length = a; } - c.defaultCompareFunction = function(a, b) { - return String(a).localeCompare(String(b)); - }; - c.compare = function(a, e, d, l) { - f(!(d & c.CASEINSENSITIVE), "CASEINSENSITIVE"); - f(!(d & c.UNIQUESORT), "UNIQUESORT"); - f(!(d & c.RETURNINDEXEDARRAY), "RETURNINDEXEDARRAY"); - var g = 0; - l || (l = c.defaultCompareFunction); - d & c.NUMERIC ? (a = b.toNumber(a), e = b.toNumber(e), g = a < e ? -1 : a > e ? 1 : 0) : g = l(a, e); - d & c.DESCENDING && (g *= -1); - return g; + __extends(e, a); + e.prototype.newThisType = function() { + return new e; }; - c.callable = function(a) { - if (a instanceof c) { + e.callable = function(a) { + if (a instanceof e) { return a; } - var e = a.asGetProperty(void 0, "length"); - if (void 0 !== e) { - for (var d = new c(e, !1), l = 0;l < e;l++) { - d.asSetNumericProperty(l, a.asGetPublicProperty(l)); + var m = a.asGetProperty(void 0, "length"); + if (void 0 !== m) { + for (var k = new e(m, !1), f = 0;f < m;f++) { + k.asSetNumericProperty(f, a.asGetPublicProperty(f)); } - return d; + return k; } - b.Debug.unexpected(); + c.Debug.unexpected(); }; - c.prototype.internalToString = function() { - for (var a = "", b = this._offset, c = b + this._length, d = 0;d < this._buffer.length;d++) { - d === b && (a += "["), d === c && (a += "]"), a += this._buffer[d], d < this._buffer.length - 1 && (a += ","); + e.prototype.internalToString = function() { + for (var a = "", e = this._offset, k = e + this._length, f = 0;f < this._buffer.length;f++) { + f === e && (a += "["), f === k && (a += "]"), a += this._buffer[f], f < this._buffer.length - 1 && (a += ","); } this._offset + this._length === this._buffer.length && (a += "]"); return a + ": offset: " + this._offset + ", length: " + this._length + ", capacity: " + this._buffer.length; }; - c.prototype.toString = function() { - for (var a = "", b = 0;b < this._length;b++) { - a += this._buffer[this._offset + b], b < this._length - 1 && (a += ","); + e.prototype.toString = function() { + for (var a = "", e = 0;e < this._length;e++) { + a += this._buffer[this._offset + e], e < this._length - 1 && (a += ","); + } + return a; + }; + e.prototype.toLocaleString = function() { + for (var a = "", e = 0;e < this._length;e++) { + a += this._buffer[this._offset + e], e < this._length - 1 && (a += ","); } return a; }; - c.prototype._view = function() { + e.prototype._view = function() { return this._buffer.subarray(this._offset, this._offset + this._length); }; - c.prototype._ensureCapacity = function(a) { - var b = this._offset + a; - b < this._buffer.length || (a <= this._buffer.length ? (b = this._buffer.length - a >> 2, this._buffer.set(this._view(), b), this._offset = b) : (a = (3 * this._buffer.length >> 1) + 1, a < b && (a = b), b = new Float64Array(a), b.set(this._buffer, 0), this._buffer = b)); - }; - c.prototype.concat = function() { - t("Float64Vector.concat"); - }; - c.prototype.every = function(a, b) { - for (var c = 0;c < this._length;c++) { - if (!a.call(b, this._buffer[this._offset + c], c, this)) { + e.prototype._ensureCapacity = function(a) { + var e = this._offset + a; + e < this._buffer.length || (a <= this._buffer.length ? (e = this._buffer.length - a >> 2, this._buffer.set(this._view(), e), this._offset = e) : (a = (3 * this._buffer.length >> 1) + 1, a < e && (a = e), e = new Int32Array(a), e.set(this._buffer, 0), this._buffer = e)); + }; + e.prototype.concat = function() { + for (var a = this._length, c = 0;c < arguments.length;c++) { + var k = arguments[c]; + k._buffer instanceof Int32Array || p("TypeError", h.Errors.CheckTypeFailedError, k.constructor.name, "__AS3__.vec.Vector."); + a += k._length; + } + var a = new e(a), f = a._buffer; + f.set(this._buffer); + for (var d = this._length, c = 0;c < arguments.length;c++) { + k = arguments[c], d + k._buffer.length < k._buffer.length ? f.set(k._buffer, d) : f.set(k._buffer.subarray(0, k._length), d), d += k._length; + } + return a; + }; + e.prototype.every = function(a, e) { + for (var k = 0;k < this._length;k++) { + if (!a.call(e, this._buffer[this._offset + k], k, this)) { return!1; } } return!0; }; - c.prototype.filter = function(a, b) { - for (var d = new c, l = 0;l < this._length;l++) { - a.call(b, this._buffer[this._offset + l], l, this) && d.push(this._buffer[this._offset + l]); - } - return d; - }; - c.prototype.some = function(a, c) { - 2 !== arguments.length ? s("ArgumentError", k.Errors.WrongArgumentCountError) : b.isFunction(a) || s("ArgumentError", k.Errors.CheckTypeFailedError); - for (var d = 0;d < this._length;d++) { - if (a.call(c, this._buffer[this._offset + d], d, this)) { + e.prototype.filter = function(a, c) { + for (var k = new e, f = 0;f < this._length;f++) { + a.call(c, this._buffer[this._offset + f], f, this) && k.push(this._buffer[this._offset + f]); + } + return k; + }; + e.prototype.some = function(a, e) { + 2 !== arguments.length ? p("ArgumentError", h.Errors.WrongArgumentCountError) : c.isFunction(a) || p("ArgumentError", h.Errors.CheckTypeFailedError); + for (var k = 0;k < this._length;k++) { + if (a.call(e, this._buffer[this._offset + k], k, this)) { return!0; } } return!1; }; - c.prototype.forEach = function(a, b) { - for (var c = 0;c < this._length;c++) { - a.call(b, this._buffer[this._offset + c], c, this); + e.prototype.forEach = function(a, e) { + for (var k = 0;k < this._length;k++) { + a.call(e, this._buffer[this._offset + k], k, this); } }; - c.prototype.join = function(a) { - t("Float64Vector.join"); - }; - c.prototype.indexOf = function(a, b) { - t("Float64Vector.indexOf"); + e.prototype.join = function(a) { + void 0 === a && (a = ","); + for (var e = this.length, k = this._buffer, f = this._offset, d = "", b = 0;b < e - 1;b++) { + d += k[f + b] + a; + } + 0 < e && (d += k[f + e - 1]); + return d; }; - c.prototype.lastIndexOf = function(a, b) { - t("Float64Vector.lastIndexOf"); + e.prototype.indexOf = function(a, e) { + void 0 === e && (e = 0); + var k = this._length, f = e | 0; + if (0 > f) { + f += k, 0 > f && (f = 0); + } else { + if (f >= k) { + return-1; + } + } + for (var d = this._buffer, k = this._length, b = this._offset, k = b + k, f = f + b;f < k;f++) { + if (d[f] === a) { + return f - b; + } + } + return-1; }; - c.prototype.map = function(a, e) { - b.isFunction(a) || s("ArgumentError", k.Errors.CheckTypeFailedError); - for (var d = new c, l = 0;l < this._length;l++) { - d.push(a.call(e, this._buffer[this._offset + l], l, this)); + e.prototype.lastIndexOf = function(a, e) { + void 0 === e && (e = 2147483647); + var k = this._length, f = e | 0; + if (0 > f) { + if (f += k, 0 > f) { + return-1; + } + } else { + f >= k && (f = k); } - return d; + for (var k = this._buffer, d = this._offset, f = f + d;f-- > d;) { + if (k[f] === a) { + return f - d; + } + } + return-1; }; - c.prototype.push = function() { - for (var a = 0;a < arguments.length - 0;a++) { + e.prototype.map = function(a, m) { + c.isFunction(a) || p("ArgumentError", h.Errors.CheckTypeFailedError); + for (var k = new e, f = 0;f < this._length;f++) { + k.push(a.call(m, this._buffer[this._offset + f], f, this)); } + return k; + }; + e.prototype.push = function(a, e, k, f, d, b, g, r) { this._checkFixed(); this._ensureCapacity(this._length + arguments.length); - for (a = 0;a < arguments.length;a++) { - this._buffer[this._offset + this._length++] = arguments[a]; + for (var c = 0;c < arguments.length;c++) { + this._buffer[this._offset + this._length++] = arguments[c]; } }; - c.prototype.pop = function() { + e.prototype.pop = function() { this._checkFixed(); if (0 === this._length) { - return c.DEFAULT_VALUE; + return e.DEFAULT_VALUE; } this._length--; return this._buffer[this._offset + this._length]; }; - c.prototype.reverse = function() { - for (var a = this._offset, b = this._offset + this._length - 1, c = this._buffer;a < b;) { - var d = c[a]; - c[a] = c[b]; - c[b] = d; + e.prototype.reverse = function() { + for (var a = this._offset, e = this._offset + this._length - 1, k = this._buffer;a < e;) { + var f = k[a]; + k[a] = k[e]; + k[e] = f; a++; - b--; - } - }; - c._sort = function(a) { - for (var b = [], c = -1, d = 0, n = a.length - 1, f, r, h, x;;) { - if (100 >= n - d) { - for (r = d + 1;r <= n;r++) { - h = a[r]; - for (f = r - 1;f >= d && a[f] > h;) { - a[f + 1] = a[f--]; - } - a[f + 1] = h; - } - if (-1 == c) { - break; - } - n = b[c--]; - d = b[c--]; - } else { - x = d + n >> 1; - f = d + 1; - r = n; - h = a[x]; - a[x] = a[f]; - a[f] = h; - a[d] > a[n] && (h = a[d], a[d] = a[n], a[n] = h); - a[f] > a[n] && (h = a[f], a[f] = a[n], a[n] = h); - a[d] > a[f] && (h = a[d], a[d] = a[f], a[f] = h); - for (x = a[f];;) { - do { - f++; - } while (a[f] < x); - do { - r--; - } while (a[r] > x); - if (r < f) { - break; - } - h = a[f]; - a[f] = a[r]; - a[r] = h; - } - a[d + 1] = a[r]; - a[r] = x; - n - f + 1 >= r - d ? (b[++c] = f, b[++c] = n, n = r - 1) : (b[++c] = d, b[++c] = r - 1, d = f); - } + e--; } - return a; - }; - c.prototype._sortNumeric = function(a) { - c._sort(this._view()); - a && this.reverse(); + return this; }; - c.prototype.sort = function() { + e.prototype.sort = function(a) { if (0 === arguments.length) { return Array.prototype.sort.call(this._view()); } - var a, e = 0; - arguments[0] instanceof Function ? a = arguments[0] : b.isNumber(arguments[0]) && (e = arguments[0]); - b.isNumber(arguments[1]) && (e = arguments[1]); - if (e & c.NUMERIC) { - return this._sortNumeric(e & c.DESCENDING); + if (a instanceof Function) { + return Array.prototype.sort.call(this._view(), a); } - Array.prototype.sort.call(this._view(), function(b, d) { - return c.compare(b, d, e, a); + var c = a | 0; + s(!(c & e.UNIQUESORT), "UNIQUESORT"); + s(!(c & e.RETURNINDEXEDARRAY), "RETURNINDEXEDARRAY"); + return c & e.DESCENDING ? Array.prototype.sort.call(this._view(), function(a, f) { + return f - a; + }) : Array.prototype.sort.call(this._view(), function(a, f) { + return a - f; }); }; - c.prototype.asGetNumericProperty = function(a) { - d(a, this._length); - return this._buffer[this._offset + a]; - }; - c.prototype.asSetNumericProperty = function(b, c) { - a(b, this._length, this._fixed); - b === this._length && (this._ensureCapacity(this._length + 1), this._length++); - this._buffer[this._offset + b] = c; - }; - c.prototype.shift = function() { + e.prototype.shift = function() { this._checkFixed(); if (0 === this._length) { return 0; @@ -11020,14 +12040,7 @@ this._length--; return this._buffer[this._offset++]; }; - c.prototype._checkFixed = function() { - this._fixed && s("RangeError", k.Errors.VectorFixedError); - }; - c.prototype._slide = function(a) { - this._buffer.set(this._view(), this._offset + a); - this._offset += a; - }; - c.prototype.unshift = function() { + e.prototype.unshift = function() { this._checkFixed(); if (arguments.length) { this._ensureCapacity(this._length + arguments.length); @@ -11039,1634 +12052,1046 @@ } } }; - c.prototype.asHasProperty = function(a, e, d) { - if (c.prototype === this || !b.isNumeric(e)) { - return Object.prototype.asHasProperty.call(this, a, e, d); + e.prototype.slice = function(a, c) { + void 0 === a && (a = 0); + void 0 === c && (c = 2147483647); + var k = this._buffer, f = this._length, d = Math.min(Math.max(a, 0), f), f = Math.min(Math.max(c, d), f), b = new e(f - d, this.fixed); + b._buffer.set(k.subarray(this._offset + d, this._offset + f), b._offset); + return b; + }; + e.prototype.splice = function(a, c) { + var k = this._buffer, f = this._length, d = Math.min(Math.max(a, 0), f), b = this._offset + d, g = Math.min(Math.max(c, 0), f - d), d = arguments.length - 2, r, m = new e(g, this.fixed); + 0 < g && (r = k.subarray(b, b + g), m._buffer.set(r, m._offset)); + this._ensureCapacity(f - g + d); + k.set(k.subarray(b + g, f), b + d); + this._length += d - g; + for (f = 0;f < d;f++) { + k[b + f] = arguments[f + 2]; } - a = b.toNumber(e); - return 0 <= a && a < this._length; + return m; + }; + e.prototype._slide = function(a) { + this._buffer.set(this._view(), this._offset + a); + this._offset += a; }; - Object.defineProperty(c.prototype, "length", {get:function() { + Object.defineProperty(e.prototype, "length", {get:function() { return this._length; }, set:function(a) { a >>>= 0; if (a > this._length) { this._ensureCapacity(a); - for (var b = this._offset + this._length, d = this._offset + a;b < d;b++) { - this._buffer[b] = c.DEFAULT_VALUE; + for (var c = this._offset + this._length, k = this._offset + a;c < k;c++) { + this._buffer[c] = e.DEFAULT_VALUE; } } this._length = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "fixed", {get:function() { + Object.defineProperty(e.prototype, "fixed", {get:function() { return this._fixed; }, set:function(a) { this._fixed = !!a; }, enumerable:!0, configurable:!0}); - c.prototype._spliceHelper = function(a, b, c, d, n) { - b = m(b, 0, d.length - n); - c = m(c, 0, this._length - a); - this._ensureCapacity(this._length - c + b); - var f = this._offset + a + c; - this._buffer.set(this._buffer.subarray(f, f + this._length - a - c), this._offset + a + b); - this._length += b - c; - for (c = 0;c < b;c++) { - this._buffer[this._offset + a + c] = d.asGetNumericProperty(n + c); + e.prototype._checkFixed = function() { + this._fixed && p("RangeError", h.Errors.VectorFixedError); + }; + e.prototype.asGetNumericProperty = function(a) { + u(a, this._length); + return this._buffer[this._offset + a]; + }; + e.prototype.asSetNumericProperty = function(a, e) { + l(a, this._length, this._fixed); + a === this._length && (this._ensureCapacity(this._length + 1), this._length++); + this._buffer[this._offset + a] = e; + }; + e.prototype.asHasProperty = function(a, m, k) { + if (e.prototype === this || !c.isNumeric(m)) { + return Object.prototype.asHasProperty.call(this, a, m, k); } + a = c.toNumber(m); + return 0 <= a && a < this._length; }; - c.prototype.asNextName = function(a) { + e.prototype.asNextName = function(a) { return a - 1; }; - c.prototype.asNextValue = function(a) { + e.prototype.asNextValue = function(a) { return this._buffer[this._offset + a - 1]; }; - c.prototype.asNextNameIndex = function(a) { + e.prototype.asNextNameIndex = function(a) { a += 1; return a <= this._length ? a : 0; }; - c.prototype.asHasNext2 = function(a) { + e.prototype.asHasNext2 = function(a) { a.index = this.asNextNameIndex(a.index); }; - c.EXTRA_CAPACITY = 4; - c.INITIAL_CAPACITY = 10; - c.DEFAULT_VALUE = 0; - c.CASEINSENSITIVE = 1; - c.DESCENDING = 2; - c.UNIQUESORT = 4; - c.RETURNINDEXEDARRAY = 8; - c.NUMERIC = 16; - return c; - }(); - g.Float64Vector = c; - c.prototype._reverse = c.prototype.reverse; - c.prototype._filter = c.prototype.filter; - c.prototype._map = c.prototype.map; - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - function f(a) { - for (var b = 0;b < arguments.length - 1;b++) { - } - } - function t(a, b) { - if (0 === b.length) { - return a.sort(); - } - var c, h = 0; - b[0] instanceof Function ? c = b[0] : q(b[0]) && (h = b[0]); - q(b[1]) && (h = b[1]); - a.sort(function(a, b) { - return B(a, b, h, c); - }); - return a; - } - function s(a, b) { - y(!la[a], "Native function: " + a + " is already registered."); - la[a] = b; - } - function m(a) { - switch(a) { - case "prototype": - return "native_prototype"; - case "hasOwnProperty": - return "native_hasOwnProperty"; - case "isPrototypeOf": - return "native_isPrototypeOf"; - case "propertyIsEnumerable": - return "native_propertyIsEnumerable"; - default: - return a; - } - } - function d(a) { - f("getNative(" + a + ")"); - for (var b = a.split("."), c = wa, h = 0, d = b.length;h < d;h++) { - c = c && c[b[h]]; - } - c || (c = la[a]); - y(c, "getNative(" + a + ") not found."); - y(0 > ia.indexOf(c), "Leaking illegal function."); - return c; - } - var a = b.AVM2.ABC.Multiname, c = b.AVM2.Runtime.Scope, n = b.ObjectUtilities.hasOwnProperty, p = b.ObjectUtilities.hasOwnGetter, e = b.ObjectUtilities.defineNonEnumerableProperty, q = b.isNumber, l = b.isNullOrUndefined, u = b.ObjectUtilities.createObject, w = b.ObjectUtilities.isPrototypeWriteable, r = b.ObjectUtilities.getOwnPropertyDescriptor, h = b.Debug.notImplemented, x = b.AVM2.Runtime.asCoerceString, y = b.Debug.assert, G = b.AVM2.Runtime.createFunction, I = b.AVM2.Runtime, C = b.ObjectUtilities.boxValue, - E = b.ObjectUtilities.createEmptyObject, O = b.AVM2.Runtime.ClassBindings, K = b.AVM2.Runtime.InstanceBindings, F = b.AVM2.AS.Int32Vector, J = b.AVM2.AS.Uint32Vector, A = b.AVM2.AS.Float64Vector, B = b.AVM2.Runtime.asCompare; - (function(a) { - a[a.NONE = 0] = "NONE"; - a[a.OWN_INITIALIZE = 1] = "OWN_INITIALIZE"; - a[a.SUPER_INITIALIZE = 2] = "SUPER_INITIALIZE"; - })(g.InitializationFlags || (g.InitializationFlags = {})); - var z = function() { - function c() { - } - c.morphIntoASClass = function(a) { - this.classInfo = a; - this.__proto__ = D.prototype; - }; - c.create = function(a, b, c) { - D.create(a, b, this.instanceConstructor); + e.EXTRA_CAPACITY = 4; + e.INITIAL_CAPACITY = 10; + e.DEFAULT_VALUE = 0; + e.DESCENDING = 2; + e.UNIQUESORT = 4; + e.RETURNINDEXEDARRAY = 8; + e.instanceConstructor = e; + e.staticNatives = [e]; + e.instanceNatives = [e.prototype]; + e.callableConstructor = e.callable; + e.classInitializer = function() { + var a = e.prototype; + v(a, "$Bgjoin", a.join); + v(a, "$BgtoString", a.join); + v(a, "$BgtoLocaleString", a.toLocaleString); + v(a, "$Bgpop", a.pop); + v(a, "$Bgpush", a.push); + v(a, "$Bgreverse", a.reverse); + v(a, "$Bgconcat", a.concat); + v(a, "$Bgsplice", a.splice); + v(a, "$Bgslice", a.slice); + v(a, "$Bgshift", a.shift); + v(a, "$Bgunshift", a.unshift); + v(a, "$BgindexOf", a.indexOf); + v(a, "$BglastIndexOf", a.lastIndexOf); + v(a, "$BgforEach", a.forEach); + v(a, "$Bgmap", a.map); + v(a, "$Bgfilter", a.filter); + v(a, "$Bgsome", a.some); + v(a, "$Bgevery", a.every); + v(a, "$Bgsort", a.sort); }; - c.initializeFrom = function(a) { - return L.initializeFrom.call(this, a); + return e; + }(a.ASVector); + a.Int32Vector = e; + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.Debug.assertNotImplemented, v = c.ObjectUtilities.defineNonEnumerableProperty, p = c.AVM2.Runtime.throwError, u = c.AVM2.Runtime.asCheckVectorGetNumericProperty, l = c.AVM2.Runtime.asCheckVectorSetNumericProperty, e = function(a) { + function e(a, c) { + void 0 === a && (a = 0); + void 0 === c && (c = !1); + a >>>= 0; + this._fixed = !!c; + this._buffer = new Uint32Array(Math.max(e.INITIAL_CAPACITY, a + e.EXTRA_CAPACITY)); + this._offset = 0; + this._length = a; + } + __extends(e, a); + e.prototype.newThisType = function() { + return new e; }; - c.asCall = function(a) { - for (var b = [], c = 0;c < arguments.length - 1;c++) { - b[c] = arguments[c + 1]; + e.callable = function(a) { + if (a instanceof e) { + return a; + } + var m = a.asGetProperty(void 0, "length"); + if (void 0 !== m) { + for (var k = new e(m, !1), f = 0;f < m;f++) { + k.asSetNumericProperty(f, a.asGetPublicProperty(f)); + } + return k; } - return this.callableConstructor.apply(a, b); + c.Debug.unexpected(); }; - c.asApply = function(a, b) { - return this.callableConstructor.apply(a, b); + e.prototype.internalToString = function() { + for (var a = "", e = this._offset, k = e + this._length, f = 0;f < this._buffer.length;f++) { + f === e && (a += "["), f === k && (a += "]"), a += this._buffer[f], f < this._buffer.length - 1 && (a += ","); + } + this._offset + this._length === this._buffer.length && (a += "]"); + return a + ": offset: " + this._offset + ", length: " + this._length + ", capacity: " + this._buffer.length; }; - c.verify = function() { - L.verify.call(this); + e.prototype.toString = function() { + for (var a = "", e = 0;e < this._length;e++) { + a += this._buffer[this._offset + e], e < this._length - 1 && (a += ","); + } + return a; }; - c.trace = function(a) { - L.trace.call(this, a); + e.prototype.toLocaleString = function() { + for (var a = "", e = 0;e < this._length;e++) { + a += this._buffer[this._offset + e], e < this._length - 1 && (a += ","); + } + return a; }; - c.getQualifiedClassName = function() { - return L.getQualifiedClassName.call(this); + e.prototype._view = function() { + return this._buffer.subarray(this._offset, this._offset + this._length); }; - c._setPropertyIsEnumerable = function(b, c, h) { - c = a.getPublicQualifiedName(c); - h = r(b, c); - h.enumerable = !1; - Object.defineProperty(b, c, h); + e.prototype._ensureCapacity = function(a) { + var e = this._offset + a; + e < this._buffer.length || (a <= this._buffer.length ? (e = this._buffer.length - a >> 2, this._buffer.set(this._view(), e), this._offset = e) : (a = (3 * this._buffer.length >> 1) + 1, a < e && (a = e), e = new Uint32Array(a), e.set(this._buffer, 0), this._buffer = e)); + }; + e.prototype.concat = function() { + for (var a = this._length, c = 0;c < arguments.length;c++) { + var k = arguments[c]; + k._buffer instanceof Uint32Array || p("TypeError", h.Errors.CheckTypeFailedError, k.constructor.name, "__AS3__.vec.Vector."); + a += k._length; + } + var a = new e(a), f = a._buffer; + f.set(this._buffer); + for (var d = this._length, c = 0;c < arguments.length;c++) { + k = arguments[c], d + k._buffer.length < k._buffer.length ? f.set(k._buffer, d) : f.set(k._buffer.subarray(0, k._length), d), d += k._length; + } + return a; }; - c._dontEnumPrototype = function(b) { - for (var c in b) { - if (a.isPublicQualifiedName(c)) { - var h = r(b, c); - h.enumerable = !1; - Object.defineProperty(b, c, h); + e.prototype.every = function(a, e) { + for (var k = 0;k < this._length;k++) { + if (!a.call(e, this._buffer[this._offset + k], k, this)) { + return!1; } } + return!0; }; - c.prototype.native_isPrototypeOf = function(a) { - h("isPrototypeOf"); + e.prototype.filter = function(a, c) { + for (var k = new e, f = 0;f < this._length;f++) { + a.call(c, this._buffer[this._offset + f], f, this) && k.push(this._buffer[this._offset + f]); + } + return k; + }; + e.prototype.some = function(a, e) { + 2 !== arguments.length ? p("ArgumentError", h.Errors.WrongArgumentCountError) : c.isFunction(a) || p("ArgumentError", h.Errors.CheckTypeFailedError); + for (var k = 0;k < this._length;k++) { + if (a.call(e, this._buffer[this._offset + k], k, this)) { + return!0; + } + } return!1; }; - c.prototype.native_hasOwnProperty = function(a) { - return this.asHasOwnProperty(null, a, 0); - }; - c.prototype.native_propertyIsEnumerable = function(a) { - return this.asPropertyIsEnumerable(null, a, 0); - }; - c.prototype.setPropertyIsEnumerable = function(a, b) { - c._setPropertyIsEnumerable(this, a, b); + e.prototype.forEach = function(a, e) { + for (var k = 0;k < this._length;k++) { + a.call(e, this._buffer[this._offset + k], k, this); + } }; - c.prototype.toString = function() { - var a = C(this); - return a instanceof D ? b.StringUtilities.concat3("[class ", a.classInfo.instanceInfo.name.name, "]") : b.StringUtilities.concat3("[object ", a.class.classInfo.instanceInfo.name.name, "]"); - }; - c.baseClass = null; - c.instanceConstructor = Object; - c.instanceConstructorNoInitialize = null; - c.initializer = null; - c.initializers = null; - c.classInitializer = null; - c.callableConstructor = c.instanceConstructor; - c.defaultValue = null; - c.initializationFlags = 0; - c.call = Function.prototype.call; - c.apply = Function.prototype.apply; - c.coerce = I.asCoerceObject; - c.defineProperty = Object.defineProperty; - return c; - }(); - g.ASObject = z; - var P = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - b.baseClass = null; - b.classInfo = null; - b.instanceConstructor = null; - b.callableConstructor = null; - b.classBindings = null; - b.instanceBindings = null; - b.staticNatives = null; - b.instanceNatives = null; - b.traitsPrototype = null; - b.dynamicPrototype = null; - b.defaultValue = null; - b.initializationFlags = 0; - return b; - }(z); - g.ASNative = P; - var D = function(c) { - function d(a) { - this.classInfo = a; - this.instanceNatives = this.staticNatives = null; - this.initializationFlags = 0; - this.defaultValue = null; - } - __extends(d, c); - d.configureBuiltinPrototype = function(a, b) { - y(a.instanceConstructor); - a.baseClass = b; - a.dynamicPrototype = a.traitsPrototype = a.instanceConstructor.prototype; - }; - d.configurePrototype = function(a, c) { - a.baseClass = c; - a.dynamicPrototype = u(c.dynamicPrototype); - a.traitsPrototype = u(a.dynamicPrototype); - for (var h = a.traitsPrototype, d = [];a;) { - d.push(a), a = a.baseClass; + e.prototype.join = function(a) { + void 0 === a && (a = ","); + for (var e = this.length, k = this._buffer, f = this._offset, d = "", b = 0;b < e - 1;b++) { + d += k[f + b] + a; } - for (var e = 0;e < d.length;e++) { - var l = [d[e].typeScriptPrototype]; - d[e].instanceNatives && b.ArrayUtilities.pushMany(l, d[e].instanceNatives); - for (var r = 0;r < l.length;r++) { - var f = l[r], p; - for (p in f) { - if (!(0 < e && "toString" === p) && n(f, p) && !n(h, p)) { - var q = Object.getOwnPropertyDescriptor(f, p); - b.Debug.assert(q); - try { - Object.defineProperty(h, p, q); - } catch (x) { - } - } - } + 0 < e && (d += k[f + e - 1]); + return d; + }; + e.prototype.indexOf = function(a, e) { + void 0 === e && (e = 0); + var k = this._length, f = e | 0; + if (0 > f) { + f += k, 0 > f && (f = 0); + } else { + if (f >= k) { + return-1; } } - }; - d.create = function(c, h, l) { - y(!c.instanceConstructorNoInitialize, "This should not be set yet."); - y(!c.dynamicPrototype && !c.traitsPrototype, "These should not be set yet."); - c.typeScriptPrototype = c.prototype; - c.instanceConstructor && !w(c.instanceConstructor) ? d.configureBuiltinPrototype(c, h) : d.configurePrototype(c, h); - c.instanceConstructor || (c.instanceConstructor = l, c !== l && (c.instanceConstructor.__proto__ = c)); - c.callableConstructor || (c.callableConstructor = c.coerce.bind(c)); - c.instanceConstructorNoInitialize = c.instanceConstructor; - c.instanceConstructor.prototype = c.traitsPrototype; - e(c.instanceConstructor.prototype, "class", c); - e(c.dynamicPrototype, a.getPublicQualifiedName("constructor"), c); - c.protocol && b.ObjectUtilities.copyOwnPropertyDescriptors(c.traitsPrototype, c.protocol); - }; - d.prototype.initializeFrom = function(a) { - var b = Object.create(this.traitsPrototype); - d.runInitializers(b, a); - return b; - }; - d.runInitializers = function(a, b) { - b = b || a.class.defaultInitializerArgument; - var c = a.class.initializers; - if (c) { - for (var h = 0;h < c.length;h++) { - c[h].call(a, b); + for (var d = this._buffer, k = this._length, b = this._offset, k = b + k, f = f + b;f < k;f++) { + if (d[f] === a) { + return f - b; } } + return-1; }; - d.configureInitializers = function(a) { - a.baseClass && a.baseClass.initializers && (a.initializers = a.baseClass.initializers.slice(0)); - a.initializer && (a.initializers || (a.initializers = []), a.initializers.push(a.initializer)); - a.initializers && (y(a.instanceConstructorNoInitialize === a.instanceConstructor), a.instanceConstructor = function() { - for (var b = 0;b < arguments.length - 0;b++) { + e.prototype.lastIndexOf = function(a, e) { + void 0 === e && (e = 2147483647); + var k = this._length, f = e | 0; + if (0 > f) { + if (f += k, 0 > f) { + return-1; } - d.runInitializers(this, void 0); - return a.instanceConstructorNoInitialize.apply(this, arguments); - }, a.instanceConstructor.prototype = a.traitsPrototype, e(a.instanceConstructor.prototype, "class", a), a.instanceConstructor.classInfo = a.classInfo, a.instanceConstructor.__proto__ = a); - }; - d.runClassInitializer = function(a) { - a.classInitializer && a.classInitializer(); - }; - d.linkSymbols = function(b) { - function c(b, d, e) { - for (var l = 0;l < d.length;l++) { - var r = d[l], n; - a: { - n = b; - for (var f = r.name.name, q = 0;q < n.length;q++) { - var x = n[q]; - if (0 <= x.indexOf(f) && ("!" === x[x.length - 1] && (x = x.slice(0, x.length - 1)), f === x)) { - n = !0; - break a; - } - } - n = !1; - } - if (n) { - y(!r.name.getNamespace().isPrivate(), "Why are you linking against private members?"); - if (r.isConst()) { - h("Don't link against const traits."); - break; - } - n = r.name.name; - f = a.getQualifiedName(r.name); - r.isSlot() ? Object.defineProperty(e, n, {get:new Function("", "return this." + f), set:new Function("v", "this." + f + " = v")}) : r.isMethod() ? (y(!e[n], "Symbol should not already exist."), y(e.asOpenMethods[f], "There should be an open method for this symbol."), e[n] = e.asOpenMethods[f]) : r.isGetter() ? (y(p(e, f), "There should be an getter method for this symbol."), Object.defineProperty(e, n, {get:new Function("", "return this." + f)})) : h(r); - } + } else { + f >= k && (f = k); + } + for (var k = this._buffer, d = this._offset, f = f + d;f-- > d;) { + if (k[f] === a) { + return f - d; } } - b.classSymbols && c(b.classSymbols, b.classInfo.traits, b); - b.instanceSymbols && c(b.instanceSymbols, b.classInfo.instanceInfo.traits, b.traitsPrototype); - }; - d.prototype.morphIntoASClass = function(a) { - y(this.classInfo === a); - y(this instanceof d); + return-1; }; - Object.defineProperty(d.prototype, "native_prototype", {get:function() { - y(this.dynamicPrototype); - return this.dynamicPrototype; - }, enumerable:!0, configurable:!0}); - d.prototype.asCall = function(a) { - for (var b = [], c = 0;c < arguments.length - 1;c++) { - b[c] = arguments[c + 1]; + e.prototype.map = function(a, m) { + c.isFunction(a) || p("ArgumentError", h.Errors.CheckTypeFailedError); + for (var k = new e, f = 0;f < this._length;f++) { + k.push(a.call(m, this._buffer[this._offset + f], f, this)); } - return this.coerce(b[0]); + return k; }; - d.prototype.asApply = function(a, b) { - return this.coerce(b[0]); - }; - d.prototype.applyType = function(a) { - debugger; - return null; + e.prototype.push = function(a, e, k, f, d, b, g, r) { + this._checkFixed(); + this._ensureCapacity(this._length + arguments.length); + for (var c = 0;c < arguments.length;c++) { + this._buffer[this._offset + this._length++] = arguments[c]; + } }; - d.prototype.isInstanceOf = function(a) { - return this.isInterface() ? !1 : this.isType(a); - }; - d.prototype.isType = function(c) { - if (b.isNullOrUndefined(c)) { - return!1; - } - c = C(c); - if (this.isInterface()) { - if (null === c || "object" !== typeof c) { - return!1; - } - y(c.class.implementedInterfaces, "No 'implementedInterfaces' map found on class " + c.class); - var h = a.getQualifiedName(this.classInfo.instanceInfo.name); - return void 0 !== c.class.implementedInterfaces[h]; - } - return this.dynamicPrototype.isPrototypeOf(c); - }; - d.prototype.isSubtypeOf = function(a) { - for (var b = this;b;) { - if (b.traitsPrototype === a.traitsPrototype) { - return!0; - } - b = b.baseClass; + e.prototype.pop = function() { + this._checkFixed(); + if (0 === this._length) { + return e.DEFAULT_VALUE; } - return!1; - }; - d.prototype.coerce = function(a) { - f(b.StringUtilities.concat4("Coercing ", a, " to ", this)); - return a; + this._length--; + return this._buffer[this._offset + this._length]; }; - d.prototype.isInterface = function() { - return this.classInfo.instanceInfo.isInterface(); + e.prototype.reverse = function() { + for (var a = this._offset, e = this._offset + this._length - 1, k = this._buffer;a < e;) { + var f = k[a]; + k[a] = k[e]; + k[e] = f; + a++; + e--; + } + return this; }; - d.prototype.getQualifiedClassName = function() { - var a = this.classInfo.instanceInfo.name, b = a.namespaces[0].uri; - return b ? b + "::" + a.name : a.name; + e.prototype.sort = function(a) { + if (0 === arguments.length) { + return Array.prototype.sort.call(this._view()); + } + if (a instanceof Function) { + return Array.prototype.sort.call(this._view(), a); + } + var c = a | 0; + s(!(c & e.UNIQUESORT), "UNIQUESORT"); + s(!(c & e.RETURNINDEXEDARRAY), "RETURNINDEXEDARRAY"); + return c & e.DESCENDING ? Array.prototype.sort.call(this._view(), function(a, f) { + return f - a; + }) : Array.prototype.sort.call(this._view(), function(a, f) { + return a - f; + }); }; - d.prototype.verify = function() { - function a(b, c, h) { - for (var d = 0;d < b.length;d++) { - if (c(b[d], h)) { - return!0; - } - } - return!1; + e.prototype.shift = function() { + this._checkFixed(); + if (0 === this._length) { + return 0; } - if (!this.isInterface()) { - var c = [this.classInfo.traits, this.classInfo.instanceInfo.traits], h = [this]; - this.staticNatives && b.ArrayUtilities.pushMany(h, this.staticNatives); - var d = [this.prototype]; - this.instanceNatives && b.ArrayUtilities.pushMany(d, this.instanceNatives); - this === z ? y(!this.baseClass, "ASObject should have no base class.") : (y(this.baseClass, this.classInfo.instanceInfo.name + " has no base class."), y(this.baseClass !== this)); - y(this.traitsPrototype === this.instanceConstructor.prototype, "The traitsPrototype is not set correctly."); - for (var e = 0;e < c.length;e++) { - for (var l = 0 === e, r = 0;r < c[e].length;r++) { - var n = c[e][r], f = m(n.name.name); - if (n.isMethodOrAccessor() && n.methodInfo.isNative()) { - var p = l ? h : d; - n.isMethod() ? a(p, b.ObjectUtilities.hasOwnProperty, f) : n.isGetter() ? a(p, b.ObjectUtilities.hasOwnGetter, f) : n.isSetter() && a(p, b.ObjectUtilities.hasOwnSetter, f); - } - } + this._length--; + return this._buffer[this._offset++]; + }; + e.prototype.unshift = function() { + this._checkFixed(); + if (arguments.length) { + this._ensureCapacity(this._length + arguments.length); + this._slide(arguments.length); + this._offset -= arguments.length; + this._length += arguments.length; + for (var a = 0;a < arguments.length;a++) { + this._buffer[this._offset + a] = arguments[a]; } - b.Debug.assert(this.instanceConstructor, "Must have a constructor function."); } }; - d.labelObject = function(a) { - if (!a) { - return a; + e.prototype.slice = function(a, c) { + void 0 === a && (a = 0); + void 0 === c && (c = 2147483647); + var k = this._buffer, f = this._length, d = Math.min(Math.max(a, 0), f), f = Math.min(Math.max(c, d), f), b = new e(f - d, this.fixed); + b._buffer.set(k.subarray(this._offset + d, this._offset + f), b._offset); + return b; + }; + e.prototype.splice = function(a, c) { + var k = this._buffer, f = this._length, d = Math.min(Math.max(a, 0), f), b = this._offset + d, g = Math.min(Math.max(c, 0), f - d), d = arguments.length - 2, r, m = new e(g, this.fixed); + 0 < g && (r = k.subarray(b, b + g), m._buffer.set(r, m._offset)); + this._ensureCapacity(f - g + d); + k.set(k.subarray(b + g, f), b + d); + this._length += d - g; + for (f = 0;f < d;f++) { + k[b + f] = arguments[f + 2]; } - n(a, "labelId") || (a.labelId = d.labelCounter++); - return a instanceof Function ? "Function [#" + a.labelId + "]" : "Object [#" + a.labelId + "]"; + return m; }; - d.prototype.trace = function(a) { - a.enter("Class: " + this.classInfo); - a.writeLn("baseClass: " + (this.baseClass ? this.baseClass.classInfo.instanceInfo.name : null)); - a.writeLn("instanceConstructor: " + this.instanceConstructor + " " + d.labelObject(this.instanceConstructor)); - a.writeLn("instanceConstructorNoInitialize: " + this.instanceConstructorNoInitialize + " " + d.labelObject(this.instanceConstructorNoInitialize)); - a.writeLn("traitsPrototype: " + d.labelObject(this.traitsPrototype)); - a.writeLn("traitsPrototype.__proto__: " + d.labelObject(this.traitsPrototype.__proto__)); - a.writeLn("dynamicPrototype: " + d.labelObject(this.dynamicPrototype)); - a.writeLn("dynamicPrototype.__proto__: " + d.labelObject(this.dynamicPrototype.__proto__)); - a.writeLn("instanceConstructor.prototype: " + d.labelObject(this.instanceConstructor.prototype)); - a.leave("}"); + e.prototype._slide = function(a) { + this._buffer.set(this._view(), this._offset + a); + this._offset += a; }; - d.instanceConstructor = d; - d.staticNatives = null; - d.instanceNatives = null; - d.labelCounter = 0; - return d; - }(z); - g.ASClass = D; - var L = D.prototype; - L.call = Function.prototype.call; - L.apply = Function.prototype.apply; - var M = function(a) { - function b() { - } - __extends(b, a); - Object.defineProperty(b.prototype, "native_prototype", {get:function() { - return this.prototype; + Object.defineProperty(e.prototype, "length", {get:function() { + return this._length; }, set:function(a) { - this.prototype = a; + a >>>= 0; + if (a > this._length) { + this._ensureCapacity(a); + for (var c = this._offset + this._length, k = this._offset + a;c < k;c++) { + this._buffer[c] = e.DEFAULT_VALUE; + } + } + this._length = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "native_length", {get:function() { - return this.hasOwnProperty(I.VM_LENGTH) ? this.asLength : this.length; + Object.defineProperty(e.prototype, "fixed", {get:function() { + return this._fixed; + }, set:function(a) { + this._fixed = !!a; }, enumerable:!0, configurable:!0}); - b.baseClass = null; - b.instanceConstructor = Function; - b.staticNatives = [Function]; - b.instanceNatives = [Function.prototype]; - return b; - }(z); - g.ASFunction = M; - var V = function(a) { - function b(a) { - } - __extends(b, a); - b.instanceConstructor = Boolean; - b.callableConstructor = b.instanceConstructor; - b.staticNatives = null; - b.instanceNatives = null; - b.coerce = I.asCoerceBoolean; - return b; - }(z); - g.ASBoolean = V; - V.prototype.toString = Boolean.prototype.toString; - V.prototype.valueOf = Boolean.prototype.valueOf; - var Q = function(a) { - function c(a, h) { - var d = b.FunctionUtilities.bindSafely(h, a); - e(this, "call", d.call.bind(d)); - e(this, "apply", d.apply.bind(d)); - } - __extends(c, a); - c.prototype.toString = function() { - return "function Function() {}"; - }; - c.staticNatives = null; - c.instanceNatives = null; - return c.instanceConstructor = c; - }(M); - g.ASMethodClosure = Q; - var U = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - b._numberToString = function(a, b) { - return Number(a).toString(b | 0); + e.prototype._checkFixed = function() { + this._fixed && p("RangeError", h.Errors.VectorFixedError); }; - b._minValue = function() { - return Number.MIN_VALUE; + e.prototype.asGetNumericProperty = function(a) { + u(a, this._length); + return this._buffer[this._offset + a]; }; - b.instanceConstructor = Number; - b.callableConstructor = b.instanceConstructor; - b.staticNatives = [Math]; - b.instanceNatives = [Number.prototype]; - b.defaultValue = Number(0); - b.coerce = I.asCoerceNumber; - return b; - }(z); - g.ASNumber = U; - var S = function(a) { - function b(a) { - return Object(Number(a | 0)); - } - __extends(b, a); - b.asCall = function(a) { - for (var b = [], c = 0;c < arguments.length - 1;c++) { - b[c] = arguments[c + 1]; + e.prototype.asSetNumericProperty = function(a, e) { + l(a, this._length, this._fixed); + a === this._length && (this._ensureCapacity(this._length + 1), this._length++); + this._buffer[this._offset + a] = e; + }; + e.prototype.asHasProperty = function(a, m, k) { + if (e.prototype === this || !c.isNumeric(m)) { + return Object.prototype.asHasProperty.call(this, a, m, k); } - return b[0] | 0; - }; - b.asApply = function(a, b) { - return b[0] | 0; - }; - b.isInstanceOf = function(a) { - return!1; + a = c.toNumber(m); + return 0 <= a && a < this._length; }; - b.isType = function(a) { - return q(a) || a instanceof Number ? (a = +a, (a | 0) === a) : !1; + e.prototype.asNextName = function(a) { + return a - 1; }; - b.instanceConstructor = b; - b.callableConstructor = b.instanceConstructor; - b.staticNatives = [Math]; - b.instanceNatives = [Number.prototype]; - b.defaultValue = 0; - b.coerce = I.asCoerceInt; - return b; - }(z); - g.ASInt = S; - var aa = function(a) { - function b(a) { - return Object(Number(a >>> 0)); - } - __extends(b, a); - b.asCall = function(a) { - for (var b = [], c = 0;c < arguments.length - 1;c++) { - b[c] = arguments[c + 1]; - } - return b[0] >>> 0; + e.prototype.asNextValue = function(a) { + return this._buffer[this._offset + a - 1]; }; - b.asApply = function(a, b) { - return b[0] >>> 0; + e.prototype.asNextNameIndex = function(a) { + a += 1; + return a <= this._length ? a : 0; }; - b.isInstanceOf = function(a) { - return!1; + e.prototype.asHasNext2 = function(a) { + a.index = this.asNextNameIndex(a.index); }; - b.isType = function(a) { - return q(a) || a instanceof Number ? (a = +a, a >>> 0 === a) : !1; + e.EXTRA_CAPACITY = 4; + e.INITIAL_CAPACITY = 10; + e.DEFAULT_VALUE = 0; + e.DESCENDING = 2; + e.UNIQUESORT = 4; + e.RETURNINDEXEDARRAY = 8; + e.instanceConstructor = e; + e.staticNatives = [e]; + e.instanceNatives = [e.prototype]; + e.callableConstructor = e.callable; + e.classInitializer = function() { + var a = e.prototype; + v(a, "$Bgjoin", a.join); + v(a, "$BgtoString", a.join); + v(a, "$BgtoLocaleString", a.toLocaleString); + v(a, "$Bgpop", a.pop); + v(a, "$Bgpush", a.push); + v(a, "$Bgreverse", a.reverse); + v(a, "$Bgconcat", a.concat); + v(a, "$Bgsplice", a.splice); + v(a, "$Bgslice", a.slice); + v(a, "$Bgshift", a.shift); + v(a, "$Bgunshift", a.unshift); + v(a, "$BgindexOf", a.indexOf); + v(a, "$BglastIndexOf", a.lastIndexOf); + v(a, "$BgforEach", a.forEach); + v(a, "$Bgmap", a.map); + v(a, "$Bgfilter", a.filter); + v(a, "$Bgsome", a.some); + v(a, "$Bgevery", a.every); + v(a, "$Bgsort", a.sort); }; - b.instanceConstructor = b; - b.callableConstructor = b.instanceConstructor; - b.staticNatives = [Math]; - b.instanceNatives = [Number.prototype]; - b.defaultValue = 0; - b.coerce = I.asCoerceUint; - return b; - }(z); - g.ASUint = aa; - var $ = function(a) { - function b() { - a.apply(this, arguments); + return e; + }(a.ASVector); + a.Uint32Vector = e; + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.Debug.assertNotImplemented, v = c.ObjectUtilities.defineNonEnumerableProperty, p = c.AVM2.Runtime.throwError, u = c.AVM2.Runtime.asCheckVectorGetNumericProperty, l = c.AVM2.Runtime.asCheckVectorSetNumericProperty, e = function(a) { + function e(a, c) { + void 0 === a && (a = 0); + void 0 === c && (c = !1); + a >>>= 0; + this._fixed = !!c; + this._buffer = new Float64Array(Math.max(e.INITIAL_CAPACITY, a + e.EXTRA_CAPACITY)); + this._offset = 0; + this._length = a; } - __extends(b, a); - Object.defineProperty(b.prototype, "native_length", {get:function() { - return this.length; - }, enumerable:!0, configurable:!0}); - b.prototype.match = function(a) { - if (void 0 === a || null === a) { - return null; + __extends(e, a); + e.prototype.newThisType = function() { + return new e; + }; + e.callable = function(a) { + if (a instanceof e) { + return a; } - if (a instanceof RegExp && a.global) { - for (var b = [], c;c = a.exec(this);) { - b.push(c[0]); + var m = a.asGetProperty(void 0, "length"); + if (void 0 !== m) { + for (var k = new e(m, !1), f = 0;f < m;f++) { + k.asSetNumericProperty(f, a.asGetPublicProperty(f)); } - return b; + return k; } - a instanceof RegExp || "string" === typeof a || (a = String(a)); - return this.match(a); + c.Debug.unexpected(); }; - b.prototype.search = function(a) { - return void 0 === a ? -1 : this.search(a); + e.prototype.internalToString = function() { + for (var a = "", e = this._offset, k = e + this._length, f = 0;f < this._buffer.length;f++) { + f === e && (a += "["), f === k && (a += "]"), a += this._buffer[f], f < this._buffer.length - 1 && (a += ","); + } + this._offset + this._length === this._buffer.length && (a += "]"); + return a + ": offset: " + this._offset + ", length: " + this._length + ", capacity: " + this._buffer.length; }; - b.prototype.toUpperCase = function() { - var a = String.prototype.toUpperCase.apply(this); - return a = a.replace(/\u039C/g, String.fromCharCode(181)); + e.prototype.toString = function() { + for (var a = "", e = 0;e < this._length;e++) { + a += this._buffer[this._offset + e], e < this._length - 1 && (a += ","); + } + return a; }; - b.prototype.toLocaleUpperCase = function() { - var a = String.prototype.toLocaleUpperCase.apply(this); - return a = a.replace(/\u039C/g, String.fromCharCode(181)); + e.prototype.toLocaleString = function() { + for (var a = "", e = 0;e < this._length;e++) { + a += this._buffer[this._offset + e], e < this._length - 1 && (a += ","); + } + return a; }; - b.instanceConstructor = String; - b.callableConstructor = b.instanceConstructor; - b.staticNatives = [String]; - b.instanceNatives = [String.prototype]; - b.coerce = I.asCoerceString; - return b; - }(z); - g.ASString = $; - g.arraySort = t; - var ea = function(c) { - function h() { - c.apply(this, arguments); - } - __extends(h, c); - h._splice = function(a, b) { - return 0 === b.length ? void 0 : a.splice.apply(a, b); + e.prototype._view = function() { + return this._buffer.subarray(this._offset, this._offset + this._length); }; - h._sort = function(a, b) { - if (0 === b.length) { - return a.sort(); - } - var c, h = 0; - b[0] instanceof Function ? c = b[0] : q(b[0]) && (h = b[0]); - q(b[1]) && (h = b[1]); - a.sort(function(a, b) { - return I.asCompare(a, b, h, c); - }); + e.prototype._ensureCapacity = function(a) { + var e = this._offset + a; + e < this._buffer.length || (a <= this._buffer.length ? (e = this._buffer.length - a >> 2, this._buffer.set(this._view(), e), this._offset = e) : (a = (3 * this._buffer.length >> 1) + 1, a < e && (a = e), e = new Float64Array(a), e.set(this._buffer, 0), this._buffer = e)); + }; + e.prototype.concat = function() { + for (var a = this._length, c = 0;c < arguments.length;c++) { + var k = arguments[c]; + k._buffer instanceof Float64Array || p("TypeError", h.Errors.CheckTypeFailedError, k.constructor.name, "__AS3__.vec.Vector."); + a += k._length; + } + var a = new e(a), f = a._buffer; + f.set(this._buffer); + for (var d = this._length, c = 0;c < arguments.length;c++) { + k = arguments[c], d + k._buffer.length < k._buffer.length ? f.set(k._buffer, d) : f.set(k._buffer.subarray(0, k._length), d), d += k._length; + } return a; }; - h._sortOn = function(c, d, e) { - b.isString(d) && (d = [d]); - q(e) && (e = [e]); - for (var l = d.length - 1;0 <= l;l--) { - var r = a.getPublicQualifiedName(d[l]); - if (h.CACHE_NUMERIC_COMPARATORS && e[l] & 16) { - var n = "var x = +(a." + r + "), y = +(b." + r + ");", n = e[l] & 2 ? n + "return x < y ? 1 : (x > y ? -1 : 0);" : n + "return x < y ? -1 : (x > y ? 1 : 0);", f = h.numericComparatorCache[n]; - f || (f = h.numericComparatorCache[n] = new Function("a", "b", n)); - c.sort(f); - } else { - c.sort(function(a, b) { - return I.asCompare(a[r], b[r], e[l] | 0); - }); + e.prototype.every = function(a, e) { + for (var k = 0;k < this._length;k++) { + if (!a.call(e, this._buffer[this._offset + k], k, this)) { + return!1; } } - return c; + return!0; }; - h._every = function(a, b, c) { - for (var h = 0;h < a.length && !0 === b.call(c, a[h], h, a);h++) { + e.prototype.filter = function(a, c) { + for (var k = new e, f = 0;f < this._length;f++) { + a.call(c, this._buffer[this._offset + f], f, this) && k.push(this._buffer[this._offset + f]); + } + return k; + }; + e.prototype.some = function(a, e) { + 2 !== arguments.length ? p("ArgumentError", h.Errors.WrongArgumentCountError) : c.isFunction(a) || p("ArgumentError", h.Errors.CheckTypeFailedError); + for (var k = 0;k < this._length;k++) { + if (a.call(e, this._buffer[this._offset + k], k, this)) { + return!0; + } } return!1; }; - h._filter = function(a, b, c) { - for (var h = [], d = 0;d < a.length;d++) { - !0 === b.call(c, a[d], d, a) && h.push(a[d]); + e.prototype.forEach = function(a, e) { + for (var k = 0;k < this._length;k++) { + a.call(e, this._buffer[this._offset + k], k, this); } - return h; }; - Object.defineProperty(h.prototype, "native_length", {get:function() { - return this.length; - }, set:function(a) { - this.length = a >>> 0; - }, enumerable:!0, configurable:!0}); - h.instanceConstructor = Array; - h.staticNatives = [Array]; - h.instanceNatives = [Array.prototype]; - h.CACHE_NUMERIC_COMPARATORS = !0; - h.numericComparatorCache = E(); - return h; - }(z); - g.ASArray = ea; - var Z = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - b.prototype.newThisType = function() { - return new this.class.instanceConstructor; + e.prototype.join = function(a) { + void 0 === a && (a = ","); + for (var e = this.length, k = this._buffer, f = this._offset, d = "", b = 0;b < e - 1;b++) { + d += k[f + b] + a; + } + 0 < e && (d += k[f + e - 1]); + return d; }; - b.staticNatives = null; - b.instanceNatives = null; - b.instanceConstructor = b; - b.callableConstructor = null; - return b; - }(P); - g.ASVector = Z; - var v = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - b._every = function(a, b, c) { - return a.every(b, c); - }; - b._forEach = function(a, b, c) { - return a.forEach(b, c); - }; - b._some = function(a, b, c) { - return a.some(b, c); - }; - b.instanceConstructor = F; - b.staticNatives = [F]; - b.instanceNatives = [F.prototype, Z.prototype]; - b.callableConstructor = F.callable; - b._sort = t; - return b; - }(Z); - g.ASIntVector = v; - var X = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - b._every = function(a, b, c) { - return a.every(b, c); - }; - b._forEach = function(a, b, c) { - return a.forEach(b, c); - }; - b._some = function(a, b, c) { - return a.some(b, c); - }; - b.instanceConstructor = J; - b.staticNatives = [J]; - b.instanceNatives = [J.prototype, Z.prototype]; - b.callableConstructor = J.callable; - b._sort = t; - return b; - }(Z); - g.ASUIntVector = X; - var ba = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - b._every = function(a, b, c) { - return a.every(b, c); - }; - b._forEach = function(a, b, c) { - return a.forEach(b, c); - }; - b._some = function(a, b, c) { - return a.some(b, c); - }; - b.instanceConstructor = A; - b.staticNatives = [A]; - b.instanceNatives = [A.prototype, Z.prototype]; - b.callableConstructor = A.callable; - b._sort = t; - return b; - }(Z); - g.ASDoubleVector = ba; - var R = function(c) { - function h() { - c.apply(this, arguments); - } - __extends(h, c); - h.transformJSValueToAS = function(a) { - if ("object" !== typeof a) { - return a; + e.prototype.indexOf = function(a, e) { + void 0 === e && (e = 0); + var k = this._length, f = e | 0; + if (0 > f) { + f += k, 0 > f && (f = 0); + } else { + if (f >= k) { + return-1; + } } - for (var b = Object.keys(a), c = a instanceof Array ? [] : {}, d = 0;d < b.length;d++) { - c.asSetPublicProperty(b[d], h.transformJSValueToAS(a[b[d]])); + for (var d = this._buffer, k = this._length, b = this._offset, k = b + k, f = f + b;f < k;f++) { + if (d[f] === a) { + return f - b; + } } - return c; + return-1; }; - h.transformASValueToJS = function(c) { - if ("object" !== typeof c || l(c)) { - return c; + e.prototype.lastIndexOf = function(a, e) { + void 0 === e && (e = 2147483647); + var k = this._length, f = e | 0; + if (0 > f) { + if (f += k, 0 > f) { + return-1; + } + } else { + f >= k && (f = k); } - for (var d = Object.keys(c), e = c instanceof Array ? [] : {}, r = 0;r < d.length;r++) { - var n = d[r], f = n; - b.isNumeric(n) || (f = a.getNameFromPublicQualifiedName(n)); - e[f] = h.transformASValueToJS(c[n]); + for (var k = this._buffer, d = this._offset, f = f + d;f-- > d;) { + if (k[f] === a) { + return f - d; + } } - return e; + return-1; }; - h.parseCore = function(a) { - a = x(a); - return h.transformJSValueToAS(JSON.parse(a)); - }; - h.stringifySpecializedToString = function(a, b, c, d) { - return JSON.stringify(h.transformASValueToJS(a), c, d); - }; - h.instanceConstructor = h; - h.staticNatives = null; - h.instanceNatives = null; - return h; - }(z); - g.ASJSON = R; - var H = function(a) { - function c(a, b) { - h("ASError"); - } - __extends(c, a); - c.prototype.getStackTrace = function() { - return b.AVM2.Runtime.AVM2.getStackTrace(); + e.prototype.map = function(a, m) { + c.isFunction(a) || p("ArgumentError", h.Errors.CheckTypeFailedError); + for (var k = new e, f = 0;f < this._length;f++) { + k.push(a.call(m, this._buffer[this._offset + f], f, this)); + } + return k; }; - c.instanceConstructor = null; - c.staticNatives = null; - c.instanceNatives = null; - c.getErrorMessage = b.AVM2.getErrorMessage; - return c; - }(P); - g.ASError = H; - var Y = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - return b; - }(H); - g.ASDefinitionError = Y; - var ga = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - return b; - }(H); - g.ASEvalError = ga; - var ca = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - return b; - }(H); - g.ASRangeError = ca; - var ka = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - return b; - }(H); - g.ASReferenceError = ka; - var W = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - return b; - }(H); - g.ASSecurityError = W; - var ha = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - return b; - }(H); - g.ASSyntaxError = ha; - var da = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - return b; - }(H); - g.ASTypeError = da; - var ja = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - return b; - }(H); - g.ASURIError = ja; - var N = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - return b; - }(H); - g.ASVerifyError = N; - var ma = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - return b; - }(H); - g.ASUninitializedError = ma; - var T = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - return b; - }(H); - g.ASArgumentError = T; - var oa = function(a) { - function c() { - a.apply(this, arguments); - } - __extends(c, a); - Object.defineProperty(c.prototype, "native_source", {get:function() { - return this.source; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "native_global", {get:function() { - return this.global; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "native_ignoreCase", {get:function() { - return this.ignoreCase; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "native_multiline", {get:function() { - return this.multiline; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "native_lastIndex", {get:function() { - return this.lastIndex; - }, set:function(a) { - this.lastIndex = a | 0; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "native_dotall", {get:function() { - return this.dotall; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "native_extended", {get:function() { - return this.extended; - }, enumerable:!0, configurable:!0}); - c.prototype.exec = function(a) { - "undefined" === typeof a && (a = ""); - var c = RegExp.prototype.exec.apply(this, arguments); - if (!c) { - return c; - } - for (var h = Object.keys(c), d = 0;d < h.length;d++) { - var e = h[d]; - b.isNumeric(e) || void 0 === c[e] && (c[e] = ""); - } - b.AVM2.Runtime.publicizeProperties(c); - return c; - }; - c.instanceConstructor = k.XRegExp; - c.staticNatives = [k.XRegExp]; - c.instanceNatives = [k.XRegExp.prototype]; - return c; - }(z); - g.ASRegExp = oa; - var pa = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - b.staticNatives = [Math]; - return b; - }(P); - g.ASMath = pa; - var na = function(a) { - function b() { - a.apply(this, arguments); - } - __extends(b, a); - b.staticNatives = [Date]; - b.instanceNatives = [Date.prototype]; - b.instanceConstructor = Date; - return b; - }(P); - g.ASDate = na; - var fa = b.ObjectUtilities.createMap(), sa = !1; - g.initialize = function(a) { - sa || (fa.ObjectClass = z, fa.Class = D, fa.FunctionClass = M, fa.BooleanClass = V, fa.MethodClosureClass = Q, fa.NamespaceClass = g.ASNamespace, fa.NumberClass = U, fa.IntClass = S, fa.UIntClass = aa, fa.StringClass = $, fa.ArrayClass = ea, fa.VectorClass = Z, fa.ObjectVectorClass = g.GenericVector, fa.IntVectorClass = v, fa.UIntVectorClass = X, fa.DoubleVectorClass = ba, fa.JSONClass = R, fa.XMLClass = g.ASXML, fa.XMLListClass = g.ASXMLList, fa.QNameClass = g.ASQName, fa.ErrorClass = H, - fa.DefinitionErrorClass = Y, fa.EvalErrorClass = ga, fa.RangeErrorClass = ca, fa.ReferenceErrorClass = ka, fa.SecurityErrorClass = W, fa.SyntaxErrorClass = ha, fa.TypeErrorClass = da, fa.URIErrorClass = ja, fa.VerifyErrorClass = N, fa.UninitializedErrorClass = ma, fa.ArgumentErrorClass = T, fa.DateClass = na, fa.MathClass = pa, fa.RegExpClass = oa, fa.ProxyClass = g.flash.utils.OriginalProxy, fa.DictionaryClass = g.flash.utils.OriginalDictionary, fa.ByteArrayClass = g.flash.utils.OriginalByteArray, - fa.SystemClass = g.flash.system.OriginalSystem, sa = !0); - }; - var xa = b.ObjectUtilities.createMap(), la = b.ObjectUtilities.createMap(); - g.registerNativeClass = function(a, b) { - y(!xa[a], "Native class: " + a + " is already registered."); - xa[a] = b; - }; - g.registerNativeFunction = s; - g.createInterface = function(a) { - var b = a.instanceInfo; - y(b.isInterface()); - a = new D(a); - a.interfaceBindings = new K(null, b, null, null); - a.verify(); - return a; - }; - var qa = []; - g.createClass = function(a, h, d) { - var e = a.instanceInfo, l = a.abc.applicationDomain, r = a.native, n; - r ? ((n = fa[a.native.cls]) || (n = xa[a.native.cls]), n || b.Debug.unexpected("No native class for " + a.native.cls), n.morphIntoASClass(a), qa && qa.push(n)) : n = new D(a); - d = new c(d, null); - d.object = n; - var f = null; - e.init.isNative() ? (y(r), f = n) : f = G(e.init, d, !1, !1); - var p = null, q = null; - r && (p = [n], n.staticNatives && b.ArrayUtilities.pushMany(p, n.staticNatives), q = [n.prototype], n.instanceNatives && b.ArrayUtilities.pushMany(q, n.instanceNatives)); - D.create(n, h, f); - n.verify(); - if ("Class" === a.instanceInfo.name.name) { - for (r = 0;r < qa.length;r++) { - qa[r].__proto__ = D.prototype; - } - qa = null; - } - k.enterTimeline("ClassBindings"); - n.classBindings = new O(a, d, p); - k.enterTimeline("applyTo"); - n.classBindings.applyTo(l, n); - k.leaveTimeline(); - k.leaveTimeline(); - k.enterTimeline("InstanceBindings"); - n.instanceBindings = new K(h ? h.instanceBindings : null, e, d, q); - n.instanceConstructor && (k.enterTimeline("applyTo"), n.instanceBindings.applyTo(l, n.traitsPrototype), k.leaveTimeline()); - k.leaveTimeline(); - n.implementedInterfaces = n.instanceBindings.implementedInterfaces; - n === D ? n.instanceBindings.applyTo(l, z, !0) : D.instanceBindings && D.instanceBindings.applyTo(l, n, !0); - k.enterTimeline("Configure"); - D.configureInitializers(n); - D.linkSymbols(n); - D.runClassInitializer(n); - k.leaveTimeline(); - return n; - }; - var ia = [I.forwardValueOf, I.forwardToString]; - g.getMethodOrAccessorNative = function(b, c) { - var h = m(a.getName(b.name)); - f("getMethodOrAccessorNative(" + h + ")"); - for (var d = 0;d < c.length;d++) { - var e = c[d], l = h; - n(e, "original_" + h) && (l = "original_" + h); - !n(e, h) && n(e, "native_" + h) && (l = "native_" + h); - if (n(e, l)) { - return b.isAccessor() ? (h = r(e, l), h = b.isGetter() ? h.get : h.set) : (y(b.isMethod()), h = e[l]), y(h, "Method or Accessor property exists but it's undefined: " + b), y(0 > ia.indexOf(h), "Leaking illegal function."), h; - } - } - f("Cannot find " + b + " in natives."); - return null; - }; - g.escapeNativeName = m; - (function(a) { - function c(a) { - for (var b = {prototype:E()}, h = Object.getOwnPropertyNames(a.prototype), d = 0;d < h.length;d++) { - b.prototype[h[d]] = a.prototype[h[d]]; - } - return b; - } - a.String = jsGlobal.String; - a.Function = jsGlobal.Function; - a.Boolean = jsGlobal.Boolean; - a.Number = jsGlobal.Number; - a.Date = jsGlobal.Date; - a.ASObject = b.AVM2.AS.ASObject; - a.Original = {Date:c(a.Date), Array:c(Array), String:c(a.String), Number:c(a.Number), Boolean:c(a.Boolean)}; - a.print = function() { - for (var a = [], b = 0;b < arguments.length - 0;b++) { - a[b] = arguments[b + 0]; - } - jsGlobal.print.apply(null, a); - }; - a.notImplemented = function(a) { - h(a); - }; - a.debugBreak = function(a) { - debugger; - }; - a.bugzilla = function(a) { - switch(a) { - case 574600: - return!0; - } - return!1; - }; - a.decodeURI = jsGlobal.decodeURI; - a.decodeURIComponent = jsGlobal.decodeURIComponent; - a.encodeURI = jsGlobal.encodeURI; - a.encodeURIComponent = jsGlobal.encodeURIComponent; - a.isNaN = jsGlobal.isNaN; - a.isFinite = jsGlobal.isFinite; - a.parseInt = jsGlobal.parseInt; - a.parseFloat = jsGlobal.parseFloat; - a.escape = jsGlobal.escape; - a.unescape = jsGlobal.unescape; - a.isXMLName = "undefined" !== typeof a.isXMLName ? jsGlobal.isXMLName : function() { - h("Chrome doesn't support isXMLName."); - }; - a.getQualifiedClassName = function(a) { - if (null === a) { - return "null"; - } - if (void 0 === a) { - return "void"; - } - if (S.isType(a)) { - return "int"; - } - a = C(a); - return D.isType(a) ? a.getQualifiedClassName() : a.class.getQualifiedClassName(); - }; - a.getQualifiedSuperclassName = function(a) { - if (l(a)) { - return "null"; - } - a = C(a); - a = D.isType(a) ? a : a.class; - return a.baseClass ? a.baseClass.getQualifiedClassName() : "null"; - }; - a.getDefinitionByName = function(c) { - c = a.String(c).replace("::", "."); - return b.AVM2.Runtime.AVM2.currentDomain().getClass(c, !1) || null; - }; - a.describeTypeJSON = function(a, c) { - return b.AVM2.AS.describeTypeJSON(a, c); - }; - })(g.Natives || (g.Natives = {})); - var wa = g.Natives; - g.getNative = d; - s("unsafeJSNative", d); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - var f = b.Debug.assert, t = b.Debug.assertNotImplemented, s = b.AVM2.Runtime.throwError, m = b.NumberUtilities.clamp, d = b.AVM2.Runtime.asCheckVectorGetNumericProperty, a = b.AVM2.Runtime.asCheckVectorSetNumericProperty, c = b.AVM2.AS.arraySort, n = function(n) { - function e(a, b, c) { - a >>>= 0; - this._fixed = !!b; - this._buffer = Array(a); - this._defaultValue = (this._type = c) ? c.defaultValue : null; - this._fill(a, this._defaultValue); - } - __extends(e, n); - e.defaultCompareFunction = function(a, b) { - return String(a).localeCompare(String(b)); - }; - e.compare = function(a, c, d, n) { - t(!(d & e.CASEINSENSITIVE), "CASEINSENSITIVE"); - t(!(d & e.UNIQUESORT), "UNIQUESORT"); - t(!(d & e.RETURNINDEXEDARRAY), "RETURNINDEXEDARRAY"); - var r = 0; - n || (n = e.defaultCompareFunction); - d & e.NUMERIC ? (a = b.toNumber(a), c = b.toNumber(c), r = a < c ? -1 : a > c ? 1 : 0) : r = n(a, c); - d & e.DESCENDING && (r *= -1); - return r; - }; - e._every = function(a, b, c) { - return a.every(b, c); - }; - e._forEach = function(a, b, c) { - return a.forEach(b, c); - }; - e._some = function(a, b, c) { - return a.some(b, c); - }; - e.applyType = function(a) { - function c(b, d) { - Function.prototype.call.call(e.instanceConstructor, this, b, d, a); - } - c.prototype = e.prototype; - c.instanceConstructor = c; - c.callableConstructor = function(a) { - if (a instanceof g.Int32Vector) { - return a; - } - var d = a.asGetProperty(void 0, "length"); - if (void 0 !== d) { - for (var e = new c(d, !1), h = 0;h < d;h++) { - e.asSetNumericProperty(h, a.asGetPublicProperty(h)); - } - return e; - } - b.Debug.unexpected(); - }; - c.__proto__ = e; - return c; - }; - e.prototype._fill = function(a, b) { - for (var c = 0;c < a;c++) { - this._buffer[0 + c] = b; - } - }; - e.prototype.toString = function() { - for (var a = "", b = 0;b < this._buffer.length;b++) { - a += this._buffer[b], b < this._buffer.length - 1 && (a += ","); - } - return a; - }; - e.prototype.every = function(a, b) { - for (var c = 0;c < this._buffer.length;c++) { - if (!a.call(b, this.asGetNumericProperty(c), c, this)) { - return!1; - } - } - return!0; - }; - e.prototype.filter = function(a, b) { - for (var c = new e(0, !1, this._type), d = 0;d < this._buffer.length;d++) { - a.call(b, this.asGetNumericProperty(d), d, this) && c.push(this.asGetNumericProperty(d)); - } - return c; - }; - e.prototype.some = function(a, c) { - 2 !== arguments.length ? s("ArgumentError", k.Errors.WrongArgumentCountError) : b.isFunction(a) || s("ArgumentError", k.Errors.CheckTypeFailedError); - for (var d = 0;d < this._buffer.length;d++) { - if (a.call(c, this.asGetNumericProperty(d), d, this)) { - return!0; - } - } - return!1; - }; - e.prototype.forEach = function(a, c) { - b.isFunction(a) || s("ArgumentError", k.Errors.CheckTypeFailedError); - for (var d = 0;d < this._buffer.length;d++) { - a.call(c, this.asGetNumericProperty(d), d, this); - } - }; - e.prototype.map = function(a, c) { - b.isFunction(a) || s("ArgumentError", k.Errors.CheckTypeFailedError); - for (var d = new e(0, !1, this._type), n = 0;n < this._buffer.length;n++) { - d.push(a.call(c, this.asGetNumericProperty(n), n, this)); - } - return d; - }; - e.prototype.push = function() { - for (var a = 0;a < arguments.length - 0;a++) { - } + e.prototype.push = function(a, e, k, f, d, b, g, r) { this._checkFixed(); - for (a = 0;a < arguments.length;a++) { - this._buffer.push(this._coerce(arguments[a])); + this._ensureCapacity(this._length + arguments.length); + for (var c = 0;c < arguments.length;c++) { + this._buffer[this._offset + this._length++] = arguments[c]; } }; e.prototype.pop = function() { this._checkFixed(); - return 0 === this._buffer.length ? void 0 : this._buffer.pop(); + if (0 === this._length) { + return e.DEFAULT_VALUE; + } + this._length--; + return this._buffer[this._offset + this._length]; }; e.prototype.reverse = function() { - this._buffer.reverse(); + for (var a = this._offset, e = this._offset + this._length - 1, k = this._buffer;a < e;) { + var f = k[a]; + k[a] = k[e]; + k[e] = f; + a++; + e--; + } + return this; }; e.prototype.sort = function(a) { - return this._buffer.sort(a); - }; - e.prototype.asGetNumericProperty = function(a) { - d(a, this._buffer.length); - return this._buffer[a]; - }; - e.prototype._coerce = function(a) { - return this._type ? this._type.coerce(a) : void 0 === a ? null : a; - }; - e.prototype.asSetNumericProperty = function(b, c) { - a(b, this._buffer.length, this._fixed); - this._buffer[b] = this._coerce(c); + if (0 === arguments.length) { + return Array.prototype.sort.call(this._view()); + } + if (a instanceof Function) { + return Array.prototype.sort.call(this._view(), a); + } + var c = a | 0; + s(!(c & e.UNIQUESORT), "UNIQUESORT"); + s(!(c & e.RETURNINDEXEDARRAY), "RETURNINDEXEDARRAY"); + return c & e.DESCENDING ? Array.prototype.sort.call(this._view(), function(a, e) { + return e - a; + }) : Array.prototype.sort.call(this._view(), function(a, e) { + return a - e; + }); }; e.prototype.shift = function() { this._checkFixed(); - return 0 === this._buffer.length ? void 0 : this._buffer.shift(); - }; - e.prototype._checkFixed = function() { - this._fixed && s("RangeError", k.Errors.VectorFixedError); + if (0 === this._length) { + return 0; + } + this._length--; + return this._buffer[this._offset++]; }; e.prototype.unshift = function() { + this._checkFixed(); if (arguments.length) { - this._checkFixed(); - for (var a = [], b = 0;b < arguments.length;b++) { - a.push(this._coerce(arguments[b])); + this._ensureCapacity(this._length + arguments.length); + this._slide(arguments.length); + this._offset -= arguments.length; + this._length += arguments.length; + for (var a = 0;a < arguments.length;a++) { + this._buffer[this._offset + a] = arguments[a]; } - this._buffer.unshift.apply(this._buffer, a); } }; + e.prototype.slice = function(a, c) { + void 0 === a && (a = 0); + void 0 === c && (c = 2147483647); + var k = this._buffer, f = this._length, d = Math.min(Math.max(a, 0), f), f = Math.min(Math.max(c, d), f), b = new e(f - d, this.fixed); + b._buffer.set(k.subarray(this._offset + d, this._offset + f), b._offset); + return b; + }; + e.prototype.splice = function(a, c) { + var k = this._buffer, f = this._length, d = Math.min(Math.max(a, 0), f), b = this._offset + d, g = Math.min(Math.max(c, 0), f - d), d = arguments.length - 2, r, m = new e(g, this.fixed); + 0 < g && (r = k.subarray(b, b + g), m._buffer.set(r, m._offset)); + this._ensureCapacity(f - g + d); + k.set(k.subarray(b + g, f), b + d); + this._length += d - g; + for (f = 0;f < d;f++) { + k[b + f] = arguments[f + 2]; + } + return m; + }; + e.prototype._slide = function(a) { + this._buffer.set(this._view(), this._offset + a); + this._offset += a; + }; Object.defineProperty(e.prototype, "length", {get:function() { - return this._buffer.length; + return this._length; }, set:function(a) { a >>>= 0; - if (a > this._buffer.length) { - for (var b = this._buffer.length;b < a;b++) { - this._buffer[b] = this._defaultValue; + if (a > this._length) { + this._ensureCapacity(a); + for (var c = this._offset + this._length, k = this._offset + a;c < k;c++) { + this._buffer[c] = e.DEFAULT_VALUE; } - } else { - this._buffer.length = a; } - f(this._buffer.length === a); + this._length = a; }, enumerable:!0, configurable:!0}); Object.defineProperty(e.prototype, "fixed", {get:function() { return this._fixed; }, set:function(a) { this._fixed = !!a; }, enumerable:!0, configurable:!0}); - e.prototype._spliceHelper = function(a, b, c, d, e) { - b = m(b, 0, d.length - e); - c = m(c, 0, this._buffer.length - a); - for (var h = [], n = 0;n < b;n++) { - h.push(this._coerce(d.asGetNumericProperty(e + n))); + e.prototype._checkFixed = function() { + this._fixed && p("RangeError", h.Errors.VectorFixedError); + }; + e.prototype.asGetNumericProperty = function(a) { + u(a, this._length); + return this._buffer[this._offset + a]; + }; + e.prototype.asSetNumericProperty = function(a, e) { + l(a, this._length, this._fixed); + a === this._length && (this._ensureCapacity(this._length + 1), this._length++); + this._buffer[this._offset + a] = e; + }; + e.prototype.asHasProperty = function(a, m, k) { + if (e.prototype === this || !c.isNumeric(m)) { + return Object.prototype.asHasProperty.call(this, a, m, k); } - this._buffer.splice.apply(this._buffer, [a, c].concat(h)); + a = c.toNumber(m); + return 0 <= a && a < this._length; }; e.prototype.asNextName = function(a) { return a - 1; }; e.prototype.asNextValue = function(a) { - return this._buffer[a - 1]; + return this._buffer[this._offset + a - 1]; }; e.prototype.asNextNameIndex = function(a) { a += 1; - return a <= this._buffer.length ? a : 0; - }; - e.prototype.asHasProperty = function(a, c, d) { - if (e.prototype === this || !b.isNumeric(c)) { - return Object.prototype.asHasProperty.call(this, a, c, d); - } - a = b.toNumber(c); - return 0 <= a && a < this._buffer.length; + return a <= this._length ? a : 0; }; e.prototype.asHasNext2 = function(a) { a.index = this.asNextNameIndex(a.index); }; - e.CASEINSENSITIVE = 1; + e.EXTRA_CAPACITY = 4; + e.INITIAL_CAPACITY = 10; + e.DEFAULT_VALUE = 0; e.DESCENDING = 2; e.UNIQUESORT = 4; e.RETURNINDEXEDARRAY = 8; - e.NUMERIC = 16; e.instanceConstructor = e; e.staticNatives = [e]; - e.instanceNatives = [e.prototype, g.ASVector.prototype]; - e._sort = c; + e.instanceNatives = [e.prototype]; + e.callableConstructor = e.callable; + e.classInitializer = function() { + var a = e.prototype; + v(a, "$Bgjoin", a.join); + v(a, "$BgtoString", a.join); + v(a, "$BgtoLocaleString", a.toLocaleString); + v(a, "$Bgpop", a.pop); + v(a, "$Bgpush", a.push); + v(a, "$Bgreverse", a.reverse); + v(a, "$Bgconcat", a.concat); + v(a, "$Bgsplice", a.splice); + v(a, "$Bgslice", a.slice); + v(a, "$Bgshift", a.shift); + v(a, "$Bgunshift", a.unshift); + v(a, "$BgindexOf", a.indexOf); + v(a, "$BglastIndexOf", a.lastIndexOf); + v(a, "$BgforEach", a.forEach); + v(a, "$Bgmap", a.map); + v(a, "$Bgfilter", a.filter); + v(a, "$Bgsome", a.some); + v(a, "$Bgevery", a.every); + v(a, "$Bgsort", a.sort); + }; return e; - }(g.ASVector); - g.GenericVector = n; - n.prototype._reverse = n.prototype.reverse; - n.prototype._filter = n.prototype.filter; - n.prototype._map = n.prototype.map; - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - function f(a) { - return a instanceof Q || a instanceof U; + }(a.ASVector); + a.Float64Vector = e; + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + function s(b) { + return b instanceof a.ASXML || b instanceof a.ASXMLList; } - function t(a) { - if ("object" === typeof a && null !== a) { - if (a instanceof U) { - return a._children.map(t).join(""); - } - switch(a._kind) { - case 3: - ; - case 2: - return a._value; - default: - return a.hasSimpleContent() ? a._children.map(t).join("") : p(a); - } - } else { - return String(a); + function v(b) { + if (!(b instanceof a.ASXML)) { + return String(b); + } + switch(b._kind) { + case 3: + ; + case 2: + return b._value; + default: + if (b.hasSimpleContent()) { + for (var d = "", g = 0;g < b._children.length;g++) { + var e = b._children[g]; + 4 !== e._kind && 5 !== e._kind && (d += v(e)); + } + return d; + } + return q(b); } } - function s(a) { - for (var b = 0, c;b < a.length && "&" !== (c = a[b]) && "<" !== c && ">" !== c;) { - b++; + function p(b) { + for (var a = 0, d;a < b.length && "&" !== (d = b[a]) && "<" !== d && ">" !== d;) { + a++; } - if (b >= a.length) { - return a; + if (a >= b.length) { + return b; } - for (var h = a.substring(0, b);b < a.length;) { - switch(c = a[b++], c) { + for (var g = b.substring(0, a);a < b.length;) { + switch(d = b[a++], d) { case "&": - h += "&"; + g += "&"; break; case "<": - h += "<"; + g += "<"; break; case ">": - h += ">"; + g += ">"; break; default: - h += c; + g += d; } } - return h; + return g; } - function m(a) { - for (var b = 0, c;b < a.length && "&" !== (c = a[b]) && "<" !== c && '"' !== c && "\n" !== c && "\r" !== c && "\t" !== c;) { - b++; + function u(b) { + for (var a = 0, d;a < b.length && "&" !== (d = b[a]) && "<" !== d && '"' !== d && "\n" !== d && "\r" !== d && "\t" !== d;) { + a++; } - if (b >= a.length) { - return a; + if (a >= b.length) { + return b; } - for (var h = a.substring(0, b);b < a.length;) { - switch(c = a[b++], c) { + for (var g = b.substring(0, a);a < b.length;) { + switch(d = b[a++], d) { case "&": - h += "&"; + g += "&"; break; case "<": - h += "<"; + g += "<"; break; case '"': - h += """; + g += """; break; case "\n": - h += " "; + g += " "; break; case "\r": - h += " "; + g += " "; break; case "\t": - h += " "; + g += " "; break; default: - h += c; + g += d; } } - return h; + return g; } - function d(a, b) { - var c = a[b]; - return " " === c || "\n" === c || "\r" === c || "\t" === c; + function l(b, a) { + var d = b[a]; + return " " === d || "\n" === d || "\r" === d || "\t" === d; } - function a(a) { - for (var b = 0;b < a.length && d(a, b);) { - b++; + function e(b) { + for (var a = 0;a < b.length && l(b, a);) { + a++; } - if (b >= a.length) { + if (a >= b.length) { return ""; } - for (var c = a.length - 1;d(a, c);) { - c--; + for (var d = b.length - 1;l(b, d);) { + d--; } - return 0 === b && c === a.length - 1 ? a : a.substring(b, c + 1); + return 0 === a && d === b.length - 1 ? b : b.substring(a, d + 1); } - function c(a) { - if (0 < a) { - if (void 0 !== A[a]) { - return A[a]; + function m(b) { + if (0 < b) { + if (void 0 !== F[b]) { + return F[b]; } - for (var b = "", c = 0;c < a;c++) { - b += " "; + for (var a = "", d = 0;d < b;d++) { + a += " "; } - return A[a] = b; + return F[b] = a; } return ""; } - function n(a) { - for (var b = 1, c;;) { - c = "_ns" + b; - if (!a.some(function(a) { - return a.prefix == c; + function t(b) { + for (var a = 1, d;;) { + d = "_ns" + a; + if (!b.some(function(b) { + return b.prefix == d; })) { break; } - b++; + a++; } - return c; + return d; } - function p(b, h, d) { + function q(b, d, g) { if (null === b || void 0 === b) { throw new TypeError; } - if (!(b instanceof Q)) { - return b instanceof U ? b._children.map(function(a) { - return p(a, h); - }).join(Q.prettyPrinting ? "\n" : "") : s(String(b)); - } - var e = Q.prettyPrinting; - d |= 0; - var l = e ? c(d) : "", r = b._kind; + if (!(b instanceof P)) { + return b instanceof V ? b._children.map(function(b) { + return q(b, d); + }).join(P.prettyPrinting ? "\n" : "") : p(String(b)); + } + var f = P.prettyPrinting; + g |= 0; + var k = f ? m(g) : "", r = b._kind; switch(r) { case 3: - return e ? l + s(a(b._value)) : s(b._value); + return f ? k + p(e(b._value)) : p(b._value); case 2: - return l + m(b._value); + return k + u(b._value); case 4: - return l + "\x3c!--" + b._value + "--\x3e"; + return k + "\x3c!--" + b._value + "--\x3e"; case 5: - return l + ""; + return k + ""; default: - y(1 === r); + z(1 === r); } - h = h || []; - for (var f = [], q = 0;q < b._inScopeNamespaces.length;q++) { - var x = b._inScopeNamespaces[q].prefix, g = b._inScopeNamespaces[q].uri; - h.every(function(a) { - return a.uri != g || a.prefix != x; - }) && (r = new z(x, g), f.push(r)); - } - var w = h.concat(f), r = b._name.getNamespace(w); - void 0 === r.prefix && (q = n(w), q = new z(q, r.uri), f.push(q), w.push(q)); - var u = (r.prefix ? r.prefix + ":" : "") + b._name.localName, l = l + ("<" + u); - b._attributes.forEach(function(a) { - a = a._name.getNamespace(w); - if (void 0 === a.prefix) { - var b = n(w); - a = new z(b, a.uri); - f.push(a); - w.push(a); + d = d || []; + for (var c = [], w = 0;b._inScopeNamespaces && w < b._inScopeNamespaces.length;w++) { + var l = b._inScopeNamespaces[w].prefix, n = b._inScopeNamespaces[w].uri; + d.every(function(b) { + return b.uri != n || b.prefix != l; + }) && (r = new a.ASNamespace(l, n), c.push(r)); + } + var s = d.concat(c), r = b._name.getNamespace(s); + void 0 === r.prefix && (w = t(s), w = new a.ASNamespace(w, r.uri), c.push(w), s.push(w)); + var h = (r.prefix ? r.prefix + ":" : "") + b._name.localName, k = k + ("<" + h); + b._attributes && b._attributes.forEach(function(b) { + b = b._name.getNamespace(s); + if (void 0 === b.prefix) { + var d = t(s); + b = new a.ASNamespace(d, b.uri); + c.push(b); + s.push(b); } }); - for (q = 0;q < f.length;q++) { - r = f[q], l += " " + (r.prefix ? "xmlns:" + r.prefix : "xmlns") + '="' + m(r.uri) + '"'; + for (w = 0;w < c.length;w++) { + r = c[w], k += " " + (r.prefix ? "xmlns:" + r.prefix : "xmlns") + '="' + u(r.uri) + '"'; } - b._attributes.forEach(function(a) { - var b = a._name, c = b.getNamespace(h); - l += " " + (c.prefix ? c.prefix + ":" + b.localName : b.localName) + '="' + m(a._value) + '"'; + b._attributes && b._attributes.forEach(function(b) { + var a = b._name, g = a.getNamespace(d); + k += " " + (g.prefix ? g.prefix + ":" + a.localName : a.localName) + '="' + u(b._value) + '"'; }); - if (0 === b._children.length) { - return l += "/>"; + if (0 === b.length()) { + return k += "/>"; } - var l = l + ">", k = 1 < b._children.length || 1 === b._children.length && 3 !== b._children[0]._kind, G = e && k ? d + Q.prettyIndent : 0; - b._children.forEach(function(a, b) { - e && k && (l += "\n"); - var c = p(a, w, G); - l += c; + var k = k + ">", A = 1 < b._children.length || 1 === b._children.length && 3 !== b._children[0]._kind, v = f && A ? g + P.prettyIndent : 0; + b._children.forEach(function(b, a) { + f && A && (k += "\n"); + var d = q(b, s, v); + k += d; }); - e && k && (l += "\n" + c(d)); - return l += ""; - } - function e(a) { - if (null === a) { - throw new TypeError(k.formatErrorMessage(k.Errors.ConvertNullToObjectError)); - } - if (void 0 === a) { - throw new TypeError(k.formatErrorMessage(k.Errors.ConvertUndefinedToObjectError)); - } - if (a instanceof Q) { - return a; - } - if (a instanceof U) { - if (1 === a.length()) { - return a._children[0]; - } - throw new TypeError(k.formatErrorMessage(k.Errors.XMLMarkupMustBeWellFormed)); - } - a = B.parseFromString(String(a)); - if (0 === a.length()) { - return a = new h(3); - } - if (1 === a.length()) { - return a._children[0]._parent = null, a._children[0]; - } - throw "SyntaxError in ToXML"; + f && A && (k += "\n" + m(g)); + return k += ""; } - function q(a) { - if (null === a) { - throw new TypeError(k.formatErrorMessage(k.Errors.ConvertNullToObjectError)); + function n(b) { + if (null === b) { + throw new TypeError(h.formatErrorMessage(h.Errors.ConvertNullToObjectError)); } - if (void 0 === a) { - throw new TypeError(k.formatErrorMessage(k.Errors.ConvertUndefinedToObjectError)); + if (void 0 === b) { + throw new TypeError(h.formatErrorMessage(h.Errors.ConvertUndefinedToObjectError)); } - if (a instanceof h) { - var b = new x(a.parent, a.name); - b.appendChild(a); + if (b instanceof P) { return b; } - if (a instanceof x) { - return a; + if (b instanceof V) { + return 1 !== b._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLMarkupMustBeWellFormed), b._children[0]; } - a = e("" + a + ""); - for (var b = new x, c = 0;c < a.length();c++) { - var d = a._children[c]; - d._parent = null; - b.appendChild(d); + b = I.parseFromString(M(b)); + var a = b.length(); + if (0 === a) { + return w(3); } - return b; - } - function l(a) { - if (void 0 === a || null === a || "boolean" === typeof a || "number" === typeof a) { - throw "TypeError: invalid operand to ToAttributeName()"; + if (1 === a) { + return b._children[0]._parent = null, b._children[0]; } - if (f(a)) { - a = t(a); + h.Runtime.throwError("TypeError", h.Errors.XMLMarkupMustBeWellFormed); + } + function k(b, a) { + z(!(b instanceof V)); + null === b && h.Runtime.throwError("TypeError", h.Errors.ConvertNullToObjectError); + void 0 === b && h.Runtime.throwError("TypeError", h.Errors.ConvertUndefinedToObjectError); + if (b instanceof P) { + a.append(b); } else { - if ("object" === typeof a && null !== a) { - if (a instanceof D) { - return new D(a.uri, a.localName, !0); - } - if (G.isQName(a)) { - return D.fromMultiname(a); + var d = n('' + b + "")._children; + if (d) { + for (var g = 0;g < d.length;g++) { + var e = d[g]; + e._parent = null; + a.append(e); } - a = t(a); } } - if ("string" === typeof a) { - var b = Namespace.createNamespace("", ""), b = new D(b, a, !0) + } + function f(b) { + void 0 !== b && null !== b && "boolean" !== typeof b && "number" !== typeof b || h.Runtime.throwError("TypeError", h.Errors.ConvertUndefinedToObjectError); + if ("object" === typeof b) { + if (b instanceof Q) { + return new a.ASQName(b.uri, b.localName, !0); + } + if (A.isQName(b)) { + return Q.fromMultiname(b); + } } - return b; + b = v(b); + return new a.ASQName(void 0, b, !0); } - function u(a) { - if (void 0 === a) { - return new D("*"); + function d(b) { + if (void 0 === b) { + return new a.ASQName("*"); } - if ("object" === typeof a && null !== a) { - if (a instanceof D) { - return a; + if ("object" === typeof b && null !== b) { + if (b instanceof Q) { + return b; } - if (G.isQName(a)) { - return D.fromMultiname(a); + if (A.isQName(b)) { + return Q.fromMultiname(b); } - a = a instanceof Q || a instanceof U ? t(a) : a instanceof G ? a.name : a.toString(); + var d; + d = b instanceof P || b instanceof V ? v(b) : b instanceof A ? b.name : b.toString(); } else { - if ("string" !== typeof a) { + if ("string" === typeof b) { + d = b; + } else { throw new TypeError; } } - return "@" === a[0] ? l(a.substring(1)) : new D(a); + return "@" === d[0] ? f(d.substring(1)) : new a.ASQName(void 0, d, !!(b.flags & 1)); } - function w(a) { - return "object" === typeof a && a instanceof D ? !!(a._flags & 1) : !1; + function b(b) { + return b instanceof Q && !!(b.name.flags & 1); } - function r(a, b, c) { - return a && 1 === a.length && a[0] instanceof z && ("string" === typeof b || void 0 === b) ? new D(a[0], b || "*", c) : b; + function g(b, d, g) { + return b && 1 === b.length && b[0] instanceof G && ("string" === typeof d || void 0 === d) ? new a.ASQName(b[0], d || "*", g) : d; } - function h(a, b, c, h) { - var d = Object.create(Q.prototype); - void 0 === a && (a = 3); - void 0 === b && (b = ""); - void 0 === c && (c = ""); - d.init(a, b, c, h); - return d; + function r(b) { + try { + new a.ASQName(b); + } catch (d) { + return!1; + } + return!0; } - function x(a, b) { - var c = Object.create(U.prototype); - c._targetObject = a ? a : null; - c._targetProperty = b ? b : null; - c._children = []; - return c; + function w(b, d, g, e) { + var f = new a.ASXML; + void 0 === b && (b = 3); + void 0 === d && (d = ""); + void 0 === g && (g = ""); + f.init(b, d, g, e); + return f; } - var y = b.Debug.assert, G = b.AVM2.ABC.Multiname, I = b.Debug.notImplemented, C = Object.prototype.asGetProperty, E = Object.prototype.asSetProperty, O = Object.prototype.asCallProperty, K = Object.prototype.asHasProperty, F = Object.prototype.asHasOwnProperty, J = Object.prototype.asGetEnumerableKeys, A = [], B = new function() { - function b(c, h) { - function e(a) { - return a.replace(/&([^;]+);/g, function(a, b) { - if ("#x" === b.substring(0, 2)) { - return String.fromCharCode(parseInt(b.substring(2), 16)); + var z = c.Debug.assert, A = c.AVM2.ABC.Multiname, B = c.Debug.notImplemented, M = c.AVM2.Runtime.asCoerceString, N = c.ObjectUtilities.defineNonEnumerableProperty, K = c.ObjectUtilities.createPublicAliases, y = Object.prototype.asGetProperty, D = Object.prototype.asSetProperty, L = Object.prototype.asCallProperty, H = Object.prototype.asHasProperty, J = Object.prototype.asHasOwnProperty, C = Object.prototype.asDeleteProperty, E = Object.prototype.asGetEnumerableKeys; + a.escapeElementValue = p; + a.escapeAttributeValue = u; + var F = []; + a.isXMLName = r; + c.AVM2.AS.Natives.isXMLName = r; + var I = new function() { + function b(a, d) { + function g(b) { + return b.replace(/&([^;]+);/g, function(b, a) { + if ("#x" === a.substring(0, 2)) { + return String.fromCharCode(parseInt(a.substring(2), 16)); } - if ("#" === b.substring(0, 1)) { - return String.fromCharCode(parseInt(b.substring(1), 10)); + if ("#" === a.substring(0, 1)) { + return String.fromCharCode(parseInt(a.substring(1), 10)); } - switch(b) { + switch(a) { case "lt": return "<"; case "gt": @@ -12676,147 +13101,147 @@ case "quot": return'"'; } - return a; + return b; }); } - function l() { - for (var a = g.length - 1;0 <= a;--a) { - if ("preserve" === g[a].space) { + function f() { + for (var b = q.length - 1;0 <= b;--b) { + if ("preserve" === q[b].space) { return!0; } } return!1; } - function n() { - for (var a = g.length - 1;0 <= a;--a) { - if ("xmlns" in g[a]) { - return g[a].xmlns; + function k() { + for (var b = q.length - 1;0 <= b;--b) { + if ("xmlns" in q[b]) { + return q[b].xmlns; } } return ""; } - function r(a) { - for (var b = g.length - 1;0 <= b;--b) { - if (a in g[b].lookup) { - return g[b].lookup[a]; + function r(b) { + for (var a = q.length - 1;0 <= a;--a) { + if (b in q[a].lookup) { + return q[a].lookup[b]; } } } - function f(a, b) { - var c = a.indexOf(":"); - if (0 <= c) { - var h = a.substring(0, c), d = r(h); - if (void 0 === d) { - throw "Unknown namespace: " + h; + function c(b, a) { + var d = b.indexOf(":"); + if (0 <= d) { + var g = b.substring(0, d), e = r(g); + if (void 0 === e) { + throw "Unknown namespace: " + g; } - c = a.substring(c + 1); - return{name:d + "::" + c, localName:c, prefix:h, namespace:d}; + d = b.substring(d + 1); + return{name:e + "::" + d, localName:d, prefix:g, namespace:e}; } - return b ? {name:a, localName:a, prefix:"", namespace:n()} : {name:a, localName:a, prefix:"", namespace:""}; + return a ? {name:b, localName:b, prefix:"", namespace:k()} : {name:b, localName:b, prefix:"", namespace:""}; } - function p(a, b) { - function c() { - for (;h < a.length && d(a, h);) { - ++h; + function m(b, a) { + function d() { + for (;e < b.length && l(b, e);) { + ++e; } } - for (var h = b, l, n = [];h < a.length && !d(a, h) && ">" !== a[h] && "/" !== a[h];) { - ++h; + for (var e = a, f, k = [];e < b.length && !l(b, e) && ">" !== b[e] && "/" !== b[e];) { + ++e; } - l = a.substring(b, h); - for (c();h < a.length && ">" !== a[h] && "/" !== a[h] && "?" !== a[h];) { - c(); - for (var r = "", f = "";h < a.length && !d(a, h) && "=" !== a[h];) { - r += a[h], ++h; + f = b.substring(a, e); + for (d();e < b.length && ">" !== b[e] && "/" !== b[e] && "?" !== b[e];) { + d(); + for (var r = "", c = "";e < b.length && !l(b, e) && "=" !== b[e];) { + r += b[e], ++e; } - c(); - if ("=" !== a[h]) { + d(); + if ("=" !== b[e]) { throw "'=' expected"; } - ++h; - c(); - f = a[h]; - if ('"' !== f && "'" !== f) { + ++e; + d(); + c = b[e]; + if ('"' !== c && "'" !== c) { throw "Quote expected"; } - var x = a.indexOf(f, ++h); - if (0 > x) { + var w = b.indexOf(c, ++e); + if (0 > w) { throw "Unexpected EOF[6]"; } - f = a.substring(h, x); - n.push({name:r, value:e(f)}); - h = x + 1; - c(); + c = b.substring(e, w); + k.push({name:r, value:g(c)}); + e = w + 1; + d(); } - return{name:l, attributes:n, parsed:h - b}; + return{name:f, attributes:k, parsed:e - a}; } - function q(a, b) { - for (var c = b, h;c < a.length && !d(a, c) && ">" !== a[c] && "/" !== a[c];) { - ++c; + function w(b, a) { + for (var d = a, g;d < b.length && !l(b, d) && ">" !== b[d] && "/" !== b[d];) { + ++d; } - for (h = a.substring(b, c);c < a.length && d(a, c);) { - ++c; + for (g = b.substring(a, d);d < b.length && l(b, d);) { + ++d; } - for (var e = c;c < a.length && ("?" !== a[c] || ">" != a[c + 1]);) { - ++c; + for (var e = d;d < b.length && ("?" !== b[d] || ">" != b[d + 1]);) { + ++d; } - return{name:h, value:a.substring(e, c), parsed:c - b}; + return{name:g, value:b.substring(e, d), parsed:d - a}; } - for (var x = 0, g = [{namespaces:[], lookup:{xmlns:"http://www.w3.org/2000/xmlns/", xml:"http://www.w3.org/XML/1998/namespace"}, inScopes:Q.defaultNamespace ? [{uri:Q.defaultNamespace, prefix:""}] : [], space:"default", xmlns:Q.defaultNamespace || ""}];x < c.length;) { - var m = x; - if ("<" === c[x]) { - switch(++m, c[m]) { + for (var n = 0, q = [{namespaces:[], lookup:{xmlns:"http://www.w3.org/2000/xmlns/", xml:"http://www.w3.org/XML/1998/namespace"}, inScopes:P.defaultNamespace ? [{uri:P.defaultNamespace, prefix:""}] : [], space:"default", xmlns:P.defaultNamespace || ""}];n < a.length;) { + var z = n; + if ("<" === a[n]) { + switch(++z, a[z]) { case "/": - ++m; - x = c.indexOf(">", m); - if (0 > x) { + ++z; + n = a.indexOf(">", z); + if (0 > n) { throw "Unexpected EOF[1]"; } - m = f(c.substring(m, x), !0); - h.endElement(m); - g.pop(); - m = x + 1; + z = c(a.substring(z, n), !0); + d.endElement(z); + q.pop(); + z = n + 1; break; case "?": - ++m; - x = q(c, m); - if ("?>" != c.substring(m + x.parsed, m + x.parsed + 2)) { + ++z; + n = w(a, z); + if ("?>" != a.substring(z + n.parsed, z + n.parsed + 2)) { throw "Unexpected EOF[2]"; } - h.pi(x.name, x.value); - m += x.parsed + 2; + d.pi(n.name, n.value); + z += n.parsed + 2; break; case "!": - if ("--" === c.substring(m + 1, m + 3)) { - x = c.indexOf("--\x3e", m + 3); - if (0 > x) { + if ("--" === a.substring(z + 1, z + 3)) { + n = a.indexOf("--\x3e", z + 3); + if (0 > n) { throw "Unexpected EOF[3]"; } - h.comment(c.substring(m + 3, x)); - m = x + 3; + d.comment(a.substring(z + 3, n)); + z = n + 3; } else { - if ("[CDATA[" === c.substring(m + 1, m + 8)) { - x = c.indexOf("]]\x3e", m + 8); - if (0 > x) { + if ("[CDATA[" === a.substring(z + 1, z + 8)) { + n = a.indexOf("]]\x3e", z + 8); + if (0 > n) { throw "Unexpected EOF[4]"; } - h.cdata(c.substring(m + 8, x)); - m = x + 3; + d.cdata(a.substring(z + 8, n)); + z = n + 3; } else { - if ("DOCTYPE" === c.substring(m + 1, m + 8)) { - var w = c.indexOf("[", m + 8), u = !1, x = c.indexOf(">", m + 8); - if (0 > x) { + if ("DOCTYPE" === a.substring(z + 1, z + 8)) { + var t = a.indexOf("[", z + 8), s = !1, n = a.indexOf(">", z + 8); + if (0 > n) { throw "Unexpected EOF[5]"; } - if (0 < w && x > w) { - x = c.indexOf("]>", m + 8); - if (0 > x) { + if (0 < t && n > t) { + n = a.indexOf("]>", z + 8); + if (0 > n) { throw "Unexpected EOF[7]"; } - u = !0; + s = !0; } - h.doctype(c.substring(m + 8, x + (u ? 1 : 0))); - m = x + (u ? 2 : 1); + d.doctype(a.substring(z + 8, n + (s ? 1 : 0))); + z = n + (s ? 2 : 1); } else { throw "Unknown !tag"; } @@ -12824,290 +13249,289 @@ } break; default: - w = p(c, m); - u = !1; - if ("/>" === c.substring(m + w.parsed, m + w.parsed + 2)) { - u = !0; + t = m(a, z); + s = !1; + if ("/>" === a.substring(z + t.parsed, z + t.parsed + 2)) { + s = !0; } else { - if (">" !== c.substring(m + w.parsed, m + w.parsed + 1)) { + if (">" !== a.substring(z + t.parsed, z + t.parsed + 1)) { throw "Unexpected EOF[2]"; } } - for (var k = {namespaces:[], lookup:Object.create(null)}, y = w.attributes, x = 0;x < y.length;++x) { - var s = y[x], G = s.name; - if ("xmlns:" === G.substring(0, 6)) { - G = G.substring(6), s = s.value, r(G) !== s && (k.lookup[G] = a(s), k.namespaces.push({uri:s, prefix:G})), delete y[x]; + for (var h = {namespaces:[], lookup:Object.create(null)}, A = t.attributes, n = 0;n < A.length;++n) { + var p = A[n], v = p.name; + if ("xmlns:" === v.substring(0, 6)) { + v = v.substring(6), p = p.value, r(v) !== p && (h.lookup[v] = e(p), h.namespaces.push({uri:p, prefix:v})), delete A[n]; } else { - if ("xmlns" === G) { - s = s.value, n() !== s && (k.xmlns = a(s), k.namespaces.push({uri:s, prefix:""})), delete y[x]; + if ("xmlns" === v) { + p = p.value, k() !== p && (h.xmlns = e(p), h.namespaces.push({uri:p, prefix:""})), delete A[n]; } else { - if ("xml:" === G.substring(0, 4)) { - var I = G.substring(4); - if ("space" !== I && "lang" !== I && "base" !== I) { - throw "Invalid xml attribute: " + G; + if ("xml:" === v.substring(0, 4)) { + var B = v.substring(4); + if ("space" !== B && "lang" !== B && "base" !== B && "id" !== B) { + throw "Invalid xml attribute: " + v; } - k[I] = a(s.value); + h[B] = e(p.value); } else { - if ("xml" === G.substring(0, 3)) { + if ("xml" === v.substring(0, 3)) { throw "Invalid xml attribute"; } } } } } - var t = []; - k.namespaces.forEach(function(a) { - a.prefix && k.lookup[a.prefix] !== a.uri || t.push(a); + var u = []; + h.namespaces.forEach(function(b) { + b.prefix && h.lookup[b.prefix] !== b.uri || u.push(b); }); - g[g.length - 1].inScopes.forEach(function(a) { - (!a.prefix || a.prefix in k.lookup) && (a.prefix || "xmlns" in k) || t.push(a); + q[q.length - 1].inScopes.forEach(function(b) { + (!b.prefix || b.prefix in h.lookup) && (b.prefix || "xmlns" in h) || u.push(b); }); - k.inScopes = t; - g.push(k); - G = []; - for (x = 0;x < y.length;++x) { - (s = y[x]) && G.push({name:f(s.name, !1), value:s.value}); - } - h.beginElement(f(w.name, !0), G, t, u); - m += w.parsed + (u ? 2 : 1); - u && g.pop(); + h.inScopes = u; + q.push(h); + v = []; + for (n = 0;n < A.length;++n) { + (p = A[n]) && v.push({name:c(p.name, !1), value:p.value}); + } + d.beginElement(c(t.name, !0), v, u, s); + z += t.parsed + (s ? 2 : 1); + s && q.pop(); } } else { - w = !0; - do { - if (w = w && d(c, m), ++m >= c.length) { - break; - } - } while ("<" !== c[m]); - h.text(e(c.substring(x, m)), w || l()); + for (;z++ < a.length && "<" !== a[z];) { + } + d.text(g(a.substring(n, z)), f()); } - x = m; + n = z; } } - this.parseFromString = function(a, c) { - var d = new h(1, "", "", ""), e = []; - b(a, {beginElement:function(a, b, c, l) { - var n = d; - e.push(n); - d = new h(1, a.namespace, a.localName, a.prefix); - for (a = 0;a < b.length;++a) { - var r = b[a], f = new h(2, r.name.namespace, r.name.localName, r.name.prefix); - f._value = r.value; - d._attributes.push(f); - } - for (a = 0;a < c.length;++a) { - b = c[a], b = Namespace.createNamespace(b.uri, b.prefix), d._inScopeNamespaces.push(b); - } - n.insert(n.length(), d); - l && (d = e.pop()); - }, endElement:function(a) { - d = e.pop(); - }, text:function(a, b) { - if (!b || !Q.ignoreWhitespace) { - var c = new h(3, "", "", void 0); - c._value = a; - d.insert(d.length(), c); - } - }, cdata:function(a) { - var b = new h(3, "", "", void 0); - b._value = a; - d.insert(d.length(), b); - }, comment:function(a) { - if (!Q.ignoreComments) { - var b = new h(4, "", "", void 0); - b._value = a; - d.insert(d.length(), b); - } - }, pi:function(a, b) { - if (!Q.ignoreProcessingInstructions) { - var c = new h(5, "", a, void 0); - c._value = b; - d.insert(d.length(), c); + this.parseFromString = function(d, g) { + var f = w(1, "", "", ""), k = []; + b(d, {beginElement:function(b, d, g, e) { + var r = f; + k.push(r); + f = w(1, b.namespace, b.localName, b.prefix); + for (b = 0;b < d.length;++b) { + var c = d[b], m = w(2, c.name.namespace, c.name.localName, c.name.prefix); + m._value = c.value; + m._parent = f; + f._attributes.push(m); + } + for (b = 0;b < g.length;++b) { + d = g[b], d = new a.ASNamespace(d.prefix, d.uri), f._inScopeNamespaces.push(d); + } + r.insert(r.length(), f); + e && (f = k.pop()); + }, endElement:function(b) { + f = k.pop(); + }, text:function(b, a) { + P.ignoreWhitespace && (b = e(b)); + if (!(0 === b.length || a && P.ignoreWhitespace)) { + var d = w(3, "", "", void 0); + d._value = b; + f.insert(f.length(), d); + } + }, cdata:function(b) { + var a = w(3, "", "", void 0); + a._value = b; + f.insert(f.length(), a); + }, comment:function(b) { + if (!P.ignoreComments) { + var a = w(4, "", "", void 0); + a._value = b; + f.insert(f.length(), a); + } + }, pi:function(b, a) { + if (!P.ignoreProcessingInstructions) { + var d = w(5, "", b, void 0); + d._value = a; + f.insert(f.length(), d); } - }, doctype:function(a) { + }, doctype:function(b) { }}); - return d; + return f; }; - }, z = function(a) { - function c(a, h) { - var d = "", e = ""; - if (0 !== arguments.length) { - if (1 === arguments.length) { - d = a, b.isObject(d) && d instanceof c ? (e = d.prefix, d = d.uri) : b.isObject(d) && d instanceof D && null !== d.uri ? d = d.uri : (d = t(d), e = "" === d ? "" : void 0); - } else { - if (d = h, d = b.isObject(d) && d instanceof D && null !== d.uri ? d.uri : t(d), "" === d) { - if (void 0 === a || "" === t(a)) { - e = ""; - } else { - throw new TypeError; - } - } else { - if (void 0 === a) { - e = void 0; - } else { - a: { - try { - new D(a); - } catch (l) { - e = !1; - break a; - } - e = !0; - } - e = !1 === e ? void 0 : t(a); - } - } - } - } - this._ns = Namespace.createNamespace(d, e); - } - __extends(c, a); - c.fromNamespace = function(a) { - return new c._namespaceConstructor(a); + }, G = function(b) { + function d(b, a) { + var g = "", e = ""; + 0 !== arguments.length && (1 === arguments.length ? (g = b, c.isObject(g) && g instanceof d ? (e = g.prefix, g = g.uri) : c.isObject(g) && g instanceof Q && null !== g.uri ? g = g.uri : (g = v(g), e = "" === g ? "" : void 0)) : (g = a, g = c.isObject(g) && g instanceof Q && null !== g.uri ? g.uri : v(g), "" === g ? void 0 === b || "" === v(b) ? e = "" : h.Runtime.throwError("TypeError", h.Errors.XMLNamespaceWithPrefixAndNoURI, b) : e = void 0 === b ? void 0 : !1 === r(b) ? void 0 : v(b))); + this._ns = Namespace.createNamespace(g, e); + } + __extends(d, b); + d.prototype.equals = function(b) { + return b instanceof d && b._ns.uri === this._ns.uri || "string" === typeof b && this._ns.uri === b; }; - Object.defineProperty(c.prototype, "prefix", {get:function() { + Object.defineProperty(d.prototype, "prefix", {get:function() { return this._ns.prefix; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "uri", {get:function() { + Object.defineProperty(d.prototype, "uri", {get:function() { return this._ns.uri; }, enumerable:!0, configurable:!0}); - c.staticNatives = null; - c.instanceNatives = null; - c.instanceConstructor = c; - c._namespaceConstructor = function(a) { - this._ns = a; - }; - c._namespaceConstructor.prototype = c.prototype; - c._ = void 0; - c.callableConstructor = function(a, h) { - if (1 === arguments.length && b.isObject(a) && a instanceof c) { - return a; + d.prototype.toString = function() { + return this === d.prototype ? "" : this._ns.uri; + }; + d.prototype.valueOf = function() { + return this === d.prototype ? "" : this._ns.uri; + }; + d.staticNatives = null; + d.instanceNatives = null; + d.instanceConstructor = d; + d.classInitializer = function() { + var b = d.prototype; + N(b, "$BgtoString", b.toString); + }; + d.callableConstructor = function(b, g) { + if (1 === arguments.length && c.isObject(b) && b instanceof d) { + return b; } switch(arguments.length) { case 0: - return new c; + return new a.ASNamespace; case 1: - return new c(a); + return new a.ASNamespace(b); default: - return new c(a, h); + return new a.ASNamespace(b, g); } }; - return c; - }(g.ASObject); - g.ASNamespace = z; - var P; - (function(a) { - a[a.ATTR_NAME = 1] = "ATTR_NAME"; - a[a.ELEM_NAME = 2] = "ELEM_NAME"; - a[a.ANY_NAME = 4] = "ANY_NAME"; - a[a.ANY_NAMESPACE = 8] = "ANY_NAMESPACE"; - })(P || (P = {})); - var D = function(a) { - function c(a, h, d) { - var e, l; - 0 === arguments.length ? e = "" : 1 === arguments.length ? e = a : (l = a, e = h); - if (b.isObject(e) && e instanceof c) { + return d; + }(a.ASObject); + a.ASNamespace = G; + var Z; + (function(b) { + b[b.ATTR_NAME = 1] = "ATTR_NAME"; + b[b.ELEM_NAME = 2] = "ELEM_NAME"; + b[b.ANY_NAME = 4] = "ANY_NAME"; + b[b.ANY_NAMESPACE = 8] = "ANY_NAMESPACE"; + })(Z || (Z = {})); + var Q = function(b) { + function d(b, g, e) { + var f, k; + 0 === arguments.length ? f = "" : 1 === arguments.length ? f = b : (k = b, f = g); + if (c.isObject(f) && f instanceof d) { if (2 > arguments.length) { - return e; + return f; } - e = e.localName; + f = f.localName; } - e = void 0 === e || 0 === arguments.length ? "" : t(e); - if (void 0 === l || 2 > arguments.length) { - l = "*" === e ? null : new z("", Q.defaultNamespace); - } - var n = e; - null !== l && (l = l instanceof z ? l : new z(l)); - var r = d ? 1 : 2; - "*" === e && (r |= 4); - null === l && (r |= 8); - this._mn = new G([l ? l._ns : null], n); - this._flags = r; - } - __extends(c, a); - c.fromMultiname = function(a) { - var b = Object.create(c.prototype); - b._mn = a; - var h = 0, h = a.isAttribute() ? h | 1 : h | 2; - a.isAnyName() && (h |= 4); - a.isAnyNamespace() && (h |= 8); - b._flags = h; - return b; + f = void 0 === f || 0 === arguments.length ? "" : v(f); + if (void 0 === k || 2 > arguments.length) { + k = "*" === f ? null : new a.ASNamespace("", P.defaultNamespace); + } + var r = f; + null !== k && (k = k instanceof G ? k : new a.ASNamespace(k)); + var m = e ? 1 : 2; + "*" === f && (m |= 4); + null === k && (m |= 8); + this.name = new A([k ? k._ns : null], r, m); + } + __extends(d, b); + d.fromMultiname = function(b) { + var a = Object.create(d.prototype); + a.name = b; + return a; + }; + d.prototype.equals = function(b) { + return b instanceof d && b.uri === this.uri && b.name.name === this.name.name || "string" === typeof b && this.toString() === b; }; - Object.defineProperty(c.prototype, "localName", {get:function() { - return this._mn.name; + Object.defineProperty(d.prototype, "localName", {get:function() { + return this.name.name; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "uri", {get:function() { - return this._mn.namespaces[0] ? this._mn.namespaces[0].uri : null; + Object.defineProperty(d.prototype, "uri", {get:function() { + return this.name.namespaces[0] ? this.name.namespaces[0].uri : null; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "prefix", {get:function() { - return this._mn.namespaces[0].prefix; + d.prototype.setUri = function() { + this.name.namespaces[0] || this.name.namespaces.push(Namespace.createNamespace("")); + this.name.namespaces[0].uri = ""; + }; + d.prototype.toString = function() { + var b = this.uri; + return b ? b + "::" + this.name.name : this.name.name; + }; + Object.defineProperty(d.prototype, "prefix", {get:function() { + return this.name.namespaces[0] ? this.name.namespaces[0].prefix : null; }, enumerable:!0, configurable:!0}); - c.prototype.getNamespace = function(a) { + d.prototype.getNamespace = function(b) { if (null === this.uri) { throw "TypeError in QName.prototype.getNamespace()"; } - a || (a = []); - for (var b, c = 0;c < a.length;c++) { - this.uri === a[c].uri && (b = a[c]); + b || (b = []); + for (var d, g = 0;g < b.length;g++) { + this.uri === b[g].uri && (d = b[g]); } - b || (b = new z(this.prefix, this.uri)); - return b; + d || (d = new a.ASNamespace(this.prefix, this.uri)); + return d; }; - c.instanceConstructor = c; - c.callableConstructor = function(a, h) { - if (1 === arguments.length && b.isObject(a) && a instanceof c) { - return a; + Object.defineProperty(d.prototype, "flags", {get:function() { + return this.name.flags; + }, enumerable:!0, configurable:!0}); + d.instanceConstructor = d; + d.callableConstructor = function(b, g) { + if (1 === arguments.length && c.isObject(b) && b instanceof d) { + return b; } switch(arguments.length) { case 0: - return new c; + return new a.ASQName; case 1: - return new c(a); + return new a.ASQName(b); default: - return new c(a, h); + return new a.ASQName(b, g); } }; - return c; - }(g.ASNative); - g.ASQName = D; - var L; - (function(a) { - a[a.FLAG_IGNORE_COMMENTS = 1] = "FLAG_IGNORE_COMMENTS"; - a[a.FLAG_IGNORE_PROCESSING_INSTRUCTIONS = 2] = "FLAG_IGNORE_PROCESSING_INSTRUCTIONS"; - a[a.FLAG_IGNORE_WHITESPACE = 4] = "FLAG_IGNORE_WHITESPACE"; - a[a.FLAG_PRETTY_PRINTING = 8] = "FLAG_PRETTY_PRINTING"; - a[a.ALL = a.FLAG_IGNORE_COMMENTS | a.FLAG_IGNORE_PROCESSING_INSTRUCTIONS | a.FLAG_IGNORE_WHITESPACE | a.FLAG_PRETTY_PRINTING] = "ALL"; - })(L || (L = {})); - var M; - (function(a) { - a[a.Unknown = 0] = "Unknown"; - a[a.Element = 1] = "Element"; - a[a.Attribute = 2] = "Attribute"; - a[a.Text = 3] = "Text"; - a[a.Comment = 4] = "Comment"; - a[a.ProcessingInstruction = 5] = "ProcessingInstruction"; - })(M || (M = {})); - var V = [null, "element", "attribute", "text", "comment", "processing-instruction"], Q = function(a) { - function c(a) { - "undefined" === typeof a && (a = void 0); - if (!(this instanceof c)) { - return a instanceof c ? a : new c(a); - } - if (null === a || void 0 === a) { - a = ""; + return d; + }(a.ASNative); + a.ASQName = Q; + var S; + (function(b) { + b[b.FLAG_IGNORE_COMMENTS = 1] = "FLAG_IGNORE_COMMENTS"; + b[b.FLAG_IGNORE_PROCESSING_INSTRUCTIONS = 2] = "FLAG_IGNORE_PROCESSING_INSTRUCTIONS"; + b[b.FLAG_IGNORE_WHITESPACE = 4] = "FLAG_IGNORE_WHITESPACE"; + b[b.FLAG_PRETTY_PRINTING = 8] = "FLAG_PRETTY_PRINTING"; + b[b.ALL = b.FLAG_IGNORE_COMMENTS | b.FLAG_IGNORE_PROCESSING_INSTRUCTIONS | b.FLAG_IGNORE_WHITESPACE | b.FLAG_PRETTY_PRINTING] = "ALL"; + })(S || (S = {})); + (function(b) { + b[b.Unknown = 0] = "Unknown"; + b[b.Element = 1] = "Element"; + b[b.Attribute = 2] = "Attribute"; + b[b.Text = 3] = "Text"; + b[b.Comment = 4] = "Comment"; + b[b.ProcessingInstruction = 5] = "ProcessingInstruction"; + })(a.ASXMLKind || (a.ASXMLKind = {})); + var O = [null, "element", "attribute", "text", "comment", "processing-instruction"], P = function(e) { + function f(b) { + this._parent = null; + c.isNullOrUndefined(b) && (b = ""); + if ("string" === typeof b && 0 === b.length) { + this._kind = 3, this._value = ""; + } else { + var a = n(b); + s(b) && (a = a._deepCopy()); + return a; } - var b = e(a); - f(a) && (b = b._deepCopy()); - return b; } - __extends(c, a); - c.prototype.init = function(a, b, c, h) { - b = b || h ? new z(h, b) : void 0; - this._name = new D(b, c, 2 === a); - this._kind = a; + __extends(f, e); + f.native_settings = function() { + return{$BgignoreComments:f.ignoreComments, $BgignoreProcessingInstructions:f.ignoreProcessingInstructions, $BgignoreWhitespace:f.ignoreWhitespace, $BgprettyPrinting:f.prettyPrinting, $BgprettyIndent:f.prettyIndent}; + }; + f.native_setSettings = function(b) { + c.isNullOrUndefined(b) ? (f.ignoreComments = !0, f.ignoreProcessingInstructions = !0, f.ignoreWhitespace = !0, f.prettyPrinting = !0, f.prettyIndent = 2) : ("boolean" === typeof b.$BgignoreComments && (f.ignoreComments = b.$BgignoreComments), "boolean" === typeof b.$BgignoreProcessingInstructions && (f.ignoreProcessingInstructions = b.$BgignoreProcessingInstructions), "boolean" === typeof b.$BgignoreWhitespace && (f.ignoreWhitespace = b.$BgignoreWhitespace), "boolean" === b.$BgprettyPrinting && + (f.prettyPrinting = b.$BgprettyPrinting), "number" === b.$BgprettyIndent && (f.prettyIndent = b.$BgprettyIndent)); + }; + f.native_defaultSettings = function() { + return{$BgignoreComments:!0, $BgignoreProcessingInstructions:!0, $BgignoreWhitespace:!0, $BgprettyPrinting:!0, $BgprettyIndent:2}; + }; + f.prototype.valueOf = function() { + return this; + }; + f.prototype.equals = function(b) { + return b instanceof V ? b.equals(this) : b instanceof f ? (3 === this._kind || 2 === this._kind) && b.hasSimpleContent() || (3 === b._kind || 2 === b._kind) && this.hasSimpleContent() ? this.toString() === b.toString() : this._deepEquals(b) : this.hasSimpleContent() && this.toString() === M(b); + }; + f.prototype.init = function(b, d, g, f) { + d = d || f ? new a.ASNamespace(f, d) : void 0; + this._name = new a.ASQName(d, g, 2 === b); + this._kind = b; this._parent = null; - switch(a) { + switch(b) { case 1: this._inScopeNamespaces = []; this._attributes = []; @@ -13124,28 +13548,58 @@ } return this; }; - c.prototype.length = function() { - return this._children ? this._children.length : 0; + f.prototype._deepEquals = function(b) { + if (!(b instanceof f) || this._kind !== b._kind || !!this._name !== !!b._name || this._name && !this._name.equals(b._name)) { + return!1; + } + if (1 !== this._kind) { + return this._value !== b._value ? !1 : !0; + } + var a = this._attributes, d = b._attributes; + if (a.length !== d.length) { + return!1; + } + var g = this._children; + b = b._children; + if (g.length !== b.length) { + return!1; + } + var e = 0; + a: for (;e < a.length;e++) { + for (var k = a[e], r = 0;r < d.length;r++) { + var c = d[r]; + if (c._name.equals(k._name) && c._value === k._value) { + continue a; + } + } + return!1; + } + for (e = 0;e < g.length;e++) { + if (!g[e].equals(b[e])) { + return!1; + } + } + return!0; }; - c.prototype._deepCopy = function() { - var a = this._kind, b = new c; - b._kind = a; - b._name = this._name; - switch(a) { + f.prototype._deepCopy = function() { + var b = this._kind, d = new a.ASXML; + d._kind = b; + d._name = this._name; + switch(b) { case 1: - b._inScopeNamespaces = []; - 0 < this._inScopeNamespaces.length && this._inScopeNamespaces.forEach(function(a) { - b._inScopeNamespaces.push(new z(a.prefix, a.uri)); + d._inScopeNamespaces = []; + 0 < this._inScopeNamespaces.length && this._inScopeNamespaces.forEach(function(b) { + d._inScopeNamespaces.push(new a.ASNamespace(b.prefix, b.uri)); }); - b._attributes = this._attributes.map(function(a) { - a = a._deepCopy(); - a._parent = b; - return a; + d._attributes = this._attributes.map(function(b) { + b = b._deepCopy(); + b._parent = d; + return b; }); - b._children = this._children.map(function(a) { - a = a._deepCopy(); - a._parent = b; - return a; + d._children = this._children.map(function(b) { + b = b._deepCopy(); + b._parent = d; + return b; }); break; case 4: @@ -13155,1256 +13609,1800 @@ case 2: ; case 3: - b._value = this._value; + d._value = this._value; } - return b; + return d; }; - c.prototype.resolveValue = function() { + f.prototype.resolveValue = function() { return this; }; - c.prototype._addInScopeNamespaces = function(a) { - this._inScopeNamespaces.some(function(b) { - return b.uri === a.uri && b.prefix === a.prefix; - }) || this._inScopeNamespaces.push(a); + f.prototype._addInScopeNamespaces = function(b) { + this._inScopeNamespaces.some(function(a) { + return a.uri === b.uri && a.prefix === b.prefix; + }) || this._inScopeNamespaces.push(b); }; - Object.defineProperty(c, "ignoreComments", {get:function() { - return!!(c._flags & 1); - }, set:function(a) { - c._flags = a ? c._flags | 1 : c._flags & -2; + Object.defineProperty(f, "ignoreComments", {get:function() { + return!!(f._flags & 1); + }, set:function(b) { + f._flags = b ? f._flags | 1 : f._flags & -2; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c, "ignoreProcessingInstructions", {get:function() { - return!!(c._flags & 2); - }, set:function(a) { - c._flags = a ? c._flags | 2 : c._flags & -3; + Object.defineProperty(f, "ignoreProcessingInstructions", {get:function() { + return!!(f._flags & 2); + }, set:function(b) { + f._flags = b ? f._flags | 2 : f._flags & -3; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c, "ignoreWhitespace", {get:function() { - return!!(c._flags & 4); - }, set:function(a) { - c._flags = a ? c._flags | 4 : c._flags & -5; + Object.defineProperty(f, "ignoreWhitespace", {get:function() { + return!!(f._flags & 4); + }, set:function(b) { + f._flags = b ? f._flags | 4 : f._flags & -5; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c, "prettyPrinting", {get:function() { - return!!(c._flags & 8); - }, set:function(a) { - c._flags = a ? c._flags | 8 : c._flags & -9; + Object.defineProperty(f, "prettyPrinting", {get:function() { + return!!(f._flags & 8); + }, set:function(b) { + f._flags = b ? f._flags | 8 : f._flags & -9; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c, "prettyIndent", {get:function() { - return c._prettyIndent; - }, set:function(a) { - c._prettyIndent = a | 0; + Object.defineProperty(f, "prettyIndent", {get:function() { + return f._prettyIndent; + }, set:function(b) { + f._prettyIndent = b | 0; }, enumerable:!0, configurable:!0}); - c.prototype.toString = function() { - return t(this); - }; - c.prototype.native_hasOwnProperty = function(a) { - "undefined" === typeof a && (a = void 0); - return this.hasProperty(a, w(a), !1) ? !0 : F.call(this, String(a)); + f.prototype.toString = function() { + return f.isTraitsOrDynamicPrototype(this) ? "" : this.hasComplexContent() ? this.toXMLString() : v(this); }; - c.prototype.native_propertyIsEnumerable = function(a) { - "undefined" === typeof a && (a = void 0); - return "0" === String(a); - }; - c.prototype.addNamespace = function(a) { - this._addInScopeNamespaces(new z(a)); + f.prototype.native_hasOwnProperty = function(b) { + if (f.isTraitsOrDynamicPrototype(this)) { + return a.ASObject.prototype.native_hasOwnProperty.call(this, b); + } + var g = d(b); + return this.hasProperty(g, !!(g.flags & 1), !1) ? !0 : J.call(this, String(b)); + }; + f.prototype.native_propertyIsEnumerable = function(b) { + void 0 === b && (b = void 0); + return "0" === String(b); + }; + f.prototype.addNamespace = function(b) { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + this._addInScopeNamespaces(new a.ASNamespace(b)); return this; }; - c.prototype.appendChild = function(a) { - if (a._parent) { - var b = a._parent._children.indexOf(a); - y(0 <= b); - a._parent._children.splice(b, 1); + f.prototype.appendChild = function(b) { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + if (b._parent) { + var a = b._parent._children.indexOf(b); + z(0 <= a); + b._parent._children.splice(a, 1); } - this._children.push(a); - a._parent = this; + this._children.push(b); + b._parent = this; return this; }; - c.prototype.attribute = function(a) { - return this.getProperty(a, !0, !1); - }; - c.prototype.attributes = function() { - var a = new x; - Array.prototype.push.apply(a._children, this._attributes); - return a; + f.prototype.attribute = function(b) { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + return this.getProperty(b, !0); + }; + f.prototype.attributes = function() { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + var b = a.ASXMLList.createList(this, this._name); + Array.prototype.push.apply(b._children, this._attributes); + return b; }; - c.prototype.child = function(a) { - return this.getProperty(a, w(a), !1); + f.prototype.child = function(d) { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + if (c.isIndex(d)) { + var g = a.ASXMLList.createList(); + this._children && d < this._children.length && g.append(this._children[d | 0]); + return g; + } + return this.getProperty(d, b(d)); }; - c.prototype.childIndex = function() { + f.prototype.childIndex = function() { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); return this._parent && 2 !== this._kind ? this._parent._children.indexOf(this) : -1; }; - c.prototype.children = function() { - var a = new x(this); - Array.prototype.push.apply(a._children, this._children); - return a; + f.prototype.children = function() { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + var b = a.ASXMLList.createList(this, this._name); + Array.prototype.push.apply(b._children, this._children); + return b; }; - c.prototype.contains = function(a) { - return this === a; + f.prototype.comments = function() { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + var b = a.ASXMLList.createList(this, this._name); + this._children && this._children.forEach(function(a, d) { + 4 === a._kind && b.append(a); + }); + return b; }; - c.prototype.copy = function() { + f.prototype.contains = function(b) { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + return this === b; + }; + f.prototype.copy = function() { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); return this._deepCopy(); }; - c.prototype.elements = function(a) { - "undefined" === typeof a && (a = "*"); - return this.getProperty(a, !1, !1); - }; - c.prototype.hasComplexContent = function() { - return 2 === this._kind || 4 === this._kind || 5 === this._kind || 3 === this._kind ? !1 : this._children.some(function(a) { - return 1 === a._kind; + f.prototype.descendants = function(b) { + void 0 === b && (b = "*"); + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + var g = a.ASXMLList.createList(this, this._name); + b = d(b); + return this.descendantsInto(b, g); + }; + f.prototype.elements = function(b) { + void 0 === b && (b = "*"); + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + return this.getProperty(b, !1); + }; + f.prototype.hasComplexContent = function() { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + return 2 === this._kind || 4 === this._kind || 5 === this._kind || 3 === this._kind ? !1 : this._children.some(function(b) { + return 1 === b._kind; }); }; - c.prototype.hasSimpleContent = function() { - return 4 === this._kind || 5 === this._kind ? !1 : this._children || 0 !== this._children.length ? this._children.every(function(a) { - return 1 !== a._kind; - }) : !0; + f.prototype.hasSimpleContent = function() { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + return 4 === this._kind || 5 === this._kind ? !1 : 1 !== this._kind || !this._children && 0 === this._children.length ? !0 : this._children.every(function(b) { + return 1 !== b._kind; + }); }; - c.prototype.inScopeNamespaces = function() { - I("public.XML::inScopeNamespaces"); + f.prototype.inScopeNamespaces = function() { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + return this._inScopeNamespacesImpl(!1); + }; + f.prototype._inScopeNamespacesImpl = function(b) { + var a = this, d = []; + for (b = b ? d : {};null !== a;) { + for (var g = a._inScopeNamespaces, f = 0;g && f < g.length;f++) { + var e = g[f]; + b[e.prefix] || (b[e.prefix] = e, d.push(e)); + } + a = a._parent; + } + return d; }; - c.prototype.insertChildAfter = function(a, b) { - I("public.XML::insertChildAfter"); + f.prototype.insertChildAfter = function(b, a) { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + B("public.XML::insertChildAfter"); + }; + f.prototype.insertChildBefore = function(b, a) { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + B("public.XML::insertChildBefore"); }; - c.prototype.insertChildBefore = function(a, b) { - I("public.XML::insertChildBefore"); + f.prototype.length = function() { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + return this._children ? this._children.length : 0; }; - c.prototype.localName = function() { + f.prototype.localName = function() { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); return this._name.localName; }; - c.prototype.name = function() { + f.prototype.name = function() { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); return this._name; }; - c.prototype._namespace = function(a, b) { - I("public.XML::private _namespace"); - }; - c.prototype.namespaceDeclarations = function() { - I("public.XML::namespaceDeclarations"); - }; - c.prototype.nodeKind = function() { - return V[this._kind]; + f.prototype.namespace = function(b) { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + if (0 === arguments.length && 3 <= this._kind) { + return null; + } + var a = this._inScopeNamespacesImpl(!0); + if (0 === arguments.length) { + return this._name.getNamespace(a); + } + b = M(b); + for (var d = 0;d < a.length;d++) { + var g = a[d]; + if (g.prefix === b) { + return g; + } + } }; - c.prototype.normalize = function() { - I("public.XML::normalize"); + f.prototype.namespaceDeclarations = function() { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + B("public.XML::namespaceDeclarations"); + }; + f.prototype.nodeKind = function() { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + return O[this._kind]; + }; + f.prototype.normalize = function() { + this instanceof f || h.Runtime.throwError("TypeError", h.Errors.CheckTypeFailedError, this, "XML"); + for (var b = 0;b < this._children.length;) { + var a = this._children[b]; + if (1 === a._kind) { + a.normalize(), b++; + } else { + if (3 === a._kind) { + for (b++;b < this._children.length;) { + var d = this._children[b]; + if (3 !== d._kind) { + break; + } + a._value += d._value; + this.removeByIndex(b); + } + 0 === a._value.length ? this.removeByIndex(b) : b++; + } else { + b++; + } + } + } + return this; }; - c.prototype.parent = function() { - return this._parent; + f.prototype.removeByIndex = function(b) { + this._children[b]._parent = null; + this._children.splice(b, 1); + }; + f.prototype.parent = function() { + return this._parent || void 0; + }; + f.prototype.processingInstructions = function(b) { + void 0 === b && (b = "*"); + var g = d(b).localName, f = a.ASXMLList.createList(this, this._name); + f._targetObject = this; + f._targetProperty = null; + return this.processingInstructionsInto(b, g, f); + }; + f.prototype.processingInstructionsInto = function(b, a, d) { + var g = this._children; + if (!g) { + return d; + } + for (var f = 0;f < g.length;f++) { + var e = g[f]; + 5 !== e._kind || "*" !== b && e._name.localName !== a || d._children.push(e); + } + return d; }; - c.prototype.processingInstructions = function(a) { - I("public.XML::processingInstructions"); + f.prototype.prependChild = function(b) { + B("public.XML::prependChild"); }; - c.prototype.prependChild = function(a) { - I("public.XML::prependChild"); + f.prototype.removeNamespace = function(b) { + B("public.XML::removeNamespace"); }; - c.prototype.removeNamespace = function(a) { - I("public.XML::removeNamespace"); + f.prototype.replace = function(b, a) { + var d, g = this; + if (3 === g._kind || 4 === g._kind || 5 === g._kind || 2 === g._kind) { + return g; + } + if (1 === a._kind) { + for (d = g;d;) { + if (d === a) { + throw "Error in XML.prototype.replace()"; + } + d = d._parent; + } + } + d = b >>> 0; + if (String(b) === String(d)) { + d >= g.length() && (b = String(g.length())), g._children && g._children[b] && (g._children[b]._parent = null); + } else { + d = this.getProperty(b, !1); + if (0 === d.length()) { + return g; + } + g._children && d._children.forEach(function(a, d) { + var f = g._children.indexOf(a); + a._parent = null; + 0 === d ? (b = String(f), g._children.splice(f, 1, void 0)) : g._children.splice(f, 1); + }); + } + if (1 === a._kind || 3 === a._kind || 4 === a._kind || 5 === a._kind) { + a._parent = g, g._children || (g._children = []), g._children[b] = a; + } else { + d = v(a); + var f = w(); + f._parent = g; + f._value = d; + g._children || (g._children = []); + g._children[b] = f; + } + return g; }; - c.prototype.setChildren = function(a) { - I("public.XML::setChildren"); + f.prototype.setChildren = function(b) { + this.setProperty("*", !1, b); + return this; }; - c.prototype.setLocalName = function(a) { - I("public.XML::setLocalName"); + f.prototype.setLocalName = function(b) { + B("public.XML::setLocalName"); }; - c.prototype.setName = function(a) { - I("public.XML::setName"); + f.prototype.setName = function(b) { + if (3 !== this._kind && 4 !== this._kind) { + b instanceof a.ASQName && null === b.uri && (b = b.localName); + b = new Q(b); + 5 === this._kind && b.setUri(); + this._name = b; + var d = this; + if (2 === this._kind) { + if (null === this._parent) { + return; + } + d = this._parent; + } + d.addInScopeNamespace(new a.ASNamespace(b.uri)); + } }; - c.prototype.setNamespace = function(a) { - I("public.XML::setNamespace"); + f.prototype.setNamespace = function(b) { + B("public.XML::setNamespace"); }; - c.prototype.toXMLString = function() { - return p(this); + f.prototype.text = function() { + var b = a.ASXMLList.createList(this, this._name); + this._children && this._children.forEach(function(a, d) { + 3 === a._kind && b.append(a); + }); + return b; }; - c.prototype.notification = function() { - I("public.XML::notification"); + f.prototype.toXMLString = function() { + return q(this); }; - c.prototype.setNotification = function(a) { - I("public.XML::setNotification"); + f.prototype.toJSON = function(b) { + return "XML"; }; - c.isTraitsOrDynamicPrototype = function(a) { - return a === c.traitsPrototype || a === c.dynamicPrototype; + f.isTraitsOrDynamicPrototype = function(b) { + return b === f.traitsPrototype || b === f.dynamicPrototype; }; - c.prototype.asGetEnumerableKeys = function() { - if (c.isTraitsOrDynamicPrototype(this)) { - return J.call(this); - } - var a = []; - this._children.forEach(function(b, c) { - a.push(b.name); + f.prototype.asGetEnumerableKeys = function() { + if (f.isTraitsOrDynamicPrototype(this)) { + return E.call(this); + } + var b = []; + this._children.forEach(function(a, d) { + b.push(a.name); }); - return a; + return b; }; - c.prototype.setProperty = function(a, b, c) { - var d, e; - if (a === a >>> 0) { - throw "TypeError in XML.prototype.setProperty(): invalid property name " + a; - } + f.prototype.setProperty = function(b, g, f) { + b === b >>> 0 && h.Runtime.throwError("TypeError", h.Errors.XMLAssignmentToIndexedXMLNotAllowed); if (3 !== this._kind && 4 !== this._kind && 5 !== this._kind && 2 !== this._kind) { - if (d = c && f(c) && 3 !== c._kind && 2 !== c._kind ? c._deepCopy() : t(c), e = u(a), b) { - this._attributes && (this._attributes.forEach(function(a, b, c) { - a.name === e.localName && delete c[b]; - }), d = new h(2, e.uri, e.localName), d._value = c, d._parent = this, this._attributes.push(d)); + if (f = s(f) && 3 !== f._kind && 2 !== f._kind ? f._deepCopy() : v(f), b = d(b), g) { + if (r(b.name)) { + if (f instanceof a.ASXMLList) { + if (0 === f._children.length) { + f = ""; + } else { + g = v(f._children[0]); + for (var e = 1;e < f._children.length;e++) { + g += " " + v(f._children[e]); + } + f = g; + } + } else { + f = M(f); + } + g = null; + for (var k = this._attributes, c = this._attributes = [], e = 0;k && e < k.length;e++) { + var m = k[e]; + if (m._name.equals(b)) { + if (g) { + m._parent = null; + continue; + } else { + g = m; + } + } + c.push(m); + } + g || (g = w(2, b.uri, b.localName), g._parent = this, c.push(g)); + g._value = f; + } } else { - c = void 0; - a = !f(d) && "*" !== e.localName; - b = e._flags & 4; - for (var l = e._flags & 8, n = this.length() - 1;0 <= n;n--) { - (b || 1 === this._children[n]._kind && this._children[n]._name.localName === e.localName) && (l || 1 === this._children[n]._kind && this._children[n]._name.uri === e.uri) && (void 0 !== c && this.deleteByIndex(String(c)), c = n); - } - void 0 === c && (c = this.length(), a && (l = null === e.uri ? new D(new z("", Q.defaultNamespace), e) : new D(e), b = new h(1, l.uri, l.localName, l.prefix), b._parent = this, l = l.getNamespace(), this.replace(String(c), b), b.addInScopeNamespace(l))); - a ? (this._children[c]._children = [], d = t(d), "" !== d && this._children[c].replace("0", d)) : this.replace(String(c), d); + g = !s(f) && "*" !== b.localName; + k = b.flags & 4; + c = b.flags & 8; + for (m = this.length() - 1;0 <= m;m--) { + (k || 1 === this._children[m]._kind && this._children[m]._name.localName === b.localName) && (c || 1 === this._children[m]._kind && this._children[m]._name.uri === b.uri) && (void 0 !== e && this.deleteByIndex(e), e = m); + } + void 0 === e && (e = this.length(), g && (k = null === b.uri ? new a.ASQName(new a.ASNamespace("", P.defaultNamespace), b) : new a.ASQName(b), b = w(1, k.uri, k.localName, k.prefix), b._parent = this, k = k.getNamespace(), this.replace(String(e), b), b.addInScopeNamespace(k))); + g ? (this._children[e]._children = [], g = v(f), "" !== g && this._children[e].replace("0", g)) : this.replace(String(e), f); } } }; - c.prototype.asSetProperty = function(a, b, h, d) { - if (c.isTraitsOrDynamicPrototype(this)) { - return E.call(this, a, b, h, d); - } - h &= G.ATTRIBUTE; - this.setProperty(r(a, b, h), h, d); - }; - c.prototype.getProperty = function(a, c, h) { - if (h) { - return c = G.isQName(a) ? a : this.resolveMultinameProperty(a.namespaces, a.name, a.flags), this[G.getQualifiedName(c)]; - } - if (!G.isQName(a) && b.isNumeric(a)) { - return 0 === Number(a) ? this : null; - } - var d = u(a), e = new x(this, d); - a = d._flags; - var l = a & 4, n = a & 8; - c ? this._attributes && this._attributes.forEach(function(a, b) { - !l && a._name.localName !== d.localName || !n && a._name.uri !== d.uri || e.appendChild(a); - }) : this._children.forEach(function(a, b) { - (l || 1 === a._kind && a._name.localName === d.localName) && (n || 1 === a._kind && a._name.uri === d.uri) && e.appendChild(a); - }); + f.prototype.asSetProperty = function(b, a, d, e) { + if (f.isTraitsOrDynamicPrototype(this)) { + return D.call(this, b, a, d, e); + } + d = !!(d & A.ATTRIBUTE); + this.setProperty(g(b, a, d), d, e); + }; + f.prototype.getProperty = function(b, g) { + if (c.isIndex(b) || !A.isQName(b) && c.isNumeric(b)) { + return 0 === (b | 0) ? this : null; + } + var f = d(b), e = a.ASXMLList.createList(this, this._name); + e._targetObject = this; + e._targetProperty = f; + var k = 0, r = f.name.flags, m = r & 4, w = r & 8; + if (g || r & 1) { + for (r = 0;this._attributes && r < this._attributes.length;r++) { + var l = this._attributes[r]; + !m && l._name.localName !== f.localName || !w && l._name.uri !== f.uri || (e._children[k++] = l); + } + return e; + } + for (r = 0;this._children && r < this._children.length;r++) { + l = this._children[r], (m || 1 === l._kind && l._name.localName === f.localName) && (w || 1 === l._kind && l._name.uri === f.uri) && (e._children[k++] = l); + } return e; }; - c.prototype.asGetNumericProperty = function(a) { - return this.asGetProperty(null, a, 0); + f.prototype.asGetNumericProperty = function(b) { + return this.asGetProperty(null, b, 0); }; - c.prototype.asSetNumericProperty = function(a, b) { - this.asSetProperty(null, a, 0, b); + f.prototype.asSetNumericProperty = function(b, a) { + this.asSetProperty(null, b, 0, a); }; - c.prototype.asGetProperty = function(a, b, h) { - if (c.isTraitsOrDynamicPrototype(this)) { - return C.call(this, a, b, h); - } - h &= G.ATTRIBUTE; - return this.getProperty(r(a, b, h), h, !1); - }; - c.prototype.hasProperty = function(a, c, h) { - if (h) { - return c = G.isQName(a) ? a : this.resolveMultinameProperty(a.namespaces, a.name, a.flags), !!this[G.getQualifiedName(c)]; - } - new x; - if (b.isIndex(a)) { - return 0 === Number(a) ? !0 : !1; - } - var d = u(a); - a = d._flags; - var e = a & 4, l = a & 8; - if (c) { - if (this._attributes) { - return this._attributes.some(function(a, b) { - return(e || a._name.localName === d.localName) && (l || a._name.uri === d.uri); - }); + f.prototype.asGetProperty = function(b, a, d) { + if (f.isTraitsOrDynamicPrototype(this)) { + return y.call(this, b, a, d); + } + d = !!(d & A.ATTRIBUTE); + return this.getProperty(g(b, a, d), d); + }; + f.prototype.hasProperty = function(b, a, g) { + if (g) { + return a = A.isQName(b) ? b : this.resolveMultinameProperty(b.namespaces, b.name, b.flags), !!this[A.getQualifiedName(a)]; + } + if (c.isIndex(b)) { + return 0 === Number(b); + } + b = d(b); + var f = b.name.flags; + g = f & 4; + f &= 8; + if (a) { + for (a = 0;this._attributes && a < this._attributes.length;a++) { + var e = this._attributes[a]; + if (g || e._name.localName === b.localName && (f || e._name.uri === b.uri)) { + return!0; + } + } + return!1; + } + for (a = 0;a < this._children.length;a++) { + if (e = this._children[a], (g || 1 === e._kind && e._name.localName === b.localName) && (f || 1 === e._kind && e._name.uri === b.uri)) { + return!0; + } + } + }; + f.prototype.deleteProperty = function(b, a) { + if (c.isIndex(b)) { + return!0; + } + var g = d(b), f = g.localName, e = g.uri, k = g.name.flags, r = k & 4, m = k & 8; + if (a) { + if (k = this._attributes) { + for (var w = this._attributes = [], l = 0;l < k.length;l++) { + var n = k[l], q = n._name; + !r && q.localName !== f || !m && q.uri !== e ? w.push(n) : n._parent = null; + } } } else { - if (this._children.some(function(a, b) { - return(e || 1 === a._kind && a._name.localName === d.localName) && (l || 1 === a._kind && a._name.uri === d.uri); + if (this._children.some(function(b, a) { + return(r || 1 === b._kind && b._name.localName === g.localName) && (m || 1 === b._kind && b._name.uri === g.uri); })) { return!0; } } }; - c.prototype.asHasProperty = function(a, b, h) { - if (c.isTraitsOrDynamicPrototype(this)) { - return K.call(this, a, b, h); - } - var d = h & G.ATTRIBUTE; - b = r(a, b, d); - if (this.hasProperty(b, d, !1)) { + f.prototype.asHasProperty = function(b, a, d) { + if (f.isTraitsOrDynamicPrototype(this)) { + return H.call(this, b, a, d); + } + var e = !!(d & A.ATTRIBUTE); + a = g(b, a, e); + if (this.hasProperty(a, e, !1)) { return!0; } - a = G.isQName(b) ? b : this.resolveMultinameProperty(a, b, h); - return!!this[G.getQualifiedName(a)]; - }; - c.prototype.asHasPropertyInternal = function(a, b, c) { - return this.asHasProperty(a, b, c); + b = A.isQName(a) ? a : this.resolveMultinameProperty(b, a, d); + return!!this[A.getQualifiedName(b)]; }; - c.prototype.asCallProperty = function(a, b, h, d, e) { - if (c.isTraitsOrDynamicPrototype(this) || d) { - return O.call(this, a, b, h, d, e); + f.prototype._asDeleteProperty = function(b, a, d) { + if (f.isTraitsOrDynamicPrototype(this)) { + return C.call(this, b, a, d); + } + var e = !!(d & A.ATTRIBUTE); + a = g(b, a, e); + if (this.deleteProperty(a, e)) { + return!0; } - var l; - l = this.resolveMultinameProperty(a, b, h); - if (this.asGetNumericProperty && G.isNumeric(l)) { - l = this.asGetNumericProperty(l); + b = A.isQName(a) ? a : this.resolveMultinameProperty(b, a, d); + return delete this[A.getQualifiedName(b)]; + }; + f.prototype.asHasPropertyInternal = function(b, a, d) { + return this.asHasProperty(b, a, d); + }; + f.prototype.asCallProperty = function(b, a, d, g, e) { + if (f.isTraitsOrDynamicPrototype(this) || g) { + return L.call(this, b, a, d, g, e); + } + var k; + k = this.resolveMultinameProperty(b, a, d); + if (this.asGetNumericProperty && A.isNumeric(k)) { + k = this.asGetNumericProperty(k); } else { - var n = this.asOpenMethods; - l = n && n[l] || this[l]; + var r = this.asOpenMethods; + k = r && r[k] || this[k]; } - if (l) { - return O.call(this, a, b, h, d, e); + if (k) { + return k.asApply(g ? null : this, e); } if (this.hasSimpleContent()) { - return Object(t(this)).asCallProperty(a, b, h, d, e); + return Object(v(this)).asCallProperty(b, a, d, g, e); } throw new TypeError; }; - c.prototype._delete = function(a, b) { - I("XML.[[Delete]]"); + f.prototype._delete = function(b, a) { + B("XML.[[Delete]]"); }; - c.prototype.deleteByIndex = function(a) { - var b = a >>> 0; - if (String(b) !== String(a)) { - throw "TypeError in XML.prototype.deleteByIndex(): invalid index " + a; - } - if (a < this.length() && this.children[a]) { - this.children[a]._parent = null; - delete this.children[a]; - for (a = b + 1;a < this.length();a++) { - this.children[a - 1] = this.children[a]; - } - this.children.length -= 1; + f.prototype.deleteByIndex = function(b) { + if (String(b >>> 0) !== String(b)) { + throw "TypeError in XML.prototype.deleteByIndex(): invalid index " + b; } + var a = this._children; + b < a.length && a[b] && (a[b]._parent = null, a.splice(b, 1)); }; - c.prototype.insert = function(a, b) { - var c, h; + f.prototype.insert = function(b, a) { + var d, g; if (3 !== this._kind && 4 !== this._kind && 5 !== this._kind && 2 !== this._kind) { - c = a >>> 0; - if (String(a) !== String(c)) { - throw "TypeError in XML.prototype.insert(): invalid property name " + a; + d = b >>> 0; + if (String(b) !== String(d)) { + throw "TypeError in XML.prototype.insert(): invalid property name " + b; } if (1 === this._kind) { - for (h = this;h;) { - if (h === b) { + for (g = this;g;) { + if (g === a) { throw "Error in XML.prototype.insert()"; } - h = h._parent; + g = g._parent; } } - if (this instanceof U) { - if (h = this.length(), 0 === h) { + if (this instanceof V) { + if (g = this.length(), 0 === g) { return; } } else { - h = 1; + g = 1; } - for (var d = this.length() - 1;d >= c;d--) { - this._children[d + h] = this._children[d]; + for (var f = this.length() - 1;f >= d;f--) { + this._children[f + g] = this._children[f]; } - if (this instanceof U) { - for (h = b.length(), d = 0;d < h;d++) { - b._children[d]._parent = this, this[c + d] = b[d]; + if (this instanceof V) { + for (g = a.length(), f = 0;f < g;f++) { + a._children[f]._parent = this, this[d + f] = a[f]; } } else { - b._parent = this, this._children[c] = b; - } - } - }; - c.prototype.replace = function(a, b) { - var c, d = this; - if (3 === d._kind || 4 === d._kind || 5 === d._kind || 2 === d._kind) { - return d; - } - if (1 === b._kind) { - for (c = d;c;) { - if (c === b) { - throw "Error in XML.prototype.replace()"; - } - c = c._parent; - } - } - c = a >>> 0; - if (String(a) === String(c)) { - c >= d.length() && (a = String(d.length())), d._children[a] && (d._children[a]._parent = null); - } else { - c = this.getProperty(a, !1, !1); - if (0 === c.length()) { - return d; + a._parent = this, this._children || (this._children = []), this._children[d] = a; } - c._children.forEach(function(b, c) { - var h = d._children.indexOf(b); - b._parent = null; - 0 === c ? (a = String(h), d._children.splice(h, 1, void 0)) : d._children.splice(h, 1); - }); - } - if (1 === b._kind || 3 === b._kind || 4 === b._kind || 5 === b._kind) { - b._parent = d, d._children[a] = b; - } else { - c = t(b); - var e = new h; - e._parent = d; - e._value = c; - d._children[a] = e; } - return d; }; - c.prototype.addInScopeNamespace = function(a) { - var b = this; - if (3 !== b._kind && 4 !== b._kind && 5 !== b._kind && 2 !== b._kind && void 0 !== a.prefix && ("" !== a.prefix || "" !== b._name.uri)) { - var c = null; - b._inScopeNamespaces.forEach(function(b, h) { - b.prefix === a.prefix && (c = b); + f.prototype.addInScopeNamespace = function(b) { + var a = this; + if (3 !== a._kind && 4 !== a._kind && 5 !== a._kind && 2 !== a._kind && void 0 !== b.prefix && ("" !== b.prefix || "" !== a._name.uri)) { + var d = null; + a._inScopeNamespaces.forEach(function(a, g) { + a.prefix === b.prefix && (d = a); }); - null !== c && c.uri !== a.uri && b._inScopeNamespaces.forEach(function(h, d) { - h.prefix === c.prefix && (b._inScopeNamespaces[d] = a); + null !== d && d.uri !== b.uri && a._inScopeNamespaces.forEach(function(g, f) { + g.prefix === d.prefix && (a._inScopeNamespaces[f] = b); }); - b._name.prefix === a.prefix && (b._name.prefix = void 0); - b._attributes.forEach(function(b, c) { - b._name.prefix === a.prefix && (b._name.prefix = void 0); + a._name.prefix === b.prefix && (a._name.prefix = void 0); + a._attributes.forEach(function(a, d) { + a._name.prefix === b.prefix && (a._name.prefix = void 0); }); } }; - c.prototype.descendants = function(a) { - "undefined" === typeof a && (a = "*"); - a = u(a); - var b = a._flags, c = new x; + f.prototype.descendantsInto = function(b, a) { + var d = b.flags; if (1 !== this._kind) { - return c; + return a; } - var h = b & 4; - b & 1 ? this._attributes.forEach(function(b, d) { - (h || a.localName === b._name.localName) && c.appendChild(b); + var g = a._children.length, f = b.localName, e = b.uri, k = d & 4; + d & 1 ? this._attributes.forEach(function(b, d) { + if (k || f === b._name.localName && e === b._name.uri) { + a._children[g++] = b; + } }) : this._children.forEach(function(b, d) { - (h || a.localName === b._name.localName) && c.appendChild(b); - }); - this._children.forEach(function(b, h) { - c.appendChild(b.descendants(a)); - }); - return c; - }; - c.prototype.comments = function() { - var a = new x(this, null); - this._children.forEach(function(b, c) { - 4 === b._kind && a.appendChild(b); + if (k || f === b._name.localName && e === b._name.uri) { + a._children[g++] = b; + } }); - return a; - }; - c.prototype.text = function() { - var a = new x(this, null); - this._children.forEach(function(b, c) { - 3 === b._kind && a.appendChild(b); + this._children.forEach(function(d, g) { + d.descendantsInto(b, a); }); return a; }; - c.instanceConstructor = c; - c.callableConstructor = function(a) { - "undefined" === typeof a && (a = void 0); - if (null === a || void 0 === a) { - a = ""; - } - return e(a); - }; - c.defaultNamespace = ""; - c._flags = L.ALL; - c._prettyIndent = 2; - return c; - }(g.ASNative); - g.ASXML = Q; - var U = function(a) { - function c(a) { - "undefined" === typeof a && (a = void 0); - if (null === a || void 0 === a) { - a = ""; + f.instanceConstructor = f; + f.classInitializer = function() { + var b = f.prototype; + N(b, "asDeleteProperty", b._asDeleteProperty); + N(b, "$BgvalueOf", Object.prototype.$BgvalueOf); + N(b, "$BghasOwnProperty", b.native_hasOwnProperty); + N(b, "$BgpropertyIsEnumerable", b.native_propertyIsEnumerable); + K(f, ["settings", "setSettings", "defaultSettings"]); + K(b, "toString addNamespace appendChild attribute attributes child childIndex children comments contains copy descendants elements hasComplexContent hasSimpleContent inScopeNamespaces insertChildAfter insertChildBefore length localName name namespace namespaceDeclarations nodeKind normalize parent processingInstructions prependChild removeNamespace replace setChildren setLocalName setName setNamespace text toXMLString toJSON".split(" ")); + }; + f.callableConstructor = function(b) { + c.isNullOrUndefined(b) && (b = ""); + return n(b); + }; + f.defaultNamespace = ""; + f._flags = S.ALL; + f._prettyIndent = 2; + return f; + }(a.ASNative); + a.ASXML = P; + var V = function(f) { + function e(b) { + this._children = []; + c.isNullOrUndefined(b) && (b = ""); + if (b) { + if (b instanceof e) { + b = b._children; + for (var a = 0;a < b.length;a++) { + this._children[a] = b[a]; + } + } else { + k(b, this); + } } - var b = q(a); - f(a) && (b = b._deepCopy()); - return b; } - __extends(c, a); - c.prototype.toString = function() { - return t(this); + __extends(e, f); + e.addXML = function(b, d) { + var g; + b instanceof P ? (g = new a.ASXMLList, g.append(b)) : g = b; + g.append(d); + return g; }; - c.prototype._deepCopy = function() { - for (var a = new x, b = 0;b < this.length();b++) { - a.appendChild(this._children[b]._deepCopy()); + e.createList = function(b, d) { + void 0 === b && (b = null); + void 0 === d && (d = null); + var g = new a.ASXMLList; + g._targetObject = b; + g._targetProperty = d; + return g; + }; + e.prototype.valueOf = function() { + return this; + }; + e.prototype.equals = function(b) { + var a = this._children; + if (void 0 === b && 0 === a.length) { + return!0; } - return a; + if (b instanceof e) { + b = b._children; + if (b.length !== a.length) { + return!1; + } + for (var d = 0;d < a.length;d++) { + if (!a[d].equals(b[d])) { + return!1; + } + } + return!0; + } + return 1 === a.length && a[0].equals(b); + }; + e.prototype.toString = function() { + if (this.hasComplexContent()) { + return this.toXMLString(); + } + for (var b = "", a = 0;a < this._children.length;a++) { + b += v(this._children[a]); + } + return b; + }; + e.prototype._deepCopy = function() { + for (var b = a.ASXMLList.createList(this._targetObject, this._targetProperty), d = this.length(), g = 0;g < d;g++) { + b._children[g] = this._children[g]._deepCopy(); + } + return b; }; - c.prototype.hasOwnProperty = function(a) { - I("public.XMLList::hasOwnProperty"); + e.prototype._shallowCopy = function() { + for (var b = a.ASXMLList.createList(this._targetObject, this._targetProperty), d = this.length(), g = 0;g < d;g++) { + b._children[g] = this._children[g]; + } + return b; }; - c.prototype.propertyIsEnumerable = function(a) { - I("public.XMLList::propertyIsEnumerable"); + e.prototype.native_hasOwnProperty = function(b) { + b = M(b); + if (e.isTraitsOrDynamicPrototype(this)) { + return a.ASObject.prototype.native_hasOwnProperty.call(this, b); + } + if (c.isIndex(b)) { + return(b | 0) < this._children.length; + } + b = d(b); + for (var g = !!(b.flags & 1), f = this._children, k = 0;k < f.length;k++) { + var r = f[k]; + if (1 === r._kind && r.hasProperty(b, g, !1)) { + return!0; + } + } + return!1; }; - c.prototype.attribute = function(a) { - return this.getProperty(a, !0, !1); + e.prototype.native_propertyIsEnumerable = function(b) { + return c.isIndex(b) && (b | 0) < this._children.length; }; - c.prototype.attributes = function() { - return this.getProperty("*", !0, !1); + e.prototype.attribute = function(b) { + return this.getProperty(b, !0); }; - c.prototype.child = function(a) { - return this.getProperty(a, !1, !1); + e.prototype.attributes = function() { + return this.getProperty("*", !0); }; - c.prototype.children = function() { - return this.getProperty("*", !1, !1); + e.prototype.child = function(b) { + if (c.isIndex(b)) { + var d = a.ASXMLList.createList(this._targetObject, this._targetProperty); + b < this._children.length && (d._children[0] = this._children[b | 0]._deepCopy()); + return d; + } + return this.getProperty(b, !1); }; - c.prototype.comments = function() { - var a = new x(this); - this._children.forEach(function(b) { - 1 === b._kind && (b = b.comments(), Array.prototype.push.apply(a._children, b._children)); + e.prototype.children = function() { + return this.getProperty("*", !1); + }; + e.prototype.descendants = function(b) { + b = d(b); + for (var g = a.ASXMLList.createList(this._targetObject, this._targetProperty), f = 0;f < this._children.length;f++) { + var e = this._children[f]; + 1 === e._kind && e.descendantsInto(b, g); + } + return g; + }; + e.prototype.comments = function() { + var b = a.ASXMLList.createList(this._targetObject, this._targetProperty); + this._children.forEach(function(a) { + 1 === a._kind && (a = a.comments(), Array.prototype.push.apply(b._children, a._children)); }); - return a; + return b; }; - c.prototype.contains = function(a) { - return 0 <= this._children.indexOf(a); + e.prototype.contains = function(b) { + for (var a = this._children, d = 0;d < a.length;d++) { + if (a[d].equals(b)) { + return!0; + } + } + return!1; }; - c.prototype.copy = function() { + e.prototype.copy = function() { return this._deepCopy(); }; - c.prototype.elements = function(a) { - "undefined" === typeof a && (a = "*"); - var b = new x(this, new D(a)); - this._children.forEach(function(c) { - 1 === c._kind && (c = c.elements(a), Array.prototype.push.apply(b._children, c._children)); + e.prototype.elements = function(b) { + void 0 === b && (b = "*"); + var g = d(b), f = a.ASXMLList.createList(this._targetObject, g); + this._children.forEach(function(b) { + 1 === b._kind && (b = b.elements(g), Array.prototype.push.apply(f._children, b._children)); }); - return b; + return f; }; - c.prototype.hasComplexContent = function() { + e.prototype.hasComplexContent = function() { switch(this.length()) { case 0: return!1; case 1: return this._children[0].hasComplexContent(); default: - return this._children.some(function(a) { - return 1 === a._kind; + return this._children.some(function(b) { + return 1 === b._kind; }); } }; - c.prototype.hasSimpleContent = function() { + e.prototype.hasSimpleContent = function() { switch(this.length()) { case 0: return!0; case 1: return this._children[0].hasSimpleContent(); default: - return this._children.every(function(a) { - return 1 !== a._kind; + return this._children.every(function(b) { + return 1 !== b._kind; }); } }; - c.prototype.length = function() { + e.prototype.length = function() { return this._children.length; }; - c.prototype.name = function() { + e.prototype.name = function() { return this._children[0].name(); }; - c.prototype.normalize = function() { - I("public.XMLList::normalize"); + e.prototype.normalize = function() { + for (var b = 0;b < this._children.length;) { + var a = this._children[b]; + if (1 === a._kind) { + a.normalize(), b++; + } else { + if (3 === a._kind) { + for (b++;b < this._children.length;) { + var d = this._children[b]; + if (3 !== d._kind) { + break; + } + a._value += d._value; + this.removeByIndex(b); + } + 0 === a._value.length ? this.removeByIndex(b) : b++; + } else { + b++; + } + } + } + return this; }; - c.prototype.parent = function() { - if (0 !== this.length()) { - for (var a = this._children[0]._parent, b = 1;b < this.length();b++) { - if (a !== this._children[b]._parent) { + e.prototype.parent = function() { + var b = this._children; + if (0 !== b.length) { + for (var a = b[0]._parent, d = 1;d < b.length;d++) { + if (b[d]._parent !== a) { return; } } return a; } }; - c.prototype.processingInstructions = function(a) { - I("public.XMLList::processingInstructions"); + e.prototype.processingInstructions = function(b) { + void 0 === b && (b = "*"); + var g = d(b).localName, f = a.ASXMLList.createList(this._targetObject, this._targetProperty); + f._targetObject = this; + f._targetProperty = null; + for (var e = this._children, k = 0;k < e.length;k++) { + e[k].processingInstructionsInto(b, g, f); + } + return f; }; - c.prototype.text = function() { - var a = new x(this); - this._children.forEach(function(b, c) { - if (1 === b._kind) { - var h = b.text(); - 0 < h.length() && a.appendChild(h); + e.prototype.text = function() { + var b = a.ASXMLList.createList(this._targetObject, this._targetProperty); + this._children.forEach(function(a, d) { + if (1 === a._kind) { + var g = a.text(); + 0 < g.length() && b._children.push(g); } }); - return a; + return b; }; - c.prototype.toXMLString = function() { - return p(this); + e.prototype.toXMLString = function() { + return q(this); }; - c.prototype.addNamespace = function(a) { - I("public.XMLList::addNamespace"); + e.prototype.toJSON = function(b) { + return "XMLList"; }; - c.prototype.appendChild = function(a) { - if (a instanceof c) { - return this._children.push.apply(this._children, a._children), a; - } - this._children.push(a); + e.prototype.addNamespace = function(b) { + 1 !== this._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLOnlyWorksWithOneItemLists, "addNamespace"); + var a = this._children[0]; + a.addNamespace(b); + return a; + }; + e.prototype.appendChild = function(b) { + 1 !== this._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLOnlyWorksWithOneItemLists, "appendChild"); + var a = this._children[0]; + a.appendChild(b); return a; }; - c.prototype.childIndex = function() { - I("public.XMLList::childIndex"); + e.prototype.append = function(b) { + var d = this._children, g = d.length, f = 1; + if (b instanceof a.ASXMLList) { + if (this._targetObject = b._targetObject, this._targetProperty = b._targetProperty, b = b._children, f = b.length, 0 !== f) { + for (f = 0;f < b.length;f++) { + d[g + f] = b[f]; + } + } + } else { + z(b instanceof a.ASXML), d[g] = b, this._targetProperty = b._name; + } }; - c.prototype.inScopeNamespaces = function() { - I("public.XMLList::inScopeNamespaces"); + e.prototype.childIndex = function() { + 1 !== this._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLOnlyWorksWithOneItemLists, "childIndex"); + return this._children[0].childIndex(); }; - c.prototype.insertChildAfter = function(a, b) { - I("public.XMLList::insertChildAfter"); + e.prototype.inScopeNamespaces = function() { + 1 !== this._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLOnlyWorksWithOneItemLists, "inScopeNamespaces"); + return this._children[0].inScopeNamespaces(); }; - c.prototype.insertChildBefore = function(a, b) { - I("public.XMLList::insertChildBefore"); + e.prototype.insertChildAfter = function(b, a) { + 1 !== this._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLOnlyWorksWithOneItemLists, "insertChildAfter"); + return this._children[0].insertChildAfter(b, a); }; - c.prototype.nodeKind = function() { - I("public.XMLList::nodeKind"); + e.prototype.insertChildBefore = function(b, a) { + 1 !== this._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLOnlyWorksWithOneItemLists, "insertChildBefore"); + return this._children[0].insertChildBefore(b, a); }; - c.prototype._namespace = function(a, b) { - I("public.XMLList::private _namespace"); + e.prototype.nodeKind = function() { + 1 !== this._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLOnlyWorksWithOneItemLists, "nodeKind"); + return this._children[0].nodeKind(); }; - c.prototype.localName = function() { - I("public.XMLList::localName"); + e.prototype.namespace = function(b) { + 1 !== this._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLOnlyWorksWithOneItemLists, "namespace"); + var a = this._children[0]; + return arguments.length ? a.namespace(b) : a.namespace(); }; - c.prototype.namespaceDeclarations = function() { - I("public.XMLList::namespaceDeclarations"); + e.prototype.localName = function() { + 1 !== this._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLOnlyWorksWithOneItemLists, "localName"); + return this._children[0].localName(); }; - c.prototype.prependChild = function(a) { - I("public.XMLList::prependChild"); + e.prototype.namespaceDeclarations = function() { + 1 !== this._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLOnlyWorksWithOneItemLists, "namespaceDeclarations"); + return this._children[0].namespaceDeclarations(); }; - c.prototype.removeNamespace = function(a) { - I("public.XMLList::removeNamespace"); + e.prototype.prependChild = function(b) { + 1 !== this._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLOnlyWorksWithOneItemLists, "prependChild"); + return this._children[0].prependChild(b); }; - c.prototype.replace = function(a, b) { - I("public.XMLList::replace"); + e.prototype.removeNamespace = function(b) { + 1 !== this._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLOnlyWorksWithOneItemLists, "removeNamespace"); + return this._children[0].removeNamespace(b); }; - c.prototype.setChildren = function(a) { - I("public.XMLList::setChildren"); + e.prototype.replace = function(b, a) { + 1 !== this._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLOnlyWorksWithOneItemLists, "replace"); + return this._children[0].replace(b, a); }; - c.prototype.setLocalName = function(a) { - I("public.XMLList::setLocalName"); + e.prototype.setChildren = function(b) { + 1 !== this._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLOnlyWorksWithOneItemLists, "setChildren"); + return this._children[0].setChildren(b); }; - c.prototype.setName = function(a) { - I("public.XMLList::setName"); + e.prototype.setLocalName = function(b) { + 1 !== this._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLOnlyWorksWithOneItemLists, "setLocalName"); + return this._children[0].setLocalName(b); }; - c.prototype.setNamespace = function(a) { - I("public.XMLList::setNamespace"); + e.prototype.setName = function(b) { + 1 !== this._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLOnlyWorksWithOneItemLists, "setName"); + return this._children[0].setName(b); }; - c.isTraitsOrDynamicPrototype = function(a) { - return a === c.traitsPrototype || a === c.dynamicPrototype; + e.prototype.setNamespace = function(b) { + 1 !== this._children.length && h.Runtime.throwError("TypeError", h.Errors.XMLOnlyWorksWithOneItemLists, "setNamespace"); + return this._children[0].setNamespace(b); }; - c.prototype.asGetEnumerableKeys = function() { - return c.isTraitsOrDynamicPrototype(this) ? J.call(this) : this._children.asGetEnumerableKeys(); + e.isTraitsOrDynamicPrototype = function(b) { + return b === e.traitsPrototype || b === e.dynamicPrototype; }; - c.prototype.getProperty = function(a, c, h) { - if (h) { - return a = G.isQName(a) ? a : this.resolveMultinameProperty(a.namespaces, a.name, a.flags), this[G.getQualifiedName(a)]; - } - if (b.isIndex(a)) { - return this._children[a]; + e.prototype.asGetEnumerableKeys = function() { + return e.isTraitsOrDynamicPrototype(this) ? E.call(this) : this._children.asGetEnumerableKeys(); + }; + e.prototype.getProperty = function(b, g) { + if (c.isIndex(b)) { + return this._children[b]; } - var d = u(a), e = new x(this, d); - this._children.forEach(function(a, b) { - if (1 === a._kind) { - var l = a.getProperty(d, c, h); - 0 < l.length() && e.appendChild(l); + var f = d(b), e = a.ASXMLList.createList(this._targetObject, f); + this._children.forEach(function(b, a) { + if (1 === b._kind) { + var d = b.getProperty(f, g); + if (0 < d.length()) { + for (var d = d._children, k = 0;k < d.length;k++) { + e._children.push(d[k]); + } + } } }); return e; }; - c.prototype.asGetNumericProperty = function(a) { - return this.asGetProperty(null, a, 0); + e.prototype.asGetNumericProperty = function(b) { + return this.asGetProperty(null, b, 0); }; - c.prototype.asSetNumericProperty = function(a, b) { - this.asSetProperty(null, a, 0, b); + e.prototype.asSetNumericProperty = function(b, a) { + this.asSetProperty(null, b, 0, a); }; - c.prototype.asGetProperty = function(a, b, h) { - if (c.isTraitsOrDynamicPrototype(this)) { - return C.call(this, a, b, h); + e.prototype.asGetProperty = function(b, a, d) { + if (e.isTraitsOrDynamicPrototype(this)) { + return y.call(this, b, a, d); } - h &= G.ATTRIBUTE; - return this.getProperty(r(a, b, h), h, !1); + d = !!(d & A.ATTRIBUTE); + return this.getProperty(g(b, a, d), d); }; - c.prototype.hasProperty = function(a, c) { - return b.isIndex(a) ? Number(a) < this._children.length : !0; + e.prototype.hasProperty = function(b, a) { + return c.isIndex(b) ? Number(b) < this._children.length : !0; }; - c.prototype.asHasProperty = function(a, b, h) { - if (c.isTraitsOrDynamicPrototype(this)) { - return C.call(this, a, b, h); + e.prototype.asHasProperty = function(b, a, d) { + if (e.isTraitsOrDynamicPrototype(this)) { + return y.call(this, b, a, d); } - h &= G.ATTRIBUTE; - return this.hasProperty(r(a, b, h), h); + d = !!(d & A.ATTRIBUTE); + return this.hasProperty(g(b, a, d), d); }; - c.prototype.asHasPropertyInternal = function(a, b, c) { - c &= G.ATTRIBUTE; - return this.hasProperty(r(a, b, c), c); + e.prototype.asHasPropertyInternal = function(b, a, d) { + d = !!(d & A.ATTRIBUTE); + return this.hasProperty(g(b, a, d), d); + }; + e.prototype.resolveValue = function() { + return this; }; - c.prototype.setProperty = function(a, c, h) { - b.isIndex(a) ? this.appendChild(h) : (a = this.getProperty(a, c, !1), e(a).replace(0, e(h))); + e.prototype.setProperty = function(d, g, f) { + if (c.isIndex(d)) { + d |= 0; + var e = null; + if (this._targetObject && (e = this._targetObject.resolveValue(), null === e)) { + return; + } + g = this.length(); + if (d >= g) { + if (e instanceof a.ASXMLList) { + if (1 !== e.length()) { + return; + } + e = e._children[0]; + } + z(null === e || e instanceof a.ASXML); + if (e && 1 !== e._kind) { + return; + } + var k = new a.ASXML; + k._parent = e; + k._name = this._targetProperty; + if (b(this._targetProperty)) { + if (e.hasProperty(this._targetProperty, !0, !1)) { + return; + } + k._kind = 2; + } else { + c.isNullOrUndefined(this._targetProperty) || "*" === this._targetProperty.localName ? (k._name = null, k._kind = 3) : k._kind = 1; + } + d = g; + if (2 !== k._kind) { + if (null !== e) { + var r; + if (0 < d) { + var m = this._children[d - 1]; + for (r = 0;r < e.length - 1 && e._children[r] !== m;r++) { + } + } else { + r = e.length() - 1; + } + e._children[r + 1] = k; + k._parent = e; + } + f instanceof a.ASXML ? k._name = f._name : f instanceof a.ASXMLList && (k._name = f._targetProperty); + this.append(k); + } + } + s(f) && 3 !== f._kind && 2 !== f._kind || (f += ""); + r = this._children[d]; + k = r._kind; + e = r._parent; + if (2 === k) { + var w = e._children.indexOf(r); + e.setProperty(r._name, !0, !1); + this._children[d] = e._children[w]; + } else { + if (f instanceof a.ASXMLList) { + w = f._shallowCopy(); + f = w.length(); + if (null !== e) { + for (k = e._children.indexOf(r), e.replace(k, w), r = 0;r < f;r++) { + w.setProperty(r, !1, e._children[k + r]); + } + } + if (0 === f) { + for (r = d + 1;r < g;r++) { + this._children[r - 1] = this._children[r]; + } + this._children.length--; + } else { + for (r = g - 1;r > d;r--) { + this._children[r + f - 1] = this._children[r]; + } + } + for (r = 0;r < f;r++) { + this._children[d + r] = w._children[r]; + } + } else { + f instanceof a.ASXML || 3 <= k ? (null !== e && (k = e._children.indexOf(r), e.replace(k, w), f = e._children[k]), "string" === typeof f ? (w = new a.ASXML(f), this._children[d] = w) : this._children[d] = f) : r.setProperty("*", !1, f); + } + } + } else { + if (0 === this._children.length) { + e = this.resolveValue(); + if (null === e || 1 !== e._children.length) { + return; + } + this.append(e._children[0]); + } + 1 === this._children.length ? this._children[0].setProperty(d, g, f) : h.Runtime.throwError("TypeError", h.Errors.XMLAssigmentOneItemLists); + } }; - c.prototype.asSetProperty = function(a, b, h, d) { - if (c.isTraitsOrDynamicPrototype(this)) { - return E.call(this, a, b, h, d); + e.prototype.asSetProperty = function(b, a, d, f) { + if (e.isTraitsOrDynamicPrototype(this)) { + return D.call(this, b, a, d, f); } - h &= G.ATTRIBUTE; - b = r(a, b, h); - return this.setProperty(b, h, d); + d = !!(d & A.ATTRIBUTE); + a = g(b, a, d); + return this.setProperty(a, d, f); }; - c.prototype.asCallProperty = function(a, b, h, d, e) { - if (c.isTraitsOrDynamicPrototype(this) || d) { - return O.call(this, a, b, h, d, e); + e.prototype._asDeleteProperty = function(b, a, d) { + if (c.isIndex(a)) { + b = a | 0; + if (b >= this._children.length) { + return!0; + } + this.removeByIndex(b); + return!0; } - var l; - l = this.resolveMultinameProperty(a, b, h); - if (this.asGetNumericProperty && G.isNumeric(l)) { - l = this.asGetNumericProperty(l); + d = !!(d & A.ATTRIBUTE); + a = g(b, a, d); + for (b = 0;b < this._children.length;b++) { + var f = this._children[b]; + 1 === f._kind && f.deleteProperty(a, d); + } + return!0; + }; + e.prototype.removeByIndex = function(b) { + var a = this._children[b], d = a._parent; + d && (a._parent = null, d._children.splice(d._children.indexOf(a), 1)); + this._children.splice(b, 1); + }; + e.prototype.asCallProperty = function(b, a, d, g, f) { + if (e.isTraitsOrDynamicPrototype(this) || g) { + return L.call(this, b, a, d, g, f); + } + var k; + k = this.resolveMultinameProperty(b, a, d); + if (this.asGetNumericProperty && A.isNumeric(k)) { + k = this.asGetNumericProperty(k); } else { - var n = this.asOpenMethods; - l = n && n[l] || this[l]; + var r = this.asOpenMethods; + k = r && r[k] || this[k]; } - if (l) { - return O.call(this, a, b, h, d, e); + if (k) { + return k.asApply(g ? null : this, f); } if (1 === this.length()) { - return this._children[0].asCallProperty(a, b, h, d, e); + return this._children[0].asCallProperty(b, a, d, g, f); } throw new TypeError; }; - c.instanceConstructor = c; - c.callableConstructor = function(a) { - "undefined" === typeof a && (a = void 0); - if (null === a || void 0 === a) { - a = ""; + e.instanceConstructor = e; + e.classInitializer = function() { + var b = e.prototype; + N(b, "asDeleteProperty", b._asDeleteProperty); + N(b, "$BgvalueOf", Object.prototype.$BgvalueOf); + N(b, "$BghasOwnProperty", b.native_hasOwnProperty); + N(b, "$BgpropertyIsEnumerable", b.native_propertyIsEnumerable); + K(b, "toString addNamespace appendChild attribute attributes child childIndex children comments contains copy descendants elements hasComplexContent hasSimpleContent inScopeNamespaces insertChildAfter insertChildBefore length localName name namespace namespaceDeclarations nodeKind normalize parent processingInstructions prependChild removeNamespace replace setChildren setLocalName setName setNamespace text toXMLString toJSON".split(" ")); + }; + e.callableConstructor = function(b) { + c.isNullOrUndefined(b) && (b = ""); + if (b instanceof e) { + return b; } - return q(a); + var d = new a.ASXMLList; + k(b, d); + return d; }; - return c; - }(g.ASNative); - g.ASXMLList = U; - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - function f(a) { - return c.getPublicQualifiedName(a); + return e; + }(a.ASNative); + a.ASXMLList = V; + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + function s(b, a) { + var d = (b | 0) === b; + if (a & 512 && (c.isNullOrUndefined(b) || d)) { + return null; + } + var g = !1; + if (b instanceof Function) { + if ("boundTo" in b) { + if (a & 512) { + return null; + } + g = !0; + } else { + if ("methodInfo" in b && b.methodInfo && "lastBoundMethod" in b.methodInfo) { + return null; + } + } + } + b = Object(b); + var f = b.classInfo ? b : Object.getPrototypeOf(b).class; + n(f, "No class found for object " + b); + var k = f === b && !(a & 512), r = f.classInfo, m = {}; + m[w] = d ? "int" : g ? "builtin.as$0::MethodClosure" : e(r.instanceInfo.name); + m[l("isDynamic")] = k || !(r.instanceInfo.flags & 1) && !g; + m[l("isFinal")] = k || !!(r.instanceInfo.flags & 2) || g; + m[l("isStatic")] = k; + if (a & 256) { + var z = m[l("traits")] = q(f, r, k, a) + } + g && (z[l("bases")].unshift("Function"), z[l("accessors")][1][l("declaredBy")] = "builtin.as$0::MethodClosure"); + return m; } - function k(a) { - var b = a.name; - return(a = a.namespaces[0]) && a.uri ? a.uri + "::" + b : b; + function v(b, d) { + for (var g = 0, f = d[l("bases")], e = 0;f && e < f.length;e++) { + var k = new a.ASXML(''); + b.appendChild(k); + g++; + } + f = d[l("interfaces")]; + for (e = 0;f && e < f.length;e++) { + k = new a.ASXML(''), b.appendChild(k), g++; + } + null !== d[l("constructor")] && (k = new a.ASXML(""), p(k, d[l("constructor")]), b.appendChild(k), g++); + f = d[l("variables")]; + for (e = 0;f && e < f.length;e++) { + var r = f[e], k = "readonly" === r[l("access")] ? "constant" : "variable", k = new a.ASXML("<" + k + ' name="' + a.escapeAttributeValue(r[l("name")]) + '" type="' + r[l("type")] + '"/>'); + null !== r[l("uri")] && k.setProperty("uri", !0, r[l("uri")]); + null !== r[l("metadata")] && u(k, r[l("metadata")]); + b.appendChild(k); + g++; + } + f = d[l("accessors")]; + for (e = 0;f && e < f.length;e++) { + var c = f[e], k = new a.ASXML(''); + null !== c[l("uri")] && k.setProperty("uri", !0, c[l("uri")]); + null !== c[l("metadata")] && u(k, r[l("metadata")]); + b.appendChild(k); + g++; + } + f = d[l("methods")]; + for (e = 0;f && e < f.length;e++) { + c = f[e], k = new a.ASXML(''), p(k, c[l("parameters")]), null !== c[l("uri")] && k.setProperty("uri", !0, c[l("uri")]), null !== c[l("metadata")] && u(k, r[l("metadata")]), b.appendChild(k), g++; + } + u(b, d[l("metadata")]); + return 0 < g; } - function s(a) { - if (!a) { + function p(b, d) { + if (d) { + for (var g = 0;g < d.length;g++) { + var f = d[g], f = new a.ASXML(''); + b.appendChild(f); + } + } + } + function u(b, d) { + if (d) { + for (var g = 0;g < d.length;g++) { + for (var f = d[g], e = new a.ASXML(''), f = f[l("value")], k = 0;k < f.length;k++) { + var r = f[k], r = new a.ASXML(''); + e.appendChild(r); + } + b.appendChild(e); + } + } + } + function l(b) { + return k.getPublicQualifiedName(b); + } + function e(b) { + var a = b.name; + return(b = b.namespaces[0]) && b.uri ? b.uri + "::" + a : a; + } + function m(b) { + if (!b) { return null; } - var b = [], c; - for (c in a) { - b.push(m(a[c])); + var a = [], d; + for (d in b) { + "native" !== d && a.push(t(b[d])); } - return b; + return a; } - function m(a) { - var b = {}; - b[u] = a.name; - b[h] = a.value.map(function(a) { - var b = {}; - b[h] = a.value; - b[x] = a.key; - return b; + function t(b) { + var a = {}; + a[w] = b.name; + a[B] = b.value.map(function(b) { + var a = {}; + a[B] = b.value; + a[M] = b.key; + return a; }); - return b; + return a; } - function d(b, c, h, d) { - function n(c) { - a(c, "No traits array found on class" + b.classInfo.instanceInfo.name); - for (var f = c.length;f--;) { - var x = c[f]; - if (x.name.getNamespace().isPublic() || x.name.uri) { - var g = k(x.name); - if (V[g] !== Q[g]) { - var C = M[g]; - C[q] = "readwrite"; - 2 === x.kind && (C[w] = k(x.methodInfo.returnType)); + function q(f, k, c, q) { + function t(a) { + n(a, "No traits array found on class" + f.classInfo.instanceInfo.name); + for (var k = a.length;k--;) { + var l = a[k], s = l.name.getNamespace(); + if (!(!s.isPublic() && !l.name.uri || q & 1 && s.uri)) { + var p = e(l.name); + if (P[p] !== V[p]) { + s = O[p], s[g] = "readwrite", 2 === l.kind && (s[z] = l.methodInfo.returnType ? e(l.methodInfo.returnType) : "*"); } else { - if (!M[g]) { - switch(C = {}, M[g] = C, x.kind) { + if (!O[p]) { + switch(s = {}, O[p] = s, l.kind) { case 6: ; case 0: - if (!(d & 8)) { + if (!(q & 8)) { continue; } - C[u] = g; - C[l] = void 0 === x.name.uri ? null : x.name.uri; - C[w] = x.typeName ? k(x.typeName) : "*"; - C[q] = "readwrite"; - C[e] = d & 64 ? s(x.metadata) : null; - B.push(C); + s[w] = p; + s[r] = void 0 === l.name.uri ? null : l.name.uri; + s[z] = l.typeName ? e(l.typeName) : "*"; + s[g] = "readwrite"; + s[b] = q & 64 ? m(l.metadata) : null; + v.push(s); break; case 1: - if (!m) { + if (!h) { continue; } - C[r] = x.methodInfo.returnType ? k(x.methodInfo.returnType) : "*"; - C[e] = d & 64 ? s(x.metadata) : null; - C[u] = g; - C[l] = void 0 === x.name.uri ? null : x.name.uri; - for (var g = C[y] = [], x = x.methodInfo.parameters, K = 0;K < x.length;K++) { - var F = x[K], A = {}; - A[w] = F.type ? k(F.type) : "*"; - A[G] = "value" in F; - g.push(A); + s[A] = l.methodInfo.returnType ? e(l.methodInfo.returnType) : "*"; + s[b] = q & 64 ? m(l.metadata) : null; + s[w] = p; + s[r] = void 0 === l.name.uri ? null : l.name.uri; + for (var p = s[N] = [], l = l.methodInfo.parameters, u = 0;u < l.length;u++) { + var M = l[u], D = {}; + D[z] = M.type ? e(M.type) : "*"; + D[K] = "value" in M; + p.push(D); } - C[p] = U; - L.push(C); + s[d] = $; + S.push(s); break; case 2: ; case 3: - if (!(d & 16) || h) { + if (!(q & 16) || c) { continue; } - C[u] = g; - 2 === x.kind ? (C[w] = x.methodInfo.returnType ? k(x.methodInfo.returnType) : "*", V[g] = C) : (K = x.methodInfo.parameters[0].type, C[w] = K ? k(K) : "*", Q[g] = C); - C[q] = 2 === x.kind ? "readonly" : "writeonly"; - C[e] = d & 64 ? s(x.metadata) : null; - C[l] = void 0 === x.name.uri ? null : x.name.uri; - C[p] = U; - z.push(C); + s[w] = p; + 2 === l.kind ? (s[z] = l.methodInfo.returnType ? e(l.methodInfo.returnType) : "*", P[p] = s) : (u = l.methodInfo.parameters[0].type, s[z] = u ? e(u) : "*", V[p] = s); + s[g] = 2 === l.kind ? "readonly" : "writeonly"; + s[b] = q & 64 ? m(l.metadata) : null; + s[r] = void 0 === l.name.uri ? null : l.name.uri; + s[d] = $; + B.push(s); break; default: - a(!1, "Unknown trait type: " + x.kind); + n(!1, "Unknown trait type: " + l.kind); } } } } } } - var x = d & 2, m = d & 32 && !h, A = {}, B = A[f("variables")] = d & 8 ? [] : null, z = A[f("accessors")] = d & 16 ? [] : null, P = null; - d & 64 && (P = h ? [] : s(c.metadata) || []); - A[e] = P; - A[f("constructor")] = null; - if (d & 4) { - if (c = A[f("interfaces")] = [], d & 512 || !h) { - for (var D in b.implementedInterfaces) { - P = b.implementedInterfaces[D].getQualifiedClassName(), c.push(P); + var s = q & 2, h = q & 32 && !c, p = {}, v = p[l("variables")] = q & 8 ? [] : null, B = p[l("accessors")] = q & 16 ? [] : null, u = null; + q & 64 && (u = c ? [] : m(k.metadata) || []); + p[b] = u; + p[l("constructor")] = null; + if (q & 4) { + if (k = p[l("interfaces")] = [], !c) { + for (var M in f.implementedInterfaces) { + u = f.implementedInterfaces[M].getQualifiedClassName(), k.push(u); } } } else { - A[f("interfaces")] = null; + p[l("interfaces")] = null; } - var L = A[f("methods")] = m ? [] : null; - D = A[f("bases")] = x ? [] : null; - var M = {}, V = {}, Q = {}; - for (c = !1;b;) { - var U = k(b.classInfo.instanceInfo.name); - x && c && !h ? D.push(U) : c = !0; - if (d & 1024 && b === g.ASObject) { - break; - } - d & 512 || !h ? n(b.classInfo.instanceInfo.traits) : n(b.classInfo.traits); - b = b.baseClass; - } - h && (d & 16 && (c = {}, c[u] = "prototype", c[w] = "*", c[q] = "readonly", c[e] = null, c[l] = null, c[p] = "Class", z.push(c)), x && (D.pop(), D.push("Class", "Object"), b = g.ASClass)); - return A; - } - var a = b.Debug.assert, c = b.AVM2.ABC.Multiname, n; - (function(a) { - a[a.HIDE_NSURI_METHODS = 1] = "HIDE_NSURI_METHODS"; - a[a.INCLUDE_BASES = 2] = "INCLUDE_BASES"; - a[a.INCLUDE_INTERFACES = 4] = "INCLUDE_INTERFACES"; - a[a.INCLUDE_VARIABLES = 8] = "INCLUDE_VARIABLES"; - a[a.INCLUDE_ACCESSORS = 16] = "INCLUDE_ACCESSORS"; - a[a.INCLUDE_METHODS = 32] = "INCLUDE_METHODS"; - a[a.INCLUDE_METADATA = 64] = "INCLUDE_METADATA"; - a[a.INCLUDE_CONSTRUCTOR = 128] = "INCLUDE_CONSTRUCTOR"; - a[a.INCLUDE_TRAITS = 256] = "INCLUDE_TRAITS"; - a[a.USE_ITRAITS = 512] = "USE_ITRAITS"; - a[a.HIDE_OBJECT = 1024] = "HIDE_OBJECT"; - })(n || (n = {})); - var p = f("declaredBy"), e = f("metadata"), q = f("access"), l = f("uri"), u = f("name"), w = f("type"), r = f("returnType"), h = f("value"), x = f("key"), y = f("parameters"), G = f("optional"); - g.describeTypeJSON = function(b, c) { - if (!b || "object" !== typeof b) { - return null; + var S = p[l("methods")] = h ? [] : null; + M = p[l("bases")] = s ? [] : null; + var O = {}, P = {}, V = {}; + for (k = !1;f;) { + var $ = e(f.classInfo.instanceInfo.name); + s && k && !c ? M.push($) : k = !0; + if (q & 1024 && f === a.ASObject) { + break; + } + c ? t(f.classInfo.traits) : t(f.classInfo.instanceInfo.traits); + f = f.baseClass; } - var h = b.classInfo ? b : Object.getPrototypeOf(b).class; - a(h, "No class found for object " + b); - var e = h === b, l = h.classInfo, n = {}; - n[u] = k(l.instanceInfo.name); - n[f("isDynamic")] = e || !(l.instanceInfo.flags & 1); - n[f("isFinal")] = e || !!(l.instanceInfo.flags & 2); - n[f("isStatic")] = e; - c & 256 && (n[f("traits")] = d(h, l, e, c)); - return n; + c && (q & 16 && (k = {}, k[w] = "prototype", k[z] = "*", k[g] = "readonly", k[b] = null, k[r] = null, k[d] = "Class", B.push(k)), s && (M.pop(), M.push("Class", "Object"), f = a.ASClass)); + return p; + } + var n = c.Debug.assert, k = c.AVM2.ABC.Multiname, f; + (function(b) { + b[b.HIDE_NSURI_METHODS = 1] = "HIDE_NSURI_METHODS"; + b[b.INCLUDE_BASES = 2] = "INCLUDE_BASES"; + b[b.INCLUDE_INTERFACES = 4] = "INCLUDE_INTERFACES"; + b[b.INCLUDE_VARIABLES = 8] = "INCLUDE_VARIABLES"; + b[b.INCLUDE_ACCESSORS = 16] = "INCLUDE_ACCESSORS"; + b[b.INCLUDE_METHODS = 32] = "INCLUDE_METHODS"; + b[b.INCLUDE_METADATA = 64] = "INCLUDE_METADATA"; + b[b.INCLUDE_CONSTRUCTOR = 128] = "INCLUDE_CONSTRUCTOR"; + b[b.INCLUDE_TRAITS = 256] = "INCLUDE_TRAITS"; + b[b.USE_ITRAITS = 512] = "USE_ITRAITS"; + b[b.HIDE_OBJECT = 1024] = "HIDE_OBJECT"; + })(f || (f = {})); + var d = l("declaredBy"), b = l("metadata"), g = l("access"), r = l("uri"), w = l("name"), z = l("type"), A = l("returnType"), B = l("value"), M = l("key"), N = l("parameters"), K = l("optional"); + a.describeTypeJSON = s; + a.describeType = function(b, d) { + var g = s(b, d), f = h.Runtime.AVM2.instance.systemDomain; + f.getClass("XML"); + f.getClass("XMLList"); + f.getClass("QName"); + f.getClass("Namespace"); + f = new a.ASXML(""); + f.setProperty("name", !0, g[l("name")]); + var e = g[l("traits")][l("bases")]; + e.length && f.setProperty("base", !0, e[0]); + f.setProperty("isDynamic", !0, g[l("isDynamic")].toString()); + f.setProperty("isFinal", !0, g[l("isFinal")].toString()); + f.setProperty("isStatic", !0, g[l("isStatic")].toString()); + v(f, g[l("traits")]); + if (g = s(b, d | 512)) { + e = new a.ASXML(""), e.setProperty("type", !0, g[l("name")]), v(e, g[l("traits")]) && f.appendChild(e); + } + return f; }; - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - var f = b.Debug.assert, k = b.ObjectUtilities.createEmptyObject; - (function(b) { - (function(b) { - var d = Object.prototype.asGetProperty, a = Object.prototype.asSetProperty, c = Object.prototype.asHasProperty, n = Object.prototype.asDeleteProperty, p = Object.prototype.asGetEnumerableKeys, e = function(b) { - function e(a) { - } - __extends(e, b); - e.isTraitsOrDynamicPrototype = function(a) { - return a === e.traitsPrototype || a === e.dynamicPrototype; +(function(c) { + (function(h) { + (function(a) { + var s = c.Debug.assert; + (function(c) { + (function(c) { + var h = Object.prototype.asGetProperty, l = Object.prototype.asSetProperty, e = Object.prototype.asHasProperty, m = Object.prototype.asDeleteProperty, t = Object.prototype.asGetEnumerableKeys, q = function(a) { + function k(a) { + } + __extends(k, a); + k.isTraitsOrDynamicPrototype = function(a) { + return a === k.traitsPrototype || a === k.dynamicPrototype; }; - e.makePrimitiveKey = function(a) { + k.makePrimitiveKey = function(a) { if ("string" === typeof a || "number" === typeof a) { return a; } - f("object" === typeof a || "function" === typeof a, typeof a); + s("object" === typeof a || "function" === typeof a, typeof a); }; - e.prototype.init = function(a) { + k.prototype.init = function(a) { this.weakKeys = !!a; this.map = new WeakMap; a || (this.keys = []); - this.primitiveMap = k(); + this.primitiveMap = Object.create(null); }; - e.prototype.asGetNumericProperty = function(a) { + k.prototype.asGetNumericProperty = function(a) { return this.asGetProperty(null, a, 0); }; - e.prototype.asSetNumericProperty = function(a, b) { - this.asSetProperty(null, a, 0, b); + k.prototype.asSetNumericProperty = function(a, d) { + this.asSetProperty(null, a, 0, d); }; - e.prototype.asGetProperty = function(a, b, c) { - if (e.isTraitsOrDynamicPrototype(this)) { - return d.call(this, a, b, c); + k.prototype.asGetProperty = function(a, d, b) { + if (k.isTraitsOrDynamicPrototype(this)) { + return h.call(this, a, d, b); } - a = e.makePrimitiveKey(b); - return void 0 !== a ? this.primitiveMap[a] : this.map.get(Object(b)); + a = k.makePrimitiveKey(d); + return void 0 !== a ? this.primitiveMap[a] : this.map.get(Object(d)); }; - e.prototype.asSetProperty = function(b, c, d, h) { - if (e.isTraitsOrDynamicPrototype(this)) { - return a.call(this, b, c, d, h); + k.prototype.asSetProperty = function(a, d, b, g) { + if (k.isTraitsOrDynamicPrototype(this)) { + return l.call(this, a, d, b, g); } - b = e.makePrimitiveKey(c); - void 0 !== b ? this.primitiveMap[b] = h : (this.map.set(Object(c), h), !this.weakKeys && 0 > this.keys.indexOf(c) && this.keys.push(c)); + a = k.makePrimitiveKey(d); + void 0 !== a ? this.primitiveMap[a] = g : (this.map.set(Object(d), g), !this.weakKeys && 0 > this.keys.indexOf(d) && this.keys.push(d)); }; - e.prototype.asHasProperty = function(a, b, d) { - if (e.isTraitsOrDynamicPrototype(this)) { - return c.call(this, a, b, d); + k.prototype.asHasProperty = function(a, d, b) { + if (k.isTraitsOrDynamicPrototype(this)) { + return e.call(this, a, d, b); } - a = e.makePrimitiveKey(b); - return void 0 !== a ? a in this.primitiveMap : this.map.has(Object(b)); + a = k.makePrimitiveKey(d); + return void 0 !== a ? a in this.primitiveMap : this.map.has(Object(d)); }; - e.prototype.asDeleteProperty = function(a, b, c) { - if (e.isTraitsOrDynamicPrototype(this)) { - return n.call(this, a, b, c); + k.prototype.asDeleteProperty = function(a, d, b) { + if (k.isTraitsOrDynamicPrototype(this)) { + return m.call(this, a, d, b); } - a = e.makePrimitiveKey(b); + a = k.makePrimitiveKey(d); void 0 !== a && delete this.primitiveMap[a]; - this.map.delete(Object(b)); - var h; - !this.weakKeys && 0 <= (h = this.keys.indexOf(b)) && this.keys.splice(h, 1); + this.map.delete(Object(d)); + var g; + !this.weakKeys && 0 <= (g = this.keys.indexOf(d)) && this.keys.splice(g, 1); return!0; }; - e.prototype.asGetEnumerableKeys = function() { - if (e.isTraitsOrDynamicPrototype(this)) { - return p.call(this); - } - var a = [], b; - for (b in this.primitiveMap) { - a.push(b); + k.prototype.asGetEnumerableKeys = function() { + if (k.isTraitsOrDynamicPrototype(this)) { + return t.call(this); + } + var a = [], d; + for (d in this.primitiveMap) { + a.push(d); } return this.weakKeys ? a : a.concat(this.keys); }; - e.protocol = e.prototype; - return e; - }(g.ASNative); - b.Dictionary = e; - b.OriginalDictionary = e; - })(b.utils || (b.utils = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - var f = b.Debug.notImplemented, k = b.AVM2.ABC.Namespace; - (function(b) { - (function(b) { - var d = Object.prototype.asGetProperty, a = Object.prototype.asSetProperty, c = Object.prototype.asCallProperty, n = Object.prototype.asHasProperty, p = Object.prototype.asHasOwnProperty, e = Object.prototype.asHasTraitProperty, q = Object.prototype.asDeleteProperty, l = function(b) { - function l() { - b.apply(this, arguments); + k.protocol = k.prototype; + return k; + }(a.ASNative); + c.Dictionary = q; + c.OriginalDictionary = q; + })(c.utils || (c.utils = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.Debug.notImplemented, h = c.AVM2.ABC.Namespace; + (function(c) { + (function(c) { + var l = Object.prototype.asGetProperty, e = Object.prototype.asSetProperty, m = Object.prototype.asCallProperty, t = Object.prototype.asHasProperty, q = Object.prototype.asHasOwnProperty, n = Object.prototype.asHasTraitProperty, k = Object.prototype.asDeleteProperty, f = function(a) { + function b() { + a.apply(this, arguments); } - __extends(l, b); - l.prototype.asGetProperty = function(a, b, l) { - return e.call(this, a, b, l) ? d.call(this, a, b, l) : c.call(this, [k.PROXY], "getProperty", 0, !1, [b]); + __extends(b, a); + b.prototype.asGetProperty = function(b, a, d) { + return n.call(this, b, a, d) ? l.call(this, b, a, d) : m.call(this, [h.PROXY], "getProperty", 0, !1, [a]); }; - l.prototype.asGetNumericProperty = function(a) { - return this.asGetProperty(null, a, 0); + b.prototype.asGetNumericProperty = function(b) { + return this.asGetProperty(null, b, 0); }; - l.prototype.asSetNumericProperty = function(a, b) { - this.asSetProperty(null, a, 0, b); + b.prototype.asSetNumericProperty = function(b, a) { + this.asSetProperty(null, b, 0, a); }; - l.prototype.asSetProperty = function(b, h, d, l) { - e.call(this, b, h, d) ? a.call(this, b, h, d, l) : c.call(this, [k.PROXY], "setProperty", 0, !1, [h, l]); + b.prototype.asSetProperty = function(b, a, d, f) { + n.call(this, b, a, d) ? e.call(this, b, a, d, f) : m.call(this, [h.PROXY], "setProperty", 0, !1, [a, f]); }; - l.prototype.asCallProperty = function(a, b, d, l, n) { - return e.call(this, a, b, d) ? c.call(this, a, b, d, !1, n) : c.call(this, [k.PROXY], "callProperty", 0, !1, [b].concat(n)); + b.prototype.asCallProperty = function(b, a, d, f, e) { + return n.call(this, b, a, d) ? m.call(this, b, a, d, !1, e) : m.call(this, [h.PROXY], "callProperty", 0, !1, [a].concat(e)); }; - l.prototype.asHasProperty = function(a, b, d) { - return e.call(this, a, b, d) ? n.call(this, a, b, d) : c.call(this, [k.PROXY], "hasProperty", 0, !1, [b]); + b.prototype.asHasProperty = function(b, a, d) { + return n.call(this, b, a, d) ? t.call(this, b, a, d) : m.call(this, [h.PROXY], "hasProperty", 0, !1, [a]); }; - l.prototype.asHasOwnProperty = function(a, b, d) { - return e.call(this, a, b, d) ? p.call(this, a, b, d) : c.call(this, [k.PROXY], "hasProperty", 0, !1, [b]); + b.prototype.asHasOwnProperty = function(b, a, d) { + return n.call(this, b, a, d) ? q.call(this, b, a, d) : m.call(this, [h.PROXY], "hasProperty", 0, !1, [a]); }; - l.prototype.asDeleteProperty = function(a, b, d) { - return e.call(this, a, b, d) ? q.call(this, a, b, d) : c.call(this, [k.PROXY], "deleteProperty", 0, !1, [b]); + b.prototype.asDeleteProperty = function(b, a, d) { + return n.call(this, b, a, d) ? k.call(this, b, a, d) : m.call(this, [h.PROXY], "deleteProperty", 0, !1, [a]); }; - l.prototype.asNextName = function(a) { - f("Proxy asNextName"); + b.prototype.asNextName = function(b) { + s("Proxy asNextName"); }; - l.prototype.asNextValue = function(a) { - f("Proxy asNextValue"); + b.prototype.asNextValue = function(b) { + s("Proxy asNextValue"); }; - l.prototype.asNextNameIndex = function(a) { - f("Proxy asNextNameIndex"); + b.prototype.asNextNameIndex = function(b) { + s("Proxy asNextNameIndex"); }; - l.protocol = l.prototype; - return l; - }(g.ASNative); - b.Proxy = l; - b.OriginalProxy = l; - })(b.utils || (b.utils = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - var f = b.Debug.unexpected, t = b.ArrayUtilities.DataBuffer, s = b.Debug.assert; - (function(b) { - (function(b) { - var a = function(a) { - function b() { + b.protocol = b.prototype; + return b; + }(a.ASNative); + c.Proxy = f; + c.OriginalProxy = f; + })(c.utils || (c.utils = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.Debug.notImplemented, v = c.Debug.unexpected, p = c.ArrayUtilities.DataBuffer, u = c.Debug.assert; + (function(c) { + (function(e) { + var c = function(a) { + function e() { a.apply(this, arguments); } - __extends(b, a); - b.AMF0 = 0; - b.AMF3 = 3; - b.DEFAULT = b.AMF3; - return b; - }(g.ASNative); - b.ObjectEncoding = a; - })(b.net || (b.net = {})); - })(g.flash || (g.flash = {})); - (function(m) { - (function(d) { - var a = function(a) { - function d() { + __extends(e, a); + Object.defineProperty(e, "dynamicPropertyWriter", {get:function() { + s("public flash.net.ObjectEncoding::get dynamicPropertyWriter"); + return null; + }, set:function(a) { + s("public flash.net.ObjectEncoding::set dynamicPropertyWriter"); + }, enumerable:!0, configurable:!0}); + e.AMF0 = 0; + e.AMF3 = 3; + e.DEFAULT = e.AMF3; + return e; + }(a.ASNative); + e.ObjectEncoding = c; + })(c.net || (c.net = {})); + })(a.flash || (a.flash = {})); + (function(l) { + (function(e) { + var m = function(a) { + function e() { } - __extends(d, a); - Object.defineProperty(d, "defaultObjectEncoding", {get:function() { + __extends(e, a); + Object.defineProperty(e, "defaultObjectEncoding", {get:function() { return this._defaultObjectEncoding; }, set:function(a) { this._defaultObjectEncoding = a >>> 0; }, enumerable:!0, configurable:!0}); - d.prototype.readObject = function() { + e.prototype.readObject = function() { switch(this._objectEncoding) { - case m.net.ObjectEncoding.AMF0: - return k.AMF0.read(this); - case m.net.ObjectEncoding.AMF3: - return k.AMF3.read(this); + case l.net.ObjectEncoding.AMF0: + return h.AMF0.read(this); + case l.net.ObjectEncoding.AMF3: + return h.AMF3.read(this); default: - f("Object Encoding"); + v("Object Encoding"); } }; - d.prototype.writeObject = function(a) { + e.prototype.writeObject = function(a) { switch(this._objectEncoding) { - case m.net.ObjectEncoding.AMF0: - return k.AMF0.write(this, a); - case m.net.ObjectEncoding.AMF3: - return k.AMF3.write(this, a); + case l.net.ObjectEncoding.AMF0: + return h.AMF0.write(this, a); + case l.net.ObjectEncoding.AMF3: + return h.AMF3.write(this, a); default: - f("Object Encoding"); + v("Object Encoding"); } }; - d.instanceConstructor = t; - d.staticNatives = [t]; - d.instanceNatives = [t.prototype]; - d.callableConstructor = null; - d.initializer = function(a) { - var c = b.Timeline.BinarySymbol; - s(c); - var f, l = 0; - a ? (a instanceof ArrayBuffer ? f = a.slice() : Array.isArray(a) ? f = (new Uint8Array(f)).buffer : a instanceof c ? f = (new Uint8Array(a.buffer)).buffer.slice() : "buffer" in a ? (s(a.buffer instanceof ArrayBuffer), f = a.buffer.slice()) : b.Debug.unexpected("Source type."), l = f.byteLength) : f = new ArrayBuffer(d.INITIAL_SIZE); - this._buffer = f; - this._length = l; + e.instanceConstructor = p; + e.staticNatives = [p]; + e.instanceNatives = [p.prototype]; + e.callableConstructor = null; + e.initializer = function(a) { + var k, f = 0; + a ? (a instanceof ArrayBuffer ? k = a.slice() : Array.isArray(a) ? k = (new Uint8Array(a)).buffer : "buffer" in a ? a.buffer instanceof ArrayBuffer ? k = (new Uint8Array(a)).buffer : a.buffer instanceof Uint8Array ? (k = a.buffer.byteOffset, k = a.buffer.buffer.slice(k, k + a.buffer.length)) : (u(a.buffer instanceof ArrayBuffer), k = a.buffer.slice()) : c.Debug.unexpected("Source type."), f = k.byteLength) : k = new ArrayBuffer(e.INITIAL_SIZE); + this._buffer = k; + this._length = f; this._position = 0; - this._updateViews(); - this._objectEncoding = d.defaultObjectEncoding; + this._resetViews(); + this._objectEncoding = e.defaultObjectEncoding; this._littleEndian = !1; this._bitLength = this._bitBuffer = 0; }; - d.protocol = d.prototype; - d.INITIAL_SIZE = 128; - d._defaultObjectEncoding = m.net.ObjectEncoding.DEFAULT; - return d; - }(g.ASNative); - d.ByteArray = a; - a.prototype.asGetNumericProperty = t.prototype.getValue; - a.prototype.asSetNumericProperty = t.prototype.setValue; - d.OriginalByteArray = a; - })(m.utils || (m.utils = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - var f = b.Debug.notImplemented, k = b.AVM2.Runtime.asCoerceString; - (function(s) { - (function(m) { - var d = function(a) { - function b() { + e.protocol = e.prototype; + e.INITIAL_SIZE = 128; + e._defaultObjectEncoding = l.net.ObjectEncoding.DEFAULT; + return e; + }(a.ASNative); + e.ByteArray = m; + m.prototype.asGetNumericProperty = p.prototype.getValue; + m.prototype.asSetNumericProperty = p.prototype.setValue; + e.OriginalByteArray = m; + })(l.utils || (l.utils = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.Debug.notImplemented, h = c.Debug.somewhatImplemented, p = c.AVM2.Runtime.asCoerceString; + (function(u) { + (function(l) { + var e = function(a) { + function e() { } - __extends(b, a); - Object.defineProperty(b.prototype, "enabled", {get:function() { - f("public flash.system.IME::get enabled"); + __extends(e, a); + Object.defineProperty(e, "enabled", {get:function() { + s("public flash.system.IME::static get enabled"); }, set:function(a) { - f("public flash.system.IME::set enabled"); + s("public flash.system.IME::static set enabled"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "conversionMode", {get:function() { - f("public flash.system.IME::get conversionMode"); + Object.defineProperty(e, "conversionMode", {get:function() { + s("public flash.system.IME::static get conversionMode"); }, set:function(a) { - k(a); - f("public flash.system.IME::set conversionMode"); + p(a); + s("public flash.system.IME::static set conversionMode"); }, enumerable:!0, configurable:!0}); - b.setCompositionString = function(a) { - k(a); - f("public flash.system.IME::static setCompositionString"); + e.setCompositionString = function(a) { + p(a); + s("public flash.system.IME::static setCompositionString"); }; - b.doConversion = function() { - f("public flash.system.IME::static doConversion"); + e.doConversion = function() { + s("public flash.system.IME::static doConversion"); }; - b.compositionSelectionChanged = function(a, b) { - f("public flash.system.IME::static compositionSelectionChanged"); + e.compositionSelectionChanged = function(a, e) { + s("public flash.system.IME::static compositionSelectionChanged"); }; - b.compositionAbandoned = function() { - f("public flash.system.IME::static compositionAbandoned"); + e.compositionAbandoned = function() { + s("public flash.system.IME::static compositionAbandoned"); }; - b._checkSupported = function() { - f("public flash.system.IME::static _checkSupported"); - }; - return b; - }(g.ASNative); - m.IME = d; - d = function(a) { - function c() { + Object.defineProperty(e, "isSupported", {get:function() { + h("public flash.system.IME::static get isSupported"); + return!1; + }, enumerable:!0, configurable:!0}); + return e; + }(a.ASNative); + l.IME = e; + e = function(a) { + function e() { a.apply(this, arguments); } - __extends(c, a); - Object.defineProperty(c, "ime", {get:function() { - f("public flash.system.System::get ime"); - }, enumerable:!0, configurable:!0}); - c.setClipboard = function(a) { - a = k(a); - null === b.ClipboardService.instance ? f("public flash.system.System::setClipboard") : b.ClipboardService.instance.setClipboard(a); + __extends(e, a); + Object.defineProperty(e, "ime", {get:function() { + s("public flash.system.System::get ime"); + }, enumerable:!0, configurable:!0}); + e.setClipboard = function(a) { + a = p(a); + null === c.ClipboardService.instance ? console.warn("setClipboard is only available in the Firefox extension") : c.ClipboardService.instance.setClipboard(a); }; - Object.defineProperty(c, "totalMemoryNumber", {get:function() { + Object.defineProperty(e, "totalMemoryNumber", {get:function() { + h("public flash.system.System::get totalMemoryNumber"); return 2097152; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c, "freeMemory", {get:function() { + Object.defineProperty(e, "freeMemory", {get:function() { + h("public flash.system.System::get freeMemory"); return 1048576; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c, "privateMemory", {get:function() { - f("public flash.system.System::get privateMemory"); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c, "processCPUUsage", {get:function() { - f("public flash.system.System::get processCPUUsage"); + Object.defineProperty(e, "privateMemory", {get:function() { + h("public flash.system.System::get privateMemory"); + return 1048576; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c, "useCodePage", {get:function() { - f("public flash.system.System::get useCodePage"); + Object.defineProperty(e, "useCodePage", {get:function() { + return e._useCodePage; }, set:function(a) { - f("public flash.system.System::set useCodePage"); + e._useCodePage = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c, "vmVersion", {get:function() { + Object.defineProperty(e, "vmVersion", {get:function() { return "1.0 Shumway - Mozilla Research"; }, enumerable:!0, configurable:!0}); - c.pause = function() { - f("public flash.system.System::static pause"); + e.pause = function() { }; - c.resume = function() { - f("public flash.system.System::static resume"); + e.resume = function() { }; - c.exit = function(a) { - f("public flash.system.System::static exit"); + e.exit = function(a) { }; - c.gc = function() { - f("public flash.system.System::static gc"); + e.gc = function() { }; - c.pauseForGCIfCollectionImminent = function(a) { - f("public flash.system.System::static pauseForGCIfCollectionImminent"); + e.pauseForGCIfCollectionImminent = function(a) { }; - c.disposeXML = function(a) { - f("public flash.system.System::static disposeXML"); + e.disposeXML = function(a) { }; - Object.defineProperty(c, "swfVersion", {get:function() { + Object.defineProperty(e, "swfVersion", {get:function() { return 19; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c, "apiVersion", {get:function() { + Object.defineProperty(e, "apiVersion", {get:function() { return 26; }, enumerable:!0, configurable:!0}); - c.getArgv = function() { + e.getArgv = function() { return[]; }; - c.getRunmode = function() { + e.getRunmode = function() { return "mixed"; }; - return c; - }(g.ASNative); - m.System = d; - m.OriginalSystem = d; - })(s.system || (s.system = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - var f = b.AVM2.ABC.Multiname, t = b.AVM2.ABC.ClassInfo, s = b.AVM2.ABC.ScriptInfo, m = b.AVM2.ABC.InstanceInfo, d = b.AVM2.ABC.Info, a = b.AVM2.ABC.MethodInfo, c = b.Debug.assert, n = b.Debug.notImplemented, p = b.ArrayUtilities.popManyIntoVoid, e = function() { - return function(a) { - "undefined" === typeof a && (a = ""); - this.message = a; + e._useCodePage = !1; + return e; + }(a.ASNative); + l.System = e; + l.OriginalSystem = e; + })(u.system || (u.system = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.AVM2.ABC.Multiname, v = c.AVM2.ABC.ClassInfo, p = c.AVM2.ABC.ScriptInfo, u = c.AVM2.ABC.InstanceInfo, l = c.AVM2.ABC.Info, e = c.AVM2.ABC.MethodInfo, m = c.Debug.assert, t = c.Debug.notImplemented, q = c.ArrayUtilities.popManyIntoVoid, n = function() { + return function(b) { + void 0 === b && (b = ""); + this.message = b; this.name = "VerifierError"; }; }(); - g.VerifierError = e; - var q = function() { + a.VerifierError = n; + var k = function() { return function() { }; }(); - g.TypeInformation = q; - var l = function() { + a.TypeInformation = k; + var f = function() { function a() { } - a.from = function(b, h) { - c(b.hash); - var d = a._cache[b.hash]; - d || (d = a._cache[b.hash] = new w(b, h)); - return d; + a.from = function(d, g) { + m(d.hash); + var e = a._cache[d.hash]; + e || (e = a._cache[d.hash] = new b(d, g)); + return e; }; - a.fromSimpleName = function(b, c) { - return a.fromName(f.fromSimpleName(b), c); + a.fromSimpleName = function(b, d) { + return a.fromName(s.fromSimpleName(b), d); }; - a.fromName = function(b, h) { + a.fromName = function(b, d) { if (void 0 === b) { return a.Undefined; } - var d = f.isQName(b) ? f.getFullQualifiedName(b) : void 0; - if (d) { - var e = a._cache.byQN[d]; + var g = s.isQName(b) ? s.getFullQualifiedName(b) : void 0; + if (g) { + var e = a._cache.byQN[g]; if (e) { return e; } } - if (d === f.getPublicQualifiedName("void")) { + if (g === s.getPublicQualifiedName("void")) { return a.Void; } - c(h, "An ApplicationDomain is needed."); - e = (e = h.findClassInfo(b)) ? a.from(e, h) : a.Any; - b.hasTypeParameter() && (e = new x(e, a.fromName(b.typeParameter, h))); - return a._cache.byQN[d] = e; + m(d, "An ApplicationDomain is needed."); + e = (e = d.findClassInfo(b)) ? a.from(e, d) : a.Any; + b.hasTypeParameter() && (e = new w(e, a.fromName(b.typeParameter, d))); + return a._cache.byQN[g] = e; }; a.initializeTypes = function(b) { - a._typesInitialized || (a.Any = new u("any", "?"), a.Null = new u("Null", "X"), a.Void = new u("Void", "V"), a.Undefined = new u("Undefined", "_"), a.Int = a.fromSimpleName("int", b).instanceType(), a.Uint = a.fromSimpleName("uint", b).instanceType(), a.Class = a.fromSimpleName("Class", b).instanceType(), a.Array = a.fromSimpleName("Array", b).instanceType(), a.Object = a.fromSimpleName("Object", b).instanceType(), a.String = a.fromSimpleName("String", b).instanceType(), a.Number = a.fromSimpleName("Number", - b).instanceType(), a.Boolean = a.fromSimpleName("Boolean", b).instanceType(), a.Function = a.fromSimpleName("Function", b).instanceType(), a.XML = a.fromSimpleName("XML", b).instanceType(), a.XMLList = a.fromSimpleName("XMLList", b).instanceType(), a.Dictionary = a.fromSimpleName("flash.utils.Dictionary", b).instanceType(), a._typesInitialized = !0); + a._typesInitialized || (a.Any = new d("any", "?"), a.Null = new d("Null", "X"), a.Void = new d("Void", "V"), a.Undefined = new d("Undefined", "_"), a.Int = a.fromSimpleName("int", b).instanceType(), a.Uint = a.fromSimpleName("uint", b).instanceType(), a.Class = a.fromSimpleName("Class", b).instanceType(), a.Array = a.fromSimpleName("Array", b).instanceType(), a.Object = a.fromSimpleName("Object", b).instanceType(), a.String = a.fromSimpleName("String", b).instanceType(), a.Number = a.fromSimpleName("Number", + b).instanceType(), a.Boolean = a.fromSimpleName("Boolean", b).instanceType(), a.Function = a.fromSimpleName("Function", b).instanceType(), a.XML = a.fromSimpleName("XML", b).instanceType(), a.XMLList = a.fromSimpleName("XMLList", b).instanceType(), a.QName = a.fromSimpleName("QName", b).instanceType(), a.Namespace = a.fromSimpleName("Namespace", b).instanceType(), a.Dictionary = a.fromSimpleName("flash.utils.Dictionary", b).instanceType(), a._typesInitialized = !0); }; - a.prototype.equals = function(a) { - return this === a; + a.prototype.equals = function(b) { + return this === b; + }; + a.prototype.canBeXML = function() { + return this === a.Any || this === a.Object || this === a.XML || this === a.XMLList || this === a.QName || this === a.QName; + }; + a.prototype.isStrictComparableWith = function(b) { + return this === b && !this.canBeXML(); }; a.prototype.merge = function(b) { return a.Any; @@ -14416,13 +15414,13 @@ return a.Any; }; a.prototype.super = function() { - b.Debug.abstractMethod("super"); + c.Debug.abstractMethod("super"); return null; }; - a.prototype.applyType = function(a) { + a.prototype.applyType = function(b) { return null; }; - a.prototype.getTrait = function(a, b, c) { + a.prototype.getTrait = function(b, a, d) { return null; }; a.prototype.isNumeric = function() { @@ -14444,554 +15442,554 @@ return!1; }; a.prototype.isTraitsType = function() { - return this instanceof w; + return this instanceof b; }; a.prototype.isParameterizedType = function() { - return this instanceof x; + return this instanceof w; }; a.prototype.isMethodType = function() { - return this instanceof r; + return this instanceof g; }; a.prototype.isMultinameType = function() { - return this instanceof h; + return this instanceof r; }; a.prototype.isConstantType = function() { - return this instanceof y; + return this instanceof z; }; - a.prototype.isSubtypeOf = function(a) { - return this === a || this.equals(a) ? !0 : this.merge(a) === this; + a.prototype.isSubtypeOf = function(b) { + return this === b || this.equals(b) ? !0 : this.merge(b) === this; }; a.prototype.asTraitsType = function() { - c(this.isTraitsType()); + m(this.isTraitsType()); return this; }; a.prototype.asMethodType = function() { - c(this.isMethodType()); + m(this.isMethodType()); return this; }; a.prototype.asMultinameType = function() { - c(this.isMultinameType()); + m(this.isMultinameType()); return this; }; a.prototype.asConstantType = function() { - c(this.isConstantType()); + m(this.isConstantType()); return this; }; a.prototype.getConstantValue = function() { - c(this.isConstantType()); + m(this.isConstantType()); return this.value; }; a.prototype.asParameterizedType = function() { - c(this.isParameterizedType()); + m(this.isParameterizedType()); return this; }; - a._cache = {byQN:b.ObjectUtilities.createEmptyObject(), byHash:b.ObjectUtilities.createEmptyObject()}; + a._cache = {byQN:Object.create(null), byHash:Object.create(null)}; a._typesInitialized = !1; return a; }(); - g.Type = l; - var u = function(a) { - function b(c, h) { - a.call(this); - this.name = c; - this.symbol = h; + a.Type = f; + var d = function(b) { + function a(d, g) { + b.call(this); + this.name = d; + this.symbol = g; } - __extends(b, a); - b.prototype.toString = function() { + __extends(a, b); + a.prototype.toString = function() { return this.symbol; }; - b.prototype.instanceType = function() { - return l.Any; + a.prototype.instanceType = function() { + return f.Any; }; - return b; - }(l); - g.AtomType = u; - var w = function(h) { - function d(a, b) { - h.call(this); - this.info = a; - this.domain = b; - } - __extends(d, h); - d.prototype.instanceType = function() { - c(this.info instanceof t); - var a = this.info; - return this._cachedType || (this._cachedType = l.from(a.instanceInfo, this.domain)); - }; - d.prototype.classType = function() { - c(this.info instanceof m); - var a = this.info; - return this._cachedType || (this._cachedType = l.from(a.classInfo, this.domain)); - }; - d.prototype.super = function() { - if (this.info instanceof t) { - return l.Class; - } - c(this.info instanceof m); - var a = this.info; - return a.superName ? (a = l.fromName(a.superName, this.domain).instanceType(), c(a instanceof d && a.info instanceof m), a) : null; - }; - d.prototype.findTraitByName = function(a, b, h) { - var d = !h, e; - if (f.isQName(b)) { - for (b = f.getQualifiedName(b), n = 0;n < a.length;n++) { - if (e = a[n], f.getQualifiedName(e.name) === b && !(h && e.isGetter() || d && e.isSetter())) { + return a; + }(f); + a.AtomType = d; + var b = function(b) { + function a(d, g) { + b.call(this); + this.info = d; + this.domain = g; + } + __extends(a, b); + a.prototype.instanceType = function() { + m(this.info instanceof v); + var b = this.info; + return this._cachedType || (this._cachedType = f.from(b.instanceInfo, this.domain)); + }; + a.prototype.classType = function() { + m(this.info instanceof u); + var b = this.info; + return this._cachedType || (this._cachedType = f.from(b.classInfo, this.domain)); + }; + a.prototype.super = function() { + if (this.info instanceof v) { + return f.Class; + } + m(this.info instanceof u); + var b = this.info; + return b.superName ? (b = f.fromName(b.superName, this.domain).instanceType(), m(b instanceof a && b.info instanceof u), b) : null; + }; + a.prototype.findTraitByName = function(b, a, d) { + var g = !d, e; + if (s.isQName(a)) { + for (a = s.getQualifiedName(a), k = 0;k < b.length;k++) { + if (e = b[k], s.getQualifiedName(e.name) === a && !(d && e.isGetter() || g && e.isSetter())) { return e; } } } else { - c(b instanceof f); - for (var l, n = 0;n < b.namespaces.length;n++) { - if (d = b.getQName(n), b.namespaces[n].isDynamic()) { - l = d; + m(a instanceof s); + for (var f, k = 0;k < a.namespaces.length;k++) { + if (g = a.getQName(k), a.namespaces[k].isDynamic()) { + f = g; } else { - if (e = this.findTraitByName(a, d, h)) { + if (e = this.findTraitByName(b, g, d)) { return e; } } } - if (l) { - return this.findTraitByName(a, l, h); + if (f) { + return this.findTraitByName(b, f, d); } } }; - d.prototype.getTrait = function(a, b, c) { - if (a.isMultinameType()) { + a.prototype.getTrait = function(b, a, d) { + if (b.isMultinameType()) { return null; } - var h = a.getConstantValue(); - if (h.isAttribute()) { + var g = b.getConstantValue(); + if (g.isAttribute()) { return null; } - if (c && (this.isInstanceInfo() || this.isClassInfo())) { - c = this; + if (d && (this.isInstanceInfo() || this.isClassInfo())) { + d = this; do { - (h = c.getTrait(a, b, !1)) || (c = c.super()); - } while (!h && c); - return h; + (g = d.getTrait(b, a, !1)) || (d = d.super()); + } while (!g && d); + return g; } - return this.findTraitByName(this.info.traits, h, b); + return this.findTraitByName(this.info.traits, g, a); }; - d.prototype.getTraitAt = function(a) { - for (var c = this.info.traits, h = c.length - 1;0 <= h;h--) { - if (c[h].slotId === a) { - return c[h]; + a.prototype.getTraitAt = function(b) { + for (var a = this.info.traits, d = a.length - 1;0 <= d;d--) { + if (a[d].slotId === b) { + return a[d]; } } - b.Debug.unexpected("Cannot find trait with slotId: " + a + " in " + c); + c.Debug.unexpected("Cannot find trait with slotId: " + b + " in " + a); }; - d.prototype.equals = function(a) { - return a.isTraitsType() ? this.info.traits === a.info.traits : !1; + a.prototype.equals = function(b) { + return b.isTraitsType() && this.info.traits === b.info.traits; }; - d.prototype.merge = function(a) { - if (a.isTraitsType()) { - if (this.equals(a)) { + a.prototype.merge = function(b) { + if (b.isTraitsType()) { + if (this.equals(b)) { return this; } - if (this.isNumeric() && a.isNumeric()) { - return l.Number; + if (this.isNumeric() && b.isNumeric()) { + return f.Number; } - if (this.isInstanceInfo() && a.isInstanceInfo()) { - for (var b = [], c = this;c;c = c.super()) { - b.push(c); - } - for (c = a;c;c = c.super()) { - for (a = 0;a < b.length;a++) { - if (b[a].equals(c)) { - return c; + if (this.isInstanceInfo() && b.isInstanceInfo()) { + for (var a = [], d = this;d;d = d.super()) { + a.push(d); + } + for (d = b;d;d = d.super()) { + for (b = 0;b < a.length;b++) { + if (a[b].equals(d)) { + return d; } } } - return l.Object; + return f.Object; } } - return l.Any; + return f.Any; }; - d.prototype.isScriptInfo = function() { - return this.info instanceof s; + a.prototype.isScriptInfo = function() { + return this.info instanceof p; }; - d.prototype.isClassInfo = function() { - return this.info instanceof t; + a.prototype.isClassInfo = function() { + return this.info instanceof v; }; - d.prototype.isMethodInfo = function() { - return this.info instanceof a; + a.prototype.isMethodInfo = function() { + return this.info instanceof e; }; - d.prototype.isInstanceInfo = function() { - return this.info instanceof m; + a.prototype.isInstanceInfo = function() { + return this.info instanceof u; }; - d.prototype.isInstanceOrClassInfo = function() { + a.prototype.isInstanceOrClassInfo = function() { return this.isInstanceInfo() || this.isClassInfo(); }; - d.prototype.applyType = function(a) { - return new x(this, a); + a.prototype.applyType = function(b) { + return new w(this, b); }; - d.prototype._getInfoName = function() { - if (this.info instanceof s) { + a.prototype._getInfoName = function() { + if (this.info instanceof p) { return "SI"; } - if (this.info instanceof t) { + if (this.info instanceof v) { return "CI:" + this.info.instanceInfo.name.name; } - if (this.info instanceof m) { + if (this.info instanceof u) { return "II:" + this.info.name.name; } - if (this.info instanceof a) { + if (this.info instanceof e) { return "MI"; } - c(!1); + m(!1); }; - d.prototype.toString = function() { + a.prototype.toString = function() { switch(this) { - case l.Int: + case f.Int: return "I"; - case l.Uint: + case f.Uint: return "U"; - case l.Array: + case f.Array: return "A"; - case l.Object: + case f.Object: return "O"; - case l.String: + case f.String: return "S"; - case l.Number: + case f.Number: return "N"; - case l.Boolean: + case f.Boolean: return "B"; - case l.Function: + case f.Function: return "F"; } return this._getInfoName(); }; - return d; - }(l); - g.TraitsType = w; - var r = function(a) { - function b(c, h) { - a.call(this, l.Function.info, h); - this.methodInfo = c; + return a; + }(f); + a.TraitsType = b; + var g = function(b) { + function a(d, g) { + b.call(this, f.Function.info, g); + this.methodInfo = d; } - __extends(b, a); - b.prototype.toString = function() { + __extends(a, b); + a.prototype.toString = function() { return "MT " + this.methodInfo; }; - b.prototype.returnType = function() { - return this._cachedType || (this._cachedType = l.fromName(this.methodInfo.returnType, this.domain)); + a.prototype.returnType = function() { + return this._cachedType || (this._cachedType = f.fromName(this.methodInfo.returnType, this.domain)); }; - return b; - }(w); - g.MethodType = r; - var h = function(a) { - function b(c, h, d) { - a.call(this); - this.namespaces = c; - this.name = h; - this.flags = d; + return a; + }(b); + a.MethodType = g; + var r = function(b) { + function a(d, g, e) { + b.call(this); + this.namespaces = d; + this.name = g; + this.flags = e; } - __extends(b, a); - b.prototype.toString = function() { + __extends(a, b); + a.prototype.toString = function() { return "MN"; }; - return b; - }(l); - g.MultinameType = h; - var x = function(a) { - function b(c, h) { - a.call(this, c.info, c.domain); - this.type = c; - this.parameter = h; + return a; + }(f); + a.MultinameType = r; + var w = function(b) { + function a(d, g) { + b.call(this, d.info, d.domain); + this.type = d; + this.parameter = g; } - __extends(b, a); - return b; - }(w); - g.ParameterizedType = x; - var y = function(a) { - function b(c) { - a.call(this); - this.value = c; + __extends(a, b); + return a; + }(b); + a.ParameterizedType = w; + var z = function(b) { + function a(d) { + b.call(this); + this.value = d; } - __extends(b, a); - b.prototype.toString = function() { + __extends(a, b); + a.prototype.toString = function() { return String(this.value); }; - b.from = function(a) { - return new b(a); + a.from = function(b) { + return new a(b); }; - b.fromArray = function(a) { - return a.map(function(a) { - return new b(a); + a.fromArray = function(b) { + return b.map(function(b) { + return new a(b); }); }; - return b; - }(l); - g.ConstantType = y; - var G = function() { - function a() { - this.id = a.id += 1; + return a; + }(f); + a.ConstantType = z; + var A = function() { + function b() { + this.id = b.id += 1; this.stack = []; this.scope = []; this.local = []; } - a.prototype.clone = function() { - var b = new a; - b.originalId = this.id; - b.stack = this.stack.slice(0); - b.scope = this.scope.slice(0); - b.local = this.local.slice(0); - return b; + b.prototype.clone = function() { + var a = new b; + a.originalId = this.id; + a.stack = this.stack.slice(0); + a.scope = this.scope.slice(0); + a.local = this.local.slice(0); + return a; }; - a.prototype.trace = function(a) { - a.writeLn(this.toString()); + b.prototype.trace = function(b) { + b.writeLn(this.toString()); }; - a.prototype.toString = function() { + b.prototype.toString = function() { return "<" + this.id + (this.originalId ? ":" + this.originalId : "") + ", L[" + this.local.join(", ") + "], S[" + this.stack.join(", ") + "], $[" + this.scope.join(", ") + "]>"; }; - a.prototype.equals = function(b) { - return a._arrayEquals(this.stack, b.stack) && a._arrayEquals(this.scope, b.scope) && a._arrayEquals(this.local, b.local); + b.prototype.equals = function(a) { + return b._arrayEquals(this.stack, a.stack) && b._arrayEquals(this.scope, a.scope) && b._arrayEquals(this.local, a.local); }; - a._arrayEquals = function(a, b) { - if (a.length != b.length) { + b._arrayEquals = function(b, a) { + if (b.length != a.length) { return!1; } - for (var c = a.length - 1;0 <= c;c--) { - if (!a[c].equals(b[c])) { + for (var d = b.length - 1;0 <= d;d--) { + if (!b[d].equals(a[d])) { return!1; } } return!0; }; - a.prototype.isSubset = function(b) { - return a._arraySubset(this.stack, b.stack) && a._arraySubset(this.scope, b.scope) && a._arraySubset(this.local, b.local); + b.prototype.isSubset = function(a) { + return b._arraySubset(this.stack, a.stack) && b._arraySubset(this.scope, a.scope) && b._arraySubset(this.local, a.local); }; - a._arraySubset = function(a, b) { - if (a.length != b.length) { + b._arraySubset = function(b, a) { + if (b.length != a.length) { return!1; } - for (var c = a.length - 1;0 <= c;c--) { - if (a[c] !== b[c] && !a[c].equals(b[c]) && a[c].merge(b[c]) !== a[c]) { + for (var d = b.length - 1;0 <= d;d--) { + if (b[d] !== a[d] && !b[d].equals(a[d]) && b[d].merge(a[d]) !== b[d]) { return!1; } } return!0; }; - a.prototype.merge = function(b) { - a._mergeArrays(this.local, b.local); - a._mergeArrays(this.stack, b.stack); - a._mergeArrays(this.scope, b.scope); - }; - a._mergeArrays = function(a, b) { - c(a.length === b.length, "a: " + a + " b: " + b); - for (var h = a.length - 1;0 <= h;h--) { - c(void 0 !== a[h] && void 0 !== b[h]), a[h] !== b[h] && (a[h] = a[h].merge(b[h])); + b.prototype.merge = function(a) { + b._mergeArrays(this.local, a.local); + b._mergeArrays(this.stack, a.stack); + b._mergeArrays(this.scope, a.scope); + }; + b._mergeArrays = function(b, a) { + m(b.length === a.length, "a: " + b + " b: " + a); + for (var d = b.length - 1;0 <= d;d--) { + m(void 0 !== b[d] && void 0 !== a[d]), b[d] !== a[d] && (b[d] = b[d].merge(a[d])); } }; - a.id = 0; - return a; + b.id = 0; + return b; }(); - g.State = G; - var I = function() { - function a(c, h, d) { - this.methodInfo = c; - this.domain = h; - this.savedScope = d; - this.writer = new b.IndentingWriter; + a.State = A; + var B = function() { + function a(b, d, g) { + this.methodInfo = b; + this.domain = d; + this.savedScope = g; + this.writer = new c.IndentingWriter; this.pushAnyCount = this.pushCount = 0; - l.initializeTypes(h); - this.writer = b.AVM2.Verifier.traceLevel.value ? new b.IndentingWriter : null; - this.multinames = c.abc.constantPool.multinames; - this.returnType = l.Undefined; + f.initializeTypes(d); + this.writer = c.AVM2.Verifier.traceLevel.value ? new c.IndentingWriter : null; + this.multinames = b.abc.constantPool.multinames; + this.returnType = f.Undefined; } a.prototype.verify = function() { - var a = this.methodInfo; + var b = this.methodInfo; this.writer && this.methodInfo.trace(this.writer); - c(a.localCount >= a.parameters.length + 1); + m(b.localCount >= b.parameters.length + 1); this._verifyBlocks(this._prepareEntryState()); }; a.prototype._prepareEntryState = function() { - var a = new G, b = this.methodInfo; - this.thisType = b.holder ? l.from(b.holder, this.domain) : l.Any; - a.local.push(this.thisType); - for (var h = b.parameters, d = 0;d < h.length;d++) { - a.local.push(l.fromName(h[d].type, this.domain).instanceType()); - } - h = b.localCount - b.parameters.length - 1; - if (b.needsRest() || b.needsArguments()) { - a.local.push(l.Array), h -= 1; - } - for (d = 0;d < h;d++) { - a.local.push(l.Undefined); - } - c(a.local.length === b.localCount); - return a; - }; - a.prototype._verifyBlocks = function(a) { - var c = this.writer, h = this.methodInfo.analysis.blocks; - h.forEach(function(a) { - a.verifierEntryState = a.verifierExitState = null; + var b = new A, a = this.methodInfo; + this.thisType = a.holder ? f.from(a.holder, this.domain) : f.Any; + b.local.push(this.thisType); + for (var d = a.parameters, g = 0;g < d.length;g++) { + b.local.push(f.fromName(d[g].type, this.domain).instanceType()); + } + d = a.localCount - a.parameters.length - 1; + if (a.needsRest() || a.needsArguments()) { + b.local.push(f.Array), d -= 1; + } + for (g = 0;g < d;g++) { + b.local.push(f.Undefined); + } + m(b.local.length === a.localCount); + return b; + }; + a.prototype._verifyBlocks = function(b) { + var a = this.writer, d = this.methodInfo.analysis.blocks; + d.forEach(function(b) { + b.verifierEntryState = b.verifierExitState = null; }); - for (var d = 0;d < h.length;d++) { - h[d].bdo = d; + for (var g = 0;g < d.length;g++) { + d[g].bdo = g; } - var e = new b.SortedList(function(a, b) { - return a.bdo - b.bdo; + var e = new c.SortedList(function(b, a) { + return b.bdo - a.bdo; }); - h[0].verifierEntryState = a; - for (e.push(h[0]);!e.isEmpty();) { - var l = e.pop(), n = l.verifierExitState = l.verifierEntryState.clone(); - this._verifyBlock(l, n); - l.succs.forEach(function(a) { - e.contains(a) ? (c && c.writeLn("Forward Merged Block: " + a.bid + " " + n.toString() + " with " + a.verifierEntryState.toString()), a.verifierEntryState.merge(n), c && c.writeLn("Merged State: " + a.verifierEntryState)) : a.verifierEntryState ? a.verifierEntryState.isSubset(n) || (c && c.writeLn("Backward Merged Block: " + l.bid + " with " + a.bid + " " + n.toString() + " with " + a.verifierEntryState.toString()), a.verifierEntryState.merge(n), e.push(a), c && c.writeLn("Merged State: " + - a.verifierEntryState)) : (a.verifierEntryState = n.clone(), e.push(a), c && c.writeLn("Added Block: " + a.bid + " to worklist: " + a.verifierEntryState.toString())); + d[0].verifierEntryState = b; + for (e.push(d[0]);!e.isEmpty();) { + var f = e.pop(), k = f.verifierExitState = f.verifierEntryState.clone(); + this._verifyBlock(f, k); + f.succs.forEach(function(b) { + e.contains(b) ? (a && a.writeLn("Forward Merged Block: " + b.bid + " " + k.toString() + " with " + b.verifierEntryState.toString()), b.verifierEntryState.merge(k), a && a.writeLn("Merged State: " + b.verifierEntryState)) : b.verifierEntryState ? b.verifierEntryState.isSubset(k) || (a && a.writeLn("Backward Merged Block: " + f.bid + " with " + b.bid + " " + k.toString() + " with " + b.verifierEntryState.toString()), b.verifierEntryState.merge(k), e.push(b), a && a.writeLn("Merged State: " + + b.verifierEntryState)) : (b.verifierEntryState = k.clone(), e.push(b), a && a.writeLn("Added Block: " + b.bid + " to worklist: " + b.verifierEntryState.toString())); }); } - c && (c.writeLn("Inferred return type: " + this.returnType), c.writeLn("Quality pushCount: " + this.pushCount + ", pushAnyCount: " + this.pushAnyCount)); + a && (a.writeLn("Inferred return type: " + this.returnType), a.writeLn("Quality pushCount: " + this.pushCount + ", pushAnyCount: " + this.pushAnyCount)); this.methodInfo.inferredReturnType = this.returnType; }; a.prototype._verifyBlock = function(a, d) { - function x() { - return X.ti || (X.ti = new q); + function e() { + return ea.ti || (ea.ti = new k); } - function g(a) { - c(a); - x().type = a; - Z.push(a); - U.pushCount++; - a === l.Any && U.pushAnyCount++; - } - function m(a) { - return Z.pop(); - } - function u() { - n(String(X)); - } - function s() { - var a = U.multinames[X.index]; - if (a.isRuntime()) { - var b; - b = a.isRuntimeName() ? m() : y.from(a.name); - var c; - c = a.isRuntimeNamespace() ? [m()] : y.fromArray(a.namespaces); - return new h(c, b, a.flags); + function w(b) { + m(b); + e().type = b; + W.push(b); + S.pushCount++; + b === f.Any && S.pushAnyCount++; + } + function l(b) { + return W.pop(); + } + function A() { + t("Bytecode not implemented in verifier: " + ea); + } + function p() { + var b = S.multinames[ea.index]; + if (b.isRuntime()) { + var a; + a = b.isRuntimeName() ? l() : z.from(b.name); + var d; + d = b.isRuntimeNamespace() ? [l()] : z.fromArray(b.namespaces); + return new r(d, a, b.flags); } - return y.from(a); + return z.from(b); } - function G(a) { - return a.isMultinameType() && a.asMultinameType().name.isNumeric() || a.isConstantType() && f.isNumeric(a.getConstantValue()) ? !0 : !1; + function v(b) { + return b.isMultinameType() && b.asMultinameType().name.isNumeric() || b.isConstantType() && s.isNumeric(b.getConstantValue()) ? !0 : !1; } - function t(a, b) { - if (a.isTraitsType() || a.isParameterizedType()) { - var c = a.getTrait(b, !1, !0); - if (c) { - S && S.debugLn("getProperty(" + b + ") -> " + c); - x().trait = c; - if (c.isSlot() || c.isConst()) { - return l.fromName(c.typeName, U.domain).instanceType(); + function B(b, a) { + if (b.isTraitsType() || b.isParameterizedType()) { + var d = b.getTrait(a, !1, !0); + if (d) { + O && O.debugLn("getProperty(" + a + ") -> " + d); + e().trait = d; + if (d.isSlot() || d.isConst()) { + return f.fromName(d.typeName, S.domain).instanceType(); } - if (c.isGetter()) { - return l.fromName(c.methodInfo.returnType, U.domain).instanceType(); + if (d.isGetter()) { + return f.fromName(d.methodInfo.returnType, S.domain).instanceType(); } - if (c.isClass()) { - return l.from(c.classInfo, U.domain); + if (d.isClass()) { + return f.from(d.classInfo, S.domain); } - if (c.isMethod()) { - return new r(c.methodInfo, U.domain); + if (d.isMethod()) { + return new g(d.methodInfo, S.domain); } } else { - if (G(b) && a.isParameterizedType()) { - return c = a.asParameterizedType().parameter, S && S.debugLn("getProperty(" + b + ") -> " + c), c; + if (v(a) && b.isParameterizedType()) { + return d = b.asParameterizedType().parameter, O && O.debugLn("getProperty(" + a + ") -> " + d), d; } - a !== l.Array && S && S.warnLn("getProperty(" + b + ")"); + b !== f.Array && O && O.warnLn("getProperty(" + a + ")"); } } - return l.Any; + return f.Any; } - function I(a, b, c) { - if (a.isTraitsType() || a.isParameterizedType()) { - (c = a.getTrait(b, !0, !0)) ? (S && S.debugLn("setProperty(" + b + ") -> " + c), x().trait = c) : G(b) && a.isParameterizedType() || a !== l.Array && S && S.warnLn("setProperty(" + b + ")"); + function u(b, a, d) { + if (b.isTraitsType() || b.isParameterizedType()) { + (d = b.getTrait(a, !0, !0)) ? (O && O.debugLn("setProperty(" + a + ") -> " + d), e().trait = d) : v(a) && b.isParameterizedType() || b !== f.Array && O && O.warnLn("setProperty(" + a + ")"); } } - function C(a, b) { - if (a.isMultinameType()) { - return l.Any; + function M(b, a) { + if (b.isMultinameType()) { + return f.Any; } - for (var c = U.savedScope, h = v.length - 1;h >= -c.length;h--) { - var d = 0 <= h ? v[h] : c[c.length + h]; - if (d.isTraitsType()) { - if (d.getTrait(a, !1, !0)) { - x().scopeDepth = v.length - h - 1; - if (d.isClassInfo() || d.isScriptInfo()) { - x().object = k.Runtime.LazyInitializer.create(d.info); + for (var d = S.savedScope, g = x.length - 1;g >= -d.length;g--) { + var k = 0 <= g ? x[g] : d[d.length + g]; + if (k.isTraitsType()) { + if (k.getTrait(b, !1, !0)) { + e().scopeDepth = x.length - g - 1; + if (k.isClassInfo() || k.isScriptInfo()) { + e().object = h.Runtime.LazyInitializer.create(k.info); } - S && S.debugLn("findProperty(" + a + ") -> " + d); - return d; + O && O.debugLn("findProperty(" + b + ") -> " + k); + return k; } } else { - return S && S.warnLn("findProperty(" + a + ")"), l.Any; + return O && O.warnLn("findProperty(" + b + ")"), f.Any; } } - if (c = U.domain.findDefiningScript(a.getConstantValue(), !1)) { - return x().object = k.Runtime.LazyInitializer.create(c.script), d = l.from(c.script, U.domain), S && S.debugLn("findProperty(" + a + ") -> " + d), d; + if (d = S.domain.findDefiningScript(b.getConstantValue(), !1)) { + return e().object = h.Runtime.LazyInitializer.create(d.script), k = f.from(d.script, S.domain), O && O.debugLn("findProperty(" + b + ") -> " + k), k; } - if (a.isConstantType() && "unsafeJSNative" === a.getConstantValue().name) { - return l.Any; + if (b.isConstantType() && "unsafeJSNative" === b.getConstantValue().name) { + return f.Any; } - S && S.warnLn("findProperty(" + a + ")"); - return l.Any; + O && O.warnLn("findProperty(" + b + ")"); + return f.Any; } - function E(a) { - if (a instanceof w && (a = a.getTraitAt(X.index), S && S.debugLn("accessSlot() -> " + a), a)) { - x().trait = a; + function N(a) { + if (a instanceof b && (a = a.getTraitAt(ea.index), O && O.debugLn("accessSlot() -> " + a), a)) { + e().trait = a; if (a.isSlot()) { - return l.fromName(a.typeName, U.domain).instanceType(); + return f.fromName(a.typeName, S.domain).instanceType(); } if (a.isClass()) { - return l.from(a.classInfo, U.domain); + return f.from(a.classInfo, S.domain); } } - return l.Any; + return f.Any; } - function Q(a) { - if (a.isTraitsType() || a.isParameterizedType()) { - return a === l.Function || a === l.Class || a === l.Object ? l.Object : a.instanceType(); + function Q(b) { + if (b.isTraitsType() || b.isParameterizedType()) { + return b === f.Function || b === f.Class || b === f.Object ? f.Object : b.instanceType(); } - S && S.warnLn("construct(" + a + ")"); - return l.Any; + O && O.warnLn("construct(" + b + ")"); + return f.Any; } - for (var U = this, S = this.writer, aa = this.methodInfo, $ = aa.analysis.bytecodes, ea = d.local, Z = d.stack, v = d.scope, X, ba = this.savedScope[0], R, H, Y, ga = a.position, ca = a.end.position;ga <= ca;ga++) { - if (X = $[ga], R = X.op, 240 !== R && 241 !== R) { - switch(S && 1 < b.AVM2.Verifier.traceLevel.value && S.writeLn(("stateBefore: " + d.toString() + " $$[" + this.savedScope.join(", ") + "]").padRight(" ", 100) + " : " + ga + ", " + X.toString(aa.abc)), R) { + for (var S = this, O = this.writer, P = this.methodInfo, V = P.analysis.bytecodes, $ = d.local, W = d.stack, x = d.scope, ea, Y = this.savedScope[0], R, U, ba, X = a.position, ga = a.end.position;X <= ga;X++) { + if (ea = V[X], R = ea.op, 240 !== R && 241 !== R) { + switch(O && 1 < c.AVM2.Verifier.traceLevel.value && O.writeLn(("stateBefore: " + d.toString() + " $$[" + this.savedScope.join(", ") + "]").padRight(" ", 100) + " : " + X + ", " + ea.toString(P.abc)), R) { case 1: break; case 3: - m(); + l(); break; case 4: - Y = s(); - H = m(); - c(H.super()); - x().baseClass = k.Runtime.LazyInitializer.create(this.thisType.asTraitsType().super().classType().info); - g(t(H.super(), Y)); + ba = p(); + U = l(); + m(U.super()); + e().baseClass = h.Runtime.LazyInitializer.create(this.thisType.asTraitsType().super().classType().info); + w(B(U.super(), ba)); break; case 5: - R = m(); - Y = s(); - H = m(); - c(H.super()); - x().baseClass = k.Runtime.LazyInitializer.create(this.thisType.asTraitsType().super().classType().info); - I(H.super(), Y, R); + R = l(); + ba = p(); + U = l(); + m(U.super()); + e().baseClass = h.Runtime.LazyInitializer.create(this.thisType.asTraitsType().super().classType().info); + u(U.super(), ba, R); break; case 6: - u(); + A(); break; case 7: - u(); + A(); break; case 8: - d.local[X.index] = l.Undefined; + d.local[ea.index] = f.Undefined; break; case 10: - u(); + A(); break; case 11: - u(); + A(); break; case 12: ; @@ -15016,132 +16014,132 @@ case 25: ; case 26: - m(); - m(); + l(); + l(); break; case 16: break; case 17: ; case 18: - m(); + l(); break; case 27: - m(l.Int); + l(f.Int); break; case 29: - v.pop(); + x.pop(); break; case 30: ; case 35: - m(l.Int); - m(); - g(l.Any); + l(f.Int); + l(); + w(f.Any); break; case 31: - g(l.Boolean); + w(f.Boolean); break; case 50: - g(l.Boolean); + w(f.Boolean); break; case 32: - g(l.Null); + w(f.Null); break; case 33: - g(l.Undefined); + w(f.Undefined); break; case 34: - u(); + A(); break; case 36: - g(l.Int); + w(f.Int); break; case 37: - g(l.Int); + w(f.Int); break; case 44: - g(l.String); + w(f.String); break; case 45: - g(l.Int); + w(f.Int); break; case 46: - g(l.Uint); + w(f.Uint); break; case 47: - g(l.Number); + w(f.Number); break; case 38: - g(l.Boolean); + w(f.Boolean); break; case 39: - g(l.Boolean); + w(f.Boolean); break; case 40: - g(l.Number); + w(f.Number); break; case 41: - m(); + l(); break; case 42: - R = m(); - g(R); - g(R); + R = l(); + w(R); + w(R); break; case 43: - H = m(); - Y = m(); - g(H); - g(Y); + U = l(); + ba = l(); + w(U); + w(ba); break; case 28: - m(); - v.push(l.Any); + l(); + x.push(f.Any); break; case 48: - v.push(m()); + x.push(l()); break; case 49: - u(); + A(); break; case 53: ; case 54: ; case 55: - g(l.Int); + w(f.Int); break; case 56: ; case 57: - g(l.Number); + w(f.Number); break; case 58: ; case 59: ; case 60: - m(l.Int); + l(f.Int); break; case 61: ; case 62: - m(l.Number); + l(f.Number); break; case 64: - g(l.Function); + w(f.Function); break; case 65: - p(Z, X.argCount); - H = m(); - m(); - g(l.Any); + q(W, ea.argCount); + U = l(); + l(); + w(f.Any); break; case 67: - throw new e("callmethod");; + throw new n("callmethod");; case 68: - u(); + A(); break; case 69: ; @@ -15152,45 +16150,45 @@ case 70: ; case 76: - p(Z, X.argCount); - Y = s(); - H = m(); + q(W, ea.argCount); + ba = p(); + U = l(); if (69 === R || 78 === R) { - H = this.thisType.super(), x().baseClass = k.Runtime.LazyInitializer.create(this.thisType.asTraitsType().super().classType().info); + U = this.thisType.super(), e().baseClass = h.Runtime.LazyInitializer.create(this.thisType.asTraitsType().super().classType().info); } - H = t(H, Y); + U = B(U, ba); if (79 === R || 78 === R) { break; } - H = H.isMethodType() ? H.asMethodType().returnType().instanceType() : H.isTraitsType() && H.isClassInfo() ? H.instanceType() : l.Any; - g(H); + U = U.isMethodType() ? U.asMethodType().returnType().instanceType() : U.isTraitsType() && U.isClassInfo() ? U.instanceType() : f.Any; + w(U); break; case 71: - this.returnType.merge(l.Undefined); + this.returnType.merge(f.Undefined); break; case 72: - H = m(); - aa.returnType && (Y = l.fromName(aa.returnType, this.domain).instanceType(), Y.isSubtypeOf(H) && (x().noCoercionNeeded = !0)); + U = l(); + P.returnType && (ba = f.fromName(P.returnType, this.domain).instanceType(), ba.isSubtypeOf(U) && (e().noCoercionNeeded = !0)); break; case 73: - p(Z, X.argCount); - Z.pop(); - this.thisType.isInstanceInfo() && this.thisType.super() === l.Object ? x().noCallSuperNeeded = !0 : x().baseClass = k.Runtime.LazyInitializer.create(this.thisType.asTraitsType().super().classType().info); + q(W, ea.argCount); + W.pop(); + this.thisType.isInstanceInfo() && this.thisType.super() === f.Object ? e().noCallSuperNeeded = !0 : e().baseClass = h.Runtime.LazyInitializer.create(this.thisType.asTraitsType().super().classType().info); break; case 66: - p(Z, X.argCount); - g(Q(m())); + q(W, ea.argCount); + w(Q(l())); break; case 74: - p(Z, X.argCount); - Y = s(); - g(Q(t(Z.pop(), Y))); + q(W, ea.argCount); + ba = p(); + w(Q(B(W.pop(), ba))); break; case 75: - u(); + A(); break; case 77: - u(); + A(); break; case 80: ; @@ -15199,205 +16197,205 @@ case 82: break; case 83: - c(1 === X.argCount); - R = m(); - H = m(); - H === l.Any ? g(l.Any) : g(H.applyType(R)); + m(1 === ea.argCount); + R = l(); + U = l(); + U === f.Any ? w(f.Any) : w(U.applyType(R)); break; case 84: - u(); + A(); break; case 85: - p(Z, 2 * X.argCount); - g(l.Object); + q(W, 2 * ea.argCount); + w(f.Object); break; case 86: - p(Z, X.argCount); - g(l.Array); + q(W, ea.argCount); + w(f.Array); break; case 87: - g(l.from(this.methodInfo, this.domain)); + w(f.from(this.methodInfo, this.domain)); break; case 88: - g(l.Any); + w(f.Any); break; case 89: - s(); - m(); - g(l.XMLList); + p(); + l(); + w(f.XMLList); break; case 90: - g(l.Any); + w(f.Any); break; case 93: - g(C(s(), !0)); + w(M(p(), !0)); break; case 94: - g(C(s(), !1)); + w(M(p(), !1)); break; case 95: - u(); + A(); break; case 96: - Y = s(); - g(t(C(Y, !0), Y)); + ba = p(); + w(B(M(ba, !0), ba)); break; case 104: ; case 97: - R = m(); - Y = s(); - H = m(); - I(H, Y, R); + R = l(); + ba = p(); + U = l(); + u(U, ba, R); break; case 98: - g(ea[X.index]); + w($[ea.index]); break; case 99: - ea[X.index] = m(); + $[ea.index] = l(); break; case 100: - g(ba); - x().object = k.Runtime.LazyInitializer.create(ba.asTraitsType().info); + w(Y); + e().object = h.Runtime.LazyInitializer.create(Y.asTraitsType().info); break; case 101: - g(v[X.index]); + w(x[ea.index]); break; case 102: - Y = s(); - H = m(); - g(t(H, Y)); + ba = p(); + U = l(); + w(B(U, ba)); break; case 103: - u(); + A(); break; case 105: - u(); + A(); break; case 106: - s(); - m(); - g(l.Boolean); + p(); + l(); + w(f.Boolean); break; case 107: - u(); + A(); break; case 108: - g(E(m())); + w(N(l())); break; case 109: - R = m(); - H = m(); - E(H); + R = l(); + U = l(); + N(U); break; case 110: - u(); + A(); break; case 111: - u(); + A(); break; case 112: - m(); - g(l.String); + l(); + w(f.String); break; case 113: - m(); - g(l.String); + l(); + w(f.String); break; case 114: - m(); - g(l.String); + l(); + w(f.String); break; case 131: ; case 115: - m(); - g(l.Int); + l(); + w(f.Int); break; case 136: ; case 116: - m(); - g(l.Uint); + l(); + w(f.Uint); break; case 132: ; case 117: - m(); - g(l.Number); + l(); + w(f.Number); break; case 129: ; case 118: - m(); - g(l.Boolean); + l(); + w(f.Boolean); break; case 119: - u(); + A(); break; case 120: break; case 121: - m(); - g(l.Number); + l(); + w(f.Number); break; case 122: - u(); + A(); break; case 123: - u(); + A(); break; case 128: - H = m(); - Y = l.fromName(this.multinames[X.index], this.domain).instanceType(); - Y.isSubtypeOf(H) && (x().noCoercionNeeded = !0); - g(Y); + U = l(); + ba = f.fromName(this.multinames[ea.index], this.domain).instanceType(); + ba.isSubtypeOf(U) && (e().noCoercionNeeded = !0); + w(ba); break; case 130: break; case 133: - m(); - g(l.String); + l(); + w(f.String); break; case 134: - H = m(); - Y = l.fromName(this.multinames[X.index], this.domain).instanceType(); - Y.isSubtypeOf(H) && (x().noCoercionNeeded = !0); - g(Y); + U = l(); + ba = f.fromName(this.multinames[ea.index], this.domain).instanceType(); + ba.isSubtypeOf(U) && (e().noCoercionNeeded = !0); + w(ba); break; case 135: - H = m(); - m(); - H.isTraitsType() ? g(H.instanceType()) : g(l.Any); + U = l(); + l(); + U === f.Class ? w(U) : U.isTraitsType() ? w(U.instanceType()) : w(f.Any); break; case 137: - u(); + A(); break; case 144: ; case 145: ; case 147: - m(); - g(l.Number); + l(); + w(f.Number); break; case 146: ; case 148: - ea[X.index] = l.Number; + $[ea.index] = f.Number; break; case 149: - m(); - g(l.String); + l(); + w(f.String); break; case 150: - m(); - g(l.Boolean); + l(); + w(f.Boolean); break; case 160: - Y = m(); - H = m(); - H.isNumeric() && Y.isNumeric() ? g(l.Number) : H === l.String || Y === l.String ? g(l.String) : g(l.Any); + ba = l(); + U = l(); + U.isNumeric() && ba.isNumeric() ? w(f.Number) : U === f.String || ba === f.String ? w(f.String) : w(f.Any); break; case 161: ; @@ -15406,9 +16404,9 @@ case 163: ; case 164: - m(); - m(); - g(l.Number); + l(); + l(); + w(f.Number); break; case 168: ; @@ -15421,13 +16419,13 @@ case 166: ; case 167: - m(); - m(); - g(l.Int); + l(); + l(); + w(f.Int); break; case 151: - m(); - g(l.Int); + l(); + w(f.Int); break; case 171: ; @@ -15444,40 +16442,40 @@ case 177: ; case 180: - m(); - m(); - g(l.Boolean); + l(); + l(); + w(f.Boolean); break; case 178: - m(); - g(l.Boolean); + l(); + w(f.Boolean); break; case 179: - m(); - m(); - g(l.Boolean); + l(); + l(); + w(f.Boolean); break; case 194: ; case 195: - ea[X.index] = l.Int; + $[ea.index] = f.Int; break; case 193: ; case 192: ; case 196: - m(); - g(l.Int); + l(); + w(f.Int); break; case 197: ; case 198: ; case 199: - m(); - m(); - g(l.Int); + l(); + l(); + w(f.Int); break; case 208: ; @@ -15486,7 +16484,7 @@ case 210: ; case 211: - g(ea[R - 208]); + w($[R - 208]); break; case 212: ; @@ -15495,7 +16493,7 @@ case 214: ; case 215: - ea[R - 212] = m(); + $[R - 212] = l(); break; case 239: break; @@ -15504,1975 +16502,1989 @@ case 243: break; default: - console.info("Not Implemented: " + X); + console.info("Not Implemented: " + ea); } } } }; return a; - }(), C = function() { - function a() { + }(), M = function() { + function b() { } - a.prototype._prepareScopeObjects = function(a, h) { - var e = a.abc.applicationDomain; - return h.getScopeObjects().map(function(a) { - if (a instanceof d) { - return l.from(a, e); - } - if (a instanceof b.AVM2.Runtime.Global) { - return l.from(a.scriptInfo, e); - } - if (a instanceof b.AVM2.AS.ASClass) { - return l.from(a.classInfo, e); + b.prototype._prepareScopeObjects = function(b, a) { + var d = b.abc.applicationDomain; + return a.getScopeObjects().map(function(b) { + if (b instanceof l) { + return f.from(b, d); + } + if (b instanceof c.AVM2.Runtime.Global) { + return f.from(b.scriptInfo, d); + } + if (b instanceof c.AVM2.AS.ASClass) { + return f.from(b.classInfo, d); } - if (a instanceof b.AVM2.Runtime.ActivationInfo) { - return l.from(a.methodInfo, e); + if (b instanceof c.AVM2.Runtime.ActivationInfo) { + return f.from(b.methodInfo, d); } - if (a.class) { - return l.from(a.class.classInfo.instanceInfo, e); + if (b.class) { + return f.from(b.class.classInfo.instanceInfo, d); } - c(!1, a.toString()); - return l.Any; + m(!1, b.toString()); + return f.Any; }); }; - a.prototype.verifyMethod = function(a, b) { - var c = this._prepareScopeObjects(a, b); - (new I(a, a.abc.applicationDomain, c)).verify(); + b.prototype.verifyMethod = function(b, a) { + var d = this._prepareScopeObjects(b, a); + (new B(b, b.abc.applicationDomain, d)).verify(); }; - return a; + return b; }(); - g.Verifier = C; - })(k.Verifier || (k.Verifier = {})); - })(b.AVM2 || (b.AVM2 = {})); + a.Verifier = M; + })(h.Verifier || (h.Verifier = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - function g(a, b) { - for (var c = 0;c < a.length;c++) { - b(a[c]); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + function h(b, a) { + for (var d = 0;d < b.length;d++) { + a(b[d]); } } - function k(c) { - var e, f = b.StringUtilities; - if (c instanceof h) { - return c.value instanceof d ? c.value.name : c.value; + function p(a) { + var w, n = c.StringUtilities; + if (a instanceof g) { + return a.value instanceof l ? a.value.name : a.value; } - if (c instanceof w) { - return c.name; + if (a instanceof d) { + return a.name; } - if (c instanceof u) { - return e = f.concat3("|", c.id, "|"), e; + if (a instanceof f) { + return w = n.concat3("|", a.id, "|"), w; } - if (c instanceof p) { - return e = f.concat3("{", c.id, "}"), e; + if (a instanceof t) { + return w = n.concat3("{", a.id, "}"), w; } - if (c instanceof r) { - return 3 === c.type ? (e = f.concat5("[", c.id, "->", c.argument.id, "]"), e) : (e = f.concat3("(", c.id, ")"), e); + if (a instanceof b) { + return 3 === a.type ? (w = n.concat5("[", a.id, "->", a.argument.id, "]"), w) : (w = n.concat3("(", a.id, ")"), w); } - if (c instanceof l) { - return e = f.concat3("(", c.id, ")"), e; + if (a instanceof k) { + return w = n.concat3("(", a.id, ")"), w; } - if (c instanceof n) { - return c.id; + if (a instanceof m) { + return a.id; } - a(c + " " + typeof c); + e(a + " " + typeof a); } - var m = b.Debug.assert, d = b.AVM2.ABC.Multiname, a = b.Debug.unexpected, c = b.ObjectUtilities.createEmptyObject; - (function(a) { - a[a.NumericProperty = 1] = "NumericProperty"; - a[a.RESOLVED = 2] = "RESOLVED"; - a[a.PRISTINE = 4] = "PRISTINE"; - a[a.IS_METHOD = 8] = "IS_METHOD"; - a[a.AS_CALL = 16] = "AS_CALL"; - })(f.Flags || (f.Flags = {})); - var n = function() { - function a() { - this.id = a.getNextID(); + var u = c.Debug.assert, l = c.AVM2.ABC.Multiname, e = c.Debug.unexpected; + (function(b) { + b[b.NumericProperty = 1] = "NumericProperty"; + b[b.RESOLVED = 2] = "RESOLVED"; + b[b.PRISTINE = 4] = "PRISTINE"; + b[b.IS_METHOD = 8] = "IS_METHOD"; + b[b.AS_CALL = 16] = "AS_CALL"; + })(a.Flags || (a.Flags = {})); + var m = function() { + function b() { + this.id = b.getNextID(); } - a.getNextID = function() { - return a._nextID[a._nextID.length - 1] += 1; + b.getNextID = function() { + return b._nextID[b._nextID.length - 1] += 1; }; - a.prototype.visitInputs = function(a) { + b.prototype.visitInputs = function(b) { }; - a.startNumbering = function() { - a._nextID.push(0); + b.startNumbering = function() { + b._nextID.push(0); }; - a.stopNumbering = function() { - a._nextID.pop(); + b.stopNumbering = function() { + b._nextID.pop(); }; - a.prototype.toString = function(a) { - if (a) { - return k(this); + b.prototype.toString = function(b) { + if (b) { + return p(this); } - var b = []; - this.visitInputs(function(a) { - b.push(k(a)); + var a = []; + this.visitInputs(function(b) { + a.push(p(b)); }); - a = k(this) + " = " + this.nodeName.toUpperCase(); - b.length && (a += " " + b.join(", ")); - return a; + b = p(this) + " = " + this.nodeName.toUpperCase(); + a.length && (b += " " + a.join(", ")); + return b; }; - a.prototype.visitInputsNoConstants = function(a) { - this.visitInputs(function(b) { - f.isConstant(b) || a(b); + b.prototype.visitInputsNoConstants = function(b) { + this.visitInputs(function(d) { + a.isConstant(d) || b(d); }); }; - a.prototype.replaceInput = function(b, c) { - var h = 0, d; - for (d in this) { - var e = this[d]; - e instanceof a && e === b && (this[d] = c, h++); - e instanceof Array && (h += e.replace(b, c)); + b.prototype.replaceInput = function(a, d) { + var g = 0, e; + for (e in this) { + var f = this[e]; + f instanceof b && f === a && (this[e] = d, g++); + f instanceof Array && (g += f.replace(a, d)); } - return h; + return g; }; - a._nextID = []; - return a; + b._nextID = []; + return b; }(); - f.Node = n; - n.prototype.nodeName = "Node"; - var p = function(a) { - function b() { - a.call(this); + a.Node = m; + m.prototype.nodeName = "Node"; + var t = function(b) { + function a() { + b.call(this); } - __extends(b, a); - return b; - }(n); - f.Control = p; - p.prototype.nodeName = "Control"; - var e = function(a) { - function b(c) { - a.call(this); - this.predecessors = c ? [c] : []; + __extends(a, b); + return a; + }(m); + a.Control = t; + t.prototype.nodeName = "Control"; + var q = function(b) { + function a(d) { + b.call(this); + this.predecessors = d ? [d] : []; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - g(this.predecessors, a); + __extends(a, b); + a.prototype.visitInputs = function(b) { + h(this.predecessors, b); }; - return b; - }(p); - f.Region = e; - e.prototype.nodeName = "Region"; - e = function(a) { - function b() { - a.call(this, null); + return a; + }(t); + a.Region = q; + q.prototype.nodeName = "Region"; + q = function(b) { + function a() { + b.call(this, null); this.control = this; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - g(this.predecessors, a); - a(this.scope); + __extends(a, b); + a.prototype.visitInputs = function(b) { + h(this.predecessors, b); + b(this.scope); }; - return b; - }(e); - f.Start = e; - e.prototype.nodeName = "Start"; - e = function(a) { - function b(c) { - a.call(this); - this.control = c; + return a; + }(q); + a.Start = q; + q.prototype.nodeName = "Start"; + q = function(b) { + function a(d) { + b.call(this); + this.control = d; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - a(this.control); + __extends(a, b); + a.prototype.visitInputs = function(b) { + b(this.control); }; - return b; - }(p); - f.End = e; - e.prototype.nodeName = "End"; - var q = function(a) { - function b(c, h, d) { - a.call(this, c); - this.store = h; - this.argument = d; - } - __extends(b, a); - b.prototype.visitInputs = function(a) { - a(this.control); - a(this.store); - a(this.argument); + return a; + }(t); + a.End = q; + q.prototype.nodeName = "End"; + var n = function(b) { + function a(d, g, e) { + b.call(this, d); + this.store = g; + this.argument = e; + } + __extends(a, b); + a.prototype.visitInputs = function(b) { + b(this.control); + b(this.store); + b(this.argument); }; - return b; - }(e); - f.Stop = q; - q.prototype.nodeName = "Stop"; - q = function(a) { - function b(c, h) { - a.call(this, c); - this.predicate = h; - } - __extends(b, a); - b.prototype.visitInputs = function(a) { - a(this.control); - a(this.predicate); + return a; + }(q); + a.Stop = n; + n.prototype.nodeName = "Stop"; + n = function(b) { + function a(d, g) { + b.call(this, d); + this.predicate = g; + } + __extends(a, b); + a.prototype.visitInputs = function(b) { + b(this.control); + b(this.predicate); }; - return b; - }(e); - f.If = q; - q.prototype.nodeName = "If"; - q = function(a) { - function b(c, h) { - a.call(this, c); - this.determinant = h; - } - __extends(b, a); - b.prototype.visitInputs = function(a) { - a(this.control); - a(this.determinant); + return a; + }(q); + a.If = n; + n.prototype.nodeName = "If"; + n = function(b) { + function a(d, g) { + b.call(this, d); + this.determinant = g; + } + __extends(a, b); + a.prototype.visitInputs = function(b) { + b(this.control); + b(this.determinant); }; - return b; - }(e); - f.Switch = q; - q.prototype.nodeName = "Switch"; - e = function(a) { - function b(c) { - a.call(this, c); - } - __extends(b, a); - b.prototype.visitInputs = function(a) { - a(this.control); + return a; + }(q); + a.Switch = n; + n.prototype.nodeName = "Switch"; + q = function(b) { + function a(d) { + b.call(this, d); + } + __extends(a, b); + a.prototype.visitInputs = function(b) { + b(this.control); }; - return b; - }(e); - f.Jump = e; - e.prototype.nodeName = "Jump"; - var l = function(a) { - function b() { - a.call(this); + return a; + }(q); + a.Jump = q; + q.prototype.nodeName = "Jump"; + var k = function(b) { + function a() { + b.call(this); } - __extends(b, a); - return b; - }(n); - f.Value = l; - l.prototype.nodeName = "Value"; - e = function(a) { - function b() { - a.call(this); + __extends(a, b); + return a; + }(m); + a.Value = k; + k.prototype.nodeName = "Value"; + q = function(b) { + function a() { + b.call(this); } - __extends(b, a); - return b; - }(l); - f.Store = e; - e.prototype.nodeName = "Store"; - e = function(a) { - function b(c, h) { - a.call(this); - this.control = c; - this.store = h; + __extends(a, b); + return a; + }(k); + a.Store = q; + q.prototype.nodeName = "Store"; + q = function(b) { + function a(d, g) { + b.call(this); + this.control = d; + this.store = g; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - this.control && a(this.control); - this.store && a(this.store); - this.loads && g(this.loads, a); + __extends(a, b); + a.prototype.visitInputs = function(b) { + this.control && b(this.control); + this.store && b(this.store); + this.loads && h(this.loads, b); }; - return b; - }(l); - f.StoreDependent = e; - e.prototype.nodeName = "StoreDependent"; - q = function(a) { - function b(c, h, d, e, l, n) { - a.call(this, c, h); - this.callee = d; - this.object = e; - this.args = l; - this.flags = n; - } - __extends(b, a); - b.prototype.visitInputs = function(a) { - this.control && a(this.control); - this.store && a(this.store); - this.loads && g(this.loads, a); - a(this.callee); - this.object && a(this.object); - g(this.args, a); + return a; + }(k); + a.StoreDependent = q; + q.prototype.nodeName = "StoreDependent"; + n = function(b) { + function a(d, g, e, f, k, c) { + b.call(this, d, g); + this.callee = e; + this.object = f; + this.args = k; + this.flags = c; + } + __extends(a, b); + a.prototype.visitInputs = function(b) { + this.control && b(this.control); + this.store && b(this.store); + this.loads && h(this.loads, b); + b(this.callee); + this.object && b(this.object); + h(this.args, b); }; - return b; - }(e); - f.Call = q; - q.prototype.nodeName = "Call"; - q = function(a) { - function b(c, h, d, e) { - a.call(this, c, h); - this.callee = d; - this.args = e; - } - __extends(b, a); - b.prototype.visitInputs = function(a) { - this.control && a(this.control); - this.store && a(this.store); - this.loads && g(this.loads, a); - a(this.callee); - g(this.args, a); + return a; + }(q); + a.Call = n; + n.prototype.nodeName = "Call"; + n = function(b) { + function a(d, g, e, f) { + b.call(this, d, g); + this.callee = e; + this.args = f; + } + __extends(a, b); + a.prototype.visitInputs = function(b) { + this.control && b(this.control); + this.store && b(this.store); + this.loads && h(this.loads, b); + b(this.callee); + h(this.args, b); }; - return b; - }(e); - f.New = q; - q.prototype.nodeName = "New"; - q = function(a) { - function b(c, h, d, e) { - a.call(this, c, h); - this.object = d; - this.name = e; + return a; + }(q); + a.New = n; + n.prototype.nodeName = "New"; + n = function(b) { + function a(d, g, e, f) { + b.call(this, d, g); + this.object = e; + this.name = f; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - this.control && a(this.control); - this.store && a(this.store); - this.loads && g(this.loads, a); - a(this.object); - a(this.name); + __extends(a, b); + a.prototype.visitInputs = function(b) { + this.control && b(this.control); + this.store && b(this.store); + this.loads && h(this.loads, b); + b(this.object); + b(this.name); }; - return b; - }(e); - f.GetProperty = q; - q.prototype.nodeName = "GetProperty"; - q = function(a) { - function b(c, h, d, e, l) { - a.call(this, c, h); - this.object = d; - this.name = e; - this.value = l; + return a; + }(q); + a.GetProperty = n; + n.prototype.nodeName = "GetProperty"; + n = function(b) { + function a(d, g, e, f, k) { + b.call(this, d, g); + this.object = e; + this.name = f; + this.value = k; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - this.control && a(this.control); - this.store && a(this.store); - this.loads && g(this.loads, a); - a(this.object); - a(this.name); - a(this.value); + __extends(a, b); + a.prototype.visitInputs = function(b) { + this.control && b(this.control); + this.store && b(this.store); + this.loads && h(this.loads, b); + b(this.object); + b(this.name); + b(this.value); }; - return b; - }(e); - f.SetProperty = q; - q.prototype.nodeName = "SetProperty"; - q = function(a) { - function b(c, h, d, e) { - a.call(this, c, h); - this.object = d; - this.name = e; + return a; + }(q); + a.SetProperty = n; + n.prototype.nodeName = "SetProperty"; + n = function(b) { + function a(d, g, e, f) { + b.call(this, d, g); + this.object = e; + this.name = f; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - this.control && a(this.control); - this.store && a(this.store); - this.loads && g(this.loads, a); - a(this.object); - a(this.name); + __extends(a, b); + a.prototype.visitInputs = function(b) { + this.control && b(this.control); + this.store && b(this.store); + this.loads && h(this.loads, b); + b(this.object); + b(this.name); }; - return b; - }(e); - f.DeleteProperty = q; - q.prototype.nodeName = "DeleteProperty"; - e = function(a) { - function b(c, h, d, e, l, n) { - a.call(this, c, h); - this.object = d; - this.name = e; - this.args = l; - this.flags = n; + return a; + }(q); + a.DeleteProperty = n; + n.prototype.nodeName = "DeleteProperty"; + q = function(b) { + function a(d, g, e, f, k, c) { + b.call(this, d, g); + this.object = e; + this.name = f; + this.args = k; + this.flags = c; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - this.control && a(this.control); - this.store && a(this.store); - this.loads && g(this.loads, a); - a(this.object); - a(this.name); - g(this.args, a); + __extends(a, b); + a.prototype.visitInputs = function(b) { + this.control && b(this.control); + this.store && b(this.store); + this.loads && h(this.loads, b); + b(this.object); + b(this.name); + h(this.args, b); }; - return b; - }(e); - f.CallProperty = e; - e.prototype.nodeName = "CallProperty"; - var u = function(a) { - function b(c, h) { - a.call(this); - this.control = this.control = c; - this.args = h ? [h] : []; + return a; + }(q); + a.CallProperty = q; + q.prototype.nodeName = "CallProperty"; + var f = function(b) { + function a(d, g) { + b.call(this); + this.control = this.control = d; + this.args = g ? [g] : []; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - this.control && a(this.control); - g(this.args, a); + __extends(a, b); + a.prototype.visitInputs = function(b) { + this.control && b(this.control); + h(this.args, b); }; - b.prototype.seal = function() { + a.prototype.seal = function() { this.sealed = !0; }; - b.prototype.pushValue = function(a) { - m(!this.sealed); - this.args.push(a); + a.prototype.pushValue = function(b) { + u(!this.sealed); + this.args.push(b); }; - return b; - }(l); - f.Phi = u; - u.prototype.nodeName = "Phi"; - var w = function(a) { - function b(c) { - a.call(this); - this.name = c; + return a; + }(k); + a.Phi = f; + f.prototype.nodeName = "Phi"; + var d = function(b) { + function a(d) { + b.call(this); + this.name = d; } - __extends(b, a); - return b; - }(l); - f.Variable = w; - w.prototype.nodeName = "Variable"; - e = function(a) { - function b(c) { - a.call(this); - this.argument = c; + __extends(a, b); + return a; + }(k); + a.Variable = d; + d.prototype.nodeName = "Variable"; + q = function(b) { + function a(d) { + b.call(this); + this.argument = d; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - a(this.argument); + __extends(a, b); + a.prototype.visitInputs = function(b) { + b(this.argument); }; - return b; - }(l); - f.Copy = e; - e.prototype.nodeName = "Copy"; - e = function(a) { - function b(c, h) { - a.call(this); - this.to = c; - this.from = h; + return a; + }(k); + a.Copy = q; + q.prototype.nodeName = "Copy"; + q = function(b) { + function a(d, g) { + b.call(this); + this.to = d; + this.from = g; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - a(this.to); - a(this.from); + __extends(a, b); + a.prototype.visitInputs = function(b) { + b(this.to); + b(this.from); }; - return b; - }(l); - f.Move = e; - e.prototype.nodeName = "Move"; - (function(a) { - a[a.CASE = 0] = "CASE"; - a[a.TRUE = 1] = "TRUE"; - a[a.FALSE = 2] = "FALSE"; - a[a.STORE = 3] = "STORE"; - a[a.SCOPE = 4] = "SCOPE"; - })(f.ProjectionType || (f.ProjectionType = {})); - var r = function(a) { - function b(c, h, d) { - a.call(this); - this.argument = c; - this.type = h; - this.selector = d; - } - __extends(b, a); - b.prototype.visitInputs = function(a) { - a(this.argument); + return a; + }(k); + a.Move = q; + q.prototype.nodeName = "Move"; + (function(b) { + b[b.CASE = 0] = "CASE"; + b[b.TRUE = 1] = "TRUE"; + b[b.FALSE = 2] = "FALSE"; + b[b.STORE = 3] = "STORE"; + b[b.SCOPE = 4] = "SCOPE"; + })(a.ProjectionType || (a.ProjectionType = {})); + var b = function(b) { + function a(d, g, e) { + b.call(this); + this.argument = d; + this.type = g; + this.selector = e; + } + __extends(a, b); + a.prototype.visitInputs = function(b) { + b(this.argument); }; - b.prototype.project = function() { + a.prototype.project = function() { return this.argument; }; - return b; - }(l); - f.Projection = r; - r.prototype.nodeName = "Projection"; - e = function(a) { - function b(c, h, d, e) { - a.call(this); - this.control = c; - this.condition = h; - this.left = d; - this.right = e; - } - __extends(b, a); - b.prototype.visitInputs = function(a) { - this.control && a(this.control); - a(this.condition); - a(this.left); - a(this.right); + return a; + }(k); + a.Projection = b; + b.prototype.nodeName = "Projection"; + q = function(b) { + function a(d, g, e, f) { + b.call(this); + this.control = d; + this.condition = g; + this.left = e; + this.right = f; + } + __extends(a, b); + a.prototype.visitInputs = function(b) { + this.control && b(this.control); + b(this.condition); + b(this.left); + b(this.right); }; - return b; - }(l); - f.Latch = e; - e.prototype.nodeName = "Latch"; - e = function() { - function a(b, c, h) { - this.name = b; - this.evaluate = c; - this.isBinary = h; - a.byName[b] = this; + return a; + }(k); + a.Latch = q; + q.prototype.nodeName = "Latch"; + q = function() { + function b(a, d, g) { + this.name = a; + this.evaluate = d; + this.isBinary = g; + b.byName[a] = this; } - a.linkOpposites = function(a, b) { - a.not = b; + b.linkOpposites = function(b, a) { b.not = a; + a.not = b; }; - a.fromName = function(b) { - return a.byName[b]; + b.fromName = function(a) { + return b.byName[a]; }; - a.byName = c(); - a.ADD = new a("+", function(a, b) { - return a + b; + b.byName = Object.create(null); + b.ADD = new b("+", function(b, a) { + return b + a; }, !0); - a.SUB = new a("-", function(a, b) { - return a - b; + b.SUB = new b("-", function(b, a) { + return b - a; }, !0); - a.MUL = new a("*", function(a, b) { - return a * b; + b.MUL = new b("*", function(b, a) { + return b * a; }, !0); - a.DIV = new a("/", function(a, b) { - return a / b; + b.DIV = new b("/", function(b, a) { + return b / a; }, !0); - a.MOD = new a("%", function(a, b) { - return a % b; + b.MOD = new b("%", function(b, a) { + return b % a; }, !0); - a.AND = new a("&", function(a, b) { - return a & b; + b.AND = new b("&", function(b, a) { + return b & a; }, !0); - a.OR = new a("|", function(a, b) { - return a | b; + b.OR = new b("|", function(b, a) { + return b | a; }, !0); - a.XOR = new a("^", function(a, b) { - return a ^ b; + b.XOR = new b("^", function(b, a) { + return b ^ a; }, !0); - a.LSH = new a("<<", function(a, b) { - return a << b; + b.LSH = new b("<<", function(b, a) { + return b << a; }, !0); - a.RSH = new a(">>", function(a, b) { - return a >> b; + b.RSH = new b(">>", function(b, a) { + return b >> a; }, !0); - a.URSH = new a(">>>", function(a, b) { - return a >>> b; + b.URSH = new b(">>>", function(b, a) { + return b >>> a; }, !0); - a.SEQ = new a("===", function(a, b) { - return a === b; + b.SEQ = new b("===", function(b, a) { + return b === a; }, !0); - a.SNE = new a("!==", function(a, b) { - return a !== b; + b.SNE = new b("!==", function(b, a) { + return b !== a; }, !0); - a.EQ = new a("==", function(a, b) { - return a == b; + b.EQ = new b("==", function(b, a) { + return b == a; }, !0); - a.NE = new a("!=", function(a, b) { - return a != b; + b.NE = new b("!=", function(b, a) { + return b != a; }, !0); - a.LE = new a("<=", function(a, b) { - return a <= b; + b.LE = new b("<=", function(b, a) { + return b <= a; }, !0); - a.GT = new a(">", function(a, b) { - return a > b; + b.GT = new b(">", function(b, a) { + return b > a; }, !0); - a.LT = new a("<", function(a, b) { - return a < b; + b.LT = new b("<", function(b, a) { + return b < a; }, !0); - a.GE = new a(">=", function(a, b) { - return a >= b; + b.GE = new b(">=", function(b, a) { + return b >= a; }, !0); - a.PLUS = new a("+", function(a) { - return+a; + b.PLUS = new b("+", function(b) { + return+b; }, !1); - a.NEG = new a("-", function(a) { - return-a; + b.NEG = new b("-", function(b) { + return-b; }, !1); - a.TRUE = new a("!!", function(a) { - return!!a; + b.TRUE = new b("!!", function(b) { + return!!b; }, !1); - a.FALSE = new a("!", function(a) { - return!a; + b.FALSE = new b("!", function(b) { + return!b; }, !1); - a.TYPE_OF = new a("typeof", function(a) { - return typeof a; + b.TYPE_OF = new b("typeof", function(b) { + return typeof b; }, !1); - a.BITWISE_NOT = new a("~", function(a) { - return~a; + b.BITWISE_NOT = new b("~", function(b) { + return~b; }, !1); - a.AS_ADD = new a("+", function(a, b) { - return "string" === typeof a || "string" === typeof b ? String(a) + String(b) : a + b; + b.AS_ADD = new b("+", function(b, a) { + return "string" === typeof b || "string" === typeof a ? String(b) + String(a) : b + a; }, !0); - return a; + return b; }(); - f.Operator = e; - e.linkOpposites(e.SEQ, e.SNE); - e.linkOpposites(e.EQ, e.NE); - e.linkOpposites(e.TRUE, e.FALSE); - e = function(a) { - function b(c, h, d) { - a.call(this); - this.operator = c; - this.left = h; - this.right = d; - } - __extends(b, a); - b.prototype.visitInputs = function(a) { - a(this.left); - a(this.right); + a.Operator = q; + q.linkOpposites(q.SEQ, q.SNE); + q.linkOpposites(q.EQ, q.NE); + q.linkOpposites(q.TRUE, q.FALSE); + q = function(b) { + function a(d, g, e) { + b.call(this); + this.operator = d; + this.left = g; + this.right = e; + } + __extends(a, b); + a.prototype.visitInputs = function(b) { + b(this.left); + b(this.right); }; - return b; - }(l); - f.Binary = e; - e.prototype.nodeName = "Binary"; - e = function(a) { - function b(c, h) { - a.call(this); - this.operator = c; - this.argument = h; + return a; + }(k); + a.Binary = q; + q.prototype.nodeName = "Binary"; + q = function(b) { + function a(d, g) { + b.call(this); + this.operator = d; + this.argument = g; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - a(this.argument); + __extends(a, b); + a.prototype.visitInputs = function(b) { + b(this.argument); }; - return b; - }(l); - f.Unary = e; - e.prototype.nodeName = "Unary"; - var h = function(a) { - function b(c) { - a.call(this); - this.value = c; + return a; + }(k); + a.Unary = q; + q.prototype.nodeName = "Unary"; + var g = function(b) { + function a(d) { + b.call(this); + this.value = d; } - __extends(b, a); - return b; - }(l); - f.Constant = h; - h.prototype.nodeName = "Constant"; - e = function(a) { - function b(c) { - a.call(this); - this.name = c; + __extends(a, b); + return a; + }(k); + a.Constant = g; + g.prototype.nodeName = "Constant"; + q = function(b) { + function a(d) { + b.call(this); + this.name = d; } - __extends(b, a); - return b; - }(l); - f.GlobalProperty = e; - e.prototype.nodeName = "GlobalProperty"; - e = function(a) { - function b(c) { - a.call(this); - this.control = c; + __extends(a, b); + return a; + }(k); + a.GlobalProperty = q; + q.prototype.nodeName = "GlobalProperty"; + q = function(b) { + function a(d) { + b.call(this); + this.control = d; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - a(this.control); + __extends(a, b); + a.prototype.visitInputs = function(b) { + b(this.control); }; - return b; - }(l); - f.This = e; - e.prototype.nodeName = "This"; - e = function(a) { - function b(c, h) { - a.call(this); - this.control = c; - this.argument = h; + return a; + }(k); + a.This = q; + q.prototype.nodeName = "This"; + q = function(b) { + function a(d, g) { + b.call(this); + this.control = d; + this.argument = g; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - a(this.control); - a(this.argument); + __extends(a, b); + a.prototype.visitInputs = function(b) { + b(this.control); + b(this.argument); }; - return b; - }(l); - f.Throw = e; - e.prototype.nodeName = "Throw"; - e = function(a) { - function b(c) { - a.call(this); - this.control = c; + return a; + }(k); + a.Throw = q; + q.prototype.nodeName = "Throw"; + q = function(b) { + function a(d) { + b.call(this); + this.control = d; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - a(this.control); + __extends(a, b); + a.prototype.visitInputs = function(b) { + b(this.control); }; - return b; - }(l); - f.Arguments = e; - e.prototype.nodeName = "Arguments"; - e = function(a) { - function b(c, h, d) { - a.call(this); - this.control = c; - this.index = h; - this.name = d; + return a; + }(k); + a.Arguments = q; + q.prototype.nodeName = "Arguments"; + q = function(b) { + function a(d, g, e) { + b.call(this); + this.control = d; + this.index = g; + this.name = e; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - a(this.control); + __extends(a, b); + a.prototype.visitInputs = function(b) { + b(this.control); }; - return b; - }(l); - f.Parameter = e; - e.prototype.nodeName = "Parameter"; - e = function(a) { - function b(c, h) { - a.call(this); - this.control = c; - this.elements = h; + return a; + }(k); + a.Parameter = q; + q.prototype.nodeName = "Parameter"; + q = function(b) { + function a(d, g) { + b.call(this); + this.control = d; + this.elements = g; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - a(this.control); - g(this.elements, a); + __extends(a, b); + a.prototype.visitInputs = function(b) { + b(this.control); + h(this.elements, b); }; - return b; - }(l); - f.NewArray = e; - e.prototype.nodeName = "NewArray"; - e = function(a) { - function b(c, h) { - a.call(this); - this.control = c; - this.properties = h; + return a; + }(k); + a.NewArray = q; + q.prototype.nodeName = "NewArray"; + q = function(b) { + function a(d, g) { + b.call(this); + this.control = d; + this.properties = g; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - a(this.control); - g(this.properties, a); + __extends(a, b); + a.prototype.visitInputs = function(b) { + b(this.control); + h(this.properties, b); }; - return b; - }(l); - f.NewObject = e; - e.prototype.nodeName = "NewObject"; - e = function(a) { - function b(c, h) { - a.call(this); - this.key = c; - this.value = h; + return a; + }(k); + a.NewObject = q; + q.prototype.nodeName = "NewObject"; + q = function(b) { + function a(d, g) { + b.call(this); + this.key = d; + this.value = g; } - __extends(b, a); - b.prototype.visitInputs = function(a) { - a(this.key); - a(this.value); + __extends(a, b); + a.prototype.visitInputs = function(b) { + b(this.key); + b(this.value); }; - return b; - }(l); - f.KeyValuePair = e; - e.prototype.mustFloat = !0; - e.prototype.nodeName = "KeyValuePair"; - f.nameOf = k; - })(g.IR || (g.IR = {})); - })(k.Compiler || (k.Compiler = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - function g(a) { - return a.id; + return a; + }(k); + a.KeyValuePair = q; + q.prototype.mustFloat = !0; + q.prototype.nodeName = "KeyValuePair"; + a.nameOf = p; + })(a.IR || (a.IR = {})); + })(h.Compiler || (h.Compiler = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + function v(b) { + return b.id; } - function s(a) { - return a instanceof f.Phi; + function p(b) { + return b instanceof a.Phi; } - function m(a) { - return a instanceof f.Constant && a.value instanceof u; + function u(b) { + return b instanceof a.Constant && b.value instanceof d; + } + function l(b) { + return p(b) || b instanceof a.Store || q(b, 3); + } + function e(b) { + return b instanceof a.Constant; } - function d(a) { - return s(a) || a instanceof f.Store || p(a, 3); + function m(b) { + return b instanceof a.Control; } - function a(a) { - return a instanceof f.Constant; + function t(b) { + return b instanceof a.Value; } - function c(a) { - return a instanceof f.Control; - } - function n(a) { - return a instanceof f.Value; - } - function p(a, b) { - return a instanceof f.Projection && (!b || a.type === b); - } - function e(a) { - return a instanceof f.Projection ? a.project() : a; - } - var q = b.Debug.assert, l = b.Debug.unexpected, u = b.AVM2.ABC.Multiname, w = b.ArrayUtilities.top, r = b.IntegerUtilities.bitCount, h = b.ArrayUtilities.pushUnique, x = b.ArrayUtilities.unique; - f.isNotPhi = function(a) { - return!s(a); - }; - f.isPhi = s; - f.isScope = function(a) { - return s(a) || a instanceof f.ASScope || p(a, 4); - }; - f.isMultinameConstant = m; - f.isMultiname = function(a) { - return m(a) || a instanceof f.ASMultiname; - }; - f.isStore = d; - f.isConstant = a; - f.isControlOrNull = function(a) { - return c(a) || null === a; - }; - f.isStoreOrNull = function(a) { - return d(a) || null === a; - }; - f.isControl = c; - f.isValueOrNull = function(a) { - return n(a) || null === a; - }; - f.isValue = n; - f.isProjection = p; - f.Null = new f.Constant(null); - f.Undefined = new f.Constant(void 0); - f.True = new f.Constant(!0); - f.False = new f.Constant(!1); - var y = function() { - function a(b, c, h) { - this.id = b; - this.nodes = [c, h]; - this.region = c; + function q(b, d) { + return b instanceof a.Projection && (!d || b.type === d); + } + function n(b) { + return b instanceof a.Projection ? b.project() : b; + } + var k = c.Debug.assert, f = c.Debug.unexpected, d = c.AVM2.ABC.Multiname, b = c.ArrayUtilities.top, g = c.IntegerUtilities.bitCount, r = c.ArrayUtilities.pushUnique, w = c.ArrayUtilities.unique; + a.isNotPhi = function(b) { + return!p(b); + }; + a.isPhi = p; + a.isScope = function(b) { + return p(b) || b instanceof a.ASScope || q(b, 4); + }; + a.isMultinameConstant = u; + a.isMultiname = function(b) { + return u(b) || b instanceof a.ASMultiname; + }; + a.isStore = l; + a.isConstant = e; + a.isControlOrNull = function(b) { + return m(b) || null === b; + }; + a.isStoreOrNull = function(b) { + return l(b) || null === b; + }; + a.isControl = m; + a.isValueOrNull = function(b) { + return t(b) || null === b; + }; + a.isValue = t; + a.isProjection = q; + a.Null = new a.Constant(null); + a.Undefined = new a.Constant(void 0); + a.True = new a.Constant(!0); + a.False = new a.Constant(!1); + var z = function() { + function b(a, d, g) { + this.id = a; + this.nodes = [d, g]; + this.region = d; this.successors = []; this.predecessors = []; } - a.prototype.pushSuccessorAt = function(a, b) { - q(a); - q(!this.successors[b]); - this.successors[b] = a; - a.pushPredecessor(this); - }; - a.prototype.pushSuccessor = function(a, b) { - q(a); - this.successors.push(a); - b && a.pushPredecessor(this); - }; - a.prototype.pushPredecessor = function(a) { - q(a); - this.predecessors.push(a); - }; - a.prototype.visitNodes = function(a) { - for (var b = this.nodes, c = 0, h = b.length;c < h;c++) { - a(b[c]); - } - }; - a.prototype.visitSuccessors = function(a) { - for (var b = this.successors, c = 0, h = b.length;c < h;c++) { - a(b[c]); - } - }; - a.prototype.visitPredecessors = function(a) { - for (var b = this.predecessors, c = 0, h = b.length;c < h;c++) { - a(b[c]); - } - }; - a.prototype.append = function(a) { - q(2 <= this.nodes.length); - q(n(a), a); - q(!s(a)); - q(0 > this.nodes.indexOf(a)); - a.mustFloat || this.nodes.splice(this.nodes.length - 1, 0, a); + b.prototype.pushSuccessorAt = function(b, a) { + k(b); + k(!this.successors[a]); + this.successors[a] = b; + b.pushPredecessor(this); + }; + b.prototype.pushSuccessor = function(b, a) { + k(b); + this.successors.push(b); + a && b.pushPredecessor(this); + }; + b.prototype.pushPredecessor = function(b) { + k(b); + this.predecessors.push(b); + }; + b.prototype.visitNodes = function(b) { + for (var a = this.nodes, d = 0, g = a.length;d < g;d++) { + b(a[d]); + } + }; + b.prototype.visitSuccessors = function(b) { + for (var a = this.successors, d = 0, g = a.length;d < g;d++) { + b(a[d]); + } + }; + b.prototype.visitPredecessors = function(b) { + for (var a = this.predecessors, d = 0, g = a.length;d < g;d++) { + b(a[d]); + } + }; + b.prototype.append = function(b) { + k(2 <= this.nodes.length); + k(t(b), b); + k(!p(b)); + k(0 > this.nodes.indexOf(b)); + b.mustFloat || this.nodes.splice(this.nodes.length - 1, 0, b); }; - a.prototype.toString = function() { + b.prototype.toString = function() { return "B" + this.id + (this.name ? " (" + this.name + ")" : ""); }; - a.prototype.trace = function(a) { - a.writeLn(this.toString()); + b.prototype.trace = function(b) { + b.writeLn(this.toString()); }; - return a; + return b; }(); - f.Block = y; - var G = function() { - function c(a) { - this.exit = this.exit = a; - } - c.prototype.buildCFG = function() { - return C.fromDFG(this); - }; - c.preOrderDepthFirstSearch = function(a, b, c) { - var h = []; - a = [a]; - for (var d = a.push.bind(a), e;e = a.pop();) { - 1 !== h[e.id] && (h[e.id] = 1, c(e), a.push(e), b(e, d)); + a.Block = z; + var A = function() { + function d(b) { + this.exit = this.exit = b; + } + d.prototype.buildCFG = function() { + return M.fromDFG(this); + }; + d.preOrderDepthFirstSearch = function(b, a, d) { + var g = []; + b = [b]; + for (var e = b.push.bind(b), f;f = b.pop();) { + 1 !== g[f.id] && (g[f.id] = 1, d(f), b.push(f), a(f, e)); } }; - c.postOrderDepthFirstSearch = function(a, b, c) { - function h(a) { - d[a.id] || e.push(a); + d.postOrderDepthFirstSearch = function(a, d, g) { + function e(b) { + f[b.id] || k.push(b); } - for (var d = [], e = [a];a = w(e);) { - d[a.id] ? (1 === d[a.id] && (d[a.id] = 2, c(a)), e.pop()) : (d[a.id] = 1, b(a, h)); + for (var f = [], k = [a];a = b(k);) { + f[a.id] ? (1 === f[a.id] && (f[a.id] = 2, g(a)), k.pop()) : (f[a.id] = 1, d(a, e)); } }; - c.prototype.forEachInPreOrderDepthFirstSearch = function(b) { - function c(b) { - a(b) || (q(b instanceof f.Node), d.push(b)); + d.prototype.forEachInPreOrderDepthFirstSearch = function(b) { + function d(b) { + e(b) || (k(b instanceof a.Node), f.push(b)); } - for (var h = Array(1024), d = [this.exit], e;e = d.pop();) { - h[e.id] || (h[e.id] = 1, b && b(e), d.push(e), e.visitInputs(c)); + for (var g = Array(1024), f = [this.exit], c;c = f.pop();) { + g[c.id] || (g[c.id] = 1, b && b(c), f.push(c), c.visitInputs(d)); } }; - c.prototype.forEach = function(a, b) { - (b ? c.postOrderDepthFirstSearch : c.preOrderDepthFirstSearch)(this.exit, function(a, b) { - a.visitInputsNoConstants(b); - }, a); - }; - c.prototype.traceMetrics = function(a) { - var h = new b.Metrics.Counter(!0); - c.preOrderDepthFirstSearch(this.exit, function(a, b) { - a.visitInputsNoConstants(b); - }, function(a) { - k.countTimeline(a.nodeName); + d.prototype.forEach = function(b, a) { + (a ? d.postOrderDepthFirstSearch : d.preOrderDepthFirstSearch)(this.exit, function(b, a) { + b.visitInputsNoConstants(a); + }, b); + }; + d.prototype.traceMetrics = function(b) { + var a = new c.Metrics.Counter(!0); + d.preOrderDepthFirstSearch(this.exit, function(b, a) { + b.visitInputsNoConstants(a); + }, function(b) { + h.countTimeline(b.nodeName); }); - h.trace(a); + a.trace(b); }; - c.prototype.trace = function(a) { - function b(a) { - return a instanceof f.Control ? "yellow" : a instanceof f.Phi ? "purple" : a instanceof f.Value ? "green" : "white"; + d.prototype.trace = function(b) { + function d(b) { + return b instanceof a.Control ? "yellow" : b instanceof a.Phi ? "purple" : b instanceof a.Value ? "green" : "white"; } - function c(a) { - return a instanceof f.Projection ? a.project() : a; + function g(b) { + return b instanceof a.Projection ? b.project() : b; + } + function e(b) { + b = g(b); + k[b.id] || (k[b.id] = !0, b.block && c.push(b.block), f.push(b), b.visitInputsNoConstants(e)); } - function h(a) { - a = c(a); - e[a.id] || (e[a.id] = !0, a.block && l.push(a.block), d.push(a), a.visitInputsNoConstants(h)); - } - var d = [], e = {}, l = []; - h(this.exit); - a.writeLn(""); - a.enter("digraph DFG {"); - a.writeLn("graph [bgcolor = gray10];"); - a.writeLn("edge [color = white];"); - a.writeLn("node [shape = box, fontname = Consolas, fontsize = 11, color = white, fontcolor = white];"); - a.writeLn("rankdir = BT;"); - l.forEach(function(b) { - a.enter("subgraph cluster" + b.nodes[0].id + " { bgcolor = gray20;"); - b.visitNodes(function(b) { - b = c(b); - a.writeLn("N" + b.id + ";"); + var f = [], k = {}, c = []; + e(this.exit); + b.writeLn(""); + b.enter("digraph DFG {"); + b.writeLn("graph [bgcolor = gray10];"); + b.writeLn("edge [color = white];"); + b.writeLn("node [shape = box, fontname = Consolas, fontsize = 11, color = white, fontcolor = white];"); + b.writeLn("rankdir = BT;"); + c.forEach(function(a) { + b.enter("subgraph cluster" + a.nodes[0].id + " { bgcolor = gray20;"); + a.visitNodes(function(a) { + a = g(a); + b.writeLn("N" + a.id + ";"); }); - a.leave("}"); + b.leave("}"); }); - d.forEach(function(c) { - a.writeLn("N" + c.id + ' [label = "' + c.toString() + '", color = "' + b(c) + '"];'); + f.forEach(function(a) { + b.writeLn("N" + a.id + ' [label = "' + a.toString() + '", color = "' + d(a) + '"];'); }); - d.forEach(function(h) { - h.visitInputsNoConstants(function(d) { - d = c(d); - a.writeLn("N" + h.id + " -> N" + d.id + " [color=" + b(d) + "];"); + f.forEach(function(a) { + a.visitInputsNoConstants(function(e) { + e = g(e); + b.writeLn("N" + a.id + " -> N" + e.id + " [color=" + d(e) + "];"); }); }); - a.leave("}"); - a.writeLn(""); + b.leave("}"); + b.writeLn(""); }; - return c; + return d; }(); - f.DFG = G; - var I = function() { - function a() { + a.DFG = A; + var B = function() { + function b() { this.entries = []; } - a.prototype.addUse = function(a, b) { - var c = this.entries[a.id]; - c || (c = this.entries[a.id] = {def:a, uses:[]}); - h(c.uses, b); - }; - a.prototype.trace = function(a) { - a.enter("> Uses"); - this.entries.forEach(function(b) { - a.writeLn(b.def.id + " -> [" + b.uses.map(g).join(", ") + "] " + b.def); + b.prototype.addUse = function(b, a) { + var d = this.entries[b.id]; + d || (d = this.entries[b.id] = {def:b, uses:[]}); + r(d.uses, a); + }; + b.prototype.trace = function(b) { + b.enter("> Uses"); + this.entries.forEach(function(a) { + b.writeLn(a.def.id + " -> [" + a.uses.map(v).join(", ") + "] " + a.def); }); - a.leave("<"); + b.leave("<"); }; - a.prototype.replace = function(a, b) { - var c = this.entries[a.id]; - if (0 === c.uses.length) { + b.prototype.replace = function(b, a) { + var d = this.entries[b.id]; + if (0 === d.uses.length) { return!1; } - var h = 0; - c.uses.forEach(function(c) { - h += c.replaceInput(a, b); + var g = 0; + d.uses.forEach(function(d) { + g += d.replaceInput(b, a); }); - q(h >= c.uses.length); - c.uses = []; + k(g >= d.uses.length); + d.uses = []; return!0; }; - a.prototype.updateUses = function(a, b, c, h) { - c = c[a.id]; - if (0 === c.uses.length) { + b.prototype.updateUses = function(b, a, d, g) { + d = d[b.id]; + if (0 === d.uses.length) { return!1; } - var d = 0; - c.uses.forEach(function(c) { - d += c.replaceInput(a, b); + var e = 0; + d.uses.forEach(function(d) { + e += d.replaceInput(b, a); }); - q(d >= c.uses.length); - c.uses = []; + k(e >= d.uses.length); + d.uses = []; return!0; }; - return a; + return b; }(); - f.Uses = I; - var C = function() { - function c() { + a.Uses = B; + var M = function() { + function b() { this.nextBlockID = 0; this.blocks = []; } - c.fromDFG = function(a) { - function b(a) { - a instanceof f.Projection && (a = a.project()); - q(a instanceof f.End || a instanceof f.Start, a); - if (!d[a.id]) { - d[a.id] = !0; - var c = a.control; - c instanceof f.Region || (c = a.control = new f.Region(c)); - a = c.block = h.buildBlock(c, a); - c instanceof f.Start && (h.root = a); - for (var e = 0;e < c.predecessors.length;e++) { - var l = c.predecessors[e], n, r = !1; - l instanceof f.Projection ? (n = l.project(), r = 1 === l.type) : n = l; - n instanceof f.Region && (n = new f.Jump(l), n = new f.Projection(n, 1), c.predecessors[e] = n, n = n.project(), r = !0); - b(n); - var g = n.control.block; - n instanceof f.Switch ? (q(p(l, 0)), g.pushSuccessorAt(a, l.selector.value)) : r && 0 < g.successors.length ? (g.pushSuccessor(a, !0), g.hasFlippedSuccessors = !0) : g.pushSuccessor(a, !0); + b.fromDFG = function(d) { + function g(b) { + b instanceof a.Projection && (b = b.project()); + k(b instanceof a.End || b instanceof a.Start, b); + if (!f[b.id]) { + f[b.id] = !0; + var d = b.control; + d instanceof a.Region || (d = b.control = new a.Region(d)); + b = d.block = e.buildBlock(d, b); + d instanceof a.Start && (e.root = b); + for (var c = 0;c < d.predecessors.length;c++) { + var r = d.predecessors[c], m, w = !1; + r instanceof a.Projection ? (m = r.project(), w = 1 === r.type) : m = r; + m instanceof a.Region && (m = new a.Jump(r), m = new a.Projection(m, 1), d.predecessors[c] = m, m = m.project(), w = !0); + g(m); + var l = m.control.block; + m instanceof a.Switch ? (k(q(r, 0)), l.pushSuccessorAt(b, r.selector.value)) : w && 0 < l.successors.length ? (l.pushSuccessor(b, !0), l.hasFlippedSuccessors = !0) : l.pushSuccessor(b, !0); } } } - var h = new c; - q(a && a instanceof G); - h.dfg = a; - var d = []; - b(a.exit); - h.splitCriticalEdges(); - h.exit = a.exit.control.block; - h.computeDominators(); - return h; + var e = new b; + k(d && d instanceof A); + e.dfg = d; + var f = []; + g(d.exit); + e.splitCriticalEdges(); + e.exit = d.exit.control.block; + e.computeDominators(); + return e; }; - c.prototype.buildRootAndExit = function() { - q(!this.root && !this.exit); - 0 < this.blocks[0].predecessors.length ? (this.root = new y(this.nextBlockID++), this.blocks.push(this.root), this.root.pushSuccessor(this.blocks[0], !0)) : this.root = this.blocks[0]; - for (var a = [], b = 0;b < this.blocks.length;b++) { - var c = this.blocks[b]; - 0 === c.successors.length && a.push(c); + b.prototype.buildRootAndExit = function() { + k(!this.root && !this.exit); + 0 < this.blocks[0].predecessors.length ? (this.root = new z(this.nextBlockID++), this.blocks.push(this.root), this.root.pushSuccessor(this.blocks[0], !0)) : this.root = this.blocks[0]; + for (var b = [], a = 0;a < this.blocks.length;a++) { + var d = this.blocks[a]; + 0 === d.successors.length && b.push(d); } - if (0 === a.length) { - l("Must have an exit block."); + if (0 === b.length) { + f("Must have an exit block."); } else { - if (1 === a.length && a[0] !== this.root) { - this.exit = a[0]; + if (1 === b.length && b[0] !== this.root) { + this.exit = b[0]; } else { - for (this.exit = new y(this.nextBlockID++), this.blocks.push(this.exit), b = 0;b < a.length;b++) { - a[b].pushSuccessor(this.exit, !0); + for (this.exit = new z(this.nextBlockID++), this.blocks.push(this.exit), a = 0;a < b.length;a++) { + b[a].pushSuccessor(this.exit, !0); } } } - q(this.root && this.exit); - q(this.root !== this.exit); + k(this.root && this.exit); + k(this.root !== this.exit); }; - c.prototype.fromString = function(a, b) { - function c(a) { - var b = d[a]; - if (b) { - return b; + b.prototype.fromString = function(b, a) { + function d(b) { + var a = e[b]; + if (a) { + return a; } - d[a] = b = new y(h.nextBlockID++); - b.name = a; - e.push(b); - return b; + e[b] = a = new z(g.nextBlockID++); + a.name = b; + f.push(a); + return a; } - var h = this, d = h.blockNames || (h.blockNames = {}), e = h.blocks; - a.replace(/\ /g, "").split(",").forEach(function(a) { - a = a.split("->"); - for (var b = null, h = 0;h < a.length;h++) { - var d = a[h]; - if (b) { - var e = d; - c(b).pushSuccessor(c(e), !0); + var g = this, e = g.blockNames || (g.blockNames = {}), f = g.blocks; + b.replace(/\ /g, "").split(",").forEach(function(b) { + b = b.split("->"); + for (var a = null, g = 0;g < b.length;g++) { + var e = b[g]; + if (a) { + var f = e; + d(a).pushSuccessor(d(f), !0); } else { - c(d); + d(e); } - b = d; + a = e; } }); - q(b && d[b]); - this.root = d[b]; + k(a && e[a]); + this.root = e[a]; }; - c.prototype.buildBlock = function(a, b) { - var c = new y(this.nextBlockID++, a, b); - this.blocks.push(c); - return c; + b.prototype.buildBlock = function(b, a) { + var d = new z(this.nextBlockID++, b, a); + this.blocks.push(d); + return d; }; - c.prototype.createBlockSet = function() { - this.setConstructor || (this.setConstructor = b.BitSets.BitSetFunctor(this.blocks.length)); + b.prototype.createBlockSet = function() { + this.setConstructor || (this.setConstructor = c.BitSets.BitSetFunctor(this.blocks.length)); return new this.setConstructor; }; - c.prototype.computeReversePostOrder = function() { + b.prototype.computeReversePostOrder = function() { if (this.order) { return this.order; } - var a = this.order = []; - this.depthFirstSearch(null, a.push.bind(a)); - a.reverse(); - for (var b = 0;b < a.length;b++) { - a[b].rpo = b; + var b = this.order = []; + this.depthFirstSearch(null, b.push.bind(b)); + b.reverse(); + for (var a = 0;a < b.length;a++) { + b[a].rpo = a; } - return a; + return b; }; - c.prototype.depthFirstSearch = function(a, b) { - function c(d) { - h.set(d.id); - a && a(d); - for (var e = d.successors, l = 0, n = e.length;l < n;l++) { - var r = e[l]; - h.get(r.id) || c(r); + b.prototype.depthFirstSearch = function(b, a) { + function d(e) { + g.set(e.id); + b && b(e); + for (var f = e.successors, k = 0, c = f.length;k < c;k++) { + var r = f[k]; + g.get(r.id) || d(r); } - b && b(d); + a && a(e); } - var h = this.createBlockSet(); - c(this.root); + var g = this.createBlockSet(); + d(this.root); }; - c.prototype.computeDominators = function() { - function a(b) { - var c; - if (void 0 !== b.dominatorDepth) { - return b.dominatorDepth; + b.prototype.computeDominators = function() { + function b(a) { + var d; + if (void 0 !== a.dominatorDepth) { + return a.dominatorDepth; } - c = b.dominator ? a(b.dominator) + 1 : 0; - return b.dominatorDepth = c; + d = a.dominator ? b(a.dominator) + 1 : 0; + return a.dominatorDepth = d; } - q(0 === this.root.predecessors.length, "Root node " + this.root + " must not have predecessors."); - for (var b = new Int32Array(this.blocks.length), c = 0;c < b.length;c++) { - b[c] = -1; - } - var h = this.createBlockSet(); - this.depthFirstSearch(function(a) { - for (var c = a.successors, d = 0, e = c.length;d < e;d++) { - var l = c[d].id, n = a.id, r = l; - if (!(0 > b[l])) { - l = b[l]; - for (h.clearAll();0 <= l;) { - h.set(l), l = b[l]; + k(0 === this.root.predecessors.length, "Root node " + this.root + " must not have predecessors."); + for (var a = new Int32Array(this.blocks.length), d = 0;d < a.length;d++) { + a[d] = -1; + } + var g = this.createBlockSet(); + this.depthFirstSearch(function(b) { + for (var d = b.successors, e = 0, f = d.length;e < f;e++) { + var k = d[e].id, c = b.id, r = k; + if (!(0 > a[k])) { + k = a[k]; + for (g.clearAll();0 <= k;) { + g.set(k), k = a[k]; } - for (;0 <= n && !h.get(n);) { - n = b[n]; + for (;0 <= c && !g.get(c);) { + c = a[c]; } } - b[r] = n; + a[r] = c; } }); - for (var c = 0, d = this.blocks.length;c < d;c++) { - this.blocks[c].dominator = this.blocks[b[c]]; + for (var d = 0, e = this.blocks.length;d < e;d++) { + this.blocks[d].dominator = this.blocks[a[d]]; } - c = 0; - for (d = this.blocks.length;c < d;c++) { - a(this.blocks[c]); + d = 0; + for (e = this.blocks.length;d < e;d++) { + b(this.blocks[d]); } + return a; }; - c.prototype.computeLoops = function() { - function a(d) { - if (c.get(d.id)) { - return b.get(d.id) && (d.isLoopHeader || (q(32 > h, "Can't handle too many loops, fall back on BitMaps if it's a problem."), d.isLoopHeader = !0, d.loops = 1 << h, h += 1), q(1 === r(d.loops))), d.loops; + b.prototype.computeLoops = function() { + function b(f) { + if (d.get(f.id)) { + return a.get(f.id) && (f.isLoopHeader || (k(32 > e, "Can't handle too many loops, fall back on BitMaps if it's a problem."), f.isLoopHeader = !0, f.loops = 1 << e, e += 1), k(1 === g(f.loops))), f.loops; } - c.set(d.id); - b.set(d.id); - for (var e = 0, l = 0, n = d.successors.length;l < n;l++) { - e |= a(d.successors[l]); - } - d.isLoopHeader && (q(1 === r(d.loops)), e &= ~d.loops); - d.loops = e; - b.clear(d.id); - return e; + d.set(f.id); + a.set(f.id); + for (var c = 0, r = 0, m = f.successors.length;r < m;r++) { + c |= b(f.successors[r]); + } + f.isLoopHeader && (k(1 === g(f.loops)), c &= ~f.loops); + f.loops = c; + a.clear(f.id); + return c; } - var b = this.createBlockSet(), c = this.createBlockSet(), h = 0, d = a(this.root); - q(0 === d); + var a = this.createBlockSet(), d = this.createBlockSet(), e = 0, f = b(this.root); + k(0 === f); }; - c.prototype.computeUses = function() { - k.enterTimeline("computeUses"); - var a = this.dfg, b = new I; - a.forEachInPreOrderDepthFirstSearch(function(a) { - a.visitInputs(function(c) { - b.addUse(c, a); + b.prototype.computeUses = function() { + h.enterTimeline("computeUses"); + var b = this.dfg, a = new B; + b.forEachInPreOrderDepthFirstSearch(function(b) { + b.visitInputs(function(d) { + a.addUse(d, b); }); }); - k.leaveTimeline(); - return b; + h.leaveTimeline(); + return a; }; - c.prototype.verify = function() { - this.computeReversePostOrder().forEach(function(a) { - a.phis && a.phis.forEach(function(b) { - q(b.control === a.region); - q(b.args.length === a.predecessors.length); + b.prototype.verify = function() { + this.computeReversePostOrder().forEach(function(b) { + b.phis && b.phis.forEach(function(a) { + k(a.control === b.region); + k(a.args.length === b.predecessors.length); }); }); }; - c.prototype.optimizePhis = function() { - function a(b, c) { - c = x(c); - if (1 === c.length) { - return c[0]; - } - if (2 === c.length) { - if (c[0] === b) { - return c[1]; + b.prototype.optimizePhis = function() { + function b(a, d) { + d = w(d); + if (1 === d.length) { + return d[0]; + } + if (2 === d.length) { + if (d[0] === a) { + return d[1]; } - if (c[1] === b) { - return c[0]; + if (d[1] === a) { + return d[0]; } } - return b; + return a; } - var b = [], c = this.computeUses().entries; - c.forEach(function(a) { - s(a.def) && b.push(a.def); + var a = [], d = this.computeUses().entries; + d.forEach(function(b) { + p(b.def) && a.push(b.def); }); - for (var h = 0, d = 0, e = !0;e;) { - d++, e = !1, b.forEach(function(b) { - var d = a(b, b.args); - if (d !== b) { - var l = c[b.id]; - if (0 === l.uses.length) { - b = !1; + for (var g = 0, e = 0, f = !0;f;) { + e++, f = !1, a.forEach(function(a) { + var e = b(a, a.args); + if (e !== a) { + var c = d[a.id]; + if (0 === c.uses.length) { + a = !1; } else { - for (var n = 0, r = l.uses, f = 0, p = r.length;f < p;f++) { - n += r[f].replaceInput(b, d); + for (var r = 0, m = c.uses, w = 0, l = m.length;w < l;w++) { + r += m[w].replaceInput(a, e); } - q(n >= l.uses.length); - l.uses = []; - b = !0; + k(r >= c.uses.length); + c.uses = []; + a = !0; } - b && (e = !0, h++); + a && (f = !0, g++); } }); } }; - c.prototype.splitCriticalEdges = function() { - for (var a = this.blocks, b = [], c = 0;c < a.length;c++) { - var h = a[c].successors; - if (1 < h.length) { - for (var d = 0;d < h.length;d++) { - 1 < h[d].predecessors.length && b.push({from:a[c], to:h[d]}); + b.prototype.splitCriticalEdges = function() { + for (var b = this.blocks, d = [], g = 0;g < b.length;g++) { + var e = b[g].successors; + if (1 < e.length) { + for (var f = 0;f < e.length;f++) { + 1 < e[f].predecessors.length && d.push({from:b[g], to:e[f]}); } } } - for (var a = b.length, e;e = b.pop();) { - c = e.from.successors.indexOf(e.to); - h = e.to.predecessors.indexOf(e.from); - q(0 <= c && 0 <= h); - var d = e.to, l = d.region, n = new f.Region(l.predecessors[h]), r = new f.Jump(n), n = this.buildBlock(n, r); - l.predecessors[h] = new f.Projection(r, 1); - e = e.from; - e.successors[c] = n; - n.pushPredecessor(e); - n.pushSuccessor(d); - d.predecessors[h] = n; + for (var b = d.length, c;c = d.pop();) { + g = c.from.successors.indexOf(c.to); + e = c.to.predecessors.indexOf(c.from); + k(0 <= g && 0 <= e); + var f = c.to, r = f.region, m = new a.Region(r.predecessors[e]), w = new a.Jump(m), m = this.buildBlock(m, w); + r.predecessors[e] = new a.Projection(w, 1); + c = c.from; + c.successors[g] = m; + m.pushPredecessor(c); + m.pushSuccessor(f); + f.predecessors[e] = m; } - a && q(0 === this.splitCriticalEdges()); - return a; + b && k(0 === this.splitCriticalEdges()); + return b; }; - c.prototype.allocateVariables = function() { - function a(b) { - !(p(b, 3) || b instanceof f.SetProperty) && b instanceof f.Value && (b.variable = new f.Variable("v" + b.id)); - } - var b = this.computeReversePostOrder(); - b.forEach(function(b) { - b.nodes.forEach(a); - b.phis && b.phis.forEach(a); + b.prototype.allocateVariables = function() { + function b(d) { + !(q(d, 3) || d instanceof a.SetProperty) && d instanceof a.Value && (d.variable = new a.Variable("v" + d.id)); + } + var d = this.computeReversePostOrder(); + d.forEach(function(a) { + a.nodes.forEach(b); + a.phis && a.phis.forEach(b); }); - for (var c = [], h = 0;h < b.length;h++) { - var d = b[h], e = d.phis, d = d.predecessors; - if (e) { - for (var l = 0;l < e.length;l++) { - var n = e[l]; - arguments = n.args; - q(d.length === arguments.length); - for (var r = 0;r < d.length;r++) { - var g = d[r], m = arguments[r]; - m.abstract || p(m, 3) || (g = c[g.id] || (c[g.id] = []), m = m.variable || m, n.variable !== m && g.push(new f.Move(n.variable, m))); + for (var g = [], e = 0;e < d.length;e++) { + var f = d[e], c = f.phis, f = f.predecessors; + if (c) { + for (var r = 0;r < c.length;r++) { + var m = c[r], w = m.args; + k(f.length === w.length); + for (var l = 0;l < f.length;l++) { + var n = f[l], t = w[l]; + t.abstract || q(t, 3) || (n = g[n.id] || (g[n.id] = []), t = t.variable || t, m.variable !== t && n.push(new a.Move(m.variable, t))); } } } } - var x = this.blocks; - c.forEach(function(a, b) { - for (var c = x[b], h = 0;a.length;) { - for (var d = 0;d < a.length;d++) { - for (var e = a[d], l = 0;l < a.length;l++) { - if (d !== l && a[l].from === e.to) { - e = null; + var z = this.blocks; + g.forEach(function(b, d) { + for (var g = z[d], e = 0;b.length;) { + for (var f = 0;f < b.length;f++) { + for (var k = b[f], c = 0;c < b.length;c++) { + if (f !== c && b[c].from === k.to) { + k = null; break; } } - e && (a.splice(d--, 1), c.append(e)); + k && (b.splice(f--, 1), g.append(k)); } - if (a.length) { - for (e = a[0], l = new f.Variable("t" + h++), x[b].append(new f.Move(l, e.to)), d = 1;d < a.length;d++) { - a[d].from === e.to && (a[d].from = l); + if (b.length) { + for (k = b[0], c = new a.Variable("t" + e++), z[d].append(new a.Move(c, k.to)), f = 1;f < b.length;f++) { + b[f].from === k.to && (b[f].from = c); } } } }); }; - c.prototype.scheduleEarly = function() { - function b(a) { - return a.mustNotFloat || a.shouldNotFloat ? !1 : a.mustFloat || a.shouldFloat || a instanceof f.Parameter || a instanceof f.This || a instanceof f.Arguments ? !0 : a instanceof f.Binary || a instanceof f.Unary || a instanceof f.Parameter; + b.prototype.scheduleEarly = function() { + function b(d) { + return d.mustNotFloat || d.shouldNotFloat ? !1 : d.mustFloat || d.shouldFloat || d instanceof a.Parameter || d instanceof a.This || d instanceof a.Arguments ? !0 : d instanceof a.Binary || d instanceof a.Unary || d instanceof a.Parameter; } - function c(a) { - q(!p[a.id], "Already scheduled " + a); - p[a.id] = !0; - q(a.control, a); + function d(a) { + k(!m[a.id], "Already scheduled " + a); + m[a.id] = !0; + k(a.control, a); b(a) || a.control.block.append(a); } - function h(a, b) { - q(!a.control, a); - q(!p[a.id]); - q(b); - a.control = b; - c(a); + function g(b, a) { + k(!b.control, b); + k(!m[b.id]); + k(a); + b.control = a; + d(b); } - function d(b) { - var r = []; + function f(b) { + var k = []; b.visitInputs(function(b) { - a(b) || n(b) && r.push(e(b)); + e(b) || t(b) && k.push(n(b)); }); - for (var g = 0;g < r.length;g++) { - var q = r[g]; - s(q) || p[q.id] || d(q); + for (var r = 0;r < k.length;r++) { + var w = k[r]; + p(w) || m[w.id] || f(w); } if (b.control) { - b instanceof f.End || b instanceof f.Phi || b instanceof f.Start || p[b.id] || c(b); + b instanceof a.End || b instanceof a.Phi || b instanceof a.Start || m[b.id] || d(b); } else { - if (r.length) { - q = r[0].control; - for (g = 1;g < r.length;g++) { - var m = r[g].control; - q.block.dominatorDepth < m.block.dominatorDepth && (q = m); + if (k.length) { + w = k[0].control; + for (r = 1;r < k.length;r++) { + var l = k[r].control; + w.block.dominatorDepth < l.block.dominatorDepth && (w = l); } - h(b, q); + g(b, w); } else { - h(b, l.root.region); + g(b, c.root.region); } } } - var l = this, r = this.dfg, p = [], g = []; - r.forEachInPreOrderDepthFirstSearch(function(a) { - a instanceof f.Region || a instanceof f.Jump || (a.control && g.push(a), s(a) && a.args.forEach(function(a) { + var c = this, r = this.dfg, m = [], w = []; + r.forEachInPreOrderDepthFirstSearch(function(d) { + d instanceof a.Region || d instanceof a.Jump || (d.control && w.push(d), p(d) && d.args.forEach(function(a) { b(a) && (a.mustNotFloat = !0); })); }); - for (var m = 0;m < g.length;m++) { - var x = g[m]; - if (x instanceof f.Phi) { - var w = x.control.block; - (w.phis || (w.phis = [])).push(x); - } - x.control && d(x); - } - g.forEach(function(a) { - a = e(a); - a === r.start || a instanceof f.Region || q(a.control, "Node is not scheduled: " + a); + for (var l = 0;l < w.length;l++) { + var q = w[l]; + if (q instanceof a.Phi) { + var z = q.control.block; + (z.phis || (z.phis = [])).push(q); + } + q.control && f(q); + } + w.forEach(function(b) { + b = n(b); + b === r.start || b instanceof a.Region || k(b.control, "Node is not scheduled: " + b); }); }; - c.prototype.trace = function(a) { - function b(a) { - h[a.id] || (h[a.id] = !0, d.push(a), a.visitSuccessors(b)); + b.prototype.trace = function(b) { + function a(b) { + g[b.id] || (g[b.id] = !0, e.push(b), b.visitSuccessors(a)); } - function c(a) { - q(a); - return a === e ? "house" : a === l ? "invhouse" : "box"; + function d(b) { + k(b); + return b === f ? "house" : b === c ? "invhouse" : "box"; } - var h = [], d = [], e = this.root, l = this.exit; - b(e); - a.writeLn(""); - a.enter("digraph CFG {"); - a.writeLn("graph [bgcolor = gray10];"); - a.writeLn("edge [fontname = Consolas, fontsize = 11, color = white, fontcolor = white];"); - a.writeLn("node [shape = box, fontname = Consolas, fontsize = 11, color = white, fontcolor = white, style = filled];"); - a.writeLn("rankdir = TB;"); - d.forEach(function(b) { - var h = ""; - void 0 !== b.name && (h += " " + b.name); - void 0 !== b.rpo && (h += " O: " + b.rpo); - a.writeLn("B" + b.id + ' [label = "B' + b.id + h + '", fillcolor = "black", shape=' + c(b) + ", style=filled];"); + var g = [], e = [], f = this.root, c = this.exit; + a(f); + b.writeLn(""); + b.enter("digraph CFG {"); + b.writeLn("graph [bgcolor = gray10];"); + b.writeLn("edge [fontname = Consolas, fontsize = 11, color = white, fontcolor = white];"); + b.writeLn("node [shape = box, fontname = Consolas, fontsize = 11, color = white, fontcolor = white, style = filled];"); + b.writeLn("rankdir = TB;"); + e.forEach(function(a) { + var g = ""; + void 0 !== a.name && (g += " " + a.name); + void 0 !== a.rpo && (g += " O: " + a.rpo); + b.writeLn("B" + a.id + ' [label = "B' + a.id + g + '", fillcolor = "black", shape=' + d(a) + ", style=filled];"); }); - d.forEach(function(b) { - b.visitSuccessors(function(c) { - a.writeLn("B" + b.id + " -> B" + c.id); + e.forEach(function(a) { + a.visitSuccessors(function(d) { + b.writeLn("B" + a.id + " -> B" + d.id); }); - b.dominator && a.writeLn("B" + b.id + " -> B" + b.dominator.id + " [color = orange];"); - b.follow && a.writeLn("B" + b.id + " -> B" + b.follow.id + " [color = purple];"); + a.dominator && b.writeLn("B" + a.id + " -> B" + a.dominator.id + " [color = orange];"); + a.follow && b.writeLn("B" + a.id + " -> B" + a.follow.id + " [color = purple];"); }); - a.leave("}"); - a.writeLn(""); + b.leave("}"); + b.writeLn(""); }; - return c; + return b; }(); - f.CFG = C; - var E = function() { + a.CFG = M; + var N = function() { function b() { } - b.prototype.foldUnary = function(b, c) { - q(b instanceof f.Unary); - if (a(b.argument)) { - return new f.Constant(b.operator.evaluate(b.argument.value)); + b.prototype.foldUnary = function(b, d) { + k(b instanceof a.Unary); + if (e(b.argument)) { + return new a.Constant(b.operator.evaluate(b.argument.value)); } - if (c) { - var h = this.fold(b.argument, !0); - if (b.operator === f.Operator.TRUE) { - return h; - } - if (h instanceof f.Unary) { - if (b.operator === f.Operator.FALSE && h.operator === f.Operator.FALSE) { - return h.argument; + if (d) { + var g = this.fold(b.argument, !0); + if (b.operator === a.Operator.TRUE) { + return g; + } + if (g instanceof a.Unary) { + if (b.operator === a.Operator.FALSE && g.operator === a.Operator.FALSE) { + return g.argument; } } else { - return new f.Unary(b.operator, h); + return new a.Unary(b.operator, g); } } return b; }; - b.prototype.foldBinary = function(b, c) { - q(b instanceof f.Binary); - return a(b.left) && a(b.right) ? new f.Constant(b.operator.evaluate(b.left.value, b.right.value)) : b; + b.prototype.foldBinary = function(b, d) { + k(b instanceof a.Binary); + return e(b.left) && e(b.right) ? new a.Constant(b.operator.evaluate(b.left.value, b.right.value)) : b; }; - b.prototype.fold = function(a, b) { - return a instanceof f.Unary ? this.foldUnary(a, b) : a instanceof f.Binary ? this.foldBinary(a, b) : a; + b.prototype.fold = function(b, d) { + return b instanceof a.Unary ? this.foldUnary(b, d) : b instanceof a.Binary ? this.foldBinary(b, d) : b; }; return b; }(); - f.PeepholeOptimizer = E; - })(g.IR || (g.IR = {})); - })(k.Compiler || (k.Compiler = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - function f(a) { - F(a instanceof v); - return a; + a.PeepholeOptimizer = N; + })(a.IR || (a.IR = {})); + })(h.Compiler || (h.Compiler = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + function s(b) { + H(b instanceof Y); + return b; } - function t(a) { - return a instanceof v && b.isString(a.value); + function v(b) { + return b instanceof Y && c.isString(b.value); } - function s(a) { - return a instanceof v && b.isNumeric(a.value) ? !0 : a.ty && a.ty.isNumeric(); + function p(b) { + return b instanceof Y && c.isNumeric(b.value) ? !0 : b.ty && b.ty.isNumeric(); } - function m(a, b) { - return s(a) && s(b) || d(a) && d(b) ? !0 : !1; + function u(b, a) { + return p(b) && p(a) || l(b) && l(a) ? !0 : !1; } - function d(a) { - return t(a) ? !0 : a.ty && a.ty.isString(); + function l(b) { + return v(b) ? !0 : b.ty && b.ty.isString(); } - function a(a) { - return new v(a); + function e(b) { + return new Y(b); } - function c(a) { - switch(a) { + function m(b) { + switch(b) { case 161: - return H.SUB; + return X.SUB; case 162: - return H.MUL; + return X.MUL; case 163: - return H.DIV; + return X.DIV; case 164: - return H.MOD; + return X.MOD; case 165: - return H.LSH; + return X.LSH; case 166: - return H.RSH; + return X.RSH; case 167: - return H.URSH; + return X.URSH; case 168: - return H.AND; + return X.AND; case 169: - return H.OR; + return X.OR; case 170: - return H.XOR; + return X.XOR; case 20: - return H.NE; + return X.NE; case 26: - return H.SNE; + return X.SNE; case 19: ; case 171: - return H.EQ; + return X.EQ; case 25: ; case 172: - return H.SEQ; + return X.SEQ; case 21: ; case 173: - return H.LT; + return X.LT; case 22: ; case 174: - return H.LE; + return X.LE; case 23: ; case 175: - return H.GT; + return X.GT; case 24: ; case 176: - return H.GE; + return X.GE; case 144: - return H.NEG; + return X.NEG; case 196: - return H.NEG; + return X.NEG; case 197: - return H.ADD; + return X.ADD; case 198: - return H.SUB; + return X.SUB; case 199: - return H.MUL; + return X.MUL; case 17: - return H.TRUE; + return X.TRUE; case 18: - return H.FALSE; + return X.FALSE; case 150: - return H.FALSE; + return X.FALSE; case 151: - return H.BITWISE_NOT; + return X.BITWISE_NOT; default: - K(String(a)); + J("Invalid operator op: " + b); } } - function n(c, h, d) { - F(b.isString(d)); - d = d.split("."); - for (var e = 0;e < d.length;e++) { - h = new g.IR.GetProperty(null, c.store, h, a(d[e])), h.shouldFloat = !0, c.loads.push(h); + function t(b, d, g) { + H(c.isString(g)); + g = g.split("."); + for (var f = 0;f < g.length;f++) { + d = new a.IR.GetProperty(null, b.store, d, e(g[f])), d.shouldFloat = !0, b.loads.push(d); } - return h; - } - function p(a) { - a = new g.IR.GlobalProperty(a); - a.mustFloat = !0; - return a; + return d; } - function e(a, b) { - var c = new Z(a, b); - da && (c = da.fold(c)); - return c; + function q(b) { + b = new a.IR.GlobalProperty(b); + b.mustFloat = !0; + return b; } - function q(a, c, h) { - var d = new ea(a, c, h); - c.ty && c.ty !== b.AVM2.Verifier.Type.Any && c.ty === h.ty && (a === H.EQ ? d.operator = H.SEQ : a === H.NE && (d.operator = H.SNE)); - da && (d = da.fold(d)); + function n(b, a) { + var d = new ea(b, a); + fa && (d = fa.fold(d)); return d; } - function l(b) { - return q(H.OR, b, a(0)); + function k(b, a, d) { + var g = new x(b, a, d); + if (b === X.EQ || b === X.NE) { + if (a.ty && a.ty.isStrictComparableWith(d.ty)) { + g.operator = b === X.EQ ? X.SEQ : X.SNE; + } else { + if (!a.ty || a.ty.canBeXML() || !d.ty || d.ty.canBeXML()) { + g = new R(null, null, q("asEquals"), null, [a, d], 0), b === X.NE && (g = n(X.FALSE, g)); + } + } + } + fa && (g = fa.fold(g)); + return g; } - function u(b) { - return q(H.URSH, b, a(0)); + function f(b) { + return k(X.OR, b, e(0)); } - function w(a) { - return s(a) ? a : e(H.PLUS, a); + function d(b) { + return k(X.URSH, b, e(0)); } - function r(a) { - return e(H.FALSE, e(H.FALSE, a)); + function b(b) { + return p(b) ? b : n(X.PLUS, b); } - function h(a) { - a.shouldNotFloat = !0; - return a; + function g(b) { + return n(X.FALSE, n(X.FALSE, b)); } - function x(a, b) { - return new X(null, null, a, null, b, 4); + function r(b) { + b.shouldNotFloat = !0; + return b; } - function y(a, b) { - return x(p(a), [b]); + function w(b, a) { + return new R(null, null, b, null, a, 4); } - function G(a) { - return t(a) ? a : x(p("String"), [a]); + function z(b, a) { + return w(q(b), [a]); } - function I(a) { - return t(a) ? a : W(a) ? new v(z.asCoerceString(f(a).value)) : x(p("asCoerceString"), [a]); + function A(b) { + return v(b) ? b : w(q("String"), [b]); } - function C(a) { - F(a instanceof E); - return ma[E.getQualifiedName(a)]; + function B(b) { + return v(b) ? b : T(b) ? new Y(F.asCoerceString(s(b).value)) : w(q("asCoerceString"), [b]); } - var E = b.AVM2.ABC.Multiname, O = b.AVM2.ABC.InstanceInfo, K = b.Debug.notImplemented, F = b.Debug.assert, J = b.ArrayUtilities.top, A = b.ArrayUtilities.unique, B = b.ObjectUtilities.createEmptyObject, z = b.AVM2.Runtime, P = b.AVM2.Runtime.GlobalMultinameResolver, D = g.IR.Node, L = g.IR.Start, M = g.IR.Region, V = g.IR.Null, Q = g.IR.Undefined, U = g.IR.True, S = g.IR.False, aa = g.IR.This, $ = g.IR.Projection, ea = g.IR.Binary, Z = g.IR.Unary, v = g.IR.Constant, X = g.IR.Call, ba = g.IR.Phi, - R = g.IR.Stop, H = g.IR.Operator, Y = g.IR.Parameter, ga = g.IR.NewArray, ca = g.IR.NewObject, ka = g.IR.KeyValuePair, W = g.IR.isConstant, ha = new b.IndentingWriter, da = new g.IR.PeepholeOptimizer, ja = function() { - function a(b) { - "undefined" === typeof b && (b = 0); - this.id = a._nextID += 1; - this.index = b; + function M(b) { + return T(b) ? new Y(F.escapeXMLAttribute(s(b).value)) : w(q("escapeXMLAttribute"), [b]); + } + function N(b) { + return T(b) ? new Y(F.escapeXMLElement(s(b).value)) : w(q("escapeXMLElement"), [b]); + } + function K(b) { + H(b instanceof y); + return na[y.getQualifiedName(b)]; + } + var y = c.AVM2.ABC.Multiname, D = c.AVM2.ABC.InstanceInfo, L = c.Debug.notImplemented, H = c.Debug.assert, J = c.Debug.assertUnreachable, C = c.ArrayUtilities.top, E = c.ArrayUtilities.unique, F = c.AVM2.Runtime, I = c.AVM2.Runtime.GlobalMultinameResolver, G = a.IR.Node, Z = a.IR.Start, Q = a.IR.Region, S = a.IR.Null, O = a.IR.Undefined, P = a.IR.True, V = a.IR.False, $ = a.IR.This, W = a.IR.Projection, x = a.IR.Binary, ea = a.IR.Unary, Y = a.IR.Constant, R = a.IR.Call, U = a.IR.Phi, ba = a.IR.Stop, + X = a.IR.Operator, ga = a.IR.Parameter, ja = a.IR.NewArray, aa = a.IR.NewObject, ia = a.IR.KeyValuePair, T = a.IR.isConstant, da = new c.IndentingWriter, fa = new a.IR.PeepholeOptimizer, la = function() { + function b(a) { + void 0 === a && (a = 0); + this.id = b._nextID += 1; + this.index = a; this.local = []; this.stack = []; this.scope = []; - this.store = Q; + this.store = O; this.loads = []; - this.saved = Q; + this.saved = O; } - a.prototype.clone = function(b) { - var c = new a; - c.index = void 0 !== b ? b : this.index; - c.local = this.local.slice(0); - c.stack = this.stack.slice(0); - c.scope = this.scope.slice(0); - c.loads = this.loads.slice(0); - c.saved = this.saved; - c.store = this.store; - return c; + b.prototype.clone = function(a) { + var d = new b; + d.index = void 0 !== a ? a : this.index; + d.local = this.local.slice(0); + d.stack = this.stack.slice(0); + d.scope = this.scope.slice(0); + d.loads = this.loads.slice(0); + d.saved = this.saved; + d.store = this.store; + return d; }; - a.prototype.matches = function(a) { - return this.stack.length === a.stack.length && this.scope.length === a.scope.length && this.local.length === a.local.length; + b.prototype.matches = function(b) { + return this.stack.length === b.stack.length && this.scope.length === b.scope.length && this.local.length === b.local.length; }; - a.prototype.makeLoopPhis = function(b, c) { - function h(a) { - a = new ba(b, a); - a.isLoop = !0; - return a; + b.prototype.makeLoopPhis = function(a, d) { + function g(b) { + b = new U(a, b); + b.isLoop = !0; + return b; } - var d = new a; - F(b); - d.index = this.index; - d.local = this.local.map(function(a, b) { - return c[b] ? h(a) : a; + var e = new b; + H(a); + e.index = this.index; + e.local = this.local.map(function(b, a) { + return d[a] ? g(b) : b; }); - d.stack = this.stack.map(h); - d.scope = this.scope.map(h); - d.loads = this.loads.slice(0); - d.saved = this.saved; - d.store = h(this.store); - return d; + e.stack = this.stack.map(g); + e.scope = this.scope.map(g); + e.loads = this.loads.slice(0); + e.saved = this.saved; + e.store = g(this.store); + return e; }; - a.tryOptimizePhi = function(a) { - if (a instanceof ba) { - if (a.isLoop) { - return a; + b.tryOptimizePhi = function(b) { + if (b instanceof U) { + if (b.isLoop) { + return b; } - var b = A(a.args); - if (1 === b.length) { - return a.seal(), k.countTimeline("Builder: OptimizedPhi"), b[0]; + var a = E(b.args); + if (1 === a.length) { + return b.seal(), h.countTimeline("Builder: OptimizedPhi"), a[0]; } } - return a; + return b; }; - a.prototype.optimize = function() { - this.local = this.local.map(a.tryOptimizePhi); - this.stack = this.stack.map(a.tryOptimizePhi); - this.scope = this.scope.map(a.tryOptimizePhi); - this.saved = a.tryOptimizePhi(this.saved); - this.store = a.tryOptimizePhi(this.store); + b.prototype.optimize = function() { + this.local = this.local.map(b.tryOptimizePhi); + this.stack = this.stack.map(b.tryOptimizePhi); + this.scope = this.scope.map(b.tryOptimizePhi); + this.saved = b.tryOptimizePhi(this.saved); + this.store = b.tryOptimizePhi(this.store); }; - a.mergeValue = function(a, b, c) { - a = b instanceof ba && b.control === a ? b : new ba(a, b); - a.pushValue(c); - return a; + b.mergeValue = function(b, a, d) { + b = a instanceof U && a.control === b ? a : new U(b, a); + b.pushValue(d); + return b; }; - a.mergeValues = function(b, c, h) { - for (var d = 0;d < c.length;d++) { - c[d] = a.mergeValue(b, c[d], h[d]); + b.mergeValues = function(a, d, g) { + for (var e = 0;e < d.length;e++) { + d[e] = b.mergeValue(a, d[e], g[e]); } }; - a.prototype.merge = function(b, c) { - F(b); - F(this.matches(c), this + " !== " + c); - a.mergeValues(b, this.local, c.local); - a.mergeValues(b, this.stack, c.stack); - a.mergeValues(b, this.scope, c.scope); - this.store = a.mergeValue(b, this.store, c.store); + b.prototype.merge = function(a, d) { + H(a); + H(this.matches(d), this + " !== " + d); + b.mergeValues(a, this.local, d.local); + b.mergeValues(a, this.stack, d.stack); + b.mergeValues(a, this.scope, d.scope); + this.store = b.mergeValue(a, this.store, d.store); this.store.abstract = !0; }; - a.prototype.trace = function(a) { - a.writeLn(this.toString()); + b.prototype.trace = function(b) { + b.writeLn(this.toString()); }; - a.toBriefString = function(a) { - return a instanceof D ? a.toString(!0) : a; + b.toBriefString = function(b) { + return b instanceof G ? b.toString(!0) : b; }; - a.prototype.toString = function() { - return "<" + String(this.id + " @ " + this.index).padRight(" ", 10) + (" M: " + a.toBriefString(this.store)).padRight(" ", 14) + (" X: " + a.toBriefString(this.saved)).padRight(" ", 14) + (" $: " + this.scope.map(a.toBriefString).join(", ")).padRight(" ", 20) + (" L: " + this.local.map(a.toBriefString).join(", ")).padRight(" ", 40) + (" S: " + this.stack.map(a.toBriefString).join(", ")).padRight(" ", 60); + b.prototype.toString = function() { + return "<" + String(this.id + " @ " + this.index).padRight(" ", 10) + (" M: " + b.toBriefString(this.store)).padRight(" ", 14) + (" X: " + b.toBriefString(this.saved)).padRight(" ", 14) + (" $: " + this.scope.map(b.toBriefString).join(", ")).padRight(" ", 20) + (" L: " + this.local.map(b.toBriefString).join(", ")).padRight(" ", 40) + (" S: " + this.stack.map(b.toBriefString).join(", ")).padRight(" ", 60); }; - a._nextID = 0; - return a; - }(), N = y.bind(null, "asCoerceObject"), ma = B(); - ma[E.Int] = l; - ma[E.Uint] = u; - ma[E.Number] = w; - ma[E.String] = I; - ma[E.Object] = N; - ma[E.Boolean] = r; - var T = B(); - T[E.Int] = l; - T[E.Uint] = u; - T[E.Number] = y.bind(null, "Number"); - T[E.String] = y.bind(null, "String"); - T[E.Object] = y.bind(null, "Object"); - T[E.Boolean] = y.bind(null, "Boolean"); - var oa = y.bind(null, "Object"), pa = function() { - function d(a, b, c, h) { - this.builder = a; - this.region = b; - this.block = c; - this.state = h; - this.abc = a.abc; - this.methodInfoConstant = a.methodInfoConstant; - this.bytecodes = a.methodInfo.analysis.bytecodes; - this.constantPool = a.abc.constantPool; - this.traceBuilder = a.traceBuilder; - this.methodInfo = a.methodInfo; - } - d.prototype.popMultiname = function() { - var b = this.constantPool.multinames[this.bc.index], c, h = b.flags; - c = b.isRuntimeName() ? this.state.stack.pop() : a(b.name); - b.isRuntimeNamespace() ? (b = new ga(this.region, [this.state.stack.pop()]), F(!(b instanceof g.IR.GetProperty), "Cannot float node : " + b), b.shouldFloat = !0) : b = a(b.namespaces); - return new g.IR.ASMultiname(b, c, h); - }; - d.prototype.setIfStops = function(a) { - F(!this.stops); - a = new g.IR.If(this.region, a); - this.stops = [{control:new $(a, 2), target:this.bytecodes[this.bc.position + 1], state:this.state}, {control:new $(a, 1), target:this.bc.target, state:this.state}]; + b._nextID = 0; + return b; + }(), ca = z.bind(null, "asCoerceObject"), na = Object.create(null); + na[y.Int] = f; + na[y.Uint] = d; + na[y.Number] = b; + na[y.String] = B; + na[y.Object] = ca; + na[y.Boolean] = g; + var ma = Object.create(null); + ma[y.Int] = f; + ma[y.Uint] = d; + ma[y.Number] = z.bind(null, "Number"); + ma[y.String] = z.bind(null, "String"); + ma[y.Object] = z.bind(null, "Object"); + ma[y.Boolean] = z.bind(null, "Boolean"); + var ra = z.bind(null, "Object"), wa = function() { + function l(b, a, d, g) { + this.builder = b; + this.region = a; + this.block = d; + this.state = g; + this.abc = b.abc; + this.methodInfoConstant = b.methodInfoConstant; + this.bytecodes = b.methodInfo.analysis.bytecodes; + this.constantPool = b.abc.constantPool; + this.traceBuilder = b.traceBuilder; + this.methodInfo = b.methodInfo; + } + l.prototype.popMultiname = function() { + var b = this.constantPool.multinames[this.bc.index], d, g; + b.isRuntimeName() ? (d = this.state.stack.pop(), g = e(0)) : (d = e(b.name), g = e(b.flags)); + b.isRuntimeNamespace() ? (b = new ja(this.region, [this.state.stack.pop()]), H(!(b instanceof a.IR.GetProperty), "Cannot float node : " + b), b.shouldFloat = !0) : b = e(b.namespaces); + return new a.IR.ASMultiname(b, d, g); + }; + l.prototype.setIfStops = function(b) { + H(!this.stops); + b = new a.IR.If(this.region, b); + this.stops = [{control:new W(b, 2), target:this.bytecodes[this.bc.position + 1], state:this.state}, {control:new W(b, 1), target:this.bc.target, state:this.state}]; }; - d.prototype.setJumpStop = function() { - F(!this.stops); + l.prototype.setJumpStop = function() { + H(!this.stops); this.stops = [{control:this.region, target:this.bc.target, state:this.state}]; }; - d.prototype.setThrowStop = function() { - F(!this.stops); + l.prototype.setThrowStop = function() { + H(!this.stops); this.stops = []; }; - d.prototype.setReturnStop = function() { - F(!this.stops); + l.prototype.setReturnStop = function() { + H(!this.stops); this.stops = []; }; - d.prototype.setSwitchStops = function(b) { - F(!this.stops); + l.prototype.setSwitchStops = function(b) { + H(!this.stops); if (2 < this.bc.targets.length) { this.stops = []; - b = new g.IR.Switch(this.region, b); - for (var c = 0;c < this.bc.targets.length;c++) { - this.stops.push({control:new $(b, 0, a(c)), target:this.bc.targets[c], state:this.state}); + b = new a.IR.Switch(this.region, b); + for (var d = 0;d < this.bc.targets.length;d++) { + this.stops.push({control:new W(b, 0, e(d)), target:this.bc.targets[d], state:this.state}); } } else { - F(2 === this.bc.targets.length), b = q(H.SEQ, b, a(0)), b = new g.IR.If(this.region, b), this.stops = [{control:new $(b, 2), target:this.bc.targets[1], state:this.state}, {control:new $(b, 1), target:this.bc.targets[0], state:this.state}]; + H(2 === this.bc.targets.length), b = k(X.SEQ, b, e(0)), b = new a.IR.If(this.region, b), this.stops = [{control:new W(b, 2), target:this.bc.targets[1], state:this.state}, {control:new W(b, 1), target:this.bc.targets[0], state:this.state}]; } }; - d.prototype.savedScope = function() { + l.prototype.savedScope = function() { return this.state.saved; }; - d.prototype.topScope = function(a) { - var b = this.state.scope; - if (void 0 !== a) { - if (a < b.length) { - return b[b.length - 1 - a]; + l.prototype.topScope = function(b) { + var a = this.state.scope; + if (void 0 !== b) { + if (b < a.length) { + return a[a.length - 1 - b]; } - if (a === b.length) { + if (b === a.length) { return this.savedScope(); } - var c = this.savedScope(); - a -= b.length; - for (b = 0;b < a;b++) { - c = n(this.state, c, "parent"); + var d = this.savedScope(); + b -= a.length; + for (a = 0;a < b;a++) { + d = t(this.state, d, "parent"); } - return c; + return d; } - return 0 < b.length ? J(b) : this.savedScope(); + return 0 < a.length ? C(a) : this.savedScope(); }; - d.prototype.getGlobalScope = function() { + l.prototype.getGlobalScope = function() { var b = this.bc.ti; - return b && b.object ? a(b.object) : new g.IR.ASGlobal(null, this.savedScope()); + return b && b.object ? e(b.object) : new a.IR.ASGlobal(null, this.savedScope()); }; - d.prototype.getScopeObject = function(a) { - return a instanceof g.IR.ASScope ? a.object : n(this.state, a, "object"); + l.prototype.getScopeObject = function(b) { + return b instanceof a.IR.ASScope ? b.object : t(this.state, b, "object"); }; - d.prototype.findProperty = function(c, h) { - var d = this.bc.ti, e = new g.IR.ASFindProperty(this.region, this.state.store, this.topScope(), c, this.methodInfoConstant, h); - if (d) { - if (d.object) { - return d.object instanceof b.AVM2.Runtime.Global && !d.object.scriptInfo.executing ? e : a(d.object); + l.prototype.findProperty = function(b, d) { + var g = this.bc.ti, f = new a.IR.ASFindProperty(this.region, this.state.store, this.topScope(), b, this.methodInfoConstant, d); + if (g) { + if (g.object) { + return g.object instanceof c.AVM2.Runtime.Global && !g.object.scriptInfo.executing ? f : e(g.object); } - if (void 0 !== d.scopeDepth) { - return this.getScopeObject(this.topScope(d.scopeDepth)); + if (void 0 !== g.scopeDepth) { + return this.getScopeObject(this.topScope(g.scopeDepth)); } } - return e; + return f; }; - d.prototype.coerce = function(a, b) { - var c = C(a); - return c ? c(b) : b; + l.prototype.coerce = function(b, a) { + var d = K(b); + return d ? d(a) : a; }; - d.prototype.store = function(a) { - var b = this.state; - b.store = new $(a, 3); - a.loads = b.loads.slice(0); - b.loads.length = 0; - return a; + l.prototype.store = function(b) { + var a = this.state; + a.store = new W(b, 3); + b.loads = a.loads.slice(0); + a.loads.length = 0; + return b; }; - d.prototype.load = function(a) { - this.state.loads.push(a); - return a; + l.prototype.load = function(b) { + this.state.loads.push(b); + return b; }; - d.prototype.call = function(a, b, c) { - return this.store(new X(this.region, this.state.store, a, b, c, 4)); + l.prototype.call = function(b, a, d) { + return this.store(new R(this.region, this.state.store, b, a, d, 4)); }; - d.prototype.callCall = function(a, b, c) { - return this.store(new X(this.region, this.state.store, a, b, c, 16)); + l.prototype.callCall = function(b, a, d) { + return this.store(new R(this.region, this.state.store, b, a, d, 16)); }; - d.prototype.callProperty = function(b, c, h, d) { - var e = this.bc.ti, l = this.region, n = this.state; - if (e && e.trait) { - if (e.trait.isMethod()) { - return e = e.trait.holder instanceof O && e.trait.holder.isInterface() ? E.getPublicQualifiedName(E.getName(e.trait.name)) : E.getQualifiedName(e.trait.name), e = z.VM_OPEN_METHOD_PREFIX + e, this.store(new g.IR.CallProperty(l, n.store, b, a(e), h, 4)); + l.prototype.callProperty = function(b, d, g, f) { + var k = this.bc.ti, c = this.region, r = this.state; + if (k && k.trait) { + if (k.trait.isMethod()) { + return k = k.trait.holder instanceof D && k.trait.holder.isInterface() ? y.getPublicQualifiedName(y.getName(k.trait.name)) : y.getQualifiedName(k.trait.name), k = F.VM_OPEN_METHOD_PREFIX + k, this.store(new a.IR.CallProperty(c, r.store, b, e(k), g, 4)); } - if (e.trait.isClass()) { - c = e.trait.name; - F(c instanceof E); - if (c = T[E.getQualifiedName(c)]) { - return c(h[0]); + if (k.trait.isClass()) { + d = k.trait.name; + H(d instanceof y); + if (d = ma[y.getQualifiedName(d)]) { + return d(g[0]); } - e = E.getQualifiedName(e.trait.name); - return this.store(new g.IR.CallProperty(l, n.store, b, a(e), h, 16)); + k = y.getQualifiedName(k.trait.name); + return this.store(new a.IR.CallProperty(c, r.store, b, e(k), g, 16)); } } - return(e = this.resolveMultinameGlobally(c)) ? this.store(new g.IR.ASCallProperty(l, n.store, b, a(E.getQualifiedName(e)), h, 6, d)) : this.store(new g.IR.ASCallProperty(l, n.store, b, c, h, 4, d)); + return(k = this.resolveMultinameGlobally(d)) ? this.store(new a.IR.ASCallProperty(c, r.store, b, e(y.getQualifiedName(k)), g, 6, f)) : this.store(new a.IR.ASCallProperty(c, r.store, b, d, g, 4, f)); }; - d.prototype.getProperty = function(b, c, h) { - var d = this.bc.ti, e = this.region, l = this.state; - F(c instanceof g.IR.ASMultiname); - h = !!h; - if (d && d.trait) { - if (d.trait.isConst() && d.trait.hasDefaultValue) { - return a(d.trait.value); + l.prototype.getProperty = function(b, d, g) { + var f = this.bc.ti, k = this.region, c = this.state; + H(d instanceof a.IR.ASMultiname); + g = !!g; + if (f && f.trait) { + if (f.trait.isConst() && f.trait.hasDefaultValue) { + return e(f.trait.value); } - b = new g.IR.GetProperty(e, l.store, b, a(E.getQualifiedName(d.trait.name))); - return d.trait.isGetter() ? this.store(b) : this.load(b); + b = new a.IR.GetProperty(k, c.store, b, e(y.getQualifiedName(f.trait.name))); + return f.trait.isGetter() ? this.store(b) : this.load(b); } - if (s(c.name)) { - return this.store(new g.IR.ASGetProperty(e, l.store, b, c, 1)); + if (p(d.name)) { + return this.store(new a.IR.ASGetProperty(k, c.store, b, d, 1)); } - if (d = this.resolveMultinameGlobally(c)) { - return this.store(new g.IR.ASGetProperty(e, l.store, b, a(E.getQualifiedName(d)), 2 | (h ? 8 : 0))); + if (f = this.resolveMultinameGlobally(d)) { + return this.store(new a.IR.ASGetProperty(k, c.store, b, e(y.getQualifiedName(f)), 2 | (g ? 8 : 0))); } - k.countTimeline("Compiler: Slow ASGetProperty"); - return this.store(new g.IR.ASGetProperty(e, l.store, b, c, h ? 8 : 0)); + h.countTimeline("Compiler: Slow ASGetProperty"); + return this.store(new a.IR.ASGetProperty(k, c.store, b, d, g ? 8 : 0)); }; - d.prototype.setProperty = function(b, c, h) { - var d = this.bc.ti, e = this.region, l = this.state; - F(c instanceof g.IR.ASMultiname); - if (d && d.trait) { - (c = d.trait.typeName ? C(d.trait.typeName) : null) && (h = c(h)), this.store(new g.IR.SetProperty(e, l.store, b, a(E.getQualifiedName(d.trait.name)), h)); + l.prototype.setProperty = function(b, d, g) { + var f = this.bc.ti, k = this.region, c = this.state; + H(d instanceof a.IR.ASMultiname); + if (f && f.trait) { + (d = f.trait.typeName ? K(f.trait.typeName) : null) && (g = d(g)), this.store(new a.IR.SetProperty(k, c.store, b, e(y.getQualifiedName(f.trait.name)), g)); } else { - if (s(c.name)) { - return this.store(new g.IR.ASSetProperty(e, l.store, b, c, h, 1)); + if (p(d.name)) { + return this.store(new a.IR.ASSetProperty(k, c.store, b, d, g, 1)); } - this.resolveMultinameGlobally(c); - return this.store(new g.IR.ASSetProperty(e, l.store, b, c, h, 0)); + this.resolveMultinameGlobally(d); + return this.store(new a.IR.ASSetProperty(k, c.store, b, d, g, 0)); } }; - d.prototype.callSuper = function(b, c, h, d) { - var e = this.bc.ti, l = this.region, n = this.state; - return e && e.trait && e.trait.isMethod() && e.baseClass ? (b = z.VM_OPEN_METHOD_PREFIX + E.getQualifiedName(e.trait.name), e = this.getJSProperty(a(e.baseClass), "traitsPrototype." + b), this.call(e, c, d)) : this.store(new g.IR.ASCallSuper(l, n.store, c, h, d, 4, b)); + l.prototype.callSuper = function(b, d, g, f) { + var k = this.bc.ti, c = this.region, r = this.state; + return k && k.trait && k.trait.isMethod() && k.baseClass ? (b = F.VM_OPEN_METHOD_PREFIX + y.getQualifiedName(k.trait.name), k = this.getJSProperty(e(k.baseClass), "traitsPrototype." + b), this.call(k, d, f)) : this.store(new a.IR.ASCallSuper(c, r.store, d, g, f, 4, b)); }; - d.prototype.getSuper = function(b, c, h) { - var d = this.bc.ti, e = this.region, l = this.state; - return d && d.trait && d.trait.isGetter() && d.baseClass ? (b = z.VM_OPEN_GET_METHOD_PREFIX + E.getQualifiedName(d.trait.name), d = this.getJSProperty(a(d.baseClass), "traitsPrototype." + b), this.call(d, c, [])) : this.store(new g.IR.ASGetSuper(e, l.store, c, h, b)); + l.prototype.getSuper = function(b, d, g) { + var f = this.bc.ti, k = this.region, c = this.state; + return f && f.trait && f.trait.isGetter() && f.baseClass ? (b = F.VM_OPEN_GET_METHOD_PREFIX + y.getQualifiedName(f.trait.name), f = this.getJSProperty(e(f.baseClass), "traitsPrototype." + b), this.call(f, d, [])) : this.store(new a.IR.ASGetSuper(k, c.store, d, g, b)); }; - d.prototype.setSuper = function(b, c, h, d) { - var e = this.bc.ti, l = this.region, n = this.state; - e && e.trait && e.trait.isSetter() && e.baseClass ? (b = z.VM_OPEN_SET_METHOD_PREFIX + E.getQualifiedName(e.trait.name), e = this.getJSProperty(a(e.baseClass), "traitsPrototype." + b), this.call(e, c, [d])) : this.store(new g.IR.ASSetSuper(l, n.store, c, h, d, b)); + l.prototype.setSuper = function(b, d, g, f) { + var k = this.bc.ti, c = this.region, r = this.state; + return k && k.trait && k.trait.isSetter() && k.baseClass ? (b = F.VM_OPEN_SET_METHOD_PREFIX + y.getQualifiedName(k.trait.name), k = this.getJSProperty(e(k.baseClass), "traitsPrototype." + b), this.call(k, d, [f])) : this.store(new a.IR.ASSetSuper(c, r.store, d, g, f, b)); }; - d.prototype.constructSuper = function(b, c, h) { - var d = this.bc.ti; - if (d) { - if (d.noCallSuperNeeded) { + l.prototype.constructSuper = function(b, a, d) { + var g = this.bc.ti; + if (g) { + if (g.noCallSuperNeeded) { return; } - if (d.baseClass) { - b = this.getJSProperty(a(d.baseClass), "instanceConstructorNoInitialize"); - this.call(b, c, h); + if (g.baseClass) { + b = this.getJSProperty(e(g.baseClass), "instanceConstructorNoInitialize"); + this.call(b, a, d); return; } } b = this.getJSProperty(b, "object.baseClass.instanceConstructorNoInitialize"); - this.call(b, c, h); + this.call(b, a, d); }; - d.prototype.getSlot = function(b, c) { - var h = this.bc.ti, d = this.region, e = this.state; - if (h) { - var l = h.trait; - if (l) { - if (l.isConst() && h.trait.hasDefaultValue) { - return a(l.value); + l.prototype.getSlot = function(b, d) { + var g = this.bc.ti, f = this.region, k = this.state; + if (g) { + var c = g.trait; + if (c) { + if (c.isConst() && g.trait.hasDefaultValue) { + return e(c.value); } - h = E.getQualifiedName(l.name); - return this.store(new g.IR.GetProperty(d, e.store, b, a(h))); + g = y.getQualifiedName(c.name); + return this.store(new a.IR.GetProperty(f, k.store, b, e(g))); } } - return this.store(new g.IR.ASGetSlot(null, e.store, b, c)); + return this.store(new a.IR.ASGetSlot(null, k.store, b, d)); }; - d.prototype.setSlot = function(b, c, h) { - var d = this.bc.ti, e = this.region, l = this.state; - if (d && (d = d.trait)) { - c = E.getQualifiedName(d.name); - this.store(new g.IR.SetProperty(e, l.store, b, a(c), h)); + l.prototype.setSlot = function(b, d, g) { + var f = this.bc.ti, k = this.region, c = this.state; + if (f && (f = f.trait)) { + d = y.getQualifiedName(f.name); + this.store(new a.IR.SetProperty(k, c.store, b, e(d), g)); return; } - this.store(new g.IR.ASSetSlot(e, l.store, b, c, h)); + this.store(new a.IR.ASSetSlot(k, c.store, b, d, g)); }; - d.prototype.resolveMultinameGlobally = function(a) { - var c = a.namespaces, h = a.name; - if (b.AVM2.Runtime.globalMultinameAnalysis.value) { - if (W(c) && W(h) && !a.isAttribute()) { - if (!b.isNumeric(h.value) && b.isString(h.value) && h.value) { - return P.resolveMultiname(new E(c.value, h.value, a.flags)); + l.prototype.resolveMultinameGlobally = function(b) { + var a = b.namespaces, d = b.name; + if (c.AVM2.Runtime.globalMultinameAnalysis.value) { + if (T(a) && T(d) && !b.isAttribute()) { + if (!c.isNumeric(d.value) && c.isString(d.value) && d.value) { + return I.resolveMultiname(new y(a.value, d.value, b.flags)); } - k.countTimeline("GlobalMultinameResolver: Cannot resolve numeric or any names."); + h.countTimeline("GlobalMultinameResolver: Cannot resolve numeric or any names."); } else { - k.countTimeline("GlobalMultinameResolver: Cannot resolve runtime multiname or attribute."); + h.countTimeline("GlobalMultinameResolver: Cannot resolve runtime multiname or attribute."); } } }; - d.prototype.getJSProperty = function(a, b) { - return n(this.state, a, b); + l.prototype.getJSProperty = function(b, a) { + return t(this.state, b, a); }; - d.prototype.setJSProperty = function(b, c, h) { - this.store(new g.IR.SetProperty(null, this.state.store, b, a(c), h)); + l.prototype.setJSProperty = function(b, d, g) { + this.store(new a.IR.SetProperty(null, this.state.store, b, e(d), g)); }; - d.prototype.simplifyName = function(b) { - return b instanceof v && b.value instanceof E && E.isQName(b.value) ? a(E.getQualifiedName(b.value)) : b; + l.prototype.simplifyName = function(b) { + return b instanceof Y && b.value instanceof y && y.isQName(b.value) ? e(y.getQualifiedName(b.value)) : b; }; - d.prototype.getDescendants = function(a, b) { - var c = this.region, h = this.state; - b = this.simplifyName(b); - return new g.IR.ASGetDescendants(c, h.store, a, b); + l.prototype.getDescendants = function(b, d) { + var g = this.region, e = this.state; + d = this.simplifyName(d); + return new a.IR.ASGetDescendants(g, e.store, b, d); }; - d.prototype.truthyCondition = function(a) { - var b = this.state.stack, c; - a.isBinary && (c = b.pop()); - b = b.pop(); - a = c ? q(a, b, c) : e(a, b); - da && (a = da.fold(a, !0)); - return a; + l.prototype.truthyCondition = function(b) { + var a = this.state.stack, d; + b.isBinary && (d = a.pop()); + a = a.pop(); + b = d ? k(b, a, d) : n(b, a); + fa && (b = fa.fold(b, !0)); + return b; }; - d.prototype.negatedTruthyCondition = function(a) { - a = e(H.FALSE, this.truthyCondition(a)); - da && (a = da.fold(a, !0)); - return a; + l.prototype.negatedTruthyCondition = function(b) { + b = n(X.FALSE, this.truthyCondition(b)); + fa && (b = fa.fold(b, !0)); + return b; }; - d.prototype.pushExpression = function(a, b) { - var c = this.state.stack, h; - a.isBinary ? (h = c.pop(), c = c.pop(), b && (h = l(h), c = l(c)), this.push(q(a, c, h))) : (c = c.pop(), b && (c = l(c)), this.push(e(a, c))); + l.prototype.pushExpression = function(b, a) { + var d = this.state.stack, g; + b.isBinary ? (g = d.pop(), d = d.pop(), a && (g = f(g), d = f(d)), this.push(k(b, d, g))) : (d = d.pop(), a && (d = f(d)), this.push(n(b, d))); }; - d.prototype.push = function(a) { - var b = this.bc; - F(a instanceof g.IR.Node); - b.ti && !a.ty && (a.ty = b.ti.type); - this.state.stack.push(a); + l.prototype.push = function(b) { + var d = this.bc; + H(b instanceof a.IR.Node); + d.ti && !b.ty && (b.ty = d.ti.type); + this.state.stack.push(b); }; - d.prototype.pushLocal = function(a) { - this.push(this.state.local[a]); + l.prototype.pushLocal = function(b) { + this.push(this.state.local[b]); }; - d.prototype.popLocal = function(a) { - var b = this.state; - b.local[a] = h(b.stack.pop()); + l.prototype.popLocal = function(b) { + var a = this.state; + a.local[b] = r(a.stack.pop()); }; - d.prototype.build = function() { - function d() { - return y.pop(); + l.prototype.build = function() { + function l() { + return p.pop(); } - function e(a) { - return b.ArrayUtilities.popMany(y, a); + function n(b) { + return c.ArrayUtilities.popMany(p, b); } - var n = this.block, f = this.state, s = this.state.local, y = this.state.stack, t = this.state.scope, C = this.region, O = this.bytecodes, z, B, v, N = this.push.bind(this); + var t = this.block, z = this.state, s = this.state.local, p = this.state.stack, v = this.state.scope, D = this.region, K = this.bytecodes, J, C, E, x = this.push.bind(this); this.stops = null; - this.traceBuilder && (ha.writeLn("Processing Region: " + C + ", Block: " + n.bid), ha.enter(("> state: " + C.entryState.toString()).padRight(" ", 100))); - for (var J = n.position, P = n.end.position;J <= P;J++) { - this.bc = n = O[J]; - var A = n.op; - f.index = J; - switch(A) { + this.traceBuilder && (da.writeLn("Processing Region: " + D + ", Block: " + t.bid), da.enter(("> state: " + D.entryState.toString()).padRight(" ", 100))); + for (var F = t.position, I = t.end.position;F <= I;F++) { + this.bc = t = K[F]; + var G = t.op; + z.index = F; + switch(G) { case 3: - this.store(new g.IR.Throw(C, d())); - this.builder.stopPoints.push({region:C, store:f.store, value:Q}); + this.store(new a.IR.Throw(D, l())); + this.builder.stopPoints.push({region:D, store:z.store, value:O}); this.setThrowStop(); break; case 98: - this.pushLocal(n.index); + this.pushLocal(t.index); break; case 208: ; @@ -17481,10 +18493,10 @@ case 210: ; case 211: - this.pushLocal(A - 208); + this.pushLocal(G - 208); break; case 99: - this.popLocal(n.index); + this.popLocal(t.index); break; case 212: ; @@ -17493,251 +18505,257 @@ case 214: ; case 215: - this.popLocal(A - 212); + this.popLocal(G - 212); break; case 28: ; case 48: - t.push(new g.IR.ASScope(this.topScope(), d(), 28 === A)); + v.push(new a.IR.ASScope(this.topScope(), l(), 28 === G)); break; case 29: - t.pop(); + v.pop(); break; case 100: - N(this.getGlobalScope()); + x(this.getGlobalScope()); break; case 101: - N(this.getScopeObject(f.scope[n.index])); + x(this.getScopeObject(z.scope[t.index])); break; case 94: ; case 93: - N(this.findProperty(this.popMultiname(), 93 === A)); + x(this.findProperty(this.popMultiname(), 93 === G)); break; case 102: - v = this.popMultiname(); - B = d(); - N(this.getProperty(B, v, !1)); + E = this.popMultiname(); + C = l(); + x(this.getProperty(C, E, !1)); break; case 89: - v = this.popMultiname(); - B = d(); - N(this.getDescendants(B, v)); + E = this.popMultiname(); + C = l(); + x(this.getDescendants(C, E)); break; case 96: - v = this.popMultiname(); - N(this.getProperty(this.findProperty(v, !0), v, !1)); + E = this.popMultiname(); + x(this.getProperty(this.findProperty(E, !0), E, !1)); break; case 104: ; case 97: - z = d(); - v = this.popMultiname(); - B = d(); - this.setProperty(B, v, z); + J = l(); + E = this.popMultiname(); + C = l(); + this.setProperty(C, E, J); break; case 106: - v = this.popMultiname(); - B = d(); - N(this.store(new g.IR.ASDeleteProperty(C, f.store, B, v))); + E = this.popMultiname(); + C = l(); + x(this.store(new a.IR.ASDeleteProperty(D, z.store, C, E))); break; case 108: - B = d(); - N(this.getSlot(B, a(n.index))); + C = l(); + x(this.getSlot(C, e(t.index))); break; case 109: - z = d(); - B = d(); - this.setSlot(B, a(n.index), z); + J = l(); + C = l(); + this.setSlot(C, e(t.index), J); break; case 4: - v = this.popMultiname(); - B = d(); - N(this.getSuper(this.savedScope(), B, v)); + E = this.popMultiname(); + C = l(); + x(this.getSuper(this.savedScope(), C, E)); break; case 5: - z = d(); - v = this.popMultiname(); - B = d(); - this.setSuper(this.savedScope(), B, v, z); + J = l(); + E = this.popMultiname(); + C = l(); + this.setSuper(this.savedScope(), C, E, J); break; case 241: ; case 240: break; case 64: - N(x(this.builder.createFunctionCallee, [a(this.abc.methods[n.index]), this.topScope(), a(!0)])); + x(w(this.builder.createFunctionCallee, [e(this.abc.methods[t.index]), this.topScope(), e(!0)])); break; case 65: - z = e(n.argCount); - B = d(); - v = d(); - N(this.callCall(v, B, z)); + J = n(t.argCount); + C = l(); + E = l(); + x(this.callCall(E, C, J)); break; case 70: ; case 79: ; case 76: - z = e(n.argCount); - v = this.popMultiname(); - B = d(); - z = this.callProperty(B, v, z, 76 === A); - 79 !== A && N(z); + J = n(t.argCount); + E = this.popMultiname(); + C = l(); + J = this.callProperty(C, E, J, 76 === G); + 79 !== G && x(J); break; case 69: ; case 78: - v = this.popMultiname(); - z = e(n.argCount); - B = d(); - z = this.callSuper(this.savedScope(), B, v, z); - 78 !== A && N(z); + E = this.popMultiname(); + J = n(t.argCount); + C = l(); + J = this.callSuper(this.savedScope(), C, E, J); + 78 !== G && x(J); break; case 66: - z = e(n.argCount); - B = d(); - N(this.store(new g.IR.ASNew(C, f.store, B, z))); + J = n(t.argCount); + C = l(); + x(this.store(new a.IR.ASNew(D, z.store, C, J))); break; case 73: - z = e(n.argCount); - B = d(); - this.constructSuper(this.savedScope(), B, z); + J = n(t.argCount); + C = l(); + this.constructSuper(this.savedScope(), C, J); break; case 74: - z = e(n.argCount); - v = this.popMultiname(); - B = d(); - v = this.getProperty(B, v, !1); - N(this.store(new g.IR.ASNew(C, f.store, v, z))); + J = n(t.argCount); + E = this.popMultiname(); + C = l(); + E = this.getProperty(C, E, !1); + x(this.store(new a.IR.ASNew(D, z.store, E, J))); break; case 128: - if (n.ti && n.ti.noCoercionNeeded) { - k.countTimeline("Compiler: NoCoercionNeeded"); + if (t.ti && t.ti.noCoercionNeeded) { + h.countTimeline("Compiler: NoCoercionNeeded"); break; } else { - k.countTimeline("Compiler: CoercionNeeded"); + h.countTimeline("Compiler: CoercionNeeded"); } - z = d(); - N(this.coerce(this.constantPool.multinames[n.index], z)); + J = l(); + x(this.coerce(this.constantPool.multinames[t.index], J)); break; case 131: ; case 115: - N(l(d())); + x(f(l())); break; case 136: ; case 116: - N(u(d())); + x(d(l())); break; case 132: ; case 117: - N(w(d())); + x(b(l())); break; case 129: ; case 118: - N(r(d())); + x(g(l())); break; case 120: - N(this.call(p("checkFilter"), null, [d()])); + x(this.call(q("checkFilter"), null, [l()])); break; case 130: break; case 133: - N(I(d())); + x(B(l())); break; case 112: - N(G(d())); + x(A(l())); + break; + case 114: + x(M(l())); + break; + case 113: + x(N(l())); break; case 134: - if (n.ti && n.ti.noCoercionNeeded) { - k.countTimeline("Compiler: NoCoercionNeeded"); + if (t.ti && t.ti.noCoercionNeeded) { + h.countTimeline("Compiler: NoCoercionNeeded"); break; } else { - k.countTimeline("Compiler: CoercionNeeded"); + h.countTimeline("Compiler: CoercionNeeded"); } - z = d(); - B = this.constantPool.multinames[n.index]; - v = new g.IR.ASMultiname(a(B.namespaces), a(B.name), B.flags); - B = this.getProperty(this.findProperty(v, !1), v); - N(this.call(p("asAsType"), null, [B, z])); + J = l(); + C = this.constantPool.multinames[t.index]; + E = new a.IR.ASMultiname(e(C.namespaces), e(C.name), e(C.flags)); + C = this.getProperty(this.findProperty(E, !1), E); + x(this.call(q("asAsType"), null, [C, J])); break; case 135: - B = d(); - z = d(); - N(this.call(p("asAsType"), null, [B, z])); + C = l(); + J = l(); + x(this.call(q("asAsType"), null, [C, J])); break; case 72: ; case 71: - z = Q; - 72 === A && (z = d(), this.methodInfo.returnType && (n.ti && n.ti.noCoercionNeeded || (z = this.coerce(this.methodInfo.returnType, z)))); - this.builder.stopPoints.push({region:C, store:f.store, value:z}); + J = O; + 72 === G && (J = l(), this.methodInfo.returnType && (t.ti && t.ti.noCoercionNeeded || (J = this.coerce(this.methodInfo.returnType, J)))); + this.builder.stopPoints.push({region:D, store:z.store, value:J}); this.setReturnStop(); break; case 30: ; case 35: - z = d(); - B = d(); - N(new g.IR.CallProperty(C, f.store, B, a(30 === A ? "asNextName" : "asNextValue"), [z], 4)); + J = l(); + C = l(); + x(new a.IR.CallProperty(D, z.store, C, e(30 === G ? "asNextName" : "asNextValue"), [J], 4)); break; case 50: - z = new g.IR.ASNewHasNext2; - this.setJSProperty(z, "object", s[n.object]); - this.setJSProperty(z, "index", s[n.index]); - this.store(new g.IR.CallProperty(C, f.store, oa(s[n.object]), a("asHasNext2"), [z], 4)); - s[n.object] = this.getJSProperty(z, "object"); - N(s[n.index] = this.getJSProperty(z, "index")); + J = new a.IR.ASNewHasNext2; + this.setJSProperty(J, "object", s[t.object]); + this.setJSProperty(J, "index", s[t.index]); + this.store(new a.IR.CallProperty(D, z.store, ra(s[t.object]), e("asHasNext2"), [J], 4)); + s[t.object] = this.getJSProperty(J, "object"); + x(s[t.index] = this.getJSProperty(J, "index")); break; case 32: - N(V); + x(S); break; case 33: - N(Q); + x(O); break; case 38: - N(U); + x(P); break; case 39: - N(S); + x(V); break; case 40: - N(a(NaN)); + x(e(NaN)); break; case 34: - K(String(n)); + L(String(t)); break; case 36: ; case 37: - N(a(n.value)); + x(e(t.value)); break; case 44: - N(a(this.constantPool.strings[n.index])); + x(e(this.constantPool.strings[t.index])); break; case 45: - N(a(this.constantPool.ints[n.index])); + x(e(this.constantPool.ints[t.index])); break; case 46: - N(a(this.constantPool.uints[n.index])); + x(e(this.constantPool.uints[t.index])); break; case 47: - N(a(this.constantPool.doubles[n.index])); + x(e(this.constantPool.doubles[t.index])); break; case 41: - d(); + l(); break; case 42: - z = h(d()); - N(z); - N(z); + J = r(l()); + x(J); + x(J); break; case 43: - f.stack.push(d(), d()); + z.stack.push(l(), l()); break; case 239: ; @@ -17749,16 +18767,16 @@ this.setJumpStop(); break; case 12: - this.setIfStops(this.negatedTruthyCondition(H.LT)); + this.setIfStops(this.negatedTruthyCondition(X.LT)); break; case 15: - this.setIfStops(this.negatedTruthyCondition(H.GE)); + this.setIfStops(this.negatedTruthyCondition(X.GE)); break; case 14: - this.setIfStops(this.negatedTruthyCondition(H.GT)); + this.setIfStops(this.negatedTruthyCondition(X.GT)); break; case 13: - this.setIfStops(this.negatedTruthyCondition(H.LE)); + this.setIfStops(this.negatedTruthyCondition(X.LE)); break; case 24: ; @@ -17779,16 +18797,16 @@ case 25: ; case 26: - this.setIfStops(this.truthyCondition(c(A))); + this.setIfStops(this.truthyCondition(m(G))); break; case 27: - this.setSwitchStops(d()); + this.setSwitchStops(l()); break; case 160: - B = d(); - z = d(); - v = m(z, B) ? H.ADD : b.AVM2.Runtime.useAsAdd ? H.AS_ADD : H.ADD; - N(q(v, z, B)); + C = l(); + J = l(); + E = u(J, C) ? X.ADD : c.AVM2.Runtime.useAsAdd ? X.AS_ADD : X.ADD; + x(k(E, J, C)); break; case 161: ; @@ -17827,7 +18845,7 @@ case 150: ; case 151: - this.pushExpression(c(A)); + this.pushExpression(m(G)); break; case 196: ; @@ -17836,7 +18854,7 @@ case 198: ; case 199: - this.pushExpression(c(A), !0); + this.pushExpression(m(G), !0); break; case 145: ; @@ -17845,9 +18863,9 @@ case 147: ; case 193: - N(a(1)); - 145 === A || 147 === A ? N(w(d())) : N(l(d())); - 145 === A || 192 === A ? this.pushExpression(H.ADD) : this.pushExpression(H.SUB); + x(e(1)); + 145 === G || 147 === G ? x(b(l())) : x(f(l())); + 145 === G || 192 === G ? this.pushExpression(X.ADD) : this.pushExpression(X.SUB); break; case 146: ; @@ -17856,409 +18874,410 @@ case 148: ; case 195: - N(a(1)); - 146 === A || 148 === A ? N(w(s[n.index])) : N(l(s[n.index])); - 146 === A || 194 === A ? this.pushExpression(H.ADD) : this.pushExpression(H.SUB); - this.popLocal(n.index); + x(e(1)); + 146 === G || 148 === G ? x(b(s[t.index])) : x(f(s[t.index])); + 146 === G || 194 === G ? this.pushExpression(X.ADD) : this.pushExpression(X.SUB); + this.popLocal(t.index); break; case 177: - B = d(); - z = d(); - N(this.call(this.getJSProperty(B, "isInstanceOf"), null, [z])); + C = l(); + J = l(); + x(this.call(this.getJSProperty(C, "isInstanceOf"), null, [J])); break; case 178: - z = d(); - v = this.popMultiname(); - B = this.getProperty(this.findProperty(v, !1), v); - N(this.call(p("asIsType"), null, [B, z])); + J = l(); + E = this.popMultiname(); + C = this.getProperty(this.findProperty(E, !1), E); + x(this.call(q("asIsType"), null, [C, J])); break; case 179: - B = d(); - z = d(); - N(this.call(p("asIsType"), null, [B, z])); + C = l(); + J = l(); + x(this.call(q("asIsType"), null, [C, J])); break; case 180: - B = d(); - z = d(); - v = new g.IR.ASMultiname(Q, z, 0); - N(this.store(new g.IR.ASHasProperty(C, f.store, B, v))); + C = l(); + J = l(); + E = new a.IR.ASMultiname(O, J, e(0)); + x(this.store(new a.IR.ASHasProperty(D, z.store, C, E))); break; case 149: - N(this.call(p("asTypeOf"), null, [d()])); + x(this.call(q("asTypeOf"), null, [l()])); break; case 8: - N(Q); - this.popLocal(n.index); + x(O); + this.popLocal(t.index); break; case 83: - z = e(n.argCount); - B = d(); - v = p("applyType"); - N(this.call(v, null, [this.methodInfoConstant, B, new ga(C, z)])); + J = n(t.argCount); + C = l(); + E = q("applyType"); + x(this.call(E, null, [this.methodInfoConstant, C, new ja(D, J)])); break; case 86: - z = e(n.argCount); - N(new ga(C, z)); + J = n(t.argCount); + x(new ja(D, J)); break; case 85: - B = []; - for (v = 0;v < n.argCount;v++) { - z = d(); - var D = d(); - F(W(D) && b.isString(D.value)); - D = a(E.getPublicQualifiedName(D.value)); - B.push(new ka(D, z)); + C = []; + for (E = 0;E < t.argCount;E++) { + J = l(); + var Q = l(); + H(T(Q) && c.isString(Q.value)); + Q = e(y.getPublicQualifiedName(Q.value)); + C.push(new ia(Q, J)); } - N(new ca(C, B)); + x(new aa(D, C)); break; case 87: - N(new g.IR.ASNewActivation(a(this.methodInfo))); + x(new a.IR.ASNewActivation(e(this.methodInfo))); break; case 88: - v = p("createClass"); - N(this.call(v, null, [a(this.abc.classes[n.index]), d(), this.topScope()])); + E = q("createClass"); + x(this.call(E, null, [e(this.abc.classes[t.index]), l(), this.topScope()])); break; default: - K(String(n)); + L(String(t)); } - 239 !== A && 241 !== A && 240 !== A && this.traceBuilder && ha.writeLn(("state: " + f.toString()).padRight(" ", 100) + " : " + J + ", " + n.toString(this.abc)); + 239 !== G && 241 !== G && 240 !== G && this.traceBuilder && da.writeLn(("state: " + z.toString()).padRight(" ", 100) + " : " + F + ", " + t.toString(this.abc)); } - this.traceBuilder && ha.leave(("< state: " + f.toString()).padRight(" ", 100)); + this.traceBuilder && da.leave(("< state: " + z.toString()).padRight(" ", 100)); }; - return d; - }(), na = function() { - function c(a, h, d) { - F(a && a.abc && h); + return l; + }(), qa = function() { + function b(a, d, g) { + H(a && a.abc && d); this.abc = a.abc; - this.methodInfoConstant = new v(a); - this.scope = h; + this.methodInfoConstant = new Y(a); + this.scope = d; this.methodInfo = a; - this.hasDynamicScope = d; - this.traceBuilder = 3 < b.AVM2.Compiler.traceLevel.value; - this.createFunctionCallee = p("createFunction"); + this.hasDynamicScope = g; + this.traceBuilder = 3 < c.AVM2.Compiler.traceLevel.value; + this.createFunctionCallee = q("createFunction"); this.stopPoints = []; this.bytecodes = this.methodInfo.analysis.bytecodes; } - c.prototype.buildStart = function(b) { - var c = this.methodInfo, h = b.entryState = new ja(0); - h.local.push(new aa(b)); - for (var d = this.hasDynamicScope ? 1 : 0, e = c.parameters.length, l = 0;l < e;l++) { - h.local.push(new Y(b, d + l, c.parameters[l].name)); - } - for (l = e;l < c.localCount;l++) { - h.local.push(Q); - } - h.store = new $(b, 3); - b.scope = this.hasDynamicScope ? new Y(b, 0, z.SAVED_SCOPE_NAME) : new v(this.scope); - h.saved = new $(b, 4); - l = new g.IR.Arguments(b); - if (c.needsRest() || c.needsArguments()) { - var r = a(d + (c.needsRest() ? e : 0)); - h.local[e + 1] = new X(b, h.store, p("sliceArguments"), null, [l, r], 4); - } - b = n(h, l, "length"); - for (l = 0;l < e;l++) { - var f = c.parameters[l], r = l + 1, q = h.local[r]; - if (void 0 !== f.value) { - var m; - m = new g.IR.Binary(H.LT, b, a(d + l + 1)); - q = new g.IR.Latch(null, m, a(f.value), q); - } - f.type && !f.type.isAnyName() && (f = C(f.type)) && (q = f(q)); - h.local[r] = q; - } - }; - c.prototype.buildGraph = function() { - for (var a = this.methodInfo.analysis.blocks, c = this.traceBuilder, h = 0;h < a.length;h++) { - a[h].bdo = h, a[h].region = null; - } - var d = new b.SortedList(function(a, b) { - return a.block.bdo - b.block.bdo; - }), h = new L; - this.buildStart(h); - for (d.push({region:h, block:a[0]});a = d.pop();) { - this.buildBlock(a.region, a.block, a.region.entryState.clone()).forEach(function(a) { - var b = a.target, h = b.region; - if (h) { - c && ha.enter("Merging into region: " + h + " @ " + b.position + ", block " + b.bid + " {"), c && ha.writeLn(" R " + h.entryState), c && ha.writeLn("+ I " + a.state), h.entryState.merge(h, a.state), h.predecessors.push(a.control), c && ha.writeLn(" = " + h.entryState), c && ha.leave("}"); + b.prototype.buildStart = function(b) { + var d = this.methodInfo, g = b.entryState = new la(0); + g.local.push(new $(b)); + for (var f = this.hasDynamicScope ? 1 : 0, k = d.parameters.length, c = 0;c < k;c++) { + g.local.push(new ga(b, f + c, d.parameters[c].name)); + } + for (c = k;c < d.localCount;c++) { + g.local.push(O); + } + g.store = new W(b, 3); + b.scope = this.hasDynamicScope ? new ga(b, 0, F.SAVED_SCOPE_NAME) : new Y(this.scope); + g.saved = new W(b, 4); + c = new a.IR.Arguments(b); + if (d.needsRest() || d.needsArguments()) { + var r = e(f + (d.needsRest() ? k : 0)); + g.local[k + 1] = new R(b, g.store, q("sliceArguments"), null, [c, r], 4); + } + r = t(g, c, "length"); + for (c = 0;c < k;c++) { + var m = d.parameters[c], w = c + 1, l = g.local[w]; + if (void 0 !== m.value) { + var n; + n = new a.IR.Binary(X.LT, r, e(f + c + 1)); + l = new a.IR.Latch(null, n, e(m.value), l); + } + m.type && !m.type.isAnyName() && (m = K(m.type)) && (l = m(l)); + g.local[w] = l; + } + return b; + }; + b.prototype.buildGraph = function() { + for (var b = this.methodInfo.analysis.blocks, d = this.traceBuilder, g = 0;g < b.length;g++) { + b[g].bdo = g, b[g].region = null; + } + var e = new c.SortedList(function(b, a) { + return b.block.bdo - a.block.bdo; + }), g = new Z; + this.buildStart(g); + for (e.push({region:g, block:b[0]});b = e.pop();) { + this.buildBlock(b.region, b.block, b.region.entryState.clone()).forEach(function(b) { + var g = b.target, f = g.region; + if (f) { + d && da.enter("Merging into region: " + f + " @ " + g.position + ", block " + g.bid + " {"), d && da.writeLn(" R " + f.entryState), d && da.writeLn("+ I " + b.state), f.entryState.merge(f, b.state), f.predecessors.push(b.control), d && da.writeLn(" = " + f.entryState), d && da.leave("}"); } else { - var h = b.region = new M(a.control), e = null; - b.loop && (e = g.enableDirtyLocals.value && b.loop.getDirtyLocals(), c && ha.writeLn("Adding PHIs to loop region. " + e)); - h.entryState = b.loop ? a.state.makeLoopPhis(h, e) : a.state.clone(b.position); - c && ha.writeLn("Adding new region: " + h + " @ " + b.position + " to worklist."); - d.push({region:h, block:b}); - } - }), c && ha.enter("Worklist: {"), d.forEach(function(a) { - c && ha.writeLn(a.region + " " + a.block.bdo + " " + a.region.entryState); - }), c && ha.leave("}"); + var f = g.region = new Q(b.control), k = null; + g.loop && (k = a.enableDirtyLocals.value && g.loop.getDirtyLocals(), d && da.writeLn("Adding PHIs to loop region. " + k)); + f.entryState = g.loop ? b.state.makeLoopPhis(f, k) : b.state.clone(g.position); + d && da.writeLn("Adding new region: " + f + " @ " + g.position + " to worklist."); + e.push({region:f, block:g}); + } + }), d && da.enter("Worklist: {"), e.forEach(function(b) { + d && da.writeLn(b.region + " " + b.block.bdo + " " + b.region.entryState); + }), d && da.leave("}"); } - c && ha.writeLn("Done"); + d && da.writeLn("Done"); if (1 < this.stopPoints.length) { - var e = new M(null), l = new ba(e, null), n = new ba(e, null); - this.stopPoints.forEach(function(a) { - e.predecessors.push(a.region); - l.pushValue(a.value); - n.pushValue(a.store); + var f = new Q(null), k = new U(f, null), r = new U(f, null); + this.stopPoints.forEach(function(b) { + f.predecessors.push(b.region); + k.pushValue(b.value); + r.pushValue(b.store); }); - a = new R(e, n, l); + b = new ba(f, r, k); } else { - a = new R(this.stopPoints[0].region, this.stopPoints[0].store, this.stopPoints[0].value); + b = new ba(this.stopPoints[0].region, this.stopPoints[0].store, this.stopPoints[0].value); } - return new g.IR.DFG(a); + return new a.IR.DFG(b); }; - c.prototype.buildBlock = function(a, b, c) { - F(a && b && c); - c.optimize(); - var h = b.verifierEntryState; - if (h) { - this.traceBuilder && ha.writeLn("Type State: " + h); - for (var d = 0;d < h.local.length;d++) { - var e = h.local[d], l = c.local[d]; - l.ty || (l.ty = e); - } - } - b = new pa(this, a, b, c); - b.build(); - h = b.stops; - h || (h = [], b.bc.position + 1 <= this.bytecodes.length && h.push({control:a, target:this.bytecodes[b.bc.position + 1], state:c})); - return h; + b.prototype.buildBlock = function(b, a, d) { + H(b && a && d); + d.optimize(); + var g = a.verifierEntryState; + if (g) { + this.traceBuilder && da.writeLn("Type State: " + g); + for (var e = 0;e < g.local.length;e++) { + var f = g.local[e], k = d.local[e]; + k.ty || (k.ty = f); + } + } + a = new wa(this, b, a, d); + a.build(); + g = a.stops; + g || (g = [], a.bc.position + 1 <= this.bytecodes.length && g.push({control:b, target:this.bytecodes[a.bc.position + 1], state:d})); + return g; }; - c.buildMethod = function(a, h, d, e) { - F(d); - F(h.analysis); - F(!h.hasExceptions()); - k.countTimeline("Compiler: Compiled Methods"); - k.enterTimeline("Compiler"); - k.enterTimeline("Mark Loops"); - h.analysis.markLoops(); - k.leaveTimeline(); - b.AVM2.Verifier.enabled.value && (k.enterTimeline("Verify"), a.verifyMethod(h, d), k.leaveTimeline()); - a = 0 < b.AVM2.Compiler.traceLevel.value; - var l = 1 < b.AVM2.Compiler.traceLevel.value, n = 2 < b.AVM2.Compiler.traceLevel.value; + b.buildMethod = function(a, d, g, e) { + H(g); + H(d.analysis); + H(!d.hasExceptions()); + h.countTimeline("Compiler: Compiled Methods"); + h.enterTimeline("Compiler"); + h.enterTimeline("Mark Loops"); + d.analysis.markLoops(); + h.leaveTimeline(); + c.AVM2.Verifier.enabled.value && (h.enterTimeline("Verify"), a.verifyMethod(d, g), h.leaveTimeline()); + a = 0 < c.AVM2.Compiler.traceLevel.value; + var f = 1 < c.AVM2.Compiler.traceLevel.value, k = 2 < c.AVM2.Compiler.traceLevel.value; if (a) { var r = window.performance.now() } - k.enterTimeline("Build IR"); - D.startNumbering(); - d = (new c(h, d, e)).buildGraph(); - k.leaveTimeline(); - n && d.trace(ha); - k.enterTimeline("Build CFG"); - d = d.buildCFG(); - k.leaveTimeline(); - k.enterTimeline("Optimize Phis"); - d.optimizePhis(); - k.leaveTimeline(); - k.enterTimeline("Schedule Nodes"); - d.scheduleEarly(); - k.leaveTimeline(); - n && d.trace(ha); - k.enterTimeline("Verify IR"); - d.verify(); - k.leaveTimeline(); - k.enterTimeline("Allocate Variables"); - d.allocateVariables(); - k.leaveTimeline(); - k.enterTimeline("Generate Source"); - n = b.AVM2.Compiler.Backend.generate(d); - k.leaveTimeline(); - l && ha.writeLn(n.body); - D.stopNumbering(); - k.leaveTimeline(); - a && ha.writeLn("Compiled " + (h.name ? "function " + h.name : "anonymous function") + " in " + (window.performance.now() - r).toPrecision(2) + "ms"); - return n; + h.enterTimeline("Build IR"); + G.startNumbering(); + g = (new b(d, g, e)).buildGraph(); + h.leaveTimeline(); + k && g.trace(da); + h.enterTimeline("Build CFG"); + g = g.buildCFG(); + h.leaveTimeline(); + h.enterTimeline("Optimize Phis"); + g.optimizePhis(); + h.leaveTimeline(); + h.enterTimeline("Schedule Nodes"); + g.scheduleEarly(); + h.leaveTimeline(); + k && g.trace(da); + h.enterTimeline("Verify IR"); + g.verify(); + h.leaveTimeline(); + h.enterTimeline("Allocate Variables"); + g.allocateVariables(); + h.leaveTimeline(); + h.enterTimeline("Generate Source"); + k = c.AVM2.Compiler.Backend.generate(g); + h.leaveTimeline(); + f && da.writeLn(k.body); + G.stopNumbering(); + h.leaveTimeline(); + a && da.writeLn("Compiled " + (d.name ? "function " + d.name : "anonymous function") + " in " + (window.performance.now() - r).toPrecision(2) + "ms"); + return k; }; - return c; - }(), fa = new b.AVM2.Verifier.Verifier; - g.compileMethod = function(a, b, c) { - return na.buildMethod(fa, a, b, c); - }; - })(k.Compiler || (k.Compiler = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - function f(b, d) { - var e = new c(null, b), h = b.abc.applicationDomain, l = []; - k(b.init, d, e, l, !1); - b.traits.forEach(function(b) { - if (b.isClass()) { - for (var n = [], f = b.classInfo;f;) { - if (n.unshift(f), f.instanceInfo.superName) { - f = h.findClassInfo(f.instanceInfo.superName); + return b; + }(), ya = new c.AVM2.Verifier.Verifier; + a.compileMethod = function(b, a, d) { + return qa.buildMethod(ya, b, a, d); + }; + })(h.Compiler || (h.Compiler = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + function h(a, b) { + var g = new m(null, a), f = a.abc.applicationDomain, k = []; + v(a.init, b, g, k, !1); + a.traits.forEach(function(a) { + if (a.isClass()) { + for (var d = [], c = a.classInfo;c;) { + if (d.unshift(c), c.instanceInfo.superName) { + c = f.findClassInfo(c.instanceInfo.superName); } else { break; } } - var p = e; - n.forEach(function(a) { - p = new c(p, a); + var l = g; + d.forEach(function(b) { + l = new m(l, b); }); - a(b.classInfo, d, p, l); + e(a.classInfo, b, l, k); } else { - (b.isMethod() || b.isGetter() || b.isSetter()) && m(b, d, e, l); + (a.isMethod() || a.isGetter() || a.isSetter()) && u(a, b, g, k); } }); - l.forEach(function(a) { - k(a.methodInfo, d, a.scope, null, !0); + k.forEach(function(a) { + v(a.methodInfo, b, a.scope, null, !0); }); } - function k(a, b, c, h, d) { - if (n(a)) { - p(a); + function v(a, b, g, e, k) { + if (t(a)) { + q(a); try { - l = !1; - var f = e(a, c, d, !1, !1); + f = !1; + var c = n(a, g, k, !1, !1); b.enter(a.index + ": "); - l ? b.writeLn("undefined") : b.writeLns(f.toSource()); + f ? b.writeLn("undefined") : b.writeLns(c.toSource()); b.leave(","); - h && s(a, b, c, h); - } catch (g) { - b.writeLn("// " + g); + e && p(a, b, g, e); + } catch (m) { + b.writeLn("// " + m); } } else { b.writeLn("// Can't compile method: " + a.index); } } - function s(a, b, d, h) { - for (var e = a.analysis.bytecodes, l = a.abc.methods, n = 0;n < e.length;n++) { - var f = e[n]; - if (64 === f.op) { - f = l[f.index]; - p(f); - var g = new c(d, a); - h.push({scope:g, methodInfo:f}); - s(f, b, g, h); + function p(a, b, g, e) { + for (var f = a.analysis.bytecodes, k = a.abc.methods, c = 0;c < f.length;c++) { + var l = f[c]; + if (64 === l.op) { + l = k[l.index]; + q(l); + var n = new m(g, a); + e.push({scope:n, methodInfo:l}); + p(l, b, n, e); } } } - function m(a, b, c, h) { - (a.isMethod() || a.isGetter() || a.isSetter()) && a.methodInfo.hasBody && (b.writeLn("// " + a), k(a.methodInfo, b, c, h, !1)); + function u(a, b, g, e) { + (a.isMethod() || a.isGetter() || a.isSetter()) && a.methodInfo.hasBody && (b.writeLn("// " + a), v(a.methodInfo, b, g, e, !1)); } - function d(a, b, c, h) { + function l(a, b, g, e) { a.forEach(function(a) { - m(a, b, c, h); + u(a, b, g, e); }); } - function a(a, b, c, h) { - k(a.init, b, c, h, !1); - d(a.traits, b, c, h); - k(a.instanceInfo.init, b, c, h, !1); - d(a.instanceInfo.traits, b, c, h); + function e(a, b, g, e) { + v(a.init, b, g, e, !1); + l(a.traits, b, g, e); + v(a.instanceInfo.init, b, g, e, !1); + l(a.instanceInfo.traits, b, g, e); } - var c = b.AVM2.Runtime.Scope, n = b.AVM2.Runtime.canCompile, p = b.AVM2.Runtime.ensureFunctionIsInitialized, e = b.AVM2.Runtime.createCompiledFunction, q = b.AVM2.Runtime.LazyInitializer, l = !1; + var m = c.AVM2.Runtime.Scope, t = c.AVM2.Runtime.canCompile, q = c.AVM2.Runtime.ensureFunctionIsInitialized, n = c.AVM2.Runtime.createCompiledFunction, k = c.AVM2.Runtime.LazyInitializer, f = !1; jsGlobal.objectConstantName = function(a) { if (a.hash) { return "$(" + a.hash + ")"; } - if (a instanceof q) { + if (a instanceof k) { return a.getName(); } - l = !0; + f = !0; }; - g.compileAbc = function(a, b) { + a.compileAbc = function(a, b) { b.enter("{"); b.enter("methods: {"); - for (var c = 0;c < a.scripts.length;c++) { - f(a.scripts[c], b); + for (var g = 0;g < a.scripts.length;g++) { + h(a.scripts[g], b); } b.leave("}"); b.leave("}"); }; - })(k.Compiler || (k.Compiler = {})); - })(b.AVM2 || (b.AVM2 = {})); + })(h.Compiler || (h.Compiler = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - function g(a) { - var b = a.length, c = [], h; - for (h = 0;h < b;++h) { - c[h] = a.charAt(h); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + function h(b) { + var a = b.length, d = [], g; + for (g = 0;g < a;++g) { + d[g] = b.charAt(g); } - return c; + return d; } - function k(b) { + function p(b) { if (null === b) { return "null"; } if ("string" === typeof b) { - var h, e, l, r, f = 0, q = 0, m = b; - h = c[m]; - if (!h) { - 1024 === a && (c = Object.create(null), a = 0); - h = ""; - "undefined" === typeof b[0] && (b = g(b)); - e = 0; - for (l = b.length;e < l;++e) { - r = b[e]; - if ("'" === r) { - ++f; + var a, d, g, f, k = 0, c = 0, n = b; + a = m[n]; + if (!a) { + 1024 === e && (m = Object.create(null), e = 0); + a = ""; + "undefined" === typeof b[0] && (b = h(b)); + d = 0; + for (g = b.length;d < g;++d) { + f = b[d]; + if ("'" === f) { + ++k; } else { - if ('"' === r) { - ++q; + if ('"' === f) { + ++c; } else { - if (0 <= "\\\n\r\u2028\u2029".indexOf(r)) { - var x = "\\"; - switch(r) { + if (0 <= "\\\n\r\u2028\u2029".indexOf(f)) { + var s = "\\"; + switch(f) { case "\\": - x += "\\"; + s += "\\"; break; case "\n": - x += "n"; + s += "n"; break; case "\r": - x += "r"; + s += "r"; break; case "\u2028": - x += "u2028"; + s += "u2028"; break; case "\u2029": - x += "u2029"; + s += "u2029"; break; default: throw Error("Incorrectly classified character");; } - h += x; + a += s; continue; } else { - if (!(" " <= r && "~" >= r)) { - var x = b[e + 1], w = r.charCodeAt(0), u = w.toString(16), s = "\\"; - switch(r) { + if (!(" " <= f && "~" >= f)) { + var s = b[d + 1], p = f.charCodeAt(0), u = p.toString(16), H = "\\"; + switch(f) { case "\b": - s += "b"; + H += "b"; break; case "\f": - s += "f"; + H += "f"; break; case "\t": - s += "t"; + H += "t"; break; default: - s = 255 < w ? s + ("u" + "0000".slice(u.length) + u) : "\x00" === r && 0 > "0123456789".indexOf(x) ? s + "0" : "\x0B" === r ? s + "x0B" : s + ("x" + "00".slice(u.length) + u); + H = 255 < p ? H + ("u" + "0000".slice(u.length) + u) : "\x00" === f && 0 > "0123456789".indexOf(s) ? H + "0" : "\x0B" === f ? H + "x0B" : H + ("x" + "00".slice(u.length) + u); } - h += s; + a += H; continue; } } } } - h += r; + a += f; } - b = h; - h = '"'; - "undefined" === typeof b[0] && (b = g(b)); - e = 0; - for (l = b.length;e < l;++e) { - r = b[e], '"' === r && (h += "\\"), h += r; + b = a; + a = '"'; + "undefined" === typeof b[0] && (b = h(b)); + d = 0; + for (g = b.length;d < g;++d) { + f = b[d], '"' === f && (a += "\\"), a += f; } - h += '"'; - c[m] = h; - a++; + a += '"'; + m[n] = a; + e++; } - return h; + return a; } if ("number" === typeof b) { if (b !== b) { @@ -18267,1183 +19286,1184 @@ if (0 > b || 0 === b && 0 > 1 / b) { throw Error("Numeric literal whose value is negative"); } - b === 1 / 0 ? b = "1e+400" : (e = p[b], e || (1024 === n && (p = Object.create(null), n = 0), e = "" + b, p[b] = e, n++), b = e); + b === 1 / 0 ? b = "1e+400" : (d = q[b], d || (1024 === t && (q = Object.create(null), t = 0), d = "" + b, q[b] = d, t++), b = d); return b; } if ("boolean" === typeof b) { return b ? "true" : "false"; } - d(b); + l(b); } - function m(a, b, c) { - for (var h = "", d = 0;d < a.length;d++) { - h += a[d].toSource(b), c && d < a.length - 1 && (h += c); + function u(b, a, d) { + for (var g = "", e = 0;e < b.length;e++) { + g += b[e].toSource(a), d && e < b.length - 1 && (g += d); } - return h; + return g; } - var d = b.Debug.notImplemented, a = 0, c = Object.create(null), n = 0, p = Object.create(null), e = {"||":3, "&&":4, "|":5, "^":6, "&":7, "==":8, "!=":8, "===":8, "!==":8, is:8, isnt:8, "<":9, ">":9, "<=":9, ">=":9, "in":9, "instanceof":9, "<<":10, ">>":10, ">>>":10, "+":11, "-":11, "*":12, "%":12, "/":12}, q = function() { - function a() { + var l = c.Debug.assertUnreachable, e = 0, m = Object.create(null), t = 0, q = Object.create(null), n = {"||":3, "&&":4, "|":5, "^":6, "&":7, "==":8, "!=":8, "===":8, "!==":8, is:8, isnt:8, "<":9, ">":9, "<=":9, ">=":9, "in":9, "instanceof":9, "<<":10, ">>":10, ">>>":10, "+":11, "-":11, "*":12, "%":12, "/":12}, k = function() { + function b() { + this.type = "Node"; } - a.prototype.toSource = function(a) { - d(this.type); + b.prototype.toSource = function(b) { + l("toSource called on abstract base class Node with type " + this.type); return ""; }; - return a; + return b; }(); - f.Node = q; - var l = function(a) { - function b() { - a.apply(this, arguments); + a.Node = k; + var f = function(b) { + function a() { + b.apply(this, arguments); + this.type = "Statement"; } - __extends(b, a); - return b; - }(q); - f.Statement = l; - var u = function(a) { - function b() { - a.apply(this, arguments); + __extends(a, b); + return a; + }(k); + a.Statement = f; + var d = function(b) { + function a() { + b.apply(this, arguments); + this.type = "Expression"; } - __extends(b, a); - return b; - }(q); - f.Expression = u; - var w = function(a) { - function b(c) { - a.call(this); - this.body = c; + __extends(a, b); + return a; + }(k); + a.Expression = d; + var b = function(b) { + function a(d) { + b.call(this); + this.body = d; + this.type = "Program"; } - __extends(b, a); - return b; - }(q); - f.Program = w; - var r = function(a) { - function b() { - a.apply(this, arguments); + __extends(a, b); + return a; + }(k); + a.Program = b; + b = function(b) { + function a() { + b.apply(this, arguments); + this.type = "EmptyStatement"; } - __extends(b, a); - return b; - }(l); - f.EmptyStatement = r; - var h = function(a) { - function b(c) { - a.call(this); - this.body = c; + __extends(a, b); + return a; + }(f); + a.EmptyStatement = b; + b = function(b) { + function a(d) { + b.call(this); + this.body = d; + this.type = "BlockStatement"; } - __extends(b, a); - b.prototype.toSource = function(a) { - return "{\n" + m(this.body, a) + "}"; + __extends(a, b); + a.prototype.toSource = function(b) { + return "{\n" + u(this.body, b) + "}"; }; - return b; - }(l); - f.BlockStatement = h; - var x = function(a) { - function b(c) { - a.call(this); - this.expression = c; + return a; + }(f); + a.BlockStatement = b; + b = function(b) { + function a(d) { + b.call(this); + this.expression = d; + this.type = "ExpressionStatement"; } - __extends(b, a); - b.prototype.toSource = function(a) { + __extends(a, b); + a.prototype.toSource = function(b) { return this.expression.toSource(0) + ";\n"; }; - return b; - }(l); - f.ExpressionStatement = x; - var y = function(a) { - function b(c, h, d) { - a.call(this); - this.test = c; - this.consequent = h; - this.alternate = d; - } - __extends(b, a); - b.prototype.toSource = function(a) { - a = "if(" + this.test.toSource(0) + "){" + this.consequent.toSource(0) + "}"; - this.alternate && (a += "else{" + this.alternate.toSource(0) + "}"); - return a; + return a; + }(f); + a.ExpressionStatement = b; + b = function(b) { + function a(d, g, e) { + b.call(this); + this.test = d; + this.consequent = g; + this.alternate = e; + this.type = "IfStatement"; + } + __extends(a, b); + a.prototype.toSource = function(b) { + b = "if(" + this.test.toSource(0) + "){" + this.consequent.toSource(0) + "}"; + this.alternate && (b += "else{" + this.alternate.toSource(0) + "}"); + return b; }; - return b; - }(l); - f.IfStatement = y; - var G = function(a) { - function b(c, h) { - a.call(this); - this.label = c; - this.body = h; + return a; + }(f); + a.IfStatement = b; + b = function(b) { + function a(d, g) { + b.call(this); + this.label = d; + this.body = g; + this.type = "LabeledStatement"; } - __extends(b, a); - return b; - }(l); - f.LabeledStatement = G; - var I = function(a) { - function b(c) { - a.call(this); - this.label = c; + __extends(a, b); + return a; + }(f); + a.LabeledStatement = b; + b = function(b) { + function a(d) { + b.call(this); + this.label = d; + this.type = "BreakStatement"; } - __extends(b, a); - b.prototype.toSource = function(a) { - a = "break"; - this.label && (a += " " + this.label.toSource(0)); - return a + ";"; + __extends(a, b); + a.prototype.toSource = function(b) { + b = "break"; + this.label && (b += " " + this.label.toSource(0)); + return b + ";"; }; - return b; - }(l); - f.BreakStatement = I; - var C = function(a) { - function b(c) { - a.call(this); - this.label = c; + return a; + }(f); + a.BreakStatement = b; + b = function(b) { + function a(d) { + b.call(this); + this.label = d; + this.type = "ContinueStatement"; } - __extends(b, a); - b.prototype.toSource = function(a) { - a = "continue"; - this.label && (a += " " + this.label.toSource(0)); - return a + ";"; + __extends(a, b); + a.prototype.toSource = function(b) { + b = "continue"; + this.label && (b += " " + this.label.toSource(0)); + return b + ";"; }; - return b; - }(l); - f.ContinueStatement = C; - var E = function(a) { - function b(c, h) { - a.call(this); - this.object = c; - this.body = h; + return a; + }(f); + a.ContinueStatement = b; + b = function(b) { + function a(d, g) { + b.call(this); + this.object = d; + this.body = g; + this.type = "WithStatement"; } - __extends(b, a); - return b; - }(l); - f.WithStatement = E; - var O = function(a) { - function b(c, h, d) { - a.call(this); - this.discriminant = c; - this.cases = h; - this.lexical = d; - } - __extends(b, a); - b.prototype.toSource = function(a) { - return "switch(" + this.discriminant.toSource(0) + "){" + m(this.cases, 0, ";") + "};"; + __extends(a, b); + return a; + }(f); + a.WithStatement = b; + b = function(b) { + function a(d, g, e) { + b.call(this); + this.discriminant = d; + this.cases = g; + this.lexical = e; + this.type = "SwitchStatement"; + } + __extends(a, b); + a.prototype.toSource = function(b) { + return "switch(" + this.discriminant.toSource(0) + "){" + u(this.cases, 0, ";") + "};"; }; - return b; - }(l); - f.SwitchStatement = O; - var K = function(a) { - function b(c) { - a.call(this); - this.argument = c; + return a; + }(f); + a.SwitchStatement = b; + b = function(b) { + function a(d) { + b.call(this); + this.argument = d; + this.type = "ReturnStatement"; } - __extends(b, a); - b.prototype.toSource = function(a) { - a = "return "; - this.argument && (a += this.argument.toSource(0)); - return a + ";\n"; + __extends(a, b); + a.prototype.toSource = function(b) { + b = "return "; + this.argument && (b += this.argument.toSource(0)); + return b + ";\n"; }; - return b; - }(l); - f.ReturnStatement = K; - var F = function(a) { - function b(c) { - a.call(this); - this.argument = c; + return a; + }(f); + a.ReturnStatement = b; + b = function(b) { + function a(d) { + b.call(this); + this.argument = d; + this.type = "ThrowStatement"; } - __extends(b, a); - b.prototype.toSource = function(a) { + __extends(a, b); + a.prototype.toSource = function(b) { return "throw " + this.argument.toSource(0) + ";\n"; }; - return b; - }(l); - f.ThrowStatement = F; - var J = function(a) { - function b(c, h, d, e) { - a.call(this); - this.block = c; - this.handlers = h; - this.guardedHandlers = d; - this.finalizer = e; + return a; + }(f); + a.ThrowStatement = b; + b = function(b) { + function a(d, g, e, f) { + b.call(this); + this.block = d; + this.handlers = g; + this.guardedHandlers = e; + this.finalizer = f; + this.type = "TryStatement"; } - __extends(b, a); - return b; - }(l); - f.TryStatement = J; - var A = function(a) { - function b(c, h) { - a.call(this); - this.test = c; - this.body = h; + __extends(a, b); + return a; + }(f); + a.TryStatement = b; + b = function(b) { + function a(d, g) { + b.call(this); + this.test = d; + this.body = g; + this.type = "WhileStatement"; } - __extends(b, a); - b.prototype.toSource = function(a) { + __extends(a, b); + a.prototype.toSource = function(b) { return "while(" + this.test.toSource(0) + "){" + this.body.toSource(0) + "}"; }; - return b; - }(l); - f.WhileStatement = A; - var B = function(a) { - function b(c, h) { - a.call(this); - this.body = c; - this.test = h; - } - __extends(b, a); - return b; - }(l); - f.DoWhileStatement = B; - var z = function(a) { - function b(c, h, d, e) { - a.call(this); - this.init = c; - this.test = h; - this.update = d; - this.body = e; - } - __extends(b, a); - return b; - }(l); - f.ForStatement = z; - var P = function(a) { - function b(c, h, d, e) { - a.call(this); - this.left = c; - this.right = h; + return a; + }(f); + a.WhileStatement = b; + b = function(b) { + function a(d, g) { + b.call(this); this.body = d; - this.each = e; + this.test = g; + this.type = "DoWhileStatement"; } - __extends(b, a); - return b; - }(l); - f.ForInStatement = P; - var D = function(a) { - function b() { - a.apply(this, arguments); + __extends(a, b); + return a; + }(f); + a.DoWhileStatement = b; + b = function(b) { + function a(d, g, e, f) { + b.call(this); + this.init = d; + this.test = g; + this.update = e; + this.body = f; + this.type = "ForStatement"; } - __extends(b, a); - return b; - }(l); - f.DebuggerStatement = D; - var L = function(a) { - function b() { - a.apply(this, arguments); + __extends(a, b); + return a; + }(f); + a.ForStatement = b; + b = function(b) { + function a(d, g, e, f) { + b.call(this); + this.left = d; + this.right = g; + this.body = e; + this.each = f; + this.type = "ForInStatement"; } - __extends(b, a); - return b; - }(l); - f.Declaration = L; - var M = function(a) { - function b(c, h, d, e, l, n, r) { - a.call(this); - this.id = c; - this.params = h; - this.defaults = d; - this.rest = e; - this.body = l; - this.generator = n; - this.expression = r; + __extends(a, b); + return a; + }(f); + a.ForInStatement = b; + b = function(b) { + function a() { + b.apply(this, arguments); + this.type = "DebuggerStatement"; } - __extends(b, a); - return b; - }(L); - f.FunctionDeclaration = M; - var V = function(a) { - function b(c, h) { - a.call(this); - this.declarations = c; - this.kind = h; + __extends(a, b); + return a; + }(f); + a.DebuggerStatement = b; + f = function(b) { + function a() { + b.apply(this, arguments); + this.type = "Declaration"; } - __extends(b, a); - b.prototype.toSource = function(a) { - return this.kind + " " + m(this.declarations, a, ",") + ";\n"; - }; - return b; - }(L); - f.VariableDeclaration = V; - var Q = function(a) { - function b(c, h) { - a.call(this); - this.id = c; - this.init = h; + __extends(a, b); + return a; + }(f); + a.Declaration = f; + b = function(b) { + function a(d, g, e, f, k, c, m) { + b.call(this); + this.id = d; + this.params = g; + this.defaults = e; + this.rest = f; + this.body = k; + this.generator = c; + this.expression = m; + this.type = "FunctionDeclaration"; } - __extends(b, a); - b.prototype.toSource = function(a) { - a = this.id.toSource(1); - this.init && (a += "=" + this.init.toSource(1)); - return a; + __extends(a, b); + return a; + }(f); + a.FunctionDeclaration = b; + f = function(b) { + function a(d, g) { + b.call(this); + this.declarations = d; + this.kind = g; + this.type = "VariableDeclaration"; + } + __extends(a, b); + a.prototype.toSource = function(b) { + return this.kind + " " + u(this.declarations, b, ",") + ";\n"; }; - return b; - }(q); - f.VariableDeclarator = Q; - var U = function(a) { - function b(c) { - a.call(this); - this.name = c; + return a; + }(f); + a.VariableDeclaration = f; + f = function(b) { + function a(d, g) { + b.call(this); + this.id = d; + this.init = g; + this.type = "VariableDeclarator"; + } + __extends(a, b); + a.prototype.toSource = function(b) { + b = this.id.toSource(1); + this.init && (b += "=" + this.init.toSource(1)); + return b; + }; + return a; + }(k); + a.VariableDeclarator = f; + f = function(b) { + function a(d) { + b.call(this); + this.name = d; + this.type = "Identifier"; } - __extends(b, a); - b.prototype.toSource = function(a) { + __extends(a, b); + a.prototype.toSource = function(b) { return this.name; }; - return b; - }(u); - f.Identifier = U; - var S = function(a) { - function b(c) { - a.call(this); - this.value = c; + return a; + }(d); + a.Identifier = f; + var g = function(b) { + function a(d) { + b.call(this); + this.value = d; + this.type = "Literal"; } - __extends(b, a); - b.prototype.toSource = function(a) { - return k(this.value); + __extends(a, b); + a.prototype.toSource = function(b) { + return p(this.value); }; - return b; - }(u); - f.Literal = S; - var aa = function(a) { - function b() { - a.apply(this, arguments); + return a; + }(d); + a.Literal = g; + f = function(b) { + function a() { + b.apply(this, arguments); + this.type = "ThisExpression"; } - __extends(b, a); - b.prototype.toSource = function(a) { + __extends(a, b); + a.prototype.toSource = function(b) { return "this"; }; - return b; - }(u); - f.ThisExpression = aa; - var $ = function(a) { - function b(c) { - a.call(this); - this.elements = c; + return a; + }(d); + a.ThisExpression = f; + f = function(b) { + function a(d) { + b.call(this); + this.elements = d; + this.type = "ArrayExpression"; } - __extends(b, a); - b.prototype.toSource = function(a) { - return "[" + m(this.elements, 1, ",") + "]"; + __extends(a, b); + a.prototype.toSource = function(b) { + return "[" + u(this.elements, 1, ",") + "]"; }; - return b; - }(u); - f.ArrayExpression = $; - var ea = function(a) { - function b(c) { - a.call(this); - this.properties = c; + return a; + }(d); + a.ArrayExpression = f; + f = function(b) { + function a(d) { + b.call(this); + this.properties = d; + this.type = "ObjectExpression"; } - __extends(b, a); - b.prototype.toSource = function(a) { - return "{" + m(this.properties, 0, ",") + "}"; + __extends(a, b); + a.prototype.toSource = function(b) { + return "{" + u(this.properties, 0, ",") + "}"; }; - return b; - }(u); - f.ObjectExpression = ea; - var Z = function(a) { - function b(c, h, d, e, l, n, r) { - a.call(this); - this.id = c; - this.params = h; - this.defaults = d; - this.rest = e; - this.body = l; - this.generator = n; - this.expression = r; - } - __extends(b, a); - return b; - }(u); - f.FunctionExpression = Z; - var v = function(a) { - function b(c) { - a.call(this); - this.expressions = c; + return a; + }(d); + a.ObjectExpression = f; + f = function(b) { + function a(d, g, e, f, k, c, m) { + b.call(this); + this.id = d; + this.params = g; + this.defaults = e; + this.rest = f; + this.body = k; + this.generator = c; + this.expression = m; + this.type = "FunctionExpression"; } - __extends(b, a); - return b; - }(u); - f.SequenceExpression = v; - var X = function(a) { - function b(c, h, d) { - a.call(this); - this.operator = c; - this.prefix = h; - this.argument = d; + __extends(a, b); + return a; + }(d); + a.FunctionExpression = f; + f = function(b) { + function a(d) { + b.call(this); + this.expressions = d; + this.type = "SequenceExpression"; } - __extends(b, a); - b.prototype.toSource = function(a) { - var b = this.argument.toSource(13), b = this.prefix ? this.operator + b : b + this.operator, b = " " + b; - return 13 < a ? "(" + b + ")" : b; + __extends(a, b); + return a; + }(d); + a.SequenceExpression = f; + f = function(b) { + function a(d, g, e) { + b.call(this); + this.operator = d; + this.prefix = g; + this.argument = e; + this.type = "UnaryExpression"; + } + __extends(a, b); + a.prototype.toSource = function(b) { + var a = this.argument.toSource(13), a = this.prefix ? this.operator + a : a + this.operator, a = " " + a; + return 13 < b ? "(" + a + ")" : a; }; - return b; - }(u); - f.UnaryExpression = X; - var ba = function(a) { - function b(c, h, d) { - a.call(this); - this.operator = c; - this.left = h; - this.right = d; - } - __extends(b, a); - b.prototype.toSource = function(a) { - var b = e[this.operator], c = this.left.toSource(b) + this.operator + this.right.toSource(b + 1); - return b < a ? "(" + c + ")" : c; + return a; + }(d); + a.UnaryExpression = f; + f = function(b) { + function a(d, g, e) { + b.call(this); + this.operator = d; + this.left = g; + this.right = e; + this.type = "BinaryExpression"; + } + __extends(a, b); + a.prototype.toSource = function(b) { + var a = n[this.operator], d = this.left.toSource(a) + this.operator + this.right.toSource(a + 1); + return a < b ? "(" + d + ")" : d; }; - return b; - }(u); - f.BinaryExpression = ba; - var R = function(a) { - function b(c, h, d) { - a.call(this); - this.operator = c; - this.left = h; - this.right = d; - } - __extends(b, a); - b.prototype.toSource = function(a) { - var b = this.left.toSource(1) + this.operator + this.right.toSource(1); - return 1 < a ? "(" + b + ")" : b; + return a; + }(d); + a.BinaryExpression = f; + b = function(b) { + function a(d, g, e) { + b.call(this); + this.operator = d; + this.left = g; + this.right = e; + this.type = "AssignmentExpression"; + } + __extends(a, b); + a.prototype.toSource = function(b) { + var a = this.left.toSource(1) + this.operator + this.right.toSource(1); + return 1 < b ? "(" + a + ")" : a; }; - return b; - }(u); - f.AssignmentExpression = R; - var H = function(a) { - function b(c, h, d) { - a.call(this); - this.operator = c; - this.argument = h; - this.prefix = d; + return a; + }(d); + a.AssignmentExpression = b; + b = function(b) { + function a(d, g, e) { + b.call(this); + this.operator = d; + this.argument = g; + this.prefix = e; + this.type = "UpdateExpression"; } - __extends(b, a); - return b; - }(u); - f.UpdateExpression = H; - var Y = function(a) { - function b(c, h, d) { - a.call(this, c, h, d); + __extends(a, b); + return a; + }(d); + a.UpdateExpression = b; + f = function(b) { + function a(d, g, e) { + b.call(this, d, g, e); + this.type = "LogicalExpression"; } - __extends(b, a); - return b; - }(ba); - f.LogicalExpression = Y; - var ga = function(a) { - function b(c, h, d) { - a.call(this); - this.test = c; - this.consequent = h; - this.alternate = d; + __extends(a, b); + return a; + }(f); + a.LogicalExpression = f; + f = function(b) { + function a(d, g, e) { + b.call(this); + this.test = d; + this.consequent = g; + this.alternate = e; + this.type = "ConditionalExpression"; } - __extends(b, a); - b.prototype.toSource = function(a) { + __extends(a, b); + a.prototype.toSource = function(b) { return this.test.toSource(3) + "?" + this.consequent.toSource(1) + ":" + this.alternate.toSource(1); }; - return b; - }(u); - f.ConditionalExpression = ga; - var ca = function(a) { - function b(c, h) { - a.call(this); - this.callee = c; - this.arguments = h; + return a; + }(d); + a.ConditionalExpression = f; + f = function(b) { + function a(d, g) { + b.call(this); + this.callee = d; + this.type = "NewExpression"; + this.arguments = g; } - __extends(b, a); - b.prototype.toSource = function(a) { - return "new " + this.callee.toSource(a) + "(" + m(this.arguments, a, ",") + ")"; + __extends(a, b); + a.prototype.toSource = function(b) { + return "new " + this.callee.toSource(b) + "(" + u(this.arguments, b, ",") + ")"; }; - return b; - }(u); - f.NewExpression = ca; - var ka = function(a) { - function b(c, h) { - a.call(this); - this.callee = c; - this.arguments = h; + return a; + }(d); + a.NewExpression = f; + f = function(b) { + function a(d, g) { + b.call(this); + this.callee = d; + this.type = "CallExpression"; + this.arguments = g; } - __extends(b, a); - b.prototype.toSource = function(a) { - return this.callee.toSource(a) + "(" + m(this.arguments, a, ",") + ")"; + __extends(a, b); + a.prototype.toSource = function(b) { + return this.callee.toSource(b) + "(" + u(this.arguments, b, ",") + ")"; }; - return b; - }(u); - f.CallExpression = ka; - var W = function(a) { - function b(c, h, d) { - a.call(this); - this.object = c; - this.property = h; - this.computed = d; - } - __extends(b, a); - b.prototype.toSource = function(a) { - var b = this.object.toSource(15); - this.object instanceof S && (b = "(" + b + ")"); - var c = this.property.toSource(0), b = this.computed ? b + ("[" + c + "]") : b + ("." + c); - return 17 < a ? "(" + b + ")" : b; + return a; + }(d); + a.CallExpression = f; + d = function(b) { + function a(d, g, e) { + b.call(this); + this.object = d; + this.property = g; + this.computed = e; + this.type = "MemberExpression"; + } + __extends(a, b); + a.prototype.toSource = function(b) { + var a = this.object.toSource(15); + this.object instanceof g && (a = "(" + a + ")"); + var d = this.property.toSource(0), a = this.computed ? a + ("[" + d + "]") : a + ("." + d); + return 17 < b ? "(" + a + ")" : a; }; - return b; - }(u); - f.MemberExpression = W; - var ha = function(a) { - function b(c, h, d) { - a.call(this); - this.key = c; - this.value = h; - this.kind = d; - } - __extends(b, a); - b.prototype.toSource = function(a) { - return this.key.toSource(a) + ":" + this.value.toSource(a); + return a; + }(d); + a.MemberExpression = d; + d = function(b) { + function a(d, g, e) { + b.call(this); + this.key = d; + this.value = g; + this.kind = e; + this.type = "Property"; + } + __extends(a, b); + a.prototype.toSource = function(b) { + return this.key.toSource(b) + ":" + this.value.toSource(b); }; - return b; - }(q); - f.Property = ha; - var da = function(a) { - function b(c, h) { - a.call(this); - this.test = c; - this.consequent = h; - } - __extends(b, a); - b.prototype.toSource = function(a) { - return(this.test ? "case " + this.test.toSource(a) : "default") + ": " + m(this.consequent, a, ";"); + return a; + }(k); + a.Property = d; + d = function(b) { + function a(d, g) { + b.call(this); + this.test = d; + this.consequent = g; + this.type = "SwitchCase"; + } + __extends(a, b); + a.prototype.toSource = function(b) { + return(this.test ? "case " + this.test.toSource(b) : "default") + ": " + u(this.consequent, b, ";"); }; - return b; - }(q); - f.SwitchCase = da; - var ja = function(a) { - function b(c, h, d) { - a.call(this); - this.param = c; - this.guard = h; - this.body = d; + return a; + }(k); + a.SwitchCase = d; + k = function(b) { + function a(d, g, e) { + b.call(this); + this.param = d; + this.guard = g; + this.body = e; + this.type = "CatchClause"; } - __extends(b, a); - return b; - }(q); - f.CatchClause = ja; - q.prototype.type = "Node"; - w.prototype.type = "Program"; - l.prototype.type = "Statement"; - r.prototype.type = "EmptyStatement"; - h.prototype.type = "BlockStatement"; - x.prototype.type = "ExpressionStatement"; - y.prototype.type = "IfStatement"; - G.prototype.type = "LabeledStatement"; - I.prototype.type = "BreakStatement"; - C.prototype.type = "ContinueStatement"; - E.prototype.type = "WithStatement"; - O.prototype.type = "SwitchStatement"; - K.prototype.type = "ReturnStatement"; - F.prototype.type = "ThrowStatement"; - J.prototype.type = "TryStatement"; - A.prototype.type = "WhileStatement"; - B.prototype.type = "DoWhileStatement"; - z.prototype.type = "ForStatement"; - P.prototype.type = "ForInStatement"; - D.prototype.type = "DebuggerStatement"; - L.prototype.type = "Declaration"; - M.prototype.type = "FunctionDeclaration"; - V.prototype.type = "VariableDeclaration"; - Q.prototype.type = "VariableDeclarator"; - u.prototype.type = "Expression"; - U.prototype.type = "Identifier"; - S.prototype.type = "Literal"; - aa.prototype.type = "ThisExpression"; - $.prototype.type = "ArrayExpression"; - ea.prototype.type = "ObjectExpression"; - Z.prototype.type = "FunctionExpression"; - v.prototype.type = "SequenceExpression"; - X.prototype.type = "UnaryExpression"; - ba.prototype.type = "BinaryExpression"; - R.prototype.type = "AssignmentExpression"; - H.prototype.type = "UpdateExpression"; - Y.prototype.type = "LogicalExpression"; - ga.prototype.type = "ConditionalExpression"; - ca.prototype.type = "NewExpression"; - ka.prototype.type = "CallExpression"; - W.prototype.type = "MemberExpression"; - ha.prototype.type = "Property"; - da.prototype.type = "SwitchCase"; - ja.prototype.type = "CatchClause"; - })(g.AST || (g.AST = {})); - })(k.Compiler || (k.Compiler = {})); - })(b.AVM2 || (b.AVM2 = {})); + __extends(a, b); + return a; + }(k); + a.CatchClause = k; + })(a.AST || (a.AST = {})); + })(h.Compiler || (h.Compiler = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(b) { - var g = function(b) { - function d(a, c, d) { - b.call(this); - this.parent = a; - this.object = c; - this.isWith = d; +(function(c) { + (function(c) { + (function(a) { + (function(a) { + var c = function(a) { + function c(e, m, l) { + a.call(this); + this.parent = e; + this.object = m; + this.isWith = l; } - __extends(d, b); - d.prototype.visitInputs = function(a) { + __extends(c, a); + c.prototype.visitInputs = function(a) { a(this.parent); a(this.object); }; - return d; - }(b.Value); - b.ASScope = g; - g.prototype.nodeName = "ASScope"; - g = function(b) { - function d(a, c, d) { - b.call(this); - this.namespaces = a; - this.name = c; - this.flags = d; + return c; + }(a.Value); + a.ASScope = c; + c.prototype.nodeName = "ASScope"; + c = function(a) { + function c(e, m, l) { + a.call(this); + this.namespaces = e; + this.name = m; + this.flags = l; } - __extends(d, b); - d.prototype.visitInputs = function(a) { + __extends(c, a); + c.prototype.visitInputs = function(a) { a(this.namespaces); a(this.name); + a(this.flags); }; - return d; - }(b.Value); - b.ASMultiname = g; - g.prototype.mustFloat = !0; - g.prototype.nodeName = "ASMultiname"; - g = function(b) { - function d(a, c, d, f, e, g, l) { - b.call(this, a, c, d, f, e, g); - this.isLex = l; + return c; + }(a.Value); + a.ASMultiname = c; + c.prototype.mustFloat = !0; + c.prototype.nodeName = "ASMultiname"; + c = function(a) { + function c(e, m, l, q, n, k, f) { + a.call(this, e, m, l, q, n, k); + this.isLex = f; } - __extends(d, b); - return d; - }(b.CallProperty); - b.ASCallProperty = g; - g.prototype.nodeName = "ASCallProperty"; - g = function(b) { - function d(a, c, d, f, e, g, l) { - b.call(this, a, c, d, f, e, g); - this.scope = l; + __extends(c, a); + return c; + }(a.CallProperty); + a.ASCallProperty = c; + c.prototype.nodeName = "ASCallProperty"; + c = function(a) { + function c(e, m, l, q, n, k, f) { + a.call(this, e, m, l, q, n, k); + this.scope = f; } - __extends(d, b); - d.prototype.visitInputs = function(a) { - b.prototype.visitInputs.call(this, a); - a(this.scope); + __extends(c, a); + c.prototype.visitInputs = function(e) { + a.prototype.visitInputs.call(this, e); + e(this.scope); }; - return d; - }(b.CallProperty); - b.ASCallSuper = g; - g.prototype.nodeName = "ASCallSuper"; - g = function(b) { - function d(a, c, d, f) { - b.call(this, a, c, d, f); + return c; + }(a.CallProperty); + a.ASCallSuper = c; + c.prototype.nodeName = "ASCallSuper"; + c = function(a) { + function c(e, m, l, q) { + a.call(this, e, m, l, q); } - __extends(d, b); - return d; - }(b.New); - b.ASNew = g; - g.prototype.nodeName = "ASNew"; - g = function(b) { - function d(a, c, d, f, e) { - b.call(this, a, c, d, f); - this.flags = e; + __extends(c, a); + return c; + }(a.New); + a.ASNew = c; + c.prototype.nodeName = "ASNew"; + c = function(a) { + function c(e, m, l, q, n) { + a.call(this, e, m, l, q); + this.flags = n; } - __extends(d, b); - return d; - }(b.GetProperty); - b.ASGetProperty = g; - g.prototype.nodeName = "ASGetProperty"; - g = function(b) { - function d(a, c, d, f) { - b.call(this, a, c, d, f); + __extends(c, a); + return c; + }(a.GetProperty); + a.ASGetProperty = c; + c.prototype.nodeName = "ASGetProperty"; + c = function(a) { + function c(e, m, l, q) { + a.call(this, e, m, l, q); } - __extends(d, b); - return d; - }(b.GetProperty); - b.ASGetDescendants = g; - g.prototype.nodeName = "ASGetDescendants"; - g = function(b) { - function d(a, c, d, f) { - b.call(this, a, c, d, f); + __extends(c, a); + return c; + }(a.GetProperty); + a.ASGetDescendants = c; + c.prototype.nodeName = "ASGetDescendants"; + c = function(a) { + function c(e, m, l, q) { + a.call(this, e, m, l, q); } - __extends(d, b); - return d; - }(b.GetProperty); - b.ASHasProperty = g; - g.prototype.nodeName = "ASHasProperty"; - g = function(b) { - function d(a, c, d, f) { - b.call(this, a, c, d, f); + __extends(c, a); + return c; + }(a.GetProperty); + a.ASHasProperty = c; + c.prototype.nodeName = "ASHasProperty"; + c = function(a) { + function c(e, m, l, q) { + a.call(this, e, m, l, q); } - __extends(d, b); - return d; - }(b.GetProperty); - b.ASGetSlot = g; - g.prototype.nodeName = "ASGetSlot"; - g = function(b) { - function d(a, c, d, f, e) { - b.call(this, a, c, d, f); - this.scope = e; - } - __extends(d, b); - d.prototype.visitInputs = function(a) { - b.prototype.visitInputs.call(this, a); - a(this.scope); + __extends(c, a); + return c; + }(a.GetProperty); + a.ASGetSlot = c; + c.prototype.nodeName = "ASGetSlot"; + c = function(a) { + function c(e, m, l, q, n) { + a.call(this, e, m, l, q); + this.scope = n; + } + __extends(c, a); + c.prototype.visitInputs = function(e) { + a.prototype.visitInputs.call(this, e); + e(this.scope); }; - return d; - }(b.GetProperty); - b.ASGetSuper = g; - g.prototype.nodeName = "ASGetSuper"; - g = function(b) { - function d(a, c, d, f, e, g) { - b.call(this, a, c, d, f, e); - this.flags = g; + return c; + }(a.GetProperty); + a.ASGetSuper = c; + c.prototype.nodeName = "ASGetSuper"; + c = function(a) { + function c(e, m, l, q, n, k) { + a.call(this, e, m, l, q, n); + this.flags = k; } - __extends(d, b); - return d; - }(b.SetProperty); - b.ASSetProperty = g; - g.prototype.nodeName = "ASSetProperty"; - g = function(b) { - function d(a, c, d, f, e) { - b.call(this, a, c, d, f, e); + __extends(c, a); + return c; + }(a.SetProperty); + a.ASSetProperty = c; + c.prototype.nodeName = "ASSetProperty"; + c = function(a) { + function c(e, m, l, q, n) { + a.call(this, e, m, l, q, n); } - __extends(d, b); - return d; - }(b.SetProperty); - b.ASSetSlot = g; - g.prototype.nodeName = "ASSetSlot"; - g = function(b) { - function d(a, c, d, f, e, g) { - b.call(this, a, c, d, f, e); - this.scope = g; - } - __extends(d, b); - d.prototype.visitInputs = function(a) { - b.prototype.visitInputs.call(this, a); - a(this.scope); + __extends(c, a); + return c; + }(a.SetProperty); + a.ASSetSlot = c; + c.prototype.nodeName = "ASSetSlot"; + c = function(a) { + function c(e, m, l, q, n, k) { + a.call(this, e, m, l, q, n); + this.scope = k; + } + __extends(c, a); + c.prototype.visitInputs = function(e) { + a.prototype.visitInputs.call(this, e); + e(this.scope); }; - return d; - }(b.SetProperty); - b.ASSetSuper = g; - g.prototype.nodeName = "ASSetSuper"; - g = function(b) { - function d(a, c, d, f) { - b.call(this, a, c, d, f); + return c; + }(a.SetProperty); + a.ASSetSuper = c; + c.prototype.nodeName = "ASSetSuper"; + c = function(a) { + function c(e, m, l, q) { + a.call(this, e, m, l, q); } - __extends(d, b); - return d; - }(b.DeleteProperty); - b.ASDeleteProperty = g; - g.prototype.nodeName = "ASDeleteProperty"; - g = function(b) { - function d(a, c, d, f, e, g) { - b.call(this, a, c); - this.scope = d; - this.name = f; - this.methodInfo = e; - this.strict = g; + __extends(c, a); + return c; + }(a.DeleteProperty); + a.ASDeleteProperty = c; + c.prototype.nodeName = "ASDeleteProperty"; + c = function(a) { + function c(e, m, l, q, n, k) { + a.call(this, e, m); + this.scope = l; + this.name = q; + this.methodInfo = n; + this.strict = k; } - __extends(d, b); - d.prototype.visitInputs = function(a) { - b.prototype.visitInputs.call(this, a); - a(this.scope); - a(this.name); + __extends(c, a); + c.prototype.visitInputs = function(e) { + a.prototype.visitInputs.call(this, e); + e(this.scope); + e(this.name); }; - return d; - }(b.StoreDependent); - b.ASFindProperty = g; - g.prototype.nodeName = "ASFindProperty"; - g = function(b) { - function d(a, c) { - b.call(this); - this.control = a; - this.scope = c; + return c; + }(a.StoreDependent); + a.ASFindProperty = c; + c.prototype.nodeName = "ASFindProperty"; + c = function(a) { + function c(e, m) { + a.call(this); + this.control = e; + this.scope = m; } - __extends(d, b); - d.prototype.visitInputs = function(a) { + __extends(c, a); + c.prototype.visitInputs = function(a) { this.control && a(this.control); a(this.scope); }; - return d; - }(b.Value); - b.ASGlobal = g; - g.prototype.nodeName = "ASGlobal"; - g = function(b) { - function d(a) { - b.call(this); - this.methodInfo = a; + return c; + }(a.Value); + a.ASGlobal = c; + c.prototype.nodeName = "ASGlobal"; + c = function(a) { + function c(e) { + a.call(this); + this.methodInfo = e; } - __extends(d, b); - return d; - }(b.Value); - b.ASNewActivation = g; - g.prototype.nodeName = "ASNewActivation"; - var k = function(b) { - function d() { - b.call(this); + __extends(c, a); + return c; + }(a.Value); + a.ASNewActivation = c; + c.prototype.nodeName = "ASNewActivation"; + var h = function(a) { + function c() { + a.call(this); } - __extends(d, b); - return d; - }(b.Value); - b.ASNewHasNext2 = k; - g.prototype.nodeName = "ASNewHasNext2"; - })(b.IR || (b.IR = {})); - })(b.Compiler || (b.Compiler = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - var g = b.ArrayUtilities.top, s = b.ArrayUtilities.peek, m = b.Debug.assert; + __extends(c, a); + return c; + }(a.Value); + a.ASNewHasNext2 = h; + c.prototype.nodeName = "ASNewHasNext2"; + })(a.IR || (a.IR = {})); + })(c.Compiler || (c.Compiler = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + var v = c.ArrayUtilities.top, p = c.ArrayUtilities.peek, u = c.Debug.assert, l; (function(a) { - (function(a) { - a[a.SEQ = 1] = "SEQ"; - a[a.LOOP = 2] = "LOOP"; - a[a.IF = 3] = "IF"; - a[a.CASE = 4] = "CASE"; - a[a.SWITCH = 5] = "SWITCH"; - a[a.LABEL_CASE = 6] = "LABEL_CASE"; - a[a.LABEL_SWITCH = 7] = "LABEL_SWITCH"; - a[a.EXIT = 8] = "EXIT"; - a[a.BREAK = 9] = "BREAK"; - a[a.CONTINUE = 10] = "CONTINUE"; - a[a.TRY = 11] = "TRY"; - a[a.CATCH = 12] = "CATCH"; + (function(b) { + b[b.SEQ = 1] = "SEQ"; + b[b.LOOP = 2] = "LOOP"; + b[b.IF = 3] = "IF"; + b[b.CASE = 4] = "CASE"; + b[b.SWITCH = 5] = "SWITCH"; + b[b.LABEL_CASE = 6] = "LABEL_CASE"; + b[b.LABEL_SWITCH = 7] = "LABEL_SWITCH"; + b[b.EXIT = 8] = "EXIT"; + b[b.BREAK = 9] = "BREAK"; + b[b.CONTINUE = 10] = "CONTINUE"; + b[b.TRY = 11] = "TRY"; + b[b.CATCH = 12] = "CATCH"; })(a.Kind || (a.Kind = {})); - var b = function() { - return function(a) { - this.kind = a; + var e = function() { + return function(b) { + this.kind = b; }; }(); - a.ControlNode = b; - var c = function(a) { - function b(c) { - a.call(this, 1); - this.body = c; + a.ControlNode = e; + var d = function(b) { + function a(d) { + b.call(this, 1); + this.body = d; } - __extends(b, a); - b.prototype.trace = function(a) { - for (var b = this.body, c = 0, d = b.length;c < d;c++) { - b[c].trace(a); + __extends(a, b); + a.prototype.trace = function(b) { + for (var a = this.body, d = 0, g = a.length;d < g;d++) { + a[d].trace(b); } }; - b.prototype.first = function() { + a.prototype.first = function() { return this.body[0]; }; - b.prototype.slice = function(a, c) { - return new b(this.body.slice(a, c)); + a.prototype.slice = function(b, d) { + return new a(this.body.slice(b, d)); }; - return b; - }(b); - a.Seq = c; - c = function(a) { - function b(c) { - a.call(this, 2); - this.body = c; + return a; + }(e); + a.Seq = d; + d = function(b) { + function a(d) { + b.call(this, 2); + this.body = d; } - __extends(b, a); - b.prototype.trace = function(a) { - a.enter("loop {"); - this.body.trace(a); - a.leave("}"); + __extends(a, b); + a.prototype.trace = function(b) { + b.enter("loop {"); + this.body.trace(b); + b.leave("}"); }; - return b; - }(b); - a.Loop = c; - c = function(a) { - function b(c, d, e, l) { - a.call(this, 3); - this.cond = c; - this.then = d; - this.nothingThrownLabel = l; + return a; + }(e); + a.Loop = d; + d = function(b) { + function a(d, g, e, f) { + b.call(this, 3); + this.cond = d; + this.then = g; + this.nothingThrownLabel = f; this.negated = !1; this.else = e; } - __extends(b, a); - b.prototype.trace = function(a) { - this.cond.trace(a); - this.nothingThrownLabel && a.enter("if (label is " + this.nothingThrownLabel + ") {"); - a.enter("if" + (this.negated ? " not" : "") + " {"); - this.then && this.then.trace(a); - this.else && (a.outdent(), a.enter("} else {"), this.else.trace(a)); - a.leave("}"); - this.nothingThrownLabel && a.leave("}"); - }; - return b; - }(b); - a.If = c; - c = function(a) { - function b(c, d) { - a.call(this, 4); - this.index = c; - this.body = d; - } - __extends(b, a); - b.prototype.trace = function(a) { - 0 <= this.index ? a.writeLn("case " + this.index + ":") : a.writeLn("default:"); - a.indent(); - this.body && this.body.trace(a); - a.outdent(); - }; - return b; - }(b); - a.Case = c; - c = function(a) { - function b(c, d, e) { - a.call(this, 5); - this.determinant = c; - this.cases = d; + __extends(a, b); + a.prototype.trace = function(b) { + this.cond.trace(b); + this.nothingThrownLabel && b.enter("if (label is " + this.nothingThrownLabel + ") {"); + b.enter("if" + (this.negated ? " not" : "") + " {"); + this.then && this.then.trace(b); + this.else && (b.outdent(), b.enter("} else {"), this.else.trace(b)); + b.leave("}"); + this.nothingThrownLabel && b.leave("}"); + }; + return a; + }(e); + a.If = d; + d = function(b) { + function a(d, g) { + b.call(this, 4); + this.index = d; + this.body = g; + } + __extends(a, b); + a.prototype.trace = function(b) { + 0 <= this.index ? b.writeLn("case " + this.index + ":") : b.writeLn("default:"); + b.indent(); + this.body && this.body.trace(b); + b.outdent(); + }; + return a; + }(e); + a.Case = d; + d = function(b) { + function a(d, g, e) { + b.call(this, 5); + this.determinant = d; + this.cases = g; this.nothingThrownLabel = e; } - __extends(b, a); - b.prototype.trace = function(a) { - this.nothingThrownLabel && a.enter("if (label is " + this.nothingThrownLabel + ") {"); - this.determinant.trace(a); - a.writeLn("switch {"); - for (var b = 0, c = this.cases.length;b < c;b++) { - this.cases[b].trace(a); - } - a.writeLn("}"); - this.nothingThrownLabel && a.leave("}"); - }; - return b; - }(b); - a.Switch = c; - c = function(a) { - function b(c, d) { - a.call(this, 6); - this.labels = c; - this.body = d; - } - __extends(b, a); - b.prototype.trace = function(a) { - a.enter("if (label is " + this.labels.join(" or ") + ") {"); - this.body && this.body.trace(a); - a.leave("}"); - }; - return b; - }(b); - a.LabelCase = c; - c = function(a) { - function b(c) { - a.call(this, 7); - this.cases = c; - for (var d = {}, e = 0, l = c.length;e < l;e++) { - for (var n = c[e], r = 0, f = n.labels.length;r < f;r++) { - d[n.labels[r]] = n; + __extends(a, b); + a.prototype.trace = function(b) { + this.nothingThrownLabel && b.enter("if (label is " + this.nothingThrownLabel + ") {"); + this.determinant.trace(b); + b.writeLn("switch {"); + for (var a = 0, d = this.cases.length;a < d;a++) { + this.cases[a].trace(b); + } + b.writeLn("}"); + this.nothingThrownLabel && b.leave("}"); + }; + return a; + }(e); + a.Switch = d; + d = function(b) { + function a(d, g) { + b.call(this, 6); + this.labels = d; + this.body = g; + } + __extends(a, b); + a.prototype.trace = function(b) { + b.enter("if (label is " + this.labels.join(" or ") + ") {"); + this.body && this.body.trace(b); + b.leave("}"); + }; + return a; + }(e); + a.LabelCase = d; + d = function(b) { + function a(d) { + b.call(this, 7); + this.cases = d; + for (var g = {}, e = 0, f = d.length;e < f;e++) { + for (var k = d[e], c = 0, m = k.labels.length;c < m;c++) { + g[k.labels[c]] = k; } } - this.labelMap = d; + this.labelMap = g; } - __extends(b, a); - b.prototype.trace = function(a) { - for (var b = 0, c = this.cases.length;b < c;b++) { - this.cases[b].trace(a); + __extends(a, b); + a.prototype.trace = function(b) { + for (var a = 0, d = this.cases.length;a < d;a++) { + this.cases[a].trace(b); } }; - return b; - }(b); - a.LabelSwitch = c; - c = function(a) { - function b(c) { - a.call(this, 8); - this.label = c; + return a; + }(e); + a.LabelSwitch = d; + d = function(b) { + function a(d) { + b.call(this, 8); + this.label = d; } - __extends(b, a); - b.prototype.trace = function(a) { - a.writeLn("label = " + this.label); + __extends(a, b); + a.prototype.trace = function(b) { + b.writeLn("label = " + this.label); }; - return b; - }(b); - a.Exit = c; - c = function(a) { - function b(c, d) { - a.call(this, 9); - this.label = c; - this.head = d; + return a; + }(e); + a.Exit = d; + d = function(b) { + function a(d, g) { + b.call(this, 9); + this.label = d; + this.head = g; } - __extends(b, a); - b.prototype.trace = function(a) { - this.label && a.writeLn("label = " + this.label); - a.writeLn("break"); + __extends(a, b); + a.prototype.trace = function(b) { + this.label && b.writeLn("label = " + this.label); + b.writeLn("break"); }; - return b; - }(b); - a.Break = c; - c = function(a) { - function b(c, d) { - a.call(this, 10); - this.label = c; - this.head = d; + return a; + }(e); + a.Break = d; + d = function(b) { + function a(d, g) { + b.call(this, 10); + this.label = d; + this.head = g; this.necessary = !0; } - __extends(b, a); - b.prototype.trace = function(a) { - this.label && a.writeLn("label = " + this.label); - this.necessary && a.writeLn("continue"); + __extends(a, b); + a.prototype.trace = function(b) { + this.label && b.writeLn("label = " + this.label); + this.necessary && b.writeLn("continue"); }; - return b; - }(b); - a.Continue = c; - c = function(a) { - function b(c, d) { - a.call(this, 11); - this.body = c; - this.catches = d; + return a; + }(e); + a.Continue = d; + d = function(b) { + function a(d, g) { + b.call(this, 11); + this.body = d; + this.catches = g; } - __extends(b, a); - b.prototype.trace = function(a) { - a.enter("try {"); - this.body.trace(a); - a.writeLn("label = " + this.nothingThrownLabel); - for (var b = 0, c = this.catches.length;b < c;b++) { - this.catches[b].trace(a); - } - a.leave("}"); - }; - return b; - }(b); - a.Try = c; - b = function(a) { - function b(c, d, e) { - a.call(this, 12); - this.varName = c; - this.typeName = d; + __extends(a, b); + a.prototype.trace = function(b) { + b.enter("try {"); + this.body.trace(b); + b.writeLn("label = " + this.nothingThrownLabel); + for (var a = 0, d = this.catches.length;a < d;a++) { + this.catches[a].trace(b); + } + b.leave("}"); + }; + return a; + }(e); + a.Try = d; + e = function(b) { + function a(d, g, e) { + b.call(this, 12); + this.varName = d; + this.typeName = g; this.body = e; } - __extends(b, a); - b.prototype.trace = function(a) { - a.outdent(); - a.enter("} catch (" + (this.varName || "e") + (this.typeName ? " : " + this.typeName : "") + ") {"); - this.body.trace(a); - }; - return b; - }(b); - a.Catch = b; - })(f.Control || (f.Control = {})); - var d = f.Control, a = b.BitSets.BITS_PER_WORD, c = b.BitSets.ADDRESS_BITS_PER_WORD, n = b.BitSets.BIT_INDEX_MASK, p = function(b) { - function d(a, c) { - b.call(this, a); - this.blockById = c; - } - __extends(d, b); - d.prototype.forEachBlock = function(b) { - m(b); - for (var c = this.blockById, d = this.bits, h = 0, e = d.length;h < e;h++) { - var l = d[h]; - if (l) { - for (var n = 0;n < a;n++) { - l & 1 << n && b(c[h * a + n]); + __extends(a, b); + a.prototype.trace = function(b) { + b.outdent(); + b.enter("} catch (" + (this.varName || "e") + (this.typeName ? " : " + this.typeName : "") + ") {"); + this.body.trace(b); + }; + return a; + }(e); + a.Catch = e; + })(l = a.Control || (a.Control = {})); + var e = c.BitSets.BITS_PER_WORD, m = c.BitSets.ADDRESS_BITS_PER_WORD, t = c.BitSets.BIT_INDEX_MASK, q = function(a) { + function f(d, b) { + a.call(this, d); + this.blockById = b; + } + __extends(f, a); + f.prototype.forEachBlock = function(a) { + u(a); + for (var b = this.blockById, g = this.bits, f = 0, k = g.length;f < k;f++) { + var c = g[f]; + if (c) { + for (var m = 0;m < e;m++) { + c & 1 << m && a(b[f * e + m]); } } } }; - d.prototype.choose = function() { - for (var b = this.blockById, c = this.bits, d = 0, h = c.length;d < h;d++) { - var e = c[d]; - if (e) { - for (var l = 0;l < a;l++) { - if (e & 1 << l) { - return b[d * a + l]; + f.prototype.choose = function() { + for (var a = this.blockById, b = this.bits, g = 0, f = b.length;g < f;g++) { + var k = b[g]; + if (k) { + for (var c = 0;c < e;c++) { + if (k & 1 << c) { + return a[g * e + c]; } } } } }; - d.prototype.members = function() { - for (var b = this.blockById, c = [], d = this.bits, h = 0, e = d.length;h < e;h++) { - var l = d[h]; - if (l) { - for (var n = 0;n < a;n++) { - l & 1 << n && c.push(b[h * a + n]); + f.prototype.members = function() { + for (var a = this.blockById, b = [], g = this.bits, f = 0, k = g.length;f < k;f++) { + var c = g[f]; + if (c) { + for (var m = 0;m < e;m++) { + c & 1 << m && b.push(a[f * e + m]); } } } - return c; + return b; }; - d.prototype.setBlocks = function(a) { - for (var b = this.bits, d = 0, h = a.length;d < h;d++) { - var e = a[d].id; - b[e >> c] |= 1 << (e & n); + f.prototype.setBlocks = function(a) { + for (var b = this.bits, g = 0, e = a.length;g < e;g++) { + var f = a[g].id; + b[f >> m] |= 1 << (f & t); } }; - return d; - }(b.BitSets.Uint32ArrayBitSet); - f.BlockSet = p; - var e = function() { - function a(b) { - this.makeBlockSetFactory(b.blocks.length, b.blocks); + return f; + }(c.BitSets.Uint32ArrayBitSet); + a.BlockSet = q; + var n = function() { + function a(e) { + this.makeBlockSetFactory(e.blocks.length, e.blocks); this.hasExceptions = !1; - this.normalizeReachableBlocks(b.root); + this.normalizeReachableBlocks(e.root); } - a.prototype.makeBlockSetFactory = function(a, b) { - m(!this.boundBlockSet); + a.prototype.makeBlockSetFactory = function(a, d) { + u(!this.boundBlockSet); this.boundBlockSet = function() { - return new p(a, b); + return new q(a, d); }; }; a.prototype.normalizeReachableBlocks = function(a) { - m(0 === a.predecessors.length); - var b = this.boundBlockSet, c = [], d = {}, h = {}, e = [a]; - for (h[a.id] = !0;a = g(e);) { - if (d[a.id]) { - 1 === d[a.id] && (d[a.id] = 2, c.push(a)), h[a.id] = !1, e.pop(); + u(0 === a.predecessors.length); + var d = this.boundBlockSet, b = [], g = {}, e = {}, k = [a]; + for (e[a.id] = !0;a = v(k);) { + if (g[a.id]) { + 1 === g[a.id] && (g[a.id] = 2, b.push(a)), e[a.id] = !1, k.pop(); } else { - d[a.id] = 1; - h[a.id] = !0; - for (var n = a.successors, f = 0, p = n.length;f < p;f++) { - var q = n[f]; - h[q.id] && (a.spbacks || (a.spbacks = new b), a.spbacks.set(q.id)); - !d[q.id] && e.push(q); + g[a.id] = 1; + e[a.id] = !0; + for (var c = a.successors, m = 0, l = c.length;m < l;m++) { + var n = c[m]; + e[n.id] && (a.spbacks || (a.spbacks = new d), a.spbacks.set(n.id)); + !g[n.id] && k.push(n); } } } - this.blocks = c.reverse(); + this.blocks = b.reverse(); }; a.prototype.computeDominance = function() { - var a = this.blocks, b = a.length, c = Array(b); - c[0] = 0; - for (var d = [], h = 0;h < b;h++) { - d[a[h].id] = h, a[h].dominatees = []; - } - for (var e = !0;e;) { - for (e = !1, h = 1;h < b;h++) { - var n = a[h].predecessors, f = n.length, g = d[n[0].id]; - if (!(g in c)) { - for (var p = 1;p < f && !(g = d[n[p].id], g in c);p++) { - } - } - m(g in c); - for (p = 0;p < f;p++) { - var q = d[n[p].id]; - if (q !== g && q in c) { - for (;q !== g;) { - for (;q > g;) { - q = c[q]; + var a = this.blocks, d = a.length, b = Array(d); + b[0] = 0; + for (var g = [], e = 0;e < d;e++) { + g[a[e].id] = e, a[e].dominatees = []; + } + for (var k = !0;k;) { + for (k = !1, e = 1;e < d;e++) { + var c = a[e].predecessors, m = c.length, l = g[c[0].id]; + if (!(l in b)) { + for (var n = 1;n < m && !(l = g[c[n].id], l in b);n++) { + } + } + u(l in b); + for (n = 0;n < m;n++) { + var q = g[c[n].id]; + if (q !== l && q in b) { + for (;q !== l;) { + for (;q > l;) { + q = b[q]; } - for (;g > q;) { - g = c[g]; + for (;l > q;) { + l = b[l]; } } - g = q; + l = q; } } - c[h] !== g && (c[h] = g, e = !0); + b[e] !== l && (b[e] = l, k = !0); } } a[0].dominator = a[0]; - for (h = 1;h < b;h++) { - d = a[h], p = a[c[h]], d.dominator = p, p.dominatees.push(d), d.npredecessors = d.predecessors.length; + for (e = 1;e < d;e++) { + g = a[e], n = a[b[e]], g.dominator = n, n.dominatees.push(g), g.npredecessors = g.predecessors.length; } - b = [a[0]]; - for (a[0].level || (a[0].level = 0);d = b.shift();) { - a = d.dominatees; - for (p = 0;p < a.length;p++) { - a[p].level = d.level + 1; + d = [a[0]]; + for (a[0].level || (a[0].level = 0);g = d.shift();) { + a = g.dominatees; + for (n = 0;n < a.length;n++) { + a[n].level = g.level + 1; } - b.push.apply(b, a); + d.push.apply(d, a); } }; a.prototype.computeFrontiers = function() { - for (var a = this.boundBlockSet, b = this.blocks, c = 0, d = b.length;c < d;c++) { - b[c].frontier = new a; + for (var a = this.boundBlockSet, d = this.blocks, b = 0, g = d.length;b < g;b++) { + d[b].frontier = new a; } - c = 1; - for (d = b.length;c < d;c++) { - var a = b[c], h = a.predecessors; - if (2 <= h.length) { - for (var e = a.dominator, n = 0, f = h.length;n < f;n++) { - for (var g = h[n];g !== e;) { - g.frontier.set(a.id), g = g.dominator; + b = 1; + for (g = d.length;b < g;b++) { + var a = d[b], e = a.predecessors; + if (2 <= e.length) { + for (var k = a.dominator, c = 0, m = e.length;c < m;c++) { + for (var l = e[c];l !== k;) { + l.frontier.set(a.id), l = l.dominator; } } } @@ -19455,33 +20475,33 @@ }; a.prototype.markLoops = function() { function a(b) { - var c = 1, h = {}, d = {}, e = [], l = [], n = [], f = b.level + 1; + var d = 1, g = {}, e = {}, f = [], k = [], c = [], m = b.level + 1; b = [b]; - for (var r, p;r = g(b);) { - if (h[r.id]) { - if (s(l) === r) { - l.pop(); - var q = []; + for (var r, l;r = v(b);) { + if (g[r.id]) { + if (p(k) === r) { + k.pop(); + var w = []; do { - p = e.pop(), d[p.id] = !0, q.push(p); - } while (p !== r); - (1 < q.length || p.spbacks && p.spbacks.get(p.id)) && n.push(q); + l = f.pop(), e[l.id] = !0, w.push(l); + } while (l !== r); + (1 < w.length || l.spbacks && l.spbacks.get(l.id)) && c.push(w); } b.pop(); } else { - h[r.id] = c++; - e.push(r); - l.push(r); - p = r.successors; - for (var q = 0, m = p.length;q < m;q++) { - if (r = p[q], !(r.level < f)) { - var x = r.id; - if (!h[x]) { + g[r.id] = d++; + f.push(r); + k.push(r); + l = r.successors; + for (var w = 0, n = l.length;w < n;w++) { + if (r = l[w], !(r.level < m)) { + var q = r.id; + if (!g[q]) { b.push(r); } else { - if (!d[x]) { - for (;h[s(l).id] > h[x];) { - l.pop(); + if (!e[q]) { + for (;g[p(k).id] > g[q];) { + k.pop(); } } } @@ -19489,917 +20509,901 @@ } } } - return n; + return c; } - function b(a, h) { - var d = new c; - d.setBlocks(a); - d.recount(); - this.id = h; - this.body = d; - this.exit = new c; + function d(a, d) { + var g = new b; + g.setBlocks(a); + g.recount(); + this.id = d; + this.body = g; + this.exit = new b; this.save = {}; - this.head = new c; + this.head = new b; this.npredecessors = 0; } if (!this.analyzedControlFlow && !this.analyzeControlFlow()) { return!1; } - var c = this.boundBlockSet, d = function(a) { - for (var b = new c, h = 0, d = a.length;h < d;h++) { - var e = a[h], l = e.spbacks; - if (l) { - for (var e = e.successors, n = 0, f = e.length;n < f;n++) { - var r = e[n]; - l.get(r.id) && b.set(r.dominator.id); + var b = this.boundBlockSet, g = function(a) { + for (var d = new b, g = 0, e = a.length;g < e;g++) { + var f = a[g], k = f.spbacks; + if (k) { + for (var f = f.successors, c = 0, m = f.length;c < m;c++) { + var r = f[c]; + k.get(r.id) && d.set(r.dominator.id); } } } - return b.members(); + return d.members(); }(this.blocks); - if (0 >= d.length) { + if (0 >= g.length) { return this.markedLoops = !0; } - for (var d = d.sort(function(a, b) { - return a.level - b.level; - }), h = 0, e = d.length - 1;0 <= e;e--) { - var n = d[e], f = a(n); - if (0 !== f.length) { - for (var p = 0, q = f.length;p < q;p++) { - for (var m = f[p], k = new b(m, h++), K = 0, F = m.length;K < F;K++) { - var J = m[K]; - if (J.level === n.level + 1 && !J.loop) { - J.loop = k; - k.head.set(J.id); - for (var A = J.predecessors, B = 0, z = A.length;B < z;B++) { - k.body.get(A[B].id) && J.npredecessors--; + for (var g = g.sort(function(b, a) { + return b.level - a.level; + }), e = 0, k = g.length - 1;0 <= k;k--) { + var c = g[k], m = a(c); + if (0 !== m.length) { + for (var l = 0, n = m.length;l < n;l++) { + for (var q = m[l], t = new d(q, e++), h = 0, s = q.length;h < s;h++) { + var u = q[h]; + if (u.level === c.level + 1 && !u.loop) { + u.loop = t; + t.head.set(u.id); + for (var H = u.predecessors, J = 0, C = H.length;J < C;J++) { + t.body.get(H[J].id) && u.npredecessors--; } - k.npredecessors += J.npredecessors; + t.npredecessors += u.npredecessors; } } - K = 0; - for (F = m.length;K < F;K++) { - J = m[K], J.level === n.level + 1 && (J.npredecessors = k.npredecessors); + h = 0; + for (s = q.length;h < s;h++) { + u = q[h], u.level === c.level + 1 && (u.npredecessors = t.npredecessors); } - k.head.recount(); + t.head.recount(); } } } return this.markedLoops = !0; }; a.prototype.induceControlTree = function() { - function a(b, c) { + function a(b, d) { b.recount(); if (0 === b.count) { return null; } - b.save = c; + b.save = d; return b; } - function b(n, f, p, q, m, k, s) { - for (var K = [];n;) { - if (1 < n.count) { - for (var F = new e, J = {}, A = [], B = n.members(), z = 0, P = B.length;z < P;z++) { - var D = B[z], L = D.id, M; - if (D.loop && n.contains(D.loop.head)) { - var V = D.loop; - if (!V.induced) { - for (var Q = V.head.members(), U = 0, S = 0, aa = Q.length;S < aa;S++) { - U += n.save[Q[S].id]; + function d(k, c, m, n, q, t, h) { + for (var s = [];k;) { + if (1 < k.count) { + for (var p = new g, u = {}, H = [], J = k.members(), C = 0, E = J.length;C < E;C++) { + var F = J[C], I = F.id, G; + if (F.loop && k.contains(F.loop.head)) { + var Z = F.loop; + if (!Z.induced) { + for (var Q = Z.head.members(), S = 0, O = 0, P = Q.length;O < P;O++) { + S += k.save[Q[O].id]; } - if (0 < D.npredecessors - U) { - D.npredecessors -= n.save[L], D.save = n.save[L], M = b(D, F, J, q), A.push(new d.LabelCase([L], M)); + if (0 < F.npredecessors - S) { + F.npredecessors -= k.save[I], F.save = k.save[I], G = d(F, p, u, n), H.push(new l.LabelCase([I], G)); } else { - S = 0; - for (aa = Q.length;S < aa;S++) { - M = Q[S], M.npredecessors -= U, M.save = U; + O = 0; + for (P = Q.length;O < P;O++) { + G = Q[O], G.npredecessors -= S, G.save = S; } - M = b(D, F, J, q); - A.push(new d.LabelCase(V.head.toArray(), M)); - V.induced = !0; + G = d(F, p, u, n); + H.push(new l.LabelCase(Z.head.toArray(), G)); + Z.induced = !0; } } } else { - D.npredecessors -= n.save[L], D.save = n.save[L], M = b(D, F, J, q), A.push(new d.LabelCase([L], M)); + F.npredecessors -= k.save[I], F.save = k.save[I], G = d(F, p, u, n), H.push(new l.LabelCase([I], G)); + } + } + for (var F = [], O = 0, C = 0;C < H.length;C++) { + G = H[C]; + E = G.labels; + Q = P = 0; + for (Z = E.length;Q < Z;Q++) { + I = E[Q], p.get(I) && 0 < J[C].npredecessors - k.save[I] ? F.push(I) : E[P++] = I; } + E.length = P; + 0 < E.length && (H[O++] = G); } - for (var D = [], S = 0, z = 0;z < A.length;z++) { - M = A[z]; - P = M.labels; - Q = aa = 0; - for (V = P.length;Q < V;Q++) { - L = P[Q], F.get(L) && 0 < B[z].npredecessors - n.save[L] ? D.push(L) : P[aa++] = L; - } - P.length = aa; - 0 < P.length && (A[S++] = M); - } - A.length = S; - if (0 === A.length) { - for (z = 0;z < D.length;z++) { - L = D[z], p[L] = (p[L] || 0) + n.save[L], f.set(L); + H.length = O; + if (0 === H.length) { + for (C = 0;C < F.length;C++) { + I = F[C], m[I] = (m[I] || 0) + k.save[I], c.set(I); } break; } - K.push(new d.LabelSwitch(A)); - n = a(F, J); + s.push(new l.LabelSwitch(H)); + k = a(p, u); } else { - 1 === n.count ? (D = n.choose(), L = D.id, D.npredecessors -= n.save[L], D.save = n.save[L]) : (D = n, L = D.id); - if (m) { - m = !1; + 1 === k.count ? (F = k.choose(), I = F.id, F.npredecessors -= k.save[I], F.save = k.save[I]) : (F = k, I = F.id); + if (q) { + q = !1; } else { - if (q && !q.body.get(L)) { - D.npredecessors += D.save; - q.exit.set(L); - q.save[L] = (q.save[L] || 0) + D.save; - K.push(new d.Break(L, q)); + if (n && !n.body.get(I)) { + F.npredecessors += F.save; + n.exit.set(I); + n.save[I] = (n.save[I] || 0) + F.save; + s.push(new l.Break(I, n)); break; } - if (q && D.loop === q) { - D.npredecessors += D.save; - K.push(new d.Continue(L, q)); + if (n && F.loop === n) { + F.npredecessors += F.save; + s.push(new l.Continue(I, n)); break; } - if (D === s) { + if (F === h) { break; } - if (0 < D.npredecessors) { - D.npredecessors += D.save; - p[L] = (p[L] || 0) + D.save; - f.set(L); - K.push(k ? new d.Break(L, k) : new d.Exit(L)); + if (0 < F.npredecessors) { + F.npredecessors += F.save; + m[I] = (m[I] || 0) + F.save; + c.set(I); + s.push(t ? new l.Break(I, t) : new l.Exit(I)); break; } - if (D.loop) { - var aa = D.loop; - if (1 === aa.head.count) { - F = b(aa.head.choose(), null, null, aa, !0); + if (F.loop) { + var P = F.loop; + if (1 === P.head.count) { + p = d(P.head.choose(), null, null, P, !0); } else { - F = []; - Q = aa.head.members(); - z = 0; - for (P = Q.length;z < P;z++) { - M = Q[z], J = M.id, M = b(M, null, null, aa, !0), F.push(new d.LabelCase([J], M)); + p = []; + Q = P.head.members(); + C = 0; + for (E = Q.length;C < E;C++) { + G = Q[C], u = G.id, G = d(G, null, null, P, !0), p.push(new l.LabelCase([u], G)); } - F = new d.LabelSwitch(F); + p = new l.LabelSwitch(p); } - K.push(new d.Loop(F)); - n = a(aa.exit, aa.save); + s.push(new l.Loop(p)); + k = a(P.exit, P.save); continue; } } - F = new e; - J = {}; - if (c && D.hasCatches) { - M = D.successors; - A = []; - n = []; - z = 0; - for (P = M.length;z < P;z++) { - B = M[z], (B.exception ? A : n).push(B); - } - P = []; - for (z = 0;z < A.length;z++) { - B = A[z], B.npredecessors -= 1, B.save = 1, M = b(B, F, J, q), B = B.exception, P.push(new d.Catch(B.varName, B.typeName, M)); + p = new g; + u = {}; + if (b && F.hasCatches) { + G = F.successors; + H = []; + k = []; + C = 0; + for (E = G.length;C < E;C++) { + J = G[C], (J.exception ? H : k).push(J); } - P = new d.Try(D, P); + E = []; + for (C = 0;C < H.length;C++) { + J = H[C], J.npredecessors -= 1, J.save = 1, G = d(J, p, u, n), J = J.exception, E.push(new l.Catch(J.varName, J.typeName, G)); + } + E = new l.Try(F, E); } else { - n = D.successors, P = D; + k = F.successors, E = F; } - if (2 < n.length) { - A = []; - for (z = n.length - 1;0 <= z;z--) { - B = n[z], B.npredecessors -= 1, B.save = 1, M = b(B, F, J, q, null, D, n[z + 1]), A.unshift(new d.Case(z, M)); - } - g(A).index = void 0; - c && D.hasCatches ? (P.nothingThrownLabel = h, P = new d.Switch(P, A, h++)) : P = new d.Switch(P, A); - n = a(F, J); + if (2 < k.length) { + H = []; + for (C = k.length - 1;0 <= C;C--) { + J = k[C], J.npredecessors -= 1, J.save = 1, G = d(J, p, u, n, null, F, k[C + 1]), H.unshift(new l.Case(C, G)); + } + v(H).index = void 0; + b && F.hasCatches ? (E.nothingThrownLabel = e, E = new l.Switch(E, H, e++)) : E = new l.Switch(E, H); + k = a(p, u); } else { - 2 === n.length ? (z = D.hasFlippedSuccessors ? n[1] : n[0], M = D.hasFlippedSuccessors ? n[0] : n[1], z.npredecessors -= 1, z.save = 1, z = b(z, F, J, q), M.npredecessors -= 1, M.save = 1, M = b(M, F, J, q), c && D.hasCatches ? (P.nothingThrownLabel = h, P = new d.If(P, z, M, h++)) : P = new d.If(P, z, M), n = a(F, J)) : (M = n[0]) ? c && D.hasCatches ? (P.nothingThrownLabel = M.id, J[M.id] = (J[M.id] || 0) + 1, F.set(M.id), n = a(F, J)) : (M.npredecessors -= 1, M.save = 1, n = - M) : c && D.hasCatches ? (P.nothingThrownLabel = -1, n = a(F, J)) : n = M; + 2 === k.length ? (C = F.hasFlippedSuccessors ? k[1] : k[0], G = F.hasFlippedSuccessors ? k[0] : k[1], C.npredecessors -= 1, C.save = 1, C = d(C, p, u, n), G.npredecessors -= 1, G.save = 1, G = d(G, p, u, n), b && F.hasCatches ? (E.nothingThrownLabel = e, E = new l.If(E, C, G, e++)) : E = new l.If(E, C, G), k = a(p, u)) : (G = k[0]) ? b && F.hasCatches ? (E.nothingThrownLabel = G.id, u[G.id] = (u[G.id] || 0) + 1, p.set(G.id), k = a(p, u)) : (G.npredecessors -= 1, G.save = 1, k = + G) : b && F.hasCatches ? (E.nothingThrownLabel = -1, k = a(p, u)) : k = G; } - K.push(P); + s.push(E); } } - return 1 < K.length ? new d.Seq(K) : K[0]; + return 1 < s.length ? new l.Seq(s) : s[0]; } - var c = this.hasExceptions, e = this.boundBlockSet, h = this.blocks.length; - this.controlTree = b(this.blocks[0], new e, {}); + var b = this.hasExceptions, g = this.boundBlockSet, e = this.blocks.length; + this.controlTree = d(this.blocks[0], new g, {}); }; a.prototype.restructureControlFlow = function() { - k.enterTimeline("Restructure Control Flow"); - if (this.markedLoops || this.markLoops()) { - this.induceControlTree(), this.restructuredControlFlow = !0; - } - k.leaveTimeline(); + h.enterTimeline("Restructure Control Flow"); + if (!this.markedLoops && !this.markLoops()) { + return h.leaveTimeline(), !1; + } + this.induceControlTree(); + this.restructuredControlFlow = !0; + h.leaveTimeline(); + return!0; }; return a; }(); - f.Analysis = e; - f.analyze = function(a) { - a = new e(a); + a.Analysis = n; + a.analyze = function(a) { + a = new n(a); a.restructureControlFlow(); return a.controlTree; }; - })(g.Looper || (g.Looper = {})); - })(k.Compiler || (k.Compiler = {})); - })(b.AVM2 || (b.AVM2 = {})); + })(a.Looper || (a.Looper = {})); + })(h.Compiler || (h.Compiler = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - function t(c, h) { - if ("string" === typeof c || null === c || !0 === c || !1 === c) { - return new y(c); +(function(c) { + (function(h) { + (function(a) { + (function(s) { + function v(b, a) { + if ("string" === typeof b || null === b || !0 === b || !1 === b) { + return new B(b); } - if (void 0 === c) { - return new G("undefined"); + if (void 0 === b) { + return new M("undefined"); } - if ("object" === typeof c || "function" === typeof c) { - return c instanceof b.AVM2.Runtime.LazyInitializer ? a(d(ka, "C"), [new y(h.useConstant(c))]) : new E(W, new y(h.useConstant(c)), !0); + if ("object" === typeof b || "function" === typeof b) { + return b instanceof c.AVM2.Runtime.LazyInitializer ? m(l(ja, "C"), [new B(a.useConstant(b))]) : new y(aa, new B(a.useConstant(b)), !0); } - if ("number" === typeof c && isNaN(c)) { - return new G("NaN"); + if ("number" === typeof b && isNaN(b)) { + return new M("NaN"); } - if (Infinity === c) { - return new G("Infinity"); + if (Infinity === b) { + return new M("Infinity"); } - if (-Infinity === c) { - return new D("-", !0, new G("Infinity")); + if (-Infinity === b) { + return new G("-", !0, new M("Infinity")); } - if ("number" === typeof c && 0 > 1 / c) { - return new D("-", !0, new y(Math.abs(c))); + if ("number" === typeof b && 0 > 1 / b) { + return new G("-", !0, new B(Math.abs(b))); } - if ("number" === typeof c) { - return new y(c); + if ("number" === typeof b) { + return new B(b); } - r("Cannot emit constant for value: " + c); + r("Cannot emit constant for value: " + b); } - function s(a) { - w("string" === typeof a); - return new G(a); + function p(b) { + g("string" === typeof b); + return new M(b); } - function m(a) { - var b = a[0]; - if (!("$" === b || "_" === b || "\\" === b || "a" <= b && "z" >= b || "A" <= b && "Z" >= b)) { + function u(b) { + var a = b[0]; + if (!("$" === a || "_" === a || "\\" === a || "a" <= a && "z" >= a || "A" <= a && "Z" >= a)) { return!1; } - for (b = 1;b < a.length;b++) { - var c = a[b]; - if (!("$" === c || "_" === c || "\\" === c || "a" <= c && "z" >= c || "A" <= c && "Z" >= c || "0" <= c && "9" >= c)) { + for (a = 1;a < b.length;a++) { + var d = b[a]; + if (!("$" === d || "_" === d || "\\" === d || "a" <= d && "z" >= d || "A" <= d && "Z" >= d || "0" <= d && "9" >= d)) { return!1; } } return!0; } - function d(a) { - for (var b = [], c = 0;c < arguments.length - 1;c++) { - b[c] = arguments[c + 1]; - } - for (c = 0;c < b.length;c++) { - var h = b[c]; - a = "string" === typeof h ? m(h) ? new E(a, new G(h), !1) : new E(a, new y(h), !0) : h instanceof y && m(h.value) ? new E(a, new G(h.value), !1) : new E(a, h, !0); - } - return a; + function l(b, a) { + return u(a) ? new y(b, new M(a), !1) : new y(b, new B(a), !0); + } + function e(b, a) { + return u(a.value) ? new y(b, new M(a.value), !1) : new y(b, a, !0); } - function a(a, b) { - w(b instanceof Array); - b.forEach(function(a) { - w(!(a instanceof Array)); - w(void 0 !== a); + function m(b, a) { + g(a instanceof Array); + a.forEach(function(b) { + g(!(b instanceof Array)); + g(void 0 !== b); }); - return new K(a, b); + return new L(b, a); + } + function t(b, a, d) { + return m(l(b, "asCall"), [a].concat(d)); + } + function q(b, a, d) { + return m(l(b, "call"), [a].concat(d)); } - function c(b, c, h) { - return a(d(b, "asCall"), [c].concat(h)); + function n(b, a) { + g(b && a); + return new H("=", b, a); } - function n(b, c, h) { - return a(d(b, "call"), [c].concat(h)); + function k(b) { + return new N(b, "var"); } - function p(a, b) { - w(a && b); - return new F("=", a, b); - } - function e(a) { - return new I(a, "var"); - } - function q(a, b, c) { - w(a); - w(a.compile, "Implement |compile| for " + a + " (" + a.nodeName + ")"); - w(b instanceof ha); - w(!(a instanceof Array)); - if (c || !a.variable) { - return a.compile(b); - } - w(a.variable, "Value has no variable: " + a); - return s(a.variable.name); - } - function l(a, b) { - return[q(a.namespaces, b), q(a.name, b), t(a.flags)]; - } - function u(a, b) { - w(a instanceof Array); - return a.map(function(a) { - return q(a, b); + function f(b, a, d) { + g(b); + g(b.compile, "Implement |compile| for " + b + " (" + b.nodeName + ")"); + g(a instanceof ia); + g(!(b instanceof Array)); + if (d || !b.variable) { + return b.compile(a); + } + g(b.variable, "Value has no variable: " + b); + return p(b.variable.name); + } + function d(b, a) { + return[f(b.namespaces, a), f(b.name, a), f(b.flags, a)]; + } + function b(b, a) { + g(b instanceof Array); + return b.map(function(b) { + return f(b, a); }); } - var w = b.Debug.assert, r = b.Debug.unexpected, h = b.Debug.notImplemented, x = b.ArrayUtilities.pushUnique, y = g.AST.Literal, G = g.AST.Identifier, I = g.AST.VariableDeclaration, C = g.AST.VariableDeclarator, E = g.AST.MemberExpression, O = g.AST.BinaryExpression, K = g.AST.CallExpression, F = g.AST.AssignmentExpression, J = g.AST.ExpressionStatement, A = g.AST.ReturnStatement, B = g.AST.ConditionalExpression, z = g.AST.ObjectExpression, P = g.AST.ArrayExpression, D = g.AST.UnaryExpression, - L = g.AST.NewExpression, M = g.AST.Property, V = g.AST.BlockStatement, Q = g.AST.ThisExpression, U = g.AST.ThrowStatement, S = g.AST.IfStatement, aa = g.AST.WhileStatement, $ = g.AST.BreakStatement, ea = g.AST.ContinueStatement, Z = g.AST.SwitchStatement, v = g.AST.SwitchCase, X = g.IR.Start, ba = g.IR.Variable, R = g.IR.Constant, H = g.IR.Operator, Y = b.AVM2.Compiler.Looper, ga = Y.Control, ca = b.ArrayUtilities.last; - ga.Break.prototype.compile = function(a) { - return a.compileBreak(this); + var g = c.Debug.assert, r = c.Debug.unexpected, w = c.Debug.notImplemented, z = c.ArrayUtilities.pushUnique, A = c.AVM2.Compiler.AST, B = A.Literal, M = A.Identifier, N = A.VariableDeclaration, K = A.VariableDeclarator, y = A.MemberExpression, D = A.BinaryExpression, L = A.CallExpression, H = A.AssignmentExpression, J = A.ExpressionStatement, C = A.ReturnStatement, E = A.ConditionalExpression, F = A.ObjectExpression, I = A.ArrayExpression, G = A.UnaryExpression, Z = A.NewExpression, Q = A.Property, + S = A.BlockStatement, O = A.ThisExpression, P = A.ThrowStatement, V = A.IfStatement, $ = A.WhileStatement, W = A.BreakStatement, x = A.ContinueStatement, ea = A.SwitchStatement, Y = A.SwitchCase, R = a.IR.Start, U = a.IR.Variable, ba = a.IR.Constant, X = a.IR.Operator, A = c.AVM2.Compiler.Looper.Control, ga = c.ArrayUtilities.last; + A.Break.prototype.compile = function(b) { + return b.compileBreak(this); }; - ga.Continue.prototype.compile = function(a) { - return a.compileContinue(this); + A.Continue.prototype.compile = function(b) { + return b.compileContinue(this); }; - ga.Exit.prototype.compile = function(a) { - return a.compileExit(this); + A.Exit.prototype.compile = function(b) { + return b.compileExit(this); }; - ga.LabelSwitch.prototype.compile = function(a) { - return a.compileLabelSwitch(this); + A.LabelSwitch.prototype.compile = function(b) { + return b.compileLabelSwitch(this); }; - ga.Seq.prototype.compile = function(a) { - return a.compileSequence(this); + A.Seq.prototype.compile = function(b) { + return b.compileSequence(this); }; - ga.Loop.prototype.compile = function(a) { - return a.compileLoop(this); + A.Loop.prototype.compile = function(b) { + return b.compileLoop(this); }; - ga.Switch.prototype.compile = function(a) { - return a.compileSwitch(this); + A.Switch.prototype.compile = function(b) { + return b.compileSwitch(this); }; - ga.If.prototype.compile = function(a) { - return a.compileIf(this); + A.If.prototype.compile = function(b) { + return b.compileIf(this); }; - ga.Try.prototype.compile = function(a) { - h("try"); + A.Try.prototype.compile = function(b) { + w("try"); return null; }; - var ka = new G("$F"), W = new G("$C"), ha = function() { - function a() { - this.label = new ba("$L"); + var ja = new M("$F"), aa = new M("$C"), ia = function() { + function b() { + this.label = new U("$L"); this.variables = []; this.constants = []; + this.lazyConstants = []; this.parameters = []; } - a.prototype.useConstant = function(a) { - return x(this.constants, a); - }; - a.prototype.useVariable = function(a) { - w(a); - x(this.variables, a); - }; - a.prototype.useParameter = function(a) { - this.parameters[a.index] = a; - }; - a.prototype.compileLabelBody = function(a) { - var b = []; - void 0 !== a.label && (this.useVariable(this.label), b.push(new J(p(s(this.label.name), new y(a.label))))); + b.prototype.useConstant = function(b) { + b instanceof c.AVM2.Runtime.LazyInitializer ? (b = z(this.lazyConstants, b), this.constants[b] = null) : (b = z(this.constants, b), this.lazyConstants[b] = null); return b; }; - a.prototype.compileBreak = function(a) { - a = this.compileLabelBody(a); - a.push(new $(null)); - return new V(a); - }; - a.prototype.compileContinue = function(a) { - a = this.compileLabelBody(a); - a.push(new ea(null)); - return new V(a); - }; - a.prototype.compileExit = function(a) { - return new V(this.compileLabelBody(a)); - }; - a.prototype.compileIf = function(a) { - var b = a.cond.compile(this), c = null, h = null; - a.then && (c = a.then.compile(this)); - a.else && (h = a.else.compile(this)); - var d = q(b.end.predicate, this); - if (a.negated) { + b.prototype.useVariable = function(b) { + g(b); + return z(this.variables, b); + }; + b.prototype.useParameter = function(b) { + return this.parameters[b.index] = b; + }; + b.prototype.compileLabelBody = function(b) { + var a = []; + void 0 !== b.label && (this.useVariable(this.label), a.push(new J(n(p(this.label.name), new B(b.label))))); + return a; + }; + b.prototype.compileBreak = function(b) { + b = this.compileLabelBody(b); + b.push(new W(null)); + return new S(b); + }; + b.prototype.compileContinue = function(b) { + b = this.compileLabelBody(b); + b.push(new x(null)); + return new S(b); + }; + b.prototype.compileExit = function(b) { + return new S(this.compileLabelBody(b)); + }; + b.prototype.compileIf = function(b) { + var a = b.cond.compile(this), d = null, e = null; + b.then && (d = b.then.compile(this)); + b.else && (e = b.else.compile(this)); + var k = f(a.end.predicate, this); + if (b.negated) { a: { - a = d; - if (a instanceof R) { - if (!0 === a.value || !1 === a.value) { - a = t(!a.value); + b = k; + if (b instanceof ba) { + if (!0 === b.value || !1 === b.value) { + b = v(!b.value); break a; } } else { - if (a instanceof G) { - a = new D(H.FALSE.name, !0, a); + if (b instanceof M) { + b = new G(X.FALSE.name, !0, b); break a; } } - w(a instanceof O || a instanceof D, a); - var d = a instanceof O ? a.left : a.argument, e = a.right, l = H.fromName(a.operator); - a = l === H.EQ && e instanceof y && !1 === e.value || l === H.FALSE ? d : l.not ? a instanceof O ? new O(l.not.name, d, e) : new D(l.not.name, !0, d) : new D(H.FALSE.name, !0, a); + g(b instanceof D || b instanceof G, b); + var k = b instanceof D ? b.left : b.argument, c = b.right, m = X.fromName(b.operator); + b = m === X.EQ && c instanceof B && !1 === c.value || m === X.FALSE ? k : m.not ? b instanceof D ? new D(m.not.name, k, c) : new G(m.not.name, !0, k) : new G(X.FALSE.name, !0, b); } } else { - a = d; + b = k; } - b.body.push(new S(a, c || new V([]), h || null)); - return b; + a.body.push(new V(b, d || new S([]), e || null)); + return a; }; - a.prototype.compileSwitch = function(a) { - var b = a.determinant.compile(this), c = []; - a.cases.forEach(function(a) { - var b; - a.body && (b = a.body.compile(this)); - a = "number" === typeof a.index ? new y(a.index) : void 0; - c.push(new v(a, b ? [b] : [])); + b.prototype.compileSwitch = function(b) { + var a = b.determinant.compile(this), d = []; + b.cases.forEach(function(b) { + var a; + b.body && (a = b.body.compile(this)); + b = "number" === typeof b.index ? new B(b.index) : void 0; + d.push(new Y(b, a ? [a] : [])); }, this); - a = q(b.end.determinant, this); - b.body.push(new Z(a, c, !1)); - return b; + b = f(a.end.determinant, this); + a.body.push(new ea(b, d, !1)); + return a; }; - a.prototype.compileLabelSwitch = function(a) { - function b(a) { - w("number" === typeof a); - return new O("===", h, new y(a)); + b.prototype.compileLabelSwitch = function(b) { + function a(b) { + g("number" === typeof b); + return new D("===", e, new B(b)); } - for (var c = null, h = s(this.label.name), d = a.cases.length - 1;0 <= d;d--) { - for (var e = a.cases[d], l = e.labels, n = b(l[0]), f = 1;f < l.length;f++) { - n = new O("||", n, b(l[f])); + for (var d = null, e = p(this.label.name), f = b.cases.length - 1;0 <= f;f--) { + for (var k = b.cases[f], c = k.labels, m = a(c[0]), r = 1;r < c.length;r++) { + m = new D("||", m, a(c[r])); } - c = new S(n, e.body ? e.body.compile(this) : new V([]), c); + d = new V(m, k.body ? k.body.compile(this) : new S([]), d); } - return c; + return d; }; - a.prototype.compileLoop = function(a) { - a = a.body.compile(this); - return new aa(t(!0), a); - }; - a.prototype.compileSequence = function(a) { - var b = this, c = []; - a.body.forEach(function(a) { - a = a.compile(b); - a instanceof V ? c = c.concat(a.body) : c.push(a); + b.prototype.compileLoop = function(b) { + b = b.body.compile(this); + return new $(v(!0), b); + }; + b.prototype.compileSequence = function(b) { + var a = this, d = []; + b.body.forEach(function(b) { + b = b.compile(a); + b instanceof S ? d = d.concat(b.body) : d.push(b); }); - return new V(c); + return new S(d); }; - a.prototype.compileBlock = function(a) { - for (var b = [], c = 1;c < a.nodes.length - 1;c++) { - var h = a.nodes[c], d; - h instanceof g.IR.Throw ? d = q(h, this, !0) : (h instanceof g.IR.Move ? (d = s(h.to.name), this.useVariable(h.to), h = q(h.from, this)) : (h.variable ? (d = s(h.variable.name), this.useVariable(h.variable)) : d = null, h = q(h, this, !0)), d = d ? new J(p(d, h)) : new J(h)); - b.push(d); - } - c = ca(a.nodes); - c instanceof g.IR.Stop && b.push(new A(q(c.argument, this))); - b = new V(b); - b.end = ca(a.nodes); - w(b.end instanceof g.IR.End); - return b; + b.prototype.compileBlock = function(b) { + for (var d = [], e = 1;e < b.nodes.length - 1;e++) { + var k = b.nodes[e], c; + k instanceof a.IR.Throw ? c = f(k, this, !0) : (k instanceof a.IR.Move ? (c = p(k.to.name), this.useVariable(k.to), k = f(k.from, this)) : (k.variable ? (c = p(k.variable.name), this.useVariable(k.variable)) : c = null, k = f(k, this, !0)), c = c ? new J(n(c, k)) : new J(k)); + d.push(c); + } + e = ga(b.nodes); + e instanceof a.IR.Stop && d.push(new C(f(e.argument, this))); + d = new S(d); + d.end = ga(b.nodes); + g(d.end instanceof a.IR.End); + return d; }; - return a; + return b; }(); - f.Context = ha; - g.IR.Parameter.prototype.compile = function(a) { - a.useParameter(this); - return s(this.name); - }; - g.IR.Constant.prototype.compile = function(a) { - return t(this.value, a); - }; - g.IR.Variable.prototype.compile = function(a) { - return s(this.name); - }; - g.IR.Phi.prototype.compile = function(a) { - w(this.variable); - return q(this.variable, a); - }; - g.IR.ASScope.prototype.compile = function(a) { - var b = q(this.parent, a); - a = q(this.object, a); - var c = new y(this.isWith); - return new L(s("Scope"), [b, a, c]); - }; - g.IR.ASFindProperty.prototype.compile = function(b) { - var c = q(this.scope, b), h = l(this.name, b); - b = q(this.methodInfo, b); - var e = new y(this.strict); - return a(d(c, "findScopeProperty"), h.concat([b, e])); + s.Context = ia; + a.IR.Parameter.prototype.compile = function(b) { + b.useParameter(this); + return p(this.name); + }; + a.IR.Constant.prototype.compile = function(b) { + return v(this.value, b); + }; + a.IR.Variable.prototype.compile = function(b) { + return p(this.name); + }; + a.IR.Phi.prototype.compile = function(b) { + g(this.variable); + return f(this.variable, b); + }; + a.IR.ASScope.prototype.compile = function(b) { + var a = f(this.parent, b); + b = f(this.object, b); + var d = new B(this.isWith); + return new Z(p("Scope"), [a, b, d]); + }; + a.IR.ASFindProperty.prototype.compile = function(b) { + var a = f(this.scope, b), g = d(this.name, b); + b = f(this.methodInfo, b); + var e = new B(this.strict); + return m(l(a, "findScopeProperty"), g.concat([b, e])); }; - g.IR.ASGetProperty.prototype.compile = function(b) { - var c = q(this.object, b); + a.IR.ASGetProperty.prototype.compile = function(b) { + var a = f(this.object, b); if (this.flags & 1) { - return w(!(this.flags & 8)), a(d(c, "asGetNumericProperty"), [q(this.name.name, b)]); + return g(!(this.flags & 8)), m(l(a, "asGetNumericProperty"), [f(this.name.name, b)]); } if (this.flags & 2) { - return a(d(c, "asGetResolvedStringProperty"), [q(this.name, b)]); + return m(l(a, "asGetResolvedStringProperty"), [f(this.name, b)]); } - b = l(this.name, b); - var h = new y(this.flags & 8); - return a(d(c, "asGetProperty"), b.concat(h)); - }; - g.IR.ASGetSuper.prototype.compile = function(b) { - var c = q(this.scope, b), h = q(this.object, b); - b = l(this.name, b); - return a(d(h, "asGetSuper"), [c].concat(b)); - }; - g.IR.Latch.prototype.compile = function(a) { - return new B(q(this.condition, a), q(this.left, a), q(this.right, a)); - }; - g.IR.Unary.prototype.compile = function(a) { - return new D(this.operator.name, !0, q(this.argument, a)); - }; - g.IR.Copy.prototype.compile = function(a) { - return q(this.argument, a); - }; - g.IR.Binary.prototype.compile = function(b) { - var c = q(this.left, b); - b = q(this.right, b); - return this.operator === g.IR.Operator.AS_ADD ? a(s("asAdd"), [c, b]) : new O(this.operator.name, c, b); - }; - g.IR.CallProperty.prototype.compile = function(b) { - var h = q(this.object, b), e = q(this.name, b), e = d(h, e), l = this.args.map(function(a) { - return q(a, b); + b = d(this.name, b); + var e = new B(this.flags & 8); + return m(l(a, "asGetProperty"), b.concat(e)); + }; + a.IR.ASGetSuper.prototype.compile = function(b) { + var a = f(this.scope, b), g = f(this.object, b); + b = d(this.name, b); + return m(l(g, "asGetSuper"), [a].concat(b)); + }; + a.IR.Latch.prototype.compile = function(b) { + return new E(f(this.condition, b), f(this.left, b), f(this.right, b)); + }; + a.IR.Unary.prototype.compile = function(b) { + return new G(this.operator.name, !0, f(this.argument, b)); + }; + a.IR.Copy.prototype.compile = function(b) { + return f(this.argument, b); + }; + a.IR.Binary.prototype.compile = function(b) { + var d = f(this.left, b); + b = f(this.right, b); + return this.operator === a.IR.Operator.AS_ADD ? m(p("asAdd"), [d, b]) : new D(this.operator.name, d, b); + }; + a.IR.CallProperty.prototype.compile = function(b) { + var a = f(this.object, b), d = f(this.name, b), d = e(a, d), g = this.args.map(function(a) { + return f(a, b); }); - return this.flags & 16 ? c(e, h, l) : this.flags & 4 ? a(e, l) : n(e, h, l); + return this.flags & 16 ? t(d, a, g) : this.flags & 4 ? m(d, g) : q(d, a, g); }; - g.IR.ASCallProperty.prototype.compile = function(b) { - var c = q(this.object, b), h = this.args.map(function(a) { - return q(a, b); + a.IR.ASCallProperty.prototype.compile = function(b) { + var a = f(this.object, b), g = this.args.map(function(a) { + return f(a, b); }); if (this.flags & 2) { - return a(d(c, "asCallResolvedStringProperty"), [q(this.name, b), new y(this.isLex), new P(h)]); + return m(l(a, "asCallResolvedStringProperty"), [f(this.name, b), new B(this.isLex), new I(g)]); } - var e = l(this.name, b); - return a(d(c, "asCallProperty"), e.concat([new y(this.isLex), new P(h)])); + var e = d(this.name, b); + return m(l(a, "asCallProperty"), e.concat([new B(this.isLex), new I(g)])); }; - g.IR.ASCallSuper.prototype.compile = function(b) { - var c = q(this.scope, b), h = q(this.object, b), e = this.args.map(function(a) { - return q(a, b); - }), n = l(this.name, b); - return a(d(h, "asCallSuper"), [c].concat(n).concat(new P(e))); - }; - g.IR.Call.prototype.compile = function(b) { - var h = this.args.map(function(a) { - return q(a, b); - }), d = q(this.callee, b), e; - e = this.object ? q(this.object, b) : new y(null); - return this.flags & 16 ? c(d, e, h) : null === this.object ? a(d, h) : n(d, e, h); - }; - g.IR.ASNew.prototype.compile = function(a) { - var b = this.args.map(function(b) { - return q(b, a); - }), c = q(this.callee, a), c = d(c, "instanceConstructor"); - return new L(c, b); - }; - g.IR.This.prototype.compile = function(a) { - return new Q; - }; - g.IR.Throw.prototype.compile = function(a) { - a = q(this.argument, a); - return new U(a); - }; - g.IR.Arguments.prototype.compile = function(a) { - return s("arguments"); - }; - g.IR.ASGlobal.prototype.compile = function(a) { - a = q(this.scope, a); - return d(a, "global", "object"); + a.IR.ASCallSuper.prototype.compile = function(b) { + var a = f(this.scope, b), g = f(this.object, b), e = this.args.map(function(a) { + return f(a, b); + }), k = d(this.name, b); + return m(l(g, "asCallSuper"), [a].concat(k).concat(new I(e))); + }; + a.IR.Call.prototype.compile = function(b) { + var a = this.args.map(function(a) { + return f(a, b); + }), d = f(this.callee, b), g; + g = this.object ? f(this.object, b) : new B(null); + return this.flags & 16 ? t(d, g, a) : null === this.object ? m(d, a) : q(d, g, a); + }; + a.IR.ASNew.prototype.compile = function(b) { + var a = this.args.map(function(a) { + return f(a, b); + }), d = f(this.callee, b), d = l(d, "instanceConstructor"); + return new Z(d, a); + }; + a.IR.This.prototype.compile = function(b) { + return new O; + }; + a.IR.Throw.prototype.compile = function(b) { + b = f(this.argument, b); + return new P(b); + }; + a.IR.Arguments.prototype.compile = function(b) { + return p("arguments"); + }; + a.IR.ASGlobal.prototype.compile = function(b) { + b = f(this.scope, b); + return l(l(b, "global"), "object"); }; - g.IR.ASSetProperty.prototype.compile = function(b) { - var c = q(this.object, b), h = q(this.value, b); + a.IR.ASSetProperty.prototype.compile = function(b) { + var a = f(this.object, b), g = f(this.value, b); if (this.flags & 1) { - return a(d(c, "asSetNumericProperty"), [q(this.name.name, b), h]); + return m(l(a, "asSetNumericProperty"), [f(this.name.name, b), g]); } - b = l(this.name, b); - return a(d(c, "asSetProperty"), b.concat(h)); + b = d(this.name, b); + return m(l(a, "asSetProperty"), b.concat(g)); }; - g.IR.ASSetSuper.prototype.compile = function(b) { - var c = q(this.scope, b), h = q(this.object, b), e = l(this.name, b); - b = q(this.value, b); - return a(d(h, "asSetSuper"), [c].concat(e).concat([b])); - }; - g.IR.ASDeleteProperty.prototype.compile = function(b) { - var c = q(this.object, b); - b = l(this.name, b); - return a(d(c, "asDeleteProperty"), b); - }; - g.IR.ASHasProperty.prototype.compile = function(b) { - var c = q(this.object, b); - b = l(this.name, b); - return a(d(c, "asHasProperty"), b); - }; - g.IR.GlobalProperty.prototype.compile = function(a) { - return s(this.name); - }; - g.IR.GetProperty.prototype.compile = function(a) { - var b = q(this.object, a); - a = q(this.name, a); - return d(b, a); - }; - g.IR.SetProperty.prototype.compile = function(a) { - var b = q(this.object, a), c = q(this.name, a); - a = q(this.value, a); - return p(d(b, c), a); - }; - g.IR.ASGetDescendants.prototype.compile = function(b) { - var c = q(this.object, b); - b = q(this.name, b); - return a(s("getDescendants"), [c, b]); - }; - g.IR.ASSetSlot.prototype.compile = function(b) { - var c = q(this.object, b), h = q(this.name, b); - b = q(this.value, b); - return a(s("asSetSlot"), [c, h, b]); - }; - g.IR.ASGetSlot.prototype.compile = function(b) { - var c = q(this.object, b); - b = q(this.name, b); - return a(s("asGetSlot"), [c, b]); - }; - g.IR.Projection.prototype.compile = function(a) { - w(4 === this.type); - w(this.argument instanceof X); - return q(this.argument.scope, a); - }; - g.IR.NewArray.prototype.compile = function(a) { - return new P(u(this.elements, a)); - }; - g.IR.NewObject.prototype.compile = function(a) { - var b = this.properties.map(function(b) { - var c = q(b.key, a); - b = q(b.value, a); - return new M(c, b, "init"); + a.IR.ASSetSuper.prototype.compile = function(b) { + var a = f(this.scope, b), g = f(this.object, b), e = d(this.name, b); + b = f(this.value, b); + return m(l(g, "asSetSuper"), [a].concat(e).concat([b])); + }; + a.IR.ASDeleteProperty.prototype.compile = function(b) { + var a = f(this.object, b); + b = d(this.name, b); + return m(l(a, "asDeleteProperty"), b); + }; + a.IR.ASHasProperty.prototype.compile = function(b) { + var a = f(this.object, b); + b = d(this.name, b); + return m(l(a, "asHasProperty"), b); + }; + a.IR.GlobalProperty.prototype.compile = function(b) { + return p(this.name); + }; + a.IR.GetProperty.prototype.compile = function(b) { + var a = f(this.object, b); + b = f(this.name, b); + return e(a, b); + }; + a.IR.SetProperty.prototype.compile = function(b) { + var a = f(this.object, b), d = f(this.name, b); + b = f(this.value, b); + return n(e(a, d), b); + }; + a.IR.ASGetDescendants.prototype.compile = function(b) { + var a = f(this.object, b); + b = f(this.name, b); + return m(p("getDescendants"), [a, b]); + }; + a.IR.ASSetSlot.prototype.compile = function(b) { + var a = f(this.object, b), d = f(this.name, b); + b = f(this.value, b); + return m(p("asSetSlot"), [a, d, b]); + }; + a.IR.ASGetSlot.prototype.compile = function(b) { + var a = f(this.object, b); + b = f(this.name, b); + return m(p("asGetSlot"), [a, b]); + }; + a.IR.Projection.prototype.compile = function(b) { + g(4 === this.type); + g(this.argument instanceof R); + return f(this.argument.scope, b); + }; + a.IR.NewArray.prototype.compile = function(a) { + return new I(b(this.elements, a)); + }; + a.IR.NewObject.prototype.compile = function(b) { + var a = this.properties.map(function(a) { + var d = f(a.key, b); + a = f(a.value, b); + return new Q(d, a, "init"); }); - return new z(b); + return new F(a); }; - g.IR.ASNewActivation.prototype.compile = function(b) { - b = q(this.methodInfo, b); - return a(s("asCreateActivation"), [b]); - }; - g.IR.ASNewHasNext2.prototype.compile = function(a) { - return new L(s("HasNext2Info"), []); - }; - g.IR.ASMultiname.prototype.compile = function(b) { - var c = q(this.namespaces, b); - b = q(this.name, b); - return a(s("createName"), [c, b]); - }; - g.IR.Block.prototype.compile = function(a) { - return a.compileBlock(this); - }; - var da = function() { - function a(b, c, h) { - this.parameters = b; - this.body = c; - this.constants = h; - } - a.prototype.C = function(a) { - var b = this.constants[a]; - b._isLazyInitializer && (this.constants[a] = b.resolve()); - return this.constants[a]; + a.IR.ASNewActivation.prototype.compile = function(b) { + b = f(this.methodInfo, b); + return m(p("asCreateActivation"), [b]); + }; + a.IR.ASNewHasNext2.prototype.compile = function(b) { + return new Z(p("HasNext2Info"), []); + }; + a.IR.ASMultiname.prototype.compile = function(b) { + var a = f(this.namespaces, b), d = f(this.name, b); + b = f(this.flags, b); + return m(p("createName"), [a, d, b]); + }; + a.IR.Block.prototype.compile = function(b) { + return b.compileBlock(this); + }; + var T = function() { + function b(a, d, g, e) { + this.parameters = a; + this.body = d; + this.constants = g; + this.lazyConstants = e; + } + b.prototype.C = function(b) { + null === this.constants[b] && (this.constants[b] = this.lazyConstants[b].resolve(), this.lazyConstants[b] = null); + return this.constants[b]; }; - a.id = 0; - return a; + b.id = 0; + return b; }(); - f.Compilation = da; - f.generate = function(a) { - k.enterTimeline("Looper"); - var c = Y.analyze(a); - k.leaveTimeline(); - new b.IndentingWriter; - a = new ha; - k.enterTimeline("Construct AST"); - var h = c.compile(a); - k.leaveTimeline(); - for (var c = [], d = 0;d < a.parameters.length;d++) { - c.push(s(a.parameters[d] ? a.parameters[d].name : "_" + d)); - } - d = "$$F" + da.id++; - if (a.constants.length) { - var l = new G(d), n = new E(l, new G("constants"), !1); - h.body.unshift(e([new C(s("$F"), l), new C(s("$C"), n)])); - } - a.variables.length && (k.countTimeline("Backend: Locals", a.variables.length), l = e(a.variables.map(function(a) { - return new C(s(a.name)); - })), h.body.unshift(l)); - k.enterTimeline("Serialize AST"); - h = h.toSource(); - k.leaveTimeline(); - return jsGlobal[d] = new da(c.map(function(a) { - return a.name; - }), h, a.constants); - }; - })(g.Backend || (g.Backend = {})); - })(k.Compiler || (k.Compiler = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - function f(c) { - k.enterTimeline("executeScript", {name:c.name}); - var d = c.abc; - p(!c.executing && !c.executed); - var h = new g.Global(c); - d.applicationDomain.allowNatives && (h[a.getPublicQualifiedName("unsafeJSNative")] = b.AVM2.AS.getNative); - c.executing = !0; - d = new g.Scope(null, c.global); - g.createFunction(c.init, d, !1, !1).call(c.global, !1); - c.executed = !0; - k.leaveTimeline(); - } - function t(a, c) { - "undefined" === typeof c && (c = ""); - a.executed || a.executing || (2 <= b.AVM2.Runtime.traceExecution.value && log("Executing Script For: " + c), f(a)); + s.Compilation = T; + s.generate = function(b) { + h.enterTimeline("Looper"); + var d = a.Looper.analyze(b); + h.leaveTimeline(); + new c.IndentingWriter; + b = new ia; + h.enterTimeline("Construct AST"); + var g = d.compile(b); + h.leaveTimeline(); + for (var d = [], e = 0;e < b.parameters.length;e++) { + d.push(p(b.parameters[e] ? b.parameters[e].name : "_" + e)); + } + e = "$$F" + T.id++; + if (b.constants.length) { + var f = new M(e), m = new y(f, new M("constants"), !1); + g.body.unshift(k([new K(p("$F"), f), new K(p("$C"), m)])); + } + b.variables.length && (h.countTimeline("Backend: Locals", b.variables.length), f = k(b.variables.map(function(b) { + return new K(p(b.name)); + })), g.body.unshift(f)); + h.enterTimeline("Serialize AST"); + g = g.toSource(); + h.leaveTimeline(); + return jsGlobal[e] = new T(d.map(function(b) { + return b.name; + }), g, b.constants, b.lazyConstants); + }; + })(a.Backend || (a.Backend = {})); + })(h.Compiler || (h.Compiler = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + function s(d) { + h.enterTimeline("executeScript", {name:d.name}); + var b = d.abc; + t(!d.executing && !d.executed); + var g = new a.Global(d); + b.applicationDomain.allowNatives && (g[e.getPublicQualifiedName("unsafeJSNative")] = c.AVM2.AS.getNative); + d.executing = !0; + b = new a.Scope(null, d.global); + a.createFunction(d.init, b, !1, !1).call(d.global, !1); + d.executed = !0; + h.leaveTimeline(); } - function s(a) { - if (!g.playerglobal) { + function v(a, b) { + void 0 === b && (b = ""); + a.executed || a.executing || (2 <= c.AVM2.Runtime.traceExecution.value && console.log("Executing Script For: " + b), s(a)); + } + function p(d) { + if (!a.playerglobal) { return null; } - for (var b = 0;b < a.namespaces.length;b++) { - var c = g.playerglobal.map[a.namespaces[b].uri + ":" + a.name]; - if (c) { + for (var b = 0;b < d.namespaces.length;b++) { + var g = a.playerglobal.map[d.namespaces[b].uri + ":" + d.name]; + if (g) { break; } } - return c ? (a = c, a = (b = g.playerglobal.scripts[a]) ? new d(new Uint8Array(g.playerglobal.abcs, b.offset, b.length), a) : null, a) : null; + return g ? (d = g, d = (b = a.playerglobal.scripts[d]) ? new l(new Uint8Array(a.playerglobal.abcs, b.offset, b.length), d) : null, d) : null; } - function m(a, b) { - return new Promise(function(c, d) { - var e = new XMLHttpRequest; - e.open("GET", a); - e.responseType = b; - e.onload = function() { - var l = e.response; - l ? ("json" === b && "json" !== e.responseType && (l = JSON.parse(l)), c(l)) : d("Unable to load " + a + ": " + e.statusText); + function u(a, b) { + return new Promise(function(g, e) { + var f = new XMLHttpRequest; + f.open("GET", a); + f.responseType = b; + f.onload = function() { + var k = f.response; + k ? ("json" === b && "json" !== f.responseType && (k = JSON.parse(k)), g(k)) : e("Unable to load " + a + ": " + f.statusText); }; - e.send(); + f.send(); }); } - var d = b.AVM2.ABC.AbcFile, a = b.AVM2.ABC.Multiname, c = b.Callback, n = b.ObjectUtilities.createEmptyObject, p = b.Debug.assert, e = b.IndentingWriter; - g.executeScript = f; - g.ensureScriptIsExecuted = t; + var l = c.AVM2.ABC.AbcFile, e = c.AVM2.ABC.Multiname, m = c.Callback, t = c.Debug.assert, q = c.IndentingWriter; + a.executeScript = s; + a.ensureScriptIsExecuted = v; (function(a) { a[a.PUBLIC_PROPERTIES = 1] = "PUBLIC_PROPERTIES"; a[a.PUBLIC_METHODS = 2] = "PUBLIC_METHODS"; a[a.ALL = 3] = "ALL"; - })(g.Glue || (g.Glue = {})); - g.playerglobalLoadedPromise; - g.playerglobal; - var q = function() { - function a(b, c, d) { - this.systemDomain = new l(this, null, b, !0); - this.applicationDomain = new l(this, this.systemDomain, c, !1); - this.findDefiningAbc = s; - this._loadAVM1 = d; - this._loadAVM1Promise = null; + })(a.Glue || (a.Glue = {})); + a.playerglobalLoadedPromise; + a.playerglobal; + var n = function() { + function d(b, a) { + this.systemDomain = new k(this, null, b, !0); + this.applicationDomain = new k(this, this.systemDomain, a, !1); + this.findDefiningAbc = p; this.exception = {value:void 0}; this.exceptions = []; - this.globals = n(); + this.globals = Object.create(null); } - a.initialize = function(b, c, d) { - "undefined" === typeof d && (d = null); - p(!a.instance); - a.instance = new a(b, c, d); - }; - a.currentAbc = function() { - for (var a = arguments.callee, b = null, c = 0;20 > c && a;c++) { - var d = a.methodInfo; - if (d) { - b = d.abc; + d.initialize = function(b, a) { + t(!d.instance); + d.instance = new d(b, a); + }; + d.currentAbc = function() { + for (var b = arguments.callee, a = null, d = 0;20 > d && b;d++) { + var e = b.methodInfo; + if (e) { + a = e.abc; break; } - a = a.caller; + b = b.caller; } - return b; + return a; }; - a.currentDomain = function() { - var b = a.currentAbc(); + d.currentDomain = function() { + var b = d.currentAbc(); if (null === b) { - return a.instance.systemDomain; + return d.instance.systemDomain; } - p(b && b.applicationDomain, "No domain environment was found on the stack, increase STACK_DEPTH or make sure that a compiled / interpreted function is on the call stack."); + t(b && b.applicationDomain, "No domain environment was found on the stack, increase STACK_DEPTH or make sure that a compiled / interpreted function is on the call stack."); return b.applicationDomain; }; - a.isPlayerglobalLoaded = function() { - return!!g.playerglobal; - }; - a.prototype.loadAVM1 = function() { - var a = this._loadAVM1; - p(a); - this._loadAVM1Promise || (this._loadAVM1Promise = new Promise(function(b) { - a(b); - })); - return this._loadAVM1Promise; + d.isPlayerglobalLoaded = function() { + return!!a.playerglobal; }; - a.loadPlayerglobal = function(a, b) { - if (g.playerglobalLoadedPromise) { + d.loadPlayerglobal = function(b, d) { + if (a.playerglobalLoadedPromise) { return Promise.reject("Playerglobal is already loaded"); } - g.playerglobalLoadedPromise = Promise.all([m(a, "arraybuffer"), m(b, "json")]).then(function(a) { - g.playerglobal = {abcs:a[0], map:Object.create(null), scripts:Object.create(null)}; - a = a[1]; - for (var b = 0;b < a.length;b++) { - var c = a[b]; - g.playerglobal.scripts[c.name] = c; - if ("string" === typeof c.defs) { - g.playerglobal.map[c.defs] = c.name; + a.playerglobalLoadedPromise = Promise.all([u(b, "arraybuffer"), u(d, "json")]).then(function(b) { + a.playerglobal = {abcs:b[0], map:Object.create(null), scripts:Object.create(null)}; + b = b[1]; + for (var d = 0;d < b.length;d++) { + var g = b[d]; + a.playerglobal.scripts[g.name] = g; + if ("string" === typeof g.defs) { + a.playerglobal.map[g.defs] = g.name; } else { - for (var h = 0;h < c.defs.length;h++) { - g.playerglobal.map[c.defs[h]] = c.name; + for (var e = 0;e < g.defs.length;e++) { + a.playerglobal.map[g.defs[e]] = g.name; } } } - }, function(a) { - console.error(a); + }, function(b) { + console.error(b); }); - return g.playerglobalLoadedPromise; - }; - a.prototype.notifyConstruct = function(a, b) { + return a.playerglobalLoadedPromise; }; - a.getStackTrace = function() { - b.Debug.somewhatImplemented("getStackTrace"); - return b.Debug.backtrace(); - }; - return a; + return d; }(); - g.AVM2 = q; - var l = function() { - function d(a, h, e, l) { - p(a instanceof q); - p(b.isNullOrUndefined(h) || h instanceof d); - this.vm = a; + a.AVM2 = n; + var k = function() { + function d(b, a, e, f) { + t(b instanceof n); + t(c.isNullOrUndefined(a) || a instanceof d); + this.vm = b; this.abcs = []; this.loadedAbcs = {}; this.loadedClasses = []; - this.classCache = n(); - this.scriptCache = n(); - this.classInfoCache = n(); - this.base = h; - this.allowNatives = l; + this.classCache = Object.create(null); + this.scriptCache = Object.create(null); + this.classInfoCache = Object.create(null); + this.base = a; + this.allowNatives = f; this.mode = e; - this.onMessage = new c; - this.system = h ? h.system : this; + this.onMessage = new m; + this.system = a ? a.system : this; } - d.passthroughCallable = function(a) { - return{call:function(b) { + d.passthroughCallable = function(b) { + return{call:function(a) { Array.prototype.shift.call(arguments); - return a.asApply(b, arguments); - }, apply:function(b, c) { - return a.asApply(b, c); + return b.asApply(a, arguments); + }, apply:function(a, d) { + return b.asApply(a, d); }}; }; - d.coerceCallable = function(a) { - return{call:function(b, c) { - return g.asCoerce(a, c); - }, apply:function(b, c) { - return g.asCoerce(a, c[0]); + d.coerceCallable = function(b) { + return{call:function(d, e) { + return a.asCoerce(b, e); + }, apply:function(d, e) { + return a.asCoerce(b, e[0]); }}; }; - d.prototype.getType = function(a) { - return this.getProperty(a, !0, !0); + d.prototype.getType = function(b) { + return this.getProperty(b, !0, !0); }; - d.prototype.getProperty = function(c, h, d) { - if (d = this.findDefiningScript(c, d)) { - return d.script.executing ? d.script.global[a.getQualifiedName(d.trait.name)] : void 0; - } - if (h) { - return b.Debug.unexpected("Cannot find property " + c); - } - }; - d.prototype.getClass = function(c, h) { - "undefined" === typeof h && (h = !0); - var d = this.classCache, e = d[c]; - e || (e = d[c] = this.getProperty(a.fromSimpleName(c), h, !0)); - e && p(e instanceof b.AVM2.AS.ASClass); - return e; + d.prototype.getProperty = function(b, a, d) { + if (d = this.findDefiningScript(b, d)) { + return d.script.executing ? d.script.global[e.getQualifiedName(d.trait.name)] : void 0; + } + if (a) { + return c.Debug.unexpected("Cannot find property " + b); + } + }; + d.prototype.getClass = function(b, a) { + void 0 === a && (a = !0); + var d = this.classCache, f = d[b]; + f || (f = d[b] = this.getProperty(e.fromSimpleName(b), a, !0)); + f && t(f instanceof c.AVM2.AS.ASClass); + return f; }; - d.prototype.findDomainProperty = function(a, c, d) { - b.AVM2.Runtime.traceDomain.value && log("ApplicationDomain.findDomainProperty: " + a); - if (d = this.findDefiningScript(a, d)) { + d.prototype.findDomainProperty = function(b, a, d) { + c.AVM2.Runtime.traceDomain.value && console.log("ApplicationDomain.findDomainProperty: " + b); + if (d = this.findDefiningScript(b, d)) { return d.script.global; } - if (c) { - return b.Debug.unexpected("Cannot find property " + a); + if (a) { + return c.Debug.unexpected("Cannot find property " + b); } }; d.prototype.findClassInfo = function(b) { - var c; - if (a.isQName(b)) { - c = a.getQualifiedName(b); - var d = this.classInfoCache[c]; + var a; + if (e.isQName(b)) { + a = e.getQualifiedName(b); + var d = this.classInfoCache[a]; } else { d = this.classInfoCache[b.runtimeId]; } - if (d || this.base && (d = this.base.findClassInfo(b))) { - return d; - } - for (var d = this.abcs, e = 0;e < d.length;e++) { - for (var l = d[e], l = l.scripts, n = 0;n < l.length;n++) { - for (var f = l[n].traits, p = 0;p < f.length;p++) { - var g = f[p]; - if (g.isClass()) { - var q = a.getQualifiedName(g.name); - if (c) { - if (q === c) { - return this.classInfoCache[c] = g.classInfo; + return d || this.base && (d = this.base.findClassInfo(b)) ? d : this.findClassInfoSlow(b, a); + }; + d.prototype.findClassInfoSlow = function(b, a) { + for (var d = this.abcs, f = 0;f < d.length;f++) { + for (var k = d[f], k = k.scripts, c = 0;c < k.length;c++) { + for (var m = k[c].traits, l = 0;l < m.length;l++) { + var n = m[l]; + if (n.isClass()) { + var q = e.getQualifiedName(n.name); + if (a) { + if (q === a) { + return this.classInfoCache[a] = n.classInfo; } } else { - for (var m = 0, k = b.namespaces.length;m < k;m++) { - var u = b.getQName(m); - if (q === a.getQualifiedName(u)) { - return this.classInfoCache[u] = g.classInfo; + for (var t = 0, h = b.namespaces.length;t < h;t++) { + var s = b.getQName(t); + if (q === e.getQualifiedName(s)) { + return this.classInfoCache[s] = n.classInfo; } } } @@ -20407,664 +21411,651 @@ } } } - if (!this.base && this.vm.findDefiningAbc && (l = this.vm.findDefiningAbc(b), null !== l && !this.loadedAbcs[l.name])) { - return this.loadedAbcs[l.name] = !0, this.loadAbc(l), this.findClassInfo(b); + if (!this.base && this.vm.findDefiningAbc && (k = this.vm.findDefiningAbc(b), null !== k && !this.loadedAbcs[k.name])) { + return this.loadedAbcs[k.name] = !0, this.loadAbc(k), this.findClassInfo(b); } }; - d.prototype.findDefiningScript = function(c, h) { - var d = this.scriptCache[c.runtimeId]; - if (d && (d.script.executed || !h) || this.base && (d = this.base.findDefiningScript(c, h))) { - return d; - } - k.countTimeline("ApplicationDomain: findDefiningScript"); - for (var d = this.abcs, e = 0;e < d.length;e++) { - for (var l = d[e], l = l.scripts, n = 0;n < l.length;n++) { - var f = l[n], p = f.traits; - if (c instanceof a) { - for (var g = 0;g < p.length;g++) { - var q = p[g]; - if (c.hasQName(q.name)) { - return h && t(f, String(q.name)), this.scriptCache[c.runtimeId] = {script:f, trait:q}; + d.prototype.findDefiningScript = function(b, a) { + var d = this.scriptCache[b.runtimeId]; + return d && (d.script.executed || !a) || this.base && (d = this.base.findDefiningScript(b, a)) ? d : this.findDefiningScriptSlow(b, a); + }; + d.prototype.findDefiningScriptSlow = function(b, a) { + h.countTimeline("ApplicationDomain: findDefiningScriptSlow"); + for (var d = this.abcs, f = 0;f < d.length;f++) { + for (var k = d[f], k = k.scripts, m = 0;m < k.length;m++) { + var l = k[m], n = l.traits; + if (b instanceof e) { + for (var q = 0;q < n.length;q++) { + var t = n[q]; + if (b.hasQName(t.name)) { + return a && v(l, String(t.name)), this.scriptCache[b.runtimeId] = {script:l, trait:t}; } } } else { - b.Debug.unexpected(); + c.Debug.unexpected(); } } } - if (!this.base && this.vm.findDefiningAbc && (l = this.vm.findDefiningAbc(c), null !== l && !this.loadedAbcs[l.name])) { - return this.loadedAbcs[l.name] = !0, this.loadAbc(l), this.findDefiningScript(c, h); + if (!this.base && this.vm.findDefiningAbc && (k = this.vm.findDefiningAbc(b), null !== k && !this.loadedAbcs[k.name])) { + return this.loadedAbcs[k.name] = !0, this.loadAbc(k), this.findDefiningScript(b, a); } }; - d.prototype.compileAbc = function(a, c) { - b.AVM2.Compiler.compileAbc(a, c); + d.prototype.compileAbc = function(b, a) { + c.AVM2.Compiler.compileAbc(b, a); }; - d.prototype.executeAbc = function(a) { - this.loadAbc(a); - f(a.lastScript); + d.prototype.executeAbc = function(b) { + this.loadAbc(b); + s(b.lastScript); }; - d.prototype.loadAbc = function(a) { - b.AVM2.Runtime.traceExecution.value && log("Loading: " + a.name); - a.applicationDomain = this; - g.GlobalMultinameResolver.loadAbc(a); - this.abcs.push(a); - this.base || (k.AS.initialize(this), b.AVM2.Verifier.Type.initializeTypes(this)); + d.prototype.loadAbc = function(b) { + c.AVM2.Runtime.traceExecution.value && console.log("Loading: " + b.name); + b.applicationDomain = this; + a.GlobalMultinameResolver.loadAbc(b); + this.abcs.push(b); + this.base || (h.AS.initialize(this), c.AVM2.Verifier.Type.initializeTypes(this)); }; - d.prototype.broadcastMessage = function(a, b, c) { + d.prototype.broadcastMessage = function(b, a, d) { try { - this.onMessage.notify1(a, {data:b, origin:c, source:this}); - } catch (d) { - throw q.instance.exceptions.push({source:a, message:d.message, stack:d.stack}), d; + this.onMessage.notify1(b, {data:a, origin:d, source:this}); + } catch (e) { + throw n.instance.exceptions.push({source:b, message:e.message, stack:e.stack}), e; } }; d.prototype.traceLoadedClasses = function() { - var a = new e; - [b.ArrayUtilities.last(this.loadedClasses)].forEach(function(c) { - c !== b.AVM2.AS.ASClass && c.trace(a); + var b = new q; + [c.ArrayUtilities.last(this.loadedClasses)].forEach(function(a) { + a !== c.AVM2.AS.ASClass && a.trace(b); }); }; return d; }(); - g.ApplicationDomain = l; - var u = function() { + a.ApplicationDomain = k; + var f = function() { function a(b) { this.compartment = newGlobal("new-compartment"); this.compartment.homePath = homePath; this.compartment.release = !1; this.compartment.eval(snarf(b)); } - a.prototype.initializeShell = function(a, b) { - var c = this.compartment; - c.AVM2.initialize(a, b); - c.AVM2.instance.systemDomain.executeAbc(c.grabAbc(homePath + "src/avm2/generated/builtin/builtin.abc")); - c.AVM2.instance.systemDomain.executeAbc(c.grabAbc(homePath + "src/avm2/generated/shell/shell.abc")); - this.systemDomain = c.AVM2.instance.systemDomain; - this.applicationDomain = c.AVM2.instance.applicationDomain; + a.prototype.initializeShell = function(b, a) { + var d = this.compartment; + d.AVM2.initialize(b, a); + d.AVM2.instance.systemDomain.executeAbc(d.grabAbc(homePath + "src/avm2/generated/builtin/builtin.abc")); + d.AVM2.instance.systemDomain.executeAbc(d.grabAbc(homePath + "src/avm2/generated/shell/shell.abc")); + this.systemDomain = d.AVM2.instance.systemDomain; + this.applicationDomain = d.AVM2.instance.applicationDomain; }; return a; }(); - g.SecurityDomain = u; - })(k.Runtime || (k.Runtime = {})); - })(b.AVM2 || (b.AVM2 = {})); + a.SecurityDomain = f; + })(h.Runtime || (h.Runtime = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); var Glue = Shumway.AVM2.Runtime.Glue, ApplicationDomain = Shumway.AVM2.Runtime.ApplicationDomain, AVM2 = Shumway.AVM2.Runtime.AVM2, EXECUTION_MODE = Shumway.AVM2.Runtime.ExecutionMode; -(function(b) { - (function(k) { - (function(g) { - function f(a) { - return a.holder instanceof m ? "static " + a.holder.instanceInfo.name.getOriginalName() + "::" + a.name.getOriginalName() : a.holder instanceof d ? a.holder.name.getOriginalName() + "::" + a.name.getOriginalName() : a.name.getOriginalName(); - } - function t(b) { - a(b && "function" === typeof b); - return b.isTrampoline; +(function(c) { + (function(h) { + (function(a) { + function s(a) { + return a.holder instanceof p ? "static " + a.holder.instanceInfo.name.getOriginalName() + "::" + a.name.getOriginalName() : a.holder instanceof u ? a.holder.name.getOriginalName() + "::" + a.name.getOriginalName() : a.name.getOriginalName(); } - var s = b.AVM2.ABC.Multiname, m = b.AVM2.ABC.ClassInfo, d = b.AVM2.ABC.InstanceInfo, a = b.Debug.assert, c = b.ObjectUtilities.defineReadOnlyProperty, n = b.FunctionUtilities.bindSafely, p = 1, e = 1; - g.getMethodOverrideKey = f; - g.checkMethodOverrides = function(a) { - if (a.name && (a = f(a), a in g.VM_METHOD_OVERRIDES)) { - return b.Debug.warning("Overriding Method: " + a), g.VM_METHOD_OVERRIDES[a]; - } - }; - g.makeTrampoline = function(d, e, n, f, r, h) { - return function() { - function m() { - k.countTimeline("Executing Trampoline"); - 1 <= b.AVM2.Runtime.traceExecution.value && (callWriter.writeLn("Trampoline: " + h), 3 <= b.AVM2.Runtime.traceExecution.value && log("Trampolining")); - s || (s = g.getTraitFunction(d, e, n), g.patch(f, s), d = e = n = f = null, a(s)); - return s.asApply(this, arguments); - } - var s = null; - m.trigger = function() { - k.countTimeline("Triggering Trampoline"); - s || (s = g.getTraitFunction(d, e, n), g.patch(f, s), d = e = n = f = null, a(s)); - }; - m.isTrampoline = !0; - m.debugName = "Trampoline #" + p++; - c(m, g.VM_LENGTH, r); - return m; - }(); + var v = c.AVM2.ABC.Multiname, p = c.AVM2.ABC.ClassInfo, u = c.AVM2.ABC.InstanceInfo, l = c.Debug.assert, e = c.ObjectUtilities.defineReadOnlyProperty, m = c.FunctionUtilities.bindSafely; + a.getMethodOverrideKey = s; + a.checkMethodOverrides = function(e) { + if (e.name && (e = s(e), e in a.VM_METHOD_OVERRIDES)) { + return c.Debug.warning("Overriding Method: " + e), a.VM_METHOD_OVERRIDES[e]; + } + }; + var t = [[208, 48, 71], [208, 48, 208, 73, 0, 71]]; + a.checkCommonMethodPatterns = function(a) { + return a.code && (c.ArrayUtilities.equals(a.code, t[0]) || c.ArrayUtilities.equals(a.code, t[1]) && a.holder instanceof u && v.getQualifiedName(a.holder.superName) === v.Object) ? function() { + } : null; + }; + a.makeTrampoline = function(m, n, k, f, d, b) { + function g() { + h.countTimeline("Executing Trampoline"); + 1 <= c.AVM2.Runtime.traceExecution.value && (a.callWriter.writeLn("Trampoline: " + b), 3 <= c.AVM2.Runtime.traceExecution.value && console.log("Trampolining")); + r || g.trigger(); + return r.asApply(this, arguments); + } + var r = null; + g.trigger = function() { + h.countTimeline("Triggering Trampoline"); + r || (l(!m.methodInfo.isNative()), r = a.createFunction(m.methodInfo, n, !1, !1, !1), a.patch(f, r), m = n = f = null, l(r)); + }; + g.isTrampoline = !0; + g.patchTargets = f; + e(g, a.VM_LENGTH, d); + return g; }; - g.makeMemoizer = function(d, l) { - function f() { - k.countTimeline("Runtime: Memoizing"); - 3 <= b.AVM2.Runtime.traceExecution.value && log("Memoizing: " + d); - 1 < b.AVM2.Runtime.traceCallExecution.value && callWriter.writeLn("Memoizing: " + d); - if (g.isNativePrototype(this)) { - return k.countTimeline("Runtime: Method Closures"), n(l.value, this); - } - t(l.value) && l.value.trigger(); - a(!t(l.value), "We should avoid binding trampolines."); - var e = null; - if (this instanceof b.AVM2.AS.ASClass) { - return k.countTimeline("Runtime: Static Method Closures"), e = n(l.value, this), c(this, d, e), e; - } - if (Object.prototype.hasOwnProperty.call(this, d)) { - if (Object.getOwnPropertyDescriptor(this, d).get) { - return k.countTimeline("Runtime: Method Closures"), n(l.value, this); - } - k.countTimeline("Runtime: Unpatched Memoizer"); - return this[d]; - } - e = n(l.value, this); - e.methodInfo = l.value.methodInfo; - c(e, s.getPublicQualifiedName("prototype"), null); - c(this, d, e); - return e; + a.makeMemoizer = function(q, n) { + function k() { + n.value.isTrampoline && (n.value.trigger(), l(!n.value.isTrampoline, "We should avoid binding trampolines.")); + var f = m(n.value, this); + c.ObjectUtilities.defineReadOnlyProperty(f, "asLength", n.value.length); + if (a.isNativePrototype(this)) { + return h.countTimeline("Runtime: Method Closures"), f; + } + f.methodInfo = n.value.methodInfo; + e(f, v.getPublicQualifiedName("prototype"), null); + e(this, q, f); + return f; } - k.countTimeline("Runtime: Memoizers"); - f.isMemoizer = !0; - f.debugName = "Memoizer #" + e++; - return f; - }; - g.isMemoizer = function(b) { - a(b && "function" === typeof b); - return b.isMemoizer; + h.countTimeline("Runtime: Memoizers"); + k.isMemoizer = !0; + return k; }; - })(k.Runtime || (k.Runtime = {})); - })(b.AVM2 || (b.AVM2 = {})); + })(h.Runtime || (h.Runtime = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - function g(a, b, c) { - c.flags = b.flags; - c.name = b.isRuntimeName() ? a.pop() : b.name; - c.namespaces = b.isRuntimeNamespace() ? [a.pop()] : b.namespaces; +(function(c) { + (function(h) { + function a(b, a, d) { + d.flags = a.flags; + d.name = a.isRuntimeName() ? b.pop() : a.name; + d.namespaces = a.isRuntimeNamespace() ? [b.pop()] : a.namespaces; } - var f = b.AVM2.Runtime.Scope, t = b.AVM2.Runtime.asCoerceByMultiname, s = b.AVM2.Runtime.asGetSlot, m = b.AVM2.Runtime.asSetSlot, d = b.AVM2.Runtime.asCoerce, a = b.AVM2.Runtime.asCoerceString, c = b.AVM2.Runtime.asAsType, n = b.AVM2.Runtime.asTypeOf, p = b.AVM2.Runtime.asIsInstanceOf, e = b.AVM2.Runtime.asIsType, q = b.AVM2.Runtime.applyType, l = b.AVM2.Runtime.createFunction, u = b.AVM2.Runtime.createClass, w = b.AVM2.Runtime.getDescendants, r = b.AVM2.Runtime.checkFilter, h = b.AVM2.Runtime.asAdd, - x = b.AVM2.Runtime.translateError, y = b.AVM2.Runtime.asCreateActivation, G = b.AVM2.Runtime.sliceArguments, I = b.ObjectUtilities.boxValue, C = b.ArrayUtilities.popManyInto, E = b.AVM2.Runtime.construct, O = b.AVM2.ABC.Multiname, K = b.Debug.assert, F = b.AVM2.Runtime.HasNext2Info, J = function() { - function a(b) { - this.parent = b; + var s = c.AVM2.Runtime.Scope, v = c.AVM2.Runtime.asCoerceByMultiname, p = c.AVM2.Runtime.asGetSlot, u = c.AVM2.Runtime.asSetSlot, l = c.AVM2.Runtime.asCoerce, e = c.AVM2.Runtime.asCoerceString, m = c.AVM2.Runtime.asAsType, t = c.AVM2.Runtime.asTypeOf, q = c.AVM2.Runtime.asIsInstanceOf, n = c.AVM2.Runtime.asIsType, k = c.AVM2.Runtime.applyType, f = c.AVM2.Runtime.createFunction, d = c.AVM2.Runtime.createClass, b = c.AVM2.Runtime.getDescendants, g = c.AVM2.Runtime.checkFilter, r = c.AVM2.Runtime.asAdd, + w = c.AVM2.Runtime.translateError, z = c.AVM2.Runtime.asCreateActivation, A = c.AVM2.Runtime.sliceArguments, B = c.ObjectUtilities.boxValue, M = c.ArrayUtilities.popManyInto, N = c.AVM2.Runtime.construct, K = c.AVM2.ABC.Multiname, y = c.Debug.assert, D = c.AVM2.Runtime.HasNext2Info, L = function() { + function b(a) { + this.parent = a; this.stack = []; this.isWith = []; } - a.prototype.push = function(a, b) { - this.stack.push(a); - this.isWith.push(!!b); + b.prototype.push = function(b, a) { + this.stack.push(b); + this.isWith.push(!!a); }; - a.prototype.get = function(a) { - return this.stack[a]; + b.prototype.get = function(b) { + return this.stack[b]; }; - a.prototype.clear = function() { + b.prototype.clear = function() { this.stack.length = 0; this.isWith.length = 0; }; - a.prototype.pop = function() { + b.prototype.pop = function() { this.isWith.pop(); this.stack.pop(); }; - a.prototype.topScope = function() { + b.prototype.topScope = function() { this.scopes || (this.scopes = []); - for (var a = this.parent, b = 0;b < this.stack.length;b++) { - var c = this.stack[b], h = this.isWith[b], d = this.scopes[b]; - d && d.parent === a && d.object === c && d.isWith === h || (d = this.scopes[b] = new f(a, c, h)); - a = d; + for (var b = this.parent, a = 0;a < this.stack.length;a++) { + var d = this.stack[a], g = this.isWith[a], e = this.scopes[a]; + e && e.parent === b && e.object === d && e.isWith === g || (e = this.scopes[a] = new s(b, d, g)); + b = e; } - return a; + return b; }; - return a; - }(), A = function() { - function f() { + return b; + }(), H = function() { + function s() { } - f.interpretMethod = function(f, B, A, L) { - K(B.analysis); - k.countTimeline("Interpret Method"); - var M = B.abc, V = M.constantPool.ints, Q = M.constantPool.uints, U = M.constantPool.doubles, S = M.constantPool.strings, aa = M.methods, $ = M.constantPool.multinames, ea = M.applicationDomain, Z = B.exceptions; - f = [f]; - for (var v = [], X = new J(A), ba = B.parameters.length, R = L.length, H, Y = 0;Y < ba;Y++) { - var ga = B.parameters[Y]; - H = Y < R ? L[Y] : ga.value; - ga.type && !ga.type.isAnyName() && (H = t(B, ga.type, H)); - f.push(H); - } - B.needsRest() ? f.push(G(L, ba)) : B.needsArguments() && f.push(G(L, 0)); - L = B.analysis.bytecodes; - var ca, ka, W, ha, da, ja, ba = [], R = O.TEMPORARY, ga = [], N = 0, ma = L.length; - a: for (;N < ma;) { + s.interpretMethod = function(s, H, J, I) { + y(H.analysis); + h.countTimeline("Interpret Method"); + var G = H.abc, Z = G.constantPool.ints, Q = G.constantPool.uints, S = G.constantPool.doubles, O = G.constantPool.strings, P = G.methods, V = G.constantPool.multinames, $ = G.applicationDomain, W = H.exceptions; + s = [s]; + for (var x = [], ea = new L(J), Y = H.parameters.length, R = I.length, U, ba = 0;ba < Y;ba++) { + var X = H.parameters[ba]; + U = ba < R ? I[ba] : X.value; + X.type && !X.type.isAnyName() && (U = v(H, X.type, U)); + s.push(U); + } + H.needsRest() ? s.push(A(I, Y)) : H.needsArguments() && s.push(A(I, 0)); + I = H.analysis.bytecodes; + var ga, ja, aa, ia, T, da, Y = [], R = K.TEMPORARY, X = [], fa = 0, la = I.length; + a: for (;fa < la;) { try { - var T = L[N], oa = T.op; - switch(oa | 0) { + var ca = I[fa], na = ca.op; + switch(na | 0) { case 3: - throw v.pop();; + throw x.pop();; case 4: - g(v, $[T.index], R); - v.push(v.pop().asGetSuper(A, R.namespaces, R.name, R.flags)); + a(x, V[ca.index], R); + x.push(x.pop().asGetSuper(J, R.namespaces, R.name, R.flags)); break; case 5: - H = v.pop(); - g(v, $[T.index], R); - v.pop().asSetSuper(A, R.namespaces, R.name, R.flags, H); + U = x.pop(); + a(x, V[ca.index], R); + x.pop().asSetSuper(J, R.namespaces, R.name, R.flags, U); break; case 8: - f[T.index] = void 0; + s[ca.index] = void 0; break; case 12: - ja = v.pop(); - da = v.pop(); - N = da < ja ? N + 1 : T.offset; + da = x.pop(); + T = x.pop(); + fa = T < da ? fa + 1 : ca.offset; continue; case 24: - ja = v.pop(); - da = v.pop(); - N = da >= ja ? T.offset : N + 1; + da = x.pop(); + T = x.pop(); + fa = T >= da ? ca.offset : fa + 1; continue; case 13: - ja = v.pop(); - da = v.pop(); - N = da <= ja ? N + 1 : T.offset; + da = x.pop(); + T = x.pop(); + fa = T <= da ? fa + 1 : ca.offset; continue; case 23: - ja = v.pop(); - da = v.pop(); - N = da > ja ? T.offset : N + 1; + da = x.pop(); + T = x.pop(); + fa = T > da ? ca.offset : fa + 1; continue; case 14: - ja = v.pop(); - da = v.pop(); - N = da > ja ? N + 1 : T.offset; + da = x.pop(); + T = x.pop(); + fa = T > da ? fa + 1 : ca.offset; continue; case 22: - ja = v.pop(); - da = v.pop(); - N = da <= ja ? T.offset : N + 1; + da = x.pop(); + T = x.pop(); + fa = T <= da ? ca.offset : fa + 1; continue; case 15: - ja = v.pop(); - da = v.pop(); - N = da >= ja ? N + 1 : T.offset; + da = x.pop(); + T = x.pop(); + fa = T >= da ? fa + 1 : ca.offset; continue; case 21: - ja = v.pop(); - da = v.pop(); - N = da < ja ? T.offset : N + 1; + da = x.pop(); + T = x.pop(); + fa = T < da ? ca.offset : fa + 1; continue; case 16: - N = T.offset; + fa = ca.offset; continue; case 17: - N = v.pop() ? T.offset : N + 1; + fa = x.pop() ? ca.offset : fa + 1; continue; case 18: - N = v.pop() ? N + 1 : T.offset; + fa = x.pop() ? fa + 1 : ca.offset; continue; case 19: - ja = v.pop(); - da = v.pop(); - N = da == ja ? T.offset : N + 1; + da = x.pop(); + T = x.pop(); + fa = asEquals(T, da) ? ca.offset : fa + 1; continue; case 20: - ja = v.pop(); - da = v.pop(); - N = da != ja ? T.offset : N + 1; + da = x.pop(); + T = x.pop(); + fa = asEquals(T, da) ? fa + 1 : ca.offset; continue; case 25: - ja = v.pop(); - da = v.pop(); - N = da === ja ? T.offset : N + 1; + da = x.pop(); + T = x.pop(); + fa = T === da ? ca.offset : fa + 1; continue; case 26: - ja = v.pop(); - da = v.pop(); - N = da !== ja ? T.offset : N + 1; + da = x.pop(); + T = x.pop(); + fa = T !== da ? ca.offset : fa + 1; continue; case 27: - ka = v.pop(); - if (0 > ka || ka >= T.offsets.length) { - ka = T.offsets.length - 1; + ja = x.pop(); + if (0 > ja || ja >= ca.offsets.length) { + ja = ca.offsets.length - 1; } - N = T.offsets[ka]; + fa = ca.offsets[ja]; continue; case 28: - X.push(I(v.pop()), !0); + ea.push(B(x.pop()), !0); break; case 29: - X.pop(); + ea.pop(); break; case 30: - ka = v.pop(); - v[v.length - 1] = I(v[v.length - 1]).asNextName(ka); + ja = x.pop(); + x[x.length - 1] = B(x[x.length - 1]).asNextName(ja); break; case 35: - ka = v.pop(); - v[v.length - 1] = I(v[v.length - 1]).asNextValue(ka); + ja = x.pop(); + x[x.length - 1] = B(x[x.length - 1]).asNextValue(ja); break; case 50: - var pa = ga[N] || (ga[N] = new F(null, 0)); - ca = f[T.object]; - ka = f[T.index]; - pa.object = ca; - pa.index = ka; - Object(ca).asHasNext2(pa); - f[T.object] = pa.object; - f[T.index] = pa.index; - v.push(!!pa.index); + var ma = X[fa] || (X[fa] = new D(null, 0)); + ga = s[ca.object]; + ja = s[ca.index]; + ma.object = ga; + ma.index = ja; + Object(ga).asHasNext2(ma); + s[ca.object] = ma.object; + s[ca.index] = ma.index; + x.push(!!ma.index); break; case 32: - v.push(null); + x.push(null); break; case 33: - v.push(void 0); + x.push(void 0); break; case 36: ; case 37: - v.push(T.value); + x.push(ca.value); break; case 44: - v.push(S[T.index]); + x.push(O[ca.index]); break; case 45: - v.push(V[T.index]); + x.push(Z[ca.index]); break; case 46: - v.push(Q[T.index]); + x.push(Q[ca.index]); break; case 47: - v.push(U[T.index]); + x.push(S[ca.index]); break; case 38: - v.push(!0); + x.push(!0); break; case 39: - v.push(!1); + x.push(!1); break; case 40: - v.push(NaN); + x.push(NaN); break; case 41: - v.pop(); + x.pop(); break; case 42: - v.push(v[v.length - 1]); + x.push(x[x.length - 1]); break; case 43: - ca = v[v.length - 1]; - v[v.length - 1] = v[v.length - 2]; - v[v.length - 2] = ca; + ga = x[x.length - 1]; + x[x.length - 1] = x[x.length - 2]; + x[x.length - 2] = ga; break; case 48: - X.push(I(v.pop()), !1); + ea.push(B(x.pop()), !1); break; case 64: - v.push(l(aa[T.index], X.topScope(), !0, !1)); + x.push(f(P[ca.index], ea.topScope(), !0, !1)); break; case 65: - C(v, T.argCount, ba); - ca = v.pop(); - v[v.length - 1] = v[v.length - 1].asApply(ca, ba); + M(x, ca.argCount, Y); + ga = x.pop(); + x[x.length - 1] = x[x.length - 1].asApply(ga, Y); break; case 66: - C(v, T.argCount, ba); - v[v.length - 1] = E(v[v.length - 1], ba); + M(x, ca.argCount, Y); + x[x.length - 1] = N(x[x.length - 1], Y); break; case 71: return; case 72: - return B.returnType ? t(B, B.returnType, v.pop()) : v.pop(); + return H.returnType ? v(H, H.returnType, x.pop()) : x.pop(); case 73: - C(v, T.argCount, ba); - ca = v.pop(); - A.object.baseClass.instanceConstructorNoInitialize.apply(ca, ba); + M(x, ca.argCount, Y); + ga = x.pop(); + J.object.baseClass.instanceConstructorNoInitialize.apply(ga, Y); break; case 74: - C(v, T.argCount, ba); - g(v, $[T.index], R); - ca = I(v[v.length - 1]); - ca = ca.asConstructProperty(R.namespaces, R.name, R.flags, ba); - v[v.length - 1] = ca; + M(x, ca.argCount, Y); + a(x, V[ca.index], R); + ga = B(x[x.length - 1]); + ga = ga.asConstructProperty(R.namespaces, R.name, R.flags, Y); + x[x.length - 1] = ga; break; case 75: - b.Debug.notImplemented("OP.callsuperid"); + c.Debug.notImplemented("OP.callsuperid"); break; case 76: ; case 70: ; case 79: - C(v, T.argCount, ba); - g(v, $[T.index], R); - ha = I(v.pop()).asCallProperty(R.namespaces, R.name, R.flags, 76 === oa, ba); - 79 !== oa && v.push(ha); + M(x, ca.argCount, Y); + a(x, V[ca.index], R); + ia = B(x.pop()).asCallProperty(R.namespaces, R.name, R.flags, 76 === na, Y); + 79 !== na && x.push(ia); break; case 69: ; case 78: - C(v, T.argCount, ba); - g(v, $[T.index], R); - ha = v.pop().asCallSuper(A, R.namespaces, R.name, R.flags, ba); - 78 !== oa && v.push(ha); + M(x, ca.argCount, Y); + a(x, V[ca.index], R); + ia = x.pop().asCallSuper(J, R.namespaces, R.name, R.flags, Y); + 78 !== na && x.push(ia); break; case 83: - C(v, T.argCount, ba); - v[v.length - 1] = q(B, v[v.length - 1], ba); + M(x, ca.argCount, Y); + x[x.length - 1] = k(H, x[x.length - 1], Y); break; case 85: - ca = {}; - for (Y = 0;Y < T.argCount;Y++) { - H = v.pop(), ca[O.getPublicQualifiedName(v.pop())] = H; + ga = {}; + for (ba = 0;ba < ca.argCount;ba++) { + U = x.pop(), ga[K.getPublicQualifiedName(x.pop())] = U; } - v.push(ca); + x.push(ga); break; case 86: - ca = []; - C(v, T.argCount, ba); - ca.push.apply(ca, ba); - v.push(ca); + ga = []; + M(x, ca.argCount, Y); + ga.push.apply(ga, Y); + x.push(ga); break; case 87: - K(B.needsActivation()); - v.push(y(B)); + y(H.needsActivation()); + x.push(z(H)); break; case 88: - v[v.length - 1] = u(M.classes[T.index], v[v.length - 1], X.topScope()); + x[x.length - 1] = d(G.classes[ca.index], x[x.length - 1], ea.topScope()); break; case 89: - g(v, $[T.index], R); - v.push(w(v.pop(), R)); + a(x, V[ca.index], R); + void 0 === R.name && (R.name = "*"); + x.push(b(x.pop(), R)); break; case 90: - K(Z[T.index].scopeObject); - v.push(Z[T.index].scopeObject); + y(W[ca.index].scopeObject); + x.push(W[ca.index].scopeObject); break; case 94: ; case 93: - g(v, $[T.index], R); - v.push(X.topScope().findScopeProperty(R.namespaces, R.name, R.flags, B, 93 === oa, !1)); + a(x, V[ca.index], R); + x.push(ea.topScope().findScopeProperty(R.namespaces, R.name, R.flags, H, 93 === na, !1)); break; case 96: - W = $[T.index]; - ca = X.topScope().findScopeProperty(W.namespaces, W.name, W.flags, B, !0, !1); - v.push(ca.asGetProperty(W.namespaces, W.name, W.flags)); + aa = V[ca.index]; + ga = ea.topScope().findScopeProperty(aa.namespaces, aa.name, aa.flags, H, !0, !1); + x.push(ga.asGetProperty(aa.namespaces, aa.name, aa.flags)); break; case 104: ; case 97: - H = v.pop(); - g(v, $[T.index], R); - I(v.pop()).asSetProperty(R.namespaces, R.name, R.flags, H); + U = x.pop(); + a(x, V[ca.index], R); + B(x.pop()).asSetProperty(R.namespaces, R.name, R.flags, U); break; case 98: - v.push(f[T.index]); + x.push(s[ca.index]); break; case 99: - f[T.index] = v.pop(); + s[ca.index] = x.pop(); break; case 100: - v.push(A.global.object); + x.push(J.global.object); break; case 101: - v.push(X.get(T.index)); + x.push(ea.get(ca.index)); break; case 102: - g(v, $[T.index], R); - v[v.length - 1] = I(v[v.length - 1]).asGetProperty(R.namespaces, R.name, R.flags); + a(x, V[ca.index], R); + x[x.length - 1] = B(x[x.length - 1]).asGetProperty(R.namespaces, R.name, R.flags); break; case 106: - g(v, $[T.index], R); - v[v.length - 1] = I(v[v.length - 1]).asDeleteProperty(R.namespaces, R.name, R.flags); + a(x, V[ca.index], R); + x[x.length - 1] = B(x[x.length - 1]).asDeleteProperty(R.namespaces, R.name, R.flags); break; case 108: - v[v.length - 1] = s(v[v.length - 1], T.index); + x[x.length - 1] = p(x[x.length - 1], ca.index); break; case 109: - H = v.pop(); - ca = v.pop(); - m(ca, T.index, H); + U = x.pop(); + ga = x.pop(); + u(ga, ca.index, U); break; case 112: - v[v.length - 1] += ""; + x[x.length - 1] = e(x[x.length - 1]); + break; + case 114: + x[x.length - 1] = h.Runtime.escapeXMLAttribute(x[x.length - 1]); + break; + case 113: + x[x.length - 1] = h.Runtime.escapeXMLElement(x[x.length - 1]); break; case 131: ; case 115: - v[v.length - 1] |= 0; + x[x.length - 1] |= 0; break; case 136: ; case 116: - v[v.length - 1] >>>= 0; + x[x.length - 1] >>>= 0; break; case 132: ; case 117: - v[v.length - 1] = +v[v.length - 1]; + x[x.length - 1] = +x[x.length - 1]; break; case 129: ; case 118: - v[v.length - 1] = !!v[v.length - 1]; + x[x.length - 1] = !!x[x.length - 1]; break; case 120: - v[v.length - 1] = r(v[v.length - 1]); + x[x.length - 1] = g(x[x.length - 1]); break; case 128: - v[v.length - 1] = d(ea.getType($[T.index]), v[v.length - 1]); + x[x.length - 1] = l($.getType(V[ca.index]), x[x.length - 1]); break; case 130: break; case 133: - v[v.length - 1] = a(v[v.length - 1]); + x[x.length - 1] = e(x[x.length - 1]); break; case 134: - v[v.length - 2] = c(ea.getType($[T.index]), v[v.length - 1]); + x[x.length - 2] = m($.getType(V[ca.index]), x[x.length - 1]); break; case 135: - v[v.length - 2] = c(v.pop(), v[v.length - 1]); + x[x.length - 2] = m(x.pop(), x[x.length - 1]); break; case 137: - ca = v[v.length - 1]; - v[v.length - 1] = void 0 == ca ? null : ca; + ga = x[x.length - 1]; + x[x.length - 1] = void 0 == ga ? null : ga; break; case 144: - v[v.length - 1] = -v[v.length - 1]; + x[x.length - 1] = -x[x.length - 1]; break; case 145: - ++v[v.length - 1]; + ++x[x.length - 1]; break; case 146: - ++f[T.index]; + ++s[ca.index]; break; case 147: - --v[v.length - 1]; + --x[x.length - 1]; break; case 148: - --f[T.index]; + --s[ca.index]; break; case 149: - v[v.length - 1] = n(v[v.length - 1]); + x[x.length - 1] = t(x[x.length - 1]); break; case 150: - v[v.length - 1] = !v[v.length - 1]; + x[x.length - 1] = !x[x.length - 1]; break; case 151: - v[v.length - 1] = ~v[v.length - 1]; + x[x.length - 1] = ~x[x.length - 1]; break; case 160: - v[v.length - 2] = h(v[v.length - 2], v.pop()); + x[x.length - 2] = r(x[x.length - 2], x.pop()); break; case 161: - v[v.length - 2] -= v.pop(); + x[x.length - 2] -= x.pop(); break; case 162: - v[v.length - 2] *= v.pop(); + x[x.length - 2] *= x.pop(); break; case 163: - v[v.length - 2] /= v.pop(); + x[x.length - 2] /= x.pop(); break; case 164: - v[v.length - 2] %= v.pop(); + x[x.length - 2] %= x.pop(); break; case 165: - v[v.length - 2] <<= v.pop(); + x[x.length - 2] <<= x.pop(); break; case 166: - v[v.length - 2] >>= v.pop(); + x[x.length - 2] >>= x.pop(); break; case 167: - v[v.length - 2] >>>= v.pop(); + x[x.length - 2] >>>= x.pop(); break; case 168: - v[v.length - 2] &= v.pop(); + x[x.length - 2] &= x.pop(); break; case 169: - v[v.length - 2] |= v.pop(); + x[x.length - 2] |= x.pop(); break; case 170: - v[v.length - 2] ^= v.pop(); + x[x.length - 2] ^= x.pop(); break; case 171: - v[v.length - 2] = v[v.length - 2] == v.pop(); + x[x.length - 2] = asEquals(x[x.length - 2], x.pop()); break; case 172: - v[v.length - 2] = v[v.length - 2] === v.pop(); + x[x.length - 2] = x[x.length - 2] === x.pop(); break; case 173: - v[v.length - 2] = v[v.length - 2] < v.pop(); + x[x.length - 2] = x[x.length - 2] < x.pop(); break; case 174: - v[v.length - 2] = v[v.length - 2] <= v.pop(); + x[x.length - 2] = x[x.length - 2] <= x.pop(); break; case 175: - v[v.length - 2] = v[v.length - 2] > v.pop(); + x[x.length - 2] = x[x.length - 2] > x.pop(); break; case 176: - v[v.length - 2] = v[v.length - 2] >= v.pop(); + x[x.length - 2] = x[x.length - 2] >= x.pop(); break; case 177: - v[v.length - 2] = p(v.pop(), v[v.length - 1]); + x[x.length - 2] = q(x.pop(), x[x.length - 1]); break; case 178: - v[v.length - 1] = e(ea.getType($[T.index]), v[v.length - 1]); + x[x.length - 1] = n($.getType(V[ca.index]), x[x.length - 1]); break; case 179: - v[v.length - 2] = e(v.pop(), v[v.length - 1]); + x[x.length - 2] = n(x.pop(), x[x.length - 1]); break; case 180: - v[v.length - 2] = I(v.pop()).asHasProperty(null, v[v.length - 1]); + x[x.length - 2] = B(x.pop()).asHasProperty(null, x[x.length - 1]); break; case 192: - v[v.length - 1] = (v[v.length - 1] | 0) + 1; + x[x.length - 1] = (x[x.length - 1] | 0) + 1; break; case 193: - v[v.length - 1] = (v[v.length - 1] | 0) - 1; + x[x.length - 1] = (x[x.length - 1] | 0) - 1; break; case 194: - f[T.index] = (f[T.index] | 0) + 1; + s[ca.index] = (s[ca.index] | 0) + 1; break; case 195: - f[T.index] = (f[T.index] | 0) - 1; + s[ca.index] = (s[ca.index] | 0) - 1; break; case 196: - v[v.length - 1] = ~v[v.length - 1]; + x[x.length - 1] = ~x[x.length - 1]; break; case 197: - v[v.length - 2] = v[v.length - 2] + v.pop() | 0; + x[x.length - 2] = x[x.length - 2] + x.pop() | 0; break; case 198: - v[v.length - 2] = v[v.length - 2] - v.pop() | 0; + x[x.length - 2] = x[x.length - 2] - x.pop() | 0; break; case 199: - v[v.length - 2] = v[v.length - 2] * v.pop() | 0; + x[x.length - 2] = x[x.length - 2] * x.pop() | 0; break; case 208: ; @@ -21073,7 +22064,7 @@ case 210: ; case 211: - v.push(f[oa - 208]); + x.push(s[na - 208]); break; case 212: ; @@ -21082,13 +22073,13 @@ case 214: ; case 215: - f[oa - 212] = v.pop(); + s[na - 212] = x.pop(); break; case 6: - b.AVM2.AS.ASXML.defaultNamespace = S[T.index]; + c.AVM2.AS.ASXML.defaultNamespace = O[ca.index]; break; case 7: - b.AVM2.AS.ASXML.defaultNamespace = v.pop(); + c.AVM2.AS.ASXML.defaultNamespace = x.pop(); break; case 239: ; @@ -21097,126 +22088,143 @@ case 241: break; default: - b.Debug.notImplemented(b.AVM2.opcodeName(oa)); + c.Debug.notImplemented(c.AVM2.opcodeName(na)); } - N++; - } catch (na) { - if (1 > Z.length) { - throw na; - } - for (var na = x(ea, na), Y = 0, fa = Z.length;Y < fa;Y++) { - var sa = Z[Y]; - if (N >= sa.start && N <= sa.end && (!sa.typeName || ea.getType(sa.typeName).isType(na))) { - v.length = 0; - v.push(na); - X.clear(); - N = sa.offset; + fa++; + } catch (ra) { + if (1 > W.length) { + throw ra; + } + for (var ra = w($, ra), ba = 0, wa = W.length;ba < wa;ba++) { + var qa = W[ba]; + if (fa >= qa.start && fa <= qa.end && (!qa.typeName || $.getType(qa.typeName).isType(ra))) { + x.length = 0; + x.push(ra); + ea.clear(); + fa = qa.offset; continue a; } } - throw na; + throw ra; } } }; - return f; + return s; }(); - k.Interpreter = A; - })(b.AVM2 || (b.AVM2 = {})); + h.Interpreter = H; + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - b.VM_METHOD_OVERRIDES["static mochi.as3.MochiServices::connect"] = function() { +(function(c) { + (function(c) { + (function(a) { + a.VM_METHOD_OVERRIDES["static mochi.as3.MochiServices::connect"] = function() { }; - b.VM_METHOD_OVERRIDES["static MochiBot::track"] = function() { + a.VM_METHOD_OVERRIDES["static MochiBot::track"] = function() { }; - b.VM_METHOD_OVERRIDES["com.midasplayer.debug.DebugLog::trace"] = function(b) { - log(b); + a.VM_METHOD_OVERRIDES["com.midasplayer.debug.DebugLog::trace"] = function(a) { + console.log(a); }; - b.VM_METHOD_OVERRIDES["com.midasplayer.engine.comm.DebugGameComm::getGameData"] = function() { + a.VM_METHOD_OVERRIDES["com.midasplayer.engine.comm.DebugGameComm::getGameData"] = function() { return'\ntrue\ntrue\nfalse\n0\n0\n0\n0\n0\n0\n0\n0\n0\n0\nCandy crushed\nbest ever\nscored {0} in one game\nAll Clear Created\ncrushed {0} candy in one game\nScore\nPlease register to play the full game\nLongest chain\nGame ends in {0} seconds\nSuper Stripes Created\nNo more moves!\nMega-Candy Created\nGame starts in {0} seconds\nnow\nLevel reached\nGame Over\nMatch 3 Candy of the same colour to crush them. Matching 4 or 5 in different formations generates special sweets that are extra tasty.\nYou can also combine the special sweets for additional effects by switching them with each other. Try these combinations for a taste you will not forget: \nDouble Colour Bombs Created\nmade {0} combined candy in one game\nPlay like this:\n'; }; - b.VM_METHOD_OVERRIDES["com.antkarlov.Preloader::com.antkarlov:Preloader.isUrl"] = function() { + a.VM_METHOD_OVERRIDES["com.antkarlov.Preloader::com.antkarlov:Preloader.isUrl"] = function() { return!0; }; - b.VM_METHOD_OVERRIDES["static com.demonsters.debugger.MonsterDebugger::initialize"] = function() { + a.VM_METHOD_OVERRIDES["static com.demonsters.debugger.MonsterDebugger::initialize"] = function() { }; - b.VM_METHOD_OVERRIDES["com.spilgames.api.core.tracking.TrackConfig::getTrackers"] = function() { + a.VM_METHOD_OVERRIDES["com.spilgames.api.core.tracking.TrackConfig::getTrackers"] = function() { return[]; }; - b.VM_METHOD_OVERRIDES["com.spilgames.api.components.TextFields.AutoFitTextFieldEx::com.spilgames.api.components.TextFields:AutoFitTextFieldEx.updateProperties"] = b.VM_METHOD_OVERRIDES["com.spilgames.api.components.TextFields.AutoFitTextFieldEx::com.spilgames.api.components.TextFields:AutoFitTextFieldEx.updateTextSize"] = function() { + a.VM_METHOD_OVERRIDES["org.robotlegs.base.CommandMap::org.robotlegs.base:CommandMap.verifyCommandClass"] = function() { + }; + a.VM_METHOD_OVERRIDES["org.swiftsuspenders.injectionpoints.PropertyInjectionPoint::org.swiftsuspenders.injectionpoints:PropertyInjectionPoint.initializeInjection"] = function() { + }; + a.VM_METHOD_OVERRIDES["org.swiftsuspenders.injectionpoints.NoParamsConstructorInjectionPoint::applyInjection"] = function() { + }; + a.VM_METHOD_OVERRIDES["org.swiftsuspenders.injectionpoints.PropertyInjectionPoint::applyInjection"] = function() { + }; + a.VM_METHOD_OVERRIDES["com.spilgames.api.components.TextFields.AutoFitTextFieldEx::com.spilgames.api.components.TextFields:AutoFitTextFieldEx.updateProperties"] = a.VM_METHOD_OVERRIDES["com.spilgames.api.components.TextFields.AutoFitTextFieldEx::com.spilgames.api.components.TextFields:AutoFitTextFieldEx.updateTextSize"] = function() { + }; + a.VM_METHOD_OVERRIDES["Preloader::isNeedSite"] = function() { + return!0; + }; + a.VM_METHOD_OVERRIDES["facebook.utils.FBURI::isFacebookURI"] = function() { + return!0; + }; + a.VM_METHOD_OVERRIDES["facebook.utils.FBURI::is_facebook_cdn_url"] = function() { + return!!this.asGetPublicProperty("authority"); }; - })(b.Runtime || (b.Runtime = {})); - })(b.AVM2 || (b.AVM2 = {})); + })(c.Runtime || (c.Runtime = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - function g(a, c) { - if (65535 < c.length) { +(function(c) { + (function(h) { + function a(b, a) { + if (65535 < a.length) { throw "AMF short string exceeded"; } - if (c.length) { - var d = b.StringUtilities.utf8decode(c); - a.writeByte(d.length >> 8 & 255); - a.writeByte(d.length & 255); + if (a.length) { + var d = c.StringUtilities.utf8decode(a); + b.writeByte(d.length >> 8 & 255); + b.writeByte(d.length & 255); for (var e = 0;e < d.length;e++) { - a.writeByte(d[e]); + b.writeByte(d[e]); } } else { - a.writeByte(0), a.writeByte(0); + b.writeByte(0), b.writeByte(0); } } - function f(a) { - var c = a.readByte() << 8 | a.readByte(); - if (!c) { + function s(b) { + var a = b.readByte() << 8 | b.readByte(); + if (!a) { return ""; } - for (var d = new Uint8Array(c), e = 0;e < c;e++) { - d[e] = a.readByte(); + for (var d = new Uint8Array(a), e = 0;e < a;e++) { + d[e] = b.readByte(); } - return b.StringUtilities.utf8encode(d); + return c.StringUtilities.utf8encode(d); } - function t(a, b) { - var c = new ArrayBuffer(8), d = new DataView(c); - d.setFloat64(0, b, !1); - for (var e = 0;e < c.byteLength;e++) { - a.writeByte(d.getUint8(e)); + function v(b, a) { + var d = new ArrayBuffer(8), e = new DataView(d); + e.setFloat64(0, a, !1); + for (var f = 0;f < d.byteLength;f++) { + b.writeByte(e.getUint8(f)); } } - function s(a) { - for (var b = new ArrayBuffer(8), c = new DataView(b), d = 0;d < b.byteLength;d++) { - c.setUint8(d, a.readByte()); + function p(b) { + for (var a = new ArrayBuffer(8), d = new DataView(a), e = 0;e < a.byteLength;e++) { + d.setUint8(e, b.readByte()); } - return c.getFloat64(0, !1); + return d.getFloat64(0, !1); } - function m(a) { - var b = a.readByte(); - if (0 === (b & 128)) { - return b; - } - var c = a.readByte(); - if (0 === (c & 128)) { - return(b & 127) << 7 | c; + function u(b) { + var a = b.readByte(); + if (0 === (a & 128)) { + return a; } - var d = a.readByte(); + var d = b.readByte(); if (0 === (d & 128)) { - return(b & 127) << 14 | (c & 127) << 7 | d; + return(a & 127) << 7 | d; } - a = a.readByte(); - return(b & 127) << 22 | (c & 127) << 15 | (d & 127) << 8 | a; - } - function d(a, b) { - if (0 === (b & 4294967168)) { - a.writeByte(b & 127); + var e = b.readByte(); + if (0 === (e & 128)) { + return(a & 127) << 14 | (d & 127) << 7 | e; + } + b = b.readByte(); + return(a & 127) << 22 | (d & 127) << 15 | (e & 127) << 8 | b; + } + function l(b, a) { + if (0 === (a & 4294967168)) { + b.writeByte(a & 127); } else { - if (0 === (b & 4294950912)) { - a.writeByte(128 | b >> 7 & 127), a.writeByte(b & 127); + if (0 === (a & 4294950912)) { + b.writeByte(128 | a >> 7 & 127), b.writeByte(a & 127); } else { - if (0 === (b & 4292870144)) { - a.writeByte(128 | b >> 14 & 127), a.writeByte(128 | b >> 7 & 127), a.writeByte(b & 127); + if (0 === (a & 4292870144)) { + b.writeByte(128 | a >> 14 & 127), b.writeByte(128 | a >> 7 & 127), b.writeByte(a & 127); } else { - if (0 === (b & 3221225472)) { - a.writeByte(128 | b >> 22 & 127), a.writeByte(128 | b >> 15 & 127), a.writeByte(128 | b >> 8 & 127), a.writeByte(b & 255); + if (0 === (a & 3221225472)) { + b.writeByte(128 | a >> 22 & 127), b.writeByte(128 | a >> 15 & 127), b.writeByte(128 | a >> 8 & 127), b.writeByte(a & 255); } else { throw "AMF3 U29 range"; } @@ -21224,40 +22232,40 @@ } } } - function a(a, c) { - var d = m(a); + function e(b, a) { + var d = u(b); if (1 === d) { return ""; } - var e = c.stringsCache || (c.stringsCache = []); + var e = a.stringsCache || (a.stringsCache = []); if (0 === (d & 1)) { return e[d >> 1]; } - for (var d = d >> 1, l = new Uint8Array(d), n = 0;n < d;n++) { - l[n] = a.readByte(); + for (var d = d >> 1, f = new Uint8Array(d), k = 0;k < d;k++) { + f[k] = b.readByte(); } - d = b.StringUtilities.utf8encode(l); + d = c.StringUtilities.utf8encode(f); e.push(d); return d; } - function c(a, c, e) { - if ("" === c) { - a.writeByte(1); + function m(b, a, d) { + if ("" === a) { + b.writeByte(1); } else { - e = e.stringsCache || (e.stringsCache = []); - var l = e.indexOf(c); - if (0 <= l) { - d(a, l << 1); + d = d.stringsCache || (d.stringsCache = []); + var e = d.indexOf(a); + if (0 <= e) { + l(b, e << 1); } else { - for (e.push(c), c = b.StringUtilities.utf8decode(c), d(a, 1 | c.length << 1), e = 0;e < c.length;e++) { - a.writeByte(c[e]); + for (d.push(a), a = c.StringUtilities.utf8decode(a), l(b, 1 | a.length << 1), d = 0;d < a.length;d++) { + b.writeByte(a[d]); } } } } - function n(b, c) { - var d = b.readByte(); - switch(d) { + function t(b, a) { + var f = b.readByte(); + switch(f) { case 1: return null; case 0: @@ -21267,3398 +22275,8697 @@ case 3: return!0; case 4: - return m(b); + return u(b); case 5: - return s(b); + return p(b); case 6: - return a(b, c); + return e(b, a); case 8: - return new Date(s(b)); + return new Date(p(b)); case 10: - var e = m(b); - if (0 === (e & 1)) { - return c.objectsCache[e >> 1]; + var c = u(b); + if (0 === (c & 1)) { + return a.objectsCache[c >> 1]; } - if (0 !== (e & 4)) { + if (0 !== (c & 4)) { throw "AMF3 Traits-Ext is not supported"; } - var l, f; - if (0 === (e & 2)) { - l = c.traitsCache[e >> 2], f = l.class; + var m, l; + if (0 === (c & 2)) { + m = a.traitsCache[c >> 2], l = m.class; } else { - l = {}; - d = a(b, c); - f = (l.className = d) && k.aliasesCache.names[d]; - l.class = f; - l.isDynamic = 0 !== (e & 8); - l.members = []; - for (var p = f && f.instanceBindings.slots, d = 0, e = e >> 4;d < e;d++) { - for (var g = a(b, c), w = null, e = 1;p && e < p.length;e++) { - if (p[e].name.name === g) { - w = p[e]; + m = {}; + f = e(b, a); + l = (m.className = f) && h.aliasesCache.names[f]; + m.class = l; + m.isDynamic = 0 !== (c & 8); + m.members = []; + for (var n = l && l.instanceBindings.slots, f = 0, c = c >> 4;f < c;f++) { + for (var q = e(b, a), s = null, c = 1;n && c < n.length;c++) { + if (n[c].name.name === q) { + s = n[c]; break; } } - l.members.push(w ? q.getQualifiedName(w.name) : q.getPublicQualifiedName(g)); + m.members.push(s ? k.getQualifiedName(s.name) : k.getPublicQualifiedName(q)); } - (c.traitsCache || (c.traitsCache = [])).push(l); + (a.traitsCache || (a.traitsCache = [])).push(m); } - p = f ? u(f, []) : {}; - (c.objectsCache || (c.objectsCache = [])).push(p); - for (d = 0;d < l.members.length;d++) { - f = n(b, c), p[l.members[d]] = f; + n = l ? d(l, []) : {}; + (a.objectsCache || (a.objectsCache = [])).push(n); + for (f = 0;f < m.members.length;f++) { + l = t(b, a), n[m.members[f]] = l; } - if (l.isDynamic) { + if (m.isDynamic) { for (;;) { - d = a(b, c); - if (!d.length) { + f = e(b, a); + if (!f.length) { break; } - f = n(b, c); - p.asSetPublicProperty(d, f); + l = t(b, a); + n.asSetPublicProperty(f, l); } } - return p; + return n; case 9: - e = m(b); - if (0 === (e & 1)) { - return c.objectsCache[e >> 1]; - } - l = []; - (c.objectsCache || (c.objectsCache = [])).push(l); - for (p = e >> 1;;) { - d = a(b, c); - if (!d.length) { + c = u(b); + if (0 === (c & 1)) { + return a.objectsCache[c >> 1]; + } + m = []; + (a.objectsCache || (a.objectsCache = [])).push(m); + for (n = c >> 1;;) { + f = e(b, a); + if (!f.length) { break; } - f = n(b, c); - l.asSetPublicProperty(d, f); + l = t(b, a); + m.asSetPublicProperty(f, l); } - for (d = 0;d < p;d++) { - f = n(b, c), l.asSetPublicProperty(d, f); + for (f = 0;f < n;f++) { + l = t(b, a), m.asSetPublicProperty(f, l); } - return l; + return m; default: - throw "AMF3 Unknown marker " + d;; + throw "AMF3 Unknown marker " + f;; } } - function p(a, b, c) { - c = c.objectsCache || (c.objectsCache = []); - var e = c.indexOf(b); + function q(b, a, d) { + d = d.objectsCache || (d.objectsCache = []); + var e = d.indexOf(a); if (0 > e) { - return c.push(b), !1; + return d.push(a), !1; } - d(a, e << 1); + l(b, e << 1); return!0; } - function e(a, h, n) { - switch(typeof h) { + function n(b, a, d) { + switch(typeof a) { case "boolean": - a.writeByte(h ? 3 : 2); + b.writeByte(a ? 3 : 2); break; case "number": - h === (h | 0) ? (a.writeByte(4), d(a, h)) : (a.writeByte(5), t(a, h)); + a === (a | 0) ? (b.writeByte(4), l(b, a)) : (b.writeByte(5), v(b, a)); break; case "undefined": - a.writeByte(0); + b.writeByte(0); break; case "string": - a.writeByte(6); - c(a, h, n); + b.writeByte(6); + m(b, a, d); break; case "object": - if (null === h) { - a.writeByte(1); + if (null === a) { + b.writeByte(1); } else { - if (Array.isArray(h)) { - a.writeByte(9); - if (p(a, h, n)) { + if (Array.isArray(a)) { + b.writeByte(9); + if (q(b, a, d)) { break; } - for (var f = 0;f in h;) { - ++f; + for (var e = 0;e in a;) { + ++e; } - d(a, f << 1 | 1); - l(h, function(d, h) { - b.isNumeric(d) && 0 <= d && d < f || (c(a, d, n), e(a, h, n)); + l(b, e << 1 | 1); + f(a, function(a, f) { + c.isNumeric(a) && 0 <= a && a < e || (m(b, a, d), n(b, f, d)); }); - c(a, "", n); - for (var g = 0;g < f;g++) { - e(a, h[g], n); + m(b, "", d); + for (var t = 0;t < e;t++) { + n(b, a[t], d); } } else { - if (h instanceof Date) { - a.writeByte(8); - if (p(a, h, n)) { + if (a instanceof Date) { + b.writeByte(8); + if (q(b, a, d)) { break; } - d(a, 1); - t(a, h.valueOf()); + l(b, 1); + v(b, a.valueOf()); } else { - a.writeByte(10); - if (p(a, h, n)) { + b.writeByte(10); + if (q(b, a, d)) { break; } - var g = !0, m = h.class; - if (m) { - var g = !m.classInfo.instanceInfo.isSealed(), u = k.aliasesCache.classes.get(m) || "", w, s = n.traitsCache || (n.traitsCache = []), K = n.traitsInfos || (n.traitsInfos = []), F = s.indexOf(m); - if (0 > F) { - var J = m.instanceBindings.slots; - w = []; - for (var F = [], A = 1;A < J.length;A++) { - var B = J[A]; - B.name.getNamespace().isPublic() && (w.push(q.getQualifiedName(B.name)), F.push(B.name.name)); - } - s.push(m); - K.push(w); - m = F.length; - d(a, (g ? 11 : 3) + (m << 4)); - c(a, u, n); - for (A = 0;A < m;A++) { - c(a, F[A], n); + var t = !0, s = a.class; + if (s) { + var t = !s.classInfo.instanceInfo.isSealed(), p = h.aliasesCache.classes.get(s) || "", u, K = d.traitsCache || (d.traitsCache = []), y = d.traitsInfos || (d.traitsInfos = []), D = K.indexOf(s); + if (0 > D) { + var L = s.instanceBindings.slots; + u = []; + for (var D = [], H = 1;H < L.length;H++) { + var J = L[H]; + J.name.getNamespace().isPublic() && (u.push(k.getQualifiedName(J.name)), D.push(J.name.name)); + } + K.push(s); + y.push(u); + s = D.length; + l(b, (t ? 11 : 3) + (s << 4)); + m(b, p, d); + for (H = 0;H < s;H++) { + m(b, D[H], d); } } else { - w = K[F], m = w.length, d(a, 1 + (F << 2)); + u = y[D], s = u.length, l(b, 1 + (D << 2)); } - for (A = 0;A < m;A++) { - e(a, h[w[A]], n); + for (H = 0;H < s;H++) { + n(b, a[u[H]], d); } } else { - d(a, 11), c(a, "", n); + l(b, 11), m(b, "", d); } - g && (l(h, function(b, d) { - c(a, b, n); - e(a, d, n); - }), c(a, "", n)); + t && (f(a, function(a, e) { + m(b, a, d); + n(b, e, d); + }), m(b, "", d)); } } } ; } } - var q = b.AVM2.ABC.Multiname, l = b.AVM2.Runtime.forEachPublicProperty, u = b.AVM2.Runtime.construct; - (function(a) { - a[a.NUMBER = 0] = "NUMBER"; - a[a.BOOLEAN = 1] = "BOOLEAN"; - a[a.STRING = 2] = "STRING"; - a[a.OBJECT = 3] = "OBJECT"; - a[a.NULL = 5] = "NULL"; - a[a.UNDEFINED = 6] = "UNDEFINED"; - a[a.REFERENCE = 7] = "REFERENCE"; - a[a.ECMA_ARRAY = 8] = "ECMA_ARRAY"; - a[a.OBJECT_END = 9] = "OBJECT_END"; - a[a.STRICT_ARRAY = 10] = "STRICT_ARRAY"; - a[a.DATE = 11] = "DATE"; - a[a.LONG_STRING = 12] = "LONG_STRING"; - a[a.XML = 15] = "XML"; - a[a.TYPED_OBJECT = 16] = "TYPED_OBJECT"; - a[a.AVMPLUS = 17] = "AVMPLUS"; - })(k.AMF0Marker || (k.AMF0Marker = {})); - var w = function() { - function a() { + var k = c.AVM2.ABC.Multiname, f = c.AVM2.Runtime.forEachPublicProperty, d = c.AVM2.Runtime.construct; + (function(b) { + b[b.NUMBER = 0] = "NUMBER"; + b[b.BOOLEAN = 1] = "BOOLEAN"; + b[b.STRING = 2] = "STRING"; + b[b.OBJECT = 3] = "OBJECT"; + b[b.NULL = 5] = "NULL"; + b[b.UNDEFINED = 6] = "UNDEFINED"; + b[b.REFERENCE = 7] = "REFERENCE"; + b[b.ECMA_ARRAY = 8] = "ECMA_ARRAY"; + b[b.OBJECT_END = 9] = "OBJECT_END"; + b[b.STRICT_ARRAY = 10] = "STRICT_ARRAY"; + b[b.DATE = 11] = "DATE"; + b[b.LONG_STRING = 12] = "LONG_STRING"; + b[b.XML = 15] = "XML"; + b[b.TYPED_OBJECT = 16] = "TYPED_OBJECT"; + b[b.AVMPLUS = 17] = "AVMPLUS"; + })(h.AMF0Marker || (h.AMF0Marker = {})); + var b = function() { + function b() { } - a.write = function(a, b) { - switch(typeof b) { + b.write = function(b, d) { + switch(typeof d) { case "boolean": - a.writeByte(1); - a.writeByte(b ? 1 : 0); + b.writeByte(1); + b.writeByte(d ? 1 : 0); break; case "number": - a.writeByte(0); - t(a, b); + b.writeByte(0); + v(b, d); break; case "undefined": - a.writeByte(6); + b.writeByte(6); break; case "string": - a.writeByte(2); - g(a, b); + b.writeByte(2); + a(b, d); break; case "object": - null === b ? a.writeByte(5) : (Array.isArray(b) ? (a.writeByte(8), a.writeByte(b.length >>> 24 & 255), a.writeByte(b.length >> 16 & 255), a.writeByte(b.length >> 8 & 255), a.writeByte(b.length & 255), l(b, function(b, c) { - g(a, b); - this.write(a, c); - }, this)) : (a.writeByte(3), l(b, function(b, c) { - g(a, b); - this.write(a, c); - }, this)), a.writeByte(0), a.writeByte(0), a.writeByte(9)); + null === d ? b.writeByte(5) : (Array.isArray(d) ? (b.writeByte(8), b.writeByte(d.length >>> 24 & 255), b.writeByte(d.length >> 16 & 255), b.writeByte(d.length >> 8 & 255), b.writeByte(d.length & 255), f(d, function(d, g) { + a(b, d); + this.write(b, g); + }, this)) : (b.writeByte(3), f(d, function(d, g) { + a(b, d); + this.write(b, g); + }, this)), b.writeByte(0), b.writeByte(0), b.writeByte(9)); } }; - a.read = function(a) { - var b = a.readByte(); - switch(b) { + b.read = function(b) { + var a = b.readByte(); + switch(a) { case 0: - return s(a); + return p(b); case 1: - return!!a.readByte(); + return!!b.readByte(); case 2: - return f(a); + return s(b); case 3: - for (var c = {};;) { - b = f(a); - if (!b.length) { + for (var d = {};;) { + a = s(b); + if (!a.length) { break; } - var d = this.read(a); - c.asSetPublicProperty(b, d); + var g = this.read(b); + d.asSetPublicProperty(a, g); } - if (9 !== a.readByte()) { + if (9 !== b.readByte()) { throw "AMF0 End marker is not found"; } - return c; + return d; case 5: return null; case 6: break; case 8: - c = []; - for (c.length = a.readByte() << 24 | a.readByte() << 16 | a.readByte() << 8 | a.readByte();;) { - b = f(a); - if (!b.length) { + d = []; + for (d.length = b.readByte() << 24 | b.readByte() << 16 | b.readByte() << 8 | b.readByte();;) { + a = s(b); + if (!a.length) { break; } - d = this.read(a); - c.asSetPublicProperty(b, d); + g = this.read(b); + d.asSetPublicProperty(a, g); } - if (9 !== a.readByte()) { + if (9 !== b.readByte()) { throw "AMF0 End marker is not found"; } - return c; + return d; case 10: - c = []; - c.length = a.readByte() << 24 | a.readByte() << 16 | a.readByte() << 8 | a.readByte(); - for (b = 0;b < c.length;b++) { - c[b] = this.read(a); + d = []; + d.length = b.readByte() << 24 | b.readByte() << 16 | b.readByte() << 8 | b.readByte(); + for (a = 0;a < d.length;a++) { + d[a] = this.read(b); } - return c; + return d; case 17: - return n(a, {}); + return t(b, {}); default: - throw "AMF0 Unknown marker " + b;; + throw "AMF0 Unknown marker " + a;; } }; - return a; + return b; }(); - k.AMF0 = w; - (function(a) { - a[a.UNDEFINED = 0] = "UNDEFINED"; - a[a.NULL = 1] = "NULL"; - a[a.FALSE = 2] = "FALSE"; - a[a.TRUE = 3] = "TRUE"; - a[a.INTEGER = 4] = "INTEGER"; - a[a.DOUBLE = 5] = "DOUBLE"; - a[a.STRING = 6] = "STRING"; - a[a.XML_DOC = 7] = "XML_DOC"; - a[a.DATE = 8] = "DATE"; - a[a.ARRAY = 9] = "ARRAY"; - a[a.OBJECT = 10] = "OBJECT"; - a[a.XML = 11] = "XML"; - a[a.BYTEARRAY = 12] = "BYTEARRAY"; - a[a.VECTOR_INT = 13] = "VECTOR_INT"; - a[a.VECTOR_UINT = 14] = "VECTOR_UINT"; - a[a.VECTOR_DOUBLE = 15] = "VECTOR_DOUBLE"; - a[a.VECTOR_OBJECT = 16] = "VECTOR_OBJECT"; - a[a.DICTIONARY = 17] = "DICTIONARY"; - })(k.AMF3Marker || (k.AMF3Marker = {})); - k.aliasesCache = {classes:new WeakMap, names:Object.create(null)}; - w = function() { - function a() { + h.AMF0 = b; + (function(b) { + b[b.UNDEFINED = 0] = "UNDEFINED"; + b[b.NULL = 1] = "NULL"; + b[b.FALSE = 2] = "FALSE"; + b[b.TRUE = 3] = "TRUE"; + b[b.INTEGER = 4] = "INTEGER"; + b[b.DOUBLE = 5] = "DOUBLE"; + b[b.STRING = 6] = "STRING"; + b[b.XML_DOC = 7] = "XML_DOC"; + b[b.DATE = 8] = "DATE"; + b[b.ARRAY = 9] = "ARRAY"; + b[b.OBJECT = 10] = "OBJECT"; + b[b.XML = 11] = "XML"; + b[b.BYTEARRAY = 12] = "BYTEARRAY"; + b[b.VECTOR_INT = 13] = "VECTOR_INT"; + b[b.VECTOR_UINT = 14] = "VECTOR_UINT"; + b[b.VECTOR_DOUBLE = 15] = "VECTOR_DOUBLE"; + b[b.VECTOR_OBJECT = 16] = "VECTOR_OBJECT"; + b[b.DICTIONARY = 17] = "DICTIONARY"; + })(h.AMF3Marker || (h.AMF3Marker = {})); + h.aliasesCache = {classes:new WeakMap, names:Object.create(null)}; + b = function() { + function b() { } - a.write = function(a, b) { - e(a, b, {}); + b.write = function(b, a) { + n(b, a, {}); }; - a.read = function(a) { - return n(a, {}); + b.read = function(b) { + return t(b, {}); }; - return a; + return b; }(); - k.AMF3 = w; - })(b.AVM2 || (b.AVM2 = {})); + h.AMF3 = b; + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); console.timeEnd("Load AVM2 Dependencies"); -console.time("Load Compiled Code Cache"); -console.timeEnd("Load Compiled Code Cache"); -console.time("Load Flash TS Dependencies"); -(function(b) { - function k(a) { - var b = {}; - a = a.split(","); - for (var c = 0;c < a.length;c++) { - b[a[c]] = !0; - } - return b; - } - var g = /^<([-A-Za-z0-9_]+)((?:\s+[-A-Za-z0-9_]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/, f = /^<\/([-A-Za-z0-9_]+)[^>]*>/, t = /([-A-Za-z0-9_]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g, s = k("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed"), m = k("address,applet,blockquote,button,center,dd,del,dir,div,dl,dt,fieldset,form,frameset,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul"), - d = k("a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var"), a = k("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr"), c = k("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected"), n = k("script,style"); - b.HTMLParser = function(b, e) { - function q() { - return this[this.length - 1]; - } - function l(b, l, n, f) { - l = l.toLowerCase(); - if (m[l]) { - for (;q() && d[q()];) { - k("", q()); - } +console.time("Load SWF Parser"); +(function(c) { + (function(h) { + (function(a) { + function h(a, e, c) { + return v(a, e, c) << 32 - c >> 32 - c; } - a[l] && q() == l && k("", l); - (f = s[l] || !!f) || h.push(l); - if (e.start) { - var g = Object.create(null); - n.replace(t, function(a, b, d, h, e) { - b = b.toLowerCase(); - g[b] = d ? d : h ? h : e ? e : c[b] ? b : ""; - return a; - }); - e.start && e.start(l, g, !!f); + function v(a, e, c) { + for (var l = e.bitBuffer, k = e.bitLength;c > k;) { + l = l << 8 | a[e.pos++], k += 8; + } + k -= c; + a = l >>> k & u[c]; + e.bitBuffer = l; + e.bitLength = k; + return a; } - } - function k(a, b) { - if (b) { - for (c = h.length - 1;0 <= c && h[c] != b;c--) { + var p = Math.pow; + a.readSi8 = function(a, e) { + return e.getInt8(e.pos++); + }; + a.readSi16 = function(a, e) { + return e.getInt16(e.pos, e.pos += 2); + }; + a.readSi32 = function(a, e) { + return e.getInt32(e.pos, e.pos += 4); + }; + a.readUi8 = function(a, e) { + return a[e.pos++]; + }; + a.readUi16 = function(a, e) { + return e.getUint16(e.pos, e.pos += 2); + }; + a.readUi32 = function(a, e) { + return e.getUint32(e.pos, e.pos += 4); + }; + a.readFixed = function(a, e) { + return e.getInt32(e.pos, e.pos += 4) / 65536; + }; + a.readFixed8 = function(a, e) { + return e.getInt16(e.pos, e.pos += 2) / 256; + }; + a.readFloat16 = function(a, e) { + var c = e.getUint16(e.pos); + e.pos += 2; + var l = c >> 15 ? -1 : 1, k = (c & 31744) >> 10, c = c & 1023; + return k ? 31 === k ? c ? NaN : Infinity * l : l * p(2, k - 15) * (1 + c / 1024) : l * p(2, -14) * (c / 1024); + }; + a.readFloat = function(a, e) { + return e.getFloat32(e.pos, e.pos += 4); + }; + a.readDouble = function(a, e) { + return e.getFloat64(e.pos, e.pos += 8); + }; + a.readEncodedU32 = function(a, e) { + var c = a[e.pos++]; + if (!(c & 128)) { + return c; } - } else { - var c = 0 - } - if (0 <= c) { - for (var d = h.length - 1;d >= c;d--) { - e.end && e.end(h[d]); + c = c & 127 | a[e.pos++] << 7; + if (!(c & 16384)) { + return c; } - h.length = c; - } - } - for (var w, r, h = [], x = b;b;) { - r = !0; - if (q() && n[q()]) { - b = b.replace(new RegExp("(.*)]*>"), function(a, b) { - b = b.replace(/\x3c!--(.*?)--\x3e/g, "$1").replace(/= l;++l) { + u[l] = e = e << 1 | 1; + } + a.readUb = v; + a.readFb = function(a, e, c) { + return h(a, e, c) / 65536; + }; + a.readString = function(a, e, l) { + var n = e.pos; + if (l) { + a = a.subarray(n, n += l); } else { - if (0 == b.indexOf(" w ? b : b.substring(0, w), b = 0 > w ? "" : b.substring(w), e.chars && e.chars(r)); - } - if (b == x) { - throw "Parse Error: " + b; - } - x = b; - } - k(); - }; + e.pos = n; + e = c.StringUtilities.utf8encode(a); + 0 <= e.indexOf("\x00") && (e = e.split("\x00").join("")); + return e; + }; + })(h.Parser || (h.Parser = {})); + })(c.SWF || (c.SWF = {})); })(Shumway || (Shumway = {})); -(function(b) { - var k = b.Debug.notImplemented, g = b.Debug.somewhatImplemented, f = b.Bounds, t = b.ArrayUtilities.DataBuffer, s = b.ColorUtilities, m = b.AVM2.AS.flash; - (function(a) { - a[a.None = 0] = "None"; - a[a.DirtyBounds = 1] = "DirtyBounds"; - a[a.DirtyContent = 2] = "DirtyContent"; - a[a.DirtyStyle = 4] = "DirtyStyle"; - a[a.DirtyFlow = 8] = "DirtyFlow"; - a[a.Dirty = a.DirtyBounds | a.DirtyContent | a.DirtyStyle | a.DirtyFlow] = "Dirty"; - })(b.TextContentFlags || (b.TextContentFlags = {})); - var d = {lt:"<", gt:">", amp:"&", quot:'"', apos:"'"}, a = function() { - function a(b) { - this._id = m.display.DisplayObject.getNextSyncID(); - this._bounds = new f(0, 0, 0, 0); - this._plainText = ""; - this._autoSize = this._borderColor = this._backgroundColor = 0; - this._wordWrap = !1; - this.flags = 0; - this.defaultTextFormat = b || new m.text.TextFormat; - this.textRuns = []; - this.textRunData = new t; - this.coords = this.matrix = null; - } - a.prototype.parseHtml = function(a, c) { - "undefined" === typeof c && (c = !1); - var e = "", f = this.textRuns, l = f.length = 0, u = 0, w = this.defaultTextFormat.clone(), r = null, h = [], x; - b.HTMLParser(a, x = {chars:function(a) { - for (var c = "", h = 0;h < a.length;h++) { - var n = a.charAt(h); - if ("&" !== n) { - c += n; +(function(c) { + (function(h) { + (function(a) { + (function(h) { + function v(d, g, e, f, k) { + e || (e = {}); + e.id = a.readUi16(d, g); + var c = e.lineBounds = {}; + b(d, g, c, f, k); + if (c = e.isMorph = 46 === k || 84 === k) { + var m = e.lineBoundsMorph = {}; + b(d, g, m, f, k); + } + if (m = e.canHaveStrokes = 83 === k || 84 === k) { + var l = e.fillBounds = {}; + b(d, g, l, f, k); + c && (l = e.fillBoundsMorph = {}, b(d, g, l, f, k)); + e.flags = a.readUi8(d, g); + } + if (c) { + e.offsetMorph = a.readUi32(d, g); + var r = e, n, w, l = B(d, g, r, f, k, c, m), q = l.lineBits, t = l.fillBits, h = r.records = []; + do { + var s = {}, l = A(d, g, s, f, k, c, t, q, m, w); + n = l.eos; + t = l.fillBits; + q = l.lineBits; + w = l.bits; + h.push(s); + } while (!n); + l = M(d, g); + t = l.fillBits; + q = l.lineBits; + r = r.recordsMorph = []; + do { + h = {}, l = A(d, g, h, f, k, c, t, q, m, w), n = l.eos, t = l.fillBits, q = l.lineBits, w = l.bits, r.push(h); + } while (!n); } else { - if (n = b.StringUtilities.indexOfAny(a, ["&", ";"], h + 1), 0 < n) { - h = a.substring(h + 1, n); - if (1 < h.length && "#" === h.charAt(0)) { - var g = 0, g = 2 < h.length && "x" === h.charAt(1) ? parseInt(h.substring(1)) : parseInt(h.substring(2), 16), c = c + String.fromCharCode(g) - } else { - void 0 !== d[h] ? c += d[h] : b.Debug.unexpected(h); + t = e; + w = B(d, g, t, f, k, c, m); + l = w.fillBits; + q = w.lineBits; + r = t.records = []; + do { + h = {}, w = A(d, g, h, f, k, c, l, q, m, n), t = w.eos, l = w.fillBits, q = w.lineBits, n = w.bits, r.push(h); + } while (!t); + } + return e; + } + function p(b, d, g, e, f, k) { + var m; + g || (g = {}); + if (4 === f) { + return g.symbolId = a.readUi16(b, d), g.depth = a.readUi16(b, d), g.flags |= 4, g.matrix = w(b, d), d.pos < k && (g.flags |= 8, e = g.cxform = {}, z(b, d, e, f)), g; + } + m = g.flags = 26 < f ? a.readUi16(b, d) : a.readUi8(b, d); + g.depth = a.readUi16(b, d); + m & 2048 && (g.className = a.readString(b, d, 0)); + m & 2 && (g.symbolId = a.readUi16(b, d)); + m & 4 && (g.matrix = w(b, d)); + if (m & 8) { + var l = g.cxform = {}; + z(b, d, l, f); + } + m & 16 && (g.ratio = a.readUi16(b, d)); + m & 32 && (g.name = a.readString(b, d, 0)); + m & 64 && (g.clipDepth = a.readUi16(b, d)); + if (m & 256) { + for (l = a.readUi8(b, d), f = g.filters = [];l--;) { + var r = {}; + K(b, d, r); + f.push(r); + } + } + m & 512 && (g.blendMode = a.readUi8(b, d)); + m & 1024 && (g.bmpCache = a.readUi8(b, d)); + m & 8192 && (g.visibility = a.readUi8(b, d)); + m & 16384 && (f = g, l = a.readUi8(b, d) | a.readUi8(b, d) << 24 | a.readUi8(b, d) << 16 | a.readUi8(b, d) << 8, f.backgroundColor = l); + if (m & 128) { + a.readUi16(b, d); + 6 <= e ? a.readUi32(b, d) : a.readUi16(b, d); + m = g.events = []; + do { + f = {}; + var l = b, r = d, n = f, q = e, t = n.flags = 6 <= q ? a.readUi32(l, r) : a.readUi16(l, r); + t ? (6 === q && (t &= -262145), q = n.length = a.readUi32(l, r), t & 131072 && (n.keyCode = a.readUi8(l, r), q--), t = r.pos + q, n.actionsData = l.subarray(r.pos, t), r.pos = t, l = !1) : l = !0; + if (l) { + break; } - h = n; - } else { - for (var p in d) { - if (a.indexOf(p, h + 1) === h + 1) { - c += d[p]; - h += p.length; + if (d.pos > k) { + c.Debug.warning("PlaceObject handler attempted to read clip events beyond tag end"); + d.pos = k; + break; + } + m.push(f); + } while (1); + } + return g; + } + function u(b, d, g, e, f) { + g || (g = {}); + 5 === f && (g.symbolId = a.readUi16(b, d)); + g.depth = a.readUi16(b, d); + return g; + } + function l(b, d, g, e, f, k, c) { + e = g || {}; + e.id = a.readUi16(b, d); + if (21 < f) { + var m = a.readUi32(b, d); + 90 === f && (e.deblock = a.readFixed8(b, d)); + m += d.pos; + g = e.imgData = b.subarray(d.pos, m); + e.alphaData = b.subarray(m, k); + } else { + g = e.imgData = b.subarray(d.pos, k); + } + d.pos = k; + switch(g[0] << 8 | g[1]) { + case 65496: + ; + case 65497: + e.mimeType = "image/jpeg"; + break; + case 35152: + e.mimeType = "image/png"; + break; + case 18249: + e.mimeType = "image/gif"; + break; + default: + e.mimeType = "application/octet-stream"; + } + e.jpegTables = 6 === f ? c : null; + return e; + } + function e(b, d, g, e, f, k) { + var c; + g || (g = {}); + g.id = a.readUi16(b, d); + if (7 == f) { + var m = g.characters = []; + do { + var l = {}; + c = b; + var r = d, n = l, q = e, t = f, h = a.readUi8(c, r), s = n.eob = !h; + n.flags = 8 <= q ? (h >> 5 & 1 ? 512 : 0) | (h >> 4 & 1 ? 256 : 0) : 0; + n.stateHitTest = h >> 3 & 1; + n.stateDown = h >> 2 & 1; + n.stateOver = h >> 1 & 1; + n.stateUp = h & 1; + s || (n.symbolId = a.readUi16(c, r), n.depth = a.readUi16(c, r), n.matrix = w(c, r), 34 === t && (q = n.cxform = {}, z(c, r, q, t)), n.flags & 256 && (n.filterCount = a.readUi8(c, r), t = n.filters = {}, K(c, r, t)), n.flags & 512 && (n.blendMode = a.readUi8(c, r))); + c = s; + m.push(l); + } while (!c); + g.actionsData = b.subarray(d.pos, k); + d.pos = k; + } else { + m = a.readUi8(b, d); + g.trackAsMenu = m >> 7 & 1; + l = a.readUi16(b, d); + m = g.characters = []; + do { + r = {}; + n = a.readUi8(b, d); + c = r.eob = !n; + r.flags = 8 <= e ? (n >> 5 & 1 ? 512 : 0) | (n >> 4 & 1 ? 256 : 0) : 0; + r.stateHitTest = n >> 3 & 1; + r.stateDown = n >> 2 & 1; + r.stateOver = n >> 1 & 1; + r.stateUp = n & 1; + if (!c) { + r.symbolId = a.readUi16(b, d); + r.depth = a.readUi16(b, d); + r.matrix = w(b, d); + 34 === f && (n = r.cxform = {}, z(b, d, n, f)); + if (r.flags & 256) { + for (s = a.readUi8(b, d), n = g.filters = [];s--;) { + t = {}, K(b, d, t), n.push(t); + } + } + r.flags & 512 && (r.blendMode = a.readUi8(b, d)); + } + m.push(r); + } while (!c); + if (l) { + for (e = g.buttonActions = [];d.pos < k;) { + f = b; + m = d; + c = k; + l = m.pos; + c = (r = a.readUi16(f, m)) ? l + r : c; + r = a.readUi16(f, m); + m.pos = c; + if (d.pos > k) { break; } + e.push({keyCode:(r & 65024) >> 9, stateTransitionFlags:r & 511, actionsData:f.subarray(l + 4, c)}); } + d.pos = k; } } + return g; } - a = c; - e += a; - u += a.length; - u - l && (r && r.textFormat.equals(w) ? r.endIndex = u : (r = new m.text.TextRun(l, u, w), f.push(r)), l = u); - }, start:function(a, b) { - switch(a) { - case "a": - h.push(w); - g(""); - var d = b.target || w.target, l = b.url || w.url; - if (d !== w.target || l !== w.url) { - w = w.clone(), w.target = d, w.url = l; + function m(b, d, g, e, f) { + g || (g = {}); + g.id = a.readUi16(b, d); + for (var k = a.readUi16(b, d), c = g.glyphCount = k / 2, m = [], l = c - 1;l--;) { + m.push(a.readUi16(b, d)); + } + g.offsets = [k].concat(m); + for (k = g.glyphs = [];c--;) { + m = {}, y(b, d, m, e, f), k.push(m); + } + return g; + } + function t(d, e, f, k, c) { + var m; + f || (f = {}); + f.id = a.readUi16(d, e); + var l = f.bbox = {}; + b(d, e, l, k, c); + f.matrix = w(d, e); + k = f.glyphBits = a.readUi8(d, e); + var l = f.advanceBits = a.readUi8(d, e), n = f.records = []; + do { + var q = {}; + m = d; + var t = e, h = q, s = c, z = k, p = l, A = void 0; + a.align(m, t); + var v = a.readUb(m, t, 8), u = h.eot = !v, A = m, B = t, M = h, N = v, v = M.hasFont = N >> 3 & 1, y = M.hasColor = N >> 2 & 1, K = M.hasMoveY = N >> 1 & 1, N = M.hasMoveX = N & 1; + v && (M.fontId = a.readUi16(A, B)); + y && (M.color = 33 === s ? r(A, B) : g(A, B)); + N && (M.moveX = a.readSi16(A, B)); + K && (M.moveY = a.readSi16(A, B)); + v && (M.fontHeight = a.readUi16(A, B)); + if (!u) { + for (A = a.readUi8(m, t), A = h.glyphCount = A, h = h.entries = [];A--;) { + B = {}, M = m, s = t, v = B, y = p, v.glyphIndex = a.readUb(M, s, z), v.advance = a.readSb(M, s, y), h.push(B); + } + } + m = u; + n.push(q); + } while (!m); + return f; + } + function q(b, d, g, e, f) { + g || (g = {}); + 15 == f && (g.soundId = a.readUi16(b, d)); + 89 == f && (g.soundClassName = a.readString(b, d, 0)); + e = g; + f = {}; + a.readUb(b, d, 2); + f.stop = a.readUb(b, d, 1); + f.noMultiple = a.readUb(b, d, 1); + var k = f.hasEnvelope = a.readUb(b, d, 1), c = f.hasLoops = a.readUb(b, d, 1), m = f.hasOutPoint = a.readUb(b, d, 1); + if (f.hasInPoint = a.readUb(b, d, 1)) { + f.inPoint = a.readUi32(b, d); + } + m && (f.outPoint = a.readUi32(b, d)); + c && (f.loopCount = a.readUi16(b, d)); + if (k) { + for (c = f.envelopeCount = a.readUi8(b, d), k = f.envelopes = [];c--;) { + var m = {}, l = b, r = d, n = m; + n.pos44 = a.readUi32(l, r); + n.volumeLeft = a.readUi16(l, r); + n.volumeRight = a.readUi16(l, r); + k.push(m); } - break; - case "b": - h.push(w); - w.bold || (w = w.clone(), w.bold = !0); - break; - case "font": - h.push(w); - var d = s.isValidHexColor(b.color) ? s.hexToRGB(b.color) : w.color, l = b.face || w.font, n = isNaN(b.size) ? w.size : +b.size; - if (d !== w.color || l !== w.font || n !== w.size) { - w = w.clone(), w.color = d, w.font = l, w.size = n; + } + e.soundInfo = f; + return g; + } + function n(b, d, g, e, f, k) { + g = g || {}; + g.id = a.readUi16(b, d); + e = g.format = a.readUi8(b, d); + g.width = a.readUi16(b, d); + g.height = a.readUi16(b, d); + g.hasAlpha = 36 === f; + 3 === e && (g.colorTableSize = a.readUi8(b, d)); + g.bmpData = b.subarray(d.pos, k); + d.pos = k; + return g; + } + function k(d, g, e, f, k) { + e || (e = {}); + e.id = a.readUi16(d, g); + var c = a.readUi8(d, g), m = e.hasLayout = c & 128 ? 1 : 0; + e.shiftJis = 5 < f && c & 64 ? 1 : 0; + e.smallText = c & 32 ? 1 : 0; + e.ansi = c & 16 ? 1 : 0; + var l = e.wideOffset = c & 8 ? 1 : 0, r = e.wide = c & 4 ? 1 : 0; + e.italic = c & 2 ? 1 : 0; + e.bold = c & 1 ? 1 : 0; + 5 < f ? e.language = a.readUi8(d, g) : (a.readUi8(d, g), e.language = 0); + c = a.readUi8(d, g); + e.name = a.readString(d, g, c); + 75 === k && (e.resolution = 20); + c = e.glyphCount = a.readUi16(d, g); + if (0 === c) { + return e; + } + var n = g.pos; + if (l) { + for (var l = e.offsets = [], w = c;w--;) { + l.push(a.readUi32(d, g)); } - break; - case "img": - k(""); - break; - case "i": - h.push(w); - r || (w = w.clone(), w.italic = !0); - break; - case "li": - if (h.push(w), w.bullet || (w = w.clone(), w.bullet = !0), "\r" === e[e.length - 1]) { + e.mapOffset = a.readUi32(d, g); + } else { + l = e.offsets = []; + for (w = c;w--;) { + l.push(a.readUi16(d, g)); + } + e.mapOffset = a.readUi16(d, g); + } + l = e.glyphs = []; + for (w = c;w--;) { + var q = {}; + 1 === e.offsets[c - w] + n - g.pos ? (a.readUi8(d, g), l.push({records:[{type:0, eos:!0, hasNewStyles:0, hasLineStyle:0, hasFillStyle1:0, hasFillStyle0:0, move:0}]})) : (y(d, g, q, f, k), l.push(q)); + } + if (r) { + for (n = e.codes = [], l = c;l--;) { + n.push(a.readUi16(d, g)); + } + } else { + for (n = e.codes = [], l = c;l--;) { + n.push(a.readUi8(d, g)); + } + } + if (m) { + e.ascent = a.readUi16(d, g); + e.descent = a.readUi16(d, g); + e.leading = a.readSi16(d, g); + m = e.advance = []; + for (n = c;n--;) { + m.push(a.readSi16(d, g)); + } + for (m = e.bbox = [];c--;) { + n = {}, b(d, g, n, f, k), m.push(n); + } + k = a.readUi16(d, g); + for (f = e.kerning = [];k--;) { + c = {}, m = d, n = g, l = c, r ? (l.code1 = a.readUi16(m, n), l.code2 = a.readUi16(m, n)) : (l.code1 = a.readUi8(m, n), l.code2 = a.readUi8(m, n)), l.adjustment = a.readUi16(m, n), f.push(c); + } + } + return e; + } + function f(b, d, g, e, f, k) { + g || (g = {}); + g.id = a.readUi16(b, d); + e = a.readUi8(b, d); + f = g.hasFontData = e & 4 ? 1 : 0; + g.italic = e & 2 ? 1 : 0; + g.bold = e & 1 ? 1 : 0; + g.name = a.readString(b, d, 0); + f && (g.data = b.subarray(d.pos, k), d.pos = k); + return g; + } + function d(b, d, g) { + g || (g = {}); + for (var e = a.readEncodedU32(b, d), f = g.scenes = [];e--;) { + var k = {}; + k.offset = a.readEncodedU32(b, d); + k.name = a.readString(b, d, 0); + f.push(k); + } + e = a.readEncodedU32(b, d); + for (f = g.labels = [];e--;) { + k = {}, k.frame = a.readEncodedU32(b, d), k.name = a.readString(b, d, 0), f.push(k); + } + return g; + } + function b(b, d, g, e, f) { + a.align(b, d); + var k = a.readUb(b, d, 5); + e = a.readSb(b, d, k); + f = a.readSb(b, d, k); + var c = a.readSb(b, d, k), k = a.readSb(b, d, k); + g.xMin = e; + g.xMax = f; + g.yMin = c; + g.yMax = k; + a.align(b, d); + } + function g(b, d) { + return(a.readUi8(b, d) << 24 | a.readUi8(b, d) << 16 | a.readUi8(b, d) << 8 | 255) >>> 0; + } + function r(b, d) { + return a.readUi8(b, d) << 24 | a.readUi8(b, d) << 16 | a.readUi8(b, d) << 8 | a.readUi8(b, d); + } + function w(b, d) { + var g = {}; + a.align(b, d); + if (a.readUb(b, d, 1)) { + var e = a.readUb(b, d, 5); + g.a = a.readFb(b, d, e); + g.d = a.readFb(b, d, e); + } else { + g.a = 1, g.d = 1; + } + a.readUb(b, d, 1) ? (e = a.readUb(b, d, 5), g.b = a.readFb(b, d, e), g.c = a.readFb(b, d, e)) : (g.b = 0, g.c = 0); + var e = a.readUb(b, d, 5), f = a.readSb(b, d, e), e = a.readSb(b, d, e); + g.tx = f; + g.ty = e; + a.align(b, d); + return g; + } + function z(b, d, g, e) { + a.align(b, d); + var f = a.readUb(b, d, 1), k = a.readUb(b, d, 1), c = a.readUb(b, d, 4); + k ? (g.redMultiplier = a.readSb(b, d, c), g.greenMultiplier = a.readSb(b, d, c), g.blueMultiplier = a.readSb(b, d, c), g.alphaMultiplier = 4 < e ? a.readSb(b, d, c) : 256) : (g.redMultiplier = 256, g.greenMultiplier = 256, g.blueMultiplier = 256, g.alphaMultiplier = 256); + f ? (g.redOffset = a.readSb(b, d, c), g.greenOffset = a.readSb(b, d, c), g.blueOffset = a.readSb(b, d, c), g.alphaOffset = 4 < e ? a.readSb(b, d, c) : 0) : (g.redOffset = 0, g.greenOffset = 0, g.blueOffset = 0, g.alphaOffset = 0); + a.align(b, d); + } + function A(b, d, g, e, f, k, c, m, l, r) { + var n, w = g.type = a.readUb(b, d, 1), q = a.readUb(b, d, 5); + n = g.eos = !(w || q); + if (w) { + e = (q & 15) + 2, (g.isStraight = q >> 4) ? (g.isGeneral = a.readUb(b, d, 1)) ? (g.deltaX = a.readSb(b, d, e), g.deltaY = a.readSb(b, d, e)) : (g.isVertical = a.readUb(b, d, 1)) ? g.deltaY = a.readSb(b, d, e) : g.deltaX = a.readSb(b, d, e) : (g.controlDeltaX = a.readSb(b, d, e), g.controlDeltaY = a.readSb(b, d, e), g.anchorDeltaX = a.readSb(b, d, e), g.anchorDeltaY = a.readSb(b, d, e)), b = e; + } else { + var t = g.hasNewStyles = 2 < f ? q >> 4 : 0, h = g.hasLineStyle = q >> 3 & 1, s = g.hasFillStyle1 = q >> 2 & 1, z = g.hasFillStyle0 = q >> 1 & 1; + if (g.move = q & 1) { + r = a.readUb(b, d, 5), g.moveX = a.readSb(b, d, r), g.moveY = a.readSb(b, d, r); + } + z && (g.fillStyle0 = a.readUb(b, d, c)); + s && (g.fillStyle1 = a.readUb(b, d, c)); + h && (g.lineStyle = a.readUb(b, d, m)); + t && (b = B(b, d, g, e, f, k, l), m = b.lineBits, c = b.fillBits); + b = r; + } + return{type:w, flags:q, eos:n, fillBits:c, lineBits:m, bits:b}; + } + function B(b, d, e, f, k, c, m) { + var l, n = a.readUi8(b, d); + l = 2 < k && 255 === n ? a.readUi16(b, d) : n; + for (n = e.fillStyles = [];l--;) { + var w = {}; + N(b, d, w, f, k, c); + n.push(w); + } + n = a.readUi8(b, d); + n = 2 < k && 255 === n ? a.readUi16(b, d) : n; + for (e = e.lineStyles = [];n--;) { + l = {}; + var w = b, q = d, t = l, h = f, s = k, z = c, p = m; + t.width = a.readUi16(w, q); + z && (t.widthMorph = a.readUi16(w, q)); + if (p) { + a.align(w, q); + t.startCapsStyle = a.readUb(w, q, 2); + var p = t.jointStyle = a.readUb(w, q, 2), A = t.hasFill = a.readUb(w, q, 1); + t.noHscale = a.readUb(w, q, 1); + t.noVscale = a.readUb(w, q, 1); + t.pixelHinting = a.readUb(w, q, 1); + a.readUb(w, q, 5); + t.noClose = a.readUb(w, q, 1); + t.endCapsStyle = a.readUb(w, q, 2); + 2 === p && (t.miterLimitFactor = a.readFixed8(w, q)); + A ? (t = t.fillStyle = {}, N(w, q, t, h, s, z)) : (t.color = r(w, q), z && (t.colorMorph = r(w, q))); + } else { + t.color = 22 < s ? r(w, q) : g(w, q), z && (t.colorMorph = r(w, q)); + } + e.push(l); + } + b = M(b, d); + return{fillBits:b.fillBits, lineBits:b.lineBits}; + } + function M(b, d) { + a.align(b, d); + var g = a.readUb(b, d, 4), e = a.readUb(b, d, 4); + return{fillBits:g, lineBits:e}; + } + function N(b, d, e, f, k, c) { + f = e.type = a.readUi8(b, d); + switch(f) { + case 0: + e.color = 22 < k || c ? r(b, d) : g(b, d); + c && (e.colorMorph = r(b, d)); + break; + case 16: + ; + case 18: + ; + case 19: + e.matrix = w(b, d); + c && (e.matrixMorph = w(b, d)); + 83 === k ? (e.spreadMode = a.readUb(b, d, 2), e.interpolationMode = a.readUb(b, d, 2)) : a.readUb(b, d, 4); + for (var m = e.count = a.readUb(b, d, 4), l = e.records = [];m--;) { + var n = {}, q = b, t = d, h = n, s = k, z = c; + h.ratio = a.readUi8(q, t); + h.color = 22 < s ? r(q, t) : g(q, t); + z && (h.ratioMorph = a.readUi8(q, t), h.colorMorph = r(q, t)); + l.push(n); + } + 19 === f && (e.focalPoint = a.readSi16(b, d), c && (e.focalPointMorph = a.readSi16(b, d))); + break; + case 64: + ; + case 65: + ; + case 66: + ; + case 67: + e.bitmapId = a.readUi16(b, d), e.condition = 64 === f || 67 === f, e.matrix = w(b, d), c && (e.matrixMorph = w(b, d)); + } + } + function K(b, d, g) { + var e = g.type = a.readUi8(b, d); + switch(e) { + case 0: + ; + case 2: + ; + case 3: + ; + case 4: + ; + case 7: + var f; + f = 4 === e || 7 === e ? a.readUi8(b, d) : 1; + for (var k = g.colors = [], c = f;c--;) { + k.push(r(b, d)); + } + 3 === e && (g.hightlightColor = r(b, d)); + if (4 === e || 7 === e) { + for (k = g.ratios = [];f--;) { + k.push(a.readUi8(b, d)); + } + } + g.blurX = a.readFixed(b, d); + g.blurY = a.readFixed(b, d); + 2 !== e && (g.angle = a.readFixed(b, d), g.distance = a.readFixed(b, d)); + g.strength = a.readFixed8(b, d); + g.inner = a.readUb(b, d, 1); + g.knockout = a.readUb(b, d, 1); + g.compositeSource = a.readUb(b, d, 1); + 3 === e || 4 === e || 7 === e ? (g.onTop = a.readUb(b, d, 1), g.quality = a.readUb(b, d, 4)) : g.quality = a.readUb(b, d, 5); break; + case 1: + g.blurX = a.readFixed(b, d); + g.blurY = a.readFixed(b, d); + g.quality = a.readUb(b, d, 5); + a.readUb(b, d, 3); + break; + case 5: + f = g.matrixX = a.readUi8(b, d); + k = g.matrixY = a.readUi8(b, d); + g.divisor = a.readFloat(b, d); + g.bias = a.readFloat(b, d); + e = g.matrix = []; + for (f *= k;f--;) { + e.push(a.readFloat(b, d)); + } + g.color = r(b, d); + a.readUb(b, d, 6); + g.clamp = a.readUb(b, d, 1); + g.preserveAlpha = a.readUb(b, d, 1); + break; + case 6: + for (g = g.matrix = [], e = 20;e--;) { + g.push(a.readFloat(b, d)); + } + ; + } + } + function y(b, a, d, g, e) { + var f, k; + k = M(b, a); + var c = k.fillBits, m = k.lineBits, l = d.records = []; + do { + var r = {}; + k = A(b, a, r, g, e, !1, c, m, !1, f); + d = k.eos; + c = k.fillBits; + m = k.lineBits; + f = k.bits; + l.push(r); + } while (!d); + } + h.defineImage = l; + h.defineFont = m; + h.soundStreamHead = function(b, d) { + var g = {}, e = a.readUi8(b, d); + g.playbackRate = e >> 2 & 3; + g.playbackSize = e >> 1 & 1; + g.playbackType = e & 1; + var e = a.readUi8(b, d), f = g.streamCompression = e >> 4 & 15; + g.streamRate = e >> 2 & 3; + g.streamSize = e >> 1 & 1; + g.streamType = e & 1; + g.samplesCount = a.readUi16(b, d); + 2 == f && (g.latencySeek = a.readSi16(b, d)); + return g; + }; + h.defineBitmap = n; + h.defineFont2 = k; + h.defineFont4 = f; + h.defineScene = d; + h.rgb = g; + h.tagHandlers = {0:void 0, 1:void 0, 2:v, 4:p, 5:u, 6:l, 7:e, 8:void 0, 9:void 0, 10:m, 11:t, 12:void 0, 13:void 0, 14:function(b, d, g, e, f, k) { + g || (g = {}); + g.id = a.readUi16(b, d); + e = a.readUi8(b, d); + g.soundFormat = e >> 4 & 15; + g.soundRate = e >> 2 & 3; + g.soundSize = e >> 1 & 1; + g.soundType = e & 1; + g.samplesCount = a.readUi32(b, d); + g.soundData = b.subarray(d.pos, k); + d.pos = k; + return g; + }, 15:q, 17:void 0, 18:void 0, 19:void 0, 20:n, 21:l, 22:v, 23:void 0, 24:void 0, 26:p, 28:u, 32:v, 33:t, 34:e, 35:l, 36:n, 37:function(d, g, e, f, k) { + e || (e = {}); + e.id = a.readUi16(d, g); + var c = e.bbox = {}; + b(d, g, c, f, k); + f = a.readUi16(d, g); + k = e.hasText = f >> 7 & 1; + e.wordWrap = f >> 6 & 1; + e.multiline = f >> 5 & 1; + e.password = f >> 4 & 1; + e.readonly = f >> 3 & 1; + var c = e.hasColor = f >> 2 & 1, m = e.hasMaxLength = f >> 1 & 1, l = e.hasFont = f & 1, n = e.hasFontClass = f >> 15 & 1; + e.autoSize = f >> 14 & 1; + var w = e.hasLayout = f >> 13 & 1; + e.noSelect = f >> 12 & 1; + e.border = f >> 11 & 1; + e.wasStatic = f >> 10 & 1; + e.html = f >> 9 & 1; + e.useOutlines = f >> 8 & 1; + l && (e.fontId = a.readUi16(d, g)); + n && (e.fontClass = a.readString(d, g, 0)); + l && (e.fontHeight = a.readUi16(d, g)); + c && (e.color = r(d, g)); + m && (e.maxLength = a.readUi16(d, g)); + w && (e.align = a.readUi8(d, g), e.leftMargin = a.readUi16(d, g), e.rightMargin = a.readUi16(d, g), e.indent = a.readSi16(d, g), e.leading = a.readSi16(d, g)); + e.variableName = a.readString(d, g, 0); + k && (e.initialText = a.readString(d, g, 0)); + return e; + }, 39:void 0, 43:void 0, 45:void 0, 46:v, 48:k, 56:void 0, 57:void 0, 58:void 0, 59:void 0, 60:void 0, 61:void 0, 62:void 0, 64:void 0, 65:void 0, 66:void 0, 69:void 0, 70:p, 71:void 0, 72:void 0, 73:void 0, 74:void 0, 75:k, 76:void 0, 77:void 0, 78:function(d, g, e, f, k) { + e || (e = {}); + e.symbolId = a.readUi16(d, g); + var c = e.splitter = {}; + b(d, g, c, f, k); + return e; + }, 82:void 0, 83:v, 84:v, 86:d, 87:function(b, d, g, e, f, k) { + g || (g = {}); + g.id = a.readUi16(b, d); + a.readUi32(b, d); + g.data = b.subarray(d.pos, k); + d.pos = k; + return g; + }, 88:void 0, 89:q, 90:l, 91:f}; + h.readHeader = function(b, d) { + var g = a.readUb(b, d, 5), e = a.readSb(b, d, g), f = a.readSb(b, d, g), k = a.readSb(b, d, g), g = a.readSb(b, d, g); + a.align(b, d); + var m = a.readUi8(b, d), m = a.readUi8(b, d) + m / 256, l = a.readUi16(b, d); + return{frameRate:m, frameCount:l, bounds:new c.Bounds(e, k, f, g)}; + }; + })(a.LowLevel || (a.LowLevel = {})); + })(h.Parser || (h.Parser = {})); + })(c.SWF || (c.SWF = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.Debug.assert, v = c.Debug.assertUnreachable, p = c.IntegerUtilities.roundToMultipleOfFour, u = c.ArrayUtilities.Inflate; + (function(a) { + a[a.FORMAT_COLORMAPPED = 3] = "FORMAT_COLORMAPPED"; + a[a.FORMAT_15BPP = 4] = "FORMAT_15BPP"; + a[a.FORMAT_24BPP = 5] = "FORMAT_24BPP"; + })(a.BitmapFormat || (a.BitmapFormat = {})); + a.defineBitmap = function(a) { + h.enterTimeline("defineBitmap"); + var e, m = 0; + switch(a.format) { + case 3: + e = a.width; + var m = a.height, t = a.hasAlpha, q = p(e) - e, n = t ? 4 : 3, k = p((a.colorTableSize + 1) * n), f = k + (e + q) * m, d = u.inflate(a.bmpData, f, !0), b = new Uint32Array(e * m), g = 0, r = 0; + if (t) { + for (t = 0;t < m;t++) { + for (var w = 0;w < e;w++) { + var r = d[k++] << 2, z = d[r + 3], A = d[r + 0], B = d[r + 1], r = d[r + 2]; + b[g++] = r << 24 | B << 16 | A << 8 | z; + } + k += q; + } + } else { + for (t = 0;t < m;t++) { + for (w = 0;w < e;w++) { + r = d[k++] * n, z = 255, A = d[r + 0], B = d[r + 1], r = d[r + 2], b[g++] = r << 24 | B << 16 | A << 8 | z; + } + k += q; + } } - ; - case "br": - c && x.chars("\r"); - break; - case "p": - h.push(w); - d = b.align; - -1 < m.text.TextFormatAlign.toNumber(d) && d !== w.align && (w = w.clone(), w.align = d); + s(k === f, "We should be at the end of the data buffer now."); + s(g === e * m, "Should have filled the entire image."); + e = new Uint8Array(b.buffer); + m = 1; break; - case "textformat": - h.push(w); - var d = isNaN(b.blockindent) ? w.blockIndent : +b.blockindent, l = isNaN(b.indent) ? w.indent : +b.indent, n = isNaN(b.leading) ? w.leading : +b.leading, f = isNaN(b.leftmargin) ? w.leftMargin : +b.leftmargin, q = isNaN(b.rightmargin) ? w.rightMargin : +b.rightmargin; - if (d !== w.blockIndent || l !== w.indent || n !== w.leading || f !== w.leftMargin || q !== w.rightMargin) { - w = w.clone(), w.blockIndent = d, w.indent = l, w.leading = n, w.leftMargin = f, w.rightMargin = q; + case 5: + n = a.width; + f = a.height; + q = a.hasAlpha; + e = f * n * 4; + m = u.inflate(a.bmpData, e, !0); + if (q) { + e = m; + } else { + q = new Uint32Array(n * f); + n *= f; + for (d = f = 0;d < n;d++) { + f++, b = m[f++], k = m[f++], g = m[f++], q[d] = g << 24 | k << 16 | b << 8 | 255; + } + s(f === e, "We should be at the end of the data buffer now."); + e = new Uint8Array(q.buffer); } + m = 1; break; - case "u": - h.push(w), w.underline || (w = w.clone(), w.underline = !0); + case 4: + c.Debug.notImplemented("parse15BPP"); + e = null; + m = 1; + break; + default: + v("invalid bitmap format"); } - }, end:function(a) { - switch(a) { - case "li": - ; - case "p": - c && x.chars("\r"); - case "a": - ; - case "b": - ; - case "font": - ; - case "i": - ; - case "textformat": - ; - case "u": - w = h.pop(); + h.leaveTimeline(); + return{definition:{type:"image", id:a.id, width:a.width, height:a.height, mimeType:"application/octet-stream", data:e, dataType:m, image:null}, type:"image"}; + }; + })(h.Parser || (h.Parser = {})); + })(c.SWF || (c.SWF = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + a.defineButton = function(a, h) { + for (var p = a.characters, u = {up:[], over:[], down:[], hitTest:[]}, l = 0, e;(e = p[l++]) && !e.eob;) { + var m = h[e.symbolId]; + m || c.Debug.warning("undefined character in button " + a.id); + m = {symbolId:m.id, code:4, depth:e.depth, flags:e.matrix ? 4 : 0, matrix:e.matrix}; + e.stateUp && u.up.push(m); + e.stateOver && u.over.push(m); + e.stateDown && u.down.push(m); + e.stateHitTest && u.hitTest.push(m); } - }}); - this._plainText = e; - this.textRunData.clear(); - for (var y = 0;y < f.length;y++) { - this._writeTextRun(f[y]); - } - this.flags |= 2; - }; - Object.defineProperty(a.prototype, "plainText", {get:function() { - return this._plainText; - }, set:function(a) { - this._plainText = a; - this.textRuns.length = 0; - a = new m.text.TextRun(0, a.length, this.defaultTextFormat); - this.textRuns[0] = a; - this.textRunData.clear(); - this._writeTextRun(a); - this.flags |= 2; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "bounds", {get:function() { - return this._bounds; - }, set:function(a) { - this._bounds.copyFrom(a); - this.flags |= 1; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "autoSize", {get:function() { - return this._autoSize; - }, set:function(a) { - a !== this._autoSize && (this._autoSize = a, this._plainText && (this.flags |= 8)); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "wordWrap", {get:function() { - return this._wordWrap; - }, set:function(a) { - a !== this._wordWrap && (this._wordWrap = a, this._plainText && (this.flags |= 8)); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "backgroundColor", {get:function() { - return this._backgroundColor; - }, set:function(a) { - a !== this._backgroundColor && (this._backgroundColor = a, this.flags |= 4); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "borderColor", {get:function() { - return this._borderColor; - }, set:function(a) { - a !== this._borderColor && (this._borderColor = a, this.flags |= 4); - }, enumerable:!0, configurable:!0}); - a.prototype._writeTextRun = function(a) { - var b = this.textRunData; - b.writeInt(a.beginIndex); - b.writeInt(a.endIndex); - a = a.textFormat; - var c = +a.size; - b.writeInt(c); - var d = m.text.Font.getByName(a.font) || m.text.Font.getDefaultFont(); - d.fontType === m.text.FontType.EMBEDDED ? b.writeInt(d._id) : (b.writeInt(0), b.writeUTF(m.text.Font.resolveFontName(d.fontName))); - b.writeInt(d.ascent * c); - b.writeInt(d.descent * c); - b.writeInt(null === a.leading ? d.leading * c : +a.leading); - c = null === a.bold ? d.fontStyle === m.text.FontStyle.BOLD || d.fontType === m.text.FontStyle.BOLD_ITALIC : !!a.bold; - d = null === a.italic ? d.fontStyle === m.text.FontStyle.ITALIC || d.fontType === m.text.FontStyle.BOLD_ITALIC : !!a.italic; - b.writeBoolean(c); - b.writeBoolean(d); - b.writeInt(+a.color); - b.writeInt(m.text.TextFormatAlign.toNumber(a.align)); - b.writeBoolean(!!a.bullet); - b.writeInt(+a.indent); - b.writeInt(+a.kerning); - b.writeInt(+a.leftMargin); - b.writeInt(+a.letterSpacing); - b.writeInt(+a.rightMargin); - b.writeBoolean(!!a.underline); - }; - return a; - }(); - b.TextContent = a; + return{type:"button", id:a.id, buttonActions:a.buttonActions, states:u}; + }; + })(h.Parser || (h.Parser = {})); + })(c.SWF || (c.SWF = {})); })(Shumway || (Shumway = {})); -var Shumway$$inline_423 = Shumway || (Shumway = {}), AVM2$$inline_424 = Shumway$$inline_423.AVM2 || (Shumway$$inline_423.AVM2 = {}), AS$$inline_425 = AVM2$$inline_424.AS || (AVM2$$inline_424.AS = {}); -AS$$inline_425.flashOptions = Shumway$$inline_423.Settings.shumwayOptions.register(new Shumway$$inline_423.Options.OptionSet("Flash Options")); -AS$$inline_425.traceLoaderOption = AS$$inline_425.flashOptions.register(new Shumway$$inline_423.Options.Option("tp", "Trace Loader", "boolean", !1, "Trace loader execution.")); -AS$$inline_425.disableAudioOption = AS$$inline_425.flashOptions.register(new Shumway$$inline_423.Options.Option("da", "Disable Audio", "boolean", !1, "Disables audio.")); -var AS$$inline_426 = AVM2$$inline_424.AS, AVM2$$inline_427 = Shumway$$inline_423.AVM2, __extends = this.__extends || function(b, k) { - function g() { - this.constructor = b; - } - for (var f in k) { - k.hasOwnProperty(f) && (b[f] = k[f]); - } - g.prototype = k.prototype; - b.prototype = new g; -}; -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(k) { - var s = function(b) { - function d(a, b, d, f, e, g) { - "undefined" === typeof a && (a = 1); - "undefined" === typeof b && (b = 0); - "undefined" === typeof d && (d = 0); - "undefined" === typeof f && (f = 1); - "undefined" === typeof e && (e = 0); - "undefined" === typeof g && (g = 0); - var l = this._data = new Float64Array(6); - l[0] = a; - l[1] = b; - l[2] = d; - l[3] = f; - l[4] = e; - l[5] = g; - } - __extends(d, b); - d.FromUntyped = function(a) { - return new f.geom.Matrix(a.a, a.b, a.c, a.d, a.tx, a.ty); - }; - d.FromDataBuffer = function(a) { - return new f.geom.Matrix(a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat()); - }; - Object.defineProperty(d.prototype, "a", {get:function() { - return this._data[0]; - }, set:function(a) { - this._data[0] = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "b", {get:function() { - return this._data[1]; - }, set:function(a) { - this._data[1] = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "c", {get:function() { - return this._data[2]; - }, set:function(a) { - this._data[2] = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "d", {get:function() { - return this._data[3]; - }, set:function(a) { - this._data[3] = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "tx", {get:function() { - return this._data[4]; - }, set:function(a) { - this._data[4] = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "ty", {get:function() { - return this._data[5]; - }, set:function(a) { - this._data[5] = a; - }, enumerable:!0, configurable:!0}); - d.prototype.concat = function(a) { - var b = this._data; - a = a._data; - var d = b[0] * a[0], f = 0, e = 0, g = b[3] * a[3], l = b[4] * a[0] + a[4], m = b[5] * a[3] + a[5]; - if (0 !== b[1] || 0 !== b[2] || 0 !== a[1] || 0 !== a[2]) { - d += b[1] * a[2], g += b[2] * a[1], f += b[0] * a[1] + b[1] * a[3], e += b[2] * a[0] + b[3] * a[2], l += b[5] * a[2], m += b[4] * a[1]; - } - b[0] = d; - b[1] = f; - b[2] = e; - b[3] = g; - b[4] = l; - b[5] = m; - }; - d.prototype.preMultiply = function(a) { - this.preMultiplyInto(a, this); - }; - d.prototype.preMultiplyInto = function(a, b) { - var d = this._data, f = a._data, e = b._data, g = f[0] * d[0], l = 0, m = 0, k = f[3] * d[3], r = f[4] * d[0] + d[4], h = f[5] * d[3] + d[5]; - if (0 !== f[1] || 0 !== f[2] || 0 !== d[1] || 0 !== d[2]) { - g += f[1] * d[2], k += f[2] * d[1], l += f[0] * d[1] + f[1] * d[3], m += f[2] * d[0] + f[3] * d[2], r += f[5] * d[2], h += f[4] * d[1]; - } - e[0] = g; - e[1] = l; - e[2] = m; - e[3] = k; - e[4] = r; - e[5] = h; - }; - d.prototype.invert = function() { - this.invertInto(this); - }; - d.prototype.invertInto = function(a) { - var b = this._data, d = a._data, f = b[1], e = b[2], g = b[4], l = b[5]; - if (0 === f && 0 === e) { - var m = d[0] = 1 / b[0], b = d[3] = 1 / b[3]; - d[1] = d[2] = 0; - d[4] = -m * g; - d[5] = -b * l; +(function(c) { + (function(c) { + (function(a) { + function c(a) { + for (var e = 0;2 <= a;) { + a /= 2, ++e; + } + return l(2, e); + } + function h(a) { + return q(a >> 8 & 255, a & 255); + } + function p(a) { + return h(a >> 16) + h(a); + } + function u(a) { + for (var e = 0, f = 0, d = 0, b = 0, g = 0;g < a.length;g++) { + var c = a[g]; + if (c) { + for (var c = c.records, m, l = 0, q = 0, t = 0;t < c.length;t++) { + m = c[t]; + if (m.type) { + m.isStraight ? (l += m.deltaX || 0, q += -(m.deltaY || 0)) : (l += m.controlDeltaX, q += -m.controlDeltaY, l += m.anchorDeltaX, q += -m.anchorDeltaY); } else { - var m = b[0], b = b[3], k = m * b - f * e; - 0 === k ? a.identity() : (k = 1 / k, a = 0, a = d[0] = b * k, f = d[1] = -f * k, e = d[2] = -e * k, b = d[3] = m * k, d[4] = -(a * g + e * l), d[5] = -(f * g + b * l)); + if (m.eos) { + break; + } + m.move && (l = m.moveX, q = -m.moveY); } - }; - d.prototype.identity = function() { - var a = this._data; - a[0] = a[3] = 1; - a[1] = a[2] = a[4] = a[5] = 0; - }; - d.prototype.createBox = function(a, b, d, f, e) { - "undefined" === typeof d && (d = 0); - "undefined" === typeof f && (f = 0); - "undefined" === typeof e && (e = 0); - var g = this._data; - if (0 !== d) { - var l = Math.cos(d); - d = Math.sin(d); - g[0] = l * a; - g[1] = d * b; - g[2] = -d * a; - g[3] = l * b; + e > l && (e = l); + f > q && (f = q); + d < l && (d = l); + b < q && (b = q); + } + } + } + return 5E3 < Math.max(d - e, b - f); + } + var l = Math.pow, e = Math.min, m = Math.max, t = Math.log, q = String.fromCharCode; + a.defineFont = function(a) { + var k = "swf-font-" + a.id, f = a.name || k, d = {type:"font", id:a.id, name:f, bold:1 === a.bold, italic:1 === a.italic, codes:null, metrics:null, data:a.data, originalSize:!1}, b = a.glyphs, g = b ? a.glyphCount = b.length : 0; + if (!g) { + return d; + } + var l = {}, w = [], q = {}, A = [], B, M = !("advance" in a), N = 48 === a.code, K = 75 === a.code; + M && (a.advance = []); + B = Math.max.apply(null, a.codes) || 35; + if (a.codes) { + for (var y = 0;y < a.codes.length;y++) { + var D = a.codes[y]; + if (32 > D || -1 < w.indexOf(D)) { + B++, 8232 == B && (B = 8240), D = B; + } + w.push(D); + q[D] = y; + } + B = w.concat(); + w.sort(function(b, a) { + return b - a; + }); + for (var y = 0, L;void 0 !== (D = w[y++]);) { + var H = D, J = H; + for (L = [y - 1];void 0 !== (D = w[y]) && J + 1 === D;) { + ++J, L.push(y), ++y; + } + A.push([H, J, L]); + } + } else { + L = []; + for (y = 0;y < g;y++) { + D = 57344 + y, w.push(D), q[D] = y, L.push(y); + } + A.push([57344, 57344 + g - 1, L]); + B = w.concat(); + } + var C = a.resolution || 1; + N && u(b) && (C = 20, d.originalSize = !0); + N = Math.ceil(a.ascent / C) || 1024; + L = -Math.ceil(a.descent / C) || 0; + for (var E = a.leading / C || 0, F = l["OS/2"] = "", I = "", G = "", Z = "", y = 0, Q;Q = A[y++];) { + H = Q[0], J = Q[1], D = Q[2][0], F += h(H), I += h(J), G += h(D - H + 1 & 65535), Z += h(0); + } + I += "\u00ff\u00ff"; + F += "\u00ff\u00ff"; + G += "\x00\u0001"; + Z += "\x00\x00"; + A = A.length + 1; + y = 2 * c(A); + H = 2 * A - y; + y = "\x00\x00" + h(2 * A) + h(y) + h(t(A) / t(2)) + h(H) + I + "\x00\x00" + F + G + Z; + l.cmap = "\x00\x00\x00\u0001\x00\u0003\x00\u0001\x00\x00\x00\f\x00\u0004" + h(y.length + 4) + y; + var S = "\x00\u0001\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\x00", F = "\x00\x00", A = 16, I = 0, Z = [], H = [], J = []; + Q = []; + for (var y = G = 0, O = {};void 0 !== (D = w[y++]);) { + for (var P = b[q[D]], V = P.records, $ = 0, W = 0, x = "", ea = "", Y = 0, R = [], Y = -1, P = 0;P < V.length;P++) { + x = V[P]; + if (x.type) { + 0 > Y && (Y = 0, R[Y] = {data:[], commands:[], xMin:0, xMax:0, yMin:0, yMax:0}); + if (x.isStraight) { + R[Y].commands.push(2); + var U = (x.deltaX || 0) / C, ba = -(x.deltaY || 0) / C; } else { - g[0] = a, g[1] = 0, g[2] = 0, g[3] = b; - } - g[4] = f; - g[5] = e; - }; - d.prototype.createGradientBox = function(a, b, d, f, e) { - "undefined" === typeof d && (d = 0); - "undefined" === typeof f && (f = 0); - "undefined" === typeof e && (e = 0); - this.createBox(a / 1638.4, b / 1638.4, d, f + a / 2, e + b / 2); - }; - d.prototype.rotate = function(a) { - a = +a; - if (0 !== a) { - var b = this._data, d = Math.cos(a); - a = Math.sin(a); - var f = b[0], e = b[1], g = b[2], l = b[3], m = b[4], k = b[5]; - b[0] = f * d - e * a; - b[1] = f * a + e * d; - b[2] = g * d - l * a; - b[3] = g * a + l * d; - b[4] = m * d - k * a; - b[5] = m * a + k * d; + R[Y].commands.push(3), U = x.controlDeltaX / C, ba = -x.controlDeltaY / C, $ += U, W += ba, R[Y].data.push($, W), U = x.anchorDeltaX / C, ba = -x.anchorDeltaY / C; } - }; - d.prototype.translate = function(a, b) { - var d = this._data; - d[4] += a; - d[5] += b; - }; - d.prototype.scale = function(a, b) { - var d = this._data; - 1 !== a && (d[0] *= a, d[2] *= a, d[4] *= a); - 1 !== b && (d[1] *= b, d[3] *= b, d[5] *= b); - }; - d.prototype.deltaTransformPoint = function(a) { - return new k.Point(this._data[0] * a.x + this._data[2] * a.y, this._data[1] * a.x + this._data[3] * a.y); - }; - d.prototype.transformX = function(a, b) { - var d = this._data; - return d[0] * a + d[2] * b + d[4]; - }; - d.prototype.transformY = function(a, b) { - var d = this._data; - return d[1] * a + d[3] * b + d[5]; - }; - d.prototype.transformPoint = function(a) { - var b = this._data; - return new k.Point(b[0] * a.x + b[2] * a.y + b[4], b[1] * a.x + b[3] * a.y + b[5]); - }; - d.prototype.transformPointInPlace = function(a) { - var b = this._data; - a.setTo(b[0] * a.x + b[2] * a.y + b[4], b[1] * a.x + b[3] * a.y + b[5]); - return a; - }; - d.prototype.transformBounds = function(a) { - var b = this._data, d = b[0], f = b[1], e = b[2], g = b[3], l = b[4], m = b[5], k = a.xMin, r = a.yMin, h = a.width, x = a.height, b = Math.round(d * k + e * r + l), s = Math.round(f * k + g * r + m), t = Math.round(d * (k + h) + e * r + l), I = Math.round(f * (k + h) + g * r + m), C = Math.round(d * (k + h) + e * (r + x) + l), h = Math.round(f * (k + h) + g * (r + x) + m), d = Math.round(d * k + e * (r + x) + l), f = Math.round(f * k + g * (r + x) + m), g = 0; - b > t && (g = b, b = t, t = g); - C > d && (g = C, C = d, d = g); - a.xMin = b < C ? b : C; - a.xMax = t > d ? t : d; - s > I && (g = s, s = I, I = g); - h > f && (g = h, h = f, f = g); - a.yMin = s < h ? s : h; - a.yMax = I > f ? I : f; - }; - d.prototype.getDeterminant = function() { - var a = this._data; - return a[0] * a[3] - a[1] * a[2]; - }; - d.prototype.getScaleX = function() { - var a = this._data; - if (1 === a[0] && 0 === a[1]) { - return 1; + $ += U; + W += ba; + R[Y].data.push($, W); + } else { + if (x.eos) { + break; } - a = Math.sqrt(a[0] * a[0] + a[1] * a[1]); - return 0 > this.getDeterminant() ? -a : a; - }; - d.prototype.getScaleY = function() { - var a = this._data; - if (0 === a[2] && 1 === a[3]) { - return 1; + if (x.move) { + Y++; + R[Y] = {data:[], commands:[], xMin:0, xMax:0, yMin:0, yMax:0}; + R[Y].commands.push(1); + var X = x.moveX / C, x = -x.moveY / C, U = X - $, ba = x - W, $ = X, W = x; + R[Y].data.push($, W); } - a = Math.sqrt(a[2] * a[2] + a[3] * a[3]); - return 0 > this.getDeterminant() ? -a : a; - }; - d.prototype.getAbsoluteScaleX = function() { - return Math.abs(this.getScaleX()); - }; - d.prototype.getAbsoluteScaleY = function() { - return Math.abs(this.getScaleY()); - }; - d.prototype.getRotation = function() { - return Math.atan2(this._data[1], this._data[0]); - }; - d.prototype.copyFrom = function(a) { - var b = this._data; - a = a._data; - b[0] = a[0]; - b[1] = a[1]; - b[2] = a[2]; - b[3] = a[3]; - b[4] = a[4]; - b[5] = a[5]; - }; - d.prototype.setTo = function(a, b, d, f, e, g) { - var l = this._data; - l[0] = a; - l[1] = b; - l[2] = d; - l[3] = f; - l[4] = e; - l[5] = g; - }; - d.prototype.toTwipsInPlace = function() { - var a = this._data; - a[4] = 20 * a[4] | 0; - a[5] = 20 * a[5] | 0; - return this; - }; - d.prototype.toPixelsInPlace = function() { - var a = this._data; - a[4] /= 20; - a[5] /= 20; - return this; - }; - d.prototype.copyRowTo = function(a, b) { - var d = this._data; - a >>>= 0; - 0 === a ? (b.x = d[0], b.y = d[2], b.z = d[4]) : 1 === a ? (b.x = d[1], b.y = d[3], b.z = d[5]) : 2 === a && (b.x = 0, b.y = 0, b.z = 1); - }; - d.prototype.copyColumnTo = function(a, b) { - var d = this._data; - a >>>= 0; - 0 === a ? (b.x = d[0], b.y = d[1], b.z = 0) : 1 === a ? (b.x = d[2], b.y = d[3], b.z = 0) : 2 === a && (b.x = d[4], b.y = d[5], b.z = 1); - }; - d.prototype.copyRowFrom = function(a, b) { - var d = this._data; - a >>>= 0; - 0 === a ? (d[0] = b.x, d[2] = b.y, d[4] = b.z) : 1 === a && (d[1] = b.x, d[3] = b.y, d[5] = b.z); - }; - d.prototype.copyColumnFrom = function(a, b) { - var d = this._data; - a >>>= 0; - 0 === a ? (d[0] = b.x, d[2] = b.y, d[4] = b.z) : 1 === a && (d[1] = b.x, d[3] = b.y, d[5] = b.z); - }; - d.prototype.updateScaleAndRotation = function(a, b, d) { - var f = this._data; - if (0 === d || 360 === d) { - f[0] = a, f[1] = f[2] = 0, f[3] = b; - } else { - var e = 0, g = 0; - switch(d) { - case 90: - ; - case -270: - e = 0; - g = 1; - break; - case 180: - ; - case -180: - e = -1; - g = 0; - break; - case 270: - ; - case -90: - e = 0; - g = -1; - break; - default: - d = d / 180 * Math.PI, e = Math.cos(d), g = Math.sin(d); - } - f[0] = e * a; - f[1] = g * a; - f[2] = -g * b; - f[3] = e * b; + } + -1 < Y && (R[Y].xMin > $ && (R[Y].xMin = $), R[Y].yMin > W && (R[Y].yMin = W), R[Y].xMax < $ && (R[Y].xMax = $), R[Y].yMax < W && (R[Y].yMax = W)); + } + K || R.sort(function(b, a) { + return(a.xMax - a.xMin) * (a.yMax - a.yMin) - (b.xMax - b.xMin) * (b.yMax - b.yMin); + }); + O[D] = R; + } + for (y = 0;void 0 !== (D = w[y++]);) { + for (var P = b[q[D]], V = P.records, R = O[D], ga = 1, Y = 0, U = W = x = $ = "", D = W = $ = 0, V = -1024, X = 0, ja = -1024, ea = x = "", Y = 0, Y = -1, aa = [], ia = [], P = 0;P < R.length;P++) { + aa = aa.concat(R[P].data), ia = ia.concat(R[P].commands); + } + for (var T = W = $ = 0, da = 0, fa = "", R = "", la = 0, Y = 0, ga = 1, ea = "", P = 0;P < ia.length;P++) { + U = ia[P], 1 === U ? (Y && (++ga, ea += h(Y - 1)), T = aa[la++], da = aa[la++], U = T - $, ba = da - W, x += "\u0001", fa += h(U), R += h(ba), $ = T, W = da) : 2 === U ? (T = aa[la++], da = aa[la++], U = T - $, ba = da - W, x += "\u0001", fa += h(U), R += h(ba), $ = T, W = da) : 3 === U && (T = aa[la++], da = aa[la++], U = T - $, ba = da - W, x += "\x00", fa += h(U), R += h(ba), $ = T, W = da, Y++, T = aa[la++], da = aa[la++], U = T - $, ba = da - W, x += "\u0001", fa += h(U), R += h(ba), + $ = T, W = da), Y++, Y > I && (I = Y), D > $ && (D = $), X > W && (X = W), V < $ && (V = $), ja < W && (ja = W); + } + $ = ea += h((Y || 1) - 1); + W = fa; + U = R; + P || (D = V = X = ja = 0, x += "1"); + P = h(ga) + h(D) + h(X) + h(V) + h(ja) + $ + "\x00\x00" + x + W + U; + P.length & 1 && (P += "\x00"); + S += P; + F += h(A / 2); + A += P.length; + Z.push(D); + H.push(V); + J.push(X); + Q.push(ja); + ga > G && (G = ga); + Y > I && (I = Y); + M && a.advance.push((V - D) * C * 1.3); + } + F += h(A / 2); + l.glyf = S; + K || (y = Math.min.apply(null, J), 0 > y && (L = L || y)); + l["OS/2"] = "\x00\u0001\x00\x00" + h(a.bold ? 700 : 400) + "\x00\u0005\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ALF " + h((a.italic ? 1 : 0) | (a.bold ? 32 : 0)) + h(w[0]) + h(w[w.length - 1]) + h(N) + h(L) + h(E) + h(N) + h(-L) + "\x00\x00\x00\x00\x00\x00\x00\x00"; + l.head = "\x00\u0001\x00\x00\x00\u0001\x00\x00\x00\x00\x00\x00_\u000f<\u00f5\x00\x0B\u0004\x00\x00\x00\x00\x00" + p(Date.now()) + "\x00\x00\x00\x00" + p(Date.now()) + h(e.apply(null, Z)) + h(e.apply(null, J)) + h(m.apply(null, H)) + h(m.apply(null, Q)) + h((a.italic ? 2 : 0) | (a.bold ? 1 : 0)) + "\x00\b\x00\u0002\x00\x00\x00\x00"; + w = a.advance; + l.hhea = "\x00\u0001\x00\x00" + h(N) + h(L) + h(E) + h(w ? m.apply(null, w) : 1024) + "\x00\x00\x00\x00\u0003\u00b8\x00\u0001\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + h(g + 1); + b = "\x00\x00\x00\x00"; + for (y = 0;y < g;++y) { + b += h(w ? w[y] / C : 1024) + "\x00\x00"; + } + l.hmtx = b; + if (a.kerning) { + w = a.kerning; + C = w.length; + y = 2 * c(C); + C = "\x00\x00\x00\u0001\x00\x00" + h(14 + 6 * C) + "\x00\u0001" + h(C) + h(y) + h(t(C) / t(2)) + h(2 * C - y); + for (y = 0;x = w[y++];) { + C += h(q[x.code1]) + h(q[x.code2]) + h(x.adjustment); + } + l.kern = C; + } + l.loca = F; + l.maxp = "\x00\u0001\x00\x00" + h(g + 1) + h(I) + h(G) + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; + y = f.replace(/ /g, ""); + k = [a.copyright || "Original licence", f, "Unknown", k, f, "1.0", y, "Unknown", "Unknown", "Unknown"]; + y = k.length; + a = "\x00\x00" + h(y) + h(12 * y + 6); + for (y = A = 0;f = k[y++];) { + a += "\x00\u0001\x00\x00\x00\x00" + h(y - 1) + h(f.length) + h(A), A += f.length; + } + l.name = a + k.join(""); + l.post = "\x00\u0003\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; + k = Object.keys(l); + y = k.length; + f = "\x00\u0001\x00\x00" + h(y) + "\x00\u0080\x00\u0003\x00 "; + g = ""; + A = 16 * y + f.length; + for (y = 0;a = k[y++];) { + q = l[a]; + w = q.length; + for (f += a + "\x00\x00\x00\x00" + p(A) + p(w);w & 3;) { + q += "\x00", ++w; + } + for (g += q;A & 3;) { + ++A; + } + A += w; + } + l = f + g; + N = {ascent:N / 1024, descent:-L / 1024, leading:E / 1024}; + L = new Uint8Array(l.length); + for (y = 0;y < l.length;y++) { + L[y] = l.charCodeAt(y) & 255; + } + d.codes = B; + d.metrics = N; + d.data = L; + return d; + }; + })(c.Parser || (c.Parser = {})); + })(c.SWF || (c.SWF = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + function s(a, e) { + return a[e] << 8 | a[e + 1]; + } + function v(a, e, l) { + var n = 0, k = e.length, f; + do { + for (var d = n;n < k && 255 !== e[n];) { + ++n; + } + for (;n < k && 255 === e[n];) { + ++n; + } + f = e[n++]; + if (218 === f) { + n = k; + } else { + if (217 === f) { + n += 2; + continue; + } else { + if (208 > f || 216 < f) { + var b = s(e, n); + 192 <= f && 195 >= f && (a.height = s(e, n + 3), a.width = s(e, n + 5)); + n += b; } - }; - d.prototype.clone = function() { - var a = this._data; - return new f.geom.Matrix(a[0], a[1], a[2], a[3], a[4], a[5]); - }; - d.prototype.equals = function(a) { - var b = this._data; - a = a._data; - return b[0] === a[0] && b[1] === a[1] && b[2] === a[2] && b[3] === a[3] && b[4] === a[4] && b[5] === a[5]; - }; - d.prototype.toString = function() { - var a = this._data; - return "(a=" + a[0] + ", b=" + a[1] + ", c=" + a[2] + ", d=" + a[3] + ", tx=" + a[4] + ", ty=" + a[5] + ")"; - }; - d.prototype.writeExternal = function(a) { - var b = this._data; - a.writeFloat(b[0]); - a.writeFloat(b[1]); - a.writeFloat(b[2]); - a.writeFloat(b[3]); - a.writeFloat(b[4]); - a.writeFloat(b[5]); - }; - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - d.FROZEN_IDENTITY_MATRIX = Object.freeze(new d); - d.TEMP_MATRIX = new d; - return d; - }(b.ASNative); - k.Matrix = s; - })(f.geom || (f.geom = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(k) { - function s(a, b, c, d, l, g, m) { - var r = b * b, h = c * c, k = d * d, s = r + h + k, t = Math.sqrt(s); - b /= t; - c /= t; - d /= t; - r /= s; - h /= s; - k /= s; - s = Math.cos(a); - a = Math.sin(a); - return new f.geom.Matrix3D([r + (h + k) * s, b * c * (1 - s) + d * a, b * d * (1 - s) - c * a, 0, b * c * (1 - s) - d * a, h + (r + k) * s, c * d * (1 - s) + b * a, 0, b * d * (1 - s) + c * a, c * d * (1 - s) - b * a, k + (r + h) * s, 0, (l * (h + k) - b * (g * c + m * d)) * (1 - s) + (g * d - m * c) * a, (g * (r + k) - c * (l * b + m * d)) * (1 - s) + (m * b - l * d) * a, (m * (r + h) - d * (l * b + g * c)) * (1 - s) + (l * c - g * b) * a, 1]); + } } - var m = b.Debug.notImplemented, d = b.AVM2.Runtime.asCoerceString, a = new Uint32Array([0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15]), c = function(b) { - function c(a) { - "undefined" === typeof a && (a = null); - this._matrix = new Float32Array(16); - a && 16 <= a.length ? this.copyRawDataFrom(a) : this.identity(); + l.push(e.subarray(d, n)); + } while (n < k); + a.width && a.height || c.Debug.warning("bad jpeg image"); + return l; + } + function p(a, e) { + if (73 === e[12] && 72 === e[13] && 68 === e[14] && 82 === e[15]) { + a.width = e[16] << 24 | e[17] << 16 | e[18] << 8 | e[19]; + a.height = e[20] << 24 | e[21] << 16 | e[22] << 8 | e[23]; + var c = e[26]; + a.hasAlpha = 4 === c || 6 === c; + } + } + function u(a) { + for (var e = 0, c = 0;c < a.length;c++) { + e += a[c].length; + } + for (var e = new Uint8Array(e), l = 0, c = 0;c < a.length;c++) { + var k = a[c]; + e.set(k, l); + l += k.length; + } + return e; + } + var l = c.Debug.assert, e = c.ArrayUtilities.Inflate; + a.parseJpegChunks = v; + a.parsePngHeaders = p; + a.defineImage = function(a) { + h.enterTimeline("defineImage"); + var t = {type:"image", id:a.id, mimeType:a.mimeType}, q = a.imgData; + if ("image/jpeg" === a.mimeType) { + var n = a.alphaData; + if (n) { + a = new c.JPEG.JpegImage; + a.parse(u(v(t, q, []))); + l(t.width === a.width); + l(t.height === a.height); + var q = t.width * t.height, n = e.inflate(n, q, !0), k = t.data = new Uint8ClampedArray(4 * q); + a.copyToImageData(t); + a = 0; + for (var f = 3;a < q;a++, f += 4) { + k[f] = n[a]; } - __extends(c, b); - c.interpolate = function(a, b, c) { - m("public flash.geom.Matrix3D::static interpolate"); - }; - Object.defineProperty(c.prototype, "rawData", {get:function() { - var a = new g.Float64Vector; - this.copyRawDataTo(a); - return a; - }, set:function(a) { - this.copyRawDataFrom(a); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "position", {get:function() { - var a = this._matrix; - return new f.geom.Vector3D(a[12], a[13], a[14]); - }, set:function(a) { - var b = this._matrix; - b[12] = a.x; - b[13] = a.y; - b[14] = a.z; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "determinant", {get:function() { - var a = this._matrix, b = a[4], c = a[8], d = a[12], f = a[5], n = a[9], h = a[13], g = a[6], p = a[10], m = a[14], k = a[7], s = a[11], t = a[15]; - return a[0] * (f * (p * t - s * m) - g * (n * t - s * h) + k * (n * m - p * h)) - a[1] * (b * (p * t - s * m) - g * (c * t - s * d) + k * (c * m - p * d)) + a[2] * (b * (n * t - s * h) - f * (c * t - s * d) + k * (c * h - n * d)) - a[3] * (b * (n * m - p * h) - f * (c * m - p * d) + g * (c * h - n * d)); - }, enumerable:!0, configurable:!0}); - c.prototype.clone = function() { - return new f.geom.Matrix3D(this._matrix); - }; - c.prototype.copyToMatrix3D = function(a) { - a._matrix.set(this._matrix); - }; - c.prototype.append = function(a) { - var b = a._matrix, c = this._matrix; - a = this._matrix; - var d = b[0], f = b[4], n = b[8], h = b[12], g = b[1], p = b[5], m = b[9], k = b[13], s = b[2], t = b[6], O = b[10], K = b[14], F = b[3], J = b[7], A = b[11], b = b[15], B = c[0], z = c[4], P = c[8], D = c[12], L = c[1], M = c[5], V = c[9], Q = c[13], U = c[2], S = c[6], aa = c[10], $ = c[14], ea = c[3], Z = c[7], v = c[11], c = c[15]; - a[0] = d * B + f * L + n * U + h * ea; - a[1] = g * B + p * L + m * U + k * ea; - a[2] = s * B + t * L + O * U + K * ea; - a[3] = F * B + J * L + A * U + b * ea; - a[4] = d * z + f * M + n * S + h * Z; - a[5] = g * z + p * M + m * S + k * Z; - a[6] = s * z + t * M + O * S + K * Z; - a[7] = F * z + J * M + A * S + b * Z; - a[8] = d * P + f * V + n * aa + h * v; - a[9] = g * P + p * V + m * aa + k * v; - a[10] = s * P + t * V + O * aa + K * v; - a[11] = F * P + J * V + A * aa + b * v; - a[12] = d * D + f * Q + n * $ + h * c; - a[13] = g * D + p * Q + m * $ + k * c; - a[14] = s * D + t * Q + O * $ + K * c; - a[15] = F * D + J * Q + A * $ + b * c; - }; - c.prototype.prepend = function(a) { - var b = this._matrix, c = a._matrix; - a = this._matrix; - var d = b[0], f = b[4], n = b[8], h = b[12], g = b[1], p = b[5], m = b[9], k = b[13], s = b[2], t = b[6], O = b[10], K = b[14], F = b[3], J = b[7], A = b[11], b = b[15], B = c[0], z = c[4], P = c[8], D = c[12], L = c[1], M = c[5], V = c[9], Q = c[13], U = c[2], S = c[6], aa = c[10], $ = c[14], ea = c[3], Z = c[7], v = c[11], c = c[15]; - a[0] = d * B + f * L + n * U + h * ea; - a[1] = g * B + p * L + m * U + k * ea; - a[2] = s * B + t * L + O * U + K * ea; - a[3] = F * B + J * L + A * U + b * ea; - a[4] = d * z + f * M + n * S + h * Z; - a[5] = g * z + p * M + m * S + k * Z; - a[6] = s * z + t * M + O * S + K * Z; - a[7] = F * z + J * M + A * S + b * Z; - a[8] = d * P + f * V + n * aa + h * v; - a[9] = g * P + p * V + m * aa + k * v; - a[10] = s * P + t * V + O * aa + K * v; - a[11] = F * P + J * V + A * aa + b * v; - a[12] = d * D + f * Q + n * $ + h * c; - a[13] = g * D + p * Q + m * $ + k * c; - a[14] = s * D + t * Q + O * $ + K * c; - a[15] = F * D + J * Q + A * $ + b * c; - }; - c.prototype.invert = function() { - var a = this.determinant; - if (1E-7 > Math.abs(a)) { - return!1; - } - var a = 1 / a, b = this._matrix, c = b[0], d = b[1], f = b[2], n = b[3], h = b[4], g = b[5], p = b[6], m = b[7], k = b[8], s = b[9], t = b[10], O = b[11], K = b[12], F = b[13], J = b[14], A = b[15]; - b[0] = a * (g * (t * A - J * O) - s * (p * A - J * m) + F * (p * O - t * m)); - b[1] = -a * (d * (t * A - J * O) - s * (f * A - J * n) + F * (f * O - t * n)); - b[2] = a * (d * (p * A - J * m) - g * (f * A - J * n) + F * (f * m - p * n)); - b[3] = -a * (d * (p * O - t * m) - g * (f * O - t * n) + s * (f * m - p * n)); - b[4] = -a * (h * (t * A - J * O) - k * (p * A - J * m) + K * (p * O - t * m)); - b[5] = a * (c * (t * A - J * O) - k * (f * A - J * n) + K * (f * O - t * n)); - b[6] = -a * (c * (p * A - J * m) - h * (f * A - J * n) + K * (f * m - p * n)); - b[7] = a * (c * (p * O - t * m) - h * (f * O - t * n) + k * (f * m - p * n)); - b[8] = a * (h * (s * A - F * O) - k * (g * A - F * m) + K * (g * O - s * m)); - b[9] = -a * (c * (s * A - F * O) - k * (d * A - F * n) + K * (d * O - s * n)); - b[10] = a * (c * (g * A - F * m) - h * (d * A - F * n) + K * (d * m - g * n)); - b[11] = -a * (c * (g * O - s * m) - h * (d * O - s * n) + k * (d * m - g * n)); - b[12] = -a * (h * (s * J - F * t) - k * (g * J - F * p) + K * (g * t - s * p)); - b[13] = a * (c * (s * J - F * t) - k * (d * J - F * f) + K * (d * t - s * f)); - b[14] = -a * (c * (g * J - F * p) - h * (d * J - F * f) + K * (d * p - g * f)); - b[15] = a * (c * (g * t - s * p) - h * (d * t - s * f) + k * (d * p - g * f)); - return!0; - }; - c.prototype.identity = function() { - var a = this._matrix; - a[0] = a[5] = a[10] = a[15] = 1; - a[1] = a[2] = a[3] = a[4] = a[6] = a[7] = a[8] = a[9] = a[11] = a[12] = a[13] = a[14] = 0; - }; - c.prototype.decompose = function(a) { - "undefined" === typeof a && (a = "eulerAngles"); - d(a); - m("public flash.geom.Matrix3D::decompose"); - }; - c.prototype.recompose = function(a, b) { - "undefined" === typeof b && (b = "eulerAngles"); - d(b); - m("public flash.geom.Matrix3D::recompose"); - }; - c.prototype.appendTranslation = function(a, b, c) { - a = +a; - b = +b; - c = +c; - var d = this._matrix, f = d[3], n = d[7], h = d[11], g = d[15]; - d[0] += a * f; - d[1] += b * f; - d[2] += c * f; - d[4] += a * n; - d[5] += b * n; - d[6] += c * n; - d[8] += a * h; - d[9] += b * h; - d[10] += c * h; - d[12] += a * g; - d[13] += b * g; - d[14] += c * g; - }; - c.prototype.appendRotation = function(a, b, c) { - "undefined" === typeof c && (c = null); - this.append(s(+a / 180 * Math.PI, b.x, b.y, b.z, c ? c.x : 0, c ? c.y : 0, c ? c.z : 0)); - }; - c.prototype.appendScale = function(a, b, c) { - a = +a; - b = +b; - c = +c; - var d = this._matrix; - d[0] *= a; - d[1] *= b; - d[2] *= c; - d[4] *= a; - d[5] *= b; - d[6] *= c; - d[8] *= a; - d[9] *= b; - d[10] *= c; - d[12] *= a; - d[13] *= b; - d[14] *= c; - }; - c.prototype.prependTranslation = function(a, b, c) { - a = +a; - b = +b; - c = +c; - var d = this._matrix, f = d[1], n = d[5], h = d[9], g = d[2], p = d[6], m = d[10], k = d[3], s = d[7], t = d[11]; - d[12] += d[0] * a + d[4] * b + d[8] * c; - d[13] += f * a + n * b + h * c; - d[14] += g * a + p * b + m * c; - d[15] += k * a + s * b + t * c; - }; - c.prototype.prependRotation = function(a, b, c) { - "undefined" === typeof c && (c = null); - this.prepend(s(+a / 180 * Math.PI, b.x, b.y, b.z, c ? c.x : 0, c ? c.y : 0, c ? c.z : 0)); - }; - c.prototype.prependScale = function(a, b, c) { - a = +a; - b = +b; - c = +c; - var d = this._matrix; - d[0] *= a; - d[1] *= a; - d[2] *= a; - d[3] *= a; - d[4] *= b; - d[5] *= b; - d[6] *= b; - d[7] *= b; - d[8] *= c; - d[9] *= c; - d[10] *= c; - d[11] *= c; - }; - c.prototype.transformVector = function(a) { - var b = this._matrix, c = a.x, d = a.y; - a = a.z; - return new f.geom.Vector3D(b[0] * c + b[4] * d + b[8] * a + b[12], b[1] * c + b[5] * d + b[9] * a + b[13], b[2] * c + b[6] * d + b[10] * a + b[14]); - }; - c.prototype.deltaTransformVector = function(a) { - var b = this._matrix, c = a.x, d = a.y; - a = a.z; - return new f.geom.Vector3D(b[0] * c + b[4] * d + b[8] * a, b[1] * c + b[5] * d + b[9] * a, b[2] * c + b[6] * d + b[10] * a); - }; - c.prototype.transformVectors = function(a, b) { - for (var c = this._matrix, d = c[0], f = c[4], n = c[8], h = c[12], g = c[1], p = c[5], m = c[9], k = c[13], s = c[2], t = c[6], O = c[10], c = c[14], K = 0;K < a.length - 2;K += 3) { - var F = a.asGetNumericProperty(K), J = a.asGetNumericProperty(K + 1), A = a.asGetNumericProperty(K + 2); - b.push(d * F + f * J + n * A + h); - b.push(g * F + p * J + m * A + k); - b.push(s * F + t * J + O * A + c); + t.mimeType = "application/octet-stream"; + t.dataType = 3; + } else { + n = [], a.jpegTables && n.push(a.jpegTables), v(t, q, n), a.jpegTables && 0 < a.jpegTables.byteLength && (n[1] = n[1].subarray(2)), t.data = u(n), t.dataType = 4; + } + } else { + p(t, q), t.data = q, t.dataType = 5; + } + h.leaveTimeline(); + return t; + }; + })(h.Parser || (h.Parser = {})); + })(c.SWF || (c.SWF = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + (function(a) { + a.defineLabel = function(a) { + return{type:"label", id:a.id, fillBounds:a.bbox, matrix:a.matrix, tag:{hasText:!0, initialText:"", html:!0, readonly:!0}, records:a.records, coords:null, static:!0, require:null}; + }; + })(c.Parser || (c.Parser = {})); + })(c.SWF || (c.SWF = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + function h(b, a, d, g) { + if (b) { + if (a.fill0) { + if (g = g[a.fill0 - 1], a.fill1 || a.line) { + g.addSegment(b.toReversed()); + } else { + b.isReversed = !0; + return; + } + } + a.line && a.fill1 && (g = d[a.line - 1], g.addSegment(b.clone())); + } + } + function v(b, a, d, e) { + d && (b.morph = p(b, a, e)); + if (a) { + b.miterLimit = 2 * (b.miterLimitFactor || 1.5); + if (!b.color && b.hasFill) { + var f = v(b.fillStyle, !1, !1, e); + b.type = f.type; + b.transform = f.transform; + b.colors = f.colors; + b.ratios = f.ratios; + b.focalPoint = f.focalPoint; + b.bitmapId = f.bitmapId; + b.bitmapIndex = f.bitmapIndex; + b.repeat = f.repeat; + b.fillStyle = null; + } else { + b.type = 0; + } + return b; + } + if (void 0 === b.type || 0 === b.type) { + return b; + } + switch(b.type) { + case 16: + ; + case 18: + ; + case 19: + f = b.records; + a = b.colors = []; + d = b.ratios = []; + for (e = 0;e < f.length;e++) { + var k = f[e]; + a.push(k.color); + d.push(k.ratio); + } + f = 819.2; + break; + case 64: + ; + case 65: + ; + case 66: + ; + case 67: + b.smooth = 66 !== b.type && 67 !== b.type; + b.repeat = 65 !== b.type && 67 !== b.type; + b.bitmapIndex = e.length; + e.push(b.bitmapId); + f = .05; + break; + default: + c.Debug.warning("shape parser encountered invalid fill style " + b.type); + } + if (!b.matrix) { + return b.transform = g, b; + } + a = b.matrix; + b.transform = {a:a.a * f, b:a.b * f, c:a.c * f, d:a.d * f, tx:a.tx / 20, ty:a.ty / 20}; + b.matrix = null; + return b; + } + function p(b, a, d) { + var e = Object.create(b); + if (a) { + return e.width = b.widthMorph, !b.color && b.hasFill ? (b = p(b.fillStyle, !1, d), e.transform = b.transform, e.colors = b.colors, e.ratios = b.ratios) : e.color = b.colorMorph, e; + } + if (void 0 === b.type) { + return e; + } + if (0 === b.type) { + return e.color = b.colorMorph, e; + } + var k; + switch(b.type) { + case 16: + ; + case 18: + ; + case 19: + k = b.records; + a = e.colors = []; + d = e.ratios = []; + for (var c = 0;c < k.length;c++) { + var m = k[c]; + a.push(m.colorMorph); + d.push(m.ratioMorph); + } + k = 819.2; + break; + case 64: + ; + case 65: + ; + case 66: + ; + case 67: + k = .05; + break; + default: + f("shape parser encountered invalid fill style"); + } + if (!b.matrix) { + return e.transform = g, e; + } + b = b.matrixMorph; + e.transform = {a:b.a * k, b:b.b * k, c:b.c * k, d:b.d * k, tx:b.tx / 20, ty:b.ty / 20}; + return e; + } + function u(b, a, d, g) { + for (var e = [], f = 0;f < b.length;f++) { + var k = v(b[f], a, d, g); + e[f] = a ? new w(null, k) : new w(k, null); + } + return e; + } + function l(b, a) { + var d = b.noHscale ? b.noVscale ? 0 : 2 : b.noVscale ? 3 : 1, g = n(b.width, 0, 5100) | 0; + a.lineStyle(g, b.color, b.pixelHinting, d, b.endCapsStyle, b.jointStyle, b.miterLimit); + } + function e(b, a) { + var d = n(b.width, 0, 5100) | 0; + a.writeMorphLineStyle(d, b.color); + } + function m(b, a, d) { + d.beginGradient(b, a.colors, a.ratios, 16 === a.type ? 16 : 18, a.transform, a.spreadMethod, a.interpolationMode, a.focalPoint / 2 | 0); + } + var t = c.ArrayUtilities.DataBuffer, q = c.ShapeData, n = c.NumberUtilities.clamp, k = c.Debug.assert, f = c.Debug.assertUnreachable, d = Array.prototype.push, b; + (function(b) { + b[b.Solid = 0] = "Solid"; + b[b.LinearGradient = 16] = "LinearGradient"; + b[b.RadialGradient = 18] = "RadialGradient"; + b[b.FocalRadialGradient = 19] = "FocalRadialGradient"; + b[b.RepeatingBitmap = 64] = "RepeatingBitmap"; + b[b.ClippedBitmap = 65] = "ClippedBitmap"; + b[b.NonsmoothedRepeatingBitmap = 66] = "NonsmoothedRepeatingBitmap"; + b[b.NonsmoothedClippedBitmap = 67] = "NonsmoothedClippedBitmap"; + })(b || (b = {})); + var g = {a:1, b:0, c:0, d:1, tx:0, ty:0}; + a.defineShape = function(b) { + for (var a = [], g = u(b.fillStyles, !1, !!b.recordsMorph, a), e = u(b.lineStyles, !0, !!b.recordsMorph, a), f = b.records, c = e, m = b.recordsMorph || null, e = null !== m, l = {fill0:0, fill1:0, line:0}, n = null, p, J, C = f.length - 1, E = 0, F = 0, I = 0, G = 0, Z, Q = 0, S = 0;Q < C;Q++) { + var O = f[Q], P; + e && (P = m[S++]); + if (0 === O.type) { + n && h(n, l, c, g), O.hasNewStyles && (p || (p = []), d.apply(p, g), g = u(O.fillStyles, !1, e, a), d.apply(p, c), c = u(O.lineStyles, !0, e, a), J && (p.push(J), J = null), l = {fill0:0, fill1:0, line:0}), O.hasFillStyle0 && (l.fill0 = O.fillStyle0), O.hasFillStyle1 && (l.fill1 = O.fillStyle1), O.hasLineStyle && (l.line = O.lineStyle), l.fill1 ? Z = g[l.fill1 - 1] : l.line ? Z = c[l.line - 1] : l.fill0 && (Z = g[l.fill0 - 1]), O.move && (E = O.moveX | 0, F = O.moveY | 0), Z && (n = r.FromDefaults(e), + Z.addSegment(n), e ? (0 === P.type ? (I = P.moveX | 0, G = P.moveY | 0) : (I = E, G = F, S--), n.morphMoveTo(E, F, I, G)) : n.moveTo(E, F)); + } else { + k(1 === O.type); + n || (J || (J = new w(null, v({color:{red:0, green:0, blue:0, alpha:0}, width:20}, !0, e, a))), n = r.FromDefaults(e), J.addSegment(n), e ? n.morphMoveTo(E, F, I, G) : n.moveTo(E, F)); + if (e) { + for (;P && 0 === P.type;) { + P = m[S++]; } - }; - c.prototype.transpose = function() { - var a = this._matrix, b; - b = a[1]; - a[1] = a[4]; - a[4] = b; - b = a[2]; - a[2] = a[8]; - a[5] = b; - b = a[3]; - a[3] = a[12]; - a[12] = b; - b = a[6]; - a[6] = a[9]; - a[9] = b; - b = a[7]; - a[7] = a[13]; - a[13] = b; - b = a[11]; - a[11] = a[14]; - a[14] = b; - }; - c.prototype.pointAt = function(a, b, c) { - m("public flash.geom.Matrix3D::pointAt"); - }; - c.prototype.interpolateTo = function(a, b) { - m("public flash.geom.Matrix3D::interpolateTo"); - }; - c.prototype.copyFrom = function(a) { - this._matrix.set(a._matrix); - }; - c.prototype.copyRawDataTo = function(b) { - var c = 0, d = !1; - "undefined" === typeof c && (c = 0); - "undefined" === typeof d && (d = !1); - var c = c >>> 0, f = this._matrix; - if (d) { - for (d = 0, c |= 0;16 > d;d++, c++) { - b.asSetNumericProperty(c, f[a[d]]); + P || (P = O); + } + if (!O.isStraight || e && !P.isStraight) { + var V, $, W; + O.isStraight ? (W = O.deltaX | 0, O = O.deltaY | 0, V = E + (W >> 1), $ = F + (O >> 1), E += W, F += O) : (V = E + O.controlDeltaX | 0, $ = F + O.controlDeltaY | 0, E = V + O.anchorDeltaX | 0, F = $ + O.anchorDeltaY | 0); + if (e) { + if (P.isStraight) { + W = P.deltaX | 0, O = P.deltaY | 0, x = I + (W >> 1), ea = G + (O >> 1), I += W, G += O; + } else { + var x = I + P.controlDeltaX | 0, ea = G + P.controlDeltaY | 0, I = x + P.anchorDeltaX | 0, G = ea + P.anchorDeltaY | 0 } + n.morphCurveTo(V, $, E, F, x, ea, I, G); } else { - for (d = 0, c |= 0;16 > d;d++, c++) { - b.asSetNumericProperty(c, f[d]); - } + n.curveTo(V, $, E, F); } - }; - c.prototype.copyRawDataFrom = function(b) { - var c = 0, d = !1; - "undefined" === typeof c && (c = 0); - "undefined" === typeof d && (d = !1); - var c = c >>> 0, f = this._matrix; - if (d) { - for (d = 0, c |= 0;16 > d;d++, c++) { - f[a[d]] = b.asGetNumericProperty(c) || 0; - } - } else { - for (d = 0, c |= 0;16 > d;d++, c++) { - f[d] = b.asGetNumericProperty(c) || 0; - } + } else { + E += O.deltaX | 0, F += O.deltaY | 0, e ? (I += P.deltaX | 0, G += P.deltaY | 0, n.morphLineTo(E, F, I, G)) : n.lineTo(E, F); + } + } + } + h(n, l, c, g); + p ? d.apply(p, g) : p = g; + d.apply(p, c); + J && p.push(J); + f = new q; + e && (f.morphCoordinates = new Int32Array(f.coordinates.length), f.morphStyles = new t(16)); + for (Q = 0;Q < p.length;Q++) { + p[Q].serialize(f); + } + return{type:b.isMorph ? "morphshape" : "shape", id:b.id, fillBounds:b.fillBounds, lineBounds:b.lineBounds, morphFillBounds:b.fillBoundsMorph || null, morphLineBounds:b.lineBoundsMorph || null, shape:f.toPlainObject(), transferables:f.buffers, require:a.length ? a : null}; + }; + var r = function() { + function b(a, d, g, e, f, k) { + this.commands = a; + this.data = d; + this.morphData = g; + this.prev = e; + this.next = f; + this.isReversed = k; + this.id = b._counter++; + } + b.FromDefaults = function(a) { + var d = new t, g = new t; + d.endian = g.endian = "auto"; + var e = null; + a && (e = new t, e.endian = "auto"); + return new b(d, g, e, null, null, !1); + }; + b.prototype.moveTo = function(b, a) { + this.commands.writeUnsignedByte(9); + this.data.write2Ints(b, a); + }; + b.prototype.morphMoveTo = function(b, a, d, g) { + this.moveTo(b, a); + this.morphData.write2Ints(d, g); + }; + b.prototype.lineTo = function(b, a) { + this.commands.writeUnsignedByte(10); + this.data.write2Ints(b, a); + }; + b.prototype.morphLineTo = function(b, a, d, g) { + this.lineTo(b, a); + this.morphData.write2Ints(d, g); + }; + b.prototype.curveTo = function(b, a, d, g) { + this.commands.writeUnsignedByte(11); + this.data.write4Ints(b, a, d, g); + }; + b.prototype.morphCurveTo = function(b, a, d, g, e, f, k, c) { + this.curveTo(b, a, d, g); + this.morphData.write4Ints(e, f, k, c); + }; + b.prototype.toReversed = function() { + k(!this.isReversed); + return new b(this.commands, this.data, this.morphData, null, null, !0); + }; + b.prototype.clone = function() { + return new b(this.commands, this.data, this.morphData, null, null, this.isReversed); + }; + b.prototype.storeStartAndEnd = function() { + var b = this.data.ints, a = b[0] + "," + b[1], d = (this.data.length >> 2) - 2, b = b[d] + "," + b[d + 1]; + this.isReversed ? (this.startPoint = b, this.endPoint = a) : (this.startPoint = a, this.endPoint = b); + }; + b.prototype.connectsTo = function(b) { + k(b !== this); + k(this.endPoint); + k(b.startPoint); + return this.endPoint === b.startPoint; + }; + b.prototype.startConnectsTo = function(b) { + k(b !== this); + return this.startPoint === b.startPoint; + }; + b.prototype.flipDirection = function() { + var b = "", b = this.startPoint; + this.startPoint = this.endPoint; + this.endPoint = b; + this.isReversed = !this.isReversed; + }; + b.prototype.serialize = function(b, a) { + if (this.isReversed) { + this._serializeReversed(b, a); + } else { + var d = this.commands.bytes, g = this.data.length >> 2, e = this.morphData ? this.morphData.ints : null, f = this.data.ints; + k(9 === d[0]); + var c = 0; + f[0] === a.x && f[1] === a.y && c++; + for (var m = this.commands.length, l = 2 * c;c < m;c++) { + l = this._writeCommand(d[c], l, f, e, b); + } + k(l === g); + a.x = f[g - 2]; + a.y = f[g - 1]; + } + }; + b.prototype._serializeReversed = function(b, a) { + var d = this.commands.length, g = (this.data.length >> 2) - 2, e = this.commands.bytes; + k(9 === e[0]); + var f = this.data.ints, c = this.morphData ? this.morphData.ints : null; + f[g] === a.x && f[g + 1] === a.y || this._writeCommand(9, g, f, c, b); + if (1 !== d) { + for (;1 < d--;) { + var g = g - 2, m = e[d]; + b.writeCommandAndCoordinates(m, f[g], f[g + 1]); + c && b.writeMorphCoordinates(c[g], c[g + 1]); + 11 === m && (g -= 2, b.writeCoordinates(f[g], f[g + 1]), c && b.writeMorphCoordinates(c[g], c[g + 1])); + } + k(0 === g); + } + a.x = f[0]; + a.y = f[1]; + }; + b.prototype._writeCommand = function(b, a, d, g, e) { + e.writeCommandAndCoordinates(b, d[a++], d[a++]); + g && e.writeMorphCoordinates(g[a - 2], g[a - 1]); + 11 === b && (e.writeCoordinates(d[a++], d[a++]), g && e.writeMorphCoordinates(g[a - 2], g[a - 1])); + return a; + }; + b._counter = 0; + return b; + }(), w = function() { + function b(a, d) { + this.fillStyle = a; + this.lineStyle = d; + this._head = null; + } + b.prototype.addSegment = function(b) { + k(b); + k(null === b.next); + k(null === b.prev); + var a = this._head; + a && (k(b !== a), a.next = b, b.prev = a); + this._head = b; + }; + b.prototype.removeSegment = function(b) { + b.prev && (b.prev.next = b.next); + b.next && (b.next.prev = b.prev); + }; + b.prototype.insertSegment = function(b, a) { + var d = a.prev; + b.prev = d; + b.next = a; + d && (d.next = b); + a.prev = b; + }; + b.prototype.head = function() { + return this._head; + }; + b.prototype.serialize = function(b) { + var a = this.head(); + if (a) { + for (;a;) { + a.storeStartAndEnd(), a = a.prev; + } + for (var d = this.head(), g = d, c = a = null, r = d.prev;d;) { + for (;r;) { + r.startConnectsTo(d) && r.flipDirection(), r.connectsTo(d) ? (r.next !== d && (this.removeSegment(r), this.insertSegment(r, d)), d = r, r = d.prev) : (r.startConnectsTo(g) && r.flipDirection(), g.connectsTo(r) ? (this.removeSegment(r), g.next = r, r = r.prev, g.next.prev = g, g.next.next = null, g = g.next) : r = r.prev); } - }; - c.prototype.copyRowTo = function(a, b) { - var c = a >>> 0 | 0, d = this._matrix; - b.x = d[c]; - b.y = d[c + 4]; - b.z = d[c + 8]; - b.w = d[c + 12]; - }; - c.prototype.copyColumnTo = function(a, b) { - var c = a >>> 0 << 2, d = this._matrix; - b.x = d[c]; - b.y = d[c + 1]; - b.z = d[c + 2]; - b.w = d[c + 3]; - }; - c.prototype.copyRowFrom = function(a, b) { - var c = a >>> 0 | 0, d = this._matrix; - d[c] = b.x; - d[c + 4] = b.y; - d[c + 8] = b.z; - d[c + 12] = b.w; - }; - c.prototype.copyColumnFrom = function(a, b) { - var c = a >>> 0 << 2, d = this._matrix; - d[c] = b.x; - d[c + 1] = b.y; - d[c + 2] = b.z; - d[c + 3] = b.w; - }; - c.classInitializer = null; - c.initializer = null; - c.classSymbols = null; - c.instanceSymbols = null; - return c; - }(g.ASNative); - k.Matrix3D = c; - })(f.geom || (f.geom = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.geom.Orientation3D"); + r = d.prev; + a ? (c.next = d, d.prev = c, c = g, c.next = null) : (a = d, c = g); + if (!r) { + break; + } + d = g = r; + r = d.prev; } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.EULER_ANGLES = "eulerAngles"; - a.AXIS_ANGLE = "axisAngle"; - a.QUATERNION = "quaternion"; - return a; - }(g.ASNative); - f.Orientation3D = m; - })(f.geom || (f.geom = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.geom.PerspectiveProjection"); + if (this.fillStyle) { + switch(r = this.fillStyle, d = r.morph, r.type) { + case 0: + b.beginFill(r.color); + d && b.writeMorphFill(d.color); + break; + case 16: + ; + case 18: + ; + case 19: + m(2, r, b); + d && b.writeMorphGradient(d.colors, d.ratios, d.transform); + break; + case 65: + ; + case 64: + ; + case 67: + ; + case 66: + b.beginBitmap(3, r.bitmapIndex, r.transform, r.repeat, r.smooth); + d && b.writeMorphBitmap(d.transform); + break; + default: + f("Invalid fill style type: " + r.type); + } + } else { + switch(r = this.lineStyle, d = r.morph, k(r), r.type) { + case 0: + l(r, b); + d && e(d, b); + break; + case 16: + ; + case 18: + ; + case 19: + l(r, b); + m(6, r, b); + d && (e(d, b), b.writeMorphGradient(d.colors, d.ratios, d.transform)); + break; + case 65: + ; + case 64: + ; + case 67: + ; + case 66: + l(r, b), b.beginBitmap(7, r.bitmapIndex, r.transform, r.repeat, r.smooth), d && (e(d, b), b.writeMorphBitmap(d.transform)); + } } - __extends(a, b); - Object.defineProperty(a.prototype, "fieldOfView", {get:function() { - k("public flash.geom.PerspectiveProjection::get fieldOfView"); - }, set:function(a) { - k("public flash.geom.PerspectiveProjection::set fieldOfView"); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "projectionCenter", {get:function() { - k("public flash.geom.PerspectiveProjection::get projectionCenter"); - }, set:function(a) { - k("public flash.geom.PerspectiveProjection::set projectionCenter"); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "focalLength", {get:function() { - k("public flash.geom.PerspectiveProjection::get focalLength"); - }, set:function(a) { - k("public flash.geom.PerspectiveProjection::set focalLength"); - }, enumerable:!0, configurable:!0}); - a.prototype.toMatrix3D = function() { - k("public flash.geom.PerspectiveProjection::toMatrix3D"); - }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.PerspectiveProjection = m; - })(f.geom || (f.geom = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + d = {x:0, y:0}; + for (r = a;r;) { + r.serialize(b, d), r = r.next; + } + this.fillStyle ? b.endFill() : b.endLine(); + return b; + } + }; + return b; + }(); + })(h.Parser || (h.Parser = {})); + })(c.SWF || (c.SWF = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { - function d(a, b) { - "undefined" === typeof a && (a = 0); - "undefined" === typeof b && (b = 0); - this.x = +a; - this.y = +b; +(function(c) { + (function(h) { + (function(a) { + function h(a, b, g, e, f) { + var k = e >> 3, c = g * b * k, k = g * k, m = a.length + (a.length & 1), l = new ArrayBuffer(t.length + m), n = new Uint8Array(l); + n.set(t); + if (f) { + f = 0; + for (var q = t.length;f < a.length;f += 2, q += 2) { + n[q] = a[f + 1], n[q + 1] = a[f]; + } + } else { + n.set(a, t.length); + } + a = new DataView(l); + a.setUint32(4, m + 36, !0); + a.setUint16(22, g, !0); + a.setUint32(24, b, !0); + a.setUint32(28, c, !0); + a.setUint16(32, k, !0); + a.setUint16(34, e, !0); + a.setUint32(40, m, !0); + return{data:n, mimeType:"audio/wav"}; + } + function v(a, b, g) { + function e(b) { + for (;c < b;) { + k = k << 8 | a[f++], c += 8; + } + c -= b; + return k >>> c & (1 << b) - 1; + } + for (var f = 0, k = 0, c = 0, m = 0, l = e(2), t = q[l];m < b.length;) { + var h = b[m++] = e(16) << 16 >> 16, s, p = e(6), v; + 1 < g && (s = b[m++] = e(16) << 16 >> 16, v = e(6)); + for (var u = 1 << l + 1, J = 0;4095 > J;J++) { + for (var C = e(l + 2), E = n[p], F = 0, I = u >> 1;I;I >>= 1, E >>= 1) { + C & I && (F += E); + } + h += (C & u ? -1 : 1) * (F + E); + b[m++] = h = -32768 > h ? -32768 : 32767 < h ? 32767 : h; + p += t[C & ~u]; + p = 0 > p ? 0 : 88 < p ? 88 : p; + if (1 < g) { + C = e(l + 2); + E = n[v]; + F = 0; + for (I = u >> 1;I;I >>= 1, E >>= 1) { + C & I && (F += E); + } + s += (C & u ? -1 : 1) * (F + E); + b[m++] = s = -32768 > s ? -32768 : 32767 < s ? 32767 : s; + v += t[C & ~u]; + v = 0 > v ? 0 : 88 < v ? 88 : v; } - __extends(d, b); - Object.defineProperty(d.prototype, "native_x", {get:function() { - return this.x; - }, set:function(a) { - this.x = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "native_y", {get:function() { - return this.y; - }, set:function(a) { - this.y = a; - }, enumerable:!0, configurable:!0}); - d.prototype.Point = function(a, b) { - "undefined" === typeof a && (a = 0); - "undefined" === typeof b && (b = 0); - this.x = a; - this.y = b; - }; - Object.defineProperty(d.prototype, "length", {get:function() { - return Math.sqrt(this.x * this.x + this.y * this.y); - }, enumerable:!0, configurable:!0}); - d.interpolate = function(a, b, f) { - var g = 1 - f; - return new d(a.x * f + b.x * g, a.y * f + b.y * g); - }; - d.distance = function(a, b) { - var d = b.x - a.x, f = b.y - a.y; - return 0 === d ? Math.abs(f) : 0 === f ? Math.abs(d) : Math.sqrt(d * d + f * f); - }; - d.polar = function(a, b) { - a = +a; - b = +b; - return new d(a * Math.cos(b), a * Math.sin(b)); - }; - d.prototype.clone = function() { - return new d(this.x, this.y); - }; - d.prototype.offset = function(a, b) { - this.x += +a; - this.y += +b; - }; - d.prototype.equals = function(a) { - return this.x === a.x && this.y === a.y; - }; - d.prototype.subtract = function(a) { - return new d(this.x - a.x, this.y - a.y); - }; - d.prototype.add = function(a) { - return new d(this.x + a.x, this.y + a.y); - }; - d.prototype.normalize = function(a) { - if (0 !== this.x || 0 !== this.y) { - a = +a / this.length, this.x *= a, this.y *= a; + } + } + } + function p(a) { + for (var b = new Float32Array(a.length), g = 0;g < b.length;g++) { + b[g] = (a[g] - 128) / 128; + } + this.currentSample += b.length / this.channels; + return{streamId:this.streamId, samplesCount:b.length / this.channels, pcm:b}; + } + function u(a) { + for (var b = new Float32Array(a.length / 2), g = 0, e = 0;g < b.length;g++, e += 2) { + b[g] = (a[e] << 24 | a[e + 1] << 16) / 2147483648; + } + this.currentSample += b.length / this.channels; + return{streamId:this.streamId, samplesCount:b.length / this.channels, pcm:b}; + } + function l(a) { + for (var b = new Float32Array(a.length / 2), g = 0, e = 0;g < b.length;g++, e += 2) { + b[g] = (a[e + 1] << 24 | a[e] << 16) / 2147483648; + } + this.currentSample += b.length / this.channels; + return{streamId:this.streamId, samplesCount:b.length / this.channels, pcm:b}; + } + function e(a) { + var b = a[1] << 8 | a[0], g = a[3] << 8 | a[2]; + this.currentSample += b; + return{streamId:this.streamId, samplesCount:b, data:new Uint8Array(a.subarray(4)), seek:g}; + } + var m = [5512, 11250, 22500, 44100], t = new Uint8Array([82, 73, 70, 70, 0, 0, 0, 0, 87, 65, 86, 69, 102, 109, 116, 32, 16, 0, 0, 0, 1, 0, 2, 0, 68, 172, 0, 0, 16, 177, 2, 0, 4, 0, 16, 0, 100, 97, 116, 97, 0, 0, 0, 0]); + a.defineSound = function(a) { + var b = 1 == a.soundType ? 2 : 1, g = a.samplesCount, e = m[a.soundRate], f = a.soundData, k; + switch(a.soundFormat) { + case 0: + k = new Float32Array(g * b); + if (1 == a.soundSize) { + for (var c = g = 0;g < k.length;g++, c += 2) { + k[g] = (f[c] << 24 | f[c + 1] << 16) / 2147483648; } - }; - d.prototype.copyFrom = function(a) { - this.x = a.x; - this.y = a.y; - }; - d.prototype.setTo = function(a, b) { - this.x = +a; - this.y = +b; - }; - d.prototype.toTwips = function() { - this.x = 20 * this.x | 0; - this.y = 20 * this.y | 0; - return this; - }; - d.prototype.toPixels = function() { - this.x /= 20; - this.y /= 20; - return this; - }; - d.prototype.round = function() { - this.x = Math.round(this.x); - this.y = Math.round(this.y); - return this; - }; - d.prototype.toString = function() { - return "(x=" + this.x + ", y=" + this.y + ")"; - }; - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - return d; - }(b.ASNative); - f.Point = k; - })(f.geom || (f.geom = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { - function d(a, b, d, f) { - "undefined" === typeof a && (a = 0); - "undefined" === typeof b && (b = 0); - "undefined" === typeof d && (d = 0); - "undefined" === typeof f && (f = 0); - this.x = +a; - this.y = +b; - this.width = +d; - this.height = +f; - } - __extends(d, b); - d.FromBounds = function(a) { - var b = a.xMin, f = a.yMin; - return new d(b / 20, f / 20, (a.xMax - b) / 20, (a.yMax - f) / 20); - }; - Object.defineProperty(d.prototype, "native_x", {get:function() { - return this.x; - }, set:function(a) { - this.x = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "native_y", {get:function() { - return this.y; - }, set:function(a) { - this.y = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "native_width", {get:function() { - return this.width; - }, set:function(a) { - this.width = +a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "native_height", {get:function() { - return this.height; - }, set:function(a) { - this.height = +a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "left", {get:function() { - return this.x; - }, set:function(a) { - a = +a; - this.width += this.x - a; - this.x = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "right", {get:function() { - return this.x + this.width; - }, set:function(a) { - this.width = +a - this.x; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "top", {get:function() { - return this.y; - }, set:function(a) { - a = +a; - this.height += this.y - a; - this.y = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "bottom", {get:function() { - return this.y + this.height; - }, set:function(a) { - this.height = +a - this.y; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "topLeft", {get:function() { - return new f.Point(this.left, this.top); - }, set:function(a) { - this.top = a.y; - this.left = a.x; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "bottomRight", {get:function() { - return new f.Point(this.right, this.bottom); - }, set:function(a) { - this.bottom = a.y; - this.right = a.x; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "size", {get:function() { - return new f.Point(this.width, this.height); - }, set:function(a) { - this.width = a.x; - this.height = a.y; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "area", {get:function() { - return this.width * this.height; - }, enumerable:!0, configurable:!0}); - d.prototype.clone = function() { - return new d(this.x, this.y, this.width, this.height); - }; - d.prototype.isEmpty = function() { - return 0 >= this.width || 0 >= this.height; - }; - d.prototype.setEmpty = function() { - this.height = this.width = this.y = this.x = 0; - }; - d.prototype.inflate = function(a, b) { - a = +a; - b = +b; - this.x -= a; - this.y -= b; - this.width += 2 * a; - this.height += 2 * b; - }; - d.prototype.inflatePoint = function(a) { - this.inflate(a.x, a.y); - }; - d.prototype.offset = function(a, b) { - this.x += +a; - this.y += +b; - }; - d.prototype.offsetPoint = function(a) { - this.offset(a.x, a.y); - }; - d.prototype.contains = function(a, b) { - a = +a; - b = +b; - return a >= this.x && a < this.right && b >= this.y && b < this.bottom; - }; - d.prototype.containsPoint = function(a) { - return this.contains(a.x, a.y); - }; - d.prototype.containsRect = function(a) { - var b = a.x + a.width, d = a.y + a.height, f = this.x + this.width, e = this.y + this.height; - return a.x >= this.x && a.x < f && a.y >= this.y && a.y < e && b > this.x && b <= f && d > this.y && d <= e; - }; - d.prototype.intersection = function(a) { - return this.clone().intersectInPlace(a); - }; - d.prototype.intersects = function(a) { - return Math.max(this.x, a.x) <= Math.min(this.right, a.right) && Math.max(this.y, a.y) <= Math.min(this.bottom, a.bottom); - }; - d.prototype.intersectInPlace = function(a) { - var b = this.x, d = this.y, f = a.x, e = a.y, g = Math.max(b, f), b = Math.min(b + this.width, f + a.width); - if (g <= b && (f = Math.max(d, e), a = Math.min(d + this.height, e + a.height), f <= a)) { - return this.setTo(g, f, b - g, a - f), this; + f = h(f, e, b, 16, !0); + } else { + for (g = 0;g < k.length;g++) { + k[g] = (f[g] - 128) / 128; } - this.setEmpty(); - return this; - }; - d.prototype.intersectInPlaceInt32 = function(a) { - var b = this.x | 0, d = this.y | 0, f = this.width | 0, e = this.height | 0, g = a.x | 0, l = a.width | 0, m = Math.max(b, g) | 0, b = Math.min(b + f | 0, g + l | 0) | 0; - if (m <= b && (f = a.y | 0, g = a.height | 0, a = Math.max(d, f) | 0, d = Math.min(d + e | 0, f + g | 0), a <= d)) { - return this.setTo(m, a, b - m, d - a), this; + f = h(f, e, b, 8, !1); + } + break; + case 3: + k = new Float32Array(g * b); + if (1 == a.soundSize) { + for (c = g = 0;g < k.length;g++, c += 2) { + k[g] = (f[c + 1] << 24 | f[c] << 16) / 2147483648; } - this.setEmpty(); - return this; - }; - d.prototype.union = function(a) { - return this.clone().unionInPlace(a); - }; - d.prototype.unionInPlace = function(a) { - if (!a.isEmpty()) { - if (this.isEmpty()) { - this.copyFrom(a); - } else { - var b = Math.min(this.x, a.x), d = Math.min(this.y, a.y); - this.setTo(b, d, Math.max(this.right, a.right) - b, Math.max(this.bottom, a.bottom) - d); - return this; - } + f = h(f, e, b, 16, !1); + } else { + for (g = 0;g < k.length;g++) { + k[g] = (f[g] - 128) / 128; } - }; - d.prototype.equals = function(a) { - return this.x === a.x && this.y === a.y && this.width === a.width && this.height === a.height; - }; - d.prototype.copyFrom = function(a) { - this.x = a.x; - this.y = a.y; - this.width = a.width; - this.height = a.height; - }; - d.prototype.setTo = function(a, b, d, f) { - this.x = +a; - this.y = +b; - this.width = +d; - this.height = +f; - }; - d.prototype.toTwips = function() { - this.x = 20 * this.x | 0; - this.y = 20 * this.y | 0; - this.width = 20 * this.width | 0; - this.height = 20 * this.height | 0; - return this; - }; - d.prototype.getBaseWidth = function(a) { - return Math.abs(Math.cos(a)) * this.width + Math.abs(Math.sin(a)) * this.height; - }; - d.prototype.getBaseHeight = function(a) { - return Math.abs(Math.sin(a)) * this.width + Math.abs(Math.cos(a)) * this.height; - }; - d.prototype.toPixels = function() { - this.x /= 20; - this.y /= 20; - this.width /= 20; - this.height /= 20; - return this; - }; - d.prototype.snapInPlace = function() { - var a = Math.ceil(this.x + this.width), b = Math.ceil(this.y + this.height); - this.x = Math.floor(this.x); - this.y = Math.floor(this.y); - this.width = a - this.x; - this.height = b - this.y; - return this; - }; - d.prototype.roundInPlace = function() { - var a = Math.round(this.x + this.width), b = Math.round(this.y + this.height); - this.x = Math.round(this.x); - this.y = Math.round(this.y); - this.width = a - this.x; - this.height = b - this.y; - return this; - }; - d.prototype.toString = function() { - return "(x=" + this.x + ", y=" + this.y + ", w=" + this.width + ", h=" + this.height + ")"; - }; - d.prototype.writeExternal = function(a) { - a.writeFloat(this.x); - a.writeFloat(this.y); - a.writeFloat(this.width); - a.writeFloat(this.height); - }; - d.prototype.readExternal = function(a) { - this.x = a.readFloat(); - this.y = a.readFloat(); - this.width = a.readFloat(); - this.height = a.readFloat(); - }; - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - return d; - }(b.ASNative); - f.Rectangle = k; - })(f.geom || (f.geom = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(k) { - var s = b.Debug.notImplemented, m = b.Debug.somewhatImplemented, d = b.AVM2.Runtime.throwError, a = b.AVM2.Errors, c = function(b) { - function c(b) { - b || d("ArgumentError", a.NullPointerError, "displayObject"); - this._displayObject = b; + f = h(f, e, b, 8, !1); } - __extends(c, b); - Object.defineProperty(c.prototype, "matrix", {get:function() { - return this._displayObject._getMatrix().clone().toPixelsInPlace(); - }, set:function(a) { - this._displayObject._setMatrix(a, !0); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "colorTransform", {get:function() { - return this._displayObject._colorTransform.clone(); - }, set:function(a) { - this._displayObject._setColorTransform(a); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "concatenatedMatrix", {get:function() { - var a = this._displayObject._getConcatenatedMatrix().clone().toPixelsInPlace(); - this._displayObject._stage || a.scale(5, 5); - return a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "concatenatedColorTransform", {get:function() { - return this._displayObject._getConcatenatedColorTransform(); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "pixelBounds", {get:function() { - s("public flash.geom.Transform::get pixelBounds"); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "matrix3D", {get:function() { - var a = this._displayObject._matrix3D; - return a && a.clone(); - }, set:function(b) { - k.Matrix3D.isType(b) || d("TypeError", a.CheckTypeFailedError, b, "flash.geom.Matrix3D"); - b = b.rawData; - this.matrix = new f.geom.Matrix(b.asGetPublicProperty(0), b.asGetPublicProperty(1), b.asGetPublicProperty(4), b.asGetPublicProperty(5), b.asGetPublicProperty(12), b.asGetPublicProperty(13)); - m("public flash.geom.Transform::set matrix3D"); - }, enumerable:!0, configurable:!0}); - c.prototype.getRelativeMatrix3D = function(a) { - s("public flash.geom.Transform::getRelativeMatrix3D"); - }; - Object.defineProperty(c.prototype, "perspectiveProjection", {get:function() { - s("public flash.geom.Transform::get perspectiveProjection"); - }, set:function(a) { - s("public flash.geom.Transform::set perspectiveProjection"); - }, enumerable:!0, configurable:!0}); - c.classInitializer = null; - c.initializer = null; - c.classSymbols = null; - c.instanceSymbols = null; - return c; - }(g.ASNative); - k.Transform = c; - })(f.geom || (f.geom = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.geom.Utils3D"); + break; + case 2: + f = {data:new Uint8Array(f.subarray(2)), mimeType:"audio/mpeg"}; + break; + case 1: + c = new Int16Array(g * b); + v(f, c, b); + k = new Float32Array(g * b); + for (g = 0;g < k.length;g++) { + k[g] = c[g] / 32768; } - __extends(a, b); - a.projectVector = function(a, b) { - k("public flash.geom.Utils3D::static projectVector"); - }; - a.projectVectors = function(a, b, d, e) { - k("public flash.geom.Utils3D::static projectVectors"); - }; - a.pointTowards = function(a, b, d, e, f) { - k("public flash.geom.Utils3D::static pointTowards"); - }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.Utils3D = m; - })(f.geom || (f.geom = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + f = h(new Uint8Array(c.buffer), e, b, 16, !(new Uint8Array((new Uint16Array([1])).buffer))[0]); + break; + default: + throw Error("Unsupported audio format: " + a.soundFormat);; + } + a = {type:"sound", id:a.id, sampleRate:e, channels:b, pcm:k, packaged:null}; + f && (a.packaged = f); + return a; + }; + var q = [[-1, 2], [-1, -1, 2, 4], [-1, -1, -1, -1, 2, 4, 6, 8], [-1, -1, -1, -1, -1, -1, -1, -1, 1, 2, 4, 6, 8, 10, 13, 16]], n = [7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, + 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767], k = 0, f = function() { + function a(b, d, e) { + this.streamId = k++; + this.samplesCount = b; + this.sampleRate = d; + this.channels = e; + this.format = null; + this.currentSample = 0; + } + a.FromTag = function(b) { + var g = new a(b.samplesCount, m[b.streamRate], 1 == b.streamType ? 2 : 1); + switch(b.streamCompression) { + case 0: + ; + case 3: + g.format = "wave"; + g.decode = 1 == b.soundSize ? 0 === b.streamCompression ? u : l : p; + break; + case 2: + g.format = "mp3"; + g.decode = e; + break; + default: + return c.Debug.warning("Unsupported audio format: " + b.soundFormat), null; + } + return g; + }; + return a; + }(); + a.SoundStream = f; + })(h.Parser || (h.Parser = {})); + })(c.SWF || (c.SWF = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { - function d(a, b, d, f) { - "undefined" === typeof a && (a = 0); - "undefined" === typeof b && (b = 0); - "undefined" === typeof d && (d = 0); - "undefined" === typeof f && (f = 0); - this.x = +a; - this.y = +b; - this.z = +d; - this.w = +f; - } - __extends(d, b); - Object.defineProperty(d.prototype, "native_x", {get:function() { - return this.x; - }, set:function(a) { - this.x = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "native_y", {get:function() { - return this.y; - }, set:function(a) { - this.y = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "native_z", {get:function() { - return this.z; - }, set:function(a) { - this.z = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "native_w", {get:function() { - return this.w; - }, set:function(a) { - this.w = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "length", {get:function() { - return Math.sqrt(this.lengthSquared); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "lengthSquared", {get:function() { - return this.x * this.x + this.y * this.y + this.z * this.z; - }, enumerable:!0, configurable:!0}); - d.angleBetween = function(a, b) { - return Math.acos(a.dotProduct(b) / (a.length * b.length)); - }; - d.distance = function(a, b) { - return a.subtract(b).length; - }; - d.prototype.dotProduct = function(a) { - return this.x * a.x + this.y * a.y + this.z * a.z; - }; - d.prototype.crossProduct = function(a) { - return new d(this.y * a.z - this.z * a.y, this.z * a.x - this.x * a.z, this.x * a.y - this.y * a.x, 1); - }; - d.prototype.normalize = function() { - var a = this.length; - 0 !== a ? (this.x /= a, this.y /= a, this.z /= a) : this.x = this.y = this.z = 0; - return a; - }; - d.prototype.scaleBy = function(a) { - a = +a; - this.x *= a; - this.y *= a; - this.z *= a; - }; - d.prototype.incrementBy = function(a) { - this.x += a.x; - this.y += a.y; - this.z += a.z; - }; - d.prototype.decrementBy = function(a) { - this.x -= a.x; - this.y -= a.y; - this.z -= a.z; - }; - d.prototype.add = function(a) { - return new d(this.x + a.x, this.y + a.y, this.z + a.z); - }; - d.prototype.subtract = function(a) { - return new d(this.x - a.x, this.y - a.y, this.z - a.z); - }; - d.prototype.negate = function() { - this.x = -this.x; - this.y = -this.y; - this.z = -this.z; - }; - d.prototype.equals = function(a, b) { - return this.x === a.x && this.y === a.y && this.z === a.z && (!b || this.w === a.w); - }; - d.prototype.nearEquals = function(a, b, d) { - return Math.abs(this.x - a.x) < b && Math.abs(this.y - a.y) < b && Math.abs(this.z - a.z) < b && (!d || Math.abs(this.w - a.w) < b); - }; - d.prototype.project = function() { - this.x /= this.w; - this.y /= this.w; - this.z /= this.w; - }; - d.prototype.copyFrom = function(a) { - this.x = a.x; - this.y = a.y; - this.z = a.z; - }; - d.prototype.setTo = function(a, b, d) { - this.x = +a; - this.y = +b; - this.z = +d; - }; - d.prototype.clone = function() { - return new d(this.x, this.y, this.z, this.w); - }; - d.prototype.toString = function() { - return "Vector3D(" + this.x + ", " + this.y + ", " + this.z + ")"; - }; - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - d.X_AXIS = Object.freeze(new d(1, 0, 0)); - d.Y_AXIS = Object.freeze(new d(0, 1, 0)); - d.Z_AXIS = Object.freeze(new d(0, 0, 1)); - return d; - }(b.ASNative); - f.Vector3D = k; - })(f.geom || (f.geom = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.accessibility.Accessibility"); - } - __extends(a, b); - Object.defineProperty(a, "active", {get:function() { - k("public flash.accessibility.Accessibility::get active"); - return a._active; - }, enumerable:!0, configurable:!0}); - a.sendEvent = function(a, b, d, e) { - k("public flash.accessibility.Accessibility::static sendEvent"); - }; - a.updateProperties = function() { - k("public flash.accessibility.Accessibility::static updateProperties"); - }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a._active = !1; - return a; - }(g.ASNative); - f.Accessibility = m; - })(f.accessibility || (f.accessibility = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.accessibility.AccessibilityImplementation"); - } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.AccessibilityImplementation = m; - })(f.accessibility || (f.accessibility = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); +(function(c) { + (function(c) { + (function(a) { + a.defineText = function(a) { + return{type:"text", id:a.id, fillBounds:a.bbox, variableName:a.variableName, tag:a, bold:!1, italic:!1}; + }; + })(c.Parser || (c.Parser = {})); + })(c.SWF || (c.SWF = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { - function d() { - } - __extends(d, b); - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - return d; - }(b.ASNative); - f.AccessibilityProperties = k; - })(f.accessibility || (f.accessibility = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.assert, m = b.AVM2.Runtime.asCoerceString, d = function(a) { - function c(a, b, c) { - "undefined" === typeof b && (b = !1); - "undefined" === typeof c && (c = !1); - this._type = m(a); - this._bubbles = !!b; - this._cancelable = !!c; - this._currentTarget = this._target = null; - this._eventPhase = f.EventPhase.AT_TARGET; - this._isDefaultPrevented = this._stopImmediatePropagation = this._stopPropagation = !1; +(function(c) { + (function(h) { + h.timelineBuffer = new c.Tools.Profiler.TimelineBuffer("Parser"); + h.enterTimeline = function(a, c) { + }; + h.leaveTimeline = function(a) { + }; + })(c.SWF || (c.SWF = {})); +})(Shumway || (Shumway = {})); +var Shumway$$inline_710 = Shumway || (Shumway = {}), SWF$$inline_711 = Shumway$$inline_710.SWF || (Shumway$$inline_710.SWF = {}), Option$$inline_712 = Shumway$$inline_710.Options.Option; +SWF$$inline_711.parserOptions = Shumway$$inline_710.Settings.shumwayOptions.register(new Shumway$$inline_710.Options.OptionSet("Parser Options")); +SWF$$inline_711.traceLevel = SWF$$inline_711.parserOptions.register(new Option$$inline_712("parsertracelevel", "Parser Trace Level", "number", 0, "Parser Trace Level")); +(function(c) { + (function(c) { + function a(a, c) { + for (var l = 0, q = [], n, k, f = 16;0 < f && !a[f - 1];) { + f--; + } + q.push({children:[], index:0}); + var d = q[0], b; + for (n = 0;n < f;n++) { + for (k = 0;k < a[n];k++) { + d = q.pop(); + for (d.children[d.index] = c[l];0 < d.index;) { + d = q.pop(); + } + d.index++; + for (q.push(d);q.length <= n;) { + q.push(b = {children:[], index:0}), d.children[d.index] = b.children, d = b; + } + l++; + } + n + 1 < f && (q.push(b = {children:[], index:0}), d.children[d.index] = b.children, d = b); + } + return q[0].children; + } + function s(a, c, l, q, n, k, f, d, b) { + function g() { + if (0 < H) { + return H--, L >> H & 1; + } + L = a[c++]; + if (255 == L) { + var b = a[c++]; + if (b) { + throw "unexpected marker: " + (L << 8 | b).toString(16); + } + } + H = 7; + return L >>> 7; + } + function r(b) { + for (var a;null !== (a = g());) { + b = b[a]; + if ("number" === typeof b) { + return b; + } + if ("object" !== typeof b) { + throw "invalid huffman sequence"; + } + } + return null; + } + function w(b) { + for (var a = 0;0 < b;) { + var d = g(); + if (null === d) { + return; + } + a = a << 1 | d; + b--; + } + return a; + } + function h(b) { + if (1 === b) { + return 1 === g() ? 1 : -1; + } + var a = w(b); + return a >= 1 << b - 1 ? a : a + (-1 << b) + 1; + } + function s(b, a) { + var d = r(b.huffmanTableDC), d = 0 === d ? 0 : h(d); + b.blockData[a] = b.pred += d; + for (d = 1;64 > d;) { + var g = r(b.huffmanTableAC), e = g & 15, g = g >> 4; + if (0 === e) { + if (15 > g) { + break; } - __extends(c, a); - c.getInstance = function(a, b) { - var d; - "undefined" === typeof b && (b = !1); - "undefined" === typeof d && (d = !1); - var f = c._instances[a]; - f || (f = new c(a, b, d), c._instances[a] = f); - f._bubbles = b; - f._cancelable = d; - return f; - }; - c.getBroadcastInstance = function(a) { - var b, d; - "undefined" === typeof b && (b = !1); - "undefined" === typeof d && (d = !1); - var f = c._instances[a]; - f || (f = new c(a, b, d), c._instances[a] = f, k(c.isBroadcastEventType(a))); - f._isBroadcastEvent = !0; - f._bubbles = b; - f._cancelable = d; - return f; - }; - c.isBroadcastEventType = function(a) { - switch(a) { - case c.ENTER_FRAME: - ; - case c.EXIT_FRAME: - ; - case c.FRAME_CONSTRUCTED: - ; - case c.RENDER: - ; - case c.ACTIVATE: - ; - case c.DEACTIVATE: - ; - case c.AVM1_LOAD: - return!0; - } - return!1; - }; - Object.defineProperty(c.prototype, "type", {get:function() { - return this._type; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "bubbles", {get:function() { - return this._bubbles; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "cancelable", {get:function() { - return this._cancelable; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "target", {get:function() { - return this._target; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "currentTarget", {get:function() { - return this._currentTarget; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "eventPhase", {get:function() { - return this._eventPhase; - }, enumerable:!0, configurable:!0}); - c.prototype.stopPropagation = function() { - this._stopPropagation = !0; - }; - c.prototype.stopImmediatePropagation = function() { - this._stopImmediatePropagation = this._stopPropagation = !0; - }; - c.prototype.preventDefault = function() { - this._cancelable && (this._isDefaultPrevented = !0); - }; - c.prototype.isDefaultPrevented = function() { - return this._isDefaultPrevented; - }; - c.prototype.isBroadcastEvent = function() { - return!!this._isBroadcastEvent; - }; - c.prototype.clone = function() { - return new c(this._type, this._bubbles, this._cancelable); - }; - c.prototype.toString = function() { - return this.formatToString("Event", "type", "bubbles", "cancelable", "eventPhase"); - }; - c.prototype.formatToString = function(a) { - for (var b = [], c = 0;c < arguments.length - 1;c++) { - b[c] = arguments[c + 1]; - } - for (var c = "[" + a, d = 0;d < b.length;d++) { - var f = b[d], g = this.asGetPublicProperty(f); - "string" === typeof g && (g = '"' + g + '"'); - c += " " + f + "=" + g; + d += 16; + } else { + d += g, b.blockData[a + u[d]] = h(e), d++; + } + } + } + function p(a, d) { + var g = r(a.huffmanTableDC), g = 0 === g ? 0 : h(g) << b; + a.blockData[d] = a.pred += g; + } + function v(a, d) { + a.blockData[d] |= g() << b; + } + function N(a, d) { + if (0 < J) { + J--; + } else { + for (var g = k;g <= f;) { + var e = r(a.huffmanTableAC), c = e & 15, e = e >> 4; + if (0 === c) { + if (15 > e) { + J = w(e) + (1 << e) - 1; + break; } - return c + "]"; - }; - c.classInitializer = function() { - c._instances = b.ObjectUtilities.createMap(); - }; - c.initializer = null; - c.classSymbols = null; - c.instanceSymbols = null; - c.ACTIVATE = "activate"; - c.ADDED = "added"; - c.ADDED_TO_STAGE = "addedToStage"; - c.CANCEL = "cancel"; - c.CHANGE = "change"; - c.CLEAR = "clear"; - c.CLOSE = "close"; - c.COMPLETE = "complete"; - c.CONNECT = "connect"; - c.COPY = "copy"; - c.CUT = "cut"; - c.DEACTIVATE = "deactivate"; - c.ENTER_FRAME = "enterFrame"; - c.FRAME_CONSTRUCTED = "frameConstructed"; - c.EXIT_FRAME = "exitFrame"; - c.FRAME_LABEL = "frameLabel"; - c.ID3 = "id3"; - c.INIT = "init"; - c.MOUSE_LEAVE = "mouseLeave"; - c.OPEN = "open"; - c.PASTE = "paste"; - c.REMOVED = "removed"; - c.REMOVED_FROM_STAGE = "removedFromStage"; - c.RENDER = "render"; - c.RESIZE = "resize"; - c.SCROLL = "scroll"; - c.TEXT_INTERACTION_MODE_CHANGE = "textInteractionModeChange"; - c.SELECT = "select"; - c.SELECT_ALL = "selectAll"; - c.SOUND_COMPLETE = "soundComplete"; - c.TAB_CHILDREN_CHANGE = "tabChildrenChange"; - c.TAB_ENABLED_CHANGE = "tabEnabledChange"; - c.TAB_INDEX_CHANGE = "tabIndexChange"; - c.UNLOAD = "unload"; - c.FULLSCREEN = "fullScreen"; - c.CONTEXT3D_CREATE = "context3DCreate"; - c.TEXTURE_READY = "textureReady"; - c.VIDEO_FRAME = "videoFrame"; - c.SUSPEND = "suspend"; - c.AVM1_INIT = "initialize"; - c.AVM1_CONSTRUCT = "construct"; - c.AVM1_LOAD = "load"; - return c; - }(g.ASNative); - f.Event = d; - })(f.events || (f.events = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(t) { - var s = b.AVM2.Runtime.asCoerceString, m = b.ObjectUtilities.createEmptyObject, d = b.isFunction, a = b.isNullOrUndefined, c = b.AVM2.Runtime.throwError, n = b.Debug.assert, p = function() { - return function(a, b, c) { - this.listener = a; - this.useCapture = b; - this.priority = c; - }; - }(), e = function() { - function a() { - this._aliasCount = 0; - this._entries = []; + g += 16; + } else { + g += e, a.blockData[d + u[g]] = h(c) * (1 << b), g++; } - a.prototype.isEmpty = function() { - return 0 === this._entries.length; - }; - a.prototype.insert = function(a, b, c) { - for (var d = this._entries, e = d.length, f = e - 1;0 <= f;f--) { - var l = d[f]; - if (l.listener === a) { - return; - } - if (c > l.priority) { - e = f; - } else { - break; - } - } - d = this.ensureNonAliasedEntries(); - d.splice(e, 0, new p(a, b, c)); - }; - a.prototype.ensureNonAliasedEntries = function() { - var a = this._entries; - 0 < this._aliasCount && (a = this._entries = a.slice(), this._aliasCount = 0); - return a; - }; - a.prototype.remove = function(a) { - for (var b = this._entries, c = 0;c < b.length;c++) { - if (b[c].listener === a) { - this.ensureNonAliasedEntries().splice(c, 1); - break; + } + } + } + function K(a, d) { + for (var e = k, c = 0, m;e <= f;) { + m = u[e]; + switch(C) { + case 0: + c = r(a.huffmanTableAC); + m = c & 15; + c >>= 4; + if (0 === m) { + 15 > c ? (J = w(c) + (1 << c), C = 4) : (c = 16, C = 1); + } else { + if (1 !== m) { + throw "invalid ACn encoding"; } + E = h(m); + C = c ? 2 : 3; } - }; - a.prototype.snapshot = function() { - this._aliasCount++; - return this._entries; - }; - a.prototype.releaseSnapshot = function(a) { - this._entries === a && 0 < this._aliasCount && this._aliasCount--; - }; - return a; - }(), q = function() { - function a() { - this.reset(); - } - a.prototype.reset = function() { - this._queues = b.ObjectUtilities.createEmptyObject(); - }; - a.prototype.add = function(a, b) { - n(t.Event.isBroadcastEventType(a), "Can only register broadcast events."); - var c = this._queues[a] || (this._queues[a] = []); - 0 <= c.indexOf(b) || c.push(b); - }; - a.prototype.remove = function(a, b) { - n(t.Event.isBroadcastEventType(a), "Can only unregister broadcast events."); - var c = this._queues[a]; - n(c, "There should already be a queue for this."); - var d = c.indexOf(b); - n(0 <= d, "Target should be somewhere in this queue."); - c[d] = null; - n(0 > c.indexOf(b), "Target shouldn't be in this queue anymore."); - }; - a.prototype.dispatchEvent = function(a) { - n(a.isBroadcastEvent(), "Cannot dispatch non-broadcast events."); - var b = this._queues[a.type]; - if (b) { - for (var c = 0, d = 0;d < b.length;d++) { - var e = b[d]; - null === e ? c++ : e.dispatchEvent(a); - } - if (16 < c && c > b.length >> 1) { - c = []; - for (d = 0;d < b.length;d++) { - b[d] && c.push(b[d]); - } - this._queues[a.type] = c; + continue; + case 1: + ; + case 2: + a.blockData[d + m] ? a.blockData[d + m] += g() << b : (c--, 0 === c && (C = 2 == C ? 3 : 0)); + break; + case 3: + a.blockData[d + m] ? a.blockData[d + m] += g() << b : (a.blockData[d + m] = E << b, C = 0); + break; + case 4: + a.blockData[d + m] && (a.blockData[d + m] += g() << b); + } + e++; + } + 4 === C && (J--, 0 === J && (C = 0)); + } + var y = l.mcusPerLine, D = c, L = 0, H = 0, J = 0, C = 0, E, F = q.length, I, G, Z, Q, S; + d = l.progressive ? 0 === k ? 0 === d ? p : v : 0 === d ? N : K : s; + var O = 0; + l = 1 == F ? q[0].blocksPerLine * q[0].blocksPerColumn : y * l.mcusPerColumn; + n || (n = l); + for (var P, V;O < l;) { + for (G = 0;G < F;G++) { + q[G].pred = 0; + } + J = 0; + if (1 == F) { + for (I = q[0], S = 0;S < n;S++) { + d(I, 64 * ((I.blocksPerLine + 1) * (O / I.blocksPerLine | 0) + O % I.blocksPerLine)), O++; + } + } else { + for (S = 0;S < n;S++) { + for (G = 0;G < F;G++) { + for (I = q[G], P = I.h, V = I.v, Z = 0;Z < V;Z++) { + for (Q = 0;Q < P;Q++) { + d(I, 64 * ((I.blocksPerLine + 1) * ((O / y | 0) * I.v + Z) + (O % y * I.h + Q))); } } - }; - a.prototype.getQueueLength = function(a) { - return this._queues[a] ? this._queues[a].length : 0; - }; - return a; - }(); - t.BroadcastEventDispatchQueue = q; - var l = function(b) { - function l(a) { - "undefined" === typeof a && (a = null); - this._target = a || this; } - __extends(l, b); - l.prototype._getListenersForType = function(a, b) { - var c = a ? this._captureListeners : this._targetOrBubblingListeners; - return c ? c[b] : null; - }; - l.prototype._getListeners = function(a) { - return a ? this._captureListeners || (this._captureListeners = m()) : this._targetOrBubblingListeners || (this._targetOrBubblingListeners = m()); - }; - l.prototype.addEventListener = function(b, h, f, n, g) { - "undefined" === typeof f && (f = !1); - "undefined" === typeof n && (n = 0); - "undefined" === typeof g && (g = !1); - (2 > arguments.length || 5 < arguments.length) && c("ArgumentError", k.Errors.WrongArgumentCountError, "flash.events::EventDispatcher/addEventListener()", 2, arguments.length); - d(h) || c("TypeError", k.Errors.CheckTypeFailedError, h, "Function"); - a(b) && c("TypeError", k.Errors.NullPointerError, "type"); - b = s(b); - f = !!f; - n |= 0; - g = !!g; - var m = this._getListeners(f); - (m[b] || (m[b] = new e)).insert(h, f, n); - !f && t.Event.isBroadcastEventType(b) && l.broadcastEventDispatchQueue.add(b, this); - }; - l.prototype.removeEventListener = function(b, h, e) { - "undefined" === typeof e && (e = !1); - (2 > arguments.length || 3 < arguments.length) && c("ArgumentError", k.Errors.WrongArgumentCountError, "flash.events::EventDispatcher/removeEventListener()", 2, arguments.length); - d(h) || c("TypeError", k.Errors.CheckTypeFailedError, h, "Function"); - a(b) && c("TypeError", k.Errors.NullPointerError, "type"); - b = s(b); - var f = this._getListeners(!!e), n = f[b]; - n && (n.remove(h), n.isEmpty() && (!e && t.Event.isBroadcastEventType(b) && l.broadcastEventDispatchQueue.remove(b, this), f[b] = null)); - }; - l.prototype._hasTargetOrBubblingEventListener = function(a) { - return!(!this._targetOrBubblingListeners || !this._targetOrBubblingListeners[a]); - }; - l.prototype._hasCaptureEventListener = function(a) { - return!(!this._captureListeners || !this._captureListeners[a]); - }; - l.prototype._hasEventListener = function(a) { - return this._hasTargetOrBubblingEventListener(a) || this._hasCaptureEventListener(a); - }; - l.prototype.hasEventListener = function(b) { - 1 !== arguments.length && c("ArgumentError", k.Errors.WrongArgumentCountError, "flash.events::EventDispatcher/hasEventListener()", 1, arguments.length); - a(b) && c("TypeError", k.Errors.NullPointerError, "type"); - b = s(b); - return this._hasEventListener(b); - }; - l.prototype.willTrigger = function(b) { - 1 !== arguments.length && c("ArgumentError", k.Errors.WrongArgumentCountError, "flash.events::EventDispatcher/hasEventListener()", 1, arguments.length); - a(b) && c("TypeError", k.Errors.NullPointerError, "type"); - b = s(b); - if (this._hasEventListener(b)) { - return!0; - } - if (f.display.DisplayObject.isType(this)) { - var d = this._parent; - do { - if (d._hasEventListener(b)) { - return!0; + O++; + } + } + H = 0; + I = a[c] << 8 | a[c + 1]; + if (65280 >= I) { + throw "marker was not found"; + } + if (65488 <= I && 65495 >= I) { + c += 2; + } else { + break; + } + } + return c - D; + } + function v(a, c) { + for (var l = c.blocksPerLine, q = c.blocksPerColumn, n = new Int32Array(64), k = 0;k < q;k++) { + for (var f = 0;f < l;f++) { + for (var d = c, b = 64 * ((c.blocksPerLine + 1) * k + f), g = n, r = d.quantizationTable, w = void 0, h = void 0, s = void 0, p = void 0, v = void 0, u = void 0, K = void 0, y = void 0, D = void 0, L = void 0, L = 0;64 > L;L++) { + g[L] = d.blockData[b + L] * r[L]; + } + for (L = 0;8 > L;++L) { + r = 8 * L, 0 === g[1 + r] && 0 === g[2 + r] && 0 === g[3 + r] && 0 === g[4 + r] && 0 === g[5 + r] && 0 === g[6 + r] && 0 === g[7 + r] ? (D = 5793 * g[0 + r] + 512 >> 10, g[0 + r] = D, g[1 + r] = D, g[2 + r] = D, g[3 + r] = D, g[4 + r] = D, g[5 + r] = D, g[6 + r] = D, g[7 + r] = D) : (w = 5793 * g[0 + r] + 128 >> 8, h = 5793 * g[4 + r] + 128 >> 8, s = g[2 + r], p = g[6 + r], v = 2896 * (g[1 + r] - g[7 + r]) + 128 >> 8, y = 2896 * (g[1 + r] + g[7 + r]) + 128 >> 8, u = g[3 + r] << 4, K = + g[5 + r] << 4, D = w - h + 1 >> 1, w = w + h + 1 >> 1, h = D, D = 3784 * s + 1567 * p + 128 >> 8, s = 1567 * s - 3784 * p + 128 >> 8, p = D, D = v - K + 1 >> 1, v = v + K + 1 >> 1, K = D, D = y + u + 1 >> 1, u = y - u + 1 >> 1, y = D, D = w - p + 1 >> 1, w = w + p + 1 >> 1, p = D, D = h - s + 1 >> 1, h = h + s + 1 >> 1, s = D, D = 2276 * v + 3406 * y + 2048 >> 12, v = 3406 * v - 2276 * y + 2048 >> 12, y = D, D = 799 * u + 4017 * K + 2048 >> 12, u = 4017 * u - 799 * K + 2048 >> 12, K = + D, g[0 + r] = w + y, g[7 + r] = w - y, g[1 + r] = h + K, g[6 + r] = h - K, g[2 + r] = s + u, g[5 + r] = s - u, g[3 + r] = p + v, g[4 + r] = p - v); + } + for (L = 0;8 > L;++L) { + r = L, 0 === g[8 + r] && 0 === g[16 + r] && 0 === g[24 + r] && 0 === g[32 + r] && 0 === g[40 + r] && 0 === g[48 + r] && 0 === g[56 + r] ? (D = 5793 * g[L + 0] + 8192 >> 14, g[0 + r] = D, g[8 + r] = D, g[16 + r] = D, g[24 + r] = D, g[32 + r] = D, g[40 + r] = D, g[48 + r] = D, g[56 + r] = D) : (w = 5793 * g[0 + r] + 2048 >> 12, h = 5793 * g[32 + r] + 2048 >> 12, s = g[16 + r], p = g[48 + r], v = 2896 * (g[8 + r] - g[56 + r]) + 2048 >> 12, y = 2896 * (g[8 + r] + g[56 + r]) + 2048 >> 12, + u = g[24 + r], K = g[40 + r], D = w - h + 1 >> 1, w = w + h + 1 >> 1, h = D, D = 3784 * s + 1567 * p + 2048 >> 12, s = 1567 * s - 3784 * p + 2048 >> 12, p = D, D = v - K + 1 >> 1, v = v + K + 1 >> 1, K = D, D = y + u + 1 >> 1, u = y - u + 1 >> 1, y = D, D = w - p + 1 >> 1, w = w + p + 1 >> 1, p = D, D = h - s + 1 >> 1, h = h + s + 1 >> 1, s = D, D = 2276 * v + 3406 * y + 2048 >> 12, v = 3406 * v - 2276 * y + 2048 >> 12, y = D, D = 799 * u + 4017 * K + 2048 >> 12, u = 4017 * u - 799 * + K + 2048 >> 12, K = D, g[0 + r] = w + y, g[56 + r] = w - y, g[8 + r] = h + K, g[48 + r] = h - K, g[16 + r] = s + u, g[40 + r] = s - u, g[24 + r] = p + v, g[32 + r] = p - v); + } + for (L = 0;64 > L;++L) { + w = b + L, h = g[L], h = -2056 >= h ? 0 : 2024 <= h ? 255 : h + 2056 >> 4, d.blockData[w] = h; + } + } + } + return c.blockData; + } + function p(a) { + return 0 >= a ? 0 : 255 <= a ? 255 : a; + } + var u = new Int32Array([0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63]), l = function() { + function e() { + } + e.prototype.parse = function(e) { + function c() { + var b = e[k] << 8 | e[k + 1]; + k += 2; + return b; + } + function l() { + var b = c(), b = e.subarray(k, k + b - 2); + k += b.length; + return b; + } + function n(b) { + for (var a = Math.ceil(b.samplesPerLine / 8 / b.maxH), d = Math.ceil(b.scanLines / 8 / b.maxV), g = 0;g < b.components.length;g++) { + L = b.components[g]; + var e = Math.ceil(Math.ceil(b.samplesPerLine / 8) * L.h / b.maxH), f = Math.ceil(Math.ceil(b.scanLines / 8) * L.v / b.maxV); + L.blockData = new Int16Array(64 * d * L.v * (a * L.h + 1)); + L.blocksPerLine = e; + L.blocksPerColumn = f; + } + b.mcusPerLine = a; + b.mcusPerColumn = d; + } + var k = 0, f = null, d = null, b, g, r = [], w = [], h = [], p = c(); + if (65496 != p) { + throw "SOI not found"; + } + for (p = c();65497 != p;) { + var B, M; + switch(p) { + case 65504: + ; + case 65505: + ; + case 65506: + ; + case 65507: + ; + case 65508: + ; + case 65509: + ; + case 65510: + ; + case 65511: + ; + case 65512: + ; + case 65513: + ; + case 65514: + ; + case 65515: + ; + case 65516: + ; + case 65517: + ; + case 65518: + ; + case 65519: + ; + case 65534: + B = l(); + 65504 === p && 74 === B[0] && 70 === B[1] && 73 === B[2] && 70 === B[3] && 0 === B[4] && (f = {version:{major:B[5], minor:B[6]}, densityUnits:B[7], xDensity:B[8] << 8 | B[9], yDensity:B[10] << 8 | B[11], thumbWidth:B[12], thumbHeight:B[13], thumbData:B.subarray(14, 14 + 3 * B[12] * B[13])}); + 65518 === p && 65 === B[0] && 100 === B[1] && 111 === B[2] && 98 === B[3] && 101 === B[4] && 0 === B[5] && (d = {version:B[6], flags0:B[7] << 8 | B[8], flags1:B[9] << 8 | B[10], transformCode:B[11]}); + break; + case 65499: + for (var p = c() + k - 2, N;k < p;) { + var K = e[k++], y = new Int32Array(64); + if (0 === K >> 4) { + for (B = 0;64 > B;B++) { + N = u[B], y[N] = e[k++]; } - } while (d = d._parent); - } - return!1; - }; - l.prototype._skipDispatchEvent = function(a) { - if (!a.isBroadcastEvent() && f.display.DisplayObject.isType(this)) { - for (var b = this;b;) { - if (b._hasEventListener(a.type)) { - return!1; + } else { + if (1 === K >> 4) { + for (B = 0;64 > B;B++) { + N = u[B], y[N] = c(); + } + } else { + throw "DQT: invalid table spec"; } - b = b._parent; } - return!0; - } - return!this._hasEventListener(a.type); - }; - l.prototype.dispatchEvent = function(a) { - if (this._skipDispatchEvent(a)) { - return!0; + r[K & 15] = y; } - 1 !== arguments.length && c("ArgumentError", k.Errors.WrongArgumentCountError, "flash.events::EventDispatcher/dispatchEvent()", 1, arguments.length); - k.counter.count("EventDispatcher::dispatchEvent"); - var b = a._type, d = this._target; - k.counter.count("EventDispatcher::dispatchEvent(" + b + ")"); - var e = !0, g = []; - if (!a.isBroadcastEvent() && f.display.DisplayObject.isType(this)) { - for (var m = this._parent;m;) { - m._hasEventListener(b) && g.push(m), m = m._parent; - } - for (m = g.length - 1;0 <= m && e;m--) { - var p = g[m]; - if (p._hasCaptureEventListener(b)) { - var q = p._getListenersForType(!0, b); - n(q); - e = l.callListeners(q, a, d, p, t.EventPhase.CAPTURING_PHASE); - } - } + break; + case 65472: + ; + case 65473: + ; + case 65474: + if (b) { + throw "Only single frame JPEGs supported"; } - e && (q = this._getListenersForType(!1, b)) && (e = l.callListeners(this._getListeners(!1)[b], a, d, d, t.EventPhase.AT_TARGET)); - if (!a.isBroadcastEvent() && e && a.bubbles) { - for (m = 0;m < g.length && e;m++) { - p = g[m], p._hasTargetOrBubblingEventListener(b) && (q = p._getListenersForType(!1, b), e = l.callListeners(q, a, d, p, t.EventPhase.BUBBLING_PHASE)); + c(); + b = {}; + b.extended = 65473 === p; + b.progressive = 65474 === p; + b.precision = e[k++]; + b.scanLines = c(); + b.samplesPerLine = c(); + b.components = []; + b.componentIds = {}; + B = e[k++]; + for (p = y = K = 0;p < B;p++) { + N = e[k]; + M = e[k + 1] >> 4; + var D = e[k + 1] & 15; + K < M && (K = M); + y < D && (y = D); + M = b.components.push({h:M, v:D, quantizationTable:r[e[k + 2]]}); + b.componentIds[N] = M - 1; + k += 3; + } + b.maxH = K; + b.maxV = y; + n(b); + break; + case 65476: + N = c(); + for (p = 2;p < N;) { + K = e[k++]; + y = new Uint8Array(16); + for (B = M = 0;16 > B;B++, k++) { + M += y[B] = e[k]; + } + D = new Uint8Array(M); + for (B = 0;B < M;B++, k++) { + D[B] = e[k]; } + p += 17 + M; + (0 === K >> 4 ? h : w)[K & 15] = a(y, D); } - return!a._isDefaultPrevented; - }; - l.callListeners = function(a, b, c, d, e) { - if (a.isEmpty()) { - return!0; - } - b._target && (b = b.clone()); - for (var f = a.snapshot(), l = 0;l < f.length;l++) { - var n = f[l]; - b._target = c; - b._currentTarget = d; - b._eventPhase = e; - n.listener(b); - if (b._stopImmediatePropagation) { - break; - } + break; + case 65501: + c(); + g = c(); + break; + case 65498: + c(); + N = e[k++]; + B = []; + for (var L, p = 0;p < N;p++) { + K = b.componentIds[e[k++]], L = b.components[K], K = e[k++], L.huffmanTableDC = h[K >> 4], L.huffmanTableAC = w[K & 15], B.push(L); + } + p = e[k++]; + N = e[k++]; + K = e[k++]; + p = s(e, k, b, B, g, p, N, K >> 4, K & 15); + k += p; + break; + default: + if (255 == e[k - 3] && 192 <= e[k - 2] && 254 >= e[k - 2]) { + k -= 3; + break; } - a.releaseSnapshot(f); - return!b._stopPropagation; - }; - l.classInitializer = function() { - l.broadcastEventDispatchQueue = new q; - }; - l.initializer = function() { - this._target = this; - this._targetOrBubblingListeners = this._captureListeners = null; - }; - l.classSymbols = null; - l.instanceSymbols = null; - return l; - }(g.ASNative); - t.EventDispatcher = l; - })(f.events || (f.events = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - b.call(this); - k("Dummy Constructor: public flash.events.EventPhase"); - } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.CAPTURING_PHASE = 1; - a.AT_TARGET = 2; - a.BUBBLING_PHASE = 3; - return a; - }(g.ASNative); - f.EventPhase = m; - })(f.events || (f.events = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, f, g, e) { - b.call(this, void 0, void 0, void 0); - k("Dummy Constructor: public flash.events.TextEvent"); - } - __extends(a, b); - a.prototype.copyNativeData = function(a) { - k("public flash.events.TextEvent::copyNativeData"); - }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.LINK = "link"; - a.TEXT_INPUT = "textInput"; - return a; - }(f.events.Event); - g.TextEvent = m; - })(f.events || (f.events = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, f, g, e, m) { - b.call(this, void 0, void 0, void 0, void 0); - k("Dummy Constructor: public flash.events.ErrorEvent"); - } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.ERROR = "error"; - return a; - }(f.events.TextEvent); - g.ErrorEvent = m; - })(f.events || (f.events = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, f, g, e) { - b.call(this, void 0, void 0, void 0); - k("Dummy Constructor: public flash.events.GameInputEvent"); - } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.DEVICE_ADDED = "deviceAdded"; - a.DEVICE_REMOVED = "deviceRemoved"; - return a; - }(f.events.Event); - g.GameInputEvent = m; - })(f.events || (f.events = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.AVM2.Runtime.asCoerceString, m = b.Debug.notImplemented, d = b.Debug.somewhatImplemented, a = function(a) { - function b(d, e, f, l, n, g, r, h, k) { - a.call(this, void 0, void 0, void 0); - m("Dummy Constructor: public flash.events.GestureEvent"); + throw "unknown JPEG marker " + p.toString(16);; + } + p = c(); + } + this.width = b.samplesPerLine; + this.height = b.scanLines; + this.jfif = f; + this.adobe = d; + this.components = []; + for (p = 0;p < b.components.length;p++) { + L = b.components[p], this.components.push({output:v(b, L), scaleX:L.h / b.maxH, scaleY:L.v / b.maxV, blocksPerLine:L.blocksPerLine, blocksPerColumn:L.blocksPerColumn}); + } + this.numComponents = this.components.length; + }; + e.prototype._getLinearizedBlockData = function(a, e) { + var c = this.width / a, l = this.height / e, k, f, d, b, g, r, w = 0, h, s = this.components.length, p = a * e * s, v = new Uint8Array(p), u = new Uint32Array(a); + for (r = 0;r < s;r++) { + k = this.components[r]; + f = k.scaleX * c; + d = k.scaleY * l; + w = r; + h = k.output; + b = k.blocksPerLine + 1 << 3; + for (g = 0;g < a;g++) { + k = 0 | g * f, u[g] = (k & 4294967288) << 3 | k & 7; + } + for (f = 0;f < e;f++) { + for (k = 0 | f * d, k = b * (k & 4294967288) | (k & 7) << 3, g = 0;g < a;g++) { + v[w] = h[k + u[g]], w += s; } - __extends(b, a); - Object.defineProperty(b.prototype, "localX", {get:function() { - return this._localX; - }, set:function(a) { - this._localX = +a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "localY", {get:function() { - return this._localY; - }, set:function(a) { - this._localY = +a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "stageX", {get:function() { - d("public flash.events.GestureEvent::stageX"); - return 0; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "stageY", {get:function() { - d("public flash.events.GestureEvent::stageY"); - return 0; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "ctrlKey", {get:function() { - return this._ctrlKey; - }, set:function(a) { - this._ctrlKey = !!a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "altKey", {get:function() { - return this._altKey; - }, set:function(a) { - this._altKey = !!a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "shiftKey", {get:function() { - return this._shiftKey; - }, set:function(a) { - this._shiftKey = !!a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "phase", {get:function() { - return this._phase; - }, set:function(a) { - this._phase = k(a); - }, enumerable:!0, configurable:!0}); - b.prototype.updateAfterEvent = function() { - d("public flash.events.GestureEvent::updateAfterEvent"); - }; - b.prototype.NativeCtor = function(a, b, c, d, f, n) { - this._phase = k(a); - this._localX = +b; - this._localY = +c; - this._ctrlKey = !!d; - this._altKey = !!f; - this._shiftKey = !!n; - }; - b.prototype.clone = function() { - return new f.events.GestureEvent(this.type, this.bubbles, this.cancelable, this.phase, this.localX, this.localY, this.ctrlKey, this.altKey, this.shiftKey); - }; - b.prototype.toString = function() { - return this.formatToString("GestureEvent", "type", "bubbles", "cancelable", "eventPhase", "localX", "localY", "ctrlKey", "altKey", "shiftKey"); - }; - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - b.GESTURE_TWO_FINGER_TAP = "gestureTwoFingerTap"; - return b; - }(f.events.Event); - g.GestureEvent = a; - })(f.events || (f.events = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, f, g, e) { - b.call(this, void 0, void 0, void 0); - k("Dummy Constructor: public flash.events.HTTPStatusEvent"); + } + } + if (l = this.decodeTransform) { + for (r = 0;r < p;) { + for (c = k = 0;k < s;k++, r++, c += 2) { + v[r] = (v[r] * l[c] >> 8) + l[c + 1]; } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.HTTP_STATUS = "httpStatus"; - a.HTTP_RESPONSE_STATUS = "httpResponseStatus"; - return a; - }(f.events.Event); - g.HTTPStatusEvent = m; - })(f.events || (f.events = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, f, g, e, m) { - b.call(this, void 0, void 0, void 0, void 0, void 0); - k("Dummy Constructor: public flash.events.IOErrorEvent"); + } + } + return v; + }; + e.prototype._isColorConversionNeeded = function() { + return this.adobe && this.adobe.transformCode ? !0 : 3 == this.numComponents ? !0 : !1; + }; + e.prototype._convertYccToRgb = function(a) { + for (var e, c, l, k = 0, f = a.length;k < f;k += 3) { + e = a[k], c = a[k + 1], l = a[k + 2], a[k] = p(e - 179.456 + 1.402 * l), a[k + 1] = p(e + 135.459 - .344 * c - .714 * l), a[k + 2] = p(e - 226.816 + 1.772 * c); + } + return a; + }; + e.prototype._convertYcckToRgb = function(a) { + for (var e, c, l, k, f, d, b, g, r, w, h, s, v, u, N = 0, K = 0, y = a.length;K < y;K += 4) { + e = a[K]; + c = a[K + 1]; + l = a[K + 2]; + k = a[K + 3]; + f = c * c; + d = c * l; + b = c * e; + g = c * k; + r = l * l; + w = l * k; + h = l * e; + s = e * e; + v = e * k; + u = k * k; + var D = -122.67195406894 - 6.60635669420364E-5 * f + 4.37130475926232E-4 * d - 5.4080610064599E-5 * b + 4.8449797120281E-4 * g - .154362151871126 * c - 9.57964378445773E-4 * r + 8.17076911346625E-4 * h - .00477271405408747 * w + 1.53380253221734 * l + 9.61250184130688E-4 * s - .00266257332283933 * v + .48357088451265 * e - 3.36197177618394E-4 * u + .484791561490776 * k, L = 107.268039397724 + 2.19927104525741E-5 * f - 6.40992018297945E-4 * d + 6.59397001245577E-4 * b + 4.26105652938837E-4 * + g - .176491792462875 * c - 7.78269941513683E-4 * r + .00130872261408275 * h + 7.70482631801132E-4 * w - .151051492775562 * l + .00126935368114843 * s - .00265090189010898 * v + .25802910206845 * e - 3.18913117588328E-4 * u - .213742400323665 * k; + e = -20.810012546947 - 5.70115196973677E-4 * f - 2.63409051004589E-5 * d + .0020741088115012 * b - .00288260236853442 * g + .814272968359295 * c - 1.53496057440975E-5 * r - 1.32689043961446E-4 * h + 5.60833691242812E-4 * w - .195152027534049 * l + .00174418132927582 * s - .00255243321439347 * v + .116935020465145 * e - 3.43531996510555E-4 * u + .24165260232407 * k; + a[N++] = p(D); + a[N++] = p(L); + a[N++] = p(e); + } + return a; + }; + e.prototype._convertYcckToCmyk = function(a) { + for (var e, c, l, k = 0, f = a.length;k < f;k += 4) { + e = a[k], c = a[k + 1], l = a[k + 2], a[k] = p(434.456 - e - 1.402 * l), a[k + 1] = p(119.541 - e + .344 * c + .714 * l), a[k + 2] = p(481.816 - e - 1.772 * c); + } + return a; + }; + e.prototype._convertCmykToRgb = function(a) { + for (var e, c, l, k, f = 0, d = 1 / 255 / 255, b = 0, g = a.length;b < g;b += 4) { + e = a[b]; + c = a[b + 1]; + l = a[b + 2]; + k = a[b + 3]; + var r = e * (-4.387332384609988 * e + 54.48615194189176 * c + 18.82290502165302 * l + 212.25662451639585 * k - 72734.4411664936) + c * (1.7149763477362134 * c - 5.6096736904047315 * l - 17.873870861415444 * k - 1401.7366389350734) + l * (-2.5217340131683033 * l - 21.248923337353073 * k + 4465.541406466231) - k * (21.86122147463605 * k + 48317.86113160301), w = e * (8.841041422036149 * e + 60.118027045597366 * c + 6.871425592049007 * l + 31.159100130055922 * k - 20220.756542821975) + c * + (-15.310361306967817 * c + 17.575251261109482 * l + 131.35250912493976 * k - 48691.05921601825) + l * (4.444339102852739 * l + 9.8632861493405 * k - 6341.191035517494) - k * (20.737325471181034 * k + 47890.15695978492); + e = e * (.8842522430003296 * e + 8.078677503112928 * c + 30.89978309703729 * l - .23883238689178934 * k - 3616.812083916688) + c * (10.49593273432072 * c + 63.02378494754052 * l + 50.606957656360734 * k - 28620.90484698408) + l * (.03296041114873217 * l + 115.60384449646641 * k - 49363.43385999684) - k * (22.33816807309886 * k + 45932.16563550634); + a[f++] = 0 <= r ? 255 : -16581375 >= r ? 0 : 255 + r * d | 0; + a[f++] = 0 <= w ? 255 : -16581375 >= w ? 0 : 255 + w * d | 0; + a[f++] = 0 <= e ? 255 : -16581375 >= e ? 0 : 255 + e * d | 0; + } + return a; + }; + e.prototype.getData = function(a, e, c) { + if (4 < this.numComponents) { + throw "Unsupported color mode"; + } + a = this._getLinearizedBlockData(a, e); + return 3 === this.numComponents ? this._convertYccToRgb(a) : 4 === this.numComponents ? this._isColorConversionNeeded() ? c ? this._convertYcckToRgb(a) : this._convertYcckToCmyk(a) : this._convertCmykToRgb(a) : a; + }; + e.prototype.copyToImageData = function(a) { + var e = a.width, c = a.height, l = e * c * 4; + a = a.data; + var e = this.getData(e, c, !0), k = c = 0, f, d, b, g; + switch(this.components.length) { + case 1: + for (;k < l;) { + f = e[c++], a[k++] = f, a[k++] = f, a[k++] = f, a[k++] = 255; } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.IO_ERROR = "ioError"; - a.NETWORK_ERROR = "networkError"; - a.DISK_ERROR = "diskError"; - a.VERIFY_ERROR = "verifyError"; - return a; - }(f.events.ErrorEvent); - g.IOErrorEvent = m; - })(f.events || (f.events = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.somewhatImplemented, m = b.Debug.notImplemented, d = function(a) { - function b(c, d, e, f, l, g, k, r, h) { - a.call(this, void 0, void 0, void 0); - m("Dummy Constructor: public flash.events.KeyboardEvent"); + break; + case 3: + for (;k < l;) { + b = e[c++], g = e[c++], f = e[c++], a[k++] = b, a[k++] = g, a[k++] = f, a[k++] = 255; } - __extends(b, a); - b.prototype.updateAfterEvent = function() { - k("public flash.events.KeyboardEvent::updateAfterEvent"); - }; - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - b.KEY_DOWN = "keyDown"; - b.KEY_UP = "keyUp"; - return b; - }(f.events.Event); - g.KeyboardEvent = d; - })(f.events || (f.events = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = b.Debug.somewhatImplemented, d = function(a) { - function c(b, c, d, f, l, g, m, r, h, x, y) { - a.call(this, void 0, void 0, void 0); - k("Dummy Constructor: public flash.events.MouseEvent"); + break; + case 4: + for (;k < l;) { + b = e[c++], g = e[c++], f = e[c++], d = e[c++], b = 255 - p(b * (1 - d / 255) + d), g = 255 - p(g * (1 - d / 255) + d), f = 255 - p(f * (1 - d / 255) + d), a[k++] = b, a[k++] = g, a[k++] = f, a[k++] = 255; } - __extends(c, a); - c.typeFromDOMType = function(a) { - switch(a) { - case "click": - return c.CLICK; - case "dblclick": - return c.DOUBLE_CLICK; - case "mousedown": - return c.MOUSE_DOWN; - case "mousemove": - return c.MOUSE_MOVE; - case "mouseup": - return c.MOUSE_UP; - default: - k(a); + break; + default: + throw "Unsupported color mode";; + } + }; + return e; + }(); + c.JpegImage = l; + })(c.JPEG || (c.JPEG = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + function a() { + this.bitBuffer = this.bitLength = 0; + } + function s(a) { + if (this.pos + a > this.end) { + throw c.StreamNoDataError; + } + } + function v() { + return this.end - this.pos; + } + function p(a, c) { + var h = new l(this.bytes); + h.pos = a; + h.end = c; + return h; + } + function u(a) { + var c = this.bytes, l = this.end + a.length; + if (l > c.length) { + throw "stream buffer overfow"; + } + c.set(a, this.end); + this.end = l; + } + c.StreamNoDataError = {}; + var l = function() { + return function(e, c, l, q) { + void 0 === c && (c = 0); + e.buffer instanceof ArrayBuffer && (c += e.byteOffset, e = e.buffer); + void 0 === l && (l = e.byteLength - c); + void 0 === q && (q = l); + var n = new Uint8Array(e, c, q); + e = new DataView(e, c, q); + e.bytes = n; + e.pos = 0; + e.end = l; + e.bitBuffer = 0; + e.bitLength = 0; + e.align = a; + e.ensure = s; + e.remaining = v; + e.substream = p; + e.push = u; + return e; + }; + }(); + c.Stream = l; + })(c.SWF || (c.SWF = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + function a() { + s || (s = new Worker(h.MP3WORKER_PATH), s.addEventListener("message", function(a) { + "console" === a.data.action && console[a.data.method].call(console, a.data.message); + })); + return s; + } + h.MP3WORKER_PATH = "../../lib/mp3/mp3worker.js"; + var s = null, v = 0, p = function() { + function h() { + this._sessionId = v++; + this._onworkermessageBound = this.onworkermessage.bind(this); + this._worker = a(); + this._worker.addEventListener("message", this._onworkermessageBound, !1); + this._worker.postMessage({sessionId:this._sessionId, action:"create"}); + } + h.prototype.onworkermessage = function(a) { + if (a.data.sessionId === this._sessionId) { + switch(a.data.action) { + case "closed": + if (this.onclosed) { + this.onclosed(); } - }; - Object.defineProperty(c.prototype, "localX", {get:function() { - return this._localX / 20 | 0; - }, set:function(a) { - this._localX = 20 * a | 0; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "localY", {get:function() { - return this._localY / 20 | 0; - }, set:function(a) { - this._localY = 20 * a | 0; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "stageX", {get:function() { - return isNaN(this.localX + this.localY) ? Number.NaN : this._getGlobalPoint().x / 20 | 0; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "stageY", {get:function() { - return isNaN(this.localX + this.localY) ? Number.NaN : this._getGlobalPoint().y / 20 | 0; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "movementX", {get:function() { - m("public flash.events.MouseEvent::set movementX"); - return this._movementX || 0; - }, set:function(a) { - this._movementX = +a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "movementY", {get:function() { - m("public flash.events.MouseEvent::set movementY"); - return this._movementY || 0; - }, set:function(a) { - this._movementY = +a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "delta", {get:function() { - return this._delta; - }, set:function(a) { - this._delta = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "ctrlKey", {get:function() { - return this._ctrlKey; - }, set:function(a) { - this._ctrlKey = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "altKey", {get:function() { - return this._altKey; - }, set:function(a) { - this._altKey = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "shiftKey", {get:function() { - return this._shiftKey; - }, set:function(a) { - this._shiftKey = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "buttonDown", {get:function() { - return this._buttonDown; - }, set:function(a) { - this._buttonDown = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "relatedObject", {get:function() { - return this._relatedObject; - }, set:function(a) { - this._relatedObject = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "isRelatedObjectInaccessible", {get:function() { - return this._isRelatedObjectInaccessible; - }, set:function(a) { - this._isRelatedObjectInaccessible = a; - }, enumerable:!0, configurable:!0}); - c.prototype.updateAfterEvent = function() { - b.AVM2.Runtime.AVM2.instance.globals["Shumway.Player.Utils"].requestRendering(); - }; - c.prototype._getGlobalPoint = function() { - var a = this._position; - a || (a = this._position = new f.geom.Point); - this.target ? (a.setTo(this._localX, this._localY), this._target._getConcatenatedMatrix().transformPointInPlace(a)) : a.setTo(0, 0); - return a; - }; - c.prototype.clone = function() { - return new f.events.MouseEvent(this.type, this.bubbles, this.cancelable, this.localX, this.localY, this.relatedObject, this.ctrlKey, this.altKey, this.shiftKey, this.buttonDown, this.delta); - }; - c.prototype.toString = function() { - return this.formatToString("MouseEvent", "type", "bubbles", "cancelable", "eventPhase", "localX", "localY", "relatedObject", "ctrlKey", "altKey", "shiftKey", "buttonDown", "delta"); - }; - c.classInitializer = null; - c.initializer = null; - c.classSymbols = null; - c.instanceSymbols = null; - c.CLICK = "click"; - c.DOUBLE_CLICK = "doubleClick"; - c.MOUSE_DOWN = "mouseDown"; - c.MOUSE_MOVE = "mouseMove"; - c.MOUSE_OUT = "mouseOut"; - c.MOUSE_OVER = "mouseOver"; - c.MOUSE_UP = "mouseUp"; - c.RELEASE_OUTSIDE = "releaseOutside"; - c.MOUSE_WHEEL = "mouseWheel"; - c.ROLL_OUT = "rollOut"; - c.ROLL_OVER = "rollOver"; - c.MIDDLE_CLICK = "middleClick"; - c.MIDDLE_MOUSE_DOWN = "middleMouseDown"; - c.MIDDLE_MOUSE_UP = "middleMouseUp"; - c.RIGHT_CLICK = "rightClick"; - c.RIGHT_MOUSE_DOWN = "rightMouseDown"; - c.RIGHT_MOUSE_UP = "rightMouseUp"; - c.CONTEXT_MENU = "contextMenu"; - return c; - }(f.events.Event); - g.MouseEvent = d; - })(f.events || (f.events = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + this._worker.removeEventListener("message", this._onworkermessageBound, !1); + this._worker = null; + break; + case "frame": + this.onframedata(a.data.frameData, a.data.channels, a.data.sampleRate, a.data.bitRate); + break; + case "id3": + if (this.onid3tag) { + this.onid3tag(a.data.id3Data); + } + break; + case "error": + if (this.onerror) { + this.onerror(a.data.message); + } + ; + } + } + }; + h.prototype.pushAsync = function(a) { + this._worker.postMessage({sessionId:this._sessionId, action:"decode", data:a}); + }; + h.prototype.close = function() { + this._worker.postMessage({sessionId:this._sessionId, action:"close"}); + }; + h.processAll = function(a) { + var e = 8E3, m = new Float32Array(e), t = 0, q = [], n = !1, k = new c.PromiseWrapper, f = new h; + f.onframedata = function(a, b, g, f) { + b = a.length + t; + if (b > e) { + do { + e *= 2; + } while (b > e); + b = new Float32Array(e); + b.set(m); + m = b; + } + m.set(a, t); + t += a.length; + }; + f.onid3tag = function(a) { + q.push(a); + }; + f.onclosed = function() { + n || k.resolve({data:m.subarray(0, t), id3Tags:q}); + }; + f.onerror = function(a) { + n || (n = !0, k.reject(a)); + }; + f.pushAsync(a); + f.close(); + return k.promise; + }; + return h; + }(); + h.MP3DecoderSession = p; + })(c.SWF || (c.SWF = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(b) { - (function(g) { - var k = function(b) { - function d(a, b, d, f) { - } - __extends(d, b); - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - d.NET_STATUS = "netStatus"; - return d; - }(b.events.Event); - g.NetStatusEvent = k; - })(b.events || (b.events = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, f, g, e, m) { - b.call(this, void 0, void 0, void 0); - k("Dummy Constructor: public flash.events.ProgressEvent"); - } - __extends(a, b); - Object.defineProperty(a.prototype, "bytesLoaded", {get:function() { - return this._bytesLoaded; - }, set:function(a) { - this._bytesLoaded = a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "bytesTotal", {get:function() { - return this._bytesTotal; - }, set:function(a) { - this._bytesTotal = a; - }, enumerable:!0, configurable:!0}); - a.prototype.clone = function() { - return new f.events.ProgressEvent(this._type, this._bubbles, this._cancelable, this._bytesLoaded, this._bytesTotal); - }; - a.prototype.toString = function() { - return this.formatToString("ProgressEvent", "bubbles", "cancelable", "eventPhase", "bytesLoaded", "bytesTotal"); - }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.PROGRESS = "progress"; - a.SOCKET_DATA = "socketData"; - return a; - }(f.events.Event); - g.ProgressEvent = m; - })(f.events || (f.events = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, f, g, e, m) { - b.call(this, void 0, void 0, void 0, void 0, void 0); - k("Dummy Constructor: public flash.events.SecurityErrorEvent"); - } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.SECURITY_ERROR = "securityError"; - return a; - }(f.events.ErrorEvent); - g.SecurityErrorEvent = m; - })(f.events || (f.events = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, f, g) { - b.call(this, void 0, void 0, void 0); - k("Dummy Constructor: public flash.events.TimerEvent"); +__extends = this.__extends || function(c, h) { + function a() { + this.constructor = c; + } + for (var s in h) { + h.hasOwnProperty(s) && (c[s] = h[s]); + } + a.prototype = h.prototype; + c.prototype = new a; +}; +(function(c) { + (function(h) { + function a(b, a) { + return b && a ? "boldItalic" : b ? "bold" : a ? "italic" : "regular"; + } + function s(b, a) { + switch(b.code) { + case 6: + ; + case 21: + ; + case 35: + ; + case 90: + return c.SWF.Parser.defineImage(b); + case 20: + ; + case 36: + return c.SWF.Parser.defineBitmap(b); + case 7: + ; + case 34: + return c.SWF.Parser.defineButton(b, a); + case 37: + return c.SWF.Parser.defineText(b); + case 10: + ; + case 48: + ; + case 75: + ; + case 91: + return c.SWF.Parser.defineFont(b); + case 46: + ; + case 84: + ; + case 2: + ; + case 22: + ; + case 32: + ; + case 83: + return c.SWF.Parser.defineShape(b); + case 14: + return c.SWF.Parser.defineSound(b); + case 39: + return b; + case 87: + return{type:"binary", id:b.id, data:b.data}; + case 11: + ; + case 33: + return c.SWF.Parser.defineLabel(b); + default: + return b; + } + } + var v = c.Debug.assert, p = c.SWF.Parser, u = h.Stream, l = c.ArrayUtilities.Inflate, e = p.SwfTag, m = p.DefinitionTags, t = p.ImageDefinitionTags, q = p.FontDefinitionTags, n = p.ControlTags, k = function() { + function k(b, a) { + v(67 === b[0] || 70 === b[0], "Unsupported compression format: " + (90 === b[0] ? "LZMA" : b[0] + "")); + v(87 === b[1]); + v(83 === b[2]); + v(30 <= b.length, "At least the header must be complete here."); + 0 < h.traceLevel.value && console.log("Create SWFFile"); + this.isCompressed = !1; + this.swfVersion = 0; + this.useAVM1 = !0; + this.backgroundColor = 4294967295; + this.bounds = null; + this.frameCount = this.frameRate = 0; + this.sceneAndFrameLabelData = this.attributes = null; + this.bytesLoaded = 0; + this.bytesTotal = a; + this.framesLoaded = this.pendingUpdateDelays = 0; + this.frames = []; + this.abcBlocks = []; + this.dictionary = []; + this.fonts = []; + this.symbolClassesMap = []; + this.symbolClassesList = []; + this.eagerlyParsedSymbolsMap = []; + this.eagerlyParsedSymbolsList = []; + this._currentExports = this._currentInitActionBlocks = this._currentActionBlocks = this._currentControlTags = this._currentSoundStreamBlock = this._currentSoundStreamHead = this._currentFrameLabel = this._jpegTables = null; + this._endTagEncountered = !1; + this.readHeaderAndInitialize(b); + } + k.prototype.appendLoadedData = function(b) { + this.bytesLoaded += b.length; + v(this.bytesLoaded <= this.bytesTotal); + this._endTagEncountered || (this.isCompressed ? this._decompressor.push(b) : this.processDecompressedData(b), this.scanLoadedData()); + }; + k.prototype.getSymbol = function(b) { + if (this.eagerlyParsedSymbolsMap[b]) { + return this.eagerlyParsedSymbolsMap[b]; + } + var a = this.dictionary[b]; + if (!a) { + return null; + } + a = 39 === a.tagCode ? this.parseSpriteTimeline(a) : this.getParsedTag(a); + a.className = this.symbolClassesMap[b] || null; + return a; + }; + k.prototype.getParsedTag = function(b) { + h.enterTimeline("Parse tag " + e[b.tagCode]); + this._dataStream.align(); + this._dataStream.pos = b.byteOffset; + var a = {code:b.tagCode}, d = p.LowLevel.tagHandlers[b.tagCode]; + c.Debug.assert(d, "handler shall exists here"); + var g = b.byteOffset + b.byteLength; + d(this.data, this._dataStream, a, this.swfVersion, b.tagCode, g); + d = this._dataStream.pos; + v(d <= g); + d < g && (c.Debug.warning("Scanning " + e[b.tagCode] + " at offset " + b.byteOffset + " consumed " + (d - b.byteOffset) + " of " + b.byteLength + " bytes. (" + (g - d) + " left)"), this._dataStream.pos = g); + b = s(a, this.dictionary); + h.leaveTimeline(); + return b; + }; + k.prototype.readHeaderAndInitialize = function(b) { + h.enterTimeline("Initialize SWFFile"); + this.isCompressed = 67 === b[0]; + this.swfVersion = b[3]; + this._loadStarted = Date.now(); + this._uncompressedLength = b[4] | b[5] << 8 | b[6] << 16 | b[7] << 24; + this.bytesLoaded = b.length; + this.data = new Uint8Array(this._uncompressedLength); + this._dataStream = new u(this.data.buffer); + this._dataStream.pos = 8; + this._dataView = this._dataStream; + if (this.isCompressed) { + this.data.set(b.subarray(0, 8)); + this._uncompressedLoadedLength = 8; + this._decompressor = l.create(!0); + var a = this; + this._decompressor.onData = function(b) { + a.data.set(b, a._uncompressedLoadedLength); + a._uncompressedLoadedLength += b.length; + a._decompressor.onData = a.processDecompressedData.bind(a); + a.parseHeaderContents(); + }; + this._decompressor.push(b.subarray(8)); + } else { + this.data.set(b), this._uncompressedLoadedLength = b.length, this._decompressor = null, this.parseHeaderContents(); + } + h.leaveTimeline(); + this._lastScanPosition = this._dataStream.pos; + this.scanLoadedData(); + }; + k.prototype.parseHeaderContents = function() { + var b = p.LowLevel.readHeader(this.data, this._dataStream); + this.bounds = b.bounds; + this.frameRate = b.frameRate; + this.frameCount = b.frameCount; + }; + k.prototype.processDecompressedData = function(b) { + this.data.set(b, this._uncompressedLoadedLength); + this._uncompressedLoadedLength += b.length; + this._uncompressedLoadedLength === this._uncompressedLength && (this._decompressor.close(), this._decompressor = null); + }; + k.prototype.scanLoadedData = function() { + h.enterTimeline("Scan loaded SWF file tags"); + this._dataStream.pos = this._lastScanPosition; + this.scanTagsToOffset(this._uncompressedLoadedLength, !0); + this._lastScanPosition = this._dataStream.pos; + h.leaveTimeline(); + }; + k.prototype.scanTagsToOffset = function(b, a) { + for (var d = new g(0, 0, 0), e;(e = this._dataStream.pos) < b - 1;) { + this.parseNextTagHeader(d); + if (0 === d.tagCode) { + a && (this._endTagEncountered = !0, console.log("SWF load time: " + (.001 * (Date.now() - this._loadStarted)).toFixed(4) + "sec")); + break; + } + var f = d.byteOffset + d.byteLength; + if (f > b) { + this._dataStream.pos = e; + break; + } + this.scanTag(d, a); + v(this._dataStream.pos <= f); + this._dataStream.pos < f && this.emitTagSlopWarning(d, f); + } + }; + k.prototype.parseNextTagHeader = function(b) { + var a = this._dataStream.pos, d = this._dataView.getUint16(a, !0), a = a + 2; + b.tagCode = d >> 6; + d &= 63; + if (63 === d) { + if (a + 4 > this._uncompressedLoadedLength) { + return; + } + d = this._dataView.getUint32(a, !0); + a += 4; + } + this._dataStream.pos = a; + b.byteOffset = a; + b.byteLength = d; + }; + k.prototype.scanTag = function(a, g) { + var f = this._dataStream, k = f.pos; + v(k === a.byteOffset); + var l = a.tagCode, r = a.byteLength; + 1 < h.traceLevel.value && console.info("Scanning tag " + e[l] + " (start: " + k + ", end: " + (k + r) + ")"); + if (39 === l) { + this.addLazySymbol(l, k, r), k += r, f.pos += 4, this.scanTagsToOffset(k, !1), this._dataStream.pos < w && (this.emitTagSlopWarning(a, w), f.pos = k); + } else { + if (t[l]) { + f = this.addLazySymbol(l, k, r), this.decodeEmbeddedImage(f); + } else { + if (q[l]) { + f = this.addLazySymbol(l, k, r), this.registerEmbeddedFont(f); + } else { + if (m[l]) { + this.addLazySymbol(l, k, r), this.jumpToNextTag(r); + } else { + if (g || 76 === l || 56 === l) { + if (n[l]) { + this.addControlTag(l, k, r); + } else { + switch(l) { + case 69: + this.setFileAttributes(r); + break; + case 86: + this.setSceneAndFrameLabelData(r); + break; + case 9: + this.backgroundColor = p.LowLevel.rgb(this.data, this._dataStream); + break; + case 8: + this._jpegTables || (this._jpegTables = 0 === r ? new Uint8Array(0) : this.data.subarray(f.pos, f.pos + r - 2)); + this.jumpToNextTag(r); + break; + case 82: + ; + case 72: + if (this.useAVM1) { + this.jumpToNextTag(r); + } else { + var w = k + r, k = new d; + 82 === l ? (k.flags = p.readUi32(this.data, f), k.name = p.readString(this.data, f, 0)) : (k.flags = 0, k.name = ""); + k.data = this.data.subarray(f.pos, w); + this.abcBlocks.push(k); + f.pos = w; + } + break; + case 76: + w = k + r; + for (r = p.readUi16(this.data, f);r--;) { + k = p.readUi16(this.data, f), l = p.readString(this.data, f, 0), 0 < h.traceLevel.value && console.log("Registering symbol class " + l + " to symbol " + k), this.symbolClassesMap[k] = l, this.symbolClassesList.push({id:k, className:l}); + } + f.pos = w; + break; + case 59: + this.useAVM1 && (w = this._currentInitActionBlocks || (this._currentInitActionBlocks = []), f = this._dataView.getUint16(f.pos, !0), w.push({spriteId:f, actionsData:this.data.subarray(k + 2, k + r)})); + this.jumpToNextTag(r); + break; + case 12: + this.useAVM1 && (this._currentActionBlocks || (this._currentActionBlocks = [])).push(this.data.subarray(f.pos, f.pos + r)); + this.jumpToNextTag(r); + break; + case 18: + ; + case 45: + f = p.LowLevel.soundStreamHead(this.data, this._dataStream); + this._currentSoundStreamHead = p.SoundStream.FromTag(f); + break; + case 19: + this._currentSoundStreamBlock = this.data.subarray(f.pos, f.pos += r); + break; + case 43: + w = f.pos + r; + this._currentFrameLabel = p.readString(this.data, f, 0); + f.pos = w; + break; + case 1: + this.finishFrame(); + break; + case 0: + break; + case 56: + w = f.pos + r; + r = p.readUi16(this.data, f); + for (l = this._currentExports || (this._currentExports = []);r--;) { + var k = p.readUi16(this.data, f), s = p.readString(this.data, f, 0); + if (f.pos > w) { + f.pos = w; + break; + } + l.push(new b(k, s)); + } + f.pos = w; + break; + case 23: + ; + case 17: + ; + case 13: + ; + case 62: + ; + case 78: + ; + case 57: + ; + case 71: + c.Debug.warning("Unsupported tag encountered " + l + ": " + e[l]); + this.jumpToNextTag(r); + break; + case 74: + ; + case 73: + ; + case 65: + ; + case 66: + this.jumpToNextTag(r); + break; + case 58: + ; + case 64: + ; + case 63: + ; + case 88: + ; + case 41: + ; + case 77: + ; + case 24: + this.jumpToNextTag(r); + break; + case 51: + ; + case 44: + ; + case 50: + ; + case 53: + ; + case 42: + ; + case 38: + ; + case 52: + ; + case 3: + ; + case 31: + ; + case 47: + ; + case 16: + ; + case 29: + console.info("Ignored tag (these shouldn't occur) " + l + ": " + e[l]); + this.jumpToNextTag(r); + break; + default: + c.Debug.warning("Tag not handled by the parser: " + l + ": " + e[l]), this.jumpToNextTag(r); + } + } + } else { + this.jumpToNextTag(r); + } + } } - __extends(a, b); - a.prototype.updateAfterEvent = function() { - k("public flash.events.TimerEvent::updateAfterEvent"); - }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.TIMER = "timer"; - a.TIMER_COMPLETE = "timerComplete"; - return a; - }(f.events.Event); - g.TimerEvent = m; - })(f.events || (f.events = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, f, g, e, m, l, u, w, r, h, x, y, t, I) { - b.call(this, void 0, void 0, void 0); - k("Dummy Constructor: public flash.events.TouchEvent"); + } + } + }; + k.prototype.parseSpriteTimeline = function(b) { + h.enterTimeline("parseSpriteTimeline"); + var a = this.data, d = this._dataStream, e = this._dataView, k = {id:b.id, type:"sprite", frames:[]}, m = b.byteOffset + b.byteLength, l = k.frames, r = null, n = [], w = null, q = null, t = null, s = null; + d.pos = b.byteOffset + 2; + k.frameCount = e.getUint16(d.pos, !0); + d.pos += 2; + for (b = new g(0, 0, 0);d.pos < m;) { + this.parseNextTagHeader(b); + var z = b.byteLength, u = b.tagCode; + if (d.pos + z > m) { + c.Debug.warning("DefineSprite child tags exceed DefineSprite tag length and are dropped"); + break; + } + if (p.ControlTags[u]) { + n.push(new g(u, d.pos, z)), d.pos += z; + } else { + switch(u) { + case 12: + this.useAVM1 && (t || (t = []), t.push(a.subarray(d.pos, d.pos + z))); + break; + case 59: + this.useAVM1 && (s || (s = []), u = e.getUint16(d.pos, !0), d.pos += 2, s.push({spriteId:u, actionsData:a.subarray(d.pos, d.pos + z)})); + break; + case 43: + z = d.pos + z; + r = p.readString(a, d, 0); + d.pos = z; + z = 0; + break; + case 1: + l.push(new f(n, r, w, q, t, s, null)); + r = null; + n = []; + s = t = q = w = null; + break; + case 0: + d.pos = m, z = 0; } - __extends(a, b); - a.prototype.updateAfterEvent = function() { - k("public flash.events.TouchEvent::updateAfterEvent"); - }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.TOUCH_BEGIN = "touchBegin"; - a.TOUCH_END = "touchEnd"; - a.TOUCH_MOVE = "touchMove"; - a.TOUCH_OVER = "touchOver"; - a.TOUCH_OUT = "touchOut"; - a.TOUCH_ROLL_OVER = "touchRollOver"; - a.TOUCH_ROLL_OUT = "touchRollOut"; - a.TOUCH_TAP = "touchTap"; - a.PROXIMITY_BEGIN = "proximityBegin"; - a.PROXIMITY_END = "proximityEnd"; - a.PROXIMITY_MOVE = "proximityMove"; - a.PROXIMITY_OUT = "proximityOut"; - a.PROXIMITY_OVER = "proximityOver"; - a.PROXIMITY_ROLL_OUT = "proximityRollOut"; - a.PROXIMITY_ROLL_OVER = "proximityRollOver"; - return a; - }(f.events.Event); - g.TouchEvent = m; - })(f.events || (f.events = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, f, g, e) { - b.call(this, void 0, void 0, void 0, void 0, void 0); - k("Dummy Constructor: public flash.events.UncaughtErrorEvent"); + d.pos += z; + v(d.pos <= m); + } + } + h.leaveTimeline(); + return k; + }; + k.prototype.jumpToNextTag = function(b) { + this._dataStream.pos += b; + }; + k.prototype.emitTagSlopWarning = function(b, a) { + var d = this._dataStream.pos - b.byteOffset; + c.Debug.warning("Scanning " + e[b.tagCode] + " at offset " + b.byteOffset + " consumed " + d + " of " + b.byteLength + " bytes. (" + (b.byteLength - d) + " left)"); + this._dataStream.pos = a; + }; + k.prototype.finishFrame = function() { + 0 === this.pendingUpdateDelays && this.framesLoaded++; + this.frames.push(new f(this._currentControlTags, this._currentFrameLabel, this._currentSoundStreamHead, this._currentSoundStreamBlock, this._currentActionBlocks, this._currentInitActionBlocks, this._currentExports)); + this._currentExports = this._currentInitActionBlocks = this._currentActionBlocks = this._currentSoundStreamBlock = this._currentSoundStreamHead = this._currentControlTags = this._currentFrameLabel = null; + }; + k.prototype.setFileAttributes = function(b) { + this.attributes && this.jumpToNextTag(b); + b = this.data[this._dataStream.pos]; + this._dataStream.pos += 4; + this.attributes = {network:b & 1, relativeUrls:b & 2, noCrossDomainCaching:b & 4, doAbc:b & 8, hasMetadata:b & 16, useGpu:b & 32, useDirectBlit:b & 64}; + this.useAVM1 = !this.attributes.doAbc; + }; + k.prototype.setSceneAndFrameLabelData = function(b) { + this.sceneAndFrameLabelData && this.jumpToNextTag(b); + this.sceneAndFrameLabelData = p.LowLevel.defineScene(this.data, this._dataStream, null); + }; + k.prototype.addControlTag = function(b, a, d) { + (this._currentControlTags || (this._currentControlTags = [])).push(new g(b, a, d)); + this.jumpToNextTag(d); + }; + k.prototype.addLazySymbol = function(b, a, d) { + var g = this._dataStream.getUint16(this._dataStream.pos, !0); + a = new r(g, b, a, d); + this.dictionary[g] = a; + 0 < h.traceLevel.value && console.info("Registering symbol " + g + " of type " + e[b]); + return a; + }; + k.prototype.decodeEmbeddedFont = function(b) { + var d = this.getParsedTag(b); + b = new w(d.id, b, "font", d); + 0 < h.traceLevel.value && console.info("Decoding embedded font " + d.id + " with name '" + d.name + "'", d); + this.eagerlyParsedSymbolsMap[b.id] = b; + this.eagerlyParsedSymbolsList.push(b); + this.fonts.push({name:d.name, id:d.id, style:a(d.bold, d.italic)}); + }; + k.prototype.registerEmbeddedFont = function(b) { + if (inFirefox) { + var d = this._dataStream, g = d.getUint16(d.pos, !0), e, f; + 10 === b.tagCode ? (f = "__autofont__" + b.byteOffset, e = "regular") : (e = this.data[d.pos + 2], e = a(!!(e & 2), !!(e & 1)), f = this.data[d.pos + 4], d.pos += 5, f = p.readString(this.data, d, f)); + this.fonts.push({name:f, id:g, style:e}); + 0 < h.traceLevel.value && console.info("Registering embedded font " + g + " with name '" + f + "'"); + d.pos = b.byteOffset + b.byteLength; + } else { + this.decodeEmbeddedFont(b); + } + }; + k.prototype.decodeEmbeddedImage = function(b) { + var a = this.getParsedTag(b), d = new w(a.id, b, "image", a); + 0 < h.traceLevel.value && console.info("Decoding embedded image " + a.id + " of type " + e[b.tagCode] + " (start: " + b.byteOffset + ", end: " + (b.byteOffset + b.byteLength) + ")"); + this.eagerlyParsedSymbolsMap[d.id] = d; + this.eagerlyParsedSymbolsList.push(d); + }; + return k; + }(); + h.SWFFile = k; + var f = function() { + return function(b, a, d, g, e, f, k) { + b && Object.freeze(b); + this.controlTags = b; + this.labelName = a; + e && Object.freeze(e); + this.soundStreamHead = d; + this.soundStreamBlock = g; + this.actionBlocks = e; + f && Object.freeze(f); + this.initActionBlocks = f; + k && Object.freeze(k); + this.exports = k; + }; + }(); + h.SWFFrame = f; + var d = function() { + return function() { + }; + }(); + h.ABCBlock = d; + h.InitActionBlock = function() { + return function() { + }; + }(); + var b = function() { + return function(b, a) { + this.symbolId = b; + this.className = a; + }; + }(); + h.SymbolExport = b; + var g = function() { + return function(b, a, d) { + this.tagCode = b; + this.byteOffset = a; + this.byteLength = d; + }; + }(); + h.UnparsedTag = g; + var r = function(b) { + function a(d, g, e, f) { + b.call(this, g, e, f); + this.id = d; + } + __extends(a, b); + return a; + }(g); + h.DictionaryEntry = r; + var w = function(b) { + function a(d, g, e, f) { + b.call(this, d, g.tagCode, g.byteOffset, g.byteLength); + this.type = e; + this.definition = f; + this.ready = !1; + } + __extends(a, b); + return a; + }(r); + h.EagerlyParsedDictionaryEntry = w; + })(c.SWF || (c.SWF = {})); +})(Shumway || (Shumway = {})); +(function(c) { + var h; + (function(a) { + a[a.JPG = 16767231] = "JPG"; + a[a.PNG = 8998990] = "PNG"; + a[a.GIF = 4671814] = "GIF"; + })(h || (h = {})); + var a = {16767231:"image/jpeg", 8998990:"image/png", 4671814:"image/gif"}; + h = function() { + function c(a, h) { + this.type = 4; + this.bytesLoaded = a.length; + a.length === h ? this.data = a : (this.data = new Uint8Array(h), this.data.set(a)); + this.setMimetype(); + } + Object.defineProperty(c.prototype, "bytesTotal", {get:function() { + return this.data.length; + }, enumerable:!0, configurable:!0}); + c.prototype.appendLoadedData = function(a) { + this.data.set(a, this.bytesLoaded); + this.bytesLoaded += a.length; + }; + c.prototype.setMimetype = function() { + this.mimeType = a[this.data[0] << 16 | this.data[1] << 8 | this.data[2]]; + }; + return c; + }(); + c.ImageFile = h; +})(Shumway || (Shumway = {})); +(function(c) { + var h = c.Debug.assert, a = c.SWF.SWFFile, s = function() { + return function(a, c) { + this.bytesLoaded = a; + this.framesLoaded = c; + }; + }(); + c.LoadProgressUpdate = s; + var v = function() { + function p(a) { + h(a); + this._file = null; + this._listener = a; + this._delayedUpdatesPromise = this._loadingServiceSession = null; + this._bytesLoaded = 0; + } + p.prototype.loadFile = function(a) { + this._bytesLoaded = 0; + var e = this._loadingServiceSession = c.FileLoadingService.instance.createSession(); + e.onopen = this.processLoadOpen.bind(this); + e.onprogress = this.processNewData.bind(this); + e.onerror = this.processError.bind(this); + e.onclose = this.processLoadClose.bind(this); + e.open(a); + }; + p.prototype.abortLoad = function() { + }; + p.prototype.loadBytes = function(a) { + this.processLoadOpen(); + this.processNewData(a, {bytesLoaded:a.length, bytesTotal:a.length}); + this.processLoadClose(); + }; + p.prototype.processLoadOpen = function() { + h(!this._file); + }; + p.prototype.processNewData = function(l, e) { + this._bytesLoaded += l.length; + if (8192 > this._bytesLoaded && this._bytesLoaded < e.bytesTotal) { + this._queuedInitialData || (this._queuedInitialData = new Uint8Array(Math.min(8192, e.bytesTotal))), this._queuedInitialData.set(l, this._bytesLoaded - l.length); + } else { + if (this._queuedInitialData) { + var m = new Uint8Array(this._bytesLoaded); + m.set(this._queuedInitialData); + m.set(l, this._bytesLoaded - l.length); + l = m; + this._queuedInitialData = null; + } + var t = this._file, q = m = 0; + if (t) { + t instanceof a && (m = t.eagerlyParsedSymbolsList.length, q = t.framesLoaded), t.appendLoadedData(l); + } else { + var t = l, n = e.bytesTotal, k = t[0] << 16 | t[1] << 8 | t[2], t = 22355 === (k & 65535) ? new a(t, n) : 16767231 === k || 8998990 === k || 4671814 === k ? new c.ImageFile(t, n) : null; + t = this._file = t; + this._listener.onLoadOpen(t); + } + if (t instanceof a) { + this.processSWFFileUpdate(t, m, q); + } else { + if (h(t instanceof c.ImageFile), this._listener.onLoadProgress(new s(e.bytesLoaded, -1)), e.bytesLoaded === e.bytesTotal) { + this._listener.onImageBytesLoaded(); + } + } + } + }; + p.prototype.processError = function(a) { + c.Debug.warning("Loading error encountered:", a); + }; + p.prototype.processLoadClose = function() { + this._file.bytesLoaded !== this._file.bytesTotal && c.Debug.warning("Not Implemented: processing loadClose when loading was aborted"); + }; + p.prototype.processSWFFileUpdate = function(a, e, m) { + var t; + if (e = a.eagerlyParsedSymbolsList.length - e) { + t = this._listener.onNewEagerlyParsedSymbols(a.eagerlyParsedSymbolsList, e); + this._delayedUpdatesPromise && (t = Promise.all([this._delayedUpdatesPromise, t])); + this._delayedUpdatesPromise = t; + this._lastDelayedUpdate = n = new s(a.bytesLoaded, a.frames.length); + a.pendingUpdateDelays++; + var q = this; + a.framesLoaded = m; + t.then(function() { + 0 < c.SWF.traceLevel.value && console.log("Reducing pending update delays from " + a.pendingUpdateDelays + " to " + (a.pendingUpdateDelays - 1)); + a.pendingUpdateDelays--; + h(0 <= a.pendingUpdateDelays); + a.framesLoaded = n.framesLoaded; + q._listener.onLoadProgress(n); + q._delayedUpdatesPromise === t && (q._delayedUpdatesPromise = null, q._lastDelayedUpdate = null); + }); + } else { + var n = this._lastDelayedUpdate; + n ? (h(n.framesLoaded <= a.frames.length), n.bytesLoaded = a.bytesLoaded, n.framesLoaded = a.frames.length) : (h(a.framesLoaded === a.frames.length), this._listener.onLoadProgress(new s(a.bytesLoaded, a.framesLoaded))); + } + }; + return p; + }(); + c.FileLoader = v; + var p; + (function(a) { + a[a.SWF = 22355] = "SWF"; + a[a.JPG = 16767231] = "JPG"; + a[a.PNG = 8998990] = "PNG"; + a[a.GIF = 4671814] = "GIF"; + })(p || (p = {})); +})(Shumway || (Shumway = {})); +console.timeEnd("Load SWF Parser"); +console.time("Load Flash TS Dependencies"); +(function(c) { + function h(a) { + var e = {}; + a = a.split(","); + for (var k = 0;k < a.length;k++) { + e[a[k]] = !0; + } + return e; + } + var a = /^<([-A-Za-z0-9_]+)((?:\s+[-A-Za-z0-9_]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/, s = /^<\/([-A-Za-z0-9_]+)[^>]*>/, v = /([-A-Za-z0-9_]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g, p = h("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed"), u = h("address,applet,blockquote,button,center,dd,del,dir,div,dl,dt,fieldset,form,frameset,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul"), + l = h("a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var"), e = h("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr"), m = h("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected"), t = h("script,style"); + c.HTMLParser = function(c, n) { + function k() { + return this[this.length - 1]; + } + function f(b, a, g, f) { + a = a.toLowerCase(); + if (u[a]) { + for (;k() && l[k()];) { + d("", k()); + } + } + e[a] && k() == a && d("", a); + (f = p[a] || !!f) || r.push(a); + if (n.start) { + var c = Object.create(null); + g.replace(v, function(b, a, d, g, e) { + a = a.toLowerCase(); + c[a] = d ? d : g ? g : e ? e : m[a] ? a : ""; + return b; + }); + n.start && n.start(a, c, !!f); + } + } + function d(b, a) { + if (a) { + for (d = r.length - 1;0 <= d && r[d] != a;d--) { + } + } else { + var d = 0 + } + if (0 <= d) { + for (var g = r.length - 1;g >= d;g--) { + n.end && n.end(r[g]); + } + r.length = d; + } + } + for (var b, g, r = [], w = c;c;) { + g = !0; + if (k() && t[k()]) { + c = c.replace(new RegExp("(.*)]*>"), function(b, a) { + a = a.replace(/\x3c!--(.*?)--\x3e/g, "$1").replace(/ r._advancableInstances.length, "Too many advancable instances."); - r._advancableInstances.forEach(function(b) { - b._initFrame(a); - }); - a && b && r._broadcastFrameEvent(l.Event.ENTER_FRAME); - r._advancableInstances.forEach(function(a) { - a._constructFrame(); - }); - b ? (r._broadcastFrameEvent(l.Event.FRAME_CONSTRUCTED), r._advancableInstances.forEach(function(a) { - g.MovieClip.isInstanceOf(a) && !a.parent && a._enqueueFrameScripts(); - }), f.display.DisplayObject._stage._enqueueFrameScripts(), g.MovieClip.runFrameScripts(), r._broadcastFrameEvent(l.Event.EXIT_FRAME)) : g.MovieClip.reset(); - a && (k.leaveTimeline(), r._runScripts = !0); - }; - r._broadcastFrameEvent = function(a) { - var b; - switch(a) { - case l.Event.ENTER_FRAME: - ; - case l.Event.FRAME_CONSTRUCTED: - ; - case l.Event.EXIT_FRAME: - ; - case l.Event.RENDER: - b = l.Event.getBroadcastInstance(a); - } - p(b, "Invalid frame event."); - l.EventDispatcher.broadcastEventDispatchQueue.dispatchEvent(b); - }; - r.prototype._setInitialName = function() { - this._name = "instance" + f.display.DisplayObject._instanceID++; - }; - r.prototype._setParent = function(a, b) { - var c = this._parent; - p(a !== this); - this._parent = a; - this._depth = b; - a && (this._addReference(), a && this._hasAnyFlags(12288) && a._propagateFlagsUp(8192)); - c && this._removeReference(); - }; - r.prototype._setFillAndLineBoundsFromWidthAndHeight = function(a, b) { - this._fillBounds.width = a; - this._fillBounds.height = b; - this._lineBounds.width = a; - this._lineBounds.height = b; - this._removeFlags(6); - this._invalidateParentFillAndLineBounds(!0, !0); - }; - r.prototype._setFillAndLineBoundsFromSymbol = function(a) { - p(a.fillBounds || a.lineBounds, "Fill or Line bounds are not defined in the symbol."); - a.fillBounds && (this._fillBounds.copyFrom(a.fillBounds), this._removeFlags(4)); - a.lineBounds && (this._lineBounds.copyFrom(a.lineBounds), this._removeFlags(2)); - this._invalidateParentFillAndLineBounds(!!a.fillBounds, !!a.lineBounds); - }; - r.prototype._setFlags = function(a) { - this._displayObjectFlags |= a; - }; - r.prototype._setDirtyFlags = function(a) { - this._displayObjectFlags |= a; - this._parent && this._parent._propagateFlagsUp(2097152); - }; - r.prototype._toggleFlags = function(a, b) { - this._displayObjectFlags = b ? this._displayObjectFlags | a : this._displayObjectFlags & ~a; - }; - r.prototype._removeFlags = function(a) { - this._displayObjectFlags &= ~a; - }; - r.prototype._hasFlags = function(a) { - return(this._displayObjectFlags & a) === a; - }; - r.prototype._hasAnyFlags = function(a) { - return!!(this._displayObjectFlags & a); - }; - r.prototype._propagateFlagsUp = function(a) { - if (!this._hasFlags(a)) { - this._setFlags(a); - var b = this._parent; - b && b._propagateFlagsUp(a); - } - }; - r.prototype._propagateFlagsDown = function(a) { - this._setFlags(a); - }; - r.prototype._findNearestAncestor = function() { - for (var a = this;a;) { - if (!1 === a._hasFlags(128)) { - return a; - } - a = a._parent; + } else { + 0 == c.indexOf("<") && (b = c.match(a)) && (c = c.substring(b[0].length), b[0].replace(a, f), g = !1); + } + } + g && (b = c.indexOf("<"), g = 0 > b ? c : c.substring(0, b), c = 0 > b ? "" : c.substring(b), n.chars && n.chars(g)); + } + if (c == w) { + throw "Parse Error: " + c; + } + w = c; + } + d(); + }; +})(Shumway || (Shumway = {})); +(function(c) { + var h = c.Debug.notImplemented, a = c.Debug.somewhatImplemented, s = c.Bounds, v = c.ArrayUtilities.DataBuffer, p = c.ColorUtilities, u = c.AVM2.AS.flash; + (function(a) { + a[a.None = 0] = "None"; + a[a.DirtyBounds = 1] = "DirtyBounds"; + a[a.DirtyContent = 2] = "DirtyContent"; + a[a.DirtyStyle = 4] = "DirtyStyle"; + a[a.DirtyFlow = 8] = "DirtyFlow"; + a[a.Dirty = a.DirtyBounds | a.DirtyContent | a.DirtyStyle | a.DirtyFlow] = "Dirty"; + })(c.TextContentFlags || (c.TextContentFlags = {})); + var l = {lt:"<", gt:">", amp:"&", quot:'"', apos:"'", nbsp:"\u00a0"}, e = function() { + function e(a) { + this._id = u.display.DisplayObject.getNextSyncID(); + this._bounds = new s(0, 0, 0, 0); + this._plainText = ""; + this._autoSize = this._borderColor = this._backgroundColor = 0; + this._wordWrap = !1; + this._scrollV = 1; + this.flags = this._scrollH = 0; + this.defaultTextFormat = a || new u.text.TextFormat; + this.textRuns = []; + this.textRunData = new v; + this.coords = this.matrix = null; + } + e.prototype.parseHtml = function(e, m, n) { + var k = "", f = this.textRuns, d = f.length = 0, b = 0, g = this.defaultTextFormat.clone(), r = null, w = [], s; + c.HTMLParser(e, s = {chars:function(a) { + for (var e = "", m = 0;m < a.length;m++) { + var n = a.charAt(m); + if ("&" !== n) { + e += n; + } else { + if (n = c.StringUtilities.indexOfAny(a, ["&", ";"], m + 1), 0 < n) { + m = a.substring(m + 1, n); + if (1 < m.length && "#" === m.charAt(0)) { + var w = 0, w = 2 < m.length && "x" === m.charAt(1) ? parseInt(m.substring(1)) : parseInt(m.substring(2), 16), e = e + String.fromCharCode(w) + } else { + void 0 !== l[m] ? e += l[m] : c.Debug.unexpected(m); } - return null; - }; - r.prototype._findFurthestAncestorOrSelf = function() { - for (var a = this;a;) { - if (!a._parent) { - return a; + m = n; + } else { + for (var q in l) { + if (a.indexOf(q, m + 1) === m + 1) { + e += l[q]; + m += q.length; + break; } - a = a._parent; } - }; - r.prototype._isAncestor = function(a) { - for (;a;) { - if (a === this) { - return!0; + } + } + } + a = e; + k += a; + b += a.length; + b - d && (r && r.textFormat.equals(g) ? r.endIndex = b : (r = new u.text.TextRun(d, b, g), f.push(r)), d = b); + }, start:function(b, d) { + var e = !1; + m && (e = m.hasStyle(b)) && (w.push(g), g = g.clone(), m.applyStyle(g, b)); + switch(b) { + case "a": + w.push(g); + a(""); + var f = d.target || g.target, c = d.url || g.url; + if (f !== g.target || c !== g.url) { + e || (g = g.clone()), g.target = f, g.url = c; + } + break; + case "b": + w.push(g); + g.bold || (e || (g = g.clone()), g.bold = !0); + break; + case "font": + w.push(g); + var f = p.isValidHexColor(d.color) ? p.hexToRGB(d.color) : g.color, c = d.face || g.font, l = isNaN(d.size) ? g.size : +d.size; + if (f !== g.color || c !== g.font || l !== g.size) { + e || (g = g.clone()), g.color = f, g.font = c, g.size = l; + } + break; + case "img": + h(""); + break; + case "i": + w.push(g); + g.italic || (e || (g = g.clone()), g.italic = !0); + break; + case "li": + if (w.push(g), g.bullet || (e || (g = g.clone()), g.bullet = !0), "\r" === k[k.length - 1]) { + break; + } + ; + case "br": + n && s.chars("\r"); + break; + case "span": + ; + case "p": + f = !1; + w.push(g); + m && d.class && (c = "." + d.class, f = m.hasStyle(c)) && (e || (g = g.clone()), m.applyStyle(g, c)); + if ("span" === b) { + break; + } + c = d.align; + -1 < u.text.TextFormatAlign.toNumber(c) && c !== g.align && (e || f || (g = g.clone()), g.align = c); + break; + case "textformat": + w.push(g); + var f = isNaN(d.blockindent) ? g.blockIndent : +d.blockindent, c = isNaN(d.indent) ? g.indent : +d.indent, l = isNaN(d.leading) ? g.leading : +d.leading, r = isNaN(d.leftmargin) ? g.leftMargin : +d.leftmargin, t = isNaN(d.rightmargin) ? g.rightMargin : +d.rightmargin; + if (f !== g.blockIndent || c !== g.indent || l !== g.leading || r !== g.leftMargin || t !== g.rightMargin) { + e || (g = g.clone()), g.blockIndent = f, g.indent = c, g.leading = l, g.leftMargin = r, g.rightMargin = t; + } + break; + case "u": + w.push(g), g.underline || (e || (g = g.clone()), g.underline = !0); + } + }, end:function(b) { + switch(b) { + case "li": + ; + case "p": + n && s.chars("\r"); + case "a": + ; + case "b": + ; + case "font": + ; + case "i": + ; + case "textformat": + ; + case "u": + g = w.pop(), m && m.hasStyle(b) && (g = w.pop()); + } + }}); + this._plainText = k; + this._serializeTextRuns(); + }; + Object.defineProperty(e.prototype, "plainText", {get:function() { + return this._plainText; + }, set:function(a) { + this._plainText = a; + this.textRuns.length = 0; + a && (a = new u.text.TextRun(0, a.length, this.defaultTextFormat), this.textRuns[0] = a); + this._serializeTextRuns(); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "bounds", {get:function() { + return this._bounds; + }, set:function(a) { + this._bounds.copyFrom(a); + this.flags |= 1; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "autoSize", {get:function() { + return this._autoSize; + }, set:function(a) { + a !== this._autoSize && (this._autoSize = a, this._plainText && (this.flags |= 8)); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "wordWrap", {get:function() { + return this._wordWrap; + }, set:function(a) { + a !== this._wordWrap && (this._wordWrap = a, this._plainText && (this.flags |= 8)); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "scrollV", {get:function() { + return this._scrollV; + }, set:function(a) { + a !== this._scrollV && (this._scrollV = a, this._plainText && (this.flags |= 8)); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "scrollH", {get:function() { + return this._scrollH; + }, set:function(a) { + a !== this._scrollH && (this._scrollH = a, this._plainText && (this.flags |= 8)); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "backgroundColor", {get:function() { + return this._backgroundColor; + }, set:function(a) { + a !== this._backgroundColor && (this._backgroundColor = a, this.flags |= 4); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "borderColor", {get:function() { + return this._borderColor; + }, set:function(a) { + a !== this._borderColor && (this._borderColor = a, this.flags |= 4); + }, enumerable:!0, configurable:!0}); + e.prototype._serializeTextRuns = function() { + var a = this.textRuns; + this.textRunData.clear(); + for (var e = 0;e < a.length;e++) { + this._writeTextRun(a[e]); + } + this.flags |= 2; + }; + e.prototype._writeTextRun = function(a) { + var e = this.textRunData; + e.writeInt(a.beginIndex); + e.writeInt(a.endIndex); + a = a.textFormat; + var c = +a.size; + e.writeInt(c); + var k = u.text.Font.getByNameAndStyle(a.font, a.style) || u.text.Font.getDefaultFont(); + k.fontType === u.text.FontType.EMBEDDED ? e.writeUTF("swffont" + k._id) : e.writeUTF(k._fontFamily); + e.writeInt(k.ascent * c); + e.writeInt(k.descent * c); + e.writeInt(null === a.leading ? k.leading * c : +a.leading); + var f = c = !1; + k.fontType === u.text.FontType.DEVICE && (c = null === a.bold ? k.fontStyle === u.text.FontStyle.BOLD || k.fontType === u.text.FontStyle.BOLD_ITALIC : !!a.bold, f = null === a.italic ? k.fontStyle === u.text.FontStyle.ITALIC || k.fontType === u.text.FontStyle.BOLD_ITALIC : !!a.italic); + e.writeBoolean(c); + e.writeBoolean(f); + e.writeInt(+a.color); + e.writeInt(u.text.TextFormatAlign.toNumber(a.align)); + e.writeBoolean(!!a.bullet); + e.writeInt(+a.indent); + e.writeInt(+a.kerning); + e.writeInt(+a.leftMargin); + e.writeInt(+a.letterSpacing); + e.writeInt(+a.rightMargin); + e.writeBoolean(!!a.underline); + }; + e.prototype.appendText = function(a, e) { + e || (e = this.defaultTextFormat); + var c = this._plainText, k = new u.text.TextRun(c.length, c.length + a.length, e); + this._plainText = c + a; + this.textRuns.push(k); + this._writeTextRun(k); + }; + e.prototype.prependText = function(a, e) { + e || (e = this.defaultTextFormat); + this._plainText = a + this._plainText; + for (var c = this.textRuns, k = a.length, f = 0;f < c.length;f++) { + var d = c[f]; + d.beginIndex += k; + d.endIndex += k; + } + c.unshift(new u.text.TextRun(0, k, e)); + this._serializeTextRuns(); + }; + e.prototype.replaceText = function(a, e, c, k) { + if (!(e < a) && c) { + if (0 === e) { + this.prependText(c, k); + } else { + var f = this._plainText; + if (a >= f.length) { + this.appendText(c, k); + } else { + var d = this.defaultTextFormat, b = d; + k && (b = b.clone(), b.merge(k)); + if (0 >= a && e >= f.length) { + k ? (this.defaultTextFormat = b, this.plainText = c, this.defaultTextFormat = d) : this.plainText = c; + } else { + for (var d = this.textRuns, g = [], m = a + c.length, l = m - e, h = 0;h < d.length;h++) { + var s = d[h]; + if (a < s.endIndex) { + if (a <= s.beginIndex && m >= s.endIndex) { + continue; + } + var p = s.containsIndex(a), v = s.containsIndex(e); + if (p && v) { + if (k) { + p = s.clone(); + p.endIndex = a; + g.push(p); + h--; + s.beginIndex = a + 1; + continue; + } + } else { + p ? s.endIndex = a : v ? k ? (g.push(new u.text.TextRun(a, m, b)), s.beginIndex = m) : (s.beginIndex = a, s.endIndex += l) : (s.beginIndex += l, s.endIndex += l); + } } - a = a._parent; + g.push(s); } - return!1; - }; - r._clampRotation = function(a) { - a %= 360; - 180 < a ? a -= 360 : -180 > a && (a += 360); - return a; - }; - r._getAncestors = function(a, b) { - var c = r._path; - for (c.length = 0;a && a !== b;) { - c.push(a), a = a._parent; + this._plainText = f.substring(0, a) + c + f.substring(e); + this.textRuns = g; + this._serializeTextRuns(); + } + } + } + } + }; + return e; + }(); + c.TextContent = e; +})(Shumway || (Shumway = {})); +var Shumway$$inline_755 = Shumway || (Shumway = {}), AVM2$$inline_756 = Shumway$$inline_755.AVM2 || (Shumway$$inline_755.AVM2 = {}), AS$$inline_757 = AVM2$$inline_756.AS || (AVM2$$inline_756.AS = {}); +AS$$inline_757.flashOptions = Shumway$$inline_755.Settings.shumwayOptions.register(new Shumway$$inline_755.Options.OptionSet("Flash Options")); +AS$$inline_757.traceEventsOption = AS$$inline_757.flashOptions.register(new Shumway$$inline_755.Options.Option("te", "Trace Events", "boolean", !1, "Trace dispatching of events.")); +AS$$inline_757.traceLoaderOption = AS$$inline_757.flashOptions.register(new Shumway$$inline_755.Options.Option("tp", "Trace Loader", "boolean", !1, "Trace loader execution.")); +AS$$inline_757.disableAudioOption = AS$$inline_757.flashOptions.register(new Shumway$$inline_755.Options.Option("da", "Disable Audio", "boolean", !1, "Disables audio.")); +AS$$inline_757.webAudioOption = AS$$inline_757.flashOptions.register(new Shumway$$inline_755.Options.Option(null, "Use WebAudio for Sound", "boolean", !1, "Enables WebAudio API for MovieClip sound stream. (MP3 format is an exception)")); +AS$$inline_757.webAudioMP3Option = AS$$inline_757.flashOptions.register(new Shumway$$inline_755.Options.Option(null, "Use MP3 decoding to WebAudio", "boolean", !1, "Enables WebAudio API and software MP3 decoding and disables any AUDIO tag usage for MP3 format")); +AS$$inline_757.mediaSourceOption = AS$$inline_757.flashOptions.register(new Shumway$$inline_755.Options.Option(null, "Use Media Source for Video", "boolean", !1, "Enables Media Source Extension API for NetStream.")); +AS$$inline_757.mediaSourceMP3Option = AS$$inline_757.flashOptions.register(new Shumway$$inline_755.Options.Option(null, "Use Media Source for MP3", "boolean", !0, "Enables Media Source Extension API for MP3 streams.")); +__extends = this.__extends || function(c, h) { + function a() { + this.constructor = c; + } + for (var s in h) { + h.hasOwnProperty(s) && (c[s] = h[s]); + } + a.prototype = h.prototype; + c.prototype = new a; +}; +(function(c) { + (function(h) { + var a = c.isInteger, s = c.Debug.assert, v = c.Debug.warning, p = c.Bounds, u = c.AVM2.AS.flash, l = function() { + function e(m, l) { + s(a(m.id)); + this.data = m; + if (m.className) { + var n = c.AVM2.Runtime.AVM2.instance.applicationDomain; + try { + this.symbolClass = n.getClass(m.className); + } catch (k) { + v("Symbol " + m.id + " bound to non-existing class " + m.className), this.symbolClass = l; + } + } else { + this.symbolClass = l; + } + this.isAVM1Object = !1; + } + Object.defineProperty(e.prototype, "id", {get:function() { + return this.data.id; + }, enumerable:!0, configurable:!0}); + return e; + }(); + h.Symbol = l; + var e = function(a) { + function e(c, l, k) { + a.call(this, c, l); + this.dynamic = k; + } + __extends(e, a); + e.prototype._setBoundsFromData = function(a) { + this.fillBounds = a.fillBounds ? p.FromUntyped(a.fillBounds) : null; + this.lineBounds = a.lineBounds ? p.FromUntyped(a.lineBounds) : null; + !this.lineBounds && this.fillBounds && (this.lineBounds = this.fillBounds.clone()); + }; + return e; + }(l); + h.DisplaySymbol = e; + l = function(a) { + function e(c) { + a.call(this, c, u.utils.ByteArray); + } + __extends(e, a); + e.FromData = function(a) { + var c = new e(a); + c.buffer = a.data; + c.byteLength = a.data.byteLength; + return c; + }; + return e; + }(l); + h.BinarySymbol = l; + h.SoundStart = function() { + return function(a, e) { + this.soundId = a; + this.soundInfo = e; + }; + }(); + })(c.Timeline || (c.Timeline = {})); +})(Shumway || (Shumway = {})); +var RtmpJs; +(function(c) { + var h = function() { + function a(c) { + this.onmessage = null; + this.id = c; + this.buffer = null; + this.bufferLength = 0; + this.lastStreamId = -1; + this.lastTypeId = this.lastLength = this.lastTimestamp = 0; + this.lastMessageComplete = !1; + this.waitingForBytes = 0; + this.sentStreamId = -1; + this.sentTypeId = this.sentLength = this.sentTimestamp = 0; + } + a.prototype.setBuffer = function() { + this.buffer || (this.buffer = new Uint8Array(128), this.bufferLength = 0); + }; + a.prototype.abort = function() { + this.buffer ? this.bufferLength = 0 : this.lastMessageComplete || (this.lastMessageComplete = !0, this.onmessage({timestamp:this.lastTimestamp, streamId:this.lastStreamId, chunkedStreamId:this.id, typeId:this.lastTypeId, data:null, firstChunk:!1, lastChunk:!0})); + }; + a.prototype._push = function(a, c, h) { + if (this.onmessage) { + if (c && h || !this.buffer) { + this.onmessage({timestamp:this.lastTimestamp, streamId:this.lastStreamId, chunkedStreamId:this.id, typeId:this.lastTypeId, data:a, firstChunk:c, lastChunk:h}); + } else { + if (c && (this.bufferLength = 0, this.lastLength > this.buffer.length && (this.buffer = new Uint8Array(this.lastLength))), this.buffer.set(a, this.bufferLength), this.bufferLength += a.length, h) { + this.onmessage({timestamp:this.lastTimestamp, streamId:this.lastStreamId, chunkedStreamId:this.id, typeId:this.lastTypeId, data:this.buffer.subarray(0, this.bufferLength), firstChunk:!0, lastChunk:!0}); + } + } + } + }; + return a; + }(); + c.ChunkedStream = h; + var a = function() { + function a() { + this.onack = this.onusercontrolmessage = null; + this.ondata = function(a) { + }; + this.onclose = function() { + }; + this.oncreated = null; + this.state = "uninitialized"; + this.buffer = new Uint8Array(4092); + this.bufferLength = 0; + this.chunkSize = 128; + this.chunkStreams = []; + this.peerChunkSize = 128; + this.lastAckSent = this.bytesReceived = this.windowAckSize = this.bandwidthLimitType = this.peerAckWindowSize = 0; + } + a.prototype.push = function(a) { + var c = a.length + this.bufferLength; + if (c > this.buffer.length) { + for (var h = 2 * this.buffer.length;c > h;) { + h *= 2; + } + 524288 < h && this._fail("Buffer overflow"); + h = new Uint8Array(h); + h.set(this.buffer); + this.buffer = h; + } + for (var h = 0, l = this.bufferLength;h < a.length;h++, l++) { + this.buffer[l] = a[h]; + } + this.bufferLength = c; + this.bytesReceived += a.length; + for (this.peerAckWindowSize && this.bytesReceived - this.lastAckSent >= this.peerAckWindowSize && this._sendAck();0 < this.bufferLength;) { + a = 0; + switch(this.state) { + case "uninitialized": + if (1 > this.bufferLength) { + return; + } + this.serverVersion = this.buffer[0]; + a = 1; + 3 !== this.serverVersion && this._fail("Unsupported protocol version: " + this.serverVersion); + this.state = "version_received"; + break; + case "version_received": + if (1536 > this.bufferLength) { + return; + } + a = 1536; + c = Date.now() - this.epochStart | 0; + this.buffer[4] = c >>> 24 & 255; + this.buffer[5] = c >>> 16 & 255; + this.buffer[6] = c >>> 8 & 255; + this.buffer[7] = c & 255; + this.ondata(this.buffer.subarray(0, 1536)); + this.state = "ack_sent"; + break; + case "ack_sent": + if (1536 > this.bufferLength) { + return; + } + a = 1536; + for (h = 8;1536 > h;h++) { + this.buffer[h] !== this.randomData[h] && this._fail("Random data do not match @" + h); + } + this.state = "handshake_done"; + this.lastAckSent = this.bytesReceived; + this._initialize(); + break; + case "handshake_done": + a = this._parseChunkedData(); + if (!a) { + return; + } + break; + default: + return; + } + this.buffer.set(this.buffer.subarray(a, this.bufferLength), 0); + this.bufferLength -= a; + } + }; + a.prototype._initialize = function() { + var a = this._getChunkStream(2); + a.setBuffer(); + a.onmessage = function(a) { + if (0 === a.streamId) { + switch(console.log("Control message: " + a.typeId), a.typeId) { + case 1: + var c = a.data[0] << 24 | a.data[1] << 16 | a.data[2] << 8 | a.data[3]; + 1 <= c && 2147483647 >= c && (this.peerChunkSize = c); + break; + case 2: + c = a.data[0] << 24 | a.data[1] << 16 | a.data[2] << 8 | a.data[3]; + 3 <= c && 65599 >= c && this._getChunkStream(c).abort(); + break; + case 3: + if (this.onack) { + this.onack(); } - p(a === b, "Last ancestor is not an ancestor."); - return c; - }; - r.prototype._getConcatenatedMatrix = function() { - this._hasFlags(32) && (this._parent ? this._parent._getConcatenatedMatrix().preMultiplyInto(this._getMatrix(), this._concatenatedMatrix) : this._concatenatedMatrix.copyFrom(this._getMatrix()), this._removeFlags(32)); - return this._concatenatedMatrix; - }; - r.prototype._getInvertedConcatenatedMatrix = function() { - this._hasFlags(64) && (this._getConcatenatedMatrix().invertInto(this._invertedConcatenatedMatrix), this._removeFlags(64)); - return this._invertedConcatenatedMatrix; - }; - r.prototype._setMatrix = function(a, b) { - if (b || !this._matrix.equals(a)) { - var c = this._matrix; - c.copyFrom(a); - b && c.toTwipsInPlace(); - this._scaleX = c.getScaleX(); - this._scaleY = c.getScaleY(); - this._rotation = r._clampRotation(180 * a.getRotation() / Math.PI); - this._removeFlags(8); - this._setFlags(16); - this._setDirtyFlags(1048576); - this._invalidatePosition(); + break; + case 4: + if (this.onusercontrolmessage) { + this.onusercontrolmessage({type:a.data[0] << 8 | a.data[1], data:a.data.subarray(2)}); } - }; - r.prototype._getMatrix = function() { - this._hasFlags(8) && (this._matrix.updateScaleAndRotation(this._scaleX, this._scaleY, this._rotation), this._removeFlags(8)); - return this._matrix; - }; - r.prototype._getInvertedMatrix = function() { - this._hasFlags(16) && (this._getMatrix().invertInto(this._invertedMatrix), this._removeFlags(16)); - return this._invertedMatrix; - }; - r.prototype._getConcatenatedColorTransform = function() { - if (!this.stage) { - return this._colorTransform.clone(); + break; + case 5: + c = a.data[0] << 24 | a.data[1] << 16 | a.data[2] << 8 | a.data[3]; + if (0 > c) { + break; } - if (this._hasFlags(128)) { - var a = this._findNearestAncestor(), b = r._getAncestors(this, a), c = b.length - 1; - f.display.Stage.isType(b[c]) && c--; - for (var d = a && !f.display.Stage.isType(a) ? a._concatenatedColorTransform.clone() : new q.ColorTransform;0 <= c;) { - a = b[c--], p(a._hasFlags(128)), d.preMultiply(a._colorTransform), d.convertToFixedPoint(), a._concatenatedColorTransform.copyFrom(d), a._removeFlags(128); + this.peerAckWindowSize = c; + break; + case 6: + c = a.data[0] << 24 | a.data[1] << 16 | a.data[2] << 8 | a.data[3]; + a = a.data[4]; + if (0 > c || 2 < a) { + break; + } + if (1 === a || 2 === a && 1 === this.bandwidthLimitType) { + c = Math.min(this.windowAckSize, c); + } + c !== this.ackWindowSize && (this.ackWindowSize = c, c = new Uint8Array([c >>> 24 & 255, c >>> 16 & 255, c >>> 8 & 255, c & 255]), this._sendMessage(2, {typeId:5, streamId:0, data:c}), 2 !== a && (this.bandwidthLimitType = a)); + } + } + }.bind(this); + if (this.oncreated) { + this.oncreated(); + } + }; + a.prototype.setChunkSize = function(a) { + if (1 > a || 2147483647 < a) { + throw Error("Invalid chunk size"); + } + this._sendMessage(2, {streamId:0, typeId:1, data:new Uint8Array([a >>> 24 & 255, a >>> 16 & 255, a >>> 8 & 255, a & 255])}); + this.chunkSize = a; + }; + a.prototype.send = function(a, c) { + if (3 > a || 65599 < a) { + throw Error("Invalid chunkStreamId"); + } + return this._sendMessage(a, c); + }; + a.prototype.sendUserControlMessage = function(a, c) { + var h = new Uint8Array(2 + c.length); + h[0] = a >> 8 & 255; + h[1] = a & 255; + h.set(c, 2); + this._sendMessage(2, {typeId:4, streamId:0, data:h}); + }; + a.prototype._sendAck = function() { + var a = new Uint8Array([this.bytesReceived >>> 24 & 255, this.bytesReceived >>> 16 & 255, this.bytesReceived >>> 8 & 255, this.bytesReceived & 255]); + this._sendMessage(2, {typeId:3, streamId:0, data:a}); + }; + a.prototype._sendMessage = function(a, c) { + var h = c.data, l = h.length, e = this._getChunkStream(a), m = ("timestamp" in c ? c.timestamp : Date.now() - this.epochStart) | 0, t = m - e.sentTimestamp | 0, q = new Uint8Array(this.chunkSize + 18), n; + 64 > a ? (n = 1, q[0] = a) : 320 > a ? (n = 2, q[0] = 0, q[1] = a - 64) : (n = 3, q[0] = 1, q[1] = a - 64 >> 8 & 255, q[2] = a - 64 & 255); + var k = n, f = 0; + c.streamId !== e.sentStreamId || 0 > t ? (0 !== (m & 4278190080) ? (f = m, q[k] = q[k + 1] = q[k + 2] = 255) : (q[k] = m >> 16 & 255, q[k + 1] = m >> 8 & 255, q[k + 2] = m & 255), k += 3, q[k++] = l >> 16 & 255, q[k++] = l >> 8 & 255, q[k++] = l & 255, q[k++] = c.typeId, q[k++] = c.streamId & 255, q[k++] = c.streamId >> 8 & 255, q[k++] = c.streamId >> 16 & 255, q[k++] = c.streamId >> 24 & 255) : l !== e.sentLength || c.typeId !== e.sentTypeId ? (q[0] |= 64, 0 !== (t & 4278190080) ? (f = t, + q[k] = q[k + 1] = q[k + 2] = 255) : (q[k] = t >> 16 & 255, q[k + 1] = t >> 8 & 255, q[k + 2] = t & 255), k += 3, q[k++] = l >> 16 & 255, q[k++] = l >> 8 & 255, q[k++] = l & 255, q[k++] = c.typeId) : 0 !== t ? (q[0] |= 128, 0 !== (t & 4278190080) ? (f = t, q[k] = q[k + 1] = q[k + 2] = 255) : (q[k] = t >> 16 & 255, q[k + 1] = t >> 8 & 255, q[k + 2] = t & 255), k += 3) : q[0] |= 192; + f && (q[k++] = f >>> 24 & 255, q[k++] = f >>> 16 & 255, q[k++] = f >>> 8 & 255, q[k++] = f & 255); + e.sentTimestamp = m; + e.sentStreamId = c.streamId; + e.sentTypeId = c.typeId; + e.sentLength = l; + for (e = 0;e < l;) { + t = Math.min(l - e, this.chunkSize), q.set(h.subarray(e, e + t), k), e += t, this.ondata(q.subarray(0, k + t)), q[0] |= 192, k = n; + } + return m; + }; + a.prototype._getChunkStream = function(a) { + var c = this.chunkStreams[a]; + c || (this.chunkStreams[a] = c = new h(a), c.setBuffer(), c.onmessage = function(a) { + if (this.onmessage) { + this.onmessage(a); + } + }.bind(this)); + return c; + }; + a.prototype._parseChunkedData = function() { + if (!(1 > this.bufferLength)) { + var a = this.buffer[0] >> 6 & 3, c = 1, h = this.buffer[0] & 63; + if (0 === h) { + if (2 > this.bufferLength) { + return; + } + h = this.buffer[1] + 64; + c = 2; + } else { + if (1 === h) { + if (2 > this.bufferLength) { + return; + } + h = (this.buffer[1] << 8) + this.buffer[2] + 64; + c = 3; + } + } + var l = 0 === a ? 11 : 1 === a ? 7 : 2 === a ? 3 : 0; + if (!(this.bufferLength < c + l)) { + var e = 3 !== a && 255 === this.buffer[c] && 255 === this.buffer[c + 1] && 255 === this.buffer[c + 2] ? 4 : 0, m = c + l + e; + if (!(this.bufferLength < m)) { + var t = this._getChunkStream(h), q; + q = 3 === a ? t.lastTimestamp : this.buffer[c] << 16 | this.buffer[c + 1] << 8 | this.buffer[c + 2]; + e && (q = c + l, q = this.buffer[q] << 24 | this.buffer[q + 1] << 16 | this.buffer[q + 2] << 8 | this.buffer[q + 3]); + if (1 === a || 2 === a) { + q = t.lastTimestamp + q | 0; + } + var l = t.lastLength, e = t.lastTypeId, n = t.lastStreamId; + if (0 === a || 1 === a) { + l = this.buffer[c + 3] << 16 | this.buffer[c + 4] << 8 | this.buffer[c + 5], e = this.buffer[c + 6]; + } + 0 === a && (n = this.buffer[c + 10] << 24 | this.buffer[c + 9] << 16 | this.buffer[c + 8] << 8 | this.buffer[c + 7]); + var k; + 3 === a && t.waitingForBytes ? (k = !1, a = Math.min(t.waitingForBytes, this.peerChunkSize), c = t.waitingForBytes - a) : (k = !0, a = Math.min(l, this.peerChunkSize), c = l - a); + if (!(this.bufferLength < m + a)) { + return!k && c || console.log("Chunk received: cs:" + h + "; f/l:" + k + "/" + !c + "; len:" + l), t.lastTimestamp = q, t.lastLength = l, t.lastTypeId = e, t.lastStreamId = n, t.lastMessageComplete = !c, t.waitingForBytes = c, t._push(this.buffer.subarray(m, m + a), k, !c), m + a; + } + } + } + } + }; + a.prototype.start = function() { + this.epochStart = Date.now(); + this.ondata(new Uint8Array([3])); + this.randomData = new Uint8Array(1536); + this.randomData[0] = 0; + this.randomData[1] = 0; + this.randomData[2] = 0; + this.randomData[3] = 0; + for (var a = 8;1536 > a;a++) { + this.randomData[a] = 256 * Math.random() | 0; + } + this.ondata(this.randomData); + console.log("## connected"); + }; + a.prototype.stop = function(a) { + a && console.error("socket error!!!"); + console.log("## closed"); + }; + a.prototype._fail = function(a) { + console.error("failed: " + a); + this.state = "failed"; + this.onclose(); + throw Error(a); + }; + return a; + }(); + c.ChunkedChannel = a; +})(RtmpJs || (RtmpJs = {})); +(function(c) { + var h = Shumway.AVM2.AS.flash, a = function() { + function a() { + this._streams = []; + } + a.prototype.connect = function(a, c) { + throw Error("Abstract BaseTransport.connect method"); + }; + a.prototype._initChannel = function(a, v) { + var l = new c.ChunkedChannel, e = this; + l.oncreated = function() { + var e = new h.utils.ByteArray; + e.objectEncoding = 0; + e.writeObject("connect"); + e.writeObject(1); + e.writeObject(a); + e.writeObject(v || null); + console.log(".. Connect sent"); + l.send(3, {streamId:0, typeId:20, data:new Uint8Array(e._buffer, 0, e.length)}); + }; + l.onmessage = function(a) { + console.log(".. Data received: typeId:" + a.typeId + ", streamId:" + a.streamId + ", cs: " + a.chunkedStreamId); + if (0 !== a.streamId) { + e._streams[a.streamId]._push(a); + } else { + if (20 === a.typeId || 17 === a.typeId) { + var c = new h.utils.ByteArray; + c.writeRawBytes(a.data); + c.position = 0; + c.objectEncoding = 20 === a.typeId ? 0 : 3; + a = c.readObject(); + void 0 === a && (c.objectEncoding = 0, a = c.readObject()); + var l = c.readObject(); + if ("_result" === a || "_error" === a) { + if (a = "_error" === a, 1 === l) { + if (l = c.readObject(), c = c.readObject(), e.onconnected) { + e.onconnected({properties:l, information:c, isError:a}); } - } - return this._concatenatedColorTransform; - }; - r.prototype._setColorTransform = function(a) { - this._colorTransform.copyFrom(a); - this._colorTransform.convertToFixedPoint(); - this._propagateFlagsDown(128); - this._setDirtyFlags(67108864); - }; - r.prototype._invalidateFillAndLineBounds = function(a, b) { - this._propagateFlagsUp((b ? 2 : 0) | (a ? 4 : 0)); - }; - r.prototype._invalidateParentFillAndLineBounds = function(a, b) { - this._parent && this._parent._invalidateFillAndLineBounds(a, b); - }; - r.prototype._getContentBounds = function(a) { - "undefined" === typeof a && (a = !0); - var b, c; - a ? (b = 2, c = this._lineBounds) : (b = 4, c = this._fillBounds); - if (this._hasFlags(b)) { - var d = this._getGraphics(); - d ? c.copyFrom(d._getContentBounds(a)) : c.setEmpty(); - this._getChildBounds(c, a); - this._removeFlags(b); - } - return c; - }; - r.prototype._getChildBounds = function(a, b) { - }; - r.prototype._getTransformedBounds = function(a, b) { - var c = this._getContentBounds(b).clone(); - if (a === this || c.isEmpty()) { - return c; - } - var d; - a ? (d = q.Matrix.TEMP_MATRIX, a._getInvertedConcatenatedMatrix().preMultiplyInto(this._getConcatenatedMatrix(), d)) : d = this._getConcatenatedMatrix(); - d.transformBounds(c); - return c; - }; - r.prototype._stopTimelineAnimation = function() { - this._removeFlags(2048); - }; - r.prototype._invalidateMatrix = function() { - this._setDirtyFlags(1048576); - this._setFlags(24); - this._invalidatePosition(); - }; - r.prototype._invalidatePosition = function() { - this._propagateFlagsDown(96); - this._invalidateParentFillAndLineBounds(!0, !0); - }; - r.prototype._animate = function(a) { - a.matrix && this._setMatrix(a.matrix, !1); - a.colorTransform && this._setColorTransform(a.colorTransform); - this._ratio = a.ratio; - a.name && (this._name = a.name); - this._clipDepth !== a.clipDepth && 0 <= a.clipDepth && (this._clipDepth = a.clipDepth, this._setDirtyFlags(268435456)); - this._filters = a.filters; - a.blendMode && a.blendMode !== this._blendMode && (this._blendMode = a.blendMode, this._setDirtyFlags(536870912)); - a.cacheAsBitmap && (this._setFlags(16384), this._setDirtyFlags(536870912)); - a.visible !== this._hasFlags(1) && (this._toggleFlags(1, a.visible), this._setDirtyFlags(536870912)); - }; - r.prototype._propagateEvent = function(a) { - this.visit(function(b) { - b.dispatchEvent(a); - return 0; - }, 0); - }; - Object.defineProperty(r.prototype, "x", {get:function() { - var a = this._matrix.tx; - if (this._canHaveTextContent()) { - var b = this._getContentBounds(), a = a + b.xMin - } - return a / 20; - }, set:function(a) { - a = 20 * a | 0; - this._stopTimelineAnimation(); - if (this._canHaveTextContent()) { - var b = this._getContentBounds(); - a -= b.xMin; - } - a !== this._matrix.tx && (this._matrix.tx = a, this._invertedMatrix.tx = -a, this._invalidatePosition(), this._setDirtyFlags(1048576)); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(r.prototype, "y", {get:function() { - var a = this._matrix.ty; - if (this._canHaveTextContent()) { - var b = this._getContentBounds(), a = a + b.yMin - } - return a / 20; - }, set:function(a) { - a = 20 * a | 0; - this._stopTimelineAnimation(); - if (this._canHaveTextContent()) { - var b = this._getContentBounds(); - a -= b.yMin; - } - a !== this._matrix.ty && (this._matrix.ty = a, this._invertedMatrix.ty = -a, this._invalidatePosition(), this._setDirtyFlags(1048576)); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(r.prototype, "scaleX", {get:function() { - return Math.abs(this._scaleX); - }, set:function(a) { - a = +a; - this._stopTimelineAnimation(); - a !== this._scaleX && (this._scaleX = a, this._invalidateMatrix()); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(r.prototype, "scaleY", {get:function() { - return this._scaleY; - }, set:function(a) { - a = +a; - this._stopTimelineAnimation(); - a !== this._scaleY && (this._scaleY = a, this._invalidateMatrix()); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(r.prototype, "scaleZ", {get:function() { - return this._scaleZ; - }, set:function(a) { - s("public DisplayObject::set scaleZ"); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(r.prototype, "rotation", {get:function() { - return this._rotation; - }, set:function(a) { - a = +a; - this._stopTimelineAnimation(); - a = r._clampRotation(a); - a !== this._rotation && (this._rotation = a, this._invalidateMatrix()); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(r.prototype, "rotationX", {get:function() { - return this._rotationX; - }, set:function(a) { - s("public DisplayObject::set rotationX"); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(r.prototype, "rotationY", {get:function() { - return this._rotationY; - }, set:function(a) { - s("public DisplayObject::set rotationY"); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(r.prototype, "rotationZ", {get:function() { - return this._rotationZ; - }, set:function(a) { - s("public DisplayObject::set rotationZ"); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(r.prototype, "width", {get:function() { - return this._getTransformedBounds(this._parent, !0).width / 20; - }, set:function(a) { - a = 20 * a | 0; - this._stopTimelineAnimation(); - if (!(0 > a)) { - var b = this._getContentBounds(!0); - if (this._canHaveTextContent()) { - var c = this._getContentBounds(); - this._setFillAndLineBoundsFromWidthAndHeight(a, b.height); - } else { - var c = this._getTransformedBounds(this._parent, !0), d = this._rotation / 180 * Math.PI, e = b.getBaseWidth(d); - e && (this._scaleY = c.height / b.getBaseHeight(d), this._scaleX = a / e, this._invalidateMatrix()); + } else { + var n = c.readObject(), c = c.readObject(); + if (e.onstreamcreated) { + var k = new s(e, c); + e._streams[c] = k; + e.onstreamcreated({transactionId:l, commandObject:n, streamId:c, stream:k, isError:a}); } } - }, enumerable:!0, configurable:!0}); + } else { + "onBWCheck" === a || "onBWDone" === a ? e.sendCommandOrResponse("_error", l, null, {code:"NetConnection.Call.Failed", level:"error"}) : (c.readObject(), c.position < c.length && c.readObject()); + } + } + } + }; + l.onusercontrolmessage = function(a) { + console.log(".. Event " + a.type + " +" + a.data.length + " bytes"); + 6 === a.type && l.sendUserControlMessage(7, a.data); + }; + return this.channel = l; + }; + a.prototype.call = function(a, c, l, e) { + var m = this.channel, t = new h.utils.ByteArray; + t.objectEncoding = 0; + t.writeObject(a); + t.writeObject(c); + t.writeObject(l); + t.writeObject(e); + m.send(3, {streamId:0, typeId:20, data:new Uint8Array(t._buffer, 0, t.length)}); + }; + a.prototype.createStream = function(a) { + this.sendCommandOrResponse("createStream", a, null); + }; + a.prototype.sendCommandOrResponse = function(a, c, l, e) { + var m = this.channel, t = new h.utils.ByteArray; + t.writeByte(0); + t.objectEncoding = 0; + t.writeObject(a); + t.writeObject(c); + t.writeObject(l || null); + 3 < arguments.length && t.writeObject(e); + m.send(3, {streamId:0, typeId:17, data:new Uint8Array(t._buffer, 0, t.length)}); + }; + a.prototype._setBuffer = function(a) { + this.channel.sendUserControlMessage(3, new Uint8Array([a >> 24 & 255, a >> 16 & 255, a >> 8 & 255, a & 255, 0, 0, 0, 100])); + }; + a.prototype._sendCommand = function(a, c) { + this.channel.send(8, {streamId:a, typeId:20, data:c}); + }; + return a; + }(); + c.BaseTransport = a; + var s = function() { + function a(c, h) { + this.transport = c; + this.streamId = h; + } + a.prototype.play = function(a, c, l, e) { + var m = new h.utils.ByteArray; + m.objectEncoding = 0; + m.writeObject("play"); + m.writeObject(0); + m.writeObject(null); + m.writeObject(a); + 1 < arguments.length && m.writeObject(c); + 2 < arguments.length && m.writeObject(l); + 3 < arguments.length && m.writeObject(e); + this.transport._sendCommand(this.streamId, new Uint8Array(m._buffer, 0, m.length)); + this.transport._setBuffer(this.streamId); + }; + a.prototype._push = function(a) { + switch(a.typeId) { + case 8: + ; + case 9: + if (this.ondata) { + this.ondata(a); + } + break; + case 18: + ; + case 20: + var c = [], l = new h.utils.ByteArray; + l.writeRawBytes(a.data); + l.position = 0; + for (l.objectEncoding = 0;l.position < l.length;) { + c.push(l.readObject()); + } + 18 === a.typeId && this.onscriptdata && this.onscriptdata.apply(this, c); + 20 === a.typeId && this.oncallback && this.oncallback.apply(this, c); + } + }; + return a; + }(); + c.parseConnectionString = function(a) { + var c = a.indexOf(":"); + if (0 > c || "/" !== a[c + 1]) { + return null; + } + var h = a.substring(0, c).toLocaleLowerCase(); + if ("rtmp" !== h && "rtmpt" !== h && "rtmps" !== h && "rtmpe" !== h && "rtmpte" !== h && "rtmfp" !== h) { + return null; + } + var l, e, m = c + 1; + if ("/" === a[c + 2]) { + m = a.indexOf("/", c + 3); + if (0 > m) { + return; + } + var t = a.indexOf(":", c + 1); + 0 <= t && t < m ? (l = a.substring(c + 3, t), e = +a.substring(t + 1, m)) : l = a.substring(c + 3, m); + } + return{protocol:h, host:l, port:e, app:a.substring(m)}; + }; +})(RtmpJs || (RtmpJs = {})); +(function(c) { + (function(h) { + function a(a, c, e) { + c || (c = p); + var m = window.createRtmpXHR, h = m ? m() : new XMLHttpRequest({mozSystem:!0}); + h.open("POST", a, !0); + h.responseType = "arraybuffer"; + h.setRequestHeader("Content-Type", "application/x-fcs"); + h.onload = function(a) { + e(new Uint8Array(h.response), h.status); + }; + h.onerror = function(a) { + console.log("error"); + throw Error("HTTP error"); + }; + h.send(c); + } + var s = navigator.mozTCPSocket, v = function(a) { + function c(e) { + a.call(this); + "string" === typeof e && (e = {host:e}); + this.host = e.host || "localhost"; + this.port = e.port || 1935; + this.ssl = !!e.ssl || !1; + } + __extends(c, a); + c.prototype.connect = function(a, c) { + function l(b) { + return d.send(b.buffer, b.byteOffset, b.byteLength); + } + if (!s) { + throw Error("Your browser does not support socket communication.\nCurrenly only Firefox with enabled mozTCPSocket is allowed (see README.md)."); + } + var q = this._initChannel(a, c), n = [], k = !1, f = window.createRtmpSocket, d = f ? f({host:this.host, port:this.port, ssl:this.ssl}) : s.open(this.host, this.port, {useSecureTransport:this.ssl, binaryType:"arraybuffer"}); + d.onopen = function(b) { + q.ondata = function(b) { + b = new Uint8Array(b); + n.push(b); + 1 < n.length || (console.log("Bytes written: " + b.length), l(b) && n.shift()); + }; + q.onclose = function() { + d.close(); + }; + q.start(); + }; + d.ondrain = function(b) { + n.shift(); + for (console.log("Write completed");0 < n.length;) { + console.log("Bytes written: " + n[0].length); + if (!l(n[0])) { + break; + } + n.shift(); + } + }; + d.onclose = function(b) { + q.stop(k); + }; + d.onerror = function(b) { + k = !0; + console.error("socket error: " + b.data); + }; + d.ondata = function(b) { + console.log("Bytes read: " + b.data.byteLength); + q.push(new Uint8Array(b.data)); + }; + }; + return c; + }(c.BaseTransport); + h.RtmpTransport = v; + v = function(c) { + function l(a) { + c.call(this); + var m = (a.ssl ? "https" : "http") + "://" + (a.host || "localhost"); + a.port && (m += ":" + a.port); + this.baseUrl = m; + this.stopped = !1; + this.sessionId = null; + this.requestId = 0; + this.data = []; + } + __extends(l, c); + l.prototype.connect = function(e, c) { + var l = this._initChannel(e, c); + l.ondata = function(a) { + console.log("Bytes written: " + a.length); + this.data.push(new Uint8Array(a)); + }.bind(this); + l.onclose = function() { + this.stopped = !0; + }.bind(this); + a(this.baseUrl + "/fcs/ident2", null, function(e, c) { + if (404 !== c) { + throw Error("Unexpected response: " + c); + } + a(this.baseUrl + "/open/1", null, function(a, e) { + this.sessionId = String.fromCharCode.apply(null, a).slice(0, -1); + console.log("session id: " + this.sessionId); + this.tick(); + l.start(); + }.bind(this)); + }.bind(this)); + }; + l.prototype.tick = function() { + var e = function(a, e) { + if (200 !== e) { + throw Error("Invalid HTTP status"); + } + var f = a[0]; + 1 < a.length && this.channel.push(a.subarray(1)); + setTimeout(this.tick.bind(this), 16 * f); + }.bind(this); + if (this.stopped) { + a(this.baseUrl + "/close/2", null, function() { + }); + } else { + if (0 < this.data.length) { + var c, l = 0; + this.data.forEach(function(a) { + l += a.length; + }); + var h = 0; + c = new Uint8Array(l); + this.data.forEach(function(a) { + c.set(a, h); + h += a.length; + }); + this.data.length = 0; + a(this.baseUrl + "/send/" + this.sessionId + "/" + this.requestId++, c, e); + } else { + a(this.baseUrl + "/idle/" + this.sessionId + "/" + this.requestId++, null, e); + } + } + }; + return l; + }(c.BaseTransport); + h.RtmptTransport = v; + var p = new Uint8Array([0]); + })(c.Browser || (c.Browser = {})); +})(RtmpJs || (RtmpJs = {})); +(function(c) { + (function(c) { + (function(a) { + function c(a) { + for (var e = [], d = 1;d < arguments.length;d++) { + e[d - 1] = arguments[d]; + } + return Array.prototype.concat.apply(a, e); + } + function h(a, e, d) { + a[e] = d >> 24 & 255; + a[e + 1] = d >> 16 & 255; + a[e + 2] = d >> 8 & 255; + a[e + 3] = d & 255; + } + function p(a) { + return a.charCodeAt(0) << 24 | a.charCodeAt(1) << 16 | a.charCodeAt(2) << 8 | a.charCodeAt(3); + } + var u = Shumway.StringUtilities.utf8decode, l = [1, 0, 0, 0, 1, 0, 0, 0, 1], e = [0, 0, 0], m = function() { + function a(e, d) { + this.boxtype = e; + "uuid" === e && (this.userType = d); + } + a.prototype.layout = function(a) { + this.offset = a; + a = 8; + this.userType && (a += 16); + return this.size = a; + }; + a.prototype.write = function(a) { + h(a, this.offset, this.size); + h(a, this.offset + 4, p(this.boxtype)); + if (!this.userType) { + return 8; + } + a.set(this.userType, this.offset + 8); + return 24; + }; + a.prototype.toUint8Array = function() { + var a = this.layout(0), a = new Uint8Array(a); + this.write(a); + return a; + }; + return a; + }(); + a.Box = m; + var t = function(a) { + function e(d, b, g) { + void 0 === b && (b = 0); + void 0 === g && (g = 0); + a.call(this, d); + this.version = b; + this.flags = g; + } + __extends(e, a); + e.prototype.layout = function(d) { + return this.size = a.prototype.layout.call(this, d) + 4; + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d); + h(d, this.offset + b, this.version << 24 | this.flags); + return b + 4; + }; + return e; + }(m); + a.FullBox = t; + var q = function(a) { + function e(d, b, g) { + a.call(this, "ftype"); + this.majorBrand = d; + this.minorVersion = b; + this.compatibleBrands = g; + } + __extends(e, a); + e.prototype.layout = function(d) { + return this.size = a.prototype.layout.call(this, d) + 4 * (2 + this.compatibleBrands.length); + }; + e.prototype.write = function(d) { + var b = this, e = a.prototype.write.call(this, d); + h(d, this.offset + e, p(this.majorBrand)); + h(d, this.offset + e + 4, this.minorVersion); + e += 8; + this.compatibleBrands.forEach(function(a) { + h(d, b.offset + e, p(a)); + e += 4; + }, this); + return e; + }; + return e; + }(m); + a.FileTypeBox = q; + q = function(a) { + function e(d, b) { + a.call(this, d); + this.children = b; + } + __extends(e, a); + e.prototype.layout = function(d) { + var b = a.prototype.layout.call(this, d); + this.children.forEach(function(a) { + a && (b += a.layout(d + b)); + }); + return this.size = b; + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d); + this.children.forEach(function(a) { + a && (b += a.write(d)); + }); + return b; + }; + return e; + }(m); + a.BoxContainerBox = q; + var n = function(a) { + function e(d, b, g, f) { + a.call(this, "moov", c([d], b, [g, f])); + this.header = d; + this.tracks = b; + this.extendsBox = g; + this.userData = f; + } + __extends(e, a); + return e; + }(q); + a.MovieBox = n; + n = function(a) { + function e(d, b, g, f, c, m, n, h) { + void 0 === f && (f = 1); + void 0 === c && (c = 1); + void 0 === m && (m = l); + void 0 === n && (n = -20828448E5); + void 0 === h && (h = -20828448E5); + a.call(this, "mvhd", 0, 0); + this.timescale = d; + this.duration = b; + this.nextTrackId = g; + this.rate = f; + this.volume = c; + this.matrix = m; + this.creationTime = n; + this.modificationTime = h; + } + __extends(e, a); + e.prototype.layout = function(d) { + return this.size = a.prototype.layout.call(this, d) + 16 + 4 + 2 + 2 + 8 + 36 + 24 + 4; + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d); + h(d, this.offset + b, (this.creationTime - -20828448E5) / 1E3 | 0); + h(d, this.offset + b + 4, (this.modificationTime - -20828448E5) / 1E3 | 0); + h(d, this.offset + b + 8, this.timescale); + h(d, this.offset + b + 12, this.duration); + b += 16; + h(d, this.offset + b, 65536 * this.rate | 0); + h(d, this.offset + b + 4, (256 * this.volume | 0) << 16); + h(d, this.offset + b + 8, 0); + h(d, this.offset + b + 12, 0); + b += 16; + h(d, this.offset + b, 65536 * this.matrix[0] | 0); + h(d, this.offset + b + 4, 65536 * this.matrix[1] | 0); + h(d, this.offset + b + 8, 65536 * this.matrix[2] | 0); + h(d, this.offset + b + 12, 65536 * this.matrix[3] | 0); + h(d, this.offset + b + 16, 65536 * this.matrix[4] | 0); + h(d, this.offset + b + 20, 65536 * this.matrix[5] | 0); + h(d, this.offset + b + 24, 1073741824 * this.matrix[6] | 0); + h(d, this.offset + b + 28, 1073741824 * this.matrix[7] | 0); + h(d, this.offset + b + 32, 1073741824 * this.matrix[8] | 0); + b += 36; + h(d, this.offset + b, 0); + h(d, this.offset + b + 4, 0); + h(d, this.offset + b + 8, 0); + h(d, this.offset + b + 12, 0); + h(d, this.offset + b + 16, 0); + h(d, this.offset + b + 20, 0); + b += 24; + h(d, this.offset + b, this.nextTrackId); + return b + 4; + }; + return e; + }(t); + a.MovieHeaderBox = n; + (function(a) { + a[a.TRACK_ENABLED = 1] = "TRACK_ENABLED"; + a[a.TRACK_IN_MOVIE = 2] = "TRACK_IN_MOVIE"; + a[a.TRACK_IN_PREVIEW = 4] = "TRACK_IN_PREVIEW"; + })(a.TrackHeaderFlags || (a.TrackHeaderFlags = {})); + n = function(a) { + function e(d, b, g, f, c, m, n, h, q, t, s) { + void 0 === n && (n = 0); + void 0 === h && (h = 0); + void 0 === q && (q = l); + void 0 === t && (t = -20828448E5); + void 0 === s && (s = -20828448E5); + a.call(this, "tkhd", 0, d); + this.trackId = b; + this.duration = g; + this.width = f; + this.height = c; + this.volume = m; + this.alternateGroup = n; + this.layer = h; + this.matrix = q; + this.creationTime = t; + this.modificationTime = s; + } + __extends(e, a); + e.prototype.layout = function(d) { + return this.size = a.prototype.layout.call(this, d) + 20 + 8 + 6 + 2 + 36 + 8; + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d); + h(d, this.offset + b, (this.creationTime - -20828448E5) / 1E3 | 0); + h(d, this.offset + b + 4, (this.modificationTime - -20828448E5) / 1E3 | 0); + h(d, this.offset + b + 8, this.trackId); + h(d, this.offset + b + 12, 0); + h(d, this.offset + b + 16, this.duration); + b += 20; + h(d, this.offset + b, 0); + h(d, this.offset + b + 4, 0); + h(d, this.offset + b + 8, this.layer << 16 | this.alternateGroup); + h(d, this.offset + b + 12, (256 * this.volume | 0) << 16); + b += 16; + h(d, this.offset + b, 65536 * this.matrix[0] | 0); + h(d, this.offset + b + 4, 65536 * this.matrix[1] | 0); + h(d, this.offset + b + 8, 65536 * this.matrix[2] | 0); + h(d, this.offset + b + 12, 65536 * this.matrix[3] | 0); + h(d, this.offset + b + 16, 65536 * this.matrix[4] | 0); + h(d, this.offset + b + 20, 65536 * this.matrix[5] | 0); + h(d, this.offset + b + 24, 1073741824 * this.matrix[6] | 0); + h(d, this.offset + b + 28, 1073741824 * this.matrix[7] | 0); + h(d, this.offset + b + 32, 1073741824 * this.matrix[8] | 0); + b += 36; + h(d, this.offset + b, 65536 * this.width | 0); + h(d, this.offset + b + 4, 65536 * this.height | 0); + return b + 8; + }; + return e; + }(t); + a.TrackHeaderBox = n; + n = function(a) { + function e(d, b, g, f, c) { + void 0 === g && (g = "unk"); + void 0 === f && (f = -20828448E5); + void 0 === c && (c = -20828448E5); + a.call(this, "mdhd", 0, 0); + this.timescale = d; + this.duration = b; + this.language = g; + this.creationTime = f; + this.modificationTime = c; + } + __extends(e, a); + e.prototype.layout = function(d) { + return this.size = a.prototype.layout.call(this, d) + 16 + 4; + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d); + h(d, this.offset + b, (this.creationTime - -20828448E5) / 1E3 | 0); + h(d, this.offset + b + 4, (this.modificationTime - -20828448E5) / 1E3 | 0); + h(d, this.offset + b + 8, this.timescale); + h(d, this.offset + b + 12, this.duration); + var e = this.language; + h(d, this.offset + b + 16, ((e.charCodeAt(0) & 31) << 10 | (e.charCodeAt(1) & 31) << 5 | e.charCodeAt(2) & 31) << 16); + return b + 20; + }; + return e; + }(t); + a.MediaHeaderBox = n; + n = function(a) { + function e(d, b) { + a.call(this, "hdlr", 0, 0); + this.handlerType = d; + this.name = b; + this._encodedName = u(this.name); + } + __extends(e, a); + e.prototype.layout = function(d) { + return this.size = a.prototype.layout.call(this, d) + 8 + 12 + (this._encodedName.length + 1); + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d); + h(d, this.offset + b, 0); + h(d, this.offset + b + 4, p(this.handlerType)); + h(d, this.offset + b + 8, 0); + h(d, this.offset + b + 12, 0); + h(d, this.offset + b + 16, 0); + b += 20; + d.set(this._encodedName, this.offset + b); + d[this.offset + b + this._encodedName.length] = 0; + return b += this._encodedName.length + 1; + }; + return e; + }(t); + a.HandlerBox = n; + n = function(a) { + function e(d) { + void 0 === d && (d = 0); + a.call(this, "smhd", 0, 0); + this.balance = d; + } + __extends(e, a); + e.prototype.layout = function(d) { + return this.size = a.prototype.layout.call(this, d) + 4; + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d); + h(d, this.offset + b, (256 * this.balance | 0) << 16); + return b + 4; + }; + return e; + }(t); + a.SoundMediaHeaderBox = n; + n = function(a) { + function f(d, b) { + void 0 === d && (d = 0); + void 0 === b && (b = e); + a.call(this, "vmhd", 0, 0); + this.graphicsMode = d; + this.opColor = b; + } + __extends(f, a); + f.prototype.layout = function(d) { + return this.size = a.prototype.layout.call(this, d) + 8; + }; + f.prototype.write = function(d) { + var b = a.prototype.write.call(this, d); + h(d, this.offset + b, this.graphicsMode << 16 | this.opColor[0]); + h(d, this.offset + b + 4, this.opColor[1] << 16 | this.opColor[2]); + return b + 8; + }; + return f; + }(t); + a.VideoMediaHeaderBox = n; + a.SELF_CONTAINED_DATA_REFERENCE_FLAG = 1; + n = function(e) { + function f(d, b) { + void 0 === b && (b = null); + e.call(this, "url ", 0, d); + this.location = b; + d & a.SELF_CONTAINED_DATA_REFERENCE_FLAG || (this._encodedLocation = u(b)); + } + __extends(f, e); + f.prototype.layout = function(a) { + a = e.prototype.layout.call(this, a); + this._encodedLocation && (a += this._encodedLocation.length + 1); + return this.size = a; + }; + f.prototype.write = function(a) { + var b = e.prototype.write.call(this, a); + this._encodedLocation && (a.set(this._encodedLocation, this.offset + b), a[this.offset + b + this._encodedLocation.length] = 0, b += this._encodedLocation.length); + return b; + }; + return f; + }(t); + a.DataEntryUrlBox = n; + n = function(a) { + function e(d) { + a.call(this, "dref", 0, 0); + this.entries = d; + } + __extends(e, a); + e.prototype.layout = function(d) { + var b = a.prototype.layout.call(this, d) + 4; + this.entries.forEach(function(a) { + b += a.layout(d + b); + }); + return this.size = b; + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d); + h(d, this.offset + b, this.entries.length); + this.entries.forEach(function(a) { + b += a.write(d); + }); + return b; + }; + return e; + }(t); + a.DataReferenceBox = n; + n = function(a) { + function e(d) { + a.call(this, "dinf", [d]); + this.dataReference = d; + } + __extends(e, a); + return e; + }(q); + a.DataInformationBox = n; + n = function(a) { + function e(d) { + a.call(this, "stsd", 0, 0); + this.entries = d; + } + __extends(e, a); + e.prototype.layout = function(d) { + var b = a.prototype.layout.call(this, d), b = b + 4; + this.entries.forEach(function(a) { + b += a.layout(d + b); + }); + return this.size = b; + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d); + h(d, this.offset + b, this.entries.length); + b += 4; + this.entries.forEach(function(a) { + b += a.write(d); + }); + return b; + }; + return e; + }(t); + a.SampleDescriptionBox = n; + n = function(a) { + function e(d, b, g, f, c) { + a.call(this, "stbl", [d, b, g, f, c]); + this.sampleDescriptions = d; + this.timeToSample = b; + this.sampleToChunk = g; + this.sampleSizes = f; + this.chunkOffset = c; + } + __extends(e, a); + return e; + }(q); + a.SampleTableBox = n; + n = function(a) { + function e(d, b, g) { + a.call(this, "minf", [d, b, g]); + this.header = d; + this.info = b; + this.sampleTable = g; + } + __extends(e, a); + return e; + }(q); + a.MediaInformationBox = n; + n = function(a) { + function e(d, b, g) { + a.call(this, "mdia", [d, b, g]); + this.header = d; + this.handler = b; + this.info = g; + } + __extends(e, a); + return e; + }(q); + a.MediaBox = n; + n = function(a) { + function e(d, b) { + a.call(this, "trak", [d, b]); + this.header = d; + this.media = b; + } + __extends(e, a); + return e; + }(q); + a.TrackBox = n; + n = function(a) { + function e(d, b, g, f, c) { + a.call(this, "trex", 0, 0); + this.trackId = d; + this.defaultSampleDescriptionIndex = b; + this.defaultSampleDuration = g; + this.defaultSampleSize = f; + this.defaultSampleFlags = c; + } + __extends(e, a); + e.prototype.layout = function(d) { + return this.size = a.prototype.layout.call(this, d) + 20; + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d); + h(d, this.offset + b, this.trackId); + h(d, this.offset + b + 4, this.defaultSampleDescriptionIndex); + h(d, this.offset + b + 8, this.defaultSampleDuration); + h(d, this.offset + b + 12, this.defaultSampleSize); + h(d, this.offset + b + 16, this.defaultSampleFlags); + return b + 20; + }; + return e; + }(t); + a.TrackExtendsBox = n; + n = function(a) { + function e(d, b, g) { + a.call(this, "mvex", c([d], b, [g])); + this.header = d; + this.tracDefaults = b; + this.levels = g; + } + __extends(e, a); + return e; + }(q); + a.MovieExtendsBox = n; + n = function(a) { + function e(d, b) { + a.call(this, "meta", 0, 0); + this.handler = d; + this.otherBoxes = b; + } + __extends(e, a); + e.prototype.layout = function(d) { + var b = a.prototype.layout.call(this, d), b = b + this.handler.layout(d + b); + this.otherBoxes.forEach(function(a) { + b += a.layout(d + b); + }); + return this.size = b; + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d), b = b + this.handler.write(d); + this.otherBoxes.forEach(function(a) { + b += a.write(d); + }); + return b; + }; + return e; + }(t); + a.MetaBox = n; + n = function(a) { + function e(d) { + a.call(this, "mfhd", 0, 0); + this.sequenceNumber = d; + } + __extends(e, a); + e.prototype.layout = function(d) { + return this.size = a.prototype.layout.call(this, d) + 4; + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d); + h(d, this.offset + b, this.sequenceNumber); + return b + 4; + }; + return e; + }(t); + a.MovieFragmentHeaderBox = n; + (function(a) { + a[a.BASE_DATA_OFFSET_PRESENT = 1] = "BASE_DATA_OFFSET_PRESENT"; + a[a.SAMPLE_DESCRIPTION_INDEX_PRESENT = 2] = "SAMPLE_DESCRIPTION_INDEX_PRESENT"; + a[a.DEFAULT_SAMPLE_DURATION_PRESENT = 8] = "DEFAULT_SAMPLE_DURATION_PRESENT"; + a[a.DEFAULT_SAMPLE_SIZE_PRESENT = 16] = "DEFAULT_SAMPLE_SIZE_PRESENT"; + a[a.DEFAULT_SAMPLE_FLAGS_PRESENT = 32] = "DEFAULT_SAMPLE_FLAGS_PRESENT"; + })(a.TrackFragmentFlags || (a.TrackFragmentFlags = {})); + n = function(a) { + function e(d, b, g, f, c, m, l) { + a.call(this, "tfhd", 0, d); + this.trackId = b; + this.baseDataOffset = g; + this.sampleDescriptionIndex = f; + this.defaultSampleDuration = c; + this.defaultSampleSize = m; + this.defaultSampleFlags = l; + } + __extends(e, a); + e.prototype.layout = function(d) { + d = a.prototype.layout.call(this, d) + 4; + var b = this.flags; + b & 1 && (d += 8); + b & 2 && (d += 4); + b & 8 && (d += 4); + b & 16 && (d += 4); + b & 32 && (d += 4); + return this.size = d; + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d), e = this.flags; + h(d, this.offset + b, this.trackId); + b += 4; + e & 1 && (h(d, this.offset + b, 0), h(d, this.offset + b + 4, this.baseDataOffset), b += 8); + e & 2 && (h(d, this.offset + b, this.sampleDescriptionIndex), b += 4); + e & 8 && (h(d, this.offset + b, this.defaultSampleDuration), b += 4); + e & 16 && (h(d, this.offset + b, this.defaultSampleSize), b += 4); + e & 32 && (h(d, this.offset + b, this.defaultSampleFlags), b += 4); + return b; + }; + return e; + }(t); + a.TrackFragmentHeaderBox = n; + n = function(a) { + function e(d) { + a.call(this, "tfdt", 0, 0); + this.baseMediaDecodeTime = d; + } + __extends(e, a); + e.prototype.layout = function(d) { + return this.size = a.prototype.layout.call(this, d) + 4; + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d); + h(d, this.offset + b, this.baseMediaDecodeTime); + return b + 4; + }; + return e; + }(t); + a.TrackFragmentBaseMediaDecodeTimeBox = n; + n = function(a) { + function e(d, b, g) { + a.call(this, "traf", [d, b, g]); + this.header = d; + this.decodeTime = b; + this.run = g; + } + __extends(e, a); + return e; + }(q); + a.TrackFragmentBox = n; + (function(a) { + a[a.IS_LEADING_MASK = 201326592] = "IS_LEADING_MASK"; + a[a.SAMPLE_DEPENDS_ON_MASK = 50331648] = "SAMPLE_DEPENDS_ON_MASK"; + a[a.SAMPLE_DEPENDS_ON_OTHER = 16777216] = "SAMPLE_DEPENDS_ON_OTHER"; + a[a.SAMPLE_DEPENDS_ON_NO_OTHERS = 33554432] = "SAMPLE_DEPENDS_ON_NO_OTHERS"; + a[a.SAMPLE_IS_DEPENDED_ON_MASK = 12582912] = "SAMPLE_IS_DEPENDED_ON_MASK"; + a[a.SAMPLE_HAS_REDUNDANCY_MASK = 3145728] = "SAMPLE_HAS_REDUNDANCY_MASK"; + a[a.SAMPLE_PADDING_VALUE_MASK = 917504] = "SAMPLE_PADDING_VALUE_MASK"; + a[a.SAMPLE_IS_NOT_SYNC = 65536] = "SAMPLE_IS_NOT_SYNC"; + a[a.SAMPLE_DEGRADATION_PRIORITY_MASK = 65535] = "SAMPLE_DEGRADATION_PRIORITY_MASK"; + })(a.SampleFlags || (a.SampleFlags = {})); + (function(a) { + a[a.DATA_OFFSET_PRESENT = 1] = "DATA_OFFSET_PRESENT"; + a[a.FIRST_SAMPLE_FLAGS_PRESENT = 4] = "FIRST_SAMPLE_FLAGS_PRESENT"; + a[a.SAMPLE_DURATION_PRESENT = 256] = "SAMPLE_DURATION_PRESENT"; + a[a.SAMPLE_SIZE_PRESENT = 512] = "SAMPLE_SIZE_PRESENT"; + a[a.SAMPLE_FLAGS_PRESENT = 1024] = "SAMPLE_FLAGS_PRESENT"; + a[a.SAMPLE_COMPOSITION_TIME_OFFSET = 2048] = "SAMPLE_COMPOSITION_TIME_OFFSET"; + })(a.TrackRunFlags || (a.TrackRunFlags = {})); + t = function(a) { + function e(d, b, g, f) { + a.call(this, "trun", 1, d); + this.samples = b; + this.dataOffset = g; + this.firstSampleFlags = f; + } + __extends(e, a); + e.prototype.layout = function(d) { + d = a.prototype.layout.call(this, d) + 4; + var b = this.samples.length, e = this.flags; + e & 1 && (d += 4); + e & 4 && (d += 4); + e & 256 && (d += 4 * b); + e & 512 && (d += 4 * b); + e & 1024 && (d += 4 * b); + e & 2048 && (d += 4 * b); + return this.size = d; + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d), e = this.samples.length, f = this.flags; + h(d, this.offset + b, e); + b += 4; + f & 1 && (h(d, this.offset + b, this.dataOffset), b += 4); + f & 4 && (h(d, this.offset + b, this.firstSampleFlags), b += 4); + for (var c = 0;c < e;c++) { + var m = this.samples[c]; + f & 256 && (h(d, this.offset + b, m.duration), b += 4); + f & 512 && (h(d, this.offset + b, m.size), b += 4); + f & 1024 && (h(d, this.offset + b, m.flags), b += 4); + f & 2048 && (h(d, this.offset + b, m.compositionTimeOffset), b += 4); + } + return b; + }; + return e; + }(t); + a.TrackRunBox = t; + t = function(a) { + function e(d, b) { + a.call(this, "moof", c([d], b)); + this.header = d; + this.trafs = b; + } + __extends(e, a); + return e; + }(q); + a.MovieFragmentBox = t; + t = function(a) { + function e(d) { + a.call(this, "mdat"); + this.chunks = d; + } + __extends(e, a); + e.prototype.layout = function(d) { + var b = a.prototype.layout.call(this, d); + this.chunks.forEach(function(a) { + b += a.length; + }); + return this.size = b; + }; + e.prototype.write = function(d) { + var b = this, e = a.prototype.write.call(this, d); + this.chunks.forEach(function(a) { + d.set(a, b.offset + e); + e += a.length; + }, this); + return e; + }; + return e; + }(m); + a.MediaDataBox = t; + t = function(a) { + function e(d, b) { + a.call(this, d); + this.dataReferenceIndex = b; + } + __extends(e, a); + e.prototype.layout = function(d) { + return this.size = a.prototype.layout.call(this, d) + 8; + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d); + h(d, this.offset + b, 0); + h(d, this.offset + b + 4, this.dataReferenceIndex); + return b + 8; + }; + return e; + }(m); + a.SampleEntry = t; + q = function(a) { + function e(d, b, g, f, c, m) { + void 0 === g && (g = 2); + void 0 === f && (f = 16); + void 0 === c && (c = 44100); + void 0 === m && (m = null); + a.call(this, d, b); + this.channelCount = g; + this.sampleSize = f; + this.sampleRate = c; + this.otherBoxes = m; + } + __extends(e, a); + e.prototype.layout = function(d) { + var b = a.prototype.layout.call(this, d) + 20; + this.otherBoxes && this.otherBoxes.forEach(function(a) { + b += a.layout(d + b); + }); + return this.size = b; + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d); + h(d, this.offset + b, 0); + h(d, this.offset + b + 4, 0); + h(d, this.offset + b + 8, this.channelCount << 16 | this.sampleSize); + h(d, this.offset + b + 12, 0); + h(d, this.offset + b + 16, this.sampleRate << 16); + b += 20; + this.otherBoxes && this.otherBoxes.forEach(function(a) { + b += a.write(d); + }); + return b; + }; + return e; + }(t); + a.AudioSampleEntry = q; + a.COLOR_NO_ALPHA_VIDEO_SAMPLE_DEPTH = 24; + t = function(e) { + function f(d, b, g, f, c, m, l, n, h, q) { + void 0 === c && (c = ""); + void 0 === m && (m = 72); + void 0 === l && (l = 72); + void 0 === n && (n = 1); + void 0 === h && (h = a.COLOR_NO_ALPHA_VIDEO_SAMPLE_DEPTH); + void 0 === q && (q = null); + e.call(this, d, b); + this.width = g; + this.height = f; + this.compressorName = c; + this.horizResolution = m; + this.vertResolution = l; + this.frameCount = n; + this.depth = h; + this.otherBoxes = q; + if (31 < c.length) { + throw Error("invalid compressor name"); + } + } + __extends(f, e); + f.prototype.layout = function(a) { + var b = e.prototype.layout.call(this, a) + 16 + 12 + 4 + 2 + 32 + 2 + 2; + this.otherBoxes && this.otherBoxes.forEach(function(e) { + b += e.layout(a + b); + }); + return this.size = b; + }; + f.prototype.write = function(a) { + var b = e.prototype.write.call(this, a); + h(a, this.offset + b, 0); + h(a, this.offset + b + 4, 0); + h(a, this.offset + b + 8, 0); + h(a, this.offset + b + 12, 0); + b += 16; + h(a, this.offset + b, this.width << 16 | this.height); + h(a, this.offset + b + 4, 65536 * this.horizResolution | 0); + h(a, this.offset + b + 8, 65536 * this.vertResolution | 0); + b += 12; + h(a, this.offset + b, 0); + h(a, this.offset + b + 4, this.frameCount << 16); + b += 6; + a[this.offset + b] = this.compressorName.length; + for (var g = 0;31 > g;g++) { + a[this.offset + b + g + 1] = g < this.compressorName.length ? this.compressorName.charCodeAt(g) & 127 : 0; + } + b += 32; + h(a, this.offset + b, this.depth << 16 | 65535); + b += 4; + this.otherBoxes && this.otherBoxes.forEach(function(e) { + b += e.write(a); + }); + return b; + }; + return f; + }(t); + a.VideoSampleEntry = t; + m = function(a) { + function e(d, b) { + a.call(this, d); + this.data = b; + } + __extends(e, a); + e.prototype.layout = function(d) { + return this.size = a.prototype.layout.call(this, d) + this.data.length; + }; + e.prototype.write = function(d) { + var b = a.prototype.write.call(this, d); + d.set(this.data, this.offset + b); + return b + this.data.length; + }; + return e; + }(m); + a.RawTag = m; + })(c.Iso || (c.Iso = {})); + })(c.MP4 || (c.MP4 = {})); +})(RtmpJs || (RtmpJs = {})); +(function(c) { + (function(c) { + function a(a) { + for (var e = a.length >> 1, c = new Uint8Array(e), k = 0;k < e;k++) { + c[k] = parseInt(a.substr(2 * k, 2), 16); + } + return c; + } + var s = [5500, 11025, 22050, 44100], v = ["PCM", "ADPCM", "MP3", "PCM le", "Nellymouser16", "Nellymouser8", "Nellymouser", "G.711 A-law", "G.711 mu-law", null, "AAC", "Speex", "MP3 8khz"], p; + (function(a) { + a[a.HEADER = 0] = "HEADER"; + a[a.RAW = 1] = "RAW"; + })(p || (p = {})); + var u = [null, "JPEG", "Sorenson", "Screen", "VP6", "VP6 alpha", "Screen2", "AVC"], l; + (function(a) { + a[a.KEY = 1] = "KEY"; + a[a.INNER = 2] = "INNER"; + a[a.DISPOSABLE = 3] = "DISPOSABLE"; + a[a.GENERATED = 4] = "GENERATED"; + a[a.INFO = 5] = "INFO"; + })(l || (l = {})); + var e; + (function(a) { + a[a.HEADER = 0] = "HEADER"; + a[a.NALU = 1] = "NALU"; + a[a.END = 2] = "END"; + })(e || (e = {})); + var m; + (function(a) { + a[a.CAN_GENERATE_HEADER = 0] = "CAN_GENERATE_HEADER"; + a[a.NEED_HEADER_DATA = 1] = "NEED_HEADER_DATA"; + a[a.MAIN_PACKETS = 2] = "MAIN_PACKETS"; + })(m || (m = {})); + p = function() { + function e(a) { + var c = this; + this.oncodecinfo = function(a) { + }; + this.ondata = function(a) { + throw Error("MP4Mux.ondata is not set"); + }; + this.metadata = a; + this.trackStates = this.metadata.tracks.map(function(a, e) { + var d = {trackId:e + 1, trackInfo:a, cachedDuration:0, samplesProcessed:0, initializationData:[]}; + c.metadata.audioTrackId === e && (c.audioTrackState = d); + c.metadata.videoTrackId === e && (c.videoTrackState = d); + return d; + }, this); + this._checkIfNeedHeaderData(); + this.filePos = 0; + this.cachedPackets = []; + this.chunkIndex = 0; + } + e.prototype.pushPacket = function(a, e, c) { + 0 === this.state && this._tryGenerateHeader(); + switch(a) { + case 8: + a = this.audioTrackState; + var f = 0, d = 1, b = e[f], g = b >> 4, m; + f++; + switch(g) { + case 10: + d = e[f++]; + m = 1024; + break; + case 2: + m = e[f + 1] >> 3 & 3; + var l = e[f + 1] >> 1 & 3; + m = 1 === l ? 3 === m ? 1152 : 576 : 3 === l ? 384 : 1152; + } + e = {codecDescription:v[g], codecId:g, data:e.subarray(f), rate:s[b >> 2 & 3], size:b & 2 ? 16 : 8, channels:b & 1 ? 2 : 1, samples:m, packetType:d}; + if (!a || a.trackInfo.codecId !== e.codecId) { + throw Error("Unexpected audio packet codec: " + e.codecDescription); + } + switch(e.codecId) { + default: + throw Error("Unsupported audio codec: " + e.codecDescription);; + case 2: + break; + case 10: + if (0 === e.packetType) { + a.initializationData.push(e.data); + return; + } + ; + } + this.cachedPackets.push({packet:e, timestamp:c, trackId:a.trackId}); + break; + case 9: + a = this.videoTrackState; + f = 0; + b = e[f] >> 4; + d = e[f] & 15; + f++; + b = {frameType:b, codecId:d, codecDescription:u[d]}; + switch(d) { + case 7: + d = e[f++]; + b.packetType = d; + b.compositionTime = (e[f] << 24 | e[f + 1] << 16 | e[f + 2] << 8) >> 8; + f += 3; + break; + case 4: + b.packetType = 1, b.horizontalOffset = e[f] >> 4 & 15, b.verticalOffset = e[f] & 15, b.compositionTime = 0, f++; + } + b.data = e.subarray(f); + if (!a || a.trackInfo.codecId !== b.codecId) { + throw Error("Unexpected video packet codec: " + b.codecDescription); + } + switch(b.codecId) { + default: + throw Error("unsupported video codec: " + b.codecDescription);; + case 4: + break; + case 7: + if (0 === b.packetType) { + a.initializationData.push(b.data); + return; + } + ; + } + this.cachedPackets.push({packet:b, timestamp:c, trackId:a.trackId}); + break; + default: + throw Error("unknown packet type: " + a);; + } + 1 === this.state && this._tryGenerateHeader(); + 200 <= this.cachedPackets.length && 2 === this.state && this._chunk(); + }; + e.prototype.flush = function() { + 0 < this.cachedPackets.length && this._chunk(); + }; + e.prototype._checkIfNeedHeaderData = function() { + this.trackStates.some(function(a) { + return 10 === a.trackInfo.codecId || 7 === a.trackInfo.codecId; + }) ? this.state = 1 : this.state = 0; + }; + e.prototype._tryGenerateHeader = function() { + if (this.trackStates.every(function(b) { + switch(b.trackInfo.codecId) { + case 10: + ; + case 7: + return 0 < b.initializationData.length; + default: + return!0; + } + })) { + for (var e = ["isom"], m = [], k = 0;k < this.trackStates.length;k++) { + var f = this.trackStates[k], d = f.trackInfo, b; + switch(d.codecId) { + case 10: + var g = f.initializationData[0]; + b = new c.Iso.AudioSampleEntry("mp4a", 1, d.channels, d.samplesize, d.samplerate); + var l = new Uint8Array(41 + g.length); + l.set(a("0000000003808080"), 0); + l[8] = 32 + g.length; + l.set(a("00020004808080"), 9); + l[16] = 18 + g.length; + l.set(a("40150000000000FA000000000005808080"), 17); + l[34] = g.length; + l.set(g, 35); + l.set(a("068080800102"), 35 + g.length); + b.otherBoxes = [new c.Iso.RawTag("esds", l)]; + f.mimeTypeCodec = "mp4a.40." + (g[0] >> 3); + break; + case 2: + b = new c.Iso.AudioSampleEntry(".mp3", 1, d.channels, d.samplesize, d.samplerate); + f.mimeTypeCodec = "mp3"; + break; + case 7: + g = f.initializationData[0]; + b = new c.Iso.VideoSampleEntry("avc1", 1, d.width, d.height); + b.otherBoxes = [new c.Iso.RawTag("avcC", g)]; + f.mimeTypeCodec = "avc1." + (16777216 | g[1] << 16 | g[2] << 8 | g[3]).toString(16).substr(1); + e.push("iso2", "avc1", "mp41"); + break; + case 4: + b = new c.Iso.VideoSampleEntry("VP6F", 1, d.width, d.height); + b.otherBoxes = [new c.Iso.RawTag("glbl", a("00"))]; + f.mimeTypeCodec = "avc1.42001E"; + break; + default: + throw Error("not supported track type");; + } + var w; + f === this.audioTrackState ? w = new c.Iso.TrackBox(new c.Iso.TrackHeaderBox(3, f.trackId, -1, 0, 0, 1, k), new c.Iso.MediaBox(new c.Iso.MediaHeaderBox(d.timescale, -1, d.language), new c.Iso.HandlerBox("soun", "SoundHandler"), new c.Iso.MediaInformationBox(new c.Iso.SoundMediaHeaderBox, new c.Iso.DataInformationBox(new c.Iso.DataReferenceBox([new c.Iso.DataEntryUrlBox(c.Iso.SELF_CONTAINED_DATA_REFERENCE_FLAG)])), new c.Iso.SampleTableBox(new c.Iso.SampleDescriptionBox([b]), new c.Iso.RawTag("stts", + a("0000000000000000")), new c.Iso.RawTag("stsc", a("0000000000000000")), new c.Iso.RawTag("stsz", a("000000000000000000000000")), new c.Iso.RawTag("stco", a("0000000000000000")))))) : f === this.videoTrackState && (w = new c.Iso.TrackBox(new c.Iso.TrackHeaderBox(3, f.trackId, -1, d.width, d.height, 0, k), new c.Iso.MediaBox(new c.Iso.MediaHeaderBox(d.timescale, -1, d.language), new c.Iso.HandlerBox("vide", "VideoHandler"), new c.Iso.MediaInformationBox(new c.Iso.VideoMediaHeaderBox, new c.Iso.DataInformationBox(new c.Iso.DataReferenceBox([new c.Iso.DataEntryUrlBox(c.Iso.SELF_CONTAINED_DATA_REFERENCE_FLAG)])), + new c.Iso.SampleTableBox(new c.Iso.SampleDescriptionBox([b]), new c.Iso.RawTag("stts", a("0000000000000000")), new c.Iso.RawTag("stsc", a("0000000000000000")), new c.Iso.RawTag("stsz", a("000000000000000000000000")), new c.Iso.RawTag("stco", a("0000000000000000"))))))); + m.push(w); + } + k = new c.Iso.MovieExtendsBox(null, [new c.Iso.TrackExtendsBox(1, 1, 0, 0, 0), new c.Iso.TrackExtendsBox(2, 1, 0, 0, 0)], null); + f = new c.Iso.BoxContainerBox("udat", [new c.Iso.MetaBox(new c.Iso.RawTag("hdlr", a("00000000000000006D6469726170706C000000000000000000")), [new c.Iso.RawTag("ilst", a("00000025A9746F6F0000001D6461746100000001000000004C61766635342E36332E313034"))])]); + d = new c.Iso.MovieHeaderBox(1E3, 0, this.trackStates.length + 1); + m = new c.Iso.MovieBox(d, m, k, f); + e = new c.Iso.FileTypeBox("isom", 512, e); + k = e.layout(0); + f = m.layout(k); + k = new Uint8Array(k + f); + e.write(k); + m.write(k); + this.trackStates.map(function(b) { + return b.mimeTypeCodec; + }); + this.ondata(k); + this.filePos += k.length; + this.state = 2; + } + }; + e.prototype._chunk = function() { + var a, e = this.cachedPackets; + if (0 !== e.length) { + for (var k = [], f = 0, d = [], b = [], g = 0;g < this.trackStates.length;g++) { + var m = this.trackStates[g], l = m.trackInfo, t = m.trackId, s = e.filter(function(b) { + return b.trackId === t; + }); + if (0 !== s.length) { + var p = new c.Iso.TrackFragmentBaseMediaDecodeTimeBox(m.cachedDuration), u; + b.push(f); + switch(l.codecId) { + case 10: + ; + case 2: + u = []; + for (a = 0;a < s.length;a++) { + var v = s[a].packet, K = Math.round(v.samples * l.timescale / l.samplerate); + k.push(v.data); + f += v.data.length; + u.push({duration:K, size:v.data.length}); + m.samplesProcessed += v.samples; + } + a = 32; + a = new c.Iso.TrackFragmentHeaderBox(a, t, 0, 0, 0, 0, 33554432); + s = 769; + u = new c.Iso.TrackRunBox(s, u, 0, 0); + m.cachedDuration = Math.round(m.samplesProcessed * l.timescale / l.samplerate); + break; + case 7: + ; + case 4: + u = []; + v = m.samplesProcessed; + K = Math.round(v * l.timescale / l.framerate); + for (a = 0;a < s.length;a++) { + var y = s[a].packet; + v++; + var D = Math.round(v * l.timescale / l.framerate), L = D - K, K = D, H = Math.round(v * l.timescale / l.framerate + y.compositionTime * l.timescale / 1E3); + k.push(y.data); + f += y.data.length; + u.push({duration:L, size:y.data.length, flags:1 === y.frameType ? 33554432 : 16842752, compositionTimeOffset:H - D}); + } + a = 32; + a = new c.Iso.TrackFragmentHeaderBox(a, t, 0, 0, 0, 0, 33554432); + s = 3841; + u = new c.Iso.TrackRunBox(s, u, 0, 0); + m.cachedDuration = K; + m.samplesProcessed = v; + break; + default: + throw Error("Un codec");; + } + m = new c.Iso.TrackFragmentBox(a, p, u); + d.push(m); + } + } + this.cachedPackets.splice(0, e.length); + g = new c.Iso.MovieFragmentHeaderBox(++this.chunkIndex); + e = new c.Iso.MovieFragmentBox(g, d); + f = e.layout(0); + k = new c.Iso.MediaDataBox(k); + m = k.layout(f); + l = f + 8; + for (g = 0;g < d.length;g++) { + d[g].run.dataOffset = l + b[g]; + } + d = new Uint8Array(f + m); + e.write(d); + k.write(d); + this.ondata(d); + this.filePos += d.length; + } + }; + return e; + }(); + c.MP4Mux = p; + c.parseFLVMetadata = function(a) { + var e = [], c = -1, k = -1, f = +a.asGetPublicProperty("duration"), d, b, g = a.asGetPublicProperty("audiocodecid"); + switch(g) { + case 2: + ; + case "mp3": + d = "mp3"; + b = 2; + break; + case 10: + ; + case "mp4a": + d = "mp4a"; + b = 10; + break; + default: + if (!isNaN(g)) { + throw Error("Unsupported audio codec: " + g); + } + d = null; + b = -1; + } + var m, l, h = a.asGetPublicProperty("videocodecid"); + switch(h) { + case 4: + ; + case "vp6f": + m = "vp6f"; + l = 4; + break; + case 7: + ; + case "avc1": + m = "avc1"; + l = 7; + break; + default: + if (!isNaN(h)) { + throw Error("Unsupported video codec: " + h); + } + m = null; + l = -1; + } + d = null === d ? null : {codecDescription:d, codecId:b, language:"und", timescale:+a.asGetPublicProperty("audiosamplerate") || 44100, samplerate:+a.asGetPublicProperty("audiosamplerate") || 44100, channels:+a.asGetPublicProperty("audiochannels") || 2, samplesize:16}; + m = null === m ? null : {codecDescription:m, codecId:l, language:"und", timescale:6E4, framerate:+a.asGetPublicProperty("videoframerate") || +a.asGetPublicProperty("framerate"), width:+a.asGetPublicProperty("width"), height:+a.asGetPublicProperty("height")}; + if (a = a.asGetPublicProperty("trackinfo")) { + for (l = 0;l < a.length;l++) { + b = a[l]; + var s = b.asGetPublicProperty("sampledescription")[0]; + s.asGetPublicProperty("sampletype") === g ? (d.language = b.asGetPublicProperty("language"), d.timescale = +b.asGetPublicProperty("timescale")) : s.asGetPublicProperty("sampletype") === h && (m.language = b.asGetPublicProperty("language"), m.timescale = +b.asGetPublicProperty("timescale")); + } + } + m && (k = e.length, e.push(m)); + d && (c = e.length, e.push(d)); + return{tracks:e, duration:f, audioTrackId:c, videoTrackId:k}; + }; + })(c.MP4 || (c.MP4 = {})); +})(RtmpJs || (RtmpJs = {})); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(h) { + function p(a) { + switch(a) { + case e: + ; + case -m: + return 0; + case l: + ; + case -l: + return-1; + case m: + ; + case -e: + return 0; + default: + return Math.cos(a); + } + } + function u(a) { + switch(a) { + case e: + ; + case -m: + return 1; + case l: + ; + case -l: + return 0; + case m: + ; + case -e: + return-1; + default: + return Math.sin(a); + } + } + var l = Math.PI, e = l / 2, m = l + e, t = 2 * l, q = function(a) { + function e(a, d, b, g, c, k) { + void 0 === a && (a = 1); + void 0 === d && (d = 0); + void 0 === b && (b = 0); + void 0 === g && (g = 1); + void 0 === c && (c = 0); + void 0 === k && (k = 0); + var m = this._data = new Float64Array(6); + m[0] = a; + m[1] = d; + m[2] = b; + m[3] = g; + m[4] = c; + m[5] = k; + } + __extends(e, a); + e.FromUntyped = function(a) { + return new c.geom.Matrix(a.a, a.b, a.c, a.d, a.tx, a.ty); + }; + e.FromDataBuffer = function(a) { + return new c.geom.Matrix(a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat()); + }; + Object.defineProperty(e.prototype, "a", {get:function() { + return this._data[0]; + }, set:function(a) { + this._data[0] = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "b", {get:function() { + return this._data[1]; + }, set:function(a) { + this._data[1] = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "c", {get:function() { + return this._data[2]; + }, set:function(a) { + this._data[2] = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "d", {get:function() { + return this._data[3]; + }, set:function(a) { + this._data[3] = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "tx", {get:function() { + return this._data[4]; + }, set:function(a) { + this._data[4] = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "ty", {get:function() { + return this._data[5]; + }, set:function(a) { + this._data[5] = a; + }, enumerable:!0, configurable:!0}); + e.prototype.concat = function(a) { + var d = this._data; + a = a._data; + var b = d[0] * a[0], e = 0, c = 0, k = d[3] * a[3], m = d[4] * a[0] + a[4], l = d[5] * a[3] + a[5]; + if (0 !== d[1] || 0 !== d[2] || 0 !== a[1] || 0 !== a[2]) { + b += d[1] * a[2], k += d[2] * a[1], e += d[0] * a[1] + d[1] * a[3], c += d[2] * a[0] + d[3] * a[2], m += d[5] * a[2], l += d[4] * a[1]; + } + d[0] = b; + d[1] = e; + d[2] = c; + d[3] = k; + d[4] = m; + d[5] = l; + }; + e.prototype.preMultiply = function(a) { + this.preMultiplyInto(a, this); + }; + e.prototype.preMultiplyInto = function(a, d) { + var b = this._data, e = a._data, c = d._data, k = e[0] * b[0], m = 0, l = 0, n = e[3] * b[3], h = e[4] * b[0] + b[4], q = e[5] * b[3] + b[5]; + if (0 !== e[1] || 0 !== e[2] || 0 !== b[1] || 0 !== b[2]) { + k += e[1] * b[2], n += e[2] * b[1], m += e[0] * b[1] + e[1] * b[3], l += e[2] * b[0] + e[3] * b[2], h += e[5] * b[2], q += e[4] * b[1]; + } + c[0] = k; + c[1] = m; + c[2] = l; + c[3] = n; + c[4] = h; + c[5] = q; + }; + e.prototype.invert = function() { + this.invertInto(this); + }; + e.prototype.invertInto = function(a) { + var d = this._data, b = a._data, e = d[1], c = d[2], k = d[4], m = d[5]; + if (0 === e && 0 === c) { + var l = b[0] = 1 / d[0], d = b[3] = 1 / d[3]; + b[1] = b[2] = 0; + b[4] = -l * k; + b[5] = -d * m; + } else { + var l = d[0], d = d[3], n = l * d - e * c; + 0 === n ? a.identity() : (n = 1 / n, a = 0, a = b[0] = d * n, e = b[1] = -e * n, c = b[2] = -c * n, d = b[3] = l * n, b[4] = -(a * k + c * m), b[5] = -(e * k + d * m)); + } + }; + e.prototype.identity = function() { + var a = this._data; + a[0] = a[3] = 1; + a[1] = a[2] = a[4] = a[5] = 0; + }; + e.prototype.createBox = function(a, d, b, e, c) { + void 0 === b && (b = 0); + void 0 === e && (e = 0); + void 0 === c && (c = 0); + var k = this._data; + if (0 !== b) { + var m = p(b); + b = u(b); + k[0] = m * a; + k[1] = b * d; + k[2] = -b * a; + k[3] = m * d; + } else { + k[0] = a, k[1] = 0, k[2] = 0, k[3] = d; + } + k[4] = e; + k[5] = c; + }; + e.prototype.createGradientBox = function(a, d, b, e, c) { + void 0 === b && (b = 0); + void 0 === e && (e = 0); + void 0 === c && (c = 0); + this.createBox(a / 1638.4, d / 1638.4, b, e + a / 2, c + d / 2); + }; + e.prototype.rotate = function(a) { + a = +a; + if (0 !== a) { + var d = this._data, b = p(a); + a = u(a); + var e = d[0], c = d[1], k = d[2], m = d[3], l = d[4], n = d[5]; + d[0] = e * b - c * a; + d[1] = e * a + c * b; + d[2] = k * b - m * a; + d[3] = k * a + m * b; + d[4] = l * b - n * a; + d[5] = l * a + n * b; + } + }; + e.prototype.translate = function(a, d) { + var b = this._data; + b[4] += a; + b[5] += d; + }; + e.prototype.scale = function(a, d) { + var b = this._data; + 1 !== a && (b[0] *= a, b[2] *= a, b[4] *= a); + 1 !== d && (b[1] *= d, b[3] *= d, b[5] *= d); + }; + e.prototype.deltaTransformPoint = function(a) { + return new h.Point(this._data[0] * a.x + this._data[2] * a.y, this._data[1] * a.x + this._data[3] * a.y); + }; + e.prototype.transformX = function(a, d) { + var b = this._data; + return b[0] * a + b[2] * d + b[4]; + }; + e.prototype.transformY = function(a, d) { + var b = this._data; + return b[1] * a + b[3] * d + b[5]; + }; + e.prototype.transformPoint = function(a) { + var d = this._data; + return new h.Point(d[0] * a.x + d[2] * a.y + d[4], d[1] * a.x + d[3] * a.y + d[5]); + }; + e.prototype.transformPointInPlace = function(a) { + var d = this._data; + a.setTo(d[0] * a.x + d[2] * a.y + d[4], d[1] * a.x + d[3] * a.y + d[5]); + return a; + }; + e.prototype.transformBounds = function(a) { + var d = this._data, b = d[0], e = d[1], c = d[2], k = d[3], m = d[4], l = d[5], n = a.xMin, h = a.yMin, q = a.width, t = a.height, d = Math.round(b * n + c * h + m), s = Math.round(e * n + k * h + l), p = Math.round(b * (n + q) + c * h + m), u = Math.round(e * (n + q) + k * h + l), v = Math.round(b * (n + q) + c * (h + t) + m), q = Math.round(e * (n + q) + k * (h + t) + l), b = Math.round(b * n + c * (h + t) + m), e = Math.round(e * n + k * (h + t) + l), k = 0; + d > p && (k = d, d = p, p = k); + v > b && (k = v, v = b, b = k); + a.xMin = d < v ? d : v; + a.xMax = p > b ? p : b; + s > u && (k = s, s = u, u = k); + q > e && (k = q, q = e, e = k); + a.yMin = s < q ? s : q; + a.yMax = u > e ? u : e; + }; + e.prototype.getDeterminant = function() { + var a = this._data; + return a[0] * a[3] - a[1] * a[2]; + }; + e.prototype.getScaleX = function() { + var a = this._data; + if (1 === a[0] && 0 === a[1]) { + return 1; + } + a = Math.sqrt(a[0] * a[0] + a[1] * a[1]); + return 0 > this.getDeterminant() ? -a : a; + }; + e.prototype.getScaleY = function() { + var a = this._data; + if (0 === a[2] && 1 === a[3]) { + return 1; + } + a = Math.sqrt(a[2] * a[2] + a[3] * a[3]); + return 0 > this.getDeterminant() ? -a : a; + }; + e.prototype.getAbsoluteScaleX = function() { + return Math.abs(this.getScaleX()); + }; + e.prototype.getAbsoluteScaleY = function() { + return Math.abs(this.getScaleY()); + }; + e.prototype.getSkewX = function() { + return Math.atan2(this._data[3], this._data[2]) - Math.PI / 2; + }; + e.prototype.getSkewY = function() { + return Math.atan2(this._data[1], this._data[0]); + }; + e.prototype.copyFrom = function(a) { + var d = this._data; + a = a._data; + d[0] = a[0]; + d[1] = a[1]; + d[2] = a[2]; + d[3] = a[3]; + d[4] = a[4]; + d[5] = a[5]; + }; + e.prototype.copyFromUntyped = function(a) { + var d = this._data; + d[0] = a.a; + d[1] = a.b; + d[2] = a.c; + d[3] = a.d; + d[4] = a.tx; + d[5] = a.ty; + }; + e.prototype.setTo = function(a, d, b, e, c, k) { + var m = this._data; + m[0] = a; + m[1] = d; + m[2] = b; + m[3] = e; + m[4] = c; + m[5] = k; + }; + e.prototype.toTwipsInPlace = function() { + var a = this._data; + a[4] = 20 * a[4] | 0; + a[5] = 20 * a[5] | 0; + return this; + }; + e.prototype.toPixelsInPlace = function() { + var a = this._data; + a[4] /= 20; + a[5] /= 20; + return this; + }; + e.prototype.copyRowTo = function(a, d) { + var b = this._data; + a >>>= 0; + 0 === a ? (d.x = b[0], d.y = b[2], d.z = b[4]) : 1 === a ? (d.x = b[1], d.y = b[3], d.z = b[5]) : 2 === a && (d.x = 0, d.y = 0, d.z = 1); + }; + e.prototype.copyColumnTo = function(a, d) { + var b = this._data; + a >>>= 0; + 0 === a ? (d.x = b[0], d.y = b[1], d.z = 0) : 1 === a ? (d.x = b[2], d.y = b[3], d.z = 0) : 2 === a && (d.x = b[4], d.y = b[5], d.z = 1); + }; + e.prototype.copyRowFrom = function(a, d) { + var b = this._data; + a >>>= 0; + 0 === a ? (b[0] = d.x, b[2] = d.y, b[4] = d.z) : 1 === a && (b[1] = d.x, b[3] = d.y, b[5] = d.z); + }; + e.prototype.copyColumnFrom = function(a, d) { + var b = this._data; + a >>>= 0; + 0 === a ? (b[0] = d.x, b[2] = d.y, b[4] = d.z) : 1 === a && (b[1] = d.x, b[3] = d.y, b[5] = d.z); + }; + e.prototype.updateScaleAndRotation = function(a, d, b, e) { + var c = this._data; + if (0 !== b && b !== t || 0 !== e && e !== t) { + var k = p(b), m = u(b); + b === e ? (c[0] = k * a, c[1] = m * a) : (c[0] = p(e) * a, c[1] = u(e) * a); + c[2] = -m * d; + c[3] = k * d; + } else { + c[0] = a, c[1] = c[2] = 0, c[3] = d; + } + }; + e.prototype.clone = function() { + var a = this._data; + return new c.geom.Matrix(a[0], a[1], a[2], a[3], a[4], a[5]); + }; + e.prototype.equals = function(a) { + var d = this._data; + a = a._data; + return d[0] === a[0] && d[1] === a[1] && d[2] === a[2] && d[3] === a[3] && d[4] === a[4] && d[5] === a[5]; + }; + e.prototype.toString = function() { + var a = this._data; + return "(a=" + a[0] + ", b=" + a[1] + ", c=" + a[2] + ", d=" + a[3] + ", tx=" + a[4] + ", ty=" + a[5] + ")"; + }; + e.prototype.writeExternal = function(a) { + var d = this._data; + a.writeFloat(d[0]); + a.writeFloat(d[1]); + a.writeFloat(d[2]); + a.writeFloat(d[3]); + a.writeFloat(d[4]); + a.writeFloat(d[5]); + }; + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.FROZEN_IDENTITY_MATRIX = Object.freeze(new e); + e.TEMP_MATRIX = new e; + return e; + }(a.ASNative); + h.Matrix = q; + })(c.geom || (c.geom = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(v) { + function p(a, e, c, k, f, d, b) { + var g = e * e, m = c * c, l = k * k, p = g + m + l, u = Math.sqrt(p); + e /= u; + c /= u; + k /= u; + g /= p; + m /= p; + l /= p; + p = Math.cos(a); + a = Math.sin(a); + return new h.geom.Matrix3D([g + (m + l) * p, e * c * (1 - p) + k * a, e * k * (1 - p) - c * a, 0, e * c * (1 - p) - k * a, m + (g + l) * p, c * k * (1 - p) + e * a, 0, e * k * (1 - p) + c * a, c * k * (1 - p) - e * a, l + (g + m) * p, 0, (f * (m + l) - e * (d * c + b * k)) * (1 - p) + (d * k - b * c) * a, (d * (g + l) - c * (f * e + b * k)) * (1 - p) + (b * e - f * k) * a, (b * (g + m) - k * (f * e + d * c)) * (1 - p) + (f * c - d * e) * a, 1]); + } + var u = c.Debug.notImplemented, l = c.AVM2.Runtime.asCoerceString, e = new Uint32Array([0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15]), m = function(c) { + function m(a) { + void 0 === a && (a = null); + this._matrix = new Float32Array(16); + a && 16 <= a.length ? this.copyRawDataFrom(a) : this.identity(); + } + __extends(m, c); + m.interpolate = function(a, e, f) { + u("public flash.geom.Matrix3D::static interpolate"); + }; + Object.defineProperty(m.prototype, "rawData", {get:function() { + var e = new a.Float64Vector; + this.copyRawDataTo(e); + return e; + }, set:function(a) { + this.copyRawDataFrom(a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "position", {get:function() { + var a = this._matrix; + return new h.geom.Vector3D(a[12], a[13], a[14]); + }, set:function(a) { + var e = this._matrix; + e[12] = a.x; + e[13] = a.y; + e[14] = a.z; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "determinant", {get:function() { + var a = this._matrix, e = a[4], f = a[8], d = a[12], b = a[5], g = a[9], c = a[13], m = a[6], l = a[10], h = a[14], q = a[7], t = a[11], s = a[15]; + return a[0] * (b * (l * s - t * h) - m * (g * s - t * c) + q * (g * h - l * c)) - a[1] * (e * (l * s - t * h) - m * (f * s - t * d) + q * (f * h - l * d)) + a[2] * (e * (g * s - t * c) - b * (f * s - t * d) + q * (f * c - g * d)) - a[3] * (e * (g * h - l * c) - b * (f * h - l * d) + m * (f * c - g * d)); + }, enumerable:!0, configurable:!0}); + m.prototype.clone = function() { + return new h.geom.Matrix3D(this._matrix); + }; + m.prototype.copyToMatrix3D = function(a) { + a._matrix.set(this._matrix); + }; + m.prototype.append = function(a) { + var e = a._matrix, f = this._matrix; + a = this._matrix; + var d = e[0], b = e[4], g = e[8], c = e[12], m = e[1], l = e[5], h = e[9], q = e[13], t = e[2], s = e[6], p = e[10], u = e[14], v = e[3], L = e[7], H = e[11], e = e[15], J = f[0], C = f[4], E = f[8], F = f[12], I = f[1], G = f[5], Z = f[9], Q = f[13], S = f[2], O = f[6], P = f[10], V = f[14], $ = f[3], W = f[7], x = f[11], f = f[15]; + a[0] = d * J + b * I + g * S + c * $; + a[1] = m * J + l * I + h * S + q * $; + a[2] = t * J + s * I + p * S + u * $; + a[3] = v * J + L * I + H * S + e * $; + a[4] = d * C + b * G + g * O + c * W; + a[5] = m * C + l * G + h * O + q * W; + a[6] = t * C + s * G + p * O + u * W; + a[7] = v * C + L * G + H * O + e * W; + a[8] = d * E + b * Z + g * P + c * x; + a[9] = m * E + l * Z + h * P + q * x; + a[10] = t * E + s * Z + p * P + u * x; + a[11] = v * E + L * Z + H * P + e * x; + a[12] = d * F + b * Q + g * V + c * f; + a[13] = m * F + l * Q + h * V + q * f; + a[14] = t * F + s * Q + p * V + u * f; + a[15] = v * F + L * Q + H * V + e * f; + }; + m.prototype.prepend = function(a) { + var e = this._matrix, c = a._matrix; + a = this._matrix; + var d = e[0], b = e[4], g = e[8], m = e[12], l = e[1], h = e[5], q = e[9], t = e[13], s = e[2], p = e[6], u = e[10], v = e[14], D = e[3], L = e[7], H = e[11], e = e[15], J = c[0], C = c[4], E = c[8], F = c[12], I = c[1], G = c[5], Z = c[9], Q = c[13], S = c[2], O = c[6], P = c[10], V = c[14], $ = c[3], W = c[7], x = c[11], c = c[15]; + a[0] = d * J + b * I + g * S + m * $; + a[1] = l * J + h * I + q * S + t * $; + a[2] = s * J + p * I + u * S + v * $; + a[3] = D * J + L * I + H * S + e * $; + a[4] = d * C + b * G + g * O + m * W; + a[5] = l * C + h * G + q * O + t * W; + a[6] = s * C + p * G + u * O + v * W; + a[7] = D * C + L * G + H * O + e * W; + a[8] = d * E + b * Z + g * P + m * x; + a[9] = l * E + h * Z + q * P + t * x; + a[10] = s * E + p * Z + u * P + v * x; + a[11] = D * E + L * Z + H * P + e * x; + a[12] = d * F + b * Q + g * V + m * c; + a[13] = l * F + h * Q + q * V + t * c; + a[14] = s * F + p * Q + u * V + v * c; + a[15] = D * F + L * Q + H * V + e * c; + }; + m.prototype.invert = function() { + var a = this.determinant; + if (1E-7 > Math.abs(a)) { + return!1; + } + var a = 1 / a, e = this._matrix, c = e[0], d = e[1], b = e[2], g = e[3], m = e[4], l = e[5], h = e[6], q = e[7], t = e[8], s = e[9], p = e[10], u = e[11], v = e[12], D = e[13], L = e[14], H = e[15]; + e[0] = a * (l * (p * H - L * u) - s * (h * H - L * q) + D * (h * u - p * q)); + e[1] = -a * (d * (p * H - L * u) - s * (b * H - L * g) + D * (b * u - p * g)); + e[2] = a * (d * (h * H - L * q) - l * (b * H - L * g) + D * (b * q - h * g)); + e[3] = -a * (d * (h * u - p * q) - l * (b * u - p * g) + s * (b * q - h * g)); + e[4] = -a * (m * (p * H - L * u) - t * (h * H - L * q) + v * (h * u - p * q)); + e[5] = a * (c * (p * H - L * u) - t * (b * H - L * g) + v * (b * u - p * g)); + e[6] = -a * (c * (h * H - L * q) - m * (b * H - L * g) + v * (b * q - h * g)); + e[7] = a * (c * (h * u - p * q) - m * (b * u - p * g) + t * (b * q - h * g)); + e[8] = a * (m * (s * H - D * u) - t * (l * H - D * q) + v * (l * u - s * q)); + e[9] = -a * (c * (s * H - D * u) - t * (d * H - D * g) + v * (d * u - s * g)); + e[10] = a * (c * (l * H - D * q) - m * (d * H - D * g) + v * (d * q - l * g)); + e[11] = -a * (c * (l * u - s * q) - m * (d * u - s * g) + t * (d * q - l * g)); + e[12] = -a * (m * (s * L - D * p) - t * (l * L - D * h) + v * (l * p - s * h)); + e[13] = a * (c * (s * L - D * p) - t * (d * L - D * b) + v * (d * p - s * b)); + e[14] = -a * (c * (l * L - D * h) - m * (d * L - D * b) + v * (d * h - l * b)); + e[15] = a * (c * (l * p - s * h) - m * (d * p - s * b) + t * (d * h - l * b)); + return!0; + }; + m.prototype.identity = function() { + var a = this._matrix; + a[0] = a[5] = a[10] = a[15] = 1; + a[1] = a[2] = a[3] = a[4] = a[6] = a[7] = a[8] = a[9] = a[11] = a[12] = a[13] = a[14] = 0; + }; + m.prototype.decompose = function(a) { + void 0 === a && (a = "eulerAngles"); + l(a); + u("public flash.geom.Matrix3D::decompose"); + }; + m.prototype.recompose = function(a, e) { + void 0 === e && (e = "eulerAngles"); + l(e); + u("public flash.geom.Matrix3D::recompose"); + }; + m.prototype.appendTranslation = function(a, e, c) { + a = +a; + e = +e; + c = +c; + var d = this._matrix, b = d[3], g = d[7], m = d[11], l = d[15]; + d[0] += a * b; + d[1] += e * b; + d[2] += c * b; + d[4] += a * g; + d[5] += e * g; + d[6] += c * g; + d[8] += a * m; + d[9] += e * m; + d[10] += c * m; + d[12] += a * l; + d[13] += e * l; + d[14] += c * l; + }; + m.prototype.appendRotation = function(a, e, c) { + void 0 === c && (c = null); + this.append(p(+a / 180 * Math.PI, e.x, e.y, e.z, c ? c.x : 0, c ? c.y : 0, c ? c.z : 0)); + }; + m.prototype.appendScale = function(a, e, c) { + a = +a; + e = +e; + c = +c; + var d = this._matrix; + d[0] *= a; + d[1] *= e; + d[2] *= c; + d[4] *= a; + d[5] *= e; + d[6] *= c; + d[8] *= a; + d[9] *= e; + d[10] *= c; + d[12] *= a; + d[13] *= e; + d[14] *= c; + }; + m.prototype.prependTranslation = function(a, e, c) { + a = +a; + e = +e; + c = +c; + var d = this._matrix, b = d[1], g = d[5], m = d[9], l = d[2], h = d[6], q = d[10], t = d[3], s = d[7], p = d[11]; + d[12] += d[0] * a + d[4] * e + d[8] * c; + d[13] += b * a + g * e + m * c; + d[14] += l * a + h * e + q * c; + d[15] += t * a + s * e + p * c; + }; + m.prototype.prependRotation = function(a, e, c) { + void 0 === c && (c = null); + this.prepend(p(+a / 180 * Math.PI, e.x, e.y, e.z, c ? c.x : 0, c ? c.y : 0, c ? c.z : 0)); + }; + m.prototype.prependScale = function(a, e, c) { + a = +a; + e = +e; + c = +c; + var d = this._matrix; + d[0] *= a; + d[1] *= a; + d[2] *= a; + d[3] *= a; + d[4] *= e; + d[5] *= e; + d[6] *= e; + d[7] *= e; + d[8] *= c; + d[9] *= c; + d[10] *= c; + d[11] *= c; + }; + m.prototype.transformVector = function(a) { + var e = this._matrix, c = a.x, d = a.y; + a = a.z; + return new h.geom.Vector3D(e[0] * c + e[4] * d + e[8] * a + e[12], e[1] * c + e[5] * d + e[9] * a + e[13], e[2] * c + e[6] * d + e[10] * a + e[14]); + }; + m.prototype.deltaTransformVector = function(a) { + var e = this._matrix, c = a.x, d = a.y; + a = a.z; + return new h.geom.Vector3D(e[0] * c + e[4] * d + e[8] * a, e[1] * c + e[5] * d + e[9] * a, e[2] * c + e[6] * d + e[10] * a); + }; + m.prototype.transformVectors = function(a, e) { + for (var c = this._matrix, d = c[0], b = c[4], g = c[8], m = c[12], l = c[1], h = c[5], q = c[9], t = c[13], s = c[2], p = c[6], u = c[10], c = c[14], v = 0;v < a.length - 2;v += 3) { + var D = a.asGetNumericProperty(v), L = a.asGetNumericProperty(v + 1), H = a.asGetNumericProperty(v + 2); + e.push(d * D + b * L + g * H + m); + e.push(l * D + h * L + q * H + t); + e.push(s * D + p * L + u * H + c); + } + }; + m.prototype.transpose = function() { + var a = this._matrix, e; + e = a[1]; + a[1] = a[4]; + a[4] = e; + e = a[2]; + a[2] = a[8]; + a[5] = e; + e = a[3]; + a[3] = a[12]; + a[12] = e; + e = a[6]; + a[6] = a[9]; + a[9] = e; + e = a[7]; + a[7] = a[13]; + a[13] = e; + e = a[11]; + a[11] = a[14]; + a[14] = e; + }; + m.prototype.pointAt = function(a, e, c) { + u("public flash.geom.Matrix3D::pointAt"); + }; + m.prototype.interpolateTo = function(a, e) { + u("public flash.geom.Matrix3D::interpolateTo"); + }; + m.prototype.copyFrom = function(a) { + this._matrix.set(a._matrix); + }; + m.prototype.copyRawDataTo = function(a) { + var c = 0, f = !1; + void 0 === c && (c = 0); + void 0 === f && (f = !1); + var c = c >>> 0, d = this._matrix; + if (f) { + for (f = 0, c |= 0;16 > f;f++, c++) { + a.asSetNumericProperty(c, d[e[f]]); + } + } else { + for (f = 0, c |= 0;16 > f;f++, c++) { + a.asSetNumericProperty(c, d[f]); + } + } + }; + m.prototype.copyRawDataFrom = function(a) { + var c = 0, f = !1; + void 0 === c && (c = 0); + void 0 === f && (f = !1); + var c = c >>> 0, d = this._matrix; + if (f) { + for (f = 0, c |= 0;16 > f;f++, c++) { + d[e[f]] = a.asGetNumericProperty(c) || 0; + } + } else { + for (f = 0, c |= 0;16 > f;f++, c++) { + d[f] = a.asGetNumericProperty(c) || 0; + } + } + }; + m.prototype.copyRowTo = function(a, e) { + var c = a >>> 0 | 0, d = this._matrix; + e.x = d[c]; + e.y = d[c + 4]; + e.z = d[c + 8]; + e.w = d[c + 12]; + }; + m.prototype.copyColumnTo = function(a, e) { + var c = a >>> 0 << 2, d = this._matrix; + e.x = d[c]; + e.y = d[c + 1]; + e.z = d[c + 2]; + e.w = d[c + 3]; + }; + m.prototype.copyRowFrom = function(a, e) { + var c = a >>> 0 | 0, d = this._matrix; + d[c] = e.x; + d[c + 4] = e.y; + d[c + 8] = e.z; + d[c + 12] = e.w; + }; + m.prototype.copyColumnFrom = function(a, e) { + var c = a >>> 0 << 2, d = this._matrix; + d[c] = e.x; + d[c + 1] = e.y; + d[c + 2] = e.z; + d[c + 3] = e.w; + }; + m.classInitializer = null; + m.initializer = null; + m.classSymbols = null; + m.instanceSymbols = null; + return m; + }(a.ASNative); + v.Matrix3D = m; + })(h.geom || (h.geom = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.geom.Orientation3D"); + } + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.EULER_ANGLES = "eulerAngles"; + e.AXIS_ANGLE = "axisAngle"; + e.QUATERNION = "quaternion"; + return e; + }(a.ASNative); + h.Orientation3D = u; + })(h.geom || (h.geom = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.notImplemented, u = c.Debug.dummyConstructor, l = function(a) { + function c() { + u("public flash.geom.PerspectiveProjection"); + } + __extends(c, a); + Object.defineProperty(c.prototype, "fieldOfView", {get:function() { + s("public flash.geom.PerspectiveProjection::get fieldOfView"); + }, set:function(a) { + s("public flash.geom.PerspectiveProjection::set fieldOfView"); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "projectionCenter", {get:function() { + s("public flash.geom.PerspectiveProjection::get projectionCenter"); + }, set:function(a) { + s("public flash.geom.PerspectiveProjection::set projectionCenter"); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "focalLength", {get:function() { + s("public flash.geom.PerspectiveProjection::get focalLength"); + }, set:function(a) { + s("public flash.geom.PerspectiveProjection::set focalLength"); + }, enumerable:!0, configurable:!0}); + c.prototype.toMatrix3D = function() { + s("public flash.geom.PerspectiveProjection::toMatrix3D"); + }; + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.ASNative); + h.PerspectiveProjection = l; + })(h.geom || (h.geom = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function c(a, m) { + void 0 === a && (a = 0); + void 0 === m && (m = 0); + this.x = +a; + this.y = +m; + } + __extends(c, a); + Object.defineProperty(c.prototype, "native_x", {get:function() { + return this.x; + }, set:function(a) { + this.x = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "native_y", {get:function() { + return this.y; + }, set:function(a) { + this.y = a; + }, enumerable:!0, configurable:!0}); + c.prototype.Point = function(a, c) { + void 0 === a && (a = 0); + void 0 === c && (c = 0); + this.x = a; + this.y = c; + }; + Object.defineProperty(c.prototype, "length", {get:function() { + return Math.sqrt(this.x * this.x + this.y * this.y); + }, enumerable:!0, configurable:!0}); + c.interpolate = function(a, m, h) { + var q = 1 - h; + return new c(a.x * h + m.x * q, a.y * h + m.y * q); + }; + c.distance = function(a, c) { + var l = c.x - a.x, h = c.y - a.y; + return 0 === l ? Math.abs(h) : 0 === h ? Math.abs(l) : Math.sqrt(l * l + h * h); + }; + c.polar = function(a, m) { + a = +a; + m = +m; + return new c(a * Math.cos(m), a * Math.sin(m)); + }; + c.prototype.clone = function() { + return new c(this.x, this.y); + }; + c.prototype.offset = function(a, c) { + this.x += +a; + this.y += +c; + }; + c.prototype.equals = function(a) { + return this.x === a.x && this.y === a.y; + }; + c.prototype.subtract = function(a) { + return new c(this.x - a.x, this.y - a.y); + }; + c.prototype.add = function(a) { + return new c(this.x + a.x, this.y + a.y); + }; + c.prototype.normalize = function(a) { + if (0 !== this.x || 0 !== this.y) { + a = +a / this.length, this.x *= a, this.y *= a; + } + }; + c.prototype.copyFrom = function(a) { + this.x = a.x; + this.y = a.y; + }; + c.prototype.setTo = function(a, c) { + this.x = +a; + this.y = +c; + }; + c.prototype.toTwips = function() { + this.x = 20 * this.x | 0; + this.y = 20 * this.y | 0; + return this; + }; + c.prototype.toPixels = function() { + this.x /= 20; + this.y /= 20; + return this; + }; + c.prototype.round = function() { + this.x = Math.round(this.x); + this.y = Math.round(this.y); + return this; + }; + c.prototype.toString = function() { + return "(x=" + this.x + ", y=" + this.y + ")"; + }; + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.ASNative); + c.Point = h; + })(c.geom || (c.geom = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function l(a, c, l, h) { + void 0 === a && (a = 0); + void 0 === c && (c = 0); + void 0 === l && (l = 0); + void 0 === h && (h = 0); + this.x = +a; + this.y = +c; + this.width = +l; + this.height = +h; + } + __extends(l, a); + l.FromBounds = function(a) { + var c = a.xMin, h = a.yMin; + return new l(c / 20, h / 20, (a.xMax - c) / 20, (a.yMax - h) / 20); + }; + Object.defineProperty(l.prototype, "native_x", {get:function() { + return this.x; + }, set:function(a) { + this.x = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "native_y", {get:function() { + return this.y; + }, set:function(a) { + this.y = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "native_width", {get:function() { + return this.width; + }, set:function(a) { + this.width = +a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "native_height", {get:function() { + return this.height; + }, set:function(a) { + this.height = +a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "left", {get:function() { + return this.x; + }, set:function(a) { + a = +a; + this.width += this.x - a; + this.x = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "right", {get:function() { + return this.x + this.width; + }, set:function(a) { + this.width = +a - this.x; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "top", {get:function() { + return this.y; + }, set:function(a) { + a = +a; + this.height += this.y - a; + this.y = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "bottom", {get:function() { + return this.y + this.height; + }, set:function(a) { + this.height = +a - this.y; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "topLeft", {get:function() { + return new c.Point(this.left, this.top); + }, set:function(a) { + this.top = a.y; + this.left = a.x; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "bottomRight", {get:function() { + return new c.Point(this.right, this.bottom); + }, set:function(a) { + this.bottom = a.y; + this.right = a.x; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "size", {get:function() { + return new c.Point(this.width, this.height); + }, set:function(a) { + this.width = a.x; + this.height = a.y; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "area", {get:function() { + return this.width * this.height; + }, enumerable:!0, configurable:!0}); + l.prototype.clone = function() { + return new l(this.x, this.y, this.width, this.height); + }; + l.prototype.isEmpty = function() { + return 0 >= this.width || 0 >= this.height; + }; + l.prototype.setEmpty = function() { + this.height = this.width = this.y = this.x = 0; + }; + l.prototype.inflate = function(a, c) { + a = +a; + c = +c; + this.x -= a; + this.y -= c; + this.width += 2 * a; + this.height += 2 * c; + }; + l.prototype.inflatePoint = function(a) { + this.inflate(a.x, a.y); + }; + l.prototype.offset = function(a, c) { + this.x += +a; + this.y += +c; + }; + l.prototype.offsetPoint = function(a) { + this.offset(a.x, a.y); + }; + l.prototype.contains = function(a, c) { + a = +a; + c = +c; + return a >= this.x && a < this.right && c >= this.y && c < this.bottom; + }; + l.prototype.containsPoint = function(a) { + return this.contains(a.x, a.y); + }; + l.prototype.containsRect = function(a) { + var c = a.x + a.width, l = a.y + a.height, h = this.x + this.width, n = this.y + this.height; + return a.x >= this.x && a.x < h && a.y >= this.y && a.y < n && c > this.x && c <= h && l > this.y && l <= n; + }; + l.prototype.intersection = function(a) { + return this.clone().intersectInPlace(a); + }; + l.prototype.intersects = function(a) { + return Math.max(this.x, a.x) <= Math.min(this.right, a.right) && Math.max(this.y, a.y) <= Math.min(this.bottom, a.bottom); + }; + l.prototype.intersectInPlace = function(a) { + var c = this.x, l = this.y, h = a.x, n = a.y, k = Math.max(c, h), c = Math.min(c + this.width, h + a.width); + if (k <= c && (h = Math.max(l, n), a = Math.min(l + this.height, n + a.height), h <= a)) { + return this.setTo(k, h, c - k, a - h), this; + } + this.setEmpty(); + return this; + }; + l.prototype.intersectInPlaceInt32 = function(a) { + var c = this.x | 0, l = this.y | 0, h = this.width | 0, n = this.height | 0, k = a.x | 0, f = a.width | 0, d = Math.max(c, k) | 0, c = Math.min(c + h | 0, k + f | 0) | 0; + if (d <= c && (h = a.y | 0, k = a.height | 0, a = Math.max(l, h) | 0, l = Math.min(l + n | 0, h + k | 0), a <= l)) { + return this.setTo(d, a, c - d, l - a), this; + } + this.setEmpty(); + return this; + }; + l.prototype.union = function(a) { + return this.clone().unionInPlace(a); + }; + l.prototype.unionInPlace = function(a) { + if (a.isEmpty()) { + return this; + } + if (this.isEmpty()) { + return this.copyFrom(a), this; + } + var c = Math.min(this.x, a.x), l = Math.min(this.y, a.y); + this.setTo(c, l, Math.max(this.right, a.right) - c, Math.max(this.bottom, a.bottom) - l); + return this; + }; + l.prototype.equals = function(a) { + return this === a || this.x === a.x && this.y === a.y && this.width === a.width && this.height === a.height; + }; + l.prototype.copyFrom = function(a) { + this.x = a.x; + this.y = a.y; + this.width = a.width; + this.height = a.height; + }; + l.prototype.setTo = function(a, c, l, h) { + this.x = +a; + this.y = +c; + this.width = +l; + this.height = +h; + }; + l.prototype.toTwips = function() { + this.x = 20 * this.x | 0; + this.y = 20 * this.y | 0; + this.width = 20 * this.width | 0; + this.height = 20 * this.height | 0; + return this; + }; + l.prototype.getBaseWidth = function(a) { + return Math.abs(Math.cos(a)) * this.width + Math.abs(Math.sin(a)) * this.height; + }; + l.prototype.getBaseHeight = function(a) { + return Math.abs(Math.sin(a)) * this.width + Math.abs(Math.cos(a)) * this.height; + }; + l.prototype.toPixels = function() { + this.x /= 20; + this.y /= 20; + this.width /= 20; + this.height /= 20; + return this; + }; + l.prototype.snapInPlace = function() { + var a = Math.ceil(this.x + this.width), c = Math.ceil(this.y + this.height); + this.x = Math.floor(this.x); + this.y = Math.floor(this.y); + this.width = a - this.x; + this.height = c - this.y; + return this; + }; + l.prototype.roundInPlace = function() { + var a = Math.round(this.x + this.width), c = Math.round(this.y + this.height); + this.x = Math.round(this.x); + this.y = Math.round(this.y); + this.width = a - this.x; + this.height = c - this.y; + return this; + }; + l.prototype.toString = function() { + return "(x=" + this.x + ", y=" + this.y + ", w=" + this.width + ", h=" + this.height + ")"; + }; + l.prototype.writeExternal = function(a) { + a.writeFloat(this.x); + a.writeFloat(this.y); + a.writeFloat(this.width); + a.writeFloat(this.height); + }; + l.prototype.readExternal = function(a) { + this.x = a.readFloat(); + this.y = a.readFloat(); + this.width = a.readFloat(); + this.height = a.readFloat(); + }; + l.classInitializer = null; + l.initializer = null; + l.classSymbols = null; + l.instanceSymbols = null; + return l; + }(a.ASNative); + c.Rectangle = h; + })(c.geom || (c.geom = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(v) { + var p = c.Debug.notImplemented, u = c.Debug.somewhatImplemented, l = c.AVM2.Runtime.throwError, e = c.AVM2.Errors, m = function(a) { + function c(a) { + a || l("ArgumentError", e.NullPointerError, "displayObject"); + this._displayObject = a; + } + __extends(c, a); + Object.defineProperty(c.prototype, "matrix", {get:function() { + return this._displayObject._getMatrix().clone().toPixelsInPlace(); + }, set:function(a) { + this._displayObject._setMatrix(a, !0); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "colorTransform", {get:function() { + return this._displayObject._colorTransform.clone(); + }, set:function(a) { + this._displayObject._setColorTransform(a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "concatenatedMatrix", {get:function() { + var a = this._displayObject._getConcatenatedMatrix().clone().toPixelsInPlace(); + this._displayObject._stage || a.scale(5, 5); + return a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "concatenatedColorTransform", {get:function() { + return this._displayObject._getConcatenatedColorTransform(); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "pixelBounds", {get:function() { + p("public flash.geom.Transform::get pixelBounds"); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "matrix3D", {get:function() { + var a = this._displayObject._matrix3D; + return a && a.clone(); + }, set:function(a) { + v.Matrix3D.isType(a) || l("TypeError", e.CheckTypeFailedError, a, "flash.geom.Matrix3D"); + a = a.rawData; + this.matrix = new h.geom.Matrix(a.asGetPublicProperty(0), a.asGetPublicProperty(1), a.asGetPublicProperty(4), a.asGetPublicProperty(5), a.asGetPublicProperty(12), a.asGetPublicProperty(13)); + u("public flash.geom.Transform::set matrix3D"); + }, enumerable:!0, configurable:!0}); + c.prototype.getRelativeMatrix3D = function(a) { + p("public flash.geom.Transform::getRelativeMatrix3D"); + }; + Object.defineProperty(c.prototype, "perspectiveProjection", {get:function() { + p("public flash.geom.Transform::get perspectiveProjection"); + }, set:function(a) { + p("public flash.geom.Transform::set perspectiveProjection"); + }, enumerable:!0, configurable:!0}); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.ASNative); + v.Transform = m; + })(h.geom || (h.geom = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.notImplemented, u = c.Debug.dummyConstructor, l = function(a) { + function c() { + u("public flash.geom.Utils3D"); + } + __extends(c, a); + c.projectVector = function(a, e) { + s("public flash.geom.Utils3D::static projectVector"); + }; + c.projectVectors = function(a, e, c, k) { + s("public flash.geom.Utils3D::static projectVectors"); + }; + c.pointTowards = function(a, e, c, k, f) { + s("public flash.geom.Utils3D::static pointTowards"); + }; + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.ASNative); + h.Utils3D = l; + })(h.geom || (h.geom = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function c(a, m, l, h) { + void 0 === a && (a = 0); + void 0 === m && (m = 0); + void 0 === l && (l = 0); + void 0 === h && (h = 0); + this.x = +a; + this.y = +m; + this.z = +l; + this.w = +h; + } + __extends(c, a); + Object.defineProperty(c.prototype, "native_x", {get:function() { + return this.x; + }, set:function(a) { + this.x = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "native_y", {get:function() { + return this.y; + }, set:function(a) { + this.y = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "native_z", {get:function() { + return this.z; + }, set:function(a) { + this.z = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "native_w", {get:function() { + return this.w; + }, set:function(a) { + this.w = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "length", {get:function() { + return Math.sqrt(this.lengthSquared); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "lengthSquared", {get:function() { + return this.x * this.x + this.y * this.y + this.z * this.z; + }, enumerable:!0, configurable:!0}); + c.angleBetween = function(a, c) { + return Math.acos(a.dotProduct(c) / (a.length * c.length)); + }; + c.distance = function(a, c) { + return a.subtract(c).length; + }; + c.prototype.dotProduct = function(a) { + return this.x * a.x + this.y * a.y + this.z * a.z; + }; + c.prototype.crossProduct = function(a) { + return new c(this.y * a.z - this.z * a.y, this.z * a.x - this.x * a.z, this.x * a.y - this.y * a.x, 1); + }; + c.prototype.normalize = function() { + var a = this.length; + 0 !== a ? (this.x /= a, this.y /= a, this.z /= a) : this.x = this.y = this.z = 0; + return a; + }; + c.prototype.scaleBy = function(a) { + a = +a; + this.x *= a; + this.y *= a; + this.z *= a; + }; + c.prototype.incrementBy = function(a) { + this.x += a.x; + this.y += a.y; + this.z += a.z; + }; + c.prototype.decrementBy = function(a) { + this.x -= a.x; + this.y -= a.y; + this.z -= a.z; + }; + c.prototype.add = function(a) { + return new c(this.x + a.x, this.y + a.y, this.z + a.z); + }; + c.prototype.subtract = function(a) { + return new c(this.x - a.x, this.y - a.y, this.z - a.z); + }; + c.prototype.negate = function() { + this.x = -this.x; + this.y = -this.y; + this.z = -this.z; + }; + c.prototype.equals = function(a, c) { + return this.x === a.x && this.y === a.y && this.z === a.z && (!c || this.w === a.w); + }; + c.prototype.nearEquals = function(a, c, l) { + return Math.abs(this.x - a.x) < c && Math.abs(this.y - a.y) < c && Math.abs(this.z - a.z) < c && (!l || Math.abs(this.w - a.w) < c); + }; + c.prototype.project = function() { + this.x /= this.w; + this.y /= this.w; + this.z /= this.w; + }; + c.prototype.copyFrom = function(a) { + this.x = a.x; + this.y = a.y; + this.z = a.z; + }; + c.prototype.setTo = function(a, c, l) { + this.x = +a; + this.y = +c; + this.z = +l; + }; + c.prototype.clone = function() { + return new c(this.x, this.y, this.z, this.w); + }; + c.prototype.toString = function() { + return "Vector3D(" + this.x + ", " + this.y + ", " + this.z + ")"; + }; + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.X_AXIS = Object.freeze(new c(1, 0, 0)); + c.Y_AXIS = Object.freeze(new c(0, 1, 0)); + c.Z_AXIS = Object.freeze(new c(0, 0, 1)); + return c; + }(a.ASNative); + c.Vector3D = h; + })(c.geom || (c.geom = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.notImplemented, u = c.Debug.dummyConstructor, l = function(a) { + function c() { + u("public flash.accessibility.Accessibility"); + } + __extends(c, a); + Object.defineProperty(c, "active", {get:function() { + s("public flash.accessibility.Accessibility::get active"); + return c._active; + }, enumerable:!0, configurable:!0}); + c.sendEvent = function(a, e, c, k) { + s("public flash.accessibility.Accessibility::static sendEvent"); + }; + c.updateProperties = function() { + s("public flash.accessibility.Accessibility::static updateProperties"); + }; + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c._active = !1; + return c; + }(a.ASNative); + h.Accessibility = l; + })(h.accessibility || (h.accessibility = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.accessibility.AccessibilityImplementation"); + } + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.ASNative); + h.AccessibilityImplementation = u; + })(h.accessibility || (h.accessibility = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function c() { + } + __extends(c, a); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.ASNative); + c.AccessibilityProperties = h; + })(c.accessibility || (c.accessibility = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.assert, u = c.AVM2.Runtime.asCoerceString, l = function(a) { + function m(a, e, c) { + this._type = u(a); + this._bubbles = !!e; + this._cancelable = !!c; + this._currentTarget = this._target = null; + this._eventPhase = h.EventPhase.AT_TARGET; + this._isDefaultPrevented = this._stopImmediatePropagation = this._stopPropagation = !1; + } + __extends(m, a); + m.getInstance = function(a, e) { + var c; + void 0 === e && (e = !1); + void 0 === c && (c = !1); + var k = m._instances[a]; + k || (k = new m(a, e, c), m._instances[a] = k); + k._bubbles = e; + k._cancelable = c; + return k; + }; + m.getBroadcastInstance = function(a) { + var e, c; + void 0 === e && (e = !1); + void 0 === c && (c = !1); + var k = m._instances[a]; + k || (k = new m(a, e, c), m._instances[a] = k, s(m.isBroadcastEventType(a))); + k._isBroadcastEvent = !0; + k._bubbles = e; + k._cancelable = c; + return k; + }; + m.isBroadcastEventType = function(a) { + switch(a) { + case m.ENTER_FRAME: + ; + case m.EXIT_FRAME: + ; + case m.FRAME_CONSTRUCTED: + ; + case m.RENDER: + ; + case m.ACTIVATE: + ; + case m.DEACTIVATE: + return!0; + } + return!1; + }; + Object.defineProperty(m.prototype, "type", {get:function() { + return this._type; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "bubbles", {get:function() { + return this._bubbles; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "cancelable", {get:function() { + return this._cancelable; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "target", {get:function() { + return this._target; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "currentTarget", {get:function() { + return this._currentTarget; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "eventPhase", {get:function() { + return this._eventPhase; + }, enumerable:!0, configurable:!0}); + m.prototype.stopPropagation = function() { + this._stopPropagation = !0; + }; + m.prototype.stopImmediatePropagation = function() { + this._stopImmediatePropagation = this._stopPropagation = !0; + }; + m.prototype.preventDefault = function() { + this._cancelable && (this._isDefaultPrevented = !0); + }; + m.prototype.isDefaultPrevented = function() { + return this._isDefaultPrevented; + }; + m.prototype.isBroadcastEvent = function() { + return!!this._isBroadcastEvent; + }; + m.prototype.clone = function() { + return new m(this._type, this._bubbles, this._cancelable); + }; + m.prototype.toString = function() { + return this.formatToString("Event", "type", "bubbles", "cancelable", "eventPhase"); + }; + m.prototype.formatToString = function(a) { + for (var e = [], c = 1;c < arguments.length;c++) { + e[c - 1] = arguments[c]; + } + for (var c = "[" + a, k = 0;k < e.length;k++) { + var f = e[k], d = this.asGetPublicProperty(f); + "string" === typeof d && (d = '"' + d + '"'); + c += " " + f + "=" + d; + } + return c + "]"; + }; + m.classInitializer = function() { + m._instances = c.ObjectUtilities.createMap(); + }; + m.initializer = null; + m.classSymbols = null; + m.instanceSymbols = null; + m.ACTIVATE = "activate"; + m.ADDED = "added"; + m.ADDED_TO_STAGE = "addedToStage"; + m.CANCEL = "cancel"; + m.CHANGE = "change"; + m.CLEAR = "clear"; + m.CLOSE = "close"; + m.COMPLETE = "complete"; + m.CONNECT = "connect"; + m.COPY = "copy"; + m.CUT = "cut"; + m.DEACTIVATE = "deactivate"; + m.ENTER_FRAME = "enterFrame"; + m.FRAME_CONSTRUCTED = "frameConstructed"; + m.EXIT_FRAME = "exitFrame"; + m.FRAME_LABEL = "frameLabel"; + m.ID3 = "id3"; + m.INIT = "init"; + m.MOUSE_LEAVE = "mouseLeave"; + m.OPEN = "open"; + m.PASTE = "paste"; + m.REMOVED = "removed"; + m.REMOVED_FROM_STAGE = "removedFromStage"; + m.RENDER = "render"; + m.RESIZE = "resize"; + m.SCROLL = "scroll"; + m.TEXT_INTERACTION_MODE_CHANGE = "textInteractionModeChange"; + m.SELECT = "select"; + m.SELECT_ALL = "selectAll"; + m.SOUND_COMPLETE = "soundComplete"; + m.TAB_CHILDREN_CHANGE = "tabChildrenChange"; + m.TAB_ENABLED_CHANGE = "tabEnabledChange"; + m.TAB_INDEX_CHANGE = "tabIndexChange"; + m.UNLOAD = "unload"; + m.FULLSCREEN = "fullScreen"; + m.CONTEXT3D_CREATE = "context3DCreate"; + m.TEXTURE_READY = "textureReady"; + m.VIDEO_FRAME = "videoFrame"; + m.SUSPEND = "suspend"; + m.AVM1_INIT = "initialize"; + m.AVM1_CONSTRUCT = "construct"; + m.AVM1_LOAD = "load"; + return m; + }(a.ASNative); + h.Event = l; + })(h.events || (h.events = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(s) { + (function(v) { + var p = c.AVM2.Runtime.asCoerceString, u = c.isFunction, l = c.isNullOrUndefined, e = c.AVM2.Runtime.throwError, m = c.Debug.assert, t = c.AVM2.AS.traceEventsOption, q = function() { + return function(a, b, e) { + this.listener = a; + this.useCapture = b; + this.priority = e; + }; + }(), n = function() { + function a() { + this._aliasCount = 0; + this._entries = []; + } + a.prototype.isEmpty = function() { + return 0 === this._entries.length; + }; + a.prototype.insert = function(a, d, e) { + for (var c = this._entries, f = c.length, k = f - 1;0 <= k;k--) { + var m = c[k]; + if (m.listener === a) { + return; + } + if (e > m.priority) { + f = k; + } else { + break; + } + } + c = this.ensureNonAliasedEntries(); + c.splice(f, 0, new q(a, d, e)); + }; + a.prototype.ensureNonAliasedEntries = function() { + var a = this._entries; + 0 < this._aliasCount && (a = this._entries = a.slice(), this._aliasCount = 0); + return a; + }; + a.prototype.remove = function(a) { + for (var d = this._entries, e = 0;e < d.length;e++) { + if (d[e].listener === a) { + this.ensureNonAliasedEntries().splice(e, 1); + break; + } + } + }; + a.prototype.snapshot = function() { + this._aliasCount++; + return this._entries; + }; + a.prototype.releaseSnapshot = function(a) { + this._entries === a && 0 < this._aliasCount && this._aliasCount--; + }; + return a; + }(), k = function() { + function a() { + this.reset(); + } + a.prototype.reset = function() { + this._queues = Object.create(null); + }; + a.prototype.add = function(a, d) { + m(v.Event.isBroadcastEventType(a), "Can only register broadcast events."); + var e = this._queues[a] || (this._queues[a] = []); + 0 <= e.indexOf(d) || e.push(d); + }; + a.prototype.remove = function(a, d) { + m(v.Event.isBroadcastEventType(a), "Can only unregister broadcast events."); + var e = this._queues[a]; + m(e, "There should already be a queue for this."); + var c = e.indexOf(d); + m(0 <= c, "Target should be somewhere in this queue."); + e[c] = null; + m(0 > e.indexOf(d), "Target shouldn't be in this queue anymore."); + }; + a.prototype.dispatchEvent = function(a) { + m(a.isBroadcastEvent(), "Cannot dispatch non-broadcast events."); + var d = this._queues[a.type]; + if (d) { + t.value && console.log("Broadcast event of type " + a._type + " to " + d.length + " listeners"); + for (var e = 0, c = 0;c < d.length;c++) { + var f = d[c]; + null === f ? e++ : f.dispatchEvent(a); + } + if (16 < e && e > d.length >> 1) { + e = []; + for (c = 0;c < d.length;c++) { + d[c] && e.push(d[c]); + } + this._queues[a.type] = e; + } + } + }; + a.prototype.getQueueLength = function(a) { + return this._queues[a] ? this._queues[a].length : 0; + }; + return a; + }(); + v.BroadcastEventDispatchQueue = k; + var f = function(d) { + function b(a) { + void 0 === a && (a = null); + this._target = a || this; + } + __extends(b, d); + b.prototype.toString = function() { + return a.ASObject.dynamicPrototype.$BgtoString.call(this); + }; + b.prototype._getListenersForType = function(a, b) { + var d = a ? this._captureListeners : this._targetOrBubblingListeners; + return d ? d[b] : null; + }; + b.prototype._getListeners = function(a) { + return a ? this._captureListeners || (this._captureListeners = Object.create(null)) : this._targetOrBubblingListeners || (this._targetOrBubblingListeners = Object.create(null)); + }; + b.prototype.addEventListener = function(a, d, c, f, k) { + void 0 === c && (c = !1); + void 0 === f && (f = 0); + void 0 === k && (k = !1); + (2 > arguments.length || 5 < arguments.length) && e("ArgumentError", h.Errors.WrongArgumentCountError, "flash.events::EventDispatcher/addEventListener()", 2, arguments.length); + u(d) || e("TypeError", h.Errors.CheckTypeFailedError, d, "Function"); + l(a) && e("TypeError", h.Errors.NullPointerError, "type"); + a = p(a); + c = !!c; + f |= 0; + k = !!k; + var m = this._getListeners(c); + (m[a] || (m[a] = new n)).insert(d, c, f); + !c && v.Event.isBroadcastEventType(a) && b.broadcastEventDispatchQueue.add(a, this); + }; + b.prototype.removeEventListener = function(a, d, c) { + void 0 === c && (c = !1); + (2 > arguments.length || 3 < arguments.length) && e("ArgumentError", h.Errors.WrongArgumentCountError, "flash.events::EventDispatcher/removeEventListener()", 2, arguments.length); + u(d) || e("TypeError", h.Errors.CheckTypeFailedError, d, "Function"); + l(a) && e("TypeError", h.Errors.NullPointerError, "type"); + a = p(a); + var f = this._getListeners(!!c), k = f[a]; + k && (k.remove(d), k.isEmpty() && (!c && v.Event.isBroadcastEventType(a) && b.broadcastEventDispatchQueue.remove(a, this), f[a] = null)); + }; + b.prototype._hasTargetOrBubblingEventListener = function(a) { + return!(!this._targetOrBubblingListeners || !this._targetOrBubblingListeners[a]); + }; + b.prototype._hasCaptureEventListener = function(a) { + return!(!this._captureListeners || !this._captureListeners[a]); + }; + b.prototype._hasEventListener = function(a) { + return this._hasTargetOrBubblingEventListener(a) || this._hasCaptureEventListener(a); + }; + b.prototype.hasEventListener = function(a) { + 1 !== arguments.length && e("ArgumentError", h.Errors.WrongArgumentCountError, "flash.events::EventDispatcher/hasEventListener()", 1, arguments.length); + l(a) && e("TypeError", h.Errors.NullPointerError, "type"); + a = p(a); + return this._hasEventListener(a); + }; + b.prototype.willTrigger = function(a) { + 1 !== arguments.length && e("ArgumentError", h.Errors.WrongArgumentCountError, "flash.events::EventDispatcher/hasEventListener()", 1, arguments.length); + l(a) && e("TypeError", h.Errors.NullPointerError, "type"); + a = p(a); + if (this._hasEventListener(a)) { + return!0; + } + if (s.display.DisplayObject.isType(this)) { + var b = this._parent; + do { + if (b._hasEventListener(a)) { + return!0; + } + } while (b = b._parent); + } + return!1; + }; + b.prototype._skipDispatchEvent = function(a) { + if (this._hasEventListener(a.type)) { + return!1; + } + if (!a.isBroadcastEvent() && a._bubbles && s.display.DisplayObject.isType(this)) { + for (var b = this._parent;b;b = b._parent) { + if (b._hasEventListener(a.type)) { + return!1; + } + } + } + return!0; + }; + b.prototype.dispatchEvent = function(a) { + 1 !== arguments.length && e("ArgumentError", h.Errors.WrongArgumentCountError, "flash.events::EventDispatcher/dispatchEvent()", 1, arguments.length); + if (this._skipDispatchEvent(a)) { + return!0; + } + t.value && console.log("Dispatch event of type " + a._type); + h.counter.count("EventDispatcher::dispatchEvent"); + var d = a._type, c = this._target; + h.counter.count("EventDispatcher::dispatchEvent(" + d + ")"); + var f = !0, k = []; + if (!a.isBroadcastEvent() && s.display.DisplayObject.isType(this)) { + for (var l = this._parent;l;) { + l._hasEventListener(d) && k.push(l), l = l._parent; + } + for (l = k.length - 1;0 <= l && f;l--) { + var n = k[l]; + if (n._hasCaptureEventListener(d)) { + var q = n._getListenersForType(!0, d); + m(q); + f = b.callListeners(q, a, c, n, v.EventPhase.CAPTURING_PHASE); + } + } + } + f && (q = this._getListenersForType(!1, d)) && (f = b.callListeners(q, a, c, c, v.EventPhase.AT_TARGET)); + if (!a.isBroadcastEvent() && f && a.bubbles) { + for (l = 0;l < k.length && f;l++) { + n = k[l], n._hasTargetOrBubblingEventListener(d) && (q = n._getListenersForType(!1, d), f = b.callListeners(q, a, c, n, v.EventPhase.BUBBLING_PHASE)); + } + } + return!a._isDefaultPrevented; + }; + b.callListeners = function(a, b, d, e, c) { + if (a.isEmpty()) { + return!0; + } + b._target && (b = b.asCallPublicProperty("clone", null)); + for (var f = a.snapshot(), k = 0;k < f.length;k++) { + var m = f[k]; + b._target = d; + b._currentTarget = e; + b._eventPhase = c; + m.listener(b); + if (b._stopImmediatePropagation) { + break; + } + } + a.releaseSnapshot(f); + return!b._stopPropagation; + }; + b.classInitializer = function() { + b.broadcastEventDispatchQueue = new k; + }; + b.initializer = function() { + this._target = this; + this._targetOrBubblingListeners = this._captureListeners = null; + }; + b.classSymbols = null; + b.instanceSymbols = null; + return b; + }(a.ASNative); + v.EventDispatcher = f; + })(s.events || (s.events = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + a.call(this); + s("public flash.events.EventPhase"); + } + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.CAPTURING_PHASE = 1; + e.AT_TARGET = 2; + e.BUBBLING_PHASE = 3; + return e; + }(a.ASNative); + h.EventPhase = u; + })(h.events || (h.events = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = function(a) { + function e(e, c, h, n) { + a.call(this, e, c, h); + this._text = n; + } + __extends(e, a); + Object.defineProperty(e.prototype, "text", {get:function() { + return this._text; + }, set:function(a) { + this._text = a; + }, enumerable:!0, configurable:!0}); + e.prototype.clone = function() { + var a = new e(this.type, this.bubbles, this.cancelable, this.text); + this.copyNativeData(a); + return a; + }; + e.prototype.toString = function() { + return this.formatToString("TextEvent", "type", "bubbles", "cancelable", "text"); + }; + e.prototype.copyNativeData = function(a) { + p("public flash.events.TextEvent::copyNativeData"); + }; + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.LINK = "link"; + e.TEXT_INPUT = "textInput"; + return e; + }(a.events.Event); + h.TextEvent = u; + })(a.events || (a.events = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + (function(a) { + (function(a) { + (function(c) { + var h = function(a) { + function c(e, m, l, h, n) { + void 0 === m && (m = !1); + void 0 === l && (l = !1); + void 0 === h && (h = ""); + void 0 === n && (n = 0); + a.call(this, e, m, l, h); + this.setID(n); + } + __extends(c, a); + c.prototype.setID = function(a) { + this._id = a; + }; + Object.defineProperty(c.prototype, "errorID", {get:function() { + return this._id; + }, enumerable:!0, configurable:!0}); + c.prototype.clone = function() { + return new c(this.type, this.bubbles, this.cancelable, this.text, this.errorID); + }; + c.prototype.toString = function() { + return this.formatToString("ErrorEvent", "type", "bubbles", "cancelable", "text", "errorID"); + }; + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.ERROR = "error"; + return c; + }(a.events.TextEvent); + c.ErrorEvent = h; + })(a.events || (a.events = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.dummyConstructor, u = function(a) { + function e(e, c, h, n) { + a.call(this, void 0, void 0, void 0); + p("public flash.events.GameInputEvent"); + } + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.DEVICE_ADDED = "deviceAdded"; + e.DEVICE_REMOVED = "deviceRemoved"; + return e; + }(a.events.Event); + h.GameInputEvent = u; + })(a.events || (a.events = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.AVM2.Runtime.asCoerceString, u = c.Debug.dummyConstructor, l = c.Debug.somewhatImplemented, e = function(e) { + function c(a, l, k, f, d, b, g, r, h) { + e.call(this, void 0, void 0, void 0); + u("public flash.events.GestureEvent"); + } + __extends(c, e); + Object.defineProperty(c.prototype, "localX", {get:function() { + return this._localX; + }, set:function(a) { + this._localX = +a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "localY", {get:function() { + return this._localY; + }, set:function(a) { + this._localY = +a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "stageX", {get:function() { + l("public flash.events.GestureEvent::stageX"); + return 0; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "stageY", {get:function() { + l("public flash.events.GestureEvent::stageY"); + return 0; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "ctrlKey", {get:function() { + return this._ctrlKey; + }, set:function(a) { + this._ctrlKey = !!a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "altKey", {get:function() { + return this._altKey; + }, set:function(a) { + this._altKey = !!a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "shiftKey", {get:function() { + return this._shiftKey; + }, set:function(a) { + this._shiftKey = !!a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "phase", {get:function() { + return this._phase; + }, set:function(a) { + this._phase = p(a); + }, enumerable:!0, configurable:!0}); + c.prototype.updateAfterEvent = function() { + l("public flash.events.GestureEvent::updateAfterEvent"); + }; + c.prototype.NativeCtor = function(a, e, c, f, d, b) { + this._phase = p(a); + this._localX = +e; + this._localY = +c; + this._ctrlKey = !!f; + this._altKey = !!d; + this._shiftKey = !!b; + }; + c.prototype.clone = function() { + return new a.events.GestureEvent(this.type, this.bubbles, this.cancelable, this.phase, this.localX, this.localY, this.ctrlKey, this.altKey, this.shiftKey); + }; + c.prototype.toString = function() { + return this.formatToString("GestureEvent", "type", "bubbles", "cancelable", "eventPhase", "localX", "localY", "ctrlKey", "altKey", "shiftKey"); + }; + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.GESTURE_TWO_FINGER_TAP = "gestureTwoFingerTap"; + return c; + }(a.events.Event); + h.GestureEvent = e; + })(a.events || (a.events = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.dummyConstructor, u = function(a) { + function e(e, c, h, n) { + a.call(this, void 0, void 0, void 0); + p("public flash.events.HTTPStatusEvent"); + } + __extends(e, a); + e.prototype._setStatus = function(a) { + this._status = a; + }; + Object.defineProperty(e.prototype, "status", {get:function() { + return this._status; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "responseURL", {get:function() { + return this._responseURL; + }, set:function(a) { + this._responseURL = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "responseHeaders", {get:function() { + return this._responseHeaders; + }, set:function(a) { + this._responseHeaders = a; + }, enumerable:!0, configurable:!0}); + e.prototype.clone = function() { + var a = new h.HTTPStatusEvent(this.type, this.bubbles, this.cancelable, this.status); + a.responseURL = this.responseURL; + a.responseHeaders = this.responseHeaders; + return a; + }; + e.prototype.toString = function() { + return this.formatToString("HTTPStatusEvent", "type", "bubbles", "cancelable", "eventPhase", "status", "responseURL", "responseHeaders"); + }; + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.HTTP_STATUS = "httpStatus"; + e.HTTP_RESPONSE_STATUS = "httpResponseStatus"; + return e; + }(a.events.Event); + h.HTTPStatusEvent = u; + })(a.events || (a.events = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + (function(a) { + (function(a) { + (function(c) { + var h = function(a) { + function c(e, m, l, h, n) { + void 0 === m && (m = !1); + void 0 === l && (l = !1); + void 0 === h && (h = ""); + void 0 === n && (n = 0); + a.call(this, e, m, l, h, n); + } + __extends(c, a); + c.prototype.clone = function() { + var a = new c(this.type, this.bubbles, this.cancelable, this.text, this.errorID); + this.copyNativeData(a); + return a; + }; + c.prototype.toString = function() { + return this.formatToString("IOErrorEvent", "type", "bubbles", "cancelable", "text", "errorID"); + }; + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.IO_ERROR = "ioError"; + c.NETWORK_ERROR = "networkError"; + c.DISK_ERROR = "diskError"; + c.VERIFY_ERROR = "verifyError"; + return c; + }(a.events.ErrorEvent); + c.IOErrorEvent = h; + })(a.events || (a.events = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.dummyConstructor, u = function(a) { + function e(e, c, h, n, k, f, d, b, g) { + a.call(this, void 0, void 0, void 0); + p("public flash.events.KeyboardEvent"); + } + __extends(e, a); + Object.defineProperty(e.prototype, "charCode", {get:function() { + return this._charCode; + }, set:function(a) { + this._charCode = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "keyCode", {get:function() { + return this._keyCode; + }, set:function(a) { + this._keyCode = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "keyLocation", {get:function() { + return this._keyLocation; + }, set:function(a) { + this._keyLocation = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "ctrlKey", {get:function() { + return this._ctrlKey; + }, set:function(a) { + this._ctrlKey = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "altKey", {get:function() { + return this._altKey; + }, set:function(a) { + this._altKey = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "shiftKey", {get:function() { + return this._shiftKey; + }, set:function(a) { + this._shiftKey = a; + }, enumerable:!0, configurable:!0}); + e.prototype.clone = function() { + return new h.KeyboardEvent(this.type, this.bubbles, this.cancelable, this.charCode, this.keyCode, this.keyLocation, this.ctrlKey, this.altKey, this.shiftKey); + }; + e.prototype.toString = function() { + return this.formatToString("KeyboardEvent", "type", "bubbles", "cancelable", "eventPhase", "charCode", "keyCode", "keyLocation", "ctrlKey", "altKey", "shiftKey"); + }; + e.prototype.updateAfterEvent = function() { + c.AVM2.Runtime.AVM2.instance.globals["Shumway.Player.Utils"].requestRendering(); + }; + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.KEY_DOWN = "keyDown"; + e.KEY_UP = "keyUp"; + return e; + }(a.events.Event); + h.KeyboardEvent = u; + })(a.events || (a.events = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = c.Debug.dummyConstructor, l = function(e) { + function m(a, c, m, k, f, d, b, g, l, h, s) { + e.call(this, void 0, void 0, void 0); + u("public flash.events.MouseEvent"); + } + __extends(m, e); + m.typeFromDOMType = function(a) { + switch(a) { + case "click": + return m.CLICK; + case "dblclick": + return m.DOUBLE_CLICK; + case "mousedown": + return m.MOUSE_DOWN; + case "mouseout": + ; + case "mouseover": + ; + case "mousemove": + return m.MOUSE_MOVE; + case "mouseup": + return m.MOUSE_UP; + default: + p(a); + } + }; + Object.defineProperty(m.prototype, "localX", {get:function() { + return this._localX / 20 | 0; + }, set:function(a) { + this._localX = 20 * a | 0; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "localY", {get:function() { + return this._localY / 20 | 0; + }, set:function(a) { + this._localY = 20 * a | 0; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "stageX", {get:function() { + return isNaN(this.localX + this.localY) ? Number.NaN : this._getGlobalPoint().x / 20 | 0; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "stageY", {get:function() { + return isNaN(this.localX + this.localY) ? Number.NaN : this._getGlobalPoint().y / 20 | 0; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "movementX", {get:function() { + return this._movementX || 0; + }, set:function(a) { + this._movementX = +a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "movementY", {get:function() { + return this._movementY || 0; + }, set:function(a) { + this._movementY = +a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "delta", {get:function() { + return this._delta; + }, set:function(a) { + this._delta = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "ctrlKey", {get:function() { + return this._ctrlKey; + }, set:function(a) { + this._ctrlKey = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "altKey", {get:function() { + return this._altKey; + }, set:function(a) { + this._altKey = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "shiftKey", {get:function() { + return this._shiftKey; + }, set:function(a) { + this._shiftKey = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "buttonDown", {get:function() { + return this._buttonDown; + }, set:function(a) { + this._buttonDown = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "relatedObject", {get:function() { + return this._relatedObject; + }, set:function(a) { + this._relatedObject = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "isRelatedObjectInaccessible", {get:function() { + return this._isRelatedObjectInaccessible; + }, set:function(a) { + this._isRelatedObjectInaccessible = a; + }, enumerable:!0, configurable:!0}); + m.prototype.updateAfterEvent = function() { + c.AVM2.Runtime.AVM2.instance.globals["Shumway.Player.Utils"].requestRendering(); + }; + m.prototype._getGlobalPoint = function() { + var e = this._position; + e || (e = this._position = new a.geom.Point); + this.target ? (e.setTo(this._localX, this._localY), this._target._getConcatenatedMatrix().transformPointInPlace(e)) : e.setTo(0, 0); + return e; + }; + m.prototype.clone = function() { + return new a.events.MouseEvent(this.type, this.bubbles, this.cancelable, this.localX, this.localY, this.relatedObject, this.ctrlKey, this.altKey, this.shiftKey, this.buttonDown, this.delta); + }; + m.prototype.toString = function() { + return this.formatToString("MouseEvent", "type", "bubbles", "cancelable", "eventPhase", "localX", "localY", "relatedObject", "ctrlKey", "altKey", "shiftKey", "buttonDown", "delta"); + }; + m.classInitializer = null; + m.initializer = null; + m.classSymbols = null; + m.instanceSymbols = null; + m.CLICK = "click"; + m.DOUBLE_CLICK = "doubleClick"; + m.MOUSE_DOWN = "mouseDown"; + m.MOUSE_MOVE = "mouseMove"; + m.MOUSE_OUT = "mouseOut"; + m.MOUSE_OVER = "mouseOver"; + m.MOUSE_UP = "mouseUp"; + m.RELEASE_OUTSIDE = "releaseOutside"; + m.MOUSE_WHEEL = "mouseWheel"; + m.ROLL_OUT = "rollOut"; + m.ROLL_OVER = "rollOver"; + m.MIDDLE_CLICK = "middleClick"; + m.MIDDLE_MOUSE_DOWN = "middleMouseDown"; + m.MIDDLE_MOUSE_UP = "middleMouseUp"; + m.RIGHT_CLICK = "rightClick"; + m.RIGHT_MOUSE_DOWN = "rightMouseDown"; + m.RIGHT_MOUSE_UP = "rightMouseUp"; + m.CONTEXT_MENU = "contextMenu"; + return m; + }(a.events.Event); + h.MouseEvent = l; + })(a.events || (a.events = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + (function(a) { + (function(a) { + (function(c) { + var h = function(c) { + function l(a, c, l, h) { + } + __extends(l, c); + Object.defineProperty(l.prototype, "info", {get:function() { + return this._info; + }, set:function(a) { + this._info = a; + }, enumerable:!0, configurable:!0}); + l.prototype.clone = function() { + return new a.events.NetStatusEvent(this.type, this.bubbles, this.cancelable, this.info); + }; + l.prototype.toString = function() { + return this.formatToString("NetStatusEvent", "type", "bubbles", "cancelable", "eventPhase", "info"); + }; + l.classInitializer = null; + l.initializer = null; + l.classSymbols = null; + l.instanceSymbols = null; + l.NET_STATUS = "netStatus"; + return l; + }(a.events.Event); + c.NetStatusEvent = h; + })(a.events || (a.events = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.dummyConstructor, u = function(c) { + function e(a, e, h, n, k) { + c.call(this, void 0, void 0, void 0); + p("public flash.events.ProgressEvent"); + } + __extends(e, c); + Object.defineProperty(e.prototype, "bytesLoaded", {get:function() { + return this._bytesLoaded; + }, set:function(a) { + this._bytesLoaded = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "bytesTotal", {get:function() { + return this._bytesTotal; + }, set:function(a) { + this._bytesTotal = a; + }, enumerable:!0, configurable:!0}); + e.prototype.clone = function() { + return new a.events.ProgressEvent(this._type, this._bubbles, this._cancelable, this._bytesLoaded, this._bytesTotal); + }; + e.prototype.toString = function() { + return this.formatToString("ProgressEvent", "bubbles", "cancelable", "eventPhase", "bytesLoaded", "bytesTotal"); + }; + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.PROGRESS = "progress"; + e.SOCKET_DATA = "socketData"; + return e; + }(a.events.Event); + h.ProgressEvent = u; + })(a.events || (a.events = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.dummyConstructor, u = function(a) { + function e(e, c, h, n, k) { + a.call(this, void 0, void 0, void 0, void 0, void 0); + p("public flash.events.SecurityErrorEvent"); + } + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.SECURITY_ERROR = "securityError"; + return e; + }(a.events.ErrorEvent); + h.SecurityErrorEvent = u; + })(a.events || (a.events = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.dummyConstructor, u = function(a) { + function e(e, c, h) { + a.call(this, void 0, void 0, void 0); + p("public flash.events.TimerEvent"); + } + __extends(e, a); + e.prototype.clone = function() { + return new h.TimerEvent(this.type, this.bubbles, this.cancelable); + }; + e.prototype.toString = function() { + return this.formatToString("TimerEvent", "type", "bubbles", "cancelable", "eventPhase"); + }; + e.prototype.updateAfterEvent = function() { + c.AVM2.Runtime.AVM2.instance.globals["Shumway.Player.Utils"].requestRendering(); + }; + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.TIMER = "timer"; + e.TIMER_COMPLETE = "timerComplete"; + return e; + }(a.events.Event); + h.TimerEvent = u; + })(a.events || (a.events = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.somewhatImplemented, u = c.Debug.dummyConstructor, l = function(a) { + function m(c, m, l, k, f, d, b, g, r, h, s, p, v, M) { + a.call(this, void 0, void 0, void 0); + u("public flash.events.TouchEvent"); + } + __extends(m, a); + Object.defineProperty(m.prototype, "touchPointID", {get:function() { + return this._touchPointID; + }, set:function(a) { + this._touchPointID = +a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "isPrimaryTouchPoint", {get:function() { + return this._isPrimaryTouchPoint; + }, set:function(a) { + this._isPrimaryTouchPoint = !!a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "localX", {get:function() { + return this._localX; + }, set:function(a) { + this._localX = +a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "localY", {get:function() { + return this._localY; + }, set:function(a) { + this._localY = +a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "sizeX", {get:function() { + return this._sizeX; + }, set:function(a) { + this._sizeX = +a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "sizeY", {get:function() { + return this._sizeY; + }, set:function(a) { + this._sizeY = +a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "pressure", {get:function() { + return this._pressure; + }, set:function(a) { + this._pressure = +a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "relatedObject", {get:function() { + return this._relatedObject; + }, set:function(a) { + this._relatedObject = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "ctrlKey", {get:function() { + return this._ctrlKey; + }, set:function(a) { + this._ctrlKey = !!a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "altKey", {get:function() { + return this._altKey; + }, set:function(a) { + this._altKey = !!a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "shiftKey", {get:function() { + return this._shiftKey; + }, set:function(a) { + this._shiftKey = !!a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "stageX", {get:function() { + p("TouchEvent::get stageX"); + return this._localX; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "stageY", {get:function() { + p("TouchEvent::get stageY"); + return this._localY; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m.prototype, "isRelatedObjectInaccessible", {get:function() { + return this._isRelatedObjectInaccessible; + }, set:function(a) { + this._isRelatedObjectInaccessible = a; + }, enumerable:!0, configurable:!0}); + m.prototype.clone = function() { + return new h.TouchEvent(this.type, this.bubbles, this.cancelable, this.touchPointID, this.isPrimaryTouchPoint, this.localX, this.localY, this.sizeX, this.sizeY, this.pressure, this.relatedObject, this.ctrlKey, this.altKey, this.shiftKey); + }; + m.prototype.toString = function() { + return this.formatToString("TouchEvent", "type", "bubbles", "cancelable", "eventPhase", "touchPointID", "isPrimaryTouchPoint", "localX", "localY", "sizeX", "sizeY", "pressure", "relatedObject", "ctrlKey", "altKey", "shiftKey"); + }; + m.prototype.updateAfterEvent = function() { + c.AVM2.Runtime.AVM2.instance.globals["Shumway.Player.Utils"].requestRendering(); + }; + m.classInitializer = null; + m.initializer = null; + m.classSymbols = null; + m.instanceSymbols = null; + m.TOUCH_BEGIN = "touchBegin"; + m.TOUCH_END = "touchEnd"; + m.TOUCH_MOVE = "touchMove"; + m.TOUCH_OVER = "touchOver"; + m.TOUCH_OUT = "touchOut"; + m.TOUCH_ROLL_OVER = "touchRollOver"; + m.TOUCH_ROLL_OUT = "touchRollOut"; + m.TOUCH_TAP = "touchTap"; + m.PROXIMITY_BEGIN = "proximityBegin"; + m.PROXIMITY_END = "proximityEnd"; + m.PROXIMITY_MOVE = "proximityMove"; + m.PROXIMITY_OUT = "proximityOut"; + m.PROXIMITY_OVER = "proximityOver"; + m.PROXIMITY_ROLL_OUT = "proximityRollOut"; + m.PROXIMITY_ROLL_OVER = "proximityRollOver"; + return m; + }(a.events.Event); + h.TouchEvent = l; + })(a.events || (a.events = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.dummyConstructor, u = function(a) { + function e(e, c, h, n) { + a.call(this, void 0, void 0, void 0, void 0, void 0); + p("public flash.events.UncaughtErrorEvent"); + } + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.UNCAUGHT_ERROR = "uncaughtError"; + return e; + }(a.events.ErrorEvent); + h.UncaughtErrorEvent = u; + })(a.events || (a.events = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + (function(a) { + (function(a) { + (function(c) { + var h = function(a) { + function l() { + c.EventDispatcher.instanceConstructorNoInitialize.call(this); + } + __extends(l, a); + l.classInitializer = null; + l.initializer = null; + l.classSymbols = null; + l.instanceSymbols = null; + return l; + }(a.events.EventDispatcher); + c.UncaughtErrorEvents = h; + })(a.events || (a.events = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(v) { + var p = c.Debug.notImplemented, u = c.Debug.somewhatImplemented, l = c.isNullOrUndefined, e = c.AVM2.Runtime.asCoerceString, m = c.AVM2.Runtime.throwError, t = c.AVM2.Runtime.checkNullParameter, q = c.Debug.assert, n = c.Bounds, k = a.geom, f = a.events; + (function(a) { + a[a.None = 0] = "None"; + a[a.Visible = 1] = "Visible"; + a[a.InvalidLineBounds = 2] = "InvalidLineBounds"; + a[a.InvalidFillBounds = 4] = "InvalidFillBounds"; + a[a.InvalidMatrix = 8] = "InvalidMatrix"; + a[a.InvalidInvertedMatrix = 16] = "InvalidInvertedMatrix"; + a[a.InvalidConcatenatedMatrix = 32] = "InvalidConcatenatedMatrix"; + a[a.InvalidInvertedConcatenatedMatrix = 64] = "InvalidInvertedConcatenatedMatrix"; + a[a.InvalidConcatenatedColorTransform = 128] = "InvalidConcatenatedColorTransform"; + a[a.Constructed = 256] = "Constructed"; + a[a.Destroyed = 512] = "Destroyed"; + a[a.NeedsLoadEvent = 1024] = "NeedsLoadEvent"; + a[a.OwnedByTimeline = 2048] = "OwnedByTimeline"; + a[a.AnimatedByTimeline = 4096] = "AnimatedByTimeline"; + a[a.HasFrameScriptPending = 8192] = "HasFrameScriptPending"; + a[a.ContainsFrameScriptPendingChildren = 16384] = "ContainsFrameScriptPendingChildren"; + a[a.ContainsMorph = 32768] = "ContainsMorph"; + a[a.CacheAsBitmap = 65536] = "CacheAsBitmap"; + a[a.DirtyMatrix = 1048576] = "DirtyMatrix"; + a[a.DirtyChildren = 2097152] = "DirtyChildren"; + a[a.DirtyGraphics = 4194304] = "DirtyGraphics"; + a[a.DirtyTextContent = 8388608] = "DirtyTextContent"; + a[a.DirtyBitmapData = 16777216] = "DirtyBitmapData"; + a[a.DirtyNetStream = 33554432] = "DirtyNetStream"; + a[a.DirtyColorTransform = 67108864] = "DirtyColorTransform"; + a[a.DirtyMask = 134217728] = "DirtyMask"; + a[a.DirtyClipDepth = 268435456] = "DirtyClipDepth"; + a[a.DirtyDescendents = 536870912] = "DirtyDescendents"; + a[a.DirtyMiscellaneousProperties = 1073741824] = "DirtyMiscellaneousProperties"; + a[a.Dirty = a.DirtyMatrix | a.DirtyChildren | a.DirtyGraphics | a.DirtyTextContent | a.DirtyBitmapData | a.DirtyNetStream | a.DirtyColorTransform | a.DirtyMask | a.DirtyClipDepth | a.DirtyMiscellaneousProperties] = "Dirty"; + a[a.Bubbling = a.ContainsFrameScriptPendingChildren | a.ContainsMorph] = "Bubbling"; + })(v.DisplayObjectFlags || (v.DisplayObjectFlags = {})); + var d = v.DisplayObjectFlags; + (function(a) { + a[a.None = 0] = "None"; + a[a.Continue = 0] = "Continue"; + a[a.Stop = 1] = "Stop"; + a[a.Skip = 2] = "Skip"; + a[a.FrontToBack = 8] = "FrontToBack"; + a[a.Filter = 16] = "Filter"; + })(v.VisitorFlags || (v.VisitorFlags = {})); + (function(a) { + a[a.HitTestBounds = 0] = "HitTestBounds"; + a[a.HitTestBoundsAndMask = 1] = "HitTestBoundsAndMask"; + a[a.HitTestShape = 2] = "HitTestShape"; + a[a.Mouse = 3] = "Mouse"; + a[a.ObjectsUnderPoint = 4] = "ObjectsUnderPoint"; + a[a.Drop = 5] = "Drop"; + })(v.HitTestingType || (v.HitTestingType = {})); + (function(a) { + a[a.None = 0] = "None"; + a[a.Bounds = 1] = "Bounds"; + a[a.Shape = 2] = "Shape"; + })(v.HitTestingResult || (v.HitTestingResult = {})); + var b = function(b) { + function r() { + f.EventDispatcher.instanceConstructorNoInitialize.call(this); + this._addReference(); + this._setFlags(256); + } + __extends(r, b); + r.getNextSyncID = function() { + return this._syncID++; + }; + r.reset = function() { + this._advancableInstances = new c.WeakList; + }; + r.prototype.createAnimatedDisplayObject = function(b, d, e) { + var g = b.symbolClass; + b = g.isSubtypeOf(a.display.BitmapData) ? a.display.Bitmap.initializeFrom(b) : g.initializeFrom(b); + d.flags & 32 && (b._name = d.name); + b._setFlags(4096); + b._animate(d); + e && g.instanceConstructorNoInitialize.call(b); + return b; + }; + r.performFrameNavigation = function(b, d) { + b ? (r._runScripts = d, h.enterTimeline("DisplayObject.performFrameNavigation", {instances:0})) : d = r._runScripts; + q(16384 > v.DisplayObject._advancableInstances.length, "Too many advancable instances."); + v.DisplayObject._advancableInstances.forEach(function(a) { + a._initFrame(b); + }); + b && d && r._broadcastFrameEvent(f.Event.ENTER_FRAME); + v.DisplayObject._advancableInstances.forEach(function(a) { + a._constructFrame(); + }); + d ? (r._broadcastFrameEvent(f.Event.FRAME_CONSTRUCTED), v.DisplayObject._advancableInstances.forEach(function(a) { + v.MovieClip.isInstanceOf(a) && !a.parent && a._enqueueFrameScripts(); + }), a.display.DisplayObject._stage._enqueueFrameScripts(), v.MovieClip.runFrameScripts(), r._broadcastFrameEvent(f.Event.EXIT_FRAME)) : v.MovieClip.reset(); + b && (h.leaveTimeline(), r._runScripts = !0); + }; + r._broadcastFrameEvent = function(a) { + var b; + switch(a) { + case f.Event.ENTER_FRAME: + ; + case f.Event.FRAME_CONSTRUCTED: + ; + case f.Event.EXIT_FRAME: + ; + case f.Event.RENDER: + b = f.Event.getBroadcastInstance(a); + } + q(b, "Invalid frame event."); + f.EventDispatcher.broadcastEventDispatchQueue.dispatchEvent(b); + }; + r.prototype._setInitialName = function() { + this._name = "instance" + a.display.DisplayObject._instanceID++; + }; + r.prototype._setParent = function(a, b) { + var e = this._parent; + q(a !== this); + this._parent = a; + this._setDepth(b); + if (a) { + this._addReference(); + var g = 0; + this._hasFlags(8192) && (g |= 16384); + this._hasAnyFlags(d.Bubbling) && (g |= this._displayObjectFlags & d.Bubbling); + g && a._propagateFlagsUp(g); + } + e && this._removeReference(); + }; + r.prototype._setDepth = function(a) { + -1 < a ? this._setFlags(2048) : this._removeFlags(2048); + this._depth = a; + }; + r.prototype._setFillAndLineBoundsFromWidthAndHeight = function(a, b) { + this._fillBounds.width = a; + this._fillBounds.height = b; + this._lineBounds.width = a; + this._lineBounds.height = b; + this._removeFlags(6); + this._invalidateParentFillAndLineBounds(!0, !0); + }; + r.prototype._setFillAndLineBoundsFromSymbol = function(a) { + q(a.fillBounds || a.lineBounds, "Fill or Line bounds are not defined in the symbol."); + a.fillBounds && (this._fillBounds.copyFrom(a.fillBounds), this._removeFlags(4)); + a.lineBounds && (this._lineBounds.copyFrom(a.lineBounds), this._removeFlags(2)); + this._invalidateParentFillAndLineBounds(!!a.fillBounds, !!a.lineBounds); + }; + r.prototype._setFlags = function(a) { + this._displayObjectFlags |= a; + }; + r.prototype._setDirtyFlags = function(a) { + this._displayObjectFlags |= a; + this._parent && this._parent._propagateFlagsUp(536870912); + }; + r.prototype._toggleFlags = function(a, b) { + this._displayObjectFlags = b ? this._displayObjectFlags | a : this._displayObjectFlags & ~a; + }; + r.prototype._removeFlags = function(a) { + this._displayObjectFlags &= ~a; + }; + r.prototype._hasFlags = function(a) { + return(this._displayObjectFlags & a) === a; + }; + r.prototype._hasAnyFlags = function(a) { + return!!(this._displayObjectFlags & a); + }; + r.prototype._propagateFlagsUp = function(a) { + if (!this._hasFlags(a)) { + this._setFlags(a); + var b = this._parent; + b && b._propagateFlagsUp(a); + } + }; + r.prototype._propagateFlagsDown = function(a) { + this._setFlags(a); + }; + r.prototype._findNearestAncestor = function() { + for (var a = this;a;) { + if (!1 === a._hasFlags(128)) { + return a; + } + a = a._parent; + } + return null; + }; + r.prototype._findFurthestAncestorOrSelf = function() { + for (var a = this;a;) { + if (!a._parent) { + return a; + } + a = a._parent; + } + }; + r.prototype._isAncestor = function(a) { + for (;a;) { + if (a === this) { + return!0; + } + a = a._parent; + } + return!1; + }; + r._clampRotation = function(a) { + a %= 360; + 180 < a ? a -= 360 : -180 > a && (a += 360); + return a; + }; + r._getAncestors = function(a, b) { + var d = r._path; + for (d.length = 0;a && a !== b;) { + d.push(a), a = a._parent; + } + q(a === b, "Last ancestor is not an ancestor."); + return d; + }; + r.prototype._getConcatenatedMatrix = function() { + this._hasFlags(32) && (this._parent ? this._parent._getConcatenatedMatrix().preMultiplyInto(this._getMatrix(), this._concatenatedMatrix) : this._concatenatedMatrix.copyFrom(this._getMatrix()), this._removeFlags(32)); + return this._concatenatedMatrix; + }; + r.prototype._getInvertedConcatenatedMatrix = function() { + this._hasFlags(64) && (this._getConcatenatedMatrix().invertInto(this._invertedConcatenatedMatrix), this._removeFlags(64)); + return this._invertedConcatenatedMatrix; + }; + r.prototype._setMatrix = function(a, b) { + if (b || !this._matrix.equals(a)) { + var d = this._matrix; + d.copyFrom(a); + b && d.toTwipsInPlace(); + this._scaleX = d.getScaleX(); + this._scaleY = d.getScaleY(); + this._skewX = a.getSkewX(); + this._skewY = a.getSkewY(); + this._rotation = r._clampRotation(180 * this._skewY / Math.PI); + this._removeFlags(8); + this._setFlags(16); + this._setDirtyFlags(1048576); + this._invalidatePosition(); + } + }; + r.prototype._getMatrix = function() { + this._hasFlags(8) && (this._matrix.updateScaleAndRotation(this._scaleX, this._scaleY, this._skewX, this._skewY), this._removeFlags(8)); + return this._matrix; + }; + r.prototype._getInvertedMatrix = function() { + this._hasFlags(16) && (this._getMatrix().invertInto(this._invertedMatrix), this._removeFlags(16)); + return this._invertedMatrix; + }; + r.prototype._getConcatenatedColorTransform = function() { + if (!this.stage) { + return this._colorTransform.clone(); + } + if (this._hasFlags(128)) { + var b = this._findNearestAncestor(), d = r._getAncestors(this, b), e = d.length - 1; + a.display.Stage.isType(d[e]) && e--; + for (var g = b && !a.display.Stage.isType(b) ? b._concatenatedColorTransform.clone() : new k.ColorTransform;0 <= e;) { + b = d[e--], q(b._hasFlags(128)), g.preMultiply(b._colorTransform), g.convertToFixedPoint(), b._concatenatedColorTransform.copyFrom(g), b._removeFlags(128); + } + } + return this._concatenatedColorTransform; + }; + r.prototype._setColorTransform = function(a) { + this._colorTransform.copyFrom(a); + this._colorTransform.convertToFixedPoint(); + this._propagateFlagsDown(128); + this._setDirtyFlags(67108864); + }; + r.prototype._invalidateFillAndLineBounds = function(a, b) { + this._propagateFlagsUp((b ? 2 : 0) | (a ? 4 : 0)); + }; + r.prototype._invalidateParentFillAndLineBounds = function(a, b) { + this._parent && this._parent._invalidateFillAndLineBounds(a, b); + }; + r.prototype._getContentBounds = function(a) { + void 0 === a && (a = !0); + var b, d; + a ? (b = 2, d = this._lineBounds) : (b = 4, d = this._fillBounds); + if (this._hasFlags(b)) { + var e = this._getGraphics(); + e ? d.copyFrom(e._getContentBounds(a)) : d.setToSentinels(); + this._getChildBounds(d, a); + this._removeFlags(b); + } + return d; + }; + r.prototype._getChildBounds = function(a, b) { + }; + r.prototype._getTransformedBounds = function(a, b) { + var d = this._getContentBounds(b).clone(); + if (a === this || d.isEmpty()) { + return d; + } + var e; + a ? (e = k.Matrix.TEMP_MATRIX, a._getInvertedConcatenatedMatrix().preMultiplyInto(this._getConcatenatedMatrix(), e)) : e = this._getConcatenatedMatrix(); + e.transformBounds(d); + return d; + }; + r.prototype._stopTimelineAnimation = function() { + this._removeFlags(4096); + }; + r.prototype._invalidateMatrix = function() { + this._setDirtyFlags(1048576); + this._setFlags(24); + this._invalidatePosition(); + }; + r.prototype._invalidatePosition = function() { + this._propagateFlagsDown(96); + this._invalidateParentFillAndLineBounds(!0, !0); + }; + r.prototype._animate = function(b) { + q(this._hasFlags(4096)); + var d = !(b.flags & 1) && b.flags & 2; + b.flags & 4 ? (k.Matrix.TEMP_MATRIX.copyFromUntyped(b.matrix), this._setMatrix(k.Matrix.TEMP_MATRIX, !1)) : d && this._setMatrix(k.Matrix.FROZEN_IDENTITY_MATRIX, !1); + b.flags & 8 ? (k.ColorTransform.TEMP_COLOR_TRANSFORM.copyFromUntyped(b.cxform), this._setColorTransform(k.ColorTransform.TEMP_COLOR_TRANSFORM)) : d && this._setColorTransform(k.ColorTransform.FROZEN_IDENTITY_COLOR_TRANSFORM); + if (b.flags & 16 || d) { + var e = b.ratio | 0; + e !== this._ratio && (q(0 <= e && 65535 >= e), this._ratio = e, this._setDirtyFlags(1073741824)); + } + if (b.flags & 64 || d) { + e = void 0 === b.clipDepth ? -1 : b.clipDepth, e !== this._clipDepth && (this._clipDepth = e, this._setDirtyFlags(268435456)); + } + if (b.flags & 256) { + for (var e = [], g = b.filters, c = 0;c < g.length;c++) { + var f = g[c], m; + switch(f.type) { + case 0: + m = a.filters.DropShadowFilter.FromUntyped(f); + break; + case 1: + m = a.filters.BlurFilter.FromUntyped(f); + break; + case 2: + m = a.filters.GlowFilter.FromUntyped(f); + break; + case 3: + m = a.filters.BevelFilter.FromUntyped(f); + break; + case 4: + m = a.filters.GradientGlowFilter.FromUntyped(f); + break; + case 5: + m = a.filters.ConvolutionFilter.FromUntyped(f); + break; + case 6: + m = a.filters.ColorMatrixFilter.FromUntyped(f); + break; + case 7: + m = a.filters.GradientBevelFilter.FromUntyped(f); + break; + default: + q(m, "Unknown filter type."); + } + e.push(m); + } + this._filters = e; + this._setDirtyFlags(1073741824); + } else { + d && (this._filters = null, this._setDirtyFlags(1073741824)); + } + if (b.flags & 512 || d) { + m = a.display.BlendMode.fromNumber(void 0 === b.blendMode ? 1 : b.blendMode), m !== this._blendMode && (this._blendMode = m, this._setDirtyFlags(1073741824)); + } + if (b.flags & 1024 || d) { + m = 0 < b.bmpCache, m !== this._hasFlags(65536) && (this._toggleFlags(65536, m), this._setDirtyFlags(1073741824)); + } + if (b.flags & 8192 || d) { + b = 0 !== b.visibility, b !== this._hasFlags(1) && (this._toggleFlags(1, b), this._setDirtyFlags(1073741824)); + } + }; + r.prototype._propagateEvent = function(a) { + this.visit(function(b) { + b.dispatchEvent(a); + return 0; + }, 0); + }; + Object.defineProperty(r.prototype, "x", {get:function() { + return this._getX(); + }, set:function(a) { + a = 20 * a | 0; + this._stopTimelineAnimation(); + if (this._canHaveTextContent()) { + var b = this._getContentBounds(); + a -= b.xMin; + } + a !== this._matrix.tx && (this._matrix.tx = a, this._invertedMatrix.tx = -a, this._invalidatePosition(), this._setDirtyFlags(1048576)); + }, enumerable:!0, configurable:!0}); + r.prototype._getX = function() { + var a = this._matrix.tx; + if (this._canHaveTextContent()) { + var b = this._getContentBounds(), a = a + b.xMin + } + return a / 20; + }; + Object.defineProperty(r.prototype, "y", {get:function() { + return this._getY(); + }, set:function(a) { + a = 20 * a | 0; + this._stopTimelineAnimation(); + if (this._canHaveTextContent()) { + var b = this._getContentBounds(); + a -= b.yMin; + } + a !== this._matrix.ty && (this._matrix.ty = a, this._invertedMatrix.ty = -a, this._invalidatePosition(), this._setDirtyFlags(1048576)); + }, enumerable:!0, configurable:!0}); + r.prototype._getY = function() { + var a = this._matrix.ty; + if (this._canHaveTextContent()) { + var b = this._getContentBounds(), a = a + b.yMin + } + return a / 20; + }; + Object.defineProperty(r.prototype, "scaleX", {get:function() { + return Math.abs(this._scaleX); + }, set:function(a) { + a = +a; + this._stopTimelineAnimation(); + a !== this._scaleX && (this._scaleX = a, this._invalidateMatrix()); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(r.prototype, "scaleY", {get:function() { + return this._scaleY; + }, set:function(a) { + a = +a; + this._stopTimelineAnimation(); + a !== this._scaleY && (this._scaleY = a, this._invalidateMatrix()); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(r.prototype, "scaleZ", {get:function() { + return this._scaleZ; + }, set:function(a) { + p("public DisplayObject::set scaleZ"); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(r.prototype, "rotation", {get:function() { + return this._rotation; + }, set:function(a) { + a = +a; + this._stopTimelineAnimation(); + a = r._clampRotation(a); + if (a !== this._rotation) { + var b = (a - this._rotation) / 180 * Math.PI; + this._skewX += b; + this._skewY += b; + this._rotation = a; + this._invalidateMatrix(); + } + }, enumerable:!0, configurable:!0}); + Object.defineProperty(r.prototype, "rotationX", {get:function() { + return this._rotationX; + }, set:function(a) { + p("public DisplayObject::set rotationX"); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(r.prototype, "rotationY", {get:function() { + return this._rotationY; + }, set:function(a) { + p("public DisplayObject::set rotationY"); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(r.prototype, "rotationZ", {get:function() { + return this._rotationZ; + }, set:function(a) { + p("public DisplayObject::set rotationZ"); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(r.prototype, "width", {get:function() { + return this._getWidth(); + }, set:function(a) { + this._setWidth(a); + }, enumerable:!0, configurable:!0}); + r.prototype._getWidth = function() { + return this._getTransformedBounds(this._parent, !0).width / 20; + }; + r.prototype._setWidth = function(a) { + a = 20 * a | 0; + this._stopTimelineAnimation(); + if (!(0 > a)) { + var b = this._getContentBounds(!0); + if (this._canHaveTextContent()) { + var d = this._getContentBounds(); + this._setFillAndLineBoundsFromWidthAndHeight(a, b.height); + } else { + var d = this._getTransformedBounds(this._parent, !0), e = this._rotation / 180 * Math.PI, g = b.getBaseWidth(e); + g && (this._scaleY = d.height / b.getBaseHeight(e), this._scaleX = a / g, this._invalidateMatrix()); + } + } + }; Object.defineProperty(r.prototype, "height", {get:function() { - return this._getTransformedBounds(this._parent, !0).height / 20; + return this._getHeight(); }, set:function(a) { + this._setHeight(a); + }, enumerable:!0, configurable:!0}); + r.prototype._getHeight = function() { + return this._getTransformedBounds(this._parent, !0).height / 20; + }; + r.prototype._setHeight = function(a) { a = 20 * a | 0; this._stopTimelineAnimation(); if (!(0 > a)) { var b = this._getContentBounds(!0); if (this._canHaveTextContent()) { - var c = this._getContentBounds(); + var d = this._getContentBounds(); this._setFillAndLineBoundsFromWidthAndHeight(b.width, a); } else { - var c = this._getTransformedBounds(this._parent, !0), d = this._rotation / 180 * Math.PI, e = b.getBaseHeight(d); - e && (b = b.getBaseWidth(d), this._scaleY = a / e, this._scaleX = c.width / b, this._invalidateMatrix()); + var d = this._getTransformedBounds(this._parent, !0), e = this._rotation / 180 * Math.PI, g = b.getBaseHeight(e); + g && (b = b.getBaseWidth(e), this._scaleY = a / g, this._scaleX = d.width / b, this._invalidateMatrix()); } } - }, enumerable:!0, configurable:!0}); + }; Object.defineProperty(r.prototype, "mask", {get:function() { return this._mask; }, set:function(a) { - this._stopTimelineAnimation(); if (this._mask !== a && a !== this) { a && a._maskedObject && (a._maskedObject.mask = null); if (this._mask = a) { @@ -24668,12 +30975,15 @@ } }, enumerable:!0, configurable:!0}); Object.defineProperty(r.prototype, "transform", {get:function() { - return new f.geom.Transform(this); + return this._getTransform(); }, set:function(a) { this._stopTimelineAnimation(); a.matrix3D ? this._matrix3D = a.matrix3D : this._setMatrix(a.matrix, !0); this._setColorTransform(a.colorTransform); }, enumerable:!0, configurable:!0}); + r.prototype._getTransform = function() { + return new a.geom.Transform(this); + }; r.prototype.destroy = function() { this._setFlags(512); }; @@ -24688,31 +30998,24 @@ return null; }, enumerable:!0, configurable:!0}); Object.defineProperty(r.prototype, "stage", {get:function() { - var a = this; + var b = this; do { - if (a._stage === a) { - return p(f.display.Stage.isType(a)), a; + if (b._stage === b) { + return q(a.display.Stage.isType(b)), b; } - a = a._parent; - } while (a); + b = b._parent; + } while (b); return null; }, enumerable:!0, configurable:!0}); Object.defineProperty(r.prototype, "name", {get:function() { return this._name; - }, set:function(b) { - n(b, "name"); - this._name = a(b); + }, set:function(a) { + t(a, "name"); + this._name = e(a); }, enumerable:!0, configurable:!0}); Object.defineProperty(r.prototype, "parent", {get:function() { return this._parent; }, enumerable:!0, configurable:!0}); - Object.defineProperty(r.prototype, "visible", {get:function() { - return this._hasFlags(1); - }, set:function(a) { - this._stopTimelineAnimation(); - a = !!a; - a !== this._hasFlags(1) && (this._toggleFlags(1, a), this._setDirtyFlags(536870912)); - }, enumerable:!0, configurable:!0}); Object.defineProperty(r.prototype, "alpha", {get:function() { return this._colorTransform.alphaMultiplier; }, set:function(a) { @@ -24722,46 +31025,62 @@ }, enumerable:!0, configurable:!0}); Object.defineProperty(r.prototype, "blendMode", {get:function() { return this._blendMode; - }, set:function(b) { + }, set:function(a) { this._stopTimelineAnimation(); - b = a(b); - b !== this._blendMode && (0 > g.BlendMode.toNumber(b) && c("ArgumentError", k.Errors.InvalidEnumError, "blendMode"), this._blendMode = b, this._setDirtyFlags(536870912)); + a = e(a); + a !== this._blendMode && (0 > v.BlendMode.toNumber(a) && m("ArgumentError", h.Errors.InvalidEnumError, "blendMode"), this._blendMode = a, this._setDirtyFlags(1073741824)); }, enumerable:!0, configurable:!0}); Object.defineProperty(r.prototype, "scale9Grid", {get:function() { - return this._scale9Grid ? f.geom.Rectangle.FromBounds(this._scale9Grid) : null; + return this._getScale9Grid(); }, set:function(a) { this._stopTimelineAnimation(); - this._scale9Grid = e.FromRectangle(a); - this._setDirtyFlags(536870912); + this._scale9Grid = n.FromRectangle(a); + this._setDirtyFlags(1073741824); }, enumerable:!0, configurable:!0}); + r.prototype._getScale9Grid = function() { + return this._scale9Grid ? a.geom.Rectangle.FromBounds(this._scale9Grid) : null; + }; Object.defineProperty(r.prototype, "cacheAsBitmap", {get:function() { - return 0 < this._filters.length || this._hasFlags(16384); + return this._getCacheAsBitmap(); }, set:function(a) { - this._hasFlags(16384) || (this._toggleFlags(16384, !!a), this._setDirtyFlags(536870912)); + this._hasFlags(65536) || (this._toggleFlags(65536, !!a), this._setDirtyFlags(1073741824)); }, enumerable:!0, configurable:!0}); + r.prototype._getCacheAsBitmap = function() { + return this._filters && 0 < this._filters.length || this._hasFlags(65536); + }; Object.defineProperty(r.prototype, "filters", {get:function() { + return this._getFilters(); + }, set:function(b) { + this._filters || (this._filters = []); + var d = !1; + l(b) ? (d = 0 < this.filters.length, this._filters.length = 0) : (this._filters = b.map(function(b) { + q(a.filters.BitmapFilter.isType(b)); + return b.clone(); + }), d = !0); + d && this._setDirtyFlags(1073741824); + }, enumerable:!0, configurable:!0}); + r.prototype._getFilters = function() { return this._filters ? this._filters.map(function(a) { return a.clone(); }) : []; + }; + Object.defineProperty(r.prototype, "visible", {get:function() { + return this._hasFlags(1); }, set:function(a) { - var b = !1; - d(a) ? (b = 0 < this.filters.length, this._filters.length = 0) : (this._filters = a.map(function(a) { - p(f.filters.BitmapFilter.isType(a)); - return a.clone(); - }), b = !0); - b && this._setDirtyFlags(536870912); + a = !!a; + a !== this._hasFlags(1) && (this._toggleFlags(1, a), this._setDirtyFlags(1073741824)); }, enumerable:!0, configurable:!0}); Object.defineProperty(r.prototype, "z", {get:function() { return this._z; }, set:function(a) { this._z = +a; - s("public DisplayObject::set z"); + p("public DisplayObject::set z"); }, enumerable:!0, configurable:!0}); r.prototype.getBounds = function(a) { - return q.Rectangle.FromBounds(this._getTransformedBounds(a || this, !0)); + return k.Rectangle.FromBounds(this._getTransformedBounds(a || this, !0)); }; r.prototype.getRect = function(a) { - return q.Rectangle.FromBounds(this._getTransformedBounds(a || this, !1)); + return k.Rectangle.FromBounds(this._getTransformedBounds(a || this, !1)); }; r.prototype.globalToLocal = function(a) { return this._getInvertedConcatenatedMatrix().transformPointInPlace(a.clone().toTwips()).round().toPixels(); @@ -24769,20 +31088,32 @@ r.prototype.localToGlobal = function(a) { return this._getConcatenatedMatrix().transformPointInPlace(a.clone().toTwips()).round().toPixels(); }; - r.prototype.visit = function(a, b, c) { - "undefined" === typeof c && (c = 0); - var d, e, f = b & 8; - for (d = [this];0 < d.length;) { - e = d.pop(); - var l = 0, l = b & 16 && !e._hasAnyFlags(c) ? 2 : a(e); - if (0 === l) { - if (e = e._children) { - for (var l = e.length, n = 0;n < l;n++) { - d.push(e[f ? n : l - 1 - n]); + r.prototype.globalToLocal3D = function(a) { + p("public DisplayObject::globalToLocal3D"); + return null; + }; + r.prototype.localToGlobal3D = function(a) { + p("public DisplayObject::localToGlobal3D"); + return null; + }; + r.prototype.local3DToGlobal = function(a) { + p("public DisplayObject::local3DToGlobal"); + return null; + }; + r.prototype.visit = function(a, b, d) { + void 0 === d && (d = 0); + var e, g, c = b & 8; + for (e = [this];0 < e.length;) { + g = e.pop(); + var f = 0, f = b & 16 && !g._hasAnyFlags(d) ? 2 : a(g); + if (0 === f) { + if (g = g._children) { + for (var f = g.length, k = 0;k < f;k++) { + e.push(g[c ? k : f - 1 - k]); } } } else { - if (1 === l) { + if (1 === f) { break; } } @@ -24790,7 +31121,7 @@ }; Object.defineProperty(r.prototype, "loaderInfo", {get:function() { var a = this.root; - return a ? (p(a._loaderInfo, "No LoaderInfo object found on root."), a._loaderInfo) : null; + return a ? (q(a._loaderInfo, "No LoaderInfo object found on root."), a._loaderInfo) : null; }, enumerable:!0, configurable:!0}); r.prototype._canHaveGraphics = function() { return!1; @@ -24805,65 +31136,68 @@ return null; }; r.prototype._ensureGraphics = function() { - p(this._canHaveGraphics()); + q(this._canHaveGraphics()); if (this._graphics) { return this._graphics; } - this._graphics = new f.display.Graphics; + this._graphics = new a.display.Graphics; this._graphics._setParent(this); this._invalidateFillAndLineBounds(!0, !0); this._setDirtyFlags(4194304); return this._graphics; }; - r.prototype._setStaticContentFromSymbol = function(a) { - p(!a.dynamic); - this._canHaveGraphics() ? (p(a instanceof b.Timeline.ShapeSymbol), this._graphics = a.graphics, this._setDirtyFlags(4194304)) : f.text.StaticText.isType(this) && (p(a instanceof b.Timeline.TextSymbol), this._textContent = a.textContent, this._setDirtyFlags(8388608)); - this._setFillAndLineBoundsFromSymbol(a); + r.prototype._setStaticContentFromSymbol = function(b) { + q(!b.dynamic); + this._canHaveGraphics() ? (q(b instanceof a.display.ShapeSymbol), this._graphics = b.graphics, this._setDirtyFlags(4194304)) : a.text.StaticText.isType(this) && (q(b instanceof a.text.TextSymbol), this._textContent = b.textContent, this._setDirtyFlags(8388608)); + this._symbol = b; + this._setFillAndLineBoundsFromSymbol(b); }; r.prototype.hitTestObject = function(a) { - p(a && r.isType(a)); - var b = this._getContentBounds(!1).clone(), c = a._getContentBounds(!1).clone(); + q(a && r.isType(a)); + var b = this._getContentBounds(!1).clone(), d = a._getContentBounds(!1).clone(); this._getConcatenatedMatrix().transformBounds(b); - a._getConcatenatedMatrix().transformBounds(c); - return b.intersects(c); + a._getConcatenatedMatrix().transformBounds(d); + return b.intersects(d); }; - r.prototype.hitTestPoint = function(a, b, c) { - return!!this._containsGlobalPoint(20 * +a | 0, 20 * +b | 0, c ? 2 : 0, null); + r.prototype.hitTestPoint = function(a, b, d) { + return!!this._containsGlobalPoint(20 * +a | 0, 20 * +b | 0, d ? 2 : 0, null); }; - r.prototype._containsPoint = function(a, b, c, d, e, f) { - a = this._boundsAndMaskContainPoint(a, b, c, d, e); - if (0 === a || 2 > e) { - return a; + r.prototype._containsPoint = function(a, b, d, e, g, c) { + var f = this._boundsAndMaskContainPoint(a, b, d, e, g); + if (0 === f || 2 > g) { + return f; } - (c = this._containsPointDirectly(c, d)) && f && (4 === e || g.InteractiveObject.isType(this) && this._mouseEnabled) && f.push(this); - return c ? 2 : a; + (a = this._containsPointDirectly(d, e, a, b)) && c && (5 === g ? c[0] = this : (4 === g || v.InteractiveObject.isType(this) && this._mouseEnabled) && c.push(this)); + return a ? 2 : f; + }; + r.prototype._containsGlobalPoint = function(a, b, d, e) { + var g = this._getInvertedConcatenatedMatrix(); + return this._containsPoint(a, b, g.transformX(a, b), g.transformY(a, b), d, e); }; - r.prototype._containsGlobalPoint = function(a, b, c, d) { - var e = this._getInvertedConcatenatedMatrix(); - return this._containsPoint(a, b, e.transformX(a, b), e.transformY(a, b), c, d); - }; - r.prototype._boundsAndMaskContainPoint = function(a, b, c, d, e) { - return this._hasFlags(1) && this._getContentBounds().contains(c, d) ? 0 !== e && this._mask ? this._mask._containsGlobalPoint(a, b, 1, null) : 1 : 0; - }; - r.prototype._containsPointDirectly = function(a, b) { - var c = this._getGraphics(); - return!!c && c._containsPoint(a, b, !0); + r.prototype._boundsAndMaskContainPoint = function(a, b, d, e, g) { + return 1 <= g && this._hasFlags(32768) ? 1 : 3 <= g && !this._hasFlags(1) || !this._getContentBounds().contains(d, e) ? 0 : 0 !== g && this._mask ? this._mask._containsGlobalPoint(a, b, 1, null) : 1; + }; + r.prototype._containsPointDirectly = function(a, b, d, e) { + return!1; }; Object.defineProperty(r.prototype, "scrollRect", {get:function() { - return this._scrollRect ? this._scrollRect.clone() : null; + return this._getScrollRect(); }, set:function(a) { this._scrollRect = a ? a.clone() : null; - m("public DisplayObject::set scrollRect"); + u("public DisplayObject::set scrollRect"); }, enumerable:!0, configurable:!0}); + r.prototype._getScrollRect = function() { + return this._scrollRect ? this._scrollRect.clone() : null; + }; Object.defineProperty(r.prototype, "opaqueBackground", {get:function() { return this._opaqueBackground; }, set:function(a) { - p(null === a || b.isInteger(a)); + q(null === a || c.isInteger(a)); this._opaqueBackground = a; }, enumerable:!0, configurable:!0}); r.prototype._getDistance = function(a) { - for (var b = 0, c = this;c !== a;) { - b++, c = c._parent; + for (var b = 0, d = this;d !== a;) { + b++, d = d._parent; } return b; }; @@ -24871,37 +31205,51 @@ if (!a) { return null; } - for (var b = this, c = b._getDistance(null), d = a._getDistance(null);c > d;) { - b = b._parent, c--; + for (var b = this, d = b._getDistance(null), e = a._getDistance(null);d > e;) { + b = b._parent, d--; } - for (;d > c;) { - a = a._parent, d--; + for (;e > d;) { + a = a._parent, e--; } for (;b !== a;) { b = b._parent, a = a._parent; } return b; }; + r.prototype._getLocalMousePosition = function() { + var b = a.ui.Mouse._currentPosition; + this._parent && (b = this._parent.globalToLocal(a.ui.Mouse._currentPosition)); + return b; + }; Object.defineProperty(r.prototype, "mouseX", {get:function() { - return this.globalToLocal(f.ui.Mouse._currentPosition).x; + return this._getLocalMousePosition().x; }, enumerable:!0, configurable:!0}); Object.defineProperty(r.prototype, "mouseY", {get:function() { - return this.globalToLocal(f.ui.Mouse._currentPosition).y; + return this._getLocalMousePosition().y; }, enumerable:!0, configurable:!0}); - r.prototype.debugName = function() { - return this._id + " [" + this._depth + "]: (" + this._referenceCount + ") " + this; + r.prototype.debugName = function(a) { + void 0 === a && (a = !1); + var b = this._id + " [" + this._depth + "]: (" + this._referenceCount + ") " + this; + if (a) { + a = []; + for (var e = 0;32 > e;e++) { + this._hasFlags(1 << e) && a.push(d[1 << e]); + } + b += " " + a.join("|"); + } + return b; }; - r.prototype.debugTrace = function(a, c) { - "undefined" === typeof a && (a = 1024); - "undefined" === typeof c && (c = ""); - var d = this, e = new b.IndentingWriter; - this.visit(function(f) { - var l = f._getDistance(d); - if (l > a) { + r.prototype.debugTrace = function(a, b) { + void 0 === a && (a = 1024); + void 0 === b && (b = ""); + var d = this, e = new c.IndentingWriter; + this.visit(function(g) { + var f = g._getDistance(d); + if (f > a) { return 2; } - l = c + b.StringUtilities.multiple(" ", l); - e.writeLn(l + f.debugName()); + f = b + c.StringUtilities.multiple(" ", f); + e.writeObject(f + g.debugName() + ", bounds: " + g.getBounds(null).toString(), {"...":{value:g}}); return 0; }, 0); }; @@ -24921,36 +31269,40 @@ }, set:function(a) { this._accessibilityProperties = a; }, enumerable:!0, configurable:!0}); + Object.defineProperty(r.prototype, "blendShader", {set:function(a) { + p("public DisplayObject::set blendShader"); + }, enumerable:!0, configurable:!0}); r._syncID = 0; r._instanceID = 1; r.classInitializer = function() { - r.reset(); + this.reset(); }; - r.initializer = function(a) { - k.counter.count("DisplayObject::initializer"); - this._id = f.display.DisplayObject.getNextSyncID(); - this._displayObjectFlags = 1011875943; + r.initializer = function(b) { + h.counter.count("DisplayObject::initializer"); + this._id = a.display.DisplayObject.getNextSyncID(); + this._displayObjectFlags = 2085617767; this._stage = this._root = null; this._setInitialName(); this._mask = this._parent = null; this._z = 0; - this._scaleZ = this._scaleY = this._scaleX = 1; + this._scaleY = this._scaleX = 1; + this._skewY = this._skewX = 0; + this._scaleZ = 1; this._height = this._width = this._rotationZ = this._rotationY = this._rotationX = this._rotation = 0; - this._scrollRect = this._opaqueBackground = null; - this._filters = []; - this._blendMode = g.BlendMode.NORMAL; - p(this._blendMode); + this._filters = this._scrollRect = this._opaqueBackground = null; + this._blendMode = v.BlendMode.NORMAL; + q(this._blendMode); this._accessibilityProperties = this._loaderInfo = this._scale9Grid = null; - this._fillBounds = new e(0, 0, 0, 0); - this._lineBounds = new e(0, 0, 0, 0); + this._fillBounds = new n(0, 0, 0, 0); + this._lineBounds = new n(0, 0, 0, 0); this._clipDepth = -1; - this._concatenatedMatrix = new q.Matrix; - this._invertedConcatenatedMatrix = new q.Matrix; - this._matrix = new q.Matrix; - this._invertedMatrix = new q.Matrix; + this._concatenatedMatrix = new k.Matrix; + this._invertedConcatenatedMatrix = new k.Matrix; + this._matrix = new k.Matrix; + this._invertedMatrix = new k.Matrix; this._matrix3D = null; - this._colorTransform = new q.ColorTransform; - this._concatenatedColorTransform = new q.ColorTransform; + this._colorTransform = new k.ColorTransform; + this._concatenatedColorTransform = new k.ColorTransform; this._depth = -1; this._ratio = 0; this._index = -1; @@ -24958,48 +31310,48 @@ this._mouseDown = this._mouseOver = !1; this._children = this._graphics = this._symbol = null; this._referenceCount = 0; - a && (a.scale9Grid && (this._scale9Grid = a.scale9Grid), this._symbol = a); + b && (b.scale9Grid && (this._scale9Grid = b.scale9Grid), this._symbol = b); }; r.classSymbols = null; r.instanceSymbols = null; r._runScripts = !0; r._path = []; return r; - }(f.events.EventDispatcher); - g.DisplayObject = u; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var s = b.AVM2.Runtime.asCoerceString, m = b.Debug.assert, d = b.AVM2.Runtime.throwError, a = function(a) { - function n(a, b, c) { - "undefined" === typeof a && (a = null); - "undefined" === typeof b && (b = "auto"); - "undefined" === typeof c && (c = !1); - g.DisplayObject.instanceConstructorNoInitialize.call(this); + }(a.events.EventDispatcher); + v.DisplayObject = b; + })(a.display || (a.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(v) { + var p = c.AVM2.Runtime.asCoerceString, u = c.Debug.assert, l = c.AVM2.Runtime.throwError, e = function(e) { + function t(a, e, c) { + void 0 === a && (a = null); + void 0 === e && (e = "auto"); + void 0 === c && (c = !1); + v.DisplayObject.instanceConstructorNoInitialize.call(this); this._symbol ? this._bitmapData.class.instanceConstructorNoInitialize.call(this._bitmapData) : this.bitmapData = a; - this._pixelSnapping = s(b); + this._pixelSnapping = p(e); this._smoothing = !!c; } - __extends(n, a); - Object.defineProperty(n.prototype, "pixelSnapping", {get:function() { + __extends(t, e); + Object.defineProperty(t.prototype, "pixelSnapping", {get:function() { return this._pixelSnapping; }, set:function(a) { - 0 > g.PixelSnapping.toNumber(a) && d("ArgumentError", k.Errors.InvalidEnumError, "pixelSnapping"); - this._pixelSnapping = s(a); + 0 > v.PixelSnapping.toNumber(a) && l("ArgumentError", h.Errors.InvalidEnumError, "pixelSnapping"); + this._pixelSnapping = p(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(n.prototype, "smoothing", {get:function() { + Object.defineProperty(t.prototype, "smoothing", {get:function() { return this._smoothing; }, set:function(a) { this._smoothing = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(n.prototype, "bitmapData", {get:function() { + Object.defineProperty(t.prototype, "bitmapData", {get:function() { return this._bitmapData; }, set:function(a) { this._bitmapData !== a && (this._bitmapData && this._bitmapData._removeBitmapReferrer(this), a && a._addBitmapReferrer(this)); @@ -25007,101 +31359,125 @@ this._invalidateParentFillAndLineBounds(!0, !0); this._setDirtyFlags(16777216); }, enumerable:!0, configurable:!0}); - n.prototype._getContentBounds = function(a) { - return this._bitmapData ? this._bitmapData._getContentBounds() : new b.Bounds(0, 0, 0, 0); + t.prototype._getContentBounds = function(a) { + return this._bitmapData ? this._bitmapData._getContentBounds() : new c.Bounds(0, 0, 0, 0); }; - n.prototype._containsPointDirectly = function(a, b) { - m(this._getContentBounds().contains(a, b)); + t.prototype._containsPointDirectly = function(a, e, c, f) { + u(this._getContentBounds().contains(a, e)); return!0; }; - n.classInitializer = null; - n.initializer = function(a) { + t.classInitializer = null; + t.initializer = function(e) { this._smoothing = this._pixelSnapping = this._bitmapData = null; - if (a) { - var b = a.symbolClass; - b.isSubtypeOf(f.display.Bitmap) && (b = f.display.BitmapData); - this._bitmapData = b.initializeFrom(a); - this._setFillAndLineBoundsFromWidthAndHeight(20 * a.width | 0, 20 * a.height | 0); + if (e) { + var c = e.symbolClass; + c.isSubtypeOf(a.display.Bitmap) && (c = a.display.BitmapData); + this._bitmapData = c.initializeFrom(e); + this._setFillAndLineBoundsFromWidthAndHeight(20 * e.width | 0, 20 * e.height | 0); } }; - n.classSymbols = null; - n.instanceSymbols = null; - return n; - }(f.display.DisplayObject); - g.Bitmap = a; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + t.classSymbols = null; + t.instanceSymbols = null; + return t; + }(a.display.DisplayObject); + v.Bitmap = e; + })(a.display || (a.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(b) { - (function(g) { - var k = function(b) { - function d() { - g.DisplayObject.instanceConstructorNoInitialize.call(this); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.warning, u = function(a) { + function e() { + h.DisplayObject.instanceConstructorNoInitialize.call(this); } - __extends(d, b); - d.prototype._canHaveGraphics = function() { + __extends(e, a); + e.prototype._canHaveGraphics = function() { return!0; }; - d.prototype._getGraphics = function() { + e.prototype._getGraphics = function() { return this._graphics; }; - Object.defineProperty(d.prototype, "graphics", {get:function() { + Object.defineProperty(e.prototype, "graphics", {get:function() { return this._ensureGraphics(); }, enumerable:!0, configurable:!0}); - d.prototype._containsPointDirectly = function(a, b) { - var d = this._getGraphics(); - return!!d && d._containsPoint(a, b, !0); + e.prototype._containsPointDirectly = function(a, e, c, l) { + c = this._getGraphics(); + return!!c && c._containsPoint(a, e, !0, 0); }; - d.classSymbols = null; - d.instanceSymbols = null; - d.classInitializer = null; - d.initializer = function(a) { + e.classSymbols = null; + e.instanceSymbols = null; + e.classInitializer = null; + e.initializer = function(a) { this._graphics = null; a && this._setStaticContentFromSymbol(a); }; - return d; - }(b.display.DisplayObject); - g.Shape = k; - })(b.display || (b.display = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = b.Debug.somewhatImplemented, d = f.display.DisplayObject, a = f.events, c = function(b) { + return e; + }(a.display.DisplayObject); + h.Shape = u; + u = function(c) { + function e(a, e) { + c.call(this, a, e, !1); + this.graphics = null; + } + __extends(e, c); + e.FromData = function(c, l) { + var h = new e(c, a.display.Shape); + h._setBoundsFromData(c); + h.graphics = a.display.Graphics.FromData(c); + h.processRequires(c.require, l); + return h; + }; + e.prototype.processRequires = function(a, e) { + if (a) { + for (var c = this.graphics._textures, l = 0;l < a.length;l++) { + var k = e.getSymbolById(a[l]); + k ? c.push(k.getSharedInstance()) : (65535 !== a[l] && p("Bitmap symbol " + a[l] + " required by shape, but not defined."), c.push(null)); + } + } + }; + return e; + }(c.Timeline.DisplaySymbol); + h.ShapeSymbol = u; + })(a.display || (a.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = c.Debug.somewhatImplemented, l = a.display.DisplayObject, e = a.events, m = function(a) { function c() { - d.instanceConstructorNoInitialize.call(this); + l.instanceConstructorNoInitialize.call(this); } - __extends(c, b); + __extends(c, a); Object.defineProperty(c.prototype, "tabEnabled", {get:function() { return this._tabEnabled; - }, set:function(b) { - b = !!b; + }, set:function(a) { + a = !!a; var c = this._tabEnabled; - this._tabEnabled = b; - c !== b && this.dispatchEvent(a.Event.getInstance(a.Event.TAB_ENABLED_CHANGE, !0)); + this._tabEnabled = a; + c !== a && this.dispatchEvent(e.Event.getInstance(e.Event.TAB_ENABLED_CHANGE, !0)); }, enumerable:!0, configurable:!0}); Object.defineProperty(c.prototype, "tabIndex", {get:function() { return this._tabIndex; - }, set:function(b) { - b |= 0; + }, set:function(a) { + a |= 0; var c = this._tabIndex; - this._tabIndex = b; - c !== b && this.dispatchEvent(a.Event.getInstance(a.Event.TAB_INDEX_CHANGE, !0)); + this._tabIndex = a; + c !== a && this.dispatchEvent(e.Event.getInstance(e.Event.TAB_INDEX_CHANGE, !0)); }, enumerable:!0, configurable:!0}); Object.defineProperty(c.prototype, "focusRect", {get:function() { return this._focusRect; }, set:function(a) { - m("public flash.display.InteractiveObject::set focusRect"); + u("public flash.display.InteractiveObject::set focusRect"); this._focusRect = a; }, enumerable:!0, configurable:!0}); Object.defineProperty(c.prototype, "mouseEnabled", {get:function() { @@ -25117,26 +31493,26 @@ Object.defineProperty(c.prototype, "accessibilityImplementation", {get:function() { return this._accessibilityImplementation; }, set:function(a) { - k("public flash.display.InteractiveObject::set accessibilityImplementation"); + p("public flash.display.InteractiveObject::set accessibilityImplementation"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c.prototype, "softKeyboardInputAreaOfInterest", {get:function() { return this._softKeyboardInputAreaOfInterest; }, set:function(a) { - k("public flash.display.InteractiveObject::set softKeyboardInputAreaOfInterest"); + p("public flash.display.InteractiveObject::set softKeyboardInputAreaOfInterest"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c.prototype, "needsSoftKeyboard", {get:function() { return this._needsSoftKeyboard; }, set:function(a) { - k("public flash.display.InteractiveObject::set needsSoftKeyboard"); + p("public flash.display.InteractiveObject::set needsSoftKeyboard"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c.prototype, "contextMenu", {get:function() { return this._contextMenu; }, set:function(a) { - m("public flash.display.InteractiveObject::set contextMenu"); + u("public flash.display.InteractiveObject::set contextMenu"); this._contextMenu = a; }, enumerable:!0, configurable:!0}); c.prototype.requestSoftKeyboard = function() { - k("public flash.display.InteractiveObject::requestSoftKeyboard"); + p("public flash.display.InteractiveObject::requestSoftKeyboard"); }; c.classInitializer = null; c.initializer = function() { @@ -25152,1130 +31528,1188 @@ c.classSymbols = null; c.instanceSymbols = null; return c; - }(f.display.DisplayObject); - g.InteractiveObject = c; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = b.Debug.assert, d = function(a) { - function b(a, c, d, f) { - k("Dummy Constructor: public flash.display.SimpleButton"); + }(a.display.DisplayObject); + h.InteractiveObject = m; + })(a.display || (a.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = c.Debug.assert, l = function(a) { + function e(a, c, k, f) { + void 0 === a && (a = null); + void 0 === c && (c = null); + void 0 === k && (k = null); + void 0 === f && (f = null); + h.InteractiveObject.instanceConstructorNoInitialize.call(this); + a && (this.upState = a); + c && (this.overState = c); + k && (this.downState = k); + f && (this.hitTestState = f); + this._updateButton(); } - __extends(b, a); - b.prototype._initFrame = function(a) { + __extends(e, a); + e.prototype._initFrame = function(a) { a && this._updateButton(); }; - b.prototype._constructFrame = function() { + e.prototype._constructFrame = function() { }; - Object.defineProperty(b.prototype, "useHandCursor", {get:function() { + Object.defineProperty(e.prototype, "useHandCursor", {get:function() { return this._useHandCursor; }, set:function(a) { this._useHandCursor = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "enabled", {get:function() { + Object.defineProperty(e.prototype, "enabled", {get:function() { return this._enabled; }, set:function(a) { this._enabled = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "trackAsMenu", {get:function() { + Object.defineProperty(e.prototype, "trackAsMenu", {get:function() { return this._trackAsMenu; }, set:function(a) { - k("public flash.display.SimpleButton::set trackAsMenu"); + p("public flash.display.SimpleButton::set trackAsMenu"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "upState", {get:function() { + Object.defineProperty(e.prototype, "upState", {get:function() { return this._upState; }, set:function(a) { - var b = this._upState; + var e = this._upState; a._parent && a._parent.removeChild(a); this._upState = a; - this._currentState === b && this._updateButton(); + this._currentState === e && this._updateButton(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "overState", {get:function() { + Object.defineProperty(e.prototype, "overState", {get:function() { return this._overState; }, set:function(a) { - var b = this._overState; + var e = this._overState; a._parent && a._parent.removeChild(a); this._overState = a; - this._currentState === b && this._updateButton(); + this._currentState === e && this._updateButton(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "downState", {get:function() { + Object.defineProperty(e.prototype, "downState", {get:function() { return this._downState; }, set:function(a) { - var b = this._downState; + var e = this._downState; a._parent && a._parent.removeChild(a); this._downState = a; - this._currentState === b && this._updateButton(); + this._currentState === e && this._updateButton(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "hitTestState", {get:function() { + Object.defineProperty(e.prototype, "hitTestState", {get:function() { return this._hitTestState; }, set:function(a) { this._hitTestState = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "soundTransform", {get:function() { - k("public flash.display.SimpleButton::get soundTransform"); + Object.defineProperty(e.prototype, "soundTransform", {get:function() { + p("public flash.display.SimpleButton::get soundTransform"); }, set:function(a) { - k("public flash.display.SimpleButton::set soundTransform"); + p("public flash.display.SimpleButton::set soundTransform"); }, enumerable:!0, configurable:!0}); - b.prototype._containsPoint = function(a, b, c, d, f, g) { - var k = this.hitTestState; - if (!k) { + e.prototype._containsPoint = function(a, e, c, f, d, b) { + c = 3 === d ? this._hitTestState : this._currentState; + if (!c) { return 0; } - if (!this._symbol) { - var r = k._getInvertedMatrix(), h = r.transformX(c, d); - d = r.transformY(c, d); - c = h; - } - a = k._containsPoint(a, b, c, d, f, g); - 0 !== a && 3 === f && g && this._mouseEnabled && (g[0] = this, m(1 === g.length)); + c._parent = this; + a = c._containsGlobalPoint(a, e, d, b); + c._parent = null; + 0 !== a && 3 === d && b && this._mouseEnabled && (b[0] = this, u(1 === b.length)); return a; }; - b.prototype._getChildBounds = function(a, b) { - var c = this.hitTestState; - if (c) { - var d = c._getContentBounds(b).clone(); - this._getConcatenatedMatrix().transformBounds(d); - this._symbol || c._getMatrix().transformBounds(a); - a.unionInPlace(d); - } + e.prototype._getChildBounds = function(a, e) { + this._currentState && (this._currentState._parent = this, a.unionInPlace(this._currentState._getTransformedBounds(this, e)), this._currentState._parent = null); }; - b.prototype._updateButton = function() { + e.prototype._propagateFlagsDown = function(a) { + this._hasFlags(a) || (this._setFlags(a), this._upState && this._upState._propagateFlagsDown(a), this._overState && this._overState._propagateFlagsDown(a), this._downState && this._downState._propagateFlagsDown(a), this._hitTestState && this._hitTestState._propagateFlagsDown(a)); + }; + e.prototype._updateButton = function() { var a; a = this._mouseOver ? this._mouseDown ? this._downState : this._overState : this._upState; a !== this._currentState && ((this._currentState = a) ? this._children[0] = a : this._children.length = 0, this._setDirtyFlags(2097152), this._invalidateFillAndLineBounds(!0, !0)); }; - b.classInitializer = null; - b.initializer = function(a) { - g.DisplayObject._advancableInstances.push(this); + e.classInitializer = null; + e.initializer = function(a) { + h.DisplayObject._advancableInstances.push(this); this._enabled = this._useHandCursor = !0; this._trackAsMenu = !1; this._currentState = this._hitTestState = this._downState = this._overState = this._upState = null; this._children = []; if (this._symbol = a) { - a.upState && (this._upState = g.DisplayObject.createAnimatedDisplayObject(a.upState, !0)), a.overState && (this._overState = g.DisplayObject.createAnimatedDisplayObject(a.overState, !0)), a.downState && (this._downState = g.DisplayObject.createAnimatedDisplayObject(a.downState, !0)), a.hitTestState && (this._hitTestState = g.DisplayObject.createAnimatedDisplayObject(a.hitTestState, !0)); + a.upState && (this._upState = this.createAnimatedDisplayObject(a.upState.symbol, a.upState.placeObjectTag, !0)), a.overState && (this._overState = this.createAnimatedDisplayObject(a.overState.symbol, a.overState.placeObjectTag, !0)), a.downState && (this._downState = this.createAnimatedDisplayObject(a.downState.symbol, a.downState.placeObjectTag, !0)), a.hitTestState && (this._hitTestState = this.createAnimatedDisplayObject(a.hitTestState.symbol, a.hitTestState.placeObjectTag, !0)) + ; } }; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(f.display.InteractiveObject); - g.SimpleButton = d; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var s = b.Debug.assert, m = b.Debug.notImplemented, d = b.AVM2.Runtime.asCoerceString, a = b.AVM2.Runtime.throwError, c = b.AVM2.Runtime.checkParameterType, n = b.NumberUtilities.clamp, p = b.AVM2.ABC.Multiname, e = f.events, q = function(b) { - function q() { - g.InteractiveObject.instanceConstructorNoInitialize.call(this); + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.display.InteractiveObject); + h.SimpleButton = l; + var e = function() { + return function(a, e) { + this.symbol = a; + this.placeObjectTag = e; + }; + }(); + h.ButtonState = e; + l = function(m) { + function l(e, c) { + m.call(this, e, a.display.SimpleButton, !0); + this.hitTestState = this.downState = this.overState = this.upState = null; + this.loaderInfo = c; + } + __extends(l, m); + l.FromData = function(m, n) { + var k = new l(m, n); + n.actionScriptVersion === h.ActionScriptVersion.ACTIONSCRIPT2 && (k.isAVM1Object = !0); + var f = m.states, d = null, b, g; + for (g in f) { + var r = f[g]; + if (1 === r.length) { + if (b = r[0], d = n.getSymbolById(b.symbolId), !d) { + continue; + } + } else { + b = {flags:1, depth:1}, d = new a.display.SpriteSymbol({id:-1, className:null}, n), d.frames.push(new c.SWF.SWFFrame(r)); + } + k[g + "State"] = new e(d, b); + } + return k; + }; + return l; + }(c.Timeline.DisplaySymbol); + h.ButtonSymbol = l; + })(a.display || (a.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(v) { + var p = c.Debug.assert, u = c.Debug.notImplemented, l = c.AVM2.Runtime.asCoerceString, e = c.AVM2.Runtime.throwError, m = c.AVM2.Runtime.checkParameterType, t = c.NumberUtilities.clamp, q = c.AVM2.ABC.Multiname, n = a.events, k = function(c) { + function d() { + v.InteractiveObject.instanceConstructorNoInitialize.call(this); this._setDirtyFlags(2097152); } - __extends(q, b); - q.prototype._invalidateChildren = function() { + __extends(d, c); + d.prototype._invalidateChildren = function() { this._setDirtyFlags(2097152); this._invalidateFillAndLineBounds(!0, !0); }; - q.prototype._propagateFlagsDown = function(a) { + d.prototype._propagateFlagsDown = function(a) { if (!this._hasFlags(a)) { this._setFlags(a); - for (var b = this._children, c = 0;c < b.length;c++) { - b[c]._propagateFlagsDown(a); + for (var d = this._children, e = 0;e < d.length;e++) { + d[e]._propagateFlagsDown(a); } } }; - q.prototype._constructChildren = function() { - k.counter.count("DisplayObjectContainer::_constructChildren"); - for (var a = this._children, b = 0;b < a.length;b++) { - var c = a[b]; - c._hasFlags(256) || (c.class.instanceConstructorNoInitialize.call(c), c._removeReference(), c._name && (this[p.getPublicQualifiedName(c._name)] = c), c._setFlags(256), c._symbol && c._symbol.isAVM1Object && (c.dispatchEvent(e.Event.getInstance(e.Event.AVM1_INIT)), c.dispatchEvent(e.Event.getInstance(e.Event.AVM1_CONSTRUCT))), c.dispatchEvent(e.Event.getInstance(e.Event.ADDED, !0)), c.stage && c.dispatchEvent(e.Event.getInstance(e.Event.ADDED_TO_STAGE))); + d.prototype._constructChildren = function() { + h.counter.count("DisplayObjectContainer::_constructChildren"); + for (var a = this._children, d = 0;d < a.length;d++) { + var e = a[d]; + e._hasFlags(256) || (e.class.instanceConstructorNoInitialize.call(e), e._removeReference(), e._name && (this[q.getPublicQualifiedName(e._name)] = e), e._setFlags(256), e._symbol && e._symbol.isAVM1Object && (e.dispatchEvent(n.Event.getInstance(n.Event.AVM1_INIT)), e.dispatchEvent(n.Event.getInstance(n.Event.AVM1_CONSTRUCT)), e._setFlags(1024), e._hasAnyFlags(24576) && this._setFlags(16384)), e.dispatchEvent(n.Event.getInstance(n.Event.ADDED, !0)), e.stage && e.dispatchEvent(n.Event.getInstance(n.Event.ADDED_TO_STAGE))); } }; - q.prototype._enqueueFrameScripts = function() { - if (this._hasFlags(8192)) { - this._removeFlags(8192); - for (var a = this._children, b = 0;b < a.length;b++) { - var c = a[b]; - (q.isType(c) || g.AVM1Movie.isType(c)) && c._enqueueFrameScripts(); + d.prototype._enqueueFrameScripts = function() { + if (this._hasFlags(16384)) { + this._removeFlags(16384); + for (var a = this._children, e = 0;e < a.length;e++) { + var c = a[e]; + (d.isType(c) || v.AVM1Movie.isType(c)) && c._enqueueFrameScripts(); } } }; - Object.defineProperty(q.prototype, "numChildren", {get:function() { + Object.defineProperty(d.prototype, "numChildren", {get:function() { return this._children.length; }, enumerable:!0, configurable:!0}); - Object.defineProperty(q.prototype, "textSnapshot", {get:function() { - m("public DisplayObjectContainer::get textSnapshot"); + d.prototype._getNumChildren = function() { + return this._children.length; + }; + Object.defineProperty(d.prototype, "textSnapshot", {get:function() { + u("public DisplayObjectContainer::get textSnapshot"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(q.prototype, "tabChildren", {get:function() { + Object.defineProperty(d.prototype, "tabChildren", {get:function() { return this._tabChildren; }, set:function(a) { + this._setTabChildren(a); + }, enumerable:!0, configurable:!0}); + d.prototype._getTabChildren = function() { + return this._tabChildren; + }; + d.prototype._setTabChildren = function(a) { a = !!a; - var b = this._tabChildren; + var d = this._tabChildren; this._tabChildren = a; - b !== a && this.dispatchEvent(e.Event.getInstance(e.Event.TAB_CHILDREN_CHANGE, !0)); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(q.prototype, "mouseChildren", {get:function() { + d !== a && this.dispatchEvent(n.Event.getInstance(n.Event.TAB_CHILDREN_CHANGE, !0)); + }; + Object.defineProperty(d.prototype, "mouseChildren", {get:function() { return this._mouseChildren; }, set:function(a) { - this._mouseChildren = !!a; + this._setMouseChildren(a); }, enumerable:!0, configurable:!0}); - q.prototype.addChild = function(a) { - c(a, "child", f.display.DisplayObject); - return this.addChildAt(a, this._children.length); - }; - q.prototype.addChildAt = function(b, d) { - c(b, "child", f.display.DisplayObject); - k.counter.count("DisplayObjectContainer::addChildAt"); - d |= 0; - s(b._hasFlags(256), "Child is not fully constructed."); - b === this && a("ArgumentError", k.Errors.CantAddSelfError); - q.isType(b) && b.contains(this) && a("ArgumentError", k.Errors.CantAddParentError); - var h = this._children; - (0 > d || d > h.length) && a("RangeError", k.Errors.ParamRangeError); + d.prototype._getMouseChildren = function() { + return this._mouseChildren; + }; + d.prototype._setMouseChildren = function(a) { + this._mouseChildren = !!a; + }; + d.prototype.addChild = function(b) { + m(b, "child", a.display.DisplayObject); + return this.addChildAt(b, this._children.length); + }; + d.prototype.addChildAt = function(b, g) { + m(b, "child", a.display.DisplayObject); + h.counter.count("DisplayObjectContainer::addChildAt"); + g |= 0; + p(b._hasFlags(256), "Child is not fully constructed."); + b === this && e("ArgumentError", h.Errors.CantAddSelfError); + d.isType(b) && b.contains(this) && e("ArgumentError", h.Errors.CantAddParentError); + var c = this._children; + (0 > g || g > c.length) && e("RangeError", h.Errors.ParamRangeError); if (b._parent === this) { - return this.setChildIndex(b, n(d, 0, h.length - 1)), b; + return this.setChildIndex(b, t(g, 0, c.length - 1)), b; } - b._parent && (b._parent.removeChild(b), d = n(d, 0, h.length)); - for (var l = h.length - 1;l >= d;l--) { - h[l]._index++; + b._parent && (d.prototype.removeChildAt.call(b._parent, b._parent.getChildIndex(b)), g = t(g, 0, c.length)); + for (var f = c.length - 1;f >= g;f--) { + c[f]._index++; } - h.splice(d, 0, b); + c.splice(g, 0, b); b._setParent(this, -1); - b._index = d; + b._index = g; b._invalidatePosition(); - b.dispatchEvent(e.Event.getInstance(e.Event.ADDED, !0)); - b.stage && b._propagateEvent(e.Event.getInstance(e.Event.ADDED_TO_STAGE)); + b.dispatchEvent(n.Event.getInstance(n.Event.ADDED, !0)); + b.stage && b._propagateEvent(n.Event.getInstance(n.Event.ADDED_TO_STAGE)); this._invalidateChildren(); b._addReference(); return b; }; - q.prototype.addTimelineObjectAtDepth = function(a, b) { - k.counter.count("DisplayObjectContainer::addTimelineObjectAtDepth"); - b |= 0; - for (var c = this._children, d = c.length - 1, e = d + 1, f = d;0 <= f;f--) { - var l = c[f]; - if (l._depth) { - if (l._depth < b) { - e = f + 1; + d.prototype.addTimelineObjectAtDepth = function(a, d) { + h.counter.count("DisplayObjectContainer::addTimelineObjectAtDepth"); + d |= 0; + for (var e = this._children, c = e.length - 1, f = c + 1, k = c;0 <= k;k--) { + var m = e[k]; + if (-1 < m._depth) { + if (m._depth < d) { + f = k + 1; break; } - e = f; + f = k; } } - if (e > d) { - c.push(a), a._index = e; + if (f > c) { + e.push(a), a._index = f; } else { - for (c.splice(e, 0, a), f = e;f < c.length;f++) { - c[f]._index = f; + for (e.splice(f, 0, a), k = f;k < e.length;k++) { + e[k]._index = k; } } - a._setParent(this, b); + a._setParent(this, d); a._invalidatePosition(); this._invalidateChildren(); }; - q.prototype.removeChild = function(a) { - c(a, "child", f.display.DisplayObject); - return this.removeChildAt(this.getChildIndex(a)); + d.prototype.removeChild = function(b) { + m(b, "child", a.display.DisplayObject); + return this.removeChildAt(this.getChildIndex(b)); }; - q.prototype.removeChildAt = function(b) { - k.counter.count("DisplayObjectContainer::removeChildAt"); - b |= 0; - var c = this._children; - (0 > b || b >= c.length) && a("RangeError", k.Errors.ParamRangeError); - var d = c[b]; - d._hasFlags(256) && (d.dispatchEvent(e.Event.getInstance(e.Event.REMOVED, !0)), this.stage && d._propagateEvent(e.Event.getInstance(e.Event.REMOVED_FROM_STAGE)), b = this.getChildIndex(d)); - c.splice(b, 1); - for (var f = c.length - 1;f >= b;f--) { - c[f]._index--; - } - d._setParent(null, -1); - d._index = -1; - d._invalidatePosition(); + d.prototype.removeChildAt = function(a) { + h.counter.count("DisplayObjectContainer::removeChildAt"); + a |= 0; + var d = this._children; + (0 > a || a >= d.length) && e("RangeError", h.Errors.ParamRangeError); + var c = d[a]; + c._hasFlags(256) && (c.dispatchEvent(n.Event.getInstance(n.Event.REMOVED, !0)), this.stage && c._propagateEvent(n.Event.getInstance(n.Event.REMOVED_FROM_STAGE)), a = this.getChildIndex(c)); + d.splice(a, 1); + for (var f = d.length - 1;f >= a;f--) { + d[f]._index--; + } + c._setParent(null, -1); + c._index = -1; + c._invalidatePosition(); this._invalidateChildren(); - return d; + return c; }; - q.prototype.getChildIndex = function(b) { - c(b, "child", f.display.DisplayObject); - b._parent !== this && a("ArgumentError", k.Errors.NotAChildError); + d.prototype.getChildIndex = function(b) { + m(b, "child", a.display.DisplayObject); + b._parent !== this && e("ArgumentError", h.Errors.NotAChildError); return b._index; }; - q.prototype.setChildIndex = function(b, d) { + d.prototype.setChildIndex = function(b, d) { d |= 0; - c(b, "child", f.display.DisplayObject); - var h = this._children; - (0 > d || d >= h.length) && a("RangeError", k.Errors.ParamRangeError); - b._parent !== this && a("ArgumentError", k.Errors.NotAChildError); - b._depth = -1; - var e = this.getChildIndex(b); - if (1 !== h.length && e !== d) { - if (d === e + 1 || d === e - 1) { - this._swapChildrenAt(e, d); + m(b, "child", a.display.DisplayObject); + var c = this._children; + (0 > d || d >= c.length) && e("RangeError", h.Errors.ParamRangeError); + b._parent !== this && e("ArgumentError", h.Errors.NotAChildError); + b._setDepth(-1); + var f = this.getChildIndex(b); + if (1 !== c.length && f !== d) { + if (d === f + 1 || d === f - 1) { + this._swapChildrenAt(f, d); } else { - for (h.splice(e, 1), h.splice(d, 0, b), e = e < d ? e : d;e < h.length;) { - h[e]._index = e++; + for (c.splice(f, 1), c.splice(d, 0, b), f = f < d ? f : d;f < c.length;) { + c[f]._index = f++; } } this._invalidateChildren(); } }; - q.prototype.getChildAt = function(b) { - b |= 0; - var c = this._children; - (0 > b || b >= c.length) && a("RangeError", k.Errors.ParamRangeError); - b = c[b]; - if (!b._hasFlags(256)) { + d.prototype.getChildAt = function(a) { + a |= 0; + var d = this._children; + (0 > a || a >= d.length) && e("RangeError", h.Errors.ParamRangeError); + a = this._lookupChildByIndex(a); + if (!a) { return null; } - b._addReference(); - return b; + a._addReference(); + return a; }; - q.prototype.getTimelineObjectAtDepth = function(a) { + d.prototype.getTimelineObjectAtDepth = function(a) { a |= 0; - for (var b = this._children, c = 0;c < b.length;c++) { - var d = b[c]; - if (d._depth > a) { + for (var d = this._children, e = 0;e < d.length;e++) { + var c = d[e]; + if (c._depth > a) { break; } - if (d._depth === a) { - return d; + if (c._depth === a) { + return c; } } return null; }; - q.prototype.getClipDepthIndex = function(a) { + d.prototype.getClipDepthIndex = function(a) { a |= 0; - for (var b = this._children, c = this._children.length - 1, d = !0, e = c;0 <= e;e--) { - var f = b[e]; - if (!(0 > f._depth)) { - if (f._depth <= a) { - return d ? c : e; + for (var d = this._children, e = this._children.length - 1, c = !0, f = e;0 <= f;f--) { + var k = d[f]; + if (!(0 > k._depth)) { + if (k._depth <= a) { + return c ? e : f; } - d = !1; + c = !1; } } return 0; }; - q.prototype.getChildByName = function(a) { - a = d(a); - for (var b = this._children, c = 0;c < b.length;c++) { - var e = b[c]; - if (e._hasFlags(256) && e.name === a) { - return e._addReference(), e; + d.prototype.getChildByName = function(a) { + a = l(a); + return(a = this._lookupChildByName(a)) ? (a._addReference(), a) : null; + }; + d.prototype._lookupChildByIndex = function(a) { + return(a = this._children[a]) && a._hasFlags(256) ? a : null; + }; + d.prototype._lookupChildByName = function(a) { + for (var d = this._children, e = 0;e < d.length;e++) { + var c = d[e]; + if (c._hasFlags(256) && c.name === a) { + return c; } } return null; }; - q.prototype._containsPoint = function(a, b, c, d, e, f) { - var l = Math.min(e, 1), n = this._boundsAndMaskContainPoint(a, b, c, d, l); - if (0 === n || 2 > e) { - return n; - } - for (var l = !1, m = this._getUnclippedChildren(e, a, b), p = m ? m.length : 0;p--;) { - if (n = m[p]._containsGlobalPoint(a, b, e, f), 2 === n) { - if (3 > e) { - return n; - } - l = !0; - if (4 !== e) { - s(3 === e); - s(1 >= f.length); + d.prototype._containsPoint = function(a, d, e, c, f, k) { + return this._containsPointImpl(a, d, e, c, f, k, !1); + }; + d.prototype._containsPointImpl = function(a, d, e, c, f, k, m) { + var l; + if (!m && (l = this._boundsAndMaskContainPoint(a, d, e, c, f), 0 === l || 2 > f)) { + return l; + } + m = !1; + for (var h = this._getUnclippedChildren(f, a, d), n = h ? h.length : 0;n--;) { + if (l = h[n], !l._maskedObject && (l = l._containsGlobalPoint(a, d, f, k), 2 === l)) { + if (3 > f) { + return l; + } + m = !0; + if (!(4 <= f)) { + p(3 === f); + p(1 >= k.length); if (!this._mouseEnabled) { - return f.length = 0, n; + return k.length = 0, l; } - this._mouseChildren || (f[0] = this); - if (0 !== f.length) { - return s(g.InteractiveObject.isType(f[0])), 2; + this._mouseChildren || (k[0] = this); + if (0 !== k.length) { + return p(v.InteractiveObject.isType(k[0])), 2; } } } } - if (l && 4 !== e) { - return 3 === e && 0 === f.length && (f[0] = this), 2; + if (m && 4 > f) { + return 3 === f && 0 === k.length && (k[0] = this), 2; } - (a = this._containsPointDirectly(c, d)) && (4 === e || f && this._mouseEnabled) && f.push(this); - return l || a ? 2 : 0; + (a = this._containsPointDirectly(e, c, a, d)) && (5 === f ? k[0] = this : (4 === f || k && this._mouseEnabled) && k.push(this)); + return m || a ? 2 : 0; }; - q.prototype._getUnclippedChildren = function(a, b, c) { - var d = this._children; - if (!d) { + d.prototype._getUnclippedChildren = function(a, d, e) { + var c = this._children; + if (!c) { return null; } - for (var e, f = 0;d && f < d.length;f++) { - var l = d[f]; - -1 !== l._clipDepth ? (e || (e = []), 2 !== a && (s(3 <= a), l._containsGlobalPoint(b, c, a, null) || (f = this.getClipDepthIndex(l._clipDepth)))) : e && e.push(l); + for (var f, k = 0;c && k < c.length;k++) { + var m = c[k]; + -1 !== m._clipDepth ? (f || (f = c.slice(0, k)), 2 !== a && (p(3 <= a), m._containsGlobalPoint(d, e, 2, null) || (k = this.getClipDepthIndex(m._clipDepth)))) : f && f.push(m); } - return e || d; + return f || c; }; - q.prototype._getChildBounds = function(a, b) { - for (var c = this._children, d = 0;d < c.length;d++) { - a.unionInPlace(c[d]._getTransformedBounds(this, b)); + d.prototype._getChildBounds = function(a, d) { + for (var e = this._children, c = 0;c < e.length;c++) { + a.unionInPlace(e[c]._getTransformedBounds(this, d)); } }; - q.prototype.getObjectsUnderPoint = function(a) { - k.counter.count("DisplayObjectContainer::getObjectsUnderPoint"); - var b = []; - this._containsGlobalPoint(20 * a.x | 0, 20 * a.y | 0, 4, b); - return b.reverse(); + d.prototype.getObjectsUnderPoint = function(a) { + h.counter.count("DisplayObjectContainer::getObjectsUnderPoint"); + var d = []; + this._containsGlobalPoint(20 * a.x | 0, 20 * a.y | 0, 4, d); + return d.reverse(); }; - q.prototype.areInaccessibleObjectsUnderPoint = function(a) { - m("public DisplayObjectContainer::areInaccessibleObjectsUnderPoint"); + d.prototype.areInaccessibleObjectsUnderPoint = function(a) { + u("public DisplayObjectContainer::areInaccessibleObjectsUnderPoint"); }; - q.prototype.contains = function(a) { - c(a, "child", f.display.DisplayObject); - return this._isAncestor(a); - }; - q.prototype.swapChildrenAt = function(b, c) { - b |= 0; - c |= 0; - var d = this._children; - (0 > b || b >= d.length || 0 > c || c >= d.length) && a("RangeError", k.Errors.ParamRangeError); - b !== c && (this._swapChildrenAt(b, c), this._invalidateChildren()); + d.prototype.contains = function(b) { + m(b, "child", a.display.DisplayObject); + return this._isAncestor(b); }; - q.prototype._swapChildrenAt = function(a, b) { - var c = this._children, d = c[a], e = c[b]; - c[b] = d; - d._depth = -1; - d._index = b; - c[a] = e; - e._depth = -1; - e._index = a; - }; - q.prototype.swapChildren = function(a, b) { - c(a, "child", f.display.DisplayObject); - c(b, "child", f.display.DisplayObject); - this.swapChildrenAt(this.getChildIndex(a), this.getChildIndex(b)); - }; - q.prototype.removeChildren = function(b, c) { - "undefined" === typeof b && (b = 0); - "undefined" === typeof c && (c = 2147483647); - b |= 0; - c |= 0; - (0 > b || 0 > c || c < b || c >= this._children.length) && a("RangeError", k.Errors.ParamRangeError); - var d = c - b + 1; - if (0 < d) { - for (;d--;) { - this.removeChildAt(b); + d.prototype.swapChildrenAt = function(a, d) { + a |= 0; + d |= 0; + var c = this._children; + (0 > a || a >= c.length || 0 > d || d >= c.length) && e("RangeError", h.Errors.ParamRangeError); + this._swapChildrenAt(a, d); + a !== d && this._invalidateChildren(); + }; + d.prototype._swapChildrenAt = function(a, d) { + var e = this._children, c = e[a], f = e[d]; + e[d] = c; + c._setDepth(-1); + c._index = d; + e[a] = f; + f._setDepth(-1); + f._index = a; + }; + d.prototype.swapChildren = function(b, d) { + m(b, "child", a.display.DisplayObject); + m(d, "child", a.display.DisplayObject); + this.swapChildrenAt(this.getChildIndex(b), this.getChildIndex(d)); + }; + d.prototype.removeChildren = function(a, d) { + void 0 === a && (a = 0); + void 0 === d && (d = 2147483647); + a |= 0; + d |= 0; + (0 > a || 0 > d || d < a || d >= this._children.length) && e("RangeError", h.Errors.ParamRangeError); + var c = d - a + 1; + if (0 < c) { + for (;c--;) { + this.removeChildAt(a); } } }; - q.bindings = null; - q.classSymbols = null; - q.classInitializer = null; - q.initializer = function() { + d.bindings = null; + d.classSymbols = null; + d.classInitializer = null; + d.initializer = function() { this._mouseChildren = this._tabChildren = !0; this._children = []; }; - return q; - }(f.display.InteractiveObject); - g.DisplayObjectContainer = q; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.JointStyle"); + return d; + }(a.display.InteractiveObject); + v.DisplayObjectContainer = k; + })(a.display || (a.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.JointStyle"); } - __extends(a, b); - a.fromNumber = function(b) { - switch(b) { + __extends(e, a); + e.fromNumber = function(a) { + switch(a) { case 0: - return a.ROUND; + return e.ROUND; case 1: - return a.BEVEL; + return e.BEVEL; case 2: - return a.MITER; + return e.MITER; default: return null; } }; - a.toNumber = function(b) { - switch(b) { - case a.ROUND: + e.toNumber = function(a) { + switch(a) { + case e.ROUND: return 0; - case a.BEVEL: + case e.BEVEL: return 1; - case a.MITER: + case e.MITER: return 2; default: return-1; } }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.ROUND = "round"; - a.BEVEL = "bevel"; - a.MITER = "miter"; - return a; - }(g.ASNative); - f.JointStyle = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.CapsStyle"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.ROUND = "round"; + e.BEVEL = "bevel"; + e.MITER = "miter"; + return e; + }(a.ASNative); + h.JointStyle = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.CapsStyle"); } - __extends(a, b); - a.fromNumber = function(b) { - switch(b) { + __extends(e, a); + e.fromNumber = function(a) { + switch(a) { case 0: - return a.ROUND; + return e.ROUND; case 1: - return a.NONE; + return e.NONE; case 2: - return a.SQUARE; + return e.SQUARE; default: return null; } }; - a.toNumber = function(b) { - switch(b) { - case a.ROUND: + e.toNumber = function(a) { + switch(a) { + case e.ROUND: return 0; - case a.NONE: + case e.NONE: return 1; - case a.SQUARE: + case e.SQUARE: return 2; default: return-1; } }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.ROUND = "round"; - a.NONE = "none"; - a.SQUARE = "square"; - return a; - }(g.ASNative); - f.CapsStyle = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.LineScaleMode"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.ROUND = "round"; + e.NONE = "none"; + e.SQUARE = "square"; + return e; + }(a.ASNative); + h.CapsStyle = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.LineScaleMode"); } - __extends(a, b); - a.fromNumber = function(b) { - switch(b) { + __extends(e, a); + e.fromNumber = function(a) { + switch(a) { case 0: - return a.NONE; + return e.NONE; case 1: - return a.NORMAL; + return e.NORMAL; case 2: - return a.VERTICAL; + return e.VERTICAL; case 3: - return a.HORIZONTAL; + return e.HORIZONTAL; default: return null; } }; - a.toNumber = function(b) { - switch(b) { - case a.NONE: + e.toNumber = function(a) { + switch(a) { + case e.NONE: return 0; - case a.NORMAL: + case e.NORMAL: return 1; - case a.VERTICAL: + case e.VERTICAL: return 2; - case a.HORIZONTAL: + case e.HORIZONTAL: return 3; default: return-1; } }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.NORMAL = "normal"; - a.VERTICAL = "vertical"; - a.HORIZONTAL = "horizontal"; - a.NONE = "none"; - return a; - }(g.ASNative); - f.LineScaleMode = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.GradientType"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.NORMAL = "normal"; + e.VERTICAL = "vertical"; + e.HORIZONTAL = "horizontal"; + e.NONE = "none"; + return e; + }(a.ASNative); + h.LineScaleMode = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.GradientType"); } - __extends(a, b); - a.fromNumber = function(b) { - switch(b) { + __extends(e, a); + e.fromNumber = function(a) { + switch(a) { case 16: - return a.LINEAR; + return e.LINEAR; case 18: - return a.RADIAL; + return e.RADIAL; default: return null; } }; - a.toNumber = function(b) { - switch(b) { - case a.LINEAR: + e.toNumber = function(a) { + switch(a) { + case e.LINEAR: return 16; - case a.RADIAL: + case e.RADIAL: return 18; default: return-1; } }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.LINEAR = "linear"; - a.RADIAL = "radial"; - return a; - }(g.ASNative); - f.GradientType = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.SpreadMethod"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.LINEAR = "linear"; + e.RADIAL = "radial"; + return e; + }(a.ASNative); + h.GradientType = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.SpreadMethod"); } - __extends(a, b); - a.fromNumber = function(b) { - switch(b) { + __extends(e, a); + e.fromNumber = function(a) { + switch(a) { case 0: - return a.PAD; + return e.PAD; case 1: - return a.REFLECT; + return e.REFLECT; case 2: - return a.REPEAT; + return e.REPEAT; default: return null; } }; - a.toNumber = function(b) { - switch(b) { - case a.PAD: + e.toNumber = function(a) { + switch(a) { + case e.PAD: return 0; - case a.REFLECT: + case e.REFLECT: return 1; - case a.REPEAT: + case e.REPEAT: return 2; default: return-1; } }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.PAD = "pad"; - a.REFLECT = "reflect"; - a.REPEAT = "repeat"; - return a; - }(g.ASNative); - f.SpreadMethod = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.InterpolationMethod"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.PAD = "pad"; + e.REFLECT = "reflect"; + e.REPEAT = "repeat"; + return e; + }(a.ASNative); + h.SpreadMethod = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.InterpolationMethod"); } - __extends(a, b); - a.fromNumber = function(b) { - switch(b) { + __extends(e, a); + e.fromNumber = function(a) { + switch(a) { case 0: - return a.RGB; + return e.RGB; case 1: - return a.LINEAR_RGB; + return e.LINEAR_RGB; default: return null; } }; - a.toNumber = function(b) { - switch(b) { - case a.RGB: + e.toNumber = function(a) { + switch(a) { + case e.RGB: return 0; - case a.LINEAR_RGB: + case e.LINEAR_RGB: return 1; default: return-1; } }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.RGB = "rgb"; - a.LINEAR_RGB = "linearRGB"; - return a; - }(g.ASNative); - f.InterpolationMethod = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.RGB = "rgb"; + e.LINEAR_RGB = "linearRGB"; + return e; + }(a.ASNative); + h.InterpolationMethod = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { - function d(a, b, d, f) { - "undefined" === typeof a && (a = null); - "undefined" === typeof b && (b = null); - "undefined" === typeof d && (d = !0); - "undefined" === typeof f && (f = !1); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function c(a, m, l, h) { + void 0 === a && (a = null); + void 0 === m && (m = null); + void 0 === l && (l = !0); + void 0 === h && (h = !1); this.bitmapData = a; - this.matrix = b; - this.repeat = !!d; - this.smooth = !!f; + this.matrix = m; + this.repeat = !!l; + this.smooth = !!h; } - __extends(d, b); - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - return d; - }(b.ASNative); - f.GraphicsBitmapFill = k; - })(f.display || (f.display = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.GraphicsEndFill"); + __extends(c, a); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.ASNative); + c.GraphicsBitmapFill = h; + })(c.display || (c.display = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.GraphicsEndFill"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.GraphicsEndFill = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.AVM2.Runtime.asCoerceString, m = function(b) { - function a(a, b, d, e, f, l, g, m) { - "undefined" === typeof a && (a = "linear"); - "undefined" === typeof b && (b = null); - "undefined" === typeof d && (d = null); - "undefined" === typeof e && (e = null); - "undefined" === typeof f && (f = null); - "undefined" === typeof l && (l = "pad"); - "undefined" === typeof g && (g = "rgb"); - "undefined" === typeof m && (m = 0); - this.type = k(a); - this.colors = b; - this.alphas = d; - this.ratios = e; - this.matrix = f; - this.spreadMethod = l; - this.interpolationMethod = k(g); - this.focalPointRatio = +m; + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.ASNative); + h.GraphicsEndFill = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.AVM2.Runtime.asCoerceString, u = function(a) { + function e(a, e, c, l, k, f, d, b) { + void 0 === a && (a = "linear"); + void 0 === e && (e = null); + void 0 === c && (c = null); + void 0 === l && (l = null); + void 0 === k && (k = null); + void 0 === f && (f = "pad"); + void 0 === d && (d = "rgb"); + void 0 === b && (b = 0); + this.type = s(a); + this.colors = e; + this.alphas = c; + this.ratios = l; + this.matrix = k; + this.spreadMethod = f; + this.interpolationMethod = s(d); + this.focalPointRatio = +b; } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.GraphicsGradientFill = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.AVM2.Runtime.asCoerceString, m = function(b) { - function a(a, b, d) { - "undefined" === typeof a && (a = null); - "undefined" === typeof b && (b = null); - "undefined" === typeof d && (d = "evenOdd"); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.ASNative); + h.GraphicsGradientFill = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.AVM2.Runtime.asCoerceString, u = function(a) { + function e(a, e, c) { + void 0 === a && (a = null); + void 0 === e && (e = null); + void 0 === c && (c = "evenOdd"); this.commands = a; - this.data = b; - this.winding = k(d); + this.data = e; + this.winding = s(c); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.GraphicsPath = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.GraphicsPathCommand"); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.ASNative); + h.GraphicsPath = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.GraphicsPathCommand"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.NO_OP = void 0; - a.MOVE_TO = 1; - a.LINE_TO = 2; - a.CURVE_TO = 3; - a.WIDE_MOVE_TO = 4; - a.WIDE_LINE_TO = 5; - a.CUBIC_CURVE_TO = 6; - return a; - }(g.ASNative); - f.GraphicsPathCommand = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.GraphicsPathWinding"); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.NO_OP = void 0; + e.MOVE_TO = 1; + e.LINE_TO = 2; + e.CURVE_TO = 3; + e.WIDE_MOVE_TO = 4; + e.WIDE_LINE_TO = 5; + e.CUBIC_CURVE_TO = 6; + return e; + }(a.ASNative); + h.GraphicsPathCommand = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.GraphicsPathWinding"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.EVEN_ODD = "evenOdd"; - a.NON_ZERO = "nonZero"; - return a; - }(g.ASNative); - f.GraphicsPathWinding = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.EVEN_ODD = "evenOdd"; + e.NON_ZERO = "nonZero"; + return e; + }(a.ASNative); + h.GraphicsPathWinding = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { - function d(a, b) { - "undefined" === typeof a && (a = 0); - "undefined" === typeof b && (b = 1); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function c(a, m) { + void 0 === a && (a = 0); + void 0 === m && (m = 1); this.color = a >>> 0; - this.alpha = +b; + this.alpha = +m; } - __extends(d, b); - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - return d; - }(b.ASNative); - f.GraphicsSolidFill = k; - })(f.display || (f.display = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.AVM2.Runtime.asCoerceString, m = function(b) { - function a(a, b, d, e, f, l, g) { - "undefined" === typeof a && (a = NaN); - "undefined" === typeof b && (b = !1); - "undefined" === typeof d && (d = "normal"); - "undefined" === typeof e && (e = "none"); - "undefined" === typeof f && (f = "round"); - "undefined" === typeof l && (l = 3); - "undefined" === typeof g && (g = null); + __extends(c, a); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.ASNative); + c.GraphicsSolidFill = h; + })(c.display || (c.display = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.AVM2.Runtime.asCoerceString, u = function(a) { + function e(a, e, c, l, k, f, d) { + void 0 === a && (a = NaN); + void 0 === e && (e = !1); + void 0 === c && (c = "normal"); + void 0 === l && (l = "none"); + void 0 === k && (k = "round"); + void 0 === f && (f = 3); + void 0 === d && (d = null); this.thickness = +a; - this.pixelHinting = !!b; - this.scaleMode = k(d); - this.caps = k(e); - this.joints = k(f); - this.miterLimit = +l; - this.fill = g; + this.pixelHinting = !!e; + this.scaleMode = s(c); + this.caps = s(l); + this.joints = s(k); + this.miterLimit = +f; + this.fill = d; } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.GraphicsStroke = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = function(a) { - function b(a, c, d, f) { - "undefined" === typeof f && (f = "none"); - m(f); - k("Dummy Constructor: public flash.display.GraphicsTrianglePath"); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.ASNative); + h.GraphicsStroke = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = c.AVM2.Runtime.asCoerceString, l = function(a) { + function c(a, e, m, k) { + void 0 === k && (k = "none"); + u(k); + s("public flash.display.GraphicsTrianglePath"); } - __extends(b, a); - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(g.ASNative); - f.GraphicsTrianglePath = d; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -var Shumway$$inline_555 = Shumway || (Shumway = {}), AVM2$$inline_556 = Shumway$$inline_555.AVM2 || (Shumway$$inline_555.AVM2 = {}), AS$$inline_557 = AVM2$$inline_556.AS || (AVM2$$inline_556.AS = {}), flash$$inline_558 = AS$$inline_557.flash || (AS$$inline_557.flash = {}), display$$inline_559 = flash$$inline_558.display || (flash$$inline_558.display = {}), display$$inline_560 = flash$$inline_558.display, flash$$inline_561 = AS$$inline_557.flash, AS$$inline_562 = AVM2$$inline_556.AS, AVM2$$inline_563 = -Shumway$$inline_555.AVM2, Shumway$$inline_565 = Shumway || (Shumway = {}), AVM2$$inline_566 = Shumway$$inline_565.AVM2 || (Shumway$$inline_565.AVM2 = {}), AS$$inline_567 = AVM2$$inline_566.AS || (AVM2$$inline_566.AS = {}), flash$$inline_568 = AS$$inline_567.flash || (AS$$inline_567.flash = {}), display$$inline_569 = flash$$inline_568.display || (flash$$inline_568.display = {}), display$$inline_570 = flash$$inline_568.display, flash$$inline_571 = AS$$inline_567.flash, AS$$inline_572 = AVM2$$inline_566.AS, -AVM2$$inline_573 = Shumway$$inline_565.AVM2, Shumway$$inline_575 = Shumway || (Shumway = {}), AVM2$$inline_576 = Shumway$$inline_575.AVM2 || (Shumway$$inline_575.AVM2 = {}), AS$$inline_577 = AVM2$$inline_576.AS || (AVM2$$inline_576.AS = {}), flash$$inline_578 = AS$$inline_577.flash || (AS$$inline_577.flash = {}), display$$inline_579 = flash$$inline_578.display || (flash$$inline_578.display = {}), display$$inline_580 = flash$$inline_578.display, flash$$inline_581 = AS$$inline_577.flash, AS$$inline_582 = -AVM2$$inline_576.AS, AVM2$$inline_583 = Shumway$$inline_575.AVM2, Shumway$$inline_585 = Shumway || (Shumway = {}), AVM2$$inline_586 = Shumway$$inline_585.AVM2 || (Shumway$$inline_585.AVM2 = {}), AS$$inline_587 = AVM2$$inline_586.AS || (AVM2$$inline_586.AS = {}), flash$$inline_588 = AS$$inline_587.flash || (AS$$inline_587.flash = {}), display$$inline_589 = flash$$inline_588.display || (flash$$inline_588.display = {}), display$$inline_590 = flash$$inline_588.display, flash$$inline_591 = AS$$inline_587.flash, -AS$$inline_592 = AVM2$$inline_586.AS, AVM2$$inline_593 = Shumway$$inline_585.AVM2, Shumway$$inline_595 = Shumway || (Shumway = {}), AVM2$$inline_596 = Shumway$$inline_595.AVM2 || (Shumway$$inline_595.AVM2 = {}), AS$$inline_597 = AVM2$$inline_596.AS || (AVM2$$inline_596.AS = {}), flash$$inline_598 = AS$$inline_597.flash || (AS$$inline_597.flash = {}), display$$inline_599 = flash$$inline_598.display || (flash$$inline_598.display = {}), display$$inline_600 = flash$$inline_598.display, flash$$inline_601 = -AS$$inline_597.flash, AS$$inline_602 = AVM2$$inline_596.AS, AVM2$$inline_603 = Shumway$$inline_595.AVM2; -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(t) { - function s(a, b, c, d) { - a = c - a; - b = d - b; + __extends(c, a); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.ASNative); + h.GraphicsTrianglePath = l; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(s) { + (function(v) { + function p(a, b, d, e) { + a = d - a; + b = e - b; return a * a + b * b; } - function m(a, b, c, d) { - var h = 1 - d; - return a * h * h + 2 * b * h * d + c * d * d; - } - function d(a, b, c) { - var d = (a - b) / (a - 2 * b + c); - return 0 > d ? a : 1 < d ? c : m(a, b, c, d); - } - function a(a, b, c, d, h) { - var e = h * h, f = 1 - h, l = f * f; - return a * f * l + 3 * b * h * l + 3 * c * f * e + d * h * e; - } - function c(b, c, d, h) { - var e = c - b, f; - f = 2 * (d - c); - var l = h - d; - e + l === f && (l *= 1.0001); - var g = 2 * e - f, n = f - 2 * e, n = Math.sqrt(n * n - 4 * e * (e - f + l)); - f = 2 * (e - f + l); - e = (g + n) / f; - g = (g - n) / f; - n = []; - 0 <= e && 1 >= e && n.push(Math.round(a(b, c, d, h, e))); - 0 <= g && 1 >= g && n.push(Math.round(a(b, c, d, h, g))); - return n; + function u(a, b, d, e) { + var c = 1 - e; + return a * c * c + 2 * b * c * e + d * e * e; + } + function l(a, b, d) { + var e = (a - b) / (a - 2 * b + d); + return 0 > e ? a : 1 < e ? d : u(a, b, d, e); + } + function e(a, b, d, e, c) { + var g = c * c, f = 1 - c, k = f * f; + return a * f * k + 3 * b * c * k + 3 * d * f * g + e * c * g; + } + function m(a, b, d, c) { + var g = b - a, f; + f = 2 * (d - b); + var k = c - d; + g + k === f && (k *= 1.0001); + var m = 2 * g - f, l = f - 2 * g, l = Math.sqrt(l * l - 4 * g * (g - f + k)); + f = 2 * (g - f + k); + g = (m + l) / f; + m = (m - l) / f; + l = []; + 0 <= g && 1 >= g && l.push(Math.round(e(a, b, d, c, g))); + 0 <= m && 1 >= m && l.push(Math.round(e(a, b, d, c, m))); + return l; } - function n(a, b, c, d, h, e, f, l, g) { - function n(a) { - return a * (q + a * (u + a * w)) + b - g; + function t(a, b, d, e, c, g, f, k, m) { + function l(a) { + return a * (w + a * (t + a * u)) + b - m; } - function m(b) { + function r(b) { 0 > b ? b = 0 : 1 < b && (b = 1); - return a + b * (r + b * (s + b * x)); + return a + b * (n + b * (s + b * p)); } - function k(a, b, c, d) { - if (!(Math.abs(c - b) <= d)) { - var h = .5 * (b + c); - 0 >= a(b) * a(c) ? (t = b, y = c) : (k(a, b, h, d), k(a, h, c, d)); + function h(a, b, d, e) { + if (!(Math.abs(d - b) <= e)) { + var c = .5 * (b + d); + 0 >= a(b) * a(d) ? (v = b, z = d) : (h(a, b, c, e), h(a, c, d, e)); } } - var r = 3 * (c - a), q = 3 * (d - b), s = 3 * (h - c) - r, u = 3 * (e - d) - q, x = f - a - r - s, w = l - b - q - u, t = 0, y = 1; - k(n, 0, 1, .05); - e = p(t, y, n); - if (1E-5 < Math.abs(n(e))) { + var n = 3 * (d - a), w = 3 * (e - b), s = 3 * (c - d) - n, t = 3 * (g - e) - w, p = f - a - n - s, u = k - b - w - t, v = 0, z = 1; + h(l, 0, 1, .05); + g = q(v, z, l); + if (1E-5 < Math.abs(l(g))) { return[]; } - c = []; - 1 >= e && c.push(m(e)); - d = w; - h = e * d + u; - f = h * h - 4 * d * (e * h + q); + d = []; + 1 >= g && d.push(r(g)); + e = u; + c = g * e + t; + f = c * c - 4 * e * (g * c + w); if (0 > f) { - return c; + return d; } f = Math.sqrt(f); - d = 1 / (d + d); - e = (f - h) * d; - d *= -h - f; - 0 <= e && 1 >= e && c.push(m(e)); - 0 <= d && 1 >= d && c.push(m(d)); - return c; + e = 1 / (e + e); + g = (f - c) * e; + e *= -c - f; + 0 <= g && 1 >= g && d.push(r(g)); + 0 <= e && 1 >= e && d.push(r(e)); + return d; } - function p(a, b, c) { - var d, h, e, f, l, g, n = a; - h = c(a); - if (0 === h) { + function q(a, b, d) { + var e, c, g, f, k, m, l = a; + c = d(a); + if (0 === c) { return a; } - f = c(b); + f = d(b); if (0 === f) { return b; } - if (0 < f * h) { + if (0 < f * c) { return a; } - for (var m = 0, p = 0;50 > p;++p) { - m++; - d = .5 * (b + a); - e = c(d); - if (0 === e) { - break; - } - if (1E-6 > Math.abs(d - a)) { - break; - } - 0 < e * h && (a = b, b = h, h = f, f = b); - b = e - h; - g = f - e; - l = f - h; - if (f * l < 2 * e * b) { - b = d, f = e; + for (var r = 0, h = 0;50 > h;++h) { + r++; + e = .5 * (b + a); + g = d(e); + if (0 === g) { + break; + } + if (1E-6 > Math.abs(e - a)) { + break; + } + 0 < g * c && (a = b, b = c, c = f, f = b); + b = g - c; + m = f - g; + k = f - c; + if (f * k < 2 * g * b) { + b = e, f = g; } else { - f = (d - a) / b; - b = (b - g) / (g * l); - b = a - f * h * (1 - b * e); - f = c(b); - if (0 === f || 1E-6 > Math.abs(b - n)) { + f = (e - a) / b; + b = (b - m) / (m * k); + b = a - f * c * (1 - b * g); + f = d(b); + if (0 === f || 1E-6 > Math.abs(b - l)) { return b; } - n = b; - 0 > f * h || (a = b, h = f, b = d, f = e); + l = b; + 0 > f * c || (a = b, c = f, b = e, f = g); } } - return d; + return e; } - function e(a, b, c, d, h, e) { - return e > b !== d > b && a < (c - h) * (b - e) / (d - e) + h; + function n(a, b, d, e, c, g) { + return g > b !== e > b && a < (d - c) * (b - g) / (e - g) + c; } - var q = b.Debug.notImplemented, l = b.AVM2.Runtime.asCoerceString, u = b.AVM2.Runtime.throwError, w = b.NumberUtilities.clamp, r = b.Bounds, h = b.Debug.assert, x = b.Debug.assertUnreachable, y = f.display.GradientType, G = f.display.SpreadMethod, I = f.display.InterpolationMethod, C = f.display.LineScaleMode, E = f.display.CapsStyle, O = f.display.JointStyle, K = b.ShapeData, F = function(g) { - function p() { - this._id = f.display.DisplayObject.getNextSyncID(); - this._graphicsData = new K; + var k = c.Debug.notImplemented, f = c.AVM2.Runtime.asCoerceString, d = c.AVM2.Runtime.throwError, b = c.NumberUtilities.clamp, g = c.Bounds, r = c.Debug.assert, w = c.Debug.assertUnreachable, z = s.display.GradientType, A = s.display.SpreadMethod, B = s.display.InterpolationMethod, M = s.display.LineScaleMode, N = s.display.CapsStyle, K = s.display.JointStyle, y = c.ShapeData, D = function(a) { + function q() { + this._id = s.display.DisplayObject.getNextSyncID(); + this._graphicsData = new y; this._textures = []; - this._fillBounds = new r(134217728, 134217728, 134217728, 134217728); - this._lineBounds = new r(134217728, 134217728, 134217728, 134217728); + this._fillBounds = new g(134217728, 134217728, 134217728, 134217728); + this._lineBounds = new g(134217728, 134217728, 134217728, 134217728); this._lastX = this._lastY = 0; - this._boundsIncludeLastCoordinates = !1; + this._boundsIncludeLastCoordinates = !0; this._parent = null; this._topLeftStrokeWidth = this._bottomRightStrokeWidth = 0; this._isDirty = !0; } - __extends(p, g); - p.FromData = function(a) { - var b = new f.display.Graphics; - b._graphicsData = K.FromPlainObject(a.shape); + __extends(q, a); + q.FromData = function(a) { + var b = new s.display.Graphics; + b._graphicsData = y.FromPlainObject(a.shape); a.lineBounds && (b._lineBounds.copyFrom(a.lineBounds), b._fillBounds.copyFrom(a.fillBounds || a.lineBounds)); return b; }; - p.prototype.getGraphicsData = function() { + q.prototype.getGraphicsData = function() { return this._graphicsData; }; - p.prototype.getUsedTextures = function() { + q.prototype.getUsedTextures = function() { return this._textures; }; - p.prototype._setStrokeWidth = function(a) { + q.prototype._setStrokeWidth = function(a) { switch(a) { case 1: this._topLeftStrokeWidth = 0; @@ -26289,154 +32723,151 @@ this._bottomRightStrokeWidth = this._topLeftStrokeWidth = a = Math.ceil(.5 * a) | 0; } }; - p.prototype._setParent = function(a) { - h(!this._parent); + q.prototype._setParent = function(a) { + r(!this._parent); this._parent = a; }; - p.prototype._invalidateParent = function() { - h(this._parent, "Graphics instances must have a parent."); + q.prototype._invalidate = function() { + r(this._parent, "Graphics instances must have a parent."); this._parent._invalidateFillAndLineBounds(!0, !0); - this._parent._setDirtyFlags(4194304); - }; - p.prototype._invalidate = function() { - this._invalidateParent(); + this._parent._propagateFlagsUp(536870912); this._isDirty = !0; }; - p.prototype._getContentBounds = function(a) { - "undefined" === typeof a && (a = !0); + q.prototype._getContentBounds = function(a) { + void 0 === a && (a = !0); return a ? this._lineBounds : this._fillBounds; }; - p.prototype.clear = function() { + q.prototype.clear = function() { this._graphicsData.isEmpty() || (this._graphicsData.clear(), this._textures.length = 0, this._fillBounds.setToSentinels(), this._lineBounds.setToSentinels(), this._lastX = this._lastY = 0, this._boundsIncludeLastCoordinates = !1, this._invalidate()); }; - p.prototype.beginFill = function(a, b) { - "undefined" === typeof b && (b = 1); + q.prototype.beginFill = function(a, d) { + void 0 === d && (d = 1); a = a >>> 0 & 16777215; - b = Math.round(255 * w(+b, -1, 1)) | 0; - this._graphicsData.beginFill(a << 8 | b); + d = Math.round(255 * b(+d, -1, 1)) | 0; + this._graphicsData.beginFill(a << 8 | d); }; - p.prototype.beginGradientFill = function(a, b, c, d, h, e, f, l) { - "undefined" === typeof h && (h = null); - "undefined" === typeof e && (e = "pad"); - "undefined" === typeof f && (f = "rgb"); - "undefined" === typeof l && (l = 0); - this._writeGradientStyle(2, a, b, c, d, h, e, f, l, !1); - }; - p.prototype.beginBitmapFill = function(a, b, c, d) { - "undefined" === typeof b && (b = null); - "undefined" === typeof c && (c = !0); - "undefined" === typeof d && (d = !1); - this._writeBitmapStyle(3, a, b, c, d, !1); + q.prototype.beginGradientFill = function(a, b, d, e, c, g, f, k) { + void 0 === c && (c = null); + void 0 === g && (g = "pad"); + void 0 === f && (f = "rgb"); + void 0 === k && (k = 0); + this._writeGradientStyle(2, a, b, d, e, c, g, f, k, !1); + }; + q.prototype.beginBitmapFill = function(a, b, d, e) { + void 0 === b && (b = null); + void 0 === d && (d = !0); + void 0 === e && (e = !1); + this._writeBitmapStyle(3, a, b, d, e, !1); }; - p.prototype.endFill = function() { + q.prototype.endFill = function() { this._graphicsData.endFill(); }; - p.prototype.lineStyle = function(a, b, c, d, h, e, f, g) { - "undefined" === typeof b && (b = 0); - "undefined" === typeof c && (c = 1); - "undefined" === typeof d && (d = !1); - "undefined" === typeof h && (h = "normal"); - "undefined" === typeof e && (e = null); - "undefined" === typeof f && (f = null); - "undefined" === typeof g && (g = 3); + q.prototype.lineStyle = function(a, d, e, c, g, k, m, l) { + void 0 === d && (d = 0); + void 0 === e && (e = 1); + void 0 === c && (c = !1); + void 0 === g && (g = "normal"); + void 0 === k && (k = null); + void 0 === m && (m = null); + void 0 === l && (l = 3); a = +a; - b = b >>> 0 & 16777215; - c = Math.round(255 * w(+c, -1, 1)); - d = !!d; - h = l(h); - e = l(e); - f = l(f); - g = w(+g | 0, 0, 255); - isNaN(a) ? (this._setStrokeWidth(0), this._graphicsData.endLine()) : (a = 20 * w(+a, 0, 255) | 0, this._setStrokeWidth(a), h = C.toNumber(l(h)), 0 > h && (h = C.toNumber(C.NORMAL)), e = E.toNumber(l(e)), 0 > e && (e = E.toNumber(E.ROUND)), f = O.toNumber(l(f)), 0 > f && (f = O.toNumber(O.ROUND)), this._graphicsData.lineStyle(a, b << 8 | c, d, h, e, f, g)); - }; - p.prototype.lineGradientStyle = function(a, b, c, d, h, e, f, l) { - "undefined" === typeof h && (h = null); - "undefined" === typeof e && (e = "pad"); - "undefined" === typeof f && (f = "rgb"); - "undefined" === typeof l && (l = 0); - this._writeGradientStyle(6, a, b, c, d, h, e, f, l, !this._graphicsData.hasLines); - }; - p.prototype.lineBitmapStyle = function(a, b, c, d) { - "undefined" === typeof b && (b = null); - "undefined" === typeof c && (c = !0); - "undefined" === typeof d && (d = !1); - this._writeBitmapStyle(7, a, b, c, d, !this._graphicsData.hasLines); + d = d >>> 0 & 16777215; + e = Math.round(255 * b(+e, -1, 1)); + c = !!c; + g = f(g); + k = f(k); + m = f(m); + l = b(+l | 0, 0, 255); + isNaN(a) ? (this._setStrokeWidth(0), this._graphicsData.endLine()) : (a = 20 * b(+a, 0, 255) | 0, this._setStrokeWidth(a), g = M.toNumber(f(g)), 0 > g && (g = M.toNumber(M.NORMAL)), k = N.toNumber(f(k)), 0 > k && (k = N.toNumber(N.ROUND)), m = K.toNumber(f(m)), 0 > m && (m = K.toNumber(K.ROUND)), this._graphicsData.lineStyle(a, d << 8 | e, c, g, k, m, l)); + }; + q.prototype.lineGradientStyle = function(a, b, d, e, c, g, f, k) { + void 0 === c && (c = null); + void 0 === g && (g = "pad"); + void 0 === f && (f = "rgb"); + void 0 === k && (k = 0); + this._writeGradientStyle(6, a, b, d, e, c, g, f, k, !this._graphicsData.hasLines); + }; + q.prototype.lineBitmapStyle = function(a, b, d, e) { + void 0 === b && (b = null); + void 0 === d && (d = !0); + void 0 === e && (e = !1); + this._writeBitmapStyle(7, a, b, d, e, !this._graphicsData.hasLines); }; - p.prototype.drawRect = function(a, b, c, d) { + q.prototype.drawRect = function(a, b, d, e) { a = 20 * a | 0; b = 20 * b | 0; - c = a + (20 * c | 0); - d = b + (20 * d | 0); + d = a + (20 * d | 0); + e = b + (20 * e | 0); a === this._lastX && b === this._lastY || this._graphicsData.moveTo(a, b); - this._graphicsData.lineTo(c, b); - this._graphicsData.lineTo(c, d); - this._graphicsData.lineTo(a, d); + this._graphicsData.lineTo(d, b); + this._graphicsData.lineTo(d, e); + this._graphicsData.lineTo(a, e); this._graphicsData.lineTo(a, b); - this._extendBoundsByPoint(c, d); + this._extendBoundsByPoint(d, e); this._applyLastCoordinates(a, b); this._invalidate(); }; - p.prototype.drawRoundRect = function(a, b, c, d, h, e) { + q.prototype.drawRoundRect = function(a, b, d, e, c, g) { a = +a; b = +b; - c = +c; d = +d; - h = +h; - if ((e = +e) && h) { - h = h / 2 | 0; - e = e / 2 | 0; - var f = c / 2, l = d / 2; - h > f && (h = f); - e > l && (e = l); - f === h && l === e ? h === e ? this.drawCircle(a + h, b + e, h) : this.drawEllipse(a, b, 2 * h, 2 * e) : (c = a + c, d = b + d, f = a + h, h = c - h, l = b + e, e = d - e, this.moveTo(c, e), this.curveTo(c, d, h, d), this.lineTo(f, d), this.curveTo(a, d, a, e), this.lineTo(a, l), this.curveTo(a, b, f, b), this.lineTo(h, b), this.curveTo(c, b, c, l), this.lineTo(c, e)); + e = +e; + c = +c; + if ((g = +g) && c) { + c = c / 2 | 0; + g = g / 2 | 0; + var f = d / 2, k = e / 2; + c > f && (c = f); + g > k && (g = k); + f === c && k === g ? c === g ? this.drawCircle(a + c, b + g, c) : this.drawEllipse(a, b, 2 * c, 2 * g) : (d = a + d, e = b + e, f = a + c, c = d - c, k = b + g, g = e - g, this.moveTo(d, g), this.curveTo(d, e, c, e), this.lineTo(f, e), this.curveTo(a, e, a, g), this.lineTo(a, k), this.curveTo(a, b, f, b), this.lineTo(c, b), this.curveTo(d, b, d, k), this.lineTo(d, g)); } else { - this.drawRect(a, b, c, d); + this.drawRect(a, b, d, e); } }; - p.prototype.drawRoundRectComplex = function(a, b, c, d, h, e, f, l) { + q.prototype.drawRoundRectComplex = function(a, b, d, e, c, g, f, k) { a = +a; b = +b; - c = +c; d = +d; - h = +h; e = +e; + c = +c; + g = +g; f = +f; - l = +l; - if (h | e | f | l) { - c = a + c; - d = b + d; - var g = a + h; - this.moveTo(c, d - l); - this.curveTo(c, d, c - l, d); - this.lineTo(a + f, d); - this.curveTo(a, d, a, d - f); - this.lineTo(a, b + h); - this.curveTo(a, b, g, b); - this.lineTo(c - e, b); - this.curveTo(c, b, c, b + e); - this.lineTo(c, d - l); + k = +k; + if (c | g | f | k) { + d = a + d; + e = b + e; + var m = a + c; + this.moveTo(d, e - k); + this.curveTo(d, e, d - k, e); + this.lineTo(a + f, e); + this.curveTo(a, e, a, e - f); + this.lineTo(a, b + c); + this.curveTo(a, b, m, b); + this.lineTo(d - g, b); + this.curveTo(d, b, d, b + g); + this.lineTo(d, e - k); } else { - this.drawRect(a, b, c, d); + this.drawRect(a, b, d, e); } }; - p.prototype.drawCircle = function(a, b, c) { - c = +c; - this.drawEllipse(+a - c, +b - c, 2 * c, 2 * c); + q.prototype.drawCircle = function(a, b, d) { + d = +d; + this.drawEllipse(+a - d, +b - d, 2 * d, 2 * d); }; - p.prototype.drawEllipse = function(a, b, c, d) { - c = +c / 2; + q.prototype.drawEllipse = function(a, b, d, e) { d = +d / 2; - a = +a + c; - b = +b + d; - var h = a + c, e = b; - this.moveTo(h, e); - for (var f = 0, l = 1, g = 0, n = 0;4 > n;n++) { - var m = f + Math.PI / 2, f = 4 / 3 * Math.tan((m - f) / 4), p = h - g * f * c, k = e + l * f * d, l = Math.cos(m), g = Math.sin(m), h = a + l * c, e = b + g * d; - this.cubicCurveTo(p, k, h + g * f * c, e - l * f * d, h, e); - f = m; + e = +e / 2; + a = +a + d; + b = +b + e; + var c = a + d, g = b; + this.moveTo(c, g); + for (var f = 0, k = 1, m = 0, l = 0;4 > l;l++) { + var r = f + Math.PI / 2, f = 4 / 3 * Math.tan((r - f) / 4), h = c - m * f * d, n = g + k * f * e, k = Math.cos(r), m = Math.sin(r), c = a + k * d, g = b + m * e; + this.cubicCurveTo(h, n, c + m * f * d, g - k * f * e, c, g); + f = r; } }; - p.prototype.moveTo = function(a, b) { + q.prototype.moveTo = function(a, b) { a = 20 * a | 0; b = 20 * b | 0; this._graphicsData.moveTo(a, b); @@ -26444,47 +32875,47 @@ this._lastY = b; this._boundsIncludeLastCoordinates = !1; }; - p.prototype.lineTo = function(a, b) { + q.prototype.lineTo = function(a, b) { a = 20 * a | 0; b = 20 * b | 0; this._graphicsData.lineTo(a, b); this._applyLastCoordinates(a, b); this._invalidate(); }; - p.prototype.curveTo = function(a, b, c, h) { + q.prototype.curveTo = function(a, b, d, e) { a = 20 * a | 0; b = 20 * b | 0; - c = 20 * c | 0; - h = 20 * h | 0; - this._graphicsData.curveTo(a, b, c, h); - (a < this._lastX || a > c) && this._extendBoundsByX(d(this._lastX, a, c) | 0); - (b < this._lastY || b > h) && this._extendBoundsByY(d(this._lastY, b, h) | 0); - this._applyLastCoordinates(c, h); + d = 20 * d | 0; + e = 20 * e | 0; + this._graphicsData.curveTo(a, b, d, e); + (a < this._lastX || a > d) && this._extendBoundsByX(l(this._lastX, a, d) | 0); + (b < this._lastY || b > e) && this._extendBoundsByY(l(this._lastY, b, e) | 0); + this._applyLastCoordinates(d, e); this._invalidate(); }; - p.prototype.cubicCurveTo = function(a, b, d, h, e, f) { + q.prototype.cubicCurveTo = function(a, b, d, e, c, g) { a = 20 * a | 0; b = 20 * b | 0; d = 20 * d | 0; - h = 20 * h | 0; e = 20 * e | 0; - f = 20 * f | 0; - this._graphicsData.cubicCurveTo(a, b, d, h, e, f); - var l = this._lastX, g = this._lastY; - if (a < l || d < l || a > e || d > e) { - for (a = c(l, a, d, e), d = a.length;d--;) { + c = 20 * c | 0; + g = 20 * g | 0; + this._graphicsData.cubicCurveTo(a, b, d, e, c, g); + var f = this._lastX, k = this._lastY; + if (a < f || d < f || a > c || d > c) { + for (a = m(f, a, d, c), d = a.length;d--;) { this._extendBoundsByX(a[d] | 0); } } - if (b < g || h < g || b > f || h > f) { - for (a = c(g, b, h, f), d = a.length;d--;) { + if (b < k || e < k || b > g || e > g) { + for (a = m(k, b, e, g), d = a.length;d--;) { this._extendBoundsByY(a[d] | 0); } } - this._applyLastCoordinates(e, f); + this._applyLastCoordinates(c, g); this._invalidate(); }; - p.prototype.copyFrom = function(a) { + q.prototype.copyFrom = function(a) { this._graphicsData = a._graphicsData.clone(); this._fillBounds = a._fillBounds.clone(); this._lineBounds = a._lineBounds.clone(); @@ -26494,91 +32925,97 @@ this._boundsIncludeLastCoordinates = a._boundsIncludeLastCoordinates; this._invalidate(); }; - p.prototype.drawPath = function(a, b, c) { - "undefined" === typeof c && (c = "evenOdd"); - l(c); - q("public flash.display.Graphics::drawPath"); - }; - p.prototype.drawTriangles = function(a, b, c, d) { - "undefined" === typeof d && (d = "none"); - l(d); - q("public flash.display.Graphics::drawTriangles"); - }; - p.prototype.drawGraphicsData = function(a) { - q("public flash.display.Graphics::drawGraphicsData"); - }; - p.prototype._containsPoint = function(a, b, c) { - var d = this._graphicsData.hasLines; - if (!(c && d ? this._lineBounds : this._fillBounds).contains(a, b)) { + q.prototype.drawPath = function(a, b, d) { + void 0 === d && (d = "evenOdd"); + f(d); + k("public flash.display.Graphics::drawPath"); + }; + q.prototype.drawTriangles = function(a, b, d, e) { + void 0 === e && (e = "none"); + f(e); + k("public flash.display.Graphics::drawTriangles"); + }; + q.prototype.drawGraphicsData = function(a) { + k("public flash.display.Graphics::drawGraphicsData"); + }; + q.prototype._containsPoint = function(a, b, d, e) { + var c = this._graphicsData.hasLines; + if (!e && !(d && c ? this._lineBounds : this._fillBounds).contains(a, b)) { return!1; } - var e = !1; - this._graphicsData.hasFills ? e = this._fillContainsPoint(a, b) : h(d, "Can't have non-empty bounds without line or fill set."); - !e && c && (e = this._linesContainsPoint(a, b)); - return e; + var g = !1; + this._graphicsData.hasFills ? g = this._fillContainsPoint(a, b, e) : r(c, "Can't have non-empty bounds without line or fill set."); + !g && d && (g = this._linesContainsPoint(a, b, e)); + return g; }; - p.prototype._fillContainsPoint = function(a, b) { - for (var c = this._graphicsData, d = c.commands, f = c.commandsPosition, l = c.coordinates, g = 0, p = 0, k = 0, r = 0, q = 0, s, u, w = !1, t = !1, y = 0, G = 0, C = !1, I = 0;I < f;I++) { - s = d[I]; - switch(s) { + q.prototype._fillContainsPoint = function(a, b, d) { + for (var e = this._graphicsData, c = e.commands, g = e.commandsPosition, f = e.coordinates, k = e.morphCoordinates, m = 0, l = 0, h = 0, q = 0, s = 0, p, v, z = !1, A = !1, B = 0, M = 0, N = !1, y = 0;y < g;y++) { + p = c[y]; + switch(p) { case 9: - h(g <= c.coordinatesPosition - 2); - w && t && e(a, b, p, k, y, G) && (C = !C); - w = !0; - p = y = l[g++]; - k = G = l[g++]; + r(m <= e.coordinatesPosition - 2); + z && A && n(a, b, l, h, B, M) && (N = !N); + z = !0; + l = B = f[m++]; + h = M = f[m++]; + d && (l = B += (k[m - 2] - B) * d, h = M += (k[m - 2] - M) * d); continue; case 10: - h(g <= c.coordinatesPosition - 2); - r = l[g++]; - q = l[g++]; - t && e(a, b, p, k, r, q) && (C = !C); + r(m <= e.coordinatesPosition - 2); + q = f[m++]; + s = f[m++]; + d && (q += (k[m - 2] - q) * d, s += (k[m - 1] - s) * d); + A && n(a, b, l, h, q, s) && (N = !N); break; case 11: - h(g <= c.coordinatesPosition - 4); - s = l[g++]; - u = l[g++]; - var r = l[g++], q = l[g++], E; - if (E = t) { - if (u > b === k > b && q > b === k > b) { - E = !1; + r(m <= e.coordinatesPosition - 4); + p = f[m++]; + v = f[m++]; + q = f[m++]; + s = f[m++]; + d && (p += (k[m - 4] - p) * d, v += (k[m - 3] - v) * d, q += (k[m - 2] - q) * d, s += (k[m - 1] - s) * d); + var K; + if (K = A) { + if (v > b === h > b && s > b === h > b) { + K = !1; } else { - if (p >= a && s >= a && r >= a) { - E = !0; + if (l >= a && p >= a && q >= a) { + K = !0; } else { - E = k - 2 * u + q; - u = 2 * (u - k); - var O = u * u - 4 * E * (k - b); - 0 > O ? E = !1 : (O = Math.sqrt(O), E = 1 / (E + E), k = (O - u) * E, u = (-u - O) * E, E = !1, 0 <= k && 1 >= k && m(p, s, r, k) > a && (E = !E), 0 <= u && 1 >= u && m(p, s, r, u) > a && (E = !E)); + K = h - 2 * v + s; + v = 2 * (v - h); + var D = v * v - 4 * K * (h - b); + 0 > D ? K = !1 : (D = Math.sqrt(D), K = 1 / (K + K), h = (D - v) * K, v = (-v - D) * K, K = !1, 0 <= h && 1 >= h && u(l, p, q, h) > a && (K = !K), 0 <= v && 1 >= v && u(l, p, q, v) > a && (K = !K)); } } } - E && (C = !C); + K && (N = !N); break; case 12: - h(g <= c.coordinatesPosition - 6); - s = l[g++]; - u = l[g++]; - var O = l[g++], K = l[g++], r = l[g++], q = l[g++]; - if (E = t) { - E = a; - var F = k > b; - if (u > b === F && K > b === F && q > b === F) { - E = !1; + r(m <= e.coordinatesPosition - 6); + p = f[m++]; + v = f[m++]; + var D = f[m++], L = f[m++], q = f[m++], s = f[m++]; + d && (p += (k[m - 6] - p) * d, v += (k[m - 5] - v) * d, D += (k[m - 4] - D) * d, L += (k[m - 3] - L) * d, q += (k[m - 2] - q) * d, s += (k[m - 1] - s) * d); + if (K = A) { + K = a; + var H = h > b; + if (v > b === H && L > b === H && s > b === H) { + K = !1; } else { - if (p < E && s < E && O < E && r < E) { - E = !1; + if (l < K && p < K && D < K && q < K) { + K = !1; } else { - F = !1; - p = n(p, k, s, u, O, K, r, q, b); - for (s = p.length;s;s--) { - p[s] >= E && (F = !F); + H = !1; + l = t(l, h, p, v, D, L, q, s, b); + for (p = l.length;p;p--) { + l[p] >= K && (H = !H); } - E = F; + K = H; } } } - E && (C = !C); + K && (N = !N); break; case 1: ; @@ -26587,15 +33024,15 @@ case 3: ; case 4: - w && t && e(a, b, p, k, y, G) && (C = !C); - if (C) { + z && A && n(a, b, l, h, B, M) && (N = !N); + if (N) { return!0; } - w = !1; - t = 4 !== s; + z = !1; + A = 4 !== p; break; case 5: - g++; + m++; break; case 6: ; @@ -26604,121 +33041,131 @@ case 8: break; default: - x("Invalid command " + s + " encountered at index" + (I - 1) + " of " + f); + w("Invalid command " + p + " encountered at index" + (y - 1) + " of " + g); } - p = r; - k = q; + l = q; + h = s; } - h(I === f); - h(g === c.coordinatesPosition); - w && t && e(a, b, p, k, y, G) && (C = !C); - return C; - }; - p.prototype._linesContainsPoint = function(b, e) { - for (var f = this._graphicsData, l = f.commands, g = f.commandsPosition, n = f.coordinates, p = 0, k = 0, r = 0, q = 0, u = 0, w, t, y, G, C, I = 0, E = 0, O = 0, K = 0, F = 0, J = 0, A = E = 0;A < g;A++) { - w = l[A]; - switch(w) { + r(y === g); + r(m === e.coordinatesPosition); + z && A && n(a, b, l, h, B, M) && (N = !N); + return N; + }; + q.prototype._linesContainsPoint = function(a, b, d) { + for (var c = this._graphicsData, g = c.commands, f = c.commandsPosition, k = c.coordinates, h = c.morphCoordinates, n = 0, q = 0, s = 0, t = 0, v = 0, z, A, B, M, N, y = 0, K = 0, D = 0, L = 0, H = 0, aa = 0, ia = K = 0;ia < f;ia++) { + z = g[ia]; + switch(z) { case 9: - h(p <= f.coordinatesPosition - 2); - k = n[p++]; - r = n[p++]; + r(n <= c.coordinatesPosition - 2); + q = k[n++]; + s = k[n++]; + d && (q += (h[n - 2] - q) * d, s += (h[n - 1] - s) * d); continue; case 10: - h(p <= f.coordinatesPosition - 2); - if (0 === I) { - p++; - k = n[p++]; + r(n <= c.coordinatesPosition - 2); + if (0 === y) { + q = k[n++]; + s = k[n++]; + d && (q += (h[n - 2] - q) * d, s += (h[n - 1] - s) * d); continue; } - q = n[p++]; - u = n[p++]; - if (k === q && r === u) { + t = k[n++]; + v = k[n++]; + d && (t += (h[n - 2] - t) * d, v += (h[n - 1] - v) * d); + if (q === t && s === v) { break; } - if (F < k && F < q || K > k && K > q || E < r && E < u || J > r && J > u) { + if (H < q && H < t || L > q && L > t || K < s && K < v || aa > s && aa > v) { break; } - if (q === k || u === r) { + if (t === q || v === s) { return!0; } - C = ((b - k) * (q - k) + (e - r) * (u - r)) / s(k, r, q, u); - if (0 > C) { - if (s(b, e, k, r) <= O) { + N = ((a - q) * (t - q) + (b - s) * (v - s)) / p(q, s, t, v); + if (0 > N) { + if (p(a, b, q, s) <= D) { return!0; } break; } - if (1 < C) { - if (s(b, e, q, u) <= O) { + if (1 < N) { + if (p(a, b, t, v) <= D) { return!0; } break; } - if (s(b, e, k + C * (q - k), r + C * (u - r)) <= O) { + if (p(a, b, q + N * (t - q), s + N * (v - s)) <= D) { return!0; } break; case 11: - h(p <= f.coordinatesPosition - 4); - if (0 === I) { - p += 2; - p++; - k = n[p++]; + r(n <= c.coordinatesPosition - 4); + if (0 === y) { + n += 2; + q = k[n++]; + s = k[n++]; + d && (q += (h[n - 2] - q) * d, s += (h[n - 1] - s) * d); continue; } - w = n[p++]; - t = n[p++]; - var q = n[p++], u = n[p++], W = d(k, w, q); - if (F < k && F < W && F < q || K > k && K > W && K > q) { + z = k[n++]; + A = k[n++]; + t = k[n++]; + v = k[n++]; + d && (z += (h[n - 4] - z) * d, A += (h[n - 3] - A) * d, t += (h[n - 2] - t) * d, v += (h[n - 1] - v) * d); + var T = l(q, z, t); + if (H < q && H < T && H < t || L > q && L > T && L > t) { break; } - W = d(r, t, u); - if (E < r && E < W && E < u || J > r && J > W && J > u) { + T = l(s, A, v); + if (K < s && K < T && K < v || aa > s && aa > T && aa > v) { break; } - for (C = 0;1 > C;C += .02) { - if (y = m(k, w, q, C), !(y < K || y > F) && (G = m(r, t, u, C), !(G < J || G > E) && (b - y) * (b - y) + (e - G) * (e - G) < O)) { + for (N = 0;1 > N;N += .02) { + if (B = u(q, z, t, N), !(B < L || B > H) && (M = u(s, A, v, N), !(M < aa || M > K) && (a - B) * (a - B) + (b - M) * (b - M) < D)) { return!0; } } break; case 12: - h(p <= f.coordinatesPosition - 6); - if (0 === I) { - p += 4; - p++; - k = n[p++]; + r(n <= c.coordinatesPosition - 6); + if (0 === y) { + n += 4; + n++; + q = k[n++]; + d && (q += (h[n - 2] - q) * d, s += (h[n - 1] - s) * d); continue; } - w = n[p++]; - t = n[p++]; - var W = n[p++], ha = n[p++], q = n[p++], u = n[p++]; - for (y = c(k, w, W, q);2 > y.length;) { - y.push(q); + z = k[n++]; + A = k[n++]; + var T = k[n++], da = k[n++], t = k[n++], v = k[n++]; + d && (z += (h[n - 6] - z) * d, A += (h[n - 5] - A) * d, T += (h[n - 4] - T) * d, da += (h[n - 3] - da) * d, t += (h[n - 2] - t) * d, v += (h[n - 1] - v) * d); + for (B = m(q, z, T, t);2 > B.length;) { + B.push(t); } - if (F < k && F < q && F < y[0] && F < y[1] || K > k && K > q && K > y[0] && K > y[1]) { + if (H < q && H < t && H < B[0] && H < B[1] || L > q && L > t && L > B[0] && L > B[1]) { break; } - for (y = c(r, t, ha, u);2 > y.length;) { - y.push(u); + for (B = m(s, A, da, v);2 > B.length;) { + B.push(v); } - if (E < r && E < u && E < y[0] && E < y[1] || J > r && J > u && J > y[0] && J > y[1]) { + if (K < s && K < v && K < B[0] && K < B[1] || aa > s && aa > v && aa > B[0] && aa > B[1]) { break; } - for (C = 0;1 > C;C += .02) { - if (y = a(k, w, W, q, C), !(y < K || y > F) && (G = a(r, t, ha, u, C), !(G < J || G > E) && (b - y) * (b - y) + (e - G) * (e - G) < O)) { + for (N = 0;1 > N;N += .02) { + if (B = e(q, z, T, t, N), !(B < L || B > H) && (M = e(s, A, da, v, N), !(M < aa || M > K) && (a - B) * (a - B) + (b - M) * (b - M) < D)) { return!0; } } break; case 5: - I = n[p++]; - E = I >> 2; - O = E * E; - K = b - E; - F = b + E; - J = e - E; - E = e + E; + y = k[n++]; + d && (y += (h[n - 1] - y) * d); + K = y >> 2; + D = K * K; + L = a - K; + H = a + K; + aa = b - K; + K = b + K; break; case 1: ; @@ -26735,236 +33182,330 @@ case 8: break; default: - x("Invalid command " + w + " encountered at index" + (A - 1) + " of " + g); + w("Invalid command " + z + " encountered at index" + (ia - 1) + " of " + f); } - k = q; - r = u; + q = t; + s = v; } - h(A === g); - h(p === f.coordinatesPosition); + r(ia === f); + r(n === c.coordinatesPosition); return!1; }; - p.prototype._writeBitmapStyle = function(a, c, d, h, e, l) { - b.isNullOrUndefined(c) ? u("TypeError", k.Errors.NullPointerError, "bitmap") : f.display.BitmapData.isType(c) || u("TypeError", k.Errors.CheckTypeFailedError, "bitmap", "flash.display.BitmapData"); - b.isNullOrUndefined(d) ? d = f.geom.Matrix.FROZEN_IDENTITY_MATRIX : f.geom.Matrix.isType(d) || u("TypeError", k.Errors.CheckTypeFailedError, "matrix", "flash.geom.Matrix"); - h = !!h; - e = !!e; - l || (l = this._textures.length, this._textures.push(c), this._graphicsData.beginBitmap(a, l, d, h, e)); - }; - p.prototype._writeGradientStyle = function(a, c, d, h, e, g, n, m, p, r) { - b.isNullOrUndefined(c) && u("TypeError", k.Errors.NullPointerError, "type"); - c = y.toNumber(l(c)); - 0 > c && u("ArgumentError", k.Errors.InvalidEnumError, "type"); - b.isNullOrUndefined(d) && u("TypeError", k.Errors.NullPointerError, "colors"); - d instanceof Array || u("TypeError", k.Errors.CheckTypeFailedError, "colors", "Array"); - h instanceof Array || u("TypeError", k.Errors.CheckTypeFailedError, "alphas", "Array"); - b.isNullOrUndefined(h) && u("TypeError", k.Errors.NullPointerError, "alphas"); - e instanceof Array || u("TypeError", k.Errors.CheckTypeFailedError, "ratios", "Array"); - b.isNullOrUndefined(e) && u("TypeError", k.Errors.NullPointerError, "ratios"); - var q = [], s = [], x = d.length, t = x === h.length && x === e.length; - if (t) { - for (var C = 0;C < x;C++) { - var E = +e[C]; - if (255 < E || 0 > E) { - t = !1; + q.prototype._writeBitmapStyle = function(a, b, e, g, f, k) { + c.isNullOrUndefined(b) ? d("TypeError", h.Errors.NullPointerError, "bitmap") : s.display.BitmapData.isType(b) || d("TypeError", h.Errors.CheckTypeFailedError, "bitmap", "flash.display.BitmapData"); + c.isNullOrUndefined(e) ? e = s.geom.Matrix.FROZEN_IDENTITY_MATRIX : s.geom.Matrix.isType(e) || d("TypeError", h.Errors.CheckTypeFailedError, "matrix", "flash.geom.Matrix"); + g = !!g; + f = !!f; + k || (k = this._textures.length, this._textures.push(b), this._graphicsData.beginBitmap(a, k, e, g, f)); + }; + q.prototype._writeGradientStyle = function(a, e, g, k, m, l, r, n, q, w) { + c.isNullOrUndefined(e) && d("TypeError", h.Errors.NullPointerError, "type"); + e = z.toNumber(f(e)); + 0 > e && d("ArgumentError", h.Errors.InvalidEnumError, "type"); + c.isNullOrUndefined(g) && d("TypeError", h.Errors.NullPointerError, "colors"); + g instanceof Array || d("TypeError", h.Errors.CheckTypeFailedError, "colors", "Array"); + k instanceof Array || d("TypeError", h.Errors.CheckTypeFailedError, "alphas", "Array"); + c.isNullOrUndefined(k) && d("TypeError", h.Errors.NullPointerError, "alphas"); + m instanceof Array || d("TypeError", h.Errors.CheckTypeFailedError, "ratios", "Array"); + c.isNullOrUndefined(m) && d("TypeError", h.Errors.NullPointerError, "ratios"); + var t = [], p = [], u = g.length, v = u === k.length && u === m.length; + if (v) { + for (var M = 0;M < u;M++) { + var N = +m[M]; + if (255 < N || 0 > N) { + v = !1; break; } - q[C] = d[C] << 8 & 4294967040 | 255 * w(+h[C], 0, 1); - s[C] = E; + t[M] = g[M] << 8 & 4294967040 | 255 * b(+k[M], 0, 1); + p[M] = N; } } - t && (b.isNullOrUndefined(g) ? g = f.geom.Matrix.FROZEN_IDENTITY_MATRIX : f.geom.Matrix.isType(g) || u("TypeError", k.Errors.CheckTypeFailedError, "matrix", "flash.geom.Matrix"), r || (d = G.toNumber(l(n)), 0 > d && (d = G.toNumber(G.PAD)), m = I.toNumber(l(m)), 0 > m && (m = I.toNumber(I.RGB)), p = w(+p, -1, 1) / 2 * 255 | 0, this._graphicsData.beginGradient(a, q, s, c, g, d, m, p))); + v && (c.isNullOrUndefined(l) ? l = s.geom.Matrix.FROZEN_IDENTITY_MATRIX : s.geom.Matrix.isType(l) || d("TypeError", h.Errors.CheckTypeFailedError, "matrix", "flash.geom.Matrix"), w || (g = A.toNumber(f(r)), 0 > g && (g = A.toNumber(A.PAD)), n = B.toNumber(f(n)), 0 > n && (n = B.toNumber(B.RGB)), q = b(+q, -1, 1) / 2 * 255 | 0, this._graphicsData.beginGradient(a, t, p, e, l, g, n, q))); }; - p.prototype._extendBoundsByPoint = function(a, b) { + q.prototype._extendBoundsByPoint = function(a, b) { this._extendBoundsByX(a); this._extendBoundsByY(b); }; - p.prototype._extendBoundsByX = function(a) { + q.prototype._extendBoundsByX = function(a) { this._fillBounds.extendByX(a); var b = this._lineBounds; 134217728 === b.xMin ? (b.xMin = a - this._topLeftStrokeWidth, b.xMax = a + this._bottomRightStrokeWidth) : (b.xMin = Math.min(a - this._topLeftStrokeWidth, b.xMin), b.xMax = Math.max(a + this._bottomRightStrokeWidth, b.xMax)); }; - p.prototype._extendBoundsByY = function(a) { + q.prototype._extendBoundsByY = function(a) { this._fillBounds.extendByY(a); var b = this._lineBounds; 134217728 === b.yMin ? (b.yMin = a - this._topLeftStrokeWidth, b.yMax = a + this._bottomRightStrokeWidth) : (b.yMin = Math.min(a - this._topLeftStrokeWidth, b.yMin), b.yMax = Math.max(a + this._bottomRightStrokeWidth, b.yMax)); }; - p.prototype._applyLastCoordinates = function(a, b) { + q.prototype._applyLastCoordinates = function(a, b) { this._boundsIncludeLastCoordinates || this._extendBoundsByPoint(this._lastX, this._lastY); this._boundsIncludeLastCoordinates = !0; this._lastX = a; this._lastY = b; this._extendBoundsByPoint(a, b); }; - p.classInitializer = null; - p.initializer = null; - p.classSymbols = null; - p.instanceSymbols = null; - return p; - }(g.ASNative); - t.Graphics = F; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(k) { - var s = b.Debug.assert, m = b.Debug.notImplemented, d = function(a) { - function b() { - k.DisplayObjectContainer.instanceConstructorNoInitialize.call(this); + q.classInitializer = null; + q.initializer = null; + q.classSymbols = null; + q.instanceSymbols = null; + return q; + }(a.ASNative); + v.Graphics = D; + })(s.display || (s.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = c.Timeline, l = c.NumberUtilities.clamp, e; + (function(a) { + a[a.Inactive = 0] = "Inactive"; + a[a.LockToPointer = 1] = "LockToPointer"; + a[a.PreserveDistance = 2] = "PreserveDistance"; + })(e || (e = {})); + e = function(e) { + function t() { + h.DisplayObjectContainer.instanceConstructorNoInitialize.call(this); this._constructChildren(); } - __extends(b, a); - b.prototype._initializeChildren = function(a) { - for (var b in a.stateAtDepth) { - var c = a.stateAtDepth[b]; - if (c) { - var d = k.DisplayObject.createAnimatedDisplayObject(c, !1); - this.addTimelineObjectAtDepth(d, c.depth); - c.symbol.isAVM1Object && this._initAvm1Bindings(d, c); - } - } + __extends(t, e); + t.prototype._addFrame = function(a) { + var e = this._symbol.frames; + e.push(a); + 1 === e.length && this._initializeChildren(a); }; - b.prototype._initAvm1Bindings = function(a, b) { - var c = g.avm1lib.getAVM1Object(a); - s(c); - b.variableName && c.asSetPublicProperty("variable", b.variableName); - var d = b.events; - if (d) { - for (var f = [], m = 0;m < d.length;m++) { - for (var k = d[m], r = k.eventNames, k = k.handler.bind(a), h = 0;h < r.length;h++) { - var x = r[h], t = a; - if ("mouseDown" === x || "mouseUp" === x || "mouseMove" === x) { - t = a.stage; + t.prototype._initializeChildren = function(a) { + a.controlTags && this._processControlTags(a.controlTags, !1); + }; + t.prototype._processControlTags = function(a, e) { + if (e) { + for (var k = this._children.slice(), f = 0;f < k.length;f++) { + var d = k[f]; + if (!(0 > d._depth)) { + for (var b = null, g = 0;g < a.length;g++) { + if (a[g].depth === d._depth) { + b = a[g]; + break; + } } - t.addEventListener(x, k, !1); - f.push({eventName:x, fn:k, target:t}); + b && d._symbol.id === b.symbolId && d._ratio === (b.ratio | 0) || this._removeAnimatedChild(d); } } - 0 < f.length && a.addEventListener("removed", function(a) { - for (var b = 0;b < a.length;b++) { - a[b].target.removeEventListener(a[b].eventName, a[b].fn, !1); - } - }.bind(a, f), !1); } - b.name && (d = g.avm1lib.getAVM1Object(this), void 0 === d.asGetPublicProperty(b.name) && d.asSetPublicProperty(b.name, c)); + k = this._symbol.loaderInfo; + for (f = 0;f < a.length;f++) { + switch(d = a[f], b = void 0 === d.tagCode ? d : k._file.getParsedTag(d), b.code) { + case 5: + ; + case 28: + (d = this.getTimelineObjectAtDepth(b.depth | 0)) && this._removeAnimatedChild(d); + break; + case 4: + ; + case 26: + ; + case 70: + var g = b, m = g.depth, d = this.getTimelineObjectAtDepth(m), l = -1 < g.symbolId; + if (g.flags & 1) { + if (!d) { + break; + } + } else { + if (!l || d && (!e || !l)) { + c.Debug.warning("Warning: Failed to place object at depth " + m + "."); + break; + } + } + var h = null; + if (l && (h = k.getSymbolById(g.symbolId), !h)) { + break; + } + d ? (h && !h.dynamic && d._setStaticContentFromSymbol(h), d._hasFlags(4096) && d._animate(b)) : (d = this.createAnimatedDisplayObject(h, g, !1), this.addTimelineObjectAtDepth(d, m), h.isAVM1Object && c.AVM1.Lib.initializeAVM1Object(d, h.avm1Context, g)); + } + } + }; + t.prototype._removeAnimatedChild = function(a) { + this.removeChild(a); + if (a._name) { + var e = c.AVM2.ABC.Multiname.getPublicQualifiedName(a._name); + this[e] === a && (this[e] = null); + } }; - b.prototype._canHaveGraphics = function() { + t.prototype._canHaveGraphics = function() { return!0; }; - b.prototype._getGraphics = function() { + t.prototype._getGraphics = function() { return this._graphics; }; - Object.defineProperty(b.prototype, "graphics", {get:function() { + Object.defineProperty(t.prototype, "graphics", {get:function() { return this._ensureGraphics(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "buttonMode", {get:function() { + Object.defineProperty(t.prototype, "buttonMode", {get:function() { return this._buttonMode; }, set:function(a) { this._buttonMode = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "dropTarget", {get:function() { - m("public flash.display.Sprite::get dropTarget"); + Object.defineProperty(t.prototype, "dropTarget", {get:function() { + return this._dropTarget; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "hitArea", {get:function() { + Object.defineProperty(t.prototype, "hitArea", {get:function() { return this._hitArea; }, set:function(a) { this._hitArea !== a && (a && a._hitTarget && (a._hitTarget._hitArea = null), this._hitArea = a) && (a._hitTarget = this); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "useHandCursor", {get:function() { + Object.defineProperty(t.prototype, "useHandCursor", {get:function() { return this._useHandCursor; }, set:function(a) { this._useHandCursor = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "soundTransform", {get:function() { - m("public flash.display.Sprite::get soundTransform"); + Object.defineProperty(t.prototype, "soundTransform", {get:function() { + p("public flash.display.Sprite::get soundTransform"); }, set:function(a) { - m("public flash.display.Sprite::set soundTransform"); + p("public flash.display.Sprite::set soundTransform"); }, enumerable:!0, configurable:!0}); - b.prototype.startDrag = function(a, b) { - m("public flash.display.Sprite::startDrag"); + t.prototype.startDrag = function(e, c) { + void 0 === e && (e = !1); + void 0 === c && (c = null); + if (e) { + this._dragMode = 1; + } else { + this._dragMode = 2; + var k = this._getLocalMousePosition(); + this._dragDeltaX = this.x - k.x; + this._dragDeltaY = this.y - k.y; + } + this._dragBounds = c; + a.ui.Mouse.draggableObject = this; }; - b.prototype.stopDrag = function() { - m("public flash.display.Sprite::stopDrag"); + t.prototype.stopDrag = function() { + a.ui.Mouse.draggableObject === this && (a.ui.Mouse.draggableObject = null, this._dragDeltaY = this._dragDeltaX = this._dragMode = 0, this._dragBounds = null); + }; + t.prototype._updateDragState = function(a) { + void 0 === a && (a = null); + var e = this._getLocalMousePosition(), c = e.x, e = e.y; + 2 === this._dragMode && (c += this._dragDeltaX, e += this._dragDeltaY); + if (this._dragBounds) { + var f = this._dragBounds, c = l(c, f.left, f.right), e = l(e, f.top, f.bottom) + } + this.x = c; + this.y = e; + this._dropTarget = a; }; - b.prototype.startTouchDrag = function(a, b, c) { - m("public flash.display.Sprite::startTouchDrag"); + t.prototype.startTouchDrag = function(a, e, c) { + p("public flash.display.Sprite::startTouchDrag"); }; - b.prototype.stopTouchDrag = function(a) { - m("public flash.display.Sprite::stopTouchDrag"); + t.prototype.stopTouchDrag = function(a) { + p("public flash.display.Sprite::stopTouchDrag"); }; - b.prototype._containsPoint = function(b, c, d, f, l, g) { - return 3 === l && this._hitArea && this._mouseEnabled ? (b = this._hitArea._containsGlobalPoint(b, c, 2, g), 2 === b && (s(0 === g.length), g.push(this)), b) : a.prototype._containsPoint.call(this, b, c, d, f, l, g); + t.prototype._containsPoint = function(a, e, c, f, d, b) { + if (!(5 === d && 0 < this._dragMode)) { + var g = this._boundsAndMaskContainPoint(a, e, c, f, d); + !g && 3 === d && this._hitArea && this._mouseEnabled && (g = this._hitArea._getInvertedConcatenatedMatrix(), g = this._hitArea._boundsAndMaskContainPoint(a, e, g.transformX(a, e), g.transformY(a, e), d)); + return 0 === g || 2 > d ? g : this._containsPointImpl(a, e, c, f, d, b, !0); + } }; - b.prototype._containsPointDirectly = function(a, b) { - var c = this._getGraphics(); - return!!c && c._containsPoint(a, b, !0); + t.prototype._containsPointDirectly = function(a, e, c, f) { + if (this._hitArea) { + return!!this._hitArea._containsGlobalPoint(c, f, 2, null); + } + c = this._getGraphics(); + return!!c && c._containsPoint(a, e, !0, 0); }; - b.classInitializer = null; - b.initializer = function(a) { + t.classInitializer = null; + t.initializer = function(a) { this._graphics = null; this._buttonMode = !1; this._hitArea = this._dropTarget = null; this._useHandCursor = !0; - this._hitTarget = null; - a && (a.isRoot && (this._root = this), a.numFrames && (s(1 <= a.frames.length, "Sprites have at least one frame."), a = a.frames[0], s(a, "Initial frame is not defined."), this._initializeChildren(a))); + this._dragDeltaY = this._dragDeltaX = this._dragMode = 0; + this._hitTarget = this._dragBounds = null; + a && (a.isRoot && (this._root = this), a.numFrames && 0 < a.frames.length && this._initializeChildren(a.frames[0])); }; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(f.display.DisplayObjectContainer); - k.Sprite = d; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(t) { - var s = b.Debug.assert, m = b.Debug.assertUnreachable, d = b.AVM2.Runtime.asCoerceString, a = b.AVM2.Runtime.throwError, c = b.Telemetry, n = b.AVM2.ABC.Multiname; + t.classSymbols = null; + t.instanceSymbols = null; + return t; + }(a.display.DisplayObjectContainer); + h.Sprite = e; + u = function(e) { + function c(l, h) { + e.call(this, l, a.display.MovieClip, !0); + this.numFrames = 1; + this.frames = []; + this.labels = []; + this.frameScripts = []; + this.loaderInfo = h; + } + __extends(c, e); + c.FromData = function(e, m) { + var k = new c(e, m); + k.numFrames = e.frameCount; + m.actionScriptVersion === h.ActionScriptVersion.ACTIONSCRIPT2 && (k.isAVM1Object = !0, k.avm1Context = m._avm1Context); + k.frameScripts = []; + for (var f = e.frames, d = 0;d < f.length;d++) { + var b = m.getFrame(e, d), g = b.actionBlocks; + if (g) { + for (var l = 0;l < g.length;l++) { + k.frameScripts.push(d), k.frameScripts.push(g[l]); + } + } + b.labelName && k.labels.push(new a.display.FrameLabel(b.labelName, d + 1)); + k.frames.push(b); + } + return k; + }; + return c; + }(u.DisplaySymbol); + h.SpriteSymbol = u; + })(a.display || (a.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(v) { + var p = c.Debug.assert, u = c.Debug.assertUnreachable, l = c.AVM2.Runtime.asCoerceString, e = c.AVM2.Runtime.throwError, m = c.Telemetry, t = a.events; (function(a) { a[a.SWF1 = 1] = "SWF1"; - a[a.SWF9 = 2] = "SWF9"; - a[a.SWF10 = 3] = "SWF10"; - })(t.FrameNavigationModel || (t.FrameNavigationModel = {})); - var p = function() { - function a(b) { - this._mc = b; + a[a.SWF9 = 9] = "SWF9"; + a[a.SWF10 = 10] = "SWF10"; + })(v.FrameNavigationModel || (v.FrameNavigationModel = {})); + var q = function() { + function a(e) { + this._mc = e; this._soundStream = this._startSoundRegistrations = null; } - a.prototype.registerStartSounds = function(a, b) { + a.prototype.registerStartSounds = function(a, d) { null === this._startSoundRegistrations && (this._startSoundRegistrations = {}); - this._startSoundRegistrations[a] = b; + this._startSoundRegistrations[a] = d; }; a.prototype.initSoundStream = function(a) { - this._soundStream = new t.MovieClipSoundStream(a, this._mc); + this._soundStream = new v.MovieClipSoundStream(a, this._mc); }; - a.prototype.addSoundStreamBlock = function(a, b) { - this._soundStream.appendBlock(a, b); + a.prototype.addSoundStreamBlock = function(a, d) { + this._soundStream && this._soundStream.appendBlock(a, d); }; a.prototype._startSounds = function(a) { if (a = this._startSoundRegistrations[a]) { - for (var b = this._soundClips || (this._soundClips = {}), c = this._mc.loaderInfo, d = 0;d < a.length;d++) { - var h = a[d], e = h.soundId, h = h.soundInfo, f = b[e]; - if (!f) { - var g = c.getSymbolById(e); - if (!g) { + for (var d = this._soundClips || (this._soundClips = {}), b = this._mc.loaderInfo, e = 0;e < a.length;e++) { + var c = a[e], k = c.soundId, c = c.soundInfo, m = d[k]; + if (!m) { + var l = b.getSymbolById(k); + if (!l) { continue; } - f = g.symbolClass; - g = f.initializeFrom(g); - f.instanceConstructorNoInitialize.call(g); - b[e] = f = {object:g}; + m = l.symbolClass; + l = m.initializeFrom(l); + m.instanceConstructorNoInitialize.call(l); + d[k] = m = {object:l}; } - f.channel && (f.channel.stop(), f.channel = null); - h.stop || (f.channel = f.object.play(0, h.hasLoops ? h.loopCount : 0)); + m.channel && (m.channel.stop(), m.channel = null); + c.stop || (m.channel = m.object.play(0, c.hasLoops ? c.loopCount : 0)); } } }; @@ -26973,29 +33514,41 @@ this._soundStream && this._soundStream.playFrame(a); }; return a; - }(), e = function(b) { - function e() { - t.Sprite.instanceConstructorNoInitialize.call(this); + }(), n = function(k) { + function f() { + v.Sprite.instanceConstructorNoInitialize.call(this); } - __extends(e, b); - e.reset = function() { - e.frameNavigationModel = 3; - e._callQueue = []; - }; - e.runFrameScripts = function() { - k.enterTimeline("MovieClip.executeFrame"); - var a = e._callQueue; - e._callQueue = []; + __extends(f, k); + f.reset = function() { + f.frameNavigationModel = 10; + f._callQueue = []; + }; + f.runFrameScripts = function() { + h.enterTimeline("MovieClip.executeFrame"); + var a = f._callQueue; + f._callQueue = []; for (var b = 0;b < a.length;b++) { - var c = a[b]; - c._allowFrameNavigation = 1 === e.frameNavigationModel; - c.callFrame(c._currentFrame); - c._allowFrameNavigation = !0; - c._nextFrame !== c._currentFrame && (2 === e.frameNavigationModel ? (c._advanceFrame(), c._constructFrame(), c._removeFlags(4096), c.callFrame(c._currentFrame)) : t.DisplayObject.performFrameNavigation(!1, !0)); + var e = a[b]; + e._hasFlags(1024) ? (e._removeFlags(1024), e.dispatchEvent(t.Event.getInstance(t.Event.AVM1_LOAD))) : (e._allowFrameNavigation = 1 === v.MovieClip.frameNavigationModel, e.callFrame(e._currentFrame), e._allowFrameNavigation = !0, e._nextFrame !== e._currentFrame && (9 === v.MovieClip.frameNavigationModel ? (e._advanceFrame(), e._constructFrame(), e._removeFlags(8192), e.callFrame(e._currentFrame)) : v.DisplayObject.performFrameNavigation(!1, !0))); } - k.leaveTimeline(); + h.leaveTimeline(); }; - e.prototype._initFrame = function(a) { + f.prototype._addFrame = function(a) { + var b = this._symbol, e = b.frames; + e.push(a); + a.labelName && this.addFrameLabel(a.labelName, e.length); + a.soundStreamHead && this._initSoundStream(a.soundStreamHead); + a.soundStreamBlock && this._addSoundStreamBlock(e.length, a.soundStreamBlock); + if (b.isAVM1Object && (c.AVM1.Lib.getAVM1Object(this, b.avm1Context).addFrameActionBlocks(e.length - 1, a), a.exports)) { + a = a.exports; + for (var f = 0;f < a.length;f++) { + var k = a[f]; + b.avm1Context.addAsset(k.className, k.symbolId, null); + } + } + 1 === e.length && this._initializeChildren(e[0]); + }; + f.prototype._initFrame = function(a) { if (a) { if (this.buttonMode && (a = null, this._mouseOver ? a = this._mouseDown ? "_down" : "_over" : null !== this._currentButtonState && (a = "_up"), a !== this._currentButtonState && this._buttonFrames[a])) { this.stop(); @@ -27008,80 +33561,102 @@ } this._advanceFrame(); }; - e.prototype._constructFrame = function() { + f.prototype._constructFrame = function() { this._constructChildren(); }; - e.prototype._enqueueFrameScripts = function() { - this._hasFlags(4096) && (this._removeFlags(4096), e._callQueue.push(this)); - b.prototype._enqueueFrameScripts.call(this); + f.prototype._enqueueFrameScripts = function() { + this._hasFlags(1024) && f._callQueue.push(this); + this._hasFlags(8192) && (this._removeFlags(8192), f._callQueue.push(this)); + k.prototype._enqueueFrameScripts.call(this); }; - Object.defineProperty(e.prototype, "currentFrame", {get:function() { + Object.defineProperty(f.prototype, "currentFrame", {get:function() { return this._currentFrame - this._sceneForFrameIndex(this._currentFrame).offset; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "framesLoaded", {get:function() { + Object.defineProperty(f.prototype, "framesLoaded", {get:function() { return this._frames.length; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "totalFrames", {get:function() { + Object.defineProperty(f.prototype, "totalFrames", {get:function() { return this._totalFrames; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "trackAsMenu", {get:function() { + Object.defineProperty(f.prototype, "trackAsMenu", {get:function() { return this._trackAsMenu; }, set:function(a) { this._trackAsMenu = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "scenes", {get:function() { + Object.defineProperty(f.prototype, "scenes", {get:function() { return this._scenes.map(function(a) { return a.clone(); }); }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "currentScene", {get:function() { + Object.defineProperty(f.prototype, "currentScene", {get:function() { return this._sceneForFrameIndex(this._currentFrame).clone(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "currentLabel", {get:function() { + Object.defineProperty(f.prototype, "currentLabel", {get:function() { var a = this._labelForFrame(this._currentFrame); return a ? a.name : null; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "currentFrameLabel", {get:function() { + Object.defineProperty(f.prototype, "currentLabels", {get:function() { + return this._sceneForFrameIndex(this._currentFrame).labels; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(f.prototype, "currentFrameLabel", {get:function() { var a = this._sceneForFrameIndex(this._currentFrame); return(a = a.getLabelByFrame(this._currentFrame - a.offset)) && a.name; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "enabled", {get:function() { + Object.defineProperty(f.prototype, "enabled", {get:function() { return this._enabled; }, set:function(a) { this._enabled = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "isPlaying", {get:function() { + Object.defineProperty(f.prototype, "isPlaying", {get:function() { return this._isPlaying; }, enumerable:!0, configurable:!0}); - e.prototype.play = function() { + f.prototype.play = function() { 1 < this._totalFrames && (this._isPlaying = !0); this._stopped = !1; }; - e.prototype.stop = function() { + f.prototype.stop = function() { this._isPlaying = !1; this._stopped = !0; }; - e.prototype._gotoFrame = function(b, c) { - var e; - if (null !== c) { - c = d(c); - var h = this._scenes; - s(h.length, "There should be at least one scene defined."); - for (var f = 0;f < h.length && (e = h[f], e.name !== c);f++) { + f.prototype._getAbsFrameNumber = function(a, b) { + var c = 10 !== v.MovieClip.frameNavigationModel, f; + if (null !== b) { + b = l(b); + var k = this._scenes; + p(k.length, "There should be at least one scene defined."); + for (var m = 0;m < k.length && (f = k[m], f.name !== b);m++) { + } + if (m === k.length) { + if (c) { + return; + } + e("ArgumentError", h.Errors.SceneNotFoundError, b); } - f === h.length && a("ArgumentError", k.Errors.SceneNotFoundError, c); } else { - e = this._sceneForFrameIndex(this._currentFrame); + f = this._sceneForFrameIndex(this._currentFrame); + } + k = parseInt(a, 10); + if (k != a) { + k = f.getLabelByName(a, c); + if (!k) { + if (c) { + return; + } + e("ArgumentError", h.Errors.FrameLabelNotFoundError, a, b); + } + k = k.frame; } - h = parseInt(b, 10); - h != b && ((h = e.getLabelByName(b)) || a("ArgumentError", k.Errors.FrameLabelNotFoundError, b, c), h = h.frame); - this._gotoFrameAbs(e.offset + h); + return f.offset + k; + }; + f.prototype._gotoFrame = function(a, b) { + var e = this._getAbsFrameNumber(a, b); + void 0 !== e && this._gotoFrameAbs(e); }; - e.prototype._gotoFrameAbs = function(a) { + f.prototype._gotoFrameAbs = function(a) { 1 > a ? a = 1 : a > this._totalFrames && (a = this._totalFrames); - a !== this._nextFrame && (this._nextFrame = a, this._allowFrameNavigation && (2 === e.frameNavigationModel ? (this._advanceFrame(), this._constructFrame()) : t.DisplayObject.performFrameNavigation(!1, !0))); + a !== this._nextFrame && (this._nextFrame = a, this._allowFrameNavigation && (9 === v.MovieClip.frameNavigationModel ? (this._advanceFrame(), this._constructFrame()) : v.DisplayObject.performFrameNavigation(!1, !0))); }; - e.prototype._advanceFrame = function() { + f.prototype._advanceFrame = function() { var a = this._currentFrame, b = this._nextFrame; b > this._totalFrames && (b = 1); if (a === b) { @@ -27090,163 +33665,171 @@ if (b > this.framesLoaded) { this._nextFrame = b; } else { - var c = this._frames, d = a, e = c[a - 1]; - if (b < a) { - var f = c[0]; - s(f, "FrameDelta is not defined."); - if (f !== e) { - for (d = f.stateAtDepth, f = this._children.slice(), a = 0;a < f.length;a++) { - var l = f[a]; - if (l._depth) { - var g = d[l._depth]; - g && g.canBeAnimated(l) || this._removeAnimatedChild(l); - } - } + var e = this._frames[b - 1]; + if (e !== this._frames[a - 1] && (this._seekToFrame(b), e.controlTags)) { + for (var a = e.controlTags, f, e = 0;e < a.length;e++) { + var k = a[e]; + 15 === k.tagCode && (k = this._symbol.loaderInfo._file.getParsedTag(k)); + 15 === k.code && (f || (f = []), f.push(new c.Timeline.SoundStart(k.soundId, k.soundInfo))); } - d = 0; + f && this._registerStartSounds(b, f); } - for (a = d;a < b;a++) { - if (f = c[a], s(f, "FrameDelta is not defined."), f !== e) { - var e = f, d = f.stateAtDepth, n; - for (n in d) { - l = this.getTimelineObjectAtDepth(n | 0); - g = d[n]; - if (l) { - if (g && g.canBeAnimated(l)) { - g.symbol && !g.symbol.dynamic && l._setStaticContentFromSymbol(g.symbol); - l._animate(g); - continue; - } - this._removeAnimatedChild(l); - } - g && g.symbol && (l = t.DisplayObject.createAnimatedDisplayObject(g, !1), this.addTimelineObjectAtDepth(l, g.depth), g.symbol.isAVM1Object && this._initAvm1Bindings(l, g)); + this._frameScripts[b] && (this._setFlags(8192), this._parent && this._propagateFlagsUp(16384)); + this._currentFrame = this._nextFrame = b; + this._syncSounds(b); + } + } + }; + f.prototype._seekToFrame = function(a) { + var b = this._currentFrame, e = this._frames; + if (a === b + 1) { + e = e[a - 1], e.controlTags && this._processControlTags(e.controlTags, !1); + } else { + for (var c = e[b - 1], f = this._symbol.loaderInfo, k = a < b, m = [], l, b = k ? 0 : b;a-- > b;) { + var h = e[a]; + if (h !== c && (c = h, h = h.controlTags)) { + for (var n = h.length;n--;) { + var q = h[n], q = void 0 === q.tagCode ? q : f._file.getParsedTag(q); + switch(q.code) { + case 5: + ; + case 28: + l || (l = Object.create(null)); + l[q.depth] = !0; + k || m.push(q); + break; + case 4: + ; + case 26: + ; + case 70: + l && l[q.depth] || m.push(q); + break; + default: + m.push(q); } - (d = f.soundStarts) && this._registerStartSounds(a + 1, d); } } - this._frameScripts[b] && (this._setFlags(4096), this._parent && this._propagateFlagsUp(8192)); - this._currentFrame = this._nextFrame = b; - this._syncSounds(b); } + m.reverse(); + this._processControlTags(m, k); } }; - e.prototype._sceneForFrameIndex = function(a) { + f.prototype._sceneForFrameIndex = function(a) { var b = this._scenes; if (0 === a) { return b[0]; } - for (var c = 0;c < b.length;c++) { - var d = b[c]; - if (d.offset < a && d.offset + d.numFrames >= a) { - return d; + for (var e = 0;e < b.length;e++) { + var c = b[e]; + if (c.offset < a && c.offset + c.numFrames >= a) { + return c; } } - m("Must have at least one scene covering all frames."); + u("Must have at least one scene covering all frames."); }; - e.prototype._labelForFrame = function(a) { - for (var b = this._scenes, c = null, d = 0;d < b.length;d++) { - var e = b[d]; - if (e.offset > a) { + f.prototype._labelForFrame = function(a) { + for (var b = this._scenes, e = null, c = 0;c < b.length;c++) { + var f = b[c]; + if (f.offset > a) { break; } - for (var f = e.labels, l = 0;l < f.length;l++) { - var g = f[l]; - if (g.frame > a - e.offset) { - return c; + for (var k = f.labels, m = 0;m < k.length;m++) { + var l = k[m]; + if (l.frame > a - f.offset) { + return e; } - c = g; + e = l; } } - return c; - }; - e.prototype._removeAnimatedChild = function(a) { - this.removeChild(a); - if (a._name) { - var b = n.getPublicQualifiedName(a._name); - this[b] === a && (this[b] = null); - } + return e; }; - e.prototype.callFrame = function(a) { + f.prototype.callFrame = function(a) { if (a = this._frameScripts[a | 0]) { try { a.call(this); } catch (b) { - throw c.instance.reportTelemetry({topic:"error", error:2}), this.stop(), b; + throw m.instance.reportTelemetry({topic:"error", error:2}), this.stop(), b; } } }; - e.prototype.nextFrame = function() { + f.prototype.nextFrame = function() { this.gotoAndStop(this._currentFrame + 1); }; - e.prototype.prevFrame = function() { + f.prototype.prevFrame = function() { this.gotoAndStop(this._currentFrame - 1); }; - e.prototype.gotoAndPlay = function(b, c) { - "undefined" === typeof c && (c = null); - (0 === arguments.length || 2 < arguments.length) && a("ArgumentError", k.Errors.WrongArgumentCountError, "flash.display::MovieClip/gotoAndPlay()", 1, arguments.length); - c = d(c); - b = d(b) + ""; + f.prototype.gotoAndPlay = function(a, b) { + void 0 === b && (b = null); + (0 === arguments.length || 2 < arguments.length) && e("ArgumentError", h.Errors.WrongArgumentCountError, "flash.display::MovieClip/gotoAndPlay()", 1, arguments.length); + b = l(b); + a = l(a) + ""; this.play(); - this._gotoFrame(b, c); + this._gotoFrame(a, b); }; - e.prototype.gotoAndStop = function(b, c) { - "undefined" === typeof c && (c = null); - (0 === arguments.length || 2 < arguments.length) && a("ArgumentError", k.Errors.WrongArgumentCountError, "flash.display::MovieClip/gotoAndPlay()", 1, arguments.length); - c = d(c); - b = d(b) + ""; + f.prototype.gotoAndStop = function(a, b) { + void 0 === b && (b = null); + (0 === arguments.length || 2 < arguments.length) && e("ArgumentError", h.Errors.WrongArgumentCountError, "flash.display::MovieClip/gotoAndPlay()", 1, arguments.length); + b = l(b); + a = l(a) + ""; this.stop(); - this._gotoFrame(b, c); + this._gotoFrame(a, b); }; - e.prototype.addFrameScript = function(b, c) { + f.prototype.addFrameScript = function(a, b) { if (this._currentFrame) { - var d = arguments.length; - d & 1 && a("ArgumentError", k.Errors.TooFewArgumentsError, d, d + 1); - for (var h = this._frameScripts, e = this._totalFrames, f = 0;f < d;f += 2) { - var l = (arguments[f] | 0) + 1; - 1 > l || l > e || (h[l] = arguments[f + 1], l === this._currentFrame && (this._setFlags(4096), this._parent && this._propagateFlagsUp(8192))); + var c = arguments.length; + c & 1 && e("ArgumentError", h.Errors.TooFewArgumentsError, c, c + 1); + for (var f = this._frameScripts, k = this._totalFrames, m = 0;m < c;m += 2) { + var l = (arguments[m] | 0) + 1; + 1 > l || l > k || (f[l] = arguments[m + 1], l === this._currentFrame && (this._setFlags(8192), this._parent && this._propagateFlagsUp(16384))); } } }; - Object.defineProperty(e.prototype, "_avm1SymbolClass", {get:function() { + Object.defineProperty(f.prototype, "_avm1SymbolClass", {get:function() { return this._symbol && this._symbol.avm1SymbolClass || null; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "_isFullyLoaded", {get:function() { + Object.defineProperty(f.prototype, "_isFullyLoaded", {get:function() { return this.framesLoaded >= this.totalFrames; }, enumerable:!0, configurable:!0}); - e.prototype._registerStartSounds = function(a, b) { - null === this._sounds && (this._sounds = new p(this)); + f.prototype._registerStartSounds = function(a, b) { + null === this._sounds && (this._sounds = new q(this)); this._sounds.registerStartSounds(a, b); }; - e.prototype._initSoundStream = function(a) { - null === this._sounds && (this._sounds = new p(this)); + f.prototype._initSoundStream = function(a) { + null === this._sounds && (this._sounds = new q(this)); this._sounds.initSoundStream(a); }; - e.prototype._addSoundStreamBlock = function(a, b) { + f.prototype._addSoundStreamBlock = function(a, b) { this._sounds.addSoundStreamBlock(a, b); }; - e.prototype._syncSounds = function(a) { + f.prototype._syncSounds = function(a) { null !== this._sounds && this._sounds.syncSounds(a); }; - e.prototype.addScene = function(a, b, c, d) { - this._scenes.push(new t.Scene(a, b, c, d)); + f.prototype.addScene = function(a, b, e, c) { + this._scenes.push(new v.Scene(a, b, e, c)); }; - e.prototype.addFrameLabel = function(a, b) { - var c = this._sceneForFrameIndex(b); - c.getLabelByName(a) || c.labels.push(new f.display.FrameLabel(a, b - c.offset)); + f.prototype.addFrameLabel = function(d, b) { + var e = this._sceneForFrameIndex(b); + e.getLabelByName(d, !1) || e.labels.push(new a.display.FrameLabel(d, b - e.offset)); }; - e.prototype.prevScene = function() { + f.prototype.prevScene = function() { var a = this._sceneForFrameIndex(this._currentFrame); 0 !== a.offset && this._gotoFrameAbs(this._sceneForFrameIndex(a.offset).offset + 1); }; - e.prototype.nextScene = function() { + f.prototype.nextScene = function() { var a = this._sceneForFrameIndex(this._currentFrame); a.offset + a.numFrames !== this._totalFrames && this._gotoFrameAbs(a.offset + a.numFrames + 1); }; - e.classInitializer = function() { - e.reset(); + f.prototype._containsPointImpl = function(a, b, e, c, f, m, l) { + a = k.prototype._containsPointImpl.call(this, a, b, e, c, f, m, !0); + 2 === a && 3 === f && "_as2Object" in this && !this.buttonMode && m[0] === this && (m.length = 0); + return a; }; - e.initializer = function(a) { - t.DisplayObject._advancableInstances.push(this); + f.classInitializer = function() { + f.reset(); + }; + f.initializer = function(a) { + v.DisplayObject._advancableInstances.push(this); this._currentFrame = 0; this._totalFrames = 1; this._trackAsMenu = !1; @@ -27263,12 +33846,11 @@ this._currentButtonState = null; if (a) { if (this._totalFrames = a.numFrames, this._currentFrame = 1, a.isRoot || this.addScene("", a.labels, 0, a.numFrames), this._frames = a.frames, a.isAVM1Object) { - this._mouseEnabled = !1; if (a.frameScripts) { - var b = g.avm1lib.getAVM1Object(this); + var b = c.AVM1.Lib.getAVM1Object(this, a.avm1Context); b.context = a.avm1Context; - for (var c = a.frameScripts, d = 0;d < c.length;d += 2) { - b.addFrameScript(c[d], c[d + 1]); + for (var e = a.frameScripts, f = 0;f < e.length;f += 2) { + b.addFrameScript(e[f], e[f + 1]); } } a.avm1Name && (this.name = a.avm1Name); @@ -27277,655 +33859,941 @@ this.addScene("", [], 0, this._totalFrames); } }; - e.classSymbols = null; - e.instanceSymbols = null; - return e; - }(f.display.Sprite); - t.MovieClip = e; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + f.classSymbols = null; + f.instanceSymbols = null; + return f; + }(a.display.Sprite); + v.MovieClip = n; + })(a.display || (a.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(b) { - (function(g) { - function k(a, b) { - var d; - try { - d = b.addSourceBuffer("audio/mpeg"); - a.mediaSource = b; - a.sourceBuffer = d; - var f = 0; - a.rawFrames.forEach(function(a) { - f += a.length; - }); - if (0 !== f) { - var e = new Uint8Array(f), g = 0; - a.rawFrames.forEach(function(a) { - e.set(a, g); - g += a.length; - }); - d.appendBuffer(e); - } - a.rawFrames = null; - } catch (l) { - console.error("MediaSource mp3 playback is not supported: " + l); - } - } - function m(a, b) { - var d = !1, f; +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(v) { + function p(a, e) { + var c = !1, d; a.addEventListener("timeupdate", function(a) { - d ? performance.now() : (f = performance.now(), d = !0); + c ? performance.now() : (d = performance.now(), c = !0); }); a.addEventListener("pause", function(a) { - d = !1; + c = !1; }); a.addEventListener("seeking", function(a) { - d = !1; + c = !1; }); } - var d = function() { - function a(a, b) { - this.movieClip = b; - this.data = {sampleRate:a.sampleRate, channels:a.channels}; + var u = c.SWF.MP3DecoderSession, l = function() { + function a(e) { + this._element = e; + } + Object.defineProperty(a.prototype, "isReady", {get:function() { + return!!this._channel; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(a.prototype, "element", {get:function() { + return this._element; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(a.prototype, "currentTime", {get:function() { + return this._element.currentTime; + }, enumerable:!0, configurable:!0}); + a.prototype.playFrom = function(a) { + var e = this._element; + e.paused ? (e.play(), e.addEventListener("playing", function b(c) { + e.removeEventListener("playing", b); + e.currentTime = a; + })) : e.currentTime = a; + }; + Object.defineProperty(a.prototype, "paused", {get:function() { + return this._element.paused; + }, set:function(a) { + var e = this._element; + a ? e.paused || e.pause() : e.paused && e.play(); + }, enumerable:!0, configurable:!0}); + a.prototype.createChannel = function() { + this._channel = h.media.SoundChannel.initializeFrom({element:this._element}); + }; + a.prototype.queueData = function(a) { + c.Debug.abstractMethod("HTMLAudioElementAdapter::queueData"); + }; + a.prototype.finish = function() { + c.Debug.abstractMethod("HTMLAudioElementAdapter::finish"); + }; + return a; + }(), e = function(a) { + function e(c) { + a.call(this, c); + this._mediaSource = new MediaSource; + this._sourceBuffer = null; + this._updating = !1; + this._loading = !0; + this._rawFrames = []; + this._isReady = !1; + this._mediaSource.addEventListener("sourceopen", this._openMediaSource.bind(this)); + this.element.src = URL.createObjectURL(this._mediaSource); + } + __extends(e, a); + e.prototype._appendSoundData = function() { + 0 !== this._rawFrames.length && !this._updating && this._sourceBuffer && (this._loading ? (this._updating = !0, this._sourceBuffer.appendBuffer(this._rawFrames.shift()), this._isReady || (this._isReady = !0, this.createChannel())) : this._mediaSource.endOfStream()); + }; + e.prototype._openMediaSource = function() { + var a = this._mediaSource.addSourceBuffer("audio/mpeg"); + a.addEventListener("update", function() { + this._updating = !1; + this._appendSoundData(); + }.bind(this)); + this._sourceBuffer = a; + this._appendSoundData(); + }; + e.prototype.queueData = function(a) { + this._rawFrames.push(a.data); + this._appendSoundData(); + }; + e.prototype.finish = function() { + this._loading = !1; + this._appendSoundData(); + }; + return e; + }(l), m = function(a) { + function e(c) { + a.call(this, c); + this._rawFrames = []; + } + __extends(e, a); + e.prototype.queueData = function(a) { + this._rawFrames.push(a.data); + }; + e.prototype.finish = function() { + this.element.src = URL.createObjectURL(new Blob(this._rawFrames)); + this.createChannel(); + }; + return e; + }(l), t = function() { + function a(e) { + this._sound = this._channel = null; + this._data = e; + this._position = 0; + } + Object.defineProperty(a.prototype, "currentTime", {get:function() { + return NaN; + }, enumerable:!0, configurable:!0}); + a.prototype.playFrom = function(a) { + }; + Object.defineProperty(a.prototype, "paused", {get:function() { + return!1; + }, set:function(a) { + }, enumerable:!0, configurable:!0}); + Object.defineProperty(a.prototype, "isReady", {get:function() { + return!!this._channel; + }, enumerable:!0, configurable:!0}); + a.prototype.queueData = function(a) { + this._data.pcm.set(a.pcm, this._position); + this._position += a.pcm.length; + }; + a.prototype.finish = function() { + var a = h.media.Sound.initializeFrom(this._data), e = a.play(); + this._sound = a; + this._channel = e; + }; + return a; + }(), q = function(a) { + function e(c) { + a.call(this, c); + this._decoderPosition = 0; + this._decoderSession = new u; + this._decoderSession.onframedata = function(a) { + var b = this._decoderPosition; + c.pcm.set(a, b); + this._decoderPosition = b + a.length; + }.bind(this); + this._decoderSession.onclosed = function() { + t.prototype.finish.call(this); + }.bind(this); + this._decoderSession.onerror = function(a) { + console.warn("MP3DecoderSession error: " + a); + }; + } + __extends(e, a); + e.prototype.queueData = function(a) { + this._decoderSession.pushAsync(a.data); + }; + e.prototype.finish = function() { + this._decoderSession.close(); + }; + return e; + }(t), l = function() { + function c(k, f) { + this.movieClip = f; + this.decode = k.decode; + this.data = {sampleRate:k.sampleRate, channels:k.channels}; this.seekIndex = []; this.position = 0; - var d = "mp3" === a.format; - if (d) { - var e = document.createElement("audio"); - e.preload = "metadata"; - e.loop = !1; - m(e, b); - if (e.canPlayType("audio/mpeg")) { - this.element = e; - "undefined" !== typeof MediaSource ? (d = new MediaSource, d.addEventListener("sourceopen", k.bind(null, this, d)), e.src = URL.createObjectURL(d)) : console.warn("MediaSource is not supported"); - this.rawFrames = []; + this.wasFullyLoaded = !1; + this.waitFor = this.expectedFrame = 0; + var d = "mp3" === k.format; + if (d && !a.webAudioMP3Option.value) { + var b = document.createElement("audio"); + b.preload = "metadata"; + b.loop = !1; + p(b, f); + if (b.canPlayType("audio/mpeg")) { + this.element = b; + a.mediaSourceMP3Option.value ? "undefined" !== typeof MediaSource && MediaSource.isTypeSupported("audio/mpeg") ? this.soundStreamAdapter = new e(b) : (console.warn("MediaSource is not supported"), this.soundStreamAdapter = new m(b)) : this.soundStreamAdapter = new m(b); return; } } - this.data.pcm = new Float32Array(a.samplesCount * a.channels); - if (d) { - var f = this; - f.decoderPosition = 0; - f.decoderSession = new MP3DecoderSession; - f.decoderSession.onframedata = function(a) { - var b = f.decoderPosition; - f.data.pcm.set(a, b); - f.decoderPosition = b + a.length; - }.bind(this); - f.decoderSession.onerror = function(a) { - console.error("ERROR: MP3DecoderSession: " + a); - }; - } + this.data.pcm = new Float32Array((k.samplesCount + 1) * this.movieClip.totalFrames * k.channels); + this.soundStreamAdapter = d ? new q(this.data) : new t(this.data); } - a.prototype.appendBlock = function(a, b) { - var d = this.position; - this.seekIndex[a] = d + b.seek * this.data.channels; - this.position = d + b.samplesCount * this.data.channels; - if (this.sourceBuffer) { - this.sourceBuffer.appendBuffer(b.data); - } else { - if (this.rawFrames) { - this.rawFrames.push(b.data); - } else { - var e = this.decoderSession; - e ? e.pushAsync(b.data) : this.data.pcm.set(b.pcm, d); - } - } - }; - a.prototype.playFrame = function(a) { - if (!isNaN(this.seekIndex[a])) { - var d = this.element; - if (d) { - var g = this.data, e = this.seekIndex[a] / g.sampleRate / g.channels; - if (!this.channel && (this.movieClip._isFullyLoaded || this.sourceBuffer)) { - this.sourceBuffer || (d.src = URL.createObjectURL(new Blob(this.rawFrames))), this.channel = a = b.media.SoundChannel.initializeFrom({element:d}), this.waitFor = this.expectedFrame = 0; - } else { - if (this.sourceBuffer || !isNaN(d.duration)) { - this.mediaSource && this.movieClip._isFullyLoaded && (this.mediaSource.endOfStream(), this.mediaSource = null), g = d.currentTime, this.expectedFrame !== a ? d.paused ? (d.play(), d.addEventListener("playing", function l(a) { - d.removeEventListener("playing", l); - d.currentTime = e; - })) : d.currentTime = e : 0 < this.waitFor ? this.waitFor <= e && (d.paused && d.play(), this.waitFor = 0) : 1 < g - e ? (console.warn("Sound is faster than frames by " + (g - e)), this.waitFor = g - .25, d.pause()) : 1 < e - g && (console.warn("Sound is slower than frames by " + (e - g)), d.currentTime = e + .25), this.expectedFrame = a + 1; - } - } - } else { - this.sound || (g = b.media.Sound.initializeFrom(this.data), a = g.play(), this.sound = g, this.channel = a); - } + c.prototype.appendBlock = function(a, e) { + var d = this.decode(e), b = this.position; + this.seekIndex[a] = b + d.seek * this.data.channels; + this.position = b + d.samplesCount * this.data.channels; + this.soundStreamAdapter.queueData(d); + }; + c.prototype.playFrame = function(a) { + if (!isNaN(this.seekIndex[a]) && (!this.wasFullyLoaded && this.movieClip._isFullyLoaded && (this.wasFullyLoaded = !0, this.soundStreamAdapter.finish()), this.soundStreamAdapter.isReady && !isNaN(this.soundStreamAdapter.currentTime))) { + var e = this.data, e = this.seekIndex[a] / e.sampleRate / e.channels, d = this.soundStreamAdapter.currentTime; + this.expectedFrame !== a ? this.soundStreamAdapter.playFrom(e) : 0 < this.waitFor ? this.waitFor <= e && (this.soundStreamAdapter.paused = !1, this.waitFor = 0) : 1 < d - e ? (console.warn("Sound is faster than frames by " + (d - e)), this.waitFor = d - .25, this.soundStreamAdapter.paused = !0) : 1 < e - d && (console.warn("Sound is slower than frames by " + (e - d)), this.soundStreamAdapter.playFrom(e + .25)); + this.expectedFrame = a + 1; } }; - return a; + return c; }(); - g.MovieClipSoundStream = d; - })(b.display || (b.display = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(t) { - var s = b.Debug.notImplemented, m = b.Debug.assert, d = b.Debug.somewhatImplemented, a = b.AVM2.Runtime.asCoerceString, c = b.AVM2.Runtime.throwError, n = function(b) { - function e() { - t.DisplayObjectContainer.instanceConstructorNoInitialize.call(this); + v.MovieClipSoundStream = l; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(s) { + (function(v) { + var p = c.Debug.notImplemented, u = c.Debug.assert, l = c.Debug.somewhatImplemented, e = c.AVM2.Runtime.asCoerceString, m = c.AVM2.Runtime.throwError, t = function(c) { + function n() { + v.DisplayObjectContainer.instanceConstructorNoInitialize.call(this); this._root = this; this._stage = this; this._frameRate = 24; - this._scaleMode = t.StageScaleMode.SHOW_ALL; + this._scaleMode = v.StageScaleMode.SHOW_ALL; this._align = ""; this._stageHeight = this._stageWidth = 0; this._showDefaultContextMenu = !0; this._focus = null; - this._colorCorrection = t.ColorCorrection.DEFAULT; - this._colorCorrectionSupport = t.ColorCorrectionSupport.DEFAULT_OFF; + this._colorCorrection = v.ColorCorrection.DEFAULT; + this._colorCorrectionSupport = v.ColorCorrectionSupport.DEFAULT_OFF; this._stageFocusRect = !0; - this._quality = t.StageQuality.HIGH; + this._quality = v.StageQuality.HIGH; this._fullScreenSourceRect = this._displayState = null; this._mouseLock = !1; - this._stageVideos = new g.GenericVector(0, !0, g.ASObject); + this._stageVideos = new a.GenericVector(0, !0, a.ASObject); this._stage3Ds = null; this._colorARGB = 4294967295; this._fullScreenHeight = this._fullScreenWidth = 0; this._wmodeGPU = !1; - this._softKeyboardRect = new f.geom.Rectangle; + this._softKeyboardRect = new s.geom.Rectangle; this._allowsFullScreenInteractive = this._allowsFullScreen = !1; this._contentsScaleFactor = 1; this._displayContextInfo = null; - this._timeout = -1; + this._stageContainerHeight = this._stageContainerWidth = this._timeout = -1; this._invalidated = !1; } - __extends(e, b); - Object.defineProperty(e.prototype, "frameRate", {get:function() { + __extends(n, c); + n.prototype.setRoot = function(a) { + this.addTimelineObjectAtDepth(a, 0); + }; + Object.defineProperty(n.prototype, "frameRate", {get:function() { return this._frameRate; }, set:function(a) { this._frameRate = +a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "scaleMode", {get:function() { + Object.defineProperty(n.prototype, "scaleMode", {get:function() { return this._scaleMode; - }, set:function(b) { - b = a(b); - 0 > f.display.StageScaleMode.toNumber(b) && c("ArgumentError", k.Errors.InvalidEnumError, "scaleMode"); - this._scaleMode = b; + }, set:function(a) { + a = e(a); + 0 > s.display.StageScaleMode.toNumber(a) && m("ArgumentError", h.Errors.InvalidEnumError, "scaleMode"); + this._scaleMode = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "align", {get:function() { + Object.defineProperty(n.prototype, "align", {get:function() { return this._align; - }, set:function(b) { - b = a(b); - b = f.display.StageAlign.toNumber(b); - m(0 <= b); - this._align = f.display.StageAlign.fromNumber(b); + }, set:function(a) { + a = e(a); + a = s.display.StageAlign.toNumber(a); + u(0 <= a); + this._align = s.display.StageAlign.fromNumber(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "stageWidth", {get:function() { - return this._stageWidth / 20 | 0; + Object.defineProperty(n.prototype, "stageWidth", {get:function() { + if (this.scaleMode !== v.StageScaleMode.NO_SCALE) { + return this._stageWidth / 20 | 0; + } + u(0 <= this._stageContainerWidth); + return this._stageContainerWidth; }, set:function(a) { }, enumerable:!0, configurable:!0}); - e.prototype._setInitialName = function() { + n.prototype._setInitialName = function() { this._name = null; }; - e.prototype.setStageWidth = function(a) { - m((a | 0) === a); + n.prototype.setStageWidth = function(a) { + u((a | 0) === a); this._stageWidth = 20 * a | 0; }; - Object.defineProperty(e.prototype, "stageHeight", {get:function() { - return this._stageHeight / 20 | 0; + Object.defineProperty(n.prototype, "stageHeight", {get:function() { + if (this.scaleMode !== v.StageScaleMode.NO_SCALE) { + return this._stageHeight / 20 | 0; + } + u(0 <= this._stageContainerHeight); + return this._stageContainerHeight; }, set:function(a) { }, enumerable:!0, configurable:!0}); - e.prototype.setStageHeight = function(a) { - m((a | 0) === a); + n.prototype.setStageHeight = function(a) { + u((a | 0) === a); this._stageHeight = 20 * a | 0; }; - e.prototype.setStageColor = function(a) { + n.prototype.setStageColor = function(a) { this._colorARGB = a; }; - Object.defineProperty(e.prototype, "showDefaultContextMenu", {get:function() { + n.prototype.setStageContainerSize = function(a, e, d) { + this._contentsScaleFactor = d; + if (this._stageContainerWidth !== a || this._stageContainerHeight !== e) { + this._stageContainerWidth = a, this._stageContainerHeight = e, this.scaleMode === v.StageScaleMode.NO_SCALE && this.dispatchEvent(s.events.Event.getInstance(s.events.Event.RESIZE)); + } + }; + Object.defineProperty(n.prototype, "showDefaultContextMenu", {get:function() { return this._showDefaultContextMenu; }, set:function(a) { this._showDefaultContextMenu = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "focus", {get:function() { + Object.defineProperty(n.prototype, "focus", {get:function() { return this._focus; }, set:function(a) { this._focus = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "colorCorrection", {get:function() { + Object.defineProperty(n.prototype, "colorCorrection", {get:function() { return this._colorCorrection; }, set:function(a) { - s("public flash.display.Stage::set colorCorrection"); + p("public flash.display.Stage::set colorCorrection"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "colorCorrectionSupport", {get:function() { + Object.defineProperty(n.prototype, "colorCorrectionSupport", {get:function() { return this._colorCorrectionSupport; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "stageFocusRect", {get:function() { + Object.defineProperty(n.prototype, "stageFocusRect", {get:function() { return this._stageFocusRect; }, set:function(a) { this._stageFocusRect = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "quality", {get:function() { + Object.defineProperty(n.prototype, "quality", {get:function() { return this._quality.toUpperCase(); - }, set:function(b) { - b = (a(b) || "").toLowerCase(); - 0 > f.display.StageQuality.toNumber(b) && (b = f.display.StageQuality.HIGH); - this._quality = b; + }, set:function(a) { + a = (e(a) || "").toLowerCase(); + 0 > s.display.StageQuality.toNumber(a) && (a = s.display.StageQuality.HIGH); + this._quality = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "displayState", {get:function() { + Object.defineProperty(n.prototype, "displayState", {get:function() { return this._displayState; - }, set:function(b) { - d("public flash.display.Stage::set displayState"); - this._displayState = a(b); + }, set:function(a) { + a = e(a); + 0 > s.display.StageDisplayState.toNumber(a) && (a = s.display.StageDisplayState.NORMAL); + this._displayState = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "fullScreenSourceRect", {get:function() { + Object.defineProperty(n.prototype, "fullScreenSourceRect", {get:function() { return this._fullScreenSourceRect; }, set:function(a) { - s("public flash.display.Stage::set fullScreenSourceRect"); + p("public flash.display.Stage::set fullScreenSourceRect"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "mouseLock", {get:function() { + Object.defineProperty(n.prototype, "mouseLock", {get:function() { return this._mouseLock; }, set:function(a) { - d("public flash.display.Stage::set mouseLock"); + l("public flash.display.Stage::set mouseLock"); this._mouseLock = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "stageVideos", {get:function() { - d("public flash.display.Stage::get stageVideos"); + Object.defineProperty(n.prototype, "stageVideos", {get:function() { + l("public flash.display.Stage::get stageVideos"); return this._stageVideos; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "stage3Ds", {get:function() { - s("public flash.display.Stage::get stage3Ds"); + Object.defineProperty(n.prototype, "stage3Ds", {get:function() { + p("public flash.display.Stage::get stage3Ds"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "color", {get:function() { + Object.defineProperty(n.prototype, "color", {get:function() { return this._colorARGB; }, set:function(a) { this._colorARGB = a | 4278190080; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "alpha", {get:function() { + Object.defineProperty(n.prototype, "alpha", {get:function() { return this._colorTransform.alphaMultiplier; }, set:function(a) { - c("Error", k.Errors.InvalidStageMethodError); + m("IllegalOperationError", h.Errors.InvalidStageMethodError); }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "fullScreenWidth", {get:function() { + Object.defineProperty(n.prototype, "fullScreenWidth", {get:function() { return this._fullScreenWidth; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "fullScreenHeight", {get:function() { + Object.defineProperty(n.prototype, "fullScreenHeight", {get:function() { return this._fullScreenHeight; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "wmodeGPU", {get:function() { + Object.defineProperty(n.prototype, "wmodeGPU", {get:function() { return this._wmodeGPU; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "softKeyboardRect", {get:function() { + Object.defineProperty(n.prototype, "softKeyboardRect", {get:function() { return this._softKeyboardRect; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "allowsFullScreen", {get:function() { + Object.defineProperty(n.prototype, "allowsFullScreen", {get:function() { return this._allowsFullScreen; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "allowsFullScreenInteractive", {get:function() { + Object.defineProperty(n.prototype, "allowsFullScreenInteractive", {get:function() { return this._allowsFullScreenInteractive; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "contentsScaleFactor", {get:function() { + Object.defineProperty(n.prototype, "contentsScaleFactor", {get:function() { return this._contentsScaleFactor; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "displayContextInfo", {get:function() { + Object.defineProperty(n.prototype, "displayContextInfo", {get:function() { return this._displayContextInfo; }, enumerable:!0, configurable:!0}); - e.prototype.swapChildrenAt = function(a, b) { - s("public flash.display.Stage::swapChildrenAt"); + n.prototype.removeChildAt = function(a) { + return c.prototype.removeChildAt.call(this, a); }; - e.prototype.invalidate = function() { - this._invalidated = !0; + n.prototype.swapChildrenAt = function(a, e) { + c.prototype.swapChildrenAt.call(this, a, e); + }; + Object.defineProperty(n.prototype, "width", {get:function() { + return this._getWidth(); + }, set:function(a) { + this._setWidth(a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "height", {get:function() { + return this._getHeight(); + }, set:function(a) { + this._setHeight(a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "mouseChildren", {get:function() { + return this._mouseChildren; + }, set:function(a) { + this._setMouseChildren(a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "numChildren", {get:function() { + return this._children.length; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "tabChildren", {get:function() { + return this._tabChildren; + }, set:function(a) { + this._setTabChildren(a); + }, enumerable:!0, configurable:!0}); + n.prototype.addChild = function(a) { + return c.prototype.addChild.call(this, a); + }; + n.prototype.addChildAt = function(a, e) { + return c.prototype.addChildAt.call(this, a, e); + }; + n.prototype.setChildIndex = function(a, e) { + c.prototype.setChildIndex.call(this, a, e); + }; + n.prototype.addEventListener = function(a, e, d, b, g) { + c.prototype.addEventListener.call(this, a, e, d, b, g); + }; + n.prototype.hasEventListener = function(a) { + return c.prototype.hasEventListener.call(this, a); + }; + n.prototype.willTrigger = function(a) { + return c.prototype.willTrigger.call(this, a); + }; + n.prototype.dispatchEvent = function(a) { + return c.prototype.dispatchEvent.call(this, a); }; - e.prototype.isInvalidated = function() { - return this._invalidated; + n.prototype.invalidate = function() { + this._invalidated = !0; }; - e.prototype.isFocusInaccessible = function() { - s("public flash.display.Stage::isFocusInaccessible"); + n.prototype.isFocusInaccessible = function() { + p("public flash.display.Stage::isFocusInaccessible"); }; - e.prototype.requireOwnerPermissions = function() { - d("public flash.display.Stage::requireOwnerPermissions"); + n.prototype.requireOwnerPermissions = function() { }; - e.prototype.render = function() { - this._invalidated && (t.DisplayObject._broadcastFrameEvent(f.events.Event.RENDER), this._invalidated = !1); + n.prototype.render = function() { + this._invalidated && (v.DisplayObject._broadcastFrameEvent(s.events.Event.RENDER), this._invalidated = !1); }; + Object.defineProperty(n.prototype, "name", {get:function() { + return this._name; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "mask", {get:function() { + return this._mask; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "visible", {get:function() { + return this._hasFlags(1); + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "x", {get:function() { + return this._getX(); + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "y", {get:function() { + return this._getY(); + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "z", {get:function() { + return this._z; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "scaleX", {get:function() { + return Math.abs(this._scaleX); + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "scaleY", {get:function() { + return this._scaleY; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "scaleZ", {get:function() { + return this._scaleZ; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "rotation", {get:function() { + return this._rotation; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "rotationX", {get:function() { + return this._rotationX; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "rotationY", {get:function() { + return this._rotationX; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "rotationZ", {get:function() { + return this._rotationX; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "cacheAsBitmap", {get:function() { + return this._getCacheAsBitmap(); + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "opaqueBackground", {get:function() { + return this._opaqueBackground; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "scrollRect", {get:function() { + return this._getScrollRect(); + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "filters", {get:function() { + return this._getFilters(); + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "blendMode", {get:function() { + return this._blendMode; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "transform", {get:function() { + return this._getTransform(); + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "accessibilityProperties", {get:function() { + return this._accessibilityProperties; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "scale9Grid", {get:function() { + return this._getScale9Grid(); + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "tabEnabled", {get:function() { + return this._tabEnabled; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "tabIndex", {get:function() { + return this._tabIndex; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "focusRect", {get:function() { + return this._focusRect; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "mouseEnabled", {get:function() { + return this._mouseEnabled; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "accessibilityImplementation", {get:function() { + return this._accessibilityImplementation; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "textSnapshot", {get:function() { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + return null; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "contextMenu", {get:function() { + return this._contextMenu; + }, set:function(a) { + m("IllegalOperationError", h.Errors.InvalidStageMethodError); + }, enumerable:!0, configurable:!0}); + n.classInitializer = null; + n.classSymbols = null; + n.instanceSymbols = null; + n.initializer = null; + return n; + }(s.display.DisplayObjectContainer); + v.Stage = t; + })(s.display || (s.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.ActionScriptVersion"); + } + __extends(e, a); e.classInitializer = null; + e.initializer = null; e.classSymbols = null; e.instanceSymbols = null; - e.initializer = null; + e.ACTIONSCRIPT2 = 2; + e.ACTIONSCRIPT3 = 3; return e; - }(f.display.DisplayObjectContainer); - t.Stage = n; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.ActionScriptVersion"); - } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.ACTIONSCRIPT2 = 2; - a.ACTIONSCRIPT3 = 3; - return a; - }(g.ASNative); - f.ActionScriptVersion = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.BlendMode"); + }(a.ASNative); + h.ActionScriptVersion = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.BlendMode"); } - __extends(a, b); - a.fromNumber = function(b) { - switch(b) { + __extends(e, a); + e.fromNumber = function(a) { + switch(a) { case 0: ; case 1: - return a.NORMAL; + return e.NORMAL; case 2: - return a.LAYER; + return e.LAYER; case 3: - return a.MULTIPLY; + return e.MULTIPLY; case 4: - return a.SCREEN; + return e.SCREEN; case 5: - return a.LIGHTEN; + return e.LIGHTEN; case 6: - return a.DARKEN; + return e.DARKEN; case 7: - return a.DIFFERENCE; + return e.DIFFERENCE; case 8: - return a.ADD; + return e.ADD; case 9: - return a.SUBTRACT; + return e.SUBTRACT; case 10: - return a.INVERT; + return e.INVERT; case 11: - return a.ALPHA; + return e.ALPHA; case 12: - return a.ERASE; + return e.ERASE; case 13: - return a.OVERLAY; + return e.OVERLAY; case 14: - return a.HARDLIGHT; + return e.HARDLIGHT; default: return null; } }; - a.toNumber = function(b) { - switch(b) { - case a.NORMAL: + e.toNumber = function(a) { + switch(a) { + case e.NORMAL: return 1; - case a.LAYER: + case e.LAYER: return 2; - case a.MULTIPLY: + case e.MULTIPLY: return 3; - case a.SCREEN: + case e.SCREEN: return 4; - case a.LIGHTEN: + case e.LIGHTEN: return 5; - case a.DARKEN: + case e.DARKEN: return 6; - case a.DIFFERENCE: + case e.DIFFERENCE: return 7; - case a.ADD: + case e.ADD: return 8; - case a.SUBTRACT: + case e.SUBTRACT: return 9; - case a.INVERT: + case e.INVERT: return 10; - case a.ALPHA: + case e.ALPHA: return 11; - case a.ERASE: + case e.ERASE: return 12; - case a.OVERLAY: + case e.OVERLAY: return 13; - case a.HARDLIGHT: + case e.HARDLIGHT: return 14; default: return-1; } }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.NORMAL = "normal"; - a.LAYER = "layer"; - a.MULTIPLY = "multiply"; - a.SCREEN = "screen"; - a.LIGHTEN = "lighten"; - a.DARKEN = "darken"; - a.ADD = "add"; - a.SUBTRACT = "subtract"; - a.DIFFERENCE = "difference"; - a.INVERT = "invert"; - a.OVERLAY = "overlay"; - a.HARDLIGHT = "hardlight"; - a.ALPHA = "alpha"; - a.ERASE = "erase"; - a.SHADER = "shader"; - return a; - }(g.ASNative); - f.BlendMode = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.ColorCorrection"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.NORMAL = "normal"; + e.LAYER = "layer"; + e.MULTIPLY = "multiply"; + e.SCREEN = "screen"; + e.LIGHTEN = "lighten"; + e.DARKEN = "darken"; + e.ADD = "add"; + e.SUBTRACT = "subtract"; + e.DIFFERENCE = "difference"; + e.INVERT = "invert"; + e.OVERLAY = "overlay"; + e.HARDLIGHT = "hardlight"; + e.ALPHA = "alpha"; + e.ERASE = "erase"; + e.SHADER = "shader"; + return e; + }(a.ASNative); + h.BlendMode = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.ColorCorrection"); } - __extends(a, b); - a.fromNumber = function(b) { - switch(b) { + __extends(e, a); + e.fromNumber = function(a) { + switch(a) { case 0: - return a.DEFAULT; + return e.DEFAULT; case 1: - return a.ON; + return e.ON; case 2: - return a.OFF; + return e.OFF; default: return null; } }; - a.toNumber = function(b) { - switch(b) { - case a.DEFAULT: + e.toNumber = function(a) { + switch(a) { + case e.DEFAULT: return 0; - case a.ON: + case e.ON: return 1; - case a.OFF: + case e.OFF: return 2; default: return-1; } }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.DEFAULT = "default"; - a.ON = "on"; - a.OFF = "off"; - return a; - }(g.ASNative); - f.ColorCorrection = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.ColorCorrectionSupport"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.DEFAULT = "default"; + e.ON = "on"; + e.OFF = "off"; + return e; + }(a.ASNative); + h.ColorCorrection = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.ColorCorrectionSupport"); } - __extends(a, b); - a.fromNumber = function(b) { - switch(b) { + __extends(e, a); + e.fromNumber = function(a) { + switch(a) { case 0: - return a.UNSUPPORTED; + return e.UNSUPPORTED; case 1: - return a.DEFAULT_ON; + return e.DEFAULT_ON; case 2: - return a.DEFAULT_OFF; + return e.DEFAULT_OFF; default: return null; } }; - a.toNumber = function(b) { - switch(b) { - case a.UNSUPPORTED: + e.toNumber = function(a) { + switch(a) { + case e.UNSUPPORTED: return 0; - case a.DEFAULT_ON: + case e.DEFAULT_ON: return 1; - case a.DEFAULT_OFF: + case e.DEFAULT_OFF: return 2; default: return-1; } }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.UNSUPPORTED = "unsupported"; - a.DEFAULT_ON = "defaultOn"; - a.DEFAULT_OFF = "defaultOff"; - return a; - }(g.ASNative); - f.ColorCorrectionSupport = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.FocusDirection"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.UNSUPPORTED = "unsupported"; + e.DEFAULT_ON = "defaultOn"; + e.DEFAULT_OFF = "defaultOff"; + return e; + }(a.ASNative); + h.ColorCorrectionSupport = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.FocusDirection"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.TOP = "top"; - a.BOTTOM = "bottom"; - a.NONE = "none"; - return a; - }(g.ASNative); - f.FocusDirection = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.AVM2.Runtime.asCoerceString, m = function(b) { - function a(a, b) { - this._name = k(a); - this._frame = b | 0; + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.TOP = "top"; + e.BOTTOM = "bottom"; + e.NONE = "none"; + return e; + }(a.ASNative); + h.FocusDirection = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.AVM2.Runtime.asCoerceString, u = function(a) { + function e(a, e) { + this._name = p(a); + this._frame = e | 0; } - __extends(a, b); - Object.defineProperty(a.prototype, "name", {get:function() { + __extends(e, a); + Object.defineProperty(e.prototype, "name", {get:function() { return this._name; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "frame", {get:function() { + Object.defineProperty(e.prototype, "frame", {get:function() { return this._frame; }, enumerable:!0, configurable:!0}); - a.prototype.clone = function() { - return new a(this._name, this._frame); + e.prototype.clone = function() { + return new e(this._name, this._frame); }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(f.events.EventDispatcher); - g.FrameLabel = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(t) { - var s = b.Debug.notImplemented, m = b.Debug.assert, d = b.Debug.somewhatImplemented, a = b.ArrayUtilities.DataBuffer, c = b.AVM2.Runtime.asCoerceString, n = b.AVM2.Runtime.throwError, p = b.AVM2.Runtime.AVM2, e = b.IntegerUtilities.swap32, q = b.ColorUtilities.premultiplyARGB, l = b.ColorUtilities.unpremultiplyARGB, u = b.ColorUtilities.RGBAToARGB, w = b.ArrayUtilities.indexOf, r = f.geom.Rectangle, h = function(h) { - function t(b, c, d, h) { - "undefined" === typeof d && (d = !0); - "undefined" === typeof h && (h = 4294967295); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.events.EventDispatcher); + h.FrameLabel = u; + })(a.display || (a.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(v) { + var p = c.Debug.notImplemented, u = c.Debug.assert, l = c.Debug.somewhatImplemented, e = c.ArrayUtilities.DataBuffer, m = c.AVM2.Runtime.asCoerceString, t = c.AVM2.Runtime.throwError, q = c.AVM2.Runtime.AVM2, n = c.IntegerUtilities.swap32, k = c.ColorUtilities.premultiplyARGB, f = c.ColorUtilities.unpremultiplyARGB, d = c.ColorUtilities.RGBAToARGB, b = c.ArrayUtilities.indexOf, g = h.geom.Rectangle, r = function(r) { + function v(a, b, d, e) { + void 0 === d && (d = !0); + void 0 === e && (e = 4294967295); + a |= 0; b |= 0; - c |= 0; - h |= 0; - this._id = f.display.DisplayObject.getNextSyncID(); - this._symbol && (b = this._symbol.width, c = this._symbol.height); - (b > t.MAXIMUM_WIDTH || 0 >= b || c > t.MAXIMUM_HEIGHT || 0 >= c || b * c > t.MAXIMUM_DIMENSION) && n("ArgumentError", k.Errors.InvalidBitmapData); + d = !!d; + e |= 0; + var c = this._symbol; + c && (a = c.width | 0, b = c.height | 0); + (a > v.MAXIMUM_WIDTH || 0 >= a || b > v.MAXIMUM_HEIGHT || 0 >= b || a * b > v.MAXIMUM_DIMENSION) && t("ArgumentError", q.Errors.InvalidBitmapData); + this._rect = new g(0, 0, a, b); + this._transparent = d; + c ? (u(c.syncId), this._id = c.syncId, 1 === c.type || 2 === c.type || 3 === c.type ? (u(c.data), this._setData(c.data, c.type)) : (this._isDirty = !1, this._isRemoteDirty = !0), this._solidFillColorPBGRA = null) : (this._id = h.display.DisplayObject.getNextSyncID(), this._setData(new Uint8Array(a * b * 4), 1), 0 === e >> 24 && d ? this._solidFillColorPBGRA = 0 : this.fillRect(this._rect, e)); this._bitmapReferrers = []; - this._transparent = !!d; - this._rect = new r(0, 0, b, c); - if (this._symbol) { - this._data = new Uint8Array(this._symbol.data); - this._type = this._symbol.type; - if (1 === this._type || 2 === this._type || 3 === this._type) { - this._view = new Int32Array(this._data.buffer); - } - this._solidFillColorPBGRA = null; - } else { - this._data = new Uint8Array(b * c * 4), this._view = new Int32Array(this._data.buffer), this._type = 1, 0 === h >> 24 && d ? this._solidFillColorPBGRA = 0 : this.fillRect(this._rect, h); - } - this._dataBuffer = a.FromArrayBuffer(this._data.buffer); - this._invalidate(); + u(this._isDirty === !!this._data); + u(this._isRemoteDirty === !this._data); } - __extends(t, h); - t.prototype._addBitmapReferrer = function(a) { - w(this._bitmapReferrers, a); + __extends(v, r); + v.prototype._setData = function(a, b) { + a instanceof Uint8ClampedArray && (a = new Uint8Array(a.buffer)); + u(a instanceof Uint8Array); + this._data = a; + this._type = b; + this._view = new Int32Array(a.buffer); + this._dataBuffer = e.FromArrayBuffer(a.buffer); + this._isDirty = !0; + this._isRemoteDirty = !1; + }; + v.prototype._addBitmapReferrer = function(a) { + var d = b(this._bitmapReferrers, a); + u(0 > d); this._bitmapReferrers.push(a); }; - t.prototype._removeBitmapReferrer = function(a) { - a = w(this._bitmapReferrers, a); + v.prototype._removeBitmapReferrer = function(a) { + a = b(this._bitmapReferrers, a); + u(0 <= a); this._bitmapReferrers[a] = null; }; - t.prototype._invalidate = function() { + v.prototype._invalidate = function() { if (!this._isDirty) { this._isDirty = !0; this._isRemoteDirty = !1; @@ -27935,166 +34803,166 @@ } } }; - t.prototype._getTemporaryRectangleFrom = function(a, b) { - "undefined" === typeof b && (b = 0); - m(0 <= b && b < t._temporaryRectangles.length); - var c = t._temporaryRectangles[b]; - a && c.copyFrom(a); - return c; + v.prototype._getTemporaryRectangleFrom = function(a, b) { + void 0 === b && (b = 0); + u(0 <= b && b < v._temporaryRectangles.length); + var d = v._temporaryRectangles[b]; + a && d.copyFrom(a); + return d; }; - t.prototype.getDataBuffer = function() { + v.prototype.getDataBuffer = function() { return this._dataBuffer; }; - t.prototype._getContentBounds = function() { - return b.Bounds.FromRectangle(this._rect); + v.prototype._getContentBounds = function() { + return c.Bounds.FromRectangle(this._rect); }; - t.prototype._getPixelData = function(a) { + v.prototype._getPixelData = function(a) { var b = this._getTemporaryRectangleFrom(this._rect).intersectInPlace(a); if (!b.isEmpty()) { a = b.x; - for (var c = b.x + b.width, d = b.y, h = b.y + b.height, e = this._view, f = this._rect.width, b = new Int32Array(b.area), l = 0;d < h;d++) { - for (var g = d * f, n = a;n < c;n++) { - var m = e[g + n], p = m & 255, m = 255 * (m >>> 8) / p << 8 | p; - b[l++] = m; + for (var d = b.x + b.width, e = b.y, c = b.y + b.height, g = this._view, f = this._rect.width, b = new Int32Array(b.area), k = 0;e < c;e++) { + for (var m = e * f, l = a;l < d;l++) { + var h = g[m + l], r = h & 255, h = 255 * (h >>> 8) / r << 8 | r; + b[k++] = h; } } return b; } }; - t.prototype._putPixelData = function(a, b) { - var c = this._getTemporaryRectangleFrom(this._rect).intersectInPlace(a); - if (!c.isEmpty()) { - for (var d = c.x, h = c.x + c.width, e = c.y + c.height, f = this._view, l = this._rect.width, g = a.width * a.height - c.height + (d - a.x), n = a.width - c.width, m = this._transparent ? 0 : 255, c = c.y;c < e;c++) { - for (var p = c * l, k = d;k < h;k++) { - var r = b[g++], q = r & m; - f[p + k] = (((r >>> 8) * q + 254) / 255 & 16777215) << 8 | q; + v.prototype._putPixelData = function(a, b) { + var d = this._getTemporaryRectangleFrom(this._rect).intersectInPlace(a); + if (!d.isEmpty()) { + for (var e = d.x, c = d.x + d.width, g = d.y + d.height, f = this._view, k = this._rect.width, m = a.width * a.height - d.height + (e - a.x), l = a.width - d.width, h = this._transparent ? 0 : 255, d = d.y;d < g;d++) { + for (var r = d * k, n = e;n < c;n++) { + var q = b[m++], w = q & h; + f[r + n] = (((q >>> 8) * w + 254) / 255 & 16777215) << 8 | w; } - g += n; + m += l; } this._invalidate(); } }; - Object.defineProperty(t.prototype, "width", {get:function() { + Object.defineProperty(v.prototype, "width", {get:function() { return this._rect.width; }, enumerable:!0, configurable:!0}); - Object.defineProperty(t.prototype, "height", {get:function() { + Object.defineProperty(v.prototype, "height", {get:function() { return this._rect.height; }, enumerable:!0, configurable:!0}); - Object.defineProperty(t.prototype, "rect", {get:function() { + Object.defineProperty(v.prototype, "rect", {get:function() { return this._rect.clone(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(t.prototype, "transparent", {get:function() { + Object.defineProperty(v.prototype, "transparent", {get:function() { return this._transparent; }, enumerable:!0, configurable:!0}); - t.prototype.clone = function() { - d("public flash.display.BitmapData::clone"); - var a = new t(this._rect.width, this._rect.height, this._transparent, this._solidFillColorPBGRA); + v.prototype.clone = function() { + l("public flash.display.BitmapData::clone"); + var a = new v(this._rect.width, this._rect.height, this._transparent, this._solidFillColorPBGRA); a._view.set(this._view); return a; }; - t.prototype.getPixel = function(a, b) { + v.prototype.getPixel = function(a, b) { return this.getPixel32(a | 0, b | 0) & 16777215; }; - t.prototype.getPixel32 = function(a, c) { + v.prototype.getPixel32 = function(a, b) { a |= 0; - c |= 0; - if (!this._rect.contains(a, c)) { + b |= 0; + if (!this._rect.contains(a, b)) { return 0; } this._ensureBitmapData(); - var d = this._view[c * this._rect.width + a]; + var e = this._view[b * this._rect.width + a]; switch(this._type) { case 1: - return d = e(d), l(d) >>> 0; + return e = n(e), f(e) >>> 0; case 3: - return u(e(d)); + return d(n(e)); default: - return b.Debug.notImplemented(b.ImageType[this._type]), 0; + return c.Debug.notImplemented(c.ImageType[this._type]), 0; } }; - t.prototype.setPixel = function(a, b, c) { + v.prototype.setPixel = function(a, b, d) { a |= 0; b |= 0; - c |= 0; - this._rect.contains(a, b) && (this._ensureBitmapData(), a = b * this._rect.width + a, c = c & 16777215 | (this._view[a] & 255) << 24, c = q(c), this._view[a] = e(c), this._invalidate(), this._solidFillColorPBGRA = null); + d |= 0; + this._rect.contains(a, b) && (this._ensureBitmapData(), a = b * this._rect.width + a, d = d & 16777215 | (this._view[a] & 255) << 24, d = k(d), this._view[a] = n(d), this._invalidate(), this._solidFillColorPBGRA = null); }; - t.prototype.setPixel32 = function(a, b, c) { + v.prototype.setPixel32 = function(a, b, d) { a |= 0; b |= 0; if (this._rect.contains(a, b)) { this._ensureBitmapData(); - var d = c >>> 24; - c &= 16777215; - d = this._transparent ? q(c | d << 24) : c | 4278190080; - this._view[b * this._rect.width + a] = e(d); + var e = d >>> 24; + d &= 16777215; + e = this._transparent ? k(d | e << 24) : d | 4278190080; + this._view[b * this._rect.width + a] = n(e); this._invalidate(); this._solidFillColorPBGRA = null; } }; - t.prototype.applyFilter = function(a, b, c, h) { - d("public flash.display.BitmapData::applyFilter " + h); + v.prototype.applyFilter = function(a, b, d, e) { + l("public flash.display.BitmapData::applyFilter " + e); }; - t.prototype.colorTransform = function(a, b) { - d("public flash.display.BitmapData::colorTransform"); + v.prototype.colorTransform = function(a, b) { + l("public flash.display.BitmapData::colorTransform"); }; - t.prototype.compare = function(a) { - s("public flash.display.BitmapData::compare"); + v.prototype.compare = function(a) { + p("public flash.display.BitmapData::compare"); }; - t.prototype.copyChannel = function(a, b, c, d, h) { - s("public flash.display.BitmapData::copyChannel"); - }; - t.prototype.copyPixels = function(a, b, c, h, e, f) { - "undefined" === typeof h && (h = null); - "undefined" === typeof e && (e = null); - "undefined" === typeof f && (f = !1); - f = !!f; - if (h || e) { - s("public flash.display.BitmapData::copyPixels - Alpha"); + v.prototype.copyChannel = function(a, b, d, e, c) { + p("public flash.display.BitmapData::copyChannel"); + }; + v.prototype.copyPixels = function(a, b, d, e, c, g) { + void 0 === e && (e = null); + void 0 === c && (c = null); + void 0 === g && (g = !1); + g = !!g; + if (e || c) { + p("public flash.display.BitmapData::copyPixels - Alpha"); } else { - var l = this._getTemporaryRectangleFrom(b, 0).roundInPlace(); + var f = this._getTemporaryRectangleFrom(b, 0).roundInPlace(); b = this._rect; - e = a._rect; - var g = Math.max(l.x, 0); - h = Math.max(l.y, 0); - var n = Math.min(l.x + l.width, e.width), m = Math.min(l.y + l.height, e.height); - e = c.x | 0 + (g - l.x); - l = c.y | 0 + (h - l.y); - 0 > e && (g -= e, e = 0); - 0 > l && (h -= l, l = 0); - c = Math.min(n - g, b.width - e); - b = Math.min(m - h, b.height - l); - if (!(0 >= c || 0 >= b)) { - var n = g, m = h, p = e, k = l; - h = a._rect.width; - e = this._rect.width; + c = a._rect; + var k = Math.max(f.x, 0); + e = Math.max(f.y, 0); + var m = Math.min(f.x + f.width, c.width), h = Math.min(f.y + f.height, c.height); + c = d.x | 0 + (k - f.x); + f = d.y | 0 + (e - f.y); + 0 > c && (k -= c, c = 0); + 0 > f && (e -= f, f = 0); + d = Math.min(m - k, b.width - c); + b = Math.min(h - e, b.height - f); + if (!(0 >= d || 0 >= b)) { + var m = k, h = e, r = c, n = f; + e = a._rect.width; + c = this._rect.width; this._ensureBitmapData(); a._ensureBitmapData(); - g = a._view; - l = this._view; - a._type !== this._type && d("public flash.display.BitmapData::copyPixels - Color Format Conversion"); - if (f && 1 !== this._type) { - s("public flash.display.BitmapData::copyPixels - Merge Alpha"); + k = a._view; + f = this._view; + a._type !== this._type && l("public flash.display.BitmapData::copyPixels - Color Format Conversion"); + if (g && 1 !== this._type) { + p("public flash.display.BitmapData::copyPixels - Merge Alpha"); } else { if (null === this._solidFillColorPBGRA || this._solidFillColorPBGRA !== a._solidFillColorPBGRA) { - null !== a._solidFillColorPBGRA && 255 === (a._solidFillColorPBGRA & 255) && (f = !1); - if (f) { - this._copyPixelsAndMergeAlpha(g, n, m, h, l, p, k, e, c, b); + null !== a._solidFillColorPBGRA && 255 === (a._solidFillColorPBGRA & 255) && (g = !1); + if (g) { + this._copyPixelsAndMergeAlpha(k, m, h, e, f, r, n, c, d, b); } else { - if (a = m * h + n | 0, f = k * e + p | 0, 0 === (c & 3)) { - for (n = 0;n < b;n = n + 1 | 0) { - for (m = 0;m < c;m = m + 4 | 0) { - l[f + m + 0 | 0] = g[a + m + 0 | 0], l[f + m + 1 | 0] = g[a + m + 1 | 0], l[f + m + 2 | 0] = g[a + m + 2 | 0], l[f + m + 3 | 0] = g[a + m + 3 | 0]; + if (a = h * e + m | 0, g = n * c + r | 0, 0 === (d & 3)) { + for (m = 0;m < b;m = m + 1 | 0) { + for (h = 0;h < d;h = h + 4 | 0) { + f[g + h + 0 | 0] = k[a + h + 0 | 0], f[g + h + 1 | 0] = k[a + h + 1 | 0], f[g + h + 2 | 0] = k[a + h + 2 | 0], f[g + h + 3 | 0] = k[a + h + 3 | 0]; } - a = a + h | 0; - f = f + e | 0; + a = a + e | 0; + g = g + c | 0; } } else { - for (n = 0;n < b;n = n + 1 | 0) { - for (m = 0;m < c;m = m + 1 | 0) { - l[f + m | 0] = g[a + m | 0]; + for (m = 0;m < b;m = m + 1 | 0) { + for (h = 0;h < d;h = h + 1 | 0) { + f[g + h | 0] = k[a + h | 0]; } - a = a + h | 0; - f = f + e | 0; + a = a + e | 0; + g = g + c | 0; } } } @@ -28105,71 +34973,71 @@ } } }; - t.prototype._copyPixelsAndMergeAlpha = function(a, b, c, d, h, e, f, l, g, n) { - b = c * d + b | 0; - e = f * l + e | 0; - for (f = 0;f < n;f = f + 1 | 0) { - for (c = 0;c < g;c = c + 1 | 0) { - var m = a[b + c | 0], p = m & 255; - if (255 === p) { - h[e + c | 0] = m; + v.prototype._copyPixelsAndMergeAlpha = function(a, b, d, e, c, g, f, k, m, l) { + b = d * e + b | 0; + g = f * k + g | 0; + for (f = 0;f < l;f = f + 1 | 0) { + for (d = 0;d < m;d = d + 1 | 0) { + var h = a[b + d | 0], r = h & 255; + if (255 === r) { + c[g + d | 0] = h; } else { - if (0 !== p) { - var k = m & 16711935, m = m >> 8 & 16711935, r = h[e + c | 0], q = r & 16711935, r = r >> 8 & 16711935, p = 256 - p, q = Math.imul(q, p) >> 8, r = Math.imul(r, p) >> 8; - h[e + c | 0] = (m + r & 16711935) << 8 | k + q & 16711935; + if (0 !== r) { + var n = h & 16711935, h = h >> 8 & 16711935, q = c[g + d | 0], w = q & 16711935, q = q >> 8 & 16711935, r = 256 - r, w = Math.imul(w, r) >> 8, q = Math.imul(q, r) >> 8; + c[g + d | 0] = (h + q & 16711935) << 8 | n + w & 16711935; } } } - b = b + d | 0; - e = e + l | 0; + b = b + e | 0; + g = g + k | 0; } }; - t.prototype.dispose = function() { + v.prototype.dispose = function() { this._rect.setEmpty(); this._view = null; this._invalidate(); }; - t.prototype.draw = function(a, b, c, h, e, f) { - "undefined" === typeof b && (b = null); - "undefined" === typeof c && (c = null); - "undefined" === typeof h && (h = null); - "undefined" === typeof e && (e = null); - "undefined" === typeof f && (f = !1); - d("public flash.display.BitmapData::draw"); - var l = p.instance.globals["Shumway.Player.Utils"]; + v.prototype.draw = function(a, b, d, e, c, g) { + void 0 === b && (b = null); + void 0 === d && (d = null); + void 0 === e && (e = null); + void 0 === c && (c = null); + void 0 === g && (g = !1); + l("public flash.display.BitmapData::draw"); + var f = q.instance.globals["Shumway.Player.Utils"]; b && (b = b.clone().toTwipsInPlace()); - l.drawToBitmap(this, a, b, c, h, e, f); + f.drawToBitmap(this, a, b, d, e, c, g); this._isRemoteDirty = !0; }; - t.prototype.drawWithQuality = function(a, b, d, h, e, f, l) { - "undefined" === typeof h && (h = null); - "undefined" === typeof l && (l = null); - c(h); - c(l); - s("public flash.display.BitmapData::drawWithQuality"); - }; - t.prototype.fillRect = function(a, b) { - var c = this._transparent ? q(b) : b | 4278190080; - m(1 === this._type); - var c = e(c), d = this._getTemporaryRectangleFrom(this._rect).intersectInPlace(a); - if (!d.isEmpty() && (this._ensureBitmapData(), this._solidFillColorPBGRA !== c)) { - var h = this._view; - if (d.equals(this._rect)) { - var f = h.length; - if (0 === (f & 3)) { - for (var l = 0;l < f;l = l + 4 | 0) { - h[l + 0 | 0] = c, h[l + 1 | 0] = c, h[l + 2 | 0] = c, h[l + 3 | 0] = c; + v.prototype.drawWithQuality = function(a, b, d, e, c, g, f) { + void 0 === e && (e = null); + void 0 === f && (f = null); + m(e); + m(f); + p("public flash.display.BitmapData::drawWithQuality"); + }; + v.prototype.fillRect = function(a, b) { + this._ensureBitmapData(); + u(1 === this._type); + var d = this._transparent ? k(b) : b | 4278190080, d = n(d), e = this._getTemporaryRectangleFrom(this._rect).intersectInPlace(a); + if (!e.isEmpty() && this._solidFillColorPBGRA !== d) { + var c = this._view; + if (e.equals(this._rect)) { + var g = c.length | 0; + if (0 === (g & 3)) { + for (var f = 0;f < g;f += 4) { + c[f] = d, c[f + 1] = d, c[f + 2] = d, c[f + 3] = d; } } else { - for (l = 0;l < f;l = l + 1 | 0) { - h[l] = c; + for (f = 0;f < g;f++) { + c[f] = d; } } - this._solidFillColorPBGRA = c; + this._solidFillColorPBGRA = d; } else { - for (var f = d.x, l = d.x + d.width, g = d.y + d.height, n = this._rect.width, d = d.y;d < g;d = d + 1 | 0) { - for (var p = d * n | 0, k = f;k < l;k = k + 1 | 0) { - h[p + k | 0] = c; + for (var g = e.x | 0, f = e.x + e.width | 0, m = e.y + e.height | 0, l = this._rect.width | 0, e = e.y | 0;e < m;e++) { + for (var h = e * l | 0, r = g;r < f;r++) { + c[h + r] = d; } } this._solidFillColorPBGRA = null; @@ -28177,2541 +35045,2579 @@ this._invalidate(); } }; - t.prototype.floodFill = function(a, b, c) { - s("public flash.display.BitmapData::floodFill"); + v.prototype.floodFill = function(a, b, d) { + p("public flash.display.BitmapData::floodFill"); }; - t.prototype.generateFilterRect = function(a, b) { - s("public flash.display.BitmapData::generateFilterRect"); + v.prototype.generateFilterRect = function(a, b) { + p("public flash.display.BitmapData::generateFilterRect"); }; - t.prototype.getColorBoundsRect = function(a, b, c) { - s("public flash.display.BitmapData::getColorBoundsRect"); + v.prototype.getColorBoundsRect = function(a, b, d) { + p("public flash.display.BitmapData::getColorBoundsRect"); }; - t.prototype.getPixels = function(a) { - var b = new f.utils.ByteArray; + v.prototype.getPixels = function(a) { + var b = new h.utils.ByteArray; this.copyPixelsToByteArray(a, b); return b; }; - t.prototype.copyPixelsToByteArray = function(a, b) { - var c = this._getPixelData(a); - c && b.writeRawBytes(new Uint8Array(c)); + v.prototype.copyPixelsToByteArray = function(a, b) { + var d = this._getPixelData(a); + d && b.writeRawBytes(new Uint8Array(d)); }; - t.prototype.getVector = function(a) { - var b = new g.Uint32Vector(c.length), c = this._getPixelData(a); - if (!c) { - return b; + v.prototype.getVector = function(b) { + var d = new a.Uint32Vector(e.length), e = this._getPixelData(b); + if (!e) { + return d; } - b.length = c.length; - b._view().set(c); - return b; + d.length = e.length; + d._view().set(e); + return d; }; - t.prototype.hitTest = function(a, b, c, d, h) { - s("public flash.display.BitmapData::hitTest"); + v.prototype.hitTest = function(a, b, d, e, c) { + p("public flash.display.BitmapData::hitTest"); }; - t.prototype.merge = function(a, b, c, h, e, f, l) { - d("public flash.display.BitmapData::merge"); + v.prototype.merge = function(a, b, d, e, c, g, f) { + l("public flash.display.BitmapData::merge"); }; - t.prototype.noise = function(a, b, c, h, e) { - d("public flash.display.BitmapData::noise"); + v.prototype.noise = function(a, b, d, e, c) { + l("public flash.display.BitmapData::noise"); }; - t.prototype.paletteMap = function(a, b, c, h, e, f, l) { - d("public flash.display.BitmapData::paletteMap"); + v.prototype.paletteMap = function(a, b, d, e, c, g, f) { + l("public flash.display.BitmapData::paletteMap"); }; - t.prototype.perlinNoise = function(a, b, c, h, e, f, l, g, n) { - d("public flash.display.BitmapData::perlinNoise"); + v.prototype.perlinNoise = function(a, b, d, e, c, g, f, k, m) { + l("public flash.display.BitmapData::perlinNoise"); }; - t.prototype.pixelDissolve = function(a, b, c, d, h, e) { - s("public flash.display.BitmapData::pixelDissolve"); + v.prototype.pixelDissolve = function(a, b, d, e, c, g) { + p("public flash.display.BitmapData::pixelDissolve"); }; - t.prototype.scroll = function(a, b) { - s("public flash.display.BitmapData::scroll"); + v.prototype.scroll = function(a, b) { + p("public flash.display.BitmapData::scroll"); }; - t.prototype.setPixels = function(a, b) { + v.prototype.setPixels = function(a, b) { this._putPixelData(a, new Int32Array(b.readRawBytes())); }; - t.prototype.setVector = function(a, b) { + v.prototype.setVector = function(a, b) { this._putPixelData(a, b._view()); }; - t.prototype.threshold = function(a, b, d, h, e, f, l, g) { - c(h); - s("public flash.display.BitmapData::threshold"); + v.prototype.threshold = function(a, b, d, e, c, g, f, k) { + m(e); + p("public flash.display.BitmapData::threshold"); }; - t.prototype.lock = function() { + v.prototype.lock = function() { this._locked = !0; }; - t.prototype.unlock = function(a) { + v.prototype.unlock = function(a) { this._locked = !1; }; - t.prototype.histogram = function(a) { - s("public flash.display.BitmapData::histogram"); + v.prototype.histogram = function(a) { + p("public flash.display.BitmapData::histogram"); }; - t.prototype.encode = function(a, b, c) { - s("public flash.display.BitmapData::encode"); + v.prototype.encode = function(a, b, d) { + p("public flash.display.BitmapData::encode"); }; - t.prototype._ensureBitmapData = function() { - var c = this._data; + v.prototype._ensureBitmapData = function() { if (this._isRemoteDirty) { - var d = b.AVM2.Runtime.AVM2.instance.globals["Shumway.Player.Utils"].requestBitmapData(this); - this._data = new Uint8Array(d.getBytes()); - this._type = 3; - this._view = new Int32Array(this._data.buffer); + var a = c.AVM2.Runtime.AVM2.instance.globals["Shumway.Player.Utils"].requestBitmapData(this); + this._setData(a.getBytes(), 3); this._isDirty = this._isRemoteDirty = !1; this._solidFillColorPBGRA = null; } - switch(this._type) { - case 5: - ; - case 4: - ; - case 6: - b.Debug.somewhatImplemented("Image conversion " + b.ImageType[this._type] + " -> " + b.ImageType[1]); + u(!(4 === this._type || 5 === this._type || 6 === this._type)); + 1 !== this._type && (c.ColorUtilities.convertImage(this._type, 1, this._view, this._view), this._type = 1, this._solidFillColorPBGRA = null); + u(this._data); + u(this._dataBuffer); + u(this._view); + }; + v.classInitializer = function() { + }; + v.initializer = function(a) { + this._symbol = a; + }; + v.classSymbols = null; + v.instanceSymbols = null; + v.MAXIMUM_WIDTH = 8191; + v.MAXIMUM_HEIGHT = 8191; + v.MAXIMUM_DIMENSION = 16777215; + v._temporaryRectangles = [new h.geom.Rectangle, new h.geom.Rectangle, new h.geom.Rectangle]; + return v; + }(a.ASNative); + v.BitmapData = r; + r = function(a) { + function b(d) { + a.call(this, d, h.display.BitmapData, !1); + this.ready = !1; + } + __extends(b, a); + b.FromData = function(a) { + var d = new b(a); + d.width = a.width || -1; + d.height = a.height || -1; + d.syncId = h.display.DisplayObject.getNextSyncID(); + d.data = a.data; + switch(a.mimeType) { + case "application/octet-stream": + d.type = a.dataType; + d.ready = !0; + break; + case "image/jpeg": + d.type = 4; + break; + case "image/png": + d.type = 5; + break; + case "image/gif": + d.type = 6; break; default: - if (1 !== this._type) { - var d = new Uint8Array(this._rect.width * this._rect.height * 4), h = new Int32Array(d.buffer); - b.ColorUtilities.convertImage(this._type, 1, this._view, h); - this._data = d; - this._view = h; - this._type = 1; - this._solidFillColorPBGRA = null; - } - ; - } - 1 !== this._type && (this._data = new Uint8Array(this._rect.width * this._rect.height * 4), this._view = new Int32Array(this._data.buffer), this._type = 1, this._fillWithDebugData(), this._solidFillColorPBGRA = null); - c !== this._data && (this._dataBuffer = a.FromArrayBuffer(this._data.buffer)); - }; - t.prototype._fillWithDebugData = function() { - var a = this._view; - e(4294928820); - for (var b = this._rect.width, c = this._rect.height, d = 0, h = 0;h < c;h++) { - for (var f = 0;f < b;f++) { - a[d++] = e(q(-1442840576 | (h & 255) << 16 | (f & 255) << 8 | 255)); - } + p(a.mimeType); } + return d; }; - t.classInitializer = function() { + b.prototype.getSharedInstance = function() { + return this.sharedInstance || this.createSharedInstance(); }; - t.initializer = function(a) { - this._symbol = a; + b.prototype.createSharedInstance = function() { + u(this.ready); + this.sharedInstance = this.symbolClass.initializeFrom(this); + this.symbolClass.instanceConstructorNoInitialize.call(this.sharedInstance); + return this.sharedInstance; + }; + Object.defineProperty(b.prototype, "resolveAssetCallback", {get:function() { + return this._unboundResolveAssetCallback.bind(this); + }, enumerable:!0, configurable:!0}); + b.prototype._unboundResolveAssetCallback = function(a) { + u(!this.ready); + this.ready = !0; + a ? (u(a.width), u(a.height), this.width = a.width, this.height = a.height) : c.Debug.error("Error while decoding image"); }; - t.classSymbols = null; - t.instanceSymbols = null; - t.MAXIMUM_WIDTH = 8191; - t.MAXIMUM_HEIGHT = 8191; - t.MAXIMUM_DIMENSION = 16777215; - t._temporaryRectangles = [new f.geom.Rectangle, new f.geom.Rectangle, new f.geom.Rectangle]; - return t; - }(g.ASNative); - t.BitmapData = h; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.BitmapDataChannel"); + return b; + }(c.Timeline.DisplaySymbol); + v.BitmapSymbol = r; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.BitmapDataChannel"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.RED = 1; - a.GREEN = 2; - a.BLUE = 4; - a.ALPHA = 8; - return a; - }(g.ASNative); - f.BitmapDataChannel = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.BitmapEncodingColorSpace"); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.RED = 1; + e.GREEN = 2; + e.BLUE = 4; + e.ALPHA = 8; + return e; + }(a.ASNative); + h.BitmapDataChannel = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.BitmapEncodingColorSpace"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.COLORSPACE_AUTO = "auto"; - a.COLORSPACE_4_4_4 = "4:4:4"; - a.COLORSPACE_4_2_2 = "4:2:2"; - a.COLORSPACE_4_2_0 = "4:2:0"; - return a; - }(g.ASNative); - f.BitmapEncodingColorSpace = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -var Shumway$$inline_605 = Shumway || (Shumway = {}), AVM2$$inline_606 = Shumway$$inline_605.AVM2 || (Shumway$$inline_605.AVM2 = {}), AS$$inline_607 = AVM2$$inline_606.AS || (AVM2$$inline_606.AS = {}), flash$$inline_608 = AS$$inline_607.flash || (AS$$inline_607.flash = {}), display$$inline_609 = flash$$inline_608.display || (flash$$inline_608.display = {}), display$$inline_610 = flash$$inline_608.display, flash$$inline_611 = AS$$inline_607.flash, AS$$inline_612 = AVM2$$inline_606.AS, AVM2$$inline_613 = -Shumway$$inline_605.AVM2; -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a) { - k("Dummy Constructor: public flash.display.JPEGEncoderOptions"); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.COLORSPACE_AUTO = "auto"; + e.COLORSPACE_4_4_4 = "4:4:4"; + e.COLORSPACE_4_2_2 = "4:2:2"; + e.COLORSPACE_4_2_0 = "4:2:0"; + return e; + }(a.ASNative); + h.BitmapEncodingColorSpace = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e(a) { + s("public flash.display.JPEGEncoderOptions"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.JPEGEncoderOptions = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(t) { - function s() { - return q.instance.globals["Shumway.Player.Utils"]; - } - var m = b.Debug.assert, d = b.Debug.warning, a = b.Debug.assertUnreachable, c = b.Debug.notImplemented, n = b.AVM2.Runtime.throwError, p = b.FileLoadingService, e = b.Telemetry, q = b.AVM2.Runtime.AVM2, l = b.AVM2.ABC.AbcFile, u = f.events, w = f.display.ActionScriptVersion, r = f.system.ApplicationDomain, h = b.Bounds, x; + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.ASNative); + h.JPEGEncoderOptions = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(v) { + var p = c.Debug.assert, u = c.AVM2.Runtime.throwError, l = h.display.ActionScriptVersion, e = c.AVM2.Runtime.AVM2, m = h.events, t = c.FileLoader, q = c.AVM2.ABC.AbcFile, n = c.SWF.SWFFile, k; (function(a) { a[a.Unloaded = 0] = "Unloaded"; a[a.Opened = 1] = "Opened"; a[a.Initialized = 2] = "Initialized"; a[a.Complete = 3] = "Complete"; - })(x || (x = {})); - var y; + })(k || (k = {})); + var f; (function(a) { a[a.External = 0] = "External"; a[a.Bytes = 1] = "Bytes"; - })(y || (y = {})); - x = function(x) { - function y() { - t.DisplayObjectContainer.instanceConstructorNoInitialize.call(this); - this._writer = new b.IndentingWriter; + })(f || (f = {})); + k = function(d) { + function b() { + v.DisplayObjectContainer.instanceConstructorNoInitialize.call(this); this._content = null; - this._contentLoaderInfo = new f.display.LoaderInfo; - this._worker = null; - this._loadStatus = 0; + b._rootLoader && (this._contentID = v.DisplayObject._instanceID++); + this._contentLoaderInfo = new v.LoaderInfo(v.LoaderInfo.CtorToken); this._contentLoaderInfo._loader = this; - this._initialDataLoaded = new b.PromiseWrapper; - this._waitForInitialData = !0; - this._commitDataQueue = this._initialDataLoaded.promise; - this._codeExecutionPromise = new b.PromiseWrapper; - this._progressPromise = new b.PromiseWrapper; - this._startPromise = Promise.all([this._codeExecutionPromise.promise, this._progressPromise.promise]); - } - __extends(y, x); - y.getRootLoader = function() { - if (y._rootLoader) { - return y._rootLoader; + this._fileLoader = null; + this._loadStatus = 0; + } + __extends(b, d); + b.getRootLoader = function() { + if (b._rootLoader) { + return b._rootLoader; } - var a = new f.display.Loader; - f.display.DisplayObject._instanceID--; + var a = new h.display.Loader; + h.display.DisplayObject._instanceID--; a._contentLoaderInfo._loader = null; - a._loadStatus = 1; - return y._rootLoader = a; + return b._rootLoader = a; }; - y.reset = function() { - y._rootLoader = null; + b.reset = function() { + b._loadQueue.forEach(function(a) { + return a.unload(); + }); + b.classInitializer(); }; - y.progress = function() { - y.FlushCommittedFrames(); - for (var b = y._loadQueue, c = 0;c < b.length;c++) { - var d = b[c], h = d._contentLoaderInfo, e = h._bytesLoaded, f = h._bytesTotal; - switch(d._loadStatus) { - case 0: - if (!f) { - break; - } - 0 === d._loadingType && h.dispatchEvent(u.Event.getInstance(u.Event.OPEN)); - h.dispatchEvent(new u.ProgressEvent(u.ProgressEvent.PROGRESS, !1, !1, 0, f)); - d._loadStatus = 1; - case 1: - h._bytesLoadedChanged && (h._bytesLoadedChanged = !1, h.dispatchEvent(new u.ProgressEvent(u.ProgressEvent.PROGRESS, !1, !1, e, f))); - if (!d._content || !d._content._hasFlags(256)) { - break; - } - d._loadStatus = 2; - h.dispatchEvent(u.Event.getInstance(u.Event.INIT)); - h._actionScriptVersion === w.ACTIONSCRIPT2 && u.EventDispatcher.broadcastEventDispatchQueue.dispatchEvent(u.Event.getBroadcastInstance(u.Event.AVM1_LOAD)); - case 2: - e === f && (d._loadStatus = 3, h.dispatchEvent(u.Event.getInstance(u.Event.COMPLETE))); - break; - case 3: - b.splice(c--, 1); - break; - default: - a("Mustn't encounter unhandled status in Loader queue."); + b.processEvents = function() { + b.processEarlyEvents(); + b.processLateEvents(); + }; + b.processEarlyEvents = function() { + for (var a = b._loadQueue, d = 0;d < a.length;d++) { + var e = a[d]; + p(3 !== e._loadStatus); + var f = e._contentLoaderInfo, k = e._imageSymbol; + if (f._file instanceof c.ImageFile) { + if (!k || !k.ready || e._queuedLoadUpdate) { + continue; + } + p(f.bytesLoaded === f.bytesTotal); + e._applyDecodedImage(k); + p(e._content); } + 1 === e._loadStatus && e._content && (f.dispatchEvent(m.Event.getInstance(m.Event.INIT)), e._loadStatus = 2, e === b._rootLoader && f.dispatchEvent(new m.ProgressEvent(m.ProgressEvent.PROGRESS, !1, !1, f.bytesLoaded, f.bytesTotal))); + 2 === e._loadStatus && f.bytesLoaded === f.bytesTotal && (a.splice(d--, 1), p(-1 === a.indexOf(e)), e._loadStatus = 3, f.dispatchEvent(m.Event.getInstance(m.Event.COMPLETE))); } }; - y.FlushCommittedFrames = function() { - for (var a = y._commitFrameQueue, b = 0;b < a.length;b++) { - a[b].loader._commitFrame(a[b].data); + b.processLateEvents = function() { + for (var a = b._loadQueue, d = 0;d < a.length;d++) { + var e = a[d]; + p(3 !== e._loadStatus); + var c = e._contentLoaderInfo, f = e._queuedLoadUpdate, k = c._bytesTotal; + if (f && k || 1 === e._loadStatus) { + e._queuedLoadUpdate = null, 0 === e._loadStatus && (0 === e._loadingType && c.dispatchEvent(m.Event.getInstance(m.Event.OPEN)), c.dispatchEvent(new m.ProgressEvent(m.ProgressEvent.PROGRESS, !1, !1, 0, k)), e._loadStatus = 1), f && (e._applyLoadUpdate(f), c.dispatchEvent(new m.ProgressEvent(m.ProgressEvent.PROGRESS, !1, !1, f.bytesLoaded, k))); + } } - a.length = 0; }; - y.prototype._initFrame = function(a) { + b.prototype._setStage = function(a) { + p(this === b.getRootLoader()); + this._stage = a; }; - y.prototype._constructFrame = function() { - this._constructChildren(); + b.prototype._initFrame = function(a) { }; - y.prototype._describeData = function(a) { - var c = [], d; - for (d in a) { - c.push(d + ":" + b.StringUtilities.toSafeString(a[d])); - } - return "{" + c.join(", ") + "}"; + b.prototype._constructFrame = function() { + this === b.getRootLoader() && this._content ? (v.DisplayObject._advancableInstances.remove(this), this._children[0] = this._content, this._constructChildren(), this._children.length = 0) : this._constructChildren(); }; - y.prototype._commitData = function(a) { - !this._waitForInitialData || "progress" !== a.command && "image" !== a.command && "error" !== a.command || (this._waitForInitialData = !1, this._initialDataLoaded.resolve(void 0)); - var b = this._commitDataQueue.then(this._commitQueuedData.bind(this, a)); - g.traceLoaderOption.value && this._writer.writeTimeLn("Making for: " + this._describeData(a)); - this._commitDataQueue = b; - }; - y.prototype._commitQueuedData = function(a) { - var c = this._contentLoaderInfo, d = a.command, h = null; - g.traceLoaderOption.value && this._writer.writeTimeLn("Executing Promise: " + this._describeData(a)); - switch(d) { - case "init": - d = a.result; - c.bytesLoaded = d.bytesLoaded; - c._bytesTotal = d.bytesTotal; - c._swfVersion = d.swfVersion; - c._frameRate = d.frameRate; - var f = d.bbox; - c._width = f.xMax - f.xMin; - c._height = f.yMax - f.yMin; - f = new b.Timeline.SpriteSymbol(0, !0); - f.numFrames = d.frameCount; - c.registerSymbol(f); - d.fileAttributes && d.fileAttributes.doAbc || (c._actionScriptVersion = w.ACTIONSCRIPT2, h = this._initAvm1(), g.traceLoaderOption.value && this._writer.writeTimeLn("Suspending until AVM1 is initialized.")); - break; - case "progress": - var d = a.result, f = d.bytesLoaded, n = d.bytesTotal; - m(f <= n, "Loaded bytes should not exceed total bytes."); - c.bytesLoaded = f; - c._bytesTotal ? m(c._bytesTotal === n, "Total bytes should not change.") : c._bytesTotal = n; - d.open && 0 === this._loadStatus && (this._loadStatus = 1); - 0 !== this._loadStatus && (c.dispatchEvent(new u.ProgressEvent(u.ProgressEvent.PROGRESS, !1, !1, f, n)), this._progressPromise.resolve(void 0), g.traceLoaderOption.value && this._writer.writeTimeLn("Resolving progress promise.")); - break; - case "complete": - a.stats && e.instance.reportTelemetry(a.stats); - this._worker && this._worker.terminate(); - break; - case "error": - this._contentLoaderInfo.dispatchEvent(new u.IOErrorEvent(u.IOErrorEvent.IO_ERROR)); - break; - default: - if (0 === a.id) { - break; - } - if (a.isSymbol) { - this._commitAsset(a); - } else { - if ("frame" === a.type) { - if (this._frameAssetsQueue) { - g.traceLoaderOption.value && this._writer.writeTimeLn("Suspending frame execution until all of its assets are resolved."); - var k = this, h = Promise.all(this._frameAssetsQueue).then(function() { - k._enqueueFrame(a); - k._frameAssetsQueue = null; - }.bind(this)); - } else { - this._enqueueFrame(a); - } - } else { - "image" === a.type ? this._commitImage(a) : "abc" === a.type && (d = q.instance.applicationDomain, f = new l(a.data, a.name), a.flags ? d.loadAbc(f) : c._allowCodeExecution && d.executeAbc(f)); - } - } - ; - } - return h; + b.prototype.addChild = function(a) { + u("IllegalOperationError", e.Errors.InvalidLoaderMethodError); + return null; }; - y.prototype._initAvm1 = function() { - var a = this._contentLoaderInfo; - return this._loaderInfo && this._loaderInfo._avm1Context ? (a._avm1Context = this._loaderInfo._avm1Context, null) : q.instance.loadAVM1().then(function() { - a._avm1Context = b.AVM1.AVM1Context.create(a.swfVersion); - }); + b.prototype.addChildAt = function(a, b) { + u("IllegalOperationError", e.Errors.InvalidLoaderMethodError); + return null; }; - y.prototype._commitAsset = function(a) { - var c = this._contentLoaderInfo, d = a.id, e; - if (a.updates) { - a = a.updates, e = c.getSymbolById(d), a.scale9Grid && (e.scale9Grid = h.FromUntyped(a.scale9Grid)); - } else { - switch(a.type) { - case "shape": - e = b.Timeline.ShapeSymbol.FromData(a, c); - break; - case "morphshape": - e = b.Timeline.MorphShapeSymbol.FromData(a, c); - break; - case "image": - var l = e = b.Timeline.BitmapSymbol.FromData(a); - if (5 === l.type || 6 === l.type || 4 === l.type) { - this._frameAssetsQueue || (this._frameAssetsQueue = []), this._frameAssetsQueue.push(new Promise(function(a) { - s().decodeImage(l, a); - })); - } - break; - case "label": - ; - case "text": - e = b.Timeline.TextSymbol.FromTextData(a); - break; - case "button": - e = b.Timeline.ButtonSymbol.FromData(a, c); - break; - case "sprite": - e = b.Timeline.SpriteSymbol.FromData(a, c); - break; - case "font": - e = b.Timeline.FontSymbol.FromData(a); - d = f.text.Font.initializeFrom(e); - f.text.Font.instanceConstructorNoInitialize.call(d); - s().registerFont(d); - "undefined" !== typeof navigator && 0 > navigator.userAgent.indexOf("Firefox") && (this._frameAssetsQueue || (this._frameAssetsQueue = []), this._frameAssetsQueue.push(new Promise(function(a) { - setTimeout(a, 400); - }))); - break; - case "sound": - e = b.Timeline.SoundSymbol.FromData(a); - break; - case "binary": - e = b.Timeline.BinarySymbol.FromData(a); - } - m(e, "Unknown symbol type."); - c.registerSymbol(e); - } + b.prototype.removeChild = function(a) { + u("IllegalOperationError", e.Errors.InvalidLoaderMethodError); + return null; }; - y.prototype._enqueueFrame = function(a) { - this === y.getRootLoader() ? (y.runtimeStartTime = Date.now(), this._commitFrame(a), this._codeExecutionPromise.resolve(void 0)) : y._commitFrameQueue.push({loader:this, data:a}); + b.prototype.removeChildAt = function(a) { + u("IllegalOperationError", e.Errors.InvalidLoaderMethodError); + return null; }; - y.prototype._commitFrame = function(a) { - var c = this._contentLoaderInfo; - void 0 !== a.bgcolor && (c._colorRGBA = a.bgcolor); - if (a.symbolClasses) { - for (var h = a.symbolClasses, e = q.instance.applicationDomain, l = 0;l < h.length;l++) { - var n = h[l]; - if (c._allowCodeExecution) { - var m = c.getSymbolById(n.symbolId); - m ? (n = e.getClass(n.className), n.defaultInitializerArgument = m, m.symbolClass = n) : d("Symbol " + n.symbolId + " is not defined."); - } - } - } - if (a.exports && c._actionScriptVersion === w.ACTIONSCRIPT2) { - for (h = a.exports, l = 0;l < h.length;l++) { - n = h[l], (m = c.getSymbolById(n.symbolId)) ? c._avm1Context.addAsset(n.className, n.symbolId, m) : d("Symbol " + n.symbolId + " is not defined."); - } - } - for (var h = c.getSymbolById(0), l = h.symbolClass, e = h.frames, m = e.length, n = new b.Timeline.FrameDelta(c, a.commands), k = a.repeat;k--;) { - e.push(n); - } - e = this._content; - if (!e) { - e = l.initializeFrom(h); - f.display.DisplayObject._instanceID--; - e._name = "root1"; - if (t.MovieClip.isType(e)) { - if (n = e, a.sceneData) { - for (var k = a.sceneData.scenes, l = 0, p = k.length;l < p;l++) { - var r = k[l], s = r.offset; - n.addScene(r.name, [], s, (l < p - 1 ? k[l + 1].offset : h.numFrames) - s); - } - h = a.sceneData.labels; - for (l = 0;l < h.length;l++) { - k = h[l], n.addFrameLabel(k.name, k.frame + 1); - } - } else { - n.addScene("Scene 1", [], 0, h.numFrames); - } - } - e._loaderInfo = c; - c._actionScriptVersion === w.ACTIONSCRIPT2 ? e = this._content = this._initAvm1Root(e) : this._content = e; - this.addTimelineObjectAtDepth(this._content, 0); - } - t.AVM1Movie.isType(e) && (e = e._children[0]); - t.MovieClip.isType(e) && (l = e, a.labelName && l.addFrameLabel(a.labelName, m + 1), c._actionScriptVersion === w.ACTIONSCRIPT2 && g.avm1lib.getAVM1Object(e).addFrameActionBlocks(m, a), a.soundStream && l._initSoundStream(a.soundStream), a.soundStreamBlock && l._addSoundStreamBlock(m + 1, a.soundStreamBlock)); + b.prototype.setChildIndex = function(a, b) { + u("IllegalOperationError", e.Errors.InvalidLoaderMethodError); }; - y.prototype._initAvm1Root = function(a) { - if (this._loaderInfo && this._loaderInfo._avm1Context) { - return a; - } - var b = g.avm1lib.getAVM1Object(a), c = this._contentLoaderInfo._avm1Context; - c.root = b; - b.context = c; - a.addEventListener("frameConstructed", c.flushPendingScripts.bind(c), !1, Number.MAX_VALUE); - c.globals.asSetPublicProperty("_root", b); - c.globals.asSetPublicProperty("_level0", b); - c = new f.display.AVM1Movie; - c.initializeContent(a); - this._content = c; - a = this._contentLoaderInfo._parameters; - for (var d in a) { - d in b || (b[d] = a[d]); + b.prototype._describeData = function(a) { + var b = [], d; + for (d in a) { + b.push(d + ":" + c.StringUtilities.toSafeString(a[d])); } - return c; - }; - y.prototype._commitImage = function(a) { - a = b.Timeline.BitmapSymbol.FromData(a.props); - var c = f.display.BitmapData.initializeFrom(a); - f.display.BitmapData.instanceConstructorNoInitialize.call(c); - this._content = new f.display.Bitmap(c); - this.addTimelineObjectAtDepth(this._content, 0); - c = this._contentLoaderInfo; - c._width = a.width; - c._height = a.height; - c.dispatchEvent(u.Event.getInstance(u.Event.INIT)); - c.dispatchEvent(u.Event.getInstance(u.Event.COMPLETE)); - this._loadStatus = 3; + return "{" + b.join(", ") + "}"; }; - Object.defineProperty(y.prototype, "content", {get:function() { + Object.defineProperty(b.prototype, "content", {get:function() { return 0 === this._loadStatus ? null : this._content; }, enumerable:!0, configurable:!0}); - Object.defineProperty(y.prototype, "contentLoaderInfo", {get:function() { + Object.defineProperty(b.prototype, "contentLoaderInfo", {get:function() { return this._contentLoaderInfo; }, enumerable:!0, configurable:!0}); - y.prototype._close = function() { - this._worker && 0 === this._loadStatus && (this._worker.terminate(), this._worker = null); + b.prototype._getJPEGLoaderContextdeblockingfilter = function(a) { + return h.system.JPEGLoaderContext.isType(a) ? a.deblockingFilter : 0; + }; + Object.defineProperty(b.prototype, "uncaughtErrorEvents", {get:function() { + return this._uncaughtErrorEvents; + }, enumerable:!0, configurable:!0}); + b.prototype.load = function(d, e) { + this.close(); + this._contentLoaderInfo._url = d.url; + this._applyLoaderContext(e); + this._loadingType = 0; + this._fileLoader = new t(this); + a.traceLoaderOption.value && console.log("Loading url " + d.url); + this._fileLoader.loadFile(d._toFileRequest()); + this._queuedLoadUpdate = null; + p(-1 === b._loadQueue.indexOf(this)); + b._loadQueue.push(this); + }; + b.prototype.loadBytes = function(d, e) { + this.close(); + this._contentLoaderInfo._url = (this.loaderInfo ? this.loaderInfo._url : "") + "/[[DYNAMIC]]/" + ++b._embeddedContentLoadCount; + this._applyLoaderContext(e); + this._loadingType = 1; + this._fileLoader = new t(this); + this._queuedLoadUpdate = null; + a.traceLoaderOption.value && console.log("Loading embedded symbol " + this._contentLoaderInfo._url); + this._fileLoader.loadBytes(new Uint8Array(d.bytes, 0, d.length)); + p(-1 === b._loadQueue.indexOf(this)); + b._loadQueue.push(this); + }; + b.prototype.close = function() { + var a = b._loadQueue.indexOf(this); + -1 < a && b._loadQueue.splice(a, 1); + this._contentLoaderInfo.reset(); + this._fileLoader && (this._fileLoader = null); + }; + b.prototype._unload = function(a, b) { + 2 > this._loadStatus ? this._loadStatus = 0 : (this.close(), this._content = null, this._contentLoaderInfo._loader = null, this._loadStatus = 0, this.dispatchEvent(m.Event.getInstance(m.Event.UNLOAD))); }; - y.prototype._unload = function(a, b) { - 2 > this._loadStatus || (this._content = null, this._worker = this._contentLoaderInfo._loader = null, this._loadStatus = 0, this.dispatchEvent(u.Event.getInstance(u.Event.UNLOAD))); + b.prototype.unload = function() { + this._unload(!1, !1); }; - y.prototype._getJPEGLoaderContextdeblockingfilter = function(a) { - return f.system.JPEGLoaderContext.isType(a) ? a.deblockingFilter : 0; + b.prototype.unloadAndStop = function(a) { + this._unload(!0, !!a); }; - y.prototype._getUncaughtErrorEvents = function() { - c("public flash.display.Loader::_getUncaughtErrorEvents"); + b.prototype._applyLoaderContext = function(a) { + var b = {}; + if (a && a.parameters) { + var d = a.parameters, f; + for (f in d) { + var k = d[f]; + c.isString(k) || u("IllegalOperationError", e.Errors.ObjectWithStringsParamError, "LoaderContext.parameters"); + b[f] = k; + } + } + a && a.applicationDomain && (a = new h.system.ApplicationDomain(h.system.ApplicationDomain.currentDomain), this._contentLoaderInfo._applicationDomain = a); + this._contentLoaderInfo._parameters = b; + }; + b.prototype.onLoadOpen = function(a) { + this._contentLoaderInfo.setFile(a); + }; + b.prototype.onLoadProgress = function(a) { + p(a); + this._queuedLoadUpdate = a; + }; + b.prototype.onNewEagerlyParsedSymbols = function(a, b) { + for (var d = [], e = a.length - b;e < a.length;e++) { + var c = this._contentLoaderInfo.getSymbolById(a[e].id); + c.ready || (p(c.resolveAssetPromise), p(!1 === c.ready), d.push(c.resolveAssetPromise.promise)); + } + return Promise.all(d); + }; + b.prototype.onImageBytesLoaded = function() { + var a = this._contentLoaderInfo._file; + p(a instanceof c.ImageFile); + var a = {id:-1, data:a.data, mimeType:a.mimeType, dataType:a.type, type:"image"}, b = v.BitmapSymbol.FromData(a); + this._imageSymbol = b; + e.instance.globals["Shumway.Player.Utils"].registerFontOrImage(b, a); + p(b.resolveAssetPromise); + p(!1 === b.ready); + }; + b.prototype._applyDecodedImage = function(a) { + a = a.createSharedInstance(); + this._content = new h.display.Bitmap(a); + this._contentLoaderInfo._width = 20 * this._content.width; + this._contentLoaderInfo._height = 20 * this._content.height; + this.addTimelineObjectAtDepth(this._content, 0); }; - y.prototype._setUncaughtErrorEvents = function(a) { - c("public flash.display.Loader::_setUncaughtErrorEvents"); + b.prototype._applyLoadUpdate = function(a) { + var b = this._contentLoaderInfo; + b._bytesLoaded = a.bytesLoaded; + var d = b._file; + if (d instanceof n && 0 !== d.framesLoaded) { + if (b._allowCodeExecution) { + var c = e.instance.applicationDomain, f = d.abcBlocks.length; + if (0 < f - b._abcBlocksLoaded) { + for (a = b._abcBlocksLoaded;a < f;a++) { + var k = d.abcBlocks[a], m = new q(k.data, k.name); + k.flags ? c.loadAbc(m) : c.executeAbc(m); + } + b._abcBlocksLoaded = f; + } + f = d.symbolClassesList.length; + if (0 < f - b._mappedSymbolsLoaded) { + for (a = b._mappedSymbolsLoaded;a < f;a++) { + k = d.symbolClassesList[a], m = c.getClass(k.className), Object.defineProperty(m, "defaultInitializerArgument", {get:b.getSymbolResolver(m, k.id), configurable:!0}); + } + b._mappedSymbolsLoaded = f; + } + } + if (inFirefox && (c = d.fonts.length, 0 < c - b._fontsLoaded)) { + for (a = b._fontsLoaded;a < c;a++) { + h.text.Font.registerEmbeddedFont(d.fonts[a], b); + } + b._fontsLoaded = c; + } + c = b.getRootSymbol(); + f = d.framesLoaded - c.frames.length; + if (0 !== f) { + for ((a = this._content) || (a = this.createContentRoot(c, d.sceneAndFrameLabelData)), d = a, a = 0;a < f;a++) { + k = b.getFrame(null, c.frames.length), d._addFrame(k); + } + } + } }; - y.prototype.load = function(a, b) { - this._contentLoaderInfo._url = a.url; - this._applyLoaderContext(b, a); - this._loadingType = 0; - var c = this._createParsingWorker(), d = this, h = p.instance.createSession(); - h.onprogress = function(a, b) { - c.postMessage({data:a, progress:b}); - }; - h.onerror = function(a) { - d._commitData({command:"error", error:a}); - }; - h.onopen = function() { - c.postMessage("pipe:"); - }; - h.onclose = function() { - c.postMessage({data:null}); - }; - h.open(a._toFileRequest()); - y._loadQueue.push(this); + b.prototype.onLoadComplete = function() { }; - y.prototype.loadBytes = function(a) { - this._contentLoaderInfo._url = (this.loaderInfo ? this.loaderInfo._url : "") + "/[[DYNAMIC]]/" + ++y._embeddedContentLoadCount; - this._applyLoaderContext(void 0, null); - this._loadingType = 1; - var b = this._createParsingWorker(); - y._loadQueue.push(this); - b.postMessage("pipe:"); - a = a.bytes; - b.postMessage({data:a, progress:{bytesLoaded:a.byteLength, bytesTotal:a.byteLength}}); - b.postMessage({data:null}); + b.prototype.onLoadError = function() { + c.Debug.warning("Not implemented: flash.display.Loader loading-error handling"); }; - y.prototype._applyLoaderContext = function(a, c) { - var d = {}; - if (a && a.parameters) { - var h = a.parameters, e; - for (e in h) { - var f = h[e]; - b.isString(f) || n("IllegalOperationError", k.Errors.ObjectWithStringsParamError, "LoaderContext.parameters"); - d[e] = f; - } - } - a && a.applicationDomain && (this._contentLoaderInfo._applicationDomain = new r(r.currentDomain)); - this._contentLoaderInfo._parameters = d; - }; - y.prototype._createParsingWorker = function() { - var a = this._contentLoaderInfo, c; - c = !y.WORKERS_AVAILABLE || b.useParsingWorkerOption && !b.useParsingWorkerOption.value ? new b.SWF.ResourceLoader(window, !1) : new Worker("undefined" !== typeof LOADER_WORKER_PATH ? LOADER_WORKER_PATH : SHUMWAY_ROOT + y.LOADER_PATH); - a._allowCodeExecution || this._codeExecutionPromise.reject("Disabled by _allowCodeExecution"); - this._waitForInitialData || this._initialDataLoaded.resolve(void 0); - var d = this; - c.onmessage = function(a) { - "exception" === a.data.type ? (console.log("error in parser: \n" + a.data.stack), q.instance.exceptions.push({source:"parser", message:a.data.message, stack:a.data.stack})) : d._commitData(a.data); - }; - return c; + b.prototype.createContentRoot = function(a, d) { + a.isAVM1Object && this._initAvm1(a); + var e = a.symbolClass.initializeFrom(a); + h.display.DisplayObject._instanceID--; + e._name = this === b._rootLoader ? "root1" : "instance" + this._contentID; + if (v.MovieClip.isType(e)) { + var c = e; + if (d) { + for (var f = d.scenes, k = 0, m = f.length;k < m;k++) { + var n = f[k], q = n.offset; + c.addScene(n.name, [], q, (k < m - 1 ? f[k + 1].offset : a.numFrames) - q); + } + f = d.labels; + for (k = 0;k < f.length;k++) { + m = f[k], c.addFrameLabel(m.name, m.frame + 1); + } + } else { + c.addScene("Scene 1", [], 0, a.numFrames); + } + } + c = this._contentLoaderInfo; + e._loaderInfo = c; + k = e; + c.actionScriptVersion === l.ACTIONSCRIPT2 ? e = this._initAvm1Root(e) : this === b.getRootLoader() && (v.MovieClip.frameNavigationModel = 10 > c.swfVersion ? 9 : 10); + this._content = e; + this === b.getRootLoader() ? (b.runtimeStartTime = Date.now(), this._stage.setRoot(e)) : this.addTimelineObjectAtDepth(e, 0); + return k; + }; + b.prototype._initAvm1 = function(a) { + var d = this._contentLoaderInfo, e; + this.loaderInfo && this.loaderInfo._avm1Context ? e = d._avm1Context = this.loaderInfo._avm1Context : (c.AVM1.Lib.installObjectMethods(), e = c.AVM1.AVM1Context.create(d), d._avm1Context = e, this === b.getRootLoader() && (e.globals.Key._bind(this._stage, e), e.globals.Mouse._bind(this._stage, e), v.MovieClip.frameNavigationModel = 1)); + a.avm1Context = e; + }; + b.prototype._initAvm1Root = function(a) { + var b = this._contentLoaderInfo._avm1Context, d = c.AVM1.Lib.getAVM1Object(a, b); + if (this.loaderInfo && this.loaderInfo._avm1Context) { + return d.context = this.loaderInfo._avm1Context, a; + } + b.root = d; + a.addEventListener("frameConstructed", b.flushPendingScripts.bind(b), !1, Number.MAX_VALUE); + a = new h.display.AVM1Movie(a); + var b = this._contentLoaderInfo._parameters, e; + for (e in b) { + e in d || (d[e] = b[e]); + } + return a; }; - y._embeddedContentLoadCount = 0; - y.classInitializer = function() { - y._rootLoader = null; - y._loadQueue = []; - }; - y.initializer = function() { - t.DisplayObject._advancableInstances.push(this); - }; - y.classSymbols = null; - y.instanceSymbols = null; - y.WORKERS_AVAILABLE = "undefined" !== typeof Worker; - y.LOADER_PATH = "swf/worker.js"; - y.runtimeStartTime = 0; - y._commitFrameQueue = []; - return y; - }(f.display.DisplayObjectContainer); - t.Loader = x; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.assert, m = b.Debug.notImplemented, d = b.Debug.somewhatImplemented, a = f.display.ActionScriptVersion, c = function(c) { - function g() { - f.events.EventDispatcher.instanceConstructorNoInitialize.call(this); - this._url = this._loaderURL = ""; - this._isURLInaccessible = !1; - this._bytesLoaded = 0; - this._bytesLoadedChanged = !1; - this._bytesTotal = 0; - this._applicationDomain = null; - this._swfVersion = 9; - this._actionScriptVersion = a.ACTIONSCRIPT3; - k(this._actionScriptVersion); - this._frameRate = 24; - this._parameters = null; + b.classInitializer = function() { + b._rootLoader = null; + b._loadQueue = []; + b.runtimeStartTime = 0; + b._embeddedContentLoadCount = 0; + }; + b.initializer = function() { + v.DisplayObject._advancableInstances.push(this); + }; + b.classSymbols = null; + b.instanceSymbols = null; + return b; + }(h.display.DisplayObjectContainer); + v.Loader = k; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(v) { + var p = c.Debug.assert, u = c.Debug.notImplemented, l = c.Debug.somewhatImplemented, e = c.SWF.SWFFile, m = function(m) { + function q(e) { + e !== q.CtorToken && throwError("ArgumentError", h.Errors.CantInstantiateError, "LoaderInfo$"); + a.events.EventDispatcher.instanceConstructorNoInitialize.call(this); + this._loader = null; + this.reset(); + } + __extends(q, m); + q.prototype.reset = function() { + this._url = ""; + this._file = null; + this._bytesTotal = this._bytesLoaded = 0; + this._parameters = this._applicationDomain = null; this._height = this._width = 0; - this._contentType = ""; - this._childSandboxBridge = this._parentSandboxBridge = this._sharedEvents = null; - this._parentAllowsChild = this._childAllowsParent = this._sameDomain = !1; - this._uncaughtErrorEvents = this._bytes = this._content = this._loader = null; + this._uncaughtErrorEvents = this._bytes = this._content = this._childSandboxBridge = this._parentSandboxBridge = this._sharedEvents = null; this._allowCodeExecution = !0; this._dictionary = []; + this._fontsLoaded = this._mappedSymbolsLoaded = this._abcBlocksLoaded = 0; this._avm1Context = null; - this._colorRGBA = 4294967295; - } - __extends(g, c); - g.getLoaderInfoByDefinition = function(a) { - m("public flash.display.LoaderInfo::static getLoaderInfoByDefinition"); }; - Object.defineProperty(g.prototype, "loaderURL", {get:function() { - return this._loaderURL; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "url", {get:function() { - return this._url; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "isURLInaccessible", {get:function() { - return this._isURLInaccessible; + q.prototype.setFile = function(a) { + p(!this._file); + this._file = a; + this._bytesTotal = a.bytesTotal; + a instanceof e ? (a = a.bounds, this._width = a.xMax - a.xMin, this._height = a.yMax - a.yMin) : p(a instanceof c.ImageFile); + }; + q.getLoaderInfoByDefinition = function(a) { + u("public flash.display.LoaderInfo::static getLoaderInfoByDefinition"); + }; + Object.defineProperty(q.prototype, "loaderURL", {get:function() { + return this._loader.loaderInfo.url; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(q.prototype, "url", {get:function() { + return this._file ? this._url : null; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(q.prototype, "isURLInaccessible", {get:function() { + l("public flash.display.LoaderInfo::get isURLInaccessible"); + return this._file ? !1 : !0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "bytesLoaded", {get:function() { + Object.defineProperty(q.prototype, "bytesLoaded", {get:function() { return this._bytesLoaded; - }, set:function(a) { - a !== this._bytesLoaded && (this._bytesLoaded = a, this._bytesLoadedChanged = !0); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "bytesTotal", {get:function() { + Object.defineProperty(q.prototype, "bytesTotal", {get:function() { return this._bytesTotal; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "applicationDomain", {get:function() { - d("public flash.display.LoaderInfo::get applicationDomain"); - return f.system.ApplicationDomain.currentDomain; + Object.defineProperty(q.prototype, "applicationDomain", {get:function() { + l("public flash.display.LoaderInfo::get applicationDomain"); + return this._file ? a.system.ApplicationDomain.currentDomain : null; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(q.prototype, "swfVersion", {get:function() { + this._file || throwError("Error", h.Errors.LoadingObjectNotInitializedError); + this._file instanceof e || throwError("Error", h.Errors.LoadingObjectNotSWFError); + return this._file.swfVersion; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(q.prototype, "actionScriptVersion", {get:function() { + this._file || throwError("Error", h.Errors.LoadingObjectNotInitializedError); + this._file instanceof e || throwError("Error", h.Errors.LoadingObjectNotSWFError); + return this._file.useAVM1 ? v.ActionScriptVersion.ACTIONSCRIPT2 : v.ActionScriptVersion.ACTIONSCRIPT3; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(q.prototype, "frameRate", {get:function() { + this._file || throwError("Error", h.Errors.LoadingObjectNotInitializedError); + this._file instanceof e || throwError("Error", h.Errors.LoadingObjectNotSWFError); + return this._file.frameRate; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "swfVersion", {get:function() { - return this._swfVersion; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "actionScriptVersion", {get:function() { - return this._actionScriptVersion; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "frameRate", {get:function() { - return this._frameRate; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "width", {get:function() { + Object.defineProperty(q.prototype, "width", {get:function() { + this._file || throwError("Error", h.Errors.LoadingObjectNotInitializedError); return this._width / 20 | 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "height", {get:function() { + Object.defineProperty(q.prototype, "height", {get:function() { + this._file || throwError("Error", h.Errors.LoadingObjectNotInitializedError); return this._height / 20 | 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "contentType", {get:function() { - return this._contentType; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "sharedEvents", {get:function() { - m("public flash.display.LoaderInfo::get sharedEvents"); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "parentSandboxBridge", {get:function() { - m("public flash.display.LoaderInfo::get parentSandboxBridge"); - }, set:function(a) { - m("public flash.display.LoaderInfo::set parentSandboxBridge"); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "childSandboxBridge", {get:function() { - m("public flash.display.LoaderInfo::get childSandboxBridge"); - }, set:function(a) { - m("public flash.display.LoaderInfo::set childSandboxBridge"); + Object.defineProperty(q.prototype, "contentType", {get:function() { + return this._file ? this._file instanceof c.ImageFile ? this._file.mimeType : "application/x-shockwave-flash" : null; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "sameDomain", {get:function() { - m("public flash.display.LoaderInfo::get sameDomain"); + Object.defineProperty(q.prototype, "sharedEvents", {get:function() { + l("public flash.display.LoaderInfo::get sharedEvents"); + this._sharedEvents || (this._sharedEvents = new a.events.EventDispatcher); + return this._sharedEvents; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(q.prototype, "parentSandboxBridge", {get:function() { + l("public flash.display.LoaderInfo::get parentSandboxBridge"); + return this._parentSandboxBridge; + }, set:function(a) { + l("public flash.display.LoaderInfo::set parentSandboxBridge"); + this._parentSandboxBridge = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(q.prototype, "childSandboxBridge", {get:function() { + l("public flash.display.LoaderInfo::get childSandboxBridge"); + return this._childSandboxBridge; + }, set:function(a) { + l("public flash.display.LoaderInfo::set childSandboxBridge"); + this._childSandboxBridge = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(q.prototype, "sameDomain", {get:function() { + this._file || throwError("Error", h.Errors.LoadingObjectNotInitializedError); + l("public flash.display.LoaderInfo::get sameDomain"); + return!0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "childAllowsParent", {get:function() { - m("public flash.display.LoaderInfo::get childAllowsParent"); + Object.defineProperty(q.prototype, "childAllowsParent", {get:function() { + this._file || throwError("Error", h.Errors.LoadingObjectNotInitializedError); + l("public flash.display.LoaderInfo::get childAllowsParent"); + return!0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "parentAllowsChild", {get:function() { - m("public flash.display.LoaderInfo::get parentAllowsChild"); + Object.defineProperty(q.prototype, "parentAllowsChild", {get:function() { + this._file || throwError("Error", h.Errors.LoadingObjectNotInitializedError); + l("public flash.display.LoaderInfo::get parentAllowsChild"); + return!0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "loader", {get:function() { + Object.defineProperty(q.prototype, "loader", {get:function() { return this._loader; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "content", {get:function() { + Object.defineProperty(q.prototype, "content", {get:function() { return this._loader && this._loader.content; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "bytes", {get:function() { - m("public flash.display.LoaderInfo::get bytes"); + Object.defineProperty(q.prototype, "bytes", {get:function() { + if (!this._file) { + return new a.utils.ByteArray; + } + u("public flash.display.LoaderInfo::get bytes"); + return null; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "parameters", {get:function() { - d("public flash.display.LoaderInfo::get parameters"); - return this._parameters ? b.ObjectUtilities.cloneObject(this._parameters) : {}; + Object.defineProperty(q.prototype, "parameters", {get:function() { + l("public flash.display.LoaderInfo::get parameters"); + return this._parameters ? c.ObjectUtilities.cloneObject(this._parameters) : {}; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(q.prototype, "uncaughtErrorEvents", {get:function() { + l("public flash.display.LoaderInfo::_getUncaughtErrorEvents"); + this._uncaughtErrorEvents || (this._uncaughtErrorEvents = new a.events.UncaughtErrorEvents); + return this._uncaughtErrorEvents; }, enumerable:!0, configurable:!0}); - g.prototype._getUncaughtErrorEvents = function() { - m("public flash.display.LoaderInfo::_getUncaughtErrorEvents"); + q.prototype.getSymbolResolver = function(a, e) { + return this.resolveClassSymbol.bind(this, a, e); }; - g.prototype._setUncaughtErrorEvents = function(a) { - m("public flash.display.LoaderInfo::_setUncaughtErrorEvents"); + q.prototype.getSymbolById = function(m) { + var k = this._dictionary[m]; + if (k) { + return!1 === k.ready ? (c.Debug.warning("Accessing symbol that's not yet ready."), null) : k; + } + p(this._file instanceof e); + var f = this._file.getSymbol(m); + if (!f) { + return 65535 !== m && c.Debug.warning("Unknown symbol requested: " + m), null; + } + switch(f.type) { + case "shape": + k = a.display.ShapeSymbol.FromData(f, this); + break; + case "morphshape": + k = a.display.MorphShapeSymbol.FromData(f, this); + break; + case "image": + f.definition && (f = f.definition); + k = a.display.BitmapSymbol.FromData(f); + break; + case "label": + k = a.text.TextSymbol.FromLabelData(f, this); + break; + case "text": + k = a.text.TextSymbol.FromTextData(f, this); + this._syncAVM1Attributes(k); + break; + case "button": + k = a.display.ButtonSymbol.FromData(f, this); + this._syncAVM1Attributes(k); + break; + case "sprite": + k = a.display.SpriteSymbol.FromData(f, this); + break; + case "font": + f.definition && (f = f.definition); + var k = a.text.FontSymbol.FromData(f), d = a.text.Font.initializeFrom(k); + a.text.Font.instanceConstructorNoInitialize.call(d); + break; + case "sound": + k = a.media.SoundSymbol.FromData(f); + break; + case "binary": + k = c.Timeline.BinarySymbol.FromData(f); + } + p(k, "Unknown symbol type " + f.type); + this._dictionary[m] = k; + !1 === k.ready && h.Runtime.AVM2.instance.globals["Shumway.Player.Utils"].registerFontOrImage(k, f); + return k; + }; + q.prototype.getRootSymbol = function() { + p(this._file instanceof e); + p(0 < this._file.framesLoaded); + var c = this._dictionary[0]; + c || (c = new a.display.SpriteSymbol({id:0, className:this._file.symbolClassesMap[0]}, this), c.isRoot = !0, c.numFrames = this._file.frameCount, this._syncAVM1Attributes(c), this._dictionary[0] = c); + return c; }; - g.prototype.registerSymbol = function(a) { - this._dictionary[a.id] = a; + q.prototype._syncAVM1Attributes = function(a) { + this.actionScriptVersion === v.ActionScriptVersion.ACTIONSCRIPT2 && (a.isAVM1Object = !0, a.avm1Context = this._avm1Context); }; - g.prototype.getSymbolById = function(a) { - return this._dictionary[a] || null; + q.prototype.getFrame = function(a, c) { + var f = this._file; + p(f instanceof e); + a || (a = f); + return a.frames[c]; + }; + q.prototype.resolveClassSymbol = function(a, e) { + var f = this.getSymbolById(e); + if (f) { + return Object.defineProperty(a, "defaultInitializerArgument", {value:f}), f; + } + c.Debug.warning("Attempt to resolve symbol for AVM2 class failed: Symbol " + e + " not found."); }; - g.classInitializer = null; - g.initializer = null; - g.classSymbols = null; - g.instanceSymbols = null; - return g; - }(f.events.EventDispatcher); - g.LoaderInfo = c; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + q.classInitializer = null; + q.initializer = null; + q.classSymbols = null; + q.instanceSymbols = null; + q.CtorToken = {}; + return q; + }(a.events.EventDispatcher); + v.LoaderInfo = m; + })(a.display || (a.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(b) { - (function(g) { - var k = function(m) { - function d() { - g.DisplayObject.instanceConstructorNoInitialize.call(this); +(function(c) { + (function(c) { + (function(a) { + (function(a) { + (function(c) { + var h = function(a) { + function l() { + c.DisplayObject.instanceConstructorNoInitialize.call(this); } - __extends(d, m); - d.prototype._canHaveGraphics = function() { + __extends(l, a); + l.prototype._canHaveGraphics = function() { return!0; }; - d.prototype._getGraphics = function() { + l.prototype._getGraphics = function() { return this._graphics; }; - Object.defineProperty(d.prototype, "graphics", {get:function() { + Object.defineProperty(l.prototype, "graphics", {get:function() { return this._ensureGraphics(); }, enumerable:!0, configurable:!0}); - d.classInitializer = null; - d.initializer = function(a) { - a ? (this._graphics = a.graphics, this.morphFillBounds = a.morphFillBounds, this.morphLineBounds = a.morphLineBounds) : (this._graphics = new b.display.Graphics, this.morphLineBounds = this.morphFillBounds = null); + l.prototype._containsPointDirectly = function(a, c, l, h) { + return(l = this._getGraphics()) && l._containsPoint(a, c, !0, this._ratio / 65535); }; - d.classSymbols = null; - d.instanceSymbols = null; - return d; - }(b.display.DisplayObject); - g.MorphShape = k; - })(b.display || (b.display = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.NativeMenu"); + l.classSymbols = null; + l.instanceSymbols = null; + l.classInitializer = null; + l.initializer = function(a) { + this._graphics = null; + a && this._setStaticContentFromSymbol(a); + this._setFlags(32768); + }; + return l; + }(a.display.DisplayObject); + c.MorphShape = h; + h = function(c) { + function l(e) { + c.call(this, e, a.display.MorphShape); + } + __extends(l, c); + l.FromData = function(e, c) { + var h = new l(e); + h._setBoundsFromData(e); + h.graphics = a.display.Graphics.FromData(e); + h.processRequires(e.require, c); + h.morphFillBounds = e.morphFillBounds; + h.morphLineBounds = e.morphLineBounds; + return h; + }; + return l; + }(a.display.ShapeSymbol); + c.MorphShapeSymbol = h; + })(a.display || (a.display = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + (function(a) { + (function(a) { + (function(c) { + var h = function(c) { + function l() { + a.events.EventDispatcher.instanceConstructorNoInitialize.call(this); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(f.events.EventDispatcher); - g.NativeMenu = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.somewhatImplemented, m = function(b) { - function a() { - f.events.EventDispatcher.instanceConstructorNoInitialize.call(this); + __extends(l, c); + l.classInitializer = null; + l.initializer = null; + l.classSymbols = null; + l.instanceSymbols = null; + return l; + }(a.events.EventDispatcher); + c.NativeMenu = h; + })(a.display || (a.display = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.somewhatImplemented, u = function(c) { + function e() { + a.events.EventDispatcher.instanceConstructorNoInitialize.call(this); this._enabled = !0; } - __extends(a, b); - Object.defineProperty(a.prototype, "enabled", {get:function() { - k("public flash.display.NativeMenuItem::get enabled"); + __extends(e, c); + Object.defineProperty(e.prototype, "enabled", {get:function() { + p("public flash.display.NativeMenuItem::get enabled"); return this._enabled; }, set:function(a) { a = !!a; - k("public flash.display.NativeMenuItem::set enabled"); + p("public flash.display.NativeMenuItem::set enabled"); this._enabled = a; }, enumerable:!0, configurable:!0}); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(f.events.EventDispatcher); - g.NativeMenuItem = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a) { - k("Dummy Constructor: public flash.display.PNGEncoderOptions"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.events.EventDispatcher); + h.NativeMenuItem = u; + })(a.display || (a.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e(a) { + s("public flash.display.PNGEncoderOptions"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.PNGEncoderOptions = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.PixelSnapping"); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.ASNative); + h.PNGEncoderOptions = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.PixelSnapping"); } - __extends(a, b); - a.fromNumber = function(b) { - switch(b) { + __extends(e, a); + e.fromNumber = function(a) { + switch(a) { case 0: - return a.NEVER; + return e.NEVER; case 1: - return a.ALWAYS; + return e.ALWAYS; case 2: - return a.AUTO; + return e.AUTO; default: return null; } }; - a.toNumber = function(b) { - switch(b) { - case a.NEVER: + e.toNumber = function(a) { + switch(a) { + case e.NEVER: return 0; - case a.ALWAYS: + case e.ALWAYS: return 1; - case a.AUTO: + case e.AUTO: return 2; default: return-1; } }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.NEVER = "never"; - a.ALWAYS = "always"; - a.AUTO = "auto"; - return a; - }(g.ASNative); - f.PixelSnapping = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.SWFVersion"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.NEVER = "never"; + e.ALWAYS = "always"; + e.AUTO = "auto"; + return e; + }(a.ASNative); + h.PixelSnapping = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.SWFVersion"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.FLASH1 = 1; - a.FLASH2 = 2; - a.FLASH3 = 3; - a.FLASH4 = 4; - a.FLASH5 = 5; - a.FLASH6 = 6; - a.FLASH7 = 7; - a.FLASH8 = 8; - a.FLASH9 = 9; - a.FLASH10 = 10; - a.FLASH11 = 11; - a.FLASH12 = 12; - return a; - }(g.ASNative); - f.SWFVersion = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.AVM2.Runtime.asCoerceString, m = function(b) { - function a(a, b, d, e) { - this._name = k(a); - this._labels = b; - this.offset = d; - this._numFrames = e | 0; + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.FLASH1 = 1; + e.FLASH2 = 2; + e.FLASH3 = 3; + e.FLASH4 = 4; + e.FLASH5 = 5; + e.FLASH6 = 6; + e.FLASH7 = 7; + e.FLASH8 = 8; + e.FLASH9 = 9; + e.FLASH10 = 10; + e.FLASH11 = 11; + e.FLASH12 = 12; + return e; + }(a.ASNative); + h.SWFVersion = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.AVM2.Runtime.asCoerceString, u = function(a) { + function e(a, e, c, l) { + this._name = s(a); + this._labels = e; + this.offset = c; + this._numFrames = l | 0; } - __extends(a, b); - Object.defineProperty(a.prototype, "name", {get:function() { + __extends(e, a); + Object.defineProperty(e.prototype, "name", {get:function() { return this._name; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "labels", {get:function() { + Object.defineProperty(e.prototype, "labels", {get:function() { return this._labels; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "numFrames", {get:function() { + Object.defineProperty(e.prototype, "numFrames", {get:function() { return this._numFrames; }, enumerable:!0, configurable:!0}); - a.prototype.clone = function() { - var b = this._labels.map(function(a) { + e.prototype.clone = function() { + var a = this._labels.map(function(a) { return a.clone(); }); - return new a(this._name, b, this.offset, this._numFrames); + return new e(this._name, a, this.offset, this._numFrames); }; - a.prototype.getLabelByName = function(a) { - for (var b = this._labels, d = 0;d < b.length;d++) { - var e = b[d]; - if (e.name === a) { - return e; + e.prototype.getLabelByName = function(a, e) { + e && (a = a.toLowerCase()); + for (var c = this._labels, l = 0;l < c.length;l++) { + var k = c[l]; + if (e ? k.name.toLowerCase() === a : k.name === a) { + return k; } } return null; }; - a.prototype.getLabelByFrame = function(a) { - for (var b = this._labels, d = 0;d < b.length;d++) { - var e = b[d]; - if (e.frame === a) { - return e; + e.prototype.getLabelByFrame = function(a) { + for (var e = this._labels, c = 0;c < e.length;c++) { + var l = e[c]; + if (l.frame === a) { + return l; } } return null; }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.Scene = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m; - (function(b) { - b[b.None = 0] = "None"; - b[b.Top = 1] = "Top"; - b[b.Bottom = 2] = "Bottom"; - b[b.Left = 4] = "Left"; - b[b.Right = 8] = "Right"; - })(m || (m = {})); - m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.StageAlign"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.ASNative); + h.Scene = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.StageAlign"); } - __extends(a, b); - a.fromNumber = function(a) { + __extends(e, a); + e.fromNumber = function(a) { if (0 === a) { return ""; } - var b = ""; - a & 1 && (b += "T"); - a & 2 && (b += "B"); - a & 4 && (b += "L"); - a & 8 && (b += "R"); - return b; + var e = ""; + a & 1 && (e += "T"); + a & 2 && (e += "B"); + a & 4 && (e += "L"); + a & 8 && (e += "R"); + return e; }; - a.toNumber = function(a) { - var b = 0; + e.toNumber = function(a) { + var e = 0; a = a.toUpperCase(); - 0 <= a.indexOf("T") && (b |= 1); - 0 <= a.indexOf("B") && (b |= 2); - 0 <= a.indexOf("L") && (b |= 4); - 0 <= a.indexOf("R") && (b |= 8); - return b; + 0 <= a.indexOf("T") && (e |= 1); + 0 <= a.indexOf("B") && (e |= 2); + 0 <= a.indexOf("L") && (e |= 4); + 0 <= a.indexOf("R") && (e |= 8); + return e; }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.TOP = "T"; - a.LEFT = "L"; - a.BOTTOM = "B"; - a.RIGHT = "R"; - a.TOP_LEFT = "TL"; - a.TOP_RIGHT = "TR"; - a.BOTTOM_LEFT = "BL"; - a.BOTTOM_RIGHT = "BR"; - return a; - }(g.ASNative); - f.StageAlign = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.StageDisplayState"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.TOP = "T"; + e.LEFT = "L"; + e.BOTTOM = "B"; + e.RIGHT = "R"; + e.TOP_LEFT = "TL"; + e.TOP_RIGHT = "TR"; + e.BOTTOM_LEFT = "BL"; + e.BOTTOM_RIGHT = "BR"; + return e; + }(a.ASNative); + h.StageAlign = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.StageDisplayState"); } - __extends(a, b); - a.fromNumber = function(b) { - switch(b) { + __extends(e, a); + e.fromNumber = function(a) { + switch(a) { case 0: - return a.FULL_SCREEN; + return e.FULL_SCREEN; case 1: - return a.FULL_SCREEN_INTERACTIVE; + return e.FULL_SCREEN_INTERACTIVE; case 2: - return a.NORMAL; + return e.NORMAL; default: return null; } }; - a.toNumber = function(b) { - switch(b) { - case a.FULL_SCREEN: + e.toNumber = function(a) { + switch(a) { + case e.FULL_SCREEN: return 0; - case a.FULL_SCREEN_INTERACTIVE: + case e.FULL_SCREEN_INTERACTIVE: return 1; - case a.NORMAL: + case e.NORMAL: return 2; default: return-1; } }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.FULL_SCREEN = "fullScreen"; - a.FULL_SCREEN_INTERACTIVE = "fullScreenInteractive"; - a.NORMAL = "normal"; - return a; - }(g.ASNative); - f.StageDisplayState = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.StageQuality"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.FULL_SCREEN = "fullScreen"; + e.FULL_SCREEN_INTERACTIVE = "fullScreenInteractive"; + e.NORMAL = "normal"; + return e; + }(a.ASNative); + h.StageDisplayState = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.StageQuality"); } - __extends(a, b); - a.fromNumber = function(b) { - switch(b) { + __extends(e, a); + e.fromNumber = function(a) { + switch(a) { case 0: - return a.LOW; + return e.LOW; case 1: - return a.MEDIUM; + return e.MEDIUM; case 2: - return a.HIGH; + return e.HIGH; case 3: - return a.BEST; + return e.BEST; case 4: - return a.HIGH_8X8; + return e.HIGH_8X8; case 5: - return a.HIGH_8X8_LINEAR; + return e.HIGH_8X8_LINEAR; case 6: - return a.HIGH_16X16; + return e.HIGH_16X16; case 7: - return a.HIGH_16X16_LINEAR; + return e.HIGH_16X16_LINEAR; default: return null; } }; - a.toNumber = function(b) { - switch(b) { - case a.LOW: + e.toNumber = function(a) { + switch(a) { + case e.LOW: return 0; - case a.MEDIUM: + case e.MEDIUM: return 1; - case a.HIGH: + case e.HIGH: return 2; - case a.BEST: + case e.BEST: return 3; - case a.HIGH_8X8: + case e.HIGH_8X8: return 4; - case a.HIGH_8X8_LINEAR: + case e.HIGH_8X8_LINEAR: return 5; - case a.HIGH_16X16: + case e.HIGH_16X16: return 6; - case a.HIGH_16X16_LINEAR: + case e.HIGH_16X16_LINEAR: return 7; default: return-1; } }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.LOW = "low"; - a.MEDIUM = "medium"; - a.HIGH = "high"; - a.BEST = "best"; - a.HIGH_8X8 = "8x8"; - a.HIGH_8X8_LINEAR = "8x8linear"; - a.HIGH_16X16 = "16x16"; - a.HIGH_16X16_LINEAR = "16x16linear"; - return a; - }(g.ASNative); - f.StageQuality = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.StageScaleMode"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.LOW = "low"; + e.MEDIUM = "medium"; + e.HIGH = "high"; + e.BEST = "best"; + e.HIGH_8X8 = "8x8"; + e.HIGH_8X8_LINEAR = "8x8linear"; + e.HIGH_16X16 = "16x16"; + e.HIGH_16X16_LINEAR = "16x16linear"; + return e; + }(a.ASNative); + h.StageQuality = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.StageScaleMode"); } - __extends(a, b); - a.fromNumber = function(b) { - switch(b) { + __extends(e, a); + e.fromNumber = function(a) { + switch(a) { case 0: - return a.SHOW_ALL; + return e.SHOW_ALL; case 1: - return a.EXACT_FIT; + return e.EXACT_FIT; case 2: - return a.NO_BORDER; + return e.NO_BORDER; case 4: - return a.NO_SCALE; + return e.NO_SCALE; default: return null; } }; - a.toNumber = function(b) { - switch(b) { - case a.SHOW_ALL: + e.toNumber = function(a) { + switch(a.toLowerCase()) { + case e.SHOW_ALL_LOWERCASE: return 0; - case a.EXACT_FIT: + case e.EXACT_FIT_LOWERCASE: return 1; - case a.NO_BORDER: + case e.NO_BORDER_LOWERCASE: return 2; - case a.NO_SCALE: - return 3; + case e.NO_SCALE_LOWERCASE: + return 4; default: return-1; } }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.SHOW_ALL = "showAll"; - a.EXACT_FIT = "exactFit"; - a.NO_BORDER = "noBorder"; - a.NO_SCALE = "noScale"; - return a; - }(g.ASNative); - f.StageScaleMode = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.display.TriangleCulling"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.SHOW_ALL = "showAll"; + e.EXACT_FIT = "exactFit"; + e.NO_BORDER = "noBorder"; + e.NO_SCALE = "noScale"; + e.SHOW_ALL_LOWERCASE = "showall"; + e.EXACT_FIT_LOWERCASE = "exactfit"; + e.NO_BORDER_LOWERCASE = "noborder"; + e.NO_SCALE_LOWERCASE = "noscale"; + return e; + }(a.ASNative); + h.StageScaleMode = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.display.TriangleCulling"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.NONE = "none"; - a.POSITIVE = "positive"; - a.NEGATIVE = "negative"; - return a; - }(g.ASNative); - f.TriangleCulling = m; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = b.Debug.assert, d = function(a) { - function b() { - f.display.DisplayObject.instanceConstructorNoInitialize.call(this); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.NONE = "none"; + e.POSITIVE = "positive"; + e.NEGATIVE = "negative"; + return e; + }(a.ASNative); + h.TriangleCulling = u; + })(h.display || (h.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = function(c) { + function e(e) { + a.display.DisplayObject.instanceConstructorNoInitialize.call(this); this._children = []; - this._constructed = !1; - } - __extends(b, a); - b.prototype.initializeContent = function(a) { - m(0 === this._children.length); - m(!this._parent, "Must have content initialized before being added to stage"); - this._children[0] = this._content = a; - a._setParent(this, 0); + this._children[0] = this._content = e; + e._setParent(this, 0); this._setDirtyFlags(2097152); this._invalidateFillAndLineBounds(!0, !0); - g.DisplayObject._advancableInstances.push(this); - }; - b.prototype.call = function(a) { - k("AVM1Movie#call"); + h.DisplayObject._advancableInstances.push(this); + this._constructed = !1; + } + __extends(e, c); + e.prototype.call = function(a) { + p("AVM1Movie#call"); }; - b.prototype.addCallback = function(a, b) { - k("AVM1Movie#call"); + e.prototype.addCallback = function(a, e) { + p("AVM1Movie#call"); }; - b.prototype._initFrame = function(a) { + e.prototype._initFrame = function(a) { }; - b.prototype._constructFrame = function() { - this._constructed || (this._constructed = !0, g.DisplayObjectContainer.prototype._constructChildren.call(this)); + e.prototype._constructFrame = function() { + this._constructed || (this._constructed = !0, h.DisplayObjectContainer.prototype._constructChildren.call(this)); this._content._constructFrame(); }; - b.prototype._enqueueFrameScripts = function() { - this._removeFlags(8192); + e.prototype._enqueueFrameScripts = function() { + this._removeFlags(16384); this._content._enqueueFrameScripts(); }; - b.prototype._propagateFlagsDown = function(a) { + e.prototype._propagateFlagsDown = function(a) { this._hasFlags(a) || (this._setFlags(a), this._content._propagateFlagsDown(a)); }; - b.prototype._containsPoint = function(a, b, c, d, f, g) { - return 3 === f ? this._content._containsPoint(a, b, c, d, f, g) : 0 === f && this._getContentBounds().contains(c, d) ? 1 : 0; + e.prototype._containsPoint = function(a, e, c, l, k, f) { + return 3 === k ? this._content._containsPoint(a, e, c, l, k, f) : 0 === k && this._getContentBounds().contains(c, l) ? 1 : 0; }; - b.prototype._getChildBounds = function(a, b) { - var c = this._content._getContentBounds(b).clone(); + e.prototype._getChildBounds = function(a, e) { + var c = this._content._getContentBounds(e).clone(); this._getConcatenatedMatrix().transformBounds(c); a.unionInPlace(c); }; - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(f.display.DisplayObject); - g.AVM1Movie = d; - })(f.display || (f.display = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = b.ObjectUtilities.createEmptyObject, a = b.Telemetry, c = b.AVM2.Runtime.forEachPublicProperty, n = b.ExternalInterfaceService, p = function(b) { - function f() { - k("Dummy Constructor: public flash.external.ExternalInterface"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.display.DisplayObject); + h.AVM1Movie = u; + })(a.display || (a.display = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = c.AVM2.Runtime.asCoerceString, l = function(a) { + function c(a, e) { + void 0 === a && (a = ""); + u(a); + s("public flash.errors.IllegalOperationError"); } - __extends(f, b); - f._getAvailable = function() { - return n.instance.enabled; + __extends(c, a); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.ASError); + h.IllegalOperationError = l; + })(h.errors || (h.errors = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = c.AVM2.Runtime.asCoerceString, l = c.Telemetry, e = c.AVM2.Runtime.forEachPublicProperty, m = c.ExternalInterfaceService, t = function(c) { + function h() { + s("public flash.external.ExternalInterface"); + } + __extends(h, c); + h._getAvailable = function() { + return m.instance.enabled; }; - f._initJS = function() { - f.initialized || (a.instance.reportTelemetry({topic:"feature", feature:1}), f.initialized = !0, n.instance.initJS(f._callIn)); + h._initJS = function() { + h.initialized || (l.instance.reportTelemetry({topic:"feature", feature:1}), h.initialized = !0, m.instance.initJS(h._callIn)); }; - f._callIn = function(a, b) { - var c = f.registeredCallbacks[a]; - if (c) { - return c(a, b); + h._callIn = function(e, c) { + var d = h.registeredCallbacks[e]; + if (d) { + return d(e, a.ASJSON.transformJSValueToAS(c, !0)); } }; - f._getPropNames = function(a) { - var b = []; - c(a, function(a) { - b.push(a); + h._getPropNames = function(a) { + var c = []; + e(a, function(a) { + c.push(a); }, null); - return b; + return c; }; - f._addCallback = function(a, b, c) { - c ? (n.instance.unregisterCallback(a), delete f.registeredCallbacks[a]) : (n.instance.registerCallback(a), f.registeredCallbacks[a] = b); + h._addCallback = function(a, e, d) { + d ? (m.instance.unregisterCallback(a), delete h.registeredCallbacks[a]) : (m.instance.registerCallback(a), h.registeredCallbacks[a] = e); }; - f._evalJS = function(a) { - a = m(a); - return n.instance.eval(a); + h._evalJS = function(a) { + a = u(a); + return m.instance.eval(a); }; - f._callOut = function(a) { - a = m(a); - return n.instance.call(a); + h._callOut = function(a) { + a = u(a); + return m.instance.call(a); }; - Object.defineProperty(f, "available", {get:function() { - return f._getAvailable(); + Object.defineProperty(h, "available", {get:function() { + return h._getAvailable(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(f, "objectID", {get:function() { - return n.instance.getId(); + Object.defineProperty(h, "objectID", {get:function() { + return m.instance.getId(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(f, "activeX", {get:function() { + Object.defineProperty(h, "activeX", {get:function() { return!1; }, enumerable:!0, configurable:!0}); - f.classInitializer = null; - f.initializer = null; - f.classSymbols = null; - f.instanceSymbols = null; - f.initialized = !1; - f.registeredCallbacks = d(); - return f; - }(g.ASNative); - f.ExternalInterface = p; - })(f.external || (f.external = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + h.classInitializer = null; + h.initializer = null; + h.classSymbols = null; + h.instanceSymbols = null; + h.initialized = !1; + h.registeredCallbacks = Object.create(null); + return h; + }(a.ASNative); + h.ExternalInterface = t; + })(h.external || (h.external = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { - function d() { +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function c() { } - __extends(d, b); - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - d.LOW = 1; - d.MEDIUM = 2; - d.HIGH = 3; - return d; - }(b.ASNative); - f.BitmapFilterQuality = k; - })(f.filters || (f.filters = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + __extends(c, a); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.LOW = 1; + c.MEDIUM = 2; + c.HIGH = 3; + return c; + }(a.ASNative); + c.BitmapFilterQuality = h; + })(c.filters || (c.filters = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { - function d() { +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function c() { } - __extends(d, b); - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - d.INNER = "inner"; - d.OUTER = "outer"; - d.FULL = "full"; - return d; - }(b.ASNative); - f.BitmapFilterType = k; - })(f.filters || (f.filters = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = function(b) { - function d() { + __extends(c, a); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.INNER = "inner"; + c.OUTER = "outer"; + c.FULL = "full"; + return c; + }(a.ASNative); + c.BitmapFilterType = h; + })(c.filters || (c.filters = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = function(a) { + function c() { } - __extends(d, b); - d._updateBlurBounds = function(a, b, f, g, e) { - "undefined" === typeof e && (e = !1); - g = d.blurFilterStepWidths[g - 1]; - e && (e = g / 4, b -= e, f -= e); - a.inflate(Math.ceil((1 > b ? 1 : b) * g), Math.ceil((1 > f ? 1 : f) * g)); + __extends(c, a); + c._updateBlurBounds = function(a, m, h, q, n) { + void 0 === n && (n = !1); + q = c.blurFilterStepWidths[q - 1]; + n && (n = q / 4, m -= n, h -= n); + a.inflate(Math.ceil((1 > m ? 1 : m) * q), Math.ceil((1 > h ? 1 : h) * q)); }; - d.prototype._updateFilterBounds = function(a) { + c.prototype._updateFilterBounds = function(a) { }; - d.prototype._serialize = function(a) { + c.prototype._serialize = function(a) { a.writeInt(-1); }; - d.prototype.clone = function() { + c.prototype.clone = function() { return null; }; - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - d.EPS = 1E-9; - d.blurFilterStepWidths = [.5, 1.05, 1.35, 1.55, 1.75, 1.9, 2, 2.1, 2.2, 2.3, 2.5, 3, 3, 3.5, 3.5]; - return d; - }(g.ASNative); - f.BitmapFilter = k; - k = function() { - function f() { + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.EPS = 1E-9; + c.blurFilterStepWidths = [.5, 1.05, 1.35, 1.55, 1.75, 1.9, 2, 2.1, 2.2, 2.3, 2.5, 3, 3, 3.5, 3.5]; + return c; + }(a.ASNative); + h.BitmapFilter = s; + s = function() { + function a() { } - f.sanitize = function(d, a, c) { - if (b.isNullOrUndefined(d) || 0 === d.length) { + a.sanitize = function(a, e, m) { + if (c.isNullOrUndefined(a) || 0 === a.length) { this.colors = [], this.alphas = [], this.ratios = []; } else { - var f; - b.isNullOrUndefined(c) ? (this.colors = this.sanitizeColors(d), f = this.colors.length, this.ratios = this.initArray(f), b.isNullOrUndefined(a) ? this.alphas = this.initArray(f) : this.alphas = this.sanitizeAlphas(a, f, f, 1)) : 0 === c.length ? (this.colors = [], this.alphas = [], this.ratios = []) : (f = Math.min(d.length, c.length, 16), this.colors = this.sanitizeColors(d, f), this.ratios = this.sanitizeRatios(c, f), b.isNullOrUndefined(a) ? this.alphas = this.initArray(f) : this.alphas = - this.sanitizeAlphas(a, f, f, 1)); + var h; + c.isNullOrUndefined(m) ? (this.colors = this.sanitizeColors(a), h = this.colors.length, this.ratios = this.initArray(h), c.isNullOrUndefined(e) ? this.alphas = this.initArray(h) : this.alphas = this.sanitizeAlphas(e, h, h, 1)) : 0 === m.length ? (this.colors = [], this.alphas = [], this.ratios = []) : (h = Math.min(a.length, m.length, 16), this.colors = this.sanitizeColors(a, h), this.ratios = this.sanitizeRatios(m, h), c.isNullOrUndefined(e) ? this.alphas = this.initArray(h) : this.alphas = + this.sanitizeAlphas(e, h, h, 1)); } }; - f.sanitizeColors = function(b, a) { - "undefined" === typeof a && (a = 16); - for (var c = [], f = 0, g = Math.min(b.length, a);f < g;f++) { - c[f] = b[f] >>> 0 & 16777215; + a.sanitizeColors = function(a, e) { + void 0 === e && (e = 16); + for (var c = [], h = 0, q = Math.min(a.length, e);h < q;h++) { + c[h] = a[h] >>> 0 & 16777215; } return c; }; - f.sanitizeAlphas = function(d, a, c, f) { - "undefined" === typeof a && (a = 16); - "undefined" === typeof c && (c = 0); - "undefined" === typeof f && (f = 0); - var g = [], e = 0; - for (a = Math.min(d.length, a);e < a;e++) { - g[e] = b.NumberUtilities.clamp(+d[e], 0, 1); + a.sanitizeAlphas = function(a, e, m, h) { + void 0 === e && (e = 16); + void 0 === m && (m = 0); + void 0 === h && (h = 0); + var q = [], n = 0; + for (e = Math.min(a.length, e);n < e;n++) { + q[n] = c.NumberUtilities.clamp(+a[n], 0, 1); } - for (;e < c;) { - g[e++] = f; + for (;n < m;) { + q[n++] = h; } - return g; + return q; }; - f.sanitizeRatios = function(d, a, c) { - var f; - "undefined" === typeof a && (a = 16); - "undefined" === typeof c && (c = 0); - "undefined" === typeof f && (f = 0); - var g = [], e = 0; - for (a = Math.min(d.length, a);e < a;e++) { - g[e] = b.NumberUtilities.clamp(+d[e], 0, 255); + a.sanitizeRatios = function(a, e, m) { + var h; + void 0 === e && (e = 16); + void 0 === m && (m = 0); + void 0 === h && (h = 0); + var q = [], n = 0; + for (e = Math.min(a.length, e);n < e;n++) { + q[n] = c.NumberUtilities.clamp(+a[n], 0, 255); } - for (;e < c;) { - g[e++] = f; + for (;n < m;) { + q[n++] = h; } - return g; + return q; }; - f.initArray = function(b) { - var a; - "undefined" === typeof a && (a = 0); - for (var c = Array(b), f = 0;f < b;f++) { - c[f] = a; + a.initArray = function(a) { + var e; + void 0 === e && (e = 0); + for (var c = Array(a), h = 0;h < a;h++) { + c[h] = e; } return c; }; - return f; + return a; }(); - f.GradientArrays = k; - })(f.filters || (f.filters = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var s = b.AVM2.Runtime.asCoerceString, m = b.Debug.assert, d = function(a) { - function c(a, b, c, d, f, g, m, k, h, s, t, G) { - "undefined" === typeof a && (a = 4); - "undefined" === typeof b && (b = 45); - "undefined" === typeof c && (c = 16777215); - "undefined" === typeof d && (d = 1); - "undefined" === typeof f && (f = 0); - "undefined" === typeof g && (g = 1); - "undefined" === typeof m && (m = 4); - "undefined" === typeof k && (k = 4); - "undefined" === typeof h && (h = 1); - "undefined" === typeof s && (s = 1); - "undefined" === typeof t && (t = "inner"); - "undefined" === typeof G && (G = !1); + h.GradientArrays = s; + })(h.filters || (h.filters = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(v) { + var p = c.AVM2.Runtime.asCoerceString, u = c.Debug.assert, l = function(e) { + function m(a, e, c, k, f, d, b, g, m, l, h, s) { + void 0 === a && (a = 4); + void 0 === e && (e = 45); + void 0 === c && (c = 16777215); + void 0 === k && (k = 1); + void 0 === f && (f = 0); + void 0 === d && (d = 1); + void 0 === b && (b = 4); + void 0 === g && (g = 4); + void 0 === m && (m = 1); + void 0 === l && (l = 1); + void 0 === h && (h = "inner"); + void 0 === s && (s = !1); this.distance = a; - this.angle = b; + this.angle = e; this.highlightColor = c; - this.highlightAlpha = d; + this.highlightAlpha = k; this.shadowColor = f; - this.shadowAlpha = g; - this.blurX = m; - this.blurY = k; - this.strength = h; - this.quality = s; - this.type = t; - this.knockout = G; + this.shadowAlpha = d; + this.blurX = b; + this.blurY = g; + this.strength = m; + this.quality = l; + this.type = h; + this.knockout = s; } - __extends(c, a); - c.FromUntyped = function(a) { - var b = a.highlightColor >>> 8, d = (a.highlightColor & 255) / 255; - m(a.colors && 1 === a.colors.length, "colors must be Array of length 1"); - var g = a.colors[0] >>> 8, l = (a.colors[0] & 255) / 255, k = f.filters.BitmapFilterType.OUTER; - a.onTop ? k = f.filters.BitmapFilterType.FULL : a.inner && (k = f.filters.BitmapFilterType.INNER); - return new c(a.distance, 180 * a.angle / Math.PI, b, d, g, l, a.blurX, a.blurY, a.strength, a.quality, k, a.knockout); - }; - c.prototype._updateFilterBounds = function(a) { - if (this.type !== g.BitmapFilterType.INNER && (g.BitmapFilter._updateBlurBounds(a, this._blurX, this._blurY, this._quality), 0 !== this._distance)) { - var b = this._angle * Math.PI / 180; - a.x += Math.floor(Math.cos(b) * this._distance); - a.y += Math.floor(Math.sin(b) * this._distance); + __extends(m, e); + m.FromUntyped = function(e) { + var c = e.highlightColor >>> 8, l = (e.highlightColor & 255) / 255; + u(e.colors && 1 === e.colors.length, "colors must be Array of length 1"); + var k = e.colors[0] >>> 8, f = (e.colors[0] & 255) / 255, d = a.filters.BitmapFilterType.OUTER; + e.onTop ? d = a.filters.BitmapFilterType.FULL : e.inner && (d = a.filters.BitmapFilterType.INNER); + return new m(e.distance, 180 * e.angle / Math.PI, c, l, k, f, e.blurX, e.blurY, e.strength, e.quality, d, e.knockout); + }; + m.prototype._updateFilterBounds = function(a) { + if (this.type !== v.BitmapFilterType.INNER && (v.BitmapFilter._updateBlurBounds(a, this._blurX, this._blurY, this._quality), 0 !== this._distance)) { + var e = this._angle * Math.PI / 180; + a.x += Math.floor(Math.cos(e) * this._distance); + a.y += Math.floor(Math.sin(e) * this._distance); 0 < a.left && (a.left = 0); 0 < a.top && (a.top = 0); } }; - Object.defineProperty(c.prototype, "distance", {get:function() { + Object.defineProperty(m.prototype, "distance", {get:function() { return this._distance; }, set:function(a) { this._distance = +a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "angle", {get:function() { + Object.defineProperty(m.prototype, "angle", {get:function() { return this._angle; }, set:function(a) { this._angle = +a % 360; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "highlightColor", {get:function() { + Object.defineProperty(m.prototype, "highlightColor", {get:function() { return this._highlightColor; }, set:function(a) { this._highlightColor = a >>> 0 & 16777215; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "highlightAlpha", {get:function() { + Object.defineProperty(m.prototype, "highlightAlpha", {get:function() { return this._highlightAlpha; }, set:function(a) { - this._highlightAlpha = b.NumberUtilities.clamp(+a, 0, 1); + this._highlightAlpha = c.NumberUtilities.clamp(+a, 0, 1); }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "shadowColor", {get:function() { + Object.defineProperty(m.prototype, "shadowColor", {get:function() { return this._shadowColor; }, set:function(a) { this._shadowColor = a >>> 0 & 16777215; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "shadowAlpha", {get:function() { + Object.defineProperty(m.prototype, "shadowAlpha", {get:function() { return this._shadowAlpha; }, set:function(a) { - this._shadowAlpha = b.NumberUtilities.clamp(+a, 0, 1); + this._shadowAlpha = c.NumberUtilities.clamp(+a, 0, 1); }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "blurX", {get:function() { + Object.defineProperty(m.prototype, "blurX", {get:function() { return this._blurX; }, set:function(a) { - this._blurX = b.NumberUtilities.clamp(+a, 0, 255); + this._blurX = c.NumberUtilities.clamp(+a, 0, 255); }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "blurY", {get:function() { + Object.defineProperty(m.prototype, "blurY", {get:function() { return this._blurY; }, set:function(a) { - this._blurY = b.NumberUtilities.clamp(+a, 0, 255); + this._blurY = c.NumberUtilities.clamp(+a, 0, 255); }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "knockout", {get:function() { + Object.defineProperty(m.prototype, "knockout", {get:function() { return this._knockout; }, set:function(a) { this._knockout = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "quality", {get:function() { + Object.defineProperty(m.prototype, "quality", {get:function() { return this._quality; }, set:function(a) { - this._quality = b.NumberUtilities.clamp(a | 0, 0, 15); + this._quality = c.NumberUtilities.clamp(a | 0, 0, 15); }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "strength", {get:function() { + Object.defineProperty(m.prototype, "strength", {get:function() { return this._strength; }, set:function(a) { - this._strength = b.NumberUtilities.clamp(+a, 0, 255); + this._strength = c.NumberUtilities.clamp(+a, 0, 255); }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "type", {get:function() { + Object.defineProperty(m.prototype, "type", {get:function() { return this._type; }, set:function(a) { - a = s(a); - null === a ? k.Runtime.throwError("TypeError", k.Errors.NullPointerError, "type") : this._type = a === g.BitmapFilterType.INNER || a === g.BitmapFilterType.OUTER ? a : g.BitmapFilterType.FULL; + a = p(a); + null === a ? h.Runtime.throwError("TypeError", h.Errors.NullPointerError, "type") : this._type = a === v.BitmapFilterType.INNER || a === v.BitmapFilterType.OUTER ? a : v.BitmapFilterType.FULL; }, enumerable:!0, configurable:!0}); - c.prototype.clone = function() { - return new c(this._distance, this._angle, this._highlightColor, this._highlightAlpha, this._shadowColor, this._shadowAlpha, this._blurX, this._blurY, this._strength, this._quality, this._type, this._knockout); + m.prototype.clone = function() { + return new m(this._distance, this._angle, this._highlightColor, this._highlightAlpha, this._shadowColor, this._shadowAlpha, this._blurX, this._blurY, this._strength, this._quality, this._type, this._knockout); }; - c.classInitializer = null; - c.initializer = null; - c.classSymbols = null; - c.instanceSymbols = null; - return c; - }(f.filters.BitmapFilter); - g.BevelFilter = d; - })(f.filters || (f.filters = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = function(f) { - function d(a, b, d) { - "undefined" === typeof a && (a = 4); - "undefined" === typeof b && (b = 4); - "undefined" === typeof d && (d = 1); + m.classInitializer = null; + m.initializer = null; + m.classSymbols = null; + m.instanceSymbols = null; + return m; + }(a.filters.BitmapFilter); + v.BevelFilter = l; + })(a.filters || (a.filters = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = function(a) { + function l(a, c, l) { + void 0 === a && (a = 4); + void 0 === c && (c = 4); + void 0 === l && (l = 1); this.blurX = a; - this.blurY = b; - this.quality = d; + this.blurY = c; + this.quality = l; } - __extends(d, f); - d.FromUntyped = function(a) { - return new d(a.blurX, a.blurY, a.quality); + __extends(l, a); + l.FromUntyped = function(a) { + return new l(a.blurX, a.blurY, a.quality); }; - d.prototype._updateFilterBounds = function(a) { - g.BitmapFilter._updateBlurBounds(a, this._blurX, this._blurY, this._quality, !0); + l.prototype._updateFilterBounds = function(a) { + h.BitmapFilter._updateBlurBounds(a, this._blurX, this._blurY, this._quality, !0); }; - d.prototype._serialize = function(a) { + l.prototype._serialize = function(a) { a.ensureAdditionalCapacity(16); a.writeIntUnsafe(1); a.writeFloatUnsafe(this._blurX); a.writeFloatUnsafe(this._blurY); a.writeIntUnsafe(this._quality); }; - Object.defineProperty(d.prototype, "blurX", {get:function() { + Object.defineProperty(l.prototype, "blurX", {get:function() { return this._blurX; }, set:function(a) { - this._blurX = b.NumberUtilities.clamp(+a, 0, 255); + this._blurX = c.NumberUtilities.clamp(+a, 0, 255); }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "blurY", {get:function() { + Object.defineProperty(l.prototype, "blurY", {get:function() { return this._blurY; }, set:function(a) { - this._blurY = b.NumberUtilities.clamp(+a, 0, 255); + this._blurY = c.NumberUtilities.clamp(+a, 0, 255); }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "quality", {get:function() { + Object.defineProperty(l.prototype, "quality", {get:function() { return this._quality; }, set:function(a) { - this._quality = b.NumberUtilities.clamp(a | 0, 0, 15); + this._quality = c.NumberUtilities.clamp(a | 0, 0, 15); }, enumerable:!0, configurable:!0}); - d.prototype.clone = function() { - return new d(this._blurX, this._blurY, this._quality); + l.prototype.clone = function() { + return new l(this._blurX, this._blurY, this._quality); }; - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - return d; - }(f.filters.BitmapFilter); - g.BlurFilter = k; - })(f.filters || (f.filters = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var s = function(f) { - function d(a) { - "undefined" === typeof a && (a = null); + l.classInitializer = null; + l.initializer = null; + l.classSymbols = null; + l.instanceSymbols = null; + return l; + }(a.filters.BitmapFilter); + h.BlurFilter = p; + })(a.filters || (a.filters = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(v) { + var p = function(a) { + function l(a) { + void 0 === a && (a = null); a ? this.matrix = a : this._matrix = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0]; } - __extends(d, f); - d.FromUntyped = function(a) { - return new d(a.matrix); - }; - d.prototype._serialize = function(a) { - var b = this._matrix; - a.ensureAdditionalCapacity(4 * (b.length + 1)); + __extends(l, a); + l.FromUntyped = function(a) { + return new l(a.matrix); + }; + l.prototype._serialize = function(a) { + var c = this._matrix; + a.ensureAdditionalCapacity(4 * (c.length + 1)); a.writeIntUnsafe(6); - for (var d = 0;d < b.length;d++) { - a.writeFloatUnsafe(b[d]); + for (var l = 0;l < c.length;l++) { + a.writeFloatUnsafe(c[l]); } }; - Object.defineProperty(d.prototype, "matrix", {get:function() { + Object.defineProperty(l.prototype, "matrix", {get:function() { return this._matrix.concat(); }, set:function(a) { - if (b.isNullOrUndefined(a)) { - k.Runtime.throwError("TypeError", k.Errors.NullPointerError, "matrix"); + if (c.isNullOrUndefined(a)) { + h.Runtime.throwError("TypeError", h.Errors.NullPointerError, "matrix"); } else { - for (var c = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], d = 0, f = Math.min(a.length, 20);d < f;d++) { - c[d] = b.toNumber(a[d]); + for (var m = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], l = 0, q = Math.min(a.length, 20);l < q;l++) { + m[l] = c.toNumber(a[l]); } - this._matrix = c; + this._matrix = m; } }, enumerable:!0, configurable:!0}); - d.prototype.clone = function() { - return new d(this.matrix); + l.prototype.clone = function() { + return new l(this.matrix); }; - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - return d; - }(f.filters.BitmapFilter); - g.ColorMatrixFilter = s; - })(f.filters || (f.filters = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var s = function(f) { - function d(a, b, d, f, e, g, l, m, k) { - "undefined" === typeof a && (a = 0); - "undefined" === typeof b && (b = 0); - "undefined" === typeof d && (d = null); - "undefined" === typeof f && (f = 1); - "undefined" === typeof e && (e = 0); - "undefined" === typeof g && (g = !0); - "undefined" === typeof l && (l = !0); - "undefined" === typeof m && (m = 0); - "undefined" === typeof k && (k = 0); + l.classInitializer = null; + l.initializer = null; + l.classSymbols = null; + l.instanceSymbols = null; + return l; + }(a.filters.BitmapFilter); + v.ColorMatrixFilter = p; + })(a.filters || (a.filters = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(v) { + var p = function(a) { + function l(a, c, l, h, n, k, f, d, b) { + void 0 === a && (a = 0); + void 0 === c && (c = 0); + void 0 === l && (l = null); + void 0 === h && (h = 1); + void 0 === n && (n = 0); + void 0 === k && (k = !0); + void 0 === f && (f = !0); + void 0 === d && (d = 0); + void 0 === b && (b = 0); this.matrixX = a; - this.matrixY = b; - d ? this.matrix = d : this._matrix = this._expandArray([], this._matrixX * this._matrixY); - this.divisor = f; - this.bias = e; - this.preserveAlpha = g; - this.clamp = l; - this.color = m; - this.alpha = k; + this.matrixY = c; + l ? this.matrix = l : this._matrix = this._expandArray([], this._matrixX * this._matrixY); + this.divisor = h; + this.bias = n; + this.preserveAlpha = k; + this.clamp = f; + this.color = d; + this.alpha = b; } - __extends(d, f); - d.FromUntyped = function(a) { - return new d(a.matrixX, a.matrixY, a.matrix, a.divisor, a.bias, a.preserveAlpha, a.clamp, a.color >>> 8, (a.color & 255) / 255); + __extends(l, a); + l.FromUntyped = function(a) { + return new l(a.matrixX, a.matrixY, a.matrix, a.divisor, a.bias, a.preserveAlpha, a.clamp, a.color >>> 8, (a.color & 255) / 255); }; - d.prototype._expandArray = function(a, b) { + l.prototype._expandArray = function(a, c) { if (a) { - for (var d = a.length;d < b;) { - a[d++] = 0; + for (var l = a.length;l < c;) { + a[l++] = 0; } } return a; }; - Object.defineProperty(d.prototype, "matrix", {get:function() { + Object.defineProperty(l.prototype, "matrix", {get:function() { return this._matrix.slice(0, this._matrixX * this._matrixY); }, set:function(a) { - if (b.isNullOrUndefined(a)) { - k.Runtime.throwError("TypeError", k.Errors.NullPointerError, "matrix"); + if (c.isNullOrUndefined(a)) { + h.Runtime.throwError("TypeError", h.Errors.NullPointerError, "matrix"); } else { - for (var c = this._matrixX * this._matrixY, d = Math.min(a.length, c), f = Array(d), e = 0;e < d;e++) { - f[e] = b.toNumber(a[e]); + for (var m = this._matrixX * this._matrixY, l = Math.min(a.length, m), q = Array(l), n = 0;n < l;n++) { + q[n] = c.toNumber(a[n]); } - this._expandArray(f, c); - this._matrix = f; + this._expandArray(q, m); + this._matrix = q; } }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "matrixX", {get:function() { + Object.defineProperty(l.prototype, "matrixX", {get:function() { return this._matrixX; }, set:function(a) { - a = b.NumberUtilities.clamp(+a, 0, 15) | 0; + a = c.NumberUtilities.clamp(+a, 0, 15) | 0; this._matrixX !== a && (this._matrixX = a, this._expandArray(this._matrix, a * this._matrixY)); }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "matrixY", {get:function() { + Object.defineProperty(l.prototype, "matrixY", {get:function() { return this._matrixY; }, set:function(a) { - a = b.NumberUtilities.clamp(+a, 0, 15) | 0; + a = c.NumberUtilities.clamp(+a, 0, 15) | 0; this._matrixY !== a && (this._matrixY = a, this._expandArray(this._matrix, a * this._matrixX)); }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "divisor", {get:function() { + Object.defineProperty(l.prototype, "divisor", {get:function() { return this._divisor; }, set:function(a) { this._divisor = +a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "bias", {get:function() { + Object.defineProperty(l.prototype, "bias", {get:function() { return this._bias; }, set:function(a) { this._bias = +a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "preserveAlpha", {get:function() { + Object.defineProperty(l.prototype, "preserveAlpha", {get:function() { return this._preserveAlpha; }, set:function(a) { this._preserveAlpha = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "clamp", {get:function() { + Object.defineProperty(l.prototype, "clamp", {get:function() { return this._clamp; }, set:function(a) { this._clamp = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "color", {get:function() { + Object.defineProperty(l.prototype, "color", {get:function() { return this._color; }, set:function(a) { this._color = a >>> 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "alpha", {get:function() { + Object.defineProperty(l.prototype, "alpha", {get:function() { return this._alpha; }, set:function(a) { - this._alpha = b.NumberUtilities.clamp(+a, 0, 1); + this._alpha = c.NumberUtilities.clamp(+a, 0, 1); }, enumerable:!0, configurable:!0}); - d.prototype.clone = function() { - return new d(this._matrixX, this._matrixY, this.matrix, this._divisor, this._bias, this._preserveAlpha, this._clamp, this._color, this._alpha); + l.prototype.clone = function() { + return new l(this._matrixX, this._matrixY, this.matrix, this._divisor, this._bias, this._preserveAlpha, this._clamp, this._color, this._alpha); }; - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - return d; - }(f.filters.BitmapFilter); - g.ConvolutionFilter = s; - })(f.filters || (f.filters = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.filters.DisplacementMapFilterMode"); + l.classInitializer = null; + l.initializer = null; + l.classSymbols = null; + l.instanceSymbols = null; + return l; + }(a.filters.BitmapFilter); + v.ConvolutionFilter = p; + })(a.filters || (a.filters = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.filters.DisplacementMapFilterMode"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.WRAP = "wrap"; - a.CLAMP = "clamp"; - a.IGNORE = "ignore"; - a.COLOR = "color"; - return a; - }(g.ASNative); - f.DisplacementMapFilterMode = m; - })(f.filters || (f.filters = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.somewhatImplemented, m = b.AVM2.Runtime.asCoerceString, d = function(a) { - function b(a, c, d, f, l, g, m, k, h) { - "undefined" === typeof a && (a = null); - "undefined" === typeof c && (c = null); - "undefined" === typeof d && (d = 0); - "undefined" === typeof f && (f = 0); - "undefined" === typeof l && (l = 0); - "undefined" === typeof g && (g = 0); - "undefined" === typeof m && (m = "wrap"); - "undefined" === typeof k && (k = 0); - "undefined" === typeof h && (h = 0); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.WRAP = "wrap"; + e.CLAMP = "clamp"; + e.IGNORE = "ignore"; + e.COLOR = "color"; + return e; + }(a.ASNative); + h.DisplacementMapFilterMode = u; + })(h.filters || (h.filters = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.somewhatImplemented, u = c.AVM2.Runtime.asCoerceString, l = function(a) { + function c(a, e, m, k, f, d, b, g, l) { + void 0 === a && (a = null); + void 0 === e && (e = null); + void 0 === m && (m = 0); + void 0 === k && (k = 0); + void 0 === f && (f = 0); + void 0 === d && (d = 0); + void 0 === b && (b = "wrap"); + void 0 === g && (g = 0); + void 0 === l && (l = 0); this.mapBitmap = a; - this.mapPoint = c; - this.componentX = d; - this.componentY = f; - this.scaleX = l; - this.scaleY = g; - this.mode = m; - this.color = k; - this.alpha = h; + this.mapPoint = e; + this.componentX = m; + this.componentY = k; + this.scaleX = f; + this.scaleY = d; + this.mode = b; + this.color = g; + this.alpha = l; } - __extends(b, a); - b.FromUntyped = function(a) { - return new b(a.mapBitmap, a.mapPoint, a.componentX, a.componentY, a.scaleX, a.scaleY, a.mode, a.color, a.alpha); + __extends(c, a); + c.FromUntyped = function(a) { + return new c(a.mapBitmap, a.mapPoint, a.componentX, a.componentY, a.scaleX, a.scaleY, a.mode, a.color, a.alpha); }; - Object.defineProperty(b.prototype, "mapBitmap", {get:function() { - k("public flash.filters.DisplacementMapFilter::get mapBitmap"); + Object.defineProperty(c.prototype, "mapBitmap", {get:function() { + p("public flash.filters.DisplacementMapFilter::get mapBitmap"); return this._mapBitmap; }, set:function(a) { - k("public flash.filters.DisplacementMapFilter::set mapBitmap"); + p("public flash.filters.DisplacementMapFilter::set mapBitmap"); this._mapBitmap = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "mapPoint", {get:function() { - k("public flash.filters.DisplacementMapFilter::get mapPoint"); + Object.defineProperty(c.prototype, "mapPoint", {get:function() { + p("public flash.filters.DisplacementMapFilter::get mapPoint"); return this._mapPoint; }, set:function(a) { - k("public flash.filters.DisplacementMapFilter::set mapPoint"); + p("public flash.filters.DisplacementMapFilter::set mapPoint"); this._mapPoint = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "componentX", {get:function() { + Object.defineProperty(c.prototype, "componentX", {get:function() { return this._componentX; }, set:function(a) { - k("public flash.filters.DisplacementMapFilter::set componentX"); + p("public flash.filters.DisplacementMapFilter::set componentX"); this._componentX = a >>> 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "componentY", {get:function() { + Object.defineProperty(c.prototype, "componentY", {get:function() { return this._componentY; }, set:function(a) { - k("public flash.filters.DisplacementMapFilter::set componentY"); + p("public flash.filters.DisplacementMapFilter::set componentY"); this._componentY = a >>> 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "scaleX", {get:function() { + Object.defineProperty(c.prototype, "scaleX", {get:function() { return this._scaleX; }, set:function(a) { - k("public flash.filters.DisplacementMapFilter::set scaleX"); + p("public flash.filters.DisplacementMapFilter::set scaleX"); this._scaleX = +a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "scaleY", {get:function() { + Object.defineProperty(c.prototype, "scaleY", {get:function() { return this._scaleY; }, set:function(a) { - k("public flash.filters.DisplacementMapFilter::set scaleY"); + p("public flash.filters.DisplacementMapFilter::set scaleY"); this._scaleY = +a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "mode", {get:function() { + Object.defineProperty(c.prototype, "mode", {get:function() { return this._mode; }, set:function(a) { - k("public flash.filters.DisplacementMapFilter::set mode"); - this._mode = m(a); + p("public flash.filters.DisplacementMapFilter::set mode"); + this._mode = u(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "color", {get:function() { + Object.defineProperty(c.prototype, "color", {get:function() { return this._color; }, set:function(a) { - k("public flash.filters.DisplacementMapFilter::set color"); + p("public flash.filters.DisplacementMapFilter::set color"); this._color = a >>> 0 & 16777215; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "alpha", {get:function() { + Object.defineProperty(c.prototype, "alpha", {get:function() { return this._alpha; }, set:function(a) { - k("public flash.filters.DisplacementMapFilter::set alpha"); + p("public flash.filters.DisplacementMapFilter::set alpha"); this._alpha = +a; }, enumerable:!0, configurable:!0}); - b.prototype.clone = function() { - return new b(this._mapBitmap, this._mapPoint, this._componentX, this._componentY, this._scaleX, this._scaleY, this._mode, this._color, this._alpha); + c.prototype.clone = function() { + return new c(this._mapBitmap, this._mapPoint, this._componentX, this._componentY, this._scaleX, this._scaleY, this._mode, this._color, this._alpha); }; - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(f.filters.BitmapFilter); - g.DisplacementMapFilter = d; - })(f.filters || (f.filters = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.assert, m = function(d) { - function a(a, b, d, e, f, l, g, m, k, h, s) { - "undefined" === typeof a && (a = 4); - "undefined" === typeof b && (b = 45); - "undefined" === typeof d && (d = 0); - "undefined" === typeof e && (e = 1); - "undefined" === typeof f && (f = 4); - "undefined" === typeof l && (l = 4); - "undefined" === typeof g && (g = 1); - "undefined" === typeof m && (m = 1); - "undefined" === typeof k && (k = !1); - "undefined" === typeof h && (h = !1); - "undefined" === typeof s && (s = !1); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.filters.BitmapFilter); + h.DisplacementMapFilter = l; + })(a.filters || (a.filters = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.assert, u = function(a) { + function e(a, e, c, l, k, f, d, b, g, h, w) { + void 0 === a && (a = 4); + void 0 === e && (e = 45); + void 0 === c && (c = 0); + void 0 === l && (l = 1); + void 0 === k && (k = 4); + void 0 === f && (f = 4); + void 0 === d && (d = 1); + void 0 === b && (b = 1); + void 0 === g && (g = !1); + void 0 === h && (h = !1); + void 0 === w && (w = !1); this.distance = a; - this.angle = b; - this.color = d; - this.alpha = e; - this.blurX = f; - this.blurY = l; - this.strength = g; - this.quality = m; - this.inner = k; + this.angle = e; + this.color = c; + this.alpha = l; + this.blurX = k; + this.blurY = f; + this.strength = d; + this.quality = b; + this.inner = g; this.knockout = h; - this.hideObject = s; + this.hideObject = w; } - __extends(a, d); - a.FromUntyped = function(b) { - k(b.colors && 1 === b.colors.length, "colors must be Array of length 1"); - return new a(b.distance, 180 * b.angle / Math.PI, b.colors[0] >>> 8, (b.colors[0] & 255) / 255, b.blurX, b.blurY, b.strength, b.quality, b.inner, b.knockout, !b.compositeSource); - }; - a.prototype._updateFilterBounds = function(a) { - if (!this.inner && (g.BitmapFilter._updateBlurBounds(a, this._blurX, this._blurY, this._quality), 0 !== this._distance)) { - var b = this._angle * Math.PI / 180; - a.x += Math.floor(Math.cos(b) * this._distance); - a.y += Math.floor(Math.sin(b) * this._distance); + __extends(e, a); + e.FromUntyped = function(a) { + p(a.colors && 1 === a.colors.length, "colors must be Array of length 1"); + return new e(a.distance, 180 * a.angle / Math.PI, a.colors[0] >>> 8, (a.colors[0] & 255) / 255, a.blurX, a.blurY, a.strength, a.quality, a.inner, a.knockout, !a.compositeSource); + }; + e.prototype._updateFilterBounds = function(a) { + if (!this.inner && (h.BitmapFilter._updateBlurBounds(a, this._blurX, this._blurY, this._quality), 0 !== this._distance)) { + var e = this._angle * Math.PI / 180; + a.x += Math.floor(Math.cos(e) * this._distance); + a.y += Math.floor(Math.sin(e) * this._distance); 0 < a.left && (a.left = 0); 0 < a.top && (a.top = 0); } }; - a.prototype._serialize = function(a) { - a.ensureAdditionalCapacity(48); - a.writeIntUnsafe(0); - a.writeFloatUnsafe(this._alpha); - a.writeFloatUnsafe(this._angle); - a.writeFloatUnsafe(this._blurX); - a.writeFloatUnsafe(this._blurY); - a.writeIntUnsafe(this._color); - a.writeFloatUnsafe(this._distance); - a.writeIntUnsafe(this._hideObject); - a.writeIntUnsafe(this._inner); - a.writeIntUnsafe(this._knockout); - a.writeIntUnsafe(this._quality); - a.writeFloatUnsafe(this._strength); - }; - Object.defineProperty(a.prototype, "distance", {get:function() { + Object.defineProperty(e.prototype, "distance", {get:function() { return this._distance; }, set:function(a) { this._distance = +a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "angle", {get:function() { + Object.defineProperty(e.prototype, "angle", {get:function() { return this._angle; }, set:function(a) { this._angle = +a % 360; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "color", {get:function() { + Object.defineProperty(e.prototype, "color", {get:function() { return this._color; }, set:function(a) { this._color = a >>> 0 & 16777215; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "alpha", {get:function() { + Object.defineProperty(e.prototype, "alpha", {get:function() { return this._alpha; }, set:function(a) { - this._alpha = b.NumberUtilities.clamp(+a, 0, 1); + this._alpha = c.NumberUtilities.clamp(+a, 0, 1); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "blurX", {get:function() { + Object.defineProperty(e.prototype, "blurX", {get:function() { return this._blurX; }, set:function(a) { - this._blurX = b.NumberUtilities.clamp(+a, 0, 255); + this._blurX = c.NumberUtilities.clamp(+a, 0, 255); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "blurY", {get:function() { + Object.defineProperty(e.prototype, "blurY", {get:function() { return this._blurY; }, set:function(a) { - this._blurY = b.NumberUtilities.clamp(+a, 0, 255); + this._blurY = c.NumberUtilities.clamp(+a, 0, 255); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "hideObject", {get:function() { + Object.defineProperty(e.prototype, "hideObject", {get:function() { return this._hideObject; }, set:function(a) { this._hideObject = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "inner", {get:function() { + Object.defineProperty(e.prototype, "inner", {get:function() { return this._inner; }, set:function(a) { this._inner = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "knockout", {get:function() { + Object.defineProperty(e.prototype, "knockout", {get:function() { return this._knockout; }, set:function(a) { this._knockout = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "quality", {get:function() { + Object.defineProperty(e.prototype, "quality", {get:function() { return this._quality; }, set:function(a) { - this._quality = b.NumberUtilities.clamp(a | 0, 0, 15); + this._quality = c.NumberUtilities.clamp(a | 0, 0, 15); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "strength", {get:function() { + Object.defineProperty(e.prototype, "strength", {get:function() { return this._strength; }, set:function(a) { - this._strength = b.NumberUtilities.clamp(+a, 0, 255); + this._strength = c.NumberUtilities.clamp(+a, 0, 255); }, enumerable:!0, configurable:!0}); - a.prototype.clone = function() { - return new a(this._distance, this._angle, this._color, this._alpha, this._blurX, this._blurY, this._strength, this._quality, this._inner, this._knockout, this._hideObject); + e.prototype.clone = function() { + return new e(this._distance, this._angle, this._color, this._alpha, this._blurX, this._blurY, this._strength, this._quality, this._inner, this._knockout, this._hideObject); }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(f.filters.BitmapFilter); - g.DropShadowFilter = m; - })(f.filters || (f.filters = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.assert, m = function(d) { - function a(a, b, d, e, f, l, g, m) { - "undefined" === typeof a && (a = 16711680); - "undefined" === typeof b && (b = 1); - "undefined" === typeof d && (d = 6); - "undefined" === typeof e && (e = 6); - "undefined" === typeof f && (f = 2); - "undefined" === typeof l && (l = 1); - "undefined" === typeof g && (g = !1); - "undefined" === typeof m && (m = !1); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.filters.BitmapFilter); + h.DropShadowFilter = u; + })(a.filters || (a.filters = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.assert, u = function(a) { + function e(a, e, c, l, k, f, d, b) { + void 0 === a && (a = 16711680); + void 0 === e && (e = 1); + void 0 === c && (c = 6); + void 0 === l && (l = 6); + void 0 === k && (k = 2); + void 0 === f && (f = 1); + void 0 === d && (d = !1); + void 0 === b && (b = !1); this.color = a; - this.alpha = b; - this.blurX = d; - this.blurY = e; - this.strength = f; - this.quality = l; - this.inner = g; - this.knockout = m; - } - __extends(a, d); - a.FromUntyped = function(b) { - k(b.colors && 1 === b.colors.length, "colors must be Array of length 1"); - return new a(b.colors[0] >>> 8, (b.colors[0] & 255) / 255, b.blurX, b.blurY, b.strength, b.quality, b.inner, b.knockout); - }; - a.prototype._updateFilterBounds = function(a) { - g.BitmapFilter._updateBlurBounds(a, this._blurX, this._blurY, this._quality); - }; - a.prototype._serialize = function(a) { - a.ensureAdditionalCapacity(36); - a.writeIntUnsafe(2); - a.writeFloatUnsafe(this._alpha); - a.writeFloatUnsafe(this._blurX); - a.writeFloatUnsafe(this._blurY); - a.writeIntUnsafe(this._color); - a.writeIntUnsafe(this._inner); - a.writeIntUnsafe(this._knockout); - a.writeIntUnsafe(this._quality); - a.writeFloatUnsafe(this._strength); + this.alpha = e; + this.blurX = c; + this.blurY = l; + this.strength = k; + this.quality = f; + this.inner = d; + this.knockout = b; + } + __extends(e, a); + e.FromUntyped = function(a) { + p(a.colors && 1 === a.colors.length, "colors must be Array of length 1"); + return new e(a.colors[0] >>> 8, (a.colors[0] & 255) / 255, a.blurX, a.blurY, a.strength, a.quality, a.inner, a.knockout); }; - Object.defineProperty(a.prototype, "color", {get:function() { + e.prototype._updateFilterBounds = function(a) { + h.BitmapFilter._updateBlurBounds(a, this._blurX, this._blurY, this._quality); + }; + Object.defineProperty(e.prototype, "color", {get:function() { return this._color; }, set:function(a) { this._color = a >>> 0 & 16777215; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "alpha", {get:function() { + Object.defineProperty(e.prototype, "alpha", {get:function() { return this._alpha; }, set:function(a) { - this._alpha = b.NumberUtilities.clamp(+a, 0, 1); + this._alpha = c.NumberUtilities.clamp(+a, 0, 1); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "blurX", {get:function() { + Object.defineProperty(e.prototype, "blurX", {get:function() { return this._blurX; }, set:function(a) { - this._blurX = b.NumberUtilities.clamp(+a, 0, 255); + this._blurX = c.NumberUtilities.clamp(+a, 0, 255); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "blurY", {get:function() { + Object.defineProperty(e.prototype, "blurY", {get:function() { return this._blurY; }, set:function(a) { - this._blurY = b.NumberUtilities.clamp(+a, 0, 255); + this._blurY = c.NumberUtilities.clamp(+a, 0, 255); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "inner", {get:function() { + Object.defineProperty(e.prototype, "inner", {get:function() { return this._inner; }, set:function(a) { this._inner = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "knockout", {get:function() { + Object.defineProperty(e.prototype, "knockout", {get:function() { return this._knockout; }, set:function(a) { this._knockout = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "quality", {get:function() { + Object.defineProperty(e.prototype, "quality", {get:function() { return this._quality; }, set:function(a) { - this._quality = b.NumberUtilities.clamp(a | 0, 0, 15); + this._quality = c.NumberUtilities.clamp(a | 0, 0, 15); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "strength", {get:function() { + Object.defineProperty(e.prototype, "strength", {get:function() { return this._strength; }, set:function(a) { - this._strength = b.NumberUtilities.clamp(+a, 0, 255); + this._strength = c.NumberUtilities.clamp(+a, 0, 255); }, enumerable:!0, configurable:!0}); - a.prototype.clone = function() { - return new a(this._color, this._alpha, this._blurX, this._blurY, this._strength, this._quality, this._inner, this._knockout); + e.prototype.clone = function() { + return new e(this._color, this._alpha, this._blurX, this._blurY, this._strength, this._quality, this._inner, this._knockout); }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(f.filters.BitmapFilter); - g.GlowFilter = m; - })(f.filters || (f.filters = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var s = b.AVM2.Runtime.asCoerceString, m = function(d) { - function a(a, b, d, e, f, l, m, k, r, h, s) { - "undefined" === typeof a && (a = 4); - "undefined" === typeof b && (b = 45); - "undefined" === typeof d && (d = null); - "undefined" === typeof e && (e = null); - "undefined" === typeof f && (f = null); - "undefined" === typeof l && (l = 4); - "undefined" === typeof m && (m = 4); - "undefined" === typeof k && (k = 1); - "undefined" === typeof r && (r = 1); - "undefined" === typeof h && (h = "inner"); - "undefined" === typeof s && (s = !1); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.filters.BitmapFilter); + h.GlowFilter = u; + })(a.filters || (a.filters = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(v) { + var p = c.AVM2.Runtime.asCoerceString, u = function(l) { + function e(a, e, c, l, k, f, d, b, g, h, w) { + void 0 === a && (a = 4); + void 0 === e && (e = 45); + void 0 === c && (c = null); + void 0 === l && (l = null); + void 0 === k && (k = null); + void 0 === f && (f = 4); + void 0 === d && (d = 4); + void 0 === b && (b = 1); + void 0 === g && (g = 1); + void 0 === h && (h = "inner"); + void 0 === w && (w = !1); this.distance = a; - this.angle = b; - g.GradientArrays.sanitize(d, e, f); - this._colors = g.GradientArrays.colors; - this._alphas = g.GradientArrays.alphas; - this._ratios = g.GradientArrays.ratios; - this.blurX = l; - this.blurY = m; - this.strength = k; - this.quality = r; + this.angle = e; + v.GradientArrays.sanitize(c, l, k); + this._colors = v.GradientArrays.colors; + this._alphas = v.GradientArrays.alphas; + this._ratios = v.GradientArrays.ratios; + this.blurX = f; + this.blurY = d; + this.strength = b; + this.quality = g; this.type = h; - this.knockout = s; + this.knockout = w; } - __extends(a, d); - a.FromUntyped = function(b) { - for (var d = [], g = [], e = 0;e < b.colors.length;e++) { - var m = b.colors[e]; - d.push(m >>> 8); - g.push(m & 255) / 255; - } - e = f.filters.BitmapFilterType.OUTER; - b.onTop ? e = f.filters.BitmapFilterType.FULL : b.inner && (e = f.filters.BitmapFilterType.INNER); - return new a(b.distance, 180 * b.angle / Math.PI, d, g, b.ratios, b.blurX, b.blurY, b.strength, b.quality, e, b.knockout); - }; - a.prototype._updateFilterBounds = function(a) { - if (this.type !== g.BitmapFilterType.INNER && (g.BitmapFilter._updateBlurBounds(a, this._blurX, this._blurY, this._quality), 0 !== this._distance)) { - var b = this._angle * Math.PI / 180; - a.x += Math.floor(Math.cos(b) * this._distance); - a.y += Math.floor(Math.sin(b) * this._distance); + __extends(e, l); + e.FromUntyped = function(c) { + for (var l = [], h = [], n = 0;n < c.colors.length;n++) { + var k = c.colors[n]; + l.push(k >>> 8); + h.push(k & 255) / 255; + } + n = a.filters.BitmapFilterType.OUTER; + c.onTop ? n = a.filters.BitmapFilterType.FULL : c.inner && (n = a.filters.BitmapFilterType.INNER); + return new e(c.distance, 180 * c.angle / Math.PI, l, h, c.ratios, c.blurX, c.blurY, c.strength, c.quality, n, c.knockout); + }; + e.prototype._updateFilterBounds = function(a) { + if (this.type !== v.BitmapFilterType.INNER && (v.BitmapFilter._updateBlurBounds(a, this._blurX, this._blurY, this._quality), 0 !== this._distance)) { + var e = this._angle * Math.PI / 180; + a.x += Math.floor(Math.cos(e) * this._distance); + a.y += Math.floor(Math.sin(e) * this._distance); 0 < a.left && (a.left = 0); 0 < a.top && (a.top = 0); } }; - Object.defineProperty(a.prototype, "distance", {get:function() { + Object.defineProperty(e.prototype, "distance", {get:function() { return this._distance; }, set:function(a) { this._distance = +a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "angle", {get:function() { + Object.defineProperty(e.prototype, "angle", {get:function() { return this._angle; }, set:function(a) { this._angle = +a % 360; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "colors", {get:function() { + Object.defineProperty(e.prototype, "colors", {get:function() { return this._colors.concat(); }, set:function(a) { - b.isNullOrUndefined(a) ? k.Runtime.throwError("TypeError", k.Errors.NullPointerError, "colors") : (this._colors = g.GradientArrays.sanitizeColors(a), a = this._colors.length, this._alphas = g.GradientArrays.sanitizeAlphas(this._alphas, a, a), this._ratios = g.GradientArrays.sanitizeRatios(this._ratios, a, a)); + c.isNullOrUndefined(a) ? h.Runtime.throwError("TypeError", h.Errors.NullPointerError, "colors") : (this._colors = v.GradientArrays.sanitizeColors(a), a = this._colors.length, this._alphas = v.GradientArrays.sanitizeAlphas(this._alphas, a, a), this._ratios = v.GradientArrays.sanitizeRatios(this._ratios, a, a)); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "alphas", {get:function() { + Object.defineProperty(e.prototype, "alphas", {get:function() { return this._alphas.concat(); }, set:function(a) { - b.isNullOrUndefined(a) ? k.Runtime.throwError("TypeError", k.Errors.NullPointerError, "alphas") : (g.GradientArrays.sanitize(this._colors, a, this._ratios), this._colors = g.GradientArrays.colors, this._alphas = g.GradientArrays.alphas, this._ratios = g.GradientArrays.ratios); + c.isNullOrUndefined(a) ? h.Runtime.throwError("TypeError", h.Errors.NullPointerError, "alphas") : (v.GradientArrays.sanitize(this._colors, a, this._ratios), this._colors = v.GradientArrays.colors, this._alphas = v.GradientArrays.alphas, this._ratios = v.GradientArrays.ratios); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "ratios", {get:function() { + Object.defineProperty(e.prototype, "ratios", {get:function() { return this._ratios.concat(); }, set:function(a) { - b.isNullOrUndefined(a) ? k.Runtime.throwError("TypeError", k.Errors.NullPointerError, "ratios") : (g.GradientArrays.sanitize(this._colors, this._alphas, a), this._colors = g.GradientArrays.colors, this._alphas = g.GradientArrays.alphas, this._ratios = g.GradientArrays.ratios); + c.isNullOrUndefined(a) ? h.Runtime.throwError("TypeError", h.Errors.NullPointerError, "ratios") : (v.GradientArrays.sanitize(this._colors, this._alphas, a), this._colors = v.GradientArrays.colors, this._alphas = v.GradientArrays.alphas, this._ratios = v.GradientArrays.ratios); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "blurX", {get:function() { + Object.defineProperty(e.prototype, "blurX", {get:function() { return this._blurX; }, set:function(a) { - this._blurX = b.NumberUtilities.clamp(+a, 0, 255); + this._blurX = c.NumberUtilities.clamp(+a, 0, 255); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "blurY", {get:function() { + Object.defineProperty(e.prototype, "blurY", {get:function() { return this._blurY; }, set:function(a) { - this._blurY = b.NumberUtilities.clamp(+a, 0, 255); + this._blurY = c.NumberUtilities.clamp(+a, 0, 255); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "knockout", {get:function() { + Object.defineProperty(e.prototype, "knockout", {get:function() { return this._knockout; }, set:function(a) { this._knockout = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "quality", {get:function() { + Object.defineProperty(e.prototype, "quality", {get:function() { return this._quality; }, set:function(a) { - this._quality = b.NumberUtilities.clamp(a | 0, 0, 15); + this._quality = c.NumberUtilities.clamp(a | 0, 0, 15); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "strength", {get:function() { + Object.defineProperty(e.prototype, "strength", {get:function() { return this._strength; }, set:function(a) { - this._strength = b.NumberUtilities.clamp(+a, 0, 255); + this._strength = c.NumberUtilities.clamp(+a, 0, 255); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "type", {get:function() { + Object.defineProperty(e.prototype, "type", {get:function() { return this._type; }, set:function(a) { - a = s(a); - null === a ? k.Runtime.throwError("TypeError", k.Errors.NullPointerError, "type") : this._type = a === g.BitmapFilterType.INNER || a === g.BitmapFilterType.OUTER ? a : g.BitmapFilterType.FULL; + a = p(a); + null === a ? h.Runtime.throwError("TypeError", h.Errors.NullPointerError, "type") : this._type = a === v.BitmapFilterType.INNER || a === v.BitmapFilterType.OUTER ? a : v.BitmapFilterType.FULL; }, enumerable:!0, configurable:!0}); - a.prototype.clone = function() { - return new a(this._distance, this._angle, this._colors, this._alphas, this._ratios, this._blurX, this._blurY, this._strength, this._quality, this._type, this._knockout); + e.prototype.clone = function() { + return new e(this._distance, this._angle, this._colors, this._alphas, this._ratios, this._blurX, this._blurY, this._strength, this._quality, this._type, this._knockout); }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(f.filters.BitmapFilter); - g.GradientBevelFilter = m; - })(f.filters || (f.filters = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var s = b.AVM2.Runtime.asCoerceString, m = function(d) { - function a(a, b, d, e, f, l, m, k, r, h, s) { - "undefined" === typeof a && (a = 4); - "undefined" === typeof b && (b = 45); - "undefined" === typeof d && (d = null); - "undefined" === typeof e && (e = null); - "undefined" === typeof f && (f = null); - "undefined" === typeof l && (l = 4); - "undefined" === typeof m && (m = 4); - "undefined" === typeof k && (k = 1); - "undefined" === typeof r && (r = 1); - "undefined" === typeof h && (h = "inner"); - "undefined" === typeof s && (s = !1); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.filters.BitmapFilter); + v.GradientBevelFilter = u; + })(a.filters || (a.filters = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(v) { + var p = c.AVM2.Runtime.asCoerceString, u = function(l) { + function e(a, e, c, l, k, f, d, b, g, h, w) { + void 0 === a && (a = 4); + void 0 === e && (e = 45); + void 0 === c && (c = null); + void 0 === l && (l = null); + void 0 === k && (k = null); + void 0 === f && (f = 4); + void 0 === d && (d = 4); + void 0 === b && (b = 1); + void 0 === g && (g = 1); + void 0 === h && (h = "inner"); + void 0 === w && (w = !1); this.distance = a; - this.angle = b; - g.GradientArrays.sanitize(d, e, f); - this._colors = g.GradientArrays.colors; - this._alphas = g.GradientArrays.alphas; - this._ratios = g.GradientArrays.ratios; - this.blurX = l; - this.blurY = m; - this.strength = k; - this.quality = r; + this.angle = e; + v.GradientArrays.sanitize(c, l, k); + this._colors = v.GradientArrays.colors; + this._alphas = v.GradientArrays.alphas; + this._ratios = v.GradientArrays.ratios; + this.blurX = f; + this.blurY = d; + this.strength = b; + this.quality = g; this.type = h; - this.knockout = s; + this.knockout = w; } - __extends(a, d); - a.FromUntyped = function(b) { - for (var d = [], g = [], e = 0;e < b.colors.length;e++) { - var m = b.colors[e]; - d.push(m >>> 8); - g.push(m & 255) / 255; - } - e = f.filters.BitmapFilterType.OUTER; - b.onTop ? e = f.filters.BitmapFilterType.FULL : b.inner && (e = f.filters.BitmapFilterType.INNER); - return new a(b.distance, 180 * b.angle / Math.PI, d, g, b.ratios, b.blurX, b.blurY, b.strength, b.quality, e, b.knockout); - }; - a.prototype._updateFilterBounds = function(a) { - if (this.type !== g.BitmapFilterType.INNER && (g.BitmapFilter._updateBlurBounds(a, this._blurX, this._blurY, this._quality), 0 !== this._distance)) { - var b = this._angle * Math.PI / 180; - a.x += Math.floor(Math.cos(b) * this._distance); - a.y += Math.floor(Math.sin(b) * this._distance); + __extends(e, l); + e.FromUntyped = function(c) { + for (var l = [], h = [], n = 0;n < c.colors.length;n++) { + var k = c.colors[n]; + l.push(k >>> 8); + h.push(k & 255) / 255; + } + n = a.filters.BitmapFilterType.OUTER; + c.onTop ? n = a.filters.BitmapFilterType.FULL : c.inner && (n = a.filters.BitmapFilterType.INNER); + return new e(c.distance, 180 * c.angle / Math.PI, l, h, c.ratios, c.blurX, c.blurY, c.strength, c.quality, n, c.knockout); + }; + e.prototype._updateFilterBounds = function(a) { + if (this.type !== v.BitmapFilterType.INNER && (v.BitmapFilter._updateBlurBounds(a, this._blurX, this._blurY, this._quality), 0 !== this._distance)) { + var e = this._angle * Math.PI / 180; + a.x += Math.floor(Math.cos(e) * this._distance); + a.y += Math.floor(Math.sin(e) * this._distance); 0 < a.left && (a.left = 0); 0 < a.top && (a.top = 0); } }; - Object.defineProperty(a.prototype, "distance", {get:function() { + Object.defineProperty(e.prototype, "distance", {get:function() { return this._distance; }, set:function(a) { this._distance = +a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "angle", {get:function() { + Object.defineProperty(e.prototype, "angle", {get:function() { return this._angle; }, set:function(a) { this._angle = +a % 360; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "colors", {get:function() { + Object.defineProperty(e.prototype, "colors", {get:function() { return this._colors.concat(); }, set:function(a) { - b.isNullOrUndefined(a) ? k.Runtime.throwError("TypeError", k.Errors.NullPointerError, "colors") : (this._colors = g.GradientArrays.sanitizeColors(a), a = this._colors.length, this._alphas = g.GradientArrays.sanitizeAlphas(this._alphas, a, a), this._ratios = g.GradientArrays.sanitizeRatios(this._ratios, a, a)); + c.isNullOrUndefined(a) ? h.Runtime.throwError("TypeError", h.Errors.NullPointerError, "colors") : (this._colors = v.GradientArrays.sanitizeColors(a), a = this._colors.length, this._alphas = v.GradientArrays.sanitizeAlphas(this._alphas, a, a), this._ratios = v.GradientArrays.sanitizeRatios(this._ratios, a, a)); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "alphas", {get:function() { + Object.defineProperty(e.prototype, "alphas", {get:function() { return this._alphas.concat(); }, set:function(a) { - b.isNullOrUndefined(a) ? k.Runtime.throwError("TypeError", k.Errors.NullPointerError, "alphas") : (g.GradientArrays.sanitize(this._colors, a, this._ratios), this._colors = g.GradientArrays.colors, this._alphas = g.GradientArrays.alphas, this._ratios = g.GradientArrays.ratios); + c.isNullOrUndefined(a) ? h.Runtime.throwError("TypeError", h.Errors.NullPointerError, "alphas") : (v.GradientArrays.sanitize(this._colors, a, this._ratios), this._colors = v.GradientArrays.colors, this._alphas = v.GradientArrays.alphas, this._ratios = v.GradientArrays.ratios); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "ratios", {get:function() { + Object.defineProperty(e.prototype, "ratios", {get:function() { return this._ratios.concat(); }, set:function(a) { - b.isNullOrUndefined(a) ? k.Runtime.throwError("TypeError", k.Errors.NullPointerError, "ratios") : (g.GradientArrays.sanitize(this._colors, this._alphas, a), this._colors = g.GradientArrays.colors, this._alphas = g.GradientArrays.alphas, this._ratios = g.GradientArrays.ratios); + c.isNullOrUndefined(a) ? h.Runtime.throwError("TypeError", h.Errors.NullPointerError, "ratios") : (v.GradientArrays.sanitize(this._colors, this._alphas, a), this._colors = v.GradientArrays.colors, this._alphas = v.GradientArrays.alphas, this._ratios = v.GradientArrays.ratios); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "blurX", {get:function() { + Object.defineProperty(e.prototype, "blurX", {get:function() { return this._blurX; }, set:function(a) { - this._blurX = b.NumberUtilities.clamp(+a, 0, 255); + this._blurX = c.NumberUtilities.clamp(+a, 0, 255); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "blurY", {get:function() { + Object.defineProperty(e.prototype, "blurY", {get:function() { return this._blurY; }, set:function(a) { - this._blurY = b.NumberUtilities.clamp(+a, 0, 255); + this._blurY = c.NumberUtilities.clamp(+a, 0, 255); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "knockout", {get:function() { + Object.defineProperty(e.prototype, "knockout", {get:function() { return this._knockout; }, set:function(a) { this._knockout = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "quality", {get:function() { + Object.defineProperty(e.prototype, "quality", {get:function() { return this._quality; }, set:function(a) { - this._quality = b.NumberUtilities.clamp(a | 0, 0, 15); + this._quality = c.NumberUtilities.clamp(a | 0, 0, 15); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "strength", {get:function() { + Object.defineProperty(e.prototype, "strength", {get:function() { return this._strength; }, set:function(a) { - this._strength = b.NumberUtilities.clamp(+a, 0, 255); + this._strength = c.NumberUtilities.clamp(+a, 0, 255); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "type", {get:function() { + Object.defineProperty(e.prototype, "type", {get:function() { return this._type; }, set:function(a) { - a = s(a); - null === a ? k.Runtime.throwError("TypeError", k.Errors.NullPointerError, "type") : this._type = a === g.BitmapFilterType.INNER || a === g.BitmapFilterType.OUTER ? a : g.BitmapFilterType.FULL; + a = p(a); + null === a ? h.Runtime.throwError("TypeError", h.Errors.NullPointerError, "type") : this._type = a === v.BitmapFilterType.INNER || a === v.BitmapFilterType.OUTER ? a : v.BitmapFilterType.FULL; }, enumerable:!0, configurable:!0}); - a.prototype.clone = function() { - return new a(this._distance, this._angle, this._colors, this._alphas, this._ratios, this._blurX, this._blurY, this._strength, this._quality, this._type, this._knockout); + e.prototype.clone = function() { + return new e(this._distance, this._angle, this._colors, this._alphas, this._ratios, this._blurX, this._blurY, this._strength, this._quality, this._type, this._knockout); }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(f.filters.BitmapFilter); - g.GradientGlowFilter = m; - })(f.filters || (f.filters = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.IntegerUtilities.toS16, m = b.IntegerUtilities.clampS8U8, d = function(a) { - function b(a, c, d, f, l, g, m, k) { - "undefined" === typeof a && (a = 1); - "undefined" === typeof c && (c = 1); - "undefined" === typeof d && (d = 1); - "undefined" === typeof f && (f = 1); - "undefined" === typeof l && (l = 0); - "undefined" === typeof g && (g = 0); - "undefined" === typeof m && (m = 0); - "undefined" === typeof k && (k = 0); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.filters.BitmapFilter); + v.GradientGlowFilter = u; + })(a.filters || (a.filters = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.IntegerUtilities.toS16, u = c.IntegerUtilities.clampS8U8, l = function(a) { + function c(a, e, l, k, f, d, b, g) { + void 0 === a && (a = 1); + void 0 === e && (e = 1); + void 0 === l && (l = 1); + void 0 === k && (k = 1); + void 0 === f && (f = 0); + void 0 === d && (d = 0); + void 0 === b && (b = 0); + void 0 === g && (g = 0); this.redMultiplier = +a; - this.greenMultiplier = +c; - this.blueMultiplier = +d; - this.alphaMultiplier = +f; - this.redOffset = +l; - this.greenOffset = +g; - this.blueOffset = +m; - this.alphaOffset = +k; + this.greenMultiplier = +e; + this.blueMultiplier = +l; + this.alphaMultiplier = +k; + this.redOffset = +f; + this.greenOffset = +d; + this.blueOffset = +b; + this.alphaOffset = +g; } - __extends(b, a); - b.FromCXForm = function(a) { - return new b(a.redMultiplier / 256, a.greenMultiplier / 256, a.blueMultiplier / 256, a.alphaMultiplier / 256, a.redOffset, a.greenOffset, a.blueOffset, a.alphaOffset); - }; - Object.defineProperty(b.prototype, "native_redMultiplier", {get:function() { + __extends(c, a); + Object.defineProperty(c.prototype, "native_redMultiplier", {get:function() { return this.redMultiplier; }, set:function(a) { this.redMultiplier = +a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "native_greenMultiplier", {get:function() { + Object.defineProperty(c.prototype, "native_greenMultiplier", {get:function() { return this.greenMultiplier; }, set:function(a) { this.greenMultiplier = +a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "native_blueMultiplier", {get:function() { + Object.defineProperty(c.prototype, "native_blueMultiplier", {get:function() { return this.blueMultiplier; }, set:function(a) { this.blueMultiplier = +a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "native_alphaMultiplier", {get:function() { + Object.defineProperty(c.prototype, "native_alphaMultiplier", {get:function() { return this.alphaMultiplier; }, set:function(a) { this.alphaMultiplier = +a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "native_redOffset", {get:function() { + Object.defineProperty(c.prototype, "native_redOffset", {get:function() { return this.redOffset; }, set:function(a) { this.redOffset = +a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "native_greenOffset", {get:function() { + Object.defineProperty(c.prototype, "native_greenOffset", {get:function() { return this.greenOffset; }, set:function(a) { this.greenOffset = +a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "native_blueOffset", {get:function() { + Object.defineProperty(c.prototype, "native_blueOffset", {get:function() { return this.blueOffset; }, set:function(a) { this.blueOffset = +a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "native_alphaOffset", {get:function() { + Object.defineProperty(c.prototype, "native_alphaOffset", {get:function() { return this.alphaOffset; }, set:function(a) { this.alphaOffset = +a; }, enumerable:!0, configurable:!0}); - b.prototype.ColorTransform = function(a, b, c, d, f, g, m, k) { - "undefined" === typeof a && (a = 1); - "undefined" === typeof b && (b = 1); - "undefined" === typeof c && (c = 1); - "undefined" === typeof d && (d = 1); - "undefined" === typeof f && (f = 0); - "undefined" === typeof g && (g = 0); - "undefined" === typeof m && (m = 0); - "undefined" === typeof k && (k = 0); + c.prototype.ColorTransform = function(a, e, c, k, f, d, b, g) { + void 0 === a && (a = 1); + void 0 === e && (e = 1); + void 0 === c && (c = 1); + void 0 === k && (k = 1); + void 0 === f && (f = 0); + void 0 === d && (d = 0); + void 0 === b && (b = 0); + void 0 === g && (g = 0); this.redMultiplier = a; - this.greenMultiplier = b; + this.greenMultiplier = e; this.blueMultiplier = c; - this.alphaMultiplier = d; + this.alphaMultiplier = k; this.redOffset = f; - this.greenOffset = g; - this.blueOffset = m; - this.alphaOffset = k; + this.greenOffset = d; + this.blueOffset = b; + this.alphaOffset = g; }; - Object.defineProperty(b.prototype, "color", {get:function() { + Object.defineProperty(c.prototype, "color", {get:function() { return this.redOffset << 16 | this.greenOffset << 8 | this.blueOffset; }, set:function(a) { this.redOffset = a >> 16 & 255; @@ -30719,7 +37625,7 @@ this.blueOffset = a & 255; this.redMultiplier = this.greenMultiplier = this.blueMultiplier = 1; }, enumerable:!0, configurable:!0}); - b.prototype.concat = function(a) { + c.prototype.concat = function(a) { this.redMultiplier *= a.redMultiplier; this.greenMultiplier *= a.greenMultiplier; this.blueMultiplier *= a.blueMultiplier; @@ -30729,7 +37635,7 @@ this.blueOffset += a.blueOffset; this.alphaOffset += a.alphaOffset; }; - b.prototype.preMultiply = function(a) { + c.prototype.preMultiply = function(a) { this.redOffset += a.redOffset * this.redMultiplier; this.greenOffset += a.greenOffset * this.greenMultiplier; this.blueOffset += a.blueOffset * this.blueMultiplier; @@ -30739,7 +37645,7 @@ this.blueMultiplier *= a.blueMultiplier; this.alphaMultiplier *= a.alphaMultiplier; }; - b.prototype.copyFrom = function(a) { + c.prototype.copyFrom = function(a) { this.redMultiplier = a.redMultiplier; this.greenMultiplier = a.greenMultiplier; this.blueMultiplier = a.blueMultiplier; @@ -30749,453 +37655,509 @@ this.blueOffset = a.blueOffset; this.alphaOffset = a.alphaOffset; }; - b.prototype.setTo = function(a, b, c, d, f, g, m, k) { + c.prototype.copyFromUntyped = function(a) { + this.redMultiplier = a.redMultiplier / 256; + this.greenMultiplier = a.greenMultiplier / 256; + this.blueMultiplier = a.blueMultiplier / 256; + this.alphaMultiplier = a.alphaMultiplier / 256; + this.redOffset = a.redOffset; + this.greenOffset = a.greenOffset; + this.blueOffset = a.blueOffset; + this.alphaOffset = a.alphaOffset; + }; + c.prototype.setTo = function(a, e, c, k, f, d, b, g) { this.redMultiplier = a; - this.greenMultiplier = b; + this.greenMultiplier = e; this.blueMultiplier = c; - this.alphaMultiplier = d; + this.alphaMultiplier = k; this.redOffset = f; - this.greenOffset = g; - this.blueOffset = m; - this.alphaOffset = k; - }; - b.prototype.clone = function() { - return new b(this.redMultiplier, this.greenMultiplier, this.blueMultiplier, this.alphaMultiplier, this.redOffset, this.greenOffset, this.blueOffset, this.alphaOffset); - }; - b.prototype.convertToFixedPoint = function() { - this.redMultiplier = m(this.redMultiplier); - this.greenMultiplier = m(this.greenMultiplier); - this.blueMultiplier = m(this.blueMultiplier); - this.alphaMultiplier = m(this.alphaMultiplier); - this.redOffset = k(this.redOffset); - this.greenOffset = k(this.greenOffset); - this.blueOffset = k(this.blueOffset); - this.alphaOffset = k(this.alphaOffset); + this.greenOffset = d; + this.blueOffset = b; + this.alphaOffset = g; }; - b.prototype.toString = function() { + c.prototype.clone = function() { + return new c(this.redMultiplier, this.greenMultiplier, this.blueMultiplier, this.alphaMultiplier, this.redOffset, this.greenOffset, this.blueOffset, this.alphaOffset); + }; + c.prototype.convertToFixedPoint = function() { + this.redMultiplier = u(this.redMultiplier); + this.greenMultiplier = u(this.greenMultiplier); + this.blueMultiplier = u(this.blueMultiplier); + this.alphaMultiplier = u(this.alphaMultiplier); + this.redOffset = s(this.redOffset); + this.greenOffset = s(this.greenOffset); + this.blueOffset = s(this.blueOffset); + this.alphaOffset = s(this.alphaOffset); + return this; + }; + c.prototype.toString = function() { return "(redMultiplier=" + this.redMultiplier + ", greenMultiplier=" + this.greenMultiplier + ", blueMultiplier=" + this.blueMultiplier + ", alphaMultiplier=" + this.alphaMultiplier + ", redOffset=" + this.redOffset + ", greenOffset=" + this.greenOffset + ", blueOffset=" + this.blueOffset + ", alphaOffset=" + this.alphaOffset + ")"; }; - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(g.ASNative); - f.ColorTransform = d; - })(f.geom || (f.geom = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = function(a) { - function b() { - k("Dummy Constructor: public flash.media.Camera"); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.FROZEN_IDENTITY_COLOR_TRANSFORM = Object.freeze(new c); + c.TEMP_COLOR_TRANSFORM = new c; + return c; + }(a.ASNative); + h.ColorTransform = l; + })(h.geom || (h.geom = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = c.Debug.dummyConstructor, l = c.AVM2.Runtime.asCoerceString, e = function(a) { + function e() { + u("public flash.media.Camera"); } - __extends(b, a); - Object.defineProperty(b.prototype, "names", {get:function() { - k("public flash.media.Camera::get names"); + __extends(e, a); + Object.defineProperty(e.prototype, "names", {get:function() { + p("public flash.media.Camera::get names"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "isSupported", {get:function() { - k("public flash.media.Camera::get isSupported"); + Object.defineProperty(e.prototype, "isSupported", {get:function() { + p("public flash.media.Camera::get isSupported"); }, enumerable:!0, configurable:!0}); - b.getCamera = function(a) { - "undefined" === typeof a && (a = null); - m(a); - k("public flash.media.Camera::static getCamera"); + e.getCamera = function(a) { + void 0 === a && (a = null); + l(a); + p("public flash.media.Camera::static getCamera"); }; - b._scanHardware = function() { - k("public flash.media.Camera::static _scanHardware"); + e._scanHardware = function() { + p("public flash.media.Camera::static _scanHardware"); }; - Object.defineProperty(b.prototype, "activityLevel", {get:function() { - k("public flash.media.Camera::get activityLevel"); + Object.defineProperty(e.prototype, "activityLevel", {get:function() { + p("public flash.media.Camera::get activityLevel"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "bandwidth", {get:function() { - k("public flash.media.Camera::get bandwidth"); + Object.defineProperty(e.prototype, "bandwidth", {get:function() { + p("public flash.media.Camera::get bandwidth"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "currentFPS", {get:function() { - k("public flash.media.Camera::get currentFPS"); + Object.defineProperty(e.prototype, "currentFPS", {get:function() { + p("public flash.media.Camera::get currentFPS"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "fps", {get:function() { - k("public flash.media.Camera::get fps"); + Object.defineProperty(e.prototype, "fps", {get:function() { + p("public flash.media.Camera::get fps"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "height", {get:function() { - k("public flash.media.Camera::get height"); + Object.defineProperty(e.prototype, "height", {get:function() { + p("public flash.media.Camera::get height"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "index", {get:function() { - k("public flash.media.Camera::get index"); + Object.defineProperty(e.prototype, "index", {get:function() { + p("public flash.media.Camera::get index"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "keyFrameInterval", {get:function() { - k("public flash.media.Camera::get keyFrameInterval"); + Object.defineProperty(e.prototype, "keyFrameInterval", {get:function() { + p("public flash.media.Camera::get keyFrameInterval"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "loopback", {get:function() { - k("public flash.media.Camera::get loopback"); + Object.defineProperty(e.prototype, "loopback", {get:function() { + p("public flash.media.Camera::get loopback"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "motionLevel", {get:function() { - k("public flash.media.Camera::get motionLevel"); + Object.defineProperty(e.prototype, "motionLevel", {get:function() { + p("public flash.media.Camera::get motionLevel"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "motionTimeout", {get:function() { - k("public flash.media.Camera::get motionTimeout"); + Object.defineProperty(e.prototype, "motionTimeout", {get:function() { + p("public flash.media.Camera::get motionTimeout"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "muted", {get:function() { - k("public flash.media.Camera::get muted"); + Object.defineProperty(e.prototype, "muted", {get:function() { + p("public flash.media.Camera::get muted"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "name", {get:function() { - k("public flash.media.Camera::get name"); + Object.defineProperty(e.prototype, "name", {get:function() { + p("public flash.media.Camera::get name"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "position", {get:function() { - k("public flash.media.Camera::get position"); + Object.defineProperty(e.prototype, "position", {get:function() { + p("public flash.media.Camera::get position"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "quality", {get:function() { - k("public flash.media.Camera::get quality"); + Object.defineProperty(e.prototype, "quality", {get:function() { + p("public flash.media.Camera::get quality"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "width", {get:function() { - k("public flash.media.Camera::get width"); + Object.defineProperty(e.prototype, "width", {get:function() { + p("public flash.media.Camera::get width"); }, enumerable:!0, configurable:!0}); - b.prototype.setCursor = function(a) { - k("public flash.media.Camera::setCursor"); + e.prototype.setCursor = function(a) { + p("public flash.media.Camera::setCursor"); }; - b.prototype.setKeyFrameInterval = function(a) { - k("public flash.media.Camera::setKeyFrameInterval"); + e.prototype.setKeyFrameInterval = function(a) { + p("public flash.media.Camera::setKeyFrameInterval"); }; - b.prototype.setLoopback = function(a) { - k("public flash.media.Camera::setLoopback"); + e.prototype.setLoopback = function(a) { + p("public flash.media.Camera::setLoopback"); }; - b.prototype.setMode = function(a, b, c, d) { - k("public flash.media.Camera::setMode"); + e.prototype.setMode = function(a, e, c, f) { + p("public flash.media.Camera::setMode"); }; - b.prototype.setMotionLevel = function(a, b) { - k("public flash.media.Camera::setMotionLevel"); + e.prototype.setMotionLevel = function(a, e) { + p("public flash.media.Camera::setMotionLevel"); }; - b.prototype.setQuality = function(a, b) { - k("public flash.media.Camera::setQuality"); + e.prototype.setQuality = function(a, e) { + p("public flash.media.Camera::setQuality"); }; - b.prototype.drawToBitmapData = function(a) { - k("public flash.media.Camera::drawToBitmapData"); + e.prototype.drawToBitmapData = function(a) { + p("public flash.media.Camera::drawToBitmapData"); }; - b.prototype.copyToByteArray = function(a, b) { - k("public flash.media.Camera::copyToByteArray"); + e.prototype.copyToByteArray = function(a, e) { + p("public flash.media.Camera::copyToByteArray"); }; - b.prototype.copyToVector = function(a, b) { - k("public flash.media.Camera::copyToVector"); + e.prototype.copyToVector = function(a, e) { + p("public flash.media.Camera::copyToVector"); }; - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(f.events.EventDispatcher); - g.Camera = d; - })(f.media || (f.media = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.media.ID3Info"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.events.EventDispatcher); + h.Camera = e; + })(a.media || (a.media = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var s = c.Debug.dummyConstructor, u = function(a) { + function e() { + s("public flash.media.ID3Info"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = "songName artist album year comment genre track".split(" "); - return a; - }(g.ASNative); - f.ID3Info = m; - })(f.media || (f.media = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = function(a) { - function b() { - k("Dummy Constructor: public flash.media.Microphone"); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = "songName artist album year comment genre track".split(" "); + return e; + }(a.ASNative); + h.ID3Info = u; + })(h.media || (h.media = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = c.Debug.somewhatImplemented, l = c.Debug.dummyConstructor, e = c.AVM2.Runtime.asCoerceString, m = function(a) { + function c() { + l("public flash.media.Microphone"); } - __extends(b, a); - Object.defineProperty(b.prototype, "names", {get:function() { - k("public flash.media.Microphone::get names"); + __extends(c, a); + Object.defineProperty(c, "names", {get:function() { + u("public flash.media.Microphone::get names"); + return[]; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "isSupported", {get:function() { - k("public flash.media.Microphone::get isSupported"); + Object.defineProperty(c, "isSupported", {get:function() { + u("public flash.media.Microphone::get isSupported"); + return!1; }, enumerable:!0, configurable:!0}); - b.getMicrophone = function(a) { - k("public flash.media.Microphone::static getMicrophone"); + c.getMicrophone = function(a) { + p("public flash.media.Microphone::static getMicrophone"); }; - b.getEnhancedMicrophone = function(a) { - k("public flash.media.Microphone::static getEnhancedMicrophone"); + c.getEnhancedMicrophone = function(a) { + p("public flash.media.Microphone::static getEnhancedMicrophone"); }; - Object.defineProperty(b.prototype, "rate", {get:function() { - k("public flash.media.Microphone::get rate"); + Object.defineProperty(c.prototype, "rate", {get:function() { + p("public flash.media.Microphone::get rate"); }, set:function(a) { - k("public flash.media.Microphone::set rate"); + p("public flash.media.Microphone::set rate"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "codec", {get:function() { - k("public flash.media.Microphone::get codec"); + Object.defineProperty(c.prototype, "codec", {get:function() { + p("public flash.media.Microphone::get codec"); }, set:function(a) { - m(a); - k("public flash.media.Microphone::set codec"); + e(a); + p("public flash.media.Microphone::set codec"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "framesPerPacket", {get:function() { - k("public flash.media.Microphone::get framesPerPacket"); + Object.defineProperty(c.prototype, "framesPerPacket", {get:function() { + p("public flash.media.Microphone::get framesPerPacket"); }, set:function(a) { - k("public flash.media.Microphone::set framesPerPacket"); + p("public flash.media.Microphone::set framesPerPacket"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "encodeQuality", {get:function() { - k("public flash.media.Microphone::get encodeQuality"); + Object.defineProperty(c.prototype, "encodeQuality", {get:function() { + p("public flash.media.Microphone::get encodeQuality"); }, set:function(a) { - k("public flash.media.Microphone::set encodeQuality"); + p("public flash.media.Microphone::set encodeQuality"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "noiseSuppressionLevel", {get:function() { - k("public flash.media.Microphone::get noiseSuppressionLevel"); + Object.defineProperty(c.prototype, "noiseSuppressionLevel", {get:function() { + p("public flash.media.Microphone::get noiseSuppressionLevel"); }, set:function(a) { - k("public flash.media.Microphone::set noiseSuppressionLevel"); + p("public flash.media.Microphone::set noiseSuppressionLevel"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "enableVAD", {get:function() { - k("public flash.media.Microphone::get enableVAD"); + Object.defineProperty(c.prototype, "enableVAD", {get:function() { + p("public flash.media.Microphone::get enableVAD"); }, set:function(a) { - k("public flash.media.Microphone::set enableVAD"); + p("public flash.media.Microphone::set enableVAD"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "activityLevel", {get:function() { - k("public flash.media.Microphone::get activityLevel"); + Object.defineProperty(c.prototype, "activityLevel", {get:function() { + p("public flash.media.Microphone::get activityLevel"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "gain", {get:function() { - k("public flash.media.Microphone::get gain"); + Object.defineProperty(c.prototype, "gain", {get:function() { + p("public flash.media.Microphone::get gain"); }, set:function(a) { - k("public flash.media.Microphone::set gain"); + p("public flash.media.Microphone::set gain"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "index", {get:function() { - k("public flash.media.Microphone::get index"); + Object.defineProperty(c.prototype, "index", {get:function() { + p("public flash.media.Microphone::get index"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "muted", {get:function() { - k("public flash.media.Microphone::get muted"); + Object.defineProperty(c.prototype, "muted", {get:function() { + p("public flash.media.Microphone::get muted"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "name", {get:function() { - k("public flash.media.Microphone::get name"); + Object.defineProperty(c.prototype, "name", {get:function() { + p("public flash.media.Microphone::get name"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "silenceLevel", {get:function() { - k("public flash.media.Microphone::get silenceLevel"); + Object.defineProperty(c.prototype, "silenceLevel", {get:function() { + p("public flash.media.Microphone::get silenceLevel"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "silenceTimeout", {get:function() { - k("public flash.media.Microphone::get silenceTimeout"); + Object.defineProperty(c.prototype, "silenceTimeout", {get:function() { + p("public flash.media.Microphone::get silenceTimeout"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "useEchoSuppression", {get:function() { - k("public flash.media.Microphone::get useEchoSuppression"); + Object.defineProperty(c.prototype, "useEchoSuppression", {get:function() { + p("public flash.media.Microphone::get useEchoSuppression"); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "soundTransform", {get:function() { + p("public flash.media.Microphone::get soundTransform"); + }, set:function(a) { + p("public flash.media.Microphone::set soundTransform"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "soundTransform", {get:function() { - k("public flash.media.Microphone::get soundTransform"); + Object.defineProperty(c.prototype, "enhancedOptions", {get:function() { + p("public flash.media.Microphone::get enhancedOptions"); }, set:function(a) { - k("public flash.media.Microphone::set soundTransform"); + p("public flash.media.Microphone::set enhancedOptions"); }, enumerable:!0, configurable:!0}); - b.prototype.setSilenceLevel = function(a, b) { - k("public flash.media.Microphone::setSilenceLevel"); + c.prototype.setSilenceLevel = function(a, e) { + p("public flash.media.Microphone::setSilenceLevel"); }; - b.prototype.setUseEchoSuppression = function(a) { - k("public flash.media.Microphone::setUseEchoSuppression"); + c.prototype.setUseEchoSuppression = function(a) { + p("public flash.media.Microphone::setUseEchoSuppression"); }; - b.prototype.setLoopBack = function(a) { - k("public flash.media.Microphone::setLoopBack"); + c.prototype.setLoopBack = function(a) { + p("public flash.media.Microphone::setLoopBack"); }; - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(f.events.EventDispatcher); - g.Microphone = d; - })(f.media || (f.media = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(k) { - function s(a, b) { - var c = document.createElement("audio"); - c.canPlayType(a.mimeType) ? (c.preload = "metadata", c.src = URL.createObjectURL(new Blob([a.data], {type:a.mimeType})), c.load(), c.addEventListener("loadedmetadata", function() { - b({duration:1E3 * this.duration}); - })) : b({duration:0}); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.events.EventDispatcher); + h.Microphone = m; + })(a.media || (a.media = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(v) { + function p(a, e) { + var d = document.createElement("audio"); + d.canPlayType(a.mimeType) ? (d.preload = "metadata", d.src = URL.createObjectURL(new Blob([a.data], {type:a.mimeType})), d.load(), d.addEventListener("loadedmetadata", function() { + e({duration:1E3 * this.duration}); + })) : e({duration:0}); } - var m = b.Debug.notImplemented, d = b.AVM2.Runtime.asCoerceString, a = b.Debug.somewhatImplemented, c = b.Telemetry, n = b.AVM2.ABC.Multiname, p = function() { + var u = c.Debug.notImplemented, l = c.AVM2.Runtime.asCoerceString, e = c.Debug.somewhatImplemented, m = c.Telemetry, t = c.AVM2.ABC.Multiname, q = function() { return function() { }; - }(), e = function(e) { - function l(a, b) { - m("Dummy Constructor: public flash.media.Sound"); + }(), n = function(k) { + function f(a, b) { + h.events.EventDispatcher.instanceConstructorNoInitialize.call(this); + this._isBuffering = this._isURLInaccessible = !1; + this.load(a, b); } - __extends(l, e); - Object.defineProperty(l.prototype, "url", {get:function() { + __extends(f, k); + Object.defineProperty(f.prototype, "url", {get:function() { return this._url; }, enumerable:!0, configurable:!0}); - Object.defineProperty(l.prototype, "isURLInaccessible", {get:function() { - m("public flash.media.Sound::get isURLInaccessible"); + Object.defineProperty(f.prototype, "isURLInaccessible", {get:function() { + e("public flash.media.Sound::get isURLInaccessible"); + return this._isURLInaccessible; }, enumerable:!0, configurable:!0}); - Object.defineProperty(l.prototype, "length", {get:function() { + Object.defineProperty(f.prototype, "length", {get:function() { return this._length; }, enumerable:!0, configurable:!0}); - Object.defineProperty(l.prototype, "isBuffering", {get:function() { - m("public flash.media.Sound::get isBuffering"); + Object.defineProperty(f.prototype, "isBuffering", {get:function() { + e("public flash.media.Sound::get isBuffering"); + return this._isBuffering; }, enumerable:!0, configurable:!0}); - Object.defineProperty(l.prototype, "bytesLoaded", {get:function() { + Object.defineProperty(f.prototype, "bytesLoaded", {get:function() { return this._bytesLoaded; }, enumerable:!0, configurable:!0}); - Object.defineProperty(l.prototype, "bytesTotal", {get:function() { + Object.defineProperty(f.prototype, "bytesTotal", {get:function() { return this._bytesTotal; }, enumerable:!0, configurable:!0}); - Object.defineProperty(l.prototype, "id3", {get:function() { + Object.defineProperty(f.prototype, "id3", {get:function() { return this._id3; }, enumerable:!0, configurable:!0}); - l.prototype.loadCompressedDataFromByteArray = function(a, b) { - m("public flash.media.Sound::loadCompressedDataFromByteArray"); + f.prototype.loadCompressedDataFromByteArray = function(a, b) { + u("public flash.media.Sound::loadCompressedDataFromByteArray"); }; - l.prototype.loadPCMFromByteArray = function(a, b, c, h, e) { - "undefined" === typeof c && (c = "float"); - d(c); - m("public flash.media.Sound::loadPCMFromByteArray"); - }; - l.prototype.play = function(a, c, d) { - "undefined" === typeof a && (a = 0); - "undefined" === typeof c && (c = 0); - "undefined" === typeof d && (d = null); - a = +a; - c |= 0; - var h = new f.media.SoundChannel; - h._sound = this; - h._soundTransform = b.isNullOrUndefined(d) ? new f.media.SoundTransform : d; - this._playQueue.push({channel:h, startTime:a}); - if (g.disableAudioOption.value) { - return h; + f.prototype.loadPCMFromByteArray = function(a, b, e, c, f) { + void 0 === e && (e = "float"); + l(e); + u("public flash.media.Sound::loadPCMFromByteArray"); + }; + f.prototype.play = function(d, b, e) { + void 0 === d && (d = 0); + void 0 === b && (b = 0); + void 0 === e && (e = null); + d = +d; + b |= 0; + var f = new h.media.SoundChannel; + f._sound = this; + f._soundTransform = c.isNullOrUndefined(e) ? new h.media.SoundTransform : e; + this._playQueue.push({channel:f, startTime:d}); + if (a.disableAudioOption.value) { + return f; } - this._soundData && h._playSoundDataViaAudio(this._soundData, a, c); - return h; + this._soundData && (a.webAudioOption.value || a.webAudioMP3Option.value ? this._soundData.pcm ? f._playSoundDataViaChannel(this._soundData, d, b) : "audio/mpeg" === this._soundData.mimeType && a.webAudioMP3Option.value ? c.SWF.MP3DecoderSession.processAll(new Uint8Array(this._soundData.data)).then(function(a) { + this._soundData.pcm = a.data; + this._soundData.end = a.data.length; + f._playSoundDataViaChannel(this._soundData, d, b); + }.bind(this), function(a) { + console.warn("Unable to decode MP3 data: " + a); + }) : console.warn("Unable to decode packaged sound data of type: " + this._soundData.mimeType) : f._playSoundDataViaAudio(this._soundData, d, b)); + return f; }; - l.prototype.close = function() { - a("public flash.media.Sound::close"); + f.prototype.close = function() { + e("public flash.media.Sound::close"); }; - l.prototype.extract = function(a, b, c) { - m("public flash.media.Sound::extract"); + f.prototype.extract = function(a, b, e) { + u("public flash.media.Sound::extract"); }; - l.prototype._load = function(a, b, c) { - if (a) { - var d = this, e = this._stream = new f.net.URLStream, g = new f.utils.ByteArray, l = 0, k = new p; - k.completed = !1; - e.addEventListener("progress", function(a) { - d._bytesLoaded = a[n.getPublicQualifiedName("bytesLoaded")]; - d._bytesTotal = a[n.getPublicQualifiedName("bytesTotal")]; - var b = e.bytesAvailable; - e.readBytes(g, l, b); - l += b; - d.dispatchEvent(a); + f.prototype.load = function(d, b) { + if (d) { + var e = this, c = this._stream = new h.net.URLStream, f = new h.utils.ByteArray, k = 0, l = a.webAudioOption.value, m = null, n = new q; + n.completed = !1; + c.addEventListener("progress", function(a) { + e._bytesLoaded = a[t.getPublicQualifiedName("bytesLoaded")]; + e._bytesTotal = a[t.getPublicQualifiedName("bytesTotal")]; + l && !m && (m = decodeMP3(n, function(a, b) { + 0 === e._length && (e._soundData = n, e._playQueue.forEach(function(a) { + a.channel._playSoundDataViaChannel(n, a.startTime); + })); + e._length = b ? 1E3 * a : 1E3 * Math.max(a, m.estimateDuration(e._bytesTotal)); + })); + var b = c.bytesAvailable; + c.readBytes(f, k, b); + m && m.pushData(new Uint8Array(f._buffer, k, b)); + k += b; + e.dispatchEvent(a); }); - e.addEventListener("complete", function(a) { - d.dispatchEvent(a); - k.data = g._buffer; - k.mimeType = "audio/mpeg"; - k.completed = !0; - d._soundData = k; - s(k, function(a) { - d._length = a.duration; - }); - d._playQueue.forEach(function(a) { - a.channel._playSoundDataViaAudio(k, a.startTime); - }); + c.addEventListener("complete", function(a) { + e.dispatchEvent(a); + n.data = f._buffer; + n.mimeType = "audio/mpeg"; + n.completed = !0; + l || (e._soundData = n, p(n, function(a) { + e._length = a.duration; + }), e._playQueue.forEach(function(a) { + a.channel._playSoundDataViaAudio(n, a.startTime); + })); + m && m.close(); }); - e.load(a); + c.load(d); } }; - l.classInitializer = null; - l.initializer = function(a) { + f.classInitializer = null; + f.initializer = function(a) { this._playQueue = []; this._url = null; this._bytesLoaded = this._bytesTotal = this._length = 0; - this._id3 = new f.media.ID3Info; - c.instance.reportTelemetry({topic:"feature", feature:5}); + this._id3 = new h.media.ID3Info; + m.instance.reportTelemetry({topic:"feature", feature:5}); if (a) { - var b = new p; - a.pcm && (b.sampleRate = a.sampleRate, b.channels = a.channels, b.pcm = a.pcm, b.end = a.pcm.length); + var b = new q; + b.sampleRate = a.sampleRate; + b.channels = a.channels; b.completed = !0; + a.pcm && (b.pcm = a.pcm, b.end = a.pcm.length); a.packaged && (b.data = a.packaged.data.buffer, b.mimeType = a.packaged.mimeType); - var d = this; - s(b, function(a) { - d._length = a.duration; + var e = this; + p(b, function(a) { + e._length = a.duration; }); this._soundData = b; } }; - l.classSymbols = null; - l.instanceSymbols = null; - return l; - }(f.events.EventDispatcher); - k.Sound = e; - })(f.media || (f.media = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.assert, m = b.Debug.notImplemented, d = b.Debug.somewhatImplemented, a = b.Debug.error, c = function() { - function a(b, c) { - this._sourceRate = b; + f.classSymbols = null; + f.instanceSymbols = null; + return f; + }(h.events.EventDispatcher); + v.Sound = n; + n = function(a) { + function e(d) { + a.call(this, d, h.media.Sound); + } + __extends(e, a); + e.FromData = function(a) { + var b = new e(a); + b.channels = a.channels; + b.sampleRate = a.sampleRate; + b.pcm = a.pcm; + b.packaged = a.packaged; + return b; + }; + return e; + }(c.Timeline.Symbol); + v.SoundSymbol = n; + })(h.media || (h.media = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.assert, u = c.Debug.dummyConstructor, l = c.Debug.somewhatImplemented, e = c.Debug.error, m = function() { + function a(e, c) { + this._sourceRate = e; this._targetRate = c; this._tail = []; this._sourceOffset = 0; } - a.prototype.getData = function(a, b) { - for (var c = this._sourceRate / this._targetRate, d = this._sourceOffset, e = Math.ceil((b - 1) * c + d) + 1, h = [], f = 0;f < a.length;f++) { - h.push(new Float32Array(e)); - } - this.ondatarequested({data:h, count:e}); - for (f = 0;f < a.length;f++) { - for (var g = a[f], k = h[f], m = 0;m < b;m++) { - var n = m * c + d, p = n | 0, s = Math.ceil(n) | 0, t = 0 > p ? this._tail[f] : k[p]; - p === s ? g[m] = t : (n -= p, g[m] = t * (1 - n) + k[s] * n); - } - this._tail[f] = k[e - 1]; - } - this._sourceOffset = (b - 1) * c + d - (e - 1); - }; - return a; - }(), n = function() { - function a(b, d) { - var f = a._cachedContext; - f || (f = new AudioContext, a._cachedContext = f); - this._context = f; - this._contextSampleRate = f.sampleRate || 44100; - this._channels = d; - this._sampleRate = b; - this._contextSampleRate !== b && (this._resampler = new c(b, this._contextSampleRate), this._resampler.ondatarequested = function(a) { + a.prototype.getData = function(a, e) { + for (var d = this._sourceRate / this._targetRate, b = this._sourceOffset, c = Math.ceil((e - 1) * d + b) + 1, l = [], m = 0;m < a.length;m++) { + l.push(new Float32Array(c)); + } + this.ondatarequested({data:l, count:c}); + for (m = 0;m < a.length;m++) { + for (var h = a[m], n = l[m], q = 0;q < e;q++) { + var p = q * d + b, s = p | 0, t = Math.ceil(p) | 0, u = 0 > s ? this._tail[m] : n[s]; + s === t ? h[q] = u : (p -= s, h[q] = u * (1 - p) + n[t] * p); + } + this._tail[m] = n[c - 1]; + } + this._sourceOffset = (e - 1) * d + b - (c - 1); + }; + return a; + }(), t = function() { + function a(e, c) { + var d = a._cachedContext; + d || (d = new AudioContext, a._cachedContext = d); + this._context = d; + this._contextSampleRate = d.sampleRate || 44100; + this._channels = c; + this._sampleRate = e; + this._contextSampleRate !== e && (this._resampler = new m(e, this._contextSampleRate), this._resampler.ondatarequested = function(a) { this.requestData(a.data, a.count); }.bind(this)); } + a.prototype.setVolume = function(a) { + }; a.prototype.start = function() { - var a = this._context.createScriptProcessor(2048, 0, this._channels), b = this; + var a = this._context.createScriptProcessor(2048, 0, this._channels), e = this; a.onaudioprocess = function(a) { - for (var c = [], d = 0;d < b._channels;d++) { - c.push(a.outputBuffer.getChannelData(d)); + for (var b = [], c = 0;c < e._channels;c++) { + b.push(a.outputBuffer.getChannelData(c)); } - a = c[0].length; - b._resampler ? b._resampler.getData(c, a) : b.requestData(c, a); + a = b[0].length; + e._resampler ? e._resampler.getData(b, a) : e.requestData(b, a); }; a.connect(this._context.destination); this._source = a; @@ -31203,12 +38165,12 @@ a.prototype.stop = function() { this._source.disconnect(this._context.destination); }; - a.prototype.requestData = function(a, b) { - var c = this._channels, d = new Float32Array(b * c); - this.ondatarequested({data:d, count:d.length}); - for (var e = 0, h = 0;e < b;e++) { - for (var f = 0;f < c;f++) { - a[f][e] = d[h++]; + a.prototype.requestData = function(a, e) { + var d = this._channels, b = new Float32Array(e * d); + this.ondatarequested({data:b, count:b.length}); + for (var c = 0, l = 0;c < e;c++) { + for (var m = 0;m < d;m++) { + a[m][c] = b[l++]; } } }; @@ -31216,259 +38178,263 @@ return "undefined" !== typeof AudioContext; }; return a; - }(), p = function(c) { - function p() { - m("Dummy Constructor: public flash.media.SoundChannel"); + }(), q = function(m) { + function k() { + u("public flash.media.SoundChannel"); } - __extends(p, c); - Object.defineProperty(p.prototype, "position", {get:function() { + __extends(k, m); + Object.defineProperty(k.prototype, "position", {get:function() { return this._position; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "soundTransform", {get:function() { + Object.defineProperty(k.prototype, "soundTransform", {get:function() { return this._soundTransform; - }, set:function(a) { - d("public flash.media.SoundChannel::set soundTransform"); - this._soundTransform = b.isNullOrUndefined(a) ? new f.media.SoundTransform : a; + }, set:function(e) { + l("public flash.media.SoundChannel::set soundTransform"); + this._soundTransform = c.isNullOrUndefined(e) ? new a.media.SoundTransform : e; + h.SoundMixer._updateSoundSource(this); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "leftPeak", {get:function() { + Object.defineProperty(k.prototype, "leftPeak", {get:function() { return this._leftPeak; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "rightPeak", {get:function() { + Object.defineProperty(k.prototype, "rightPeak", {get:function() { return this._rightPeak; }, enumerable:!0, configurable:!0}); - p.prototype.stop = function() { - this._element && (this._unregisterWithSoundMixer(), this._element.pause()); - this._audioChannel && (this._unregisterWithSoundMixer(), this._audioChannel.stop()); - }; - p.prototype._playSoundDataViaAudio = function(a, b, c) { - if (a.mimeType) { - this._registerWithSoundMixer(); - this._position = b; - var d = this, h = 0, e = document.createElement("audio"); - e.canPlayType(a.mimeType) ? (e.preload = "metadata", e.loop = 0 < c, e.src = URL.createObjectURL(new Blob([a.data], {type:a.mimeType})), e.addEventListener("loadeddata", function() { - e.currentTime = b / 1E3; - e.play(); - }), e.addEventListener("timeupdate", function() { - var a = e.currentTime; - c && h > a && (--c, c || (e.loop = !1), a < b / 1E3 && (e.currentTime = b / 1E3)); - d._position = 1E3 * (h = a); - }), e.addEventListener("ended", function() { - d._unregisterWithSoundMixer(); - d.dispatchEvent(new f.events.Event("soundComplete", !1, !1)); - d._element = null; - }), this._element = e, this._applySoundTransform()) : console.error('ERROR: "' + a.mimeType + '" type playback is not supported by the browser'); - } - }; - p.prototype._playSoundDataViaChannel = function(b, c, d) { - k(b.pcm, "no pcm data found"); - this._registerWithSoundMixer(); - var e = this, h = Math.round(c / 1E3 * b.sampleRate) * b.channels, g = h; - this._position = c; - n.isSupported ? c = new n(b.sampleRate, b.channels) : (a("PCM data playback is not supported by the browser"), c = void 0); - this._audioChannel = c; - this._audioChannel.ondatarequested = function(a) { - var c = b.end; - if (g >= c && b.completed) { - e._unregisterWithSoundMixer(), e._audioChannel.stop(), e.dispatchEvent(new f.events.Event("soundComplete", !1, !1)); + k.prototype.stop = function() { + this._element && (h.SoundMixer._unregisterSoundSource(this), this._element.loop = !1, this._element.pause(), this._element.removeAttribute("src")); + this._audioChannel && (h.SoundMixer._unregisterSoundSource(this), this._audioChannel.stop()); + }; + k.prototype._playSoundDataViaAudio = function(e, d, b) { + if (e.mimeType) { + h.SoundMixer._registerSoundSource(this); + this._position = d; + var c = this, k = 0, l = document.createElement("audio"); + l.canPlayType(e.mimeType) ? (l.preload = "metadata", l.loop = 0 < b, l.src = URL.createObjectURL(new Blob([e.data], {type:e.mimeType})), l.addEventListener("loadeddata", function() { + l.currentTime = d / 1E3; + l.play(); + }), l.addEventListener("timeupdate", function() { + var a = l.currentTime; + b && k > a && (--b, b || (l.loop = !1), a < d / 1E3 && (l.currentTime = d / 1E3)); + c._position = 1E3 * (k = a); + }), l.addEventListener("ended", function() { + h.SoundMixer._unregisterSoundSource(c); + c.dispatchEvent(new a.events.Event("soundComplete", !1, !1)); + c._element = null; + }), this._element = l, h.SoundMixer._updateSoundSource(this)) : console.error('ERROR: "' + e.mimeType + '" type playback is not supported by the browser'); + } + }; + k.prototype._playSoundDataViaChannel = function(c, d, b) { + p(c.pcm, "no pcm data found"); + h.SoundMixer._registerSoundSource(this); + var g = this, k = Math.round(d / 1E3 * c.sampleRate) * c.channels, l = k; + this._position = d; + t.isSupported ? d = new t(c.sampleRate, c.channels) : (e("PCM data playback is not supported by the browser"), d = void 0); + this._audioChannel = d; + this._audioChannel.ondatarequested = function(d) { + var e = c.end; + if (l >= e && c.completed) { + h.SoundMixer._unregisterSoundSource(this), g._audioChannel.stop(), g.dispatchEvent(new a.events.Event("soundComplete", !1, !1)); } else { - var k = a.count; - a = a.data; - var m = b.pcm; + var m = d.count; + d = d.data; + var n = c.pcm; do { - for (var n = Math.min(c - g, k), p = 0;p < n;p++) { - a[p] = m[g++]; + for (var q = Math.min(e - l, m), p = 0;p < q;p++) { + d[p] = n[l++]; } - k -= n; - if (g >= c) { - if (!d) { + m -= q; + if (l >= e) { + if (!b) { break; } - d--; - g = h; + b--; + l = k; } - } while (0 < k); - e._position = g / b.sampleRate / b.channels * 1E3; + } while (0 < m); + g._position = l / c.sampleRate / c.channels * 1E3; } }; this._audioChannel.start(); - this._applySoundTransform(); - }; - p.prototype._applySoundTransform = function() { - var a = this._soundTransform.volume; - g.SoundMixer._soundTransform && (a *= g.SoundMixer._soundTransform.volume); - a *= g.SoundMixer._getMasterVolume(); - this._element && (this._element.volume = 0 >= a ? 0 : 1 <= a ? 1 : a); + h.SoundMixer._updateSoundSource(this); }; - p.prototype._registerWithSoundMixer = function() { - g.SoundMixer._registerChannel(this); + k.prototype.stopSound = function() { + this.stop(); }; - p.prototype._unregisterWithSoundMixer = function() { - g.SoundMixer._unregisterChannel(this); + k.prototype.updateSoundLevels = function(a) { + this._element && (this._element.volume = 0 >= a ? 0 : 1 <= a ? 1 : a); + this._audioChannel && this._audioChannel.setVolume(a); }; - p.classInitializer = null; - p.initializer = function(a) { + k.classInitializer = null; + k.initializer = function(e) { this._element = null; this._rightPeak = this._leftPeak = this._position = 0; this._pcmData = null; - this._soundTransform = new f.media.SoundTransform; + this._soundTransform = new a.media.SoundTransform; }; - p.classSymbols = null; - p.instanceSymbols = null; - return p; - }(f.events.EventDispatcher); - g.SoundChannel = p; - })(f.media || (f.media = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, b) { - k("Dummy Constructor: public flash.media.SoundLoaderContext"); + k.classSymbols = null; + k.instanceSymbols = null; + return k; + }(a.events.EventDispatcher); + h.SoundChannel = q; + })(a.media || (a.media = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = function(a) { + function e(a, e) { + p("public flash.media.SoundLoaderContext"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.SoundLoaderContext = m; - })(f.media || (f.media = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(k) { - var s = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = b.Debug.somewhatImplemented, a = function(a) { - function g() { - s("Dummy Constructor: public flash.media.SoundMixer"); - } - __extends(g, a); - Object.defineProperty(g, "bufferTime", {get:function() { - s("public flash.media.SoundMixer::get bufferTime"); - }, set:function(a) { - s("public flash.media.SoundMixer::set bufferTime"); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(g, "soundTransform", {get:function() { - d("public flash.media.SoundMixer::get soundTransform"); - return b.isNullOrUndefined(g._soundTransform) ? new f.media.SoundTransform : new f.media.SoundTransform(g._soundTransform.volume, g._soundTransform.pan); - }, set:function(a) { - d("public flash.media.SoundMixer::set soundTransform"); - g._soundTransform = b.isNullOrUndefined(a) ? new f.media.SoundTransform : a; - g._registeredChannels.forEach(function(a) { - a._applySoundTransform(); - }); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(g, "audioPlaybackMode", {get:function() { - s("public flash.media.SoundMixer::get audioPlaybackMode"); - }, set:function(a) { - m(a); - s("public flash.media.SoundMixer::set audioPlaybackMode"); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(g, "useSpeakerphoneForVoice", {get:function() { - s("public flash.media.SoundMixer::get useSpeakerphoneForVoice"); - }, set:function(a) { - s("public flash.media.SoundMixer::set useSpeakerphoneForVoice"); - }, enumerable:!0, configurable:!0}); - g.stopAll = function() { - g._registeredChannels.forEach(function(a) { - a.stop(); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.ASNative); + h.SoundLoaderContext = s; + })(h.media || (h.media = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(v) { + var p = c.Debug.notImplemented, u = c.Debug.dummyConstructor, l = c.AVM2.Runtime.asCoerceString, e = c.Debug.somewhatImplemented, m = function(a) { + function m() { + u("public flash.media.SoundMixer"); + } + __extends(m, a); + Object.defineProperty(m, "bufferTime", {get:function() { + p("public flash.media.SoundMixer::get bufferTime"); + return m._bufferTime; + }, set:function(a) { + e("public flash.media.SoundMixer::set bufferTime"); + m._bufferTime = a | 0; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m, "soundTransform", {get:function() { + e("public flash.media.SoundMixer::get soundTransform"); + return c.isNullOrUndefined(m._soundTransform) ? new h.media.SoundTransform : new h.media.SoundTransform(m._soundTransform.volume, m._soundTransform.pan); + }, set:function(a) { + e("public flash.media.SoundMixer::set soundTransform"); + m._soundTransform = c.isNullOrUndefined(a) ? new h.media.SoundTransform : a; + m._updateAllSoundSources(); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m, "audioPlaybackMode", {get:function() { + p("public flash.media.SoundMixer::get audioPlaybackMode"); + }, set:function(a) { + l(a); + p("public flash.media.SoundMixer::set audioPlaybackMode"); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(m, "useSpeakerphoneForVoice", {get:function() { + p("public flash.media.SoundMixer::get useSpeakerphoneForVoice"); + }, set:function(a) { + p("public flash.media.SoundMixer::set useSpeakerphoneForVoice"); + }, enumerable:!0, configurable:!0}); + m.stopAll = function() { + m._registeredSoundSources.forEach(function(a) { + a.stopSound(); }); - g._registeredChannels = []; + m._registeredSoundSources = []; }; - g.computeSpectrum = function(a, b, c) { - d("public flash.media.SoundMixer::static computeSpectrum"); - b = new Float32Array(1024); - for (c = 0;1024 > c;c++) { - b[c] = Math.random(); + m.computeSpectrum = function(a, c, f) { + e("public flash.media.SoundMixer::static computeSpectrum"); + c = new Float32Array(1024); + for (f = 0;1024 > f;f++) { + c[f] = Math.random(); } - a.writeRawBytes(b); + a.writeRawBytes(c); a.position = 0; }; - g.areSoundsInaccessible = function() { - s("public flash.media.SoundMixer::static areSoundsInaccessible"); - }; - g._getMasterVolume = function() { - return g._masterVolume; - }; - g._setMasterVolume = function(a) { - g._masterVolume = +a; - g._registeredChannels.forEach(function(a) { - a._applySoundTransform(); - }); + m.areSoundsInaccessible = function() { + p("public flash.media.SoundMixer::static areSoundsInaccessible"); }; - g._registerChannel = function(a) { - g._registeredChannels.push(a); + m._getMasterVolume = function() { + return m._masterVolume; }; - g._unregisterChannel = function(a) { - a = g._registeredChannels.indexOf(a); - 0 <= a && g._registeredChannels.splice(a, 1); - }; - g.classInitializer = null; - g.initializer = null; - g.classSymbols = null; - g.instanceSymbols = null; - g._masterVolume = 1; - g._registeredChannels = []; - return g; - }(g.ASNative); - k.SoundMixer = a; - })(f.media || (f.media = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = b.Debug.somewhatImplemented, d = function(a) { - function b(a, c) { - k("Dummy Constructor: public flash.media.SoundTransform"); + m._setMasterVolume = function(a) { + m._masterVolume = +a; + m._updateAllSoundSources(); + }; + m._registerSoundSource = function(a) { + m._registeredSoundSources.push(a); + }; + m._unregisterSoundSource = function(a) { + a = m._registeredSoundSources.indexOf(a); + 0 <= a && m._registeredSoundSources.splice(a, 1); + }; + m._updateSoundSource = function(a) { + var e = a.soundTransform.volume; + m._soundTransform && (e *= m._soundTransform.volume); + e *= m._getMasterVolume(); + a.updateSoundLevels(e); + }; + m._updateAllSoundSources = function() { + m._registeredSoundSources.forEach(m._updateSoundSource); + }; + m.classInitializer = null; + m.initializer = null; + m.classSymbols = null; + m.instanceSymbols = null; + m._masterVolume = 1; + m._registeredSoundSources = []; + m._bufferTime = 0; + return m; + }(a.ASNative); + v.SoundMixer = m; + })(h.media || (h.media = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = c.Debug.somewhatImplemented, l = function(a) { + function c(a, e) { + p("public flash.media.SoundTransform"); } - __extends(b, a); - Object.defineProperty(b.prototype, "volume", {get:function() { + __extends(c, a); + Object.defineProperty(c.prototype, "volume", {get:function() { return this._volume; }, set:function(a) { this._volume = +a; this._updateTransform(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "leftToLeft", {get:function() { + Object.defineProperty(c.prototype, "leftToLeft", {get:function() { return this._leftToLeft; }, set:function(a) { this._leftToLeft = +a; this._updateTransform(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "leftToRight", {get:function() { + Object.defineProperty(c.prototype, "leftToRight", {get:function() { return this._leftToRight; }, set:function(a) { this._leftToRight = +a; this._updateTransform(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "rightToRight", {get:function() { + Object.defineProperty(c.prototype, "rightToRight", {get:function() { return this._rightToRight; }, set:function(a) { this._rightToRight = +a; this._updateTransform(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "rightToLeft", {get:function() { + Object.defineProperty(c.prototype, "rightToLeft", {get:function() { return this._rightToLeft; }, set:function(a) { this._rightToLeft = +a; this._updateTransform(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "pan", {get:function() { + Object.defineProperty(c.prototype, "pan", {get:function() { return 0 === this._leftToRight && 0 === this._rightToLeft ? 1 - this._leftToLeft * this._leftToLeft : 0; }, set:function(a) { this.leftToLeft = Math.sqrt(1 - a); @@ -31476,736 +38442,1036 @@ this.rightToRight = Math.sqrt(1 + a); this.rightToLeft = 0; }, enumerable:!0, configurable:!0}); - b.prototype._updateTransform = function() { - m("public flash.media.SoundTransform::_updateTransform"); + c.prototype._updateTransform = function() { + s("public flash.media.SoundTransform::_updateTransform"); }; - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(g.ASNative); - f.SoundTransform = d; - })(f.media || (f.media = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = b.Debug.assert, d = function(a) { - function b(a, c) { - "undefined" === typeof a && (a = 320); - "undefined" === typeof c && (c = 240); - f.display.DisplayObject.instanceConstructorNoInitialize.call(this); - this._width = a | 0; - this._height = c | 0; + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.ASNative); + h.SoundTransform = l; + })(h.media || (h.media = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = c.Debug.dummyConstructor, l = function(a) { + function c() { + u("public flash.media.StageVideo"); } - __extends(b, a); - Object.defineProperty(b.prototype, "deblocking", {get:function() { + __extends(c, a); + Object.defineProperty(c.prototype, "viewPort", {get:function() { + p("public flash.media.StageVideo::get viewPort"); + }, set:function(a) { + p("public flash.media.StageVideo::set viewPort"); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "pan", {get:function() { + p("public flash.media.StageVideo::get pan"); + }, set:function(a) { + p("public flash.media.StageVideo::set pan"); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "zoom", {get:function() { + p("public flash.media.StageVideo::get zoom"); + }, set:function(a) { + p("public flash.media.StageVideo::set zoom"); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "depth", {get:function() { + p("public flash.media.StageVideo::get depth"); + }, set:function(a) { + p("public flash.media.StageVideo::set depth"); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "videoWidth", {get:function() { + p("public flash.media.StageVideo::get videoWidth"); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "videoHeight", {get:function() { + p("public flash.media.StageVideo::get videoHeight"); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "colorSpaces", {get:function() { + p("public flash.media.StageVideo::get colorSpaces"); + }, enumerable:!0, configurable:!0}); + c.prototype.attachNetStream = function(a) { + p("public flash.media.StageVideo::attachNetStream"); + }; + c.prototype.attachCamera = function(a) { + p("public flash.media.StageVideo::attachCamera"); + }; + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.events.EventDispatcher); + h.StageVideo = l; + })(a.media || (a.media = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = function(a) { + function e() { + p("public flash.media.StageVideoAvailability"); + } + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.AVAILABLE = "available"; + e.UNAVAILABLE = "unavailable"; + return e; + }(a.ASNative); + h.StageVideoAvailability = s; + })(h.media || (h.media = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = c.Debug.somewhatImplemented, l = c.Debug.assert, e = function(e) { + function c(e, l) { + void 0 === e && (e = 320); + void 0 === l && (l = 240); + a.display.DisplayObject.instanceConstructorNoInitialize.call(this); + e = e | 0 || 320; + l = l | 0 || 240; + this._setFillAndLineBoundsFromWidthAndHeight(20 * e, 20 * l); + } + __extends(c, e); + Object.defineProperty(c.prototype, "deblocking", {get:function() { return this._deblocking; }, set:function(a) { this._deblocking = a | 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "smoothing", {get:function() { + Object.defineProperty(c.prototype, "smoothing", {get:function() { return this._smoothing; }, set:function(a) { this._smoothing = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "videoWidth", {get:function() { + Object.defineProperty(c.prototype, "videoWidth", {get:function() { return this._videoWidth; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "videoHeight", {get:function() { + Object.defineProperty(c.prototype, "videoHeight", {get:function() { return this._videoHeight; }, enumerable:!0, configurable:!0}); - b.prototype._containsPointDirectly = function(a, b) { - m(this._getContentBounds().contains(a, b)); + c.prototype._containsPointDirectly = function(a, e, c, f) { + l(this._getContentBounds().contains(a, e)); return!0; }; - b.prototype.clear = function() { - k("public flash.media.Video::clear"); + c.prototype.clear = function() { + u("public flash.media.Video::clear"); }; - b.prototype.attachNetStream = function(a) { + c.prototype.attachNetStream = function(a) { if (this._netStream !== a) { - this._netStream && (a._videoReferrer = null); + this._netStream && (this._netStream._videoReferrer = null); if (this._netStream = a) { a._videoReferrer = this; } this._setDirtyFlags(33554432); } }; - b.prototype.attachCamera = function(a) { - k("public flash.media.Video::attachCamera"); + c.prototype.attachCamera = function(a) { + p("public flash.media.Video::attachCamera"); }; - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(f.display.DisplayObject); - g.Video = d; - })(f.media || (f.media = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.media.VideoStreamSettings"); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.display.DisplayObject); + h.Video = e; + })(a.media || (a.media = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = function(a) { + function e() { + p("public flash.media.VideoStreamSettings"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.VideoStreamSettings = m; - })(f.media || (f.media = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = function(a) { - function b(a, c, d) { - k("Dummy Constructor: public flash.net.FileFilter"); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.ASNative); + h.VideoStreamSettings = s; + })(h.media || (h.media = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = c.AVM2.Runtime.asCoerceString, l = function(a) { + function c(a, e, l) { + p("public flash.net.FileFilter"); } - __extends(b, a); - Object.defineProperty(b.prototype, "description", {get:function() { + __extends(c, a); + Object.defineProperty(c.prototype, "description", {get:function() { return this._description; }, set:function(a) { - this._description = m(a); + this._description = s(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "extension", {get:function() { + Object.defineProperty(c.prototype, "extension", {get:function() { return this._extension; }, set:function(a) { - this._extension = m(a); + this._extension = s(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "macType", {get:function() { + Object.defineProperty(c.prototype, "macType", {get:function() { return this._macType; }, set:function(a) { - this._macType = m(a); + this._macType = s(a); }, enumerable:!0, configurable:!0}); - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(g.ASNative); - f.FileFilter = d; - })(f.net || (f.net = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = b.Debug.somewhatImplemented, a = b.FileLoadingService, c = function(b) { + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.ASNative); + h.FileFilter = l; + })(h.net || (h.net = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = c.Debug.dummyConstructor, l = c.AVM2.Runtime.asCoerceString, e = c.Debug.somewhatImplemented, m = c.FileLoadingService, t = function(a) { function c() { - k("Dummy Constructor: public flash.net.LocalConnection"); + u("public flash.net.LocalConnection"); } - __extends(c, b); + __extends(c, a); c.prototype.close = function() { - k("public flash.net.LocalConnection::close"); + p("public flash.net.LocalConnection::close"); }; c.prototype.connect = function(a) { - m(a); - k("public flash.net.LocalConnection::connect"); + l(a); + p("public flash.net.LocalConnection::connect"); }; Object.defineProperty(c.prototype, "domain", {get:function() { - d("public flash.net.LocalConnection::get domain"); - var b = a.instance.resolveUrl("/"); - return(b = /:\/\/(.+?)[:?#\/]/.exec(b)) && b[1]; - }, enumerable:!0, configurable:!0}); - c.prototype.send = function(a, b) { - m(a); - m(b); - k("public flash.net.LocalConnection::send"); + e("public flash.net.LocalConnection::get domain"); + var a = m.instance.resolveUrl("/"); + return(a = /:\/\/(.+?)[:?#\/]/.exec(a)) && a[1]; + }, enumerable:!0, configurable:!0}); + c.prototype.send = function(a, e) { + l(a); + l(e); + p("public flash.net.LocalConnection::send"); }; Object.defineProperty(c.prototype, "client", {get:function() { - k("public flash.net.LocalConnection::get client"); + p("public flash.net.LocalConnection::get client"); }, set:function(a) { - k("public flash.net.LocalConnection::set client"); + p("public flash.net.LocalConnection::set client"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c.prototype, "isPerUser", {get:function() { - k("public flash.net.LocalConnection::get isPerUser"); + p("public flash.net.LocalConnection::get isPerUser"); }, set:function(a) { - k("public flash.net.LocalConnection::set isPerUser"); + p("public flash.net.LocalConnection::set isPerUser"); }, enumerable:!0, configurable:!0}); c.prototype.allowDomain = function() { - k("public flash.net.LocalConnection::allowDomain"); + p("public flash.net.LocalConnection::allowDomain"); }; c.prototype.allowInsecureDomain = function() { - k("public flash.net.LocalConnection::allowInsecureDomain"); + p("public flash.net.LocalConnection::allowInsecureDomain"); }; c.classInitializer = null; c.initializer = null; c.classSymbols = null; c.instanceSymbols = null; return c; - }(f.events.EventDispatcher); - g.LocalConnection = c; - })(f.net || (f.net = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = b.Debug.somewhatImplemented, a = b.AVM2.Runtime.wrapJSObject, c = b.Telemetry, n = b.AVM2.AS.flash.events.NetStatusEvent, p = function(b) { - function f() { - k("Dummy Constructor: public flash.net.NetConnection"); + }(a.events.EventDispatcher); + h.LocalConnection = t; + })(a.net || (a.net = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = c.Debug.dummyConstructor, l = c.AVM2.Runtime.asCoerceString, e = c.Debug.somewhatImplemented, m = c.AVM2.Runtime.wrapJSObject, t = c.Telemetry, q = c.AVM2.AS.flash.events, n = function(a) { + function c() { + u("public flash.net.NetConnection"); } - __extends(f, b); - Object.defineProperty(f, "defaultObjectEncoding", {get:function() { - return f._defaultObjectEncoding; + __extends(c, a); + Object.defineProperty(c, "defaultObjectEncoding", {get:function() { + return c._defaultObjectEncoding; }, set:function(a) { - f._defaultObjectEncoding = a >>> 0; + c._defaultObjectEncoding = a >>> 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(f.prototype, "connected", {get:function() { + Object.defineProperty(c.prototype, "connected", {get:function() { return this._connected; }, enumerable:!0, configurable:!0}); - Object.defineProperty(f.prototype, "uri", {get:function() { + Object.defineProperty(c.prototype, "uri", {get:function() { return this._uri; }, enumerable:!0, configurable:!0}); - f.prototype.connect = function(b) { - b = m(b); - d("public flash.net.NetConnection::connect"); - (this._uri = b) ? this.dispatchEvent(new n(n.NET_STATUS, !1, !1, a({level:"status", code:"NetConnection.Connect.Failed"}))) : (this._connected = !0, this.dispatchEvent(new n(n.NET_STATUS, !1, !1, a({level:"status", code:"NetConnection.Connect.Success"})))); + c.prototype.connect = function(a) { + a = l(a); + e("public flash.net.NetConnection::connect"); + if (this._uri = a) { + var b = RtmpJs.parseConnectionString(a); + if (!b || !b.host || "rtmp" !== b.protocol && "rtmpt" !== b.protocol && "rtmps" !== b.protocol) { + this.dispatchEvent(new q.NetStatusEvent(q.NetStatusEvent.NET_STATUS, !1, !1, m({level:"status", code:"NetConnection.Connect.Failed"}))); + } else { + a = m({app:b.app, flashver:"MAC 11,6,602,180", swfUrl:"http://localhost:5080/demos/Something.swf", tcUrl:a, fpad:!1, audioCodecs:4095, videoCodecs:255, videoFunction:1, pageUrl:"http://localhost:5080/demos/Something.html", objectEncoding:0}); + this._protocol = b.protocol; + var c = "rtmps" === b.protocol || "rtmpt" === b.protocol && (443 === b.port || 8443 === b.port); + this._usingTLS = c; + this._rtmpConnection = b = "rtmp" === b.protocol || "rtmps" === b.protocol ? new RtmpJs.Browser.RtmpTransport({host:b.host, port:b.port || 1935, ssl:c}) : new RtmpJs.Browser.RtmptTransport({host:b.host, port:b.port || 80, ssl:c}); + this._rtmpCreateStreamCallbacks = [null, null]; + b.onresponse = function(a) { + }; + b.onevent = function(a) { + }; + b.onconnected = function(a) { + this._connected = !0; + this.dispatchEvent(new q.NetStatusEvent(q.NetStatusEvent.NET_STATUS, !1, !1, m({level:"status", code:"NetConnection.Connect.Success"}))); + }.bind(this); + b.onstreamcreated = function(a) { + console.log("#streamcreated: " + a.streamId); + var b = this._rtmpCreateStreamCallbacks[a.transactionId]; + delete this._rtmpCreateStreamCallbacks[a.transactionId]; + b(a.stream, a.streamId); + }.bind(this); + b.connect(a); + } + } else { + this._connected = !0, this.dispatchEvent(new q.NetStatusEvent(q.NetStatusEvent.NET_STATUS, !1, !1, m({level:"status", code:"NetConnection.Connect.Success"}))); + } }; - Object.defineProperty(f.prototype, "client", {get:function() { + c.prototype._createRtmpStream = function(a) { + var b = this._rtmpCreateStreamCallbacks.length; + this._rtmpCreateStreamCallbacks[b] = a; + this._rtmpConnection.createStream(b); + }; + Object.defineProperty(c.prototype, "client", {get:function() { return this._client; }, set:function(a) { this._client = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(f.prototype, "objectEncoding", {get:function() { + Object.defineProperty(c.prototype, "objectEncoding", {get:function() { return this._objectEncoding; }, set:function(a) { a >>>= 0; - d("public flash.net.NetConnection::set objectEncoding"); + e("public flash.net.NetConnection::set objectEncoding"); this._objectEncoding = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(f.prototype, "proxyType", {get:function() { + Object.defineProperty(c.prototype, "proxyType", {get:function() { return this._proxyType; }, set:function(a) { - a = m(a); - d("public flash.net.NetConnection::set proxyType"); + a = l(a); + e("public flash.net.NetConnection::set proxyType"); this._proxyType = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(f.prototype, "connectedProxyType", {get:function() { - k("public flash.net.NetConnection::get connectedProxyType"); + Object.defineProperty(c.prototype, "connectedProxyType", {get:function() { + p("public flash.net.NetConnection::get connectedProxyType"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(f.prototype, "usingTLS", {get:function() { - d("public flash.net.NetConnection::get usingTLS"); + Object.defineProperty(c.prototype, "usingTLS", {get:function() { return this._usingTLS; }, enumerable:!0, configurable:!0}); - Object.defineProperty(f.prototype, "protocol", {get:function() { - k("public flash.net.NetConnection::get protocol"); + Object.defineProperty(c.prototype, "protocol", {get:function() { + return this._protocol; }, enumerable:!0, configurable:!0}); - Object.defineProperty(f.prototype, "maxPeerConnections", {get:function() { - k("public flash.net.NetConnection::get maxPeerConnections"); + Object.defineProperty(c.prototype, "maxPeerConnections", {get:function() { + p("public flash.net.NetConnection::get maxPeerConnections"); }, set:function(a) { - k("public flash.net.NetConnection::set maxPeerConnections"); + p("public flash.net.NetConnection::set maxPeerConnections"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(f.prototype, "nearID", {get:function() { - k("public flash.net.NetConnection::get nearID"); + Object.defineProperty(c.prototype, "nearID", {get:function() { + p("public flash.net.NetConnection::get nearID"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(f.prototype, "farID", {get:function() { - k("public flash.net.NetConnection::get farID"); + Object.defineProperty(c.prototype, "farID", {get:function() { + p("public flash.net.NetConnection::get farID"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(f.prototype, "nearNonce", {get:function() { - k("public flash.net.NetConnection::get nearNonce"); + Object.defineProperty(c.prototype, "nearNonce", {get:function() { + p("public flash.net.NetConnection::get nearNonce"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(f.prototype, "farNonce", {get:function() { - k("public flash.net.NetConnection::get farNonce"); + Object.defineProperty(c.prototype, "farNonce", {get:function() { + p("public flash.net.NetConnection::get farNonce"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(f.prototype, "unconnectedPeerStreams", {get:function() { - k("public flash.net.NetConnection::get unconnectedPeerStreams"); + Object.defineProperty(c.prototype, "unconnectedPeerStreams", {get:function() { + p("public flash.net.NetConnection::get unconnectedPeerStreams"); }, enumerable:!0, configurable:!0}); - f.prototype.ctor = function() { + c.prototype.ctor = function() { this._uri = null; this._connected = !1; this._client = null; this._proxyType = "none"; - this._objectEncoding = f.defaultObjectEncoding; + this._objectEncoding = c.defaultObjectEncoding; this._usingTLS = !1; - c.instance.reportTelemetry({topic:"feature", feature:6}); + this._protocol = null; + t.instance.reportTelemetry({topic:"feature", feature:6}); }; - f.prototype.invoke = function(a) { + c.prototype.invoke = function(a) { return this._invoke(a >>> 0, Array.prototype.slice.call(arguments, 1)); }; - f.prototype.invokeWithArgsArray = function(a, b) { + c.prototype.invokeWithArgsArray = function(a, b) { return this._invoke.call(this, a >>> 0, b); }; - f.prototype._invoke = function(a, b) { + c.prototype._invoke = function(a, b) { var c = !1; switch(a) { case 2: c = !0; } - (c ? d : k)("private flash.net.NetConnection::_invoke (" + a + ")"); + (c ? e : p)("private flash.net.NetConnection::_invoke (" + a + ")"); }; - f.classInitializer = null; - f.initializer = null; - f.classSymbols = null; - f.instanceSymbols = ["close", "addHeader", "call"]; - f._defaultObjectEncoding = 3; - return f; - }(f.events.EventDispatcher); - g.NetConnection = p; - })(f.net || (f.net = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = b.Debug.somewhatImplemented, a = b.AVM2.Runtime.wrapJSObject, c = b.AVM2.AS.flash.events.NetStatusEvent, n = b.FileLoadingService, p = function(b) { - function g(a, b) { - "undefined" === typeof b && (b = "connectToFMS"); - f.events.EventDispatcher.instanceConstructorNoInitialize.call(this); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = ["close", "addHeader", "call"]; + c._defaultObjectEncoding = 3; + return c; + }(a.events.EventDispatcher); + h.NetConnection = n; + })(a.net || (a.net = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(v) { + var p = c.Debug.notImplemented, u = c.Debug.assert, l = c.AVM2.Runtime.asCoerceString, e = c.Debug.somewhatImplemented, m = c.AVM2.Runtime.wrapJSObject, t = c.AVM2.AS.flash.events; + v = c.AVM2.AS.flash.net; + var q = c.AVM2.AS.flash.utils, n = c.FileLoadingService, k = c.AVM2.Runtime.AVM2, f = function(a) { + function d(a, e) { + void 0 === e && (e = "connectToFMS"); + t.EventDispatcher.instanceConstructorNoInitialize.call(this); this._connection = a; - this._peerID = m(b); - this._id = f.display.DisplayObject.getNextSyncID(); + this._peerID = l(e); + this._id = h.display.DisplayObject.getNextSyncID(); this._isDirty = !0; + this._soundTransform = new h.media.SoundTransform; + this._contentTypeHint = null; + this._checkPolicyFile = !0; + this._videoStream = new b; + this._videoStream._onEnsurePlay = function() { + this._notifyVideoControl(2, {paused:!1, time:NaN}); + }.bind(this); } - __extends(g, b); - g.prototype.dispose = function() { - k("public flash.net.NetStream::dispose"); + __extends(d, a); + d.prototype.dispose = function() { + p("public flash.net.NetStream::dispose"); + }; + d.prototype._getVideoStreamURL = function() { + return this._videoStream.url; }; - g.prototype.play = function(a) { - this._url = n.instance.resolveUrl(a); - d("public flash.net.NetStream::play"); + d.prototype.play = function(a) { + h.media.SoundMixer._registerSoundSource(this); + a = l(a); + k.instance.globals["Shumway.Player.Utils"].registerEventListener(this._id, this.processVideoEvent.bind(this)); + this._connection && this._connection.uri ? this._videoStream.playInConnection(this._connection, a) : null !== a || this._connection ? this._videoStream.play(a, this.checkPolicyFile) : this._videoStream.openInDataGenerationMode(); + this._notifyVideoControl(1, {url:this._videoStream.url}); }; - g.prototype.play2 = function(a) { - k("public flash.net.NetStream::play2"); + d.prototype.play2 = function(a) { + p("public flash.net.NetStream::play2"); }; - Object.defineProperty(g.prototype, "info", {get:function() { - k("public flash.net.NetStream::get info"); + Object.defineProperty(d.prototype, "info", {get:function() { + p("public flash.net.NetStream::get info"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "multicastInfo", {get:function() { - k("public flash.net.NetStream::get multicastInfo"); + Object.defineProperty(d.prototype, "multicastInfo", {get:function() { + p("public flash.net.NetStream::get multicastInfo"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "soundTransform", {get:function() { + Object.defineProperty(d.prototype, "soundTransform", {get:function() { return this._soundTransform; }, set:function(a) { - d("public flash.net.NetStream::set soundTransform"); this._soundTransform = a; + h.media.SoundMixer._updateSoundSource(this); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "checkPolicyFile", {get:function() { + Object.defineProperty(d.prototype, "checkPolicyFile", {get:function() { return this._checkPolicyFile; }, set:function(a) { this._checkPolicyFile = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "client", {get:function() { + Object.defineProperty(d.prototype, "client", {get:function() { return this._client; }, set:function(a) { - d("public flash.net.NetStream::set client"); + e("public flash.net.NetStream::set client"); this._client = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "objectEncoding", {get:function() { - k("public flash.net.NetStream::get objectEncoding"); + Object.defineProperty(d.prototype, "objectEncoding", {get:function() { + p("public flash.net.NetStream::get objectEncoding"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "multicastPushNeighborLimit", {get:function() { - k("public flash.net.NetStream::get multicastPushNeighborLimit"); + Object.defineProperty(d.prototype, "multicastPushNeighborLimit", {get:function() { + p("public flash.net.NetStream::get multicastPushNeighborLimit"); }, set:function(a) { - k("public flash.net.NetStream::set multicastPushNeighborLimit"); + p("public flash.net.NetStream::set multicastPushNeighborLimit"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "multicastWindowDuration", {get:function() { - k("public flash.net.NetStream::get multicastWindowDuration"); + Object.defineProperty(d.prototype, "multicastWindowDuration", {get:function() { + p("public flash.net.NetStream::get multicastWindowDuration"); }, set:function(a) { - k("public flash.net.NetStream::set multicastWindowDuration"); + p("public flash.net.NetStream::set multicastWindowDuration"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "multicastRelayMarginDuration", {get:function() { - k("public flash.net.NetStream::get multicastRelayMarginDuration"); + Object.defineProperty(d.prototype, "multicastRelayMarginDuration", {get:function() { + p("public flash.net.NetStream::get multicastRelayMarginDuration"); }, set:function(a) { - k("public flash.net.NetStream::set multicastRelayMarginDuration"); + p("public flash.net.NetStream::set multicastRelayMarginDuration"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "multicastAvailabilityUpdatePeriod", {get:function() { - k("public flash.net.NetStream::get multicastAvailabilityUpdatePeriod"); + Object.defineProperty(d.prototype, "multicastAvailabilityUpdatePeriod", {get:function() { + p("public flash.net.NetStream::get multicastAvailabilityUpdatePeriod"); }, set:function(a) { - k("public flash.net.NetStream::set multicastAvailabilityUpdatePeriod"); + p("public flash.net.NetStream::set multicastAvailabilityUpdatePeriod"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "multicastFetchPeriod", {get:function() { - k("public flash.net.NetStream::get multicastFetchPeriod"); + Object.defineProperty(d.prototype, "multicastFetchPeriod", {get:function() { + p("public flash.net.NetStream::get multicastFetchPeriod"); }, set:function(a) { - k("public flash.net.NetStream::set multicastFetchPeriod"); + p("public flash.net.NetStream::set multicastFetchPeriod"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "multicastAvailabilitySendToAll", {get:function() { - k("public flash.net.NetStream::get multicastAvailabilitySendToAll"); + Object.defineProperty(d.prototype, "multicastAvailabilitySendToAll", {get:function() { + p("public flash.net.NetStream::get multicastAvailabilitySendToAll"); }, set:function(a) { - k("public flash.net.NetStream::set multicastAvailabilitySendToAll"); + p("public flash.net.NetStream::set multicastAvailabilitySendToAll"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "farID", {get:function() { - k("public flash.net.NetStream::get farID"); + Object.defineProperty(d.prototype, "farID", {get:function() { + p("public flash.net.NetStream::get farID"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "nearNonce", {get:function() { - k("public flash.net.NetStream::get nearNonce"); + Object.defineProperty(d.prototype, "nearNonce", {get:function() { + p("public flash.net.NetStream::get nearNonce"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "farNonce", {get:function() { - k("public flash.net.NetStream::get farNonce"); + Object.defineProperty(d.prototype, "farNonce", {get:function() { + p("public flash.net.NetStream::get farNonce"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "peerStreams", {get:function() { - k("public flash.net.NetStream::get peerStreams"); + Object.defineProperty(d.prototype, "peerStreams", {get:function() { + p("public flash.net.NetStream::get peerStreams"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "audioReliable", {get:function() { - k("public flash.net.NetStream::get audioReliable"); + Object.defineProperty(d.prototype, "audioReliable", {get:function() { + p("public flash.net.NetStream::get audioReliable"); }, set:function(a) { - k("public flash.net.NetStream::set audioReliable"); + p("public flash.net.NetStream::set audioReliable"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "videoReliable", {get:function() { - k("public flash.net.NetStream::get videoReliable"); + Object.defineProperty(d.prototype, "videoReliable", {get:function() { + p("public flash.net.NetStream::get videoReliable"); }, set:function(a) { - k("public flash.net.NetStream::set videoReliable"); + p("public flash.net.NetStream::set videoReliable"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "dataReliable", {get:function() { - k("public flash.net.NetStream::get dataReliable"); + Object.defineProperty(d.prototype, "dataReliable", {get:function() { + p("public flash.net.NetStream::get dataReliable"); }, set:function(a) { - k("public flash.net.NetStream::set dataReliable"); + p("public flash.net.NetStream::set dataReliable"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "audioSampleAccess", {get:function() { - k("public flash.net.NetStream::get audioSampleAccess"); + Object.defineProperty(d.prototype, "audioSampleAccess", {get:function() { + p("public flash.net.NetStream::get audioSampleAccess"); }, set:function(a) { - k("public flash.net.NetStream::set audioSampleAccess"); + p("public flash.net.NetStream::set audioSampleAccess"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "videoSampleAccess", {get:function() { - k("public flash.net.NetStream::get videoSampleAccess"); + Object.defineProperty(d.prototype, "videoSampleAccess", {get:function() { + p("public flash.net.NetStream::get videoSampleAccess"); }, set:function(a) { - k("public flash.net.NetStream::set videoSampleAccess"); + p("public flash.net.NetStream::set videoSampleAccess"); }, enumerable:!0, configurable:!0}); - g.prototype.appendBytes = function(a) { - this._mediaSource && (this._mediaSourceBuffer || (this._mediaSourceBuffer = this._mediaSource.addSourceBuffer(this._contentTypeHint)), this._mediaSourceBuffer.appendBuffer(new Uint8Array(a._buffer, 0, a.length))); - d("public flash.net.NetStream::appendBytes"); + d.prototype.appendBytes = function(a) { + a = new Uint8Array(a._buffer, 0, a.length); + this._videoStream.appendBytes(a); }; - g.prototype.appendBytesAction = function() { - var a; - a = m("endSequence"); - "endSequence" === a && this._mediaSource && this._mediaSource.endOfStream(); - d("public flash.net.NetStream::appendBytesAction"); + d.prototype.appendBytesAction = function(a) { + this._videoStream.appendBytesAction(a); }; - Object.defineProperty(g.prototype, "useHardwareDecoder", {get:function() { - k("public flash.net.NetStream::get useHardwareDecoder"); + Object.defineProperty(d.prototype, "useHardwareDecoder", {get:function() { + p("public flash.net.NetStream::get useHardwareDecoder"); }, set:function(a) { - k("public flash.net.NetStream::set useHardwareDecoder"); + p("public flash.net.NetStream::set useHardwareDecoder"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "useJitterBuffer", {get:function() { - k("public flash.net.NetStream::get useJitterBuffer"); + Object.defineProperty(d.prototype, "useJitterBuffer", {get:function() { + p("public flash.net.NetStream::get useJitterBuffer"); }, set:function(a) { - k("public flash.net.NetStream::set useJitterBuffer"); + p("public flash.net.NetStream::set useJitterBuffer"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "videoStreamSettings", {get:function() { - k("public flash.net.NetStream::get videoStreamSettings"); + Object.defineProperty(d.prototype, "videoStreamSettings", {get:function() { + p("public flash.net.NetStream::get videoStreamSettings"); }, set:function(a) { - k("public flash.net.NetStream::set videoStreamSettings"); + p("public flash.net.NetStream::set videoStreamSettings"); }, enumerable:!0, configurable:!0}); - g.prototype.ctor = function(a, b) { - b = m(b); - d("public flash.net.NetStream::ctor"); - this._mediaSource = this._contentTypeHint = null; - this._checkPolicyFile = !0; - this._videoElement = null; - var c, e; - this._videoReady = new Promise(function(a, b) { - c = a; - e = b; - }); - this._videoReady.resolve = c; - this._videoReady.reject = e; - var h, f; - this._videoMetadataReady = new Promise(function(a, b) { - h = a; - f = b; - }); - this._videoMetadataReady.resolve = h; - this._videoMetadataReady.reject = f; - this._videoState = {started:!1, buffer:"empty", bufferTime:.1}; - }; - g.prototype.invoke = function(a) { + d.prototype.invoke = function(a) { return this._invoke(a >>> 0, Array.prototype.slice.call(arguments, 1)); }; - g.prototype.invokeWithArgsArray = function(a, b) { + d.prototype.invokeWithArgsArray = function(a, b) { return this._invoke.call(this, a >>> 0, b); }; - g.prototype._invoke = function(a, b) { - var c = !1, e, h = this._videoElement; + Object.defineProperty(d.prototype, "inBufferSeek", {get:function() { + return this._inBufferSeek; + }, set:function(a) { + this._inBufferSeek = !!a; + }, enumerable:!0, configurable:!0}); + d.prototype._invoke = function(a, b) { + var d = !1, c; switch(a) { case 4: - this._videoState.bufferTime = b[0]; - c = !0; + this._videoStream.bufferTime = b[0]; + d = !0; break; case 202: switch(b[1]) { case "pause": - c = !0; - h && (!1 === b[3] || h.paused ? !0 !== b[3] && h.paused && h.play() : h.pause(), h.currentTime = b[4] / 1E3); + d = !0; + this._notifyVideoControl(2, {paused:!!b[3], time:b[4] / 1E3}); break; case "seek": - c = !0, h && !h.paused && (h.currentTime = b[3] / 1E3); + d = !0, this._notifyVideoControl(3, {time:b[3] / 1E3}); } break; case 300: - e = h ? h.currentTime : 0; - c = !0; + c = this._notifyVideoControl(4, null); + d = !0; break; case 302: - e = this._videoState.bufferTime; - c = !0; + c = this._videoStream.bufferTime; + d = !0; break; case 303: - e = h ? h.duration : 0; - c = !0; + c = this._notifyVideoControl(5, null); + d = !0; break; case 305: - e = "full" === this._videoState.buffer ? 100 : "progress" === this._videoState.buffer ? 50 : 0; - c = !0; + c = this._notifyVideoControl(7, null); + d = !0; break; case 306: - e = 100, c = !0; + c = this._notifyVideoControl(8, null), d = !0; + } + (d ? e : p)("NetStream._invoke (" + a + ")"); + return c; + }; + d.prototype._notifyVideoControl = function(a, b) { + return k.instance.globals["Shumway.Player.Utils"].notifyVideoControl(this._id, a, b); + }; + d.prototype.processVideoEvent = function(a, b) { + this._videoStream.processVideoPlaybackEvent(a, b); + switch(a) { + case 0: + h.media.SoundMixer._updateSoundSource(this); + break; + case 1: + this.dispatchEvent(new t.NetStatusEvent(t.NetStatusEvent.NET_STATUS, !1, !1, m({code:"NetStream.Play.Start", level:"status"}))); + break; + case 2: + this.dispatchEvent(new t.NetStatusEvent(t.NetStatusEvent.NET_STATUS, !1, !1, m({code:"NetStream.Play.Stop", level:"status"}))); + h.media.SoundMixer._unregisterSoundSource(this); + break; + case 3: + this.dispatchEvent(new t.NetStatusEvent(t.NetStatusEvent.NET_STATUS, !1, !1, m({code:"NetStream.Buffer.Full", level:"status"}))); + break; + case 5: + this.dispatchEvent(new t.NetStatusEvent(t.NetStatusEvent.NET_STATUS, !1, !1, m({code:"NetStream.Buffer.Empty", level:"status"}))); + break; + case 6: + this.dispatchEvent(new t.NetStatusEvent(t.NetStatusEvent.NET_STATUS, !1, !1, m({code:4 === b.code ? "NetStream.Play.NoSupportedTrackFound" : 3 === b.code ? "NetStream.Play.FileStructureInvalid" : "NetStream.Play.StreamNotFound", level:"error"}))); + break; + case 8: + this.dispatchEvent(new t.NetStatusEvent(t.NetStatusEvent.NET_STATUS, !1, !1, m({code:"NetStream.Seek.Notify", level:"status"}))); + break; + case 7: + if (this._client) { + var d = {}; + d.asSetPublicProperty("width", b.videoWidth); + d.asSetPublicProperty("height", b.videoHeight); + d.asSetPublicProperty("duration", b.duration); + this._client.asCallPublicProperty("onMetaData", [d]); + } + ; + } + }; + d.prototype.stopSound = function() { + this.pause(); + }; + d.prototype.updateSoundLevels = function(a) { + this._notifyVideoControl(6, {volume:a}); + }; + d.classInitializer = null; + d.initializer = null; + d.classSymbols = null; + d.instanceSymbols = null; + d.DIRECT_CONNECTIONS = "directConnections"; + d.CONNECT_TO_FMS = "connectToFMS"; + return d; + }(h.events.EventDispatcher); + v.NetStream = f; + var d; + (function(a) { + a[a.CLOSED = 0] = "CLOSED"; + a[a.OPENED = 1] = "OPENED"; + a[a.ENDED = 2] = "ENDED"; + a[a.OPENED_DATA_GENERATION = 3] = "OPENED_DATA_GENERATION"; + a[a.ERROR = 4] = "ERROR"; + })(d || (d = {})); + var b = function() { + function b() { + this._domReady = new c.PromiseWrapper; + this._metadataReady = new c.PromiseWrapper; + this._started = !1; + this._buffer = "empty"; + this._bufferTime = .1; + this._contentTypeHint = this._mediaSourceBuffer = this._mediaSource = this._url = null; + this._state = 0; + } + Object.defineProperty(b.prototype, "state", {get:function() { + return this._state; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(b.prototype, "bufferTime", {get:function() { + return this._bufferTime; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(b.prototype, "url", {get:function() { + return this._url; + }, enumerable:!0, configurable:!0}); + b.prototype.play = function(b, d) { + u(0 === this._state); + this._state = 1; + var c = a.mediaSourceOption.value; + c && "undefined" === typeof MediaSource && (console.warn("MediaSource API is not enabled, falling back to regular playback"), c = !1); + if (c) { + var g = new MediaSource; + g.addEventListener("sourceopen", function(a) { + this._mediaSource = g; + }.bind(this)); + g.addEventListener("sourceend", function(a) { + this._mediaSource = null; + }.bind(this)); + if (b) { + this._url = URL.createObjectURL(g); + c = new v.URLRequest(b); + c._checkPolicyFile = d; + var f = new v.URLStream; + f.addEventListener("httpStatus", function(a) { + if (a = a.asGetPublicProperty("responseHeaders").filter(function(a) { + return "Content-Type" === a.asGetPublicProperty("name"); + })[0]) { + a = a.asGetPublicProperty("value"), "application/octet-stream" !== a && (this._contentTypeHint = a); + } + }.bind(this)); + f.addEventListener("progress", function(a) { + a = f.bytesAvailable; + var b = new q.ByteArray; + f.readBytes(b, 0, a); + a = new Uint8Array(b._buffer, 0, b.length); + this.appendBytes(a); + }.bind(this)); + f.addEventListener("complete", function(a) { + this.appendBytesAction("endSequence"); + }.bind(this)); + f.load(c); + } else { + this._url = null; + } + } else { + e("public flash.net.NetStream::play"), this._url = n.instance.resolveUrl(b); + } + }; + b.prototype.playInConnection = function(a, b) { + this.openInDataGenerationMode(); + var d = this, e, c = {packets:0, init:function(a) { + if (a.asGetPublicProperty("audiocodecid") || a.asGetPublicProperty("videocodecid")) { + a = RtmpJs.MP4.parseFLVMetadata(a), e = new RtmpJs.MP4.MP4Mux(a), e.ondata = function(a) { + d.appendBytes(new Uint8Array(a)); + }.bind(this); + } + }, packet:function(a, b, d) { + e.pushPacket(a, new Uint8Array(b), d); + }, generate:function() { + e.flush(); + }}; + a._createRtmpStream(function(a, d) { + a.ondata = function(a) { + console.log("#packet (" + a.typeId + "): @" + a.timestamp); + 0 < a.data.length && c.packet(a.typeId, a.data, a.timestamp); + }; + a.oncallback = function() { + console.log("#callback"); + }; + a.onscriptdata = function(a, b) { + console.log("#object: " + a); + "onMetaData" === a && c.init(b); + }; + a.play(b); + }); + }; + b.prototype.openInDataGenerationMode = function() { + u(0 === this._state); + this._state = 3; + var a = new MediaSource; + a.addEventListener("sourceopen", function(b) { + this._mediaSource = a; + this._ensurePlaying(); + }.bind(this)); + a.addEventListener("sourceend", function(a) { + this._mediaSource = null; + }.bind(this)); + this._url = URL.createObjectURL(a); + }; + b.prototype.appendBytes = function(a) { + u(3 === this._state || 1 === this._state); + if (this._mediaSource) { + if (!this._mediaSourceBuffer) { + var b = this._contentTypeHint || 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'; + this._mediaSourceBufferLock = Promise.resolve(void 0); + this._mediaSourceBuffer = this._mediaSource.addSourceBuffer(b); + } + var d = this._mediaSourceBuffer; + this._mediaSourceBufferLock = this._mediaSourceBufferLock.then(function() { + d.appendBuffer(a); + return new Promise(function(a) { + d.addEventListener("update", function M() { + d.removeEventListener("update", M); + a(); + }); + }); + }); + } + e("public flash.net.NetStream::appendBytes"); + }; + b.prototype.appendBytesAction = function(a) { + u(3 === this._state || 1 === this._state); + a = l(a); + "endSequence" === a && this._mediaSourceBufferLock.then(function() { + this._mediaSource && this._mediaSource.endOfStream(); + this.close(); + }.bind(this)); + e("public flash.net.NetStream::appendBytesAction"); + }; + b.prototype.close = function() { + this._state = 0; + }; + b.prototype._ensurePlaying = function() { + this._onEnsurePlay && this._onEnsurePlay(); + }; + b.prototype._detectContentType = function(a) { + return'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'; + }; + b.prototype.processVideoPlaybackEvent = function(a, b) { + switch(a) { + case 0: + this._domReady.resolve(void 0); + break; + case 1: + if (this._started) { + break; + } + this._started = !0; + break; + case 2: + this._started = !1; + break; + case 3: + this._buffer = "full"; + break; + case 4: + this._buffer = "progress"; + break; + case 5: + this._buffer = "empty"; + break; + case 7: + this._metadataReady.resolve({videoWidth:b.videoWidth, videoHeight:b.videoHeight}); } - (c ? d : k)("NetStream._invoke (" + a + ")"); - return e; }; - g.prototype._createVideoElement = function(b) { - var d = this; - /\.mp4$/i.test(b) && /Intel Mac OS X.*?Firefox\/\d+/.test(window.navigator.userAgent) && (b = "http://videos-cdn.mozilla.net/brand/Mozilla_2011_Story.webm"); - var e = document.createElement("video"); - e.preload = "metadata"; - e.src = b; - e.addEventListener("play", function(b) { - d._videoState.started || (d._videoState.started = !0, d.dispatchEvent(new c(c.NET_STATUS, !1, !1, a({code:"NetStream.Play.Start", level:"status"})))); - }); - e.addEventListener("ended", function(b) { - d._videoState.started = !1; - d.dispatchEvent(new c(c.NET_STATUS, !1, !1, a({code:"NetStream.Play.Stop", level:"status"}))); - }); - e.addEventListener("loadeddata", function(b) { - d._videoState.buffer = "full"; - d.dispatchEvent(new c(c.NET_STATUS, !1, !1, a({code:"NetStream.Buffer.Full", level:"status"}))); - }); - e.addEventListener("progress", function(a) { - d._videoState.buffer = "progress"; - }); - e.addEventListener("waiting", function(b) { - d._videoState.buffer = "empty"; - d.dispatchEvent(new c(c.NET_STATUS, !1, !1, a({code:"NetStream.Buffer.Empty", level:"status"}))); - }); - e.addEventListener("loadedmetadata", function(a) { - d._videoMetadataReady.resolve({videoWidth:e.videoWidth, videoHeight:e.videoHeight}); - d._client && (a = {}, a.asSetPublicProperty("width", e.videoWidth), a.asSetPublicProperty("height", e.videoHeight), a.asSetPublicProperty("duration", e.duration), d._client.asCallPublicProperty("onMetaData", [a])); - }); - e.addEventListener("error", function(b) { - d.dispatchEvent(new c(c.NET_STATUS, !1, !1, a({code:4 === b.target.error.code ? "NetStream.Play.NoSupportedTrackFound" : 3 === b.target.error.code ? "NetStream.Play.FileStructureInvalid" : "NetStream.Play.StreamNotFound", level:"error"}))); - }); - e.play(); - this._videoElement = e; - this._videoReady.resolve(e); - }; - g.classInitializer = null; - g.initializer = null; - g.classSymbols = null; - g.instanceSymbols = null; - g.DIRECT_CONNECTIONS = "directConnections"; - g.CONNECT_TO_FMS = "connectToFMS"; - return g; - }(f.events.EventDispatcher); - g.NetStream = p; - })(f.net || (f.net = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = function(a) { - function b(a, c, d, f, g, u, w, r, h, x, y, t, I, C, E, O, K, F, J, A, B, z, P, D, L) { - "undefined" === typeof P && (P = null); - "undefined" === typeof D && (D = null); - m(P); - m(D); - k("Dummy Constructor: public flash.net.NetStreamInfo"); - } - __extends(b, a); - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; return b; - }(g.ASNative); - f.NetStreamInfo = d; - })(f.net || (f.net = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, b, d, e, f, g, m, w, r, h, x, y, t, I, C, E, O, K, F) { - k("Dummy Constructor: public flash.net.NetStreamMulticastInfo"); + }(); + })(h.net || (h.net = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = c.AVM2.Runtime.asCoerceString, l = function(a) { + function c(a, e, l, k, f, d, b, g, m, h, v, A, B, M, N, K, y, D, L, H, J, C, E, F, I) { + void 0 === E && (E = null); + void 0 === F && (F = null); + s(E); + s(F); + p("public flash.net.NetStreamInfo"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.NetStreamMulticastInfo = m; - })(f.net || (f.net = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.net.NetStreamPlayOptions"); + __extends(c, a); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.ASNative); + h.NetStreamInfo = l; + })(h.net || (h.net = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = function(a) { + function e(a, e, c, l, k, f, d, b, g, h, w, s, u, v, M, N, K, y, D) { + p("public flash.net.NetStreamMulticastInfo"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(f.events.EventDispatcher); - g.NetStreamPlayOptions = m; - })(f.net || (f.net = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, b) { - k("Dummy Constructor: public flash.net.Responder"); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.ASNative); + h.NetStreamMulticastInfo = s; + })(h.net || (h.net = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.dummyConstructor, u = function(a) { + function e() { + p("public flash.net.NetStreamPlayOptions"); } - __extends(a, b); - a.prototype.ctor = function(a, b) { + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.events.EventDispatcher); + h.NetStreamPlayOptions = u; + })(a.net || (a.net = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = function(a) { + function e(a, e) { + p("public flash.net.Responder"); + } + __extends(e, a); + e.prototype.ctor = function(a, e) { this._result = a; - this._status = b; + this._status = e; }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.Responder = m; - })(f.net || (f.net = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = b.Debug.somewhatImplemented, a = b.ObjectUtilities.createEmptyObject, c = function(c) { - function g() { - f.events.EventDispatcher.instanceConstructorNoInitialize.call(this); - this._data = a(); - } - __extends(g, c); - g.deleteAll = function(a) { - m(a); - k("public flash.net.SharedObject::static deleteAll"); - }; - g.getDiskUsage = function(a) { - m(a); - k("public flash.net.SharedObject::static getDiskUsage"); - }; - g._create = function(a, c) { - var d = new g; - d._path = a; - d._data = c; - d._objectEncoding = g._defaultObjectEncoding; - b.Telemetry.instance.reportTelemetry({topic:"feature", feature:3}); - return d; - }; - g.getLocal = function(a, b, c) { - "undefined" === typeof b && (b = null); - a = m(a); - b = m(b); - a = (b || "") + "/" + a; - if (g._sharedObjects[a]) { - return g._sharedObjects[a]; - } - b = sessionStorage.getItem(a); - b = g._create(a, b ? JSON.parse(b) : {}); - return g._sharedObjects[a] = b; - }; - g.getRemote = function(a, b, c, d) { - "undefined" === typeof b && (b = null); - m(a); - m(b); - k("public flash.net.SharedObject::static getRemote"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.ASNative); + h.Responder = s; + })(h.net || (h.net = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = c.AVM2.Runtime.asCoerceString, l = c.Debug.somewhatImplemented, e = function(e) { + function h() { + a.events.EventDispatcher.instanceConstructorNoInitialize.call(this); + this._data = Object.create(null); + } + __extends(h, e); + h.deleteAll = function(a) { + u(a); + p("public flash.net.SharedObject::static deleteAll"); + }; + h.getDiskUsage = function(a) { + u(a); + p("public flash.net.SharedObject::static getDiskUsage"); + }; + h._create = function(a, e) { + var k = new h; + k._path = a; + k._data = e; + k._objectEncoding = h._defaultObjectEncoding; + c.Telemetry.instance.reportTelemetry({topic:"feature", feature:3}); + return k; + }; + h.getLocal = function(a, e, c) { + void 0 === e && (e = null); + a = u(a); + e = u(e); + a = (e || "") + "/" + a; + if (h._sharedObjects[a]) { + return h._sharedObjects[a]; + } + e = sessionStorage.getItem(a); + e = h._create(a, e ? JSON.parse(e) : {}); + return h._sharedObjects[a] = e; + }; + h.getRemote = function(a, e, c, f) { + void 0 === e && (e = null); + u(a); + u(e); + p("public flash.net.SharedObject::static getRemote"); }; - Object.defineProperty(g, "defaultObjectEncoding", {get:function() { - return g._defaultObjectEncoding; + Object.defineProperty(h, "defaultObjectEncoding", {get:function() { + return h._defaultObjectEncoding; }, set:function(a) { - g._defaultObjectEncoding = a >>> 0; + h._defaultObjectEncoding = a >>> 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "data", {get:function() { + Object.defineProperty(h.prototype, "data", {get:function() { return this._data; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "objectEncoding", {get:function() { + Object.defineProperty(h.prototype, "objectEncoding", {get:function() { return this._objectEncoding; }, set:function(a) { this._objectEncoding = a >>> 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "client", {get:function() { - k("public flash.net.SharedObject::get client"); + Object.defineProperty(h.prototype, "client", {get:function() { + p("public flash.net.SharedObject::get client"); }, set:function(a) { - k("public flash.net.SharedObject::set client"); + p("public flash.net.SharedObject::set client"); }, enumerable:!0, configurable:!0}); - g.prototype.setDirty = function(a) { - m(a); - d("public flash.net.SharedObject::setDirty"); + h.prototype.setDirty = function(a) { + u(a); + l("public flash.net.SharedObject::setDirty"); }; - g.prototype.invoke = function(a) { + h.prototype.invoke = function(a) { return this._invoke(a >>> 0, Array.prototype.slice.call(arguments, 1)); }; - g.prototype.invokeWithArgsArray = function(a, b) { - return this._invoke(a >>> 0, b); + h.prototype.invokeWithArgsArray = function(a, e) { + return this._invoke(a >>> 0, e); }; - g.prototype._invoke = function(a, b) { + h.prototype._invoke = function(a, e) { var c = !1, f; switch(a) { case 4: @@ -32224,705 +39490,800 @@ case 3: c = !0; } - (c ? d : k)("private flash.net.SharedObject::_invoke (" + a + ")"); + (c ? l : p)("private flash.net.SharedObject::_invoke (" + a + ")"); return f; }; - g.classInitializer = null; - g.initializer = null; - g.classSymbols = null; - g.instanceSymbols = null; - g._sharedObjects = a(); - g._defaultObjectEncoding = 3; - return g; - }(f.events.EventDispatcher); - g.SharedObject = c; - })(f.net || (f.net = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + h.classInitializer = null; + h.initializer = null; + h.classSymbols = null; + h.instanceSymbols = null; + h._sharedObjects = Object.create(null); + h._defaultObjectEncoding = 3; + return h; + }(a.events.EventDispatcher); + h.SharedObject = e; + })(a.net || (a.net = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = b.Debug.somewhatImplemented, a = b.AVM2.Errors, c = b.AVM2.Runtime.throwError, n = function(b) { - function e(a, b) { - "undefined" === typeof a && (a = null); - m(a); - k("Dummy Constructor: public flash.net.Socket"); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = c.Debug.dummyConstructor, l = c.AVM2.Runtime.asCoerceString, e = c.Debug.somewhatImplemented, m = c.AVM2.Errors, t = c.AVM2.Runtime.throwError, q = function(a) { + function c(a, d) { + void 0 === a && (a = null); + l(a); + u("public flash.net.Socket"); } - __extends(e, b); - Object.defineProperty(e.prototype, "bytesAvailable", {get:function() { - k("public flash.net.Socket::get bytesAvailable"); + __extends(c, a); + Object.defineProperty(c.prototype, "bytesAvailable", {get:function() { + p("public flash.net.Socket::get bytesAvailable"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "connected", {get:function() { - k("public flash.net.Socket::get connected"); + Object.defineProperty(c.prototype, "connected", {get:function() { + p("public flash.net.Socket::get connected"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "objectEncoding", {get:function() { - k("public flash.net.Socket::get objectEncoding"); + Object.defineProperty(c.prototype, "objectEncoding", {get:function() { + p("public flash.net.Socket::get objectEncoding"); }, set:function(a) { - k("public flash.net.Socket::set objectEncoding"); + p("public flash.net.Socket::set objectEncoding"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "endian", {get:function() { - k("public flash.net.Socket::get endian"); + Object.defineProperty(c.prototype, "endian", {get:function() { + p("public flash.net.Socket::get endian"); }, set:function(a) { - m(a); - k("public flash.net.Socket::set endian"); + l(a); + p("public flash.net.Socket::set endian"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "bytesPending", {get:function() { - k("public flash.net.Socket::get bytesPending"); + Object.defineProperty(c.prototype, "bytesPending", {get:function() { + p("public flash.net.Socket::get bytesPending"); }, enumerable:!0, configurable:!0}); - e.prototype.readBytes = function(a, b, c) { - k("public flash.net.Socket::readBytes"); + c.prototype.readBytes = function(a, d, b) { + p("public flash.net.Socket::readBytes"); }; - e.prototype.writeBytes = function(a, b, c) { - k("public flash.net.Socket::writeBytes"); + c.prototype.writeBytes = function(a, d, b) { + p("public flash.net.Socket::writeBytes"); }; - e.prototype.writeBoolean = function(a) { - k("public flash.net.Socket::writeBoolean"); + c.prototype.writeBoolean = function(a) { + p("public flash.net.Socket::writeBoolean"); }; - e.prototype.writeByte = function(a) { - k("public flash.net.Socket::writeByte"); + c.prototype.writeByte = function(a) { + p("public flash.net.Socket::writeByte"); }; - e.prototype.writeShort = function(a) { - k("public flash.net.Socket::writeShort"); + c.prototype.writeShort = function(a) { + p("public flash.net.Socket::writeShort"); }; - e.prototype.writeInt = function(a) { - k("public flash.net.Socket::writeInt"); + c.prototype.writeInt = function(a) { + p("public flash.net.Socket::writeInt"); }; - e.prototype.writeUnsignedInt = function(a) { - k("public flash.net.Socket::writeUnsignedInt"); + c.prototype.writeUnsignedInt = function(a) { + p("public flash.net.Socket::writeUnsignedInt"); }; - e.prototype.writeFloat = function(a) { - k("public flash.net.Socket::writeFloat"); + c.prototype.writeFloat = function(a) { + p("public flash.net.Socket::writeFloat"); }; - e.prototype.writeDouble = function(a) { - k("public flash.net.Socket::writeDouble"); + c.prototype.writeDouble = function(a) { + p("public flash.net.Socket::writeDouble"); }; - e.prototype.writeMultiByte = function(a, b) { - m(a); - m(b); - k("public flash.net.Socket::writeMultiByte"); + c.prototype.writeMultiByte = function(a, d) { + l(a); + l(d); + p("public flash.net.Socket::writeMultiByte"); }; - e.prototype.writeUTF = function(a) { - m(a); - k("public flash.net.Socket::writeUTF"); + c.prototype.writeUTF = function(a) { + l(a); + p("public flash.net.Socket::writeUTF"); }; - e.prototype.writeUTFBytes = function(a) { - m(a); - k("public flash.net.Socket::writeUTFBytes"); + c.prototype.writeUTFBytes = function(a) { + l(a); + p("public flash.net.Socket::writeUTFBytes"); }; - e.prototype.readBoolean = function() { - k("public flash.net.Socket::readBoolean"); + c.prototype.readBoolean = function() { + p("public flash.net.Socket::readBoolean"); }; - e.prototype.readByte = function() { - k("public flash.net.Socket::readByte"); + c.prototype.readByte = function() { + p("public flash.net.Socket::readByte"); }; - e.prototype.readUnsignedByte = function() { - k("public flash.net.Socket::readUnsignedByte"); + c.prototype.readUnsignedByte = function() { + p("public flash.net.Socket::readUnsignedByte"); }; - e.prototype.readShort = function() { - k("public flash.net.Socket::readShort"); + c.prototype.readShort = function() { + p("public flash.net.Socket::readShort"); }; - e.prototype.readUnsignedShort = function() { - k("public flash.net.Socket::readUnsignedShort"); + c.prototype.readUnsignedShort = function() { + p("public flash.net.Socket::readUnsignedShort"); }; - e.prototype.readInt = function() { - k("public flash.net.Socket::readInt"); + c.prototype.readInt = function() { + p("public flash.net.Socket::readInt"); }; - e.prototype.readUnsignedInt = function() { - k("public flash.net.Socket::readUnsignedInt"); + c.prototype.readUnsignedInt = function() { + p("public flash.net.Socket::readUnsignedInt"); }; - e.prototype.readFloat = function() { - k("public flash.net.Socket::readFloat"); + c.prototype.readFloat = function() { + p("public flash.net.Socket::readFloat"); }; - e.prototype.readDouble = function() { - k("public flash.net.Socket::readDouble"); + c.prototype.readDouble = function() { + p("public flash.net.Socket::readDouble"); }; - e.prototype.readMultiByte = function(a, b) { - m(b); - k("public flash.net.Socket::readMultiByte"); + c.prototype.readMultiByte = function(a, d) { + l(d); + p("public flash.net.Socket::readMultiByte"); }; - e.prototype.readUTF = function() { - k("public flash.net.Socket::readUTF"); + c.prototype.readUTF = function() { + p("public flash.net.Socket::readUTF"); }; - e.prototype.readUTFBytes = function(a) { - k("public flash.net.Socket::readUTFBytes"); + c.prototype.readUTFBytes = function(a) { + p("public flash.net.Socket::readUTFBytes"); }; - e.prototype.flush = function() { - k("public flash.net.Socket::flush"); + c.prototype.flush = function() { + p("public flash.net.Socket::flush"); }; - e.prototype.writeObject = function(a) { - k("public flash.net.Socket::writeObject"); + c.prototype.writeObject = function(a) { + p("public flash.net.Socket::writeObject"); }; - e.prototype.readObject = function() { - k("public flash.net.Socket::readObject"); + c.prototype.readObject = function() { + p("public flash.net.Socket::readObject"); }; - e.prototype.internalGetSecurityErrorMessage = function(a, b) { - m(a); - d("flash.net.Socket::internalGetSecurityErrorMessage"); + c.prototype.internalGetSecurityErrorMessage = function(a, d) { + l(a); + e("flash.net.Socket::internalGetSecurityErrorMessage"); return "SecurityErrorEvent"; }; - e.prototype.internalConnect = function(b, e) { - b = m(b); - e |= 0; - d("flash.net.Socket::internalConnect"); - c("SecurityError", a.SocketConnectError, b, e); + c.prototype.internalConnect = function(a, d) { + a = l(a); + d |= 0; + e("flash.net.Socket::internalConnect"); + t("SecurityError", m.SocketConnectError, a, d); }; - e.prototype.didFailureOccur = function() { - d("flash.net.Socket::didFailureOccur"); + c.prototype.didFailureOccur = function() { + e("flash.net.Socket::didFailureOccur"); return!0; }; - e.classInitializer = null; - e.initializer = null; - e.classSymbols = null; - e.instanceSymbols = null; - return e; - }(f.events.EventDispatcher); - g.Socket = n; - })(f.net || (f.net = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a) { - k("Dummy Constructor: public flash.net.URLLoader"); - } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = "data dataFormat bytesLoaded bytesTotal load close".split(" "); - return a; - }(f.events.EventDispatcher); - g.URLLoader = m; - })(f.net || (f.net = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(t) { - var s = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = b.AVM2.Runtime.throwError, a = function(a) { - function b(a) { - "undefined" === typeof a && (a = null); - m(a); - s("Dummy Constructor: public flash.net.URLRequest"); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.events.EventDispatcher); + h.Socket = q; + })(a.net || (a.net = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = a.events.Event, u = a.events.IOErrorEvent, l = a.events.ProgressEvent, e = a.events.HTTPStatusEvent, m = a.events.SecurityErrorEvent, t = function(q) { + function n(c) { + a.events.EventDispatcher.instanceConstructorNoInitialize.call(this); + var f = this._stream = new h.URLStream; + f.addEventListener(p.OPEN, this.onStreamOpen.bind(this)); + f.addEventListener(p.COMPLETE, this.onStreamComplete.bind(this)); + f.addEventListener(l.PROGRESS, this.onStreamProgress.bind(this)); + f.addEventListener(u.IO_ERROR, this.onStreamIOError.bind(this)); + f.addEventListener(e.HTTP_STATUS, this.onStreamHTTPStatus.bind(this)); + f.addEventListener(e.HTTP_RESPONSE_STATUS, this.onStreamHTTPResponseStatus.bind(this)); + f.addEventListener(m.SECURITY_ERROR, this.onStreamSecurityError.bind(this)); + this.$BgdataFormat = "text"; + c && this.load(c); + } + __extends(n, q); + Object.defineProperty(n.prototype, "data", {get:function() { + return this.$Bgdata; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "dataFormat", {get:function() { + return this.$BgdataFormat; + }, set:function(a) { + c.Debug.assert("string" === typeof a); + this.$BgdataFormat = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "bytesLoaded", {get:function() { + return this.$BgbytesLoaded; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(n.prototype, "bytesTotal", {get:function() { + return this.$BgbytesTotal; + }, enumerable:!0, configurable:!0}); + n.prototype.load = function(a) { + this._stream.load(a); + }; + n.prototype.close = function() { + this._stream.close(); + }; + n.prototype.complete = function() { + var e = new a.utils.ByteArray; + this._stream.readBytes(e); + if ("binary" === this.$BgdataFormat) { + this.$Bgdata = e; + } else { + var c = e.toString(); + 0 < e.length && "variables" === this.$BgdataFormat ? (e = new h.URLVariables, this._ignoreDecodeErrors && (e._ignoreDecodingErrors = !0), e.decode(String(c)), this.$Bgdata = e) : this.$Bgdata = c; + } + }; + n.prototype.addEventListener = function(a, c, d, b, g) { + q.prototype.addEventListener.call(this, a, c, d, b, g); + a === e.HTTP_RESPONSE_STATUS && (this._httpResponseEventBound = !0); + }; + n.prototype.onStreamOpen = function(a) { + this.dispatchEvent(a); + }; + n.prototype.onStreamComplete = function(a) { + this.complete(); + this.dispatchEvent(a); + }; + n.prototype.onStreamProgress = function(a) { + this.$BgbytesLoaded = a.bytesLoaded; + this.$BgbytesTotal = a.bytesTotal; + this.dispatchEvent(a); + }; + n.prototype.onStreamIOError = function(a) { + this.complete(); + this.dispatchEvent(a); + }; + n.prototype.onStreamHTTPStatus = function(a) { + this.dispatchEvent(a); + }; + n.prototype.onStreamHTTPResponseStatus = function(a) { + this._httpResponseEventBound && this.dispatchEvent(a); + }; + n.prototype.onStreamSecurityError = function(a) { + this.dispatchEvent(a); + }; + n.classInitializer = null; + n.initializer = null; + n.classSymbols = null; + n.instanceSymbols = null; + return n; + }(a.events.EventDispatcher); + h.URLLoader = t; + })(a.net || (a.net = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(s) { + (function(v) { + var p = c.AVM2.Runtime.asCoerceString, u = c.AVM2.Runtime.throwError, l = function(a) { + function c(a) { + void 0 === a && (a = null); + this._url = p(a); } - __extends(b, a); - Object.defineProperty(b.prototype, "url", {get:function() { + __extends(c, a); + Object.defineProperty(c.prototype, "url", {get:function() { return this._url; }, set:function(a) { - this._url = a = m(a); + this._url = a = p(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "data", {get:function() { + Object.defineProperty(c.prototype, "data", {get:function() { return this._data; }, set:function(a) { this._data = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "method", {get:function() { + Object.defineProperty(c.prototype, "method", {get:function() { return this._method; }, set:function(a) { - a = m(a); - "get" !== a && "GET" !== a && "post" !== a && "POST" !== a && d("ArgumentError", k.Errors.InvalidArgumentError); + a = p(a); + "get" !== a && "GET" !== a && "post" !== a && "POST" !== a && u("ArgumentError", h.Errors.InvalidArgumentError); this._method = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "contentType", {get:function() { + Object.defineProperty(c.prototype, "contentType", {get:function() { return this._contentType; }, set:function(a) { - this._contentType = a = m(a); + this._contentType = a = p(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "requestHeaders", {get:function() { + Object.defineProperty(c.prototype, "requestHeaders", {get:function() { return this._requestHeaders; }, set:function(a) { - Array.isArray(a) || d("ArgumentError", k.Errors.InvalidArgumentError, "value"); + Array.isArray(a) || u("ArgumentError", h.Errors.InvalidArgumentError, "value"); this._requestHeaders = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "digest", {get:function() { + Object.defineProperty(c.prototype, "digest", {get:function() { return this._digest; }, set:function(a) { - this._digest = a = m(a); + this._digest = a = p(a); }, enumerable:!0, configurable:!0}); - b.prototype._toFileRequest = function() { + c.prototype._toFileRequest = function() { var a = {}; a.url = this._url; a.method = this._method; a.checkPolicyFile = this._checkPolicyFile; if (this._data) { - if (a.mimeType = this._contentType, f.utils.ByteArray.isType(this._data)) { + if (a.mimeType = this._contentType, s.utils.ByteArray.isType(this._data)) { a.data = new Uint8Array(this._data._buffer, 0, this._data.length); } else { - var b = this._data.asGetPublicProperty("toString").call(this._data); + var e = this._data.asGetPublicProperty("toString").call(this._data); if ("GET" === this._method) { var c = a.url.lastIndexOf("?"); - a.url = (0 > c ? a.url : a.url.substring(0, c)) + "?" + b; + a.url = (0 > c ? a.url : a.url.substring(0, c)) + "?" + e; } else { - a.data = b; + a.data = e; } } } return a; }; - b.classInitializer = null; - b.initializer = function() { + c.classInitializer = null; + c.initializer = function() { this._url = null; this._method = "GET"; this._digest = this._data = null; this._contentType = "application/x-www-form-urlencoded"; - this._requestHeaders = null; + this._requestHeaders = []; this._checkPolicyFile = !0; }; - b.classSymbols = null; - b.bindings = null; - return b; - }(g.ASNative); - t.URLRequest = a; - })(f.net || (f.net = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.AVM2.Runtime.asCoerceString, m = function(b) { - function a(a, b) { - "undefined" === typeof a && (a = ""); - "undefined" === typeof b && (b = ""); - k(a); - k(b); + c.classSymbols = null; + c.bindings = null; + return c; + }(a.ASNative); + v.URLRequest = l; + })(s.net || (s.net = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.AVM2.Runtime.asCoerceString, s = function(a) { + function e(a, e) { + void 0 === a && (a = ""); + void 0 === e && (e = ""); + p(a); + p(e); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = ["name!", "value!"]; - return a; - }(g.ASNative); - f.URLRequestHeader = m; - })(f.net || (f.net = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var s = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = b.FileLoadingService, a = b.AVM2.Runtime.throwError, c = b.AVM2.AS.flash.utils, n = function(b) { - function e() { - s("Dummy Constructor: public flash.net.URLStream"); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = ["name!", "value!"]; + return e; + }(a.ASNative); + h.URLRequestHeader = s; + })(h.net || (h.net = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(v) { + var p = c.Debug.notImplemented, u = c.Debug.dummyConstructor, l = c.AVM2.Runtime.asCoerceString, e = c.FileLoadingService, m = c.AVM2.Runtime.throwError, t = c.AVM2.AS.flash.utils, q = function(c) { + function k() { + u("public flash.net.URLStream"); } - __extends(e, b); - Object.defineProperty(e.prototype, "connected", {get:function() { + __extends(k, c); + Object.defineProperty(k.prototype, "connected", {get:function() { return this._connected; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "bytesAvailable", {get:function() { + Object.defineProperty(k.prototype, "bytesAvailable", {get:function() { return this._buffer.length - this._buffer.position; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "objectEncoding", {get:function() { + Object.defineProperty(k.prototype, "objectEncoding", {get:function() { return this._buffer.objectEncoding; }, set:function(a) { this._buffer.objectEncoding = a >>> 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "endian", {get:function() { + Object.defineProperty(k.prototype, "endian", {get:function() { return this._buffer.endian; }, set:function(a) { - a = m(a); + a = l(a); this._buffer.endian = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "diskCacheEnabled", {get:function() { - s("public flash.net.URLStream::get diskCacheEnabled"); + Object.defineProperty(k.prototype, "diskCacheEnabled", {get:function() { + p("public flash.net.URLStream::get diskCacheEnabled"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "position", {get:function() { + Object.defineProperty(k.prototype, "position", {get:function() { return this._buffer.position; }, set:function(a) { this._buffer.position = +a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "length", {get:function() { + Object.defineProperty(k.prototype, "length", {get:function() { return this._buffer.length; }, enumerable:!0, configurable:!0}); - e.prototype.load = function(a) { - var b = f.events.Event, c = f.events.IOErrorEvent, e = f.events.ProgressEvent, g = f.events.HTTPStatusEvent, h = d.instance.createSession(), k = this; - h.onprogress = function(a, b) { - var c = k._buffer.position; - k._buffer.position = k._writePosition; - k._buffer.writeRawBytes(a); - k._writePosition = k._buffer.position; - k._buffer.position = c; - k.dispatchEvent(new e(e.PROGRESS, !1, !1, b.bytesLoaded, b.bytesTotal)); + k.prototype.load = function(c) { + var d = a.events.Event, b = a.events.IOErrorEvent, g = a.events.ProgressEvent, k = a.events.HTTPStatusEvent, l = e.instance.createSession(), h = this; + l.onprogress = function(a, b) { + var d = h._buffer.position; + h._buffer.position = h._writePosition; + h._buffer.writeRawBytes(a); + h._writePosition = h._buffer.position; + h._buffer.position = d; + h.dispatchEvent(new g(g.PROGRESS, !1, !1, b.bytesLoaded, b.bytesTotal)); }; - h.onerror = function(a) { - k._connected = !1; - k.dispatchEvent(new c(c.IO_ERROR, !1, !1, a)); + l.onerror = function(a) { + h._connected = !1; + h.dispatchEvent(new b(b.IO_ERROR, !1, !1, a)); }; - h.onopen = function() { - k._connected = !0; - k.dispatchEvent(new b(b.OPEN, !1, !1)); + l.onopen = function() { + h._connected = !0; + h.dispatchEvent(new d(d.OPEN, !1, !1)); }; - h.onhttpstatus = function(a, b, c) { - b = new g(g.HTTP_STATUS, !1, !1, b); - var d = []; - c.split(/(?:\n|\r?\n)/g).forEach(function(b) { - if (b = /^([^:]+): (.*)$/.exec(b)) { - d.push(new f.net.URLRequestHeader(b[1], b[2])), "Location" === b[1] && (a = b[2]); + l.onhttpstatus = function(b, d, e) { + d = new k(k.HTTP_STATUS, !1, !1, d); + var c = []; + e.split(/(?:\n|\r?\n)/g).forEach(function(d) { + if (d = /^([^:]+): (.*)$/.exec(d)) { + c.push(new a.net.URLRequestHeader(d[1], d[2])), "Location" === d[1] && (b = d[2]); } }); - b.asSetPublicProperty("responseHeaders", d); - b.asSetPublicProperty("responseURL", a); - k.dispatchEvent(b); + d.asSetPublicProperty("responseHeaders", c); + d.asSetPublicProperty("responseURL", b); + h.dispatchEvent(d); }; - h.onclose = function() { - k._connected = !1; - k.dispatchEvent(new b(b.COMPLETE, !1, !1)); + l.onclose = function() { + h._connected = !1; + h.dispatchEvent(new d(d.COMPLETE, !1, !1)); }; - h.open(a._toFileRequest()); - this._session = h; + l.open(c._toFileRequest()); + this._session = l; }; - e.prototype.readBytes = function(b, c, d) { - "undefined" === typeof c && (c = 0); - "undefined" === typeof d && (d = 0); - c >>>= 0; + k.prototype.readBytes = function(a, d, b) { + void 0 === d && (d = 0); + void 0 === b && (b = 0); d >>>= 0; - 0 > d && a("ArgumentError", k.Errors.InvalidArgumentError, "length"); - this._buffer.readBytes(b, c, d); + b >>>= 0; + 0 > b && m("ArgumentError", h.Errors.InvalidArgumentError, "length"); + this._buffer.readBytes(a, d, b); }; - e.prototype.readBoolean = function() { - s("public flash.net.URLStream::readBoolean"); + k.prototype.readBoolean = function() { + p("public flash.net.URLStream::readBoolean"); }; - e.prototype.readByte = function() { + k.prototype.readByte = function() { return this._buffer.readByte(); }; - e.prototype.readUnsignedByte = function() { - s("public flash.net.URLStream::readUnsignedByte"); + k.prototype.readUnsignedByte = function() { + p("public flash.net.URLStream::readUnsignedByte"); }; - e.prototype.readShort = function() { - s("public flash.net.URLStream::readShort"); + k.prototype.readShort = function() { + p("public flash.net.URLStream::readShort"); }; - e.prototype.readUnsignedShort = function() { + k.prototype.readUnsignedShort = function() { return this._buffer.readUnsignedShort(); }; - e.prototype.readUnsignedInt = function() { - s("public flash.net.URLStream::readUnsignedInt"); + k.prototype.readUnsignedInt = function() { + p("public flash.net.URLStream::readUnsignedInt"); }; - e.prototype.readInt = function() { - s("public flash.net.URLStream::readInt"); + k.prototype.readInt = function() { + p("public flash.net.URLStream::readInt"); }; - e.prototype.readFloat = function() { - s("public flash.net.URLStream::readFloat"); + k.prototype.readFloat = function() { + p("public flash.net.URLStream::readFloat"); }; - e.prototype.readDouble = function() { - s("public flash.net.URLStream::readDouble"); + k.prototype.readDouble = function() { + p("public flash.net.URLStream::readDouble"); }; - e.prototype.readMultiByte = function(a, b) { - m(b); - s("public flash.net.URLStream::readMultiByte"); + k.prototype.readMultiByte = function(a, d) { + l(d); + p("public flash.net.URLStream::readMultiByte"); }; - e.prototype.readUTF = function() { + k.prototype.readUTF = function() { return this._buffer.readUTF(); }; - e.prototype.readUTFBytes = function(a) { + k.prototype.readUTFBytes = function(a) { return this._buffer.readUTFBytes(a); }; - e.prototype.close = function() { + k.prototype.close = function() { this._session.close(); }; - e.prototype.readObject = function() { - s("public flash.net.URLStream::readObject"); + k.prototype.readObject = function() { + p("public flash.net.URLStream::readObject"); }; - e.prototype.stop = function() { - s("public flash.net.URLStream::stop"); + k.prototype.stop = function() { + p("public flash.net.URLStream::stop"); }; - e.classInitializer = null; - e.initializer = function() { - this._buffer = new c.ByteArray; + k.classInitializer = null; + k.initializer = function() { + this._buffer = new t.ByteArray; this._writePosition = 0; this._connected = !1; }; + k.classSymbols = null; + k.instanceSymbols = null; + return k; + }(a.events.EventDispatcher); + v.URLStream = q; + })(a.net || (a.net = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(s) { + (function(s) { + var p = c.AVM2.Runtime.asCoerceString, u = function(a) { + function e(a) { + void 0 === a && (a = null); + this._ignoreDecodingErrors = !1; + a && this.decode(a); + } + __extends(e, a); + e.prototype.decode = function(a) { + a = p(a); + a = a.split("&"); + for (var e = 0;e < a.length;e++) { + var c = a[e], l = c.indexOf("="); + 0 > l && (this._ignoreDecodingErrors ? l = c.length : throwError("Error", h.Errors.DecodeParamError)); + var k = unescape(c.substring(0, l).split("+").join(" ")), c = unescape(c.substring(l + 1).split("+").join(" ")), l = this.asGetPublicProperty(k); + "undefined" === typeof l ? this.asSetPublicProperty(k, c) : Array.isArray(l) ? l.push(c) : this.asSetPublicProperty(k, [l, c]); + } + }; + e.prototype.toString = function() { + for (var a = [], e = this.asGetEnumerableKeys(), c = 0;c < e.length;c++) { + var l = e[c].split(" ").join("+"), k = this.asGetPublicProperty(l), l = escape(l).split(" ").join("+"); + if (Array.isArray(k)) { + for (var f = 0;f < k.length;f++) { + a.push(l + "=" + escape(k[f])); + } + } else { + a.push(l + "=" + escape(k)); + } + } + return a.join("&"); + }; + e.classInitializer = null; + e.initializer = null; e.classSymbols = null; e.instanceSymbols = null; return e; - }(f.events.EventDispatcher); - g.URLStream = n; - })(f.net || (f.net = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a) { - k("Dummy Constructor: public flash.net.URLVariables"); - } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = ["decode!"]; - return a; - }(g.ASNative); - f.URLVariables = m; - })(f.net || (f.net = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.sensors.Accelerometer"); + }(a.ASNative); + s.URLVariables = u; + })(s.net || (s.net = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = c.Debug.dummyConstructor, l = function(a) { + function c() { + u("public flash.sensors.Accelerometer"); } - __extends(a, b); - Object.defineProperty(a.prototype, "isSupported", {get:function() { - k("public flash.sensors.Accelerometer::get isSupported"); + __extends(c, a); + Object.defineProperty(c.prototype, "isSupported", {get:function() { + p("public flash.sensors.Accelerometer::get isSupported"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "muted", {get:function() { - k("public flash.sensors.Accelerometer::get muted"); + Object.defineProperty(c.prototype, "muted", {get:function() { + p("public flash.sensors.Accelerometer::get muted"); }, enumerable:!0, configurable:!0}); - a.prototype.setRequestedUpdateInterval = function(a) { - k("public flash.sensors.Accelerometer::setRequestedUpdateInterval"); + c.prototype.setRequestedUpdateInterval = function(a) { + p("public flash.sensors.Accelerometer::setRequestedUpdateInterval"); }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(f.events.EventDispatcher); - g.Accelerometer = m; - })(f.sensors || (f.sensors = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.sensors.Geolocation"); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.events.EventDispatcher); + h.Accelerometer = l; + })(a.sensors || (a.sensors = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = c.Debug.dummyConstructor, l = function(a) { + function c() { + u("public flash.sensors.Geolocation"); } - __extends(a, b); - Object.defineProperty(a.prototype, "isSupported", {get:function() { - k("public flash.sensors.Geolocation::get isSupported"); + __extends(c, a); + Object.defineProperty(c.prototype, "isSupported", {get:function() { + p("public flash.sensors.Geolocation::get isSupported"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "muted", {get:function() { - k("public flash.sensors.Geolocation::get muted"); + Object.defineProperty(c.prototype, "muted", {get:function() { + p("public flash.sensors.Geolocation::get muted"); }, enumerable:!0, configurable:!0}); - a.prototype.setRequestedUpdateInterval = function(a) { - k("public flash.sensors.Geolocation::setRequestedUpdateInterval"); + c.prototype.setRequestedUpdateInterval = function(a) { + p("public flash.sensors.Geolocation::setRequestedUpdateInterval"); }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(f.events.EventDispatcher); - g.Geolocation = m; - })(f.sensors || (f.sensors = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = b.AVM2.Runtime.AVM2, a = b.AVM2.Runtime.ApplicationDomain, c = b.AVM2.ABC.Multiname, n = function(b) { - function e(b) { - "undefined" === typeof b && (b = null); - b instanceof a ? this._runtimeDomain = b : (b = b ? b._runtimeDomain : d.currentDomain().system, this._runtimeDomain = new a(b.vm, b, 2, !1)); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.events.EventDispatcher); + h.Geolocation = l; + })(a.sensors || (a.sensors = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.notImplemented, s = c.AVM2.Runtime.asCoerceString, l = c.AVM2.Runtime.AVM2, e = c.AVM2.Runtime.ApplicationDomain, m = c.AVM2.ABC.Multiname, t = function(a) { + function c(a) { + void 0 === a && (a = null); + a instanceof e ? this._runtimeDomain = a : (a = a ? a._runtimeDomain : l.currentDomain().system, this._runtimeDomain = new e(a.vm, a, 2, !1)); } - __extends(e, b); - Object.defineProperty(e, "currentDomain", {get:function() { - return new e(d.currentDomain()); + __extends(c, a); + Object.defineProperty(c, "currentDomain", {get:function() { + return new c(l.currentDomain()); }, enumerable:!0, configurable:!0}); - Object.defineProperty(e, "MIN_DOMAIN_MEMORY_LENGTH", {get:function() { - k("public flash.system.ApplicationDomain::get MIN_DOMAIN_MEMORY_LENGTH"); + Object.defineProperty(c, "MIN_DOMAIN_MEMORY_LENGTH", {get:function() { + p("public flash.system.ApplicationDomain::get MIN_DOMAIN_MEMORY_LENGTH"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "parentDomain", {get:function() { - return this._runtimeDomain.base ? new e(this._runtimeDomain.base) : null; + Object.defineProperty(c.prototype, "parentDomain", {get:function() { + return this._runtimeDomain.base ? new c(this._runtimeDomain.base) : null; }, enumerable:!0, configurable:!0}); - Object.defineProperty(e.prototype, "domainMemory", {get:function() { - k("public flash.system.ApplicationDomain::get domainMemory"); + Object.defineProperty(c.prototype, "domainMemory", {get:function() { + p("public flash.system.ApplicationDomain::get domainMemory"); }, set:function(a) { - k("public flash.system.ApplicationDomain::set domainMemory"); + p("public flash.system.ApplicationDomain::set domainMemory"); }, enumerable:!0, configurable:!0}); - e.prototype.getDefinition = function(a) { - return(a = m(a)) ? (a = a.replace("::", "."), this._runtimeDomain.getProperty(c.fromSimpleName(a), !0, !0)) : null; + c.prototype.getDefinition = function(a) { + return(a = s(a)) ? (a = a.replace("::", "."), this._runtimeDomain.getProperty(m.fromSimpleName(a), !0, !0)) : null; }; - e.prototype.hasDefinition = function(a) { - return(a = m(a)) ? (a = a.replace("::", "."), !!this._runtimeDomain.findDomainProperty(c.fromSimpleName(a), !1, !1)) : !1; + c.prototype.hasDefinition = function(a) { + return(a = s(a)) ? (a = a.replace("::", "."), !!this._runtimeDomain.findDomainProperty(m.fromSimpleName(a), !1, !1)) : !1; }; - e.prototype.getQualifiedDefinitionNames = function() { - k("public flash.system.ApplicationDomain::getQualifiedDefinitionNames"); + c.prototype.getQualifiedDefinitionNames = function() { + p("public flash.system.ApplicationDomain::getQualifiedDefinitionNames"); }; - e.classInitializer = null; - e.initializer = null; - e.classSymbols = null; - e.instanceSymbols = null; - return e; - }(g.ASNative); - f.ApplicationDomain = n; - })(f.system || (f.system = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = b.Debug.somewhatImplemented, a = b.ObjectUtilities.toKeyValueArray, c = function(b) { + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.ASNative); + h.ApplicationDomain = t; + })(h.system || (h.system = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.notImplemented, s = c.Debug.dummyConstructor, l = c.AVM2.Runtime.asCoerceString, e = c.Debug.somewhatImplemented, m = c.ObjectUtilities.toKeyValueArray, t = function(a) { function c() { - k("Dummy Constructor: public flash.system.Capabilities"); + s("public flash.system.Capabilities"); } - __extends(c, b); + __extends(c, a); Object.defineProperty(c, "isEmbeddedInAcrobat", {get:function() { - k("public flash.system.Capabilities::get isEmbeddedInAcrobat"); + p("public flash.system.Capabilities::get isEmbeddedInAcrobat"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "hasEmbeddedVideo", {get:function() { - k("public flash.system.Capabilities::get hasEmbeddedVideo"); + p("public flash.system.Capabilities::get hasEmbeddedVideo"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "hasAudio", {get:function() { - k("public flash.system.Capabilities::get hasAudio"); + p("public flash.system.Capabilities::get hasAudio"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "avHardwareDisable", {get:function() { - k("public flash.system.Capabilities::get avHardwareDisable"); + p("public flash.system.Capabilities::get avHardwareDisable"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "hasAccessibility", {get:function() { - d("public flash.system.Capabilities::get hasAccessibility"); + e("public flash.system.Capabilities::get hasAccessibility"); return c._hasAccessibility; }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "hasAudioEncoder", {get:function() { - k("public flash.system.Capabilities::get hasAudioEncoder"); + p("public flash.system.Capabilities::get hasAudioEncoder"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "hasMP3", {get:function() { - k("public flash.system.Capabilities::get hasMP3"); + p("public flash.system.Capabilities::get hasMP3"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "hasPrinting", {get:function() { - k("public flash.system.Capabilities::get hasPrinting"); + p("public flash.system.Capabilities::get hasPrinting"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "hasScreenBroadcast", {get:function() { - k("public flash.system.Capabilities::get hasScreenBroadcast"); + p("public flash.system.Capabilities::get hasScreenBroadcast"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "hasScreenPlayback", {get:function() { - k("public flash.system.Capabilities::get hasScreenPlayback"); + p("public flash.system.Capabilities::get hasScreenPlayback"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "hasStreamingAudio", {get:function() { - k("public flash.system.Capabilities::get hasStreamingAudio"); + p("public flash.system.Capabilities::get hasStreamingAudio"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "hasStreamingVideo", {get:function() { - k("public flash.system.Capabilities::get hasStreamingVideo"); + p("public flash.system.Capabilities::get hasStreamingVideo"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "hasVideoEncoder", {get:function() { - k("public flash.system.Capabilities::get hasVideoEncoder"); + p("public flash.system.Capabilities::get hasVideoEncoder"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "isDebugger", {get:function() { - d("public flash.system.Capabilities::get isDebugger"); + e("public flash.system.Capabilities::get isDebugger"); return c._isDebugger; }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "localFileReadDisable", {get:function() { - k("public flash.system.Capabilities::get localFileReadDisable"); + p("public flash.system.Capabilities::get localFileReadDisable"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "language", {get:function() { - d("public flash.system.Capabilities::get language"); + e("public flash.system.Capabilities::get language"); return c._language; }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "manufacturer", {get:function() { - d("public flash.system.Capabilities::get manufacturer"); + e("public flash.system.Capabilities::get manufacturer"); return c._manufacturer; }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "os", {get:function() { if (null === c._os) { - var a, b = window.navigator.userAgent; - 0 < b.indexOf("Macintosh") ? a = "Mac OS 10.5.2" : 0 < b.indexOf("Windows") ? a = "Windows XP" : 0 < b.indexOf("Linux") ? a = "Linux" : /(iPad|iPhone|iPod|Android)/.test(b) ? a = "iPhone3,1" : k("public flash.system.Capabilities::get os"); + var a, e = window.navigator.userAgent; + 0 < e.indexOf("Macintosh") ? a = "Mac OS 10.5.2" : 0 < e.indexOf("Windows") ? a = "Windows XP" : 0 < e.indexOf("Linux") ? a = "Linux" : /(iPad|iPhone|iPod|Android)/.test(e) ? a = "iPhone3,1" : p("public flash.system.Capabilities::get os"); c._os = a; } return c._os; }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "cpuArchitecture", {get:function() { - k("public flash.system.Capabilities::get cpuArchitecture"); + p("public flash.system.Capabilities::get cpuArchitecture"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "playerType", {get:function() { - d("public flash.system.Capabilities::get playerType"); + e("public flash.system.Capabilities::get playerType"); return c._playerType; }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "serverString", {get:function() { - var b = a({OS:c.os}).map(function(a) { + var a = m({OS:c.os}).map(function(a) { return a[0] + "=" + encodeURIComponent(a[1]); }).join("&"); - d("Capabilities.serverString: " + b); - return b; + e("Capabilities.serverString: " + a); + return a; }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "version", {get:function() { return c._version; }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "screenColor", {get:function() { - k("public flash.system.Capabilities::get screenColor"); + return "color"; }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "pixelAspectRatio", {get:function() { - k("public flash.system.Capabilities::get pixelAspectRatio"); + p("public flash.system.Capabilities::get pixelAspectRatio"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "screenDPI", {get:function() { - k("public flash.system.Capabilities::get screenDPI"); + e("public flash.system.Capabilities::get screenDPI"); + return c._screenDPI; }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "screenResolutionX", {get:function() { - d("public flash.system.Capabilities::get screenResolutionX"); + e("public flash.system.Capabilities::get screenResolutionX"); return window.screen.width; }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "screenResolutionY", {get:function() { - d("public flash.system.Capabilities::get screenResolutionY"); + e("public flash.system.Capabilities::get screenResolutionY"); return window.screen.height; }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "touchscreenType", {get:function() { - k("public flash.system.Capabilities::get touchscreenType"); + p("public flash.system.Capabilities::get touchscreenType"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "hasIME", {get:function() { - k("public flash.system.Capabilities::get hasIME"); + p("public flash.system.Capabilities::get hasIME"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "hasTLS", {get:function() { - k("public flash.system.Capabilities::get hasTLS"); + p("public flash.system.Capabilities::get hasTLS"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "maxLevelIDC", {get:function() { - k("public flash.system.Capabilities::get maxLevelIDC"); + p("public flash.system.Capabilities::get maxLevelIDC"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "supports32BitProcesses", {get:function() { - k("public flash.system.Capabilities::get supports32BitProcesses"); + p("public flash.system.Capabilities::get supports32BitProcesses"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "supports64BitProcesses", {get:function() { - k("public flash.system.Capabilities::get supports64BitProcesses"); + p("public flash.system.Capabilities::get supports64BitProcesses"); }, enumerable:!0, configurable:!0}); Object.defineProperty(c, "_internal", {get:function() { - k("public flash.system.Capabilities::get _internal"); + p("public flash.system.Capabilities::get _internal"); }, enumerable:!0, configurable:!0}); c.hasMultiChannelAudio = function(a) { - m(a); - k("public flash.system.Capabilities::static hasMultiChannelAudio"); + l(a); + p("public flash.system.Capabilities::static hasMultiChannelAudio"); }; c.classInitializer = null; c.initializer = null; @@ -32935,1032 +40296,1296 @@ c._os = null; c._playerType = "PlugIn"; c._version = "SHUMWAY 10,0,0,0"; + c._screenDPI = 96; return c; - }(g.ASNative); - f.Capabilities = c; - })(f.system || (f.system = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = function(a) { - function c() { - k("Dummy Constructor: packageInternal flash.system.FSCommand"); + }(a.ASNative); + h.Capabilities = t; + })(h.system || (h.system = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = c.AVM2.Runtime.asCoerceString, l = function(a) { + function l() { + p("packageInternal flash.system.FSCommand"); } - __extends(c, a); - c._fscommand = function(a, c) { - a = m(a); - c = m(c); - console.log("FSCommand: " + a + "; " + c); + __extends(l, a); + l._fscommand = function(a, e) { + a = s(a); + e = s(e); + console.log("FSCommand: " + a + "; " + e); a = a.toLowerCase(); if ("debugger" === a) { debugger; } else { - b.AVM2.Runtime.AVM2.instance.globals["Shumway.Player.Utils"].executeFSCommand(a, c); + c.AVM2.Runtime.AVM2.instance.globals["Shumway.Player.Utils"].executeFSCommand(a, e); } }; - c.classInitializer = null; - c.initializer = null; - c.classSymbols = null; - c.instanceSymbols = null; - return c; - }(g.ASNative); - f.FSCommand = d; - })(f.system || (f.system = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.system.ImageDecodingPolicy"); - } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.ON_DEMAND = "onDemand"; - a.ON_LOAD = "onLoad"; - return a; - }(g.ASNative); - f.ImageDecodingPolicy = m; - })(f.system || (f.system = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, b, d) { - k("Dummy Constructor: public flash.system.LoaderContext"); - } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.LoaderContext = m; - })(f.system || (f.system = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, b, d, e) { - k("Dummy Constructor: public flash.system.JPEGLoaderContext"); + l.classInitializer = null; + l.initializer = null; + l.classSymbols = null; + l.instanceSymbols = null; + return l; + }(a.ASNative); + h.FSCommand = l; + })(h.system || (h.system = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = function(a) { + function e() { + p("public flash.system.ImageDecodingPolicy"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(f.system.LoaderContext); - g.JPEGLoaderContext = m; - })(f.system || (f.system = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.system.MessageChannel"); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.ON_DEMAND = "onDemand"; + e.ON_LOAD = "onLoad"; + return e; + }(a.ASNative); + h.ImageDecodingPolicy = s; + })(h.system || (h.system = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(h) { + var p = function(a) { + function l(a, l, h) { + void 0 === a && (a = !1); + void 0 === l && (l = null); + void 0 === h && (h = null); + this.checkPolicyFile = a; + this.applicationDomain = l; + this.securityDomain = h; + this.imageDecodingPolicy = c.system.ImageDecodingPolicy.ON_DEMAND; } - __extends(a, b); - Object.defineProperty(a.prototype, "messageAvailable", {get:function() { - k("public flash.system.MessageChannel::get messageAvailable"); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "state", {get:function() { - k("public flash.system.MessageChannel::get state"); - }, enumerable:!0, configurable:!0}); - a.prototype.send = function(a, b) { - k("public flash.system.MessageChannel::send"); - }; - a.prototype.receive = function(a) { - k("public flash.system.MessageChannel::receive"); - }; - a.prototype.close = function() { - k("public flash.system.MessageChannel::close"); - }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(f.events.EventDispatcher); - g.MessageChannel = m; - })(f.system || (f.system = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.system.MessageChannelState"); + __extends(l, a); + l.classInitializer = null; + l.initializer = null; + l.classSymbols = null; + l.instanceSymbols = "checkPolicyFile! applicationDomain! securityDomain! allowCodeImport! requestedContentParent! parameters! imageDecodingPolicy!".split(" "); + return l; + }(a.ASNative); + h.LoaderContext = p; + })(c.system || (c.system = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.dummyConstructor, u = function(a) { + function e(a, e, c, l) { + p("public flash.system.JPEGLoaderContext"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.OPEN = "open"; - a.CLOSING = "closing"; - a.CLOSED = "closed"; - return a; - }(g.ASNative); - f.MessageChannelState = m; - })(f.system || (f.system = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = b.Debug.somewhatImplemented, a = function(a) { - function f() { - k("Dummy Constructor: public flash.system.Security"); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.system.LoaderContext); + h.JPEGLoaderContext = u; + })(a.system || (a.system = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = c.Debug.dummyConstructor, l = function(a) { + function c() { + u("public flash.system.MessageChannel"); } - __extends(f, a); - Object.defineProperty(f, "exactSettings", {get:function() { - return f._exactSettings; - }, set:function(a) { - f._exactSettings = !!a; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(f, "disableAVM1Loading", {get:function() { - k("public flash.system.Security::get disableAVM1Loading"); - }, set:function(a) { - k("public flash.system.Security::set disableAVM1Loading"); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(f, "sandboxType", {get:function() { - d("public flash.system.Security::get sandboxType"); - return f._sandboxType; + __extends(c, a); + Object.defineProperty(c.prototype, "messageAvailable", {get:function() { + p("public flash.system.MessageChannel::get messageAvailable"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(f, "pageDomain", {get:function() { - d("public flash.system.Security::get pageDomain"); - var a = b.FileLoadingService.instance.resolveUrl("/").split("/"); - a.pop(); - return a.pop(); + Object.defineProperty(c.prototype, "state", {get:function() { + p("public flash.system.MessageChannel::get state"); }, enumerable:!0, configurable:!0}); - f.allowDomain = function() { - d('public flash.system.Security::static allowDomain ["' + Array.prototype.join.call(arguments, '", "') + '"]'); + c.prototype.send = function(a, e) { + p("public flash.system.MessageChannel::send"); }; - f.allowInsecureDomain = function() { - d("public flash.system.Security::static allowInsecureDomain"); + c.prototype.receive = function(a) { + p("public flash.system.MessageChannel::receive"); }; - f.loadPolicyFile = function(a) { - m(a); - d("public flash.system.Security::static loadPolicyFile"); - }; - f.showSettings = function(a) { - "undefined" === typeof a && (a = "default"); - m(a); - k("public flash.system.Security::static showSettings"); + c.prototype.close = function() { + p("public flash.system.MessageChannel::close"); }; - f.duplicateSandboxBridgeInputArguments = function(a, b) { - k("public flash.system.Security::static duplicateSandboxBridgeInputArguments"); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.events.EventDispatcher); + h.MessageChannel = l; + })(a.system || (a.system = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = function(a) { + function e() { + p("public flash.system.MessageChannelState"); + } + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.OPEN = "open"; + e.CLOSING = "closing"; + e.CLOSED = "closed"; + return e; + }(a.ASNative); + h.MessageChannelState = s; + })(h.system || (h.system = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.notImplemented, s = c.Debug.dummyConstructor, l = c.AVM2.Runtime.asCoerceString, e = c.Debug.somewhatImplemented, m = function(a) { + function h() { + s("public flash.system.Security"); + } + __extends(h, a); + Object.defineProperty(h, "exactSettings", {get:function() { + return h._exactSettings; + }, set:function(a) { + h._exactSettings = !!a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h, "disableAVM1Loading", {get:function() { + p("public flash.system.Security::get disableAVM1Loading"); + }, set:function(a) { + p("public flash.system.Security::set disableAVM1Loading"); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h, "sandboxType", {get:function() { + e("public flash.system.Security::get sandboxType"); + return h._sandboxType; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h, "pageDomain", {get:function() { + e("public flash.system.Security::get pageDomain"); + var a = c.FileLoadingService.instance.resolveUrl("/").split("/"); + a.pop(); + return a.pop(); + }, enumerable:!0, configurable:!0}); + h.allowDomain = function() { + e('public flash.system.Security::static allowDomain ["' + Array.prototype.join.call(arguments, '", "') + '"]'); }; - f.duplicateSandboxBridgeOutputArgument = function(a, b) { - k("public flash.system.Security::static duplicateSandboxBridgeOutputArgument"); + h.allowInsecureDomain = function() { + e("public flash.system.Security::static allowInsecureDomain"); }; - f.classInitializer = null; - f.initializer = null; - f.classSymbols = null; - f.instanceSymbols = null; - f.REMOTE = "remote"; - f.LOCAL_WITH_FILE = "localWithFile"; - f.LOCAL_WITH_NETWORK = "localWithNetwork"; - f.LOCAL_TRUSTED = "localTrusted"; - f.APPLICATION = "application"; - f._exactSettings = !1; - f._sandboxType = "remote"; - return f; - }(g.ASNative); - f.Security = a; - })(f.system || (f.system = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.system.SecurityDomain"); + h.loadPolicyFile = function(a) { + l(a); + e("public flash.system.Security::static loadPolicyFile"); + }; + h.showSettings = function(a) { + void 0 === a && (a = "default"); + l(a); + p("public flash.system.Security::static showSettings"); + }; + h.duplicateSandboxBridgeInputArguments = function(a, e) { + p("public flash.system.Security::static duplicateSandboxBridgeInputArguments"); + }; + h.duplicateSandboxBridgeOutputArgument = function(a, e) { + p("public flash.system.Security::static duplicateSandboxBridgeOutputArgument"); + }; + h.classInitializer = null; + h.initializer = null; + h.classSymbols = null; + h.instanceSymbols = null; + h.REMOTE = "remote"; + h.LOCAL_WITH_FILE = "localWithFile"; + h.LOCAL_WITH_NETWORK = "localWithNetwork"; + h.LOCAL_TRUSTED = "localTrusted"; + h.APPLICATION = "application"; + h._exactSettings = !1; + h._sandboxType = "remote"; + return h; + }(a.ASNative); + h.Security = m; + })(h.system || (h.system = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.somewhatImplemented, s = function(a) { + function e() { } - __extends(a, b); - Object.defineProperty(a.prototype, "currentDomain", {get:function() { - k("public flash.system.SecurityDomain::get currentDomain"); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "domainID", {get:function() { - k("public flash.system.SecurityDomain::get domainID"); + __extends(e, a); + Object.defineProperty(e, "currentDomain", {get:function() { + this._currentDomain || (this._currentDomain = new h.SecurityDomain); + p("public flash.system.SecurityDomain::get currentDomain"); + return this._currentDomain; }, enumerable:!0, configurable:!0}); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.SecurityDomain = m; - })(f.system || (f.system = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.system.SecurityPanel"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.ASNative); + h.SecurityDomain = s; + })(h.system || (h.system = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = function(a) { + function e() { + p("public flash.system.SecurityPanel"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.DEFAULT = "default"; - a.PRIVACY = "privacy"; - a.LOCAL_STORAGE = "localStorage"; - a.MICROPHONE = "microphone"; - a.CAMERA = "camera"; - a.DISPLAY = "display"; - a.SETTINGS_MANAGER = "settingsManager"; - return a; - }(g.ASNative); - f.SecurityPanel = m; - })(f.system || (f.system = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.system.TouchscreenType"); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.DEFAULT = "default"; + e.PRIVACY = "privacy"; + e.LOCAL_STORAGE = "localStorage"; + e.MICROPHONE = "microphone"; + e.CAMERA = "camera"; + e.DISPLAY = "display"; + e.SETTINGS_MANAGER = "settingsManager"; + return e; + }(a.ASNative); + h.SecurityPanel = s; + })(h.system || (h.system = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = function(a) { + function e() { + p("public flash.system.TouchscreenType"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.FINGER = "finger"; - a.STYLUS = "stylus"; - a.NONE = "none"; - return a; - }(g.ASNative); - f.TouchscreenType = m; - })(f.system || (f.system = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.FINGER = "finger"; + e.STYLUS = "stylus"; + e.NONE = "none"; + return e; + }(a.ASNative); + h.TouchscreenType = s; + })(h.system || (h.system = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { - function d() { - b.call(this); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function c() { + a.call(this); } - __extends(d, b); - d.fromNumber = function(a) { + __extends(c, a); + c.fromNumber = function(a) { switch(a) { case 1: - return d.NORMAL; + return c.NORMAL; case 2: - return d.ADVANCED; + return c.ADVANCED; default: return null; } }; - d.toNumber = function(a) { + c.toNumber = function(a) { switch(a) { - case d.NORMAL: + case c.NORMAL: return 1; - case d.ADVANCED: + case c.ADVANCED: return 2; default: return-1; } }; - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - d.NORMAL = "normal"; - d.ADVANCED = "advanced"; - return d; - }(b.ASNative); - f.AntiAliasType = k; - })(f.text || (f.text = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.NORMAL = "normal"; + c.ADVANCED = "advanced"; + return c; + }(a.ASNative); + c.AntiAliasType = h; + })(c.text || (c.text = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { - function d() { - b.call(this); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function c() { + a.call(this); } - __extends(d, b); - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - d.REGULAR = "regular"; - d.BOLD = "bold"; - d.ITALIC = "italic"; - d.BOLD_ITALIC = "boldItalic"; - return d; - }(b.ASNative); - f.FontStyle = k; - })(f.text || (f.text = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + __extends(c, a); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.REGULAR = "regular"; + c.BOLD = "bold"; + c.ITALIC = "italic"; + c.BOLD_ITALIC = "boldItalic"; + return c; + }(a.ASNative); + c.FontStyle = h; + })(c.text || (c.text = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { - function d() { - b.call(this); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function c() { + a.call(this); } - __extends(d, b); - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - d.EMBEDDED = "embedded"; - d.EMBEDDED_CFF = "embeddedCFF"; - d.DEVICE = "device"; - return d; - }(b.ASNative); - f.FontType = k; - })(f.text || (f.text = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(k) { - var s = b.Debug.somewhatImplemented, m = b.AVM2.Runtime.asCoerceString, d = f.text.FontStyle, a = f.text.FontType, c = function(c) { - function g() { - } - __extends(g, c); - g._getFontMetrics = function(a) { - if (!this._deviceFontMetrics) { - var b = self.navigator.userAgent; - -1 < b.indexOf("Windows") ? this._deviceFontMetrics = g.DEVICE_FONT_METRICS_WIN : this._deviceFontMetrics = /(Macintosh|iPad|iPhone|iPod|Android)/.test(b) ? this.DEVICE_FONT_METRICS_MAC : this.DEVICE_FONT_METRICS_LINUX; - } - return this._deviceFontMetrics[g.resolveFontName(a)]; + __extends(c, a); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.EMBEDDED = "embedded"; + c.EMBEDDED_CFF = "embeddedCFF"; + c.DEVICE = "device"; + return c; + }(a.ASNative); + c.FontType = h; + })(c.text || (c.text = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(v) { + var p = c.Debug.somewhatImplemented, u = c.AVM2.Runtime.asCoerceString, l = h.text.FontStyle, e = h.text.FontType, m = function(a) { + function m() { + } + __extends(m, a); + m._getFontMetrics = function(a, e) { + return this._deviceFontMetrics[a + e] || this._deviceFontMetrics[a]; }; - g.resolveFontName = function(a) { - return "_sans" === a ? "sans-serif" : "_serif" === a ? "serif" : "_typewriter" === a ? "monospace" : a; + m.resolveFontName = function(a) { + return "_sans" === a ? m.DEFAULT_FONT_SANS : "_serif" === a ? m.DEFAULT_FONT_SERIF : "_typewriter" === a ? m.DEFAULT_FONT_TYPEWRITER : a; }; - g.getBySymbolId = function(a) { + m.getBySymbolId = function(a) { return this._fontsBySymbolId[a]; }; - g.getByName = function(b) { - b = b.toLowerCase(); - var c = this._fontsByName[b]; - c || (c = new g, c._fontName = b, c._fontStyle = d.REGULAR, c._fontType = a.DEVICE, this._fontsByName[b] = c); - c._fontType === a.DEVICE && (b = g._getFontMetrics(b)) && (c.ascent = b[0], c.descent = b[1], c.leading = b[2]); - return c; - }; - g.getDefaultFont = function() { - return g.getByName("times roman"); + m.getByNameAndStyle = function(a, f) { + for (var d, b, g = a.split(","), l = 0;l < g.length && !b;l++) { + d = g[l].toLowerCase() + f, b = this._fontsByName[d]; + } + b || (b = new m, b._fontName = g[0], b._fontFamily = m.resolveFontName(g[0].toLowerCase()), b._fontStyle = f, b._fontType = e.DEVICE, this._fontsByName[d] = b); + b._fontType === e.DEVICE && (d = m._getFontMetrics(b._fontName, b._fontStyle), d || (c.Debug.warning('Font metrics for "' + b._fontName + '" unknown. Fallback to default.'), d = m._getFontMetrics(m.DEFAULT_FONT_SANS, b._fontStyle), b._fontFamily = m.DEFAULT_FONT_SANS), b.ascent = d[0], b.descent = d[1], b.leading = d[2]); + return b; }; - g.enumerateFonts = function(a) { - s("public flash.text.Font::static enumerateFonts"); - return g._fonts.slice(); + m.getDefaultFont = function() { + return m.getByNameAndStyle(m.DEFAULT_FONT_SANS, l.REGULAR); }; - g.registerFont = function(a) { - s("Font.registerFont"); + m.enumerateFonts = function(a) { + p("public flash.text.Font::static enumerateFonts"); + return m._fonts.slice(); + }; + m.registerFont = function(a) { + p("Font.registerFont"); + }; + m.registerEmbeddedFont = function(a, e) { + var d = h.display.DisplayObject.getNextSyncID(), b = {get:m.resolveEmbeddedFont.bind(m, e, a.id, d), configurable:!0}; + Object.defineProperty(m._fontsByName, a.name.toLowerCase() + a.style, b); + Object.defineProperty(m._fontsByName, "swffont" + d + a.style, b); + Object.defineProperty(m._fontsBySymbolId, d + "", b); + }; + m.resolveEmbeddedFont = function(a, e, d) { + a.getSymbolById(e).syncId = d; + return m._fontsBySymbolId[e]; }; - Object.defineProperty(g.prototype, "fontName", {get:function() { + Object.defineProperty(m.prototype, "fontName", {get:function() { return this._fontName; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "fontStyle", {get:function() { + Object.defineProperty(m.prototype, "fontStyle", {get:function() { return this._fontStyle; }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "fontType", {get:function() { + Object.defineProperty(m.prototype, "fontType", {get:function() { return this._fontType; }, enumerable:!0, configurable:!0}); - g.prototype.hasGlyphs = function(a) { - m(a); - s("Font#hasGlyphs"); + m.prototype.hasGlyphs = function(a) { + u(a); + p("Font#hasGlyphs"); return!0; }; - g.classInitializer = function() { - g._fonts = []; - g._fontsBySymbolId = b.ObjectUtilities.createMap(); - g._fontsByName = b.ObjectUtilities.createMap(); - g.DEVICE_FONT_METRICS_WIN = {serif:[1, .25, 0], "sans-serif":[1, .25, 0], monospace:[1, .25, 0], "birch std":[.9167, .25, 0], "blackoak std":[1, .3333, 0], "chaparral pro":[.8333, .3333, 0], "chaparral pro light":[.8333, .3333, 0], "charlemagne std":[.9167, .25, 0], "cooper std black":[.9167, .25, 0], "giddyup std":[.8333, .3333, 0], "hobo std":[1.0833, .3333, 0], "kozuka gothic pro b":[1, .4167, 0], "kozuka gothic pro el":[1.0833, .25, 0], "kozuka gothic pro h":[1, .4167, 0], "kozuka gothic pro l":[1, - .3333, 0], "kozuka gothic pro m":[1.0833, .3333, 0], "kozuka gothic pro r":[1, .3333, 0], "kozuka mincho pro b":[1.0833, .25, 0], "kozuka mincho pro el":[1.0833, .25, 0], "kozuka mincho pro h":[1.1667, .25, 0], "kozuka mincho pro l":[1.0833, .25, 0], "kozuka mincho pro m":[1.0833, .25, 0], "kozuka mincho pro r":[1.0833, .25, 0], "mesquite std":[.9167, .25, 0], "minion pro cond":[1, .3333, 0], "minion pro med":[1, .3333, 0], "minion pro smbd":[1, .3333, 0], "myriad arabic":[1, .4167, - 0], "nueva std":[.75, .25, 0], "nueva std cond":[.75, .25, 0], "ocr a std":[.8333, .25, 0], "orator std":[1.0833, .25, 0], "poplar std":[.9167, .25, 0], "prestige elite std":[.9167, .25, 0], "rosewood std regular":[.8333, .3333, 0], "stencil std":[1, .3333, 0], "trajan pro":[1, .25, 0], "kozuka gothic pr6n b":[1.4167, .4167, 0], "kozuka gothic pr6n el":[1.4167, .3333, 0], "kozuka gothic pr6n h":[1.4167, .4167, 0], "kozuka gothic pr6n l":[1.4167, .3333, 0], "kozuka gothic pr6n m":[1.5, - .3333, 0], "kozuka gothic pr6n r":[1.4167, .3333, 0], "kozuka mincho pr6n b":[1.3333, .3333, 0], "kozuka mincho pr6n el":[1.3333, .3333, 0], "kozuka mincho pr6n h":[1.4167, .3333, 0], "kozuka mincho pr6n l":[1.3333, .3333, 0], "kozuka mincho pr6n m":[1.3333, .3333, 0], "kozuka mincho pr6n r":[1.3333, .3333, 0], "letter gothic std":[1, .25, 0], "minion pro":[1, .3333, 0], "myriad hebrew":[.8333, .3333, 0], "myriad pro":[.9167, .25, 0], "myriad pro cond":[.9167, .25, 0], "myriad pro light":[1, - .25, 0], marlett:[1, 0, 0], arial:[1, .25, 0], "arabic transparent":[1, .25, 0], "arial baltic":[1, .25, 0], "arial ce":[1, .25, 0], "arial cyr":[1, .25, 0], "arial greek":[1, .25, 0], "arial tur":[1, .25, 0], batang:[.8333, .1667, 0], batangche:[.8333, .1667, 0], gungsuh:[.8333, .1667, 0], gungsuhche:[.8333, .1667, 0], "courier new":[1, .25, 0], "courier new baltic":[1, .25, 0], "courier new ce":[1, .25, 0], "courier new cyr":[1, .25, 0], "courier new greek":[1, .25, 0], "courier new tur":[1, - .25, 0], daunpenh:[.6667, .6667, 0], dokchampa:[1.4167, .5833, 0], "estrangelo edessa":[.75, .3333, 0], euphemia:[1.0833, .3333, 0], gautami:[1.1667, .8333, 0], vani:[1.0833, .75, 0], gulim:[.8333, .1667, 0], gulimche:[.8333, .1667, 0], dotum:[.8333, .1667, 0], dotumche:[.8333, .1667, 0], impact:[1.0833, .25, 0], "iskoola pota":[1, .3333, 0], kalinga:[1.0833, .5, 0], kartika:[1, .4167, 0], "khmer ui":[1.0833, .3333, 0], "lao ui":[1, .25, 0], latha:[1.0833, .4167, 0], "lucida console":[.75, - .25, 0], "malgun gothic":[1, .25, 0], mangal:[1.0833, .3333, 0], meiryo:[1.0833, .4167, 0], "meiryo ui":[1, .25, 0], "microsoft himalaya":[.5833, .4167, 0], "microsoft jhenghei":[1, .3333, 0], "microsoft yahei":[1.0833, .3333, 0], mingliu:[.8333, .1667, 0], pmingliu:[.8333, .1667, 0], mingliu_hkscs:[.8333, .1667, 0], "mingliu-extb":[.8333, .1667, 0], "pmingliu-extb":[.8333, .1667, 0], "mingliu_hkscs-extb":[.8333, .1667, 0], "mongolian baiti":[.8333, .25, 0], "ms gothic":[.8333, .1667, - 0], "ms pgothic":[.8333, .1667, 0], "ms ui gothic":[.8333, .1667, 0], "ms mincho":[.8333, .1667, 0], "ms pmincho":[.8333, .1667, 0], "mv boli":[1.1667, .25, 0], "microsoft new tai lue":[1, .4167, 0], nyala:[.9167, .3333, 0], "microsoft phagspa":[1.0833, .25, 0], "plantagenet cherokee":[1, .4167, 0], raavi:[1.0833, .6667, 0], "segoe script":[1.0833, .5, 0], "segoe ui":[1, .25, 0], "segoe ui semibold":[1, .25, 0], "segoe ui light":[1, .25, 0], "segoe ui symbol":[1, .25, 0], shruti:[1.0833, - .5, 0], simsun:[.8333, .1667, 0], nsimsun:[.8333, .1667, 0], "simsun-extb":[.8333, .1667, 0], sylfaen:[1, .3333, 0], "microsoft tai le":[1, .3333, 0], "times new roman":[1, .25, 0], "times new roman baltic":[1, .25, 0], "times new roman ce":[1, .25, 0], "times new roman cyr":[1, .25, 0], "times new roman greek":[1, .25, 0], "times new roman tur":[1, .25, 0], tunga:[1.0833, .75, 0], vrinda:[1, .4167, 0], "shonar bangla":[.8333, .5, 0], "microsoft yi baiti":[.8333, .1667, 0], tahoma:[1, - .1667, 0], "microsoft sans serif":[1.0833, .1667, 0], "angsana new":[.9167, .4167, 0], aparajita:[.75, .4167, 0], "cordia new":[.9167, .5, 0], ebrima:[1.0833, .5, 0], gisha:[.9167, .25, 0], kokila:[.8333, .3333, 0], leelawadee:[.9167, .25, 0], "microsoft uighur":[1.0833, .5, 0], moolboran:[.6667, .6667, 0], symbol:[1, .25, 0], utsaah:[.8333, .4167, 0], vijaya:[1.0833, .25, 0], wingdings:[.9167, .25, 0], andalus:[1.3333, .4167, 0], "arabic typesetting":[.8333, .5, 0], "simplified arabic":[1.3333, - .5, 0], "simplified arabic fixed":[1, .4167, 0], "sakkal majalla":[.9167, .5, 0], "traditional arabic":[1.3333, .5, 0], aharoni:[.75, .25, 0], david:[.75, .25, 0], frankruehl:[.75, .25, 0], fangsong:[.8333, .1667, 0], simhei:[.8333, .1667, 0], kaiti:[.8333, .1667, 0], "browallia new":[.8333, .4167, 0], "lucida sans unicode":[1.0833, .25, 0], "arial black":[1.0833, .3333, 0], calibri:[.9167, .25, 0], cambria:[.9167, .25, 0], "cambria math":[3.0833, 2.5, 0], candara:[.9167, .25, 0], "comic sans ms":[1.0833, - .3333, 0], consolas:[.9167, .25, 0], constantia:[.9167, .25, 0], corbel:[.9167, .25, 0], "franklin gothic medium":[1, .3333, 0], gabriola:[1.1667, .6667, 0], georgia:[1, .25, 0], "palatino linotype":[1.0833, .3333, 0], "segoe print":[1.25, .5, 0], "trebuchet ms":[1.0833, .4167, 0], verdana:[1, .1667, 0], webdings:[1.0833, .5, 0], "lucida bright":[.9167, .25, 0], "lucida sans":[.9167, .25, 0], "lucida sans typewriter":[.9167, .25, 0], "gentium basic":[.8333, .25, 0], "dejavu serif condensed":[.9167, - .25, 0], arimo:[1, .25, 0], "dejavu sans condensed":[.9167, .25, 0], "dejavu sans":[.9167, .25, 0], "dejavu sans light":[.9167, .25, 0], opensymbol:[.8333, .1667, 0], "gentium book basic":[.8333, .25, 0], "dejavu sans mono":[.9167, .25, 0], "dejavu serif":[.9167, .25, 0], "calibri light":[.9167, .25, 0]}; - g.DEVICE_FONT_METRICS_MAC = {"al bayan plain":[1, .5, 0], "al bayan bold":[1, .5833, 0], "american typewriter":[.9167, .25, 0], "american typewriter bold":[.9167, .25, 0], "american typewriter condensed":[.9167, .25, 0], "american typewriter condensed bold":[.9167, .25, 0], "american typewriter condensed light":[.8333, .25, 0], "american typewriter light":[.9167, .25, 0], "andale mono":[.9167, .25, 0], "apple symbols":[.6667, .25, 0], "arial bold italic":[.9167, .25, 0], "arial bold":[.9167, - .25, 0], "arial italic":[.9167, .25, 0], "arial hebrew":[.75, .3333, 0], "arial hebrew bold":[.75, .3333, 0], arial:[.9167, .25, 0], "arial narrow":[.9167, .25, 0], "arial narrow bold":[.9167, .25, 0], "arial narrow bold italic":[.9167, .25, 0], "arial narrow italic":[.9167, .25, 0], "arial rounded mt bold":[.9167, .25, 0], "arial unicode ms":[1.0833, .25, 0], "avenir black":[1, .3333, 0], "avenir black oblique":[1, .3333, 0], "avenir book":[1, .3333, 0], "avenir book oblique":[1, .3333, - 0], "avenir heavy":[1, .3333, 0], "avenir heavy oblique":[1, .3333, 0], "avenir light":[1, .3333, 0], "avenir light oblique":[1, .3333, 0], "avenir medium":[1, .3333, 0], "avenir medium oblique":[1, .3333, 0], "avenir oblique":[1, .3333, 0], "avenir roman":[1, .3333, 0], "avenir next bold":[1, .3333, 0], "avenir next bold italic":[1, .3333, 0], "avenir next demi bold":[1, .3333, 0], "avenir next demi bold italic":[1, .3333, 0], "avenir next heavy":[1, .3333, 0], "avenir next heavy italic":[1, - .3333, 0], "avenir next italic":[1, .3333, 0], "avenir next medium":[1, .3333, 0], "avenir next medium italic":[1, .3333, 0], "avenir next regular":[1, .3333, 0], "avenir next ultra light":[1, .3333, 0], "avenir next ultra light italic":[1, .3333, 0], "avenir next condensed bold":[1, .3333, 0], "avenir next condensed bold italic":[1, .3333, 0], "avenir next condensed demi bold":[1, .3333, 0], "avenir next condensed demi bold italic":[1, .3333, 0], "avenir next condensed heavy":[1, .3333, - 0], "avenir next condensed heavy italic":[1, .3333, 0], "avenir next condensed italic":[1, .3333, 0], "avenir next condensed medium":[1, .3333, 0], "avenir next condensed medium italic":[1, .3333, 0], "avenir next condensed regular":[1, .3333, 0], "avenir next condensed ultra light":[1, .3333, 0], "avenir next condensed ultra light italic":[1, .3333, 0], ayuthaya:[1.0833, .3333, 0], baghdad:[.9167, .4167, 0], "bangla mn":[.9167, .6667, 0], "bangla mn bold":[.9167, .6667, 0], "bangla sangam mn":[.9167, - .4167, 0], "bangla sangam mn bold":[.9167, .4167, 0], baskerville:[.9167, .25, 0], "baskerville bold":[.9167, .25, 0], "baskerville bold italic":[.9167, .25, 0], "baskerville italic":[.9167, .25, 0], "baskerville semibold":[.9167, .25, 0], "baskerville semibold italic":[.9167, .25, 0], "big caslon medium":[.9167, .25, 0], "brush script mt italic":[.9167, .3333, 0], chalkboard:[1, .25, 0], "chalkboard bold":[1, .25, 0], "chalkboard se bold":[1.1667, .25, 0], "chalkboard se light":[1.1667, - .25, 0], "chalkboard se regular":[1.1667, .25, 0], chalkduster:[1, .25, 0], "charcoal cy":[1, .25, 0], cochin:[.9167, .25, 0], "cochin bold":[.9167, .25, 0], "cochin bold italic":[.9167, .25, 0], "cochin italic":[.9167, .25, 0], "comic sans ms":[1.0833, .25, 0], "comic sans ms bold":[1.0833, .25, 0], copperplate:[.75, .25, 0], "copperplate bold":[.75, .25, 0], "copperplate light":[.75, .25, 0], "corsiva hebrew":[.6667, .3333, 0], "corsiva hebrew bold":[.6667, .3333, 0], courier:[.75, - .25, 0], "courier bold":[.75, .25, 0], "courier bold oblique":[.75, .25, 0], "courier oblique":[.75, .25, 0], "courier new bold italic":[.8333, .3333, 0], "courier new bold":[.8333, .3333, 0], "courier new italic":[.8333, .3333, 0], "courier new":[.8333, .3333, 0], biaukai:[.8333, .1667, 0], damascus:[.5833, .4167, 0], "damascus bold":[.5833, .4167, 0], "decotype naskh":[1.1667, .6667, 0], "devanagari mt":[.9167, .6667, 0], "devanagari mt bold":[.9167, .6667, 0], "devanagari sangam mn":[.9167, - .4167, 0], "devanagari sangam mn bold":[.9167, .4167, 0], didot:[.9167, .3333, 0], "didot bold":[1, .3333, 0], "didot italic":[.9167, .25, 0], "euphemia ucas":[1.0833, .25, 0], "euphemia ucas bold":[1.0833, .25, 0], "euphemia ucas italic":[1.0833, .25, 0], "futura condensed extrabold":[1, .25, 0], "futura condensed medium":[1, .25, 0], "futura medium":[1, .25, 0], "futura medium italic":[1, .25, 0], "gb18030 bitmap":[1, .6667, 0], "geeza pro":[.9167, .3333, 0], "geeza pro bold":[.9167, - .3333, 0], geneva:[1, .25, 0], "geneva cy":[1, .25, 0], georgia:[.9167, .25, 0], "georgia bold":[.9167, .25, 0], "georgia bold italic":[.9167, .25, 0], "georgia italic":[.9167, .25, 0], "gill sans":[.9167, .25, 0], "gill sans bold":[.9167, .25, 0], "gill sans bold italic":[.9167, .25, 0], "gill sans italic":[.9167, .25, 0], "gill sans light":[.9167, .25, 0], "gill sans light italic":[.9167, .25, 0], "gujarati mt":[.9167, .6667, 0], "gujarati mt bold":[.9167, .6667, 0], "gujarati sangam mn":[.8333, - .4167, 0], "gujarati sangam mn bold":[.8333, .4167, 0], "gurmukhi mn":[.9167, .25, 0], "gurmukhi mn bold":[.9167, .25, 0], "gurmukhi sangam mn":[.9167, .3333, 0], "gurmukhi sangam mn bold":[.9167, .3333, 0], helvetica:[.75, .25, 0], "helvetica bold":[.75, .25, 0], "helvetica bold oblique":[.75, .25, 0], "helvetica light":[.75, .25, 0], "helvetica light oblique":[.75, .25, 0], "helvetica oblique":[.75, .25, 0], "helvetica neue":[.9167, .25, 0], "helvetica neue bold":[1, .25, 0], "helvetica neue bold italic":[1, - .25, 0], "helvetica neue condensed black":[1, .25, 0], "helvetica neue condensed bold":[1, .25, 0], "helvetica neue italic":[.9167, .25, 0], "helvetica neue light":[1, .25, 0], "helvetica neue light italic":[.9167, .25, 0], "helvetica neue medium":[1, .25, 0], "helvetica neue ultralight":[.9167, .25, 0], "helvetica neue ultralight italic":[.9167, .25, 0], herculanum:[.8333, .1667, 0], "hiragino kaku gothic pro w3":[.9167, .0833, 0], "hiragino kaku gothic pro w6":[.9167, .0833, 0], "hiragino kaku gothic pron w3":[.9167, - .0833, 0], "hiragino kaku gothic pron w6":[.9167, .0833, 0], "hiragino kaku gothic std w8":[.9167, .0833, 0], "hiragino kaku gothic stdn w8":[.9167, .0833, 0], "hiragino maru gothic pro w4":[.9167, .0833, 0], "hiragino maru gothic pron w4":[.9167, .0833, 0], "hiragino mincho pro w3":[.9167, .0833, 0], "hiragino mincho pro w6":[.9167, .0833, 0], "hiragino mincho pron w3":[.9167, .0833, 0], "hiragino mincho pron w6":[.9167, .0833, 0], "hiragino sans gb w3":[.9167, .0833, 0], "hiragino sans gb w6":[.9167, - .0833, 0], "hoefler text black":[.75, .25, 0], "hoefler text black italic":[.75, .25, 0], "hoefler text italic":[.75, .25, 0], "hoefler text ornaments":[.8333, .1667, 0], "hoefler text":[.75, .25, 0], impact:[1, .25, 0], inaimathi:[.8333, .4167, 0], "headlinea regular":[.8333, .1667, 0], "pilgi regular":[.8333, .25, 0], "gungseo regular":[.8333, .25, 0], "pcmyungjo regular":[.8333, .25, 0], "kailasa regular":[1.0833, .5833, 0], "kannada mn":[.9167, .25, 0], "kannada mn bold":[.9167, - .25, 0], "kannada sangam mn":[1, .5833, 0], "kannada sangam mn bold":[1, .5833, 0], "kefa bold":[.9167, .25, 0], "kefa regular":[.9167, .25, 0], "khmer mn":[1, .6667, 0], "khmer mn bold":[1, .6667, 0], "khmer sangam mn":[1.0833, .6667, 0], "kokonor regular":[1.0833, .5833, 0], krungthep:[1, .25, 0], kufistandardgk:[.9167, .5, 0], "lao mn":[.9167, .4167, 0], "lao mn bold":[.9167, .4167, 0], "lao sangam mn":[1, .3333, 0], "apple ligothic medium":[.8333, .1667, 0], "lihei pro":[.8333, - .1667, 0], "lisong pro":[.8333, .1667, 0], "lucida grande":[1, .25, 0], "lucida grande bold":[1, .25, 0], "malayalam mn":[1, .4167, 0], "malayalam mn bold":[1, .4167, 0], "malayalam sangam mn":[.8333, .4167, 0], "malayalam sangam mn bold":[.8333, .4167, 0], "marion bold":[.6667, .3333, 0], "marion italic":[.6667, .3333, 0], "marion regular":[.6667, .3333, 0], "marker felt thin":[.8333, .25, 0], "marker felt wide":[.9167, .25, 0], "menlo bold":[.9167, .25, 0], "menlo bold italic":[.9167, - .25, 0], "menlo italic":[.9167, .25, 0], "menlo regular":[.9167, .25, 0], "microsoft sans serif":[.9167, .25, 0], monaco:[1, .25, 0], "gurmukhi mt":[.8333, .4167, 0], mshtakan:[.9167, .25, 0], "mshtakan bold":[.9167, .25, 0], "mshtakan boldoblique":[.9167, .25, 0], "mshtakan oblique":[.9167, .25, 0], "myanmar mn":[1, .4167, 0], "myanmar mn bold":[1, .4167, 0], "myanmar sangam mn":[.9167, .4167, 0], nadeem:[.9167, .4167, 0], "nanum brush script":[.9167, .25, 0], nanumgothic:[.9167, .25, - 0], "nanumgothic bold":[.9167, .25, 0], "nanumgothic extrabold":[.9167, .25, 0], nanummyeongjo:[.9167, .25, 0], "nanummyeongjo bold":[.9167, .25, 0], "nanummyeongjo extrabold":[.9167, .25, 0], "nanum pen script":[.9167, .25, 0], "optima bold":[.9167, .25, 0], "optima bold italic":[.9167, .25, 0], "optima extrablack":[1, .25, 0], "optima italic":[.9167, .25, 0], "optima regular":[.9167, .25, 0], "oriya mn":[.9167, .25, 0], "oriya mn bold":[.9167, .25, 0], "oriya sangam mn":[.8333, .4167, - 0], "oriya sangam mn bold":[.8333, .4167, 0], osaka:[1, .25, 0], "osaka-mono":[.8333, .1667, 0], "palatino bold":[.8333, .25, 0], "palatino bold italic":[.8333, .25, 0], "palatino italic":[.8333, .25, 0], palatino:[.8333, .25, 0], papyrus:[.9167, .5833, 0], "papyrus condensed":[.9167, .5833, 0], "plantagenet cherokee":[.6667, .25, 0], raanana:[.75, .25, 0], "raanana bold":[.75, .25, 0], "hei regular":[.8333, .1667, 0], "kai regular":[.8333, .1667, 0], stfangsong:[.8333, .1667, 0], stheiti:[.8333, - .1667, 0], "heiti sc light":[.8333, .1667, 0], "heiti sc medium":[.8333, .1667, 0], "heiti tc light":[.8333, .1667, 0], "heiti tc medium":[.8333, .1667, 0], stkaiti:[.8333, .1667, 0], "kaiti sc black":[1.0833, .3333, 0], "kaiti sc bold":[1.0833, .3333, 0], "kaiti sc regular":[1.0833, .3333, 0], stsong:[.8333, .1667, 0], "songti sc black":[1.0833, .3333, 0], "songti sc bold":[1.0833, .3333, 0], "songti sc light":[1.0833, .3333, 0], "songti sc regular":[1.0833, .3333, 0], stxihei:[.8333, - .1667, 0], sathu:[.9167, .3333, 0], silom:[1, .3333, 0], "sinhala mn":[.9167, .25, 0], "sinhala mn bold":[.9167, .25, 0], "sinhala sangam mn":[1.1667, .3333, 0], "sinhala sangam mn bold":[1.1667, .3333, 0], "skia regular":[.75, .25, 0], symbol:[.6667, .3333, 0], "tahoma negreta":[1, .1667, 0], "tamil mn":[.9167, .25, 0], "tamil mn bold":[.9167, .25, 0], "tamil sangam mn":[.75, .25, 0], "tamil sangam mn bold":[.75, .25, 0], "telugu mn":[.9167, .25, 0], "telugu mn bold":[.9167, .25, 0], - "telugu sangam mn":[1, .5833, 0], "telugu sangam mn bold":[1, .5833, 0], thonburi:[1.0833, .25, 0], "thonburi bold":[1.0833, .25, 0], "times bold":[.75, .25, 0], "times bold italic":[.75, .25, 0], "times italic":[.75, .25, 0], "times roman":[.75, .25, 0], "times new roman bold italic":[.9167, .25, 0], "times new roman bold":[.9167, .25, 0], "times new roman italic":[.9167, .25, 0], "times new roman":[.9167, .25, 0], "trebuchet ms bold italic":[.9167, .25, 0], "trebuchet ms":[.9167, - .25, 0], "trebuchet ms bold":[.9167, .25, 0], "trebuchet ms italic":[.9167, .25, 0], verdana:[1, .25, 0], "verdana bold":[1, .25, 0], "verdana bold italic":[1, .25, 0], "verdana italic":[1, .25, 0], webdings:[.8333, .1667, 0], "wingdings 2":[.8333, .25, 0], "wingdings 3":[.9167, .25, 0], "yuppy sc regular":[1.0833, .3333, 0], "yuppy tc regular":[1.0833, .3333, 0], "zapf dingbats":[.8333, .1667, 0], zapfino:[1.9167, 1.5, 0]}; - g.DEVICE_FONT_METRICS_LINUX = {kacstfarsi:[1.0831, .5215, 0], meera:[.682, .4413, 0], freemono:[.8023, .2006, 0], undotum:[1.0029, .2808, 0], loma:[1.1634, .4814, 0], "century schoolbook l":[1.0029, .3209, 0], kacsttitlel:[1.0831, .5215, 0], undinaru:[1.0029, .2407, 0], ungungseo:[1.0029, .2808, 0], garuda:[1.3238, .6017, 0], rekha:[1.1232, .2808, 0], purisa:[1.1232, .5215, 0], "dejavu sans mono":[.9628, .2407, 0], vemana2000:[.8825, .8424, 0], kacstoffice:[1.0831, .5215, 0], umpush:[1.2837, - .682, 0], opensymbol:[.8023, .2006, 0], sawasdee:[1.1232, .4413, 0], "urw palladio l":[1.0029, .3209, 0], freeserif:[.9227, .3209, 0], kacstdigital:[1.0831, .5215, 0], "ubuntu condensed":[.9628, .2006, 0], unpilgi:[1.0029, .4413, 0], mry_kacstqurn:[1.4442, .7221, 0], "urw gothic l":[1.0029, .2407, 0], dingbats:[.8424, .1605, 0], "urw chancery l":[1.0029, .3209, 0], "phetsarath ot":[1.0831, .5215, 0], "tlwg typist":[.8825, .4012, 0], kacstletter:[1.0831, .5215, 0], utkal:[1.2035, .6418, - 0], "dejavu sans light":[.9628, .2407, 0], norasi:[1.2436, .5215, 0], "dejavu serif condensed":[.9628, .2407, 0], kacstone:[1.2436, .6418, 0], "liberation sans narrow":[.9628, .2407, 0], symbol:[1.043, .3209, 0], nanummyeongjo:[.9227, .2407, 0], untitled1:[.682, .5616, 0], "lohit gujarati":[.9628, .4012, 0], "liberation mono":[.8424, .3209, 0], kacstart:[1.0831, .5215, 0], mallige:[1.0029, .682, 0], "bitstream charter":[1.0029, .2407, 0], nanumgothic:[.9227, .2407, 0], "liberation serif":[.9227, - .2407, 0], "dejavu sans condensed":[.9628, .2407, 0], ubuntu:[.9628, .2006, 0], "courier 10 pitch":[.8825, .3209, 0], "nimbus sans l":[.9628, .3209, 0], takaopgothic:[.8825, .2006, 0], "wenquanyi micro hei mono":[.9628, .2407, 0], "dejavu sans":[.9628, .2407, 0], kedage:[1.0029, .682, 0], kinnari:[1.3238, .5215, 0], tlwgmono:[.8825, .4012, 0], "standard symbols l":[1.043, .3209, 0], "lohit punjabi":[1.2035, .682, 0], "nimbus mono l":[.8424, .2808, 0], rachana:[.682, .5616, 0], waree:[1.2436, - .4413, 0], kacstposter:[1.0831, .5215, 0], "khmer os":[1.2837, .7622, 0], freesans:[1.0029, .3209, 0], gargi:[.9628, .2808, 0], "nimbus roman no9 l":[.9628, .3209, 0], "dejavu serif":[.9628, .2407, 0], "wenquanyi micro hei":[.9628, .2407, 0], "ubuntu light":[.9628, .2006, 0], tlwgtypewriter:[.9227, .4012, 0], kacstpen:[1.0831, .5215, 0], "tlwg typo":[.8825, .4012, 0], "mukti narrow":[1.2837, .4413, 0], "ubuntu mono":[.8424, .2006, 0], "lohit bengali":[1.0029, .4413, 0], "liberation sans":[.9227, - .2407, 0], unbatang:[1.0029, .2808, 0], kacstdecorative:[1.1232, .5215, 0], "khmer os system":[1.2436, .6017, 0], saab:[1.0029, .682, 0], kacsttitle:[1.0831, .5215, 0], "mukti narrow bold":[1.2837, .4413, 0], "lohit hindi":[1.0029, .5215, 0], kacstqurn:[1.0831, .5215, 0], "urw bookman l":[.9628, .2808, 0], kacstnaskh:[1.0831, .5215, 0], kacstscreen:[1.0831, .5215, 0], pothana2000:[.8825, .8424, 0], ungraphic:[1.0029, .2808, 0], "lohit tamil":[.8825, .361, 0], kacstbook:[1.0831, .5215, - 0]}; - g.DEVICE_FONT_METRICS_MAC.__proto__ = g.DEVICE_FONT_METRICS_WIN; - g.DEVICE_FONT_METRICS_LINUX.__proto__ = g.DEVICE_FONT_METRICS_MAC; - }; - g.classSymbols = null; - g.instanceSymbols = null; - g.initializer = function(b) { - this._id = f.display.DisplayObject.getNextSyncID(); - this._fontType = this._fontStyle = this._fontName = null; + m.DEFAULT_FONT_SANS = "Arial"; + m.DEFAULT_FONT_SERIF = "Times New Roman"; + m.DEFAULT_FONT_TYPEWRITER = "Courier New"; + m.classInitializer = function() { + m._fonts = []; + m._fontsBySymbolId = c.ObjectUtilities.createMap(); + m._fontsByName = c.ObjectUtilities.createMap(); + m.DEVICE_FONT_METRICS_WIN = {Arial:[1, .25, 0], "Arial Baltic":[1, .25, 0], "Arial Black":[1.0833, .3333, 0], "Arial CE":[1, .25, 0], "Arial CYR":[1, .25, 0], "Arial Greek":[1, .25, 0], "Arial TUR":[1, .25, 0], "Comic Sans MS":[1.0833, .3333, 0], "Courier New":[1, .25, 0], "Courier New Baltic":[1, .25, 0], "Courier New CE":[1, .25, 0], "Courier New CYR":[1, .25, 0], "Courier New Greek":[1, .25, 0], "Courier New TUR":[1, .25, 0], "Estrangelo Edessa":[.75, .3333, 0], "Franklin Gothic Medium":[1, + .3333, 0], Gautami:[.9167, .8333, 0], Georgia:[1, .25, 0], Impact:[1.0833, .25, 0], Latha:[1.0833, .25, 0], "Lucida Console":[.75, .25, 0], "Lucida Sans Unicode":[1.0833, .25, 0], Mangal:[1.0833, .25, 0], Marlett:[1, 0, 0], "Microsoft Sans Serif":[1.0833, .1667, 0], "MV Boli":[.9167, .25, 0], "Palatino Linotype":[1.0833, .3333, 0], Raavi:[1.0833, .6667, 0], Shruti:[1, .5, 0], Sylfaen:[1, .3333, 0], Symbol:[1, .25, 0], Tahoma:[1, .1667, 0], "Times New Roman":[1, .25, 0], "Times New Roman Baltic":[1, + .25, 0], "Times New Roman CE":[1, .25, 0], "Times New Roman CYR":[1, .25, 0], "Times New Roman Greek":[1, .25, 0], "Times New Roman TUR":[1, .25, 0], "Trebuchet MS":[1.0833, .4167, 0], Tunga:[1, .75, 0], Verdana:[1, .1667, 0], Webdings:[1.0833, .5, 0], Wingdings:[.9167, .25, 0]}; + m.DEVICE_FONT_METRICS_MAC = {"Al Bayan Bold":[1, .5833, 0], "Al Bayan Plain":[1, .5, 0], "Al Nile":[.8333, .5, 0], "Al Nile Bold":[.8333, .5, 0], "Al Tarikh Regular":[.5833, .4167, 0], "American Typewriter":[.9167, .25, 0], "American Typewriter Bold":[.9167, .25, 0], "American Typewriter Condensed":[.9167, .25, 0], "American Typewriter Condensed Bold":[.9167, .25, 0], "American Typewriter Condensed Light":[.8333, .25, 0], "American Typewriter Light":[.9167, .25, 0], "Andale Mono":[.9167, + .25, 0], "Apple Braille":[.75, .25, 0], "Apple Braille Outline 6 Dot":[.75, .25, 0], "Apple Braille Outline 8 Dot":[.75, .25, 0], "Apple Braille Pinpoint 6 Dot":[.75, .25, 0], "Apple Braille Pinpoint 8 Dot":[.75, .25, 0], "Apple Chancery":[1.0833, .5, 0], "Apple Color Emoji":[1.25, .4167, 0], "Apple SD Gothic Neo Bold":[.9167, .3333, 0], "Apple SD Gothic Neo Heavy":[.9167, .3333, 0], "Apple SD Gothic Neo Light":[.9167, .3333, 0], "Apple SD Gothic Neo Medium":[.9167, .3333, 0], "Apple SD Gothic Neo Regular":[.9167, + .3333, 0], "Apple SD Gothic Neo SemiBold":[.9167, .3333, 0], "Apple SD Gothic Neo Thin":[.9167, .3333, 0], "Apple SD Gothic Neo UltraLight":[.9167, .3333, 0], "Apple SD GothicNeo ExtraBold":[.9167, .3333, 0], "Apple Symbols":[.6667, .25, 0], "AppleGothic Regular":[.9167, .3333, 0], "AppleMyungjo Regular":[.8333, .3333, 0], Arial:[.9167, .25, 0], "Arial Black":[1.0833, .3333, 0], "Arial Bold":[.9167, .25, 0], "Arial Bold Italic":[.9167, .25, 0], "Arial Hebrew":[.75, .3333, 0], "Arial Hebrew Bold":[.75, + .3333, 0], "Arial Hebrew Light":[.75, .3333, 0], "Arial Hebrew Scholar":[.75, .3333, 0], "Arial Hebrew Scholar Bold":[.75, .3333, 0], "Arial Hebrew Scholar Light":[.75, .3333, 0], "Arial Italic":[.9167, .25, 0], "Arial Narrow":[.9167, .25, 0], "Arial Narrow Bold":[.9167, .25, 0], "Arial Narrow Bold Italic":[.9167, .25, 0], "Arial Narrow Italic":[.9167, .25, 0], "Arial Rounded MT Bold":[.9167, .25, 0], "Arial Unicode MS":[1.0833, .25, 0], "Athelas Bold":[.9167, .25, 0], "Athelas Bold Italic":[.9167, + .25, 0], "Athelas Italic":[.9167, .25, 0], "Athelas Regular":[.9167, .25, 0], "Avenir Black":[1, .3333, 0], "Avenir Black Oblique":[1, .3333, 0], "Avenir Book":[1, .3333, 0], "Avenir Book Oblique":[1, .3333, 0], "Avenir Heavy":[1, .3333, 0], "Avenir Heavy Oblique":[1, .3333, 0], "Avenir Light":[1, .3333, 0], "Avenir Light Oblique":[1, .3333, 0], "Avenir Medium":[1, .3333, 0], "Avenir Medium Oblique":[1, .3333, 0], "Avenir Next Bold":[1, .3333, 0], "Avenir Next Bold Italic":[1, .3333, + 0], "Avenir Next Condensed Bold":[1, .3333, 0], "Avenir Next Condensed Bold Italic":[1, .3333, 0], "Avenir Next Condensed Demi Bold":[1, .3333, 0], "Avenir Next Condensed Demi Bold Italic":[1, .3333, 0], "Avenir Next Condensed Heavy":[1, .3333, 0], "Avenir Next Condensed Heavy Italic":[1, .3333, 0], "Avenir Next Condensed Italic":[1, .3333, 0], "Avenir Next Condensed Medium":[1, .3333, 0], "Avenir Next Condensed Medium Italic":[1, .3333, 0], "Avenir Next Condensed Regular":[1, .3333, + 0], "Avenir Next Condensed Ultra Light":[1, .3333, 0], "Avenir Next Condensed Ultra Light Italic":[1, .3333, 0], "Avenir Next Demi Bold":[1, .3333, 0], "Avenir Next Demi Bold Italic":[1, .3333, 0], "Avenir Next Heavy":[1, .3333, 0], "Avenir Next Heavy Italic":[1, .3333, 0], "Avenir Next Italic":[1, .3333, 0], "Avenir Next Medium":[1, .3333, 0], "Avenir Next Medium Italic":[1, .3333, 0], "Avenir Next Regular":[1, .3333, 0], "Avenir Next Ultra Light":[1, .3333, 0], "Avenir Next Ultra Light Italic":[1, + .3333, 0], "Avenir Oblique":[1, .3333, 0], "Avenir Roman":[1, .3333, 0], Ayuthaya:[1.0833, .3333, 0], "Baghdad Regular":[.9167, .4167, 0], "Bangla MN":[1.0833, .75, 0], "Bangla MN Bold":[1.0833, .75, 0], "Bangla Sangam MN":[.9167, .4167, 0], "Bangla Sangam MN Bold":[.9167, .4167, 0], "Baoli SC Regular":[1.0833, .3333, 0], Baskerville:[.9167, .25, 0], "Baskerville Bold":[.9167, .25, 0], "Baskerville Bold Italic":[.9167, .25, 0], "Baskerville Italic":[.9167, .25, 0], "Baskerville SemiBold":[.9167, + .25, 0], "Baskerville SemiBold Italic":[.9167, .25, 0], "Beirut Regular":[.75, .25, 0], "Big Caslon Medium":[.9167, .25, 0], "Bodoni 72 Bold":[.9167, .25, 0], "Bodoni 72 Book":[.9167, .25, 0], "Bodoni 72 Book Italic":[.9167, .3333, 0], "Bodoni 72 Oldstyle Bold":[.9167, .25, 0], "Bodoni 72 Oldstyle Book":[.9167, .25, 0], "Bodoni 72 Oldstyle Book Italic":[.9167, .3333, 0], "Bodoni 72 Smallcaps Book":[.9167, .25, 0], "Bodoni Ornaments":[.8333, .1667, 0], "Bradley Hand Bold":[.8333, .4167, + 0], "Brush Script MT Italic":[.9167, .3333, 0], Chalkboard:[1, .25, 0], "Chalkboard Bold":[1, .25, 0], "Chalkboard SE Bold":[1.1667, .25, 0], "Chalkboard SE Light":[1.1667, .25, 0], "Chalkboard SE Regular":[1.1667, .25, 0], Chalkduster:[1, .25, 0], "Charter Black":[1, .25, 0], "Charter Black Italic":[1, .25, 0], "Charter Bold":[1, .25, 0], "Charter Bold Italic":[1, .25, 0], "Charter Italic":[1, .25, 0], "Charter Roman":[1, .25, 0], Cochin:[.9167, .25, 0], "Cochin Bold":[.9167, .25, + 0], "Cochin Bold Italic":[.9167, .25, 0], "Cochin Italic":[.9167, .25, 0], "Comic Sans MS":[1.0833, .25, 0], "Comic Sans MS Bold":[1.0833, .25, 0], Copperplate:[.75, .25, 0], "Copperplate Bold":[.75, .25, 0], "Copperplate Light":[.75, .25, 0], "Corsiva Hebrew":[.6667, .3333, 0], "Corsiva Hebrew Bold":[.6667, .3333, 0], Courier:[.75, .25, 0], "Courier Bold":[.75, .25, 0], "Courier Bold Oblique":[.75, .25, 0], "Courier New":[.8333, .3333, 0], "Courier New Bold":[.8333, .3333, 0], "Courier New Bold Italic":[.8333, + .3333, 0], "Courier New Italic":[.8333, .3333, 0], "Courier Oblique":[.75, .25, 0], "Damascus Bold":[.5833, .4167, 0], "Damascus Light":[.5833, .4167, 0], "Damascus Medium":[.5833, .4167, 0], "Damascus Regular":[.5833, .4167, 0], "Damascus Semi Bold":[.5833, .4167, 0], "DecoType Naskh Regular":[1.1667, .6667, 0], "Devanagari MT":[.9167, .6667, 0], "Devanagari MT Bold":[.9167, .6667, 0], "Devanagari Sangam MN":[.9167, .4167, 0], "Devanagari Sangam MN Bold":[.9167, .4167, 0], Didot:[.9167, + .3333, 0], "Didot Bold":[1, .3333, 0], "Didot Italic":[.9167, .25, 0], "DIN Alternate Bold":[.9167, .25, 0], "DIN Condensed Bold":[.75, .25, 0], "Diwan Kufi Regular":[1.4167, .5, 0], "Diwan Thuluth Regular":[1, .6667, 0], "Euphemia UCAS":[1.0833, .25, 0], "Euphemia UCAS Bold":[1.0833, .25, 0], "Euphemia UCAS Italic":[1.0833, .25, 0], "Farah Regular":[.75, .25, 0], "Farisi Regular":[1.0833, 1, 0], "Futura Condensed ExtraBold":[1, .25, 0], "Futura Condensed Medium":[1, .25, 0], "Futura Medium":[1, + .25, 0], "Futura Medium Italic":[1, .25, 0], "GB18030 Bitmap":[1.1667, .1667, 0], "Geeza Pro Bold":[.9167, .3333, 0], "Geeza Pro Regular":[.9167, .3333, 0], Geneva:[1, .25, 0], Georgia:[.9167, .25, 0], "Georgia Bold":[.9167, .25, 0], "Georgia Bold Italic":[.9167, .25, 0], "Georgia Italic":[.9167, .25, 0], "Gill Sans":[.9167, .25, 0], "Gill Sans Bold":[.9167, .25, 0], "Gill Sans Bold Italic":[.9167, .25, 0], "Gill Sans Italic":[.9167, .25, 0], "Gill Sans Light":[.9167, .25, 0], "Gill Sans Light Italic":[.9167, + .25, 0], "Gill Sans SemiBold":[.9167, .25, 0], "Gill Sans SemiBold Italic":[.9167, .25, 0], "Gill Sans UltraBold":[1, .25, 0], "Gujarati MT":[.9167, .6667, 0], "Gujarati MT Bold":[.9167, .6667, 0], "Gujarati Sangam MN":[.8333, .4167, 0], "Gujarati Sangam MN Bold":[.8333, .4167, 0], "GungSeo Regular":[.8333, .25, 0], "Gurmukhi MN":[.9167, .25, 0], "Gurmukhi MN Bold":[.9167, .25, 0], "Gurmukhi MT":[.8333, .4167, 0], "Gurmukhi Sangam MN":[.9167, .3333, 0], "Gurmukhi Sangam MN Bold":[.9167, + .3333, 0], "Hannotate SC Bold":[1.0833, .3333, 0], "Hannotate SC Regular":[1.0833, .3333, 0], "Hannotate TC Bold":[1.0833, .3333, 0], "Hannotate TC Regular":[1.0833, .3333, 0], "HanziPen SC Bold":[1.0833, .3333, 0], "HanziPen SC Regular":[1.0833, .3333, 0], "HanziPen TC Bold":[1.0833, .3333, 0], "HanziPen TC Regular":[1.0833, .3333, 0], "HeadLineA Regular":[.8333, .1667, 0], "Heiti SC Light":[.8333, .1667, 0], "Heiti SC Medium":[.8333, .1667, 0], "Heiti TC Light":[.8333, .1667, 0], + "Heiti TC Medium":[.8333, .1667, 0], Helvetica:[.75, .25, 0], "Helvetica Bold":[.75, .25, 0], "Helvetica Bold Oblique":[.75, .25, 0], "Helvetica Light":[.75, .25, 0], "Helvetica Light Oblique":[.75, .25, 0], "Helvetica Neue":[.9167, .25, 0], "Helvetica Neue Bold":[1, .25, 0], "Helvetica Neue Bold Italic":[1, .25, 0], "Helvetica Neue Condensed Black":[1, .25, 0], "Helvetica Neue Condensed Bold":[1, .25, 0], "Helvetica Neue Italic":[.9167, .25, 0], "Helvetica Neue Light":[1, .25, 0], + "Helvetica Neue Light Italic":[.9167, .25, 0], "Helvetica Neue Medium":[1, .25, 0], "Helvetica Neue Medium Italic":[1, .25, 0], "Helvetica Neue Thin":[1, .25, 0], "Helvetica Neue Thin Italic":[1, .25, 0], "Helvetica Neue UltraLight":[.9167, .25, 0], "Helvetica Neue UltraLight Italic":[.9167, .25, 0], "Helvetica Oblique":[.75, .25, 0], Herculanum:[.8333, .1667, 0], "Hiragino Kaku Gothic Pro W3":[.9167, .0833, 0], "Hiragino Kaku Gothic Pro W6":[.9167, .0833, 0], "Hiragino Kaku Gothic ProN W3":[.9167, + .0833, 0], "Hiragino Kaku Gothic ProN W6":[.9167, .0833, 0], "Hiragino Kaku Gothic Std W8":[.9167, .0833, 0], "Hiragino Kaku Gothic StdN W8":[.9167, .0833, 0], "Hiragino Maru Gothic Pro W4":[.9167, .0833, 0], "Hiragino Maru Gothic ProN W4":[.9167, .0833, 0], "Hiragino Mincho Pro W3":[.9167, .0833, 0], "Hiragino Mincho Pro W6":[.9167, .0833, 0], "Hiragino Mincho ProN W3":[.9167, .0833, 0], "Hiragino Mincho ProN W6":[.9167, .0833, 0], "Hiragino Sans GB W3":[.9167, .0833, 0], "Hiragino Sans GB W6":[.9167, + .0833, 0], "Hoefler Text":[.75, .25, 0], "Hoefler Text Black":[.75, .25, 0], "Hoefler Text Black Italic":[.75, .25, 0], "Hoefler Text Italic":[.75, .25, 0], "Hoefler Text Ornaments":[.8333, .1667, 0], Impact:[1, .25, 0], InaiMathi:[.8333, .4167, 0], "Iowan Old Style Black":[1, .3333, 0], "Iowan Old Style Black Italic":[1, .3333, 0], "Iowan Old Style Bold":[1, .3333, 0], "Iowan Old Style Bold Italic":[1, .3333, 0], "Iowan Old Style Italic":[1, .3333, 0], "Iowan Old Style Roman":[1, .3333, + 0], "Iowan Old Style Titling":[1, .3333, 0], "ITF Devanagari Bold":[1.0833, .3333, 0], "ITF Devanagari Book":[1.0833, .3333, 0], "ITF Devanagari Demi":[1.0833, .3333, 0], "ITF Devanagari Light":[1.0833, .3333, 0], "ITF Devanagari Medium":[1.0833, .3333, 0], "Kailasa Regular":[1.0833, .5833, 0], "Kaiti SC Black":[1.0833, .3333, 0], "Kaiti SC Bold":[1.0833, .3333, 0], "Kaiti SC Regular":[1.0833, .3333, 0], "Kaiti TC Bold":[1.0833, .3333, 0], "Kaiti TC Regular":[1.0833, .3333, 0], "Kannada MN":[.9167, + .25, 0], "Kannada MN Bold":[.9167, .25, 0], "Kannada Sangam MN":[1, .5833, 0], "Kannada Sangam MN Bold":[1, .5833, 0], "Kefa Bold":[.9167, .25, 0], "Kefa Regular":[.9167, .25, 0], "Khmer MN":[1, .8333, 0], "Khmer MN Bold":[1, .8333, 0], "Khmer Sangam MN":[1.0833, .8333, 0], "Kohinoor Devanagari Bold":[1.0833, .3333, 0], "Kohinoor Devanagari Book":[1.0833, .3333, 0], "Kohinoor Devanagari Demi":[1.0833, .3333, 0], "Kohinoor Devanagari Light":[1.0833, .3333, 0], "Kohinoor Devanagari Medium":[1.0833, + .3333, 0], "Kokonor Regular":[1.0833, .5833, 0], Krungthep:[1, .25, 0], "KufiStandardGK Regular":[.9167, .5, 0], "Lantinghei SC Demibold":[1, .3333, 0], "Lantinghei SC Extralight":[1, .3333, 0], "Lantinghei SC Heavy":[1, .3333, 0], "Lantinghei TC Demibold":[1, .3333, 0], "Lantinghei TC Extralight":[1, .3333, 0], "Lantinghei TC Heavy":[1, .3333, 0], "Lao MN":[.9167, .4167, 0], "Lao MN Bold":[.9167, .4167, 0], "Lao Sangam MN":[1, .3333, 0], "Libian SC Regular":[1.0833, .3333, 0], "LiHei Pro":[.8333, + .1667, 0], "LiSong Pro":[.8333, .1667, 0], "Lucida Grande":[1, .25, 0], "Lucida Grande Bold":[1, .25, 0], Luminari:[1, .3333, 0], "Malayalam MN":[1, .4167, 0], "Malayalam MN Bold":[1, .4167, 0], "Malayalam Sangam MN":[.8333, .4167, 0], "Malayalam Sangam MN Bold":[.8333, .4167, 0], "Marion Bold":[.6667, .3333, 0], "Marion Italic":[.6667, .3333, 0], "Marion Regular":[.6667, .3333, 0], "Marker Felt Thin":[.8333, .25, 0], "Marker Felt Wide":[.9167, .25, 0], "Menlo Bold":[.9167, .25, 0], + "Menlo Bold Italic":[.9167, .25, 0], "Menlo Italic":[.9167, .25, 0], "Menlo Regular":[.9167, .25, 0], "Microsoft Sans Serif":[.9167, .25, 0], "Mishafi Gold Regular":[.75, .6667, 0], "Mishafi Regular":[.75, .6667, 0], Monaco:[1, .25, 0], Mshtakan:[.9167, .25, 0], "Mshtakan Bold":[.9167, .25, 0], "Mshtakan BoldOblique":[.9167, .25, 0], "Mshtakan Oblique":[.9167, .25, 0], "Muna Black":[.75, .3333, 0], "Muna Bold":[.75, .3333, 0], "Muna Regular":[.75, .3333, 0], "Myanmar MN":[1, .4167, + 0], "Myanmar MN Bold":[1, .4167, 0], "Myanmar Sangam MN":[.9167, .4167, 0], "Nadeem Regular":[.9167, .4167, 0], "Nanum Brush Script":[.9167, .25, 0], "Nanum Pen Script":[.9167, .25, 0], NanumGothic:[.9167, .25, 0], "NanumGothic Bold":[.9167, .25, 0], "NanumGothic ExtraBold":[.9167, .25, 0], NanumMyeongjo:[.9167, .25, 0], "NanumMyeongjo Bold":[.9167, .25, 0], "NanumMyeongjo ExtraBold":[.9167, .25, 0], "New Peninim MT":[.75, .3333, 0], "New Peninim MT Bold":[.75, .3333, 0], "New Peninim MT Bold Inclined":[.75, + .3333, 0], "New Peninim MT Inclined":[.75, .3333, 0], "Noteworthy Bold":[1.25, .3333, 0], "Noteworthy Light":[1.25, .3333, 0], "Optima Bold":[.9167, .25, 0], "Optima Bold Italic":[.9167, .25, 0], "Optima ExtraBlack":[1, .25, 0], "Optima Italic":[.9167, .25, 0], "Optima Regular":[.9167, .25, 0], "Oriya MN":[.9167, .25, 0], "Oriya MN Bold":[.9167, .25, 0], "Oriya Sangam MN":[.8333, .4167, 0], "Oriya Sangam MN Bold":[.8333, .4167, 0], Osaka:[1, .25, 0], "Osaka-Mono":[.8333, .1667, 0], + Palatino:[.8333, .25, 0], "Palatino Bold":[.8333, .25, 0], "Palatino Bold Italic":[.8333, .25, 0], "Palatino Italic":[.8333, .25, 0], Papyrus:[.9167, .5833, 0], "Papyrus Condensed":[.9167, .5833, 0], "PCMyungjo Regular":[.8333, .25, 0], "Phosphate Inline":[.9167, .25, 0], "Phosphate Solid":[.9167, .25, 0], "PilGi Regular":[.8333, .25, 0], "Plantagenet Cherokee":[.6667, .25, 0], "PT Mono":[.9167, .25, 0], "PT Mono Bold":[.9167, .25, 0], "PT Sans":[.9167, .25, 0], "PT Sans Bold":[.9167, + .25, 0], "PT Sans Bold Italic":[.9167, .25, 0], "PT Sans Caption":[.9167, .25, 0], "PT Sans Caption Bold":[.9167, .25, 0], "PT Sans Italic":[.9167, .25, 0], "PT Sans Narrow":[.9167, .25, 0], "PT Sans Narrow Bold":[.9167, .25, 0], "PT Serif":[1, .25, 0], "PT Serif Bold":[1, .25, 0], "PT Serif Bold Italic":[1, .25, 0], "PT Serif Caption":[1, .25, 0], "PT Serif Caption Italic":[1, .25, 0], "PT Serif Italic":[1, .25, 0], Raanana:[.75, .25, 0], "Raanana Bold":[.75, .25, 0], "Sana Regular":[.75, + .25, 0], Sathu:[.9167, .3333, 0], "Savoye LET Plain CC.:1.0":[1.0833, .75, 0], "Savoye LET Plain:1.0":[.6667, .5, 0], Seravek:[.9167, .3333, 0], "Seravek Bold":[.9167, .3333, 0], "Seravek Bold Italic":[.9167, .3333, 0], "Seravek ExtraLight":[.9167, .3333, 0], "Seravek ExtraLight Italic":[.9167, .3333, 0], "Seravek Italic":[.9167, .3333, 0], "Seravek Light":[.9167, .3333, 0], "Seravek Light Italic":[.9167, .3333, 0], "Seravek Medium":[.9167, .3333, 0], "Seravek Medium Italic":[.9167, + .3333, 0], "Shree Devanagari 714":[.9167, .4167, 0], "Shree Devanagari 714 Bold":[.9167, .4167, 0], "Shree Devanagari 714 Bold Italic":[.9167, .4167, 0], "Shree Devanagari 714 Italic":[.9167, .4167, 0], "SignPainter-HouseScript":[.6667, .1667, 0], Silom:[1, .3333, 0], "Sinhala MN":[.9167, .25, 0], "Sinhala MN Bold":[.9167, .25, 0], "Sinhala Sangam MN":[1.1667, .3333, 0], "Sinhala Sangam MN Bold":[1.1667, .3333, 0], "Skia Black":[.75, .25, 0], "Skia Black Condensed":[.75, .25, 0], "Skia Black Extended":[.75, + .25, 0], "Skia Bold":[.75, .25, 0], "Skia Condensed":[.75, .25, 0], "Skia Extended":[.75, .25, 0], "Skia Light":[.75, .25, 0], "Skia Light Condensed":[.75, .25, 0], "Skia Light Extended":[.75, .25, 0], "Skia Regular":[.75, .25, 0], "Snell Roundhand":[.9167, .3333, 0], "Snell Roundhand Black":[.9167, .3333, 0], "Snell Roundhand Bold":[.9167, .3333, 0], "Songti SC Black":[1.0833, .3333, 0], "Songti SC Bold":[1.0833, .3333, 0], "Songti SC Light":[1.0833, .3333, 0], "Songti SC Regular":[1.0833, + .3333, 0], "Songti TC Bold":[1.0833, .3333, 0], "Songti TC Light":[1.0833, .3333, 0], "Songti TC Regular":[1.0833, .3333, 0], STFangsong:[.8333, .1667, 0], STHeiti:[.8333, .1667, 0], "STIXGeneral-Bold":[1.0833, .4167, 0], "STIXGeneral-BoldItalic":[1.0833, .4167, 0], "STIXGeneral-Italic":[1.0833, .4167, 0], "STIXGeneral-Regular":[1.0833, .4167, 0], "STIXIntegralsD-Bold":[2.1667, .4167, 0], "STIXIntegralsD-Regular":[2.1667, .4167, 0], "STIXIntegralsSm-Bold":[1.0833, .4167, 0], "STIXIntegralsSm-Regular":[1.0833, + .4167, 0], "STIXIntegralsUp-Bold":[1.0833, .4167, 0], "STIXIntegralsUp-Regular":[1.0833, .4167, 0], "STIXIntegralsUpD-Bold":[2.1667, .4167, 0], "STIXIntegralsUpD-Regular":[2.1667, .4167, 0], "STIXIntegralsUpSm-Bold":[1.0833, .4167, 0], "STIXIntegralsUpSm-Regular":[1.0833, .4167, 0], "STIXNonUnicode-Bold":[1.4167, .5833, 0], "STIXNonUnicode-BoldItalic":[1.4167, .5833, 0], "STIXNonUnicode-Italic":[1.4167, .5833, 0], "STIXNonUnicode-Regular":[1.4167, .5833, 0], "STIXSizeFiveSym-Regular":[1, + .4167, 0], "STIXSizeFourSym-Bold":[2.5833, .5, 0], "STIXSizeFourSym-Regular":[2.5833, .5, 0], "STIXSizeOneSym-Bold":[1.5833, .3333, 0], "STIXSizeOneSym-Regular":[1.5833, .3333, 0], "STIXSizeThreeSym-Bold":[2.5833, .5, 0], "STIXSizeThreeSym-Regular":[2.5833, .5, 0], "STIXSizeTwoSym-Bold":[2.0833, .4167, 0], "STIXSizeTwoSym-Regular":[2.0833, .4167, 0], "STIXVariants-Bold":[1.0833, .4167, 0], "STIXVariants-Regular":[1.0833, .4167, 0], STKaiti:[.8333, .1667, 0], STSong:[.8333, .1667, 0], + STXihei:[.8333, .1667, 0], "Sukhumvit Set Bold":[1.0833, .5, 0], "Sukhumvit Set Light":[1.0833, .5, 0], "Sukhumvit Set Medium":[1.0833, .5, 0], "Sukhumvit Set Semi Bold":[1.0833, .5, 0], "Sukhumvit Set Text":[1.0833, .5, 0], "Sukhumvit Set Thin":[1.0833, .5, 0], "Superclarendon Black":[1, .25, 0], "Superclarendon Black Italic":[1, .25, 0], "Superclarendon Bold":[1, .25, 0], "Superclarendon Bold Italic":[1, .25, 0], "Superclarendon Italic":[1, .25, 0], "Superclarendon Light":[1, .25, + 0], "Superclarendon Light Italic":[1, .25, 0], "Superclarendon Regular":[1, .25, 0], Symbol:[.6667, .3333, 0], "System Font Bold":[1, .25, 0], "System Font Bold Italic":[1, .25, 0], "System Font Heavy":[1, .25, 0], "System Font Italic":[1, .25, 0], "System Font Light":[1, .25, 0], "System Font Medium Italic P4":[1, .25, 0], "System Font Medium P4":[1, .25, 0], "System Font Regular":[1, .25, 0], "System Font Thin":[1, .25, 0], "System Font UltraLight":[1, .25, 0], Tahoma:[1, .1667, 0], + "Tahoma Negreta":[1, .1667, 0], "Tamil MN":[.9167, .25, 0], "Tamil MN Bold":[.9167, .25, 0], "Tamil Sangam MN":[.75, .25, 0], "Tamil Sangam MN Bold":[.75, .25, 0], "Telugu MN":[.9167, .25, 0], "Telugu MN Bold":[.9167, .25, 0], "Telugu Sangam MN":[1, .5833, 0], "Telugu Sangam MN Bold":[1, .5833, 0], Thonburi:[1.0833, .25, 0], "Thonburi Bold":[1.0833, .25, 0], "Thonburi Light":[1.0833, .25, 0], "Times Bold":[.75, .25, 0], "Times Bold Italic":[.75, .25, 0], "Times Italic":[.75, .25, 0], + "Times New Roman":[.9167, .25, 0], "Times New Roman Bold":[.9167, .25, 0], "Times New Roman Bold Italic":[.9167, .25, 0], "Times New Roman Italic":[.9167, .25, 0], "Times Roman":[.75, .25, 0], Trattatello:[1.1667, .6667, 0], "Trebuchet MS":[.9167, .25, 0], "Trebuchet MS Bold":[.9167, .25, 0], "Trebuchet MS Bold Italic":[.9167, .25, 0], "Trebuchet MS Italic":[.9167, .25, 0], Verdana:[1, .25, 0], "Verdana Bold":[1, .25, 0], "Verdana Bold Italic":[1, .25, 0], "Verdana Italic":[1, .25, + 0], "Waseem Light":[.9167, .5833, 0], "Waseem Regular":[.9167, .5833, 0], "Wawati SC Regular":[1.0833, .3333, 0], "Wawati TC Regular":[1.0833, .3333, 0], Webdings:[.8333, .1667, 0], "Weibei SC Bold":[1.0833, .3333, 0], "Weibei TC Bold":[1.0833, .3333, 0], Wingdings:[.9167, .25, 0], "Wingdings 2":[.8333, .25, 0], "Wingdings 3":[.9167, .25, 0], "Xingkai SC Bold":[1.0833, .3333, 0], "Xingkai SC Light":[1.0833, .3333, 0], "Yuanti SC Bold":[1.0833, .3333, 0], "Yuanti SC Light":[1.0833, .3333, + 0], "Yuanti SC Regular":[1.0833, .3333, 0], "YuGothic Bold":[.9167, .0833, 0], "YuGothic Medium":[.9167, .0833, 0], "YuMincho Demibold":[.9167, .0833, 0], "YuMincho Medium":[.9167, .0833, 0], "Yuppy SC Regular":[1.0833, .3333, 0], "Yuppy TC Regular":[1.0833, .3333, 0], "Zapf Dingbats":[.8333, .1667, 0], Zapfino:[1.9167, 1.5, 0]}; + m.DEVICE_FONT_METRICS_LINUX = {KacstFarsi:[1.0417, .5208, 0], Meera:[.651, .4557, 0], FreeMono:[.7812, .1953, 0], Loma:[1.1719, .4557, 0], "Century Schoolbook L":[.9766, .3255, 0], KacstTitleL:[1.0417, .5208, 0], Garuda:[1.3021, .5859, 0], Rekha:[1.1068, .2604, 0], Purisa:[1.1068, .5208, 0], "DejaVu Sans Mono":[.9115, .2604, 0], Vemana2000:[.9115, .8464, 0], KacstOffice:[1.0417, .5208, 0], Umpush:[1.237, .651, 0], OpenSymbol:[.7812, .1953, 0], Sawasdee:[1.1068, .4557, 0], "URW Palladio L":[.9766, + .3255, 0], FreeSerif:[.9115, .3255, 0], KacstDigital:[1.0417, .5208, 0], "Ubuntu Condensed":[.9115, .1953, 0], mry_KacstQurn:[1.4323, .7161, 0], "URW Gothic L":[.9766, .2604, 0], Dingbats:[.8464, .1953, 0], "URW Chancery L":[.9766, .3255, 0], "Phetsarath OT":[1.1068, .5208, 0], "Tlwg Typist":[.9115, .3906, 0], KacstLetter:[1.0417, .5208, 0], utkal:[1.1719, .651, 0], Norasi:[1.237, .5208, 0], KacstOne:[1.237, .651, 0], "Liberation Sans Narrow":[.9115, .2604, 0], Symbol:[1.0417, .3255, + 0], NanumMyeongjo:[.9115, .2604, 0], Untitled1:[.651, .5859, 0], "Lohit Gujarati":[.9115, .3906, 0], "Liberation Mono":[.8464, .3255, 0], KacstArt:[1.0417, .5208, 0], Mallige:[.9766, .651, 0], "Bitstream Charter":[.9766, .2604, 0], NanumGothic:[.9115, .2604, 0], "Liberation Serif":[.9115, .2604, 0], Ubuntu:[.9115, .1953, 0], "Courier 10 Pitch":[.8464, .3255, 0], "Nimbus Sans L":[.9766, .3255, 0], TakaoPGothic:[.9115, .1953, 0], "WenQuanYi Micro Hei Mono":[.9766, .2604, 0], "DejaVu Sans":[.9115, + .2604, 0], Kedage:[.9766, .651, 0], Kinnari:[1.3021, .5208, 0], TlwgMono:[.8464, .3906, 0], "Standard Symbols L":[1.0417, .3255, 0], "Lohit Punjabi":[1.1719, .651, 0], "Nimbus Mono L":[.8464, .3255, 0], Rachana:[.651, .5859, 0], Waree:[1.237, .4557, 0], KacstPoster:[1.0417, .5208, 0], "Khmer OS":[1.3021, .7161, 0], FreeSans:[.9766, .3255, 0], gargi:[.9115, .3255, 0], "Nimbus Roman No9 L":[.9115, .3255, 0], "DejaVu Serif":[.9115, .2604, 0], "WenQuanYi Micro Hei":[.9766, .2604, 0], "Ubuntu Light":[.9115, + .1953, 0], TlwgTypewriter:[.9115, .3906, 0], KacstPen:[1.0417, .5208, 0], "Tlwg Typo":[.9115, .3906, 0], "Mukti Narrow":[1.237, .4557, 0], "Ubuntu Mono":[.8464, .1953, 0], "Lohit Bengali":[.9766, .4557, 0], "Liberation Sans":[.9115, .2604, 0], KacstDecorative:[1.1068, .5208, 0], "Khmer OS System":[1.237, .5859, 0], Saab:[.9766, .651, 0], KacstTitle:[1.0417, .5208, 0], "Mukti Narrow Bold":[1.237, .4557, 0], "Lohit Hindi":[.9766, .5208, 0], KacstQurn:[1.0417, .5208, 0], "URW Bookman L":[.9766, + .3255, 0], KacstNaskh:[1.0417, .5208, 0], KacstScreen:[1.0417, .5208, 0], Pothana2000:[.9115, .8464, 0], "Lohit Tamil":[.8464, .3906, 0], KacstBook:[1.0417, .5208, 0], Sans:[.9115, .2604, 0], Times:[.9115, .3255, 0], Monospace:[.9115, .2604, 0]}; + m.DEVICE_FONT_METRICS_BUILTIN = {_sans:[.9, .22, .08], _serif:[.88, .26, .08], _typewriter:[.86, .24, .08]}; + m.DEVICE_FONT_METRICS_WIN.__proto__ = m.DEVICE_FONT_METRICS_BUILTIN; + m.DEVICE_FONT_METRICS_MAC.__proto__ = m.DEVICE_FONT_METRICS_BUILTIN; + m.DEVICE_FONT_METRICS_LINUX.__proto__ = m.DEVICE_FONT_METRICS_BUILTIN; + var a = self.navigator.userAgent; + -1 < a.indexOf("Windows") ? m._deviceFontMetrics = m.DEVICE_FONT_METRICS_WIN : /(Macintosh|iPad|iPhone|iPod|Android)/.test(a) ? (m._deviceFontMetrics = this.DEVICE_FONT_METRICS_MAC, m.DEFAULT_FONT_SANS = "Helvetica", m.DEFAULT_FONT_SERIF = "Times Roman", m.DEFAULT_FONT_TYPEWRITER = "Courier") : (m._deviceFontMetrics = this.DEVICE_FONT_METRICS_LINUX, m.DEFAULT_FONT_SANS = "Sans", m.DEFAULT_FONT_SERIF = "Times", m.DEFAULT_FONT_TYPEWRITER = "Monospace"); + var a = m._deviceFontMetrics, e; + for (e in a) { + a[e.toLowerCase()] = a[e]; + } + }; + m.classSymbols = null; + m.instanceSymbols = null; + m.initializer = function(a) { + this._fontType = this._fontStyle = this._fontFamily = this._fontName = null; this.leading = this.descent = this.ascent = 0; this.advances = null; - if (b) { - this._symbol = b; - this._fontName = b.name; - this._fontStyle = b.bold ? b.italic ? d.BOLD_ITALIC : d.BOLD : b.italic ? d.ITALIC : d.REGULAR; - var c = b.metrics; - c && (this.ascent = c.ascent, this.descent = c.descent, this.leading = c.leading, this.advances = c.advances); - this._fontType = b.data ? a.EMBEDDED : a.DEVICE; - g._fontsBySymbolId[b.id] = this; - g._fontsByName[b.name.toLowerCase()] = this; - g._fontsByName["swffont" + b.id] = this; + if (a) { + this._symbol = a; + c.Debug.assert(a.syncId); + this._id = a.syncId; + this._fontName = a.name; + this._fontFamily = m.resolveFontName(a.name); + this._fontStyle = a.bold ? a.italic ? l.BOLD_ITALIC : l.BOLD : a.italic ? l.ITALIC : l.REGULAR; + var f = a.metrics; + f && (this.ascent = f.ascent, this.descent = f.descent, this.leading = f.leading, this.advances = f.advances); + this._fontType = f ? e.EMBEDDED : e.DEVICE; + f = Object.getOwnPropertyDescriptor(m._fontsBySymbolId, a.syncId + ""); + f && f.value || (f = {value:this, configurable:!0}, Object.defineProperty(m._fontsBySymbolId, a.syncId + "", f), Object.defineProperty(m._fontsByName, a.name.toLowerCase() + this._fontStyle, f), this._fontType === e.EMBEDDED && Object.defineProperty(m._fontsByName, "swffont" + a.syncId + this._fontStyle, f)); + } else { + this._id = h.display.DisplayObject.getNextSyncID(); } }; - return g; - }(g.ASNative); - k.Font = c; - })(f.text || (f.text = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + return m; + }(a.ASNative); + v.Font = m; + var t = function(a) { + function e(c) { + a.call(this, c, m); + } + __extends(e, a); + e.FromData = function(a) { + var c = new e(a); + c.ready = !a.metrics; + c.name = a.name; + c.data = {id:a.id}; + c.bold = a.bold; + c.italic = a.italic; + c.originalSize = a.originalSize; + c.codes = a.codes; + c.metrics = a.metrics; + c.syncId = h.display.DisplayObject.getNextSyncID(); + return c; + }; + Object.defineProperty(e.prototype, "resolveAssetCallback", {get:function() { + return this._unboundResolveAssetCallback.bind(this); + }, enumerable:!0, configurable:!0}); + e.prototype._unboundResolveAssetCallback = function(a) { + c.Debug.assert(!this.ready); + this.ready = !0; + }; + return e; + }(c.Timeline.Symbol); + v.FontSymbol = t; + })(h.text || (h.text = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { - function d() { - b.call(this); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function c() { + a.call(this); } - __extends(d, b); - d.fromNumber = function(a) { + __extends(c, a); + c.fromNumber = function(a) { switch(a) { case 0: - return d.NONE; + return c.NONE; case 1: - return d.PIXEL; + return c.PIXEL; case 2: - return d.SUBPIXEL; + return c.SUBPIXEL; default: return null; } }; - d.toNumber = function(a) { + c.toNumber = function(a) { switch(a) { - case d.NONE: + case c.NONE: return 0; - case d.PIXEL: + case c.PIXEL: return 1; - case d.SUBPIXEL: + case c.SUBPIXEL: return 2; default: return-1; } }; - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - d.NONE = "none"; - d.PIXEL = "pixel"; - d.SUBPIXEL = "subpixel"; - return d; - }(b.ASNative); - f.GridFitType = k; - })(f.text || (f.text = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.NONE = "none"; + c.PIXEL = "pixel"; + c.SUBPIXEL = "subpixel"; + return c; + }(a.ASNative); + c.GridFitType = h; + })(c.text || (c.text = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(b) { - (function(g) { - var k = function(g) { - function d() { - b.display.DisplayObject.instanceConstructorNoInitialize.call(this); +(function(c) { + (function(c) { + (function(a) { + (function(a) { + (function(c) { + var h = function(c) { + function l() { + a.display.DisplayObject.instanceConstructorNoInitialize.call(this); } - __extends(d, g); - d.prototype._canHaveTextContent = function() { + __extends(l, c); + l.prototype._canHaveTextContent = function() { return!0; }; - d.prototype._getTextContent = function() { + l.prototype._getTextContent = function() { return this._textContent; }; - Object.defineProperty(d.prototype, "text", {get:function() { + Object.defineProperty(l.prototype, "text", {get:function() { return this._textContent.plainText; }, enumerable:!0, configurable:!0}); - d.classInitializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - d.initializer = function(a) { + l.classInitializer = null; + l.classSymbols = null; + l.instanceSymbols = null; + l.initializer = function(a) { this._textContent = null; a && this._setStaticContentFromSymbol(a); }; - return d; - }(b.display.DisplayObject); - g.StaticText = k; - })(b.text || (b.text = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.text.StyleSheet"); + return l; + }(a.display.DisplayObject); + c.StaticText = h; + })(a.text || (a.text = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(v) { + function p(a, e, c) { + for (;e < c;) { + switch(a[e]) { + case " ": + ; + case "\n": + ; + case "\r": + ; + case "\t": + e++; + break; + default: + return e; + } } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(f.events.EventDispatcher); - g.StyleSheet = m; - })(f.text || (f.text = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + l(e === c); + return c; + } + var u = c.AVM2.Runtime.asCoerceString, l = c.Debug.assert, e = function(e) { + function c() { + h.events.EventDispatcher.instanceConstructorNoInitialize.call(this); + this.clear(); + } + __extends(c, e); + Object.defineProperty(c.prototype, "styleNames", {get:function() { + var a = this._rules, e = [], c; + for (c in a) { + a[c] && e.push(c); + } + return e; + }, enumerable:!0, configurable:!0}); + c.prototype.getStyle = function(e) { + e = u(e); + return(e = this._rules[e.toLowerCase()]) ? a.ASJSON.transformJSValueToAS(e, !1) : null; + }; + c.prototype.applyStyle = function(a, e) { + e = u(e); + var c = this._rules[e.toLowerCase()]; + return c ? a.transform(c) : a; + }; + c.prototype.setStyle = function(e, c) { + "object" === typeof c && (e = u(e), this._rules[e.toLowerCase()] = a.ASJSON.transformASValueToJS(c, !1)); + }; + c.prototype.hasStyle = function(a) { + return!!this._rules[a.toLowerCase()]; + }; + c.prototype.clear = function() { + this._rules = Object.create(null); + }; + c.prototype.transform = function(e) { + if ("object" !== typeof e) { + return null; + } + e = a.ASJSON.transformASValueToJS(e, !1); + var c = new v.TextFormat; + c.transform(e); + return c; + }; + c.prototype.parseCSS = function(a) { + a = u(a) + ""; + for (var e = a.length, c = p(a, 0, e), f = {}, d = [], b = !1, g = "";c < e;) { + var h = a[c++]; + switch(h) { + case "{": + b = !1; + d.push(g); + a: { + var g = a, h = e, m = f; + l(0 < c); + l("{" === g[c - 1]); + var s = {}, t = "", v = !1, M = !1, c = p(g, c, h); + b: for (;c < h;) { + var N = g[c++]; + switch(N) { + case "}": + if (0 < t.length) { + c = -1; + break a; + } + break b; + case ":": + var K = "", y = t, t = "", M = v = !1; + for (;c < h;) { + switch(N = g[c], N) { + case ";": + ; + case "\r": + ; + case "\n": + c++, c = p(g, c, h); + case "}": + s[y] = K; + continue b; + default: + c++, K += N; + } + } + c = -1; + break a; + case "-": + ":" === g[c] ? t += N : M = !0; + break; + case " ": + ; + case "\n": + ; + case "\r": + ; + case "\t": + v = !0; + t += N; + M = !1; + break; + default: + if (v) { + c = -1; + break a; + } + M && (N = N.toUpperCase(), M = !1); + t += N; + } + } + if ("}" !== g[c - 1]) { + c = -1; + } else { + for (g = 0;g < d.length;g++) { + m[d[g]] = s; + } + } + } + if (-1 === c) { + return; + } + l("}" === a[c - 1]); + d = []; + g = ""; + c = p(a, c, e); + break; + case ",": + b = !1; + d.push(g); + g = ""; + c = p(a, c, e); + break; + case " ": + ; + case "\n": + ; + case "\r": + ; + case "\t": + b = !0; + c = p(a, c, e); + break; + default: + if (b) { + return; + } + g += h; + } + } + a = this._rules; + for (g in f) { + a[g.toLowerCase()] = f[g]; + } + }; + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(h.events.EventDispatcher); + v.StyleSheet = e; + })(h.text || (h.text = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function c() { + a.call(this); + } + __extends(c, a); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.LCD = "lcd"; + c.CRT = "crt"; + c.DEFAULT = "default"; + return c; + }(a.ASNative); + c.TextDisplayMode = h; + })(c.text || (c.text = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(v) { + var p = c.Debug.notImplemented, u = c.Debug.assert, l = c.Debug.warning, e = c.Debug.somewhatImplemented, m = c.AVM2.Runtime.throwError, t = c.AVM2.Runtime.asCoerceString, q = c.NumberUtilities.clamp, n = function(f) { function d() { - b.call(this); + a.display.InteractiveObject.instanceConstructorNoInitialize.call(this); } - __extends(d, b); - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - d.LCD = "lcd"; - d.CRT = "crt"; - d.DEFAULT = "default"; - return d; - }(b.ASNative); - f.TextDisplayMode = k; - })(f.text || (f.text = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var s = b.Debug.notImplemented, m = b.Debug.assert, d = b.Debug.somewhatImplemented, a = b.AVM2.Runtime.throwError, c = b.AVM2.Runtime.asCoerceString, n = b.NumberUtilities.clamp, p = function(e) { - function p() { - e.call(this); - s("Dummy Constructor: public flash.text.TextField"); - } - __extends(p, e); - p.prototype._setFillAndLineBoundsFromSymbol = function(a) { - e.prototype._setFillAndLineBoundsFromSymbol.call(this, a); + __extends(d, f); + d.prototype._setFillAndLineBoundsFromSymbol = function(a) { + f.prototype._setFillAndLineBoundsFromSymbol.call(this, a); this._textContent.bounds = this._lineBounds; this._invalidateContent(); }; - p.prototype._setFillAndLineBoundsFromWidthAndHeight = function(a, b) { - e.prototype._setFillAndLineBoundsFromWidthAndHeight.call(this, a, b); + d.prototype._setFillAndLineBoundsFromWidthAndHeight = function(a, d) { + f.prototype._setFillAndLineBoundsFromWidthAndHeight.call(this, a, d); this._textContent.bounds = this._lineBounds; this._invalidateContent(); }; - p.prototype._canHaveTextContent = function() { + d.prototype._canHaveTextContent = function() { return!0; }; - p.prototype._getTextContent = function() { + d.prototype._getTextContent = function() { return this._textContent; }; - p.prototype._getContentBounds = function(a) { - "undefined" === typeof a && (a = !0); + d.prototype._getContentBounds = function(a) { + void 0 === a && (a = !0); this._ensureLineMetrics(); - return e.prototype._getContentBounds.call(this, a); + return f.prototype._getContentBounds.call(this, a); }; - p.prototype._containsPointDirectly = function(a, b) { - m(this._getContentBounds().contains(a, b)); + d.prototype._containsPointDirectly = function(a, d, e, c) { + u(this._getContentBounds().contains(a, d)); return!0; }; - p.prototype._invalidateContent = function() { - this._textContent.flags & b.TextContentFlags.Dirty && this._setFlags(8388608); + d.prototype._invalidateContent = function() { + this._textContent.flags & c.TextContentFlags.Dirty && this._setFlags(8388608); }; - p.isFontCompatible = function(a, b) { - c(a); - c(b); - d("flash.text.TextField.isFontCompatible"); - return!0; + d.isFontCompatible = function(a, d) { + a = t(a); + d = t(d); + var e = v.Font.getByNameAndStyle(a, d); + return e ? e.fontStyle === d : !1; }; - Object.defineProperty(p.prototype, "alwaysShowSelection", {get:function() { + Object.defineProperty(d.prototype, "alwaysShowSelection", {get:function() { return this._alwaysShowSelection; }, set:function(a) { - d("public flash.text.TextField::set alwaysShowSelection"); this._alwaysShowSelection = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "antiAliasType", {get:function() { + Object.defineProperty(d.prototype, "antiAliasType", {get:function() { return this._antiAliasType; - }, set:function(b) { - d("public flash.text.TextField::set antiAliasType"); - b = c(b); - 0 > g.AntiAliasType.toNumber(b) && a("ArgumentError", k.Errors.InvalidParamError, "antiAliasType"); - this._antiAliasType = b; + }, set:function(a) { + a = t(a); + 0 > v.AntiAliasType.toNumber(a) && m("ArgumentError", h.Errors.InvalidParamError, "antiAliasType"); + this._antiAliasType = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "autoSize", {get:function() { + Object.defineProperty(d.prototype, "autoSize", {get:function() { return this._autoSize; - }, set:function(b) { - b = c(b); - b !== this._autoSize && (0 > g.TextFieldAutoSize.toNumber(b) && a("ArgumentError", k.Errors.InvalidParamError, "autoSize"), this._autoSize = b, this._textContent.autoSize = g.TextFieldAutoSize.toNumber(b), this._invalidateContent()); + }, set:function(a) { + a = t(a); + a !== this._autoSize && (0 > v.TextFieldAutoSize.toNumber(a) && m("ArgumentError", h.Errors.InvalidParamError, "autoSize"), this._autoSize = a, this._textContent.autoSize = v.TextFieldAutoSize.toNumber(a), this._invalidateContent(), this._ensureLineMetrics()); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "background", {get:function() { + Object.defineProperty(d.prototype, "background", {get:function() { return this._background; }, set:function(a) { a = !!a; a !== this._background && (this._background = a, this._textContent.backgroundColor = a ? this._backgroundColor : 0, this._setDirtyFlags(8388608)); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "backgroundColor", {get:function() { + Object.defineProperty(d.prototype, "backgroundColor", {get:function() { return this._backgroundColor >> 8; }, set:function(a) { a = (a << 8 | 255) >>> 0; a !== this._backgroundColor && (this._backgroundColor = a, this._background && (this._textContent.backgroundColor = a, this._setDirtyFlags(8388608))); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "border", {get:function() { + Object.defineProperty(d.prototype, "border", {get:function() { return this._border; }, set:function(a) { a = !!a; a !== this._border && (this._border = a, this._textContent.borderColor = a ? this._borderColor : 0, this._setDirtyFlags(8388608)); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "borderColor", {get:function() { + Object.defineProperty(d.prototype, "borderColor", {get:function() { return this._borderColor >> 8; }, set:function(a) { a = (a << 8 | 255) >>> 0; a !== this._borderColor && (this._borderColor = a, this._border && (this._textContent.borderColor = a, this._setDirtyFlags(8388608))); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "bottomScrollV", {get:function() { - s("public flash.text.TextField::get bottomScrollV"); + Object.defineProperty(d.prototype, "bottomScrollV", {get:function() { + return this._bottomScrollV; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "caretIndex", {get:function() { - s("public flash.text.TextField::get caretIndex"); + Object.defineProperty(d.prototype, "caretIndex", {get:function() { + p("public flash.text.TextField::get caretIndex"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "condenseWhite", {get:function() { - d("public flash.text.TextField::get condenseWhite"); + Object.defineProperty(d.prototype, "condenseWhite", {get:function() { return this._condenseWhite; }, set:function(a) { this._condenseWhite = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "defaultTextFormat", {get:function() { + Object.defineProperty(d.prototype, "defaultTextFormat", {get:function() { return this._textContent.defaultTextFormat.clone(); }, set:function(a) { this._textContent.defaultTextFormat.merge(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "embedFonts", {get:function() { + Object.defineProperty(d.prototype, "embedFonts", {get:function() { return this._embedFonts; }, set:function(a) { this._embedFonts = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "gridFitType", {get:function() { - d("public flash.text.TextField::get gridFitType"); + Object.defineProperty(d.prototype, "gridFitType", {get:function() { return this._gridFitType; - }, set:function(a) { - a = c(a); - m(0 <= f.text.GridFitType.toNumber(a)); - d("public flash.text.TextField::set gridFitType"); - this._gridFitType = a; + }, set:function(b) { + b = t(b); + u(0 <= a.text.GridFitType.toNumber(b)); + this._gridFitType = b; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "htmlText", {get:function() { + Object.defineProperty(d.prototype, "htmlText", {get:function() { return this._htmlText; }, set:function(a) { - d("public flash.text.TextField::set htmlText"); - a = c(a); + a = t(a); this._symbol && (this._textContent.defaultTextFormat.bold = !1, this._textContent.defaultTextFormat.italic = !1); - this._textContent.parseHtml(a, this._multiline); + this._textContent.parseHtml(a, this._styleSheet, this._multiline); this._htmlText = a; this._invalidateContent(); this._ensureLineMetrics(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "length", {get:function() { + Object.defineProperty(d.prototype, "length", {get:function() { return this._length; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "textInteractionMode", {get:function() { - s("public flash.text.TextField::get textInteractionMode"); + Object.defineProperty(d.prototype, "textInteractionMode", {get:function() { + p("public flash.text.TextField::get textInteractionMode"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "maxChars", {get:function() { + Object.defineProperty(d.prototype, "maxChars", {get:function() { return this._maxChars; }, set:function(a) { this._maxChars = a | 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "maxScrollH", {get:function() { + Object.defineProperty(d.prototype, "maxScrollH", {get:function() { + this._ensureLineMetrics(); return this._maxScrollH; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "maxScrollV", {get:function() { + Object.defineProperty(d.prototype, "maxScrollV", {get:function() { + this._ensureLineMetrics(); return this._maxScrollV; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "mouseWheelEnabled", {get:function() { + Object.defineProperty(d.prototype, "mouseWheelEnabled", {get:function() { return this._mouseWheelEnabled; }, set:function(a) { - d("public flash.text.TextField::set mouseWheelEnabled"); + e("public flash.text.TextField::set mouseWheelEnabled"); this._mouseWheelEnabled = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "multiline", {get:function() { + Object.defineProperty(d.prototype, "multiline", {get:function() { return this._multiline; }, set:function(a) { this._multiline = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "numLines", {get:function() { + Object.defineProperty(d.prototype, "numLines", {get:function() { return this._numLines; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "displayAsPassword", {get:function() { + Object.defineProperty(d.prototype, "displayAsPassword", {get:function() { return this._displayAsPassword; }, set:function(a) { - d("public flash.text.TextField::set displayAsPassword"); + e("public flash.text.TextField::set displayAsPassword"); this._displayAsPassword = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "restrict", {get:function() { + Object.defineProperty(d.prototype, "restrict", {get:function() { return this._restrict; }, set:function(a) { - d("public flash.text.TextField::set restrict"); - this._restrict = c(a); + e("public flash.text.TextField::set restrict"); + this._restrict = t(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "scrollH", {get:function() { - d("public flash.text.TextField::get scrollH"); - return this._scrollH; + Object.defineProperty(d.prototype, "scrollH", {get:function() { + return this._textContent.scrollH; }, set:function(a) { a |= 0; - d("public flash.text.TextField::set scrollH"); - this._scrollH = a; + this._ensureLineMetrics(); + this._textContent.scrollH = q(Math.abs(a), 0, this._maxScrollH); + this._invalidateContent(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "scrollV", {get:function() { - d("public flash.text.TextField::get scrollV"); - return this._scrollV; + Object.defineProperty(d.prototype, "scrollV", {get:function() { + return this._textContent.scrollV; }, set:function(a) { a |= 0; - d("public flash.text.TextField::set scrollV"); - this._scrollV = a; + this._ensureLineMetrics(); + this._textContent.scrollV = q(a, 1, this._maxScrollV); + this._invalidateContent(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "selectable", {get:function() { + Object.defineProperty(d.prototype, "selectable", {get:function() { return this._selectable; }, set:function(a) { - d("public flash.text.TextField::set selectable"); this._selectable = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "selectionBeginIndex", {get:function() { + Object.defineProperty(d.prototype, "selectedText", {get:function() { + return this._textContent.plainText.substring(this._selectionBeginIndex, this._selectionEndIndex); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(d.prototype, "selectionBeginIndex", {get:function() { return this._selectionBeginIndex; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "selectionEndIndex", {get:function() { + Object.defineProperty(d.prototype, "selectionEndIndex", {get:function() { return this._selectionEndIndex; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "sharpness", {get:function() { + Object.defineProperty(d.prototype, "sharpness", {get:function() { return this._sharpness; }, set:function(a) { - this._sharpness = n(+a, -400, 400); + this._sharpness = q(+a, -400, 400); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "styleSheet", {get:function() { - s("public flash.text.TextField::get styleSheet"); + Object.defineProperty(d.prototype, "styleSheet", {get:function() { + return this._styleSheet; }, set:function(a) { - s("public flash.text.TextField::set styleSheet"); + this._styleSheet = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "text", {get:function() { + Object.defineProperty(d.prototype, "text", {get:function() { return this._textContent.plainText; }, set:function(a) { - d("public flash.text.TextField::set text"); - this._textContent.plainText = c(a); - this._invalidateContent(); - this._ensureLineMetrics(); + a = t(a); + a !== this._textContent.plainText && (this._textContent.plainText = a, this._invalidateContent(), this._ensureLineMetrics()); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "textColor", {get:function() { + Object.defineProperty(d.prototype, "textColor", {get:function() { return 0 > this._textColor ? +this._textContent.defaultTextFormat.color : this._textColor; }, set:function(a) { this._textColor = a >>> 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "textHeight", {get:function() { + Object.defineProperty(d.prototype, "textHeight", {get:function() { this._ensureLineMetrics(); return this._textHeight / 20 | 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "textWidth", {get:function() { + Object.defineProperty(d.prototype, "textWidth", {get:function() { this._ensureLineMetrics(); return this._textWidth / 20 | 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "thickness", {get:function() { + Object.defineProperty(d.prototype, "thickness", {get:function() { return this._thickness; }, set:function(a) { - this._thickness = n(+a, -200, 200); + this._thickness = q(+a, -200, 200); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "type", {get:function() { + Object.defineProperty(d.prototype, "type", {get:function() { return this._type; }, set:function(a) { - this._type = c(a); + this._type = t(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "wordWrap", {get:function() { + Object.defineProperty(d.prototype, "wordWrap", {get:function() { return this._textContent.wordWrap; }, set:function(a) { a = !!a; a !== this._textContent.wordWrap && (this._textContent.wordWrap = !!a, this._invalidateContent()); }, enumerable:!0, configurable:!0}); - Object.defineProperty(p.prototype, "useRichTextClipboard", {get:function() { - s("public flash.text.TextField::get useRichTextClipboard"); + Object.defineProperty(d.prototype, "useRichTextClipboard", {get:function() { + p("public flash.text.TextField::get useRichTextClipboard"); }, set:function(a) { - s("public flash.text.TextField::set useRichTextClipboard"); + p("public flash.text.TextField::set useRichTextClipboard"); }, enumerable:!0, configurable:!0}); - p.prototype._ensureLineMetrics = function() { + d.prototype.copyRichText = function() { + p("public flash.text.TextField::copyRichText"); + }; + d.prototype.pasteRichText = function(a) { + t(a); + p("public flash.text.TextField::pasteRichText"); + }; + d.prototype.getXMLText = function(a, d) { + p("public flash.text.TextField::getXMLText"); + return ""; + }; + d.prototype.insertXMLText = function(a, d, e, c) { + t(e); + p("public flash.text.TextField::insertXMLText"); + }; + d.prototype._ensureLineMetrics = function() { if (this._hasFlags(8388608)) { - var a = b.AVM2.Runtime.AVM2.instance.globals["Shumway.Player.Utils"].syncDisplayObject(this, !1), c = a.readInt(), d = a.readInt(), e = a.readInt(); - this._autoSize !== g.TextFieldAutoSize.NONE && (this._fillBounds.xMin = this._lineBounds.xMin = e, this._fillBounds.xMax = this._lineBounds.xMax = e + c + 80, this._fillBounds.yMax = this._lineBounds.yMax = this._lineBounds.yMin + d + 80); - this._textWidth = c; - this._textHeight = d; + var a = c.AVM2.Runtime.AVM2.instance.globals["Shumway.Player.Utils"].syncDisplayObject(this, !1), d = a.readInt(), e = a.readInt(), f = a.readInt(), k = this._lineBounds; + this._autoSize !== v.TextFieldAutoSize.NONE && (k.xMin = k.xMin = f, k.xMax = k.xMax = f + d + 80, k.yMax = k.yMax = k.yMin + e + 80); + this._textWidth = d; + this._textHeight = e; this._numLines = a.readInt(); this._lineMetricsData = a; + if (this._textHeight > k.height) { + e = d = 1; + a.position = 16; + for (var l = f = 0;l < this._numLines;l++) { + a.position += 8; + var h = a.readInt(), m = a.readInt(), n = a.readInt(), h = h + m + n; + f > k.height / 20 ? d++ : e++; + f += h; + } + this._maxScrollV = d; + this._bottomScrollV = e; + } + this._maxScrollH = this._textWidth > k.width ? (this._textWidth + 80 - k.width) / 20 | 0 : 0; } }; - p.prototype.getCharBoundaries = function(a) { - s("public flash.text.TextField::getCharBoundaries"); + d.prototype.appendText = function(a) { + this._textContent.appendText(t(a)); + }; + d.prototype.getCharBoundaries = function(b) { + b |= 0; + e("public flash.text.TextField::getCharBoundaries"); + var d = this.textHeight, c = .75 * d; + return new a.geom.Rectangle(b * c, 0, c, d); }; - p.prototype.getCharIndexAtPoint = function(a, b) { - s("public flash.text.TextField::getCharIndexAtPoint"); + d.prototype.getCharIndexAtPoint = function(a, d) { + p("public flash.text.TextField::getCharIndexAtPoint"); }; - p.prototype.getFirstCharInParagraph = function(a) { - s("public flash.text.TextField::getFirstCharInParagraph"); + d.prototype.getFirstCharInParagraph = function(a) { + p("public flash.text.TextField::getFirstCharInParagraph"); }; - p.prototype.getLineIndexAtPoint = function(a, b) { - s("public flash.text.TextField::getLineIndexAtPoint"); + d.prototype.getLineIndexAtPoint = function(a, d) { + p("public flash.text.TextField::getLineIndexAtPoint"); }; - p.prototype.getLineIndexOfChar = function(a) { - s("public flash.text.TextField::getLineIndexOfChar"); + d.prototype.getLineIndexOfChar = function(a) { + p("public flash.text.TextField::getLineIndexOfChar"); }; - p.prototype.getLineLength = function(a) { - s("public flash.text.TextField::getLineLength"); + d.prototype.getLineLength = function(a) { + p("public flash.text.TextField::getLineLength"); }; - p.prototype.getLineMetrics = function() { - var b; - b = 0; - (0 > b || b > this._numLines - 1) && a("RangeError", k.Errors.ParamRangeError); + d.prototype.getLineMetrics = function() { + var a; + a = 0; + (0 > a || a > this._numLines - 1) && m("RangeError", h.Errors.ParamRangeError); this._ensureLineMetrics(); - var c = this._lineMetricsData; - c.position = 12 + 20 * b; - b = c.readInt(); - var d = c.readInt(), e = c.readInt(), h = c.readInt(), c = c.readInt(); - return new g.TextLineMetrics(b, d, e + h + c, e, h, c); - }; - p.prototype.getLineOffset = function(a) { - s("public flash.text.TextField::getLineOffset"); - }; - p.prototype.getLineText = function(a) { - s("public flash.text.TextField::getLineText"); - }; - p.prototype.getParagraphLength = function(a) { - s("public flash.text.TextField::getParagraphLength"); - }; - p.prototype.getTextFormat = function(a, b) { - s("public flash.text.TextField::getTextFormat"); - }; - p.prototype.getTextRuns = function(a, b) { - "undefined" === typeof a && (a = 0); - "undefined" === typeof b && (b = 2147483647); - for (var c = this._textContent.textRuns, d = [], h = 0;h < c.length;h++) { - var e = c[h]; - e.beginIndex >= a && e.endIndex <= b && d.push(e.clone()); + var d = this._lineMetricsData; + d.position = 16 + 20 * a; + a = d.readInt() + this._lineBounds.xMin + 2; + var e = d.readInt(), c = d.readInt(), f = d.readInt(), d = d.readInt(); + return new v.TextLineMetrics(a, e, c + f + d, c, f, d); + }; + d.prototype.getLineOffset = function(a) { + p("public flash.text.TextField::getLineOffset"); + }; + d.prototype.getLineText = function(a) { + p("public flash.text.TextField::getLineText"); + }; + d.prototype.getParagraphLength = function(a) { + p("public flash.text.TextField::getParagraphLength"); + }; + d.prototype.getTextFormat = function(a, d) { + void 0 === a && (a = -1); + void 0 === d && (d = -1); + a |= 0; + d |= 0; + var e = this._textContent.plainText.length; + 0 > a ? (a = 0, 0 > d && (d = e)) : 0 > d && (d = a + 1); + (d <= a || d > e) && m("RangeError", h.Errors.ParamRangeError); + for (var c, e = this._textContent.textRuns, f = 0;f < e.length;f++) { + var k = e[f]; + k.intersects(a, d) && (c ? c.intersect(k.textFormat) : c = k.textFormat.clone()); } - return d; + return c; + }; + d.prototype.getTextRuns = function(a, d) { + void 0 === a && (a = 0); + void 0 === d && (d = 2147483647); + for (var e = this._textContent.textRuns, c = [], f = 0;f < e.length;f++) { + var k = e[f]; + k.beginIndex >= a && k.endIndex <= d && c.push(k.clone()); + } + return c; }; - p.prototype.getRawText = function() { - s("public flash.text.TextField::getRawText"); + d.prototype.getRawText = function() { + p("public flash.text.TextField::getRawText"); }; - p.prototype.replaceSelectedText = function(a) { - s("public flash.text.TextField::replaceSelectedText"); + d.prototype.replaceSelectedText = function(a) { + this.replaceText(this._selectionBeginIndex, this._selectionEndIndex, "" + a); }; - p.prototype.replaceText = function(a, b, c) { + d.prototype.replaceText = function(a, d, e) { a |= 0; - b |= 0; - c = "" + c; - d("public flash.text.TextField::replaceText"); - var e = this._textContent.plainText; - this._textContent.plainText = e.substring(0, a) + c + e.substring(b); - this._invalidateContent(); - this._ensureLineMetrics(); + d |= 0; + 0 > a || 0 > d || (this._textContent.replaceText(a, d, "" + e), this._invalidateContent(), this._ensureLineMetrics()); }; - p.prototype.setSelection = function(a, b) { - d("public flash.text.TextField::setSelection"); + d.prototype.setSelection = function(a, d) { this._selectionBeginIndex = a | 0; - this._selectionEndIndex = b | 0; + this._selectionEndIndex = d | 0; }; - p.prototype.setTextFormat = function(a, b, c) { - d("public flash.text.TextField::setTextFormat"); + d.prototype.setTextFormat = function(a, d, e) { + void 0 === d && (d = -1); + void 0 === e && (e = -1); + d |= 0; + e |= 0; + var c = this._textContent.plainText, f = c.length; + 0 > d ? (d = 0, 0 > e && (e = f)) : 0 > e && (e = d + 1); + (d > f || e > f) && m("RangeError", h.Errors.ParamRangeError); + e <= d || (this._textContent.replaceText(d, e, c.substring(d, e), a), this._invalidateContent(), this._ensureLineMetrics()); }; - p.prototype.getImageReference = function(a) { - s("public flash.text.TextField::getImageReference"); + d.prototype.getImageReference = function(a) { + p("public flash.text.TextField::getImageReference"); }; - p.classSymbols = null; - p.instanceSymbols = null; - p.classInitializer = null; - p.initializer = function(a) { + d.classSymbols = null; + d.instanceSymbols = null; + d.classInitializer = null; + d.initializer = function(b) { this._alwaysShowSelection = !1; - this._antiAliasType = g.AntiAliasType.NORMAL; - this._autoSize = g.TextFieldAutoSize.NONE; + this._antiAliasType = v.AntiAliasType.NORMAL; + this._autoSize = v.TextFieldAutoSize.NONE; this._background = !1; this._backgroundColor = 4294967295; this._border = !1; @@ -33968,286 +41593,353 @@ this._bottomScrollV = 1; this._caretIndex = 0; this._embedFonts = this._condenseWhite = !1; - this._gridFitType = g.GridFitType.PIXEL; + this._gridFitType = v.GridFitType.PIXEL; this._htmlText = ""; this._length = 0; - this._textInteractionMode = g.TextInteractionMode.NORMAL; + this._textInteractionMode = v.TextInteractionMode.NORMAL; this._maxScrollH = this._maxChars = 0; this._maxScrollV = 1; this._multiline = this._mouseWheelEnabled = !1; this._numLines = 1; this._displayAsPassword = !1; this._restrict = null; - this._scrollH = 0; - this._scrollV = 1; this._selectable = !0; this._selectedText = ""; this._sharpness = this._selectionEndIndex = this._selectionBeginIndex = 0; this._styleSheet = null; this._textColor = -1; this._thickness = this._textWidth = this._textHeight = 0; - this._type = g.TextFieldType.DYNAMIC; + this._type = v.TextFieldType.DYNAMIC; this._useRichTextClipboard = !1; - var c = new f.text.TextFormat("Times Roman", 12, 0, !1, !1, !1, "", "", g.TextFormatAlign.LEFT); - this._textContent = new b.TextContent(c); + var d = new a.text.TextFormat(v.Font.DEFAULT_FONT_SERIF, 12, 0, !1, !1, !1, "", "", v.TextFormatAlign.LEFT); + this._textContent = new c.TextContent(d); this._lineMetricsData = null; - a ? (this._setFillAndLineBoundsFromSymbol(a), c.color = a.color, c.size = a.size / 20 | 0, c.font = a.font, c.align = a.align, c.leftMargin = a.leftMargin / 20 | 0, c.rightMargin = a.rightMargin / 20 | 0, c.indent = a.indent / 20 | 0, c.leading = a.leading / 20 | 0, this._multiline = a.multiline, this._embedFonts = a.embedFonts, this._selectable = a.selectable, this._displayAsPassword = a.displayAsPassword, this._type = a.type, this._maxChars = a.maxChars, a.border && (this.border = - this.background = !0), a.html ? this.htmlText = a.initialText : this.text = a.initialText, this.wordWrap = a.wordWrap, this.autoSize = a.autoSize) : this._setFillAndLineBoundsFromWidthAndHeight(2E3, 2E3); + b ? (this._setFillAndLineBoundsFromSymbol(b), d.color = b.color, d.size = b.size / 20 | 0, d.font = b.face, d.bold = b.bold, d.italic = b.italic, d.align = b.align, d.leftMargin = b.leftMargin / 20 | 0, d.rightMargin = b.rightMargin / 20 | 0, d.indent = b.indent / 20 | 0, d.leading = b.leading / 20 | 0, this._multiline = b.multiline, this._embedFonts = b.embedFonts, this._selectable = b.selectable, this._displayAsPassword = b.displayAsPassword, this._type = b.type, this._maxChars = + b.maxChars, b.border && (this.border = this.background = !0), b.html ? this.htmlText = b.initialText : this.text = b.initialText, this.wordWrap = b.wordWrap, this.autoSize = b.autoSize) : this._setFillAndLineBoundsFromWidthAndHeight(2E3, 2E3); + }; + return d; + }(a.display.InteractiveObject); + v.TextField = n; + n = function(e) { + function d(b) { + e.call(this, b, a.text.TextField, !0); + this.size = this.color = 0; + this.face = ""; + this.italic = this.bold = !1; + this.align = a.text.TextFormatAlign.LEFT; + this.leading = this.indent = this.rightMargin = this.leftMargin = 0; + this.embedFonts = this.wordWrap = this.multiline = !1; + this.selectable = !0; + this.border = !1; + this.initialText = ""; + this.displayAsPassword = this.html = !1; + this.type = a.text.TextFieldType.DYNAMIC; + this.maxChars = 0; + this.autoSize = a.text.TextFieldAutoSize.NONE; + this.textContent = this.variableName = null; + } + __extends(d, e); + d.FromTextData = function(b, e) { + var f = new d(b); + f._setBoundsFromData(b); + var k = b.tag; + if (b.static && (f.dynamic = !1, f.symbolClass = a.text.StaticText, k.initialText)) { + var h = new c.TextContent; + h.bounds = f.lineBounds; + h.parseHtml(k.initialText, null, !1); + new a.geom.Matrix; + h.matrix = new a.geom.Matrix; + h.matrix.copyFromUntyped(b.matrix); + h.coords = b.coords; + f.textContent = h; + } + k.hasColor && (f.color = k.color >>> 8); + k.hasFont && (f.size = k.fontHeight, (h = e.getSymbolById(k.fontId)) ? (f.face = k.useOutlines ? h.name : "swffont" + h.syncId, f.bold = h.bold, f.italic = h.italic) : l("Font " + k.fontId + " is not defined.")); + k.hasLayout && (f.align = a.text.TextFormatAlign.fromNumber(k.align), f.leftMargin = k.leftMargin, f.rightMargin = k.rightMargin, f.indent = k.indent, f.leading = k.leading); + f.multiline = !!k.multiline; + f.wordWrap = !!k.wordWrap; + f.embedFonts = !!k.useOutlines; + f.selectable = !k.noSelect; + f.border = !!k.border; + k.hasText && (f.initialText = k.initialText); + f.html = !!k.html; + f.displayAsPassword = !!k.password; + f.type = k.readonly ? a.text.TextFieldType.DYNAMIC : a.text.TextFieldType.INPUT; + k.hasMaxLength && (f.maxChars = k.maxLength); + f.autoSize = k.autoSize ? a.text.TextFieldAutoSize.LEFT : a.text.TextFieldAutoSize.NONE; + f.variableName = k.variableName; + return f; + }; + d.FromLabelData = function(a, e) { + for (var f = a.fillBounds, l = a.records, h = a.coords = [], m = "", n = 12, q = "Times Roman", p = !1, s = !1, t = 0, v = 0, L = 0, H, J = 0;J < l.length;J++) { + var C = l[J]; + if (C.eot) { + break; + } + if (C.hasFont) { + var E = e.getSymbolById(C.fontId); + E ? (H = E.codes, n = C.fontHeight, E.originalSize || (n /= 20), q = E.metrics ? "swffont" + E.syncId : E.name, p = E.bold, s = E.italic) : c.Debug.warning("Label " + a.id + "refers to undefined font symbol " + C.fontId); + } + C.hasColor && (t = C.color >>> 8); + C.hasMoveX && (v = C.moveX, v < f.xMin && (f.xMin = v)); + C.hasMoveY && (L = C.moveY, L < f.yMin && (f.yMin = L)); + for (var E = "", C = C.entries, F = 0, I;I = C[F++];) { + var G = H[I.glyphIndex]; + u(G, "undefined label glyph"); + G = String.fromCharCode(G); + E += k[G] || G; + h.push(v, L); + v += I.advance; + } + s && (E = "" + E + ""); + p && (E = "" + E + ""); + m += '' + E + ""; + } + a.tag.initialText = m; + return d.FromTextData(a, e); }; - return p; - }(f.display.InteractiveObject); - g.TextField = p; - })(f.text || (f.text = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + return d; + }(c.Timeline.DisplaySymbol); + v.TextSymbol = n; + var k = {"<":"<", ">":">", "&":"&"}; + })(a.text || (a.text = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { - function d() { - b.call(this); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function c() { + a.call(this); } - __extends(d, b); - d.fromNumber = function(a) { + __extends(c, a); + c.fromNumber = function(a) { switch(a) { case 0: - return d.NONE; + return c.NONE; case 1: - return d.CENTER; + return c.CENTER; case 2: - return d.LEFT; + return c.LEFT; case 3: - return d.RIGHT; + return c.RIGHT; default: return null; } }; - d.toNumber = function(a) { + c.toNumber = function(a) { switch(a) { - case d.NONE: + case c.NONE: return 0; - case d.CENTER: + case c.CENTER: return 1; - case d.LEFT: + case c.LEFT: return 2; - case d.RIGHT: + case c.RIGHT: return 3; default: return-1; } }; - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - d.NONE = "none"; - d.LEFT = "left"; - d.CENTER = "center"; - d.RIGHT = "right"; - return d; - }(b.ASNative); - f.TextFieldAutoSize = k; - })(f.text || (f.text = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.NONE = "none"; + c.LEFT = "left"; + c.CENTER = "center"; + c.RIGHT = "right"; + return c; + }(a.ASNative); + c.TextFieldAutoSize = h; + })(c.text || (c.text = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { - function d() { - b.call(this); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function c() { + a.call(this); } - __extends(d, b); - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - d.INPUT = "input"; - d.DYNAMIC = "dynamic"; - return d; - }(b.ASNative); - f.TextFieldType = k; - })(f.text || (f.text = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(t) { - var s = b.AVM2.Runtime.asCoerceString, m = b.NumberUtilities.roundHalfEven, d = b.AVM2.Runtime.throwError, a = function(a) { - function b(a, c, d, f, g, k, m, h, n, s, t, I, C) { - "undefined" === typeof a && (a = null); - "undefined" === typeof c && (c = null); - "undefined" === typeof d && (d = null); - "undefined" === typeof f && (f = null); - "undefined" === typeof g && (g = null); - "undefined" === typeof k && (k = null); - "undefined" === typeof m && (m = null); - "undefined" === typeof h && (h = null); - "undefined" === typeof n && (n = null); - "undefined" === typeof s && (s = null); - "undefined" === typeof t && (t = null); - "undefined" === typeof I && (I = null); - "undefined" === typeof C && (C = null); + __extends(c, a); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.INPUT = "input"; + c.DYNAMIC = "dynamic"; + return c; + }(a.ASNative); + c.TextFieldType = h; + })(c.text || (c.text = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(s) { + (function(v) { + var p = c.AVM2.Runtime.asCoerceString, u = c.NumberUtilities.roundHalfEven, l = c.AVM2.Runtime.throwError, e = function(a) { + function e(a, c, k, f, d, b, g, l, h, m, p, s, u) { + void 0 === a && (a = null); + void 0 === c && (c = null); + void 0 === k && (k = null); + void 0 === f && (f = null); + void 0 === d && (d = null); + void 0 === b && (b = null); + void 0 === g && (g = null); + void 0 === l && (l = null); + void 0 === h && (h = null); + void 0 === m && (m = null); + void 0 === p && (p = null); + void 0 === s && (s = null); + void 0 === u && (u = null); this.font = a; this.size = c; - this.color = d; + this.color = k; this.bold = f; - this.italic = g; - this.underline = k; - this.url = m; - this.target = h; - this.align = n; - this.leftMargin = s; - this.rightMargin = t; - this.indent = I; - this.leading = C; + this.italic = d; + this.underline = b; + this.url = g; + this.target = l; + this.align = h; + this.leftMargin = m; + this.rightMargin = p; + this.indent = s; + this.leading = u; } - __extends(b, a); - b.prototype.as2GetTextExtent = function(a, c) { - b.measureTextField || (b.measureTextField = new f.text.TextField, b.measureTextField._multiline = !0); - var d = b.measureTextField; - !isNaN(c) && 0 < c ? (d.width = c + 4, d._wordWrap = !0) : d._wordWrap = !1; - d.defaultTextFormat = this; - d.text = a; - var g = {}, k = d.textWidth, m = d.textHeight; - g.asSetPublicProperty("width", k); - g.asSetPublicProperty("height", m); - g.asSetPublicProperty("textFieldWidth", k + 4); - g.asSetPublicProperty("textFieldHeight", m + 4); - d = d.getLineMetrics(); - g.asSetPublicProperty("ascent", d.ascent); - g.asSetPublicProperty("descent", d.descent); - return g; - }; - Object.defineProperty(b.prototype, "align", {get:function() { + __extends(e, a); + Object.defineProperty(e.prototype, "align", {get:function() { return this._align; }, set:function(a) { - this._align = a = s(a); + this._align = a = p(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "blockIndent", {get:function() { + Object.defineProperty(e.prototype, "blockIndent", {get:function() { return this._blockIndent; }, set:function(a) { - this._blockIndent = b.coerceNumber(a); + this._blockIndent = e.coerceNumber(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "bold", {get:function() { + Object.defineProperty(e.prototype, "bold", {get:function() { return this._bold; }, set:function(a) { - this._bold = b.coerceBoolean(a); + this._bold = e.coerceBoolean(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "bullet", {get:function() { + Object.defineProperty(e.prototype, "bullet", {get:function() { return this._bullet; }, set:function(a) { - this._bullet = b.coerceBoolean(a); + this._bullet = e.coerceBoolean(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "color", {get:function() { + Object.defineProperty(e.prototype, "color", {get:function() { return this._color; }, set:function(a) { this._color = +a | 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "display", {get:function() { + Object.defineProperty(e.prototype, "display", {get:function() { return this._display; }, set:function(a) { - this._display = s(a); + this._display = p(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "font", {get:function() { + Object.defineProperty(e.prototype, "font", {get:function() { return this._font; }, set:function(a) { - this._font = s(a); + this._font = p(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "indent", {get:function() { + Object.defineProperty(e.prototype, "style", {get:function() { + return this._bold && this._italic ? v.FontStyle.BOLD_ITALIC : this._bold ? v.FontStyle.BOLD : this._italic ? v.FontStyle.ITALIC : v.FontStyle.REGULAR; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "indent", {get:function() { return this._indent; }, set:function(a) { - this._indent = b.coerceNumber(a); + this._indent = e.coerceNumber(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "italic", {get:function() { + Object.defineProperty(e.prototype, "italic", {get:function() { return this._italic; }, set:function(a) { - this._italic = b.coerceBoolean(a); + this._italic = e.coerceBoolean(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "kerning", {get:function() { + Object.defineProperty(e.prototype, "kerning", {get:function() { return this._kerning; }, set:function(a) { - this._kerning = b.coerceBoolean(a); + this._kerning = e.coerceBoolean(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "leading", {get:function() { + Object.defineProperty(e.prototype, "leading", {get:function() { return this._leading; }, set:function(a) { - this._leading = b.coerceNumber(a); + this._leading = e.coerceNumber(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "leftMargin", {get:function() { + Object.defineProperty(e.prototype, "leftMargin", {get:function() { return this._leftMargin; }, set:function(a) { - this._leftMargin = b.coerceNumber(a); + this._leftMargin = e.coerceNumber(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "letterSpacing", {get:function() { + Object.defineProperty(e.prototype, "letterSpacing", {get:function() { return this._letterSpacing; }, set:function(a) { - this._letterSpacing = b.coerceBoolean(a); + this._letterSpacing = e.coerceBoolean(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "rightMargin", {get:function() { + Object.defineProperty(e.prototype, "rightMargin", {get:function() { return this._rightMargin; }, set:function(a) { - this._rightMargin = b.coerceNumber(a); + this._rightMargin = e.coerceNumber(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "size", {get:function() { + Object.defineProperty(e.prototype, "size", {get:function() { return this._size; }, set:function(a) { - this._size = b.coerceNumber(a); + this._size = e.coerceNumber(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "tabStops", {get:function() { + Object.defineProperty(e.prototype, "tabStops", {get:function() { return this._tabStops; }, set:function(a) { - a instanceof Array || d("ArgumentError", k.Errors.CheckTypeFailedError, a, "Array"); + a instanceof Array || l("ArgumentError", h.Errors.CheckTypeFailedError, a, "Array"); this._tabStops = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "target", {get:function() { + Object.defineProperty(e.prototype, "target", {get:function() { return this._target; }, set:function(a) { - this._target = s(a); + this._target = p(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "underline", {get:function() { + Object.defineProperty(e.prototype, "underline", {get:function() { return this._underline; }, set:function(a) { - this._underline = b.coerceBoolean(a); + this._underline = e.coerceBoolean(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "url", {get:function() { + Object.defineProperty(e.prototype, "url", {get:function() { return this._url; }, set:function(a) { - this._url = s(a); + this._url = p(a); }, enumerable:!0, configurable:!0}); - b.coerceNumber = function(a) { - return void 0 == a ? null : isNaN(a) || 268435455 < a ? -2147483648 : m(a); + e.coerceNumber = function(a) { + return void 0 == a ? null : isNaN(a) || 268435455 < a ? -2147483648 : u(a); }; - b.coerceBoolean = function(a) { + e.coerceBoolean = function(a) { return void 0 == a ? null : !!a; }; - b.prototype.clone = function() { - return new f.text.TextFormat(this.font, this.size, this.color, this.bold, this.italic, this.underline, this.url, this.target, this.align, this.leftMargin, this.rightMargin, this.indent, this.leading); + e.prototype.clone = function() { + return new s.text.TextFormat(this.font, this.size, this.color, this.bold, this.italic, this.underline, this.url, this.target, this.align, this.leftMargin, this.rightMargin, this.indent, this.leading); }; - b.prototype.equals = function(a) { + e.prototype.equals = function(a) { return this._align === a._align && this._blockIndent === a._blockIndent && this._bold === a._bold && this._bullet === a._bullet && this._color === a._color && this._display === a._display && this._font === a._font && this._indent === a._indent && this._italic === a._italic && this._kerning === a._kerning && this._leading === a._leading && this._leftMargin === a._leftMargin && this._letterSpacing === a._letterSpacing && this._rightMargin === a._rightMargin && this._size === a._size && this._tabStops === a._tabStops && this._target === a._target && this._underline === a._underline && this._url === a._url; }; - b.prototype.merge = function(a) { + e.prototype.merge = function(a) { null !== a._align && (this._align = a._align); null !== a._blockIndent && (this._blockIndent = a._blockIndent); null !== a._bold && (this._bold = a._bold); null !== a._bullet && (this._bullet = a._bullet); null !== a._color && (this._color = a._color); null !== a._display && (this._display = a._display); - null !== a._font && (this._font = a._font); + a._font && (this._font = a._font); null !== a._indent && (this._indent = a._indent); null !== a._italic && (this._italic = a._italic); null !== a._kerning && (this._kerning = a._kerning); @@ -34257,4781 +41949,6508 @@ null !== a._rightMargin && (this._rightMargin = a._rightMargin); null !== a._size && (this._size = a._size); null !== a._tabStops && (this._tabStops = a._tabStops); - null !== a._target && (this._target = a._target); + a._target && (this._target = a._target); null !== a._underline && (this._underline = a._underline); - null !== a._url && (this._url = a._url); + a._url && (this._url = a._url); }; - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(g.ASNative); - t.TextFormat = a; - })(f.text || (f.text = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + e.prototype.intersect = function(a) { + a._align !== this._align && (this._align = null); + a._blockIndent !== this._blockIndent && (this._blockIndent = null); + a._bold !== this._bold && (this._bold = null); + a._bullet !== this._bullet && (this._bullet = null); + a._color !== this._color && (this._color = null); + a._display !== this._display && (this._display = null); + a._font !== this._font && (this._font = null); + a._indent !== this._indent && (this._indent = null); + a._italic !== this._italic && (this._italic = null); + a._kerning !== this._kerning && (this._kerning = null); + a._leading !== this._leading && (this._leading = null); + a._leftMargin !== this._leftMargin && (this._leftMargin = null); + a._letterSpacing !== this._letterSpacing && (this._letterSpacing = null); + a._rightMargin !== this._rightMargin && (this._rightMargin = null); + a._size !== this._size && (this._size = null); + a._tabStops !== this._tabStops && (this._tabStops = null); + a._target !== this._target && (this._target = null); + a._underline !== this._underline && (this._underline = null); + a._url !== this._url && (this._url = null); + }; + e.prototype.transform = function(a) { + var e = a.textAlign; + e && (this.align = e); + e = a.fontWeight; + "bold" === e ? this.bold = !0 : "normal" === e && (this.bold = !1); + if (e = a.color) { + if (e = p(e).trim().toLowerCase(), "#" === e[0]) { + for (e = e.substr(1);"0" === e[0];) { + e = e.substr(1); + } + var c = parseInt(e, 16); + c.original_toString(16) === e && (this.color = c); + } + } + if (e = a.display) { + this.display = e; + } + if (e = a.fontFamily) { + this.font = e.replace("sans-serif", "_sans").replace("serif", "_serif"); + } + if (e = a.textIndent) { + this.indent = parseInt(e); + } + e = a.fontStyle; + "italic" === e ? this.italic = !0 : "normal" === e && (this.italic = !1); + e = a.kerning; + this.kerning = "true" === e ? 1 : "false" === e ? 0 : parseInt(e); + if (e = a.leading) { + this.leading = parseInt(e); + } + if (e = a.marginLeft) { + this.leftMargin = parseInt(e); + } + if (e = a.letterSpacing) { + this.letterSpacing = parseFloat(e); + } + if (e = a.marginRight) { + this.rightMargin = parseInt(e); + } + if (e = a.fontSize) { + e = parseInt(e), 0 < e && (this.size = e); + } + e = a.textDecoration; + "none" === e ? this.underline = !1 : "underline" === e && (this.underline = !0); + return this; + }; + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.ASNative); + v.TextFormat = e; + })(s.text || (s.text = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { - function d() { - b.call(this); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function c() { + a.call(this); } - __extends(d, b); - d.fromNumber = function(a) { + __extends(c, a); + c.fromNumber = function(a) { switch(a) { case 0: - return d.LEFT; + return c.LEFT; case 1: - return d.RIGHT; + return c.RIGHT; case 2: - return d.CENTER; + return c.CENTER; case 3: - return d.JUSTIFY; + return c.JUSTIFY; case 4: - return d.START; + return c.START; case 5: - return d.END; + return c.END; default: return null; } }; - d.toNumber = function(a) { + c.toNumber = function(a) { switch(a) { - case d.LEFT: + case c.LEFT: return 0; - case d.RIGHT: + case c.RIGHT: return 1; - case d.CENTER: + case c.CENTER: return 2; - case d.JUSTIFY: + case c.JUSTIFY: return 3; - case d.START: + case c.START: return 4; - case d.END: + case c.END: return 5; default: return-1; } }; - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - d.LEFT = "left"; - d.CENTER = "center"; - d.RIGHT = "right"; - d.JUSTIFY = "justify"; - d.START = "start"; - d.END = "end"; - return d; - }(b.ASNative); - f.TextFormatAlign = k; - })(f.text || (f.text = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.LEFT = "left"; + c.CENTER = "center"; + c.RIGHT = "right"; + c.JUSTIFY = "justify"; + c.START = "start"; + c.END = "end"; + return c; + }(a.ASNative); + c.TextFormatAlign = h; + })(c.text || (c.text = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { - function d() { - b.call(this); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function c() { + a.call(this); } - __extends(d, b); - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - d.INLINE = "inline"; - d.BLOCK = "block"; - return d; - }(b.ASNative); - f.TextFormatDisplay = k; - })(f.text || (f.text = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + __extends(c, a); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.INLINE = "inline"; + c.BLOCK = "block"; + return c; + }(a.ASNative); + c.TextFormatDisplay = h; + })(c.text || (c.text = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(f) { - var k = function(b) { - function d() { - b.call(this); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function c() { + a.call(this); } - __extends(d, b); - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - d.NORMAL = "normal"; - d.SELECTION = "selection"; - return d; - }(b.ASNative); - f.TextInteractionMode = k; - })(f.text || (f.text = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, b, d, e, f, g) { - k("Dummy Constructor: public flash.text.TextLineMetrics"); + __extends(c, a); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.NORMAL = "normal"; + c.SELECTION = "selection"; + return c; + }(a.ASNative); + c.TextInteractionMode = h; + })(c.text || (c.text = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = function(a) { + function e(a, e, c, h, k, f) { + p("public flash.text.TextLineMetrics"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.TextLineMetrics = m; - })(f.text || (f.text = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.ASNative); + h.TextLineMetrics = s; + })(h.text || (h.text = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - (function(k) { - var s = function(b) { - function d(a, b, d) { +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(h) { + var p = function(a) { + function h(a, c, l) { this._beginIndex = a | 0; - this._endIndex = b | 0; - this._textFormat = d; + this._endIndex = c | 0; + this._textFormat = l; } - __extends(d, b); - Object.defineProperty(d.prototype, "beginIndex", {get:function() { + __extends(h, a); + Object.defineProperty(h.prototype, "beginIndex", {get:function() { return this._beginIndex; }, set:function(a) { this._beginIndex = a | 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "endIndex", {get:function() { + Object.defineProperty(h.prototype, "endIndex", {get:function() { return this._endIndex; }, set:function(a) { this._endIndex = a | 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(d.prototype, "textFormat", {get:function() { + Object.defineProperty(h.prototype, "textFormat", {get:function() { return this._textFormat; }, set:function(a) { this._textFormat = a; }, enumerable:!0, configurable:!0}); - d.prototype.clone = function() { - return new f.text.TextRun(this.beginIndex, this.endIndex, this.textFormat); + h.prototype.clone = function() { + return new c.text.TextRun(this.beginIndex, this.endIndex, this.textFormat.clone()); }; - d.classInitializer = null; - d.initializer = null; - d.classSymbols = null; - d.instanceSymbols = null; - return d; - }(b.ASNative); - k.TextRun = s; - })(f.text || (f.text = {})); - })(b.flash || (b.flash = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = function(a) { - function b() { - k("Dummy Constructor: public flash.text.TextSnapshot"); + h.prototype.containsIndex = function(a) { + return a >= this._beginIndex && a < this._endIndex; + }; + h.prototype.intersects = function(a, c) { + return Math.max(this._beginIndex, a) < Math.min(this._endIndex, c); + }; + h.classInitializer = null; + h.initializer = null; + h.classSymbols = null; + h.instanceSymbols = null; + return h; + }(a.ASNative); + h.TextRun = p; + })(c.text || (c.text = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.notImplemented, s = c.Debug.dummyConstructor, l = c.AVM2.Runtime.asCoerceString, e = function(a) { + function e() { + s("public flash.text.TextSnapshot"); } - __extends(b, a); - Object.defineProperty(b.prototype, "charCount", {get:function() { - k("public flash.text.TextSnapshot::get charCount"); + __extends(e, a); + Object.defineProperty(e.prototype, "charCount", {get:function() { + p("public flash.text.TextSnapshot::get charCount"); }, enumerable:!0, configurable:!0}); - b.prototype.findText = function(a, b, c) { - m(b); - k("public flash.text.TextSnapshot::findText"); + e.prototype.findText = function(a, e, c) { + l(e); + p("public flash.text.TextSnapshot::findText"); }; - b.prototype.getSelected = function(a, b) { - k("public flash.text.TextSnapshot::getSelected"); + e.prototype.getSelected = function(a, e) { + p("public flash.text.TextSnapshot::getSelected"); }; - b.prototype.getSelectedText = function(a) { - k("public flash.text.TextSnapshot::getSelectedText"); + e.prototype.getSelectedText = function(a) { + p("public flash.text.TextSnapshot::getSelectedText"); }; - b.prototype.getText = function(a, b, c) { - k("public flash.text.TextSnapshot::getText"); + e.prototype.getText = function(a, e, c) { + p("public flash.text.TextSnapshot::getText"); }; - b.prototype.getTextRunInfo = function(a, b) { - k("public flash.text.TextSnapshot::getTextRunInfo"); + e.prototype.getTextRunInfo = function(a, e) { + p("public flash.text.TextSnapshot::getTextRunInfo"); }; - b.prototype.hitTestTextNearPos = function(a, b, c) { - k("public flash.text.TextSnapshot::hitTestTextNearPos"); + e.prototype.hitTestTextNearPos = function(a, e, c) { + p("public flash.text.TextSnapshot::hitTestTextNearPos"); }; - b.prototype.setSelectColor = function(a) { - k("public flash.text.TextSnapshot::setSelectColor"); + e.prototype.setSelectColor = function(a) { + p("public flash.text.TextSnapshot::setSelectColor"); }; - b.prototype.setSelected = function(a, b, c) { - k("public flash.text.TextSnapshot::setSelected"); + e.prototype.setSelected = function(a, e, c) { + p("public flash.text.TextSnapshot::setSelected"); }; - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(g.ASNative); - f.TextSnapshot = d; - })(f.text || (f.text = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.trace.Trace"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.ASNative); + h.TextSnapshot = e; + })(h.text || (h.text = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.notImplemented, s = c.Debug.dummyConstructor, l = function(a) { + function c() { + s("public flash.trace.Trace"); } - __extends(a, b); - a.setLevel = function(a, b) { - k("public flash.trace.Trace::static setLevel"); + __extends(c, a); + c.setLevel = function(a, e) { + p("public flash.trace.Trace::static setLevel"); }; - a.getLevel = function(a) { - k("public flash.trace.Trace::static getLevel"); + c.getLevel = function(a) { + p("public flash.trace.Trace::static getLevel"); }; - a.setListener = function(a) { - k("public flash.trace.Trace::static setListener"); + c.setListener = function(a) { + p("public flash.trace.Trace::static setListener"); }; - a.getListener = function() { - k("public flash.trace.Trace::static getListener"); - }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.OFF = void 0; - a.METHODS = 1; - a.METHODS_WITH_ARGS = 2; - a.METHODS_AND_LINES = 3; - a.METHODS_AND_LINES_WITH_ARGS = 4; - a.FILE = 1; - a.LISTENER = 2; - return a; - }(g.ASNative); - f.Trace = m; - })(f.trace || (f.trace = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = b.Debug.somewhatImplemented, d = function(a) { - function b() { - k("Dummy Constructor: public flash.ui.ContextMenu"); - } - __extends(b, a); - Object.defineProperty(b.prototype, "builtInItems", {get:function() { - m("public flash.ui.ContextMenu::get builtInItems"); + c.getListener = function() { + p("public flash.trace.Trace::static getListener"); + }; + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.OFF = void 0; + c.METHODS = 1; + c.METHODS_WITH_ARGS = 2; + c.METHODS_AND_LINES = 3; + c.METHODS_AND_LINES_WITH_ARGS = 4; + c.FILE = 1; + c.LISTENER = 2; + return c; + }(a.ASNative); + h.Trace = l; + })(h.trace || (h.trace = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.somewhatImplemented, u = function(c) { + function e() { + a.display.NativeMenu.instanceConstructorNoInitialize.call(this); + this.builtInItems = new h.ContextMenuBuiltInItems; + this.customItems = []; + } + __extends(e, c); + Object.defineProperty(e, "isSupported", {get:function() { + p("ContextMenu::isSupported"); + return!1; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "builtInItems", {get:function() { + p("public flash.ui.ContextMenu::get builtInItems"); return this._builtInItems; }, set:function(a) { - m("public flash.ui.ContextMenu::set builtInItems"); + p("public flash.ui.ContextMenu::set builtInItems"); this._builtInItems = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "customItems", {get:function() { - m("public flash.ui.ContextMenu::get customItems"); + Object.defineProperty(e.prototype, "customItems", {get:function() { + p("public flash.ui.ContextMenu::get customItems"); return this._customItems; }, set:function(a) { - m("public flash.ui.ContextMenu::set customItems"); + p("public flash.ui.ContextMenu::set customItems"); this._customItems = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "link", {get:function() { - k("public flash.ui.ContextMenu::get link"); + Object.defineProperty(e.prototype, "link", {get:function() { + p("public flash.ui.ContextMenu::get link"); + return this._link; + }, set:function(a) { + p("public flash.ui.ContextMenu::set link"); + this._link = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "clipboardMenu", {get:function() { + p("public flash.ui.ContextMenu::get clipboardMenu"); + return this._clipboardMenu; }, set:function(a) { - k("public flash.ui.ContextMenu::set link"); + a = !!a; + p("public flash.ui.ContextMenu::set clipboardMenu"); + this._clipboardMenu = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "clipboardItems", {get:function() { + p("public flash.ui.ContextMenu::get clipboardItems"); + return this._clipboardItems; + }, set:function(a) { + p("public flash.ui.ContextMenu::set clipboardItems"); + this._clipboardItems = a; + }, enumerable:!0, configurable:!0}); + e.prototype.hideBuiltInItems = function() { + var a = this.builtInItems; + a && (a.save = !1, a.zoom = !1, a.quality = !1, a.play = !1, a.loop = !1, a.rewind = !1, a.forwardAndBack = !1, a.print = !1); + }; + e.prototype.clone = function() { + var a = new h.ContextMenu; + a.builtInItems = this.builtInItems.clone(); + this.cloneLinkAndClipboardProperties(a); + for (var e = this.customItems, c = 0;c < e.length;c++) { + a.customItems.push(e[c].clone()); + } + return a; + }; + e.prototype.cloneLinkAndClipboardProperties = function(a) { + p("public flash.ui.ContextMenu::cloneLinkAndClipboardProperties"); + }; + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.display.NativeMenu); + h.ContextMenu = u; + })(a.ui || (a.ui = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + (function(a) { + (function(c) { + (function(c) { + var h = function(a) { + function h() { + this._print = this._forwardAndBack = this._rewind = this._loop = this._play = this._quality = this._zoom = this._save = !0; + } + __extends(h, a); + Object.defineProperty(h.prototype, "save", {get:function() { + return this._save; + }, set:function(a) { + this._save = !!a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "zoom", {get:function() { + return this._zoom; + }, set:function(a) { + this._zoom = !!a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "quality", {get:function() { + return this._quality; + }, set:function(a) { + this._quality = !!a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "play", {get:function() { + return this._play; + }, set:function(a) { + this._play = !!a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "loop", {get:function() { + return this._loop; + }, set:function(a) { + this._loop = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "clipboardMenu", {get:function() { - k("public flash.ui.ContextMenu::get clipboardMenu"); + Object.defineProperty(h.prototype, "rewind", {get:function() { + return this._rewind; }, set:function(a) { - k("public flash.ui.ContextMenu::set clipboardMenu"); + this._rewind = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "clipboardItems", {get:function() { - k("public flash.ui.ContextMenu::get clipboardItems"); + Object.defineProperty(h.prototype, "forwardAndBack", {get:function() { + return this._forwardAndBack; }, set:function(a) { - k("public flash.ui.ContextMenu::set clipboardItems"); + this._forwardAndBack = !!a; }, enumerable:!0, configurable:!0}); - b.prototype.cloneLinkAndClipboardProperties = function(a) { - k("public flash.ui.ContextMenu::cloneLinkAndClipboardProperties"); + Object.defineProperty(h.prototype, "print", {get:function() { + return this._print; + }, set:function(a) { + this._print = !!a; + }, enumerable:!0, configurable:!0}); + h.prototype.clone = function() { + var a = new c.ContextMenuBuiltInItems; + a._save = this._save; + a._zoom = this._zoom; + a._quality = this._quality; + a._play = this._play; + a._loop = this._loop; + a._rewind = this._rewind; + a._forwardAndBack = this._forwardAndBack; + a._print = this._print; + return a; }; - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(f.display.NativeMenu); - g.ContextMenu = d; - })(f.ui || (f.ui = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.ui.ContextMenuBuiltInItems"); - } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.ContextMenuBuiltInItems = m; - })(f.ui || (f.ui = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.ui.ContextMenuClipboardItems"); + h.classInitializer = null; + h.initializer = null; + h.classSymbols = null; + h.instanceSymbols = null; + return h; + }(a.ASNative); + c.ContextMenuBuiltInItems = h; + })(c.ui || (c.ui = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.somewhatImplemented, s = function(a) { + function e() { + this._selectAll = this._clear = this._paste = this._copy = this._cut = !0; } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.ContextMenuClipboardItems = m; - })(f.ui || (f.ui = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.AVM2.Runtime.asCoerceString, m = function(b) { - function a(a, b, d, e) { - "undefined" === typeof b && (b = !1); - "undefined" === typeof d && (d = !0); - "undefined" === typeof e && (e = !0); - this._caption = (a = k(a)) ? a : ""; - this._separatorBefore = !!b; - this._enabled = !!d; - this._visible = !!e; + __extends(e, a); + Object.defineProperty(e.prototype, "cut", {get:function() { + p("cut"); + return this._cut; + }, set:function(a) { + p("cut"); + this._cut = !!a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "copy", {get:function() { + p("copy"); + return this._copy; + }, set:function(a) { + p("copy"); + this._copy = !!a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "paste", {get:function() { + p("paste"); + return this._paste; + }, set:function(a) { + p("paste"); + this._paste = !!a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "clear", {get:function() { + p("clear"); + return this._clear; + }, set:function(a) { + p("clear"); + this._clear = !!a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(e.prototype, "selectAll", {get:function() { + p("selectAll"); + return this._selectAll; + }, set:function(a) { + p("selectAll"); + this._selectAll = !!a; + }, enumerable:!0, configurable:!0}); + e.prototype.clone = function() { + var a = new h.ContextMenuClipboardItems; + a._cut = this._cut; + a._copy = this._copy; + a._paste = this._paste; + a._clear = this._clear; + a._selectAll = this._selectAll; + return a; + }; + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.ASNative); + h.ContextMenuClipboardItems = s; + })(h.ui || (h.ui = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.AVM2.Runtime.asCoerceString, u = function(a) { + function e(a, e, c, h) { + void 0 === e && (e = !1); + void 0 === c && (c = !0); + void 0 === h && (h = !0); + this._caption = (a = p(a)) ? a : ""; + this._separatorBefore = !!e; + this._enabled = !!c; + this._visible = !!h; } - __extends(a, b); - Object.defineProperty(a.prototype, "caption", {get:function() { + __extends(e, a); + Object.defineProperty(e.prototype, "caption", {get:function() { return this._caption; }, set:function(a) { - this._caption = a = k(a); + this._caption = a = p(a); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "separatorBefore", {get:function() { + Object.defineProperty(e.prototype, "separatorBefore", {get:function() { return this._separatorBefore; }, set:function(a) { this._separatorBefore = !!a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "visible", {get:function() { + Object.defineProperty(e.prototype, "visible", {get:function() { return this._visible; }, set:function(a) { this._visible = !!a; }, enumerable:!0, configurable:!0}); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(f.display.NativeMenuItem); - g.ContextMenuItem = m; - })(f.ui || (f.ui = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var s = b.Debug.somewhatImplemented, m = b.Debug.notImplemented, d = b.AVM2.Runtime.throwError, a = function(a) { - function b() { - m("Dummy Constructor: public flash.ui.GameInput"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.display.NativeMenuItem); + h.ContextMenuItem = u; + })(a.ui || (a.ui = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(v) { + var p = c.Debug.somewhatImplemented, u = c.Debug.dummyConstructor, l = c.AVM2.Runtime.throwError, e = function(a) { + function e() { + u("public flash.ui.GameInput"); } - __extends(b, a); - Object.defineProperty(b.prototype, "numDevices", {get:function() { - s("public flash.ui.GameInput::get numDevices"); + __extends(e, a); + Object.defineProperty(e.prototype, "numDevices", {get:function() { + p("public flash.ui.GameInput::get numDevices"); return 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "isSupported", {get:function() { - s("public flash.ui.GameInput::get isSupported"); + Object.defineProperty(e.prototype, "isSupported", {get:function() { + p("public flash.ui.GameInput::get isSupported"); return!1; }, enumerable:!0, configurable:!0}); - b.getDeviceAt = function(a) { - s("public flash.ui.GameInput::static getDeviceAt"); - d("RangeError", k.Errors.ParamRangeError, "index"); + e.getDeviceAt = function(a) { + p("public flash.ui.GameInput::static getDeviceAt"); + l("RangeError", h.Errors.ParamRangeError, "index"); return null; }; - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(f.events.EventDispatcher); - g.GameInput = a; - })(f.ui || (f.ui = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.ui.GameInputControl"); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + return e; + }(a.events.EventDispatcher); + v.GameInput = e; + })(a.ui || (a.ui = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = c.Debug.dummyConstructor, l = function(a) { + function c() { + u("public flash.ui.GameInputControl"); } - __extends(a, b); - Object.defineProperty(a.prototype, "numValues", {get:function() { - k("public flash.ui.GameInputControl::get numValues"); + __extends(c, a); + Object.defineProperty(c.prototype, "numValues", {get:function() { + p("public flash.ui.GameInputControl::get numValues"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "index", {get:function() { - k("public flash.ui.GameInputControl::get index"); + Object.defineProperty(c.prototype, "index", {get:function() { + p("public flash.ui.GameInputControl::get index"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "relative", {get:function() { - k("public flash.ui.GameInputControl::get relative"); + Object.defineProperty(c.prototype, "relative", {get:function() { + p("public flash.ui.GameInputControl::get relative"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "type", {get:function() { - k("public flash.ui.GameInputControl::get type"); + Object.defineProperty(c.prototype, "type", {get:function() { + p("public flash.ui.GameInputControl::get type"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "hand", {get:function() { - k("public flash.ui.GameInputControl::get hand"); + Object.defineProperty(c.prototype, "hand", {get:function() { + p("public flash.ui.GameInputControl::get hand"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "finger", {get:function() { - k("public flash.ui.GameInputControl::get finger"); + Object.defineProperty(c.prototype, "finger", {get:function() { + p("public flash.ui.GameInputControl::get finger"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "device", {get:function() { - k("public flash.ui.GameInputControl::get device"); + Object.defineProperty(c.prototype, "device", {get:function() { + p("public flash.ui.GameInputControl::get device"); }, enumerable:!0, configurable:!0}); - a.prototype.getValueAt = function(a) { - k("public flash.ui.GameInputControl::getValueAt"); + c.prototype.getValueAt = function(a) { + p("public flash.ui.GameInputControl::getValueAt"); }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(f.events.EventDispatcher); - g.GameInputControl = m; - })(f.ui || (f.ui = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.ui.GameInputControlType"); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.events.EventDispatcher); + h.GameInputControl = l; + })(a.ui || (a.ui = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = function(a) { + function e() { + p("public flash.ui.GameInputControlType"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.MOVEMENT = "movement"; - a.ROTATION = "rotation"; - a.DIRECTION = "direction"; - a.ACCELERATION = "acceleration"; - a.BUTTON = "button"; - a.TRIGGER = "trigger"; - return a; - }(g.ASNative); - f.GameInputControlType = m; - })(f.ui || (f.ui = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.ui.GameInputDevice"); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.MOVEMENT = "movement"; + e.ROTATION = "rotation"; + e.DIRECTION = "direction"; + e.ACCELERATION = "acceleration"; + e.BUTTON = "button"; + e.TRIGGER = "trigger"; + return e; + }(a.ASNative); + h.GameInputControlType = s; + })(h.ui || (h.ui = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.notImplemented, u = c.Debug.dummyConstructor, l = function(a) { + function c() { + u("public flash.ui.GameInputDevice"); } - __extends(a, b); - Object.defineProperty(a.prototype, "numControls", {get:function() { - k("public flash.ui.GameInputDevice::get numControls"); + __extends(c, a); + Object.defineProperty(c.prototype, "numControls", {get:function() { + p("public flash.ui.GameInputDevice::get numControls"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "sampleInterval", {get:function() { - k("public flash.ui.GameInputDevice::get sampleInterval"); + Object.defineProperty(c.prototype, "sampleInterval", {get:function() { + p("public flash.ui.GameInputDevice::get sampleInterval"); }, set:function(a) { - k("public flash.ui.GameInputDevice::set sampleInterval"); + p("public flash.ui.GameInputDevice::set sampleInterval"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "enabled", {get:function() { - k("public flash.ui.GameInputDevice::get enabled"); + Object.defineProperty(c.prototype, "enabled", {get:function() { + p("public flash.ui.GameInputDevice::get enabled"); }, set:function(a) { - k("public flash.ui.GameInputDevice::set enabled"); + p("public flash.ui.GameInputDevice::set enabled"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "id", {get:function() { - k("public flash.ui.GameInputDevice::get id"); + Object.defineProperty(c.prototype, "id", {get:function() { + p("public flash.ui.GameInputDevice::get id"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "name", {get:function() { - k("public flash.ui.GameInputDevice::get name"); + Object.defineProperty(c.prototype, "name", {get:function() { + p("public flash.ui.GameInputDevice::get name"); }, enumerable:!0, configurable:!0}); - a.prototype.getControlAt = function(a) { - k("public flash.ui.GameInputDevice::getControlAt"); + c.prototype.getControlAt = function(a) { + p("public flash.ui.GameInputDevice::getControlAt"); }; - a.prototype.startCachingSamples = function(a, b) { - k("public flash.ui.GameInputDevice::startCachingSamples"); + c.prototype.startCachingSamples = function(a, e) { + p("public flash.ui.GameInputDevice::startCachingSamples"); }; - a.prototype.stopCachingSamples = function() { - k("public flash.ui.GameInputDevice::stopCachingSamples"); + c.prototype.stopCachingSamples = function() { + p("public flash.ui.GameInputDevice::stopCachingSamples"); }; - a.prototype.getCachedSamples = function(a, b) { - k("public flash.ui.GameInputDevice::getCachedSamples"); + c.prototype.getCachedSamples = function(a, e) { + p("public flash.ui.GameInputDevice::getCachedSamples"); }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.MAX_BUFFER_SIZE = 4800; - return a; - }(f.events.EventDispatcher); - g.GameInputDevice = m; - })(f.ui || (f.ui = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.ui.GameInputFinger"); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.MAX_BUFFER_SIZE = 4800; + return c; + }(a.events.EventDispatcher); + h.GameInputDevice = l; + })(a.ui || (a.ui = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = function(a) { + function e() { + p("public flash.ui.GameInputFinger"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.THUMB = "thumb"; - a.INDEX = "index"; - a.MIDDLE = "middle"; - a.UNKNOWN = "unknown"; - return a; - }(g.ASNative); - f.GameInputFinger = m; - })(f.ui || (f.ui = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.ui.GameInputHand"); + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.THUMB = "thumb"; + e.INDEX = "index"; + e.MIDDLE = "middle"; + e.UNKNOWN = "unknown"; + return e; + }(a.ASNative); + h.GameInputFinger = s; + })(h.ui || (h.ui = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = function(a) { + function e() { + p("public flash.ui.GameInputHand"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.RIGHT = "right"; - a.LEFT = "left"; - a.UNKNOWN = "unknown"; - return a; - }(g.ASNative); - f.GameInputHand = m; - })(f.ui || (f.ui = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(k) { - var s = b.Debug.notImplemented, m = function() { - function b() { + __extends(e, a); + e.classInitializer = null; + e.initializer = null; + e.classSymbols = null; + e.instanceSymbols = null; + e.RIGHT = "right"; + e.LEFT = "left"; + e.UNKNOWN = "unknown"; + return e; + }(a.ASNative); + h.GameInputHand = s; + })(h.ui || (h.ui = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(v) { + var p = c.Debug.notImplemented, u = c.Debug.dummyConstructor, l = function() { + function a() { this._lastKeyCode = 0; this._captureKeyPress = !1; this._charCodeMap = []; } - b.prototype.dispatchKeyboardEvent = function(a) { - var b = a.keyCode; + a.prototype.dispatchKeyboardEvent = function(a) { + var e = a.keyCode; if ("keydown" === a.type) { - this._lastKeyCode = b; - if (this._captureKeyPress = 8 === b || 9 === b || 13 === b || 32 === b || 48 <= b && 90 >= b || 145 < b) { + this._lastKeyCode = e; + if (this._captureKeyPress = 8 === e || 9 === e || 13 === e || 32 === e || 48 <= e && 90 >= e || 145 < e) { return; } - this._charCodeMap[b] = 0; + this._charCodeMap[e] = 0; } else { if ("keypress" === a.type) { if (this._captureKeyPress) { - b = this._lastKeyCode, this._charCodeMap[b] = a.charCode; + e = this._lastKeyCode, this._charCodeMap[e] = a.charCode; } else { return; } } } if (this.target) { - var d = "keyup" === a.type; - this.target.dispatchEvent(new f.events.KeyboardEvent(d ? "keyUp" : "keyDown", !0, !1, d ? this._charCodeMap[b] : a.charCode, d ? a.keyCode : this._lastKeyCode, a.location, a.ctrlKey, a.altKey, a.shiftKey)); + var c = "keyup" === a.type; + this.target.dispatchEvent(new h.events.KeyboardEvent(c ? "keyUp" : "keyDown", !0, !1, c ? this._charCodeMap[e] : a.charCode, c ? a.keyCode : this._lastKeyCode, a.location, a.ctrlKey, a.altKey, a.shiftKey)); } }; - return b; + return a; }(); - k.KeyboardEventDispatcher = m; - m = function(b) { - function a() { - s("Dummy Constructor: public flash.ui.Keyboard"); + v.KeyboardEventDispatcher = l; + l = function(a) { + function c() { + u("public flash.ui.Keyboard"); } - __extends(a, b); - Object.defineProperty(a.prototype, "capsLock", {get:function() { - s("public flash.ui.Keyboard::get capsLock"); + __extends(c, a); + Object.defineProperty(c, "capsLock", {get:function() { + p("public flash.ui.Keyboard::get capsLock"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "numLock", {get:function() { - s("public flash.ui.Keyboard::get numLock"); + Object.defineProperty(c, "numLock", {get:function() { + p("public flash.ui.Keyboard::get numLock"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "hasVirtualKeyboard", {get:function() { - s("public flash.ui.Keyboard::get hasVirtualKeyboard"); + Object.defineProperty(c, "hasVirtualKeyboard", {get:function() { + p("public flash.ui.Keyboard::get hasVirtualKeyboard"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "physicalKeyboardType", {get:function() { - s("public flash.ui.Keyboard::get physicalKeyboardType"); - }, enumerable:!0, configurable:!0}); - a.isAccessible = function() { - s("public flash.ui.Keyboard::static isAccessible"); - }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.KEYNAME_UPARROW = "Up"; - a.KEYNAME_DOWNARROW = "Down"; - a.KEYNAME_LEFTARROW = "Left"; - a.KEYNAME_RIGHTARROW = "Right"; - a.KEYNAME_F1 = "F1"; - a.KEYNAME_F2 = "F2"; - a.KEYNAME_F3 = "F3"; - a.KEYNAME_F4 = "F4"; - a.KEYNAME_F5 = "F5"; - a.KEYNAME_F6 = "F6"; - a.KEYNAME_F7 = "F7"; - a.KEYNAME_F8 = "F8"; - a.KEYNAME_F9 = "F9"; - a.KEYNAME_F10 = "F10"; - a.KEYNAME_F11 = "F11"; - a.KEYNAME_F12 = "F12"; - a.KEYNAME_F13 = "F13"; - a.KEYNAME_F14 = "F14"; - a.KEYNAME_F15 = "F15"; - a.KEYNAME_F16 = "F16"; - a.KEYNAME_F17 = "F17"; - a.KEYNAME_F18 = "F18"; - a.KEYNAME_F19 = "F19"; - a.KEYNAME_F20 = "F20"; - a.KEYNAME_F21 = "F21"; - a.KEYNAME_F22 = "F22"; - a.KEYNAME_F23 = "F23"; - a.KEYNAME_F24 = "F24"; - a.KEYNAME_F25 = "F25"; - a.KEYNAME_F26 = "F26"; - a.KEYNAME_F27 = "F27"; - a.KEYNAME_F28 = "F28"; - a.KEYNAME_F29 = "F29"; - a.KEYNAME_F30 = "F30"; - a.KEYNAME_F31 = "F31"; - a.KEYNAME_F32 = "F32"; - a.KEYNAME_F33 = "F33"; - a.KEYNAME_F34 = "F34"; - a.KEYNAME_F35 = "F35"; - a.KEYNAME_INSERT = "Insert"; - a.KEYNAME_DELETE = "Delete"; - a.KEYNAME_HOME = "Home"; - a.KEYNAME_BEGIN = "Begin"; - a.KEYNAME_END = "End"; - a.KEYNAME_PAGEUP = "PgUp"; - a.KEYNAME_PAGEDOWN = "PgDn"; - a.KEYNAME_PRINTSCREEN = "PrntScrn"; - a.KEYNAME_SCROLLLOCK = "ScrlLck"; - a.KEYNAME_PAUSE = "Pause"; - a.KEYNAME_SYSREQ = "SysReq"; - a.KEYNAME_BREAK = "Break"; - a.KEYNAME_RESET = "Reset"; - a.KEYNAME_STOP = "Stop"; - a.KEYNAME_MENU = "Menu"; - a.KEYNAME_USER = "User"; - a.KEYNAME_SYSTEM = "Sys"; - a.KEYNAME_PRINT = "Print"; - a.KEYNAME_CLEARLINE = "ClrLn"; - a.KEYNAME_CLEARDISPLAY = "ClrDsp"; - a.KEYNAME_INSERTLINE = "InsLn"; - a.KEYNAME_DELETELINE = "DelLn"; - a.KEYNAME_INSERTCHAR = "InsChr"; - a.KEYNAME_DELETECHAR = "DelChr"; - a.KEYNAME_PREV = "Prev"; - a.KEYNAME_NEXT = "Next"; - a.KEYNAME_SELECT = "Select"; - a.KEYNAME_EXECUTE = "Exec"; - a.KEYNAME_UNDO = "Undo"; - a.KEYNAME_REDO = "Redo"; - a.KEYNAME_FIND = "Find"; - a.KEYNAME_HELP = "Help"; - a.KEYNAME_MODESWITCH = "ModeSw"; - a.STRING_UPARROW = "\uf700"; - a.STRING_DOWNARROW = "\uf701"; - a.STRING_LEFTARROW = "\uf702"; - a.STRING_RIGHTARROW = "\uf703"; - a.STRING_F1 = "\uf704"; - a.STRING_F2 = "\uf705"; - a.STRING_F3 = "\uf706"; - a.STRING_F4 = "\uf707"; - a.STRING_F5 = "\uf708"; - a.STRING_F6 = "\uf709"; - a.STRING_F7 = "\uf70a"; - a.STRING_F8 = "\uf70b"; - a.STRING_F9 = "\uf70c"; - a.STRING_F10 = "\uf70d"; - a.STRING_F11 = "\uf70e"; - a.STRING_F12 = "\uf70f"; - a.STRING_F13 = "\uf710"; - a.STRING_F14 = "\uf711"; - a.STRING_F15 = "\uf712"; - a.STRING_F16 = "\uf713"; - a.STRING_F17 = "\uf714"; - a.STRING_F18 = "\uf715"; - a.STRING_F19 = "\uf716"; - a.STRING_F20 = "\uf717"; - a.STRING_F21 = "\uf718"; - a.STRING_F22 = "\uf719"; - a.STRING_F23 = "\uf71a"; - a.STRING_F24 = "\uf71b"; - a.STRING_F25 = "\uf71c"; - a.STRING_F26 = "\uf71d"; - a.STRING_F27 = "\uf71e"; - a.STRING_F28 = "\uf71f"; - a.STRING_F29 = "\uf720"; - a.STRING_F30 = "\uf721"; - a.STRING_F31 = "\uf722"; - a.STRING_F32 = "\uf723"; - a.STRING_F33 = "\uf724"; - a.STRING_F34 = "\uf725"; - a.STRING_F35 = "\uf726"; - a.STRING_INSERT = "\uf727"; - a.STRING_DELETE = "\uf728"; - a.STRING_HOME = "\uf729"; - a.STRING_BEGIN = "\uf72a"; - a.STRING_END = "\uf72b"; - a.STRING_PAGEUP = "\uf72c"; - a.STRING_PAGEDOWN = "\uf72d"; - a.STRING_PRINTSCREEN = "\uf72e"; - a.STRING_SCROLLLOCK = "\uf72f"; - a.STRING_PAUSE = "\uf730"; - a.STRING_SYSREQ = "\uf731"; - a.STRING_BREAK = "\uf732"; - a.STRING_RESET = "\uf733"; - a.STRING_STOP = "\uf734"; - a.STRING_MENU = "\uf735"; - a.STRING_USER = "\uf736"; - a.STRING_SYSTEM = "\uf737"; - a.STRING_PRINT = "\uf738"; - a.STRING_CLEARLINE = "\uf739"; - a.STRING_CLEARDISPLAY = "\uf73a"; - a.STRING_INSERTLINE = "\uf73b"; - a.STRING_DELETELINE = "\uf73c"; - a.STRING_INSERTCHAR = "\uf73d"; - a.STRING_DELETECHAR = "\uf73e"; - a.STRING_PREV = "\uf73f"; - a.STRING_NEXT = "\uf740"; - a.STRING_SELECT = "\uf741"; - a.STRING_EXECUTE = "\uf742"; - a.STRING_UNDO = "\uf743"; - a.STRING_REDO = "\uf744"; - a.STRING_FIND = "\uf745"; - a.STRING_HELP = "\uf746"; - a.STRING_MODESWITCH = "\uf747"; - a.CharCodeStrings = void 0; - a.NUMBER_0 = 48; - a.NUMBER_1 = 49; - a.NUMBER_2 = 50; - a.NUMBER_3 = 51; - a.NUMBER_4 = 52; - a.NUMBER_5 = 53; - a.NUMBER_6 = 54; - a.NUMBER_7 = 55; - a.NUMBER_8 = 56; - a.NUMBER_9 = 57; - a.A = 65; - a.B = 66; - a.C = 67; - a.D = 68; - a.E = 69; - a.F = 70; - a.G = 71; - a.H = 72; - a.I = 73; - a.J = 74; - a.K = 75; - a.L = 76; - a.M = 77; - a.N = 78; - a.O = 79; - a.P = 80; - a.Q = 81; - a.R = 82; - a.S = 83; - a.T = 84; - a.U = 85; - a.V = 86; - a.W = 87; - a.X = 88; - a.Y = 89; - a.Z = 90; - a.SEMICOLON = 186; - a.EQUAL = 187; - a.COMMA = 188; - a.MINUS = 189; - a.PERIOD = 190; - a.SLASH = 191; - a.BACKQUOTE = 192; - a.LEFTBRACKET = 219; - a.BACKSLASH = 220; - a.RIGHTBRACKET = 221; - a.QUOTE = 222; - a.ALTERNATE = 18; - a.BACKSPACE = 8; - a.CAPS_LOCK = 20; - a.COMMAND = 15; - a.CONTROL = 17; - a.DELETE = 46; - a.DOWN = 40; - a.END = 35; - a.ENTER = 13; - a.ESCAPE = 27; - a.F1 = 112; - a.F2 = 113; - a.F3 = 114; - a.F4 = 115; - a.F5 = 116; - a.F6 = 117; - a.F7 = 118; - a.F8 = 119; - a.F9 = 120; - a.F10 = 121; - a.F11 = 122; - a.F12 = 123; - a.F13 = 124; - a.F14 = 125; - a.F15 = 126; - a.HOME = 36; - a.INSERT = 45; - a.LEFT = 37; - a.NUMPAD = 21; - a.NUMPAD_0 = 96; - a.NUMPAD_1 = 97; - a.NUMPAD_2 = 98; - a.NUMPAD_3 = 99; - a.NUMPAD_4 = 100; - a.NUMPAD_5 = 101; - a.NUMPAD_6 = 102; - a.NUMPAD_7 = 103; - a.NUMPAD_8 = 104; - a.NUMPAD_9 = 105; - a.NUMPAD_ADD = 107; - a.NUMPAD_DECIMAL = 110; - a.NUMPAD_DIVIDE = 111; - a.NUMPAD_ENTER = 108; - a.NUMPAD_MULTIPLY = 106; - a.NUMPAD_SUBTRACT = 109; - a.PAGE_DOWN = 34; - a.PAGE_UP = 33; - a.RIGHT = 39; - a.SHIFT = 16; - a.SPACE = 32; - a.TAB = 9; - a.UP = 38; - a.RED = 16777216; - a.GREEN = 16777217; - a.YELLOW = 16777218; - a.BLUE = 16777219; - a.CHANNEL_UP = 16777220; - a.CHANNEL_DOWN = 16777221; - a.RECORD = 16777222; - a.PLAY = 16777223; - a.PAUSE = 16777224; - a.STOP = 16777225; - a.FAST_FORWARD = 16777226; - a.REWIND = 16777227; - a.SKIP_FORWARD = 16777228; - a.SKIP_BACKWARD = 16777229; - a.NEXT = 16777230; - a.PREVIOUS = 16777231; - a.LIVE = 16777232; - a.LAST = 16777233; - a.MENU = 16777234; - a.INFO = 16777235; - a.GUIDE = 16777236; - a.EXIT = 16777237; - a.BACK = 16777238; - a.AUDIO = 16777239; - a.SUBTITLE = 16777240; - a.DVR = 16777241; - a.VOD = 16777242; - a.INPUT = 16777243; - a.SETUP = 16777244; - a.HELP = 16777245; - a.MASTER_SHELL = 16777246; - a.SEARCH = 16777247; - return a; - }(g.ASNative); - k.Keyboard = m; - })(f.ui || (f.ui = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(k) { - var s = b.Debug.notImplemented, m = b.Debug.somewhatImplemented, d = b.Debug.assert, a = b.AVM2.Runtime.asCoerceString, c = f.events, n = function() { + Object.defineProperty(c, "physicalKeyboardType", {get:function() { + p("public flash.ui.Keyboard::get physicalKeyboardType"); + }, enumerable:!0, configurable:!0}); + c.isAccessible = function() { + p("public flash.ui.Keyboard::static isAccessible"); + }; + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.KEYNAME_UPARROW = "Up"; + c.KEYNAME_DOWNARROW = "Down"; + c.KEYNAME_LEFTARROW = "Left"; + c.KEYNAME_RIGHTARROW = "Right"; + c.KEYNAME_F1 = "F1"; + c.KEYNAME_F2 = "F2"; + c.KEYNAME_F3 = "F3"; + c.KEYNAME_F4 = "F4"; + c.KEYNAME_F5 = "F5"; + c.KEYNAME_F6 = "F6"; + c.KEYNAME_F7 = "F7"; + c.KEYNAME_F8 = "F8"; + c.KEYNAME_F9 = "F9"; + c.KEYNAME_F10 = "F10"; + c.KEYNAME_F11 = "F11"; + c.KEYNAME_F12 = "F12"; + c.KEYNAME_F13 = "F13"; + c.KEYNAME_F14 = "F14"; + c.KEYNAME_F15 = "F15"; + c.KEYNAME_F16 = "F16"; + c.KEYNAME_F17 = "F17"; + c.KEYNAME_F18 = "F18"; + c.KEYNAME_F19 = "F19"; + c.KEYNAME_F20 = "F20"; + c.KEYNAME_F21 = "F21"; + c.KEYNAME_F22 = "F22"; + c.KEYNAME_F23 = "F23"; + c.KEYNAME_F24 = "F24"; + c.KEYNAME_F25 = "F25"; + c.KEYNAME_F26 = "F26"; + c.KEYNAME_F27 = "F27"; + c.KEYNAME_F28 = "F28"; + c.KEYNAME_F29 = "F29"; + c.KEYNAME_F30 = "F30"; + c.KEYNAME_F31 = "F31"; + c.KEYNAME_F32 = "F32"; + c.KEYNAME_F33 = "F33"; + c.KEYNAME_F34 = "F34"; + c.KEYNAME_F35 = "F35"; + c.KEYNAME_INSERT = "Insert"; + c.KEYNAME_DELETE = "Delete"; + c.KEYNAME_HOME = "Home"; + c.KEYNAME_BEGIN = "Begin"; + c.KEYNAME_END = "End"; + c.KEYNAME_PAGEUP = "PgUp"; + c.KEYNAME_PAGEDOWN = "PgDn"; + c.KEYNAME_PRINTSCREEN = "PrntScrn"; + c.KEYNAME_SCROLLLOCK = "ScrlLck"; + c.KEYNAME_PAUSE = "Pause"; + c.KEYNAME_SYSREQ = "SysReq"; + c.KEYNAME_BREAK = "Break"; + c.KEYNAME_RESET = "Reset"; + c.KEYNAME_STOP = "Stop"; + c.KEYNAME_MENU = "Menu"; + c.KEYNAME_USER = "User"; + c.KEYNAME_SYSTEM = "Sys"; + c.KEYNAME_PRINT = "Print"; + c.KEYNAME_CLEARLINE = "ClrLn"; + c.KEYNAME_CLEARDISPLAY = "ClrDsp"; + c.KEYNAME_INSERTLINE = "InsLn"; + c.KEYNAME_DELETELINE = "DelLn"; + c.KEYNAME_INSERTCHAR = "InsChr"; + c.KEYNAME_DELETECHAR = "DelChr"; + c.KEYNAME_PREV = "Prev"; + c.KEYNAME_NEXT = "Next"; + c.KEYNAME_SELECT = "Select"; + c.KEYNAME_EXECUTE = "Exec"; + c.KEYNAME_UNDO = "Undo"; + c.KEYNAME_REDO = "Redo"; + c.KEYNAME_FIND = "Find"; + c.KEYNAME_HELP = "Help"; + c.KEYNAME_MODESWITCH = "ModeSw"; + c.STRING_UPARROW = "\uf700"; + c.STRING_DOWNARROW = "\uf701"; + c.STRING_LEFTARROW = "\uf702"; + c.STRING_RIGHTARROW = "\uf703"; + c.STRING_F1 = "\uf704"; + c.STRING_F2 = "\uf705"; + c.STRING_F3 = "\uf706"; + c.STRING_F4 = "\uf707"; + c.STRING_F5 = "\uf708"; + c.STRING_F6 = "\uf709"; + c.STRING_F7 = "\uf70a"; + c.STRING_F8 = "\uf70b"; + c.STRING_F9 = "\uf70c"; + c.STRING_F10 = "\uf70d"; + c.STRING_F11 = "\uf70e"; + c.STRING_F12 = "\uf70f"; + c.STRING_F13 = "\uf710"; + c.STRING_F14 = "\uf711"; + c.STRING_F15 = "\uf712"; + c.STRING_F16 = "\uf713"; + c.STRING_F17 = "\uf714"; + c.STRING_F18 = "\uf715"; + c.STRING_F19 = "\uf716"; + c.STRING_F20 = "\uf717"; + c.STRING_F21 = "\uf718"; + c.STRING_F22 = "\uf719"; + c.STRING_F23 = "\uf71a"; + c.STRING_F24 = "\uf71b"; + c.STRING_F25 = "\uf71c"; + c.STRING_F26 = "\uf71d"; + c.STRING_F27 = "\uf71e"; + c.STRING_F28 = "\uf71f"; + c.STRING_F29 = "\uf720"; + c.STRING_F30 = "\uf721"; + c.STRING_F31 = "\uf722"; + c.STRING_F32 = "\uf723"; + c.STRING_F33 = "\uf724"; + c.STRING_F34 = "\uf725"; + c.STRING_F35 = "\uf726"; + c.STRING_INSERT = "\uf727"; + c.STRING_DELETE = "\uf728"; + c.STRING_HOME = "\uf729"; + c.STRING_BEGIN = "\uf72a"; + c.STRING_END = "\uf72b"; + c.STRING_PAGEUP = "\uf72c"; + c.STRING_PAGEDOWN = "\uf72d"; + c.STRING_PRINTSCREEN = "\uf72e"; + c.STRING_SCROLLLOCK = "\uf72f"; + c.STRING_PAUSE = "\uf730"; + c.STRING_SYSREQ = "\uf731"; + c.STRING_BREAK = "\uf732"; + c.STRING_RESET = "\uf733"; + c.STRING_STOP = "\uf734"; + c.STRING_MENU = "\uf735"; + c.STRING_USER = "\uf736"; + c.STRING_SYSTEM = "\uf737"; + c.STRING_PRINT = "\uf738"; + c.STRING_CLEARLINE = "\uf739"; + c.STRING_CLEARDISPLAY = "\uf73a"; + c.STRING_INSERTLINE = "\uf73b"; + c.STRING_DELETELINE = "\uf73c"; + c.STRING_INSERTCHAR = "\uf73d"; + c.STRING_DELETECHAR = "\uf73e"; + c.STRING_PREV = "\uf73f"; + c.STRING_NEXT = "\uf740"; + c.STRING_SELECT = "\uf741"; + c.STRING_EXECUTE = "\uf742"; + c.STRING_UNDO = "\uf743"; + c.STRING_REDO = "\uf744"; + c.STRING_FIND = "\uf745"; + c.STRING_HELP = "\uf746"; + c.STRING_MODESWITCH = "\uf747"; + c.CharCodeStrings = void 0; + c.NUMBER_0 = 48; + c.NUMBER_1 = 49; + c.NUMBER_2 = 50; + c.NUMBER_3 = 51; + c.NUMBER_4 = 52; + c.NUMBER_5 = 53; + c.NUMBER_6 = 54; + c.NUMBER_7 = 55; + c.NUMBER_8 = 56; + c.NUMBER_9 = 57; + c.A = 65; + c.B = 66; + c.C = 67; + c.D = 68; + c.E = 69; + c.F = 70; + c.G = 71; + c.H = 72; + c.I = 73; + c.J = 74; + c.K = 75; + c.L = 76; + c.M = 77; + c.N = 78; + c.O = 79; + c.P = 80; + c.Q = 81; + c.R = 82; + c.S = 83; + c.T = 84; + c.U = 85; + c.V = 86; + c.W = 87; + c.X = 88; + c.Y = 89; + c.Z = 90; + c.SEMICOLON = 186; + c.EQUAL = 187; + c.COMMA = 188; + c.MINUS = 189; + c.PERIOD = 190; + c.SLASH = 191; + c.BACKQUOTE = 192; + c.LEFTBRACKET = 219; + c.BACKSLASH = 220; + c.RIGHTBRACKET = 221; + c.QUOTE = 222; + c.ALTERNATE = 18; + c.BACKSPACE = 8; + c.CAPS_LOCK = 20; + c.COMMAND = 15; + c.CONTROL = 17; + c.DELETE = 46; + c.DOWN = 40; + c.END = 35; + c.ENTER = 13; + c.ESCAPE = 27; + c.F1 = 112; + c.F2 = 113; + c.F3 = 114; + c.F4 = 115; + c.F5 = 116; + c.F6 = 117; + c.F7 = 118; + c.F8 = 119; + c.F9 = 120; + c.F10 = 121; + c.F11 = 122; + c.F12 = 123; + c.F13 = 124; + c.F14 = 125; + c.F15 = 126; + c.HOME = 36; + c.INSERT = 45; + c.LEFT = 37; + c.NUMPAD = 21; + c.NUMPAD_0 = 96; + c.NUMPAD_1 = 97; + c.NUMPAD_2 = 98; + c.NUMPAD_3 = 99; + c.NUMPAD_4 = 100; + c.NUMPAD_5 = 101; + c.NUMPAD_6 = 102; + c.NUMPAD_7 = 103; + c.NUMPAD_8 = 104; + c.NUMPAD_9 = 105; + c.NUMPAD_ADD = 107; + c.NUMPAD_DECIMAL = 110; + c.NUMPAD_DIVIDE = 111; + c.NUMPAD_ENTER = 108; + c.NUMPAD_MULTIPLY = 106; + c.NUMPAD_SUBTRACT = 109; + c.PAGE_DOWN = 34; + c.PAGE_UP = 33; + c.RIGHT = 39; + c.SHIFT = 16; + c.SPACE = 32; + c.TAB = 9; + c.UP = 38; + c.RED = 16777216; + c.GREEN = 16777217; + c.YELLOW = 16777218; + c.BLUE = 16777219; + c.CHANNEL_UP = 16777220; + c.CHANNEL_DOWN = 16777221; + c.RECORD = 16777222; + c.PLAY = 16777223; + c.PAUSE = 16777224; + c.STOP = 16777225; + c.FAST_FORWARD = 16777226; + c.REWIND = 16777227; + c.SKIP_FORWARD = 16777228; + c.SKIP_BACKWARD = 16777229; + c.NEXT = 16777230; + c.PREVIOUS = 16777231; + c.LIVE = 16777232; + c.LAST = 16777233; + c.MENU = 16777234; + c.INFO = 16777235; + c.GUIDE = 16777236; + c.EXIT = 16777237; + c.BACK = 16777238; + c.AUDIO = 16777239; + c.SUBTITLE = 16777240; + c.DVR = 16777241; + c.VOD = 16777242; + c.INPUT = 16777243; + c.SETUP = 16777244; + c.HELP = 16777245; + c.MASTER_SHELL = 16777246; + c.SEARCH = 16777247; + return c; + }(a.ASNative); + v.Keyboard = l; + })(h.ui || (h.ui = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(s) { + (function(v) { + var p = c.Debug.notImplemented, u = c.Debug.somewhatImplemented, l = c.Debug.assert, e = c.AVM2.Runtime.asCoerceString, m = s.events, t = function() { function a() { this.currentTarget = this.stage = null; } - a.prototype._findTarget = function(a) { - var b = []; - this.stage._containsGlobalPoint(20 * a.x | 0, 20 * a.y | 0, 3, b); - d(2 > b.length); - return b.length ? b[0] : this.stage; - }; - a.prototype._dispatchMouseEvent = function(a, b, d, f) { - "undefined" === typeof f && (f = null); - var g = a.globalToLocal(d.point); - b = new c.MouseEvent(b, b !== c.MouseEvent.ROLL_OVER && b !== c.MouseEvent.ROLL_OUT && b !== c.MouseEvent.MOUSE_LEAVE, !1, g.x, g.y, f, d.ctrlKey, d.altKey, d.shiftKey, !!d.buttons); - a.dispatchEvent(b); + a.prototype._findTarget = function(a, c) { + var e = []; + this.stage._containsGlobalPoint(20 * a.x | 0, 20 * a.y | 0, c, e); + l(2 > e.length); + return e.length ? e[0] : e.length ? e[0] : null; + }; + a.prototype._dispatchMouseEvent = function(a, c, e, d) { + void 0 === d && (d = null); + var b = a.globalToLocal(e.point); + c = new m.MouseEvent(c, c !== m.MouseEvent.ROLL_OVER && c !== m.MouseEvent.ROLL_OUT && c !== m.MouseEvent.MOUSE_LEAVE, !1, b.x, b.y, d, e.ctrlKey, e.altKey, e.shiftKey, !!e.buttons); + a.dispatchEvent(c); }; a.prototype.handleMouseEvent = function(a) { - var b = this.stage; - if (!b) { - return b; - } - var d = a.point; - f.ui.Mouse.updateCurrentPosition(d); - var g = this.currentTarget; - if (0 > d.x || d.x > b.stageWidth || 0 > d.y || d.y > b.stageHeight) { - return g && this._dispatchMouseEvent(b, c.MouseEvent.MOUSE_LEAVE, a), this.currentTarget = null, b; + var c = this.stage; + if (!c) { + return c; } - var d = this._findTarget(d), k = f.events.MouseEvent.typeFromDOMType(a.type); - switch(k) { - case c.MouseEvent.MOUSE_DOWN: - a.buttons & 1 ? a.buttons = 1 : a.buttons & 2 ? (k = c.MouseEvent.MIDDLE_MOUSE_DOWN, a.buttons = 2) : a.buttons & 4 && (k = c.MouseEvent.RIGHT_MOUSE_DOWN, a.buttons = 4); - d._mouseDown = !0; - break; - case c.MouseEvent.MOUSE_UP: - a.buttons & 1 ? a.buttons = 1 : a.buttons & 2 ? (k = c.MouseEvent.MIDDLE_MOUSE_UP, a.buttons = 2) : a.buttons & 4 && (k = c.MouseEvent.RIGHT_MOUSE_UP, a.buttons = 4); - d._mouseDown = !1; + var e = a.point; + s.ui.Mouse.updateCurrentPosition(e); + var d = this.currentTarget, b = null, g = s.events.MouseEvent.typeFromDOMType(a.type); + if (0 <= e.x && e.x < c.stageWidth && 0 <= e.y && e.y < c.stageHeight) { + b = this._findTarget(e, 3) || this.stage; + } else { + if (!d) { + return c; + } + this._dispatchMouseEvent(c, m.MouseEvent.MOUSE_LEAVE, a); + if (g !== m.MouseEvent.MOUSE_MOVE) { + return c; + } + } + s.ui.Mouse.draggableObject && (e = this._findTarget(e, 5), s.ui.Mouse.draggableObject._updateDragState(e)); + switch(g) { + case m.MouseEvent.MOUSE_DOWN: + a.buttons & 1 ? a.buttons = 1 : a.buttons & 2 ? (g = m.MouseEvent.MIDDLE_MOUSE_DOWN, a.buttons = 2) : a.buttons & 4 && (g = m.MouseEvent.RIGHT_MOUSE_DOWN, a.buttons = 4); + b._mouseDown = !0; + break; + case m.MouseEvent.MOUSE_UP: + a.buttons & 1 ? a.buttons = 1 : a.buttons & 2 ? (g = m.MouseEvent.MIDDLE_MOUSE_UP, a.buttons = 2) : a.buttons & 4 && (g = m.MouseEvent.RIGHT_MOUSE_UP, a.buttons = 4); + b._mouseDown = !1; break; - case c.MouseEvent.CLICK: - a.buttons & 1 || (a.buttons & 2 ? k = c.MouseEvent.MIDDLE_CLICK : a.buttons & 4 && (k = c.MouseEvent.RIGHT_CLICK)); + case m.MouseEvent.CLICK: + a.buttons & 1 || (a.buttons & 2 ? g = m.MouseEvent.MIDDLE_CLICK : a.buttons & 4 && (g = m.MouseEvent.RIGHT_CLICK)); a.buttons = 0; break; - case c.MouseEvent.DOUBLE_CLICK: - if (!d.doubleClickEnabled) { + case m.MouseEvent.DOUBLE_CLICK: + if (!b.doubleClickEnabled) { return; } a.buttons = 0; break; - case c.MouseEvent.MOUSE_MOVE: - this.currentTarget = d; + case m.MouseEvent.MOUSE_MOVE: + this.currentTarget = b; a.buttons &= 1; - if (d === g) { + if (b === d) { break; } - var m = d.findNearestCommonAncestor(g); - if (g && g !== b) { - g._mouseOver = !1; - g._mouseDown = !1; - this._dispatchMouseEvent(g, c.MouseEvent.MOUSE_OUT, a, d); - for (var h = g;h !== m;) { - this._dispatchMouseEvent(h, c.MouseEvent.ROLL_OUT, a, d), h = h.parent; + e = b ? b.findNearestCommonAncestor(d) : c; + if (d && d !== c) { + d._mouseOver = !1; + d._mouseDown = !1; + this._dispatchMouseEvent(d, m.MouseEvent.MOUSE_OUT, a, b); + for (var h = d;h !== e;) { + this._dispatchMouseEvent(h, m.MouseEvent.ROLL_OUT, a, b), h = h.parent; } } - if (d === b) { + if (!b) { + return c; + } + if (b === c) { break; } - for (b = d;b !== m;) { - this._dispatchMouseEvent(b, c.MouseEvent.ROLL_OVER, a, g), b = b.parent; + for (c = b;c !== e;) { + this._dispatchMouseEvent(c, m.MouseEvent.ROLL_OVER, a, d), c = c.parent; } - d._mouseOver = !0; - this._dispatchMouseEvent(d, c.MouseEvent.MOUSE_OVER, a, g); - return d; + b._mouseOver = !0; + this._dispatchMouseEvent(b, m.MouseEvent.MOUSE_OVER, a, d); + return b; } - this._dispatchMouseEvent(d, k, a); - return d; + this._dispatchMouseEvent(b, g, a); + return b; }; return a; }(); - k.MouseEventDispatcher = n; + v.MouseEventDispatcher = t; (function(a) { a[a.Left = 1] = "Left"; a[a.Middle = 2] = "Middle"; a[a.Right = 4] = "Right"; - })(k.MouseButtonFlags || (k.MouseButtonFlags = {})); - n = function(b) { + })(v.MouseButtonFlags || (v.MouseButtonFlags = {})); + t = function(a) { function c() { - s("Dummy Constructor: public flash.ui.Mouse"); } - __extends(c, b); - Object.defineProperty(c.prototype, "supportsCursor", {get:function() { - s("public flash.ui.Mouse::get supportsCursor"); + __extends(c, a); + Object.defineProperty(c, "supportsCursor", {get:function() { + return!0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "cursor", {get:function() { - s("public flash.ui.Mouse::get cursor"); - }, set:function(b) { - a(b); - s("public flash.ui.Mouse::set cursor"); + Object.defineProperty(c, "cursor", {get:function() { + return this._cursor; + }, set:function(a) { + a = e(a); + 0 > v.MouseCursor.toNumber(a) && throwError("ArgumentError", h.Errors.InvalidParamError, "cursor"); + this._cursor = a; }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "supportsNativeCursor", {get:function() { - s("public flash.ui.Mouse::get supportsNativeCursor"); + Object.defineProperty(c, "supportsNativeCursor", {get:function() { + return!0; }, enumerable:!0, configurable:!0}); c.hide = function() { - m("public flash.ui.Mouse::static hide"); + u("public flash.ui.Mouse::static hide"); }; c.show = function() { - m("public flash.ui.Mouse::static show"); + u("public flash.ui.Mouse::static show"); }; - c.registerCursor = function(b, c) { - a(b); - s("public flash.ui.Mouse::static registerCursor"); - }; - c.unregisterCursor = function(b) { - a(b); - s("public flash.ui.Mouse::static unregisterCursor"); + c.registerCursor = function(a, c) { + e(a); + p("public flash.ui.Mouse::static registerCursor"); + }; + c.unregisterCursor = function(a) { + e(a); + p("public flash.ui.Mouse::static unregisterCursor"); }; c.updateCurrentPosition = function(a) { this._currentPosition.copyFrom(a); }; c.classInitializer = function() { - this._currentPosition = new f.geom.Point; + this._currentPosition = new s.geom.Point; + this._cursor = v.MouseCursor.AUTO; + this.draggableObject = null; }; c.initializer = null; c.classSymbols = null; c.instanceSymbols = null; return c; - }(g.ASNative); - k.Mouse = n; - })(f.ui || (f.ui = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.ui.MouseCursorData"); + }(a.ASNative); + v.Mouse = t; + })(s.ui || (s.ui = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = function(a) { + function c() { + p("public flash.ui.MouseCursor"); } - __extends(a, b); - Object.defineProperty(a.prototype, "data", {get:function() { - k("public flash.ui.MouseCursorData::get data"); + __extends(c, a); + c.fromNumber = function(a) { + switch(a) { + case 0: + return c.AUTO; + case 1: + return c.ARROW; + case 2: + return c.BUTTON; + case 3: + return c.HAND; + case 4: + return c.IBEAM; + default: + return null; + } + }; + c.toNumber = function(a) { + switch(a) { + case c.AUTO: + return 0; + case c.ARROW: + return 1; + case c.BUTTON: + return 2; + case c.HAND: + return 3; + case c.IBEAM: + return 4; + default: + return-1; + } + }; + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.AUTO = "auto"; + c.ARROW = "arrow"; + c.BUTTON = "button"; + c.HAND = "hand"; + c.IBEAM = "ibeam"; + return c; + }(a.ASNative); + h.MouseCursor = s; + })(h.ui || (h.ui = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.notImplemented, s = c.Debug.dummyConstructor, l = function(a) { + function c() { + s("public flash.ui.MouseCursorData"); + } + __extends(c, a); + Object.defineProperty(c.prototype, "data", {get:function() { + p("public flash.ui.MouseCursorData::get data"); }, set:function(a) { - k("public flash.ui.MouseCursorData::set data"); + p("public flash.ui.MouseCursorData::set data"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "hotSpot", {get:function() { - k("public flash.ui.MouseCursorData::get hotSpot"); + Object.defineProperty(c.prototype, "hotSpot", {get:function() { + p("public flash.ui.MouseCursorData::get hotSpot"); }, set:function(a) { - k("public flash.ui.MouseCursorData::set hotSpot"); + p("public flash.ui.MouseCursorData::set hotSpot"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "frameRate", {get:function() { - k("public flash.ui.MouseCursorData::get frameRate"); + Object.defineProperty(c.prototype, "frameRate", {get:function() { + p("public flash.ui.MouseCursorData::get frameRate"); }, set:function(a) { - k("public flash.ui.MouseCursorData::set frameRate"); + p("public flash.ui.MouseCursorData::set frameRate"); }, enumerable:!0, configurable:!0}); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.MouseCursorData = m; - })(f.ui || (f.ui = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.somewhatImplemented, m = b.Debug.notImplemented, d = b.AVM2.Runtime.asCoerceString, a = function(a) { - function b() { - m("Dummy Constructor: public flash.ui.Multitouch"); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.ASNative); + h.MouseCursorData = l; + })(h.ui || (h.ui = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.somewhatImplemented, s = c.Debug.notImplemented, l = c.Debug.dummyConstructor, e = c.AVM2.Runtime.asCoerceString, m = function(a) { + function c() { + l("public flash.ui.Multitouch"); } - __extends(b, a); - Object.defineProperty(b.prototype, "inputMode", {get:function() { - m("public flash.ui.Multitouch::get inputMode"); + __extends(c, a); + Object.defineProperty(c, "inputMode", {get:function() { + s("public flash.ui.Multitouch::get inputMode"); }, set:function(a) { - d(a); - m("public flash.ui.Multitouch::set inputMode"); + e(a); + s("public flash.ui.Multitouch::set inputMode"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "supportsTouchEvents", {get:function() { - k("public flash.ui.Multitouch::get supportsTouchEvents"); + Object.defineProperty(c, "supportsTouchEvents", {get:function() { + p("public flash.ui.Multitouch::get supportsTouchEvents"); return!1; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "supportsGestureEvents", {get:function() { - k("public flash.ui.Multitouch::get supportsGestureEvents"); + Object.defineProperty(c, "supportsGestureEvents", {get:function() { + p("public flash.ui.Multitouch::get supportsGestureEvents"); return!1; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "supportedGestures", {get:function() { - k("public flash.ui.Multitouch::get supportedGestures"); + Object.defineProperty(c, "supportedGestures", {get:function() { + p("public flash.ui.Multitouch::get supportedGestures"); return null; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "maxTouchPoints", {get:function() { - k("public flash.ui.Multitouch::get maxTouchPoints"); + Object.defineProperty(c, "maxTouchPoints", {get:function() { + p("public flash.ui.Multitouch::get maxTouchPoints"); return 0; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "mapTouchToMouse", {get:function() { - k("public flash.ui.Multitouch::get mapTouchToMouse"); + Object.defineProperty(c, "mapTouchToMouse", {get:function() { + p("public flash.ui.Multitouch::get mapTouchToMouse"); return!0; }, set:function(a) { - m("public flash.ui.Multitouch::set mapTouchToMouse"); + s("public flash.ui.Multitouch::set mapTouchToMouse"); }, enumerable:!0, configurable:!0}); - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(g.ASNative); - f.Multitouch = a; - })(f.ui || (f.ui = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.ui.MultitouchInputMode"); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.ASNative); + h.Multitouch = m; + })(h.ui || (h.ui = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = function(a) { + function c() { + p("public flash.ui.MultitouchInputMode"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.NONE = "none"; - a.GESTURE = "gesture"; - a.TOUCH_POINT = "touchPoint"; - return a; - }(g.ASNative); - f.MultitouchInputMode = m; - })(f.ui || (f.ui = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.utils.Endian"); + __extends(c, a); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.NONE = "none"; + c.GESTURE = "gesture"; + c.TOUCH_POINT = "touchPoint"; + return c; + }(a.ASNative); + h.MultitouchInputMode = s; + })(h.ui || (h.ui = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = function(a) { + function c() { + p("public flash.utils.Endian"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - a.BIG_ENDIAN = "bigEndian"; - a.LITTLE_ENDIAN = "littleEndian"; - return a; - }(g.ASNative); - f.Endian = m; - })(f.utils || (f.utils = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -var Shumway$$inline_615 = Shumway || (Shumway = {}), AVM2$$inline_616 = Shumway$$inline_615.AVM2 || (Shumway$$inline_615.AVM2 = {}), AS$$inline_617 = AVM2$$inline_616.AS || (AVM2$$inline_616.AS = {}), flash$$inline_618 = AS$$inline_617.flash || (AS$$inline_617.flash = {}), utils$$inline_619 = flash$$inline_618.utils || (flash$$inline_618.utils = {}), utils$$inline_620 = flash$$inline_618.utils, flash$$inline_621 = AS$$inline_617.flash, AS$$inline_622 = AVM2$$inline_616.AS, AVM2$$inline_623 = Shumway$$inline_615.AVM2, -Shumway$$inline_625 = Shumway || (Shumway = {}), AVM2$$inline_626 = Shumway$$inline_625.AVM2 || (Shumway$$inline_625.AVM2 = {}), AS$$inline_627 = AVM2$$inline_626.AS || (AVM2$$inline_626.AS = {}), flash$$inline_628 = AS$$inline_627.flash || (AS$$inline_627.flash = {}), utils$$inline_629 = flash$$inline_628.utils || (flash$$inline_628.utils = {}), utils$$inline_630 = flash$$inline_628.utils, flash$$inline_631 = AS$$inline_627.flash, AS$$inline_632 = AVM2$$inline_626.AS, AVM2$$inline_633 = Shumway$$inline_625.AVM2, -Shumway$$inline_635 = Shumway || (Shumway = {}), AVM2$$inline_636 = Shumway$$inline_635.AVM2 || (Shumway$$inline_635.AVM2 = {}), AS$$inline_637 = AVM2$$inline_636.AS || (AVM2$$inline_636.AS = {}), flash$$inline_638 = AS$$inline_637.flash || (AS$$inline_637.flash = {}), utils$$inline_639 = flash$$inline_638.utils || (flash$$inline_638.utils = {}), utils$$inline_640 = flash$$inline_638.utils, flash$$inline_641 = AS$$inline_637.flash, AS$$inline_642 = AVM2$$inline_636.AS, AVM2$$inline_643 = Shumway$$inline_635.AVM2; -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = function(a) { - function b() { - k("Dummy Constructor: packageInternal flash.utils.ObjectInput"); + __extends(c, a); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + c.BIG_ENDIAN = "bigEndian"; + c.LITTLE_ENDIAN = "littleEndian"; + return c; + }(a.ASNative); + h.Endian = s; + })(h.utils || (h.utils = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + (function(a) { + (function(a) { + (function(v) { + var p = function(p) { + function l(c, h) { + a.events.EventDispatcher.instanceConstructorNoInitialize.call(this); + this._delay = +c; + this._repeatCount = h | 0; + this._iteration = 0; } - __extends(b, a); - Object.defineProperty(b.prototype, "bytesAvailable", {get:function() { - k("packageInternal flash.utils.ObjectInput::get bytesAvailable"); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "objectEncoding", {get:function() { - k("packageInternal flash.utils.ObjectInput::get objectEncoding"); - }, set:function(a) { - k("packageInternal flash.utils.ObjectInput::set objectEncoding"); + __extends(l, p); + Object.defineProperty(l.prototype, "running", {get:function() { + return this._running; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "endian", {get:function() { - k("packageInternal flash.utils.ObjectInput::get endian"); + Object.defineProperty(l.prototype, "delay", {get:function() { + return this._delay; }, set:function(a) { - m(a); - k("packageInternal flash.utils.ObjectInput::set endian"); + a = +a; + (0 > a || !isFinite(a)) && throwError("RangeError", c.Errors.DelayRangeError); + this._delay = a; + this._running && (this.stop(), this.start()); }, enumerable:!0, configurable:!0}); - b.prototype.readBytes = function(a, b, c) { - k("packageInternal flash.utils.ObjectInput::readBytes"); - }; - b.prototype.readBoolean = function() { - k("packageInternal flash.utils.ObjectInput::readBoolean"); - }; - b.prototype.readByte = function() { - k("packageInternal flash.utils.ObjectInput::readByte"); - }; - b.prototype.readUnsignedByte = function() { - k("packageInternal flash.utils.ObjectInput::readUnsignedByte"); - }; - b.prototype.readShort = function() { - k("packageInternal flash.utils.ObjectInput::readShort"); - }; - b.prototype.readUnsignedShort = function() { - k("packageInternal flash.utils.ObjectInput::readUnsignedShort"); - }; - b.prototype.readInt = function() { - k("packageInternal flash.utils.ObjectInput::readInt"); - }; - b.prototype.readUnsignedInt = function() { - k("packageInternal flash.utils.ObjectInput::readUnsignedInt"); - }; - b.prototype.readFloat = function() { - k("packageInternal flash.utils.ObjectInput::readFloat"); - }; - b.prototype.readDouble = function() { - k("packageInternal flash.utils.ObjectInput::readDouble"); - }; - b.prototype.readMultiByte = function(a, b) { - m(b); - k("packageInternal flash.utils.ObjectInput::readMultiByte"); - }; - b.prototype.readUTF = function() { - k("packageInternal flash.utils.ObjectInput::readUTF"); - }; - b.prototype.readUTFBytes = function(a) { - k("packageInternal flash.utils.ObjectInput::readUTFBytes"); - }; - b.prototype.readObject = function() { - k("packageInternal flash.utils.ObjectInput::readObject"); - }; - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(g.ASNative); - f.ObjectInput = d; - })(f.utils || (f.utils = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = function(a) { - function b() { - k("Dummy Constructor: packageInternal flash.utils.ObjectOutput"); - } - __extends(b, a); - Object.defineProperty(b.prototype, "objectEncoding", {get:function() { - k("packageInternal flash.utils.ObjectOutput::get objectEncoding"); + Object.defineProperty(l.prototype, "repeatCount", {get:function() { + return this._repeatCount; }, set:function(a) { - k("packageInternal flash.utils.ObjectOutput::set objectEncoding"); + (this._repeatCount = a | 0) && this._running && this._iteration >= this._repeatCount && this.stop(); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "endian", {get:function() { - k("packageInternal flash.utils.ObjectOutput::get endian"); - }, set:function(a) { - m(a); - k("packageInternal flash.utils.ObjectOutput::set endian"); + Object.defineProperty(l.prototype, "currentCount", {get:function() { + return this._iteration; }, enumerable:!0, configurable:!0}); - b.prototype.writeBytes = function(a, b, c) { - k("packageInternal flash.utils.ObjectOutput::writeBytes"); - }; - b.prototype.writeBoolean = function(a) { - k("packageInternal flash.utils.ObjectOutput::writeBoolean"); - }; - b.prototype.writeByte = function(a) { - k("packageInternal flash.utils.ObjectOutput::writeByte"); - }; - b.prototype.writeShort = function(a) { - k("packageInternal flash.utils.ObjectOutput::writeShort"); - }; - b.prototype.writeInt = function(a) { - k("packageInternal flash.utils.ObjectOutput::writeInt"); - }; - b.prototype.writeUnsignedInt = function(a) { - k("packageInternal flash.utils.ObjectOutput::writeUnsignedInt"); - }; - b.prototype.writeFloat = function(a) { - k("packageInternal flash.utils.ObjectOutput::writeFloat"); - }; - b.prototype.writeDouble = function(a) { - k("packageInternal flash.utils.ObjectOutput::writeDouble"); + l.prototype.reset = function() { + this._running && this.stop(); + this._iteration = 0; }; - b.prototype.writeMultiByte = function(a, b) { - m(a); - m(b); - k("packageInternal flash.utils.ObjectOutput::writeMultiByte"); - }; - b.prototype.writeUTF = function(a) { - m(a); - k("packageInternal flash.utils.ObjectOutput::writeUTF"); - }; - b.prototype.writeUTFBytes = function(a) { - m(a); - k("packageInternal flash.utils.ObjectOutput::writeUTFBytes"); - }; - b.prototype.writeObject = function(a) { - k("packageInternal flash.utils.ObjectOutput::writeObject"); - }; - b.classInitializer = null; - b.initializer = null; - b.classSymbols = null; - b.instanceSymbols = null; - return b; - }(g.ASNative); - f.ObjectOutput = d; - })(f.utils || (f.utils = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, b) { - k("Dummy Constructor: public flash.utils.Timer"); - } - __extends(a, b); - Object.defineProperty(a.prototype, "running", {get:function() { - return this._running; - }, enumerable:!0, configurable:!0}); - a.prototype.stop = function() { + l.prototype.stop = function() { this._running = !1; clearInterval(this._interval); }; - a.prototype._start = function(a, b) { - this._delay = +a; - this._running = !0; - this._interval = setInterval(b, a); - }; - a.prototype._tick = function() { - this._running && f.utils.Timer.dispatchingEnabled && this.dispatchEvent(new f.events.TimerEvent("timer", !0, !1)); - }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = ["start!"]; - a.dispatchingEnabled = !0; - return a; - }(f.events.EventDispatcher); - g.Timer = m; - })(f.utils || (f.utils = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = function(b) { - function a(a, b, d, e) { - k("Dummy Constructor: packageInternal flash.utils.SetIntervalTimer"); + l.prototype.start = function() { + this._running || (this._running = !0, this._interval = setInterval(this._tick.bind(this), this._delay)); + }; + l.prototype._tick = function() { + this._iteration++; + this._running && (a.utils.Timer.dispatchingEnabled && this.dispatchEvent(new a.events.TimerEvent("timer", !0, !1)), 0 !== this._repeatCount && this._iteration >= this._repeatCount && (this.stop(), this.dispatchEvent(new a.events.TimerEvent(a.events.TimerEvent.TIMER_COMPLETE, !1, !1)))); + }; + l.classInitializer = null; + l.initializer = null; + l.classSymbols = null; + l.instanceSymbols = null; + l.dispatchingEnabled = !0; + return l; + }(a.events.EventDispatcher); + v.Timer = p; + })(a.utils || (a.utils = {})); + })(a.flash || (a.flash = {})); + })(c.AS || (c.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.dummyConstructor, u = function(a) { + function c(a, e, h, l) { + p("packageInternal flash.utils.SetIntervalTimer"); } - __extends(a, b); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(f.utils.Timer); - g.SetIntervalTimer = m; - })(f.utils || (f.utils = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = function(a) { - function b(a, c) { - m(c); - k("Dummy Constructor: public flash.xml.XMLNode"); + __extends(c, a); + c.classInitializer = null; + c.initializer = null; + c.classSymbols = null; + c.instanceSymbols = null; + return c; + }(a.utils.Timer); + h.SetIntervalTimer = u; + })(a.utils || (a.utils = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.notImplemented, s = c.Debug.dummyConstructor, l = c.AVM2.Runtime.asCoerceString, e = function(a) { + function c(a, e) { + l(e); + s("public flash.xml.XMLNode"); } - __extends(b, a); - b.escapeXML = function(a) { - m(a); - k("public flash.xml.XMLNode::static escapeXML"); + __extends(c, a); + c.escapeXML = function(a) { + l(a); + p("public flash.xml.XMLNode::static escapeXML"); }; - b.initializer = null; - return b; - }(g.ASNative); - f.XMLNode = d; - })(f.xml || (f.xml = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(g) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = function(a) { - function b(a) { - "undefined" === typeof a && (a = null); - m(a); - k("Dummy Constructor: public flash.xml.XMLDocument"); + c.initializer = null; + return c; + }(a.ASNative); + h.XMLNode = e; + })(h.xml || (h.xml = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(a) { + (function(h) { + var p = c.Debug.dummyConstructor, u = c.AVM2.Runtime.asCoerceString, l = function(a) { + function c(a) { + void 0 === a && (a = null); + u(a); + p("public flash.xml.XMLDocument"); } - __extends(b, a); - b.initializer = null; - return b; - }(f.xml.XMLNode); - g.XMLDocument = d; - })(f.xml || (f.xml = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = function(b) { - function a() { - k("Dummy Constructor: public flash.xml.XMLNodeType"); + __extends(c, a); + c.initializer = null; + return c; + }(a.xml.XMLNode); + h.XMLDocument = l; + })(a.xml || (a.xml = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.dummyConstructor, s = function(a) { + function c() { + p("public flash.xml.XMLNodeType"); } - __extends(a, b); - a.initializer = null; - return a; - }(g.ASNative); - f.XMLNodeType = m; - })(f.xml || (f.xml = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = function(a) { - function b() { - k("Dummy Constructor: packageInternal flash.xml.XMLParser"); + __extends(c, a); + c.initializer = null; + return c; + }(a.ASNative); + h.XMLNodeType = s; + })(h.xml || (h.xml = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.notImplemented, s = c.Debug.dummyConstructor, l = c.AVM2.Runtime.asCoerceString, e = function(a) { + function c() { + s("packageInternal flash.xml.XMLParser"); } - __extends(b, a); - b.prototype.startParse = function(a, b) { - m(a); - k("packageInternal flash.xml.XMLParser::startParse"); + __extends(c, a); + c.prototype.startParse = function(a, c) { + l(a); + p("packageInternal flash.xml.XMLParser::startParse"); }; - b.prototype.getNext = function(a) { - k("packageInternal flash.xml.XMLParser::getNext"); + c.prototype.getNext = function(a) { + p("packageInternal flash.xml.XMLParser::getNext"); }; - b.initializer = null; - return b; - }(g.ASNative); - f.XMLParser = d; - })(f.xml || (f.xml = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - (function(f) { - var k = b.Debug.notImplemented, m = b.AVM2.Runtime.asCoerceString, d = function(a) { - function b() { - k("Dummy Constructor: packageInternal flash.xml.XMLTag"); + c.initializer = null; + return c; + }(a.ASNative); + h.XMLParser = e; + })(h.xml || (h.xml = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + (function(h) { + (function(h) { + var p = c.Debug.notImplemented, s = c.Debug.dummyConstructor, l = c.AVM2.Runtime.asCoerceString, e = function(a) { + function c() { + s("packageInternal flash.xml.XMLTag"); } - __extends(b, a); - Object.defineProperty(b.prototype, "type", {get:function() { - k("packageInternal flash.xml.XMLTag::get type"); + __extends(c, a); + Object.defineProperty(c.prototype, "type", {get:function() { + p("packageInternal flash.xml.XMLTag::get type"); }, set:function(a) { - k("packageInternal flash.xml.XMLTag::set type"); + p("packageInternal flash.xml.XMLTag::set type"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "empty", {get:function() { - k("packageInternal flash.xml.XMLTag::get empty"); + Object.defineProperty(c.prototype, "empty", {get:function() { + p("packageInternal flash.xml.XMLTag::get empty"); }, set:function(a) { - k("packageInternal flash.xml.XMLTag::set empty"); + p("packageInternal flash.xml.XMLTag::set empty"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "value", {get:function() { - k("packageInternal flash.xml.XMLTag::get value"); + Object.defineProperty(c.prototype, "value", {get:function() { + p("packageInternal flash.xml.XMLTag::get value"); }, set:function(a) { - m(a); - k("packageInternal flash.xml.XMLTag::set value"); + l(a); + p("packageInternal flash.xml.XMLTag::set value"); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "attrs", {get:function() { - k("packageInternal flash.xml.XMLTag::get attrs"); + Object.defineProperty(c.prototype, "attrs", {get:function() { + p("packageInternal flash.xml.XMLTag::get attrs"); }, set:function(a) { - k("packageInternal flash.xml.XMLTag::set attrs"); + p("packageInternal flash.xml.XMLTag::set attrs"); }, enumerable:!0, configurable:!0}); - b.initializer = null; - return b; - }(g.ASNative); - f.XMLTag = d; - })(f.xml || (f.xml = {})); - })(g.flash || (g.flash = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - var g = b.Debug.notImplemented, f = b.isInteger, t = b.Debug.assert, s = b.Debug.warning, m = b.Bounds, d = b.AVM2.AS.flash, a = b.AVM2.AS.flash.display.ActionScriptVersion, c = function() { - return function(a, b) { - this.id = -1; - t(f(a)); - this.id = a; - this.symbolClass = b; - this.isAVM1Object = !1; + c.initializer = null; + return c; + }(a.ASNative); + h.XMLTag = e; + })(h.xml || (h.xml = {})); + })(a.flash || (a.flash = {})); + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + function s(a, d, b) { + return{classSimpleName:a, nativeName:d, cls:b}; + } + function v(a, d, b) { + Object.defineProperty(a, b, {get:function() { + k(c.AVM2.Runtime.AVM2.instance, "AVM2 needs to be initialized."); + var e = c.AVM2.Runtime.AVM2.instance.systemDomain.getClass(d); + k(e.instanceConstructor); + Object.defineProperty(a, b, {value:e.instanceConstructor, writable:!1}); + return a[b]; + }, configurable:!0}); + } + function p() { + return Date.now() - q.display.Loader.runtimeStartTime; + } + function u(a, d) { + null !== a && void 0 !== a || t("TypeError", h.Errors.NullPointerError, "request"); + c.AVM2.Runtime.AVM2.instance.systemDomain.getClass("flash.net.URLRequest").isInstanceOf(a) || t("TypeError", h.Errors.CheckTypeFailedError, a, "flash.net.URLRequest"); + var b = a.url; + c.isNullOrUndefined(b) && t("TypeError", h.Errors.NullPointerError, "url"); + /^fscommand:/i.test(b) ? c.AVM2.Runtime.AVM2.instance.applicationDomain.getProperty(n.fromSimpleName("flash.system.fscommand"), !0, !0).call(null, b.substring(10), d) : c.FileLoadingService.instance.navigateTo(b, d); + } + function l(a) { + null !== a && void 0 !== a || t("TypeError", h.Errors.NullPointerError, "request"); + c.AVM2.Runtime.AVM2.instance.systemDomain.getClass("flash.net.URLRequest").isInstanceOf(a) || t("TypeError", h.Errors.CheckTypeFailedError, a, "flash.net.URLRequest"); + var d = c.FileLoadingService.instance.createSession(); + d.onprogress = function() { + }; + d.open(a); + } + function e(a, d) { + a || t("TypeError", h.Errors.NullPointerError, "aliasName"); + d || t("TypeError", h.Errors.NullPointerError, "classObject"); + h.aliasesCache.classes.set(d, a); + h.aliasesCache.names[a] = d; + } + function m(a) { + a || t("TypeError", h.Errors.NullPointerError, "aliasName"); + var d = h.aliasesCache.names[a]; + d || t("ReferenceError", h.Errors.ClassNotFoundError, a); + return d; + } + var t = c.AVM2.Runtime.throwError, q = c.AVM2.AS.flash, n = c.AVM2.ABC.Multiname, k = c.Debug.assert; + jsGlobal.flash = c.AVM2.AS.flash; + a.linkNatives = function(f) { + [s("flash.display.DisplayObject", "DisplayObjectClass", q.display.DisplayObject), s("flash.display.InteractiveObject", "InteractiveObjectClass", q.display.InteractiveObject), s("flash.display.DisplayObjectContainer", "ContainerClass", q.display.DisplayObjectContainer), s("flash.display.Sprite", "SpriteClass", q.display.Sprite), s("flash.display.MovieClip", "MovieClipClass", q.display.MovieClip), s("flash.display.Shape", "ShapeClass", q.display.Shape), s("flash.display.Bitmap", "BitmapClass", + q.display.Bitmap), s("flash.display.BitmapData", "BitmapDataClass", q.display.BitmapData), s("flash.display.Stage", "StageClass", q.display.Stage), s("flash.display.Loader", "LoaderClass", q.display.Loader), s("flash.display.LoaderInfo", "LoaderInfoClass", q.display.LoaderInfo), s("flash.display.Graphics", "GraphicsClass", q.display.Graphics), s("flash.display.SimpleButton", "SimpleButtonClass", q.display.SimpleButton), s("flash.display.MorphShape", "MorphShapeClass", q.display.MorphShape), + s("flash.display.NativeMenu", "NativeMenuClass", q.display.NativeMenu), s("flash.display.NativeMenuItem", "NativeMenuItemClass", q.display.NativeMenuItem), s("flash.display.FrameLabel", "FrameLabelClass", q.display.FrameLabel), s("flash.display.Scene", "SceneClass", q.display.Scene), s("flash.display.AVM1Movie", "AVM1MovieClass", q.display.AVM1Movie), s("flash.filters.BevelFilter", "BevelFilterClass", q.filters.BevelFilter), s("flash.filters.BitmapFilter", "BitmapFilterClass", q.filters.BitmapFilter), + s("flash.filters.BlurFilter", "BlurFilterClass", q.filters.BlurFilter), s("flash.filters.ColorMatrixFilter", "ColorMatrixFilterClass", q.filters.ColorMatrixFilter), s("flash.filters.ConvolutionFilter", "ConvolutionFilterClass", q.filters.ConvolutionFilter), s("flash.filters.DisplacementMapFilter", "DisplacementMapFilterClass", q.filters.DisplacementMapFilter), s("flash.filters.DropShadowFilter", "DropShadowFilterClass", q.filters.DropShadowFilter), s("flash.filters.GlowFilter", "GlowFilterClass", + q.filters.GlowFilter), s("flash.filters.GradientBevelFilter", "GradientBevelFilterClass", q.filters.GradientBevelFilter), s("flash.filters.GradientGlowFilter", "GradientGlowFilterClass", q.filters.GradientGlowFilter), s("flash.geom.Point", "PointClass", q.geom.Point), s("flash.geom.Rectangle", "RectangleClass", q.geom.Rectangle), s("flash.geom.Matrix", "MatrixClass", q.geom.Matrix), s("flash.geom.Matrix3D", "Matrix3DClass", q.geom.Matrix3D), s("flash.geom.Vector3D", "Vector3DClass", q.geom.Vector3D), + s("flash.geom.Transform", "TransformClass", q.geom.Transform), s("flash.geom.ColorTransform", "ColorTransformClass", q.geom.ColorTransform), s("flash.events.EventDispatcher", "EventDispatcherClass", q.events.EventDispatcher), s("flash.events.Event", "EventClass", q.events.Event), s("flash.events.ErrorEvent", "ErrorEventClass", q.events.ErrorEvent), s("flash.events.IOErrorEvent", "IOErrorEventClass", q.events.IOErrorEvent), s("flash.events.KeyboardEvent", "KeyboardEventClass", q.events.KeyboardEvent), + s("flash.events.MouseEvent", "MouseEventClass", q.events.MouseEvent), s("flash.events.GestureEvent", "GestureEventClass", q.events.GestureEvent), s("flash.events.TextEvent", "TextEventClass", q.events.TextEvent), s("flash.events.TimerEvent", "TimerEventClass", q.events.TimerEvent), s("flash.events.ProgressEvent", "ProgressEventClass", q.events.ProgressEvent), s("flash.events.NetStatusEvent", "NetStatusEventClass", q.events.NetStatusEvent), s("flash.events.HTTPStatusEvent", "HTTPStatusEventClass", + q.events.HTTPStatusEvent), s("flash.external.ExternalInterface", "ExternalInterfaceClass", q.external.ExternalInterface), s("flash.ui.ContextMenu", "ContextMenuClass", q.ui.ContextMenu), s("flash.ui.ContextMenuItem", "ContextMenuItemClass", q.ui.ContextMenuItem), s("flash.ui.ContextMenuBuiltInItems", "ContextMenuBuiltInItemsClass", q.ui.ContextMenuBuiltInItems), s("flash.ui.ContextMenuClipboardItems", "ContextMenuClipboardItemsClass", q.ui.ContextMenuClipboardItems), s("flash.ui.Keyboard", + "KeyboardClass", q.ui.Keyboard), s("flash.ui.Mouse", "MouseClass", q.ui.Mouse), s("flash.ui.MouseCursorData", "MouseCursorDataClass", q.ui.MouseCursorData), s("flash.ui.GameInput", "GameInputClass", q.ui.GameInput), s("flash.events.GameInputEvent", "GameInputEventClass", q.events.GameInputEvent), s("flash.ui.GameInputControl", "GameInputControlClass", q.ui.GameInputControl), s("flash.ui.GameInputControlType", "GameInputControlTypeClass", q.ui.GameInputControlType), s("flash.ui.GameInputDevice", + "GameInputDeviceClass", q.ui.GameInputDevice), s("flash.ui.GameInputFinger", "GameInputFingerClass", q.ui.GameInputFinger), s("flash.ui.GameInputHand", "GameInputHandClass", q.ui.GameInputHand), s("flash.ui.Multitouch", "MultitouchClass", q.ui.Multitouch), s("flash.ui.MultitouchInputMode", "MultitouchInputModeClass", q.ui.MultitouchInputMode), s("flash.events.TouchEvent", "TouchEventClass", q.events.TouchEvent), s("flash.text.Font", "FontClass", q.text.Font), s("flash.text.TextField", "TextFieldClass", + q.text.TextField), s("flash.text.StaticText", "StaticTextClass", q.text.StaticText), s("flash.text.StyleSheet", "StyleSheetClass", q.text.StyleSheet), s("flash.text.TextFormat", "TextFormatClass", q.text.TextFormat), s("flash.text.TextRun", "TextRunClass", q.text.TextRun), s("flash.text.TextLineMetrics"), s("flash.media.Sound", "SoundClass", q.media.Sound), s("flash.media.SoundChannel", "SoundChannelClass", q.media.SoundChannel), s("flash.media.SoundMixer", "SoundMixerClass", q.media.SoundMixer), + s("flash.media.SoundTransform", "SoundTransformClass", q.media.SoundTransform), s("flash.media.Video", "VideoClass", q.media.Video), s("flash.media.StageVideo", "StageVideoClass", q.media.StageVideo), s("flash.media.ID3Info", "ID3InfoClass", q.media.ID3Info), s("flash.media.Microphone", "MicrophoneClass", q.media.Microphone), s("flash.net.FileFilter", "FileFilterClass", q.net.FileFilter), s("flash.net.NetConnection", "NetConnectionClass", q.net.NetConnection), s("flash.net.NetStream", "NetStreamClass", + q.net.NetStream), s("flash.net.Responder", "ResponderClass", q.net.Responder), s("flash.net.URLRequest", "URLRequestClass", q.net.URLRequest), s("flash.net.URLRequestHeader"), s("flash.net.URLStream", "URLStreamClass", q.net.URLStream), s("flash.net.URLLoader", "URLLoaderClass", q.net.URLLoader), s("flash.net.SharedObject", "SharedObjectClass", q.net.SharedObject), s("flash.net.ObjectEncoding", "ObjectEncodingClass", q.net.ObjectEncoding), s("flash.net.LocalConnection", "LocalConnectionClass", + q.net.LocalConnection), s("flash.net.Socket", "SocketClass", q.net.Socket), s("flash.net.URLVariables", "URLVariablesClass", q.net.URLVariables), s("packageInternal flash.system.FSCommand", "FSCommandClass", q.system.FSCommand), s("flash.system.Capabilities", "CapabilitiesClass", q.system.Capabilities), s("flash.system.Security", "SecurityClass", q.system.Security), s("flash.system.SecurityDomain", "SecurityDomainClass", q.system.SecurityDomain), s("flash.system.ApplicationDomain", "ApplicationDomainClass", + q.system.ApplicationDomain), s("flash.system.JPEGLoaderContext", "JPEGLoaderContextClass", q.system.JPEGLoaderContext), s("flash.system.LoaderContext", "LoaderContextClass", q.system.LoaderContext), s("flash.accessibility.Accessibility", "AccessibilityClass", q.accessibility.Accessibility), s("flash.utils.Timer", "TimerClass", q.utils.Timer), s("flash.utils.ByteArray", "ByteArrayClass", q.utils.ByteArray)].forEach(function(d) { + for (var b = n.fromSimpleName(d.classSimpleName).getOriginalName().split("."), e = c.AVM2.AS, f = 0, k = b.length - 1;f < k;f++) { + e[b[f]] || (e[b[f]] = {}), e = e[b[f]]; + } + v(e, d.classSimpleName, b[b.length - 1]); + a.registerNativeClass(d.nativeName, d.cls); + }); + a.registerNativeFunction("FlashUtilScript::getDefinitionByName", c.AVM2.AS.Natives.getDefinitionByName); + a.registerNativeFunction("FlashUtilScript::getTimer", p); + a.registerNativeFunction("FlashUtilScript::escapeMultiByte", escape); + a.registerNativeFunction("FlashUtilScript::unescapeMultiByte", unescape); + a.registerNativeFunction("FlashNetScript::navigateToURL", u); + a.registerNativeFunction("FlashNetScript::sendToURL", l); + a.registerNativeFunction("Toplevel::registerClassAlias", e); + a.registerNativeFunction("Toplevel::getClassByAlias", m); + }; + a.FlashUtilScript_getTimer = p; + a.FlashNetScript_navigateToURL = u; + })(h.AS || (h.AS = {})); + })(c.AVM2 || (c.AVM2 = {})); +})(Shumway || (Shumway = {})); +console.timeEnd("Load Flash TS Dependencies"); +console.time("Load AVM1 Dependencies"); +(function(c) { + (function(c) { + var a = function() { + function a(c, h) { + this.array = c; + this.position = 0; + this.end = c.length; + this.readANSI = 6 > h; + var s = new ArrayBuffer(4); + (new Int32Array(s))[0] = 1; + if (!(new Uint8Array(s))[0]) { + throw Error("big-endian platform"); + } + } + a.prototype.readUI8 = function() { + return this.array[this.position++]; + }; + a.prototype.readUI16 = function() { + var a = this.position, c = this.array, c = c[a + 1] << 8 | c[a]; + this.position = a + 2; + return c; + }; + a.prototype.readSI16 = function() { + var a = this.position, c = this.array, c = c[a + 1] << 8 | c[a]; + this.position = a + 2; + return 32768 > c ? c : c - 65536; + }; + a.prototype.readInteger = function() { + var a = this.position, c = this.array, c = c[a] | c[a + 1] << 8 | c[a + 2] << 16 | c[a + 3] << 24; + this.position = a + 4; + return c; + }; + a.prototype.readFloat = function() { + var a = this.position, c = this.array, h = new ArrayBuffer(4), l = new Uint8Array(h); + l[0] = c[a]; + l[1] = c[a + 1]; + l[2] = c[a + 2]; + l[3] = c[a + 3]; + this.position = a + 4; + return(new Float32Array(h))[0]; + }; + a.prototype.readDouble = function() { + var a = this.position, c = this.array, h = new ArrayBuffer(8), l = new Uint8Array(h); + l[4] = c[a]; + l[5] = c[a + 1]; + l[6] = c[a + 2]; + l[7] = c[a + 3]; + l[0] = c[a + 4]; + l[1] = c[a + 5]; + l[2] = c[a + 6]; + l[3] = c[a + 7]; + this.position = a + 8; + return(new Float64Array(h))[0]; + }; + a.prototype.readBoolean = function() { + return!!this.readUI8(); + }; + a.prototype.readANSIString = function() { + for (var a = "", c;c = this.readUI8();) { + a += String.fromCharCode(c); + } + return a; + }; + a.prototype.readUTF8String = function() { + for (var a = "", c;c = this.readUI8();) { + if (128 > c) { + a += String.fromCharCode(c); + } else { + if (128 === (c & 192)) { + a += String.fromCharCode(c); + } else { + var h = this.position - 1, l = 192, e = 5; + do { + var m = l >> 1 | 128; + if ((c & m) === l) { + break; + } + l = m; + --e; + } while (0 <= e); + l = c & (1 << e) - 1; + for (m = 5;m >= e;--m) { + if (c = this.readUI8(), 128 !== (c & 192)) { + for (c = this.position - 1, this.position = h;this.position < c;) { + a += String.fromCharCode(this.readUI8()); + } + } else { + l = l << 6 | c & 63; + } + } + a = 65536 <= l ? a + String.fromCharCode(l - 65536 >> 10 & 1023 | 55296, l & 1023 | 56320) : a + String.fromCharCode(l); + } + } + } + return a; + }; + a.prototype.readString = function() { + return this.readANSI ? this.readANSIString() : this.readUTF8String(); + }; + a.prototype.readBytes = function(a) { + var c = this.position, h = Math.max(this.end - c, 0); + h < a && (a = h); + h = this.array.subarray(c, c + a); + this.position = c + a; + return h; }; + return a; }(); - k.Symbol = c; - var n = function(a) { - function b(c, d, h) { - "undefined" === typeof h && (h = !0); - a.call(this, c, d); - this.dynamic = h; + c.ActionsDataStream = a; + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + var a = c.AVM1.ActionsDataStream; + (function(a) { + a[a.None = 0] = "None"; + a[a.ActionGotoFrame = 129] = "ActionGotoFrame"; + a[a.ActionGetURL = 131] = "ActionGetURL"; + a[a.ActionNextFrame = 4] = "ActionNextFrame"; + a[a.ActionPreviousFrame = 5] = "ActionPreviousFrame"; + a[a.ActionPlay = 6] = "ActionPlay"; + a[a.ActionStop = 7] = "ActionStop"; + a[a.ActionToggleQuality = 8] = "ActionToggleQuality"; + a[a.ActionStopSounds = 9] = "ActionStopSounds"; + a[a.ActionWaitForFrame = 138] = "ActionWaitForFrame"; + a[a.ActionSetTarget = 139] = "ActionSetTarget"; + a[a.ActionGoToLabel = 140] = "ActionGoToLabel"; + a[a.ActionPush = 150] = "ActionPush"; + a[a.ActionPop = 23] = "ActionPop"; + a[a.ActionAdd = 10] = "ActionAdd"; + a[a.ActionSubtract = 11] = "ActionSubtract"; + a[a.ActionMultiply = 12] = "ActionMultiply"; + a[a.ActionDivide = 13] = "ActionDivide"; + a[a.ActionEquals = 14] = "ActionEquals"; + a[a.ActionLess = 15] = "ActionLess"; + a[a.ActionAnd = 16] = "ActionAnd"; + a[a.ActionOr = 17] = "ActionOr"; + a[a.ActionNot = 18] = "ActionNot"; + a[a.ActionStringEquals = 19] = "ActionStringEquals"; + a[a.ActionStringLength = 20] = "ActionStringLength"; + a[a.ActionMBStringLength = 49] = "ActionMBStringLength"; + a[a.ActionStringAdd = 33] = "ActionStringAdd"; + a[a.ActionStringExtract = 21] = "ActionStringExtract"; + a[a.ActionMBStringExtract = 53] = "ActionMBStringExtract"; + a[a.ActionStringLess = 41] = "ActionStringLess"; + a[a.ActionToInteger = 24] = "ActionToInteger"; + a[a.ActionCharToAscii = 50] = "ActionCharToAscii"; + a[a.ActionMBCharToAscii = 54] = "ActionMBCharToAscii"; + a[a.ActionAsciiToChar = 51] = "ActionAsciiToChar"; + a[a.ActionMBAsciiToChar = 55] = "ActionMBAsciiToChar"; + a[a.ActionJump = 153] = "ActionJump"; + a[a.ActionIf = 157] = "ActionIf"; + a[a.ActionCall = 158] = "ActionCall"; + a[a.ActionGetVariable = 28] = "ActionGetVariable"; + a[a.ActionSetVariable = 29] = "ActionSetVariable"; + a[a.ActionGetURL2 = 154] = "ActionGetURL2"; + a[a.ActionGotoFrame2 = 159] = "ActionGotoFrame2"; + a[a.ActionSetTarget2 = 32] = "ActionSetTarget2"; + a[a.ActionGetProperty = 34] = "ActionGetProperty"; + a[a.ActionSetProperty = 35] = "ActionSetProperty"; + a[a.ActionCloneSprite = 36] = "ActionCloneSprite"; + a[a.ActionRemoveSprite = 37] = "ActionRemoveSprite"; + a[a.ActionStartDrag = 39] = "ActionStartDrag"; + a[a.ActionEndDrag = 40] = "ActionEndDrag"; + a[a.ActionWaitForFrame2 = 141] = "ActionWaitForFrame2"; + a[a.ActionTrace = 38] = "ActionTrace"; + a[a.ActionGetTime = 52] = "ActionGetTime"; + a[a.ActionRandomNumber = 48] = "ActionRandomNumber"; + a[a.ActionCallFunction = 61] = "ActionCallFunction"; + a[a.ActionCallMethod = 82] = "ActionCallMethod"; + a[a.ActionConstantPool = 136] = "ActionConstantPool"; + a[a.ActionDefineFunction = 155] = "ActionDefineFunction"; + a[a.ActionDefineLocal = 60] = "ActionDefineLocal"; + a[a.ActionDefineLocal2 = 65] = "ActionDefineLocal2"; + a[a.ActionDelete = 58] = "ActionDelete"; + a[a.ActionDelete2 = 59] = "ActionDelete2"; + a[a.ActionEnumerate = 70] = "ActionEnumerate"; + a[a.ActionEquals2 = 73] = "ActionEquals2"; + a[a.ActionGetMember = 78] = "ActionGetMember"; + a[a.ActionInitArray = 66] = "ActionInitArray"; + a[a.ActionInitObject = 67] = "ActionInitObject"; + a[a.ActionNewMethod = 83] = "ActionNewMethod"; + a[a.ActionNewObject = 64] = "ActionNewObject"; + a[a.ActionSetMember = 79] = "ActionSetMember"; + a[a.ActionTargetPath = 69] = "ActionTargetPath"; + a[a.ActionWith = 148] = "ActionWith"; + a[a.ActionToNumber = 74] = "ActionToNumber"; + a[a.ActionToString = 75] = "ActionToString"; + a[a.ActionTypeOf = 68] = "ActionTypeOf"; + a[a.ActionAdd2 = 71] = "ActionAdd2"; + a[a.ActionLess2 = 72] = "ActionLess2"; + a[a.ActionModulo = 63] = "ActionModulo"; + a[a.ActionBitAnd = 96] = "ActionBitAnd"; + a[a.ActionBitLShift = 99] = "ActionBitLShift"; + a[a.ActionBitOr = 97] = "ActionBitOr"; + a[a.ActionBitRShift = 100] = "ActionBitRShift"; + a[a.ActionBitURShift = 101] = "ActionBitURShift"; + a[a.ActionBitXor = 98] = "ActionBitXor"; + a[a.ActionDecrement = 81] = "ActionDecrement"; + a[a.ActionIncrement = 80] = "ActionIncrement"; + a[a.ActionPushDuplicate = 76] = "ActionPushDuplicate"; + a[a.ActionReturn = 62] = "ActionReturn"; + a[a.ActionStackSwap = 77] = "ActionStackSwap"; + a[a.ActionStoreRegister = 135] = "ActionStoreRegister"; + a[a.ActionInstanceOf = 84] = "ActionInstanceOf"; + a[a.ActionEnumerate2 = 85] = "ActionEnumerate2"; + a[a.ActionStrictEquals = 102] = "ActionStrictEquals"; + a[a.ActionGreater = 103] = "ActionGreater"; + a[a.ActionStringGreater = 104] = "ActionStringGreater"; + a[a.ActionDefineFunction2 = 142] = "ActionDefineFunction2"; + a[a.ActionExtends = 105] = "ActionExtends"; + a[a.ActionCastOp = 43] = "ActionCastOp"; + a[a.ActionImplementsOp = 44] = "ActionImplementsOp"; + a[a.ActionTry = 143] = "ActionTry"; + a[a.ActionThrow = 42] = "ActionThrow"; + a[a.ActionFSCommand2 = 45] = "ActionFSCommand2"; + a[a.ActionStrictMode = 137] = "ActionStrictMode"; + })(h.ActionCode || (h.ActionCode = {})); + var s = function() { + return function(a) { + this.registerNumber = a; + }; + }(); + h.ParsedPushRegisterAction = s; + var v = function() { + return function(a) { + this.constantIndex = a; + }; + }(); + h.ParsedPushConstantAction = v; + (function(a) { + a[a.None = 0] = "None"; + a[a.Argument = 1] = "Argument"; + a[a.This = 2] = "This"; + a[a.Arguments = 4] = "Arguments"; + a[a.Super = 8] = "Super"; + a[a.Global = 16] = "Global"; + a[a.Parent = 32] = "Parent"; + a[a.Root = 64] = "Root"; + })(h.ArgumentAssignmentType || (h.ArgumentAssignmentType = {})); + var p = function() { + function c(e, h) { + this._actionsData = e; + this.dataId = e.id; + this._stream = new a(e.bytes, h); + } + Object.defineProperty(c.prototype, "position", {get:function() { + return this._stream.position; + }, set:function(a) { + this._stream.position = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "eof", {get:function() { + return this._stream.position >= this._stream.end; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "length", {get:function() { + return this._stream.end; + }, enumerable:!0, configurable:!0}); + c.prototype.readNext = function() { + var a = this._stream, c = a.position, l = a.readUI8(), p = 128 <= l ? a.readUI16() : 0, p = a.position + p, n = null; + switch(l | 0) { + case 129: + var k = a.readUI16(), n = a.readUI8(), f = !1; + 6 !== n && 7 !== n ? a.position-- : (p++, f = 6 === n); + n = [k, f]; + break; + case 131: + k = a.readString(); + n = a.readString(); + n = [k, n]; + break; + case 138: + var k = a.readUI16(), d = a.readUI8(), n = [k, d]; + break; + case 139: + n = [a.readString()]; + break; + case 140: + k = a.readString(); + n = a.readUI8(); + f = !1; + 6 !== n && 7 !== n ? a.position-- : (p++, f = 6 === n); + n = [k, f]; + break; + case 150: + for (n = [];a.position < p;) { + k = a.readUI8(); + switch(k | 0) { + case 0: + k = a.readString(); + break; + case 1: + k = a.readFloat(); + break; + case 2: + k = null; + break; + case 3: + k = void 0; + break; + case 4: + k = new s(a.readUI8()); + break; + case 5: + k = a.readBoolean(); + break; + case 6: + k = a.readDouble(); + break; + case 7: + k = a.readInteger(); + break; + case 8: + k = new v(a.readUI8()); + break; + case 9: + k = new v(a.readUI16()); + break; + default: + console.error("Unknown value type: " + k); + a.position = p; + continue; + } + n.push(k); + } + break; + case 153: + k = a.readSI16(); + n = [k]; + break; + case 157: + k = a.readSI16(); + n = [k]; + break; + case 154: + k = a.readUI8(); + n = [k]; + break; + case 159: + k = a.readUI8(); + n = [k]; + k & 2 && n.push(a.readUI16()); + break; + case 141: + d = a.readUI8(); + n = [d]; + break; + case 136: + for (var d = a.readUI16(), k = [], b = 0;b < d;b++) { + k.push(a.readString()); + } + n = [k]; + break; + case 155: + n = a.readString(); + d = a.readUI16(); + f = []; + for (b = 0;b < d;b++) { + f.push(a.readString()); + } + k = a.readUI16(); + p += k; + k = new h.AVM1ActionsData(a.readBytes(k), this.dataId + "_f" + a.position, this._actionsData); + n = [k, n, f]; + break; + case 148: + k = a.readUI16(); + p += k; + n = [new h.AVM1ActionsData(a.readBytes(k), this.dataId + "_w" + a.position, this._actionsData)]; + break; + case 135: + var g = a.readUI8(), n = [g]; + break; + case 142: + for (var n = a.readString(), d = a.readUI16(), r = a.readUI8(), k = a.readUI16(), w = [], f = [], b = 0;b < d;b++) { + var g = a.readUI8(), z = a.readString(); + f.push(z); + g && (w[g] = {type:1, name:z, index:b}); + } + d = 1; + k & 1 && (w[d++] = {type:2}); + k & 4 && (w[d++] = {type:4}); + k & 16 && (w[d++] = {type:8}); + k & 64 && (w[d++] = {type:64}); + k & 128 && (w[d++] = {type:32}); + k & 256 && (w[d++] = {type:16}); + d = 0; + k & 2 && (d |= 2); + k & 8 && (d |= 4); + k & 32 && (d |= 8); + k = a.readUI16(); + p += k; + k = new h.AVM1ActionsData(a.readBytes(k), this.dataId + "_f" + a.position, this._actionsData); + n = [k, n, f, r, w, d]; + break; + case 143: + k = a.readUI8(); + n = !!(k & 4); + f = !!(k & 2); + k = !!(k & 1); + d = a.readUI16(); + b = a.readUI16(); + w = a.readUI16(); + r = n ? a.readUI8() : a.readString(); + p += d + b + w; + d = new h.AVM1ActionsData(a.readBytes(d), this.dataId + "_t" + a.position, this._actionsData); + b = new h.AVM1ActionsData(a.readBytes(b), this.dataId + "_c" + a.position, this._actionsData); + w = new h.AVM1ActionsData(a.readBytes(w), this.dataId + "_z" + a.position, this._actionsData); + n = [n, r, d, k, b, f, w]; + break; + case 137: + n = [a.readUI8()]; + } + a.position = p; + return{position:c, actionCode:l, actionName:u[l], args:n}; + }; + c.prototype.skip = function(a) { + for (var c = this._stream;0 < a && c.position < c.end;) { + var h = 128 <= c.readUI8() ? c.readUI16() : 0; + c.position += h; + a--; + } + }; + return c; + }(); + h.ActionsDataParser = p; + var u = {0:"EOA", 4:"ActionNextFrame", 5:"ActionPreviousFrame", 6:"ActionPlay", 7:"ActionStop", 8:"ActionToggleQuality", 9:"ActionStopSounds", 10:"ActionAdd", 11:"ActionSubtract", 12:"ActionMultiply", 13:"ActionDivide", 14:"ActionEquals", 15:"ActionLess", 16:"ActionAnd", 17:"ActionOr", 18:"ActionNot", 19:"ActionStringEquals", 20:"ActionStringLength", 21:"ActionStringExtract", 23:"ActionPop", 24:"ActionToInteger", 28:"ActionGetVariable", 29:"ActionSetVariable", 32:"ActionSetTarget2", 33:"ActionStringAdd", + 34:"ActionGetProperty", 35:"ActionSetProperty", 36:"ActionCloneSprite", 37:"ActionRemoveSprite", 38:"ActionTrace", 39:"ActionStartDrag", 40:"ActionEndDrag", 41:"ActionStringLess", 42:"ActionThrow", 43:"ActionCastOp", 44:"ActionImplementsOp", 45:"ActionFSCommand2", 48:"ActionRandomNumber", 49:"ActionMBStringLength", 50:"ActionCharToAscii", 51:"ActionAsciiToChar", 52:"ActionGetTime", 53:"ActionMBStringExtract", 54:"ActionMBCharToAscii", 55:"ActionMBAsciiToChar", 58:"ActionDelete", 59:"ActionDelete2", + 60:"ActionDefineLocal", 61:"ActionCallFunction", 62:"ActionReturn", 63:"ActionModulo", 64:"ActionNewObject", 65:"ActionDefineLocal2", 66:"ActionInitArray", 67:"ActionInitObject", 68:"ActionTypeOf", 69:"ActionTargetPath", 70:"ActionEnumerate", 71:"ActionAdd2", 72:"ActionLess2", 73:"ActionEquals2", 74:"ActionToNumber", 75:"ActionToString", 76:"ActionPushDuplicate", 77:"ActionStackSwap", 78:"ActionGetMember", 79:"ActionSetMember", 80:"ActionIncrement", 81:"ActionDecrement", 82:"ActionCallMethod", + 83:"ActionNewMethod", 84:"ActionInstanceOf", 85:"ActionEnumerate2", 96:"ActionBitAnd", 97:"ActionBitOr", 98:"ActionBitXor", 99:"ActionBitLShift", 100:"ActionBitRShift", 101:"ActionBitURShift", 102:"ActionStrictEquals", 103:"ActionGreater", 104:"ActionStringGreater", 105:"ActionExtends", 129:"ActionGotoFrame", 131:"ActionGetURL", 135:"ActionStoreRegister", 136:"ActionConstantPool", 137:"ActionStrictMode", 138:"ActionWaitForFrame", 139:"ActionSetTarget", 140:"ActionGoToLabel", 141:"ActionWaitForFrame2", + 142:"ActionDefineFunction2", 143:"ActionTry", 148:"ActionWith", 150:"ActionPush", 153:"ActionJump", 154:"ActionGetURL2", 155:"ActionDefineFunction", 157:"ActionIf", 158:"ActionCall", 159:"ActionGotoFrame2"}; + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + var a = function() { + function a() { + this.parentResults = null; + this.registersLimit = 0; + } + a.prototype.analyze = function(a) { + for (var c = [], h = [0], l = [!0], e = !1, m = null, s = [0];0 < s.length;) { + var q = s.shift(); + if (!c[q]) { + for (a.position = q;!a.eof && !c[q];) { + var n = a.readNext(); + if (0 === n.actionCode) { + break; + } + var k = a.position, f = {action:n, next:k, conditionalJumpTo:-1}, d = 0, b = !1, g = !1; + switch(n.actionCode) { + case 138: + ; + case 141: + b = !0; + a.skip(138 === n.actionCode ? n.args[1] : n.args[0]); + d = a.position; + a.position = k; + break; + case 153: + b = g = !0; + d = k + n.args[0]; + break; + case 157: + b = !0; + d = k + n.args[0]; + break; + case 42: + ; + case 62: + ; + case 0: + b = g = !0; + d = a.length; + break; + case 136: + if (e) { + m = null; + break; + } + e = !0; + 0 === q && (m = n.args[0]); + } + if (b) { + if (0 > d || d > a.length) { + console.error("jump outside the action block;"), d = a.length; + } + g ? f.next = d : f.conditionalJumpTo = d; + l[d] || (h.push(d), s.push(d), l[d] = !0); + } + c[q] = f; + if (g) { + break; + } + q = k; + } + } + } + var r = []; + h.forEach(function(a) { + if (c[a]) { + var b = [], d = a; + do { + d = c[d], b.push(d), d = d.next; + } while (!l[d] && c[d]); + r.push({label:a, items:b, jump:d}); + } + }); + h = null; + e ? h = m : this.parentResults && (h = this.parentResults.singleConstantPool); + return{actions:c, blocks:r, dataId:a.dataId, singleConstantPool:h, registersLimit:this.registersLimit}; + }; + return a; + }(); + c.ActionsDataAnalyzer = a; + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + var a = c.Debug.assert; + h.AVM1ActionsData = function() { + return function(c, h, s) { + void 0 === s && (s = null); + this.bytes = c; + this.id = h; + this.parent = s; + a(c instanceof Uint8Array); + }; + }(); + var s = function() { + function a() { + this.globals = this.root = null; + } + a.prototype.flushPendingScripts = function() { + }; + a.prototype.addAsset = function(a, c, h) { + }; + a.prototype.registerClass = function(a, c) { + }; + a.prototype.getAsset = function(a) { + }; + a.prototype.resolveTarget = function(a) { + }; + a.prototype.resolveLevel = function(a) { + }; + a.prototype.addToPendingScripts = function(a) { + }; + a.prototype.registerEventPropertyObserver = function(a, c) { + }; + a.prototype.unregisterEventPropertyObserver = function(a, c) { + }; + a.prototype.enterContext = function(a, c) { + }; + a.prototype.executeActions = function(a, c) { + }; + a.instance = null; + return a; + }(); + h.AVM1Context = s; + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +__extends = this.__extends || function(c, h) { + function a() { + this.constructor = c; + } + for (var s in h) { + h.hasOwnProperty(s) && (c[s] = h[s]); + } + a.prototype = h.prototype; + c.prototype = new a; +}; +(function(c) { + (function(h) { + function a(a, b, d, c, e) { + if (h.avm1ErrorsEnabled.value) { + try { + throw Error(a); + } catch (g) { + } + } + console.warn.apply(console, arguments); + } + function s(a) { + if (null === a) { + return "null"; } - __extends(b, a); - b.prototype._setBoundsFromData = function(a) { - this.fillBounds = a.fillBounds ? m.FromUntyped(a.fillBounds) : null; - this.lineBounds = a.lineBounds ? m.FromUntyped(a.lineBounds) : null; - !this.lineBounds && this.fillBounds && (this.lineBounds = this.fillBounds.clone()); - }; - return b; - }(c); - k.DisplaySymbol = n; - var p = function(a) { - function b(c, e) { - "undefined" === typeof e && (e = d.display.Shape); - a.call(this, c, e, !1); - this.graphics = null; + var b = typeof a; + return "function" === b ? "object" : "object" === b && "object" === typeof a && a && a instanceof h.Lib.AVM1MovieClip ? "movieclip" : b; + } + function v(a) { + return "object" !== s(a) ? a : a.valueOf(); + } + function p() { + return h.AVM1Context.instance.loaderInfo.swfVersion; + } + function u(a) { + return "object" !== s(a) ? a : a instanceof Date && 6 <= p() ? a.toString() : a.valueOf(); + } + function l(a) { + switch(s(a)) { + default: + ; + case "undefined": + ; + case "null": + return!1; + case "boolean": + return a; + case "number": + return 0 !== a && !isNaN(a); + case "string": + return 0 !== a.length; + case "movieclip": + ; + case "object": + return!0; } - __extends(b, a); - b.FromData = function(a, c) { - var h = new b(a.id); - h._setBoundsFromData(a); - h.graphics = d.display.Graphics.FromData(a); - h.processRequires(a.require, c); - return h; - }; - b.prototype.processRequires = function(a, b) { - if (a) { - for (var c = this.graphics._textures, d = 0;d < a.length;d++) { - var e = b.getSymbolById(a[d]); - t(e, "Bitmap symbol is not defined."); - var f = e.symbolClass.initializeFrom(e); - e.symbolClass.instanceConstructorNoInitialize.call(f); - c.push(f); + } + function e(a) { + a = v(a); + switch(s(a)) { + case "undefined": + ; + case "null": + return 7 <= p() ? NaN : 0; + case "boolean": + return a ? 1 : 0; + case "number": + return a; + case "string": + return "" === a && 5 > p() ? 0 : +a; + default: + return 5 <= p() ? NaN : 0; + } + } + function m(a) { + a = e(a); + return isNaN(a) || !isFinite(a) || 0 === a ? 0 : a | 0; + } + function t(a) { + switch(s(a)) { + case "undefined": + return 7 <= p() ? "undefined" : ""; + case "null": + return "null"; + case "boolean": + return a ? "true" : "false"; + case "number": + return a.toString(); + case "string": + return a; + case "movieclip": + return a.__targetPath; + case "object": + if ("function" === typeof a && a.asGetPublicProperty("toString") === c.AVM2.AS.ASFunction.traitsPrototype.asGetPublicProperty("toString")) { + return "[type Function]"; } - } - }; - return b; - }(n); - k.ShapeSymbol = p; - p = function(a) { - function b(c) { - a.call(this, c, d.display.MorphShape); + var b = a.asCallPublicProperty("toString", null); + return "string" === typeof b ? b : "function" === typeof a ? "[type Function]" : "[type Object]"; } - __extends(b, a); - b.FromData = function(a, c) { - var h = new b(a.id); - h._setBoundsFromData(a); - h.graphics = d.display.Graphics.FromData(a); - h.processRequires(a.require, c); - h.morphFillBounds = a.morphFillBounds; - h.morphLineBounds = a.morphLineBounds; - return h; - }; - return b; - }(p); - k.MorphShapeSymbol = p; - p = function(a) { - function b(c) { - a.call(this, c, d.display.BitmapData); + } + function q(a, b) { + var d = v(a), c = v(b); + if ("string" !== typeof d || "string" !== typeof c) { + return d = e(d), c = e(c), isNaN(d) || isNaN(c) ? void 0 : d < c; } - __extends(b, a); - b.FromData = function(a) { - var c = new b(a.id); - c.width = a.width; - c.height = a.height; - c.data = a.data; - switch(a.mimeType) { - case "application/octet-stream": - c.type = a.dataType; - break; - case "image/jpeg": - c.type = 4; - break; - case "image/png": - c.type = 5; - break; - case "image/gif": - c.type = 6; - break; - default: - g(a.mimeType); - } - return c; - }; - return b; - }(n); - k.BitmapSymbol = p; - p = function(a) { - function c(b) { - a.call(this, b, d.text.TextField); - this.size = this.color = 0; - this.font = ""; - this.fontClass = null; - this.align = d.text.TextFormatAlign.LEFT; - this.leading = this.indent = this.rightMargin = this.leftMargin = 0; - this.embedFonts = this.wordWrap = this.multiline = !1; - this.selectable = !0; - this.border = !1; - this.initialText = ""; - this.displayAsPassword = this.html = !1; - this.type = d.text.TextFieldType.DYNAMIC; - this.maxChars = 0; - this.autoSize = d.text.TextFieldAutoSize.NONE; - this.textContent = this.variableName = null; + } + function n(a, b) { + if (c.isNullOrUndefined(a) || c.isNullOrUndefined(b)) { + return!1; } - __extends(c, a); - c.FromTextData = function(a) { - var e = new c(a.id); - e._setBoundsFromData(a); - var h = a.tag; - if (a.static && (e.dynamic = !1, e.symbolClass = d.text.StaticText, h.initialText)) { - var f = new b.TextContent; - f.bounds = e.lineBounds; - f.parseHtml(h.initialText); - f.matrix = d.geom.Matrix.FromUntyped(a.matrix); - f.coords = a.coords; - e.textContent = f; - } - h.hasColor && (e.color = h.color >>> 8); - h.hasFont && (e.size = h.fontHeight, (a = d.text.Font.getBySymbolId(h.fontId)) ? (e.font = a.fontName, h.fontClass && (e.fontClass = b.AVM2.Runtime.AVM2.instance.applicationDomain.getClass(h.fontClass))) : s("Font " + h.fontId + " is not defined.")); - h.hasLayout && (e.align = d.text.TextFormatAlign.fromNumber(h.align), e.leftMargin = h.leftMargin, e.rightMargin = h.rightMargin, e.indent = h.indent, e.leading = h.leading); - e.multiline = !!h.multiline; - e.wordWrap = !!h.wordWrap; - e.embedFonts = !!h.useOutlines; - e.selectable = !h.noSelect; - e.border = !!h.border; - h.hasText && (e.initialText = h.initialText); - e.html = !!h.html; - e.displayAsPassword = !!h.password; - e.type = h.readonly ? d.text.TextFieldType.DYNAMIC : d.text.TextFieldType.INPUT; - h.hasMaxLength && (e.maxChars = h.maxLength); - e.autoSize = h.autoSize ? d.text.TextFieldAutoSize.LEFT : d.text.TextFieldAutoSize.NONE; - e.variableName = h.variableName; - return e; - }; - return c; - }(n); - k.TextSymbol = p; - p = function(b) { - function c(a) { - b.call(this, a, d.display.SimpleButton); - this.hitTestState = this.downState = this.overState = this.upState = null; + if (b === c.AVM2.AS.ASString) { + return "string" === typeof a; } - __extends(c, b); - c.FromData = function(b, e) { - var h = new c(b.id); - e.actionScriptVersion === a.ACTIONSCRIPT2 && (h.isAVM1Object = !0, h.buttonActions = b.buttonActions); - var f = b.states, g, l, m, n; - for (n in f) { - var p = f[n]; - 1 === p.length ? (p = p[0], g = e.getSymbolById(p.symbolId), l = d.geom.Matrix.FromUntyped(p.matrix), p.cxform && (m = d.geom.ColorTransform.FromCXForm(p.cxform))) : (g = new k.SpriteSymbol(-1), g.frames.push(new q(e, p))); - h[n + "State"] = new k.AnimationState(g, 0, l, m); + if (b === c.AVM2.AS.ASNumber) { + return "number" === typeof a; + } + if (b === c.AVM2.AS.ASBoolean) { + return "boolean" === typeof a; + } + if (b === c.AVM2.AS.ASArray) { + return Array.isArray(a); + } + if (b === c.AVM2.AS.ASFunction) { + return "function" === typeof a; + } + if (b === c.AVM2.AS.ASObject) { + return "object" === typeof a; + } + var d = b.asGetPublicProperty("prototype"); + if (!d) { + return!1; + } + for (var e = a;e;) { + if (e === d) { + return!0; } - return h; - }; - return c; - }(n); - k.ButtonSymbol = p; - n = function(b) { - function c(a, e) { - "undefined" === typeof e && (e = !1); - b.call(this, a, d.display.MovieClip); - this.numFrames = 1; - this.frames = []; - this.labels = []; - this.frameScripts = []; - this.isRoot = e; + e = e.asGetPublicProperty("__proto__"); } - __extends(c, b); - c.FromData = function(b, e) { - var h = new c(b.id); - h.numFrames = b.frameCount; - e.actionScriptVersion === a.ACTIONSCRIPT2 && (h.isAVM1Object = !0, h.avm1Context = e._avm1Context); - h.frameScripts = b.frameScripts; - for (var f = b.frames, g = 1, k = 0;k < f.length;k++) { - for (var l = f[k], m = new q(e, l.commands), n = l.repeat;n--;) { - h.frames.push(m); - } - l.labelName && h.labels.push(new d.display.FrameLabel(l.labelName, g)); - g += l.repeat; + return!1; + } + function k(a, b, d) { + var c = Object.create(null); + do { + Yb(a, function(e) { + c[e] || (b.call(d, a, e), c[e] = !0); + }), a = a.asGetPublicProperty("__proto__"); + } while (a); + } + function f(a, b) { + do { + if (a.asHasProperty(void 0, b, 0)) { + return sa.link = a, sa.name = b, sa; } - return h; - }; - return c; - }(n); - k.SpriteSymbol = n; - n = function(a) { - function b(c) { - a.call(this, c, d.text.Font); - this.name = ""; - this.italic = this.bold = !1; + a = a.asGetPublicProperty("__proto__"); + } while (a); + return null; + } + function d(b, d, e) { + if (c.isNullOrUndefined(b)) { + return a("AVM1 warning: cannot look up member '" + d + "' on undefined object"), null; + } + var g; + b = Object(b); + if (null !== (g = f(b, d))) { + return g; } - __extends(b, a); - b.FromData = function(a) { - var c = new b(a.id); - c.name = a.name; - c.bold = a.bold; - c.italic = a.italic; - c.data = a.data; - c.metrics = a.metrics; - return c; - }; - return b; - }(c); - k.FontSymbol = n; - n = function(a) { - function b(c) { - a.call(this, c, d.media.Sound); + if (Zb(d) || 6 < p()) { + return e ? (sa.link = b, sa.name = d, sa) : null; } - __extends(b, a); - b.FromData = function(a) { - var c = new b(a.id); - c.channels = a.channels; - c.sampleRate = a.sampleRate; - c.pcm = a.pcm; - c.packaged = a.packaged; - return c; - }; - return b; - }(c); - k.SoundSymbol = n; - c = function(a) { - function b(c) { - a.call(this, c, d.utils.ByteArray); + var h = d.toLowerCase(); + if (null !== (g = f(b, h))) { + return g; } - __extends(b, a); - b.FromData = function(a) { - var c = new b(a.id); - c.buffer = a.data; - c.byteLength = a.data.byteLength; - return c; - }; - return b; - }(c); - k.BinarySymbol = c; - c = function() { - function a(b, c, d, h, e, f, g, k, l, m, n, p, q) { - "undefined" === typeof b && (b = null); - "undefined" === typeof c && (c = 0); - "undefined" === typeof d && (d = null); - "undefined" === typeof h && (h = null); - "undefined" === typeof e && (e = 0); - "undefined" === typeof f && (f = null); - "undefined" === typeof g && (g = -1); - "undefined" === typeof k && (k = null); - "undefined" === typeof l && (l = null); - "undefined" === typeof m && (m = !1); - "undefined" === typeof n && (n = !0); - "undefined" === typeof p && (p = null); - "undefined" === typeof q && (q = null); - this.symbol = b; - this.depth = c; - this.matrix = d; - this.colorTransform = h; - this.ratio = e; - this.name = f; - this.clipDepth = g; - this.filters = k; - this.blendMode = l; - this.cacheAsBitmap = m; - this.visible = n; - this.events = p; - this.variableName = q; + null === Ka && (Ka = Object.create(null), $b.forEach(function(a) { + Ka[a.toLowerCase()] = a; + })); + var l = Ka[h] || null; + if (l && null !== (g = f(b, l))) { + return g; } - a.prototype.canBeAnimated = function(a) { - if (!a._hasFlags(2048) || a._depth !== this.depth) { - return!1; + var m = null, r = null; + k(b, function(a, b) { + null === m && b.toLowerCase() === h && (r = a, m = b); + }, null); + return m ? (sa.link = r, sa.name = m, sa) : e ? (sa.link = b, sa.name = l || d, sa) : null; + } + function b(a, b) { + var c = d(a, b, !1); + return c ? c.link.asGetPublicProperty(c.name) : void 0; + } + function g(a, b, c) { + if (b = d(a, b, !0)) { + var e = b.link.asGetPropertyDescriptor(void 0, b.name, 0); + !e || "value" in e ? a.asSetPublicProperty(b.name, c) : b.link.asSetPublicProperty(b.name, c); + r(b.name); + } + } + function r(a) { + "o" === a[0] && "n" === a[1] && h.AVM1Context.instance.broadcastEventPropertyChange(a); + } + function w(a) { + return "undefined" !== typeof InternalError && a instanceof InternalError && "too much recursion" === a.message ? new ua("long running script -- AVM1 recursion limit is reached") : a; + } + function z(a, b, d) { + a.asSetPublicProperty("__proto__", b); + a.asDefinePublicProperty("__constructor__", {value:d, writable:!0, enumerable:!1, configurable:!1}); + } + function A(a, b) { + var d; + if (a instanceof c.AVM2.AS.ASClass) { + d = ac(a, b), z(d, a.asGetPublicProperty("prototype"), a); + } else { + if (Na(a)) { + for (var e = a.asGetPublicProperty("prototype");e && !e.initAVM1ObjectInstance;) { + e = e.asGetPublicProperty("__proto__"); + } + d = e ? Object.create(e) : {}; + z(d, a.asGetPublicProperty("prototype"), a); + e && e.initAVM1ObjectInstance.call(d, h.AVM1Context.instance); + a.apply(d, b); + } else { + return; } - var b = this.symbol; - return!b || !b.dynamic && a._clipDepth === this.clipDepth && b.symbolClass.isType(a) ? !0 : !1; - }; - return a; - }(); - k.AnimationState = c; - var e = function() { - return function(a, b) { - this.soundId = a; - this.soundInfo = b; - }; - }(); - k.SoundStart = e; - var q = function() { - function c(a, b) { - this.loaderInfo = a; - this.commands = b; - this._stateAtDepth = null; - this._soundStarts = void 0; } - Object.defineProperty(c.prototype, "stateAtDepth", {get:function() { - return this._stateAtDepth || this._initialize(); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(c.prototype, "soundStarts", {get:function() { - void 0 === this._soundStarts && this._initialize(); - return this._soundStarts; - }, enumerable:!0, configurable:!0}); - c.prototype._initialize = function() { - for (var c = this._stateAtDepth = Object.create(null), f = null, g = this.commands, h = this.loaderInfo, l = 0;l < g.length;l++) { - var m = g[l], n = m.depth; - switch(m.code) { - case 5: - ; - case 28: - c[n] = null; - break; - case 15: - f || (f = []); - f.push(new e(m.soundId, m.soundInfo)); - break; - default: - var p = null, q = null, E = null, O = null, K = null; - if (m.symbolId && (p = h.getSymbolById(m.symbolId), !p)) { - s("Symbol " + m.symbolId + " is not defined."); - continue; - } - m.flags & 4 && (q = d.geom.Matrix.FromUntyped(m.matrix)); - m.flags & 8 && (E = d.geom.ColorTransform.FromCXForm(m.cxform)); - if (m.flags & 256) { - for (var O = [], F = m.filters, J = 0;J < F.length;J++) { - var A = F[J], B; - switch(A.type) { - case 0: - B = d.filters.DropShadowFilter.FromUntyped(A); - break; - case 1: - B = d.filters.BlurFilter.FromUntyped(A); - break; - case 2: - B = d.filters.GlowFilter.FromUntyped(A); - break; - case 3: - B = d.filters.BevelFilter.FromUntyped(A); - break; - case 4: - B = d.filters.GradientGlowFilter.FromUntyped(A); - break; - case 5: - B = d.filters.ConvolutionFilter.FromUntyped(A); - break; - case 6: - B = d.filters.ColorMatrixFilter.FromUntyped(A); - break; - case 7: - B = d.filters.GradientBevelFilter.FromUntyped(A); - } - t(B, "Unknown filter type."); - O.push(B); - } - } - if (m.flags & 128 && h._allowCodeExecution && h._actionScriptVersion === a.ACTIONSCRIPT2) { - for (F = m.events, K = [], J = 0;J < F.length;J++) { - A = F[J]; - if (A.eoe) { - break; - } - var z = function(a, c) { - return function() { - var d = c._avm1Context, h = b.AVM2.AS.avm1lib.getAVM1Object(this); - return d.executeActions(a, h); - }; - }(new b.AVM1.AVM1ActionsData(A.actionsData, "s" + m.symbolId + "e" + J), h), P = [], D; - for (D in A) { - if (0 === D.indexOf("on") && A[D]) { - var L = D[2].toLowerCase() + D.substring(3); - "enterFrame" === L && (L = "frameConstructed"); - P.push(L); - } - } - K.push({eventNames:P, handler:z, keyPress:A.keyPress}); - } - } - m = new k.AnimationState(p, n, q, E, m.ratio, m.name, m.clipDepth, O, d.display.BlendMode.fromNumber(m.blendMode), !!(m.flags & 1024), m.flags & 512 ? !!m.visibility : !0, K, m.variableName); - c[n] = m; - } + return d; + } + function B(a, b) { + k(a, function(a, d) { + return b.call(null, d); + }); + } + function M(a, b) { + if (6 > p()) { + return null; + } + var c; + c = a.inSequence && a.previousFrame.calleeSuper; + if (!c) { + c = d(a.currentThis, b, !1); + if (!c) { + return null; } - this._soundStarts = f; - this.commands = null; - return c; - }; - return c; - }(); - k.FrameDelta = q; - })(b.Timeline || (b.Timeline = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - function f(a, b, d) { - return{classSimpleName:a, nativeName:b, cls:d}; - } - function t(c, d, f) { - Object.defineProperty(c, f, {get:function() { - a(b.AVM2.Runtime.AVM2.instance, "AVM2 needs to be initialized."); - var e = b.AVM2.Runtime.AVM2.instance.systemDomain.getClass(d); - a(e.instanceConstructor); - Object.defineProperty(c, f, {value:e.instanceConstructor, writable:!1}); - return c[f]; - }, configurable:!0}); + c = c.link; } - var s = b.AVM2.Runtime.throwError, m = b.AVM2.AS.flash, d = b.AVM2.ABC.Multiname, a = b.Debug.assert; - jsGlobal.flash = b.AVM2.AS.flash; - g.linkNatives = function(a) { - [f("flash.display.DisplayObject", "DisplayObjectClass", m.display.DisplayObject), f("flash.display.InteractiveObject", "InteractiveObjectClass", m.display.InteractiveObject), f("flash.display.DisplayObjectContainer", "ContainerClass", m.display.DisplayObjectContainer), f("flash.display.Sprite", "SpriteClass", m.display.Sprite), f("flash.display.MovieClip", "MovieClipClass", m.display.MovieClip), f("flash.display.Shape", "ShapeClass", m.display.Shape), f("flash.display.Bitmap", "BitmapClass", - m.display.Bitmap), f("flash.display.BitmapData", "BitmapDataClass", m.display.BitmapData), f("flash.display.Stage", "StageClass", m.display.Stage), f("flash.display.Loader", "LoaderClass", m.display.Loader), f("flash.display.LoaderInfo", "LoaderInfoClass", m.display.LoaderInfo), f("flash.display.Graphics", "GraphicsClass", m.display.Graphics), f("flash.display.SimpleButton", "SimpleButtonClass", m.display.SimpleButton), f("flash.display.MorphShape", "MorphShapeClass", m.display.MorphShape), - f("flash.display.NativeMenu", "MenuClass", m.display.NativeMenu), f("flash.display.NativeMenuItem", "MenuItemClass", m.display.NativeMenuItem), f("flash.display.FrameLabel", "FrameLabelClass", m.display.FrameLabel), f("flash.display.Scene", "SceneClass", m.display.Scene), f("flash.display.AVM1Movie", "AVM1MovieClass", m.display.AVM1Movie), f("flash.filters.BevelFilter", "BevelFilterClass", m.filters.BevelFilter), f("flash.filters.BitmapFilter", "BitmapFilterClass", m.filters.BitmapFilter), - f("flash.filters.BlurFilter", "BlurFilterClass", m.filters.BlurFilter), f("flash.filters.ColorMatrixFilter", "ColorMatrixFilterClass", m.filters.ColorMatrixFilter), f("flash.filters.ConvolutionFilter", "ConvolutionFilterClass", m.filters.ConvolutionFilter), f("flash.filters.DisplacementMapFilter", "DisplacementMapFilterClass", m.filters.DisplacementMapFilter), f("flash.filters.DropShadowFilter", "DropShadowFilterClass", m.filters.DropShadowFilter), f("flash.filters.GlowFilter", "GlowFilterClass", - m.filters.GlowFilter), f("flash.filters.GradientBevelFilter", "GradientBevelFilterClass", m.filters.GradientBevelFilter), f("flash.filters.GradientGlowFilter", "GradientGlowFilterClass", m.filters.GradientGlowFilter), f("flash.geom.Point", "PointClass", m.geom.Point), f("flash.geom.Rectangle", "RectangleClass", m.geom.Rectangle), f("flash.geom.Matrix", "MatrixClass", m.geom.Matrix), f("flash.geom.Matrix3D", "Matrix3DClass", m.geom.Matrix3D), f("flash.geom.Vector3D", "Vector3DClass", m.geom.Vector3D), - f("flash.geom.Transform", "TransformClass", m.geom.Transform), f("flash.geom.ColorTransform", "ColorTransformClass", m.geom.ColorTransform), f("flash.events.EventDispatcher", "EventDispatcherClass", m.events.EventDispatcher), f("flash.events.Event", "EventClass", m.events.Event), f("flash.events.IOErrorEvent"), f("flash.events.KeyboardEvent", "KeyboardEventClass", m.events.KeyboardEvent), f("flash.events.MouseEvent", "MouseEventClass", m.events.MouseEvent), f("flash.events.GestureEvent", - "GestureEventClass", m.events.GestureEvent), f("flash.events.TextEvent", "TextEventClass", m.events.TextEvent), f("flash.events.TimerEvent", "TimerEventClass", m.events.TimerEvent), f("flash.events.ProgressEvent", "ProgressEventClass", m.events.ProgressEvent), f("flash.events.NetStatusEvent"), f("flash.events.HTTPStatusEvent"), f("flash.external.ExternalInterface", "ExternalInterfaceClass", m.external.ExternalInterface), f("flash.ui.ContextMenu", "ContextMenuClass", m.ui.ContextMenu), f("flash.ui.ContextMenuItem", - "ContextMenuItemClass", m.ui.ContextMenuItem), f("flash.ui.Keyboard", "KeyboardClass", m.ui.Keyboard), f("flash.ui.Mouse", "MouseClass", m.ui.Mouse), f("flash.ui.MouseCursorData", "MouseCursorDataClass", m.ui.MouseCursorData), f("flash.ui.GameInput", "GameInputClass", m.ui.GameInput), f("flash.events.GameInputEvent", "GameInputEventClass", m.events.GameInputEvent), f("flash.ui.GameInputControl", "GameInputControlClass", m.ui.GameInputControl), f("flash.ui.GameInputControlType", "GameInputControlTypeClass", - m.ui.GameInputControlType), f("flash.ui.GameInputDevice", "GameInputDeviceClass", m.ui.GameInputDevice), f("flash.ui.GameInputFinger", "GameInputFingerClass", m.ui.GameInputFinger), f("flash.ui.GameInputHand", "GameInputHandClass", m.ui.GameInputHand), f("flash.ui.Multitouch", "MultitouchClass", m.ui.Multitouch), f("flash.ui.MultitouchInputMode", "MultitouchInputModeClass", m.ui.MultitouchInputMode), f("flash.events.TouchEvent", "TouchEventClass", m.events.TouchEvent), f("flash.text.Font", - "FontClass", m.text.Font), f("flash.text.TextField", "TextFieldClass", m.text.TextField), f("flash.text.StaticText", "StaticTextClass", m.text.StaticText), f("flash.text.StyleSheet", "StyleSheetClass", m.text.StyleSheet), f("flash.text.TextFormat", "TextFormatClass", m.text.TextFormat), f("flash.text.TextRun", "TextRunClass", m.text.TextRun), f("flash.text.TextLineMetrics"), f("flash.media.Sound", "SoundClass", m.media.Sound), f("flash.media.SoundChannel", "SoundChannelClass", m.media.SoundChannel), - f("flash.media.SoundMixer", "SoundMixerClass", m.media.SoundMixer), f("flash.media.SoundTransform", "SoundTransformClass", m.media.SoundTransform), f("flash.media.Video", "VideoClass", m.media.Video), f("flash.media.ID3Info", "ID3InfoClass", m.media.ID3Info), f("flash.media.Microphone", "MicrophoneClass", m.media.Microphone), f("flash.net.FileFilter", "FileFilterClass", m.net.FileFilter), f("flash.net.NetConnection", "NetConnectionClass", m.net.NetConnection), f("flash.net.NetStream", "NetStreamClass", - m.net.NetStream), f("flash.net.Responder", "ResponderClass", m.net.Responder), f("flash.net.URLRequest", "URLRequestClass", m.net.URLRequest), f("flash.net.URLRequestHeader"), f("flash.net.URLStream", "URLStreamClass", m.net.URLStream), f("flash.net.URLLoader", "URLLoaderClass", m.net.URLLoader), f("flash.net.SharedObject", "SharedObjectClass", m.net.SharedObject), f("flash.net.ObjectEncoding", "ObjectEncodingClass", m.net.ObjectEncoding), f("flash.net.LocalConnection", "LocalConnectionClass", - m.net.LocalConnection), f("flash.net.Socket", "SocketClass", m.net.Socket), f("flash.net.URLVariables", "URLVariablesClass", m.net.URLVariables), f("packageInternal flash.system.FSCommand", "FSCommandClass", m.system.FSCommand), f("flash.system.Capabilities", "CapabilitiesClass", m.system.Capabilities), f("flash.system.Security", "SecurityClass", m.system.Security), f("flash.system.SecurityDomain", "SecurityDomainClass", m.system.SecurityDomain), f("flash.system.ApplicationDomain", "ApplicationDomainClass", - m.system.ApplicationDomain), f("flash.system.JPEGLoaderContext", "JPEGLoaderContextClass", m.system.JPEGLoaderContext), f("flash.accessibility.Accessibility", "AccessibilityClass", m.accessibility.Accessibility), f("flash.utils.Timer", "TimerClass", m.utils.Timer), f("flash.utils.ByteArray", "ByteArrayClass", m.utils.ByteArray), f("avm1lib.AVM1Utils", "AVM1Utils", b.AVM2.AS.avm1lib.AVM1Utils), f("avm1lib.AVM1Broadcaster"), f("avm1lib.AVM1Key"), f("avm1lib.AVM1Mouse"), f("avm1lib.AVM1MovieClip", - "AVM1MovieClip", b.AVM2.AS.avm1lib.AVM1MovieClip), f("avm1lib.AVM1BitmapData", "AVM1BitmapData", b.AVM2.AS.avm1lib.AVM1BitmapData), f("avm1lib.AVM1Button", "AVM1Button", b.AVM2.AS.avm1lib.AVM1Button), f("avm1lib.AVM1Sound"), f("avm1lib.AVM1TextField", "AVM1TextField", b.AVM2.AS.avm1lib.AVM1TextField), f("avm1lib.AVM1Stage"), f("avm1lib.AVM1System"), f("avm1lib.AVM1Color"), f("avm1lib.AVM1Transform"), f("avm1lib.AVM1Globals", "AVM1Globals", b.AVM2.AS.avm1lib.AVM1Globals), f("avm1lib.AVM1MovieClipLoader", - "AVM1MovieClipLoader", b.AVM2.AS.avm1lib.AVM1MovieClipLoader)].forEach(function(a) { - for (var c = d.fromSimpleName(a.classSimpleName).getOriginalName().split("."), e = b.AVM2.AS, f = 0, k = c.length - 1;f < k;f++) { - e[c[f]] || (e[c[f]] = {}), e = e[c[f]]; - } - t(e, a.classSimpleName, c[c.length - 1]); - g.registerNativeClass(a.nativeName, a.cls); - }); - g.registerNativeFunction("FlashUtilScript::getDefinitionByName", b.AVM2.AS.Natives.getDefinitionByName); - g.registerNativeFunction("FlashUtilScript::getTimer", function() { - return Date.now() - m.display.Loader.runtimeStartTime; - }); - g.registerNativeFunction("FlashUtilScript::escapeMultiByte", escape); - g.registerNativeFunction("FlashUtilScript::unescapeMultiByte", unescape); - g.registerNativeFunction("FlashNetScript::navigateToURL", function(a, c) { - null !== a && void 0 !== a || s("TypeError", k.Errors.NullPointerError, "request"); - b.AVM2.Runtime.AVM2.instance.systemDomain.getClass("flash.net.URLRequest").isInstanceOf(a) || s("TypeError", k.Errors.CheckTypeFailedError, a, "flash.net.URLRequest"); - var e = a.url; - if (/^fscommand:/i.test(e)) { - b.AVM2.Runtime.AVM2.instance.applicationDomain.getProperty(d.fromSimpleName("flash.system.fscommand"), !0, !0).call(null, e.substring(10), c); - } else { - var f = c || "_parent"; - window.open(b.FileLoadingService.instance.resolveUrl(e), f); + c = c.asGetPublicProperty("__proto__"); + return c ? (c = d(c, b, !1)) ? {target:c.link, name:c.name, obj:c.link.asGetPublicProperty(c.name)} : null : null; + } + function N(a, b, d) { + if (!b.executionProhibited) { + var c = Ya.get(), e = []; + e.length = 4; + var g = b.initialScope.create(d), f; + b.pushCallFrame(d, null, null); + c.message("ActionScript Execution Starts"); + c.indent(); + b.enterContext(function() { + try { + Aa(a, g, [], e); + } catch (b) { + f = w(b); } - }); - g.registerNativeFunction("FlashNetScript::sendToURL", function(a) { - null !== a && void 0 !== a || s("TypeError", k.Errors.NullPointerError, "request"); - b.AVM2.Runtime.AVM2.instance.systemDomain.getClass("flash.net.URLRequest").isInstanceOf(a) || s("TypeError", k.Errors.CheckTypeFailedError, a, "flash.net.URLRequest"); - var c = b.FileLoadingService.instance.createSession(); - c.onprogress = function() { - }; - c.open(a); - }); - g.registerNativeFunction("Toplevel::registerClassAlias", function(a, b) { - a || s("TypeError", k.Errors.NullPointerError, "aliasName"); - b || s("TypeError", k.Errors.NullPointerError, "classObject"); - k.aliasesCache.classes.set(b, a); - k.aliasesCache.names[a] = b; - }); - g.registerNativeFunction("Toplevel::getClassByAlias", function(a) { - a || s("TypeError", k.Errors.NullPointerError, "aliasName"); - var b = k.aliasesCache.names[a]; - b || s("ReferenceError", k.Errors.ClassNotFoundError, a); - return b; - }); - g.registerNativeFunction("isFinite", isFinite); - }; - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -console.timeEnd("Load Flash TS Dependencies"); -console.time("Load AVM1 Dependencies"); -(function(b) { - (function(b) { - var g = function() { - function b(f, g) { - this.array = f; - this.position = 0; - this.end = f.length; - this.readANSI = 6 > g; - var k = new ArrayBuffer(4); - (new Int32Array(k))[0] = 1; - if (!(new Uint8Array(k))[0]) { - throw Error("big-endian platform"); + }, d); + f instanceof ua && (b.executionProhibited = !0, console.error("Disabling AVM1 execution")); + b.popCallFrame(); + c.unindent(); + c.message("ActionScript Execution Stops"); + if (f) { + throw f; + } + } + } + function K(b, d, c) { + var e = b.split(/[\/.]/g); + "" === e[e.length - 1] && e.pop(); + if ("" === e[0] || "_level0" === e[0] || "_root" === e[0]) { + d = c, e.shift(); + } + for (;0 < e.length;) { + c = d; + d = d.__lookupChild(e[0]); + if (!d) { + return a(e[0] + " (expr " + b + ") is not found in " + c._target), {}; + } + e.shift(); + } + return d; + } + function y(a, b) { + if (a === Array) { + var d = b; + 1 == b.length && "number" === typeof b[0] && (d = [], d.length = b[0]); + return d; + } + if (a === Boolean || a === Number || a === String || a === Function) { + return a.apply(null, b); + } + if (a === Date) { + switch(b.length) { + case 0: + return new Date; + case 1: + return new Date(b[0]); + default: + return new Date(b[0], b[1], 2 < b.length ? b[2] : 1, 3 < b.length ? b[3] : 0, 4 < b.length ? b[4] : 0, 5 < b.length ? b[5] : 0, 6 < b.length ? b[6] : 0); } } - b.prototype.readUI8 = function() { - return this.array[this.position++]; - }; - b.prototype.readUI16 = function() { - var b = this.position, f = this.array, f = f[b + 1] << 8 | f[b]; - this.position = b + 2; - return f; - }; - b.prototype.readSI16 = function() { - var b = this.position, f = this.array, f = f[b + 1] << 8 | f[b]; - this.position = b + 2; - return 32768 > f ? f : f - 65536; - }; - b.prototype.readInteger = function() { - var b = this.position, f = this.array, f = f[b] | f[b + 1] << 8 | f[b + 2] << 16 | f[b + 3] << 24; - this.position = b + 4; - return f; - }; - b.prototype.readFloat = function() { - var b = this.position, f = this.array, g = new ArrayBuffer(4), d = new Uint8Array(g); - d[0] = f[b]; - d[1] = f[b + 1]; - d[2] = f[b + 2]; - d[3] = f[b + 3]; - this.position = b + 4; - return(new Float32Array(g))[0]; - }; - b.prototype.readDouble = function() { - var b = this.position, f = this.array, g = new ArrayBuffer(8), d = new Uint8Array(g); - d[4] = f[b]; - d[5] = f[b + 1]; - d[6] = f[b + 2]; - d[7] = f[b + 3]; - d[0] = f[b + 4]; - d[1] = f[b + 5]; - d[2] = f[b + 6]; - d[3] = f[b + 7]; - this.position = b + 8; - return(new Float64Array(g))[0]; - }; - b.prototype.readBoolean = function() { - return!!this.readUI8(); - }; - b.prototype.readANSIString = function() { - for (var b = "", f;f = this.readUI8();) { - b += String.fromCharCode(f); - } - return b; - }; - b.prototype.readUTF8String = function() { - for (var b = "", f;f = this.readUI8();) { - if (128 > f) { - b += String.fromCharCode(f); - } else { - if (128 === (f & 192)) { - throw Error("Invalid UTF8 encoding"); - } - var g = 192, d = 5; - do { - var a = g >> 1 | 128; - if ((f & a) === g) { - break; - } - g = a; - --d; - } while (0 <= d); - g = f & (1 << d) - 1; - for (a = 5;a >= d;--a) { - f = this.readUI8(); - if (128 !== (f & 192)) { - throw Error("Invalid UTF8 encoding"); - } - g = g << 6 | f & 63; - } - b = 65536 <= g ? b + String.fromCharCode(g - 65536 >> 10 & 1023 | 55296, g & 1023 | 56320) : b + String.fromCharCode(g); - } + if (a === Object) { + return new Za; + } + } + function D(b, d) { + if (isNaN(b) || 0 > b) { + return a("Invalid amount of arguments: " + b), 0; + } + b |= 0; + return b > d ? (a("Truncating amount of arguments: from " + b + " to " + d), d) : b; + } + function L(a) { + for (var b = +a.pop(), b = D(b, a.length), d = [], c = 0;c < b;c++) { + d.push(a.pop()); + } + return d; + } + function H(a, b) { + var d = a.context; + if (b) { + try { + var c = K(b, d.currentTarget || d.defaultTarget, d.resolveLevel(0)); + d.currentTarget = c; + } catch (e) { + throw d.currentTarget = null, e; } - return b; - }; - b.prototype.readString = function() { - return this.readANSI ? this.readANSIString() : this.readUTF8String(); - }; - b.prototype.readBytes = function(b) { - var f = this.position, g = Math.max(this.end - f, 0); - g < b && (b = g); - g = this.array.subarray(f, f + b); - this.position = f + b; - return g; - }; - return b; - }(); - b.ActionsDataStream = g; - })(b.AVM1 || (b.AVM1 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - b[b.None = 0] = "None"; - b[b.ActionGotoFrame = 129] = "ActionGotoFrame"; - b[b.ActionGetURL = 131] = "ActionGetURL"; - b[b.ActionNextFrame = 4] = "ActionNextFrame"; - b[b.ActionPreviousFrame = 5] = "ActionPreviousFrame"; - b[b.ActionPlay = 6] = "ActionPlay"; - b[b.ActionStop = 7] = "ActionStop"; - b[b.ActionToggleQuality = 8] = "ActionToggleQuality"; - b[b.ActionStopSounds = 9] = "ActionStopSounds"; - b[b.ActionWaitForFrame = 138] = "ActionWaitForFrame"; - b[b.ActionSetTarget = 139] = "ActionSetTarget"; - b[b.ActionGoToLabel = 140] = "ActionGoToLabel"; - b[b.ActionPush = 150] = "ActionPush"; - b[b.ActionPop = 23] = "ActionPop"; - b[b.ActionAdd = 10] = "ActionAdd"; - b[b.ActionSubtract = 11] = "ActionSubtract"; - b[b.ActionMultiply = 12] = "ActionMultiply"; - b[b.ActionDivide = 13] = "ActionDivide"; - b[b.ActionEquals = 14] = "ActionEquals"; - b[b.ActionLess = 15] = "ActionLess"; - b[b.ActionAnd = 16] = "ActionAnd"; - b[b.ActionOr = 17] = "ActionOr"; - b[b.ActionNot = 18] = "ActionNot"; - b[b.ActionStringEquals = 19] = "ActionStringEquals"; - b[b.ActionStringLength = 20] = "ActionStringLength"; - b[b.ActionMBStringLength = 49] = "ActionMBStringLength"; - b[b.ActionStringAdd = 33] = "ActionStringAdd"; - b[b.ActionStringExtract = 21] = "ActionStringExtract"; - b[b.ActionMBStringExtract = 53] = "ActionMBStringExtract"; - b[b.ActionStringLess = 41] = "ActionStringLess"; - b[b.ActionToInteger = 24] = "ActionToInteger"; - b[b.ActionCharToAscii = 50] = "ActionCharToAscii"; - b[b.ActionMBCharToAscii = 54] = "ActionMBCharToAscii"; - b[b.ActionAsciiToChar = 51] = "ActionAsciiToChar"; - b[b.ActionMBAsciiToChar = 55] = "ActionMBAsciiToChar"; - b[b.ActionJump = 153] = "ActionJump"; - b[b.ActionIf = 157] = "ActionIf"; - b[b.ActionCall = 158] = "ActionCall"; - b[b.ActionGetVariable = 28] = "ActionGetVariable"; - b[b.ActionSetVariable = 29] = "ActionSetVariable"; - b[b.ActionGetURL2 = 154] = "ActionGetURL2"; - b[b.ActionGotoFrame2 = 159] = "ActionGotoFrame2"; - b[b.ActionSetTarget2 = 32] = "ActionSetTarget2"; - b[b.ActionGetProperty = 34] = "ActionGetProperty"; - b[b.ActionSetProperty = 35] = "ActionSetProperty"; - b[b.ActionCloneSprite = 36] = "ActionCloneSprite"; - b[b.ActionRemoveSprite = 37] = "ActionRemoveSprite"; - b[b.ActionStartDrag = 39] = "ActionStartDrag"; - b[b.ActionEndDrag = 40] = "ActionEndDrag"; - b[b.ActionWaitForFrame2 = 141] = "ActionWaitForFrame2"; - b[b.ActionTrace = 38] = "ActionTrace"; - b[b.ActionGetTime = 52] = "ActionGetTime"; - b[b.ActionRandomNumber = 48] = "ActionRandomNumber"; - b[b.ActionCallFunction = 61] = "ActionCallFunction"; - b[b.ActionCallMethod = 82] = "ActionCallMethod"; - b[b.ActionConstantPool = 136] = "ActionConstantPool"; - b[b.ActionDefineFunction = 155] = "ActionDefineFunction"; - b[b.ActionDefineLocal = 60] = "ActionDefineLocal"; - b[b.ActionDefineLocal2 = 65] = "ActionDefineLocal2"; - b[b.ActionDelete = 58] = "ActionDelete"; - b[b.ActionDelete2 = 59] = "ActionDelete2"; - b[b.ActionEnumerate = 70] = "ActionEnumerate"; - b[b.ActionEquals2 = 73] = "ActionEquals2"; - b[b.ActionGetMember = 78] = "ActionGetMember"; - b[b.ActionInitArray = 66] = "ActionInitArray"; - b[b.ActionInitObject = 67] = "ActionInitObject"; - b[b.ActionNewMethod = 83] = "ActionNewMethod"; - b[b.ActionNewObject = 64] = "ActionNewObject"; - b[b.ActionSetMember = 79] = "ActionSetMember"; - b[b.ActionTargetPath = 69] = "ActionTargetPath"; - b[b.ActionWith = 148] = "ActionWith"; - b[b.ActionToNumber = 74] = "ActionToNumber"; - b[b.ActionToString = 75] = "ActionToString"; - b[b.ActionTypeOf = 68] = "ActionTypeOf"; - b[b.ActionAdd2 = 71] = "ActionAdd2"; - b[b.ActionLess2 = 72] = "ActionLess2"; - b[b.ActionModulo = 63] = "ActionModulo"; - b[b.ActionBitAnd = 96] = "ActionBitAnd"; - b[b.ActionBitLShift = 99] = "ActionBitLShift"; - b[b.ActionBitOr = 97] = "ActionBitOr"; - b[b.ActionBitRShift = 100] = "ActionBitRShift"; - b[b.ActionBitURShift = 101] = "ActionBitURShift"; - b[b.ActionBitXor = 98] = "ActionBitXor"; - b[b.ActionDecrement = 81] = "ActionDecrement"; - b[b.ActionIncrement = 80] = "ActionIncrement"; - b[b.ActionPushDuplicate = 76] = "ActionPushDuplicate"; - b[b.ActionReturn = 62] = "ActionReturn"; - b[b.ActionStackSwap = 77] = "ActionStackSwap"; - b[b.ActionStoreRegister = 135] = "ActionStoreRegister"; - b[b.ActionInstanceOf = 84] = "ActionInstanceOf"; - b[b.ActionEnumerate2 = 85] = "ActionEnumerate2"; - b[b.ActionStrictEquals = 102] = "ActionStrictEquals"; - b[b.ActionGreater = 103] = "ActionGreater"; - b[b.ActionStringGreater = 104] = "ActionStringGreater"; - b[b.ActionDefineFunction2 = 142] = "ActionDefineFunction2"; - b[b.ActionExtends = 105] = "ActionExtends"; - b[b.ActionCastOp = 43] = "ActionCastOp"; - b[b.ActionImplementsOp = 44] = "ActionImplementsOp"; - b[b.ActionTry = 143] = "ActionTry"; - b[b.ActionThrow = 42] = "ActionThrow"; - b[b.ActionFSCommand2 = 45] = "ActionFSCommand2"; - b[b.ActionStrictMode = 137] = "ActionStrictMode"; - })(b.ActionCode || (b.ActionCode = {})); - var g = function() { - return function(b) { - this.registerNumber = b; - }; - }(); - b.ParsedPushRegisterAction = g; - var f = function() { - return function(b) { - this.constantIndex = b; - }; - }(); - b.ParsedPushConstantAction = f; - (function(b) { - b[b.None = 0] = "None"; - b[b.Argument = 1] = "Argument"; - b[b.This = 2] = "This"; - b[b.Arguments = 4] = "Arguments"; - b[b.Super = 8] = "Super"; - b[b.Global = 16] = "Global"; - b[b.Parent = 32] = "Parent"; - b[b.Root = 64] = "Root"; - })(b.ArgumentAssignmentType || (b.ArgumentAssignmentType = {})); - var t = function() { - function m(b) { - this.stream = b; + } else { + d.currentTarget = null; } - Object.defineProperty(m.prototype, "position", {get:function() { - return this.stream.position; - }, set:function(b) { - this.stream.position = b; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(m.prototype, "eof", {get:function() { - return this.stream.position >= this.stream.end; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(m.prototype, "length", {get:function() { - return this.stream.end; - }, enumerable:!0, configurable:!0}); - m.prototype.readNext = function() { - var d = this.stream, a = d.position, c = d.readUI8(), m = 128 <= c ? d.readUI16() : 0, m = d.position + m, p = null; - switch(c | 0) { - case 129: - var e = d.readUI16(), p = d.readUI8(), q = !1; - 6 !== p && 7 !== p ? d.position-- : (m++, q = 6 === p); - p = [e, q]; - break; - case 131: - e = d.readString(); - p = d.readString(); - p = [e, p]; - break; - case 138: - var e = d.readUI16(), l = d.readUI8(), p = [e, l]; - break; - case 139: - p = [d.readString()]; - break; - case 140: - p = [d.readString()]; - break; - case 150: - for (p = [];d.position < m;) { - e = d.readUI8(); - switch(e | 0) { - case 0: - e = d.readString(); - break; + } + function J(a) { + return a === this; + } + function C(a, b, d, c, e, g, f) { + function k() { + if (!l.executionProhibited) { + var a, d = new bc, e = J(this) ? n : this, t, v, A = l.pushCallFrame(e, k, arguments); + f & 4 || (t = $a(arguments, 0), d.asSetPublicProperty("arguments", t)); + f & 2 || d.asSetPublicProperty("this", e); + f & 8 || (v = new Ba(A), d.asSetPublicProperty("super", v)); + a = r.create(d); + var oa, B = h(); + for (oa = 0;oa < u;oa++) { + var x = g[oa]; + if (x) { + switch(x.type) { case 1: - e = d.readFloat(); + B[oa] = arguments[x.index]; break; case 2: - e = null; - break; - case 3: - e = void 0; + B[oa] = e; break; case 4: - e = new g(d.readUI8()); - break; - case 5: - e = d.readBoolean(); - break; - case 6: - e = d.readDouble(); - break; - case 7: - e = d.readInteger(); + t = t || $a(arguments, 0); + B[oa] = t; break; case 8: - e = new f(d.readUI8()); + v = v || new Ba(A); + B[oa] = v; break; - case 9: - e = new f(d.readUI16()); + case 16: + B[oa] = m; break; - default: - console.error("Unknown value type: " + e); - d.position = m; - continue; + case 32: + B[oa] = n.asGetPublicProperty("_parent"); + break; + case 64: + B[oa] = l.resolveLevel(0); } - p.push(e); } - break; - case 153: - e = d.readSI16(); - p = [e]; - break; - case 157: - e = d.readSI16(); - p = [e]; - break; - case 154: - e = d.readUI8(); - p = [e]; - break; - case 159: - e = d.readUI8(); - p = [e]; - e & 2 && p.push(d.readUI16()); - break; - case 141: - l = d.readUI8(); - p = [l]; - break; - case 136: - for (var l = d.readUI16(), e = [], u = 0;u < l;u++) { - e.push(d.readString()); + } + for (oa = 0;oa < arguments.length || oa < c.length;oa++) { + s && s[oa] || d.asSetPublicProperty(c[oa], arguments[oa]); + } + var N, y; + w.indent(); + if (256 <= ++l.stackDepth) { + throw new ua("long running script -- AVM1 recursion limit is reached"); + } + l.enterContext(function() { + try { + N = Aa(b, a, q, B); + } catch (d) { + y = d; } - p = [e]; - break; - case 155: - p = d.readString(); - l = d.readUI16(); - q = []; - for (u = 0;u < l;u++) { - q.push(d.readString()); - } - e = d.readUI16(); - m += e; - e = new b.AVM1ActionsData(d.readBytes(e), this.dataId + "_f" + d.position); - p = [e, p, q]; - break; - case 148: - e = d.readUI16(); - m += e; - p = [new b.AVM1ActionsData(d.readBytes(e), this.dataId + "_w" + d.position)]; - break; - case 135: - var w = d.readUI8(), p = [w]; - break; - case 142: - for (var p = d.readString(), l = d.readUI16(), r = d.readUI8(), e = d.readUI16(), h = [], q = [], u = 0;u < l;u++) { - var w = d.readUI8(), x = d.readString(); - q.push(x); - w && (h[w] = {type:1, name:x, index:u}); - } - l = 1; - e & 1 && (h[l++] = {type:2}); - e & 4 && (h[l++] = {type:4}); - e & 16 && (h[l++] = {type:8}); - e & 64 && (h[l++] = {type:64}); - e & 128 && (h[l++] = {type:32}); - e & 256 && (h[l++] = {type:16}); - l = 0; - e & 2 && (l |= 2); - e & 8 && (l |= 4); - e & 32 && (l |= 8); - e = d.readUI16(); - m += e; - e = new b.AVM1ActionsData(d.readBytes(e), this.dataId + "_f" + d.position); - p = [e, p, q, r, h, l]; - break; - case 143: - e = d.readUI8(); - p = !!(e & 4); - q = !!(e & 2); - e = !!(e & 1); - l = d.readUI16(); - u = d.readUI16(); - h = d.readUI16(); - r = p ? d.readUI8() : d.readString(); - m += l + u + h; - l = new b.AVM1ActionsData(d.readBytes(l), this.dataId + "_t" + d.position); - u = new b.AVM1ActionsData(d.readBytes(u), this.dataId + "_c" + d.position); - h = new b.AVM1ActionsData(d.readBytes(h), this.dataId + "_z" + d.position); - p = [p, r, l, e, u, q, h]; - break; - case 137: - p = [d.readUI8()]; + }, p); + l.stackDepth--; + l.popCallFrame(); + w.unindent(); + 10 < z.length || z.push(B); + if (y) { + throw y; + } + return N; } - d.position = m; - return{position:a, actionCode:c, actionName:s[c], args:p}; - }; - m.prototype.skip = function(b) { - for (var a = this.stream;0 < b && a.position < a.end;) { - var c = 128 <= a.readUI8() ? a.readUI16() : 0; - a.position += c; - b--; + } + function h() { + if (0 < z.length) { + return z.pop(); } - }; - return m; - }(); - b.ActionsDataParser = t; - var s = {0:"EOA", 4:"ActionNextFrame", 5:"ActionPreviousFrame", 6:"ActionPlay", 7:"ActionStop", 8:"ActionToggleQuality", 9:"ActionStopSounds", 10:"ActionAdd", 11:"ActionSubtract", 12:"ActionMultiply", 13:"ActionDivide", 14:"ActionEquals", 15:"ActionLess", 16:"ActionAnd", 17:"ActionOr", 18:"ActionNot", 19:"ActionStringEquals", 20:"ActionStringLength", 21:"ActionStringExtract", 23:"ActionPop", 24:"ActionToInteger", 28:"ActionGetVariable", 29:"ActionSetVariable", 32:"ActionSetTarget2", 33:"ActionStringAdd", - 34:"ActionGetProperty", 35:"ActionSetProperty", 36:"ActionCloneSprite", 37:"ActionRemoveSprite", 38:"ActionTrace", 39:"ActionStartDrag", 40:"ActionEndDrag", 41:"ActionStringLess", 42:"ActionThrow", 43:"ActionCastOp", 44:"ActionImplementsOp", 45:"ActionFSCommand2", 48:"ActionRandomNumber", 49:"ActionMBStringLength", 50:"ActionCharToAscii", 51:"ActionAsciiToChar", 52:"ActionGetTime", 53:"ActionMBStringExtract", 54:"ActionMBCharToAscii", 55:"ActionMBAsciiToChar", 58:"ActionDelete", 59:"ActionDelete2", - 60:"ActionDefineLocal", 61:"ActionCallFunction", 62:"ActionReturn", 63:"ActionModulo", 64:"ActionNewObject", 65:"ActionDefineLocal2", 66:"ActionInitArray", 67:"ActionInitObject", 68:"ActionTypeOf", 69:"ActionTargetPath", 70:"ActionEnumerate", 71:"ActionAdd2", 72:"ActionLess2", 73:"ActionEquals2", 74:"ActionToNumber", 75:"ActionToString", 76:"ActionPushDuplicate", 77:"ActionStackSwap", 78:"ActionGetMember", 79:"ActionSetMember", 80:"ActionIncrement", 81:"ActionDecrement", 82:"ActionCallMethod", - 83:"ActionNewMethod", 84:"ActionInstanceOf", 85:"ActionEnumerate2", 96:"ActionBitAnd", 97:"ActionBitOr", 98:"ActionBitXor", 99:"ActionBitLShift", 100:"ActionBitRShift", 101:"ActionBitURShift", 102:"ActionStrictEquals", 103:"ActionGreater", 104:"ActionStringGreater", 105:"ActionExtends", 129:"ActionGotoFrame", 131:"ActionGetURL", 135:"ActionStoreRegister", 136:"ActionConstantPool", 137:"ActionStrictMode", 138:"ActionWaitForFrame", 139:"ActionSetTarget", 140:"ActionGoToLabel", 141:"ActionWaitForFrame2", - 142:"ActionDefineFunction2", 143:"ActionTry", 148:"ActionWith", 150:"ActionPush", 153:"ActionJump", 154:"ActionGetURL2", 155:"ActionDefineFunction", 157:"ActionIf", 158:"ActionCall", 159:"ActionGotoFrame2"}; - })(b.AVM1 || (b.AVM1 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - var g = function() { - function b() { + var a = []; + a.length = v; + return a; + } + var l = a.context, m = a.global, r = a.scopeContainer, n = a.scope, w = a.actionTracer, p = l.defaultTarget, q = a.constantPool, s = null, u = g ? g.length : 0; + for (a = 0;a < u;a++) { + var t = g[a]; + t && 1 === t.type && (s || (s = []), s[g[a].index] = !0); + } + var v = Math.min(e, 255), v = Math.max(v, u + 1), z = []; + e = k; + e.instanceConstructor = k; + e.debugName = "avm1 " + (d || ""); + d && (e.name = d); + return k; + } + function E(a) { + return a && (0 <= a.indexOf(".") || 0 <= a.indexOf(":")); + } + function F(b, e, g) { + var f = b.global; + b = b.context; + var k, h; + if (0 <= e.indexOf(":")) { + k = b.currentTarget || b.defaultTarget; + h = e.split(":"); + k = K(h[0], k, b.resolveLevel(0)); + if (!k) { + return a(h[0] + " is undefined"), null; + } + h = h[1]; + } else { + if (0 <= e.indexOf(".")) { + for (e = e.split("."), h = e.pop(), k = f, f = 0;f < e.length;f++) { + if (k = (b = d(k, e[f], !1)) && b.link.asGetPublicProperty(b.name), !k) { + return a(e.slice(0, f + 1) + " is undefined"), null; + } + } + } else { + c.Debug.assert(!1, "AVM1 variable has no path"); + } + } + return(b = d(k, h, g)) ? (ka.obj = k, ka.link = b.link, ka.name = b.name, ka) : null; + } + function I(a, b) { + if (E(b)) { + return F(a, b, !1); + } + var c = a.scopeContainer, e = a.context, e = e.currentTarget || e.defaultTarget, g = a.scope, f; + if (f = d(g, b, !1)) { + return ka.obj = g, ka.link = f.link, ka.name = f.name, ka; + } + for (;c;c = c.next) { + if (f = d(c.scope, b, !1)) { + return ka.obj = c.scope, ka.link = f.link, ka.name = f.name, ka; + } + } + return(f = d(e, b, !1)) ? (ka.obj = e, ka.link = f.link, ka.name = f.name, ka) : "this" === b ? (g.asDefinePublicProperty("this", {value:e, configurable:!0}), ka.obj = g, ka.link = g, ka.name = "this", ka) : null; + } + function G(a, b) { + if (E(b)) { + return F(a, b, !0); + } + var c = a.scopeContainer, e = a.context, g = e.currentTarget || e.defaultTarget, f = a.scope; + if (e.currentTarget) { + return ka.obj = g, ka.link = g, ka.name = b, ka; + } + if (e = d(f, b, !1)) { + return ka.obj = f, ka.link = e.link, ka.name = e.name, ka; + } + for (;c.next;c = c.next) { + if (e = d(c.scope, b, !1)) { + return ka.obj = c.scope, ka.link = e.link, ka.name = e.name, ka; + } + } + ka.obj = g; + ka.link = g; + ka.name = b; + return ka; + } + function Z(a, b) { + var d = a.global, c = b[0]; + b[1] ? d.gotoAndPlay(c + 1) : d.gotoAndStop(c + 1); + } + function Q(a, b) { + a.global.getURL(b[0], b[1]); + } + function S(a) { + a.global.nextFrame(); + } + function O(a) { + a.global.prevFrame(); + } + function P(a) { + a.global.play(); + } + function V(a) { + a.global.stop(); + } + function $(a) { + a.global.toggleHighQuality(); + } + function W(a) { + a.global.stopAllSounds(); + } + function x(a, b) { + return!a.global.ifFrameLoaded(b[0]); + } + function ea(a, b) { + H(a, b[0]); + } + function Y(a, b) { + var d = a.global, c = b[0]; + b[1] ? d.gotoAndPlay(c) : d.gotoAndStop(c); + } + function R(a, b) { + var d = a.registers, c = a.constantPool, e = a.stack; + b.forEach(function(a) { + a instanceof h.ParsedPushConstantAction ? e.push(c[a.constantIndex]) : a instanceof h.ParsedPushRegisterAction ? (a = a.registerNumber, 0 > a || a >= d.length ? e.push(void 0) : e.push(d[a])) : e.push(a); + }); + } + function U(a) { + a.stack.pop(); + } + function ba(a) { + a = a.stack; + var b = e(a.pop()), d = e(a.pop()); + a.push(b + d); + } + function X(a) { + a = a.stack; + var b = e(a.pop()), d = e(a.pop()); + a.push(d - b); + } + function ga(a) { + a = a.stack; + var b = e(a.pop()), d = e(a.pop()); + a.push(b * d); + } + function ja(a) { + var b = a.stack; + a = a.isSwfVersion5; + var d = e(b.pop()), d = e(b.pop()) / d; + b.push(a ? d : isFinite(d) ? d : "#ERROR#"); + } + function aa(a) { + var b = a.stack; + a = a.isSwfVersion5; + var d = e(b.pop()), c = e(b.pop()), d = d == c; + b.push(a ? d : d ? 1 : 0); + } + function ia(a) { + var b = a.stack; + a = a.isSwfVersion5; + var d = e(b.pop()), d = e(b.pop()) < d; + b.push(a ? d : d ? 1 : 0); + } + function T(a) { + var b = a.stack; + a = a.isSwfVersion5; + var d = l(b.pop()), c = l(b.pop()), d = d && c; + b.push(a ? d : d ? 1 : 0); + } + function da(a) { + var b = a.stack; + a = a.isSwfVersion5; + var d = l(b.pop()), c = l(b.pop()), d = d || c; + b.push(a ? d : d ? 1 : 0); + } + function fa(a) { + var b = a.stack; + a = a.isSwfVersion5; + var d = !l(b.pop()); + b.push(a ? d : d ? 1 : 0); + } + function la(a) { + var b = a.stack; + a = a.isSwfVersion5; + var d = t(b.pop()), c = t(b.pop()), d = d == c; + b.push(a ? d : d ? 1 : 0); + } + function ca(a) { + var b = a.stack; + a = a.global; + var d = t(b.pop()); + b.push(a.length_(d)); + } + function na(a) { + var b = a.stack; + a = a.global; + var d = t(b.pop()); + b.push(a.length_(d)); + } + function ma(a) { + a = a.stack; + var b = t(a.pop()), d = t(a.pop()); + a.push(d + b); + } + function ra(a) { + var b = a.stack; + a = a.global; + var d = b.pop(), c = b.pop(), e = t(b.pop()); + b.push(a.substring(e, c, d)); + } + function wa(a) { + var b = a.stack; + a = a.global; + var d = b.pop(), c = b.pop(), e = t(b.pop()); + b.push(a.mbsubstring(e, c, d)); + } + function qa(a) { + var b = a.stack; + a = a.isSwfVersion5; + var d = t(b.pop()), d = t(b.pop()) < d; + b.push(a ? d : d ? 1 : 0); + } + function ya(a) { + var b = a.stack; + b.push(a.global.int(b.pop())); + } + function pa(a) { + var b = a.stack; + a = a.global; + var d = b.pop(); + b.push(a.ord(d)); + } + function za(a) { + var b = a.stack; + a = a.global; + var d = b.pop(); + b.push(a.mbord(d)); + } + function ha(a) { + var b = a.stack; + a = a.global; + var d = +b.pop(); + b.push(a.chr(d)); + } + function Ha(a) { + var b = a.stack; + a = a.global; + var d = +b.pop(); + b.push(a.mbchr(d)); + } + function Ma(a, b) { + } + function va(a, b) { + return!!a.stack.pop(); + } + function xa(a) { + var b = a.global; + a = a.stack.pop(); + b.call(a); + } + function Ia(a) { + var b = a.stack, d = "" + b.pop(), c = b.length; + b.push(void 0); + a = I(a, d); + b[c] = a ? a.link.asGetPublicProperty(a.name) : void 0; + } + function Ja(a) { + var b = a.stack, d = b.pop(), b = "" + b.pop(); + if (a = G(a, b)) { + a.link.asSetPublicProperty(a.name, d), r(a.name); } - b.prototype.analyze = function(b) { - for (var f = [], g = [0], d = [!0], a = [0];0 < a.length;) { - var c = a.shift(); - if (!f[c]) { - for (b.position = c;!b.eof && !f[c];) { - var k = b.readNext(); - if (0 === k.actionCode) { - break; - } - var p = b.position, e = {action:k, next:p, conditionalJumpTo:-1}, q = 0, l = !1, u = !1; - switch(k.actionCode) { - case 138: - ; - case 141: - l = !0; - b.skip(138 === k.actionCode ? k.args[1] : k.args[0]); - q = b.position; - b.position = p; - break; - case 153: - l = u = !0; - q = p + k.args[0]; - break; - case 157: - l = !0; - q = p + k.args[0]; - break; - case 42: - ; - case 62: - ; - case 0: - l = u = !0, q = b.length; - } - if (l) { - if (0 > q || q > b.length) { - console.error("jump outside the action block;"), q = b.length; - } - u ? e.next = q : e.conditionalJumpTo = q; - d[q] || (g.push(q), a.push(q), d[q] = !0); - } - f[c] = e; - if (u) { - break; - } - c = p; - } + } + function Ca(a, b) { + var d = a.global, c = a.stack, e = b[0], g = c.pop(), c = c.pop(), f; + e & 1 ? f = "GET" : e & 2 && (f = "POST"); + var k = e & 64; + e & 128 ? d.loadVariables(c, g, f) : k ? d.loadMovie(c, g, f) : d.getURL(c, g, f); + } + function ta(a, b) { + var d = a.global, c = b[0], e = [a.stack.pop()]; + c & 2 && e.push(b[1]); + (c & 1 ? d.gotoAndPlay : d.gotoAndStop).apply(d, e); + } + function Da(a) { + var b = a.stack.pop(); + H(a, b); + } + function Oa(a) { + var b = a.global; + a = a.stack; + var d = a.pop(), c = a.pop(), e = a.length; + a.push(void 0); + a[e] = b.getAVM1Property(c, d); + } + function Pa(a) { + var b = a.global, d = a.stack; + a = d.pop(); + var c = d.pop(), d = d.pop(); + b.setAVM1Property(d, c, a); + } + function Qa(a) { + var b = a.global, d = a.stack; + a = d.pop(); + var c = d.pop(), d = d.pop(); + b.duplicateMovieClip(d, c, a); + } + function Ra(a) { + var b = a.global; + a = a.stack.pop(); + b.removeMovieClip(a); + } + function Sa(a) { + var b = a.global, d = a.stack; + a = d.pop(); + var c = d.pop(), d = d.pop() ? {y2:d.pop(), x2:d.pop(), y1:d.pop(), x1:d.pop()} : null; + a = [a, c]; + d && (a = a.concat(d.x1, d.y1, d.x2, d.y2)); + b.startDrag.apply(b, a); + } + function Ta(a) { + a.global.stopDrag(); + } + function Ua(a, b) { + var d = a.global, c = a.stack.pop(); + return!d.ifFrameLoaded(c); + } + function Va(a) { + var b = a.global; + a = a.stack.pop(); + b.trace(void 0 === a ? "undefined" : t(a)); + } + function Wa(a) { + a.stack.push(a.global.getTimer()); + } + function Xa(a) { + var b = a.stack; + b.push(a.global.random(b.pop())); + } + function ab(b) { + var d = b.stack, c = d.pop(), e = L(d), g = d.length; + d.push(void 0); + var f = (b = I(b, c)) ? b.link.asGetPublicProperty(b.name) : void 0; + f instanceof Function ? (Ea(d.length === g + 1), d[g] = f.apply(b.obj || null, e)) : a("AVM1 warning: function '" + c + (f ? "' is not callable" : "' is undefined")); + } + function bb(d) { + var e = d.stack, g = e.pop(), f = e.pop(), k = L(e), h, l = e.length; + e.push(void 0); + if (c.isNullOrUndefined(f)) { + a("AVM1 warning: method '" + g + "' can't be called on undefined object"); + } else { + d = d.context.frame; + var m, r; + if (c.isNullOrUndefined(g) || "" === g) { + if (f instanceof Ba) { + var n = f.callFrame, w = M(n, "__constructor__"); + w && (m = w.target, r = w.obj, h = n.currentThis); + } else { + h = r = f; } - } - var w = []; - g.forEach(function(a) { - if (f[a]) { - var b = [], c = a; - do { - c = f[c], b.push(c), c = c.next; - } while (!d[c] && f[c]); - w.push({label:a, items:b, jump:c}); + Na(r) ? (d.setCallee(h, m, r, k), e[l] = r.apply(h, k), d.resetCallee()) : a("AVM1 warning: obj '" + f + (f ? "' is not callable" : "' is undefined")); + Ea(e.length === l + 1); + } else { + if (f instanceof Ba) { + if (n = f.callFrame, w = M(n, g)) { + m = w.target, r = w.obj, h = n.currentThis; + } + } else { + r = b(f, g), h = f; } - }); - return{actions:f, blocks:w, dataId:b.dataId}; - }; - return b; - }(); - b.ActionsDataAnalyzer = g; - })(b.AVM1 || (b.AVM1 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - var g = b.Debug.assert; - k.AVM1ActionsData = function() { - return function(b, f) { - this.bytes = b; - this.id = f; - g(b instanceof Uint8Array); - }; - }(); - var f = function() { - function b() { - this.root = null; - this.swfVersion = 0; - this.globals = null; - } - b.prototype.flushPendingScripts = function() { - }; - b.prototype.addAsset = function(b, f, d) { - }; - b.prototype.registerClass = function(b, f) { - }; - b.prototype.getAsset = function(b) { - }; - b.prototype.resolveTarget = function(b) { - }; - b.prototype.resolveLevel = function(b) { - }; - b.prototype.addToPendingScripts = function(b) { - }; - b.prototype.executeActions = function(b, f) { - }; - b.instance = null; - return b; - }(); - k.AVM1Context = f; - })(b.AVM1 || (b.AVM1 = {})); -})(Shumway || (Shumway = {})); -__extends = this.__extends || function(b, k) { - function g() { - this.constructor = b; - } - for (var f in k) { - k.hasOwnProperty(f) && (b[f] = k[f]); - } - g.prototype = k.prototype; - b.prototype = new g; -}; -(function(b) { - (function(k) { - function g(a) { - for (var b = 0;b < arguments.length - 1;b++) { - } - if (k.avm1ErrorsEnabled.value) { - try { - throw Error(a); - } catch (c) { + Na(r) ? (Ea(e.length === l + 1), d.setCallee(h, m, r, k), e[l] = r.apply(h, k), d.resetCallee()) : a("AVM1 warning: method '" + g + "' on object", f, c.isNullOrUndefined(r) ? "is undefined" : "is not callable"); } } - warn.apply(null, arguments); } - function f(a) { - return "object" === typeof a && a && a instanceof b.AVM2.AS.avm1lib.AVM1MovieClip; + function cb(a, b) { + a.constantPool = b[0]; } - function t(a) { - if (null === a) { - return "null"; - } - var b = typeof a; - return "function" === b ? "object" : "object" === b && f(a) ? "movieclip" : b; + function db(a, b) { + var d = a.stack, c = a.scope, e = b[1], g = C(a, b[0], e, b[2], 4, null, 0); + e ? c.asSetPublicProperty(e, g) : d.push(g); } - function s(a) { - return "object" !== t(a) ? a : a.valueOf(); + function eb(a) { + var b = a.stack; + a = a.scope; + var d = b.pop(), b = b.pop(); + a.asSetPublicProperty(b, d); } - function m() { - return k.AVM1Context.instance.swfVersion; + function fb(a) { + var b = a.scope; + a = a.stack.pop(); + b.asSetPublicProperty(a, void 0); } - function d(a) { - return "object" !== t(a) ? a : a instanceof Date && 6 <= m() ? a.toString() : a.valueOf(); + function gb(a) { + a = a.stack; + var b = a.pop(), d = a.pop(); + a.push(d.asDeleteProperty(void 0, b, 0)); + r(b); } - function a(a) { - switch(t(a)) { - default: - ; - case "undefined": - ; - case "null": - return!1; - case "boolean": - return a; - case "number": - return 0 !== a && !isNaN(a); - case "string": - return 0 !== a.length; - case "movieclip": - ; - case "object": - return!0; + function hb(a) { + var b = a.stack, c = b.pop(); + a: { + for (a = a.scopeContainer;a;a = a.next) { + var e = d(a.scope, c, !1); + if (e) { + e.link.asSetPublicProperty(e.name, void 0); + a = e.link.asDeleteProperty(void 0, e.name, 0); + break a; + } + } + a = !1; } + b.push(a); + r(c); } - function c(a) { - a = s(a); - switch(t(a)) { - case "undefined": - ; - case "null": - return 7 <= m() ? NaN : 0; - case "boolean": - return a ? 1 : 0; - case "number": - return a; - case "string": - return "" === a && 5 > m() ? 0 : +a; - default: - return 5 <= m() ? NaN : 0; - } + function ib(a) { + var b = a.stack, d = b.pop(); + b.push(null); + a = (a = I(a, d)) ? a.link.asGetPublicProperty(a.name) : void 0; + c.isNullOrUndefined(a) || B(a, function(a) { + b.push(a); + }); } - function n(a) { - a = c(a); - return isNaN(a) || !isFinite(a) || 0 === a ? 0 : a | 0; + function jb(a) { + a = a.stack; + var b = a.pop(), d = a.pop(); + a.push(b == d); } - function p(a) { - switch(t(a)) { - case "undefined": - return 7 <= m() ? "undefined" : ""; - case "null": - return "null"; - case "boolean": - return a ? "true" : "false"; - case "number": - return a.toString(); - case "string": - return a; - case "movieclip": - return a.__targetPath; - case "object": - if ("function" === typeof a && a.asGetPublicProperty("toString") === b.AVM2.AS.ASFunction.traitsPrototype.asGetPublicProperty("toString")) { - return "[type Function]"; + function kb(d) { + d = d.stack; + var e = d.pop(), g = d.pop(); + d.push(void 0); + if (c.isNullOrUndefined(g)) { + a("AVM1 warning: cannot get member '" + e + "' on undefined object"); + } else { + if (g instanceof Ba) { + if (e = M(g.callFrame, e)) { + d[d.length - 1] = e.obj; } - var c = a.asCallPublicProperty("toString", null); - return "string" === typeof c ? c : "function" === typeof a ? "[type Function]" : "[type Object]"; + } else { + d[d.length - 1] = b(g, e); + } } } - function e(a, b) { - var d = s(a), h = s(b); - return "string" === typeof d && "string" === typeof h ? d < h : c(d) < c(h); + function lb(a) { + a = a.stack; + var b = L(a); + a.push(b); + } + function mb(a) { + a = a.stack; + var b = +a.pop(), b = D(b, a.length >> 1), d = {}; + z(d, null, Za); + for (var c = 0;c < b;c++) { + var e = a.pop(), g = a.pop(); + d.asSetPublicProperty(g, e); + } + a.push(d); + } + function nb(b) { + b = b.stack; + var e = b.pop(), g = b.pop(), f = L(b), k = b.length; + b.push(void 0); + if (c.isNullOrUndefined(g)) { + a("AVM1 warning: method '" + e + "' can't be constructed on undefined object"); + } else { + var h; + c.isNullOrUndefined(e) || "" === e ? h = g : (h = d(g, e, !1), h = g.asGetPublicProperty(h ? h.name : null)); + f = A(h, f); + void 0 === f && a("AVM1 warning: method '" + e + "' on object", g, "is not constructible"); + b[k] = f; + Ea(b.length === k + 1); + } + } + function ob(b) { + var d = b.stack, c = d.pop(), e = L(d), g = d.length; + d.push(void 0); + b = (b = I(b, c)) ? b.link.asGetPublicProperty(b.name) : void 0; + var f = y(b, e); + void 0 === f && (f = A(b, e), void 0 === f && a("AVM1 warning: object '" + c + (b ? "' is not constructible" : "' is undefined"))); + Ea(d.length === g + 1); + d[g] = f; + } + function pb(b) { + var d = b.stack; + b = d.pop(); + var e = d.pop(), d = d.pop(); + c.isNullOrUndefined(d) ? a("AVM1 warning: cannot set member '" + e + "' on undefined object") : d instanceof Ba ? a("AVM1 warning: cannot set member '" + e + "' on super") : g(d, e, b); + } + function qb(a) { + a = a.stack; + var b = a.pop(); + a.push("movieclip" === s(b) ? b._target : void 0); + } + function rb(a, b) { + var d = b[0], c = a.stack.pop(), e = a.constantPool, g = a.registers, c = a.scopeContainer.create(Object(c)); + Aa(d, c, e, g); + } + function sb(a) { + a = a.stack; + a.push(e(a.pop())); + } + function tb(a) { + a = a.stack; + a.push(t(a.pop())); + } + function ub(a) { + a = a.stack; + var b = a.pop(); + a.push(s(b)); + } + function vb(a) { + a = a.stack; + var b = u(a.pop()), d = u(a.pop()); + "string" === typeof b || "string" === typeof d ? a.push(t(d) + t(b)) : a.push(e(d) + e(b)); + } + function wb(a) { + a = a.stack; + var b = a.pop(), d = a.pop(); + a.push(q(d, b)); + } + function xb(a) { + a = a.stack; + var b = e(a.pop()), d = e(a.pop()); + a.push(d % b); + } + function yb(a) { + a = a.stack; + var b = m(a.pop()), d = m(a.pop()); + a.push(d & b); + } + function zb(a) { + a = a.stack; + var b = m(a.pop()), d = m(a.pop()); + a.push(d << b); + } + function Ab(a) { + a = a.stack; + var b = m(a.pop()), d = m(a.pop()); + a.push(d | b); + } + function Bb(a) { + a = a.stack; + var b = m(a.pop()), d = m(a.pop()); + a.push(d >> b); + } + function Cb(a) { + a = a.stack; + var b = m(a.pop()), d = m(a.pop()); + a.push(d >>> b); + } + function Db(a) { + a = a.stack; + var b = m(a.pop()), d = m(a.pop()); + a.push(d ^ b); + } + function Eb(a) { + a = a.stack; + var b = e(a.pop()); + b--; + a.push(b); + } + function Fb(a) { + a = a.stack; + var b = e(a.pop()); + b++; + a.push(b); + } + function Gb(a) { + a = a.stack; + a.push(a[a.length - 1]); + } + function Hb(a) { + a.isEndOfActions = !0; } - function q(a, c) { - if (b.isNullOrUndefined(a)) { - return g("AVM1 warning: cannot look up member '" + c + "' on undefined object"), null; - } - a = Object(a); - if (Lb.getPublicQualifiedName(c) in a) { - return c; - } - if (Mb(c)) { - return null; - } - if (f(a) && a.__lookupChild(c)) { - return c; - } - if (6 < m()) { - return null; - } - var d = null, e = c.toLowerCase(); - h(a, function(a) { - a.toLowerCase() === e && (d = a); + function Ib(a) { + a = a.stack; + a.push(a.pop(), a.pop()); + } + function Jb(a, b) { + var d = a.stack, c = a.registers, e = b[0]; + 0 > e || e >= c.length || (c[e] = d[d.length - 1]); + } + function Kb(a) { + a = a.stack; + var b = a.pop(), d = a.pop(); + a.push(n(d, b)); + } + function Lb(b) { + var d = b.stack; + b = d.pop(); + d.push(null); + c.isNullOrUndefined(b) ? a("AVM1 warning: cannot iterate over undefined object") : B(b, function(a) { + d.push(a); }); - return d; } - function l(a, c) { - if (b.isNullOrUndefined(a)) { - g("AVM1 warning: cannot get property '" + c + "' on undefined object"); - } else { - return a = Object(a), !a.asHasProperty(void 0, c, 0) && f(a) ? a.__lookupChild(c) : a.asGetPublicProperty(c); - } + function Mb(a) { + a = a.stack; + var b = a.pop(), d = a.pop(); + a.push(d === b); } - function u(a) { - return a && a.asGetPublicProperty("prototype"); + function Nb(a) { + a = a.stack; + var b = a.pop(), d = a.pop(); + a.push(q(b, d)); } - function w(a) { - return "undefined" !== typeof InternalError && a instanceof InternalError && "too much recursion" === a.message ? new ua("long running script -- AVM1 recursion limit is reached") : a; + function Ob(a) { + var b = a.stack; + a = a.isSwfVersion5; + var d = t(b.pop()), d = t(b.pop()) > d; + b.push(a ? d : d ? 1 : 0); } - function r(a, c) { - var d; - if (a instanceof b.AVM2.AS.ASClass) { - d = Nb(a, c); - } else { - if (!La(a)) { - return; - } - d = Object.create(u(a) || u(Object)); - a.apply(d, c); + function Pb(a, b) { + var d = a.stack, c = a.scope, e = b[1], g = C(a, b[0], e, b[2], b[3], b[4], b[5]); + e ? c.asSetPublicProperty(e, g) : d.push(g); + } + function Qb(a) { + var b = a.stack; + a = b.pop(); + var b = b.pop().asGetPublicProperty("prototype"), d = a.asGetPublicProperty("prototype"); + b.asSetPublicProperty("__proto__", d); + b.asSetPublicProperty("__constructor__", a); + } + function Rb(a) { + a = a.stack; + var b = a.pop(), d = a.pop(); + a.push(n(b, d) ? b : null); + } + function Sb(a) { + a = a.stack; + var b = a.pop(), d = +a.pop(); + D(d, a.length); + for (var c = [], e = 0;e < d;e++) { + c.push(a.pop()); } - d.constructor = a; - return d; + b._as2Interfaces = c; } - function h(a, b) { - Ob(a, b, null); - if (f(a)) { - for (var c = a._nativeAS3Object, d = 0, h = c._children.length;d < h;d++) { - var e = c._children[d].name; - a.asHasProperty(void 0, e, 0) || b.call(null, e); - } + function Tb(a, b) { + var d = b[5], c = b[3], e = b[1], g = b[2], f = b[4], k = b[6], h = a.context, l = a.scopeContainer, m = a.scope, r = a.constantPool, n = a.registers, w = h.isTryCatchListening, p; + try { + h.isTryCatchListening = !0, Aa(g, l, r, n); + } catch (q) { + h.isTryCatchListening = w, c && q instanceof La ? ("string" === typeof e ? m.asSetPublicProperty(e, q.error) : n[e] = q.error, Aa(f, l, r, n)) : p = q; + } + h.isTryCatchListening = w; + d && Aa(k, l, r, n); + if (p) { + throw p; } } - function x(a) { - var b = a.asGetPublicProperty("prototype"); - return "undefined" === typeof Proxy ? (console.error("ES6 proxies are not found"), b) : Proxy.create({getOwnPropertyDescriptor:function(a) { - return Object.getOwnPropertyDescriptor(b, a); - }, getPropertyDescriptor:function(a) { - for (var c = b;c;c = Object.getPrototypeOf(c)) { - var d = Object.getOwnPropertyDescriptor(c, a); - if (d) { - return d; - } - } - }, getOwnPropertyNames:function() { - return Object.getOwnPropertyNames(b); - }, getPropertyNames:function() { - for (var a = Object.getOwnPropertyNames(b), c = Object.getPrototypeOf(b);c;c = Object.getPrototypeOf(c)) { - a = a.concat(Object.getOwnPropertyNames(c)); - } - return a; - }, defineProperty:function(c, d) { - d && ("function" === typeof d.value && "_setClass" in d.value && d.value._setClass(a), "function" === typeof d.get && "_setClass" in d.get && d.get._setClass(a), "function" === typeof d.set && "_setClass" in d.set && d.set._setClass(a)); - return Object.defineProperty(b, c, d); - }, delete:function(a) { - return delete b[a]; - }, fix:function() { - }}); + function Ub(a) { + a = a.stack.pop(); + throw new La(a); } - function y(a, b, c) { - if (!b.executionProhibited) { - var d = Va.get(), h = b.initialScope.create(c), e = k.AVM1Context.instance, f; + function Vb(a) { + var b = a.stack, d = a.global, c = L(b); + a = b.length; + b.push(void 0); + d = d.fscommand.apply(null, c); + b[a] = d; + } + function cc(a, b) { + } + function dc(a) { + return function(b, d) { + var e; try { - k.AVM1Context.instance = b, b.isActive = !0, b.abortExecutionAt = k.avm1TimeoutDisabled.value ? Number.MAX_VALUE : Date.now() + 1E3, b.errorsIgnored = 0, b.defaultTarget = c, b.currentTarget = null, d.message("ActionScript Execution Starts"), d.indent(), za(a, h, [], []); + a(b, d), b.recoveringFromError = !1; } catch (g) { - f = w(g), f instanceof ua && (b.executionProhibited = !0, console.error("Disabling AVM1 execution")); - } - b.isActive = !1; - b.defaultTarget = null; - b.currentTarget = null; - d.unindent(); - d.message("ActionScript Execution Stops"); - k.AVM1Context.instance = e; - if (f) { - throw f; + e = b.context; + g = w(g); + if (g instanceof ua) { + throw g; + } + if (g instanceof La) { + throw g; + } + Wb.instance.reportTelemetry({topic:"error", error:1}); + if (!b.recoveringFromError) { + if (1E3 <= e.errorsIgnored++) { + throw new ua("long running script -- AVM1 errors limit is reached"); + } + console.log(typeof g); + console.log(Object.getPrototypeOf(g)); + console.log(Object.getPrototypeOf(Object.getPrototypeOf(g))); + console.error("AVM1 error: " + g); + c.AVM2.Runtime.AVM2.instance.exceptions.push({source:"avm1", message:g.message, stack:g.stack}); + b.recoveringFromError = !0; + } } - } + }; } - function G(a, b, c) { - var d = a.split(/[\/.]/g); - "" === d[d.length - 1] && d.pop(); - if ("" === d[0] || "_level0" === d[0] || "_root" === d[0]) { - b = c, d.shift(); - } - for (;0 < d.length;) { - c = b; - b = b.__lookupChild(d[0]); - if (!b) { - throw Error(d[0] + " (expr " + a + ") is not found in " + c._target); - } - d.shift(); - } - return b; + function ec() { + var a; + a = h.avm1ErrorsEnabled.value ? function(a) { + return a; + } : dc; + return{ActionGotoFrame:a(Z), ActionGetURL:a(Q), ActionNextFrame:a(S), ActionPreviousFrame:a(O), ActionPlay:a(P), ActionStop:a(V), ActionToggleQuality:a($), ActionStopSounds:a(W), ActionWaitForFrame:a(x), ActionSetTarget:a(ea), ActionGoToLabel:a(Y), ActionPush:a(R), ActionPop:a(U), ActionAdd:a(ba), ActionSubtract:a(X), ActionMultiply:a(ga), ActionDivide:a(ja), ActionEquals:a(aa), ActionLess:a(ia), ActionAnd:a(T), ActionOr:a(da), ActionNot:a(fa), ActionStringEquals:a(la), ActionStringLength:a(ca), + ActionMBStringLength:a(na), ActionStringAdd:a(ma), ActionStringExtract:a(ra), ActionMBStringExtract:a(wa), ActionStringLess:a(qa), ActionToInteger:a(ya), ActionCharToAscii:a(pa), ActionMBCharToAscii:a(za), ActionAsciiToChar:a(ha), ActionMBAsciiToChar:a(Ha), ActionJump:a(Ma), ActionIf:a(va), ActionCall:a(xa), ActionGetVariable:a(Ia), ActionSetVariable:a(Ja), ActionGetURL2:a(Ca), ActionGotoFrame2:a(ta), ActionSetTarget2:a(Da), ActionGetProperty:a(Oa), ActionSetProperty:a(Pa), ActionCloneSprite:a(Qa), + ActionRemoveSprite:a(Ra), ActionStartDrag:a(Sa), ActionEndDrag:a(Ta), ActionWaitForFrame2:a(Ua), ActionTrace:a(Va), ActionGetTime:a(Wa), ActionRandomNumber:a(Xa), ActionCallFunction:a(ab), ActionCallMethod:a(bb), ActionConstantPool:a(cb), ActionDefineFunction:a(db), ActionDefineLocal:a(eb), ActionDefineLocal2:a(fb), ActionDelete:a(gb), ActionDelete2:a(hb), ActionEnumerate:a(ib), ActionEquals2:a(jb), ActionGetMember:a(kb), ActionInitArray:a(lb), ActionInitObject:a(mb), ActionNewMethod:a(nb), + ActionNewObject:a(ob), ActionSetMember:a(pb), ActionTargetPath:a(qb), ActionWith:a(rb), ActionToNumber:a(sb), ActionToString:a(tb), ActionTypeOf:a(ub), ActionAdd2:a(vb), ActionLess2:a(wb), ActionModulo:a(xb), ActionBitAnd:a(yb), ActionBitLShift:a(zb), ActionBitOr:a(Ab), ActionBitRShift:a(Bb), ActionBitURShift:a(Cb), ActionBitXor:a(Db), ActionDecrement:a(Eb), ActionIncrement:a(Fb), ActionPushDuplicate:a(Gb), ActionReturn:a(Hb), ActionStackSwap:a(Ib), ActionStoreRegister:a(Jb), ActionInstanceOf:a(Kb), + ActionEnumerate2:a(Lb), ActionStrictEquals:a(Mb), ActionGreater:a(Nb), ActionStringGreater:a(Ob), ActionDefineFunction2:a(Pb), ActionExtends:a(Qb), ActionCastOp:a(Rb), ActionImplementsOp:a(Sb), ActionTry:a(Tb), ActionThrow:a(Ub), ActionFSCommand2:a(Vb), ActionStrictMode:a(cc)}; } - function I(a, b) { - if (a === Array) { - var c = b; - 1 == b.length && "number" === typeof b[0] && (c = [], c.length = b[0]); - return c; - } - if (a === Boolean || a === Number || a === String || a === Function) { - return a.apply(null, b); - } - if (a === Date) { - switch(b.length) { - case 0: - return new Date; - case 1: - return new Date(b[0]); - default: - return new Date(b[0], b[1], 2 < b.length ? b[2] : 1, 3 < b.length ? b[3] : 0, 4 < b.length ? b[4] : 0, 5 < b.length ? b[5] : 0, 6 < b.length ? b[6] : 0); + function Aa(a, b, d, e) { + var g = h.AVM1Context.instance; + if (!a.ir) { + var f = new h.ActionsDataParser(a, g.loaderInfo.swfVersion), k = new h.ActionsDataAnalyzer; + k.registersLimit = e.length; + k.parentResults = a.parent && a.parent.ir; + a.ir = k.analyze(f); + if (h.avm1CompilerEnabled.value) { + try { + var l = new fc; + a.ir.compiled = l.generate(a.ir); + } catch (m) { + console.error("Unable to compile AVM1 function: " + m); + } } } - if (a === Object) { - return{}; - } - } - function C(a, b) { - if (isNaN(a) || 0 > a || a > b || a != (0 | a)) { - throw Error("Invalid number of arguments: " + a); + a = a.ir; + var k = a.compiled, f = [], r = 5 <= g.loaderInfo.swfVersion, n = Ya.get(), l = b.scope; + b = {context:g, global:g.globals, scopeContainer:b, scope:l, actionTracer:n, constantPool:d, registers:e, stack:f, frame:null, isSwfVersion5:r, recoveringFromError:!1, isEndOfActions:!1}; + l._as3Object && l._as3Object._deferScriptExecution && (g.deferScriptExecution = !0); + if (k) { + return k(b); } - } - function E(a) { - var b = +a.pop(); - C(b, a.length); - for (var c = [], d = 0;d < b;d++) { - c.push(a.pop()); + d = 0; + g = g.abortExecutionAt; + if (h.avm1DebuggerEnabled.value && (h.Debugger.pause || h.Debugger.breakpoints[a.dataId])) { + debugger; } - return c; - } - function O(a, b) { - var c = a.context, d = a.global; - if (b) { - try { - var h = G(b, c.currentTarget || c.defaultTarget, d.asGetPublicProperty("_root")); - c.currentTarget = h; - } catch (e) { - throw c.currentTarget = null, e; + for (e = a.actions[0];e && !b.isEndOfActions;) { + if (0 === d++ % 1E3 && Date.now() >= g) { + throw new ua("long running script -- AVM1 instruction hang timeout"); } - } else { - c.currentTarget = null; - } - } - function K(a, b, c, d, h, e, f) { - function g() { - if (!l.executionProhibited) { - var a, c = {}; - f & 4 || c.asSetPublicProperty("arguments", arguments); - f & 2 || c.asSetPublicProperty("this", this); - f & 8 || c.asSetPublicProperty("super", Ja); - c.asSetPublicProperty("__class", s); - a = n.create(c); - var h, y = []; - if (e) { - for (h = 0;h < e.length;h++) { - var Ca = e[h]; - if (Ca) { - switch(Ca.type) { - case 1: - y[h] = arguments[Ca.index]; - break; - case 2: - y[h] = this; - break; - case 4: - y[h] = arguments; - break; - case 8: - y[h] = Ja; - break; - case 16: - y[h] = m; - break; - case 32: - y[h] = r.asGetPublicProperty("_parent"); - break; - case 64: - y[h] = m.asGetPublicProperty("_root"); - } - } - } + k = b; + l = r = void 0; + try { + var r = k, p = e.action, q = p.actionCode, s = p.args; + r.actionTracer.print(p, r.stack); + n = !1; + switch(q | 0) { + case 129: + Z(r, s); + break; + case 131: + Q(r, s); + break; + case 4: + S(r); + break; + case 5: + O(r); + break; + case 6: + P(r); + break; + case 7: + V(r); + break; + case 8: + $(r); + break; + case 9: + W(r); + break; + case 138: + n = x(r, s); + break; + case 139: + ea(r, s); + break; + case 140: + Y(r, s); + break; + case 150: + R(r, s); + break; + case 23: + U(r); + break; + case 10: + ba(r); + break; + case 11: + X(r); + break; + case 12: + ga(r); + break; + case 13: + ja(r); + break; + case 14: + aa(r); + break; + case 15: + ia(r); + break; + case 16: + T(r); + break; + case 17: + da(r); + break; + case 18: + fa(r); + break; + case 19: + la(r); + break; + case 20: + ca(r); + break; + case 49: + na(r); + break; + case 33: + ma(r); + break; + case 21: + ra(r); + break; + case 53: + wa(r); + break; + case 41: + qa(r); + break; + case 24: + ya(r); + break; + case 50: + pa(r); + break; + case 54: + za(r); + break; + case 51: + ha(r); + break; + case 55: + Ha(r); + break; + case 153: + break; + case 157: + n = va(r, s); + break; + case 158: + xa(r); + break; + case 28: + Ia(r); + break; + case 29: + Ja(r); + break; + case 154: + Ca(r, s); + break; + case 159: + ta(r, s); + break; + case 32: + Da(r); + break; + case 34: + Oa(r); + break; + case 35: + Pa(r); + break; + case 36: + Qa(r); + break; + case 37: + Ra(r); + break; + case 39: + Sa(r); + break; + case 40: + Ta(r); + break; + case 141: + n = Ua(r, s); + break; + case 38: + Va(r); + break; + case 52: + Wa(r); + break; + case 48: + Xa(r); + break; + case 61: + ab(r); + break; + case 82: + bb(r); + break; + case 136: + cb(r, s); + break; + case 155: + db(r, s); + break; + case 60: + eb(r); + break; + case 65: + fb(r); + break; + case 58: + gb(r); + break; + case 59: + hb(r); + break; + case 70: + ib(r); + break; + case 73: + jb(r); + break; + case 78: + kb(r); + break; + case 66: + lb(r); + break; + case 67: + mb(r); + break; + case 83: + nb(r); + break; + case 64: + ob(r); + break; + case 79: + pb(r); + break; + case 69: + qb(r); + break; + case 148: + rb(r, s); + break; + case 74: + sb(r); + break; + case 75: + tb(r); + break; + case 68: + ub(r); + break; + case 71: + vb(r); + break; + case 72: + wb(r); + break; + case 63: + xb(r); + break; + case 96: + yb(r); + break; + case 99: + zb(r); + break; + case 97: + Ab(r); + break; + case 100: + Bb(r); + break; + case 101: + Cb(r); + break; + case 98: + Db(r); + break; + case 81: + Eb(r); + break; + case 80: + Fb(r); + break; + case 76: + Gb(r); + break; + case 62: + Hb(r); + break; + case 77: + Ib(r); + break; + case 135: + Jb(r, s); + break; + case 84: + Kb(r); + break; + case 85: + Lb(r); + break; + case 102: + Mb(r); + break; + case 103: + Nb(r); + break; + case 104: + Ob(r); + break; + case 142: + Pb(r, s); + break; + case 105: + Qb(r); + break; + case 43: + Rb(r); + break; + case 44: + Sb(r); + break; + case 143: + Tb(r, s); + break; + case 42: + Ub(r); + break; + case 45: + Vb(r); + break; + case 137: + break; + case 0: + r.isEndOfActions = !0; + break; + default: + throw Error("Unknown action code: " + q);; } - for (h = 0;h < arguments.length || h < d.length;h++) { - u && u[h] || c.asSetPublicProperty(d[h], arguments[h]); + l = n; + k.recoveringFromError = !1; + } catch (u) { + r = k.context; + u = w(u); + if (h.avm1ErrorsEnabled.value && !r.isTryCatchListening || u instanceof ua) { + throw u; } - c = k.AVM1Context.instance; - h = l.isActive; - var Ca = l.defaultTarget, w = l.currentTarget, t, G; - try { - k.AVM1Context.instance = l; - h || (l.abortExecutionAt = k.avm1TimeoutDisabled.value ? Number.MAX_VALUE : Date.now() + 1E3, l.errorsIgnored = 0, l.isActive = !0); - l.defaultTarget = x; - l.currentTarget = null; - p.indent(); - if (256 <= ++l.stackDepth) { - throw new ua("long running script -- AVM1 recursion limit is reached"); - } - t = za(b, a, q, y); - } catch (E) { - G = E; - } - l.defaultTarget = Ca; - l.currentTarget = w; - l.isActive = h; - l.stackDepth--; - p.unindent(); - k.AVM1Context.instance = c; - if (G) { - throw G; + if (u instanceof La) { + throw u; } - return t; - } - } - var l = a.context, m = a.global, n = a.scopeContainer, r = a.scope, p = a.actionTracer, x = l.defaultTarget, q = a.constantPool, u = null; - if (e) { - for (a = 0;a < e.length;a++) { - (h = e[a]) && 1 === h.type && (u || (u = []), u[e[a].index] = !0); - } - } - var s; - s = g; - g._setClass = function(a) { - s = a; - }; - g.instanceConstructor = g; - g.debugName = "avm1 " + (c || ""); - c && (g.name = c); - return g; - } - function F(a, b, c) { - var d = a.global; - a = a.context; - a = a.currentTarget || a.defaultTarget; - var h, e; - if (0 <= b.indexOf(":")) { - e = b.split(":"); - h = G(e[0], a, d.asGetPublicProperty("_root")); - if (!h) { - throw Error(e[0] + " is undefined"); - } - e = e[1]; - } else { - if (0 <= b.indexOf(".")) { - for (b = b.split("."), e = b.pop(), h = d, d = 0;d < b.length;d++) { - if (h = h.asGetPublicProperty(b[d]) || h[b[d]], !h) { - throw Error(b.slice(0, d + 1) + " is undefined"); + Wb.instance.reportTelemetry({topic:"error", error:1}); + if (!k.recoveringFromError) { + if (1E3 <= r.errorsIgnored++) { + throw new ua("long running script -- AVM1 errors limit is reached"); } + console.error("AVM1 error: " + u); + c.AVM2.Runtime.AVM2.instance.exceptions.push({source:"avm1", message:u.message, stack:u.stack}); + k.recoveringFromError = !0; } } + e = l ? e.conditionalJumpTo : e.next; + e = a.actions[e]; } - if (!h) { - return null; - } - d = q(h, e); - return(b = null !== d) || c ? {obj:h, name:d || e, resolved:b} : null; + return f.pop(); } - function J(a, b) { - var c = a.scopeContainer, d = a.context, d = d.currentTarget || d.defaultTarget, h = a.scope; - if (h.asHasProperty(void 0, b, 0)) { - return h.asGetPublicProperty(b); - } - var e = F(a, b, !1); - if (e) { - return e.obj.asGetPublicProperty(e.name); - } - if (e = q(h, b)) { - return h.asGetPublicProperty(e); - } - for (;c;c = c.next) { - if (e = q(c.scope, b), null !== e) { - return c.scope.asGetPublicProperty(e); - } - } - if (d.asHasProperty(void 0, b, 0)) { - return d.asGetPublicProperty(b); - } - if ("this" === b) { - return d; + var Yb = c.AVM2.Runtime.forEachPublicProperty, ac = c.AVM2.Runtime.construct, Zb = c.isNumeric, Na = c.isFunction, gc = c.Debug.notImplemented, Xb = c.AVM2.Runtime.asCoerceString, $a = c.AVM2.Runtime.sliceArguments, Fa = c.Options.Option, Wb = c.Telemetry, Ea = c.Debug.assert, Ga = c.Settings.shumwayOptions.register(new c.Options.OptionSet("AVM1")); + h.avm1TraceEnabled = Ga.register(new Fa("t1", "traceAvm1", "boolean", !1, "trace AVM1 execution")); + h.avm1ErrorsEnabled = Ga.register(new Fa("e1", "errorsAvm1", "boolean", !1, "fail on AVM1 warnings and errors")); + h.avm1TimeoutDisabled = Ga.register(new Fa("ha1", "nohangAvm1", "boolean", !1, "disable fail on AVM1 hang")); + h.avm1CompilerEnabled = Ga.register(new Fa("ca1", "compileAvm1", "boolean", !0, "compiles AVM1 code")); + h.avm1DebuggerEnabled = Ga.register(new Fa("da1", "debugAvm1", "boolean", !1, "allows AVM1 code debugging")); + h.Debugger = {pause:!1, breakpoints:{}}; + var $b = "addCallback addListener addProperty addRequestHeader AsBroadcaster attachAudio attachMovie attachSound attachVideo beginFill beginGradientFill blendMode blockIndent broadcastMessage cacheAsBitmap CASEINSENSITIVE charAt charCodeAt checkPolicyFile clearInterval clearRequestHeaders concat createEmptyMovieClip curveTo DESCENDING displayState duplicateMovieClip E endFill exactSettings fromCharCode fullScreenSourceRect getBounds getBytesLoaded getBytesTotal getDate getDay getDepth getDepth getDuration getFullYear getHours getLocal getMilliseconds getMinutes getMonth getNewTextFormat getPan getPosition getRGB getSeconds getSize getTextFormat getTime getTimezoneOffset getTransform getUTCDate getUTCDay getUTCFullYear getUTCHours getUTCMilliseconds getUTCMinutes getUTCMonth getUTCSeconds getUTCYear getVolume getYear globalToLocal gotoAndPlay gotoAndStop hasAccessibility hasAudio hasAudioEncoder hasEmbeddedVideo hasIME hasMP3 hasOwnProperty hasPrinting hasScreenBroadcast hasScreenPlayback hasStreamingAudio hasStreamingVideo hasVideoEncoder hitTest indexOf isActive isDebugger isFinite isNaN isPropertyEnumerable isPrototypeOf lastIndexOf leftMargin letterSpacing lineStyle lineTo LN10 LN10E LN2 LN2E loadSound localFileReadDisable localToGlobal MAX_VALUE MIN_VALUE moveTo NaN NEGATIVE_INFINITY nextFrame NUMERIC onChanged onData onDragOut onDragOver onEnterFrame onFullScreen onKeyDown onKeyUp onKillFocus onLoad onMouseDown onMouseMove onMouseUp onPress onRelease onReleaseOutside onResize onResize onRollOut onRollOver onScroller onSetFocus onStatus onSync onUnload parseFloat parseInt PI pixelAspectRatio playerType POSITIVE_INFINITY prevFrame registerClass removeListener removeMovieClip removeTextField replaceSel RETURNINDEXEDARRAY rightMargin scale9Grid scaleMode screenColor screenDPI screenResolutionX screenResolutionY serverString setClipboard setDate setDuration setFps setFullYear setHours setInterval setMask setMilliseconds setMinutes setMonth setNewTextFormat setPan setPosition setRGB setSeconds setTextFormat setTime setTimeout setTransform setTransform setUTCDate setUTCFullYear setUTCHours setUTCMilliseconds setUTCMinutes setUTCMonth setUTCSeconds setVolume setYear showMenu showRedrawRegions sortOn SQRT1_2 SQRT2 startDrag stopDrag swapDepths tabEnabled tabIndex tabIndex tabStops toLowerCase toString toUpperCase trackAsMenu UNIQUESORT updateAfterEvent updateProperties useCodepage useHandCursor UTC valueOf".split(" "), + Ka = null, hc = function() { + function a(b, d) { + this.scope = b; + this.next = d; } - if (c = f(d) && d.__lookupChild(b)) { - return c; + a.prototype.create = function(b) { + return new a(b, this); + }; + return a; + }(), bc = function(a) { + function b() { + a.call(this); + this.asSetPublicProperty("toString", this.toString); } - } - function A(a, b) { - var c = a.global, d = b[0]; - b[1] ? c.gotoAndPlay(d + 1) : c.gotoAndStop(d + 1); - } - function B(a, b) { - a.global.getURL(b[0], b[1]); - } - function z(a) { - a.global.nextFrame(); - } - function P(a) { - a.global.prevFrame(); - } - function D(a) { - a.global.play(); - } - function L(a) { - a.global.stop(); - } - function M(a) { - a.global.toggleHighQuality(); - } - function V(a) { - a.global.stopAllSounds(); - } - function Q(a, b) { - return!a.global.ifFrameLoaded(b[0]); - } - function U(a, b) { - O(a, b[0]); - } - function S(a, b) { - a.global.gotoLabel(b[0]); - } - function aa(a, b) { - var c = a.registers, d = a.constantPool, h = a.stack; - b.forEach(function(a) { - a instanceof k.ParsedPushConstantAction ? h.push(d[a.constantIndex]) : a instanceof k.ParsedPushRegisterAction ? h.push(c[a.registerNumber]) : h.push(a); - }); - } - function $(a) { - a.stack.pop(); - } - function ea(a) { - a = a.stack; - var b = c(a.pop()), d = c(a.pop()); - a.push(b + d); - } - function Z(a) { - a = a.stack; - var b = c(a.pop()), d = c(a.pop()); - a.push(d - b); - } - function v(a) { - a = a.stack; - var b = c(a.pop()), d = c(a.pop()); - a.push(b * d); - } - function X(a) { - var b = a.stack; - a = a.isSwfVersion5; - var d = c(b.pop()), d = c(b.pop()) / d; - b.push(a ? d : isFinite(d) ? d : "#ERROR#"); - } - function ba(a) { - var b = a.stack; - a = a.isSwfVersion5; - var d = c(b.pop()), h = c(b.pop()), d = d == h; - b.push(a ? d : d ? 1 : 0); - } - function R(a) { - var b = a.stack; - a = a.isSwfVersion5; - var d = c(b.pop()), d = c(b.pop()) < d; - b.push(a ? d : d ? 1 : 0); - } - function H(b) { - var c = b.stack; - b = b.isSwfVersion5; - var d = a(c.pop()), h = a(c.pop()), d = d && h; - c.push(b ? d : d ? 1 : 0); - } - function Y(b) { - var c = b.stack; - b = b.isSwfVersion5; - var d = a(c.pop()), h = a(c.pop()), d = d || h; - c.push(b ? d : d ? 1 : 0); - } - function ga(b) { - var c = b.stack; - b = b.isSwfVersion5; - var d = !a(c.pop()); - c.push(b ? d : d ? 1 : 0); - } - function ca(a) { - var b = a.stack; - a = a.isSwfVersion5; - var c = p(b.pop()), d = p(b.pop()), c = c == d; - b.push(a ? c : c ? 1 : 0); - } - function ka(a) { - var b = a.stack; - a = a.global; - var c = p(b.pop()); - b.push(a.length(c)); - } - function W(a) { - var b = a.stack; - a = a.global; - var c = p(b.pop()); - b.push(a.length(c)); - } - function ha(a) { - a = a.stack; - var b = p(a.pop()), c = p(a.pop()); - a.push(c + b); - } - function da(a) { - var b = a.stack; - a = a.global; - var c = b.pop(), d = b.pop(), h = p(b.pop()); - b.push(a.substring(h, d, c)); - } - function ja(a) { - var b = a.stack; - a = a.global; - var c = b.pop(), d = b.pop(), h = p(b.pop()); - b.push(a.mbsubstring(h, d, c)); - } - function N(a) { - var b = a.stack; - a = a.isSwfVersion5; - var c = p(b.pop()), c = p(b.pop()) < c; - b.push(a ? c : c ? 1 : 0); - } - function ma(a) { - var b = a.stack; - b.push(a.global.int(b.pop())); - } - function T(a) { - var b = a.stack; - a = a.global; - var c = b.pop(); - a = a.ord(c); - b.push(a); - } - function oa(a) { - var b = a.stack; - a = a.global; - var c = b.pop(); - a = a.mbord(c); - b.push(a); - } - function pa(a) { - var b = a.stack; - a = a.global; - var c = +b.pop(); - a = a.chr(c); - b.push(a); - } - function na(a) { - var b = a.stack; - a = a.global; - var c = +b.pop(); - a = a.mbchr(c); - b.push(a); - } - function fa(a, b) { - } - function sa(a, b) { - return!!a.stack.pop(); - } - function xa(a) { - var b = a.global; - a = a.stack.pop(); - b.call(a); - } - function la(a) { - var b = a.stack, c = "" + b.pop(), d = b.length; - b.push(void 0); - b[d] = J(a, c); - } - function qa(a) { - var b = a.stack, c = b.pop(); - a: { - var b = "" + b.pop(), d = a.scopeContainer, h = a.context, e = h.currentTarget || h.defaultTarget, f = a.scope; - if (h.currentTarget) { - e.asSetPublicProperty(b, c); - } else { - if (f.asHasProperty(void 0, b, 0)) { - f.asSetPublicProperty(b, c); - } else { - if (a = F(a, b, !0)) { - a.obj.asSetPublicProperty(a.name, c); - } else { - for (a = d;a.next;a = a.next) { - if (d = q(a.scope, b), null !== d) { - a.scope.asSetPublicProperty(d, c); - break a; - } - } - e.asSetPublicProperty(b, c); + __extends(b, a); + b.prototype.toString = function() { + return this; + }; + return b; + }(c.AVM2.AS.ASObject), ic = function() { + function a(b, d, c, e) { + this.previousFrame = b; + this.currentThis = d; + this.fn = c; + this.args = e; + this.inSequence = b ? b.calleeThis === d && b.calleeFn === c : !1; + this.resetCallee(); + } + a.prototype.setCallee = function(a, b, d, c) { + this.calleeThis = a; + this.calleeSuper = b; + this.calleeFn = d; + this.calleeArgs = c; + }; + a.prototype.resetCallee = function() { + this.calleeFn = this.calleeSuper = this.calleeThis = null; + }; + return a; + }(), jc = function(e) { + function f(a) { + e.call(this); + this.loaderInfo = a; + this.globals = new (h.Lib.AVM1Globals.createAVM1Class())(this); + this.initialScope = new hc(this.globals, null); + this.assets = {}; + this.assetsSymbols = []; + this.assetsClasses = []; + this.executionProhibited = this.isActive = !1; + this.stackDepth = this.abortExecutionAt = 0; + this.frame = null; + this.isTryCatchListening = !1; + this.errorsIgnored = 0; + this.deferScriptExecution = !0; + this.pendingScripts = []; + this.eventObservers = Object.create(null); + var c = this; + this.utils = {hasProperty:function(a, b) { + var e; + c.enterContext(function() { + e = !!d(a, b, !1); + }, a); + return e; + }, getProperty:function(a, d) { + var e; + c.enterContext(function() { + e = b(a, d); + }, a); + return e; + }, setProperty:function(a, b, d) { + c.enterContext(function() { + g(a, b, d); + }, a); + }}; + } + __extends(f, e); + f.prototype.addAsset = function(a, b, d) { + c.Debug.assert("string" === typeof a && !isNaN(b)); + this.assets[a.toLowerCase()] = b; + this.assetsSymbols[b] = d; + }; + f.prototype.registerClass = function(b, d) { + b = Xb(b); + if (null === b) { + return a("Cannot register class for symbol: className is missing"), null; + } + var c = this.assets[b.toLowerCase()]; + void 0 === c ? a("Cannot register " + b + " class for symbol") : this.assetsClasses[c] = d; + }; + f.prototype.getAsset = function(a) { + a = Xb(a); + if (null !== a && (a = this.assets[a.toLowerCase()], void 0 !== a)) { + var b = this.assetsSymbols[a]; + if (!b) { + b = this.loaderInfo.getSymbolById(a); + if (!b) { + c.Debug.warning("Symbol " + a + " is not defined."); + return; } + this.assetsSymbols[a] = b; } + return{symbolId:a, symbolProps:b, theClass:this.assetsClasses[a]}; } - } - } - function ia(a, b) { - var c = a.global, d = a.stack, h = b[0], e = d.pop(), d = d.pop(), f; - h & 1 ? f = "GET" : h & 2 && (f = "POST"); - h & 64 ? h & 128 ? c.loadVariables(d, e, f) : c.loadMovie(d, e, f) : c.getURL(d, e, f); - } - function wa(a, b) { - var c = a.global, d = b[0], h = [a.stack.pop()]; - d & 2 && h.push(b[1]); - (d & 1 ? c.gotoAndPlay : c.gotoAndStop).apply(c, h); - } - function Ia(a) { - var b = a.stack.pop(); - O(a, b); - } - function va(a) { - var b = a.global; - a = a.stack; - var c = a.pop(), d = a.pop(), h = a.length; - a.push(void 0); - a[h] = b.getAVM1Property(d, c); - } - function ya(a) { - var b = a.global, c = a.stack; - a = c.pop(); - var d = c.pop(), c = c.pop(); - b.setAVM1Property(c, d, a); - } - function Ga(a) { - var b = a.global, c = a.stack; - a = c.pop(); - var d = c.pop(), c = c.pop(); - b.duplicateMovieClip(c, d, a); - } - function Ha(a) { - var b = a.global; - a = a.stack.pop(); - b.removeMovieClip(a); - } - function Aa(a) { - var b = a.global, c = a.stack; - a = c.pop(); - var d = c.pop(), c = c.pop() ? {y2:c.pop(), x2:c.pop(), y1:c.pop(), x1:c.pop()} : null; - a = [a, d]; - c && (a = a.concat(c.x1, c.y1, c.x2, c.y2)); - b.startDrag.apply(b, a); - } - function ra(a) { - a.global.stopDrag(); - } - function ta(a, b) { - var c = a.global, d = a.stack.pop(); - return!c.ifFrameLoaded(d); - } - function Ba(a) { - var b = a.global; - a = a.stack.pop(); - b.trace(a); - } - function Ma(a) { - a.stack.push(a.global.getTimer()); - } - function Na(a) { - var b = a.stack; - b.push(a.global.random(b.pop())); - } - function Oa(a) { - var b = a.stack, c = a.scope, d = b.pop(), h = E(b), e = b.length; - b.push(void 0); - a = J(a, d); - a instanceof Function ? (Da(b.length === e + 1), b[e] = a.apply(c, h)) : g("AVM1 warning: function '" + d + (a ? "' is not callable" : "' is undefined")); - } - function Pa(a) { - var c = a.stack, d = c.pop(), h = c.pop(), e = E(c), f, k = c.length; - c.push(void 0); - b.isNullOrUndefined(h) ? g("AVM1 warning: method '" + d + "' can't be called on undefined object") : b.isNullOrUndefined(d) || "" === d ? (h === Ja ? (h = J(a, "__class").__super, f = J(a, "this")) : f = h, La(h) ? c[k] = h.apply(f, e) : g("AVM1 warning: obj '" + h + (h ? "' is not callable" : "' is undefined")), Da(c.length === k + 1)) : (h === Ja ? (f = u(J(a, "__class").__super), h = J(a, "this")) : f = h, a = q(f, d), f = f.asGetPublicProperty(a), La(f) ? (Da(c.length === k + 1), c[k] = - f.apply(h, e)) : g("AVM1 warning: method '" + d + "' on object", h, b.isNullOrUndefined(f) ? "is undefined" : "is not callable")); - } - function Qa(a, b) { - a.constantPool = b[0]; - } - function Ra(a, b) { - var c = a.stack, d = a.scope, h = b[1], e = K(a, b[0], h, b[2], 0, null, 0); - h ? d.asSetPublicProperty(h, e) : c.push(e); - } - function Sa(a) { - var b = a.stack; - a = a.scope; - var c = b.pop(), b = b.pop(); - a.asSetPublicProperty(b, c); - } - function Ta(a) { - var b = a.scope; - a = a.stack.pop(); - b.asSetPublicProperty(a, void 0); - } - function Ua(a) { - a = a.stack; - var b = a.pop(), c = a.pop(); - c.asSetPublicProperty(b, void 0); - a.push(c.asDeleteProperty(void 0, b, 0)); - } - function Wa(a) { - var b = a.stack, c; - a: { - c = b.pop(); - for (a = a.scopeContainer;a;a = a.next) { - if (a.scope.asHasProperty(void 0, c, 0)) { - a.scope.asSetPublicProperty(c, void 0); - c = a.scope.asDeleteProperty(void 0, c, 0); - break a; + }; + f.prototype.resolveTarget = function(a) { + var b = this.currentTarget || this.defaultTarget; + a ? "string" === typeof a && (a = K(a, b, this.resolveLevel(0))) : a = b; + if ("object" !== typeof a || null === a || !("_as3Object" in a)) { + throw Error("Invalid AVM1 target object: " + Object.prototype.toString.call(a)); + } + return a; + }; + f.prototype.resolveLevel = function(a) { + if (0 === a) { + return this.root; + } + }; + f.prototype.addToPendingScripts = function(a) { + this.deferScriptExecution ? this.pendingScripts.push(a) : a(); + }; + f.prototype.flushPendingScripts = function() { + for (var a = this.pendingScripts;a.length;) { + a.shift()(); + } + this.deferScriptExecution = !1; + }; + f.prototype.pushCallFrame = function(a, b, d) { + return this.frame = a = new ic(this.frame, a, b, d); + }; + f.prototype.popCallFrame = function() { + var a = this.frame.previousFrame; + return this.frame = a; + }; + f.prototype.enterContext = function(a, b) { + if (this === h.AVM1Context.instance && this.isActive && this.defaultTarget === b && null === this.currentTarget) { + a(), this.currentTarget = null; + } else { + var d = h.AVM1Context.instance, c = this.isActive, e = this.defaultTarget, g = this.currentTarget, f; + try { + h.AVM1Context.instance = this, c || (this.abortExecutionAt = h.avm1TimeoutDisabled.value ? Number.MAX_VALUE : Date.now() + 1E3, this.errorsIgnored = 0, this.isActive = !0), this.defaultTarget = b, this.currentTarget = null, a(); + } catch (k) { + f = k; + } + this.defaultTarget = e; + this.currentTarget = g; + this.isActive = c; + h.AVM1Context.instance = d; + if (f) { + throw f; } } - c = !1; + }; + f.prototype.executeActions = function(a, b) { + N(a, this, b); + }; + f.prototype.registerEventPropertyObserver = function(a, b) { + var d = this.eventObservers[a]; + d || (d = [], this.eventObservers[a] = d); + d.push(b); + }; + f.prototype.unregisterEventPropertyObserver = function(a, b) { + var d = this.eventObservers[a]; + if (d) { + var c = d.indexOf(b); + 0 > c || d.splice(c, 1); + } + }; + f.prototype.broadcastEventPropertyChange = function(a) { + var b = this.eventObservers[a]; + b && b.forEach(function(b) { + return b.onEventPropertyModified(a); + }); + }; + return f; + }(h.AVM1Context); + h.AVM1Context.create = function(a) { + return new jc(a); + }; + var La = function() { + return function(a) { + this.error = a; + }; + }(), ua = function(a) { + function b(d, c) { + a.call(this, d); + this.error = c; } - b.push(c); - } - function Xa(a) { - var c = a.stack, d = c.pop(); - c.push(null); - a = J(a, d); - b.isNullOrUndefined(a) || h(a, function(a) { - c.push(a); - }); - } - function Ya(a) { - a = a.stack; - var b = a.pop(), c = a.pop(); - a.push(b == c); - } - function Za(a) { - a = a.stack; - var b = a.pop(), c = a.pop(); - "prototype" === b ? a.push(x(c)) : (b = q(c, b), a.push(null === b ? void 0 : l(c, b))); - } - function $a(a) { - a = a.stack; - var b = E(a); - a.push(b); - } - function ab(a) { - a = a.stack; - var b = +a.pop(); - C(b, a.length >> 1); - for (var c = {}, d = 0;d < b;d++) { - var h = a.pop(), e = a.pop(); - c.asSetPublicProperty(e, h); + __extends(b, a); + return b; + }(Error), sa = {link:void 0, name:null}; + h.executeActions = N; + var Za = function(a) { + function b() { + a.apply(this, arguments); } - a.push(c); - } - function bb(a) { - a = a.stack; - var c = a.pop(), d = a.pop(), h = E(a), e = a.length; - a.push(void 0); - if (b.isNullOrUndefined(d)) { - g("AVM1 warning: method '" + c + "' can't be constructed on undefined object"); - } else { - var f; - b.isNullOrUndefined(c) || "" === c ? f = d : (f = q(d, c), f = d.asGetPublicProperty(f)); - h = r(f, h); - void 0 === h && g("AVM1 warning: method '" + c + "' on object", d, "is not constructible"); - a[e] = h; - Da(a.length === e + 1); + __extends(b, a); + return b; + }(c.AVM2.AS.ASObject), Ba = function() { + return function(a) { + this.callFrame = a; + }; + }(), ka = {obj:null, link:null, name:null}, fc = function() { + function a() { + a.cachedCalls || (a.cachedCalls = ec()); } - } - function cb(a) { - var b = a.stack, c = b.pop(), d = E(b), h = b.length; - b.push(void 0); - a = J(a, c); - var e = I(a, d); - void 0 === e && (e = r(a, d), void 0 === e && g("AVM1 warning: object '" + c + (a ? "' is not constructible" : "' is undefined"))); - Da(b.length === h + 1); - b[h] = e; - } - function db(a) { - var c = a.stack; - a = c.pop(); - var d = c.pop(), c = c.pop(); - b.isNullOrUndefined(c) ? g("AVM1 warning: cannot set member '" + d + "' on undefined object") : c.asSetPublicProperty(d, a); - } - function eb(a) { - a = a.stack; - var b = a.pop(); - a.push("movieclip" === t(b) ? b._target : void 0); - } - function fb(a, b) { - var c = b[0], d = a.stack.pop(), h = a.constantPool, e = a.registers, d = a.scopeContainer.create(Object(d)); - za(c, d, h, e); - } - function gb(a) { - a = a.stack; - a.push(c(a.pop())); - } - function hb(a) { - a = a.stack; - a.push(p(a.pop())); - } - function ib(a) { - a = a.stack; - var b = a.pop(); - a.push(t(b)); - } - function jb(a) { - a = a.stack; - var b = d(a.pop()), h = d(a.pop()); - "string" === typeof b || "string" === typeof h ? a.push(p(h) + p(b)) : a.push(c(h) + c(b)); - } - function kb(a) { - a = a.stack; - var b = a.pop(), c = a.pop(); - a.push(e(c, b)); - } - function lb(a) { - a = a.stack; - var b = c(a.pop()), d = c(a.pop()); - a.push(d % b); - } - function mb(a) { - a = a.stack; - var b = n(a.pop()), c = n(a.pop()); - a.push(c & b); - } - function nb(a) { - a = a.stack; - var b = n(a.pop()), c = n(a.pop()); - a.push(c << b); - } - function ob(a) { - a = a.stack; - var b = n(a.pop()), c = n(a.pop()); - a.push(c | b); - } - function pb(a) { - a = a.stack; - var b = n(a.pop()), c = n(a.pop()); - a.push(c >> b); - } - function qb(a) { - a = a.stack; - var b = n(a.pop()), c = n(a.pop()); - a.push(c >>> b); - } - function rb(a) { - a = a.stack; - var b = n(a.pop()), c = n(a.pop()); - a.push(c ^ b); - } - function sb(a) { - a = a.stack; - var b = c(a.pop()); - b--; - a.push(b); - } - function tb(a) { - a = a.stack; - var b = c(a.pop()); - b++; - a.push(b); - } - function ub(a) { - a = a.stack; - a.push(a[a.length - 1]); - } - function vb(a) { - a.isEndOfActions = !0; - } - function wb(a) { - a = a.stack; - a.push(a.pop(), a.pop()); - } - function xb(a, b) { - var c = a.stack; - a.registers[b[0]] = c[c.length - 1]; - } - function yb(a) { - a = a.stack; - var b = a.pop(), c = a.pop(); - a.push(c instanceof b ? !0 : !1); - } - function zb(a) { - var c = a.stack; - a = c.pop(); - c.push(null); - b.isNullOrUndefined(a) ? g("AVM1 warning: cannot iterate over undefined object") : h(a, function(a) { - c.push(a); - }); - } - function Ab(a) { - a = a.stack; - var b = a.pop(), c = a.pop(); - a.push(c === b); - } - function Bb(a) { - a = a.stack; - var b = a.pop(), c = a.pop(); - a.push(e(b, c)); - } - function Cb(a) { - var b = a.stack; - a = a.isSwfVersion5; - var c = p(b.pop()), c = p(b.pop()) > c; - b.push(a ? c : c ? 1 : 0); - } - function Db(a, b) { - var c = a.stack, d = a.scope, h = b[1], e = K(a, b[0], h, b[2], b[3], b[4], b[5]); - h ? d.asSetPublicProperty(h, e) : c.push(e); - } - function Eb(a) { - var b = a.stack; - a = b.pop(); - var b = b.pop(), c = Object.create(a.traitsPrototype || u(a), {constructor:{value:b, enumerable:!1}}); - b.__super = a; - b.prototype = c; - } - function Fb(a) { - a = a.stack; - var b = a.pop(), c = a.pop(); - a.push(b instanceof c ? b : null); - } - function Gb(a) { - a = a.stack; - var b = a.pop(), c = +a.pop(); - C(c, a.length); - for (var d = [], h = 0;h < c;h++) { - d.push(a.pop()); + a.prototype.convertArgs = function(a, b, d, c) { + for (var e = [], g = 0;g < a.length;g++) { + var f = a[g]; + if ("object" !== typeof f || null === f || Array.isArray(f)) { + void 0 === f ? e.push("undefined") : e.push(JSON.stringify(f)); + } else { + if (f instanceof h.ParsedPushConstantAction) { + if (c.singleConstantPool) { + var k = c.singleConstantPool[f.constantIndex]; + e.push(void 0 === k ? "undefined" : JSON.stringify(k)); + } else { + var k = "", l = d.constantPool; + l && (k = l[f.constantIndex], k = void 0 === k ? "undefined" : JSON.stringify(k), k = 0 <= k.indexOf("*/") ? "" : " /* " + k + " */"); + e.push("constantPool[" + f.constantIndex + "]" + k); + } + } else { + f instanceof h.ParsedPushRegisterAction ? (f = f.registerNumber, 0 > f || f >= c.registersLimit ? e.push("undefined") : e.push("registers[" + f + "]")) : f instanceof h.AVM1ActionsData ? (k = "code_" + b + "_" + g, d[k] = f, e.push("res." + k)) : gc("Unknown AVM1 action argument type"); + } + } + } + return e.join(","); + }; + a.prototype.convertAction = function(a, b, d, c, e) { + switch(a.action.actionCode) { + case 153: + ; + case 62: + return ""; + case 136: + return d.constantPool = a.action.args[0], " constantPool = [" + this.convertArgs(a.action.args[0], b, d, e) + "];\n ectx.constantPool = constantPool;\n"; + case 150: + return " stack.push(" + this.convertArgs(a.action.args, b, d, e) + ");\n"; + case 135: + return a = a.action.args[0], 0 > a || a >= e.registersLimit ? "" : " registers[" + a + "] = stack[stack.length - 1];\n"; + case 138: + ; + case 141: + return " if (calls." + a.action.actionName + "(ectx,[" + this.convertArgs(a.action.args, b, d, e) + "])) { position = " + a.conditionalJumpTo + "; checkTimeAfter -= " + (c + 1) + "; break; }\n"; + case 157: + return " if (!!stack.pop()) { position = " + a.conditionalJumpTo + "; checkTimeAfter -= " + (c + 1) + "; break; }\n"; + default: + return " calls." + a.action.actionName + "(ectx" + (a.action.args ? ",[" + this.convertArgs(a.action.args, b, d, e) + "]" : "") + ");\n"; + } + }; + a.prototype.checkAvm1Timeout = function(a) { + if (Date.now() >= a.context.abortExecutionAt) { + throw new ua("long running script -- AVM1 instruction hang timeout"); + } + }; + a.prototype.generate = function(b) { + var d = this, c = b.blocks, e = {}, g = 0, f = b.dataId, k = "return function avm1gen_" + f + "(ectx) {\nvar position = 0;\nvar checkTimeAfter = 0;\nvar constantPool = ectx.constantPool, registers = ectx.registers, stack = ectx.stack;\n"; + h.avm1DebuggerEnabled.value && (k += "/* Running " + f + " */ if (Shumway.AVM1.Debugger.pause || Shumway.AVM1.Debugger.breakpoints." + f + ") { debugger; }\n"); + k += "while (!ectx.isEndOfActions) {\nif (checkTimeAfter <= 0) { checkTimeAfter = 1000; checkTimeout(ectx); }\nswitch(position) {\n"; + c.forEach(function(a) { + k += " case " + a.label + ":\n"; + a.items.forEach(function(a, c) { + k += d.convertAction(a, g++, e, c, b); + }); + k += " position = " + a.jump + ";\n checkTimeAfter -= " + a.items.length + ";\n break;\n"; + }); + k += " default: ectx.isEndOfActions = true; break;\n}\n}\nreturn stack.pop();};"; + k += "//# sourceURL=avm1gen-" + f; + return(new Function("calls", "res", "checkTimeout", k))(a.cachedCalls, e, this.checkAvm1Timeout); + }; + return a; + }(), Ya = function() { + function a() { + } + a.get = function() { + return h.avm1TraceEnabled.value ? a.tracer : a.nullTracer; + }; + a.tracer = function() { + var a = 0; + return{print:function(b, d) { + for (var c = b.position, e = b.actionCode, g = b.actionName, f = [], k = 0;k < d.length;k++) { + var h = d[k]; + h && "object" === typeof h ? (h = h.asGetPublicProperty("__constructor__"), f.push("[" + (h ? h.name : "Object") + "]")) : f.push(h); + } + console.log("AVM1 trace: " + Array(a + 1).join("..") + c + ": " + g + "(" + e.toString(16) + "), stack=" + f); + }, indent:function() { + a++; + }, unindent:function() { + a--; + }, message:function(a) { + console.log("AVM1 trace: ------- " + a); + }}; + }(); + a.nullTracer = {print:function(a, b) { + }, indent:function() { + }, unindent:function() { + }, message:function(a) { + }}; + return a; + }(); + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + function s(a, c, d) { + return a.utils.hasProperty(c, d) ? !0 : (c = a.utils.getProperty(c, "_listeners")) ? c.some(function(b) { + return a.utils.hasProperty(b, d); + }) : !1; + } + function v(a, e, d, b) { + void 0 === b && (b = null); + var g = a.utils.getProperty(e, d); + c.isFunction(g) && g.apply(e, b); + g = a.utils.getProperty(this, "_listeners"); + Array.isArray(g) && g.forEach(function(g) { + g = a.utils.getProperty(g, d); + c.isFunction(g) && g.apply(e, b); + }); } - b._as2Interfaces = d; - } - function Hb(a, b) { - var c = b[5], d = b[3], h = b[1], e = b[2], f = b[4], g = b[6], k = a.context, l = a.scopeContainer, m = a.scope, n = a.constantPool, r = a.registers, p = k.isTryCatchListening, x; - try { - k.isTryCatchListening = !0, za(e, l, n, r); - } catch (q) { - k.isTryCatchListening = p, d && q instanceof Ka ? ("string" === typeof h ? m.asSetPublicProperty(h, q.error) : r[h] = q.error, za(f, l, n, r)) : x = q; + function p(a, e, d) { + for (var b = a.prototype;b && !b.initAVM1SymbolInstance;) { + b = b.asGetPublicProperty("__proto__"); + } + c.Debug.assert(b); + b = Object.create(b); + b.asSetPublicProperty("__proto__", a.asGetPublicProperty("prototype")); + b.asDefinePublicProperty("__constructor__", {value:a, writable:!0, enumerable:!1, configurable:!1}); + e._as2Object = b; + b.initAVM1SymbolInstance(d, e); + a.call(b); + return b; } - k.isTryCatchListening = p; - c && za(g, l, n, r); - if (x) { - throw x; + function u(a, c) { + return a ? a._as2Object ? a._as2Object : m.display.MovieClip.isType(a) ? a._avm1SymbolClass ? p(a._avm1SymbolClass, a, c) : p(c.globals.MovieClip, a, c) : m.display.SimpleButton.isType(a) ? p(c.globals.Button, a, c) : m.text.TextField.isType(a) ? p(c.globals.TextField, a, c) : m.display.BitmapData.isType(a) ? new a : null : null; } - } - function Ib(a) { - a = a.stack.pop(); - throw new Ka(a); - } - function Jb(a) { - var b = a.stack, c = a.global, d = E(b); - a = b.length; - b.push(void 0); - c = c.fscommand.apply(null, d); - b[a] = c; - } - function Pb(a, b) { - } - function Qb(a) { - return function(c, d) { - var h; - try { - a(c, d), c.recoveringFromError = !1; - } catch (e) { - h = c.context; - e = w(e); - if (e instanceof ua) { - throw e; - } - if (e instanceof Ka) { - throw e; - } - Kb.instance.reportTelemetry({topic:"error", error:1}); - if (!c.recoveringFromError) { - if (1E3 <= h.errorsIgnored++) { - throw new ua("long running script -- AVM1 errors limit is reached"); + function l(a, e) { + var d; + "function" === typeof a ? (d = function() { + return a.apply(this, arguments); + }, Object.getOwnPropertyNames(a).forEach(function(b) { + d.hasOwnProperty(b) || Object.defineProperty(d, b, Object.getOwnPropertyDescriptor(a, b)); + })) : (c.Debug.assert("object" === typeof a && null !== a), d = Object.create(a)); + if (!e) { + return d; + } + e.forEach(function(a) { + var c = a, e = a.indexOf("=>"); + 0 <= e && (c = a.substring(0, e), a = a.substring(e + 2)); + d.asDefinePublicProperty(c, {get:function() { + return this[a]; + }, set:function(d) { + this[a] = d; + }, enumerable:!1, configurable:!0}); + }); + return d; + } + function e(a, c) { + return c.context.executeActions(a, c); + } + var m = c.AVM2.AS.flash, t = function() { + function a(c, d, b) { + void 0 === b && (b = null); + this.propertyName = c; + this.eventName = d; + this.argsConverter = b; + } + a.prototype.onBind = function(a) { + }; + a.prototype.onUnbind = function(a) { + }; + return a; + }(); + a.AVM1EventHandler = t; + t = function() { + function a() { + } + Object.defineProperty(a.prototype, "context", {get:function() { + return this._context; + }, enumerable:!0, configurable:!0}); + a.prototype.initAVM1ObjectInstance = function(a) { + this._context = a; + }; + return a; + }(); + a.AVM1NativeObject = t; + t = function(a) { + function c() { + a.apply(this, arguments); + } + __extends(c, a); + Object.defineProperty(c.prototype, "isAVM1Instance", {get:function() { + return!!this._as3Object; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "as3Object", {get:function() { + return this._as3Object; + }, enumerable:!0, configurable:!0}); + c.prototype.initAVM1SymbolInstance = function(a, b) { + this.initAVM1ObjectInstance(a); + this._as3Object = b; + }; + c.prototype.bindEvents = function(a) { + var b; + void 0 === b && (b = !0); + this._events = a; + var c = Object.create(null); + this._eventsMap = c; + this._eventsListeners = Object.create(null); + var e = this, f = this.context; + a.forEach(function(a) { + c[a.propertyName] = a; + f.registerEventPropertyObserver(a.propertyName, e); + e._updateEvent(a); + }); + b && e.as3Object.addEventListener("removedFromStage", function A() { + e.as3Object.removeEventListener("removedFromStage", A); + e.unbindEvents(); + }); + }; + c.prototype.unbindEvents = function() { + var a = this, b = this.context; + this._events.forEach(function(c) { + b.unregisterEventPropertyObserver(c.propertyName, a); + a._removeEventListener(c); + }); + this._eventsListeners = this._eventsMap = this._events = null; + }; + c.prototype.updateAllEvents = function() { + this._events.forEach(function(a) { + this._updateEvent(a); + }, this); + }; + c.prototype._updateEvent = function(a) { + s(this.context, this, a.propertyName) ? this._addEventListener(a) : this._removeEventListener(a); + }; + c.prototype._addEventListener = function(a) { + var b = this._eventsListeners[a.propertyName]; + b || (b = function() { + var b = a.argsConverter ? a.argsConverter.apply(null, arguments) : null; + v(this.context, this, a.propertyName, b); + }.bind(this), this.as3Object.addEventListener(a.eventName, b), a.onBind(this), this._eventsListeners[a.propertyName] = b); + }; + c.prototype._removeEventListener = function(a) { + var b = this._eventsListeners[a.propertyName]; + b && (this.as3Object.removeEventListener(a.eventName, b), this._eventsListeners[a.propertyName] = null); + }; + c.prototype.onEventPropertyModified = function(a) { + this._updateEvent(this._eventsMap[a]); + }; + return c; + }(t); + a.AVM1SymbolBase = t; + a.avm1HasEventProperty = s; + a.avm1BroadcastEvent = v; + t = function() { + function a() { + } + a.addProperty = function(a, d, b, c, e) { + void 0 === e && (e = !0); + a.asDefinePublicProperty(d, {get:b, set:c || void 0, enumerable:e, configurable:!0}); + }; + a.resolveTarget = function(a) { + void 0 === a && (a = void 0); + return h.AVM1Context.instance.resolveTarget(a); + }; + a.resolveMovieClip = function(a) { + void 0 === a && (a = void 0); + return a ? h.AVM1Context.instance.resolveTarget(a) : void 0; + }; + a.resolveLevel = function(a) { + return h.AVM1Context.instance.resolveLevel(+a); + }; + Object.defineProperty(a, "currentStage", {get:function() { + return h.AVM1Context.instance.root.as3Object.stage; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(a, "swfVersion", {get:function() { + return h.AVM1Context.instance.loaderInfo.swfVersion; + }, enumerable:!0, configurable:!0}); + a.getTarget = function(a) { + a = a.as3Object; + if (a === a.root) { + return "/"; + } + var d = ""; + do { + d = "/" + a.name + d, a = a.parent; + } while (a !== a.root); + return d; + }; + return a; + }(); + a.AVM1Utils = t; + a.getAVM1Object = u; + a.wrapAVM1Object = l; + a.wrapAVM1Class = function(a, c, d) { + c = l(a, c); + a = l(a.prototype, d); + c.asSetPublicProperty("prototype", a); + return c; + }; + var q = !1; + a.installObjectMethods = function() { + if (!q) { + q = !0; + var a = c.AVM2.AS.ASObject, e = a.asGetPublicProperty("prototype"); + a.asSetPublicProperty("registerClass", function(a, b) { + h.AVM1Context.instance.registerClass(a, b); + }); + e.asDefinePublicProperty("addProperty", {value:function(a, b, c) { + if ("string" !== typeof a || "" === a || "function" !== typeof b || "function" !== typeof c && null !== c) { + return!1; + } + this.asDefinePublicProperty(a, {get:b, set:c || void 0, configurable:!0, enumerable:!0}); + return!0; + }, writable:!1, enumerable:!1, configurable:!1}); + Object.defineProperty(e, "__proto__avm1", {value:null, writable:!0, enumerable:!1}); + e.asDefinePublicProperty("__proto__", {get:function() { + return this.__proto__avm1; + }, set:function(a) { + if (a !== this.__proto__avm1) { + if (a) { + for (var b = a;b;) { + if (b === this) { + return; + } + b = b.__proto__avm1; + } + this.__proto__avm1 = a; + } else { + this.__proto__avm1 = null; + } + } + }, enumerable:!1, configurable:!1}); + } + }; + a.initializeAVM1Object = function(a, f, d) { + f = u(a, f); + c.Debug.assert(f); + d.variableName && f.asSetPublicProperty("variable", d.variableName); + var b = d.events; + if (b) { + for (var g = 0;g < b.length;g++) { + var l = b[g], m; + l.actionsData ? (m = new h.AVM1ActionsData(l.actionsData, "s" + d.symbolId + "e" + g), l.actionsData = null, l.compiled = m) : m = l.compiled; + c.Debug.assert(m); + m = e.bind(null, m, f); + var l = l.flags, p; + for (p in n) { + if (p |= 0, l & (p | 0)) { + var q = n[p]; + switch(p) { + case 2048: + ; + case 4096: + ; + case 8192: + ; + case 16384: + a.buttonMode = !0; + } + a.addEventListener(q, m); + } + } + } + } + }; + var n = Object.create(null); + n[1] = "load"; + n[2] = "frameConstructed"; + n[4] = "unload"; + n[8] = "mouseMove"; + n[16] = "mouseDown"; + n[32] = "mouseUp"; + n[64] = "keyDown"; + n[128] = "keyUp"; + n[256] = {toString:function() { + c.Debug.warning("Data ClipEvent not implemented"); + }}; + n[512] = "initialize"; + n[1024] = "mouseDown"; + n[2048] = "click"; + n[4096] = "releaseOutside"; + n[8192] = "mouseOver"; + n[16384] = "mouseOut"; + n[32768] = {toString:function() { + c.Debug.warning("DragOver ClipEvent not implemented"); + }}; + n[65536] = {toString:function() { + c.Debug.warning("DragOut ClipEvent not implemented"); + }}; + n[131072] = {toString:function() { + c.Debug.warning("KeyPress ClipEvent not implemented"); + }}; + n[262144] = "construct"; + })(h.Lib || (h.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.Debug.notImplemented, v = c.Debug.somewhatImplemented, p = c.AVM2.Runtime.forEachPublicProperty, u = c.Debug.assert, l = c.AVM2.AS.flash, e = jsGlobal.escape, m = [], t = function() { + function n(e) { + this.fscommand = l.system.FSCommand._fscommand; + this.getTimer = c.AVM2.AS.FlashUtilScript_getTimer; + this.NaN = Number.NaN; + this.Infinity = Number.POSITIVE_INFINITY; + this.isFinite = isFinite; + this.isNaN = isNaN; + this.parseFloat = parseFloat; + this.parseInt = parseInt; + this.Object = c.AVM2.AS.ASObject; + this.Function = c.AVM2.AS.ASFunction; + this.Array = c.AVM2.AS.ASArray; + this.Number = c.AVM2.AS.ASNumber; + this.Math = c.AVM2.AS.ASMath; + this.Boolean = c.AVM2.AS.ASBoolean; + this.Date = c.AVM2.AS.ASDate; + this.RegExp = c.AVM2.AS.ASRegExp; + this.String = c.AVM2.AS.ASString; + this.undefined = void 0; + this.MovieClip = a.AVM1MovieClip.createAVM1Class(); + this.AsBroadcaster = a.AVM1Broadcaster.createAVM1Class(); + this.System = a.AVM1System.createAVM1Class(); + this.Stage = a.AVM1Stage.createAVM1Class(); + this.Button = a.AVM1Button.createAVM1Class(); + this.TextField = a.AVM1TextField.createAVM1Class(); + this.Color = a.AVM1Color.createAVM1Class(); + this.Key = a.AVM1Key.createAVM1Class(); + this.Mouse = a.AVM1Mouse.createAVM1Class(); + this.MovieClipLoader = a.AVM1MovieClipLoader.createAVM1Class(); + this.Sound = a.AVM1Sound.createAVM1Class(); + this.SharedObject = l.net.SharedObject; + this.ContextMenu = l.ui.ContextMenu; + this.ContextMenuItem = l.ui.ContextMenuItem; + this.TextFormat = a.AVM1TextFormat.createAVM1Class(); + n.instance = this; + this._global = this; + "Object Function Array Number Math Boolean Date RegExp String".split(" ").forEach(function(a) { + c.AVM2.Runtime.AVM2.instance.systemDomain.getClass(a); + }); + 8 <= e.loaderInfo.swfVersion && this._initializeFlashObject(); + this.AsBroadcaster.initializeWithContext(this.Stage, e); + this.AsBroadcaster.initializeWithContext(this.Key, e); + this.AsBroadcaster.initializeWithContext(this.Mouse, e); + } + n.createAVM1Class = function() { + return a.wrapAVM1Class(n, [], "_global flash ASSetPropFlags call chr clearInterval clearTimeout duplicateMovieClip fscommand escape unescape getTimer getURL getVersion gotoAndPlay gotoAndStop ifFrameLoaded int length=>length_ loadMovie loadMovieNum loadVariables loadVariablesNum mbchr mblength mbord mbsubstring nextFrame nextScene ord play prevFrame prevScene print printAsBitmap printAsBitmapNum printNum random removeMovieClip setInterval setTimeout showRedrawRegions startDrag stop stopDrag substring targetPath toggleHighQuality trace unloadMovie unloadMovieNum updateAfterEvent NaN Infinity isFinite isNaN parseFloat parseInt undefined Object Function Array Number Math Boolean Date RegExp String MovieClip AsBroadcaster System Stage Button TextField Color Key Mouse MovieClipLoader Sound SharedObject ContextMenu ContextMenuItem TextFormat toString $asfunction=>asfunction".split(" ")); + }; + n.prototype.asfunction = function(a) { + s("AVM1Globals.$asfunction"); + }; + n.prototype.ASSetPropFlags = function(a, c, d, b) { + }; + n.prototype.call = function(c) { + var e = a.AVM1Utils.resolveTarget().as3Object; + c = e._getAbsFrameNumber(c, null); + void 0 !== c && e.callFrame(c); + }; + n.prototype.chr = function(a) { + return String.fromCharCode(a); + }; + n.prototype.clearInterval = function(a) { + var c = m[a - 1]; + c && (clearInterval(c), delete m[a - 1]); + }; + n.prototype.clearTimeout = function(a) { + var c = m[a - 1]; + c && (clearTimeout(c), delete m[a - 1]); + }; + n.prototype.duplicateMovieClip = function(c, e, d) { + a.AVM1Utils.resolveTarget(c).duplicateMovieClip(e, d, null); + }; + n.prototype.getAVM1Property = function(c, e) { + return a.AVM1Utils.resolveTarget(c)[q[e]]; + }; + n.prototype.getURL = function(a, e, d) { + var b = new l.net.URLRequest(String(a)); + d && (b.method = d); + "string" === typeof e && 0 === e.indexOf("_level") ? this.loadMovieNum(a, +e.substr(6), d) : c.AVM2.AS.FlashNetScript_navigateToURL(b, e); + }; + n.prototype.getVersion = function() { + return l.system.Capabilities.version; + }; + n.prototype._addToPendingScripts = function(a, c, d) { + void 0 === d && (d = null); + u(c, "invalid function in _addToPendingScripts"); + var b = h.AVM1Context.instance, e = b.resolveTarget(void 0); + b.addToPendingScripts(function() { + b.enterContext(function() { + try { + c.apply(a, d); + } catch (b) { + console.error("AVM1 pending script error: " + b.message); + } + }, e); + }); + }; + n.prototype.escape = function(a) { + return encodeURIComponent(a).replace(/!|'|\(|\)|\*|-|\.|_|~/g, function(a) { + switch(a) { + case "*": + return "%2A"; + case "-": + return "%2D"; + case ".": + return "%2E"; + case "_": + return "%5F"; + default: + return e(a); + } + }); + }; + n.prototype.unescape = function(a) { + return decodeURIComponent(a); + }; + n.prototype.gotoAndPlay = function(c, e) { + var d = a.AVM1Utils.resolveTarget(); + 2 > arguments.length ? this._addToPendingScripts(d, d.gotoAndPlay, [arguments[0]]) : this._addToPendingScripts(d, d.gotoAndPlay, [arguments[1], arguments[0]]); + }; + n.prototype.gotoAndStop = function(c, e) { + var d = a.AVM1Utils.resolveTarget(); + 2 > arguments.length ? this._addToPendingScripts(d, d.gotoAndStop, [arguments[0]]) : this._addToPendingScripts(d, d.gotoAndStop, [arguments[1], arguments[0]]); + }; + n.prototype.ifFrameLoaded = function(c, e) { + var d = a.AVM1Utils.resolveTarget(), b = d._framesloaded; + return Math.min((2 > arguments.length ? arguments[0] : arguments[1]) + 1, d._totalframes) <= b; + }; + n.prototype.int = function(a) { + return a | 0; + }; + n.prototype.length_ = function(a) { + return("" + a).length; + }; + n.prototype.loadMovie = function(c, e, d) { + if (c && 0 === c.toLowerCase().indexOf("fscommand:")) { + this.fscommand(c.substring(10), e); + } else { + var b = "string" === typeof e && 0 === e.indexOf("_level"), g; + b && (b = e.charAt(6), g = parseInt(b, 10), b = g.toString() === b); + g = new l.display.Loader; + b ? (c = new l.net.URLRequest(c), d && (c.method = d), g.load(c)) : a.AVM1Utils.resolveTarget(e).loadMovie(c, d); + } + }; + n.prototype._setLevel = function(a, c) { + }; + n.prototype.loadMovieNum = function(a, c, d) { + if (a && 0 === a.toLowerCase().indexOf("fscommand:")) { + return this.fscommand(a.substring(10)); + } + c = new l.display.Loader; + a = new l.net.URLRequest(a); + d && (a.method = d); + c.load(a); + }; + n.prototype.loadVariables = function(c, e, d) { + void 0 === d && (d = ""); + e = a.AVM1Utils.resolveTarget(e); + this._loadVariables(e, c, d); + }; + n.prototype.loadVariablesNum = function(c, e, d) { + void 0 === d && (d = ""); + this._loadVariables(a.AVM1Utils.resolveLevel(e), c, d); + }; + n.prototype._loadVariables = function(e, f, d) { + function b(d) { + m.removeEventListener(l.events.Event.COMPLETE, b); + c.Debug.assert("object" === typeof m.data); + p(m.data, function(a, b) { + g.utils.setProperty(e, a, b); + }); + e instanceof a.AVM1MovieClip && a.avm1BroadcastEvent(g, e, "onData"); + } + f = new l.net.URLRequest(f); + d && (f.method = d); + var g = h.AVM1Context.instance, m = new l.net.URLLoader(f); + m._ignoreDecodeErrors = !0; + m.dataFormat = "variables"; + m.addEventListener(l.events.Event.COMPLETE, b); + }; + n.prototype.mbchr = function(a) { + return String.fromCharCode(a); + }; + n.prototype.mblength = function(a) { + return("" + a).length; + }; + n.prototype.mbord = function(a) { + return("" + a).charCodeAt(0); + }; + n.prototype.mbsubstring = function(a, c, d) { + return c !== (0 | c) || d !== (0 | d) ? "" : ("" + a).substr(c, d); + }; + n.prototype.nextFrame = function() { + var c = a.AVM1Utils.resolveTarget(); + this._addToPendingScripts(c, c.nextFrame); + }; + n.prototype.nextScene = function() { + var c = a.AVM1Utils.resolveTarget(); + this._addToPendingScripts(c, c.nextScene); + }; + n.prototype.ord = function(a) { + return("" + a).charCodeAt(0); + }; + n.prototype.play = function() { + a.AVM1Utils.resolveTarget().play(); + }; + n.prototype.prevFrame = function() { + var c = a.AVM1Utils.resolveTarget(); + this._addToPendingScripts(c, c.prevFrame); + }; + n.prototype.prevScene = function() { + var c = a.AVM1Utils.resolveTarget(); + this._addToPendingScripts(c, c.prevScene); + }; + n.prototype.print = function(a, c) { + s("AVM1Globals.print"); + }; + n.prototype.printAsBitmap = function(a, c) { + s("AVM1Globals.printAsBitmap"); + }; + n.prototype.printAsBitmapNum = function(a, c) { + s("AVM1Globals.printAsBitmapNum"); + }; + n.prototype.printNum = function(a, c) { + s("AVM1Globals.printNum"); + }; + n.prototype.random = function(a) { + return 0 | Math.random() * (0 | a); + }; + n.prototype.removeMovieClip = function(c) { + a.AVM1Utils.resolveTarget(c).removeMovieClip(); + }; + n.prototype.setInterval = function() { + if (!(2 > arguments.length)) { + var a = []; + if ("function" === typeof arguments[0]) { + a = arguments; + } else { + if (3 > arguments.length) { + return; + } + var c = arguments[0], d = arguments[1]; + if (!c || "object" !== typeof c || "string" !== typeof d) { + return; + } + a[0] = function() { + c.asCallPublicProperty(d, arguments); + }; + for (var b = 2;b < arguments.length;b++) { + a.push(arguments[b]); + } + } + a[1] |= 0; + a = setInterval.apply(null, a); + return m.push(a); + } + }; + n.prototype.setAVM1Property = function(c, e, d) { + a.AVM1Utils.resolveTarget(c)[q[e]] = d; + }; + n.prototype.setTimeout = function() { + if (!(2 > arguments.length || "function" !== typeof arguments[0])) { + arguments[1] |= 0; + var a = setTimeout.apply(null, arguments); + return m.push(a); + } + }; + n.prototype.showRedrawRegions = function(a, c) { + s("AVM1Globals.showRedrawRegions"); + }; + n.prototype.startDrag = function(c, e, d, b, g, h) { + a.AVM1Utils.resolveTarget(c).startDrag(e, 3 > arguments.length ? null : new l.geom.Rectangle(d, b, g - d, h - b)); + }; + n.prototype.stop = function() { + a.AVM1Utils.resolveTarget().stop(); + }; + n.prototype.stopAllSounds = function() { + l.media.SoundMixer.stopAll(); + }; + n.prototype.stopDrag = function(c) { + a.AVM1Utils.resolveTarget(c).stopDrag(); + }; + n.prototype.substring = function(a, c, d) { + return this.mbsubstring(a, c, d); + }; + n.prototype.targetPath = function(c) { + return a.AVM1Utils.resolveTarget(c)._target; + }; + n.prototype.toggleHighQuality = function() { + s("AVM1Globals.toggleHighQuality"); + }; + n.prototype.trace = function(a) { + c.AVM2.AS.Natives.print(a); + }; + n.prototype.unloadMovie = function(c) { + a.AVM1Utils.resolveTarget(c).unloadMovie(); + }; + n.prototype.unloadMovieNum = function(c) { + a.AVM1Utils.resolveLevel(c).unloadMovie(); + }; + n.prototype.updateAfterEvent = function() { + v("AVM1Globals.updateAfterEvent"); + }; + n.prototype._initializeFlashObject = function() { + this.flash = {}; + this.flash.asSetPublicProperty("_MovieClip", this.MovieClip); + var c = {}; + c.asSetPublicProperty("BitmapData", a.AVM1BitmapData.createAVM1Class()); + this.flash.asSetPublicProperty("display", c); + c = {}; + c.asSetPublicProperty("ExternalInterface", a.AVM1ExternalInterface.createAVM1Class()); + this.flash.asSetPublicProperty("external", c); + this.flash.asSetPublicProperty("filters", {}); + c = {}; + c.asSetPublicProperty("ColorTransform", l.geom.ColorTransform); + c.asSetPublicProperty("Matrix", l.geom.Matrix); + c.asSetPublicProperty("Point", l.geom.Point); + c.asSetPublicProperty("Rectangle", l.geom.Rectangle); + c.asSetPublicProperty("Transform", a.AVM1Transform.createAVM1Class()); + this.flash.asSetPublicProperty("geom", c); + this.flash.asSetPublicProperty("text", {}); + }; + n.prototype.toString = function() { + return "[type Object]"; + }; + return n; + }(); + a.AVM1Globals = t; + var q = "_x _y _xscale _yscale _currentframe _totalframes _alpha _visible _width _height _rotation _target _framesloaded _name _droptarget _url _highquality _focusrect _soundbuftime _quality _xmouse _ymouse".split(" "); + })(h.Lib || (h.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + (function(a) { + var s = function() { + function s() { + } + s.setAVM1Context = function(a) { + this._context = a; + }; + s.createAVM1Class = function() { + return a.wrapAVM1Class(s, ["initialize"], []); + }; + s.initialize = function(a) { + s.initializeWithContext(a, c.AVM1Context.instance); + }; + s.initializeWithContext = function(c, h) { + c.asSetPublicProperty("_listeners", []); + c.asSetPublicProperty("broadcastMessage", function(c) { + for (var e = [], m = 1;m < arguments.length;m++) { + e[m - 1] = arguments[m]; + } + a.avm1BroadcastEvent(h, this, c, e); + }); + c.asSetPublicProperty("addListener", function(a) { + h.utils.getProperty(this, "_listeners").push(a); + this.isAVM1Instance && this.updateAllEvents(); + }); + c.asSetPublicProperty("removeListener", function(a) { + var c = h.utils.getProperty(this, "_listeners"); + a = c.indexOf(a); + if (0 > a) { + return!1; + } + c.splice(a, 1); + this.isAVM1Instance && this.updateAllEvents(); + return!0; + }); + }; + return s; + }(); + a.AVM1Broadcaster = s; + })(c.Lib || (c.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + (function(a) { + var c = function() { + function c() { + } + c.createAVM1Class = function() { + return a.wrapAVM1Class(c, ["DOWN", "LEFT", "RIGHT", "UP", "isDown"], []); + }; + c._bind = function(a, h) { + a.addEventListener("keyDown", function(a) { + a = a.asGetPublicProperty("keyCode"); + c._lastKeyCode = a; + c._keyStates[a] = 1; + h.globals.Key.asCallPublicProperty("broadcastMessage", ["onKeyDown"]); + }, !1); + a.addEventListener("keyUp", function(a) { + a = a.asGetPublicProperty("keyCode"); + c._lastKeyCode = a; + delete c._keyStates[a]; + h.globals.Key.asCallPublicProperty("broadcastMessage", ["onKeyUp"]); + }, !1); + }; + c.isDown = function(a) { + return!!c._keyStates[a]; + }; + c.DOWN = 40; + c.LEFT = 37; + c.RIGHT = 39; + c.UP = 38; + c._keyStates = []; + c._lastKeyCode = 0; + return c; + }(); + a.AVM1Key = c; + })(c.Lib || (c.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + (function(a) { + var c = function() { + function c() { + } + c.createAVM1Class = function() { + return a.wrapAVM1Class(c, ["show", "hide"], []); + }; + c._bind = function(a, c) { + a.addEventListener("mouseDown", function(a) { + c.globals.Mouse.asCallPublicProperty("broadcastMessage", ["onMouseDown"]); + }, !1); + a.addEventListener("mouseMove", function(a) { + c.globals.Mouse.asCallPublicProperty("broadcastMessage", ["onMouseMove"]); + }, !1); + a.addEventListener("mouseOut", function(a) { + c.globals.Mouse.asCallPublicProperty("broadcastMessage", ["onMouseMove"]); + }, !1); + a.addEventListener("mouseUp", function(a) { + c.globals.Mouse.asCallPublicProperty("broadcastMessage", ["onMouseUp"]); + }, !1); + }; + c.hide = function() { + }; + c.show = function() { + }; + return c; + }(); + a.AVM1Mouse = c; + })(c.Lib || (c.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + (function(a) { + var c = function() { + function c() { + } + c.createAVM1Class = function() { + return a.wrapAVM1Class(c, "align displayState fullScreenSourceRect height scaleMode showMenu width".split(" "), []); + }; + Object.defineProperty(c, "align", {get:function() { + return a.AVM1Utils.currentStage.align; + }, set:function(c) { + a.AVM1Utils.currentStage.align = c; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c, "displayState", {get:function() { + return a.AVM1Utils.currentStage.displayState; + }, set:function(c) { + a.AVM1Utils.currentStage.displayState = c; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c, "fullScreenSourceRect", {get:function() { + return a.AVM1Utils.currentStage.fullScreenSourceRect; + }, set:function(c) { + a.AVM1Utils.currentStage.fullScreenSourceRect = c; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c, "height", {get:function() { + return a.AVM1Utils.currentStage.stageHeight; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c, "scaleMode", {get:function() { + return a.AVM1Utils.currentStage.scaleMode; + }, set:function(c) { + a.AVM1Utils.currentStage.scaleMode = c; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c, "showMenu", {get:function() { + return a.AVM1Utils.currentStage.showDefaultContextMenu; + }, set:function(c) { + a.AVM1Utils.currentStage.showDefaultContextMenu = c; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c, "width", {get:function() { + return a.AVM1Utils.currentStage.stageWidth; + }, enumerable:!0, configurable:!0}); + return c; + }(); + a.AVM1Stage = c; + })(c.Lib || (c.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.AVM2.AS.flash, v = c.Debug.assert, p = c.AVM2.ABC.Multiname, u = c.AVM2.Runtime.resolveMultinameProperty, l = Object.prototype.asGetProperty, e = Object.prototype.asHasProperty, m = Object.prototype.asGetEnumerableKeys, t = function(a) { + function c(e, d, b) { + void 0 === b && (b = null); + a.call(this, e, d, b); + this.propertyName = e; + this.eventName = d; + this.argsConverter = b; + } + __extends(c, a); + c.prototype.onBind = function(a) { + a.as3Object.buttonMode = !0; + }; + return c; + }(a.AVM1EventHandler), q = function(n) { + function k() { + n.call(this); + } + __extends(k, n); + k.createAVM1Class = function() { + return a.wrapAVM1Class(k, [], "_alpha attachAudio attachBitmap attachMovie beginFill beginBitmapFill beginGradientFill blendMode cacheAsBitmap _callFrame clear createEmptyMovieClip createTextField _currentframe curveTo _droptarget duplicateMovieClip enabled endFill filters _framesloaded focusEnabled _focusrect forceSmothing getBounds getBytesLoaded getBytesTotal getDepth getInstanceAtDepth getNextHighestDepth getRect getSWFVersion getTextSnapshot getURL globalToLocal gotoAndPlay gotoAndStop _height _highquality hitArea hitTest lineGradientStyle listStyle lineTo loadMovie loadVariables localToGlobal _lockroot menu moveTo _name nextFrame opaqueBackground _parent play prevFrame _quality removeMovieClip _rotation scale9Grid scrollRect setMask _soundbuftime startDrag stop stopDrag swapDepths tabChildren tabEnabled tabIndex _target _totalframes trackAsMenu transform toString unloadMovie _url useHandCursor _visible _width _x _xmouse _xscale _y _ymouse _yscale".split(" ")); + }; + Object.defineProperty(k.prototype, "graphics", {get:function() { + return this.as3Object.graphics; + }, enumerable:!0, configurable:!0}); + k.prototype.initAVM1SymbolInstance = function(a, d) { + n.prototype.initAVM1SymbolInstance.call(this, a, d); + this._boundExecuteFrameScripts = this._frameScripts = null; + this._initEventsHandlers(); + }; + k.prototype.__lookupChild = function(c) { + return "." == c ? this : ".." == c ? a.getAVM1Object(this.as3Object.parent, this.context) : a.getAVM1Object(this._lookupChildByName(c), this.context); + }; + k.prototype._lookupChildByName = function(a) { + a = asCoerceString(a); + return this.as3Object._lookupChildByName(a); + }; + Object.defineProperty(k.prototype, "__targetPath", {get:function() { + var a = this._target; + return "/" != a ? "_level0" + a.replace(/\//g, ".") : "_level0"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "_alpha", {get:function() { + return this.as3Object.alpha; + }, set:function(a) { + this.as3Object.alpha = a; + }, enumerable:!0, configurable:!0}); + k.prototype.attachAudio = function(a) { + throw "Not implemented: attachAudio"; + }; + k.prototype._constructMovieClipSymbol = function(a, d) { + var b = h.AVM1Context.instance.getAsset(a); + if (b) { + var c = Object.create(b.symbolProps); + c.avm1Name = d; + c.avm1SymbolClass = b.theClass; + b = s.display.MovieClip.initializeFrom(c); + s.display.MovieClip.instanceConstructorNoInitialize.call(b); + return b; + } + }; + k.prototype.attachMovie = function(a, d, b, c) { + if (a = this._constructMovieClipSymbol(a, d)) { + b = this._insertChildAtDepth(a, b); + for (var e in c) { + b[e] = c[e]; + } + return b; + } + }; + k.prototype.beginFill = function(a, d) { + this.graphics.beginFill(a, d); + }; + k.prototype.beginBitmapFill = function(a, d, b, c) { + a instanceof s.display.BitmapData && this.graphics.beginBitmapFill(a, d, b, c); + }; + k.prototype.beginGradientFill = function(a, d, b, c, e, k, h, l) { + this.graphics.beginGradientFill(a, d, b, c, e, k, h, l); + }; + Object.defineProperty(k.prototype, "blendMode", {get:function() { + return this.as3Object.blendMode; + }, set:function(a) { + this.as3Object.blendMode = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "cacheAsBitmap", {get:function() { + return this.as3Object.cacheAsBitmap; + }, set:function(a) { + this.as3Object.cacheAsBitmap = a; + }, enumerable:!0, configurable:!0}); + k.prototype._callFrame = function(a) { + }; + k.prototype.clear = function() { + this.graphics.clear(); + }; + k.prototype._insertChildAtDepth = function(c, d) { + var b = this.as3Object; + b.addTimelineObjectAtDepth(c, Math.min(b.numChildren, d)); + return s.display.Bitmap.isType(c) ? null : a.getAVM1Object(c, this.context); + }; + k.prototype.createEmptyMovieClip = function(a, d) { + var b = new s.display.MovieClip; + b.name = a; + return this._insertChildAtDepth(b, d); + }; + k.prototype.createTextField = function(a, d, b, c, e, k) { + var h = new s.text.TextField; + h.name = a; + h.x = b; + h.y = c; + h.width = e; + h.height = k; + return this._insertChildAtDepth(h, d); + }; + Object.defineProperty(k.prototype, "_currentframe", {get:function() { + return this.as3Object.currentFrame; + }, enumerable:!0, configurable:!0}); + k.prototype.curveTo = function(a, d, b, c) { + this.graphics.curveTo(a, d, b, c); + }; + Object.defineProperty(k.prototype, "_droptarget", {get:function() { + return this.as3Object.dropTarget; + }, enumerable:!0, configurable:!0}); + k.prototype._duplicate = function(a, d, b) { + }; + k.prototype.duplicateMovieClip = function(c, d, b) { + return a.getAVM1Object(this._duplicate(c, +d, b), this.context); + }; + Object.defineProperty(k.prototype, "enabled", {get:function() { + return this.as3Object.enabled; + }, set:function(a) { + this.as3Object.enabled = a; + }, enumerable:!0, configurable:!0}); + k.prototype.endFill = function() { + this.graphics.endFill(); + }; + Object.defineProperty(k.prototype, "filters", {get:function() { + throw "Not implemented: get$filters"; + }, set:function(a) { + throw "Not implemented: set$filters"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "focusEnabled", {get:function() { + throw "Not implemented: get$focusEnabled"; + }, set:function(a) { + throw "Not implemented: set$focusEnabled"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "_focusrect", {get:function() { + throw "Not implemented: get$_focusrect"; + }, set:function(a) { + throw "Not implemented: set$_focusrect"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "forceSmoothing", {get:function() { + throw "Not implemented: get$forceSmoothing"; + }, set:function(a) { + throw "Not implemented: set$forceSmoothing"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "_framesloaded", {get:function() { + return this.as3Object.framesLoaded; + }, enumerable:!0, configurable:!0}); + k.prototype.getBounds = function(a) { + a = a.as3Object; + if (!a) { + throw "Unsupported bounds type"; + } + return this.as3Object.getBounds(a); + }; + k.prototype.getBytesLoaded = function() { + return this.as3Object.loaderInfo.bytesLoaded; + }; + k.prototype.getBytesTotal = function() { + return this.as3Object.loaderInfo.bytesTotal; + }; + k.prototype.getDepth = function() { + return this.as3Object._depth; + }; + k.prototype.getInstanceAtDepth = function(c) { + for (var d = this.as3Object, b = 0, e = d.numChildren;b < e;b++) { + var k = d._lookupChildByIndex(b); + if (k && k._depth === c) { + return s.display.Bitmap.isType(k) ? this : a.getAVM1Object(k, this.context); } - console.log(typeof e); - console.log(Object.getPrototypeOf(e)); - console.log(Object.getPrototypeOf(Object.getPrototypeOf(e))); - console.error("AVM1 error: " + e); - b.AVM2.Runtime.AVM2.instance.exceptions.push({source:"avm1", message:e.message, stack:e.stack}); - c.recoveringFromError = !0; } - } - }; - } - function Rb() { - var a; - a = k.avm1ErrorsEnabled.value ? function(a) { - return a; - } : Qb; - return{ActionGotoFrame:a(A), ActionGetURL:a(B), ActionNextFrame:a(z), ActionPreviousFrame:a(P), ActionPlay:a(D), ActionStop:a(L), ActionToggleQuality:a(M), ActionStopSounds:a(V), ActionWaitForFrame:a(Q), ActionSetTarget:a(U), ActionGoToLabel:a(S), ActionPush:a(aa), ActionPop:a($), ActionAdd:a(ea), ActionSubtract:a(Z), ActionMultiply:a(v), ActionDivide:a(X), ActionEquals:a(ba), ActionLess:a(R), ActionAnd:a(H), ActionOr:a(Y), ActionNot:a(ga), ActionStringEquals:a(ca), ActionStringLength:a(ka), - ActionMBStringLength:a(W), ActionStringAdd:a(ha), ActionStringExtract:a(da), ActionMBStringExtract:a(ja), ActionStringLess:a(N), ActionToInteger:a(ma), ActionCharToAscii:a(T), ActionMBCharToAscii:a(oa), ActionAsciiToChar:a(pa), ActionMBAsciiToChar:a(na), ActionJump:a(fa), ActionIf:a(sa), ActionCall:a(xa), ActionGetVariable:a(la), ActionSetVariable:a(qa), ActionGetURL2:a(ia), ActionGotoFrame2:a(wa), ActionSetTarget2:a(Ia), ActionGetProperty:a(va), ActionSetProperty:a(ya), ActionCloneSprite:a(Ga), - ActionRemoveSprite:a(Ha), ActionStartDrag:a(Aa), ActionEndDrag:a(ra), ActionWaitForFrame2:a(ta), ActionTrace:a(Ba), ActionGetTime:a(Ma), ActionRandomNumber:a(Na), ActionCallFunction:a(Oa), ActionCallMethod:a(Pa), ActionConstantPool:a(Qa), ActionDefineFunction:a(Ra), ActionDefineLocal:a(Sa), ActionDefineLocal2:a(Ta), ActionDelete:a(Ua), ActionDelete2:a(Wa), ActionEnumerate:a(Xa), ActionEquals2:a(Ya), ActionGetMember:a(Za), ActionInitArray:a($a), ActionInitObject:a(ab), ActionNewMethod:a(bb), - ActionNewObject:a(cb), ActionSetMember:a(db), ActionTargetPath:a(eb), ActionWith:a(fb), ActionToNumber:a(gb), ActionToString:a(hb), ActionTypeOf:a(ib), ActionAdd2:a(jb), ActionLess2:a(kb), ActionModulo:a(lb), ActionBitAnd:a(mb), ActionBitLShift:a(nb), ActionBitOr:a(ob), ActionBitRShift:a(pb), ActionBitURShift:a(qb), ActionBitXor:a(rb), ActionDecrement:a(sb), ActionIncrement:a(tb), ActionPushDuplicate:a(ub), ActionReturn:a(vb), ActionStackSwap:a(wb), ActionStoreRegister:a(xb), ActionInstanceOf:a(yb), - ActionEnumerate2:a(zb), ActionStrictEquals:a(Ab), ActionGreater:a(Bb), ActionStringGreater:a(Cb), ActionDefineFunction2:a(Db), ActionExtends:a(Eb), ActionCastOp:a(Fb), ActionImplementsOp:a(Gb), ActionTry:a(Hb), ActionThrow:a(Ib), ActionFSCommand2:a(Jb), ActionStrictMode:a(Pb)}; - } - function za(a, c, d, h) { - var e = k.AVM1Context.instance; - if (!a.ir) { - var f = new k.ActionsDataStream(a.bytes, e.swfVersion), f = new k.ActionsDataParser(f); - f.dataId = a.id; - var g = new k.ActionsDataAnalyzer; - a.ir = g.analyze(f); - if (k.avm1CompilerEnabled.value) { - try { - var l = new Sb; - a.ir.compiled = l.generate(a.ir); - } catch (m) { - console.error("Unable to compile AVM1 function: " + m); + return null; + }; + k.prototype.getNextHighestDepth = function() { + for (var a = this.as3Object, d = 0, b = 0, c = a.numChildren;b < c;b++) { + var e = a._lookupChildByIndex(b); + e._depth > d && (d = e._depth); } - } - } - a = a.ir; - var f = a.compiled, l = [], n = 5 <= e.swfVersion, r = Va.get(), g = c.scope; - c = {context:e, global:e.globals, scopeContainer:c, scope:g, actionTracer:r, constantPool:d, registers:h, stack:l, isSwfVersion5:n, recoveringFromError:!1, isEndOfActions:!1}; - g._nativeAS3Object && g._nativeAS3Object._deferScriptExecution && (e.deferScriptExecution = !0); - if (f) { - return f(c); - } - d = 0; - e = e.abortExecutionAt; - if (k.avm1DebuggerEnabled.value && (k.Debugger.pause || k.Debugger.breakpoints[a.dataId])) { - debugger; - } - for (h = a.actions[0];h && !c.isEndOfActions;) { - if (0 === d++ % 1E3 && Date.now() >= e) { - throw new ua("long running script -- AVM1 instruction hang timeout"); - } - f = c; - g = n = void 0; - try { - var n = f, p = h.action, x = p.actionCode, q = p.args; - n.actionTracer.print(p, n.stack); - r = !1; - switch(x | 0) { - case 129: - A(n, q); - break; - case 131: - B(n, q); - break; - case 4: - z(n); - break; - case 5: - P(n); - break; - case 6: - D(n); - break; - case 7: - L(n); - break; - case 8: - M(n); - break; - case 9: - V(n); - break; - case 138: - r = Q(n, q); - break; - case 139: - U(n, q); - break; - case 140: - S(n, q); - break; - case 150: - aa(n, q); - break; - case 23: - $(n); - break; - case 10: - ea(n); - break; - case 11: - Z(n); - break; - case 12: - v(n); - break; - case 13: - X(n); - break; - case 14: - ba(n); - break; - case 15: - R(n); - break; - case 16: - H(n); - break; - case 17: - Y(n); - break; - case 18: - ga(n); - break; - case 19: - ca(n); - break; - case 20: - ka(n); - break; - case 49: - W(n); - break; - case 33: - ha(n); - break; - case 21: - da(n); - break; - case 53: - ja(n); - break; - case 41: - N(n); - break; - case 24: - ma(n); - break; - case 50: - T(n); - break; - case 54: - oa(n); - break; - case 51: - pa(n); - break; - case 55: - na(n); - break; - case 153: - break; - case 157: - r = sa(n, q); - break; - case 158: - xa(n); - break; - case 28: - la(n); - break; - case 29: - qa(n); - break; - case 154: - ia(n, q); - break; - case 159: - wa(n, q); - break; - case 32: - Ia(n); - break; - case 34: - va(n); - break; - case 35: - ya(n); - break; - case 36: - Ga(n); - break; - case 37: - Ha(n); - break; - case 39: - Aa(n); - break; - case 40: - ra(n); - break; - case 141: - r = ta(n, q); - break; - case 38: - Ba(n); - break; - case 52: - Ma(n); - break; - case 48: - Na(n); - break; - case 61: - Oa(n); - break; - case 82: - Pa(n); - break; - case 136: - Qa(n, q); - break; - case 155: - Ra(n, q); - break; - case 60: - Sa(n); - break; - case 65: - Ta(n); - break; - case 58: - Ua(n); - break; - case 59: - Wa(n); - break; - case 70: - Xa(n); - break; - case 73: - Ya(n); - break; - case 78: - Za(n); - break; - case 66: - $a(n); - break; - case 67: - ab(n); - break; - case 83: - bb(n); - break; - case 64: - cb(n); - break; - case 79: - db(n); - break; - case 69: - eb(n); - break; - case 148: - fb(n, q); - break; - case 74: - gb(n); - break; - case 75: - hb(n); - break; - case 68: - ib(n); - break; - case 71: - jb(n); - break; - case 72: - kb(n); - break; - case 63: - lb(n); - break; - case 96: - mb(n); - break; - case 99: - nb(n); - break; - case 97: - ob(n); - break; - case 100: - pb(n); - break; - case 101: - qb(n); - break; - case 98: - rb(n); - break; - case 81: - sb(n); - break; - case 80: - tb(n); - break; - case 76: - ub(n); - break; - case 62: - vb(n); - break; - case 77: - wb(n); - break; - case 135: - xb(n, q); - break; - case 84: - yb(n); - break; - case 85: - zb(n); - break; - case 102: - Ab(n); - break; - case 103: - Bb(n); - break; - case 104: - Cb(n); - break; - case 142: - Db(n, q); - break; - case 105: - Eb(n); - break; - case 43: - Fb(n); - break; - case 44: - Gb(n); - break; - case 143: - Hb(n, q); - break; - case 42: - Ib(n); - break; - case 45: - Jb(n); - break; - case 137: - break; - case 0: - n.isEndOfActions = !0; - break; - default: - throw Error("Unknown action code: " + x);; + return d + 1; + }; + k.prototype.getRect = function(a) { + throw "Not implemented: getRect"; + }; + k.prototype.getSWFVersion = function() { + return this.as3Object.loaderInfo.swfVersion; + }; + k.prototype.getTextSnapshot = function() { + throw "Not implemented: getTextSnapshot"; + }; + k.prototype.getURL = function(a, d, b) { + a = new s.net.URLRequest(a); + b && (a.method = b); + c.AVM2.AS.FlashNetScript_navigateToURL(a, d); + }; + k.prototype.globalToLocal = function(a) { + var d = this.as3Object.globalToLocal(new s.geom.Point(a.asGetPublicProperty("x"), a.asGetPublicProperty("y"))); + a.asSetPublicProperty("x", d.x); + a.asSetPublicProperty("y", d.y); + }; + k.prototype.gotoAndPlay = function(a) { + return this.as3Object.gotoAndPlay(a); + }; + k.prototype.gotoAndStop = function(a) { + return this.as3Object.gotoAndStop(a); + }; + Object.defineProperty(k.prototype, "_height", {get:function() { + return this.as3Object.height; + }, set:function(a) { + isNaN(a) || (this.as3Object.height = a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "_highquality", {get:function() { + return 1; + }, set:function(a) { + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "hitArea", {get:function() { + throw "Not implemented: get$hitArea"; + }, set:function(a) { + throw "Not implemented: set$hitArea"; + }, enumerable:!0, configurable:!0}); + k.prototype.hitTest = function(a, d, b) { + return a instanceof k ? this.as3Object.hitTestObject(a.as3Object) : this.as3Object.hitTestPoint(a, d, b); + }; + k.prototype.lineGradientStyle = function(a, d, b, c, e, k, h, l) { + this.graphics.lineGradientStyle(a, d, b, c, e, k, h, l); + }; + k.prototype.lineStyle = function(a, d, b, c, e, k, h, l) { + this.graphics.lineStyle(a, d, b, c, e, k, h, l); + }; + k.prototype.lineTo = function(a, d) { + this.graphics.lineTo(a, d); + }; + k.prototype.loadMovie = function(a, d) { + function b(a) { + c.removeEventListener(s.events.Event.COMPLETE, b); + a = this.as3Object.parent; + var d = a.getChildIndex(this.as3Object); + a.removeChild(this.as3Object); + a.addChildAt(c.content, d); + } + var c = new s.display.Loader, e = new s.net.URLRequest(a); + d && (e.method = d); + c.load(e); + c.addEventListener(s.events.Event.COMPLETE, b); + }; + k.prototype.loadVariables = function(a, d) { + this.context.globals._loadVariables(this, a, d); + }; + k.prototype.localToGlobal = function(a) { + var d = this.as3Object.localToGlobal(new s.geom.Point(a.asGetPublicProperty("x"), a.asGetPublicProperty("y"))); + a.asSetPublicProperty("x", d.x); + a.asSetPublicProperty("y", d.y); + }; + Object.defineProperty(k.prototype, "_lockroot", {get:function() { + throw "Not implemented: get$_lockroot"; + }, set:function(a) { + throw "Not implemented: set$_lockroot"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "menu", {get:function() { + return this.as3Object.contextMenu; + }, set:function(a) { + this.as3Object.contextMenu = a; + }, enumerable:!0, configurable:!0}); + k.prototype.moveTo = function(a, d) { + this.graphics.moveTo(a, d); + }; + Object.defineProperty(k.prototype, "_name", {get:function() { + return this.as3Object.name; + }, set:function(a) { + this.as3Object.name = a; + }, enumerable:!0, configurable:!0}); + k.prototype.nextFrame = function() { + this.as3Object.nextFrame(); + }; + k.prototype.nextScene = function() { + this.as3Object.nextScene(); + }; + Object.defineProperty(k.prototype, "opaqueBackground", {get:function() { + return this.as3Object.opaqueBackground; + }, set:function(a) { + this.as3Object.opaqueBackground = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "_parent", {get:function() { + return a.getAVM1Object(this.as3Object.parent, this.context) || void 0; + }, set:function(a) { + throw "Not implemented: set$_parent"; + }, enumerable:!0, configurable:!0}); + k.prototype.play = function() { + this.as3Object.play(); + }; + k.prototype.prevFrame = function() { + this.as3Object.prevFrame(); + }; + k.prototype.prevScene = function() { + this.as3Object.prevScene(); + }; + Object.defineProperty(k.prototype, "_quality", {get:function() { + return "HIGH"; + }, set:function(a) { + }, enumerable:!0, configurable:!0}); + k.prototype.removeMovieClip = function() { + this._parent.as3Object.removeChild(this.as3Object); + }; + Object.defineProperty(k.prototype, "_rotation", {get:function() { + return this.as3Object.rotation; + }, set:function(a) { + this.as3Object.rotation = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "scale9Grid", {get:function() { + throw "Not implemented: get$scale9Grid"; + }, set:function(a) { + throw "Not implemented: set$scale9Grid"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "scrollRect", {get:function() { + throw "Not implemented: get$scrollRect"; + }, set:function(a) { + throw "Not implemented: set$scrollRect"; + }, enumerable:!0, configurable:!0}); + k.prototype.setMask = function(c) { + var d = this.as3Object; + if (c = a.AVM1Utils.resolveMovieClip(c)) { + d.mask = c.as3Object; } - g = r; - f.recoveringFromError = !1; - } catch (u) { - n = f.context; - u = w(u); - if (k.avm1ErrorsEnabled.value && !n.isTryCatchListening || u instanceof ua) { - throw u; + }; + Object.defineProperty(k.prototype, "_soundbuftime", {get:function() { + throw "Not implemented: get$_soundbuftime"; + }, set:function(a) { + throw "Not implemented: set$_soundbuftime"; + }, enumerable:!0, configurable:!0}); + k.prototype.startDrag = function(a, d, b, c, e) { + this.as3Object.startDrag(a, 3 > arguments.length ? null : new s.geom.Rectangle(d, b, c - d, e - b)); + }; + k.prototype.stop = function() { + return this.as3Object.stop(); + }; + k.prototype.stopDrag = function() { + return this.as3Object.stopDrag(); + }; + k.prototype.swapDepths = function(c) { + var d = this.as3Object; + c = "number" === typeof c ? a.AVM1Utils.resolveLevel(Number(c)).as3Object : a.AVM1Utils.resolveTarget(c).as3Object; + d.parent === c.parent && d.parent.swapChildren(d, c); + }; + Object.defineProperty(k.prototype, "tabChildren", {get:function() { + return this.as3Object.tabChildren; + }, set:function(a) { + this.as3Object.tabChildren = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "tabEnabled", {get:function() { + return this.as3Object.tabEnabled; + }, set:function(a) { + this.as3Object.tabEnabled = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "tabIndex", {get:function() { + return this.as3Object.tabIndex; + }, set:function(a) { + this.as3Object.tabIndex = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "_target", {get:function() { + var a = this.as3Object; + if (a === a.root) { + return "/"; } - if (u instanceof Ka) { - throw u; + var c = ""; + do { + c = "/" + a.name + c, a = a.parent; + } while (a !== a.root); + return c; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "_totalframes", {get:function() { + return this.as3Object.totalFrames; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "trackAsMenu", {get:function() { + throw "Not implemented: get$trackAsMenu"; + }, set:function(a) { + throw "Not implemented: set$trackAsMenu"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "transform", {get:function() { + throw "Not implemented: get$transform"; + }, set:function(a) { + throw "Not implemented: set$transform"; + }, enumerable:!0, configurable:!0}); + k.prototype.toString = function() { + return this.as3Object.toString(); + }; + k.prototype.unloadMovie = function() { + var a = this.as3Object, c = a.loaderInfo.loader; + c.parent && c.parent.removeChild(c); + a.stop(); + }; + Object.defineProperty(k.prototype, "_url", {get:function() { + return this.as3Object.loaderInfo.url; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "useHandCursor", {get:function() { + return this.as3Object.useHandCursor; + }, set:function(a) { + this.as3Object.useHandCursor = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "_visible", {get:function() { + return this.as3Object.visible; + }, set:function(a) { + this.as3Object.visible = 0 !== +a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "_width", {get:function() { + return this.as3Object.width; + }, set:function(a) { + isNaN(a) || (this.as3Object.width = a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "_x", {get:function() { + return this.as3Object.x; + }, set:function(a) { + isNaN(a) || (this.as3Object.x = a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "_xmouse", {get:function() { + return this.as3Object.mouseX; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "_xscale", {get:function() { + return 100 * this.as3Object.scaleX; + }, set:function(a) { + isNaN(a) || (this.as3Object.scaleX = a / 100); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "_y", {get:function() { + return this.as3Object.y; + }, set:function(a) { + isNaN(a) || (this.as3Object.y = a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "_ymouse", {get:function() { + return this.as3Object.mouseY; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(k.prototype, "_yscale", {get:function() { + return 100 * this.as3Object.scaleY; + }, set:function(a) { + isNaN(a) || (this.as3Object.scaleY = a / 100); + }, enumerable:!0, configurable:!0}); + k.prototype._resolveLevelNProperty = function(a) { + if ("_root" === a || "_level0" === a) { + return h.AVM1Context.instance.resolveLevel(0); + } + if (0 === a.indexOf("_level")) { + a = a.substring(6); + var c = a | 0; + if (0 < c && a == c) { + return h.AVM1Context.instance.resolveLevel(c); + } } - Kb.instance.reportTelemetry({topic:"error", error:1}); - if (!f.recoveringFromError) { - if (1E3 <= n.errorsIgnored++) { - throw new ua("long running script -- AVM1 errors limit is reached"); + return null; + }; + k.prototype.asGetProperty = function(a, c, b) { + if (e.call(this, a, c, b)) { + return l.call(this, a, c, b); + } + if ("string" === typeof c && "_" === c[0]) { + var g = this._resolveLevelNProperty(c); + if (g) { + return g; } - console.error("AVM1 error: " + u); - b.AVM2.Runtime.AVM2.instance.exceptions.push({source:"avm1", message:u.message, stack:u.stack}); - f.recoveringFromError = !0; } - } - h = g ? h.conditionalJumpTo : h.next; - h = a.actions[h]; - } - return l.pop(); - } - var Lb = b.AVM2.ABC.Multiname, Ob = b.AVM2.Runtime.forEachPublicProperty, Nb = b.AVM2.Runtime.construct, Mb = b.isNumeric, La = b.isFunction, Tb = b.Debug.notImplemented, Ea = b.Options.Option, Kb = b.Telemetry, Da = b.Debug.assert, Fa = b.Settings.shumwayOptions.register(new b.Options.OptionSet("AVM1")); - k.avm1TraceEnabled = Fa.register(new Ea("t1", "traceAvm1", "boolean", !1, "trace AVM1 execution")); - k.avm1ErrorsEnabled = Fa.register(new Ea("e1", "errorsAvm1", "boolean", !1, "fail on AVM1 warnings and errors")); - k.avm1TimeoutDisabled = Fa.register(new Ea("ha1", "nohangAvm1", "boolean", !1, "disable fail on AVM1 hang")); - k.avm1CompilerEnabled = Fa.register(new Ea("ca1", "compileAvm1", "boolean", !0, "compiles AVM1 code")); - k.avm1DebuggerEnabled = Fa.register(new Ea("da1", "debugAvm1", "boolean", !1, "allows AVM1 code debugging")); - k.Debugger = {pause:!1, breakpoints:{}}; - var Ub = function() { - function a(b, c) { - this.scope = b; - this.next = c; - } - a.prototype.create = function(b) { - return new a(b, this); - }; - return a; - }(), Vb = function(a) { - function c(d) { - a.call(this); - this.swfVersion = d; - this.globals = new b.AVM2.AS.avm1lib.AVM1Globals; - 8 <= d && this.globals.asSetPublicProperty("flash", b.AVM2.AS.avm1lib.createFlashObject()); - this.initialScope = new Ub(this.globals, null); - this.assets = {}; - this.assetsSymbols = []; - this.assetsClasses = []; - this.executionProhibited = this.isActive = !1; - this.stackDepth = this.abortExecutionAt = 0; - this.isTryCatchListening = !1; - this.errorsIgnored = 0; - this.deferScriptExecution = !0; - this.pendingScripts = []; - } - __extends(c, a); - c.prototype.addAsset = function(a, c, d) { - this.assets[a] = c; - this.assetsSymbols[c] && b.Debug.warning("Symbol " + c + " was exported already under different name"); - this.assetsSymbols[c] = d; - }; - c.prototype.registerClass = function(a, c) { - var d = this.assets[a]; - void 0 === d ? b.Debug.error("Cannot register " + a + " class for symbol") : this.assetsClasses[d] = c; - }; - c.prototype.getAsset = function(a) { - a = this.assets[a]; - return void 0 === a ? void 0 : {symbolId:a, symbolProps:this.assetsSymbols[a], theClass:this.assetsClasses[a]}; - }; - c.prototype.resolveTarget = function(a) { - var b = this.currentTarget || this.defaultTarget; - a ? "string" === typeof a && (a = G(a, b, this.globals.asGetPublicProperty("_root"))) : a = b; - if ("object" !== typeof a || null === a || !("_nativeAS3Object" in a)) { - throw Error("Invalid AVM1 target object: " + Object.prototype.toString.call(a)); - } - return a; - }; - c.prototype.resolveLevel = function(a) { - return this.resolveTarget(this.globals["_level" + a]); - }; - c.prototype.addToPendingScripts = function(a) { - this.deferScriptExecution ? this.pendingScripts.push(a) : a(); - }; - c.prototype.flushPendingScripts = function() { - for (var a = this.pendingScripts;a.length;) { - a.shift()(); - } - this.deferScriptExecution = !1; - }; - c.prototype.executeActions = function(a, b) { - y(a, this, b); - }; - return c; - }(k.AVM1Context); - k.AVM1Context.create = function(a) { - return new Vb(a); - }; - var Ka = function() { - return function(a) { - this.error = a; - }; - }(), ua = function(a) { - function b(c, d) { - a.call(this, c); - this.error = d; - } - __extends(b, a); - return b; - }(Error); - k.executeActions = y; - var Ja = {}, Sb = function() { - function a() { - a.cachedCalls || (a.cachedCalls = Rb()); - } - a.prototype.convertArgs = function(a, b, c) { - for (var d = [], h = 0;h < a.length;h++) { - var e = a[h]; - if ("object" !== typeof e || null === e || Array.isArray(e)) { - void 0 === e ? d.push("undefined") : d.push(JSON.stringify(e)); - } else { - if (e instanceof k.ParsedPushConstantAction) { - var f = "", g = c.constantPool; - g && (f = g[e.constantIndex], f = void 0 === f ? "undefined" : JSON.stringify(f), f = 0 <= f.indexOf("*/") ? "" : " /* " + f + " */"); - d.push("constantPool[" + e.constantIndex + "]" + f); - } else { - e instanceof k.ParsedPushRegisterAction ? d.push("registers[" + e.registerNumber + "]") : e instanceof k.AVM1ActionsData ? (f = "code_" + b + "_" + h, c[f] = e, d.push("res." + f)) : Tb("Unknown AVM1 action argument type"); + a = u(a, c, b); + if (p.isPublicQualifiedName(a) && this.isAVM1Instance) { + return this.__lookupChild(p.getNameFromPublicQualifiedName(a)); + } + }; + k.prototype.asHasProperty = function(a, c, b) { + if (e.call(this, a, c, b) || "string" === typeof c && "_" === c[0] && this._resolveLevelNProperty(c)) { + return!0; + } + a = u(a, c, b); + return p.isPublicQualifiedName(a) && this.isAVM1Instance ? !!this.__lookupChild(p.getNameFromPublicQualifiedName(a)) : !1; + }; + k.prototype.asGetEnumerableKeys = function() { + var a = m.call(this); + if (!this.isAVM1Instance) { + return a; + } + for (var c = this.as3Object, b = 0, g = c._children.length;b < g;b++) { + var k = c._children[b].name; + e.call(this, void 0, k, 0) || a.push(p.getPublicQualifiedName(k)); + } + return a; + }; + k.prototype.addFrameActionBlocks = function(a, c) { + var b = c.initActionBlocks, e = c.actionBlocks; + b && this._addInitActionBlocks(a, b); + if (e) { + for (b = 0;b < e.length;b++) { + this.addFrameScript(a, e[b]); } } + }; + k.prototype.addFrameScript = function(a, c) { + var b = this._frameScripts; + b || (v(!this._boundExecuteFrameScripts), this._boundExecuteFrameScripts = this._executeFrameScripts.bind(this), b = this._frameScripts = []); + var e = b[a + 1]; + e || (e = b[a + 1] = [], this.as3Object.addFrameScript(a, this._boundExecuteFrameScripts)); + b = new h.AVM1ActionsData(c, "f" + a + "i" + e.length); + e.push(b); + }; + k.prototype._addInitActionBlocks = function(a, c) { + function b(l) { + if (e.currentFrame === a + 1) { + e.removeEventListener("enterFrame", b); + l = k.context; + for (var m = 0;m < c.length;m++) { + var n = new h.AVM1ActionsData(c[m].actionsData, "f" + a + "i" + m); + l.executeActions(n, k); + } + } + } + var e = this.as3Object, k = this; + e.addEventListener("enterFrame", b); + }; + k.prototype._executeFrameScripts = function() { + var a = this.context, c = this._frameScripts[this.as3Object.currentFrame]; + v(c && c.length); + for (var b = 0;b < c.length;b++) { + a.executeActions(c[b], this); + } + }; + k.prototype._initEventsHandlers = function() { + this.bindEvents([new a.AVM1EventHandler("onData", "data"), new a.AVM1EventHandler("onDragOut", "dragOut"), new a.AVM1EventHandler("onDragOver", "dragOver"), new a.AVM1EventHandler("onEnterFrame", "enterFrame"), new a.AVM1EventHandler("onKeyDown", "keyDown"), new a.AVM1EventHandler("onKeyUp", "keyUp"), new a.AVM1EventHandler("onKillFocus", "focusOut", function(a) { + return[a.relatedObject]; + }), new a.AVM1EventHandler("onLoad", "load"), new a.AVM1EventHandler("onMouseDown", "mouseDown"), new a.AVM1EventHandler("onMouseUp", "mouseUp"), new t("onPress", "mouseDown"), new t("onRelease", "mouseUp"), new t("onReleaseOutside", "releaseOutside"), new t("onRollOut", "mouseOut"), new t("onRollOver", "mouseOver"), new a.AVM1EventHandler("onSetFocus", "focusIn", function(a) { + return[a.relatedObject]; + }), new a.AVM1EventHandler("onUnload", "unload")]); + }; + return k; + }(a.AVM1SymbolBase); + a.AVM1MovieClip = q; + })(h.Lib || (h.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.Debug.somewhatImplemented, v; + (function(a) { + a[a.IdleToOverUp = 1] = "IdleToOverUp"; + a[a.OverUpToIdle = 2] = "OverUpToIdle"; + a[a.OverUpToOverDown = 4] = "OverUpToOverDown"; + a[a.OverDownToOverUp = 8] = "OverDownToOverUp"; + a[a.OverDownToOutDown = 16] = "OverDownToOutDown"; + a[a.OutDownToOverDown = 32] = "OutDownToOverDown"; + a[a.OutDownToIdle = 64] = "OutDownToIdle"; + a[a.IdleToOverDown = 128] = "IdleToOverDown"; + a[a.OverDownToIdle = 256] = "OverDownToIdle"; + })(v || (v = {})); + var p = [-1, 37, 39, 36, 35, 45, 46, -1, 8, -1, -1, -1, -1, 13, 38, 40, 33, 34, 9, 27]; + v = function(c) { + function l() { + c.apply(this, arguments); } - return d.join(","); - }; - a.prototype.convertAction = function(a, b, c) { - switch(a.action.actionCode) { - case 153: - ; - case 62: - return ""; - case 136: - return c.constantPool = a.action.args[0], " constantPool = [" + this.convertArgs(a.action.args[0], b, c) + "];\n ectx.constantPool = constantPool;\n"; - case 150: - return " stack.push(" + this.convertArgs(a.action.args, b, c) + ");\n"; - case 138: - ; - case 141: - return " if (calls." + a.action.actionName + "(ectx,[" + this.convertArgs(a.action.args, b, c) + "])) { position = " + a.conditionalJumpTo + "; break; }\n"; - case 157: - return " if (!!stack.pop()) { position = " + a.conditionalJumpTo + "; break; }\n"; - default: - return " calls." + a.action.actionName + "(ectx" + (a.action.args ? ",[" + this.convertArgs(a.action.args, b, c) + "]" : "") + ");\n"; - } - }; - a.prototype.checkAvm1Timeout = function(a) { - if (Date.now() >= a.context.abortExecutionAt) { - throw new ua("long running script -- AVM1 instruction hang timeout"); - } - }; - a.prototype.generate = function(b) { - var c = this, d = b.blocks, h = {}, e = 0; - b = b.dataId; - var f = "return function avm1gen_" + b + "(ectx) {\nvar position = 0;\nvar checkTimeAfter = 0;\nvar constantPool = ectx.constantPool, registers = ectx.registers, stack = ectx.stack;\n"; - k.avm1DebuggerEnabled.value && (f += "/* Running " + b + " */ if (Shumway.AVM1.Debugger.pause || Shumway.AVM1.Debugger.breakpoints." + b + ") { debugger; }\n"); - f += "while (!ectx.isEndOfActions) {\nif (checkTimeAfter <= 0) { checkTimeAfter = 1000; checkTimeout(ectx); }\nswitch(position) {\n"; - d.forEach(function(a) { - f += " case " + a.label + ":\n"; - a.items.forEach(function(a) { - f += c.convertAction(a, e++, h); - }); - f += " position = " + a.jump + ";\n checkTimeAfter -= " + a.items.length + ";\n break;\n"; - }); - f += " default: ectx.isEndOfActions = true; break;\n}\n}\nreturn stack.pop();};"; - return(new Function("calls", "res", "checkTimeout", f))(a.cachedCalls, h, this.checkAvm1Timeout); - }; - return a; - }(), Va = function() { - function a() { - } - a.get = function() { - return k.avm1TraceEnabled.value ? a.tracer : a.nullTracer; - }; - a.tracer = function() { - var a = 0; - return{print:function(b, c) { - for (var d = b.position, h = b.actionCode, e = b.actionName, f = [], g = 0;g < c.length;g++) { - var k = c[g]; - f.push(k && "object" === typeof k ? "[" + (k.constructor && k.constructor.name ? k.constructor.name : "Object") + "]" : k); + __extends(l, c); + l.createAVM1Class = function() { + return a.wrapAVM1Class(l, [], "_alpha blendMode cacheAsBitmap enabled filters _focusrect getDepth _height _highquality menu _name _parent _quality _rotation scale9Grid _soundbuftime tabEnabled tabIndex _target trackAsMenu _url useHandCursor _visible _width _x _xmouse _xscale _y _ymouse _yscale".split(" ")); + }; + l.prototype.initAVM1SymbolInstance = function(a, l) { + c.prototype.initAVM1SymbolInstance.call(this, a, l); + var p = this._as3Object; + if (p._symbol && p._symbol.data.buttonActions) { + p.buttonMode = !0; + p.addEventListener("addedToStage", this._addListeners.bind(this)); + p.addEventListener("removedFromStage", this._removeListeners.bind(this)); + for (var q = this._requiredListeners = Object.create(null), n = this._actions = p._symbol.data.buttonActions, k = 0;k < n.length;k++) { + var f = n[k]; + f.actionsBlock || (f.actionsBlock = new h.AVM1ActionsData(f.actionsData, "s" + p._symbol.id + "e" + k)); + if (f.keyCode) { + q.keyDown = this._keyDownHandler.bind(this); + } else { + var d; + switch(f.stateTransitionFlags) { + case 64: + d = "releaseOutside"; + break; + case 1: + d = "rollOver"; + break; + case 2: + d = "rollOut"; + break; + case 4: + d = "mouseDown"; + break; + case 8: + d = "mouseUp"; + break; + case 16: + ; + case 32: + s("AVM1 drag over/out button actions"); + break; + case 128: + ; + case 256: + s("AVM1 drag trackAsMenu over/out button actions"); + break; + default: + console.warn("Unknown AVM1 button action type: " + f.stateTransitionFlags); + continue; + } + q[d] = this._mouseEventHandler.bind(this, f.stateTransitionFlags); + } + } + } + this._initEventsHandlers(); + }; + Object.defineProperty(l.prototype, "_alpha", {get:function() { + return this._as3Object.alpha; + }, set:function(a) { + this._as3Object.alpha = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "blendMode", {get:function() { + return this._as3Object.blendMode; + }, set:function(a) { + this._as3Object.blendMode = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "cacheAsBitmap", {get:function() { + return this._as3Object.cacheAsBitmap; + }, set:function(a) { + this._as3Object.cacheAsBitmap = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "enabled", {get:function() { + return this._as3Object.enabled; + }, set:function(a) { + this._as3Object.enabled = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "filters", {get:function() { + throw "Not implemented: get$filters"; + }, set:function(a) { + throw "Not implemented: set$filters"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "_focusrect", {get:function() { + throw "Not implemented: get$_focusrect"; + }, set:function(a) { + throw "Not implemented: set$_focusrect"; + }, enumerable:!0, configurable:!0}); + l.prototype.getDepth = function() { + return this._as3Object._clipDepth; + }; + Object.defineProperty(l.prototype, "_height", {get:function() { + return this._as3Object.height; + }, set:function(a) { + isNaN(a) || (this._as3Object.height = a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "_highquality", {get:function() { + return 1; + }, set:function(a) { + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "menu", {get:function() { + return this._as3Object.contextMenu; + }, set:function(a) { + this._as3Object.contextMenu = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "_name", {get:function() { + return this._as3Object.contextMenu; + }, set:function(a) { + this._as3Object.contextMenu = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "_parent", {get:function() { + return a.getAVM1Object(this._as3Object.parent, this.context); + }, set:function(a) { + throw "Not implemented: set$_parent"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "_quality", {get:function() { + return "HIGH"; + }, set:function(a) { + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "_rotation", {get:function() { + return this._as3Object.rotation; + }, set:function(a) { + this._as3Object.rotation = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "scale9Grid", {get:function() { + throw "Not implemented: get$scale9Grid"; + }, set:function(a) { + throw "Not implemented: set$scale9Grid"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "_soundbuftime", {get:function() { + throw "Not implemented: get$_soundbuftime"; + }, set:function(a) { + throw "Not implemented: set$_soundbuftime"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "tabEnabled", {get:function() { + return this._as3Object.tabEnabled; + }, set:function(a) { + this._as3Object.tabEnabled = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "tabIndex", {get:function() { + return this._as3Object.tabIndex; + }, set:function(a) { + this._as3Object.tabIndex = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "_target", {get:function() { + return a.AVM1Utils.getTarget(this); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "trackAsMenu", {get:function() { + throw "Not implemented: get$trackAsMenu"; + }, set:function(a) { + throw "Not implemented: set$trackAsMenu"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "_url", {get:function() { + return this._as3Object.loaderInfo.url; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "useHandCursor", {get:function() { + return this._as3Object.useHandCursor; + }, set:function(a) { + this._as3Object.useHandCursor = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "_visible", {get:function() { + return this._as3Object.visible; + }, set:function(a) { + this._as3Object.visible = 0 !== +a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "_width", {get:function() { + return this._as3Object.width; + }, set:function(a) { + isNaN(a) || (this._as3Object.width = a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "_x", {get:function() { + return this._as3Object.x; + }, set:function(a) { + isNaN(a) || (this._as3Object.x = a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "_xmouse", {get:function() { + return this._as3Object.mouseX; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "_xscale", {get:function() { + return this._as3Object.scaleX; + }, set:function(a) { + isNaN(a) || (this._as3Object.scaleX = a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "_y", {get:function() { + return this._as3Object.y; + }, set:function(a) { + isNaN(a) || (this._as3Object.y = a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "_ymouse", {get:function() { + return this._as3Object.mouseY; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(l.prototype, "_yscale", {get:function() { + return this._as3Object.scaleY; + }, set:function(a) { + isNaN(a) || (this._as3Object.scaleY = a); + }, enumerable:!0, configurable:!0}); + l.prototype._addListeners = function() { + for (var a in this._requiredListeners) { + ("keyDown" === a ? this._as3Object.stage : this._as3Object).addEventListener(a, this._requiredListeners[a]); } - g = Array(a + 1).join(".."); - console.log("AVM1 trace: " + g + d + ": " + e + "(" + h.toString(16) + "), stack=" + f); - }, indent:function() { - a++; - }, unindent:function() { - a--; - }, message:function(a) { - console.log("AVM1 trace: ------- " + a); - }}; - }(); - a.nullTracer = {print:function(a, b) { - }, indent:function() { - }, unindent:function() { - }, message:function(a) { - }}; - return a; - }(); - })(b.AVM1 || (b.AVM1 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - var g = b.AVM2.AS.ASObject, k = b.AVM2.AS.flash, m = b.AVM1.AVM1Context, d = function(a) { - function b() { + }; + l.prototype._removeListeners = function() { + for (var a in this._requiredListeners) { + ("keyDown" === a ? this._as3Object.stage : this._as3Object).removeEventListener(a, this._requiredListeners[a]); } - __extends(b, a); - b.addProperty = function(a, b, c, d, f) { - "undefined" === typeof f && (f = !0); - a.asDefinePublicProperty(b, {get:c, set:d || void 0, enumerable:f, configurable:!0}); - }; - b.resolveTarget = function(a) { - "undefined" === typeof a && (a = void 0); - return m.instance.resolveTarget(a); - }; - b.resolveLevel = function(a) { - return m.instance.resolveLevel(+a); - }; - Object.defineProperty(b, "currentStage", {get:function() { - return f.getAVM1Object(m.instance.root._nativeAS3Object.stage); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(b, "swfVersion", {get:function() { - return m.instance.swfVersion; - }, enumerable:!0, configurable:!0}); - b.getAVM1Object = function(a) { - return f.getAVM1Object(a); - }; - b._installObjectMethods = function() { - var a = g.asGetPublicProperty("prototype"); - g.asSetPublicProperty("registerClass", function(a, b) { - m.instance.registerClass(a, b); - }); - a.asDefinePublicProperty("addProperty", {value:function(a, b, c) { - if ("string" !== typeof a || "" === a || "function" !== typeof b || "function" !== typeof c && null !== c) { - return!1; - } - this.asDefinePublicProperty(a, {get:b, set:c || void 0, configurable:!0, enumerable:!0}); - return!0; - }, writable:!1, enumerable:!1, configurable:!1}); - }; - b.classInitializer = null; - b.initializer = null; - b.classSymbols = ["createFlashObject!"]; - b.instanceSymbols = null; - return b; - }(b.AVM2.AS.ASNative); - f.AVM1Utils = d; - f.initDefaultListeners = function(a) { - var b = a.asGetPublicProperty("_as2DefaultListeners"); - if (b) { - for (var d = 0;d < b.length;d++) { - var f = b[d]; - f.asGetPublicProperty("setter").call(a, f.value); - } + }; + l.prototype._keyDownHandler = function(a) { + for (var c = this._actions, h = 0;h < c.length;h++) { + var l = c[h]; + l.keyCode && (32 > l.keyCode && p[l.keyCode] === a.asGetPublicProperty("keyCode") || l.keyCode === a.asGetPublicProperty("charCode")) && this._runAction(l); + } + }; + l.prototype._mouseEventHandler = function(a) { + for (var c = this._actions, h = 0;h < c.length;h++) { + var l = c[h]; + l.stateTransitionFlags === a && this._runAction(l); } }; - f.createFlashObject = function() { - return d.createFlashObject(); + l.prototype._runAction = function(c) { + this._as3Object.loaderInfo._avm1Context.executeActions(c.actionsBlock, a.getAVM1Object(this._as3Object._parent, this.context)); }; - f.getAVM1Object = function(a) { - return a ? a._as2Object ? a._as2Object : k.display.MovieClip.isType(a) ? a._avm1SymbolClass ? b.AVM2.AS.avm1lib.AVM1MovieClip._initFromConstructor(a._avm1SymbolClass, a) : new b.AVM2.AS.avm1lib.AVM1MovieClip(a) : k.display.SimpleButton.isType(a) ? new b.AVM2.AS.avm1lib.AVM1Button(a) : k.text.TextField.isType(a) ? new b.AVM2.AS.avm1lib.AVM1TextField(a) : k.display.BitmapData.isType(a) ? new a : null : null; + l.prototype._initEventsHandlers = function() { + this.bindEvents([new a.AVM1EventHandler("onDragOut", "dragOut"), new a.AVM1EventHandler("onDragOver", "dragOver"), new a.AVM1EventHandler("onKeyDown", "keyDown"), new a.AVM1EventHandler("onKeyUp", "keyUp"), new a.AVM1EventHandler("onKillFocus", "focusOut", function(a) { + return[a.relatedObject]; + }), new a.AVM1EventHandler("onLoad", "load"), new a.AVM1EventHandler("onMouseDown", "mouseDown"), new a.AVM1EventHandler("onMouseUp", "mouseUp"), new a.AVM1EventHandler("onPress", "mouseDown"), new a.AVM1EventHandler("onRelease", "mouseUp"), new a.AVM1EventHandler("onReleaseOutside", "releaseOutside"), new a.AVM1EventHandler("onRollOut", "mouseOut"), new a.AVM1EventHandler("onRollOver", "mouseOver"), new a.AVM1EventHandler("onSetFocus", "focusIn", function(a) { + return[a.relatedObject]; + })]); }; - })(g.avm1lib || (g.avm1lib = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + return l; + }(a.AVM1SymbolBase); + a.AVM1Button = v; + })(h.Lib || (h.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - var k = b.Debug.somewhatImplemented, s; - (function(b) { - b[b.IdleToOverUp = 1] = "IdleToOverUp"; - b[b.OverUpToIdle = 2] = "OverUpToIdle"; - b[b.OverUpToOverDown = 4] = "OverUpToOverDown"; - b[b.OverDownToOverUp = 8] = "OverDownToOverUp"; - b[b.OverDownToOutDown = 16] = "OverDownToOutDown"; - b[b.OutDownToOverDown = 32] = "OutDownToOverDown"; - b[b.OutDownToIdle = 64] = "OutDownToIdle"; - b[b.IdleToOverDown = 128] = "IdleToOverDown"; - b[b.OverDownToIdle = 256] = "OverDownToIdle"; - })(s || (s = {})); - var m = [-1, 37, 39, 36, 35, 45, 46, -1, 8, -1, -1, -1, -1, 13, 38, 40, 33, 34, 9, 27]; - s = function(d) { - function a(a) { - a && this._init(a); - } - __extends(a, d); - a.prototype._init = function(a) { - this._nativeAS3Object = a; - f.initDefaultListeners(this); - if (a._symbol && a._symbol.buttonActions) { - this._nativeAS3Object.addEventListener("addedToStage", this._addListeners.bind(this)); - this._nativeAS3Object.addEventListener("removedFromStage", this._removeListeners.bind(this)); - var d = this._requiredListeners = Object.create(null); - a = this._actions = a._symbol.buttonActions; - for (var g = 0;g < a.length;g++) { - var e = a[g]; - e.actionsBlock || (e.actionsBlock = new b.AVM1.AVM1ActionsData(e.actionsData, "i" + g)); - if (e.keyCode) { - d.keyDown = this._keyDownHandler.bind(this); - } else { - var m; - switch(e.stateTransitionFlags) { - case 64: - m = "releaseOutside"; - break; - case 1: - m = "rollOver"; - break; - case 2: - m = "rollOut"; - break; - case 4: - m = "mouseDown"; - break; - case 8: - m = "mouseUp"; - break; - case 16: - ; - case 32: - k("AVM1 drag over/out button actions"); - break; - case 128: - ; - case 256: - k("AVM1 drag trackAsMenu over/out button actions"); - break; - default: - warn("Unknown AVM1 button action type: " + e.stateTransitionFlags); - continue; - } - d[m] = this._mouseEventHandler.bind(this, e.stateTransitionFlags); - } - } - } - }; - a.prototype._addListeners = function() { - for (var a in this._requiredListeners) { - ("keyDown" === a ? this._nativeAS3Object.stage : this._nativeAS3Object).addEventListener(a, this._requiredListeners[a]); - } - }; - a.prototype._removeListeners = function() { - for (var a in this._requiredListeners) { - ("keyDown" === a ? this._nativeAS3Object.stage : this._nativeAS3Object).removeEventListener(a, this._requiredListeners[a]); - } - }; - a.prototype._keyDownHandler = function(a) { - for (var b = this._actions, d = 0;d < b.length;d++) { - var e = b[d]; - e.keyCode && (32 > e.keyCode && m[e.keyCode] === a.asGetPublicProperty("keyCode") || e.keyCode === a.asGetPublicProperty("charCode")) && this._runAction(e); - } - }; - a.prototype._mouseEventHandler = function(a) { - for (var b = this._actions, d = 0;d < b.length;d++) { - var e = b[d]; - e.stateTransitionFlags === a && this._runAction(e); - } - }; - a.prototype._runAction = function(a) { - this._nativeAS3Object.loaderInfo._avm1Context.executeActions(a.actionsBlock, f.getAVM1Object(this._nativeAS3Object._parent)); - }; - Object.defineProperty(a.prototype, "_as3Object", {get:function() { - return this._nativeAS3Object; - }, enumerable:!0, configurable:!0}); - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(g.ASNative); - f.AVM1Button = s; - })(g.avm1lib || (g.avm1lib = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - var k = b.Debug.assert, s = b.Debug.notImplemented, m = b.AVM2.AS.flash.text.TextFormat, d = b.AVM1.AVM1Context, a = b.AVM2.AS.Natives, c = function(b) { - function c() { - s("Dummy Constructor: public avm1lib.AVM1Globals"); - } - __extends(c, b); - c._addInternalClasses = function(a) { - a.asSetPublicProperty("Object", g.ASObject); - a.asSetPublicProperty("Function", g.ASFunction); - a.asSetPublicProperty("Array", g.ASArray); - a.asSetPublicProperty("Number", g.ASNumber); - a.asSetPublicProperty("Math", g.ASMath); - a.asSetPublicProperty("Boolean", g.ASBoolean); - a.asSetPublicProperty("Date", g.ASDate); - a.asSetPublicProperty("RegExp", g.ASRegExp); - a.asSetPublicProperty("String", g.ASString); - }; - c.prototype.ASSetPropFlags = function(a, b, c, d) { - }; - c.prototype._addToPendingScripts = function(a, b, c) { - "undefined" === typeof c && (c = null); - k(b, "invalid function in _addToPendingScripts"); - d.instance.addToPendingScripts(function() { - b.apply(a, c); - }); - }; - c.prototype.escape = function(a) { - return encodeURIComponent(a).replace(/\!|'|\(|\)|\*|-|\.|_|~/g, function(a) { - switch(a) { - case "*": - return "%2A"; - case "-": - return "%2D"; - case ".": - return "%2E"; - case "_": - return "%5F"; - default: - return globalEscape(a); - } - }); - }; - c.prototype.unescape = function(a) { - return decodeURIComponent(a); - }; - c.prototype._setLevel = function(a, b) { - }; - c.prototype.trace = function(b) { - a.print(b); - }; - c.classInitializer = null; - c.initializer = function() { - m.prototype.asDefinePublicProperty("getTextExtent", {value:f.AVM1TextFormat.prototype._as2GetTextExtent, writable:!1, enumerable:!1, configurable:!1}); - }; - c.classSymbols = null; - c.instanceSymbols = "_global! flash $asfunction call! chr! clearInterval! clearTimeout! duplicateMovieClip! fscommand! getAVM1Property! getTimer! getURL! getVersion! gotoAndPlay! gotoAndStop! gotoLabel! ifFrameLoaded! int! length! loadMovie! loadMovieNum! loadVariables! mbchr! mblength! mbord! mbsubstring! nextFrame! nextScene! ord! play! prevFrame! prevScene! print! printAsBitmap! printAsBitmapNum! printNum! random! removeMovieClip! setInterval! setAVM1Property! setTimeout! showRedrawRegions! startDrag! stop! stopAllSounds! stopDrag! substring! targetPath! toggleHighQuality! unloadMovie! unloadMovieNum! updateAfterEvent!".split(" "); - return c; - }(g.ASNative); - f.AVM1Globals = c; - })(g.avm1lib || (g.avm1lib = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -var globalEscape = this.escape; -(function(b) { - (function(k) { - (function(g) { - (function(f) { - var k = b.Debug.assert, s = b.AVM2.Runtime.construct, m = b.AVM1.AVM1Context, d = function(a) { - function c(a) { - this._nativeAS3Object = a; - this._boundExecuteFrameScripts = this._frameScripts = null; +(function(c) { + (function(c) { + (function(a) { + var c = function(c) { + function h() { + c.apply(this, arguments); + } + __extends(h, c); + h.createAVM1Class = function() { + return a.wrapAVM1Class(h, [], "_alpha antiAliasType autoSize background backgroundColor border borderColor bottomScroll condenseWhite embedFonts getNewTextFormat getTextFormat _height _highquality hscroll html htmlText length maxChars maxhscroll maxscroll multiline _name _parent password _quality _rotation scroll selectable setNewTextFormat setTextFormat _soundbuftime tabEnabled tabIndex _target text textColor textHeight textWidth type _url _visible _width wordWrap _x _xmouse _xscale _y _ymouse _yscale".split(" ")); + }; + h.prototype.initAVM1SymbolInstance = function(a, h) { + c.prototype.initAVM1SymbolInstance.call(this, a, h); + this._variable = ""; + this._exitFrameHandler = null; + h._symbol && (this.variable = h._symbol.variableName || ""); + this._initEventsHandlers(); + }; + Object.defineProperty(h.prototype, "_alpha", {get:function() { + return this._as3Object.alpha; + }, set:function(a) { + this._as3Object.alpha = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "antiAliasType", {get:function() { + return this._as3Object.antiAliasType; + }, set:function(a) { + this._as3Object.antiAliasType = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "autoSize", {get:function() { + return this._as3Object.autoSize; + }, set:function(a) { + !0 === a ? a = "left" : !1 === a && (a = "none"); + this._as3Object.autoSize = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "background", {get:function() { + return this._as3Object.background; + }, set:function(a) { + this._as3Object.background = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "backgroundColor", {get:function() { + return this._as3Object.backgroundColor; + }, set:function(a) { + this._as3Object.backgroundColor = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "border", {get:function() { + return this._as3Object.border; + }, set:function(a) { + this._as3Object.border = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "borderColor", {get:function() { + return this._as3Object.borderColor; + }, set:function(a) { + this._as3Object.borderColor = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "bottomScroll", {get:function() { + return this._as3Object.bottomScrollV; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "condenseWhite", {get:function() { + return this._as3Object.condenseWhite; + }, set:function(a) { + this._as3Object.condenseWhite = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "embedFonts", {get:function() { + return this._as3Object.embedFonts; + }, set:function(a) { + this._as3Object.embedFonts = a; + }, enumerable:!0, configurable:!0}); + h.prototype.getNewTextFormat = function() { + return this._as3Object.defaultTextFormat; + }; + h.prototype.getTextFormat = function() { + return this._as3Object.getTextFormat; + }; + Object.defineProperty(h.prototype, "_height", {get:function() { + return this._as3Object.height; + }, set:function(a) { + isNaN(a) || (this._as3Object.height = a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "_highquality", {get:function() { + return 1; + }, set:function(a) { + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "hscroll", {get:function() { + return this._as3Object.scrollH; + }, set:function(a) { + this._as3Object.scrollH = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "html", {get:function() { + throw "Not implemented: get$_html"; + }, set:function(a) { + throw "Not implemented: set$_html"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "htmlText", {get:function() { + return this._as3Object.htmlText; + }, set:function(a) { + this._as3Object.htmlText = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "length", {get:function() { + return this._as3Object.length; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "maxChars", {get:function() { + return this._as3Object.maxChars; + }, set:function(a) { + this._as3Object.maxChars = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "maxhscroll", {get:function() { + return this._as3Object.maxScrollH; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "maxscroll", {get:function() { + return this._as3Object.maxScrollV; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "multiline", {get:function() { + return this._as3Object.multiline; + }, set:function(a) { + this._as3Object.multiline = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "_name", {get:function() { + return this.as3Object._name; + }, set:function(a) { + this.as3Object._name = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "_parent", {get:function() { + return this._as3Object.parent; + }, set:function(a) { + throw "Not implemented: set$_parent"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "password", {get:function() { + return this._as3Object.displayAsPassword; + }, set:function(a) { + this._as3Object.displayAsPassword = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "_quality", {get:function() { + return "HIGH"; + }, set:function(a) { + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "_rotation", {get:function() { + return this._as3Object.rotation; + }, set:function(a) { + this._as3Object.rotation = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "scroll", {get:function() { + return this._as3Object.scrollV; + }, set:function(a) { + this._as3Object.scrollV = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "selectable", {get:function() { + return this._as3Object.selectable; + }, set:function(a) { + this._as3Object.selectable = a; + }, enumerable:!0, configurable:!0}); + h.prototype.setNewTextFormat = function(a) { + this._as3Object.defaultTextFormat = a; + }; + h.prototype.setTextFormat = function() { + this._as3Object.setTextFormat.apply(this._as3Object, arguments); + }; + Object.defineProperty(h.prototype, "_soundbuftime", {get:function() { + throw "Not implemented: get$_soundbuftime"; + }, set:function(a) { + throw "Not implemented: set$_soundbuftime"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "tabEnabled", {get:function() { + return this._as3Object.tabEnabled; + }, set:function(a) { + this._as3Object.tabEnabled = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "tabIndex", {get:function() { + return this._as3Object.tabIndex; + }, set:function(a) { + this._as3Object.tabIndex = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "_target", {get:function() { + return a.AVM1Utils.getTarget(this); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "text", {get:function() { + return this._as3Object.text; + }, set:function(a) { + this._as3Object.text = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "textColor", {get:function() { + return this._as3Object.textColor; + }, set:function(a) { + this._as3Object.textColor = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "textHeight", {get:function() { + return this._as3Object.textHeight; + }, set:function(a) { + throw "Not supported: set$textHeight"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "textWidth", {get:function() { + return this._as3Object.textWidth; + }, set:function(a) { + throw "Not supported: set$textWidth"; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "type", {get:function() { + return this._as3Object.type; + }, set:function(a) { + this._as3Object.type = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "_url", {get:function() { + return this._as3Object.loaderInfo.url; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "variable", {get:function() { + return this._variable; + }, set:function(a) { + if (a !== this._variable) { + var c = this.as3Object; + this._exitFrameHandler && !a && (c.removeEventListener("exitFrame", this._exitFrameHandler), this._exitFrameHandler = null); + this._variable = a; + !this._exitFrameHandler && a && (this._exitFrameHandler = this._onAS3ObjectExitFrame.bind(this), c.addEventListener("exitFrame", this._exitFrameHandler)); } - __extends(c, a); - c.prototype._init = function(a) { - !this._nativeAS3Object && a && (this._nativeAS3Object = a, a._as2Object = this, f.initDefaultListeners(this)); - }; - Object.defineProperty(c.prototype, "context", {set:function(a) { - k(!this._context); - this._context = a; - }, enumerable:!0, configurable:!0}); - c._initFromConstructor = function(a, b) { - var c = Object.create(a.asGetPublicProperty("prototype")); - c._nativeAS3Object = b; - b._as2Object = c; - f.initDefaultListeners(c); - a.call(c); - return c; - }; - Object.defineProperty(c.prototype, "_as3Object", {get:function() { - return this._nativeAS3Object; - }, enumerable:!0, configurable:!0}); - c.prototype.attachBitmap = function(a, b, c, d) { - "undefined" === typeof c && (c = "auto"); - "undefined" === typeof d && (d = !1); - a = s(g.flash.display.Bitmap, [a, c, d]); - this._insertChildAtDepth(a, b); - }; - c.prototype._constructMovieClipSymbol = function(a, b) { - var c = m.instance.getAsset(a), d = Object.create(c.symbolProps); - d.avm1Name = b; - d.avm1SymbolClass = c.theClass; - c = g.flash.display.MovieClip.initializeFrom(d); - g.flash.display.MovieClip.instanceConstructorNoInitialize.call(c); - return c; - }; - c.prototype.addFrameActionBlocks = function(a, b) { - var c = b.initActionBlocks, d = b.actionBlocks; - c && this._addInitActionBlocks(a, c); - if (d) { - for (c = 0;c < d.length;c++) { - this.addFrameScript(a, d[c]); - } - } - }; - c.prototype.addFrameScript = function(a, c) { - var d = this._frameScripts; - d || (k(!this._boundExecuteFrameScripts), this._boundExecuteFrameScripts = this._executeFrameScripts.bind(this), d = this._frameScripts = []); - var f = d[a + 1]; - f || (f = d[a + 1] = [], this._nativeAS3Object.addFrameScript(a, this._boundExecuteFrameScripts)); - d = new b.AVM1.AVM1ActionsData(c, "f" + a + "i" + f.length); - f.push(d); - }; - c.prototype._addInitActionBlocks = function(a, c) { - function d(k) { - if (f.currentFrame === a + 1) { - f.removeEventListener("enterFrame", d); - k = g._context; - for (var m = 0;m < c.length;m++) { - var r = new b.AVM1.AVM1ActionsData(c[m].actionsData, "f" + a + "i" + m); - k.executeActions(r, g); - } + }, enumerable:!0, configurable:!0}); + h.prototype._onAS3ObjectExitFrame = function() { + this._syncTextFieldValue(this.as3Object, this._variable); + }; + h.prototype._syncTextFieldValue = function(c, h) { + var e; + e = 0 <= h.indexOf(".") || 0 <= h.indexOf(":"); + var m = this.context.utils; + if (e) { + var p = h.split(/[.:\/]/g); + h = p.pop(); + if ("_root" == p[0] || "" === p[0]) { + if (null === c.root) { + return; } + e = a.getAVM1Object(c.root, this.context); + p.shift(); + "" === p[0] && p.shift(); + } else { + e = a.getAVM1Object(c._parent, this.context); } - var f = this._nativeAS3Object, g = this; - f.addEventListener("enterFrame", d); - }; - c.prototype._executeFrameScripts = function() { - var a = this._context, b = this._frameScripts[this._nativeAS3Object.currentFrame]; - k(b && b.length); - for (var c = 0;c < b.length;c++) { - a.executeActions(b[c], this); - } - }; - c.prototype._callFrame = function(a) { - }; - c.prototype._insertChildAtDepth = function(a, b) { - var c = this._nativeAS3Object; - c.addTimelineObjectAtDepth(a, Math.min(c.numChildren, b)); - if (!g.flash.display.Bitmap.isType(a)) { - var c = f.getAVM1Object(a), d = a.name; - d && void 0 === this.asGetPublicProperty(d) && this.asSetPublicProperty(d, c); - } - }; - c.prototype.getInstanceAtDepth = function(a) { - for (var b = this._nativeAS3Object, c = 0, d = b.numChildren;c < d;c++) { - var k = b.getChildAt(c); - if (k && k._depth === a) { - return g.flash.display.Bitmap.isType(k) ? this : f.getAVM1Object(k); + for (;0 < p.length;) { + var q = p.shift(); + e = m.getProperty(e, q); + if (!e) { + return; } } - return null; - }; - c.prototype.getNextHighestDepth = function() { - for (var a = this._nativeAS3Object, b = 0, c = 0, d = a.numChildren;c < d;c++) { - var f = a.getChildAt(c); - f._depth > b && (b = f._depth); + } else { + e = a.getAVM1Object(c._parent, this.context); + } + m.hasProperty(e, h) || m.setProperty(e, h, c.text); + c.text = "" + m.getProperty(e, h); + }; + Object.defineProperty(h.prototype, "_visible", {get:function() { + return this._as3Object.visible; + }, set:function(a) { + this._as3Object.visible = 0 !== +a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "_width", {get:function() { + return this._as3Object.width; + }, set:function(a) { + isNaN(a) || (this._as3Object.width = a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "wordWrap", {get:function() { + return this._as3Object.wordWrap; + }, set:function(a) { + this._as3Object.wordWrap = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "_x", {get:function() { + return this._as3Object.x; + }, set:function(a) { + isNaN(a) || (this._as3Object.x = a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "_xmouse", {get:function() { + return this._as3Object.mouseX; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "_xscale", {get:function() { + return this._as3Object.scaleX; + }, set:function(a) { + isNaN(a) || (this._as3Object.scaleX = a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "_y", {get:function() { + return this._as3Object.y; + }, set:function(a) { + isNaN(a) || (this._as3Object.y = a); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "_ymouse", {get:function() { + return this._as3Object.mouseY; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(h.prototype, "_yscale", {get:function() { + return this._as3Object.scaleY; + }, set:function(a) { + isNaN(a) || (this._as3Object.scaleY = a); + }, enumerable:!0, configurable:!0}); + h.prototype._initEventsHandlers = function() { + this.bindEvents([new a.AVM1EventHandler("onDragOut", "dragOut"), new a.AVM1EventHandler("onDragOver", "dragOver"), new a.AVM1EventHandler("onKeyDown", "keyDown"), new a.AVM1EventHandler("onKeyUp", "keyUp"), new a.AVM1EventHandler("onKillFocus", "focusOut", function(a) { + return[a.relatedObject]; + }), new a.AVM1EventHandler("onLoad", "load"), new a.AVM1EventHandler("onMouseDown", "mouseDown"), new a.AVM1EventHandler("onMouseUp", "mouseUp"), new a.AVM1EventHandler("onPress", "mouseDown"), new a.AVM1EventHandler("onRelease", "mouseUp"), new a.AVM1EventHandler("onReleaseOutside", "releaseOutside"), new a.AVM1EventHandler("onRollOut", "mouseOut"), new a.AVM1EventHandler("onRollOver", "mouseOver"), new a.AVM1EventHandler("onSetFocus", "focusIn", function(a) { + return[a.relatedObject]; + })]); + }; + return h; + }(a.AVM1SymbolBase); + a.AVM1TextField = c; + })(c.Lib || (c.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var h = c.AVM2.AS.flash, v = function() { + function c(h) { + this._target = a.AVM1Utils.resolveTarget(h); + } + c.createAVM1Class = function() { + return a.wrapAVM1Class(c, [], ["getRGB", "getTransform", "setRGB", "setTransform"]); + }; + c.prototype.getRGB = function() { + return this.getTransform().asGetPublicProperty("color"); + }; + c.prototype.getTransform = function() { + return this._target.as3Object.transform.colorTransform; + }; + c.prototype.setRGB = function(a) { + var c = new h.geom.ColorTransform; + c.asSetPublicProperty("color", a); + this.setTransform(c); + }; + c.prototype.setTransform = function(a) { + this._target.as3Object.transform.colorTransform = a; + }; + return c; + }(); + a.AVM1Color = v; + })(h.Lib || (h.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var h = c.AVM2.AS.flash, v = function() { + function c(h) { + this._target = a.AVM1Utils.resolveTarget(h); + } + c.createAVM1Class = function() { + return a.wrapAVM1Class(c, [], ["matrix", "concatenatedMatrix", "colorTransform", "pixelBounds"]); + }; + Object.defineProperty(c.prototype, "matrix", {get:function() { + return this._target.as3Object.transform.matrix; + }, set:function(a) { + if (a instanceof h.geom.Matrix) { + this._target.as3Object.transform.matrix = a; + } else { + if (null != a) { + var c = this.matrix; + a.asHasProperty(void 0, "a", 0) && (c.a = a.asGetPublicProperty("a")); + a.asHasProperty(void 0, "b", 0) && (c.b = a.asGetPublicProperty("b")); + a.asHasProperty(void 0, "c", 0) && (c.c = a.asGetPublicProperty("c")); + a.asHasProperty(void 0, "d", 0) && (c.d = a.asGetPublicProperty("d")); + a.asHasProperty(void 0, "tx", 0) && (c.tx = a.asGetPublicProperty("tx")); + a.asHasProperty(void 0, "ty", 0) && (c.ty = a.asGetPublicProperty("ty")); + this._target.as3Object.transform.matrix = c; } - return b + 1; - }; - c.prototype._duplicate = function(a, b, c) { - }; - c.prototype._gotoLabel = function(a) { - this._nativeAS3Object._gotoFrame(a, null); - }; - c.classInitializer = null; - c.initializer = null; - c.classSymbols = null; - c.instanceSymbols = ["__lookupChild!", "__targetPath!"]; + } + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "concatenatedMatrix", {get:function() { + return this._target.as3Object.transform.concatenatedMatrix; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "colorTransform", {get:function() { + return this._target.as3Object.transform.colorTransform; + }, set:function(a) { + this._target.as3Object.transform.colorTransform = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "pixelBounds", {get:function() { + return this._target.as3Object.pixelBounds; + }, enumerable:!0, configurable:!0}); + return c; + }(); + a.AVM1Transform = v; + })(h.Lib || (h.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var h = c.Debug.notImplemented, v = Object.prototype.asGetProperty, p = Object.prototype.asSetProperty, u = Object.prototype.asCallProperty, l = Object.prototype.asHasProperty, e = Object.prototype.asHasOwnProperty, m = Object.prototype.asHasTraitProperty, t = Object.prototype.asDeleteProperty, q = function(c) { + function k() { + } + __extends(k, c); + k.prototype.setTarget = function(a) { + this._target = a; + }; + k.prototype._isInternalProperty = function(a, c, b) { + return!this._target || a || "__proto__" === c || "__constructor__" === c ? !0 : !1; + }; + k.prototype.asGetProperty = function(a, c, b) { + return this._isInternalProperty(a, c, b) ? v.call(this, a, c, b) : this._target.asGetPublicProperty(c); + }; + k.prototype.asGetNumericProperty = function(a) { + return this._target.asGetNumericProperty(a); + }; + k.prototype.asSetNumericProperty = function(a, c) { + return this._target.asSetNumericProperty(a, c); + }; + k.prototype.asSetProperty = function(a, c, b, e) { + if (this._isInternalProperty(a, c, b)) { + p.call(this, a, c, b, e); + } else { + return this._target.asSetPublicProperty(c, e); + } + }; + k.prototype.asCallProperty = function(a, c, b, e, k) { + return this._isInternalProperty(a, c, b) ? u.call(this, a, c, b, !1, k) : this._target.asCallPublicProperty(c, k); + }; + k.prototype.asHasProperty = function(a, c, b) { + return this._isInternalProperty(a, c, b) ? l.call(this, a, c, b) : this._target.asHasProperty(void 0, c, 0); + }; + k.prototype.asHasOwnProperty = function(a, c, b) { + return this._isInternalProperty(a, c, b) ? e.call(this, a, c, b) : this._target.asHasOwnProperty(void 0, c, 0); + }; + k.prototype.asDeleteProperty = function(a, c, b) { + if (m.call(this, a, c, b)) { + return t.call(this, a, c, b); + } + h("AVM1Proxy asDeleteProperty"); + return!1; + }; + k.prototype.asNextName = function(a) { + h("AVM1Proxy asNextName"); + }; + k.prototype.asNextValue = function(a) { + h("AVM1Proxy asNextValue"); + }; + k.prototype.asNextNameIndex = function(a) { + h("AVM1Proxy asNextNameIndex"); + }; + k.prototype.proxyNativeMethod = function(a) { + this._target.asSetPublicProperty(a, this._target[a].bind(this._target)); + }; + k.wrap = function(c, d) { + function b() { + var b = Object.create(c.prototype); + a.AVM1TextFormat.apply(b, arguments); + this.setTarget(b); + d && d.methods && d.methods.forEach(this.proxyNativeMethod, this); + } + b.prototype = k.prototype; + return b; + }; + return k; + }(c.AVM2.AS.ASObject); + a.AVM1Proxy = q; + })(h.Lib || (h.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var h = c.AVM2.AS.flash, v = c.AVM2.Runtime.asCoerceString, p = function(c) { + function l(a, c, l, p, n, k, f, d, b, g, r, w, u) { + h.text.TextFormat.apply(this, arguments); + } + __extends(l, c); + l.createAVM1Class = function() { + return a.AVM1Proxy.wrap(l, {methods:["getTextExtent"]}); + }; + l.prototype.getTextExtent = function(a, c) { + a = v(a); + c = +c; + var p = l._measureTextField; + p || (p = new h.text.TextField, p.multiline = !0, l._measureTextField = p); + !isNaN(c) && 0 < c ? (p.width = c + 4, p.wordWrap = !0) : p.wordWrap = !1; + p.defaultTextFormat = this; + p.text = a; + var q = {}, n = p.textWidth, k = p.textHeight; + q.asSetPublicProperty("width", n); + q.asSetPublicProperty("height", k); + q.asSetPublicProperty("textFieldWidth", n + 4); + q.asSetPublicProperty("textFieldHeight", k + 4); + p = p.getLineMetrics(); + q.asSetPublicProperty("ascent", p.asGetPublicProperty("ascent")); + q.asSetPublicProperty("descent", p.asGetPublicProperty("descent")); + return q; + }; + return l; + }(h.text.TextFormat); + a.AVM1TextFormat = p; + })(h.Lib || (h.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var s = c.AVM2.AS.flash, v = function(c) { + function u() { + c.apply(this, arguments); + } + __extends(u, c); + u.createAVM1Class = function() { + var c = a.AVM1Proxy.wrap(u, null); + c.asSetPublicProperty("loadBitmap", u.loadBitmap); return c; - }(g.ASNative); - f.AVM1MovieClip = d; - })(g.avm1lib || (g.avm1lib = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + }; + u.loadBitmap = function(a) { + a = asCoerceString(a); + return(a = h.AVM1Context.instance.getAsset(a)) && a.symbolProps instanceof s.display.BitmapSymbol ? (a = u.initializeFrom(a), a.class.instanceConstructorNoInitialize.call(a), a) : null; + }; + return u; + }(s.display.BitmapData); + a.AVM1BitmapData = v; + })(h.Lib || (h.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - var k = function(b) { - function f() { +(function(c) { + (function(h) { + (function(a) { + var h = c.AVM2.AS.flash, v = function() { + function c() { + } + c.createAVM1Class = function() { + return a.wrapAVM1Class(c, ["available", "addCallback", "call"], []); + }; + Object.defineProperty(c, "available", {get:function() { + return h.external.ExternalInterface.asGetPublicProperty("available"); + }, enumerable:!0, configurable:!0}); + c.addCallback = function(a, c, e) { + try { + return h.external.ExternalInterface.asCallPublicProperty("addCallback", [a, function() { + return e.apply(c, arguments); + }]), !0; + } catch (m) { } - __extends(f, b); - Object.defineProperty(f.prototype, "_as3Object", {get:function() { - return this._nativeAS3Object; - }, enumerable:!0, configurable:!0}); - f.prototype._setAS3Object = function(b) { - this._nativeAS3Object = b; - }; - Object.defineProperty(f.prototype, "_bytesLoaded", {get:function() { - return this._nativeAS3Object._contentLoaderInfo._bytesLoaded; - }, enumerable:!0, configurable:!0}); - f.classInitializer = null; - f.initializer = null; - f.classSymbols = null; - f.instanceSymbols = null; - return f; - }(b.ASNative); - f.AVM1MovieClipLoader = k; - })(b.avm1lib || (b.avm1lib = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - var g = b.AVM2.Runtime.asCoerceString, k = b.AVM1.AVM1Context, m = function(d) { - function a(a, b, d) { - } - __extends(a, d); - a.loadBitmap = function(c) { - c = g(c); - return(c = k.instance.getAsset(c)) && c.symbolProps instanceof b.Timeline.BitmapSymbol ? (c = a.initializeFrom(c), c.class.instanceConstructorNoInitialize.call(c), c) : null; - }; - a.classInitializer = null; - a.initializer = null; - a.classSymbols = null; - a.instanceSymbols = null; - return a; - }(b.AVM2.AS.flash.display.BitmapData); - f.AVM1BitmapData = m; - })(g.avm1lib || (g.avm1lib = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); + return!1; + }; + c.call = function(a) { + var c = Array.prototype.slice.call(arguments, 0); + return h.external.ExternalInterface.asCallPublicProperty("call", c); + }; + return c; + }(); + a.AVM1ExternalInterface = v; + })(h.Lib || (h.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - (function(f) { - var k = function(b) { - function g(b) { - this._variable = ""; - this._init(b); - } - __extends(g, b); - g.prototype._init = function(b) { - this._nativeAS3Object = b; - b._as2Object = this; - f.initDefaultListeners(this); - }; - Object.defineProperty(g.prototype, "_as3Object", {get:function() { - return this._nativeAS3Object; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(g.prototype, "variable", {get:function() { - return this._variable; - }, set:function(b) { - if (b !== this._variable) { - this._variable = b; - var a = this._nativeAS3Object, c; - if (0 <= b.indexOf(".") || 0 <= b.indexOf(":")) { - var g = b.split(/[.:\/]/g); - b = g.pop(); - "_root" == g[0] || "" === g[0] ? (c = f.getAVM1Object(a.root), g.shift(), "" === g[0] && g.shift()) : c = f.getAVM1Object(a._parent); - for (;0 < g.length;) { - var k = g.shift(); - c = c.asGetPublicProperty(k) || c[k]; - if (!c) { - throw Error("Cannot find " + k + " variable"); - } - } - } else { - c = f.getAVM1Object(a._parent); - } - c.asHasProperty(void 0, b, 0) || c.asSetPublicProperty(b, a.text); - a._addEventListener("advanceFrame", function() { - a.text = "" + c.asGetPublicProperty(b); - }); - } - }, enumerable:!0, configurable:!0}); - g.classInitializer = null; - g.initializer = null; - g.classSymbols = null; - g.instanceSymbols = null; - return g; - }(b.ASNative); - f.AVM1TextField = k; - })(b.avm1lib || (b.avm1lib = {})); - })(b.AS || (b.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - (function(f) { - var g = b.AVM2.AS.flash.text.TextField, k = function(b) { - function d() { - b.apply(this, arguments); - } - __extends(d, b); - d.prototype._as2GetTextExtent = function(a, b) { - var f = d._measureTextField; - f || (f = new g, f.multiline = !0, d._measureTextField = f); - !isNaN(b) && 0 < b ? (f.width = b + 4, f.wordWrap = !0) : f.wordWrap = !1; - f.defaultTextFormat = this; - f.text = a; - var k = {}, e = f.textWidth, m = f.textHeight; - k.asSetPublicProperty("width", e); - k.asSetPublicProperty("height", m); - k.asSetPublicProperty("textFieldWidth", e + 4); - k.asSetPublicProperty("textFieldHeight", m + 4); - f = f.getLineMetrics(); - k.asSetPublicProperty("ascent", f.asGetPublicProperty("ascent")); - k.asSetPublicProperty("descent", f.asGetPublicProperty("descent")); - return k; - }; - return d; - }(b.AVM2.AS.flash.text.TextFormat); - f.AVM1TextFormat = k; - })(g.avm1lib || (g.avm1lib = {})); - })(k.AS || (k.AS = {})); - })(b.AVM2 || (b.AVM2 = {})); +(function(c) { + (function(h) { + (function(a) { + var h = c.AVM2.AS.flash, v = function(c) { + function u(h) { + c.call(this); + this._target = a.AVM1Utils.resolveTarget(h); + this._linkageID = this._channel = this._sound = null; + } + __extends(u, c); + u.createAVM1Class = function() { + return a.wrapAVM1Class(u, [], "attachSound duration getBytesLoaded getBytesTotal getPan setPan getTransform setTransform getVolume setVolume start stop".split(" ")); + }; + u.prototype.attachSound = function(a) { + var c = this.context.getAsset(a); + c && (c = Object.create(c.symbolProps), c = h.media.Sound.initializeFrom(c), h.media.Sound.instanceConstructorNoInitialize.call(c), this._linkageID = a, this._sound = c); + }; + u.prototype.loadSound = function(a, c) { + }; + u.prototype.getBytesLoaded = function() { + return 0; + }; + u.prototype.getBytesTotal = function() { + return 0; + }; + u.prototype.getPan = function() { + var a = this._channel && this._channel.soundTransform; + return a ? 100 * a.asGetPublicProperty("pan") : 0; + }; + u.prototype.setPan = function(a) { + var c = this._channel && this._channel.soundTransform; + c && (c.asSetPublicProperty("pan", a / 100), this._channel.soundTransform = c); + }; + u.prototype.getTransform = function() { + return null; + }; + u.prototype.setTransform = function(a) { + }; + u.prototype.getVolume = function() { + var a = this._channel && this._channel.soundTransform; + return a ? 100 * a.asGetPublicProperty("volume") : 0; + }; + u.prototype.setVolume = function(a) { + var c = this._channel && this._channel.soundTransform; + c && (c.asSetPublicProperty("volume", a / 100), this._channel.soundTransform = c); + }; + u.prototype.start = function(a, c) { + this._sound && (a = isNaN(a) || 0 > a ? 0 : +a, c = isNaN(c) || 1 > c ? 1 : Math.floor(c), this._stopSoundChannel(), this._channel = this._sound.play(a, c - 1)); + }; + u.prototype._stopSoundChannel = function() { + this._channel && (this._channel.stop(), this._channel = null); + }; + u.prototype.stop = function(a) { + a && a !== this._linkageID || this._stopSoundChannel(); + }; + return u; + }(a.AVM1NativeObject); + a.AVM1Sound = v; + })(h.Lib || (h.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var h = c.AVM2.AS.flash, v = function() { + function c() { + } + c.createAVM1Class = function() { + return a.wrapAVM1Class(c, ["capabilities", "security"], []); + }; + Object.defineProperty(c, "capabilities", {get:function() { + return h.system.Capabilities; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c, "security", {get:function() { + return h.system.Security; + }, enumerable:!0, configurable:!0}); + return c; + }(); + a.AVM1System = v; + })(h.Lib || (h.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var h = c.AVM2.AS.flash, v = function() { + function c() { + this._loader = new h.display.Loader; + } + c.createAVM1Class = function() { + return a.wrapAVM1Class(c, [], ["loadClip", "unloadClip", "getProgress"]); + }; + c.prototype.initAVM1ObjectInstance = function(a) { + }; + c.prototype.loadClip = function(c, l) { + this._target = "number" === typeof l ? a.AVM1Utils.resolveLevel(l) : a.AVM1Utils.resolveTarget(l); + this._target.as3Object.addChild(this._loader); + this._loader.contentLoaderInfo.addEventListener(h.events.Event.OPEN, this.openHandler.bind(this)); + this._loader.contentLoaderInfo.addEventListener(h.events.ProgressEvent.PROGRESS, this.progressHandler.bind(this)); + this._loader.contentLoaderInfo.addEventListener(h.events.IOErrorEvent.IO_ERROR, this.ioErrorHandler.bind(this)); + this._loader.contentLoaderInfo.addEventListener(h.events.Event.COMPLETE, this.completeHandler.bind(this)); + this._loader.contentLoaderInfo.addEventListener(h.events.Event.INIT, this.initHandler.bind(this)); + this._loader.load(new h.net.URLRequest(c)); + return!0; + }; + c.prototype.unloadClip = function(c) { + ("number" === typeof c ? a.AVM1Utils.resolveLevel(c) : a.AVM1Utils.resolveTarget(c)).as3Object.removeChild(this._loader); + return!0; + }; + c.prototype.getProgress = function(a) { + return this._loader.contentLoaderInfo.bytesLoaded; + }; + c.prototype._broadcastMessage = function(c, h) { + void 0 === h && (h = null); + a.avm1BroadcastEvent(this._target.context, this, c, h); + }; + c.prototype.openHandler = function(a) { + this._broadcastMessage("onLoadStart", [this._target]); + }; + c.prototype.progressHandler = function(a) { + this._broadcastMessage("onLoadProgress", [this._target, a.bytesLoaded, a.bytesTotal]); + }; + c.prototype.ioErrorHandler = function(a) { + this._broadcastMessage("onLoadError", [this._target, a.errorID, 501]); + }; + c.prototype.completeHandler = function(a) { + this._broadcastMessage("onLoadComplete", [this._target]); + }; + c.prototype.initHandler = function(a) { + var c = function() { + this._target.as3Object.removeEventListener(h.events.Event.EXIT_FRAME, c); + this._broadcastMessage("onLoadInit", [this._target]); + }.bind(this); + this._target.as3Object.addEventListener(h.events.Event.EXIT_FRAME, c); + }; + return c; + }(); + a.AVM1MovieClipLoader = v; + })(h.Lib || (h.Lib = {})); + })(c.AVM1 || (c.AVM1 = {})); })(Shumway || (Shumway = {})); console.timeEnd("Load AVM1 Dependencies"); -(function(b) { - (function(k) { - function g(a, d, h) { - return c && h ? "string" === typeof d ? (a = b.ColorUtilities.cssStyleToRGBA(d), b.ColorUtilities.rgbaToCSSStyle(h.transformRGBA(a))) : d instanceof CanvasGradient && d._template ? d._template.createCanvasGradient(a, h) : d : d; +(function(c) { + (function(h) { + function a(a, b, d) { + return m && d ? "string" === typeof b ? (a = c.ColorUtilities.cssStyleToRGBA(b), c.ColorUtilities.rgbaToCSSStyle(d.transformRGBA(a))) : b instanceof CanvasGradient && b._template ? b._template.createCanvasGradient(a, d) : b : b; } - var f = b.Debug.assert, t = b.NumberUtilities.clamp; + var s = c.Debug.assert, v = c.NumberUtilities.clamp; (function(a) { a[a.None = 0] = "None"; a[a.Brief = 1] = "Brief"; a[a.Verbose = 2] = "Verbose"; - })(k.TraceLevel || (k.TraceLevel = {})); - var s = b.Metrics.Counter.instance; - k.frameCounter = new b.Metrics.Counter(!0); - k.traceLevel = 2; - k.writer = null; - k.frameCount = function(a) { - s.count(a); - k.frameCounter.count(a); - }; - k.timelineBuffer = new b.Tools.Profiler.TimelineBuffer("GFX"); - k.enterTimeline = function(a, b) { - }; - k.leaveTimeline = function(a, b) { - }; - var m = null, d = null, a = null, c = !0; - c && "undefined" !== typeof CanvasRenderingContext2D && (m = CanvasGradient.prototype.addColorStop, d = CanvasRenderingContext2D.prototype.createLinearGradient, a = CanvasRenderingContext2D.prototype.createRadialGradient, CanvasRenderingContext2D.prototype.createLinearGradient = function(a, b, c, d) { - return(new p(a, b, c, d)).createCanvasGradient(this, null); - }, CanvasRenderingContext2D.prototype.createRadialGradient = function(a, b, c, d, h, f) { - return(new e(a, b, c, d, h, f)).createCanvasGradient(this, null); + })(h.TraceLevel || (h.TraceLevel = {})); + var p = c.Metrics.Counter.instance; + h.frameCounter = new c.Metrics.Counter(!0); + h.traceLevel = 2; + h.writer = null; + h.frameCount = function(a) { + p.count(a); + h.frameCounter.count(a); + }; + h.timelineBuffer = new c.Tools.Profiler.TimelineBuffer("GFX"); + h.enterTimeline = function(a, b) { + }; + h.leaveTimeline = function(a, b) { + }; + var u = null, l = null, e = null, m = !0; + m && "undefined" !== typeof CanvasRenderingContext2D && (u = CanvasGradient.prototype.addColorStop, l = CanvasRenderingContext2D.prototype.createLinearGradient, e = CanvasRenderingContext2D.prototype.createRadialGradient, CanvasRenderingContext2D.prototype.createLinearGradient = function(a, b, c, d) { + return(new q(a, b, c, d)).createCanvasGradient(this, null); + }, CanvasRenderingContext2D.prototype.createRadialGradient = function(a, b, c, d, e, g) { + return(new n(a, b, c, d, e, g)).createCanvasGradient(this, null); }, CanvasGradient.prototype.addColorStop = function(a, b) { - m.call(this, a, b); + u.call(this, a, b); this._template.addColorStop(a, b); }); - var n = function() { + var t = function() { return function(a, b) { this.offset = a; this.color = b; }; - }(), p = function() { - function a(b, c, d, h) { - this.x0 = b; + }(), q = function() { + function b(a, c, d, e) { + this.x0 = a; this.y0 = c; this.x1 = d; - this.y1 = h; + this.y1 = e; this.colorStops = []; } - a.prototype.addColorStop = function(a, b) { - this.colorStops.push(new n(a, b)); + b.prototype.addColorStop = function(a, b) { + this.colorStops.push(new t(a, b)); }; - a.prototype.createCanvasGradient = function(a, b) { - for (var c = d.call(a, this.x0, this.y0, this.x1, this.y1), h = this.colorStops, e = 0;e < h.length;e++) { - var f = h[e], k = f.offset, f = f.color, f = b ? g(a, f, b) : f; - m.call(c, k, f); + b.prototype.createCanvasGradient = function(b, c) { + for (var d = l.call(b, this.x0, this.y0, this.x1, this.y1), e = this.colorStops, g = 0;g < e.length;g++) { + var f = e[g], k = f.offset, f = f.color, f = c ? a(b, f, c) : f; + u.call(d, k, f); } - c._template = this; - c._transform = this._transform; - return c; + d._template = this; + d._transform = this._transform; + return d; }; - return a; - }(), e = function() { - function b(a, c, d, h, f, e) { + return b; + }(), n = function() { + function b(a, c, d, e, g, f) { this.x0 = a; this.y0 = c; this.r0 = d; - this.x1 = h; - this.y1 = f; - this.r1 = e; + this.x1 = e; + this.y1 = g; + this.r1 = f; this.colorStops = []; } b.prototype.addColorStop = function(a, b) { - this.colorStops.push(new n(a, b)); + this.colorStops.push(new t(a, b)); }; b.prototype.createCanvasGradient = function(b, c) { - for (var d = a.call(b, this.x0, this.y0, this.r0, this.x1, this.y1, this.r1), h = this.colorStops, f = 0;f < h.length;f++) { - var e = h[f], k = e.offset, e = e.color, e = c ? g(b, e, c) : e; - m.call(d, k, e); + for (var d = e.call(b, this.x0, this.y0, this.r0, this.x1, this.y1, this.r1), g = this.colorStops, f = 0;f < g.length;f++) { + var k = g[f], h = k.offset, k = k.color, k = c ? a(b, k, c) : k; + u.call(d, h, k); } d._template = this; d._transform = this._transform; return d; }; return b; - }(), q; + }(), k; (function(a) { a[a.ClosePath = 1] = "ClosePath"; a[a.MoveTo = 2] = "MoveTo"; @@ -39044,8 +48463,8 @@ a[a.Save = 9] = "Save"; a[a.Restore = 10] = "Restore"; a[a.Transform = 11] = "Transform"; - })(q || (q = {})); - var l = function() { + })(k || (k = {})); + var f = function() { function a(b) { this._commands = new Uint8Array(a._arrayBufferPool.acquire(8), 0, 8); this._commandPosition = 0; @@ -39054,33 +48473,33 @@ b instanceof a && this.addPath(b); } a._apply = function(a, b) { - var c = a._commands, d = a._data, h = 0, e = 0; + var c = a._commands, d = a._data, e = 0, g = 0; b.beginPath(); - for (var f = a._commandPosition;h < f;) { - switch(c[h++]) { + for (var f = a._commandPosition;e < f;) { + switch(c[e++]) { case 1: b.closePath(); break; case 2: - b.moveTo(d[e++], d[e++]); + b.moveTo(d[g++], d[g++]); break; case 3: - b.lineTo(d[e++], d[e++]); + b.lineTo(d[g++], d[g++]); break; case 4: - b.quadraticCurveTo(d[e++], d[e++], d[e++], d[e++]); + b.quadraticCurveTo(d[g++], d[g++], d[g++], d[g++]); break; case 5: - b.bezierCurveTo(d[e++], d[e++], d[e++], d[e++], d[e++], d[e++]); + b.bezierCurveTo(d[g++], d[g++], d[g++], d[g++], d[g++], d[g++]); break; case 6: - b.arcTo(d[e++], d[e++], d[e++], d[e++], d[e++]); + b.arcTo(d[g++], d[g++], d[g++], d[g++], d[g++]); break; case 7: - b.rect(d[e++], d[e++], d[e++], d[e++]); + b.rect(d[g++], d[g++], d[g++], d[g++]); break; case 8: - b.arc(d[e++], d[e++], d[e++], d[e++], d[e++], !!d[e++]); + b.arc(d[g++], d[g++], d[g++], d[g++], d[g++], !!d[g++]); break; case 9: b.save(); @@ -39089,7 +48508,7 @@ b.restore(); break; case 11: - b.transform(d[e++], d[e++], d[e++], d[e++], d[e++], d[e++]); + b.transform(d[g++], d[g++], d[g++], d[g++], d[g++], d[g++]); } } }; @@ -39103,15 +48522,15 @@ this._commandPosition >= this._commands.length && this._ensureCommandCapacity(this._commandPosition + 1); this._commands[this._commandPosition++] = a; }; - a.prototype._writeData = function(a, b, c, d, h, e) { - var g = arguments.length; - f(6 >= g && (0 === g % 2 || 5 === g)); - this._dataPosition + g >= this._data.length && this._ensureDataCapacity(this._dataPosition + g); - var k = this._data, l = this._dataPosition; - k[l] = a; - k[l + 1] = b; - 2 < g && (k[l + 2] = c, k[l + 3] = d, 4 < g && (k[l + 4] = h, 6 === g && (k[l + 5] = e))); - this._dataPosition += g; + a.prototype._writeData = function(a, b, c, d, e, g) { + var f = arguments.length; + s(6 >= f && (0 === f % 2 || 5 === f)); + this._dataPosition + f >= this._data.length && this._ensureDataCapacity(this._dataPosition + f); + var k = this._data, h = this._dataPosition; + k[h] = a; + k[h + 1] = b; + 2 < f && (k[h + 2] = c, k[h + 3] = d, 4 < f && (k[h + 4] = e, 6 === f && (k[h + 5] = g))); + this._dataPosition += f; }; a.prototype.closePath = function() { this._writeCommand(1); @@ -39128,71 +48547,71 @@ this._writeCommand(4); this._writeData(a, b, c, d); }; - a.prototype.bezierCurveTo = function(a, b, c, d, h, e) { + a.prototype.bezierCurveTo = function(a, b, c, d, e, g) { this._writeCommand(5); - this._writeData(a, b, c, d, h, e); + this._writeData(a, b, c, d, e, g); }; - a.prototype.arcTo = function(a, b, c, d, h) { + a.prototype.arcTo = function(a, b, c, d, e) { this._writeCommand(6); - this._writeData(a, b, c, d, h); + this._writeData(a, b, c, d, e); }; a.prototype.rect = function(a, b, c, d) { this._writeCommand(7); this._writeData(a, b, c, d); }; - a.prototype.arc = function(a, b, c, d, h, e) { + a.prototype.arc = function(a, b, c, d, e, g) { this._writeCommand(8); - this._writeData(a, b, c, d, h, +e); + this._writeData(a, b, c, d, e, +g); }; a.prototype.addPath = function(a, b) { b && (this._writeCommand(9), this._writeCommand(11), this._writeData(b.a, b.b, b.c, b.d, b.e, b.f)); var c = this._commandPosition + a._commandPosition; c >= this._commands.length && this._ensureCommandCapacity(c); - for (var d = this._commands, h = a._commands, e = this._commandPosition, f = 0;e < c;e++) { - d[e] = h[f++]; + for (var d = this._commands, e = a._commands, g = this._commandPosition, f = 0;g < c;g++) { + d[g] = e[f++]; } this._commandPosition = c; c = this._dataPosition + a._dataPosition; c >= this._data.length && this._ensureDataCapacity(c); d = this._data; - h = a._data; - e = this._dataPosition; - for (f = 0;e < c;e++) { - d[e] = h[f++]; + e = a._data; + g = this._dataPosition; + for (f = 0;g < c;g++) { + d[g] = e[f++]; } this._dataPosition = c; b && this._writeCommand(10); }; - a._arrayBufferPool = new b.ArrayBufferPool; + a._arrayBufferPool = new c.ArrayBufferPool; return a; }(); - k.Path = l; + h.Path = f; if ("undefined" !== typeof CanvasRenderingContext2D && ("undefined" === typeof Path2D || !Path2D.prototype.addPath)) { - var u = CanvasRenderingContext2D.prototype.fill; + var d = CanvasRenderingContext2D.prototype.fill; CanvasRenderingContext2D.prototype.fill = function(a, b) { - arguments.length && (a instanceof l ? l._apply(a, this) : b = a); - b ? u.call(this, b) : u.call(this); + arguments.length && (a instanceof f ? f._apply(a, this) : b = a); + b ? d.call(this, b) : d.call(this); }; - var w = CanvasRenderingContext2D.prototype.stroke; - CanvasRenderingContext2D.prototype.stroke = function(a, b) { - arguments.length && (a instanceof l ? l._apply(a, this) : b = a); - b ? w.call(this, b) : w.call(this); + var b = CanvasRenderingContext2D.prototype.stroke; + CanvasRenderingContext2D.prototype.stroke = function(a, c) { + arguments.length && (a instanceof f ? f._apply(a, this) : c = a); + c ? b.call(this, c) : b.call(this); }; - var r = CanvasRenderingContext2D.prototype.clip; + var g = CanvasRenderingContext2D.prototype.clip; CanvasRenderingContext2D.prototype.clip = function(a, b) { - arguments.length && (a instanceof l ? l._apply(a, this) : b = a); - b ? r.call(this, b) : r.call(this); + arguments.length && (a instanceof f ? f._apply(a, this) : b = a); + b ? g.call(this, b) : g.call(this); }; - window.Path2D = l; + window.Path2D = f; } if ("undefined" !== typeof CanvasPattern && Path2D.prototype.addPath) { - q = function(a) { + k = function(a) { this._transform = a; this._template && (this._template._transform = a); }; - CanvasPattern.prototype.setTransform || (CanvasPattern.prototype.setTransform = q); - CanvasGradient.prototype.setTransform || (CanvasGradient.prototype.setTransform = q); - var h = CanvasRenderingContext2D.prototype.fill, x = CanvasRenderingContext2D.prototype.stroke; + CanvasPattern.prototype.setTransform || (CanvasPattern.prototype.setTransform = k); + CanvasGradient.prototype.setTransform || (CanvasGradient.prototype.setTransform = k); + var r = CanvasRenderingContext2D.prototype.fill, w = CanvasRenderingContext2D.prototype.stroke; CanvasRenderingContext2D.prototype.fill = function(a, b) { var c = !!this.fillStyle._transform; if ((this.fillStyle instanceof CanvasPattern || this.fillStyle instanceof CanvasGradient) && c && a instanceof Path2D) { @@ -39200,15 +48619,15 @@ try { d = c.inverse(); } catch (e) { - d = c = k.Geometry.Matrix.createIdentitySVGMatrix(); + d = c = h.Geometry.Matrix.createIdentitySVGMatrix(); } this.transform(c.a, c.b, c.c, c.d, c.e, c.f); c = new Path2D; c.addPath(a, d); - h.call(this, c, b); + r.call(this, c, b); this.transform(d.a, d.b, d.c, d.d, d.e, d.f); } else { - 0 === arguments.length ? h.call(this) : 1 === arguments.length ? h.call(this, a) : 2 === arguments.length && h.call(this, a, b); + 0 === arguments.length ? r.call(this) : 1 === arguments.length ? r.call(this, a) : 2 === arguments.length && r.call(this, a, b); } }; CanvasRenderingContext2D.prototype.stroke = function(a) { @@ -39220,208 +48639,220 @@ c.addPath(a, b); var d = this.lineWidth; this.lineWidth *= (b.a + b.d) / 2; - x.call(this, c); + w.call(this, c); this.transform(b.a, b.b, b.c, b.d, b.e, b.f); this.lineWidth = d; } else { - 0 === arguments.length ? x.call(this) : 1 === arguments.length && x.call(this, a); + 0 === arguments.length ? w.call(this) : 1 === arguments.length && w.call(this, a); } }; } "undefined" !== typeof CanvasRenderingContext2D && function() { - function a(b) { - return b.a; - } - function b(a) { - return a.d; + function a() { + s(this.mozCurrentTransform); + return h.Geometry.Matrix.createSVGMatrixFromArray(this.mozCurrentTransform); } - CanvasRenderingContext2D.prototype.flashStroke = function(c, d) { - var h = this.currentTransform; - if (!h) { - if (h = this.mozCurrentTransform) { - h = k.Geometry.Matrix.createSVGMatrixFromArray(h); - } else { - this.stroke(c); - return; + CanvasRenderingContext2D.prototype.flashStroke = function(a, b) { + var d = this.currentTransform; + if (d) { + var e = new Path2D; + e.addPath(a, d); + var g = this.lineWidth; + this.setTransform(1, 0, 0, 1, 0, 0); + switch(b) { + case 1: + this.lineWidth = v(g * (c.getScaleX(d) + c.getScaleY(d)) / 2, 1, 1024); + break; + case 2: + this.lineWidth = v(g * c.getScaleY(d), 1, 1024); + break; + case 3: + this.lineWidth = v(g * c.getScaleX(d), 1, 1024); } + this.stroke(e); + this.setTransform(d.a, d.b, d.c, d.d, d.e, d.f); + this.lineWidth = g; + } else { + this.stroke(a); } - var e = new Path2D; - e.addPath(c, h); - var f = this.lineWidth; - this.setTransform(1, 0, 0, 1, 0, 0); - switch(d) { - case 1: - this.lineWidth = t(f * (a(h) + b(h)) / 2, 1, 1024); - break; - case 2: - this.lineWidth = t(f * b(h), 1, 1024); - break; - case 3: - this.lineWidth = t(f * a(h), 1, 1024); - } - this.stroke(e); - this.setTransform(h.a, h.b, h.c, h.d, h.e, h.f); - this.lineWidth = f; }; + !("currentTransform" in CanvasRenderingContext2D.prototype) && "mozCurrentTransform" in CanvasRenderingContext2D.prototype && Object.defineProperty(CanvasRenderingContext2D.prototype, "currentTransform", {get:a}); }(); if ("undefined" !== typeof CanvasRenderingContext2D && void 0 === CanvasRenderingContext2D.prototype.globalColorMatrix) { - var y = CanvasRenderingContext2D.prototype.fill, G = CanvasRenderingContext2D.prototype.stroke, I = CanvasRenderingContext2D.prototype.fillText, C = CanvasRenderingContext2D.prototype.strokeText; + var z = CanvasRenderingContext2D.prototype.fill, A = CanvasRenderingContext2D.prototype.stroke, B = CanvasRenderingContext2D.prototype.fillText, M = CanvasRenderingContext2D.prototype.strokeText; Object.defineProperty(CanvasRenderingContext2D.prototype, "globalColorMatrix", {get:function() { return this._globalColorMatrix ? this._globalColorMatrix.clone() : null; }, set:function(a) { a ? this._globalColorMatrix ? this._globalColorMatrix.set(a) : this._globalColorMatrix = a.clone() : this._globalColorMatrix = null; }, enumerable:!0, configurable:!0}); - CanvasRenderingContext2D.prototype.fill = function(a, b) { - var c = null; - this._globalColorMatrix && (c = this.fillStyle, this.fillStyle = g(this, this.fillStyle, this._globalColorMatrix)); - 0 === arguments.length ? y.call(this) : 1 === arguments.length ? y.call(this, a) : 2 === arguments.length && y.call(this, a, b); - c && (this.fillStyle = c); - }; - CanvasRenderingContext2D.prototype.stroke = function(a, b) { - var c = null; - this._globalColorMatrix && (c = this.strokeStyle, this.strokeStyle = g(this, this.strokeStyle, this._globalColorMatrix)); - 0 === arguments.length ? G.call(this) : 1 === arguments.length && G.call(this, a); - c && (this.strokeStyle = c); - }; - CanvasRenderingContext2D.prototype.fillText = function(a, c, d, h) { - var e = null; - this._globalColorMatrix && (e = this.fillStyle, this.fillStyle = g(this, this.fillStyle, this._globalColorMatrix)); - 3 === arguments.length ? I.call(this, a, c, d) : 4 === arguments.length ? I.call(this, a, c, d, h) : b.Debug.unexpected(); - e && (this.fillStyle = e); - }; - CanvasRenderingContext2D.prototype.strokeText = function(a, c, d, h) { - var e = null; - this._globalColorMatrix && (e = this.strokeStyle, this.strokeStyle = g(this, this.strokeStyle, this._globalColorMatrix)); - 3 === arguments.length ? C.call(this, a, c, d) : 4 === arguments.length ? C.call(this, a, c, d, h) : b.Debug.unexpected(); - e && (this.strokeStyle = e); - }; - } - })(b.GFX || (b.GFX = {})); -})(Shumway || (Shumway = {})); -(function(b) { - var k = b.Debug.assert, g = function() { - function b() { + CanvasRenderingContext2D.prototype.fill = function(b, c) { + var d = null; + this._globalColorMatrix && (d = this.fillStyle, this.fillStyle = a(this, this.fillStyle, this._globalColorMatrix)); + 0 === arguments.length ? z.call(this) : 1 === arguments.length ? z.call(this, b) : 2 === arguments.length && z.call(this, b, c); + d && (this.fillStyle = d); + }; + CanvasRenderingContext2D.prototype.stroke = function(b, c) { + var d = null; + this._globalColorMatrix && (d = this.strokeStyle, this.strokeStyle = a(this, this.strokeStyle, this._globalColorMatrix)); + 0 === arguments.length ? A.call(this) : 1 === arguments.length && A.call(this, b); + d && (this.strokeStyle = d); + }; + CanvasRenderingContext2D.prototype.fillText = function(b, d, e, g) { + var f = null; + this._globalColorMatrix && (f = this.fillStyle, this.fillStyle = a(this, this.fillStyle, this._globalColorMatrix)); + 3 === arguments.length ? B.call(this, b, d, e) : 4 === arguments.length ? B.call(this, b, d, e, g) : c.Debug.unexpected(); + f && (this.fillStyle = f); + }; + CanvasRenderingContext2D.prototype.strokeText = function(b, d, e, g) { + var f = null; + this._globalColorMatrix && (f = this.strokeStyle, this.strokeStyle = a(this, this.strokeStyle, this._globalColorMatrix)); + 3 === arguments.length ? M.call(this, b, d, e) : 4 === arguments.length ? M.call(this, b, d, e, g) : c.Debug.unexpected(); + f && (this.strokeStyle = f); + }; + } + })(c.GFX || (c.GFX = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(c) { + c.ScreenShot = function() { + return function(a, c, h) { + this.dataURL = a; + this.w = c; + this.h = h; + }; + }(); + })(c.GFX || (c.GFX = {})); +})(Shumway || (Shumway = {})); +(function(c) { + var h = c.Debug.assert, a = function() { + function a() { this._count = 0; this._head = this._tail = null; } - Object.defineProperty(b.prototype, "count", {get:function() { + Object.defineProperty(a.prototype, "count", {get:function() { return this._count; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "head", {get:function() { + Object.defineProperty(a.prototype, "head", {get:function() { return this._head; }, enumerable:!0, configurable:!0}); - b.prototype._unshift = function(b) { - k(!b.next && !b.previous); - 0 === this._count ? this._head = this._tail = b : (b.next = this._head, this._head = b.next.previous = b); + a.prototype._unshift = function(a) { + h(!a.next && !a.previous); + 0 === this._count ? this._head = this._tail = a : (a.next = this._head, this._head = a.next.previous = a); this._count++; }; - b.prototype._remove = function(b) { - k(0 < this._count); - b === this._head && b === this._tail ? this._head = this._tail = null : b === this._head ? (this._head = b.next, this._head.previous = null) : b == this._tail ? (this._tail = b.previous, this._tail.next = null) : (b.previous.next = b.next, b.next.previous = b.previous); - b.previous = b.next = null; + a.prototype._remove = function(a) { + h(0 < this._count); + a === this._head && a === this._tail ? this._head = this._tail = null : a === this._head ? (this._head = a.next, this._head.previous = null) : a == this._tail ? (this._tail = a.previous, this._tail.next = null) : (a.previous.next = a.next, a.next.previous = a.previous); + a.previous = a.next = null; this._count--; }; - b.prototype.use = function(b) { - this._head !== b && ((b.next || b.previous || this._tail === b) && this._remove(b), this._unshift(b)); + a.prototype.use = function(a) { + this._head !== a && ((a.next || a.previous || this._tail === a) && this._remove(a), this._unshift(a)); }; - b.prototype.pop = function() { + a.prototype.pop = function() { if (!this._tail) { return null; } - var b = this._tail; - this._remove(b); - return b; + var a = this._tail; + this._remove(a); + return a; }; - b.prototype.visit = function(b, f) { - "undefined" === typeof f && (f = !0); - for (var g = f ? this._head : this._tail;g && b(g);) { - g = f ? g.next : g.previous; + a.prototype.visit = function(a, c) { + void 0 === c && (c = !0); + for (var h = c ? this._head : this._tail;h && a(h);) { + h = c ? h.next : h.previous; } }; - return b; + return a; }(); - b.LRUList = g; + c.LRUList = a; + c.getScaleX = function(a) { + return a.a; + }; + c.getScaleY = function(a) { + return a.d; + }; })(Shumway || (Shumway = {})); -var Shumway$$inline_404 = Shumway || (Shumway = {}), GFX$$inline_405 = Shumway$$inline_404.GFX || (Shumway$$inline_404.GFX = {}), Option$$inline_406 = Shumway$$inline_404.Options.Option, OptionSet$$inline_407 = Shumway$$inline_404.Options.OptionSet, shumwayOptions$$inline_408 = Shumway$$inline_404.Settings.shumwayOptions, rendererOptions$$inline_409 = shumwayOptions$$inline_408.register(new OptionSet$$inline_407("Renderer Options")); -GFX$$inline_405.imageUpdateOption = rendererOptions$$inline_409.register(new Option$$inline_406("", "imageUpdate", "boolean", !0, "Enable image updating.")); -GFX$$inline_405.imageConvertOption = rendererOptions$$inline_409.register(new Option$$inline_406("", "imageConvert", "boolean", !0, "Enable image conversion.")); -GFX$$inline_405.stageOptions = shumwayOptions$$inline_408.register(new OptionSet$$inline_407("Stage Renderer Options")); -GFX$$inline_405.forcePaint = GFX$$inline_405.stageOptions.register(new Option$$inline_406("", "forcePaint", "boolean", !1, "Force repainting.")); -GFX$$inline_405.ignoreViewport = GFX$$inline_405.stageOptions.register(new Option$$inline_406("", "ignoreViewport", "boolean", !1, "Cull elements outside of the viewport.")); -GFX$$inline_405.viewportLoupeDiameter = GFX$$inline_405.stageOptions.register(new Option$$inline_406("", "viewportLoupeDiameter", "number", 256, "Size of the viewport loupe.", {range:{min:1, max:1024, step:1}})); -GFX$$inline_405.disableClipping = GFX$$inline_405.stageOptions.register(new Option$$inline_406("", "disableClipping", "boolean", !1, "Disable clipping.")); -GFX$$inline_405.debugClipping = GFX$$inline_405.stageOptions.register(new Option$$inline_406("", "debugClipping", "boolean", !1, "Disable clipping.")); -GFX$$inline_405.backend = GFX$$inline_405.stageOptions.register(new Option$$inline_406("", "backend", "number", 0, "Backends", {choices:{Canvas2D:0, WebGL:1, Both:2}})); -GFX$$inline_405.hud = GFX$$inline_405.stageOptions.register(new Option$$inline_406("", "hud", "boolean", !1, "Enable HUD.")); -var webGLOptions$$inline_410 = GFX$$inline_405.stageOptions.register(new OptionSet$$inline_407("WebGL Options")); -GFX$$inline_405.perspectiveCamera = webGLOptions$$inline_410.register(new Option$$inline_406("", "pc", "boolean", !1, "Use perspective camera.")); -GFX$$inline_405.perspectiveCameraFOV = webGLOptions$$inline_410.register(new Option$$inline_406("", "pcFOV", "number", 60, "Perspective Camera FOV.")); -GFX$$inline_405.perspectiveCameraDistance = webGLOptions$$inline_410.register(new Option$$inline_406("", "pcDistance", "number", 2, "Perspective Camera Distance.")); -GFX$$inline_405.perspectiveCameraAngle = webGLOptions$$inline_410.register(new Option$$inline_406("", "pcAngle", "number", 0, "Perspective Camera Angle.")); -GFX$$inline_405.perspectiveCameraAngleRotate = webGLOptions$$inline_410.register(new Option$$inline_406("", "pcRotate", "boolean", !1, "Rotate Use perspective camera.")); -GFX$$inline_405.perspectiveCameraSpacing = webGLOptions$$inline_410.register(new Option$$inline_406("", "pcSpacing", "number", .01, "Element Spacing.")); -GFX$$inline_405.perspectiveCameraSpacingInflate = webGLOptions$$inline_410.register(new Option$$inline_406("", "pcInflate", "boolean", !1, "Rotate Use perspective camera.")); -GFX$$inline_405.drawTiles = webGLOptions$$inline_410.register(new Option$$inline_406("", "drawTiles", "boolean", !1, "Draw WebGL Tiles")); -GFX$$inline_405.drawSurfaces = webGLOptions$$inline_410.register(new Option$$inline_406("", "drawSurfaces", "boolean", !1, "Draw WebGL Surfaces.")); -GFX$$inline_405.drawSurface = webGLOptions$$inline_410.register(new Option$$inline_406("", "drawSurface", "number", -1, "Draw WebGL Surface #")); -GFX$$inline_405.drawElements = webGLOptions$$inline_410.register(new Option$$inline_406("", "drawElements", "boolean", !0, "Actually call gl.drawElements. This is useful to test if the GPU is the bottleneck.")); -GFX$$inline_405.disableSurfaceUploads = webGLOptions$$inline_410.register(new Option$$inline_406("", "disableSurfaceUploads", "boolean", !1, "Disable surface uploads.")); -GFX$$inline_405.premultipliedAlpha = webGLOptions$$inline_410.register(new Option$$inline_406("", "premultipliedAlpha", "boolean", !1, "Set the premultipliedAlpha flag on getContext().")); -GFX$$inline_405.unpackPremultiplyAlpha = webGLOptions$$inline_410.register(new Option$$inline_406("", "unpackPremultiplyAlpha", "boolean", !0, "Use UNPACK_PREMULTIPLY_ALPHA_WEBGL in pixelStorei.")); -var factorChoices$$inline_411 = {ZERO:0, ONE:1, SRC_COLOR:768, ONE_MINUS_SRC_COLOR:769, DST_COLOR:774, ONE_MINUS_DST_COLOR:775, SRC_ALPHA:770, ONE_MINUS_SRC_ALPHA:771, DST_ALPHA:772, ONE_MINUS_DST_ALPHA:773, SRC_ALPHA_SATURATE:776, CONSTANT_COLOR:32769, ONE_MINUS_CONSTANT_COLOR:32770, CONSTANT_ALPHA:32771, ONE_MINUS_CONSTANT_ALPHA:32772}; -GFX$$inline_405.sourceBlendFactor = webGLOptions$$inline_410.register(new Option$$inline_406("", "sourceBlendFactor", "number", factorChoices$$inline_411.ONE, "", {choices:factorChoices$$inline_411})); -GFX$$inline_405.destinationBlendFactor = webGLOptions$$inline_410.register(new Option$$inline_406("", "destinationBlendFactor", "number", factorChoices$$inline_411.ONE_MINUS_SRC_ALPHA, "", {choices:factorChoices$$inline_411})); -var canvas2DOptions$$inline_412 = GFX$$inline_405.stageOptions.register(new OptionSet$$inline_407("Canvas2D Options")); -GFX$$inline_405.clipDirtyRegions = canvas2DOptions$$inline_412.register(new Option$$inline_406("", "clipDirtyRegions", "boolean", !1, "Clip dirty regions.")); -GFX$$inline_405.clipCanvas = canvas2DOptions$$inline_412.register(new Option$$inline_406("", "clipCanvas", "boolean", !1, "Clip Regions.")); -GFX$$inline_405.cull = canvas2DOptions$$inline_412.register(new Option$$inline_406("", "cull", "boolean", !1, "Enable culling.")); -GFX$$inline_405.compositeMask = canvas2DOptions$$inline_412.register(new Option$$inline_406("", "compositeMask", "boolean", !1, "Composite Mask.")); -GFX$$inline_405.snapToDevicePixels = canvas2DOptions$$inline_412.register(new Option$$inline_406("", "snapToDevicePixels", "boolean", !1, "")); -GFX$$inline_405.imageSmoothing = canvas2DOptions$$inline_412.register(new Option$$inline_406("", "imageSmoothing", "boolean", !1, "")); -GFX$$inline_405.blending = canvas2DOptions$$inline_412.register(new Option$$inline_406("", "blending", "boolean", !0, "")); -GFX$$inline_405.cacheShapes = canvas2DOptions$$inline_412.register(new Option$$inline_406("", "cacheShapes", "boolean", !1, "")); -GFX$$inline_405.cacheShapesMaxSize = canvas2DOptions$$inline_412.register(new Option$$inline_406("", "cacheShapesMaxSize", "number", 256, "", {range:{min:1, max:1024, step:1}})); -GFX$$inline_405.cacheShapesThreshold = canvas2DOptions$$inline_412.register(new Option$$inline_406("", "cacheShapesThreshold", "number", 256, "", {range:{min:1, max:1024, step:1}})); -var GFX$$inline_413 = Shumway$$inline_404.GFX; -(function(b) { - (function(k) { - (function(g) { - function f(a, b, c, d) { - var e = 1 - d; - return a * e * e + 2 * b * e * d + c * d * d; - } - function t(a, b, c, d, e) { - var f = e * e, g = 1 - e, k = g * g; - return a * g * k + 3 * b * e * k + 3 * c * g * f + d * e * f; +var Shumway$$inline_737 = Shumway || (Shumway = {}), GFX$$inline_738 = Shumway$$inline_737.GFX || (Shumway$$inline_737.GFX = {}), Option$$inline_739 = Shumway$$inline_737.Options.Option, OptionSet$$inline_740 = Shumway$$inline_737.Options.OptionSet, shumwayOptions$$inline_741 = Shumway$$inline_737.Settings.shumwayOptions, rendererOptions$$inline_742 = shumwayOptions$$inline_741.register(new OptionSet$$inline_740("Renderer Options")); +GFX$$inline_738.imageUpdateOption = rendererOptions$$inline_742.register(new Option$$inline_739("", "imageUpdate", "boolean", !0, "Enable image updating.")); +GFX$$inline_738.imageConvertOption = rendererOptions$$inline_742.register(new Option$$inline_739("", "imageConvert", "boolean", !0, "Enable image conversion.")); +GFX$$inline_738.stageOptions = shumwayOptions$$inline_741.register(new OptionSet$$inline_740("Stage Renderer Options")); +GFX$$inline_738.forcePaint = GFX$$inline_738.stageOptions.register(new Option$$inline_739("", "forcePaint", "boolean", !1, "Force repainting.")); +GFX$$inline_738.ignoreViewport = GFX$$inline_738.stageOptions.register(new Option$$inline_739("", "ignoreViewport", "boolean", !1, "Cull elements outside of the viewport.")); +GFX$$inline_738.viewportLoupeDiameter = GFX$$inline_738.stageOptions.register(new Option$$inline_739("", "viewportLoupeDiameter", "number", 256, "Size of the viewport loupe.", {range:{min:1, max:1024, step:1}})); +GFX$$inline_738.disableClipping = GFX$$inline_738.stageOptions.register(new Option$$inline_739("", "disableClipping", "boolean", !1, "Disable clipping.")); +GFX$$inline_738.debugClipping = GFX$$inline_738.stageOptions.register(new Option$$inline_739("", "debugClipping", "boolean", !1, "Disable clipping.")); +GFX$$inline_738.hud = GFX$$inline_738.stageOptions.register(new Option$$inline_739("", "hud", "boolean", !1, "Enable HUD.")); +var webGLOptions$$inline_743 = GFX$$inline_738.stageOptions.register(new OptionSet$$inline_740("WebGL Options")); +GFX$$inline_738.perspectiveCamera = webGLOptions$$inline_743.register(new Option$$inline_739("", "pc", "boolean", !1, "Use perspective camera.")); +GFX$$inline_738.perspectiveCameraFOV = webGLOptions$$inline_743.register(new Option$$inline_739("", "pcFOV", "number", 60, "Perspective Camera FOV.")); +GFX$$inline_738.perspectiveCameraDistance = webGLOptions$$inline_743.register(new Option$$inline_739("", "pcDistance", "number", 2, "Perspective Camera Distance.")); +GFX$$inline_738.perspectiveCameraAngle = webGLOptions$$inline_743.register(new Option$$inline_739("", "pcAngle", "number", 0, "Perspective Camera Angle.")); +GFX$$inline_738.perspectiveCameraAngleRotate = webGLOptions$$inline_743.register(new Option$$inline_739("", "pcRotate", "boolean", !1, "Rotate Use perspective camera.")); +GFX$$inline_738.perspectiveCameraSpacing = webGLOptions$$inline_743.register(new Option$$inline_739("", "pcSpacing", "number", .01, "Element Spacing.")); +GFX$$inline_738.perspectiveCameraSpacingInflate = webGLOptions$$inline_743.register(new Option$$inline_739("", "pcInflate", "boolean", !1, "Rotate Use perspective camera.")); +GFX$$inline_738.drawTiles = webGLOptions$$inline_743.register(new Option$$inline_739("", "drawTiles", "boolean", !1, "Draw WebGL Tiles")); +GFX$$inline_738.drawSurfaces = webGLOptions$$inline_743.register(new Option$$inline_739("", "drawSurfaces", "boolean", !1, "Draw WebGL Surfaces.")); +GFX$$inline_738.drawSurface = webGLOptions$$inline_743.register(new Option$$inline_739("", "drawSurface", "number", -1, "Draw WebGL Surface #")); +GFX$$inline_738.drawElements = webGLOptions$$inline_743.register(new Option$$inline_739("", "drawElements", "boolean", !0, "Actually call gl.drawElements. This is useful to test if the GPU is the bottleneck.")); +GFX$$inline_738.disableSurfaceUploads = webGLOptions$$inline_743.register(new Option$$inline_739("", "disableSurfaceUploads", "boolean", !1, "Disable surface uploads.")); +GFX$$inline_738.premultipliedAlpha = webGLOptions$$inline_743.register(new Option$$inline_739("", "premultipliedAlpha", "boolean", !1, "Set the premultipliedAlpha flag on getContext().")); +GFX$$inline_738.unpackPremultiplyAlpha = webGLOptions$$inline_743.register(new Option$$inline_739("", "unpackPremultiplyAlpha", "boolean", !0, "Use UNPACK_PREMULTIPLY_ALPHA_WEBGL in pixelStorei.")); +var factorChoices$$inline_744 = {ZERO:0, ONE:1, SRC_COLOR:768, ONE_MINUS_SRC_COLOR:769, DST_COLOR:774, ONE_MINUS_DST_COLOR:775, SRC_ALPHA:770, ONE_MINUS_SRC_ALPHA:771, DST_ALPHA:772, ONE_MINUS_DST_ALPHA:773, SRC_ALPHA_SATURATE:776, CONSTANT_COLOR:32769, ONE_MINUS_CONSTANT_COLOR:32770, CONSTANT_ALPHA:32771, ONE_MINUS_CONSTANT_ALPHA:32772}; +GFX$$inline_738.sourceBlendFactor = webGLOptions$$inline_743.register(new Option$$inline_739("", "sourceBlendFactor", "number", factorChoices$$inline_744.ONE, "", {choices:factorChoices$$inline_744})); +GFX$$inline_738.destinationBlendFactor = webGLOptions$$inline_743.register(new Option$$inline_739("", "destinationBlendFactor", "number", factorChoices$$inline_744.ONE_MINUS_SRC_ALPHA, "", {choices:factorChoices$$inline_744})); +var canvas2DOptions$$inline_745 = GFX$$inline_738.stageOptions.register(new OptionSet$$inline_740("Canvas2D Options")); +GFX$$inline_738.clipDirtyRegions = canvas2DOptions$$inline_745.register(new Option$$inline_739("", "clipDirtyRegions", "boolean", !1, "Clip dirty regions.")); +GFX$$inline_738.clipCanvas = canvas2DOptions$$inline_745.register(new Option$$inline_739("", "clipCanvas", "boolean", !1, "Clip Regions.")); +GFX$$inline_738.cull = canvas2DOptions$$inline_745.register(new Option$$inline_739("", "cull", "boolean", !1, "Enable culling.")); +GFX$$inline_738.snapToDevicePixels = canvas2DOptions$$inline_745.register(new Option$$inline_739("", "snapToDevicePixels", "boolean", !1, "")); +GFX$$inline_738.imageSmoothing = canvas2DOptions$$inline_745.register(new Option$$inline_739("", "imageSmoothing", "boolean", !1, "")); +GFX$$inline_738.masking = canvas2DOptions$$inline_745.register(new Option$$inline_739("", "masking", "boolean", !0, "Composite Mask.")); +GFX$$inline_738.blending = canvas2DOptions$$inline_745.register(new Option$$inline_739("", "blending", "boolean", !0, "")); +GFX$$inline_738.debugLayers = canvas2DOptions$$inline_745.register(new Option$$inline_739("", "debugLayers", "boolean", !1, "")); +GFX$$inline_738.filters = canvas2DOptions$$inline_745.register(new Option$$inline_739("", "filters", "boolean", !1, "")); +GFX$$inline_738.cacheShapes = canvas2DOptions$$inline_745.register(new Option$$inline_739("", "cacheShapes", "boolean", !0, "")); +GFX$$inline_738.cacheShapesMaxSize = canvas2DOptions$$inline_745.register(new Option$$inline_739("", "cacheShapesMaxSize", "number", 256, "", {range:{min:1, max:1024, step:1}})); +GFX$$inline_738.cacheShapesThreshold = canvas2DOptions$$inline_745.register(new Option$$inline_739("", "cacheShapesThreshold", "number", 256, "", {range:{min:1, max:1024, step:1}})); +(function(c) { + (function(h) { + (function(a) { + function s(a, c, d, e) { + var f = 1 - e; + return a * f * f + 2 * c * f * e + d * e * e; + } + function v(a, c, d, e, f) { + var k = f * f, h = 1 - f, l = h * h; + return a * h * l + 3 * c * f * l + 3 * d * h * k + e * f * k; } - var s = b.NumberUtilities.clamp, m = b.NumberUtilities.pow2, d = b.NumberUtilities.epsilonEquals, a = b.Debug.assert; - g.radianToDegrees = function(a) { + var p = c.NumberUtilities.clamp, u = c.NumberUtilities.pow2, l = c.NumberUtilities.epsilonEquals, e = c.Debug.assert; + a.radianToDegrees = function(a) { return 180 * a / Math.PI; }; - g.degreesToRadian = function(a) { + a.degreesToRadian = function(a) { return a * Math.PI / 180; }; - g.quadraticBezier = f; - g.quadraticBezierExtreme = function(a, b, c) { - var d = (a - b) / (a - 2 * b + c); - return 0 > d ? a : 1 < d ? c : f(a, b, c, d); - }; - g.cubicBezier = t; - g.cubicBezierExtremes = function(a, b, c, d) { - var e = b - a, f; - f = 2 * (c - b); - var g = d - c; - e + g === f && (g *= 1.0001); - var k = 2 * e - f, l = f - 2 * e, l = Math.sqrt(l * l - 4 * e * (e - f + g)); - f = 2 * (e - f + g); - e = (k + l) / f; - k = (k - l) / f; - l = []; - 0 <= e && 1 >= e && l.push(t(a, b, c, d, e)); - 0 <= k && 1 >= k && l.push(t(a, b, c, d, k)); - return l; + a.quadraticBezier = s; + a.quadraticBezierExtreme = function(a, c, d) { + var e = (a - c) / (a - 2 * c + d); + return 0 > e ? a : 1 < e ? d : s(a, c, d, e); + }; + a.cubicBezier = v; + a.cubicBezierExtremes = function(a, c, d, e) { + var f = c - a, k; + k = 2 * (d - c); + var h = e - d; + f + h === k && (h *= 1.0001); + var l = 2 * f - k, m = k - 2 * f, m = Math.sqrt(m * m - 4 * f * (f - k + h)); + k = 2 * (f - k + h); + f = (l + m) / k; + l = (l - m) / k; + m = []; + 0 <= f && 1 >= f && m.push(v(a, c, d, e, f)); + 0 <= l && 1 >= l && m.push(v(a, c, d, e, l)); + return m; }; - var c = function() { + var m = function() { function a(b, c) { this.x = b; this.y = c; @@ -39458,8 +48889,9 @@ a.prototype.clone = function() { return new a(this.x, this.y); }; - a.prototype.toString = function() { - return "{x: " + this.x + ", y: " + this.y + "}"; + a.prototype.toString = function(a) { + void 0 === a && (a = 2); + return "{x: " + this.x.toFixed(a) + ", y: " + this.y.toFixed(a) + "}"; }; a.prototype.inTriangle = function(a, b, c) { var d = a.y * c.x - a.x * c.y + (c.y - a.y) * this.x + (a.x - c.x) * this.y, e = a.x * b.y - a.y * b.x + (a.y - b.y) * this.x + (b.x - a.x) * this.y; @@ -39473,16 +48905,16 @@ a.createEmpty = function() { return new a(0, 0); }; - a.createEmptyPoints = function(b) { - for (var c = [], d = 0;d < b;d++) { - c.push(new a(0, 0)); + a.createEmptyPoints = function(c) { + for (var d = [], e = 0;e < c;e++) { + d.push(new a(0, 0)); } - return c; + return d; }; return a; }(); - g.Point = c; - var n = function() { + a.Point = m; + var t = function() { function a(b, c, d) { this.x = b; this.y = c; @@ -39533,24 +48965,26 @@ a.prototype.clone = function() { return new a(this.x, this.y, this.z); }; - a.prototype.toString = function() { - return "{x: " + this.x + ", y: " + this.y + ", z: " + this.z + "}"; + a.prototype.toString = function(a) { + void 0 === a && (a = 2); + return "{x: " + this.x.toFixed(a) + ", y: " + this.y.toFixed(a) + ", z: " + this.z.toFixed(a) + "}"; }; a.createEmpty = function() { return new a(0, 0, 0); }; - a.createEmptyPoints = function(b) { - for (var c = [], d = 0;d < b;d++) { - c.push(new a(0, 0, 0)); + a.createEmptyPoints = function(c) { + for (var d = [], e = 0;e < c;e++) { + d.push(new a(0, 0, 0)); } - return c; + return d; }; return a; }(); - g.Point3D = n; - var p = function() { - function a(b, c, d, e) { - this.setElements(b, c, d, e); + a.Point3D = t; + var q = function() { + function a(c, d, e, f) { + this.setElements(c, d, e, f); + a.allocationCount++; } a.prototype.setElements = function(a, b, c, d) { this.x = a; @@ -39589,36 +49023,38 @@ if (this.isEmpty()) { this.set(a); } else { - var b = this.x, c = this.y; - this.x > a.x && (b = a.x); - this.y > a.y && (c = a.y); - var d = this.x + this.w; - d < a.x + a.w && (d = a.x + a.w); - var e = this.y + this.h; - e < a.y + a.h && (e = a.y + a.h); - this.x = b; - this.y = c; - this.w = d - b; - this.h = e - c; + if (!a.isEmpty()) { + var b = this.x, c = this.y; + this.x > a.x && (b = a.x); + this.y > a.y && (c = a.y); + var d = this.x + this.w; + d < a.x + a.w && (d = a.x + a.w); + var e = this.y + this.h; + e < a.y + a.h && (e = a.y + a.h); + this.x = b; + this.y = c; + this.w = d - b; + this.h = e - c; + } } }; a.prototype.isEmpty = function() { return 0 >= this.w || 0 >= this.h; }; a.prototype.setEmpty = function() { - this.h = this.w = 0; + this.h = this.w = this.y = this.x = 0; }; - a.prototype.intersect = function(b) { - var c = a.createEmpty(); - if (this.isEmpty() || b.isEmpty()) { - return c.setEmpty(), c; - } - c.x = Math.max(this.x, b.x); - c.y = Math.max(this.y, b.y); - c.w = Math.min(this.x + this.w, b.x + b.w) - c.x; - c.h = Math.min(this.y + this.h, b.y + b.h) - c.y; - c.isEmpty() && c.setEmpty(); - this.set(c); + a.prototype.intersect = function(c) { + var d = a.createEmpty(); + if (this.isEmpty() || c.isEmpty()) { + return d.setEmpty(), d; + } + d.x = Math.max(this.x, c.x); + d.y = Math.max(this.y, c.y); + d.w = Math.min(this.x + this.w, c.x + c.w) - d.x; + d.h = Math.min(this.y + this.h, c.y + c.h) - d.y; + d.isEmpty() && d.setEmpty(); + this.set(d); }; a.prototype.intersects = function(a) { if (this.isEmpty() || a.isEmpty()) { @@ -39628,11 +49064,11 @@ a = Math.min(this.y + this.h, a.y + a.h) - c; return!(0 >= b || 0 >= a); }; - a.prototype.intersectsTransformedAABB = function(b, c) { - var d = a._temporary; - d.set(b); - c.transformRectangleAABB(d); - return this.intersects(d); + a.prototype.intersectsTransformedAABB = function(c, d) { + var e = a._temporary; + e.set(c); + d.transformRectangleAABB(e); + return this.intersects(e); }; a.prototype.intersectsTranslated = function(a, b, c) { if (this.isEmpty() || a.isEmpty()) { @@ -39647,7 +49083,16 @@ return this.w * this.h; }; a.prototype.clone = function() { - return new a(this.x, this.y, this.w, this.h); + var c = a.allocate(); + c.set(this); + return c; + }; + a.allocate = function() { + var c = a._dirtyStack; + return c.length ? c.pop() : new a(12345, 67890, 12345, 67890); + }; + a.prototype.free = function() { + a._dirtyStack.push(this); }; a.prototype.snap = function() { var a = Math.ceil(this.x + this.w), b = Math.ceil(this.y + this.h); @@ -39679,22 +49124,28 @@ return this; }; a.prototype.getCenter = function() { - return new c(this.x + this.w / 2, this.y + this.h / 2); + return new m(this.x + this.w / 2, this.y + this.h / 2); }; a.prototype.getAbsoluteBounds = function() { return new a(0, 0, this.w, this.h); }; - a.prototype.toString = function() { - return "{" + this.x + ", " + this.y + ", " + this.w + ", " + this.h + "}"; + a.prototype.toString = function(a) { + void 0 === a && (a = 2); + return "{" + this.x.toFixed(a) + ", " + this.y.toFixed(a) + ", " + this.w.toFixed(a) + ", " + this.h.toFixed(a) + "}"; }; a.createEmpty = function() { - return new a(0, 0, 0, 0); + var c = a.allocate(); + c.setEmpty(); + return c; }; a.createSquare = function() { return new a(-512, -512, 1024, 1024); }; a.createMaxI16 = function() { - return new a(b.Numbers.MinI16, b.Numbers.MinI16, 65535, 65535); + return new a(-32768, -32768, 65535, 65535); + }; + a.prototype.setMaxI16 = function() { + this.setElements(-32768, -32768, 65535, 65535); }; a.prototype.getCorners = function(a) { a[0].x = this.x; @@ -39706,11 +49157,13 @@ a[3].x = this.x; a[3].y = this.y + this.h; }; - a._temporary = a.createEmpty(); + a.allocationCount = 0; + a._temporary = new a(0, 0, 0, 0); + a._dirtyStack = []; return a; }(); - g.Rectangle = p; - var e = function() { + a.Rectangle = q; + var n = function() { function a(b) { this.corners = b.map(function(a) { return a.clone(); @@ -39725,14 +49178,14 @@ return a.getBounds(this.corners); }; a.getBounds = function(a) { - for (var b = new c(Number.MAX_VALUE, Number.MAX_VALUE), d = new c(Number.MIN_VALUE, Number.MIN_VALUE), e = 0;4 > e;e++) { - var f = a[e].x, g = a[e].y; - b.x = Math.min(b.x, f); - b.y = Math.min(b.y, g); - d.x = Math.max(d.x, f); - d.y = Math.max(d.y, g); + for (var b = new m(Number.MAX_VALUE, Number.MAX_VALUE), c = new m(Number.MIN_VALUE, Number.MIN_VALUE), d = 0;4 > d;d++) { + var e = a[d].x, f = a[d].y; + b.x = Math.min(b.x, e); + b.y = Math.min(b.y, f); + c.x = Math.max(c.x, e); + c.y = Math.max(c.y, f); } - return new p(b.x, b.y, d.x - b.x, d.y - b.y); + return new q(b.x, b.y, c.x - b.x, c.y - b.y); }; a.prototype.intersects = function(a) { return this.intersectsOneWay(a) && a.intersectsOneWay(this); @@ -39751,60 +49204,74 @@ }; return a; }(); - g.OBB = e; - var q = function() { - function a(b, c, d, e, f, g) { + a.OBB = n; + (function(a) { + a[a.Unknown = 0] = "Unknown"; + a[a.Identity = 1] = "Identity"; + a[a.Translation = 2] = "Translation"; + })(a.MatrixType || (a.MatrixType = {})); + var k = function() { + function a(c, d, e, f, k, h) { this._data = new Float64Array(6); - this.setElements(b, c, d, e, f, g); + this._type = 0; + this.setElements(c, d, e, f, k, h); + a.allocationCount++; } Object.defineProperty(a.prototype, "a", {get:function() { return this._data[0]; }, set:function(a) { this._data[0] = a; + this._type = 0; }, enumerable:!0, configurable:!0}); Object.defineProperty(a.prototype, "b", {get:function() { return this._data[1]; }, set:function(a) { this._data[1] = a; + this._type = 0; }, enumerable:!0, configurable:!0}); Object.defineProperty(a.prototype, "c", {get:function() { return this._data[2]; }, set:function(a) { this._data[2] = a; + this._type = 0; }, enumerable:!0, configurable:!0}); Object.defineProperty(a.prototype, "d", {get:function() { return this._data[3]; }, set:function(a) { this._data[3] = a; + this._type = 0; }, enumerable:!0, configurable:!0}); Object.defineProperty(a.prototype, "tx", {get:function() { return this._data[4]; }, set:function(a) { this._data[4] = a; + 1 === this._type && (this._type = 2); }, enumerable:!0, configurable:!0}); Object.defineProperty(a.prototype, "ty", {get:function() { return this._data[5]; }, set:function(a) { this._data[5] = a; + 1 === this._type && (this._type = 2); }, enumerable:!0, configurable:!0}); a.prototype.setElements = function(a, b, c, d, e, f) { - var g = this._data; - g[0] = a; - g[1] = b; - g[2] = c; - g[3] = d; - g[4] = e; - g[5] = f; + var k = this._data; + k[0] = a; + k[1] = b; + k[2] = c; + k[3] = d; + k[4] = e; + k[5] = f; + this._type = 0; }; a.prototype.set = function(a) { - var b = this._data; - a = a._data; - b[0] = a[0]; - b[1] = a[1]; - b[2] = a[2]; - b[3] = a[3]; - b[4] = a[4]; - b[5] = a[5]; + var b = this._data, c = a._data; + b[0] = c[0]; + b[1] = c[1]; + b[2] = c[2]; + b[3] = c[3]; + b[4] = c[4]; + b[5] = c[5]; + this._type = a._type; }; a.prototype.emptyArea = function(a) { a = this._data; @@ -39815,49 +49282,72 @@ return Infinity === Math.abs(a[0]) || Infinity === Math.abs(a[3]) ? !0 : !1; }; a.prototype.isEqual = function(a) { + if (1 === this._type && 1 === a._type) { + return!0; + } var b = this._data; a = a._data; return b[0] === a[0] && b[1] === a[1] && b[2] === a[2] && b[3] === a[3] && b[4] === a[4] && b[5] === a[5]; }; a.prototype.clone = function() { - var b = this._data; - return new a(b[0], b[1], b[2], b[3], b[4], b[5]); + var c = a.allocate(); + c.set(this); + return c; + }; + a.allocate = function() { + var c = a._dirtyStack; + return c.length ? c.pop() : new a(12345, 12345, 12345, 12345, 12345, 12345); + }; + a.prototype.free = function() { + a._dirtyStack.push(this); }; a.prototype.transform = function(a, b, c, d, e, f) { - var g = this._data, k = g[0], l = g[1], m = g[2], n = g[3], r = g[4], p = g[5]; - g[0] = k * a + m * b; - g[1] = l * a + n * b; - g[2] = k * c + m * d; - g[3] = l * c + n * d; - g[4] = k * e + m * f + r; - g[5] = l * e + n * f + p; + var k = this._data, h = k[0], l = k[1], m = k[2], n = k[3], p = k[4], q = k[5]; + k[0] = h * a + m * b; + k[1] = l * a + n * b; + k[2] = h * c + m * d; + k[3] = l * c + n * d; + k[4] = h * e + m * f + p; + k[5] = l * e + n * f + q; + this._type = 0; return this; }; a.prototype.transformRectangle = function(a, b) { - var c = this._data, d = c[0], e = c[1], f = c[2], g = c[3], k = c[4], c = c[5], l = a.x, m = a.y, n = a.w, r = a.h; - b[0].x = d * l + f * m + k; - b[0].y = e * l + g * m + c; - b[1].x = d * (l + n) + f * m + k; - b[1].y = e * (l + n) + g * m + c; - b[2].x = d * (l + n) + f * (m + r) + k; - b[2].y = e * (l + n) + g * (m + r) + c; - b[3].x = d * l + f * (m + r) + k; - b[3].y = e * l + g * (m + r) + c; + e(4 === b.length); + var c = this._data, d = c[0], f = c[1], k = c[2], h = c[3], l = c[4], c = c[5], m = a.x, n = a.y, p = a.w, q = a.h; + b[0].x = d * m + k * n + l; + b[0].y = f * m + h * n + c; + b[1].x = d * (m + p) + k * n + l; + b[1].y = f * (m + p) + h * n + c; + b[2].x = d * (m + p) + k * (n + q) + l; + b[2].y = f * (m + p) + h * (n + q) + c; + b[3].x = d * m + k * (n + q) + l; + b[3].y = f * m + h * (n + q) + c; }; a.prototype.isTranslationOnly = function() { + if (2 === this._type) { + return!0; + } var a = this._data; - return 1 === a[0] && 0 === a[1] && 0 === a[2] && 1 === a[3] || d(a[0], 1) && d(a[1], 0) && d(a[2], 0) && d(a[3], 1) ? !0 : !1; + return 1 === a[0] && 0 === a[1] && 0 === a[2] && 1 === a[3] || l(a[0], 1) && l(a[1], 0) && l(a[2], 0) && l(a[3], 1) ? (this._type = 2, !0) : !1; }; a.prototype.transformRectangleAABB = function(a) { - var b = this._data, c = b[0], d = b[1], e = b[2], f = b[3], g = b[4], k = b[5], l = a.x, m = a.y, n = a.w, r = a.h, b = c * l + e * m + g, p = d * l + f * m + k, q = c * (l + n) + e * m + g, u = d * (l + n) + f * m + k, s = c * (l + n) + e * (m + r) + g, n = d * (l + n) + f * (m + r) + k, c = c * l + e * (m + r) + g, d = d * l + f * (m + r) + k, f = 0; - b > q && (f = b, b = q, q = f); - s > c && (f = s, s = c, c = f); - a.x = b < s ? b : s; - a.w = (q > c ? q : c) - a.x; - p > u && (f = p, p = u, u = f); - n > d && (f = n, n = d, d = f); - a.y = p < n ? p : n; - a.h = (u > d ? u : d) - a.y; + var b = this._data; + if (1 !== this._type) { + if (2 === this._type) { + a.x += b[4], a.y += b[5]; + } else { + var c = b[0], d = b[1], e = b[2], f = b[3], k = b[4], h = b[5], l = a.x, m = a.y, n = a.w, p = a.h, b = c * l + e * m + k, q = d * l + f * m + h, s = c * (l + n) + e * m + k, t = d * (l + n) + f * m + h, u = c * (l + n) + e * (m + p) + k, n = d * (l + n) + f * (m + p) + h, c = c * l + e * (m + p) + k, d = d * l + f * (m + p) + h, f = 0; + b > s && (f = b, b = s, s = f); + u > c && (f = u, u = c, c = f); + a.x = b < u ? b : u; + a.w = (s > c ? s : c) - a.x; + q > t && (f = q, q = t, t = f); + n > d && (f = n, n = d, d = f); + a.y = q < n ? q : n; + a.h = (t > d ? t : d) - a.y; + } + } }; a.prototype.scale = function(a, b) { var c = this._data; @@ -39867,54 +49357,72 @@ c[3] *= b; c[4] *= a; c[5] *= b; + this._type = 0; return this; }; a.prototype.scaleClone = function(a, b) { return 1 === a && 1 === b ? this : this.clone().scale(a, b); }; a.prototype.rotate = function(a) { - var b = this._data, c = b[0], d = b[1], e = b[2], f = b[3], g = b[4], k = b[5], l = Math.cos(a); + var b = this._data, c = b[0], d = b[1], e = b[2], f = b[3], k = b[4], h = b[5], l = Math.cos(a); a = Math.sin(a); b[0] = l * c - a * d; b[1] = a * c + l * d; b[2] = l * e - a * f; b[3] = a * e + l * f; - b[4] = l * g - a * k; - b[5] = a * g + l * k; + b[4] = l * k - a * h; + b[5] = a * k + l * h; + this._type = 0; return this; }; a.prototype.concat = function(a) { + if (1 === a._type) { + return this; + } var b = this._data; a = a._data; - var c = b[0] * a[0], d = 0, e = 0, f = b[3] * a[3], g = b[4] * a[0] + a[4], k = b[5] * a[3] + a[5]; + var c = b[0] * a[0], d = 0, e = 0, f = b[3] * a[3], k = b[4] * a[0] + a[4], h = b[5] * a[3] + a[5]; if (0 !== b[1] || 0 !== b[2] || 0 !== a[1] || 0 !== a[2]) { - c += b[1] * a[2], f += b[2] * a[1], d += b[0] * a[1] + b[1] * a[3], e += b[2] * a[0] + b[3] * a[2], g += b[5] * a[2], k += b[4] * a[1]; + c += b[1] * a[2], f += b[2] * a[1], d += b[0] * a[1] + b[1] * a[3], e += b[2] * a[0] + b[3] * a[2], k += b[5] * a[2], h += b[4] * a[1]; } b[0] = c; b[1] = d; b[2] = e; b[3] = f; - b[4] = g; - b[5] = k; + b[4] = k; + b[5] = h; + this._type = 0; + return this; + }; + a.prototype.concatClone = function(a) { + return this.clone().concat(a); }; a.prototype.preMultiply = function(a) { - var b = this._data; - a = a._data; - var c = a[0] * b[0], d = 0, e = 0, f = a[3] * b[3], g = a[4] * b[0] + b[4], k = a[5] * b[3] + b[5]; - if (0 !== a[1] || 0 !== a[2] || 0 !== b[1] || 0 !== b[2]) { - c += a[1] * b[2], f += a[2] * b[1], d += a[0] * b[1] + a[1] * b[3], e += a[2] * b[0] + a[3] * b[2], g += a[5] * b[2], k += a[4] * b[1]; + var b = this._data, c = a._data; + if (2 === a._type && this._type & 3) { + b[4] += c[4], b[5] += c[5], this._type = 2; + } else { + if (1 !== a._type) { + a = c[0] * b[0]; + var d = 0, e = 0, f = c[3] * b[3], k = c[4] * b[0] + b[4], h = c[5] * b[3] + b[5]; + if (0 !== c[1] || 0 !== c[2] || 0 !== b[1] || 0 !== b[2]) { + a += c[1] * b[2], f += c[2] * b[1], d += c[0] * b[1] + c[1] * b[3], e += c[2] * b[0] + c[3] * b[2], k += c[5] * b[2], h += c[4] * b[1]; + } + b[0] = a; + b[1] = d; + b[2] = e; + b[3] = f; + b[4] = k; + b[5] = h; + this._type = 0; + } } - b[0] = c; - b[1] = d; - b[2] = e; - b[3] = f; - b[4] = g; - b[5] = k; }; a.prototype.translate = function(a, b) { var c = this._data; c[4] += a; c[5] += b; + 1 === this._type && (this._type = 2); return this; }; a.prototype.setIdentity = function() { @@ -39925,37 +49433,67 @@ a[3] = 1; a[4] = 0; a[5] = 0; + this._type = 1; }; a.prototype.isIdentity = function() { + if (1 === this._type) { + return!0; + } var a = this._data; return 1 === a[0] && 0 === a[1] && 0 === a[2] && 1 === a[3] && 0 === a[4] && 0 === a[5]; }; a.prototype.transformPoint = function(a) { - var b = this._data, c = a.x, d = a.y; - a.x = b[0] * c + b[2] * d + b[4]; - a.y = b[1] * c + b[3] * d + b[5]; + if (1 !== this._type) { + var b = this._data, c = a.x, d = a.y; + a.x = b[0] * c + b[2] * d + b[4]; + a.y = b[1] * c + b[3] * d + b[5]; + } }; a.prototype.transformPoints = function(a) { - for (var b = 0;b < a.length;b++) { - this.transformPoint(a[b]); + if (1 !== this._type) { + for (var b = 0;b < a.length;b++) { + this.transformPoint(a[b]); + } } }; a.prototype.deltaTransformPoint = function(a) { - var b = this._data, c = a.x, d = a.y; - a.x = b[0] * c + b[2] * d; - a.y = b[1] * c + b[3] * d; + if (1 !== this._type) { + var b = this._data, c = a.x, d = a.y; + a.x = b[0] * c + b[2] * d; + a.y = b[1] * c + b[3] * d; + } }; a.prototype.inverse = function(a) { - var b = this._data, c = a._data, d = b[1], e = b[2], f = b[4], g = b[5]; - if (0 === d && 0 === e) { - var k = c[0] = 1 / b[0], b = c[3] = 1 / b[3]; - c[1] = 0; - c[2] = 0; - c[4] = -k * f; - c[5] = -b * g; + var b = this._data, c = a._data; + if (1 === this._type) { + a.setIdentity(); } else { - var k = b[0], b = b[3], l = k * b - d * e; - 0 === l ? a.setIdentity() : (l = 1 / l, c[0] = b * l, d = c[1] = -d * l, e = c[2] = -e * l, b = c[3] = k * l, c[4] = -(c[0] * f + e * g), c[5] = -(d * f + b * g)); + if (2 === this._type) { + c[0] = 1, c[1] = 0, c[2] = 0, c[3] = 1, c[4] = -b[4], c[5] = -b[5], a._type = 2; + } else { + var d = b[1], e = b[2], f = b[4], k = b[5]; + if (0 === d && 0 === e) { + var h = c[0] = 1 / b[0], b = c[3] = 1 / b[3]; + c[1] = 0; + c[2] = 0; + c[4] = -h * f; + c[5] = -b * k; + } else { + var h = b[0], b = b[3], l = h * b - d * e; + if (0 === l) { + a.setIdentity(); + return; + } + l = 1 / l; + c[0] = b * l; + d = c[1] = -d * l; + e = c[2] = -e * l; + b = c[3] = h * l; + c[4] = -(c[0] * f + e * k); + c[5] = -(d * f + b * k); + } + a._type = 0; + } } }; a.prototype.getTranslateX = function() { @@ -39980,6 +49518,9 @@ var b = Math.sqrt(a[2] * a[2] + a[3] * a[3]); return 0 < a[3] ? b : -b; }; + a.prototype.getScale = function() { + return(this.getScaleX() + this.getScaleY()) / 2; + }; a.prototype.getAbsoluteScaleX = function() { return Math.abs(this.getScaleX()); }; @@ -39994,9 +49535,10 @@ var a = this._data; return.01 > Math.abs(a[0] * a[2] + a[1] * a[3]); }; - a.prototype.toString = function() { - var a = this._data; - return "{" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ", " + a[4] + ", " + a[5] + "}"; + a.prototype.toString = function(a) { + void 0 === a && (a = 2); + var b = this._data; + return "{" + b[0].toFixed(a) + ", " + b[1].toFixed(a) + ", " + b[2].toFixed(a) + ", " + b[3].toFixed(a) + ", " + b[4].toFixed(a) + ", " + b[5].toFixed(a) + "}"; }; a.prototype.toWebGLMatrix = function() { var a = this._data; @@ -40007,35 +49549,39 @@ return "matrix(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ", " + a[4] + ", " + a[5] + ")"; }; a.createIdentity = function() { - return new a(1, 0, 0, 1, 0, 0); + var c = a.allocate(); + c.setIdentity(); + return c; }; a.prototype.toSVGMatrix = function() { - var b = this._data, c = a._svg.createSVGMatrix(); - c.a = b[0]; - c.b = b[1]; - c.c = b[2]; - c.d = b[3]; - c.e = b[4]; - c.f = b[5]; - return c; + var c = this._data, d = a._svg.createSVGMatrix(); + d.a = c[0]; + d.b = c[1]; + d.c = c[2]; + d.d = c[3]; + d.e = c[4]; + d.f = c[5]; + return d; }; a.prototype.snap = function() { var a = this._data; - return this.isTranslationOnly() ? (a[0] = 1, a[1] = 0, a[2] = 0, a[3] = 1, a[4] = Math.round(a[4]), a[5] = Math.round(a[5]), !0) : !1; + return this.isTranslationOnly() ? (a[0] = 1, a[1] = 0, a[2] = 0, a[3] = 1, a[4] = Math.round(a[4]), a[5] = Math.round(a[5]), this._type = 2, !0) : !1; }; a.createIdentitySVGMatrix = function() { return a._svg.createSVGMatrix(); }; - a.createSVGMatrixFromArray = function(b) { - var c = a._svg.createSVGMatrix(); - c.a = b[0]; - c.b = b[1]; - c.c = b[2]; - c.d = b[3]; - c.e = b[4]; - c.f = b[5]; - return c; + a.createSVGMatrixFromArray = function(c) { + var d = a._svg.createSVGMatrix(); + d.a = c[0]; + d.b = c[1]; + d.c = c[2]; + d.d = c[3]; + d.e = c[4]; + d.f = c[5]; + return d; }; + a.allocationCount = 0; + a._dirtyStack = []; a._svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); a.multiply = function(a, b) { var c = b._data; @@ -40043,25 +49589,25 @@ }; return a; }(); - g.Matrix = q; - q = function() { + a.Matrix = k; + k = function() { function a(b) { this._m = new Float32Array(b); } a.prototype.asWebGLMatrix = function() { return this._m; }; - a.createCameraLookAt = function(b, c, d) { - c = b.clone().sub(c).normalize(); - d = d.clone().cross(c).normalize(); - var e = c.clone().cross(d); - return new a([d.x, d.y, d.z, 0, e.x, e.y, e.z, 0, c.x, c.y, c.z, 0, b.x, b.y, b.z, 1]); - }; - a.createLookAt = function(b, c, d) { - c = b.clone().sub(c).normalize(); - d = d.clone().cross(c).normalize(); - var e = c.clone().cross(d); - return new a([d.x, e.x, c.x, 0, e.x, e.y, c.y, 0, c.x, e.z, c.z, 0, -d.dot(b), -e.dot(b), -c.dot(b), 1]); + a.createCameraLookAt = function(c, d, e) { + d = c.clone().sub(d).normalize(); + e = e.clone().cross(d).normalize(); + var f = d.clone().cross(e); + return new a([e.x, e.y, e.z, 0, f.x, f.y, f.z, 0, d.x, d.y, d.z, 0, c.x, c.y, c.z, 1]); + }; + a.createLookAt = function(c, d, e) { + d = c.clone().sub(d).normalize(); + e = e.clone().cross(d).normalize(); + var f = d.clone().cross(e); + return new a([e.x, f.x, d.x, 0, f.x, f.y, d.y, 0, d.x, f.z, d.z, 0, -e.dot(c), -f.dot(c), -d.dot(c), 1]); }; a.prototype.mul = function(a) { a = [a.x, a.y, a.z, 0]; @@ -40071,15 +49617,15 @@ c[d] += b[e + f] * a[f]; } } - return new n(c[0], c[1], c[2]); + return new t(c[0], c[1], c[2]); }; - a.create2DProjection = function(b, c, d) { - return new a([2 / b, 0, 0, 0, 0, -2 / c, 0, 0, 0, 0, 2 / d, 0, -1, 1, 0, 1]); + a.create2DProjection = function(c, d, e) { + return new a([2 / c, 0, 0, 0, 0, -2 / d, 0, 0, 0, 0, 2 / e, 0, -1, 1, 0, 1]); }; - a.createPerspective = function(b, c, d, e) { - b = Math.tan(.5 * Math.PI - .5 * b); - var f = 1 / (d - e); - return new a([b / c, 0, 0, 0, 0, b, 0, 0, 0, 0, (d + e) * f, -1, 0, 0, d * e * f * 2, 0]); + a.createPerspective = function(c, d, e, f) { + c = Math.tan(.5 * Math.PI - .5 * c); + var k = 1 / (e - f); + return new a([c / d, 0, 0, 0, 0, c, 0, 0, 0, 0, (e + f) * k, -1, 0, 0, e * f * k * 2, 0]); }; a.createIdentity = function() { return a.createTranslation(); @@ -40087,53 +49633,53 @@ a.createTranslation = function() { return new a([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); }; - a.createXRotation = function(b) { - var c = Math.cos(b); - b = Math.sin(b); - return new a([1, 0, 0, 0, 0, c, b, 0, 0, -b, c, 0, 0, 0, 0, 1]); - }; - a.createYRotation = function(b) { - var c = Math.cos(b); - b = Math.sin(b); - return new a([c, 0, -b, 0, 0, 1, 0, 0, b, 0, c, 0, 0, 0, 0, 1]); - }; - a.createZRotation = function(b) { - var c = Math.cos(b); - b = Math.sin(b); - return new a([c, b, 0, 0, -b, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); - }; - a.createScale = function(b, c, d) { - return new a([b, 0, 0, 0, 0, c, 0, 0, 0, 0, d, 0, 0, 0, 0, 1]); - }; - a.createMultiply = function(b, c) { - var d = b._m, e = c._m, f = d[0], g = d[1], k = d[2], l = d[3], m = d[4], n = d[5], p = d[6], q = d[7], u = d[8], s = d[9], w = d[10], t = d[11], L = d[12], M = d[13], V = d[14], d = d[15], Q = e[0], U = e[1], S = e[2], aa = e[3], $ = e[4], ea = e[5], Z = e[6], v = e[7], X = e[8], ba = e[9], R = e[10], H = e[11], Y = e[12], ga = e[13], ca = e[14], e = e[15]; - return new a([f * Q + g * $ + k * X + l * Y, f * U + g * ea + k * ba + l * ga, f * S + g * Z + k * R + l * ca, f * aa + g * v + k * H + l * e, m * Q + n * $ + p * X + q * Y, m * U + n * ea + p * ba + q * ga, m * S + n * Z + p * R + q * ca, m * aa + n * v + p * H + q * e, u * Q + s * $ + w * X + t * Y, u * U + s * ea + w * ba + t * ga, u * S + s * Z + w * R + t * ca, u * aa + s * v + w * H + t * e, L * Q + M * $ + V * X + d * Y, L * U + M * ea + V * ba + d * ga, L * S + M * Z + V * R + d * - ca, L * aa + M * v + V * H + d * e]); - }; - a.createInverse = function(b) { - var c = b._m; - b = c[0]; - var d = c[1], e = c[2], f = c[3], g = c[4], k = c[5], l = c[6], m = c[7], n = c[8], p = c[9], q = c[10], u = c[11], s = c[12], w = c[13], t = c[14], c = c[15], L = q * c, M = t * u, V = l * c, Q = t * m, U = l * u, S = q * m, aa = e * c, $ = t * f, ea = e * u, Z = q * f, v = e * m, X = l * f, ba = n * w, R = s * p, H = g * w, Y = s * k, ga = g * p, ca = n * k, ka = b * w, W = s * d, ha = b * p, da = n * d, ja = b * k, N = g * d, ma = L * k + Q * p + U * w - (M * k + V * p + S * w), T = - M * d + aa * p + Z * w - (L * d + $ * p + ea * w), w = V * d + $ * k + v * w - (Q * d + aa * k + X * w), d = S * d + ea * k + X * p - (U * d + Z * k + v * p), k = 1 / (b * ma + g * T + n * w + s * d); - return new a([k * ma, k * T, k * w, k * d, k * (M * g + V * n + S * s - (L * g + Q * n + U * s)), k * (L * b + $ * n + ea * s - (M * b + aa * n + Z * s)), k * (Q * b + aa * g + X * s - (V * b + $ * g + v * s)), k * (U * b + Z * g + v * n - (S * b + ea * g + X * n)), k * (ba * m + Y * u + ga * c - (R * m + H * u + ca * c)), k * (R * f + ka * u + da * c - (ba * f + W * u + ha * c)), k * (H * f + W * m + ja * c - (Y * f + ka * m + N * c)), k * (ca * f + ha * m + N * u - (ga * f + da * m + - ja * u)), k * (H * q + ca * t + R * l - (ga * t + ba * l + Y * q)), k * (ha * t + ba * e + W * q - (ka * q + da * t + R * e)), k * (ka * l + N * t + Y * e - (ja * t + H * e + W * l)), k * (ja * q + ga * e + da * l - (ha * l + N * q + ca * e))]); + a.createXRotation = function(c) { + var d = Math.cos(c); + c = Math.sin(c); + return new a([1, 0, 0, 0, 0, d, c, 0, 0, -c, d, 0, 0, 0, 0, 1]); + }; + a.createYRotation = function(c) { + var d = Math.cos(c); + c = Math.sin(c); + return new a([d, 0, -c, 0, 0, 1, 0, 0, c, 0, d, 0, 0, 0, 0, 1]); + }; + a.createZRotation = function(c) { + var d = Math.cos(c); + c = Math.sin(c); + return new a([d, c, 0, 0, -c, d, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + }; + a.createScale = function(c, d, e) { + return new a([c, 0, 0, 0, 0, d, 0, 0, 0, 0, e, 0, 0, 0, 0, 1]); + }; + a.createMultiply = function(c, d) { + var e = c._m, f = d._m, k = e[0], h = e[1], l = e[2], m = e[3], n = e[4], p = e[5], q = e[6], s = e[7], t = e[8], u = e[9], v = e[10], E = e[11], F = e[12], I = e[13], G = e[14], e = e[15], Z = f[0], Q = f[1], S = f[2], O = f[3], P = f[4], V = f[5], $ = f[6], W = f[7], x = f[8], ea = f[9], Y = f[10], R = f[11], U = f[12], ba = f[13], X = f[14], f = f[15]; + return new a([k * Z + h * P + l * x + m * U, k * Q + h * V + l * ea + m * ba, k * S + h * $ + l * Y + m * X, k * O + h * W + l * R + m * f, n * Z + p * P + q * x + s * U, n * Q + p * V + q * ea + s * ba, n * S + p * $ + q * Y + s * X, n * O + p * W + q * R + s * f, t * Z + u * P + v * x + E * U, t * Q + u * V + v * ea + E * ba, t * S + u * $ + v * Y + E * X, t * O + u * W + v * R + E * f, F * Z + I * P + G * x + e * U, F * Q + I * V + G * ea + e * ba, F * S + I * $ + G * Y + e * X, F * + O + I * W + G * R + e * f]); + }; + a.createInverse = function(c) { + var d = c._m; + c = d[0]; + var e = d[1], f = d[2], k = d[3], h = d[4], l = d[5], m = d[6], n = d[7], p = d[8], q = d[9], s = d[10], t = d[11], u = d[12], v = d[13], E = d[14], d = d[15], F = s * d, I = E * t, G = m * d, Z = E * n, Q = m * t, S = s * n, O = f * d, P = E * k, V = f * t, $ = s * k, W = f * n, x = m * k, ea = p * v, Y = u * q, R = h * v, U = u * l, ba = h * q, X = p * l, ga = c * v, ja = u * e, aa = c * q, ia = p * e, T = c * l, da = h * e, fa = F * l + Z * q + Q * v - (I * l + G * q + S * v), la = I * + e + O * q + $ * v - (F * e + P * q + V * v), v = G * e + P * l + W * v - (Z * e + O * l + x * v), e = S * e + V * l + x * q - (Q * e + $ * l + W * q), l = 1 / (c * fa + h * la + p * v + u * e); + return new a([l * fa, l * la, l * v, l * e, l * (I * h + G * p + S * u - (F * h + Z * p + Q * u)), l * (F * c + P * p + V * u - (I * c + O * p + $ * u)), l * (Z * c + O * h + x * u - (G * c + P * h + W * u)), l * (Q * c + $ * h + W * p - (S * c + V * h + x * p)), l * (ea * n + U * t + ba * d - (Y * n + R * t + X * d)), l * (Y * k + ga * t + ia * d - (ea * k + ja * t + aa * d)), l * (R * k + ja * n + T * d - (U * k + ga * n + da * d)), l * (X * k + aa * n + da * t - (ba * k + ia * n + T * + t)), l * (R * s + X * E + Y * m - (ba * E + ea * m + U * s)), l * (aa * E + ea * f + ja * s - (ga * s + ia * E + Y * f)), l * (ga * m + da * E + U * f - (T * E + R * f + ja * m)), l * (T * s + ba * f + ia * m - (aa * m + da * s + X * f))]); }; return a; }(); - g.Matrix3D = q; - q = function() { - function a(b, c, d) { - "undefined" === typeof d && (d = 7); - var e = this.size = 1 << d; - this.sizeInBits = d; - this.w = b; - this.h = c; - this.c = Math.ceil(b / e); - this.r = Math.ceil(c / e); + a.Matrix3D = k; + k = function() { + function a(c, d, e) { + void 0 === e && (e = 7); + var f = this.size = 1 << e; + this.sizeInBits = e; + this.w = c; + this.h = d; + this.c = Math.ceil(c / f); + this.r = Math.ceil(d / f); this.grid = []; - for (b = 0;b < this.r;b++) { - for (this.grid.push([]), c = 0;c < this.c;c++) { - this.grid[b][c] = new a.Cell(new p(c * e, b * e, e, e)); + for (c = 0;c < this.r;c++) { + for (this.grid.push([]), d = 0;d < this.c;d++) { + this.grid[c][d] = new a.Cell(new q(d * f, c * f, f, f)); } } } @@ -40145,7 +49691,7 @@ } }; a.prototype.getBounds = function() { - return new p(0, 0, this.w, this.h); + return new q(0, 0, this.w, this.h); }; a.prototype.addDirtyRectangle = function(a) { var b = a.x >> this.sizeInBits, c = a.y >> this.sizeInBits; @@ -40158,9 +49704,9 @@ if (d.region.contains(a)) { d.bounds.isEmpty() ? d.bounds.set(a) : d.bounds.contains(a) || d.bounds.union(a); } else { - for (var e = Math.min(this.c, Math.ceil((a.x + a.w) / this.size)) - b, f = Math.min(this.r, Math.ceil((a.y + a.h) / this.size)) - c, g = 0;g < e;g++) { - for (var k = 0;k < f;k++) { - d = this.grid[c + k][b + g], d = d.region.clone(), d.intersect(a), d.isEmpty() || this.addDirtyRectangle(d); + for (var e = Math.min(this.c, Math.ceil((a.x + a.w) / this.size)) - b, f = Math.min(this.r, Math.ceil((a.y + a.h) / this.size)) - c, k = 0;k < e;k++) { + for (var h = 0;h < f;h++) { + d = this.grid[c + h][b + k], d = d.region.clone(), d.intersect(a), d.isEmpty() || this.addDirtyRectangle(d); } } } @@ -40211,60 +49757,60 @@ } } }; - a.tmpRectangle = p.createEmpty(); + a.tmpRectangle = q.createEmpty(); return a; }(); - g.DirtyRegion = q; + a.DirtyRegion = k; (function(a) { - var b = function() { + var c = function() { function a(b) { this.region = b; - this.bounds = p.createEmpty(); + this.bounds = q.createEmpty(); } a.prototype.clear = function() { this.bounds.setEmpty(); }; return a; }(); - a.Cell = b; - })(g.DirtyRegion || (g.DirtyRegion = {})); - var q = g.DirtyRegion, l = function() { - function a(b, c, d, e, f, g) { + a.Cell = c; + })(k = a.DirtyRegion || (a.DirtyRegion = {})); + var f = function() { + function a(b, c, d, e, f, k) { this.index = b; this.x = c; this.y = d; - this.scale = g; - this.bounds = new p(c * e, d * f, e, f); + this.scale = k; + this.bounds = new q(c * e, d * f, e, f); } a.prototype.getOBB = function() { if (this._obb) { return this._obb; } this.bounds.getCorners(a.corners); - return this._obb = new e(a.corners); + return this._obb = new n(a.corners); }; - a.corners = c.createEmptyPoints(4); + a.corners = m.createEmptyPoints(4); return a; }(); - g.Tile = l; - var u = function() { - function b(c, d, e, f, g) { - this.tileW = e; - this.tileH = f; - this.scale = g; - this.w = c; - this.h = d; - this.rows = Math.ceil(d / f); - this.columns = Math.ceil(c / e); - a(2048 > this.rows && 2048 > this.columns); + a.Tile = f; + var d = function() { + function a(b, c, d, k, h) { + this.tileW = d; + this.tileH = k; + this.scale = h; + this.w = b; + this.h = c; + this.rows = Math.ceil(c / k); + this.columns = Math.ceil(b / d); + e(2048 > this.rows && 2048 > this.columns); this.tiles = []; - for (d = c = 0;d < this.rows;d++) { - for (var k = 0;k < this.columns;k++) { - this.tiles.push(new l(c++, k, d, e, f, g)); + for (c = b = 0;c < this.rows;c++) { + for (var l = 0;l < this.columns;l++) { + this.tiles.push(new f(b++, l, c, d, k, h)); } } } - b.prototype.getTiles = function(a, b) { + a.prototype.getTiles = function(a, b) { if (b.emptyArea(a)) { return[]; } @@ -40274,1198 +49820,1393 @@ var c = this.columns * this.rows; return 40 > c && b.isScaleOrRotation() ? this.getFewTiles(a, b, 10 < c) : this.getManyTiles(a, b); }; - b.prototype.getFewTiles = function(a, c, d) { - "undefined" === typeof d && (d = !0); - if (c.isTranslationOnly() && 1 === this.tiles.length) { - return this.tiles[0].bounds.intersectsTranslated(a, c.tx, c.ty) ? [this.tiles[0]] : []; + a.prototype.getFewTiles = function(c, d, e) { + void 0 === e && (e = !0); + if (d.isTranslationOnly() && 1 === this.tiles.length) { + return this.tiles[0].bounds.intersectsTranslated(c, d.tx, d.ty) ? [this.tiles[0]] : []; } - c.transformRectangle(a, b._points); + d.transformRectangle(c, a._points); var f; - a = new p(0, 0, this.w, this.h); - d && (f = new e(b._points)); - a.intersect(e.getBounds(b._points)); - if (a.isEmpty()) { + c = new q(0, 0, this.w, this.h); + e && (f = new n(a._points)); + c.intersect(n.getBounds(a._points)); + if (c.isEmpty()) { return[]; } - var g = a.x / this.tileW | 0; - c = a.y / this.tileH | 0; - var k = Math.ceil((a.x + a.w) / this.tileW) | 0, l = Math.ceil((a.y + a.h) / this.tileH) | 0, g = s(g, 0, this.columns), k = s(k, 0, this.columns); - c = s(c, 0, this.rows); - for (var l = s(l, 0, this.rows), m = [];g < k;g++) { - for (var n = c;n < l;n++) { - var q = this.tiles[n * this.columns + g]; - q.bounds.intersects(a) && (d ? q.getOBB().intersects(f) : 1) && m.push(q); + var k = c.x / this.tileW | 0; + d = c.y / this.tileH | 0; + var h = Math.ceil((c.x + c.w) / this.tileW) | 0, l = Math.ceil((c.y + c.h) / this.tileH) | 0, k = p(k, 0, this.columns), h = p(h, 0, this.columns); + d = p(d, 0, this.rows); + for (var l = p(l, 0, this.rows), m = [];k < h;k++) { + for (var s = d;s < l;s++) { + var t = this.tiles[s * this.columns + k]; + t.bounds.intersects(c) && (e ? t.getOBB().intersects(f) : 1) && m.push(t); } } return m; }; - b.prototype.getManyTiles = function(a, c) { - function d(a, b, c) { + a.prototype.getManyTiles = function(c, d) { + function e(a, b, c) { return(a - b.x) * (c.y - b.y) / (c.x - b.x) + b.y; } - function e(a, b, c, d, f) { + function f(a, b, c, d, e) { if (!(0 > c || c >= b.columns)) { - for (d = s(d, 0, b.rows), f = s(f + 1, 0, b.rows);d < f;d++) { + for (d = p(d, 0, b.rows), e = p(e + 1, 0, b.rows);d < e;d++) { a.push(b.tiles[d * b.columns + c]); } } } - var f = b._points; - c.transformRectangle(a, f); - for (var g = f[0].x < f[1].x ? 0 : 1, k = f[2].x < f[3].x ? 2 : 3, k = f[g].x < f[k].x ? g : k, g = [], l = 0;5 > l;l++, k++) { - g.push(f[k % 4]); - } - (g[1].x - g[0].x) * (g[3].y - g[0].y) < (g[1].y - g[0].y) * (g[3].x - g[0].x) && (f = g[1], g[1] = g[3], g[3] = f); - var f = [], m, n, k = Math.floor(g[0].x / this.tileW), l = (k + 1) * this.tileW; - if (g[2].x < l) { - m = Math.min(g[0].y, g[1].y, g[2].y, g[3].y); - n = Math.max(g[0].y, g[1].y, g[2].y, g[3].y); - var p = Math.floor(m / this.tileH), q = Math.floor(n / this.tileH); - e(f, this, k, p, q); - return f; + var k = a._points; + d.transformRectangle(c, k); + for (var h = k[0].x < k[1].x ? 0 : 1, l = k[2].x < k[3].x ? 2 : 3, l = k[h].x < k[l].x ? h : l, h = [], m = 0;5 > m;m++, l++) { + h.push(k[l % 4]); + } + (h[1].x - h[0].x) * (h[3].y - h[0].y) < (h[1].y - h[0].y) * (h[3].x - h[0].x) && (k = h[1], h[1] = h[3], h[3] = k); + var k = [], n, q, l = Math.floor(h[0].x / this.tileW), m = (l + 1) * this.tileW; + if (h[2].x < m) { + n = Math.min(h[0].y, h[1].y, h[2].y, h[3].y); + q = Math.max(h[0].y, h[1].y, h[2].y, h[3].y); + var s = Math.floor(n / this.tileH), t = Math.floor(q / this.tileH); + f(k, this, l, s, t); + return k; } - var u = 0, w = 4, t = !1; - if (g[0].x === g[1].x || g[0].x === g[3].x) { - g[0].x === g[1].x ? (t = !0, u++) : w--, m = d(l, g[u], g[u + 1]), n = d(l, g[w], g[w - 1]), p = Math.floor(g[u].y / this.tileH), q = Math.floor(g[w].y / this.tileH), e(f, this, k, p, q), k++; + var u = 0, v = 4, C = !1; + if (h[0].x === h[1].x || h[0].x === h[3].x) { + h[0].x === h[1].x ? (C = !0, u++) : v--, n = e(m, h[u], h[u + 1]), q = e(m, h[v], h[v - 1]), s = Math.floor(h[u].y / this.tileH), t = Math.floor(h[v].y / this.tileH), f(k, this, l, s, t), l++; } do { - var D, L, M, V; - g[u + 1].x < l ? (D = g[u + 1].y, M = !0) : (D = d(l, g[u], g[u + 1]), M = !1); - g[w - 1].x < l ? (L = g[w - 1].y, V = !0) : (L = d(l, g[w], g[w - 1]), V = !1); - p = Math.floor((g[u].y < g[u + 1].y ? m : D) / this.tileH); - q = Math.floor((g[w].y > g[w - 1].y ? n : L) / this.tileH); - e(f, this, k, p, q); - if (M && t) { - break; - } - M ? (t = !0, u++, m = d(l, g[u], g[u + 1])) : m = D; - V ? (w--, n = d(l, g[w], g[w - 1])) : n = L; - k++; - l = (k + 1) * this.tileW; - } while (u < w); - return f; + var E, F, I, G; + h[u + 1].x < m ? (E = h[u + 1].y, I = !0) : (E = e(m, h[u], h[u + 1]), I = !1); + h[v - 1].x < m ? (F = h[v - 1].y, G = !0) : (F = e(m, h[v], h[v - 1]), G = !1); + s = Math.floor((h[u].y < h[u + 1].y ? n : E) / this.tileH); + t = Math.floor((h[v].y > h[v - 1].y ? q : F) / this.tileH); + f(k, this, l, s, t); + if (I && C) { + break; + } + I ? (C = !0, u++, n = e(m, h[u], h[u + 1])) : n = E; + G ? (v--, q = e(m, h[v], h[v - 1])) : q = F; + l++; + m = (l + 1) * this.tileW; + } while (u < v); + return k; }; - b._points = c.createEmptyPoints(4); - return b; + a._points = m.createEmptyPoints(4); + return a; }(); - g.TileCache = u; - q = function() { - function b(a, c, d) { + a.TileCache = d; + k = function() { + function a(b, c, d) { this._cacheLevels = []; - this._source = a; + this._source = b; this._tileSize = c; this._minUntiledSize = d; } - b.prototype._getTilesAtScale = function(b, c, d) { - var e = Math.max(c.getAbsoluteScaleX(), c.getAbsoluteScaleY()), f = 0; - 1 !== e && (f = s(Math.round(Math.log(1 / e) / Math.LN2), -5, 3)); - e = m(f); - if (this._source.hasFlags(1)) { + a.prototype._getTilesAtScale = function(a, b, c) { + var f = Math.max(b.getAbsoluteScaleX(), b.getAbsoluteScaleY()), k = 0; + 1 !== f && (k = p(Math.round(Math.log(1 / f) / Math.LN2), -5, 3)); + f = u(k); + if (this._source.hasFlags(1048576)) { for (;;) { - e = m(f); - if (d.contains(this._source.getBounds().getAbsoluteBounds().clone().scale(e, e))) { + f = u(k); + if (c.contains(this._source.getBounds().getAbsoluteBounds().clone().scale(f, f))) { break; } - f--; - a(-5 <= f); + k--; + e(-5 <= k); } } - this._source.hasFlags(4) || (f = s(f, -5, 0)); - e = m(f); - d = 5 + f; - f = this._cacheLevels[d]; - if (!f) { - var f = this._source.getBounds().getAbsoluteBounds().clone().scale(e, e), g, k; - this._source.hasFlags(1) || !this._source.hasFlags(8) || Math.max(f.w, f.h) <= this._minUntiledSize ? (g = f.w, k = f.h) : g = k = this._tileSize; - f = this._cacheLevels[d] = new u(f.w, f.h, g, k, e); + this._source.hasFlags(2097152) || (k = p(k, -5, 0)); + f = u(k); + c = 5 + k; + k = this._cacheLevels[c]; + if (!k) { + var k = this._source.getBounds().getAbsoluteBounds().clone().scale(f, f), h, l; + this._source.hasFlags(1048576) || !this._source.hasFlags(4194304) || Math.max(k.w, k.h) <= this._minUntiledSize ? (h = k.w, l = k.h) : h = l = this._tileSize; + k = this._cacheLevels[c] = new d(k.w, k.h, h, l, f); } - return f.getTiles(b, c.scaleClone(e, e)); + return k.getTiles(a, b.scaleClone(f, f)); }; - b.prototype.fetchTiles = function(a, b, c, d) { - var e = new p(0, 0, c.canvas.width, c.canvas.height); + a.prototype.fetchTiles = function(a, b, c, d) { + var e = new q(0, 0, c.canvas.width, c.canvas.height); a = this._getTilesAtScale(a, b, e); var f; b = this._source; - for (var g = 0;g < a.length;g++) { - var k = a[g]; - k.cachedSurfaceRegion && k.cachedSurfaceRegion.surface && !b.hasFlags(3) || (f || (f = []), f.push(k)); + for (var k = 0;k < a.length;k++) { + var h = a[k]; + h.cachedSurfaceRegion && h.cachedSurfaceRegion.surface && !b.hasFlags(1048592) || (f || (f = []), f.push(h)); } f && this._cacheTiles(c, f, d, e); - b.removeFlags(2); + b.removeFlags(16); return a; }; - b.prototype._getTileBounds = function(a) { - for (var b = p.createEmpty(), c = 0;c < a.length;c++) { + a.prototype._getTileBounds = function(a) { + for (var b = q.createEmpty(), c = 0;c < a.length;c++) { b.union(a[c].bounds); } return b; }; - b.prototype._cacheTiles = function(b, c, d, e, f) { - "undefined" === typeof f && (f = 4); - a(0 < f, "Infinite recursion is likely."); - var g = this._getTileBounds(c); - b.save(); - b.setTransform(1, 0, 0, 1, 0, 0); - b.clearRect(0, 0, e.w, e.h); - b.translate(-g.x, -g.y); - b.scale(c[0].scale, c[0].scale); + a.prototype._cacheTiles = function(a, b, c, d, f) { + void 0 === f && (f = 4); + e(0 < f, "Infinite recursion is likely."); + var k = this._getTileBounds(b); + a.save(); + a.setTransform(1, 0, 0, 1, 0, 0); + a.clearRect(0, 0, d.w, d.h); + a.translate(-k.x, -k.y); + a.scale(b[0].scale, b[0].scale); var l = this._source.getBounds(); - b.translate(-l.x, -l.y); - 2 <= k.traceLevel && k.writer && k.writer.writeLn("Rendering Tiles: " + g); - this._source.render(b); - b.restore(); - for (var l = null, m = 0;m < c.length;m++) { - var n = c[m], p = n.bounds.clone(); - p.x -= g.x; - p.y -= g.y; - e.contains(p) || (l || (l = []), l.push(n)); - n.cachedSurfaceRegion = d(n.cachedSurfaceRegion, b, p); - } - l && (2 <= l.length ? (c = l.slice(0, l.length / 2 | 0), g = l.slice(c.length), this._cacheTiles(b, c, d, e, f - 1), this._cacheTiles(b, g, d, e, f - 1)) : this._cacheTiles(b, l, d, e, f - 1)); - }; - return b; - }(); - g.RenderableTileCache = q; - var w = function() { - return function(a, b) { - this.surfaceRegion = a; - this.scale = b; - }; - }(); - g.MipMapLevel = w; - q = function() { - function a(b, c, d) { - this._source = b; - this._levels = []; - this._surfaceRegionAllocator = c; - this._size = d; - } - a.prototype.render = function(a) { - }; - a.prototype.getLevel = function(a) { - a = Math.max(a.getAbsoluteScaleX(), a.getAbsoluteScaleY()); - var b = 0; - 1 !== a && (b = s(Math.round(Math.log(a) / Math.LN2), -5, 3)); - this._source.hasFlags(4) || (b = s(b, -5, 0)); - a = m(b); - var c = 5 + b, d = this._levels[c]; - if (!d) { - b = this._source.getBounds().clone(); - b.scale(a, a); - b.snap(); - var d = this._surfaceRegionAllocator.allocate(b.w, b.h), e = d.region, d = this._levels[c] = new w(d, a), c = d.surfaceRegion.surface.context; - c.save(); - c.beginPath(); - c.rect(e.x, e.y, e.w, e.h); - c.clip(); - c.setTransform(a, 0, 0, a, e.x - b.x, e.y - b.y); - this._source.render(c); - c.restore(); + a.translate(-l.x, -l.y); + 2 <= h.traceLevel && h.writer && h.writer.writeLn("Rendering Tiles: " + k); + this._source.render(a, 0); + a.restore(); + for (var l = null, m = 0;m < b.length;m++) { + var n = b[m], p = n.bounds.clone(); + p.x -= k.x; + p.y -= k.y; + d.contains(p) || (l || (l = []), l.push(n)); + n.cachedSurfaceRegion = c(n.cachedSurfaceRegion, a, p); } - return d; + l && (2 <= l.length ? (b = l.slice(0, l.length / 2 | 0), k = l.slice(b.length), this._cacheTiles(a, b, c, d, f - 1), this._cacheTiles(a, k, c, d, f - 1)) : this._cacheTiles(a, l, c, d, f - 1)); }; return a; }(); - g.MipMap = q; - })(k.Geometry || (k.Geometry = {})); - })(b.GFX || (b.GFX = {})); -})(Shumway || (Shumway = {})); -__extends = this.__extends || function(b, k) { - function g() { - this.constructor = b; + a.RenderableTileCache = k; + })(h.Geometry || (h.Geometry = {})); + })(c.GFX || (c.GFX = {})); +})(Shumway || (Shumway = {})); +__extends = this.__extends || function(c, h) { + function a() { + this.constructor = c; } - for (var f in k) { - k.hasOwnProperty(f) && (b[f] = k[f]); + for (var s in h) { + h.hasOwnProperty(s) && (c[s] = h[s]); } - g.prototype = k.prototype; - b.prototype = new g; + a.prototype = h.prototype; + c.prototype = new a; }; -(function(b) { - (function(k) { - var g = b.IntegerUtilities.roundToMultipleOfPowerOfTwo, f = b.Debug.assert, t = k.Geometry.Rectangle; - (function(b) { - var m = function(a) { - function b() { +(function(c) { + (function(h) { + var a = c.IntegerUtilities.roundToMultipleOfPowerOfTwo, s = c.Debug.assert, v = h.Geometry.Rectangle; + (function(c) { + var u = function(a) { + function c() { a.apply(this, arguments); } - __extends(b, a); - return b; - }(k.Geometry.Rectangle); - b.Region = m; - var d = function() { - function b(c, d) { - this._root = new a(0, 0, c | 0, d | 0, !1); + __extends(c, a); + return c; + }(h.Geometry.Rectangle); + c.Region = u; + var l = function() { + function a(c, d) { + this._root = new e(0, 0, c | 0, d | 0, !1); } - b.prototype.allocate = function(a, b) { + a.prototype.allocate = function(a, c) { a = Math.ceil(a); - b = Math.ceil(b); - f(0 < a && 0 < b); - var c = this._root.insert(a, b); - c && (c.allocator = this, c.allocated = !0); - return c; + c = Math.ceil(c); + s(0 < a && 0 < c); + var b = this._root.insert(a, c); + b && (b.allocator = this, b.allocated = !0); + return b; }; - b.prototype.free = function(a) { - f(a.allocator === this); + a.prototype.free = function(a) { + s(a.allocator === this); a.clear(); a.allocated = !1; }; - b.RANDOM_ORIENTATION = !0; - b.MAX_DEPTH = 256; - return b; + a.RANDOM_ORIENTATION = !0; + a.MAX_DEPTH = 256; + return a; }(); - b.CompactAllocator = d; - var a = function(a) { - function b(c, d, e, f, g) { - a.call(this, c, d, e, f); + c.CompactAllocator = l; + var e = function(a) { + function c(d, b, e, f, h) { + a.call(this, d, b, e, f); this._children = null; - this._horizontal = g; + this._horizontal = h; this.allocated = !1; } - __extends(b, a); - b.prototype.clear = function() { + __extends(c, a); + c.prototype.clear = function() { this._children = null; this.allocated = !1; }; - b.prototype.insert = function(a, b) { + c.prototype.insert = function(a, b) { return this._insert(a, b, 0); }; - b.prototype._insert = function(a, c, e) { - if (!(e > d.MAX_DEPTH || this.allocated || this.w < a || this.h < c)) { + c.prototype._insert = function(a, b, e) { + if (!(e > l.MAX_DEPTH || this.allocated || this.w < a || this.h < b)) { if (this._children) { - var f; - if ((f = this._children[0]._insert(a, c, e + 1)) || (f = this._children[1]._insert(a, c, e + 1))) { - return f; + var k; + if ((k = this._children[0]._insert(a, b, e + 1)) || (k = this._children[1]._insert(a, b, e + 1))) { + return k; } } else { - return f = !this._horizontal, d.RANDOM_ORIENTATION && (f = .5 <= Math.random()), this._children = this._horizontal ? [new b(this.x, this.y, this.w, c, !1), new b(this.x, this.y + c, this.w, this.h - c, f)] : [new b(this.x, this.y, a, this.h, !0), new b(this.x + a, this.y, this.w - a, this.h, f)], f = this._children[0], f.w === a && f.h === c ? (f.allocated = !0, f) : this._insert(a, c, e + 1); + return k = !this._horizontal, l.RANDOM_ORIENTATION && (k = .5 <= Math.random()), this._children = this._horizontal ? [new c(this.x, this.y, this.w, b, !1), new c(this.x, this.y + b, this.w, this.h - b, k)] : [new c(this.x, this.y, a, this.h, !0), new c(this.x + a, this.y, this.w - a, this.h, k)], k = this._children[0], k.w === a && k.h === b ? (k.allocated = !0, k) : this._insert(a, b, e + 1); } } }; - return b; - }(b.Region), c = function() { - function a(b, c, d, e) { - this._columns = b / d | 0; - this._rows = c / e | 0; - this._sizeW = d; + return c; + }(c.Region), m = function() { + function a(c, d, b, e) { + this._columns = c / b | 0; + this._rows = d / e | 0; + this._sizeW = b; this._sizeH = e; this._freeList = []; this._index = 0; this._total = this._columns * this._rows; } - a.prototype.allocate = function(a, b) { + a.prototype.allocate = function(a, c) { a = Math.ceil(a); - b = Math.ceil(b); - f(0 < a && 0 < b); - var c = this._sizeW, d = this._sizeH; - if (a > c || b > d) { + c = Math.ceil(c); + s(0 < a && 0 < c); + var b = this._sizeW, e = this._sizeH; + if (a > b || c > e) { return null; } - var e = this._freeList, g = this._index; - return 0 < e.length ? (c = e.pop(), f(!1 === c.allocated), c.allocated = !0, c) : g < this._total ? (e = g / this._columns | 0, c = new n((g - e * this._columns) * c, e * d, a, b), c.index = g, c.allocator = this, c.allocated = !0, this._index++, c) : null; + var k = this._freeList, h = this._index; + return 0 < k.length ? (b = k.pop(), s(!1 === b.allocated), b.w = a, b.h = c, b.allocated = !0, b) : h < this._total ? (k = h / this._columns | 0, b = new t((h - k * this._columns) * b, k * e, a, c), b.index = h, b.allocator = this, b.allocated = !0, this._index++, b) : null; }; a.prototype.free = function(a) { - f(a.allocator === this); + s(a.allocator === this); a.allocated = !1; this._freeList.push(a); }; return a; }(); - b.GridAllocator = c; - var n = function(a) { - function b(c, d, e, f) { - a.call(this, c, d, e, f); + c.GridAllocator = m; + var t = function(a) { + function c(d, b, e, f) { + a.call(this, d, b, e, f); this.index = -1; } - __extends(b, a); - return b; - }(b.Region); - b.GridCell = n; - var p = function() { - return function(a, b, c) { + __extends(c, a); + return c; + }(c.Region); + c.GridCell = t; + var q = function() { + return function(a, c, d) { this.size = a; - this.region = b; - this.allocator = c; + this.region = c; + this.allocator = d; }; - }(), e = function(a) { - function b(c, d, e, f, g) { - a.call(this, c, d, e, f); - this.region = g; + }(), n = function(a) { + function c(d, b, e, f, h) { + a.call(this, d, b, e, f); + this.region = h; } - __extends(b, a); - return b; - }(b.Region); - b.BucketCell = e; - m = function() { - function a(b, c) { - f(0 < b && 0 < c); + __extends(c, a); + return c; + }(c.Region); + c.BucketCell = n; + u = function() { + function c(a, d) { + s(0 < a && 0 < d); this._buckets = []; - this._w = b | 0; - this._h = c | 0; + this._w = a | 0; + this._h = d | 0; this._filled = 0; } - a.prototype.allocate = function(a, b) { - a = Math.ceil(a); - b = Math.ceil(b); - f(0 < a && 0 < b); - var d = Math.max(a, b); - if (a > this._w || b > this._h) { + c.prototype.allocate = function(c, d) { + c = Math.ceil(c); + d = Math.ceil(d); + s(0 < c && 0 < d); + var b = Math.max(c, d); + if (c > this._w || d > this._h) { return null; } - var k = null, h, m = this._buckets; + var e = null, k, h = this._buckets; do { - for (var n = 0;n < m.length && !(m[n].size >= d && (h = m[n], k = h.allocator.allocate(a, b)));n++) { + for (var l = 0;l < h.length && !(h[l].size >= b && (k = h[l], e = k.allocator.allocate(c, d)));l++) { } - if (!k) { - var q = this._h - this._filled; - if (q < b) { + if (!e) { + var p = this._h - this._filled; + if (p < d) { return null; } - var n = g(d, 2), s = 2 * n; - s > q && (s = q); - if (s < n) { + var l = a(b, 8), t = 2 * l; + t > p && (t = p); + if (t < l) { return null; } - q = new t(0, this._filled, this._w, s); - this._buckets.push(new p(n, q, new c(q.w, q.h, n, n))); - this._filled += s; + p = new v(0, this._filled, this._w, t); + this._buckets.push(new q(l, p, new m(p.w, p.h, l, l))); + this._filled += t; } - } while (!k); - return new e(h.region.x + k.x, h.region.y + k.y, k.w, k.h, k); + } while (!e); + return new n(k.region.x + e.x, k.region.y + e.y, e.w, e.h, e); }; - a.prototype.free = function(a) { + c.prototype.free = function(a) { a.region.allocator.free(a.region); }; - return a; + return c; }(); - b.BucketAllocator = m; - })(k.RegionAllocator || (k.RegionAllocator = {})); - (function(b) { - var f = function() { - function b(a) { - this._createSurface = a; + c.BucketAllocator = u; + })(h.RegionAllocator || (h.RegionAllocator = {})); + (function(a) { + var c = function() { + function a(c) { + this._createSurface = c; this._surfaces = []; } - Object.defineProperty(b.prototype, "surfaces", {get:function() { + Object.defineProperty(a.prototype, "surfaces", {get:function() { return this._surfaces; }, enumerable:!0, configurable:!0}); - b.prototype._createNewSurface = function(a, b) { - var d = this._createSurface(a, b); - this._surfaces.push(d); - return d; + a.prototype._createNewSurface = function(a, c) { + var h = this._createSurface(a, c); + this._surfaces.push(h); + return h; }; - b.prototype.addSurface = function(a) { + a.prototype.addSurface = function(a) { this._surfaces.push(a); }; - b.prototype.allocate = function(a, b) { - for (var d = 0;d < this._surfaces.length;d++) { - var f = this._surfaces[d].allocate(a, b); - if (f) { - return f; + a.prototype.allocate = function(a, c) { + for (var h = 0;h < this._surfaces.length;h++) { + var l = this._surfaces[h].allocate(a, c); + if (l) { + return l; } } - return this._createNewSurface(a, b).allocate(a, b); + return this._createNewSurface(a, c).allocate(a, c); }; - b.prototype.free = function(a) { + a.prototype.free = function(a) { }; - return b; + return a; }(); - b.SimpleAllocator = f; - })(k.SurfaceRegionAllocator || (k.SurfaceRegionAllocator = {})); - })(b.GFX || (b.GFX = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - var g = k.Geometry.Point, f = k.Geometry.Matrix, t = b.Debug.assert, s = b.Debug.unexpected; + a.SimpleAllocator = c; + })(h.SurfaceRegionAllocator || (h.SurfaceRegionAllocator = {})); + })(c.GFX || (c.GFX = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + var a = h.Geometry.Rectangle, s = h.Geometry.Matrix, v = h.Geometry.DirtyRegion, p = c.Debug.assert; + (function(a) { + a[a.Normal = 1] = "Normal"; + a[a.Layer = 2] = "Layer"; + a[a.Multiply = 3] = "Multiply"; + a[a.Screen = 4] = "Screen"; + a[a.Lighten = 5] = "Lighten"; + a[a.Darken = 6] = "Darken"; + a[a.Difference = 7] = "Difference"; + a[a.Add = 8] = "Add"; + a[a.Subtract = 9] = "Subtract"; + a[a.Invert = 10] = "Invert"; + a[a.Alpha = 11] = "Alpha"; + a[a.Erase = 12] = "Erase"; + a[a.Overlay = 13] = "Overlay"; + a[a.HardLight = 14] = "HardLight"; + })(h.BlendMode || (h.BlendMode = {})); + var u = h.BlendMode; (function(a) { a[a.None = 0] = "None"; - a[a.Upward = 1] = "Upward"; - a[a.Downward = 2] = "Downward"; - })(k.Direction || (k.Direction = {})); - (function(a) { - a[a.Never = 0] = "Never"; - a[a.Always = 1] = "Always"; - a[a.Auto = 2] = "Auto"; - })(k.PixelSnapping || (k.PixelSnapping = {})); - (function(a) { - a[a.Never = 0] = "Never"; - a[a.Always = 1] = "Always"; - })(k.Smoothing || (k.Smoothing = {})); - (function(a) { - a[a.Empty = 0] = "Empty"; - a[a.Dirty = 1] = "Dirty"; - a[a.IsMask = 2] = "IsMask"; - a[a.IgnoreMask = 8] = "IgnoreMask"; - a[a.IgnoreQuery = 16] = "IgnoreQuery"; - a[a.InvalidBounds = 32] = "InvalidBounds"; - a[a.InvalidConcatenatedMatrix = 64] = "InvalidConcatenatedMatrix"; - a[a.InvalidInvertedConcatenatedMatrix = 128] = "InvalidInvertedConcatenatedMatrix"; - a[a.InvalidConcatenatedColorMatrix = 256] = "InvalidConcatenatedColorMatrix"; - a[a.InvalidPaint = 512] = "InvalidPaint"; - a[a.EnterClip = 4096] = "EnterClip"; - a[a.LeaveClip = 8192] = "LeaveClip"; - a[a.Visible = 16384] = "Visible"; + a[a.BoundsAutoCompute = 2] = "BoundsAutoCompute"; + a[a.IsMask = 4] = "IsMask"; + a[a.Dirty = 16] = "Dirty"; + a[a.InvalidBounds = 256] = "InvalidBounds"; + a[a.InvalidConcatenatedMatrix = 512] = "InvalidConcatenatedMatrix"; + a[a.InvalidInvertedConcatenatedMatrix = 1024] = "InvalidInvertedConcatenatedMatrix"; + a[a.InvalidConcatenatedColorMatrix = 2048] = "InvalidConcatenatedColorMatrix"; + a[a.UpOnAddedOrRemoved = a.InvalidBounds | a.Dirty] = "UpOnAddedOrRemoved"; + a[a.UpOnMoved = a.InvalidBounds | a.Dirty] = "UpOnMoved"; + a[a.DownOnAddedOrRemoved = a.InvalidConcatenatedMatrix | a.InvalidInvertedConcatenatedMatrix | a.InvalidConcatenatedColorMatrix] = "DownOnAddedOrRemoved"; + a[a.DownOnMoved = a.InvalidConcatenatedMatrix | a.InvalidInvertedConcatenatedMatrix | a.InvalidConcatenatedColorMatrix] = "DownOnMoved"; + a[a.UpOnColorMatrixChanged = a.Dirty] = "UpOnColorMatrixChanged"; + a[a.DownOnColorMatrixChanged = a.InvalidConcatenatedColorMatrix] = "DownOnColorMatrixChanged"; + a[a.Visible = 65536] = "Visible"; + a[a.UpOnInvalidate = a.InvalidBounds | a.Dirty] = "UpOnInvalidate"; + a[a.Default = a.BoundsAutoCompute | a.InvalidBounds | a.InvalidConcatenatedMatrix | a.InvalidInvertedConcatenatedMatrix | a.Visible] = "Default"; + a[a.CacheAsBitmap = 131072] = "CacheAsBitmap"; + a[a.PixelSnapping = 262144] = "PixelSnapping"; + a[a.ImageSmoothing = 524288] = "ImageSmoothing"; + a[a.Dynamic = 1048576] = "Dynamic"; + a[a.Scalable = 2097152] = "Scalable"; + a[a.Tileable = 4194304] = "Tileable"; a[a.Transparent = 32768] = "Transparent"; - })(k.FrameFlags || (k.FrameFlags = {})); + })(h.NodeFlags || (h.NodeFlags = {})); + var l = h.NodeFlags; + (function(a) { + a[a.Node = 1] = "Node"; + a[a.Shape = 3] = "Shape"; + a[a.Group = 5] = "Group"; + a[a.Stage = 13] = "Stage"; + a[a.Renderable = 33] = "Renderable"; + })(h.NodeType || (h.NodeType = {})); + var e = h.NodeType; (function(a) { a[a.None = 0] = "None"; - a[a.AllowMatrixWrite = 1] = "AllowMatrixWrite"; - a[a.AllowColorMatrixWrite = 2] = "AllowColorMatrixWrite"; - a[a.AllowBlendModeWrite = 4] = "AllowBlendModeWrite"; - a[a.AllowFiltersWrite = 8] = "AllowFiltersWrite"; - a[a.AllowMaskWrite = 16] = "AllowMaskWrite"; - a[a.AllowChildrenWrite = 32] = "AllowChildrenWrite"; - a[a.AllowClipWrite = 64] = "AllowClipWrite"; - a[a.AllowAllWrite = a.AllowMatrixWrite | a.AllowColorMatrixWrite | a.AllowBlendModeWrite | a.AllowFiltersWrite | a.AllowMaskWrite | a.AllowChildrenWrite | a.AllowClipWrite] = "AllowAllWrite"; - })(k.FrameCapabilityFlags || (k.FrameCapabilityFlags = {})); - var m = k.FrameCapabilityFlags, d = function() { + a[a.OnStageBoundsChanged = 1] = "OnStageBoundsChanged"; + })(h.NodeEventType || (h.NodeEventType = {})); + var m = function() { function a() { - this._id = a._nextID++; - this._flags = 17376; - this._capability = m.AllowAllWrite; - this._parent = null; - this._clip = -1; - this._blendMode = 1; - this._filters = []; - this._mask = null; - this._matrix = f.createIdentity(); - this._concatenatedMatrix = f.createIdentity(); - this._invertedConcatenatedMatrix = null; - this._colorMatrix = k.ColorMatrix.createIdentity(); - this._concatenatedColorMatrix = k.ColorMatrix.createIdentity(); - this._pixelSnapping = this._smoothing = 0; - } - a._getAncestors = function(b, d) { - "undefined" === typeof d && (d = null); - var f = a._path; - for (f.length = 0;b && b !== d;) { - t(b !== b._parent), f.push(b), b = b._parent; - } - t(b === d, "Last ancestor is not an ancestor."); - return f; - }; - Object.defineProperty(a.prototype, "parent", {get:function() { - return this._parent; - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "id", {get:function() { - return this._id; - }, enumerable:!0, configurable:!0}); - a.prototype._setFlags = function(a) { - this._flags |= a; + } + a.prototype.visitNode = function(a, b) { }; - a.prototype._removeFlags = function(a) { - this._flags &= ~a; + a.prototype.visitShape = function(a, b) { + this.visitNode(a, b); }; - a.prototype._hasFlags = function(a) { - return(this._flags & a) === a; + a.prototype.visitGroup = function(a, b) { + this.visitNode(a, b); + for (var c = a.getChildren(), d = 0;d < c.length;d++) { + c[d].visit(this, b); + } }; - a.prototype._toggleFlags = function(a, b) { - this._flags = b ? this._flags | a : this._flags & ~a; + a.prototype.visitStage = function(a, b) { + this.visitGroup(a, b); }; - a.prototype._hasAnyFlags = function(a) { - return!!(this._flags & a); + a.prototype.visitRenderable = function(a, b) { + this.visitNode(a, b); }; - a.prototype._findClosestAncestor = function(a) { - for (var b = this;b;) { - if (!1 === b._hasFlags(a)) { - return b; - } - t(b !== b._parent); - b = b._parent; - } - return null; + return a; + }(); + h.NodeVisitor = m; + var t = function() { + return function() { }; - a.prototype._isAncestor = function(a) { - for (;a;) { - if (a === this) { - return!0; - } - t(a !== a._parent); - a = a._parent; - } - return!1; + }(); + h.State = t; + var q = function(a) { + function b() { + a.call(this); + this.matrix = s.createIdentity(); + } + __extends(b, a); + b.prototype.transform = function(a) { + var b = this.clone(); + b.matrix.preMultiply(a.getMatrix()); + return b; }; - a.prototype.setCapability = function(a, b, d) { - "undefined" === typeof b && (b = !0); - "undefined" === typeof d && (d = 0); - this._capability = b ? this._capability | a : this._capability & ~a; - if (1 === d && this._parent) { - this._parent.setCapability(a, b, d); - } else { - if (2 === d && this instanceof k.FrameContainer) { - for (var e = this._children, f = 0;f < e.length;f++) { - e[f].setCapability(a, b, d); - } - } - } + b.allocate = function() { + var a = b._dirtyStack, c = null; + a.length && (c = a.pop()); + return c; }; - a.prototype.removeCapability = function(a) { - this.setCapability(a, !1); + b.prototype.clone = function() { + var a = b.allocate(); + a || (a = new b); + a.set(this); + return a; }; - a.prototype.hasCapability = function(a) { - return this._capability & a; + b.prototype.set = function(a) { + this.matrix.set(a.matrix); }; - a.prototype.checkCapability = function(a) { - this._capability & a || s("Frame doesn't have capability: " + m[a]); + b.prototype.free = function() { + b._dirtyStack.push(this); }; - a.prototype._propagateFlagsUp = function(a) { - if (!this._hasFlags(a)) { - this._setFlags(a); - var b = this._parent; - b && b._propagateFlagsUp(a); + b._dirtyStack = []; + return b; + }(t); + h.MatrixState = q; + var n = function(a) { + function b() { + a.apply(this, arguments); + this.isDirty = !0; + } + __extends(b, a); + b.prototype.start = function(a, b) { + this._dirtyRegion = b; + var c = new q; + c.matrix.setIdentity(); + a.visit(this, c); + c.free(); + }; + b.prototype.visitGroup = function(a, b) { + var c = a.getChildren(); + this.visitNode(a, b); + for (var d = 0;d < c.length;d++) { + var e = c[d], f = b.transform(e.getTransform()); + e.visit(this, f); + f.free(); } }; - a.prototype._propagateFlagsDown = function(a) { - this._setFlags(a); + b.prototype.visitNode = function(a, b) { + a.hasFlags(16) && (this.isDirty = !0); + a.toggleFlags(); }; - a.prototype._invalidatePosition = function() { - this._propagateFlagsDown(192); - this._parent && this._parent._invalidateBounds(); - this._invalidateParentPaint(); - }; - a.prototype.invalidatePaint = function() { - this._propagateFlagsUp(512); + return b; + }(m); + h.DirtyNodeVisitor = n; + t = function(a) { + function b(c) { + a.call(this); + this.writer = c; + } + __extends(b, a); + b.prototype.visitNode = function(a, b) { }; - a.prototype._invalidateParentPaint = function() { - this._parent && this._parent._propagateFlagsUp(512); + b.prototype.visitShape = function(a, b) { + this.writer.writeLn(a.toString()); + this.visitNode(a, b); + }; + b.prototype.visitGroup = function(a, b) { + this.visitNode(a, b); + var c = a.getChildren(); + this.writer.enter(a.toString() + " " + c.length); + for (var d = 0;d < c.length;d++) { + c[d].visit(this, b); + } + this.writer.outdent(); }; - a.prototype._invalidateBounds = function() { - this._propagateFlagsUp(32); + b.prototype.visitStage = function(a, b) { + this.visitGroup(a, b); }; - Object.defineProperty(a.prototype, "properties", {get:function() { - return this._properties || (this._properties = Object.create(null)); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "x", {get:function() { - return this._matrix.tx; - }, set:function(a) { - this.checkCapability(1); - this._matrix.tx = a; - this._invalidatePosition(); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "y", {get:function() { - return this._matrix.ty; - }, set:function(a) { - this.checkCapability(1); - this._matrix.ty = a; - this._invalidatePosition(); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "matrix", {get:function() { - return this._matrix; - }, set:function(a) { - this.checkCapability(1); - this._matrix.set(a); - this._invalidatePosition(); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "blendMode", {get:function() { - return this._blendMode; - }, set:function(a) { - a |= 0; - this.checkCapability(4); - this._blendMode = a; - this._invalidateParentPaint(); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "filters", {get:function() { - return this._filters; - }, set:function(a) { - this.checkCapability(8); - this._filters = a; - this._invalidateParentPaint(); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "colorMatrix", {get:function() { - return this._colorMatrix; - }, set:function(a) { - this.checkCapability(2); - this._colorMatrix.set(a); - this._propagateFlagsDown(256); - this._invalidateParentPaint(); + return b; + }(m); + h.TracingNodeVisitor = t; + var k = function() { + function f() { + this._clip = -1; + this._eventListeners = null; + this._id = f._nextId++; + this._type = 1; + this._index = -1; + this._parent = null; + this.reset(); + } + Object.defineProperty(f.prototype, "id", {get:function() { + return this._id; }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "mask", {get:function() { - return this._mask; - }, set:function(a) { - this.checkCapability(16); - this._mask && this._mask !== a && this._mask._removeFlags(2); - if (this._mask = a) { - this._mask._setFlags(2), this._mask.invalidate(); + f.prototype._dispatchEvent = function() { + if (this._eventListeners) { + for (var a = this._eventListeners, b = 0;b < a.length;b++) { + var c = a[b]; + 1 === c.type && c.listener(this, 1); + } } - this.invalidate(); + }; + f.prototype.addEventListener = function(a, b) { + this._eventListeners || (this._eventListeners = []); + this._eventListeners.push({type:a, listener:b}); + }; + Object.defineProperty(f.prototype, "properties", {get:function() { + return this._properties || (this._properties = {}); }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "clip", {get:function() { + f.prototype.reset = function() { + this._flags = l.Default; + this._properties = this._transform = this._layer = this._bounds = null; + }; + Object.defineProperty(f.prototype, "clip", {get:function() { return this._clip; }, set:function(a) { - this.checkCapability(64); this._clip = a; }, enumerable:!0, configurable:!0}); - a.prototype.getBounds = function() { - t(!1, "Override this."); - return null; + Object.defineProperty(f.prototype, "parent", {get:function() { + return this._parent; + }, enumerable:!0, configurable:!0}); + f.prototype.getTransformedBounds = function(a) { + var b = this.getBounds(!0); + if (a !== this && !b.isEmpty()) { + var c = this.getTransform().getConcatenatedMatrix(); + a ? (a = a.getTransform().getInvertedConcatenatedMatrix(), a.preMultiply(c), a.transformRectangleAABB(b), a.free()) : c.transformRectangleAABB(b); + } + return b; }; - a.prototype.gatherPreviousDirtyRegions = function() { - var a = this.stage; - a.trackDirtyRegions && this.visit(function(b) { - if (b instanceof k.FrameContainer) { - return 0; + f.prototype._markCurrentBoundsAsDirtyRegion = function() { + }; + f.prototype.getStage = function(a) { + void 0 === a && (a = !0); + for (var b = this._parent;b;) { + if (b.isType(13)) { + var c = b; + if (a) { + if (c.dirtyRegion) { + return c; + } + } else { + return c; + } } - b._previouslyRenderedAABB && a.dirtyRegion.addDirtyRectangle(b._previouslyRenderedAABB); - return 0; - }); + b = b._parent; + } + return null; + }; + f.prototype.getChildren = function(a) { + throw c.Debug.abstractMethod("Node::getChildren"); + }; + f.prototype.getBounds = function(a) { + throw c.Debug.abstractMethod("Node::getBounds"); + }; + f.prototype.setBounds = function(b) { + p(!(this._flags & 2)); + (this._bounds || (this._bounds = a.createEmpty())).set(b); + this.removeFlags(256); + }; + f.prototype.clone = function() { + throw c.Debug.abstractMethod("Node::clone"); + }; + f.prototype.setFlags = function(a) { + this._flags |= a; + }; + f.prototype.hasFlags = function(a) { + return(this._flags & a) === a; + }; + f.prototype.hasAnyFlags = function(a) { + return!!(this._flags & a); }; - a.prototype.getConcatenatedColorMatrix = function() { - if (!this._parent) { - return this._colorMatrix; + f.prototype.removeFlags = function(a) { + this._flags &= ~a; + }; + f.prototype.toggleFlags = function() { + this._flags &= -17; + }; + f.prototype._propagateFlagsUp = function(a) { + if (0 !== a && !this.hasFlags(a)) { + this.hasFlags(2) || (a &= -257); + this.setFlags(a); + var b = this._parent; + b && b._propagateFlagsUp(a); } - if (this._hasFlags(256)) { - for (var b = this._findClosestAncestor(256), d = a._getAncestors(this, b), f = b ? b._concatenatedColorMatrix.clone() : k.ColorMatrix.createIdentity(), e = d.length - 1;0 <= e;e--) { - b = d[e], t(b._hasFlags(256)), f.multiply(b._colorMatrix), b._concatenatedColorMatrix.set(f), b._removeFlags(256); + }; + f.prototype._propagateFlagsDown = function(a) { + throw c.Debug.abstractMethod("Node::_propagateFlagsDown"); + }; + f.prototype.isAncestor = function(a) { + for (;a;) { + if (a === this) { + return!0; } + p(a !== a._parent); + a = a._parent; } - return this._concatenatedColorMatrix; + return!1; }; - a.prototype.getConcatenatedAlpha = function(a) { - "undefined" === typeof a && (a = null); - for (var b = this, d = 1;b && b !== a;) { - d *= b._colorMatrix.alphaMultiplier, b = b._parent; + f._getAncestors = function(a, b) { + var c = f._path; + for (c.length = 0;a && a !== b;) { + p(a !== a._parent), c.push(a), a = a._parent; } - return d; + p(a === b, "Last ancestor is not an ancestor."); + return c; }; - Object.defineProperty(a.prototype, "stage", {get:function() { - for (var a = this;a._parent;) { + f.prototype._findClosestAncestor = function() { + for (var a = this;a;) { + if (!1 === a.hasFlags(512)) { + return a; + } + p(a !== a._parent); a = a._parent; } - return a instanceof k.Stage ? a : null; - }, enumerable:!0, configurable:!0}); - a.prototype.getConcatenatedMatrix = function() { - if (this._hasFlags(64)) { - for (var b = this._findClosestAncestor(64), d = a._getAncestors(this, b), g = b ? b._concatenatedMatrix.clone() : f.createIdentity(), e = d.length - 1;0 <= e;e--) { - b = d[e], t(b._hasFlags(64)), g.preMultiply(b._matrix), b._concatenatedMatrix.set(g), b._removeFlags(64); - } - } - return this._concatenatedMatrix; - }; - a.prototype._getInvertedConcatenatedMatrix = function() { - this._hasFlags(128) && (this._invertedConcatenatedMatrix || (this._invertedConcatenatedMatrix = f.createIdentity()), this._invertedConcatenatedMatrix.set(this.getConcatenatedMatrix()), this._invertedConcatenatedMatrix.inverse(this._invertedConcatenatedMatrix), this._removeFlags(128)); - return this._invertedConcatenatedMatrix; - }; - a.prototype.invalidate = function() { - this._setFlags(1); - }; - a.prototype.visit = function(a, b, d, e) { - "undefined" === typeof d && (d = 0); - "undefined" === typeof e && (e = 0); - var f, g, m = e & 8; - f = [this]; - var s, r = !!b; - r && (s = [b.clone()]); - for (var h = [d];0 < f.length;) { - g = f.pop(); - r && (b = s.pop()); - d = h.pop() | g._flags; - var x = a(g, b, d); - if (0 === x) { - if (g instanceof k.FrameContainer) { - var y = g._children.length; - if (e & 16 && !k.disableClipping.value) { - for (var x = g.gatherLeaveClipEvents(), G = y - 1;0 <= G;G--) { - if (x && x[G]) { - for (;x[G].length;) { - if (y = x[G].shift(), f.push(y), h.push(8192), r) { - var I = b.clone(); - I.preMultiply(y.matrix); - s.push(I); - } - } - } - var C = g._children[G]; - t(C); - f.push(C); - r && (I = b.clone(), I.preMultiply(C.matrix), s.push(I)); - 0 <= C.clip ? h.push(d | 4096) : h.push(d); - } - } else { - for (G = 0;G < y;G++) { - if (C = g._children[m ? G : y - 1 - G]) { - f.push(C), r && (I = b.clone(), I.preMultiply(C.matrix), s.push(I)), h.push(d); - } - } - } - } - } else { - if (1 === x) { - break; - } + return null; + }; + f.prototype.isType = function(a) { + return this._type === a; + }; + f.prototype.isTypeOf = function(a) { + return(this._type & a) === a; + }; + f.prototype.isLeaf = function() { + return this.isType(33) || this.isType(3); + }; + f.prototype.isLinear = function() { + if (this.isLeaf()) { + return!0; + } + if (this.isType(5)) { + var a = this._children; + if (1 === a.length && a[0].isLinear()) { + return!0; } } + return!1; }; - a.prototype.getDepth = function() { - for (var a = 0, b = this;b._parent;) { - a++, b = b._parent; - } - return a; + f.prototype.getTransformMatrix = function() { + var a; + void 0 === a && (a = !1); + return this.getTransform().getMatrix(a); + }; + f.prototype.getTransform = function() { + null === this._transform && (this._transform = new d(this)); + return this._transform; + }; + f.prototype.getLayer = function() { + null === this._layer && (this._layer = new b(this)); + return this._layer; }; - Object.defineProperty(a.prototype, "smoothing", {get:function() { - return this._smoothing; - }, set:function(a) { - this._smoothing = a; - this.invalidate(); - }, enumerable:!0, configurable:!0}); - Object.defineProperty(a.prototype, "pixelSnapping", {get:function() { - return this._pixelSnapping; - }, set:function(a) { - this._pixelSnapping = a; - this.invalidate(); - }, enumerable:!0, configurable:!0}); - a.prototype.queryFramesByPoint = function(a, b, d) { - "undefined" === typeof b && (b = !1); - "undefined" === typeof d && (d = !1); - var e = f.createIdentity(), m = g.createEmpty(), l = []; - this.visit(function(f, g, r) { - if (r & 16) { - return 2; - } - g.inverse(e); - m.set(a); - e.transformPoint(m); - if (f.getBounds().containsPoint(m)) { - if (f instanceof k.FrameContainer) { - if (d && (l.push(f), !b)) { - return 1; - } - } else { - if (l.push(f), !b) { - return 1; - } - } - return 0; - } - return 2; - }, f.createIdentity(), 0); - l.reverse(); - return l; + f.prototype.visit = function(a, b) { + switch(this._type) { + case 1: + a.visitNode(this, b); + break; + case 5: + a.visitGroup(this, b); + break; + case 13: + a.visitStage(this, b); + break; + case 3: + a.visitShape(this, b); + break; + case 33: + a.visitRenderable(this, b); + break; + default: + c.Debug.unexpectedCase(); + } }; - a._path = []; - a._nextID = 0; - return a; + f.prototype.invalidate = function() { + this._propagateFlagsUp(l.UpOnInvalidate); + }; + f.prototype.toString = function(a) { + void 0 === a && (a = !1); + var b = e[this._type] + " " + this._id; + a && (b += " " + this._bounds.toString()); + return b; + }; + f._path = []; + f._nextId = 0; + return f; }(); - k.Frame = d; - })(b.GFX || (b.GFX = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - var g = k.Geometry.Rectangle, f = b.Debug.assert, t = function(b) { - function k() { + h.Node = k; + var f = function(b) { + function c() { b.call(this); + this._type = 5; this._children = []; } - __extends(k, b); - k.prototype.addChild = function(b) { - this.checkCapability(32); - b && (f(b !== this), b._parent = this, b._invalidatePosition()); - this._children.push(b); - return b; - }; - k.prototype.addChildAt = function(b, a) { - this.checkCapability(32); - f(0 <= a && a <= this._children.length); - a === this._children.length ? this._children.push(b) : this._children.splice(a, 0, b); - b && (f(b !== this), b._parent = this, b._invalidatePosition()); - return b; - }; - k.prototype.removeChild = function(b) { - this.checkCapability(32); - b._parent === this && (b = this._children.indexOf(b), this.removeChildAt(b)); - }; - k.prototype.removeChildAt = function(b) { - this.checkCapability(32); - f(0 <= b && b < this._children.length); - if (b = this._children.splice(b, 1)[0]) { - b._parent = null, b._invalidatePosition(); - } - }; - k.prototype.clearChildren = function() { - this.checkCapability(32); - for (var b = 0;b < this._children.length;b++) { - var a = this._children[b]; - a && a._invalidatePosition(); + __extends(c, b); + c.prototype.getChildren = function(a) { + void 0 === a && (a = !1); + return a ? this._children.slice(0) : this._children; + }; + c.prototype.childAt = function(a) { + p(0 <= a && a < this._children.length); + return this._children[a]; + }; + Object.defineProperty(c.prototype, "child", {get:function() { + p(1 === this._children.length); + return this._children[0]; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "groupChild", {get:function() { + p(1 === this._children.length); + p(this._children[0] instanceof c); + return this._children[0]; + }, enumerable:!0, configurable:!0}); + c.prototype.addChild = function(a) { + p(a); + p(!a.isAncestor(this)); + a._parent && a._parent.removeChildAt(a._index); + a._parent = this; + a._index = this._children.length; + this._children.push(a); + this._propagateFlagsUp(l.UpOnAddedOrRemoved); + a._propagateFlagsDown(l.DownOnAddedOrRemoved); + }; + c.prototype.removeChildAt = function(a) { + p(0 <= a && a < this._children.length); + var b = this._children[a]; + p(a === b._index); + this._children.splice(a, 1); + b._index = -1; + b._parent = null; + this._propagateFlagsUp(l.UpOnAddedOrRemoved); + b._propagateFlagsDown(l.DownOnAddedOrRemoved); + }; + c.prototype.clearChildren = function() { + for (var a = 0;a < this._children.length;a++) { + var b = this._children[a]; + b && (b._index = -1, b._parent = null, b._propagateFlagsDown(l.DownOnAddedOrRemoved)); } this._children.length = 0; + this._propagateFlagsUp(l.UpOnAddedOrRemoved); }; - k.prototype._propagateFlagsDown = function(b) { - if (!this._hasFlags(b)) { - this._setFlags(b); - for (var a = this._children, c = 0;c < a.length;c++) { - a[c]._propagateFlagsDown(b); + c.prototype._propagateFlagsDown = function(a) { + if (!this.hasFlags(a)) { + this.setFlags(a); + for (var b = this._children, c = 0;c < b.length;c++) { + b[c]._propagateFlagsDown(a); } } }; - k.prototype.getBounds = function() { - if (!this._hasFlags(32)) { - return this._bounds; - } - for (var b = g.createEmpty(), a = 0;a < this._children.length;a++) { - var c = this._children[a], f = c.getBounds().clone(); - c.matrix.transformRectangleAABB(f); - b.union(f); - } - this._bounds = b; - this._removeFlags(32); - return b; - }; - k.prototype.gatherLeaveClipEvents = function() { - for (var b = this._children.length, a = null, c = 0;c < b;c++) { - var f = this._children[c]; - if (0 <= f.clip) { - var g = c + f.clip, a = a || []; - a[g] || (a[g] = []); - a[g].push(f); + c.prototype.getBounds = function(b) { + void 0 === b && (b = !1); + var c = this._bounds || (this._bounds = a.createEmpty()); + if (this.hasFlags(256)) { + c.setEmpty(); + for (var d = this._children, e = a.allocate(), f = 0;f < d.length;f++) { + var g = d[f]; + e.set(g.getBounds()); + g.getTransformMatrix().transformRectangleAABB(e); + c.union(e); } + e.free(); + this.removeFlags(256); } - return a; + return b ? c.clone() : c; }; - return k; - }(k.Frame); - k.FrameContainer = t; - })(b.GFX || (b.GFX = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - var g = k.Geometry.Rectangle, f = k.Geometry.DirtyRegion, t = b.Debug.assert; - (function(b) { - b[b.Normal = 1] = "Normal"; - b[b.Layer = 2] = "Layer"; - b[b.Multiply = 3] = "Multiply"; - b[b.Screen = 4] = "Screen"; - b[b.Lighten = 5] = "Lighten"; - b[b.Darken = 6] = "Darken"; - b[b.Difference = 7] = "Difference"; - b[b.Add = 8] = "Add"; - b[b.Subtract = 9] = "Subtract"; - b[b.Invert = 10] = "Invert"; - b[b.Alpha = 11] = "Alpha"; - b[b.Erase = 12] = "Erase"; - b[b.Overlay = 13] = "Overlay"; - b[b.HardLight = 14] = "HardLight"; - })(k.BlendMode || (k.BlendMode = {})); - (function(b) { - b[b.None = 0] = "None"; - b[b.Continue = 0] = "Continue"; - b[b.Stop = 1] = "Stop"; - b[b.Skip = 2] = "Skip"; - b[b.FrontToBack = 8] = "FrontToBack"; - b[b.Clips = 16] = "Clips"; - })(k.VisitorFlags || (k.VisitorFlags = {})); - k.StageRendererOptions = function() { - return function() { - this.debug = !1; - this.paintRenderable = !0; - this.paintViewport = this.paintFlashing = this.paintBounds = !1; + return c; + }(k); + h.Group = f; + var d = function() { + function a(b) { + this._node = b; + this._matrix = s.createIdentity(); + this._colorMatrix = h.ColorMatrix.createIdentity(); + this._concatenatedMatrix = s.createIdentity(); + this._invertedConcatenatedMatrix = s.createIdentity(); + this._concatenatedColorMatrix = h.ColorMatrix.createIdentity(); + } + a.prototype.setMatrix = function(a) { + this._matrix.isEqual(a) || (this._matrix.set(a), this._node._propagateFlagsUp(l.UpOnMoved), this._node._propagateFlagsDown(l.DownOnMoved)); + }; + a.prototype.setColorMatrix = function(a) { + this._colorMatrix.set(a); + this._node._propagateFlagsUp(l.UpOnColorMatrixChanged); + this._node._propagateFlagsDown(l.DownOnColorMatrixChanged); + }; + a.prototype.getMatrix = function(a) { + void 0 === a && (a = !1); + return a ? this._matrix.clone() : this._matrix; + }; + a.prototype.hasColorMatrix = function() { + return null !== this._colorMatrix; + }; + a.prototype.getColorMatrix = function(a) { + void 0 === a && (a = !1); + null === this._colorMatrix && (this._colorMatrix = h.ColorMatrix.createIdentity()); + return a ? this._colorMatrix.clone() : this._colorMatrix; + }; + a.prototype.getConcatenatedMatrix = function() { + var a; + void 0 === a && (a = !1); + if (this._node.hasFlags(512)) { + for (var b = this._node._findClosestAncestor(), c = k._getAncestors(this._node, b), d = b ? b.getTransform()._concatenatedMatrix.clone() : s.createIdentity(), e = c.length - 1;0 <= e;e--) { + var b = c[e], f = b.getTransform(); + p(b.hasFlags(512)); + d.preMultiply(f._matrix); + f._concatenatedMatrix.set(d); + b.removeFlags(512); + } + } + return a ? this._concatenatedMatrix.clone() : this._concatenatedMatrix; + }; + a.prototype.getInvertedConcatenatedMatrix = function() { + var a = !0; + void 0 === a && (a = !1); + this._node.hasFlags(1024) && (this.getConcatenatedMatrix().inverse(this._invertedConcatenatedMatrix), this._node.removeFlags(1024)); + return a ? this._invertedConcatenatedMatrix.clone() : this._invertedConcatenatedMatrix; }; + a.prototype.toString = function() { + return this._matrix.toString(); + }; + return a; }(); - (function(b) { - b[b.Canvas2D = 0] = "Canvas2D"; - b[b.WebGL = 1] = "WebGL"; - b[b.Both = 2] = "Both"; - b[b.DOM = 3] = "DOM"; - b[b.SVG = 4] = "SVG"; - })(k.Backend || (k.Backend = {})); - var s = function() { - function b(d, a, c) { - this._canvas = d; - this._stage = a; - this._options = c; - this._viewport = g.createSquare(); + h.Transform = d; + var b = function() { + function a(b) { + this._node = b; + this._mask = null; + this._blendMode = 1; } - Object.defineProperty(b.prototype, "viewport", {set:function(b) { - this._viewport.set(b); + Object.defineProperty(a.prototype, "filters", {get:function() { + return this._filters; + }, set:function(a) { + this._filters = a; }, enumerable:!0, configurable:!0}); - b.prototype.render = function() { - }; - b.prototype.resize = function() { + Object.defineProperty(a.prototype, "blendMode", {get:function() { + return this._blendMode; + }, set:function(a) { + this._blendMode = a; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(a.prototype, "mask", {get:function() { + return this._mask; + }, set:function(a) { + this._mask && this._mask !== a && this._mask.removeFlags(4); + (this._mask = a) && this._mask.setFlags(4); + }, enumerable:!0, configurable:!0}); + a.prototype.toString = function() { + return u[this._blendMode]; }; - return b; + return a; }(); - k.StageRenderer = s; - s = function(b) { - function d(a, c, d) { - "undefined" === typeof d && (d = !1); + h.Layer = b; + t = function(b) { + function c(a) { b.call(this); - this.w = a; - this.h = c; - this.dirtyRegion = new f(a, c); - this.trackDirtyRegions = d; - this._setFlags(1); + p(a); + this._source = a; + this._type = 3; + this.ratio = 0; } - __extends(d, b); - d.prototype.readyToRender = function(a) { - "undefined" === typeof a && (a = !0); - if (!this._hasFlags(512)) { - return!1; - } - a && (k.enterTimeline("readyToRender"), this.visit(function(a) { - return a._hasFlags(512) ? (a._toggleFlags(512, !1), 0) : 2; - }), k.leaveTimeline()); - return!0; - }; - d.prototype.gatherMarkedDirtyRegions = function(a) { - var b = this; - this.visit(function(a, d, e) { - a._removeFlags(1); - if (a instanceof k.FrameContainer) { - return 0; - } - e & 1 && (e = a.getBounds().clone(), d.transformRectangleAABB(e), b.dirtyRegion.addDirtyRectangle(e), a._previouslyRenderedAABB && b.dirtyRegion.addDirtyRectangle(a._previouslyRenderedAABB)); - return 0; - }, a, 0); - }; - d.prototype.gatherFrames = function() { - var a = []; - this.visit(function(b, d) { - b instanceof k.FrameContainer || a.push(b); - return 0; - }, this.matrix); - return a; + __extends(c, b); + c.prototype.getBounds = function(b) { + void 0 === b && (b = !1); + var c = this._bounds || (this._bounds = a.createEmpty()); + this.hasFlags(256) && (c.set(this._source.getBounds()), this.removeFlags(256)); + return b ? c.clone() : c; }; - d.prototype.gatherLayers = function() { - var a = [], b; - this.visit(function(d, f) { - if (d instanceof k.FrameContainer) { - return 0; - } - var e = d.getBounds().clone(); - f.transformRectangleAABB(e); - d._hasFlags(1) ? (b && a.push(b), a.push(e.clone()), b = null) : b ? b.union(e) : b = e.clone(); - return 0; - }, this.matrix); - b && a.push(b); - return a; + Object.defineProperty(c.prototype, "source", {get:function() { + return this._source; + }, enumerable:!0, configurable:!0}); + c.prototype._propagateFlagsDown = function(a) { + this.setFlags(a); }; - return d; - }(k.FrameContainer); - k.Stage = s; - s = function(f) { - function d(a, c) { - f.call(this); - this.color = b.Color.None; - this._bounds = new g(0, 0, a, c); - } - __extends(d, f); - d.prototype.setBounds = function(a) { - this._bounds.set(a); + c.prototype.getChildren = function(a) { + return[this._source]; }; - d.prototype.getBounds = function() { - return this._bounds; + return c; + }(k); + h.Shape = t; + h.RendererOptions = function() { + return function() { + this.debug = !1; + this.paintRenderable = !0; + this.paintViewport = this.paintFlashing = this.paintDirtyRegion = this.paintBounds = !1; + this.clear = !0; }; - return d; - }(k.FrameContainer); - k.ClipRectangle = s; - s = function(b) { - function d(a) { + }(); + (function(a) { + a[a.Canvas2D = 0] = "Canvas2D"; + a[a.WebGL = 1] = "WebGL"; + a[a.Both = 2] = "Both"; + a[a.DOM = 3] = "DOM"; + a[a.SVG = 4] = "SVG"; + })(h.Backend || (h.Backend = {})); + m = function(b) { + function d(c, e, f) { b.call(this); - t(a); - this._source = a; + this._container = c; + this._stage = e; + this._options = f; + this._viewport = a.createSquare(); + this._devicePixelRatio = 1; } __extends(d, b); - Object.defineProperty(d.prototype, "source", {get:function() { - return this._source; + Object.defineProperty(d.prototype, "viewport", {set:function(a) { + this._viewport.set(a); }, enumerable:!0, configurable:!0}); - d.prototype.getBounds = function() { - return this.source.getBounds(); + d.prototype.render = function() { + throw c.Debug.abstractMethod("Renderer::render"); + }; + d.prototype.resize = function() { + throw c.Debug.abstractMethod("Renderer::resize"); + }; + d.prototype.screenShot = function(a, b) { + throw c.Debug.abstractMethod("Renderer::screenShot"); }; return d; - }(k.Frame); - k.Shape = s; - })(b.GFX || (b.GFX = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - var g = k.Geometry.Rectangle, f = k.Geometry.Matrix, t = b.Debug.assertUnreachable, s = b.Debug.assert, m = b.Debug.unexpected, d = b.ArrayUtilities.indexOf; - (function(a) { - a[a.None = 0] = "None"; - a[a.Dynamic = 1] = "Dynamic"; - a[a.Dirty = 2] = "Dirty"; - a[a.Scalable = 4] = "Scalable"; - a[a.Tileable = 8] = "Tileable"; - a[a.Loading = 16] = "Loading"; - })(k.RenderableFlags || (k.RenderableFlags = {})); - var a = function() { - function a(b) { - this._flags = 0; - this.properties = {}; - this._frameReferrers = []; - this._renderableReferrers = []; - this._bounds = b.clone(); + }(m); + h.Renderer = m; + m = function(b) { + function c(d, e, k) { + void 0 === k && (k = !1); + b.call(this); + this._dirtyVisitor = new n; + this._flags &= -3; + this._type = 13; + this._scaleMode = c.DEFAULT_SCALE; + this._align = c.DEFAULT_ALIGN; + this._content = new f; + this._content._flags &= -3; + this.addChild(this._content); + this.setFlags(16); + this.setBounds(new a(0, 0, d, e)); + k ? (this._dirtyRegion = new v(d, e), this._dirtyRegion.addDirtyRectangle(new a(0, 0, d, e))) : this._dirtyRegion = null; + this._updateContentMatrix(); } - a.prototype.setFlags = function(a) { - this._flags |= a; + __extends(c, b); + Object.defineProperty(c.prototype, "dirtyRegion", {get:function() { + return this._dirtyRegion; + }, enumerable:!0, configurable:!0}); + c.prototype.setBounds = function(a) { + b.prototype.setBounds.call(this, a); + this._updateContentMatrix(); + this._dispatchEvent(); + this._dirtyRegion && (this._dirtyRegion = new v(a.w, a.h), this._dirtyRegion.addDirtyRectangle(a)); }; - a.prototype.hasFlags = function(a) { - return(this._flags & a) === a; + Object.defineProperty(c.prototype, "content", {get:function() { + return this._content; + }, enumerable:!0, configurable:!0}); + c.prototype.readyToRender = function() { + this._dirtyVisitor.isDirty = !1; + this._dirtyVisitor.start(this, this._dirtyRegion); + return this._dirtyVisitor.isDirty ? !0 : !1; }; - a.prototype.removeFlags = function(a) { - this._flags &= ~a; + Object.defineProperty(c.prototype, "align", {get:function() { + return this._align; + }, set:function(a) { + this._align = a; + this._updateContentMatrix(); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(c.prototype, "scaleMode", {get:function() { + return this._scaleMode; + }, set:function(a) { + this._scaleMode = a; + this._updateContentMatrix(); + }, enumerable:!0, configurable:!0}); + c.prototype._updateContentMatrix = function() { + if (this._scaleMode === c.DEFAULT_SCALE && this._align === c.DEFAULT_ALIGN) { + this._content.getTransform().setMatrix(new s(1, 0, 0, 1, 0, 0)); + } else { + var a = this.getBounds(), b = this._content.getBounds(), d = a.w / b.w, e = a.h / b.h; + switch(this._scaleMode) { + case 2: + d = e = Math.max(d, e); + break; + case 4: + d = e = 1; + break; + case 1: + break; + default: + d = e = Math.min(d, e); + } + var f; + f = this._align & 4 ? 0 : this._align & 8 ? a.w - b.w * d : (a.w - b.w * d) / 2; + a = this._align & 1 ? 0 : this._align & 2 ? a.h - b.h * e : (a.h - b.h * e) / 2; + this._content.getTransform().setMatrix(new s(d, 0, 0, e, f, a)); + } + }; + c.DEFAULT_SCALE = 4; + c.DEFAULT_ALIGN = 5; + return c; + }(f); + h.Stage = m; + })(c.GFX || (c.GFX = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + function a(a, b, c) { + return a + (b - a) * c; + } + function s(b, c, d) { + return a(b >> 24 & 255, c >> 24 & 255, d) << 24 | a(b >> 16 & 255, c >> 16 & 255, d) << 16 | a(b >> 8 & 255, c >> 8 & 255, d) << 8 | a(b & 255, c & 255, d); + } + var v = h.Geometry.Point, p = h.Geometry.Rectangle, u = h.Geometry.Matrix, l = c.Debug.assertUnreachable, e = c.Debug.assert, m = c.ArrayUtilities.indexOf, t = function(a) { + function b() { + a.call(this); + this._parents = []; + this._renderableParents = []; + this._invalidateEventListeners = null; + this._flags &= -3; + this._type = 33; + } + __extends(b, a); + b.prototype.addParent = function(a) { + e(a); + var b = m(this._parents, a); + e(0 > b); + this._parents.push(a); + }; + b.prototype.addRenderableParent = function(a) { + e(a); + var b = m(this._renderableParents, a); + e(0 > b); + this._renderableParents.push(a); + }; + b.prototype.wrap = function() { + for (var a, b = this._parents, c = 0;c < b.length;c++) { + if (a = b[c], !a._parent) { + return a; + } + } + a = new h.Shape(this); + this.addParent(a); + return a; }; - a.prototype.addFrameReferrer = function(a) { - d(this._frameReferrers, a); - this._frameReferrers.push(a); - }; - a.prototype.addRenderableReferrer = function(a) { - d(this._renderableReferrers, a); - this._renderableReferrers.push(a); - }; - a.prototype.invalidatePaint = function() { - this.setFlags(2); - for (var a = this._frameReferrers, b = 0;b < a.length;b++) { - a[b].invalidatePaint(); + b.prototype.invalidate = function() { + this.setFlags(16); + for (var a = this._parents, b = 0;b < a.length;b++) { + a[b].invalidate(); } - a = this._renderableReferrers; + a = this._renderableParents; for (b = 0;b < a.length;b++) { - a[b].invalidatePaint(); + a[b].invalidate(); + } + if (a = this._invalidateEventListeners) { + for (b = 0;b < a.length;b++) { + a[b](this); + } } }; - a.prototype.getBounds = function() { - return this._bounds; + b.prototype.addInvalidateEventListener = function(a) { + this._invalidateEventListeners || (this._invalidateEventListeners = []); + var b = m(this._invalidateEventListeners, a); + e(0 > b); + this._invalidateEventListeners.push(a); }; - a.prototype.render = function(a, b, c) { + b.prototype.getBounds = function(a) { + void 0 === a && (a = !1); + return a ? this._bounds.clone() : this._bounds; }; - return a; - }(); - k.Renderable = a; - var c = function(a) { + b.prototype.getChildren = function(a) { + return null; + }; + b.prototype._propagateFlagsUp = function(a) { + if (0 !== a && !this.hasFlags(a)) { + for (var b = 0;b < this._parents.length;b++) { + this._parents[b]._propagateFlagsUp(a); + } + } + }; + b.prototype.render = function(a, b, c, d, e) { + }; + return b; + }(h.Node); + h.Renderable = t; + var q = function(a) { function b(c, d) { - a.call(this, c); + a.call(this); + this.setBounds(c); this.render = d; } __extends(b, a); return b; - }(a); - k.CustomRenderable = c; - c = function(a) { + }(t); + h.CustomRenderable = q; + (function(a) { + a[a.Idle = 1] = "Idle"; + a[a.Playing = 2] = "Playing"; + a[a.Paused = 3] = "Paused"; + })(h.RenderableVideoState || (h.RenderableVideoState = {})); + q = function(a) { function b(c, d) { - a.call(this, d); - this._flags = 3; - this._lastCurrentTime = 0; - this._video = document.createElement("video"); - this._video.src = c; - this._video.loop = !0; - this._video.play(); + a.call(this); + this._flags = 1048592; + this._lastPausedTime = this._lastTimeInvalidated = 0; + this._seekHappens = !1; + this._isDOMElement = !0; + this.setBounds(new p(0, 0, 1, 1)); + this._assetId = c; + this._eventSerializer = d; + var e = document.createElement("video"), f = this._handleVideoEvent.bind(this); + e.preload = "metadata"; + e.addEventListener("play", f); + e.addEventListener("ended", f); + e.addEventListener("loadeddata", f); + e.addEventListener("progress", f); + e.addEventListener("waiting", f); + e.addEventListener("loadedmetadata", f); + e.addEventListener("error", f); + e.addEventListener("seeking", f); + this._video = e; + this._videoEventHandler = f; b._renderableVideos.push(this); + "undefined" !== typeof registerInspectorAsset && registerInspectorAsset(-1, -1, this); + this._state = 1; } __extends(b, a); - b.prototype.invalidatePaintCheck = function() { - this._lastCurrentTime !== this._video.currentTime && this.invalidatePaint(); - this._lastCurrentTime = this._video.currentTime; + Object.defineProperty(b.prototype, "video", {get:function() { + return this._video; + }, enumerable:!0, configurable:!0}); + Object.defineProperty(b.prototype, "state", {get:function() { + return this._state; + }, enumerable:!0, configurable:!0}); + b.prototype.play = function() { + this._video.play(); + this._state = 2; + }; + b.prototype.pause = function() { + this._video.pause(); + this._state = 3; + }; + b.prototype._handleVideoEvent = function(a) { + var b = null, c = this._video; + switch(a.type) { + case "play": + a = 1; + break; + case "ended": + a = 2; + break; + case "loadeddata": + a = 3; + break; + case "progress": + a = 4; + break; + case "waiting": + a = 5; + break; + case "loadedmetadata": + a = 7; + b = {videoWidth:c.videoWidth, videoHeight:c.videoHeight, duration:c.duration}; + break; + case "error": + a = 6; + b = {code:c.error.code}; + break; + case "seeking": + a = 8; + this._seekHappens = !0; + break; + default: + return; + } + this._notifyNetStream(a, b); + }; + b.prototype._notifyNetStream = function(a, b) { + this._eventSerializer.sendVideoPlaybackEvent(this._assetId, a, b); + }; + b.prototype.processControlRequest = function(a, b) { + var c = this._video; + switch(a) { + case 1: + c.src = b.url; + this._notifyNetStream(0, null); + break; + case 2: + c && (b.paused && !c.paused ? (this._lastPausedTime = isNaN(b.time) ? c.currentTime : c.currentTime = b.time, this.pause()) : !b.paused && c.paused && (this.play(), isNaN(b.time) || this._lastPausedTime === b.time || (c.currentTime = b.time), this._seekHappens && (this._seekHappens = !1, this._notifyNetStream(3, null)))); + break; + case 3: + c && (c.currentTime = b.time); + break; + case 4: + return c ? c.currentTime : 0; + case 5: + return c ? c.duration : 0; + case 6: + c && (c.volume = b.volume); + break; + case 7: + if (!c) { + return 0; + } + var d = -1; + if (c.buffered) { + for (var e = 0;e < c.buffered.length;e++) { + d = Math.max(d, c.buffered.end(e)); + } + } else { + d = c.duration; + } + return Math.round(500 * d); + case 8: + return c ? Math.round(500 * c.duration) : 0; + } }; - b.invalidateVideos = function() { + b.prototype.checkForUpdate = function() { + this._lastTimeInvalidated !== this._video.currentTime && (this._isDOMElement || this.invalidate()); + this._lastTimeInvalidated = this._video.currentTime; + }; + b.checkForVideoUpdates = function() { for (var a = b._renderableVideos, c = 0;c < a.length;c++) { - a[c].invalidatePaintCheck(); + a[c].checkForUpdate(); } }; - b.prototype.render = function(a, b) { - k.enterTimeline("RenderableVideo.render"); - this._video && a.drawImage(this._video, 0, 0); - k.leaveTimeline("RenderableVideo.render"); + b.prototype.render = function(a, b, c) { + h.enterTimeline("RenderableVideo.render"); + (b = this._video) && 0 < b.videoWidth && a.drawImage(b, 0, 0, b.videoWidth, b.videoHeight, 0, 0, this._bounds.w, this._bounds.h); + h.leaveTimeline("RenderableVideo.render"); }; b._renderableVideos = []; return b; - }(a); - k.RenderableVideo = c; - c = function(a) { - function c(b, d) { - a.call(this, d); - this._flags = 3; + }(t); + h.RenderableVideo = q; + q = function(a) { + function b(c, d) { + a.call(this); + this._flags = 1048592; this.properties = {}; - this._canvas = b; - this._context = this._canvas.getContext("2d"); - this._imageData = this._context.createImageData(this._bounds.w, this._bounds.h); + this.setBounds(d); + c instanceof HTMLCanvasElement ? this._initializeSourceCanvas(c) : this._sourceImage = c; } - __extends(c, a); - c.FromDataBuffer = function(a, b, d) { - k.enterTimeline("RenderableBitmap.FromDataBuffer"); + __extends(b, a); + b.FromDataBuffer = function(a, c, d) { + h.enterTimeline("RenderableBitmap.FromDataBuffer"); var e = document.createElement("canvas"); e.width = d.w; e.height = d.h; - d = new c(e, d); - d.updateFromDataBuffer(a, b); - k.leaveTimeline("RenderableBitmap.FromDataBuffer"); + d = new b(e, d); + d.updateFromDataBuffer(a, c); + h.leaveTimeline("RenderableBitmap.FromDataBuffer"); return d; }; - c.FromFrame = function(a, b, d, e, f) { - k.enterTimeline("RenderableBitmap.FromFrame"); - var g = document.createElement("canvas"), l = a.getBounds(); - g.width = l.w; - g.height = l.h; - g = new c(g, l); - g.drawFrame(a, b, d, e, f); - k.leaveTimeline("RenderableBitmap.FromFrame"); + b.FromNode = function(a, c, d, e, f) { + h.enterTimeline("RenderableBitmap.FromFrame"); + var g = document.createElement("canvas"), k = a.getBounds(); + g.width = k.w; + g.height = k.h; + g = new b(g, k); + g.drawNode(a, c, d, e, f); + h.leaveTimeline("RenderableBitmap.FromFrame"); return g; }; - c.prototype.updateFromDataBuffer = function(a, c) { - if (k.imageUpdateOption.value) { - k.enterTimeline("RenderableBitmap.updateFromDataBuffer", {length:c.length}); + b.FromImage = function(a, c, d) { + return new b(a, new p(0, 0, c, d)); + }; + b.prototype.updateFromDataBuffer = function(a, b) { + if (h.imageUpdateOption.value) { + var d = b.buffer; + h.enterTimeline("RenderableBitmap.updateFromDataBuffer", {length:b.length}); if (4 === a || 5 === a || 6 === a) { - var d = this; - d.setFlags(16); - var e = new Image; - e.src = URL.createObjectURL(c.toBlob()); - e.onload = function() { - d._context.drawImage(e, 0, 0); - d.removeFlags(16); - d.invalidatePaint(); - }; - e.onerror = function() { - m("Image loading error: " + b.ImageType[a]); - }; + c.Debug.assertUnreachable("Mustn't encounter un-decoded images here"); } else { - k.imageConvertOption.value && (k.enterTimeline("ColorUtilities.convertImage"), b.ColorUtilities.convertImage(a, 3, new Int32Array(c.buffer), new Int32Array(this._imageData.data.buffer)), k.leaveTimeline("ColorUtilities.convertImage")), k.enterTimeline("putImageData"), this._context.putImageData(this._imageData, 0, 0), k.leaveTimeline("putImageData"); - } - this.invalidatePaint(); - k.leaveTimeline("RenderableBitmap.updateFromDataBuffer"); - } + var e = this._bounds, f = this._imageData; + f && f.width === e.w && f.height === e.h || (f = this._imageData = this._context.createImageData(e.w, e.h)); + h.imageConvertOption.value && (h.enterTimeline("ColorUtilities.convertImage"), d = new Int32Array(d), e = new Int32Array(f.data.buffer), c.ColorUtilities.convertImage(a, 3, d, e), h.leaveTimeline("ColorUtilities.convertImage")); + h.enterTimeline("putImageData"); + this._ensureSourceCanvas(); + this._context.putImageData(f, 0, 0); + h.leaveTimeline("putImageData"); + } + this.invalidate(); + h.leaveTimeline("RenderableBitmap.updateFromDataBuffer"); + } + }; + b.prototype.readImageData = function(a) { + a.writeRawBytes(this.imageData.data); + }; + b.prototype.render = function(a, b, c) { + h.enterTimeline("RenderableBitmap.render"); + this.renderSource ? a.drawImage(this.renderSource, 0, 0) : this._renderFallback(a); + h.leaveTimeline("RenderableBitmap.render"); + }; + b.prototype.drawNode = function(a, b, c, d, e) { + h.enterTimeline("RenderableBitmap.drawFrame"); + c = h.Canvas2D; + d = this.getBounds(); + (new c.Canvas2DRenderer(this._canvas, null)).renderNode(a, e || d, b); + h.leaveTimeline("RenderableBitmap.drawFrame"); }; - c.prototype.readImageData = function(a) { - var b = this._context.getImageData(0, 0, this._canvas.width, this._canvas.height).data; - a.writeRawBytes(b); + b.prototype._initializeSourceCanvas = function(a) { + this._canvas = a; + this._context = this._canvas.getContext("2d"); }; - c.prototype.render = function(a, b) { - k.enterTimeline("RenderableBitmap.render"); - this._canvas ? a.drawImage(this._canvas, 0, 0) : this._renderFallback(a); - k.leaveTimeline("RenderableBitmap.render"); + b.prototype._ensureSourceCanvas = function() { + if (!this._canvas) { + var a = document.createElement("canvas"), b = this._bounds; + a.width = b.w; + a.height = b.h; + this._initializeSourceCanvas(a); + } }; - c.prototype.drawFrame = function(a, b, c, d, e) { - k.enterTimeline("RenderableBitmap.drawFrame"); - c = k.Canvas2D; - d = this.getBounds(); - var f = new c.Canvas2DStageRendererOptions; - f.cacheShapes = !0; - (new c.Canvas2DStageRenderer(this._canvas, null, f)).renderFrame(a, e || d, b); - k.leaveTimeline("RenderableBitmap.drawFrame"); - }; - c.prototype._renderFallback = function(a) { - this.fillStyle || (this.fillStyle = b.ColorStyle.randomStyle()); - var c = this._bounds; + Object.defineProperty(b.prototype, "imageData", {get:function() { + this._canvas || (e(this._sourceImage), this._ensureSourceCanvas(), this._context.drawImage(this._sourceImage, 0, 0), this._sourceImage = null); + return this._context.getImageData(0, 0, this._bounds.w, this._bounds.h); + }, enumerable:!0, configurable:!0}); + Object.defineProperty(b.prototype, "renderSource", {get:function() { + return this._canvas || this._sourceImage; + }, enumerable:!0, configurable:!0}); + b.prototype._renderFallback = function(a) { + this.fillStyle || (this.fillStyle = c.ColorStyle.randomStyle()); + var b = this._bounds; a.save(); a.beginPath(); a.lineWidth = 2; a.fillStyle = this.fillStyle; - a.fillRect(c.x, c.y, c.w, c.h); + a.fillRect(b.x, b.y, b.w, b.h); a.restore(); }; - return c; - }(a); - k.RenderableBitmap = c; - var n; + return b; + }(t); + h.RenderableBitmap = q; (function(a) { a[a.Fill = 0] = "Fill"; a[a.Stroke = 1] = "Stroke"; a[a.StrokeFill = 2] = "StrokeFill"; - })(n || (n = {})); - var p = function() { + })(h.PathType || (h.PathType = {})); + var n = function() { return function(a, b, c, d) { this.type = a; this.style = b; this.smoothImage = c; this.strokeProperties = d; this.path = new Path2D; - s(1 === a === !!d); + e(1 === a === !!d); }; - }(), e = function() { + }(); + h.StyledPath = n; + var k = function() { return function(a, b, c, d, e) { this.thickness = a; this.scaleMode = b; @@ -41474,248 +51215,385 @@ this.miterLimit = e; }; }(); - n = function(a) { - function c(b, d, e, f) { - a.call(this, f); - this._flags = 14; + h.StrokeProperties = k; + var f = function(a) { + function b(c, d, e, f) { + a.call(this); + this._flags = 6291472; this.properties = {}; - this._id = b; + this.setBounds(f); + this._id = c; this._pathData = d; this._textures = e; - e.length && this.setFlags(1); + e.length && this.setFlags(1048576); } - __extends(c, a); - c.prototype.update = function(a, b, c) { - this._bounds = c; + __extends(b, a); + b.prototype.update = function(a, b, c) { + this.setBounds(c); this._pathData = a; this._paths = null; this._textures = b; + this.setFlags(1048576); + this.invalidate(); }; - c.prototype.getBounds = function() { - return this._bounds; - }; - c.prototype.render = function(a, b, c) { - "undefined" === typeof c && (c = !1); + b.prototype.render = function(a, b, c, d, f) { + void 0 === d && (d = !1); + void 0 === f && (f = !1); a.fillStyle = a.strokeStyle = "transparent"; - var d = this._textures; - for (b = 0;b < d.length;b++) { - if (d[b].hasFlags(16)) { - return; + b = this._deserializePaths(this._pathData, a, b); + e(b); + h.enterTimeline("RenderableShape.render", this); + for (c = 0;c < b.length;c++) { + var g = b[c]; + a.mozImageSmoothingEnabled = a.msImageSmoothingEnabled = a.imageSmoothingEnabled = g.smoothImage; + if (0 === g.type) { + a.fillStyle = f ? "#FF4981" : g.style, d ? a.clip(g.path, "evenodd") : a.fill(g.path, "evenodd"), a.fillStyle = "transparent"; + } else { + if (!d && !f) { + a.strokeStyle = g.style; + var k = 1; + g.strokeProperties && (k = g.strokeProperties.scaleMode, a.lineWidth = g.strokeProperties.thickness, a.lineCap = g.strokeProperties.capsStyle, a.lineJoin = g.strokeProperties.jointsStyle, a.miterLimit = g.strokeProperties.miterLimit); + var l = a.lineWidth; + (l = 1 === l || 3 === l) && a.translate(.5, .5); + a.flashStroke(g.path, k); + l && a.translate(-.5, -.5); + a.strokeStyle = "transparent"; + } + } + } + h.leaveTimeline("RenderableShape.render"); + }; + b.prototype._deserializePaths = function(a, d, f) { + e(a ? !this._paths : this._paths); + h.enterTimeline("RenderableShape.deserializePaths"); + if (this._paths) { + return this._paths; + } + f = this._paths = []; + for (var g = null, m = null, n = 0, p = 0, q, s, t = !1, u = 0, v = 0, C = a.commands, E = a.coordinates, F = a.styles, I = F.position = 0, G = a.commandsPosition, Z = 0;Z < G;Z++) { + switch(q = C[Z], q) { + case 9: + e(I <= a.coordinatesPosition - 2); + t && g && (g.lineTo(u, v), m && m.lineTo(u, v)); + t = !0; + n = u = E[I++] / 20; + p = v = E[I++] / 20; + g && g.moveTo(n, p); + m && m.moveTo(n, p); + break; + case 10: + e(I <= a.coordinatesPosition - 2); + n = E[I++] / 20; + p = E[I++] / 20; + g && g.lineTo(n, p); + m && m.lineTo(n, p); + break; + case 11: + e(I <= a.coordinatesPosition - 4); + q = E[I++] / 20; + s = E[I++] / 20; + n = E[I++] / 20; + p = E[I++] / 20; + g && g.quadraticCurveTo(q, s, n, p); + m && m.quadraticCurveTo(q, s, n, p); + break; + case 12: + e(I <= a.coordinatesPosition - 6); + q = E[I++] / 20; + s = E[I++] / 20; + var Q = E[I++] / 20, S = E[I++] / 20, n = E[I++] / 20, p = E[I++] / 20; + g && g.bezierCurveTo(q, s, Q, S, n, p); + m && m.bezierCurveTo(q, s, Q, S, n, p); + break; + case 1: + e(4 <= F.bytesAvailable); + g = this._createPath(0, c.ColorUtilities.rgbaToCSSStyle(F.readUnsignedInt()), !1, null, n, p); + break; + case 3: + q = this._readBitmap(F, d); + g = this._createPath(0, q.style, q.smoothImage, null, n, p); + break; + case 2: + g = this._createPath(0, this._readGradient(F, d), !1, null, n, p); + break; + case 4: + g = null; + break; + case 5: + m = c.ColorUtilities.rgbaToCSSStyle(F.readUnsignedInt()); + F.position += 1; + q = F.readByte(); + s = b.LINE_CAPS_STYLES[F.readByte()]; + Q = b.LINE_JOINTS_STYLES[F.readByte()]; + q = new k(E[I++] / 20, q, s, Q, F.readByte()); + m = this._createPath(1, m, !1, q, n, p); + break; + case 6: + m = this._createPath(2, this._readGradient(F, d), !1, null, n, p); + break; + case 7: + q = this._readBitmap(F, d); + m = this._createPath(2, q.style, q.smoothImage, null, n, p); + break; + case 8: + m = null; + break; + default: + l("Invalid command " + q + " encountered at index" + Z + " of " + G); } } - (b = this._pathData) && this._deserializePaths(b, a); - d = this._paths; - s(d); - k.enterTimeline("RenderableShape.render", this); - for (b = 0;b < d.length;b++) { - var e = d[b]; - a.mozImageSmoothingEnabled = a.msImageSmoothingEnabled = a.imageSmoothingEnabled = e.smoothImage; - if (0 === e.type) { - a.fillStyle = e.style, c ? a.clip(e.path, "evenodd") : a.fill(e.path, "evenodd"), a.fillStyle = "transparent"; - } else { - if (!c) { - a.strokeStyle = e.style; - var f = 1; - e.strokeProperties && (f = e.strokeProperties.scaleMode, a.lineWidth = e.strokeProperties.thickness, a.lineCap = e.strokeProperties.capsStyle, a.lineJoin = e.strokeProperties.jointsStyle, a.miterLimit = e.strokeProperties.miterLimit); - var g = a.lineWidth; - (g = 1 === g || 3 === g) && a.translate(.5, .5); - a.flashStroke(e.path, f); - g && a.translate(-.5, -.5); - a.strokeStyle = "transparent"; - } - } + e(0 === F.bytesAvailable); + e(Z === G); + e(I === a.coordinatesPosition); + t && g && (g.lineTo(u, v), m && m.lineTo(u, v)); + this._pathData = null; + h.leaveTimeline("RenderableShape.deserializePaths"); + return f; + }; + b.prototype._createPath = function(a, b, c, d, e, f) { + a = new n(a, b, c, d); + this._paths.push(a); + a.path.moveTo(e, f); + return a.path; + }; + b.prototype._readMatrix = function(a) { + return new u(a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat()); + }; + b.prototype._readGradient = function(a, b) { + e(34 <= a.bytesAvailable); + var d = a.readUnsignedByte(), f = 2 * a.readShort() / 255; + e(-1 <= f && 1 >= f); + var g = this._readMatrix(a), d = 16 === d ? b.createLinearGradient(-1, 0, 1, 0) : b.createRadialGradient(f, 0, 0, 0, 0, 1); + d.setTransform && d.setTransform(g.toSVGMatrix()); + g = a.readUnsignedByte(); + for (f = 0;f < g;f++) { + var k = a.readUnsignedByte() / 255, h = c.ColorUtilities.rgbaToCSSStyle(a.readUnsignedInt()); + d.addColorStop(k, h); } - k.leaveTimeline("RenderableShape.render"); + a.position += 2; + return d; + }; + b.prototype._readBitmap = function(a, b) { + e(30 <= a.bytesAvailable); + var c = a.readUnsignedInt(), d = this._readMatrix(a), f = a.readBoolean() ? "repeat" : "no-repeat", g = a.readBoolean(); + (c = this._textures[c]) ? (f = b.createPattern(c.renderSource, f), f.setTransform(d.toSVGMatrix())) : f = null; + return{style:f, smoothImage:g}; + }; + b.prototype._renderFallback = function(a) { + this.fillStyle || (this.fillStyle = c.ColorStyle.randomStyle()); + var b = this._bounds; + a.save(); + a.beginPath(); + a.lineWidth = 2; + a.fillStyle = this.fillStyle; + a.fillRect(b.x, b.y, b.w, b.h); + a.restore(); }; - c.prototype._deserializePaths = function(a, d) { - s(!this._paths); - k.enterTimeline("RenderableShape.deserializePaths"); - this._paths = []; - for (var f = null, g = null, l = 0, m = 0, n, p, q = !1, u = 0, F = 0, J = a.commands, A = a.coordinates, B = a.styles, z = B.position = 0, P = a.commandsPosition, D = 0;D < P;D++) { - switch(n = J[D], n) { + b.LINE_CAPS_STYLES = ["round", "butt", "square"]; + b.LINE_JOINTS_STYLES = ["round", "bevel", "miter"]; + return b; + }(t); + h.RenderableShape = f; + q = function(b) { + function d() { + b.apply(this, arguments); + this._flags = 7340048; + this._morphPaths = Object.create(null); + } + __extends(d, b); + d.prototype._deserializePaths = function(b, d, g) { + h.enterTimeline("RenderableMorphShape.deserializePaths"); + if (this._morphPaths[g]) { + return this._morphPaths[g]; + } + var m = this._morphPaths[g] = [], n = null, r = null, p = 0, q = 0, t, u, v = !1, J = 0, C = 0, E = b.commands, F = b.coordinates, I = b.morphCoordinates, G = b.styles, Z = b.morphStyles; + G.position = 0; + for (var Q = Z.position = 0, S = b.commandsPosition, O = 0;O < S;O++) { + switch(t = E[O], t) { case 9: - s(z <= a.coordinatesPosition - 2); - q && f && (f.lineTo(u, F), g && g.lineTo(u, F)); - q = !0; - l = u = A[z++] / 20; - m = F = A[z++] / 20; - f && f.moveTo(l, m); - g && g.moveTo(l, m); + e(Q <= b.coordinatesPosition - 2); + v && n && (n.lineTo(J, C), r && r.lineTo(J, C)); + v = !0; + p = J = a(F[Q], I[Q++], g) / 20; + q = C = a(F[Q], I[Q++], g) / 20; + n && n.moveTo(p, q); + r && r.moveTo(p, q); break; case 10: - s(z <= a.coordinatesPosition - 2); - l = A[z++] / 20; - m = A[z++] / 20; - f && f.lineTo(l, m); - g && g.lineTo(l, m); + e(Q <= b.coordinatesPosition - 2); + p = a(F[Q], I[Q++], g) / 20; + q = a(F[Q], I[Q++], g) / 20; + n && n.lineTo(p, q); + r && r.lineTo(p, q); break; case 11: - s(z <= a.coordinatesPosition - 4); - n = A[z++] / 20; - p = A[z++] / 20; - l = A[z++] / 20; - m = A[z++] / 20; - f && f.quadraticCurveTo(n, p, l, m); - g && g.quadraticCurveTo(n, p, l, m); + e(Q <= b.coordinatesPosition - 4); + t = a(F[Q], I[Q++], g) / 20; + u = a(F[Q], I[Q++], g) / 20; + p = a(F[Q], I[Q++], g) / 20; + q = a(F[Q], I[Q++], g) / 20; + n && n.quadraticCurveTo(t, u, p, q); + r && r.quadraticCurveTo(t, u, p, q); break; case 12: - s(z <= a.coordinatesPosition - 6); - n = A[z++] / 20; - p = A[z++] / 20; - var L = A[z++] / 20, M = A[z++] / 20, l = A[z++] / 20, m = A[z++] / 20; - f && f.bezierCurveTo(n, p, L, M, l, m); - g && g.bezierCurveTo(n, p, L, M, l, m); + e(Q <= b.coordinatesPosition - 6); + t = a(F[Q], I[Q++], g) / 20; + u = a(F[Q], I[Q++], g) / 20; + var P = a(F[Q], I[Q++], g) / 20, V = a(F[Q], I[Q++], g) / 20, p = a(F[Q], I[Q++], g) / 20, q = a(F[Q], I[Q++], g) / 20; + n && n.bezierCurveTo(t, u, P, V, p, q); + r && r.bezierCurveTo(t, u, P, V, p, q); break; case 1: - s(4 <= B.bytesAvailable); - f = this._createPath(0, b.ColorUtilities.rgbaToCSSStyle(B.readUnsignedInt()), !1, null, l, m); + e(4 <= G.bytesAvailable); + n = this._createMorphPath(0, g, c.ColorUtilities.rgbaToCSSStyle(s(G.readUnsignedInt(), Z.readUnsignedInt(), g)), !1, null, p, q); break; case 3: - n = this._readBitmap(B, d); - f = this._createPath(0, n.style, n.smoothImage, null, l, m); + t = this._readMorphBitmap(G, Z, g, d); + n = this._createMorphPath(0, g, t.style, t.smoothImage, null, p, q); break; case 2: - f = this._createPath(0, this._readGradient(B, d), !1, null, l, m); + t = this._readMorphGradient(G, Z, g, d); + n = this._createMorphPath(0, g, t, !1, null, p, q); break; case 4: - f = null; + n = null; break; case 5: - g = b.ColorUtilities.rgbaToCSSStyle(B.readUnsignedInt()); - B.position += 1; - n = B.readByte(); - p = c.LINE_CAPS_STYLES[B.readByte()]; - L = c.LINE_JOINTS_STYLES[B.readByte()]; - n = new e(A[z++] / 20, n, p, L, B.readByte()); - g = this._createPath(1, g, !1, n, l, m); + t = a(F[Q], I[Q++], g) / 20; + r = c.ColorUtilities.rgbaToCSSStyle(s(G.readUnsignedInt(), Z.readUnsignedInt(), g)); + G.position += 1; + u = G.readByte(); + P = f.LINE_CAPS_STYLES[G.readByte()]; + V = f.LINE_JOINTS_STYLES[G.readByte()]; + t = new k(t, u, P, V, G.readByte()); + r = this._createMorphPath(1, g, r, !1, t, p, q); break; case 6: - g = this._createPath(2, this._readGradient(B, d), !1, null, l, m); + t = this._readMorphGradient(G, Z, g, d); + r = this._createMorphPath(2, g, t, !1, null, p, q); break; case 7: - n = this._readBitmap(B, d); - g = this._createPath(2, n.style, n.smoothImage, null, l, m); + t = this._readMorphBitmap(G, Z, g, d); + r = this._createMorphPath(2, g, t.style, t.smoothImage, null, p, q); break; case 8: - g = null; + r = null; break; default: - t("Invalid command " + n + " encountered at index" + D + " of " + P); + l("Invalid command " + t + " encountered at index" + O + " of " + S); } } - s(0 === B.bytesAvailable); - s(D === P); - s(z === a.coordinatesPosition); - q && f && (f.lineTo(u, F), g && g.lineTo(u, F)); - this._pathData = null; - k.leaveTimeline("RenderableShape.deserializePaths"); + e(0 === G.bytesAvailable); + e(O === S); + e(Q === b.coordinatesPosition); + v && n && (n.lineTo(J, C), r && r.lineTo(J, C)); + h.leaveTimeline("RenderableMorphShape.deserializPaths"); + return m; }; - c.prototype._createPath = function(a, b, c, d, e, f) { - a = new p(a, b, c, d); - this._paths.push(a); - a.path.moveTo(e, f); + d.prototype._createMorphPath = function(a, b, c, d, e, f, g) { + a = new n(a, c, d, e); + this._morphPaths[b].push(a); + a.path.moveTo(f, g); return a.path; }; - c.prototype._readMatrix = function(a) { - return new f(a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat(), a.readFloat()); + d.prototype._readMorphMatrix = function(b, c, d) { + return new u(a(b.readFloat(), c.readFloat(), d), a(b.readFloat(), c.readFloat(), d), a(b.readFloat(), c.readFloat(), d), a(b.readFloat(), c.readFloat(), d), a(b.readFloat(), c.readFloat(), d), a(b.readFloat(), c.readFloat(), d)); }; - c.prototype._readGradient = function(a, c) { - s(34 <= a.bytesAvailable); - var d = a.readUnsignedByte(), e = 2 * a.readShort() / 255; - s(-1 <= e && 1 >= e); - var f = this._readMatrix(a), d = 16 === d ? c.createLinearGradient(-1, 0, 1, 0) : c.createRadialGradient(e, 0, 0, 0, 0, 1); - d.setTransform && d.setTransform(f.toSVGMatrix()); - f = a.readUnsignedByte(); - for (e = 0;e < f;e++) { - var g = a.readUnsignedByte() / 255, k = b.ColorUtilities.rgbaToCSSStyle(a.readUnsignedInt()); - d.addColorStop(g, k); + d.prototype._readMorphGradient = function(b, d, f, g) { + e(34 <= b.bytesAvailable); + var k = b.readUnsignedByte(), h = 2 * b.readShort() / 255; + e(-1 <= h && 1 >= h); + var l = this._readMorphMatrix(b, d, f); + g = 16 === k ? g.createLinearGradient(-1, 0, 1, 0) : g.createRadialGradient(h, 0, 0, 0, 0, 1); + g.setTransform && g.setTransform(l.toSVGMatrix()); + l = b.readUnsignedByte(); + for (k = 0;k < l;k++) { + var h = a(b.readUnsignedByte() / 255, d.readUnsignedByte() / 255, f), m = s(b.readUnsignedInt(), d.readUnsignedInt(), f), m = c.ColorUtilities.rgbaToCSSStyle(m); + g.addColorStop(h, m); } - a.position += 2; - return d; + b.position += 2; + return g; }; - c.prototype._readBitmap = function(a, b) { - s(30 <= a.bytesAvailable); - var c = a.readUnsignedInt(), d = this._readMatrix(a), e = a.readBoolean() ? "repeat" : "no-repeat", f = a.readBoolean(), c = this._textures[c]; - s(c._canvas); - e = b.createPattern(c._canvas, e); - e.setTransform(d.toSVGMatrix()); - return{style:e, smoothImage:f}; - }; - c.prototype._renderFallback = function(a) { - this.fillStyle || (this.fillStyle = b.ColorStyle.randomStyle()); - var c = this._bounds; - a.save(); - a.beginPath(); - a.lineWidth = 2; - a.fillStyle = this.fillStyle; - a.fillRect(c.x, c.y, c.w, c.h); - a.restore(); + d.prototype._readMorphBitmap = function(a, b, c, d) { + e(30 <= a.bytesAvailable); + var f = a.readUnsignedInt(); + b = this._readMorphMatrix(a, b, c); + c = a.readBoolean() ? "repeat" : "no-repeat"; + a = a.readBoolean(); + f = this._textures[f]; + e(f._canvas); + d = d.createPattern(f._canvas, c); + d.setTransform(b.toSVGMatrix()); + return{style:d, smoothImage:a}; }; - c.LINE_CAPS_STYLES = ["round", "butt", "square"]; - c.LINE_JOINTS_STYLES = ["round", "bevel", "miter"]; - return c; - }(a); - k.RenderableShape = n; - var q = function() { + return d; + }(f); + h.RenderableMorphShape = q; + var d = function() { function a() { this.align = this.leading = this.descent = this.ascent = this.width = this.y = this.x = 0; this.runs = []; } - a.prototype.addRun = function(b, c, d, e) { - if (d) { - a._measureContext.font = b; - var f = a._measureContext.measureText(d).width | 0; - this.runs.push(new l(b, c, d, f, e)); - this.width += f; - } - }; - a.prototype.wrap = function(b) { - var c = [this], d = this.runs, e = this; - e.width = 0; - e.runs = []; - for (var f = a._measureContext, g = 0;g < d.length;g++) { - var k = d[g], m = k.text; - k.text = ""; - k.width = 0; - f.font = k.font; - for (var n = b, p = m.split(/[\s.-]/), q = 0, s = 0;s < p.length;s++) { - var t = p[s], A = m.substr(q, t.length + 1), B = f.measureText(A).width | 0; - if (B > n) { + a.prototype.addRun = function(c, d, e, f) { + if (e) { + a._measureContext.font = c; + var k = a._measureContext.measureText(e).width | 0; + this.runs.push(new b(c, d, e, k, f)); + this.width += k; + } + }; + a.prototype.wrap = function(c) { + var d = [this], e = this.runs, f = this; + f.width = 0; + f.runs = []; + for (var k = a._measureContext, h = 0;h < e.length;h++) { + var l = e[h], m = l.text; + l.text = ""; + l.width = 0; + k.font = l.font; + for (var n = c, p = m.split(/[\s.-]/), q = 0, s = 0;s < p.length;s++) { + var t = p[s], u = m.substr(q, t.length + 1), v = k.measureText(u).width | 0; + if (v > n) { do { - if (e.runs.push(k), e.width += k.width, k = new l(k.font, k.fillStyle, "", 0, k.underline), n = new a, n.y = e.y + e.descent + e.leading + e.ascent | 0, n.ascent = e.ascent, n.descent = e.descent, n.leading = e.leading, n.align = e.align, c.push(n), e = n, n = b - B, 0 > n) { - var B = A.length, z, P; + if (l.text && (f.runs.push(l), f.width += l.width, l = new b(l.font, l.fillStyle, "", 0, l.underline), n = new a, n.y = f.y + f.descent + f.leading + f.ascent | 0, n.ascent = f.ascent, n.descent = f.descent, n.leading = f.leading, n.align = f.align, d.push(n), f = n), n = c - v, 0 > n) { + var v = u.length, F, I; do { - B--, z = A.substr(0, B), P = f.measureText(z).width | 0; - } while (P > b); - k.text = z; - k.width = P; - A = A.substr(B); - B = f.measureText(A).width | 0; + v--; + if (1 > v) { + throw Error("Shall never happen: bad maxWidth?"); + } + F = u.substr(0, v); + I = k.measureText(F).width | 0; + } while (I > c); + l.text = F; + l.width = I; + u = u.substr(v); + v = k.measureText(u).width | 0; } } while (0 > n); } else { - n -= B; + n -= v; } - k.text += A; - k.width += B; + l.text += u; + l.width += v; q += t.length + 1; } - e.runs.push(k); - e.width += k.width; + f.runs.push(l); + f.width += l.width; } - return c; + return d; }; a._measureContext = document.createElement("canvas").getContext("2d"); return a; }(); - k.TextLine = q; - var l = function() { + h.TextLine = d; + var b = function() { return function(a, b, c, d, e) { - "undefined" === typeof a && (a = ""); - "undefined" === typeof b && (b = ""); - "undefined" === typeof c && (c = ""); - "undefined" === typeof d && (d = 0); - "undefined" === typeof e && (e = !1); + void 0 === a && (a = ""); + void 0 === b && (b = ""); + void 0 === c && (c = ""); + void 0 === d && (d = 0); + void 0 === e && (e = !1); this.font = a; this.fillStyle = b; this.text = c; @@ -41723,149 +51601,177 @@ this.underline = e; }; }(); - k.TextRun = l; - n = function(a) { - function c(b) { - a.call(this, b); - this._flags = 3; + h.TextRun = b; + q = function(a) { + function b(c) { + a.call(this); + this._flags = 1048592; this.properties = {}; - this._textBounds = b.clone(); + this._textBounds = c.clone(); this._textRunData = null; this._plainText = ""; this._borderColor = this._backgroundColor = 0; - this._matrix = f.createIdentity(); + this._matrix = u.createIdentity(); this._coords = null; - this.textRect = b.clone(); + this._scrollV = 1; + this._scrollH = 0; + this.textRect = c.clone(); this.lines = []; + this.setBounds(c); } - __extends(c, a); - c.prototype.setBounds = function(a) { - this._bounds.set(a); - this._textBounds.set(a); - this.textRect.setElements(a.x + 2, a.y + 2, a.x - 2, a.x - 2); + __extends(b, a); + b.prototype.setBounds = function(b) { + a.prototype.setBounds.call(this, b); + this._textBounds.set(b); + this.textRect.setElements(b.x + 2, b.y + 2, b.w - 2, b.h - 2); }; - c.prototype.setContent = function(a, b, c, d) { + b.prototype.setContent = function(a, b, c, d) { this._textRunData = b; this._plainText = a; this._matrix.set(c); this._coords = d; this.lines = []; }; - c.prototype.setStyle = function(a, b) { + b.prototype.setStyle = function(a, b, c, d) { this._backgroundColor = a; this._borderColor = b; + this._scrollV = c; + this._scrollH = d; }; - c.prototype.reflow = function(a, c) { - var d = this._textRunData; - if (d) { - var e = this._bounds, f = e.w - 4, g = this._plainText, l = this.lines, m = new q, n = 0, p = 0, s = 0, u = 0, w = 0, t = -1; - for (k.enterTimeline("RenderableText.reflow");d.position < d.length;) { - var z = d.readInt(), P = d.readInt(), D = d.readInt(), L = d.readInt(), L = L ? "swffont" + L : d.readUTF(), M = d.readInt(), V = d.readInt(), Q = d.readInt(); - M > s && (s = M); - V > u && (u = V); - Q > w && (w = Q); - M = d.readBoolean(); - V = ""; - d.readBoolean() && (V += "italic"); - M && (V += " bold"); - D = V + " " + D + "px " + L; - L = d.readInt(); - L = b.ColorUtilities.rgbToHex(L); - M = d.readInt(); - -1 === t && (t = M); - d.readBoolean(); - d.readInt(); - d.readInt(); - d.readInt(); - d.readInt(); - d.readInt(); - for (var M = d.readBoolean(), U = "", V = !1;!V;z++) { - V = z >= P - 1; - Q = g[z]; - if ("\r" !== Q && "\n" !== Q && (U += Q, z < g.length - 1)) { + b.prototype.reflow = function(a, b) { + var e = this._textRunData; + if (e) { + var f = this._bounds, g = f.w - 4, k = this._plainText, l = this.lines, m = new d, n = 0, r = 0, p = 0, q = 0, s = 0, t = -1; + for (h.enterTimeline("RenderableText.reflow");e.position < e.length;) { + var u = e.readInt(), v = e.readInt(), G = e.readInt(), Z = e.readUTF(), Q = e.readInt(), S = e.readInt(), O = e.readInt(); + Q > p && (p = Q); + S > q && (q = S); + O > s && (s = O); + Q = e.readBoolean(); + S = ""; + e.readBoolean() && (S += "italic "); + Q && (S += "bold "); + G = S + G + "px " + Z; + Z = e.readInt(); + Z = c.ColorUtilities.rgbToHex(Z); + Q = e.readInt(); + -1 === t && (t = Q); + e.readBoolean(); + e.readInt(); + e.readInt(); + e.readInt(); + e.readInt(); + e.readInt(); + for (var Q = e.readBoolean(), P = "", S = !1;!S;u++) { + S = u >= v - 1; + O = k[u]; + if ("\r" !== O && "\n" !== O && (P += O, u < k.length - 1)) { continue; } - m.addRun(D, L, U, M); + m.addRun(G, Z, P, Q); if (m.runs.length) { - n += s; + l.length && (n += s); + n += p; m.y = n | 0; - n += u + w; - m.ascent = s; - m.descent = u; - m.leading = w; + n += q; + m.ascent = p; + m.descent = q; + m.leading = s; m.align = t; - if (c && m.width > f) { - for (m = m.wrap(f), U = 0;U < m.length;U++) { - var S = m[U], n = S.y + S.descent + S.leading; - l.push(S); - S.width > p && (p = S.width); + if (b && m.width > g) { + for (m = m.wrap(g), P = 0;P < m.length;P++) { + var V = m[P], n = V.y + V.descent + V.leading; + l.push(V); + V.width > r && (r = V.width); } } else { - l.push(m), m.width > p && (p = m.width); + l.push(m), m.width > r && (r = m.width); } - m = new q; + m = new d; } else { - n += s + u + w; + n += p + q + s; } - U = ""; - if (V) { - w = u = s = 0; + P = ""; + if (S) { + s = q = p = 0; t = -1; break; } - "\r" === Q && "\n" === g[z + 1] && z++; + "\r" === O && "\n" === k[u + 1] && u++; } - m.addRun(D, L, U, M); + m.addRun(G, Z, P, Q); } - d = this.textRect; - d.w = p; - d.h = n; + e = k[k.length - 1]; + "\r" !== e && "\n" !== e || l.push(m); + e = this.textRect; + e.w = r; + e.h = n; if (a) { - if (!c) { - f = p; - p = e.w; + if (!b) { + g = r; + r = f.w; switch(a) { case 1: - d.x = p - (f + 4) >> 1; + e.x = r - (g + 4) >> 1; break; case 3: - d.x = p - (f + 4); + e.x = r - (g + 4); } - this._textBounds.setElements(d.x - 2, d.y - 2, d.w + 4, d.h + 4); + this._textBounds.setElements(e.x - 2, e.y - 2, e.w + 4, e.h + 4); } - e.h = n + 4; + f.h = n + 4; } else { - this._textBounds = e; + this._textBounds = f; } - for (z = 0;z < l.length;z++) { - if (e = l[z], e.width < f) { - switch(e.align) { + for (u = 0;u < l.length;u++) { + if (f = l[u], f.width < g) { + switch(f.align) { case 1: - e.x = f - e.width | 0; + f.x = g - f.width | 0; break; case 2: - e.x = (f - e.width) / 2 | 0; + f.x = (g - f.width) / 2 | 0; } } } - this.invalidatePaint(); - k.leaveTimeline("RenderableText.reflow"); + this.invalidate(); + h.leaveTimeline("RenderableText.reflow"); } }; - c.prototype.getBounds = function() { - return this._bounds; + b.roundBoundPoints = function(a) { + e(a === b.absoluteBoundPoints); + for (var c = 0;c < a.length;c++) { + var d = a[c]; + d.x = Math.floor(d.x + .1) + .5; + d.y = Math.floor(d.y + .1) + .5; + } }; - c.prototype.render = function(a) { - k.enterTimeline("RenderableText.render"); + b.prototype.render = function(a) { + h.enterTimeline("RenderableText.render"); a.save(); - var c = this._textBounds; - this._backgroundColor && (a.fillStyle = b.ColorUtilities.rgbaToCSSStyle(this._backgroundColor), a.fillRect(c.x, c.y, c.w, c.h)); - this._borderColor && (a.strokeStyle = b.ColorUtilities.rgbaToCSSStyle(this._borderColor), a.lineCap = "square", a.lineWidth = 1, a.strokeRect(c.x, c.y, c.w, c.h)); + var d = this._textBounds; + this._backgroundColor && (a.fillStyle = c.ColorUtilities.rgbaToCSSStyle(this._backgroundColor), a.fillRect(d.x, d.y, d.w, d.h)); + if (this._borderColor) { + a.strokeStyle = c.ColorUtilities.rgbaToCSSStyle(this._borderColor); + a.lineCap = "square"; + a.lineWidth = 1; + var e = b.absoluteBoundPoints, f = a.currentTransform; + f ? (d = d.clone(), (new u(f.a, f.b, f.c, f.d, f.e, f.f)).transformRectangle(d, e), a.setTransform(1, 0, 0, 1, 0, 0)) : (e[0].x = d.x, e[0].y = d.y, e[1].x = d.x + d.w, e[1].y = d.y, e[2].x = d.x + d.w, e[2].y = d.y + d.h, e[3].x = d.x, e[3].y = d.y + d.h); + b.roundBoundPoints(e); + d = new Path2D; + d.moveTo(e[0].x, e[0].y); + d.lineTo(e[1].x, e[1].y); + d.lineTo(e[2].x, e[2].y); + d.lineTo(e[3].x, e[3].y); + d.lineTo(e[0].x, e[0].y); + a.stroke(d); + f && a.setTransform(f.a, f.b, f.c, f.d, f.e, f.f); + } this._coords ? this._renderChars(a) : this._renderLines(a); a.restore(); - k.leaveTimeline("RenderableText.render"); + h.leaveTimeline("RenderableText.render"); }; - c.prototype._renderChars = function(a) { + b.prototype._renderChars = function(a) { if (this._matrix) { var b = this._matrix; a.transform(b.a, b.b, b.c, b.d, b.tx, b.ty); @@ -41876,37 +51782,50 @@ a.font = g.font; a.fillStyle = g.fillStyle; for (var g = g.text, k = 0;k < g.length;k++) { - var l = c.readInt() / 20, m = c.readInt() / 20; - a.fillText(g[k], l, m); + var h = c.readInt() / 20, l = c.readInt() / 20; + a.fillText(g[k], h, l); } } } }; - c.prototype._renderLines = function(a) { + b.prototype._renderLines = function(a) { var b = this._textBounds; a.beginPath(); - a.rect(b.x, b.y, b.w, b.h); + a.rect(b.x + 2, b.y + 2, b.w - 4, b.h - 4); a.clip(); - a.translate(b.x + 2, b.y + 2); - for (var b = this.lines, c = 0;c < b.length;c++) { - for (var d = b[c], e = d.x, f = d.y, g = d.runs, k = 0;k < g.length;k++) { - var l = g[k]; - a.font = l.font; - a.fillStyle = l.fillStyle; - l.underline && a.fillRect(e, f + d.descent / 2 | 0, l.width, 1); - a.fillText(l.text, e, f); - e += l.width; + a.translate(b.x - this._scrollH + 2, b.y + 2); + for (var c = this.lines, d = this._scrollV, e = 0, f = 0;f < c.length;f++) { + var g = c[f], k = g.x, h = g.y; + if (f + 1 < d) { + e = h + g.descent + g.leading; + } else { + h -= e; + if (f + 1 - d && h > b.h) { + break; + } + for (var l = g.runs, m = 0;m < l.length;m++) { + var n = l[m]; + a.font = n.font; + a.fillStyle = n.fillStyle; + n.underline && a.fillRect(k, h + g.descent / 2 | 0, n.width, 1); + a.textAlign = "left"; + a.textBaseline = "alphabetic"; + a.fillText(n.text, k, h); + k += n.width; + } } } }; - return c; - }(a); - k.RenderableText = n; - n = function(a) { + b.absoluteBoundPoints = [new v(0, 0), new v(0, 0), new v(0, 0), new v(0, 0)]; + return b; + }(t); + h.RenderableText = q; + t = function(a) { function b(c, d) { - a.call(this, new g(0, 0, c, d)); - this._flags = 5; + a.call(this); + this._flags = 3145728; this.properties = {}; + this.setBounds(new p(0, 0, c, d)); } __extends(b, a); Object.defineProperty(b.prototype, "text", {get:function() { @@ -41914,7 +51833,7 @@ }, set:function(a) { this._text = a; }, enumerable:!0, configurable:!0}); - b.prototype.render = function(a, b) { + b.prototype.render = function(a, b, c) { a.save(); a.textBaseline = "top"; a.fillStyle = "white"; @@ -41922,277 +51841,214 @@ a.restore(); }; return b; - }(a); - k.Label = n; - a = function(a) { - function c() { - a.call(this, g.createMaxI16()); - this._flags = 14; - this.properties = {}; - } - __extends(c, a); - c.prototype.render = function(a, c) { - function d(b) { - for (var c = Math.ceil((e.x + e.w) / b) * b, f = Math.floor(e.x / b) * b;f < c;f += b) { - a.moveTo(f + .5, e.y), a.lineTo(f + .5, e.y + e.h); - } - c = Math.ceil((e.y + e.h) / b) * b; - for (f = Math.floor(e.y / b) * b;f < c;f += b) { - a.moveTo(e.x, f + .5), a.lineTo(e.x + e.w, f + .5); - } - } - a.save(); - var e = c || this.getBounds(); - a.fillStyle = b.ColorStyle.VeryDark; - a.fillRect(e.x, e.y, e.w, e.h); - a.beginPath(); - d(100); - a.lineWidth = 1; - a.strokeStyle = b.ColorStyle.Dark; - a.stroke(); - a.beginPath(); - d(500); - a.lineWidth = 1; - a.strokeStyle = b.ColorStyle.TabToolbar; - a.stroke(); - a.beginPath(); - d(1E3); - a.lineWidth = 3; - a.strokeStyle = b.ColorStyle.Toolbars; - a.stroke(); - a.lineWidth = 3; - a.beginPath(); - a.moveTo(-1048576, .5); - a.lineTo(1048576, .5); - a.moveTo(.5, -1048576); - a.lineTo(.5, 1048576); - a.strokeStyle = b.ColorStyle.Orange; - a.stroke(); - a.restore(); - }; - return c; - }(a); - k.Grid = a; - })(b.GFX || (b.GFX = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - var g = b.ColorUtilities.clampByte, f = b.Debug.assert, t = function() { + }(t); + h.Label = t; + })(c.GFX || (c.GFX = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + var a = c.ColorUtilities.clampByte, s = c.Debug.assert, v = function() { return function() { }; }(); - k.Filter = t; - var s = function(b) { - function d(a, c, d) { - b.call(this); - this.blurX = a; - this.blurY = c; - this.quality = d; - } - __extends(d, b); - return d; - }(t); - k.BlurFilter = s; - s = function(b) { - function d(a, c, d, f, e, g, k, s, w, r, h) { - b.call(this); - this.alpha = a; - this.angle = c; - this.blurX = d; - this.blurY = f; - this.color = e; - this.distance = g; - this.hideObject = k; - this.inner = s; - this.knockout = w; - this.quality = r; - this.strength = h; + h.Filter = v; + var p = function(a) { + function c(e, h, l) { + a.call(this); + this.blurX = e; + this.blurY = h; + this.quality = l; } - __extends(d, b); - return d; - }(t); - k.DropshadowFilter = s; - t = function(b) { - function d(a, c, d, f, e, g, k, s) { - b.call(this); - this.alpha = a; - this.blurX = c; - this.blurY = d; - this.color = f; - this.inner = e; - this.knockout = g; - this.quality = k; - this.strength = s; + __extends(c, a); + return c; + }(v); + h.BlurFilter = p; + p = function(a) { + function c(e, h, l, p, n, k, f, d, b, g, r) { + a.call(this); + this.alpha = e; + this.angle = h; + this.blurX = l; + this.blurY = p; + this.color = n; + this.distance = k; + this.hideObject = f; + this.inner = d; + this.knockout = b; + this.quality = g; + this.strength = r; } - __extends(d, b); - return d; - }(t); - k.GlowFilter = t; - t = function() { - function b(d) { - f(20 === d.length); - this._m = new Float32Array(d); + __extends(c, a); + return c; + }(v); + h.DropshadowFilter = p; + v = function(a) { + function c(e, h, l, p, n, k, f, d) { + a.call(this); + this.alpha = e; + this.blurX = h; + this.blurY = l; + this.color = p; + this.inner = n; + this.knockout = k; + this.quality = f; + this.strength = d; } - b.prototype.clone = function() { - return new b(this._m); + __extends(c, a); + return c; + }(v); + h.GlowFilter = v; + (function(a) { + a[a.Unknown = 0] = "Unknown"; + a[a.Identity = 1] = "Identity"; + })(h.ColorMatrixType || (h.ColorMatrixType = {})); + v = function() { + function c(a) { + s(20 === a.length); + this._data = new Float32Array(a); + this._type = 0; + } + c.prototype.clone = function() { + var a = new c(this._data); + a._type = this._type; + return a; }; - b.prototype.set = function(b) { - this._m.set(b._m); + c.prototype.set = function(a) { + this._data.set(a._data); + this._type = a._type; }; - b.prototype.toWebGLMatrix = function() { - return new Float32Array(this._m); + c.prototype.toWebGLMatrix = function() { + return new Float32Array(this._data); }; - b.prototype.asWebGLMatrix = function() { - return this._m.subarray(0, 16); - }; - b.prototype.asWebGLVector = function() { - return this._m.subarray(16, 20); - }; - b.prototype.getColorMatrix = function() { - var b = new Float32Array(20), a = this._m; - b[0] = a[0]; - b[1] = a[4]; - b[2] = a[8]; - b[3] = a[12]; - b[4] = 255 * a[16]; - b[5] = a[1]; - b[6] = a[5]; - b[7] = a[9]; - b[8] = a[13]; - b[9] = 255 * a[17]; - b[10] = a[2]; - b[11] = a[6]; - b[12] = a[10]; - b[13] = a[14]; - b[14] = 255 * a[18]; - b[15] = a[3]; - b[16] = a[7]; - b[17] = a[11]; - b[18] = a[15]; - b[19] = 255 * a[19]; - return b; + c.prototype.asWebGLMatrix = function() { + return this._data.subarray(0, 16); }; - b.prototype.getColorTransform = function() { - var b = new Float32Array(8), a = this._m; - b[0] = a[0]; - b[1] = a[5]; - b[2] = a[10]; - b[3] = a[15]; - b[4] = 255 * a[16]; - b[5] = 255 * a[17]; - b[6] = 255 * a[18]; - b[7] = 255 * a[19]; - return b; + c.prototype.asWebGLVector = function() { + return this._data.subarray(16, 20); + }; + c.prototype.isIdentity = function() { + if (this._type & 1) { + return!0; + } + var a = this._data; + return 1 == a[0] && 0 == a[1] && 0 == a[2] && 0 == a[3] && 0 == a[4] && 1 == a[5] && 0 == a[6] && 0 == a[7] && 0 == a[8] && 0 == a[9] && 1 == a[10] && 0 == a[11] && 0 == a[12] && 0 == a[13] && 0 == a[14] && 1 == a[15] && 0 == a[16] && 0 == a[17] && 0 == a[18] && 0 == a[19]; }; - b.prototype.isIdentity = function() { - var b = this._m; - return 1 == b[0] && 0 == b[1] && 0 == b[2] && 0 == b[3] && 0 == b[4] && 1 == b[5] && 0 == b[6] && 0 == b[7] && 0 == b[8] && 0 == b[9] && 1 == b[10] && 0 == b[11] && 0 == b[12] && 0 == b[13] && 0 == b[14] && 1 == b[15] && 0 == b[16] && 0 == b[17] && 0 == b[18] && 0 == b[19]; - }; - b.createIdentity = function() { - return new b([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0]); - }; - b.prototype.setMultipliersAndOffsets = function(b, a, c, f, g, e, k, l) { - for (var m = this._m, s = 0;s < m.length;s++) { - m[s] = 0; - } - m[0] = b; - m[5] = a; - m[10] = c; - m[15] = f; - m[16] = g; - m[17] = e; - m[18] = k; - m[19] = l; - }; - b.prototype.transformRGBA = function(b) { - var a = b >> 24 & 255, c = b >> 16 & 255, f = b >> 8 & 255, k = b & 255, e = this._m; - b = g(a * e[0] + c * e[1] + f * e[2] + k * e[3] + e[16]); - var m = g(a * e[4] + c * e[5] + f * e[6] + k * e[7] + e[17]), l = g(a * e[8] + c * e[9] + f * e[10] + k * e[11] + e[18]), a = g(a * e[12] + c * e[13] + f * e[14] + k * e[15] + e[19]); - return b << 24 | m << 16 | l << 8 | a; - }; - b.prototype.multiply = function(b) { - var a = this._m, c = b._m; - b = a[0]; - var f = a[1], g = a[2], e = a[3], k = a[4], l = a[5], m = a[6], s = a[7], r = a[8], h = a[9], t = a[10], y = a[11], G = a[12], I = a[13], C = a[14], E = a[15], O = a[16], K = a[17], F = a[18], J = a[19], A = c[0], B = c[1], z = c[2], P = c[3], D = c[4], L = c[5], M = c[6], V = c[7], Q = c[8], U = c[9], S = c[10], aa = c[11], $ = c[12], ea = c[13], Z = c[14], v = c[15], X = c[16], ba = c[17], R = c[18], c = c[19]; - a[0] = b * A + k * B + r * z + G * P; - a[1] = f * A + l * B + h * z + I * P; - a[2] = g * A + m * B + t * z + C * P; - a[3] = e * A + s * B + y * z + E * P; - a[4] = b * D + k * L + r * M + G * V; - a[5] = f * D + l * L + h * M + I * V; - a[6] = g * D + m * L + t * M + C * V; - a[7] = e * D + s * L + y * M + E * V; - a[8] = b * Q + k * U + r * S + G * aa; - a[9] = f * Q + l * U + h * S + I * aa; - a[10] = g * Q + m * U + t * S + C * aa; - a[11] = e * Q + s * U + y * S + E * aa; - a[12] = b * $ + k * ea + r * Z + G * v; - a[13] = f * $ + l * ea + h * Z + I * v; - a[14] = g * $ + m * ea + t * Z + C * v; - a[15] = e * $ + s * ea + y * Z + E * v; - a[16] = b * X + k * ba + r * R + G * c + O; - a[17] = f * X + l * ba + h * R + I * c + K; - a[18] = g * X + m * ba + t * R + C * c + F; - a[19] = e * X + s * ba + y * R + E * c + J; + c.createIdentity = function() { + var a = new c([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0]); + a._type = 1; + return a; + }; + c.prototype.setMultipliersAndOffsets = function(a, c, h, p, q, n, k, f) { + for (var d = this._data, b = 0;b < d.length;b++) { + d[b] = 0; + } + d[0] = a; + d[5] = c; + d[10] = h; + d[15] = p; + d[16] = q / 255; + d[17] = n / 255; + d[18] = k / 255; + d[19] = f / 255; + this._type = 0; + }; + c.prototype.transformRGBA = function(c) { + var e = c >> 24 & 255, h = c >> 16 & 255, p = c >> 8 & 255, q = c & 255, n = this._data; + c = a(e * n[0] + h * n[1] + p * n[2] + q * n[3] + 255 * n[16]); + var k = a(e * n[4] + h * n[5] + p * n[6] + q * n[7] + 255 * n[17]), f = a(e * n[8] + h * n[9] + p * n[10] + q * n[11] + 255 * n[18]), e = a(e * n[12] + h * n[13] + p * n[14] + q * n[15] + 255 * n[19]); + return c << 24 | k << 16 | f << 8 | e; + }; + c.prototype.multiply = function(a) { + if (!(a._type & 1)) { + var c = this._data, h = a._data; + a = c[0]; + var p = c[1], q = c[2], n = c[3], k = c[4], f = c[5], d = c[6], b = c[7], g = c[8], r = c[9], s = c[10], u = c[11], v = c[12], B = c[13], M = c[14], N = c[15], K = c[16], y = c[17], D = c[18], L = c[19], H = h[0], J = h[1], C = h[2], E = h[3], F = h[4], I = h[5], G = h[6], Z = h[7], Q = h[8], S = h[9], O = h[10], P = h[11], V = h[12], $ = h[13], W = h[14], x = h[15], ea = h[16], Y = h[17], R = h[18], h = h[19]; + c[0] = a * H + k * J + g * C + v * E; + c[1] = p * H + f * J + r * C + B * E; + c[2] = q * H + d * J + s * C + M * E; + c[3] = n * H + b * J + u * C + N * E; + c[4] = a * F + k * I + g * G + v * Z; + c[5] = p * F + f * I + r * G + B * Z; + c[6] = q * F + d * I + s * G + M * Z; + c[7] = n * F + b * I + u * G + N * Z; + c[8] = a * Q + k * S + g * O + v * P; + c[9] = p * Q + f * S + r * O + B * P; + c[10] = q * Q + d * S + s * O + M * P; + c[11] = n * Q + b * S + u * O + N * P; + c[12] = a * V + k * $ + g * W + v * x; + c[13] = p * V + f * $ + r * W + B * x; + c[14] = q * V + d * $ + s * W + M * x; + c[15] = n * V + b * $ + u * W + N * x; + c[16] = a * ea + k * Y + g * R + v * h + K; + c[17] = p * ea + f * Y + r * R + B * h + y; + c[18] = q * ea + d * Y + s * R + M * h + D; + c[19] = n * ea + b * Y + u * R + N * h + L; + this._type = 0; + } }; - Object.defineProperty(b.prototype, "alphaMultiplier", {get:function() { - return this._m[15]; + Object.defineProperty(c.prototype, "alphaMultiplier", {get:function() { + return this._data[15]; }, enumerable:!0, configurable:!0}); - b.prototype.hasOnlyAlphaMultiplier = function() { - var b = this._m; - return 1 == b[0] && 0 == b[1] && 0 == b[2] && 0 == b[3] && 0 == b[4] && 1 == b[5] && 0 == b[6] && 0 == b[7] && 0 == b[8] && 0 == b[9] && 1 == b[10] && 0 == b[11] && 0 == b[12] && 0 == b[13] && 0 == b[14] && 0 == b[16] && 0 == b[17] && 0 == b[18] && 0 == b[19]; + c.prototype.hasOnlyAlphaMultiplier = function() { + var a = this._data; + return 1 == a[0] && 0 == a[1] && 0 == a[2] && 0 == a[3] && 0 == a[4] && 1 == a[5] && 0 == a[6] && 0 == a[7] && 0 == a[8] && 0 == a[9] && 1 == a[10] && 0 == a[11] && 0 == a[12] && 0 == a[13] && 0 == a[14] && 0 == a[16] && 0 == a[17] && 0 == a[18] && 0 == a[19]; }; - b.prototype.equals = function(b) { - if (!b) { + c.prototype.equals = function(a) { + if (!a) { return!1; } - var a = this._m; - b = b._m; - for (var c = 0;20 > c;c++) { - if (.001 < Math.abs(a[c] - b[c])) { + if (this._type === a._type && 1 === this._type) { + return!0; + } + var c = this._data; + a = a._data; + for (var h = 0;20 > h;h++) { + if (.001 < Math.abs(c[h] - a[h])) { return!1; } } return!0; }; - return b; + c.prototype.toSVGFilterMatrix = function() { + var a = this._data; + return[a[0], a[4], a[8], a[12], a[16], a[1], a[5], a[9], a[13], a[17], a[2], a[6], a[10], a[14], a[18], a[3], a[7], a[11], a[15], a[19]].join(" "); + }; + return c; }(); - k.ColorMatrix = t; - })(b.GFX || (b.GFX = {})); + h.ColorMatrix = v; + })(c.GFX || (c.GFX = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - k.timelineBuffer = new b.Tools.Profiler.TimelineBuffer("Player"); - k.counter = new b.Metrics.Counter(!0); - k.writer = null; - k.enterTimeline = function(b, f) { - k.writer && k.writer.enter(b); - }; - k.leaveTimeline = function(b, f) { - k.writer && k.writer.leave(b); - }; - })(b.Player || (b.Player = {})); -})(Shumway || (Shumway = {})); -var Shumway$$inline_284 = Shumway || (Shumway = {}); -Shumway$$inline_284.playerOptions = Shumway$$inline_284.Settings.shumwayOptions.register(new Shumway$$inline_284.Options.OptionSet("Player Options")); -Shumway$$inline_284.frameEnabledOption = Shumway$$inline_284.playerOptions.register(new Shumway$$inline_284.Options.Option("", "Enable Frame Execution", "boolean", !0, "Enable frame execution.")); -Shumway$$inline_284.timerEnabledOption = Shumway$$inline_284.playerOptions.register(new Shumway$$inline_284.Options.Option("", "Enable Timers", "boolean", !0, "Enable timer events.")); -Shumway$$inline_284.pumpEnabledOption = Shumway$$inline_284.playerOptions.register(new Shumway$$inline_284.Options.Option("", "Enable Pump", "boolean", !0, "Enable display tree serialization.")); -Shumway$$inline_284.pumpRateOption = Shumway$$inline_284.playerOptions.register(new Shumway$$inline_284.Options.Option("", "Pump Rate", "number", 80, "Number of times / second that the display list is synchronized.", {range:{min:1, max:120, step:1}})); -Shumway$$inline_284.frameRateOption = Shumway$$inline_284.playerOptions.register(new Shumway$$inline_284.Options.Option("", "Frame Rate", "number", -1, "Override a movie's frame rate, set to -1 to use the movies default frame rate.", {range:{min:-1, max:120, step:1}})); -Shumway$$inline_284.tracePlayerOption = Shumway$$inline_284.playerOptions.register(new Shumway$$inline_284.Options.Option("tp", "Trace Player", "number", 0, "Trace player every n frames.", {range:{min:0, max:512, step:1}})); -Shumway$$inline_284.traceMouseEventOption = Shumway$$inline_284.playerOptions.register(new Shumway$$inline_284.Options.Option("tme", "Trace Mouse Events", "boolean", !1, "Trace mouse events.")); -Shumway$$inline_284.frameRateMultiplierOption = Shumway$$inline_284.playerOptions.register(new Shumway$$inline_284.Options.Option("", "Frame Rate Multiplier", "number", 1, "Play frames at a faster rate.", {range:{min:1, max:16, step:1}})); -Shumway$$inline_284.dontSkipFramesOption = Shumway$$inline_284.playerOptions.register(new Shumway$$inline_284.Options.Option("", "Disables Frame Skipping", "boolean", !1, "Play all frames, e.g. no skipping frame during throttle.")); -Shumway$$inline_284.playAllSymbolsOption = Shumway$$inline_284.playerOptions.register(new Shumway$$inline_284.Options.Option("", "Play Symbols", "boolean", !1, "Plays all SWF symbols automatically.")); -Shumway$$inline_284.playSymbolOption = Shumway$$inline_284.playerOptions.register(new Shumway$$inline_284.Options.Option("", "Play Symbol Number", "number", 0, "Select symbol by Id.", {range:{min:0, max:2E4, step:1}})); -Shumway$$inline_284.playSymbolFrameDurationOption = Shumway$$inline_284.playerOptions.register(new Shumway$$inline_284.Options.Option("", "Play Symbol Duration", "number", 0, "How many frames to play, 0 for all frames of the movie clip.", {range:{min:0, max:128, step:1}})); -Shumway$$inline_284.playSymbolCountOption = Shumway$$inline_284.playerOptions.register(new Shumway$$inline_284.Options.Option("", "Play Symbol Count", "number", -1, "Select symbol count.", {range:{min:0, max:2E4, step:1}})); -Shumway$$inline_284.stageScaleOption = Shumway$$inline_284.playerOptions.register(new Shumway$$inline_284.Options.Option("", "Stage Scale", "number", 1, "Scales the symbols.", {range:{min:.1, max:16, step:.01}})); -Shumway$$inline_284.useParsingWorkerOption = Shumway$$inline_284.playerOptions.register(new Shumway$$inline_284.Options.Option("useWorker", "Use Parsing Worker", "boolean", !0, "Determines whether to spawn a DOM worker thread for parsing")); -(function(b) { - var k = function() { - function b() { +(function(c) { + (function(h) { + h.timelineBuffer = new c.Tools.Profiler.TimelineBuffer("Player"); + h.counter = new c.Metrics.Counter(!0); + h.writer = null; + h.enterTimeline = function(a, c) { + h.writer && h.writer.enter(a); + }; + h.leaveTimeline = function(a, c) { + h.writer && h.writer.leave(a); + }; + })(c.Player || (c.Player = {})); +})(Shumway || (Shumway = {})); +var Shumway$$inline_609 = Shumway || (Shumway = {}); +Shumway$$inline_609.playerOptions = Shumway$$inline_609.Settings.shumwayOptions.register(new Shumway$$inline_609.Options.OptionSet("Player Options")); +Shumway$$inline_609.frameEnabledOption = Shumway$$inline_609.playerOptions.register(new Shumway$$inline_609.Options.Option("", "Enable Frame Execution", "boolean", !0, "Enable frame execution.")); +Shumway$$inline_609.timerEnabledOption = Shumway$$inline_609.playerOptions.register(new Shumway$$inline_609.Options.Option("", "Enable Timers", "boolean", !0, "Enable timer events.")); +Shumway$$inline_609.pumpEnabledOption = Shumway$$inline_609.playerOptions.register(new Shumway$$inline_609.Options.Option("", "Enable Pump", "boolean", !0, "Enable display tree serialization.")); +Shumway$$inline_609.pumpRateOption = Shumway$$inline_609.playerOptions.register(new Shumway$$inline_609.Options.Option("", "Pump Rate", "number", 60, "Number of times / second that the display list is synchronized.", {range:{min:1, max:120, step:1}})); +Shumway$$inline_609.frameRateOption = Shumway$$inline_609.playerOptions.register(new Shumway$$inline_609.Options.Option("", "Frame Rate", "number", 60, "Override a movie's frame rate, set to -1 to use the movies default frame rate.", {range:{min:-1, max:120, step:1}})); +Shumway$$inline_609.tracePlayerOption = Shumway$$inline_609.playerOptions.register(new Shumway$$inline_609.Options.Option("tp", "Trace Player", "number", 0, "Trace player every n frames.", {range:{min:0, max:512, step:1}})); +Shumway$$inline_609.traceMouseEventOption = Shumway$$inline_609.playerOptions.register(new Shumway$$inline_609.Options.Option("tme", "Trace Mouse Events", "boolean", !1, "Trace mouse events.")); +Shumway$$inline_609.frameRateMultiplierOption = Shumway$$inline_609.playerOptions.register(new Shumway$$inline_609.Options.Option("", "Frame Rate Multiplier", "number", 1, "Play frames at a faster rate.", {range:{min:1, max:16, step:1}})); +Shumway$$inline_609.dontSkipFramesOption = Shumway$$inline_609.playerOptions.register(new Shumway$$inline_609.Options.Option("", "Disables Frame Skipping", "boolean", !1, "Play all frames, e.g. no skipping frame during throttle.")); +Shumway$$inline_609.playAllSymbolsOption = Shumway$$inline_609.playerOptions.register(new Shumway$$inline_609.Options.Option("", "Play Symbols", "boolean", !1, "Plays all SWF symbols automatically.")); +Shumway$$inline_609.playSymbolOption = Shumway$$inline_609.playerOptions.register(new Shumway$$inline_609.Options.Option("", "Play Symbol Number", "number", 0, "Select symbol by Id.", {range:{min:0, max:2E4, step:1}})); +Shumway$$inline_609.playSymbolFrameDurationOption = Shumway$$inline_609.playerOptions.register(new Shumway$$inline_609.Options.Option("", "Play Symbol Duration", "number", 0, "How many frames to play, 0 for all frames of the movie clip.", {range:{min:0, max:128, step:1}})); +Shumway$$inline_609.playSymbolCountOption = Shumway$$inline_609.playerOptions.register(new Shumway$$inline_609.Options.Option("", "Play Symbol Count", "number", -1, "Select symbol count.", {range:{min:0, max:2E4, step:1}})); +(function(c) { + var h = function() { + function a() { this._expectedNextFrameAt = performance.now(); this._drawStats = []; this._drawsSkipped = this._drawStarted = this._drawStatsSum = 0; @@ -42201,273 +52057,292 @@ this._trackDelta = !1; this._onTimeDelta = this._delta = 0; } - Object.defineProperty(b.prototype, "shallSkipDraw", {get:function() { - if (this._drawsSkipped >= b.MAX_DRAWS_TO_SKIP) { + Object.defineProperty(a.prototype, "shallSkipDraw", {get:function() { + if (this._drawsSkipped >= a.MAX_DRAWS_TO_SKIP) { return!1; } - var f = this._drawStats.length < b.STATS_TO_REMEMBER ? 0 : this._drawStatsSum / this._drawStats.length; - return performance.now() + f + b.INTERVAL_PADDING_MS > this._expectedNextFrameAt; + var c = this._drawStats.length < a.STATS_TO_REMEMBER ? 0 : this._drawStatsSum / this._drawStats.length; + return performance.now() + c + a.INTERVAL_PADDING_MS > this._expectedNextFrameAt; }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "nextFrameIn", {get:function() { + Object.defineProperty(a.prototype, "nextFrameIn", {get:function() { return Math.max(0, this._expectedNextFrameAt - performance.now()); }, enumerable:!0, configurable:!0}); - Object.defineProperty(b.prototype, "isOnTime", {get:function() { + Object.defineProperty(a.prototype, "isOnTime", {get:function() { return this._onTime; }, enumerable:!0, configurable:!0}); - b.prototype.startFrame = function(f) { - var k = f = 1E3 / f, s = this._onTimeDelta + this._delta; - 0 !== s && (0 > s ? k *= b.SPEED_ADJUST_RATE : 0 < s && (k /= b.SPEED_ADJUST_RATE), this._onTimeDelta += f - k); - this._expectedNextFrameAt += k; + a.prototype.startFrame = function(c) { + var h = c = 1E3 / c, p = this._onTimeDelta + this._delta; + 0 !== p && (0 > p ? h *= a.SPEED_ADJUST_RATE : 0 < p && (h /= a.SPEED_ADJUST_RATE), this._onTimeDelta += c - h); + this._expectedNextFrameAt += h; this._onTime = !0; }; - b.prototype.endFrame = function() { - var f = performance.now() + b.INTERVAL_PADDING_MS; - f > this._expectedNextFrameAt && (this._trackDelta && (this._onTimeDelta += this._expectedNextFrameAt - f, console.log(this._onTimeDelta)), this._expectedNextFrameAt = f, this._onTime = !1); + a.prototype.endFrame = function() { + var c = performance.now() + a.INTERVAL_PADDING_MS; + c > this._expectedNextFrameAt && (this._trackDelta && (this._onTimeDelta += this._expectedNextFrameAt - c, console.log(this._onTimeDelta)), this._expectedNextFrameAt = c, this._onTime = !1); }; - b.prototype.startDraw = function() { + a.prototype.startDraw = function() { this._drawsSkipped = 0; this._drawStarted = performance.now(); }; - b.prototype.endDraw = function() { - var f = performance.now() - this._drawStarted; - this._drawStats.push(f); - for (this._drawStatsSum += f;this._drawStats.length > b.STATS_TO_REMEMBER;) { + a.prototype.endDraw = function() { + var c = performance.now() - this._drawStarted; + this._drawStats.push(c); + for (this._drawStatsSum += c;this._drawStats.length > a.STATS_TO_REMEMBER;) { this._drawStatsSum -= this._drawStats.shift(); } }; - b.prototype.skipDraw = function() { + a.prototype.skipDraw = function() { this._drawsSkipped++; }; - b.prototype.setDelta = function(b) { - this._trackDelta && (this._delta = b); + a.prototype.setDelta = function(a) { + this._trackDelta && (this._delta = a); }; - b.prototype.startTrackDelta = function() { + a.prototype.startTrackDelta = function() { this._trackDelta = !0; }; - b.prototype.endTrackDelta = function() { + a.prototype.endTrackDelta = function() { this._trackDelta && (this._trackDelta = !1, this._onTimeDelta = this._delta = 0); }; - b.STATS_TO_REMEMBER = 50; - b.MAX_DRAWS_TO_SKIP = 2; - b.INTERVAL_PADDING_MS = 4; - b.SPEED_ADJUST_RATE = .9; - return b; + a.STATS_TO_REMEMBER = 50; + a.MAX_DRAWS_TO_SKIP = 2; + a.INTERVAL_PADDING_MS = 4; + a.SPEED_ADJUST_RATE = .9; + return a; }(); - b.FrameScheduler = k; + c.FrameScheduler = h; })(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - var f = b.AVM2.AS.flash, k = b.AVM2.AS.flash.display, s = b.AVM2.AS.flash.display.BitmapData, m = b.AVM2.AS.flash.display.DisplayObjectFlags, d = b.AVM2.AS.flash.display.BlendMode, a = b.AVM2.AS.flash.display.PixelSnapping, c = b.AVM2.AS.flash.geom.Point, n = b.Bounds, p = b.Debug.assert, e = b.Player.writer, q = function() { - function c() { +(function(c) { + (function(h) { + (function(a) { + var h = c.AVM2.AS.flash, v = h.display, p = h.display.BitmapData, u = h.display.DisplayObjectFlags, l = h.display.BlendMode, e = h.display.PixelSnapping, m = h.geom.Point, t = c.Bounds, q = h.ui.MouseCursor, n = c.Debug.assert, k = c.Player.writer, f = function() { + function a() { this.phase = 0; this.roots = null; } - c.prototype.begin = function(a) { + a.prototype.begin = function(a) { this.roots = [a]; }; - c.prototype.remoteObjects = function() { + a.prototype.remoteObjects = function() { this.phase = 0; - for (var a = this.roots, c = 0;c < a.length;c++) { - b.Player.enterTimeline("remoting objects"), this.writeDisplayObject(a[c]), b.Player.leaveTimeline("remoting objects"); + for (var a = this.roots, d = 0;d < a.length;d++) { + c.Player.enterTimeline("remoting objects"), this.writeDirtyDisplayObjects(a[d]), c.Player.leaveTimeline("remoting objects"); } }; - c.prototype.remoteReferences = function() { + a.prototype.remoteReferences = function() { this.phase = 1; - for (var a = this.roots, c = 0;c < a.length;c++) { - b.Player.enterTimeline("remoting references"), this.writeDisplayObject(a[c]), b.Player.leaveTimeline("remoting references"); + for (var a = this.roots, d = 0;d < a.length;d++) { + c.Player.enterTimeline("remoting references"), this.writeDirtyDisplayObjects(a[d], !0), c.Player.leaveTimeline("remoting references"); } }; - c.prototype.writeDisplayObject = function(a) { - var c = this, d = this.roots; + a.prototype.writeDirtyDisplayObjects = function(a, d) { + void 0 === d && (d = !1); + var e = this, f = this.roots; a.visit(function(a) { - c.writeUpdateFrame(a); - d && a.mask && b.ArrayUtilities.pushUnique(d, a.mask._findFurthestAncestorOrSelf()); - return 0; - }, 16, m.Dirty); + a._hasAnyFlags(u.Dirty) && (e.writeUpdateFrame(a), f && a.mask && c.ArrayUtilities.pushUnique(f, a.mask._findFurthestAncestorOrSelf())); + e.writeDirtyAssets(a); + if (a._hasFlags(536870912)) { + return 0; + } + d && a._removeFlags(536870912); + return 2; + }, 0); }; - c.prototype.writeStage = function(a) { - e && e.writeLn("Sending Stage"); + a.prototype.writeStage = function(a, c) { + k && k.writeLn("Sending Stage"); this.output.writeInt(104); this.output.writeInt(a._id); this.output.writeInt(a.color); - this._writeRectangle(new n(0, 0, 20 * a.stageWidth, 20 * a.stageHeight)); + this._writeRectangle(new t(0, 0, 20 * a.stageWidth, 20 * a.stageHeight)); + this.output.writeInt(h.display.StageAlign.toNumber(a.align)); + this.output.writeInt(h.display.StageScaleMode.toNumber(a.scaleMode)); + this.output.writeInt(h.display.StageDisplayState.toNumber(a.displayState)); + var d = h.ui.Mouse.cursor; + if (c) { + if (this.output.writeInt(c._id), d === q.AUTO) { + var e = c; + do { + if (h.display.SimpleButton.isType(e) || h.display.Sprite.isType(e) && e.buttonMode && c.useHandCursor) { + d = q.BUTTON; + break; + } + e = e._parent; + } while (e && e !== a); + } + } else { + this.output.writeInt(-1); + } + this.output.writeInt(q.toNumber(d)); }; - c.prototype.writeGraphics = function(a) { + a.prototype.writeGraphics = function(a) { if (a._isDirty) { - e && e.writeLn("Sending Graphics: " + a._id); - for (var b = a._textures, c = b.length, d = 0;d < c;d++) { - this.writeBitmapData(b[d]); + k && k.writeLn("Sending Graphics: " + a._id); + for (var c = a._textures, d = c.length, e = 0;e < d;e++) { + c[e] && this.writeBitmapData(c[e]); } this.output.writeInt(101); this.output.writeInt(a._id); this.output.writeInt(-1); this._writeRectangle(a._getContentBounds()); this._writeAsset(a._graphicsData.toPlainObject()); - this.output.writeInt(c); - for (d = 0;d < c;d++) { - this.output.writeInt(b[d]._id); + this.output.writeInt(d); + for (e = 0;e < d;e++) { + this.output.writeInt(c[e] ? c[e]._id : -1); } a._isDirty = !1; } }; - c.prototype.writeNetStream = function(a) { - a._isDirty && (e && e.writeLn("Sending NetStream: " + a._id), this.output.writeInt(105), this.output.writeInt(a._id), this.output.writeUTF(a._url), a._isDirty = !1); + a.prototype.writeNetStream = function(a, c) { + a._isDirty && (k && k.writeLn("Sending NetStream: " + a._id), this.output.writeInt(105), this.output.writeInt(a._id), this._writeRectangle(c), a._isDirty = !1); }; - c.prototype.writeBitmapData = function(a) { - a._isDirty && (e && e.writeLn("Sending BitmapData: " + a._id), this.output.writeInt(102), this.output.writeInt(a._id), this.output.writeInt(a._symbol ? a._symbol.id : -1), this._writeRectangle(a._getContentBounds()), this.output.writeInt(a._type), this._writeAsset(a._dataBuffer.toPlainObject()), a._isDirty = !1); + a.prototype.writeBitmapData = function(a) { + a._isDirty && (k && k.writeLn("Sending BitmapData: " + a._id), this.output.writeInt(102), this.output.writeInt(a._id), this.output.writeInt(a._symbol ? a._symbol.id : -1), this._writeRectangle(a._getContentBounds()), this.output.writeInt(a._type), this._writeAsset(a._dataBuffer.toPlainObject()), a._isDirty = !1); }; - c.prototype.writeTextContent = function(a) { - if (a.flags & b.TextContentFlags.Dirty) { - e && e.writeLn("Sending TextContent: " + a._id); + a.prototype.writeTextContent = function(a) { + if (a.flags & c.TextContentFlags.Dirty) { + k && k.writeLn("Sending TextContent: " + a._id); this.output.writeInt(103); this.output.writeInt(a._id); this.output.writeInt(-1); this._writeRectangle(a.bounds); - this._writeMatrix(a.matrix || f.geom.Matrix.FROZEN_IDENTITY_MATRIX); + this._writeMatrix(a.matrix || h.geom.Matrix.FROZEN_IDENTITY_MATRIX); this.output.writeInt(a.backgroundColor); this.output.writeInt(a.borderColor); this.output.writeInt(a.autoSize); this.output.writeBoolean(a.wordWrap); + this.output.writeInt(a.scrollV); + this.output.writeInt(a.scrollH); this._writeAsset(a.plainText); this._writeAsset(a.textRunData.toPlainObject()); - var c = a.coords; - if (c) { - var d = c.length; - this.output.writeInt(d); - for (var g = 0;g < d;g++) { - this.output.writeInt(c[g]); + var d = a.coords; + if (d) { + var e = d.length; + this.output.writeInt(e); + for (var f = 0;f < e;f++) { + this.output.writeInt(d[f]); } } else { this.output.writeInt(0); } - a.flags &= ~b.TextContentFlags.Dirty; - } - }; - c.prototype.writeFont = function(a) { - if ("embedded" === a.fontType) { - e && e.writeLn("Sending Font: " + a._id); - var b = a._symbol; - p(b); - this.output.writeInt(200); - this.output.writeInt(a._id); - this.output.writeBoolean(b.bold); - this.output.writeBoolean(b.italic); - this._writeAsset(b.data); + a.flags &= ~c.TextContentFlags.Dirty; } }; - c.prototype.writeClip = function(a) { + a.prototype.writeClippedObjectsCount = function(a) { if (0 <= a._clipDepth && a._parent) { - var b = a._parent.getChildIndex(a); + var c = a._parent.getChildIndex(a); a = a._parent.getClipDepthIndex(a._clipDepth); - for (var c = b + 1;c <= b;c++) { + if (0 > a - c) { + this.output.writeInt(-1); + } else { + for (var d = c + 1;d <= c;d++) { + } + this.output.writeInt(a - c); } - p(0 <= a - b); - this.output.writeInt(a - b); } else { this.output.writeInt(-1); } }; - c.prototype.writeUpdateFrame = function(b) { + a.prototype.writeUpdateFrame = function(a) { this.output.writeInt(100); - this.output.writeInt(b._id); - e && e.writeLn("Sending UpdateFrame: " + b.debugName()); - var c = !1, g = b._hasFlags(1048576), h = b._hasFlags(67108864), l = b._hasFlags(536870912), n = null; - f.media.Video.isType(b) && (n = b); + this.output.writeInt(a._id); + k && k.writeLn("Sending UpdateFrame: " + a.debugName(!0)); + var c = !1, d = a._hasFlags(1048576), f = a._hasFlags(67108864), m = a._hasFlags(1073741824), n = null; + h.media.Video.isType(a) && (n = a); var p = !1; - 1 === this.phase && (p = b._hasAnyFlags(65011712), c = b._hasFlags(134217728)); + 1 === this.phase && (p = a._hasAnyFlags(65011712), c = a._hasFlags(134217728)); var q = null; - k.Bitmap.isType(b) && (q = b); - var s = b._hasFlags(268435456), E; - E = 0 | (g ? 1 : 0) | (h ? 8 : 0); - E |= c ? 64 : 0; - E |= s ? 128 : 0; - E |= l ? 32 : 0; - E |= p ? 4 : 0; - this.output.writeInt(E); - g && this._writeMatrix(b._getMatrix()); - h && this._writeColorTransform(b._colorTransform); - c && this.output.writeInt(b.mask ? b.mask._id : -1); - s && this.writeClip(b); - l && (this.output.writeInt(d.toNumber(b._blendMode)), this.output.writeBoolean(b._hasFlags(1)), q ? (this.output.writeInt(a.toNumber(q.pixelSnapping)), this.output.writeInt(q.smoothing ? 1 : 0)) : (this.output.writeInt(a.toNumber(a.AUTO)), this.output.writeInt(1))); - c = b._getGraphics(); - g = b._getTextContent(); + v.Bitmap.isType(a) && (q = a); + var t = a._hasFlags(268435456), K; + K = 0 | (d ? 1 : 0) | (f ? 8 : 0); + K |= c ? 64 : 0; + K |= t ? 128 : 0; + K |= m ? 32 : 0; + K |= p ? 4 : 0; + this.output.writeInt(K); + d && this._writeMatrix(a._getMatrix()); + f && this._writeColorTransform(a._colorTransform); + c && this.output.writeInt(a.mask ? a.mask._id : -1); + t && this.writeClippedObjectsCount(a); + m && (this.output.writeInt(a._ratio), this.output.writeInt(l.toNumber(a._blendMode)), this._writeFilters(a.filters), this.output.writeBoolean(a._hasFlags(1)), this.output.writeBoolean(a.cacheAsBitmap), q ? (this.output.writeInt(e.toNumber(q.pixelSnapping)), this.output.writeInt(q.smoothing ? 1 : 0)) : (this.output.writeInt(e.toNumber(e.AUTO)), this.output.writeInt(1))); + c = a._getGraphics(); + d = a._getTextContent(); if (p) { - e && e.enter("Children: {"); + k && k.enter("Children: {"); if (q) { q.bitmapData ? (this.output.writeInt(1), this.output.writeInt(134217728 | q.bitmapData._id)) : this.output.writeInt(0); } else { if (n) { n._netStream ? (this.output.writeInt(1), this.output.writeInt(134217728 | n._netStream._id)) : this.output.writeInt(0); } else { - if (h = c || g ? 1 : 0, (p = b._children) && (h += p.length), this.output.writeInt(h), c ? (e && e.writeLn("Reference Graphics: " + c._id), this.output.writeInt(134217728 | c._id)) : g && (e && e.writeLn("Reference TextContent: " + g._id), this.output.writeInt(134217728 | g._id)), p) { - for (h = 0;h < p.length;h++) { - e && e.writeLn("Reference DisplayObject: " + p[h].debugName()), this.output.writeInt(p[h]._id), 0 <= p[h]._clipDepth && p[h]._setFlags(268435456); + if (p = c || d ? 1 : 0, (n = a._children) && (p += n.length), this.output.writeInt(p), c ? (k && k.writeLn("Reference Graphics: " + c._id), this.output.writeInt(134217728 | c._id)) : d && (k && k.writeLn("Reference TextContent: " + d._id), this.output.writeInt(134217728 | d._id)), n) { + for (p = 0;p < n.length;p++) { + k && k.writeLn("Reference DisplayObject: " + n[p].debugName()), this.output.writeInt(n[p]._id), 0 <= n[p]._clipDepth && n[p]._setFlags(268435456); } } } } - e && e.leave("}"); + k && k.leave("}"); } - 1 === this.phase && b._removeFlags(m.Dirty); - c ? this.writeGraphics(c) : g ? this.writeTextContent(g) : q ? q.bitmapData && this.writeBitmapData(q.bitmapData) : n && n._netStream && this.writeNetStream(n._netStream); + 1 === this.phase && a._removeFlags(u.Dirty); }; - c.prototype.writeDrawToBitmap = function(a, b, c, e, f, g, k) { - "undefined" === typeof c && (c = null); - "undefined" === typeof e && (e = null); - "undefined" === typeof f && (f = null); - "undefined" === typeof g && (g = null); - "undefined" === typeof k && (k = !1); - this.output.writeInt(201); + a.prototype.writeDirtyAssets = function(a) { + var c = a._getGraphics(); + c ? this.writeGraphics(c) : (c = a._getTextContent()) ? this.writeTextContent(c) : (c = null, v.Bitmap.isType(a) ? (c = a, c.bitmapData && this.writeBitmapData(c.bitmapData)) : (c = null, h.media.Video.isType(a) && (c = a, c._netStream && this.writeNetStream(c._netStream, c._getContentBounds())))); + }; + a.prototype.writeDrawToBitmap = function(a, c, d, e, f, h, k) { + void 0 === d && (d = null); + void 0 === e && (e = null); + void 0 === f && (f = null); + void 0 === h && (h = null); + void 0 === k && (k = !1); + this.output.writeInt(200); this.output.writeInt(a._id); - s.isType(b) ? this.output.writeInt(134217728 | b._id) : this.output.writeInt(b._id); - a = 0 | (c ? 1 : 0) | (e ? 8 : 0); - a |= g ? 16 : 0; + p.isType(c) ? this.output.writeInt(134217728 | c._id) : this.output.writeInt(c._id); + a = 0 | (d ? 1 : 0) | (e ? 8 : 0); + a |= h ? 16 : 0; this.output.writeInt(a); - c && this._writeMatrix(c); + d && this._writeMatrix(d); e && this._writeColorTransform(e); - g && this._writeRectangle(n.FromRectangle(g)); - this.output.writeInt(d.toNumber(f)); + h && this._writeRectangle(t.FromRectangle(h)); + this.output.writeInt(l.toNumber(f)); this.output.writeBoolean(k); }; - c.prototype._writeMatrix = function(a) { + a.prototype._writeMatrix = function(a) { this.output.write6Floats(a.a, a.b, a.c, a.d, a.tx, a.ty); }; - c.prototype._writeRectangle = function(a) { + a.prototype._writeRectangle = function(a) { this.output.write4Ints(a.xMin, a.yMin, a.width, a.height); }; - c.prototype._writeAsset = function(a) { + a.prototype._writeAsset = function(a) { this.output.writeInt(this.outputAssets.length); this.outputAssets.push(a); }; - c.prototype._writeColorTransform = function(a) { - var b = this.output, c = a.redMultiplier, d = a.greenMultiplier, e = a.blueMultiplier, f = a.alphaMultiplier, g = a.redOffset, k = a.greenOffset, l = a.blueOffset; + a.prototype._writeFilters = function(a) { + for (var d = 0, e = 0;e < a.length;e++) { + h.filters.BlurFilter.isType(a[e]) || h.filters.DropShadowFilter.isType(a[e]) || h.filters.GlowFilter.isType(a[e]) ? d++ : c.Debug.somewhatImplemented(a[e].toString()); + } + this.output.writeInt(d); + for (e = 0;e < a.length;e++) { + d = a[e], h.filters.BlurFilter.isType(d) ? (this.output.writeInt(0), this.output.writeFloat(d.blurX), this.output.writeFloat(d.blurY), this.output.writeInt(d.quality)) : h.filters.DropShadowFilter.isType(d) ? (this.output.writeInt(1), this.output.writeFloat(d.alpha), this.output.writeFloat(d.angle), this.output.writeFloat(d.blurX), this.output.writeFloat(d.blurY), this.output.writeInt(d.color), this.output.writeFloat(d.distance), this.output.writeBoolean(d.hideObject), this.output.writeBoolean(d.inner), + this.output.writeBoolean(d.knockout), this.output.writeInt(d.quality), this.output.writeFloat(d.strength)) : h.filters.GlowFilter.isType(d) && (this.output.writeInt(1), this.output.writeFloat(d.alpha), this.output.writeFloat(0), this.output.writeFloat(d.blurX), this.output.writeFloat(d.blurY), this.output.writeInt(d.color), this.output.writeFloat(0), this.output.writeBoolean(!1), this.output.writeBoolean(d.inner), this.output.writeBoolean(d.knockout), this.output.writeInt(d.quality), + this.output.writeFloat(d.strength)); + } + }; + a.prototype._writeColorTransform = function(a) { + var c = this.output, d = a.redMultiplier, e = a.greenMultiplier, f = a.blueMultiplier, h = a.alphaMultiplier, k = a.redOffset, l = a.greenOffset, m = a.blueOffset; a = a.alphaOffset; - g === k && k === l && l === a && 0 === a && c === d && d === e && 1 === e ? 1 === f ? b.writeInt(0) : (b.writeInt(1), b.writeFloat(f)) : (b.writeInt(2), b.writeFloat(c), b.writeFloat(d), b.writeFloat(e), b.writeFloat(f), b.writeInt(g), b.writeInt(k), b.writeInt(l), b.writeInt(a)); + k === l && l === m && m === a && 0 === a && d === e && e === f && 1 === f ? 1 === h ? c.writeInt(0) : (c.writeInt(1), c.writeFloat(h)) : (c.writeInt(2), c.writeFloat(d), c.writeFloat(e), c.writeFloat(f), c.writeFloat(h), c.writeInt(k), c.writeInt(l), c.writeInt(m), c.writeInt(a)); }; - c.prototype.writeRequestBitmapData = function(a) { - e && e.writeLn("Sending BitmapData Request"); + a.prototype.writeRequestBitmapData = function(a) { + k && k.writeLn("Sending BitmapData Request"); this.output.writeInt(106); this.output.writeInt(a._id); }; - c.prototype.writeDecodeImage = function(a, b, c) { - e && e.writeLn("Sending DecodeImage"); - this.output.writeInt(107); - this.output.writeInt(a); - this.output.writeInt(b); - this._writeAsset(c); - }; - return c; + return a; }(); - g.PlayerChannelSerializer = q; - q = function() { + a.PlayerChannelSerializer = f; + f = function() { function a() { } - a.prototype._readAsset = function() { - var a = this.input.readInt(), b = this.inputAssets[a]; - this.inputAssets[a] = null; - return b; - }; a.prototype.read = function() { var a = this.input.readInt(); switch(a) { @@ -42477,47 +52352,42 @@ return this._readKeyboardEvent(); case 302: return this._readFocusEvent(); - case 108: - return this._readDecodeImageResponse(); } - p(!1, "Unknown MessageReader tag: " + a); - }; - a.prototype._readDecodeImageResponse = function() { - var a = this.input, b = a.readInt(), c = a.readInt(), d = this._readAsset(), e = a.readInt(), a = a.readInt(); - return{tag:108, promiseId:b, type:c, data:d, width:e, height:a}; + n(!1, "Unknown MessageReader tag: " + a); }; a.prototype._readFocusEvent = function() { return{tag:302, type:this.input.readInt()}; }; a.prototype._readMouseEvent = function() { - var a = this.input, d = a.readInt(), d = b.Remoting.MouseEventNames[d], e = a.readFloat(), f = a.readFloat(), g = a.readInt(), a = a.readInt(); - return{tag:300, type:d, point:new c(e, f), ctrlKey:!!(a & 1), altKey:!!(a & 2), shiftKey:!!(a & 4), buttons:g}; + var a = this.input, d = a.readInt(), d = c.Remoting.MouseEventNames[d], e = a.readFloat(), f = a.readFloat(), h = a.readInt(), a = a.readInt(); + return{tag:300, type:d, point:new m(e, f), ctrlKey:!!(a & 1), altKey:!!(a & 2), shiftKey:!!(a & 4), buttons:h}; }; a.prototype._readKeyboardEvent = function() { - var a = this.input, c = a.readInt(), c = b.Remoting.KeyboardEventNames[c], d = a.readInt(), e = a.readInt(), f = a.readInt(), a = a.readInt(); - return{tag:301, type:c, keyCode:d, charCode:e, location:f, ctrlKey:!!(a & 1), altKey:!!(a & 2), shiftKey:!!(a & 4)}; + var a = this.input, d = a.readInt(), d = c.Remoting.KeyboardEventNames[d], e = a.readInt(), f = a.readInt(), h = a.readInt(), a = a.readInt(); + return{tag:301, type:d, keyCode:e, charCode:f, location:h, ctrlKey:!!(a & 1), altKey:!!(a & 2), shiftKey:!!(a & 4)}; }; return a; }(); - g.PlayerChannelDeserializer = q; - })(k.Player || (k.Player = {})); - })(b.Remoting || (b.Remoting = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - var g = b.Debug.assert, f = b.AVM2.AS.flash, t = b.ArrayUtilities.DataBuffer, s = b.AVM2.Runtime.AVM2, m = b.AVM2.AS.avm1lib, d = b.AVM2.AS.flash.events.Event, a = b.AVM2.AS.flash.display.DisplayObject, c = b.AVM2.AS.flash.events.EventDispatcher, n = b.AVM2.AS.flash.display.MovieClip, p = b.AVM2.AS.flash.display.Loader, e = b.AVM2.AS.flash.ui.MouseEventDispatcher, q = b.AVM2.AS.flash.ui.KeyboardEventDispatcher, l = function() { - function l() { + a.PlayerChannelDeserializer = f; + })(h.Player || (h.Player = {})); + })(c.Remoting || (c.Remoting = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + var a = c.Debug.assert, s = c.Debug.somewhatImplemented, v = c.AVM2.AS.flash, p = c.ArrayUtilities.DataBuffer, u = c.AVM2.Runtime.AVM2, l = v.events.Event, e = v.display.DisplayObject, m = v.events.EventDispatcher, t = v.display.Loader, q = v.ui.MouseEventDispatcher, n = v.ui.KeyboardEventDispatcher, k = function() { + function f() { this._framesPlayed = 0; + this._videoEventListeners = []; this._pendingPromises = []; this.externalCallback = null; this._lastPumpTime = 0; this._hasFocus = this._isPageVisible = !0; - this._keyboardEventDispatcher = new q; - this._mouseEventDispatcher = new e; - this._writer = new b.IndentingWriter; - s.instance.globals["Shumway.Player.Utils"] = this; + this._keyboardEventDispatcher = new n; + this._mouseEventDispatcher = new q; + this._writer = new c.IndentingWriter; + u.instance.globals["Shumway.Player.Utils"] = this; } - l.prototype._getNextAvailablePromiseId = function() { + f.prototype._getNextAvailablePromiseId = function() { for (var a = this._pendingPromises.length, b = 0;b < a;b++) { if (!this._pendingPromises[b]) { return b; @@ -42525,55 +52395,61 @@ } return a; }; - l.prototype.onSendUpdates = function(a, b, c) { + Object.defineProperty(f.prototype, "stage", {get:function() { + return this._stage; + }, enumerable:!0, configurable:!0}); + f.prototype.onSendUpdates = function(a, b, c) { throw Error("This method is abstract"); }; - l.prototype._shouldThrottleDownRendering = function() { + f.prototype._shouldThrottleDownRendering = function() { return!this._isPageVisible; }; - l.prototype._shouldThrottleDownFrameExecution = function() { + f.prototype._shouldThrottleDownFrameExecution = function() { return!this._isPageVisible; }; - l.prototype.load = function(a, c) { - g(!this._loader, "Can't load twice."); - var d = this, e = this._stage = new f.display.Stage, k = this._loader = f.display.Loader.getRootLoader(), l = this._loaderInfo = k.contentLoaderInfo; - b.playAllSymbolsOption.value ? (this._playAllSymbols(), l._allowCodeExecution = !1) : k._startPromise.then(function() { - l.actionScriptVersion === f.display.ActionScriptVersion.ACTIONSCRIPT2 ? (m.AVM1Key.asCallPublicProperty("__bind", [e]), m.AVM1Mouse.asCallPublicProperty("__bind", [e]), n.frameNavigationModel = 1) : 10 > l.swfVersion && (n.frameNavigationModel = 2); - var a = void 0 !== d.defaultStageColor ? d.defaultStageColor : l._colorRGBA, c = k.content; - e._loaderInfo = l; - e.frameRate = l.frameRate; - e.setStageWidth(l.width); - e.setStageHeight(l.height); - e.setStageColor(b.ColorUtilities.RGBAToARGB(a)); - e.addTimelineObjectAtDepth(c, 0); - d._enterLoops(); - }); - if (c) { - var p = b.Timeline.BinarySymbol.FromData({id:-1, data:c}), q = p.symbolClass.initializeFrom(p); - p.symbolClass.instanceConstructorNoInitialize.call(q); - this._loader.loadBytes(q); + f.prototype.load = function(d, b) { + a(!this._loader, "Can't load twice."); + this._stage = new v.display.Stage; + var e = this._loaderInfo = (this._loader = v.display.Loader.getRootLoader()).contentLoaderInfo; + c.playAllSymbolsOption.value ? (this._playAllSymbols(), e._allowCodeExecution = !1) : this._enterRootLoadingLoop(); + e = this.createLoaderContext(); + if (b) { + var f = c.Timeline.BinarySymbol.FromData({id:-1, data:b}), h = f.symbolClass.initializeFrom(f); + f.symbolClass.instanceConstructorNoInitialize.call(h); + this._loader.loadBytes(h, e); } else { - this._loader.load(new f.net.URLRequest(a)); + this._loader.load(new v.net.URLRequest(d), e); + } + }; + f.prototype.createLoaderContext = function() { + var a = new v.system.LoaderContext; + if (this.movieParams) { + var b = {}, c; + for (c in this.movieParams) { + b.asSetPublicProperty(c, this.movieParams[c]); + } + a.parameters = b; } + return a; }; - l.prototype.processUpdates = function(a, e) { - var f = new b.Remoting.Player.PlayerChannelDeserializer; - f.input = a; - f.inputAssets = e; - f = f.read(); - switch(f.tag) { + f.prototype.processUpdates = function(a, b) { + var e = new c.Remoting.Player.PlayerChannelDeserializer; + e.input = a; + e.inputAssets = b; + e = e.read(); + switch(e.tag) { case 301: - var k = this._stage.focus ? this._stage.focus : this._stage; - this._keyboardEventDispatcher.target = k; - this._keyboardEventDispatcher.dispatchKeyboardEvent(f); + var f = this._stage.focus ? this._stage.focus : this._stage; + this._keyboardEventDispatcher.target = f; + this._keyboardEventDispatcher.dispatchKeyboardEvent(e); break; case 300: this._mouseEventDispatcher.stage = this._stage; - k = this._mouseEventDispatcher.handleMouseEvent(f); - b.traceMouseEventOption.value && this._writer.writeLn("Mouse Event: type: " + f.type + ", target: " + k + ", name: " + k._name); + f = this._mouseEventDispatcher.handleMouseEvent(e); + c.traceMouseEventOption.value && (this._writer.writeLn("Mouse Event: type: " + e.type + ", point: " + e.point + ", target: " + f + (f ? ", name: " + f._name : "")), "click" === e.type && f && f.debugTrace()); break; case 302: - switch(f.type) { + switch(e.type) { case 0: this._isPageVisible = !1; break; @@ -42584,187 +52460,188 @@ this._hasFocus = !1; break; case 3: - c.broadcastEventDispatchQueue.dispatchEvent(d.getBroadcastInstance(d.ACTIVATE)), this._hasFocus = !0; + m.broadcastEventDispatchQueue.dispatchEvent(l.getBroadcastInstance(l.ACTIVATE)), this._hasFocus = !0; } - break; - case 108: - var k = f.promiseId, l = this._pendingPromises[k]; - g(l, "We should be resolving an unresolved decode image promise at this point."); - l.resolve(f); - this._pendingPromises[k] = null; + ; } }; - l.prototype._enterLoops = function() { - this._enterEventLoop(); - }; - l.prototype._pumpDisplayListUpdates = function() { + f.prototype._pumpDisplayListUpdates = function() { this.syncDisplayObject(this._stage); }; - l.prototype.syncDisplayObject = function(a, c) { - "undefined" === typeof c && (c = !0); - var d = new t, e = [], g = new b.Remoting.Player.PlayerChannelSerializer; - g.output = d; - g.outputAssets = e; - f.display.Stage.isType(a) && g.writeStage(a); - g.begin(a); - g.remoteObjects(); - g.remoteReferences(); - d.writeInt(0); - k.enterTimeline("remoting assets"); - d = this.onSendUpdates(d, e, c); - k.leaveTimeline("remoting assets"); - return d; + f.prototype.syncDisplayObject = function(a, b) { + void 0 === b && (b = !0); + var e = new p, f = [], k = new c.Remoting.Player.PlayerChannelSerializer; + k.output = e; + k.outputAssets = f; + v.display.Stage.isType(a) && k.writeStage(a, this._mouseEventDispatcher.currentTarget); + k.begin(a); + k.remoteObjects(); + k.remoteReferences(); + e.writeInt(0); + h.enterTimeline("remoting assets"); + e = this.onSendUpdates(e, f, b); + h.leaveTimeline("remoting assets"); + return e; }; - l.prototype.requestBitmapData = function(a) { - var c = new t, d = [], e = new b.Remoting.Player.PlayerChannelSerializer; - e.output = c; - e.outputAssets = d; - e.writeRequestBitmapData(a); - c.writeInt(0); - return this.onSendUpdates(c, d, !1); - }; - l.prototype.decodeImage = function(a, c) { - g(5 === a.type || 6 === a.type || 4 === a.type, "No need to decode any other image formats."); - var d = new t, e = [], f = new b.Remoting.Player.PlayerChannelSerializer; - f.output = d; + f.prototype.requestBitmapData = function(a) { + var b = new p, e = [], f = new c.Remoting.Player.PlayerChannelSerializer; + f.output = b; f.outputAssets = e; - var k = this._getNextAvailablePromiseId(); - f.writeDecodeImage(k, a.type, a.data); - d.writeInt(0); - this.onSendUpdates(d, e); - (this._pendingPromises[k] = new b.PromiseWrapper).promise.then(function(b) { - a.data = b.data; - a.type = b.type; - a.width = b.width; - a.height = b.height; - c(void 0); - }); - }; - l.prototype.registerFont = function(a) { - var c = new t, d = [], e = new b.Remoting.Player.PlayerChannelSerializer; - e.output = c; - e.outputAssets = d; - e.writeFont(a); - this.onSendUpdates(c, d); - }; - l.prototype.drawToBitmap = function(a, c, d, e, g, l, m) { - "undefined" === typeof d && (d = null); - "undefined" === typeof e && (e = null); - "undefined" === typeof g && (g = null); - "undefined" === typeof l && (l = null); - "undefined" === typeof m && (m = !1); - var n = new t, p = [], q = new b.Remoting.Player.PlayerChannelSerializer; - q.output = n; - q.outputAssets = p; - q.writeBitmapData(a); - f.display.BitmapData.isType(c) ? q.writeBitmapData(c) : (q.begin(c), q.remoteObjects(), q.remoteReferences()); - q.writeDrawToBitmap(a, c, d, e, g, l, m); + f.writeRequestBitmapData(a); + b.writeInt(0); + return this.onSendUpdates(b, e, !1); + }; + f.prototype.drawToBitmap = function(a, b, e, f, k, l, m) { + void 0 === e && (e = null); + void 0 === f && (f = null); + void 0 === k && (k = null); + void 0 === l && (l = null); + void 0 === m && (m = !1); + var n = new p, q = [], s = new c.Remoting.Player.PlayerChannelSerializer; + s.output = n; + s.outputAssets = q; + s.writeBitmapData(a); + v.display.BitmapData.isType(b) ? s.writeBitmapData(b) : (s.begin(b), s.remoteObjects(), s.remoteReferences()); + s.writeDrawToBitmap(a, b, e, f, k, l, m); n.writeInt(0); - k.enterTimeline("sendUpdates"); - this.onSendUpdates(n, p, !1); - k.leaveTimeline("sendUpdates"); + h.enterTimeline("sendUpdates"); + this.onSendUpdates(n, q, !1); + h.leaveTimeline("sendUpdates"); }; - l.prototype.executeFSCommand = function(a, b) { + f.prototype.registerEventListener = function(a, b) { + this._videoEventListeners[a] = b; + }; + f.prototype.notifyVideoControl = function(a, b, c) { + return this.onVideoControl(a, b, c); + }; + f.prototype.executeFSCommand = function(a, b) { switch(a) { case "quit": this._leaveEventLoop(); + break; + default: + s("FSCommand " + a); } this.onFSCommand(a, b); }; - l.prototype.requestRendering = function() { + f.prototype.requestRendering = function() { this._pumpDisplayListUpdates(); }; - l.prototype._pumpUpdates = function() { - if (b.dontSkipFramesOption.value || !(this._shouldThrottleDownRendering() || performance.now() - this._lastPumpTime < 1E3 / b.pumpRateOption.value)) { - k.enterTimeline("pump"), b.pumpEnabledOption.value && (this._pumpDisplayListUpdates(), this._lastPumpTime = performance.now()), k.leaveTimeline("pump"); + f.prototype._pumpUpdates = function() { + if (c.dontSkipFramesOption.value || !(this._shouldThrottleDownRendering() || performance.now() - this._lastPumpTime < 1E3 / c.pumpRateOption.value)) { + h.enterTimeline("pump"), c.pumpEnabledOption.value && (this._pumpDisplayListUpdates(), this._lastPumpTime = performance.now()), h.leaveTimeline("pump"); } }; - l.prototype._leaveSyncLoop = function() { - g(-1 < this._frameTimeout); + f.prototype._leaveSyncLoop = function() { + a(-1 < this._frameTimeout); clearInterval(this._frameTimeout); }; - l.prototype._getFrameInterval = function() { - var a = b.frameRateOption.value; + f.prototype._getFrameInterval = function() { + var a = c.frameRateOption.value; 0 > a && (a = this._stage.frameRate); return Math.floor(1E3 / a); }; - l.prototype._enterEventLoop = function() { + f.prototype._enterEventLoop = function() { this._eventLoopIsRunning = !0; this._eventLoopTick = this._eventLoopTick.bind(this); this._eventLoopTick(); }; - l.prototype._eventLoopTick = function() { - var c = !b.playAllSymbolsOption.value, d = b.dontSkipFramesOption.value; + f.prototype._enterRootLoadingLoop = function() { + function a() { + var f = e.contentLoaderInfo; + if (f._file) { + var h = b._stage, k = void 0 !== b.defaultStageColor ? b.defaultStageColor : f._file.backgroundColor; + h._loaderInfo = f; + h.align = b.stageAlign || ""; + h.scaleMode = b.stageScale || "showall"; + h.frameRate = f.frameRate; + h.setStageWidth(f.width); + h.setStageHeight(f.height); + h.setStageColor(c.ColorUtilities.RGBAToARGB(k)); + b.displayParameters && b.processDisplayParameters(b.displayParameters); + b._enterEventLoop(); + } else { + setTimeout(a, b._getFrameInterval()); + } + } + var b = this, e = t.getRootLoader(); + e._setStage(this._stage); + a(); + }; + f.prototype._eventLoopTick = function() { + var a = !c.playAllSymbolsOption.value, b = c.dontSkipFramesOption.value; this._frameTimeout = setTimeout(this._eventLoopTick, this._getFrameInterval()); - if (d || !(!b.frameEnabledOption.value && c || this._shouldThrottleDownFrameExecution())) { - this._stage.scaleX = this._stage.scaleY = b.stageScaleOption.value; - a._stage = this._stage; - for (d = 0;d < b.frameRateMultiplierOption.value;d++) { - k.enterTimeline("eventLoop"); - var e = performance.now(); - a.performFrameNavigation(!0, c); - k.counter.count("performFrameNavigation", 1, performance.now() - e); - this._framesPlayed++; - p.progress(); - 0 < b.tracePlayerOption.value && 0 === this._framesPlayed % b.tracePlayerOption.value && this._tracePlayer(); - k.leaveTimeline("eventLoop"); + if (b || !(!c.frameEnabledOption.value && a || this._shouldThrottleDownFrameExecution())) { + e._stage = this._stage; + if (!t.getRootLoader().content && (t.processEvents(), !t.getRootLoader().content)) { + return; } - this._rootInitialized ? this._stage.render() : this._rootInitialized = !0; + for (b = 0;b < c.frameRateMultiplierOption.value;b++) { + h.enterTimeline("eventLoop"); + var f = performance.now(); + e.performFrameNavigation(!0, a); + h.counter.count("performFrameNavigation", 1, performance.now() - f); + t.processEvents(); + h.leaveTimeline("eventLoop"); + } + this._framesPlayed++; + 0 < c.tracePlayerOption.value && 0 === this._framesPlayed % c.tracePlayerOption.value && this._tracePlayer(); + this._stage.render(); this._pumpUpdates(); this.onFrameProcessed(); } }; - l.prototype._tracePlayer = function() { + f.prototype._tracePlayer = function() { var a = this._writer; a.enter("Frame: " + this._framesPlayed); - b.AVM2.counter.traceSorted(a); - b.AVM2.counter.clear(); - b.Player.counter.traceSorted(a); - b.Player.counter.clear(); - a.writeLn("advancableInstances: " + f.display.DisplayObject._advancableInstances.length); + c.AVM2.counter.traceSorted(a); + c.AVM2.counter.clear(); + c.Player.counter.traceSorted(a); + c.Player.counter.clear(); + a.writeLn("advancableInstances: " + v.display.DisplayObject._advancableInstances.length); a.outdent(); }; - l.prototype._leaveEventLoop = function() { - g(this._eventLoopIsRunning); + f.prototype._leaveEventLoop = function() { + a(this._eventLoopIsRunning); clearTimeout(this._frameTimeout); this._eventLoopIsRunning = !1; }; - l.prototype._playAllSymbols = function() { - var a = this._stage, c = this._loader, d = this._loaderInfo, e = this; - d.addEventListener(f.events.ProgressEvent.PROGRESS, function G() { - c.content && (d.removeEventListener(f.events.ProgressEvent.PROGRESS, G), e._enterLoops()); + f.prototype._playAllSymbols = function() { + var a = this._stage, b = this._loader, e = this._loaderInfo, f = this; + e.addEventListener(v.events.ProgressEvent.PROGRESS, function z() { + b.content && (e.removeEventListener(v.events.ProgressEvent.PROGRESS, z), f._enterEventLoop()); }); - d.addEventListener(f.events.Event.COMPLETE, function() { - function c() { + e.addEventListener(v.events.Event.COMPLETE, function() { + function b() { var l; - 0 < b.playSymbolOption.value ? (l = d.getSymbolById(b.playSymbolOption.value), l instanceof b.Timeline.DisplaySymbol || (l = null)) : (l = g[k++], k === g.length && (k = 0), 0 <= b.playSymbolCountOption.value && k > b.playSymbolCountOption.value && (k = 0)); + 0 < c.playSymbolOption.value ? (l = e.getSymbolById(c.playSymbolOption.value), l instanceof c.Timeline.DisplaySymbol || (l = null)) : (l = h[k++], k === h.length && (k = 0), 0 <= c.playSymbolCountOption.value && k > c.playSymbolCountOption.value && (k = 0)); var m = 1; if (l && 0 < l.id) { var n = l; - f.display.DisplayObject.reset(); - f.display.MovieClip.reset(); + v.display.DisplayObject.reset(); + v.display.MovieClip.reset(); var p = n.symbolClass.initializeFrom(n); n.symbolClass.instanceConstructorNoInitialize.call(p); - for (n instanceof b.Timeline.BitmapSymbol && (p = new f.display.Bitmap(p));0 < a.numChildren;) { + for (n instanceof v.display.BitmapSymbol && (p = new v.display.Bitmap(p));0 < a.numChildren;) { a.removeChildAt(0); } a.addChild(p); - l instanceof b.Timeline.SpriteSymbol && (m = l.numFrames); + l instanceof v.display.SpriteSymbol && (m = l.numFrames); } - 0 < b.playSymbolFrameDurationOption.value && (m = b.playSymbolFrameDurationOption.value); - setTimeout(c, e._getFrameInterval() * m); + 0 < c.playSymbolFrameDurationOption.value && (m = c.playSymbolFrameDurationOption.value); + setTimeout(b, f._getFrameInterval() * m); } a.setStageWidth(1024); a.setStageHeight(1024); - var g = []; - d._dictionary.forEach(function(a, c) { - a instanceof b.Timeline.DisplaySymbol && g.push(a); + var h = []; + e._dictionary.forEach(function(a, b) { + a instanceof c.Timeline.DisplaySymbol && h.push(a); }); var k = 0; - setTimeout(c, e._getFrameInterval()); + setTimeout(b, f._getFrameInterval()); }); }; - l.prototype.processExternalCallback = function(a) { + f.prototype.processExternalCallback = function(a) { if (this.externalCallback) { try { a.result = this.externalCallback(a.functionName, a.args); @@ -42773,16 +52650,36 @@ } } }; - l.prototype.onExternalCommand = function(a) { + f.prototype.processVideoEvent = function(a, b, e) { + a = this._videoEventListeners[a]; + c.Debug.assert(a, "Video event listener is not found"); + a(b, e); + }; + f.prototype.processDisplayParameters = function(a) { + this._stage.setStageContainerSize(a.stageWidth, a.stageHeight, a.pixelRatio); + }; + f.prototype.onExternalCommand = function(a) { + throw Error("This method is abstract"); + }; + f.prototype.onFSCommand = function(a, b) { + throw Error("This method is abstract"); + }; + f.prototype.onVideoControl = function(a, b, c) { throw Error("This method is abstract"); }; - l.prototype.onFSCommand = function(a, b) { + f.prototype.onFrameProcessed = function() { throw Error("This method is abstract"); }; - l.prototype.onFrameProcessed = function() { + f.prototype.registerFontOrImage = function(d, b) { + a(d.syncId); + d.resolveAssetPromise = new c.PromiseWrapper; + this.registerFontOrImageImpl(d, b); + "font" === b.type && inFirefox ? d.ready = !0 : d.resolveAssetPromise.then(d.resolveAssetCallback, null); + }; + f.prototype.registerFontOrImageImpl = function(a, b) { throw Error("This method is abstract"); }; - l.prototype.createExternalInterfaceService = function() { + f.prototype.createExternalInterfaceService = function() { var a, b = this; return{get enabled() { if (void 0 === a) { @@ -42812,58 +52709,52 @@ return a.result; }}; }; - return l; + return f; }(); - k.Player = l; - })(b.Player || (b.Player = {})); + h.Player = k; + })(c.Player || (c.Player = {})); })(Shumway || (Shumway = {})); -(function(b) { - var k = b.BinaryFileReader, g = b.AVM2.ABC.AbcFile, f = b.AVM2.Runtime.AVM2, t = b.Debug.assert; - b.createAVM2 = function(s, m, d, a, c, n) { - function p(a) { - (new k(d)).readAll(function(b) { - e.systemDomain.executeAbc(new g(new Uint8Array(b), d)); - a(); - }); - } - var e; - t(s); - (new k(s)).readAll(function(q) { - f.initialize(a, c, d ? p : null); - e = f.instance; - b.AVM2.AS.linkNatives(e); +(function(c) { + var h = c.BinaryFileReader, a = c.AVM2.ABC.AbcFile, s = c.AVM2.Runtime.AVM2, v = c.Debug.assert; + c.createAVM2 = function(p, u, l, e, m) { + var t; + v(p); + (new h(p)).readAll(function(p) { + s.initialize(l, e); + t = s.instance; + c.AVM2.AS.linkNatives(t); console.time("Execute builtin.abc"); - e.builtinsLoaded = !1; - e.systemDomain.executeAbc(new g(new Uint8Array(q), "builtin.abc")); - e.builtinsLoaded = !0; + t.builtinsLoaded = !1; + t.systemDomain.executeAbc(new a(new Uint8Array(p), "builtin.abc")); + t.builtinsLoaded = !0; console.timeEnd("Execute builtin.abc"); - "string" === typeof m ? (new k(m)).readAll(function(a) { - e.systemDomain.executeAbc(new g(new Uint8Array(a), m)); - n(e); - }) : f.isPlayerglobalLoaded() || f.loadPlayerglobal(m.abcs, m.catalog).then(function() { - n(e); + "string" === typeof u ? (new h(u)).readAll(function(c) { + t.systemDomain.executeAbc(new a(new Uint8Array(c), u)); + m(t); + }) : s.isPlayerglobalLoaded() || s.loadPlayerglobal(u.abcs, u.catalog).then(function() { + m(t); }); }); }; })(Shumway || (Shumway = {})); -__extends = this.__extends || function(b, k) { - function g() { - this.constructor = b; +__extends = this.__extends || function(c, h) { + function a() { + this.constructor = c; } - for (var f in k) { - k.hasOwnProperty(f) && (b[f] = k[f]); + for (var s in h) { + h.hasOwnProperty(s) && (c[s] = h[s]); } - g.prototype = k.prototype; - b.prototype = new g; + a.prototype = h.prototype; + c.prototype = new a; }; -(function(b) { - (function(k) { - (function(g) { - var f = b.ArrayUtilities.DataBuffer, k = function(g) { - function k(b, a) { - g.call(this); - this._window = b; - this._parent = a || b.parent; +(function(c) { + (function(h) { + (function(a) { + var h = c.ArrayUtilities.DataBuffer, v = function(a) { + function u(c, e) { + a.call(this); + this._window = c; + this._parent = e || c.parent; this._window.addEventListener("message", function(a) { this.onWindowMessage(a.data, !0); }.bind(this)); @@ -42871,168 +52762,215 @@ this.onWindowMessage(a.detail, !1); }.bind(this)); } - __extends(k, g); - k.prototype.onSendUpdates = function(b, a, c) { - "undefined" === typeof c && (c = !0); - b = b.getBytes(); - a = {type:"player", updates:b, assets:a, result:void 0}; - b = [b.buffer]; - if (!c) { - return c = this._parent.document.createEvent("CustomEvent"), c.initCustomEvent("syncmessage", !1, !1, a), this._parent.dispatchEvent(c), f.FromPlainObject(a.result); + __extends(u, a); + u.prototype.onSendUpdates = function(a, c, m) { + void 0 === m && (m = !0); + a = a.getBytes(); + c = {type:"player", updates:a, assets:c, result:void 0}; + a = [a.buffer]; + if (!m) { + return m = this._parent.document.createEvent("CustomEvent"), m.initCustomEvent("syncmessage", !1, !1, c), this._parent.dispatchEvent(m), h.FromPlainObject(c.result); } - this._parent.postMessage(a, "*", b); + this._parent.postMessage(c, "*", a); return null; }; - k.prototype.onExternalCommand = function(b) { - var a = this._parent.document.createEvent("CustomEvent"); - a.initCustomEvent("syncmessage", !1, !1, {type:"external", request:b}); - this._parent.dispatchEvent(a); - }; - k.prototype.onFSCommand = function(b, a) { - this._parent.postMessage({type:"fscommand", command:b, args:a}, "*"); + u.prototype.onExternalCommand = function(a) { + var c = this._parent.document.createEvent("CustomEvent"); + c.initCustomEvent("syncmessage", !1, !1, {type:"external", request:a}); + this._parent.dispatchEvent(c); + }; + u.prototype.onFSCommand = function(a, c) { + this._parent.postMessage({type:"fscommand", command:a, args:c}, "*"); + }; + u.prototype.onVideoControl = function(a, c, h) { + var p = this._parent.document.createEvent("CustomEvent"); + p.initCustomEvent("syncmessage", !1, !1, {type:"videoControl", id:a, eventType:c, data:h, result:void 0}); + this._parent.dispatchEvent(p); + return p.detail.result; }; - k.prototype.onFrameProcessed = function() { + u.prototype.onFrameProcessed = function() { this._parent.postMessage({type:"frame"}, "*"); }; - k.prototype.onWindowMessage = function(d, a) { - if ("object" === typeof d && null !== d) { - switch(d.type) { + u.prototype.registerFontOrImageImpl = function(a, c) { + var h = this._parent.document.createEvent("CustomEvent"); + h.initCustomEvent("syncmessage", !1, !1, {type:"registerFontOrImage", syncId:a.syncId, symbolId:a.id, assetType:c.type, data:c, resolve:a.resolveAssetPromise.resolve}); + this._parent.dispatchEvent(h); + }; + u.prototype.onWindowMessage = function(a, e) { + if ("object" === typeof a && null !== a) { + switch(a.type) { case "gfx": - var c = b.ArrayUtilities.DataBuffer.FromArrayBuffer(d.updates.buffer); - this.processUpdates(c, d.assets); + var h = c.ArrayUtilities.DataBuffer.FromArrayBuffer(a.updates.buffer); + this.processUpdates(h, a.assets); break; case "externalCallback": - this.processExternalCallback(d.request); + this.processExternalCallback(a.request); + break; + case "videoPlayback": + this.processVideoEvent(a.id, a.eventType, a.data); + break; + case "displayParameters": + this.processDisplayParameters(a.params); break; case "options": - b.Settings.setSettings(d.settings); + c.Settings.setSettings(a.settings); break; case "timeline": - switch(d.request) { + switch(a.request) { case "AVM2": - if ("clear" === d.cmd) { - b.AVM2.timelineBuffer.reset(); + if ("clear" === a.cmd) { + c.AVM2.timelineBuffer.reset(); break; } - this._parent.postMessage({type:"timelineResponse", request:d.request, timeline:b.AVM2.timelineBuffer}, "*"); + this._parent.postMessage({type:"timelineResponse", request:a.request, timeline:c.AVM2.timelineBuffer}, "*"); break; case "Player": - if ("clear" === d.cmd) { - b.Player.timelineBuffer.reset(); + if ("clear" === a.cmd) { + c.Player.timelineBuffer.reset(); break; } - this._parent.postMessage({type:"timelineResponse", request:d.request, timeline:b.Player.timelineBuffer}, "*"); + this._parent.postMessage({type:"timelineResponse", request:a.request, timeline:c.Player.timelineBuffer}, "*"); break; case "SWF": - if ("clear" === d.cmd) { - b.SWF.timelineBuffer.reset(); + if ("clear" === a.cmd) { + c.SWF.timelineBuffer.reset(); break; } - this._parent.postMessage({type:"timelineResponse", request:d.request, timeline:b.SWF.timelineBuffer}, "*"); + this._parent.postMessage({type:"timelineResponse", request:a.request, timeline:c.SWF.timelineBuffer}, "*"); } ; } } }; - return k; - }(b.Player.Player); - g.WindowPlayer = k; - })(k.Window || (k.Window = {})); - })(b.Player || (b.Player = {})); -})(Shumway || (Shumway = {})); -(function(b) { - (function(k) { - (function(g) { - var f = b.ArrayUtilities.DataBuffer, k = function(g) { - function k() { - g.call(this); - this._worker = b.Player.Test.FakeSyncWorker.instance; + return u; + }(c.Player.Player); + a.WindowPlayer = v; + })(h.Window || (h.Window = {})); + })(c.Player || (c.Player = {})); +})(Shumway || (Shumway = {})); +(function(c) { + (function(h) { + (function(a) { + var h = c.ArrayUtilities.DataBuffer, v = function(a) { + function u() { + a.call(this); + this._worker = c.Player.Test.FakeSyncWorker.instance; this._worker.addEventListener("message", this._onWorkerMessage.bind(this)); this._worker.addEventListener("syncmessage", this._onSyncWorkerMessage.bind(this)); } - __extends(k, g); - k.prototype.onSendUpdates = function(b, a, c) { - "undefined" === typeof c && (c = !0); - b = b.getBytes(); - a = {type:"player", updates:b, assets:a}; - b = [b.buffer]; - if (!c) { - return c = this._worker.postSyncMessage(a, b), f.FromPlainObject(c); + __extends(u, a); + u.prototype.onSendUpdates = function(a, c, m) { + void 0 === m && (m = !0); + a = a.getBytes(); + c = {type:"player", updates:a, assets:c}; + a = [a.buffer]; + if (!m) { + return m = this._worker.postSyncMessage(c, a), h.FromPlainObject(m); } - this._worker.postMessage(a, b); + this._worker.postMessage(c, a); return null; }; - k.prototype.onExternalCommand = function(b) { - this._worker.postSyncMessage({type:"external", command:b}); + u.prototype.onExternalCommand = function(a) { + this._worker.postSyncMessage({type:"external", command:a}); + }; + u.prototype.onFSCommand = function(a, c) { + this._worker.postMessage({type:"fscommand", command:a, args:c}); }; - k.prototype.onFSCommand = function(b, a) { - this._worker.postMessage({type:"fscommand", command:b, args:a}); + u.prototype.onVideoControl = function(a, c, h) { + return this._worker.postSyncMessage({type:"videoControl", id:a, eventType:c, data:h}); }; - k.prototype.onFrameProcessed = function() { + u.prototype.onFrameProcessed = function() { this._worker.postMessage({type:"frame"}); }; - k.prototype._onWorkerMessage = function(b) { - var a = b.data; - if ("object" === typeof a && null !== a) { - switch(a.type) { + u.prototype.registerFontOrImageImpl = function(a, c) { + return this._worker.postSyncMessage({type:"registerFontOrImage", syncId:a.syncId, symbolId:a.id, assetType:c.type, data:c, resolve:a.resolveAssetPromise.resolve}); + }; + u.prototype._onWorkerMessage = function(a) { + var c = a.data; + if ("object" === typeof c && null !== c) { + switch(c.type) { case "gfx": - a = f.FromArrayBuffer(b.data.updates.buffer); - this.processUpdates(a, b.data.assets); + c = h.FromArrayBuffer(a.data.updates.buffer); + this.processUpdates(c, a.data.assets); break; case "externalCallback": - this.processExternalCallback(a.request), b.handled = !0; + this.processExternalCallback(c.request); + a.handled = !0; + break; + case "videoPlayback": + this.processVideoEvent(c.id, c.eventType, c.data); + break; + case "displayParameters": + this.processDisplayParameters(c.params); } } }; - k.prototype._onSyncWorkerMessage = function(b) { - return this._onWorkerMessage(b); + u.prototype._onSyncWorkerMessage = function(a) { + return this._onWorkerMessage(a); }; - return k; - }(b.Player.Player); - g.TestPlayer = k; - })(k.Test || (k.Test = {})); - })(b.Player || (b.Player = {})); + return u; + }(c.Player.Player); + a.TestPlayer = v; + })(h.Test || (h.Test = {})); + })(c.Player || (c.Player = {})); })(Shumway || (Shumway = {})); -(function(b) { - (function(b) { - (function(b) { - var f = function() { - function b() { - this._worker = new Worker(b.WORKER_PATH); +(function(c) { + (function(h) { + (function(a) { + var h = function() { + function a() { + this._onmessageListeners = []; this._onsyncmessageListeners = []; } - Object.defineProperty(b, "instance", {get:function() { - b._singelton || (b._singelton = new b); - return b._singelton; + Object.defineProperty(a, "instance", {get:function() { + a._singelton || (a._singelton = new a); + return a._singelton; }, enumerable:!0, configurable:!0}); - b.prototype.addEventListener = function(b, f, d) { - "syncmessage" !== b ? this._worker.addEventListener(b, f, d) : this._onsyncmessageListeners.push(f); + a.prototype.addEventListener = function(a, h, l) { + c.Debug.assert("syncmessage" === a || "message" === a); + "syncmessage" !== a ? this._onmessageListeners.push(h) : this._onsyncmessageListeners.push(h); }; - b.prototype.removeEventListener = function(b, f, d) { - "syncmessage" === b ? (b = this._onsyncmessageListeners.indexOf(f), 0 <= b && this._onsyncmessageListeners.splice(b, 1)) : this._worker.removeEventListener(b, f, d); + a.prototype.removeEventListener = function(a, c, h) { + "syncmessage" === a ? (a = this._onsyncmessageListeners.indexOf(c), 0 <= a && this._onsyncmessageListeners.splice(a, 1)) : (a = this._onmessageListeners.indexOf(c), 0 <= a && this._onmessageListeners.splice(a, 1)); }; - b.prototype.postMessage = function(b, f) { - this._worker.postMessage(b, f); + a.prototype.postMessage = function(a, h) { + var l; + this._onmessageListeners.some(function(e) { + var h = {data:a, result:void 0, handled:!1}; + try { + if ("function" === typeof e ? e(h) : e.handleEvent(h), !h.handled) { + return!1; + } + } catch (s) { + c.Debug.warning("Failure at postMessage: " + s.message); + } + l = h.result; + return!0; + }); + return l; }; - b.prototype.postSyncMessage = function(b, f) { - var d; - this._onsyncmessageListeners.some(function(a) { - var c = {data:b, result:void 0, handled:!1}; - "function" === typeof a ? a(c) : a.handleEvent(c); - if (!c.handled) { - return!1; + a.prototype.postSyncMessage = function(a, h) { + var l; + this._onsyncmessageListeners.some(function(e) { + var h = {data:a, result:void 0, handled:!1}; + try { + if ("function" === typeof e ? e(h) : e.handleEvent(h), !h.handled) { + return!1; + } + } catch (s) { + c.Debug.warning("Failure at postSyncMessage: " + s.message); } - d = c.result; + l = h.result; return!0; }); - return d; + return l; }; - b.WORKER_PATH = "../../src/player/fakechannel.js"; - return b; + a.WORKER_PATH = "../../src/player/fakechannel.js"; + return a; }(); - b.FakeSyncWorker = f; - })(b.Test || (b.Test = {})); - })(b.Player || (b.Player = {})); + a.FakeSyncWorker = h; + })(h.Test || (h.Test = {})); + })(c.Player || (c.Player = {})); })(Shumway || (Shumway = {})); console.timeEnd("Load Player Dependencies"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/ShumwayStreamConverter.jsm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/ShumwayStreamConverter.jsm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/ShumwayStreamConverter.jsm 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/ShumwayStreamConverter.jsm 2015-02-03 14:33:22.000000000 +0000 @@ -184,9 +184,17 @@ xhr.send(null); } +function isContentWindowPrivate(win) { + if (!('isContentWindowPrivate' in PrivateBrowsingUtils)) { + return PrivateBrowsingUtils.isWindowPrivate(win); + } + return PrivateBrowsingUtils.isContentWindowPrivate(win); +} + function isShumwayEnabledFor(actions) { // disabled for PrivateBrowsing windows - if (PrivateBrowsingUtils.isWindowPrivate(actions.window)) { + if (isContentWindowPrivate(actions.window) && + !getBoolPref('shumway.enableForPrivate', false)) { return false; } // disabled if embed tag specifies shumwaymode (for testing purpose) @@ -212,13 +220,15 @@ function getVersionInfo() { var deferred = Promise.defer(); var versionInfo = { - geckoMstone : 'unknown', + version: 'unknown', geckoBuildID: 'unknown', shumwayVersion: 'unknown' }; try { - versionInfo.geckoMstone = Services.prefs.getCharPref('gecko.mstone'); - versionInfo.geckoBuildID = Services.prefs.getCharPref('gecko.buildID'); + var appInfo = Components.classes["@mozilla.org/xre/app-info;1"] + .getService(Components.interfaces.nsIXULAppInfo); + versionInfo.geckoVersion = appInfo.version; + versionInfo.geckoBuildID = appInfo.appBuildID; } catch (e) { log('Error encountered while getting platform version info:', e); } @@ -275,11 +285,11 @@ return getBoolPref(data.pref, data.def); }, getCompilerSettings: function getCompilerSettings() { - return JSON.stringify({ + return { appCompiler: getBoolPref('shumway.appCompiler', true), sysCompiler: getBoolPref('shumway.sysCompiler', false), verifier: getBoolPref('shumway.verifier', true) - }); + }; }, addProfilerMarker: function (marker) { if ('nsIProfiler' in Ci) { @@ -288,14 +298,14 @@ } }, getPluginParams: function getPluginParams() { - return JSON.stringify({ + return { url: this.url, baseUrl : this.baseUrl, movieParams: this.movieParams, objectParams: this.objectParams, isOverlay: this.isOverlay, isPausedAtStart: this.isPausedAtStart - }); + }; }, _canDownloadFile: function canDownloadFile(data, callback) { var url = data.url, checkPolicyFile = data.checkPolicyFile; @@ -352,6 +362,13 @@ }.bind(this)); }, loadFile: function loadFile(data) { + function notifyLoadFileListener(data) { + if (!win.wrappedJSObject.onLoadFileCallback) { + return; + } + win.wrappedJSObject.onLoadFileCallback(data); + } + var url = data.url; var checkPolicyFile = data.checkPolicyFile; var sessionId = data.sessionId; @@ -381,8 +398,8 @@ xhr.onprogress = function (e) { var position = e.loaded; var data = new Uint8Array(xhr.response); - win.postMessage({callback:"loadFile", sessionId: sessionId, topic: "progress", - array: data, loaded: e.loaded, total: e.total}, "*"); + notifyLoadFileListener({callback:"loadFile", sessionId: sessionId, + topic: "progress", array: data, loaded: e.loaded, total: e.total}); lastPosition = position; if (limit && e.total >= limit) { xhr.abort(); @@ -391,16 +408,15 @@ xhr.onreadystatechange = function(event) { if (xhr.readyState === 4) { if (xhr.status !== 200 && xhr.status !== 0) { - win.postMessage({callback:"loadFile", sessionId: sessionId, topic: "error", - error: xhr.statusText}, "*"); + notifyLoadFileListener({callback:"loadFile", sessionId: sessionId, topic: "error", error: xhr.statusText}); } - win.postMessage({callback:"loadFile", sessionId: sessionId, topic: "close"}, "*"); + notifyLoadFileListener({callback:"loadFile", sessionId: sessionId, topic: "close"}); } }; if (mimeType) xhr.setRequestHeader("Content-Type", mimeType); xhr.send(postData); - win.postMessage({callback:"loadFile", sessionId: sessionId, topic: "open"}, "*"); + notifyLoadFileListener({callback:"loadFile", sessionId: sessionId, topic: "open"}); }; this._canDownloadFile({url: url, checkPolicyFile: checkPolicyFile}, function (data) { @@ -408,28 +424,65 @@ performXHR(); } else { log("data access id prohibited to " + url + " from " + baseUrl); - win.postMessage({callback:"loadFile", sessionId: sessionId, topic: "error", - error: "only original swf file or file from the same origin loading supported"}, "*"); + notifyLoadFileListener({callback:"loadFile", sessionId: sessionId, topic: "error", + error: "only original swf file or file from the same origin loading supported"}); } }); }, + navigateTo: function (data) { + var embedTag = this.embedTag.wrappedJSObject; + var window = embedTag ? embedTag.ownerDocument.defaultView : this.window; + window.open(data.url, data.target || '_self'); + }, fallback: function(automatic) { automatic = !!automatic; fallbackToNativePlugin(this.window, !automatic, automatic); }, - setClipboard: function (data) { + userInput: function() { + var win = this.window; + var winUtils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor). + getInterface(Components.interfaces.nsIDOMWindowUtils); + if (winUtils.isHandlingUserInput) { + this.lastUserInput = Date.now(); + } + }, + isUserInputInProgress: function () { + // TODO userInput does not work for OOP + if (!getBoolPref('shumway.userInputSecurity', true)) { + return true; + } + // We don't trust our Shumway non-privileged code just yet to verify the - // user input -- using monitorUserInput function below to track that. - if (typeof data !== 'string' || - (Date.now() - this.lastUserInput) > MAX_USER_INPUT_TIMEOUT) { - return; + // user input -- using userInput function above to track that. + if ((Date.now() - this.lastUserInput) > MAX_USER_INPUT_TIMEOUT) { + return false; } // TODO other security checks? + return true; + }, + setClipboard: function (data) { + if (typeof data !== 'string' || !this.isUserInputInProgress()) { + return; + } let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"] .getService(Ci.nsIClipboardHelper); clipboard.copyString(data); }, + setFullscreen: function (enabled) { + enabled = !!enabled; + + if (!this.isUserInputInProgress()) { + return; + } + + var target = this.embedTag || this.document.body; + if (enabled) { + target.mozRequestFullScreen(); + } else { + target.ownerDocument.mozCancelFullScreen(); + } + }, endActivation: function () { if (ActivationQueue.currentNonActive === this) { ActivationQueue.activateNext(); @@ -487,20 +540,15 @@ getVersionInfo().then(function (versions) { params.versions = versions; }).then(function () { - params.ffbuild = encodeURIComponent(params.versions.geckoMstone + - ' (' + params.versions.geckoBuildID + ')'); - params.shubuild = encodeURIComponent(params.versions.shumwayVersion); - params.exceptions = encodeURIComponent(exceptions); - var comment = '%2B%2B%2B This bug was initially via the problem reporting functionality in ' + - 'Shumway %2B%2B%2B%0A%0A' + - 'Please add any further information that you deem helpful here:%0A%0A%0A' + - '----------------------%0A%0A' + - 'Technical Information:%0A' + - 'Firefox version: ' + params.ffbuild + '%0A' + - 'Shumway version: ' + params.shubuild; - url = url.split('{comment}').join(comment); - //this.window.openDialog('chrome://browser/content', '_blank', 'all,dialog=no', url); - dump(111); + var ffbuild = params.versions.geckoVersion + ' (' + params.versions.geckoBuildID + ')'; + //params.exceptions = encodeURIComponent(exceptions); + var comment = '+++ Initially filed via the problem reporting functionality in Shumway +++\n' + + 'Please add any further information that you deem helpful here:\n\n\n\n' + + '----------------------\n\n' + + 'Technical Information:\n' + + 'Firefox version: ' + ffbuild + '\n' + + 'Shumway version: ' + params.versions.shumwayVersion; + url = url.split('{comment}').join(encodeURIComponent(comment)); this.window.open(url); }.bind(this)); }, @@ -517,8 +565,7 @@ return; this.externalComInitialized = true; - var eventTarget = this.window.document; - initExternalCom(parentWindow, embedTag, eventTarget); + initExternalCom(parentWindow, embedTag, this.window); return; case 'getId': return embedTag.id; @@ -537,33 +584,15 @@ } }; -function monitorUserInput(actions) { - function notifyUserInput() { - var win = actions.window; - var winUtils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor). - getInterface(Components.interfaces.nsIDOMWindowUtils); - if (winUtils.isHandlingUserInput) { - actions.lastUserInput = Date.now(); - } - } - - var document = actions.document; - document.addEventListener('mousedown', notifyUserInput, false); - document.addEventListener('mouseup', notifyUserInput, false); - document.addEventListener('keydown', notifyUserInput, false); - document.addEventListener('keyup', notifyUserInput, false); -} - // Event listener to trigger chrome privedged code. function RequestListener(actions) { this.actions = actions; } // Receive an event and synchronously or asynchronously responds. -RequestListener.prototype.receive = function(event) { - var message = event.target; - var action = event.detail.action; - var data = event.detail.data; - var sync = event.detail.sync; +RequestListener.prototype.receive = function(detail) { + var action = detail.action; + var data = detail.data; + var sync = detail.sync; var actions = this.actions; if (!(action in actions)) { log('Unknown action: ' + action); @@ -571,30 +600,23 @@ } if (sync) { var response = actions[action].call(this.actions, data); - event.detail.response = response; - } else { - var response; - if (event.detail.callback) { - var cookie = event.detail.cookie; - response = function sendResponse(response) { - var doc = actions.document; - try { - var listener = doc.createEvent('CustomEvent'); - listener.initCustomEvent('shumway.response', true, false, - makeContentReadable({ - response: response, - cookie: cookie - }, doc.defaultView)); - - return message.dispatchEvent(listener); - } catch (e) { - // doc is no longer accessible because the requestor is already - // gone. unloaded content cannot receive the response anyway. - } - }; - } - actions[action].call(this.actions, data, response); + return response === undefined ? undefined : JSON.stringify(response); } + + var responseCallback; + if (detail.callback) { + var cookie = detail.cookie; + response = function sendResponse(response) { + var win = actions.window; + if (win.wrappedJSObject.onMessageCallback) { + win.wrappedJSObject.onMessageCallback({ + response: response === undefined ? undefined : JSON.stringify(response), + cookie: cookie + }); + } + }; + } + actions[action].call(this.actions, data, responseCallback); }; var ActivationQueue = { @@ -696,7 +718,7 @@ } }; -function activateShumwayScripts(window, preview) { +function activateShumwayScripts(window, requestListener) { function loadScripts(scripts, callback) { function loadScript(i) { if (i >= scripts.length) { @@ -717,14 +739,12 @@ } function initScripts() { - loadScripts(['resource://shumway/shumway.gfx.js', - 'resource://shumway/web/viewer.js'], function () { - window.wrappedJSObject.runViewer(); - }); + window.wrappedJSObject.notifyShumwayMessage = function () { + return requestListener.receive.apply(requestListener, arguments); + }; + window.wrappedJSObject.runViewer(); } - window.wrappedJSObject.SHUMWAY_ROOT = "resource://shumway/"; - if (window.document.readyState === "interactive" || window.document.readyState === "complete") { initScripts(); @@ -733,7 +753,7 @@ } } -function initExternalCom(wrappedWindow, wrappedObject, targetDocument) { +function initExternalCom(wrappedWindow, wrappedObject, targetWindow) { if (!wrappedWindow.__flash__initialized) { wrappedWindow.__flash__initialized = true; wrappedWindow.__flash__toXML = function __flash__toXML(obj) { @@ -777,18 +797,15 @@ } wrappedObject.__flash__registerCallback = function (functionName) { wrappedWindow.console.log('__flash__registerCallback: ' + functionName); - this[functionName] = function () { + Components.utils.exportFunction(function () { var args = Array.prototype.slice.call(arguments, 0); wrappedWindow.console.log('__flash__callIn: ' + functionName); - var e = targetDocument.createEvent('CustomEvent'); - e.initCustomEvent('shumway.remote', true, false, makeContentReadable({ - functionName: functionName, - args: args, - result: undefined - }, targetDocument.defaultView)); - targetDocument.dispatchEvent(e); - return e.detail.result; - }; + var result; + if (targetWindow.wrappedJSObject.onExternalCallback) { + result = targetWindow.wrappedJSObject.onExternalCallback({functionName: functionName, args: args}); + } + return wrappedWindow.eval(result); + }, this, { defineAs: functionName }); }; wrappedObject.__flash__unregisterCallback = function (functionName) { wrappedWindow.console.log('__flash__unregisterCallback: ' + functionName); @@ -849,6 +866,12 @@ } if (isOverlay) { + // HACK For Facebook, CSS embed tag rescaling -- iframe (our overlay) + // has no styling in document. Shall removed with jsplugins. + for (var child = window.frameElement; child !== element; child = child.parentNode) { + child.setAttribute('style', 'max-width: 100%; max-height: 100%'); + } + // Checking if overlay is a proper PlayPreview overlay. for (var i = 0; i < element.children.length; i++) { if (element.children[i] === containerElement) { @@ -860,7 +883,7 @@ if (element) { // Getting absolute URL from the EMBED tag - url = element.srcURI.spec; + url = element.srcURI && element.srcURI.spec; pageUrl = element.ownerDocument.location.href; // proper page url? @@ -961,14 +984,8 @@ var originalURI = aRequest.URI; - // checking if the plug-in shall be run in simple mode - var isSimpleMode = originalURI.spec === EXPECTED_PLAYPREVIEW_URI_PREFIX && - getBoolPref('shumway.simpleMode', false); - - // Create a new channel that loads the viewer as a resource. - var viewerUrl = isSimpleMode ? - 'resource://shumway/web/simple.html' : - 'resource://shumway/web/viewer.html'; + // Create a new channel that loads the viewer as a chrome resource. + var viewerUrl = 'chrome://shumway/content/viewer.wrapper.html'; var channel = Services.io.newChannel(viewerUrl, null, null); var converter = this; @@ -1008,18 +1025,14 @@ ShumwayTelemetry.onPageIndex(0); } - actions.activationCallback = function(domWindow, isSimpleMode) { + let requestListener = new RequestListener(actions); + + actions.activationCallback = function(domWindow, requestListener) { delete this.activationCallback; - activateShumwayScripts(domWindow, isSimpleMode); - }.bind(actions, domWindow, isSimpleMode); + activateShumwayScripts(domWindow, requestListener); + }.bind(actions, domWindow, requestListener); ActivationQueue.enqueue(actions); - let requestListener = new RequestListener(actions); - domWindow.addEventListener('shumway.message', function(event) { - requestListener.receive(event); - }, false, true); - monitorUserInput(actions); - listener.onStopRequest(aRequest, context, statusCode); } }; @@ -1028,12 +1041,11 @@ channel.originalURI = aRequest.URI; channel.loadGroup = aRequest.loadGroup; - // We can use resource principal when data is fetched by the chrome - // e.g. useful for NoScript + // We can use all powerful principal: we are opening chrome:// web page, + // which will need lots of permission. var securityManager = Cc['@mozilla.org/scriptsecuritymanager;1'] .getService(Ci.nsIScriptSecurityManager); - var uri = Services.io.newURI(viewerUrl, null, null); - var resourcePrincipal = securityManager.getNoAppCodebasePrincipal(uri); + var resourcePrincipal = securityManager.getSystemPrincipal(); aRequest.owner = resourcePrincipal; channel.asyncOpen(proxy, aContext); }, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/ShumwayUtils.jsm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/ShumwayUtils.jsm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/ShumwayUtils.jsm 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/ShumwayUtils.jsm 2015-02-03 14:33:22.000000000 +0000 @@ -15,12 +15,8 @@ var EXPORTED_SYMBOLS = ["ShumwayUtils"]; -const RESOURCE_NAME = 'shumway'; -const EXT_PREFIX = 'shumway@research.mozilla.org'; -const SWF_CONTENT_TYPE = 'application/x-shockwave-flash'; const PREF_PREFIX = 'shumway.'; const PREF_DISABLED = PREF_PREFIX + 'disabled'; -const PREF_IGNORE_CTP = PREF_PREFIX + 'ignoreCTP'; let Cc = Components.classes; let Ci = Components.interfaces; @@ -30,14 +26,6 @@ Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/Services.jsm'); -let Svc = {}; -XPCOMUtils.defineLazyServiceGetter(Svc, 'mime', - '@mozilla.org/mime;1', - 'nsIMIMEService'); -XPCOMUtils.defineLazyServiceGetter(Svc, 'pluginHost', - '@mozilla.org/plugin/host;1', - 'nsIPluginHost'); - function getBoolPref(pref, def) { try { return Services.prefs.getBoolPref(pref); @@ -50,31 +38,6 @@ dump(str + '\n'); } -// Register/unregister a constructor as a factory. -function Factory() {} -Factory.prototype = { - register: function register(targetConstructor) { - var proto = targetConstructor.prototype; - this._classID = proto.classID; - - var factory = XPCOMUtils._getFactory(targetConstructor); - this._factory = factory; - - var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); - registrar.registerFactory(proto.classID, proto.classDescription, - proto.contractID, factory); - }, - - unregister: function unregister() { - var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); - registrar.unregisterFactory(this._classID, this._factory); - this._factory = null; - } -}; - -let converterFactory = new Factory(); -let overlayConverterFactory = new Factory(); - let ShumwayUtils = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), _registered: false, @@ -85,6 +48,10 @@ else this._ensureUnregistered(); + Cc["@mozilla.org/parentprocessmessagemanager;1"] + .getService(Ci.nsIMessageBroadcaster) + .addMessageListener('Shumway:Chrome:isEnabled', this); + // Listen for when shumway is completely disabled. Services.prefs.addObserver(PREF_DISABLED, this, false); }, @@ -96,6 +63,13 @@ else this._ensureUnregistered(); }, + + receiveMessage: function(message) { + switch (message.name) { + case 'Shumway:Chrome:isEnabled': + return this.enabled; + } + }, /** * shumway is only enabled if the global switch enabling is true. @@ -110,17 +84,16 @@ return; // Load the component and register it. - Cu.import('resource://shumway/ShumwayStreamConverter.jsm'); - converterFactory.register(ShumwayStreamConverter); - overlayConverterFactory.register(ShumwayStreamOverlayConverter); - - var ignoreCTP = getBoolPref(PREF_IGNORE_CTP, true); - - Svc.pluginHost.registerPlayPreviewMimeType(SWF_CONTENT_TYPE, ignoreCTP); + Cu.import('resource://shumway/ShumwayBootstrapUtils.jsm'); + ShumwayBootstrapUtils.register(); this._registered = true; log('Shumway is registered'); + + let globalMM = Cc['@mozilla.org/globalmessagemanager;1'] + .getService(Ci.nsIFrameScriptLoader); + globalMM.broadcastAsyncMessage('Shumway:Child:refreshSettings'); }, _ensureUnregistered: function _ensureUnregistered() { @@ -128,14 +101,15 @@ return; // Remove the contract/component. - converterFactory.unregister(); - overlayConverterFactory.unregister(); - Cu.unload('resource://shumway/ShumwayStreamConverter.jsm'); - - Svc.pluginHost.unregisterPlayPreviewMimeType(SWF_CONTENT_TYPE); + ShumwayBootstrapUtils.unregister(); + Cu.unload('resource://shumway/ShumwayBootstrapUtils.jsm'); this._registered = false; log('Shumway is unregistered'); + + let globalMM = Cc['@mozilla.org/globalmessagemanager;1'] + .getService(Ci.nsIFrameScriptLoader); + globalMM.broadcastAsyncMessage('Shumway:Child:refreshSettings'); } }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/version.txt thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/version.txt --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/version.txt 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/version.txt 2015-02-03 14:33:23.000000000 +0000 @@ -1,2 +1,2 @@ -0.9.2970 -22f884f +0.9.3693 +217e2e2 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/web/viewer.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/web/viewer.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/web/viewer.html 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/web/viewer.html 2015-02-03 14:33:23.000000000 +0000 @@ -35,7 +35,7 @@ display: none; } - #stageContainer { + #easelContainer { position:fixed !important; left:0;top:0;bottom:0;right:0; overflow: hidden; @@ -49,7 +49,7 @@ #overlay.enabled { display: block; position:fixed; - bottom: 0; + top: 0; right: 0; } @@ -58,7 +58,7 @@ width: 70px; height: 16px; padding: 8px 4px 4px; color: white; - background-color: rgba(0, 0, 0, 0.62); + background-color: rgba(218, 56, 7, 0.63); font: bold 10px sans-serif; text-align: center; text-decoration: none; @@ -100,7 +100,7 @@ -
    +
    Shumway × @@ -114,5 +114,8 @@
    + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/web/viewer.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/web/viewer.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/web/viewer.js 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/web/viewer.js 2015-02-03 14:33:23.000000000 +0000 @@ -27,11 +27,8 @@ * @return {*} The response. */ requestSync: function(action, data) { - var e = document.createEvent('CustomEvent'); - e.initCustomEvent('shumway.message', true, false, - {action: action, data: data, sync: true}); - document.dispatchEvent(e); - return e.detail.response; + var result = String(ShumwayCom.sendMessage(action, data, true, undefined)); + return result !== 'undefined' ? JSON.parse(result) : undefined; }, /** * Creates an event that the extension is listening for and will @@ -42,38 +39,45 @@ * with one data argument. */ request: function(action, data, callback) { - var e = document.createEvent('CustomEvent'); - e.initCustomEvent('shumway.message', true, false, - {action: action, data: data, sync: false}); + var cookie = undefined; if (callback) { - if ('nextId' in FirefoxCom.request) { - FirefoxCom.request.nextId = 1; + cookie = "requestId" + (this._nextRequestId++); + + if (!ShumwayCom.onMessageCallback) { + ShumwayCom.onMessageCallback = this._notifyMessageCallback.bind(this); } - var cookie = "requestId" + (FirefoxCom.request.nextId++); - e.detail.cookie = cookie; - e.detail.callback = true; - - document.addEventListener('shumway.response', function listener(event) { - if (cookie !== event.detail.cookie) - return; - - document.removeEventListener('shumway.response', listener, false); - - var response = event.detail.response; - return callback(response); - }, false); + this._requestCallbacks[cookie] = callback; + } + ShumwayCom.sendMessage(action, data, false, cookie); + }, + _notifyMessageCallback: function (cookie, response) { + var callback = this._requestCallbacks[cookie]; + if (!callback) { + return; } - return document.dispatchEvent(e); + delete this._requestCallbacks[cookie]; + callback(response !== 'undefined' ? JSON.parse(response) : undefined); }, + _nextRequestId: 1, + _requestCallbacks: Object.create(null), initJS: function (callback) { FirefoxCom.request('externalCom', {action: 'init'}); - document.addEventListener('shumway.remote', function (e) { - e.detail.result = callback(e.detail.functionName, e.detail.args); - }, false); + ShumwayCom.onExternalCallback = function (call) { + return callback(call.functionName, call.args); + }; } }; })(); +function notifyUserInput() { + ShumwayCom.sendMessage('userInput', null, true, undefined); +} + +document.addEventListener('mousedown', notifyUserInput, true); +document.addEventListener('mouseup', notifyUserInput, true); +document.addEventListener('keydown', notifyUserInput, true); +document.addEventListener('keyup', notifyUserInput, true); + function fallback() { FirefoxCom.requestSync('fallback', null) } @@ -82,13 +86,14 @@ console.log(msg); }; +var SHUMWAY_ROOT = "resource://shumway/"; + var viewerPlayerglobalInfo = { abcs: SHUMWAY_ROOT + "playerglobal/playerglobal.abcs", catalog: SHUMWAY_ROOT + "playerglobal/playerglobal.json" }; var builtinPath = SHUMWAY_ROOT + "avm2/generated/builtin/builtin.abc"; -var avm1Path = SHUMWAY_ROOT + "avm2/generated/avm1lib/avm1lib.abc"; var playerWindow; var playerWindowLoaded = new Promise(function(resolve) { @@ -101,7 +106,14 @@ }); function runViewer() { - var flashParams = JSON.parse(FirefoxCom.requestSync('getPluginParams', null)); + ShumwayCom.onLoadFileCallback = function (data) { + playerWindow.postMessage({ + type: "loadFileResponse", + args: data + }, '*'); + }; + + var flashParams = FirefoxCom.requestSync('getPluginParams', null); movieUrl = flashParams.url; if (!movieUrl) { @@ -112,6 +124,7 @@ movieParams = flashParams.movieParams; objectParams = flashParams.objectParams; + var baseUrl = flashParams.baseUrl; var isOverlay = flashParams.isOverlay; pauseExecution = flashParams.isPausedAtStart; @@ -125,7 +138,7 @@ } playerWindowLoaded.then(function () { - parseSwf(movieUrl, movieParams, objectParams); + parseSwf(movieUrl, baseUrl, movieParams, objectParams); }); if (isOverlay) { @@ -193,12 +206,6 @@ window.addEventListener("message", function handlerMessage(e) { var args = e.data; switch (args.callback) { - case 'loadFile': - playerWindow.postMessage({ - type: "loadFileResponse", - args: args - }, '*'); - break; case 'loadFileRequest': FirefoxCom.request('loadFile', args.data, null); break; @@ -208,6 +215,9 @@ case 'setClipboard': FirefoxCom.request('setClipboard', args.data, null); break; + case 'navigateTo': + FirefoxCom.request('navigateTo', args.data, null); + break; case 'started': document.body.classList.add('started'); break; @@ -232,13 +242,11 @@ } } -function parseSwf(url, movieParams, objectParams) { - var compilerSettings = JSON.parse( - FirefoxCom.requestSync('getCompilerSettings', null)); +function parseSwf(url, baseUrl, movieParams, objectParams) { + var compilerSettings = FirefoxCom.requestSync('getCompilerSettings', null); // init misc preferences var turboMode = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.turboMode', def: false}); - Shumway.GFX.backend.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.webgl', def: false}) ? 1 : 0; Shumway.GFX.hud.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.hud', def: false}); //forceHidpi.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.force_hidpi', def: false}); //dummyAnimation.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.dummyMode', def: false}); @@ -249,22 +257,23 @@ FirefoxCom.request('endActivation', null); } - var bgcolor; + var backgroundColor; if (objectParams) { var m; if (objectParams.bgcolor && (m = /#([0-9A-F]{6})/i.exec(objectParams.bgcolor))) { var hexColor = parseInt(m[1], 16); - bgcolor = hexColor << 8 | 0xff; + backgroundColor = hexColor << 8 | 0xff; } if (objectParams.wmode === 'transparent') { - bgcolor = 0; + backgroundColor = 0; } } - var easel = createEasel(bgcolor); + var easel = createEasel(backgroundColor); easelHost = new Shumway.GFX.Window.WindowEaselHost(easel, playerWindow, window); easelHost.processExternalCommand = processExternalCommand; + var displayParameters = easel.getDisplayParameters(); var data = { type: 'runSwf', settings: Shumway.Settings.getSettings(), @@ -272,21 +281,21 @@ compilerSettings: compilerSettings, movieParams: movieParams, objectParams: objectParams, + displayParameters: displayParameters, turboMode: turboMode, - bgcolor: bgcolor, + bgcolor: backgroundColor, url: url, - baseUrl: url + baseUrl: baseUrl || url } }; playerWindow.postMessage(data, '*'); } -function createEasel(bgcolor) { +function createEasel(backgroundColor) { var Stage = Shumway.GFX.Stage; var Easel = Shumway.GFX.Easel; - var Canvas2DStageRenderer = Shumway.GFX.Canvas2DStageRenderer; + var Canvas2DRenderer = Shumway.GFX.Canvas2DRenderer; Shumway.GFX.WebGL.SHADER_ROOT = SHUMWAY_ROOT + "gfx/gl/shaders/"; - var backend = Shumway.GFX.backend.value | 0; - return new Easel(document.getElementById("stageContainer"), backend, false, bgcolor); + return new Easel(document.getElementById("easelContainer"), false, backgroundColor); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/web/viewerPlayer.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/web/viewerPlayer.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/extensions/shumway/content/web/viewerPlayer.js 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/extensions/shumway/content/web/viewerPlayer.js 2015-02-03 14:33:23.000000000 +0000 @@ -24,7 +24,6 @@ var avm2Root = SHUMWAY_ROOT + "avm2/"; var builtinPath = avm2Root + "generated/builtin/builtin.abc"; -var avm1Path = avm2Root + "generated/avm1lib/avm1lib.abc"; window.print = function(msg) { console.log(msg); @@ -38,29 +37,32 @@ var appMode = compilerSettings.appCompiler ? EXECUTION_MODE.COMPILE : EXECUTION_MODE.INTERPRET; var asyncLoading = true; var baseUrl = flashParams.baseUrl; - var movieParams = flashParams.movieParams; var objectParams = flashParams.objectParams; var movieUrl = flashParams.url; Shumway.frameRateOption.value = flashParams.turboMode ? 60 : -1; Shumway.AVM2.Verifier.enabled.value = compilerSettings.verifier; - Shumway.createAVM2(builtinPath, viewerPlayerglobalInfo, avm1Path, sysMode, appMode, function (avm2) { + Shumway.createAVM2(builtinPath, viewerPlayerglobalInfo, sysMode, appMode, function (avm2) { function runSWF(file) { var player = new Shumway.Player.Window.WindowPlayer(window, window.parent); player.defaultStageColor = flashParams.bgcolor; + player.movieParams = flashParams.movieParams; + player.stageAlign = (objectParams && (objectParams.salign || objectParams.align)) || ''; + player.stageScale = (objectParams && objectParams.scale) || 'showall'; + player.displayParameters = flashParams.displayParameters; Shumway.ExternalInterfaceService.instance = player.createExternalInterfaceService(); player.load(file); } - file = Shumway.FileLoadingService.instance.setBaseUrl(baseUrl); + Shumway.FileLoadingService.instance.setBaseUrl(baseUrl); if (asyncLoading) { runSWF(movieUrl); } else { new Shumway.BinaryFileReader(movieUrl).readAll(null, function(buffer, error) { if (!buffer) { - throw "Unable to open the file " + file + ": " + error; + throw "Unable to open the file " + movieUrl + ": " + error; } runSWF(movieUrl, buffer); }); @@ -129,32 +131,34 @@ }; }, setBaseUrl: function (url) { - var baseUrl; - if (typeof URL !== 'undefined') { - baseUrl = new URL(url, document.location.href).href; - } else { - var a = document.createElement('a'); - a.href = url || '#'; - a.setAttribute('style', 'display: none;'); - document.body.appendChild(a); - baseUrl = a.href; - document.body.removeChild(a); - } - Shumway.FileLoadingService.instance.baseUrl = baseUrl; - return baseUrl; + Shumway.FileLoadingService.instance.baseUrl = url; }, resolveUrl: function (url) { - if (url.indexOf('://') >= 0) return url; - - var base = Shumway.FileLoadingService.instance.baseUrl; - base = base.lastIndexOf('/') >= 0 ? base.substring(0, base.lastIndexOf('/') + 1) : ''; - if (url.indexOf('/') === 0) { - var m = /^[^:]+:\/\/[^\/]+/.exec(base); - if (m) base = m[0]; - } - return base + url; + return new URL(url, Shumway.FileLoadingService.instance.baseUrl).href; + }, + navigateTo: function (url, target) { + window.parent.postMessage({ + callback: 'navigateTo', + data: { + url: this.resolveUrl(url), + target: target + } + }, '*'); } }; + + // Using SpecialInflate when chrome code provides it. + if (parent.createSpecialInflate) { + window.SpecialInflate = function () { + return parent.createSpecialInflate(); + }; + } + + // Using createRtmpXHR/createRtmpSocket when chrome code provides it. + if (parent.createRtmpXHR) { + window.createRtmpSocket = parent.createRtmpSocket; + window.createRtmpXHR = parent.createRtmpXHR; + } } window.addEventListener('message', function onWindowMessage(e) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/installer/Makefile.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/installer/Makefile.in --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/installer/Makefile.in 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/installer/Makefile.in 2015-02-03 14:33:23.000000000 +0000 @@ -73,6 +73,12 @@ DEFINES += -DMOZ_PACKAGE_MSVC_DLLS=1 DEFINES += -DMSVC_C_RUNTIME_DLL=$(MSVC_C_RUNTIME_DLL) DEFINES += -DMSVC_CXX_RUNTIME_DLL=$(MSVC_CXX_RUNTIME_DLL) +ifdef MSVC_APPCRT_DLL +DEFINES += -DMSVC_APPCRT_DLL=$(MSVC_APPCRT_DLL) +endif +ifdef MSVC_DESKTOPCRT_DLL +DEFINES += -DMSVC_DESKTOPCRT_DLL=$(MSVC_DESKTOPCRT_DLL) +endif endif endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/installer/package-manifest.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/installer/package-manifest.in --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/installer/package-manifest.in 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/installer/package-manifest.in 2015-02-03 14:33:23.000000000 +0000 @@ -100,6 +100,12 @@ #if MOZ_PACKAGE_MSVC_DLLS @BINPATH@/@MSVC_C_RUNTIME_DLL@ @BINPATH@/@MSVC_CXX_RUNTIME_DLL@ +#ifdef MSVC_APPCRT_DLL +@BINPATH@/@MSVC_APPCRT_DLL@ +#endif +#ifdef MSVC_DESKTOPCRT_DLL +@BINPATH@/@MSVC_DESKTOPCRT_DLL@ +#endif #endif #endif #ifndef MOZ_NATIVE_ICU diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/locales/en-US/chrome/browser/aboutTabCrashed.dtd thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/locales/en-US/chrome/browser/aboutTabCrashed.dtd --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/locales/en-US/chrome/browser/aboutTabCrashed.dtd 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/locales/en-US/chrome/browser/aboutTabCrashed.dtd 2015-02-03 14:33:23.000000000 +0000 @@ -0,0 +1,11 @@ + + + + + + + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/locales/en-US/chrome/browser/browser.dtd thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/locales/en-US/chrome/browser/browser.dtd --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/locales/en-US/chrome/browser/browser.dtd 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/locales/en-US/chrome/browser/browser.dtd 2015-02-03 14:33:23.000000000 +0000 @@ -795,11 +795,6 @@ a CSS length value. --> - - - - - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties 2015-02-03 14:33:23.000000000 +0000 @@ -220,7 +220,7 @@ # 'highlight' command, displayed to the user once the command has been entered, # informing the user how many nodes have been highlighted successfully and how # to turn highlighting off -highlightOutputConfirm=%1$S nodes highlighted +highlightOutputConfirm2=%1$S node highlighted;%1$S nodes highlighted # LOCALIZATION NOTE (highlightOutputMaxReached) A confirmation message for the # 'highlight' command, displayed to the user once the command has been entered, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/locales/en-US/chrome/browser/devtools/inspector.dtd thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/locales/en-US/chrome/browser/devtools/inspector.dtd --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/locales/en-US/chrome/browser/devtools/inspector.dtd 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/locales/en-US/chrome/browser/devtools/inspector.dtd 2015-02-03 14:33:23.000000000 +0000 @@ -77,9 +77,9 @@ - - + + + + + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/locales/en-US/chrome/browser/devtools/netmonitor.properties thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/locales/en-US/chrome/browser/devtools/netmonitor.properties --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/locales/en-US/chrome/browser/devtools/netmonitor.properties 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/locales/en-US/chrome/browser/devtools/netmonitor.properties 2015-02-03 14:33:23.000000000 +0000 @@ -44,6 +44,10 @@ # issues. netmonitor.security.state.broken=A security error prevented the resource from being loaded. +# LOCALIZATION NOTE (netmonitor.security.state.weak) +# This string is used as an tooltip for request that had minor security issues +netmonitor.security.state.weak=This resource was transferred over a connection that used weak encryption. + # LOCALIZATION NOTE (netmonitor.security.enabled): # This string is used to indicate that a specific security feature is used by # a connection in the security details tab. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/locales/en-US/chrome/browser/devtools/profiler.dtd thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/locales/en-US/chrome/browser/devtools/profiler.dtd --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/locales/en-US/chrome/browser/devtools/profiler.dtd 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/locales/en-US/chrome/browser/devtools/profiler.dtd 2015-02-03 14:33:23.000000000 +0000 @@ -41,13 +41,23 @@ - on a button that remvoes all the recordings. --> + + + + + + + - - + + + + + + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/locales/jar.mn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/locales/jar.mn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/locales/jar.mn 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/locales/jar.mn 2015-02-03 14:33:24.000000000 +0000 @@ -18,6 +18,7 @@ locale/browser/aboutHealthReport.dtd (%chrome/browser/aboutHealthReport.dtd) #endif locale/browser/aboutSessionRestore.dtd (%chrome/browser/aboutSessionRestore.dtd) + locale/browser/aboutTabCrashed.dtd (%chrome/browser/aboutTabCrashed.dtd) #ifdef MOZ_SERVICES_SYNC locale/browser/syncProgress.dtd (%chrome/browser/syncProgress.dtd) locale/browser/syncCustomize.dtd (%chrome/browser/syncCustomize.dtd) @@ -45,6 +46,7 @@ locale/browser/devtools/webconsole.properties (%chrome/browser/devtools/webconsole.properties) locale/browser/devtools/inspector.properties (%chrome/browser/devtools/inspector.properties) locale/browser/devtools/tilt.properties (%chrome/browser/devtools/tilt.properties) + locale/browser/devtools/shared.properties (%chrome/browser/devtools/shared.properties) locale/browser/devtools/scratchpad.properties (%chrome/browser/devtools/scratchpad.properties) locale/browser/devtools/scratchpad.dtd (%chrome/browser/devtools/scratchpad.dtd) locale/browser/devtools/storage.properties (%chrome/browser/devtools/storage.properties) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/locales/Makefile.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/locales/Makefile.in --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/locales/Makefile.in 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/locales/Makefile.in 2015-02-03 14:33:23.000000000 +0000 @@ -44,7 +44,10 @@ MOZ_LANGPACK_EID=langpack-$(AB_CD)@firefox.mozilla.org -PREF_JS_EXPORTS = $(call MERGE_FILE,firefox-l10n.js) +L10N_PREF_JS_EXPORTS = $(call MERGE_FILE,firefox-l10n.js) +L10N_PREF_JS_EXPORTS_PATH = $(FINAL_TARGET)/$(PREF_DIR) +L10N_PREF_JS_EXPORTS_FLAGS = $(PREF_PPFLAGS) --silence-missing-directive-warnings +PP_TARGETS += L10N_PREF_JS_EXPORTS ifneq (,$(filter cocoa,$(MOZ_WIDGET_TOOLKIT))) MOZ_PKG_MAC_DSSTORE=$(_ABS_DIST)/branding/dsstore diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/metro/base/tests/mochitest/browser_ui_telemetry.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/metro/base/tests/mochitest/browser_ui_telemetry.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/metro/base/tests/mochitest/browser_ui_telemetry.js 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/metro/base/tests/mochitest/browser_ui_telemetry.js 2015-02-03 14:33:24.000000000 +0000 @@ -5,16 +5,22 @@ "use strict"; +Cu.import("resource://gre/modules/TelemetrySession.jsm", this); + function test() { runTests(); } function getTelemetryPayload() { - return Cu.import("resource://gre/modules/TelemetryPing.jsm", {}). - TelemetryPing.getPayload(); + return TelemetrySession.getPayload(); } gTests.push({ + desc: "Setup", + run: () => { yield TelemetrySession.setup(); } +}); + +gTests.push({ desc: "Test browser-ui telemetry", run: function testBrowserUITelemetry() { // startup should have registered simple measures function @@ -63,4 +69,9 @@ is(simpleMeasurements.UITelemetry["metro-tabs"]["currTabCount"], 1); is(simpleMeasurements.UITelemetry["metro-tabs"]["maxTabCount"], 3); } -}); \ No newline at end of file +}); + +gTests.push({ + desc: "Shutdown", + run: () => { yield TelemetrySession.shutdown(); } +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/metro/components/HelperAppDialog.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/metro/components/HelperAppDialog.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/metro/components/HelperAppDialog.js 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/metro/components/HelperAppDialog.js 2015-02-03 14:33:24.000000000 +0000 @@ -138,10 +138,6 @@ messageContainer.appendChild(fragment); }, - promptForSaveToFile: function hald_promptForSaveToFile(aLauncher, aContext, aDefaultFile, aSuggestedFileExt, aForcePrompt) { - throw new Components.Exception("Async version must be used", Cr.NS_ERROR_NOT_AVAILABLE); - }, - promptForSaveToFileAsync: function hald_promptForSaveToFileAsync(aLauncher, aContext, aDefaultFile, aSuggestedFileExt, aForcePrompt) { let file = null; let prefs = Services.prefs; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/metro/locales/import/Makefile.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/metro/locales/import/Makefile.in --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/metro/locales/import/Makefile.in 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/metro/locales/import/Makefile.in 2015-02-03 14:33:24.000000000 +0000 @@ -11,8 +11,11 @@ # l10s prefs file # copying firefox-l10n.js over from LOCALE_SRCDIR or browser -PREF_JS_EXPORTS = $(firstword $(wildcard $(LOCALE_SRCDIR)/firefox-l10n.js) \ - $(topsrcdir)/$(relativesrcdir)/en-US/firefox-l10n.js ) +L10N_PREF_JS_EXPORTS = $(firstword $(wildcard $(LOCALE_SRCDIR)/firefox-l10n.js) \ + $(srcdir)/en-US/firefox-l10n.js ) +L10N_PREF_JS_EXPORTS_PATH = $(FINAL_TARGET)/$(PREF_DIR) +L10N_PREF_JS_EXPORTS_FLAGS = $(PREF_PPFLAGS) --silence-missing-directive-warnings +PP_TARGETS += L10N_PREF_JS_EXPORTS include $(topsrcdir)/config/rules.mk diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/metro/profile/Makefile.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/metro/profile/Makefile.in --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/metro/profile/Makefile.in 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/metro/profile/Makefile.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. - -include $(topsrcdir)/config/config.mk - -PREF_JS_EXPORTS = $(srcdir)/metro.js diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/metro/profile/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/metro/profile/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/metro/profile/moz.build 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/metro/profile/moz.build 2015-02-03 14:33:24.000000000 +0000 @@ -4,3 +4,7 @@ # 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/. +JS_PREFERENCE_FILES += [ + 'metro.js', +] + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/modules/BrowserUITelemetry.jsm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/modules/BrowserUITelemetry.jsm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/modules/BrowserUITelemetry.jsm 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/modules/BrowserUITelemetry.jsm 2015-02-03 14:33:24.000000000 +0000 @@ -138,6 +138,7 @@ "BMB_bookmarksPopup", "BMB_unsortedBookmarksPopup", "BMB_bookmarksToolbarPopup", + "search-go-button", ] return DEFAULT_ITEMS.concat(PALETTE_ITEMS) .concat(SPECIAL_CASES); @@ -458,6 +459,13 @@ return; } + // If not, we need to check if the item's anonid is in our list + // of built-in items to check. + if (ALL_BUILTIN_ITEMS.indexOf(item.getAttribute("anonid")) != -1) { + this._countMouseUpEvent("click-builtin-item", item.getAttribute("anonid"), aEvent.button); + return; + } + // If not, we need to check if one of the ancestors of the clicked // item is in our list of built-in items to check. let candidate = getIDBasedOnFirstIDedAncestor(item); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/modules/ReaderParent.jsm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/modules/ReaderParent.jsm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/modules/ReaderParent.jsm 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/modules/ReaderParent.jsm 2015-02-03 14:33:24.000000000 +0000 @@ -45,7 +45,10 @@ case "Reader:ArticleGet": this._getArticle(message.data.url, message.target).then((article) => { - message.target.messageManager.sendAsyncMessage("Reader:ArticleData", { article: article }); + // Make sure the target browser is still alive before trying to send data back. + if (message.target.messageManager) { + message.target.messageManager.sendAsyncMessage("Reader:ArticleData", { article: article }); + } }); break; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/modules/TabCrashReporter.jsm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/modules/TabCrashReporter.jsm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/modules/TabCrashReporter.jsm 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/modules/TabCrashReporter.jsm 2015-02-03 14:33:24.000000000 +0000 @@ -82,6 +82,7 @@ if (this.browserMap.get(browser) == childID) { this.browserMap.delete(browser); browser.contentDocument.documentElement.classList.remove("crashDumpAvailable"); + browser.contentDocument.documentElement.classList.add("crashDumpSubmitted"); } } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/modules/test/browser.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/modules/test/browser.ini --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/modules/test/browser.ini 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/modules/test/browser.ini 2015-02-03 14:33:24.000000000 +0000 @@ -15,5 +15,4 @@ [browser_SignInToWebsite.js] skip-if = e10s # Bug 941426 - SignIntoWebsite.jsm not e10s friendly [browser_taskbar_preview.js] -run-if = os == "win" -skip-if = e10s # Bug 666808 - AeroPeek support for e10s +skip-if = os != win || e10s # Bug 666808 - AeroPeek support for e10s diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/linux/aboutTabCrashed.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/linux/aboutTabCrashed.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/linux/aboutTabCrashed.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/linux/aboutTabCrashed.css 1970-01-01 00:00:00.000000000 +0000 @@ -1,108 +0,0 @@ -body { - background-color: rgb(241, 244, 248); - margin-top: 2em; - font: message-box; - font-size: 100%; -} - -p { - font-size: .8em; -} - -#error-box { - background: url('chrome://global/skin/icons/information-24.png') no-repeat left 4px; - -moz-padding-start: 30px; -} - -#error-box:-moz-locale-dir(rtl) { - background-position: right 4px; -} - -#main-error-msg { - color: #4b4b4b; - font-weight: bold; -} - -#report-box { - text-align: center; - width: 75%; - margin: 0 auto; - display: none; -} - -.crashDumpAvailable #report-box { - display: block -} - -#button-box { - text-align: center; - width: 75%; - margin: 0 auto; -} - -@media all and (min-width: 300px) { - #error-box { - max-width: 50%; - margin: 0 auto; - background-image: url('chrome://global/skin/icons/information-32.png'); - min-height: 36px; - -moz-padding-start: 38px; - } - - button { - width: auto !important; - min-width: 150px; - } -} - -@media all and (min-width: 780px) { - #error-box { - max-width: 30%; - } -} - -button { - font: message-box; - font-size: 0.6875em; - -moz-appearance: none; - -moz-user-select: none; - width: 100%; - margin: 2px 0; - padding: 2px 6px; - line-height: 1.2; - background-color: hsla(210,30%,95%,.1); - background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1)); - background-clip: padding-box; - border: 1px solid hsla(210,15%,25%,.4); - border-color: hsla(210,15%,25%,.3) hsla(210,15%,25%,.35) hsla(210,15%,25%,.4); - border-radius: 3px; - box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset, - 0 0 0 1px hsla(0,0%,100%,.3) inset, - 0 1px 0 hsla(0,0%,100%,.1); - - transition-property: background-color, border-color, box-shadow; - transition-duration: 150ms; - transition-timing-function: ease; - -} - -button:hover { - background-color: hsla(210,30%,95%,.8); - border-color: hsla(210,15%,25%,.45) hsla(210,15%,25%,.5) hsla(210,15%,25%,.55); - box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset, - 0 0 0 1px hsla(0,0%,100%,.3) inset, - 0 1px 0 hsla(0,0%,100%,.1), - 0 0 3px hsla(210,15%,25%,.1); - transition-property: background-color, border-color, box-shadow; - transition-duration: 150ms; - transition-timing-function: ease; -} - -button:hover:active { - background-color: hsla(210,15%,25%,.2); - box-shadow: 0 1px 1px hsla(210,15%,25%,.2) inset, - 0 0 2px hsla(210,15%,25%,.4) inset; - transition-property: background-color, border-color, box-shadow; - transition-duration: 10ms; - transition-timing-function: linear; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/linux/jar.mn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/linux/jar.mn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/linux/jar.mn 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/linux/jar.mn 2015-02-03 14:33:24.000000000 +0000 @@ -34,6 +34,9 @@ skin/classic/browser/fullscreen-darknoise.png skin/classic/browser/Geolocation-16.png skin/classic/browser/Geolocation-64.png + skin/classic/browser/heartbeat-icon.svg (../shared/heartbeat-icon.svg) + skin/classic/browser/heartbeat-star-lit.svg (../shared/heartbeat-star-lit.svg) + skin/classic/browser/heartbeat-star-off.svg (../shared/heartbeat-star-off.svg) skin/classic/browser/identity.png skin/classic/browser/identity-icons-generic.png skin/classic/browser/identity-icons-https.png @@ -188,6 +191,7 @@ skin/classic/browser/tabbrowser/alltabs.png (tabbrowser/alltabs.png) skin/classic/browser/tabbrowser/alltabs-inverted.png (tabbrowser/alltabs-inverted.png) skin/classic/browser/tabbrowser/connecting.png (tabbrowser/connecting.png) + skin/classic/browser/tabbrowser/crashed.svg (../shared/tabbrowser/crashed.svg) skin/classic/browser/tabbrowser/loading.png (tabbrowser/loading.png) skin/classic/browser/tabbrowser/tab-active-middle.png (tabbrowser/tab-active-middle.png) skin/classic/browser/tabbrowser/tab-arrow-left.png (tabbrowser/tab-arrow-left.png) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/linux/searchbar.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/linux/searchbar.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/linux/searchbar.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/linux/searchbar.css 2015-02-03 14:33:24.000000000 +0000 @@ -4,6 +4,7 @@ #PopupSearchAutoComplete { -moz-margin-start: -24px; + padding: 1px; } .searchbar-textbox { @@ -123,7 +124,6 @@ font-weight: normal; background-color: rgb(245, 245, 245); border-top: 1px solid #ccc; - margin: 0 1px; padding: 3px 5px; color: #666; } @@ -137,7 +137,7 @@ } .search-panel-one-offs { - margin: 0 0 !important; + margin: 0 -1px !important; border-top: 1px solid #ccc; } @@ -172,7 +172,7 @@ .searchbar-engine-one-off-item > .button-box { border: none; - padding: 0 0; + padding: 0; } .searchbar-engine-one-off-item > .button-box > .button-text { @@ -190,7 +190,7 @@ -moz-appearance: none; border: none; height: 32px; - margin: 0 0; + margin: 0; padding: 0 10px; } @@ -260,10 +260,9 @@ .search-setting-button { -moz-appearance: none; - border-bottom: none; - border-left: none; - border-right: none; - -moz-border-top-colors: none; + border: none; + border-top: 1px solid #ccc; + margin: 0; min-height: 32px; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/osx/browser.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/osx/browser.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/osx/browser.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/osx/browser.css 2015-02-03 14:33:24.000000000 +0000 @@ -3092,6 +3092,14 @@ opacity: .9; } +/* + * Force the overlay to create a new stacking context so it always appears on + * top of the icon. + */ +.tab-icon-overlay { + opacity: 0.9999; +} + .tab-label:not([selected="true"]) { opacity: .7; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/osx/jar.mn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/osx/jar.mn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/osx/jar.mn 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/osx/jar.mn 2015-02-03 14:33:24.000000000 +0000 @@ -36,6 +36,9 @@ skin/classic/browser/Geolocation-16@2x.png skin/classic/browser/Geolocation-64.png skin/classic/browser/Geolocation-64@2x.png + skin/classic/browser/heartbeat-icon.svg (../shared/heartbeat-icon.svg) + skin/classic/browser/heartbeat-star-lit.svg (../shared/heartbeat-star-lit.svg) + skin/classic/browser/heartbeat-star-off.svg (../shared/heartbeat-star-off.svg) skin/classic/browser/identity.png skin/classic/browser/identity@2x.png skin/classic/browser/identity-icons-generic.png @@ -298,6 +301,7 @@ skin/classic/browser/tabbrowser/alltabs-box-bkgnd-icon.png (tabbrowser/alltabs-box-bkgnd-icon.png) skin/classic/browser/tabbrowser/alltabs-box-bkgnd-icon-inverted.png (tabbrowser/alltabs-box-bkgnd-icon-inverted.png) skin/classic/browser/tabbrowser/alltabs-box-bkgnd-icon-inverted@2x.png (tabbrowser/alltabs-box-bkgnd-icon-inverted@2x.png) + skin/classic/browser/tabbrowser/crashed.svg (../shared/tabbrowser/crashed.svg) skin/classic/browser/tabbrowser/newtab.png (tabbrowser/newtab.png) skin/classic/browser/tabbrowser/newtab@2x.png (tabbrowser/newtab@2x.png) skin/classic/browser/tabbrowser/newtab-inverted.png (tabbrowser/newtab-inverted.png) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/osx/preferences/in-content/preferences.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/osx/preferences/in-content/preferences.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/osx/preferences/in-content/preferences.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/osx/preferences/in-content/preferences.css 2015-02-03 14:33:24.000000000 +0000 @@ -58,6 +58,11 @@ border-bottom: none; } +#advancedPrefs { + margin-right: 0; /*override margin from normal preferences.css */ + margin-left: 0; +} + /** * Dialog */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/aboutNetError.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/aboutNetError.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/aboutNetError.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/aboutNetError.css 2015-02-03 14:33:24.000000000 +0000 @@ -28,6 +28,7 @@ } #errorPageContainer { + position: relative; min-width: 320px; max-width: 512px; } @@ -74,15 +75,10 @@ div#certificateErrorReporting { display: none; - float:right; + float: right; /* Align with the "Try Again" button */ - margin-top:24px; - margin-right:24px; -} - -div#certificateErrorReporting a, -div#certificateErrorReportingPanel a { - color: #0095DD; + margin-top: 24px; + -moz-margin-end: 24px; } div#certificateErrorReporting a { @@ -94,7 +90,11 @@ } span.downArrow { - font-size: 0.9em; + display: inline-block; + vertical-align: middle; + font-size: 0.6em; + -moz-margin-start: 0.5em; + transform: scaleY(0.7); } div#certificateErrorReportingPanel { @@ -106,11 +106,18 @@ * makes the overall div look uneven */ padding: 0 12px 12px 12px; box-shadow: 0 0 4px #ddd; - position: relative; + font-size: 0.9em; + position: absolute; width: 75%; + margin-top: 10px; +} + +div#certificateErrorReportingPanel:-moz-dir(ltr) { left: 34%; - font-size: 0.9em; - top: 8px; +} + +div#certificateErrorReportingPanel:-moz-dir(rtl) { + right: 0; } span#hostname { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/aboutTabCrashed.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/aboutTabCrashed.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/aboutTabCrashed.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/aboutTabCrashed.css 2015-02-03 14:33:24.000000000 +0000 @@ -1,11 +1,11 @@ +/* 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/. */ + .title { background-image: url("chrome://browser/skin/tab-crashed.svg"); } -#report-box { - display: none; +#reportSent { + font-weight: bold; } - -.crashDumpAvailable #report-box { - display: block -} \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/devedition.inc.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/devedition.inc.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/devedition.inc.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/devedition.inc.css 2015-02-03 14:33:24.000000000 +0000 @@ -157,7 +157,8 @@ /* End override @tabCurveHalfWidth@ and @tabCurveWidth@ */ #urlbar ::-moz-selection, -#navigator-toolbox .searchbar-textbox ::-moz-selection { +#navigator-toolbox .searchbar-textbox ::-moz-selection, +.browserContainer > findbar ::-moz-selection { background-color: var(--chrome-selection-background-color); color: var(--chrome-selection-color); } @@ -182,6 +183,16 @@ color: var(--chrome-color); } +.browserContainer > findbar { + background-image: none; +} + +/* Default findbar text color doesn't look good - Bug 1125677 */ +.browserContainer > findbar .findbar-find-status, +.browserContainer > findbar .found-matches { + color: inherit; +} + #navigator-toolbox .toolbarbutton-1, .browserContainer > findbar .findbar-button, #PlacesToolbar toolbarbutton.bookmark-item { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/devtools/images/performance-icons.svg thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/devtools/images/performance-icons.svg --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/devtools/images/performance-icons.svg 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/devtools/images/performance-icons.svg 2015-02-03 14:33:25.000000000 +0000 @@ -28,9 +28,9 @@ - - - + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/devtools/netmonitor.inc.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/devtools/netmonitor.inc.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/devtools/netmonitor.inc.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/devtools/netmonitor.inc.css 2015-02-03 14:33:25.000000000 +0000 @@ -178,6 +178,11 @@ list-style-image: url(chrome://browser/skin/identity-icons-https.png); } +.security-state-weak { + cursor: pointer; + list-style-image: url(chrome://browser/skin/identity-icons-https-mixed-display.png); +} + .security-state-broken { cursor: pointer; list-style-image: url(chrome://browser/skin/identity-icons-https-mixed-active.png); @@ -578,6 +583,21 @@ white-space: pre-wrap; } +.security-warning-icon { + background-image: url(alerticon-warning.png); + background-size: 13px 12px; + -moz-margin-start: 5px; + vertical-align: top; + width: 13px; + height: 12px; +} + +@media (min-resolution: 2dppx) { + .security-warning-icon { + background-image: url(alerticon-warning@2x.png); + } +} + /* Custom request form */ #custom-pane { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/devtools/performance.inc.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/devtools/performance.inc.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/devtools/performance.inc.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/devtools/performance.inc.css 2015-02-03 14:33:25.000000000 +0000 @@ -25,7 +25,14 @@ -moz-border-end-color: var(--theme-splitter-color); } -/* Overview Panel */ +#performance-toolbar-controls-detail-views > toolbarbutton { + min-width: 0; +} + +#performance-toolbar-controls-detail-views .toolbarbutton-text { + -moz-padding-start: 4px; + -moz-padding-end: 8px; +} #record-button { list-style-image: url(profiler-stopwatch.svg); @@ -45,11 +52,13 @@ list-style-image: url(performance-icons.svg#details-waterfall); } -#select-calltree-view { +#select-js-calltree-view, +#select-memory-calltree-view { list-style-image: url(performance-icons.svg#details-call-tree); } -#select-flamegraph-view { +#select-js-flamegraph-view, +#select-memory-flamegraph-view { list-style-image: url(performance-icons.svg#details-flamegraph); } @@ -61,27 +70,42 @@ overflow: auto; } +.call-tree-cells-container[allocations-hidden] .call-tree-cell[type="allocations"], +.call-tree-cells-container[allocations-hidden] .call-tree-cell[type="self-allocations"], .call-tree-cells-container[categories-hidden] .call-tree-category { display: none; } +.call-tree-header { + font-size: 90%; + padding-top: 2px !important; + padding-bottom: 2px !important; +} + .call-tree-header[type="duration"], .call-tree-cell[type="duration"], .call-tree-header[type="self-duration"], .call-tree-cell[type="self-duration"] { - width: 9em; + width: 6vw; } .call-tree-header[type="percentage"], .call-tree-cell[type="percentage"], .call-tree-header[type="self-percentage"], .call-tree-cell[type="self-percentage"] { - width: 6em; + width: 5vw; } .call-tree-header[type="samples"], .call-tree-cell[type="samples"] { - width: 5em; + width: 4vw; +} + +.call-tree-header[type="allocations"], +.call-tree-cell[type="allocations"], +.call-tree-header[type="self-allocations"], +.call-tree-cell[type="self-allocations"] { + width: 7vw; } .call-tree-header[type="function"], @@ -142,7 +166,8 @@ .call-tree-item:not([origin="content"]) .call-tree-name, .call-tree-item:not([origin="content"]) .call-tree-url, -.call-tree-item:not([origin="content"]) .call-tree-line { +.call-tree-item:not([origin="content"]) .call-tree-line, +.call-tree-item:not([origin="content"]) .call-tree-column { /* Style chrome and non-JS nodes differently. */ opacity: 0.6; } @@ -164,14 +189,21 @@ color: var(--theme-highlight-orange); } +.call-tree-column { + color: var(--theme-highlight-orange); + opacity: 0.6; +} + .call-tree-host { -moz-margin-start: 8px !important; font-size: 90%; color: var(--theme-content-color2); } +.call-tree-name[value=""], .call-tree-url[value=""], .call-tree-line[value=""], +.call-tree-column[value=""], .call-tree-host[value=""] { display: none; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/devtools/profiler.inc.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/devtools/profiler.inc.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/devtools/profiler.inc.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/devtools/profiler.inc.css 2015-02-03 14:33:25.000000000 +0000 @@ -220,6 +220,8 @@ overflow: auto; } +.call-tree-cells-container[allocations-hidden] .call-tree-cell[type="allocations"], +.call-tree-cells-container[allocations-hidden] .call-tree-cell[type="self-allocations"], .call-tree-cells-container[categories-hidden] .call-tree-category { display: none; } @@ -305,7 +307,8 @@ .call-tree-item:not([origin="content"]) .call-tree-name, .call-tree-item:not([origin="content"]) .call-tree-url, -.call-tree-item:not([origin="content"]) .call-tree-line { +.call-tree-item:not([origin="content"]) .call-tree-line, +.call-tree-item:not([origin="content"]) .call-tree-column { /* Style chrome and non-JS nodes differently. */ opacity: 0.6; } @@ -323,19 +326,25 @@ color: var(--theme-highlight-blue); } - .call-tree-line { color: var(--theme-highlight-orange); } +.call-tree-column { + color: var(--theme-highlight-orange); + opacity: 0.6; +} + .call-tree-host { -moz-margin-start: 8px !important; font-size: 90%; color: var(--theme-content-color2); } +.call-tree-name[value=""], .call-tree-url[value=""], .call-tree-line[value=""], +.call-tree-column[value=""], .call-tree-host[value=""] { display: none; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/devtools/webconsole.inc.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/devtools/webconsole.inc.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/devtools/webconsole.inc.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/devtools/webconsole.inc.css 2015-02-03 14:33:25.000000000 +0000 @@ -117,7 +117,7 @@ display: flex; } -.message-body { +.message-body > * { white-space: pre-wrap; word-wrap: break-word; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/heartbeat-icon.svg thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/heartbeat-icon.svg --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/heartbeat-icon.svg 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/heartbeat-icon.svg 2015-02-03 14:33:25.000000000 +0000 @@ -0,0 +1,23 @@ + + + +  + Line 14 + Created with Sketch. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/heartbeat-star-lit.svg thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/heartbeat-star-lit.svg --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/heartbeat-star-lit.svg 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/heartbeat-star-lit.svg 2015-02-03 14:33:25.000000000 +0000 @@ -0,0 +1,428 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/heartbeat-star-off.svg thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/heartbeat-star-off.svg --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/heartbeat-star-off.svg 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/heartbeat-star-off.svg 2015-02-03 14:33:25.000000000 +0000 @@ -0,0 +1,428 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/incontentprefs/preferences.inc.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/incontentprefs/preferences.inc.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/incontentprefs/preferences.inc.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/incontentprefs/preferences.inc.css 2015-02-03 14:33:25.000000000 +0000 @@ -25,6 +25,14 @@ /* Category List */ +#categories { + max-height: 100vh; +} + +#categories > scrollbox { + overflow-x: hidden !important; +} + .category-icon { list-style-image: url("chrome://browser/skin/preferences/in-content/icons.png"); } @@ -194,10 +202,6 @@ -moz-margin-start: 0; } -#advancedPrefs { - padding-bottom: 0; /* no padding needed in inContent prefs */ -} - #tabsElement { -moz-margin-end: 4px; /* add the 4px end-margin of other elements */ } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/incontentprefs/search.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/incontentprefs/search.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/incontentprefs/search.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/incontentprefs/search.css 2015-02-03 14:33:25.000000000 +0000 @@ -2,6 +2,10 @@ * 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/. */ + #defaultEngine { + -moz-margin-start: 0; + } + #defaultEngine > .menulist-label-box > .menulist-icon { height: 16px; } @@ -12,7 +16,7 @@ } #engineList { - margin: .5em 6px; + margin: .5em 0; } #engineList treechildren::-moz-tree-image(engineShown, checked) { @@ -51,3 +55,9 @@ #addEnginesBox { margin-bottom: 1em; } + +#removeEngineButton, +#restoreDefaultSearchEngines { + margin-right: 0; + margin-left: 0; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/tabbrowser/crashed.svg thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/tabbrowser/crashed.svg --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/tabbrowser/crashed.svg 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/tabbrowser/crashed.svg 2015-02-03 14:33:25.000000000 +0000 @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/tabs.inc.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/tabs.inc.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/tabs.inc.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/tabs.inc.css 2015-02-03 14:33:25.000000000 +0000 @@ -65,6 +65,10 @@ -moz-padding-start: 9px; } +.tab-content[pinned] { + -moz-padding-end: 3px; +} + .tab-throbber, .tab-icon-image, .tab-close-button { @@ -75,12 +79,26 @@ .tab-icon-image { height: 16px; width: 16px; + -moz-margin-end: 6px; } .tab-icon-image { list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png"); } +.tab-icon-overlay { + width: 16px; + height: 16px; + margin-top: 10px; + -moz-margin-start: -16px; + display: none; +} + +.tab-icon-overlay[crashed] { + display: -moz-box; + list-style-image: url("chrome://browser/skin/tabbrowser/crashed.svg"); +} + .tab-throbber[busy] { list-style-image: url("chrome://browser/skin/tabbrowser/connecting.png"); } @@ -89,11 +107,6 @@ list-style-image: url("chrome://browser/skin/tabbrowser/loading.png"); } -.tab-throbber:not([pinned]), -.tab-icon-image:not([pinned]) { - -moz-margin-end: 6px; -} - .tab-label { -moz-margin-end: 0; -moz-margin-start: 0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/UITour.inc.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/UITour.inc.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/shared/UITour.inc.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/shared/UITour.inc.css 2015-02-03 14:33:24.000000000 +0000 @@ -195,3 +195,110 @@ background-image: -moz-image-rect(url("chrome://browser/skin/dots@2x.png"), 0, 14, 100%, 0); } } + +/* Notification overrides for Heartbeat UI */ + +notification.heartbeat { + background-color: #F1F1F1; +%ifdef XP_MACOSX + background-image: linear-gradient(-179deg, #FBFBFB 0%, #EBEBEB 100%); +%endif + box-shadow: 0px 1px 0px 0px rgba(0,0,0,0.35); +} + +@keyframes pulse-onshow { + 0% { + opacity: 0; + transform: scale(1.0); + } + 25% { + opacity: 1; + transform: scale(1.1); + } + 50% { + transform: scale(1.0); + } + 75% { + transform: scale(1.1); + } + 100% { + transform: scale(1.0); + } +} + +@keyframes pulse-twice { + 0% { + transform: scale(1.1); + } + 50% { + transform: scale(0.8); + } + 100% { + transform: scale(1); + } +} + +.messageText.heartbeat { + color: #333333; + font-weight: normal; + font-family: "Lucida Grande", Segoe, Ubuntu; + font-size: 14px; + line-height: 16px; + text-shadow: none; +} + +.messageImage.heartbeat { + width: 36px; + height: 36px; + -moz-margin-end: 10px; +} + +.messageImage.heartbeat.pulse-onshow { + animation-name: pulse-onshow; + animation-duration: 1.5s; + animation-iteration-count: 1; + animation-timing-function: cubic-bezier(.7,1.8,.9,1.1); +} + +.messageImage.heartbeat.pulse-twice { + animation-name: pulse-twice; + animation-duration: 1s; + animation-iteration-count: 2; + animation-timing-function: linear; +} + +/* Heartbeat UI Rating Star Classes */ +.heartbeat > #star-rating-container { + display: -moz-box; +} + +.heartbeat > #star-rating-container > #star5 { + -moz-box-ordinal-group: 5; +} + +.heartbeat > #star-rating-container > #star4 { + -moz-box-ordinal-group: 4; +} + +.heartbeat > #star-rating-container > #star3 { + -moz-box-ordinal-group: 3; +} + +.heartbeat > #star-rating-container > #star2 { + -moz-box-ordinal-group: 2; +} + +.heartbeat > #star-rating-container > .star-x { + background: url("chrome://browser/skin/heartbeat-star-off.svg"); + cursor: pointer; + width: 24px; + height: 24px; +} + +.heartbeat > #star-rating-container > .star-x:hover, +.heartbeat > #star-rating-container > .star-x:hover ~ .star-x { + background: url("chrome://browser/skin/heartbeat-star-lit.svg"); + cursor: pointer; + width: 24px; + height: 24px; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/windows/browser.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/windows/browser.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/windows/browser.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/windows/browser.css 2015-02-03 14:33:25.000000000 +0000 @@ -753,7 +753,7 @@ } #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon { - -moz-border-end: none; + -moz-border-end: none transparent; } #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/windows/customizableui/panelUIOverlay.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/windows/customizableui/panelUIOverlay.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/windows/customizableui/panelUIOverlay.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/windows/customizableui/panelUIOverlay.css 2015-02-03 14:33:25.000000000 +0000 @@ -140,3 +140,95 @@ } } %endif + +@media not all and (-moz-windows-default-theme) { + #edit-controls@inAnyPanel@ > #copy-button, + #zoom-controls@inAnyPanel@ > #zoom-reset-button, + .toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton { + border: 1px solid transparent; + } + + panelview .toolbarbutton-1@buttonStateHover@, + toolbarbutton.subviewbutton@buttonStateHover@, + menu.subviewbutton@menuStateHover@, + menuitem.subviewbutton@menuStateHover@, + .widget-overflow-list .toolbarbutton-1@buttonStateHover@, + .toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton@buttonStateHover@ { + border-color: ThreeDLightShadow !important; + } + + panelview:not(#PanelUI-mainView) .toolbarbutton-1@buttonStateHover@, + toolbarbutton.subviewbutton@buttonStateHover@, + menu.subviewbutton@menuStateHover@, + menuitem.subviewbutton@menuStateHover@, + .widget-overflow-list .toolbarbutton-1@buttonStateHover@ { + background-color: Highlight; + color: highlighttext; + } + + panelview .toolbarbutton-1:-moz-any(@buttonStateActive@,[checked=true]), + toolbarbutton.subviewbutton@buttonStateActive@, + menu.subviewbutton@menuStateActive@, + menuitem.subviewbutton@menuStateActive@, + .widget-overflow-list .toolbarbutton-1@buttonStateActive@, + .toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton@buttonStateActive@ { + background-color: Highlight; + border-color: ThreeDLightShadow; + color: highlighttext; + box-shadow: none; + } + + panelview .toolbarbutton-1[disabled], + toolbarbutton.subviewbutton[disabled], + menu.subviewbutton[disabled], + menuitem.subviewbutton[disabled], + .widget-overflow-list .toolbarbutton-1[disabled], + .toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton[disabled] { + text-shadow: none; + } + + #PanelUI-fxa-status, + #PanelUI-help, + #PanelUI-customize { + border: 1px solid transparent; + } + + #PanelUI-fxa-status:not([disabled]):hover, + #PanelUI-help:not([disabled]):hover, + #PanelUI-customize:hover, + #PanelUI-fxa-status:not([disabled]):hover:active, + #PanelUI-help:not([disabled]):hover:active, + #PanelUI-customize:hover:active { + border-color: ThreeDLightShadow; + box-shadow: none; + } + + #BMB_bookmarksPopup .menu-text, + #BMB_bookmarksPopup menupopup { + color: -moz-FieldText; + } + + #BMB_bookmarksPopup .subviewbutton[disabled=true] > .menu-text { + color: GrayText; + } + + #BMB_bookmarksPopup menupopup[placespopup=true] > hbox { + box-shadow: none; + background: -moz-field; + border: 1px solid ThreeDShadow; + } + + .subviewbutton.panel-subview-footer, + #BMB_bookmarksPopup .subviewbutton.panel-subview-footer { + color: ButtonText; + } + + .subviewbutton@menuStateHover@, + menuitem.panel-subview-footer@menuStateHover@, + .subviewbutton.panel-subview-footer@buttonStateHover@, + .subviewbutton.panel-subview-footer@buttonStateActive@, + #BMB_bookmarksPopup .panel-subview-footer@menuStateHover@ > .menu-text { + background-color: Highlight; + color: highlighttext !important; + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/windows/jar.mn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/windows/jar.mn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/windows/jar.mn 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/windows/jar.mn 2015-02-03 14:33:25.000000000 +0000 @@ -36,6 +36,9 @@ skin/classic/browser/fullscreen-darknoise.png skin/classic/browser/Geolocation-16.png skin/classic/browser/Geolocation-64.png + skin/classic/browser/heartbeat-icon.svg (../shared/heartbeat-icon.svg) + skin/classic/browser/heartbeat-star-lit.svg (../shared/heartbeat-star-lit.svg) + skin/classic/browser/heartbeat-star-off.svg (../shared/heartbeat-star-off.svg) skin/classic/browser/Info.png skin/classic/browser/identity.png skin/classic/browser/identity-icons-generic.png @@ -216,6 +219,7 @@ skin/classic/browser/tabbrowser/newtab.png (tabbrowser/newtab-XPVista7.png) skin/classic/browser/tabbrowser/newtab-inverted.png (tabbrowser/newtab-inverted.png) skin/classic/browser/tabbrowser/connecting.png (tabbrowser/connecting.png) + skin/classic/browser/tabbrowser/crashed.svg (../shared/tabbrowser/crashed.svg) skin/classic/browser/tabbrowser/loading.png (tabbrowser/loading.png) skin/classic/browser/tabbrowser/tab-active-middle.png (tabbrowser/tab-active-middle.png) skin/classic/browser/tabbrowser/tab-active-middle@2x.png (tabbrowser/tab-active-middle@2x.png) @@ -494,6 +498,9 @@ skin/classic/aero/browser/fullscreen-darknoise.png skin/classic/aero/browser/Geolocation-16.png skin/classic/aero/browser/Geolocation-64.png + skin/classic/aero/browser/heartbeat-icon.svg (../shared/heartbeat-icon.svg) + skin/classic/aero/browser/heartbeat-star-lit.svg (../shared/heartbeat-star-lit.svg) + skin/classic/aero/browser/heartbeat-star-off.svg (../shared/heartbeat-star-off.svg) skin/classic/aero/browser/Info.png (Info-aero.png) skin/classic/aero/browser/identity.png (identity-aero.png) skin/classic/aero/browser/identity-icons-generic.png @@ -683,6 +690,7 @@ skin/classic/aero/browser/tabbrowser/newtab-XPVista7.png (tabbrowser/newtab-XPVista7.png) skin/classic/aero/browser/tabbrowser/newtab-inverted.png (tabbrowser/newtab-inverted.png) skin/classic/aero/browser/tabbrowser/connecting.png (tabbrowser/connecting.png) + skin/classic/aero/browser/tabbrowser/crashed.svg (../shared/tabbrowser/crashed.svg) skin/classic/aero/browser/tabbrowser/loading.png (tabbrowser/loading.png) skin/classic/aero/browser/tabbrowser/tab-active-middle.png (tabbrowser/tab-active-middle.png) skin/classic/aero/browser/tabbrowser/tab-active-middle@2x.png (tabbrowser/tab-active-middle@2x.png) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/windows/preferences/in-content/preferences.css thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/windows/preferences/in-content/preferences.css --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/browser/themes/windows/preferences/in-content/preferences.css 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/browser/themes/windows/preferences/in-content/preferences.css 2015-02-03 14:33:25.000000000 +0000 @@ -13,6 +13,10 @@ -moz-margin-start: -4px; } +#advancedPrefs { + padding-bottom: 0; /* override padding from normal preferences.css */ +} + /** * Dialog */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/build/gecko_templates.mozbuild thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/build/gecko_templates.mozbuild --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/build/gecko_templates.mozbuild 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/build/gecko_templates.mozbuild 2015-02-03 14:33:26.000000000 +0000 @@ -78,6 +78,11 @@ else: error('`mozglue` must be "program" or "library"') + if not CONFIG['JS_STANDALONE']: + USE_LIBS += [ + 'fallible', + ] + @template def GeckoProgram(name, linkage='standalone', **kwargs): diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/build/gyp.mozbuild thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/build/gyp.mozbuild --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/build/gyp.mozbuild 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/build/gyp.mozbuild 2015-02-03 14:33:26.000000000 +0000 @@ -22,9 +22,11 @@ # use_system_lib* still seems to be in use in trunk/build 'use_system_libjpeg': 0, 'use_system_libvpx': 0, + 'build_json': 0, 'build_libjpeg': 0, - 'build_libvpx': 0, 'build_libyuv': 0, + 'build_libvpx': 0, + 'build_ssl': 0, 'libyuv_dir': '/media/libyuv', 'yuv_disable_avx2': 0 if CONFIG['HAVE_X86_AVX2'] else 1, # don't use openssl @@ -47,7 +49,7 @@ 'use_temporal_layers': 0, # Creates AEC internal sample dump files in current directory - 'aec_debug_dump': 1, + 'aec_debug_dump': 0, # Enable and force use of hardware AEC 'hardware_aec_ns': 1 if CONFIG['MOZ_WEBRTC_HARDWARE_AEC_NS'] else 0, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/build/mach_bootstrap.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/build/mach_bootstrap.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/build/mach_bootstrap.py 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/build/mach_bootstrap.py 2015-02-03 14:33:26.000000000 +0000 @@ -34,6 +34,8 @@ 'python/jsmin', 'python/psutil', 'python/which', + 'python/pystache', + 'python/pyyaml/lib', 'build/pymake', 'config', 'dom/bindings', @@ -42,6 +44,7 @@ 'other-licenses/ply', 'xpcom/idl-parser', 'testing', + 'testing/taskcluster', 'testing/xpcshell', 'testing/web-platform', 'testing/web-platform/harness', @@ -79,6 +82,7 @@ 'python/mozbuild/mozbuild/frontend/mach_commands.py', 'services/common/tests/mach_commands.py', 'testing/mach_commands.py', + 'testing/taskcluster/mach_commands.py', 'testing/marionette/mach_commands.py', 'testing/mochitest/mach_commands.py', 'testing/xpcshell/mach_commands.py', @@ -108,6 +112,11 @@ 'long': 'Run tests.', 'priority': 60, }, + 'ci': { + 'short': 'CI', + 'long': 'Taskcluster commands', + 'priority': 59 + }, 'devenv': { 'short': 'Development Environment', 'long': 'Set up and configure your development environment.', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/build/sanitizers/lsan_suppressions.txt thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/build/sanitizers/lsan_suppressions.txt --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/build/sanitizers/lsan_suppressions.txt 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/build/sanitizers/lsan_suppressions.txt 2015-02-03 14:33:26.000000000 +0000 @@ -46,7 +46,7 @@ ### # Bug 981195 - Small leak in the parser. m4 -leak:TypeCompartment::fixObjectType +leak:TypeCompartment::fixObjectGroup # Bug 982111 - WebM is leaking. m1 leak:nestegg_read_packet diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/build/win32/Makefile.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/build/win32/Makefile.in --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/build/win32/Makefile.in 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/build/win32/Makefile.in 2015-02-03 14:33:27.000000000 +0000 @@ -9,6 +9,8 @@ REDIST_FILES = \ $(MSVC_C_RUNTIME_DLL) \ $(MSVC_CXX_RUNTIME_DLL) \ + $(MSVC_APPCRT_DLL) \ + $(MSVC_DESKTOPCRT_DLL) \ $(NULL) libs-preqs = \ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/caps/nsNullPrincipal.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/caps/nsNullPrincipal.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/caps/nsNullPrincipal.cpp 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/caps/nsNullPrincipal.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -116,7 +116,6 @@ } mURI = new nsNullPrincipalURI(str); - NS_ENSURE_TRUE(mURI, NS_ERROR_OUT_OF_MEMORY); return NS_OK; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/caps/nsScriptSecurityManager.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/caps/nsScriptSecurityManager.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/caps/nsScriptSecurityManager.cpp 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/caps/nsScriptSecurityManager.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -986,8 +986,6 @@ } nsRefPtr codebase = new nsPrincipal(); - if (!codebase) - return NS_ERROR_OUT_OF_MEMORY; nsresult rv = codebase->Init(aURI, aAppId, aInMozBrowser); if (NS_FAILED(rv)) @@ -1269,7 +1267,6 @@ // Create our system principal singleton nsRefPtr system = new nsSystemPrincipal(); - NS_ENSURE_TRUE(system, NS_ERROR_OUT_OF_MEMORY); mSystemPrincipal = system; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/CLOBBER thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/CLOBBER --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/CLOBBER 2015-01-25 22:24:21.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/CLOBBER 2015-02-03 14:33:10.000000000 +0000 @@ -22,4 +22,4 @@ # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -bug 1114669 removes nsIStreamCipher.idl, which requires a clobber according to bug 1114669 +Bug 1120369 - Needed a CLOBBER on at least Linux and OSX. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/configure.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/configure.in --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/configure.in 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/configure.in 2015-02-03 14:33:28.000000000 +0000 @@ -52,7 +52,7 @@ MOZJPEG=62 MOZPNG=10616 NSPR_VERSION=4 -NSPR_MINVER=4.10.3 +NSPR_MINVER=4.10.8 NSS_VERSION=3 dnl Set the minimum version of toolkit libs used by mozilla @@ -71,7 +71,7 @@ GIO_VERSION=2.20 STARTUP_NOTIFICATION_VERSION=0.8 DBUS_VERSION=0.60 -SQLITE_VERSION=3.8.8.1 +SQLITE_VERSION=3.8.8.2 MSMANIFEST_TOOL= @@ -296,6 +296,7 @@ if test -d "$gonkdir/system/bluetoothd"; then MOZ_B2G_BT_DAEMON=1 fi + MOZ_NFC=1 ;; *) AC_MSG_ERROR([Unsupported platform version: $ANDROID_VERSION]) @@ -496,6 +497,24 @@ MSVS_VERSION=2013 MSVC_C_RUNTIME_DLL=msvcr120.dll MSVC_CXX_RUNTIME_DLL=msvcp120.dll + elif test "$_CC_MAJOR_VERSION" = "19"; then + _CC_SUITE=14 + MSVS_VERSION=2015 + MSVC_C_RUNTIME_DLL=vcruntime140.dll + MSVC_CXX_RUNTIME_DLL=msvcp140.dll + MSVC_APPCRT_DLL=appcrt140.dll + MSVC_DESKTOPCRT_DLL=desktopcrt140.dll + + # -Wv:18 disables all warnings introduced after VS2013 + # See http://blogs.msdn.com/b/vcblog/archive/2014/11/12/improvements-to-warnings-in-the-c-compiler.aspx + CFLAGS="$CFLAGS -Wv:18" + CXXFLAGS="$CXXFLAGS -Wv:18" + + # https://connect.microsoft.com/VisualStudio/feedback/details/888527/warnings-on-dbghelp-h + # for dbghelp.h, imagehlp.h, and shobj.h + # C4091: 'typedef ': ignored on left of '' when no variable is declared + CFLAGS="$CFLAGS -wd4091" + CXXFLAGS="$CXXFLAGS -wd4091" else AC_MSG_ERROR([This version (${_CC_MAJOR_VERSION}.${_CC_MINOR_VERSION}.${_CC_BUILD_VERSION}) of the MSVC compiler is unsupported. You must install Visual C++ 2013 Update 3 or newer in order to build. @@ -504,6 +523,8 @@ AC_SUBST(MSVS_VERSION) AC_SUBST(MSVC_C_RUNTIME_DLL) AC_SUBST(MSVC_CXX_RUNTIME_DLL) + AC_SUBST(MSVC_APPCRT_DLL) + AC_SUBST(MSVC_DESKTOPCRT_DLL) # Disable SEH on clang-cl because it doesn't implement them yet. if test -z "$CLANG_CL"; then @@ -4106,23 +4127,6 @@ fi AC_SUBST(EARLY_BETA_OR_EARLIER) -# Allow the application to provide a subconfigure script -if test -f "${srcdir}/${MOZ_BUILD_APP}/configure.in" ; then - do_output_subdirs() { - if test -n "$_subconfigure_subdirs"; then - AC_MSG_ERROR([Cannot specify more than one sub-sub-configure]) - fi - _subconfigure_subdir="$1" - _subconfigure_config_args="$ac_configure_args" - } - tmpscript=`$PYTHON -c 'import os, tempfile; print tempfile.mktemp(prefix="subscript.").replace(os.sep, "/")'` || exit 1 - m4 "${srcdir}/build/autoconf/subconfigure.m4" \ - "${srcdir}/build/autoconf/altoptions.m4" \ - "${srcdir}/${MOZ_BUILD_APP}/configure.in" > $tmpscript - . $tmpscript - rm -f $tmpscript -fi - # Allow someone to change MOZ_APP_NAME and MOZ_APP_BASENAME in mozconfig MOZ_ARG_WITH_STRING(app-name, [--with-app-name=APPNAME sets MOZ_APP_NAME to APPNAME], @@ -5964,7 +5968,6 @@ fi else AC_MSG_RESULT([Couldn't find an acceptable DirectX SDK for ANGLE, needed for d3dcompiler_43.]) - AC_MSG_RESULT([ Either ignore, install DirectX SDK (June 2010 version or newer), or reconfigure with --disable-webgl.]) fi fi @@ -5990,7 +5993,6 @@ if test -z "$CROSS_COMPILE"; then if test -z "MOZ_FOUND_A_D3D_COMPILER"; then AC_MSG_ERROR([Couldn't find an acceptable D3D compiler DLL.]) - AC_MSG_ERROR([ Either install Windows SDK 8.0+, install DirectX SDK (June 2010 version or newer), or reconfigure with --disable-webgl.]) fi if test -n "$MOZ_REQUIRE_ALL_D3DCS" -a -z "$MOZ_FOUND_BOTH_D3D_COMPILERS"; then @@ -7257,6 +7259,25 @@ AC_SUBST(MOZ_GLUE_IN_PROGRAM) AC_SUBST_LIST(WIN32_CRT_LIBS) +# Allow the application to provide a subconfigure script. +# This should be after 'export MOZ_NO_DEBUG_RTL=1' since +# ldap/c-sdk/configure refers to the enviroment value. +if test -f "${srcdir}/${MOZ_BUILD_APP}/configure.in" ; then + do_output_subdirs() { + if test -n "$_subconfigure_subdirs"; then + AC_MSG_ERROR([Cannot specify more than one sub-sub-configure]) + fi + _subconfigure_subdir="$1" + _subconfigure_config_args="$ac_configure_args" + } + tmpscript=`$PYTHON -c 'import os, tempfile; print tempfile.mktemp(prefix="subscript.").replace(os.sep, "/")'` || exit 1 + m4 "${srcdir}/build/autoconf/subconfigure.m4" \ + "${srcdir}/build/autoconf/altoptions.m4" \ + "${srcdir}/${MOZ_BUILD_APP}/configure.in" > $tmpscript + . $tmpscript + rm -f $tmpscript +fi + dnl We need to wrap dlopen and related functions on Android because we use dnl our own linker. if test "$OS_TARGET" = Android; then @@ -7273,18 +7294,6 @@ export MOZ_GLUE_WRAP_LDFLAGS dnl ======================================================== -dnl JS opt-mode assertions and minidump instrumentation -dnl ======================================================== -MOZ_ARG_ENABLE_BOOL(js-diagnostics, -[ --enable-js-diagnostics - Enable JS diagnostic assertions and breakpad data], - JS_CRASH_DIAGNOSTICS=1, - JS_CRASH_DIAGNOSTICS= ) -if test -n "$JS_CRASH_DIAGNOSTICS"; then - AC_DEFINE(JS_CRASH_DIAGNOSTICS) -fi - -dnl ======================================================== dnl = Enable static checking using gcc-dehydra dnl ======================================================== diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/db/sqlite3/README.MOZILLA thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/db/sqlite3/README.MOZILLA --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/db/sqlite3/README.MOZILLA 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/db/sqlite3/README.MOZILLA 2015-02-03 14:33:28.000000000 +0000 @@ -1,4 +1,5 @@ -This is SQLite 3.8.8.1 +This is the SQLite amalgamation. +Check sqlite3.h for the version number and source id. See http://www.sqlite.org/ for more info. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/db/sqlite3/src/sqlite3.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/db/sqlite3/src/sqlite3.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/db/sqlite3/src/sqlite3.c 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/db/sqlite3/src/sqlite3.c 2015-02-03 14:33:28.000000000 +0000 @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.8.8.1. By combining all the individual C code files into this +** version 3.8.8.2. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -278,9 +278,9 @@ ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.8.8.1" +#define SQLITE_VERSION "3.8.8.2" #define SQLITE_VERSION_NUMBER 3008008 -#define SQLITE_SOURCE_ID "2015-01-20 16:51:25 f73337e3e289915a76ca96e7a05a1a8d4e890d55" +#define SQLITE_SOURCE_ID "2015-01-30 14:30:45 7757fc721220e136620a89c9d28247f28bbbc098" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -50197,7 +50197,7 @@ int sync_flags, /* Flags for OsSync() (or 0) */ u8 *zBuf /* Temporary buffer to use */ ){ - int rc; /* Return code */ + int rc = SQLITE_OK; /* Return code */ int szPage; /* Database page-size */ WalIterator *pIter = 0; /* Wal iterator context */ u32 iDbpage = 0; /* Next database page to write */ @@ -50211,104 +50211,107 @@ testcase( szPage<=32768 ); testcase( szPage>=65536 ); pInfo = walCkptInfo(pWal); - if( pInfo->nBackfill>=pWal->hdr.mxFrame ) return SQLITE_OK; + if( pInfo->nBackfillhdr.mxFrame ){ - /* Allocate the iterator */ - rc = walIteratorInit(pWal, &pIter); - if( rc!=SQLITE_OK ){ - return rc; - } - assert( pIter ); - - /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked - ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ - assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); + /* Allocate the iterator */ + rc = walIteratorInit(pWal, &pIter); + if( rc!=SQLITE_OK ){ + return rc; + } + assert( pIter ); - /* Compute in mxSafeFrame the index of the last frame of the WAL that is - ** safe to write into the database. Frames beyond mxSafeFrame might - ** overwrite database pages that are in use by active readers and thus - ** cannot be backfilled from the WAL. - */ - mxSafeFrame = pWal->hdr.mxFrame; - mxPage = pWal->hdr.nPage; - for(i=1; iaReadMark[i]; - if( mxSafeFrame>y ){ - assert( y<=pWal->hdr.mxFrame ); - rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); - if( rc==SQLITE_OK ){ - pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED); - walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); - }else if( rc==SQLITE_BUSY ){ - mxSafeFrame = y; - xBusy = 0; - }else{ - goto walcheckpoint_out; + /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked + ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ + assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); + + /* Compute in mxSafeFrame the index of the last frame of the WAL that is + ** safe to write into the database. Frames beyond mxSafeFrame might + ** overwrite database pages that are in use by active readers and thus + ** cannot be backfilled from the WAL. + */ + mxSafeFrame = pWal->hdr.mxFrame; + mxPage = pWal->hdr.nPage; + for(i=1; iaReadMark[i]; + if( mxSafeFrame>y ){ + assert( y<=pWal->hdr.mxFrame ); + rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); + if( rc==SQLITE_OK ){ + pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED); + walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); + }else if( rc==SQLITE_BUSY ){ + mxSafeFrame = y; + xBusy = 0; + }else{ + goto walcheckpoint_out; + } } } - } - - if( pInfo->nBackfillnBackfill; - /* Sync the WAL to disk */ - if( sync_flags ){ - rc = sqlite3OsSync(pWal->pWalFd, sync_flags); - } + if( pInfo->nBackfillnBackfill; - /* If the database may grow as a result of this checkpoint, hint - ** about the eventual size of the db file to the VFS layer. - */ - if( rc==SQLITE_OK ){ - i64 nReq = ((i64)mxPage * szPage); - rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); - if( rc==SQLITE_OK && nSizepDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); + /* Sync the WAL to disk */ + if( sync_flags ){ + rc = sqlite3OsSync(pWal->pWalFd, sync_flags); } - } + /* If the database may grow as a result of this checkpoint, hint + ** about the eventual size of the db file to the VFS layer. + */ + if( rc==SQLITE_OK ){ + i64 nReq = ((i64)mxPage * szPage); + rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); + if( rc==SQLITE_OK && nSizepDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); + } + } - /* Iterate through the contents of the WAL, copying data to the db file. */ - while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ - i64 iOffset; - assert( walFramePgno(pWal, iFrame)==iDbpage ); - if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ) continue; - iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE; - /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */ - rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset); - if( rc!=SQLITE_OK ) break; - iOffset = (iDbpage-1)*(i64)szPage; - testcase( IS_BIG_INT(iOffset) ); - rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); - if( rc!=SQLITE_OK ) break; - } - /* If work was actually accomplished... */ - if( rc==SQLITE_OK ){ - if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){ - i64 szDb = pWal->hdr.nPage*(i64)szPage; - testcase( IS_BIG_INT(szDb) ); - rc = sqlite3OsTruncate(pWal->pDbFd, szDb); - if( rc==SQLITE_OK && sync_flags ){ - rc = sqlite3OsSync(pWal->pDbFd, sync_flags); + /* Iterate through the contents of the WAL, copying data to the db file */ + while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ + i64 iOffset; + assert( walFramePgno(pWal, iFrame)==iDbpage ); + if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){ + continue; } + iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE; + /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */ + rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset); + if( rc!=SQLITE_OK ) break; + iOffset = (iDbpage-1)*(i64)szPage; + testcase( IS_BIG_INT(iOffset) ); + rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); + if( rc!=SQLITE_OK ) break; } + + /* If work was actually accomplished... */ if( rc==SQLITE_OK ){ - pInfo->nBackfill = mxSafeFrame; + if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){ + i64 szDb = pWal->hdr.nPage*(i64)szPage; + testcase( IS_BIG_INT(szDb) ); + rc = sqlite3OsTruncate(pWal->pDbFd, szDb); + if( rc==SQLITE_OK && sync_flags ){ + rc = sqlite3OsSync(pWal->pDbFd, sync_flags); + } + } + if( rc==SQLITE_OK ){ + pInfo->nBackfill = mxSafeFrame; + } } - } - /* Release the reader lock held while backfilling */ - walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); - } + /* Release the reader lock held while backfilling */ + walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); + } - if( rc==SQLITE_BUSY ){ - /* Reset the return code so as not to report a checkpoint failure - ** just because there are active readers. */ - rc = SQLITE_OK; + if( rc==SQLITE_BUSY ){ + /* Reset the return code so as not to report a checkpoint failure + ** just because there are active readers. */ + rc = SQLITE_OK; + } } /* If this is an SQLITE_CHECKPOINT_RESTART or TRUNCATE operation, and the @@ -50323,7 +50326,7 @@ }else if( eMode>=SQLITE_CHECKPOINT_RESTART ){ u32 salt1; sqlite3_randomness(4, &salt1); - assert( mxSafeFrame==pWal->hdr.mxFrame ); + assert( pInfo->nBackfill==pWal->hdr.mxFrame ); rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1); if( rc==SQLITE_OK ){ if( eMode==SQLITE_CHECKPOINT_TRUNCATE ){ @@ -128369,6 +128372,7 @@ rc = SQLITE_ERROR; sqlite3ErrorWithMsg(db, SQLITE_ERROR, "unknown database: %s", zDb); }else{ + db->busyHandler.nBusy = 0; rc = sqlite3Checkpoint(db, iDb, eMode, pnLog, pnCkpt); sqlite3Error(db, rc); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/db/sqlite3/src/sqlite3.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/db/sqlite3/src/sqlite3.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/db/sqlite3/src/sqlite3.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/db/sqlite3/src/sqlite3.h 2015-02-03 14:33:28.000000000 +0000 @@ -107,9 +107,9 @@ ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.8.8.1" +#define SQLITE_VERSION "3.8.8.2" #define SQLITE_VERSION_NUMBER 3008008 -#define SQLITE_SOURCE_ID "2015-01-20 16:51:25 f73337e3e289915a76ca96e7a05a1a8d4e890d55" +#define SQLITE_SOURCE_ID "2015-01-30 14:30:45 7757fc721220e136620a89c9d28247f28bbbc098" /* ** CAPI3REF: Run-Time Library Version Numbers diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/docshell/base/nsDocShell.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/docshell/base/nsDocShell.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/docshell/base/nsDocShell.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/docshell/base/nsDocShell.cpp 2015-02-03 14:33:28.000000000 +0000 @@ -842,6 +842,7 @@ mInPrivateBrowsing(false), mUseRemoteTabs(false), mDeviceSizeIsPageSize(false), + mWindowDraggingAllowed(false), mCanExecuteScripts(false), mFiredUnloadEvent(false), mEODForCurrentDocument(false), @@ -3013,6 +3014,35 @@ } } +NS_IMETHODIMP +nsDocShell::SetWindowDraggingAllowed(bool aValue) +{ + nsRefPtr parent = GetParentDocshell(); + if (!aValue && mItemType == typeChrome && !parent) { + // Window dragging is always allowed for top level + // chrome docshells. + return NS_ERROR_FAILURE; + } + mWindowDraggingAllowed = aValue; + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::GetWindowDraggingAllowed(bool* aValue) +{ + // window dragging regions in CSS (-moz-window-drag:drag) + // can be slow. Default behavior is to only allow it for + // chrome top level windows. + nsRefPtr parent = GetParentDocshell(); + if (mItemType == typeChrome && !parent) { + // Top level chrome window + *aValue = true; + } else { + *aValue = mWindowDraggingAllowed; + } + return NS_OK; +} + void nsDocShell::ClearProfileTimelineMarkers() { @@ -3383,10 +3413,8 @@ { SetIsActive(value); } - if (NS_SUCCEEDED(parentAsDocShell->GetIsPrerendered(&value))) { - if (value) { - SetIsPrerendered(true); - } + if (parentAsDocShell->GetIsPrerendered()) { + SetIsPrerendered(true); } if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) { value = false; @@ -9637,9 +9665,13 @@ nsAutoCString spec; if (aURI) aURI->GetSpec(spec); + nsAutoString features; + if (mInPrivateBrowsing) { + features.AssignLiteral("private"); + } rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec), name, // window name - EmptyString(), // Features + features, getter_AddRefs(newWin)); // In some cases the Open call doesn't actually result in a new @@ -13678,7 +13710,7 @@ *aOut = tabChild->IsAsyncPanZoomEnabled(); return NS_OK; } - *aOut = false; + *aOut = Preferences::GetBool("layers.async-pan-zoom.enabled", false); return NS_OK; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/docshell/base/nsDocShell.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/docshell/base/nsDocShell.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/docshell/base/nsDocShell.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/docshell/base/nsDocShell.h 2015-02-03 14:33:28.000000000 +0000 @@ -885,6 +885,7 @@ bool mInPrivateBrowsing; bool mUseRemoteTabs; bool mDeviceSizeIsPageSize; + bool mWindowDraggingAllowed; // Because scriptability depends on the mAllowJavascript values of our // ancestors, we cache the effective scriptability and recompute it when diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/docshell/base/nsIDocShell.idl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/docshell/base/nsIDocShell.idl --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/docshell/base/nsIDocShell.idl 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/docshell/base/nsIDocShell.idl 2015-02-03 14:33:28.000000000 +0000 @@ -54,7 +54,7 @@ typedef unsigned long nsLoadFlags; -[scriptable, builtinclass, uuid(e0e833fe-3a5b-48b0-8684-a097e09c0723)] +[scriptable, builtinclass, uuid(888fcf04-a69b-11e4-8d33-6fbb72d2eb03)] interface nsIDocShell : nsIDocShellTreeItem { /** @@ -624,7 +624,7 @@ * native code to be able to put a docshell in prerendering. */ [noscript] void SetIsPrerendered(in boolean prerendered); - readonly attribute boolean isPrerendered; + [infallible] readonly attribute boolean isPrerendered; /** * The ID of the docshell in the session history. @@ -1053,4 +1053,11 @@ * Holds the id of the payment request associated with this docshell if any. */ attribute DOMString paymentRequestId; + + /** + * Allow usage of -moz-window-dragging:drag for content docshells. + * True for top level chrome docshells. Throws if set to false with + * top level chrome docshell. + */ + attribute boolean windowDraggingAllowed; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/animation/AnimationPlayer.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/animation/AnimationPlayer.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/animation/AnimationPlayer.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/animation/AnimationPlayer.cpp 2015-02-03 14:33:29.000000000 +0000 @@ -180,6 +180,27 @@ ResumeAt(mTimeline->GetCurrentTime().Value()); } +Nullable +AnimationPlayer::GetCurrentOrPendingStartTime() const +{ + Nullable result; + + if (!mStartTime.IsNull()) { + result = mStartTime; + return result; + } + + if (mPendingReadyTime.IsNull() || mHoldTime.IsNull()) { + return result; + } + + // Calculate the equivalent start time from the pending ready time. + // This is the same as the calculation performed in ResumeAt and will + // need to incorporate the playbackRate when implemented (bug 1127380). + result.SetValue(mPendingReadyTime.Value() - mHoldTime.Value()); + return result; +} + void AnimationPlayer::Cancel() { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/animation/AnimationPlayer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/animation/AnimationPlayer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/animation/AnimationPlayer.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/animation/AnimationPlayer.h 2015-02-03 14:33:29.000000000 +0000 @@ -149,6 +149,26 @@ // to. void StartNow(); + /** + * When StartOnNextTick is called, we store the ready time but we don't apply + * it until the next tick. In the meantime, GetStartTime() will return null. + * + * However, if we build layer animations again before the next tick, we + * should initialize them with the start time that GetStartTime() will return + * on the next tick. + * + * If we were to simply set the start time of layer animations to null, their + * start time would be updated to the current wallclock time when rendering + * finishes, thus making them out of sync with the start time stored here. + * This, in turn, will make the animation jump backwards when we build + * animations on the next tick and apply the start time stored here. + * + * This method returns the start time, if resolved. Otherwise, if we have + * a pending ready time, it returns the corresponding start time. If neither + * of those are available, it returns null. + */ + Nullable GetCurrentOrPendingStartTime() const; + void Cancel(); const nsString& Name() const { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/animation/AnimationTimeline.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/animation/AnimationTimeline.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/animation/AnimationTimeline.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/animation/AnimationTimeline.cpp 2015-02-03 14:33:29.000000000 +0000 @@ -15,7 +15,7 @@ namespace mozilla { namespace dom { -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationTimeline, mDocument) +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationTimeline, mDocument, mWindow) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AnimationTimeline, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AnimationTimeline, Release) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/animation/AnimationTimeline.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/animation/AnimationTimeline.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/animation/AnimationTimeline.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/animation/AnimationTimeline.h 2015-02-03 14:33:29.000000000 +0000 @@ -10,6 +10,7 @@ #include "nsCycleCollectionParticipant.h" #include "mozilla/Attributes.h" #include "mozilla/TimeStamp.h" +#include "nsIGlobalObject.h" #include "js/TypeDecls.h" #include "nsIDocument.h" #include "nsRefreshDriver.h" @@ -24,7 +25,9 @@ public: explicit AnimationTimeline(nsIDocument* aDocument) : mDocument(aDocument) + , mWindow(aDocument->GetParentObject()) { + MOZ_ASSERT(mWindow); } protected: @@ -36,7 +39,7 @@ nsIGlobalObject* GetParentObject() const { - return mDocument->GetParentObject(); + return mWindow; } virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; @@ -70,7 +73,12 @@ protected: TimeStamp GetCurrentTimeStamp() const; + // Sometimes documents can be given a new window, or windows can be given a + // new document (e.g. document.open()). Since GetParentObject is required to + // _always_ return the same object it can't get the window from our + // mDocument, which is why we have pointers to both our document and window. nsCOMPtr mDocument; + nsCOMPtr mWindow; // The most recently used refresh driver time. This is used in cases where // we don't have a refresh driver (e.g. because we are in a display:none diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/apps/ImportExport.jsm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/apps/ImportExport.jsm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/apps/ImportExport.jsm 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/apps/ImportExport.jsm 2015-02-03 14:33:29.000000000 +0000 @@ -377,6 +377,9 @@ if (meta.id in DOMApplicationRegistry.webapps) { throw "DuplicateOrigin"; } + // We need to change the app directory name to match the new meta.id + appDir.moveTo(appDir.parent, meta.id); + meta.origin = uri.prePath; } } else { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/apps/PermissionsTable.jsm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/apps/PermissionsTable.jsm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/apps/PermissionsTable.jsm 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/apps/PermissionsTable.jsm 2015-02-03 14:33:29.000000000 +0000 @@ -524,6 +524,12 @@ trusted: DENY_ACTION, privileged: DENY_ACTION, certified: ALLOW_ACTION + }, + "requestsync-manager": { + app: DENY_ACTION, + trusted: DENY_ACTION, + privileged: DENY_ACTION, + certified: ALLOW_ACTION } }; @@ -662,7 +668,7 @@ AllPossiblePermissions.concat(["indexedDB", "offline-app", "pin-app"]); })(); -this.isExplicitInPermissionsTable = function(aPermName, aIntStatus) { +this.isExplicitInPermissionsTable = function(aPermName, aIntStatus, aAppKind) { // Check to see if the 'webapp' is app/privileged/certified. let appStatus; @@ -674,7 +680,7 @@ appStatus = "privileged"; break; default: // If it isn't certified or privileged, it's app - appStatus = "app"; + appStatus = aAppKind == "hosted-trusted" ? "trusted" : "app"; break; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/CompositionStringSynthesizer.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/CompositionStringSynthesizer.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/CompositionStringSynthesizer.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/CompositionStringSynthesizer.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,160 +0,0 @@ -/* -*- 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 "CompositionStringSynthesizer.h" -#include "nsContentUtils.h" -#include "nsIDocShell.h" -#include "nsIFrame.h" -#include "nsIPresShell.h" -#include "nsIWidget.h" -#include "nsPIDOMWindow.h" -#include "nsView.h" -#include "mozilla/TextEvents.h" - -namespace mozilla { -namespace dom { - -NS_IMPL_ISUPPORTS(CompositionStringSynthesizer, - nsICompositionStringSynthesizer) - -CompositionStringSynthesizer::CompositionStringSynthesizer( - nsPIDOMWindow* aWindow) -{ - mWindow = do_GetWeakReference(aWindow); - mClauses = new TextRangeArray(); - ClearInternal(); -} - -CompositionStringSynthesizer::~CompositionStringSynthesizer() -{ -} - -void -CompositionStringSynthesizer::ClearInternal() -{ - mString.Truncate(); - mClauses->Clear(); - mCaret.mRangeType = 0; -} - -nsIWidget* -CompositionStringSynthesizer::GetWidget() -{ - nsCOMPtr window = do_QueryReferent(mWindow); - if (!window) { - return nullptr; - } - nsIDocShell *docShell = window->GetDocShell(); - if (!docShell) { - return nullptr; - } - nsCOMPtr presShell = docShell->GetPresShell(); - if (!presShell) { - return nullptr; - } - nsIFrame* frame = presShell->GetRootFrame(); - if (!frame) { - return nullptr; - } - return frame->GetView()->GetNearestWidget(nullptr); -} - -NS_IMETHODIMP -CompositionStringSynthesizer::SetString(const nsAString& aString) -{ - nsCOMPtr widget = GetWidget(); - NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE); - - mString = aString; - return NS_OK; -} - -NS_IMETHODIMP -CompositionStringSynthesizer::AppendClause(uint32_t aLength, - uint32_t aAttribute) -{ - nsCOMPtr widget = GetWidget(); - NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE); - - switch (aAttribute) { - case ATTR_RAWINPUT: - case ATTR_SELECTEDRAWTEXT: - case ATTR_CONVERTEDTEXT: - case ATTR_SELECTEDCONVERTEDTEXT: { - TextRange textRange; - textRange.mStartOffset = - mClauses->IsEmpty() ? 0 : mClauses->LastElement().mEndOffset; - textRange.mEndOffset = textRange.mStartOffset + aLength; - textRange.mRangeType = aAttribute; - mClauses->AppendElement(textRange); - return NS_OK; - } - default: - return NS_ERROR_INVALID_ARG; - } -} - -NS_IMETHODIMP -CompositionStringSynthesizer::SetCaret(uint32_t aOffset, uint32_t aLength) -{ - nsCOMPtr widget = GetWidget(); - NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE); - - mCaret.mStartOffset = aOffset; - mCaret.mEndOffset = mCaret.mStartOffset + aLength; - mCaret.mRangeType = NS_TEXTRANGE_CARETPOSITION; - return NS_OK; -} - -NS_IMETHODIMP -CompositionStringSynthesizer::DispatchEvent(bool* aDefaultPrevented) -{ - NS_ENSURE_ARG_POINTER(aDefaultPrevented); - nsCOMPtr widget = GetWidget(); - NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE); - - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } - - if (!mClauses->IsEmpty() && - mClauses->LastElement().mEndOffset != mString.Length()) { - NS_WARNING("Sum of length of the all clauses must be same as the string " - "length"); - ClearInternal(); - return NS_ERROR_ILLEGAL_VALUE; - } - if (mCaret.mRangeType == NS_TEXTRANGE_CARETPOSITION) { - if (mCaret.mEndOffset > mString.Length()) { - NS_WARNING("Caret position is out of the composition string"); - ClearInternal(); - return NS_ERROR_ILLEGAL_VALUE; - } - mClauses->AppendElement(mCaret); - } - - WidgetCompositionEvent compChangeEvent(true, NS_COMPOSITION_CHANGE, widget); - compChangeEvent.time = PR_IntervalNow(); - compChangeEvent.mData = mString; - if (!mClauses->IsEmpty()) { - compChangeEvent.mRanges = mClauses; - } - - // XXX How should we set false for this on b2g? - compChangeEvent.mFlags.mIsSynthesizedForTests = true; - - nsEventStatus status = nsEventStatus_eIgnore; - nsresult rv = widget->DispatchEvent(&compChangeEvent, status); - *aDefaultPrevented = (status == nsEventStatus_eConsumeNoDefault); - - ClearInternal(); - - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -} // namespace dom -} // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/CompositionStringSynthesizer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/CompositionStringSynthesizer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/CompositionStringSynthesizer.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/CompositionStringSynthesizer.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -/* -*- 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 mozilla_dom_compositionstringsynthesizer_h__ -#define mozilla_dom_compositionstringsynthesizer_h__ - -#include "nsICompositionStringSynthesizer.h" -#include "nsString.h" -#include "nsWeakReference.h" -#include "mozilla/Attributes.h" -#include "mozilla/TextRange.h" - -class nsIWidget; -class nsPIDOMWindow; - -namespace mozilla { -namespace dom { - -class CompositionStringSynthesizer MOZ_FINAL : - public nsICompositionStringSynthesizer -{ -public: - explicit CompositionStringSynthesizer(nsPIDOMWindow* aWindow); - - NS_DECL_ISUPPORTS - NS_DECL_NSICOMPOSITIONSTRINGSYNTHESIZER - -private: - ~CompositionStringSynthesizer(); - - nsWeakPtr mWindow; // refers an instance of nsPIDOMWindow - nsString mString; - nsRefPtr mClauses; - TextRange mCaret; - - nsIWidget* GetWidget(); - void ClearInternal(); -}; - -} // namespace dom -} // namespace mozilla - -#endif // #ifndef mozilla_dom_compositionstringsynthesizer_h__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/Console.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/Console.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/Console.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/Console.cpp 2015-02-03 14:33:29.000000000 +0000 @@ -6,7 +6,9 @@ #include "mozilla/dom/Console.h" #include "mozilla/dom/ConsoleBinding.h" +#include "mozilla/dom/BlobBinding.h" #include "mozilla/dom/Exceptions.h" +#include "mozilla/dom/File.h" #include "mozilla/dom/ToJSValue.h" #include "mozilla/Maybe.h" #include "nsCycleCollectionParticipant.h" @@ -41,9 +43,10 @@ // console.trace(). #define DEFAULT_MAX_STACKTRACE_DEPTH 200 -// This tag is used in the Structured Clone Algorithm to move js values from +// This tags are used in the Structured Clone Algorithm to move js values from // worker thread to main thread -#define CONSOLE_TAG JS_SCTAG_USER_MIN +#define CONSOLE_TAG_STRING JS_SCTAG_USER_MIN +#define CONSOLE_TAG_BLOB JS_SCTAG_USER_MIN + 1 using namespace mozilla::dom::exceptions; using namespace mozilla::dom::workers; @@ -51,6 +54,14 @@ namespace mozilla { namespace dom { +struct +ConsoleStructuredCloneData +{ + nsCOMPtr mParent; + nsTArray mStrings; + nsTArray> mFiles; +}; + /** * Console API in workers uses the Structured Clone Algorithm to move any value * from the worker thread to the main-thread. Some object cannot be moved and, @@ -63,29 +74,47 @@ static JSObject* ConsoleStructuredCloneCallbacksRead(JSContext* aCx, JSStructuredCloneReader* /* unused */, - uint32_t aTag, uint32_t aData, + uint32_t aTag, uint32_t aIndex, void* aClosure) { AssertIsOnMainThread(); + ConsoleStructuredCloneData* data = + static_cast(aClosure); + MOZ_ASSERT(data); - if (aTag != CONSOLE_TAG) { - return nullptr; - } + if (aTag == CONSOLE_TAG_STRING) { + MOZ_ASSERT(data->mStrings.Length() > aIndex); - nsTArray* strings = static_cast*>(aClosure); - MOZ_ASSERT(strings->Length() > aData); + JS::Rooted value(aCx); + if (!xpc::StringToJsval(aCx, data->mStrings.ElementAt(aIndex), &value)) { + return nullptr; + } - JS::Rooted value(aCx); - if (!xpc::StringToJsval(aCx, strings->ElementAt(aData), &value)) { - return nullptr; + JS::Rooted obj(aCx); + if (!JS_ValueToObject(aCx, value, &obj)) { + return nullptr; + } + + return obj; } - JS::Rooted obj(aCx); - if (!JS_ValueToObject(aCx, value, &obj)) { - return nullptr; + if (aTag == CONSOLE_TAG_BLOB) { + MOZ_ASSERT(data->mFiles.Length() > aIndex); + + JS::Rooted val(aCx); + { + nsRefPtr file = + new File(data->mParent, data->mFiles.ElementAt(aIndex)); + if (!GetOrCreateDOMReflector(aCx, file, &val)) { + return nullptr; + } + } + + return &val.toObject(); } - return obj; + MOZ_CRASH("No other tags are supported."); + return nullptr; } // This method is called by the Structured Clone Algorithm when some data has @@ -96,6 +125,21 @@ JS::Handle aObj, void* aClosure) { + ConsoleStructuredCloneData* data = + static_cast(aClosure); + MOZ_ASSERT(data); + + nsRefPtr file; + if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, file)) && + file->Impl()->MayBeClonedToOtherThreads()) { + if (!JS_WriteUint32Pair(aWriter, CONSOLE_TAG_BLOB, data->mFiles.Length())) { + return false; + } + + data->mFiles.AppendElement(file->Impl()); + return true; + } + JS::Rooted value(aCx, JS::ObjectOrNullValue(aObj)); JS::Rooted jsString(aCx, JS::ToString(aCx, value)); if (!jsString) { @@ -107,14 +151,12 @@ return false; } - nsTArray* strings = static_cast*>(aClosure); - - if (!JS_WriteUint32Pair(aWriter, CONSOLE_TAG, strings->Length())) { + if (!JS_WriteUint32Pair(aWriter, CONSOLE_TAG_STRING, + data->mStrings.Length())) { return false; } - strings->AppendElement(string); - + data->mStrings.AppendElement(string); return true; } @@ -414,7 +456,7 @@ JS::Rooted value(aCx, JS::ObjectValue(*arguments)); - if (!mArguments.write(aCx, value, &gConsoleCallbacks, &mStrings)) { + if (!mArguments.write(aCx, value, &gConsoleCallbacks, &mData)) { return false; } @@ -451,8 +493,13 @@ mCallData->SetIDs(id, frame.mFilename); } + // Now we could have the correct window (if we are not window-less). + mData.mParent = aInnerWindow; + ProcessCallData(aCx); mCallData->CleanupJSObjects(); + + mData.mParent = nullptr; } private: @@ -462,7 +509,7 @@ ClearException ce(aCx); JS::Rooted argumentsValue(aCx); - if (!mArguments.read(aCx, &argumentsValue, &gConsoleCallbacks, &mStrings)) { + if (!mArguments.read(aCx, &argumentsValue, &gConsoleCallbacks, &mData)) { return; } @@ -494,7 +541,7 @@ ConsoleCallData* mCallData; JSAutoStructuredCloneBuffer mArguments; - nsTArray mStrings; + ConsoleStructuredCloneData mData; }; // This runnable calls ProfileMethod() on the console on the main-thread. @@ -539,7 +586,7 @@ JS::Rooted value(aCx, JS::ObjectValue(*arguments)); - if (!mBuffer.write(aCx, value, &gConsoleCallbacks, &mStrings)) { + if (!mBuffer.write(aCx, value, &gConsoleCallbacks, &mData)) { return false; } @@ -552,8 +599,14 @@ { ClearException ce(aCx); + // Now we could have the correct window (if we are not window-less). + mData.mParent = aInnerWindow; + JS::Rooted argumentsValue(aCx); - if (!mBuffer.read(aCx, &argumentsValue, &gConsoleCallbacks, &mStrings)) { + bool ok = mBuffer.read(aCx, &argumentsValue, &gConsoleCallbacks, &mData); + mData.mParent = nullptr; + + if (!ok) { return; } @@ -585,7 +638,7 @@ Sequence mArguments; JSAutoStructuredCloneBuffer mBuffer; - nsTArray mStrings; + ConsoleStructuredCloneData mData; }; NS_IMPL_CYCLE_COLLECTION_CLASS(Console) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/DOMParser.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/DOMParser.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/DOMParser.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/DOMParser.cpp 2015-02-03 14:33:29.000000000 +0000 @@ -109,7 +109,7 @@ nsAutoCString utf8str; // Convert from UTF16 to UTF8 using fallible allocations - if (!AppendUTF16toUTF8(str, utf8str, mozilla::fallible_t())) { + if (!AppendUTF16toUTF8(str, utf8str, mozilla::fallible)) { return NS_ERROR_OUT_OF_MEMORY; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/Element.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/Element.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/Element.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/Element.cpp 2015-02-03 14:33:29.000000000 +0000 @@ -1198,6 +1198,12 @@ Element::RemoveAttributeNode(Attr& aAttribute, ErrorResult& aError) { + Element *elem = aAttribute.GetElement(); + if (elem != this) { + aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR); + return nullptr; + } + OwnerDoc()->WarnOnceAbout(nsIDocument::eRemoveAttributeNode); nsAutoString nameSpaceURI; aAttribute.NodeInfo()->GetNamespaceURI(nameSpaceURI); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/File.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/File.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/File.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/File.cpp 2015-02-03 14:33:29.000000000 +0000 @@ -626,7 +626,7 @@ const ChromeFilePropertyBag& aBag, ErrorResult& aRv) { - if (!nsContentUtils::IsCallerChrome()) { + if (!nsContentUtils::ThreadsafeIsCallerChrome()) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } @@ -648,6 +648,7 @@ const ChromeFilePropertyBag& aBag, ErrorResult& aRv) { + MOZ_ASSERT(NS_IsMainThread()); if (!nsContentUtils::IsCallerChrome()) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; @@ -672,7 +673,7 @@ const ChromeFilePropertyBag& aBag, ErrorResult& aRv) { - if (!nsContentUtils::IsCallerChrome()) { + if (!nsContentUtils::ThreadsafeIsCallerChrome()) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/FragmentOrElement.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/FragmentOrElement.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/FragmentOrElement.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/FragmentOrElement.cpp 2015-02-03 14:33:29.000000000 +0000 @@ -2202,7 +2202,7 @@ bool ToString(nsAString& aOut) { - if (!aOut.SetCapacity(mLength, fallible_t())) { + if (!aOut.SetCapacity(mLength, fallible)) { return false; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/moz.build 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/moz.build 2015-02-03 14:33:30.000000000 +0000 @@ -138,6 +138,7 @@ EXPORTS.mozilla += [ 'CORSMode.h', 'FeedWriterEnabled.h', + 'TextInputProcessor.h', ] EXPORTS.mozilla.dom += [ @@ -201,7 +202,6 @@ 'BarProps.cpp', 'ChildIterator.cpp', 'Comment.cpp', - 'CompositionStringSynthesizer.cpp', 'Console.cpp', 'Crypto.cpp', 'DirectionalityUtils.cpp', @@ -262,7 +262,6 @@ 'nsFocusManager.cpp', 'nsFormData.cpp', 'nsFrameLoader.cpp', - 'nsFrameMessageManager.cpp', 'nsGenConImageContent.cpp', 'nsGenericDOMDataNode.cpp', 'nsGkAtoms.cpp', @@ -323,6 +322,7 @@ 'StyleSheetList.cpp', 'SubtleCrypto.cpp', 'Text.cpp', + 'TextInputProcessor.cpp', 'ThirdPartyUtil.cpp', 'TreeWalker.cpp', 'URL.cpp', @@ -344,6 +344,8 @@ 'nsContentUtils.cpp', # this file doesn't like windows.h 'nsDOMWindowUtils.cpp', + # Conflicts with windows.h's definition of SendMessage. + 'nsFrameMessageManager.cpp', # This file has a #error "Never include windows.h in this file!" 'nsGlobalWindow.cpp', # Conflicts with windows.h's definition of LoadImage. @@ -403,7 +405,6 @@ '/dom/xslt/xpath', '/dom/xul', '/image/src', - '/js/ipc', '/js/xpconnect/src', '/js/xpconnect/wrappers', '/layout/base', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsContentList.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsContentList.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsContentList.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsContentList.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -223,9 +223,6 @@ ContentListHashEntry *entry = nullptr; // First we look in our hashtable. Then we create a content list if needed if (gContentListHashTable.IsInitialized()) { - - // A PL_DHASH_ADD is equivalent to a PL_DHASH_LOOKUP for cases - // when the entry is already in the hashtable. entry = static_cast (PL_DHashTableAdd(&gContentListHashTable, &hashKey)); if (entry) @@ -244,8 +241,7 @@ } else { htmlAtom = xmlAtom; } - list = new nsContentList(aRootNode, aMatchNameSpaceId, - htmlAtom, xmlAtom); + list = new nsContentList(aRootNode, aMatchNameSpaceId, htmlAtom, xmlAtom); if (entry) { entry->mContentList = list; } @@ -335,8 +331,6 @@ if (gFuncStringContentListHashTable.IsInitialized()) { nsFuncStringCacheKey hashKey(aRootNode, aFunc, aString); - // A PL_DHASH_ADD is equivalent to a PL_DHASH_LOOKUP for cases - // when the entry is already in the hashtable. entry = static_cast (PL_DHashTableAdd(&gFuncStringContentListHashTable, &hashKey)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsContentUtils.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsContentUtils.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsContentUtils.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsContentUtils.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -397,10 +397,10 @@ lm->~EventListenerManagerMapEntry(); } -class SameOriginChecker MOZ_FINAL : public nsIChannelEventSink, - public nsIInterfaceRequestor +class SameOriginCheckerImpl MOZ_FINAL : public nsIChannelEventSink, + public nsIInterfaceRequestor { - ~SameOriginChecker() {} + ~SameOriginCheckerImpl() {} NS_DECL_ISUPPORTS NS_DECL_NSICHANNELEVENTSINK @@ -732,7 +732,7 @@ const char16_t* start = aAsciiBase64String.BeginReading(); const char16_t* end = aAsciiBase64String.EndReading(); nsString trimmedString; - if (!trimmedString.SetCapacity(aAsciiBase64String.Length(), fallible_t())) { + if (!trimmedString.SetCapacity(aAsciiBase64String.Length(), fallible)) { return NS_ERROR_DOM_INVALID_CHARACTER_ERR; } while (start < end) { @@ -3941,8 +3941,8 @@ EventListenerManagerMapEntry *entry = static_cast - (PL_DHashTableLookup(&sEventListenerManagersHash, aNode)); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + (PL_DHashTableSearch(&sEventListenerManagersHash, aNode)); + if (entry) { CycleCollectionNoteChild(cb, entry->mListenerManager.get(), "[via hash] mListenerManager"); } @@ -3981,7 +3981,7 @@ if (!aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) { return nullptr; } - + if (!sEventListenerManagersHash.IsInitialized()) { // We're already shut down, don't bother creating an event listener // manager. @@ -3991,8 +3991,8 @@ EventListenerManagerMapEntry *entry = static_cast - (PL_DHashTableLookup(&sEventListenerManagersHash, aNode)); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + (PL_DHashTableSearch(&sEventListenerManagersHash, aNode)); + if (entry) { return entry->mListenerManager; } @@ -4006,8 +4006,8 @@ if (sEventListenerManagersHash.IsInitialized()) { EventListenerManagerMapEntry *entry = static_cast - (PL_DHashTableLookup(&sEventListenerManagersHash, aNode)); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + (PL_DHashTableSearch(&sEventListenerManagersHash, aNode)); + if (entry) { nsRefPtr listenerManager; listenerManager.swap(entry->mListenerManager); // Remove the entry and *then* do operations that could cause further @@ -4425,20 +4425,20 @@ static bool AppendNodeTextContentsRecurse(nsINode* aNode, nsAString& aResult, - const mozilla::fallible_t&) + const fallible_t& aFallible) { for (nsIContent* child = aNode->GetFirstChild(); child; child = child->GetNextSibling()) { if (child->IsElement()) { bool ok = AppendNodeTextContentsRecurse(child, aResult, - mozilla::fallible_t()); + aFallible); if (!ok) { return false; } } else if (child->IsNodeOfType(nsINode::eTEXT)) { - bool ok = child->AppendTextTo(aResult, mozilla::fallible_t()); + bool ok = child->AppendTextTo(aResult, aFallible); if (!ok) { return false; } @@ -4452,21 +4452,21 @@ bool nsContentUtils::AppendNodeTextContent(nsINode* aNode, bool aDeep, nsAString& aResult, - const mozilla::fallible_t&) + const fallible_t& aFallible) { if (aNode->IsNodeOfType(nsINode::eTEXT)) { return static_cast(aNode)->AppendTextTo(aResult, - mozilla::fallible_t()); + aFallible); } else if (aDeep) { - return AppendNodeTextContentsRecurse(aNode, aResult, mozilla::fallible_t()); + return AppendNodeTextContentsRecurse(aNode, aResult, aFallible); } else { for (nsIContent* child = aNode->GetFirstChild(); child; child = child->GetNextSibling()) { if (child->IsNodeOfType(nsINode::eTEXT)) { - bool ok = child->AppendTextTo(aResult, mozilla::fallible_t()); + bool ok = child->AppendTextTo(aResult, fallible); if (!ok) { return false; } @@ -5648,11 +5648,11 @@ /* static */ nsIInterfaceRequestor* -nsContentUtils::GetSameOriginChecker() +nsContentUtils::SameOriginChecker() { if (!sSameOriginChecker) { - sSameOriginChecker = new SameOriginChecker(); - NS_IF_ADDREF(sSameOriginChecker); + sSameOriginChecker = new SameOriginCheckerImpl(); + NS_ADDREF(sSameOriginChecker); } return sSameOriginChecker; } @@ -5683,15 +5683,15 @@ return rv; } -NS_IMPL_ISUPPORTS(SameOriginChecker, +NS_IMPL_ISUPPORTS(SameOriginCheckerImpl, nsIChannelEventSink, nsIInterfaceRequestor) NS_IMETHODIMP -SameOriginChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel, - nsIChannel *aNewChannel, - uint32_t aFlags, - nsIAsyncVerifyRedirectCallback *cb) +SameOriginCheckerImpl::AsyncOnChannelRedirect(nsIChannel* aOldChannel, + nsIChannel* aNewChannel, + uint32_t aFlags, + nsIAsyncVerifyRedirectCallback* cb) { NS_PRECONDITION(aNewChannel, "Redirecting to null channel?"); @@ -5704,7 +5704,7 @@ } NS_IMETHODIMP -SameOriginChecker::GetInterface(const nsIID & aIID, void **aResult) +SameOriginCheckerImpl::GetInterface(const nsIID& aIID, void** aResult) { return QueryInterface(aIID, aResult); } @@ -6242,7 +6242,7 @@ void nsContentUtils::PlatformToDOMLineBreaks(nsString &aString) { - if (!PlatformToDOMLineBreaks(aString, mozilla::fallible_t())) { + if (!PlatformToDOMLineBreaks(aString, fallible)) { aString.AllocFailed(aString.Length()); } } @@ -6962,7 +6962,7 @@ nsContentUtils::GetNodeTextContent(nsINode* aNode, bool aDeep, nsAString& aResult) { aResult.Truncate(); - return AppendNodeTextContent(aNode, aDeep, aResult, mozilla::fallible_t()); + return AppendNodeTextContent(aNode, aDeep, aResult, fallible); } void diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsContentUtils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsContentUtils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsContentUtils.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsContentUtils.h 2015-02-03 14:33:30.000000000 +0000 @@ -1635,7 +1635,7 @@ // Returns NS_OK for same origin, error (NS_ERROR_DOM_BAD_URI) if not. static nsresult CheckSameOrigin(nsIChannel *aOldChannel, nsIChannel *aNewChannel); - static nsIInterfaceRequestor* GetSameOriginChecker(); + static nsIInterfaceRequestor* SameOriginChecker(); /** * Get the Origin of the passed in nsIPrincipal or nsIURI. If the passed in diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsCopySupport.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsCopySupport.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsCopySupport.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsCopySupport.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -97,7 +97,8 @@ // Do the first and potentially trial encoding as preformatted and raw. uint32_t flags = aFlags | nsIDocumentEncoder::OutputPreformatted - | nsIDocumentEncoder::OutputRaw; + | nsIDocumentEncoder::OutputRaw + | nsIDocumentEncoder::OutputForPlainTextClipboardCopy; nsCOMPtr domDoc = do_QueryInterface(aDoc); NS_ASSERTION(domDoc, "Need a document"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsDocument.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsDocument.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsDocument.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsDocument.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -1337,8 +1337,7 @@ nsIDocument* doc = aRequestingNode->OwnerDoc(); - nsCOMPtr req = nsContentUtils::GetSameOriginChecker(); - NS_ENSURE_TRUE(req, NS_ERROR_OUT_OF_MEMORY); + nsCOMPtr req = nsContentUtils::SameOriginChecker(); nsCOMPtr loadGroup = doc->GetDocumentLoadGroup(); nsCOMPtr channel; @@ -2291,6 +2290,11 @@ ResetToURI(uri, aLoadGroup, principal); + // Note that, since mTiming does not change during a reset, the + // navigationStart time remains unchanged and therefore any future new + // timeline will have the same global clock time as the old one. + mAnimationTimeline = nullptr; + nsCOMPtr bag = do_QueryInterface(aChannel); if (bag) { nsCOMPtr baseURI; @@ -3960,9 +3964,9 @@ if (mSubDocuments) { SubDocMapEntry *entry = static_cast - (PL_DHashTableLookup(mSubDocuments, aElement)); + (PL_DHashTableSearch(mSubDocuments, aElement)); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + if (entry) { PL_DHashTableRawRemove(mSubDocuments, entry); } } @@ -4016,9 +4020,9 @@ if (mSubDocuments && aContent->IsElement()) { SubDocMapEntry *entry = static_cast - (PL_DHashTableLookup(mSubDocuments, aContent->AsElement())); + (PL_DHashTableSearch(mSubDocuments, aContent->AsElement())); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + if (entry) { return entry->mSubDocument; } } @@ -4713,7 +4717,23 @@ // still test false at this point and no state change will happen) or we're // doing the initial document load and don't want to fire the event for this // change. + dom::VisibilityState oldState = mVisibilityState; mVisibilityState = GetVisibilityState(); + // When the visibility is changed, notify it to observers. + // Some observers need the notification, for example HTMLMediaElement uses + // it to update internal media resource allocation. + // When video is loaded via VideoDocument, HTMLMediaElement and MediaDecoder + // creation are already done before nsDocument::SetScriptGlobalObject() call. + // MediaDecoder decides whether starting decoding is decided based on + // document's visibility. When the MediaDecoder is created, + // nsDocument::SetScriptGlobalObject() is not yet called and document is + // hidden state. Therefore the MediaDecoder decides that decoding is + // not yet necessary. But soon after nsDocument::SetScriptGlobalObject() + // call, the document becomes not hidden. At the time, MediaDecoder needs + // to know it and needs to start updating decoding. + if (oldState != mVisibilityState) { + EnumerateActivityObservers(NotifyActivityChanged, nullptr); + } // The global in the template contents owner document should be the same. if (mTemplateContentsOwner && mTemplateContentsOwner != this) { @@ -6134,7 +6154,7 @@ } if (!aOptions.mPrototype) { - protoObject = JS_NewObject(aCx, nullptr, htmlProto, JS::NullPtr()); + protoObject = JS_NewObjectWithGivenProto(aCx, nullptr, htmlProto, JS::NullPtr()); if (!protoObject) { rv.Throw(NS_ERROR_UNEXPECTED); return; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsDocumentEncoder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsDocumentEncoder.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsDocumentEncoder.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsDocumentEncoder.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -572,7 +572,7 @@ } nsAutoCString charXferString; - if (!charXferString.SetLength(charLength, fallible_t())) + if (!charXferString.SetLength(charLength, fallible)) return NS_ERROR_OUT_OF_MEMORY; char* charXferBuf = charXferString.BeginWriting(); @@ -971,6 +971,7 @@ NS_ENSURE_TRUE(endParent, NS_ERROR_FAILURE); int32_t endOffset = aRange->EndOffset(); + mStartDepth = mEndDepth = 0; mCommonAncestors.Clear(); mStartNodes.Clear(); mStartOffsets.Clear(); @@ -1071,6 +1072,7 @@ NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr node, prevNode; + uint32_t firstRangeStartDepth = 0; for (i = 0; i < count; i++) { mSelection->GetRangeAt(i, getter_AddRefs(range)); @@ -1120,7 +1122,11 @@ nsRange* r = static_cast(range.get()); rv = SerializeRangeToString(r, output); NS_ENSURE_SUCCESS(rv, rv); + if (i == 0) { + firstRangeStartDepth = mStartDepth; + } } + mStartDepth = firstRangeStartDepth; if (prevNode) { nsCOMPtr p = do_QueryInterface(prevNode); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsDOMClassInfo.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsDOMClassInfo.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsDOMClassInfo.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsDOMClassInfo.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -2075,7 +2075,7 @@ if (aWin->GetDoc()) { aWin->GetDoc()->WarnOnceAbout(nsIDocument::eWindow_Controllers); } - JS::Rooted shim(cx, JS_NewObject(cx, &ControllersShimClass, JS::NullPtr(), obj)); + JS::Rooted shim(cx, JS_NewObject(cx, &ControllersShimClass, obj)); if (NS_WARN_IF(!shim)) { return NS_ERROR_OUT_OF_MEMORY; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsDOMFileReader.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsDOMFileReader.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsDOMFileReader.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsDOMFileReader.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -348,7 +348,7 @@ if (uint64_t(oldLen) + aCount > UINT32_MAX) return NS_ERROR_OUT_OF_MEMORY; char16_t *buf = nullptr; - mResult.GetMutableData(&buf, oldLen + aCount, fallible_t()); + mResult.GetMutableData(&buf, oldLen + aCount, fallible); NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY); uint32_t bytesRead = 0; @@ -522,7 +522,7 @@ rv = Base64Encode(Substring(aFileData, aDataLen), encodedData); NS_ENSURE_SUCCESS(rv, rv); - if (!AppendASCIItoUTF16(encodedData, aResult, fallible_t())) { + if (!AppendASCIItoUTF16(encodedData, aResult, fallible)) { return NS_ERROR_OUT_OF_MEMORY; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsDOMWindowUtils.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsDOMWindowUtils.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsDOMWindowUtils.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsDOMWindowUtils.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -13,7 +13,6 @@ #include "nsError.h" #include "nsIDOMEvent.h" #include "nsQueryContentEventResult.h" -#include "CompositionStringSynthesizer.h" #include "nsGlobalWindow.h" #include "nsIDocument.h" #include "nsFocusManager.h" @@ -40,6 +39,7 @@ #include "mozilla/MiscEvents.h" #include "mozilla/MouseEvents.h" #include "mozilla/TextEvents.h" +#include "mozilla/TextEventDispatcher.h" #include "mozilla/TouchEvents.h" #include "nsViewManager.h" @@ -1158,7 +1158,7 @@ LayoutDeviceIntPoint pt = ToWidgetPoint(CSSPoint(aXs[i], aYs[i]), offset, presContext); nsRefPtr t = new Touch(aIdentifiers[i], - LayoutDeviceIntPoint::ToUntyped(pt), + pt, nsIntPoint(aRxs[i], aRys[i]), aRotationAngles[i], aForces[i]); @@ -1220,8 +1220,7 @@ uint32_t locationFlag = (aAdditionalFlags & (KEY_FLAG_LOCATION_STANDARD | KEY_FLAG_LOCATION_LEFT | - KEY_FLAG_LOCATION_RIGHT | KEY_FLAG_LOCATION_NUMPAD | - KEY_FLAG_LOCATION_MOBILE | KEY_FLAG_LOCATION_JOYSTICK)); + KEY_FLAG_LOCATION_RIGHT | KEY_FLAG_LOCATION_NUMPAD)); switch (locationFlag) { case KEY_FLAG_LOCATION_STANDARD: event.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD; @@ -1235,12 +1234,6 @@ case KEY_FLAG_LOCATION_NUMPAD: event.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD; break; - case KEY_FLAG_LOCATION_MOBILE: - event.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_MOBILE; - break; - case KEY_FLAG_LOCATION_JOYSTICK: - event.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_JOYSTICK; - break; default: if (locationFlag != 0) { return NS_ERROR_INVALID_ARG; @@ -2134,80 +2127,6 @@ } NS_IMETHODIMP -nsDOMWindowUtils::SendCompositionEvent(const nsAString& aType, - const nsAString& aData, - const nsAString& aLocale) -{ - MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); - - // get the widget to send the event to - nsCOMPtr widget = GetWidget(); - if (!widget) { - return NS_ERROR_FAILURE; - } - - uint32_t msg; - if (aType.EqualsLiteral("compositionstart")) { - msg = NS_COMPOSITION_START; - } else if (aType.EqualsLiteral("compositionend")) { - // Now we don't support manually dispatching composition end with this - // API. A compositionend is dispatched when this is called with - // compositioncommitasis or compositioncommit automatically. For backward - // compatibility, this shouldn't return error in this case. - NS_WARNING("Don't call nsIDOMWindowUtils.sendCompositionEvent() for " - "compositionend. Instead, use it with compositioncommitasis or " - "compositioncommit. Then, compositionend will be automatically " - "dispatched."); - return NS_OK; - } else if (aType.EqualsLiteral("compositionupdate")) { - // Now we don't support manually dispatching composition update with this - // API. A compositionupdate is dispatched when a DOM text event modifies - // composition string automatically. For backward compatibility, this - // shouldn't return error in this case. - NS_WARNING("Don't call nsIDOMWindowUtils.sendCompositionEvent() for " - "compositionupdate since it's ignored and the event is " - "fired automatically when it's necessary"); - return NS_OK; - } else if (aType.EqualsLiteral("compositioncommitasis")) { - msg = NS_COMPOSITION_COMMIT_AS_IS; - } else if (aType.EqualsLiteral("compositioncommit")) { - msg = NS_COMPOSITION_COMMIT; - } else { - return NS_ERROR_FAILURE; - } - - WidgetCompositionEvent compositionEvent(true, msg, widget); - InitEvent(compositionEvent); - if (msg != NS_COMPOSITION_START && msg != NS_COMPOSITION_COMMIT_AS_IS) { - compositionEvent.mData = aData; - } - - compositionEvent.mFlags.mIsSynthesizedForTests = true; - - nsEventStatus status; - nsresult rv = widget->DispatchEvent(&compositionEvent, status); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -NS_IMETHODIMP -nsDOMWindowUtils::CreateCompositionStringSynthesizer( - nsICompositionStringSynthesizer** aResult) -{ - NS_ENSURE_ARG_POINTER(aResult); - *aResult = nullptr; - - MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); - - nsCOMPtr window = do_QueryReferent(mWindow); - NS_ENSURE_TRUE(window, NS_ERROR_NOT_AVAILABLE); - - NS_ADDREF(*aResult = new CompositionStringSynthesizer(window)); - return NS_OK; -} - -NS_IMETHODIMP nsDOMWindowUtils::SendQueryContentEvent(uint32_t aType, uint32_t aOffset, uint32_t aLength, int32_t aX, int32_t aY, @@ -3473,8 +3392,8 @@ // Get the target frame at the client coordinates passed to us nsPoint offset; nsCOMPtr widget = GetWidget(&offset); - nsIntPoint pt = LayoutDeviceIntPoint::ToUntyped( - ToWidgetPoint(CSSPoint(aX, aY), offset, GetPresContext())); + LayoutDeviceIntPoint pt = + ToWidgetPoint(CSSPoint(aX, aY), offset, GetPresContext()); nsPoint ptInRoot = nsLayoutUtils::GetEventCoordinatesRelativeTo(widget, pt, rootFrame); nsIFrame* targetFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsDOMWindowUtils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsDOMWindowUtils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsDOMWindowUtils.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsDOMWindowUtils.h 2015-02-03 14:33:30.000000000 +0000 @@ -57,6 +57,8 @@ class nsDOMWindowUtils MOZ_FINAL : public nsIDOMWindowUtils, public nsSupportsWeakReference { + typedef mozilla::widget::TextEventDispatcher + TextEventDispatcher; public: explicit nsDOMWindowUtils(nsGlobalWindow *aWindow); NS_DECL_ISUPPORTS diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsElementFrameLoaderOwner.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsElementFrameLoaderOwner.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsElementFrameLoaderOwner.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsElementFrameLoaderOwner.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -101,8 +101,7 @@ nsElementFrameLoaderOwner::EnsureFrameLoader() { Element* thisElement = ThisFrameElement(); - if (!thisElement->GetParent() || - !thisElement->IsInDoc() || + if (!thisElement->IsInDoc() || mFrameLoader || mFrameLoaderCreationDisallowed) { // If frame loader is there, we just keep it around, cached diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsFrameLoader.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsFrameLoader.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsFrameLoader.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsFrameLoader.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -80,6 +80,7 @@ #include "mozilla/Preferences.h" #include "mozilla/unused.h" #include "mozilla/dom/Element.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "mozilla/layout/RenderFrameParent.h" #include "nsIAppsService.h" #include "GeckoProfiler.h" @@ -88,7 +89,6 @@ #include "mozilla/dom/HTMLIFrameElement.h" #include "mozilla/dom/SVGIFrameElement.h" #include "nsSandboxFlags.h" -#include "JavaScriptParent.h" #include "mozilla/layers/CompositorChild.h" #include "mozilla/dom/StructuredCloneUtils.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsFrameMessageManager.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsFrameMessageManager.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsFrameMessageManager.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsFrameMessageManager.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -37,9 +37,8 @@ #include "mozilla/dom/StructuredCloneUtils.h" #include "mozilla/dom/ipc/BlobChild.h" #include "mozilla/dom/ipc/BlobParent.h" -#include "JavaScriptChild.h" -#include "JavaScriptParent.h" #include "mozilla/dom/DOMStringList.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "nsPrintfCString.h" #include "nsXULAppAPI.h" #include @@ -927,7 +926,7 @@ const nsAString& aMessage, bool aIsSync, const StructuredCloneData* aCloneData, - CpowHolder* aCpows, + mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal, InfallibleTArray* aJSONRetVal) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsFrameMessageManager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsFrameMessageManager.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsFrameMessageManager.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsFrameMessageManager.h 2015-02-03 14:33:30.000000000 +0000 @@ -28,6 +28,7 @@ #include "js/RootingAPI.h" #include "nsTObserverArray.h" #include "mozilla/dom/StructuredCloneUtils.h" +#include "mozilla/jsipc/CpowHolder.h" namespace mozilla { namespace dom { @@ -134,13 +135,8 @@ nsWeakPtr mWeakListener; }; -class CpowHolder -{ -public: - virtual bool ToObject(JSContext* cx, JS::MutableHandle objp) = 0; -}; -class MOZ_STACK_CLASS SameProcessCpowHolder : public CpowHolder +class MOZ_STACK_CLASS SameProcessCpowHolder : public mozilla::jsipc::CpowHolder { public: SameProcessCpowHolder(JSRuntime *aRuntime, JS::Handle aObj) @@ -230,7 +226,7 @@ nsresult ReceiveMessage(nsISupports* aTarget, const nsAString& aMessage, bool aIsSync, const StructuredCloneData* aCloneData, - CpowHolder* aCpows, nsIPrincipal* aPrincipal, + mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal, InfallibleTArray* aJSONRetVal); void AddChildManager(nsFrameMessageManager* aManager); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsGenericDOMDataNode.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsGenericDOMDataNode.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsGenericDOMDataNode.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsGenericDOMDataNode.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -1074,9 +1074,10 @@ } bool -nsGenericDOMDataNode::AppendTextTo(nsAString& aResult, const mozilla::fallible_t&) +nsGenericDOMDataNode::AppendTextTo(nsAString& aResult, + const mozilla::fallible_t& aFallible) { - return mText.AppendTo(aResult, mozilla::fallible_t()); + return mText.AppendTo(aResult, aFallible); } already_AddRefed diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsGkAtomList.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsGkAtomList.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsGkAtomList.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsGkAtomList.h 2015-02-03 14:33:30.000000000 +0000 @@ -492,7 +492,6 @@ GK_ATOM(itemscope, "itemscope") GK_ATOM(itemtype, "itemtype") GK_ATOM(kbd, "kbd") -GK_ATOM(noautofocus, "noautofocus") GK_ATOM(keepcurrentinview, "keepcurrentinview") GK_ATOM(keepobjectsalive, "keepobjectsalive") GK_ATOM(key, "key") @@ -632,7 +631,9 @@ GK_ATOM(newline, "newline") GK_ATOM(nextBidi, "NextBidi") GK_ATOM(no, "no") +GK_ATOM(noautofocus, "noautofocus") GK_ATOM(noautohide, "noautohide") +GK_ATOM(norolluponanchor, "norolluponanchor") GK_ATOM(nobr, "nobr") GK_ATOM(node, "node") GK_ATOM(nodefaultsrc, "nodefaultsrc") diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsGlobalWindow.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsGlobalWindow.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsGlobalWindow.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsGlobalWindow.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -274,7 +274,6 @@ static bool gMouseDown = false; static bool gDragServiceDisabled = false; static FILE *gDumpFile = nullptr; -static uint64_t gNextWindowID = 0; static uint32_t gSerialCounter = 0; static uint32_t gTimeoutsRecentlySet = 0; static TimeStamp gLastRecordedRecentTimeouts; @@ -562,6 +561,13 @@ return mRefCnt.get() == 1; } +namespace mozilla { +namespace dom { +extern uint64_t +NextWindowID(); +} +} + nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow) : mFrameElement(nullptr), mDocShell(nullptr), mModalStateDepth(0), mRunningTimeout(nullptr), mMutationBits(0), mIsDocumentLoaded(false), @@ -576,7 +582,7 @@ mAudioMuted(false), mAudioVolume(1.0), mInnerWindow(nullptr), mOuterWindow(aOuterWindow), // Make sure no actual window ends up with mWindowID == 0 - mWindowID(++gNextWindowID), mHasNotifiedGlobalCreated(false), + mWindowID(NextWindowID()), mHasNotifiedGlobalCreated(false), mMarkedCCGeneration(0), mSendAfterRemotePaint(false) {} @@ -4342,10 +4348,11 @@ } /* static */ bool -nsGlobalWindow::IsChromeWindow(JSContext* aCx, JSObject* aObj) +nsGlobalWindow::IsPrivilegedChromeWindow(JSContext* aCx, JSObject* aObj) { // For now, have to deal with XPConnect objects here. - return xpc::WindowOrNull(aObj)->IsChromeWindow(); + return xpc::WindowOrNull(aObj)->IsChromeWindow() && + nsContentUtils::ObjectPrincipal(aObj) == nsContentUtils::GetSystemPrincipal(); } /* static */ bool @@ -4406,7 +4413,7 @@ return rv.ErrorCode(); } -nsIDOMCrypto* +Crypto* nsGlobalWindow::GetCrypto(ErrorResult& aError) { FORWARD_TO_INNER_OR_THROW(GetCrypto, (aError), aError, nullptr); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsGlobalWindow.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsGlobalWindow.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsGlobalWindow.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsGlobalWindow.h 2015-02-03 14:33:30.000000000 +0000 @@ -74,7 +74,6 @@ class nsIContent; class nsICSSDeclaration; class nsIDocShellTreeOwner; -class nsIDOMCrypto; class nsIDOMOfflineResourceList; class nsIScrollableFrame; class nsIControllers; @@ -101,6 +100,7 @@ namespace dom { class BarProp; class Console; +class Crypto; class External; class Function; class Gamepad; @@ -491,7 +491,7 @@ void GetSupportedNames(nsTArray& aNames); - static bool IsChromeWindow(JSContext* /* unused */, JSObject* aObj); + static bool IsPrivilegedChromeWindow(JSContext* /* unused */, JSObject* aObj); static bool IsShowModalDialogEnabled(JSContext* /* unused */ = nullptr, JSObject* /* unused */ = nullptr); @@ -995,7 +995,7 @@ } int64_t GetMozAnimationStartTime(mozilla::ErrorResult& aError); void SizeToContent(mozilla::ErrorResult& aError); - nsIDOMCrypto* GetCrypto(mozilla::ErrorResult& aError); + mozilla::dom::Crypto* GetCrypto(mozilla::ErrorResult& aError); nsIControllers* GetControllers(mozilla::ErrorResult& aError); mozilla::dom::Element* GetRealFrameElement(mozilla::ErrorResult& aError); float GetMozInnerScreenX(mozilla::ErrorResult& aError); @@ -1556,7 +1556,7 @@ nsString mStatus; nsString mDefaultStatus; nsGlobalWindowObserver* mObserver; // Inner windows only. - nsCOMPtr mCrypto; + nsRefPtr mCrypto; nsRefPtr mConsole; // We need to store an nsISupports pointer to this object because the // mozilla::dom::External class doesn't exist on b2g and using the type diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsIDocumentEncoder.idl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsIDocumentEncoder.idl --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsIDocumentEncoder.idl 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsIDocumentEncoder.idl 2015-02-03 14:33:30.000000000 +0000 @@ -228,6 +228,13 @@ const unsigned long OutputDontRemoveLineEndingSpaces = (1 << 24); /** + * Serialize in a way that is suitable for copying a plaintext version of the + * document to the clipboard. This can for example cause line endings to be + * injected at preformatted block element boundaries. + */ + const unsigned long OutputForPlainTextClipboardCopy = (1 << 25); + + /** * Initialize with a pointer to the document and the mime type. * @param aDocument Document to encode. * @param aMimeType MimeType to use. May also be set by SetMimeType. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsJSEnvironment.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsJSEnvironment.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsJSEnvironment.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsJSEnvironment.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -2355,6 +2355,13 @@ } static void +SetMemoryGCCompactingPrefChangedCallback(const char* aPrefName, void* aClosure) +{ + bool pref = Preferences::GetBool(aPrefName); + JS_SetGCParameter(sRuntime, JSGC_COMPACTING_ENABLED, pref); +} + +static void SetMemoryGCPrefChangedCallback(const char* aPrefName, void* aClosure) { int32_t pref = Preferences::GetInt(aPrefName, -1); @@ -2616,6 +2623,9 @@ Preferences::RegisterCallbackAndCall(SetMemoryGCSliceTimePrefChangedCallback, "javascript.options.mem.gc_incremental_slice_ms"); + Preferences::RegisterCallbackAndCall(SetMemoryGCCompactingPrefChangedCallback, + "javascript.options.mem.gc_compacting"); + Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback, "javascript.options.mem.gc_high_frequency_time_limit_ms", (void *)JSGC_HIGH_FREQUENCY_TIME_LIMIT); @@ -2773,7 +2783,6 @@ // copy the array - we don't know its lifetime, and ours is tied to xpcom // refcounting. if (argc) { - static const fallible_t fallible = fallible_t(); mArgv = new (fallible) JS::Heap[argc]; if (!mArgv) { *prv = NS_ERROR_OUT_OF_MEMORY; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsJSUtils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsJSUtils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsJSUtils.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsJSUtils.h 2015-02-03 14:33:30.000000000 +0000 @@ -17,6 +17,7 @@ #include "jsapi.h" #include "jsfriendapi.h" +#include "js/Conversions.h" #include "nsString.h" class nsIScriptContext; @@ -160,7 +161,7 @@ size_t len = js::GetStringLength(s); static_assert(js::MaxStringLength < (1 << 28), "Shouldn't overflow here or in SetCapacity"); - if (MOZ_UNLIKELY(!dest.SetLength(len, mozilla::fallible_t()))) { + if (MOZ_UNLIKELY(!dest.SetLength(len, mozilla::fallible))) { JS_ReportOutOfMemory(cx); return false; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsLineBreaker.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsLineBreaker.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsLineBreaker.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsLineBreaker.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -145,6 +145,13 @@ return NS_OK; } +// If the aFlags parameter to AppendText has all these bits set, +// then we don't need to worry about finding break opportunities +// in the appended text. +#define NO_BREAKS_NEEDED_FLAGS (BREAK_SUPPRESS_INITIAL | \ + BREAK_SUPPRESS_INSIDE | \ + BREAK_SKIP_SETTING_NO_BREAKS) + nsresult nsLineBreaker::AppendText(nsIAtom* aHyphenationLanguage, const char16_t* aText, uint32_t aLength, uint32_t aFlags, nsILineBreakSink* aSink) @@ -184,23 +191,25 @@ if (!breakState.AppendElements(aLength)) return NS_ERROR_OUT_OF_MEMORY; } - + + bool noCapitalizationNeeded = true; nsTArray capitalizationState; if (aSink && (aFlags & BREAK_NEED_CAPITALIZATION)) { if (!capitalizationState.AppendElements(aLength)) return NS_ERROR_OUT_OF_MEMORY; memset(capitalizationState.Elements(), false, aLength*sizeof(bool)); + noCapitalizationNeeded = false; } uint32_t start = offset; bool noBreaksNeeded = !aSink || - (aFlags == (BREAK_SUPPRESS_INITIAL | BREAK_SUPPRESS_INSIDE | BREAK_SKIP_SETTING_NO_BREAKS) && + ((aFlags & NO_BREAKS_NEEDED_FLAGS) == NO_BREAKS_NEEDED_FLAGS && !mBreakHere && !mAfterBreakableSpace); - if (noBreaksNeeded) { + if (noBreaksNeeded && noCapitalizationNeeded) { // Skip to the space before the last word, since either the break data // here is not needed, or no breaks are set in the sink and there cannot - // be any breaks in this chunk; all we need is the context for the next - // chunk (if any) + // be any breaks in this chunk; and we don't need to do word-initial + // capitalization. All we need is the context for the next chunk (if any). offset = aLength; while (offset > start) { --offset; @@ -223,7 +232,7 @@ bool isSpace = IsSpace(ch); bool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE); - if (aSink) { + if (aSink && !noBreaksNeeded) { breakState[offset] = mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) || (mWordBreak == nsILineBreaker::kWordBreak_BreakAll) ? @@ -252,7 +261,7 @@ breakState.Elements() + wordStart); } } - if (aFlags & BREAK_NEED_CAPITALIZATION) { + if (!noCapitalizationNeeded) { SetupCapitalization(aText + wordStart, offset - wordStart, capitalizationState.Elements() + wordStart); } @@ -284,10 +293,11 @@ } } - if (!noBreaksNeeded) { - // aSink must not be null - aSink->SetBreaks(start, offset - start, breakState.Elements() + start); - if (aFlags & BREAK_NEED_CAPITALIZATION) { + if (aSink) { + if (!noBreaksNeeded) { + aSink->SetBreaks(start, offset - start, breakState.Elements() + start); + } + if (!noCapitalizationNeeded) { aSink->SetCapitalization(start, offset - start, capitalizationState.Elements() + start); } @@ -365,7 +375,7 @@ uint32_t start = offset; bool noBreaksNeeded = !aSink || - (aFlags == (BREAK_SUPPRESS_INITIAL | BREAK_SUPPRESS_INSIDE | BREAK_SKIP_SETTING_NO_BREAKS) && + ((aFlags & NO_BREAKS_NEEDED_FLAGS) == NO_BREAKS_NEEDED_FLAGS && !mBreakHere && !mAfterBreakableSpace); if (noBreaksNeeded) { // Skip to the space before the last word, since either the break data diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsObjectLoadingContent.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsObjectLoadingContent.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsObjectLoadingContent.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsObjectLoadingContent.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -113,6 +113,13 @@ #define LOG_ENABLED() PR_LOG_TEST(GetObjectLog(), PR_LOG_DEBUG) static bool +IsJavaMIME(const nsACString & aMIMEType) +{ + return + nsPluginHost::GetSpecialType(aMIMEType) == nsPluginHost::eSpecialType_Java; +} + +static bool InActiveDocument(nsIContent *aContent) { if (!aContent->IsInDoc()) { @@ -992,7 +999,7 @@ mCachedAttributes.AppendElement(param); } - bool isJava = nsPluginHost::IsJavaMIMEType(mContentType.get()); + bool isJava = IsJavaMIME(mContentType); nsCString codebase; if (isJava) { @@ -1584,8 +1591,8 @@ if (aJavaURI || thisContent->NodeInfo()->Equals(nsGkAtoms::applet)) { nsAdoptingCString javaMIME = Preferences::GetCString(kPrefJavaMIME); newMime = javaMIME; - NS_ASSERTION(nsPluginHost::IsJavaMIMEType(newMime.get()), - "plugin.mime.java should be recognized by IsJavaMIMEType"); + NS_ASSERTION(IsJavaMIME(newMime), + "plugin.mime.java should be recognized as java"); isJava = true; } else { nsAutoString rawTypeAttr; @@ -1593,7 +1600,7 @@ if (!rawTypeAttr.IsEmpty()) { typeAttr = rawTypeAttr; CopyUTF16toUTF8(rawTypeAttr, newMime); - isJava = nsPluginHost::IsJavaMIMEType(newMime.get()); + isJava = IsJavaMIME(newMime); } } @@ -1607,8 +1614,8 @@ if (!classIDAttr.IsEmpty()) { // Our classid support is limited to 'java:' ids nsAdoptingCString javaMIME = Preferences::GetCString(kPrefJavaMIME); - NS_ASSERTION(nsPluginHost::IsJavaMIMEType(javaMIME.get()), - "plugin.mime.java should be recognized by IsJavaMIMEType"); + NS_ASSERTION(IsJavaMIME(javaMIME), + "plugin.mime.java should be recognized as java"); if (StringBeginsWith(classIDAttr, NS_LITERAL_STRING("java:")) && PluginExistsForType(javaMIME)) { newMime = javaMIME; @@ -1720,7 +1727,7 @@ (caps & eAllowPluginSkipChannel) && IsPluginEnabledByExtension(newURI, newMime)) { LOG(("OBJLC [%p]: Using extension as type hint (%s)", this, newMime.get())); - if (!isJava && nsPluginHost::IsJavaMIMEType(newMime.get())) { + if (!isJava && IsJavaMIME(newMime)) { return UpdateObjectParameters(true); } } @@ -1836,7 +1843,7 @@ } } else { newMime = channelType; - if (nsPluginHost::IsJavaMIMEType(newMime.get())) { + if (IsJavaMIME(newMime)) { // Java does not load with a channel, and being java retroactively // changes how we may have interpreted the codebase to construct this // URI above. Because the behavior here is more or less undefined, play @@ -2107,7 +2114,7 @@ if (mType != eType_Null) { bool allowLoad = true; - if (nsPluginHost::IsJavaMIMEType(mContentType.get())) { + if (IsJavaMIME(mContentType)) { allowLoad = CheckJavaCodebase(); } int16_t contentPolicy = nsIContentPolicy::ACCEPT; @@ -3017,7 +3024,8 @@ if (inst) { const char* mime = nullptr; if (NS_SUCCEEDED(inst->GetMIMEType(&mime)) && mime) { - if (strcmp(mime, "audio/x-pn-realaudio-plugin") == 0) { + if (nsPluginHost::GetSpecialType(nsDependentCString(mime)) == + nsPluginHost::eSpecialType_RealPlayer) { delayedStop = true; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsPlainTextSerializer.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsPlainTextSerializer.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsPlainTextSerializer.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsPlainTextSerializer.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -91,6 +91,8 @@ mPreFormatted = false; mStartedOutput = false; + mPreformattedBlockBoundary = false; + // initialize the tag stack to zero: // The stack only ever contains pointers to static atoms, so they don't // need refcounting. @@ -167,6 +169,8 @@ mLineBreakDue = false; mFloatingLines = -1; + mPreformattedBlockBoundary = false; + if (mFlags & nsIDocumentEncoder::OutputFormatted) { // Get some prefs that controls how we do formatted output mStructs = Preferences::GetBool(PREF_STRUCTS, mStructs); @@ -437,6 +441,16 @@ return NS_OK; } + if (mFlags & nsIDocumentEncoder::OutputForPlainTextClipboardCopy) { + if (mPreformattedBlockBoundary && DoOutput()) { + // Should always end a line, but get no more whitespace + if (mFloatingLines < 0) + mFloatingLines = 0; + mLineBreakDue = true; + } + mPreformattedBlockBoundary = false; + } + if (mFlags & nsIDocumentEncoder::OutputRaw) { // Raw means raw. Don't even think about doing anything fancy // here like indenting, adding line breaks or any other @@ -670,7 +684,7 @@ // Else make sure we'll separate block level tags, // even if we're about to leave, before doing any other formatting. - else if (nsContentUtils::IsHTMLBlock(aTag)) { + else if (IsElementBlock(mElement)) { EnsureVerticalSpace(0); } @@ -767,6 +781,14 @@ return NS_OK; } + if (mFlags & nsIDocumentEncoder::OutputForPlainTextClipboardCopy) { + if (DoOutput() && IsInPre() && IsElementBlock(mElement)) { + // If we're closing a preformatted block element, output a line break + // when we find a new container. + mPreformattedBlockBoundary = true; + } + } + if (mFlags & nsIDocumentEncoder::OutputRaw) { // Raw means raw. Don't even think about doing anything fancy // here like indenting, adding line breaks or any other @@ -887,8 +909,7 @@ else if (aTag == nsGkAtoms::q) { Write(NS_LITERAL_STRING("\"")); } - else if (nsContentUtils::IsHTMLBlock(aTag) - && aTag != nsGkAtoms::script) { + else if (IsElementBlock(mElement) && aTag != nsGkAtoms::script) { // All other blocks get 1 vertical space after them // in formatted mode, otherwise 0. // This is hard. Sometimes 0 is a better number, but @@ -1037,6 +1058,8 @@ nsresult nsPlainTextSerializer::DoAddLeaf(nsIAtom* aTag) { + mPreformattedBlockBoundary = false; + // If we don't want any output, just return if (!DoOutput()) { return NS_OK; @@ -1778,6 +1801,20 @@ return GetIdForContent(aElement) == nsGkAtoms::pre; } +bool +nsPlainTextSerializer::IsElementBlock(Element* aElement) +{ + nsRefPtr styleContext = + nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, nullptr, + nullptr); + if (styleContext) { + const nsStyleDisplay* displayStyle = styleContext->StyleDisplay(); + return displayStyle->IsBlockOutsideStyle(); + } + // Fall back to looking at the tag, in case there is no style information. + return nsContentUtils::IsHTMLBlock(GetIdForContent(aElement)); +} + /** * This method is required only to identify LI's inside OL. * Returns TRUE if we are inside an OL tag and FALSE otherwise. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsPlainTextSerializer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsPlainTextSerializer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsPlainTextSerializer.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsPlainTextSerializer.h 2015-02-03 14:33:30.000000000 +0000 @@ -115,6 +115,7 @@ bool ShouldReplaceContainerWithPlaceholder(nsIAtom* aTag); bool IsElementPreformatted(mozilla::dom::Element* aElement); + bool IsElementBlock(mozilla::dom::Element* aElement); private: nsString mCurrentLine; @@ -169,7 +170,9 @@ // While handling a new tag, this variable should remind if any line break // is due because of a closing tag. Setting it to "TRUE" while closing the tags. // Hence opening tags are guaranteed to start with appropriate line breaks. - bool mLineBreakDue; + bool mLineBreakDue; + + bool mPreformattedBlockBoundary; nsString mURL; int32_t mHeaderStrategy; /* Header strategy (pref) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsPropertyTable.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsPropertyTable.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsPropertyTable.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsPropertyTable.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -94,9 +94,10 @@ nsresult rv = NS_OK; for (PropertyList* prop = mPropertyList; prop; prop = prop->mNext) { if (prop->mTransfer) { - PropertyListMapEntry *entry = static_cast - (PL_DHashTableLookup(&prop->mObjectValueMap, aObject)); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + PropertyListMapEntry *entry = + static_cast + (PL_DHashTableSearch(&prop->mObjectValueMap, aObject)); + if (entry) { rv = aOtherTable->SetProperty(aObject, prop->mName, entry->value, prop->mDtorFunc, prop->mDtorData, prop->mTransfer); @@ -125,10 +126,10 @@ PropertyList* prop; for (prop = mPropertyList; prop; prop = prop->mNext) { PropertyListMapEntry *entry = static_cast - (PL_DHashTableLookup(&prop->mObjectValueMap, aObject)); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + (PL_DHashTableSearch(&prop->mObjectValueMap, aObject)); + if (entry) { aCallback(const_cast(aObject.get()), prop->mName, entry->value, - aData); + aData); } } } @@ -172,9 +173,10 @@ PropertyList* propertyList = GetPropertyListFor(aPropertyName); if (propertyList) { - PropertyListMapEntry *entry = static_cast - (PL_DHashTableLookup(&propertyList->mObjectValueMap, aObject)); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + PropertyListMapEntry *entry = + static_cast + (PL_DHashTableSearch(&propertyList->mObjectValueMap, aObject)); + if (entry) { propValue = entry->value; if (aRemove) { // don't call propertyList->mDtorFunc. That's the caller's job now. @@ -325,9 +327,10 @@ bool nsPropertyTable::PropertyList::DeletePropertyFor(nsPropertyOwner aObject) { - PropertyListMapEntry *entry = static_cast - (PL_DHashTableLookup(&mObjectValueMap, aObject)); - if (!PL_DHASH_ENTRY_IS_BUSY(entry)) + PropertyListMapEntry *entry = + static_cast + (PL_DHashTableSearch(&mObjectValueMap, aObject)); + if (!entry) return false; void* value = entry->value; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsScriptNameSpaceManager.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsScriptNameSpaceManager.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsScriptNameSpaceManager.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsScriptNameSpaceManager.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -172,10 +172,9 @@ if (!aStruct->mAlias->mProto) { GlobalNameMapEntry *proto = static_cast - (PL_DHashTableLookup(&mGlobalNames, + (PL_DHashTableSearch(&mGlobalNames, &aStruct->mAlias->mProtoName)); - - if (PL_DHASH_ENTRY_IS_BUSY(proto)) { + if (proto) { aStruct->mAlias->mProto = &proto->mGlobalName; } } @@ -329,13 +328,13 @@ mIsInitialized = PL_DHashTableInit(&mGlobalNames, &hash_table_ops, sizeof(GlobalNameMapEntry), - fallible_t(), + fallible, GLOBALNAME_HASHTABLE_INITIAL_LENGTH); NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_OUT_OF_MEMORY); mIsInitialized = PL_DHashTableInit(&mNavigatorNames, &hash_table_ops, sizeof(GlobalNameMapEntry), - fallible_t(), + fallible, GLOBALNAME_HASHTABLE_INITIAL_LENGTH); if (!mIsInitialized) { PL_DHashTableFinish(&mGlobalNames); @@ -385,9 +384,9 @@ { GlobalNameMapEntry *entry = static_cast - (PL_DHashTableLookup(&mGlobalNames, &aName)); + (PL_DHashTableSearch(&mGlobalNames, &aName)); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + if (entry) { if (aClassName) { *aClassName = entry->mKey.get(); } @@ -405,13 +404,9 @@ { GlobalNameMapEntry *entry = static_cast - (PL_DHashTableLookup(&mNavigatorNames, &aName)); - - if (!PL_DHASH_ENTRY_IS_BUSY(entry)) { - return nullptr; - } + (PL_DHashTableSearch(&mNavigatorNames, &aName)); - return &entry->mGlobalName; + return entry ? &entry->mGlobalName : nullptr; } nsresult diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsTextFragment.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsTextFragment.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsTextFragment.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsTextFragment.h 2015-02-03 14:33:30.000000000 +0000 @@ -125,7 +125,7 @@ * Append the contents of this string fragment to aString */ void AppendTo(nsAString& aString) const { - if (!AppendTo(aString, mozilla::fallible_t())) { + if (!AppendTo(aString, mozilla::fallible)) { aString.AllocFailed(aString.Length() + GetLength()); } } @@ -135,9 +135,9 @@ * @return false if an out of memory condition is detected, true otherwise */ bool AppendTo(nsAString& aString, - const mozilla::fallible_t&) const NS_WARN_UNUSED_RESULT { + const mozilla::fallible_t& aFallible) const NS_WARN_UNUSED_RESULT { if (mState.mIs2b) { - bool ok = aString.Append(m2b, mState.mLength, mozilla::fallible_t()); + bool ok = aString.Append(m2b, mState.mLength, aFallible); if (!ok) { return false; } @@ -145,7 +145,7 @@ return true; } else { return AppendASCIItoUTF16(Substring(m1b, mState.mLength), aString, - mozilla::fallible_t()); + aFallible); } } @@ -155,7 +155,7 @@ * @param aLength the length of the substring */ void AppendTo(nsAString& aString, int32_t aOffset, int32_t aLength) const { - if (!AppendTo(aString, aOffset, aLength, mozilla::fallible_t())) { + if (!AppendTo(aString, aOffset, aLength, mozilla::fallible)) { aString.AllocFailed(aString.Length() + aLength); } } @@ -168,10 +168,10 @@ * @return false if an out of memory condition is detected, true otherwise */ bool AppendTo(nsAString& aString, int32_t aOffset, int32_t aLength, - const mozilla::fallible_t&) const NS_WARN_UNUSED_RESULT + const mozilla::fallible_t& aFallible) const NS_WARN_UNUSED_RESULT { if (mState.mIs2b) { - bool ok = aString.Append(m2b + aOffset, aLength, mozilla::fallible_t()); + bool ok = aString.Append(m2b + aOffset, aLength, aFallible); if (!ok) { return false; } @@ -179,7 +179,7 @@ return true; } else { return AppendASCIItoUTF16(Substring(m1b + aOffset, aLength), aString, - mozilla::fallible_t()); + aFallible); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsXMLHttpRequest.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsXMLHttpRequest.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/nsXMLHttpRequest.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/nsXMLHttpRequest.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -670,7 +670,7 @@ &destBufferLen); NS_ENSURE_SUCCESS(rv, rv); - if (!mResponseText.SetCapacity(mResponseText.Length() + destBufferLen, fallible_t())) { + if (!mResponseText.SetCapacity(mResponseText.Length() + destBufferLen, fallible)) { return NS_ERROR_OUT_OF_MEMORY; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/bug403852_fileOpener.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/bug403852_fileOpener.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/bug403852_fileOpener.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/bug403852_fileOpener.js 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,15 @@ +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; +Cu.importGlobalProperties(["File"]); + +let testFile = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIDirectoryService) + .QueryInterface(Ci.nsIProperties) + .get("ProfD", Ci.nsIFile); +testFile.append("prefs.js"); + +addMessageListener("file.open", function () { + sendAsyncMessage("file.opened", { + file: new File(testFile), + mtime: testFile.lastModifiedTime, + }); +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/bug578096LoadChromeScript.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/bug578096LoadChromeScript.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/bug578096LoadChromeScript.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/bug578096LoadChromeScript.js 2015-02-03 14:33:30.000000000 +0000 @@ -1,11 +1,13 @@ var file; +Components.utils.importGlobalProperties(["File"]); + addMessageListener("file.create", function (message) { file = Components.classes["@mozilla.org/file/directory_service;1"] .getService(Components.interfaces.nsIProperties) .get("TmpD", Components.interfaces.nsIFile); file.append("foo.txt"); file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0600); - sendAsyncMessage("file.path", file.path); + sendAsyncMessage("file.created", new File(file)); }); addMessageListener("file.remove", function (message) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/chrome/chrome.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/chrome/chrome.ini --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/chrome/chrome.ini 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/chrome/chrome.ini 2015-02-03 14:33:30.000000000 +0000 @@ -17,6 +17,7 @@ fileconstructor_file.png frame_bug814638.xul host_bug814638.xul + window_nsITextInputProcessor.xul title_window.xul [test_bug206691.xul] @@ -60,4 +61,5 @@ [test_domparsing.xul] [test_fileconstructor.xul] [test_fileconstructor_tempfile.xul] +[test_nsITextInputProcessor.xul] [test_title.xul] diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/chrome/cpows_parent.xul thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/chrome/cpows_parent.xul --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/chrome/cpows_parent.xul 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/chrome/cpows_parent.xul 2015-02-03 14:33:30.000000000 +0000 @@ -197,6 +197,24 @@ let savedElement = null; function recvDomTest(message) { savedElement = message.objects.element; + + // Test to ensure that we don't pass CPOWs to C++-implemented interfaces. + // See bug 1072980. + if (test_state == "remote") { + let walker = Components.classes["@mozilla.org/inspector/deep-tree-walker;1"] + .createInstance(Components.interfaces.inIDeepTreeWalker); + const SHOW_ELEMENT = Components.interfaces.nsIDOMNodeFilter.SHOW_ELEMENT; + walker.showAnonymousContent = true; + walker.showSubDocuments = false; + + try { + walker.init(savedElement, SHOW_ELEMENT); + ok(false, "expected exception passing CPOW to C++"); + } catch (e) { + is(e.result, Components.results.NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE, + "got exception when passing CPOW to C++"); + } + } } function recvDomTestAfterGC(message) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/chrome/test_bug599295.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/chrome/test_bug599295.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/chrome/test_bug599295.html 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/chrome/test_bug599295.html 2015-02-03 14:33:30.000000000 +0000 @@ -33,6 +33,11 @@ Components.Constructor("@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream", "setInputStream"); + +const Ci = Components.interfaces; +const Cu = Components.utils; +Cu.import("resource://gre/modules/Services.jsm"); + var listener = { _httpstatus : 0, @@ -59,7 +64,13 @@ var ios = Components.classes["@mozilla.org/network/io-service;1"]. getService(Components.interfaces.nsIIOService); var uri = ios.newURI("https://redirproxy.example.com/test", "", null); - var channel = ios.newChannelFromURI(uri); + + var channel = ios.newChannelFromURI2(uri, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); /* Previously, necko would allow a 302 as part of a CONNECT response if the LOAD_DOCUMENT_URI flag was set and the original document diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/chrome/test_nsITextInputProcessor.xul thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/chrome/test_nsITextInputProcessor.xul --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/chrome/test_nsITextInputProcessor.xul 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/chrome/test_nsITextInputProcessor.xul 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,32 @@ + + + + + + + + +

    +
    +

    + +
    +
    + + + +
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/chrome/window_nsITextInputProcessor.xul thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/chrome/window_nsITextInputProcessor.xul --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/chrome/window_nsITextInputProcessor.xul 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/chrome/window_nsITextInputProcessor.xul 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,1038 @@ + + + + + + +

    +
    +
    +

    + +
    +
    + + + + +
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/copypaste.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/copypaste.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/copypaste.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/copypaste.js 2015-02-03 14:33:30.000000000 +0000 @@ -52,30 +52,33 @@ if (!suppressHTMLCheck) ok(clipboard.hasDataMatchingFlavors(["text/html"], 1,1), "check text/html"); } - function copyToClipboard(node, suppressUnicodeCheck) { + function clear(node, suppressUnicodeCheck) { textarea.blur(); clipboard.emptyClipboard(1); var sel = window.getSelection(); sel.removeAllRanges(); + } + function copyToClipboard(node, suppressUnicodeCheck) { + clear(); var r = document.createRange(); r.selectNode(node); window.getSelection().addRange(r); copySelectionToClipboard(suppressUnicodeCheck); } - function copyRangeToClipboard(startNode,startIndex,endNode,endIndex,suppressUnicodeCheck) { - textarea.blur(); - clipboard.emptyClipboard(1); + function addRange(startNode,startIndex,endNode,endIndex) { var sel = window.getSelection(); - sel.removeAllRanges(); var r = document.createRange(); r.setStart(startNode,startIndex) r.setEnd(endNode,endIndex) - window.getSelection().addRange(r); + sel.addRange(r); + } + function copyRangeToClipboard(startNode,startIndex,endNode,endIndex,suppressUnicodeCheck) { + clear(); + addRange(startNode,startIndex,endNode,endIndex); copySelectionToClipboard(suppressUnicodeCheck); } function copyChildrenToClipboard(id) { - textarea.blur(); - clipboard.emptyClipboard(1); + clear(); window.getSelection().selectAllChildren(document.getElementById(id)); copySelectionToClipboard(); } @@ -105,6 +108,12 @@ textarea.editor.paste(1); is(textarea.value, expected, "value of the textarea after the paste"); } + function testPasteHTML(id, expected) { + var contentEditable = $(id); + contentEditable.focus(); + synthesizeKey("v", {accelKey: 1}); + is(contentEditable.innerHTML, expected, id+".innerHtml after the paste"); + } function testSelectionToString(expected) { is(window.getSelection().toString().replace(/\r\n/g,"\n"), expected, "Selection.toString"); } @@ -221,6 +230,116 @@ copyRangeToClipboard($("div10").childNodes[1],0, $("div10").childNodes[1],1,suppressUnicodeCheckIfHidden); testSelectionToString(""); + if (!isXHTML) { + // ============ copy/paste multi-range selection (bug 1123505) + // with text start node + var sel = window.getSelection(); + sel.removeAllRanges(); + var r = document.createRange(); + var ul = $('ul1'); + var parent = ul.parentNode; + r.setStart(parent, 0); + r.setEnd(parent.firstChild, 15); // the end of "Copy..." + sel.addRange(r); + + r = document.createRange(); + r.setStart(ul, 1); // before the space inside the UL + r.setEnd(parent, 2); // after the UL + sel.addRange(r); + copySelectionToClipboard(true); + testPasteHTML('contentEditable1', 'Copy1then Paste'); + + // with text end node + var sel = window.getSelection(); + sel.removeAllRanges(); + var r = document.createRange(); + var ul = $('ul2'); + var parent = ul.parentNode; + r.setStart(parent, 0); + r.setEnd(ul, 1); // after the space + sel.addRange(r); + + r = document.createRange(); + r.setStart(parent.childNodes[1], 0); // the start of "Copy..." + r.setEnd(parent, 2); + sel.addRange(r); + copySelectionToClipboard(true); + testPasteHTML('contentEditable2', 'Copy2then Paste'); + + // with text end node and non-empty start + var sel = window.getSelection(); + sel.removeAllRanges(); + var r = document.createRange(); + var ul = $('ul3'); + var parent = ul.parentNode; + r.setStart(parent, 0); + r.setEnd(ul, 1); // after the space + sel.addRange(r); + + r = document.createRange(); + r.setStart(parent.childNodes[1], 0); // the start of "Copy..." + r.setEnd(parent, 2); + sel.addRange(r); + copySelectionToClipboard(true); + testPasteHTML('contentEditable3', '
    • \n
    Copy3then Paste'); + + // with elements of different depth + var sel = window.getSelection(); + sel.removeAllRanges(); + var r = document.createRange(); + var div1 = $('div1s'); + var parent = div1.parentNode; + r.setStart(parent, 0); + r.setEnd(document.getElementById('div1se1'), 1); // after the "inner" DIV + sel.addRange(r); + + r = document.createRange(); + r.setStart(div1.childNodes[1], 0); // the start of "after" + r.setEnd(parent, 1); + sel.addRange(r); + copySelectionToClipboard(true); + testPasteHTML('contentEditable4', '
    before
    after
    '); + + // with elements of different depth, and a text node at the end + var sel = window.getSelection(); + sel.removeAllRanges(); + var r = document.createRange(); + var div1 = $('div2s'); + var parent = div1.parentNode; + r.setStart(parent, 0); + r.setEnd(document.getElementById('div2se1'), 1); // after the "inner" DIV + sel.addRange(r); + + r = document.createRange(); + r.setStart(div1.childNodes[1], 0); // the start of "after" + r.setEnd(parent, 1); + sel.addRange(r); + copySelectionToClipboard(true); + testPasteHTML('contentEditable5', '
    before
    after
    '); + + // crash test for bug 1127835 + var e1 = document.getElementById('1127835crash1'); + var e2 = document.getElementById('1127835crash2'); + var e3 = document.getElementById('1127835crash3'); + var t1 = e1.childNodes[0]; + var t3 = e3.childNodes[0]; + + var sel = window.getSelection(); + sel.removeAllRanges(); + + var r = document.createRange(); + r.setStart(t1, 1); + r.setEnd(e2, 0); + sel.addRange(r); + + r = document.createRange(); + r.setStart(e2, 1); + r.setEnd(t3, 0); + sel.addRange(r); + copySelectionToClipboard(true); + testPasteHTML('contentEditable6', '
    \n

    '); + } + // ============ copy/paste test from/to a textarea var val = "1\n 2\n 3"; @@ -246,6 +365,23 @@ copyToClipboard($("tr1")); testClipboardValue("text/unicode", "foo\tbar"); + if (!isXHTML) { + // ============ spanning multiple rows + + copyRangeToClipboard($("tr2"),0,$("tr3"),0); + testClipboardValue("text/unicode", "1\t2\n3\t4\n"); + testClipboardValue("text/html", '
    12
    34
    '); + + // ============ spanning multiple rows in multi-range selection + + clear(); + addRange($("tr2"),0,$("tr2"),2); + addRange($("tr3"),0,$("tr3"),2); + copySelectionToClipboard(); + testClipboardValue("text/unicode", "1\t2\n5\t6"); + testClipboardValue("text/html", '
    12
    56
    '); + } + // ============ manipulating Selection in oncopy copyRangeToClipboard($("div11").childNodes[0],0, $("div11").childNodes[1],2); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/csp/test_bug949549.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/csp/test_bug949549.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/csp/test_bug949549.html 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/csp/test_bug949549.html 2015-02-03 14:33:30.000000000 +0000 @@ -41,7 +41,14 @@ // as app channels can't be instanciated in xpcshell. // Because app protocol depends on webapps.jsm, // which doesn't instanciate properly on xpcshell without many hacks - let appchan = SpecialPowers.Services.io.newChannel(gManifestURL, null, null); + let appchan = SpecialPowers.Services.io.newChannel2(gManifestURL, + null, + null, + null, // aLoadingNode + SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + SpecialPowers.Ci.nsILoadInfo.SEC_NORMAL, + SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER); try { csp.setRequestContext(null, null, appchan); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/fileapi_chromeScript.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/fileapi_chromeScript.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/fileapi_chromeScript.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/fileapi_chromeScript.js 2015-02-03 14:33:31.000000000 +0000 @@ -0,0 +1,29 @@ +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; +Cu.importGlobalProperties(["File"]); + +var fileNum = 1; + +function createFileWithData(fileData) { + var willDelete = fileData === null; + var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties); + var testFile = dirSvc.get("ProfD", Ci.nsIFile); + testFile.append("fileAPItestfile" + fileNum); + fileNum++; + var outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + if (willDelete) { + fileData = "some irrelevant test data\n"; + } + outStream.write(fileData, fileData.length); + outStream.close(); + var domFile = new File(testFile); + if (willDelete) { + testFile.remove(/* recursive: */ false); + } + return domFile; +} + +addMessageListener("files.open", function (message) { + sendAsyncMessage("files.opened", message.map(createFileWithData)); +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/mochitest.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/mochitest.ini --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/mochitest.ini 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/mochitest.ini 2015-02-03 14:33:31.000000000 +0000 @@ -27,6 +27,7 @@ bug298064-subframe.html bug313646.txt bug382113_object.html + bug403852_fileOpener.js bug419132.html bug426308-redirect.sjs bug435425.sjs @@ -218,6 +219,7 @@ file_xhtmlserializer_2_enthtml.xhtml file_xhtmlserializer_2_entw3c.xhtml file_xhtmlserializer_2_latin1.xhtml + fileapi_chromeScript.js fileutils.js forRemoval.resource forRemoval.resource^headers^ @@ -431,7 +433,6 @@ support-files = test_bug402150.html^headers^ [test_bug403841.html] [test_bug403852.html] -skip-if = e10s [test_bug403868.xml] [test_bug405182.html] [test_bug409380.html] @@ -548,7 +549,7 @@ [test_bug564863.xhtml] [test_bug567350.html] [test_bug578096.html] -skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) || e10s # b2g-debug(debug-only failure; crash) b2g-desktop(Bug 931116, b2g desktop specific, initial triage) +skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(debug-only failure; crash) b2g-desktop(Bug 931116, b2g desktop specific, initial triage) [test_bug585978.html] skip-if = (toolkit == 'gonk' && debug) #debug-only timeout [test_bug587931.html] @@ -658,6 +659,7 @@ [test_bug1008126.html] run-if = os == 'linux' [test_bug1057176.html] +[test_bug1070015.html] [test_bug1075702.html] [test_bug1101364.html] skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' @@ -679,7 +681,6 @@ [test_element_closest.html] [test_encodeToStringWithMaxLength.html] [test_fileapi.html] -skip-if = e10s [test_fileapi_slice.html] skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' || e10s #bug 775227 [test_getElementById.html] @@ -770,3 +771,4 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s [test_window_define_nonconfigurable.html] skip-if = true # bug 1107443 - code for newly-added test was disabled +[test_root_iframe.html] diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_bug1070015.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_bug1070015.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_bug1070015.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_bug1070015.html 2015-02-03 14:33:31.000000000 +0000 @@ -0,0 +1,53 @@ + + + + + Test for Bug 1070015 + + + + + +Mozilla Bug 1070015 +

    + +
    +
    +
    +
    +
    + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_bug116083.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_bug116083.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_bug116083.html 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_bug116083.html 2015-02-03 14:33:31.000000000 +0000 @@ -20,6 +20,14 @@
    bar baz
    bar baz
    bar baz
    +
    foo
    bar

    !


    baz
    +
    foo
    bar

    !


    baz
    +
    foo
    bar

    !


    baz
    +
    foo
    bar

    !


    baz
    +
    foo
    bar

    !


    baz
    +
    foo
    bar

    !


    baz
    +
    foo
    bar

    !


    baz
    +
    foo
    bar

    !


    baz
    foo bar
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_bug422403-1.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_bug422403-1.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_bug422403-1.html 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_bug422403-1.html 2015-02-03 14:33:31.000000000 +0000 @@ -29,7 +29,14 @@ var ios = SpecialPowers.Cc['@mozilla.org/network/io-service;1'] .getService(SpecialPowers.Ci.nsIIOService); - var chann = ios.newChannel(aFile, aCharset, baseUri); + var chann = ios.newChannel2(aFile, + aCharset, + baseUri, + null, // aLoadingNode + SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + SpecialPowers.Ci.nsILoadInfo.SEC_NORMAL, + SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER); var cis = SpecialPowers.Ci.nsIConverterInputStream; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_bug422403-2.xhtml thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_bug422403-2.xhtml --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_bug422403-2.xhtml 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_bug422403-2.xhtml 2015-02-03 14:33:31.000000000 +0000 @@ -29,7 +29,14 @@ var ios = SpecialPowers.Cc['@mozilla.org/network/io-service;1'] .getService(SpecialPowers.Ci.nsIIOService); - var chann = ios.newChannel(aFile, aCharset, baseUri); + var chann = ios.newChannel2(aFile, + aCharset, + baseUri, + null, // aLoadingNode + SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + SpecialPowers.Ci.nsILoadInfo.SEC_NORMAL, + SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER); var cis = SpecialPowers.Ci.nsIConverterInputStream; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_bug424359-1.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_bug424359-1.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_bug424359-1.html 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_bug424359-1.html 2015-02-03 14:33:31.000000000 +0000 @@ -29,7 +29,14 @@ var ios = SpecialPowers.Cc['@mozilla.org/network/io-service;1'] .getService(SpecialPowers.Ci.nsIIOService); - var chann = ios.newChannel(aFile, aCharset, baseUri); + var chann = ios.newChannel2(aFile, + aCharset, + baseUri, + null, // aLoadingNode + SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + SpecialPowers.Ci.nsILoadInfo.SEC_NORMAL, + SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER); var cis = SpecialPowers.Ci.nsIConverterInputStream; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_bug424359-2.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_bug424359-2.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_bug424359-2.html 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_bug424359-2.html 2015-02-03 14:33:31.000000000 +0000 @@ -29,7 +29,14 @@ var ios = SpecialPowers.Cc['@mozilla.org/network/io-service;1'] .getService(SpecialPowers.Ci.nsIIOService); - var chann = ios.newChannel(aFile, aCharset, baseUri); + var chann = ios.newChannel2(aFile, + aCharset, + baseUri, + null, // aLoadingNode + SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + SpecialPowers.Ci.nsILoadInfo.SEC_NORMAL, + SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER); var cis = SpecialPowers.Ci.nsIConverterInputStream; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_bug498433.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_bug498433.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_bug498433.html 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_bug498433.html 2015-02-03 14:33:32.000000000 +0000 @@ -28,7 +28,14 @@ var ios = SpecialPowers.Cc['@mozilla.org/network/io-service;1'] .getService(SpecialPowers.Ci.nsIIOService); - var chann = ios.newChannel(aFile, aCharset, baseUri); + var chann = ios.newChannel2(aFile, + aCharset, + baseUri, + null, // aLoadingNode + SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + SpecialPowers.Ci.nsILoadInfo.SEC_NORMAL, + SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER); var cis = SpecialPowers.Ci.nsIConverterInputStream; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_bug578096.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_bug578096.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_bug578096.html 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_bug578096.html 2015-02-03 14:33:32.000000000 +0000 @@ -22,8 +22,8 @@ var url = SimpleTest.getTestFileURL("bug578096LoadChromeScript.js"); var script = SpecialPowers.loadChromeScript(url); -script.addMessageListener("file.path", function (message) { - SpecialPowers.wrap(document.getElementById('file')).value = message; +script.addMessageListener("file.created", function (message) { + SpecialPowers.wrap(document.getElementById('file')).mozSetFileArray([message]); var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(event) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_copypaste.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_copypaste.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/test_copypaste.html 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/test_copypaste.html 2015-02-03 14:33:32.000000000 +0000 @@ -5,6 +5,7 @@ Test for copy/paste + @@ -63,6 +64,31 @@ T +
    Copy1then Paste
    • LI
    • +
    + +
      +
    • LI
    Copy2then Paste
    + +
    • +
    • LI
    Copy3then Paste
    + +
    before
    inner
    after
    +
    before
    inner
    after
    +
    + +
    +
    +
    +
    +
    + +
    +1
    +
    3 +
    +
    + + + + +Mozilla Bug 511084 +

    + +
    +  
    +
    + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/unit/test_cspreports.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/unit/test_cspreports.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/unit/test_cspreports.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/unit/test_cspreports.js 2015-02-03 14:33:32.000000000 +0000 @@ -8,6 +8,7 @@ const Cr = Components.results; Cu.import('resource://gre/modules/NetUtil.jsm'); +Cu.import("resource://gre/modules/Services.jsm"); var httpServer = new HttpServer(); httpServer.start(-1); @@ -70,7 +71,14 @@ var selfuri = NetUtil.newURI(REPORT_SERVER_URI + ":" + REPORT_SERVER_PORT + "/foo/self"); - var selfchan = NetUtil.newChannel(selfuri); + var selfchan = NetUtil.newChannel2(selfuri, + null, + null, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); dump("Created test " + id + " : " + policy + "\n\n"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/unit/test_thirdpartyutil.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/unit/test_thirdpartyutil.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/test/unit/test_thirdpartyutil.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/test/unit/test_thirdpartyutil.js 2015-02-03 14:33:32.000000000 +0000 @@ -4,10 +4,13 @@ // Test ThirdPartyUtil methods. See mozIThirdPartyUtil. -Components.utils.import("resource://gre/modules/NetUtil.jsm"); - let Cc = Components.classes; let Ci = Components.interfaces; +let Cu = Components.utils; + +Cu.import("resource://gre/modules/NetUtil.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + let NS_ERROR_INVALID_ARG = Components.results.NS_ERROR_INVALID_ARG; function do_check_throws(f, result, stack) @@ -38,16 +41,44 @@ let spec2 = "http://bar.com/bar.html"; let uri1 = NetUtil.newURI(spec1); let uri2 = NetUtil.newURI(spec2); - let channel1 = NetUtil.newChannel(uri1); - let channel2 = NetUtil.newChannel(uri2); + let channel1 = NetUtil.newChannel2(uri1, + null, + null, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); + let channel2 = NetUtil.newChannel2(uri2, + null, + null, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); // Create some file:// URIs. let filespec1 = "file://foo.txt"; let filespec2 = "file://bar.txt"; let fileuri1 = NetUtil.newURI(filespec1); let fileuri2 = NetUtil.newURI(filespec2); - let filechannel1 = NetUtil.newChannel(fileuri1); - let filechannel2 = NetUtil.newChannel(fileuri2); + let filechannel1 = NetUtil.newChannel2(fileuri1, + null, + null, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); + let filechannel2 = NetUtil.newChannel2(fileuri2, + null, + null, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); // Test isThirdPartyURI. do_check_false(util.isThirdPartyURI(uri1, uri1)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/TextInputProcessor.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/TextInputProcessor.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/TextInputProcessor.cpp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/TextInputProcessor.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,430 @@ +/* -*- 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 "mozilla/EventForwards.h" +#include "mozilla/TextEventDispatcher.h" +#include "mozilla/TextInputProcessor.h" +#include "nsIDocShell.h" +#include "nsIWidget.h" +#include "nsPIDOMWindow.h" +#include "nsPresContext.h" + +using namespace mozilla::widget; + +namespace mozilla { + +/****************************************************************************** + * TextInputProcessorNotification + ******************************************************************************/ + +class TextInputProcessorNotification MOZ_FINAL : + public nsITextInputProcessorNotification +{ +public: + explicit TextInputProcessorNotification(const char* aType) + : mType(aType) + { + } + + NS_DECL_ISUPPORTS + + NS_IMETHOD GetType(nsACString& aType) MOZ_OVERRIDE MOZ_FINAL + { + aType = mType; + return NS_OK; + } + +protected: + ~TextInputProcessorNotification() { } + +private: + nsAutoCString mType; + + TextInputProcessorNotification() { } +}; + +NS_IMPL_ISUPPORTS(TextInputProcessorNotification, + nsITextInputProcessorNotification) + +/****************************************************************************** + * TextInputProcessor + ******************************************************************************/ + +NS_IMPL_ISUPPORTS(TextInputProcessor, + nsITextInputProcessor, + TextEventDispatcherListener, + nsISupportsWeakReference) + +TextInputProcessor::TextInputProcessor() + : mDispatcher(nullptr) + , mForTests(false) +{ +} + +TextInputProcessor::~TextInputProcessor() +{ + if (mDispatcher && mDispatcher->IsComposing()) { + // If this is composing and not canceling the composition, nobody can steal + // the rights of TextEventDispatcher from this instance. Therefore, this + // needs to cancel the composition here. + if (NS_SUCCEEDED(IsValidStateForComposition())) { + nsRefPtr kungFuDeathGrip(mDispatcher); + nsEventStatus status = nsEventStatus_eIgnore; + mDispatcher->CommitComposition(status, &EmptyString()); + } + } +} + +NS_IMETHODIMP +TextInputProcessor::Init(nsIDOMWindow* aWindow, + nsITextInputProcessorCallback* aCallback, + bool* aSucceeded) +{ + MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr"); + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); + if (NS_WARN_IF(!aCallback)) { + *aSucceeded = false; + return NS_ERROR_INVALID_ARG; + } + return InitInternal(aWindow, aCallback, false, *aSucceeded); +} + +NS_IMETHODIMP +TextInputProcessor::InitForTests(nsIDOMWindow* aWindow, + nsITextInputProcessorCallback* aCallback, + uint8_t aOptionalArgc, + bool* aSucceeded) +{ + MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr"); + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); + nsITextInputProcessorCallback* callback = + aOptionalArgc >= 1 ? aCallback : nullptr; + return InitInternal(aWindow, callback, true, *aSucceeded); +} + +nsresult +TextInputProcessor::InitInternal(nsIDOMWindow* aWindow, + nsITextInputProcessorCallback* aCallback, + bool aForTests, + bool& aSucceeded) +{ + aSucceeded = false; + if (NS_WARN_IF(!aWindow)) { + return NS_ERROR_INVALID_ARG; + } + nsCOMPtr pWindow(do_QueryInterface(aWindow)); + if (NS_WARN_IF(!pWindow)) { + return NS_ERROR_INVALID_ARG; + } + nsCOMPtr docShell(pWindow->GetDocShell()); + if (NS_WARN_IF(!docShell)) { + return NS_ERROR_FAILURE; + } + nsRefPtr presContext; + nsresult rv = docShell->GetPresContext(getter_AddRefs(presContext)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + if (NS_WARN_IF(!presContext)) { + return NS_ERROR_FAILURE; + } + nsCOMPtr widget = presContext->GetRootWidget(); + if (NS_WARN_IF(!widget)) { + return NS_ERROR_FAILURE; + } + + nsRefPtr dispatcher = widget->GetTextEventDispatcher(); + MOZ_RELEASE_ASSERT(dispatcher, "TextEventDispatcher must not be null"); + + // If the instance was initialized and is being initialized for same + // dispatcher and same purpose, we don't need to initialize the dispatcher + // again. + if (mDispatcher && dispatcher == mDispatcher && aCallback == mCallback && + aForTests == mForTests) { + aSucceeded = true; + return NS_OK; + } + + // If this instance is composing, don't allow to initialize again. + if (mDispatcher && mDispatcher->IsComposing()) { + return NS_ERROR_ALREADY_INITIALIZED; + } + + // And also if another instance is composing with the new dispatcher, it'll + // fail to steal its ownership. Then, we should not throw an exception, + // just return false. + if (dispatcher->IsComposing()) { + return NS_OK; + } + + // This instance has finished preparing to link to the dispatcher. Therefore, + // let's forget the old dispatcher and purpose. + UnlinkFromTextEventDispatcher(); + + if (aForTests) { + rv = dispatcher->InitForTests(this); + } else { + rv = dispatcher->Init(this); + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mDispatcher = dispatcher; + mCallback = aCallback; + mForTests = aForTests; + aSucceeded = true; + return NS_OK; +} + +void +TextInputProcessor::UnlinkFromTextEventDispatcher() +{ + mDispatcher = nullptr; + mForTests = false; + if (mCallback) { + nsCOMPtr callback(mCallback); + mCallback = nullptr; + + nsRefPtr notification = + new TextInputProcessorNotification("notify-detached"); + bool result = false; + callback->OnNotify(this, notification, &result); + } +} + +nsresult +TextInputProcessor::IsValidStateForComposition() +{ + if (NS_WARN_IF(!mDispatcher)) { + return NS_ERROR_NOT_INITIALIZED; + } + + nsresult rv = mDispatcher->GetState(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +NS_IMETHODIMP +TextInputProcessor::StartComposition(bool* aSucceeded) +{ + MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr"); + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); + *aSucceeded = false; + nsRefPtr kungfuDeathGrip(mDispatcher); + nsresult rv = IsValidStateForComposition(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + nsEventStatus status = nsEventStatus_eIgnore; + rv = mDispatcher->StartComposition(status); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + *aSucceeded = status != nsEventStatus_eConsumeNoDefault && + mDispatcher && mDispatcher->IsComposing(); + return NS_OK; +} + +NS_IMETHODIMP +TextInputProcessor::SetPendingCompositionString(const nsAString& aString) +{ + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); + nsRefPtr kungfuDeathGrip(mDispatcher); + nsresult rv = IsValidStateForComposition(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return mDispatcher->SetPendingCompositionString(aString); +} + +NS_IMETHODIMP +TextInputProcessor::AppendClauseToPendingComposition(uint32_t aLength, + uint32_t aAttribute) +{ + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); + nsRefPtr kungfuDeathGrip(mDispatcher); + switch (aAttribute) { + case ATTR_RAW_CLAUSE: + case ATTR_SELECTED_RAW_CLAUSE: + case ATTR_CONVERTED_CLAUSE: + case ATTR_SELECTED_CLAUSE: + break; + default: + return NS_ERROR_INVALID_ARG; + } + nsresult rv = IsValidStateForComposition(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return mDispatcher->AppendClauseToPendingComposition(aLength, aAttribute); +} + +NS_IMETHODIMP +TextInputProcessor::SetCaretInPendingComposition(uint32_t aOffset) +{ + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); + nsRefPtr kungfuDeathGrip(mDispatcher); + nsresult rv = IsValidStateForComposition(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return mDispatcher->SetCaretInPendingComposition(aOffset, 0); +} + +NS_IMETHODIMP +TextInputProcessor::FlushPendingComposition(bool* aSucceeded) +{ + MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr"); + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); + *aSucceeded = false; + nsRefPtr kungfuDeathGrip(mDispatcher); + nsresult rv = IsValidStateForComposition(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + nsEventStatus status = nsEventStatus_eIgnore; + rv = mDispatcher->FlushPendingComposition(status); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + *aSucceeded = status != nsEventStatus_eConsumeNoDefault; + return rv; +} + +NS_IMETHODIMP +TextInputProcessor::CommitComposition(const nsAString& aCommitString, + uint8_t aOptionalArgc, + bool* aSucceeded) +{ + MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr"); + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); + const nsAString* commitString = + aOptionalArgc >= 1 ? &aCommitString : nullptr; + return CommitCompositionInternal(commitString, aSucceeded); +} + +nsresult +TextInputProcessor::CommitCompositionInternal(const nsAString* aCommitString, + bool* aSucceeded) +{ + if (aSucceeded) { + *aSucceeded = false; + } + nsRefPtr kungfuDeathGrip(mDispatcher); + nsresult rv = IsValidStateForComposition(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + nsEventStatus status = nsEventStatus_eIgnore; + rv = mDispatcher->CommitComposition(status, aCommitString); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + if (aSucceeded) { + *aSucceeded = status != nsEventStatus_eConsumeNoDefault; + } + return rv; +} + +NS_IMETHODIMP +TextInputProcessor::CancelComposition() +{ + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); + return CancelCompositionInternal(); +} + +nsresult +TextInputProcessor::CancelCompositionInternal() +{ + nsRefPtr kungfuDeathGrip(mDispatcher); + nsresult rv = IsValidStateForComposition(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + nsEventStatus status = nsEventStatus_eIgnore; + return mDispatcher->CommitComposition(status, &EmptyString()); +} + +NS_IMETHODIMP +TextInputProcessor::NotifyIME(TextEventDispatcher* aTextEventDispatcher, + const IMENotification& aNotification) +{ + // If This is called while this is being initialized, ignore the call. + if (!mDispatcher) { + return NS_ERROR_NOT_AVAILABLE; + } + MOZ_ASSERT(aTextEventDispatcher == mDispatcher, + "Wrong TextEventDispatcher notifies this"); + NS_ASSERTION(mForTests || mCallback, + "mCallback can be null only when IME is initialized for tests"); + if (mCallback) { + nsRefPtr notification; + switch (aNotification.mMessage) { + case REQUEST_TO_COMMIT_COMPOSITION: { + NS_ASSERTION(aTextEventDispatcher->IsComposing(), + "Why is this requested without composition?"); + notification = new TextInputProcessorNotification("request-to-commit"); + break; + } + case REQUEST_TO_CANCEL_COMPOSITION: { + NS_ASSERTION(aTextEventDispatcher->IsComposing(), + "Why is this requested without composition?"); + notification = new TextInputProcessorNotification("request-to-cancel"); + break; + } + case NOTIFY_IME_OF_FOCUS: + notification = new TextInputProcessorNotification("notify-focus"); + break; + case NOTIFY_IME_OF_BLUR: + notification = new TextInputProcessorNotification("notify-blur"); + break; + default: + return NS_ERROR_NOT_IMPLEMENTED; + } + MOZ_RELEASE_ASSERT(notification); + bool result = false; + nsresult rv = mCallback->OnNotify(this, notification, &result); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return result ? NS_OK : NS_ERROR_FAILURE; + } + + switch (aNotification.mMessage) { + case REQUEST_TO_COMMIT_COMPOSITION: { + NS_ASSERTION(aTextEventDispatcher->IsComposing(), + "Why is this requested without composition?"); + CommitCompositionInternal(); + return NS_OK; + } + case REQUEST_TO_CANCEL_COMPOSITION: { + NS_ASSERTION(aTextEventDispatcher->IsComposing(), + "Why is this requested without composition?"); + CancelCompositionInternal(); + return NS_OK; + } + default: + return NS_ERROR_NOT_IMPLEMENTED; + } +} + +NS_IMETHODIMP_(void) +TextInputProcessor::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) +{ + // If This is called while this is being initialized, ignore the call. + if (!mDispatcher) { + return; + } + MOZ_ASSERT(aTextEventDispatcher == mDispatcher, + "Wrong TextEventDispatcher notifies this"); + UnlinkFromTextEventDispatcher(); +} + +} // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/TextInputProcessor.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/TextInputProcessor.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/TextInputProcessor.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/TextInputProcessor.h 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,58 @@ +/* -*- 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 mozilla_dom_textinputprocessor_h_ +#define mozilla_dom_textinputprocessor_h_ + +#include "mozilla/TextEventDispatcherListener.h" +#include "nsITextInputProcessor.h" +#include "nsITextInputProcessorCallback.h" + +namespace mozilla { + +namespace widget{ +class TextEventDispatcher; +} // namespace widget + +class TextInputProcessor MOZ_FINAL : public nsITextInputProcessor + , public widget::TextEventDispatcherListener +{ + typedef mozilla::widget::IMENotification IMENotification; + typedef mozilla::widget::TextEventDispatcher TextEventDispatcher; + +public: + TextInputProcessor(); + + NS_DECL_ISUPPORTS + NS_DECL_NSITEXTINPUTPROCESSOR + + // TextEventDispatcherListener + NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher, + const IMENotification& aNotification) MOZ_OVERRIDE; + NS_IMETHOD_(void) + OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) MOZ_OVERRIDE; + +protected: + virtual ~TextInputProcessor(); + +private: + nsresult InitInternal(nsIDOMWindow* aWindow, + nsITextInputProcessorCallback* aCallback, + bool aForTests, + bool& aSucceeded); + nsresult CommitCompositionInternal(const nsAString* aCommitString = nullptr, + bool* aSucceeded = nullptr); + nsresult CancelCompositionInternal(); + nsresult IsValidStateForComposition(); + void UnlinkFromTextEventDispatcher(); + + TextEventDispatcher* mDispatcher; // [Weak] + nsCOMPtr mCallback; + bool mForTests; +}; + +} // namespace mozilla + +#endif // #ifndef mozilla_dom_textinputprocessor_h_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/URLSearchParams.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/URLSearchParams.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/base/URLSearchParams.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/base/URLSearchParams.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -190,7 +190,7 @@ return; } - if (!aOutput.SetLength(outputLength, fallible_t())) { + if (!aOutput.SetLength(outputLength, fallible)) { return; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/Bindings.conf thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/Bindings.conf --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/Bindings.conf 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/Bindings.conf 2015-02-03 14:33:32.000000000 +0000 @@ -633,6 +633,14 @@ 'headerFile': 'nsIMediaList.h', }, +'MediaKeys' : { + 'implicitJSContext': [ 'createSession'] +}, + +'MediaKeyStatusMap' : { + 'implicitJSContext': [ 'size', 'get', 'has' ] +}, + 'MediaStream': { 'headerFile': 'DOMMediaStream.h', 'nativeType': 'mozilla::DOMMediaStream' @@ -1829,7 +1837,7 @@ addExternalIface('MozRDFResource', nativeType='nsIRDFResource', notflattened=True) addExternalIface('MozSmsMessage') addExternalIface('MozTreeView', nativeType='nsITreeView', - headerFile='nsITreeView.h') + headerFile='nsITreeView.h', notflattened=True) addExternalIface('MozWakeLockListener', headerFile='nsIDOMWakeLockListener.h') addExternalIface('MozXULTemplateBuilder', nativeType='nsIXULTemplateBuilder') addExternalIface('nsIBrowserDOMWindow', nativeType='nsIBrowserDOMWindow', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/BindingUtils.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/BindingUtils.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/BindingUtils.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/BindingUtils.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -9,8 +9,6 @@ #include #include -#include "JavaScriptParent.h" - #include "mozilla/DebugOnly.h" #include "mozilla/FloatingPoint.h" #include "mozilla/Assertions.h" @@ -20,6 +18,7 @@ #include "jsfriendapi.h" #include "nsContentUtils.h" #include "nsGlobalWindow.h" +#include "nsIDocShell.h" #include "nsIDOMGlobalPropertyInitializer.h" #include "nsIPermissionManager.h" #include "nsIPrincipal.h" @@ -43,6 +42,7 @@ #include "mozilla/dom/HTMLEmbedElementBinding.h" #include "mozilla/dom/HTMLAppletElementBinding.h" #include "mozilla/dom/Promise.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "WorkerPrivate.h" #include "nsDOMClassInfo.h" @@ -481,8 +481,8 @@ JS::Rooted constructor(cx); if (constructorClass) { MOZ_ASSERT(constructorProto); - constructor = JS_NewObject(cx, Jsvalify(constructorClass), constructorProto, - global); + constructor = JS_NewObjectWithGivenProto(cx, Jsvalify(constructorClass), + constructorProto, global); } else { MOZ_ASSERT(constructorNative); MOZ_ASSERT(constructorProto == JS_GetFunctionPrototype(cx, global)); @@ -2381,12 +2381,47 @@ return false; } +void +HandlePrerenderingViolation(nsPIDOMWindow* aWindow) +{ + // Suspend the window and its workers, and its children too. + aWindow->SuspendTimeouts(); + + // Suspend event handling on the document + nsCOMPtr doc = aWindow->GetExtantDoc(); + if (doc) { + doc->SuppressEventHandling(nsIDocument::eEvents); + } +} + bool -CheckSafetyInPrerendering(JSContext* aCx, JSObject* aObj) +EnforceNotInPrerendering(JSContext* aCx, JSObject* aObj) { - //TODO: Check if page is being prerendered. - //Returning false for now. - return false; + JS::Rooted thisObj(aCx, js::CheckedUnwrap(aObj)); + if (!thisObj) { + // Without a this object, we cannot check the safety. + return true; + } + nsGlobalWindow* window = xpc::WindowGlobalOrNull(thisObj); + if (!window) { + // Without a window, we cannot check the safety. + return true; + } + + nsIDocShell* docShell = window->GetDocShell(); + if (!docShell) { + // Without a docshell, we cannot check the safety. + return true; + } + + if (docShell->GetIsPrerendered()) { + HandlePrerenderingViolation(window); + // When the bindings layer sees a false return value, it returns false form + // the JSNative in order to trigger an uncatchable exception. + return false; + } + + return true; } bool @@ -2691,6 +2726,10 @@ const nsIID &iid, void **ppArg) { + if (!NS_IsMainThread()) { + return NS_ERROR_NOT_AVAILABLE; + } + nsISupports *iface = xpc::UnwrapReflectorToISupports(src); if (iface) { if (NS_FAILED(iface->QueryInterface(iid, ppArg))) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/BindingUtils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/BindingUtils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/BindingUtils.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/BindingUtils.h 2015-02-03 14:33:32.000000000 +0000 @@ -9,6 +9,7 @@ #include "jsfriendapi.h" #include "jswrapper.h" +#include "js/Conversions.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Alignment.h" #include "mozilla/Array.h" @@ -2797,7 +2798,7 @@ JS::Handle aProto, JS::Handle aParent, T* aNative, JS::MutableHandle aReflector) { - aReflector.set(JS_NewObject(aCx, aClass, aProto, aParent)); + aReflector.set(JS_NewObjectWithGivenProto(aCx, aClass, aProto, aParent)); if (aReflector) { js::SetReservedSlot(aReflector, DOM_OBJECT_SLOT, JS::PrivateValue(aNative)); mNative = aNative; @@ -3142,9 +3143,20 @@ bool CheckPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]); -//Returns true if page is being prerendered. -bool -CheckSafetyInPrerendering(JSContext* aCx, JSObject* aObj); +// This function is called by the bindings layer for methods/getters/setters +// that are not safe to be called in prerendering mode. It checks to make sure +// that the |this| object is not running in a global that is in prerendering +// mode. Otherwise, it aborts execution of timers and event handlers, and +// returns false which gets converted to an uncatchable exception by the +// bindings layer. +bool +EnforceNotInPrerendering(JSContext* aCx, JSObject* aObj); + +// Handles the violation of a blacklisted action in prerendering mode by +// aborting the scripts, and preventing timers and event handlers from running +// in the window in the future. +void +HandlePrerenderingViolation(nsPIDOMWindow* aWindow); bool CallerSubsumes(JSObject* aObject); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/Codegen.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/Codegen.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/Codegen.py 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/Codegen.py 2015-02-03 14:33:32.000000000 +0000 @@ -392,21 +392,13 @@ ${objectMoved} /* objectMovedOp */ }, { - nullptr, /* lookupGeneric */ nullptr, /* lookupProperty */ - nullptr, /* lookupElement */ - nullptr, /* defineGeneric */ nullptr, /* defineProperty */ - nullptr, /* defineElement */ - nullptr, /* getGeneric */ nullptr, /* getProperty */ - nullptr, /* getElement */ - nullptr, /* setGeneric */ nullptr, /* setProperty */ - nullptr, /* setElement */ nullptr, /* getOwnPropertyDescriptor */ - nullptr, /* setGenericAttributes */ - nullptr, /* deleteGeneric */ + nullptr, /* setPropertyAttributes */ + nullptr, /* deleteProperty */ nullptr, /* watch */ nullptr, /* unwatch */ nullptr, /* getElements */ @@ -6506,8 +6498,10 @@ for i in descriptor.interface.getInheritedInterfaces())): cgThings.append(CGGeneric(dedent( """ - if (mozilla::dom::CheckSafetyInPrerendering(cx, obj)) { - //TODO: Handle call into unsafe API during Prerendering (Bug 730101) + if (!mozilla::dom::EnforceNotInPrerendering(cx, obj)) { + // Return false from the JSNative in order to trigger + // an uncatchable exception. + MOZ_ASSERT(!JS_IsExceptionPending(cx)); return false; } """))) @@ -8440,10 +8434,10 @@ def declare(self): decl = fill( """ - MOZ_BEGIN_ENUM_CLASS(${name}, uint32_t) + enum class ${name} : uint32_t { $*{enums} EndGuard_ - MOZ_END_ENUM_CLASS(${name}) + }; """, name=self.enum.identifier.name, enums=",\n".join(map(getEnumValueName, self.enum.values())) + ",\n") diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/moz.build 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/moz.build 2015-02-03 14:33:32.000000000 +0000 @@ -58,7 +58,6 @@ '/dom/xslt/base', '/dom/xslt/xpath', '/dom/xul', - '/js/ipc', '/js/xpconnect/src', '/js/xpconnect/wrappers', '/layout/style', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/MozMap.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/MozMap.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/MozMap.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/MozMap.h 2015-02-03 14:33:32.000000000 +0000 @@ -102,9 +102,7 @@ DataType* AddEntry(const nsAString& aKey) NS_WARN_UNUSED_RESULT { - // XXXbz can't just use fallible_t() because our superclass has a - // private typedef by that name. - EntryType* ent = this->PutEntry(aKey, mozilla::fallible_t()); + EntryType* ent = this->PutEntry(aKey, fallible); if (!ent) { return nullptr; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/PrimitiveConversions.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/PrimitiveConversions.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/PrimitiveConversions.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/PrimitiveConversions.h 2015-02-03 14:33:32.000000000 +0000 @@ -16,6 +16,7 @@ #include #include "jsapi.h" +#include "js/Conversions.h" #include "mozilla/Assertions.h" #include "mozilla/ErrorResult.h" #include "mozilla/FloatingPoint.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/test/chrome.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/test/chrome.ini --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/test/chrome.ini 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/test/chrome.ini 2015-02-03 14:33:32.000000000 +0000 @@ -6,3 +6,8 @@ [test_dom_xrays.html] [test_proxies_via_xray.html] [test_document_location_via_xray_cached.html] +[test_blacklisted_prerendering_function.xul] +support-files = + file_focuser.html + file_fullScreenPropertyAccessor.html +skip-if = e10s # prerendering doesn't work in e10s yet diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/test/file_focuser.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/test/file_focuser.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/test/file_focuser.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/test/file_focuser.html 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,24 @@ + +
    + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/test/file_fullScreenPropertyAccessor.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/test/file_fullScreenPropertyAccessor.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/test/file_fullScreenPropertyAccessor.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/test/file_fullScreenPropertyAccessor.html 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,24 @@ + +
    + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/test/mochitest.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/test/mochitest.ini --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/test/mochitest.ini 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/test/mochitest.ini 2015-02-03 14:33:32.000000000 +0000 @@ -57,3 +57,4 @@ skip-if = debug == false [test_promise_rejections_from_jsimplemented.html] skip-if = debug == false +[test_worker_UnwrapArg.html] diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/test/test_blacklisted_prerendering_function.xul thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/test/test_blacklisted_prerendering_function.xul --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/test/test_blacklisted_prerendering_function.xul 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/test/test_blacklisted_prerendering_function.xul 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,124 @@ + + + + + + + + + + +Mozilla Bug 1069719 +

    + +
    +
    + + + +
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/test/test_sequence_detection.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/test/test_sequence_detection.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/test/test_sequence_detection.html 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/test/test_sequence_detection.html 2015-02-03 14:33:32.000000000 +0000 @@ -16,9 +16,7 @@ var testInterfaceJS = new TestInterfaceJS(); ok(testInterfaceJS, "got a TestInterfaceJS object"); - var JS_HAS_SYMBOLS = typeof Symbol === "function"; - var std_iterator = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator"; - var nonIterableObject = {[std_iterator]: 5}; + var nonIterableObject = {[Symbol.iterator]: 5}; try { testInterfaceJS.testSequenceOverload(nonIterableObject); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/test/test_worker_UnwrapArg.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/test/test_worker_UnwrapArg.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bindings/test/test_worker_UnwrapArg.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bindings/test/test_worker_UnwrapArg.html 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,58 @@ + + + + + + Test for Bug 1127206 + + + + + +Mozilla Bug 1127206 +

    + +
    +
    + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -1600,7 +1600,7 @@ return NS_ERROR_ILLEGAL_VALUE; // end of PDU } - const char* end = static_cast(memchr(str, '\0', aPDU.GetSize())); + const char* end = static_cast(memchr(str, '\0', aPDU.GetSize() + 1)); if (NS_WARN_IF(!end)) { return NS_ERROR_ILLEGAL_VALUE; // no string terminator } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonA2dpInterface.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonA2dpInterface.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonA2dpInterface.cpp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonA2dpInterface.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,429 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "BluetoothDaemonA2dpInterface.h" +#include "BluetoothDaemonSetupInterface.h" +#include "mozilla/unused.h" + +BEGIN_BLUETOOTH_NAMESPACE + +// +// A2DP module +// + +BluetoothA2dpNotificationHandler* + BluetoothDaemonA2dpModule::sNotificationHandler; + +void +BluetoothDaemonA2dpModule::SetNotificationHandler( + BluetoothA2dpNotificationHandler* aNotificationHandler) +{ + sNotificationHandler = aNotificationHandler; +} + +nsresult +BluetoothDaemonA2dpModule::Send(BluetoothDaemonPDU* aPDU, + BluetoothA2dpResultHandler* aRes) +{ + aRes->AddRef(); // Keep reference for response + return Send(aPDU, static_cast(aRes)); +} + +void +BluetoothDaemonA2dpModule::HandleSvc(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, void* aUserData) +{ + static void (BluetoothDaemonA2dpModule::* const HandleOp[])( + const BluetoothDaemonPDUHeader&, BluetoothDaemonPDU&, void*) = { + INIT_ARRAY_AT(0, &BluetoothDaemonA2dpModule::HandleRsp), + INIT_ARRAY_AT(1, &BluetoothDaemonA2dpModule::HandleNtf), + }; + + MOZ_ASSERT(!NS_IsMainThread()); + + // negate twice to map bit to 0/1 + unsigned int isNtf = !!(aHeader.mOpcode & 0x80); + + (this->*(HandleOp[isNtf]))(aHeader, aPDU, aUserData); +} + +// Commands +// + +nsresult +BluetoothDaemonA2dpModule::ConnectCmd( + const nsAString& aRemoteAddr, BluetoothA2dpResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu(new BluetoothDaemonPDU(SERVICE_ID, + OPCODE_CONNECT, + 6)); // Address + nsresult rv = PackPDU( + PackConversion(aRemoteAddr), *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonA2dpModule::DisconnectCmd( + const nsAString& aRemoteAddr, BluetoothA2dpResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu(new BluetoothDaemonPDU(SERVICE_ID, + OPCODE_DISCONNECT, + 6)); // Address + nsresult rv = PackPDU( + PackConversion(aRemoteAddr), *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + unused << pdu.forget(); + return NS_OK; +} + +// Responses +// + +void +BluetoothDaemonA2dpModule::ErrorRsp( + const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, BluetoothA2dpResultHandler* aRes) +{ + ErrorRunnable::Dispatch( + aRes, &BluetoothA2dpResultHandler::OnError, UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonA2dpModule::ConnectRsp( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU, + BluetoothA2dpResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothA2dpResultHandler::Connect, UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonA2dpModule::DisconnectRsp( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU, + BluetoothA2dpResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothA2dpResultHandler::Disconnect, UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonA2dpModule::HandleRsp( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU, + void* aUserData) +{ + static void (BluetoothDaemonA2dpModule::* const HandleRsp[])( + const BluetoothDaemonPDUHeader&, + BluetoothDaemonPDU&, + BluetoothA2dpResultHandler*) = { + INIT_ARRAY_AT(OPCODE_ERROR, + &BluetoothDaemonA2dpModule::ErrorRsp), + INIT_ARRAY_AT(OPCODE_CONNECT, + &BluetoothDaemonA2dpModule::ConnectRsp), + INIT_ARRAY_AT(OPCODE_DISCONNECT, + &BluetoothDaemonA2dpModule::DisconnectRsp), + }; + + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread + + if (NS_WARN_IF(!(aHeader.mOpcode < MOZ_ARRAY_LENGTH(HandleRsp))) || + NS_WARN_IF(!HandleRsp[aHeader.mOpcode])) { + return; + } + + nsRefPtr res = + already_AddRefed( + static_cast(aUserData)); + + if (!res) { + return; // Return early if no result handler has been set for response + } + + (this->*(HandleRsp[aHeader.mOpcode]))(aHeader, aPDU, res); +} + +// Notifications +// + +// Returns the current notification handler to a notification runnable +class BluetoothDaemonA2dpModule::NotificationHandlerWrapper MOZ_FINAL +{ +public: + typedef BluetoothA2dpNotificationHandler ObjectType; + + static ObjectType* GetInstance() + { + MOZ_ASSERT(NS_IsMainThread()); + + return sNotificationHandler; + } +}; + +// Init operator class for ConnectionStateNotification +class BluetoothDaemonA2dpModule::ConnectionStateInitOp MOZ_FINAL + : private PDUInitOp +{ +public: + ConnectionStateInitOp(BluetoothDaemonPDU& aPDU) + : PDUInitOp(aPDU) + { } + + nsresult + operator () (BluetoothA2dpConnectionState& aArg1, nsString& aArg2) const + { + BluetoothDaemonPDU& pdu = GetPDU(); + + /* Read state */ + nsresult rv = UnpackPDU(pdu, aArg1); + if (NS_FAILED(rv)) { + return rv; + } + + /* Read address */ + rv = UnpackPDU( + pdu, UnpackConversion(aArg2)); + if (NS_FAILED(rv)) { + return rv; + } + WarnAboutTrailingData(); + return NS_OK; + } +}; + +void +BluetoothDaemonA2dpModule::ConnectionStateNtf( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU) +{ + ConnectionStateNotification::Dispatch( + &BluetoothA2dpNotificationHandler::ConnectionStateNotification, + ConnectionStateInitOp(aPDU)); +} + +// Init operator class for AudioStateNotification +class BluetoothDaemonA2dpModule::AudioStateInitOp MOZ_FINAL + : private PDUInitOp +{ +public: + AudioStateInitOp(BluetoothDaemonPDU& aPDU) + : PDUInitOp(aPDU) + { } + + nsresult + operator () (BluetoothA2dpAudioState& aArg1, + nsString& aArg2) const + { + BluetoothDaemonPDU& pdu = GetPDU(); + + /* Read state */ + nsresult rv = UnpackPDU(pdu, aArg1); + if (NS_FAILED(rv)) { + return rv; + } + + /* Read address */ + rv = UnpackPDU( + pdu, UnpackConversion(aArg2)); + if (NS_FAILED(rv)) { + return rv; + } + WarnAboutTrailingData(); + return NS_OK; + } +}; + +void +BluetoothDaemonA2dpModule::AudioStateNtf( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU) +{ + AudioStateNotification::Dispatch( + &BluetoothA2dpNotificationHandler::AudioStateNotification, + AudioStateInitOp(aPDU)); +} + +void +BluetoothDaemonA2dpModule::HandleNtf( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU, + void* aUserData) +{ + static void (BluetoothDaemonA2dpModule::* const HandleNtf[])( + const BluetoothDaemonPDUHeader&, BluetoothDaemonPDU&) = { + INIT_ARRAY_AT(0, &BluetoothDaemonA2dpModule::ConnectionStateNtf), + INIT_ARRAY_AT(1, &BluetoothDaemonA2dpModule::AudioStateNtf), + }; + + MOZ_ASSERT(!NS_IsMainThread()); + + uint8_t index = aHeader.mOpcode - 0x81; + + if (NS_WARN_IF(!(index < MOZ_ARRAY_LENGTH(HandleNtf))) || + NS_WARN_IF(!HandleNtf[index])) { + return; + } + + (this->*(HandleNtf[index]))(aHeader, aPDU); +} + +// +// A2DP interface +// + +BluetoothDaemonA2dpInterface::BluetoothDaemonA2dpInterface( + BluetoothDaemonA2dpModule* aModule) + : mModule(aModule) +{ } + +BluetoothDaemonA2dpInterface::~BluetoothDaemonA2dpInterface() +{ } + +class BluetoothDaemonA2dpInterface::InitResultHandler MOZ_FINAL + : public BluetoothSetupResultHandler +{ +public: + InitResultHandler(BluetoothA2dpResultHandler* aRes) + : mRes(aRes) + { + MOZ_ASSERT(mRes); + } + + void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE + { + MOZ_ASSERT(NS_IsMainThread()); + + mRes->OnError(aStatus); + } + + void RegisterModule() MOZ_OVERRIDE + { + MOZ_ASSERT(NS_IsMainThread()); + + mRes->Init(); + } + +private: + nsRefPtr mRes; +}; + +void +BluetoothDaemonA2dpInterface::Init( + BluetoothA2dpNotificationHandler* aNotificationHandler, + BluetoothA2dpResultHandler* aRes) +{ + // Set notification handler _before_ registering the module. It could + // happen that we receive notifications, before the result handler runs. + mModule->SetNotificationHandler(aNotificationHandler); + + InitResultHandler* res; + + if (aRes) { + res = new InitResultHandler(aRes); + } else { + // We don't need a result handler if the caller is not interested. + res = nullptr; + } + + nsresult rv = mModule->RegisterModule(BluetoothDaemonA2dpModule::SERVICE_ID, + 0x00, res); + if (NS_FAILED(rv) && aRes) { + DispatchError(aRes, STATUS_FAIL); + } +} + +class BluetoothDaemonA2dpInterface::CleanupResultHandler MOZ_FINAL + : public BluetoothSetupResultHandler +{ +public: + CleanupResultHandler(BluetoothDaemonA2dpModule* aModule, + BluetoothA2dpResultHandler* aRes) + : mModule(aModule) + , mRes(aRes) + { + MOZ_ASSERT(mModule); + } + + void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE + { + MOZ_ASSERT(NS_IsMainThread()); + + if (mRes) { + mRes->OnError(aStatus); + } + } + + void UnregisterModule() MOZ_OVERRIDE + { + MOZ_ASSERT(NS_IsMainThread()); + + // Clear notification handler _after_ module has been + // unregistered. While unregistering the module, we might + // still receive notifications. + mModule->SetNotificationHandler(nullptr); + + if (mRes) { + mRes->Cleanup(); + } + } + +private: + BluetoothDaemonA2dpModule* mModule; + nsRefPtr mRes; +}; + +void +BluetoothDaemonA2dpInterface::Cleanup( + BluetoothA2dpResultHandler* aRes) +{ + mModule->UnregisterModule(BluetoothDaemonA2dpModule::SERVICE_ID, + new CleanupResultHandler(mModule, aRes)); +} + +/* Connect / Disconnect */ + +void +BluetoothDaemonA2dpInterface::Connect( + const nsAString& aBdAddr, BluetoothA2dpResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + mModule->ConnectCmd(aBdAddr, aRes); +} + +void +BluetoothDaemonA2dpInterface::Disconnect( + const nsAString& aBdAddr, BluetoothA2dpResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + mModule->DisconnectCmd(aBdAddr, aRes); +} + +void +BluetoothDaemonA2dpInterface::DispatchError( + BluetoothA2dpResultHandler* aRes, BluetoothStatus aStatus) +{ + BluetoothResultRunnable1::Dispatch( + aRes, &BluetoothA2dpResultHandler::OnError, + ConstantInitOp1(aStatus)); +} + +END_BLUETOOTH_NAMESPACE diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonA2dpInterface.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonA2dpInterface.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonA2dpInterface.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonA2dpInterface.h 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,152 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_bluetooth_bluetoothdaemona2dpinterface_h +#define mozilla_dom_bluetooth_bluetoothdaemona2dpinterface_h + +#include "BluetoothDaemonHelpers.h" +#include "BluetoothInterface.h" +#include "BluetoothInterfaceHelpers.h" + +BEGIN_BLUETOOTH_NAMESPACE + +class BluetoothSetupResultHandler; + +class BluetoothDaemonA2dpModule +{ +public: + enum { + SERVICE_ID = 0x06 + }; + + enum { + OPCODE_ERROR = 0x00, + OPCODE_CONNECT = 0x01, + OPCODE_DISCONNECT = 0x02 + }; + + virtual nsresult Send(BluetoothDaemonPDU* aPDU, void* aUserData) = 0; + + virtual nsresult RegisterModule(uint8_t aId, uint8_t aMode, + BluetoothSetupResultHandler* aRes) = 0; + + virtual nsresult UnregisterModule(uint8_t aId, + BluetoothSetupResultHandler* aRes) = 0; + + void SetNotificationHandler( + BluetoothA2dpNotificationHandler* aNotificationHandler); + + // + // Commands + // + + nsresult ConnectCmd(const nsAString& aBdAddr, + BluetoothA2dpResultHandler* aRes); + nsresult DisconnectCmd(const nsAString& aBdAddr, + BluetoothA2dpResultHandler* aRes); + +protected: + nsresult Send(BluetoothDaemonPDU* aPDU, + BluetoothA2dpResultHandler* aRes); + + void HandleSvc(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, void* aUserData); + + // + // Responses + // + + typedef BluetoothResultRunnable0 + ResultRunnable; + + typedef BluetoothResultRunnable1 + ErrorRunnable; + + void ErrorRsp(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + BluetoothA2dpResultHandler* aRes); + + void ConnectRsp(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + BluetoothA2dpResultHandler* aRes); + + void DisconnectRsp(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + BluetoothA2dpResultHandler* aRes); + + void HandleRsp(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + void* aUserData); + + // + // Notifications + // + + class NotificationHandlerWrapper; + + typedef BluetoothNotificationRunnable2 + ConnectionStateNotification; + + typedef BluetoothNotificationRunnable2 + AudioStateNotification; + + class AudioStateInitOp; + class ConnectionStateInitOp; + + void ConnectionStateNtf(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU); + + void AudioStateNtf(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU); + + void HandleNtf(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + void* aUserData); + + static BluetoothA2dpNotificationHandler* sNotificationHandler; +}; + +class BluetoothDaemonA2dpInterface MOZ_FINAL + : public BluetoothA2dpInterface +{ + class CleanupResultHandler; + class InitResultHandler; + +public: + BluetoothDaemonA2dpInterface(BluetoothDaemonA2dpModule* aModule); + ~BluetoothDaemonA2dpInterface(); + + void Init( + BluetoothA2dpNotificationHandler* aNotificationHandler, + BluetoothA2dpResultHandler* aRes); + void Cleanup(BluetoothA2dpResultHandler* aRes); + + /* Connect / Disconnect */ + + void Connect(const nsAString& aBdAddr, + BluetoothA2dpResultHandler* aRes); + void Disconnect(const nsAString& aBdAddr, + BluetoothA2dpResultHandler* aRes); + +private: + void DispatchError(BluetoothA2dpResultHandler* aRes, + BluetoothStatus aStatus); + + BluetoothDaemonA2dpModule* mModule; +}; + +END_BLUETOOTH_NAMESPACE + +#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonAvrcpInterface.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonAvrcpInterface.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonAvrcpInterface.cpp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonAvrcpInterface.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,1056 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "BluetoothDaemonAvrcpInterface.h" +#include "BluetoothDaemonSetupInterface.h" +#include "mozilla/unused.h" + +BEGIN_BLUETOOTH_NAMESPACE + +// +// AVRCP module +// + +BluetoothAvrcpNotificationHandler* + BluetoothDaemonAvrcpModule::sNotificationHandler; + +void +BluetoothDaemonAvrcpModule::SetNotificationHandler( + BluetoothAvrcpNotificationHandler* aNotificationHandler) +{ + sNotificationHandler = aNotificationHandler; +} + +nsresult +BluetoothDaemonAvrcpModule::Send(BluetoothDaemonPDU* aPDU, + BluetoothAvrcpResultHandler* aRes) +{ + if (aRes) { + aRes->AddRef(); // Keep reference for response + } + return Send(aPDU, static_cast(aRes)); +} + +void +BluetoothDaemonAvrcpModule::HandleSvc(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, void* aUserData) +{ + static void (BluetoothDaemonAvrcpModule::* const HandleOp[])( + const BluetoothDaemonPDUHeader&, BluetoothDaemonPDU&, void*) = { + INIT_ARRAY_AT(0, &BluetoothDaemonAvrcpModule::HandleRsp), + INIT_ARRAY_AT(1, &BluetoothDaemonAvrcpModule::HandleNtf), + }; + + MOZ_ASSERT(!NS_IsMainThread()); + + unsigned int isNtf = !!(aHeader.mOpcode & 0x80); + + (this->*(HandleOp[isNtf]))(aHeader, aPDU, aUserData); +} + +// Commands +// + +nsresult +BluetoothDaemonAvrcpModule::GetPlayStatusRspCmd( + ControlPlayStatus aPlayStatus, uint32_t aSongLen, uint32_t aSongPos, + BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new BluetoothDaemonPDU(SERVICE_ID, OPCODE_GET_PLAY_STATUS_RSP, + 1 + // Play status + 4 + // Duration + 4)); // Position + + nsresult rv = PackPDU(aPlayStatus, aSongLen, aSongPos, *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonAvrcpModule::ListPlayerAppAttrRspCmd( + int aNumAttr, const BluetoothAvrcpPlayerAttribute* aPAttrs, + BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new BluetoothDaemonPDU(SERVICE_ID, OPCODE_LIST_PLAYER_APP_ATTR_RSP, + 1 + // # Attributes + aNumAttr)); // Player attributes + + nsresult rv = PackPDU( + PackConversion(aNumAttr), + PackArray(aPAttrs, aNumAttr), *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonAvrcpModule::ListPlayerAppValueRspCmd( + int aNumVal, uint8_t* aPVals, BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new BluetoothDaemonPDU(SERVICE_ID, OPCODE_LIST_PLAYER_APP_VALUE_RSP, + 1 + // # Values + aNumVal)); // Player values + + nsresult rv = PackPDU(PackConversion(aNumVal), + PackArray(aPVals, aNumVal), *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonAvrcpModule::GetPlayerAppValueRspCmd( + uint8_t aNumAttrs, const uint8_t* aIds, const uint8_t* aValues, + BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new BluetoothDaemonPDU(SERVICE_ID, OPCODE_GET_PLAYER_APP_VALUE_RSP, + 1 + // # Pairs + 2 * aNumAttrs)); // Attribute-value pairs + nsresult rv = PackPDU( + aNumAttrs, + BluetoothAvrcpAttributeValuePairs(aIds, aValues, aNumAttrs), *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonAvrcpModule::GetPlayerAppAttrTextRspCmd( + int aNumAttr, const uint8_t* aIds, const char** aTexts, + BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new BluetoothDaemonPDU(SERVICE_ID, OPCODE_GET_PLAYER_APP_ATTR_TEXT_RSP, + 0)); // Dynamically allocated + nsresult rv = PackPDU( + PackConversion(aNumAttr), + BluetoothAvrcpAttributeTextPairs(aIds, aTexts, aNumAttr), *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonAvrcpModule::GetPlayerAppValueTextRspCmd( + int aNumVal, const uint8_t* aIds, const char** aTexts, + BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new BluetoothDaemonPDU(SERVICE_ID, OPCODE_GET_PLAYER_APP_VALUE_TEXT_RSP, + 0)); // Dynamically allocated + nsresult rv = PackPDU( + PackConversion(aNumVal), + BluetoothAvrcpAttributeTextPairs(aIds, aTexts, aNumVal), *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonAvrcpModule::GetElementAttrRspCmd( + uint8_t aNumAttr, const BluetoothAvrcpElementAttribute* aAttr, + BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new BluetoothDaemonPDU(SERVICE_ID, OPCODE_GET_ELEMENT_ATTR_RSP, + 0)); // Dynamically allocated + nsresult rv = PackPDU( + aNumAttr, + PackArray(aAttr, aNumAttr), *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonAvrcpModule::SetPlayerAppValueRspCmd( + BluetoothAvrcpStatus aRspStatus, BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new BluetoothDaemonPDU(SERVICE_ID, OPCODE_SET_PLAYER_APP_VALUE_RSP, + 1)); // Status code + + nsresult rv = PackPDU(aRspStatus, *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonAvrcpModule::RegisterNotificationRspCmd( + BluetoothAvrcpEvent aEvent, BluetoothAvrcpNotification aType, + const BluetoothAvrcpNotificationParam& aParam, + BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new BluetoothDaemonPDU(SERVICE_ID, OPCODE_REGISTER_NOTIFICATION_RSP, + 1 + // Event + 1 + // Type + 1 + // Data length + 256)); // Maximum data length + + nsresult rv = PackPDU(aEvent, aType, + BluetoothAvrcpEventParamPair(aEvent, aParam), *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonAvrcpModule::SetVolumeCmd(uint8_t aVolume, + BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new BluetoothDaemonPDU(SERVICE_ID, OPCODE_SET_VOLUME, + 1)); // Volume + + nsresult rv = PackPDU(aVolume, *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + unused << pdu.forget(); + return NS_OK; +} + +// Responses +// + +void +BluetoothDaemonAvrcpModule::ErrorRsp( + const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, BluetoothAvrcpResultHandler* aRes) +{ + ErrorRunnable::Dispatch( + aRes, &BluetoothAvrcpResultHandler::OnError, UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonAvrcpModule::GetPlayStatusRspRsp( + const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, BluetoothAvrcpResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothAvrcpResultHandler::GetPlayStatusRsp, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonAvrcpModule::ListPlayerAppAttrRspRsp( + const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, BluetoothAvrcpResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothAvrcpResultHandler::ListPlayerAppAttrRsp, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonAvrcpModule::ListPlayerAppValueRspRsp( + const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, BluetoothAvrcpResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothAvrcpResultHandler::ListPlayerAppValueRsp, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonAvrcpModule::GetPlayerAppValueRspRsp( + const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, BluetoothAvrcpResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothAvrcpResultHandler::GetPlayerAppValueRsp, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonAvrcpModule::GetPlayerAppAttrTextRspRsp( + const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, BluetoothAvrcpResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothAvrcpResultHandler::GetPlayerAppAttrTextRsp, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonAvrcpModule::GetPlayerAppValueTextRspRsp( + const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, BluetoothAvrcpResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothAvrcpResultHandler::GetPlayerAppValueTextRsp, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonAvrcpModule::GetElementAttrRspRsp( + const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, BluetoothAvrcpResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothAvrcpResultHandler::GetElementAttrRsp, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonAvrcpModule::SetPlayerAppValueRspRsp( + const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, BluetoothAvrcpResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothAvrcpResultHandler::SetPlayerAppValueRsp, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonAvrcpModule::RegisterNotificationRspRsp( + const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, BluetoothAvrcpResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothAvrcpResultHandler::RegisterNotificationRsp, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonAvrcpModule::SetVolumeRsp( + const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, BluetoothAvrcpResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothAvrcpResultHandler::SetVolume, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonAvrcpModule::HandleRsp( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU, + void* aUserData) +{ + static void (BluetoothDaemonAvrcpModule::* const HandleRsp[])( + const BluetoothDaemonPDUHeader&, + BluetoothDaemonPDU&, + BluetoothAvrcpResultHandler*) = { + INIT_ARRAY_AT(OPCODE_ERROR, + &BluetoothDaemonAvrcpModule::ErrorRsp), + INIT_ARRAY_AT(OPCODE_GET_PLAY_STATUS_RSP, + &BluetoothDaemonAvrcpModule::GetPlayStatusRspRsp), + INIT_ARRAY_AT(OPCODE_LIST_PLAYER_APP_ATTR_RSP, + &BluetoothDaemonAvrcpModule::ListPlayerAppAttrRspRsp), + INIT_ARRAY_AT(OPCODE_LIST_PLAYER_APP_VALUE_RSP, + &BluetoothDaemonAvrcpModule::ListPlayerAppValueRspRsp), + INIT_ARRAY_AT(OPCODE_GET_PLAYER_APP_VALUE_RSP, + &BluetoothDaemonAvrcpModule::GetPlayerAppValueRspRsp), + INIT_ARRAY_AT(OPCODE_GET_PLAYER_APP_ATTR_TEXT_RSP, + &BluetoothDaemonAvrcpModule::GetPlayerAppAttrTextRspRsp), + INIT_ARRAY_AT(OPCODE_GET_PLAYER_APP_VALUE_TEXT_RSP, + &BluetoothDaemonAvrcpModule::GetPlayerAppValueTextRspRsp), + INIT_ARRAY_AT(OPCODE_GET_ELEMENT_ATTR_RSP, + &BluetoothDaemonAvrcpModule::GetElementAttrRspRsp), + INIT_ARRAY_AT(OPCODE_SET_PLAYER_APP_VALUE_RSP, + &BluetoothDaemonAvrcpModule::SetPlayerAppValueRspRsp), + INIT_ARRAY_AT(OPCODE_REGISTER_NOTIFICATION_RSP, + &BluetoothDaemonAvrcpModule::RegisterNotificationRspRsp), + INIT_ARRAY_AT(OPCODE_SET_VOLUME, + &BluetoothDaemonAvrcpModule::SetVolumeRsp) + }; + + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread + + if (NS_WARN_IF(!(aHeader.mOpcode < MOZ_ARRAY_LENGTH(HandleRsp))) || + NS_WARN_IF(!HandleRsp[aHeader.mOpcode])) { + return; + } + + nsRefPtr res = + already_AddRefed( + static_cast(aUserData)); + + if (!res) { + return; // Return early if no result handler has been set for response + } + + (this->*(HandleRsp[aHeader.mOpcode]))(aHeader, aPDU, res); +} + +// Notifications +// + +// Returns the current notification handler to a notification runnable +class BluetoothDaemonAvrcpModule::NotificationHandlerWrapper MOZ_FINAL +{ +public: + typedef BluetoothAvrcpNotificationHandler ObjectType; + + static ObjectType* GetInstance() + { + MOZ_ASSERT(NS_IsMainThread()); + + return sNotificationHandler; + } +}; + +// Init operator class for RemoteFeatureNotification +class BluetoothDaemonAvrcpModule::RemoteFeatureInitOp MOZ_FINAL + : private PDUInitOp +{ +public: + RemoteFeatureInitOp(BluetoothDaemonPDU& aPDU) + : PDUInitOp(aPDU) + { } + + nsresult + operator () (nsString& aArg1, unsigned long& aArg2) const + { + BluetoothDaemonPDU& pdu = GetPDU(); + + /* Read address */ + nsresult rv = UnpackPDU( + pdu, UnpackConversion(aArg1)); + if (NS_FAILED(rv)) { + return rv; + } + + /* Read feature */ + rv = UnpackPDU( + pdu, + UnpackConversion(aArg2)); + if (NS_FAILED(rv)) { + return rv; + } + WarnAboutTrailingData(); + return NS_OK; + } +}; + +void +BluetoothDaemonAvrcpModule::RemoteFeatureNtf( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU) +{ + RemoteFeatureNotification::Dispatch( + &BluetoothAvrcpNotificationHandler::RemoteFeatureNotification, + RemoteFeatureInitOp(aPDU)); +} + +void +BluetoothDaemonAvrcpModule::GetPlayStatusNtf( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU) +{ + GetPlayStatusNotification::Dispatch( + &BluetoothAvrcpNotificationHandler::GetPlayStatusNotification, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonAvrcpModule::ListPlayerAppAttrNtf( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU) +{ + ListPlayerAppAttrNotification::Dispatch( + &BluetoothAvrcpNotificationHandler::ListPlayerAppAttrNotification, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonAvrcpModule::ListPlayerAppValuesNtf( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU) +{ + ListPlayerAppValuesNotification::Dispatch( + &BluetoothAvrcpNotificationHandler::ListPlayerAppValuesNotification, + UnpackPDUInitOp(aPDU)); +} + +// Init operator class for GetPlayerAppValueNotification +class BluetoothDaemonAvrcpModule::GetPlayerAppValueInitOp MOZ_FINAL + : private PDUInitOp +{ +public: + GetPlayerAppValueInitOp(BluetoothDaemonPDU& aPDU) + : PDUInitOp(aPDU) + { } + + nsresult + operator () (uint8_t& aArg1, + nsAutoArrayPtr& aArg2) const + { + BluetoothDaemonPDU& pdu = GetPDU(); + + /* Read number of attributes */ + nsresult rv = UnpackPDU(pdu, aArg1); + if (NS_FAILED(rv)) { + return rv; + } + + /* Read attributes */ + rv = UnpackPDU( + pdu, UnpackArray(aArg2, aArg1)); + if (NS_FAILED(rv)) { + return rv; + } + WarnAboutTrailingData(); + return NS_OK; + } +}; + +void +BluetoothDaemonAvrcpModule::GetPlayerAppValueNtf( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU) +{ + GetPlayerAppValueNotification::Dispatch( + &BluetoothAvrcpNotificationHandler::GetPlayerAppValueNotification, + GetPlayerAppValueInitOp(aPDU)); +} + +// Init operator class for GetPlayerAppAttrsTextNotification +class BluetoothDaemonAvrcpModule::GetPlayerAppAttrsTextInitOp MOZ_FINAL + : private PDUInitOp +{ +public: + GetPlayerAppAttrsTextInitOp(BluetoothDaemonPDU& aPDU) + : PDUInitOp(aPDU) + { } + + nsresult + operator () (uint8_t& aArg1, + nsAutoArrayPtr& aArg2) const + { + BluetoothDaemonPDU& pdu = GetPDU(); + + /* Read number of attributes */ + nsresult rv = UnpackPDU(pdu, aArg1); + if (NS_FAILED(rv)) { + return rv; + } + + /* Read attributes */ + rv = UnpackPDU( + pdu, UnpackArray(aArg2, aArg1)); + if (NS_FAILED(rv)) { + return rv; + } + WarnAboutTrailingData(); + return NS_OK; + } +}; + +void +BluetoothDaemonAvrcpModule::GetPlayerAppAttrsTextNtf( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU) +{ + GetPlayerAppAttrsTextNotification::Dispatch( + &BluetoothAvrcpNotificationHandler::GetPlayerAppAttrsTextNotification, + GetPlayerAppAttrsTextInitOp(aPDU)); +} + +// Init operator class for GetPlayerAppValuesTextNotification +class BluetoothDaemonAvrcpModule::GetPlayerAppValuesTextInitOp MOZ_FINAL + : private PDUInitOp +{ +public: + GetPlayerAppValuesTextInitOp(BluetoothDaemonPDU& aPDU) + : PDUInitOp(aPDU) + { } + + nsresult + operator () (uint8_t& aArg1, uint8_t& aArg2, + nsAutoArrayPtr& aArg3) const + { + BluetoothDaemonPDU& pdu = GetPDU(); + + /* Read attribute */ + nsresult rv = UnpackPDU(pdu, aArg1); + if (NS_FAILED(rv)) { + return rv; + } + + /* Read number of values */ + rv = UnpackPDU(pdu, aArg2); + if (NS_FAILED(rv)) { + return rv; + } + + /* Read values */ + rv = UnpackPDU(pdu, UnpackArray(aArg3, aArg2)); + if (NS_FAILED(rv)) { + return rv; + } + WarnAboutTrailingData(); + return NS_OK; + } +}; + +void +BluetoothDaemonAvrcpModule::GetPlayerAppValuesTextNtf( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU) +{ + GetPlayerAppValuesTextNotification::Dispatch( + &BluetoothAvrcpNotificationHandler::GetPlayerAppValuesTextNotification, + GetPlayerAppValuesTextInitOp(aPDU)); +} + +void +BluetoothDaemonAvrcpModule::SetPlayerAppValueNtf( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU) +{ + SetPlayerAppValueNotification::Dispatch( + &BluetoothAvrcpNotificationHandler::SetPlayerAppValueNotification, + UnpackPDUInitOp(aPDU)); +} + +// Init operator class for GetElementAttrNotification +class BluetoothDaemonAvrcpModule::GetElementAttrInitOp MOZ_FINAL + : private PDUInitOp +{ +public: + GetElementAttrInitOp(BluetoothDaemonPDU& aPDU) + : PDUInitOp(aPDU) + { } + + nsresult + operator () (uint8_t& aArg1, + nsAutoArrayPtr& aArg2) const + { + BluetoothDaemonPDU& pdu = GetPDU(); + + /* Read number of attributes */ + nsresult rv = UnpackPDU(pdu, aArg1); + if (NS_FAILED(rv)) { + return rv; + } + + /* Read attributes */ + rv = UnpackPDU( + pdu, UnpackArray(aArg2, aArg1)); + if (NS_FAILED(rv)) { + return rv; + } + WarnAboutTrailingData(); + return NS_OK; + } +}; + +void +BluetoothDaemonAvrcpModule::GetElementAttrNtf( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU) +{ + GetElementAttrNotification::Dispatch( + &BluetoothAvrcpNotificationHandler::GetElementAttrNotification, + GetElementAttrInitOp(aPDU)); +} + +void +BluetoothDaemonAvrcpModule::RegisterNotificationNtf( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU) +{ + RegisterNotificationNotification::Dispatch( + &BluetoothAvrcpNotificationHandler::RegisterNotificationNotification, + UnpackPDUInitOp(aPDU)); +} + +#if ANDROID_VERSION >= 19 +void +BluetoothDaemonAvrcpModule::VolumeChangeNtf( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU) +{ + VolumeChangeNotification::Dispatch( + &BluetoothAvrcpNotificationHandler::VolumeChangeNotification, + UnpackPDUInitOp(aPDU)); +} + +// Init operator class for PassthroughCmdNotification +class BluetoothDaemonAvrcpModule::PassthroughCmdInitOp MOZ_FINAL + : private PDUInitOp +{ +public: + PassthroughCmdInitOp(BluetoothDaemonPDU& aPDU) + : PDUInitOp(aPDU) + { } + + nsresult + operator () (int& aArg1, int& aArg2) const + { + BluetoothDaemonPDU& pdu = GetPDU(); + + nsresult rv = UnpackPDU(pdu, UnpackConversion(aArg1)); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, UnpackConversion(aArg2)); + if (NS_FAILED(rv)) { + return rv; + } + WarnAboutTrailingData(); + return NS_OK; + } +}; + +void +BluetoothDaemonAvrcpModule::PassthroughCmdNtf( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU) +{ + PassthroughCmdNotification::Dispatch( + &BluetoothAvrcpNotificationHandler::PassthroughCmdNotification, + PassthroughCmdInitOp(aPDU)); +} +#endif + +void +BluetoothDaemonAvrcpModule::HandleNtf( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU, + void* aUserData) +{ + static void (BluetoothDaemonAvrcpModule::* const HandleNtf[])( + const BluetoothDaemonPDUHeader&, BluetoothDaemonPDU&) = { +#if ANDROID_VERSION >= 19 + INIT_ARRAY_AT(0, &BluetoothDaemonAvrcpModule::RemoteFeatureNtf), + INIT_ARRAY_AT(1, &BluetoothDaemonAvrcpModule::GetPlayStatusNtf), + INIT_ARRAY_AT(2, &BluetoothDaemonAvrcpModule::ListPlayerAppAttrNtf), + INIT_ARRAY_AT(3, &BluetoothDaemonAvrcpModule::ListPlayerAppValuesNtf), + INIT_ARRAY_AT(4, &BluetoothDaemonAvrcpModule::GetPlayerAppValueNtf), + INIT_ARRAY_AT(5, &BluetoothDaemonAvrcpModule::GetPlayerAppAttrsTextNtf), + INIT_ARRAY_AT(6, &BluetoothDaemonAvrcpModule::GetPlayerAppValuesTextNtf), + INIT_ARRAY_AT(7, &BluetoothDaemonAvrcpModule::SetPlayerAppValueNtf), + INIT_ARRAY_AT(8, &BluetoothDaemonAvrcpModule::GetElementAttrNtf), + INIT_ARRAY_AT(9, &BluetoothDaemonAvrcpModule::RegisterNotificationNtf), + INIT_ARRAY_AT(10, &BluetoothDaemonAvrcpModule::VolumeChangeNtf), + INIT_ARRAY_AT(11, &BluetoothDaemonAvrcpModule::PassthroughCmdNtf) +#else + INIT_ARRAY_AT(0, &BluetoothDaemonAvrcpModule::GetPlayStatusNtf), + INIT_ARRAY_AT(1, &BluetoothDaemonAvrcpModule::ListPlayerAppAttrNtf), + INIT_ARRAY_AT(2, &BluetoothDaemonAvrcpModule::ListPlayerAppValuesNtf), + INIT_ARRAY_AT(3, &BluetoothDaemonAvrcpModule::GetPlayerAppValueNtf), + INIT_ARRAY_AT(4, &BluetoothDaemonAvrcpModule::GetPlayerAppAttrsTextNtf), + INIT_ARRAY_AT(5, &BluetoothDaemonAvrcpModule::GetPlayerAppValuesTextNtf), + INIT_ARRAY_AT(6, &BluetoothDaemonAvrcpModule::SetPlayerAppValueNtf), + INIT_ARRAY_AT(7, &BluetoothDaemonAvrcpModule::GetElementAttrNtf), + INIT_ARRAY_AT(8, &BluetoothDaemonAvrcpModule::RegisterNotificationNtf) +#endif + }; + + MOZ_ASSERT(!NS_IsMainThread()); + + uint8_t index = aHeader.mOpcode - 0x81; + + if (NS_WARN_IF(!(index < MOZ_ARRAY_LENGTH(HandleNtf))) || + NS_WARN_IF(!HandleNtf[index])) { + return; + } + + (this->*(HandleNtf[index]))(aHeader, aPDU); +} + +// +// AVRCP interface +// + +BluetoothDaemonAvrcpInterface::BluetoothDaemonAvrcpInterface( + BluetoothDaemonAvrcpModule* aModule) + : mModule(aModule) +{ } + +BluetoothDaemonAvrcpInterface::~BluetoothDaemonAvrcpInterface() +{ } + +class BluetoothDaemonAvrcpInterface::InitResultHandler MOZ_FINAL + : public BluetoothSetupResultHandler +{ +public: + InitResultHandler(BluetoothAvrcpResultHandler* aRes) + : mRes(aRes) + { + MOZ_ASSERT(mRes); + } + + void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE + { + MOZ_ASSERT(NS_IsMainThread()); + + mRes->OnError(aStatus); + } + + void RegisterModule() MOZ_OVERRIDE + { + MOZ_ASSERT(NS_IsMainThread()); + + mRes->Init(); + } + +private: + nsRefPtr mRes; +}; + +void +BluetoothDaemonAvrcpInterface::Init( + BluetoothAvrcpNotificationHandler* aNotificationHandler, + BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + // Set notification handler _before_ registering the module. It could + // happen that we receive notifications, before the result handler runs. + mModule->SetNotificationHandler(aNotificationHandler); + + InitResultHandler* res; + + if (aRes) { + res = new InitResultHandler(aRes); + } else { + // We don't need a result handler if the caller is not interested. + res = nullptr; + } + + nsresult rv = mModule->RegisterModule( + BluetoothDaemonAvrcpModule::SERVICE_ID, 0x00, res); + + if (NS_FAILED(rv) && aRes) { + DispatchError(aRes, STATUS_FAIL); + } +} + +class BluetoothDaemonAvrcpInterface::CleanupResultHandler MOZ_FINAL + : public BluetoothSetupResultHandler +{ +public: + CleanupResultHandler(BluetoothDaemonAvrcpModule* aModule, + BluetoothAvrcpResultHandler* aRes) + : mModule(aModule) + , mRes(aRes) + { + MOZ_ASSERT(mModule); + } + + void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE + { + MOZ_ASSERT(NS_IsMainThread()); + + if (mRes) { + mRes->OnError(aStatus); + } + } + + void UnregisterModule() MOZ_OVERRIDE + { + MOZ_ASSERT(NS_IsMainThread()); + + // Clear notification handler _after_ module has been + // unregistered. While unregistering the module, we might + // still receive notifications. + mModule->SetNotificationHandler(nullptr); + + if (mRes) { + mRes->Cleanup(); + } + } + +private: + BluetoothDaemonAvrcpModule* mModule; + nsRefPtr mRes; +}; + +void +BluetoothDaemonAvrcpInterface::Cleanup( + BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + mModule->UnregisterModule(BluetoothDaemonAvrcpModule::SERVICE_ID, + new CleanupResultHandler(mModule, aRes)); +} + +void +BluetoothDaemonAvrcpInterface::GetPlayStatusRsp( + ControlPlayStatus aPlayStatus, uint32_t aSongLen, uint32_t aSongPos, + BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + mModule->GetPlayStatusRspCmd(aPlayStatus, aSongLen, aSongPos, aRes); +} + +void +BluetoothDaemonAvrcpInterface::ListPlayerAppAttrRsp( + int aNumAttr, const BluetoothAvrcpPlayerAttribute* aPAttrs, + BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + mModule->ListPlayerAppAttrRspCmd(aNumAttr, aPAttrs, aRes); +} + +void +BluetoothDaemonAvrcpInterface::ListPlayerAppValueRsp( + int aNumVal, uint8_t* aPVals, BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + mModule->ListPlayerAppValueRspCmd(aNumVal, aPVals, aRes); +} + +void +BluetoothDaemonAvrcpInterface::GetPlayerAppValueRsp( + uint8_t aNumAttrs, const uint8_t* aIds, const uint8_t* aValues, + BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + mModule->GetPlayerAppValueRspCmd(aNumAttrs, aIds, aValues, aRes); +} + +void +BluetoothDaemonAvrcpInterface::GetPlayerAppAttrTextRsp( + int aNumAttr, const uint8_t* aIds, const char** aTexts, + BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + mModule->GetPlayerAppAttrTextRspCmd(aNumAttr, aIds, aTexts, aRes); +} + +void +BluetoothDaemonAvrcpInterface::GetPlayerAppValueTextRsp( + int aNumVal, const uint8_t* aIds, const char** aTexts, + BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + mModule->GetPlayerAppValueTextRspCmd(aNumVal, aIds, aTexts, aRes); +} + +void +BluetoothDaemonAvrcpInterface::GetElementAttrRsp( + uint8_t aNumAttr, const BluetoothAvrcpElementAttribute* aAttr, + BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + mModule->GetElementAttrRspCmd(aNumAttr, aAttr, aRes); +} + +void +BluetoothDaemonAvrcpInterface::SetPlayerAppValueRsp( + BluetoothAvrcpStatus aRspStatus, BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + mModule->SetPlayerAppValueRspCmd(aRspStatus, aRes); +} + +void +BluetoothDaemonAvrcpInterface::RegisterNotificationRsp( + BluetoothAvrcpEvent aEvent, + BluetoothAvrcpNotification aType, + const BluetoothAvrcpNotificationParam& aParam, + BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + mModule->RegisterNotificationRspCmd(aEvent, aType, aParam, aRes); +} + +void +BluetoothDaemonAvrcpInterface::SetVolume( + uint8_t aVolume, BluetoothAvrcpResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + mModule->SetVolumeCmd(aVolume, aRes); +} + +void +BluetoothDaemonAvrcpInterface::DispatchError( + BluetoothAvrcpResultHandler* aRes, BluetoothStatus aStatus) +{ + BluetoothResultRunnable1::Dispatch( + aRes, &BluetoothAvrcpResultHandler::OnError, + ConstantInitOp1(aStatus)); +} + +END_BLUETOOTH_NAMESPACE diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonAvrcpInterface.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonAvrcpInterface.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonAvrcpInterface.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonAvrcpInterface.h 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,352 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_bluetooth_bluetoothdaemonavrcpinterface_h +#define mozilla_dom_bluetooth_bluetoothdaemonavrcpinterface_h + +#include "BluetoothDaemonHelpers.h" +#include "BluetoothInterface.h" +#include "BluetoothInterfaceHelpers.h" + +BEGIN_BLUETOOTH_NAMESPACE + +class BluetoothSetupResultHandler; + +class BluetoothDaemonAvrcpModule +{ +public: + enum { + SERVICE_ID = 0x08 + }; + + enum { + OPCODE_ERROR = 0x00, + OPCODE_GET_PLAY_STATUS_RSP = 0x01, + OPCODE_LIST_PLAYER_APP_ATTR_RSP = 0x02, + OPCODE_LIST_PLAYER_APP_VALUE_RSP = 0x03, + OPCODE_GET_PLAYER_APP_VALUE_RSP = 0x04, + OPCODE_GET_PLAYER_APP_ATTR_TEXT_RSP = 0x05, + OPCODE_GET_PLAYER_APP_VALUE_TEXT_RSP = 0x06, + OPCODE_GET_ELEMENT_ATTR_RSP = 0x07, + OPCODE_SET_PLAYER_APP_VALUE_RSP = 0x08, + OPCODE_REGISTER_NOTIFICATION_RSP = 0x09, + OPCODE_SET_VOLUME = 0x0a, +#if ANDROID_VERSION >= 19 + OPCODE_REMOTE_FEATURES_NTF = 0x81, + OPCODE_GET_PLAY_STATUS_NTF = 0x82, + OPCODE_LIST_PLAYER_APP_ATTR_NTF = 0x83, + OPCODE_LIST_PLAYER_APP_VALUES_NTF = 0x84, + OPCODE_GET_PLAYER_APP_VALUE_NTF = 0x85, + OPCODE_GET_PLAYER_APP_ATTRS_TEXT_NTF = 0x86, + OPCODE_GET_PLAYER_APP_VALUES_TEXT_NTF = 0x87, + OPCODE_SET_PLAYER_APP_VALUE_NTF = 0x88, + OPCODE_GET_ELEMENT_ATTR_NTF = 0x89, + OPCODE_REGISTER_NOTIFICATION_NTF = 0x8a, + OPCODE_VOLUME_CHANGE_NTF = 0x8b, + OPCODE_PASSTHROUGH_CMD_NTF = 0x8c +#else /* defined by BlueZ 5.14 */ + OPCODE_GET_PLAY_STATUS_NTF = 0x81, + OPCODE_LIST_PLAYER_APP_ATTR_NTF = 0x82, + OPCODE_LIST_PLAYER_APP_VALUES_NTF = 0x83, + OPCODE_GET_PLAYER_APP_VALUE_NTF = 0x84, + OPCODE_GET_PLAYER_APP_ATTRS_TEXT_NTF = 0x85, + OPCODE_GET_PLAYER_APP_VALUES_TEXT_NTF = 0x86, + OPCODE_SET_PLAYER_APP_VALUE_NTF = 0x87, + OPCODE_GET_ELEMENT_ATTR_NTF = 0x88, + OPCODE_REGISTER_NOTIFICATION_NTF = 0x89 +#endif + }; + + virtual nsresult Send(BluetoothDaemonPDU* aPDU, void* aUserData) = 0; + + virtual nsresult RegisterModule(uint8_t aId, uint8_t aMode, + BluetoothSetupResultHandler* aRes) = 0; + + virtual nsresult UnregisterModule(uint8_t aId, + BluetoothSetupResultHandler* aRes) = 0; + + void SetNotificationHandler( + BluetoothAvrcpNotificationHandler* aNotificationHandler); + + // + // Commands + // + + nsresult GetPlayStatusRspCmd( + ControlPlayStatus aPlayStatus, uint32_t aSongLen, uint32_t aSongPos, + BluetoothAvrcpResultHandler* aRes); + + nsresult ListPlayerAppAttrRspCmd( + int aNumAttr, const BluetoothAvrcpPlayerAttribute* aPAttrs, + BluetoothAvrcpResultHandler* aRes); + + nsresult ListPlayerAppValueRspCmd( + int aNumVal, uint8_t* aPVals, BluetoothAvrcpResultHandler* aRes); + + nsresult GetPlayerAppValueRspCmd( + uint8_t aNumAttrs, const uint8_t* aIds, const uint8_t* aValues, + BluetoothAvrcpResultHandler* aRes); + + nsresult GetPlayerAppAttrTextRspCmd( + int aNumAttr, const uint8_t* aIds, const char** aTexts, + BluetoothAvrcpResultHandler* aRes); + + nsresult GetPlayerAppValueTextRspCmd( + int aNumVal, const uint8_t* aIds, const char** aTexts, + BluetoothAvrcpResultHandler* aRes); + + nsresult GetElementAttrRspCmd( + uint8_t aNumAttr, const BluetoothAvrcpElementAttribute* aAttr, + BluetoothAvrcpResultHandler* aRes); + + nsresult SetPlayerAppValueRspCmd( + BluetoothAvrcpStatus aRspStatus, BluetoothAvrcpResultHandler* aRes); + + nsresult RegisterNotificationRspCmd( + BluetoothAvrcpEvent aEvent, BluetoothAvrcpNotification aType, + const BluetoothAvrcpNotificationParam& aParam, + BluetoothAvrcpResultHandler* aRes); + + nsresult SetVolumeCmd(uint8_t aVolume, BluetoothAvrcpResultHandler* aRes); + +protected: + nsresult Send(BluetoothDaemonPDU* aPDU, + BluetoothAvrcpResultHandler* aRes); + + void HandleSvc(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, void* aUserData); + + // + // Responses + // + + typedef BluetoothResultRunnable0 + ResultRunnable; + + typedef BluetoothResultRunnable1 + ErrorRunnable; + + void ErrorRsp(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + BluetoothAvrcpResultHandler* aRes); + + void GetPlayStatusRspRsp(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + BluetoothAvrcpResultHandler* aRes); + + void ListPlayerAppAttrRspRsp(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + BluetoothAvrcpResultHandler* aRes); + + void ListPlayerAppValueRspRsp(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + BluetoothAvrcpResultHandler* aRes); + + void GetPlayerAppValueRspRsp(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + BluetoothAvrcpResultHandler* aRes); + + void GetPlayerAppAttrTextRspRsp(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + BluetoothAvrcpResultHandler* aRes); + + void GetPlayerAppValueTextRspRsp(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + BluetoothAvrcpResultHandler* aRes); + + void GetElementAttrRspRsp(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + BluetoothAvrcpResultHandler* aRes); + + void SetPlayerAppValueRspRsp(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + BluetoothAvrcpResultHandler* aRes); + + void RegisterNotificationRspRsp(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + BluetoothAvrcpResultHandler* aRes); + + void SetVolumeRsp(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + BluetoothAvrcpResultHandler* aRes); + + void HandleRsp(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + void* aUserData); + + // + // Notifications + // + + class NotificationHandlerWrapper; + + typedef BluetoothNotificationRunnable2 + RemoteFeatureNotification; + + typedef BluetoothNotificationRunnable0 + GetPlayStatusNotification; + + typedef BluetoothNotificationRunnable0 + ListPlayerAppAttrNotification; + + typedef BluetoothNotificationRunnable1 + ListPlayerAppValuesNotification; + + typedef BluetoothNotificationRunnable2, + uint8_t, const BluetoothAvrcpPlayerAttribute*> + GetPlayerAppValueNotification; + + typedef BluetoothNotificationRunnable2, + uint8_t, const BluetoothAvrcpPlayerAttribute*> + GetPlayerAppAttrsTextNotification; + + typedef BluetoothNotificationRunnable3, uint8_t, + uint8_t, const uint8_t*> + GetPlayerAppValuesTextNotification; + + typedef BluetoothNotificationRunnable1 + SetPlayerAppValueNotification; + + typedef BluetoothNotificationRunnable2, + uint8_t, const BluetoothAvrcpMediaAttribute*> + GetElementAttrNotification; + + typedef BluetoothNotificationRunnable2 + RegisterNotificationNotification; + + typedef BluetoothNotificationRunnable2 + VolumeChangeNotification; + + typedef BluetoothNotificationRunnable2 + PassthroughCmdNotification; + + class GetElementAttrInitOp; + class GetPlayerAppAttrsTextInitOp; + class GetPlayerAppValueInitOp; + class GetPlayerAppValuesTextInitOp; + class PassthroughCmdInitOp; + class RemoteFeatureInitOp; + + void RemoteFeatureNtf(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU); + + void GetPlayStatusNtf(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU); + + void ListPlayerAppAttrNtf(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU); + + void ListPlayerAppValuesNtf(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU); + + void GetPlayerAppValueNtf(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU); + + void GetPlayerAppAttrsTextNtf(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU); + + void GetPlayerAppValuesTextNtf(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU); + + void SetPlayerAppValueNtf(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU); + + void GetElementAttrNtf(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU); + + void RegisterNotificationNtf(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU); + + void VolumeChangeNtf(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU); + + void PassthroughCmdNtf(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU); + + void HandleNtf(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, + void* aUserData); + + static BluetoothAvrcpNotificationHandler* sNotificationHandler; +}; + +class BluetoothDaemonAvrcpInterface MOZ_FINAL + : public BluetoothAvrcpInterface +{ + class CleanupResultHandler; + class InitResultHandler; + +public: + BluetoothDaemonAvrcpInterface(BluetoothDaemonAvrcpModule* aModule); + ~BluetoothDaemonAvrcpInterface(); + + void Init(BluetoothAvrcpNotificationHandler* aNotificationHandler, + BluetoothAvrcpResultHandler* aRes) MOZ_OVERRIDE; + + void Cleanup(BluetoothAvrcpResultHandler* aRes) MOZ_OVERRIDE; + + void GetPlayStatusRsp(ControlPlayStatus aPlayStatus, + uint32_t aSongLen, uint32_t aSongPos, + BluetoothAvrcpResultHandler* aRes) MOZ_OVERRIDE; + + void ListPlayerAppAttrRsp(int aNumAttr, + const BluetoothAvrcpPlayerAttribute* aPAttrs, + BluetoothAvrcpResultHandler* aRes) MOZ_OVERRIDE; + + void ListPlayerAppValueRsp(int aNumVal, uint8_t* aPVals, + BluetoothAvrcpResultHandler* aRes) MOZ_OVERRIDE; + + void GetPlayerAppValueRsp(uint8_t aNumAttrs, const uint8_t* aIds, + const uint8_t* aValues, + BluetoothAvrcpResultHandler* aRes) MOZ_OVERRIDE; + + void GetPlayerAppAttrTextRsp(int aNumAttr, const uint8_t* aIds, + const char** aTexts, + BluetoothAvrcpResultHandler* aRes) MOZ_OVERRIDE; + + void GetPlayerAppValueTextRsp(int aNumVal, const uint8_t* aIds, + const char** aTexts, + BluetoothAvrcpResultHandler* aRes) MOZ_OVERRIDE; + + void GetElementAttrRsp(uint8_t aNumAttr, + const BluetoothAvrcpElementAttribute* aAttr, + BluetoothAvrcpResultHandler* aRes) MOZ_OVERRIDE; + + void SetPlayerAppValueRsp(BluetoothAvrcpStatus aRspStatus, + BluetoothAvrcpResultHandler* aRes) MOZ_OVERRIDE; + + void RegisterNotificationRsp(BluetoothAvrcpEvent aEvent, + BluetoothAvrcpNotification aType, + const BluetoothAvrcpNotificationParam& aParam, + BluetoothAvrcpResultHandler* aRes) MOZ_OVERRIDE; + + void SetVolume(uint8_t aVolume, + BluetoothAvrcpResultHandler* aRes) MOZ_OVERRIDE; + +private: + void DispatchError(BluetoothAvrcpResultHandler* aRes, + BluetoothStatus aStatus); + + BluetoothDaemonAvrcpModule* mModule; +}; + +END_BLUETOOTH_NAMESPACE + +#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonHelpers.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonHelpers.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonHelpers.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonHelpers.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -98,6 +98,44 @@ } nsresult +Convert(uint8_t aIn, unsigned long& aOut) +{ + aOut = static_cast(aIn); + return NS_OK; +} + +nsresult +Convert(uint8_t aIn, BluetoothA2dpAudioState& aOut) +{ + static const BluetoothA2dpAudioState sAudioState[] = { + CONVERT(0x00, A2DP_AUDIO_STATE_REMOTE_SUSPEND), + CONVERT(0x01, A2DP_AUDIO_STATE_STOPPED), + CONVERT(0x02, A2DP_AUDIO_STATE_STARTED) + }; + if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sAudioState))) { + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = sAudioState[aIn]; + return NS_OK; +} + +nsresult +Convert(uint8_t aIn, BluetoothA2dpConnectionState& aOut) +{ + static const BluetoothA2dpConnectionState sConnectionState[] = { + CONVERT(0x00, A2DP_CONNECTION_STATE_DISCONNECTED), + CONVERT(0x01, A2DP_CONNECTION_STATE_CONNECTING), + CONVERT(0x02, A2DP_CONNECTION_STATE_CONNECTED), + CONVERT(0x03, A2DP_CONNECTION_STATE_DISCONNECTING) + }; + if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sConnectionState))) { + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = sConnectionState[aIn]; + return NS_OK; +} + +nsresult Convert(uint8_t aIn, BluetoothAclState& aOut) { static const BluetoothAclState sAclState[] = { @@ -112,6 +150,93 @@ } nsresult +Convert(uint8_t aIn, BluetoothAvrcpEvent& aOut) +{ + static const BluetoothAvrcpEvent sAvrcpEvent[] = { + CONVERT(0x00, static_cast(0)), + CONVERT(0x01, AVRCP_EVENT_PLAY_STATUS_CHANGED), + CONVERT(0x02, AVRCP_EVENT_TRACK_CHANGE), + CONVERT(0x03, AVRCP_EVENT_TRACK_REACHED_END), + CONVERT(0x04, AVRCP_EVENT_TRACK_REACHED_START), + CONVERT(0x05, AVRCP_EVENT_PLAY_POS_CHANGED), + CONVERT(0x06, static_cast(0)), + CONVERT(0x07, static_cast(0)), + CONVERT(0x08, AVRCP_EVENT_APP_SETTINGS_CHANGED) + }; + if (NS_WARN_IF(!aIn) || + NS_WARN_IF(aIn == 0x06) || + NS_WARN_IF(aIn == 0x07) || + NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sAvrcpEvent))) { + aOut = static_cast(0); // silences compiler warning + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = sAvrcpEvent[aIn]; + return NS_OK; +} + +nsresult +Convert(uint8_t aIn, BluetoothAvrcpMediaAttribute& aOut) +{ + static const BluetoothAvrcpMediaAttribute sAvrcpMediaAttribute[] = { + CONVERT(0x00, static_cast(0)), + CONVERT(0x01, AVRCP_MEDIA_ATTRIBUTE_TITLE), + CONVERT(0x02, AVRCP_MEDIA_ATTRIBUTE_ARTIST), + CONVERT(0x03, AVRCP_MEDIA_ATTRIBUTE_ALBUM), + CONVERT(0x04, AVRCP_MEDIA_ATTRIBUTE_TRACK_NUM), + CONVERT(0x05, AVRCP_MEDIA_ATTRIBUTE_NUM_TRACKS), + CONVERT(0x06, AVRCP_MEDIA_ATTRIBUTE_GENRE), + CONVERT(0x07, AVRCP_MEDIA_ATTRIBUTE_PLAYING_TIME) + }; + if (NS_WARN_IF(!aIn) || + NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sAvrcpMediaAttribute))) { + // silences compiler warning + aOut = static_cast(0); + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = sAvrcpMediaAttribute[aIn]; + return NS_OK; +} + +nsresult +Convert(uint8_t aIn, BluetoothAvrcpPlayerAttribute& aOut) +{ + static const BluetoothAvrcpPlayerAttribute sAvrcpPlayerAttribute[] = { + CONVERT(0x00, static_cast(0)), + CONVERT(0x01, AVRCP_PLAYER_ATTRIBUTE_EQUALIZER), + CONVERT(0x02, AVRCP_PLAYER_ATTRIBUTE_REPEAT), + CONVERT(0x03, AVRCP_PLAYER_ATTRIBUTE_SHUFFLE), + CONVERT(0x04, AVRCP_PLAYER_ATTRIBUTE_SCAN) + }; + if (NS_WARN_IF(!aIn) || + NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sAvrcpPlayerAttribute))) { + // silences compiler warning + aOut = static_cast(0); + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = sAvrcpPlayerAttribute[aIn]; + return NS_OK; +} + +nsresult +Convert(uint8_t aIn, BluetoothAvrcpRemoteFeature& aOut) +{ + static const BluetoothAvrcpRemoteFeature sAvrcpRemoteFeature[] = { + CONVERT(0x00, AVRCP_REMOTE_FEATURE_NONE), + CONVERT(0x01, AVRCP_REMOTE_FEATURE_METADATA), + CONVERT(0x02, AVRCP_REMOTE_FEATURE_ABSOLUTE_VOLUME), + CONVERT(0x03, AVRCP_REMOTE_FEATURE_BROWSE) + }; + if (NS_WARN_IF(!aIn) || + NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sAvrcpRemoteFeature))) { + // silences compiler warning + aOut = static_cast(0); + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = sAvrcpRemoteFeature[aIn]; + return NS_OK; +} + +nsresult Convert(uint8_t aIn, BluetoothBondState& aOut) { static const BluetoothBondState sBondState[] = { @@ -349,6 +474,18 @@ } nsresult +Convert(uint32_t aIn, uint8_t& aOut) +{ + if (NS_WARN_IF(aIn < std::numeric_limits::min()) || + NS_WARN_IF(aIn > std::numeric_limits::max())) { + aOut = 0; // silences compiler warning + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = static_cast(aIn); + return NS_OK; +} + +nsresult Convert(size_t aIn, uint16_t& aOut) { if (NS_WARN_IF(aIn >= (1ul << 16))) { @@ -491,6 +628,50 @@ } nsresult +Convert(BluetoothAvrcpEvent aIn, uint8_t& aOut) +{ + static const uint8_t sValue[] = { + CONVERT(AVRCP_EVENT_PLAY_STATUS_CHANGED, 0x01), + CONVERT(AVRCP_EVENT_TRACK_CHANGE, 0x02), + CONVERT(AVRCP_EVENT_TRACK_REACHED_END, 0x03), + CONVERT(AVRCP_EVENT_TRACK_REACHED_START, 0x04), + CONVERT(AVRCP_EVENT_PLAY_POS_CHANGED, 0x05), + CONVERT(AVRCP_EVENT_APP_SETTINGS_CHANGED, 0x08) + }; + if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sValue))) { + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = sValue[aIn]; + return NS_OK; +} + +nsresult +Convert(BluetoothAvrcpNotification aIn, uint8_t& aOut) +{ + static const bool sValue[] = { + CONVERT(AVRCP_NTF_INTERIM, 0x00), + CONVERT(AVRCP_NTF_CHANGED, 0x01) + }; + if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sValue))) { + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = sValue[aIn]; + return NS_OK; +} + +nsresult +Convert(BluetoothAvrcpRemoteFeature aIn, unsigned long& aOut) +{ + if (NS_WARN_IF(aIn < std::numeric_limits::min()) || + NS_WARN_IF(aIn > std::numeric_limits::max())) { + aOut = 0; // silences compiler warning + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = static_cast(aIn); + return NS_OK; +} + +nsresult Convert(BluetoothHandsfreeAtResponse aIn, uint8_t& aOut) { static const uint8_t sAtResponse[] = { @@ -701,6 +882,30 @@ return NS_OK; } +nsresult +Convert(ControlPlayStatus aIn, uint8_t& aOut) +{ + static const uint8_t sValue[] = { + CONVERT(PLAYSTATUS_STOPPED, 0x00), + CONVERT(PLAYSTATUS_PLAYING, 0x01), + CONVERT(PLAYSTATUS_PAUSED, 0x02), + CONVERT(PLAYSTATUS_FWD_SEEK, 0x03), + CONVERT(PLAYSTATUS_REV_SEEK, 0x04) + }; + if (aIn == PLAYSTATUS_ERROR) { + /* This case is handled separately to not populate + * |sValue| with empty entries. */ + aOut = 0xff; + return NS_OK; + } + if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sValue))) { + aOut = 0; // silences compiler warning + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = sValue[aIn]; + return NS_OK; +} + /* |ConvertArray| is a helper for converting arrays. Pass an * instance of this structure as the first argument to |Convert| * to convert an array. The output type has to support the array @@ -752,6 +957,142 @@ } nsresult +PackPDU(const BluetoothAvrcpAttributeTextPairs& aIn, + BluetoothDaemonPDU& aPDU) +{ + size_t i; + + for (i = 0; i < aIn.mLength; ++i) { + nsresult rv = PackPDU(aIn.mAttr[i], aPDU); + if (NS_FAILED(rv)) { + return rv; + } + + uint8_t len; + const uint8_t* str; + + if (aIn.mText[i]) { + str = reinterpret_cast(aIn.mText[i]); + len = strlen(aIn.mText[i]) + 1; + } else { + /* write \0 character for NULL strings */ + str = reinterpret_cast("\0"); + len = 1; + } + + rv = PackPDU(len, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(PackArray(str, len), aPDU); + if (NS_FAILED(rv)) { + return rv; + } + } + return NS_OK; +} + +nsresult +PackPDU(const BluetoothAvrcpAttributeValuePairs& aIn, + BluetoothDaemonPDU& aPDU) +{ + size_t i; + + for (i = 0; i < aIn.mLength; ++i) { + nsresult rv = PackPDU(aIn.mAttr[i], aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn.mValue[i], aPDU); + if (NS_FAILED(rv)) { + return rv; + } + } + return NS_OK; +} + +nsresult +PackPDU(const BluetoothAvrcpElementAttribute& aIn, BluetoothDaemonPDU& aPDU) +{ + nsresult rv = PackPDU(PackConversion(aIn.mId), aPDU); + if (NS_FAILED(rv)) { + return rv; + } + + const NS_ConvertUTF16toUTF8 cstr(aIn.mValue); + + if (NS_WARN_IF(cstr.Length() == PR_UINT32_MAX)) { + return NS_ERROR_ILLEGAL_VALUE; /* integer overflow detected */ + } + + PRUint32 clen = cstr.Length() + 1; /* include \0 character */ + + rv = PackPDU(PackConversion(clen), aPDU); + if (NS_FAILED(rv)) { + return rv; + } + + return PackPDU( + PackArray(reinterpret_cast(cstr.get()), clen), + aPDU); +} + +nsresult +PackPDU(BluetoothAvrcpEvent aIn, BluetoothDaemonPDU& aPDU) +{ + return PackPDU(PackConversion(aIn), aPDU); +} + +nsresult +PackPDU(const BluetoothAvrcpEventParamPair& aIn, BluetoothDaemonPDU& aPDU) +{ + nsresult rv; + + switch (aIn.mEvent) { + case AVRCP_EVENT_PLAY_STATUS_CHANGED: + rv = PackPDU(aIn.mParam.mPlayStatus, aPDU); + break; + case AVRCP_EVENT_TRACK_CHANGE: + rv = PackPDU(PackArray(aIn.mParam.mTrack, + MOZ_ARRAY_LENGTH(aIn.mParam.mTrack)), + aPDU); + break; + case AVRCP_EVENT_TRACK_REACHED_END: + /* fall through */ + case AVRCP_EVENT_TRACK_REACHED_START: + /* no data to pack */ + rv = NS_OK; + break; + case AVRCP_EVENT_PLAY_POS_CHANGED: + rv = PackPDU(aIn.mParam.mSongPos, aPDU); + break; + case AVRCP_EVENT_APP_SETTINGS_CHANGED: + /* pack number of attribute-value pairs */ + rv = PackPDU(aIn.mParam.mNumAttr, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + /* pack attribute-value pairs */ + rv = PackPDU(BluetoothAvrcpAttributeValuePairs(aIn.mParam.mIds, + aIn.mParam.mValues, + aIn.mParam.mNumAttr), + aPDU); + break; + default: + rv = NS_ERROR_ILLEGAL_VALUE; + break; + } + return rv; +} + +nsresult +PackPDU(BluetoothAvrcpNotification aIn, BluetoothDaemonPDU& aPDU) +{ + return PackPDU( + PackConversion(aIn), aPDU); +} + +nsresult PackPDU(const BluetoothConfigurationParameter& aIn, BluetoothDaemonPDU& aPDU) { return PackPDU(aIn.mType, aIn.mLength, @@ -902,6 +1243,12 @@ return PackPDU(PackConversion(aIn), aPDU); } +nsresult +PackPDU(ControlPlayStatus aIn, BluetoothDaemonPDU& aPDU) +{ + return PackPDU(PackConversion(aIn), aPDU); +} + // // Unpacking // @@ -919,12 +1266,76 @@ } nsresult +UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothA2dpAudioState& aOut) +{ + return UnpackPDU( + aPDU, UnpackConversion(aOut)); +} + +nsresult +UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothA2dpConnectionState& aOut) +{ + return UnpackPDU( + aPDU, UnpackConversion(aOut)); +} + +nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAclState& aOut) { return UnpackPDU(aPDU, UnpackConversion(aOut)); } nsresult +UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAvrcpEvent& aOut) +{ + return UnpackPDU( + aPDU, UnpackConversion(aOut)); +} + +nsresult +UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAvrcpMediaAttribute& aOut) +{ + return UnpackPDU( + aPDU, UnpackConversion(aOut)); +} + +nsresult +UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAvrcpPlayerAttribute& aOut) +{ + return UnpackPDU( + aPDU, UnpackConversion(aOut)); +} + +nsresult +UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAvrcpPlayerSettings& aOut) +{ + /* Read number of attribute-value pairs */ + nsresult rv = UnpackPDU(aPDU, aOut.mNumAttr); + if (NS_FAILED(rv)) { + return rv; + } + /* Read attribute-value pairs */ + for (uint8_t i = 0; i < aOut.mNumAttr; ++i) { + nsresult rv = UnpackPDU(aPDU, aOut.mIds[i]); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(aPDU, aOut.mValues[i]); + if (NS_FAILED(rv)) { + return rv; + } + } + return NS_OK; +} + +nsresult +UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAvrcpRemoteFeature& aOut) +{ + return UnpackPDU( + aPDU, UnpackConversion(aOut)); +} + +nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothBondState& aOut) { return UnpackPDU(aPDU, UnpackConversion(aOut)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonHelpers.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonHelpers.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonHelpers.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonHelpers.h 2015-02-03 14:33:32.000000000 +0000 @@ -30,6 +30,45 @@ uint8_t mAddr[6]; }; +struct BluetoothAvrcpAttributeTextPairs { + BluetoothAvrcpAttributeTextPairs(const uint8_t* aAttr, + const char** aText, + size_t aLength) + : mAttr(aAttr) + , mText(aText) + , mLength(aLength) + { } + + const uint8_t* mAttr; + const char** mText; + size_t mLength; +}; + +struct BluetoothAvrcpAttributeValuePairs { + BluetoothAvrcpAttributeValuePairs(const uint8_t* aAttr, + const uint8_t* aValue, + size_t aLength) + : mAttr(aAttr) + , mValue(aValue) + , mLength(aLength) + { } + + const uint8_t* mAttr; + const uint8_t* mValue; + size_t mLength; +}; + +struct BluetoothAvrcpEventParamPair { + BluetoothAvrcpEventParamPair(BluetoothAvrcpEvent aEvent, + const BluetoothAvrcpNotificationParam& aParam) + : mEvent(aEvent) + , mParam(aParam) + { } + + BluetoothAvrcpEvent mEvent; + const BluetoothAvrcpNotificationParam& mParam; +}; + struct BluetoothConfigurationParameter { uint8_t mType; uint16_t mLength; @@ -99,12 +138,33 @@ Convert(uint8_t aIn, int& aOut); nsresult +Convert(uint8_t aIn, unsigned long& aOut); + +nsresult +Convert(uint8_t aIn, BluetoothA2dpAudioState& aOut); + +nsresult +Convert(uint8_t aIn, BluetoothA2dpConnectionState& aOut); + +nsresult Convert(uint8_t aIn, BluetoothAclState& aOut); nsresult Convert(uint8_t aIn, BluetoothBondState& aOut); nsresult +Convert(uint8_t aIn, BluetoothAvrcpEvent& aOut); + +nsresult +Convert(uint8_t aIn, BluetoothAvrcpMediaAttribute& aOut); + +nsresult +Convert(uint8_t aIn, BluetoothAvrcpPlayerAttribute& aOut); + +nsresult +Convert(uint8_t aIn, BluetoothAvrcpRemoteFeature& aOut); + +nsresult Convert(uint8_t aIn, BluetoothHandsfreeAudioState& aOut); nsresult @@ -141,6 +201,9 @@ Convert(uint32_t aIn, int& aOut); nsresult +Convert(uint32_t aIn, uint8_t& aOut); + +nsresult Convert(size_t aIn, uint16_t& aOut); nsresult @@ -165,6 +228,15 @@ Convert(const BluetoothAddress& aIn, nsAString& aOut); nsresult +Convert(BluetoothAvrcpEvent aIn, uint8_t& aOut); + +nsresult +Convert(BluetoothAvrcpNotification aIn, uint8_t& aOut); + +nsresult +Convert(BluetoothAvrcpRemoteFeature aIn, unsigned long& aOut); + +nsresult Convert(BluetoothHandsfreeAtResponse aIn, uint8_t& aOut); nsresult @@ -206,6 +278,9 @@ nsresult Convert(BluetoothSspVariant aIn, uint8_t& aOut); +nsresult +Convert(ControlPlayStatus aIn, uint8_t& aOut); + // // Packing // @@ -241,6 +316,26 @@ PackPDU(const BluetoothAddress& aIn, BluetoothDaemonPDU& aPDU); nsresult +PackPDU(const BluetoothAvrcpAttributeTextPairs& aIn, + BluetoothDaemonPDU& aPDU); + +nsresult +PackPDU(const BluetoothAvrcpAttributeValuePairs& aIn, + BluetoothDaemonPDU& aPDU); + +nsresult +PackPDU(const BluetoothAvrcpElementAttribute& aIn, BluetoothDaemonPDU& aPDU); + +nsresult +PackPDU(BluetoothAvrcpEvent aIn, BluetoothDaemonPDU& aPDU); + +nsresult +PackPDU(const BluetoothAvrcpEventParamPair& aIn, BluetoothDaemonPDU& aPDU); + +nsresult +PackPDU(BluetoothAvrcpNotification aIn, BluetoothDaemonPDU& aPDU); + +nsresult PackPDU(const BluetoothConfigurationParameter& aIn, BluetoothDaemonPDU& aPDU); nsresult @@ -294,6 +389,9 @@ nsresult PackPDU(BluetoothScanMode aIn, BluetoothDaemonPDU& aPDU); +nsresult +PackPDU(ControlPlayStatus aIn, BluetoothDaemonPDU& aPDU); + /* |PackConversion| is a helper for packing converted values. Pass * an instance of this structure to |PackPDU| to convert a value from * the input type to the output type and and write it to the PDU. @@ -532,6 +630,12 @@ UnpackPDU(BluetoothDaemonPDU& aPDU, char& aOut); nsresult +UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothA2dpAudioState& aOut); + +nsresult +UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothA2dpConnectionState& aOut); + +nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAclState& aOut); inline nsresult @@ -541,6 +645,21 @@ } nsresult +UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAvrcpEvent& aOut); + +nsresult +UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAvrcpMediaAttribute& aOut); + +nsresult +UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAvrcpPlayerAttribute& aOut); + +nsresult +UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAvrcpPlayerSettings& aOut); + +nsresult +UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAvrcpRemoteFeature& aOut); + +nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothBondState& aOut); inline nsresult diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonInterface.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonInterface.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonInterface.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonInterface.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -5,6 +5,8 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "BluetoothDaemonInterface.h" +#include "BluetoothDaemonA2dpInterface.h" +#include "BluetoothDaemonAvrcpInterface.h" #include "BluetoothDaemonHandsfreeInterface.h" #include "BluetoothDaemonHelpers.h" #include "BluetoothDaemonSetupInterface.h" @@ -1452,12 +1454,62 @@ // Protocol handling // +// |BluetoothDaemonProtocol| is the central class for communicating +// with the Bluetooth daemon. It maintains both socket connections +// to the external daemon and implements the complete HAL protocol +// by inheriting from base-class modules. +// +// Each |BluetoothDaemon*Module| class implements an individual +// module of the HAL protocol. Each class contains the abstract +// methods +// +// - |Send|, +// - |RegisterModule|, and +// - |UnregisterModule|. +// +// Module classes use |Send| to send out command PDUs. The socket +// in |BluetoothDaemonProtocol| is required for sending. The abstract +// method hides all these internal details from the modules. +// +// |RegisterModule| is required during module initialization, when +// modules must register themselves at the daemon. The register command +// is not part of the module itself, but contained in the Setup module +// (id of 0x00). The abstract method |RegisterModule| allows modules to +// call into the Setup module for generating the register command. +// +// |UnregisterModule| works like |RegisterModule|, but for cleanups. +// +// |BluetoothDaemonProtocol| also handles PDU receiving. It implements +// the method |Handle| from |BluetoothDaemonPDUConsumer|. The socket +// connections of type |BluetoothDaemonConnection| invoke this method +// to forward received PDUs for processing by higher layers. The +// implementation of |Handle| checks the service id of the PDU and +// forwards it to the correct module class using the module's method +// |HandleSvc|. Further PDU processing is module-dependent. +// +// To summarize the interface between |BluetoothDaemonProtocol| and +// modules; the former implements the abstract methods +// +// - |Send|, +// - |RegisterModule|, and +// - |UnregisterModule|, +// +// which allow modules to send out data. Each module implements the +// method +// +// - |HandleSvc|, +// +// which is called by |BluetoothDaemonProtcol| to hand over received +// PDUs into a module. +// class BluetoothDaemonProtocol MOZ_FINAL : public BluetoothDaemonPDUConsumer , public BluetoothDaemonSetupModule , public BluetoothDaemonCoreModule , public BluetoothDaemonSocketModule , public BluetoothDaemonHandsfreeModule + , public BluetoothDaemonA2dpModule + , public BluetoothDaemonAvrcpModule { public: BluetoothDaemonProtocol(BluetoothDaemonConnection* aConnection); @@ -1491,6 +1543,10 @@ BluetoothDaemonPDU& aPDU, void* aUserData); void HandleHandsfreeSvc(const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU, void* aUserData); + void HandleA2dpSvc(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, void* aUserData); + void HandleAvrcpSvc(const BluetoothDaemonPDUHeader& aHeader, + BluetoothDaemonPDU& aPDU, void* aUserData); BluetoothDaemonConnection* mConnection; nsTArray mUserDataQ; @@ -1560,6 +1616,22 @@ } void +BluetoothDaemonProtocol::HandleA2dpSvc( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU, + void* aUserData) +{ + BluetoothDaemonA2dpModule::HandleSvc(aHeader, aPDU, aUserData); +} + +void +BluetoothDaemonProtocol::HandleAvrcpSvc( + const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU, + void* aUserData) +{ + BluetoothDaemonAvrcpModule::HandleSvc(aHeader, aPDU, aUserData); +} + +void BluetoothDaemonProtocol::Handle(BluetoothDaemonPDU& aPDU) { static void (BluetoothDaemonProtocol::* const HandleSvc[])( @@ -1573,7 +1645,12 @@ INIT_ARRAY_AT(0x03, nullptr), // HID host INIT_ARRAY_AT(0x04, nullptr), // PAN INIT_ARRAY_AT(BluetoothDaemonHandsfreeModule::SERVICE_ID, - &BluetoothDaemonProtocol::HandleHandsfreeSvc) + &BluetoothDaemonProtocol::HandleHandsfreeSvc), + INIT_ARRAY_AT(BluetoothDaemonA2dpModule::SERVICE_ID, + &BluetoothDaemonProtocol::HandleA2dpSvc), + INIT_ARRAY_AT(0x07, nullptr), // Health + INIT_ARRAY_AT(BluetoothDaemonAvrcpModule::SERVICE_ID, + &BluetoothDaemonProtocol::HandleAvrcpSvc) }; BluetoothDaemonPDUHeader header; @@ -2158,13 +2235,25 @@ BluetoothA2dpInterface* BluetoothDaemonInterface::GetBluetoothA2dpInterface() { - return nullptr; + if (mA2dpInterface) { + return mA2dpInterface; + } + + mA2dpInterface = new BluetoothDaemonA2dpInterface(mProtocol); + + return mA2dpInterface; } BluetoothAvrcpInterface* BluetoothDaemonInterface::GetBluetoothAvrcpInterface() { - return nullptr; + if (mAvrcpInterface) { + return mAvrcpInterface; + } + + mAvrcpInterface = new BluetoothDaemonAvrcpInterface(mProtocol); + + return mAvrcpInterface; } BluetoothGattInterface* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonInterface.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonInterface.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonInterface.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothDaemonInterface.h 2015-02-03 14:33:32.000000000 +0000 @@ -12,6 +12,8 @@ BEGIN_BLUETOOTH_NAMESPACE class BluetoothDaemonChannel; +class BluetoothDaemonA2dpInterface; +class BluetoothDaemonAvrcpInterface; class BluetoothDaemonHandsfreeInterface; class BluetoothDaemonProtocol; class BluetoothDaemonSocketInterface; @@ -128,6 +130,8 @@ nsAutoPtr mSocketInterface; nsAutoPtr mHandsfreeInterface; + nsAutoPtr mA2dpInterface; + nsAutoPtr mAvrcpInterface; }; END_BLUETOOTH_NAMESPACE diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -64,6 +64,8 @@ static bool sAdapterDiscoverable(false); static bool sAdapterDiscovering(false); static bool sAdapterEnabled(false); +// InfallibleTArray is an alias for nsTArray. +static InfallibleTArray sAdapterBondedAddressArray; static BluetoothInterface* sBtInterface; static nsTArray > sControllerArray; @@ -422,6 +424,8 @@ "Discoverable", sAdapterDiscoverable); BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(), "Discovering", sAdapterDiscovering); + BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(), + "PairedDevices", sAdapterBondedAddressArray); BT_APPEND_NAMED_VALUE(adaptersProperties.get_ArrayOfBluetoothNamedValue(), "Adapter", properties); @@ -1190,6 +1194,10 @@ sAdapterDiscovering = false; BT_APPEND_NAMED_VALUE(props, "Discovering", false); } + if (!sAdapterBondedAddressArray.IsEmpty()) { + BT_APPEND_NAMED_VALUE(props, "PairedDevices", + InfallibleTArray()); + } BluetoothSignal signal(NS_LITERAL_STRING("PropertyChanged"), NS_LITERAL_STRING(KEY_ADAPTER), props); @@ -1279,13 +1287,15 @@ BT_LOGD("Adapter property: BONDED_DEVICES. Count: %d", p.mStringArray.Length()); - nsTArray pairedDeviceAddresses; + // Whenever reloading paired devices, force refresh + sAdapterBondedAddressArray.Clear(); + for (size_t index = 0; index < p.mStringArray.Length(); index++) { - pairedDeviceAddresses.AppendElement(p.mStringArray[index]); + sAdapterBondedAddressArray.AppendElement(p.mStringArray[index]); } - BT_APPEND_NAMED_VALUE(propertiesArray, "PairedDevices", pairedDeviceAddresses); - + BT_APPEND_NAMED_VALUE(propertiesArray, "PairedDevices", + sAdapterBondedAddressArray); } else if (p.mType == PROPERTY_UNKNOWN) { /* Bug 1065999: working around unknown properties */ } else { @@ -1570,6 +1580,16 @@ bool bonded = (aState == BOND_STATE_BONDED); + // Update bonded address array + nsString remoteBdAddr = nsString(aRemoteBdAddr); + if (bonded) { + if (!sAdapterBondedAddressArray.Contains(remoteBdAddr)) { + sAdapterBondedAddressArray.AppendElement(remoteBdAddr); + } + } else { + sAdapterBondedAddressArray.RemoveElement(remoteBdAddr); + } + // Update attribute BluetoothDevice.paired InfallibleTArray propertiesArray; BT_APPEND_NAMED_VALUE(propertiesArray, "Paired", bonded); @@ -1594,7 +1614,6 @@ DispatchBluetoothReply(sBondingRunnableArray[0], BluetoothValue(true), EmptyString()); sBondingRunnableArray.RemoveElementAt(0); - } else if (!bonded && !sUnbondingRunnableArray.IsEmpty()) { DispatchBluetoothReply(sUnbondingRunnableArray[0], BluetoothValue(true), EmptyString()); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothUtils.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothUtils.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/bluedroid/BluetoothUtils.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/bluedroid/BluetoothUtils.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -116,7 +116,7 @@ aData.get_nsString().Length()); value = STRING_TO_JSVAL(jsData); } else if (aData.type() == BluetoothValue::TArrayOfBluetoothNamedValue) { - JS::Rooted obj(cx, JS_NewPlainObject(cx))); + JS::Rooted obj(cx, JS_NewPlainObject(cx)); if (!obj) { BT_WARNING("Failed to new JSObject for system message!"); return false; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/BluetoothAdapter.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/BluetoothAdapter.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/BluetoothAdapter.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/BluetoothAdapter.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -280,6 +280,13 @@ = value.get_ArrayOfnsString(); for (uint32_t i = 0; i < pairedDeviceAddresses.Length(); i++) { + // Check whether or not the address exists in mDevices. + if (mDevices.Contains(pairedDeviceAddresses[i])) { + // If the paired device exists in mDevices, it would handle + // 'PropertyChanged' signal in BluetoothDevice::Notify(). + continue; + } + InfallibleTArray props; BT_APPEND_NAMED_VALUE(props, "Address", pairedDeviceAddresses[i]); BT_APPEND_NAMED_VALUE(props, "Paired", true); @@ -288,10 +295,8 @@ nsRefPtr pairedDevice = BluetoothDevice::Create(GetOwner(), BluetoothValue(props)); - // Append to adapter's device array if the device hasn't been created - if (!mDevices.Contains(pairedDevice)) { - mDevices.AppendElement(pairedDevice); - } + // Append to adapter's device array + mDevices.AppendElement(pairedDevice); } // Retrieve device properties, result will be handled by device objects. @@ -885,14 +890,27 @@ return; } - // Create paired device with 'address' and 'paired' attributes - nsRefPtr pairedDevice = - BluetoothDevice::Create(GetOwner(), aValue); + const InfallibleTArray& arr = + aValue.get_ArrayOfBluetoothNamedValue(); + + MOZ_ASSERT(arr.Length() == 2 && + arr[0].value().type() == BluetoothValue::TnsString && // Address + arr[1].value().type() == BluetoothValue::Tbool); // Paired + MOZ_ASSERT(!arr[0].value().get_nsString().IsEmpty() && + arr[1].value().get_bool()); + + nsString deviceAddress = arr[0].value().get_nsString(); - size_t index = mDevices.IndexOf(pairedDevice); + nsRefPtr pairedDevice = nullptr; + + // Check whether or not the address exists in mDevices. + size_t index = mDevices.IndexOf(deviceAddress); if (index == mDevices.NoIndex) { + // Create a new device and append it to adapter's device array + pairedDevice = BluetoothDevice::Create(GetOwner(), aValue); mDevices.AppendElement(pairedDevice); } else { + // Use existing device pairedDevice = mDevices[index]; } @@ -912,19 +930,19 @@ return; } - // Create unpaired device with 'address' and 'paired' attributes - nsRefPtr unpairedDevice = - BluetoothDevice::Create(GetOwner(), aValue); + const InfallibleTArray& arr = + aValue.get_ArrayOfBluetoothNamedValue(); - size_t index = mDevices.IndexOf(unpairedDevice); + MOZ_ASSERT(arr.Length() == 2 && + arr[0].value().type() == BluetoothValue::TnsString && // Address + arr[1].value().type() == BluetoothValue::Tbool); // Paired + MOZ_ASSERT(!arr[0].value().get_nsString().IsEmpty() && + !arr[1].value().get_bool()); - nsString deviceAddress; - if (index == mDevices.NoIndex) { - unpairedDevice->GetAddress(deviceAddress); - } else { - mDevices[index]->GetAddress(deviceAddress); - mDevices.RemoveElementAt(index); - } + nsString deviceAddress = arr[0].value().get_nsString(); + + // Remove the device with the same address + mDevices.RemoveElement(deviceAddress); // Notify application of unpaired device BluetoothDeviceEventInit init; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/BluetoothDevice.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/BluetoothDevice.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/BluetoothDevice.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/BluetoothDevice.h 2015-02-03 14:33:32.000000000 +0000 @@ -94,16 +94,6 @@ virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; virtual void DisconnectFromOwner() MOZ_OVERRIDE; - /** - * Override operator== for device comparison - */ - bool operator==(BluetoothDevice& aDevice) const - { - nsString address; - aDevice.GetAddress(address); - return mAddress.Equals(address); - } - private: BluetoothDevice(nsPIDOMWindow* aOwner, const BluetoothValue& aValue); ~BluetoothDevice(); @@ -187,4 +177,51 @@ END_BLUETOOTH_NAMESPACE +/** + * Explicit Specialization of Function Templates + * + * Allows customizing the template code for a given set of template arguments. + * With this function template, nsTArray can handle comparison of + * 'nsRefPtr' properly, including IndexOf() and Contains(); + */ +template <> +class nsDefaultComparator , + nsRefPtr> { + public: + + bool Equals( + const nsRefPtr& aDeviceA, + const nsRefPtr& aDeviceB) const + { + nsString addressA, addressB; + aDeviceA->GetAddress(addressA); + aDeviceB->GetAddress(addressB); + + return addressA.Equals(addressB); + } +}; + +/** + * Explicit Specialization of Function Templates + * + * Allows customizing the template code for a given set of template arguments. + * With this function template, nsTArray can handle comparison between + * 'nsRefPtr' and nsString properly, including IndexOf() and + * Contains(); + */ +template <> +class nsDefaultComparator , + nsString> { +public: + bool Equals( + const nsRefPtr& aDevice, + const nsString& aAddress) const + { + nsString deviceAddress; + aDevice->GetAddress(deviceAddress); + + return deviceAddress.Equals(aAddress); + } +}; + #endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/bluetooth2/moz.build 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/bluetooth2/moz.build 2015-02-03 14:33:32.000000000 +0000 @@ -51,6 +51,8 @@ 'bluedroid/BluetoothA2dpHALInterface.cpp', 'bluedroid/BluetoothA2dpManager.cpp', 'bluedroid/BluetoothAvrcpHALInterface.cpp', + 'bluedroid/BluetoothDaemonA2dpInterface.cpp', + 'bluedroid/BluetoothDaemonAvrcpInterface.cpp', 'bluedroid/BluetoothDaemonHandsfreeInterface.cpp', 'bluedroid/BluetoothDaemonHelpers.cpp', 'bluedroid/BluetoothDaemonInterface.cpp', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/camera/GonkCameraControl.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/camera/GonkCameraControl.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/camera/GonkCameraControl.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/camera/GonkCameraControl.cpp 2015-02-03 14:33:33.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 Mozilla Foundation + * Copyright (C) 2012-2015 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -349,11 +349,19 @@ if (NS_FAILED(rv)) { DOM_CAMERA_LOGE("Failed to set recording hint (0x%x)\n", rv); } - } - mCurrentConfiguration.mMode = config.mMode; - mCurrentConfiguration.mRecorderProfile = config.mRecorderProfile; - mCurrentConfiguration.mPictureSize = config.mPictureSize; + mCurrentConfiguration.mMode = config.mMode; + mCurrentConfiguration.mRecorderProfile = config.mRecorderProfile; + + if (config.mMode == kPictureMode) { + mCurrentConfiguration.mPictureSize = config.mPictureSize; + } else /* if config.mMode == kVideoMode */ { + // The following is best-effort; we don't currently support taking + // pictures while in video mode, but we should at least return + // sane values to OnConfigurationChange() handlers... + SetPictureSizeImpl(config.mPictureSize); + } + } return NS_OK; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/CanvasGradient.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/CanvasGradient.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/CanvasGradient.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/CanvasGradient.h 2015-02-03 14:33:34.000000000 +0000 @@ -23,10 +23,10 @@ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CanvasGradient) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(CanvasGradient) - MOZ_BEGIN_NESTED_ENUM_CLASS(Type, uint8_t) + enum class Type : uint8_t { LINEAR = 0, RADIAL - MOZ_END_NESTED_ENUM_CLASS(Type) + }; Type GetType() { @@ -75,8 +75,6 @@ virtual ~CanvasGradient() {} }; -MOZ_FINISH_NESTED_ENUM_CLASS(CanvasGradient::Type) - } // namespace dom } // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/CanvasPattern.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/CanvasPattern.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/CanvasPattern.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/CanvasPattern.h 2015-02-03 14:33:34.000000000 +0000 @@ -29,12 +29,12 @@ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CanvasPattern) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(CanvasPattern) - MOZ_BEGIN_NESTED_ENUM_CLASS(RepeatMode, uint8_t) + enum class RepeatMode : uint8_t { REPEAT, REPEATX, REPEATY, NOREPEAT - MOZ_END_NESTED_ENUM_CLASS(RepeatMode) + }; CanvasPattern(CanvasRenderingContext2D* aContext, gfx::SourceSurface* aSurface, @@ -74,8 +74,6 @@ const RepeatMode mRepeat; }; -MOZ_FINISH_NESTED_ENUM_CLASS(CanvasPattern::RepeatMode) - } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/CanvasRenderingContext2D.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/CanvasRenderingContext2D.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/CanvasRenderingContext2D.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/CanvasRenderingContext2D.cpp 2015-02-03 14:33:34.000000000 +0000 @@ -68,6 +68,7 @@ #include "jsapi.h" #include "jsfriendapi.h" +#include "js/Conversions.h" #include "mozilla/Alignment.h" #include "mozilla/Assertions.h" @@ -4820,10 +4821,10 @@ return nullptr; } - int32_t x = JS_DoubleToInt32(aSx); - int32_t y = JS_DoubleToInt32(aSy); - int32_t wi = JS_DoubleToInt32(aSw); - int32_t hi = JS_DoubleToInt32(aSh); + int32_t x = JS::ToInt32(aSx); + int32_t y = JS::ToInt32(aSy); + int32_t wi = JS::ToInt32(aSw); + int32_t hi = JS::ToInt32(aSh); // Handle negative width and height by flipping the rectangle over in the // relevant direction. @@ -5016,7 +5017,7 @@ DebugOnly inited = arr.Init(imageData.GetDataObject()); MOZ_ASSERT(inited); - error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy), + error = PutImageData_explicit(JS::ToInt32(dx), JS::ToInt32(dy), imageData.Width(), imageData.Height(), &arr, false, 0, 0, 0, 0); } @@ -5032,13 +5033,13 @@ DebugOnly inited = arr.Init(imageData.GetDataObject()); MOZ_ASSERT(inited); - error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy), + error = PutImageData_explicit(JS::ToInt32(dx), JS::ToInt32(dy), imageData.Width(), imageData.Height(), &arr, true, - JS_DoubleToInt32(dirtyX), - JS_DoubleToInt32(dirtyY), - JS_DoubleToInt32(dirtyWidth), - JS_DoubleToInt32(dirtyHeight)); + JS::ToInt32(dirtyX), + JS::ToInt32(dirtyY), + JS::ToInt32(dirtyWidth), + JS::ToInt32(dirtyHeight)); } // void putImageData (in ImageData d, in float x, in float y); @@ -5206,8 +5207,8 @@ return nullptr; } - int32_t wi = JS_DoubleToInt32(sw); - int32_t hi = JS_DoubleToInt32(sh); + int32_t wi = JS::ToInt32(sw); + int32_t hi = JS::ToInt32(sh); uint32_t w = Abs(wi); uint32_t h = Abs(hi); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/CanvasRenderingContext2D.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/CanvasRenderingContext2D.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/CanvasRenderingContext2D.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/CanvasRenderingContext2D.h 2015-02-03 14:33:34.000000000 +0000 @@ -543,17 +543,17 @@ NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(CanvasRenderingContext2D) - MOZ_BEGIN_NESTED_ENUM_CLASS(CanvasMultiGetterType, uint8_t) + enum class CanvasMultiGetterType : uint8_t { STRING = 0, PATTERN = 1, GRADIENT = 2 - MOZ_END_NESTED_ENUM_CLASS(CanvasMultiGetterType) + }; - MOZ_BEGIN_NESTED_ENUM_CLASS(Style, uint8_t) + enum class Style : uint8_t { STROKE = 0, FILL, MAX - MOZ_END_NESTED_ENUM_CLASS(Style) + }; nsINode* GetParentObject() { @@ -891,31 +891,29 @@ // text -public: // These enums are public only to accomodate non-C++11 legacy path of - // MOZ_FINISH_NESTED_ENUM_CLASS. Can move back to protected as soon - // as that legacy path is dropped. - MOZ_BEGIN_NESTED_ENUM_CLASS(TextAlign, uint8_t) +protected: + enum class TextAlign : uint8_t { START, END, LEFT, RIGHT, CENTER - MOZ_END_NESTED_ENUM_CLASS(TextAlign) + }; - MOZ_BEGIN_NESTED_ENUM_CLASS(TextBaseline, uint8_t) + enum class TextBaseline : uint8_t { TOP, HANGING, MIDDLE, ALPHABETIC, IDEOGRAPHIC, BOTTOM - MOZ_END_NESTED_ENUM_CLASS(TextBaseline) + }; - MOZ_BEGIN_NESTED_ENUM_CLASS(TextDrawOperation, uint8_t) + enum class TextDrawOperation : uint8_t { FILL, STROKE, MEASURE - MOZ_END_NESTED_ENUM_CLASS(TextDrawOperation) + }; protected: gfxFontGroup *GetCurrentFontStyle(); @@ -1104,12 +1102,6 @@ friend class CanvasDrawObserver; }; -MOZ_FINISH_NESTED_ENUM_CLASS(CanvasRenderingContext2D::CanvasMultiGetterType) -MOZ_FINISH_NESTED_ENUM_CLASS(CanvasRenderingContext2D::Style) -MOZ_FINISH_NESTED_ENUM_CLASS(CanvasRenderingContext2D::TextAlign) -MOZ_FINISH_NESTED_ENUM_CLASS(CanvasRenderingContext2D::TextBaseline) -MOZ_FINISH_NESTED_ENUM_CLASS(CanvasRenderingContext2D::TextDrawOperation) - } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGL2ContextPrograms.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGL2ContextPrograms.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGL2ContextPrograms.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGL2ContextPrograms.cpp 2015-02-03 14:33:34.000000000 +0000 @@ -4,16 +4,25 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "WebGL2Context.h" + #include "GLContext.h" +#include "WebGLProgram.h" -using namespace mozilla; -using namespace mozilla::dom; +namespace mozilla { // ------------------------------------------------------------------------- // Programs and shaders + GLint -WebGL2Context::GetFragDataLocation(WebGLProgram* program, const nsAString& name) +WebGL2Context::GetFragDataLocation(WebGLProgram* prog, const nsAString& name) { - MOZ_CRASH("Not Implemented."); - return 0; + if (IsContextLost()) + return -1; + + if (!ValidateObject("getFragDataLocation: program", prog)) + return -1; + + return prog->GetFragDataLocation(name); } + +} // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLContext.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLContext.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLContext.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLContext.cpp 2015-02-03 14:33:34.000000000 +0000 @@ -1060,7 +1060,6 @@ if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) return; - static const fallible_t fallible = fallible_t(); uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4]; if (!imageBuffer) { dataSurface->Unmap(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLContextDraw.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLContextDraw.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLContextDraw.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLContextDraw.cpp 2015-02-03 14:33:34.000000000 +0000 @@ -581,7 +581,7 @@ GetAndFlushUnderlyingGLErrors(); if (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray) { - UniquePtr array(new ((fallible_t())) GLfloat[4 * vertexCount]); + UniquePtr array(new (fallible) GLfloat[4 * vertexCount]); if (!array) { ErrorOutOfMemory("Fake attrib0 array."); return false; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLContextGL.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLContextGL.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLContextGL.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLContextGL.cpp 2015-02-03 14:33:34.000000000 +0000 @@ -48,7 +48,6 @@ #include "mozilla/dom/ImageData.h" #include "mozilla/dom/ToJSValue.h" #include "mozilla/Endian.h" -#include "mozilla/fallible.h" using namespace mozilla; using namespace mozilla::dom; @@ -2086,7 +2085,7 @@ uint32_t subrect_byteLength = (subrect_height-1)*subrect_alignedRowSize + subrect_plainRowSize; // create subrect buffer, call glReadPixels, copy pixels into destination buffer, delete subrect buffer - UniquePtr subrect_data(new ((fallible_t())) GLubyte[subrect_byteLength]); + UniquePtr subrect_data(new (fallible) GLubyte[subrect_byteLength]); if (!subrect_data) return ErrorOutOfMemory("readPixels: subrect_data"); @@ -3203,7 +3202,7 @@ else { size_t convertedDataSize = height * dstStride; - convertedData = new ((fallible_t())) uint8_t[convertedDataSize]; + convertedData = new (fallible) uint8_t[convertedDataSize]; if (!convertedData) { ErrorOutOfMemory("texImage2D: Ran out of memory when allocating" " a buffer for doing format conversion."); @@ -3404,7 +3403,7 @@ if (!noConversion) { size_t convertedDataSize = height * dstStride; - convertedData = new ((fallible_t())) uint8_t[convertedDataSize]; + convertedData = new (fallible) uint8_t[convertedDataSize]; if (!convertedData) { ErrorOutOfMemory("texImage2D: Ran out of memory when allocating" " a buffer for doing format conversion."); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLContext.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLContext.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLContext.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLContext.h 2015-02-03 14:33:34.000000000 +0000 @@ -1524,7 +1524,10 @@ const bool mNeedsChange; static bool NeedsChange(WebGLContext& webgl) { - return webgl.mNeedsFakeNoAlpha && + // We should only be doing this if we're about to draw to the backbuffer, but + // the backbuffer needs to have this fake-no-alpha workaround. + return !webgl.mBoundDrawFramebuffer && + webgl.mNeedsFakeNoAlpha && webgl.mColorWriteMask[3] != false; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLProgram.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLProgram.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLProgram.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLProgram.cpp 2015-02-03 14:33:34.000000000 +0000 @@ -212,6 +212,7 @@ webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* aProg) : prog(aProg) + , fragDataMap(nullptr) { } //////////////////////////////////////////////////////////////////////////////// @@ -417,6 +418,29 @@ return gl->fGetAttribLocation(mGLName, mappedName.BeginReading()); } +GLint +WebGLProgram::GetFragDataLocation(const nsAString& userName_wide) const +{ + if (!ValidateGLSLVariableName(userName_wide, mContext, "getFragDataLocation")) + return -1; + + if (!IsLinked()) { + mContext->ErrorInvalidOperation("getFragDataLocation: `program` must be linked."); + return -1; + } + + const NS_LossyConvertUTF16toASCII userName(userName_wide); + + nsCString mappedName; + if (!LinkInfo()->FindFragData(userName, &mappedName)) + return -1; + + gl::GLContext* gl = mContext->GL(); + gl->MakeCurrent(); + + return gl->fGetFragDataLocation(mGLName, mappedName.BeginReading()); +} + void WebGLProgram::GetProgramInfoLog(nsAString* const out) const { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLProgram.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLProgram.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLProgram.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLProgram.h 2015-02-03 14:33:34.000000000 +0000 @@ -38,6 +38,7 @@ // user-facing `GLActiveInfo::name`s, without any final "[0]". std::map attribMap; std::map uniformMap; + std::map* fragDataMap; // Needed for draw call validation. std::set activeAttribLocs; @@ -66,6 +67,17 @@ return true; } + bool FindFragData(const nsCString& baseUserName, + nsCString* const out_baseMappedName) const + { + if (!fragDataMap) { + *out_baseMappedName = baseUserName; + return true; + } + + MOZ_CRASH("Not implemented."); + } + bool HasActiveAttrib(GLuint loc) const { auto itr = activeAttribLocs.find(loc); return itr != activeAttribLocs.end(); @@ -100,6 +112,7 @@ already_AddRefed GetActiveUniform(GLuint index) const; void GetAttachedShaders(nsTArray>* const out) const; GLint GetAttribLocation(const nsAString& name) const; + GLint GetFragDataLocation(const nsAString& name) const; void GetProgramInfoLog(nsAString* const out) const; JS::Value GetProgramParameter(GLenum pname) const; already_AddRefed GetUniformLocation(const nsAString& name) const; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLTexelConversions.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLTexelConversions.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLTexelConversions.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLTexelConversions.cpp 2015-02-03 14:33:34.000000000 +0000 @@ -37,7 +37,7 @@ * texels with typed pointers and this value will tell us by how much we need * to increment these pointers to advance to the next texel. */ - template + template static size_t NumElementsPerTexelForFormat() { switch (Format) { case WebGLTexelFormat::R8: @@ -78,9 +78,9 @@ * to return immediately in these cases to allow the compiler to avoid generating * useless code. */ - template + template void run() { // check for never-called cases. We early-return to allow the compiler @@ -151,9 +151,9 @@ typename DataTypeForFormat::Type DstType; - const MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) IntermediateSrcFormat + const WebGLTexelFormat IntermediateSrcFormat = IntermediateFormat::Value; - const MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) IntermediateDstFormat + const WebGLTexelFormat IntermediateDstFormat = IntermediateFormat::Value; typedef typename DataTypeForFormat::Type @@ -218,8 +218,8 @@ return; } - template + template void run(WebGLTexelPremultiplicationOp premultiplicationOp) { #define WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(PremultiplicationOp) \ @@ -237,7 +237,7 @@ #undef WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP } - template + template void run(WebGLTexelFormat dstFormat, WebGLTexelPremultiplicationOp premultiplicationOp) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLTexelConversions.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLTexelConversions.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLTexelConversions.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLTexelConversions.h 2015-02-03 14:33:34.000000000 +0000 @@ -147,15 +147,15 @@ return f32Value; } -MOZ_BEGIN_ENUM_CLASS(WebGLTexelPremultiplicationOp, int) +enum class WebGLTexelPremultiplicationOp : int { None, Premultiply, Unpremultiply -MOZ_END_ENUM_CLASS(WebGLTexelPremultiplicationOp) +}; namespace WebGLTexelConversions { -template +template struct IsFloatFormat { static const bool Value = @@ -166,7 +166,7 @@ Format == WebGLTexelFormat::A32F; }; -template +template struct IsHalfFloatFormat { static const bool Value = @@ -177,7 +177,7 @@ Format == WebGLTexelFormat::A16F; }; -template +template struct Is16bppFormat { static const bool Value = @@ -186,7 +186,7 @@ Format == WebGLTexelFormat::RGB565; }; -template::Value, bool Is16bpp = Is16bppFormat::Value, bool IsHalfFloat = IsHalfFloatFormat::Value> @@ -195,28 +195,28 @@ typedef uint8_t Type; }; -template +template struct DataTypeForFormat { typedef float Type; }; -template +template struct DataTypeForFormat { typedef uint16_t Type; }; -template +template struct DataTypeForFormat { typedef uint16_t Type; }; -template +template struct IntermediateFormat { - static const MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Value + static const WebGLTexelFormat Value = IsFloatFormat::Value ? WebGLTexelFormat::RGBA32F : IsHalfFloatFormat::Value ? WebGLTexelFormat::RGBA16F @@ -332,7 +332,7 @@ //---------------------------------------------------------------------- // Pixel unpacking routines. -template +template MOZ_ALWAYS_INLINE void unpack(const SrcType* __restrict src, DstType* __restrict dst) @@ -537,8 +537,8 @@ // Pixel packing routines. // -template MOZ_ALWAYS_INLINE void diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLTypes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLTypes.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/canvas/WebGLTypes.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/canvas/WebGLTypes.h 2015-02-03 14:33:34.000000000 +0000 @@ -6,8 +6,6 @@ #ifndef WEBGLTYPES_H_ #define WEBGLTYPES_H_ -#include "mozilla/TypedEnum.h" - // Most WebIDL typedefs are identical to their OpenGL counterparts. #include "GLTypes.h" @@ -46,18 +44,18 @@ * reason why it needs to be faked (incomplete texture vs. uninitialized image data), * whereas the WebGL context can only know whether _any_ faking is currently needed at all. */ -MOZ_BEGIN_ENUM_CLASS(WebGLContextFakeBlackStatus, uint8_t) +enum class WebGLContextFakeBlackStatus : uint8_t { Unknown, NotNeeded, Needed -MOZ_END_ENUM_CLASS(WebGLContextFakeBlackStatus) +}; -MOZ_BEGIN_ENUM_CLASS(WebGLTextureFakeBlackStatus, uint8_t) +enum class WebGLTextureFakeBlackStatus : uint8_t { Unknown, NotNeeded, IncompleteTexture, UninitializedImageData -MOZ_END_ENUM_CLASS(WebGLTextureFakeBlackStatus) +}; /* * Implementing WebGL (or OpenGL ES 2.0) on top of desktop OpenGL requires @@ -65,11 +63,11 @@ * OpenGL ES 2.0 allows drawing without vertex attrib 0 array enabled, but * desktop OpenGL does not allow that. */ -MOZ_BEGIN_ENUM_CLASS(WebGLVertexAttrib0Status, uint8_t) +enum class WebGLVertexAttrib0Status : uint8_t { Default, // default status - no emulation needed EmulatedUninitializedArray, // need an artificial attrib 0 array, but contents may be left uninitialized EmulatedInitializedArray // need an artificial attrib 0 array, and contents must be initialized -MOZ_END_ENUM_CLASS(WebGLVertexAttrib0Status) +}; /* * Enum to track the status of image data (renderbuffer or texture image) presence @@ -81,11 +79,11 @@ * initialized. It is the state that renderbuffers are in after a renderbufferStorage call, * and it is the state that texture images are in after a texImage2D call with null data. */ -MOZ_BEGIN_ENUM_CLASS(WebGLImageDataStatus, uint8_t) +enum class WebGLImageDataStatus : uint8_t { NoImageData, UninitializedImageData, InitializedImageData -MOZ_END_ENUM_CLASS(WebGLImageDataStatus) +}; /* * The formats that may participate, either as source or destination formats, @@ -95,7 +93,7 @@ * - additional source formats, depending on browser details, used when uploading * textures from DOM elements. See gfxImageSurface::Format(). */ -MOZ_BEGIN_ENUM_CLASS(WebGLTexelFormat, uint8_t) +enum class WebGLTexelFormat : uint8_t { // returned by SurfaceFromElementResultToImageSurface to indicate absence of image data None, // common value for formats for which format conversions are not supported @@ -128,24 +126,24 @@ RGBA4444, RGBA16F, // OES_texture_half_float RGBA32F // OES_texture_float -MOZ_END_ENUM_CLASS(WebGLTexelFormat) +}; -MOZ_BEGIN_ENUM_CLASS(WebGLTexImageFunc, uint8_t) +enum class WebGLTexImageFunc : uint8_t { TexImage, TexSubImage, CopyTexImage, CopyTexSubImage, CompTexImage, CompTexSubImage, -MOZ_END_ENUM_CLASS(WebGLTexImageFunc) +}; -MOZ_BEGIN_ENUM_CLASS(WebGLTexDimensions, uint8_t) +enum class WebGLTexDimensions : uint8_t { Tex2D, Tex3D -MOZ_END_ENUM_CLASS(WebGLTexDimensions) +}; // Please keep extensions in alphabetic order. -MOZ_BEGIN_ENUM_CLASS(WebGLExtensionID, uint8_t) +enum class WebGLExtensionID : uint8_t { ANGLE_instanced_arrays, EXT_blend_minmax, EXT_color_buffer_half_float, @@ -172,7 +170,7 @@ WEBGL_lose_context, Max, Unknown -MOZ_END_ENUM_CLASS(WebGLExtensionID) +}; } // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/contacts/fallback/ContactDB.jsm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/contacts/fallback/ContactDB.jsm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/contacts/fallback/ContactDB.jsm 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/contacts/fallback/ContactDB.jsm 2015-02-03 14:33:35.000000000 +0000 @@ -944,7 +944,8 @@ if (DEBUG) debug("No object ID passed"); return; } - this.newTxn("readwrite", SAVED_GETALL_STORE_NAME, function(txn, store) { + this.newTxn("readwrite", this.dbStoreNames, function(txn, stores) { + let store = txn.objectStore(SAVED_GETALL_STORE_NAME); store.openCursor().onsuccess = function(e) { let cursor = e.target.result; if (cursor) { @@ -961,10 +962,7 @@ aCallback(txn); } }.bind(this); - }.bind(this), null, - function(errorMsg) { - aFailureCb(errorMsg); - }); + }.bind(this), null, aFailureCb); }, incrementRevision: function CDB_incrementRevision(txn) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/contacts/tests/chrome.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/contacts/tests/chrome.ini --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/contacts/tests/chrome.ini 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/contacts/tests/chrome.ini 2015-02-03 14:33:35.000000000 +0000 @@ -1,10 +1,6 @@ [DEFAULT] - -[test_contacts_shutdown.xul] skip-if = os == "android" +[test_contacts_shutdown.xul] [test_contacts_upgrade.xul] -skip-if = os == "android" - [test_contacts_cache.xul] -skip-if = os == "android" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/contacts/tests/test_contacts_cache.xul thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/contacts/tests/test_contacts_cache.xul --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/contacts/tests/test_contacts_cache.xul 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/contacts/tests/test_contacts_cache.xul 2015-02-03 14:33:35.000000000 +0000 @@ -42,6 +42,13 @@ req.onerror = makeFailure("onerror", true); }; + function checkRevision(expectedRevision, then) { + contactsDB.getRevision(function(revision) { + ok(expectedRevision === revision, "Revision OK"); + then(); + }, makeFailure("Could not get revision")); + }; + let CONTACT_PROPS = { id: "ab74671e36be41b680f8f030e7e24ea2", properties: { @@ -61,25 +68,31 @@ }; let Tests = [function() { + info("Deleting database"); + deleteDatabase(next); + }, function() { + info("Checking initial revision"); + checkRevision(0, next); + }, function() { info("Save contact"); contactsDB.saveContact(CONTACT_PROPS, function() { ok(true, "Saved contact successfully"); - next(); - }); + checkRevision(1, next); + }, makeFailure("Could not save contact")); }, function() { info("Save another contact"); contactsDB.saveContact(ANOTHER_CONTACT_PROPS, function() { ok(true, "Saved contact successfully"); - next(); - }); + checkRevision(2, next); + }, makeFailure("Could not save contact")); }, function() { info("Get all contacts so cache is built"); contactsDB.getAll(function(contacts) { ok(true, "Got all contacts " + contacts.length); next(); - }, function(e) { - makeFailure("Unexpected error getting contacts " + e); - }, {"sortBy":"givenName","sortOrder":"ascending"}); + }, makeFailure("Unexpected error getting contacts"), { + "sortBy":"givenName","sortOrder":"ascending" + }); }, function() { info("Contacts cache should have both ids"); let contactsCount = 0; @@ -87,20 +100,32 @@ store.openCursor().onsuccess = function(e) { let cursor = e.target.result; if (!cursor) { - makeFailure("Wrong cache"); + makeFailure("Wrong cache")(); return; } ok(cursor.value.length == 2, "Both contacts ids are in the cache"); next(); }; - }, null, makeFailure); + }, null, makeFailure("Txn error")); }, function() { info("Remove contact " + CONTACT_PROPS.id); contactsDB.removeContact(CONTACT_PROPS.id, function() { ok(true, "Removed contact"); - next(); - }, function() { - makeFailure("Unexpected error removing contact " + e); + checkRevision(3, next); + }, makeFailure("Unexpected error removing contact ")); + }, function() { + info("Check that contact has been removed for good"); + contactsDB.newTxn("readonly", STORE_NAME, function(txn, store) { + let req = store.openCursor(IDBKeyRange.only(CONTACT_PROPS.id)); + req.onsuccess = function(event) { + if (event.target.result) { + makeFailure("Should not have cursor")(); + return; + } + ok(true, "Yep, the contact was removed"); + next(); + }; + req.onerror = makeFailure("OpenCursor error"); }); }, function() { info("Contacts cache should have only one id"); @@ -108,14 +133,14 @@ store.openCursor().onsuccess = function(e) { let cursor = e.target.result; if (!cursor) { - makeFailure; + makeFailure("Missing cursor")(); return; } ok(cursor.value.length == 1, "Only one contacts id is in the cache"); ok(cursor.value[0] == ANOTHER_CONTACT_PROPS.id, "And it is the right id"); next(); }; - }, null, makeFailure); + }, null, makeFailure("Txn error")); }, function() { deleteDatabase(next); }]; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/contacts/tests/test_contacts_upgrade.xul thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/contacts/tests/test_contacts_upgrade.xul --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/contacts/tests/test_contacts_upgrade.xul 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/contacts/tests/test_contacts_upgrade.xul 2015-02-03 14:33:35.000000000 +0000 @@ -15,8 +15,6 @@ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/encoding/TextDecoder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/encoding/TextDecoder.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/encoding/TextDecoder.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/encoding/TextDecoder.cpp 2015-02-03 14:33:36.000000000 +0000 @@ -63,7 +63,6 @@ } // Need a fallible allocator because the caller may be a content // and the content can specify the length of the string. - static const fallible_t fallible = fallible_t(); nsAutoArrayPtr buf(new (fallible) char16_t[outLen + 1]); if (!buf) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/encoding/TextEncoder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/encoding/TextEncoder.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/encoding/TextEncoder.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/encoding/TextEncoder.cpp 2015-02-03 14:33:36.000000000 +0000 @@ -53,7 +53,6 @@ } // Need a fallible allocator because the caller may be a content // and the content can specify the length of the string. - static const fallible_t fallible = fallible_t(); nsAutoArrayPtr buf(new (fallible) char[maxLen + 1]); if (!buf) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/ContentEventHandler.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/ContentEventHandler.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/ContentEventHandler.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/ContentEventHandler.cpp 2015-02-03 14:33:36.000000000 +0000 @@ -314,6 +314,17 @@ return GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aMaxLength); } +static inline uint32_t +GetBRLength(LineBreakType aLineBreakType) +{ +#if defined(XP_WIN) + // Length of \r\n + return (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1; +#else + return 1; +#endif +} + /* static */ uint32_t ContentEventHandler::GetTextLength(nsIContent* aContent, LineBreakType aLineBreakType, @@ -344,12 +355,7 @@ uint32_t length = std::min(text->GetLength(), aMaxLength); return length + textLengthDifference; } else if (IsContentBR(aContent)) { -#if defined(XP_WIN) - // Length of \r\n - return (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1; -#else - return 1; -#endif + return GetBRLength(aLineBreakType); } return 0; } @@ -393,7 +399,6 @@ return NS_OK; } - nsAutoString tmpStr; for (; !iter->IsDone(); iter->Next()) { nsINode* node = iter->GetCurrentNode(); if (!node) { @@ -423,6 +428,171 @@ return NS_OK; } +static FontRange* +AppendFontRange(nsTArray& aFontRanges, uint32_t aBaseOffset) +{ + FontRange* fontRange = aFontRanges.AppendElement(); + fontRange->mStartOffset = aBaseOffset; + return fontRange; +} + +/* static */ uint32_t +ContentEventHandler::GetTextLengthInRange(nsIContent* aContent, + uint32_t aXPStartOffset, + uint32_t aXPEndOffset, + LineBreakType aLineBreakType) +{ + return aLineBreakType == LINE_BREAK_TYPE_NATIVE ? + GetNativeTextLength(aContent, aXPStartOffset, aXPEndOffset) : + aXPEndOffset - aXPStartOffset; +} + +/* static */ void +ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges, + nsIContent* aContent, + int32_t aBaseOffset, + int32_t aXPStartOffset, + int32_t aXPEndOffset, + LineBreakType aLineBreakType) +{ + nsIFrame* frame = aContent->GetPrimaryFrame(); + if (!frame) { + // It is a non-rendered content, create an empty range for it. + AppendFontRange(aFontRanges, aBaseOffset); + return; + } + + int32_t baseOffset = aBaseOffset; + nsTextFrame* curr = do_QueryFrame(frame); + MOZ_ASSERT(curr, "Not a text frame"); + while (curr) { + int32_t frameXPStart = std::max(curr->GetContentOffset(), aXPStartOffset); + int32_t frameXPEnd = std::min(curr->GetContentEnd(), aXPEndOffset); + if (frameXPStart >= frameXPEnd) { + curr = static_cast(curr->GetNextContinuation()); + continue; + } + + gfxSkipCharsIterator iter = curr->EnsureTextRun(nsTextFrame::eInflated); + gfxTextRun* textRun = curr->GetTextRun(nsTextFrame::eInflated); + + nsTextFrame* next = nullptr; + if (frameXPEnd < aXPEndOffset) { + next = static_cast(curr->GetNextContinuation()); + while (next && next->GetTextRun(nsTextFrame::eInflated) == textRun) { + frameXPEnd = std::min(next->GetContentEnd(), aXPEndOffset); + next = frameXPEnd < aXPEndOffset ? + static_cast(next->GetNextContinuation()) : nullptr; + } + } + + uint32_t skipStart = iter.ConvertOriginalToSkipped(frameXPStart); + uint32_t skipEnd = iter.ConvertOriginalToSkipped(frameXPEnd); + gfxTextRun::GlyphRunIterator runIter( + textRun, skipStart, skipEnd - skipStart); + int32_t lastXPEndOffset = frameXPStart; + while (runIter.NextRun()) { + gfxFont* font = runIter.GetGlyphRun()->mFont.get(); + int32_t startXPOffset = + iter.ConvertSkippedToOriginal(runIter.GetStringStart()); + // It is possible that the first glyph run has exceeded the frame, + // because the whole frame is filled by skipped chars. + if (startXPOffset >= frameXPEnd) { + break; + } + + if (startXPOffset > lastXPEndOffset) { + // Create range for skipped leading chars. + AppendFontRange(aFontRanges, baseOffset); + baseOffset += GetTextLengthInRange( + aContent, lastXPEndOffset, startXPOffset, aLineBreakType); + lastXPEndOffset = startXPOffset; + } + + FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset); + fontRange->mFontName = font->GetName(); + fontRange->mFontSize = font->GetAdjustedSize(); + + // The converted original offset may exceed the range, + // hence we need to clamp it. + int32_t endXPOffset = + iter.ConvertSkippedToOriginal(runIter.GetStringEnd()); + endXPOffset = std::min(frameXPEnd, endXPOffset); + baseOffset += GetTextLengthInRange(aContent, startXPOffset, endXPOffset, + aLineBreakType); + lastXPEndOffset = endXPOffset; + } + if (lastXPEndOffset < frameXPEnd) { + // Create range for skipped trailing chars. It also handles case + // that the whole frame contains only skipped chars. + AppendFontRange(aFontRanges, baseOffset); + baseOffset += GetTextLengthInRange( + aContent, lastXPEndOffset, frameXPEnd, aLineBreakType); + } + + curr = next; + } +} + +/* static */ nsresult +ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange, + FontRangeArray& aFontRanges, + uint32_t& aLength, + LineBreakType aLineBreakType) +{ + MOZ_ASSERT(aFontRanges.IsEmpty(), "aRanges must be empty array"); + + nsINode* startNode = aRange->GetStartParent(); + nsINode* endNode = aRange->GetEndParent(); + if (NS_WARN_IF(!startNode || !endNode)) { + return NS_ERROR_FAILURE; + } + + // baseOffset is the flattened offset of each content node. + int32_t baseOffset = 0; + nsCOMPtr iter = NS_NewContentIterator(); + for (iter->Init(aRange); !iter->IsDone(); iter->Next()) { + nsINode* node = iter->GetCurrentNode(); + if (NS_WARN_IF(!node)) { + break; + } + if (!node->IsContent()) { + continue; + } + nsIContent* content = node->AsContent(); + + if (content->IsNodeOfType(nsINode::eTEXT)) { + int32_t startOffset = content != startNode ? 0 : aRange->StartOffset(); + int32_t endOffset = content != endNode ? + content->TextLength() : aRange->EndOffset(); + AppendFontRanges(aFontRanges, content, baseOffset, + startOffset, endOffset, aLineBreakType); + baseOffset += GetTextLengthInRange(content, startOffset, endOffset, + aLineBreakType); + } else if (IsContentBR(content)) { + if (aFontRanges.IsEmpty()) { + MOZ_ASSERT(baseOffset == 0); + FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset); + nsIFrame* frame = content->GetPrimaryFrame(); + if (frame) { + const nsFont& font = frame->GetParent()->StyleFont()->mFont; + const FontFamilyList& fontList = font.fontlist; + const FontFamilyName& fontName = fontList.IsEmpty() ? + FontFamilyName(fontList.GetDefaultFontType()) : + fontList.GetFontlist()[0]; + fontName.AppendToString(fontRange->mFontName, false); + fontRange->mFontSize = + frame->PresContext()->AppUnitsToDevPixels(font.size); + } + } + baseOffset += GetBRLength(aLineBreakType); + } + } + + aLength = baseOffset; + return NS_OK; +} + nsresult ContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent, bool aForward, @@ -697,6 +867,18 @@ rv = GenerateFlatTextContent(range, aEvent->mReply.mString, lineBreakType); NS_ENSURE_SUCCESS(rv, rv); + if (aEvent->mWithFontRanges) { + uint32_t fontRangeLength; + rv = GenerateFlatFontRanges(range, aEvent->mReply.mFontRanges, + fontRangeLength, lineBreakType); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(fontRangeLength == aEvent->mReply.mString.Length(), + "Font ranges doesn't match the string"); + } + aEvent->mSucceeded = true; return NS_OK; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/ContentEventHandler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/ContentEventHandler.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/ContentEventHandler.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/ContentEventHandler.h 2015-02-03 14:33:36.000000000 +0000 @@ -94,6 +94,12 @@ // Get the native text length of a content node excluding any children static uint32_t GetNativeTextLength(nsIContent* aContent, uint32_t aMaxLength = UINT32_MAX); + // Get the text length of a given range of a content node in + // the given line break type. + static uint32_t GetTextLengthInRange(nsIContent* aContent, + uint32_t aXPStartOffset, + uint32_t aXPEndOffset, + LineBreakType aLineBreakType); protected: static uint32_t GetTextLength(nsIContent* aContent, LineBreakType aLineBreakType, @@ -129,6 +135,18 @@ // true, it is expanded to forward. nsresult ExpandToClusterBoundary(nsIContent* aContent, bool aForward, uint32_t* aXPOffset); + + typedef nsTArray FontRangeArray; + static void AppendFontRanges(FontRangeArray& aFontRanges, + nsIContent* aContent, + int32_t aBaseOffset, + int32_t aXPStartOffset, + int32_t aXPEndOffset, + LineBreakType aLineBreakType); + static nsresult GenerateFlatFontRanges(nsRange* aRange, + FontRangeArray& aFontRanges, + uint32_t& aLength, + LineBreakType aLineBreakType); }; } // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/Event.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/Event.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/Event.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/Event.cpp 2015-02-03 14:33:36.000000000 +0000 @@ -966,8 +966,7 @@ return CSSIntPoint(0, 0); } nsPoint pt = - nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, - LayoutDeviceIntPoint::ToUntyped(aPoint), rootFrame); + nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, aPoint, rootFrame); return CSSIntPoint::FromAppUnitsRounded(pt); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/Event.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/Event.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/Event.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/Event.h 2015-02-03 14:33:36.000000000 +0000 @@ -32,6 +32,12 @@ #define GENERATED_EVENT(EventClass_) class EventClass_; #include "mozilla/dom/GeneratedEventList.h" #undef GENERATED_EVENT +// ExtendableEvent and InstallEvent are ServiceWorker events that are not +// autogenerated since they have some extra methods. +namespace workers { +class ExtendableEvent; +class InstallEvent; +} // namespace workers // Dummy class so we can cast through it to get from nsISupports to // Event subclasses with only two non-ambiguous static casts. @@ -104,6 +110,18 @@ #include "mozilla/dom/GeneratedEventList.h" #undef GENERATED_EVENT + // ExtendableEvent and InstallEvent are ServiceWorker events that are not + // autogenerated since they have some extra methods. + virtual workers::ExtendableEvent* AsExtendableEvent() + { + return nullptr; + } + + virtual workers::InstallEvent* AsInstallEvent() + { + return nullptr; + } + // nsIDOMEvent Interface NS_DECL_NSIDOMEVENT diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/EventStateManager.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/EventStateManager.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/EventStateManager.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/EventStateManager.cpp 2015-02-03 14:33:36.000000000 +0000 @@ -5490,6 +5490,12 @@ } bool +EventStateManager::WheelEventIsScrollAction(WidgetWheelEvent* aEvent) +{ + return WheelPrefs::GetInstance()->ComputeActionFor(aEvent) == WheelPrefs::ACTION_SCROLL; +} + +bool EventStateManager::WheelPrefs::IsOverOnePageScrollAllowedX( WidgetWheelEvent* aEvent) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/EventStateManager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/EventStateManager.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/EventStateManager.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/EventStateManager.h 2015-02-03 14:33:36.000000000 +0000 @@ -7,7 +7,6 @@ #define mozilla_EventStateManager_h_ #include "mozilla/EventForwards.h" -#include "mozilla/TypedEnum.h" #include "nsIObserver.h" #include "nsWeakReference.h" @@ -225,6 +224,9 @@ static LayoutDeviceIntPoint GetChildProcessOffset(nsFrameLoader* aFrameLoader, const WidgetEvent& aEvent); + // Returns true if the given WidgetWheelEvent will resolve to a scroll action. + static bool WheelEventIsScrollAction(WidgetWheelEvent* aEvent); + // Holds the point in screen coords that a mouse event was dispatched to, // before we went into pointer lock mode. This is constantly updated while // the pointer is not locked, but we don't update it while the pointer is diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/MouseEvent.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/MouseEvent.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/MouseEvent.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/MouseEvent.cpp 2015-02-03 14:33:36.000000000 +0000 @@ -466,6 +466,12 @@ return NS_OK; } +bool +MouseEvent::HitCluster() const +{ + return mEvent->AsMouseEventBase()->hitCluster; +} + uint16_t MouseEvent::MozInputSource() const { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/MouseEvent.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/MouseEvent.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/MouseEvent.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/MouseEvent.h 2015-02-03 14:33:36.000000000 +0000 @@ -83,6 +83,7 @@ return GetMovementPoint().y; } float MozPressure() const; + bool HitCluster() const; uint16_t MozInputSource() const; void InitNSMouseEvent(const nsAString& aType, bool aCanBubble, bool aCancelable, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/test/bug648573.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/test/bug648573.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/test/bug648573.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/test/bug648573.html 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,111 @@ + + + + + Test for Bug 648573 + + + + +Mozilla Bug 648573 +

    + +
    +
    +
    + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/test/mochitest.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/test/mochitest.ini --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/test/mochitest.ini 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/test/mochitest.ini 2015-02-03 14:33:36.000000000 +0000 @@ -5,6 +5,7 @@ bug299673.js bug322588-popup.html bug426082.html + bug648573.html bug656379-1.html error_event_worker.js empty.js diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/test/test_bug648573.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/test/test_bug648573.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/test/test_bug648573.html 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/test/test_bug648573.html 2015-02-03 14:33:36.000000000 +0000 @@ -1,110 +1,30 @@ - - - - - Test for Bug 648573 - - - - -Mozilla Bug 648573 -

    - -
    -
    -
    - + + + + Bug 648573 test + + + +
    + + - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/test/test_dom_keyboard_event.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/test/test_dom_keyboard_event.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/test/test_dom_keyboard_event.html 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/test/test_dom_keyboard_event.html 2015-02-03 14:33:36.000000000 +0000 @@ -200,10 +200,6 @@ event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, }, - { key: "VK_DOWN", isModifier: false, - event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, - location: KeyboardEvent.DOM_KEY_LOCATION_JOYSTICK }, - }, { key: "5", isModifier: false, event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, @@ -212,14 +208,6 @@ event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, }, - { key: "5", isModifier: false, - event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, - location: KeyboardEvent.DOM_KEY_LOCATION_MOBILE }, - }, - { key: "VK_NUMPAD5", isModifier: false, - event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, - location: KeyboardEvent.DOM_KEY_LOCATION_MOBILE }, - }, { key: "+", isModifier: false, event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, @@ -259,12 +247,8 @@ return "DOM_KEY_LOCATION_LEFT"; case KeyboardEvent.DOM_KEY_LOCATION_RIGHT: return "DOM_KEY_LOCATION_RIGHT"; - case KeyboardEvent.DOM_KEY_LOCATION_MOBILE: - return "DOM_KEY_LOCATION_MOBILE"; case KeyboardEvent.DOM_KEY_LOCATION_NUMPAD: return "DOM_KEY_LOCATION_NUMPAD"; - case KeyboardEvent.DOM_KEY_LOCATION_JOYSTICK: - return "DOM_KEY_LOCATION_JOYSTICK"; default: return "Invalid value (" + aLocation + ")"; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/TextComposition.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/TextComposition.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/TextComposition.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/TextComposition.cpp 2015-02-03 14:33:36.000000000 +0000 @@ -340,35 +340,14 @@ mIsRequestingCancel = false; mIsRequestingCommit = true; } - if (!mIsSynthesizedForTests) { - // FYI: CompositionEvents caused by a call of NotifyIME() may be - // discarded by PresShell if it's not safe to dispatch the event. - nsresult rv = - aWidget->NotifyIME(IMENotification(aDiscard ? - REQUEST_TO_CANCEL_COMPOSITION : - REQUEST_TO_COMMIT_COMPOSITION)); - if (rv == NS_ERROR_NOT_IMPLEMENTED) { - return rv; - } - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } else { - // Emulates to commit or cancel the composition - // FYI: These events may be discarded by PresShell if it's not safe to - // dispatch the event. - nsCOMPtr widget(aWidget); - nsAutoString commitData(aDiscard ? EmptyString() : lastData); - bool isChanging = commitData != mLastData; - uint32_t message = - isChanging ? NS_COMPOSITION_COMMIT : NS_COMPOSITION_COMMIT_AS_IS; - WidgetCompositionEvent commitEvent(true, message, widget); - if (commitEvent.message == NS_COMPOSITION_COMMIT) { - commitEvent.mData = commitData; - } - commitEvent.mFlags.mIsSynthesizedForTests = true; - nsEventStatus status = nsEventStatus_eIgnore; - widget->DispatchEvent(&commitEvent, status); + // FYI: CompositionEvents caused by a call of NotifyIME() may be + // discarded by PresShell if it's not safe to dispatch the event. + nsresult rv = + aWidget->NotifyIME(IMENotification(aDiscard ? + REQUEST_TO_CANCEL_COMPOSITION : + REQUEST_TO_COMMIT_COMPOSITION)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/Touch.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/Touch.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/Touch.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/Touch.cpp 2015-02-03 14:33:36.000000000 +0000 @@ -33,7 +33,7 @@ mPagePoint = CSSIntPoint(aPageX, aPageY); mScreenPoint = nsIntPoint(aScreenX, aScreenY); mClientPoint = CSSIntPoint(aClientX, aClientY); - mRefPoint = nsIntPoint(0, 0); + mRefPoint = LayoutDeviceIntPoint(0, 0); mPointsInitialized = true; mRadius.x = aRadiusX; mRadius.y = aRadiusY; @@ -46,7 +46,7 @@ } Touch::Touch(int32_t aIdentifier, - nsIntPoint aPoint, + LayoutDeviceIntPoint aPoint, nsIntPoint aRadius, float aRotationAngle, float aForce) @@ -106,13 +106,10 @@ return; } mClientPoint = Event::GetClientCoords( - aPresContext, aEvent, LayoutDeviceIntPoint::FromUntyped(mRefPoint), - mClientPoint); + aPresContext, aEvent, mRefPoint, mClientPoint); mPagePoint = Event::GetPageCoords( - aPresContext, aEvent, LayoutDeviceIntPoint::FromUntyped(mRefPoint), - mClientPoint); - mScreenPoint = Event::GetScreenCoords(aPresContext, aEvent, - LayoutDeviceIntPoint::FromUntyped(mRefPoint)); + aPresContext, aEvent, mRefPoint, mClientPoint); + mScreenPoint = Event::GetScreenCoords(aPresContext, aEvent, mRefPoint); mPointsInitialized = true; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/Touch.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/Touch.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/events/Touch.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/events/Touch.h 2015-02-03 14:33:36.000000000 +0000 @@ -40,7 +40,7 @@ float aRotationAngle, float aForce); Touch(int32_t aIdentifier, - nsIntPoint aPoint, + LayoutDeviceIntPoint aPoint, nsIntPoint aRadius, float aRotationAngle, float aForce); @@ -73,7 +73,7 @@ float Force() const { return mForce; } nsCOMPtr mTarget; - nsIntPoint mRefPoint; + LayoutDeviceIntPoint mRefPoint; bool mChanged; uint32_t mMessage; int32_t mIdentifier; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/fetch/Fetch.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/fetch/Fetch.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/fetch/Fetch.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/fetch/Fetch.cpp 2015-02-03 14:33:36.000000000 +0000 @@ -470,7 +470,7 @@ } nsCString encoded; - if (!encoded.SetCapacity(destBufferLen, fallible_t())) { + if (!encoded.SetCapacity(destBufferLen, fallible)) { return NS_ERROR_OUT_OF_MEMORY; } @@ -583,7 +583,7 @@ return rv; } - if (!mDecoded.SetCapacity(mDecoded.Length() + destBufferLen, fallible_t())) { + if (!mDecoded.SetCapacity(mDecoded.Length() + destBufferLen, fallible)) { return NS_ERROR_OUT_OF_MEMORY; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/fetch/Response.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/fetch/Response.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/fetch/Response.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/fetch/Response.h 2015-02-03 14:33:36.000000000 +0000 @@ -62,6 +62,13 @@ return mInternalResponse->GetStatus(); } + bool + Ok() const + { + return mInternalResponse->GetStatus() >= 200 && + mInternalResponse->GetStatus() <= 299; + } + void GetStatusText(nsCString& aStatusText) const { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/filehandle/MemoryStreams.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/filehandle/MemoryStreams.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/filehandle/MemoryStreams.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/filehandle/MemoryStreams.cpp 2015-02-03 14:33:36.000000000 +0000 @@ -26,7 +26,7 @@ nsRefPtr stream = new MemoryOutputStream(); char* dummy; - uint32_t length = stream->mData.GetMutableData(&dummy, aSize, fallible_t()); + uint32_t length = stream->mData.GetMutableData(&dummy, aSize, fallible); NS_ENSURE_TRUE(length == aSize, nullptr); return stream.forget(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/geolocation/nsGeolocation.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/geolocation/nsGeolocation.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/geolocation/nsGeolocation.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/geolocation/nsGeolocation.cpp 2015-02-03 14:33:36.000000000 +0000 @@ -809,7 +809,7 @@ #endif #ifdef MOZ_WIDGET_COCOA - if (Preferences::GetBool("geo.provider.use_corelocation", false)) { + if (Preferences::GetBool("geo.provider.use_corelocation", true)) { mProvider = new CoreLocationLocationProvider(); } #endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/HTMLCanvasElement.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/HTMLCanvasElement.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/HTMLCanvasElement.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/HTMLCanvasElement.h 2015-02-03 14:33:36.000000000 +0000 @@ -7,7 +7,6 @@ #define mozilla_dom_HTMLCanvasElement_h #include "mozilla/Attributes.h" -#include "mozilla/TypedEnum.h" #include "nsIDOMHTMLCanvasElement.h" #include "nsGenericHTMLElement.h" #include "nsGkAtoms.h" @@ -36,11 +35,11 @@ class HTMLCanvasPrintState; class PrintCallback; -MOZ_BEGIN_ENUM_CLASS(CanvasContextType, uint8_t) +enum class CanvasContextType : uint8_t { Canvas2D, WebGL1, WebGL2 -MOZ_END_ENUM_CLASS(CanvasContextType) +}; class HTMLCanvasElement MOZ_FINAL : public nsGenericHTMLElement, public nsIDOMHTMLCanvasElement diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/HTMLFrameSetElement.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/HTMLFrameSetElement.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/HTMLFrameSetElement.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/HTMLFrameSetElement.cpp 2015-02-03 14:33:36.000000000 +0000 @@ -232,7 +232,6 @@ commaX = spec.FindChar(sComma, commaX + 1); } - static const fallible_t fallible = fallible_t(); nsFramesetSpec* specs = new (fallible) nsFramesetSpec[count]; if (!specs) { *aSpecs = nullptr; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/HTMLInputElement.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/HTMLInputElement.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/HTMLInputElement.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/HTMLInputElement.cpp 2015-02-03 14:33:36.000000000 +0000 @@ -2329,6 +2329,16 @@ } void +HTMLInputElement::MozSetFileArray(const Sequence>& aFiles) +{ + nsTArray> files; + for (uint32_t i = 0; i < aFiles.Length(); ++i) { + files.AppendElement(aFiles[i]); + } + SetFiles(files, true); +} + +void HTMLInputElement::MozSetFileNameArray(const Sequence< nsString >& aFileNames) { nsTArray> files; @@ -6611,27 +6621,32 @@ } /** - * Splits the string on the first "@" character and punycode encodes the first - * and second parts separately before rejoining them with an "@" and returning - * the result via the aEncodedEmail out-param. Returns false if there is no - * "@" caracter, if the "@" character is at the start or end, or if the - * conversion to punycode fails. + * Takes aEmail and attempts to convert everything after the first "@" + * character (if anything) to punycode before returning the complete result via + * the aEncodedEmail out-param. The aIndexOfAt out-param is set to the index of + * the "@" character. + * + * If no "@" is found in aEmail, aEncodedEmail is simply set to aEmail and + * the aIndexOfAt out-param is set to kNotFound. * - * This function exists because ConvertUTF8toACE() treats 'username@domain' as - * a single label, but we need to encode the username and domain parts - * separately. + * Returns true in all cases unless an attempt to punycode encode fails. If + * false is returned, aEncodedEmail has not been set. + * + * This function exists because ConvertUTF8toACE() splits on ".", meaning that + * for 'user.name@sld.tld' it would treat "name@sld" as a label. We want to + * encode the domain part only. */ static bool PunycodeEncodeEmailAddress(const nsAString& aEmail, nsAutoCString& aEncodedEmail, uint32_t* aIndexOfAt) { nsAutoCString value = NS_ConvertUTF16toUTF8(aEmail); - uint32_t length = value.Length(); + *aIndexOfAt = (uint32_t)value.FindChar('@'); - uint32_t atPos = (uint32_t)value.FindChar('@'); - // Email addresses must contain a '@', but can't begin or end with it. - if (atPos == (uint32_t)kNotFound || atPos == 0 || atPos == length - 1) { - return false; + if (*aIndexOfAt == (uint32_t)kNotFound || + *aIndexOfAt == value.Length() - 1) { + aEncodedEmail = value; + return true; } nsCOMPtr idnSrv = do_GetService(NS_IDNSERVICE_CONTRACTID); @@ -6640,29 +6655,19 @@ return false; } - const nsDependentCSubstring username = Substring(value, 0, atPos); - bool ace; - if (NS_SUCCEEDED(idnSrv->IsACE(username, &ace)) && !ace) { - nsAutoCString usernameACE; - // TODO: Bug 901347: Usernames longer than 63 chars are not converted by - // ConvertUTF8toACE(). For now, continue on even if the conversion fails. - if (NS_SUCCEEDED(idnSrv->ConvertUTF8toACE(username, usernameACE))) { - value.Replace(0, atPos, usernameACE); - atPos = usernameACE.Length(); - } - } + uint32_t indexOfDomain = *aIndexOfAt + 1; - const nsDependentCSubstring domain = Substring(value, atPos + 1); + const nsDependentCSubstring domain = Substring(value, indexOfDomain); + bool ace; if (NS_SUCCEEDED(idnSrv->IsACE(domain, &ace)) && !ace) { nsAutoCString domainACE; if (NS_FAILED(idnSrv->ConvertUTF8toACE(domain, domainACE))) { return false; } - value.Replace(atPos + 1, domain.Length(), domainACE); + value.Replace(indexOfDomain, domain.Length(), domainACE); } aEncodedEmail = value; - *aIndexOfAt = atPos; return true; } @@ -6889,6 +6894,9 @@ case NS_FORM_INPUT_RADIO: key.AssignLiteral("FormValidationRadioMissing"); break; + case NS_FORM_INPUT_NUMBER: + key.AssignLiteral("FormValidationBadInputNumber"); + break; default: key.AssignLiteral("FormValidationValueMissing"); } @@ -7112,8 +7120,10 @@ uint32_t atPos; nsAutoCString value; - // This call also checks whether aValue contains a correctly-placed '@' sign. - if (!PunycodeEncodeEmailAddress(aValue, value, &atPos)) { + if (!PunycodeEncodeEmailAddress(aValue, value, &atPos) || + atPos == (uint32_t)kNotFound || atPos == 0 || atPos == value.Length() - 1) { + // Could not encode, or "@" was not found, or it was at the start or end + // of the input - in all cases, not a valid email address. return false; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/HTMLInputElement.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/HTMLInputElement.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/HTMLInputElement.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/HTMLInputElement.h 2015-02-03 14:33:36.000000000 +0000 @@ -716,6 +716,7 @@ void MozGetFileNameArray(nsTArray< nsString >& aFileNames); void MozSetFileNameArray(const Sequence< nsString >& aFileNames); + void MozSetFileArray(const Sequence>& aFiles); HTMLInputElement* GetOwnerNumberControl(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/HTMLMediaElement.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/HTMLMediaElement.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/HTMLMediaElement.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/HTMLMediaElement.cpp 2015-02-03 14:33:36.000000000 +0000 @@ -515,6 +515,16 @@ return false; } +already_AddRefed +HTMLMediaElement::GetMozMediaSourceObject() const +{ + nsRefPtr source; + if (IsMediaSourceURI(mLoadingSrc)) { + NS_GetSourceForMediaSourceURI(mLoadingSrc, getter_AddRefs(source)); + } + return source.forget(); +} + already_AddRefed HTMLMediaElement::GetMozSrcObject() const { @@ -1879,7 +1889,6 @@ // back into the output stream. out->mStream->GetStream()->ChangeExplicitBlockerCount(1); if (mDecoder) { - mDecoder->SetAudioCaptured(true); mDecoder->AddOutputStream( out->mStream->GetStream()->AsProcessedStream(), aFinishWhenEnded); } @@ -2069,6 +2078,7 @@ mCORSMode(CORS_NONE), mHasAudio(false), mHasVideo(false), + mIsEncrypted(false), mDownloadSuspendedByCache(false), mAudioChannelFaded(false), mPlayingThroughTheAudioChannel(false), @@ -2694,7 +2704,6 @@ // available immediately. mDecoder->SetResource(aStream); mDecoder->SetAudioChannel(mAudioChannel); - mDecoder->SetAudioCaptured(mAudioCaptured); mDecoder->SetVolume(mMuted ? 0.0 : mVolume); mDecoder->SetPreservesPitch(mPreservesPitch); mDecoder->SetPlaybackRate(mPlaybackRate); @@ -3003,9 +3012,16 @@ { mHasAudio = aInfo->HasAudio(); mHasVideo = aInfo->HasVideo(); + mIsEncrypted = aInfo->mIsEncrypted; mTags = aTags.forget(); mLoadedDataFired = false; ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA); + + if (mIsEncrypted) { + nsCOMPtr obs = services::GetObserverService(); + obs->NotifyObservers(static_cast(this), "media-eme-metadataloaded", nullptr); + } + DispatchAsyncEvent(NS_LITERAL_STRING("durationchange")); if (IsVideo() && mHasVideo) { mMediaSize = aInfo->mVideo.mDisplay; @@ -3301,6 +3317,15 @@ return mCORSMode != CORS_NONE; } +bool HTMLMediaElement::IsCORSSameOrigin() +{ + bool subsumes; + nsRefPtr principal = GetCurrentPrincipal(); + return + (NS_SUCCEEDED(NodePrincipal()->Subsumes(principal, &subsumes)) && subsumes) || + ShouldCheckAllowOrigin(); +} + void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatus aNextFrame) { mLastNextFrameStatus = aNextFrame; @@ -3646,22 +3671,13 @@ { nsRefPtr principal = GetCurrentPrincipal(); - bool subsumes; - mDecoder->UpdateSameOriginStatus( - !principal || - (NS_SUCCEEDED(NodePrincipal()->Subsumes(principal, &subsumes)) && subsumes) || - mCORSMode != CORS_NONE); + mDecoder->UpdateSameOriginStatus(!principal || IsCORSSameOrigin()); for (uint32_t i = 0; i < mOutputStreams.Length(); ++i) { OutputMediaStream* ms = &mOutputStreams[i]; ms->mStream->SetCORSMode(mCORSMode); ms->mStream->CombineWithPrincipal(principal); } -#ifdef MOZ_EME - if (mMediaKeys && NS_FAILED(mMediaKeys->CheckPrincipals())) { - mMediaKeys->Shutdown(); - } -#endif } void HTMLMediaElement::UpdateMediaSize(nsIntSize size) @@ -4305,8 +4321,6 @@ if (mDecoder) { mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy()); } - // Update the same-origin status. - NotifyDecoderPrincipalChanged(); } promise->MaybeResolve(JS::UndefinedHandleValue); return promise.forget(); @@ -4339,8 +4353,13 @@ HTMLMediaElement::DispatchEncrypted(const nsTArray& aInitData, const nsAString& aInitDataType) { - nsRefPtr event( - MediaEncryptedEvent::Constructor(this, aInitDataType, aInitData)); + nsRefPtr event; + if (IsCORSSameOrigin()) { + event = MediaEncryptedEvent::Constructor(this, aInitDataType, aInitData); + } else { + event = MediaEncryptedEvent::Constructor(this); + } + nsRefPtr asyncDispatcher = new AsyncEventDispatcher(this, event); asyncDispatcher->PostDOMEvent(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/HTMLMediaElement.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/HTMLMediaElement.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/HTMLMediaElement.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/HTMLMediaElement.h 2015-02-03 14:33:36.000000000 +0000 @@ -245,6 +245,10 @@ // Check if the media element had crossorigin set when loading started bool ShouldCheckAllowOrigin(); + // Returns true if the currently loaded resource is CORS same-origin with + // respect to the document. + bool IsCORSSameOrigin(); + // Is the media element potentially playing as defined by the HTML 5 specification. // http://www.whatwg.org/specs/web-apps/current-work/#potentially-playing bool IsPotentiallyPlaying() const; @@ -411,6 +415,11 @@ double Duration() const; + bool IsEncrypted() const + { + return mIsEncrypted; + } + bool Paused() const { return mPaused; @@ -524,6 +533,7 @@ mIsCasting = aShow; } + already_AddRefed GetMozMediaSourceObject() const; already_AddRefed GetMozSrcObject() const; void SetMozSrcObject(DOMMediaStream& aValue); @@ -1298,6 +1308,9 @@ // True if the media has a video track bool mHasVideo; + // True if the media has encryption information. + bool mIsEncrypted; + // True if the media's channel's download has been suspended. bool mDownloadSuspendedByCache; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/nsHTMLDocument.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/nsHTMLDocument.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/nsHTMLDocument.cpp 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/nsHTMLDocument.cpp 2015-02-03 14:33:36.000000000 +0000 @@ -1225,6 +1225,32 @@ return rv.ErrorCode(); } +already_AddRefed +nsHTMLDocument::CreateDummyChannelForCookies(nsIURI* aCodebaseURI) +{ + // The cookie service reads the privacy status of the channel we pass to it in + // order to determine which cookie database to query. In some cases we don't + // have a proper channel to hand it to the cookie service though. This + // function creates a dummy channel that is not used to load anything, for the + // sole purpose of handing it to the cookie service. DO NOT USE THIS CHANNEL + // FOR ANY OTHER PURPOSE. + MOZ_ASSERT(!mChannel); + + nsCOMPtr channel; + NS_NewChannel(getter_AddRefs(channel), aCodebaseURI, this, + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_INVALID); + nsCOMPtr pbChannel = + do_QueryInterface(channel); + nsCOMPtr docShell(mDocumentContainer); + nsCOMPtr loadContext = do_QueryInterface(docShell); + if (!pbChannel || !loadContext) { + return nullptr; + } + pbChannel->SetPrivate(loadContext->UsePrivateBrowsing()); + return channel.forget(); +} + void nsHTMLDocument::GetCookie(nsAString& aCookie, ErrorResult& rv) { @@ -1257,8 +1283,16 @@ return; } + nsCOMPtr channel(mChannel); + if (!channel) { + channel = CreateDummyChannelForCookies(codebaseURI); + if (!channel) { + return; + } + } + nsXPIDLCString cookie; - service->GetCookieString(codebaseURI, mChannel, getter_Copies(cookie)); + service->GetCookieString(codebaseURI, channel, getter_Copies(cookie)); // CopyUTF8toUTF16 doesn't handle error // because it assumes that the input is valid. nsContentUtils::ConvertStringFromEncoding(NS_LITERAL_CSTRING("UTF-8"), @@ -1302,8 +1336,16 @@ return; } + nsCOMPtr channel(mChannel); + if (!channel) { + channel = CreateDummyChannelForCookies(codebaseURI); + if (!channel) { + return; + } + } + NS_ConvertUTF16toUTF8 cookie(aCookie); - service->SetCookieString(codebaseURI, nullptr, cookie.get(), mChannel); + service->SetCookieString(codebaseURI, nullptr, cookie.get(), channel); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/nsHTMLDocument.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/nsHTMLDocument.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/nsHTMLDocument.h 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/nsHTMLDocument.h 2015-02-03 14:33:36.000000000 +0000 @@ -275,6 +275,9 @@ nsresult CreateAndAddWyciwygChannel(void); nsresult RemoveWyciwygChannel(void); + // This should *ONLY* be used in GetCookie/SetCookie. + already_AddRefed CreateDummyChannelForCookies(nsIURI* aCodebaseURI); + /** * Like IsEditingOn(), but will flush as needed first. */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/nsTextEditorState.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/nsTextEditorState.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/nsTextEditorState.cpp 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/nsTextEditorState.cpp 2015-02-03 14:33:36.000000000 +0000 @@ -1873,8 +1873,6 @@ nsTextEditorState::SetValue(const nsAString& aValue, bool aUserInput, bool aSetValueChanged) { - mozilla::fallible_t fallible; - if (mEditor && mBoundFrame) { // The InsertText call below might flush pending notifications, which // could lead into a scheduled PrepareEditor to be called. That will diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/test/browser_bug1108547.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/test/browser_bug1108547.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/test/browser_bug1108547.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/test/browser_bug1108547.js 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,109 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function test() { + waitForExplicitFinish(); + + runPass("file_bug1108547-2.html", function() { + runPass("file_bug1108547-3.html", function() { + finish(); + }); + }); +} + +function runPass(getterFile, finishedCallback) { + var rootDir = "http://mochi.test:8888/browser/dom/html/test/"; + var testBrowser; + var privateWin; + + function whenDelayedStartupFinished(win, callback) { + let topic = "browser-delayed-startup-finished"; + Services.obs.addObserver(function onStartup(aSubject) { + if (win != aSubject) + return; + + Services.obs.removeObserver(onStartup, topic); + executeSoon(callback); + }, topic, false); + } + + // First, set the cookie in a normal window. + gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug1108547-1.html"); + gBrowser.selectedBrowser.addEventListener("load", afterOpenCookieSetter, true); + + function afterOpenCookieSetter() { + gBrowser.selectedBrowser.removeEventListener("load", afterOpenCookieSetter, true); + gBrowser.removeCurrentTab(); + + // Now, open a private window. + privateWin = OpenBrowserWindow({private: true}); + whenDelayedStartupFinished(privateWin, afterPrivateWindowOpened); + } + + function afterPrivateWindowOpened() { + // In the private window, open the getter file, and wait for a new tab to be opened. + privateWin.gBrowser.selectedTab = privateWin.gBrowser.addTab(rootDir + getterFile); + testBrowser = privateWin.gBrowser.selectedBrowser; + privateWin.gBrowser.tabContainer.addEventListener("TabOpen", onNewTabOpened, true); + } + + function onNewTabOpened() { + // When the new tab is opened, wait for it to load. + privateWin.gBrowser.tabContainer.removeEventListener("TabOpen", onNewTabOpened, true); + privateWin.gBrowser.tabs[privateWin.gBrowser.tabs.length - 1].linkedBrowser.addEventListener("load", onNewTabLoaded, true); + } + + function onNewTabLoaded() { + privateWin.gBrowser.tabs[privateWin.gBrowser.tabs.length - 1].linkedBrowser.removeEventListener("load", onNewTabLoaded, true); + + // Now, ensure that the private tab doesn't have access to the cookie set in normal mode. + is(testBrowser.contentDocument.getElementById("result").textContent, "", + "Shouldn't have access to the cookies"); + + // We're done with the private window, close it. + privateWin.close(); + + // Clear all cookies. + Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager).removeAll(); + + // Open a new private window, this time to set a cookie inside it. + privateWin = OpenBrowserWindow({private: true}); + whenDelayedStartupFinished(privateWin, afterPrivateWindowOpened2); + } + + function afterPrivateWindowOpened2() { + // In the private window, open the setter file, and wait for it to load. + privateWin.gBrowser.selectedTab = privateWin.gBrowser.addTab(rootDir + "file_bug1108547-1.html"); + privateWin.gBrowser.selectedBrowser.addEventListener("load", afterOpenCookieSetter2, true); + } + + function afterOpenCookieSetter2() { + // We're done with the private window now, close it. + privateWin.close(); + + // Now try to read the cookie in a normal window, and wait for a new tab to be opened. + gBrowser.selectedTab = gBrowser.addTab(rootDir + getterFile); + testBrowser = gBrowser.selectedBrowser; + gBrowser.tabContainer.addEventListener("TabOpen", onNewTabOpened2, true); + } + + function onNewTabOpened2() { + // When the new tab is opened, wait for it to load. + gBrowser.tabContainer.removeEventListener("TabOpen", onNewTabOpened2, true); + gBrowser.tabs[gBrowser.tabs.length - 1].linkedBrowser.addEventListener("load", onNewTabLoaded2, true); + } + + function onNewTabLoaded2() { + gBrowser.tabs[gBrowser.tabs.length - 1].linkedBrowser.removeEventListener("load", onNewTabLoaded2, true); + + // Now, ensure that the normal tab doesn't have access to the cookie set in private mode. + is(testBrowser.contentDocument.getElementById("result").textContent, "", + "Shouldn't have access to the cookies"); + + // Remove both of the tabs opened here. + gBrowser.removeCurrentTab(); + gBrowser.removeCurrentTab(); + + finishedCallback(); + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/test/browser.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/test/browser.ini --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/test/browser.ini 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/test/browser.ini 2015-02-03 14:33:37.000000000 +0000 @@ -8,3 +8,8 @@ [browser_bug649778.js] skip-if = e10s # Bug ?????? - leaked until shutdown [nsGlobalWindow #16 about:blank] [browser_bug1081537.js] +[browser_bug1108547.js] +support-files = + file_bug1108547-1.html + file_bug1108547-2.html + file_bug1108547-3.html diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/test/file_bug1108547-1.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/test/file_bug1108547-1.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/test/file_bug1108547-1.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/test/file_bug1108547-1.html 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,4 @@ + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/test/file_bug1108547-2.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/test/file_bug1108547-2.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/test/file_bug1108547-2.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/test/file_bug1108547-2.html 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,6 @@ + + +
    +
    +
    not tested yet
    + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/test/file_bug1108547-3.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/test/file_bug1108547-3.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/test/file_bug1108547-3.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/test/file_bug1108547-3.html 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,5 @@ + + +test +
    not tested yet
    + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/test/forms/test_input_email.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/test/forms/test_input_email.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/test/forms/test_input_email.html 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/test/forms/test_input_email.html 2015-02-03 14:33:37.000000000 +0000 @@ -36,8 +36,8 @@ function checkValidEmailAddress(element) { gInvalid = false; - ok(!element.validity.typeMismatch, - "Element should not suffer from type mismatch (with value='"+element.value+"')"); + ok(!element.validity.typeMismatch && !element.validity.badInput, + "Element should not suffer from type mismatch or bad input (with value='"+element.value+"')"); ok(element.validity.valid, "Element should be valid"); ok(element.checkValidity(), "Element should be valid"); ok(!gInvalid, "The invalid event should not have been thrown"); @@ -46,11 +46,20 @@ ok(element.matches(":valid"), ":valid pseudo-class should apply"); } -function checkInvalidEmailAddress(element) +const VALID = 0; +const TYPE_MISMATCH = 1 << 0; +const BAD_INPUT = 1 << 1; + +function checkInvalidEmailAddress(element, failedValidityStates) { + info("Checking " + element.value); gInvalid = false; - ok(element.validity.typeMismatch, - "Element should suffer from type mismatch (with value='"+element.value+"')"); + var expectTypeMismatch = !!(failedValidityStates & TYPE_MISMATCH); + var expectBadInput = !!(failedValidityStates & BAD_INPUT); + ok(element.validity.typeMismatch == expectTypeMismatch, + "Element should " + (expectTypeMismatch ? "" : "not ") + "suffer from type mismatch (with value='"+element.value+"')"); + ok(element.validity.badInput == expectBadInput, + "Element should " + (expectBadInput ? "" : "not ") + "suffer from bad input (with value='"+element.value+"')"); ok(!element.validity.valid, "Element should not be valid"); ok(!element.checkValidity(), "Element should not be valid"); ok(gInvalid, "The invalid event should have been thrown"); @@ -59,15 +68,15 @@ ok(element.matches(":invalid"), ":invalid pseudo-class should apply"); } -function testEmailAddress(aElement, aValue, aMultiple, aValidity) +function testEmailAddress(aElement, aValue, aMultiple, aValidityFailures) { aElement.multiple = aMultiple; aElement.value = aValue; - if (aValidity) { + if (!aValidityFailures) { checkValidEmailAddress(aElement); } else { - checkInvalidEmailAddress(aElement); + checkInvalidEmailAddress(aElement, aValidityFailures); } } @@ -75,91 +84,92 @@ // Simple values, checking the e-mail syntax validity. var values = [ - [ '', true ], // The empty string shouldn't be considered as invalid. - [ 'foo@bar.com', true ], - [ ' foo@bar.com', true ], - [ 'foo@bar.com ', true ], - [ '\r\n foo@bar.com', true ], - [ 'foo@bar.com \n\r', true ], - [ '\n\n \r\rfoo@bar.com\n\n \r\r', true ], - [ '\n\r \n\rfoo@bar.com\n\r \n\r', true ], - [ 'tulip', false ], + [ '' ], // The empty string shouldn't be considered as invalid. + [ 'foo@bar.com', VALID ], + [ ' foo@bar.com', VALID ], + [ 'foo@bar.com ', VALID ], + [ '\r\n foo@bar.com', VALID ], + [ 'foo@bar.com \n\r', VALID ], + [ '\n\n \r\rfoo@bar.com\n\n \r\r', VALID ], + [ '\n\r \n\rfoo@bar.com\n\r \n\r', VALID ], + [ 'tulip', TYPE_MISMATCH ], // Some checks on the user part of the address. - [ '@bar.com', false ], - [ 'f\noo@bar.com', true ], - [ 'f\roo@bar.com', true ], - [ 'f\r\noo@bar.com', true ], - [ 'fü@foo.com', true ], + [ '@bar.com', TYPE_MISMATCH ], + [ 'f\noo@bar.com', VALID ], + [ 'f\roo@bar.com', VALID ], + [ 'f\r\noo@bar.com', VALID ], + [ 'fü@foo.com', TYPE_MISMATCH ], // Some checks for the domain part. - [ 'foo@bar', true ], - [ 'foo@b', true ], - [ 'foo@', false ], - [ 'foo@bar.', false ], - [ 'foo@foo.bar', true ], - [ 'foo@foo..bar', false ], - [ 'foo@.bar', false ], - [ 'foo@tulip.foo.bar', true ], - [ 'foo@tulip.foo-bar', true ], - [ 'foo@1.2', true ], - [ 'foo@127.0.0.1', true ], - [ 'foo@1.2.3', true ], - [ 'foo@b\nar.com', true ], - [ 'foo@b\rar.com', true ], - [ 'foo@b\r\nar.com', true ], - [ 'foo@.', false ], - [ 'foo@fü.com', true ], - [ 'foo@fu.cüm', true ], - // Long strings with UTF-8. - [ 'this.is.email.should.be.longer.than.sixty.four.characters.föö@mözillä.tld', true ], - [ 'this-is-email-should-be-longer-than-sixty-four-characters-föö@mözillä.tld', true, true ], - // Long labels. - [ 'foo@thislabelisexactly63characterssssssssssssssssssssssssssssssssss', true ], - [ 'foo@thislabelisexactly63characterssssssssssssssssssssssssssssssssss.com', true ], - [ 'foo@foo.thislabelisexactly63characterssssssssssssssssssssssssssssssssss.com', true ], - [ 'foo@foo.thislabelisexactly63characterssssssssssssssssssssssssssssssssss', true ], - [ 'foo@thislabelisexactly64charactersssssssssssssssssssssssssssssssssss', false ], - [ 'foo@thislabelisexactly64charactersssssssssssssssssssssssssssssssssss.com', false ], - [ 'foo@foo.thislabelisexactly64charactersssssssssssssssssssssssssssssssssss.com', false ], - [ 'foo@foo.thislabelisexactly64charactersssssssssssssssssssssssssssssssssss', false ], - // Long labels with UTF-8. - [ 'foo@thisläbelisexäctly63charäcterssssssssssssssssssssssssssssssssss', false ], - [ 'foo@thisläbelisexäctly63charäcterssssssssssssssssssssssssssssssssss.com', false ], - [ 'foo@foo.thisläbelisexäctly63charäcterssssssssssssssssssssssssssssssssss.com', false ], - [ 'foo@foo.thisläbelisexäctly63charäcterssssssssssssssssssssssssssssssssss', false ], + [ 'foo@bar', VALID ], + [ 'foo@b', VALID ], + [ 'foo@', TYPE_MISMATCH ], + [ 'foo@bar.', TYPE_MISMATCH ], + [ 'foo@foo.bar', VALID ], + [ 'foo@foo..bar', TYPE_MISMATCH ], + [ 'foo@.bar', TYPE_MISMATCH ], + [ 'foo@tulip.foo.bar', VALID ], + [ 'foo@tulip.foo-bar', VALID ], + [ 'foo@1.2', VALID ], + [ 'foo@127.0.0.1', VALID ], + [ 'foo@1.2.3', VALID ], + [ 'foo@b\nar.com', VALID ], + [ 'foo@b\rar.com', VALID ], + [ 'foo@b\r\nar.com', VALID ], + [ 'foo@.', TYPE_MISMATCH ], + [ 'foo@fü.com', VALID ], + [ 'foo@fu.cüm', VALID ], + [ 'thisUsernameIsLongerThanSixtyThreeCharactersInLengthRightAboutNow@mozilla.tld', VALID ], + // Long strings with UTF-8 in username. + [ 'this.is.email.should.be.longer.than.sixty.four.characters.föö@mözillä.tld', TYPE_MISMATCH ], + [ 'this-is-email-should-be-longer-than-sixty-four-characters-föö@mözillä.tld', TYPE_MISMATCH, true ], + // Long labels (labels greater than 63 chars long are not allowed). + [ 'foo@thislabelisexactly63characterssssssssssssssssssssssssssssssssss', VALID ], + [ 'foo@thislabelisexactly63characterssssssssssssssssssssssssssssssssss.com', VALID ], + [ 'foo@foo.thislabelisexactly63characterssssssssssssssssssssssssssssssssss.com', VALID ], + [ 'foo@foo.thislabelisexactly63characterssssssssssssssssssssssssssssssssss', VALID ], + [ 'foo@thislabelisexactly64charactersssssssssssssssssssssssssssssssssss', TYPE_MISMATCH | BAD_INPUT ], + [ 'foo@thislabelisexactly64charactersssssssssssssssssssssssssssssssssss.com', TYPE_MISMATCH | BAD_INPUT ], + [ 'foo@foo.thislabelisexactly64charactersssssssssssssssssssssssssssssssssss.com', TYPE_MISMATCH | BAD_INPUT ], + [ 'foo@foo.thislabelisexactly64charactersssssssssssssssssssssssssssssssssss', TYPE_MISMATCH | BAD_INPUT ], + // Long labels with UTF-8 (punycode encoding will increase the label to more than 63 chars). + [ 'foo@thisläbelisexäctly63charäcterssssssssssssssssssssssssssssssssss', TYPE_MISMATCH | BAD_INPUT ], + [ 'foo@thisläbelisexäctly63charäcterssssssssssssssssssssssssssssssssss.com', TYPE_MISMATCH | BAD_INPUT ], + [ 'foo@foo.thisläbelisexäctly63charäcterssssssssssssssssssssssssssssssssss.com', TYPE_MISMATCH | BAD_INPUT ], + [ 'foo@foo.thisläbelisexäctly63charäcterssssssssssssssssssssssssssssssssss', TYPE_MISMATCH | BAD_INPUT ], // The domains labels (sub-domains or tld) can't start or finish with a '-' - [ 'foo@foo-bar', true ], - [ 'foo@-foo', false ], - [ 'foo@foo-.bar', false ], - [ 'foo@-.-', false ], - [ 'foo@fo-o.bar', true ], - [ 'foo@fo-o.-bar', false ], - [ 'foo@fo-o.bar-', false ], - [ 'foo@fo-o.-', false ], - [ 'foo@fo--o', true ], + [ 'foo@foo-bar', VALID ], + [ 'foo@-foo', TYPE_MISMATCH ], + [ 'foo@foo-.bar', TYPE_MISMATCH ], + [ 'foo@-.-', TYPE_MISMATCH ], + [ 'foo@fo-o.bar', VALID ], + [ 'foo@fo-o.-bar', TYPE_MISMATCH ], + [ 'foo@fo-o.bar-', TYPE_MISMATCH ], + [ 'foo@fo-o.-', TYPE_MISMATCH ], + [ 'foo@fo--o', VALID ], ]; // Multiple values, we don't check e-mail validity, only multiple stuff. var multipleValues = [ - [ 'foo@bar.com, foo@bar.com', true ], - [ 'foo@bar.com,foo@bar.com', true ], - [ 'foo@bar.com,foo@bar.com,foo@bar.com', true ], - [ ' foo@bar.com , foo@bar.com ', true ], - [ '\tfoo@bar.com\t,\tfoo@bar.com\t', true ], - [ '\rfoo@bar.com\r,\rfoo@bar.com\r', true ], - [ '\nfoo@bar.com\n,\nfoo@bar.com\n', true ], - [ '\ffoo@bar.com\f,\ffoo@bar.com\f', true ], - [ '\t foo@bar.com\r,\nfoo@bar.com\f', true ], - [ 'foo@b,ar.com,foo@bar.com', false ], - [ 'foo@bar.com,foo@bar.com,', false ], - [ ' foo@bar.com , foo@bar.com , ', false ], - [ ',foo@bar.com,foo@bar.com', false ], - [ ',foo@bar.com,foo@bar.com', false ], - [ 'foo@bar.com,,,foo@bar.com', false ], - [ 'foo@bar.com;foo@bar.com', false ], - [ ', ', false ], - [ 'foo@bar, foo@bar.com', true ], - [ 'foo@bar.com, foo', false ], - [ 'foo, foo@bar.com', false ], + [ 'foo@bar.com, foo@bar.com', VALID ], + [ 'foo@bar.com,foo@bar.com', VALID ], + [ 'foo@bar.com,foo@bar.com,foo@bar.com', VALID ], + [ ' foo@bar.com , foo@bar.com ', VALID ], + [ '\tfoo@bar.com\t,\tfoo@bar.com\t', VALID ], + [ '\rfoo@bar.com\r,\rfoo@bar.com\r', VALID ], + [ '\nfoo@bar.com\n,\nfoo@bar.com\n', VALID ], + [ '\ffoo@bar.com\f,\ffoo@bar.com\f', VALID ], + [ '\t foo@bar.com\r,\nfoo@bar.com\f', VALID ], + [ 'foo@b,ar.com,foo@bar.com', TYPE_MISMATCH ], + [ 'foo@bar.com,foo@bar.com,', TYPE_MISMATCH ], + [ ' foo@bar.com , foo@bar.com , ', TYPE_MISMATCH ], + [ ',foo@bar.com,foo@bar.com', TYPE_MISMATCH ], + [ ',foo@bar.com,foo@bar.com', TYPE_MISMATCH ], + [ 'foo@bar.com,,,foo@bar.com', TYPE_MISMATCH ], + [ 'foo@bar.com;foo@bar.com', TYPE_MISMATCH ], + [ ', ', TYPE_MISMATCH ], + [ 'foo@bar, foo@bar.com', VALID ], + [ 'foo@bar.com, foo', TYPE_MISMATCH ], + [ 'foo, foo@bar.com', TYPE_MISMATCH ], ]; /* Additional username checks. */ @@ -171,15 +181,15 @@ // Add all username legal characters individually to the list. for (c of legalCharacters) { - values.push([c + "@bar.com", true]); + values.push([c + "@bar.com", VALID]); } // Add the concatenation of all legal characters too. -values.push([legalCharacters + "@bar.com", true]); +values.push([legalCharacters + "@bar.com", VALID]); // Add username illegal characters, the same way. var illegalCharacters = "()<>[]:;@\\, \t"; for (c of illegalCharacters) { - values.push([illegalCharacters + "@bar.com", false]); + values.push([illegalCharacters + "@bar.com", TYPE_MISMATCH]); } /* Additional domain checks. */ @@ -190,15 +200,15 @@ // Add domain legal characters (except '.' and '-' because they are special). for (c of legalCharacters) { - values.push(["foo@foo.bar" + c, true]); + values.push(["foo@foo.bar" + c, VALID]); } // Add the concatenation of all legal characters too. -values.push(["foo@bar." + legalCharacters, true]); +values.push(["foo@bar." + legalCharacters, VALID]); // Add domain illegal characters. illegalCharacters = "()<>[]:;@\\,!#$%&'*+/=?^_`{|}~ \t"; for (c of illegalCharacters) { - values.push(['foo@foo.ba' + c + 'r', false]); + values.push(['foo@foo.ba' + c + 'r', TYPE_MISMATCH]); } values.forEach(function([value, valid, todo]) { @@ -217,7 +227,7 @@ // Make sure setting multiple changes the value. email.multiple = false; email.value = "foo@bar.com, foo@bar.com"; -checkInvalidEmailAddress(email); +checkInvalidEmailAddress(email, TYPE_MISMATCH); email.multiple = true; checkValidEmailAddress(email); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/test/forms/test_input_number_validation.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/test/forms/test_input_number_validation.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/test/forms/test_input_number_validation.html 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/test/forms/test_input_number_validation.html 2015-02-03 14:33:37.000000000 +0000 @@ -16,6 +16,8 @@

    +
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/test/test_rowscollection.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/test/test_rowscollection.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/test/test_rowscollection.html 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/test/test_rowscollection.html 2015-02-03 14:33:38.000000000 +0000 @@ -38,9 +38,7 @@ for (var name in x) { names.push(name); } -var JS_HAS_SYMBOLS = typeof Symbol === "function"; -is(names.length, JS_HAS_SYMBOLS ? 10 : 11, - "Should have 10 enumerated names (or 11 with '@@iterator')"); +is(names.length, 10, "Should have 10 enumerated names"); is(names[0], "0", "Enum entry 1") is(names[1], "1", "Enum entry 2") is(names[2], "2", "Enum entry 3") @@ -50,12 +48,7 @@ is(names[6], "something", "Enum entry 7") is(names[7], "item", "Enum entry 8") is(names[8], "namedItem", "Enum entry 9") -if (JS_HAS_SYMBOLS) { - is(names[9], "length", "Enum entry 10"); -} else { - is(names[9], "@@iterator", "Enum entry 10"); - is(names[10], "length", "Enum entry 11"); -} +is(names[9], "length", "Enum entry 10"); names = Object.getOwnPropertyNames(x); is(names.length, 11, "Should have 11 items"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/TimeRanges.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/TimeRanges.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/TimeRanges.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/TimeRanges.cpp 2015-02-03 14:33:36.000000000 +0000 @@ -98,7 +98,7 @@ } void -TimeRanges::Normalize(double aError) +TimeRanges::Normalize(double aTolerance) { if (mRanges.Length() >= 2) { nsAutoTArray normalized; @@ -112,7 +112,7 @@ current.mEnd >= mRanges[i].mEnd) { continue; } - if (current.mEnd + aError >= mRanges[i].mStart) { + if (current.mEnd + aTolerance >= mRanges[i].mStart) { current.mEnd = mRanges[i].mEnd; } else { normalized.AppendElement(current); @@ -127,10 +127,10 @@ } void -TimeRanges::Union(const TimeRanges* aOtherRanges, double aError) +TimeRanges::Union(const TimeRanges* aOtherRanges, double aTolerance) { mRanges.AppendElements(aOtherRanges->mRanges); - Normalize(aError); + Normalize(aTolerance); } void @@ -156,10 +156,10 @@ } TimeRanges::index_type -TimeRanges::Find(double aTime, double aError /* = 0 */) +TimeRanges::Find(double aTime, double aTolerance /* = 0 */) { for (index_type i = 0; i < mRanges.Length(); ++i) { - if (aTime < mRanges[i].mEnd && (aTime + aError) >= mRanges[i].mStart) { + if (aTime < mRanges[i].mEnd && (aTime + aTolerance) >= mRanges[i].mStart) { return i; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/TimeRanges.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/TimeRanges.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/html/TimeRanges.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/html/TimeRanges.h 2015-02-03 14:33:36.000000000 +0000 @@ -42,10 +42,10 @@ double GetEndTime(); // See http://www.whatwg.org/html/#normalized-timeranges-object - void Normalize(double aError = 0.0); + void Normalize(double aTolerance = 0.0); // Mutate this TimeRange to be the union of this and aOtherRanges. - void Union(const TimeRanges* aOtherRanges, double aError); + void Union(const TimeRanges* aOtherRanges, double aTolerance); // Mutate this TimeRange to be the intersection of this and aOtherRanges. void Intersection(const TimeRanges* aOtherRanges); @@ -91,7 +91,7 @@ typedef nsTArray::index_type index_type; static const index_type NoIndex = index_type(-1); - index_type Find(double aTime, double aError = 0); + index_type Find(double aTime, double aTolerance = 0); bool Contains(double aStart, double aEnd) { index_type target = Find(aStart); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/gonk/StkProactiveCmdFactory.jsm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/gonk/StkProactiveCmdFactory.jsm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/gonk/StkProactiveCmdFactory.jsm 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/gonk/StkProactiveCmdFactory.jsm 2015-02-03 14:33:38.000000000 +0000 @@ -312,7 +312,7 @@ } StkSetupEventListMessage.prototype = Object.create(StkCommandMessage.prototype); -function StkMenuCmd(aCommandDetails) { +function StkSetUpMenuCmd(aCommandDetails) { // Call |StkProactiveCommand| constructor. StkProactiveCommand.call(this, aCommandDetails); @@ -340,10 +340,10 @@ this.isHelpAvailable = !!(options.isHelpAvailable); } -StkMenuCmd.prototype = Object.create(StkProactiveCommand.prototype, { +StkSetUpMenuCmd.prototype = Object.create(StkProactiveCommand.prototype, { QueryInterface: { value: XPCOMUtils.generateQI([Ci.nsIStkProactiveCmd, - Ci.nsIStkMenuCmd]) + Ci.nsIStkSetUpMenuCmd]) }, // Cache items for getItems() @@ -352,7 +352,7 @@ // Cache items for getNextActionList() nextActionList: { value: null, writable: true }, - // nsIStkMenuCmd + // nsIStkSetUpMenuCmd title: { value: null, writable: true }, getItems: { @@ -393,12 +393,12 @@ isHelpAvailable: { value: false, writable: true } }); -function StkMenuMessage(aStkMenuCmd) { +function StkSetUpMenuMessage(aStkSetUpMenuCmd) { // Call |StkCommandMessage| constructor. - StkCommandMessage.call(this, aStkMenuCmd); + StkCommandMessage.call(this, aStkSetUpMenuCmd); this.options = { - items: aStkMenuCmd.getItems().map(function(aStkItem) { + items: aStkSetUpMenuCmd.getItems().map(function(aStkItem) { if (!aStkItem) { return null; } @@ -414,70 +414,50 @@ return item; }), - isHelpAvailable: aStkMenuCmd.isHelpAvailable + isHelpAvailable: aStkSetUpMenuCmd.isHelpAvailable }; - if (aStkMenuCmd.title) { - this.options.title = aStkMenuCmd.title; + if (aStkSetUpMenuCmd.title) { + this.options.title = aStkSetUpMenuCmd.title; } - let nextActionList = aStkMenuCmd.getNextActionList(); + let nextActionList = aStkSetUpMenuCmd.getNextActionList(); if (nextActionList && nextActionList.length > 0) { this.options.nextActionList = nextActionList; } - if (aStkMenuCmd.iconInfo) { - appendIconInfo(this.options, aStkMenuCmd.iconInfo); + if (aStkSetUpMenuCmd.iconInfo) { + appendIconInfo(this.options, aStkSetUpMenuCmd.iconInfo); } } -StkMenuMessage.prototype = Object.create(StkCommandMessage.prototype); +StkSetUpMenuMessage.prototype = Object.create(StkCommandMessage.prototype); -function StkSetUpMenuCmd(aCommandDetails) { - // Call |StkMenuCmd| constructor. - StkMenuCmd.call(this, aCommandDetails); +function StkSelectItemCmd(aCommandDetails) { + // Call |StkSetUpMenuCmd| constructor. + StkSetUpMenuCmd.call(this, aCommandDetails); let options = aCommandDetails.options; this.presentationType = options.presentationType; -} -StkSetUpMenuCmd.prototype = Object.create(StkMenuCmd.prototype, { - QueryInterface: { - value: XPCOMUtils.generateQI([Ci.nsIStkProactiveCmd, - Ci.nsIStkMenuCmd, - Ci.nsIStkSetUpMenuCmd]) - }, - - // nsIStkSetUpMenuCmd - presentationType: { value: 0, writable: true } -}); - -function StkSetUpMenuMessage(aStkSetUpMenuCmd) { - // Call |StkMenuMessage| constructor. - StkMenuMessage.call(this, aStkSetUpMenuCmd); - - this.options.presentationType = aStkSetUpMenuCmd.presentationType; -} -StkSetUpMenuMessage.prototype = Object.create(StkMenuMessage.prototype); - -function StkSelectItemCmd(aCommandDetails) { - // Call |StkMenuCmd| constructor. - StkMenuCmd.call(this, aCommandDetails); - - let options = aCommandDetails.options; if (options.defaultItem !== undefined && options.defaultItem !== null) { this.defaultItem = options.defaultItem; } } -StkSelectItemCmd.prototype = Object.create(StkMenuCmd.prototype, { +StkSelectItemCmd.prototype = Object.create(StkSetUpMenuCmd.prototype, { QueryInterface: { value: XPCOMUtils.generateQI([Ci.nsIStkProactiveCmd, - Ci.nsIStkMenuCmd, + Ci.nsIStkSetUpMenuCmd, Ci.nsIStkSelectItemCmd]) }, // nsIStkSelectItemCmd + presentationType: { + value: 0, + writable: true + }, + defaultItem: { value: Ci.nsIStkSelectItemCmd.DEFAULT_ITEM_INVALID, writable: true @@ -485,14 +465,16 @@ }); function StkSelectItemMessage(aStkSelectItemCmd) { - // Call |StkMenuMessage| constructor. - StkMenuMessage.call(this, aStkSelectItemCmd); + // Call |StkSetUpMenuMessage| constructor. + StkSetUpMenuMessage.call(this, aStkSelectItemCmd); + + this.options.presentationType = aStkSelectItemCmd.presentationType; if (aStkSelectItemCmd.defaultItem !== Ci.nsIStkSelectItemCmd.DEFAULT_ITEM_INVALID) { this.options.defaultItem = aStkSelectItemCmd.defaultItem; } } -StkSelectItemMessage.prototype = Object.create(StkMenuMessage.prototype); +StkSelectItemMessage.prototype = Object.create(StkSetUpMenuMessage.prototype); function StkTextMessageCmd(aCommandDetails) { // Call |StkProactiveCommand| constructor. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/interfaces/nsIIccMessenger.idl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/interfaces/nsIIccMessenger.idl --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/interfaces/nsIIccMessenger.idl 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/interfaces/nsIIccMessenger.idl 2015-02-03 14:33:38.000000000 +0000 @@ -240,10 +240,10 @@ }; /** - * The base interface of nsIStkSetUpMenuCmd and nsIStkSelectItemCmd. + * This interface is to be applied by STK_CMD_SET_UP_MENU. */ -[scriptable, uuid(298bf414-6a1f-11e4-9045-3b3fd28e7f6a)] -interface nsIStkMenuCmd : nsIStkProactiveCmd +[scriptable, uuid(d7a66664-a602-11e4-9cc7-c7ce5fdade7d)] +interface nsIStkSetUpMenuCmd : nsIStkProactiveCmd { /** * (Optional for STK_CMD_SELECT_ITEM) @@ -304,29 +304,22 @@ }; /** - * This interface is to be applied by STK_CMD_SET_UP_MENU. + * This interface is to be applied by STK_CMD_SELECT_ITEM. */ -[scriptable, uuid(d3ddb1f0-631c-11e4-aac3-4beb03196b7b)] -interface nsIStkSetUpMenuCmd : nsIStkMenuCmd +[scriptable, uuid(eb71f0fa-a602-11e4-926f-a3814461d218)] +interface nsIStkSelectItemCmd : nsIStkSetUpMenuCmd { /** * Presentation type, one of PRESENTATION_TYPE_*. * * @See TS 11.14, clause 12.6, Command Qualifier: Select Item */ - const short PRESENTATION_TYPE_NOT_SPECIFIED = 0x00; - const short PRESENTATION_TYPE_DATA_VALUES = 0x01; - const short PRESENTATION_TYPE_NAVIGATION_OPTIONS = 0x03; + const unsigned short PRESENTATION_TYPE_NOT_SPECIFIED = 0x00; + const unsigned short PRESENTATION_TYPE_DATA_VALUES = 0x01; + const unsigned short PRESENTATION_TYPE_NAVIGATION_OPTIONS = 0x03; readonly attribute unsigned short presentationType; -}; -/** - * This interface is to be applied by STK_CMD_SELECT_ITEM. - */ -[scriptable, uuid(816dc186-631b-11e4-864f-b37f7b54a1b8)] -interface nsIStkSelectItemCmd : nsIStkMenuCmd -{ const unsigned short DEFAULT_ITEM_INVALID = 0xFFFF; /** diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/head.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/head.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/head.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/head.js 2015-02-03 14:33:38.000000000 +0000 @@ -10,14 +10,86 @@ // The puk code hard coded in emulator is "12345678". const DEFAULT_PUK = "12345678"; -// Emulate Promise.jsm semantics. -Promise.defer = function() { return new Deferred(); } -function Deferred() { - this.promise = new Promise(function(resolve, reject) { - this.resolve = resolve; - this.reject = reject; - }.bind(this)); - Object.freeze(this); +const WHT = 0xFFFFFFFF; +const BLK = 0x000000FF; +const RED = 0xFF0000FF; +const GRN = 0x00FF00FF; +const BLU = 0x0000FFFF; +const TSP = 0; + +// Basic Image, see record number 1 in EFimg. +const BASIC_ICON = { + width: 8, + height: 8, + codingScheme: "basic", + pixels: [WHT, WHT, WHT, WHT, WHT, WHT, WHT, WHT, + BLK, BLK, BLK, BLK, BLK, BLK, WHT, WHT, + WHT, BLK, WHT, BLK, BLK, WHT, BLK, WHT, + WHT, BLK, BLK, WHT, WHT, BLK, BLK, WHT, + WHT, BLK, BLK, WHT, WHT, BLK, BLK, WHT, + WHT, BLK, WHT, BLK, BLK, WHT, BLK, WHT, + WHT, WHT, BLK, BLK, BLK, BLK, WHT, WHT, + WHT, WHT, WHT, WHT, WHT, WHT, WHT, WHT] +}; +// Color Image, see record number 3 in EFimg. +const COLOR_ICON = { + width: 8, + height: 8, + codingScheme: "color", + pixels: [BLU, BLU, BLU, BLU, BLU, BLU, BLU, BLU, + BLU, RED, RED, RED, RED, RED, RED, BLU, + BLU, RED, GRN, GRN, GRN, RED, RED, BLU, + BLU, RED, RED, GRN, GRN, RED, RED, BLU, + BLU, RED, RED, GRN, GRN, RED, RED, BLU, + BLU, RED, RED, GRN, GRN, GRN, RED, BLU, + BLU, RED, RED, RED, RED, RED, RED, BLU, + BLU, BLU, BLU, BLU, BLU, BLU, BLU, BLU] +}; +// Color Image with Transparency, see record number 5 in EFimg. +const COLOR_TRANSPARENCY_ICON = { + width: 8, + height: 8, + codingScheme: "color-transparency", + pixels: [TSP, TSP, TSP, TSP, TSP, TSP, TSP, TSP, + TSP, RED, RED, RED, RED, RED, RED, TSP, + TSP, RED, GRN, GRN, GRN, RED, RED, TSP, + TSP, RED, RED, GRN, GRN, RED, RED, TSP, + TSP, RED, RED, GRN, GRN, RED, RED, TSP, + TSP, RED, RED, GRN, GRN, GRN, RED, TSP, + TSP, RED, RED, RED, RED, RED, RED, TSP, + TSP, TSP, TSP, TSP, TSP, TSP, TSP, TSP] +}; + +/** + * Helper function for checking stk icon. + */ +function isIcons(aIcons, aExpectedIcons) { + is(aIcons.length, aExpectedIcons.length, "icons.length"); + for (let i = 0; i < aIcons.length; i++) { + let icon = aIcons[i]; + let expectedIcon = aExpectedIcons[i]; + + is(icon.width, expectedIcon.width, "icon.width"); + is(icon.height, expectedIcon.height, "icon.height"); + is(icon.codingScheme, expectedIcon.codingScheme, "icon.codingScheme"); + + is(icon.pixels.length, expectedIcon.pixels.length); + for (let j = 0; j < icon.pixels.length; j++) { + is(icon.pixels[j], expectedIcon.pixels[j], "icon.pixels[" + j + "]"); + } + } +} + +/** + * Helper function for checking stk text. + */ +function isStkText(aStkText, aExpectedStkText) { + is(aStkText.text, aExpectedStkText.text, "stkText.text"); + if (aExpectedStkText.icons) { + is(aStkText.iconSelfExplanatory, aExpectedStkText.iconSelfExplanatory, + "stkText.iconSelfExplanatory"); + isIcons(aStkText.icons, aExpectedStkText.icons); + } } let _pendingEmulatorCmdCount = 0; @@ -40,22 +112,35 @@ * @return A deferred promise. */ function runEmulatorCmdSafe(aCommand) { - let deferred = Promise.defer(); - - ++_pendingEmulatorCmdCount; - runEmulatorCmd(aCommand, function(aResult) { - --_pendingEmulatorCmdCount; - - ok(true, "Emulator response: " + JSON.stringify(aResult)); - if (Array.isArray(aResult) && - aResult[aResult.length - 1] === "OK") { - deferred.resolve(aResult); - } else { - deferred.reject(aResult); - } + return new Promise(function(aResolve, aReject) { + ++_pendingEmulatorCmdCount; + runEmulatorCmd(aCommand, function(aResult) { + --_pendingEmulatorCmdCount; + + ok(true, "Emulator response: " + JSON.stringify(aResult)); + if (Array.isArray(aResult) && + aResult[aResult.length - 1] === "OK") { + aResolve(aResult); + } else { + aReject(aResult); + } + }); }); +} - return deferred.promise; +/** + * Send stk proactive pdu. + * + * Fulfill params: (none) + * Reject params: (none) + * + * @param aPdu + * + * @return A deferred promise. + */ +function sendEmulatorStkPdu(aPdu) { + let cmd = "stk pdu " + aPdu; + return runEmulatorCmdSafe(cmd); } let workingFrame; @@ -78,46 +163,44 @@ * @return A deferred promise. */ function ensureIccManager(aAdditionalPermissions) { - let deferred = Promise.defer(); + return new Promise(function(aResolve, aReject) { + aAdditionalPermissions = aAdditionalPermissions || []; - aAdditionalPermissions = aAdditionalPermissions || []; - - if (aAdditionalPermissions.indexOf("mobileconnection") < 0) { - aAdditionalPermissions.push("mobileconnection"); - } - let permissions = []; - for (let perm of aAdditionalPermissions) { - permissions.push({ "type": perm, "allow": 1, "context": document }); - } - - SpecialPowers.pushPermissions(permissions, function() { - ok(true, "permissions pushed: " + JSON.stringify(permissions)); + if (aAdditionalPermissions.indexOf("mobileconnection") < 0) { + aAdditionalPermissions.push("mobileconnection"); + } + let permissions = []; + for (let perm of aAdditionalPermissions) { + permissions.push({ "type": perm, "allow": 1, "context": document }); + } - // Permission changes can't change existing Navigator.prototype - // objects, so grab our objects from a new Navigator. - workingFrame = document.createElement("iframe"); - workingFrame.addEventListener("load", function load() { - workingFrame.removeEventListener("load", load); + SpecialPowers.pushPermissions(permissions, function() { + ok(true, "permissions pushed: " + JSON.stringify(permissions)); - iccManager = workingFrame.contentWindow.navigator.mozIccManager; + // Permission changes can't change existing Navigator.prototype + // objects, so grab our objects from a new Navigator. + workingFrame = document.createElement("iframe"); + workingFrame.addEventListener("load", function load() { + workingFrame.removeEventListener("load", load); + + iccManager = workingFrame.contentWindow.navigator.mozIccManager; + + if (iccManager) { + ok(true, "navigator.mozIccManager is instance of " + iccManager.constructor); + } else { + ok(true, "navigator.mozIccManager is undefined"); + } + + if (iccManager instanceof MozIccManager) { + aResolve(iccManager); + } else { + aReject(); + } + }); - if (iccManager) { - ok(true, "navigator.mozIccManager is instance of " + iccManager.constructor); - } else { - ok(true, "navigator.mozIccManager is undefined"); - } - - if (iccManager instanceof MozIccManager) { - deferred.resolve(iccManager); - } else { - deferred.reject(); - } + document.body.appendChild(workingFrame); }); - - document.body.appendChild(workingFrame); }); - - return deferred.promise; } /** @@ -193,17 +276,15 @@ * @return A deferred promise. */ function waitForTargetEvent(aEventTarget, aEventName, aMatchFun) { - let deferred = Promise.defer(); - - aEventTarget.addEventListener(aEventName, function onevent(aEvent) { - if (!aMatchFun || aMatchFun(aEvent)) { - aEventTarget.removeEventListener(aEventName, onevent); - ok(true, "Event '" + aEventName + "' got."); - deferred.resolve(aEvent); - } + return new Promise(function(aResolve, aReject) { + aEventTarget.addEventListener(aEventName, function onevent(aEvent) { + if (!aMatchFun || aMatchFun(aEvent)) { + aEventTarget.removeEventListener(aEventName, onevent); + ok(true, "Event '" + aEventName + "' got."); + aResolve(aEvent); + } + }); }); - - return deferred.promise; } /** diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/icc_header.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/icc_header.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/icc_header.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/icc_header.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,69 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers; - -SpecialPowers.addPermission("mobileconnection", true, document); - -let iccManager = navigator.mozIccManager; -ok(iccManager instanceof MozIccManager, - "iccManager is instanceof " + iccManager.constructor); - -// TODO: Bug 932650 - B2G RIL: WebIccManager API - add marionette tests for -// multi-sim -// In single sim scenario, there is only one sim card, we can use below way to -// check iccId and get icc object. But in multi-sim, the index of iccIds may -// not map to sim slot directly, we should have a better way to handle this. -let iccIds = iccManager.iccIds; -ok(Array.isArray(iccIds), "iccIds is an array"); -ok(iccIds.length > 0, "iccIds.length is " + iccIds.length); - -let iccId = iccIds[0]; -is(iccId, "89014103211118510720", "iccId is " + iccId); - -let icc = iccManager.getIccById(iccId); -ok(icc instanceof MozIcc, "icc is instanceof " + icc.constructor); - -/* Remove permission and execute finish() */ -let cleanUp = function() { - SpecialPowers.removePermission("mobileconnection", document); - finish(); -}; - -/* Helper for tasks */ -let taskHelper = { - tasks: [], - - push: function(task) { - this.tasks.push(task); - }, - - runNext: function() { - let task = this.tasks.shift(); - if (!task) { - cleanUp(); - return; - } - - if (typeof task === "function") { - task(); - } - }, -}; - -/* Helper for emulator console command */ -let emulatorHelper = { - pendingCommandCount: 0, - - sendCommand: function(cmd, callback) { - this.pendingCommandCount++; - runEmulatorCmd(cmd, function(result) { - this.pendingCommandCount--; - is(result[result.length - 1], "OK"); - - if (callback && typeof callback === "function") { - callback(result); - } - }); - }, -}; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/manifest.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/manifest.ini --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/manifest.ini 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/manifest.ini 2015-02-03 14:33:38.000000000 +0000 @@ -3,7 +3,6 @@ browser = false qemu = true -[test_stk_proactive_command.js] [test_icc_contact.js] [test_icc_card_lock_get_retry_count.js] [test_icc_card_lock_change_pin.js] @@ -28,6 +27,8 @@ [test_stk_setup_menu.js] [test_stk_setup_idle_mode_text.js] [test_stk_bip_command.js] +[test_stk_local_info.js] +[test_stk_timer_management.js] [test_icc_access_invalid_object.js] [test_icc_detected_undetected_event.js] [test_icc_match_mvno.js] diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/stk_helper.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/stk_helper.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/stk_helper.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/stk_helper.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,140 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -MARIONETTE_TIMEOUT = 30000; - -SpecialPowers.addPermission("mobileconnection", true, document); - -const WHT = 0xFFFFFFFF; -const BLK = 0x000000FF; -const RED = 0xFF0000FF; -const GRN = 0x00FF00FF; -const BLU = 0x0000FFFF; -const TSP = 0; - -let iccManager = navigator.mozIccManager; -ok(iccManager instanceof MozIccManager, - "iccManager is instanceof " + iccManager.constructor); - -// TODO: Bug 932650 - B2G RIL: WebIccManager API - add marionette tests for -// multi-sim -// In single sim scenario, there is only one sim card, we can use below way to -// check iccId and get icc object. But in multi-sim, the index of iccIds may -// not map to sim slot directly, we should have a better way to handle this. -let iccIds = iccManager.iccIds; -ok(Array.isArray(iccIds), "iccIds is an array"); -ok(iccIds.length > 0, "iccIds.length is " + iccIds.length); - -let iccId = iccIds[0]; -is(iccId, "89014103211118510720", "iccId is " + iccId); - -let icc = iccManager.getIccById(iccId); -ok(icc instanceof MozIcc, "icc is instanceof " + icc.constructor); - - -// Basic Image, see record number 1 in EFimg. -let basicIcon = { - width: 8, - height: 8, - codingScheme: "basic", - pixels: [WHT, WHT, WHT, WHT, WHT, WHT, WHT, WHT, - BLK, BLK, BLK, BLK, BLK, BLK, WHT, WHT, - WHT, BLK, WHT, BLK, BLK, WHT, BLK, WHT, - WHT, BLK, BLK, WHT, WHT, BLK, BLK, WHT, - WHT, BLK, BLK, WHT, WHT, BLK, BLK, WHT, - WHT, BLK, WHT, BLK, BLK, WHT, BLK, WHT, - WHT, WHT, BLK, BLK, BLK, BLK, WHT, WHT, - WHT, WHT, WHT, WHT, WHT, WHT, WHT, WHT] -}; -// Color Image, see record number 3 in EFimg. -let colorIcon = { - width: 8, - height: 8, - codingScheme: "color", - pixels: [BLU, BLU, BLU, BLU, BLU, BLU, BLU, BLU, - BLU, RED, RED, RED, RED, RED, RED, BLU, - BLU, RED, GRN, GRN, GRN, RED, RED, BLU, - BLU, RED, RED, GRN, GRN, RED, RED, BLU, - BLU, RED, RED, GRN, GRN, RED, RED, BLU, - BLU, RED, RED, GRN, GRN, GRN, RED, BLU, - BLU, RED, RED, RED, RED, RED, RED, BLU, - BLU, BLU, BLU, BLU, BLU, BLU, BLU, BLU] -}; -// Color Image with Transparency, see record number 5 in EFimg. -let colorTransparencyIcon = { - width: 8, - height: 8, - codingScheme: "color-transparency", - pixels: [TSP, TSP, TSP, TSP, TSP, TSP, TSP, TSP, - TSP, RED, RED, RED, RED, RED, RED, TSP, - TSP, RED, GRN, GRN, GRN, RED, RED, TSP, - TSP, RED, RED, GRN, GRN, RED, RED, TSP, - TSP, RED, RED, GRN, GRN, RED, RED, TSP, - TSP, RED, RED, GRN, GRN, GRN, RED, TSP, - TSP, RED, RED, RED, RED, RED, RED, TSP, - TSP, TSP, TSP, TSP, TSP, TSP, TSP, TSP] -}; - -function isIcons(icons, expectedIcons, message) { - is(icons.length, expectedIcons.length, message); - for (let i = 0; i < icons.length; i++) { - let icon = icons[i]; - let expectedIcon = expectedIcons[i]; - - is(icon.width, expectedIcon.width, message); - is(icon.height, expectedIcon.height, message); - is(icon.codingScheme, expectedIcon.codingScheme, message); - - is(icon.pixels.length, expectedIcon.pixels.length); - for (let j = 0; j < icon.pixels.length; j++) { - is(icon.pixels[j], expectedIcon.pixels[j], message); - } - } -} - -function isStkText(stkText, expectedStkText, message) { - is(stkText.text, expectedStkText.text, message); - if (expectedStkText.icons) { - is(stkText.iconSelfExplanatory, expectedStkText.iconSelfExplanatory, message); - isIcons(stkText.icons, expectedStkText.icons, message); - } -} - -let pendingEmulatorCmdCount = 0; -function sendStkPduToEmulator(command, func, expect) { - ++pendingEmulatorCmdCount; - - runEmulatorCmd(command, function(result) { - --pendingEmulatorCmdCount; - is(result[0], "OK"); - }); - - icc.onstkcommand = function(evt) { - if (expect) { - func(evt.command, expect); - } else { - func(evt.command); - } - } -} - -function runNextTest() { - let test = tests.pop(); - if (!test) { - cleanUp(); - return; - } - - let command = "stk pdu " + test.command; - sendStkPduToEmulator(command, test.func, test.expect); -} - -function cleanUp() { - if (pendingEmulatorCmdCount) { - window.setTimeout(cleanUp, 100); - return; - } - - SpecialPowers.removePermission("mobileconnection", document); - finish(); -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_icc_access_invalid_object.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_icc_access_invalid_object.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_icc_access_invalid_object.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_icc_access_invalid_object.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,114 +1,101 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ MARIONETTE_TIMEOUT = 30000; -MARIONETTE_HEAD_JS = "icc_header.js"; +MARIONETTE_HEAD_JS = "head.js"; -function setRadioEnabled(enabled) { - let connection = navigator.mozMobileConnections[0]; - ok(connection); - - let request = connection.setRadioEnabled(enabled); - - request.onsuccess = function onsuccess() { - log('setRadioEnabled: ' + enabled); - }; - - request.onerror = function onerror() { - ok(false, "setRadioEnabled should be ok"); - }; +function testInvalidIccObject(aIcc) { + // Test access iccInfo. + try { + is(aIcc.iccInfo, null, "iccInfo: expect to get null"); + } catch(e) { + ok(false, "access iccInfo should not get exception"); + } + + // Test access cardState. + try { + is(aIcc.cardState, null, "cardState: expect to get null"); + } catch(e) { + ok(false, "access cardState should not get exception"); + } + + // Test STK related function. + try { + aIcc.sendStkResponse({}, {}); + ok(false, "sendStkResponse() should get exception"); + } catch(e) {} + try { + aIcc.sendStkMenuSelection(0, false); + ok(false, "sendStkMenuSelection() should get exception"); + } catch(e) {} + try { + aIcc.sendStkTimerExpiration({}); + ok(false, "sendStkTimerExpiration() should get exception"); + } catch(e) {} + try { + aIcc.sendStkEventDownload({}); + ok(false, "sendStkEventDownload() should get exception"); + } catch(e) {} + + // Test card lock related function. + try { + aIcc.getCardLock("pin"); + ok(false, "getCardLock() should get exception"); + } catch(e) {} + try { + aIcc.unlockCardLock({}); + ok(false, "unlockCardLock() should get exception"); + } catch(e) {} + try { + aIcc.setCardLock({}); + ok(false, "setCardLock() should get exception"); + } catch(e) {} + try { + aIcc.getCardLockRetryCount("pin"); + ok(false, "getCardLockRetryCount() should get exception"); + } catch(e) {} + + // Test contact related function. + try { + aIcc.readContacts("adn"); + ok(false, "readContacts() should get exception"); + } catch(e) {} + try { + aIcc.updateContact("adn", {}); + ok(false, "updateContact() should get exception"); + } catch(e) {} + + // Test mvno function. + try { + aIcc.matchMvno("imsi"); + ok(false, "matchMvno() should get exception"); + } catch(e) {} + + // Test service state function. + return aIcc.getServiceState("fdn").then(() => { + ok(false, "getServiceState() should be rejected"); + }, () => {}); } -/* Test access invalid icc object */ -taskHelper.push(function testAccessRemovedIccObject() { - setRadioEnabled(false); - iccManager.addEventListener("iccundetected", function oniccundetected(evt) { - log("got icc undetected event"); - iccManager.removeEventListener("iccundetected", oniccundetected); - is(evt.iccId, iccId, "icc " + evt.iccId + " becomes undetected"); - - // Test access iccInfo. - try { - is(icc.iccInfo, null, "iccInfo: expect to get null"); - } catch(e) { - ok(false, "access iccInfo should not get exception"); - } - - // Test access cardState. - try { - is(icc.cardState, null, "cardState: expect to get null"); - } catch(e) { - ok(false, "access cardState should not get exception"); - } - - // Test STK related function. - try { - icc.sendStkResponse({}, {}); - ok(false, "sendStkResponse() should get exception"); - } catch(e) {} - try { - icc.sendStkMenuSelection(0, false); - ok(false, "sendStkMenuSelection() should get exception"); - } catch(e) {} - try { - icc.sendStkTimerExpiration({}); - ok(false, "sendStkTimerExpiration() should get exception"); - } catch(e) {} - try { - icc.sendStkEventDownload({}); - ok(false, "sendStkEventDownload() should get exception"); - } catch(e) {} - - // Test card lock related function. - try { - icc.getCardLock(""); - ok(false, "getCardLock() should get exception"); - } catch(e) {} - try { - icc.unlockCardLock({}); - ok(false, "unlockCardLock() should get exception"); - } catch(e) {} - try { - icc.setCardLock({}); - ok(false, "setCardLock() should get exception"); - } catch(e) {} - try { - icc.getCardLockRetryCount(""); - ok(false, "getCardLockRetryCount() should get exception"); - } catch(e) {} - - // Test contact related function. - try { - icc.readContacts(""); - ok(false, "readContacts() should get exception"); - } catch(e) {} - try { - icc.updateContact("", {}); - ok(false, "updateContact() should get exception"); - } catch(e) {} - - // Test secure element related function. - try { - icc.iccOpenChannel(""); - ok(false, "iccOpenChannel() should get exception"); - } catch(e) {} - try { - icc.iccExchangeAPDU(0, {}); - ok(false, "iccExchangeAPDU() should get exception"); - } catch(e) {} - try { - icc.iccCloseChannel(0); - ok(false, "iccCloseChannel() should get exception"); - } catch(e) {} - +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + + return Promise.resolve() + // Turn off radio. + .then(() => { + let promises = []; + promises.push(setRadioEnabled(false)); + promises.push(waitForTargetEvent(iccManager, "iccundetected")); + return Promise.all(promises); + }) + // Test accessing invalid icc object. + .then(() => testInvalidIccObject(icc)) // We should restore the radio status. - setRadioEnabled(true); - iccManager.addEventListener("iccdetected", function oniccdetected(evt) { - iccManager.removeEventListener("iccdetected", oniccdetected); - taskHelper.runNext(); + .then(() => { + let promises = []; + promises.push(setRadioEnabled(true)); + promises.push(waitForTargetEvent(iccManager, "iccdetected")); + return Promise.all(promises); }); - }); }); - -// Start test -taskHelper.runNext(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_icc_card_state.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_icc_card_state.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_icc_card_state.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_icc_card_state.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,49 +1,32 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ MARIONETTE_TIMEOUT = 30000; -MARIONETTE_HEAD_JS = "icc_header.js"; +MARIONETTE_HEAD_JS = "head.js"; -function setRadioEnabled(enabled) { - let connection = navigator.mozMobileConnections[0]; - ok(connection); +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); - let request = connection.setRadioEnabled(enabled); - - request.onsuccess = function onsuccess() { - log('setRadioEnabled: ' + enabled); - }; - - request.onerror = function onerror() { - ok(false, "setRadioEnabled should be ok"); - }; -} - -/* Basic test */ -taskHelper.push(function basicTest() { + // Basic test. is(icc.cardState, "ready", "card state is " + icc.cardState); - taskHelper.runNext(); -}); -/* Test cardstatechange event by switching radio off */ -taskHelper.push(function testCardStateChange() { - // Turn off radio. - setRadioEnabled(false); - icc.addEventListener("cardstatechange", function oncardstatechange() { - log("card state changes to " + icc.cardState); - // Expect to get card state changing to null. - if (icc.cardState === null) { - icc.removeEventListener("cardstatechange", oncardstatechange); - // We should restore radio status and expect to get iccdetected event. - setRadioEnabled(true); - iccManager.addEventListener("iccdetected", function oniccdetected(evt) { - log("icc iccdetected: " + evt.iccId); - iccManager.removeEventListener("iccdetected", oniccdetected); - taskHelper.runNext(); - }); - } - }); + // Test cardstatechange event by switching radio off. + return Promise.resolve() + // Turn off radio and expect to get card state changing to null. + .then(() => { + let promises = []; + promises.push(setRadioEnabled(false)); + promises.push(waitForTargetEvent(icc, "cardstatechange", function() { + return icc.cardState === null; + })); + return Promise.all(promises); + }) + // Restore radio status and expect to get iccdetected event. + .then(() => { + let promises = []; + promises.push(setRadioEnabled(true)); + promises.push(waitForTargetEvent(iccManager, "iccdetected")); + return Promise.all(promises); + }); }); - -// Start test -taskHelper.runNext(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_icc_contact.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_icc_contact.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_icc_contact.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_icc_contact.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,120 +1,81 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ MARIONETTE_TIMEOUT = 60000; -MARIONETTE_HEAD_JS = "icc_header.js"; +MARIONETTE_HEAD_JS = "head.js"; -const EMULATOR_ICCID = "89014103211118510720"; - -function testReadContacts(type) { - let request = icc.readContacts(type); - request.onsuccess = function onsuccess() { - let contacts = request.result; - - is(Array.isArray(contacts), true); - - is(contacts[0].name[0], "Mozilla"); - is(contacts[0].tel[0].value, "15555218201"); - is(contacts[0].id, EMULATOR_ICCID + "1"); - - is(contacts[1].name[0], "Saßê黃"); - is(contacts[1].tel[0].value, "15555218202"); - is(contacts[1].id, EMULATOR_ICCID + "2"); - - is(contacts[2].name[0], "Fire 火"); - is(contacts[2].tel[0].value, "15555218203"); - is(contacts[2].id, EMULATOR_ICCID + "3"); - - is(contacts[3].name[0], "Huang 黃"); - is(contacts[3].tel[0].value, "15555218204"); - is(contacts[3].id, EMULATOR_ICCID + "4"); - - taskHelper.runNext(); - }; - - request.onerror = function onerror() { - ok(false, "Cannot get " + type + " contacts"); - taskHelper.runNext(); - }; +function testReadContacts(aIcc, aType) { + log("testReadContacts: type=" + aType); + let iccId = aIcc.iccInfo.iccid; + return aIcc.readContacts(aType) + .then((aResult) => { + is(Array.isArray(aResult), true); + + is(aResult[0].name[0], "Mozilla"); + is(aResult[0].tel[0].value, "15555218201"); + is(aResult[0].id, iccId + "1"); + + is(aResult[1].name[0], "Saßê黃"); + is(aResult[1].tel[0].value, "15555218202"); + is(aResult[1].id, iccId + "2"); + + is(aResult[2].name[0], "Fire 火"); + is(aResult[2].tel[0].value, "15555218203"); + is(aResult[2].id, iccId + "3"); + + is(aResult[3].name[0], "Huang 黃"); + is(aResult[3].tel[0].value, "15555218204"); + is(aResult[3].id, iccId + "4"); + }, (aError) => { + ok(false, "Cannot get " + aType + " contacts"); + }); } -function testAddContact(type, pin2) { +function testAddContact(aIcc, aType, aPin2) { + log("testAddContact: type=" + aType + ", pin2=" + aPin2); let contact = new mozContact({ name: ["add"], tel: [{value: "0912345678"}], email:[] }); - let updateRequest = icc.updateContact(type, contact, pin2); - - updateRequest.onsuccess = function onsuccess() { - let updatedContact = updateRequest.result; - ok(updatedContact, "updateContact should have retuend a mozContact."); - ok(updatedContact.id.startsWith(EMULATOR_ICCID), - "The returned mozContact has wrong id."); - - // Get ICC contact for checking new contact - - let getRequest = icc.readContacts(type); - - getRequest.onsuccess = function onsuccess() { - let contacts = getRequest.result; - - // There are 4 SIM contacts which are harded in emulator - is(contacts.length, 5); - - is(contacts[4].name[0], "add"); - is(contacts[4].tel[0].value, "0912345678"); - - taskHelper.runNext(); - }; - - getRequest.onerror = function onerror() { - ok(false, "Cannot get " + type + " contacts: " + getRequest.error.name); - taskHelper.runNext(); - }; - }; - - updateRequest.onerror = function onerror() { - if (type === "fdn" && pin2 === undefined) { - ok(updateRequest.error.name === "SimPin2", - "expected error when pin2 is not provided"); - } else { - ok(false, "Cannot add " + type + " contact: " + updateRequest.error.name); - } - taskHelper.runNext(); - }; + return aIcc.updateContact(aType, contact, aPin2) + .then((aResult) => { + // Get ICC contact for checking new contact + return aIcc.readContacts(aType) + .then((aResult) => { + // There are 4 SIM contacts which are harded in emulator + is(aResult.length, 5); + + is(aResult[4].name[0], "add"); + is(aResult[4].tel[0].value, "0912345678"); + }, (aError) => { + ok(false, "Cannot get " + aType + " contacts: " + aError.name); + }) + }, (aError) => { + if (aType === "fdn" && aPin2 === undefined) { + ok(aError.name === "SimPin2", + "expected error when pin2 is not provided"); + } else { + ok(false, "Cannot add " + aType + " contact: " + aError.name); + } + }); } -/* Test read adn contacts */ -taskHelper.push(function testReadAdnContacts() { - testReadContacts("adn"); -}); - -/* Test add adn contacts */ -taskHelper.push(function testAddAdnContact() { - testAddContact("adn"); -}); - -/* Test read fdn contacts */ -taskHelper.push(function testReadAdnContacts() { - testReadContacts("fdn"); -}); - -/* Test add fdn contacts */ -taskHelper.push(function testReadAdnContacts() { - testAddContact("fdn", "0000"); +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + + // Test read adn contacts + return testReadContacts(icc, "adn") + // Test add adn contacts + .then(() => testAddContact(icc, "adn")) + // Test read fdn contact + .then(() => testReadContacts(icc, "fdn")) + // Test add fdn contacts + .then(() => testAddContact(icc, "fdn", "0000")) + // Test add fdn contacts without passing pin2 + .then(() => testAddContact(icc, "fdn")) + // Test read sdn contacts + .then(() => testReadContacts(icc, "sdn")); }); - -/* Test add fdn contacts without passing pin2 */ -taskHelper.push(function testReadAdnContacts() { - testAddContact("fdn"); -}); - -/* Test read sdn contacts */ -taskHelper.push(function testReadSdnContacts() { - testReadContacts("sdn"); -}); - -// Start test -taskHelper.runNext(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_icc_detected_undetected_event.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_icc_detected_undetected_event.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_icc_detected_undetected_event.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_icc_detected_undetected_event.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,67 +1,51 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ MARIONETTE_TIMEOUT = 30000; -MARIONETTE_HEAD_JS = "icc_header.js"; +MARIONETTE_HEAD_JS = "head.js"; -let origNumICCs = iccManager.iccIds.length; +// Start tests +startTestCommon(function() { + let origNumIccs = iccManager.iccIds.length; + let icc = getMozIcc(); + let iccId = icc.iccInfo.iccid; + let mobileConnection = getMozMobileConnectionByServiceId(); + + return Promise.resolve() + // Test iccundetected event. + .then(() => { + let promises = []; + + promises.push(setRadioEnabled(false)); + promises.push(waitForTargetEvent(iccManager, "iccundetected").then((aEvt) => { + is(aEvt.iccId, iccId, "icc " + aEvt.iccId + " becomes undetected"); + is(iccManager.iccIds.length, origNumIccs - 1, + "iccIds.length becomes to " + iccManager.iccIds.length); + is(iccManager.getIccById(aEvt.iccId), null, + "should not get a valid icc object here"); + + // The mozMobileConnection.iccId should be in sync. + is(mobileConnection.iccId, null, "check mozMobileConnection.iccId"); + })); + + return Promise.all(promises); + }) + // Test iccdetected event. + .then(() => { + let promises = []; + + promises.push(setRadioEnabled(true)); + promises.push(waitForTargetEvent(iccManager, "iccdetected").then((aEvt) => { + is(aEvt.iccId, iccId, "icc " + aEvt.iccId + " is detected"); + is(iccManager.iccIds.length, origNumIccs, + "iccIds.length becomes to " + iccManager.iccIds.length); + ok(iccManager.getIccById(aEvt.iccId) instanceof MozIcc, + "should get a valid icc object here"); + + // The mozMobileConnection.iccId should be in sync. + is(mobileConnection.iccId, iccId, "check mozMobileConnection.iccId"); + })); -function setRadioEnabled(enabled) { - let connection = navigator.mozMobileConnections[0]; - ok(connection); - - let request = connection.setRadioEnabled(enabled); - - request.onsuccess = function onsuccess() { - log('setRadioEnabled: ' + enabled); - }; - - request.onerror = function onerror() { - ok(false, "setRadioEnabled should be ok"); - }; -} - -/* Test iccundetected event */ -taskHelper.push(function testIccUndetectedEvent() { - setRadioEnabled(false); - iccManager.addEventListener("iccundetected", function oniccundetected(evt) { - log("got icc undetected event"); - iccManager.removeEventListener("iccundetected", oniccundetected); - - is(evt.iccId, iccId, "icc " + evt.iccId + " becomes undetected"); - is(iccManager.iccIds.length, origNumICCs - 1, - "iccIds.length becomes to " + iccManager.iccIds.length); - is(iccManager.getIccById(evt.iccId), null, - "should not get a valid icc object here"); - - // The mozMobileConnection.iccId should be in sync. - is(navigator.mozMobileConnections[0].iccId, null, - "check mozMobileConnection.iccId"); - - taskHelper.runNext(); - }); + return Promise.all(promises); + }); }); - -/* Test iccdetected event */ -taskHelper.push(function testIccDetectedEvent() { - setRadioEnabled(true); - iccManager.addEventListener("iccdetected", function oniccdetected(evt) { - log("got icc detected event"); - iccManager.removeEventListener("iccdetected", oniccdetected); - - is(evt.iccId, iccId, "icc " + evt.iccId + " is detected"); - is(iccManager.iccIds.length, origNumICCs, - "iccIds.length becomes to " + iccManager.iccIds.length); - ok(iccManager.getIccById(evt.iccId) instanceof MozIcc, - "should get a valid icc object here"); - - // The mozMobileConnection.iccId should be in sync. - is(navigator.mozMobileConnections[0].iccId, iccId, - "check mozMobileConnection.iccId"); - - taskHelper.runNext(); - }); -}); - -// Start test -taskHelper.runNext(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_icc_info.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_icc_info.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_icc_info.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_icc_info.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,35 +1,12 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ MARIONETTE_TIMEOUT = 30000; -MARIONETTE_HEAD_JS = "icc_header.js"; - -function setRadioEnabled(enabled) { - let connection = navigator.mozMobileConnections[0]; - ok(connection); - - let request = connection.setRadioEnabled(enabled); - - request.onsuccess = function onsuccess() { - log('setRadioEnabled: ' + enabled); - }; - - request.onerror = function onerror() { - ok(false, "setRadioEnabled should be ok"); - }; -} - -function setEmulatorMccMnc(mcc, mnc) { - let cmd = "operator set 0 Android,Android," + mcc + mnc; - emulatorHelper.sendCommand(cmd, function(result) { - let re = new RegExp("" + mcc + mnc + "$"); - ok(result[0].match(re), "MCC/MNC should be changed."); - }); -} +MARIONETTE_HEAD_JS = "head.js"; /* Basic test */ -taskHelper.push(function basicTest() { - let iccInfo = icc.iccInfo; +function basicTest(aIcc) { + let iccInfo = aIcc.iccInfo; // The emulator's hard coded iccid value. // See it here {B2G_HOME}/external/qemu/telephony/sim_card.c#L299. @@ -44,42 +21,44 @@ is(iccInfo.mcc, 310); is(iccInfo.mnc, 260); // Phone number is hardcoded in MSISDN - // See {B2G_HOME}/external/qemu/telephony/sim_card.c, in asimcard_io() + // See {B2G_HOME}/external/qemu/telephony/sim_card.c, in asimcard_io(). is(iccInfo.msisdn, "15555215554"); } else { log("Test Cdma IccInfo"); is(iccInfo.iccType, "ruim"); // MDN is hardcoded as "8587777777". // See it here {B2G_HOME}/hardware/ril/reference-ril/reference-ril.c, - // in requestCdmaSubscription() + // in requestCdmaSubscription(). is(iccInfo.mdn, "8587777777"); // PRL version is hardcoded as 1. // See it here {B2G_HOME}/hardware/ril/reference-ril/reference-ril.c, - // in requestCdmaSubscription() + // in requestCdmaSubscription(). is(iccInfo.prlVersion, 1); } +} - taskHelper.runNext(); -}); - -/* Test iccInfo when card becomes undetected */ -taskHelper.push(function testCardIsNotReady() { - // Turn off radio. - setRadioEnabled(false); - icc.addEventListener("iccinfochange", function oniccinfochange() { - // Expect iccInfo changes to null - if (icc.iccInfo === null) { - icc.removeEventListener("iccinfochange", oniccinfochange); - // We should restore radio status and expect to get iccdetected event. - setRadioEnabled(true); - iccManager.addEventListener("iccdetected", function oniccdetected(evt) { - log("icc detected: " + evt.iccId); - iccManager.removeEventListener("iccdetected", oniccdetected); - taskHelper.runNext(); - }); - } - }); +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + + return Promise.resolve() + // Basic test + .then(() => basicTest(icc)) + // Test iccInfo when card becomes undetected + .then(() => { + let promises = []; + promises.push(setRadioEnabled(false)); + promises.push(waitForTargetEvent(icc, "iccinfochange", function() { + // Expect iccInfo changes to null + return icc.iccInfo === null; + })); + return Promise.all(promises); + }) + // Restore radio status and expect to get iccdetected event. + .then(() => { + let promises = []; + promises.push(setRadioEnabled(true)); + promises.push(waitForTargetEvent(iccManager, "iccdetected")); + return Promise.all(promises); + }); }); - -// Start test -taskHelper.runNext(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_icc_match_mvno.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_icc_match_mvno.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_icc_match_mvno.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_icc_match_mvno.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,10 +1,10 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ MARIONETTE_TIMEOUT = 30000; -MARIONETTE_HEAD_JS = "icc_header.js"; +MARIONETTE_HEAD_JS = "head.js"; -let testCases = [ +const TEST_DATA = [ // mvno type, mvno data, request success, expected result // Emulator's hard coded IMSI: 310260000000000 ["imsi", "3102600", true, true ], @@ -31,36 +31,27 @@ ["gid", "5a4d6c", true, false ] ]; -function matchMvno(mvnoType, mvnoData, success, expectedResult) { - log("matchMvno: " + mvnoType + ", " + mvnoData); - let request = icc.matchMvno(mvnoType, mvnoData); - request.onsuccess = function onsuccess() { - log("onsuccess: " + request.result); - ok(success, "onsuccess while error expected"); - is(request.result, expectedResult); - testMatchMvno(); - } - request.onerror = function onerror() { - log("onerror: " + request.error.name); - ok(!success, "onerror while success expected"); - is(request.error.name, expectedResult); - testMatchMvno(); - } +function testMatchMvno(aIcc, aMvnoType, aMvnoData, aSuccess, aExpectedResult) { + log("matchMvno: " + aMvnoType + ", " + aMvnoData); + return aIcc.matchMvno(aMvnoType, aMvnoData) + .then((aResult) => { + log("onsuccess: " + aResult); + ok(aSuccess, "onsuccess while error expected"); + is(aResult, aExpectedResult); + }, (aError) => { + log("onerror: " + aError.name); + ok(!aSuccess, "onerror while success expected"); + is(aError.name, aExpectedResult); + }); } -function testMatchMvno() { - let testCase = testCases.shift(); - if (!testCase) { - taskHelper.runNext(); - return; +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => testMatchMvno.apply(null, [icc].concat(data))); } - matchMvno(testCase[0], testCase[1], testCase[2], testCase[3]); -} - -taskHelper.push( - testMatchMvno -); - -// Start test -taskHelper.runNext(); - + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_bip_command.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_bip_command.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_bip_command.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_bip_command.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,58 +1,65 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -MARIONETTE_HEAD_JS = "stk_helper.js"; +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; -function testBipCommand(command, expect) { - log("STK CMD " + JSON.stringify(command)); - - is(command.typeOfCommand, expect.typeOfCommand, expect.name); - is(command.options.text, expect.text, expect.name); - - let icons = command.options.icons; - if (icons) { - isIcons(icons, expect.icons, expect.name); - - let iconSelfExplanatory = command.options.iconSelfExplanatory; - is(iconSelfExplanatory, expect.iconSelfExplanatory, expect.name); - } - - runNextTest(); -} - -let tests = [ +const TEST_DATA = [ + // Open channel. {command: "d04f81030140018202818205074f70656e204944350702030403041f0239020578470a065465737447700272730d08f4557365724c6f670d08f4557365725077643c0301ad9c3e0521010101019e020007", - func: testBipCommand, - expect: {name: "open_channel_1", - typeOfCommand: iccManager.STK_CMD_OPEN_CHANNEL, + expect: {typeOfCommand: MozIccManager.STK_CMD_OPEN_CHANNEL, text: "Open ID", iconSelfExplanatory: true, - icons: [colorIcon, colorTransparencyIcon]}}, + icons: [COLOR_ICON, COLOR_TRANSPARENCY_ICON]}}, {command: "d0448103014001820281820500350702030403041f0239020578470a065465737447700272730d08f4557365724c6f670d08f4557365725077643c0301ad9c3e052101010101", - func: testBipCommand, - expect: {name: "open_channel_2", - typeOfCommand: iccManager.STK_CMD_OPEN_CHANNEL, + expect: {typeOfCommand: MozIccManager.STK_CMD_OPEN_CHANNEL, text: ""}}, {command: "d05381030140018202818205094f70656e2049442031350702030403041f0239020578470a065465737447700272730d08f4557365724c6f670d08f4557365725077643c0301ad9c3e052101010101d004000900b4", - func: testBipCommand, - expect: {name: "open_channel_3", - typeOfCommand: iccManager.STK_CMD_OPEN_CHANNEL, + expect: {typeOfCommand: MozIccManager.STK_CMD_OPEN_CHANNEL, text: "Open ID 1"}}, + // Close channel. {command: "d01b810301410082028121850a436c6f73652049442031d004000a00b4", - func: testBipCommand, - expect: {name: "close_channel_1", - typeOfCommand: iccManager.STK_CMD_CLOSE_CHANNEL, + expect: {typeOfCommand: MozIccManager.STK_CMD_CLOSE_CHANNEL, text: "Close ID 1"}}, + // Recive data. {command: "d022810301420082028121850e5265636569766520446174612031b701c8d004000e00b4", - func: testBipCommand, - expect: {name: "receive_data_1", - typeOfCommand: iccManager.STK_CMD_RECEIVE_DATA, + expect: {typeOfCommand: MozIccManager.STK_CMD_RECEIVE_DATA, text: "Receive Data 1"}}, + // Send data. {command: "d026810301430182028121850b53656e6420446174612031b6080001020304050607d004000b00b4", - func: testBipCommand, - expect: {name: "send_data_1", - typeOfCommand: iccManager.STK_CMD_SEND_DATA, + expect: {typeOfCommand: MozIccManager.STK_CMD_SEND_DATA, text: "Send Data 1"}}, ]; -runNextTest(); +function testBipCommand(aCommand, aExpect) { + is(aCommand.typeOfCommand, aExpect.typeOfCommand, "typeOfCommand"); + is(aCommand.options.text, aExpect.text, "options.text"); + + if (aExpect.icons) { + isIcons(aCommand.options.icons, aExpect.icons); + is(aCommand.options.iconSelfExplanatory, aExpect.iconSelfExplanatory, + "options.iconSelfExplanatory"); + } +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("bip_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testBipCommand(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); + + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_display_text.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_display_text.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_display_text.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_display_text.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,119 +1,69 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -MARIONETTE_HEAD_JS = "stk_helper.js"; +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; -function testDisplayText(command, expect) { - log("STK CMD " + JSON.stringify(command)); - is(command.typeOfCommand, iccManager.STK_CMD_DISPLAY_TEXT, expect.name); - is(command.options.text, expect.text, expect.name); - is(command.commandQualifier, expect.commandQualifier, expect.name); - is(command.options.userClear, expect.userClear, expect.name); - is(command.options.isHighPriority, expect.isHighPriority, expect.name); - - let duration = command.options.duration; - if (duration) { - is(duration.timeUnit, expect.duration.timeUnit, expect.name); - is(duration.timeInterval, expect.duration.timeInterval, expect.name); - } - - let icons = command.options.icons; - if (icons) { - isIcons(icons, expect.icons, expect.name); - - let iconSelfExplanatory = command.options.iconSelfExplanatory; - is(iconSelfExplanatory, expect.iconSelfExplanatory, expect.name); - } - - runNextTest(); -} - -let tests = [ +const TEST_DATA = [ {command: "d01a8103012180820281028d0f04546f6f6c6b697420546573742031", - func: testDisplayText, - expect: {name: "display_text_cmd_1", - commandQualifier: 0x80, + expect: {commandQualifier: 0x80, text: "Toolkit Test 1", userClear: true}}, {command: "d01a8103012181820281028d0f04546f6f6c6b697420546573742032", - func: testDisplayText, - expect: {name: "display_text_cmd_2", - commandQualifier: 0x81, + expect: {commandQualifier: 0x81, text: "Toolkit Test 2", isHighPriority: true, userClear: true}}, {command: "d0198103012180820281028d0e00d4f79bbd4ed341d4f29c0e9a01", - func: testDisplayText, - expect: {name: "display_text_cmd_3", - commandQualifier: 0x80, + expect: {commandQualifier: 0x80, text: "Toolkit Test 3", userClear: true}}, {command: "d01a8103012100820281028d0f04546f6f6c6b697420546573742034", - func: testDisplayText, - expect: {name: "display_text_cmd_4", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Toolkit Test 4"}}, {command: "d081ad8103012180820281028d81a1045468697320636f6d6d616e6420696e7374727563747320746865204d4520746f20646973706c617920612074657874206d6573736167652e20497420616c6c6f7773207468652053494d20746f20646566696e6520746865207072696f72697479206f662074686174206d6573736167652c20616e6420746865207465787420737472696e6720666f726d61742e2054776f207479706573206f66207072696f", - func: testDisplayText, - expect: {name: "display_text_cmd_5", - commandQualifier: 0x80, + expect: {commandQualifier: 0x80, text: "This command instructs the ME to display a text message. It allows the SIM to define the priority of that message, and the text string format. Two types of prio", userClear: true}}, {command: "d01a8103012180820281028d0f043c474f2d4241434b57415244533e", - func: testDisplayText, - expect: {name: "display_text_cmd_6", - commandQualifier: 0x80, + expect: {commandQualifier: 0x80, text: "", userClear: true}}, {command: "d0248103012180820281028d1908041704140420041004120421042204120423041904220415", - func: testDisplayText, - expect: {name: "display_text_cmd_7", - commandQualifier: 0x80, + expect: {commandQualifier: 0x80, text: "ЗДРАВСТВУЙТЕ", userClear: true}}, {command: "d0108103012180820281028d05084f60597d", - func: testDisplayText, - expect: {name: "display_text_cmd_8", - commandQualifier: 0x80, + expect: {commandQualifier: 0x80, text: "你好", userClear: true}}, {command: "d0128103012180820281028d07080038003030eb", - func: testDisplayText, - expect: {name: "display_text_cmd_9", - commandQualifier: 0x80, + expect: {commandQualifier: 0x80, text: "80ル", userClear: true}}, {command: "d0288103012180820281020d1d00d3309bfc06c95c301aa8e80259c3ec34b9ac07c9602f58ed159bb940", - func: testDisplayText, - expect: {name: "display_text_cmd_10", - commandQualifier: 0x80, + expect: {commandQualifier: 0x80, text: "Saldo 2.04 E. Validez 20/05/13. ", userClear: true}}, {command: "d0198103012180820281028D0A043130205365636F6E648402010A", - func: testDisplayText, - expect: {name: "display_text_cmd_11", - commandQualifier: 0x80, + expect: {commandQualifier: 0x80, text: "10 Second", userClear: true, - duration: {timeUnit: iccManager.STK_TIME_UNIT_SECOND, + duration: {timeUnit: MozIccManager.STK_TIME_UNIT_SECOND, timeInterval: 0x0A}}}, {command: "d01a8103012180820281028d0b0442617369632049636f6e9e020001", - func: testDisplayText, - expect: {name: "display_text_cmd_12", - commandQualifier: 0x80, + expect: {commandQualifier: 0x80, text: "Basic Icon", userClear: true, iconSelfExplanatory: true, - icons: [basicIcon]}}, + icons: [BASIC_ICON]}}, {command: "D026810301210082028102" + "8D" + "1B" + "00" + // 7BIT "D4F79BBD4ED341D4F29C0E3A4A9F55A8" + "0E8687C158A09B304905", - func: testDisplayText, - expect: {name: "display_text_cmd_13", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Toolkit Test GROUP:0x00, 7BIT"}}, {command: "D029810301210082028102" + "8D" + @@ -121,9 +71,7 @@ "04" + // 8BIT "546F6F6C6B697420546573742047524F" + "55503A307830302C2038424954", - func: testDisplayText, - expect: {name: "display_text_cmd_14", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Toolkit Test GROUP:0x00, 8BIT"}}, {command: "D046810301210082028102" + "8D" + @@ -133,9 +81,7 @@ "0054006500730074002000470052004F" + "00550050003A0030007800300030002C" + "00200055004300530032", - func: testDisplayText, - expect: {name: "display_text_cmd_15", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Toolkit Test GROUP:0x00, UCS2"}}, {command: "D026810301210082028102" + "8D" + @@ -143,9 +89,7 @@ "12" + // 7BIT + Class 2 "D4F79BBD4ED341D4F29C0E3A4A9F55A8" + "0E868FC158A09B304905", - func: testDisplayText, - expect: {name: "display_text_cmd_16", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Toolkit Test GROUP:0x10, 7BIT"}}, {command: "D029810301210082028102" + "8D" + @@ -153,9 +97,7 @@ "16" + // 8BIT + Class 2 "546F6F6C6B697420546573742047524F" + "55503A307831302C2038424954", - func: testDisplayText, - expect: {name: "display_text_cmd_17", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Toolkit Test GROUP:0x10, 8BIT"}}, {command: "D046810301210082028102" + "8D" + @@ -165,9 +107,7 @@ "0054006500730074002000470052004F" + "00550050003A0030007800310030002C" + "00200055004300530032", - func: testDisplayText, - expect: {name: "display_text_cmd_18", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Toolkit Test GROUP:0x10, UCS2"}}, {command: "D026810301210082028102" + "8D" + @@ -175,9 +115,7 @@ "F2" + // 7BIT + Class 2 "D4F79BBD4ED341D4F29C0E3A4A9F55A8" + "0E8637C258A09B304905", - func: testDisplayText, - expect: {name: "display_text_cmd_19", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Toolkit Test GROUP:0xF0, 7BIT"}}, {command: "D029810301210082028102" + "8D" + @@ -185,9 +123,7 @@ "F6" + // 8BIT + Class 2 "546F6F6C6B697420546573742047524F" + "55503A307846302C2038424954", - func: testDisplayText, - expect: {name: "display_text_cmd_20", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Toolkit Test GROUP:0xF0, 8BIT"}}, // Bug 1088573: this test case is to ensure that we provide |length| argument // in |integer| format to GsmPDUHelper.readSeptetsToString(). @@ -214,9 +150,7 @@ "0C8287E5207619346D1E73A0783D0D9A" + "9FCA733A885C96BFEBEC32280C9A6689" + "CE621654768382D529551A64268B2E", - func: testDisplayText, - expect: {name: "display_text_cmd_21", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Ce message se compose de 273 caracteres en mode " + "compresse. Ce message est affiche sur plusieurs " + "ecrans et ne doit pas etre tronque. 273 est le " + @@ -225,4 +159,48 @@ "deroule a SYDNEY en AUSTRALIE."}}, ]; -runNextTest(); +function testDisplayText(aCommand, aExpect) { + is(aCommand.typeOfCommand, MozIccManager.STK_CMD_DISPLAY_TEXT, + "typeOfCommand"); + is(aCommand.commandQualifier, aExpect.commandQualifier, "commandQualifier"); + is(aCommand.options.text, aExpect.text, "options.text"); + is(aCommand.options.userClear, aExpect.userClear, "options.userClear"); + is(aCommand.options.isHighPriority, aExpect.isHighPriority, + "options.isHighPriority"); + + if (aExpect.duration) { + let duration = aCommand.options.duration; + is(duration.timeUnit, aExpect.duration.timeUnit, + "options.duration.timeUnit"); + is(duration.timeInterval, aExpect.duration.timeInterval, + "options.duration.timeInterval"); + } + + if (aExpect.icons) { + isIcons(aCommand.options.icons, aExpect.icons); + is(aCommand.options.iconSelfExplanatory, aExpect.iconSelfExplanatory, + "options.iconSelfExplanatory"); + } +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("display_text_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testDisplayText(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); + + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_get_inkey.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_get_inkey.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_get_inkey.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_get_inkey.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,115 +1,106 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -MARIONETTE_HEAD_JS = "stk_helper.js"; +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; -function testGetInKey(command, expect) { - log("STK CMD " + JSON.stringify(command)); - is(command.typeOfCommand, iccManager.STK_CMD_GET_INKEY, expect.name); - is(command.commandQualifier, expect.commandQualifier, expect.name); - is(command.options.text, expect.text, expect.name); - is(command.options.isAlphabet, expect.isAlphabet, expect.name); - is(command.options.isUCS2, expect.isUCS2, expect.name); - is(command.options.isYesNoRequested, expect.isYesNoRequested, expect.name); - - let duration = command.options.duration; - if (duration) { - is(duration.timeUnit, expect.duration.timeUnit, expect.name); - is(duration.timeInterval, expect.duration.timeInterval, expect.name); - } - - let icons = command.options.icons; - if (icons) { - isIcons(icons, expect.icons, expect.name); - - let iconSelfExplanatory = command.options.iconSelfExplanatory; - is(iconSelfExplanatory, expect.iconSelfExplanatory, expect.name); - } - - runNextTest(); -} - -let tests = [ +const TEST_DATA = [ {command: "d0158103012200820281828d0a04456e74657220222b22", - func: testGetInKey, - expect: {name: "get_inkey_cmd_1", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Enter \"+\""}}, {command: "d0148103012200820281828d09004537bd2c07896022", - func: testGetInKey, - expect: {name: "get_inkey_cmd_2", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Enter \"0\""}}, {command: "d01a8103012200820281828d0f043c474f2d4241434b57415244533e", - func: testGetInKey, - expect: {name: "get_inkey_cmd_3", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: ""}}, {command: "d0138103012200820281828d08043c41424f52543e", - func: testGetInKey, - expect: {name: "get_inkey_cmd_4", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: ""}}, {command: "d0158103012201820281828d0a04456e74657220227122", - func: testGetInKey, - expect: {name: "get_inkey_cmd_5", - commandQualifier: 0x01, + expect: {commandQualifier: 0x01, text: "Enter \"q\"", isAlphabet: true}}, {command: "d081ad8103012201820281828d81a104456e746572202278222e205468697320636f6d6d616e6420696e7374727563747320746865204d4520746f20646973706c617920746578742c20616e6420746f2065787065637420746865207573657220746f20656e74657220612073696e676c65206368617261637465722e20416e7920726573706f6e736520656e7465726564206279207468652075736572207368616c6c206265207061737365642074", - func: testGetInKey, - expect: {name: "get_inkey_cmd_6", - commandQualifier: 0x01, + expect: {commandQualifier: 0x01, text: "Enter \"x\". This command instructs the ME to display text, and to expect the user to enter a single character. Any response entered by the user shall be passed t", isAlphabet: true}}, {command: "d0168103012200820281828d0b043c54494d452d4f55543e", - func: testGetInKey, - expect: {name: "get_inkey_cmd_7", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: ""}}, {command: "d0248103012200820281828d1908041704140420041004120421042204120423041904220415", - func: testGetInKey, - expect: {name: "get_inkey_cmd_8", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "ЗДРАВСТВУЙТЕ"}}, {command: "d081998103012200820281828d818d080417041404200410041204210422041204230419042204150417041404200410041204210422041204230419042204150417041404200410041204210422041204230419042204150417041404200410041204210422041204230419042204150417041404200410041204210422041204230419042204150417041404200410041204210422041204230419", - func: testGetInKey, - expect: {name: "get_inkey_cmd_9", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕЗДРАВСТВУЙ"}}, {command: "d0118103012203820281828d0604456e746572", - func: testGetInKey, - expect: {name: "get_inkey_cmd_10", - commandQualifier: 0x03, + expect: {commandQualifier: 0x03, text: "Enter", isAlphabet: true, isUCS2: true}}, {command: "d0158103012204820281828d0a04456e74657220594553", - func: testGetInKey, - expect: {name: "get_inkey_cmd_11", - commandQualifier: 0x04, + expect: {commandQualifier: 0x04, text: "Enter YES", isYesNoRequested: true}}, {command: "d0148103012204820281828d0904456e746572204e4f", - func: testGetInKey, - expect: {name: "get_inkey_cmd_12", - commandQualifier: 0x04, + expect: {commandQualifier: 0x04, text: "Enter NO", isYesNoRequested: true}}, {command: "d0198103012200820281828d0a043c4e4f2d49434f4e3e1e020002", - func: testGetInKey, - expect: {name: "get_inkey_cmd_13", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, // The record number 02 in EFimg is not defined, so no icon will be // shown, but the text string should still be displayed. text: ""}}, {command: "D0198103012200820281828D0A04456E74657220222B228402010A", - func: testGetInKey, - expect: {name: "get_inkey_cmd_14", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Enter \"+\"", - duration: {timeUnit: iccManager.STK_TIME_UNIT_SECOND, + duration: {timeUnit: MozIccManager.STK_TIME_UNIT_SECOND, timeInterval: 0x0A}}}, ]; -runNextTest(); +function testGetInKey(aCommand, aExpect) { + is(aCommand.typeOfCommand, MozIccManager.STK_CMD_GET_INKEY, "typeOfCommand"); + is(aCommand.commandQualifier, aExpect.commandQualifier, "commandQualifier"); + is(aCommand.options.text, aExpect.text, "options.text"); + is(aCommand.options.isAlphabet, aExpect.isAlphabet, "options.isAlphabet"); + is(aCommand.options.isUCS2, aExpect.isUCS2, "options.isUCS2"); + is(aCommand.options.isYesNoRequested, aExpect.isYesNoRequested, + "options.isYesNoRequested"); + + if (aExpect.duration) { + let duration = aCommand.options.duration; + is(duration.timeUnit, aExpect.duration.timeUnit, + "options.duration.timeUnit"); + is(duration.timeInterval, aExpect.duration.timeInterval, + "options.duration.timeInterval"); + } + + if (aExpect.icons) { + isIcons(aCommand.options.icons, aExpect.icons); + is(aCommand.options.iconSelfExplanatory, aExpect.iconSelfExplanatory, + "options.iconSelfExplanatory"); + } +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("get_inkey_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testGetInKey(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); + + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_get_input.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_get_input.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_get_input.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_get_input.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,183 +1,169 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -MARIONETTE_HEAD_JS = "stk_helper.js"; +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; -function testGetInput(command, expect) { - log("STK CMD " + JSON.stringify(command)); - is(command.typeOfCommand, iccManager.STK_CMD_GET_INPUT, expect.name); - is(command.commandQualifier, expect.commandQualifier, expect.name); - is(command.options.text, expect.text, expect.name); - is(command.options.minLength, expect.minLength, expect.name); - is(command.options.maxLength, expect.maxLength, expect.name); - if (command.options.defaultText) { - is(command.options.defaultText, expect.defaultText, expect.name); - } - if (command.options.isAlphabet) { - is(command.options.isAlphabet, expect.isAlphabet, expect.name); - } - if (command.options.isUCS2) { - is(command.options.isUCS2, expect.isUCS2, expect.name); - } - if (command.options.isPacked) { - is(command.options.isPacked, expect.isPacked, expect.name); - } - if (command.options.hideInput) { - is(command.options.hideInput, expect.hideInput, expect.name); - } - let icons = command.options.icons; - if (icons) { - isIcons(icons, expect.icons, expect.name); - - let iconSelfExplanatory = command.options.iconSelfExplanatory; - is(iconSelfExplanatory, expect.iconSelfExplanatory, expect.name); - } - - runNextTest(); -} - -let tests = [ +const TEST_DATA = [ {command: "d01b8103012300820281828d0c04456e74657220313233343591020505", - func: testGetInput, - expect: {name: "get_input_cmd_1", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Enter 12345", minLength: 5, maxLength: 5}}, {command: "d01a8103012308820281828d0b004537bd2c07d96eaad10a91020505", - func: testGetInput, - expect: {name: "get_input_cmd_2", - commandQualifier: 0x08, + expect: {commandQualifier: 0x08, text: "Enter 67*#+", minLength: 5, maxLength: 5, isPacked: true}}, {command: "d01b8103012301820281828d0c04456e74657220416243644591020505", - func: testGetInput, - expect: {name: "get_input_cmd_3", - commandQualifier: 0x01, + expect: {commandQualifier: 0x01, text: "Enter AbCdE", minLength: 5, maxLength: 5, isAlphabet: true}}, {command: "d0278103012304820281828d180450617373776f726420313c53454e443e3233343536373891020408", - func: testGetInput, - expect: {name: "get_input_cmd_4", - commandQualifier: 0x04, + expect: {commandQualifier: 0x04, text: "Password 12345678", minLength: 4, maxLength: 8, hideInput: true}}, {command: "d0248103012300820281828d1504456e74657220312e2e392c302e2e392c3028312991020114", - func: testGetInput, - expect: {name: "get_input_cmd_5", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Enter 1..9,0..9,0(1)", minLength: 1, maxLength: 20}}, {command: "d01e8103012300820281828d0f043c474f2d4241434b57415244533e91020008", - func: testGetInput, - expect: {name: "get_input_cmd_6", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "", minLength: 0, maxLength: 8}}, {command: "d0178103012300820281828d08043c41424f52543e91020008", - func: testGetInput, - expect: {name: "get_input_cmd_7", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "", minLength: 0, maxLength: 8}}, {command: "d081b18103012300820281828d81a1042a2a2a313131313131313131312323232a2a2a323232323232323232322323232a2a2a333333333333333333332323232a2a2a343434343434343434342323232a2a2a353535353535353535352323232a2a2a363636363636363636362323232a2a2a373737373737373737372323232a2a2a383838383838383838382323232a2a2a393939393939393939392323232a2a2a303030303030303030302323239102a0a0", - func: testGetInput, - expect: {name: "get_input_cmd_8", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "***1111111111###***2222222222###***3333333333###***4444444444###***5555555555###***6666666666###***7777777777###***8888888888###***9999999999###***0000000000###", minLength: 160, maxLength: 160}}, {command: "d0168103012300820281828d07043c53454e443e91020001", - func: testGetInput, - expect: {name: "get_input_cmd_9", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "", minLength: 0, maxLength: 1}}, {command: "d01a8103012300820281828d0b043c54494d452d4f55543e9102000a", - func: testGetInput, - expect: {name: "get_input_cmd_10", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "", minLength: 0, maxLength: 10}}, {command: "d0288103012301820281828d190804170414042004100412042104220412042304190422041591020505", - func: testGetInput, - expect: {name: "get_input_cmd_11", - commandQualifier: 0x01, + expect: {commandQualifier: 0x01, text: "ЗДРАВСТВУЙТЕ", minLength: 5, maxLength: 5, isAlphabet: true}}, {command: "d0819d8103012301820281828d818d08041704140420041004120421042204120423041904220415041704140420041004120421042204120423041904220415041704140420041004120421042204120423041904220415041704140420041004120421042204120423041904220415041704140420041004120421042204120423041904220415041704140420041004120421042204120423041991020505", - func: testGetInput, - expect: {name: "get_input_cmd_12", - commandQualifier: 0x01, + expect: {commandQualifier: 0x01, text: "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕЗДРАВСТВУЙ", minLength: 5, maxLength: 5, isAlphabet: true}}, {command: "d01b8103012303820281828d0c04456e7465722048656c6c6f91020c0c", - func: testGetInput, - expect: {name: "get_input_cmd_13", - commandQualifier: 0x03, + expect: {commandQualifier: 0x03, text: "Enter Hello", minLength: 12, maxLength: 12, isAlphabet: true, isUCS2: true}}, {command: "d01b8103012303820281828d0c04456e7465722048656c6c6f910205ff", - func: testGetInput, - expect: {name: "get_input_cmd_14", - commandQualifier: 0x03, + expect: {commandQualifier: 0x03, text: "Enter Hello", minLength: 5, maxLength: 0xFF, isAlphabet: true, isUCS2: true}}, {command: "d0238103012300820281828d0c04456e746572203132333435910205051706043132333435", - func: testGetInput, - expect: {name: "get_input_cmd_15", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Enter 12345", minLength: 5, maxLength: 5, defaultText: "12345"}}, {command: "d081ba8103012300820281828d0704456e7465723a9102a0a01781a1042a2a2a313131313131313131312323232a2a2a323232323232323232322323232a2a2a333333333333333333332323232a2a2a343434343434343434342323232a2a2a353535353535353535352323232a2a2a363636363636363636362323232a2a2a373737373737373737372323232a2a2a383838383838383838382323232a2a2a393939393939393939392323232a2a2a30303030303030303030232323", - func: testGetInput, - expect: {name: "get_input_cmd_16", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Enter:", minLength: 160, maxLength: 160, defaultText: "***1111111111###***2222222222###***3333333333###***4444444444###***5555555555###***6666666666###***7777777777###***8888888888###***9999999999###***0000000000###"}}, {command: "d01d8103012300820281828d0a043c4e4f2d49434f4e3e9102000a1e020002", - func: testGetInput, - expect: {name: "get_input_cmd_17", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, // The record number 02 in EFimg is not defined, so no icon will be // shown, but the text string should still be displayed. text: "", minLength: 0, maxLength: 10}}, {command: "d0208103012300820281828d0d043c42415349432d49434f4e3e9102000a1e020101", - func: testGetInput, - expect: {name: "get_input_cmd_18", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "", minLength: 0, maxLength: 10, iconSelfExplanatory: false, - icons: [basicIcon]}}, + icons: [BASIC_ICON]}}, ]; -runNextTest(); +function testGetInput(aCommand, aExpect) { + is(aCommand.typeOfCommand, MozIccManager.STK_CMD_GET_INPUT, "typeOfCommand"); + is(aCommand.commandQualifier, aExpect.commandQualifier, "commandQualifier"); + is(aCommand.options.text, aExpect.text, "options.text"); + is(aCommand.options.minLength, aExpect.minLength, "options.minLength"); + is(aCommand.options.maxLength, aExpect.maxLength, "options.maxLength"); + + if (aExpect.defaultText) { + is(aCommand.options.defaultText, aExpect.defaultText, "options.defaultText"); + } + + if (aExpect.isAlphabet) { + is(aCommand.options.isAlphabet, aExpect.isAlphabet, "options.isAlphabet"); + } + + if (aExpect.isUCS2) { + is(aCommand.options.isUCS2, aExpect.isUCS2, "options.isUCS2"); + } + + if (aExpect.isPacked) { + is(aCommand.options.isPacked, aExpect.isPacked, "options.isPacked"); + } + + if (aExpect.hideInput) { + is(aCommand.options.hideInput, aExpect.hideInput, "options.hideInput"); + } + + if (aExpect.icons) { + isIcons(aCommand.options.icons, aExpect.icons); + is(aCommand.options.iconSelfExplanatory, aExpect.iconSelfExplanatory, + "options.iconSelfExplanatory"); + } +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("get_input_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testGetInput(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); + + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_launch_browser.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_launch_browser.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_launch_browser.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_launch_browser.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,267 +1,208 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -MARIONETTE_HEAD_JS = "stk_helper.js"; +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; -function testLaunchBrowser(command, expect) { - log("STK CMD " + JSON.stringify(command)); - is(command.typeOfCommand, iccManager.STK_CMD_LAUNCH_BROWSER, expect.name); - is(command.commandQualifier, expect.commandQualifier, expect.name); - is(command.options.url, expect.url, expect.name); - if (expect.confirmMessage) { - isStkText(command.options.confirmMessage, expect.confirmMessage, expect.name); - } - - runNextTest(); -} - -let tests = [ +const TEST_DATA = [ {command: "d0188103011500820281823100050b44656661756c742055524c", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_1", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL" }}}, {command: "d01f8103011500820281823112687474703a2f2f7878782e7979792e7a7a7a0500", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_2", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "http://xxx.yyy.zzz", confirmMessage: { text: "" }}}, {command: "d00e8103011500820281823001003100", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_3", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: ""}}, {command: "d0208103011500820281823100320103" + "0d10046162632e6465662e6768692e6a6b6c", // "0D" String TLV is useless for Launch Browser. - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_4", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: ""}}, {command: "d0188103011502820281823100050b44656661756c742055524c", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_5", - commandQualifier: 0x02, + expect: {commandQualifier: 0x02, url: "", confirmMessage: { text: "Default URL" }}}, {command: "d0188103011503820281823100050b44656661756c742055524c", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_6", - commandQualifier: 0x03, + expect: {commandQualifier: 0x03, url: "", confirmMessage: { text: "Default URL"}}}, {command: "d00b8103011500820281823100", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_7", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: ""}}, {command: "d0268103011502820281823100051980041704140420041004120421042204120423041904220415", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_8", - commandQualifier: 0x02, + expect: {commandQualifier: 0x02, url: "", confirmMessage: { text: "ЗДРАВСТВУЙТЕ" }}}, {command: "d021810301150282028182310005104e6f742073656c66206578706c616e2e1e020101", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_9", - commandQualifier: 0x02, + expect: {commandQualifier: 0x02, url: "", confirmMessage: { text: "Not self explan.", iconSelfExplanatory: false, - icons : [basicIcon] } + icons : [BASIC_ICON] } }}, {command: "d01d8103011502820281823100050c53656c66206578706c616e2e1e020001", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_10", - commandQualifier: 0x02, + expect: {commandQualifier: 0x02, url: "", confirmMessage: { text: "Self explan.", iconSelfExplanatory: true, - icons : [basicIcon] } + icons : [BASIC_ICON] } }}, {command: "d0208103011500820281823100050d44656661756c742055524c2031d004000d00b4", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_11", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 1" }}}, {command: "d01a8103011500820281823100050d44656661756c742055524c2032", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_12", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 2" }}}, {command: "d0208103011500820281823100050d44656661756c742055524c2031d004000d01b4", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_13", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 1" }}}, {command: "d01a8103011500820281823100050d44656661756c742055524c2032", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_14", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 2" }}}, {command: "d0208103011500820281823100050d44656661756c742055524c2031d004000d02b4", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_15", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 1" }}}, {command: "d01a8103011500820281823100050d44656661756c742055524c2032", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_16", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 2" }}}, - {command: "d0208103011500820281823100050d44656661756c742055524c2031d004000d04b4", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_17", - commandQualifier: 0x00, + {command: "d0208103011500820281823100050d44656661756c742055524c2031d004000d04b4", + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 1" }}}, {command: "d0208103011500820281823100050d44656661756c742055524c2032d004000d00b4", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_18", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 2" }}}, {command: "d01a8103011500820281823100050d44656661756c742055524c2033", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_19", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 3" }}}, - {command: "d0208103011500820281823100050d44656661756c742055524c2031d004000d08b4", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_20", - commandQualifier: 0x00, + {command: "d0208103011500820281823100050d44656661756c742055524c2031d004000d08b4", + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 1" }}}, {command: "d0208103011500820281823100050d44656661756c742055524c2032d004000d00b4", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_21", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 2" }}}, {command: "d01a8103011500820281823100050d44656661756c742055524c2033", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_22", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 3" }}}, - {command: "d0208103011500820281823100050d44656661756c742055524c2031d004000d10b4", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_23", - commandQualifier: 0x00, + {command: "d0208103011500820281823100050d44656661756c742055524c2031d004000d10b4", + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 1" }}}, {command: "d0208103011500820281823100050d44656661756c742055524c2032d004000d00b4", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_24", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 2" }}}, {command: "d01a8103011500820281823100050d44656661756c742055524c2033", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_25", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 3" }}}, - {command: "d0208103011500820281823100050d44656661756c742055524c2031d004000d20b4", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_26", - commandQualifier: 0x00, + {command: "d0208103011500820281823100050d44656661756c742055524c2031d004000d20b4", + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 1" }}}, {command: "d0208103011500820281823100050d44656661756c742055524c2032d004000d00b4", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_27", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 2" }}}, {command: "d01a8103011500820281823100050d44656661756c742055524c2033", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_28", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 3" }}}, - {command: "d0208103011500820281823100050d44656661756c742055524c2031d004000d40b4", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_29", - commandQualifier: 0x00, + {command: "d0208103011500820281823100050d44656661756c742055524c2031d004000d40b4", + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 1" }}}, {command: "d0208103011500820281823100050d44656661756c742055524c2032d004000d00b4", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_30", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 2" }}}, {command: "d01a8103011500820281823100050d44656661756c742055524c2033", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_31", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 3" }}}, - {command: "d0208103011500820281823100050d44656661756c742055524c2031d004000d80b4", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_32", - commandQualifier: 0x00, + {command: "d0208103011500820281823100050d44656661756c742055524c2031d004000d80b4", + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 1" }}}, {command: "d0208103011500820281823100050d44656661756c742055524c2032d004000d00b4", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_33", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 2" }}}, {command: "d01a8103011500820281823100050d44656661756c742055524c2033", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_34", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 3" }}}, - {command: "d0208103011500820281823100050d44656661756c742055524c2031d004000d00b4", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_35", - commandQualifier: 0x00, + {command: "d0208103011500820281823100050d44656661756c742055524c2031d004000d00b4", + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 1" }}}, {command: "d01a8103011500820281823100050d44656661756c742055524c2032", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_36", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { text: "Default URL 2" }}}, - {command: "d01281030115028202818231000505804f60597d", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_37", - commandQualifier: 0x02, + {command: "d01281030115028202818231000505804f60597d", + expect: {commandQualifier: 0x02, url: "", confirmMessage: { text: "你好" }}}, {command: "d010810301150282028182310005038030eb", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_38", - commandQualifier: 0x02, + expect: {commandQualifier: 0x02, url: "", confirmMessage: { text: "ル" }}}, {command: "d01281030115008202818230010031001e020001", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_39", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { iconSelfExplanatory: true, - icons: [basicIcon] }}}, + icons: [BASIC_ICON] }}}, {command: "d01281030115008202818230010031001e020003", - func: testLaunchBrowser, - expect: {name: "launch_browser_cmd_40", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, url: "", confirmMessage: { iconSelfExplanatory: true, - icons: [colorIcon] }}}, + icons: [COLOR_ICON] }}}, ]; -runNextTest(); +function testLaunchBrowser(aCommand, aExpect) { + is(aCommand.typeOfCommand, MozIccManager.STK_CMD_LAUNCH_BROWSER, + "typeOfCommand"); + is(aCommand.commandQualifier, aExpect.commandQualifier, "commandQualifier"); + is(aCommand.options.url, aExpect.url, "options.url"); + + if (aExpect.confirmMessage) { + isStkText(aCommand.options.confirmMessage, aExpect.confirmMessage, + "options.confirmMessage"); + } +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("launch_browser_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testLaunchBrowser(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); + + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_local_info.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_local_info.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_local_info.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_local_info.js 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,56 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; + +const TEST_DATA = [ + // Location + {command: "d009810301260082028182", + expect: {commandNumber: 0x01, + commandQualifier: MozIccManager.STK_LOCAL_INFO_LOCATION_INFO, + localInfoType: MozIccManager.STK_LOCAL_INFO_LOCATION_INFO}}, + // Imei + {command: "d009810301260182028182", + expect: {commandNumber: 0x01, + commandQualifier: MozIccManager.STK_LOCAL_INFO_IMEI, + localInfoType: MozIccManager.STK_LOCAL_INFO_IMEI}}, + // Data + {command: "d009810301260382028182", + expect: {commandNumber: 0x01, + commandQualifier: MozIccManager.STK_LOCAL_INFO_DATE_TIME_ZONE, + localInfoType: MozIccManager.STK_LOCAL_INFO_DATE_TIME_ZONE}}, + // Language + {command: "d009810301260482028182", + expect: {commandNumber: 0x01, + commandQualifier: MozIccManager.STK_LOCAL_INFO_LANGUAGE, + localInfoType: MozIccManager.STK_LOCAL_INFO_LANGUAGE}}, +]; + +function testLocalInfo(aCommand, aExpect) { + is(aCommand.typeOfCommand, MozIccManager.STK_CMD_PROVIDE_LOCAL_INFO, "typeOfCommand"); + is(aCommand.commandQualifier, aExpect.commandQualifier, "commandQualifier"); + is(aCommand.options.localInfoType, aExpect.localInfoType, "options.localInfoType"); +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("local_info_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testLocalInfo(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); + + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_poll_off.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_poll_off.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_poll_off.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_poll_off.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,21 +1,37 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -MARIONETTE_HEAD_JS = "stk_helper.js"; +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; -function testPollOff(command, expect) { - log("STK CMD " + JSON.stringify(command)); - is(command.typeOfCommand, iccManager.STK_CMD_POLL_OFF, expect.name); - is(command.commandQualifier, expect.commandQualifier, expect.name); +const TEST_DATA = [ + {command: "d009810301040082028182", + expect: {commandQualifier: 0x00}} +]; - runNextTest(); +function testPollOff(aCommand, aExpect) { + is(aCommand.typeOfCommand, MozIccManager.STK_CMD_POLL_OFF, "typeOfCommand"); + is(aCommand.commandQualifier, aExpect.commandQualifier, "commandQualifier"); } -let tests = [ - {command: "d009810301040082028182", - func: testPollOff, - expect: {name: "pull_off_cmd_1", - commandQualifier: 0x00}} -]; +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("pull_off_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testPollOff(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); -runNextTest(); + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_proactive_command.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_proactive_command.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_proactive_command.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_proactive_command.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,107 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -MARIONETTE_HEAD_JS = "stk_helper.js"; - -function testLocalInfoLocation(cmd) { - log("STK CMD " + JSON.stringify(cmd)); - is(cmd.typeOfCommand, iccManager.STK_CMD_PROVIDE_LOCAL_INFO); - is(cmd.commandNumber, 0x01); - is(cmd.commandQualifier, iccManager.STK_LOCAL_INFO_LOCATION_INFO); - is(cmd.options.localInfoType, iccManager.STK_LOCAL_INFO_LOCATION_INFO); - - runNextTest(); -} - -function testLocalInfoImei(cmd) { - log("STK CMD " + JSON.stringify(cmd)); - is(cmd.typeOfCommand, iccManager.STK_CMD_PROVIDE_LOCAL_INFO); - is(cmd.commandNumber, 0x01); - is(cmd.commandQualifier, iccManager.STK_LOCAL_INFO_IMEI); - is(cmd.options.localInfoType, iccManager.STK_LOCAL_INFO_IMEI); - - runNextTest(); -} - -function testLocalInfoDate(cmd) { - log("STK CMD " + JSON.stringify(cmd)); - is(cmd.typeOfCommand, iccManager.STK_CMD_PROVIDE_LOCAL_INFO); - is(cmd.commandNumber, 0x01); - is(cmd.commandQualifier, iccManager.STK_LOCAL_INFO_DATE_TIME_ZONE); - is(cmd.options.localInfoType, iccManager.STK_LOCAL_INFO_DATE_TIME_ZONE); - - runNextTest(); -} - -function testLocalInfoLanguage(cmd) { - log("STK CMD " + JSON.stringify(cmd)); - is(cmd.typeOfCommand, iccManager.STK_CMD_PROVIDE_LOCAL_INFO); - is(cmd.commandNumber, 0x01); - is(cmd.commandQualifier, iccManager.STK_LOCAL_INFO_LANGUAGE); - is(cmd.options.localInfoType, iccManager.STK_LOCAL_INFO_LANGUAGE); - - runNextTest(); -} - -function testRefresh(cmd) { - log("STK CMD " + JSON.stringify(cmd)); - is(cmd.typeOfCommand, iccManager.STK_CMD_REFRESH); - is(cmd.commandNumber, 0x01); - is(cmd.commandQualifier, 0x01); - is(cmd.options, null); - - runNextTest(); -} - -function testTimerManagementStart(cmd) { - log("STK CMD " + JSON.stringify(cmd)); - is(cmd.typeOfCommand, iccManager.STK_CMD_TIMER_MANAGEMENT); - is(cmd.commandNumber, 0x01); - is(cmd.commandQualifier, iccManager.STK_TIMER_START); - is(cmd.options.timerAction, iccManager.STK_TIMER_START); - is(cmd.options.timerId, 0x01); - is(cmd.options.timerValue, (0x01 * 60 * 60) + (0x02 * 60) + 0x03); - - runNextTest(); -} - -function testTimerManagementDeactivate(cmd) { - log("STK CMD " + JSON.stringify(cmd)); - is(cmd.typeOfCommand, iccManager.STK_CMD_TIMER_MANAGEMENT); - is(cmd.commandNumber, 0x01); - is(cmd.commandQualifier, iccManager.STK_TIMER_DEACTIVATE); - is(cmd.options.timerAction, iccManager.STK_TIMER_DEACTIVATE); - is(cmd.options.timerId, 0x04); - - runNextTest(); -} - -function testTimerManagementGetCurrentValue(cmd) { - log("STK CMD " + JSON.stringify(cmd)); - is(cmd.typeOfCommand, iccManager.STK_CMD_TIMER_MANAGEMENT); - is(cmd.commandNumber, 0x01); - is(cmd.commandQualifier, iccManager.STK_TIMER_GET_CURRENT_VALUE); - is(cmd.options.timerAction, iccManager.STK_TIMER_GET_CURRENT_VALUE); - is(cmd.options.timerId, 0x08); - - runNextTest(); -} - -let tests = [ - {command: "d009810301260082028182", - func: testLocalInfoLocation}, - {command: "d009810301260182028182", - func: testLocalInfoImei}, - {command: "d009810301260382028182", - func: testLocalInfoDate}, - {command: "d009810301260482028182", - func: testLocalInfoLanguage}, - {command: "d011810301270082028182a40101a503102030", - func: testTimerManagementStart}, - {command: "d00c810301270182028182a40104", - func: testTimerManagementDeactivate}, - {command: "d00c810301270282028182a40108", - func: testTimerManagementGetCurrentValue}, - ]; - -runNextTest(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_refresh.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_refresh.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_refresh.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_refresh.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,25 +1,39 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -MARIONETTE_HEAD_JS = "stk_helper.js"; +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; -function testRefresh(command, expect) { - log("STK CMD " + JSON.stringify(command)); - is(command.typeOfCommand, iccManager.STK_CMD_REFRESH, expect.name); - is(command.commandQualifier, expect.commandQualifier, expect.name); - - runNextTest(); -} - -let tests = [ +const TEST_DATA = [ {command: "d0108103010101820281829205013f002fe2", - func: testRefresh, - expect: {name: "refresh_cmd_1", - commandQualifier: 0x01}}, + expect: {commandQualifier: 0x01}}, {command: "d009810301010482028182", - func: testRefresh, - expect: {name: "refresh_cmd_2", - commandQualifier: 0x04}} + expect: {commandQualifier: 0x04}} ]; -runNextTest(); +function testRefresh(aCommand, aExpect) { + is(aCommand.typeOfCommand, MozIccManager.STK_CMD_REFRESH, "typeOfCommand"); + is(aCommand.commandQualifier, aExpect.commandQualifier, "commandQualifier"); +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("refresh_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testRefresh(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); + + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_select_item.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_select_item.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_select_item.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_select_item.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,347 +1,271 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -MARIONETTE_HEAD_JS = "stk_helper.js"; +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; -function testSelectItem(command, expect) { - log("STK CMD " + JSON.stringify(command)); - is(command.typeOfCommand, iccManager.STK_CMD_SELECT_ITEM, expect.name); - is(command.commandQualifier, expect.commandQualifier, expect.name); - is(command.options.title, expect.title, expect.name); - for (let index in command.options.items) { - is(command.options.items[index].identifier, expect.items[index].identifier, expect.name); - is(command.options.items[index].text, expect.items[index].text, expect.name); - - let itemIcons = command.options.items[index].icons; - if (itemIcons) { - isIcons(itemIcons, expect.items[index].icons, expect.name); - - let iconSelfExplanatory = command.options.items[index].iconSelfExplanatory; - is(iconSelfExplanatory, expect.items[index].iconSelfExplanatory, expect.name); - } - } - - let icons = command.options.icons; - if (icons) { - isIcons(icons, expect.icons, expect.name); - - let iconSelfExplanatory = command.options.iconSelfExplanatory; - is(iconSelfExplanatory, expect.iconSelfExplanatory, expect.name); - } - - let length = command.options.nextActionList ? command.options.nextActionList.length : 0; - for (let i = 0; i < length; i++) { - is(command.options.nextActionList[i], expect.nextActionList[i], expect.name); - } - - runNextTest(); -} - -let tests = [ +const TEST_DATA = [ {command: "d03d810301240082028182850e546f6f6c6b69742053656c6563748f07014974656d20318f07024974656d20328f07034974656d20338f07044974656d2034", - func: testSelectItem, - expect: {name: "select_item_cmd_1", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}, {identifier: 4, text: "Item 4"}]}}, {command: "d081fc810301240082028182850a4c617267654d656e75318f05505a65726f8f044f4f6e658f044e54776f8f064d54687265658f054c466f75728f054b466976658f044a5369788f0649536576656e8f064845696768748f05474e696e658f0646416c7068618f0645427261766f8f0844436861726c69658f064344656c74618f05424563686f8f0941466f782d74726f748f0640426c61636b8f063f42726f776e8f043e5265648f073d4f72616e67658f073c59656c6c6f778f063b477265656e8f053a426c75658f073956696f6c65748f0538477265798f063757686974658f06366d696c6c698f06356d6963726f8f05346e616e6f8f05337069636f", - func: testSelectItem, - expect: {name: "select_item_cmd_2", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "LargeMenu1", items: [{identifier: 80, text: "Zero"}, {identifier: 79, text: "One"}, {identifier: 78, text: "Two"}, {identifier: 77, text: "Three"}, {identifier: 76, text: "Four"}, {identifier: 75, text: "Five"}, {identifier: 74, text: "Six"}, {identifier: 73, text: "Seven"}, {identifier: 72, text: "Eight"}, {identifier: 71, text: "Nine"}, {identifier: 70, text: "Alpha"}, {identifier: 69, text: "Bravo"}, {identifier: 68, text: "Charlie"}, {identifier: 67, text: "Delta"}, {identifier: 66, text: "Echo"}, {identifier: 65, text: "Fox-trot"}, {identifier: 64, text: "Black"}, {identifier: 63, text: "Brown"}, {identifier: 62, text: "Red"}, {identifier: 61, text: "Orange"}, {identifier: 60, text: "Yellow"}, {identifier: 59, text: "Green"}, {identifier: 58, text: "Blue"}, {identifier: 57, text: "Violet"}, {identifier: 56, text: "Grey"}, {identifier: 55, text: "White"}, {identifier: 54, text: "milli"}, {identifier: 53, text: "micro"}, {identifier: 52, text: "nano"}, {identifier: 51, text: "pico"}]}}, {command: "d081fb810301240082028182850a4c617267654d656e75328f1eff43616c6c20466f7277617264696e6720556e636f6e646974696f6e616c8f1dfe43616c6c20466f7277617264696e67204f6e205573657220427573798f1cfd43616c6c20466f7277617264696e67204f6e204e6f205265706c798f26fc43616c6c20466f7277617264696e67204f6e2055736572204e6f7420526561636861626c658f1efb42617272696e67204f6620416c6c204f7574676f696e672043616c6c738f2cfa42617272696e67204f6620416c6c204f7574676f696e6720496e7465726e6174696f6e616c2043616c6c738f11f9434c492050726573656e746174696f6e", - func: testSelectItem, - expect: {name: "select_item_cmd_3", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "LargeMenu2", items: [{identifier: 255, text: "Call Forwarding Unconditional"}, {identifier: 254, text: "Call Forwarding On User Busy"}, {identifier: 253, text: "Call Forwarding On No Reply"}, {identifier: 252, text: "Call Forwarding On User Not Reachable"}, {identifier: 251, text: "Barring Of All Outgoing Calls"}, {identifier: 250, text: "Barring Of All Outgoing International Calls"}, {identifier: 249, text: "CLI Presentation"}]}}, {command: "d022810301240082028182850b53656c656374204974656d8f04114f6e658f041254776f", - func: testSelectItem, - expect: {name: "select_item_cmd_4", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Select Item", items: [{identifier: 17, text: "One"}, {identifier: 18, text: "Two"}]}}, {command: "d081fd8103012400820281828581ed5468652053494d207368616c6c20737570706c79206120736574206f66206974656d732066726f6d207768696368207468652075736572206d61792063686f6f7365206f6e652e2045616368206974656d20636f6d70726973657320612073686f7274206964656e74696669657220287573656420746f20696e646963617465207468652073656c656374696f6e2920616e642061207465787420737472696e672e204f7074696f6e616c6c79207468652053494d206d617920696e636c75646520616e20616c706861206964656e7469666965722e2054686520616c706861206964656e74696669657220698f020159", - func: testSelectItem, - expect: {name: "select_item_cmd_5", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "The SIM shall supply a set of items from which the user may choose one. Each item comprises a short identifier (used to indicate the selection) and a text string. Optionally the SIM may include an alpha identifier. The alpha identifier i", items: [{identifier: 1, text: "Y"}]}}, {command: "d081f3810301240082028182850a304c617267654d656e758f1dff312043616c6c20466f727761726420556e636f6e646974696f6e616c8f1cfe322043616c6c20466f7277617264204f6e205573657220427573798f1bfd332043616c6c20466f7277617264204f6e204e6f205265706c798f25fc342043616c6c20466f7277617264204f6e2055736572204e6f7420526561636861626c658f20fb352042617272696e67204f6620416c6c204f7574676f696e672043616c6c738f24fa362042617272696e67204f6620416c6c204f7574676f696e6720496e742043616c6c738f13f93720434c492050726573656e746174696f6e", - func: testSelectItem, - expect: {name: "select_item_cmd_6", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "0LargeMenu", items: [{identifier: 255, text: "1 Call Forward Unconditional"}, {identifier: 254, text: "2 Call Forward On User Busy"}, {identifier: 253, text: "3 Call Forward On No Reply"}, {identifier: 252, text: "4 Call Forward On User Not Reachable"}, {identifier: 251, text: "5 Barring Of All Outgoing Calls"}, {identifier: 250, text: "6 Barring Of All Outgoing Int Calls"}, {identifier: 249, text: "7 CLI Presentation"}]}}, {command: "d039810301240082028182850e546f6f6c6b69742053656c6563748f07014974656d20318f07024974656d20328f07034974656d20331803131026", - func: testSelectItem, - expect: {name: "select_item_cmd_7", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}], - nextActionList: [iccManager.STK_CMD_SEND_SMS, iccManager.STK_CMD_SET_UP_CALL, iccManager.STK_CMD_PROVIDE_LOCAL_INFO]}}, + nextActionList: [MozIccManager.STK_CMD_SEND_SMS, MozIccManager.STK_CMD_SET_UP_CALL, MozIccManager.STK_CMD_PROVIDE_LOCAL_INFO]}}, {command: "d037810301240082028182850e546f6f6c6b69742053656c6563748f07014974656d20318f07024974656d20328f07034974656d2033900102", - func: testSelectItem, - expect: {name: "select_item_cmd_8", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, {command: "d034810301248082028182850e546f6f6c6b69742053656c6563748f07014974656d20318f07024974656d20328f07034974656d2033", - func: testSelectItem, - expect: {name: "select_item_cmd_9", - commandQualifier: 0x80, + expect: {commandQualifier: 0x80, title: "Toolkit Select", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, {command: "d03e810301240082028182850e546f6f6c6b69742053656c6563748f07014974656d20318f07024974656d20328f07034974656d20339e0201019f0401030303", - func: testSelectItem, - expect: {name: "select_item_cmd_10", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select", iconSelfExplanatory: false, - icons: [basicIcon], - items: [{identifier: 1, text: "Item 1", iconSelfExplanatory: false, icons: [colorIcon]}, - {identifier: 2, text: "Item 2", iconSelfExplanatory: false, icons: [colorIcon]}, - {identifier: 3, text: "Item 3", iconSelfExplanatory: false, icons: [colorIcon]}]}}, + icons: [BASIC_ICON], + items: [{identifier: 1, text: "Item 1", iconSelfExplanatory: false, icons: [COLOR_ICON]}, + {identifier: 2, text: "Item 2", iconSelfExplanatory: false, icons: [COLOR_ICON]}, + {identifier: 3, text: "Item 3", iconSelfExplanatory: false, icons: [COLOR_ICON]}]}}, {command: "d03e810301240082028182850e546f6f6c6b69742053656c6563748f07014974656d20318f07024974656d20328f07034974656d20339e0200019f0400050505", - func: testSelectItem, - expect: {name: "select_item_cmd_11", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select", iconSelfExplanatory: true, - icons: [basicIcon], - items: [{identifier: 1, text: "Item 1", iconSelfExplanatory: true, icons: [colorTransparencyIcon]}, - {identifier: 2, text: "Item 2", iconSelfExplanatory: true, icons: [colorTransparencyIcon]}, - {identifier: 3, text: "Item 3", iconSelfExplanatory: true, icons: [colorTransparencyIcon]}]}}, + icons: [BASIC_ICON], + items: [{identifier: 1, text: "Item 1", iconSelfExplanatory: true, icons: [COLOR_TRANSPARENCY_ICON]}, + {identifier: 2, text: "Item 2", iconSelfExplanatory: true, icons: [COLOR_TRANSPARENCY_ICON]}, + {identifier: 3, text: "Item 3", iconSelfExplanatory: true, icons: [COLOR_TRANSPARENCY_ICON]}]}}, {command: "d034810301240382028182850e546f6f6c6b69742053656c6563748f07014974656d20318f07024974656d20328f07034974656d2033", - func: testSelectItem, - expect: {name: "select_item_cmd_12", - commandQualifier: 0x03, + expect: {commandQualifier: 0x03, title: "Toolkit Select", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, {command: "d034810301240182028182850e546f6f6c6b69742053656c6563748f07014974656d20318f07024974656d20328f07034974656d2033", - func: testSelectItem, - expect: {name: "select_item_cmd_13", - commandQualifier: 0x01, + expect: {commandQualifier: 0x01, title: "Toolkit Select", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, {command: "d02b810301240482028182850e546f6f6c6b69742053656c6563748f07014974656d20318f07024974656d2032", - func: testSelectItem, - expect: {name: "select_item_cmd_14", - commandQualifier: 0x04, + expect: {commandQualifier: 0x04, title: "Toolkit Select", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}]}}, {command: "d030810301240082028182850a3c54494d452d4f55543e8f07014974656d20318f07024974656d20328f07034974656d2033", - func: testSelectItem, - expect: {name: "select_item_cmd_15", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, {command: "d03d8103012400820281828510546f6f6c6b69742053656c65637420318f07014974656d20318f07024974656d2032d004001000b4d108000600b4000600b4", - func: testSelectItem, - expect: {name: "select_item_cmd_16", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}]}}, {command: "d02d8103012400820281828510546f6f6c6b69742053656c65637420328f07014974656d20338f07024974656d2034", - func: testSelectItem, - expect: {name: "select_item_cmd_17", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 2", items: [{identifier: 1, text: "Item 3"}, {identifier: 2, text: "Item 4"}]}}, {command: "d03d8103012400820281828510546f6f6c6b69742053656c65637420318f07014974656d20318f07024974656d2032d004001001b4d108000601b4000601b4", - func: testSelectItem, - expect: {name: "select_item_cmd_18", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}]}}, {command: "d02d8103012400820281828510546f6f6c6b69742053656c65637420328f07014974656d20338f07024974656d2034", - func: testSelectItem, - expect: {name: "select_item_cmd_19", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 2", items: [{identifier: 1, text: "Item 3"}, {identifier: 2, text: "Item 4"}]}}, {command: "d03d8103012400820281828510546f6f6c6b69742053656c65637420318f07014974656d20318f07024974656d2032d004001002b4d108000602b4000602b4", - func: testSelectItem, - expect: {name: "select_item_cmd_20", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}]}}, {command: "d02d8103012400820281828510546f6f6c6b69742053656c65637420328f07014974656d20338f07024974656d2034", - func: testSelectItem, - expect: {name: "select_item_cmd_21", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 2", items: [{identifier: 1, text: "Item 3"}, {identifier: 2, text: "Item 4"}]}}, {command: "d03d8103012400820281828510546f6f6c6b69742053656c65637420318f07014974656d20318f07024974656d2032d004001004b4d108000604b4000604b4", - func: testSelectItem, - expect: {name: "select_item_cmd_22", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}]}}, {command: "d03d8103012400820281828510546f6f6c6b69742053656c65637420328f07014974656d20338f07024974656d2034d004001000b4d108000600b4000600b4", - func: testSelectItem, - expect: {name: "select_item_cmd_23", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 2", items: [{identifier: 1, text: "Item 3"}, {identifier: 2, text: "Item 4"}]}}, {command: "d02d8103012400820281828510546f6f6c6b69742053656c65637420338f07014974656d20358f07024974656d2036", - func: testSelectItem, - expect: {name: "select_item_cmd_24", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 3", items: [{identifier: 1, text: "Item 5"}, {identifier: 2, text: "Item 6"}]}}, {command: "d03d8103012400820281828510546f6f6c6b69742053656c65637420318f07014974656d20318f07024974656d2032d004001008b4d108000608b4000608b4", - func: testSelectItem, - expect: {name: "select_item_cmd_25", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}]}}, {command: "d03d8103012400820281828510546f6f6c6b69742053656c65637420328f07014974656d20338f07024974656d2034d004001000b4d108000600b4000600b4", - func: testSelectItem, - expect: {name: "select_item_cmd_26", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 2", items: [{identifier: 1, text: "Item 3"}, {identifier: 2, text: "Item 4"}]}}, {command: "d02d8103012400820281828510546f6f6c6b69742053656c65637420338f07014974656d20358f07024974656d2036", - func: testSelectItem, - expect: {name: "select_item_cmd_27", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 3", items: [{identifier: 1, text: "Item 5"}, {identifier: 2, text: "Item 6"}]}}, {command: "d03d8103012400820281828510546f6f6c6b69742053656c65637420318f07014974656d20318f07024974656d2032d004001010b4d108000610b4000610b4", - func: testSelectItem, - expect: {name: "select_item_cmd_28", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}]}}, {command: "d03d8103012400820281828510546f6f6c6b69742053656c65637420328f07014974656d20338f07024974656d2034d004001000b4d108000600b4000600b4", - func: testSelectItem, - expect: {name: "select_item_cmd_29", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 2", items: [{identifier: 1, text: "Item 3"}, {identifier: 2, text: "Item 4"}]}}, {command: "d02d8103012400820281828510546f6f6c6b69742053656c65637420338f07014974656d20358f07024974656d2036", - func: testSelectItem, - expect: {name: "select_item_cmd_30", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 3", items: [{identifier: 1, text: "Item 5"}, {identifier: 2, text: "Item 6"}]}}, {command: "d03d8103012400820281828510546f6f6c6b69742053656c65637420318f07014974656d20318f07024974656d2032d004001020b4d108000620b4000620b4", - func: testSelectItem, - expect: {name: "select_item_cmd_31", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}]}}, {command: "d03d8103012400820281828510546f6f6c6b69742053656c65637420328f07014974656d20338f07024974656d2034d004001000b4d108000600b4000600b4", - func: testSelectItem, - expect: {name: "select_item_cmd_32", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 2", items: [{identifier: 1, text: "Item 3"}, {identifier: 2, text: "Item 4"}]}}, {command: "d02d8103012400820281828510546f6f6c6b69742053656c65637420338f07014974656d20358f07024974656d2036", - func: testSelectItem, - expect: {name: "select_item_cmd_33", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 3", items: [{identifier: 1, text: "Item 5"}, {identifier: 2, text: "Item 6"}]}}, {command: "d03d8103012400820281828510546f6f6c6b69742053656c65637420318f07014974656d20318f07024974656d2032d004001040b4d108000640b4000640b4", - func: testSelectItem, - expect: {name: "select_item_cmd_34", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}]}}, {command: "d03d8103012400820281828510546f6f6c6b69742053656c65637420328f07014974656d20338f07024974656d2034d004001000b4d108000600b4000600b4", - func: testSelectItem, - expect: {name: "select_item_cmd_35", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 2", items: [{identifier: 1, text: "Item 3"}, {identifier: 2, text: "Item 4"}]}}, {command: "d02d8103012400820281828510546f6f6c6b69742053656c65637420338f07014974656d20358f07024974656d2036", - func: testSelectItem, - expect: {name: "select_item_cmd_36", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 3", items: [{identifier: 1, text: "Item 5"}, {identifier: 2, text: "Item 6"}]}}, {command: "d03d8103012400820281828510546f6f6c6b69742053656c65637420318f07014974656d20318f07024974656d2032d004001080b4d108000680b4000680b4", - func: testSelectItem, - expect: {name: "select_item_cmd_37", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}]}}, {command: "d03d8103012400820281828510546f6f6c6b69742053656c65637420328f07014974656d20338f07024974656d2034d004001000b4d108000600b4000600b4", - func: testSelectItem, - expect: {name: "select_item_cmd_38", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 2", items: [{identifier: 1, text: "Item 3"}, {identifier: 2, text: "Item 4"}]}}, {command: "d02d8103012400820281828510546f6f6c6b69742053656c65637420338f07014974656d20358f07024974656d2036", - func: testSelectItem, - expect: {name: "select_item_cmd_39", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 3", items: [{identifier: 1, text: "Item 5"}, {identifier: 2, text: "Item 6"}]}}, {command: "d03d8103012400820281828510546f6f6c6b69742053656c65637420318f07014974656d20318f07024974656d2032d004001000b4d108000600b4000600b4", - func: testSelectItem, - expect: {name: "select_item_cmd_40", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}]}}, {command: "d02d8103012400820281828510546f6f6c6b69742053656c65637420328f07014974656d20338f07024974656d2034", - func: testSelectItem, - expect: {name: "select_item_cmd_41", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select 2", items: [{identifier: 1, text: "Item 3"}, {identifier: 2, text: "Item 4"}]}}, {command: "d07e8103012400820281828519800417041404200410041204210422041204230419042204158f1c018004170414042004100412042104220412042304190422041500318f1c028004170414042004100412042104220412042304190422041500328f1c03800417041404200410041204210422041204230419042204150033", - func: testSelectItem, - expect: {name: "select_item_cmd_42", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "ЗДРАВСТВУЙТЕ", items: [{identifier: 1, text: "ЗДРАВСТВУЙТЕ1"}, {identifier: 2, text: "ЗДРАВСТВУЙТЕ2"}, {identifier: 3, text: "ЗДРАВСТВУЙТЕ3"}]}}, {command: "d053810301240082028182850f810c089794a09092a1a292a399a2958f1101810d089794a09092a1a292a399a295318f1102810d089794a09092a1a292a399a295328f1103810d089794a09092a1a292a399a29533", - func: testSelectItem, - expect: {name: "select_item_cmd_43", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "ЗДРАВСТВУЙТЕ", items: [{identifier: 1, text: "ЗДРАВСТВУЙТЕ1"}, {identifier: 2, text: "ЗДРАВСТВУЙТЕ2"}, {identifier: 3, text: "ЗДРАВСТВУЙТЕ3"}]}}, {command: "d0578103012400820281828510820c04108784908082919282938992858f1201820d0410878490808291928293899285318f1202820d0410878490808291928293899285328f1203820d041087849080829192829389928533", - func: testSelectItem, - expect: {name: "select_item_cmd_44", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "ЗДРАВСТВУЙТЕ", items: [{identifier: 1, text: "ЗДРАВСТВУЙТЕ1"}, {identifier: 2, text: "ЗДРАВСТВУЙТЕ2"}, {identifier: 3, text: "ЗДРАВСТВУЙТЕ3"}]}}, {command: "d03e810301240082028182850b805de551777bb1900962e98f080180987976ee4e008f080280987976ee4e8c8f080380987976ee4e098f080480987976ee56db", - func: testSelectItem, - expect: {name: "select_item_cmd_45", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "工具箱选择", items: [{identifier: 1, text: "项目一"}, {identifier: 2, text: "项目二"}, {identifier: 3, text: "项目三"}, {identifier: 4, text: "项目四"}]}}, {command: "d0388103012400820281828509800038003030eb00308f0a01800038003030eb00318f0a02800038003030eb00328f0a03800038003030eb0033", - func: testSelectItem, - expect: {name: "select_item_cmd_46", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "80ル0", items: [{identifier: 1, text: "80ル1"}, {identifier: 2, text: "80ル2"}, {identifier: 3, text: "80ル3"}]}}, {command: "d03081030124008202818285078104613831eb308f08018104613831eb318f08028104613831eb328f08038104613831eb33", - func: testSelectItem, - expect: {name: "select_item_cmd_47", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "81ル0", items: [{identifier: 1, text: "81ル1"}, {identifier: 2, text: "81ル2"}, {identifier: 3, text: "81ル3"}]}}, {command: "d0348103012400820281828508820430a03832cb308f0901820430a03832cb318f0902820430a03832cb328f0903820430a03832cb33", - func: testSelectItem, - expect: {name: "select_item_cmd_48", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "82ル0", items: [{identifier: 1, text: "82ル1"}, {identifier: 2, text: "82ル2"}, {identifier: 3, text: "82ル3"}]}}, {command: "d039810301240082028182850e546f6f6c6b69742053656c6563748f07014974656d20318f07024974656d20328f07034974656d20331803000081", - func: testSelectItem, - expect: {name: "select_item_cmd_49", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Select", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}], - nextActionList: [iccManager.STK_NEXT_ACTION_NULL, iccManager.STK_NEXT_ACTION_NULL, iccManager.STK_NEXT_ACTION_END_PROACTIVE_SESSION]}}, + nextActionList: [MozIccManager.STK_NEXT_ACTION_NULL, MozIccManager.STK_NEXT_ACTION_NULL, MozIccManager.STK_NEXT_ACTION_END_PROACTIVE_SESSION]}}, ]; -runNextTest(); +function testSelectItem(aCommand, aExpect) { + is(aCommand.typeOfCommand, MozIccManager.STK_CMD_SELECT_ITEM, "typeOfCommand"); + is(aCommand.commandQualifier, aExpect.commandQualifier, "commandQualifier"); + is(aCommand.options.title, aExpect.title, "options.title"); + is(aCommand.options.presentationType, aCommand.commandQualifier & 0x03, "presentationType"); + + for (let index in aExpect.items) { + let item = aCommand.options.items[index]; + let itemExpect = aExpect.items[index]; + is(item.identifier, itemExpect.identifier, + "options.items[" + index + "].identifier"); + is(item.text, itemExpect.text, + "options.items[" + index + "].text"); + + if (itemExpect.icons) { + isIcons(item.icons, itemExpect.icons); + is(item.iconSelfExplanatory, itemExpect.iconSelfExplanatory, + "options.items[" + index + "].iconSelfExplanatory"); + } + } + + if (aExpect.icons) { + isIcons(aCommand.options.icons, aExpect.icons); + is(aCommand.options.iconSelfExplanatory, aExpect.iconSelfExplanatory, + "options.iconSelfExplanatory"); + } + + if (aExpect.nextActionList) { + for (let index in aExpect.nextActionList) { + is(aCommand.options.nextActionList[index], aExpect.nextActionList[index], + "options.nextActionList[" + index + "]"); + } + } +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("select_item_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testSelectItem(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); + + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_send_dtmf.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_send_dtmf.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_send_dtmf.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_send_dtmf.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,195 +1,138 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -MARIONETTE_HEAD_JS = "stk_helper.js"; +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; -function testSendDTMF(command, expect) { - log("STK CMD " + JSON.stringify(command)); - is(command.typeOfCommand, iccManager.STK_CMD_SEND_DTMF, expect.name); - is(command.commandQualifier, expect.commandQualifier, expect.name); - is(command.options.text, expect.text, expect.name); - - let icons = command.options.icons; - if (icons) { - isIcons(icons, expect.icons, expect.name); - - let iconSelfExplanatory = command.options.iconSelfExplanatory; - is(iconSelfExplanatory, expect.iconSelfExplanatory, expect.name); - } - - runNextTest(); -} - -let tests = [ +const TEST_DATA = [ {command: "d01b810301140082028183850953656e642044544d46ac052143658709", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_1_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Send DTMF"}}, {command: "d010810301140082028183ac052143658709", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_1_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d0138103011400820281838500ac06c1cccccccc2c", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_2_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: ""}}, {command: "d011810301140082028183ac06c1cccccccc2c", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_2_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d01d810301140082028183850a42617369632049636f6eac02c1f29e020001", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_3_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Basic Icon", iconSelfExplanatory: true, - icons: [basicIcon]}}, + icons: [BASIC_ICON]}}, {command: "d011810301140082028183ac02c1f29e020005", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_3_without_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, iconSelfExplanatory: true, - icons: [colorTransparencyIcon]}}, + icons: [COLOR_TRANSPARENCY_ICON]}}, {command: "d01c810301140082028183850953656e642044544d46ac02c1f29e020101", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_4_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Send DTMF", iconSelfExplanatory: false, - icons: [basicIcon]}}, + icons: [BASIC_ICON]}}, {command: "d011810301140082028183ac02c1f29e020105", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_4_without_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, iconSelfExplanatory: false, - icons: [colorTransparencyIcon]}}, + icons: [COLOR_TRANSPARENCY_ICON]}}, {command: "d028810301140082028183851980041704140420041004120421042204120423041904220415ac02c1f2", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_5_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "ЗДРАВСТВУЙТЕ"}}, {command: "d00d810301140082028183ac02c1f2", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_5_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d023810301140082028183850b53656e642044544d462031ac052143658709d004000b00b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_6_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Send DTMF 1"}}, {command: "d016810301140082028183ac052143658709d004000b00b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_6_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d01d810301140082028183850b53656e642044544d462032ac052143658709", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_7_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Send DTMF 2"}}, - // send_dtmf_cmd_7_without_alpha_identifier has the same pdu as - // send_dtmf_cmd_1_without_alpha_identifier. {command: "d023810301140082028183850b53656e642044544d462031ac052143658709d004000b01b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_8_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Send DTMF 1"}}, {command: "d016810301140082028183ac052143658709d004000b01b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_8_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d023810301140082028183850b53656e642044544d462031ac052143658709d00400b002b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_9_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Send DTMF 1"}}, {command: "d016810301140082028183ac052143658709d00400b002b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_9_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d023810301140082028183850b53656e642044544d462031ac052143658709d004000b04b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_10_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Send DTMF 1"}}, {command: "d016810301140082028183ac052143658709d004000b04b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_10_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d023810301140082028183850b53656e642044544d462032ac052143658709d004000b00b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_11_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Send DTMF 2"}}, - // send_dtmf_cmd_11_without_alpha_identifier has the same pdu as - // send_dtmf_cmd_6_without_alpha_identifier. {command: "d01d810301140082028183850b53656e642044544d462033ac052143658709", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_12_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Send DTMF 3"}}, - // send_dtmf_cmd_12_without_alpha_identifier has the same pdu as - // send_dtmf_cmd_1_without_alpha_identifier. {command: "d023810301140082028183850b53656e642044544d462031ac052143658709d004000b08b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_13_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Send DTMF 1"}}, {command: "d016810301140082028183ac052143658709d004000b08b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_13_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d023810301140082028183850b53656e642044544d462031ac052143658709d004000b10b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_14_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Send DTMF 1"}}, {command: "d016810301140082028183ac052143658709d004000b10b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_14_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d023810301140082028183850b53656e642044544d462031ac052143658709d004000b20b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_15_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Send DTMF 1"}}, {command: "d016810301140082028183ac052143658709d004000b20b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_15_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d023810301140082028183850b53656e642044544d462031ac052143658709d004000b40b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_16_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Send DTMF 1"}}, {command: "d016810301140082028183ac052143658709d004000b40b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_16_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d023810301140082028183850b53656e642044544d462031ac052143658709d004000b80b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_17_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Send DTMF 1"}}, {command: "d016810301140082028183ac052143658709d004000b80b4", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_17_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d0148103011400820281838505804f60597dac02c1f2", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_18_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "你好"}}, - // send_dtmf_cmd_18_without_alpha_identifier has the same pdu as - // send_dtmf_cmd_5_without_alpha_identifier. {command: "d01281030114008202818385038030ebac02c1f2", - func: testSendDTMF, - expect: {name: "send_dtmf_cmd_19_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "ル"}} - // send_dtmf_cmd_19_without_alpha_identifier has the same pdu as - // send_dtmf_cmd_5_without_alpha_identifier. ]; -runNextTest(); +function testSendDTMF(aCommand, aExpect) { + is(aCommand.typeOfCommand, MozIccManager.STK_CMD_SEND_DTMF, "typeOfCommand"); + is(aCommand.commandQualifier, aExpect.commandQualifier, "commandQualifier"); + + if (aExpect.text) { + is(aCommand.options.text, aExpect.text, "options.text"); + } + + if (aExpect.icons) { + isIcons(aCommand.options.icons, aExpect.icons); + is(aCommand.options.iconSelfExplanatory, aExpect.iconSelfExplanatory, + "options.iconSelfExplanatory"); + } +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("send_dtmf_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testSendDTMF(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); + + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_send_sms.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_send_sms.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_send_sms.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_send_sms.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,281 +1,185 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -MARIONETTE_HEAD_JS = "stk_helper.js"; +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; -function testSendSMS(command, expect) { - log("STK CMD " + JSON.stringify(command)); - is(command.typeOfCommand, iccManager.STK_CMD_SEND_SMS, expect.name); - is(command.commandQualifier, expect.commandQualifier, expect.name); - is(command.options.text, expect.title, expect.name); - - let icons = command.options.icons; - if (icons) { - isIcons(icons, expect.icons, expect.name); - - let iconSelfExplanatory = command.options.iconSelfExplanatory; - is(iconSelfExplanatory, expect.iconSelfExplanatory, expect.name); - } - - runNextTest(); -} - -let tests = [ +const TEST_DATA = [ {command: "d037810301130082028183850753656e6420534d86099111223344556677f88b180100099110325476f840f40c54657374204d657373616765", - func: testSendSMS, - expect: {name: "send_sms_cmd_1_with_alpha_identifier", - commandQualifier: 0x00, - title: "Send SM"}}, + expect: {commandQualifier: 0x00, + text: "Send SM"}}, {command: "d02e81030113008202818386099111223344556677f88b180100099110325476f840f40c54657374204d657373616765", - func: testSendSMS, - expect: {name: "send_sms_cmd_1_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d032810301130182028183850753656e6420534d86099111223344556677f88b130100099110325476f840f40753656e6420534d", - func: testSendSMS, - expect: {name: "send_sms_cmd_2_with_alpha_identifier", - commandQualifier: 0x01, - title: "Send SM"}}, + expect: {commandQualifier: 0x01, + text: "Send SM"}}, {command: "d02981030113018202818386099111223344556677f88b130100099110325476f840f40753656e6420534d", - func: testSendSMS, - expect: {name: "send_sms_cmd_2_without_alpha_identifier", - commandQualifier: 0x01}}, + expect: {commandQualifier: 0x01}}, {command: "d03d810301130082028183850d53686f7274204d65737361676586099111223344556677f88b180100099110325476f840f00d53f45b4e0735cbf379f85c06", - func: testSendSMS, - expect: {name: "send_sms_cmd_3_with_alpha_identifier", - commandQualifier: 0x00, - title: "Short Message"}}, + expect: {commandQualifier: 0x00, + text: "Short Message"}}, {command: "d02e81030113008202818386099111223344556677f88b180100099110325476f840f00d53f45b4e0735cbf379f85c06", - func: testSendSMS, - expect: {name: "send_sms_cmd_3_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d081fd810301130182028183853854686520616464726573732064617461206f626a65637420686f6c6473207468652052501144657374696e6174696f6e114164647265737386099111223344556677f88b81ac0100099110325476f840f4a054776f2074797065732061726520646566696e65643a202d20412073686f7274206d65737361676520746f2062652073656e7420746f20746865206e6574776f726b20696e20616e20534d532d5355424d4954206d6573736167652c206f7220616e20534d532d434f4d4d414e44206d6573736167652c20776865726520746865207573657220646174612063616e20626520706173736564207472616e7370", - func: testSendSMS, - expect: {name: "send_sms_cmd_4_with_alpha_identifier", - commandQualifier: 0x01, - title: "The address data object holds the RP_Destination_Address"}}, + expect: {commandQualifier: 0x01, + text: "The address data object holds the RP_Destination_Address"}}, {command: "d081c381030113018202818386099111223344556677f88b81ac0100099110325476f840f4a054776f2074797065732061726520646566696e65643a202d20412073686f7274206d65737361676520746f2062652073656e7420746f20746865206e6574776f726b20696e20616e20534d532d5355424d4954206d6573736167652c206f7220616e20534d532d434f4d4d414e44206d6573736167652c20776865726520746865207573657220646174612063616e20626520706173736564207472616e7370", - func: testSendSMS, - expect: {name: "send_sms_cmd_4_without_alpha_identifier", - commandQualifier: 0x01}}, + expect: {commandQualifier: 0x01}}, {command: "d081e9810301130082028183853854686520616464726573732064617461206f626a65637420686f6c6473207468652052502044657374696e6174696f6e204164647265737386099111223344556677f88b81980100099110325476f840f0a0d4fb1b44cfc3cb7350585e0691cbe6b4bb4cd6815aa020688e7ecbe9a076793e0f9fcb20fa1b242e83e665371d447f83e8e832c85da6dfdff23528ed0685dda06973da9a5685cd2415d42ecfe7e17399057acb41613768da9cb686cf6633e82482dae5f93c7c2eb3407774595e06d1d165507d5e9683c8617a18340ebb41e232081e9ecfcb64105d1e76cfe1", - func: testSendSMS, - expect: {name: "send_sms_cmd_5_with_alpha_identifier", - commandQualifier: 0x00, - title: "The address data object holds the RP Destination Address"}}, + expect: {commandQualifier: 0x00, + text: "The address data object holds the RP Destination Address"}}, {command: "d081af81030113008202818386099111223344556677f88b81980100099110325476f840f0a0d4fb1b44cfc3cb7350585e0691cbe6b4bb4cd6815aa020688e7ecbe9a076793e0f9fcb20fa1b242e83e665371d447f83e8e832c85da6dfdff23528ed0685dda06973da9a5685cd2415d42ecfe7e17399057acb41613768da9cb686cf6633e82482dae5f93c7c2eb3407774595e06d1d165507d5e9683c8617a18340ebb41e232081e9ecfcb64105d1e76cfe1", - func: testSendSMS, - expect: {name: "send_sms_cmd_5_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d081fd8103011300820281838581e654776f2074797065732061726520646566696e65643a202d20412073686f7274206d65737361676520746f2062652073656e7420746f20746865206e6574776f726b20696e20616e20534d532d5355424d4954206d6573736167652c206f7220616e20534d532d434f4d4d414e44206d6573736167652c20776865726520746865207573657220646174612063616e20626520706173736564207472616e73706172656e746c793b202d20412073686f7274206d65737361676520746f2062652073656e7420746f20746865206e6574776f726b20696e20616e20534d532d5355424d4954208b09010002911040f00120", - func: testSendSMS, - expect: {name: "send_sms_cmd_6_with_alpha_identifier", - commandQualifier: 0x00, - title: "Two types are defined: - A short message to be sent to the network in an SMS-SUBMIT message, or an SMS-COMMAND message, where the user data can be passed transparently; - A short message to be sent to the network in an SMS-SUBMIT "}}, + expect: {commandQualifier: 0x00, + text: "Two types are defined: - A short message to be sent to the network in an SMS-SUBMIT message, or an SMS-COMMAND message, where the user data can be passed transparently; - A short message to be sent to the network in an SMS-SUBMIT "}}, {command: "d0148103011300820281838b09010002911040f00120", - func: testSendSMS, - expect: {name: "send_sms_cmd_6_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d030810301130082028183850086099111223344556677f88b180100099110325476f840f40c54657374204d657373616765", func: testSendSMS, - expect: {name: "send_sms_cmd_7_with_alpha_identifier", - commandQualifier: 0x00, - title: ""}}, - // send_sms_cmd_7_without_alpha_identifier has the same pdu as - // send_sms_cmd_1_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: ""}}, {command: "d05581030113008202818385198004170414042004100412042104220412042304190422041586099111223344556677f88b240100099110325476f8400818041704140420041004120421042204120423041904220415", - func: testSendSMS, - expect: {name: "send_sms_cmd_8_with_alpha_identifier", - commandQualifier: 0x00, - title: "ЗДРАВСТВУЙТЕ"}}, + expect: {commandQualifier: 0x00, + text: "ЗДРАВСТВУЙТЕ"}}, {command: "d03a81030113008202818386099111223344556677f88b240100099110325476f8400818041704140420041004120421042204120423041904220415", - func: testSendSMS, - expect: {name: "send_sms_cmd_8_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d04b810301130082028183850f810c089794a09092a1a292a399a29586099111223344556677f88b240100099110325476f8400818041704140420041004120421042204120423041904220415", - func: testSendSMS, - expect: {name: "send_sms_cmd_9_with_alpha_identifier", - commandQualifier: 0x00, - title: "ЗДРАВСТВУЙТЕ"}}, - // send_sms_cmd_9_without_alpha_identifier has the same pdu as - // send_sms_cmd_8_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "ЗДРАВСТВУЙТЕ"}}, {command: "d03b81030113008202818385074e4f2049434f4e86099111223344556677f88b180100099110325476f840f40c54657374204d6573736167659e020002", - func: testSendSMS, - expect: {name: "send_sms_cmd_10_with_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, // The record number 02 in EFimg is not defined, so no icon will be // shown, but the text string should still be displayed. - title: "NO ICON"}}, + text: "NO ICON"}}, {command: "d03281030113008202818386099111223344556677f88b180100099110325476f840f40c54657374204d6573736167659e020001", - func: testSendSMS, - expect: {name: "send_sms_cmd_10_without_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, iconSelfExplanatory: true, - icons: [basicIcon]}}, + icons: [BASIC_ICON]}}, {command: "d03b810301130082028183850753656e6420534d86099111223344556677f88b180100099110325476f840f40c54657374204d6573736167651e020101", - func: testSendSMS, - expect: {name: "send_sms_cmd_11_with_alpha_identifier", - commandQualifier: 0x00, - title: "Send SM", + expect: {commandQualifier: 0x00, + text: "Send SM", iconSelfExplanatory: false, - icons: [basicIcon]}}, + icons: [BASIC_ICON]}}, {command: "d03281030113008202818386099111223344556677f88b180100099110325476f840f40c54657374204d6573736167651e020101", - func: testSendSMS, - expect: {name: "send_sms_cmd_11_without_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, iconSelfExplanatory: false, - icons: [basicIcon]}}, + icons: [BASIC_ICON]}}, {command: "d02c8103011300820281838510546578742041747472696275746520318b09010002911040f00120d004001000b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_12_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d01a8103011300820281838b09010002911040f00120d004001000b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_12_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d0268103011300820281838510546578742041747472696275746520328b09010002911040f00120", - func: testSendSMS, - expect: {name: "send_sms_cmd_13_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 2"}}, - // send_sms_cmd_18_without_alpha_identifier has the same pdu as - // send_sms_cmd_6_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "Text Attribute 2"}}, {command: "d02c8103011300820281838510546578742041747472696275746520318b09010002911040f00120d004001001b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_14_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d01a8103011300820281838b09010002911040f00120d004001001b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_14_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d02c8103011300820281838510546578742041747472696275746520318b09010002911040f00120d004001002b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_15_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d01a8103011300820281838b09010002911040f00120d004001002b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_15_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d02c8103011300820281838510546578742041747472696275746520318b09010002911040f00120d004001004b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_16_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d01a8103011300820281838b09010002911040f00120d004001004b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_16_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d02c8103011300820281838510546578742041747472696275746520328b09010002911040f00120d004001000b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_17_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 2"}}, - // send_sms_cmd_17_without_alpha_identifier has the same pdu as - // send_sms_cmd_12_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "Text Attribute 2"}}, {command: "d0268103011300820281838510546578742041747472696275746520338b09010002911040f00120", - func: testSendSMS, - expect: {name: "send_sms_cmd_18_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 3"}}, - // send_sms_cmd_18_without_alpha_identifier has the same pdu as - // send_sms_cmd_6_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "Text Attribute 3"}}, {command: "d02c8103011300820281838510546578742041747472696275746520318b09010002911040f00120d004001008b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_19_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d01a8103011300820281838b09010002911040f00120d004001008b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_19_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d02c8103011300820281838510546578742041747472696275746520318b09010002911040f00120d004001010b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_20_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d01a8103011300820281838b09010002911040f00120d004001010b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_20_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d02c8103011300820281838510546578742041747472696275746520318b09010002911040f00120d004001020b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_21_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d01a8103011300820281838b09010002911040f00120d004001020b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_21_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d02c8103011300820281838510546578742041747472696275746520318b09010002911040f00120d004001040b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_22_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d01a8103011300820281838b09010002911040f00120d004001040b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_22_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d02c8103011300820281838510546578742041747472696275746520318b09010002911040f00120d004001080b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_23_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d01a8103011300820281838b09010002911040f00120d004001080b4", - func: testSendSMS, - expect: {name: "send_sms_cmd_23_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d02d8103011300820281838505804e2d4e0086099111223344556677f88b100100099110325476f84008044e2d4e00", - func: testSendSMS, - expect: {name: "send_sms_cmd_24_with_alpha_identifier", - commandQualifier: 0x00, - title: "中一"}}, + expect: {commandQualifier: 0x00, + text: "中一"}}, {command: "d02681030113008202818386099111223344556677f88b100100099110325476f84008044e2d4e00", - func: testSendSMS, - expect: {name: "send_sms_cmd_24_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d02d810301130082028183850581029cad8086099111223344556677f88b100100099110325476f84008044e2d4e00", - func: testSendSMS, - expect: {name: "send_sms_cmd_25_with_alpha_identifier", - commandQualifier: 0x00, - title: "中一"}}, - // send_sms_cmd_25_without_alpha_identifier has the same pdu as - // send_sms_cmd_24_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "中一"}}, {command: "d02e810301130082028183850682024e00ad8086099111223344556677f88b100100099110325476f84008044e2d4e00", - func: testSendSMS, - expect: {name: "send_sms_cmd_26_with_alpha_identifier", - commandQualifier: 0x00, - title: "中一"}}, - // send_sms_cmd_26_without_alpha_identifier has the same pdu as - // send_sms_cmd_24_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "中一"}}, {command: "d0358103011300820281838509800038003030eb003086099111223344556677f88b140100099110325476f84008080038003030eb0031", - func: testSendSMS, - expect: {name: "send_sms_cmd_27_with_alpha_identifier", - commandQualifier: 0x00, - title: "80ル0"}}, + expect: {commandQualifier: 0x00, + text: "80ル0"}}, {command: "d02a81030113008202818386099111223344556677f88b140100099110325476f84008080038003030eb0031", - func: testSendSMS, - expect: {name: "send_sms_cmd_27_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d03381030113008202818385078104613831eb3186099111223344556677f88b140100099110325476f84008080038003030eb0032", - func: testSendSMS, - expect: {name: "send_sms_cmd_28_with_alpha_identifier", - commandQualifier: 0x00, - title: "81ル1"}}, + expect: {commandQualifier: 0x00, + text: "81ル1"}}, {command: "d02a81030113008202818386099111223344556677f88b140100099110325476f84008080038003030eb0032", - func: testSendSMS, - expect: {name: "send_sms_cmd_28_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d0348103011300820281838508820430a03832cb3286099111223344556677f88b140100099110325476f84008080038003030eb0033", - func: testSendSMS, - expect: {name: "send_sms_cmd_29_with_alpha_identifier", - commandQualifier: 0x00, - title: "82ル2"}}, + expect: {commandQualifier: 0x00, + text: "82ル2"}}, {command: "d02a81030113008202818386099111223344556677f88b140100099110325476f84008080038003030eb0033", - func: testSendSMS, - expect: {name: "send_sms_cmd_29_without_alpha_identifier", - commandQualifier: 0x00}} + expect: {commandQualifier: 0x00}} ]; -runNextTest(); +function testSendSMS(aCommand, aExpect) { + is(aCommand.typeOfCommand, MozIccManager.STK_CMD_SEND_SMS, "typeOfCommand"); + is(aCommand.commandQualifier, aExpect.commandQualifier, "commandQualifier"); + + if (aExpect.text) { + is(aCommand.options.text, aExpect.text, "options.text"); + } + + if (aExpect.icons) { + isIcons(aCommand.options.icons, aExpect.icons); + is(aCommand.options.iconSelfExplanatory, aExpect.iconSelfExplanatory, + "options.iconSelfExplanatory"); + } +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("send_sms_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testSendSMS(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); + + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_send_ss.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_send_ss.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_send_ss.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_send_ss.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,222 +1,155 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -MARIONETTE_HEAD_JS = "stk_helper.js"; +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; -function testSendSS(command, expect) { - log("STK CMD " + JSON.stringify(command)); - is(command.typeOfCommand, iccManager.STK_CMD_SEND_SS, expect.name); - is(command.commandQualifier, expect.commandQualifier, expect.name); - is(command.options.text, expect.title, expect.name); - - let icons = command.options.icons; - if (icons) { - isIcons(icons, expect.icons, expect.name); - - let iconSelfExplanatory = command.options.iconSelfExplanatory; - is(iconSelfExplanatory, expect.iconSelfExplanatory, expect.name); - } - - runNextTest(); -} - -let tests = [ +const TEST_DATA = [ {command: "d029810301110082028183850c43616c6c20466f7277617264891091aa120a214365870921436587a901fb", - func: testSendSS, - expect: {name: "send_ss_cmd_1_with_alpha_identifier", - commandQualifier: 0x00, - title: "Call Forward"}}, + expect: {commandQualifier: 0x00, + text: "Call Forward"}}, {command: "d01b810301110082028183891091aa120a214365870921436587a901fb", - func: testSendSS, expect: {name: "send_ss_cmd_1_without_alpha_identifier", commandQualifier: 0x00}}, {command: "d02d810301110082028183850c43616c6c20466f7277617264891491aa120a21436587092143658709214365a711fb", - func: testSendSS, expect: {name: "send_ss_cmd_2_with_alpha_identifier", commandQualifier: 0x00, - title: "Call Forward"}}, + text: "Call Forward"}}, {command: "d01f810301110082028183891491aa120a21436587092143658709214365a711fb", - func: testSendSS, - expect: {name: "send_ss_cmd_2_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d081fd8103011100820281838581eb4576656e20696620746865204669786564204469616c6c696e67204e756d626572207365727669636520697320656e61626c65642c2074686520737570706c656d656e74617279207365727669636520636f6e74726f6c20737472696e6720696e636c7564656420696e207468652053454e442053532070726f61637469766520636f6d6d616e64207368616c6c206e6f7420626520636865636b656420616761696e73742074686f7365206f66207468652046444e206c6973742e2055706f6e20726563656976696e67207468697320636f6d6d616e642c20746865204d45207368616c6c20646563698904ffba13fb", - func: testSendSS, - expect: {name: "send_ss_cmd_3_with_alpha_identifier", - commandQualifier: 0x00, - title: "Even if the Fixed Dialling Number service is enabled, the supplementary service control string included in the SEND SS proactive command shall not be checked against those of the FDN list. Upon receiving this command, the ME shall deci"}}, + expect: {commandQualifier: 0x00, + text: "Even if the Fixed Dialling Number service is enabled, the supplementary service control string included in the SEND SS proactive command shall not be checked against those of the FDN list. Upon receiving this command, the ME shall deci"}}, {command: "d00f8103011100820281838904ffba13fb", - func: testSendSS, - expect: {name: "send_ss_cmd_3_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d01d8103011100820281838500891091aa120a214365870921436587a901fb", - func: testSendSS, - expect: {name: "send_ss_cmd_4_with_alpha_identifier", - commandQualifier: 0x00, - title: ""}}, - // send_ss_cmd_4_without_alpha_identifier has the same pdu as - // send_ss_cmd_1_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: ""}}, {command: "d02b810301110082028183850a42617369632049636f6e891091aa120a214365870921436587a901fb9e020001", - func: testSendSS, - expect: {name: "send_ss_cmd_5_with_alpha_identifier", - commandQualifier: 0x00, - title: "Basic Icon", + expect: {commandQualifier: 0x00, + text: "Basic Icon", iconSelfExplanatory: true, - icons: [basicIcon]}}, + icons: [BASIC_ICON]}}, {command: "d01f810301110082028183891091aa120a214365870921436587a901fb9e020001", - func: testSendSS, - expect: {name: "send_ss_cmd_5_without_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, iconSelfExplanatory: true, - icons: [basicIcon]}}, + icons: [BASIC_ICON]}}, {command: "d02c810301110082028183850b436f6c6f75722049636f6e891091aa120a214365870921436587a901fb9e020003", - func: testSendSS, - expect: {name: "send_ss_cmd_6_with_alpha_identifier", - commandQualifier: 0x00, - title: "Colour Icon", + expect: {commandQualifier: 0x00, + text: "Colour Icon", iconSelfExplanatory: true, - icons: [colorIcon]}}, + icons: [COLOR_ICON]}}, {command: "d01f810301110082028183891091aa120a214365870921436587a901fb9e020003", - func: testSendSS, - expect: {name: "send_ss_cmd_6_without_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, iconSelfExplanatory: true, - icons: [colorIcon]}}, + icons: [COLOR_ICON]}}, {command: "d02b810301110082028183850a42617369632049636f6e891091aa120a214365870921436587a901fb9e020101", - func: testSendSS, - expect: {name: "send_ss_cmd_7_with_alpha_identifier", - commandQualifier: 0x00, - title: "Basic Icon", + expect: {commandQualifier: 0x00, + text: "Basic Icon", iconSelfExplanatory: false, - icons: [basicIcon]}}, + icons: [BASIC_ICON]}}, {command: "d01f810301110082028183891091aa120a214365870921436587a901fb9e020101", - func: testSendSS, - expect: {name: "send_ss_cmd_7_without_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, iconSelfExplanatory: false, - icons: [basicIcon]}}, + icons: [BASIC_ICON]}}, {command: "d036810301110082028183851980041704140420041004120421042204120423041904220415891091aa120a214365870921436587a901fb", - func: testSendSS, - expect: {name: "send_ss_cmd_8_with_alpha_identifier", - commandQualifier: 0x00, - title: "ЗДРАВСТВУЙТЕ"}}, - // send_ss_cmd_8_without_alpha_identifier has the same pdu as - // send_ss_cmd_1_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "ЗДРАВСТВУЙТЕ"}}, {command: "d033810301110082028183851054657874204174747269627574652031891091aa120a214365870921436587a901fbd004001000b4", - func: testSendSS, - expect: {name: "send_ss_cmd_9_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d021810301110082028183891091aa120a214365870921436587a901fbd004001000b4", - func: testSendSS, - expect: {name: "send_ss_cmd_9_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d02d810301110082028183851054657874204174747269627574652032891091aa120a214365870921436587a901fb", - func: testSendSS, - expect: {name: "send_ss_cmd_10_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 2"}}, - // send_ss_cmd_10_without_alpha_identifier has the same pdu as - // send_ss_cmd_1_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "Text Attribute 2"}}, {command: "d033810301110082028183851054657874204174747269627574652031891091aa120a214365870921436587a901fbd004001001b4", - func: testSendSS, - expect: {name: "send_ss_cmd_11_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d021810301110082028183891091aa120a214365870921436587a901fbd004001001b4", - func: testSendSS, - expect: {name: "send_ss_cmd_11_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d033810301110082028183851054657874204174747269627574652031891091aa120a214365870921436587a901fbd004001002b4", - func: testSendSS, - expect: {name: "send_ss_cmd_12_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d021810301110082028183891091aa120a214365870921436587a901fbd004001002b4", - func: testSendSS, - expect: {name: "send_ss_cmd_12_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d033810301110082028183851054657874204174747269627574652031891091aa120a214365870921436587a901fbd004001004b4", - func: testSendSS, - expect: {name: "send_ss_cmd_13_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d021810301110082028183891091aa120a214365870921436587a901fbd004001004b4", - func: testSendSS, - expect: {name: "send_ss_cmd_13_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d033810301110082028183851054657874204174747269627574652032891091aa120a214365870921436587a901fbd004001000b4", - func: testSendSS, - expect: {name: "send_ss_cmd_14_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 2"}}, - // send_ss_cmd_14_without_alpha_identifier has the same pdu as - // send_ss_cmd_9_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "Text Attribute 2"}}, {command: "d02d810301110082028183851054657874204174747269627574652033891091aa120a214365870921436587a901fb", - func: testSendSS, - expect: {name: "send_ss_cmd_15_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 3"}}, - // send_ss_cmd_15_without_alpha_identifier has the same pdu as - // send_ss_cmd_1_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "Text Attribute 3"}}, {command: "d033810301110082028183851054657874204174747269627574652031891091aa120a214365870921436587a901fbd004001008b4", - func: testSendSS, - expect: {name: "send_ss_cmd_16_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d021810301110082028183891091aa120a214365870921436587a901fbd004001008b4", - func: testSendSS, - expect: {name: "send_ss_cmd_16_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d033810301110082028183851054657874204174747269627574652031891091aa120a214365870921436587a901fbd004001010b4", - func: testSendSS, - expect: {name: "send_ss_cmd_17_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d021810301110082028183891091aa120a214365870921436587a901fbd004001010b4", - func: testSendSS, - expect: {name: "send_ss_cmd_17_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d033810301110082028183851054657874204174747269627574652031891091aa120a214365870921436587a901fbd004001020b4", - func: testSendSS, - expect: {name: "send_ss_cmd_18_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d021810301110082028183891091aa120a214365870921436587a901fbd004001020b4", - func: testSendSS, - expect: {name: "send_ss_cmd_18_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d033810301110082028183851054657874204174747269627574652031891091aa120a214365870921436587a901fbd004001040b4", - func: testSendSS, - expect: {name: "send_ss_cmd_19_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d021810301110082028183891091aa120a214365870921436587a901fbd004001040b4", - func: testSendSS, - expect: {name: "send_ss_cmd_19_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d033810301110082028183851054657874204174747269627574652031891091aa120a214365870921436587a901fbd004001080b4", - func: testSendSS, - expect: {name: "send_ss_cmd_20_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d021810301110082028183891091aa120a214365870921436587a901fbd004001080b4", - func: testSendSS, - expect: {name: "send_ss_cmd_20_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d0228103011100820281838505804f60597d891091aa120a214365870921436587a901fb", - func: testSendSS, - expect: {name: "send_ss_cmd_21_with_alpha_identifier", - commandQualifier: 0x00, - title: "你好"}}, - // send_ss_cmd_21_without_alpha_identifier has the same pdu as - // send_ss_cmd_1_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "你好"}}, {command: "d02081030111008202818385038030eb891091aa120a214365870921436587a901fb", - func: testSendSS, - expect: {name: "send_ss_cmd_22_with_alpha_identifier", - commandQualifier: 0x00, - title: "ル"}}, - // send_ss_cmd_22_without_alpha_identifier has the same pdu as - // send_ss_cmd_1_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "ル"}}, ]; -runNextTest(); +function testSendSS(aCommand, aExpect) { + is(aCommand.typeOfCommand, MozIccManager.STK_CMD_SEND_SS, "typeOfCommand"); + is(aCommand.commandQualifier, aExpect.commandQualifier, "commandQualifier"); + + if (aExpect.text) { + is(aCommand.options.text, aExpect.text, "options.text"); + } + + if (aExpect.icons) { + isIcons(aCommand.options.icons, aExpect.icons); + is(aCommand.options.iconSelfExplanatory, aExpect.iconSelfExplanatory, + "options.iconSelfExplanatory"); + } +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("send_ss_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testSendSS(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); + + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_send_ussd.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_send_ussd.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_send_ussd.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_send_ussd.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,229 +1,156 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -MARIONETTE_HEAD_JS = "stk_helper.js"; +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; -function testSendUSSD(command, expect) { - log("STK CMD " + JSON.stringify(command)); - is(command.typeOfCommand, iccManager.STK_CMD_SEND_USSD, expect.name); - is(command.commandQualifier, expect.commandQualifier, expect.name); - is(command.options.text, expect.title, expect.name); - - let icons = command.options.icons; - if (icons) { - isIcons(icons, expect.icons, expect.name); - - let iconSelfExplanatory = command.options.iconSelfExplanatory; - is(iconSelfExplanatory, expect.iconSelfExplanatory, expect.name); - } - - runNextTest(); -} - -let tests = [ +const TEST_DATA = [ {command: "d050810301120082028183850a372d62697420555353448a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_1_with_alpha_identifier", - commandQualifier: 0x00, - title: "7-bit USSD"}}, + expect: {commandQualifier: 0x00, + text: "7-bit USSD"}}, {command: "d0448103011200820281838a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_1_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d058810301120082028183850a382d62697420555353448a41444142434445464748494a4b4c4d4e4f505152535455565758595a2d6162636465666768696a6b6c6d6e6f707172737475767778797a2d31323334353637383930", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_2_with_alpha_identifier", - commandQualifier: 0x00, - title: "8-bit USSD"}}, + expect: {commandQualifier: 0x00, + text: "8-bit USSD"}}, {command: "d04c8103011200820281838a41444142434445464748494a4b4c4d4e4f505152535455565758595a2d6162636465666768696a6b6c6d6e6f707172737475767778797a2d31323334353637383930", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_2_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d02f81030112008202818385095543533220555353448a1948041704140420041004120421042204120423041904220415", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_3_with_alpha_identifier", - commandQualifier: 0x00, - title: "UCS2 USSD"}}, + expect: {commandQualifier: 0x00, + text: "UCS2 USSD"}}, {command: "d0248103011200820281838a1948041704140420041004120421042204120423041904220415", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_3_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d081fd8103011200820281838581b66f6e636520612052454c4541534520434f4d504c455445206d65737361676520636f6e7461696e696e672074686520555353442052657475726e20526573756c74206d657373616765206e6f7420636f6e7461696e696e6720616e206572726f7220686173206265656e2072656365697665642066726f6d20746865206e6574776f726b2c20746865204d45207368616c6c20696e666f726d207468652053494d20746861742074686520636f6d6d616e64206861738a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_4_with_alpha_identifier", - commandQualifier: 0x00, - title: "once a RELEASE COMPLETE message containing the USSD Return Result message not containing an error has been received from the network, the ME shall inform the SIM that the command has"}}, - // send_ussd_cmd_4_without_alpha_identifier has the same pdu as - // send_ussd_cmd_1_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "once a RELEASE COMPLETE message containing the USSD Return Result message not containing an error has been received from the network, the ME shall inform the SIM that the command has"}}, {command: "d04681030112008202818385008a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_5_with_alpha_identifier", - commandQualifier: 0x00, - title: ""}}, - // send_ussd_cmd_5_without_alpha_identifier has the same pdu as - // send_ussd_cmd_1_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: ""}}, {command: "d054810301120082028183850a42617369632049636f6e8a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e5609e020001", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_6_with_alpha_identifier", - commandQualifier: 0x00, - title: "Basic Icon", + expect: {commandQualifier: 0x00, + text: "Basic Icon", iconSelfExplanatory: true, - icons: [basicIcon]}}, + icons: [BASIC_ICON]}}, {command: "d0488103011200820281838a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e5609e020001", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_6_without_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, iconSelfExplanatory: true, - icons: [basicIcon]}}, + icons: [BASIC_ICON]}}, {command: "d054810301120082028183850a436f6c6f722049636f6e8a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e5609e020003", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_7_with_alpha_identifier", - commandQualifier: 0x00, - title: "Color Icon", + expect: {commandQualifier: 0x00, + text: "Color Icon", iconSelfExplanatory: true, - icons: [colorIcon]}}, + icons: [COLOR_ICON]}}, {command: "d0488103011200820281838a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e5609e020003", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_7_without_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, iconSelfExplanatory: true, - icons: [colorIcon]}}, + icons: [COLOR_ICON]}}, {command: "d054810301120082028183850a42617369632049636f6e8a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e5609e020101", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_8_with_alpha_identifier", - commandQualifier: 0x00, - title: "Basic Icon", + expect: {commandQualifier: 0x00, + text: "Basic Icon", iconSelfExplanatory: false, - icons: [basicIcon]}}, + icons: [BASIC_ICON]}}, {command: "d0488103011200820281838a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e5609e020101", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_8_without_alpha_identifier", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, iconSelfExplanatory: false, - icons: [basicIcon]}}, + icons: [BASIC_ICON]}}, {command: "d05f8103011200820281838519800417041404200410041204210422041204230419042204158a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_9_with_alpha_identifier", - commandQualifier: 0x00, - title: "ЗДРАВСТВУЙТЕ"}}, - // send_ussd_cmd_9_without_alpha_identifier has the same pdu as - // send_ussd_cmd_1_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "ЗДРАВСТВУЙТЕ"}}, {command: "d05c8103011200820281838510546578742041747472696275746520318a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001000b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_10_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d04a8103011200820281838a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001000b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_10_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d0568103011200820281838510546578742041747472696275746520328a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_11_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 2"}}, - // send_ussd_cmd_11_without_alpha_identifier has the same pdu as - // send_ussd_cmd_1_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "Text Attribute 2"}}, {command: "d05c8103011200820281838510546578742041747472696275746520318a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001001b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_12_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d04a8103011200820281838a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001001b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_12_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d05c8103011200820281838510546578742041747472696275746520318a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001002b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_13_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d04a8103011200820281838a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001002b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_13_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d05c8103011200820281838510546578742041747472696275746520318a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001004b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_14_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d04a8103011200820281838a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001004b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_14_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d05c8103011200820281838510546578742041747472696275746520328a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001000b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_15_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 2"}}, - // send_ussd_cmd_15_without_alpha_identifier has the same pdu as - // send_ussd_cmd_10_with_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "Text Attribute 2"}}, {command: "d0568103011200820281838510546578742041747472696275746520338a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_16_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 3"}}, - // send_ussd_cmd_16_without_alpha_identifier has the same pdu as - // send_ussd_cmd_1_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "Text Attribute 3"}}, {command: "d05c8103011200820281838510546578742041747472696275746520318a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001008b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_17_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d04a8103011200820281838a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001008b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_17_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d05c8103011200820281838510546578742041747472696275746520318a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001010b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_18_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d04a8103011200820281838a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001010b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_18_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d05c8103011200820281838510546578742041747472696275746520318a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001020b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_19_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d04a8103011200820281838a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001020b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_19_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d05c8103011200820281838510546578742041747472696275746520318a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001040b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_20_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d04a8103011200820281838a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001040b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_20_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d05c8103011200820281838510546578742041747472696275746520318a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001080b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_21_with_alpha_identifier", - commandQualifier: 0x00, - title: "Text Attribute 1"}}, + expect: {commandQualifier: 0x00, + text: "Text Attribute 1"}}, {command: "d04a8103011200820281838a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560d004001080b4", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_21_without_alpha_identifier", - commandQualifier: 0x00}}, + expect: {commandQualifier: 0x00}}, {command: "d04b8103011200820281838505804f60597d8a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_22_with_alpha_identifier", - commandQualifier: 0x00, - title: "你好"}}, - // send_ussd_cmd_22_without_alpha_identifier has the same pdu as - // send_ussd_cmd_1_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "你好"}}, {command: "d04981030112008202818385038030eb8a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560", - func: testSendUSSD, - expect: {name: "send_ussd_cmd_23_with_alpha_identifier", - commandQualifier: 0x00, - title: "ル"}}, - // send_ussd_cmd_23_without_alpha_identifier has the same pdu as - // send_ussd_cmd_1_without_alpha_identifier. + expect: {commandQualifier: 0x00, + text: "ル"}}, ]; -runNextTest(); +function testSendUSSD(aCommand, aExpect) { + is(aCommand.typeOfCommand, MozIccManager.STK_CMD_SEND_USSD, "typeOfCommand"); + is(aCommand.commandQualifier, aExpect.commandQualifier, "commandQualifier"); + + if (aExpect.text) { + is(aCommand.options.text, aExpect.text, "options.text"); + } + + if (aExpect.icons) { + isIcons(aCommand.options.icons, aExpect.icons); + is(aCommand.options.iconSelfExplanatory, aExpect.iconSelfExplanatory, + "options.iconSelfExplanatory"); + } +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("send_ussd_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testSendUSSD(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); + + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_setup_call.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_setup_call.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_setup_call.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_setup_call.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,376 +1,301 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -MARIONETTE_HEAD_JS = "stk_helper.js"; +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; -function testSetupCall(command, expect) { - log("STK CMD " + JSON.stringify(command)); - is(command.typeOfCommand, iccManager.STK_CMD_SET_UP_CALL, expect.name); - is(command.commandQualifier, expect.commandQualifier, expect.name); - is(command.options.address, expect.address, expect.name); - - if (expect.confirmMessage) { - isStkText(command.options.confirmMessage, expect.confirmMessage, expect.name); - } - if (expect.callMessage) { - isStkText(command.options.callMessage, expect.callMessage, expect.name); - } - - let duration = command.options.duration; - if (duration) { - is(duration.timeUnit, expect.duration.timeUnit, expect.name); - is(duration.timeInterval, expect.duration.timeInterval, expect.name); - } - - runNextTest(); -} - -let tests = [ +const TEST_DATA = [ {command: "d01e81030110008202818385084e6f7420627573798609911032042143651c2c", - func: testSetupCall, - expect: {name: "setup_call_cmd_1", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "Not busy" }, address: "+012340123456,1,2"}}, {command: "d01d81030110028202818385074f6e20686f6c648609911032042143651c2c", - func: testSetupCall, - expect: {name: "setup_call_cmd_2", - commandQualifier: 0x02, + expect: {commandQualifier: 0x02, confirmMessage: { text: "On hold" }, address: "+012340123456,1,2"}}, {command: "d020810301100482028183850a446973636f6e6e6563748609911032042143651c2c", - func: testSetupCall, - expect: {name: "setup_call_cmd_3", - commandQualifier: 0x04, + expect: {commandQualifier: 0x04, confirmMessage: { text: "Disconnect" }, address: "+012340123456,1,2"}}, {command: "d02b81030110008202818385114361706162696c69747920636f6e6669678609911032042143651c2c870201a0", - func: testSetupCall, - expect: {name: "setup_call_cmd_4", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "Capability config" }, address: "+012340123456,1,2"}}, {command: "d01c81030110018202818386119110325476981032547698103254769810", - func: testSetupCall, - expect: {name: "setup_call_cmd_5", - commandQualifier: 0x01, + expect: {commandQualifier: 0x01, address: "+01234567890123456789012345678901"}}, {command: "d081fd8103011001820281838581ed54687265652074797065732061726520646566696e65643a202d2073657420757020612063616c6c2c20627574206f6e6c79206966206e6f742063757272656e746c792062757379206f6e20616e6f746865722063616c6c3b202d2073657420757020612063616c6c2c2070757474696e6720616c6c206f746865722063616c6c732028696620616e7929206f6e20686f6c643b202d2073657420757020612063616c6c2c20646973636f6e6e656374696e6720616c6c206f746865722063616c6c732028696620616e79292066697273742e20466f722065616368206f662074686573652074797065732c2086029110", - func: testSetupCall, - expect: {name: "setup_call_cmd_6", - commandQualifier: 0x01, + expect: {commandQualifier: 0x01, confirmMessage: { text: "Three types are defined: - set up a call, but only if not currently busy on another call; - set up a call, putting all other calls (if any) on hold; - set up a call, disconnecting all other calls (if any) first. For each of these types, " }, address: "+01"}}, {command: "d02b810301100082028183850c43616c6c65642070617274798609911032042143651c2c880780509595959595", - func: testSetupCall, - expect: {name: "setup_call_cmd_7", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "Called party" }, address: "+012340123456,1,2"}}, {command: "d02281030110018202818385084475726174696f6e8609911032042143651c2c8402010a", - func: testSetupCall, - expect: {name: "setup_call_cmd_8", - commandQualifier: 0x01, + expect: {commandQualifier: 0x01, confirmMessage: { text: "Duration" }, address: "+012340123456,1,2", - duration: {timeUnit: iccManager.STK_TIME_UNIT_SECOND, + duration: {timeUnit: MozIccManager.STK_TIME_UNIT_SECOND, timeInterval: 0x0A}}}, {command: "d028810301100082028183850c434f4e4649524d4154494f4e8609911032042143651c2c850443414c4c", - func: testSetupCall, - expect: {name: "setup_call_cmd_9", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION" }, callMessage: { text: "CALL" }, address: "+012340123456,1,2"}}, {command: "d03081030110008202818385165365742075702063616c6c2049636f6e20332e312e318609911032042143651c2c9e020101", - func: testSetupCall, - expect: {name: "setup_call_cmd_10", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "Set up call Icon 3.1.1", iconSelfExplanatory: false, - icons: [basicIcon] }, + icons: [BASIC_ICON] }, address: "+012340123456,1,2"}}, {command: "d03081030110008202818385165365742075702063616c6c2049636f6e20332e322e318609911032042143651c2c9e020001", - func: testSetupCall, - expect: {name: "setup_call_cmd_11", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "Set up call Icon 3.2.1", iconSelfExplanatory: true, - icons: [basicIcon] }, + icons: [BASIC_ICON] }, address: "+012340123456,1,2"}}, {command: "d03081030110008202818385165365742075702063616c6c2049636f6e20332e332e318609911032042143651c2c9e020103", - func: testSetupCall, - expect: {name: "setup_call_cmd_12", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "Set up call Icon 3.3.1", iconSelfExplanatory: false, - icons: [colorIcon] }, + icons: [COLOR_ICON] }, address: "+012340123456,1,2"}}, {command: "d04c81030110008202818385165365742075702063616c6c2049636f6e20332e342e318609911032042143651c2c9e02000185165365742075702063616c6c2049636f6e20332e342e329e020001", - func: testSetupCall, - expect: {name: "setup_call_cmd_13", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "Set up call Icon 3.4.1", iconSelfExplanatory: true, - icons: [basicIcon] }, + icons: [BASIC_ICON] }, callMessage: { text: "Set up call Icon 3.4.2" }, address: "+012340123456,1,2"}}, {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e00b4d004000600b4", - func: testSetupCall, - expect: {name: "setup_call_cmd_14", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 1" }, callMessage: { text: "CALL 1" }, address: "+012340123456,1,2"}}, {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032", - func: testSetupCall, - expect: {name: "setup_call_cmd_15", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 2" }, callMessage: { text: "CALL 2" }, address: "+012340123456,1,2"}}, {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e01b4d004000601b4", - func: testSetupCall, - expect: {name: "setup_call_cmd_16", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 1" }, callMessage: { text: "CALL 1" }, address: "+012340123456,1,2"}}, {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032", - func: testSetupCall, - expect: {name: "setup_call_cmd_17", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 2" }, callMessage: { text: "CALL 2" }, address: "+012340123456,1,2"}}, {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e02b4d004000602b4", - func: testSetupCall, - expect: {name: "setup_call_cmd_18", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 1" }, callMessage: { text: "CALL 1" }, address: "+012340123456,1,2"}}, {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032", - func: testSetupCall, - expect: {name: "setup_call_cmd_19", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 2" }, callMessage: { text: "CALL 2" }, address: "+012340123456,1,2"}}, {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e04b4d004000604b4", - func: testSetupCall, - expect: {name: "setup_call_cmd_20", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 1" }, callMessage: { text: "CALL 1" }, address: "+012340123456,1,2"}}, {command: "d038810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032d004000e00b4d004000600b4", - func: testSetupCall, - expect: {name: "setup_call_cmd_21", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 2" }, callMessage: { text: "CALL 2" }, address: "+012340123456,1,2"}}, {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20338609911032042143651c2c850643414c4c2033", - func: testSetupCall, - expect: {name: "setup_call_cmd_22", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 3" }, callMessage: { text: "CALL 3" }, address: "+012340123456,1,2"}}, {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e08b4d004000608b4", - func: testSetupCall, - expect: {name: "setup_call_cmd_23", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 1" }, callMessage: { text: "CALL 1" }, address: "+012340123456,1,2"}}, {command: "d038810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032d004000e00b4d004000600b4", - func: testSetupCall, - expect: {name: "setup_call_cmd_24", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 2" }, callMessage: { text: "CALL 2" }, address: "+012340123456,1,2"}}, {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20338609911032042143651c2c850643414c4c2033", - func: testSetupCall, - expect: {name: "setup_call_cmd_25", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 3" }, callMessage: { text: "CALL 3" }, address: "+012340123456,1,2"}}, {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e10b4d004000610b4", - func: testSetupCall, - expect: {name: "setup_call_cmd_26", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 1" }, callMessage: { text: "CALL 1" }, address: "+012340123456,1,2"}}, {command: "d038810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032d004000e00b4d004000600b4", - func: testSetupCall, - expect: {name: "setup_call_cmd_27", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 2" }, callMessage: { text: "CALL 2" }, address: "+012340123456,1,2"}}, {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20338609911032042143651c2c850643414c4c2033", - func: testSetupCall, - expect: {name: "setup_call_cmd_28", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 3" }, callMessage: { text: "CALL 3" }, address: "+012340123456,1,2"}}, {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e20b4d004000620b4", - func: testSetupCall, - expect: {name: "setup_call_cmd_29", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 1" }, callMessage: { text: "CALL 1" }, address: "+012340123456,1,2"}}, {command: "d038810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032d004000e00b4d004000600b4", - func: testSetupCall, - expect: {name: "setup_call_cmd_30", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 2" }, callMessage: { text: "CALL 2" }, address: "+012340123456,1,2"}}, {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20338609911032042143651c2c850643414c4c2033", - func: testSetupCall, - expect: {name: "setup_call_cmd_31", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 3" }, callMessage: { text: "CALL 3" }, address: "+012340123456,1,2"}}, {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e40b4d004000640b4", - func: testSetupCall, - expect: {name: "setup_call_cmd_32", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 1" }, callMessage: { text: "CALL 1" }, address: "+012340123456,1,2"}}, {command: "d038810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032d004000e00b4d004000600b4", - func: testSetupCall, - expect: {name: "setup_call_cmd_33", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 2" }, callMessage: { text: "CALL 2" }, address: "+012340123456,1,2"}}, {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20338609911032042143651c2c850643414c4c2033", - func: testSetupCall, - expect: {name: "setup_call_cmd_34", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 3" }, callMessage: { text: "CALL 3" }, address: "+012340123456,1,2"}}, {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e80b4d004000680b4", - func: testSetupCall, - expect: {name: "setup_call_cmd_35", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 1" }, callMessage: { text: "CALL 1" }, address: "+012340123456,1,2"}}, {command: "d038810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032d004000e00b4d004000600b4", - func: testSetupCall, - expect: {name: "setup_call_cmd_36", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 2" }, callMessage: { text: "CALL 2" }, address: "+012340123456,1,2"}}, {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20338609911032042143651c2c850643414c4c2033", - func: testSetupCall, - expect: {name: "setup_call_cmd_37", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 3" }, callMessage: { text: "CALL 3" }, address: "+012340123456,1,2"}}, {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e00b4d0040006004b", - func: testSetupCall, - expect: {name: "setup_call_cmd_38", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 1" }, callMessage: { text: "CALL 1" }, address: "+012340123456,1,2"}}, {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032", - func: testSetupCall, - expect: {name: "setup_call_cmd_39", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "CONFIRMATION 2" }, callMessage: { text: "CALL 2" }, address: "+012340123456,1,2"}}, {command: "d02d810301100082028183851980041704140420041004120421042204120423041904220415860791103204214365", - func: testSetupCall, - expect: {name: "setup_call_cmd_40", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "ЗДРАВСТВУЙТЕ" }, address: "+012340123456"}}, {command: "d04c810301100082028183851b800417041404200410041204210422041204230419042204150031860791103204214365851b800417041404200410041204210422041204230419042204150032", - func: testSetupCall, - expect: {name: "setup_call_cmd_41", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "ЗДРАВСТВУЙТЕ1" }, callMessage: { text: "ЗДРАВСТВУЙТЕ2" }, address: "+012340123456"}}, {command: "d0198103011000820281838505804e0d5fd9860791103204214365", - func: testSetupCall, - expect: {name: "setup_call_cmd_42", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "不忙" }, address: "+012340123456"}}, {command: "d022810301100082028183850580786e5b9a860791103204214365850780625375358bdd", - func: testSetupCall, - expect: {name: "setup_call_cmd_43", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "确定" }, callMessage: { text: "打电话" }, address: "+012340123456"}}, {command: "d01781030110008202818385038030eb860791103204214365", - func: testSetupCall, - expect: {name: "setup_call_cmd_44", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "ル" }, address: "+012340123456"}}, {command: "d02081030110008202818385058030eb003186079110320421436585058030eb0032", - func: testSetupCall, - expect: {name: "setup_call_cmd_45", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "ル1" }, callMessage: { text: "ル2" }, address: "+012340123456"}}, {command: "d029810301100482028182050a446973636f6e6e6563748609811032042143651c2c05074d657373616765", - func: testSetupCall, - expect: {name: "setup_call_cmd_46", - commandQualifier: 0x04, + expect: {commandQualifier: 0x04, confirmMessage: { text: "Disconnect" }, address: "012340123456,1,2", callMessage: { text: "Message"}}}, {command: "d02281030110008202818385084E6F7420627573798609911032042143651C2C8402010A", - func: testSetupCall, - expect: {name: "setup_call_cmd_47", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "Not busy" }, address: "+012340123456,1,2", - duration: {timeUnit: iccManager.STK_TIME_UNIT_SECOND, + duration: {timeUnit: MozIccManager.STK_TIME_UNIT_SECOND, timeInterval: 0x0A}}}, {command: "d04c81030110008202818385165365742075702063616c6c2049636f6e20332e352e318609911032042143651c2c9e02000185165365742075702063616c6c2049636f6e20332e352e329e020103", - func: testSetupCall, - expect: {name: "setup_call_cmd_48", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { text: "Set up call Icon 3.5.1", iconSelfExplanatory: true, - icons: [basicIcon] }, + icons: [BASIC_ICON] }, callMessage: { text: "Set up call Icon 3.5.2", iconSelfExplanatory: false, - icons: [colorIcon] }, + icons: [COLOR_ICON] }, address: "+012340123456,1,2"}}, {command: "d01c8103011000820281838609911032042143651c2c9e0200019e020103", - func: testSetupCall, - expect: {name: "setup_call_cmd_49", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, confirmMessage: { iconSelfExplanatory: true, - icons: [basicIcon] }, + icons: [BASIC_ICON] }, callMessage: { iconSelfExplanatory: false, - icons: [colorIcon] }, + icons: [COLOR_ICON] }, address: "+012340123456,1,2"}}, ]; -runNextTest(); +function testSetupCall(aCommand, aExpect) { + is(aCommand.typeOfCommand, MozIccManager.STK_CMD_SET_UP_CALL, + "typeOfCommand"); + is(aCommand.commandQualifier, aExpect.commandQualifier, + "commandQualifier"); + is(aCommand.options.address, aExpect.address, + "options.address"); + + if (aExpect.confirmMessage) { + isStkText(aCommand.options.confirmMessage, aExpect.confirmMessage); + } + + if (aExpect.callMessage) { + isStkText(aCommand.options.callMessage, aExpect.callMessage); + } + + let duration = aCommand.options.duration; + if (duration) { + is(duration.timeUnit, aExpect.duration.timeUnit, "duration.timeUnit"); + is(duration.timeInterval, aExpect.duration.timeInterval, + "duration.timeInterval"); + } +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("setup_call_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testSetupCall(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); + + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_setup_event_list.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_setup_event_list.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_setup_event_list.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_setup_event_list.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,50 +1,59 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -MARIONETTE_HEAD_JS = "stk_helper.js"; +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; -function testSetupEventList(command, expect) { - log("STK CMD " + JSON.stringify(command)); - is(command.typeOfCommand, iccManager.STK_CMD_SET_UP_EVENT_LIST, expect.name); - is(command.commandQualifier, expect.commandQualifier, expect.name); - for (let index in command.options.eventList) { - is(command.options.eventList[index], expect.eventList[index], expect.name); - } - - runNextTest(); -} - -let tests = [ +const TEST_DATA = [ {command: "d00c810301050082028182990104", - func: testSetupEventList, - expect: {name: "setup_event_list_cmd_1", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, eventList: [4]}}, {command: "d00d81030105008202818299020507", - func: testSetupEventList, - expect: {name: "setup_event_list_cmd_2", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, eventList: [5, 7]}}, {command: "d00c810301050082028182990107", - func: testSetupEventList, - expect: {name: "setup_event_list_cmd_3", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, eventList: [7]}}, {command: "d00c810301050082028182990107", - func: testSetupEventList, - expect: {name: "setup_event_list_cmd_4", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, eventList: [7]}}, {command: "d00b8103010500820281829900", - func: testSetupEventList, - expect: {name: "setup_event_list_cmd_5", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, eventList: null}}, {command: "d00c810301050082028182990107", - func: testSetupEventList, - expect: {name: "setup_event_list_cmd_6", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, eventList: [7]}} ]; -runNextTest(); +function testSetupEventList(aCommand, aExpect) { + is(aCommand.typeOfCommand, MozIccManager.STK_CMD_SET_UP_EVENT_LIST, + "typeOfCommand"); + is(aCommand.commandQualifier, aExpect.commandQualifier, "commandQualifier"); + + for (let index in aExpect.eventList) { + is(aCommand.options.eventList[index], aExpect.eventList[index], + "options.eventList[" + index + "]"); + } +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("setup_event_list_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testSetupEventList(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); + + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_setup_idle_mode_text.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_setup_idle_mode_text.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_setup_idle_mode_text.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_setup_idle_mode_text.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,207 +1,154 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -MARIONETTE_HEAD_JS = "stk_helper.js"; +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; -function testSetupIdleModeText(command, expect) { - log("STK CMD " + JSON.stringify(command)); - is(command.typeOfCommand, iccManager.STK_CMD_SET_UP_IDLE_MODE_TEXT, expect.name); - is(command.commandQualifier, expect.commandQualifier, expect.name); - is(command.options.text, expect.text, expect.name); - - let icons = command.options.icons; - if (icons) { - isIcons(icons, expect.icons, expect.name); - - let iconSelfExplanatory = command.options.iconSelfExplanatory; - is(iconSelfExplanatory, expect.iconSelfExplanatory, expect.name); - } - - runNextTest(); -} - -let tests = [ +const TEST_DATA = [ {command: "d01a8103012800820281828d0f0449646c65204d6f64652054657874", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_1", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text"}}, {command: "d0188103012800820281828d0d04546f6f6c6b69742054657374", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_2", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Toolkit Test"}}, {command: "d081fd8103012800820281828d81f100547419344d3641737498cd06cdeb70383b0f0a83e8653c1d34a7cbd3ee330b7447a7c768d01c1d66b341e232889c9ec3d9e17c990c12e741747419d42c82c27350d80d4a93d96550fb4d2e83e8653c1d943683e8e832a85904a5e7a0b0985d06d1df20f21b94a6bba8e832082e2fcfcb6e7a989e7ebb41737a9e5d06a5e72076d94c0785e7a0b01b946ec3d9e576d94d0fd3d36f37885c1ea7e7e9b71b447f83e8e832a85904b5c3eeba393ca6d7e565b90b444597416932bb0c6abfc96510bd8ca783e6e8309b0d129741e4f41cce0ee7cb6450da0d0a83da61b7bb2c07d1d1613aa8ec9ed7e5e539888e0ed341ee32", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_3", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "The SIM shall supply a text string, which shall be displayed by the ME as an idle mode text if the ME is able to do it.The presentation style is left as an implementation decision to the ME manufacturer. The idle mode text shall be displayed in a manner that ensures that ne"}}, {command: "d0198103012800820281828d0a0449646c6520746578749e020001", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_4", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle text", iconSelfExplanatory: true, - icons: [basicIcon]}}, + icons: [BASIC_ICON]}}, {command: "d0198103012800820281828d0a0449646c6520746578749e020105", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_5", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle text", iconSelfExplanatory: false, - icons: [colorTransparencyIcon]}}, + icons: [COLOR_TRANSPARENCY_ICON]}}, {command: "d0198103012800820281828d0a0449646c6520746578749e020007", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_6", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle text", iconSelfExplanatory: true, - icons: [colorIcon, colorTransparencyIcon]}}, + icons: [COLOR_ICON, COLOR_TRANSPARENCY_ICON]}}, {command: "d0248103012800820281828d1908041704140420041004120421042204120423041904220415", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_7", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "ЗДРАВСТВУЙТЕ"}}, {command: "d0228103012800820281828d110449646c65204d6f646520546578742031d004001000b4", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_8", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 1"}}, {command: "d01c8103012800820281828d110449646c65204d6f646520546578742032", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_9", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 2"}}, {command: "d0228103012800820281828d110449646c65204d6f646520546578742031d004001001b4", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_10", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 1"}}, {command: "d01c8103012800820281828d110449646c65204d6f646520546578742032", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_11", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 2"}}, {command: "d0228103012800820281828d110449646c65204d6f646520546578742031d004001002b4", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_12", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 1"}}, {command: "d01c8103012800820281828d110449646c65204d6f646520546578742032", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_13", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 2"}}, {command: "d0228103012800820281828d110449646c65204d6f646520546578742031d004001004b4", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_14", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 1"}}, {command: "d0228103012800820281828d110449646c65204d6f646520546578742032d004001000b4", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_15", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 2"}}, {command: "d01c8103012800820281828d110449646c65204d6f646520546578742033", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_16", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 3"}}, {command: "d0228103012800820281828d110449646c65204d6f646520546578742031d004001008b4", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_17", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 1"}}, {command: "d0228103012800820281828d110449646c65204d6f646520546578742032d004001000b4", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_18", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 2"}}, {command: "d01c8103012800820281828d110449646c65204d6f646520546578742033", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_19", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 3"}}, {command: "d0228103012800820281828d110449646c65204d6f646520546578742031d004001010b4", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_20", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 1"}}, {command: "d0228103012800820281828d110449646c65204d6f646520546578742032d004001000b4", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_21", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 2"}}, {command: "d01c8103012800820281828d110449646c65204d6f646520546578742033", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_22", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 3"}}, {command: "d0228103012800820281828d110449646c65204d6f646520546578742031d004001020b4", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_23", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 1"}}, {command: "d0228103012800820281828d110449646c65204d6f646520546578742032d004001000b4", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_24", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 2"}}, {command: "d01c8103012800820281828d110449646c65204d6f646520546578742033", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_25", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 3"}}, {command: "d0228103012800820281828d110449646c65204d6f646520546578742031d004001040b4", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_26", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 1"}}, {command: "d0228103012800820281828d110449646c65204d6f646520546578742032d004001000b4", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_27", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 2"}}, {command: "d01c8103012800820281828d110449646c65204d6f646520546578742033", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_28", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 3"}}, {command: "d0228103012800820281828d110449646c65204d6f646520546578742031d004001080b4", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_29", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 1"}}, {command: "d0228103012800820281828d110449646c65204d6f646520546578742032d004001000b4", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_30", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 2"}}, {command: "d01c8103012800820281828d110449646c65204d6f646520546578742033", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_31", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 3"}}, {command: "d0228103012800820281828d110449646c65204d6f646520546578742031d004001000b4", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_32", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 1"}}, {command: "d01c8103012800820281828d110449646c65204d6f646520546578742032", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_33", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "Idle Mode Text 2"}}, {command: "d0108103012800820281828d05084f60597d", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_34", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "你好"}}, {command: "d0148103012800820281828d09080038003030eb0030", - func: testSetupIdleModeText, - expect: {name: "setup_idle_mode_text_cmd_35", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, text: "80ル0"}}, ]; -runNextTest(); +function testSetupIdleModeText(aCommand, aExpect) { + is(aCommand.typeOfCommand, MozIccManager.STK_CMD_SET_UP_IDLE_MODE_TEXT, + "typeOfCommand"); + is(aCommand.commandQualifier, aExpect.commandQualifier, "commandQualifier"); + is(aCommand.options.text, aExpect.text, "options.text"); + + if (aExpect.icons) { + isIcons(aCommand.options.icons, aExpect.icons); + is(aCommand.options.iconSelfExplanatory, aExpect.iconSelfExplanatory, + "options.iconSelfExplanatory"); + } +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("setup_idle_mode_text_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testSetupIdleModeText(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); + + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_setup_menu.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_setup_menu.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_setup_menu.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_setup_menu.js 2015-02-03 14:33:38.000000000 +0000 @@ -1,269 +1,218 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -MARIONETTE_HEAD_JS = "stk_helper.js"; +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; -function testSetupMenu(command, expect) { - log("STK CMD " + JSON.stringify(command)); - is(command.typeOfCommand, iccManager.STK_CMD_SET_UP_MENU, expect.name); - is(command.commandQualifier, expect.commandQualifier, expect.name); - is(command.options.title, expect.title, expect.name); - for (let index in command.options.items) { - is(command.options.items[index].identifier, expect.items[index].identifier, expect.name); - is(command.options.items[index].text, expect.items[index].text, expect.name); - - let itemIcons = command.options.items[index].icons; - if (itemIcons) { - isIcons(itemIcons, expect.items[index].icons, expect.name); - - let iconSelfExplanatory = command.options.items[index].iconSelfExplanatory; - is(iconSelfExplanatory, expect.items[index].iconSelfExplanatory, expect.name); - } - } - - let icons = command.options.icons; - if (icons) { - isIcons(icons, expect.icons, expect.name); - - let iconSelfExplanatory = command.options.iconSelfExplanatory; - is(iconSelfExplanatory, expect.iconSelfExplanatory, expect.name); - } - - let length = command.options.nextActionList ? command.options.nextActionList.length : 0; - for (let i = 0; i < length; i++) { - is(command.options.nextActionList[i], expect.nextActionList[i], expect.name); - } - - runNextTest(); -} - -function isFirstMenuItemNull(command) { - return (command.options.items.length == 1 && !(command.options.items[0])); -} - -function testInitialSetupMenu(command) { - log("STK CMD " + JSON.stringify(command)); - is(command.typeOfCommand, iccManager.STK_CMD_SET_UP_MENU); - is(isFirstMenuItemNull(command), false); - - runNextTest(); -} -function testRemoveSetupMenu(command) { - log("STK CMD " + JSON.stringify(command)); - is(command.typeOfCommand, iccManager.STK_CMD_SET_UP_MENU); - is(isFirstMenuItemNull(command), true); - - runNextTest(); -} - -let tests = [ +const TEST_DATA = [ {command: "d03b810301250082028182850c546f6f6c6b6974204d656e758f07014974656d20318f07024974656d20328f07034974656d20338f07044974656d2034", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_1", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}, {identifier: 4, text: "Item 4"}]}}, {command: "d023810301250082028182850c546f6f6c6b6974204d656e758f04114f6e658f041254776f", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_2", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu", items: [{identifier: 17, text: "One"}, {identifier: 18, text: "Two"}]}}, {command: "d081fc810301250082028182850a4c617267654d656e75318f05505a65726f8f044f4f6e658f044e54776f8f064d54687265658f054c466f75728f054b466976658f044a5369788f0649536576656e8f064845696768748f05474e696e658f0646416c7068618f0645427261766f8f0844436861726c69658f064344656c74618f05424563686f8f0941466f782d74726f748f0640426c61636b8f063f42726f776e8f043e5265648f073d4f72616e67658f073c59656c6c6f778f063b477265656e8f053a426c75658f073956696f6c65748f0538477265798f063757686974658f06366d696c6c698f06356d6963726f8f05346e616e6f8f05337069636f", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_3", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "LargeMenu1", items: [{identifier: 80, text: "Zero"}, {identifier: 79, text: "One"}, {identifier: 78, text: "Two"}, {identifier: 77, text: "Three"}, {identifier: 76, text: "Four"}, {identifier: 75, text: "Five"}, {identifier: 74, text: "Six"}, {identifier: 73, text: "Seven"}, {identifier: 72, text: "Eight"}, {identifier: 71, text: "Nine"}, {identifier: 70, text: "Alpha"}, {identifier: 69, text: "Bravo"}, {identifier: 68, text: "Charlie"}, {identifier: 67, text: "Delta"}, {identifier: 66, text: "Echo"}, {identifier: 65, text: "Fox-trot"}, {identifier: 64, text: "Black"}, {identifier: 63, text: "Brown"}, {identifier: 62, text: "Red"}, {identifier: 61, text: "Orange"}, {identifier: 60, text: "Yellow"}, {identifier: 59, text: "Green"}, {identifier: 58, text: "Blue"}, {identifier: 57, text: "Violet"}, {identifier: 56, text: "Grey"}, {identifier: 55, text: "White"}, {identifier: 54, text: "milli"}, {identifier: 53, text: "micro"}, {identifier: 52, text: "nano"}, {identifier: 51, text: "pico"}]}}, {command: "d081f3810301250082028182850a4c617267654d656e75328f1dff312043616c6c20466f727761726420556e636f6e646974696f6e616c8f1cfe322043616c6c20466f7277617264204f6e205573657220427573798f1bfd332043616c6c20466f7277617264204f6e204e6f205265706c798f25fc342043616c6c20466f7277617264204f6e2055736572204e6f7420526561636861626c658f20fb352042617272696e67204f6620416c6c204f7574676f696e672043616c6c738f24fa362042617272696e67204f6620416c6c204f7574676f696e6720496e742043616c6c738f13f93720434c492050726573656e746174696f6e", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_4", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "LargeMenu2", items: [{identifier: 255, text: "1 Call Forward Unconditional"}, {identifier: 254, text: "2 Call Forward On User Busy"}, {identifier: 253, text: "3 Call Forward On No Reply"}, {identifier: 252, text: "4 Call Forward On User Not Reachable"}, {identifier: 251, text: "5 Barring Of All Outgoing Calls"}, {identifier: 250, text: "6 Barring Of All Outgoing Int Calls"}, {identifier: 249, text: "7 CLI Presentation"}]}}, {command: "d081fc8103012500820281828581ec5468652053494d207368616c6c20737570706c79206120736574206f66206d656e75206974656d732c207768696368207368616c6c20626520696e7465677261746564207769746820746865206d656e752073797374656d20286f72206f74686572204d4d4920666163696c6974792920696e206f7264657220746f206769766520746865207573657220746865206f70706f7274756e69747920746f2063686f6f7365206f6e65206f66207468657365206d656e75206974656d7320617420686973206f776e2064697363726574696f6e2e2045616368206974656d20636f6d70726973657320612073688f020159", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_5", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "The SIM shall supply a set of menu items, which shall be integrated with the menu system (or other MMI facility) in order to give the user the opportunity to choose one of these menu items at his own discretion. Each item comprises a sh", items: [{identifier: 1, text: "Y"}]}}, {command: "d03b810301258082028182850c546f6f6c6b6974204d656e758f07014974656d20318f07024974656d20328f07034974656d20338f07044974656d2034", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_6", - commandQualifier: 0x80, + expect: {commandQualifier: 0x80, title: "Toolkit Menu", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}, {identifier: 4, text: "Item 4"}]}}, {command: "d041810301250082028182850c546f6f6c6b6974204d656e758f07014974656d20318f07024974656d20328f07034974656d20338f07044974656d2034180413101526", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_7", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}, {identifier: 4, text: "Item 4"}], - nextActionList: [iccManager.STK_CMD_SEND_SMS, iccManager.STK_CMD_SET_UP_CALL, iccManager.STK_CMD_LAUNCH_BROWSER, iccManager.STK_CMD_PROVIDE_LOCAL_INFO]}}, + nextActionList: [MozIccManager.STK_CMD_SEND_SMS, MozIccManager.STK_CMD_SET_UP_CALL, MozIccManager.STK_CMD_LAUNCH_BROWSER, MozIccManager.STK_CMD_PROVIDE_LOCAL_INFO]}}, {command: "d03c810301250082028182850c546f6f6c6b6974204d656e758f07014974656d20318f07024974656d20328f07034974656d20339e0201019f0401030303", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_8", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu", iconSelfExplanatory: false, - icons: [basicIcon], - items: [{identifier: 1, text: "Item 1", iconSelfExplanatory: false, icons: [colorIcon]}, - {identifier: 2, text: "Item 2", iconSelfExplanatory: false, icons: [colorIcon]}, - {identifier: 3, text: "Item 3", iconSelfExplanatory: false, icons: [colorIcon]}]}}, + icons: [BASIC_ICON], + items: [{identifier: 1, text: "Item 1", iconSelfExplanatory: false, icons: [COLOR_ICON]}, + {identifier: 2, text: "Item 2", iconSelfExplanatory: false, icons: [COLOR_ICON]}, + {identifier: 3, text: "Item 3", iconSelfExplanatory: false, icons: [COLOR_ICON]}]}}, {command: "d03c810301250082028182850c546f6f6c6b6974204d656e758f07014974656d20318f07024974656d20328f07034974656d20339e0200019f0400050505", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_9", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu", iconSelfExplanatory: true, - icons: [basicIcon], - items: [{identifier: 1, text: "Item 1", iconSelfExplanatory: true, icons: [colorTransparencyIcon]}, - {identifier: 2, text: "Item 2", iconSelfExplanatory: true, icons: [colorTransparencyIcon]}, - {identifier: 3, text: "Item 3", iconSelfExplanatory: true, icons: [colorTransparencyIcon]}]}}, + icons: [BASIC_ICON], + items: [{identifier: 1, text: "Item 1", iconSelfExplanatory: true, icons: [COLOR_TRANSPARENCY_ICON]}, + {identifier: 2, text: "Item 2", iconSelfExplanatory: true, icons: [COLOR_TRANSPARENCY_ICON]}, + {identifier: 3, text: "Item 3", iconSelfExplanatory: true, icons: [COLOR_TRANSPARENCY_ICON]}]}}, {command: "d029810301250182028182850c546f6f6c6b6974204d656e758f07014974656d20318f07024974656d2032", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_10", - commandQualifier: 0x01, + expect: {commandQualifier: 0x01, title: "Toolkit Menu", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}]}}, {command: "d048810301250082028182850e546f6f6c6b6974204d656e7520318f07014974656d20318f07024974656d20328f07034974656d2033d004000e00b4d10c000600b4000600b4000600b4", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_11", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, {command: "d034810301250082028182850e546f6f6c6b6974204d656e7520328f07044974656d20348f07054974656d20358f07064974656d2036", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_12", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu 2", items: [{identifier: 4, text: "Item 4"}, {identifier: 5, text: "Item 5"}, {identifier: 6, text: "Item 6"}]}}, {command: "d048810301250082028182850e546f6f6c6b6974204d656e7520318f07014974656d20318f07024974656d20328f07034974656d2033d004000e01b4d10c000601b4000601b4000601b4", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_13", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, {command: "d034810301250082028182850e546f6f6c6b6974204d656e7520328f07044974656d20348f07054974656d20358f07064974656d2036", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_14", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu 2", items: [{identifier: 4, text: "Item 4"}, {identifier: 5, text: "Item 5"}, {identifier: 6, text: "Item 6"}]}}, {command: "d048810301250082028182850e546f6f6c6b6974204d656e7520318f07014974656d20318f07024974656d20328f07034974656d2033d004000e02b4d10c000602b4000602b4000602b4", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_15", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, {command: "d034810301250082028182850e546f6f6c6b6974204d656e7520328f07044974656d20348f07054974656d20358f07064974656d2036", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_16", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu 2", items: [{identifier: 4, text: "Item 4"}, {identifier: 5, text: "Item 5"}, {identifier: 6, text: "Item 6"}]}}, {command: "d048810301250082028182850e546f6f6c6b6974204d656e7520318f07014974656d20318f07024974656d20328f07034974656d2033d004000e04b4d10c000604b4000604b4000604b4", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_17", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, {command: "d048810301250082028182850e546f6f6c6b6974204d656e7520328f07044974656d20348f07054974656d20358f07064974656d2036d004000e00b4d10c000600b4000600b4000600b4", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_18", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu 2", items: [{identifier: 4, text: "Item 4"}, {identifier: 5, text: "Item 5"}, {identifier: 6, text: "Item 6"}]}}, {command: "d034810301250082028182850e546f6f6c6b6974204d656e7520338f07074974656d20378f07084974656d20388f07094974656d2039", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_19", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu 3", items: [{identifier: 7, text: "Item 7"}, {identifier: 8, text: "Item 8"}, {identifier: 9, text: "Item 9"}]}}, {command: "d048810301250082028182850e546f6f6c6b6974204d656e7520318f07014974656d20318f07024974656d20328f07034974656d2033d004000e08b4d10c000608b4000608b4000608b4", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_20", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, {command: "d048810301250082028182850e546f6f6c6b6974204d656e7520318f07014974656d20318f07024974656d20328f07034974656d2033d004000e10b4d10c000610b4000610b4000610b4", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_21", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, {command: "d048810301250082028182850e546f6f6c6b6974204d656e7520318f07014974656d20318f07024974656d20328f07034974656d2033d004000e20b4d10c000620b4000620b4000620b4", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_22", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, {command: "d048810301250082028182850e546f6f6c6b6974204d656e7520318f07014974656d20318f07024974656d20328f07034974656d2033d004000e40b4d10c000640b4000640b4000640b4", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_23", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, {command: "d048810301250082028182850e546f6f6c6b6974204d656e7520318f07014974656d20318f07024974656d20328f07034974656d2033d004000e80b4d10c000680b4000680b4000680b4", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_24", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu 1", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, {command: "d046810301250082028182850c546f6f6c6b6974204d656e758f07014974656d20318f07024974656d20328f07034974656d2033d004000c00b4d10c000600b4000600b4000600b4", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_25", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, {command: "d0819c8103012500820281828519800417041404200410041204210422041204230419042204158f1c018004170414042004100412042104220412042304190422041500318f1c028004170414042004100412042104220412042304190422041500328f1c038004170414042004100412042104220412042304190422041500338f1c04800417041404200410041204210422041204230419042204150034", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_26", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "ЗДРАВСТВУЙТЕ", items: [{identifier: 1, text: "ЗДРАВСТВУЙТЕ1"}, {identifier: 2, text: "ЗДРАВСТВУЙТЕ2"}, {identifier: 3, text: "ЗДРАВСТВУЙТЕ3"}, {identifier: 4, text: "ЗДРАВСТВУЙТЕ4"}]}}, {command: "d0608103012500820281828519800417041404200410041204210422041204230419042204158f1c118004170414042004100412042104220412042304190422041500358f1c12800417041404200410041204210422041204230419042204150036", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_27", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "ЗДРАВСТВУЙТЕ", items: [{identifier: 17, text: "ЗДРАВСТВУЙТЕ5"}, {identifier: 18, text: "ЗДРАВСТВУЙТЕ6"}]}}, {command: "d03c8103012500820281828509805de551777bb153558f080180987976ee4e008f080280987976ee4e8c8f080380987976ee4e098f080480987976ee56db", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_28", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "工具箱单", items: [{identifier: 1, text: "项目一"}, {identifier: 2, text: "项目二"}, {identifier: 3, text: "项目三"}, {identifier: 4, text: "项目四"}]}}, {command: "d0208103012500820281828509805de551777bb153558f0411804e008f0412804e8c", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_29", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "工具箱单", items: [{identifier: 17, text: "一"}, {identifier: 18, text: "二"}]}}, {command: "d0448103012500820281828509800038003030eb00308f0a01800038003030eb00318f0a02800038003030eb00328f0a03800038003030eb00338f0a04800038003030eb0034", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_30", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "80ル0", items: [{identifier: 1, text: "80ル1"}, {identifier: 2, text: "80ル2"}, {identifier: 3, text: "80ル3"}, {identifier: 4, text: "80ル4"}]}}, {command: "d02c8103012500820281828509800038003030eb00308f0a11800038003030eb00358f0a12800038003030eb0036", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_31", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "80ル0", items: [{identifier: 17, text: "80ル5"}, {identifier: 18, text: "80ル6"}]}}, {command: "d041810301250082028182850c546f6f6c6b6974204d656e758f07014974656d20318f07024974656d20328f07034974656d20338f07044974656d2034180481000000", - func: testSetupMenu, - expect: {name: "setup_menu_cmd_32", - commandQualifier: 0x00, + expect: {commandQualifier: 0x00, title: "Toolkit Menu", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}, {identifier: 4, text: "Item 4"}], - nextActionList: [iccManager.STK_NEXT_ACTION_END_PROACTIVE_SESSION, iccManager.STK_NEXT_ACTION_NULL, iccManager.STK_NEXT_ACTION_NULL, iccManager.STK_NEXT_ACTION_NULL]}}, - {command: "D00D81030125008202818285008F00", - func: testRemoveSetupMenu}, + nextActionList: [MozIccManager.STK_NEXT_ACTION_END_PROACTIVE_SESSION, MozIccManager.STK_NEXT_ACTION_NULL, MozIccManager.STK_NEXT_ACTION_NULL, MozIccManager.STK_NEXT_ACTION_NULL]}}, {command:"D03B810301250082028182850C546F6F6C6B6974204D656E758F07014974656D20318F07024974656D20328F07034974656D20338F07044974656D2034", - func: testInitialSetupMenu}, - + expect: {commandQualifier: 0x00, + title: "Toolkit Menu", + items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}, {identifier: 4, text: "Item 4"}]}}, + // Test remove setup menu. + {command: "D00D81030125008202818285008F00", + expect: {commandQualifier: 0x00, + title: "", + items: [null]}}, ]; -runNextTest(); +function testSetupMenu(aCommand, aExpect) { + is(aCommand.typeOfCommand, MozIccManager.STK_CMD_SET_UP_MENU, "typeOfCommand"); + is(aCommand.commandQualifier, aExpect.commandQualifier, "commandQualifier"); + is(aCommand.options.title, aExpect.title, "options.title"); + // To ensure that 'presentationType' will only be available in SELECT_ITEM. + is(aCommand.options.presentationType, undefined, "presentationType"); + + for (let index in aExpect.items) { + let item = aCommand.options.items[index]; + let itemExpect = aExpect.items[index]; + + if (!itemExpect) { + is(item, itemExpect, "options.items[" + index + "]"); + } else { + is(item.identifier, itemExpect.identifier, + "options.items[" + index + "].identifier"); + is(item.text, itemExpect.text, + "options.items[" + index + "].text"); + + if (itemExpect.icons) { + isIcons(item.icons, itemExpect.icons); + is(item.iconSelfExplanatory, itemExpect.iconSelfExplanatory, + "options.items[" + index + "].iconSelfExplanatory"); + } + } + } + + if (aExpect.icons) { + isIcons(aCommand.options.icons, aExpect.icons); + is(aCommand.options.iconSelfExplanatory, aExpect.iconSelfExplanatory, + "options.iconSelfExplanatory"); + } + + if (aExpect.nextActionList) { + for (let index in aExpect.nextActionList) { + is(aCommand.options.nextActionList[index], aExpect.nextActionList[index], + "options.nextActionList[" + index + "]"); + } + } +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("setup_menu_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testSetupMenu(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); + + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_timer_management.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_timer_management.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/icc/tests/marionette/test_stk_timer_management.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/icc/tests/marionette/test_stk_timer_management.js 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,61 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; + +const TEST_DATA = [ + // Start + {command: "d011810301270082028182a40101a503102030", + expect: {commandNumber: 0x01, + commandQualifier: MozIccManager.STK_TIMER_START, + timerAction: MozIccManager.STK_TIMER_START, + timerId: 0x01, + timerValue: (0x01 * 60 * 60) + (0x02 * 60) + 0x03}}, + // Deactivate + {command: "d00c810301270182028182a40104", + expect: {commandNumber: 0x01, + commandQualifier: MozIccManager.STK_TIMER_DEACTIVATE, + timerAction: MozIccManager.STK_TIMER_DEACTIVATE, + timerId: 0x04}}, + // Get current value + {command: "d00c810301270282028182a40108", + expect: {commandNumber: 0x01, + commandQualifier: MozIccManager.STK_TIMER_GET_CURRENT_VALUE, + timerAction: MozIccManager.STK_TIMER_GET_CURRENT_VALUE, + timerId: 0x08}}, +]; + +function testTimerManagement(aCommand, aExpect) { + is(aCommand.typeOfCommand, MozIccManager.STK_CMD_TIMER_MANAGEMENT, + "typeOfCommand"); + is(aCommand.commandQualifier, aExpect.commandQualifier, "commandQualifier"); + is(aCommand.options.timerAction, aExpect.timerAction, "options.timerAction"); + is(aCommand.options.timerId, aExpect.timerId, "options.timerId"); + + if (aExpect.timerValue) { + is(aCommand.options.timerValue, aExpect.timerValue, "options.timerValue"); + } +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => { + log("timer_management_cmd: " + data.command); + + let promises = []; + // Wait onstkcommand event. + promises.push(waitForTargetEvent(icc, "stkcommand") + .then((aEvent) => testTimerManagement(aEvent.command, data.expect))); + // Send emulator command to generate stk unsolicited event. + promises.push(sendEmulatorStkPdu(data.command)); + + return Promise.all(promises); + }); + } + return promise; +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/indexedDB/ActorsParent.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/indexedDB/ActorsParent.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/indexedDB/ActorsParent.cpp 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/indexedDB/ActorsParent.cpp 2015-02-03 14:33:38.000000000 +0000 @@ -146,8 +146,6 @@ const char kSavepointClause[] = "SAVEPOINT sp;"; -const fallible_t fallible = fallible_t(); - const uint32_t kFileCopyBufferSize = 32768; const char kJournalDirectoryName[] = "journals"; @@ -12273,12 +12271,12 @@ MOZ_ASSERT(thisDB->mDatabaseId == otherDB->mDatabaseId); MOZ_ASSERT(thisDB->mFilePath == otherDB->mFilePath); - // The newer database metadata (db2) reflects the latest objectStore and index - // ids that have committed to disk. The in-memory metadata (db1) keeps track - // of objectStores and indexes that were created and then removed as well, so - // the next ids for db1 may be higher than for db2. - MOZ_ASSERT(thisDB->mNextObjectStoreId >= otherDB->mNextObjectStoreId); - MOZ_ASSERT(thisDB->mNextIndexId >= otherDB->mNextIndexId); + // |thisDB| reflects the latest objectStore and index ids that have committed + // to disk. The in-memory metadata |otherDB| keeps track of objectStores and + // indexes that were created and then removed as well, so the next ids for + // |otherDB| may be higher than for |thisDB|. + MOZ_ASSERT(thisDB->mNextObjectStoreId <= otherDB->mNextObjectStoreId); + MOZ_ASSERT(thisDB->mNextIndexId <= otherDB->mNextIndexId); MOZ_ASSERT(thisDB->mObjectStores.Count() == otherDB->mObjectStores.Count()); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/indexedDB/IDBObjectStore.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/indexedDB/IDBObjectStore.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/indexedDB/IDBObjectStore.cpp 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/indexedDB/IDBObjectStore.cpp 2015-02-03 14:33:38.000000000 +0000 @@ -464,7 +464,7 @@ } length = NativeEndian::swapFromLittleEndian(length); - if (!aString.SetLength(length, fallible_t())) { + if (!aString.SetLength(length, fallible)) { NS_WARNING("Out of memory?"); return false; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/indexedDB/KeyPath.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/indexedDB/KeyPath.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/indexedDB/KeyPath.cpp 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/indexedDB/KeyPath.cpp 2015-02-03 14:33:39.000000000 +0000 @@ -172,8 +172,7 @@ } else { JS::Rooted dummy(aCx, - JS_NewObject(aCx, IDBObjectStore::DummyPropClass(), JS::NullPtr(), - JS::NullPtr())); + JS_NewObject(aCx, IDBObjectStore::DummyPropClass())); if (!dummy) { IDB_REPORT_INTERNAL_ERR(); rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/indexedDB/test/browser.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/indexedDB/test/browser.ini --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/indexedDB/test/browser.ini 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/indexedDB/test/browser.ini 2015-02-03 14:33:39.000000000 +0000 @@ -1,6 +1,5 @@ [DEFAULT] -run-if = buildapp == "browser" -skip-if = e10s +skip-if = (buildapp != "browser") || e10s support-files = head.js browser_forgetThisSiteAdd.html diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/inputmethod/forms.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/inputmethod/forms.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/inputmethod/forms.js 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/inputmethod/forms.js 2015-02-03 14:33:39.000000000 +0000 @@ -1207,15 +1207,43 @@ let CompositionManager = { _isStarted: false, + _textInputProcessor: null, _clauseAttrMap: { 'raw-input': - Ci.nsICompositionStringSynthesizer.ATTR_RAWINPUT, + Ci.nsITextInputProcessor.ATTR_RAW_CLAUSE, 'selected-raw-text': - Ci.nsICompositionStringSynthesizer.ATTR_SELECTEDRAWTEXT, + Ci.nsITextInputProcessor.ATTR_SELECTED_RAW_CLAUSE, 'converted-text': - Ci.nsICompositionStringSynthesizer.ATTR_CONVERTEDTEXT, + Ci.nsITextInputProcessor.ATTR_CONVERTED_CLAUSE, 'selected-converted-text': - Ci.nsICompositionStringSynthesizer.ATTR_SELECTEDCONVERTEDTEXT + Ci.nsITextInputProcessor.ATTR_SELECTED_CLAUSE + }, + + _callback: function cm_callback(aTIP, aNotification) + { + try { + switch (aNotification.type) { + case "request-to-commit": + aTIP.commitComposition(); + break; + case "request-to-cancel": + aTIP.cancelComposition(); + break; + } + } catch (e) { + return false; + } + return true; + }, + + _prepareTextInputProcessor: function cm_prepareTextInputProcessor(aWindow) + { + if (!this._textInputProcessor) { + this._textInputProcessor = + Cc["@mozilla.org/text-input-processor;1"]. + createInstance(Ci.nsITextInputProcessor); + } + return this._textInputProcessor.init(aWindow, this._callback); }, setComposition: function cm_setComposition(element, text, cursor, clauses) { @@ -1242,7 +1270,7 @@ remainingLength -= clauseLength; clauseLens.push(clauseLength); clauseAttrs.push(this._clauseAttrMap[clauses[i].selectionType] || - Ci.nsICompositionStringSynthesizer.ATTR_RAWINPUT); + Ci.nsITextInputProcessor.ATTR_RAW_CLAUSE); } } // If the total clauses length is less than that of the composition @@ -1252,32 +1280,33 @@ } } else { clauseLens.push(len); - clauseAttrs.push(Ci.nsICompositionStringSynthesizer.ATTR_RAWINPUT); + clauseAttrs.push(Ci.nsITextInputProcessor.ATTR_RAW_CLAUSE); } - // Start composition if need to. - if (!this._isStarted) { - this._isStarted = true; - domWindowUtils.sendCompositionEvent('compositionstart', '', ''); + let win = element.ownerDocument.defaultView; + if (!this._prepareTextInputProcessor(win)) { + return; } - // Update the composing text. - let compositionString = domWindowUtils.createCompositionStringSynthesizer(); - compositionString.setString(text); + this._textInputProcessor.setPendingCompositionString(text); for (var i = 0; i < clauseLens.length; i++) { - compositionString.appendClause(clauseLens[i], clauseAttrs[i]); + if (!clauseLens[i]) { + continue; + } + this._textInputProcessor.appendClauseToPendingComposition(clauseLens[i], + clauseAttrs[i]); } if (cursor >= 0) { - compositionString.setCaret(cursor, 0); + this._textInputProcessor.setCaretInPendingComposition(cursor); } - compositionString.dispatchEvent(); + this._isStarted = this._textInputProcessor.flushPendingComposition(); }, endComposition: function cm_endComposition(text) { if (!this._isStarted) { return; } - domWindowUtils.sendCompositionEvent('compositioncommit', text, ''); + this._textInputProcessor.commitComposition(text ? text : ""); this._isStarted = false; }, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/interfaces/apps/mozIApplication.idl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/interfaces/apps/mozIApplication.idl --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/interfaces/apps/mozIApplication.idl 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/interfaces/apps/mozIApplication.idl 2015-02-03 14:33:40.000000000 +0000 @@ -11,7 +11,7 @@ * We expose Gecko-internal helpers related to "web apps" through this * sub-interface. */ -[scriptable, uuid(1d856b11-ac29-47d3-bd52-a86e3d45fcf4)] +[scriptable, uuid(1d856b11-ac29-47d3-bd52-a86e3d45fcf5)] interface mozIApplication: nsISupports { /* Return true if this app has |permission|. */ @@ -55,4 +55,7 @@ /* role copied from the manifest */ readonly attribute DOMString role; + + /* Returns the kind of the app. */ + readonly attribute DOMString kind; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/interfaces/base/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/interfaces/base/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/interfaces/base/moz.build 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/interfaces/base/moz.build 2015-02-03 14:33:40.000000000 +0000 @@ -7,7 +7,6 @@ XPIDL_SOURCES += [ 'domstubs.idl', 'nsIBrowserDOMWindow.idl', - 'nsICompositionStringSynthesizer.idl', 'nsIContentPermissionPrompt.idl', 'nsIContentPrefService.idl', 'nsIContentPrefService2.idl', @@ -36,6 +35,8 @@ 'nsIStructuredCloneContainer.idl', 'nsITabChild.idl', 'nsITabParent.idl', + 'nsITextInputProcessor.idl', + 'nsITextInputProcessorCallback.idl', ] XPIDL_MODULE = 'dom_base' diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/interfaces/base/nsICompositionStringSynthesizer.idl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/interfaces/base/nsICompositionStringSynthesizer.idl --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/interfaces/base/nsICompositionStringSynthesizer.idl 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/interfaces/base/nsICompositionStringSynthesizer.idl 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -/* -*- Mode: IDL; 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 "nsISupports.idl" - -/** - * Stores composition clauses information and caret information for synthesizing - * composition string. - */ - -[scriptable, uuid(9a7d7851-8c0a-4061-9edc-60d6693f86c9)] -interface nsICompositionStringSynthesizer : nsISupports -{ - /** - * Set composition string or committed string. - */ - void setString(in AString aString); - - // NOTE: These values must be same to NS_TEXTRANGE_* in TextEvents.h - const unsigned long ATTR_RAWINPUT = 0x02; - const unsigned long ATTR_SELECTEDRAWTEXT = 0x03; - const unsigned long ATTR_CONVERTEDTEXT = 0x04; - const unsigned long ATTR_SELECTEDCONVERTEDTEXT = 0x05; - - /** - * Append a clause. - * - * TODO: Should be able to specify custom clause style. - */ - void appendClause(in unsigned long aLength, - in unsigned long aAttribute); - - /** - * Set caret information. - */ - void setCaret(in unsigned long aOffset, - in unsigned long aLength); - - /** - * Synthesize composition string with given information by dispatching - * a proper event. - * - * If clauses have never been set, this dispatches a commit event. - * If clauses are not filled all over the composition string, this throw an - * error. - * - * After dispatching event, this clears all the information about the - * composition string. So, you can reuse this instance. - */ - bool dispatchEvent(); -}; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/interfaces/base/nsIDOMWindowUtils.idl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/interfaces/base/nsIDOMWindowUtils.idl --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/interfaces/base/nsIDOMWindowUtils.idl 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/interfaces/base/nsIDOMWindowUtils.idl 2015-02-03 14:33:40.000000000 +0000 @@ -46,12 +46,11 @@ interface nsIURI; interface nsIDOMEventTarget; interface nsIRunnable; -interface nsICompositionStringSynthesizer; interface nsITranslationNodeList; interface nsIJSRAIIHelper; interface nsIContentPermissionRequest; -[scriptable, uuid(0281e107-394d-4c96-830b-2f830b07c6c0)] +[scriptable, uuid(5ed850de-2b57-4555-ac48-93292e852eab)] interface nsIDOMWindowUtils : nsISupports { /** @@ -557,8 +556,6 @@ 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; boolean sendKeyEvent(in AString aType, in long aKeyCode, @@ -1036,33 +1033,6 @@ [optional] in nsITransferable aTransferable); /** - * Synthesize a composition event to the window. - * - * Cannot be accessed from unprivileged context (not content-accessible) - * Will throw a DOM security error if called without chrome privileges. - * - * @param aType The event type: "compositionstart", - * "compositioncommitasis", or "compositioncommit". - * @param aData The data property value. Note that this isn't applied - * for compositionstart event because its value is the - * selected text which is automatically computed. And also - * this isn't applied for compositioncommitasis because - * the last data will be set automatically. - * @param aLocale The locale property value. - */ - void sendCompositionEvent(in AString aType, - in AString aData, - in AString aLocale); - - /** - * Creating synthesizer of composition string on the window. - * - * Cannot be accessed from unprivileged context (not content-accessible) - * Will throw a DOM security error if called without chrome privileges. - */ - nsICompositionStringSynthesizer createCompositionStringSynthesizer(); - - /** * If sendQueryContentEvent()'s aAdditionalFlags argument is * QUERY_CONTENT_FLAG_USE_XP_LINE_BREAK, plain text generated from content * is created with "\n". diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/interfaces/base/nsITextInputProcessorCallback.idl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/interfaces/base/nsITextInputProcessorCallback.idl --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/interfaces/base/nsITextInputProcessorCallback.idl 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/interfaces/base/nsITextInputProcessorCallback.idl 2015-02-03 14:33:40.000000000 +0000 @@ -0,0 +1,102 @@ +/* -*- Mode: IDL; 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 "nsISupports.idl" + +interface nsITextInputProcessor; + +/** + * nsITextInputProcessorNotification stores the type of notification to IME and + * its detail. See each explanation of attribute for the detail. + */ + +[scriptable, builtinclass, uuid(c0ce1add-82bb-45ab-b99a-42cfba7fd5d7)] +interface nsITextInputProcessorNotification : nsISupports +{ + /** + * type attribute represents what's notified or requested. Value must be + * one of following values: + * + * "request-to-commit" (required to be handled) + * This is requested when Gecko believes that active composition should be + * committed. nsITextInputProcessorCallback::onNotify() has to handle this + * notification. + * + * "request-to-cancel" (required to be handled) + * This is requested when Gecko believes that active composition should be + * canceled. I.e., composition should be committed with empty string. + * nsITextInputProcessorCallback::onNotify() has to handle this + * notification. + * + * "notify-detached" (optional) + * This is notified when the callback is detached from + * nsITextInputProcessor. + * + * "notify-focus" (optional) + * This is notified when an editable editor gets focus and Gecko starts + * to observe changes in the content. E.g., selection changes. + * IME shouldn't change DOM tree, focus nor something when this is notified. + * + * "notify-blur" (optional) + * This is notified when an editable editor loses focus and Gecko stops + * observing the changes in the content. + */ + readonly attribute ACString type; +}; + +/** + * nsITextInputProcessorCallback is a callback interface for JS to implement + * IME. IME implemented by JS can implement onNotify() function and must send + * it to nsITextInputProcessor at initializing. Then, onNotify() will be + * called with nsITextInputProcessorNotification instance. + * The reason why onNotify() uses string simply is that if we will support + * other notifications such as text changes and selection changes, we need to + * notify IME of some other information. Then, only changing + * nsITextInputProcessorNotification interface is better for compatibility. + */ + +[scriptable, function, uuid(23d5f242-adb5-46f1-8766-90d1bf0383df)] +interface nsITextInputProcessorCallback : nsISupports +{ + /** + * When Gecko notifies IME of something or requests something to IME, + * this is called. + * + * @param aTextInputProcessor Reference to the nsITextInputProcessor service + * which is the original receiver of the request + * or notification. + * @param aNotification Stores type of notifications and additional + * information. + * @return Return true if it succeeded or does nothing. + * Otherwise, return false. + * + * Example #1 The simplest implementation of nsITextInputProcessorCallback is: + * + * function simpleCallback(aTIP, aNotification) + * { + * try { + * switch (aNotification.type) { + * case "request-to-commit": + * aTIP.commitComposition(); + * break; + * case "request-to-cancel": + * aTIP.cancelComposition(); + * break; + * } + * } catch (e) { + * return false; + * } + * return true; + * } + * + * var TIP = Components.classes["@mozilla.org/text-input-processor;1"]. + * createInstance(Components.interfaces.nsITextInputProcessor); + * if (!TIP.init(window, simpleCallback)) { + * return; + * } + */ + boolean onNotify(in nsITextInputProcessor aTextInputProcessor, + in nsITextInputProcessorNotification aNotification); +}; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/interfaces/base/nsITextInputProcessor.idl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/interfaces/base/nsITextInputProcessor.idl --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/interfaces/base/nsITextInputProcessor.idl 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/interfaces/base/nsITextInputProcessor.idl 2015-02-03 14:33:40.000000000 +0000 @@ -0,0 +1,274 @@ +/* -*- Mode: IDL; 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 "nsISupports.idl" + +interface nsIDOMWindow; +interface nsITextInputProcessorCallback; + +/** + * An nsITextInputProcessor instance is associated with a top level widget which + * handles native IME. It's associated by calling init() or initForTests(). + * While an instance has composition, nobody can steal the rights to make + * composition on the top level widget. In other words, if another instance is + * composing on a top level widget, either init() or initForTests() returns + * false (i.e., not throws an exception). + * + * NOTE: See nsITextInputProcessorCallback.idl for examples of |callback| in + * following examples, + * + * Example #1 JS-IME can start composition like this: + * + * var TIP = Components.classes["@mozilla.org/text-input-processor;1"]. + * createInstance(Components.interfaces.nsITextInputProcessor); + * if (!TIP.init(window, callback)) { + * return; // You failed to get the rights to make composition + * } + * // Set new composition string first + * TIP.setPendingCompositionString("some-words-are-inputted"); + * // Set clause information. + * TIP.appendClauseToPendingComposition(23, TIP.ATTR_RAW_CLAUSE); + * // Set caret position, this is optional. + * TIP.setCaretInPendingComposition(23); + * // Flush the pending composition + * if (!TIP.flushPendingComposition()) { + * // If it returns false, it fails to start composition. + * return; + * } + * + * Example #2 JS-IME can separate composition string to two or more clauses: + * + * // First, set composition string again + * TIP.setPendingCompositionString("some-words-are-inputted"); + * // Then, if "are" is selected to convert, there are 3 clauses: + * TIP.appendClauseToPendingComposition(11, TIP.ATTR_CONVERTED_CLAUSE); + * TIP.appendClauseToPendingComposition(3, TIP.ATTR_SELECTED_CLAUSE); + * TIP.appendClauseToPendingComposition(9, TIP.ATTR_CONVERTED_CLAUSE); + * // Show caret at the beginning of the selected clause + * TIP.setCaretInPendingComposition(11); + * // Flush the pending composition. Note that if there is a composition, + * // flushPendingComposition() won't return false. + * TIP.flushPendingComposition(); + * + * Example #3 JS-IME can commit composition with specific string with this: + * + * // First, there is a composition. + * TIP.setPendingCompositionString("some-words-directly-inputted"); + * TIP.appendClauseToPendingComposition(28, TIP.ATTR_RAW_CLAUSE); + * TIP.flushPendingComposition(); + * // This is useful when user selects a commit string from candidate list UI + * // which is provided by JS-IME. + * TIP.commitComposition("selected-words-from-candidate-list"); + * + * Example #4 JS-IME can commit composition with the last composition string + * without specifying commit string: + * + * // First, there is a composition. + * TIP.setPendingCompositionString("some-words-will-be-commited"); + * TIP.appendClauseToPendingComposition(27, TIP.ATTR_RAW_CLAUSE); + * TIP.flushPendingComposition(); + * // This is useful when user just type Enter key. + * TIP.commitComposition(); + * + * Example #5 JS-IME can cancel composition with this: + * + * // First, there is a composition. + * TIP.setPendingCompositionString("some-words-will-be-canceled"); + * TIP.appendClauseToPendingComposition(27, TIP.ATTR_RAW_CLAUSE); + * TIP.flushPendingComposition(); + * // This is useful when user doesn't want to commit the composition. + * // FYI: This is same as TIP.commitComposition("") for now. + * TIP.cancelComposition(); + * + * Example #6 JS-IME can insert text only with commitComposition(): + * + * if (!TIP.init(window, callback)) { + * return; // You failed to get the rights to make composition + * } + * TIP.commitComposition("Some words"); + * + * Example #7 JS-IME can start composition explicitly: + * + * if (!TIP.init(window, callback)) { + * return; // You failed to get the rights to make composition + * } + * // If JS-IME don't want to show composing string in the focused editor, + * // JS-IME can dispatch only compositionstart event with this. + * if (!TIP.startComposition()) { + * // Failed to start composition. + * return; + * } + * // And when user selects a result from UI of JS-IME, commit with it. + * TIP.commitComposition("selected-words"); + */ + +[scriptable, builtinclass, uuid(8c20753c-8339-4e9c-86c5-ae30f1b456c3)] +interface nsITextInputProcessor : nsISupports +{ + /** + * When you create an instance, you must call init() first except when you + * created the instance for automated tests. + * + * @param aWindow A DOM window. The instance will look for a top + * level widget from this. + * @param aCallback Callback interface which handles requests to + * IME and notifications to IME. This must not be + * null. + * @return If somebody uses internal text input service for a + * composition, this returns false. Otherwise, returns + * true. I.e., only your TIP can create composition + * when this returns true. If this returns false, + * your TIP should wait next chance. + */ + boolean init(in nsIDOMWindow aWindow, + in nsITextInputProcessorCallback aCallback); + + /** + * When you create an instance for automated test, you must call + * initForTest(), first. See init() for more detail of this. + * Note that aCallback can be null. If it's null, nsITextInputProcessor + * implementation will handle them automatically. + */ + [optional_argc] boolean + initForTests(in nsIDOMWindow aWindow, + [optional] in nsITextInputProcessorCallback aCallback); + + /** + * startComposition() dispatches compositionstart event explicitly. + * IME does NOT need to call this typically since compositionstart event + * is automatically dispatched by sendPendingComposition() if + * compositionstart event hasn't been dispatched yet. If this is called + * when compositionstart has already been dispatched, this throws an + * exception. + * + * @return Returns true if composition starts normally. + * Otherwise, returns false because it might be + * canceled by the web application. + */ + boolean startComposition(); + + /** + * Set new composition string. Pending composition will be flushed by + * a call of flushPendingComposition(). However, if the new composition + * string isn't empty, you need to call appendClauseToPendingComposition() to + * fill all characters of aString with one or more clauses before flushing. + * Note that if you need to commit or cancel composition, use + * commitComposition() or cancelComposition(). + */ + void setPendingCompositionString(in DOMString aString); + + // ATTR_RAW_CLAUSE means that the clause hasn't been selected nor converted + // yet. + const unsigned long ATTR_RAW_CLAUSE = 0x02; + // ATTR_SELECTED_RAW_CLAUSE means that the clause hasn't been converted yet + // but is selected for converting to the other string. + const unsigned long ATTR_SELECTED_RAW_CLAUSE = 0x03; + // ATTR_CONVERTED_CLAUSE means that the clause has already been converted but + // is not selected. This does NOT mean that this clause isn't modifiable. + const unsigned long ATTR_CONVERTED_CLAUSE = 0x04; + // ATTR_SELECTED_CLAUSE means that the clause has already been converted and + // is selected. In other words, the clause is being converted. + const unsigned long ATTR_SELECTED_CLAUSE = 0x05; + + /** + * Append a clause to the pending composition. + * + * If you need to fill the pending composition string with a clause, you + * should call this once. For example: + * appendClauseToPendingComposition(compositionString.length, + * ATTR_RAW_CLAUSE); + * is enough. If you need to separate the pending composition string to + * multiple clauses, you need to call this multiple times. For example, + * if your pending composition string has three clauses and the second clause + * is being converted: + * appendClauseToPendingComposition(firstClauseLength, + * ATTR_CONVERTED_CLAUSE); + * appendClauseToPendingComposition(secondClauseLength, + * ATTR_SELECTED_CLAUSE); + * appendClauseToPendingComposition(thirdClauseLength, + * ATTR_CONVERTED_CLAUSE); + * Note that if sum of aLength mismatches length of the pending composition + * string, flushPendingComposition() will throw an exception. I.e., + * |firstClauseLength + secondClauseLength + thirdClauseLength| must be + * same as the length of pending composition string. + * + * TODO: Should be able to specify custom clause style. + * + * @param aLength Length of the clause. + * @param aAttribute One of ATTR_* constants. + */ + void appendClauseToPendingComposition(in unsigned long aLength, + in unsigned long aAttribute); + + /** + * Set caret offset in the pending composition string. If you don't need to + * show a caret, you don't need to call this. + * + * @param aOffset Caret offset in the pending composition string. + * This must be between 0 and length of the pending + * composition string. + */ + void setCaretInPendingComposition(in unsigned long aOffset); + + /** + * flushPendingComposition() must be called after + * setPendingCompositionString() and appendClauseToPendingComposition() + * (setCaretInPendingComposition() is optional) are called. + * + * Note that compositionstart will be automatically dispatched if this is + * called when there is no composition. + * + * Note that if sum of lengths of appended clauses are not same as composition + * string or caret offset is larger than the composition string length, this + * throws an exception. + * + * @return Returns true if there is a composition already or + * starting composition automatically. + * Otherwise, i.e., if it cannot start composition + * automatically, e.g., canceled by web apps, returns + * false. + */ + boolean flushPendingComposition(); + + /** + * commitComposition() will commit composition. + * If there is a composition which is started by the instance, this commits + * the composition. Otherwise, throws an exception. + * + * Note that if you tries to commit composition without specifying commit + * string when there is no composition, this throws an exception. + * + * @param aCommitString This is optional. If this is not specified, + * composition will be committed with the last + * composing string. Otherwise, committed with + * the specified string. + * @return Returns true if there is a composition already or + * starting composition automatically. + * Otherwise, i.e., if it cannot start composition + * automatically, e.g., canceled by web apps, returns + * false. + */ + [optional_argc] + boolean commitComposition([optional] in DOMString aCommitString); + + /** + * cancelComposition() will cancel composition. This is for now the same as + * calling commitComposition(""). However, in the future, this might work + * better. If your IME needs to cancel composition, use this instead of + * commitComposition(). + * + * Note that if you tries to cancel composition when there is no composition, + * this throws an exception. + */ + void cancelComposition(); +}; + +%{C++ +#define TEXT_INPUT_PROCESSOR_CID \ + { 0xcaaab47f, 0x1e31, 0x478e, \ + { 0x89, 0x19, 0x97, 0x09, 0x04, 0xe9, 0xcb, 0x72 } } +#define TEXT_INPUT_PROCESSOR_CONTRACTID \ + "@mozilla.org/text-input-processor;1" +%} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/interfaces/events/nsIDOMKeyEvent.idl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/interfaces/events/nsIDOMKeyEvent.idl --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/interfaces/events/nsIDOMKeyEvent.idl 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/interfaces/events/nsIDOMKeyEvent.idl 2015-02-03 14:33:40.000000000 +0000 @@ -250,8 +250,6 @@ const unsigned long DOM_KEY_LOCATION_LEFT = 0x01; const unsigned long DOM_KEY_LOCATION_RIGHT = 0x02; const unsigned long DOM_KEY_LOCATION_NUMPAD = 0x03; - const unsigned long DOM_KEY_LOCATION_MOBILE = 0x04; - const unsigned long DOM_KEY_LOCATION_JOYSTICK = 0x05; readonly attribute unsigned long location; readonly attribute boolean repeat; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/ContentBridgeChild.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/ContentBridgeChild.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/ContentBridgeChild.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/ContentBridgeChild.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -10,8 +10,8 @@ #include "mozilla/dom/StructuredCloneUtils.h" #include "mozilla/dom/TabChild.h" #include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "mozilla/ipc/InputStreamUtils.h" -#include "JavaScriptChild.h" using namespace base; using namespace mozilla::ipc; @@ -100,14 +100,13 @@ // This implementation is identical to ContentChild::GetCPOWManager but we can't // move it to nsIContentChild because it calls ManagedPJavaScriptChild() which // only exists in PContentChild and PContentBridgeChild. -jsipc::JavaScriptShared* +jsipc::CPOWManager* ContentBridgeChild::GetCPOWManager() { if (ManagedPJavaScriptChild().Length()) { - return static_cast(ManagedPJavaScriptChild()[0]); + return CPOWManagerFor(ManagedPJavaScriptChild()[0]); } - JavaScriptChild* actor = static_cast(SendPJavaScriptConstructor()); - return actor; + return CPOWManagerFor(SendPJavaScriptConstructor()); } mozilla::jsipc::PJavaScriptChild * diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/ContentBridgeChild.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/ContentBridgeChild.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/ContentBridgeChild.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/ContentBridgeChild.h 2015-02-03 14:33:40.000000000 +0000 @@ -36,7 +36,7 @@ SendPBlobConstructor(PBlobChild* actor, const BlobConstructorParams& aParams) MOZ_OVERRIDE; - jsipc::JavaScriptShared* GetCPOWManager() MOZ_OVERRIDE; + jsipc::CPOWManager* GetCPOWManager() MOZ_OVERRIDE; virtual bool SendPBrowserConstructor(PBrowserChild* aActor, const TabId& aTabId, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/ContentBridgeParent.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/ContentBridgeParent.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/ContentBridgeParent.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/ContentBridgeParent.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -6,7 +6,7 @@ #include "mozilla/dom/ContentBridgeParent.h" #include "mozilla/dom/TabParent.h" -#include "JavaScriptParent.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "nsXULAppAPI.h" using namespace base; @@ -154,14 +154,13 @@ // This implementation is identical to ContentParent::GetCPOWManager but we can't // move it to nsIContentParent because it calls ManagedPJavaScriptParent() which // only exists in PContentParent and PContentBridgeParent. -jsipc::JavaScriptShared* +jsipc::CPOWManager* ContentBridgeParent::GetCPOWManager() { if (ManagedPJavaScriptParent().Length()) { - return static_cast(ManagedPJavaScriptParent()[0]); + return CPOWManagerFor(ManagedPJavaScriptParent()[0]); } - JavaScriptParent* actor = static_cast(SendPJavaScriptConstructor()); - return actor; + return CPOWManagerFor(SendPJavaScriptConstructor()); } } // namespace dom diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/ContentBridgeParent.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/ContentBridgeParent.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/ContentBridgeParent.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/ContentBridgeParent.h 2015-02-03 14:33:40.000000000 +0000 @@ -41,7 +41,7 @@ const bool& aIsForApp, const bool& aIsForBrowser) MOZ_OVERRIDE; - jsipc::JavaScriptShared* GetCPOWManager() MOZ_OVERRIDE; + jsipc::CPOWManager* GetCPOWManager() MOZ_OVERRIDE; virtual ContentParentId ChildID() MOZ_OVERRIDE { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/ContentChild.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/ContentChild.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/ContentChild.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/ContentChild.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -41,6 +41,7 @@ #include "mozilla/ipc/FileDescriptorUtils.h" #include "mozilla/ipc/GeckoChildProcessHost.h" #include "mozilla/ipc/TestShellChild.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "mozilla/layers/CompositorChild.h" #include "mozilla/layers/ImageBridgeChild.h" #include "mozilla/layers/PCompositorChild.h" @@ -163,7 +164,6 @@ #include "nsIPrincipal.h" #include "nsDeviceStorage.h" #include "AudioChannelService.h" -#include "JavaScriptChild.h" #include "mozilla/dom/DataStoreService.h" #include "mozilla/dom/telephony/PTelephonyChild.h" #include "mozilla/dom/time/DateCacheCleaner.h" @@ -1403,14 +1403,13 @@ return true; } -jsipc::JavaScriptShared* +jsipc::CPOWManager* ContentChild::GetCPOWManager() { if (ManagedPJavaScriptChild().Length()) { - return static_cast(ManagedPJavaScriptChild()[0]); + return CPOWManagerFor(ManagedPJavaScriptChild()[0]); } - JavaScriptChild* actor = static_cast(SendPJavaScriptConstructor()); - return actor; + return CPOWManagerFor(SendPJavaScriptConstructor()); } bool @@ -1911,7 +1910,7 @@ nsRefPtr cpm = nsFrameMessageManager::sChildProcessManager; if (cpm) { StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForChild(aData); - CpowIdHolder cpows(this, aCpows); + CrossProcessCpowHolder cpows(this, aCpows); cpm->ReceiveMessage(static_cast(cpm.get()), aMsg, false, &cloneData, &cpows, aPrincipal, nullptr); } @@ -2138,6 +2137,18 @@ } bool +ContentChild::RecvVolumes(nsTArray&& aVolumes) +{ +#ifdef MOZ_WIDGET_GONK + nsRefPtr vs = nsVolumeService::GetSingleton(); + if (vs) { + vs->RecvVolumesFromParent(aVolumes); + } +#endif + return true; +} + +bool ContentChild::RecvFilePathUpdate(const nsString& aStorageType, const nsString& aStorageName, const nsString& aPath, @@ -2485,7 +2496,10 @@ bool ContentChild::RecvLoadPluginResult(const uint32_t& aPluginId, const bool& aResult) { - bool finalResult = aResult && SendConnectPluginBridge(aPluginId); + nsresult rv; + bool finalResult = aResult && + SendConnectPluginBridge(aPluginId, &rv) && + NS_SUCCEEDED(rv); plugins::PluginModuleContentParent::OnLoadPluginResult(aPluginId, finalResult); return true; @@ -2525,6 +2539,41 @@ } } +// This code goes here rather than nsGlobalWindow.cpp because nsGlobalWindow.cpp +// can't include ContentChild.h since it includes windows.h. + +static uint64_t gNextWindowID = 0; + +// We use only 53 bits for the window ID so that it can be converted to and from +// a JS value without loss of precision. The upper bits of the window ID hold the +// process ID. The lower bits identify the window. +static const uint64_t kWindowIDTotalBits = 53; +static const uint64_t kWindowIDProcessBits = 22; +static const uint64_t kWindowIDWindowBits = kWindowIDTotalBits - kWindowIDProcessBits; + +// Try to return a window ID that is unique across processes and that will never +// be recycled. +uint64_t +NextWindowID() +{ + uint64_t processID = 0; + if (XRE_GetProcessType() == GeckoProcessType_Content) { + ContentChild* cc = ContentChild::GetSingleton(); + processID = cc->GetID(); + } + + MOZ_RELEASE_ASSERT(processID < (uint64_t(1) << kWindowIDProcessBits)); + uint64_t processBits = processID & ((uint64_t(1) << kWindowIDProcessBits) - 1); + + // Make sure no actual window ends up with mWindowID == 0. + uint64_t windowID = ++gNextWindowID; + + MOZ_RELEASE_ASSERT(windowID < (uint64_t(1) << kWindowIDWindowBits)); + uint64_t windowBits = windowID & ((uint64_t(1) << kWindowIDWindowBits) - 1); + + return (processBits << kWindowIDWindowBits) | windowBits; +} + } // namespace dom } // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/ContentChild.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/ContentChild.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/ContentChild.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/ContentChild.h 2015-02-03 14:33:40.000000000 +0000 @@ -209,7 +209,7 @@ virtual PTestShellChild* AllocPTestShellChild() MOZ_OVERRIDE; virtual bool DeallocPTestShellChild(PTestShellChild*) MOZ_OVERRIDE; virtual bool RecvPTestShellConstructor(PTestShellChild*) MOZ_OVERRIDE; - jsipc::JavaScriptShared* GetCPOWManager() MOZ_OVERRIDE; + jsipc::CPOWManager* GetCPOWManager() MOZ_OVERRIDE; PMobileConnectionChild* SendPMobileConnectionConstructor(PMobileConnectionChild* aActor, @@ -332,6 +332,7 @@ virtual bool RecvLastPrivateDocShellDestroyed() MOZ_OVERRIDE; + virtual bool RecvVolumes(InfallibleTArray&& aVolumes) MOZ_OVERRIDE; virtual bool RecvFilePathUpdate(const nsString& aStorageType, const nsString& aStorageName, const nsString& aPath, @@ -481,6 +482,9 @@ DISALLOW_EVIL_CONSTRUCTORS(ContentChild); }; +uint64_t +NextWindowID(); + } // namespace dom } // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/ContentParent.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/ContentParent.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/ContentParent.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/ContentParent.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -67,6 +67,7 @@ #include "mozilla/ipc/PFileDescriptorSetParent.h" #include "mozilla/ipc/TestShellParent.h" #include "mozilla/ipc/InputStreamUtils.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "mozilla/layers/CompositorParent.h" #include "mozilla/layers/ImageBridgeParent.h" #include "mozilla/layers/SharedBufferManagerParent.h" @@ -178,8 +179,6 @@ #include "BluetoothService.h" #endif -#include "JavaScriptParent.h" - #include "mozilla/RemoteSpellCheckEngineParent.h" #ifdef MOZ_B2G_FM @@ -673,6 +672,8 @@ } process->TransformPreallocatedIntoApp(aOpener, manifestURL); + process->ForwardKnownInfo(); + if (aTookPreAllocated) { *aTookPreAllocated = true; } @@ -689,6 +690,7 @@ /* isForPreallocated = */ false, aInitialPriority); process->Init(); + process->ForwardKnownInfo(); if (aTookPreAllocated) { *aTookPreAllocated = false; @@ -832,6 +834,7 @@ aPriority); p->Init(); } + p->ForwardKnownInfo(); sNonAppContentParents->AppendElement(p); return p.forget(); @@ -976,15 +979,17 @@ } bool -ContentParent::RecvLoadPlugin(const uint32_t& aPluginId) +ContentParent::RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv) { - return mozilla::plugins::SetupBridge(aPluginId, this); + *aRv = NS_OK; + return mozilla::plugins::SetupBridge(aPluginId, this, false, aRv); } bool -ContentParent::RecvConnectPluginBridge(const uint32_t& aPluginId) +ContentParent::RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv) { - return mozilla::plugins::SetupBridge(aPluginId, this, true); + *aRv = NS_OK; + return mozilla::plugins::SetupBridge(aPluginId, this, true, aRv); } bool @@ -1309,6 +1314,23 @@ NS_ASSERTION(observer, "FileUpdateDispatcher is null"); } +void +ContentParent::ForwardKnownInfo() +{ + MOZ_ASSERT(mMetamorphosed); + if (!mMetamorphosed) { + return; + } +#ifdef MOZ_WIDGET_GONK + InfallibleTArray volumeInfo; + nsRefPtr vs = nsVolumeService::GetSingleton(); + if (vs) { + vs->GetVolumesForIPC(&volumeInfo); + unused << SendVolumes(volumeInfo); + } +#endif /* MOZ_WIDGET_GONK */ +} + namespace { class SystemMessageHandledListener MOZ_FINAL @@ -1498,6 +1520,7 @@ const nsAString& aAppManifestURL) { MOZ_ASSERT(IsPreallocated()); + mMetamorphosed = true; mOpener = aOpener; mAppManifestURL = aAppManifestURL; TryGetNameFromManifestURL(aAppManifestURL, mAppName); @@ -1507,6 +1530,7 @@ ContentParent::TransformPreallocatedIntoBrowser(ContentParent* aOpener) { // Reset mAppManifestURL, mIsForBrowser and mOSPrivileges for browser. + mMetamorphosed = true; mOpener = aOpener; mAppManifestURL.Truncate(); mIsForBrowser = true; @@ -1996,11 +2020,11 @@ } } -jsipc::JavaScriptShared* +jsipc::CPOWManager* ContentParent::GetCPOWManager() { if (ManagedPJavaScriptParent().Length()) { - return static_cast(ManagedPJavaScriptParent()[0]); + return CPOWManagerFor(ManagedPJavaScriptParent()[0]); } return nullptr; } @@ -2033,6 +2057,7 @@ mGeolocationWatchID = -1; mNumDestroyingTabs = 0; mIsAlive = true; + mMetamorphosed = false; mSendPermissionUpdates = false; mSendDataStoreInfos = false; mCalledClose = false; @@ -2064,6 +2089,10 @@ // Only the preallocated process uses Nuwa. MOZ_ASSERT_IF(aIsNuwaProcess, aIsForPreallocated); + if (!aIsNuwaProcess && !aIsForPreallocated) { + mMetamorphosed = true; + } + // Insert ourselves into the global linked list of ContentParent objects. if (!sContentParents) { sContentParents = new LinkedList(); @@ -2673,19 +2702,6 @@ } bool -ContentParent::RecvGetVolumes(InfallibleTArray* aResult) -{ -#ifdef MOZ_WIDGET_GONK - nsRefPtr vs = nsVolumeService::GetSingleton(); - vs->GetVolumesForIPC(aResult); - return true; -#else - NS_WARNING("ContentParent::RecvGetVolumes shouldn't be called when MOZ_WIDGET_GONK is not defined"); - return false; -#endif -} - -bool ContentParent::RecvNuwaReady() { #ifdef MOZ_NUWA_PROCESS diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/ContentParent.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/ContentParent.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/ContentParent.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/ContentParent.h 2015-02-03 14:33:40.000000000 +0000 @@ -153,8 +153,8 @@ TabId* aTabId) MOZ_OVERRIDE; virtual bool RecvBridgeToChildProcess(const ContentParentId& aCpId) MOZ_OVERRIDE; - virtual bool RecvLoadPlugin(const uint32_t& aPluginId) MOZ_OVERRIDE; - virtual bool RecvConnectPluginBridge(const uint32_t& aPluginId) MOZ_OVERRIDE; + virtual bool RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv) MOZ_OVERRIDE; + virtual bool RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv) MOZ_OVERRIDE; virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch, nsTArray* aPlugins, uint32_t* aNewPluginEpoch) MOZ_OVERRIDE; @@ -189,7 +189,7 @@ TestShellParent* CreateTestShell(); bool DestroyTestShell(TestShellParent* aTestShell); TestShellParent* GetTestShellSingleton(); - jsipc::JavaScriptShared* GetCPOWManager() MOZ_OVERRIDE; + jsipc::CPOWManager* GetCPOWManager() MOZ_OVERRIDE; static TabId AllocateTabId(const TabId& aOpenerTabId, @@ -410,6 +410,11 @@ void Init(); + // Some information could be sent to content very early, it + // should be send from this function. This function should only be + // called after the process has been transformed to app or browser. + void ForwardKnownInfo(); + // If the frame element indicates that the child process is "critical" and // has a pending system message, this function acquires the CPU wake lock on // behalf of the child. We'll release the lock when the system message is @@ -695,7 +700,6 @@ virtual bool RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel, const bool& aHidden) MOZ_OVERRIDE; virtual bool RecvGetSystemMemory(const uint64_t& getterId) MOZ_OVERRIDE; - virtual bool RecvGetVolumes(InfallibleTArray* aResult) MOZ_OVERRIDE; virtual bool RecvDataStoreGetStores( const nsString& aName, @@ -812,6 +816,10 @@ // through. bool mIsAlive; + // True only the if process is already a browser or app or has + // been transformed into one. + bool mMetamorphosed; + bool mSendPermissionUpdates; bool mSendDataStoreInfos; bool mIsForBrowser; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/CPOWManagerGetter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/CPOWManagerGetter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/CPOWManagerGetter.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/CPOWManagerGetter.h 2015-02-03 14:33:40.000000000 +0000 @@ -10,14 +10,14 @@ namespace mozilla { namespace jsipc { -class JavaScriptShared; +class CPOWManager; } /* namespace jsipc */ namespace dom { class CPOWManagerGetter { public: - virtual mozilla::jsipc::JavaScriptShared* GetCPOWManager() = 0; + virtual mozilla::jsipc::CPOWManager* GetCPOWManager() = 0; }; } /* namespace dom */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/moz.build 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/moz.build 2015-02-03 14:33:40.000000000 +0000 @@ -130,7 +130,6 @@ '/extensions/cookie', '/extensions/spellcheck/src', '/hal/sandbox', - '/js/ipc', '/layout/base', '/netwerk/base', '/toolkit/xre', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/nsIContentChild.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/nsIContentChild.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/nsIContentChild.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/nsIContentChild.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -15,7 +15,6 @@ #include "mozilla/dom/ipc/BlobChild.h" #include "mozilla/ipc/InputStreamUtils.h" -#include "JavaScriptChild.h" #include "nsIJSRuntimeService.h" #include "nsPrintfCString.h" @@ -35,17 +34,13 @@ svc->GetRuntime(&rt); NS_ENSURE_TRUE(svc, nullptr); - nsAutoPtr child(new JavaScriptChild(rt)); - if (!child->init()) { - return nullptr; - } - return child.forget(); + return NewJavaScriptChild(rt); } bool nsIContentChild::DeallocPJavaScriptChild(PJavaScriptChild* aChild) { - static_cast(aChild)->decref(); + ReleaseJavaScriptChild(aChild); return true; } @@ -121,7 +116,7 @@ nsRefPtr cpm = nsFrameMessageManager::sChildProcessManager; if (cpm) { StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForChild(aData); - CpowIdHolder cpows(this, aCpows); + CrossProcessCpowHolder cpows(this, aCpows); cpm->ReceiveMessage(static_cast(cpm.get()), aMsg, false, &cloneData, &cpows, aPrincipal, nullptr); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/nsIContentParent.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/nsIContentParent.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/nsIContentParent.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/nsIContentParent.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -15,9 +15,9 @@ #include "mozilla/dom/StructuredCloneUtils.h" #include "mozilla/dom/TabParent.h" #include "mozilla/dom/ipc/BlobParent.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "mozilla/unused.h" -#include "JavaScriptParent.h" #include "nsFrameMessageManager.h" #include "nsIJSRuntimeService.h" #include "nsPrintfCString.h" @@ -57,17 +57,13 @@ svc->GetRuntime(&rt); NS_ENSURE_TRUE(svc, nullptr); - nsAutoPtr parent(new JavaScriptParent(rt)); - if (!parent->init()) { - return nullptr; - } - return parent.forget(); + return NewJavaScriptParent(rt); } bool nsIContentParent::DeallocPJavaScriptParent(PJavaScriptParent* aParent) { - static_cast(aParent)->decref(); + ReleaseJavaScriptParent(aParent); return true; } @@ -197,7 +193,7 @@ nsRefPtr ppm = mMessageManager; if (ppm) { StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData); - CpowIdHolder cpows(this, aCpows); + CrossProcessCpowHolder cpows(this, aCpows); ppm->ReceiveMessage(static_cast(ppm.get()), aMsg, true, &cloneData, &cpows, aPrincipal, aRetvals); } @@ -224,7 +220,7 @@ nsRefPtr ppm = mMessageManager; if (ppm) { StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData); - CpowIdHolder cpows(this, aCpows); + CrossProcessCpowHolder cpows(this, aCpows); ppm->ReceiveMessage(static_cast(ppm.get()), aMsg, true, &cloneData, &cpows, aPrincipal, aRetvals); } @@ -250,7 +246,7 @@ nsRefPtr ppm = mMessageManager; if (ppm) { StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData); - CpowIdHolder cpows(this, aCpows); + CrossProcessCpowHolder cpows(this, aCpows); ppm->ReceiveMessage(static_cast(ppm.get()), aMsg, false, &cloneData, &cpows, aPrincipal, nullptr); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/PBrowser.ipdl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/PBrowser.ipdl --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/PBrowser.ipdl 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/PBrowser.ipdl 2015-02-03 14:33:40.000000000 +0000 @@ -85,7 +85,7 @@ bool runInGlobalScope; }; -prio(normal upto urgent) intr protocol PBrowser +prio(normal upto urgent) sync protocol PBrowser { manager PContent or PContentBridge; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/PContentBridge.ipdl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/PContentBridge.ipdl --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/PContentBridge.ipdl 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/PContentBridge.ipdl 2015-02-03 14:33:40.000000000 +0000 @@ -28,7 +28,7 @@ * allocate the PContentBridgeChild. This protocol allows these processes to * share PBrowsers and send messages to each other. */ -prio(normal upto urgent) intr protocol PContentBridge +prio(normal upto urgent) sync protocol PContentBridge { bridges PContent, PContent; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/PContent.ipdl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/PContent.ipdl --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/PContent.ipdl 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/PContent.ipdl 2015-02-03 14:33:40.000000000 +0000 @@ -341,7 +341,7 @@ void_t; }; -prio(normal upto urgent) intr protocol PContent +prio(normal upto urgent) sync protocol PContent { parent spawns PPluginModule; @@ -471,6 +471,8 @@ ScreenSizeChanged(gfxIntSize size); + Volumes(VolumeInfo[] volumes); + FlushMemory(nsString reason); GarbageCollect(); @@ -592,7 +594,7 @@ * process. We use intr semantics here to ensure that the PluginModuleParent * allocation message is dispatched before LoadPlugin returns. */ - sync LoadPlugin(uint32_t pluginId); + sync LoadPlugin(uint32_t pluginId) returns (nsresult rv); /** * This call is used by asynchronous plugin instantiation to notify the @@ -600,7 +602,7 @@ * the specified plugin id. When this call returns, the requested bridge * connection has been made. */ - sync ConnectPluginBridge(uint32_t aPluginId); + sync ConnectPluginBridge(uint32_t aPluginId) returns (nsresult rv); /** * This call returns the set of plugins loaded in the chrome @@ -764,8 +766,6 @@ nsString aFilepath, nsCString aReason); - sync GetVolumes() returns (VolumeInfo[] volumes); - // Notify the parent that the child has finished handling a system message. async SystemMessageHandled(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/PPluginWidget.ipdl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/PPluginWidget.ipdl --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/PPluginWidget.ipdl 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/PPluginWidget.ipdl 2015-02-03 14:33:40.000000000 +0000 @@ -36,7 +36,6 @@ async Create(); async Destroy(); async SetFocus(bool aRaise); - async Invalidate(nsIntRect aRect); /** * Returns NS_NATIVE_PLUGIN_PORT and its variants: a sharable native @@ -46,14 +45,19 @@ */ sync GetNativePluginPort() returns (uintptr_t value); +child: /** - * nsIWidget interfaces we'll need until this information flows - * over the compositor connection. + * Sent from content when a plugin is unloaded via layout. We shut down + * some things in response to this so that we don't end up firing async + * msgs after the child is destroyed. We know that after this call + * the child actor may not be on the other side. */ - async Show(bool aState); - async Resize(nsIntRect aRect); - async Move(double aX, double aY); - async SetWindowClipRegion(nsIntRect[] Regions, bool aIntersectWithExisting); + async ParentShutdown(); + + /** + * Requests a full update of the plugin window. + */ + async UpdateWindow(uintptr_t aChildId); }; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/PScreenManager.ipdl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/PScreenManager.ipdl --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/PScreenManager.ipdl 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/PScreenManager.ipdl 2015-02-03 14:33:40.000000000 +0000 @@ -6,6 +6,8 @@ include protocol PBrowser; include protocol PContent; +include "mozilla/GfxMessageUtils.h"; + using struct nsIntRect from "nsRect.h"; namespace mozilla { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/TabChild.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/TabChild.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/TabChild.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/TabChild.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -75,7 +75,6 @@ #include "PuppetWidget.h" #include "StructuredCloneUtils.h" #include "nsViewportInfo.h" -#include "JavaScriptChild.h" #include "nsILoadContext.h" #include "ipc/nsGUIEventIPC.h" #include "mozilla/gfx/Matrix.h" @@ -125,10 +124,10 @@ public: NS_DECL_ISUPPORTS - DelayedFireSingleTapEvent(TabChild* aTabChild, + DelayedFireSingleTapEvent(nsIWidget* aWidget, LayoutDevicePoint& aPoint, nsITimer* aTimer) - : mTabChild(do_GetWeakReference(static_cast(aTabChild))) + : mWidget(do_GetWeakReference(aWidget)) , mPoint(aPoint) // Hold the reference count until we are called back. , mTimer(aTimer) @@ -137,9 +136,9 @@ NS_IMETHODIMP Notify(nsITimer*) MOZ_OVERRIDE { - nsCOMPtr tabChild = do_QueryReferent(mTabChild); - if (tabChild) { - static_cast(tabChild.get())->FireSingleTapEvent(mPoint); + nsCOMPtr widget = do_QueryReferent(mWidget); + if (widget) { + APZCCallbackHelper::FireSingleTapEvent(mPoint, widget); } mTimer = nullptr; return NS_OK; @@ -154,7 +153,7 @@ { } - nsWeakPtr mTabChild; + nsWeakPtr mWidget; LayoutDevicePoint mPoint; nsCOMPtr mTimer; }; @@ -603,46 +602,6 @@ return newMetrics; } -nsEventStatus -TabChildBase::DispatchSynthesizedMouseEvent(uint32_t aMsg, uint64_t aTime, - const LayoutDevicePoint& aRefPoint, - nsIWidget* aWidget) -{ - MOZ_ASSERT(aMsg == NS_MOUSE_MOVE || aMsg == NS_MOUSE_BUTTON_DOWN || - aMsg == NS_MOUSE_BUTTON_UP || aMsg == NS_MOUSE_MOZLONGTAP); - - WidgetMouseEvent event(true, aMsg, nullptr, - WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); - event.refPoint = LayoutDeviceIntPoint(aRefPoint.x, aRefPoint.y); - event.time = aTime; - event.button = WidgetMouseEvent::eLeftButton; - event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; - event.ignoreRootScrollFrame = true; - if (aMsg != NS_MOUSE_MOVE) { - event.clickCount = 1; - } - event.widget = aWidget; - - return DispatchWidgetEvent(event); -} - -nsEventStatus -TabChildBase::DispatchWidgetEvent(WidgetGUIEvent& event) -{ - if (!event.widget) - return nsEventStatus_eConsumeNoDefault; - - if (TabParent* capturer = TabParent::GetEventCapturer()) { - if (capturer->TryCapture(event)) { - return nsEventStatus_eConsumeNoDefault; - } - } - nsEventStatus status; - NS_ENSURE_SUCCESS(event.widget->DispatchEvent(&event, status), - nsEventStatus_eConsumeNoDefault); - return status; -} - bool TabChildBase::IsAsyncPanZoomEnabled() { @@ -2156,14 +2115,14 @@ // If the active element isn't visually affected by the :active style, we // have no need to wait the extra sActiveDurationMs to make the activation // visually obvious to the user. - FireSingleTapEvent(currentPoint); + APZCCallbackHelper::FireSingleTapEvent(currentPoint, mWidget); return true; } TABC_LOG("Active element uses style, scheduling timer for click event\n"); nsCOMPtr timer = do_CreateInstance(NS_TIMER_CONTRACTID); nsRefPtr callback = - new DelayedFireSingleTapEvent(this, currentPoint, timer); + new DelayedFireSingleTapEvent(mWidget, currentPoint, timer); nsresult rv = timer->InitWithCallback(callback, sActiveDurationMs, nsITimer::TYPE_ONE_SHOT); @@ -2175,20 +2134,6 @@ return true; } -void -TabChild::FireSingleTapEvent(LayoutDevicePoint aPoint) -{ - if (mDestroyed) { - return; - } - TABC_LOG("Dispatching single-tap component events to %s\n", - Stringify(aPoint).c_str()); - int time = 0; - DispatchSynthesizedMouseEvent(NS_MOUSE_MOVE, time, aPoint, mWidget); - DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_DOWN, time, aPoint, mWidget); - DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_UP, time, aPoint, mWidget); -} - bool TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId) { @@ -2216,7 +2161,7 @@ * mWidget->GetDefaultScale(); int time = 0; nsEventStatus status = - DispatchSynthesizedMouseEvent(NS_MOUSE_MOZLONGTAP, time, currentPoint, mWidget); + APZCCallbackHelper::DispatchSynthesizedMouseEvent(NS_MOUSE_MOZLONGTAP, time, currentPoint, mWidget); eventHandled = (status == nsEventStatus_eConsumeNoDefault); TABC_LOG("MOZLONGTAP event handled: %d\n", eventHandled); } @@ -2343,7 +2288,7 @@ { WidgetMouseEvent localEvent(event); localEvent.widget = mWidget; - DispatchWidgetEvent(localEvent); + APZCCallbackHelper::DispatchWidgetEvent(localEvent); return true; } @@ -2357,9 +2302,8 @@ if (nsIPresShell* shell = document->GetShell()) { if (nsIFrame* rootFrame = shell->GetRootFrame()) { nsTArray targets; - nsIntPoint refPoint(aEvent.refPoint.x, aEvent.refPoint.y); bool waitForRefresh = - PrepareForSetTargetAPZCNotification(aGuid, aInputBlockId, rootFrame, refPoint, &targets); + PrepareForSetTargetAPZCNotification(aGuid, aInputBlockId, rootFrame, aEvent.refPoint, &targets); SendSetTargetAPZCNotification(shell, aInputBlockId, targets, waitForRefresh); } @@ -2368,7 +2312,7 @@ WidgetWheelEvent event(aEvent); event.widget = mWidget; - DispatchWidgetEvent(event); + APZCCallbackHelper::DispatchWidgetEvent(event); if (IsAsyncPanZoomEnabled()) { SendContentReceivedInputBlock(aGuid, aInputBlockId, event.mFlags.mDefaultPrevented); @@ -2462,9 +2406,9 @@ case NS_TOUCH_END: if (!nsIPresShell::gPreventMouseEvents) { - DispatchSynthesizedMouseEvent(NS_MOUSE_MOVE, time, currentPoint, mWidget); - DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_DOWN, time, currentPoint, mWidget); - DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_UP, time, currentPoint, mWidget); + APZCCallbackHelper::DispatchSynthesizedMouseEvent(NS_MOUSE_MOVE, time, currentPoint, mWidget); + APZCCallbackHelper::DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_DOWN, time, currentPoint, mWidget); + APZCCallbackHelper::DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_UP, time, currentPoint, mWidget); } // fall through case NS_TOUCH_CANCEL: @@ -2596,7 +2540,7 @@ TabChild::PrepareForSetTargetAPZCNotification(const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId, nsIFrame* aRootFrame, - const nsIntPoint& aRefPoint, + const LayoutDeviceIntPoint& aRefPoint, nsTArray* aTargets) { ScrollableLayerGuid guid(aGuid.mLayersId, 0, FrameMetrics::NULL_SCROLL_ID); @@ -2703,7 +2647,7 @@ WidgetTouchEvent localEvent(aEvent); localEvent.widget = mWidget; // Dispatch event to content (potentially a long-running operation) - nsEventStatus status = DispatchWidgetEvent(localEvent); + nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent); if (!IsAsyncPanZoomEnabled()) { UpdateTapState(localEvent, status); @@ -2808,7 +2752,7 @@ WidgetKeyboardEvent localEvent(event); localEvent.widget = mWidget; - nsEventStatus status = DispatchWidgetEvent(localEvent); + nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent); if (event.message == NS_KEY_DOWN) { mIgnoreKeyPressEvent = status == nsEventStatus_eConsumeNoDefault; @@ -2845,7 +2789,7 @@ { WidgetCompositionEvent localEvent(event); localEvent.widget = mWidget; - DispatchWidgetEvent(localEvent); + APZCCallbackHelper::DispatchWidgetEvent(localEvent); return true; } @@ -2854,7 +2798,7 @@ { WidgetSelectionEvent localEvent(event); localEvent.widget = mWidget; - DispatchWidgetEvent(localEvent); + APZCCallbackHelper::DispatchWidgetEvent(localEvent); return true; } @@ -3009,7 +2953,7 @@ StructuredCloneData cloneData = UnpackClonedMessageDataForChild(aData); nsRefPtr mm = static_cast(mTabChildGlobal->mMessageManager.get()); - CpowIdHolder cpows(Manager(), aCpows); + CrossProcessCpowHolder cpows(Manager(), aCpows); mm->ReceiveMessage(static_cast(mTabChildGlobal), aMessage, false, &cloneData, &cpows, aPrincipal, nullptr); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/TabChild.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/TabChild.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/TabChild.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/TabChild.h 2015-02-03 14:33:40.000000000 +0000 @@ -196,10 +196,6 @@ const bool& aIsRoot, const mozilla::layers::ZoomConstraints& aConstraints) = 0; - nsEventStatus DispatchSynthesizedMouseEvent(uint32_t aMsg, uint64_t aTime, - const LayoutDevicePoint& aRefPoint, - nsIWidget* aWidget); - protected: virtual ~TabChildBase(); CSSSize GetPageSize(nsCOMPtr aDocument, const CSSSize& aViewport); @@ -221,8 +217,6 @@ void DispatchMessageManagerMessage(const nsAString& aMessageName, const nsAString& aJSONData); - nsEventStatus DispatchWidgetEvent(WidgetGUIEvent& event); - void InitializeRootMetrics(); mozilla::layers::FrameMetrics ProcessUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics); @@ -598,7 +592,7 @@ bool PrepareForSetTargetAPZCNotification(const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId, nsIFrame* aRootFrame, - const nsIntPoint& aRefPoint, + const LayoutDeviceIntPoint& aRefPoint, nsTArray* aTargets); // Sends a SetTarget notification for APZC, given one or more previous @@ -659,7 +653,6 @@ bool mPendingTouchPreventedResponse; ScrollableLayerGuid mPendingTouchPreventedGuid; uint64_t mPendingTouchPreventedBlockId; - void FireSingleTapEvent(LayoutDevicePoint aPoint); bool mTouchEndCancelled; bool mEndTouchIsClick; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/TabParent.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/TabParent.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/TabParent.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/TabParent.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -18,6 +18,7 @@ #include "mozilla/EventStateManager.h" #include "mozilla/Hal.h" #include "mozilla/ipc/DocumentRendererParent.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "mozilla/layers/CompositorParent.h" #include "mozilla/layers/InputAPZContext.h" #include "mozilla/layout/RenderFrameParent.h" @@ -65,7 +66,6 @@ #include "PermissionMessageUtils.h" #include "StructuredCloneUtils.h" #include "ColorPickerParent.h" -#include "JavaScriptParent.h" #include "FilePickerParent.h" #include "TabChild.h" #include "LoadContext.h" @@ -366,6 +366,15 @@ if (XRE_GetProcessType() == GeckoProcessType_Default) { Manager()->AsContentParent()->NotifyTabDestroying(this); } + + // Let all PluginWidgets know we are tearing down. Prevents + // these objects from sending async events after the child side + // is shut down. + const nsTArray& kids = ManagedPPluginWidgetParent(); + for (uint32_t idx = 0; idx < kids.Length(); ++idx) { + static_cast(kids[idx])->ParentDestroy(); + } + mMarkedDestroying = true; } @@ -1076,7 +1085,7 @@ for (uint32_t i = 0; i < touches.Length(); ++i) { Touch* touch = touches[i]; if (touch) { - touch->mRefPoint += LayoutDeviceIntPoint::ToUntyped(aOffset); + touch->mRefPoint += aOffset; } } } @@ -1348,7 +1357,7 @@ } StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData); - CpowIdHolder cpows(Manager(), aCpows); + CrossProcessCpowHolder cpows(Manager(), aCpows); return ReceiveMessage(aMessage, true, &cloneData, &cpows, aPrincipal, aJSONRetVal); } @@ -1370,7 +1379,7 @@ } StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData); - CpowIdHolder cpows(Manager(), aCpows); + CrossProcessCpowHolder cpows(Manager(), aCpows); return ReceiveMessage(aMessage, true, &cloneData, &cpows, aPrincipal, aJSONRetVal); } @@ -1391,7 +1400,7 @@ } StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData); - CpowIdHolder cpows(Manager(), aCpows); + CrossProcessCpowHolder cpows(Manager(), aCpows); return ReceiveMessage(aMessage, false, &cloneData, &cpows, aPrincipal, nullptr); } @@ -1722,7 +1731,7 @@ return offset; } nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(widget, - nsIntPoint(0, 0), + LayoutDeviceIntPoint(0, 0), targetFrame); return LayoutDeviceIntPoint::ToUntyped(LayoutDeviceIntPoint::FromAppUnitsToNearest( @@ -2117,6 +2126,7 @@ bool TabParent::RecvGetWidgetNativeData(WindowsHandle* aValue) { + *aValue = 0; nsCOMPtr content = do_QueryInterface(mFrameElement); if (content) { nsIPresShell* shell = content->OwnerDoc()->GetShell(); @@ -2127,11 +2137,10 @@ if (widget) { *aValue = reinterpret_cast( widget->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW)); - return true; } } } - return false; + return true; } bool @@ -2476,7 +2485,7 @@ presContext->AppUnitsPerDevPixel()); nsRefPtr t = new Touch(aIdentifiers[i], - LayoutDeviceIntPoint::ToUntyped(pt), + pt, nsIntPoint(aRxs[i], aRys[i]), aRotationAngles[i], aForces[i]); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/TabParent.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/TabParent.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/TabParent.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/TabParent.h 2015-02-03 14:33:40.000000000 +0000 @@ -29,11 +29,14 @@ class nsIURI; class nsIWidget; class nsILoadContext; -class CpowHolder; class nsIDocShell; namespace mozilla { +namespace jsipc { +class CpowHolder; +} + namespace layers { struct FrameMetrics; struct TextureFactoryIdentifier; @@ -363,7 +366,7 @@ bool ReceiveMessage(const nsString& aMessage, bool aSync, const StructuredCloneData* aCloneData, - CpowHolder* aCpows, + mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal, InfallibleTArray* aJSONRetVal = nullptr); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/tests/mochitest.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/tests/mochitest.ini --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/ipc/tests/mochitest.ini 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/ipc/tests/mochitest.ini 2015-02-03 14:33:40.000000000 +0000 @@ -5,7 +5,7 @@ # This test is only supposed to run in the main process. skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s [test_NuwaProcessCreation.html] -run-if = toolkit == 'gonk' +skip-if = toolkit != 'gonk' [test_NuwaProcessDeadlock.html] run-if = toolkit == 'gonk' [test_child_docshell.html] diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/locales/en-US/chrome/security/security.properties thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/locales/en-US/chrome/security/security.properties --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/locales/en-US/chrome/security/security.properties 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/locales/en-US/chrome/security/security.properties 2015-02-03 14:33:40.000000000 +0000 @@ -4,7 +4,18 @@ BlockMixedActiveContent = Blocked loading mixed active content "%1$S" # CORS -CrossSiteRequestBlocked=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. This can be fixed by moving the resource to the same domain or enabling CORS. +# LOCALIZATION NOTE: Do not translate "Access-Control-Allow-Origin", Access-Control-Allow-Credentials, Access-Control-Allow-Methods, Access-Control-Allow-Headers +CORSDisabled=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS disabled). +CORSRequestFailed=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS request failed). +CORSRequestNotHttp=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS request not http). +CORSMissingAllowOrigin=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS header 'Access-Control-Allow-Origin' missing). +CORSAllowOriginNotMatchingOrigin=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS header 'Access-Control-Allow-Origin' does not match '%2$S'). +CORSMethodNotFound=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: Did not find method in CORS header 'Access-Control-Allow-Methods'). +CORSMissingAllowCredentials=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: expected 'true' in CORS header 'Access-Control-Allow-Credentials'). +CORSPreflightDidNotSucceed=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS preflight channel did not succeed). +CORSInvalidAllowMethod=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: invalid token '%2$S' in CORS header 'Access-Control-Allow-Methods'). +CORSInvalidAllowHeader=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: invalid token '%2$S' in CORS header 'Access-Control-Allow-Headers'). +CORSMissingAllowHeaderFromPreflight=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: missing token '%2$S' in CORS header 'Access-Control-Allow-Headers' from CORS preflight channel). # LOCALIZATION NOTE: Do not translate "Strict-Transport-Security" or "HSTS" InvalidSTSHeaders=The site specified an invalid Strict-Transport-Security header. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/CDMCallbackProxy.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/CDMCallbackProxy.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/CDMCallbackProxy.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/CDMCallbackProxy.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -267,35 +267,18 @@ } void -CDMCallbackProxy::KeyIdUsable(const nsCString& aSessionId, - const nsTArray& aKeyId) +CDMCallbackProxy::KeyStatusChanged(const nsCString& aSessionId, + const nsTArray& aKeyId, + GMPMediaKeyStatus aStatus) { MOZ_ASSERT(mProxy->IsOnGMPThread()); bool keysChange = false; { CDMCaps::AutoLock caps(mProxy->Capabilites()); - keysChange = caps.SetKeyUsable(aKeyId, NS_ConvertUTF8toUTF16(aSessionId)); - } - if (keysChange) { - nsRefPtr task; - task = NS_NewRunnableMethodWithArg(mProxy, - &CDMProxy::OnKeysChange, - NS_ConvertUTF8toUTF16(aSessionId)); - NS_DispatchToMainThread(task); - } -} - -void -CDMCallbackProxy::KeyIdNotUsable(const nsCString& aSessionId, - const nsTArray& aKeyId) -{ - MOZ_ASSERT(mProxy->IsOnGMPThread()); - - bool keysChange = false; - { - CDMCaps::AutoLock caps(mProxy->Capabilites()); - keysChange = caps.SetKeyUnusable(aKeyId, NS_ConvertUTF8toUTF16(aSessionId)); + keysChange = caps.SetKeyStatus(aKeyId, + NS_ConvertUTF8toUTF16(aSessionId), + aStatus); } if (keysChange) { nsRefPtr task; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/CDMCallbackProxy.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/CDMCallbackProxy.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/CDMCallbackProxy.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/CDMCallbackProxy.h 2015-02-03 14:33:40.000000000 +0000 @@ -43,11 +43,9 @@ uint32_t aSystemCode, const nsCString& aMessage) MOZ_OVERRIDE; - virtual void KeyIdUsable(const nsCString& aSessionId, - const nsTArray& aKeyId) MOZ_OVERRIDE; - - virtual void KeyIdNotUsable(const nsCString& aSessionId, - const nsTArray& aKeyId) MOZ_OVERRIDE; + virtual void KeyStatusChanged(const nsCString& aSessionId, + const nsTArray& aKeyId, + GMPMediaKeyStatus aStatus) MOZ_OVERRIDE; virtual void SetCaps(uint64_t aCaps) MOZ_OVERRIDE; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/CDMCaps.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/CDMCaps.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/CDMCaps.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/CDMCaps.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -5,7 +5,6 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "CDMCaps.h" -#include "gmp-decryption.h" #include "EMELog.h" #include "nsThreadUtils.h" #include "SamplesWaitingForKey.h" @@ -80,9 +79,9 @@ CDMCaps::AutoLock::IsKeyUsable(const CencKeyId& aKeyId) { mData.mMonitor.AssertCurrentThreadOwns(); - const auto& keys = mData.mUsableKeyIds; + const auto& keys = mData.mKeyStatuses; for (size_t i = 0; i < keys.Length(); i++) { - if (keys[i].mId == aKeyId) { + if (keys[i].mId == aKeyId && keys[i].mStatus == kGMPUsable) { return true; } } @@ -90,15 +89,32 @@ } bool -CDMCaps::AutoLock::SetKeyUsable(const CencKeyId& aKeyId, - const nsString& aSessionId) +CDMCaps::AutoLock::SetKeyStatus(const CencKeyId& aKeyId, + const nsString& aSessionId, + GMPMediaKeyStatus aStatus) { mData.mMonitor.AssertCurrentThreadOwns(); - UsableKey key(aKeyId, aSessionId); - if (mData.mUsableKeyIds.Contains(key)) { - return false; + KeyStatus key(aKeyId, aSessionId, aStatus); + auto index = mData.mKeyStatuses.IndexOf(key); + + if (aStatus == kGMPUnknown) { + // Return true if the element is found to notify key changes. + return mData.mKeyStatuses.RemoveElement(key); + } + + if (index != mData.mKeyStatuses.NoIndex) { + if (mData.mKeyStatuses[index].mStatus == aStatus) { + return false; + } + mData.mKeyStatuses[index].mStatus = aStatus; + } else { + mData.mKeyStatuses.AppendElement(key); } - mData.mUsableKeyIds.AppendElement(key); + + if (aStatus != kGMPUsable) { + return true; + } + auto& waiters = mData.mWaitForKeys; size_t i = 0; while (i < waiters.Length()) { @@ -113,26 +129,6 @@ return true; } -bool -CDMCaps::AutoLock::SetKeyUnusable(const CencKeyId& aKeyId, - const nsString& aSessionId) -{ - mData.mMonitor.AssertCurrentThreadOwns(); - UsableKey key(aKeyId, aSessionId); - if (!mData.mUsableKeyIds.Contains(key)) { - return false; - } - auto& keys = mData.mUsableKeyIds; - for (size_t i = 0; i < keys.Length(); i++) { - if (keys[i].mId == aKeyId && - keys[i].mSessionId == aSessionId) { - keys.RemoveElementAt(i); - break; - } - } - return true; -} - void CDMCaps::AutoLock::NotifyWhenKeyIdUsable(const CencKeyId& aKey, SamplesWaitingForKey* aListener) @@ -175,15 +171,15 @@ } void -CDMCaps::AutoLock::GetUsableKeysForSession(const nsAString& aSessionId, - nsTArray& aOutKeyIds) +CDMCaps::AutoLock::GetKeyStatusesForSession(const nsAString& aSessionId, + nsTArray& aOutKeyStatuses) { - for (size_t i = 0; i < mData.mUsableKeyIds.Length(); i++) { - const auto& key = mData.mUsableKeyIds[i]; + for (size_t i = 0; i < mData.mKeyStatuses.Length(); i++) { + const auto& key = mData.mKeyStatuses[i]; if (key.mSessionId.Equals(aSessionId)) { - aOutKeyIds.AppendElement(key.mId); + aOutKeyStatuses.AppendElement(key); } } } -} // namespace mozilla \ No newline at end of file +} // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/CDMCaps.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/CDMCaps.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/CDMCaps.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/CDMCaps.h 2015-02-03 14:33:40.000000000 +0000 @@ -14,6 +14,7 @@ #include "nsTArray.h" #include "mozilla/Attributes.h" #include "SamplesWaitingForKey.h" +#include "gmp-decryption.h" namespace mozilla { @@ -24,6 +25,29 @@ CDMCaps(); ~CDMCaps(); + struct KeyStatus { + KeyStatus(const CencKeyId& aId, + const nsString& aSessionId, + GMPMediaKeyStatus aStatus) + : mId(aId) + , mSessionId(aSessionId) + , mStatus(aStatus) + {} + KeyStatus(const KeyStatus& aOther) + : mId(aOther.mId) + , mSessionId(aOther.mSessionId) + , mStatus(aOther.mStatus) + {} + bool operator==(const KeyStatus& aOther) const { + return mId == aOther.mId && + mSessionId == aOther.mSessionId; + }; + + CencKeyId mId; + nsString mSessionId; + GMPMediaKeyStatus mStatus; + }; + // Locks the CDMCaps. It must be locked to access its shared state. // Threadsafe when locked. class MOZ_STACK_CLASS AutoLock { @@ -37,16 +61,12 @@ bool IsKeyUsable(const CencKeyId& aKeyId); - // Returns true if setting this key usable results in the usable keys - // changing for this session, i.e. the key was not previously marked usable. - bool SetKeyUsable(const CencKeyId& aKeyId, const nsString& aSessionId); - - // Returns true if setting this key unusable results in the usable keys - // changing for this session, i.e. the key was previously marked usable. - bool SetKeyUnusable(const CencKeyId& aKeyId, const nsString& aSessionId); + // Returns true if key status changed, + // i.e. the key status changed from usable to expired. + bool SetKeyStatus(const CencKeyId& aKeyId, const nsString& aSessionId, GMPMediaKeyStatus aStatus); - void GetUsableKeysForSession(const nsAString& aSessionId, - nsTArray& aOutKeyIds); + void GetKeyStatusesForSession(const nsAString& aSessionId, + nsTArray& aOutKeyStatuses); // Sets the capabilities of the CDM. aCaps is the logical OR of the // GMP_EME_CAP_* flags from gmp-decryption.h. @@ -85,25 +105,7 @@ Monitor mMonitor; - struct UsableKey { - UsableKey(const CencKeyId& aId, - const nsString& aSessionId) - : mId(aId) - , mSessionId(aSessionId) - {} - UsableKey(const UsableKey& aOther) - : mId(aOther.mId) - , mSessionId(aOther.mSessionId) - {} - bool operator==(const UsableKey& aOther) const { - return mId == aOther.mId && - mSessionId == aOther.mSessionId; - }; - - CencKeyId mId; - nsString mSessionId; - }; - nsTArray mUsableKeyIds; + nsTArray mKeyStatuses; nsTArray mWaitForKeys; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/CDMProxy.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/CDMProxy.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/CDMProxy.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/CDMProxy.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -415,6 +415,7 @@ case kGMPLicenseRequest: return dom::MediaKeyMessageType::License_request; case kGMPLicenseRenewal: return dom::MediaKeyMessageType::License_renewal; case kGMPLicenseRelease: return dom::MediaKeyMessageType::License_release; + case kGMPIndividualizationRequest: return dom::MediaKeyMessageType::Individualization_request; default: return dom::MediaKeyMessageType::License_request; }; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/MediaEncryptedEvent.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/MediaEncryptedEvent.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/MediaEncryptedEvent.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/MediaEncryptedEvent.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -52,6 +52,15 @@ } already_AddRefed +MediaEncryptedEvent::Constructor(EventTarget* aOwner) +{ + nsRefPtr e = new MediaEncryptedEvent(aOwner); + e->InitEvent(NS_LITERAL_STRING("encrypted"), false, false); + e->SetTrusted(true); + return e.forget(); +} + +already_AddRefed MediaEncryptedEvent::Constructor(EventTarget* aOwner, const nsAString& aInitDataType, const nsTArray& aInitData) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/MediaEncryptedEvent.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/MediaEncryptedEvent.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/MediaEncryptedEvent.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/MediaEncryptedEvent.h 2015-02-03 14:33:40.000000000 +0000 @@ -39,6 +39,9 @@ virtual JSObject* WrapObjectInternal(JSContext* aCx) MOZ_OVERRIDE; static already_AddRefed + Constructor(EventTarget* aOwner); + + static already_AddRefed Constructor(EventTarget* aOwner, const nsAString& aInitDataType, const nsTArray& aInitData); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/MediaKeys.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/MediaKeys.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/MediaKeys.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/MediaKeys.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -359,15 +359,21 @@ } already_AddRefed -MediaKeys::CreateSession(SessionType aSessionType, +MediaKeys::CreateSession(JSContext* aCx, + SessionType aSessionType, ErrorResult& aRv) { - nsRefPtr session = new MediaKeySession(GetParentObject(), + nsRefPtr session = new MediaKeySession(aCx, + GetParentObject(), this, mKeySystem, aSessionType, aRv); + if (aRv.Failed()) { + return nullptr; + } + // Add session to the set of sessions awaiting their sessionId being ready. mPendingSessions.Put(session->Token(), session); @@ -433,33 +439,6 @@ } mElement = aElement; - nsresult rv = CheckPrincipals(); - if (NS_FAILED(rv)) { - mElement = nullptr; - return rv; - } - - return NS_OK; -} - -nsresult -MediaKeys::CheckPrincipals() -{ - MOZ_ASSERT(NS_IsMainThread()); - if (!IsBoundToMediaElement()) { - return NS_ERROR_FAILURE; - } - - nsRefPtr elementPrincipal(mElement->GetCurrentPrincipal()); - nsRefPtr elementTopLevelPrincipal(mElement->GetTopLevelPrincipal()); - if (!elementPrincipal || - !mPrincipal || - !elementPrincipal->Equals(mPrincipal) || - !elementTopLevelPrincipal || - !mTopLevelPrincipal || - !elementTopLevelPrincipal->Equals(mTopLevelPrincipal)) { - return NS_ERROR_FAILURE; - } return NS_OK; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/MediaKeySession.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/MediaKeySession.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/MediaKeySession.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/MediaKeySession.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -9,6 +9,7 @@ #include "mozilla/dom/MediaKeyError.h" #include "mozilla/dom/MediaKeyMessageEvent.h" #include "mozilla/dom/MediaEncryptedEvent.h" +#include "mozilla/dom/MediaKeyStatusMap.h" #include "nsCycleCollectionParticipant.h" #include "mozilla/CDMProxy.h" #include "mozilla/AsyncEventDispatcher.h" @@ -22,6 +23,7 @@ DOMEventTargetHelper, mMediaKeyError, mKeys, + mKeyStatusMap, mClosed) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaKeySession) @@ -34,7 +36,8 @@ // unique token. static uint32_t sMediaKeySessionNum = 0; -MediaKeySession::MediaKeySession(nsPIDOMWindow* aParent, +MediaKeySession::MediaKeySession(JSContext* aCx, + nsPIDOMWindow* aParent, MediaKeys* aKeys, const nsAString& aKeySystem, SessionType aSessionType, @@ -46,8 +49,12 @@ , mToken(sMediaKeySessionNum++) , mIsClosed(false) , mUninitialized(true) + , mKeyStatusMap(new MediaKeyStatusMap(aCx, aParent, aRv)) { MOZ_ASSERT(aParent); + if (aRv.Failed()) { + return; + } mClosed = mKeys->MakePromise(aRv); } @@ -106,6 +113,30 @@ return mClosed; } + +void +MediaKeySession::UpdateKeyStatusMap() +{ + MOZ_ASSERT(!IsClosed()); + if (!mKeys->GetCDMProxy()) { + return; + } + + nsTArray keyStatuses; + { + CDMCaps::AutoLock caps(mKeys->GetCDMProxy()->Capabilites()); + caps.GetKeyStatusesForSession(mSessionId, keyStatuses); + } + + mKeyStatusMap->Update(keyStatuses); +} + +MediaKeyStatusMap* +MediaKeySession::KeyStatuses() const +{ + return mKeyStatusMap; +} + already_AddRefed MediaKeySession::GenerateRequest(const nsAString& aInitDataType, const ArrayBufferViewOrArrayBuffer& aInitData, @@ -248,34 +279,6 @@ return promise.forget(); } -already_AddRefed -MediaKeySession::GetUsableKeyIds(ErrorResult& aRv) -{ - nsRefPtr promise(mKeys->MakePromise(aRv)); - if (aRv.Failed()) { - return nullptr; - } - - if (IsClosed() || !mKeys->GetCDMProxy()) { - promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); - return promise.forget(); - } - - nsTArray keyIds; - { - CDMCaps::AutoLock caps(mKeys->GetCDMProxy()->Capabilites()); - caps.GetUsableKeysForSession(mSessionId, keyIds); - } - - nsTArray> array; - for (size_t i = 0; i < keyIds.Length(); i++) { - array.AppendElement(keyIds[i]); - } - promise->MaybeResolve(array); - - return promise.forget(); -} - void MediaKeySession::DispatchKeyMessage(MediaKeyMessageType aMessageType, const nsTArray& aMessage) @@ -302,6 +305,9 @@ if (IsClosed()) { return; } + + UpdateKeyStatusMap(); + nsRefPtr asyncDispatcher = new AsyncEventDispatcher(this, NS_LITERAL_STRING("keyschange"), false); asyncDispatcher->PostDOMEvent(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/MediaKeySession.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/MediaKeySession.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/MediaKeySession.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/MediaKeySession.h 2015-02-03 14:33:40.000000000 +0000 @@ -30,6 +30,7 @@ class ArrayBufferViewOrArrayBuffer; class MediaKeyError; +class MediaKeyStatusMap; class MediaKeySession MOZ_FINAL : public DOMEventTargetHelper { @@ -38,7 +39,8 @@ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaKeySession, DOMEventTargetHelper) public: - MediaKeySession(nsPIDOMWindow* aParent, + MediaKeySession(JSContext* aCx, + nsPIDOMWindow* aParent, MediaKeys* aKeys, const nsAString& aKeySystem, SessionType aSessionType, @@ -51,6 +53,8 @@ // Mark this as resultNotAddRefed to return raw pointers MediaKeyError* GetError() const; + MediaKeyStatusMap* KeyStatuses() const; + void GetKeySystem(nsString& aRetval) const; void GetSessionId(nsString& aRetval) const; @@ -78,8 +82,6 @@ already_AddRefed Remove(ErrorResult& aRv); - already_AddRefed GetUsableKeyIds(ErrorResult& aRv); - void DispatchKeyMessage(MediaKeyMessageType aMessageType, const nsTArray& aMessage); @@ -97,6 +99,8 @@ private: ~MediaKeySession(); + void UpdateKeyStatusMap(); + nsRefPtr mClosed; nsRefPtr mMediaKeyError; @@ -107,6 +111,7 @@ const uint32_t mToken; bool mIsClosed; bool mUninitialized; + nsRefPtr mKeyStatusMap; }; } // namespace dom diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/MediaKeys.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/MediaKeys.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/MediaKeys.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/MediaKeys.h 2015-02-03 14:33:40.000000000 +0000 @@ -65,7 +65,8 @@ void GetKeySystem(nsString& retval) const; // JavaScript: MediaKeys.createSession() - already_AddRefed CreateSession(SessionType aSessionType, + already_AddRefed CreateSession(JSContext* aCx, + SessionType aSessionType, ErrorResult& aRv); // JavaScript: MediaKeys.SetServerCertificate() @@ -117,10 +118,6 @@ // Returns true if this MediaKeys has been bound to a media element. bool IsBoundToMediaElement() const; - // Return NS_OK if the principals are the same as when the MediaKeys - // was created, failure otherwise. - nsresult CheckPrincipals(); - private: bool IsInPrivateBrowsing(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/MediaKeyStatusMap.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/MediaKeyStatusMap.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/MediaKeyStatusMap.cpp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/MediaKeyStatusMap.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -0,0 +1,242 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsPIDOMWindow.h" +#include "mozilla/dom/MediaKeyStatusMap.h" +#include "mozilla/dom/UnionTypes.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeyStatusMap) +NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeyStatusMap) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeyStatusMap) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTION_CLASS(MediaKeyStatusMap) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaKeyStatusMap) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER + tmp->mMap = nullptr; +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaKeyStatusMap) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(MediaKeyStatusMap) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mMap) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +MediaKeyStatusMap::MediaKeyStatusMap(JSContext* aCx, + nsPIDOMWindow* aParent, + ErrorResult& aRv) + : mParent(aParent) + , mUpdateError(NS_OK) +{ + mMap = JS::NewMapObject(aCx); + if (NS_WARN_IF(!mMap)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + } +} + +MediaKeyStatusMap::~MediaKeyStatusMap() +{ +} + +JSObject* +MediaKeyStatusMap::WrapObject(JSContext* aCx) +{ + return MediaKeyStatusMapBinding::Wrap(aCx, this); +} + +nsPIDOMWindow* +MediaKeyStatusMap::GetParentObject() const +{ + return mParent; +} + +MediaKeyStatus +MediaKeyStatusMap::Get(JSContext* aCx, + const ArrayBufferViewOrArrayBuffer& aKey, + ErrorResult& aRv) const +{ + if (NS_FAILED(mUpdateError)) { + aRv.Throw(mUpdateError); + return MediaKeyStatus::Internal_error; + } + + JS::Rooted map(aCx, mMap); + JS::Rooted key(aCx); + JS::Rooted val(aCx); + + if (!aKey.ToJSVal(aCx, map, &key) || + !JS::MapGet(aCx, map, key, &val)) { + aRv.Throw(NS_ERROR_FAILURE); + return MediaKeyStatus::Internal_error; + } + + bool ok; + int index = FindEnumStringIndex( + aCx, val, MediaKeyStatusValues::strings, + "MediaKeyStatus", "Invalid MediaKeyStatus value", &ok); + + return ok ? static_cast(index) : + MediaKeyStatus::Internal_error; +} + +bool +MediaKeyStatusMap::Has(JSContext* aCx, + const ArrayBufferViewOrArrayBuffer& aKey, + ErrorResult& aRv) const +{ + if (NS_FAILED(mUpdateError)) { + aRv.Throw(mUpdateError); + return false; + } + + JS::Rooted map(aCx, mMap); + JS::Rooted key(aCx); + bool result = false; + + if (!aKey.ToJSVal(aCx, map, &key) || + !JS::MapHas(aCx, map, key, &result)) { + aRv.Throw(NS_ERROR_FAILURE); + } + + return result; +} + +template +static void CallMapMethod(JSContext* aCx, + const JS::Heap& aMap, + JS::MutableHandle aResult, + ErrorResult& aRv, + nsresult aUpdateError) +{ + if (NS_FAILED(aUpdateError)) { + aRv.Throw(aUpdateError); + return; + } + + JS::Rooted map(aCx, aMap); + JS::Rooted result(aCx); + if (!Method(aCx, map, &result)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + aResult.set(&result.toObject()); +} + +void +MediaKeyStatusMap::Keys(JSContext* aCx, + JS::MutableHandle aResult, + ErrorResult& aRv) const +{ + CallMapMethod(aCx, mMap, aResult, aRv, mUpdateError); +} + +void +MediaKeyStatusMap::Values(JSContext* aCx, + JS::MutableHandle aResult, + ErrorResult& aRv) const +{ + CallMapMethod(aCx, mMap, aResult, aRv, mUpdateError); +} +void +MediaKeyStatusMap::Entries(JSContext* aCx, + JS::MutableHandle aResult, + ErrorResult& aRv) const +{ + CallMapMethod(aCx, mMap, aResult, aRv, mUpdateError); +} + +uint32_t +MediaKeyStatusMap::GetSize(JSContext* aCx, ErrorResult& aRv) const +{ + if (NS_FAILED(mUpdateError)) { + aRv.Throw(mUpdateError); + return 0; + } + JS::Rooted map(aCx, mMap); + return JS::MapSize(aCx, map); +} + +static MediaKeyStatus +ToMediaKeyStatus(GMPMediaKeyStatus aStatus) { + switch (aStatus) { + case kGMPUsable: return MediaKeyStatus::Usable; + case kGMPExpired: return MediaKeyStatus::Expired; + case kGMPOutputDownscaled: return MediaKeyStatus::Output_downscaled; + case kGMPOutputNotAllowed: return MediaKeyStatus::Output_not_allowed; + case kGMPInternalError: return MediaKeyStatus::Internal_error; + default: return MediaKeyStatus::Internal_error; + } +} + +static bool +ToJSString(JSContext* aCx, GMPMediaKeyStatus aStatus, + JS::MutableHandle aResult) +{ + auto val = uint32_t(ToMediaKeyStatus(aStatus)); + MOZ_ASSERT(val < ArrayLength(MediaKeyStatusValues::strings)); + JSString* str = JS_NewStringCopyN(aCx, + MediaKeyStatusValues::strings[val].value, + MediaKeyStatusValues::strings[val].length); + if (!str) { + return false; + } + aResult.setString(str); + return true; +} + +nsresult +MediaKeyStatusMap::UpdateInternal(const nsTArray& keys) +{ + AutoJSAPI jsapi; + if (NS_WARN_IF(!jsapi.Init(mParent))) { + return NS_ERROR_FAILURE; + } + + jsapi.TakeOwnershipOfErrorReporting(); + JSContext* cx = jsapi.cx(); + JS::Rooted map(cx, mMap); + if (!JS::MapClear(cx, map)) { + return NS_ERROR_FAILURE; + } + + for (size_t i = 0; i < keys.Length(); i++) { + const auto& ks = keys[i]; + JS::Rooted key(cx); + JS::Rooted val(cx); + if (!ToJSValue(cx, TypedArrayCreator(ks.mId), &key) || + !ToJSString(cx, ks.mStatus, &val) || + !JS::MapSet(cx, map, key, val)) { + return NS_ERROR_OUT_OF_MEMORY; + } + } + + return NS_OK; +} + +void +MediaKeyStatusMap::Update(const nsTArray& keys) +{ + // Since we can't leave the map in a partial update state, we need + // to remember the error and throw it next time the interface methods + // are called. + mUpdateError = UpdateInternal(keys); +} + +} // namespace dom +} // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/MediaKeyStatusMap.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/MediaKeyStatusMap.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/MediaKeyStatusMap.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/MediaKeyStatusMap.h 2015-02-03 14:33:40.000000000 +0000 @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_MediaKeyStatuses_h +#define mozilla_dom_MediaKeyStatuses_h + +#include "mozilla/ErrorResult.h" +#include "mozilla/Attributes.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" + +#include "mozilla/dom/TypedArray.h" +#include "mozilla/dom/MediaKeyStatusMapBinding.h" +#include "mozilla/CDMCaps.h" + +class nsPIDOMWindow; +class ArrayBufferViewOrArrayBuffer; + +namespace mozilla { +namespace dom { + +class MediaKeyStatusMap MOZ_FINAL : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaKeyStatusMap) + +public: + explicit MediaKeyStatusMap(JSContext* aCx, + nsPIDOMWindow* aParent, + ErrorResult& aRv); + +protected: + ~MediaKeyStatusMap(); + +public: + nsPIDOMWindow* GetParentObject() const; + + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + MediaKeyStatus Get(JSContext* aCx, + const ArrayBufferViewOrArrayBuffer& aKey, + ErrorResult& aRv) const; + + bool Has(JSContext* aCx, + const ArrayBufferViewOrArrayBuffer& aKey, + ErrorResult& aRv) const; + + void Keys(JSContext* aCx, + JS::MutableHandle aResult, + ErrorResult& aRv) const; + + void Values(JSContext* aCx, + JS::MutableHandle aResult, + ErrorResult& aRv) const; + + void Entries(JSContext* aCx, + JS::MutableHandle aResult, + ErrorResult& aRv) const; + + uint32_t GetSize(JSContext* aCx, ErrorResult& aRv) const; + + void Update(const nsTArray& keys); + +private: + nsresult UpdateInternal(const nsTArray& keys); + + nsCOMPtr mParent; + JS::Heap mMap; + nsresult mUpdateError; +}; + +} // namespace dom +} // namespace mozilla + +#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/eme/moz.build 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/eme/moz.build 2015-02-03 14:33:40.000000000 +0000 @@ -10,6 +10,7 @@ 'MediaKeyMessageEvent.h', 'MediaKeys.h', 'MediaKeySession.h', + 'MediaKeyStatusMap.h', 'MediaKeySystemAccess.h', ] @@ -30,6 +31,7 @@ 'MediaKeyMessageEvent.cpp', 'MediaKeys.cpp', 'MediaKeySession.cpp', + 'MediaKeyStatusMap.cpp', 'MediaKeySystemAccess.cpp', ] diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/ffmpeg/FFmpegDataDecoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/ffmpeg/FFmpegDataDecoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/ffmpeg/FFmpegDataDecoder.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/ffmpeg/FFmpegDataDecoder.h 2015-02-03 14:33:40.000000000 +0000 @@ -30,9 +30,9 @@ static bool Link(); virtual nsresult Init() MOZ_OVERRIDE; - virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) = 0; + virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE = 0; virtual nsresult Flush() MOZ_OVERRIDE; - virtual nsresult Drain() = 0; + virtual nsresult Drain() MOZ_OVERRIDE = 0; virtual nsresult Shutdown() MOZ_OVERRIDE; protected: diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/gonk/GonkAudioDecoderManager.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/gonk/GonkAudioDecoderManager.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/gonk/GonkAudioDecoderManager.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/gonk/GonkAudioDecoderManager.cpp 2015-02-03 14:33:41.000000000 +0000 @@ -38,7 +38,8 @@ GonkAudioDecoderManager::GonkAudioDecoderManager( const mp4_demuxer::AudioDecoderConfig& aConfig) - : mAudioChannels(aConfig.channel_count) + : GonkDecoderManager() + , mAudioChannels(aConfig.channel_count) , mAudioRate(aConfig.samples_per_second) , mAudioProfile(aConfig.aac_profile) , mUseAdts(true) @@ -97,6 +98,31 @@ } } +status_t +GonkAudioDecoderManager::SendSampleToOMX(mp4_demuxer::MP4Sample* aSample) +{ + return mDecoder->Input(reinterpret_cast(aSample->data), + aSample->size, + aSample->composition_timestamp, + 0); +} + +void +GonkAudioDecoderManager::PerformFormatSpecificProcess(mp4_demuxer::MP4Sample* aSample) +{ + if (aSample && mUseAdts) { + int8_t frequency_index = + mp4_demuxer::Adts::GetFrequencyIndex(mAudioRate); + bool rv = mp4_demuxer::Adts::ConvertSample(mAudioChannels, + frequency_index, + mAudioProfile, + aSample); + if (!rv) { + GADM_LOG("Failed to apply ADTS header"); + } + } +} + nsresult GonkAudioDecoderManager::CreateAudioData(int64_t aStreamOffset, AudioData **v) { if (!(mAudioBuffer != nullptr && mAudioBuffer->data() != nullptr)) { @@ -210,12 +236,6 @@ return NS_OK; } -nsresult -GonkAudioDecoderManager::Flush() -{ - return NS_OK; -} - void GonkAudioDecoderManager::ReleaseAudioBuffer() { if (mAudioBuffer) { mDecoder->ReleaseMediaBuffer(mAudioBuffer); @@ -223,36 +243,4 @@ } } -nsresult -GonkAudioDecoderManager::Input(mp4_demuxer::MP4Sample* aSample) -{ - if (mDecoder == nullptr) { - GADM_LOG("Decoder is not inited"); - return NS_ERROR_UNEXPECTED; - } - if (aSample && mUseAdts) { - int8_t frequency_index = - mp4_demuxer::Adts::GetFrequencyIndex(mAudioRate); - bool rv = mp4_demuxer::Adts::ConvertSample(mAudioChannels, - frequency_index, - mAudioProfile, - aSample); - if (!rv) { - GADM_LOG("Failed to apply ADTS header"); - return NS_ERROR_FAILURE; - } - } - - status_t rv; - if (aSample) { - const uint8_t* data = reinterpret_cast(aSample->data); - uint32_t length = aSample->size; - rv = mDecoder->Input(data, length, aSample->composition_timestamp, 0); - } else { - // Inputted data is null, so it is going to notify decoder EOS - rv = mDecoder->Input(0, 0, 0ll, 0); - } - return rv == OK ? NS_OK : NS_ERROR_UNEXPECTED; -} - } // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/gonk/GonkAudioDecoderManager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/gonk/GonkAudioDecoderManager.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/gonk/GonkAudioDecoderManager.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/gonk/GonkAudioDecoderManager.h 2015-02-03 14:33:41.000000000 +0000 @@ -28,12 +28,14 @@ virtual android::sp Init(MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE; - virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE; - virtual nsresult Output(int64_t aStreamOffset, nsRefPtr& aOutput) MOZ_OVERRIDE; - virtual nsresult Flush() MOZ_OVERRIDE; +protected: + virtual void PerformFormatSpecificProcess(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE; + + virtual status_t SendSampleToOMX(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE; + private: nsresult CreateAudioData(int64_t aStreamOffset, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/gonk/GonkMediaDataDecoder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/gonk/GonkMediaDataDecoder.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/gonk/GonkMediaDataDecoder.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/gonk/GonkMediaDataDecoder.cpp 2015-02-03 14:33:41.000000000 +0000 @@ -24,6 +24,86 @@ namespace mozilla { +GonkDecoderManager::GonkDecoderManager() + : mMonitor("GonkDecoderManager") + , mInputEOS(false) +{ +} + +nsresult +GonkDecoderManager::Input(mp4_demuxer::MP4Sample* aSample) +{ + ReentrantMonitorAutoEnter mon(mMonitor); + + // To maintain the order of the MP4Sample, it needs to send the queued samples + // to OMX first. And then the current input aSample. + // If it fails to input sample to OMX, it needs to add current into queue + // for next round. + uint32_t len = mQueueSample.Length(); + status_t rv = OK; + + for (uint32_t i = 0; i < len; i++) { + rv = SendSampleToOMX(mQueueSample.ElementAt(0)); + if (rv != OK) { + break; + } + mQueueSample.RemoveElementAt(0); + } + + // Already reaching EOS, do not add any sample to queue. + if (mInputEOS) { + return NS_OK; + } + + // When EOS, aSample will be null and sends this empty MP4Sample to nofity + // OMX it reachs EOS. + nsAutoPtr sample; + if (!aSample) { + sample = new mp4_demuxer::MP4Sample(); + mInputEOS = true; + } + + // If rv is OK, that means mQueueSample is empty, now try to queue current input + // aSample. + if (rv == OK) { + MOZ_ASSERT(!mQueueSample.Length()); + mp4_demuxer::MP4Sample* tmp; + if (aSample) { + tmp = aSample; + PerformFormatSpecificProcess(aSample); + } else { + tmp = sample; + } + rv = SendSampleToOMX(tmp); + if (rv == OK) { + return NS_OK; + } + } + + // Current valid sample can't be sent into OMX, adding the clone one into queue + // for next round. + if (!sample) { + sample = new mp4_demuxer::MP4Sample(*aSample); + } + mQueueSample.AppendElement(sample); + + // In most cases, EAGAIN or ETIMEOUT safe due to OMX can't process the + // filled buffer on time. It should be gone When requeuing sample next time. + if (rv == -EAGAIN || rv == -ETIMEDOUT) { + return NS_OK; + } + + return NS_ERROR_UNEXPECTED; +} + +nsresult +GonkDecoderManager::Flush() +{ + ReentrantMonitorAutoEnter mon(mMonitor); + mQueueSample.Clear(); + return NS_OK; +} + GonkMediaDataDecoder::GonkMediaDataDecoder(GonkDecoderManager* aManager, MediaTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback) @@ -92,6 +172,11 @@ nsresult rv = NS_ERROR_ABORT; while (!mDrainComplete) { + // There are samples in queue, try to send them into decoder when EOS. + if (mSignaledEOS && mManager->HasQueuedSample()) { + GMDD_LOG("ProcessOutput: drain all input samples"); + rv = mManager->Input(nullptr); + } rv = mManager->Output(mLastStreamOffset, output); if (rv == NS_OK) { mCallback->Output(output); @@ -105,7 +190,9 @@ } } - if (rv == NS_ERROR_NOT_AVAILABLE) { + MOZ_ASSERT_IF(mSignaledEOS, !mManager->HasQueuedSample()); + + if (rv == NS_ERROR_NOT_AVAILABLE && !mSignaledEOS) { mCallback->InputExhausted(); return; } @@ -161,8 +248,8 @@ } bool -GonkMediaDataDecoder::IsDormantNeeded() { - +GonkMediaDataDecoder::IsDormantNeeded() +{ return mDecoder.get() ? true : false; } @@ -173,7 +260,8 @@ } void -GonkMediaDataDecoder::ReleaseMediaResources() { +GonkMediaDataDecoder::ReleaseMediaResources() +{ mManager->ReleaseMediaResources(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/gonk/GonkMediaDataDecoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/gonk/GonkMediaDataDecoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/gonk/GonkMediaDataDecoder.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/gonk/GonkMediaDataDecoder.h 2015-02-03 14:33:41.000000000 +0000 @@ -19,25 +19,58 @@ // Manage the data flow from inputting encoded data and outputting decode data. class GonkDecoderManager { public: + GonkDecoderManager(); + virtual ~GonkDecoderManager() {} // Creates and initializs the GonkDecoder. // Returns nullptr on failure. virtual android::sp Init(MediaDataDecoderCallback* aCallback) = 0; + // Add samples into OMX decoder or queue them if decoder is out of input buffer. + virtual nsresult Input(mp4_demuxer::MP4Sample* aSample); + // Produces decoded output, it blocks until output can be produced or a timeout // is expired or until EOS. Returns NS_OK on success, or NS_ERROR_NOT_AVAILABLE // if there's not enough data to produce more output. If this returns a failure // code other than NS_ERROR_NOT_AVAILABLE, an error will be reported to the // MP4Reader. - virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) = 0; + // The overrided class should follow the same behaviour. virtual nsresult Output(int64_t aStreamOffset, nsRefPtr& aOutput) = 0; - virtual nsresult Flush() = 0; - virtual void AllocateMediaResources() {}; + // Flush the queued sample. + // It this function is overrided by subclass, this functino should be called + // in the overrided function. + virtual nsresult Flush(); + + virtual void AllocateMediaResources() {} + + virtual void ReleaseMediaResources() {} + + bool HasQueuedSample() { + ReentrantMonitorAutoEnter mon(mMonitor); + return mQueueSample.Length(); + } + +protected: + // It performs special operation to MP4 sample, the real action is depended on + // the codec type. + virtual void PerformFormatSpecificProcess(mp4_demuxer::MP4Sample* aSample) {} + + // It sends MP4Sample to OMX layer. It must be overrided by subclass. + virtual android::status_t SendSampleToOMX(mp4_demuxer::MP4Sample* aSample) = 0; + + // It protects mQueueSample. + ReentrantMonitor mMonitor; + + // An queue with the MP4 samples which are waiting to be sent into OMX. + // If an element is an empty MP4Sample, that menas EOS. There should not + // any sample be queued after EOS. + nsTArray> mQueueSample; - virtual void ReleaseMediaResources() {}; + // True when mQueueSample gets an empty MP4Sample. + bool mInputEOS; }; // Samples are decoded using the GonkDecoder (MediaCodec) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp 2015-02-03 14:33:41.000000000 +0000 @@ -49,11 +49,11 @@ }; GonkVideoDecoderManager::GonkVideoDecoderManager( - mozilla::layers::ImageContainer* aImageContainer, - const mp4_demuxer::VideoDecoderConfig& aConfig) - : mImageContainer(aImageContainer) + mozilla::layers::ImageContainer* aImageContainer, + const mp4_demuxer::VideoDecoderConfig& aConfig) + : GonkDecoderManager() + , mImageContainer(aImageContainer) , mReaderCallback(nullptr) - , mMonitor("GonkVideoDecoderManager") , mColorConverterBufferSize(0) , mNativeWindow(nullptr) , mPendingVideoBuffersLock("GonkVideoDecoderManager::mPendingVideoBuffersLock") @@ -124,7 +124,7 @@ void GonkVideoDecoderManager::QueueFrameTimeIn(int64_t aPTS, int64_t aDuration) { - MonitorAutoLock mon(mMonitor); + ReentrantMonitorAutoEnter mon(mMonitor); FrameTimeInfo timeInfo = {aPTS, aDuration}; mFrameTimeInfo.AppendElement(timeInfo); } @@ -132,7 +132,7 @@ nsresult GonkVideoDecoderManager::QueueFrameTimeOut(int64_t aPTS, int64_t& aDuration) { - MonitorAutoLock mon(mMonitor); + ReentrantMonitorAutoEnter mon(mMonitor); // Set default to 1 here. // During seeking, frames could still in MediaCodec and the mFrameTimeInfo could @@ -435,41 +435,41 @@ } } -nsresult -GonkVideoDecoderManager::Input(mp4_demuxer::MP4Sample* aSample) +status_t +GonkVideoDecoderManager::SendSampleToOMX(mp4_demuxer::MP4Sample* aSample) { - if (mDecoder == nullptr) { - GVDM_LOG("Decoder is not inited"); - return NS_ERROR_UNEXPECTED; + // An empty MP4Sample is going to notify EOS to decoder. It doesn't need + // to keep PTS and duration. + if (aSample->data && aSample->duration && aSample->composition_timestamp) { + QueueFrameTimeIn(aSample->composition_timestamp, aSample->duration); } - status_t rv; + + return mDecoder->Input(reinterpret_cast(aSample->data), + aSample->size, + aSample->composition_timestamp, + 0); +} + +void +GonkVideoDecoderManager::PerformFormatSpecificProcess(mp4_demuxer::MP4Sample* aSample) +{ if (aSample != nullptr) { // We must prepare samples in AVC Annex B. mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample); - // Forward sample data to the decoder. - - QueueFrameTimeIn(aSample->composition_timestamp, aSample->duration); - - const uint8_t* data = reinterpret_cast(aSample->data); - uint32_t length = aSample->size; - rv = mDecoder->Input(data, length, aSample->composition_timestamp, 0); - } - else { - // Inputted data is null, so it is going to notify decoder EOS - rv = mDecoder->Input(nullptr, 0, 0ll, 0); } - return (rv == OK) ? NS_OK : NS_ERROR_FAILURE; } nsresult GonkVideoDecoderManager::Flush() { + GonkDecoderManager::Flush(); + status_t err = mDecoder->flush(); if (err != OK) { return NS_ERROR_FAILURE; } - MonitorAutoLock mon(mMonitor); + ReentrantMonitorAutoEnter mon(mMonitor); mFrameTimeInfo.Clear(); return NS_OK; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/gonk/GonkVideoDecoderManager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/gonk/GonkVideoDecoderManager.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/gonk/GonkVideoDecoderManager.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/gonk/GonkVideoDecoderManager.h 2015-02-03 14:33:41.000000000 +0000 @@ -45,8 +45,6 @@ virtual android::sp Init(MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE; - virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE; - virtual nsresult Output(int64_t aStreamOffset, nsRefPtr& aOutput) MOZ_OVERRIDE; @@ -58,6 +56,11 @@ static void RecycleCallback(TextureClient* aClient, void* aClosure); +protected: + virtual void PerformFormatSpecificProcess(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE; + + virtual android::status_t SendSampleToOMX(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE; + private: struct FrameInfo { @@ -155,12 +158,11 @@ android::sp mManagerLooper; FrameInfo mFrameInfo; - // It protects mFrameTimeInfo. - Monitor mMonitor; // Array of FrameTimeInfo whose corresponding frames are sent to OMX. // Ideally, it is a FIFO. Input() adds the entry to the end element and // CreateVideoData() takes the first entry. However, there are exceptions // due to MediaCodec error or seeking. + // It is protected by mMonitor. nsTArray mFrameTimeInfo; // color converter diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/MP4Reader.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/MP4Reader.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/MP4Reader.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/MP4Reader.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -117,6 +117,9 @@ , mIsEncrypted(false) , mIndexReady(false) , mDemuxerMonitor("MP4 Demuxer") +#if defined(XP_WIN) + , mDormantEnabled(Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false)) +#endif { MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread."); MOZ_COUNT_CTOR(MP4Reader); @@ -252,15 +255,15 @@ #endif void MP4Reader::RequestCodecResource() { -#ifdef MOZ_GONK_MEDIACODEC - if(mVideo.mDecoder) { +#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN) + if (mVideo.mDecoder) { mVideo.mDecoder->AllocateMediaResources(); } #endif } bool MP4Reader::IsWaitingOnCodecResource() { -#ifdef MOZ_GONK_MEDIACODEC +#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN) return mVideo.mDecoder && mVideo.mDecoder->IsWaitingMediaResources(); #endif return false; @@ -362,7 +365,7 @@ { MonitorAutoUnlock unlock(mDemuxerMonitor); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - mIsEncrypted = mDemuxer->Crypto().valid; + mInfo.mIsEncrypted = mIsEncrypted = mDemuxer->Crypto().valid; } // Remember that we've initialized the demuxer, so that if we're decoding @@ -446,7 +449,8 @@ mVideo.mCallback = new DecoderCallback(this, kVideo); if (mSharedDecoderManager) { mVideo.mDecoder = - mSharedDecoderManager->CreateVideoDecoder(video, + mSharedDecoderManager->CreateVideoDecoder(mPlatform, + video, mLayersBackendType, mDecoder->GetImageContainer(), mVideo.mTaskQueue, @@ -461,6 +465,7 @@ NS_ENSURE_TRUE(mVideo.mDecoder != nullptr, NS_ERROR_FAILURE); nsresult rv = mVideo.mDecoder->Init(); NS_ENSURE_SUCCESS(rv, rv); + mInfo.mVideo.mIsHardwareAccelerated = mVideo.mDecoder->IsHardwareAccelerated(); } // Get the duration, and report it to the decoder if we have it. @@ -1001,15 +1006,20 @@ bool MP4Reader::IsDormantNeeded() { -#ifdef MOZ_GONK_MEDIACODEC - return mVideo.mDecoder && mVideo.mDecoder->IsDormantNeeded(); +#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN) + return +#if defined(XP_WIN) + mDormantEnabled && +#endif + mVideo.mDecoder && + mVideo.mDecoder->IsDormantNeeded(); #endif return false; } void MP4Reader::ReleaseMediaResources() { -#ifdef MOZ_GONK_MEDIACODEC +#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN) // Before freeing a video codec, all video buffers needed to be released // even from graphics pipeline. VideoFrameContainer* container = mDecoder->GetVideoFrameContainer(); @@ -1024,7 +1034,7 @@ void MP4Reader::NotifyResourcesStatusChanged() { -#ifdef MOZ_GONK_MEDIACODEC +#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN) if (mDecoder) { mDecoder->NotifyWaitingForResourcesStatusChanged(); } @@ -1043,7 +1053,7 @@ void MP4Reader::SetSharedDecoderManager(SharedDecoderManager* aManager) { -#ifdef MOZ_GONK_MEDIACODEC +#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN) mSharedDecoderManager = aManager; #endif } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/MP4Reader.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/MP4Reader.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/MP4Reader.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/MP4Reader.h 2015-02-03 14:33:40.000000000 +0000 @@ -267,6 +267,10 @@ bool mIndexReady; Monitor mDemuxerMonitor; nsRefPtr mSharedDecoderManager; + +#if defined(XP_WIN) + const bool mDormantEnabled; +#endif }; } // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/MP4Stream.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/MP4Stream.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/MP4Stream.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/MP4Stream.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -28,12 +28,15 @@ { MOZ_ASSERT(mPinCount > 0); CacheBlock block(aOffset, aCount); + if (!block.Init()) { + return false; + } uint32_t sum = 0; uint32_t bytesRead = 0; do { uint64_t offset = aOffset + sum; - char* buffer = reinterpret_cast(block.mBuffer.get()) + sum; + char* buffer = block.Buffer() + sum; uint32_t toRead = aCount - sum; MonitorAutoUnlock unlock(*aToUnlock); nsresult rv = mResource->ReadAt(offset, buffer, toRead, &bytesRead); @@ -76,7 +79,7 @@ // First, check our local cache. for (size_t i = 0; i < mCache.Length(); ++i) { if (mCache[i].mOffset == aOffset && mCache[i].mCount >= aCount) { - memcpy(aBuffer, mCache[i].mBuffer, aCount); + memcpy(aBuffer, mCache[i].Buffer(), aCount); *aBytesRead = aCount; return true; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/MP4Stream.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/MP4Stream.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/MP4Stream.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/MP4Stream.h 2015-02-03 14:33:40.000000000 +0000 @@ -70,10 +70,24 @@ struct CacheBlock { CacheBlock(int64_t aOffset, size_t aCount) - : mOffset(aOffset), mCount(aCount), mBuffer(new uint8_t[aCount]) {} + : mOffset(aOffset), mCount(aCount), mBuffer(nullptr) {} int64_t mOffset; size_t mCount; - nsAutoArrayPtr mBuffer; + + bool Init() + { + mBuffer = new (fallible) char[mCount]; + return !!mBuffer; + } + + char* Buffer() + { + MOZ_ASSERT(mBuffer.get()); + return mBuffer.get(); + } + + private: + nsAutoArrayPtr mBuffer; }; nsTArray mCache; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/PlatformDecoderModule.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/PlatformDecoderModule.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/PlatformDecoderModule.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/PlatformDecoderModule.h 2015-02-03 14:33:40.000000000 +0000 @@ -245,6 +245,7 @@ virtual void AllocateMediaResources() {} virtual void ReleaseMediaResources() {} virtual void ReleaseDecoder() {} + virtual bool IsHardwareAccelerated() const { return false; } }; } // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/SharedDecoderManager.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/SharedDecoderManager.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/SharedDecoderManager.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/SharedDecoderManager.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -55,11 +55,14 @@ }; SharedDecoderManager::SharedDecoderManager() - : mActiveProxy(nullptr) + : mTaskQueue(new MediaTaskQueue(GetMediaDecodeThreadPool())) + , mActiveProxy(nullptr) , mActiveCallback(nullptr) , mWaitForInternalDrain(false) , mMonitor("SharedDecoderProxy") + , mDecoderReleasedResources(false) { + MOZ_ASSERT(NS_IsMainThread()); // taskqueue must be created on main thread. mCallback = new SharedDecoderCallback(this); } @@ -67,14 +70,18 @@ already_AddRefed SharedDecoderManager::CreateVideoDecoder( + PlatformDecoderModule* aPDM, const mp4_demuxer::VideoDecoderConfig& aConfig, layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer, MediaTaskQueue* aVideoTaskQueue, MediaDataDecoderCallback* aCallback) { if (!mDecoder) { - nsRefPtr platform(PlatformDecoderModule::Create()); - mDecoder = platform->CreateVideoDecoder( - aConfig, aLayersBackend, aImageContainer, aVideoTaskQueue, mCallback); + // We use the manager's task queue for the decoder, rather than the one + // passed in, so that none of the objects sharing the decoder can shutdown + // the task queue while we're potentially still using it for a *different* + // object also sharing the decoder. + mDecoder = aPDM->CreateVideoDecoder( + aConfig, aLayersBackend, aImageContainer, mTaskQueue, mCallback); if (!mDecoder) { return nullptr; } @@ -96,6 +103,11 @@ mActiveProxy = aProxy; mActiveCallback = aProxy->mCallback; + + if (mDecoderReleasedResources) { + mDecoder->AllocateMediaResources(); + mDecoderReleasedResources = false; + } } void @@ -125,6 +137,28 @@ } } +void +SharedDecoderManager::ReleaseMediaResources() +{ + mDecoderReleasedResources = true; + mDecoder->ReleaseMediaResources(); + mActiveProxy = nullptr; +} + +void +SharedDecoderManager::Shutdown() +{ + if (mDecoder) { + mDecoder->Shutdown(); + mDecoder = nullptr; + } + if (mTaskQueue) { + mTaskQueue->BeginShutdown(); + mTaskQueue->AwaitShutdownAndIdle(); + mTaskQueue = nullptr; + } +} + SharedDecoderProxy::SharedDecoderProxy( SharedDecoderManager* aManager, MediaDataDecoderCallback* aCallback) : mManager(aManager), mCallback(aCallback) @@ -146,7 +180,6 @@ mManager->Select(this); } return mManager->mDecoder->Input(aSample); - return NS_OK; } nsresult @@ -193,7 +226,7 @@ SharedDecoderProxy::ReleaseMediaResources() { if (mManager->mActiveProxy == this) { - mManager->mDecoder->ReleaseMediaResources(); + mManager->ReleaseMediaResources(); } } @@ -204,4 +237,11 @@ mManager->mDecoder->ReleaseMediaResources(); } } + +bool +SharedDecoderProxy::IsHardwareAccelerated() const +{ + return mManager->mDecoder->IsHardwareAccelerated(); +} + } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/SharedDecoderManager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/SharedDecoderManager.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/SharedDecoderManager.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/SharedDecoderManager.h 2015-02-03 14:33:40.000000000 +0000 @@ -25,6 +25,7 @@ SharedDecoderManager(); already_AddRefed CreateVideoDecoder( + PlatformDecoderModule* aPDM, const mp4_demuxer::VideoDecoderConfig& aConfig, layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer, MediaTaskQueue* aVideoTaskQueue, @@ -33,6 +34,8 @@ void SetReader(MediaDecoderReader* aReader); void Select(SharedDecoderProxy* aProxy); void SetIdle(MediaDataDecoder* aProxy); + void ReleaseMediaResources(); + void Shutdown(); friend class SharedDecoderProxy; friend class SharedDecoderCallback; @@ -42,11 +45,13 @@ void DrainComplete(); nsRefPtr mDecoder; + nsRefPtr mTaskQueue; SharedDecoderProxy* mActiveProxy; MediaDataDecoderCallback* mActiveCallback; nsAutoPtr mCallback; bool mWaitForInternalDrain; Monitor mMonitor; + bool mDecoderReleasedResources; }; class SharedDecoderProxy : public MediaDataDecoder @@ -65,6 +70,7 @@ virtual bool IsDormantNeeded() MOZ_OVERRIDE; virtual void ReleaseMediaResources() MOZ_OVERRIDE; virtual void ReleaseDecoder() MOZ_OVERRIDE; + virtual bool IsHardwareAccelerated() const MOZ_OVERRIDE; friend class SharedDecoderManager; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/wmf/WMFMediaDataDecoder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/wmf/WMFMediaDataDecoder.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/wmf/WMFMediaDataDecoder.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/wmf/WMFMediaDataDecoder.cpp 2015-02-03 14:33:41.000000000 +0000 @@ -48,7 +48,13 @@ nsresult WMFMediaDataDecoder::Shutdown() { - mTaskQueue->FlushAndDispatch(NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessShutdown)); + DebugOnly rv = mTaskQueue->FlushAndDispatch( + NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessShutdown)); +#ifdef DEBUG + if (NS_FAILED(rv)) { + NS_WARNING("WMFMediaDataDecoder::Shutdown() dispatch of task failed!"); + } +#endif return NS_OK; } @@ -60,6 +66,13 @@ mDecoder = nullptr; } +void +WMFMediaDataDecoder::ProcessReleaseDecoder() +{ + mMFTManager->Shutdown(); + mDecoder = nullptr; +} + // Inserts data into the decoder's pipeline. nsresult WMFMediaDataDecoder::Input(mp4_demuxer::MP4Sample* aSample) @@ -142,4 +155,33 @@ return NS_OK; } +void +WMFMediaDataDecoder::AllocateMediaResources() +{ + mDecoder = mMFTManager->Init(); +} + +void +WMFMediaDataDecoder::ReleaseMediaResources() +{ + DebugOnly rv = mTaskQueue->FlushAndDispatch( + NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessReleaseDecoder)); +#ifdef DEBUG + if (NS_FAILED(rv)) { + NS_WARNING("WMFMediaDataDecoder::ReleaseMediaResources() dispatch of task failed!"); + } +#endif +} + +void +WMFMediaDataDecoder::ReleaseDecoder() +{ + ReleaseMediaResources(); +} + +bool +WMFMediaDataDecoder::IsHardwareAccelerated() const { + return mMFTManager && mMFTManager->IsHardwareAccelerated(); +} + } // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/wmf/WMFMediaDataDecoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/wmf/WMFMediaDataDecoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/wmf/WMFMediaDataDecoder.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/wmf/WMFMediaDataDecoder.h 2015-02-03 14:33:41.000000000 +0000 @@ -46,6 +46,9 @@ // Destroys all resources. virtual void Shutdown() = 0; + + virtual bool IsHardwareAccelerated() const { return false; } + }; // Decodes audio and video using Windows Media Foundation. Samples are decoded @@ -70,6 +73,13 @@ virtual nsresult Shutdown() MOZ_OVERRIDE; + virtual bool IsWaitingMediaResources() { return false; }; + virtual bool IsDormantNeeded() { return true; }; + virtual void AllocateMediaResources() MOZ_OVERRIDE; + virtual void ReleaseMediaResources() MOZ_OVERRIDE; + virtual void ReleaseDecoder() MOZ_OVERRIDE; + virtual bool IsHardwareAccelerated() const MOZ_OVERRIDE; + private: // Called on the task queue. Inserts the sample into the decoder, and @@ -85,6 +95,7 @@ void ProcessDrain(); void ProcessShutdown(); + void ProcessReleaseDecoder(); RefPtr mTaskQueue; MediaDataDecoderCallback* mCallback; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp 2015-02-03 14:33:41.000000000 +0000 @@ -100,7 +100,9 @@ { MOZ_COUNT_DTOR(WMFVideoMFTManager); // Ensure DXVA/D3D9 related objects are released on the main thread. - DeleteOnMainThread(mDXVA2Manager); + if (mDXVA2Manager) { + DeleteOnMainThread(mDXVA2Manager); + } } const GUID& @@ -140,6 +142,8 @@ bool WMFVideoMFTManager::InitializeDXVA() { + MOZ_ASSERT(!mDXVA2Manager); + // If we use DXVA but aren't running with a D3D layer manager then the // readback of decoded video frames from GPU to CPU memory grinds painting // to a halt, and makes playback performance *worse*. @@ -150,7 +154,8 @@ return false; } - if (gfxWindowsPlatform::GetPlatform()->IsWARP()) { + if (gfxWindowsPlatform::GetPlatform()->IsWARP() || + !gfxPlatform::CanUseDXVA()) { return false; } @@ -487,6 +492,13 @@ WMFVideoMFTManager::Shutdown() { mDecoder = nullptr; + DeleteOnMainThread(mDXVA2Manager); +} + +bool +WMFVideoMFTManager::IsHardwareAccelerated() const +{ + return mUseHwAccel; } } // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/wmf/WMFVideoMFTManager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/wmf/WMFVideoMFTManager.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/fmp4/wmf/WMFVideoMFTManager.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/fmp4/wmf/WMFVideoMFTManager.h 2015-02-03 14:33:41.000000000 +0000 @@ -35,6 +35,8 @@ virtual void Shutdown() MOZ_OVERRIDE; + virtual bool IsHardwareAccelerated() const MOZ_OVERRIDE; + private: bool InitializeDXVA(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/gmp-api/gmp-decryption.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/gmp-api/gmp-decryption.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/gmp-api/gmp-decryption.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/gmp-api/gmp-decryption.h 2015-02-03 14:33:41.000000000 +0000 @@ -72,7 +72,18 @@ kGMPLicenseRequest = 0, kGMPLicenseRenewal = 1, kGMPLicenseRelease = 2, - kGMPMessageInvalid = 3 // Must always be last. + kGMPIndividualizationRequest = 3, + kGMPMessageInvalid = 4 // Must always be last. +}; + +enum GMPMediaKeyStatus { + kGMPUsable = 0, + kGMPExpired = 1, + kGMPOutputDownscaled = 2, + kGMPOutputNotAllowed = 3, + kGMPInternalError = 4, + kGMPUnknown = 5, + kGMPMediaKeyStatusInvalid = 6 // Must always be last. }; // Time in milliseconds, as offset from epoch, 1 Jan 1970. @@ -172,20 +183,14 @@ const char* aMessage, uint32_t aMessageLength) = 0; - // Marks a key as usable. Gecko will not call into the CDM to decrypt + // Notifies the status of a key. Gecko will not call into the CDM to decrypt // or decode content encrypted with a key unless the CDM has marked it // usable first. So a CDM *MUST* mark its usable keys as usable! - virtual void KeyIdUsable(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 KeyIdNotUsable(const char* aSessionId, - uint32_t aSessionIdLength, - const uint8_t* aKeyId, - uint32_t aKeyIdLength) = 0; + virtual void KeyStatusChanged(const char* aSessionId, + uint32_t aSessionIdLength, + const uint8_t* aKeyId, + uint32_t aKeyIdLength, + GMPMediaKeyStatus aStatus) = 0; // The CDM must report its capabilites of this CDM. aCaps should be a // logical OR of the GMP_EME_CAP_* flags. The CDM *MUST* call this @@ -219,7 +224,7 @@ kGMPSessionInvalid = 2 // Must always be last. }; -#define GMP_API_DECRYPTOR "eme-decrypt-v3" +#define GMP_API_DECRYPTOR "eme-decrypt-v6" // API exposed by plugin library to manage decryption sessions. // When the Host requests this by calling GMPGetAPIFunc(). diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPChild.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPChild.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPChild.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPChild.cpp 2015-02-03 14:33:41.000000000 +0000 @@ -37,9 +37,6 @@ #if defined(XP_WIN) #define TARGET_SANDBOX_EXPORTS #include "mozilla/sandboxTarget.h" -#elif defined (XP_LINUX) -#include "mozilla/Sandbox.h" -#include "mozilla/SandboxInfo.h" #elif defined(XP_MACOSX) #include "mozilla/Sandbox.h" #endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPDecryptorChild.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPDecryptorChild.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPDecryptorChild.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPDecryptorChild.cpp 2015-02-03 14:33:41.000000000 +0000 @@ -126,27 +126,17 @@ } void -GMPDecryptorChild::KeyIdUsable(const char* aSessionId, - uint32_t aSessionIdLength, - const uint8_t* aKeyId, - uint32_t aKeyIdLength) +GMPDecryptorChild::KeyStatusChanged(const char* aSessionId, + uint32_t aSessionIdLength, + const uint8_t* aKeyId, + uint32_t aKeyIdLength, + GMPMediaKeyStatus aStatus) { nsAutoTArray kid; kid.AppendElements(aKeyId, aKeyIdLength); - CALL_ON_GMP_THREAD(SendKeyIdUsable, - nsAutoCString(aSessionId, aSessionIdLength), kid); -} - -void -GMPDecryptorChild::KeyIdNotUsable(const char* aSessionId, - uint32_t aSessionIdLength, - const uint8_t* aKeyId, - uint32_t aKeyIdLength) -{ - nsAutoTArray kid; - kid.AppendElements(aKeyId, aKeyIdLength); - CALL_ON_GMP_THREAD(SendKeyIdNotUsable, - nsAutoCString(aSessionId, aSessionIdLength), kid); + CALL_ON_GMP_THREAD(SendKeyStatusChanged, + nsAutoCString(aSessionId, aSessionIdLength), kid, + aStatus); } void diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPDecryptorChild.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPDecryptorChild.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPDecryptorChild.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPDecryptorChild.h 2015-02-03 14:33:41.000000000 +0000 @@ -63,15 +63,11 @@ const char* aMessage, uint32_t aMessageLength) MOZ_OVERRIDE; - virtual void KeyIdUsable(const char* aSessionId, - uint32_t aSessionIdLength, - const uint8_t* aKeyId, - uint32_t aKeyIdLength) MOZ_OVERRIDE; - - virtual void KeyIdNotUsable(const char* aSessionId, - uint32_t aSessionIdLength, - const uint8_t* aKeyId, - uint32_t aKeyIdLength) MOZ_OVERRIDE; + virtual void KeyStatusChanged(const char* aSessionId, + uint32_t aSessionIdLength, + const uint8_t* aKeyId, + uint32_t aKeyIdLength, + GMPMediaKeyStatus aStatus) MOZ_OVERRIDE; virtual void SetCapabilities(uint64_t aCaps) MOZ_OVERRIDE; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPDecryptorParent.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPDecryptorParent.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPDecryptorParent.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPDecryptorParent.cpp 2015-02-03 14:33:41.000000000 +0000 @@ -265,26 +265,15 @@ } bool -GMPDecryptorParent::RecvKeyIdUsable(const nsCString& aSessionId, - InfallibleTArray&& aKeyId) +GMPDecryptorParent::RecvKeyStatusChanged(const nsCString& aSessionId, + InfallibleTArray&& aKeyId, + const GMPMediaKeyStatus& aStatus) { if (!mIsOpen) { NS_WARNING("Trying to use a dead GMP decrypter!"); return false; } - mCallback->KeyIdUsable(aSessionId, aKeyId); - return true; -} - -bool -GMPDecryptorParent::RecvKeyIdNotUsable(const nsCString& aSessionId, - InfallibleTArray&& aKeyId) -{ - if (!mIsOpen) { - NS_WARNING("Trying to use a dead GMP decrypter!"); - return false; - } - mCallback->KeyIdNotUsable(aSessionId, aKeyId); + mCallback->KeyStatusChanged(aSessionId, aKeyId, aStatus); return true; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPDecryptorParent.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPDecryptorParent.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPDecryptorParent.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPDecryptorParent.h 2015-02-03 14:33:41.000000000 +0000 @@ -91,11 +91,9 @@ const uint32_t& aSystemCode, const nsCString& aMessage) MOZ_OVERRIDE; - virtual bool RecvKeyIdUsable(const nsCString& aSessionId, - InfallibleTArray&& aKeyId) MOZ_OVERRIDE; - - virtual bool RecvKeyIdNotUsable(const nsCString& aSessionId, - InfallibleTArray&& aKeyId) MOZ_OVERRIDE; + virtual bool RecvKeyStatusChanged(const nsCString& aSessionId, + InfallibleTArray&& aKeyId, + const GMPMediaKeyStatus& aStatus) MOZ_OVERRIDE; virtual bool RecvDecrypted(const uint32_t& aId, const GMPErr& aErr, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPDecryptorProxy.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPDecryptorProxy.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPDecryptorProxy.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPDecryptorProxy.h 2015-02-03 14:33:41.000000000 +0000 @@ -44,11 +44,9 @@ uint32_t aSystemCode, const nsCString& aMessage) = 0; - virtual void KeyIdUsable(const nsCString& aSessionId, - const nsTArray& aKeyId) = 0; - - virtual void KeyIdNotUsable(const nsCString& aSessionId, - const nsTArray& aKeyId) = 0; + virtual void KeyStatusChanged(const nsCString& aSessionId, + const nsTArray& aKeyId, + GMPMediaKeyStatus aStatus) = 0; virtual void SetCaps(uint64_t aCaps) = 0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPLoader.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPLoader.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPLoader.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPLoader.cpp 2015-02-03 14:33:41.000000000 +0000 @@ -14,6 +14,7 @@ #if defined(XP_WIN) && defined(MOZ_SANDBOX) #include "mozilla/sandboxTarget.h" +#include "mozilla/Scoped.h" #include "windows.h" #include #include @@ -33,6 +34,31 @@ #include "sha256.h" #endif +#if defined(XP_WIN) && defined(MOZ_SANDBOX) +namespace { + +// Scoped type used by Load +struct ScopedActCtxHandleTraits +{ + typedef HANDLE type; + + static type empty() + { + return INVALID_HANDLE_VALUE; + } + + static void release(type aActCtxHandle) + { + if (aActCtxHandle != INVALID_HANDLE_VALUE) { + ReleaseActCtx(aActCtxHandle); + } + } +}; +typedef mozilla::Scoped ScopedActCtxHandle; + +} // anonymous namespace +#endif + namespace mozilla { namespace gmp { @@ -168,6 +194,30 @@ nodeId = std::string(aOriginSalt, aOriginSalt + aOriginSaltLen); } +#if defined(XP_WIN) && defined(MOZ_SANDBOX) + // If the GMP DLL is a side-by-side assembly with static imports then the DLL + // loader will attempt to create an activation context which will fail because + // of the sandbox. If we create an activation context before we start the + // sandbox then this one will get picked up by the DLL loader. + int pathLen = MultiByteToWideChar(CP_ACP, 0, aLibPath, -1, nullptr, 0); + if (pathLen == 0) { + return false; + } + + wchar_t* widePath = new wchar_t[pathLen]; + if (MultiByteToWideChar(CP_ACP, 0, aLibPath, -1, widePath, pathLen) == 0) { + delete[] widePath; + return false; + } + + ACTCTX actCtx = { sizeof(actCtx) }; + actCtx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID; + actCtx.lpSource = widePath; + actCtx.lpResourceName = ISOLATIONAWARE_MANIFEST_RESOURCE_ID; + ScopedActCtxHandle actCtxHandle(CreateActCtx(&actCtx)); + delete[] widePath; +#endif + // Start the sandbox now that we've generated the device bound node id. // This must happen after the node id is bound to the device id, as // generating the device id requires privileges. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPMessageUtils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPMessageUtils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPMessageUtils.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPMessageUtils.h 2015-02-03 14:33:41.000000000 +0000 @@ -61,6 +61,13 @@ {}; template <> +struct ParamTraits +: public ContiguousEnumSerializer +{}; + +template <> struct ParamTraits : public ContiguousEnumSerializer gmp = task->GetParent(); - rv = gmp->Init(this, directory); + rv = gmp ? gmp->Init(this, directory) : NS_ERROR_NOT_AVAILABLE; if (NS_FAILED(rv)) { NS_WARNING("Can't Create GMPParent"); return; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPVideoDecoderParent.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPVideoDecoderParent.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/GMPVideoDecoderParent.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/GMPVideoDecoderParent.cpp 2015-02-03 14:33:41.000000000 +0000 @@ -199,7 +199,6 @@ mCallback->Terminated(); mCallback = nullptr; } - mVideoHost.DoneWithAPI(); mIsOpen = false; unused << SendDecodingComplete(); @@ -212,6 +211,8 @@ GMPVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy) { mIsOpen = false; + mVideoHost.DoneWithAPI(); + if (mCallback) { // May call Close() (and Shutdown()) immediately or with a delay mCallback->Terminated(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/PGMPDecryptor.ipdl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/PGMPDecryptor.ipdl --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp/PGMPDecryptor.ipdl 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp/PGMPDecryptor.ipdl 2015-02-03 14:33:41.000000000 +0000 @@ -7,6 +7,7 @@ include GMPTypes; using GMPSessionMessageType from "gmp-decryption.h"; +using GMPMediaKeyStatus from "gmp-decryption.h"; using GMPSessionType from "gmp-decryption.h"; using GMPDOMException from "gmp-decryption.h"; using GMPErr from "gmp-errors.h"; @@ -77,12 +78,11 @@ uint32_t aSystemCode, nsCString aMessage); - KeyIdUsable(nsCString aSessionId, uint8_t[] aKey); + KeyStatusChanged(nsCString aSessionId, uint8_t[] aKey, + GMPMediaKeyStatus aStatus); SetCaps(uint64_t aCaps); - KeyIdNotUsable(nsCString aSessionId,uint8_t[] aKey); - Decrypted(uint32_t aId, GMPErr aResult, uint8_t[] aBuffer); }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp-plugin/fake.info thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp-plugin/fake.info --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp-plugin/fake.info 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp-plugin/fake.info 2015-02-03 14:33:41.000000000 +0000 @@ -1,5 +1,5 @@ Name: fake Description: Fake GMP Plugin Version: 1.0 -APIs: encode-video[h264], decode-video[h264], eme-decrypt-v3[fake] +APIs: encode-video[h264], decode-video[h264], eme-decrypt-v6[fake] Libraries: dxva2.dll diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp-plugin/gmp-fake.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp-plugin/gmp-fake.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gmp-plugin/gmp-fake.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gmp-plugin/gmp-fake.cpp 2015-02-03 14:33:41.000000000 +0000 @@ -60,6 +60,8 @@ #define PUBLIC_FUNC #endif +#define BIG_FRAME 10000 + static int g_log_level = 0; #define GMPLOG(l, x) do { \ @@ -166,7 +168,7 @@ // Now return the encoded data back to the parent. GMPVideoFrame* ftmp; - GMPErr err = host_->CreateFrame (kGMPEncodedVideoFrame, &ftmp); + GMPErr err = host_->CreateFrame(kGMPEncodedVideoFrame, &ftmp); if (err != GMPNoErr) { GMPLOG (GL_ERROR, "Error creating encoded frame"); return; @@ -192,13 +194,17 @@ eframe.timestamp_ = inputImage->Timestamp(); - err = f->CreateEmptyFrame (sizeof(eframe)); + err = f->CreateEmptyFrame (sizeof(eframe) + + (frame_type == kGMPKeyFrame ? sizeof(uint32_t) + BIG_FRAME : 0)); if (err != GMPNoErr) { GMPLOG (GL_ERROR, "Error allocating frame data"); f->Destroy(); return; } memcpy(f->Buffer(), &eframe, sizeof(eframe)); + if (frame_type == kGMPKeyFrame) { + *((uint32_t*) f->Buffer() + sizeof(eframe)) = BIG_FRAME; + } f->SetEncodedWidth (inputImage->Width()); f->SetEncodedHeight (inputImage->Height()); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gstreamer/GStreamerReader.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gstreamer/GStreamerReader.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gstreamer/GStreamerReader.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gstreamer/GStreamerReader.h 2015-02-03 14:33:42.000000000 +0000 @@ -41,26 +41,26 @@ explicit GStreamerReader(AbstractMediaDecoder* aDecoder); virtual ~GStreamerReader(); - virtual nsresult Init(MediaDecoderReader* aCloneDonor); - virtual nsresult ResetDecode(); - virtual bool DecodeAudioData(); + virtual nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE; + virtual nsresult ResetDecode() MOZ_OVERRIDE; + virtual bool DecodeAudioData() MOZ_OVERRIDE; virtual bool DecodeVideoFrame(bool &aKeyframeSkip, - int64_t aTimeThreshold); + int64_t aTimeThreshold) MOZ_OVERRIDE; virtual nsresult ReadMetadata(MediaInfo* aInfo, - MetadataTags** aTags); + MetadataTags** aTags) MOZ_OVERRIDE; virtual nsRefPtr Seek(int64_t aTime, int64_t aEndTime) MOZ_OVERRIDE; - virtual nsresult GetBuffered(dom::TimeRanges* aBuffered); + virtual nsresult GetBuffered(dom::TimeRanges* aBuffered) MOZ_OVERRIDE; virtual void NotifyDataArrived(const char *aBuffer, uint32_t aLength, int64_t aOffset) MOZ_OVERRIDE; - virtual bool HasAudio() { + virtual bool HasAudio() MOZ_OVERRIDE { return mInfo.HasAudio(); } - virtual bool HasVideo() { + virtual bool HasVideo() MOZ_OVERRIDE { return mInfo.HasVideo(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gtest/MockMediaResource.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gtest/MockMediaResource.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gtest/MockMediaResource.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gtest/MockMediaResource.cpp 2015-02-03 14:33:42.000000000 +0000 @@ -60,7 +60,8 @@ int64_t MockMediaResource::GetLength() { - return fseek(mFileHandle, 0, SEEK_END); + fseek(mFileHandle, 0, SEEK_END); + return ftell(mFileHandle); } void diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gtest/TestGMPCrossOrigin.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gtest/TestGMPCrossOrigin.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gtest/TestGMPCrossOrigin.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gtest/TestGMPCrossOrigin.cpp 2015-02-03 14:33:42.000000000 +0000 @@ -1116,10 +1116,9 @@ nsresult aException, uint32_t aSystemCode, const nsCString& aMessage) MOZ_OVERRIDE {} - virtual void KeyIdUsable(const nsCString& aSessionId, - const nsTArray& aKeyId) MOZ_OVERRIDE { } - virtual void KeyIdNotUsable(const nsCString& aSessionId, - const nsTArray& aKeyId) MOZ_OVERRIDE {} + virtual void KeyStatusChanged(const nsCString& aSessionId, + const nsTArray& aKeyId, + GMPMediaKeyStatus aStatus) MOZ_OVERRIDE { } virtual void SetCaps(uint64_t aCaps) MOZ_OVERRIDE {} virtual void Decrypted(uint32_t aId, GMPErr aResult, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gtest/TestMP4Demuxer.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gtest/TestMP4Demuxer.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/gtest/TestMP4Demuxer.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/gtest/TestMP4Demuxer.cpp 2015-02-03 14:33:42.000000000 +0000 @@ -331,3 +331,34 @@ } EXPECT_EQ(ArrayLength(audio), i); } + +TEST(MP4Demuxer, GetNextKeyframe) +{ + nsRefPtr b = new MP4DemuxerBinding("gizmo-frag.mp4"); + MonitorAutoLock mon(b->mMonitor); + MP4Demuxer* d = b->demuxer; + + EXPECT_TRUE(d->Init()); + + // Insert a [0,end] buffered range, to simulate Moof's being buffered + // via MSE. + auto len = b->resource->GetLength(); + b->resource->MockAddBufferedRange(0, len); + + // Rebuild the index so that it can be used to find the keyframes. + nsTArray ranges; + EXPECT_TRUE(NS_SUCCEEDED(b->resource->GetCachedRanges(ranges))); + d->UpdateIndex(ranges); + + // gizmp-frag has two keyframes; one at dts=cts=0, and another at + // dts=cts=1000000. Verify we get expected results. + + MP4Sample* sample; + size_t i = 0; + const int64_t keyframe = 1000000; + while (!!(sample = d->DemuxVideoSample())) { + int64_t expected = (sample->decode_timestamp < keyframe) ? keyframe : -1; + EXPECT_EQ(d->GetNextKeyframeTime(), expected); + i++; + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaData.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaData.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaData.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaData.h 2015-02-03 14:33:40.000000000 +0000 @@ -12,6 +12,8 @@ #include "AudioSampleFormat.h" #include "nsIMemoryReporter.h" #include "SharedBuffer.h" +#include "nsRefPtr.h" +#include "nsTArray.h" namespace mozilla { @@ -272,6 +274,15 @@ ~VideoData(); }; + // LargeDataBuffer is a ref counted fallible TArray. + // It is designed to share potentially big byte arrays. +class LargeDataBuffer : public FallibleTArray { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LargeDataBuffer); + +private: + ~LargeDataBuffer() {} +}; + } // namespace mozilla #endif // MediaData_h diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaDecoder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaDecoder.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaDecoder.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaDecoder.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -36,6 +36,9 @@ using namespace mozilla::layers; using namespace mozilla::dom; +// Default timeout msecs until try to enter dormant state by heuristic. +static const int DEFAULT_HEURISTIC_DORMANT_TIMEOUT_MSECS = 60000; + namespace mozilla { // Number of estimated seconds worth of data we need to have buffered @@ -123,27 +126,56 @@ MOZ_ASSERT(NS_IsMainThread()); ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - if (!mDecoderStateMachine || - !mDecoderStateMachine->IsDormantNeeded() || - mPlayState == PLAY_STATE_SHUTDOWN) { + if (!mOwner) { + NS_WARNING("MediaDecoder without a decoder owner, can't update dormant"); return; } - if (!mOwner) { - NS_WARNING("MediaDecoder without a decoder owner, can't update dormant"); + UpdateDormantState(false /* aDormantTimeout */, false /* aActivity */); + // Start dormant timer if necessary + StartDormantTimer(); +} + +void MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity) +{ + MOZ_ASSERT(NS_IsMainThread()); + GetReentrantMonitor().AssertCurrentThreadIn(); + + if (!mDecoderStateMachine || + mPlayState == PLAY_STATE_SHUTDOWN || + !mOwner->GetVideoFrameContainer() || + !mDecoderStateMachine->IsDormantNeeded()) + { return; } bool prevDormant = mIsDormant; mIsDormant = false; - if (!mOwner->IsActive() && mOwner->GetVideoFrameContainer()) { + if (!mOwner->IsActive()) { mIsDormant = true; } #ifdef MOZ_WIDGET_GONK - if (mOwner->IsHidden() && mOwner->GetVideoFrameContainer()) { + if (mOwner->IsHidden()) { mIsDormant = true; } #endif + // Try to enable dormant by idle heuristic, when the owner is hidden. + bool prevHeuristicDormant = mIsHeuristicDormant; + mIsHeuristicDormant = false; + if (mIsHeuristicDormantSupported && mOwner->IsHidden()) { + if (aDormantTimeout && !aActivity && + (mPlayState == PLAY_STATE_PAUSED || mPlayState == PLAY_STATE_ENDED)) { + // Enable heuristic dormant + mIsHeuristicDormant = true; + } else if(prevHeuristicDormant && !aActivity) { + // Continue heuristic dormant + mIsHeuristicDormant = true; + } + + if (mIsHeuristicDormant) { + mIsDormant = true; + } + } if (prevDormant == mIsDormant) { // No update to dormant state @@ -167,6 +199,47 @@ } } +void MediaDecoder::DormantTimerExpired(nsITimer* aTimer, void* aClosure) +{ + MOZ_ASSERT(aClosure); + MediaDecoder* decoder = static_cast(aClosure); + ReentrantMonitorAutoEnter mon(decoder->GetReentrantMonitor()); + decoder->UpdateDormantState(true /* aDormantTimeout */, + false /* aActivity */); +} + +void MediaDecoder::StartDormantTimer() +{ + if (!mIsHeuristicDormantSupported) { + return; + } + + if (mIsHeuristicDormant || + mShuttingDown || + !mOwner || + !mOwner->IsHidden() || + (mPlayState != PLAY_STATE_PAUSED && + mPlayState != PLAY_STATE_ENDED)) + { + return; + } + + if (!mDormantTimer) { + mDormantTimer = do_CreateInstance("@mozilla.org/timer;1"); + } + mDormantTimer->InitWithFuncCallback(&MediaDecoder::DormantTimerExpired, + this, + mHeuristicDormantTimeout, + nsITimer::TYPE_ONE_SHOT); +} + +void MediaDecoder::CancelDormantTimer() +{ + if (mDormantTimer) { + mDormantTimer->Cancel(); + } +} + void MediaDecoder::Pause() { MOZ_ASSERT(NS_IsMainThread()); @@ -190,15 +263,6 @@ } } -void MediaDecoder::SetAudioCaptured(bool aCaptured) -{ - MOZ_ASSERT(NS_IsMainThread()); - mInitialAudioCaptured = aCaptured; - if (mDecoderStateMachine) { - mDecoderStateMachine->SetAudioCaptured(aCaptured); - } -} - void MediaDecoder::ConnectDecodedStreamToOutputStream(OutputStreamData* aStream) { NS_ASSERTION(!aStream->mPort, "Already connected?"); @@ -215,11 +279,10 @@ MediaDecoder::DecodedStreamData::DecodedStreamData(MediaDecoder* aDecoder, int64_t aInitialTime, SourceMediaStream* aStream) - : mLastAudioPacketTime(-1), - mLastAudioPacketEndTime(-1), - mAudioFramesWritten(0), + : mAudioFramesWritten(0), mInitialTime(aInitialTime), - mNextVideoTime(aInitialTime), + mNextVideoTime(-1), + mNextAudioTime(-1), mDecoder(aDecoder), mStreamInitialized(false), mHaveSentFinish(false), @@ -287,13 +350,6 @@ } } -void MediaDecoder::RecreateDecodedStreamIfNecessary(int64_t aStartTimeUSecs) -{ - if (mInitialAudioCaptured) { - RecreateDecodedStream(aStartTimeUSecs); - } -} - void MediaDecoder::DestroyDecodedStream() { MOZ_ASSERT(NS_IsMainThread()); @@ -397,9 +453,13 @@ { ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - if (!mDecodedStream) { - RecreateDecodedStream(mDecoderStateMachine ? - int64_t(mDecoderStateMachine->GetCurrentTime()*USECS_PER_S) : 0); + if (mDecoderStateMachine) { + mDecoderStateMachine->SetAudioCaptured(); + } + if (!GetDecodedStream()) { + int64_t t = mDecoderStateMachine ? + mDecoderStateMachine->GetCurrentTimeUs() : 0; + RecreateDecodedStream(t); } OutputStreamData* os = mOutputStreams.AppendElement(); os->Init(aStream, aFinishWhenEnded); @@ -472,7 +532,13 @@ mPausedForPlaybackRateNull(false), mMinimizePreroll(false), mMediaTracksConstructed(false), - mIsDormant(false) + mIsDormant(false), + mIsHeuristicDormantSupported( + Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false)), + mHeuristicDormantTimeout( + Preferences::GetInt("media.decoder.heuristic.dormant.timeout", + DEFAULT_HEURISTIC_DORMANT_TIMEOUT_MSECS)), + mIsHeuristicDormant(false) { MOZ_COUNT_CTOR(MediaDecoder); MOZ_ASSERT(NS_IsMainThread()); @@ -521,6 +587,8 @@ mResource->Close(); } + CancelDormantTimer(); + ChangeState(PLAY_STATE_SHUTDOWN); mOwner = nullptr; @@ -591,7 +659,9 @@ ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); mDecoderStateMachine->SetDuration(mDuration); mDecoderStateMachine->SetVolume(mInitialVolume); - mDecoderStateMachine->SetAudioCaptured(mInitialAudioCaptured); + if (GetDecodedStream()) { + mDecoderStateMachine->SetAudioCaptured(); + } SetPlaybackRate(mInitialPlaybackRate); mDecoderStateMachine->SetPreservesPitch(mInitialPreservesPitch); if (mMinimizePreroll) { @@ -622,6 +692,8 @@ { MOZ_ASSERT(NS_IsMainThread()); ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); + UpdateDormantState(false /* aDormantTimeout */, true /* aActivity */); + NS_ASSERTION(mDecoderStateMachine != nullptr, "Should have state machine."); if (mPausedForPlaybackRateNull) { return NS_OK; @@ -644,6 +716,7 @@ { MOZ_ASSERT(NS_IsMainThread()); ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); + UpdateDormantState(false /* aDormantTimeout */, true /* aActivity */); NS_ABORT_IF_FALSE(aTime >= 0.0, "Cannot seek to a negative value."); @@ -1192,6 +1265,10 @@ ApplyStateToStateMachine(mPlayState); + CancelDormantTimer(); + // Start dormant timer if necessary + StartDormantTimer(); + GetReentrantMonitor().NotifyAll(); } @@ -1210,7 +1287,9 @@ mRequestedSeekTarget.Reset(); break; default: - /* No action needed */ + // The state machine checks for things like PAUSED in RunStateMachine. + // Make sure to keep it in the loop. + ScheduleStateMachineThread(); break; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaDecoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaDecoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaDecoder.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaDecoder.h 2015-02-03 14:33:40.000000000 +0000 @@ -188,6 +188,7 @@ #include "nsCOMPtr.h" #include "nsIObserver.h" #include "nsAutoPtr.h" +#include "nsITimer.h" #include "MediaResource.h" #include "mozilla/dom/AudioChannelBinding.h" #include "mozilla/gfx/Rect.h" @@ -367,13 +368,12 @@ // It is used to share scarece media resources in system. virtual void NotifyOwnerActivityChanged(); + void UpdateDormantState(bool aDormantTimeout, bool aActivity); + // Pause video playback. virtual void Pause(); // Adjust the speed of the playback, optionally with pitch correction, virtual void SetVolume(double aVolume); - // Sets whether audio is being captured. If it is, we won't play any - // of our audio. - virtual void SetAudioCaptured(bool aCaptured); virtual void NotifyWaitingForResourcesStatusChanged() MOZ_OVERRIDE; @@ -405,8 +405,6 @@ // The following group of fields are protected by the decoder's monitor // and can be read or written on any thread. - int64_t mLastAudioPacketTime; // microseconds - int64_t mLastAudioPacketEndTime; // microseconds // Count of audio frames written to the stream int64_t mAudioFramesWritten; // Saved value of aInitialTime. Timestamp of the first audio and/or @@ -416,6 +414,7 @@ // Therefore video packets starting at or after this time need to be copied // to the output stream. int64_t mNextVideoTime; // microseconds + int64_t mNextAudioTime; // microseconds MediaDecoder* mDecoder; // The last video image sent to the stream. Useful if we need to replicate // the image. @@ -854,9 +853,6 @@ // The decoder monitor must be held. bool IsLogicallyPlaying(); - // Re-create a decoded stream if audio being captured - void RecreateDecodedStreamIfNecessary(int64_t aStartTimeUSecs); - #ifdef MOZ_EME // This takes the decoder monitor. virtual nsresult SetCDMProxy(CDMProxy* aProxy) MOZ_OVERRIDE; @@ -1022,6 +1018,14 @@ virtual ~MediaDecoder(); void SetStateMachineParameters(); + static void DormantTimerExpired(nsITimer *aTimer, void *aClosure); + + // Start a timer for heuristic dormant. + void StartDormantTimer(); + + // Cancel a timer for heuristic dormant. + void CancelDormantTimer(); + /****** * The following members should be accessed with the decoder lock held. ******/ @@ -1057,9 +1061,6 @@ // only. int64_t mDuration; - // True when playback should start with audio captured (not playing). - bool mInitialAudioCaptured; - // True if the media is seekable (i.e. supports random access). bool mMediaSeekable; @@ -1219,6 +1220,18 @@ // True if MediaDecoder is in dormant state. bool mIsDormant; + + // True if heuristic dormant is supported. + const bool mIsHeuristicDormantSupported; + + // Timeout ms of heuristic dormant timer. + const int mHeuristicDormantTimeout; + + // True if MediaDecoder is in dormant by heuristic. + bool mIsHeuristicDormant; + + // Timer to schedule updating dormant state. + nsCOMPtr mDormantTimer; }; } // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaDecoderReader.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaDecoderReader.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaDecoderReader.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaDecoderReader.h 2015-02-03 14:33:40.000000000 +0000 @@ -13,8 +13,6 @@ #include "MediaQueue.h" #include "AudioCompactor.h" -#include "mozilla/TypedEnum.h" - namespace mozilla { namespace dom { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaDecoderStateMachine.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaDecoderStateMachine.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaDecoderStateMachine.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaDecoderStateMachine.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -191,6 +191,12 @@ return static_cast(aDuration.ToSeconds() * USECS_PER_S); } +static const uint32_t MIN_VIDEO_QUEUE_SIZE = 3; +static const uint32_t MAX_VIDEO_QUEUE_SIZE = 10; + +static uint32_t sVideoQueueDefaultSize = MAX_VIDEO_QUEUE_SIZE; +static uint32_t sVideoQueueHWAccelSize = MIN_VIDEO_QUEUE_SIZE; + MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, MediaDecoderReader* aReader, bool aRealTime) : @@ -216,7 +222,7 @@ mVolume(1.0), mPlaybackRate(1.0), mPreservesPitch(true), - mAmpleVideoFrames(2), + mAmpleVideoFrames(MIN_VIDEO_QUEUE_SIZE), mLowAudioThresholdUsecs(detail::LOW_AUDIO_USECS), mAmpleAudioThresholdUsecs(detail::AMPLE_AUDIO_USECS), mQuickBufferingLowDataThresholdUsecs(detail::QUICK_BUFFERING_LOW_DATA_USECS), @@ -247,8 +253,16 @@ MOZ_COUNT_CTOR(MediaDecoderStateMachine); NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); - mAmpleVideoFrames = - std::max(Preferences::GetUint("media.video-queue.default-size", 10), 3); + static bool sPrefCacheInit = false; + if (!sPrefCacheInit) { + sPrefCacheInit = true; + Preferences::AddUintVarCache(&sVideoQueueDefaultSize, + "media.video-queue.default-size", + MAX_VIDEO_QUEUE_SIZE); + Preferences::AddUintVarCache(&sVideoQueueHWAccelSize, + "media.video-queue.hw-accel-size", + MIN_VIDEO_QUEUE_SIZE); + } mBufferingWait = IsRealTime() ? 0 : 30; mLowDataThresholdUsecs = IsRealTime() ? 0 : detail::LOW_DATA_THRESHOLD_USECS; @@ -312,48 +326,39 @@ DecodedStreamData* aStream, AudioSegment* aOutput) { - NS_ASSERTION(OnDecodeThread() || - OnStateMachineThread(), "Should be on decode thread or state machine thread"); + NS_ASSERTION(OnDecodeThread() || OnStateMachineThread(), + "Should be on decode thread or state machine thread"); AssertCurrentThreadInMonitor(); - if (aAudio->mTime <= aStream->mLastAudioPacketTime) { - // ignore packet that we've already processed - return; - } - aStream->mLastAudioPacketTime = aAudio->mTime; - aStream->mLastAudioPacketEndTime = aAudio->GetEndTime(); - // This logic has to mimic AudioSink closely to make sure we write // the exact same silences - CheckedInt64 audioWrittenOffset = UsecsToFrames(mInfo.mAudio.mRate, - aStream->mInitialTime + mStartTime) + aStream->mAudioFramesWritten; + CheckedInt64 audioWrittenOffset = aStream->mAudioFramesWritten + + UsecsToFrames(mInfo.mAudio.mRate, aStream->mInitialTime + mStartTime); CheckedInt64 frameOffset = UsecsToFrames(mInfo.mAudio.mRate, aAudio->mTime); - if (!audioWrittenOffset.isValid() || !frameOffset.isValid()) + + if (!audioWrittenOffset.isValid() || + !frameOffset.isValid() || + // ignore packet that we've already processed + frameOffset.value() + aAudio->mFrames <= audioWrittenOffset.value()) { return; + } + if (audioWrittenOffset.value() < frameOffset.value()) { + int64_t silentFrames = frameOffset.value() - audioWrittenOffset.value(); // Write silence to catch up - VERBOSE_LOG("writing %d frames of silence to MediaStream", - int32_t(frameOffset.value() - audioWrittenOffset.value())); + VERBOSE_LOG("writing %lld frames of silence to MediaStream", silentFrames); AudioSegment silence; - silence.InsertNullDataAtStart(frameOffset.value() - audioWrittenOffset.value()); - aStream->mAudioFramesWritten += silence.GetDuration(); + StreamTime duration = aStream->mStream->TicksToTimeRoundDown( + mInfo.mAudio.mRate, silentFrames); + silence.InsertNullDataAtStart(duration); + aStream->mAudioFramesWritten += silentFrames; + audioWrittenOffset += silentFrames; aOutput->AppendFrom(&silence); } - int64_t offset; - if (aStream->mAudioFramesWritten == 0) { - NS_ASSERTION(frameOffset.value() <= audioWrittenOffset.value(), - "Otherwise we'd have taken the write-silence path"); - // We're starting in the middle of a packet. Split the packet. - offset = audioWrittenOffset.value() - frameOffset.value(); - } else { - // Write the entire packet. - offset = 0; - } - - if (offset >= aAudio->mFrames) - return; + MOZ_ASSERT(audioWrittenOffset.value() >= frameOffset.value()); + int64_t offset = audioWrittenOffset.value() - frameOffset.value(); size_t framesToWrite = aAudio->mFrames - offset; aAudio->EnsureAudioBuffer(); @@ -369,6 +374,8 @@ aAudio->mTime); aStream->mAudioFramesWritten += framesToWrite; aOutput->ApplyVolume(mVolume); + + aStream->mNextAudioTime = aAudio->GetEndTime(); } static void WriteVideoToMediaStream(MediaStream* aStream, @@ -387,29 +394,12 @@ void MediaDecoderStateMachine::SendStreamData() { - NS_ASSERTION(OnDecodeThread() || OnStateMachineThread(), - "Should be on decode thread or state machine thread"); + MOZ_ASSERT(OnStateMachineThread(), "Should be on state machine thread"); AssertCurrentThreadInMonitor(); - MOZ_ASSERT(mState != DECODER_STATE_DECODING_NONE); + MOZ_ASSERT(!mAudioSink, "Should've been stopped in CallRunStateMachine()"); DecodedStreamData* stream = mDecoder->GetDecodedStream(); - if (!stream) { - return; - } - - if (mState == DECODER_STATE_DECODING_METADATA || - mState == DECODER_STATE_DECODING_FIRSTFRAME) { - return; - } - // If there's still an audio sink alive, then we can't send any stream - // data yet since both SendStreamData and the audio sink want to be in - // charge of popping the audio queue. We're waiting for the audio sink - if (mAudioSink) { - return; - } - - int64_t minLastAudioPacketTime = INT64_MAX; bool finished = (!mInfo.HasAudio() || AudioQueue().IsFinished()) && (!mInfo.HasVideo() || VideoQueue().IsFinished()); @@ -423,25 +413,36 @@ mediaStream->AddAudioTrack(kAudioTrack, mInfo.mAudio.mRate, 0, audio); stream->mStream->DispatchWhenNotEnoughBuffered(kAudioTrack, GetStateMachineThread(), GetWakeDecoderRunnable()); + stream->mNextAudioTime = mStartTime + stream->mInitialTime; } if (mInfo.HasVideo()) { VideoSegment* video = new VideoSegment(); mediaStream->AddTrack(kVideoTrack, 0, video); stream->mStream->DispatchWhenNotEnoughBuffered(kVideoTrack, GetStateMachineThread(), GetWakeDecoderRunnable()); + + // TODO: We can't initialize |mNextVideoTime| until |mStartTime| + // is set. This is a good indication that DecodedStreamData is in + // deep coupling with the state machine and we should move the class + // into MediaDecoderStateMachine. + stream->mNextVideoTime = mStartTime + stream->mInitialTime; } stream->mStreamInitialized = true; } if (mInfo.HasAudio()) { + MOZ_ASSERT(stream->mNextAudioTime != -1, "Should've been initialized"); nsAutoTArray,10> audio; - // It's OK to hold references to the AudioData because while audio - // is captured, only the decoder thread pops from the queue (see below). - AudioQueue().GetElementsAfter(stream->mLastAudioPacketTime, &audio); + // It's OK to hold references to the AudioData because AudioData + // is ref-counted. + AudioQueue().GetElementsAfter(stream->mNextAudioTime, &audio); AudioSegment output; for (uint32_t i = 0; i < audio.Length(); ++i) { SendStreamAudio(audio[i], stream, &output); } + // |mNextAudioTime| is updated as we process each audio sample in + // SendStreamAudio(). This is consistent with how |mNextVideoTime| + // is updated for video samples. if (output.GetDuration() > 0) { mediaStream->AppendToTrack(kAudioTrack, &output); } @@ -449,16 +450,16 @@ mediaStream->EndTrack(kAudioTrack); stream->mHaveSentFinishAudio = true; } - minLastAudioPacketTime = std::min(minLastAudioPacketTime, stream->mLastAudioPacketTime); endPosition = std::max(endPosition, mediaStream->TicksToTimeRoundDown(mInfo.mAudio.mRate, stream->mAudioFramesWritten)); } if (mInfo.HasVideo()) { + MOZ_ASSERT(stream->mNextVideoTime != -1, "Should've been initialized"); nsAutoTArray,10> video; - // It's OK to hold references to the VideoData only the decoder thread - // pops from the queue. + // It's OK to hold references to the VideoData because VideoData + // is ref-counted. VideoQueue().GetElementsAfter(stream->mNextVideoTime, &video); VideoSegment output; for (uint32_t i = 0; i < video.Length(); ++i) { @@ -468,6 +469,13 @@ mediaStream, v->mTime - stream->mNextVideoTime); // Write last video frame to catch up. mLastVideoImage can be null here // which is fine, it just means there's no video. + + // TODO: |mLastVideoImage| should come from the last image rendered + // by the state machine. This will avoid the black frame when capture + // happens in the middle of playback (especially in th middle of a + // video frame). E.g. if we have a video frame that is 30 sec long + // and capture happens at 15 sec, we'll have to append a black frame + // that is 15 sec long. WriteVideoToMediaStream(mediaStream, stream->mLastVideoImage, v->mTime, stream->mNextVideoTime, stream->mLastVideoImageDisplaySize, &output); @@ -509,26 +517,26 @@ } } - if (mAudioCaptured) { - // Discard audio packets that are no longer needed. - while (true) { - const AudioData* a = AudioQueue().PeekFront(); - // Packet times are not 100% reliable so this may discard packets that - // actually contain data for mCurrentFrameTime. This means if someone might - // create a new output stream and we actually don't have the audio for the - // very start. That's OK, we'll play silence instead for a brief moment. - // That's OK. Seeking to this time would have a similar issue for such - // badly muxed resources. - if (!a || a->GetEndTime() >= minLastAudioPacketTime) - break; + const auto clockTime = GetClock(); + while (true) { + const AudioData* a = AudioQueue().PeekFront(); + // If we discard audio samples fed to the stream immediately, we will + // keep decoding audio samples till the end and consume a lot of memory. + // Therefore we only discard those behind the stream clock to throttle + // the decoding speed. + if (a && a->mTime <= clockTime) { OnAudioEndTimeUpdate(std::max(mAudioEndTime, a->GetEndTime())); nsRefPtr releaseMe = AudioQueue().PopFront(); + continue; } + break; + } - if (finished) { - mAudioCompleted = true; - UpdateReadyState(); - } + // To be consistent with AudioSink, |mAudioCompleted| is not set + // until all samples are drained. + if (finished && AudioQueue().GetSize() == 0) { + mAudioCompleted = true; + UpdateReadyState(); } } @@ -557,12 +565,6 @@ DecodedStreamData* stream = mDecoder->GetDecodedStream(); - // Since stream is initialized in SendStreamData() which is called when - // audio/video samples are received, we need to keep decoding to ensure - // the stream is initialized. - if (stream && !stream->mStreamInitialized) { - return false; - } if (stream && stream->mStreamInitialized && !stream->mHaveSentFinishAudio) { if (!stream->mStream->HaveEnoughBuffered(kAudioTrack)) { return false; @@ -584,10 +586,6 @@ DecodedStreamData* stream = mDecoder->GetDecodedStream(); - if (stream && !stream->mStreamInitialized) { - return false; - } - if (stream && stream->mStreamInitialized && !stream->mHaveSentFinishVideo) { if (!stream->mStream->HaveEnoughBuffered(kVideoTrack)) { return false; @@ -799,6 +797,10 @@ if (mIsAudioPrerolling && DonePrerollingAudio()) { StopPrerollingAudio(); } + // Schedule the state machine to send stream data as soon as possible. + if (mAudioCaptured) { + ScheduleStateMachine(); + } return; } @@ -855,7 +857,6 @@ // to reach playing. AudioQueue().Push(aSample); if (mState > DECODER_STATE_DECODING_FIRSTFRAME) { - SendStreamData(); // The ready state can change when we've decoded data, so update the // ready state, so that DOM events can fire. UpdateReadyState(); @@ -873,7 +874,6 @@ // to reach playing. VideoQueue().Push(aSample); if (mState > DECODER_STATE_DECODING_FIRSTFRAME) { - SendStreamData(); // The ready state can change when we've decoded data, so update the // ready state, so that DOM events can fire. UpdateReadyState(); @@ -945,11 +945,14 @@ case DECODER_STATE_BUFFERING: case DECODER_STATE_DECODING: { CheckIfDecodeComplete(); - SendStreamData(); // The ready state can change when we've decoded data, so update the // ready state, so that DOM events can fire. UpdateReadyState(); mDecoder->GetReentrantMonitor().NotifyAll(); + // Schedule the state machine to notify track ended as soon as possible. + if (mAudioCaptured) { + ScheduleStateMachine(); + } return; } case DECODER_STATE_SEEKING: { @@ -1047,6 +1050,11 @@ DECODER_LOG("Slow video decode, set mLowAudioThresholdUsecs=%lld mAmpleAudioThresholdUsecs=%lld", mLowAudioThresholdUsecs, mAmpleAudioThresholdUsecs); } + + // Schedule the state machine to send stream data as soon as possible. + if (mAudioCaptured) { + ScheduleStateMachine(); + } return; } case DECODER_STATE_SEEKING: { @@ -1387,11 +1395,11 @@ } } -void MediaDecoderStateMachine::SetAudioCaptured(bool aCaptured) +void MediaDecoderStateMachine::SetAudioCaptured() { NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); - ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - if (!mAudioCaptured && aCaptured && !mStopAudioThread) { + AssertCurrentThreadInMonitor(); + if (!mAudioCaptured && !mStopAudioThread) { // Make sure the state machine runs as soon as possible. That will // stop the audio sink. // If mStopAudioThread is true then we're already stopping the audio sink @@ -1405,7 +1413,7 @@ ResyncAudioClock(); } } - mAudioCaptured = aCaptured; + mAudioCaptured = true; } double MediaDecoderStateMachine::GetCurrentTime() const @@ -1418,6 +1426,16 @@ return static_cast(mCurrentFrameTime) / static_cast(USECS_PER_S); } +int64_t MediaDecoderStateMachine::GetCurrentTimeUs() const +{ + NS_ASSERTION(NS_IsMainThread() || + OnStateMachineThread() || + OnDecodeThread(), + "Should be on main, decode, or state machine thread."); + + return mCurrentFrameTime; +} + bool MediaDecoderStateMachine::IsRealTime() const { return mScheduler->IsRealTime(); } @@ -1578,9 +1596,9 @@ void MediaDecoderStateMachine::StartWaitForResources() { - NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), - "Should be on state machine or decode thread."); - AssertCurrentThreadInMonitor(); + ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); + NS_ASSERTION(OnDecodeThread(), + "Should be on decode thread."); SetState(DECODER_STATE_WAIT_FOR_RESOURCES); DECODER_LOG("StartWaitForResources"); } @@ -1782,7 +1800,9 @@ DECODER_LOG("Changed state to SEEKING (to %lld)", mSeekTarget.mTime); SetState(DECODER_STATE_SEEKING); - mDecoder->RecreateDecodedStreamIfNecessary(seekTime - mStartTime); + if (mAudioCaptured) { + mDecoder->RecreateDecodedStream(seekTime - mStartTime); + } ScheduleStateMachine(); } @@ -1811,9 +1831,6 @@ mAudioSink->Shutdown(); } mAudioSink = nullptr; - // Now that the audio sink is dead, try sending data to our MediaStream(s). - // That may have been waiting for the audio thread to stop. - SendStreamData(); } // Wake up those waiting for audio sink to finish. mDecoder->GetReentrantMonitor().NotifyAll(); @@ -1967,7 +1984,7 @@ SAMPLE_LOG("EnsureAudioDecodeTaskQueued isDecoding=%d status=%d", IsAudioDecoding(), mAudioRequestStatus); - if (mState >= DECODER_STATE_COMPLETED) { + if (mState >= DECODER_STATE_COMPLETED || mState == DECODER_STATE_DORMANT) { return NS_OK; } @@ -2012,7 +2029,7 @@ NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), "Should be on state machine or decode thread."); - if (mState >= DECODER_STATE_COMPLETED) { + if (mState >= DECODER_STATE_COMPLETED || mState == DECODER_STATE_DORMANT) { return NS_OK; } @@ -2179,28 +2196,33 @@ MOZ_ASSERT(mState == DECODER_STATE_DECODING_METADATA); DECODER_LOG("Decoding Media Headers"); - mReader->PreReadMetadata(); - - if (mReader->IsWaitingMediaResources()) { - StartWaitForResources(); - return NS_OK; - } - nsresult res; MediaInfo info; + bool isAwaitingResources = false; { ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); - res = mReader->ReadMetadata(&info, getter_Transfers(mMetadataTags)); - } + mReader->PreReadMetadata(); - if (NS_SUCCEEDED(res)) { - if (mState == DECODER_STATE_DECODING_METADATA && - mReader->IsWaitingMediaResources()) { - // change state to DECODER_STATE_WAIT_FOR_RESOURCES + if (mReader->IsWaitingMediaResources()) { StartWaitForResources(); - // affect values only if ReadMetadata succeeds return NS_OK; } + res = mReader->ReadMetadata(&info, getter_Transfers(mMetadataTags)); + isAwaitingResources = mReader->IsWaitingMediaResources(); + } + + if (NS_SUCCEEDED(res) && + mState == DECODER_STATE_DECODING_METADATA && + isAwaitingResources) { + // change state to DECODER_STATE_WAIT_FOR_RESOURCES + StartWaitForResources(); + // affect values only if ReadMetadata succeeds + return NS_OK; + } + + if (NS_FAILED(res) || (!info.HasValidMedia())) { + DECODER_WARN("ReadMetadata failed, res=%x HasValidMedia=%d", res, info.HasValidMedia()); + return NS_ERROR_FAILURE; } if (NS_SUCCEEDED(res)) { @@ -2209,10 +2231,16 @@ mInfo = info; - if (NS_FAILED(res) || (!info.HasValidMedia())) { - DECODER_WARN("ReadMetadata failed, res=%x HasValidMedia=%d", res, info.HasValidMedia()); - return NS_ERROR_FAILURE; + if (HasVideo()) { + mAmpleVideoFrames = (mReader->IsAsync() && mInfo.mVideo.mIsHardwareAccelerated) + ? std::max(sVideoQueueHWAccelSize, MIN_VIDEO_QUEUE_SIZE) + : std::max(sVideoQueueDefaultSize, MIN_VIDEO_QUEUE_SIZE); + DECODER_LOG("Video decode isAsync=%d HWAccel=%d videoQueueSize=%d", + mReader->IsAsync(), + mInfo.mVideo.mIsHardwareAccelerated, + mAmpleVideoFrames); } + mDecoder->StartProgressUpdates(); mGotDurationFromMetaData = (GetDuration() != -1) || mDurationSet; @@ -2301,6 +2329,7 @@ NS_ENSURE_SUCCESS(res, res); } else { if (HasAudio()) { + mAudioRequestStatus = RequestStatus::Pending; ReentrantMonitorAutoExit unlock(mDecoder->GetReentrantMonitor()); mReader->RequestAudioData()->Then(DecodeTaskQueue(), __func__, this, &MediaDecoderStateMachine::OnAudioDecoded, @@ -2308,6 +2337,7 @@ } if (HasVideo()) { mVideoDecodeStartTime = TimeStamp::Now(); + mVideoRequestStatus = RequestStatus::Pending; ReentrantMonitorAutoExit unlock(mDecoder->GetReentrantMonitor()); mReader->RequestVideoData(false, 0) ->Then(DecodeTaskQueue(), __func__, this, @@ -2764,12 +2794,11 @@ // Now that those threads are stopped, there's no possibility of // mPendingWakeDecoder being needed again. Revoke it. mPendingWakeDecoder = nullptr; - { - ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); - // Wait for the thread decoding, if any, to exit. - DecodeTaskQueue()->AwaitIdle(); - mReader->ReleaseMediaResources(); - } + DebugOnly rv = DecodeTaskQueue()->Dispatch( + NS_NewRunnableMethod(mReader, &MediaDecoderReader::ReleaseMediaResources)); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + mAudioRequestStatus = RequestStatus::Idle; + mVideoRequestStatus = RequestStatus::Idle; return NS_OK; } @@ -3052,11 +3081,7 @@ return; } - DecodedStreamData* stream = mDecoder->GetDecodedStream(); - if (stream && !stream->mStreamInitialized) { - // Output streams exist but are not initialized yet. - // Send the data we already have to allow stream clock to progress and - // avoid stalling playback. + if (mAudioCaptured) { SendStreamData(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaDecoderStateMachine.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaDecoderStateMachine.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaDecoderStateMachine.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaDecoderStateMachine.h 2015-02-03 14:33:40.000000000 +0000 @@ -155,7 +155,7 @@ // Set the audio volume. The decoder monitor must be obtained before // calling this. void SetVolume(double aVolume); - void SetAudioCaptured(bool aCapture); + void SetAudioCaptured(); // Check if the decoder needs to become dormant state. bool IsDormantNeeded(); @@ -241,6 +241,7 @@ // Called from the main thread to get the current frame time. The decoder // monitor must be obtained before calling this. double GetCurrentTime() const; + int64_t GetCurrentTimeUs() const; // Clear the flag indicating that a playback position change event // is currently queued. This is called from the main thread and must @@ -1025,11 +1026,11 @@ bool mIsAudioPrerolling; bool mIsVideoPrerolling; - MOZ_BEGIN_NESTED_ENUM_CLASS(RequestStatus) + enum class RequestStatus { Idle, Pending, Waiting - MOZ_END_NESTED_ENUM_CLASS(RequestStatus) + }; // True when we have dispatched a task to the decode task queue to request // decoded audio/video, and/or we are waiting for the requested sample to be diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaInfo.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaInfo.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaInfo.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaInfo.h 2015-02-03 14:33:40.000000000 +0000 @@ -41,6 +41,7 @@ : mDisplay(0,0) , mStereoMode(StereoMode::MONO) , mHasVideo(false) + , mIsHardwareAccelerated(false) { // TODO: TrackInfo should be initialized by its specific codec decoder. // This following call should be removed once we have that implemented. @@ -59,6 +60,8 @@ bool mHasVideo; TrackInfo mTrackInfo; + + bool mIsHardwareAccelerated; }; class AudioInfo { @@ -88,6 +91,8 @@ class MediaInfo { public: + MediaInfo() : mIsEncrypted(false) {} + bool HasVideo() const { return mVideo.mHasVideo; @@ -103,6 +108,8 @@ return HasVideo() || HasAudio(); } + bool mIsEncrypted; + // TODO: Store VideoInfo and AudioIndo in arrays to support multi-tracks. VideoInfo mVideo; AudioInfo mAudio; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaManager.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaManager.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaManager.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaManager.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -1683,6 +1683,18 @@ nsIURI* docURI = aWindow->GetDocumentURI(); + bool isLoop = false; + nsCOMPtr loopURI; + nsresult rv = NS_NewURI(getter_AddRefs(loopURI), "about:loopconversation"); + NS_ENSURE_SUCCESS(rv, rv); + rv = docURI->EqualsExceptRef(loopURI, &isLoop); + NS_ENSURE_SUCCESS(rv, rv); + + if (isLoop) { + privileged = true; + } + + if (c.mVideo.IsMediaTrackConstraints()) { auto& tc = c.mVideo.GetAsMediaTrackConstraints(); MediaSourceEnum src = StringToEnum(dom::MediaSourceEnumValues::strings, @@ -1725,6 +1737,17 @@ default: return task->Denied(NS_LITERAL_STRING("NotFoundError")); } + + // For all but tab sharing, Loop needs to prompt as we are using the + // permission menu for selection of the device currently. For tab sharing, + // Loop has implicit permissions within Firefox, as it is built-in, + // and will manage the active tab and provide appropriate UI. + if (isLoop && + (src == dom::MediaSourceEnum::Window || + src == dom::MediaSourceEnum::Application || + src == dom::MediaSourceEnum::Screen)) { + privileged = false; + } } #ifdef MOZ_B2G_CAMERA @@ -1733,17 +1756,6 @@ } #endif - bool isLoop = false; - nsCOMPtr loopURI; - nsresult rv = NS_NewURI(getter_AddRefs(loopURI), "about:loopconversation"); - NS_ENSURE_SUCCESS(rv, rv); - rv = docURI->EqualsExceptRef(loopURI, &isLoop); - NS_ENSURE_SUCCESS(rv, rv); - - if (isLoop) { - privileged = true; - } - // XXX No full support for picture in Desktop yet (needs proper UI) if (privileged || (c.mFake && !Preferences::GetBool("media.navigator.permission.fake"))) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaPromise.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaPromise.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaPromise.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaPromise.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -23,5 +23,18 @@ return aEventTarget->Dispatch(aRunnable, NS_DISPATCH_NORMAL); } +void +AssertOnThread(MediaTaskQueue* aQueue) +{ + MOZ_ASSERT(aQueue->IsCurrentThreadIn()); +} + +void AssertOnThread(nsIEventTarget* aTarget) +{ + nsCOMPtr targetThread = do_QueryInterface(aTarget); + MOZ_ASSERT(targetThread, "Don't know how to deal with threadpools etc here"); + MOZ_ASSERT(NS_GetCurrentThread() == targetThread); +} + } } // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaPromise.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaPromise.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaPromise.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaPromise.h 2015-02-03 14:33:40.000000000 +0000 @@ -37,6 +37,11 @@ nsresult DispatchMediaPromiseRunnable(MediaTaskQueue* aQueue, nsIRunnable* aRunnable); nsresult DispatchMediaPromiseRunnable(nsIEventTarget* aTarget, nsIRunnable* aRunnable); +#ifdef DEBUG +void AssertOnThread(MediaTaskQueue* aQueue); +void AssertOnThread(nsIEventTarget* aTarget); +#endif + } // namespace detail /* @@ -81,6 +86,32 @@ return p; } + class Consumer + { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Consumer) + + void Disconnect() + { + AssertOnDispatchThread(); + MOZ_RELEASE_ASSERT(!mComplete); + mDisconnected = true; + } + +#ifdef DEBUG + virtual void AssertOnDispatchThread() = 0; +#else + void AssertOnDispatchThread() {} +#endif + + protected: + Consumer() : mComplete(false), mDisconnected(false) {} + virtual ~Consumer() {} + + bool mComplete; + bool mDisconnected; + }; + protected: /* @@ -89,7 +120,7 @@ * resolved or rejected, a {Resolve,Reject}Runnable is dispatched, which * invokes the resolve/reject method and then deletes the ThenValue. */ - class ThenValueBase + class ThenValueBase : public Consumer { public: class ResolveRunnable : public nsRunnable @@ -108,14 +139,12 @@ { PROMISE_LOG("ResolveRunnable::Run() [this=%p]", this); mThenValue->DoResolve(mResolveValue); - - delete mThenValue; mThenValue = nullptr; return NS_OK; } private: - ThenValueBase* mThenValue; + nsRefPtr mThenValue; ResolveValueType mResolveValue; }; @@ -135,28 +164,20 @@ { PROMISE_LOG("RejectRunnable::Run() [this=%p]", this); mThenValue->DoReject(mRejectValue); - - delete mThenValue; mThenValue = nullptr; return NS_OK; } private: - ThenValueBase* mThenValue; + nsRefPtr mThenValue; RejectValueType mRejectValue; }; - explicit ThenValueBase(const char* aCallSite) : mCallSite(aCallSite) - { - MOZ_COUNT_CTOR(ThenValueBase); - } + explicit ThenValueBase(const char* aCallSite) : mCallSite(aCallSite) {} virtual void Dispatch(MediaPromise *aPromise) = 0; protected: - // This may only be deleted by {Resolve,Reject}Runnable::Run. - virtual ~ThenValueBase() { MOZ_COUNT_DTOR(ThenValueBase); } - virtual void DoResolve(ResolveValueType aResolveValue) = 0; virtual void DoReject(RejectValueType aRejectValue) = 0; @@ -218,18 +239,48 @@ MOZ_ASSERT(NS_SUCCEEDED(rv)); } +#ifdef DEBUG + // Can't mark MOZ_OVERRIDE due to bug in clang builders we use for osx b2g desktop. :-( + virtual void AssertOnDispatchThread() + { + detail::AssertOnThread(mResponseTarget); + } +#endif + protected: virtual void DoResolve(ResolveValueType aResolveValue) MOZ_OVERRIDE { + Consumer::mComplete = true; + if (Consumer::mDisconnected) { + PROMISE_LOG("ThenValue::DoResolve disconnected - bailing out [this=%p]", this); + return; + } InvokeCallbackMethod(mThisVal.get(), mResolveMethod, aResolveValue); + + // Null these out after invoking the callback so that any references are + // released predictably on the target thread. Otherwise, they would be + // released on whatever thread last drops its reference to the ThenValue, + // which may or may not be ok. + mResponseTarget = nullptr; + mThisVal = nullptr; } virtual void DoReject(RejectValueType aRejectValue) MOZ_OVERRIDE { + Consumer::mComplete = true; + if (Consumer::mDisconnected) { + PROMISE_LOG("ThenValue::DoReject disconnected - bailing out [this=%p]", this); + return; + } InvokeCallbackMethod(mThisVal.get(), mRejectMethod, aRejectValue); - } - virtual ~ThenValue() {} + // Null these out after invoking the callback so that any references are + // released predictably on the target thread. Otherwise, they would be + // released on whatever thread last drops its reference to the ThenValue, + // which may or may not be ok. + mResponseTarget = nullptr; + mThisVal = nullptr; + } private: nsRefPtr mResponseTarget; @@ -241,23 +292,35 @@ template - void Then(TargetType* aResponseTarget, const char* aCallSite, ThisType* aThisVal, - ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod) + already_AddRefed RefableThen(TargetType* aResponseTarget, const char* aCallSite, ThisType* aThisVal, + ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod) { MutexAutoLock lock(mMutex); MOZ_RELEASE_ASSERT(!IsExclusive || !mHaveConsumer); mHaveConsumer = true; - ThenValueBase* thenValue = new ThenValue(aResponseTarget, aThisVal, - aResolveMethod, aRejectMethod, - aCallSite); + nsRefPtr thenValue = new ThenValue(aResponseTarget, aThisVal, + aResolveMethod, aRejectMethod, + aCallSite); PROMISE_LOG("%s invoking Then() [this=%p, thenValue=%p, aThisVal=%p, isPending=%d]", - aCallSite, this, thenValue, aThisVal, (int) IsPending()); + aCallSite, this, thenValue.get(), aThisVal, (int) IsPending()); if (!IsPending()) { thenValue->Dispatch(this); } else { mThenValues.AppendElement(thenValue); } + + return thenValue.forget(); + } + + template + void Then(TargetType* aResponseTarget, const char* aCallSite, ThisType* aThisVal, + ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod) + { + nsRefPtr c = + RefableThen(aResponseTarget, aCallSite, aThisVal, aResolveMethod, aRejectMethod); + return; } void ChainTo(already_AddRefed aChainedPromise, const char* aCallSite) @@ -331,7 +394,7 @@ Mutex mMutex; Maybe mResolveValue; Maybe mRejectValue; - nsTArray mThenValues; + nsTArray> mThenValues; nsTArray> mChainedPromises; bool mHaveConsumer; }; @@ -425,6 +488,49 @@ nsRefPtr mPromise; }; +/* + * Class to encapsulate a MediaPromise::Consumer reference. Use this as the member + * variable for a class waiting on a media promise. + */ +template +class MediaPromiseConsumerHolder +{ +public: + MediaPromiseConsumerHolder() {} + ~MediaPromiseConsumerHolder() { MOZ_ASSERT(!mConsumer); } + + void Begin(already_AddRefed aConsumer) + { + MOZ_RELEASE_ASSERT(!Exists()); + mConsumer = aConsumer; + } + + void Complete() + { + MOZ_RELEASE_ASSERT(Exists()); + mConsumer = nullptr; + } + + // Disconnects and forgets an outstanding promise. The resolve/reject methods + // will never be called. + void Disconnect() { + MOZ_ASSERT(Exists()); + mConsumer->Disconnect(); + mConsumer = nullptr; + } + + void DisconnectIfExists() { + if (Exists()) { + Disconnect(); + } + } + + bool Exists() { return !!mConsumer; } + +private: + nsRefPtr mConsumer; +}; + #undef PROMISE_LOG } // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/ContainerParser.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/ContainerParser.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/ContainerParser.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/ContainerParser.cpp 2015-02-03 14:33:42.000000000 +0000 @@ -8,9 +8,13 @@ #include "WebMBufferedParser.h" #include "mozilla/Endian.h" -#include "mp4_demuxer/BufferStream.h" #include "mp4_demuxer/MoofParser.h" #include "prlog.h" +#include "MediaData.h" +#ifdef MOZ_FMP4 +#include "MP4Stream.h" +#endif +#include "SourceBufferResource.h" #ifdef PR_LOGGING extern PRLogModuleInfo* GetMediaSourceLog(); @@ -27,32 +31,38 @@ namespace mozilla { +ContainerParser::ContainerParser() + : mInitData(new LargeDataBuffer()) + , mHasInitData(false) +{ +} + bool -ContainerParser::IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength) +ContainerParser::IsInitSegmentPresent(LargeDataBuffer* aData) { MSE_DEBUG("ContainerParser(%p)::IsInitSegmentPresent aLength=%u [%x%x%x%x]", - this, aLength, - aLength > 0 ? aData[0] : 0, - aLength > 1 ? aData[1] : 0, - aLength > 2 ? aData[2] : 0, - aLength > 3 ? aData[3] : 0); + this, aData->Length(), + aData->Length() > 0 ? (*aData)[0] : 0, + aData->Length() > 1 ? (*aData)[1] : 0, + aData->Length() > 2 ? (*aData)[2] : 0, + aData->Length() > 3 ? (*aData)[3] : 0); return false; } bool -ContainerParser::IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength) +ContainerParser::IsMediaSegmentPresent(LargeDataBuffer* aData) { MSE_DEBUG("ContainerParser(%p)::IsMediaSegmentPresent aLength=%u [%x%x%x%x]", - this, aLength, - aLength > 0 ? aData[0] : 0, - aLength > 1 ? aData[1] : 0, - aLength > 2 ? aData[2] : 0, - aLength > 3 ? aData[3] : 0); + this, aData->Length(), + aData->Length() > 0 ? (*aData)[0] : 0, + aData->Length() > 1 ? (*aData)[1] : 0, + aData->Length() > 2 ? (*aData)[2] : 0, + aData->Length() > 3 ? (*aData)[3] : 0); return false; } bool -ContainerParser::ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength, +ContainerParser::ParseStartAndEndTimestamps(LargeDataBuffer* aData, int64_t& aStart, int64_t& aEnd) { return false; @@ -71,7 +81,7 @@ return 0; } -const nsTArray& +LargeDataBuffer* ContainerParser::InitData() { MOZ_ASSERT(mHasInitData); @@ -87,9 +97,9 @@ static const unsigned NS_PER_USEC = 1000; static const unsigned USEC_PER_SEC = 1000000; - bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength) + bool IsInitSegmentPresent(LargeDataBuffer* aData) { - ContainerParser::IsInitSegmentPresent(aData, aLength); + ContainerParser::IsInitSegmentPresent(aData); // XXX: This is overly primitive, needs to collect data as it's appended // to the SB and handle, rather than assuming everything is present in a // single aData segment. @@ -100,16 +110,17 @@ // 0x18538067 // Segment (must be "unknown" size) // 0x1549a966 // -> Segment Info // 0x1654ae6b // -> One or more Tracks - if (aLength >= 4 && - aData[0] == 0x1a && aData[1] == 0x45 && aData[2] == 0xdf && aData[3] == 0xa3) { + if (aData->Length() >= 4 && + (*aData)[0] == 0x1a && (*aData)[1] == 0x45 && (*aData)[2] == 0xdf && + (*aData)[3] == 0xa3) { return true; } return false; } - bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength) + bool IsMediaSegmentPresent(LargeDataBuffer* aData) { - ContainerParser::IsMediaSegmentPresent(aData, aLength); + ContainerParser::IsMediaSegmentPresent(aData); // XXX: This is overly primitive, needs to collect data as it's appended // to the SB and handle, rather than assuming everything is present in a // single aData segment. @@ -120,17 +131,18 @@ // 0x18538067 // Segment (must be "unknown" size) // 0x1549a966 // -> Segment Info // 0x1654ae6b // -> One or more Tracks - if (aLength >= 4 && - aData[0] == 0x1f && aData[1] == 0x43 && aData[2] == 0xb6 && aData[3] == 0x75) { + if (aData->Length() >= 4 && + (*aData)[0] == 0x1f && (*aData)[1] == 0x43 && (*aData)[2] == 0xb6 && + (*aData)[3] == 0x75) { return true; } return false; } - bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength, + bool ParseStartAndEndTimestamps(LargeDataBuffer* aData, int64_t& aStart, int64_t& aEnd) { - bool initSegment = IsInitSegmentPresent(aData, aLength); + bool initSegment = IsInitSegmentPresent(aData); if (initSegment) { mOffset = 0; mParser = WebMBufferedParser(0); @@ -143,24 +155,27 @@ mapping.AppendElements(mOverlappedMapping); mOverlappedMapping.Clear(); ReentrantMonitor dummy("dummy"); - mParser.Append(aData, aLength, mapping, dummy); + mParser.Append(aData->Elements(), aData->Length(), mapping, dummy); // XXX This is a bit of a hack. Assume if there are no timecodes // present and it's an init segment that it's _just_ an init segment. // We should be more precise. if (initSegment) { - uint32_t length = aLength; + uint32_t length = aData->Length(); if (!mapping.IsEmpty()) { length = mapping[0].mSyncOffset; - MOZ_ASSERT(length <= aLength); + MOZ_ASSERT(length <= aData->Length()); } MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.", this, length); - - mInitData.ReplaceElementsAt(0, mInitData.Length(), aData, length); + if (!mInitData->ReplaceElementsAt(0, mInitData->Length(), + aData->Elements(), length)) { + // Unlikely OOM + return false; + } mHasInitData = true; } - mOffset += aLength; + mOffset += aData->Length(); if (mapping.IsEmpty()) { return false; @@ -201,54 +216,58 @@ int64_t mOffset; }; +#ifdef MOZ_FMP4 class MP4ContainerParser : public ContainerParser { public: MP4ContainerParser() :mMonitor("MP4ContainerParser Index Monitor") {} - bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength) + bool IsInitSegmentPresent(LargeDataBuffer* aData) { - ContainerParser::IsInitSegmentPresent(aData, aLength); + ContainerParser::IsInitSegmentPresent(aData); // Each MP4 atom has a chunk size and chunk type. The root chunk in an MP4 // file is the 'ftyp' atom followed by a file type. We just check for a // vaguely valid 'ftyp' atom. - if (aLength < 8) { + if (aData->Length() < 8) { return false; } - uint32_t chunk_size = BigEndian::readUint32(aData); + uint32_t chunk_size = BigEndian::readUint32(aData->Elements()); if (chunk_size < 8) { return false; } - return aData[4] == 'f' && aData[5] == 't' && aData[6] == 'y' && - aData[7] == 'p'; + return (*aData)[4] == 'f' && (*aData)[5] == 't' && (*aData)[6] == 'y' && + (*aData)[7] == 'p'; } - bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength) + bool IsMediaSegmentPresent(LargeDataBuffer* aData) { - ContainerParser::IsMediaSegmentPresent(aData, aLength); - if (aLength < 8) { + ContainerParser::IsMediaSegmentPresent(aData); + if (aData->Length() < 8) { return false; } - uint32_t chunk_size = BigEndian::readUint32(aData); + uint32_t chunk_size = BigEndian::readUint32(aData->Elements()); if (chunk_size < 8) { return false; } - return (aData[4] == 'm' && aData[5] == 'o' && aData[6] == 'o' && aData[7] == 'f') || - (aData[4] == 's' && aData[5] == 't' && aData[6] == 'y' && aData[7] == 'p'); + return ((*aData)[4] == 'm' && (*aData)[5] == 'o' && (*aData)[6] == 'o' && + (*aData)[7] == 'f') || + ((*aData)[4] == 's' && (*aData)[5] == 't' && (*aData)[6] == 'y' && + (*aData)[7] == 'p'); } - bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength, + bool ParseStartAndEndTimestamps(LargeDataBuffer* aData, int64_t& aStart, int64_t& aEnd) { MonitorAutoLock mon(mMonitor); // We're not actually racing against anything, // but mParser requires us to hold a monitor. - bool initSegment = IsInitSegmentPresent(aData, aLength); + bool initSegment = IsInitSegmentPresent(aData); if (initSegment) { - mStream = new mp4_demuxer::BufferStream(); + mResource = new SourceBufferResource(NS_LITERAL_CSTRING("video/mp4")); + mStream = new MP4Stream(mResource); // We use a timestampOffset of 0 for ContainerParser, and require // consumers of ParseStartAndEndTimestamps to add their timestamp offset // manually. This allows the ContainerParser to be shared across different @@ -258,9 +277,11 @@ return false; } - mStream->AppendBytes(aData, aLength); + mResource->AppendData(aData); nsTArray byteRanges; - byteRanges.AppendElement(mStream->GetByteRange()); + MediaByteRange mbr = + MediaByteRange(mParser->mOffset, mResource->GetLength()); + byteRanges.AppendElement(mbr); mParser->RebuildFragmentedIndex(byteRanges); if (initSegment) { @@ -268,16 +289,18 @@ MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.", this, range.mEnd - range.mStart); - mInitData.ReplaceElementsAt(0, mInitData.Length(), - aData + range.mStart, - range.mEnd - range.mStart); + if (!mInitData->ReplaceElementsAt(0, mInitData->Length(), + aData->Elements() + range.mStart, + range.mEnd - range.mStart)) { + // Super unlikely OOM + return false; + } mHasInitData = true; } mp4_demuxer::Interval compositionRange = mParser->GetCompositionRange(byteRanges); - - mStream->DiscardBefore(mParser->mOffset); + mResource->EvictData(mParser->mOffset, mParser->mOffset); if (compositionRange.IsNull()) { return false; @@ -297,10 +320,12 @@ } private: - nsRefPtr mStream; + nsRefPtr mStream; nsAutoPtr mParser; + nsRefPtr mResource; Monitor mMonitor; }; +#endif /*static*/ ContainerParser* ContainerParser::CreateForMIMEType(const nsACString& aType) @@ -309,9 +334,11 @@ return new WebMContainerParser(); } +#ifdef MOZ_FMP4 if (aType.LowerCaseEqualsLiteral("video/mp4") || aType.LowerCaseEqualsLiteral("audio/mp4")) { return new MP4ContainerParser(); } +#endif return new ContainerParser(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/ContainerParser.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/ContainerParser.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/ContainerParser.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/ContainerParser.h 2015-02-03 14:33:42.000000000 +0000 @@ -7,29 +7,31 @@ #ifndef MOZILLA_CONTAINERPARSER_H_ #define MOZILLA_CONTAINERPARSER_H_ -#include "nsTArray.h" +#include "nsRefPtr.h" namespace mozilla { +class LargeDataBuffer; + class ContainerParser { public: - ContainerParser() : mHasInitData(false) {} + ContainerParser(); virtual ~ContainerParser() {} // Return true if aData starts with an initialization segment. // The base implementation exists only for debug logging and is expected // to be called first from the overriding implementation. - virtual bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength); + virtual bool IsInitSegmentPresent(LargeDataBuffer* aData); // Return true if aData starts with a media segment. // The base implementation exists only for debug logging and is expected // to be called first from the overriding implementation. - virtual bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength); + virtual bool IsMediaSegmentPresent(LargeDataBuffer* aData); // Parse aData to extract the start and end frame times from the media // segment. aData may not start on a parser sync boundary. Return true // if aStart and aEnd have been updated. - virtual bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength, + virtual bool ParseStartAndEndTimestamps(LargeDataBuffer* aData, int64_t& aStart, int64_t& aEnd); // Compare aLhs and rHs, considering any error that may exist in the @@ -39,7 +41,7 @@ virtual int64_t GetRoundingError(); - const nsTArray& InitData(); + LargeDataBuffer* InitData(); bool HasInitData() { @@ -49,7 +51,7 @@ static ContainerParser* CreateForMIMEType(const nsACString& aType); protected: - nsTArray mInitData; + nsRefPtr mInitData; bool mHasInitData; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/MediaSource.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/MediaSource.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/MediaSource.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/MediaSource.cpp 2015-02-03 14:33:42.000000000 +0000 @@ -341,19 +341,14 @@ } // We want to restrict to YouTube only. - // We define that as the origin being https://*.youtube.com. - // We also support https://*.youtube-nocookie.com. + // We define that as the origin being *.youtube.com. + // We also support *.youtube-nocookie.com nsIPrincipal* principal = nsContentUtils::ObjectPrincipal(global); nsCOMPtr uri; if (NS_FAILED(principal->GetURI(getter_AddRefs(uri))) || !uri) { return false; } - bool isHttps = false; - if (NS_FAILED(uri->SchemeIs("https", &isHttps)) || !isHttps) { - return false; - } - nsCOMPtr tldServ = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); NS_ENSURE_TRUE(tldServ, false); @@ -540,6 +535,12 @@ } #endif +void +MediaSource::GetMozDebugReaderData(nsAString& aString) +{ + mDecoder->GetMozDebugReaderData(aString); +} + nsPIDOMWindow* MediaSource::GetParentObject() const { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/MediaSourceDecoder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/MediaSourceDecoder.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/MediaSourceDecoder.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/MediaSourceDecoder.cpp 2015-02-03 14:33:42.000000000 +0000 @@ -290,6 +290,12 @@ mReader->PrepareInitialization(); } +void +MediaSourceDecoder::GetMozDebugReaderData(nsAString& aString) +{ + mReader->GetMozDebugReaderData(aString); +} + #ifdef MOZ_EME nsresult MediaSourceDecoder::SetCDMProxy(CDMProxy* aProxy) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/MediaSourceDecoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/MediaSourceDecoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/MediaSourceDecoder.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/MediaSourceDecoder.h 2015-02-03 14:33:42.000000000 +0000 @@ -79,6 +79,10 @@ // reader in this decoders MediaSourceReader. bool IsActiveReader(MediaDecoderReader* aReader); + // Returns a string describing the state of the MediaSource internal + // buffered data. Used for debugging purposes. + void GetMozDebugReaderData(nsAString& aString); + private: void DoSetMediaSourceDuration(double aDuration); void ScheduleDurationChange(double aOldDuration, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/MediaSource.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/MediaSource.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/MediaSource.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/MediaSource.h 2015-02-03 14:33:42.000000000 +0000 @@ -114,6 +114,10 @@ void Dump(const char* aPath); #endif + // Returns a string describing the state of the MediaSource internal + // buffered data. Used for debugging purposes. + void GetMozDebugReaderData(nsAString& aString); + private: // MediaSourceDecoder uses DurationChange to set the duration // without hitting the checks in SetDuration. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/MediaSourceReader.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/MediaSourceReader.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/MediaSourceReader.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/MediaSourceReader.cpp 2015-02-03 14:33:42.000000000 +0000 @@ -13,6 +13,7 @@ #include "MediaSourceUtils.h" #include "SourceBufferDecoder.h" #include "TrackBuffer.h" +#include "nsPrintfCString.h" #ifdef MOZ_FMP4 #include "SharedDecoderManager.h" @@ -50,9 +51,7 @@ , mLastVideoTime(0) , mPendingSeekTime(-1) , mWaitingForSeekData(false) - , mAudioIsSeeking(false) - , mVideoIsSeeking(false) - , mTimeThreshold(-1) + , mTimeThreshold(0) , mDropAudioBeforeThreshold(false) , mDropVideoBeforeThreshold(false) , mEnded(false) @@ -119,18 +118,20 @@ mAudioPromise.Reject(DECODE_ERROR, __func__); return p; } - if (mAudioIsSeeking) { + if (IsSeeking()) { MSE_DEBUG("MediaSourceReader(%p)::RequestAudioData called mid-seek. Rejecting.", this); mAudioPromise.Reject(CANCELED, __func__); return p; } + MOZ_RELEASE_ASSERT(!mAudioSeekRequest.Exists()); + SwitchReaderResult ret = SwitchAudioReader(mLastAudioTime); switch (ret) { case READER_NEW: - mAudioReader->Seek(mLastAudioTime, 0) - ->Then(GetTaskQueue(), __func__, this, - &MediaSourceReader::RequestAudioDataComplete, - &MediaSourceReader::RequestAudioDataFailed); + mAudioSeekRequest.Begin(mAudioReader->Seek(mLastAudioTime, 0) + ->RefableThen(GetTaskQueue(), __func__, this, + &MediaSourceReader::CompleteAudioSeekAndDoRequest, + &MediaSourceReader::CompleteAudioSeekAndRejectPromise)); break; case READER_ERROR: if (mLastAudioTime) { @@ -139,49 +140,42 @@ } // Fallback to using current reader default: - RequestAudioDataComplete(0); + DoAudioRequest(); break; } return p; } -void -MediaSourceReader::RequestAudioDataComplete(int64_t aTime) -{ - mAudioReader->RequestAudioData()->Then(GetTaskQueue(), __func__, this, - &MediaSourceReader::OnAudioDecoded, - &MediaSourceReader::OnAudioNotDecoded); -} - -void -MediaSourceReader::RequestAudioDataFailed(nsresult aResult) +void MediaSourceReader::DoAudioRequest() { - OnAudioNotDecoded(DECODE_ERROR); + mAudioRequest.Begin(mAudioReader->RequestAudioData() + ->RefableThen(GetTaskQueue(), __func__, this, + &MediaSourceReader::OnAudioDecoded, + &MediaSourceReader::OnAudioNotDecoded)); } void MediaSourceReader::OnAudioDecoded(AudioData* aSample) { + MOZ_RELEASE_ASSERT(!IsSeeking()); + mAudioRequest.Complete(); + MSE_DEBUGV("MediaSourceReader(%p)::OnAudioDecoded [mTime=%lld mDuration=%lld mDiscontinuity=%d]", this, aSample->mTime, aSample->mDuration, aSample->mDiscontinuity); if (mDropAudioBeforeThreshold) { if (aSample->mTime < mTimeThreshold) { MSE_DEBUG("MediaSourceReader(%p)::OnAudioDecoded mTime=%lld < mTimeThreshold=%lld", this, aSample->mTime, mTimeThreshold); - mAudioReader->RequestAudioData()->Then(GetTaskQueue(), __func__, this, - &MediaSourceReader::OnAudioDecoded, - &MediaSourceReader::OnAudioNotDecoded); + mAudioRequest.Begin(mAudioReader->RequestAudioData() + ->RefableThen(GetTaskQueue(), __func__, this, + &MediaSourceReader::OnAudioDecoded, + &MediaSourceReader::OnAudioNotDecoded)); return; } mDropAudioBeforeThreshold = false; } - // Any OnAudioDecoded callbacks received while mAudioIsSeeking must be not - // update our last used timestamp, as these are emitted by the reader we're - // switching away from. - if (!mAudioIsSeeking) { - mLastAudioTime = aSample->mTime + aSample->mDuration; - } + mLastAudioTime = aSample->mTime + aSample->mDuration; mAudioPromise.Resolve(aSample, __func__); } @@ -215,6 +209,9 @@ void MediaSourceReader::OnAudioNotDecoded(NotDecodedReason aReason) { + MOZ_RELEASE_ASSERT(!IsSeeking()); + mAudioRequest.Complete(); + MSE_DEBUG("MediaSourceReader(%p)::OnAudioNotDecoded aReason=%u IsEnded: %d", this, aReason, IsEnded()); if (aReason == DECODE_ERROR || aReason == CANCELED) { mAudioPromise.Reject(aReason, __func__); @@ -228,14 +225,12 @@ AdjustEndTime(&mLastAudioTime, mAudioReader); } - // See if we can find a different reader that can pick up where we left off. We use the - // EOS_FUZZ_US to allow for the fact that our end time can be inaccurate due to bug - // 1065207. - if (SwitchAudioReader(mLastAudioTime, EOS_FUZZ_US) == READER_NEW) { - mAudioReader->Seek(mLastAudioTime, 0) - ->Then(GetTaskQueue(), __func__, this, - &MediaSourceReader::RequestAudioDataComplete, - &MediaSourceReader::RequestAudioDataFailed); + // See if we can find a different reader that can pick up where we left off. + if (SwitchAudioReader(mLastAudioTime) == READER_NEW) { + mAudioSeekRequest.Begin(mAudioReader->Seek(mLastAudioTime, 0) + ->RefableThen(GetTaskQueue(), __func__, this, + &MediaSourceReader::CompleteAudioSeekAndDoRequest, + &MediaSourceReader::CompleteAudioSeekAndRejectPromise)); return; } @@ -259,18 +254,20 @@ mDropAudioBeforeThreshold = true; mDropVideoBeforeThreshold = true; } - if (mVideoIsSeeking) { + if (IsSeeking()) { MSE_DEBUG("MediaSourceReader(%p)::RequestVideoData called mid-seek. Rejecting.", this); mVideoPromise.Reject(CANCELED, __func__); return p; } + MOZ_RELEASE_ASSERT(!mVideoSeekRequest.Exists()); + SwitchReaderResult ret = SwitchVideoReader(mLastVideoTime); switch (ret) { case READER_NEW: - mVideoReader->Seek(mLastVideoTime, 0) - ->Then(GetTaskQueue(), __func__, this, - &MediaSourceReader::RequestVideoDataComplete, - &MediaSourceReader::RequestVideoDataFailed); + mVideoSeekRequest.Begin(mVideoReader->Seek(mLastVideoTime, 0) + ->RefableThen(GetTaskQueue(), __func__, this, + &MediaSourceReader::CompleteVideoSeekAndDoRequest, + &MediaSourceReader::CompleteVideoSeekAndRejectPromise)); break; case READER_ERROR: if (mLastVideoTime) { @@ -279,9 +276,7 @@ } // Fallback to using current reader. default: - mVideoReader->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold) - ->Then(GetTaskQueue(), __func__, this, - &MediaSourceReader::OnVideoDecoded, &MediaSourceReader::OnVideoNotDecoded); + DoVideoRequest(); break; } @@ -289,42 +284,34 @@ } void -MediaSourceReader::RequestVideoDataComplete(int64_t aTime) +MediaSourceReader::DoVideoRequest() { - mVideoReader->RequestVideoData(false, 0) - ->Then(GetTaskQueue(), __func__, this, - &MediaSourceReader::OnVideoDecoded, &MediaSourceReader::OnVideoNotDecoded); -} - -void -MediaSourceReader::RequestVideoDataFailed(nsresult aResult) -{ - OnVideoNotDecoded(DECODE_ERROR); + mVideoRequest.Begin(mVideoReader->RequestVideoData(mDropVideoBeforeThreshold, mTimeThreshold) + ->RefableThen(GetTaskQueue(), __func__, this, + &MediaSourceReader::OnVideoDecoded, + &MediaSourceReader::OnVideoNotDecoded)); } void MediaSourceReader::OnVideoDecoded(VideoData* aSample) { + MOZ_RELEASE_ASSERT(!IsSeeking()); + mVideoRequest.Complete(); + MSE_DEBUGV("MediaSourceReader(%p)::OnVideoDecoded [mTime=%lld mDuration=%lld mDiscontinuity=%d]", this, aSample->mTime, aSample->mDuration, aSample->mDiscontinuity); if (mDropVideoBeforeThreshold) { if (aSample->mTime < mTimeThreshold) { MSE_DEBUG("MediaSourceReader(%p)::OnVideoDecoded mTime=%lld < mTimeThreshold=%lld", this, aSample->mTime, mTimeThreshold); - mVideoReader->RequestVideoData(false, 0)->Then(GetTaskQueue(), __func__, this, - &MediaSourceReader::OnVideoDecoded, - &MediaSourceReader::OnVideoNotDecoded); + DoVideoRequest(); return; } mDropVideoBeforeThreshold = false; + mTimeThreshold = 0; } - // Any OnVideoDecoded callbacks received while mVideoIsSeeking must be not - // update our last used timestamp, as these are emitted by the reader we're - // switching away from. - if (!mVideoIsSeeking) { - mLastVideoTime = aSample->mTime + aSample->mDuration; - } + mLastVideoTime = aSample->mTime + aSample->mDuration; mVideoPromise.Resolve(aSample, __func__); } @@ -332,6 +319,9 @@ void MediaSourceReader::OnVideoNotDecoded(NotDecodedReason aReason) { + MOZ_RELEASE_ASSERT(!IsSeeking()); + mVideoRequest.Complete(); + MSE_DEBUG("MediaSourceReader(%p)::OnVideoNotDecoded aReason=%u IsEnded: %d", this, aReason, IsEnded()); if (aReason == DECODE_ERROR || aReason == CANCELED) { mVideoPromise.Reject(aReason, __func__); @@ -345,14 +335,12 @@ AdjustEndTime(&mLastVideoTime, mVideoReader); } - // See if we can find a different reader that can pick up where we left off. We use the - // EOS_FUZZ_US to allow for the fact that our end time can be inaccurate due to bug - // 1065207. - if (SwitchVideoReader(mLastVideoTime, EOS_FUZZ_US) == READER_NEW) { - mVideoReader->Seek(mLastVideoTime, 0) - ->Then(GetTaskQueue(), __func__, this, - &MediaSourceReader::RequestVideoDataComplete, - &MediaSourceReader::RequestVideoDataFailed); + // See if we can find a different reader that can pick up where we left off. + if (SwitchVideoReader(mLastVideoTime) == READER_NEW) { + mVideoSeekRequest.Begin(mVideoReader->Seek(mLastVideoTime, 0) + ->RefableThen(GetTaskQueue(), __func__, this, + &MediaSourceReader::CompleteVideoSeekAndDoRequest, + &MediaSourceReader::CompleteVideoSeekAndRejectPromise)); return; } @@ -411,6 +399,13 @@ mVideoTrack = nullptr; mVideoReader = nullptr; +#ifdef MOZ_FMP4 + if (mSharedDecoderManager) { + mSharedDecoderManager->Shutdown(); + mSharedDecoderManager = nullptr; + } +#endif + MOZ_ASSERT(mAudioPromise.IsEmpty()); MOZ_ASSERT(mVideoPromise.IsEmpty()); @@ -440,7 +435,7 @@ already_AddRefed MediaSourceReader::SelectReader(int64_t aTarget, - int64_t aError, + int64_t aTolerance, const nsTArray>& aTrackDecoders) { mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); @@ -453,7 +448,7 @@ nsRefPtr ranges = new dom::TimeRanges(); aTrackDecoders[i]->GetBuffered(ranges); if (ranges->Find(double(aTarget) / USECS_PER_S, - double(aError) / USECS_PER_S) == dom::TimeRanges::NoIndex) { + double(aTolerance) / USECS_PER_S) == dom::TimeRanges::NoIndex) { MSE_DEBUGV("MediaSourceReader(%p)::SelectReader(%lld) newReader=%p target not in ranges=%s", this, aTarget, newReader.get(), DumpTimeRanges(ranges).get()); continue; @@ -475,14 +470,21 @@ } MediaSourceReader::SwitchReaderResult -MediaSourceReader::SwitchAudioReader(int64_t aTarget, int64_t aError) +MediaSourceReader::SwitchAudioReader(int64_t aTarget) { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); // XXX: Can't handle adding an audio track after ReadMetadata. if (!mAudioTrack) { return READER_ERROR; } - nsRefPtr newReader = SelectReader(aTarget, aError, mAudioTrack->Decoders()); + + // We first search without the tolerance and then search with it, so that, in + // the case of perfectly-aligned data, we don't prematurely jump to a new + // reader and skip the last few samples of the current one. + nsRefPtr newReader = SelectReader(aTarget, /* aTolerance = */ 0, mAudioTrack->Decoders()); + if (!newReader) { + newReader = SelectReader(aTarget, EOS_FUZZ_US, mAudioTrack->Decoders()); + } if (newReader && newReader != mAudioReader) { mAudioReader->SetIdle(); mAudioReader = newReader; @@ -493,14 +495,21 @@ } MediaSourceReader::SwitchReaderResult -MediaSourceReader::SwitchVideoReader(int64_t aTarget, int64_t aError) +MediaSourceReader::SwitchVideoReader(int64_t aTarget) { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); // XXX: Can't handle adding a video track after ReadMetadata. if (!mVideoTrack) { return READER_ERROR; } - nsRefPtr newReader = SelectReader(aTarget, aError, mVideoTrack->Decoders()); + + // We first search without the tolerance and then search with it, so that, in + // the case of perfectly-aligned data, we don't prematurely jump to a new + // reader and skip the last few samples of the current one. + nsRefPtr newReader = SelectReader(aTarget, /* aTolerance = */ 0, mVideoTrack->Decoders()); + if (!newReader) { + newReader = SelectReader(aTarget, EOS_FUZZ_US, mVideoTrack->Decoders()); + } if (newReader && newReader != mVideoReader) { mVideoReader->SetIdle(); mVideoReader = newReader; @@ -635,10 +644,10 @@ MediaSourceReader::TrackBuffersContainTime(int64_t aTime) { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - if (mAudioTrack && !mAudioTrack->ContainsTime(aTime)) { + if (mAudioTrack && !mAudioTrack->ContainsTime(aTime, EOS_FUZZ_US)) { return false; } - if (mVideoTrack && !mVideoTrack->ContainsTime(aTime)) { + if (mVideoTrack && !mVideoTrack->ContainsTime(aTime, EOS_FUZZ_US)) { return false; } return true; @@ -670,6 +679,20 @@ return p; } + // Any previous requests we've been waiting on are now unwanted. + mAudioRequest.DisconnectIfExists(); + mVideoRequest.DisconnectIfExists(); + + // Additionally, reject any outstanding promises _we_ made that we might have + // been waiting on the above to fulfill. + mAudioPromise.RejectIfExists(CANCELED, __func__); + mVideoPromise.RejectIfExists(CANCELED, __func__); + + // Finally, if we were midway seeking a new reader to find a sample, abandon + // that too. + mAudioSeekRequest.DisconnectIfExists(); + mVideoSeekRequest.DisconnectIfExists(); + // Store pending seek target in case the track buffers don't contain // the desired time and we delay doing the seek. mPendingSeekTime = aTime; @@ -688,69 +711,64 @@ { MOZ_ASSERT(OnDecodeThread()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - if (mWaitingForSeekData) { - mSeekPromise.Reject(NS_OK, __func__); - mWaitingForSeekData = false; - mPendingSeekTime = -1; - } else if (mVideoIsSeeking) { - // NB: Currently all readers have sync Seeks(), so this is a no-op. - mVideoReader->CancelSeek(); - } else if (mAudioIsSeeking) { - // NB: Currently all readers have sync Seeks(), so this is a no-op. + mWaitingForSeekData = false; + mPendingSeekTime = -1; + if (mAudioReader) { + mAudioSeekRequest.DisconnectIfExists(); mAudioReader->CancelSeek(); - } else { - MOZ_ASSERT(mSeekPromise.IsEmpty()); } + if (mVideoReader) { + mVideoSeekRequest.DisconnectIfExists(); + mVideoReader->CancelSeek(); + } + mSeekPromise.RejectIfExists(NS_OK, __func__); } void MediaSourceReader::OnVideoSeekCompleted(int64_t aTime) { - mPendingSeekTime = aTime; - MOZ_ASSERT(mVideoIsSeeking); - MOZ_ASSERT(!mAudioIsSeeking); - mVideoIsSeeking = false; + mVideoSeekRequest.Complete(); if (mAudioTrack) { - mAudioIsSeeking = true; - SwitchAudioReader(mPendingSeekTime); - mAudioReader->Seek(mPendingSeekTime, 0) - ->Then(GetTaskQueue(), __func__, this, - &MediaSourceReader::OnAudioSeekCompleted, - &MediaSourceReader::OnSeekFailed); - MSE_DEBUG("MediaSourceReader(%p)::Seek audio reader=%p", this, mAudioReader.get()); - return; + mPendingSeekTime = aTime; + DoAudioSeek(); + } else { + mPendingSeekTime = -1; + mSeekPromise.Resolve(aTime, __func__); } - mSeekPromise.Resolve(mPendingSeekTime, __func__); } void -MediaSourceReader::OnAudioSeekCompleted(int64_t aTime) +MediaSourceReader::OnVideoSeekFailed(nsresult aResult) { - mPendingSeekTime = aTime; - MOZ_ASSERT(mAudioIsSeeking); - MOZ_ASSERT(!mVideoIsSeeking); - mAudioIsSeeking = false; - - mSeekPromise.Resolve(mPendingSeekTime, __func__); + mVideoSeekRequest.Complete(); mPendingSeekTime = -1; + mSeekPromise.Reject(aResult, __func__); } void -MediaSourceReader::OnSeekFailed(nsresult aResult) +MediaSourceReader::DoAudioSeek() { - MOZ_ASSERT(mVideoIsSeeking || mAudioIsSeeking); - - if (mVideoIsSeeking) { - MOZ_ASSERT(!mAudioIsSeeking); - mVideoIsSeeking = false; - } + SwitchAudioReader(mPendingSeekTime); + mAudioSeekRequest.Begin(mAudioReader->Seek(mPendingSeekTime, 0) + ->RefableThen(GetTaskQueue(), __func__, this, + &MediaSourceReader::OnAudioSeekCompleted, + &MediaSourceReader::OnAudioSeekFailed)); + MSE_DEBUG("MediaSourceReader(%p)::DoAudioSeek reader=%p", this, mAudioReader.get()); +} - if (mAudioIsSeeking) { - MOZ_ASSERT(!mVideoIsSeeking); - mAudioIsSeeking = false; - } +void +MediaSourceReader::OnAudioSeekCompleted(int64_t aTime) +{ + mAudioSeekRequest.Complete(); + mPendingSeekTime = -1; + mSeekPromise.Resolve(aTime, __func__); +} +void +MediaSourceReader::OnAudioSeekFailed(nsresult aResult) +{ + mAudioSeekRequest.Complete(); mPendingSeekTime = -1; mSeekPromise.Reject(aResult, __func__); } @@ -778,18 +796,25 @@ mLastVideoTime = mPendingSeekTime; if (mVideoTrack) { - mVideoIsSeeking = true; - SwitchVideoReader(mPendingSeekTime); - mVideoReader->Seek(mPendingSeekTime, 0) - ->Then(GetTaskQueue(), __func__, this, - &MediaSourceReader::OnVideoSeekCompleted, - &MediaSourceReader::OnSeekFailed); - MSE_DEBUG("MediaSourceReader(%p)::Seek video reader=%p", this, mVideoReader.get()); + DoVideoSeek(); + } else if (mAudioTrack) { + DoAudioSeek(); } else { - OnVideoSeekCompleted(mPendingSeekTime); + MOZ_CRASH(); } } +void +MediaSourceReader::DoVideoSeek() +{ + SwitchVideoReader(mPendingSeekTime); + mVideoSeekRequest.Begin(mVideoReader->Seek(mPendingSeekTime, 0) + ->RefableThen(GetTaskQueue(), __func__, this, + &MediaSourceReader::OnVideoSeekCompleted, + &MediaSourceReader::OnVideoSeekFailed)); + MSE_DEBUG("MediaSourceReader(%p)::DoVideoSeek reader=%p", this, mVideoReader.get()); +} + nsresult MediaSourceReader::GetBuffered(dom::TimeRanges* aBuffered) { @@ -844,11 +869,11 @@ { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); bool haveAudio = false, haveVideo = false; - if (!mAudioIsSeeking && mAudioTrack && HaveData(mLastAudioTime, MediaData::AUDIO_DATA)) { + if (!IsSeeking() && mAudioTrack && HaveData(mLastAudioTime, MediaData::AUDIO_DATA)) { haveAudio = true; WaitPromise(MediaData::AUDIO_DATA).ResolveIfExists(MediaData::AUDIO_DATA, __func__); } - if (!mVideoIsSeeking && mVideoTrack && HaveData(mLastVideoTime, MediaData::VIDEO_DATA)) { + if (!IsSeeking() && mVideoTrack && HaveData(mLastVideoTime, MediaData::VIDEO_DATA)) { haveVideo = true; WaitPromise(MediaData::VIDEO_DATA).ResolveIfExists(MediaData::VIDEO_DATA, __func__); } @@ -881,6 +906,7 @@ const MediaInfo& info = mAudioReader->GetMediaInfo(); MOZ_ASSERT(info.HasAudio()); mInfo.mAudio = info.mAudio; + mInfo.mIsEncrypted = mInfo.mIsEncrypted || info.mIsEncrypted; maxDuration = std::max(maxDuration, mAudioReader->GetDecoder()->GetMediaDuration()); MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata audio reader=%p maxDuration=%lld", this, mAudioReader.get(), maxDuration); @@ -893,6 +919,7 @@ const MediaInfo& info = mVideoReader->GetMediaInfo(); MOZ_ASSERT(info.HasVideo()); mInfo.mVideo = info.mVideo; + mInfo.mIsEncrypted = mInfo.mIsEncrypted || info.mIsEncrypted; maxDuration = std::max(maxDuration, mVideoReader->GetDecoder()->GetMediaDuration()); MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata video reader=%p maxDuration=%lld", this, mVideoReader.get(), maxDuration); @@ -961,6 +988,42 @@ mMediaSourceDuration = aDuration; } +void +MediaSourceReader::GetMozDebugReaderData(nsAString& aString) +{ + ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); + nsAutoCString result; + result += nsPrintfCString("Dumping data for reader %p:\n", this); + if (mAudioTrack) { + result += nsPrintfCString("\tDumping Audio Track Decoders: - mLastAudioTime: %f\n", double(mLastAudioTime) / USECS_PER_S); + for (int32_t i = mAudioTrack->Decoders().Length() - 1; i >= 0; --i) { + nsRefPtr newReader = mAudioTrack->Decoders()[i]->GetReader(); + + nsRefPtr ranges = new dom::TimeRanges(); + mAudioTrack->Decoders()[i]->GetBuffered(ranges); + result += nsPrintfCString("\t\tReader %d: %p ranges=%s active=%s size=%lld\n", + i, newReader.get(), DumpTimeRanges(ranges).get(), + newReader.get() == mAudioReader.get() ? "true" : "false", + mAudioTrack->Decoders()[i]->GetResource()->GetSize()); + } + } + + if (mVideoTrack) { + result += nsPrintfCString("\tDumping Video Track Decoders - mLastVideoTime: %f\n", double(mLastVideoTime) / USECS_PER_S); + for (int32_t i = mVideoTrack->Decoders().Length() - 1; i >= 0; --i) { + nsRefPtr newReader = mVideoTrack->Decoders()[i]->GetReader(); + + nsRefPtr ranges = new dom::TimeRanges(); + mVideoTrack->Decoders()[i]->GetBuffered(ranges); + result += nsPrintfCString("\t\tReader %d: %p ranges=%s active=%s size=%lld\n", + i, newReader.get(), DumpTimeRanges(ranges).get(), + newReader.get() == mVideoReader.get() ? "true" : "false", + mVideoTrack->Decoders()[i]->GetResource()->GetSize()); + } + } + aString += NS_ConvertUTF8toUTF16(result); +} + #ifdef MOZ_EME nsresult MediaSourceReader::SetCDMProxy(CDMProxy* aProxy) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/MediaSourceReader.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/MediaSourceReader.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/MediaSourceReader.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/MediaSourceReader.h 2015-02-03 14:33:42.000000000 +0000 @@ -61,9 +61,12 @@ void OnVideoDecoded(VideoData* aSample); void OnVideoNotDecoded(NotDecodedReason aReason); + void DoVideoSeek(); + void DoAudioSeek(); void OnVideoSeekCompleted(int64_t aTime); + void OnVideoSeekFailed(nsresult aResult); void OnAudioSeekCompleted(int64_t aTime); - void OnSeekFailed(nsresult aResult); + void OnAudioSeekFailed(nsresult aResult); virtual bool IsWaitForDataSupported() MOZ_OVERRIDE { return true; } virtual nsRefPtr WaitForData(MediaData::Type aType) MOZ_OVERRIDE; @@ -146,21 +149,50 @@ // Returns true if aReader is a currently active audio or video bool IsActiveReader(MediaDecoderReader* aReader); + // Returns a string describing the state of the MediaSource internal + // buffered data. Used for debugging purposes. + void GetMozDebugReaderData(nsAString& aString); + private: // Switch the current audio/video reader to the reader that - // contains aTarget (or up to aError after target). Both - // aTarget and aError are in microseconds. + // contains aTarget (or up to aTolerance after target). Both + // aTarget and aTolerance are in microseconds. enum SwitchReaderResult { READER_ERROR = -1, READER_EXISTING = 0, READER_NEW = 1, }; - SwitchReaderResult SwitchAudioReader(int64_t aTarget, int64_t aError = 0); - SwitchReaderResult SwitchVideoReader(int64_t aTarget, int64_t aError = 0); - void RequestAudioDataComplete(int64_t aTime); - void RequestAudioDataFailed(nsresult aResult); - void RequestVideoDataComplete(int64_t aTime); - void RequestVideoDataFailed(nsresult aResult); + + SwitchReaderResult SwitchAudioReader(int64_t aTarget); + SwitchReaderResult SwitchVideoReader(int64_t aTarget); + + void DoAudioRequest(); + void DoVideoRequest(); + + void CompleteAudioSeekAndDoRequest() + { + mAudioSeekRequest.Complete(); + DoAudioRequest(); + } + + void CompleteVideoSeekAndDoRequest() + { + mVideoSeekRequest.Complete(); + DoVideoRequest(); + } + + void CompleteAudioSeekAndRejectPromise() + { + mAudioSeekRequest.Complete(); + mAudioPromise.Reject(DECODE_ERROR, __func__); + } + + void CompleteVideoSeekAndRejectPromise() + { + mVideoSeekRequest.Complete(); + mVideoPromise.Reject(DECODE_ERROR, __func__); + } + // Will reject the MediaPromise with END_OF_STREAM if mediasource has ended // or with WAIT_FOR_DATA otherwise. void CheckForWaitOrEndOfStream(MediaData::Type aType, int64_t aTime /* microseconds */); @@ -168,11 +200,12 @@ // Return a reader from the set available in aTrackDecoders that has data // available in the range requested by aTarget. already_AddRefed SelectReader(int64_t aTarget, - int64_t aError, + int64_t aTolerance, const nsTArray>& aTrackDecoders); bool HaveData(int64_t aTarget, MediaData::Type aType); void AttemptSeek(); + bool IsSeeking() { return mPendingSeekTime != -1; } nsRefPtr mAudioReader; nsRefPtr mVideoReader; @@ -183,6 +216,9 @@ nsRefPtr mAudioTrack; nsRefPtr mVideoTrack; + MediaPromiseConsumerHolder mAudioRequest; + MediaPromiseConsumerHolder mVideoRequest; + MediaPromiseHolder mAudioPromise; MediaPromiseHolder mVideoPromise; @@ -201,13 +237,14 @@ int64_t mLastAudioTime; int64_t mLastVideoTime; + MediaPromiseConsumerHolder mAudioSeekRequest; + MediaPromiseConsumerHolder mVideoSeekRequest; + MediaPromiseHolder mSeekPromise; + // Temporary seek information while we wait for the data // to be added to the track buffer. - MediaPromiseHolder mSeekPromise; int64_t mPendingSeekTime; bool mWaitingForSeekData; - bool mAudioIsSeeking; - bool mVideoIsSeeking; int64_t mTimeThreshold; bool mDropAudioBeforeThreshold; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/MediaSourceUtils.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/MediaSourceUtils.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/MediaSourceUtils.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/MediaSourceUtils.cpp 2015-02-03 14:33:42.000000000 +0000 @@ -11,7 +11,6 @@ namespace mozilla { -#if defined(PR_LOGGING) nsCString DumpTimeRanges(dom::TimeRanges* aRanges) { @@ -31,6 +30,5 @@ return dump; } -#endif } // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/ResourceQueue.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/ResourceQueue.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/ResourceQueue.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/ResourceQueue.h 2015-02-03 14:33:42.000000000 +0000 @@ -9,7 +9,7 @@ #include #include "nsDeque.h" -#include "nsTArray.h" +#include "MediaData.h" #include "prlog.h" #ifdef PR_LOGGING @@ -36,8 +36,9 @@ // timepoint. struct ResourceItem { - ResourceItem(const uint8_t* aData, uint32_t aSize) { - mData.AppendElements(aData, aSize); + explicit ResourceItem(LargeDataBuffer* aData) + : mData(aData) + { } size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { @@ -45,12 +46,12 @@ size_t size = aMallocSizeOf(this); // size excluding this - size += mData.SizeOfExcludingThis(aMallocSizeOf); + size += mData->SizeOfExcludingThis(aMallocSizeOf); return size; } - nsTArray mData; + nsRefPtr mData; }; class ResourceQueueDeallocator : public nsDequeFunctor { @@ -87,9 +88,9 @@ uint32_t end = std::min(GetAtOffset(aOffset + aCount, nullptr) + 1, uint32_t(GetSize())); for (uint32_t i = start; i < end; ++i) { ResourceItem* item = ResourceAt(i); - uint32_t bytes = std::min(aCount, uint32_t(item->mData.Length() - offset)); + uint32_t bytes = std::min(aCount, uint32_t(item->mData->Length() - offset)); if (bytes != 0) { - memcpy(aDest, &item->mData[offset], bytes); + memcpy(aDest, &(*item->mData)[offset], bytes); offset = 0; aCount -= bytes; aDest += bytes; @@ -97,9 +98,9 @@ } } - void AppendItem(const uint8_t* aData, uint32_t aLength) { - mLogicalLength += aLength; - Push(new ResourceItem(aData, aLength)); + void AppendItem(LargeDataBuffer* aData) { + mLogicalLength += aData->Length(); + Push(new ResourceItem(aData)); } // Tries to evict at least aSizeToEvict from the queue up until @@ -107,7 +108,7 @@ uint32_t Evict(uint64_t aOffset, uint32_t aSizeToEvict) { SBR_DEBUG("ResourceQueue(%p)::Evict(aOffset=%llu, aSizeToEvict=%u)", this, aOffset, aSizeToEvict); - return EvictBefore(std::min(aOffset, (uint64_t)aSizeToEvict)); + return EvictBefore(std::min(aOffset, mOffset + (uint64_t)aSizeToEvict)); } uint32_t EvictBefore(uint64_t aOffset) { @@ -115,12 +116,12 @@ uint32_t evicted = 0; while (ResourceItem* item = ResourceAt(0)) { SBR_DEBUG("ResourceQueue(%p)::EvictBefore item=%p length=%d offset=%llu", - this, item, item->mData.Length(), mOffset); - if (item->mData.Length() + mOffset >= aOffset) { + this, item, item->mData->Length(), mOffset); + if (item->mData->Length() + mOffset >= aOffset) { break; } - mOffset += item->mData.Length(); - evicted += item->mData.Length(); + mOffset += item->mData->Length(); + evicted += item->mData->Length(); delete PopFront(); } return evicted; @@ -131,9 +132,9 @@ uint32_t evicted = 0; while (ResourceItem* item = ResourceAt(0)) { SBR_DEBUG("ResourceQueue(%p)::EvictAll item=%p length=%d offset=%llu", - this, item, item->mData.Length(), mOffset); - mOffset += item->mData.Length(); - evicted += item->mData.Length(); + this, item, item->mData->Length(), mOffset); + mOffset += item->mData->Length(); + evicted += item->mData->Length(); delete PopFront(); } return evicted; @@ -163,7 +164,7 @@ if (!fp) { return; } - fwrite(item->mData.Elements(), item->mData.Length(), 1, fp); + fwrite(item->mData->Elements(), item->mData->Length(), 1, fp); fclose(fp); } } @@ -186,13 +187,13 @@ ResourceItem* item = ResourceAt(i); // If the item contains the start of the offset we want to // break out of the loop. - if (item->mData.Length() + offset > aOffset) { + if (item->mData->Length() + offset > aOffset) { if (aResourceOffset) { *aResourceOffset = aOffset - offset; } return i; } - offset += item->mData.Length(); + offset += item->mData->Length(); } return GetSize(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/SourceBuffer.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/SourceBuffer.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/SourceBuffer.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/SourceBuffer.cpp 2015-02-03 14:33:42.000000000 +0000 @@ -7,6 +7,7 @@ #include "SourceBuffer.h" #include "AsyncEventRunner.h" +#include "MediaData.h" #include "MediaSourceUtils.h" #include "TrackBuffer.h" #include "mozilla/ErrorResult.h" @@ -19,6 +20,7 @@ #include "nsIRunnable.h" #include "nsThreadUtils.h" #include "prlog.h" +#include struct JSContext; class JSObject; @@ -36,14 +38,34 @@ #define MSE_API(...) #endif -// RangeRemoval must be synchronous if appendBuffer is also synchronous. -// While waiting for bug 1118589 to land, ensure RangeRemoval is synchronous -#define APPENDBUFFER_IS_SYNCHRONOUS - namespace mozilla { namespace dom { +class AppendDataRunnable : public nsRunnable { +public: + AppendDataRunnable(SourceBuffer* aSourceBuffer, + LargeDataBuffer* aData, + double aTimestampOffset) + : mSourceBuffer(aSourceBuffer) + , mData(aData) + , mTimestampOffset(aTimestampOffset) + { + } + + NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL { + + mSourceBuffer->AppendData(mData, mTimestampOffset); + + return NS_OK; + } + +private: + nsRefPtr mSourceBuffer; + nsRefPtr mData; + double mTimestampOffset; +}; + class RangeRemovalRunnable : public nsRunnable { public: RangeRemovalRunnable(SourceBuffer* aSourceBuffer, @@ -240,18 +262,8 @@ { StartUpdating(); -#if defined(APPENDBUFFER_IS_SYNCHRONOUS) - DoRangeRemoval(aStart, aEnd); - - // Run the final step of the buffer append algorithm asynchronously to - // ensure the SourceBuffer's updating flag transition behaves as required - // by the spec. - nsCOMPtr event = NS_NewRunnableMethod(this, &SourceBuffer::StopUpdating); - NS_DispatchToMainThread(event); -#else nsRefPtr task = new RangeRemovalRunnable(this, aStart, aEnd); NS_DispatchToMainThread(task); -#endif } void @@ -267,9 +279,7 @@ mTrackBuffer->RangeRemoval(start, end); } -#if !defined(APPENDBUFFER_IS_SYNCHRONOUS) StopUpdating(); -#endif } void @@ -398,17 +408,38 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv) { MSE_DEBUG("SourceBuffer(%p)::AppendData(aLength=%u)", this, aLength); - if (!PrepareAppend(aRv)) { + + nsRefPtr data = PrepareAppend(aData, aLength, aRv); + if (!data) { return; } StartUpdating(); MOZ_ASSERT(mAppendMode == SourceBufferAppendMode::Segments, "We don't handle timestampOffset for sequence mode yet"); - if (aLength) { - if (!mTrackBuffer->AppendData(aData, aLength, mTimestampOffset * USECS_PER_S)) { - nsCOMPtr event = NS_NewRunnableMethodWithArg(this, &SourceBuffer::AppendError, true); - NS_DispatchToMainThread(event); + nsRefPtr task = + new AppendDataRunnable(this, data, mTimestampOffset); + NS_DispatchToMainThread(task); +} + +void +SourceBuffer::AppendData(LargeDataBuffer* aData, double aTimestampOffset) +{ + if (!mUpdating) { + // The buffer append algorithm has been interrupted by abort(). + // + // If the sequence appendBuffer(), abort(), appendBuffer() occurs before + // the first StopUpdating() runnable runs, then a second StopUpdating() + // runnable will be scheduled, but still only one (the first) will queue + // events. + return; + } + + MOZ_ASSERT(mMediaSource); + + if (aData->Length()) { + if (!mTrackBuffer->AppendData(aData, aTimestampOffset * USECS_PER_S)) { + AppendError(true); return; } @@ -419,12 +450,8 @@ CheckEndTime(); } - // Run the final step of the buffer append algorithm asynchronously to - // ensure the SourceBuffer's updating flag transition behaves as required - // by the spec. - nsCOMPtr event = NS_NewRunnableMethod(this, &SourceBuffer::StopUpdating); - NS_DispatchToMainThread(event); -} + StopUpdating(); + } void SourceBuffer::AppendError(bool aDecoderError) @@ -449,12 +476,12 @@ } } -bool -SourceBuffer::PrepareAppend(ErrorResult& aRv) +already_AddRefed +SourceBuffer::PrepareAppend(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv) { if (!IsAttached() || mUpdating) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); - return false; + return nullptr; } if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) { mMediaSource->SetReadyState(MediaSourceReadyState::Open); @@ -469,9 +496,13 @@ // TODO: Drive evictions off memory pressure notifications. // TODO: Consider a global eviction threshold rather than per TrackBuffer. double newBufferStartTime = 0.0; + // Attempt to evict the amount of data we are about to add by lowering the + // threshold. + uint32_t toEvict = + (mEvictionThreshold > aLength) ? mEvictionThreshold - aLength : aLength; bool evicted = mTrackBuffer->EvictData(mMediaSource->GetDecoder()->GetCurrentTime(), - mEvictionThreshold, &newBufferStartTime); + toEvict, &newBufferStartTime); if (evicted) { MSE_DEBUG("SourceBuffer(%p)::AppendData Evict; current buffered start=%f", this, GetBufferedStart()); @@ -481,8 +512,13 @@ mMediaSource->NotifyEvicted(0.0, newBufferStartTime); } + nsRefPtr data = new LargeDataBuffer(); + if (!data->AppendElements(aData, aLength)) { + aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR); + return nullptr; + } // TODO: Test buffer full flag. - return true; + return data.forget(); } double diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/SourceBufferDecoder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/SourceBufferDecoder.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/SourceBufferDecoder.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/SourceBufferDecoder.cpp 2015-02-03 14:33:42.000000000 +0000 @@ -8,6 +8,7 @@ #include "prlog.h" #include "AbstractMediaDecoder.h" #include "MediaDecoderReader.h" +#include "mozilla/dom/TimeRanges.h" #ifdef PR_LOGGING extern PRLogModuleInfo* GetMediaSourceLog(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/SourceBuffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/SourceBuffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/SourceBuffer.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/SourceBuffer.h 2015-02-03 14:33:42.000000000 +0000 @@ -29,6 +29,7 @@ namespace mozilla { class ErrorResult; +class LargeDataBuffer; class TrackBuffer; template class AsyncEventRunner; @@ -122,6 +123,7 @@ ~SourceBuffer(); friend class AsyncEventRunner; + friend class AppendDataRunnable; void DispatchSimpleEvent(const char* aName); void QueueAsyncSimpleEvent(const char* aName); @@ -137,6 +139,7 @@ // Shared implementation of AppendBuffer overloads. void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv); + void AppendData(LargeDataBuffer* aData, double aTimestampOffset); // Implement the "Append Error Algorithm". // Will call endOfStream() with "decode" error if aDecodeError is true. @@ -144,9 +147,11 @@ // http://w3c.github.io/media-source/#sourcebuffer-append-error void AppendError(bool aDecoderError); - // Implements the "Prepare Append Algorithm". Returns true if the append - // may continue, or false (with aRv set) on error. - bool PrepareAppend(ErrorResult& aRv); + // Implements the "Prepare Append Algorithm". Returns LargeDataBuffer object + // on success or nullptr (with aRv set) on error. + already_AddRefed PrepareAppend(const uint8_t* aData, + uint32_t aLength, + ErrorResult& aRv); nsRefPtr mMediaSource; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/SourceBufferResource.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/SourceBufferResource.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/SourceBufferResource.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/SourceBufferResource.cpp 2015-02-03 14:33:42.000000000 +0000 @@ -11,6 +11,7 @@ #include "nsISeekableStream.h" #include "nsISupports.h" #include "prlog.h" +#include "MediaData.h" #ifdef PR_LOGGING PRLogModuleInfo* GetSourceBufferResourceLog() @@ -200,11 +201,12 @@ } void -SourceBufferResource::AppendData(const uint8_t* aData, uint32_t aLength) +SourceBufferResource::AppendData(LargeDataBuffer* aData) { - SBR_DEBUG("SourceBufferResource(%p)::AppendData(aData=%p, aLength=%u)", this, aData, aLength); + SBR_DEBUG("SourceBufferResource(%p)::AppendData(aData=%p, aLength=%u)", this, + aData->Elements(), aData->Length()); ReentrantMonitorAutoEnter mon(mMonitor); - mInputBuffer.AppendItem(aData, aLength); + mInputBuffer.AppendItem(aData); mEnded = false; mon.NotifyAll(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/SourceBufferResource.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/SourceBufferResource.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/SourceBufferResource.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/SourceBufferResource.h 2015-02-03 14:33:42.000000000 +0000 @@ -36,6 +36,7 @@ namespace mozilla { class MediaDecoder; +class LargeDataBuffer; namespace dom { @@ -111,7 +112,7 @@ } // Used by SourceBuffer. - void AppendData(const uint8_t* aData, uint32_t aLength); + void AppendData(LargeDataBuffer* aData); void Ended(); // Remove data from resource if it holds more than the threshold // number of bytes. Returns amount evicted. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/TrackBuffer.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/TrackBuffer.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/TrackBuffer.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/TrackBuffer.cpp 2015-02-03 14:33:42.000000000 +0000 @@ -7,6 +7,7 @@ #include "TrackBuffer.h" #include "ContainerParser.h" +#include "MediaData.h" #include "MediaSourceDecoder.h" #include "SharedThreadPool.h" #include "MediaTaskQueue.h" @@ -144,12 +145,12 @@ } bool -TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength, int64_t aTimestampOffset) +TrackBuffer::AppendData(LargeDataBuffer* aData, int64_t aTimestampOffset) { MOZ_ASSERT(NS_IsMainThread()); DecodersToInitialize decoders(this); // TODO: Run more of the buffer append algorithm asynchronously. - if (mParser->IsInitSegmentPresent(aData, aLength)) { + if (mParser->IsInitSegmentPresent(aData)) { MSE_DEBUG("TrackBuffer(%p)::AppendData: New initialization segment.", this); if (!decoders.NewDecoder(aTimestampOffset)) { return false; @@ -160,14 +161,14 @@ } int64_t start, end; - if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) { + if (mParser->ParseStartAndEndTimestamps(aData, start, end)) { start += aTimestampOffset; end += aTimestampOffset; - if (mParser->IsMediaSegmentPresent(aData, aLength) && + if (mParser->IsMediaSegmentPresent(aData) && mLastEndTimestamp && (!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value()) || mLastTimestampOffset != aTimestampOffset || - mDecoderPerSegment || mCurrentDecoder->WasTrimmed())) { + mDecoderPerSegment || (mCurrentDecoder && mCurrentDecoder->WasTrimmed()))) { MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]", this, mLastStartTimestamp, mLastEndTimestamp.value(), start, end); @@ -177,8 +178,8 @@ return false; } MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this); - const nsTArray& initData = mParser->InitData(); - AppendDataToCurrentResource(initData.Elements(), initData.Length(), end - start); + nsRefPtr initData = mParser->InitData(); + AppendDataToCurrentResource(initData, end - start); mLastStartTimestamp = start; } else { MSE_DEBUG("TrackBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]", @@ -188,7 +189,7 @@ mLastEndTimestamp.emplace(end); } - if (!AppendDataToCurrentResource(aData, aLength, end - start)) { + if (!AppendDataToCurrentResource(aData, end - start)) { return false; } @@ -199,7 +200,7 @@ } bool -TrackBuffer::AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength, uint32_t aDuration) +TrackBuffer::AppendDataToCurrentResource(LargeDataBuffer* aData, uint32_t aDuration) { MOZ_ASSERT(NS_IsMainThread()); if (!mCurrentDecoder) { @@ -208,11 +209,11 @@ SourceBufferResource* resource = mCurrentDecoder->GetResource(); int64_t appendOffset = resource->GetLength(); - resource->AppendData(aData, aLength); + resource->AppendData(aData); mCurrentDecoder->SetRealMediaDuration(mCurrentDecoder->GetRealMediaDuration() + aDuration); // XXX: For future reference: NDA call must run on the main thread. - mCurrentDecoder->NotifyDataArrived(reinterpret_cast(aData), - aLength, appendOffset); + mCurrentDecoder->NotifyDataArrived(reinterpret_cast(aData->Elements()), + aData->Length(), appendOffset); mParentDecoder->NotifyBytesDownloaded(); mParentDecoder->NotifyTimeRangesChanged(); @@ -583,13 +584,14 @@ } bool -TrackBuffer::ContainsTime(int64_t aTime) +TrackBuffer::ContainsTime(int64_t aTime, int64_t aTolerance) { ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor()); for (uint32_t i = 0; i < mInitializedDecoders.Length(); ++i) { nsRefPtr r = new dom::TimeRanges(); mInitializedDecoders[i]->GetBuffered(r); - if (r->Find(double(aTime) / USECS_PER_S) != dom::TimeRanges::NoIndex) { + if (r->Find(double(aTime) / USECS_PER_S, + double(aTolerance) / USECS_PER_S) != dom::TimeRanges::NoIndex) { return true; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/TrackBuffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/TrackBuffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/mediasource/TrackBuffer.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/mediasource/TrackBuffer.h 2015-02-03 14:33:42.000000000 +0000 @@ -21,6 +21,7 @@ class ContainerParser; class MediaSourceDecoder; +class LargeDataBuffer; namespace dom { @@ -39,7 +40,7 @@ // Append data to the current decoder. Also responsible for calling // NotifyDataArrived on the decoder to keep buffered range computation up // to date. Returns false if the append failed. - bool AppendData(const uint8_t* aData, uint32_t aLength, int64_t aTimestampOffset /* microseconds */); + bool AppendData(LargeDataBuffer* aData, int64_t aTimestampOffset /* microseconds */); // Evicts data held in the current decoders SourceBufferResource from the // start of the buffer through to aPlaybackTime. aThreshold is used to @@ -75,7 +76,7 @@ // Returns true if any of the decoders managed by this track buffer // contain aTime in their buffered ranges. - bool ContainsTime(int64_t aTime); + bool ContainsTime(int64_t aTime, int64_t aTolerance); void BreakCycles(); @@ -120,7 +121,7 @@ // Helper for AppendData, ensures NotifyDataArrived is called whenever // data is appended to the current decoder's SourceBufferResource. - bool AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength, + bool AppendDataToCurrentResource(LargeDataBuffer* aData, uint32_t aDuration /* microseconds */); // Queue execution of InitializeDecoder on mTaskQueue. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaStreamGraph.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaStreamGraph.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaStreamGraph.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaStreamGraph.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -2477,12 +2477,9 @@ SourceMediaStream::EndTrack(TrackID aID) { MutexAutoLock lock(mMutex); - // ::EndAllTrackAndFinished() can end these before the sources call this - if (!mFinished) { - TrackData *track = FindDataForTrack(aID); - if (track) { - track->mCommands |= TRACK_END; - } + TrackData *track = FindDataForTrack(aID); + if (track) { + track->mCommands |= TRACK_END; } if (auto graph = GraphImpl()) { graph->EnsureNextIteration(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaStreamTrack.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaStreamTrack.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaStreamTrack.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaStreamTrack.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -15,14 +15,20 @@ MediaStreamTrack::MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID) : mStream(aStream), mTrackID(aTrackID), mEnded(false), mEnabled(true) { - memset(&mID, 0, sizeof(mID)); nsresult rv; nsCOMPtr uuidgen = do_GetService("@mozilla.org/uuid-generator;1", &rv); + + nsID uuid; + memset(&uuid, 0, sizeof(uuid)); if (uuidgen) { - uuidgen->GenerateUUIDInPlace(&mID); + uuidgen->GenerateUUIDInPlace(&uuid); } + + char chars[NSID_LENGTH]; + uuid.ToProvidedString(chars); + mID = NS_ConvertASCIItoUTF16(chars); } MediaStreamTrack::~MediaStreamTrack() @@ -38,11 +44,9 @@ NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) void -MediaStreamTrack::GetId(nsAString& aID) +MediaStreamTrack::GetId(nsAString& aID) const { - char chars[NSID_LENGTH]; - mID.ToProvidedString(chars); - aID = NS_ConvertASCIItoUTF16(chars); + aID = mID; } void diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaStreamTrack.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaStreamTrack.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MediaStreamTrack.h 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MediaStreamTrack.h 2015-02-03 14:33:40.000000000 +0000 @@ -44,7 +44,7 @@ // WebIDL virtual void GetKind(nsAString& aKind) = 0; - void GetId(nsAString& aID); + void GetId(nsAString& aID) const; void GetLabel(nsAString& aLabel) { aLabel.Truncate(); } bool Enabled() { return mEnabled; } void SetEnabled(bool aEnabled); @@ -53,12 +53,16 @@ // Notifications from the MediaStreamGraph void NotifyEnded() { mEnded = true; } + // Webrtc allows the remote side to name tracks whatever it wants, and we + // need to surface this to content. + void AssignId(const nsAString& aID) { mID = aID; } + protected: virtual ~MediaStreamTrack(); nsRefPtr mStream; TrackID mTrackID; - nsID mID; + nsString mID; bool mEnded; bool mEnabled; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MP3FrameParser.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MP3FrameParser.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/MP3FrameParser.cpp 2015-01-25 22:24:34.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/MP3FrameParser.cpp 2015-02-03 14:33:40.000000000 +0000 @@ -337,6 +337,11 @@ // Found an ID3 header. We don't care about the body of the header, so // just skip past. buffer = ch + mID3Parser.GetHeaderLength() - (ID3_HEADER_LENGTH - 1); + + if (buffer <= ch) { + return NS_ERROR_FAILURE; + } + ch = buffer; mTotalID3Size += mID3Parser.GetHeaderLength(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/omx/MediaCodecReader.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/omx/MediaCodecReader.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/omx/MediaCodecReader.cpp 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/omx/MediaCodecReader.cpp 2015-02-03 14:33:42.000000000 +0000 @@ -41,6 +41,7 @@ #include "VideoUtils.h" using namespace android; +using namespace mozilla::layers; namespace mozilla { @@ -758,12 +759,20 @@ MediaCodecReader::ResetDecode() { if (CheckAudioResources()) { - mAudioTrack.mTaskQueue->AwaitIdle(); + mAudioTrack.mTaskQueue->Flush(); + MonitorAutoLock al(mAudioTrack.mTrackMonitor); + if (!mAudioTrack.mAudioPromise.IsEmpty()) { + mAudioTrack.mAudioPromise.Reject(CANCELED, __func__); + } FlushCodecData(mAudioTrack); mAudioTrack.mDiscontinuity = true; } if (CheckVideoResources()) { - mVideoTrack.mTaskQueue->AwaitIdle(); + mVideoTrack.mTaskQueue->Flush(); + MonitorAutoLock al(mVideoTrack.mTrackMonitor); + if (!mVideoTrack.mVideoPromise.IsEmpty()) { + mVideoTrack.mVideoPromise.Reject(CANCELED, __func__); + } FlushCodecData(mVideoTrack); mVideoTrack.mDiscontinuity = true; } @@ -798,11 +807,49 @@ if (!mTextureClientIndexes.Get(aClient, &index)) { return; } + +#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17 + sp fence = aClient->GetReleaseFenceHandle().mFence; + if (fence.get() && fence->isValid()) { + mPendingReleaseItems.AppendElement(ReleaseItem(index, fence)); + } else { + mPendingReleaseItems.AppendElement(ReleaseItem(index, nullptr)); + } +#else + mPendingReleaseItems.AppendElement(ReleaseItem(index)); +#endif mTextureClientIndexes.Remove(aClient); } - if (mVideoTrack.mCodec != nullptr) { - mVideoTrack.mCodec->releaseOutputBuffer(index); + if (mVideoTrack.mReleaseBufferTaskQueue->IsEmpty()) { + RefPtr task = + NS_NewRunnableMethod(this, + &MediaCodecReader::WaitFenceAndReleaseOutputBuffer); + mVideoTrack.mReleaseBufferTaskQueue->Dispatch(task); + } +} + +void +MediaCodecReader::WaitFenceAndReleaseOutputBuffer() +{ + nsTArray releasingItems; + { + MutexAutoLock autoLock(mTextureClientIndexesLock); + releasingItems.AppendElements(mPendingReleaseItems); + mPendingReleaseItems.Clear(); + } + + for (size_t i = 0; i < releasingItems.Length(); i++) { +#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17 + sp fence; + fence = releasingItems[i].mReleaseFence; + if (fence.get() && fence->isValid()) { + fence->waitForever("MediaCodecReader"); + } +#endif + if (mVideoTrack.mCodec != nullptr) { + mVideoTrack.mCodec->releaseOutputBuffer(releasingItems[i].mReleaseIndex); + } } } @@ -1242,16 +1289,21 @@ void MediaCodecReader::ShutdownTaskQueues() { - if(mAudioTrack.mTaskQueue) { + if (mAudioTrack.mTaskQueue) { mAudioTrack.mTaskQueue->BeginShutdown(); mAudioTrack.mTaskQueue->AwaitShutdownAndIdle(); mAudioTrack.mTaskQueue = nullptr; } - if(mVideoTrack.mTaskQueue) { + if (mVideoTrack.mTaskQueue) { mVideoTrack.mTaskQueue->BeginShutdown(); mVideoTrack.mTaskQueue->AwaitShutdownAndIdle(); mVideoTrack.mTaskQueue = nullptr; } + if (mVideoTrack.mReleaseBufferTaskQueue) { + mVideoTrack.mReleaseBufferTaskQueue->BeginShutdown(); + mVideoTrack.mReleaseBufferTaskQueue->AwaitShutdownAndIdle(); + mVideoTrack.mReleaseBufferTaskQueue = nullptr; + } } bool @@ -1266,6 +1318,8 @@ !mVideoTrack.mTaskQueue) { mVideoTrack.mTaskQueue = CreateMediaDecodeTaskQueue(); NS_ENSURE_TRUE(mVideoTrack.mTaskQueue, false); + mVideoTrack.mReleaseBufferTaskQueue = CreateMediaDecodeTaskQueue(); + NS_ENSURE_TRUE(mVideoTrack.mReleaseBufferTaskQueue, false); } return true; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/omx/MediaCodecReader.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/omx/MediaCodecReader.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/omx/MediaCodecReader.h 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/omx/MediaCodecReader.h 2015-02-03 14:33:42.000000000 +0000 @@ -22,6 +22,10 @@ #include "I420ColorConverterHelper.h" #include "MediaCodecProxy.h" #include "MediaOmxCommonReader.h" +#include "mozilla/layers/FenceUtils.h" +#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17 +#include +#endif namespace android { struct ALooper; @@ -47,6 +51,7 @@ class MediaCodecReader : public MediaOmxCommonReader { typedef mozilla::layers::TextureClient TextureClient; + typedef mozilla::layers::FenceHandle FenceHandle; public: MediaCodecReader(AbstractMediaDecoder* aDecoder); @@ -264,6 +269,7 @@ // Protected by mTrackMonitor. MediaPromiseHolder mVideoPromise; + nsRefPtr mReleaseBufferTaskQueue; private: // Forbidden VideoTrack(const VideoTrack &rhs) = delete; @@ -415,6 +421,7 @@ static void TextureClientRecycleCallback(TextureClient* aClient, void* aClosure); void TextureClientRecycleCallback(TextureClient* aClient); + void WaitFenceAndReleaseOutputBuffer(); void ReleaseRecycledTextureClients(); static PLDHashOperator ReleaseTextureClient(TextureClient* aClient, @@ -450,6 +457,26 @@ int64_t mNextParserPosition; int64_t mParsedDataLength; nsAutoPtr mMP3FrameParser; +#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17 + // mReleaseIndex corresponding to a graphic buffer, and the mReleaseFence is + // the graohic buffer's fence. We must wait for the fence signaled by + // compositor, otherwise we will see the flicker because the HW decoder and + // compositor use the buffer concurrently. + struct ReleaseItem { + ReleaseItem(size_t aIndex, const android::sp& aFence) + : mReleaseIndex(aIndex) + , mReleaseFence(aFence) {} + size_t mReleaseIndex; + android::sp mReleaseFence; + }; +#else + struct ReleaseItem { + ReleaseItem(size_t aIndex) + : mReleaseIndex(aIndex) {} + size_t mReleaseIndex; + }; +#endif + nsTArray mPendingReleaseItems; }; } // namespace mozilla diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/allowed.sjs thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/allowed.sjs --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/allowed.sjs 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/allowed.sjs 2015-02-03 14:33:42.000000000 +0000 @@ -13,6 +13,9 @@ } var types = { + js: "text/javascript", + m4s: "video/mp4", + mp4: "video/mp4", ogg: "video/ogg", ogv: "video/ogg", oga: "audio/ogg", diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/eme.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/eme.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/eme.js 2015-01-25 22:24:35.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/eme.js 2015-02-03 14:33:42.000000000 +0000 @@ -114,6 +114,15 @@ } } +function MaybeCrossOriginURI(test, uri) +{ + if (test.crossOrigin) { + return "http://test2.mochi.test:8888/tests/dom/media/test/allowed.sjs?" + uri; + } else { + return uri; + } +} + function PlayFragmented(test, elem, token) { return new Promise(function(resolve, reject) { @@ -140,7 +149,7 @@ return; } - var fragmentFile = test.fragments[curFragment++]; + var fragmentFile = MaybeCrossOriginURI(test, test.fragments[curFragment++]); var req = new XMLHttpRequest(); req.open("GET", fragmentFile); @@ -179,7 +188,7 @@ // This file isn't fragmented; set the media source normally. return new Promise(function(resolve, reject) { - elem.src = test.name; + elem.src = MaybeCrossOriginURI(test, test.name); resolve(); }); } @@ -187,6 +196,7 @@ function SetupEME(test, token, params) { var v = document.createElement("video"); + v.crossOrigin = test.crossOrigin || false; // Log events dispatched to make debugging easier... [ "canplay", "canplaythrough", "ended", "error", "loadeddata", diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/manifest.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/manifest.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/manifest.js 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/manifest.js 2015-02-03 14:33:42.000000000 +0000 @@ -96,6 +96,10 @@ { name:"bogus.duh", type:"bogus/duh" } ]; +var gClosingConnectionsTest = [ + { name:"seek.ogv", type:"video/ogg", duration:3.966 } +]; + // Used by any media recorder test. Need one test file per decoder backend // currently supported by the media encoder. var gMediaRecorderTests = [ @@ -653,6 +657,30 @@ duration:0.47 }, { + name:"short-cenc.mp4", + type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"", + keys: { + // "keyid" : "key" + "7e571d017e571d017e571d017e571d01" : "7e5711117e5711117e5711117e571111", + "7e571d027e571d027e571d027e571d02" : "7e5722227e5722227e5722227e572222", + }, + sessionType:"temporary", + duration:0.47, + crossOrigin:true, + }, + { + name:"gizmo-frag-cencinit.mp4", + fragments: [ "gizmo-frag-cencinit.mp4", "gizmo-frag-cenc1.m4s", "gizmo-frag-cenc2.m4s" ], + type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"", + keys: { + // "keyid" : "key" + "7e571d037e571d037e571d037e571d03" : "7e5733337e5733337e5733337e573333", + "7e571d047e571d047e571d047e571d04" : "7e5744447e5744447e5744447e574444", + }, + sessionType:"temporary", + duration:2.00, + }, + { name:"gizmo-frag-cencinit.mp4", fragments: [ "gizmo-frag-cencinit.mp4", "gizmo-frag-cenc1.m4s", "gizmo-frag-cenc2.m4s" ], type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"", @@ -663,6 +691,7 @@ }, sessionType:"temporary", duration:2.00, + crossOrigin:true, }, ]; @@ -677,6 +706,8 @@ ok(Math.abs(e.duration - test.duration) < 0.1, msg + " duration (" + e.duration + ") should be around " + test.duration); } + is(!!test.keys, SpecialPowers.do_lookupGetter(e, "isEncrypted").apply(e), + msg + " isEncrypted should be true if we have decryption keys"); } // Returns the first test from candidates array which we can play with the diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/mochitest.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/mochitest.ini --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/mochitest.ini 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/mochitest.ini 2015-02-03 14:33:42.000000000 +0000 @@ -364,8 +364,12 @@ [test_defaultMuted.html] [test_delay_load.html] skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984 +[test_eme_access_control.html] +skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908 [test_eme_canvas_blocked.html] skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908 +[test_eme_obs_notification.html] +skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908 [test_eme_persistent_sessions.html] skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908 [test_eme_playback.html] diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_bug495300.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_bug495300.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_bug495300.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_bug495300.html 2015-02-03 14:33:43.000000000 +0000 @@ -28,7 +28,14 @@ ok(Math.abs(event.target.currentTime - event.target._expectedDuration) < 0.1, "currentTime equals duration: " + filename(event.target.currentSrc)); + event.target.removeEventListener("ended", mediaEnded, false); manager.finished(event.target.token); + removeNodeAndSource(event.target); +} + +function mediaLoadedmetadata(event) { + event.target.currentTime = event.target.duration; + event.target.removeEventListener("loadedmetadata", mediaLoadedmetadata, false); } function startTest(test, token) { @@ -40,9 +47,7 @@ if (test.duration) { v1._expectedDuration = test.duration; } - v1.addEventListener("loadedmetadata", function (event) { - event.target.currentTime = event.target.duration; - }, false); + v1.addEventListener("loadedmetadata", mediaLoadedmetadata, false); v1.addEventListener("ended", mediaEnded, false); v1.load(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_closing_connections.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_closing_connections.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_closing_connections.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_closing_connections.html 2015-02-03 14:33:43.000000000 +0000 @@ -32,7 +32,7 @@ we've got the first frame. */ -var resource = getPlayableVideo(gSeekTests); +var resource = getPlayableVideo(gClosingConnectionsTest); SimpleTest.waitForExplicitFinish(); function beginTest() { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_eme_access_control.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_eme_access_control.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_eme_access_control.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_eme_access_control.html 2015-02-03 14:33:43.000000000 +0000 @@ -0,0 +1,112 @@ + + + + Test EME blocked cross-origin + + + + + + +
    +
    +
    + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_eme_obs_notification.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_eme_obs_notification.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_eme_obs_notification.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_eme_obs_notification.html 2015-02-03 14:33:43.000000000 +0000 @@ -0,0 +1,81 @@ + + + + Test Encrypted Media Extensions + + + + + + +
    +
    +
    + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_eme_persistent_sessions.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_eme_persistent_sessions.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_eme_persistent_sessions.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_eme_persistent_sessions.html 2015-02-03 14:33:43.000000000 +0000 @@ -31,14 +31,17 @@ function AwaitAllKeysUsable(session, keys, token) { return new Promise(function(resolve, reject) { function listener(event) { - session.getUsableKeyIds().then(function(usableKeyIds) { - var u = UsableKeyIdsMatch(usableKeyIds, keys); - if (UsableKeyIdsMatch(usableKeyIds, keys)) { - Log(token, "resolving AwaitAllKeysUsable promise"); - session.removeEventListener("keyschange", listener); - resolve(); - } - }, bail(token + " failed to get usableKeyIds")); + var map = session.keyStatuses; + var usableKeyIds = []; + for (var [key, val] of map.entries()) { + is(val, "usable", token + ": key status should be usable"); + usableKeyIds.push(key); + } + if (UsableKeyIdsMatch(usableKeyIds, keys)) { + Log(token, "resolving AwaitAllKeysUsable promise"); + session.removeEventListener("keyschange", listener); + resolve(); + } } session.addEventListener("keyschange", listener); }); @@ -47,12 +50,11 @@ function AwaitAllKeysNotUsable(session, token) { return new Promise(function(resolve, reject) { function listener(event) { - session.getUsableKeyIds().then(function(usableKeyIds) { - if (usableKeyIds.length == 0) { - session.removeEventListener("keyschange", listener); - resolve(); - } - }, bail(token + " failed to get usableKeyIds")); + var map = session.keyStatuses; + if (map.size == 0) { + session.removeEventListener("keyschange", listener); + resolve(); + } } session.addEventListener("keyschange", listener); }); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_eme_playback.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_eme_playback.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_eme_playback.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_eme_playback.html 2015-02-03 14:33:43.000000000 +0000 @@ -5,7 +5,7 @@ - +
    @@ -22,13 +22,14 @@
       return function(ev) {
         var session = ev.target;
         session.gotKeysChanged = true;
    -    session.getUsableKeyIds().then(function(keyIds) {
    -      for (var k = 0; k < keyIds.length; k++) {
    -        var kid = Base64ToHex(window.btoa(ArrayBufferToString(keyIds[k])));
    -        ok(kid in session.keyIdsReceived, TimeStamp(token) + " session.keyIdsReceived contained " + kid + " as expected.");
    -        session.keyIdsReceived[kid] = true;
    -      }
    -    }, bail("Failed to get keyIds"));
    +
    +    var map = session.keyStatuses;
    +    for (var [key, val] of map.entries()) {
    +      is(val, "usable", token + ": key status should be usable");
    +      var kid = Base64ToHex(window.btoa(ArrayBufferToString(key)));
    +      ok(kid in session.keyIdsReceived, TimeStamp(token) + " session.keyIdsReceived contained " + kid + " as expected.");
    +      session.keyIdsReceived[kid] = true;
    +    }
       }
     }
     
    @@ -56,6 +57,12 @@
     
       v.addEventListener("playing", function () { gotPlaying = true; });
     
    +  v.addEventListener("loadedmetadata", function() {
    +    ok(SpecialPowers.do_lookupGetter(v, "isEncrypted").apply(v),
    +       TimeStamp(token) + " isEncrypted should be true");
    +    is(v.isEncrypted, undefined, "isEncrypted should not be accessible from content");
    +  });
    +
       v.addEventListener("ended", function(ev) {
         ok(true, TimeStamp(token) + " got ended event");
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_empty_resource.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_empty_resource.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_empty_resource.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_empty_resource.html	2015-02-03 14:33:43.000000000 +0000
    @@ -25,8 +25,8 @@
     }
     
     // We expect this test to finish very soon.
    -// A timeout of 15s should be enough for the slow B2G emulator.
    -var timer = setTimeout(onTimeout, 15000);
    +// A timeout of 90s should be enough for the slow B2G emulator.
    +var timer = setTimeout(onTimeout, 90000);
     
     function finish(v) {
       clearTimeout(timer);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_fastSeek-forwards.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_fastSeek-forwards.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_fastSeek-forwards.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_fastSeek-forwards.html	2015-02-03 14:33:43.000000000 +0000
    @@ -29,8 +29,10 @@
     
         var onSecondSeekComplete = function(event) {
           var v = event.target;
    +      v.removeEventListener("seeked", onSecondSeekComplete);
           ok(v.currentTime >= v.firstSeekTarget, v.name + " seek never go backwards. time=" + v.currentTime + " firstSeekTarget=" + v.firstSeekTarget + " secondSeekTarget=" + v.secondSeekTarget);
           manager.finished(v.token);
    +      removeNodeAndSource(v);
         };
     
         var onFirstSeekComplete = function(event) {
    @@ -47,6 +49,7 @@
         var onLoadedMetadata = function(event) {
           // Seek to the mid-point between the start and the first keyframe.
           var v = event.target;
    +      v.removeEventListener("loadedmetadata", onLoadedMetadata);
           v.addEventListener("seeked", onFirstSeekComplete);
           v.firstSeekTarget = v.keyframes[1] * 0.5;
           v.currentTime = v.firstSeekTarget;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_reactivate.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_reactivate.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_reactivate.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_reactivate.html	2015-02-03 14:33:43.000000000 +0000
    @@ -39,6 +39,19 @@
     function loadedAll(elementList) {
       elements = elementList;
     
    +  // Log events for debugging.
    +  var events = ["suspend", "play", "canplay", "canplaythrough", "loadstart", "loadedmetadata",
    +                "loadeddata", "playing", "ended", "error", "stalled", "emptied", "abort",
    +                "waiting", "pause"];
    +  function logEvent(e) {
    +    info(e.target._name + ": got " + e.type);
    +  }
    +  elementList.forEach(function(element) {
    +    events.forEach(function(evt) {
    +      element.addEventListener(evt, logEvent, false);
    +    });
    +  });
    +
       // Blow away the subframe
       document.body.removeChild(document.getElementById("frame"));
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_streams_element_capture.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_streams_element_capture.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/test/test_streams_element_capture.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/test/test_streams_element_capture.html	2015-02-03 14:33:43.000000000 +0000
    @@ -9,8 +9,6 @@
     
     
     
    -  
    +  
       
    -  
     
     
     
     
    -  
    +  
       
    -  
       
    -  
     
     
    -
     
    -  
    +  
       
    -  
       
     
     
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/identity/test_setIdentityProviderWithErrors.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/identity/test_setIdentityProviderWithErrors.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/identity/test_setIdentityProviderWithErrors.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/identity/test_setIdentityProviderWithErrors.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,23 +1,20 @@ - - - + -
     
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoCombined.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoCombined.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoCombined.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoCombined.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoNoBundle.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoNoBundle.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoNoBundle.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoNoBundle.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    @@ -17,27 +11,22 @@
         title: "Basic data channel audio/video connection without bundle"
       });
     
    -  var test;
    -  runNetworkTest(function () {
    -    test = new PeerConnectionTest();
    -    addInitialDataChannel(test.chain);
    -    test.chain.insertAfter("PC_LOCAL_CREATE_OFFER",
    -      [[
    -        'PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER',
    -        function (test) {
    -          // Just replace a=group:BUNDLE with something that will be ignored.
    -          test.originalOffer.sdp = test.originalOffer.sdp.replace(
    -            "a=group:BUNDLE",
    -            "a=foo:");
    -          test.next();
    -        }
    -      ]]
    -      );
    -    test.setMediaConstraints([{audio: true}, {video: true}],
    -                             [{audio: true}, {video: true}]);
    -    test.run();
    -  });
    -
    +var test;
    +runNetworkTest(function () {
    +  test = new PeerConnectionTest();
    +  addInitialDataChannel(test.chain);
    +  test.chain.insertAfter("PC_LOCAL_CREATE_OFFER", [
    +    function PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER(test) {
    +      // Just replace a=group:BUNDLE with something that will be ignored.
    +      test.originalOffer.sdp = test.originalOffer.sdp.replace(
    +        "a=group:BUNDLE",
    +        "a=foo:");
    +    }
    +  ]);
    +  test.setMediaConstraints([{audio: true}, {video: true}],
    +                           [{audio: true}, {video: true}]);
    +  test.run();
    +});
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_dataChannel_basicDataOnly.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_dataChannel_basicDataOnly.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_dataChannel_basicDataOnly.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_dataChannel_basicDataOnly.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,13 +1,7 @@  - - - - - -
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_dataChannel_basicVideo.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_dataChannel_basicVideo.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_dataChannel_basicVideo.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_dataChannel_basicVideo.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_dataChannel_bug1013809.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_dataChannel_bug1013809.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_dataChannel_bug1013809.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_dataChannel_bug1013809.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_dataChannel_noOffer.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_dataChannel_noOffer.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_dataChannel_noOffer.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_dataChannel_noOffer.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,9 +1,6 @@
     
     
     
    -  
    -  
    -  
       
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,43 +1,26 @@
     
     
    -
     
    -  
    -  mozGetUserMedia Basic Audio Test
    -  
    -  
    -  
       
     
     
    -mozGetUserMedia Basic Audio Test
    -

    -
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_basicScreenshare.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_basicScreenshare.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_basicScreenshare.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_basicScreenshare.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,24 +1,15 @@
     
     
    -
     
    -  
    -  mozGetUserMedia Basic Screenshare Test
    -  
    -  
    -  
       
     
     
    -mozGetUserMedia Basic Screenshare Test
    -

    -
     
    -  
       
     
     
    -mozGetUserMedia Basic Video & Audio Test
    -

    -
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,43 +1,29 @@
     
     
    -
     
    -  
    -  mozGetUserMedia Basic Video Test
    -  
    -  
    -  
       
     
     
    -mozGetUserMedia Basic Video Test
    -

    -
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_basicWindowshare.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_basicWindowshare.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_basicWindowshare.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_basicWindowshare.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,24 +1,15 @@
     
     
    -
     
    -  
    -  mozGetUserMedia Basic Windowshare Test
    -  
    -  
    -  
       
     
     
    -mozGetUserMedia Basic Windowshare Test
    -

    -
     
    +
    +
    +
    +
    +
    + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_constraints.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_constraints.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_constraints.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_constraints.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,24 +1,13 @@ - - - Test mozGetUserMedia Constraints - - - - + + -Test mozGetUserMedia Constraints (desktop) -

    -
     
    -  
    -  
    +  
    +  
     
     
    -Test mozGetUserMedia Constraints (mobile)
    -

    -
     
    -  
       
     
     
    -mozGetUserMedia gum within gum
    -

    -
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_peerIdentity.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_peerIdentity.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_peerIdentity.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_peerIdentity.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,24 +1,13 @@
     
     
    -
     
    -  
    -  Test mozGetUserMedia peerIdentity Constraint
    -  
    -  
    -  
    +  
       
     
     
    -Test mozGetUserMedia peerIdentity Constraint
    -

    -
     
    -  
       
     
     
    -mozGetUserMedia Play Audio Twice
    -

    -
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_playVideoAudioTwice.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_playVideoAudioTwice.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_playVideoAudioTwice.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_playVideoAudioTwice.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,42 +1,24 @@ - + - - - mozGetUserMedia Play Video and Audio Twice - - - -mozGetUserMedia Play Video and Audio Twice -

    -
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_playVideoTwice.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_playVideoTwice.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_playVideoTwice.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_playVideoTwice.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,43 +1,24 @@
    -
    +
     
    -
     
    -  
    -  mozGetUserMedia Play Video Twice
    -  
    -  
    -  
       
     
     
    -mozGetUserMedia Play Video Twice
    -

    -
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopAudioStream.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopAudioStream.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopAudioStream.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopAudioStream.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,36 +1,25 @@
    -
    +
     
    -
     
    -  
    -  mozGetUserMedia Stop Audio Stream
    -  
    -  
    -  
       
     
     
    -mozGetUserMedia Stop Audio Stream
    -

    -
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopAudioStreamWithFollowupAudio.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopAudioStreamWithFollowupAudio.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopAudioStreamWithFollowupAudio.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopAudioStreamWithFollowupAudio.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,48 +1,33 @@
    -
    +
     
    -
     
    -  
    -  mozGetUserMedia Stop Audio Stream With Followup Audio
    -  
    -  
    -  
       
     
     
    -mozGetUserMedia Stop Audio Stream With Followup Audio
    -

    -
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStream.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStream.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStream.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStream.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,37 +1,26 @@
     
     
    -
     
    -  
    -  mozGetUserMedia Stop Video Audio Stream
    -  
    -  
    -  
       
     
     
    -mozGetUserMedia Stop Video Audio Stream
    -

    -
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,48 +1,36 @@
    -
    +
     
    -
     
    -  
    -  mozGetUserMedia Stop Video+Audio Stream With Followup Video+Audio
    -  
    -  
    -  
       
     
     
    -mozGetUserMedia Stop Video+Audio Stream With Followup Video+Audio
    -

    -
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopVideoStream.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopVideoStream.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopVideoStream.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopVideoStream.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,36 +1,26 @@
    -
    +
     
    -
     
    -  
    -  mozGetUserMedia Stop Video Stream
    -  
    -  
    -  
       
     
     
    -mozGetUserMedia Stop Video Stream
    -

    -
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopVideoStreamWithFollowupVideo.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopVideoStreamWithFollowupVideo.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopVideoStreamWithFollowupVideo.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_getUserMedia_stopVideoStreamWithFollowupVideo.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,49 +1,33 @@
    -
    +
     
    -
     
    -  
    -  mozGetUserMedia Stop Video Stream With Followup Video
    -  
    -  
    -  
    -  
    +  
     
     
    -mozGetUserMedia Stop Video Stream With Followup Video
    -

    -
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_addCandidateInHaveLocalOffer.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_addCandidateInHaveLocalOffer.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_addCandidateInHaveLocalOffer.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_addCandidateInHaveLocalOffer.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    @@ -23,20 +17,18 @@
         test.setMediaConstraints([{audio: true}], [{audio: true}]);
         test.chain.removeAfter("PC_LOCAL_SET_LOCAL_DESCRIPTION");
     
    -    test.chain.append([[
    -      "PC_LOCAL_ADD_CANDIDATE",
    -      function (test) {
    -        test.pcLocal.addIceCandidateAndFail(
    -          new mozRTCIceCandidate(
    -            {candidate:"1 1 UDP 2130706431 192.168.2.1 50005 typ host",
    -             sdpMLineIndex: 1}),
    -          function(err) {
    +    test.chain.append([
    +      function PC_LOCAL_ADD_CANDIDATE(test) {
    +        var candidate = new mozRTCIceCandidate(
    +          {candidate:"1 1 UDP 2130706431 192.168.2.1 50005 typ host",
    +           sdpMLineIndex: 1});
    +        return test.pcLocal._pc.addIceCandidate(candidate).then(
    +          generateErrorCallback("addIceCandidate should have failed."),
    +          err => {
                 is(err.name, "InvalidStateError", "Error is InvalidStateError");
    -            test.next();
    -          } );
    -      }
    -    ]]);
    -
    +          });
    +        }
    +    ]);
         test.run();
       });
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_addSecondAudioStream.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_addSecondAudioStream.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_addSecondAudioStream.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_addSecondAudioStream.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    @@ -21,77 +15,40 @@
       runNetworkTest(function (options) {
         test = new PeerConnectionTest(options);
         test.chain.append([
    -    [
    -      'PC_LOCAL_SETUP_NEGOTIATION_CALLBACK',
    -      function (test) {
    +      function PC_LOCAL_SETUP_NEGOTIATION_CALLBACK(test) {
             test.pcLocal.onNegotiationneededFired = false;
    -        test.pcLocal._pc.onnegotiationneeded = function (anEvent) {
    +        test.pcLocal._pc.onnegotiationneeded = anEvent => {
               info("pcLocal.onnegotiationneeded fired");
               test.pcLocal.onNegotiationneededFired = true;
             };
    -        test.next();
    -      }
    -    ],
    -    [
    -      'PC_LOCAL_ADD_SECOND_STREAM',
    -      function (test) {
    -        test.pcLocal.getAllUserMedia([{audio: true}], function () {
    -          test.next();
    -        });
    -      }
    -    ],
    -    [
    -      'PC_LOCAL_CREATE_NEW_OFFER',
    -      function (test) {
    +      },
    +      function PC_LOCAL_ADD_SECOND_STREAM(test) {
    +        return test.pcLocal.getAllUserMedia([{audio: true}]);
    +      },
    +      function PC_LOCAL_CREATE_NEW_OFFER(test) {
             ok(test.pcLocal.onNegotiationneededFired, "onnegotiationneeded");
    -        test.createOffer(test.pcLocal, function (offer) {
    +        return test.createOffer(test.pcLocal).then(offer => {
               test._new_offer = offer;
    -          test.next();
    -        });
    -      }
    -    ],
    -    [
    -      'PC_LOCAL_SET_NEW_LOCAL_DESCRIPTION',
    -      function (test) {
    -        test.setLocalDescription(test.pcLocal, test._new_offer, HAVE_LOCAL_OFFER, function () {
    -          test.next();
    -        });
    -      }
    -    ],
    -    [
    -      'PC_REMOTE_SET_NEW_REMOTE_DESCRIPTION',
    -      function (test) {
    -        test.setRemoteDescription(test.pcRemote, test._new_offer, HAVE_REMOTE_OFFER, function () {
    -          test.next();
             });
    -      }
    -    ],
    -    [
    -      'PC_REMOTE_CREATE_NEW_ANSWER',
    -      function (test) {
    -        test.createAnswer(test.pcRemote, function (answer) {
    +      },
    +      function PC_LOCAL_SET_NEW_LOCAL_DESCRIPTION(test) {
    +        return test.setLocalDescription(test.pcLocal, test._new_offer, HAVE_LOCAL_OFFER);
    +      },
    +      function PC_REMOTE_SET_NEW_REMOTE_DESCRIPTION(test) {
    +        return test.setRemoteDescription(test.pcRemote, test._new_offer, HAVE_REMOTE_OFFER);
    +      },
    +      function PC_REMOTE_CREATE_NEW_ANSWER(test) {
    +        return test.createAnswer(test.pcRemote).then(answer => {
               test._new_answer = answer;
    -          test.next();
    -        });
    -      }
    -    ],
    -    [
    -      'PC_REMOTE_SET_NEW_LOCAL_DESCRIPTION',
    -      function (test) {
    -        test.setLocalDescription(test.pcRemote, test._new_answer, STABLE, function () {
    -          test.next();
    -        });
    -      }
    -    ],
    -    [
    -      'PC_LOCAL_SET_NEW_REMOTE_DESCRIPTION',
    -      function (test) {
    -        test.setRemoteDescription(test.pcLocal, test._new_answer, STABLE, function () {
    -          test.next();
             });
    +      },
    +      function PC_REMOTE_SET_NEW_LOCAL_DESCRIPTION(test) {
    +        return test.setLocalDescription(test.pcRemote, test._new_answer, STABLE);
    +      },
    +      function PC_LOCAL_SET_NEW_REMOTE_DESCRIPTION(test) {
    +        return test.setRemoteDescription(test.pcLocal, test._new_answer, STABLE);
           }
    -    ]
    -    // TODO(bug 1093835): figure out how to verify if media flows through the new stream
    +      // TODO(bug 1093835): figure out how to verify if media flows through the new stream
         ]);
         test.setMediaConstraints([{audio: true}], [{audio: true}]);
         test.run();
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudio.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudio.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudio.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudio.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudio_long.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudio_long.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudio_long.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudio_long.html	2015-02-03 14:33:44.000000000 +0000
    @@ -6,14 +6,8 @@
     
     
     
    -  
    -  
    -  
       
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined_long.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined_long.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined_long.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined_long.html	2015-02-03 14:33:44.000000000 +0000
    @@ -6,14 +6,8 @@
     
     
     
    -  
    -  
    -  
       
    -  
       
    -  
    -  
     
     
     
    @@ -25,7 +19,7 @@
       });
     
       var test;
    -  runTest(function (options) {
    +  runNetworkTest(function (options) {
         options = options || {};
         options.commands = commandsPeerConnection.slice(0);
         options.commands.push(generateIntervalCommand(verifyConnectionStatus,
    @@ -41,4 +35,3 @@
     
    - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudioVideo.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudioVideo.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudioVideo.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudioVideo.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,13 +1,7 @@  - - - - - -
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoBundle.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoBundle.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoBundle.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoBundle.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    @@ -17,22 +11,19 @@
         title: "Basic audio/video peer connection with no Bundle"
       });
     
    -  SimpleTest.requestFlakyTimeout("WebRTC is full of inherent timeouts");
    -
    -  var test;
    -  runNetworkTest(function (options) {
    -    test = new PeerConnectionTest(options);
    -    test.chain.insertAfter('PC_LOCAL_CREATE_OFFER',
    -    [['PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER',
    -      function (test) {
    -        test.originalOffer.sdp = test.originalOffer.sdp.replace(
    -          /a=group:BUNDLE .*\r\n/g,
    -          ""
    -        );
    -        info("Updated no bundle offer: " + JSON.stringify(test.originalOffer));
    -        test.next();
    -      }
    -    ]]);
    +  runNetworkTest(options => {
    +    var test = new PeerConnectionTest(options);
    +    test.chain.insertAfter(
    +      'PC_LOCAL_CREATE_OFFER',
    +      [
    +        function PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER(test) {
    +          test.originalOffer.sdp = test.originalOffer.sdp.replace(
    +              /a=group:BUNDLE .*\r\n/g,
    +            ""
    +          );
    +          info("Updated no bundle offer: " + JSON.stringify(test.originalOffer));
    +        }
    +      ]);
         test.setMediaConstraints([{audio: true}, {video: true}],
                                  [{audio: true}, {video: true}]);
         test.run();
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicH264Video.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicH264Video.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicH264Video.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicH264Video.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicScreenshare.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicScreenshare.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicScreenshare.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicScreenshare.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicVideo.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicVideo.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicVideo.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicVideo.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicVideo_long.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicVideo_long.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicVideo_long.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicVideo_long.html	2015-02-03 14:33:44.000000000 +0000
    @@ -6,14 +6,8 @@
     
     
     
    -  
    -  
    -  
       
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicWindowshare.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicWindowshare.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_basicWindowshare.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_basicWindowshare.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_bug1013809.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_bug1013809.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_bug1013809.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_bug1013809.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,12 +1,7 @@
     
     
     
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_bug1042791.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_bug1042791.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_bug1042791.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_bug1042791.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    @@ -25,16 +19,14 @@
         test.setMediaConstraints([{video: true}], [{video: true}]);
         test.chain.removeAfter("PC_LOCAL_CREATE_OFFER");
     
    -    test.chain.append([[
    -      "PC_LOCAL_VERIFY_H264_OFFER",
    -      function (test) {
    +    test.chain.append([
    +      function PC_LOCAL_VERIFY_H264_OFFER(test) {
             ok(!test.pcLocal._latest_offer.sdp.toLowerCase().contains("profile-level-id=0x42e0"),
    -          "H264 offer does not contain profile-level-id=0x42e0");
    +           "H264 offer does not contain profile-level-id=0x42e0");
             ok(test.pcLocal._latest_offer.sdp.toLowerCase().contains("profile-level-id=42e0"),
    -          "H264 offer contains profile-level-id=42e0");
    -        test.next();
    +           "H264 offer contains profile-level-id=42e0");
           }
    -    ]]);
    +    ]);
     
         test.run();
       });
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_bug822674.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_bug822674.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_bug822674.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_bug822674.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,9 +1,6 @@
     
     
     
    -  
    -  
    -  
       
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_bug825703.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_bug825703.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_bug825703.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_bug825703.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,9 +1,6 @@
     
     
     
    -  
    -  
    -  
       
     
     
    @@ -14,69 +11,69 @@
         title: "RTCConfiguration valid/invalid permutations"
       });
     
    -  makePC = (config, expected_error) => {
    -    var exception;
    -    try {
    -      new mozRTCPeerConnection(config).close();
    -    } catch (e) {
    -      exception = e;
    -    }
    -    is((exception? exception.name : "success"), expected_error || "success",
    -       "mozRTCPeerConnection(" + JSON.stringify(config) + ")");
    +var makePC = (config, expected_error) => {
    +  var exception;
    +  try {
    +    new mozRTCPeerConnection(config).close();
    +  } catch (e) {
    +    exception = e;
       }
    +  is((exception? exception.name : "success"), expected_error || "success",
    +     "mozRTCPeerConnection(" + JSON.stringify(config) + ")");
    +};
    +
    +// This is a test of the iceServers parsing code + readable errors
    +runNetworkTest(() => {
    +  var exception = null;
    +
    +  try {
    +    new mozRTCPeerConnection().close();
    +  } catch (e) {
    +    exception = e;
    +  }
    +  ok(!exception, "mozRTCPeerConnection() succeeds");
    +  exception = null;
     
    -  // This is a test of the iceServers parsing code + readable errors
    -
    -  runNetworkTest(function () {
    -    var exception = null;
    -
    -    try {
    -      new mozRTCPeerConnection().close();
    -    } catch (e) {
    -      exception = e;
    -    }
    -    ok(!exception, "mozRTCPeerConnection() succeeds");
    -    exception = null;
    +  makePC();
     
    -    makePC();
    +  makePC(1, "TypeError");
     
    -    makePC(1, "TypeError");
    +  makePC({});
     
    -    makePC({});
    +  makePC({ iceServers: [] });
     
    -    makePC({ iceServers: [] });
    +  makePC({ iceServers: [{ urls:"" }] }, "SyntaxError");
     
    -    makePC({ iceServers: [{ urls:"" }] }, "SyntaxError");
    +  makePC({ iceServers: [
    +    { urls:"stun:127.0.0.1" },
    +    { urls:"stun:localhost", foo:"" },
    +    { urls: ["stun:127.0.0.1", "stun:localhost"] },
    +    { urls:"stuns:localhost", foo:"" },
    +    { urls:"turn:[::1]:3478", username:"p", credential:"p" },
    +    { urls:"turn:localhost:3478?transport=udp", username:"p", credential:"p" },
    +    { urls: ["turn:[::1]:3478", "turn:localhost"], username:"p", credential:"p" },
    +    { urls:"turns:localhost:3478?transport=udp", username:"p", credential:"p" },
    +    { url:"stun:localhost", foo:"" },
    +    { url:"turn:localhost", username:"p", credential:"p" }
    +  ]});
     
    -    makePC({ iceServers: [
    -      { urls:"stun:127.0.0.1" },
    -      { urls:"stun:localhost", foo:"" },
    -      { urls: ["stun:127.0.0.1", "stun:localhost"] },
    -      { urls:"stuns:localhost", foo:"" },
    -      { urls:"turn:[::1]:3478", username:"p", credential:"p" },
    -      { urls:"turn:localhost:3478?transport=udp", username:"p", credential:"p" },
    -      { urls: ["turn:[::1]:3478", "turn:localhost"], username:"p", credential:"p" },
    -      { urls:"turns:localhost:3478?transport=udp", username:"p", credential:"p" },
    -      { url:"stun:localhost", foo:"" },
    -      { url:"turn:localhost", username:"p", credential:"p" }
    -    ]});
    +  makePC({ iceServers: [{ urls: ["stun:127.0.0.1", ""] }] }, "SyntaxError");
     
    -    makePC({ iceServers: [{ urls: ["stun:127.0.0.1", ""] }] }, "SyntaxError");
    +  makePC({ iceServers: [{ urls:"turns:localhost:3478", username:"p" }] }, "InvalidAccessError");
     
    -    makePC({ iceServers: [{ urls:"turns:localhost:3478", username:"p" }] }, "InvalidAccessError");
    +  makePC({ iceServers: [{ url:"turns:localhost:3478", credential:"p" }] }, "InvalidAccessError");
     
    -    makePC({ iceServers: [{ url:"turns:localhost:3478", credential:"p" }] }, "InvalidAccessError");
    +  makePC({ iceServers: [{ urls:"http:0.0.0.0" }] }, "SyntaxError");
     
    -    makePC({ iceServers: [{ urls:"http:0.0.0.0" }] }, "SyntaxError");
    -    try {
    -      new mozRTCPeerConnection({ iceServers: [{ url:"http:0.0.0.0" }] }).close();
    -    } catch (e) {
    -      ok(e.message.indexOf("http") > 0,
    -         "mozRTCPeerConnection() constructor has readable exceptions");
    -    }
    +  try {
    +    new mozRTCPeerConnection({ iceServers: [{ url:"http:0.0.0.0" }] }).close();
    +  } catch (e) {
    +    ok(e.message.indexOf("http") > 0,
    +       "mozRTCPeerConnection() constructor has readable exceptions");
    +  }
     
    -    networkTestFinished();
    -  });
    +  networkTestFinished();
    +});
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_bug827843.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_bug827843.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_bug827843.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_bug827843.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,12 +1,7 @@ - - - - -
    @@ -16,56 +11,59 @@
         title: "Ensure that localDescription and remoteDescription are null after close"
       });
     
    -  var steps = [
    -    [
    -      "CHECK_SDP_ON_CLOSED_PC",
    -      function (test) {
    -        var description;
    -        var exception = null;
    -
    -        // handle the event which the close() triggers
    -        test.pcLocal.onsignalingstatechange = function (e) {
    -          is(e.target.signalingState, "closed",
    -             "Received expected onsignalingstatechange event on 'closed'");
    -        }
    -
    -        test.pcLocal.close();
    -
    -        try { description = test.pcLocal.localDescription; } catch (e) { exception = e; }
    -        ok(exception, "Attempt to access localDescription of pcLocal after close throws exception");
    -        exception = null;
    -
    -        try { description = test.pcLocal.remoteDescription; } catch (e) { exception = e; }
    -        ok(exception, "Attempt to access remoteDescription of pcLocal after close throws exception");
    -        exception = null;
    -
    -        // handle the event which the close() triggers
    -        test.pcRemote.onsignalingstatechange = function (e) {
    -          is(e.target.signalingState, "closed",
    -             "Received expected onsignalingstatechange event on 'closed'");
    -        }
    -
    -        test.pcRemote.close();
    -
    -        try  { description = test.pcRemote.localDescription; } catch (e) { exception = e; }
    -        ok(exception, "Attempt to access localDescription of pcRemote after close throws exception");
    -        exception = null;
    +var steps = [
    +  function CHECK_SDP_ON_CLOSED_PC(test) {
    +    var description;
    +    var exception = null;
    +
    +    // handle the event which the close() triggers
    +    var localClosed = new Promise(resolve => {
    +      test.pcLocal.onsignalingstatechange = e => {
    +        is(e.target.signalingState, "closed",
    +           "Received expected onsignalingstatechange event on 'closed'");
    +        resolve();
    +      }
    +    });
     
    -        try  { description = test.pcRemote.remoteDescription; } catch (e) { exception = e; }
    -        ok(exception, "Attempt to access remoteDescription of pcRemote after close throws exception");
    +    test.pcLocal.close();
     
    -        test.next();
    +    try { description = test.pcLocal.localDescription; } catch (e) { exception = e; }
    +    ok(exception, "Attempt to access localDescription of pcLocal after close throws exception");
    +    exception = null;
    +
    +    try { description = test.pcLocal.remoteDescription; } catch (e) { exception = e; }
    +    ok(exception, "Attempt to access remoteDescription of pcLocal after close throws exception");
    +    exception = null;
    +
    +    // handle the event which the close() triggers
    +    var remoteClosed = new Promise(resolve => {
    +      test.pcRemote.onsignalingstatechange = e => {
    +        is(e.target.signalingState, "closed",
    +           "Received expected onsignalingstatechange event on 'closed'");
    +        resolve();
           }
    -    ]
    -  ];
    +    });
     
    -  var test;
    -  runNetworkTest(function () {
    -    test = new PeerConnectionTest();
    -    test.setMediaConstraints([{audio: true}], [{audio: true}]);
    -    test.chain.append(steps);
    -    test.run();
    -  });
    +    test.pcRemote.close();
    +
    +    try  { description = test.pcRemote.localDescription; } catch (e) { exception = e; }
    +    ok(exception, "Attempt to access localDescription of pcRemote after close throws exception");
    +    exception = null;
    +
    +    try  { description = test.pcRemote.remoteDescription; } catch (e) { exception = e; }
    +    ok(exception, "Attempt to access remoteDescription of pcRemote after close throws exception");
    +
    +    return Promise.all([localClosed, remoteClosed]);
    +  }
    +];
    +
    +var test;
    +runNetworkTest(() => {
    +  test = new PeerConnectionTest();
    +  test.setMediaConstraints([{audio: true}], [{audio: true}]);
    +  test.chain.append(steps);
    +  test.run();
    +});
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_bug834153.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_bug834153.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_bug834153.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_bug834153.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,9 +1,6 @@ - - - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_callbacks.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_callbacks.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_callbacks.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_callbacks.html 2015-02-03 14:33:44.000000000 +0000 @@ -0,0 +1,92 @@ + + + + + + +
    +
    +
    + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_capturedVideo.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_capturedVideo.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_capturedVideo.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_capturedVideo.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,13 +1,7 @@  - - - - - - @@ -19,36 +13,32 @@ visible: true }); - var metadataLoaded = new Promise(resolve => { - if (v1.readyState < v1.HAVE_METADATA) { - v1.onloadedmetadata = e => resolve(); - return; - } +var metadataLoaded = new Promise(resolve => { + if (v1.readyState < v1.HAVE_METADATA) { + v1.onloadedmetadata = resolve; + } else { resolve(); - }); + } +}); - runNetworkTest(function() { - var test = new PeerConnectionTest(); - test.setOfferOptions({ offerToReceiveVideo: false, - offerToReceiveAudio: false }); - test.chain.insertAfter("PC_LOCAL_GUM", [["PC_LOCAL_CAPTUREVIDEO", function (test) { - metadataLoaded - .then(function() { - var stream = v1.mozCaptureStreamUntilEnded(); - is(stream.getTracks().length, 2, "Captured stream has 2 tracks"); - stream.getTracks().forEach(tr => test.pcLocal._pc.addTrack(tr, stream)); - test.pcLocal.constraints = [{ video: true, audio:true }]; // fool tests - test.next(); - }) - .catch(function(reason) { - ok(false, "unexpected failure: " + reason); - SimpleTest.finish(); - }); +runNetworkTest(function() { + var test = new PeerConnectionTest(); + test.setOfferOptions({ offerToReceiveVideo: false, + offerToReceiveAudio: false }); + test.chain.insertAfter("PC_LOCAL_GUM", [ + function PC_LOCAL_CAPTUREVIDEO(test) { + return metadataLoaded + .then(() => { + var stream = v1.mozCaptureStreamUntilEnded(); + is(stream.getTracks().length, 2, "Captured stream has 2 tracks"); + stream.getTracks().forEach(tr => test.pcLocal._pc.addTrack(tr, stream)); + test.pcLocal.constraints = [{ video: true, audio:true }]; // fool tests + }); } - ]]); - test.chain.removeAfter("PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT"); - test.run(); - }); + ]); + test.chain.removeAfter("PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT"); + test.run(); +});
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_close.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_close.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_close.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_close.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,9 +1,6 @@ - - - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_errorCallbacks.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_errorCallbacks.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_errorCallbacks.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_errorCallbacks.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,9 +1,6 @@ - - - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_noTrickleAnswer.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_noTrickleAnswer.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_noTrickleAnswer.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_noTrickleAnswer.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,14 +1,8 @@  - - - - - -
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_noTrickleOfferAnswer.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_noTrickleOfferAnswer.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_noTrickleOfferAnswer.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_noTrickleOfferAnswer.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,14 +1,8 @@
     
     
     
    -  
    -  
    -  
    -  
       
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_noTrickleOffer.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_noTrickleOffer.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_noTrickleOffer.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_noTrickleOffer.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,14 +1,8 @@
     
     
     
    -  
    -  
    -  
    -  
       
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveAudio.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveAudio.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveAudio.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveAudio.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,12 +1,7 @@
     
     
     
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideoAudio.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideoAudio.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideoAudio.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideoAudio.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,12 +1,7 @@
     
     
     
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideo.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideo.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideo.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideo.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,12 +1,7 @@
     
     
     
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_promiseSendOnly.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_promiseSendOnly.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_promiseSendOnly.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_promiseSendOnly.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,14 +1,9 @@
     
     
     
    -  
    -  
    -  
       
     
     
    -
    -
     
    -  
    -  
       
    -  
    -  
     
     
     
    @@ -31,27 +25,23 @@
         test.chain.removeAfter("PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT");
         var flowtest = test.chain.remove("PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT");
         test.chain.append(flowtest);
    -    test.chain.append([["PC_LOCAL_REPLACE_VIDEOTRACK",
    -      function (test) {
    +    test.chain.append([
    +      function PC_LOCAL_REPLACE_VIDEOTRACK(test) {
             var stream = test.pcLocal._pc.getLocalStreams()[0];
             var track = stream.getVideoTracks()[0];
             var sender = test.pcLocal._pc.getSenders().find(isSenderOfTrack, track);
             ok(sender, "track has a sender");
             var newtrack;
    -        navigator.mediaDevices.getUserMedia({video:true, fake: true})
    -        .then(function(newStream) {
    -          newtrack = newStream.getVideoTracks()[0];
    -          return sender.replaceTrack(newtrack);
    -        })
    -        .then(function() {
    -          is(sender.track, newtrack, "sender.track has been replaced");
    -        })
    -        .catch(function(reason) {
    -          ok(false, "unexpected error = " + reason.message);
    -        })
    -        .then(test.next.bind(test));
    +        return navigator.mediaDevices.getUserMedia({video:true, fake: true})
    +          .then(newStream => {
    +            newtrack = newStream.getVideoTracks()[0];
    +            return sender.replaceTrack(newtrack);
    +          })
    +          .then(() => {
    +            is(sender.track, newtrack, "sender.track has been replaced");
    +          });
           }
    -    ]]);
    +    ]);
         test.chain.append(flowtest);
     
         test.run();
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInHaveLocalOffer.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInHaveLocalOffer.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInHaveLocalOffer.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInHaveLocalOffer.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    @@ -17,26 +11,23 @@
         title: "setLocalDescription (answer) in 'have-local-offer'"
       });
     
    -  var test;
    -  runNetworkTest(function () {
    -    test = new PeerConnectionTest();
    -    test.setMediaConstraints([{audio: true}], [{audio: true}]);
    -    test.chain.removeAfter("PC_LOCAL_SET_LOCAL_DESCRIPTION");
    +runNetworkTest(function () {
    +  var test = new PeerConnectionTest();
    +  test.setMediaConstraints([{audio: true}], [{audio: true}]);
    +  test.chain.removeAfter("PC_LOCAL_SET_LOCAL_DESCRIPTION");
     
    -    test.chain.append([[
    -      "PC_LOCAL_SET_LOCAL_ANSWER",
    -      function (test) {
    -        test.pcLocal._latest_offer.type="answer";
    -        test.pcLocal.setLocalDescriptionAndFail(test.pcLocal._latest_offer,
    -          function(err) {
    -            is(err.name, "InvalidStateError", "Error is InvalidStateError");
    -            test.next();
    -          } );
    -      }
    -    ]]);
    +  test.chain.append([
    +    function PC_LOCAL_SET_LOCAL_ANSWER(test) {
    +      test.pcLocal._latest_offer.type = "answer";
    +      return test.pcLocal.setLocalDescriptionAndFail(test.pcLocal._latest_offer)
    +        .then(err => {
    +          is(err.name, "InvalidStateError", "Error is InvalidStateError");
    +        });
    +    }
    +  ]);
     
    -    test.run();
    -  });
    +  test.run();
    +});
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInStable.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInStable.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInStable.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInStable.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,13 +1,7 @@  - - - - - -
    @@ -17,26 +11,23 @@
         title: "setLocalDescription (answer) in 'stable'"
       });
     
    -  var test;
    -  runNetworkTest(function () {
    -    test = new PeerConnectionTest();
    -    test.setMediaConstraints([{audio: true}], [{audio: true}]);
    -    test.chain.removeAfter("PC_LOCAL_CREATE_OFFER");
    +runNetworkTest(function () {
    +  var test = new PeerConnectionTest();
    +  test.setMediaConstraints([{audio: true}], [{audio: true}]);
    +  test.chain.removeAfter("PC_LOCAL_CREATE_OFFER");
     
    -    test.chain.append([[
    -      "PC_LOCAL_SET_LOCAL_ANSWER",
    -      function (test) {
    -        test.pcLocal._latest_offer.type="answer";
    -        test.pcLocal.setLocalDescriptionAndFail(test.pcLocal._latest_offer,
    -          function(err) {
    -            is(err.name, "InvalidStateError", "Error is InvalidStateError");
    -            test.next();
    -          } );
    -      }
    -    ]]);
    +  test.chain.append([
    +    function PC_LOCAL_SET_LOCAL_ANSWER(test) {
    +      test.pcLocal._latest_offer.type = "answer";
    +      return test.pcLocal.setLocalDescriptionAndFail(test.pcLocal._latest_offer)
    +        .then(err => {
    +          is(err.name, "InvalidStateError", "Error is InvalidStateError");
    +        });
    +    }
    +  ]);
     
    -    test.run();
    -  });
    +  test.run();
    +});
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_setLocalOfferInHaveRemoteOffer.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_setLocalOfferInHaveRemoteOffer.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_setLocalOfferInHaveRemoteOffer.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_setLocalOfferInHaveRemoteOffer.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,13 +1,7 @@  - - - - - -
    @@ -17,25 +11,22 @@
         title: "setLocalDescription (offer) in 'have-remote-offer'"
       });
     
    -  var test;
    -  runNetworkTest(function () {
    -    test = new PeerConnectionTest();
    -    test.setMediaConstraints([{audio: true}], [{audio: true}]);
    -    test.chain.removeAfter("PC_REMOTE_SET_REMOTE_DESCRIPTION");
    +runNetworkTest(function () {
    +  var test = new PeerConnectionTest();
    +  test.setMediaConstraints([{audio: true}], [{audio: true}]);
    +  test.chain.removeAfter("PC_REMOTE_SET_REMOTE_DESCRIPTION");
     
    -    test.chain.append([[
    -      "PC_REMOTE_SET_LOCAL_OFFER",
    -      function (test) {
    -        test.pcRemote.setLocalDescriptionAndFail(test.pcLocal._latest_offer,
    -          function(err) {
    -            is(err.name, "InvalidStateError", "Error is InvalidStateError");
    -            test.next();
    -          } );
    -      }
    -    ]]);
    +  test.chain.append([
    +    function PC_REMOTE_SET_LOCAL_OFFER(test) {
    +      test.pcRemote.setLocalDescriptionAndFail(test.pcLocal._latest_offer)
    +        .then(err => {
    +          is(err.name, "InvalidStateError", "Error is InvalidStateError");
    +        });
    +    }
    +  ]);
     
    -    test.run();
    -  });
    +  test.run();
    +});
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInHaveRemoteOffer.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInHaveRemoteOffer.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInHaveRemoteOffer.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInHaveRemoteOffer.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,13 +1,7 @@  - - - - - -
    @@ -17,26 +11,23 @@
         title: "setRemoteDescription (answer) in 'have-remote-offer'"
       });
     
    -  var test;
    -  runNetworkTest(function () {
    -    test = new PeerConnectionTest();
    -    test.setMediaConstraints([{audio: true}], [{audio: true}]);
    -    test.chain.removeAfter("PC_REMOTE_SET_REMOTE_DESCRIPTION");
    +runNetworkTest(function () {
    +  var test = new PeerConnectionTest();
    +  test.setMediaConstraints([{audio: true}], [{audio: true}]);
    +  test.chain.removeAfter("PC_REMOTE_SET_REMOTE_DESCRIPTION");
     
    -    test.chain.append([[
    -      "PC_REMOTE_SET_REMOTE_ANSWER",
    -      function (test) {
    -        test.pcLocal._latest_offer.type="answer";
    -        test.pcRemote.setRemoteDescriptionAndFail(test.pcLocal._latest_offer,
    -          function(err) {
    -            is(err.name, "InvalidStateError", "Error is InvalidStateError");
    -            test.next();
    -          } );
    -      }
    -    ]]);
    +  test.chain.append([
    +    function PC_REMOTE_SET_REMOTE_ANSWER(test) {
    +      test.pcLocal._latest_offer.type = "answer";
    +      test.pcRemote._pc.setRemoteDescription(test.pcLocal._latest_offer)
    +        .then(generateErrorCallback('setRemoteDescription should fail'),
    +              err =>
    +              is(err.name, "InvalidStateError", "Error is InvalidStateError"));
    +    }
    +  ]);
     
    -    test.run();
    -  });
    +  test.run();
    +});
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInStable.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInStable.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInStable.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInStable.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,13 +1,7 @@  - - - - - -
    @@ -17,26 +11,23 @@
         title: "setRemoteDescription (answer) in 'stable'"
       });
     
    -  var test;
    -  runNetworkTest(function () {
    -    test = new PeerConnectionTest();
    -    test.setMediaConstraints([{audio: true}], [{audio: true}]);
    -    test.chain.removeAfter("PC_LOCAL_CREATE_OFFER");
    +runNetworkTest(function () {
    +  var test = new PeerConnectionTest();
    +  test.setMediaConstraints([{audio: true}], [{audio: true}]);
    +  test.chain.removeAfter("PC_LOCAL_CREATE_OFFER");
     
    -    test.chain.append([[
    -      "PC_LOCAL_SET_REMOTE_ANSWER",
    -      function (test) {
    -        test.pcLocal._latest_offer.type="answer";
    -        test.pcLocal.setRemoteDescriptionAndFail(test.pcLocal._latest_offer,
    -          function(err) {
    -            is(err.name, "InvalidStateError", "Error is InvalidStateError");
    -            test.next();
    -          } );
    -      }
    -    ]]);
    +  test.chain.append([
    +    function PC_LOCAL_SET_REMOTE_ANSWER(test) {
    +      test.pcLocal._latest_offer.type = "answer";
    +      test.pcLocal._pc.setRemoteDescription(test.pcLocal._latest_offer)
    +        .then(generateErrorCallback('setRemoteDescription should fail'),
    +              err =>
    +              is(err.name, "InvalidStateError", "Error is InvalidStateError"));
    +    }
    +  ]);
     
    -    test.run();
    -  });
    +  test.run();
    +});
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_setRemoteOfferInHaveLocalOffer.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_setRemoteOfferInHaveLocalOffer.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_setRemoteOfferInHaveLocalOffer.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_setRemoteOfferInHaveLocalOffer.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,13 +1,7 @@  - - - - - -
    @@ -17,25 +11,22 @@
         title: "setRemoteDescription (offer) in 'have-local-offer'"
       });
     
    -  var test;
    -  runNetworkTest(function () {
    -    test = new PeerConnectionTest();
    -    test.setMediaConstraints([{audio: true}], [{audio: true}]);
    -    test.chain.removeAfter("PC_LOCAL_SET_LOCAL_DESCRIPTION");
    +runNetworkTest(function () {
    +  var test = new PeerConnectionTest();
    +  test.setMediaConstraints([{audio: true}], [{audio: true}]);
    +  test.chain.removeAfter("PC_LOCAL_SET_LOCAL_DESCRIPTION");
     
    -    test.chain.append([[
    -      "PC_LOCAL_SET_REMOTE_OFFER",
    -      function (test) {
    -        test.pcLocal.setRemoteDescriptionAndFail(test.pcLocal._latest_offer,
    -          function(err) {
    -            is(err.name, "InvalidStateError", "Error is InvalidStateError");
    -            test.next();
    -          } );
    -      }
    -    ]]);
    +  test.chain.append([
    +    function PC_LOCAL_SET_REMOTE_OFFER(test) {
    +      test.pcLocal._pc.setRemoteDescription(test.pcLocal._latest_offer)
    +        .then(generateErrorCallback('setRemoteDescription should fail'),
    +              err =>
    +              is(err.name, "InvalidStateError", "Error is InvalidStateError"));
    +    }
    +  ]);
     
    -    test.run();
    -  });
    +  test.run();
    +});
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_syncSetDescription.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_syncSetDescription.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_syncSetDescription.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_syncSetDescription.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,13 +1,7 @@  - - - - - -
    @@ -18,62 +12,40 @@
         visible: true
       });
     
    -  // Test setDescription without callbacks, which many webrtc examples still do
    +// Test setDescription without callbacks, which many webrtc examples still do
     
    -  var pc1_local =
    -  [[
    -    'PC_LOCAL_SET_LOCAL_DESCRIPTION_SYNC',
    -    function (test) {
    -      test.pcLocal.onsignalingstatechange = function() {};
    -      test.pcLocal.setLocalDescription(test.originalOffer);
    -      test.next();
    -    }
    -  ]];
    -
    -  var pc2_remote =
    -  [[
    -    'PC_REMOTE_SET_REMOTE_DESCRIPTION_SYNC',
    -    function (test) {
    -      test.pcRemote.onsignalingstatechange = function() {};
    -      test.pcRemote.setRemoteDescription(test._local_offer);
    -      test.next();
    -    }
    -  ]];
    -
    -  var pc2_local =
    -  [[
    -    'PC_REMOTE_SET_LOCAL_DESCRIPTION_SYNC',
    -    function (test) {
    -      test.pcRemote.onsignalingstatechange = function() {};
    -      test.pcRemote.setLocalDescription(test.originalAnswer);
    -      test.next();
    -    }
    -  ]];
    -
    -  var pc1_remote =
    -  [[
    -    'PC_LOCAL_SET_REMOTE_DESCRIPTION_SYNC',
    -    function (test) {
    -      test.pcLocal.onsignalingstatechange = function() {};
    -      test.pcLocal.setRemoteDescription(test._remote_answer);
    -      test.next();
    -    }
    -  ]];
    -
    -  runNetworkTest(function () {
    -    function replace(test, name, command) {
    -      test.chain.insertAfter(name, command);
    -      test.chain.remove(name);
    -    }
    -
    -    var test = new PeerConnectionTest();
    -    test.setMediaConstraints([{video: true}], [{video: true}]);
    -    replace(test, "PC_LOCAL_SET_LOCAL_DESCRIPTION", pc1_local);
    -    replace(test, "PC_REMOTE_SET_REMOTE_DESCRIPTION", pc2_remote);
    -    replace(test, "PC_REMOTE_SET_LOCAL_DESCRIPTION", pc2_local);
    -    replace(test, "PC_LOCAL_SET_REMOTE_DESCRIPTION", pc1_remote);
    -    test.run();
    -  });
    +function PC_LOCAL_SET_LOCAL_DESCRIPTION_SYNC(test) {
    +  test.pcLocal.onsignalingstatechange = function() {};
    +  test.pcLocal._pc.setLocalDescription(test.originalOffer);
    +}
    +
    +function PC_REMOTE_SET_REMOTE_DESCRIPTION_SYNC(test) {
    +  test.pcRemote.onsignalingstatechange = function() {};
    +  test.pcRemote._pc.setRemoteDescription(test._local_offer);
    +}
    +function PC_REMOTE_SET_LOCAL_DESCRIPTION_SYNC(test) {
    +  test.pcRemote.onsignalingstatechange = function() {};
    +  test.pcRemote._pc.setLocalDescription(test.originalAnswer);
    +}
    +function PC_LOCAL_SET_REMOTE_DESCRIPTION_SYNC(test) {
    +  test.pcLocal.onsignalingstatechange = function() {};
    +  test.pcLocal._pc.setRemoteDescription(test._remote_answer);
    +}
    +
    +runNetworkTest(() => {
    +  var replace = (test, name, command) => {
    +    test.chain.insertAfter(name, command);
    +    test.chain.remove(name);
    +  }
    +
    +  var test = new PeerConnectionTest();
    +  test.setMediaConstraints([{video: true}], [{video: true}]);
    +  test.chain.replace(test, "PC_LOCAL_SET_LOCAL_DESCRIPTION", PC_LOCAL_SET_LOCAL_DESCRIPTION_SYNC);
    +  test.chain.replace(test, "PC_REMOTE_SET_REMOTE_DESCRIPTION", PC_REMOTE_SET_REMOTE_DESCRIPTION_SYNC);
    +  test.chain.replace(test, "PC_REMOTE_SET_LOCAL_DESCRIPTION", PC_REMOTE_SET_LOCAL_DESCRIPTION_SYNC);
    +  test.chain.replace(test, "PC_LOCAL_SET_REMOTE_DESCRIPTION", PC_LOCAL_SET_REMOTE_DESCRIPTION_SYNC);
    +  test.run();
    +});
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_throwInCallbacks.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_throwInCallbacks.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_throwInCallbacks.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_throwInCallbacks.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,10 +1,6 @@ - - - - @@ -15,9 +11,23 @@ title: "Throw in PeerConnection callbacks" }); +runNetworkTest(function () { + function finish() { + window.onerror = oldOnError; + is(error_count, 7, "Seven expected errors verified."); + networkTestFinished(); + } + + function getFail() { + return err => { + window.onerror = oldOnError; + generateErrorCallback()(err); + }; + } + let error_count = 0; let oldOnError = window.onerror; - window.onerror = function (errorMsg, url, lineNumber) { + window.onerror = (errorMsg, url, lineNumber) => { if (errorMsg.indexOf("Expected") == -1) { getFail()(errorMsg); } @@ -31,27 +41,21 @@ } let pc0, pc1, pc2; - - runNetworkTest(function () { - error_count = 0; - - // Test failure callbacks (limited to 1 for now) - pc0 = new mozRTCPeerConnection(); - pc0.createOffer(getFail(), function(err) { - pc1 = new mozRTCPeerConnection(); - pc2 = new mozRTCPeerConnection(); - - // Test success callbacks (happy path) - navigator.mozGetUserMedia({video:true, fake: true}, function(video1) { - pc1.addStream(video1); - pc1.createOffer(function(offer) { - pc1.setLocalDescription(offer, function() { - pc2.setRemoteDescription(offer, function() { - pc2.createAnswer(function(answer) { - pc2.setLocalDescription(answer, function() { - pc1.setRemoteDescription(answer, function() { - throw new Error("Expected"); - }, getFail()); + // Test failure callbacks (limited to 1 for now) + pc0 = new mozRTCPeerConnection(); + pc0.createOffer(getFail(), function(err) { + pc1 = new mozRTCPeerConnection(); + pc2 = new mozRTCPeerConnection(); + + // Test success callbacks (happy path) + navigator.mozGetUserMedia({video:true, fake: true}, function(video1) { + pc1.addStream(video1); + pc1.createOffer(function(offer) { + pc1.setLocalDescription(offer, function() { + pc2.setRemoteDescription(offer, function() { + pc2.createAnswer(function(answer) { + pc2.setLocalDescription(answer, function() { + pc1.setRemoteDescription(answer, function() { throw new Error("Expected"); }, getFail()); throw new Error("Expected"); @@ -62,23 +66,13 @@ }, getFail()); throw new Error("Expected"); }, getFail()); + throw new Error("Expected"); }, getFail()); - throw new Error("Expected"); - }); + }, getFail()); + throw new Error("Expected"); }); +}); - function finish() { - window.onerror = oldOnError; - is(error_count, 7, "Seven expected errors verified."); - networkTestFinished(); - } - - function getFail() { - return function (err) { - window.onerror = oldOnError; - generateErrorCallback()(err); - }; - }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_toJSON.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_toJSON.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_toJSON.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_toJSON.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,14 +1,6 @@ - - - Test for Bug 872377 and Bug 928304 - - - @@ -29,9 +21,9 @@ if (typeof(rtcSession[key]) == "function") continue; is(rtcSession[key], jsonCopy[key], "key " + key + " should match."); } - + /** Test for Bug 928304 **/ - + var rtcIceCandidate = new mozRTCIceCandidate({ candidate: "dummy", sdpMid: "test", sdpMLineIndex: 3 }); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_twoAudioStreams.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_twoAudioStreams.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_twoAudioStreams.html 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_twoAudioStreams.html 2015-02-03 14:33:44.000000000 +0000 @@ -1,13 +1,7 @@  - - - - - -
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_twoAudioVideoStreamsCombined.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_twoAudioVideoStreamsCombined.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_twoAudioVideoStreamsCombined.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_twoAudioVideoStreamsCombined.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_twoAudioVideoStreams.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_twoAudioVideoStreams.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_twoAudioVideoStreams.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_twoAudioVideoStreams.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_twoVideoStreams.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_twoVideoStreams.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_peerConnection_twoVideoStreams.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_peerConnection_twoVideoStreams.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,13 +1,7 @@
     
     
     
    -  
    -  
    -  
    -  
       
    -  
    -  
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_zmedia_cleanup.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_zmedia_cleanup.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/media/tests/mochitest/test_zmedia_cleanup.html	2015-01-25 22:24:36.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/media/tests/mochitest/test_zmedia_cleanup.html	2015-02-03 14:33:44.000000000 +0000
    @@ -1,24 +1,22 @@
     
     
       
    -    
    -    
    -    
    +    
    +    
       
     
     
     
     
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/tethering/tests/marionette/head.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/tethering/tests/marionette/head.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/tethering/tests/marionette/head.js	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/tethering/tests/marionette/head.js	2015-02-03 14:33:52.000000000 +0000
    @@ -16,6 +16,8 @@
     const TETHERING_SETTING_DNS1 = "8.8.8.8";
     const TETHERING_SETTING_DNS2 = "8.8.4.4";
     
    +const TETHERING_NETWORK_ADDR = "192.168.1.0/24";
    +
     /**
      * Wifi tethering setting.
      */
    @@ -24,6 +26,7 @@
     const TETHERING_SETTING_KEY = "1234567890";
     
     const SETTINGS_RIL_DATA_ENABLED = 'ril.data.enabled';
    +const SETTINGS_KEY_DATA_APN_SETTINGS = "ril.data.apnSettings";
     
     // Emulate Promise.jsm semantics.
     Promise.defer = function() { return new Deferred(); }
    @@ -202,6 +205,20 @@
       }
     
       /**
    +   * Convenient MozSettings getter for SETTINGS_KEY_DATA_APN_SETTINGS.
    +   */
    +  function getDataApnSettings(aAllowError) {
    +    return getSettings(SETTINGS_KEY_DATA_APN_SETTINGS, aAllowError);
    +  }
    +
    +  /**
    +   * Convenient MozSettings setter for SETTINGS_KEY_DATA_APN_SETTINGS.
    +   */
    +  function setDataApnSettings(aApnSettings, aAllowError) {
    +    return setSettings1(SETTINGS_KEY_DATA_APN_SETTINGS, aApnSettings, aAllowError);
    +  }
    +
    +  /**
        * Wrap DOMRequest onsuccess/onerror events to Promise resolve/reject.
        *
        * Fulfill params: A DOMEvent.
    @@ -297,6 +314,9 @@
        * We also verify iptables output as netd's NatController will execute
        *   $ iptables -t nat -A POSTROUTING -o rmnet0 -j MASQUERADE
        *
    +   * For tethering through dun, we use 'ip rule' to find the secondary routing
    +   * table and look for default route on that table.
    +   *
        * Resolve when the verification is successful and reject otherwise.
        *
        * Fulfill params: (none)
    @@ -304,9 +324,10 @@
        *
        * @return A deferred promise.
        */
    -  function verifyTetheringRouting(aEnabled) {
    +  function verifyTetheringRouting(aEnabled, aIsDun) {
         let netcfgResult = {};
         let ipRouteResult = {};
    +    let ipSecondaryRouteResult = {};
     
         // Execute 'netcfg' and parse to |netcfgResult|, each key of which is the
         // interface name and value is { ip(string) }.
    @@ -418,8 +439,76 @@
             });
         }
     
    +    // Execute 'ip rule show', there must be one rule for tethering network
    +    // address to lookup for a secondary routing table, return that table id.
    +    function verifyIpRule() {
    +      if (!aIsDun) {
    +        return;
    +      }
    +
    +      return runEmulatorShellSafe(['ip', 'rule', 'show'])
    +        .then(function (aLines) {
    +          // Sample output:
    +          //
    +          // 0: from all lookup local
    +          // 32765: from 192.168.1.0/24 lookup 60
    +          // 32766: from all lookup main
    +          // 32767: from all lookup default
    +          //
    +          let tableId = (function findTableId() {
    +            for (let i = 0; i < aLines.length; i++) {
    +              let tokens = aLines[i].split(/\s+/);
    +              if (-1 != tokens.indexOf(TETHERING_NETWORK_ADDR)) {
    +                let lookupIndex = tokens.indexOf('lookup');
    +                if (lookupIndex < 0 || lookupIndex + 1 >= tokens.length) {
    +                  return;
    +                }
    +                return tokens[lookupIndex + 1];
    +              }
    +            }
    +            return;
    +          })();
    +
    +          if ((aEnabled && !tableId) || (!aEnabled && tableId)) {
    +            throw 'Secondary table' + (tableId ? '' : ' not') + ' found while tethering is ' +
    +                  (aEnabled ? 'enabled' : 'disabled');
    +          }
    +
    +          return tableId;
    +        });
    +    }
    +
    +    // Given the table id, use 'ip rule show table ' to find the
    +    // default route on that secondary routing table.
    +    function execAndParseSecondaryTable(aTableId) {
    +      if (!aIsDun || !aEnabled) {
    +        return;
    +      }
    +
    +      return runEmulatorShellSafe(['ip', 'route', 'show', 'table', aTableId])
    +        .then(function (aLines) {
    +          // We only look for default route in secondary table.
    +          aLines.forEach(function (aLine) {
    +            let tokens = aLine.split(/\s+/);
    +            if (tokens.length < 2) {
    +              return;
    +            }
    +            if ('default' === tokens[0]) {
    +              let ifnameIndex = tokens.indexOf('dev');
    +              if (ifnameIndex < 0 || ifnameIndex + 1 >= tokens.length) {
    +                return;
    +              }
    +              let ifname = tokens[ifnameIndex + 1];
    +              ipSecondaryRouteResult[ifname] = { default: true };
    +              return;
    +            }
    +          });
    +        });
    +    }
    +
         function verifyDefaultRouteAndIp(aExpectedWifiTetheringIp) {
           log(JSON.stringify(ipRouteResult));
    +      log(JSON.stringify(ipSecondaryRouteResult));
           log(JSON.stringify(netcfgResult));
     
           if (aEnabled) {
    @@ -429,10 +518,19 @@
             isOrThrow(ipRouteResult['wlan0'].src, netcfgResult['wlan0'].ip, 'wlan0.ip');
             isOrThrow(ipRouteResult['wlan0'].src, aExpectedWifiTetheringIp, 'expected ip');
             isOrThrow(ipRouteResult['wlan0'].default, false, 'wlan0.default');
    +
    +        if (aIsDun) {
    +          isOrThrow(ipRouteResult['rmnet1'].src, netcfgResult['rmnet1'].ip, 'rmnet1.ip');
    +          isOrThrow(ipRouteResult['rmnet1'].default, false, 'rmnet1.default');
    +          // Dun's network default route is set on secondary routing table.
    +          isOrThrow(ipSecondaryRouteResult['rmnet1'].default, true, 'secondary rmnet1.default');
    +        }
           }
         }
     
         return verifyIptables()
    +      .then(verifyIpRule)
    +      .then(tableId => execAndParseSecondaryTable(tableId))
           .then(exeAndParseNetcfg)
           .then(exeAndParseIpRoute)
           .then(() => verifyDefaultRouteAndIp(TETHERING_SETTING_IP));
    @@ -450,10 +548,12 @@
        *
        * @param aEnabled
        *        Boolean that indicates to enable or disable wifi tethering.
    +   * @param aIsDun
    +   *        Boolean that indicates whether dun is required.
        *
        * @return A deferred promise.
        */
    -  function setWifiTetheringEnabled(aEnabled) {
    +  function setWifiTetheringEnabled(aEnabled, aIsDun) {
         let RETRY_INTERVAL_MS = 1000;
         let retryCnt = 20;
     
    @@ -472,7 +572,7 @@
     
         return tetheringManager.setTetheringEnabled(aEnabled, TYPE_WIFI, config)
           .then(function waitForRoutingVerified() {
    -        return verifyTetheringRouting(aEnabled)
    +        return verifyTetheringRouting(aEnabled, aIsDun)
               .then(null, function onreject(aReason) {
     
                 log('verifyTetheringRouting rejected due to ' + aReason +
    @@ -602,6 +702,9 @@
       //---------------------------------------------------
       suite.ensureWifiEnabled = ensureWifiEnabled;
       suite.setWifiTetheringEnabled = setWifiTetheringEnabled;
    +  suite.getDataApnSettings = getDataApnSettings;
    +  suite.setDataApnSettings = setDataApnSettings;
    +
     
       /**
        * The common test routine for wifi tethering.
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/tethering/tests/marionette/manifest.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/tethering/tests/marionette/manifest.ini
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/tethering/tests/marionette/manifest.ini	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/tethering/tests/marionette/manifest.ini	2015-02-03 14:33:52.000000000 +0000
    @@ -4,3 +4,4 @@
     qemu = true
     
     [test_wifi_tethering_enabled.js]
    +[test_wifi_tethering_dun.js]
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/tethering/tests/marionette/test_wifi_tethering_dun.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/tethering/tests/marionette/test_wifi_tethering_dun.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/tethering/tests/marionette/test_wifi_tethering_dun.js	1970-01-01 00:00:00.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/tethering/tests/marionette/test_wifi_tethering_dun.js	2015-02-03 14:33:52.000000000 +0000
    @@ -0,0 +1,36 @@
    +/* This Source Code Form is subject to the terms of the Mozilla Public
    + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
    + * You can obtain one at http://mozilla.org/MPL/2.0/. */
    +
    +MARIONETTE_TIMEOUT = 60000;
    +MARIONETTE_HEAD_JS = 'head.js';
    +
    +gTestSuite.startTest(function() {
    +  let origApnSettings;
    +  return gTestSuite.getDataApnSettings()
    +    .then(value => {
    +      origApnSettings = value;
    +    })
    +    .then(() => {
    +      // Set dun apn settings.
    +      let apnSettings = [[ { "carrier": "T-Mobile US",
    +                             "apn": "epc1.tmobile.com",
    +                             "mmsc": "http://mms.msg.eng.t-mobile.com/mms/wapenc",
    +                             "types": ["default","supl","mms"] },
    +                           { "carrier": "T-Mobile US",
    +                             "apn": "epc2.tmobile.com",
    +                             "types": ["dun"] } ]];
    +      return gTestSuite.setDataApnSettings(apnSettings);
    +    })
    +    .then(() => gTestSuite.startTetheringTest(function() {
    +      return gTestSuite.ensureWifiEnabled(false)
    +        .then(() => gTestSuite.setWifiTetheringEnabled(true, true))
    +        .then(() => gTestSuite.setWifiTetheringEnabled(false, true));
    +    }))
    +    // Restore apn settings.
    +    .then(() => {
    +      if (origApnSettings) {
    +        return gTestSuite.setDataApnSettings(origApnSettings);
    +      }
    +    });
    +});
    \ No newline at end of file
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/tethering/tests/marionette/test_wifi_tethering_enabled.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/tethering/tests/marionette/test_wifi_tethering_enabled.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/tethering/tests/marionette/test_wifi_tethering_enabled.js	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/tethering/tests/marionette/test_wifi_tethering_enabled.js	2015-02-03 14:33:52.000000000 +0000
    @@ -7,5 +7,6 @@
     
     gTestSuite.startTetheringTest(function() {
       return gTestSuite.ensureWifiEnabled(false)
    -    .then(() => gTestSuite.setWifiTetheringEnabled(true));
    +    .then(() => gTestSuite.setWifiTetheringEnabled(true))
    +    .then(() => gTestSuite.setWifiTetheringEnabled(false));
     });
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/BluetoothAdapter2.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/BluetoothAdapter2.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/BluetoothAdapter2.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/BluetoothAdapter2.webidl	2015-02-03 14:33:52.000000000 +0000
    @@ -72,12 +72,12 @@
        * Several onattributechanged events would be triggered during processing the
        * request, and the last one indicates adapter.state becomes enabled/disabled.
        */
    -  [NewObject]
    +  [NewObject, AvailableIn=CertifiedApps]
       Promise enable();
    -  [NewObject]
    +  [NewObject, AvailableIn=CertifiedApps]
       Promise disable();
     
    -  [NewObject]
    +  [NewObject, AvailableIn=CertifiedApps]
       Promise setName(DOMString aName);
       [NewObject]
       Promise setDiscoverable(boolean aDiscoverable);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/Crypto.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/Crypto.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/Crypto.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/Crypto.webidl	2015-02-03 14:33:52.000000000 +0000
    @@ -4,18 +4,19 @@
      * You can obtain one at http://mozilla.org/MPL/2.0/.
      *
      * The origin of this IDL file is
    - * http://www.w3.org/TR/WebCryptoAPI/
    + * https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#crypto-interface
      */
     
     [NoInterfaceObject]
    -interface RandomSource {
    -  [Throws]
    -  ArrayBufferView getRandomValues(ArrayBufferView array);
    +interface GlobalCrypto {
    +  [Throws] readonly attribute Crypto crypto;
     };
     
    -Crypto implements RandomSource;
    -
    +//[Exposed=(Window,Worker)]
     interface Crypto {
       [Pref="dom.webcrypto.enabled"]
       readonly attribute SubtleCrypto subtle;
    +
    +  [Throws]
    +  ArrayBufferView getRandomValues(ArrayBufferView array);
     };
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/HTMLInputElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/HTMLInputElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/HTMLInputElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/HTMLInputElement.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -154,6 +154,9 @@
       [ChromeOnly]
       void mozSetFileNameArray(sequence fileNames);
     
    +  [ChromeOnly]
    +  void mozSetFileArray(sequence files);
    +
       // Number controls () have an anonymous text control
       // () in the anonymous shadow tree that they contain. On
       // such an anonymous text control this property provides access to the
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/HTMLMediaElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/HTMLMediaElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/HTMLMediaElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/HTMLMediaElement.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -50,6 +50,8 @@
       [Throws]
       void fastSeek(double time);
       readonly attribute unrestricted double duration;
    +  [ChromeOnly]
    +  readonly attribute boolean isEncrypted;
       // TODO: Bug 847376 - readonly attribute any startDate;
       readonly attribute boolean paused;
       [SetterThrows]
    @@ -100,6 +102,8 @@
     
     // Mozilla extensions:
     partial interface HTMLMediaElement {
    +  [ChromeOnly]
    +  readonly attribute MediaSource? mozMediaSourceObject;
       attribute MediaStream? mozSrcObject;
       attribute boolean mozPreservesPitch;
       readonly attribute boolean mozAutoplayEnabled;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/InstallEvent.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/InstallEvent.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/InstallEvent.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/InstallEvent.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -16,6 +16,6 @@
     
     // Should be in the spec soon to satisfy conventions about events.
     // https://github.com/slightlyoff/ServiceWorker/issues/216.
    -dictionary InstallEventInit : EventInit {
    +dictionary InstallEventInit : ExtendableEventInit {
       ServiceWorker? activeWorker = null;
     };
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/KeyboardEvent.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/KeyboardEvent.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/KeyboardEvent.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/KeyboardEvent.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -21,8 +21,6 @@
       const unsigned long DOM_KEY_LOCATION_LEFT     = 0x01;
       const unsigned long DOM_KEY_LOCATION_RIGHT    = 0x02;
       const unsigned long DOM_KEY_LOCATION_NUMPAD   = 0x03;
    -  const unsigned long DOM_KEY_LOCATION_MOBILE   = 0x04;
    -  const unsigned long DOM_KEY_LOCATION_JOYSTICK = 0x05;
     
       readonly attribute unsigned long location;
       readonly attribute boolean       repeat;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/MediaKeyMessageEvent.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/MediaKeyMessageEvent.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/MediaKeyMessageEvent.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/MediaKeyMessageEvent.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -13,7 +13,8 @@
     enum MediaKeyMessageType {
       "license-request",
       "license-renewal",
    -  "license-release"
    +  "license-release",
    +  "individualization-request"
     };
     
     [Pref="media.eme.enabled", Constructor(DOMString type, optional MediaKeyMessageEventInit eventInitDict)]
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/MediaKeySession.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/MediaKeySession.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/MediaKeySession.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/MediaKeySession.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -23,22 +23,21 @@
     
       readonly attribute Promise closed;
     
    +  readonly attribute MediaKeyStatusMap keyStatuses;
    +
       [NewObject]
    -  Promise generateRequest(DOMString initDataType, (ArrayBufferView or ArrayBuffer) initData);
    +  Promise generateRequest(DOMString initDataType, BufferSource initData);
     
       [NewObject]
       Promise load(DOMString sessionId);
     
       // session operations
       [NewObject]
    -  Promise update((ArrayBufferView or ArrayBuffer) response);
    +  Promise update(BufferSource response);
     
       [NewObject]
       Promise close();
     
       [NewObject]
       Promise remove();
    -
    -  [NewObject]
    -  Promise> getUsableKeyIds();
     };
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/MediaKeyStatusMap.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/MediaKeyStatusMap.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/MediaKeyStatusMap.webidl	1970-01-01 00:00:00.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/MediaKeyStatusMap.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -0,0 +1,36 @@
    +/* -*- Mode: IDL; 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/.
    + *
    + * The origin of this IDL file is
    + * https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html
    + *
    + * Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
    + * W3C liability, trademark and document use rules apply.
    + */
    +
    +enum MediaKeyStatus {
    +  "usable",
    +  "expired",
    +  "output-downscaled",
    +  "output-not-allowed",
    +  "internal-error"
    +};
    +
    +[Pref="media.eme.enabled"]
    +interface MediaKeyStatusMap {
    +  [Throws]
    +  readonly attribute unsigned long size;
    +
    +  [Throws]
    +  object keys();
    +
    +  [Throws]
    +  object values();
    +
    +  [Throws]
    +  object entries();
    +
    +  // XXX: forEach, @@iterator
    +};
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/MediaSource.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/MediaSource.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/MediaSource.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/MediaSource.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -35,4 +35,6 @@
       [Throws]
       void endOfStream(optional MediaSourceEndOfStreamError error);
       static boolean isTypeSupported(DOMString type);
    +  [ChromeOnly]
    +  readonly attribute DOMString mozDebugReaderData;
     };
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/MouseEvent.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/MouseEvent.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/MouseEvent.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/MouseEvent.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -106,6 +106,8 @@
                                            EventTarget? relatedTargetArg,
                                            float pressure,
                                            unsigned short inputSourceArg);
    +  [ChromeOnly]
    +  readonly attribute boolean hitCluster; // True when touch occurs in a cluster of links
     
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/moz.build
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/moz.build	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/moz.build	2015-02-03 14:33:54.000000000 +0000
    @@ -9,7 +9,6 @@
     ]
     
     PREPROCESSED_WEBIDL_FILES = [
    -    'Crypto.webidl',
         'HTMLMediaElement.webidl',
         'Navigator.webidl',
         'Window.webidl',
    @@ -81,6 +80,7 @@
         'ContainerBoxObject.webidl',
         'ConvolverNode.webidl',
         'Coordinates.webidl',
    +    'Crypto.webidl',
         'CSPReport.webidl',
         'CSS.webidl',
         'CSSPrimitiveValue.webidl',
    @@ -807,6 +807,7 @@
             'MediaKeyMessageEvent.webidl',
             'MediaKeys.webidl',
             'MediaKeySession.webidl',
    +        'MediaKeyStatusMap.webidl',
             'MediaKeySystemAccess.webidl',
         ]
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/MozNFCTag.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/MozNFCTag.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/MozNFCTag.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/MozNFCTag.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -19,7 +19,8 @@
       "ISO-DEP",  // NFCForum-TS-DigitalProtocol-1.1 ISO-DEP.
       "MIFARE-Classic",  // MIFARE Classic from NXP.
       "MIFARE-Ultralight",  // MIFARE Ultralight from NXP.
    -  "NFC-Barcode" // NFC Barcode from Kovio.
    +  "NFC-Barcode", // NFC Barcode from Kovio.
    +  "Unknown"
     };
     
     /**
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/MozNFC.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/MozNFC.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/MozNFC.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/MozNFC.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -90,23 +90,41 @@
       attribute EventHandler onpeerready;
     
       /**
    -   * This event will be fired when a NFCPeer is detected.
    +   * This event will be fired when a NFCPeer is detected. The application has to
    +   * be running on the foreground (decided by System app) to receive this event.
    +   *
    +   * The default action of this event is to dispatch the event in System app
    +   * again, and System app will run the default UX behavior (like vibration).
    +   * So if the application would like to cancel the event, the application
    +   * should call event.preventDefault() or return false in this event handler.
        */
       attribute EventHandler onpeerfound;
     
       /**
        * This event will be fired when NFCPeer, earlier detected in onpeerready
    -   * or onpeerfound, moves out of range.
    +   * or onpeerfound, moves out of range, or if the application has been switched
    +   * to the background (decided by System app).
        */
       attribute EventHandler onpeerlost;
     
       /**
    -   * Ths event will be fired when a NFCTag is detected.
    +   * This event will be fired when a NFCTag is detected. The application has to
    +   * be running on the foreground (decided by System app) to receive this event.
    +   *
    +   * The default action of this event is to dispatch the event in System app
    +   * again, and System app will run the default UX behavior (like vibration) and
    +   * launch MozActivity to handle the content of the tag. (For example, System
    +   * app will launch Browser if the tag contains URL). So if the application
    +   * would like to cancel the event, i.e. in the above example, the application
    +   * would process the URL by itself without launching Browser, the application
    +   * should call event.preventDefault() or return false in this event handler.
        */
       attribute EventHandler ontagfound;
     
       /**
    -   * This event will be fired if the tag detected in ontagfound has been removed.
    +   * This event will be fired if the tag detected in ontagfound has been
    +   * removed, or if the application has been switched to the background (decided
    +   * by System app).
        */
       attribute EventHandler ontaglost;
     };
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/Response.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/Response.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/Response.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/Response.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -19,6 +19,7 @@
     
       readonly attribute USVString url;
       readonly attribute unsigned short status;
    +  readonly attribute boolean ok;
       readonly attribute ByteString statusText;
       [SameObject] readonly attribute Headers headers;
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGClipPathElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGClipPathElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGClipPathElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGClipPathElement.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -11,7 +11,9 @@
      */
     
     interface SVGClipPathElement : SVGElement {
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration clipPathUnits;
    +  [Constant]
       readonly attribute SVGAnimatedTransformList transform;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGComponentTransferFunctionElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGComponentTransferFunctionElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGComponentTransferFunctionElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGComponentTransferFunctionElement.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -19,11 +19,18 @@
       const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_LINEAR = 4;
       const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_GAMMA = 5;
     
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration type;
    +  [Constant]
       readonly attribute SVGAnimatedNumberList tableValues;
    +  [Constant]
       readonly attribute SVGAnimatedNumber slope;
    +  [Constant]
       readonly attribute SVGAnimatedNumber intercept;
    +  [Constant]
       readonly attribute SVGAnimatedNumber amplitude;
    +  [Constant]
       readonly attribute SVGAnimatedNumber exponent;
    +  [Constant]
       readonly attribute SVGAnimatedNumber offset;
     };
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGDocument.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGDocument.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGDocument.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGDocument.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -10,6 +10,6 @@
     interface SVGDocument : Document {
       [Throws]
       readonly attribute DOMString domain;
    -  [Throws]
    +  [Pure, Throws]
       readonly attribute SVGElement? rootElement;
     };
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGElement.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -15,6 +15,7 @@
     /*           [SetterThrows]
                attribute DOMString xmlbase; */
     
    +  [Constant]
       readonly attribute SVGAnimatedString className;
       [PutForwards=cssText, Constant]
       readonly attribute CSSStyleDeclaration style;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEBlendElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEBlendElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEBlendElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEBlendElement.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -30,8 +30,11 @@
       const unsigned short SVG_FEBLEND_MODE_SATURATION = 14;
       const unsigned short SVG_FEBLEND_MODE_COLOR = 15;
       const unsigned short SVG_FEBLEND_MODE_LUMINOSITY = 16;
    +  [Constant]
       readonly attribute SVGAnimatedString in1;
    +  [Constant]
       readonly attribute SVGAnimatedString in2;
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration mode;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEColorMatrixElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEColorMatrixElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEColorMatrixElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEColorMatrixElement.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -19,8 +19,11 @@
       const unsigned short SVG_FECOLORMATRIX_TYPE_HUEROTATE = 3;
       const unsigned short SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA = 4;
     
    +  [Constant]
       readonly attribute SVGAnimatedString in1;
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration type;
    +  [Constant]
       readonly attribute SVGAnimatedNumberList values;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEComponentTransferElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEComponentTransferElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEComponentTransferElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEComponentTransferElement.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -11,6 +11,7 @@
      */
     
     interface SVGFEComponentTransferElement : SVGElement {
    +  [Constant]
       readonly attribute SVGAnimatedString in1;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFECompositeElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFECompositeElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFECompositeElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFECompositeElement.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -21,12 +21,19 @@
       const unsigned short SVG_FECOMPOSITE_OPERATOR_XOR = 5;
       const unsigned short SVG_FECOMPOSITE_OPERATOR_ARITHMETIC = 6;
     
    +  [Constant]
       readonly attribute SVGAnimatedString in1;
    +  [Constant]
       readonly attribute SVGAnimatedString in2;
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration operator;
    +  [Constant]
       readonly attribute SVGAnimatedNumber k1;
    +  [Constant]
       readonly attribute SVGAnimatedNumber k2;
    +  [Constant]
       readonly attribute SVGAnimatedNumber k3;
    +  [Constant]
       readonly attribute SVGAnimatedNumber k4;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEConvolveMatrixElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEConvolveMatrixElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEConvolveMatrixElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEConvolveMatrixElement.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -18,17 +18,29 @@
       const unsigned short SVG_EDGEMODE_WRAP = 2;
       const unsigned short SVG_EDGEMODE_NONE = 3;
     
    +  [Constant]
       readonly attribute SVGAnimatedString in1;
    +  [Constant]
       readonly attribute SVGAnimatedInteger orderX;
    +  [Constant]
       readonly attribute SVGAnimatedInteger orderY;
    +  [Constant]
       readonly attribute SVGAnimatedNumberList kernelMatrix;
    +  [Constant]
       readonly attribute SVGAnimatedNumber divisor;
    +  [Constant]
       readonly attribute SVGAnimatedNumber bias;
    +  [Constant]
       readonly attribute SVGAnimatedInteger targetX;
    +  [Constant]
       readonly attribute SVGAnimatedInteger targetY;
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration edgeMode;
    +  [Constant]
       readonly attribute SVGAnimatedNumber kernelUnitLengthX;
    +  [Constant]
       readonly attribute SVGAnimatedNumber kernelUnitLengthY;
    +  [Constant]
       readonly attribute SVGAnimatedBoolean preserveAlpha;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEDiffuseLightingElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEDiffuseLightingElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEDiffuseLightingElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEDiffuseLightingElement.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -11,10 +11,15 @@
      */
     
     interface SVGFEDiffuseLightingElement : SVGElement {
    +  [Constant]
       readonly attribute SVGAnimatedString in1;
    +  [Constant]
       readonly attribute SVGAnimatedNumber surfaceScale;
    +  [Constant]
       readonly attribute SVGAnimatedNumber diffuseConstant;
    +  [Constant]
       readonly attribute SVGAnimatedNumber kernelUnitLengthX;
    +  [Constant]
       readonly attribute SVGAnimatedNumber kernelUnitLengthY;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEDisplacementMapElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEDisplacementMapElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEDisplacementMapElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEDisplacementMapElement.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -19,10 +19,15 @@
       const unsigned short SVG_CHANNEL_B = 3;
       const unsigned short SVG_CHANNEL_A = 4;
     
    +  [Constant]
       readonly attribute SVGAnimatedString in1;
    +  [Constant]
       readonly attribute SVGAnimatedString in2;
    +  [Constant]
       readonly attribute SVGAnimatedNumber scale;
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration xChannelSelector;
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration yChannelSelector;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEDistantLightElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEDistantLightElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEDistantLightElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEDistantLightElement.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -11,6 +11,8 @@
      */
     
     interface SVGFEDistantLightElement : SVGElement {
    +  [Constant]
       readonly attribute SVGAnimatedNumber azimuth;
    +  [Constant]
       readonly attribute SVGAnimatedNumber elevation;
     };
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEDropShadowElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEDropShadowElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEDropShadowElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEDropShadowElement.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -11,10 +11,15 @@
      */
     
     interface SVGFEDropShadowElement : SVGElement {
    +  [Constant]
       readonly attribute SVGAnimatedString in1;
    +  [Constant]
       readonly attribute SVGAnimatedNumber dx;
    +  [Constant]
       readonly attribute SVGAnimatedNumber dy;
    +  [Constant]
       readonly attribute SVGAnimatedNumber stdDeviationX;
    +  [Constant]
       readonly attribute SVGAnimatedNumber stdDeviationY;
     
       void setStdDeviation(float stdDeviationX, float stdDeviationY);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEGaussianBlurElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEGaussianBlurElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEGaussianBlurElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEGaussianBlurElement.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -11,8 +11,11 @@
      */
     
     interface SVGFEGaussianBlurElement : SVGElement {
    +  [Constant]
       readonly attribute SVGAnimatedString in1;
    +  [Constant]
       readonly attribute SVGAnimatedNumber stdDeviationX;
    +  [Constant]
       readonly attribute SVGAnimatedNumber stdDeviationY;
     
       void setStdDeviation(float stdDeviationX, float stdDeviationY);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEImageElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEImageElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEImageElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEImageElement.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -11,6 +11,7 @@
      */
     
     interface SVGFEImageElement : SVGElement {
    +  [Constant]
       readonly attribute SVGAnimatedPreserveAspectRatio preserveAspectRatio;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEMergeNodeElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEMergeNodeElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEMergeNodeElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEMergeNodeElement.webidl	2015-02-03 14:33:53.000000000 +0000
    @@ -11,5 +11,6 @@
      */
     
     interface SVGFEMergeNodeElement : SVGElement {
    +  [Constant]
       readonly attribute SVGAnimatedString in1;
     };
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEMorphologyElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEMorphologyElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEMorphologyElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEMorphologyElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -17,9 +17,13 @@
       const unsigned short SVG_MORPHOLOGY_OPERATOR_ERODE = 1;
       const unsigned short SVG_MORPHOLOGY_OPERATOR_DILATE = 2;
     
    +  [Constant]
       readonly attribute SVGAnimatedString in1;
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration operator;
    +  [Constant]
       readonly attribute SVGAnimatedNumber radiusX;
    +  [Constant]
       readonly attribute SVGAnimatedNumber radiusY;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEOffsetElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEOffsetElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEOffsetElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEOffsetElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -11,8 +11,11 @@
      */
     
     interface SVGFEOffsetElement : SVGElement {
    +  [Constant]
       readonly attribute SVGAnimatedString in1;
    +  [Constant]
       readonly attribute SVGAnimatedNumber dx;
    +  [Constant]
       readonly attribute SVGAnimatedNumber dy;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEPointLightElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEPointLightElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFEPointLightElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFEPointLightElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -11,7 +11,10 @@
      */
     
     interface SVGFEPointLightElement : SVGElement {
    +  [Constant]
       readonly attribute SVGAnimatedNumber x;
    +  [Constant]
       readonly attribute SVGAnimatedNumber y;
    +  [Constant]
       readonly attribute SVGAnimatedNumber z;
     };
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFESpecularLightingElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFESpecularLightingElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFESpecularLightingElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFESpecularLightingElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -11,11 +11,17 @@
      */
     
     interface SVGFESpecularLightingElement : SVGElement {
    +  [Constant]
       readonly attribute SVGAnimatedString in1;
    +  [Constant]
       readonly attribute SVGAnimatedNumber surfaceScale;
    +  [Constant]
       readonly attribute SVGAnimatedNumber specularConstant;
    +  [Constant]
       readonly attribute SVGAnimatedNumber specularExponent;
    +  [Constant]
       readonly attribute SVGAnimatedNumber kernelUnitLengthX;
    +  [Constant]
       readonly attribute SVGAnimatedNumber kernelUnitLengthY;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFESpotLightElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFESpotLightElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFESpotLightElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFESpotLightElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -11,12 +11,20 @@
      */
     
     interface SVGFESpotLightElement : SVGElement {
    +  [Constant]
       readonly attribute SVGAnimatedNumber x;
    +  [Constant]
       readonly attribute SVGAnimatedNumber y;
    +  [Constant]
       readonly attribute SVGAnimatedNumber z;
    +  [Constant]
       readonly attribute SVGAnimatedNumber pointsAtX;
    +  [Constant]
       readonly attribute SVGAnimatedNumber pointsAtY;
    +  [Constant]
       readonly attribute SVGAnimatedNumber pointsAtZ;
    +  [Constant]
       readonly attribute SVGAnimatedNumber specularExponent;
    +  [Constant]
       readonly attribute SVGAnimatedNumber limitingConeAngle;
     };
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFETileElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFETileElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFETileElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFETileElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -11,6 +11,7 @@
      */
     
     interface SVGFETileElement : SVGElement {
    +  [Constant]
       readonly attribute SVGAnimatedString in1;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFETurbulenceElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFETurbulenceElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFETurbulenceElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFETurbulenceElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -22,11 +22,17 @@
       const unsigned short SVG_STITCHTYPE_STITCH = 1;
       const unsigned short SVG_STITCHTYPE_NOSTITCH = 2;
     
    +  [Constant]
       readonly attribute SVGAnimatedNumber baseFrequencyX;
    +  [Constant]
       readonly attribute SVGAnimatedNumber baseFrequencyY;
    +  [Constant]
       readonly attribute SVGAnimatedInteger numOctaves;
    +  [Constant]
       readonly attribute SVGAnimatedNumber seed;
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration stitchTiles;
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration type;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFilterElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFilterElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFilterElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFilterElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -11,11 +11,17 @@
      */
     
     interface SVGFilterElement : SVGElement {
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration filterUnits;
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration primitiveUnits;
    +  [Constant]
       readonly attribute SVGAnimatedLength x;
    +  [Constant]
       readonly attribute SVGAnimatedLength y;
    +  [Constant]
       readonly attribute SVGAnimatedLength width;
    +  [Constant]
       readonly attribute SVGAnimatedLength height;
     
       // ImageData apply(ImageData source);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFilterPrimitiveStandardAttributes.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFilterPrimitiveStandardAttributes.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGFilterPrimitiveStandardAttributes.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGFilterPrimitiveStandardAttributes.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -12,9 +12,14 @@
     
     [NoInterfaceObject]
     interface SVGFilterPrimitiveStandardAttributes {
    +  [Constant]
       readonly attribute SVGAnimatedLength x;
    +  [Constant]
       readonly attribute SVGAnimatedLength y;
    +  [Constant]
       readonly attribute SVGAnimatedLength width;
    +  [Constant]
       readonly attribute SVGAnimatedLength height;
    +  [Constant]
       readonly attribute SVGAnimatedString result;
     };
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGGradientElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGGradientElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGGradientElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGGradientElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -18,8 +18,11 @@
       const unsigned short SVG_SPREADMETHOD_REFLECT = 2;
       const unsigned short SVG_SPREADMETHOD_REPEAT = 3;
     
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration gradientUnits;
    +  [Constant]
       readonly attribute SVGAnimatedTransformList gradientTransform;
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration spreadMethod;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGLinearGradientElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGLinearGradientElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGLinearGradientElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGLinearGradientElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -11,8 +11,12 @@
      */
     
     interface SVGLinearGradientElement : SVGGradientElement {
    +  [Constant]
       readonly attribute SVGAnimatedLength x1;
    +  [Constant]
       readonly attribute SVGAnimatedLength y1;
    +  [Constant]
       readonly attribute SVGAnimatedLength x2;
    +  [Constant]
       readonly attribute SVGAnimatedLength y2;
     };
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGMarkerElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGMarkerElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGMarkerElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGMarkerElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -22,12 +22,19 @@
       const unsigned short SVG_MARKER_ORIENT_AUTO = 1;
       const unsigned short SVG_MARKER_ORIENT_ANGLE = 2;
     
    +  [Constant]
       readonly attribute SVGAnimatedLength refX;
    +  [Constant]
       readonly attribute SVGAnimatedLength refY;
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration markerUnits;
    +  [Constant]
       readonly attribute SVGAnimatedLength markerWidth;
    +  [Constant]
       readonly attribute SVGAnimatedLength markerHeight;
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration orientType;
    +  [Constant]
       readonly attribute SVGAnimatedAngle orientAngle;
     
       void setOrientToAuto();
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGMaskElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGMaskElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGMaskElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGMaskElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -16,11 +16,17 @@
       const unsigned short SVG_MASKTYPE_LUMINANCE = 0;
       const unsigned short SVG_MASKTYPE_ALPHA = 1;
     
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration maskUnits;
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration maskContentUnits;
    +  [Constant]
       readonly attribute SVGAnimatedLength x;
    +  [Constant]
       readonly attribute SVGAnimatedLength y;
    +  [Constant]
       readonly attribute SVGAnimatedLength width;
    +  [Constant]
       readonly attribute SVGAnimatedLength height;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGPathElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGPathElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGPathElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGPathElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -11,6 +11,7 @@
      */
     interface SVGPathElement : SVGGraphicsElement {
     
    +  [Constant]
       readonly attribute SVGAnimatedNumber pathLength;
     
       float getTotalLength();
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGPathSeg.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGPathSeg.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGPathSeg.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGPathSeg.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -34,7 +34,9 @@
       const unsigned short PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18;
       const unsigned short PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19;
     
    +  [Pure]
       readonly attribute unsigned short pathSegType;
    +  [Pure]
       readonly attribute DOMString pathSegTypeAsLetter;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGRadialGradientElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGRadialGradientElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGRadialGradientElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGRadialGradientElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -11,10 +11,15 @@
      */
     
     interface SVGRadialGradientElement : SVGGradientElement {
    +  [Constant]
       readonly attribute SVGAnimatedLength cx;
    +  [Constant]
       readonly attribute SVGAnimatedLength cy;
    +  [Constant]
       readonly attribute SVGAnimatedLength r;
    +  [Constant]
       readonly attribute SVGAnimatedLength fx;
    +  [Constant]
       readonly attribute SVGAnimatedLength fy;
       // readonly attribute SVGAnimatedLength fr;
     };
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGStopElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGStopElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGStopElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGStopElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -11,6 +11,7 @@
      */
     
     interface SVGStopElement : SVGElement {
    +  [Constant]
       readonly attribute SVGAnimatedNumber offset;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGSVGElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGSVGElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGSVGElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGSVGElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -14,9 +14,13 @@
     
     interface SVGSVGElement : SVGGraphicsElement {
     
    +  [Constant]
       readonly attribute SVGAnimatedLength x;
    +  [Constant]
       readonly attribute SVGAnimatedLength y;
    +  [Constant]
       readonly attribute SVGAnimatedLength width;
    +  [Constant]
       readonly attribute SVGAnimatedLength height;
       // readonly attribute SVGRect viewport;
       [Constant]
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGTextContentElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGTextContentElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGTextContentElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGTextContentElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -17,7 +17,9 @@
       const unsigned short LENGTHADJUST_SPACING = 1;
       const unsigned short LENGTHADJUST_SPACINGANDGLYPHS = 2;
     
    +  [Constant]
       readonly attribute SVGAnimatedLength textLength;
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration lengthAdjust;
     
       long getNumberOfChars();
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGTextPathElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGTextPathElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGTextPathElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGTextPathElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -22,8 +22,11 @@
       const unsigned short TEXTPATH_SPACINGTYPE_AUTO = 1;
       const unsigned short TEXTPATH_SPACINGTYPE_EXACT = 2;
     
    +  [Constant]
       readonly attribute SVGAnimatedLength startOffset;
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration method;
    +  [Constant]
       readonly attribute SVGAnimatedEnumeration spacing;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGTextPositioningElement.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGTextPositioningElement.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGTextPositioningElement.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGTextPositioningElement.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -11,10 +11,15 @@
      */
     
     interface SVGTextPositioningElement : SVGTextContentElement {
    +  [Constant]
       readonly attribute SVGAnimatedLengthList x;
    +  [Constant]
       readonly attribute SVGAnimatedLengthList y;
    +  [Constant]
       readonly attribute SVGAnimatedLengthList dx;
    +  [Constant]
       readonly attribute SVGAnimatedLengthList dy;
    +  [Constant]
       readonly attribute SVGAnimatedNumberList rotate;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGURIReference.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGURIReference.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/SVGURIReference.webidl	2015-01-25 22:24:40.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/SVGURIReference.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -12,6 +12,7 @@
     
     [NoInterfaceObject]
     interface SVGURIReference {
    +  [Constant]
       readonly attribute SVGAnimatedString href;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/Window.webidl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/Window.webidl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/webidl/Window.webidl	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/webidl/Window.webidl	2015-02-03 14:33:54.000000000 +0000
    @@ -230,10 +230,7 @@
     };
     
     // https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html
    -partial interface Window {
    -  //[Throws] readonly attribute Crypto crypto;
    -  [Throws] readonly attribute nsIDOMCrypto crypto;
    -};
    +Window implements GlobalCrypto;
     
     #ifdef MOZ_WEBSPEECH
     // http://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
    @@ -405,16 +402,16 @@
     
     [Func="IsChromeOrXBL"]
     interface ChromeWindow {
    -  [Func="nsGlobalWindow::IsChromeWindow"]
    +  [Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
       const unsigned short STATE_MAXIMIZED = 1;
    -  [Func="nsGlobalWindow::IsChromeWindow"]
    +  [Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
       const unsigned short STATE_MINIMIZED = 2;
    -  [Func="nsGlobalWindow::IsChromeWindow"]
    +  [Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
       const unsigned short STATE_NORMAL = 3;
    -  [Func="nsGlobalWindow::IsChromeWindow"]
    +  [Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
       const unsigned short STATE_FULLSCREEN = 4;
     
    -  [Func="nsGlobalWindow::IsChromeWindow"]
    +  [Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
       readonly attribute unsigned short windowState;
     
       /**
    @@ -422,40 +419,40 @@
        * utility functions implemented by chrome script. It will be null
        * for DOMWindows not corresponding to browsers.
        */
    -  [Throws, Func="nsGlobalWindow::IsChromeWindow"]
    +  [Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
                attribute nsIBrowserDOMWindow? browserDOMWindow;
     
    -  [Throws, Func="nsGlobalWindow::IsChromeWindow"]
    +  [Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
       void                      getAttention();
     
    -  [Throws, Func="nsGlobalWindow::IsChromeWindow"]
    +  [Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
       void                      getAttentionWithCycleCount(long aCycleCount);
     
    -  [Throws, Func="nsGlobalWindow::IsChromeWindow"]
    +  [Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
       void                      setCursor(DOMString cursor);
     
    -  [Throws, Func="nsGlobalWindow::IsChromeWindow", UnsafeInPrerendering]
    +  [Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow", UnsafeInPrerendering]
       void                      maximize();
    -  [Throws, Func="nsGlobalWindow::IsChromeWindow", UnsafeInPrerendering]
    +  [Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow", UnsafeInPrerendering]
       void                      minimize();
    -  [Throws, Func="nsGlobalWindow::IsChromeWindow", UnsafeInPrerendering]
    +  [Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow", UnsafeInPrerendering]
       void                      restore();
     
       /**
        * Notify a default button is loaded on a dialog or a wizard.
        * defaultButton is the default button.
        */
    -  [Throws, Func="nsGlobalWindow::IsChromeWindow"]
    +  [Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
       void notifyDefaultButtonLoaded(Element defaultButton);
     
    -  [Throws, Func="nsGlobalWindow::IsChromeWindow"]
    +  [Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
       readonly attribute nsIMessageBroadcaster messageManager;
     
       /**
        * Returns the message manager identified by the given group name that
        * manages all frame loaders belonging to that group.
        */
    -  [Throws, Func="nsGlobalWindow::IsChromeWindow"]
    +  [Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
       nsIMessageBroadcaster getGroupMessageManager(DOMString aGroup);
     
       /**
    @@ -468,7 +465,7 @@
        *
        * Throws NS_ERROR_NOT_IMPLEMENTED if the OS doesn't support this.
        */
    -  [Throws, Func="nsGlobalWindow::IsChromeWindow"]
    +  [Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
       void beginWindowMove(Event mouseDownEvent, optional Element? panel = null);
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/wifi/DOMWifiP2pManager.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/wifi/DOMWifiP2pManager.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/wifi/DOMWifiP2pManager.js	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/wifi/DOMWifiP2pManager.js	2015-02-03 14:33:54.000000000 +0000
    @@ -116,6 +116,11 @@
         let state = this._mm.sendSyncMessage("WifiP2pManager:getState")[0];
         if (state) {
           debug('State: ' + JSON.stringify(state));
    +      this.enabled = state.enabled;
    +      this.currentPeer = state.currentPeer;
    +      if (state.groupOwner) {
    +        this.groupOwner = new MozWifiP2pGroupOwner(state.groupOwner);
    +      }
         } else {
           debug('Failed to get state');
         }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/wifi/WifiP2pManager.jsm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/wifi/WifiP2pManager.jsm
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/wifi/WifiP2pManager.jsm	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/wifi/WifiP2pManager.jsm	2015-02-03 14:33:54.000000000 +0000
    @@ -597,12 +597,14 @@
           function onFailure()
           {
             _onEnabled(false);
    +        _observer.onDisabled();
             _sm.gotoState(stateDisabled);
           }
     
           function onSuccess()
           {
             _onEnabled(true);
    +        _observer.onEnabled();
             _sm.gotoState(stateInactive);
           }
     
    @@ -1335,6 +1337,7 @@
                 gNetworkService.disableInterface(P2P_INTERFACE_NAME, function (success){
                   debug('Disabled interface: ' + P2P_INTERFACE_NAME);
                   _onDisabled(true);
    +              _observer.onDisabled();
                   _sm.gotoState(stateDisabled);
                 });
               });
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/wifi/WifiP2pWorkerObserver.jsm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/wifi/WifiP2pWorkerObserver.jsm
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/wifi/WifiP2pWorkerObserver.jsm	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/wifi/WifiP2pWorkerObserver.jsm	2015-02-03 14:33:54.000000000 +0000
    @@ -39,6 +39,9 @@
       let _localDevice;
       let _peerList = {}; // List of P2pDevice.
       let _domManagers = [];
    +  let _enabled = false;
    +  let _groupOwner = null;
    +  let _currentPeer = null;
     
       // Constructor of P2pDevice. It will be exposed to DOM.
       //
    @@ -130,10 +133,12 @@
     
         onEnabled: function() {
           _peerList = [];
    +      _enabled = true;
           fireEvent("p2pUp", {});
         },
     
         onDisbaled: function() {
    +      _enabled = false;
           fireEvent("p2pDown", {});
         },
     
    @@ -182,6 +187,9 @@
           peer.connectionStatus = CONNECTION_STATUS_CONNECTED;
           peer.isGroupOwner = (aPeer.address === aGroupOwner.address);
     
    +      _groupOwner = go;
    +      _currentPeer = peer;
    +
           fireEvent('onconnected', { groupOwner: go, peer: peer });
         },
     
    @@ -193,6 +201,10 @@
           }
     
           peer.connectionStatus = CONNECTION_STATUS_DISCONNECTED;
    +
    +      _groupOwner = null;
    +      _currentPeer = null;
    +
           fireEvent('ondisconnected', { peer: peer });
         },
     
    @@ -227,7 +239,11 @@
           switch (aMessage.name) {
             case "WifiP2pManager:getState": // A new DOM manager is created.
               addDomManager(msg);
    -          return { peerList: _peerList, }; // Synchronous call. Simply return it.
    +          return { // Synchronous call. Simply return it.
    +            enabled: _enabled,
    +            groupOwner: _groupOwner,
    +            currentPeer: _currentPeer
    +          };
     
             case "WifiP2pManager:setScanEnabled":
               {
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/RuntimeService.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/RuntimeService.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/RuntimeService.cpp	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/RuntimeService.cpp	2015-02-03 14:33:54.000000000 +0000
    @@ -400,8 +400,8 @@
     }
     
     void
    -UpdatOtherJSGCMemoryOption(RuntimeService* aRuntimeService,
    -                           JSGCParamKey aKey, uint32_t aValue)
    +UpdateOtherJSGCMemoryOption(RuntimeService* aRuntimeService,
    +                            JSGCParamKey aKey, uint32_t aValue)
     {
       AssertIsOnMainThread();
     
    @@ -466,14 +466,14 @@
           uint32_t value = (prefValue <= 0 || prefValue >= 0x1000) ?
                            uint32_t(-1) :
                            uint32_t(prefValue) * 1024 * 1024;
    -      UpdatOtherJSGCMemoryOption(rts, JSGC_MAX_BYTES, value);
    +      UpdateOtherJSGCMemoryOption(rts, JSGC_MAX_BYTES, value);
           continue;
         }
     
         matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "high_water_mark");
         if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 1)) {
           int32_t prefValue = GetWorkerPref(matchName, 128);
    -      UpdatOtherJSGCMemoryOption(rts, JSGC_MAX_MALLOC_BYTES,
    +      UpdateOtherJSGCMemoryOption(rts, JSGC_MAX_MALLOC_BYTES,
                                      uint32_t(prefValue) * 1024 * 1024);
           continue;
         }
    @@ -538,7 +538,7 @@
           int32_t prefValue = GetWorkerPref(matchName, -1);
           uint32_t value =
             (prefValue <= 0 || prefValue >= 100000) ? 0 : uint32_t(prefValue);
    -      UpdatOtherJSGCMemoryOption(rts, JSGC_SLICE_TIME_BUDGET, value);
    +      UpdateOtherJSGCMemoryOption(rts, JSGC_SLICE_TIME_BUDGET, value);
           continue;
         }
     
    @@ -546,7 +546,7 @@
         if (memPrefName == matchName ||
             (gRuntimeServiceDuringInit && index == 10)) {
           bool prefValue = GetWorkerPref(matchName, false);
    -      UpdatOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_HEAP_GROWTH,
    +      UpdateOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_HEAP_GROWTH,
                                      prefValue ? 0 : 1);
           continue;
         }
    @@ -555,7 +555,7 @@
         if (memPrefName == matchName ||
             (gRuntimeServiceDuringInit && index == 11)) {
           bool prefValue = GetWorkerPref(matchName, false);
    -      UpdatOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_MARK_SLICE,
    +      UpdateOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_MARK_SLICE,
                                      prefValue ? 0 : 1);
           continue;
         }
    @@ -574,6 +574,15 @@
           continue;
         }
     
    +    matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_compacting");
    +    if (memPrefName == matchName ||
    +        (gRuntimeServiceDuringInit && index == 14)) {
    +      bool prefValue = GetWorkerPref(matchName, false);
    +      UpdateOtherJSGCMemoryOption(rts, JSGC_COMPACTING_ENABLED,
    +                                 prefValue ? 0 : 1);
    +      continue;
    +    }
    +
     #ifdef DEBUG
         nsAutoCString message("Workers don't support the 'mem.");
         message.Append(memPrefName);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/ServiceWorkerCommon.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/ServiceWorkerCommon.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/ServiceWorkerCommon.h	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/ServiceWorkerCommon.h	2015-02-03 14:33:54.000000000 +0000
    @@ -12,11 +12,11 @@
     
     // Use multiples of 2 since they can be bitwise ORed when calling
     // InvalidateServiceWorkerRegistrationWorker.
    -MOZ_BEGIN_ENUM_CLASS(WhichServiceWorker)
    +enum class WhichServiceWorker {
       INSTALLING_WORKER = 1,
       WAITING_WORKER    = 2,
       ACTIVE_WORKER     = 4,
    -MOZ_END_ENUM_CLASS(WhichServiceWorker)
    +};
     MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(WhichServiceWorker)
     
     } // dom namespace
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/ServiceWorkerContainer.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/ServiceWorkerContainer.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/ServiceWorkerContainer.cpp	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/ServiceWorkerContainer.cpp	2015-02-03 14:33:54.000000000 +0000
    @@ -56,7 +56,10 @@
       if (window) {
         nsCOMPtr swm =
           mozilla::services::GetServiceWorkerManager();
    -    MOZ_ASSERT(swm);
    +    if (!swm) {
    +      // If the browser is shutting down, we don't need to remove the promise.
    +      return;
    +    }
     
         swm->RemoveReadyPromise(window);
       }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/ServiceWorkerEvents.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/ServiceWorkerEvents.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/ServiceWorkerEvents.h	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/ServiceWorkerEvents.h	2015-02-03 14:33:54.000000000 +0000
    @@ -69,6 +69,11 @@
         nsRefPtr p = mPromise;
         return p.forget();
       }
    +
    +  virtual ExtendableEvent* AsExtendableEvent() MOZ_OVERRIDE
    +  {
    +    return this;
    +  }
     };
     
     class InstallEvent MOZ_FINAL : public ExtendableEvent
    @@ -132,6 +137,11 @@
       {
         return mActivateImmediately;
       }
    +
    +  InstallEvent* AsInstallEvent() MOZ_OVERRIDE
    +  {
    +    return this;
    +  }
     };
     
     END_WORKERS_NAMESPACE
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/ServiceWorkerManager.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/ServiceWorkerManager.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/ServiceWorkerManager.cpp	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/ServiceWorkerManager.cpp	2015-02-03 14:33:54.000000000 +0000
    @@ -106,26 +106,6 @@
       }
     }
     
    -class QueueFireUpdateFoundRunnable MOZ_FINAL : public nsRunnable
    -{
    -  nsRefPtr mRegistration;
    -public:
    -  explicit QueueFireUpdateFoundRunnable(ServiceWorkerRegistrationInfo* aReg)
    -    : mRegistration(aReg)
    -  {
    -    MOZ_ASSERT(aReg);
    -  }
    -
    -  NS_IMETHOD
    -  Run()
    -  {
    -    nsRefPtr swm = ServiceWorkerManager::GetInstance();
    -    swm->FireEventOnServiceWorkerRegistrations(mRegistration,
    -                                               NS_LITERAL_STRING("updatefound"));
    -    return NS_OK;
    -  }
    -};
    -
     //////////////////////////
     // ServiceWorkerManager //
     //////////////////////////
    @@ -163,17 +143,60 @@
     
     class ServiceWorkerRegisterJob;
     
    -class FinishInstallRunnable MOZ_FINAL : public nsRunnable
    +class ContinueLifecycleTask : public nsISupports
     {
    -  nsMainThreadPtrHandle mJob;
    +  NS_DECL_ISUPPORTS
    +
    +protected:
    +  virtual ~ContinueLifecycleTask()
    +  { }
    +
    +public:
    +  virtual void ContinueAfterWorkerEvent(bool aSuccess,
    +                                        bool aActivateImmediately) = 0;
    +};
    +
    +NS_IMPL_ISUPPORTS0(ContinueLifecycleTask);
    +
    +class ContinueInstallTask MOZ_FINAL : public ContinueLifecycleTask
    +{
    +  nsRefPtr mJob;
    +
    +public:
    +  explicit ContinueInstallTask(ServiceWorkerRegisterJob* aJob)
    +    : mJob(aJob)
    +  { }
    +
    +  void ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately) MOZ_OVERRIDE;
    +};
    +
    +class ContinueActivateTask MOZ_FINAL : public ContinueLifecycleTask
    +{
    +  nsRefPtr mRegistration;
    +
    +public:
    +  explicit ContinueActivateTask(ServiceWorkerRegistrationInfo* aReg)
    +    : mRegistration(aReg)
    +  { }
    +
    +  void
    +  ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately /* unused */) MOZ_OVERRIDE
    +  {
    +    mRegistration->FinishActivate(aSuccess);
    +  }
    +};
    +
    +class ContinueLifecycleRunnable MOZ_FINAL : public nsRunnable
    +{
    +  nsMainThreadPtrHandle mTask;
       bool mSuccess;
       bool mActivateImmediately;
     
     public:
    -  explicit FinishInstallRunnable(const nsMainThreadPtrHandle& aJob,
    -                                 bool aSuccess,
    -                                 bool aActivateImmediately)
    -    : mJob(aJob)
    +  ContinueLifecycleRunnable(const nsMainThreadPtrHandle& aTask,
    +                            bool aSuccess,
    +                            bool aActivateImmediately)
    +    : mTask(aTask)
         , mSuccess(aSuccess)
         , mActivateImmediately(aActivateImmediately)
       {
    @@ -181,7 +204,12 @@
       }
     
       NS_IMETHOD
    -  Run() MOZ_OVERRIDE;
    +  Run() MOZ_OVERRIDE
    +  {
    +    AssertIsOnMainThread();
    +    mTask->ContinueAfterWorkerEvent(mSuccess, mActivateImmediately);
    +    return NS_OK;
    +  }
     };
     
     /*
    @@ -190,33 +218,33 @@
      * ServiceWorkers, so the parent thread -> worker thread requirement for
      * runnables is satisfied.
      */
    -class InstallEventRunnable MOZ_FINAL : public WorkerRunnable
    +class LifecycleEventWorkerRunnable MOZ_FINAL : public WorkerRunnable
     {
    -  nsMainThreadPtrHandle mJob;
    -  nsCString mScope;
    +  nsString mEventName;
    +  nsMainThreadPtrHandle mTask;
     
     public:
    -  InstallEventRunnable(WorkerPrivate* aWorkerPrivate,
    -                       const nsMainThreadPtrHandle& aJob,
    -                       const nsCString& aScope)
    -      : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
    -        mJob(aJob),
    -        mScope(aScope)
    +  LifecycleEventWorkerRunnable(WorkerPrivate* aWorkerPrivate,
    +                               const nsString& aEventName,
    +                               const nsMainThreadPtrHandle& aTask)
    +      : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
    +      , mEventName(aEventName)
    +      , mTask(aTask)
       {
         AssertIsOnMainThread();
         MOZ_ASSERT(aWorkerPrivate);
       }
     
       bool
    -  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    +  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
       {
         MOZ_ASSERT(aWorkerPrivate);
    -    return DispatchInstallEvent(aCx, aWorkerPrivate);
    +    return DispatchLifecycleEvent(aCx, aWorkerPrivate);
       }
     
     private:
       bool
    -  DispatchInstallEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
    +  DispatchLifecycleEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
     
     };
     
    @@ -356,7 +384,7 @@
     class ServiceWorkerRegisterJob MOZ_FINAL : public ServiceWorkerJob,
                                                public nsIStreamLoaderObserver
     {
    -  friend class FinishInstallRunnable;
    +  friend class ContinueInstallTask;
     
       nsCString mScope;
       nsCString mScriptSpec;
    @@ -504,8 +532,7 @@
       {
         MOZ_ASSERT(mCallback);
         mCallback->UpdateFailed(aError);
    -    mCallback = nullptr;
    -    Done(NS_ERROR_DOM_JS_EXCEPTION);
    +    FailCommon(NS_ERROR_DOM_JS_EXCEPTION);
       }
     
       // Public so our error handling code can continue with a successful worker.
    @@ -531,15 +558,14 @@
     
         Succeed();
     
    -    nsRefPtr upr =
    -      new QueueFireUpdateFoundRunnable(mRegistration);
    +    nsCOMPtr upr =
    +      NS_NewRunnableMethodWithArg(swm,
    +                                                                  &ServiceWorkerManager::FireUpdateFound,
    +                                                                  mRegistration);
         NS_DispatchToMainThread(upr);
     
    -    // XXXnsm this leads to double fetches right now, ideally we'll be able to
    -    // use the persistent cache later.
    -    nsRefPtr upcasted = this;
    -    nsMainThreadPtrHandle handle(
    -        new nsMainThreadPtrHolder(upcasted));
    +    nsMainThreadPtrHandle handle(
    +        new nsMainThreadPtrHolder(new ContinueInstallTask(this)));
     
         nsRefPtr serviceWorker;
         nsresult rv =
    @@ -552,8 +578,8 @@
           return;
         }
     
    -    nsRefPtr r =
    -      new InstallEventRunnable(serviceWorker->GetWorkerPrivate(), handle, mRegistration->mScope);
    +    nsRefPtr r =
    +      new LifecycleEventWorkerRunnable(serviceWorker->GetWorkerPrivate(), NS_LITERAL_STRING("install"), handle);
     
         AutoJSAPI jsapi;
         jsapi.Init();
    @@ -634,16 +660,39 @@
         mCallback = nullptr;
       }
     
    +  void
    +  FailCommon(nsresult aRv)
    +  {
    +    mCallback = nullptr;
    +    MaybeRemoveRegistration();
    +    // Ensures that the job can't do anything useful from this point on.
    +    mRegistration = nullptr;
    +    Done(aRv);
    +  }
    +
       // This MUST only be called when the job is still performing actions related
       // to registration or update. After the spec resolves the update promise, use
       // Done() with the failure code instead.
       void
    -  Fail(nsresult rv)
    +  Fail(nsresult aRv)
       {
         MOZ_ASSERT(mCallback);
    -    mCallback->UpdateFailed(rv);
    -    mCallback = nullptr;
    -    Done(rv);
    +    mCallback->UpdateFailed(aRv);
    +    FailCommon(aRv);
    +  }
    +
    +  void
    +  MaybeRemoveRegistration()
    +  {
    +    MOZ_ASSERT(mRegistration);
    +    nsRefPtr newest = mRegistration->Newest();
    +    if (!newest) {
    +      nsRefPtr swm = ServiceWorkerManager::GetInstance();
    +      nsRefPtr domainInfo =
    +        swm->GetDomainInfo(mRegistration->mScope);
    +      MOZ_ASSERT(domainInfo);
    +      domainInfo->RemoveRegistration(mRegistration);
    +    }
       }
     
       void
    @@ -666,6 +715,7 @@
           mRegistration->mInstallingWorker = nullptr;
           swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
                                                          WhichServiceWorker::INSTALLING_WORKER);
    +      MaybeRemoveRegistration();
           return Done(NS_ERROR_DOM_ABORT_ERR);
         }
     
    @@ -705,6 +755,12 @@
       return NS_OK;
     }
     
    +void
    +ContinueInstallTask::ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately)
    +{
    +  mJob->ContinueAfterInstallEvent(aSuccess, aActivateImmediately);
    +}
    +
     // If we return an error code here, the ServiceWorkerContainer will
     // automatically reject the Promise.
     NS_IMETHODIMP
    @@ -853,33 +909,23 @@
       return NS_OK;
     }
     
    -NS_IMETHODIMP
    -FinishInstallRunnable::Run()
    -{
    -  AssertIsOnMainThread();
    -  nsRefPtr job = static_cast(mJob.get());
    -  nsRefPtr upjob = static_cast(job.get());
    -  MOZ_ASSERT(upjob);
    -  upjob->ContinueAfterInstallEvent(mSuccess, mActivateImmediately);
    -  return NS_OK;
    -}
    -
     /*
    - * Used to handle InstallEvent::waitUntil() and proceed with installation.
    + * Used to handle ExtendableEvent::waitUntil() and proceed with
    + * installation/activation.
      */
    -class FinishInstallHandler MOZ_FINAL : public PromiseNativeHandler
    +class LifecycleEventPromiseHandler MOZ_FINAL : public PromiseNativeHandler
     {
    -  nsMainThreadPtrHandle mJob;
    +  nsMainThreadPtrHandle mTask;
       bool mActivateImmediately;
     
       virtual
    -  ~FinishInstallHandler()
    +  ~LifecycleEventPromiseHandler()
       { }
     
     public:
    -  FinishInstallHandler(const nsMainThreadPtrHandle& aJob,
    -                       bool aActivateImmediately)
    -    : mJob(aJob)
    +  LifecycleEventPromiseHandler(const nsMainThreadPtrHandle& aTask,
    +                               bool aActivateImmediately)
    +    : mTask(aTask)
         , mActivateImmediately(aActivateImmediately)
       {
         MOZ_ASSERT(!NS_IsMainThread());
    @@ -892,33 +938,47 @@
         MOZ_ASSERT(workerPrivate);
         workerPrivate->AssertIsOnWorkerThread();
     
    -    nsRefPtr r = new FinishInstallRunnable(mJob, true, mActivateImmediately);
    +    nsRefPtr r =
    +      new ContinueLifecycleRunnable(mTask, true /* success */, mActivateImmediately);
         NS_DispatchToMainThread(r);
       }
     
       void
       RejectedCallback(JSContext* aCx, JS::Handle aValue) MOZ_OVERRIDE
       {
    -    nsRefPtr r = new FinishInstallRunnable(mJob, false, mActivateImmediately);
    +    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    +    MOZ_ASSERT(workerPrivate);
    +    workerPrivate->AssertIsOnWorkerThread();
    +
    +    nsRefPtr r =
    +      new ContinueLifecycleRunnable(mTask, false /* success */, mActivateImmediately);
         NS_DispatchToMainThread(r);
       }
     };
     
     bool
    -InstallEventRunnable::DispatchInstallEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    +LifecycleEventWorkerRunnable::DispatchLifecycleEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
     {
       aWorkerPrivate->AssertIsOnWorkerThread();
       MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
    -  InstallEventInit init;
    -  init.mBubbles = false;
    -  init.mCancelable = true;
     
    -  // FIXME(nsm): Bug 982787 pass previous active worker.
    -
    -  // FIXME(nsm): Set error handler so we can grab handler errors.
    +  nsRefPtr event;
       nsRefPtr target = aWorkerPrivate->GlobalScope();
    -  nsRefPtr event =
    -    InstallEvent::Constructor(target, NS_LITERAL_STRING("install"), init);
    +
    +  if (mEventName.EqualsASCII("install")) {
    +    // FIXME(nsm): Bug 982787 pass previous active worker.
    +    InstallEventInit init;
    +    init.mBubbles = false;
    +    init.mCancelable = true;
    +    event = InstallEvent::Constructor(target, mEventName, init);
    +  } else if (mEventName.EqualsASCII("activate")) {
    +    ExtendableEventInit init;
    +    init.mBubbles = false;
    +    init.mCancelable = true;
    +    event = ExtendableEvent::Constructor(target, mEventName, init);
    +  } else {
    +    MOZ_CRASH("Unexpected lifecycle event");
    +  }
     
       event->SetTrusted(true);
     
    @@ -952,134 +1012,20 @@
         return false;
       }
     
    -  nsRefPtr handler =
    -    new FinishInstallHandler(mJob, event->ActivateImmediately());
    -  waitUntilPromise->AppendNativeHandler(handler);
    -  return true;
    -}
    -
    -class FinishActivationRunnable MOZ_FINAL : public nsRunnable
    -{
    -  bool mSuccess;
    -  nsMainThreadPtrHandle mRegistration;
    -
    -public:
    -  FinishActivationRunnable(bool aSuccess,
    -                           const nsMainThreadPtrHandle& aRegistration)
    -    : mSuccess(aSuccess)
    -    , mRegistration(aRegistration)
    -  {
    -    MOZ_ASSERT(!NS_IsMainThread());
    -  }
    -
    -  NS_IMETHODIMP
    -  Run()
    -  {
    -    AssertIsOnMainThread();
    -
    -    mRegistration->FinishActivate(mSuccess);
    -    return NS_OK;
    -  }
    -};
    -
    -class FinishActivateHandler MOZ_FINAL : public PromiseNativeHandler
    -{
    -  nsMainThreadPtrHandle mRegistration;
    -
    -public:
    -  explicit FinishActivateHandler(const nsMainThreadPtrHandle& aRegistration)
    -    : mRegistration(aRegistration)
    -  {
    -    MOZ_ASSERT(!NS_IsMainThread());
    -  }
    -
    -  virtual
    -  ~FinishActivateHandler()
    -  { }
    -
    -  void
    -  ResolvedCallback(JSContext* aCx, JS::Handle aValue) MOZ_OVERRIDE
    -  {
    -    nsRefPtr r = new FinishActivationRunnable(true /* success */, mRegistration);
    -    NS_DispatchToMainThread(r);
    -  }
    -
    -  void
    -  RejectedCallback(JSContext* aCx, JS::Handle aValue) MOZ_OVERRIDE
    -  {
    -    nsRefPtr r = new FinishActivationRunnable(false /* success */, mRegistration);
    -    NS_DispatchToMainThread(r);
    -  }
    -};
    -
    -class ActivateEventRunnable : public WorkerRunnable
    -{
    -  nsMainThreadPtrHandle mRegistration;
    -
    -public:
    -  ActivateEventRunnable(WorkerPrivate* aWorkerPrivate,
    -                        const nsMainThreadPtrHandle& aRegistration)
    -      : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
    -        mRegistration(aRegistration)
    -  {
    -    MOZ_ASSERT(aWorkerPrivate);
    -  }
    -
    -  bool
    -  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    -  {
    -    MOZ_ASSERT(aWorkerPrivate);
    -    return DispatchActivateEvent(aCx, aWorkerPrivate);
    -  }
    -
    -private:
    -  bool
    -  DispatchActivateEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    -  {
    -    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
    -    nsRefPtr target = do_QueryObject(aWorkerPrivate->GlobalScope());
    -
    +  // activateimmediately is only relevant to "install" event.
    +  bool activateImmediately = false;
    +  InstallEvent* installEvent = event->AsInstallEvent();
    +  if (installEvent) {
    +    activateImmediately = installEvent->ActivateImmediately();
         // FIXME(nsm): Set activeWorker to the correct thing.
    -    EventInit init;
    -    init.mBubbles = false;
    -    init.mCancelable = true;
    -    nsRefPtr event =
    -      ExtendableEvent::Constructor(target, NS_LITERAL_STRING("activate"), init);
    -
    -    event->SetTrusted(true);
    -
    -    nsRefPtr waitUntilPromise;
    -
         // FIXME(nsm): Install error handler for any listener errors.
    -    ErrorResult result;
    -    result = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
    -    WidgetEvent* internalEvent = event->GetInternalNSEvent();
    -    if (!result.Failed() && !internalEvent->mFlags.mExceptionHasBeenRisen) {
    -      waitUntilPromise = event->GetPromise();
    -      if (!waitUntilPromise) {
    -        nsCOMPtr global =
    -          do_QueryObject(aWorkerPrivate->GlobalScope());
    -        waitUntilPromise =
    -          Promise::Resolve(global,
    -                           aCx, JS::UndefinedHandleValue, result);
    -      }
    -    } else {
    -      nsCOMPtr global =
    -        do_QueryObject(aWorkerPrivate->GlobalScope());
    -      // Continue with a canceled install.
    -      waitUntilPromise = Promise::Reject(global, aCx,
    -                                         JS::UndefinedHandleValue, result);
    -    }
    -
    -    if (result.Failed()) {
    -      return false;
    -    }
    -
    -    nsRefPtr handler = new FinishActivateHandler(mRegistration);
    -    waitUntilPromise->AppendNativeHandler(handler);
    -    return true;
       }
    -};
    +
    +  nsRefPtr handler =
    +    new LifecycleEventPromiseHandler(mTask, activateImmediately);
    +  waitUntilPromise->AppendNativeHandler(handler);
    +  return true;
    +}
     
     void
     ServiceWorkerRegistrationInfo::TryToActivate()
    @@ -1138,11 +1084,11 @@
         return;
       }
     
    -  nsMainThreadPtrHandle handle(
    -    new nsMainThreadPtrHolder(this));
    +  nsMainThreadPtrHandle handle(
    +    new nsMainThreadPtrHolder(new ContinueActivateTask(this)));
     
    -  nsRefPtr r =
    -    new ActivateEventRunnable(serviceWorker->GetWorkerPrivate(), handle);
    +  nsRefPtr r =
    +    new LifecycleEventWorkerRunnable(serviceWorker->GetWorkerPrivate(), NS_LITERAL_STRING("activate"), handle);
     
       AutoJSAPI jsapi;
       jsapi.Init();
    @@ -1475,85 +1421,117 @@
       return false;
     }
     
    -NS_IMETHODIMP
    -ServiceWorkerManager::Unregister(nsIServiceWorkerUnregisterCallback* aCallback,
    -                                 const nsAString& aScope)
    +class ServiceWorkerUnregisterJob MOZ_FINAL : public ServiceWorkerJob
     {
    -  AssertIsOnMainThread();
    -  MOZ_ASSERT(aCallback);
    +  nsRefPtr mRegistration;
    +  const nsCString mScope;
    +  nsCOMPtr mCallback;
     
    -  nsCOMPtr scopeURI;
    -  nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
    -  if (NS_WARN_IF(NS_FAILED(rv))) {
    -    return NS_ERROR_DOM_SECURITY_ERR;
    +  ~ServiceWorkerUnregisterJob()
    +  { }
    +
    +public:
    +  ServiceWorkerUnregisterJob(ServiceWorkerJobQueue* aQueue,
    +                             const nsACString& aScope,
    +                             nsIServiceWorkerUnregisterCallback* aCallback)
    +    : ServiceWorkerJob(aQueue)
    +    , mScope(aScope)
    +    , mCallback(aCallback)
    +  {
    +    AssertIsOnMainThread();
       }
     
    -  /*
    -   * Implements the async aspects of the unregister algorithm.
    -   */
    -  class UnregisterRunnable : public nsRunnable
    -  {
    -    nsCOMPtr mCallback;
    -    nsCOMPtr mScopeURI;
    -
    -  public:
    -    UnregisterRunnable(nsIServiceWorkerUnregisterCallback* aCallback,
    -                       nsIURI* aScopeURI)
    -      : mCallback(aCallback), mScopeURI(aScopeURI)
    -    {
    -      AssertIsOnMainThread();
    -    }
    -
    -    NS_IMETHODIMP
    -    Run()
    -    {
    -      AssertIsOnMainThread();
    +  void
    +  Start() MOZ_OVERRIDE
    +  {
    +    AssertIsOnMainThread();
    +    nsCOMPtr r =
    +      NS_NewRunnableMethod(this, &ServiceWorkerUnregisterJob::UnregisterAndDone);
    +    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
    +  }
     
    -      nsRefPtr swm = ServiceWorkerManager::GetInstance();
    +private:
    +  // You probably want UnregisterAndDone().
    +  nsresult
    +  Unregister()
    +  {
    +    AssertIsOnMainThread();
     
    -      nsRefPtr domainInfo =
    -        swm->GetDomainInfo(mScopeURI);
    -      MOZ_ASSERT(domainInfo);
    +    nsRefPtr swm = ServiceWorkerManager::GetInstance();
     
    -      nsCString spec;
    -      nsresult rv = mScopeURI->GetSpecIgnoringRef(spec);
    -      if (NS_WARN_IF(NS_FAILED(rv))) {
    -        return mCallback->UnregisterFailed();
    -      }
    +    nsRefPtr domainInfo =
    +      swm->GetDomainInfo(mScope);
    +    MOZ_ASSERT(domainInfo);
     
    -      nsRefPtr registration;
    -      if (!domainInfo->mServiceWorkerRegistrationInfos.Get(spec,
    -                                                           getter_AddRefs(registration))) {
    -        return mCallback->UnregisterSucceeded(false);
    -      }
    +    // "Let registration be the result of running [[Get Registration]]
    +    // algorithm passing scope as the argument."
    +    nsRefPtr registration;
    +    if (!domainInfo->mServiceWorkerRegistrationInfos.Get(mScope,
    +                                                         getter_AddRefs(registration))) {
    +      // "If registration is null, then, resolve promise with false."
    +      return mCallback->UnregisterSucceeded(false);
    +    }
     
    -      MOZ_ASSERT(registration);
    +    MOZ_ASSERT(registration);
    +
    +    // "Set registration's uninstalling flag."
    +    registration->mPendingUninstall = true;
    +    // "Resolve promise with true"
    +    nsresult rv = mCallback->UnregisterSucceeded(true);
    +    if (NS_WARN_IF(NS_FAILED(rv))) {
    +      return rv;
    +    }
     
    -      registration->mPendingUninstall = true;
    -      rv = mCallback->UnregisterSucceeded(true);
    -      if (NS_WARN_IF(NS_FAILED(rv))) {
    -        return rv;
    +    // "If no service worker client is using registration..."
    +    if (!registration->IsControllingDocuments()) {
    +      // "If registration's uninstalling flag is set.."
    +      if (!registration->mPendingUninstall) {
    +        return NS_OK;
           }
     
    -      // The "Wait until no document is using registration" can actually be
    -      // handled by [[HandleDocumentUnload]] in Bug 1041340, so we simply check
    -      // if the document is currently in use here.
    -      if (!registration->IsControllingDocuments()) {
    -        if (!registration->mPendingUninstall) {
    -          return NS_OK;
    -        }
    +      // "Invoke [[Clear Registration]]..."
    +      registration->Clear();
    +      domainInfo->RemoveRegistration(registration);
    +    }
     
    -        registration->Clear();
    -        domainInfo->RemoveRegistration(registration);
    -      }
    +    return NS_OK;
    +  }
     
    -      return NS_OK;
    -    }
    -  };
    +  // The unregister job is done irrespective of success or failure of any sort.
    +  void
    +  UnregisterAndDone()
    +  {
    +    Done(Unregister());
    +  }
    +};
    +
    +NS_IMETHODIMP
    +ServiceWorkerManager::Unregister(nsIServiceWorkerUnregisterCallback* aCallback,
    +                                 const nsAString& aScope)
    +{
    +  AssertIsOnMainThread();
    +  MOZ_ASSERT(aCallback);
     
    -  nsRefPtr unregisterRunnable =
    -    new UnregisterRunnable(aCallback, scopeURI);
    -  return NS_DispatchToCurrentThread(unregisterRunnable);
    +// This is not accessible by content, and callers should always ensure scope is
    +// a correct URI, so this is wrapped in DEBUG
    +#ifdef DEBUG
    +  nsCOMPtr scopeURI;
    +  nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
    +  if (NS_WARN_IF(NS_FAILED(rv))) {
    +    return NS_ERROR_DOM_SECURITY_ERR;
    +  }
    +#endif
    +
    +  NS_ConvertUTF16toUTF8 scope(aScope);
    +  nsRefPtr domainInfo =
    +    GetDomainInfo(scope);
    +  ServiceWorkerJobQueue* queue = domainInfo->GetOrCreateJobQueue(scope);
    +  MOZ_ASSERT(queue);
    +
    +  nsRefPtr job =
    +    new ServiceWorkerUnregisterJob(queue, scope, aCallback);
    +  queue->Append(job);
    +  return NS_OK;
     }
     
     /* static */
    @@ -2247,8 +2225,8 @@
       nsRefPtr cb =
         new ServiceWorkerUpdateFinishCallback();
     
    -  nsRefPtr job
    -    = new ServiceWorkerRegisterJob(queue, registration, cb);
    +  nsRefPtr job =
    +    new ServiceWorkerRegisterJob(queue, registration, cb);
       queue->Append(job);
       return NS_OK;
     }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/ServiceWorkerManager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/ServiceWorkerManager.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/ServiceWorkerManager.h	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/ServiceWorkerManager.h	2015-02-03 14:33:54.000000000 +0000
    @@ -11,7 +11,6 @@
     #include "mozilla/Attributes.h"
     #include "mozilla/LinkedList.h"
     #include "mozilla/Preferences.h"
    -#include "mozilla/TypedEnum.h"
     #include "mozilla/TypedEnumBits.h"
     #include "mozilla/WeakPtr.h"
     #include "mozilla/dom/BindingUtils.h"
    @@ -276,7 +275,7 @@
       friend class GetRegistrationsRunnable;
       friend class GetRegistrationRunnable;
       friend class QueueFireUpdateFoundRunnable;
    -  friend class UnregisterRunnable;
    +  friend class ServiceWorkerUnregisterJob;
     
     public:
       NS_DECL_ISUPPORTS
    @@ -463,6 +462,13 @@
                                             const nsAString& aName);
     
       void
    +  FireUpdateFound(ServiceWorkerRegistrationInfo* aRegistration)
    +  {
    +    FireEventOnServiceWorkerRegistrations(aRegistration,
    +                                          NS_LITERAL_STRING("updatefound"));
    +  }
    +
    +  void
       FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration);
     
       void
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/ServiceWorkerRegistration.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/ServiceWorkerRegistration.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/ServiceWorkerRegistration.cpp	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/ServiceWorkerRegistration.cpp	2015-02-03 14:33:54.000000000 +0000
    @@ -164,6 +164,7 @@
     
       nsCOMPtr scopeURI;
       nsCOMPtr baseURI = document->GetBaseURI();
    +  // "If the origin of scope is not client's origin..."
       nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), mScope, nullptr, baseURI);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    @@ -179,7 +180,7 @@
       }
     
       nsAutoCString uriSpec;
    -  aRv = scopeURI->GetSpec(uriSpec);
    +  aRv = scopeURI->GetSpecIgnoringRef(uriSpec);
       if (NS_WARN_IF(aRv.Failed())) {
         return nullptr;
       }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/ServiceWorkerRegistration.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/ServiceWorkerRegistration.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/ServiceWorkerRegistration.h	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/ServiceWorkerRegistration.h	2015-02-03 14:33:54.000000000 +0000
    @@ -8,6 +8,7 @@
     #define mozilla_dom_ServiceWorkerRegistration_h
     
     #include "mozilla/DOMEventTargetHelper.h"
    +#include "mozilla/dom/ServiceWorkerBinding.h"
     #include "mozilla/dom/ServiceWorkerCommon.h"
     
     class nsPIDOMWindow;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/test/fetch/worker_test_response.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/test/fetch/worker_test_response.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/test/fetch/worker_test_response.js	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/test/fetch/worker_test_response.js	2015-02-03 14:33:54.000000000 +0000
    @@ -52,6 +52,20 @@
       }
     }
     
    +function testOk() {
    +  var r1 = new Response("", { status: 200});
    +  ok(r1.ok, "Response with status 200 should have ok true");
    +
    +  var r2 = new Response("", { status: 204});
    +  ok(r2.ok, "Response with status 204 should have ok true");
    +
    +  var r3 = new Response("", { status: 299});
    +  ok(r3.ok, "Response with status 299 should have ok true");
    +
    +  var r4 = new Response("", { status: 302});
    +  ok(!r4.ok, "Response with status 302 should have ok false");
    +}
    +
     function testBodyUsed() {
       var res = new Response("Sample body");
       ok(!res.bodyUsed, "bodyUsed is initially false.");
    @@ -138,6 +152,7 @@
       testDefaultCtor();
       testClone();
       testRedirect();
    +  testOk();
     
       Promise.resolve()
         .then(testBodyCreation)
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/test/mochitest.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/test/mochitest.ini
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/test/mochitest.ini	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/test/mochitest.ini	2015-02-03 14:33:54.000000000 +0000
    @@ -97,6 +97,7 @@
       bug1062920_worker.js
       webSocket_sharedWorker.js
       bug1104064_worker.js
    +  worker_consoleAndBlobs.js
     
     [test_404.html]
     [test_atob.html]
    @@ -197,3 +198,4 @@
     skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #bug 982828
     [test_websocket_pref.html]
     [test_bug1104064.html]
    +[test_consoleAndBlobs.html]
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/test/serviceworkers/mochitest.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/test/serviceworkers/mochitest.ini
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/test/serviceworkers/mochitest.ini	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/test/serviceworkers/mochitest.ini	2015-02-03 14:33:54.000000000 +0000
    @@ -27,7 +27,6 @@
     [test_install_event.html]
     [test_navigator.html]
     [test_scopes.html]
    -skip-if = true # bug 1124743
     [test_controller.html]
     [test_workerUpdate.html]
     skip-if = true # Enable after Bug 982726 postMessage is landed.
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/test/serviceworkers/test_installation_simple.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/test/serviceworkers/test_installation_simple.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/test/serviceworkers/test_installation_simple.html	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/test/serviceworkers/test_installation_simple.html	2015-02-03 14:33:54.000000000 +0000
    @@ -106,6 +106,10 @@
         var p = navigator.serviceWorker.register("parse_error_worker.js", { scope: "parse_error/" });
         return p.then(function(wr) {
           ok(false, "Registration should fail with parse error");
    +      return navigator.serviceWorker.getRegistration("parse_error/").then(function(swr) {
    +        // See https://github.com/slightlyoff/ServiceWorker/issues/547
    +        is(swr, undefined, "A failed registration for a scope with no prior controllers should clear itself");
    +      });
         }, function(e) {
         info("NSM " + e.name);
           ok(e instanceof Error, "Registration should fail with parse error");
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/test/serviceworkers/test_unregister.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/test/serviceworkers/test_unregister.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/test/serviceworkers/test_unregister.html	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/test/serviceworkers/test_unregister.html	2015-02-03 14:33:54.000000000 +0000
    @@ -86,25 +86,9 @@
     
       function runTest() {
         simpleRegister()
    -      .then(function(v) {
    -        info("simpleRegister() promise resolved");
    -      })
           .then(testControlled)
    -      .then(function(v) {
    -        info("testControlled() promise resolved");
    -      }, function(e) {
    -        info("testControlled() promise rejected " + e);
    -      })
           .then(unregister)
    -      .then(function(v) {
    -        info("unregister() promise resolved");
    -      })
           .then(testUncontrolled)
    -      .then(function(v) {
    -        info("testUncontrolled() promise resolved");
    -      }, function(e) {
    -        info("testUncontrolled() promise rejected " + e);
    -      })
           .then(function() {
             SimpleTest.finish();
           }).catch(function(e) {
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/test/test_consoleAndBlobs.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/test/test_consoleAndBlobs.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/test/test_consoleAndBlobs.html	1970-01-01 00:00:00.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/test/test_consoleAndBlobs.html	2015-02-03 14:33:54.000000000 +0000
    @@ -0,0 +1,41 @@
    +
    +
    +
    +  
    +    Test for console API and blobs
    +    
    +    
    +  
    +  
    +    
    +  
    +
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/test/worker_consoleAndBlobs.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/test/worker_consoleAndBlobs.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/workers/test/worker_consoleAndBlobs.js	1970-01-01 00:00:00.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/workers/test/worker_consoleAndBlobs.js	2015-02-03 14:33:54.000000000 +0000
    @@ -0,0 +1,8 @@
    +/**
    + * Any copyright is dedicated to the Public Domain.
    + * http://creativecommons.org/publicdomain/zero/1.0/
    + */
    +"use strict";
    +
    +var b = new Blob(['123'], { type: 'foo/bar'});
    +console.log(b);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsBindingManager.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsBindingManager.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsBindingManager.cpp	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsBindingManager.cpp	2015-02-03 14:33:54.000000000 +0000
    @@ -344,8 +344,7 @@
     nsresult
     nsBindingManager::AddToAttachedQueue(nsXBLBinding* aBinding)
     {
    -  if (!mAttachedStack.AppendElement(aBinding))
    -    return NS_ERROR_OUT_OF_MEMORY;
    +  mAttachedStack.AppendElement(aBinding);
     
       // If we're in the middle of processing our queue already, don't
       // bother posting the event.
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsXBLContentSink.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsXBLContentSink.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsXBLContentSink.cpp	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsXBLContentSink.cpp	2015-02-03 14:33:54.000000000 +0000
    @@ -39,7 +39,6 @@
       NS_ENSURE_ARG_POINTER(aResult);
     
       nsXBLContentSink* it = new nsXBLContentSink();
    -  NS_ENSURE_TRUE(it, NS_ERROR_OUT_OF_MEMORY);
     
       nsCOMPtr kungFuDeathGrip = it;
       nsresult rv = it->Init(aDoc, aURI, aContainer);
    @@ -461,11 +460,9 @@
         }
         nsXBLProtoImplAnonymousMethod* newMethod =
           new nsXBLProtoImplAnonymousMethod(name.get());
    -    if (newMethod) {
    -      newMethod->SetLineNumber(aLineNumber);
    -      mBinding->SetConstructor(newMethod);
    -      AddMember(newMethod);
    -    }
    +    newMethod->SetLineNumber(aLineNumber);
    +    mBinding->SetConstructor(newMethod);
    +    AddMember(newMethod);
       }
       else if (aTagName == nsGkAtoms::destructor) {
         ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
    @@ -481,11 +478,9 @@
         }
         nsXBLProtoImplAnonymousMethod* newMethod =
           new nsXBLProtoImplAnonymousMethod(name.get());
    -    if (newMethod) {
    -      newMethod->SetLineNumber(aLineNumber);
    -      mBinding->SetDestructor(newMethod);
    -      AddMember(newMethod);
    -    }
    +    newMethod->SetLineNumber(aLineNumber);
    +    mBinding->SetDestructor(newMethod);
    +    AddMember(newMethod);
       }
       else if (aTagName == nsGkAtoms::field) {
         ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
    @@ -551,9 +546,7 @@
       // performs this check.
       if (!cid.IsEmpty()) {
         mBinding = new nsXBLPrototypeBinding();
    -    if (!mBinding)
    -      return NS_ERROR_OUT_OF_MEMORY;
    -      
    +
         rv = mBinding->Init(cid, mDocInfo, binding, !mFoundFirstBinding);
         if (NS_SUCCEEDED(rv) &&
             NS_SUCCEEDED(mDocInfo->SetPrototypeBinding(cid, mBinding))) {
    @@ -675,22 +668,17 @@
                                              clickcount, group, preventdefault,
                                              allowuntrusted, mBinding, aLineNumber);
     
    -  if (newHandler) {
    -    // Add this handler to our chain of handlers.
    -    if (mHandler) {
    -      // Already have a chain. Just append to the end.
    -      mHandler->SetNextHandler(newHandler);
    -    }
    -    else {
    -      // We're the first handler in the chain.
    -      mBinding->SetPrototypeHandlers(newHandler);
    -    }
    -    // Adjust our mHandler pointer to point to the new last handler in the
    -    // chain.
    -    mHandler = newHandler;
    +  // Add this handler to our chain of handlers.
    +  if (mHandler) {
    +    // Already have a chain. Just append to the end.
    +    mHandler->SetNextHandler(newHandler);
       } else {
    -    mState = eXBL_Error;
    +    // We're the first handler in the chain.
    +    mBinding->SetPrototypeHandlers(newHandler);
       }
    +  // Adjust our mHandler pointer to point to the new last handler in the
    +  // chain.
    +  mHandler = newHandler;
     }
     
     void
    @@ -773,10 +761,8 @@
         // All of our pointers are now filled in. Construct our field with all of
         // these parameters.
         mField = new nsXBLProtoImplField(name, readonly);
    -    if (mField) {
    -      mField->SetLineNumber(aLineNumber);
    -      AddField(mField);
    -    }
    +    mField->SetLineNumber(aLineNumber);
    +    AddField(mField);
       }
     }
     
    @@ -882,8 +868,6 @@
     
       *aAppendContent = true;
       nsRefPtr prototype = new nsXULPrototypeElement();
    -  if (!prototype)
    -    return NS_ERROR_OUT_OF_MEMORY;
     
       prototype->mNodeInfo = aNodeInfo;
     
    @@ -919,8 +903,6 @@
       nsXULPrototypeAttribute* attrs = nullptr;
       if (aAttsCount > 0) {
         attrs = new nsXULPrototypeAttribute[aAttsCount];
    -    if (!attrs)
    -      return NS_ERROR_OUT_OF_MEMORY;
       }
     
       aElement->mAttributes    = attrs;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsXBLEventHandler.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsXBLEventHandler.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsXBLEventHandler.cpp	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsXBLEventHandler.cpp	2015-02-03 14:33:54.000000000 +0000
    @@ -160,42 +160,24 @@
     
     ///////////////////////////////////////////////////////////////////////////////////
     
    -nsresult
    +already_AddRefed
     NS_NewXBLEventHandler(nsXBLPrototypeHandler* aHandler,
    -                      nsIAtom* aEventType,
    -                      nsXBLEventHandler** aResult)
    +                      nsIAtom* aEventType)
     {
    +  nsRefPtr handler;
    +
       switch (nsContentUtils::GetEventClassID(nsDependentAtomString(aEventType))) {
         case eDragEventClass:
         case eMouseEventClass:
         case eMouseScrollEventClass:
         case eWheelEventClass:
         case eSimpleGestureEventClass:
    -      *aResult = new nsXBLMouseEventHandler(aHandler);
    +      handler = new nsXBLMouseEventHandler(aHandler);
           break;
         default:
    -      *aResult = new nsXBLEventHandler(aHandler);
    +      handler = new nsXBLEventHandler(aHandler);
           break;
       }
     
    -  if (!*aResult)
    -    return NS_ERROR_OUT_OF_MEMORY;
    -
    -  NS_ADDREF(*aResult);
    -
    -  return NS_OK;
    -}
    -
    -nsresult
    -NS_NewXBLKeyEventHandler(nsIAtom* aEventType, uint8_t aPhase, uint8_t aType,
    -                         nsXBLKeyEventHandler** aResult)
    -{
    -  *aResult = new nsXBLKeyEventHandler(aEventType, aPhase, aType);
    -
    -  if (!*aResult)
    -    return NS_ERROR_OUT_OF_MEMORY;
    -
    -  NS_ADDREF(*aResult);
    -
    -  return NS_OK;
    +  return handler.forget();
     }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsXBLEventHandler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsXBLEventHandler.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsXBLEventHandler.h	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsXBLEventHandler.h	2015-02-03 14:33:54.000000000 +0000
    @@ -113,13 +113,8 @@
       bool mUsingContentXBLScope;
     };
     
    -nsresult
    +already_AddRefed
     NS_NewXBLEventHandler(nsXBLPrototypeHandler* aHandler,
    -                      nsIAtom* aEventType,
    -                      nsXBLEventHandler** aResult);
    -
    -nsresult
    -NS_NewXBLKeyEventHandler(nsIAtom* aEventType, uint8_t aPhase,
    -                         uint8_t aType, nsXBLKeyEventHandler** aResult);
    +                      nsIAtom* aEventType);
     
     #endif
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsXBLProtoImpl.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsXBLProtoImpl.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsXBLProtoImpl.cpp	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsXBLProtoImpl.cpp	2015-02-03 14:33:54.000000000 +0000
    @@ -520,8 +520,6 @@
                        nsXBLProtoImpl** aResult)
     {
       nsXBLProtoImpl* impl = new nsXBLProtoImpl();
    -  if (!impl)
    -    return NS_ERROR_OUT_OF_MEMORY;
       if (aClassName)
         impl->mClassName.AssignWithConversion(aClassName);
       else
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsXBLPrototypeBinding.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsXBLPrototypeBinding.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsXBLPrototypeBinding.cpp	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsXBLPrototypeBinding.cpp	2015-02-03 14:33:54.000000000 +0000
    @@ -807,11 +807,9 @@
           }
     
           if (i == count) {
    -        nsRefPtr newHandler;
    -        NS_NewXBLKeyEventHandler(eventAtom, phase, type,
    -                                 getter_AddRefs(newHandler));
    -        if (newHandler)
    -          mKeyHandlers.AppendObject(newHandler);
    +        nsRefPtr newHandler =
    +          new nsXBLKeyEventHandler(eventAtom, phase, type);
    +        mKeyHandlers.AppendObject(newHandler);
             handler = newHandler;
           }
     
    @@ -1264,7 +1262,6 @@
         nsIURI* documentURI = aDocument->GetDocumentURI();
     
         nsRefPtr prototype = new nsXULPrototypeElement();
    -    NS_ENSURE_TRUE(prototype, NS_ERROR_OUT_OF_MEMORY);
     
         prototype->mNodeInfo = nodeInfo;
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsXBLPrototypeHandler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsXBLPrototypeHandler.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsXBLPrototypeHandler.h	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsXBLPrototypeHandler.h	2015-02-03 14:33:54.000000000 +0000
    @@ -132,8 +132,7 @@
       nsXBLEventHandler* GetEventHandler()
       {
         if (!mHandler) {
    -      NS_NewXBLEventHandler(this, mEventName, getter_AddRefs(mHandler));
    -      // XXX Need to signal out of memory?
    +      mHandler = NS_NewXBLEventHandler(this, mEventName);
         }
     
         return mHandler;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsXBLService.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsXBLService.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsXBLService.cpp	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsXBLService.cpp	2015-02-03 14:33:54.000000000 +0000
    @@ -764,12 +764,10 @@
        return NS_ERROR_FAILURE;
       }
     
    -  NS_ENSURE_TRUE(aDontExtendURIs.AppendElement(protoBinding->BindingURI()),
    -                 NS_ERROR_OUT_OF_MEMORY);
    +  aDontExtendURIs.AppendElement(protoBinding->BindingURI());
       nsCOMPtr altBindingURI = protoBinding->AlternateBindingURI();
       if (altBindingURI) {
    -    NS_ENSURE_TRUE(aDontExtendURIs.AppendElement(altBindingURI),
    -                   NS_ERROR_OUT_OF_MEMORY);
    +    aDontExtendURIs.AppendElement(altBindingURI);
       }
     
       // Our prototype binding must have all its resources loaded.
    @@ -831,7 +829,6 @@
       if (!aPeekOnly) {
         // Make a new binding
         nsXBLBinding *newBinding = new nsXBLBinding(protoBinding);
    -    NS_ENSURE_TRUE(newBinding, NS_ERROR_OUT_OF_MEMORY);
     
         if (baseBinding) {
           if (!baseProto) {
    @@ -1100,8 +1097,7 @@
     
       NS_ENSURE_SUCCESS(rv, rv);
     
    -  nsCOMPtr sameOriginChecker = nsContentUtils::GetSameOriginChecker();
    -  NS_ENSURE_TRUE(sameOriginChecker, NS_ERROR_OUT_OF_MEMORY);
    +  nsCOMPtr sameOriginChecker = nsContentUtils::SameOriginChecker();
     
       channel->SetNotificationCallbacks(sameOriginChecker);
     
    @@ -1109,7 +1105,6 @@
         // We can be asynchronous
         nsXBLStreamListener* xblListener =
           new nsXBLStreamListener(aBoundDocument, xblSink, doc);
    -    NS_ENSURE_TRUE(xblListener,NS_ERROR_OUT_OF_MEMORY);
     
         // Add ourselves to the list of loading docs.
         nsBindingManager *bindingManager;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsXBLWindowKeyHandler.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsXBLWindowKeyHandler.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xbl/nsXBLWindowKeyHandler.cpp	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xbl/nsXBLWindowKeyHandler.cpp	2015-02-03 14:33:54.000000000 +0000
    @@ -214,9 +214,6 @@
     
           nsXBLPrototypeHandler* handler = new nsXBLPrototypeHandler(key);
     
    -      if (!handler)
    -        return;
    -
           handler->SetNextHandler(*aResult);
           *aResult = handler;
         }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xml/XMLDocument.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xml/XMLDocument.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xml/XMLDocument.cpp	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xml/XMLDocument.cpp	2015-02-03 14:33:55.000000000 +0000
    @@ -434,11 +434,7 @@
       mListenerManager = elm;
     
       // Create a channel
    -  nsCOMPtr req = nsContentUtils::GetSameOriginChecker();
    -  if (!req) {
    -    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    -    return false;
    -  }
    +  nsCOMPtr req = nsContentUtils::SameOriginChecker();
     
       nsCOMPtr channel;
       // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active,
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xslt/base/txDouble.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xslt/base/txDouble.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xslt/base/txDouble.cpp	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xslt/base/txDouble.cpp	2015-02-03 14:33:55.000000000 +0000
    @@ -179,7 +179,7 @@
             ++length;
         // grow the string
         uint32_t oldlength = aDest.Length();
    -    if (!aDest.SetLength(oldlength + length, mozilla::fallible_t()))
    +    if (!aDest.SetLength(oldlength + length, mozilla::fallible))
             return; // out of memory
         nsAString::iterator dest;
         aDest.BeginWriting(dest).advance(int32_t(oldlength));
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xslt/tests/buster/buster-test.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xslt/tests/buster/buster-test.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xslt/tests/buster/buster-test.js	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xslt/tests/buster/buster-test.js	2015-02-03 14:33:55.000000000 +0000
    @@ -312,7 +312,14 @@
             if (!serv) {
                 throw Components.results.ERR_FAILURE;
             }
    -        var chan = serv.newChannel(url, null, null);
    +        var chan = serv.newChannel2(url,
    +                                    null,
    +                                    null,
    +                                    null,      // aLoadingNode
    +                                    Services.scriptSecurityManager.getSystemPrincipal(),
    +                                    null,      // aTriggeringPrincipal
    +                                    Ci.nsILoadInfo.SEC_NORMAL,
    +                                    Ci.nsIContentPolicy.TYPE_OTHER);
             var instream = doCreate(SIS_CTRID, nsISIS);
             instream.init(chan.open());
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xslt/tests/XSLTMark/XSLTMark-static.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xslt/tests/XSLTMark/XSLTMark-static.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xslt/tests/XSLTMark/XSLTMark-static.js	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xslt/tests/XSLTMark/XSLTMark-static.js	2015-02-03 14:33:55.000000000 +0000
    @@ -22,7 +22,14 @@
         if (!serv) {
             throw Components.results.ERR_FAILURE;
         }
    -    var chan = serv.newChannel(aUriSpec, null, null);
    +    var chan = serv.newChannel2(aUriSpec,
    +                                null,
    +                                null,
    +                                null,      // aLoadingNode
    +                                Services.scriptSecurityManager.getSystemPrincipal(),
    +                                null,      // aTriggeringPrincipal
    +                                Ci.nsILoadInfo.SEC_NORMAL,
    +                                Ci.nsIContentPolicy.TYPE_OTHER);
         var instream = 
             Components.classes[SIS_CTRID].createInstance(nsISIS);
         instream.init(chan.open());
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp	2015-02-03 14:33:56.000000000 +0000
    @@ -514,7 +514,7 @@
             aNode.mNode->IsElement() ||
             aNode.mNode->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT)) {
             nsContentUtils::AppendNodeTextContent(aNode.mNode, true, aResult,
    -                                              mozilla::fallible_t());
    +                                              mozilla::fallible);
     
             return;
         }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xul/templates/nsContentSupportMap.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xul/templates/nsContentSupportMap.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xul/templates/nsContentSupportMap.h	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xul/templates/nsContentSupportMap.h	2015-02-03 14:33:56.000000000 +0000
    @@ -32,23 +32,25 @@
             if (!hdr)
                 return NS_ERROR_OUT_OF_MEMORY;
     
    -        Entry* entry = reinterpret_cast(hdr);
    +        Entry* entry = static_cast(hdr);
             NS_ASSERTION(entry->mMatch == nullptr, "over-writing entry");
             entry->mContent = aElement;
             entry->mMatch   = aMatch;
    -        return NS_OK; }
    +        return NS_OK;
    +    }
     
         bool Get(nsIContent* aElement, nsTemplateMatch** aMatch) {
             if (!mMap.IsInitialized())
                 return false;
     
    -        PLDHashEntryHdr* hdr = PL_DHashTableLookup(&mMap, aElement);
    -        if (PL_DHASH_ENTRY_IS_FREE(hdr))
    +        PLDHashEntryHdr* hdr = PL_DHashTableSearch(&mMap, aElement);
    +        if (!hdr)
                 return false;
     
    -        Entry* entry = reinterpret_cast(hdr);
    +        Entry* entry = static_cast(hdr);
             *aMatch = entry->mMatch;
    -        return true; }
    +        return true;
    +    }
     
         nsresult Remove(nsIContent* aElement);
     
    @@ -60,8 +62,7 @@
         void Init();
         void Finish();
     
    -    struct Entry {
    -        PLDHashEntryHdr  mHdr;
    +    struct Entry : public PLDHashEntryHdr {
             nsIContent*      mContent;
             nsTemplateMatch* mMatch;
         };
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xul/templates/nsTemplateMap.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xul/templates/nsTemplateMap.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xul/templates/nsTemplateMap.h	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xul/templates/nsTemplateMap.h	2015-02-03 14:33:56.000000000 +0000
    @@ -11,8 +11,7 @@
     
     class nsTemplateMap {
     protected:
    -    struct Entry {
    -        PLDHashEntryHdr mHdr;
    +    struct Entry : public PLDHashEntryHdr {
             nsIContent*     mContent;
             nsIContent*     mTemplate;
         };
    @@ -35,11 +34,10 @@
     
         void
         Put(nsIContent* aContent, nsIContent* aTemplate) {
    -        NS_ASSERTION(PL_DHASH_ENTRY_IS_FREE(PL_DHashTableLookup(&mTable, aContent)),
    +        NS_ASSERTION(!PL_DHashTableSearch(&mTable, aContent),
                          "aContent already in map");
     
    -        Entry* entry =
    -            reinterpret_cast(PL_DHashTableAdd(&mTable, aContent));
    +        Entry* entry = static_cast(PL_DHashTableAdd(&mTable, aContent));
     
             if (entry) {
                 entry->mContent = aContent;
    @@ -62,9 +60,9 @@
         void
         GetTemplateFor(nsIContent* aContent, nsIContent** aResult) {
             Entry* entry =
    -            reinterpret_cast(PL_DHashTableLookup(&mTable, aContent));
    +            static_cast(PL_DHashTableSearch(&mTable, aContent));
     
    -        if (PL_DHASH_ENTRY_IS_BUSY(&entry->mHdr))
    +        if (entry)
                 NS_IF_ADDREF(*aResult = entry->mTemplate);
             else
                 *aResult = nullptr;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xul/XULDocument.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xul/XULDocument.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/dom/xul/XULDocument.cpp	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/dom/xul/XULDocument.cpp	2015-02-03 14:33:56.000000000 +0000
    @@ -778,9 +778,9 @@
     
         BroadcasterMapEntry* entry =
             static_cast
    -                   (PL_DHashTableLookup(mBroadcasterMap, &aBroadcaster));
    +                   (PL_DHashTableSearch(mBroadcasterMap, &aBroadcaster));
     
    -    if (PL_DHASH_ENTRY_IS_FREE(entry)) {
    +    if (!entry) {
             entry =
                 static_cast
                            (PL_DHashTableAdd(mBroadcasterMap, &aBroadcaster));
    @@ -844,9 +844,9 @@
     
         BroadcasterMapEntry* entry =
             static_cast
    -                   (PL_DHashTableLookup(mBroadcasterMap, &aBroadcaster));
    +                   (PL_DHashTableSearch(mBroadcasterMap, &aBroadcaster));
     
    -    if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
    +    if (entry) {
             nsCOMPtr attr = do_GetAtom(aAttr);
             for (int32_t i = entry->mListeners.Count() - 1; i >= 0; --i) {
                 BroadcastListener* bl =
    @@ -958,7 +958,7 @@
         if (aAttribute == nsGkAtoms::ref) {
             AddElementToRefMap(aElement);
         }
    -    
    +
         nsresult rv;
     
         // Synchronize broadcast listeners
    @@ -966,9 +966,9 @@
             CanBroadcast(aNameSpaceID, aAttribute)) {
             BroadcasterMapEntry* entry =
                 static_cast
    -                       (PL_DHashTableLookup(mBroadcasterMap, aElement));
    +                       (PL_DHashTableSearch(mBroadcasterMap, aElement));
     
    -        if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
    +        if (entry) {
                 // We've got listeners: push the value.
                 nsAutoString value;
                 bool attrSet = aElement->GetAttr(kNameSpaceID_None, aAttribute, value);
    @@ -4160,8 +4160,8 @@
             return rv;
     
         BroadcasterMapEntry* entry = static_cast
    -        (PL_DHashTableLookup(mBroadcasterMap, aNode->AsElement()));
    -    if (!PL_DHASH_ENTRY_IS_BUSY(entry))
    +        (PL_DHashTableSearch(mBroadcasterMap, aNode->AsElement()));
    +    if (!entry)
             return rv;
     
         // We've got listeners: push the value.
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/editor/libeditor/nsEditor.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/editor/libeditor/nsEditor.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/editor/libeditor/nsEditor.h	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/editor/libeditor/nsEditor.h	2015-02-03 14:33:56.000000000 +0000
    @@ -7,7 +7,6 @@
     #define __editor_h__
     
     #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc.
    -#include "mozilla/TypedEnum.h"          // for MOZ_BEGIN_ENUM_CLASS, etc.
     #include "mozilla/dom/Text.h"
     #include "nsAutoPtr.h"                  // for nsRefPtr
     #include "nsCOMArray.h"                 // for nsCOMArray
    @@ -90,7 +89,7 @@
     
     // This is int32_t instead of int16_t because nsIInlineSpellChecker.idl's
     // spellCheckAfterEditorChange is defined to take it as a long.
    -MOZ_BEGIN_ENUM_CLASS(EditAction, int32_t)
    +enum class EditAction : int32_t {
       ignore = -1,
       none = 0,
       undo,
    @@ -128,7 +127,7 @@
       removeAbsolutePosition = 3016,
       decreaseZIndex      = 3017,
       increaseZIndex      = 3018
    -MOZ_END_ENUM_CLASS(EditAction)
    +};
     
     inline bool operator!(const EditAction& aOp)
     {
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/editor/libeditor/nsHTMLDataTransfer.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/editor/libeditor/nsHTMLDataTransfer.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/editor/libeditor/nsHTMLDataTransfer.cpp	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/editor/libeditor/nsHTMLDataTransfer.cpp	2015-02-03 14:33:56.000000000 +0000
    @@ -710,8 +710,10 @@
             int32_t linkOffset;
             rv = SplitNodeDeep(link, selNode, selOffset, &linkOffset, true, address_of(leftLink));
             NS_ENSURE_SUCCESS(rv, rv);
    -        selNode = GetNodeLocation(leftLink, &selOffset);
    -        selection->Collapse(selNode, selOffset+1);
    +        if (leftLink) {
    +          selNode = GetNodeLocation(leftLink, &selOffset);
    +          selection->Collapse(selNode, selOffset+1);
    +        }
           }
         }
       }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/editor/libeditor/nsTextEditRules.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/editor/libeditor/nsTextEditRules.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/editor/libeditor/nsTextEditRules.cpp	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/editor/libeditor/nsTextEditRules.cpp	2015-02-03 14:33:56.000000000 +0000
    @@ -1198,7 +1198,7 @@
       if (!aSelection || !aInString || !aOutString) {return NS_ERROR_NULL_POINTER;}
       
       nsresult res = NS_OK;
    -  if (!aOutString->Assign(*aInString, mozilla::fallible_t())) {
    +  if (!aOutString->Assign(*aInString, mozilla::fallible)) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
       if (aTruncated) {
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/editor/libeditor/tests/test_bug1026397.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/editor/libeditor/tests/test_bug1026397.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/editor/libeditor/tests/test_bug1026397.html	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/editor/libeditor/tests/test_bug1026397.html	2015-02-03 14:33:56.000000000 +0000
    @@ -46,13 +46,12 @@
           aAdditionalExplanation = "";
         }
     
    -    synthesizeComposition({ type: "compositionstart" });
         synthesizeCompositionChange(
           { "composition":
             { "string": aInsertString,
               "clauses":
               [
    -            { "length": aInsertString.length, "attr": COMPOSITION_ATTR_RAWINPUT }
    +            { "length": aInsertString.length, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
               ]
             },
             "caret": { "start": aInsertString.length, "length": 0 }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/editor/libeditor/tests/test_bug1109465.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/editor/libeditor/tests/test_bug1109465.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/editor/libeditor/tests/test_bug1109465.html	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/editor/libeditor/tests/test_bug1109465.html	2015-02-03 14:33:56.000000000 +0000
    @@ -47,7 +47,7 @@
           { "string": composingString,
             "clauses":
             [
    -          { "length": 1, "attr": COMPOSITION_ATTR_RAWINPUT }
    +          { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
             ]
           },
           "caret": { "start": 1, "length": 0 }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/editor/libeditor/tests/test_bug697842.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/editor/libeditor/tests/test_bug697842.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/editor/libeditor/tests/test_bug697842.html	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/editor/libeditor/tests/test_bug697842.html	2015-02-03 14:33:57.000000000 +0000
    @@ -36,8 +36,17 @@
         var composingString = "";
     
         function handler(aEvent) {
    -      if (aEvent.type != "text") {
    -        is(aEvent.data, composingString, "mismatch composition string");
    +      switch (aEvent.type) {
    +        case "compositionstart":
    +          // Selected string at starting composition must be empty in this test.
    +          is(aEvent.data, "", "mismatch selected string");
    +          break;
    +        case "compositionupdate":
    +        case "compositionend":
    +          is(aEvent.data, composingString, "mismatch composition string");
    +          break;
    +        default:
    +          break;
           }
           aEvent.stopPropagation();
           aEvent.preventDefault();
    @@ -48,9 +57,6 @@
         editor.addEventListener("compositionupdate", handler, true);
         editor.addEventListener("text", handler, true);
     
    -    // start composition
    -    synthesizeComposition({ type: "compositionstart" });
    -
         // input first character
         composingString = "\u306B";
         synthesizeCompositionChange(
    @@ -58,7 +64,7 @@
             { "string": composingString,
               "clauses":
               [
    -            { "length": 1, "attr": COMPOSITION_ATTR_RAWINPUT }
    +            { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
               ]
             },
             "caret": { "start": 1, "length": 0 }
    @@ -71,7 +77,7 @@
             { "string": composingString,
               "clauses":
               [
    -            { "length": 2, "attr": COMPOSITION_ATTR_RAWINPUT }
    +            { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
               ]
             },
             "caret": { "start": 2, "length": 0 }
    @@ -84,7 +90,7 @@
               "clauses":
               [
                 { "length": 2,
    -              "attr": COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT }
    +              "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
               ]
             },
             "caret": { "start": 2, "length": 0 }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/editor/libeditor/tests/test_bug795785.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/editor/libeditor/tests/test_bug795785.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/editor/libeditor/tests/test_bug795785.html	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/editor/libeditor/tests/test_bug795785.html	2015-02-03 14:33:57.000000000 +0000
    @@ -114,7 +114,6 @@
       hitEventLoop(function () {
         is(aElement.scrollTop, 0,
            aElementDescription + "'s scrollTop isn't 0");
    -    synthesizeComposition({ type: "compositionstart" });
         var str = "Web \u958b\u767a\u8005\u306e\u7686\u3055\u3093\u306f\u3001" +
                   "Firefox \u306b\u5b9f\u88c5\u3055\u308c\u3066\u3044\u308b HTML5" +
                   " \u3084 CSS \u306e\u65b0\u6a5f\u80fd\u3092\u6d3b\u7528\u3059" +
    @@ -127,7 +126,7 @@
             composition: {
               string: str,
               clauses: [
    -            { length: str.length, attr: COMPOSITION_ATTR_RAWINPUT }
    +            { length: str.length, attr: COMPOSITION_ATTR_RAW_CLAUSE }
               ]
             },
             caret: { start: str.length, length: 0 }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/editor/libeditor/tests/test_contenteditable_text_input_handling.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/editor/libeditor/tests/test_contenteditable_text_input_handling.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/editor/libeditor/tests/test_contenteditable_text_input_handling.html	2015-01-25 22:24:41.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/editor/libeditor/tests/test_contenteditable_text_input_handling.html	2015-02-03 14:33:57.000000000 +0000
    @@ -219,15 +219,13 @@
         }
     
         // IME
    -    // start composition
    -    synthesizeComposition({ type: "compositionstart" });
         // input first character
         synthesizeCompositionChange(
           { "composition":
             { "string": "\u3089",
               "clauses":
               [
    -            { "length": 1, "attr": COMPOSITION_ATTR_RAWINPUT }
    +            { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
               ]
             },
             "caret": { "start": 1, "length": 0 }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/embedding/components/commandhandler/nsCommandParams.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/embedding/components/commandhandler/nsCommandParams.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/embedding/components/commandhandler/nsCommandParams.cpp	2015-01-25 22:24:42.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/embedding/components/commandhandler/nsCommandParams.cpp	2015-02-03 14:33:58.000000000 +0000
    @@ -208,7 +208,7 @@
     NS_IMETHODIMP
     nsCommandParams::RemoveValue(const char* aName)
     {
    -  // PL_DHASH_REMOVE doesn't tell us if the entry was really removed, so we
    +  // PL_DHashTableRemove doesn't tell us if the entry was really removed, so we
       // return NS_OK unconditionally.
       (void)PL_DHashTableRemove(&mValuesHash, (void *)aName);
       return NS_OK;
    @@ -217,20 +217,15 @@
     nsCommandParams::HashEntry*
     nsCommandParams::GetNamedEntry(const char* aName)
     {
    -  HashEntry *foundEntry =
    -    (HashEntry *)PL_DHashTableLookup(&mValuesHash, (void *)aName);
    -  if (PL_DHASH_ENTRY_IS_BUSY(foundEntry)) {
    -    return foundEntry;
    -  }
    -  return nullptr;
    +  return (HashEntry *)PL_DHashTableSearch(&mValuesHash, (void *)aName);
     }
     
     nsCommandParams::HashEntry*
     nsCommandParams::GetOrMakeEntry(const char* aName, uint8_t entryType)
     {
       HashEntry *foundEntry =
    -    (HashEntry *)PL_DHashTableLookup(&mValuesHash, (void *)aName);
    -  if (PL_DHASH_ENTRY_IS_BUSY(foundEntry)) { // reuse existing entry
    +    (HashEntry *)PL_DHashTableSearch(&mValuesHash, (void *)aName);
    +  if (foundEntry) { // reuse existing entry
         foundEntry->Reset(entryType);
         return foundEntry;
       }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/extensions/inspector/Makefile.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/extensions/inspector/Makefile.in
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/extensions/inspector/Makefile.in	2015-01-25 22:24:56.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/extensions/inspector/Makefile.in	2015-02-03 14:34:37.000000000 +0000
    @@ -18,8 +18,6 @@
     INSTALL_EXTENSION_ID   = inspector@mozilla.org
     XPI_PKGNAME            = inspector-$(DOMi_VERSION)
     
    -PREF_JS_EXPORTS = $(srcdir)/resources/content/prefs/inspector.js
    -
     DIST_FILES = install.rdf
     
     XULAPP_DEFINES = \
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/extensions/inspector/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/extensions/inspector/moz.build
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/extensions/inspector/moz.build	2015-01-25 22:24:56.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/extensions/inspector/moz.build	2015-02-03 14:34:37.000000000 +0000
    @@ -12,3 +12,7 @@
     export('XPI_NAME')
     
     JAR_MANIFESTS += ['jar.mn']
    +
    +JS_PREFERENCE_FILES += [
    +    'resources/content/prefs/inspector.js',
    +]
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/extensions/irc/xul/content/static.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/extensions/irc/xul/content/static.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/extensions/irc/xul/content/static.js	2015-01-25 22:24:51.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/extensions/irc/xul/content/static.js	2015-02-03 14:34:24.000000000 +0000
    @@ -3089,7 +3089,7 @@
         // to prevent munging the URLs resulting in broken links. Leave codes at
         // the start of the URL alone.
         msg = msg.replace(new RegExp(client.linkRE.source, "g"), function(url, _foo, scheme) {
    -        if (!client.checkURLScheme(scheme))
    +        if (scheme && !client.checkURLScheme(scheme))
                 return url;
             return url.replace(/%[BC][0-9A-Fa-f]/g, function(hex, index) {
                 // as JS does not support lookbehind and we don't want to consume the
    @@ -4326,6 +4326,12 @@
                 client._loader = cls.getService(mozIJSSubScriptLoader);
         }
     
    +    if (client._loader.loadSubScriptWithOptions)
    +    {
    +        var opts = {target: scope, ignoreCache: true};
    +        return client._loader.loadSubScriptWithOptions(url, opts);
    +    }
    +
         return client._loader.loadSubScript(url, scope);
     }
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp	2015-01-25 22:24:42.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp	2015-02-03 14:33:58.000000000 +0000
    @@ -576,7 +576,7 @@
               DOMTextMapping(NodeOffset(node, firstOffsetInNode), mSoftText.Length(), len));
     
             bool ok = textFragment->AppendTo(mSoftText, firstOffsetInNode, len,
    -                                         mozilla::fallible_t());
    +                                         mozilla::fallible);
             if (!ok) {
                 // probably out of memory, remove from mSoftTextDOMMapping
                 mSoftTextDOMMapping.RemoveElementAt(mSoftTextDOMMapping.Length() - 1);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/2D.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/2D.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/2D.h	2015-01-25 22:24:42.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/2D.h	2015-02-03 14:33:58.000000000 +0000
    @@ -162,6 +162,7 @@
       virtual ~GradientStops() {}
     
       virtual BackendType GetBackendType() const = 0;
    +  virtual bool IsValid() const { return true; }
     
     protected:
       GradientStops() {}
    @@ -187,6 +188,8 @@
     class ColorPattern : public Pattern
     {
     public:
    +  // Explicit because consumers should generally use ToDeviceColor when
    +  // creating a ColorPattern.
       explicit ColorPattern(const Color &aColor)
         : mColor(aColor)
       {}
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/Blur.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/Blur.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/Blur.cpp	2015-01-25 22:24:42.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/Blur.cpp	2015-02-03 14:33:58.000000000 +0000
    @@ -17,6 +17,10 @@
     #include "DataSurfaceHelpers.h"
     #include "Tools.h"
     
    +#ifdef BUILD_ARM_NEON
    +#include "mozilla/arm.h"
    +#endif
    +
     using namespace std;
     
     namespace mozilla {
    @@ -544,6 +548,7 @@
           if (!integralImage) {
             return;
           }
    +
     #ifdef USE_SSE2
           if (Factory::HasSSE2()) {
             BoxBlur_SSE2(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
    @@ -554,6 +559,16 @@
                          verticalLobes[2][1], integralImage, integralImageStride);
           } else
     #endif
    +#ifdef BUILD_ARM_NEON
    +      if (mozilla::supports_neon()) {
    +        BoxBlur_NEON(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
    +                     verticalLobes[0][1], integralImage, integralImageStride);
    +        BoxBlur_NEON(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
    +                     verticalLobes[1][1], integralImage, integralImageStride);
    +        BoxBlur_NEON(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
    +                     verticalLobes[2][1], integralImage, integralImageStride);
    +      } else
    +#endif
           {
             BoxBlur_C(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
                       verticalLobes[0][1], integralImage, integralImageStride);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/Blur.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/Blur.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/Blur.h	2015-01-25 22:24:42.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/Blur.h	2015-02-03 14:33:58.000000000 +0000
    @@ -124,6 +124,11 @@
       void BoxBlur_SSE2(uint8_t* aData,
                         int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
                         int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride);
    +#ifdef BUILD_ARM_NEON
    +  void BoxBlur_NEON(uint8_t* aData,
    +                    int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
    +                    int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride);
    +#endif
     
       static CheckedInt RoundUpToMultipleOf4(int32_t aVal);
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/BlurNEON.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/BlurNEON.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/BlurNEON.cpp	1970-01-01 00:00:00.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/BlurNEON.cpp	2015-02-03 14:33:58.000000000 +0000
    @@ -0,0 +1,288 @@
    +/* 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 "Blur.h"
    +#include 
    +
    +namespace mozilla {
    +namespace gfx {
    +
    +MOZ_ALWAYS_INLINE
    +uint16x4_t Divide(uint32x4_t aValues, uint32x2_t aDivisor)
    +{
    +  uint64x2_t roundingAddition = vdupq_n_u64(int64_t(1) << 31);
    +  uint64x2_t multiplied21 = vmull_u32(vget_low_u32(aValues), aDivisor);
    +  uint64x2_t multiplied43 = vmull_u32(vget_high_u32(aValues), aDivisor);
    +  return vqmovn_u32(vcombine_u32(vshrn_n_u64(vaddq_u64(multiplied21, roundingAddition), 32),
    +                                 vshrn_n_u64(vaddq_u64(multiplied43, roundingAddition), 32)));
    +}
    +
    +MOZ_ALWAYS_INLINE
    +uint16x4_t BlurFourPixels(const uint32x4_t& aTopLeft, const uint32x4_t& aTopRight,
    +                          const uint32x4_t& aBottomRight, const uint32x4_t& aBottomLeft,
    +                          const uint32x2_t& aDivisor)
    +{
    +  uint32x4_t values = vaddq_u32(vsubq_u32(vsubq_u32(aBottomRight, aTopRight), aBottomLeft), aTopLeft);
    +  return Divide(values, aDivisor);
    +}
    +
    +MOZ_ALWAYS_INLINE
    +void LoadIntegralRowFromRow(uint32_t *aDest, const uint8_t *aSource,
    +                            int32_t aSourceWidth, int32_t aLeftInflation,
    +                            int32_t aRightInflation)
    +{
    +  int32_t currentRowSum = 0;
    +
    +  for (int x = 0; x < aLeftInflation; x++) {
    +    currentRowSum += aSource[0];
    +    aDest[x] = currentRowSum;
    +  }
    +  for (int x = aLeftInflation; x < (aSourceWidth + aLeftInflation); x++) {
    +    currentRowSum += aSource[(x - aLeftInflation)];
    +    aDest[x] = currentRowSum;
    +  }
    +  for (int x = (aSourceWidth + aLeftInflation); x < (aSourceWidth + aLeftInflation + aRightInflation); x++) {
    +    currentRowSum += aSource[aSourceWidth - 1];
    +    aDest[x] = currentRowSum;
    +  }
    +}
    +
    +MOZ_ALWAYS_INLINE void
    +GenerateIntegralImage_NEON(int32_t aLeftInflation, int32_t aRightInflation,
    +                           int32_t aTopInflation, int32_t aBottomInflation,
    +                           uint32_t *aIntegralImage, size_t aIntegralImageStride,
    +                           uint8_t *aSource, int32_t aSourceStride, const IntSize &aSize)
    +{
    +  MOZ_ASSERT(!(aLeftInflation & 3));
    +
    +  uint32_t stride32bit = aIntegralImageStride / 4;
    +  IntSize integralImageSize(aSize.width + aLeftInflation + aRightInflation,
    +                            aSize.height + aTopInflation + aBottomInflation);
    +
    +  LoadIntegralRowFromRow(aIntegralImage, aSource, aSize.width, aLeftInflation, aRightInflation);
    +
    +  for (int y = 1; y < aTopInflation + 1; y++) {
    +    uint32_t *intRow = aIntegralImage + (y * stride32bit);
    +    uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
    +    uint32_t *intFirstRow = aIntegralImage;
    +
    +    for (int x = 0; x < integralImageSize.width; x += 4) {
    +      uint32x4_t firstRow = vld1q_u32(intFirstRow + x);
    +      uint32x4_t previousRow = vld1q_u32(intPrevRow + x);
    +      vst1q_u32(intRow + x, vaddq_u32(firstRow, previousRow));
    +    }
    +  }
    +
    +  for (int y = aTopInflation + 1; y < (aSize.height + aTopInflation); y++) {
    +    uint32x4_t currentRowSum = vdupq_n_u32(0);
    +    uint32_t *intRow = aIntegralImage + (y * stride32bit);
    +    uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
    +    uint8_t *sourceRow = aSource + aSourceStride * (y - aTopInflation);
    +
    +    uint32_t pixel = sourceRow[0];
    +    for (int x = 0; x < aLeftInflation; x += 4) {
    +      uint32_t temp[4];
    +      temp[0] = pixel;
    +      temp[1] = temp[0] + pixel;
    +      temp[2] = temp[1] + pixel;
    +      temp[3] = temp[2] + pixel;
    +      uint32x4_t sumPixels = vld1q_u32(temp);
    +      sumPixels = vaddq_u32(sumPixels, currentRowSum);
    +      currentRowSum = vdupq_n_u32(vgetq_lane_u32(sumPixels, 3));
    +      vst1q_u32(intRow + x, vaddq_u32(sumPixels, vld1q_u32(intPrevRow + x)));
    +    }
    +
    +    for (int x = aLeftInflation; x < (aSize.width + aLeftInflation); x += 4) {
    +      // It's important to shuffle here. When we exit this loop currentRowSum
    +      // has to be set to sumPixels, so that the following loop can get the
    +      // correct pixel for the currentRowSum. The highest order pixel in
    +      // currentRowSum could've originated from accumulation in the stride.
    +      currentRowSum = vdupq_n_u32(vgetq_lane_u32(currentRowSum, 3));
    +
    +      uint32_t temp[4];
    +      temp[0] = *(sourceRow + (x - aLeftInflation));
    +      temp[1] = temp[0] + *(sourceRow + (x - aLeftInflation) + 1);
    +      temp[2] = temp[1] + *(sourceRow + (x - aLeftInflation) + 2);
    +      temp[3] = temp[2] + *(sourceRow + (x - aLeftInflation) + 3);
    +      uint32x4_t sumPixels = vld1q_u32(temp);
    +      sumPixels = vaddq_u32(sumPixels, currentRowSum);
    +      currentRowSum = sumPixels;
    +      vst1q_u32(intRow + x, vaddq_u32(sumPixels, vld1q_u32(intPrevRow + x)));
    +    }
    +
    +    pixel = sourceRow[aSize.width - 1];
    +    int x = (aSize.width + aLeftInflation);
    +    if ((aSize.width & 3)) {
    +      // Deal with unaligned portion. Get the correct pixel from currentRowSum,
    +      // see explanation above.
    +      uint32_t intCurrentRowSum = ((uint32_t*)¤tRowSum)[(aSize.width % 4) - 1];
    +      for (; x < integralImageSize.width; x++) {
    +        // We could be unaligned here!
    +        if (!(x & 3)) {
    +          // aligned!
    +          currentRowSum = vdupq_n_u32(intCurrentRowSum);
    +          break;
    +        }
    +        intCurrentRowSum += pixel;
    +        intRow[x] = intPrevRow[x] + intCurrentRowSum;
    +      }
    +    } else {
    +      currentRowSum = vdupq_n_u32(vgetq_lane_u32(currentRowSum, 3));
    +    }
    +
    +    for (; x < integralImageSize.width; x += 4) {
    +      uint32_t temp[4];
    +      temp[0] = pixel;
    +      temp[1] = temp[0] + pixel;
    +      temp[2] = temp[1] + pixel;
    +      temp[3] = temp[2] + pixel;
    +      uint32x4_t sumPixels = vld1q_u32(temp);
    +      sumPixels = vaddq_u32(sumPixels, currentRowSum);
    +      currentRowSum = vdupq_n_u32(vgetq_lane_u32(sumPixels, 3));
    +      vst1q_u32(intRow + x, vaddq_u32(sumPixels, vld1q_u32(intPrevRow + x)));
    +    }
    +  }
    +
    +  if (aBottomInflation) {
    +    // Store the last valid row of our source image in the last row of
    +    // our integral image. This will be overwritten with the correct values
    +    // in the upcoming loop.
    +    LoadIntegralRowFromRow(aIntegralImage + (integralImageSize.height - 1) * stride32bit,
    +                           aSource + (aSize.height - 1) * aSourceStride, aSize.width, aLeftInflation, aRightInflation);
    +
    +    for (int y = aSize.height + aTopInflation; y < integralImageSize.height; y++) {
    +      uint32_t *intRow = aIntegralImage + (y * stride32bit);
    +      uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
    +      uint32_t *intLastRow = aIntegralImage + (integralImageSize.height - 1) * stride32bit;
    +      for (int x = 0; x < integralImageSize.width; x += 4) {
    +        vst1q_u32(intRow + x,
    +                        vaddq_u32(vld1q_u32(intLastRow + x),
    +                                  vld1q_u32(intPrevRow + x)));
    +      }
    +    }
    +  }
    +}
    +
    +/**
    + * Attempt to do an in-place box blur using an integral image.
    + */
    +void
    +AlphaBoxBlur::BoxBlur_NEON(uint8_t* aData,
    +                           int32_t aLeftLobe,
    +                           int32_t aRightLobe,
    +                           int32_t aTopLobe,
    +                           int32_t aBottomLobe,
    +                           uint32_t *aIntegralImage,
    +                           size_t aIntegralImageStride)
    +{
    +  IntSize size = GetSize();
    +
    +  MOZ_ASSERT(size.height > 0);
    +
    +  // Our 'left' or 'top' lobe will include the current pixel. i.e. when
    +  // looking at an integral image the value of a pixel at 'x,y' is calculated
    +  // using the value of the integral image values above/below that.
    +  aLeftLobe++;
    +  aTopLobe++;
    +  int32_t boxSize = (aLeftLobe + aRightLobe) * (aTopLobe + aBottomLobe);
    +
    +  MOZ_ASSERT(boxSize > 0);
    +
    +  if (boxSize == 1) {
    +      return;
    +  }
    +
    +  uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
    +  uint32_t stride32bit = aIntegralImageStride / 4;
    +  int32_t leftInflation = RoundUpToMultipleOf4(aLeftLobe).value();
    +
    +  GenerateIntegralImage_NEON(leftInflation, aRightLobe, aTopLobe, aBottomLobe,
    +                             aIntegralImage, aIntegralImageStride, aData,
    +                             mStride, size);
    +
    +  uint32x2_t divisor = vdup_n_u32(reciprocal);
    +
    +  // This points to the start of the rectangle within the IntegralImage that overlaps
    +  // the surface being blurred.
    +  uint32_t *innerIntegral = aIntegralImage + (aTopLobe * stride32bit) + leftInflation;
    +  IntRect skipRect = mSkipRect;
    +  int32_t stride = mStride;
    +  uint8_t *data = aData;
    +
    +  for (int32_t y = 0; y < size.height; y++) {
    +    bool inSkipRectY = y > skipRect.y && y < skipRect.YMost();
    +    uint32_t *topLeftBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
    +    uint32_t *topRightBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) + aRightLobe);
    +    uint32_t *bottomRightBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) + aRightLobe);
    +    uint32_t *bottomLeftBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
    +
    +    int32_t x = 0;
    +    // Process 16 pixels at a time for as long as possible.
    +    for (; x <= size.width - 16; x += 16) {
    +      if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
    +        x = skipRect.XMost() - 16;
    +        // Trigger early jump on coming loop iterations, this will be reset
    +        // next line anyway.
    +        inSkipRectY = false;
    +        continue;
    +      }
    +
    +      uint32x4_t topLeft;
    +      uint32x4_t topRight;
    +      uint32x4_t bottomRight;
    +      uint32x4_t bottomLeft;
    +      topLeft = vld1q_u32(topLeftBase + x);
    +      topRight = vld1q_u32(topRightBase + x);
    +      bottomRight = vld1q_u32(bottomRightBase + x);
    +      bottomLeft = vld1q_u32(bottomLeftBase + x);
    +      uint16x4_t result1 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
    +
    +      topLeft = vld1q_u32(topLeftBase + x + 4);
    +      topRight = vld1q_u32(topRightBase + x + 4);
    +      bottomRight = vld1q_u32(bottomRightBase + x + 4);
    +      bottomLeft = vld1q_u32(bottomLeftBase + x + 4);
    +      uint16x4_t result2 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
    +
    +      topLeft = vld1q_u32(topLeftBase + x + 8);
    +      topRight = vld1q_u32(topRightBase + x + 8);
    +      bottomRight = vld1q_u32(bottomRightBase + x + 8);
    +      bottomLeft = vld1q_u32(bottomLeftBase + x + 8);
    +      uint16x4_t result3 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
    +
    +      topLeft = vld1q_u32(topLeftBase + x + 12);
    +      topRight = vld1q_u32(topRightBase + x + 12);
    +      bottomRight = vld1q_u32(bottomRightBase + x + 12);
    +      bottomLeft = vld1q_u32(bottomLeftBase + x + 12);
    +      uint16x4_t result4 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
    +
    +      uint8x8_t combine1 = vqmovn_u16(vcombine_u16(result1, result2));
    +      uint8x8_t combine2 = vqmovn_u16(vcombine_u16(result3, result4));
    +      uint8x16_t final = vcombine_u8(combine1, combine2);
    +      vst1q_u8(data + stride * y + x, final);
    +    }
    +
    +    // Process the remaining pixels 4 bytes at a time.
    +    for (; x < size.width; x += 4) {
    +      if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
    +        x = skipRect.XMost() - 4;
    +        // Trigger early jump on coming loop iterations, this will be reset
    +        // next line anyway.
    +        inSkipRectY = false;
    +        continue;
    +      }
    +
    +      uint32x4_t topLeft = vld1q_u32(topLeftBase + x);
    +      uint32x4_t topRight = vld1q_u32(topRightBase + x);
    +      uint32x4_t bottomRight = vld1q_u32(bottomRightBase + x);
    +      uint32x4_t bottomLeft = vld1q_u32(bottomLeftBase + x);
    +      uint16x4_t result = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
    +      uint32x2_t final = vreinterpret_u32_u8(vmovn_u16(vcombine_u16(result, vdup_n_u16(0))));
    +      *(uint32_t*)(data + stride * y + x) = vget_lane_u32(final, 0);
    +    }
    +  }
    +}
    +
    +}
    +}
    +
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/DrawCommand.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/DrawCommand.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/DrawCommand.h	2015-01-25 22:24:42.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/DrawCommand.h	2015-02-03 14:33:58.000000000 +0000
    @@ -13,7 +13,7 @@
     namespace mozilla {
     namespace gfx {
     
    -MOZ_BEGIN_ENUM_CLASS(CommandType, int8_t)
    +enum class CommandType : int8_t {
       DRAWSURFACE = 0,
       DRAWFILTER,
       DRAWSURFACEWITHSHADOW,
    @@ -32,7 +32,7 @@
       PUSHCLIPRECT,
       POPCLIP,
       SETTRANSFORM
    -MOZ_END_ENUM_CLASS(CommandType)
    +};
     
     class DrawingCommand
     {
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/DrawTargetD2D1.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/DrawTargetD2D1.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/DrawTargetD2D1.cpp	2015-01-25 22:24:42.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/DrawTargetD2D1.cpp	2015-02-03 14:33:58.000000000 +0000
    @@ -725,7 +725,7 @@
         return nullptr;
       }
     
    -  return new GradientStopsD2D(stopCollection);
    +  return new GradientStopsD2D(stopCollection, Factory::GetDirect3D11Device());
     }
     
     TemporaryRef
    @@ -1228,6 +1228,12 @@
                                        D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)),
                                        stops->mStopCollection,
                                        byRef(gradBrush));
    +
    +    if (!gradBrush) {
    +      gfxWarning() << "Couldn't create gradient brush.";
    +      return CreateTransparentBlackBrush();
    +    }
    +
         return gradBrush.forget();
       }
       if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) {
    @@ -1251,6 +1257,11 @@
                                 stops->mStopCollection,
                                 byRef(gradBrush));
     
    +    if (!gradBrush) {
    +      gfxWarning() << "Couldn't create gradient brush.";
    +      return CreateTransparentBlackBrush();
    +    }
    +
         return gradBrush.forget();
       }
       if (aPattern.GetType() == PatternType::SURFACE) {
    @@ -1279,6 +1290,8 @@
                                        Float(pat->mSurface->GetSize().height));
         }
     
    +    MOZ_ASSERT(pat->mSurface->IsValid());
    +
         RefPtr imageBrush;
         RefPtr image = GetImageForSurface(pat->mSurface, mat, pat->mExtendMode, !pat->mSamplingRect.IsEmpty() ? &pat->mSamplingRect : nullptr);
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/DrawTargetD2D.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/DrawTargetD2D.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/DrawTargetD2D.cpp	2015-01-25 22:24:42.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/DrawTargetD2D.cpp	2015-02-03 14:33:58.000000000 +0000
    @@ -1300,7 +1300,7 @@
         return nullptr;
       }
     
    -  return new GradientStopsD2D(stopCollection);
    +  return new GradientStopsD2D(stopCollection, Factory::GetDirect3D11Device());
     }
     
     TemporaryRef
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/GradientStopsD2D.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/GradientStopsD2D.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/GradientStopsD2D.h	2015-01-25 22:24:42.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/GradientStopsD2D.h	2015-02-03 14:33:58.000000000 +0000
    @@ -17,17 +17,21 @@
     {
     public:
       MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsD2D)
    -  GradientStopsD2D(ID2D1GradientStopCollection *aStopCollection)
    +  GradientStopsD2D(ID2D1GradientStopCollection *aStopCollection, ID3D11Device *aDevice)
         : mStopCollection(aStopCollection)
    +    , mDevice(aDevice)
       {}
     
       virtual BackendType GetBackendType() const { return BackendType::DIRECT2D; }
     
    +  virtual bool IsValid() const MOZ_FINAL{ return mDevice == Factory::GetDirect3D11Device(); }
    +
     private:
       friend class DrawTargetD2D;
       friend class DrawTargetD2D1;
     
       mutable RefPtr mStopCollection;
    +  RefPtr mDevice;
     };
     
     }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/Logging.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/Logging.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/Logging.h	2015-01-25 22:24:42.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/Logging.h	2015-02-03 14:33:58.000000000 +0000
    @@ -21,7 +21,6 @@
     #include "Point.h"
     #include "BaseRect.h"
     #include "Matrix.h"
    -#include "mozilla/TypedEnum.h"
     
     #ifdef WIN32
     // This file gets included from nsGlobalWindow.cpp, which doesn't like
    @@ -225,11 +224,11 @@
       NoLog &operator <<(const T &aLogText) { return *this; }
     };
     
    -MOZ_BEGIN_ENUM_CLASS(LogOptions, int)
    +enum class LogOptions : int {
       NoNewline = 0x01,
       AutoPrefix = 0x02,
       AssertOnCall = 0x04
    -MOZ_END_ENUM_CLASS(LogOptions)
    +};
     
     template
     struct Hexa {
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/moz.build
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/moz.build	2015-01-25 22:24:42.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/moz.build	2015-02-03 14:33:59.000000000 +0000
    @@ -136,6 +136,10 @@
             'QuartzSupport.mm',
         ]
     
    +if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['BUILD_ARM_NEON']:
    +    SOURCES += ['BlurNEON.cpp']
    +    SOURCES['BlurNEON.cpp'].flags += ['-mfpu=neon']
    +
     FAIL_ON_WARNINGS = True
     
     MSVC_ENABLE_PGO = True
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/PathHelpers.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/PathHelpers.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/PathHelpers.h	2015-01-25 22:24:42.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/PathHelpers.h	2015-02-03 14:33:58.000000000 +0000
    @@ -8,7 +8,6 @@
     
     #include "2D.h"
     #include "mozilla/Constants.h"
    -#include "mozilla/TypedEnum.h"
     #include "UserData.h"
     
     namespace mozilla {
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/Types.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/Types.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/2d/Types.h	2015-01-25 22:24:42.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/2d/Types.h	2015-02-03 14:33:59.000000000 +0000
    @@ -6,8 +6,6 @@
     #ifndef MOZILLA_GFX_TYPES_H_
     #define MOZILLA_GFX_TYPES_H_
     
    -#include "mozilla/TypedEnum.h"
    -
     #include 
     #include 
     
    @@ -16,7 +14,7 @@
     
     typedef float Float;
     
    -MOZ_BEGIN_ENUM_CLASS(SurfaceType, int8_t)
    +enum class SurfaceType : int8_t {
       DATA, /* Data surface - bitmap in memory */
       D2D1_BITMAP, /* Surface wrapping a ID2D1Bitmap */
       D2D1_DRAWTARGET, /* Surface made from a D2D draw target */
    @@ -29,9 +27,9 @@
       D2D1_1_IMAGE, /* A D2D 1.1 ID2D1Image SourceSurface */
       RECORDING, /* Surface used for recording */
       TILED /* Surface from a tiled DrawTarget */
    -MOZ_END_ENUM_CLASS(SurfaceType)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(SurfaceFormat, int8_t)
    +enum class SurfaceFormat : int8_t {
       B8G8R8A8,
       B8G8R8X8,
       R8G8B8A8,
    @@ -40,7 +38,7 @@
       A8,
       YUV,
       UNKNOWN
    -MOZ_END_ENUM_CLASS(SurfaceFormat)
    +};
     
     inline bool IsOpaque(SurfaceFormat aFormat)
     {
    @@ -55,7 +53,7 @@
       }
     }
     
    -MOZ_BEGIN_ENUM_CLASS(FilterType, int8_t)
    +enum class FilterType : int8_t {
       BLEND = 0,
       TRANSFORM,
       MORPHOLOGY,
    @@ -82,15 +80,15 @@
       CROP,
       PREMULTIPLY,
       UNPREMULTIPLY
    -MOZ_END_ENUM_CLASS(FilterType)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(DrawTargetType, int8_t)
    +enum class DrawTargetType : int8_t {
       SOFTWARE_RASTER = 0,
       HARDWARE_RASTER,
       VECTOR
    -MOZ_END_ENUM_CLASS(DrawTargetType)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(BackendType, int8_t)
    +enum class BackendType : int8_t {
       NONE = 0,
       DIRECT2D,
       COREGRAPHICS,
    @@ -99,49 +97,49 @@
       SKIA,
       RECORDING,
       DIRECT2D1_1
    -MOZ_END_ENUM_CLASS(BackendType)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(FontType, int8_t)
    +enum class FontType : int8_t {
       DWRITE,
       GDI,
       MAC,
       SKIA,
       CAIRO,
       COREGRAPHICS
    -MOZ_END_ENUM_CLASS(FontType)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(NativeSurfaceType, int8_t)
    +enum class NativeSurfaceType : int8_t {
       D3D10_TEXTURE,
       CAIRO_SURFACE,
       CAIRO_CONTEXT,
       CGCONTEXT,
       CGCONTEXT_ACCELERATED,
       OPENGL_TEXTURE
    -MOZ_END_ENUM_CLASS(NativeSurfaceType)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(NativeFontType, int8_t)
    +enum class NativeFontType : int8_t {
       DWRITE_FONT_FACE,
       GDI_FONT_FACE,
       MAC_FONT_FACE,
       SKIA_FONT_FACE,
       CAIRO_FONT_FACE
    -MOZ_END_ENUM_CLASS(NativeFontType)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(FontStyle, int8_t)
    +enum class FontStyle : int8_t {
       NORMAL,
       ITALIC,
       BOLD,
       BOLD_ITALIC
    -MOZ_END_ENUM_CLASS(FontStyle)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(FontHinting, int8_t)
    +enum class FontHinting : int8_t {
       NONE,
       LIGHT,
       NORMAL,
       FULL
    -MOZ_END_ENUM_CLASS(FontHinting)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(CompositionOp, int8_t)
    +enum class CompositionOp : int8_t {
       OP_OVER,
       OP_ADD,
       OP_ATOP,
    @@ -169,58 +167,58 @@
       OP_COLOR,
       OP_LUMINOSITY,
       OP_COUNT
    -MOZ_END_ENUM_CLASS(CompositionOp)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(ExtendMode, int8_t)
    +enum class ExtendMode : int8_t {
       CLAMP,
       REPEAT,
       REFLECT
    -MOZ_END_ENUM_CLASS(ExtendMode)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(FillRule, int8_t)
    +enum class FillRule : int8_t {
       FILL_WINDING,
       FILL_EVEN_ODD
    -MOZ_END_ENUM_CLASS(FillRule)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(AntialiasMode, int8_t)
    +enum class AntialiasMode : int8_t {
       NONE,
       GRAY,
       SUBPIXEL,
       DEFAULT
    -MOZ_END_ENUM_CLASS(AntialiasMode)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(Filter, int8_t)
    +enum class Filter : int8_t {
       GOOD,
       LINEAR,
       POINT
    -MOZ_END_ENUM_CLASS(Filter)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(PatternType, int8_t)
    +enum class PatternType : int8_t {
       COLOR,
       SURFACE,
       LINEAR_GRADIENT,
       RADIAL_GRADIENT
    -MOZ_END_ENUM_CLASS(PatternType)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(JoinStyle, int8_t)
    +enum class JoinStyle : int8_t {
       BEVEL,
       ROUND,
       MITER, //!< Mitered if within the miter limit, else, if the backed supports
              //!< it (D2D), the miter is clamped. If the backend does not support
              //!< miter clamping the behavior is as for MITER_OR_BEVEL.
       MITER_OR_BEVEL //!< Mitered if within the miter limit, else beveled.
    -MOZ_END_ENUM_CLASS(JoinStyle)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(CapStyle, int8_t)
    +enum class CapStyle : int8_t {
       BUTT,
       ROUND,
       SQUARE
    -MOZ_END_ENUM_CLASS(CapStyle)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(SamplingBounds, int8_t)
    +enum class SamplingBounds : int8_t {
       UNBOUNDED,
       BOUNDED
    -MOZ_END_ENUM_CLASS(SamplingBounds)
    +};
     
     /* Color is stored in non-premultiplied form */
     struct Color
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/AndroidNativeWindow.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/AndroidNativeWindow.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/AndroidNativeWindow.h	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/AndroidNativeWindow.h	2015-02-03 14:34:01.000000000 +0000
    @@ -12,19 +12,18 @@
     #include "GLDefs.h"
     
     #include "nsISupports.h"
    -#include "mozilla/TypedEnum.h"
     #include "mozilla/gfx/2D.h"
     
     
     namespace mozilla {
     namespace gl {
     
    -MOZ_BEGIN_ENUM_CLASS(AndroidWindowFormat)
    +enum class AndroidWindowFormat {
       Unknown = -1,
       RGBA_8888 = 1,
       RGBX_8888 = 1 << 1,
       RGB_565 = 1 << 2
    -MOZ_END_ENUM_CLASS(AndroidWindowFormat)
    +};
     
     /**
      * This class is a wrapper around Android's SurfaceTexture class.
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/AndroidSurfaceTexture.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/AndroidSurfaceTexture.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/AndroidSurfaceTexture.cpp	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/AndroidSurfaceTexture.cpp	2015-02-03 14:34:01.000000000 +0000
    @@ -31,12 +31,6 @@
     static int sNextID = 0;
     
     static bool
    -IsDetachSupported()
    -{
    -  return AndroidBridge::Bridge()->GetAPIVersion() >= 16; /* Jelly Bean */
    -}
    -
    -static bool
     IsSTSupported()
     {
       return AndroidBridge::Bridge()->GetAPIVersion() >= 14; /* ICS */
    @@ -87,7 +81,7 @@
         return NS_OK;
       }
     
    -  if (!IsDetachSupported()) {
    +  if (!CanDetach()) {
         return NS_ERROR_NOT_AVAILABLE;
       }
     
    @@ -104,6 +98,8 @@
       mAttachedContext->MakeCurrent();
       aContext->fGenTextures(1, &mTexture);
     
    +  UpdateCanDetach();
    +
       return mSurfaceTexture->AttachToGLContext(mTexture);
     }
     
    @@ -112,8 +108,10 @@
     {
       MonitorAutoLock lock(mMonitor);
     
    -  if (!IsDetachSupported() ||
    -      !mAttachedContext || !mAttachedContext->IsOwningThreadCurrent()) {
    +  if (!CanDetach() ||
    +      !mAttachedContext ||
    +      !mAttachedContext->IsOwningThreadCurrent())
    +  {
         return NS_ERROR_FAILURE;
       }
     
    @@ -127,10 +125,21 @@
       return NS_OK;
     }
     
    +void
    +AndroidSurfaceTexture::UpdateCanDetach()
    +{
    +  // The API for attach/detach only exists on 16+, and PowerVR has some sort of
    +  // fencing issue.
    +  mCanDetach = AndroidBridge::Bridge()->GetAPIVersion() >= 16 &&
    +    (!mAttachedContext || mAttachedContext->Vendor() != GLVendor::Imagination);
    +}
    +
     bool
     AndroidSurfaceTexture::Init(GLContext* aContext, GLuint aTexture)
     {
    -  if (!aTexture && !IsDetachSupported()) {
    +  UpdateCanDetach();
    +
    +  if (!aTexture && !CanDetach()) {
         // We have no texture and cannot initialize detached, bail out
         return false;
       }
    @@ -167,6 +176,7 @@
       , mSurface()
       , mMonitor("AndroidSurfaceTexture::mContextMonitor")
       , mAttachedContext(nullptr)
    +  , mCanDetach(false)
     {
     }
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/AndroidSurfaceTexture.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/AndroidSurfaceTexture.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/AndroidSurfaceTexture.h	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/AndroidSurfaceTexture.h	2015-02-03 14:34:01.000000000 +0000
    @@ -61,9 +61,12 @@
       // attached, we try to wait for it to become detached.
       nsresult Attach(GLContext* aContext, PRIntervalTime aTiemout = PR_INTERVAL_NO_TIMEOUT);
     
    -  // This is a noop on ICS, and will always fail
       nsresult Detach();
     
    +  // Ability to detach is based on API version (16+), and we also block PowerVR since it has some type
    +  // of fencing problem. Bug 1100126.
    +  bool CanDetach() { return mCanDetach; }
    +
       GLContext* GetAttachedContext() { return mAttachedContext; }
     
       AndroidNativeWindow* NativeWindow() {
    @@ -94,6 +97,7 @@
       ~AndroidSurfaceTexture();
     
       bool Init(GLContext* aContext, GLuint aTexture);
    +  void UpdateCanDetach();
     
       GLuint mTexture;
       widget::sdk::SurfaceTexture::GlobalRef mSurfaceTexture;
    @@ -101,6 +105,7 @@
     
       Monitor mMonitor;
       GLContext* mAttachedContext;
    +  bool mCanDetach;
     
       RefPtr mNativeWindow;
       int mID;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/GLBlitHelper.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/GLBlitHelper.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/GLBlitHelper.cpp	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/GLBlitHelper.cpp	2015-02-03 14:34:01.000000000 +0000
    @@ -723,6 +723,8 @@
     
     #ifdef MOZ_WIDGET_ANDROID
     
    +#define ATTACH_WAIT_MS 50
    +
     bool
     GLBlitHelper::BlitSurfaceTextureImage(layers::SurfaceTextureImage* stImage, bool yflip)
     {
    @@ -730,7 +732,7 @@
     
         ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
     
    -    if (NS_FAILED(surfaceTexture->Attach(mGL)))
    +    if (NS_FAILED(surfaceTexture->Attach(mGL, PR_MillisecondsToInterval(ATTACH_WAIT_MS))))
             return false;
     
         // UpdateTexImage() changes the EXTERNAL binding, so save it here
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/GLContextFeatures.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/GLContextFeatures.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/GLContextFeatures.cpp	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/GLContextFeatures.cpp	2015-02-03 14:34:01.000000000 +0000
    @@ -16,7 +16,7 @@
     
     const size_t kMAX_EXTENSION_GROUP_SIZE = 5;
     
    -MOZ_BEGIN_ENUM_CLASS(GLVersion, uint32_t)
    +enum class GLVersion : uint32_t {
         NONE  = 0,   // Feature is not supported natively by GL
         GL1_2 = 120,
         GL1_3 = 130,
    @@ -30,14 +30,14 @@
         GL4_1 = 410,
         GL4_2 = 420,
         GL4_3 = 430,
    -MOZ_END_ENUM_CLASS(GLVersion)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(GLESVersion, uint32_t)
    +enum class GLESVersion : uint32_t {
         NONE  = 0,   // Feature is not support natively by GL ES
         ES2   = 200,
         ES3   = 300,
         ES3_1 = 310,
    -MOZ_END_ENUM_CLASS(GLESVersion)
    +};
     
     // ARB_ES2_compatibility is natively supported in OpenGL 4.1.
     static const GLVersion kGLCoreVersionForES2Compat = GLVersion::GL4_1;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/GLContextGLX.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/GLContextGLX.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/GLContextGLX.h	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/GLContextGLX.h	2015-02-03 14:34:01.000000000 +0000
    @@ -16,7 +16,7 @@
     class GLContextGLX : public GLContext
     {
     public:
    -    MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextGLX)
    +    MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextGLX, MOZ_OVERRIDE)
         static already_AddRefed
         CreateGLContext(const SurfaceCaps& caps,
                         GLContextGLX* shareContext,
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/GLContext.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/GLContext.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/GLContext.h	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/GLContext.h	2015-02-03 14:34:01.000000000 +0000
    @@ -84,7 +84,7 @@
     namespace mozilla {
     namespace gl {
     
    -MOZ_BEGIN_ENUM_CLASS(GLFeature)
    +enum class GLFeature {
         bind_buffer_offset,
         blend_minmax,
         clear_buffers,
    @@ -134,17 +134,17 @@
         uniform_matrix_nonsquare,
         vertex_array_object,
         EnumMax
    -MOZ_END_ENUM_CLASS(GLFeature)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(ContextProfile, uint8_t)
    +enum class ContextProfile : uint8_t {
         Unknown = 0,
         OpenGL, // only for IsAtLeast's  parameter
         OpenGLCore,
         OpenGLCompatibility,
         OpenGLES
    -MOZ_END_ENUM_CLASS(ContextProfile)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(GLVendor)
    +enum class GLVendor {
         Intel,
         NVIDIA,
         ATI,
    @@ -154,9 +154,9 @@
         Vivante,
         VMware,
         Other
    -MOZ_END_ENUM_CLASS(GLVendor)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(GLRenderer)
    +enum class GLRenderer {
         Adreno200,
         Adreno205,
         AdrenoTM200,
    @@ -170,7 +170,7 @@
         IntelHD3000,
         MicrosoftBasicRenderDriver,
         Other
    -MOZ_END_ENUM_CLASS(GLRenderer)
    +};
     
     class GLContext
         : public GLLibraryLoader
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/GLContextTypes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/GLContextTypes.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/GLContextTypes.h	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/GLContextTypes.h	2015-02-03 14:34:01.000000000 +0000
    @@ -7,25 +7,24 @@
     #define GLCONTEXT_TYPES_H_
     
     #include "GLTypes.h"
    -#include "mozilla/TypedEnum.h"
     
     namespace mozilla {
     namespace gl {
     
     class GLContext;
     
    -MOZ_BEGIN_ENUM_CLASS(GLContextType)
    +enum class GLContextType {
         Unknown,
         WGL,
         CGL,
         GLX,
         EGL
    -MOZ_END_ENUM_CLASS(GLContextType)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(OriginPos, uint8_t)
    +enum class OriginPos : uint8_t {
       TopLeft,
       BottomLeft
    -MOZ_END_ENUM_CLASS(OriginPos)
    +};
     
     struct GLFormats
     {
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/GLContextWGL.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/GLContextWGL.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/GLContextWGL.h	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/GLContextWGL.h	2015-02-03 14:34:01.000000000 +0000
    @@ -16,7 +16,7 @@
     class GLContextWGL : public GLContext
     {
     public:
    -    MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextWGL)
    +    MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextWGL, MOZ_OVERRIDE)
         // From Window: (possibly for offscreen!)
         GLContextWGL(const SurfaceCaps& caps,
                      GLContext* sharedContext,
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/SurfaceTypes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/SurfaceTypes.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/gl/SurfaceTypes.h	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/gl/SurfaceTypes.h	2015-02-03 14:34:01.000000000 +0000
    @@ -6,7 +6,6 @@
     #ifndef SURFACE_TYPES_H_
     #define SURFACE_TYPES_H_
     
    -#include "mozilla/TypedEnum.h"
     #include "mozilla/RefPtr.h"
     #include "mozilla/Attributes.h"
     #include 
    @@ -67,7 +66,7 @@
         }
     };
     
    -MOZ_BEGIN_ENUM_CLASS(SharedSurfaceType, uint8_t)
    +enum class SharedSurfaceType : uint8_t {
         Unknown = 0,
     
         Basic,
    @@ -80,16 +79,16 @@
         IOSurface,
     
         Max
    -MOZ_END_ENUM_CLASS(SharedSurfaceType)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(AttachmentType, uint8_t)
    +enum class AttachmentType : uint8_t {
         Screen = 0,
     
         GLTexture,
         GLRenderbuffer,
     
         Max
    -MOZ_END_ENUM_CLASS(AttachmentType)
    +};
     
     } /* namespace gfx */
     } /* namespace mozilla */
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/ipc/GfxMessageUtils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/ipc/GfxMessageUtils.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/ipc/GfxMessageUtils.h	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/ipc/GfxMessageUtils.h	2015-02-03 14:34:02.000000000 +0000
    @@ -34,13 +34,7 @@
     namespace mozilla {
     
     typedef gfxImageFormat PixelFormat;
    -#if defined(MOZ_HAVE_CXX11_STRONG_ENUMS)
     typedef ::GraphicsFilter GraphicsFilterType;
    -#else
    -// If we don't have support for enum classes, then we need to use the actual
    -// enum type here instead of the simulated enum class.
    -typedef GraphicsFilter::Enum GraphicsFilterType;
    -#endif
     
     } // namespace mozilla
     
    @@ -198,7 +192,7 @@
     
     template <>
     struct ParamTraits
    -  : public ContiguousTypedEnumSerializer<
    +  : public ContiguousEnumSerializer<
                  gfxContentType,
                  gfxContentType::COLOR,
                  gfxContentType::SENTINEL>
    @@ -206,7 +200,7 @@
     
     template <>
     struct ParamTraits
    -  : public ContiguousTypedEnumSerializer<
    +  : public ContiguousEnumSerializer<
                  gfxSurfaceType,
                  gfxSurfaceType::Image,
                  gfxSurfaceType::Max>
    @@ -222,7 +216,7 @@
     
     template <>
     struct ParamTraits
    -  : public ContiguousTypedEnumSerializer<
    +  : public ContiguousEnumSerializer<
                  mozilla::layers::LayersBackend,
                  mozilla::layers::LayersBackend::LAYERS_NONE,
                  mozilla::layers::LayersBackend::LAYERS_LAST>
    @@ -230,7 +224,7 @@
     
     template <>
     struct ParamTraits
    -  : public ContiguousTypedEnumSerializer<
    +  : public ContiguousEnumSerializer<
                  mozilla::layers::ScaleMode,
                  mozilla::layers::ScaleMode::SCALE_NONE,
                  mozilla::layers::ScaleMode::SENTINEL>
    @@ -238,7 +232,7 @@
     
     template <>
     struct ParamTraits
    -  : public ContiguousTypedEnumSerializer<
    +  : public ContiguousEnumSerializer<
                  gfxImageFormat,
                  gfxImageFormat::ARGB32,
                  gfxImageFormat::Unknown>
    @@ -254,7 +248,7 @@
     
     template <>
     struct ParamTraits
    -  : public ContiguousTypedEnumSerializer<
    +  : public ContiguousEnumSerializer<
                  mozilla::gfx::AttributeType,
                  mozilla::gfx::AttributeType::eBool,
                  mozilla::gfx::AttributeType::Max>
    @@ -262,7 +256,7 @@
     
     template <>
     struct ParamTraits
    -  : public ContiguousTypedEnumSerializer<
    +  : public ContiguousEnumSerializer<
                  mozilla::gfx::PrimitiveType,
                  mozilla::gfx::PrimitiveType::Empty,
                  mozilla::gfx::PrimitiveType::Max>
    @@ -270,7 +264,7 @@
     
     template <>
     struct ParamTraits
    -  : public ContiguousTypedEnumSerializer<
    +  : public ContiguousEnumSerializer<
                  mozilla::gfx::ColorSpace,
                  mozilla::gfx::ColorSpace::SRGB,
                  mozilla::gfx::ColorSpace::Max>
    @@ -278,14 +272,14 @@
     
     template <>
     struct ParamTraits
    -  : public BitFlagsTypedEnumSerializer<
    +  : public BitFlagsEnumSerializer<
                 mozilla::layers::TextureFlags,
                 mozilla::layers::TextureFlags::ALL_BITS>
     {};
     
     template <>
     struct ParamTraits
    -  : public ContiguousTypedEnumSerializer<
    +  : public ContiguousEnumSerializer<
                  mozilla::layers::TextureIdentifier,
                  mozilla::layers::TextureIdentifier::Front,
                  mozilla::layers::TextureIdentifier::HighBound>
    @@ -293,14 +287,14 @@
     
     template <>
     struct ParamTraits
    -  : public BitFlagsTypedEnumSerializer<
    +  : public BitFlagsEnumSerializer<
                  mozilla::layers::DeprecatedTextureHostFlags,
                  mozilla::layers::DeprecatedTextureHostFlags::ALL_BITS>
     {};
     
     template <>
     struct ParamTraits
    -  : public BitFlagsTypedEnumSerializer<
    +  : public BitFlagsEnumSerializer<
                  mozilla::layers::DiagnosticTypes,
                  mozilla::layers::DiagnosticTypes::ALL_BITS>
     {};
    @@ -854,7 +848,7 @@
     
     template <>
     struct ParamTraits
    -  : public ContiguousTypedEnumSerializer<
    +  : public ContiguousEnumSerializer<
                  mozilla::layers::CompositableType,
                  mozilla::layers::CompositableType::UNKNOWN,
                  mozilla::layers::CompositableType::COUNT>
    @@ -862,7 +856,7 @@
     
     template <>
     struct ParamTraits
    -  : public ContiguousTypedEnumSerializer<
    +  : public ContiguousEnumSerializer<
                  mozilla::gfx::SurfaceFormat,
                  mozilla::gfx::SurfaceFormat::B8G8R8A8,
                  mozilla::gfx::SurfaceFormat::UNKNOWN>
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/apz/public/GeckoContentController.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/apz/public/GeckoContentController.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/apz/public/GeckoContentController.h	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/apz/public/GeckoContentController.h	2015-02-03 14:34:02.000000000 +0000
    @@ -157,6 +157,7 @@
                                         int aArg = 0) {}
     
       GeckoContentController() {}
    +  virtual void Destroy() {}
     
     protected:
       // Protected destructor, to discourage deletion outside of Release():
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/apz/src/APZCTreeManager.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/apz/src/APZCTreeManager.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/apz/src/APZCTreeManager.cpp	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/apz/src/APZCTreeManager.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -19,6 +19,7 @@
     #include "mozilla/mozalloc.h"           // for operator new
     #include "mozilla/TouchEvents.h"
     #include "mozilla/Preferences.h"        // for Preferences
    +#include "mozilla/EventStateManager.h"  // for WheelPrefs
     #include "nsDebug.h"                    // for NS_WARNING
     #include "nsPoint.h"                    // for nsIntPoint
     #include "nsThreadUtils.h"              // for NS_IsMainThread
    @@ -891,11 +892,11 @@
         }
         case eWheelEventClass: {
           WidgetWheelEvent& wheelEvent = *aEvent.AsWheelEvent();
    -      if (wheelEvent.IsControl() ||
    -          wheelEvent.deltaMode != nsIDOMWheelEvent::DOM_DELTA_LINE)
    +      if (wheelEvent.deltaMode != nsIDOMWheelEvent::DOM_DELTA_LINE ||
    +          !EventStateManager::WheelEventIsScrollAction(&wheelEvent))
           {
    -        // Don't send through APZ if we could be ctrl+zooming or if the delta
    -        // mode is not line-based.
    +        // Don't send through APZ if we're not scrolling or if the delta mode
    +        // is not line-based.
             return ProcessEvent(aEvent, aOutTargetGuid, aOutInputBlockId);
           }
           return ProcessWheelEvent(wheelEvent, aOutTargetGuid, aOutInputBlockId);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/apz/src/AsyncPanZoomController.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/apz/src/AsyncPanZoomController.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/apz/src/AsyncPanZoomController.cpp	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/apz/src/AsyncPanZoomController.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -2800,7 +2800,7 @@
     
           mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics);
     
    -      // Cancel the animation (which will also trigger a repaint request)
    +      // Cancel the animation (which might also trigger a repaint request)
           // after we update the scroll offset above. Otherwise we can be left
           // in a state where things are out of sync.
           CancelAnimation();
    @@ -2811,6 +2811,15 @@
           // correct this we need to update mLastDispatchedPaintMetrics to be the
           // last thing we know was painted by Gecko.
           mLastDispatchedPaintMetrics = aLayerMetrics;
    +
    +      // Since the scroll offset has changed, we need to recompute the
    +      // displayport margins and send them to layout. Otherwise there might be
    +      // scenarios where for example we scroll from the top of a page (where the
    +      // top displayport margin is zero) to the bottom of a page, which will
    +      // result in a displayport that doesn't extend upwards at all.
    +      // Note that even if the CancelAnimation call above requested a repaint
    +      // this is fine because we already have repaint request deduplication.
    +      needContentRepaint = true;
         }
       }
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/apz/util/APZCCallbackHelper.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/apz/util/APZCCallbackHelper.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/apz/util/APZCCallbackHelper.cpp	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/apz/util/APZCCallbackHelper.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -4,7 +4,9 @@
      * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     
     #include "APZCCallbackHelper.h"
    +
     #include "gfxPlatform.h" // For gfxPlatform::UseTiling
    +#include "mozilla/dom/TabParent.h"
     #include "nsIScrollableFrame.h"
     #include "nsLayoutUtils.h"
     #include "nsIDOMElement.h"
    @@ -13,9 +15,14 @@
     #include "nsIDocument.h"
     #include "nsIDOMWindow.h"
     
    +#define APZCCH_LOG(...)
    +// #define APZCCH_LOG(...) printf_stderr("APZCCH: " __VA_ARGS__)
    +
     namespace mozilla {
     namespace layers {
     
    +using dom::TabParent;
    +
     bool
     APZCCallbackHelper::HasValidPresShellId(nsIDOMWindowUtils* aUtils,
                                             const FrameMetrics& aMetrics)
    @@ -348,16 +355,77 @@
         return input;
     }
     
    -nsIntPoint
    -APZCCallbackHelper::ApplyCallbackTransform(const nsIntPoint& aPoint,
    +LayoutDeviceIntPoint
    +APZCCallbackHelper::ApplyCallbackTransform(const LayoutDeviceIntPoint& aPoint,
                                                const ScrollableLayerGuid& aGuid,
                                                const CSSToLayoutDeviceScale& aScale,
                                                float aPresShellResolution)
     {
         LayoutDevicePoint point = LayoutDevicePoint(aPoint.x, aPoint.y);
         point = ApplyCallbackTransform(point / aScale, aGuid, aPresShellResolution) * aScale;
    -    LayoutDeviceIntPoint ret = gfx::RoundedToInt(point);
    -    return nsIntPoint(ret.x, ret.y);
    +    return gfx::RoundedToInt(point);
    +}
    +
    +nsEventStatus
    +APZCCallbackHelper::DispatchWidgetEvent(WidgetGUIEvent& aEvent)
    +{
    +  if (!aEvent.widget)
    +    return nsEventStatus_eConsumeNoDefault;
    +
    +  // A nested process may be capturing events.
    +  if (TabParent* capturer = TabParent::GetEventCapturer()) {
    +    if (capturer->TryCapture(aEvent)) {
    +      // Only touch events should be captured, and touch events from a parent
    +      // process should not make it here. Capture for those is done elsewhere
    +      // (for gonk, in nsWindow::DispatchTouchInputViaAPZ).
    +      MOZ_ASSERT(!XRE_IsParentProcess());
    +
    +      return nsEventStatus_eConsumeNoDefault;
    +    }
    +  }
    +  nsEventStatus status;
    +  NS_ENSURE_SUCCESS(aEvent.widget->DispatchEvent(&aEvent, status),
    +                    nsEventStatus_eConsumeNoDefault);
    +  return status;
    +}
    +
    +nsEventStatus
    +APZCCallbackHelper::DispatchSynthesizedMouseEvent(uint32_t aMsg,
    +                                                  uint64_t aTime,
    +                                                  const LayoutDevicePoint& aRefPoint,
    +                                                  nsIWidget* aWidget)
    +{
    +  MOZ_ASSERT(aMsg == NS_MOUSE_MOVE || aMsg == NS_MOUSE_BUTTON_DOWN ||
    +             aMsg == NS_MOUSE_BUTTON_UP || aMsg == NS_MOUSE_MOZLONGTAP);
    +
    +  WidgetMouseEvent event(true, aMsg, nullptr,
    +                         WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
    +  event.refPoint = LayoutDeviceIntPoint(aRefPoint.x, aRefPoint.y);
    +  event.time = aTime;
    +  event.button = WidgetMouseEvent::eLeftButton;
    +  event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
    +  event.ignoreRootScrollFrame = true;
    +  if (aMsg != NS_MOUSE_MOVE) {
    +    event.clickCount = 1;
    +  }
    +  event.widget = aWidget;
    +
    +  return DispatchWidgetEvent(event);
    +}
    +
    +void
    +APZCCallbackHelper::FireSingleTapEvent(const LayoutDevicePoint& aPoint,
    +                                       nsIWidget* aWidget)
    +{
    +  if (aWidget->Destroyed()) {
    +    return;
    +  }
    +  APZCCH_LOG("Dispatching single-tap component events to %s\n",
    +    Stringify(aPoint).c_str());
    +  int time = 0;
    +  DispatchSynthesizedMouseEvent(NS_MOUSE_MOVE, time, aPoint, aWidget);
    +  DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_DOWN, time, aPoint, aWidget);
    +  DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_UP, time, aPoint, aWidget);
     }
     
     }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/apz/util/APZCCallbackHelper.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/apz/util/APZCCallbackHelper.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/apz/util/APZCCallbackHelper.h	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/apz/util/APZCCallbackHelper.h	2015-02-03 14:34:02.000000000 +0000
    @@ -7,10 +7,12 @@
     #define mozilla_layers_APZCCallbackHelper_h
     
     #include "FrameMetrics.h"
    +#include "mozilla/EventForwards.h"
     #include "nsIDOMWindowUtils.h"
     
     class nsIContent;
     class nsIDocument;
    +class nsIWidget;
     template struct already_AddRefed;
     
     namespace mozilla {
    @@ -97,13 +99,31 @@
                                                const ScrollableLayerGuid& aGuid,
                                                float aPresShellResolution);
     
    -    /* Same as above, but operates on nsIntPoint that are assumed to be in LayoutDevice
    -       pixel space. Requires an additonal |aScale| parameter to convert between CSS and
    +    /* Same as above, but operates on LayoutDeviceIntPoint.
    +       Requires an additonal |aScale| parameter to convert between CSS and
            LayoutDevice space. */
    -    static nsIntPoint ApplyCallbackTransform(const nsIntPoint& aPoint,
    -                                             const ScrollableLayerGuid& aGuid,
    -                                             const CSSToLayoutDeviceScale& aScale,
    -                                             float aPresShellResolution);
    +    static mozilla::LayoutDeviceIntPoint
    +    ApplyCallbackTransform(const LayoutDeviceIntPoint& aPoint,
    +                           const ScrollableLayerGuid& aGuid,
    +                           const CSSToLayoutDeviceScale& aScale,
    +                           float aPresShellResolution);
    +
    +    /* Dispatch a widget event via the widget stored in the event, if any.
    +     * In a child process, allows the TabParent event-capture mechanism to
    +     * intercept the event. */
    +    static nsEventStatus DispatchWidgetEvent(WidgetGUIEvent& aEvent);
    +
    +    /* Synthesize a mouse event with the given parameters, and dispatch it
    +     * via the given widget. */
    +    static nsEventStatus DispatchSynthesizedMouseEvent(uint32_t aMsg,
    +                                                       uint64_t aTime,
    +                                                       const LayoutDevicePoint& aRefPoint,
    +                                                       nsIWidget* aWidget);
    +
    +    /* Fire a single-tap event at the given point. The event is dispatched
    +     * via the given widget. */
    +    static void FireSingleTapEvent(const LayoutDevicePoint& aPoint,
    +                                   nsIWidget* aWidget);
     };
     
     }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/apz/util/ChromeProcessController.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/apz/util/ChromeProcessController.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/apz/util/ChromeProcessController.cpp	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/apz/util/ChromeProcessController.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -3,15 +3,58 @@
      * 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 "mozilla/layers/ChromeProcessController.h"
    +#include "ChromeProcessController.h"
    +
    +#include "MainThreadUtils.h"    // for NS_IsMainThread()
    +#include "base/message_loop.h"  // for MessageLoop
     #include "mozilla/layers/CompositorParent.h"
     #include "mozilla/layers/APZCCallbackHelper.h"
    +#include "nsIDocument.h"
    +#include "nsIPresShell.h"
     #include "nsLayoutUtils.h"
    +#include "nsView.h"
     
     using namespace mozilla;
     using namespace mozilla::layers;
     using namespace mozilla::widget;
     
    +ChromeProcessController::ChromeProcessController(nsIWidget* aWidget)
    +  : mWidget(aWidget)
    +  , mUILoop(MessageLoop::current())
    +{
    +  // Otherwise we're initializing mUILoop incorrectly.
    +  MOZ_ASSERT(NS_IsMainThread());
    +
    +  mUILoop->PostTask(
    +      FROM_HERE,
    +      NewRunnableMethod(this, &ChromeProcessController::InitializeRoot));
    +}
    +
    +void
    +ChromeProcessController::InitializeRoot()
    +{
    +  // Create a view-id and set a zero-margin displayport for the root element
    +  // of the root document in the chrome process. This ensures that the scroll
    +  // frame for this element gets an APZC, which in turn ensures that all content
    +  // in the chrome processes is covered by an APZC.
    +  // The displayport is zero-margin because this element is generally not
    +  // actually scrollable (if it is, APZC will set proper margins when it's
    +  // scrolled).
    +  nsView* view = nsView::GetViewFor(mWidget);
    +  MOZ_ASSERT(view);
    +  nsIPresShell* presShell = view->GetPresShell();
    +  MOZ_ASSERT(presShell);
    +  MOZ_ASSERT(presShell->GetDocument());
    +  nsIContent* content = presShell->GetDocument()->GetDocumentElement();
    +  MOZ_ASSERT(content);
    +  uint32_t presShellId;
    +  FrameMetrics::ViewID viewId;
    +  if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(content, &presShellId, &viewId)) {
    +    nsLayoutUtils::SetDisplayPortMargins(content, presShell, ScreenMargin(), 0,
    +        nsLayoutUtils::RepaintMode::DoNotRepaint);
    +  }
    +}
    +
     void
     ChromeProcessController::RequestContentRepaint(const FrameMetrics& aFrameMetrics)
     {
    @@ -40,3 +83,45 @@
     {
       APZCCallbackHelper::AcknowledgeScrollUpdate(aScrollId, aScrollGeneration);
     }
    +
    +void
    +ChromeProcessController::Destroy()
    +{
    +  if (MessageLoop::current() != mUILoop) {
    +    mUILoop->PostTask(
    +      FROM_HERE,
    +      NewRunnableMethod(this, &ChromeProcessController::Destroy));
    +    return;
    +  }
    +
    +  MOZ_ASSERT(MessageLoop::current() == mUILoop);
    +  mWidget = nullptr;
    +}
    +
    +float
    +ChromeProcessController::GetPresShellResolution() const
    +{
    +  // The document in the chrome process cannot be zoomed, so its pres shell
    +  // resolution is 1.
    +  return 1.0f;
    +}
    +
    +void
    +ChromeProcessController::HandleSingleTap(const CSSPoint& aPoint,
    +                                         int32_t aModifiers,
    +                                         const ScrollableLayerGuid& aGuid)
    +{
    +  if (MessageLoop::current() != mUILoop) {
    +    mUILoop->PostTask(
    +        FROM_HERE,
    +        NewRunnableMethod(this, &ChromeProcessController::HandleSingleTap,
    +                          aPoint, aModifiers, aGuid));
    +    return;
    +  }
    +
    +  LayoutDevicePoint point =
    +      APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid, GetPresShellResolution())
    +    * mWidget->GetDefaultScale();
    +
    +  APZCCallbackHelper::FireSingleTapEvent(point, mWidget);
    +}
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/apz/util/ChromeProcessController.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/apz/util/ChromeProcessController.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/apz/util/ChromeProcessController.h	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/apz/util/ChromeProcessController.h	2015-02-03 14:34:02.000000000 +0000
    @@ -7,6 +7,11 @@
     #define mozilla_layers_ChromeProcessController_h
     
     #include "mozilla/layers/GeckoContentController.h"
    +#include "nsCOMPtr.h"
    +
    +class nsIWidget;
    +
    +class MessageLoop;
     
     namespace mozilla {
     
    @@ -21,6 +26,9 @@
       typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid;
     
     public:
    +  explicit ChromeProcessController(nsIWidget* aWidget);
    +  virtual void Destroy() MOZ_OVERRIDE;
    +
       // GeckoContentController interface
       virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) MOZ_OVERRIDE;
       virtual void PostDelayedTask(Task* aTask, int aDelayMs) MOZ_OVERRIDE;
    @@ -30,7 +38,7 @@
       virtual void HandleDoubleTap(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
                                    const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE {}
       virtual void HandleSingleTap(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
    -                               const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE {}
    +                               const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
       virtual void HandleLongTap(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
                                    const ScrollableLayerGuid& aGuid,
                                    uint64_t aInputBlockId) MOZ_OVERRIDE {}
    @@ -38,6 +46,13 @@
                                    const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE {}
       virtual void SendAsyncScrollDOMEvent(bool aIsRoot, const mozilla::CSSRect &aContentRect,
                                            const mozilla::CSSSize &aScrollableSize) MOZ_OVERRIDE {}
    +
    +private:
    +  nsCOMPtr mWidget;
    +  MessageLoop* mUILoop;
    +
    +  void InitializeRoot();
    +  float GetPresShellResolution() const;
     };
     
     } // namespace layers
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/client/ClientLayerManager.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/client/ClientLayerManager.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/client/ClientLayerManager.cpp	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/client/ClientLayerManager.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -22,18 +22,19 @@
     #include "mozilla/layers/TextureClientPool.h" // for TextureClientPool
     #include "ClientReadbackLayer.h"        // for ClientReadbackLayer
     #include "nsAString.h"
    -#include "nsIWidget.h"                  // for nsIWidget
     #include "nsIWidgetListener.h"
     #include "nsTArray.h"                   // for AutoInfallibleTArray
     #include "nsXULAppAPI.h"                // for XRE_GetProcessType, etc
     #include "TiledLayerBuffer.h"
     #include "mozilla/dom/WindowBinding.h"  // for Overfill Callback
     #include "FrameLayerBuilder.h"          // for FrameLayerbuilder
    -#include "gfxPrefs.h"
     #ifdef MOZ_WIDGET_ANDROID
     #include "AndroidBridge.h"
     #include "LayerMetricsWrapper.h"
     #endif
    +#ifdef XP_WIN
    +#include "gfxWindowsPlatform.h"
    +#endif
     
     namespace mozilla {
     namespace layers {
    @@ -207,11 +208,16 @@
       // composited (including resampling) asynchronously before we get
       // a chance to repaint, so we have to ensure that it's all valid
       // and not rotated.
    +  //
    +  // Desktop does not support async zoom yet, so we ignore this for those
    +  // platforms.
    +#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
       if (mWidget) {
         if (dom::TabChild* window = mWidget->GetOwningTabChild()) {
           mCompositorMightResample = window->IsAsyncPanZoomEnabled();
         }
       }
    +#endif
     
       // If we have a non-default target, we need to let our shadow manager draw
       // to it. This will happen at the end of the transaction.
    @@ -293,6 +299,14 @@
     }
     
     void
    +ClientLayerManager::StorePluginWidgetConfigurations(const nsTArray& aConfigurations)
    +{
    +  if (mForwarder) {
    +    mForwarder->StorePluginWidgetConfigurations(aConfigurations);
    +  }
    +}
    +
    +void
     ClientLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
                                        void* aCallbackData,
                                        EndTransactionFlags aFlags)
    @@ -736,7 +750,16 @@
         case LayersBackend::LAYERS_OPENGL: aName.AssignLiteral("OpenGL"); return;
         case LayersBackend::LAYERS_D3D9: aName.AssignLiteral("Direct3D 9"); return;
         case LayersBackend::LAYERS_D3D10: aName.AssignLiteral("Direct3D 10"); return;
    -    case LayersBackend::LAYERS_D3D11: aName.AssignLiteral("Direct3D 11"); return;
    +    case LayersBackend::LAYERS_D3D11: {
    +#ifdef XP_WIN
    +      if (gfxWindowsPlatform::GetPlatform()->IsWARP()) {
    +        aName.AssignLiteral("Direct3D 11 WARP");
    +      } else {
    +        aName.AssignLiteral("Direct3D 11");
    +      }
    +#endif
    +      return;
    +    }
         default: NS_RUNTIMEABORT("Invalid backend");
       }
     }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/client/ClientLayerManager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/client/ClientLayerManager.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/client/ClientLayerManager.h	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/client/ClientLayerManager.h	2015-02-03 14:34:02.000000000 +0000
    @@ -26,8 +26,7 @@
     #include "nsTArray.h"                   // for nsTArray
     #include "nscore.h"                     // for nsAString
     #include "mozilla/layers/TransactionIdAllocator.h"
    -
    -class nsIWidget;
    +#include "nsIWidget.h"                  // For plugin window configuration information structs
     
     namespace mozilla {
     namespace layers {
    @@ -129,6 +128,15 @@
       void ReturnTextureClient(TextureClient& aClient);
       void ReportClientLost(TextureClient& aClient);
     
    +  /**
    +   * Pass through call to the forwarder for nsPresContext's
    +   * CollectPluginGeometryUpdates. Passes widget configuration information
    +   * to the compositor for transmission to the chrome process. This
    +   * configuration gets set when the window paints.
    +   */
    +  void StorePluginWidgetConfigurations(const nsTArray&
    +                                       aConfigurations) MOZ_OVERRIDE;
    +
       // Drop cached resources and ask our shadow manager to do the same,
       // if we have one.
       virtual void ClearCachedResources(Layer* aSubtree = nullptr) MOZ_OVERRIDE;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/client/ClientTiledPaintedLayer.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/client/ClientTiledPaintedLayer.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/client/ClientTiledPaintedLayer.cpp	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/client/ClientTiledPaintedLayer.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -418,20 +418,36 @@
           ToClientLayer(GetMaskLayer())->RenderLayer();
         }
     
    +    // For more complex cases we need to calculate a bunch of metrics before we
    +    // can do the paint.
    +    BeginPaint();
    +    if (mPaintData.mPaintFinished) {
    +      return;
    +    }
    +
         // In some cases we can take a fast path and just be done with it.
         if (UseFastPath()) {
           TILING_LOG("TILING %p: Taking fast-path\n", this);
           mValidRegion = neededRegion;
    +
    +      // Make sure that tiles that fall outside of the visible region or outside of the
    +      // critical displayport are discarded on the first update. Also make sure that we
    +      // only draw stuff inside the critical displayport on the first update.
    +      if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
    +        mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
    +        invalidRegion.And(invalidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
    +      }
    +
    +      if (invalidRegion.IsEmpty()) {
    +        EndPaint();
    +        return;
    +      }
    +
    +      mContentClient->mTiledBuffer.SetFrameResolution(mPaintData.mResolution);
           mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data);
           ClientManager()->Hold(this);
           mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER);
    -      return;
    -    }
    -
    -    // For more complex cases we need to calculate a bunch of metrics before we
    -    // can do the paint.
    -    BeginPaint();
    -    if (mPaintData.mPaintFinished) {
    +      EndPaint();
           return;
         }
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/client/TextureClient.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/client/TextureClient.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/client/TextureClient.cpp	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/client/TextureClient.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -674,8 +674,7 @@
     MemoryTextureClient::Allocate(uint32_t aSize)
     {
       MOZ_ASSERT(!mBuffer);
    -  static const fallible_t fallible = fallible_t();
    -  mBuffer = new(fallible) uint8_t[aSize];
    +  mBuffer = new (fallible) uint8_t[aSize];
       if (!mBuffer) {
         NS_WARNING("Failed to allocate buffer");
         return false;
    @@ -909,6 +908,7 @@
       , mSurf(surf)
       , mGL(mSurf->mGL)
     {
    +  AddFlags(TextureFlags::DEALLOCATE_CLIENT);
     }
     
     SharedSurfaceTextureClient::~SharedSurfaceTextureClient()
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/client/TextureClient.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/client/TextureClient.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/client/TextureClient.h	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/client/TextureClient.h	2015-02-03 14:34:02.000000000 +0000
    @@ -89,9 +89,9 @@
     
       static TemporaryRef CreateSyncObject(SyncHandle aHandle);
     
    -  MOZ_BEGIN_NESTED_ENUM_CLASS(SyncType)
    +  enum class SyncType {
         D3D11,
    -  MOZ_END_NESTED_ENUM_CLASS(SyncType)
    +  };
     
       virtual SyncType GetSyncType() = 0;
       virtual void FinalizeFrame() = 0;
    @@ -459,7 +459,7 @@
       /**
        * This function waits until the buffer is no longer being used.
        */
    -  virtual void WaitForBufferOwnership() {}
    +  virtual void WaitForBufferOwnership(bool aWaitReleaseFence = true) {}
     
       /**
        * Track how much of this texture is wasted.
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/Compositor.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/Compositor.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/Compositor.cpp	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/Compositor.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -22,30 +22,32 @@
     /* static */ LayersBackend
     Compositor::GetBackend()
     {
    -  AssertOnCompositorThread();
    +  if (sBackend != LayersBackend::LAYERS_NONE) {
    +    AssertOnCompositorThread();
    +  }
       return sBackend;
     }
     
     /* static */ void
     Compositor::SetBackend(LayersBackend backend)
     {
    -  if (!gIsGtest && sBackend != LayersBackend::LAYERS_NONE && sBackend != backend) {
    +  if (!gIsGtest && sBackend != backend &&
    +      sBackend != LayersBackend::LAYERS_NONE &&
    +      backend != LayersBackend::LAYERS_NONE) {
         // Assert this once we figure out bug 972891.
    -    //MOZ_CRASH("Trying to use more than one OMTC compositor.");
    -
     #ifdef XP_MACOSX
    -    printf("ERROR: Changing compositor from %u to %u.\n",
    -           unsigned(sBackend), unsigned(backend));
    +    gfxWarning() << "Changing compositor from " << unsigned(sBackend) << " to " << unsigned(backend);
     #endif
       }
    +
       sBackend = backend;
     }
     
     /* static */ void
     Compositor::AssertOnCompositorThread()
     {
    -  MOZ_ASSERT(CompositorParent::CompositorLoop() ==
    -             MessageLoop::current(),
    +  MOZ_ASSERT(!CompositorParent::CompositorLoop() ||
    +             CompositorParent::CompositorLoop() == MessageLoop::current(),
                  "Can only call this from the compositor thread!");
     }
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/CompositorTypes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/CompositorTypes.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/CompositorTypes.h	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/CompositorTypes.h	2015-02-03 14:34:02.000000000 +0000
    @@ -13,7 +13,6 @@
     #include "mozilla/gfx/Types.h"
     #include "mozilla/EnumSet.h"
     
    -#include "mozilla/TypedEnum.h"
     #include "mozilla/TypedEnumBits.h"
     
     namespace mozilla {
    @@ -25,7 +24,7 @@
      * by the compositableCient, they may be modified by either the compositable or
      * texture clients.
      */
    -MOZ_BEGIN_ENUM_CLASS(TextureFlags, uint32_t)
    +enum class TextureFlags : uint32_t {
       NO_FLAGS           = 0,
       // Use nearest-neighbour texture filtering (as opposed to linear filtering).
       USE_NEAREST_FILTER = 1 << 0,
    @@ -67,7 +66,7 @@
       ALL_BITS           = (1 << 10) - 1,
       // the default flags
       DEFAULT = NO_FLAGS
    -MOZ_END_ENUM_CLASS(TextureFlags)
    +};
     MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TextureFlags)
     
     static inline bool
    @@ -83,14 +82,14 @@
     /**
      * The type of debug diagnostic to enable.
      */
    -MOZ_BEGIN_ENUM_CLASS(DiagnosticTypes, uint8_t)
    +enum class DiagnosticTypes : uint8_t {
       NO_DIAGNOSTIC    = 0,
       TILE_BORDERS     = 1 << 0,
       LAYER_BORDERS    = 1 << 1,
       BIGIMAGE_BORDERS = 1 << 2,
       FLASH_BORDERS    = 1 << 3,
       ALL_BITS         = (1 << 4) - 1
    -MOZ_END_ENUM_CLASS(DiagnosticTypes)
    +};
     MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DiagnosticTypes)
     
     #define DIAGNOSTIC_FLASH_COUNTER_MAX 100
    @@ -98,7 +97,7 @@
     /**
      * Information about the object that is being diagnosed.
      */
    -MOZ_BEGIN_ENUM_CLASS(DiagnosticFlags, uint16_t)
    +enum class DiagnosticFlags : uint16_t {
       NO_DIAGNOSTIC   = 0,
       IMAGE           = 1 << 0,
       CONTENT         = 1 << 1,
    @@ -109,13 +108,13 @@
       BIGIMAGE        = 1 << 6,
       COMPONENT_ALPHA = 1 << 7,
       REGION_RECT     = 1 << 8
    -MOZ_END_ENUM_CLASS(DiagnosticFlags)
    +};
     MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DiagnosticFlags)
     
     /**
      * See gfx/layers/Effects.h
      */
    -MOZ_BEGIN_ENUM_CLASS(EffectTypes, uint8_t)
    +enum class EffectTypes : uint8_t {
       MASK,
       BLEND_MODE,
       COLOR_MATRIX,
    @@ -127,12 +126,12 @@
       RENDER_TARGET,
       VR_DISTORTION,
       MAX  //sentinel for the count of all effect types
    -MOZ_END_ENUM_CLASS(EffectTypes)
    +};
     
     /**
      * How the Compositable should manage textures.
      */
    -MOZ_BEGIN_ENUM_CLASS(CompositableType, uint8_t)
    +enum class CompositableType : uint8_t {
       UNKNOWN,
       CONTENT_INC,     // painted layer interface, only sends incremental
                        // updates to a texture on the compositor side.
    @@ -143,19 +142,19 @@
       CONTENT_SINGLE,  // painted layer interface, single buffering
       CONTENT_DOUBLE,  // painted layer interface, double buffering
       COUNT
    -MOZ_END_ENUM_CLASS(CompositableType)
    +};
     
     /**
      * How the texture host is used for composition,
      * XXX - Only used by ContentClientIncremental
      */
    -MOZ_BEGIN_ENUM_CLASS(DeprecatedTextureHostFlags, uint8_t)
    +enum class DeprecatedTextureHostFlags : uint8_t {
       DEFAULT = 0,       // The default texture host for the given SurfaceDescriptor
       TILED = 1 << 0,    // A texture host that supports tiling
       COPY_PREVIOUS = 1 << 1, // Texture contents should be initialized
                                           // from the previous texture.
       ALL_BITS = (1 << 2) - 1
    -MOZ_END_ENUM_CLASS(DeprecatedTextureHostFlags)
    +};
     MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DeprecatedTextureHostFlags)
     
     #ifdef XP_WIN
    @@ -201,13 +200,13 @@
      * XXX - We don't really need this, it will be removed along with the incremental
      * ContentClient/Host.
      */
    -MOZ_BEGIN_ENUM_CLASS(TextureIdentifier, uint8_t)
    +enum class TextureIdentifier : uint8_t {
       Front = 1,
       Back = 2,
       OnWhiteFront = 3,
       OnWhiteBack = 4,
       HighBound
    -MOZ_END_ENUM_CLASS(TextureIdentifier)
    +};
     
     /**
      * Information required by the compositor from the content-side for creating or
    @@ -249,24 +248,24 @@
      *
      * See ShadowLayerForwarder::OpenDescriptor for example.
      */
    -MOZ_BEGIN_ENUM_CLASS(OpenMode, uint8_t)
    +enum class OpenMode : uint8_t {
       OPEN_NONE        = 0,
       OPEN_READ        = 0x1,
       OPEN_WRITE       = 0x2,
       OPEN_READ_WRITE  = OPEN_READ|OPEN_WRITE,
       OPEN_READ_ONLY   = OPEN_READ,
       OPEN_WRITE_ONLY  = OPEN_WRITE
    -MOZ_END_ENUM_CLASS(OpenMode)
    +};
     MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(OpenMode)
     
     // The kinds of mask texture a shader can support
     // We rely on the items in this enum being sequential
    -MOZ_BEGIN_ENUM_CLASS(MaskType, uint8_t)
    +enum class MaskType : uint8_t {
       MaskNone = 0,   // no mask layer
       Mask2d,         // mask layer for layers with 2D transforms
       Mask3d,         // mask layer for layers with 3D transforms
       NumMaskTypes
    -MOZ_END_ENUM_CLASS(MaskType)
    +};
     
     } // namespace layers
     } // namespace mozilla
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/d3d11/TextureD3D11.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/d3d11/TextureD3D11.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/d3d11/TextureD3D11.cpp	2015-01-25 22:24:21.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/d3d11/TextureD3D11.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -364,6 +364,54 @@
       return mDrawTarget;
     }
     
    +static const GUID sD3D11TextureUsage =
    +{ 0xd89275b0, 0x6c7d, 0x4038, { 0xb5, 0xfa, 0x4d, 0x87, 0x16, 0xd5, 0xcc, 0x4e } };
    +
    +/* This class gets its lifetime tied to a D3D texture
    + * and increments memory usage on construction and decrements
    + * on destruction */
    +class TextureMemoryMeasurer : public IUnknown
    +{
    +public:
    +  TextureMemoryMeasurer(size_t aMemoryUsed)
    +  {
    +    mMemoryUsed = aMemoryUsed;
    +    gfxWindowsPlatform::sD3D11MemoryUsed += mMemoryUsed;
    +    mRefCnt = 0;
    +  }
    +  STDMETHODIMP_(ULONG) AddRef() {
    +    mRefCnt++;
    +    return mRefCnt;
    +  }
    +  STDMETHODIMP QueryInterface(REFIID riid,
    +                              void **ppvObject)
    +  {
    +    IUnknown *punk = nullptr;
    +    if (riid == IID_IUnknown) {
    +      punk = this;
    +    }
    +    *ppvObject = punk;
    +    if (punk) {
    +      punk->AddRef();
    +      return S_OK;
    +    } else {
    +      return E_NOINTERFACE;
    +    }
    +  }
    +
    +  STDMETHODIMP_(ULONG) Release() {
    +    int refCnt = --mRefCnt;
    +    if (refCnt == 0) {
    +      gfxWindowsPlatform::sD3D11MemoryUsed -= mMemoryUsed;
    +      delete this;
    +    }
    +    return refCnt;
    +  }
    +private:
    +  int mRefCnt;
    +  int mMemoryUsed;
    +};
    +
     bool
     TextureClientD3D11::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags)
     {
    @@ -386,6 +434,14 @@
         newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
     
         hr = d3d11device->CreateTexture2D(&newDesc, nullptr, byRef(mTexture));
    +    if (FAILED(hr)) {
    +      gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "[D3D11] 2 CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr);
    +      return false;
    +    }
    +    mTexture->SetPrivateDataInterface(sD3D11TextureUsage,
    +                                      new TextureMemoryMeasurer(newDesc.Width * newDesc.Height *
    +                                                                (mFormat == SurfaceFormat::A8 ?
    +                                                                 1 : 4)));
       } else
       {
         ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device();
    @@ -397,11 +453,15 @@
         newDesc.MiscFlags = D3D10_RESOURCE_MISC_SHARED;
     
         hr = device->CreateTexture2D(&newDesc, nullptr, byRef(mTexture10));
    -  }
    +    if (FAILED(hr)) {
    +      gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "[D3D10] 2 CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr);
    +      return false;
    +    }
    +    mTexture10->SetPrivateDataInterface(sD3D11TextureUsage,
    +                                        new TextureMemoryMeasurer(newDesc.Width * newDesc.Height *
    +                                                                  (mFormat == SurfaceFormat::A8 ?
    +                                                                   1 : 4)));
     
    -  if (FAILED(hr)) {
    -    gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "[D3D11] 2 CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr);
    -    return false;
       }
     
       // Defer clearing to the next time we lock to avoid an extra (expensive) lock.
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/D3D9SurfaceImage.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/D3D9SurfaceImage.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/D3D9SurfaceImage.cpp	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/D3D9SurfaceImage.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -21,6 +21,58 @@
     
     D3D9SurfaceImage::~D3D9SurfaceImage() {}
     
    +static const GUID sD3D9TextureUsage =
    +{ 0x631e1338, 0xdc22, 0x497f, { 0xa1, 0xa8, 0xb4, 0xfe, 0x3a, 0xf4, 0x13, 0x4d } };
    +
    +/* This class get's it's lifetime tied to a D3D texture
    + * and increments memory usage on construction and decrements
    + * on destruction */
    +class TextureMemoryMeasurer9 : public IUnknown
    +{
    +public:
    +  TextureMemoryMeasurer9(size_t aMemoryUsed)
    +  {
    +    mMemoryUsed = aMemoryUsed;
    +    gfxWindowsPlatform::sD3D9MemoryUsed += mMemoryUsed;
    +    mRefCnt = 0;
    +  }
    +  ~TextureMemoryMeasurer9()
    +  {
    +    gfxWindowsPlatform::sD3D9MemoryUsed -= mMemoryUsed;
    +  }
    +  STDMETHODIMP_(ULONG) AddRef() {
    +    mRefCnt++;
    +    return mRefCnt;
    +  }
    +  STDMETHODIMP QueryInterface(REFIID riid,
    +                              void **ppvObject)
    +  {
    +    IUnknown *punk = nullptr;
    +    if (riid == IID_IUnknown) {
    +      punk = this;
    +    }
    +    *ppvObject = punk;
    +    if (punk) {
    +      punk->AddRef();
    +      return S_OK;
    +    } else {
    +      return E_NOINTERFACE;
    +    }
    +  }
    +
    +  STDMETHODIMP_(ULONG) Release() {
    +    int refCnt = --mRefCnt;
    +    if (refCnt == 0) {
    +      delete this;
    +    }
    +    return refCnt;
    +  }
    +private:
    +  int mRefCnt;
    +  int mMemoryUsed;
    +};
    +
    +
     HRESULT
     D3D9SurfaceImage::SetData(const Data& aData)
     {
    @@ -62,6 +114,9 @@
                                  &shareHandle);
       NS_ENSURE_TRUE(SUCCEEDED(hr) && shareHandle, hr);
     
    +  // Track the lifetime of this memory
    +  texture->SetPrivateData(sD3D9TextureUsage, new TextureMemoryMeasurer9(region.width * region.height * 4), sizeof(IUnknown *), D3DSPD_IUNKNOWN);
    +
       // Copy the image onto the texture, preforming YUV -> RGB conversion if necessary.
       RefPtr textureSurface;
       hr = texture->GetSurfaceLevel(0, byRef(textureSurface));
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/GLImages.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/GLImages.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/GLImages.cpp	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/GLImages.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -61,9 +61,9 @@
     
       GLBlitHelper helper(sSnapshotContext);
     
    -  helper.BlitImageToFramebuffer(this, size, fb.FB(), true);
    -
    -  ScopedBindFramebuffer bind(sSnapshotContext, fb.FB());
    +  if (!helper.BlitImageToFramebuffer(this, size, fb.FB(), true)) {
    +    return nullptr;
    +  }
     
       RefPtr source =
             gfx::Factory::CreateDataSourceSurface(size, gfx::SurfaceFormat::B8G8R8A8);
    @@ -71,6 +71,7 @@
         return nullptr;
       }
     
    +  ScopedBindFramebuffer bind(sSnapshotContext, fb.FB());
       ReadPixelsIntoDataSurface(sSnapshotContext, source);
       return source.forget();
     }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ImageTypes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ImageTypes.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ImageTypes.h	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ImageTypes.h	2015-02-03 14:34:02.000000000 +0000
    @@ -6,11 +6,9 @@
     #ifndef GFX_IMAGETYPES_H
     #define GFX_IMAGETYPES_H
     
    -#include "mozilla/TypedEnum.h"
    -
     namespace mozilla {
     
    -MOZ_BEGIN_ENUM_CLASS(ImageFormat)
    +enum class ImageFormat {
       /**
        * The PLANAR_YCBCR format creates a PlanarYCbCrImage. All backends should
        * support this format, because the Ogg video decoder depends on it.
    @@ -84,15 +82,15 @@
        * The opaque handle would be a platform specific identifier.
        */
       OVERLAY_IMAGE
    -MOZ_END_ENUM_CLASS(ImageFormat)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(StereoMode)
    +enum class StereoMode {
       MONO,
       LEFT_RIGHT,
       RIGHT_LEFT,
       BOTTOM_TOP,
       TOP_BOTTOM
    -MOZ_END_ENUM_CLASS(StereoMode)
    +};
     
     } // namespace
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/CompositorChild.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/CompositorChild.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/CompositorChild.cpp	2015-01-25 22:24:21.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/CompositorChild.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -22,6 +22,10 @@
     #include "FrameLayerBuilder.h"
     #include "mozilla/dom/TabChild.h"
     #include "mozilla/unused.h"
    +#include "mozilla/DebugOnly.h"
    +#if defined(XP_WIN)
    +#include "WinUtils.h"
    +#endif
     
     using mozilla::layers::LayerTransactionChild;
     using mozilla::dom::TabChildBase;
    @@ -135,6 +139,123 @@
       return true;
     }
     
    +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
    +static void CalculatePluginClip(const nsIntRect& aBounds,
    +                                const nsTArray& aPluginClipRects,
    +                                const nsIntPoint& aContentOffset,
    +                                const nsIntRegion& aParentLayerVisibleRegion,
    +                                nsTArray& aResult,
    +                                nsIntRect& aVisibleBounds,
    +                                bool& aPluginIsVisible)
    +{
    +  aPluginIsVisible = true;
    +  // aBounds (content origin)
    +  nsIntRegion contentVisibleRegion(aBounds);
    +  // aPluginClipRects (plugin widget origin)
    +  for (uint32_t idx = 0; idx < aPluginClipRects.Length(); idx++) {
    +    nsIntRect rect = aPluginClipRects[idx];
    +    // shift to content origin
    +    rect.MoveBy(aBounds.x, aBounds.y);
    +    contentVisibleRegion.AndWith(rect);
    +  }
    +  // apply layers clip (window origin)
    +  nsIntRegion region = aParentLayerVisibleRegion;
    +  region.MoveBy(-aContentOffset.x, -aContentOffset.y);
    +  contentVisibleRegion.AndWith(region);
    +  if (contentVisibleRegion.IsEmpty()) {
    +    aPluginIsVisible = false;
    +    return;
    +  }
    +  // shift to plugin widget origin
    +  contentVisibleRegion.MoveBy(-aBounds.x, -aBounds.y);
    +  nsIntRegionRectIterator iter(contentVisibleRegion);
    +  for (const nsIntRect* rgnRect = iter.Next(); rgnRect; rgnRect = iter.Next()) {
    +    aResult.AppendElement(*rgnRect);
    +    aVisibleBounds.UnionRect(aVisibleBounds, *rgnRect);
    +  }
    +}
    +#endif
    +
    +bool
    +CompositorChild::RecvUpdatePluginConfigurations(const nsIntPoint& aContentOffset,
    +                                                const nsIntRegion& aParentLayerVisibleRegion,
    +                                                nsTArray&& aPlugins)
    +{
    +#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
    +  NS_NOTREACHED("CompositorChild::RecvUpdatePluginConfigurations calls "
    +                "unexpected on this platform.");
    +  return false;
    +#else
    +  // Now that we are on the main thread, update plugin widget config.
    +  // This should happen a little before we paint to the screen assuming
    +  // the main thread is running freely.
    +  DebugOnly rv;
    +  MOZ_ASSERT(NS_IsMainThread());
    +
    +  // Tracks visible plugins we update, so we can hide any plugins we don't.
    +  nsTArray visiblePluginIds;
    +
    +  for (uint32_t pluginsIdx = 0; pluginsIdx < aPlugins.Length(); pluginsIdx++) {
    +    nsIWidget* widget =
    +      nsIWidget::LookupRegisteredPluginWindow(aPlugins[pluginsIdx].windowId());
    +    if (!widget) {
    +      NS_WARNING("Unexpected, plugin id not found!");
    +      continue;
    +    }
    +    bool isVisible = aPlugins[pluginsIdx].visible();
    +    if (widget && !widget->Destroyed()) {
    +      nsIntRect bounds;
    +      nsIntRect visibleBounds;
    +      // If the plugin is visible update it's geometry.
    +      if (isVisible) {
    +        // bounds (content origin) - don't pass true to Resize, it triggers a
    +        // sync paint update to the plugin process on Windows, which happens
    +        // prior to clipping information being applied.
    +        bounds = aPlugins[pluginsIdx].bounds();
    +        rv = widget->Resize(aContentOffset.x + bounds.x,
    +                            aContentOffset.y + bounds.y,
    +                            bounds.width, bounds.height, false);
    +        NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure");
    +        nsTArray rectsOut;
    +        CalculatePluginClip(bounds, aPlugins[pluginsIdx].clip(),
    +                            aContentOffset, aParentLayerVisibleRegion,
    +                            rectsOut, visibleBounds, isVisible);
    +        if (isVisible) {
    +          // content clipping region (widget origin)
    +          rv = widget->SetWindowClipRegion(rectsOut, false);
    +          NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure");
    +        }
    +      }
    +
    +      // visible state - updated after clipping, prior to invalidating
    +      rv = widget->Show(isVisible);
    +      NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure");
    +
    +      // Handle invalidation, this can be costly, avoid if it is not needed.
    +      if (isVisible) {
    +        // invalidate region (widget origin)
    +        nsIntRect bounds = aPlugins[pluginsIdx].bounds();
    +        nsIntRect rect(0, 0, bounds.width, bounds.height);
    +#if defined(XP_WIN)
    +        // Work around for flash's crummy sandbox. See bug 762948. This call
    +        // digs down into the window hirearchy, invalidating regions on
    +        // windows owned by other processes.
    +        mozilla::widget::WinUtils::InvalidatePluginAsWorkaround(widget, visibleBounds);
    +#else
    +        rv = widget->Invalidate(visibleBounds);
    +        NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure");
    +#endif
    +        visiblePluginIds.AppendElement(aPlugins[pluginsIdx].windowId());
    +      }
    +    }
    +  }
    +  // Any plugins we didn't update need to be hidden, as they are
    +  // not associated with visible content.
    +  nsIWidget::UpdateRegisteredPluginWindowVisibility(visiblePluginIds);
    +  return true;
    +#endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
    +}
    +
     bool
     CompositorChild::RecvDidComposite(const uint64_t& aId, const uint64_t& aTransactionId)
     {
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/CompositorChild.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/CompositorChild.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/CompositorChild.h	2015-01-25 22:24:21.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/CompositorChild.h	2015-02-03 14:34:02.000000000 +0000
    @@ -64,11 +64,21 @@
     
       static bool ChildProcessHasCompositor() { return sCompositor != nullptr; }
     
    -  virtual bool RecvInvalidateAll() MOZ_OVERRIDE;
    -  virtual bool RecvOverfill(const uint32_t &aOverfill) MOZ_OVERRIDE;
       void AddOverfillObserver(ClientLayerManager* aLayerManager);
     
    -  virtual bool RecvDidComposite(const uint64_t& aId, const uint64_t& aTransactionId) MOZ_OVERRIDE;
    +  virtual bool
    +  RecvDidComposite(const uint64_t& aId, const uint64_t& aTransactionId) MOZ_OVERRIDE;
    +
    +  virtual bool
    +  RecvInvalidateAll() MOZ_OVERRIDE;
    +
    +  virtual bool
    +  RecvOverfill(const uint32_t &aOverfill) MOZ_OVERRIDE;
    +
    +  virtual bool
    +  RecvUpdatePluginConfigurations(const nsIntPoint& aContentOffset,
    +                                 const nsIntRegion& aVisibleRegion,
    +                                 nsTArray&& aPlugins) MOZ_OVERRIDE;
     
       /**
        * Request that the parent tell us when graphics are ready on GPU.
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/CompositorParent.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/CompositorParent.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/CompositorParent.cpp	2015-01-25 22:24:21.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/CompositorParent.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -84,9 +84,17 @@
       , mLayerManager(nullptr)
       , mCrossProcessParent(nullptr)
       , mLayerTree(nullptr)
    +  , mUpdatedPluginDataAvailable(false)
     {
     }
     
    +CompositorParent::LayerTreeState::~LayerTreeState()
    +{
    +  if (mController) {
    +    mController->Destroy();
    +  }
    +}
    +
     typedef map LayerTreeMap;
     static LayerTreeMap sIndirectLayerTrees;
     static StaticAutoPtr sIndirectLayerTreesLock;
    @@ -228,7 +236,6 @@
     
     CompositorVsyncObserver::~CompositorVsyncObserver()
     {
    -  MOZ_ASSERT(CompositorParent::IsInCompositorThread());
       MOZ_ASSERT(!mIsObservingVsync);
       // The CompositorVsyncDispatcher is cleaned up before this in the nsBaseWidget, which stops vsync listeners
       mCompositorParent = nullptr;
    @@ -271,6 +278,7 @@
         mSetNeedsCompositeTask = NewRunnableMethod(this,
                                                   &CompositorVsyncObserver::SetNeedsComposite,
                                                   aNeedsComposite);
    +    MOZ_ASSERT(CompositorParent::CompositorLoop());
         CompositorParent::CompositorLoop()->PostTask(FROM_HERE, mSetNeedsCompositeTask);
         return;
       } else {
    @@ -296,6 +304,7 @@
         mCurrentCompositeTask = NewRunnableMethod(this,
                                                   &CompositorVsyncObserver::Composite,
                                                   aVsyncTimestamp);
    +    MOZ_ASSERT(CompositorParent::CompositorLoop());
         CompositorParent::CompositorLoop()->PostTask(FROM_HERE, mCurrentCompositeTask);
       }
       return true;
    @@ -416,6 +425,7 @@
       // FIXME: This holds on the the fact that right now the only thing that
       // can destroy this instance is initialized on the compositor thread after
       // this task has been processed.
    +  MOZ_ASSERT(CompositorLoop());
       CompositorLoop()->PostTask(FROM_HERE, NewRunnableFunction(&AddCompositor,
                                                               this, &mCompositorID));
     
    @@ -647,6 +657,7 @@
     CompositorParent::ScheduleRenderOnCompositorThread()
     {
       CancelableTask *renderTask = NewRunnableMethod(this, &CompositorParent::ScheduleComposition);
    +  MOZ_ASSERT(CompositorLoop());
       CompositorLoop()->PostTask(FROM_HERE, renderTask);
     }
     
    @@ -743,6 +754,7 @@
     
       CancelableTask *pauseTask = NewRunnableMethod(this,
                                                     &CompositorParent::PauseComposition);
    +  MOZ_ASSERT(CompositorLoop());
       CompositorLoop()->PostTask(FROM_HERE, pauseTask);
     
       // Wait until the pause has actually been processed by the compositor thread
    @@ -756,6 +768,7 @@
     
       CancelableTask *resumeTask =
         NewRunnableMethod(this, &CompositorParent::ResumeCompositionAndResize, width, height);
    +  MOZ_ASSERT(CompositorLoop());
       CompositorLoop()->PostTask(FROM_HERE, resumeTask);
     
       // Wait until the resume has actually been processed by the compositor thread
    @@ -1042,6 +1055,7 @@
     CompositorParent::ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
                                           const uint64_t& aTransactionId,
                                           const TargetConfig& aTargetConfig,
    +                                      const InfallibleTArray& aUnused,
                                           bool aIsFirstPaint,
                                           bool aScheduleComposite,
                                           uint32_t aPaintSequenceNumber,
    @@ -1330,6 +1344,7 @@
       // Here main thread notifies compositor to remove an element from
       // sIndirectLayerTrees. This removed element might be queried soon.
       // Checking the elements of sIndirectLayerTrees exist or not before using.
    +  MOZ_ASSERT(CompositorLoop());
       CompositorLoop()->PostTask(FROM_HERE,
                                  NewRunnableFunction(&EraseLayerState, aId));
     }
    @@ -1485,6 +1500,7 @@
       virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
                                        const uint64_t& aTransactionId,
                                        const TargetConfig& aTargetConfig,
    +                                   const InfallibleTArray& aPlugins,
                                        bool aIsFirstPaint,
                                        bool aScheduleComposite,
                                        uint32_t aPaintSequenceNumber,
    @@ -1687,6 +1703,7 @@
       LayerTransactionParent* aLayerTree,
       const uint64_t& aTransactionId,
       const TargetConfig& aTargetConfig,
    +  const InfallibleTArray& aPlugins,
       bool aIsFirstPaint,
       bool aScheduleComposite,
       uint32_t aPaintSequenceNumber,
    @@ -1696,7 +1713,7 @@
     
       MOZ_ASSERT(id != 0);
     
    -  const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(id);
    +  CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(id);
       if (!state) {
         return;
       }
    @@ -1709,6 +1726,10 @@
       }
       UpdateIndirectTree(id, shadowRoot, aTargetConfig);
     
    +  // Cache the plugin data for this remote layer tree
    +  state->mPluginData = aPlugins;
    +  state->mUpdatedPluginDataAvailable = !!state->mPluginData.Length();
    +
       state->mParent->NotifyShadowTreeTransaction(id, aIsFirstPaint, aScheduleComposite,
           aPaintSequenceNumber, aIsRepeatTransaction);
     
    @@ -1721,6 +1742,62 @@
       aLayerTree->SetPendingTransactionId(aTransactionId);
     }
     
    +// Sends plugin window state changes to the main thread
    +static void
    +UpdatePluginWindowState(uint64_t aId)
    +{
    +  CompositorParent::LayerTreeState& lts = sIndirectLayerTrees[aId];
    +  if (!lts.mPluginData.Length()) {
    +    return;
    +  }
    +
    +  bool shouldComposePlugin = !!lts.mRoot &&
    +                             !!lts.mRoot->GetParent() &&
    +                             lts.mUpdatedPluginDataAvailable;
    +
    +  bool shouldHidePlugin = (!lts.mRoot ||
    +                           !lts.mRoot->GetParent()) &&
    +                          !lts.mUpdatedPluginDataAvailable;
    +
    +  if (shouldComposePlugin) {
    +    // Retrieve the offset and visible region of the layer that hosts
    +    // the plugins, CompositorChild needs these in calculating proper
    +    // plugin clipping.
    +    LayerTransactionParent* layerTree = lts.mLayerTree;
    +    Layer* contentRoot = layerTree->GetRoot();
    +    if (contentRoot) {
    +      nsIntPoint offset;
    +      nsIntRegion visibleRegion;
    +      if (contentRoot->GetVisibleRegionRelativeToRootLayer(visibleRegion,
    +                                                           &offset)) {
    +        unused <<
    +          lts.mParent->SendUpdatePluginConfigurations(offset, visibleRegion,
    +                                                      lts.mPluginData);
    +        lts.mUpdatedPluginDataAvailable = false;
    +      } else {
    +        shouldHidePlugin = true;
    +      }
    +    }
    +  }
    +
    +  // Hide all plugins, this remote layer tree is no longer active
    +  if (shouldHidePlugin) {
    +    // hide all the plugins
    +    for (uint32_t pluginsIdx = 0; pluginsIdx < lts.mPluginData.Length();
    +         pluginsIdx++) {
    +      lts.mPluginData[pluginsIdx].visible() = false;
    +    }
    +    nsIntPoint offset;
    +    nsIntRegion region;
    +    unused << lts.mParent->SendUpdatePluginConfigurations(offset,
    +                                                          region,
    +                                                          lts.mPluginData);
    +    // Clear because there's no recovering from this until we receive
    +    // new shadow layer plugin data in ShadowLayersUpdated.
    +    lts.mPluginData.Clear();
    +  }
    +}
    +
     void
     CrossProcessCompositorParent::DidComposite(uint64_t aId)
     {
    @@ -1730,6 +1807,7 @@
         unused << SendDidComposite(aId, layerTree->GetPendingTransactionId());
         layerTree->SetPendingTransactionId(0);
       }
    +  UpdatePluginWindowState(aId);
     }
     
     void
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/CompositorParent.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/CompositorParent.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/CompositorParent.h	2015-01-25 22:24:21.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/CompositorParent.h	2015-02-03 14:34:02.000000000 +0000
    @@ -173,6 +173,7 @@
       virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
                                        const uint64_t& aTransactionId,
                                        const TargetConfig& aTargetConfig,
    +                                   const InfallibleTArray& aPlugins,
                                        bool aIsFirstPaint,
                                        bool aScheduleComposite,
                                        uint32_t aPaintSequenceNumber,
    @@ -290,6 +291,7 @@
     
       struct LayerTreeState {
         LayerTreeState();
    +    ~LayerTreeState();
         nsRefPtr mRoot;
         nsRefPtr mController;
         CompositorParent* mParent;
    @@ -301,6 +303,8 @@
         TargetConfig mTargetConfig;
         APZTestData mApzTestData;
         LayerTransactionParent* mLayerTree;
    +    nsTArray mPluginData;
    +    bool mUpdatedPluginDataAvailable;
       };
     
       /**
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/LayersMessages.ipdlh thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/LayersMessages.ipdlh
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/LayersMessages.ipdlh	2015-01-25 22:24:21.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/LayersMessages.ipdlh	2015-02-03 14:34:02.000000000 +0000
    @@ -259,6 +259,14 @@
       SpecificLayerAttributes specific;
     };
     
    +// See nsIWidget Configurations
    +struct PluginWindowData {
    +  uintptr_t windowId;
    +  nsIntRect[] clip;
    +  nsIntRect bounds;
    +  bool visible;
    +};
    +
     struct OpSetLayerAttributes {
       PLayer layer;
       LayerAttributes attrs;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/LayerTransactionParent.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/LayerTransactionParent.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/LayerTransactionParent.cpp	2015-01-25 22:24:21.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/LayerTransactionParent.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -193,13 +193,14 @@
     LayerTransactionParent::RecvUpdateNoSwap(InfallibleTArray&& cset,
                                              const uint64_t& aTransactionId,
                                              const TargetConfig& targetConfig,
    +                                         PluginsArray&& aPlugins,
                                              const bool& isFirstPaint,
                                              const bool& scheduleComposite,
                                              const uint32_t& paintSequenceNumber,
                                              const bool& isRepeatTransaction,
                                              const mozilla::TimeStamp& aTransactionStart)
     {
    -  return RecvUpdate(Move(cset), aTransactionId, targetConfig, isFirstPaint,
    +  return RecvUpdate(Move(cset), aTransactionId, targetConfig, Move(aPlugins), isFirstPaint,
           scheduleComposite, paintSequenceNumber, isRepeatTransaction,
           aTransactionStart, nullptr);
     }
    @@ -223,6 +224,7 @@
     LayerTransactionParent::RecvUpdate(InfallibleTArray&& cset,
                                        const uint64_t& aTransactionId,
                                        const TargetConfig& targetConfig,
    +                                   PluginsArray&& aPlugins,
                                        const bool& isFirstPaint,
                                        const bool& scheduleComposite,
                                        const uint32_t& paintSequenceNumber,
    @@ -581,7 +583,8 @@
       }
     
       mShadowLayersManager->ShadowLayersUpdated(this, aTransactionId, targetConfig,
    -      isFirstPaint, scheduleComposite, paintSequenceNumber, isRepeatTransaction);
    +                                            aPlugins, isFirstPaint, scheduleComposite,
    +                                            paintSequenceNumber, isRepeatTransaction);
     
       {
         AutoResolveRefLayers resolve(mShadowLayersManager->GetCompositionManager(this));
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/LayerTransactionParent.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/LayerTransactionParent.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/LayerTransactionParent.h	2015-01-25 22:24:21.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/LayerTransactionParent.h	2015-02-03 14:34:02.000000000 +0000
    @@ -42,6 +42,7 @@
       typedef InfallibleTArray EditArray;
       typedef InfallibleTArray EditReplyArray;
       typedef InfallibleTArray AsyncChildMessageArray;
    +  typedef InfallibleTArray PluginsArray;
     
     public:
       LayerTransactionParent(LayerManagerComposite* aManager,
    @@ -106,6 +107,7 @@
       virtual bool RecvUpdate(EditArray&& cset,
                               const uint64_t& aTransactionId,
                               const TargetConfig& targetConfig,
    +                          PluginsArray&& aPlugins,
                               const bool& isFirstPaint,
                               const bool& scheduleComposite,
                               const uint32_t& paintSequenceNumber,
    @@ -116,6 +118,7 @@
       virtual bool RecvUpdateNoSwap(EditArray&& cset,
                                     const uint64_t& aTransactionId,
                                     const TargetConfig& targetConfig,
    +                                PluginsArray&& aPlugins,
                                     const bool& isFirstPaint,
                                     const bool& scheduleComposite,
                                     const uint32_t& paintSequenceNumber,
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/PCompositor.ipdl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/PCompositor.ipdl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/PCompositor.ipdl	2015-01-25 22:24:21.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/PCompositor.ipdl	2015-02-03 14:34:02.000000000 +0000
    @@ -6,6 +6,7 @@
      * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     
     include LayersSurfaces;
    +include LayersMessages;
     include protocol PLayerTransaction;
     include "mozilla/GfxMessageUtils.h";
     include "nsRegion.h";
    @@ -56,8 +57,16 @@
        */
       async RemotePaintIsReady();
     
    -parent:
    +  /**
    +   * Bounce plugin widget configurations over to the main thread for
    +   * application on the widgets. Used on Windows and Linux in managing
    +   * plugin widgets.
    +   */
    +  async UpdatePluginConfigurations(nsIntPoint aContentOffset,
    +                                   nsIntRegion aVisibleRegion,
    +                                   PluginWindowData[] aPlugins);
     
    +parent:
       // Child sends the parent a request for fill ratio numbers.
       async RequestOverfill();
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/PLayerTransaction.ipdl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/PLayerTransaction.ipdl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/PLayerTransaction.ipdl	2015-01-25 22:24:21.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/PLayerTransaction.ipdl	2015-02-03 14:34:02.000000000 +0000
    @@ -53,14 +53,16 @@
     
       // The isFirstPaint flag can be used to indicate that this is the first update
       // for a particular document.
    -  sync Update(Edit[] cset, uint64_t id, TargetConfig targetConfig, bool isFirstPaint,
    +  sync Update(Edit[] cset, uint64_t id, TargetConfig targetConfig,
    +              PluginWindowData[] plugins, bool isFirstPaint,
                   bool scheduleComposite, uint32_t paintSequenceNumber,
                   bool isRepeatTransaction, TimeStamp transactionStart)
         returns (EditReply[] reply);
     
       // We don't need to send a sync transaction if
       // no transaction operate require a swap.
    -  async UpdateNoSwap(Edit[] cset, uint64_t id, TargetConfig targetConfig, bool isFirstPaint,
    +  async UpdateNoSwap(Edit[] cset, uint64_t id, TargetConfig targetConfig,
    +                     PluginWindowData[] plugins, bool isFirstPaint,
                          bool scheduleComposite, uint32_t paintSequenceNumber,
                          bool isRepeatTransaction, TimeStamp transactionStart);
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/ShadowLayers.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/ShadowLayers.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/ShadowLayers.cpp	2015-01-25 22:24:21.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/ShadowLayers.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -538,6 +538,22 @@
       }
     }
     
    +void
    +ShadowLayerForwarder::StorePluginWidgetConfigurations(const nsTArray&
    +                                                      aConfigurations)
    +{
    +  // Cache new plugin widget configs here until we call update, at which
    +  // point this data will get shipped over to chrome.
    +  mPluginWindowData.Clear();
    +  for (uint32_t idx = 0; idx < aConfigurations.Length(); idx++) {
    +    const nsIWidget::Configuration& configuration = aConfigurations[idx];
    +    mPluginWindowData.AppendElement(PluginWindowData(configuration.mWindowID,
    +                                                     configuration.mClipRegion,
    +                                                     configuration.mBounds,
    +                                                     configuration.mVisible));
    +  }
    +}
    +
     bool
     ShadowLayerForwarder::EndTransaction(InfallibleTArray* aReplies,
                                          const nsIntRegion& aRegionToClear,
    @@ -674,10 +690,10 @@
         RenderTraceScope rendertrace3("Forward Transaction", "000093");
         if (!HasShadowManager() ||
             !mShadowManager->IPCOpen() ||
    -        !mShadowManager->SendUpdate(cset, aId, targetConfig, mIsFirstPaint,
    -                                    aScheduleComposite, aPaintSequenceNumber,
    -                                    aIsRepeatTransaction, aTransactionStart,
    -                                    aReplies)) {
    +        !mShadowManager->SendUpdate(cset, aId, targetConfig, mPluginWindowData,
    +                                    mIsFirstPaint, aScheduleComposite,
    +                                    aPaintSequenceNumber, aIsRepeatTransaction,
    +                                    aTransactionStart, aReplies)) {
           MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!"));
           return false;
         }
    @@ -688,14 +704,19 @@
         RenderTraceScope rendertrace3("Forward NoSwap Transaction", "000093");
         if (!HasShadowManager() ||
             !mShadowManager->IPCOpen() ||
    -        !mShadowManager->SendUpdateNoSwap(cset, aId, targetConfig, mIsFirstPaint,
    -                                          aScheduleComposite, aPaintSequenceNumber,
    -                                          aIsRepeatTransaction, aTransactionStart)) {
    +        !mShadowManager->SendUpdateNoSwap(cset, aId, targetConfig, mPluginWindowData,
    +                                          mIsFirstPaint, aScheduleComposite,
    +                                          aPaintSequenceNumber, aIsRepeatTransaction,
    +                                          aTransactionStart)) {
           MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!"));
           return false;
         }
       }
     
    +  // Clear any cached plugin data we might have, now that the
    +  // transaction is complete.
    +  mPluginWindowData.Clear();
    +
       *aSent = true;
       mIsFirstPaint = false;
       MOZ_LAYERS_LOG(("[LayersForwarder] ... done"));
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/ShadowLayers.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/ShadowLayers.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/ShadowLayers.h	2015-01-25 22:24:21.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/ShadowLayers.h	2015-02-03 14:34:02.000000000 +0000
    @@ -20,7 +20,8 @@
     #include "nsCOMPtr.h"                   // for already_AddRefed
     #include "nsRegion.h"                   // for nsIntRegion
     #include "nsTArrayForwardDeclare.h"     // for InfallibleTArray
    - 
    +#include "nsIWidget.h"
    +
     struct nsIntPoint;
     struct nsIntRect;
     
    @@ -313,6 +314,14 @@
        */
       void SetShadowManager(PLayerTransactionChild* aShadowManager);
     
    +  /**
    +   * Layout calls here to cache current plugin widget configuration
    +   * data. We ship this across with the rest of the layer updates when
    +   * we update. Chrome handles applying these changes.
    +   */
    +  void StorePluginWidgetConfigurations(const nsTArray&
    +                                       aConfigurations);
    +
       void StopReceiveAsyncParentMessge();
     
       void ClearCachedResources();
    @@ -407,6 +416,7 @@
       DiagnosticTypes mDiagnosticTypes;
       bool mIsFirstPaint;
       bool mWindowOverlayChanged;
    +  InfallibleTArray mPluginWindowData;
     };
     
     class CompositableClient;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/ShadowLayersManager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/ShadowLayersManager.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/ipc/ShadowLayersManager.h	2015-01-25 22:24:21.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/ipc/ShadowLayersManager.h	2015-02-03 14:34:02.000000000 +0000
    @@ -21,6 +21,7 @@
         virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
                                          const uint64_t& aTransactionId,
                                          const TargetConfig& aTargetConfig,
    +                                     const InfallibleTArray& aPlugins,
                                          bool aIsFirstPaint,
                                          bool aScheduleComposite,
                                          uint32_t aPaintSequenceNumber,
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/Layers.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/Layers.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/Layers.cpp	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/Layers.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -851,6 +851,66 @@
       return quad;
     }
     
    +bool
    +Layer::GetVisibleRegionRelativeToRootLayer(nsIntRegion& aResult,
    +                                           nsIntPoint* aLayerOffset)
    +{
    +  MOZ_ASSERT(aLayerOffset, "invalid offset pointer");
    +
    +  IntPoint offset;
    +  aResult = GetEffectiveVisibleRegion();
    +  for (Layer* layer = this; layer; layer = layer->GetParent()) {
    +    gfx::Matrix matrix;
    +    if (!layer->GetLocalTransform().Is2D(&matrix) ||
    +        !matrix.IsTranslation()) {
    +      return false;
    +    }
    +
    +    // The offset of |layer| to its parent.
    +    IntPoint currentLayerOffset = RoundedToInt(matrix.GetTranslation());
    +
    +    // Translate the accumulated visible region of |this| by the offset of
    +    // |layer|.
    +    aResult.MoveBy(currentLayerOffset.x, currentLayerOffset.y);
    +
    +    // If the parent layer clips its lower layers, clip the visible region
    +    // we're accumulating.
    +    if (layer->GetEffectiveClipRect()) {
    +      aResult.AndWith(*layer->GetEffectiveClipRect());
    +    }
    +
    +    // Now we need to walk across the list of siblings for this parent layer,
    +    // checking to see if any of these layer trees obscure |this|. If so,
    +    // remove these areas from the visible region as well. This will pick up
    +    // chrome overlays like a tab modal prompt.
    +    Layer* sibling;
    +    for (sibling = layer->GetNextSibling(); sibling;
    +         sibling = sibling->GetNextSibling()) {
    +      gfx::Matrix siblingMatrix;
    +      if (!sibling->GetLocalTransform().Is2D(&siblingMatrix) ||
    +          !siblingMatrix.IsTranslation()) {
    +        return false;
    +      }
    +
    +      // Retreive the translation from sibling to |layer|. The accumulated
    +      // visible region is currently oriented with |layer|.
    +      IntPoint siblingOffset = RoundedToInt(siblingMatrix.GetTranslation());
    +      nsIntRegion siblingVisibleRegion(sibling->GetEffectiveVisibleRegion());
    +      // Translate the siblings region to |layer|'s origin.
    +      siblingVisibleRegion.MoveBy(-siblingOffset.x, -siblingOffset.y);
    +      // Subtract the sibling visible region from the visible region of |this|.
    +      aResult.SubOut(siblingVisibleRegion);
    +    }
    +
    +    // Keep track of the total offset for aLayerOffset.  We use this in plugin
    +    // positioning code.
    +    offset += currentLayerOffset;
    +  }
    +
    +  *aLayerOffset = nsIntPoint(offset.x, offset.y);
    +  return true;
    +}
    +
     ContainerLayer::ContainerLayer(LayerManager* aManager, void* aImplData)
       : Layer(aManager, aImplData),
         mFirstChild(nullptr),
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/Layers.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/Layers.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/Layers.h	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/Layers.h	2015-02-03 14:34:02.000000000 +0000
    @@ -44,7 +44,7 @@
     #include "nsTArrayForwardDeclare.h"     // for InfallibleTArray
     #include "nscore.h"                     // for nsACString, nsAString
     #include "prlog.h"                      // for PRLogModuleInfo
    -#include "gfx2DGlue.h"
    +#include "nsIWidget.h"                  // For plugin window configuration information structs
     #include "gfxVR.h"
     
     class gfxContext;
    @@ -307,7 +307,7 @@
     
       virtual bool HasShadowManagerInternal() const { return false; }
       bool HasShadowManager() const { return HasShadowManagerInternal(); }
    -
    +  virtual void StorePluginWidgetConfigurations(const nsTArray& aConfigurations) {}
       bool IsSnappingEffectiveTransforms() { return mSnapEffectiveTransforms; }
     
     
    @@ -1251,6 +1251,24 @@
       Layer* GetMaskLayer() const { return mMaskLayer; }
     
     
    +  /**
    +   * Retrieve the root level visible region for |this| taking into account
    +   * clipping applied to parent layers of |this| as well as subtracting
    +   * visible regions of higher siblings of this layer and each ancestor.
    +   *
    +   * Note translation values for offsets of visible regions and accumulated
    +   * aLayerOffset are integer rounded using Point's RoundedToInt.
    +   *
    +   * @param aResult - the resulting visible region of this layer.
    +   * @param aLayerOffset - this layer's total offset from the root layer.
    +   * @return - false if during layer tree traversal a parent or sibling
    +   *  transform is found to be non-translational. This method returns early
    +   *  in this case, results will not be valid. Returns true on successful
    +   *  traversal.
    +   */
    +  bool GetVisibleRegionRelativeToRootLayer(nsIntRegion& aResult,
    +                                           nsIntPoint* aLayerOffset);
    +
       // Note that all lengths in animation data are either in CSS pixels or app
       // units and must be converted to device pixels by the compositor.
       AnimationArray& GetAnimations() { return mAnimations; }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/LayersTypes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/LayersTypes.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/LayersTypes.h	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/LayersTypes.h	2015-02-03 14:34:02.000000000 +0000
    @@ -10,7 +10,6 @@
     #include "nsPoint.h"                    // for nsIntPoint
     #include "nsRegion.h"
     
    -#include "mozilla/TypedEnum.h"
     #include "mozilla/TypedEnumBits.h"
     
     #ifdef MOZ_WIDGET_GONK
    @@ -46,7 +45,7 @@
     #undef NONE
     #undef OPAQUE
     
    -MOZ_BEGIN_ENUM_CLASS(LayersBackend, int8_t)
    +enum class LayersBackend : int8_t {
       LAYERS_NONE = 0,
       LAYERS_BASIC,
       LAYERS_OPENGL,
    @@ -55,29 +54,29 @@
       LAYERS_D3D11,
       LAYERS_CLIENT,
       LAYERS_LAST
    -MOZ_END_ENUM_CLASS(LayersBackend)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(BufferMode, int8_t)
    +enum class BufferMode : int8_t {
       BUFFER_NONE,
       BUFFERED
    -MOZ_END_ENUM_CLASS(BufferMode)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(DrawRegionClip, int8_t)
    +enum class DrawRegionClip : int8_t {
       DRAW,
       NONE
    -MOZ_END_ENUM_CLASS(DrawRegionClip)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(SurfaceMode, int8_t)
    +enum class SurfaceMode : int8_t {
       SURFACE_NONE = 0,
       SURFACE_OPAQUE,
       SURFACE_SINGLE_CHANNEL_ALPHA,
       SURFACE_COMPONENT_ALPHA
    -MOZ_END_ENUM_CLASS(SurfaceMode)
    +};
     
     // LayerRenderState for Composer2D
     // We currently only support Composer2D using gralloc. If we want to be backed
     // by other surfaces we will need a more generic LayerRenderState.
    -MOZ_BEGIN_ENUM_CLASS(LayerRenderStateFlags, int8_t)
    +enum class LayerRenderStateFlags : int8_t {
       LAYER_RENDER_STATE_DEFAULT = 0,
       ORIGIN_BOTTOM_LEFT = 1 << 0,
       BUFFER_ROTATION = 1 << 1,
    @@ -87,7 +86,7 @@
       // render. This avoids confusion when a layer might return different kinds
       // of surfaces over time (e.g. video frames).
       OPAQUE = 1 << 3
    -MOZ_END_ENUM_CLASS(LayerRenderStateFlags)
    +};
     MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(LayerRenderStateFlags)
     
     // The 'ifdef MOZ_WIDGET_GONK' sadness here is because we don't want to include
    @@ -151,12 +150,12 @@
     #endif
     };
     
    -MOZ_BEGIN_ENUM_CLASS(ScaleMode, int8_t)
    +enum class ScaleMode : int8_t {
       SCALE_NONE,
       STRETCH,
       SENTINEL
     // Unimplemented - PRESERVE_ASPECT_RATIO_CONTAIN
    -MOZ_END_ENUM_CLASS(ScaleMode)
    +};
     
     struct EventRegions {
       nsIntRegion mHitRegion;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/opengl/GrallocTextureClient.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/opengl/GrallocTextureClient.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/opengl/GrallocTextureClient.cpp	2015-01-25 22:24:21.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/opengl/GrallocTextureClient.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -88,13 +88,17 @@
     }
     
     void
    -GrallocTextureClientOGL::WaitForBufferOwnership()
    +GrallocTextureClientOGL::WaitForBufferOwnership(bool aWaitReleaseFence)
     {
       if (mRemoveFromCompositableTracker) {
         mRemoveFromCompositableTracker->WaitComplete();
         mRemoveFromCompositableTracker = nullptr;
       }
     
    +  if (!aWaitReleaseFence) {
    +    return;
    +  }
    +
     #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
        if (mReleaseFenceHandle.IsValid()) {
          android::sp fence = mReleaseFenceHandle.mFence;
    @@ -122,7 +126,11 @@
         return true;
       }
     
    +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
    +  WaitForBufferOwnership(false /* aWaitReleaseFence */);
    +#else
       WaitForBufferOwnership();
    +#endif
     
       uint32_t usage = 0;
       if (aMode & OpenMode::OPEN_READ) {
    @@ -131,7 +139,19 @@
       if (aMode & OpenMode::OPEN_WRITE) {
         usage |= GRALLOC_USAGE_SW_WRITE_OFTEN;
       }
    -  int32_t rv = mGraphicBuffer->lock(usage, reinterpret_cast(&mMappedBuffer));
    +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
    +  android::sp fence = android::Fence::NO_FENCE;
    +  if (mReleaseFenceHandle.IsValid()) {
    +    fence = mReleaseFenceHandle.mFence;
    +  }
    +  mReleaseFenceHandle = FenceHandle();
    +  int32_t rv = mGraphicBuffer->lockAsync(usage,
    +                                         reinterpret_cast(&mMappedBuffer),
    +                                         fence->dup());
    +#else
    +  int32_t rv = mGraphicBuffer->lock(usage,
    +                                    reinterpret_cast(&mMappedBuffer));
    +#endif
       if (rv) {
         mMappedBuffer = nullptr;
         NS_WARNING("Couldn't lock graphic buffer");
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/opengl/GrallocTextureClient.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/opengl/GrallocTextureClient.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/opengl/GrallocTextureClient.h	2015-01-25 22:24:21.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/opengl/GrallocTextureClient.h	2015-02-03 14:34:02.000000000 +0000
    @@ -58,7 +58,7 @@
     
       virtual void SetRemoveFromCompositableTracker(AsyncTransactionTracker* aTracker) MOZ_OVERRIDE;
     
    -  virtual void WaitForBufferOwnership() MOZ_OVERRIDE;
    +  virtual void WaitForBufferOwnership(bool aWaitReleaseFence = true) MOZ_OVERRIDE;
     
       void InitWith(MaybeMagicGrallocBufferHandle aDesc, gfx::IntSize aSize);
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp	2015-01-25 22:24:21.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -27,7 +27,19 @@
       }
     
       if (!mTextureSource) {
    -    mTextureSource = new MacIOSurfaceTextureSourceOGL(mCompositor, mSurface);
    +    GLuint textureHandle;
    +    gl::GLContext* gl = mCompositor->gl();
    +    gl->fGenTextures(1, &textureHandle);
    +    gl->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, textureHandle);
    +    gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
    +    gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
    +    mSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(gl)->GetCGLContext());
    +
    +    mTextureSource = new GLTextureSource(mCompositor, textureHandle, LOCAL_GL_TEXTURE_RECTANGLE_ARB,
    +                                         gfx::IntSize(mSurface->GetDevicePixelWidth(),
    +                                                      mSurface->GetDevicePixelHeight()),
    +                                         mSurface->HasAlpha() ? gfx::SurfaceFormat::R8G8B8A8:
    +                                                                gfx::SurfaceFormat::R8G8B8X8);
       }
       return true;
     }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h	2015-01-25 22:24:21.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h	2015-02-03 14:34:02.000000000 +0000
    @@ -94,7 +94,7 @@
     
     protected:
       RefPtr mCompositor;
    -  RefPtr mTextureSource;
    +  RefPtr mTextureSource;
       RefPtr mSurface;
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/RotatedBuffer.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/RotatedBuffer.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/layers/RotatedBuffer.cpp	2015-01-25 22:24:43.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/layers/RotatedBuffer.cpp	2015-02-03 14:34:02.000000000 +0000
    @@ -26,6 +26,7 @@
     #include "mozilla/layers/TextureClient.h"  // for TextureClient
     #include "nsSize.h"                     // for nsIntSize
     #include "gfx2DGlue.h"
    +#include "nsLayoutUtils.h"              // for invalidation debugging
     
     namespace mozilla {
     
    @@ -446,13 +447,14 @@
     
       SurfaceMode mode;
       nsIntRegion neededRegion;
    -  bool canReuseBuffer;
       nsIntRect destBufferRect;
     
    +  bool canReuseBuffer = HaveBuffer();
    +
       while (true) {
         mode = aLayer->GetSurfaceMode();
         neededRegion = aLayer->GetVisibleRegion();
    -    canReuseBuffer = HaveBuffer() && BufferSizeOkFor(neededRegion.GetBounds().Size());
    +    canReuseBuffer &= BufferSizeOkFor(neededRegion.GetBounds().Size());
         result.mContentType = layerContentType;
     
         if (canReuseBuffer) {
    @@ -488,37 +490,56 @@
     
         if ((aFlags & PAINT_WILL_RESAMPLE) &&
             (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) ||
    -         neededRegion.GetNumRects() > 1)) {
    -      // The area we add to neededRegion might not be painted opaquely
    +         neededRegion.GetNumRects() > 1))
    +    {
    +      // The area we add to neededRegion might not be painted opaquely.
           if (mode == SurfaceMode::SURFACE_OPAQUE) {
             result.mContentType = gfxContentType::COLOR_ALPHA;
             mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
           }
     
           // We need to validate the entire buffer, to make sure that only valid
    -      // pixels are sampled
    +      // pixels are sampled.
           neededRegion = destBufferRect;
         }
     
         // If we have an existing buffer, but the content type has changed or we
         // have transitioned into/out of component alpha, then we need to recreate it.
    -    if (HaveBuffer() &&
    +    if (canReuseBuffer &&
             (result.mContentType != BufferContentType() ||
    -        (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite())) {
    -
    -      // We're effectively clearing the valid region, so we need to draw
    -      // the entire needed region now.
    -      result.mRegionToInvalidate = aLayer->GetValidRegion();
    -      validRegion.SetEmpty();
    -      Clear();
    -      // Restart decision process with the cleared buffer. We can only go
    -      // around the loop one more iteration, since mDTBuffer is null now.
    +        (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite()))
    +    {
    +      // Restart the decision process; we won't re-enter since we guard on
    +      // being able to re-use the buffer.
    +      canReuseBuffer = false;
           continue;
         }
     
         break;
       }
     
    +  if (HaveBuffer() &&
    +      (result.mContentType != BufferContentType() ||
    +      (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite()))
    +  {
    +    // We're effectively clearing the valid region, so we need to draw
    +    // the entire needed region now.
    +    canReuseBuffer = false;
    +    result.mRegionToInvalidate = aLayer->GetValidRegion();
    +    validRegion.SetEmpty();
    +    Clear();
    +
    +#if defined(MOZ_DUMP_PAINTING)
    +    if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
    +      if (result.mContentType != BufferContentType()) {
    +        printf_stderr("Invalidating entire rotated buffer (layer %p): content type changed\n", aLayer);
    +      } else if ((mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite()) {
    +        printf_stderr("Invalidating entire rotated buffer (layer %p): component alpha changed\n", aLayer);
    +      }
    +    }
    +#endif
    +  }
    +
       NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()),
                    "Destination rect doesn't contain what we need to paint");
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/src/FilterSupport.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/src/FilterSupport.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/src/FilterSupport.h	2015-01-25 22:24:22.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/src/FilterSupport.h	2015-02-03 14:33:12.000000000 +0000
    @@ -8,7 +8,6 @@
     
     #include "mozilla/Attributes.h"
     #include "mozilla/RefPtr.h"
    -#include "mozilla/TypedEnum.h"
     #include "mozilla/gfx/Rect.h"
     #include "mozilla/gfx/Matrix.h"
     #include "mozilla/gfx/2D.h"
    @@ -162,7 +161,7 @@
     class FilterNode;
     struct FilterAttribute;
     
    -MOZ_BEGIN_ENUM_CLASS(AttributeType)
    +enum class AttributeType {
       eBool,
       eUint,
       eFloat,
    @@ -176,7 +175,7 @@
       eAttributeMap,
       eFloats,
       Max
    -MOZ_END_ENUM_CLASS(AttributeType)
    +};
     
     // Limits
     const float kMaxStdDeviation = 500;
    @@ -231,16 +230,16 @@
       mutable nsClassHashtable  mMap;
     };
     
    -MOZ_BEGIN_ENUM_CLASS(ColorSpace)
    +enum class ColorSpace {
       SRGB,
       LinearRGB,
       Max
    -MOZ_END_ENUM_CLASS(ColorSpace)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(AlphaModel)
    +enum class AlphaModel {
       Unpremultiplied,
       Premultiplied
    -MOZ_END_ENUM_CLASS(AlphaModel)
    +};
     
     class ColorModel {
     public:
    @@ -268,7 +267,7 @@
       AlphaModel mAlphaModel;
     };
     
    -MOZ_BEGIN_ENUM_CLASS(PrimitiveType)
    +enum class PrimitiveType {
       Empty = 0,
       Blend,
       Morphology,
    @@ -289,7 +288,7 @@
       SpecularLighting,
       ToAlpha,
       Max
    -MOZ_END_ENUM_CLASS(PrimitiveType)
    +};
     
     /**
      * A data structure to carry attributes for a given primitive that's part of a
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/src/nsRegion.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/src/nsRegion.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/src/nsRegion.h	2015-01-25 22:24:22.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/src/nsRegion.h	2015-02-03 14:33:12.000000000 +0000
    @@ -18,7 +18,6 @@
     #include "nsMargin.h"                   // for nsIntMargin
     #include "nsStringGlue.h"               // for nsCString
     #include "xpcom-config.h"               // for CPP_THROW_NEW
    -#include "mozilla/TypedEnum.h"          // for the VisitEdges typed enum
     #include "mozilla/Move.h"               // for mozilla::Move
     
     class nsIntRegion;
    @@ -40,12 +39,12 @@
      * projects including Qt, Gtk, Wine. It should perform reasonably well.
      */
     
    -MOZ_BEGIN_ENUM_CLASS(VisitSide)
    +enum class VisitSide {
     	TOP,
     	BOTTOM,
     	LEFT,
     	RIGHT
    -MOZ_END_ENUM_CLASS(VisitSide)
    +};
     
     class nsRegionRectIterator;
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/DrawMode.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/DrawMode.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/DrawMode.h	2015-01-25 22:24:22.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/DrawMode.h	2015-02-03 14:33:13.000000000 +0000
    @@ -6,10 +6,8 @@
     #ifndef DrawMode_h
     #define DrawMode_h
     
    -#include "mozilla/TypedEnum.h"
    -
     // Options for how the text should be drawn
    -MOZ_BEGIN_ENUM_CLASS(DrawMode, int)
    +enum class DrawMode : int {
       // GLYPH_FILL and GLYPH_STROKE draw into the current context
       //  and may be used together with bitwise OR.
       GLYPH_FILL = 1,
    @@ -21,7 +19,7 @@
       // When GLYPH_FILL and GLYPH_STROKE are both set, draws the
       //  stroke underneath the fill.
       GLYPH_STROKE_UNDERNEATH = 8
    -MOZ_END_ENUM_CLASS(DrawMode)
    +};
     
     #endif
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxFont.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxFont.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxFont.h	2015-01-25 22:24:22.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxFont.h	2015-02-03 14:33:13.000000000 +0000
    @@ -1406,6 +1406,16 @@
             return mFontEntry->HasGraphiteTables();
         }
     
    +    // Whether this is a font that may be doing full-color rendering,
    +    // and therefore needs us to use a mask for text-shadow even when
    +    // we're not actually blurring.
    +    bool AlwaysNeedsMaskForShadow() {
    +        return mFontEntry->TryGetColorGlyphs() ||
    +               mFontEntry->TryGetSVGData(this) ||
    +               mFontEntry->HasFontTable(TRUETYPE_TAG('C','B','D','T')) ||
    +               mFontEntry->HasFontTable(TRUETYPE_TAG('s','b','i','x'));
    +    }
    +
         // whether a feature is supported by the font (limited to a small set
         // of features for which some form of fallback needs to be implemented)
         bool SupportsFeature(int32_t aScript, uint32_t aFeatureTag);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxFT2FontList.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxFT2FontList.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxFT2FontList.cpp	2015-01-25 22:24:22.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxFT2FontList.cpp	2015-02-03 14:33:13.000000000 +0000
    @@ -715,13 +715,10 @@
             if (!mMap.IsInitialized()) {
                 return;
             }
    -        PLDHashEntryHdr *hdr =
    -            PL_DHashTableLookup(&mMap, aFileName.get());
    -        if (!hdr) {
    -            return;
    -        }
    -        FNCMapEntry* entry = static_cast(hdr);
    -        if (entry && entry->mFilesize) {
    +        FNCMapEntry *entry =
    +            static_cast(PL_DHashTableSearch(&mMap,
    +                                                          aFileName.get()));
    +        if (entry) {
                 *aTimestamp = entry->mTimestamp;
                 *aFilesize = entry->mFilesize;
                 aFaceList.Assign(entry->mFaces);
    @@ -1099,7 +1096,6 @@
         uint32_t bufSize = item->RealSize();
         // We use fallible allocation here; if there's not enough RAM, we'll simply
         // ignore the bundled fonts and fall back to the device's installed fonts.
    -    static const fallible_t fallible = fallible_t();
         nsAutoArrayPtr buf(new (fallible) uint8_t[bufSize]);
         if (!buf) {
             return;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxGradientCache.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxGradientCache.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxGradientCache.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxGradientCache.cpp	2015-02-03 14:33:13.000000000 +0000
    @@ -185,7 +185,15 @@
       }
       GradientCacheData* cached =
         gGradientCache->Lookup(aStops, aExtend, aDT->GetBackendType());
    -  return cached ? cached->mStops : nullptr;
    +  if (cached && cached->mStops) {
    +    if (!cached->mStops->IsValid()) {
    +      gGradientCache->NotifyExpired(cached);
    +    } else {
    +      return cached->mStops;
    +    }
    +  }
    +
    +  return nullptr;
     }
     
     GradientStops *
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxHarfBuzzShaper.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxHarfBuzzShaper.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxHarfBuzzShaper.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxHarfBuzzShaper.cpp	2015-02-03 14:33:13.000000000 +0000
    @@ -53,6 +53,7 @@
           mUseFontGlyphWidths(false),
           mInitialized(false),
           mVerticalInitialized(false),
    +      mLoadedLocaGlyf(false),
           mLocaLongOffsets(false)
     {
     }
    @@ -346,54 +347,13 @@
         }
     
         if (mVmtxTable) {
    -        if (mLocaTable && mGlyfTable) {
    -            // TrueType outlines: use glyph bbox + top sidebearing
    -            uint32_t offset; // offset of glyph record in the 'glyf' table
    -            uint32_t len;
    -            const char* data = hb_blob_get_data(mLocaTable, &len);
    -            if (mLocaLongOffsets) {
    -                if ((aGlyph + 1) * sizeof(AutoSwap_PRUint32) > len) {
    -                    *aY = 0;
    -                    return;
    -                }
    -                const AutoSwap_PRUint32* offsets =
    -                    reinterpret_cast(data);
    -                offset = offsets[aGlyph];
    -                if (offset == offsets[aGlyph + 1]) {
    -                    // empty glyph
    -                    *aY = 0;
    -                    return;
    -                }
    -            } else {
    -                if ((aGlyph + 1) * sizeof(AutoSwap_PRUint16) > len) {
    -                    *aY = 0;
    -                    return;
    -                }
    -                const AutoSwap_PRUint16* offsets =
    -                    reinterpret_cast(data);
    -                offset = uint16_t(offsets[aGlyph]);
    -                if (offset == uint16_t(offsets[aGlyph + 1])) {
    -                    // empty glyph
    -                    *aY = 0;
    -                    return;
    -                }
    -                offset *= 2;
    -            }
    -
    -            struct Glyf { // we only need the bounding-box at the beginning
    -                          // of the glyph record, not the actual outline data
    -                AutoSwap_PRInt16 numberOfContours;
    -                AutoSwap_PRInt16 xMin;
    -                AutoSwap_PRInt16 yMin;
    -                AutoSwap_PRInt16 xMax;
    -                AutoSwap_PRInt16 yMax;
    -            };
    -            data = hb_blob_get_data(mGlyfTable, &len);
    -            if (offset + sizeof(Glyf) > len) {
    +        bool emptyGlyf;
    +        const Glyf *glyf = FindGlyf(aGlyph, &emptyGlyf);
    +        if (glyf) {
    +            if (emptyGlyf) {
                     *aY = 0;
                     return;
                 }
    -            const Glyf* glyf = reinterpret_cast(data + offset);
     
                 if (aGlyph >= uint32_t(mNumLongVMetrics)) {
                     aGlyph = mNumLongVMetrics - 1;
    @@ -406,7 +366,8 @@
                                      int16_t(glyf->yMax)));
                 return;
             } else {
    -            // XXX TODO: CFF outlines - need to get glyph extents.
    +            // XXX TODO: not a truetype font; need to get glyph extents
    +            // via some other API?
                 // For now, fall through to default code below.
             }
         }
    @@ -432,6 +393,112 @@
     }
     
     static hb_bool_t
    +HBGetGlyphExtents(hb_font_t *font, void *font_data,
    +                  hb_codepoint_t glyph,
    +                  hb_glyph_extents_t *extents,
    +                  void *user_data)
    +{
    +    const gfxHarfBuzzShaper::FontCallbackData *fcd =
    +        static_cast(font_data);
    +    return fcd->mShaper->GetGlyphExtents(glyph, extents);
    +}
    +
    +// Find the data for glyph ID |aGlyph| in the 'glyf' table, if present.
    +// Returns null if not found, otherwise pointer to the beginning of the
    +// glyph's data. Sets aEmptyGlyf true if there is no actual data;
    +// otherwise, it's guaranteed that we can read at least the bounding box.
    +const gfxHarfBuzzShaper::Glyf*
    +gfxHarfBuzzShaper::FindGlyf(hb_codepoint_t aGlyph, bool *aEmptyGlyf) const
    +{
    +    if (!mLoadedLocaGlyf) {
    +        mLoadedLocaGlyf = true; // only try this once; if it fails, this
    +                                // isn't a truetype font
    +        gfxFontEntry *entry = mFont->GetFontEntry();
    +        uint32_t len;
    +        gfxFontEntry::AutoTable headTable(entry,
    +                                          TRUETYPE_TAG('h','e','a','d'));
    +        const HeadTable* head =
    +            reinterpret_cast(hb_blob_get_data(headTable,
    +                                                                &len));
    +        if (len < sizeof(HeadTable)) {
    +            return nullptr;
    +        }
    +        mLocaLongOffsets = int16_t(head->indexToLocFormat) > 0;
    +        mLocaTable = entry->GetFontTable(TRUETYPE_TAG('l','o','c','a'));
    +        mGlyfTable = entry->GetFontTable(TRUETYPE_TAG('g','l','y','f'));
    +    }
    +
    +    if (!mLocaTable || !mGlyfTable) {
    +        // it's not a truetype font
    +        return nullptr;
    +    }
    +
    +    uint32_t offset; // offset of glyph record in the 'glyf' table
    +    uint32_t len;
    +    const char* data = hb_blob_get_data(mLocaTable, &len);
    +    if (mLocaLongOffsets) {
    +        if ((aGlyph + 1) * sizeof(AutoSwap_PRUint32) > len) {
    +            return nullptr;
    +        }
    +        const AutoSwap_PRUint32* offsets =
    +            reinterpret_cast(data);
    +        offset = offsets[aGlyph];
    +        *aEmptyGlyf = (offset == uint16_t(offsets[aGlyph + 1]));
    +    } else {
    +        if ((aGlyph + 1) * sizeof(AutoSwap_PRUint16) > len) {
    +            return nullptr;
    +        }
    +        const AutoSwap_PRUint16* offsets =
    +            reinterpret_cast(data);
    +        offset = uint16_t(offsets[aGlyph]);
    +        *aEmptyGlyf = (offset == uint16_t(offsets[aGlyph + 1]));
    +        offset *= 2;
    +    }
    +
    +    data = hb_blob_get_data(mGlyfTable, &len);
    +    if (offset + sizeof(Glyf) > len) {
    +        return nullptr;
    +    }
    +
    +    return reinterpret_cast(data + offset);
    +}
    +
    +hb_bool_t
    +gfxHarfBuzzShaper::GetGlyphExtents(hb_codepoint_t aGlyph,
    +                                   hb_glyph_extents_t *aExtents) const
    +{
    +    bool emptyGlyf;
    +    const Glyf *glyf = FindGlyf(aGlyph, &emptyGlyf);
    +    if (!glyf) {
    +        // TODO: for non-truetype fonts, get extents some other way?
    +        return false;
    +    }
    +
    +    if (emptyGlyf) {
    +        aExtents->x_bearing = 0;
    +        aExtents->y_bearing = 0;
    +        aExtents->width = 0;
    +        aExtents->height = 0;
    +        return true;
    +    }
    +
    +    double f = mFont->FUnitsToDevUnitsFactor();
    +    aExtents->x_bearing = FloatToFixed(int16_t(glyf->xMin) * f);
    +    aExtents->width =
    +        FloatToFixed((int16_t(glyf->xMax) - int16_t(glyf->xMin)) * f);
    +
    +    // Our y-coordinates are positive-downwards, whereas harfbuzz assumes
    +    // positive-upwards; hence the apparently-reversed subtractions here.
    +    aExtents->y_bearing =
    +        FloatToFixed(int16_t(glyf->yMax) * f -
    +                     mFont->GetHorizontalMetrics().emAscent);
    +    aExtents->height =
    +        FloatToFixed((int16_t(glyf->yMin) - int16_t(glyf->yMax)) * f);
    +
    +    return true;
    +}
    +
    +static hb_bool_t
     HBGetContourPoint(hb_font_t *font, void *font_data,
                       unsigned int point_index, hb_codepoint_t glyph,
                       hb_position_t *x, hb_position_t *y,
    @@ -1066,6 +1133,9 @@
             hb_font_funcs_set_glyph_v_origin_func(sHBFontFuncs,
                                                   HBGetGlyphVOrigin,
                                                   nullptr, nullptr);
    +        hb_font_funcs_set_glyph_extents_func(sHBFontFuncs,
    +                                             HBGetGlyphExtents,
    +                                             nullptr, nullptr);
             hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs,
                                                        HBGetContourPoint,
                                                        nullptr, nullptr);
    @@ -1225,20 +1295,6 @@
                     mVORGTable = nullptr;
                 }
             }
    -    } else if (mVmtxTable) {
    -        // Otherwise, try to load loca and glyf tables so that we can read
    -        // bounding boxes (needed to support vertical glyph origin).
    -        uint32_t len;
    -        gfxFontEntry::AutoTable headTable(entry,
    -                                          TRUETYPE_TAG('h','e','a','d'));
    -        const HeadTable* head =
    -            reinterpret_cast(hb_blob_get_data(headTable,
    -                                                                &len));
    -        if (len >= sizeof(HeadTable)) {
    -            mLocaLongOffsets = int16_t(head->indexToLocFormat) > 0;
    -            mLocaTable = entry->GetFontTable(TRUETYPE_TAG('l','o','c','a'));
    -            mGlyfTable = entry->GetFontTable(TRUETYPE_TAG('g','l','y','f'));
    -        }
         }
     
         return true;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxHarfBuzzShaper.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxHarfBuzzShaper.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxHarfBuzzShaper.h	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxHarfBuzzShaper.h	2015-02-03 14:33:13.000000000 +0000
    @@ -73,6 +73,9 @@
         hb_position_t GetHKerning(uint16_t aFirstGlyph,
                                   uint16_t aSecondGlyph) const;
     
    +    hb_bool_t GetGlyphExtents(hb_codepoint_t aGlyph,
    +                              hb_glyph_extents_t *aExtents) const;
    +
         static hb_script_t
         GetHBScriptUsedForShaping(int32_t aScript) {
             // Decide what harfbuzz script code will be used for shaping
    @@ -107,6 +110,17 @@
         bool InitializeVertical();
         bool LoadHmtxTable();
     
    +    struct Glyf { // we only need the bounding-box at the beginning
    +                  // of the glyph record, not the actual outline data
    +        AutoSwap_PRInt16 numberOfContours;
    +        AutoSwap_PRInt16 xMin;
    +        AutoSwap_PRInt16 yMin;
    +        AutoSwap_PRInt16 xMax;
    +        AutoSwap_PRInt16 yMax;
    +    };
    +
    +    const Glyf *FindGlyf(hb_codepoint_t aGlyph, bool *aEmptyGlyf) const;
    +
         // harfbuzz face object: we acquire a reference from the font entry
         // on shaper creation, and release it in our destructor
         hb_face_t         *mHBFace;
    @@ -161,7 +175,10 @@
     
         bool mInitialized;
         bool mVerticalInitialized;
    -    bool mLocaLongOffsets;
    +
    +    // these are set from the FindGlyf callback on first use of the glyf data
    +    mutable bool mLoadedLocaGlyf;
    +    mutable bool mLocaLongOffsets;
     };
     
     #endif /* GFX_HARFBUZZSHAPER_H */
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxPangoFonts.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxPangoFonts.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxPangoFonts.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxPangoFonts.cpp	2015-02-03 14:33:13.000000000 +0000
    @@ -205,8 +205,8 @@
             cairo_font_face_destroy(mFontFace);
         }
     
    -    virtual void ForgetHBFace();
    -    virtual void ReleaseGrFace(gr_face* aFace);
    +    virtual void ForgetHBFace() MOZ_OVERRIDE;
    +    virtual void ReleaseGrFace(gr_face* aFace) MOZ_OVERRIDE;
     
     protected:
         virtual nsresult
    @@ -671,12 +671,12 @@
     
         // return a cloned font resized and offset to simulate sub/superscript glyphs
         virtual already_AddRefed
    -    GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel);
    +    GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel) MOZ_OVERRIDE;
     
     protected:
         virtual already_AddRefed MakeScaledFont(gfxFontStyle *aFontStyle,
                                                          gfxFloat aFontScale);
    -    virtual already_AddRefed GetSmallCapsFont();
    +    virtual already_AddRefed GetSmallCapsFont() MOZ_OVERRIDE;
     
     private:
         gfxFcFont(cairo_scaled_font_t *aCairoFont, gfxFcFontEntry *aFontEntry,
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxPlatform.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxPlatform.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxPlatform.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxPlatform.cpp	2015-02-03 14:33:13.000000000 +0000
    @@ -169,7 +169,7 @@
       explicit CrashStatsLogForwarder(const char* aKey);
       virtual void Log(const std::string& aString) MOZ_OVERRIDE;
     
    -  virtual std::vector > StringsVectorCopy();
    +  virtual std::vector > StringsVectorCopy() MOZ_OVERRIDE;
     
       void SetCircularBufferSize(uint32_t aCapacity);
     
    @@ -2164,6 +2164,7 @@
      */
     static bool sLayersSupportsD3D9 = false;
     static bool sLayersSupportsD3D11 = false;
    +static bool sLayersSupportsDXVA = false;
     static bool sBufferRotationCheckPref = true;
     static bool sPrefBrowserTabsRemoteAutostart = false;
     
    @@ -2205,6 +2206,11 @@
               // Always support D3D11 when WARP is allowed.
               sLayersSupportsD3D11 = true;
             }
    +        if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DXVA, &status))) {
    +          if (status == nsIGfxInfo::FEATURE_STATUS_OK) {
    +            sLayersSupportsDXVA = true;
    +          }
    +        }
           }
         }
     #endif
    @@ -2232,6 +2238,15 @@
     }
     
     bool
    +gfxPlatform::CanUseDXVA()
    +{
    +  // this function is called from the compositor thread, so it is not
    +  // safe to init the prefs etc. from here.
    +  MOZ_ASSERT(sLayersAccelerationPrefsInitialized);
    +  return sLayersSupportsDXVA;
    +}
    +
    +bool
     gfxPlatform::BufferRotationEnabled()
     {
       MutexAutoLock autoLock(*gGfxPlatformPrefsLock);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxPlatform.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxPlatform.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxPlatform.h	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxPlatform.h	2015-02-03 14:33:13.000000000 +0000
    @@ -481,6 +481,7 @@
     
         static bool CanUseDirect3D9();
         static bool CanUseDirect3D11();
    +    static bool CanUseDXVA();
     
         /**
          * Is it possible to use buffer rotation.  Note that these
    @@ -673,6 +674,8 @@
         // Hardware vsync source. Only valid on parent process
         nsRefPtr mVsyncSource;
     
    +    mozilla::RefPtr mScreenReferenceDrawTarget;
    +
     private:
         /**
          * Start up Thebes.
    @@ -688,7 +691,6 @@
         virtual void GetPlatformCMSOutputProfile(void *&mem, size_t &size);
     
         nsRefPtr mScreenReferenceSurface;
    -    mozilla::RefPtr mScreenReferenceDrawTarget;
         nsTArray mCJKPrefLangs;
         nsCOMPtr mSRGBOverrideObserver;
         nsCOMPtr mFontPrefsObserver;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxPlatformMac.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxPlatformMac.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxPlatformMac.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxPlatformMac.cpp	2015-02-03 14:33:13.000000000 +0000
    @@ -81,6 +81,18 @@
         uint32_t contentMask = BackendTypeBit(BackendType::COREGRAPHICS);
         InitBackendPrefs(canvasMask, BackendType::COREGRAPHICS,
                          contentMask, BackendType::COREGRAPHICS);
    +
    +    // XXX: Bug 1036682 - we run out of fds on Mac when using tiled layers because
    +    // with 256x256 tiles we can easily hit the soft limit of 800 when using double
    +    // buffered tiles in e10s, so let's bump the soft limit to the hard limit for the OS
    +    // up to a new cap of 16k
    +    struct rlimit limits;
    +    if (getrlimit(RLIMIT_NOFILE, &limits) == 0) {
    +        limits.rlim_cur = std::min(rlim_t(16384), limits.rlim_max);
    +        if (setrlimit(RLIMIT_NOFILE, &limits) != 0) {
    +            NS_WARNING("Unable to bump RLIMIT_NOFILE to the maximum number on this OS");
    +        }
    +    }
     }
     
     gfxPlatformMac::~gfxPlatformMac()
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxPrefs.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxPrefs.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxPrefs.h	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxPrefs.h	2015-02-03 14:33:13.000000000 +0000
    @@ -9,7 +9,6 @@
     #include 
     #include "mozilla/Assertions.h"
     #include "mozilla/Constants.h"   // for M_PI
    -#include "mozilla/TypedEnum.h"
     
     // First time gfxPrefs::GetSingleton() needs to be called on the main thread,
     // before any of the methods accessing the values are used, but after
    @@ -78,14 +77,14 @@
     
     private:
       // Enums for the update policy.
    -  MOZ_BEGIN_NESTED_ENUM_CLASS(UpdatePolicy)
    +  enum class UpdatePolicy {
         Skip, // Set the value to default, skip any Preferences calls
         Once, // Evaluate the preference once, unchanged during the session
         Live  // Evaluate the preference and set callback so it stays current/live
    -  MOZ_END_NESTED_ENUM_CLASS(UpdatePolicy)
    +  };
     
       // Since we cannot use const char*, use a function that returns it.
    -  template 
    +  template 
       class PrefTemplate
       {
       public:
    @@ -247,6 +246,7 @@
       DECL_GFX_PREF(Once, "image.mem.surfacecache.size_factor",    ImageMemSurfaceCacheSizeFactor, uint32_t, 64);
       DECL_GFX_PREF(Live, "image.mozsamplesize.enabled",           ImageMozSampleSizeEnabled, bool, false);
       DECL_GFX_PREF(Once, "image.multithreaded_decoding.limit",    ImageMTDecodingLimit, int32_t, -1);
    +  DECL_GFX_PREF(Live, "image.single-color-optimization.enabled", ImageSingleColorOptimizationEnabled, bool, true);
     
       DECL_GFX_PREF(Once, "layers.acceleration.disabled",          LayersAccelerationDisabled, bool, false);
       DECL_GFX_PREF(Live, "layers.acceleration.draw-fps",          LayersDrawFPS, bool, false);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxTypes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxTypes.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxTypes.h	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxTypes.h	2015-02-03 14:33:14.000000000 +0000
    @@ -7,7 +7,6 @@
     #define GFX_TYPES_H
     
     #include 
    -#include "mozilla/TypedEnum.h"
     
     typedef struct _cairo_surface cairo_surface_t;
     typedef struct _cairo_user_data_key cairo_user_data_key_t;
    @@ -39,26 +38,26 @@
      * @see gfxTextRun::BreakAndMeasureText
      * @see nsLineLayout::NotifyOptionalBreakPosition
      */
    -MOZ_BEGIN_ENUM_CLASS(gfxBreakPriority)
    +enum class gfxBreakPriority {
       eNoBreak       = 0,
       eWordWrapBreak,
       eNormalBreak
    -MOZ_END_ENUM_CLASS(gfxBreakPriority)
    +};
     
     /**
       * The format for an image surface. For all formats with alpha data, 0
       * means transparent, 1 or 255 means fully opaque.
       */
    -MOZ_BEGIN_ENUM_CLASS(gfxImageFormat)
    +enum class gfxImageFormat {
       ARGB32, ///< ARGB data in native endianness, using premultiplied alpha
       RGB24,  ///< xRGB data in native endianness
       A8,     ///< Only an alpha channel
       A1,     ///< Packed transparency information (one byte refers to 8 pixels)
       RGB16_565,  ///< RGB_565 data in native endianness
       Unknown
    -MOZ_END_ENUM_CLASS(gfxImageFormat)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(gfxSurfaceType)
    +enum class gfxSurfaceType {
       Image,
       PDF,
       PS,
    @@ -85,24 +84,24 @@
       Subsurface,
       D2D,
       Max
    -MOZ_END_ENUM_CLASS(gfxSurfaceType)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(gfxContentType)
    +enum class gfxContentType {
       COLOR       = 0x1000,
       ALPHA       = 0x2000,
       COLOR_ALPHA = 0x3000,
       SENTINEL    = 0xffff
    -MOZ_END_ENUM_CLASS(gfxContentType)
    +};
     
     /**
       * The memory used by a gfxASurface (as reported by KnownMemoryUsed()) can
       * either live in this process's heap, in this process but outside the
       * heap, or in another process altogether.
       */
    -MOZ_BEGIN_ENUM_CLASS(gfxMemoryLocation)
    +enum class gfxMemoryLocation {
       IN_PROCESS_HEAP,
       IN_PROCESS_NONHEAP,
       OUT_OF_PROCESS
    -MOZ_END_ENUM_CLASS(gfxMemoryLocation)
    +};
     
     #endif /* GFX_TYPES_H */
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxUserFontSet.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxUserFontSet.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxUserFontSet.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxUserFontSet.cpp	2015-02-03 14:33:14.000000000 +0000
    @@ -650,9 +650,10 @@
             if (LOG_ENABLED()) {
                 nsAutoCString fontURI;
                 mSrcList[mSrcIndex].mURI->GetSpec(fontURI);
    -            LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) gen: %8.8x\n",
    +            LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) (%p) gen: %8.8x\n",
                      mFontSet, mSrcIndex, fontURI.get(),
                      NS_ConvertUTF16toUTF8(mFamilyName).get(),
    +                 this,
                      uint32_t(mFontSet->mGeneration)));
             }
     #endif
    @@ -786,17 +787,6 @@
                                       aItalicStyle, aFeatureSettings,
                                       aLanguageOverride, aUnicodeRanges);
           entry->mFamilyName = aFamilyName;
    -
    -#ifdef PR_LOGGING
    -      if (LOG_ENABLED()) {
    -          LOG(("userfonts (%p) created \"%s\" (%p) with style: %s weight: %d "
    -               "stretch: %d",
    -               this, NS_ConvertUTF16toUTF8(aFamilyName).get(), entry.get(),
    -               (aItalicStyle & NS_FONT_STYLE_ITALIC ? "italic" :
    -                   (aItalicStyle & NS_FONT_STYLE_OBLIQUE ? "oblique" : "normal")),
    -               aWeight, aStretch));
    -      }
    -#endif
         }
     
         return entry.forget();
    @@ -865,8 +855,11 @@
     
     #ifdef PR_LOGGING
         if (LOG_ENABLED()) {
    -        LOG(("userfonts (%p) added \"%s\" (%p)",
    -             this, NS_ConvertUTF16toUTF8(aFamilyName).get(), aUserFontEntry));
    +        LOG(("userfonts (%p) added to \"%s\" (%p) style: %s weight: %d "
    +             "stretch: %d",
    +             this, NS_ConvertUTF16toUTF8(aFamilyName).get(), aUserFontEntry,
    +             (aUserFontEntry->IsItalic() ? "italic" : "normal"),
    +             aUserFontEntry->Weight(), aUserFontEntry->Stretch()));
         }
     #endif
     }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxVR.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxVR.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxVR.h	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxVR.h	2015-02-03 14:33:14.000000000 +0000
    @@ -17,10 +17,10 @@
     namespace mozilla {
     namespace gfx {
     
    -MOZ_BEGIN_ENUM_CLASS(VRHMDType, uint16_t)
    +enum class VRHMDType : uint16_t {
       Oculus,
       NumHMDTypes
    -MOZ_END_ENUM_CLASS(VRHMDType)
    +};
     
     struct VRFieldOfView {
       static VRFieldOfView FromCSSPerspectiveInfo(double aPerspectiveDistance,
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxWindowsPlatform.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxWindowsPlatform.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxWindowsPlatform.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxWindowsPlatform.cpp	2015-02-03 14:33:14.000000000 +0000
    @@ -191,7 +191,7 @@
     #define GFX_CLEARTYPE_PARAMS_STRUCTURE "gfx.font_rendering.cleartype_params.pixel_structure"
     #define GFX_CLEARTYPE_PARAMS_MODE      "gfx.font_rendering.cleartype_params.rendering_mode"
     
    -class GPUAdapterReporter : public nsIMemoryReporter
    +class GPUAdapterReporter MOZ_FINAL : public nsIMemoryReporter
     {
         // Callers must Release the DXGIAdapter after use or risk mem-leak
         static bool GetDXGIAdapter(IDXGIAdapter **DXGIAdapter)
    @@ -325,6 +325,45 @@
     
     NS_IMPL_ISUPPORTS(GPUAdapterReporter, nsIMemoryReporter)
     
    +
    +Atomic gfxWindowsPlatform::sD3D11MemoryUsed;
    +
    +class D3D11TextureReporter MOZ_FINAL : public nsIMemoryReporter
    +{
    +  ~D3D11TextureReporter() {}
    +
    +public:
    +  NS_DECL_ISUPPORTS
    +
    +  NS_IMETHOD CollectReports(nsIHandleReportCallback *aHandleReport,
    +                            nsISupports* aData, bool aAnonymize) MOZ_OVERRIDE
    +  {
    +      return MOZ_COLLECT_REPORT("d3d11-shared-textures", KIND_OTHER, UNITS_BYTES,
    +                                gfxWindowsPlatform::sD3D11MemoryUsed,
    +                                "Memory used for D3D11 shared textures");
    +  }
    +};
    +
    +NS_IMPL_ISUPPORTS(D3D11TextureReporter, nsIMemoryReporter)
    +
    +Atomic gfxWindowsPlatform::sD3D9MemoryUsed;
    +
    +class D3D9TextureReporter MOZ_FINAL : public nsIMemoryReporter
    +{
    +public:
    +  NS_DECL_ISUPPORTS
    +
    +  NS_IMETHOD CollectReports(nsIHandleReportCallback *aHandleReport,
    +                            nsISupports* aData, bool aAnonymize) MOZ_OVERRIDE
    +  {
    +    return MOZ_COLLECT_REPORT("d3d9-shared-textures", KIND_OTHER, UNITS_BYTES,
    +                              gfxWindowsPlatform::sD3D9MemoryUsed,
    +                              "Memory used for D3D9 shared textures");
    +  }
    +};
    +
    +NS_IMPL_ISUPPORTS(D3D9TextureReporter, nsIMemoryReporter)
    +
     gfxWindowsPlatform::gfxWindowsPlatform()
       : mD3D11DeviceInitialized(false)
       , mIsWARP(false)
    @@ -352,6 +391,8 @@
         UpdateRenderMode();
     
         RegisterStrongMemoryReporter(new GPUAdapterReporter());
    +    RegisterStrongMemoryReporter(new D3D11TextureReporter());
    +    RegisterStrongMemoryReporter(new D3D9TextureReporter());
     }
     
     gfxWindowsPlatform::~gfxWindowsPlatform()
    @@ -386,6 +427,7 @@
     /* Pick the default render mode for
      * desktop.
      */
    +    bool didReset = false;
         if (DidRenderingDeviceReset()) {
           mD3D11DeviceInitialized = false;
           mD3D11Device = nullptr;
    @@ -395,6 +437,8 @@
           imgLoader::Singleton()->ClearCache(true);
           imgLoader::Singleton()->ClearCache(false);
           Factory::SetDirect3D11Device(nullptr);
    +
    +      didReset = true;
         }
     
         mRenderMode = RENDER_GDI;
    @@ -512,6 +556,10 @@
         contentMask |= BackendTypeBit(BackendType::SKIA);
         InitBackendPrefs(canvasMask, defaultBackend,
                          contentMask, defaultBackend);
    +
    +    if (didReset) {
    +      mScreenReferenceDrawTarget = CreateOffscreenContentDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8);
    +    }
     }
     
     #ifdef CAIRO_HAS_D2D_SURFACE
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxWindowsPlatform.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxWindowsPlatform.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/gfxWindowsPlatform.h	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/gfxWindowsPlatform.h	2015-02-03 14:33:14.000000000 +0000
    @@ -22,6 +22,7 @@
     #include "gfxPlatform.h"
     #include "gfxTypes.h"
     #include "mozilla/Attributes.h"
    +#include "mozilla/Atomics.h"
     #include "nsTArray.h"
     #include "nsDataHashtable.h"
     
    @@ -252,6 +253,9 @@
     
         bool IsWARP() { return mIsWARP; }
     
    +    static mozilla::Atomic sD3D11MemoryUsed;
    +    static mozilla::Atomic sD3D9MemoryUsed;
    +
     protected:
         RenderMode mRenderMode;
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/GraphicsFilter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/GraphicsFilter.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/gfx/thebes/GraphicsFilter.h	2015-01-25 22:24:22.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/gfx/thebes/GraphicsFilter.h	2015-02-03 14:33:13.000000000 +0000
    @@ -6,9 +6,7 @@
     #ifndef GraphicsFilter_h
     #define GraphicsFilter_h
     
    -#include "mozilla/TypedEnum.h"
    -
    -MOZ_BEGIN_ENUM_CLASS(GraphicsFilter, int)
    +enum class GraphicsFilter : int {
       FILTER_FAST,
       FILTER_GOOD,
       FILTER_BEST,
    @@ -16,7 +14,7 @@
       FILTER_BILINEAR,
       FILTER_GAUSSIAN,
       FILTER_SENTINEL
    -MOZ_END_ENUM_CLASS(GraphicsFilter)
    +};
     
     #endif
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/hal/gonk/GonkHal.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/hal/gonk/GonkHal.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/hal/gonk/GonkHal.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/hal/gonk/GonkHal.cpp	2015-02-03 14:33:14.000000000 +0000
    @@ -24,7 +24,6 @@
     #include 
     #include 
     #include 
    -#include 
     #include 
     #include 
     #include 
    @@ -48,7 +47,6 @@
     #include "HalImpl.h"
     #include "HalLog.h"
     #include "mozilla/ArrayUtils.h"
    -#include "mozilla/ClearOnShutdown.h"
     #include "mozilla/dom/battery/Constants.h"
     #include "mozilla/DebugOnly.h"
     #include "mozilla/FileUtils.h"
    @@ -630,6 +628,28 @@
     /**
      * RAII class to help us remember to close file descriptors.
      */
    +const char *wakeLockFilename = "/sys/power/wake_lock";
    +const char *wakeUnlockFilename = "/sys/power/wake_unlock";
    +
    +template
    +bool ReadFromFile(const char *filename, char (&buf)[n])
    +{
    +  int fd = open(filename, O_RDONLY);
    +  ScopedClose autoClose(fd);
    +  if (fd < 0) {
    +    HAL_LOG("Unable to open file %s.", filename);
    +    return false;
    +  }
    +
    +  ssize_t numRead = read(fd, buf, n);
    +  if (numRead < 0) {
    +    HAL_LOG("Error reading from file %s.", filename);
    +    return false;
    +  }
    +
    +  buf[std::min(numRead, n - 1)] = '\0';
    +  return true;
    +}
     
     bool WriteToFile(const char *filename, const char *toWrite)
     {
    @@ -755,9 +775,6 @@
     static void
     UpdateCpuSleepState()
     {
    -  const char *wakeLockFilename = "/sys/power/wake_lock";
    -  const char *wakeUnlockFilename = "/sys/power/wake_unlock";
    -
       sInternalLockCpuMonitor->AssertCurrentThreadOwns();
       bool allowed = sCpuSleepAllowed && !sInternalLockCpuCount;
       WriteToFile(allowed ? wakeUnlockFilename : wakeLockFilename, "gecko");
    @@ -1269,6 +1286,7 @@
           lineTimestampFound = true;
           mLastLineChecked = lineTimestamp;
         }
    +      
     
         // Log interesting lines
         for (size_t i = 0; i < regex_count; i++) {
    @@ -1294,347 +1312,6 @@
       return NS_OK;
     }
     
    -/**
    - * Wraps a particular ProcessPriority, giving us easy access to the prefs that
    - * are relevant to it.
    - *
    - * Creating a PriorityClass also ensures that the control group is created.
    - */
    -class PriorityClass
    -{
    -public:
    -  /**
    -   * Create a PriorityClass for the given ProcessPriority.  This implicitly
    -   * reads the relevant prefs and opens the cgroup.procs file of the relevant
    -   * control group caching its file descriptor for later use.
    -   */
    -  PriorityClass(ProcessPriority aPriority);
    -
    -  /**
    -   * Closes the file descriptor for the cgroup.procs file of the associated
    -   * control group.
    -   */
    -  ~PriorityClass();
    -
    -  PriorityClass(const PriorityClass& aOther);
    -  PriorityClass& operator=(const PriorityClass& aOther);
    -
    -  ProcessPriority Priority()
    -  {
    -    return mPriority;
    -  }
    -
    -  int32_t OomScoreAdj()
    -  {
    -    return clamped(mOomScoreAdj, OOM_SCORE_ADJ_MIN, OOM_SCORE_ADJ_MAX);
    -  }
    -
    -  int32_t KillUnderKB()
    -  {
    -    return mKillUnderKB;
    -  }
    -
    -  nsCString CGroup()
    -  {
    -    return mGroup;
    -  }
    -
    -  /**
    -   * Adds a process to this priority class, this moves the process' PID into
    -   * the associated control group.
    -   *
    -   * @param aPid The PID of the process to be added.
    -   */
    -  void AddProcess(int aPid);
    -
    -private:
    -  ProcessPriority mPriority;
    -  int32_t mOomScoreAdj;
    -  int32_t mKillUnderKB;
    -  int mCpuCGroupProcsFd;
    -  int mMemCGroupProcsFd;
    -  nsCString mGroup;
    -
    -  /**
    -   * Return a string that identifies where we can find the value of aPref
    -   * that's specific to mPriority.  For example, we might return
    -   * "hal.processPriorityManager.gonk.FOREGROUND_HIGH.oomScoreAdjust".
    -   */
    -  nsCString PriorityPrefName(const char* aPref)
    -  {
    -    return nsPrintfCString("hal.processPriorityManager.gonk.%s.%s",
    -                           ProcessPriorityToString(mPriority), aPref);
    -  }
    -
    -  /**
    -   * Get the full path of the cgroup.procs file associated with the group.
    -   */
    -  nsCString CpuCGroupProcsFilename()
    -  {
    -    nsCString cgroupName = mGroup;
    -
    -    /* If mGroup is empty, our cgroup.procs file is the root procs file,
    -     * located at /dev/cpuctl/cgroup.procs.  Otherwise our procs file is
    -     * /dev/cpuctl/NAME/cgroup.procs. */
    -
    -    if (!mGroup.IsEmpty()) {
    -      cgroupName.AppendLiteral("/");
    -    }
    -
    -    return NS_LITERAL_CSTRING("/dev/cpuctl/") + cgroupName +
    -           NS_LITERAL_CSTRING("cgroup.procs");
    -  }
    -
    -  nsCString MemCGroupProcsFilename()
    -  {
    -    nsCString cgroupName = mGroup;
    -
    -    /* If mGroup is empty, our cgroup.procs file is the root procs file,
    -     * located at /sys/fs/cgroup/memory/cgroup.procs.  Otherwise our procs
    -     * file is /sys/fs/cgroup/memory/NAME/cgroup.procs. */
    -
    -    if (!mGroup.IsEmpty()) {
    -      cgroupName.AppendLiteral("/");
    -    }
    -
    -    return NS_LITERAL_CSTRING("/sys/fs/cgroup/memory/") + cgroupName +
    -           NS_LITERAL_CSTRING("cgroup.procs");
    -  }
    -
    -  int OpenCpuCGroupProcs()
    -  {
    -    return open(CpuCGroupProcsFilename().get(), O_WRONLY);
    -  }
    -
    -  int OpenMemCGroupProcs()
    -  {
    -    return open(MemCGroupProcsFilename().get(), O_WRONLY);
    -  }
    -};
    -
    -/**
    - * Creates a directory and parents (essentially mkdir -p, but
    - * this only create the directories within the cgroup name).
    - */
    -static bool MakeCGroupDir(const nsACString& aRootDir,
    -                          const nsACString& aGroupName)
    -{
    -  NS_NAMED_LITERAL_CSTRING(kSlash, "/");
    -
    -  // Create directories contained within aGroupName
    -  nsCString cgroupIter = aGroupName + kSlash;
    -
    -  int32_t offset = 0;
    -  while ((offset = cgroupIter.FindChar('/', offset)) != -1) {
    -    nsAutoCString path = aRootDir + Substring(cgroupIter, 0, offset);
    -    int rv = mkdir(path.get(), 0744);
    -
    -    if (rv == -1 && errno != EEXIST) {
    -      HAL_LOG("Could not create the %s control group.", path.get());
    -      return false;
    -    }
    -
    -    offset++;
    -  }
    -  return true;
    -}
    -
    -/**
    - * Try to create the cgroup for the given PriorityClass, if it doesn't already
    - * exist.  This essentially implements mkdir -p; that is, we create parent
    - * cgroups as necessary.  The group parameters are also set according to
    - * the corresponding preferences.
    - *
    - * @param aGroup The name of the group.
    - * @return true if we successfully created the cgroup, or if it already
    - * exists.  Otherwise, return false.
    - */
    -static bool
    -EnsureCpuCGroupExists(const nsACString& aGroup)
    -{
    -  NS_NAMED_LITERAL_CSTRING(kDevCpuCtl, "/dev/cpuctl/");
    -  NS_NAMED_LITERAL_CSTRING(kSlash, "/");
    -
    -  nsAutoCString prefPrefix("hal.processPriorityManager.gonk.cgroups.");
    -
    -  /* If cgroup is not empty, append the cgroup name and a dot to obtain the
    -   * group specific preferences. */
    -  if (!aGroup.IsEmpty()) {
    -    prefPrefix += aGroup + NS_LITERAL_CSTRING(".");
    -  }
    -
    -  nsAutoCString cpuSharesPref(prefPrefix + NS_LITERAL_CSTRING("cpu_shares"));
    -  int cpuShares = Preferences::GetInt(cpuSharesPref.get());
    -
    -  nsAutoCString cpuNotifyOnMigratePref(prefPrefix
    -    + NS_LITERAL_CSTRING("cpu_notify_on_migrate"));
    -  int cpuNotifyOnMigrate = Preferences::GetInt(cpuNotifyOnMigratePref.get());
    -
    -  if (!MakeCGroupDir(kDevCpuCtl, aGroup)) {
    -    return false;
    -  }
    -
    -  nsAutoCString pathPrefix(kDevCpuCtl + aGroup + kSlash);
    -  nsAutoCString cpuSharesPath(pathPrefix + NS_LITERAL_CSTRING("cpu.shares"));
    -  if (cpuShares && !WriteToFile(cpuSharesPath.get(),
    -                                nsPrintfCString("%d", cpuShares).get())) {
    -    HAL_LOG("Could not set the cpu share for group %s", cpuSharesPath.get());
    -    return false;
    -  }
    -
    -  nsAutoCString notifyOnMigratePath(pathPrefix
    -    + NS_LITERAL_CSTRING("cpu.notify_on_migrate"));
    -  if (!WriteToFile(notifyOnMigratePath.get(),
    -                   nsPrintfCString("%d", cpuNotifyOnMigrate).get())) {
    -    HAL_LOG("Could not set the cpu migration notification flag for group %s",
    -            notifyOnMigratePath.get());
    -    return false;
    -  }
    -
    -  return true;
    -}
    -
    -static bool
    -EnsureMemCGroupExists(const nsACString& aGroup)
    -{
    -  NS_NAMED_LITERAL_CSTRING(kMemCtl, "/sys/fs/cgroup/memory/");
    -  NS_NAMED_LITERAL_CSTRING(kSlash, "/");
    -
    -  nsAutoCString prefPrefix("hal.processPriorityManager.gonk.cgroups.");
    -
    -  /* If cgroup is not empty, append the cgroup name and a dot to obtain the
    -   * group specific preferences. */
    -  if (!aGroup.IsEmpty()) {
    -    prefPrefix += aGroup + NS_LITERAL_CSTRING(".");
    -  }
    -
    -  nsAutoCString memSwappinessPref(prefPrefix +
    -                                  NS_LITERAL_CSTRING("memory_swappiness"));
    -  int memSwappiness = Preferences::GetInt(memSwappinessPref.get());
    -
    -  if (!MakeCGroupDir(kMemCtl, aGroup)) {
    -    return false;
    -  }
    -
    -  nsAutoCString pathPrefix(kMemCtl + aGroup + kSlash);
    -  nsAutoCString memSwappinessPath(pathPrefix +
    -                                  NS_LITERAL_CSTRING("memory.swappiness"));
    -  if (!WriteToFile(memSwappinessPath.get(),
    -                   nsPrintfCString("%d", memSwappiness).get())) {
    -    HAL_LOG("Could not set the memory.swappiness for group %s",
    -            memSwappinessPath.get());
    -    return false;
    -  }
    -  return true;
    -}
    -
    -PriorityClass::PriorityClass(ProcessPriority aPriority)
    -  : mPriority(aPriority)
    -  , mOomScoreAdj(0)
    -  , mKillUnderKB(0)
    -  , mCpuCGroupProcsFd(-1)
    -  , mMemCGroupProcsFd(-1)
    -{
    -  DebugOnly rv;
    -
    -  rv = Preferences::GetInt(PriorityPrefName("OomScoreAdjust").get(),
    -                           &mOomScoreAdj);
    -  MOZ_ASSERT(NS_SUCCEEDED(rv), "Missing oom_score_adj preference");
    -
    -  rv = Preferences::GetInt(PriorityPrefName("KillUnderKB").get(),
    -                           &mKillUnderKB);
    -
    -  rv = Preferences::GetCString(PriorityPrefName("cgroup").get(), &mGroup);
    -  MOZ_ASSERT(NS_SUCCEEDED(rv), "Missing control group preference");
    -
    -  if (EnsureCpuCGroupExists(mGroup)) {
    -    mCpuCGroupProcsFd = OpenCpuCGroupProcs();
    -  }
    -  if (EnsureMemCGroupExists(mGroup)) {
    -    mMemCGroupProcsFd = OpenMemCGroupProcs();
    -  }
    -}
    -
    -PriorityClass::~PriorityClass()
    -{
    -  MOZ_TEMP_FAILURE_RETRY(close(mCpuCGroupProcsFd));
    -  MOZ_TEMP_FAILURE_RETRY(close(mMemCGroupProcsFd));
    -}
    -
    -PriorityClass::PriorityClass(const PriorityClass& aOther)
    -  : mPriority(aOther.mPriority)
    -  , mOomScoreAdj(aOther.mOomScoreAdj)
    -  , mKillUnderKB(aOther.mKillUnderKB)
    -  , mGroup(aOther.mGroup)
    -{
    -  mCpuCGroupProcsFd = OpenCpuCGroupProcs();
    -  mMemCGroupProcsFd = OpenMemCGroupProcs();
    -}
    -
    -PriorityClass& PriorityClass::operator=(const PriorityClass& aOther)
    -{
    -  mPriority = aOther.mPriority;
    -  mOomScoreAdj = aOther.mOomScoreAdj;
    -  mKillUnderKB = aOther.mKillUnderKB;
    -  mGroup = aOther.mGroup;
    -  mCpuCGroupProcsFd = OpenCpuCGroupProcs();
    -  mMemCGroupProcsFd = OpenMemCGroupProcs();
    -  return *this;
    -}
    -
    -void PriorityClass::AddProcess(int aPid)
    -{
    -  if (mCpuCGroupProcsFd >= 0) {
    -    nsPrintfCString str("%d", aPid);
    -
    -    if (write(mCpuCGroupProcsFd, str.get(), str.Length()) < 0) {
    -      HAL_ERR("Couldn't add PID %d to the %s cpu control group",
    -              aPid, mGroup.get());
    -    }
    -  }
    -
    -  if (mMemCGroupProcsFd >= 0) {
    -    nsPrintfCString str("%d", aPid);
    -
    -    if (write(mMemCGroupProcsFd, str.get(), str.Length()) < 0) {
    -      HAL_ERR("Couldn't add PID %d to the %s memory control group",
    -              aPid, mGroup.get());
    -    }
    -  }
    -}
    -
    -/**
    - * Get the PriorityClass associated with the given ProcessPriority.
    - *
    - * If you pass an invalid ProcessPriority value, we return null.
    - *
    - * The pointers returned here are owned by GetPriorityClass (don't free them
    - * yourself).  They are guaranteed to stick around until shutdown.
    - */
    -PriorityClass*
    -GetPriorityClass(ProcessPriority aPriority)
    -{
    -  static StaticAutoPtr> priorityClasses;
    -
    -  // Initialize priorityClasses if this is the first time we're running this
    -  // method.
    -  if (!priorityClasses) {
    -    priorityClasses = new nsTArray();
    -    ClearOnShutdown(&priorityClasses);
    -
    -    for (int32_t i = 0; i < NUM_PROCESS_PRIORITY; i++) {
    -      priorityClasses->AppendElement(PriorityClass(ProcessPriority(i)));
    -    }
    -  }
    -
    -  if (aPriority < 0 ||
    -      static_cast(aPriority) >= priorityClasses->Length()) {
    -    return nullptr;
    -  }
    -
    -  return &(*priorityClasses)[aPriority];
    -}
    -
     static void
     EnsureKernelLowMemKillerParamsSet()
     {
    @@ -1675,12 +1352,21 @@
         // The system doesn't function correctly if we're missing these prefs, so
         // crash loudly.
     
    -    PriorityClass* pc = GetPriorityClass(static_cast(i));
    +    ProcessPriority priority = static_cast(i);
     
    -    int32_t oomScoreAdj = pc->OomScoreAdj();
    -    int32_t killUnderKB = pc->KillUnderKB();
    +    int32_t oomScoreAdj;
    +    if (!NS_SUCCEEDED(Preferences::GetInt(
    +          nsPrintfCString("hal.processPriorityManager.gonk.%s.OomScoreAdjust",
    +                          ProcessPriorityToString(priority)).get(),
    +          &oomScoreAdj))) {
    +      MOZ_CRASH();
    +    }
     
    -    if (killUnderKB == 0) {
    +    int32_t killUnderKB;
    +    if (!NS_SUCCEEDED(Preferences::GetInt(
    +          nsPrintfCString("hal.processPriorityManager.gonk.%s.KillUnderKB",
    +                          ProcessPriorityToString(priority)).get(),
    +          &killUnderKB))) {
           // ProcessPriority values like PROCESS_PRIORITY_FOREGROUND_KEYBOARD,
           // which has only OomScoreAdjust but lacks KillUnderMB value, will not
           // create new LMK parameters.
    @@ -1711,8 +1397,7 @@
       minfreeParams.Cut(minfreeParams.Length() - 1, 1);
       if (!adjParams.IsEmpty() && !minfreeParams.IsEmpty()) {
         WriteToFile("/sys/module/lowmemorykiller/parameters/adj", adjParams.get());
    -    WriteToFile("/sys/module/lowmemorykiller/parameters/minfree",
    -                minfreeParams.get());
    +    WriteToFile("/sys/module/lowmemorykiller/parameters/minfree", minfreeParams.get());
       }
     
       // Set the low-memory-notification threshold.
    @@ -1734,6 +1419,148 @@
       }
     }
     
    +static void
    +SetNiceForPid(int aPid, int aNice)
    +{
    +  errno = 0;
    +  int origProcPriority = getpriority(PRIO_PROCESS, aPid);
    +  if (errno) {
    +    HAL_LOG("Unable to get nice for pid=%d; error %d.  SetNiceForPid bailing.",
    +            aPid, errno);
    +    return;
    +  }
    +
    +  int rv = setpriority(PRIO_PROCESS, aPid, aNice);
    +  if (rv) {
    +    HAL_LOG("Unable to set nice for pid=%d; error %d.  SetNiceForPid bailing.",
    +            aPid, errno);
    +    return;
    +  }
    +
    +  // On Linux, setpriority(aPid) modifies the priority only of the main
    +  // thread of that process.  We have to modify the priorities of all of the
    +  // process's threads as well, so iterate over all the threads and increase
    +  // each of their priorites by aNice - origProcPriority (and also ensure that
    +  // none of the tasks has a lower priority than the main thread).
    +  //
    +  // This is horribly racy.
    +
    +  DIR* tasksDir = opendir(nsPrintfCString("/proc/%d/task/", aPid).get());
    +  if (!tasksDir) {
    +    HAL_LOG("Unable to open /proc/%d/task.  SetNiceForPid bailing.", aPid);
    +    return;
    +  }
    +
    +  // Be careful not to leak tasksDir; after this point, we must call closedir().
    +
    +  while (struct dirent* de = readdir(tasksDir)) {
    +    char* endptr = nullptr;
    +    long tidlong = strtol(de->d_name, &endptr, /* base */ 10);
    +    if (*endptr || tidlong < 0 || tidlong > INT32_MAX || tidlong == aPid) {
    +      // if dp->d_name was not an integer, was negative (?!) or too large, or
    +      // was the same as aPid, we're not interested.
    +      //
    +      // (The |tidlong == aPid| check is very important; without it, we'll
    +      // renice aPid twice, and the second renice will be relative to the
    +      // priority set by the first renice.)
    +      continue;
    +    }
    +
    +    int tid = static_cast(tidlong);
    +
    +    // Do not set the priority of threads running with a real-time policy
    +    // as part of the bulk process adjustment.  These threads need to run
    +    // at their specified priority in order to meet timing guarantees.
    +    int schedPolicy = sched_getscheduler(tid);
    +    if (schedPolicy == SCHED_FIFO || schedPolicy == SCHED_RR) {
    +      continue;
    +    }
    +
    +    errno = 0;
    +    // Get and set the task's new priority.
    +    int origtaskpriority = getpriority(PRIO_PROCESS, tid);
    +    if (errno) {
    +      HAL_LOG("Unable to get nice for tid=%d (pid=%d); error %d.  This isn't "
    +              "necessarily a problem; it could be a benign race condition.",
    +              tid, aPid, errno);
    +      continue;
    +    }
    +
    +    int newtaskpriority =
    +      std::max(origtaskpriority - origProcPriority + aNice, aNice);
    +
    +    // Do not reduce priority of threads already running at priorities greater
    +    // than normal.  These threads are likely special service threads that need
    +    // elevated priorities to process audio, display composition, etc.
    +    if (newtaskpriority > origtaskpriority &&
    +        origtaskpriority < ANDROID_PRIORITY_NORMAL) {
    +      continue;
    +    }
    +
    +    rv = setpriority(PRIO_PROCESS, tid, newtaskpriority);
    +
    +    if (rv) {
    +      HAL_LOG("Unable to set nice for tid=%d (pid=%d); error %d.  This isn't "
    +              "necessarily a problem; it could be a benign race condition.",
    +              tid, aPid, errno);
    +      continue;
    +    }
    +  }
    +
    +  HAL_LOG("Changed nice for pid %d from %d to %d.",
    +          aPid, origProcPriority, aNice);
    +
    +  closedir(tasksDir);
    +}
    +
    +/*
    + * Used to store the nice value adjustments and oom_adj values for the various
    + * process priority levels.
    + */
    +struct ProcessPriorityPrefs {
    +  bool initialized;
    +  int lowPriorityNice;
    +  struct {
    +    int nice;
    +    int oomScoreAdj;
    +  } priorities[NUM_PROCESS_PRIORITY];
    +};
    +
    +/*
    + * Reads the preferences for the various process priority levels and sets up
    + * watchers so that if they're dynamically changed the change is reflected on
    + * the appropriate variables.
    + */
    +void
    +EnsureProcessPriorityPrefs(ProcessPriorityPrefs* prefs)
    +{
    +  if (prefs->initialized) {
    +    return;
    +  }
    +
    +  // Read the preferences for process priority levels
    +  for (int i = PROCESS_PRIORITY_BACKGROUND; i < NUM_PROCESS_PRIORITY; i++) {
    +    ProcessPriority priority = static_cast(i);
    +
    +    // Read the nice values
    +    const char* processPriorityStr = ProcessPriorityToString(priority);
    +    nsPrintfCString niceStr("hal.processPriorityManager.gonk.%s.Nice",
    +                            processPriorityStr);
    +    Preferences::AddIntVarCache(&prefs->priorities[i].nice, niceStr.get());
    +
    +    // Read the oom_adj scores
    +    nsPrintfCString oomStr("hal.processPriorityManager.gonk.%s.OomScoreAdjust",
    +                           processPriorityStr);
    +    Preferences::AddIntVarCache(&prefs->priorities[i].oomScoreAdj,
    +                                oomStr.get());
    +  }
    +
    +  Preferences::AddIntVarCache(&prefs->lowPriorityNice,
    +                              "hal.processPriorityManager.gonk.LowCPUNice");
    +
    +  prefs->initialized = true;
    +}
    +
     void
     SetProcessPriority(int aPid,
                        ProcessPriority aPriority,
    @@ -1752,23 +1579,49 @@
       // SetProcessPriority being called early in startup.
       EnsureKernelLowMemKillerParamsSet();
     
    -  PriorityClass* pc = GetPriorityClass(aPriority);
    +  static ProcessPriorityPrefs prefs = { 0 };
    +  EnsureProcessPriorityPrefs(&prefs);
     
    -  int oomScoreAdj = pc->OomScoreAdj();
    +  int oomScoreAdj = prefs.priorities[aPriority].oomScoreAdj;
     
       RoundOomScoreAdjUpWithBackroundLRU(oomScoreAdj, aBackgroundLRU);
     
    +  int clampedOomScoreAdj = clamped(oomScoreAdj, OOM_SCORE_ADJ_MIN,
    +                                                     OOM_SCORE_ADJ_MAX);
    +  if (clampedOomScoreAdj != oomScoreAdj) {
    +    HAL_LOG("Clamping OOM adjustment for pid %d to %d", aPid,
    +            clampedOomScoreAdj);
    +  } else {
    +    HAL_LOG("Setting OOM adjustment for pid %d to %d", aPid,
    +            clampedOomScoreAdj);
    +  }
    +
       // We try the newer interface first, and fall back to the older interface
       // on failure.
    +
       if (!WriteToFile(nsPrintfCString("/proc/%d/oom_score_adj", aPid).get(),
    -                   nsPrintfCString("%d", oomScoreAdj).get()))
    +                   nsPrintfCString("%d", clampedOomScoreAdj).get()))
       {
    +    int oomAdj = OomAdjOfOomScoreAdj(clampedOomScoreAdj);
    +
         WriteToFile(nsPrintfCString("/proc/%d/oom_adj", aPid).get(),
    -                nsPrintfCString("%d", OomAdjOfOomScoreAdj(oomScoreAdj)).get());
    +                nsPrintfCString("%d", oomAdj).get());
    +  }
    +
    +  int nice = 0;
    +
    +  if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
    +    nice = prefs.priorities[aPriority].nice;
    +  } else if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
    +    nice = prefs.lowPriorityNice;
    +  } else {
    +    HAL_ERR("Unknown aCPUPriority value %d", aCPUPriority);
    +    MOZ_ASSERT(false);
    +    return;
       }
     
    -  HAL_LOG("Assigning pid %d to cgroup %s", aPid, pc->CGroup().get());
    -  pc->AddProcess(aPid);
    +  HAL_LOG("Setting nice for pid %d to %d", aPid, nice);
    +  SetNiceForPid(aPid, nice);
     }
     
     static bool
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/hal/linux/UPowerClient.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/hal/linux/UPowerClient.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/hal/linux/UPowerClient.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/hal/linux/UPowerClient.cpp	2015-02-03 14:33:14.000000000 +0000
    @@ -9,6 +9,7 @@
     #include 
     #include 
     #include "nsAutoRef.h"
    +#include 
     
     /*
      * Helper that manages the destruction of glib objects as soon as they leave
    @@ -428,14 +429,14 @@
       }
     
       /*
    -   * The battery level might be very close to 100% (like 99.xxxx%) without
    +   * The battery level might be very close to 100% (like 99%) without
        * increasing. It seems that upower sets the battery state as 'full' in that
        * case so we should trust it and not even try to get the value.
        */
       if (isFull) {
         mLevel = 1.0;
       } else {
    -    mLevel = g_value_get_double(static_cast(g_hash_table_lookup(aHashTable, "Percentage")))*0.01;
    +    mLevel = round(g_value_get_double(static_cast(g_hash_table_lookup(aHashTable, "Percentage"))))*0.01;
       }
     
       if (isFull) {
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/decoders/EXIF.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/decoders/EXIF.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/decoders/EXIF.h	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/decoders/EXIF.h	2015-02-03 14:33:14.000000000 +0000
    @@ -7,7 +7,6 @@
     #define MOZILLA_IMAGELIB_EXIF_H
     
     #include 
    -#include "mozilla/TypedEnum.h"
     #include "nsDebug.h"
     
     #include "Orientation.h"
    @@ -15,11 +14,11 @@
     namespace mozilla {
     namespace image {
     
    -MOZ_BEGIN_ENUM_CLASS(ByteOrder, uint8_t)
    +enum class ByteOrder : uint8_t {
       Unknown,
       LittleEndian,
       BigEndian
    -MOZ_END_ENUM_CLASS(ByteOrder)
    +};
     
     struct EXIFData
     {
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/decoders/icon/win/nsIconChannel.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/decoders/icon/win/nsIconChannel.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/decoders/icon/win/nsIconChannel.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/decoders/icon/win/nsIconChannel.cpp	2015-02-03 14:33:14.000000000 +0000
    @@ -490,7 +490,7 @@
     {
       BITMAPINFO* bmi = (BITMAPINFO*) ::operator new(sizeof(BITMAPINFOHEADER) +
                                                      aColorTableSize,
    -                                                 mozilla::fallible_t());
    +                                                 mozilla::fallible);
       if (bmi) {
         memcpy(bmi, aHeader, sizeof(BITMAPINFOHEADER));
         memset(bmi->bmiColors, 0, aColorTableSize);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/decoders/nsJPEGDecoder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/decoders/nsJPEGDecoder.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/decoders/nsJPEGDecoder.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/decoders/nsJPEGDecoder.cpp	2015-02-03 14:33:14.000000000 +0000
    @@ -158,7 +158,7 @@
     nsJPEGDecoder::InitInternal()
     {
       mCMSMode = gfxPlatform::GetCMSMode();
    -  if ((mDecodeFlags & DECODER_NO_COLORSPACE_CONVERSION) != 0) {
    +  if (GetDecodeFlags() & imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION) {
         mCMSMode = eCMSMode_Off;
       }
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/decoders/nsPNGDecoder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/decoders/nsPNGDecoder.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/decoders/nsPNGDecoder.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/decoders/nsPNGDecoder.cpp	2015-02-03 14:33:14.000000000 +0000
    @@ -226,11 +226,11 @@
       }
     
       mCMSMode = gfxPlatform::GetCMSMode();
    -  if ((mDecodeFlags & DECODER_NO_COLORSPACE_CONVERSION) != 0) {
    +  if (GetDecodeFlags() & imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION) {
         mCMSMode = eCMSMode_Off;
       }
    -  mDisablePremultipliedAlpha = (mDecodeFlags & DECODER_NO_PREMULTIPLY_ALPHA)
    -                                != 0;
    +  mDisablePremultipliedAlpha =
    +    GetDecodeFlags() & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
     
     #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
       static png_byte color_chunks[]=
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/encoders/bmp/nsBMPEncoder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/encoders/bmp/nsBMPEncoder.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/encoders/bmp/nsBMPEncoder.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/encoders/bmp/nsBMPEncoder.cpp	2015-02-03 14:33:14.000000000 +0000
    @@ -185,7 +185,6 @@
         return NS_ERROR_INVALID_ARG;
       }
     
    -  static fallible_t fallible = fallible_t();
       nsAutoArrayPtr row(new (fallible)
                                   uint8_t[mBMPInfoHeader.width *
                                   BytesPerPixel(mBMPInfoHeader.bpp)]);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/public/imgIContainer.idl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/public/imgIContainer.idl
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/public/imgIContainer.idl	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/public/imgIContainer.idl	2015-02-03 14:33:14.000000000 +0000
    @@ -69,7 +69,7 @@
      *
      * Internally, imgIContainer also manages animation of images.
      */
    -[scriptable, builtinclass, uuid(4adb4c92-284d-4f5a-85e7-e924ec57510f)]
    +[scriptable, builtinclass, uuid(3a630c1d-3365-4873-bc01-54468decf8c6)]
     interface imgIContainer : nsISupports
     {
       /**
    @@ -146,12 +146,16 @@
        *
        * Meanings:
        *
    -   * FLAG_NONE: Lack of flags
    +   * FLAG_NONE: Lack of flags.
        *
        * FLAG_SYNC_DECODE: Forces synchronous/non-progressive decode of all
    -   * available data before the call returns. It is an error to pass this flag
    -   * from a call stack that originates in a decoder (ie, from a decoder
    -   * observer event).
    +   * available data before the call returns.
    +   *
    +   * FLAG_SYNC_DECODE_IF_FAST: Like FLAG_SYNC_DECODE, but requests a sync decode
    +   * be performed only if ImageLib estimates it can be completed very quickly.
    +   *
    +   * FLAG_ASYNC_NOTIFY: Send notifications asynchronously, even if we decode
    +   * synchronously beause of FLAG_SYNC_DECODE or FLAG_SYNC_DECODE_IF_FAST.
        *
        * FLAG_DECODE_NO_PREMULTIPLY_ALPHA: Do not premultiply alpha if
        * it's not already premultiplied in the image data.
    @@ -166,28 +170,33 @@
        * FLAG_HIGH_QUALITY_SCALING: A hint as to whether this image should be
        * scaled using the high quality scaler. Do not set this if not drawing to
        * a window or not listening to invalidations.
    -   */
    +   *
    +   * FLAG_WANT_DATA_SURFACE: Can be passed to GetFrame when the caller wants a
    +   * DataSourceSurface instead of a hardware accelerated surface. This can be
    +   * important for performance (by avoiding an upload to/readback from the GPU)
    +   * when the caller knows they want a SourceSurface of type DATA.
    +   *
    +   * FLAG_BYPASS_SURFACE_CACHE: Forces drawing to happen rather than taking
    +   * cached rendering from the surface cache. This is used when we are printing,
    +   * for example, where we want the vector commands from VectorImages to end up
    +   * in the PDF output rather than a cached rendering at screen resolution.
    +   */
    +  const unsigned long FLAG_NONE                            = 0x0;
    +  const unsigned long FLAG_SYNC_DECODE                     = 0x1;
    +  const unsigned long FLAG_SYNC_DECODE_IF_FAST             = 0x2;
    +  const unsigned long FLAG_ASYNC_NOTIFY                    = 0x4;
    +  const unsigned long FLAG_DECODE_NO_PREMULTIPLY_ALPHA     = 0x8;
    +  const unsigned long FLAG_DECODE_NO_COLORSPACE_CONVERSION = 0x10;
    +  const unsigned long FLAG_CLAMP                           = 0x20;
    +  const unsigned long FLAG_HIGH_QUALITY_SCALING            = 0x40;
    +  const unsigned long FLAG_WANT_DATA_SURFACE               = 0x80;
    +  const unsigned long FLAG_BYPASS_SURFACE_CACHE            = 0x100;
     
    -  const long FLAG_NONE            = 0x0;
    -  const long FLAG_SYNC_DECODE     = 0x1;
    -  const long FLAG_DECODE_NO_PREMULTIPLY_ALPHA = 0x2;
    -  const long FLAG_DECODE_NO_COLORSPACE_CONVERSION = 0x4;
    -  const long FLAG_CLAMP           = 0x8;
    -  const long FLAG_HIGH_QUALITY_SCALING = 0x10;
    -  /**
    -   * Can be passed to GetFrame when the caller wants a DataSourceSurface
    -   * instead of a hardware accelerated surface. This can be important for
    -   * performance (by avoiding an upload to/readback from the GPU) when the
    -   * caller knows they want a SourceSurface of type DATA.
    -   */
    -  const long FLAG_WANT_DATA_SURFACE = 0x20;
    -  /**
    -   * Forces drawing to happen rather than taking cached rendering from the
    -   * surface cache. This is used when we are printing, for example, where we
    -   * want the vector commands from VectorImages to end up in the PDF output
    -   * rather than a cached rendering at screen resolution.
    +  /**
    +   * A constant specifying the default set of decode flags (i.e., the default
    +   * values for FLAG_DECODE_*).
        */
    -  const long FLAG_BYPASS_SURFACE_CACHE = 0x40;
    +  const unsigned long DECODE_FLAGS_DEFAULT = 0;
     
       /**
         * Constants for specifying various "special" frames.
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/DecodePool.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/DecodePool.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/DecodePool.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/DecodePool.cpp	2015-02-03 14:33:14.000000000 +0000
    @@ -345,7 +345,8 @@
     {
       MOZ_ASSERT(aDecoder);
     
    -  if (!NS_IsMainThread()) {
    +  if (!NS_IsMainThread() ||
    +      (aDecoder->GetFlags() & imgIContainer::FLAG_ASYNC_NOTIFY)) {
         NotifyProgressWorker::Dispatch(aDecoder->GetImage(),
                                        aDecoder->TakeProgress(),
                                        aDecoder->TakeInvalidRect(),
    @@ -363,7 +364,8 @@
     {
       MOZ_ASSERT(aDecoder);
     
    -  if (!NS_IsMainThread()) {
    +  if (!NS_IsMainThread() ||
    +      (aDecoder->GetFlags() & imgIContainer::FLAG_ASYNC_NOTIFY)) {
         NotifyDecodeCompleteWorker::Dispatch(aDecoder);
         return;
       }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/DecodePool.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/DecodePool.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/DecodePool.h	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/DecodePool.h	2015-02-03 14:33:14.000000000 +0000
    @@ -12,7 +12,6 @@
     
     #include "mozilla/Mutex.h"
     #include "mozilla/StaticPtr.h"
    -#include 
     #include "nsCOMPtr.h"
     #include "nsIEventTarget.h"
     #include "nsIObserver.h"
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/Decoder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/Decoder.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/Decoder.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/Decoder.cpp	2015-02-03 14:33:14.000000000 +0000
    @@ -28,13 +28,14 @@
       , mImageData(nullptr)
       , mColormap(nullptr)
       , mChunkCount(0)
    -  , mDecodeFlags(0)
    +  , mFlags(0)
       , mBytesDecoded(0)
       , mSendPartialInvalidations(false)
       , mDataDone(false)
       , mDecodeDone(false)
       , mDataError(false)
       , mDecodeAborted(false)
    +  , mShouldReportError(false)
       , mImageIsTransient(false)
       , mImageIsLocked(false)
       , mFrameCount(0)
    @@ -146,6 +147,7 @@
             PostDataError();
           }
     
    +      CompleteDecode();
           return finalStatus;
         }
     
    @@ -154,6 +156,7 @@
         Write(mIterator->Data(), mIterator->Length());
       }
     
    +  CompleteDecode();
       return HasError() ? NS_ERROR_FAILURE : NS_OK;
     }
     
    @@ -241,10 +244,8 @@
     }
     
     void
    -Decoder::Finish()
    +Decoder::CompleteDecode()
     {
    -  MOZ_ASSERT(NS_IsMainThread());
    -
       // Implementation-specific finalization
       if (!HasError())
         FinishInternal();
    @@ -255,10 +256,39 @@
     
       // If PostDecodeDone() has not been called, and this decoder wasn't aborted
       // early because of low-memory conditions or losing a race with another
    -  // decoder, we need to send teardown notifications.
    +  // decoder, we need to send teardown notifications (and report an error to the
    +  // console later).
       if (!IsSizeDecode() && !mDecodeDone && !WasAborted()) {
    +    mShouldReportError = true;
    +
    +    // If we only have a data error, we're usable if we have at least one
    +    // complete frame.
    +    if (!HasDecoderError() && GetCompleteFrameCount() > 0) {
    +      // We're usable, so do exactly what we should have when the decoder
    +      // completed.
    +      if (mInFrame) {
    +        PostFrameStop();
    +      }
    +      PostDecodeDone();
    +    } else {
    +      // We're not usable. Record some final progress indicating the error.
    +      if (!IsSizeDecode()) {
    +        mProgress |= FLAG_DECODE_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
    +      }
    +      mProgress |= FLAG_HAS_ERROR;
    +    }
    +  }
    +}
     
    -    // Log data errors to the error console
    +void
    +Decoder::Finish()
    +{
    +  MOZ_ASSERT(NS_IsMainThread());
    +
    +  MOZ_ASSERT(HasError() || !mInFrame, "Finishing while we're still in a frame");
    +
    +  // If we detected an error in CompleteDecode(), log it to the error console.
    +  if (mShouldReportError && !WasAborted()) {
         nsCOMPtr consoleService =
           do_GetService(NS_CONSOLESERVICE_CONTRACTID);
         nsCOMPtr errorObject =
    @@ -277,23 +307,6 @@
             consoleService->LogMessage(errorObject);
           }
         }
    -
    -    // If we only have a data error, we're usable if we have at least one
    -    // complete frame.
    -    if (!HasDecoderError() && GetCompleteFrameCount() > 0) {
    -      // We're usable, so do exactly what we should have when the decoder
    -      // completed.
    -      if (mInFrame) {
    -        PostFrameStop();
    -      }
    -      PostDecodeDone();
    -    } else {
    -      // We're not usable. Record some final progress indicating the error.
    -      if (!IsSizeDecode()) {
    -        mProgress |= FLAG_DECODE_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
    -      }
    -      mProgress |= FLAG_HAS_ERROR;
    -    }
       }
     
       // Set image metadata before calling DecodingComplete, because
    @@ -321,8 +334,6 @@
     void
     Decoder::FinishSharedDecoder()
     {
    -  MOZ_ASSERT(NS_IsMainThread());
    -
       if (!HasError()) {
         FinishInternal();
       }
    @@ -342,7 +353,7 @@
       mCurrentFrame = EnsureFrame(mNewFrameData.mFrameNum,
                                   targetSize,
                                   mNewFrameData.mFrameRect,
    -                              mDecodeFlags,
    +                              GetDecodeFlags(),
                                   mNewFrameData.mFormat,
                                   mNewFrameData.mPaletteDepth,
                                   mCurrentFrame.get());
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/Decoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/Decoder.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/Decoder.h	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/Decoder.h	2015-02-03 14:33:14.000000000 +0000
    @@ -108,7 +108,7 @@
       }
     
       // We're not COM-y, so we don't get refcounts by default
    -  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Decoder)
    +  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Decoder, MOZ_OVERRIDE)
     
       // Implement IResumable.
       virtual void Resume() MOZ_OVERRIDE;
    @@ -240,21 +240,14 @@
        */
       bool WasAborted() const { return mDecodeAborted; }
     
    -  // flags.  Keep these in sync with imgIContainer.idl.
    -  // SetDecodeFlags must be called before Init(), otherwise
    -  // default flags are assumed.
    -  enum {
    -    DECODER_NO_PREMULTIPLY_ALPHA = 0x2,     // imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA
    -    DECODER_NO_COLORSPACE_CONVERSION = 0x4  // imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION
    -  };
    -
       enum DecodeStyle {
           PROGRESSIVE, // produce intermediate frames representing the partial state of the image
           SEQUENTIAL // decode to final image immediately
       };
     
    -  void SetDecodeFlags(uint32_t aFlags) { mDecodeFlags = aFlags; }
    -  uint32_t GetDecodeFlags() { return mDecodeFlags; }
    +  void SetFlags(uint32_t aFlags) { mFlags = aFlags; }
    +  uint32_t GetFlags() const { return mFlags; }
    +  uint32_t GetDecodeFlags() const { return DecodeFlags(mFlags); }
     
       bool HasSize() const { return mImageMetadata.HasSize(); }
       void SetSizeOnImage();
    @@ -403,6 +396,13 @@
       bool NeedsToFlushData() const { return mNeedsToFlushData; }
     
       /**
    +   * CompleteDecode() finishes up the decoding process after Decode() determines
    +   * that we're finished. It records final progress and does all the cleanup
    +   * that's possible off-main-thread.
    +   */
    +  void CompleteDecode();
    +
    +  /**
        * Ensures that a given frame number exists with the given parameters, and
        * returns a RawAccessFrameRef for that frame.
        * It is not possible to create sparse frame arrays; you can only append
    @@ -450,13 +450,14 @@
       TimeDuration mDecodeTime;
       uint32_t mChunkCount;
     
    -  uint32_t mDecodeFlags;
    +  uint32_t mFlags;
       size_t mBytesDecoded;
       bool mSendPartialInvalidations;
       bool mDataDone;
       bool mDecodeDone;
       bool mDataError;
       bool mDecodeAborted;
    +  bool mShouldReportError;
       bool mImageIsTransient;
       bool mImageIsLocked;
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/imgFrame.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/imgFrame.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/imgFrame.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/imgFrame.cpp	2015-02-03 14:33:14.000000000 +0000
    @@ -421,8 +421,8 @@
     
       /* Figure out if the entire image is a constant color */
     
    -  // this should always be true
    -  if (mImageSurface->Stride() == mSize.width * 4) {
    +  if (gfxPrefs::ImageSingleColorOptimizationEnabled() &&
    +      mImageSurface->Stride() == mSize.width * 4) {
         uint32_t *imgData = (uint32_t*) ((uint8_t *)mVBufPtr);
         uint32_t firstPixel = * (uint32_t*) imgData;
         uint32_t pixelCount = mSize.width * mSize.height + 1;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/imgFrame.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/imgFrame.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/imgFrame.h	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/imgFrame.h	2015-02-03 14:33:14.000000000 +0000
    @@ -10,7 +10,6 @@
     #include "mozilla/MemoryReporting.h"
     #include "mozilla/Monitor.h"
     #include "mozilla/Move.h"
    -#include "mozilla/TypedEnum.h"
     #include "mozilla/VolatileBuffer.h"
     #include "gfxDrawable.h"
     #include "imgIContainer.h"
    @@ -23,7 +22,7 @@
     class DrawableFrameRef;
     class RawAccessFrameRef;
     
    -MOZ_BEGIN_ENUM_CLASS(BlendMethod, int8_t)
    +enum class BlendMethod : int8_t {
       // All color components of the frame, including alpha, overwrite the current
       // contents of the frame's output buffer region.
       SOURCE,
    @@ -31,20 +30,20 @@
       // The frame should be composited onto the output buffer based on its alpha,
       // using a simple OVER operation.
       OVER
    -MOZ_END_ENUM_CLASS(BlendMethod)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(DisposalMethod, int8_t)
    +enum class DisposalMethod : int8_t {
       CLEAR_ALL = -1,  // Clear the whole image, revealing what's underneath.
       NOT_SPECIFIED,   // Leave the frame and let the new frame draw on top.
       KEEP,            // Leave the frame and let the new frame draw on top.
       CLEAR,           // Clear the frame's area, revealing what's underneath.
       RESTORE_PREVIOUS // Restore the previous (composited) frame.
    -MOZ_END_ENUM_CLASS(DisposalMethod)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(Opacity, uint8_t)
    +enum class Opacity : uint8_t {
       OPAQUE,
       SOME_TRANSPARENCY
    -MOZ_END_ENUM_CLASS(Opacity)
    +};
     
     
     /**
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/imgLoader.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/imgLoader.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/imgLoader.h	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/imgLoader.h	2015-02-03 14:33:14.000000000 +0000
    @@ -206,10 +206,10 @@
       uint32_t mSize;
     };
     
    -MOZ_BEGIN_ENUM_CLASS(AcceptedMimeTypes, uint8_t)
    +enum class AcceptedMimeTypes : uint8_t {
       IMAGES,
       IMAGES_AND_DOCUMENTS,
    -MOZ_END_ENUM_CLASS(AcceptedMimeTypes)
    +};
     
     class imgLoader MOZ_FINAL : public imgILoader,
                                 public nsIContentSniffer,
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/Orientation.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/Orientation.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/Orientation.h	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/Orientation.h	2015-02-03 14:33:14.000000000 +0000
    @@ -7,22 +7,21 @@
     #define MOZILLA_IMAGELIB_ORIENTATION_H_
     
     #include 
    -#include "mozilla/TypedEnum.h"
     
     namespace mozilla {
     namespace image {
     
    -MOZ_BEGIN_ENUM_CLASS(Angle, uint8_t)
    +enum class Angle : uint8_t {
       D0,
       D90,
       D180,
       D270
    -MOZ_END_ENUM_CLASS(Angle)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(Flip, uint8_t)
    +enum class Flip : uint8_t {
       Unflipped,
       Horizontal
    -MOZ_END_ENUM_CLASS(Flip)
    +};
     
     /**
      * A struct that describes an image's orientation as a rotation optionally
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/RasterImage.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/RasterImage.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/RasterImage.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/RasterImage.cpp	2015-02-03 14:33:14.000000000 +0000
    @@ -61,16 +61,6 @@
     using std::ceil;
     using std::min;
     
    -// a mask for flags that will affect the decoding
    -#define DECODE_FLAGS_MASK (imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA | imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION)
    -#define DECODE_FLAGS_DEFAULT 0
    -
    -static uint32_t
    -DecodeFlags(uint32_t aFlags)
    -{
    -  return aFlags & DECODE_FLAGS_MASK;
    -}
    -
     // The maximum number of times any one RasterImage was decoded.  This is only
     // used for statistics.
     static int32_t sMaxDecodeCount = 0;
    @@ -340,7 +330,7 @@
       }
     
       // Create the initial size decoder.
    -  nsresult rv = Decode(DecodeStrategy::ASYNC, Nothing(), DECODE_FLAGS_DEFAULT);
    +  nsresult rv = Decode(Nothing(), DECODE_FLAGS_DEFAULT);
       if (NS_FAILED(rv)) {
         return NS_ERROR_FAILURE;
       }
    @@ -516,8 +506,7 @@
     DrawableFrameRef
     RasterImage::LookupFrame(uint32_t aFrameNum,
                              const nsIntSize& aSize,
    -                         uint32_t aFlags,
    -                         bool aShouldSyncNotify /* = true */)
    +                         uint32_t aFlags)
     {
       MOZ_ASSERT(NS_IsMainThread());
     
    @@ -536,10 +525,10 @@
         // The OS threw this frame away. We need to redecode if we can.
         MOZ_ASSERT(!mAnim, "Animated frames should be locked");
     
    -    WantDecodedFrames(ThebesIntSize(requestedSize), aFlags, aShouldSyncNotify);
    +    Decode(Some(ThebesIntSize(requestedSize)), aFlags);
     
         // If we can sync decode, we should already have the frame.
    -    if ((aFlags & FLAG_SYNC_DECODE) && aShouldSyncNotify) {
    +    if (aFlags & FLAG_SYNC_DECODE) {
           ref = LookupFrameInternal(aFrameNum, requestedSize, aFlags);
         }
       }
    @@ -558,8 +547,7 @@
       // Sync decoding guarantees that we got the frame, but if it's owned by an
       // async decoder that's currently running, the contents of the frame may not
       // be available yet. Make sure we get everything.
    -  if (ref && mHasSourceData && aShouldSyncNotify &&
    -      (aFlags & FLAG_SYNC_DECODE)) {
    +  if (ref && mHasSourceData && (aFlags & FLAG_SYNC_DECODE)) {
         ref->WaitUntilComplete();
       }
     
    @@ -614,16 +602,11 @@
     void
     RasterImage::OnSurfaceDiscarded()
     {
    -  if (!NS_IsMainThread()) {
    -    nsCOMPtr runnable =
    -      NS_NewRunnableMethod(this, &RasterImage::OnSurfaceDiscarded);
    -    NS_DispatchToMainThread(runnable);
    -    return;
    -  }
    +  MOZ_ASSERT(mProgressTracker);
     
    -  if (mProgressTracker) {
    -    mProgressTracker->OnDiscard();
    -  }
    +  nsCOMPtr runnable =
    +    NS_NewRunnableMethod(mProgressTracker, &ProgressTracker::OnDiscard);
    +  NS_DispatchToMainThread(runnable);
     }
     
     //******************************************************************************
    @@ -670,9 +653,7 @@
     }
     
     TemporaryRef
    -RasterImage::CopyFrame(uint32_t aWhichFrame,
    -                       uint32_t aFlags,
    -                       bool aShouldSyncNotify /* = true */)
    +RasterImage::CopyFrame(uint32_t aWhichFrame, uint32_t aFlags)
     {
       if (aWhichFrame > FRAME_MAX_VALUE)
         return nullptr;
    @@ -683,8 +664,8 @@
       // Get the frame. If it's not there, it's probably the caller's fault for
       // not waiting for the data to be loaded from the network or not passing
       // FLAG_SYNC_DECODE
    -  DrawableFrameRef frameRef = LookupFrame(GetRequestedFrameIndex(aWhichFrame),
    -                                          mSize, aFlags, aShouldSyncNotify);
    +  DrawableFrameRef frameRef =
    +    LookupFrame(GetRequestedFrameIndex(aWhichFrame), mSize, aFlags);
       if (!frameRef) {
         // The OS threw this frame away and we couldn't redecode it right now.
         return nullptr;
    @@ -742,9 +723,7 @@
     }
     
     TemporaryRef
    -RasterImage::GetFrameInternal(uint32_t aWhichFrame,
    -                              uint32_t aFlags,
    -                              bool aShouldSyncNotify /* = true */)
    +RasterImage::GetFrameInternal(uint32_t aWhichFrame, uint32_t aFlags)
     {
       MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
     
    @@ -757,8 +736,8 @@
       // Get the frame. If it's not there, it's probably the caller's fault for
       // not waiting for the data to be loaded from the network or not passing
       // FLAG_SYNC_DECODE
    -  DrawableFrameRef frameRef = LookupFrame(GetRequestedFrameIndex(aWhichFrame),
    -                                          mSize, aFlags, aShouldSyncNotify);
    +  DrawableFrameRef frameRef =
    +    LookupFrame(GetRequestedFrameIndex(aWhichFrame), mSize, aFlags);
       if (!frameRef) {
         // The OS threw this frame away and we couldn't redecode it.
         return nullptr;
    @@ -777,7 +756,7 @@
       // The image doesn't have a usable surface because it's been optimized away or
       // because it's a partial update frame from an animation. Create one.
       if (!frameSurf) {
    -    frameSurf = CopyFrame(aWhichFrame, aFlags, aShouldSyncNotify);
    +    frameSurf = CopyFrame(aWhichFrame, aFlags);
       }
     
       return frameSurf;
    @@ -790,7 +769,7 @@
       MOZ_ASSERT(aContainer);
     
       RefPtr surface =
    -    GetFrameInternal(FRAME_CURRENT, FLAG_NONE, /* aShouldSyncNotify = */ false);
    +    GetFrameInternal(FRAME_CURRENT, FLAG_ASYNC_NOTIFY);
       if (!surface) {
         // The OS threw out some or all of our buffer. We'll need to wait for the
         // redecode (which was automatically triggered by GetFrame) to complete.
    @@ -1153,7 +1132,7 @@
         // We need to guarantee that we've gotten the image's size, or at least
         // determined that we won't be able to get it, before we deliver the load
         // event. That means we have to do a synchronous size decode here.
    -    Decode(DecodeStrategy::SYNC_IF_POSSIBLE, Nothing(), DECODE_FLAGS_DEFAULT);
    +    Decode(Nothing(), FLAG_SYNC_DECODE);
       }
     
       // Determine our final status, giving precedence to Necko failure codes. We
    @@ -1364,7 +1343,7 @@
       decoder->SetSizeDecode(!aSize);
       decoder->SetSendPartialInvalidations(!mHasBeenDecoded);
       decoder->SetImageIsTransient(mTransient);
    -  decoder->SetDecodeFlags(DecodeFlags(aFlags));
    +  decoder->SetFlags(aFlags);
     
       if (!mHasBeenDecoded && aSize) {
         // Lock the image while we're decoding, so that it doesn't get evicted from
    @@ -1420,40 +1399,6 @@
       return decoder.forget();
     }
     
    -void
    -RasterImage::WantDecodedFrames(const nsIntSize& aSize, uint32_t aFlags,
    -                               bool aShouldSyncNotify)
    -{
    -  if (mDownscaleDuringDecode) {
    -    // We're about to decode again, which may mean that some of the previous
    -    // sizes we've decoded at aren't useful anymore. We can allow them to
    -    // expire from the cache by unlocking them here. When the decode finishes,
    -    // it will send an invalidation that will cause all instances of this image
    -    // to redraw. If this image is locked, any surfaces that are still useful
    -    // will become locked again when LookupFrame touches them, and the remainder
    -    // will eventually expire.
    -    SurfaceCache::UnlockSurfaces(ImageKey(this));
    -  }
    -
    -  if (aShouldSyncNotify) {
    -    // We can sync notify, which means we can also sync decode.
    -    if (aFlags & FLAG_SYNC_DECODE) {
    -      Decode(DecodeStrategy::SYNC_IF_POSSIBLE, Some(aSize), aFlags);
    -      return;
    -    }
    -
    -    // Here we are explicitly trading off flashing for responsiveness in the
    -    // case that we're redecoding an image (see bug 845147).
    -    Decode(mHasBeenDecoded ? DecodeStrategy::ASYNC
    -                           : DecodeStrategy::SYNC_FOR_SMALL_IMAGES,
    -           Some(aSize), aFlags);
    -    return;
    -  }
    -
    -  // We can't sync notify, so do an async decode.
    -  Decode(DecodeStrategy::ASYNC, Some(aSize), aFlags);
    -}
    -
     //******************************************************************************
     /* void requestDecode() */
     NS_IMETHODIMP
    @@ -1481,7 +1426,7 @@
         return NS_OK;
       }
     
    -  return RequestDecodeForSize(mSize, FLAG_SYNC_DECODE);
    +  return RequestDecodeForSize(mSize, FLAG_SYNC_DECODE_IF_FAST);
     }
     
     NS_IMETHODIMP
    @@ -1502,17 +1447,19 @@
       // downscale-during-decode.
       nsIntSize targetSize = mDownscaleDuringDecode ? aSize : mSize;
     
    -  // Sync decode small images if requested.
    -  bool shouldSyncDecodeSmallImages = aFlags & FLAG_SYNC_DECODE;
    +  // Decide whether to sync decode images we can decode quickly. Here we are
    +  // explicitly trading off flashing for responsiveness in the case that we're
    +  // redecoding an image (see bug 845147).
    +  bool shouldSyncDecodeIfFast =
    +    !mHasBeenDecoded && (aFlags & FLAG_SYNC_DECODE_IF_FAST);
    +
    +  uint32_t flags = shouldSyncDecodeIfFast
    +                 ? aFlags
    +                 : aFlags & ~FLAG_SYNC_DECODE_IF_FAST;
     
       // Look up the first frame of the image, which will implicitly start decoding
       // if it's not available right now.
    -  // XXX(seth): Passing true for aShouldSyncNotify here has the effect of
    -  // synchronously decoding small images, while passing false has the effect of
    -  // decoding asynchronously, but that's not obvious from the argument name.
    -  // This API needs to be reworked.
    -  LookupFrame(0, targetSize, DecodeFlags(aFlags),
    -              /* aShouldSyncNotify = */ shouldSyncDecodeSmallImages);
    +  LookupFrame(0, targetSize, flags);
     
       return NS_OK;
     }
    @@ -1525,9 +1472,7 @@
     }
     
     NS_IMETHODIMP
    -RasterImage::Decode(DecodeStrategy aStrategy,
    -                    const Maybe& aSize,
    -                    uint32_t aFlags)
    +RasterImage::Decode(const Maybe& aSize, uint32_t aFlags)
     {
       MOZ_ASSERT(!aSize || NS_IsMainThread());
     
    @@ -1547,6 +1492,17 @@
         return NS_ERROR_FAILURE;
       }
     
    +  if (mDownscaleDuringDecode && aSize) {
    +    // We're about to decode again, which may mean that some of the previous
    +    // sizes we've decoded at aren't useful anymore. We can allow them to
    +    // expire from the cache by unlocking them here. When the decode finishes,
    +    // it will send an invalidation that will cause all instances of this image
    +    // to redraw. If this image is locked, any surfaces that are still useful
    +    // will become locked again when LookupFrame touches them, and the remainder
    +    // will eventually expire.
    +    SurfaceCache::UnlockSurfaces(ImageKey(this));
    +  }
    +
       if (aSize) {
         // This isn't a size decode (which doesn't send any early notifications), so
         // send out notifications right away.
    @@ -1557,17 +1513,17 @@
     
       if (mHasSourceData) {
         // If we have all the data, we can sync decode if requested.
    -    if (aStrategy == DecodeStrategy::SYNC_FOR_SMALL_IMAGES) {
    -      PROFILER_LABEL_PRINTF("DecodePool", "SyncDecodeIfSmall",
    +    if (aFlags & FLAG_SYNC_DECODE) {
    +      PROFILER_LABEL_PRINTF("DecodePool", "SyncDecodeIfPossible",
             js::ProfileEntry::Category::GRAPHICS, "%s", GetURIString().get());
    -      DecodePool::Singleton()->SyncDecodeIfSmall(decoder);
    +      DecodePool::Singleton()->SyncDecodeIfPossible(decoder);
           return NS_OK;
         }
     
    -    if (aStrategy == DecodeStrategy::SYNC_IF_POSSIBLE) {
    -      PROFILER_LABEL_PRINTF("DecodePool", "SyncDecodeIfPossible",
    +    if (aFlags & FLAG_SYNC_DECODE_IF_FAST) {
    +      PROFILER_LABEL_PRINTF("DecodePool", "SyncDecodeIfSmall",
             js::ProfileEntry::Category::GRAPHICS, "%s", GetURIString().get());
    -      DecodePool::Singleton()->SyncDecodeIfPossible(decoder);
    +      DecodePool::Singleton()->SyncDecodeIfSmall(decoder);
           return NS_OK;
         }
       }
    @@ -1788,7 +1744,7 @@
       // Illegal -- you can't draw with non-default decode flags.
       // (Disabling colorspace conversion might make sense to allow, but
       // we don't currently.)
    -  if ((aFlags & DECODE_FLAGS_MASK) != DECODE_FLAGS_DEFAULT)
    +  if (DecodeFlags(aFlags) != DECODE_FLAGS_DEFAULT)
         return NS_ERROR_FAILURE;
     
       NS_ENSURE_ARG_POINTER(aContext);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/RasterImage.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/RasterImage.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/RasterImage.h	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/RasterImage.h	2015-02-03 14:33:14.000000000 +0000
    @@ -27,10 +27,10 @@
     #include "DecodePool.h"
     #include "Orientation.h"
     #include "nsIObserver.h"
    +#include "mozilla/Attributes.h"
     #include "mozilla/Maybe.h"
     #include "mozilla/MemoryReporting.h"
     #include "mozilla/TimeStamp.h"
    -#include "mozilla/TypedEnum.h"
     #include "mozilla/WeakPtr.h"
     #include "mozilla/UniquePtr.h"
     #ifdef DEBUG
    @@ -133,11 +133,16 @@
     class FrameAnimator;
     class SourceBuffer;
     
    -MOZ_BEGIN_ENUM_CLASS(DecodeStrategy, uint8_t)
    -  ASYNC,
    -  SYNC_FOR_SMALL_IMAGES,
    -  SYNC_IF_POSSIBLE
    -MOZ_END_ENUM_CLASS(DecodeStrategy)
    +/**
    + * Given a set of imgIContainer FLAG_* flags, returns those flags that can
    + * affect the output of decoders.
    + */
    +inline MOZ_CONSTEXPR uint32_t
    +DecodeFlags(uint32_t aFlags)
    +{
    +  return aFlags & (imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA |
    +                   imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION);
    +}
     
     class RasterImage MOZ_FINAL : public ImageResource
                                 , public nsIProperties
    @@ -218,7 +223,7 @@
        */
       void NotifyProgress(Progress aProgress,
                           const nsIntRect& aInvalidRect = nsIntRect(),
    -                      uint32_t aFlags = 0);
    +                      uint32_t aFlags = DECODE_FLAGS_DEFAULT);
     
       /**
        * Records telemetry and does final teardown of the provided decoder.
    @@ -291,19 +296,16 @@
                                         uint32_t aFlags);
     
       TemporaryRef CopyFrame(uint32_t aWhichFrame,
    -                                             uint32_t aFlags,
    -                                             bool aShouldSyncNotify = true);
    +                                             uint32_t aFlags);
       TemporaryRef GetFrameInternal(uint32_t aWhichFrame,
    -                                                    uint32_t aFlags,
    -                                                    bool aShouldSyncNotify = true);
    +                                                    uint32_t aFlags);
     
       DrawableFrameRef LookupFrameInternal(uint32_t aFrameNum,
                                            const gfx::IntSize& aSize,
                                            uint32_t aFlags);
       DrawableFrameRef LookupFrame(uint32_t aFrameNum,
                                    const nsIntSize& aSize,
    -                               uint32_t aFlags,
    -                               bool aShouldSyncNotify = true);
    +                               uint32_t aFlags);
       uint32_t GetCurrentFrameIndex() const;
       uint32_t GetRequestedFrameIndex(uint32_t aWhichFrame) const;
     
    @@ -329,13 +331,11 @@
     
       /**
        * Creates and runs a decoder, either synchronously or asynchronously
    -   * according to @aStrategy. Passes the provided target size @aSize and decode
    +   * according to @aFlags. Passes the provided target size @aSize and decode
        * flags @aFlags to CreateDecoder. If a size decode is desired, pass Nothing
        * for @aSize.
        */
    -  NS_IMETHOD Decode(DecodeStrategy aStrategy,
    -                    const Maybe& aSize,
    -                    uint32_t aFlags);
    +  NS_IMETHOD Decode(const Maybe& aSize, uint32_t aFlags);
     
       /**
        * Creates a new decoder with a target size of @aSize and decode flags
    @@ -345,9 +345,6 @@
       already_AddRefed CreateDecoder(const Maybe& aSize,
                                               uint32_t aFlags);
     
    -  void WantDecodedFrames(const nsIntSize& aSize, uint32_t aFlags,
    -                         bool aShouldSyncNotify);
    -
     private: // data
       nsIntSize                  mSize;
       Orientation                mOrientation;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/SourceBuffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/SourceBuffer.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/SourceBuffer.h	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/SourceBuffer.h	2015-02-03 14:33:14.000000000 +0000
    @@ -272,7 +272,6 @@
           , mLength(0)
         {
           MOZ_ASSERT(aCapacity > 0, "Creating zero-capacity chunk");
    -      static const fallible_t fallible = fallible_t();
           mData = new (fallible) char[mCapacity];
         }
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/SurfaceCache.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/SurfaceCache.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/SurfaceCache.cpp	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/SurfaceCache.cpp	2015-02-03 14:33:14.000000000 +0000
    @@ -362,7 +362,7 @@
       SurfaceCacheImpl(uint32_t aSurfaceCacheExpirationTimeMS,
                        uint32_t aSurfaceCacheDiscardFactor,
                        uint32_t aSurfaceCacheSize)
    -    : mExpirationTracker(this, aSurfaceCacheExpirationTimeMS)
    +    : mExpirationTracker(aSurfaceCacheExpirationTimeMS)
         , mMemoryPressureObserver(new MemoryPressureObserver)
         , mMutex("SurfaceCache")
         , mDiscardFactor(aSurfaceCacheDiscardFactor)
    @@ -742,6 +742,8 @@
                      nsISupports*             aData,
                      bool                     aAnonymize) MOZ_OVERRIDE
       {
    +    MutexAutoLock lock(mMutex);
    +
         // We have explicit memory reporting for the surface cache which is more
         // accurate than the cost metrics we report here, but these metrics are
         // still useful to report, since they control the cache's behavior.
    @@ -809,21 +811,18 @@
     
       struct SurfaceTracker : public nsExpirationTracker
       {
    -    SurfaceTracker(SurfaceCacheImpl* aCache, uint32_t aSurfaceCacheExpirationTimeMS)
    +    explicit SurfaceTracker(uint32_t aSurfaceCacheExpirationTimeMS)
           : nsExpirationTracker(aSurfaceCacheExpirationTimeMS)
    -      , mCache(aCache)
         { }
     
       protected:
         virtual void NotifyExpired(CachedSurface* aSurface) MOZ_OVERRIDE
         {
    -      if (mCache) {
    -        mCache->Remove(aSurface);
    +      if (sInstance) {
    +        MutexAutoLock lock(sInstance->GetMutex());
    +        sInstance->Remove(aSurface);
           }
         }
    -
    -  private:
    -    SurfaceCacheImpl* const mCache;  // Weak pointer to owner.
       };
     
       struct MemoryPressureObserver : public nsIObserver
    @@ -835,6 +834,7 @@
                            const char16_t*) MOZ_OVERRIDE
         {
           if (sInstance && strcmp(aTopic, "memory-pressure") == 0) {
    +        MutexAutoLock lock(sInstance->GetMutex());
             sInstance->DiscardForMemoryPressure();
           }
           return NS_OK;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/SurfaceCache.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/SurfaceCache.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/src/SurfaceCache.h	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/src/SurfaceCache.h	2015-02-03 14:33:14.000000000 +0000
    @@ -117,16 +117,16 @@
       return SurfaceKey(aSize, aSVGContext, aAnimationTime, 0);
     }
     
    -MOZ_BEGIN_ENUM_CLASS(Lifetime, uint8_t)
    +enum class Lifetime : uint8_t {
       Transient,
       Persistent
    -MOZ_END_ENUM_CLASS(Lifetime)
    +};
     
    -MOZ_BEGIN_ENUM_CLASS(InsertOutcome, uint8_t)
    +enum class InsertOutcome : uint8_t {
       SUCCESS,                 // Success (but see Insert documentation).
       FAILURE,                 // Couldn't insert (e.g., for capacity reasons).
       FAILURE_ALREADY_PRESENT  // A surface with the same key is already present.
    -MOZ_END_ENUM_CLASS(InsertOutcome)
    +};
     
     /**
      * SurfaceCache is an imagelib-global service that allows caching of temporary
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/test/browser/browser_bug666317.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/test/browser/browser_bug666317.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/image/test/browser/browser_bug666317.js	2015-01-25 22:24:23.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/image/test/browser/browser_bug666317.js	2015-02-03 14:33:14.000000000 +0000
    @@ -27,18 +27,6 @@
       return img.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
     }
     
    -function attachDiscardObserver(result) {
    -  // Create the discard observer.
    -  let observer = new ImageDiscardObserver(result);
    -  let scriptedObserver = Cc["@mozilla.org/image/tools;1"]
    -                           .getService(Ci.imgITools)
    -                           .createScriptedObserver(observer);
    -
    -  // Clone the current imgIRequest with our new observer.
    -  let request = currentRequest();
    -  return request.clone(scriptedObserver);
    -}
    -
     function isImgDecoded() {
       let request = currentRequest();
       return request.imageStatus & Ci.imgIRequest.STATUS_FRAME_COMPLETE ? true : false;
    @@ -69,9 +57,18 @@
     }
     
     function step2() {
    -  // Attach a discard listener and create a place to hold the result.
    +  // Create a place to hold the result.
       var result = { wasDiscarded: false };
    -  var clonedRequest = attachDiscardObserver(result);
    +
    +  // Create the discard observer.
    +  var observer = new ImageDiscardObserver(result);
    +  var scriptedObserver = Cc["@mozilla.org/image/tools;1"]
    +                           .getService(Ci.imgITools)
    +                           .createScriptedObserver(observer);
    +
    +  // Clone the current imgIRequest with our new observer.
    +  var request = currentRequest();
    +  var clonedRequest = request.clone(scriptedObserver);
     
       // Check that the image is decoded.
       forceDecodeImg();
    @@ -83,6 +80,19 @@
       var os = Cc["@mozilla.org/observer-service;1"]
                  .getService(Ci.nsIObserverService);
       os.notifyObservers(null, 'memory-pressure', 'heap-minimize');
    +
    +  // The discard notification is delivered asynchronously, so pump the event
    +  // loop before checking.
    +  window.addEventListener('message', function (event) {
    +    if (event.data == 'step3') {
    +      step3(result, scriptedObserver, clonedRequest);
    +    }
    +  }, false);
    +
    +  window.postMessage('step3', '*');
    +}
    +
    +function step3(result, scriptedObserver, clonedRequest) {
       ok(result.wasDiscarded, 'Image should be discarded.');
     
       // And we're done.
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/intl/locale/tests/unit/xpcshell.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/intl/locale/tests/unit/xpcshell.ini
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/intl/locale/tests/unit/xpcshell.ini	2015-01-25 22:24:27.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/intl/locale/tests/unit/xpcshell.ini	2015-02-03 14:33:22.000000000 +0000
    @@ -4,7 +4,7 @@
     skip-if = toolkit == 'gonk'
     
     [test_bug22310.js]
    -run-if = toolkit == "windows" || toolkit == "cocoa"
    +skip-if = toolkit != "windows" && toolkit != "cocoa"
     
     [test_bug371611.js]
     [test_bug374040.js]
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/intl/locale/unix/nsDateTimeFormatUnix.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/intl/locale/unix/nsDateTimeFormatUnix.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/intl/locale/unix/nsDateTimeFormatUnix.h	2015-01-25 22:24:27.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/intl/locale/unix/nsDateTimeFormatUnix.h	2015-02-03 14:33:22.000000000 +0000
    @@ -24,28 +24,28 @@
                             const nsDateFormatSelector  dateFormatSelector, 
                             const nsTimeFormatSelector timeFormatSelector, 
                             const time_t  timetTime, 
    -                        nsAString& stringOut); 
    +                        nsAString& stringOut) MOZ_OVERRIDE;
     
       // performs a locale sensitive date formatting operation on the struct tm parameter
       NS_IMETHOD FormatTMTime(nsILocale* locale, 
                             const nsDateFormatSelector  dateFormatSelector, 
                             const nsTimeFormatSelector timeFormatSelector, 
                             const struct tm*  tmTime, 
    -                        nsAString& stringOut); 
    +                        nsAString& stringOut) MOZ_OVERRIDE;
     
       // performs a locale sensitive date formatting operation on the PRTime parameter
       NS_IMETHOD FormatPRTime(nsILocale* locale, 
                               const nsDateFormatSelector  dateFormatSelector, 
                               const nsTimeFormatSelector timeFormatSelector, 
                               const PRTime  prTime, 
    -                          nsAString& stringOut);
    +                          nsAString& stringOut) MOZ_OVERRIDE;
     
       // performs a locale sensitive date formatting operation on the PRExplodedTime parameter
       NS_IMETHOD FormatPRExplodedTime(nsILocale* locale, 
                                       const nsDateFormatSelector  dateFormatSelector, 
                                       const nsTimeFormatSelector timeFormatSelector, 
                                       const PRExplodedTime*  explodedTime, 
    -                                  nsAString& stringOut); 
    +                                  nsAString& stringOut) MOZ_OVERRIDE;
     
     
       nsDateTimeFormatUnix() {mLocale.Truncate();mAppLocale.Truncate();}
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/intl/lwbrk/th_char.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/intl/lwbrk/th_char.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/intl/lwbrk/th_char.h	2015-01-25 22:24:27.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/intl/lwbrk/th_char.h	2015-02-03 14:33:22.000000000 +0000
    @@ -10,7 +10,6 @@
     */
     #ifndef __TH_CHAR_H__
     #define __TH_CHAR_H__
    -#include "nscore.h"
     
     
     typedef unsigned char	tis_char;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/intl/uconv/nsScriptableUConv.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/intl/uconv/nsScriptableUConv.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/intl/uconv/nsScriptableUConv.cpp	2015-01-25 22:24:27.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/intl/uconv/nsScriptableUConv.cpp	2015-02-03 14:33:22.000000000 +0000
    @@ -65,7 +65,7 @@
       nsresult rv = ConvertFromUnicodeWithLength(aSrc, &len, &str);
       if (NS_SUCCEEDED(rv)) {
         // No Adopt on nsACString :(
    -    if (!_retval.Assign(str, len, mozilla::fallible_t())) {
    +    if (!_retval.Assign(str, len, mozilla::fallible)) {
           rv = NS_ERROR_OUT_OF_MEMORY;
         }
         moz_free(str);
    @@ -114,7 +114,7 @@
       nsresult rv = FinishWithLength(&str, &len);
       if (NS_SUCCEEDED(rv)) {
         // No Adopt on nsACString :(
    -    if (!_retval.Assign(str, len, mozilla::fallible_t())) {
    +    if (!_retval.Assign(str, len, mozilla::fallible)) {
           rv = NS_ERROR_OUT_OF_MEMORY;
         }
         moz_free(str);
    @@ -160,7 +160,7 @@
         if (NS_SUCCEEDED(rv))
         {
           buf[outLength] = 0;
    -      if (!_retval.Assign(buf, outLength, mozilla::fallible_t())) {
    +      if (!_retval.Assign(buf, outLength, mozilla::fallible)) {
             rv = NS_ERROR_OUT_OF_MEMORY;
           }
         }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/ipc/glue/BackgroundChildImpl.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/ipc/glue/BackgroundChildImpl.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/ipc/glue/BackgroundChildImpl.cpp	2015-01-25 22:24:27.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/ipc/glue/BackgroundChildImpl.cpp	2015-02-03 14:33:24.000000000 +0000
    @@ -190,7 +190,10 @@
     BackgroundChildImpl::PVsyncChild*
     BackgroundChildImpl::AllocPVsyncChild()
     {
    -  return new mozilla::layout::VsyncChild();
    +  nsRefPtr actor = new mozilla::layout::VsyncChild();
    +  // There still has one ref-count after return, and it will be released in
    +  // DeallocPVsyncChild().
    +  return actor.forget().take();
     }
     
     bool
    @@ -198,7 +201,9 @@
     {
       MOZ_ASSERT(aActor);
     
    -  delete static_cast(aActor);
    +  // This actor already has one ref-count. Please check AllocPVsyncChild().
    +  nsRefPtr actor =
    +      dont_AddRef(static_cast(aActor));
       return true;
     }
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/ipc/glue/GeckoChildProcessHost.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/ipc/glue/GeckoChildProcessHost.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/ipc/glue/GeckoChildProcessHost.cpp	2015-01-25 22:24:27.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/ipc/glue/GeckoChildProcessHost.cpp	2015-02-03 14:33:24.000000000 +0000
    @@ -98,9 +98,7 @@
     #if defined(MOZ_SANDBOX) && defined(XP_WIN)
         mEnableSandboxLogging(false),
         mEnableNPAPISandbox(false),
    -#if defined(MOZ_CONTENT_SANDBOX)
    -    mMoreStrictContentSandbox(false),
    -#endif
    +    mMoreStrictSandbox(false),
     #endif
         mChildProcessHandle(0)
     #if defined(MOZ_WIDGET_COCOA)
    @@ -273,7 +271,7 @@
     #if defined(MOZ_CONTENT_SANDBOX)
       // We need to get the pref here as the process is launched off main thread.
       if (mProcessType == GeckoProcessType_Content) {
    -    mMoreStrictContentSandbox =
    +    mMoreStrictSandbox =
           Preferences::GetBool("security.sandbox.windows.content.moreStrict");
         mEnableSandboxLogging =
           Preferences::GetBool("security.sandbox.windows.log");
    @@ -807,7 +805,7 @@
         case GeckoProcessType_Content:
     #if defined(MOZ_CONTENT_SANDBOX)
           if (!PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
    -        mSandboxBroker.SetSecurityLevelForContentProcess(mMoreStrictContentSandbox);
    +        mSandboxBroker.SetSecurityLevelForContentProcess(mMoreStrictSandbox);
             cmdLine.AppendLooseValue(UTF8ToWide("-sandbox"));
             shouldSandboxCurrentProcess = true;
           }
    @@ -816,7 +814,7 @@
         case GeckoProcessType_Plugin:
           if (mEnableNPAPISandbox &&
               !PR_GetEnv("MOZ_DISABLE_NPAPI_SANDBOX")) {
    -        mSandboxBroker.SetSecurityLevelForPluginProcess();
    +        mSandboxBroker.SetSecurityLevelForPluginProcess(mMoreStrictSandbox);
             cmdLine.AppendLooseValue(UTF8ToWide("-sandbox"));
             shouldSandboxCurrentProcess = true;
           }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/ipc/glue/GeckoChildProcessHost.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/ipc/glue/GeckoChildProcessHost.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/ipc/glue/GeckoChildProcessHost.h	2015-01-25 22:24:27.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/ipc/glue/GeckoChildProcessHost.h	2015-02-03 14:33:24.000000000 +0000
    @@ -177,9 +177,7 @@
       // sandboxing in this class at some point. Unfortunately it will take a bit
       // of reorganizing so I don't think this patch is the right time.
       bool mEnableNPAPISandbox;
    -#if defined(MOZ_CONTENT_SANDBOX)
    -  bool mMoreStrictContentSandbox;
    -#endif
    +  bool mMoreStrictSandbox;
     #endif
     #endif // XP_WIN
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/ipc/glue/IPCMessageUtils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/ipc/glue/IPCMessageUtils.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/ipc/glue/IPCMessageUtils.h	2015-01-25 22:24:27.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/ipc/glue/IPCMessageUtils.h	2015-02-03 14:33:24.000000000 +0000
    @@ -16,7 +16,6 @@
     #ifdef XP_WIN
     #include "mozilla/TimeStamp_windows.h"
     #endif
    -#include "mozilla/TypedEnum.h"
     #include "mozilla/TypeTraits.h"
     #include "mozilla/IntegerTypeTraits.h"
     
    @@ -129,18 +128,7 @@
     template 
    -struct ContiguousEnumValidator
    -{
    -  static bool IsLegalValue(E e)
    -  {
    -    return MinLegal <= e && e < HighBound;
    -  }
    -};
    -
    -template 
    -class ContiguousTypedEnumValidator
    +class ContiguousEnumValidator
     {
       // Silence overzealous -Wtype-limits bug in GCC fixed in GCC 4.8:
       // "comparison of unsigned expression >= 0 is always true"
    @@ -151,9 +139,7 @@
     public:
       static bool IsLegalValue(E e)
       {
    -    typedef MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(E) ActualEnumType;
    -    return IsLessThanOrEqual(MinLegal, ActualEnumType(e)) &&
    -           ActualEnumType(e) < HighBound;
    +    return IsLessThanOrEqual(MinLegal, e) && e < HighBound;
       }
     };
     
    @@ -167,16 +153,6 @@
       }
     };
     
    -template 
    -struct BitFlagsTypedEnumValidator
    -{
    -  static bool IsLegalValue(E e)
    -  {
    -    return (e & AllBits) == e;
    -  }
    -};
    -
     /**
      * Specialization of EnumSerializer for enums with contiguous enum values.
      *
    @@ -202,19 +178,6 @@
     {};
     
     /**
    - * Similar to ContiguousEnumSerializer, but for MFBT typed enums
    - * as constructed by MOZ_BEGIN_ENUM_CLASS. This can go away when
    - * we drop MOZ_BEGIN_ENUM_CLASS and use C++11 enum classes directly.
    - */
    -template 
    -struct ContiguousTypedEnumSerializer
    -  : EnumSerializer>
    -{};
    -
    -/**
      * Specialization of EnumSerializer for enums representing bit flags.
      *
      * Provide one value: AllBits. An enum value x will be
    @@ -241,18 +204,6 @@
                        BitFlagsEnumValidator>
     {};
     
    -/**
    - * Similar to BitFlagsEnumSerializer, but for MFBT typed enums
    - * as constructed by MOZ_BEGIN_ENUM_CLASS. This can go away when
    - * we drop MOZ_BEGIN_ENUM_CLASS and use C++11 enum classes directly.
    - */
    -template 
    -struct BitFlagsTypedEnumSerializer
    -  : EnumSerializer>
    -{};
    -
     template <>
     struct ParamTraits
       : public ContiguousEnumSerializer
    +struct ParamTraits> : ParamTraits>
    +{
    +  typedef nsAutoTArray paramType;
    +};
    +
     template<>
     struct ParamTraits
     {
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/ipc/ipdl/ipdl/type.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/ipc/ipdl/ipdl/type.py
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/ipc/ipdl/ipdl/type.py	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/ipc/ipdl/ipdl/type.py	2015-02-03 14:33:24.000000000 +0000
    @@ -218,6 +218,12 @@
                 lesser.priorityRange[1] > greater.priorityRange[1]):
                 return False
     
    +        # Protocols that use intr semantics are not allowed to use
    +        # message priorities.
    +        if (greater.isInterrupt() and
    +            lesser.priorityRange != (NORMAL_PRIORITY, NORMAL_PRIORITY)):
    +            return False
    +
             if lesser.isAsync():
                 return True
             elif lesser.isSync() and not greater.isAsync():
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/ipc/CpowHolder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/ipc/CpowHolder.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/ipc/CpowHolder.h	1970-01-01 00:00:00.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/ipc/CpowHolder.h	2015-02-03 14:33:24.000000000 +0000
    @@ -0,0 +1,25 @@
    +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
    + * vim: set ts=8 sw=4 et tw=80:
    + *
    + * This Source Code Form is subject to the terms of the Mozilla Public
    + * License, v. 2.0. If a copy of the MPL was not distributed with this
    + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
    +
    +#ifndef mozilla_jsipc_CpowHolder_h__
    +#define mozilla_jsipc_CpowHolder_h__
    +
    +#include "js/TypeDecls.h"
    +
    +namespace mozilla {
    +namespace jsipc {
    +
    +class CpowHolder
    +{
    +  public:
    +    virtual bool ToObject(JSContext* cx, JS::MutableHandle objp) = 0;
    +};
    +
    +} // namespace jsipc
    +} // namespace mozilla
    +
    +#endif // mozilla_jsipc_CpowHolder_h__
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/ipc/CrossProcessObjectWrappers.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/ipc/CrossProcessObjectWrappers.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/ipc/CrossProcessObjectWrappers.h	1970-01-01 00:00:00.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/ipc/CrossProcessObjectWrappers.h	2015-02-03 14:33:24.000000000 +0000
    @@ -0,0 +1,95 @@
    +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
    + * vim: set ts=8 sw=4 et tw=80:
    + *
    + * This Source Code Form is subject to the terms of the Mozilla Public
    + * License, v. 2.0. If a copy of the MPL was not distributed with this
    + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
    +
    +#ifndef mozilla_jsipc_CrossProcessObjectWrappers_h__
    +#define mozilla_jsipc_CrossProcessObjectWrappers_h__
    +
    +#include "js/TypeDecls.h"
    +#include "mozilla/jsipc/CpowHolder.h"
    +#include "mozilla/jsipc/JavaScriptTypes.h"
    +#include "nsID.h"
    +#include "nsString.h"
    +#include "nsTArray.h"
    +
    +#ifdef XP_WIN
    +#undef GetClassName
    +#undef GetClassInfo
    +#endif
    +
    +namespace mozilla {
    +
    +namespace dom {
    +class CPOWManagerGetter;
    +}
    +
    +namespace jsipc {
    +
    +class PJavaScriptParent;
    +class PJavaScriptChild;
    +
    +class CPOWManager
    +{
    +  public:
    +    virtual bool Unwrap(JSContext *cx,
    +                        const InfallibleTArray &aCpows,
    +                        JS::MutableHandleObject objp) = 0;
    +
    +    virtual bool Wrap(JSContext *cx,
    +                      JS::HandleObject aObj,
    +                      InfallibleTArray *outCpows) = 0;
    +};
    +
    +class CrossProcessCpowHolder : public CpowHolder
    +{
    +  public:
    +    CrossProcessCpowHolder(dom::CPOWManagerGetter *managerGetter,
    +                           const InfallibleTArray &cpows);
    +
    +    bool ToObject(JSContext *cx, JS::MutableHandleObject objp);
    +
    +  private:
    +    CPOWManager *js_;
    +    const InfallibleTArray &cpows_;
    +};
    +
    +CPOWManager*
    +CPOWManagerFor(PJavaScriptParent* aParent);
    +
    +CPOWManager*
    +CPOWManagerFor(PJavaScriptChild* aChild);
    +
    +bool
    +IsCPOW(JSObject *obj);
    +
    +bool
    +IsWrappedCPOW(JSObject *obj);
    +
    +nsresult
    +InstanceOf(JSObject *obj, const nsID *id, bool *bp);
    +
    +bool
    +DOMInstanceOf(JSContext *cx, JSObject *obj, int prototypeID, int depth, bool *bp);
    +
    +void
    +GetWrappedCPOWTag(JSObject *obj, nsACString &out);
    +
    +PJavaScriptParent *
    +NewJavaScriptParent(JSRuntime *rt);
    +
    +void
    +ReleaseJavaScriptParent(PJavaScriptParent *parent);
    +
    +PJavaScriptChild *
    +NewJavaScriptChild(JSRuntime *rt);
    +
    +void
    +ReleaseJavaScriptChild(PJavaScriptChild *child);
    +
    +} // namespace jsipc
    +} // namespace mozilla
    +
    +#endif // mozilla_jsipc_CrossProcessObjectWrappers_h__
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/ipc/JavaScriptChild.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/ipc/JavaScriptChild.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/ipc/JavaScriptChild.cpp	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/ipc/JavaScriptChild.cpp	2015-02-03 14:33:24.000000000 +0000
    @@ -64,3 +64,20 @@
         // scope so that they can benefit from XrayWrappers in the child.
         return xpc::PrivilegedJunkScope();
     }
    +
    +PJavaScriptChild *
    +mozilla::jsipc::NewJavaScriptChild(JSRuntime *rt)
    +{
    +    JavaScriptChild *child = new JavaScriptChild(rt);
    +    if (!child->init()) {
    +        delete child;
    +        return nullptr;
    +    }
    +    return child;
    +}
    +
    +void
    +mozilla::jsipc::ReleaseJavaScriptChild(PJavaScriptChild *child)
    +{
    +    static_cast(child)->decref();
    +}
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/ipc/JavaScriptParent.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/ipc/JavaScriptParent.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/ipc/JavaScriptParent.cpp	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/ipc/JavaScriptParent.cpp	2015-02-03 14:33:24.000000000 +0000
    @@ -76,3 +76,20 @@
         }
         return actor.forget();
     }
    +
    +PJavaScriptParent *
    +mozilla::jsipc::NewJavaScriptParent(JSRuntime *rt)
    +{
    +    JavaScriptParent *parent = new JavaScriptParent(rt);
    +    if (!parent->init()) {
    +        delete parent;
    +        return nullptr;
    +    }
    +    return parent;
    +}
    +
    +void
    +mozilla::jsipc::ReleaseJavaScriptParent(PJavaScriptParent *parent)
    +{
    +    static_cast(parent)->decref();
    +}
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/ipc/JavaScriptShared.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/ipc/JavaScriptShared.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/ipc/JavaScriptShared.cpp	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/ipc/JavaScriptShared.cpp	2015-02-03 14:33:24.000000000 +0000
    @@ -672,7 +672,8 @@
         return fromObjectVariant(cx, objVar.get_ObjectVariant());
     }
     
    -CpowIdHolder::CpowIdHolder(dom::CPOWManagerGetter *managerGetter, const InfallibleTArray &cpows)
    +CrossProcessCpowHolder::CrossProcessCpowHolder(dom::CPOWManagerGetter *managerGetter,
    +                                               const InfallibleTArray &cpows)
       : js_(nullptr),
         cpows_(cpows)
     {
    @@ -682,7 +683,7 @@
     }
     
     bool
    -CpowIdHolder::ToObject(JSContext *cx, JS::MutableHandleObject objp)
    +CrossProcessCpowHolder::ToObject(JSContext *cx, JS::MutableHandleObject objp)
     {
         if (!cpows_.Length())
             return true;
    @@ -757,3 +758,15 @@
     
         return true;
     }
    +
    +CPOWManager*
    +mozilla::jsipc::CPOWManagerFor(PJavaScriptParent* aParent)
    +{
    +    return static_cast(aParent);
    +}
    +
    +CPOWManager*
    +mozilla::jsipc::CPOWManagerFor(PJavaScriptChild* aChild)
    +{
    +    return static_cast(aChild);
    +}
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/ipc/JavaScriptShared.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/ipc/JavaScriptShared.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/ipc/JavaScriptShared.h	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/ipc/JavaScriptShared.h	2015-02-03 14:33:24.000000000 +0000
    @@ -9,9 +9,9 @@
     #define mozilla_jsipc_JavaScriptShared_h__
     
     #include "mozilla/dom/DOMTypes.h"
    +#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
     #include "mozilla/jsipc/PJavaScript.h"
     #include "nsJSUtils.h"
    -#include "nsFrameMessageManager.h"
     
     namespace mozilla {
     
    @@ -65,18 +65,6 @@
     
     class JavaScriptShared;
     
    -class CpowIdHolder : public CpowHolder
    -{
    -  public:
    -    CpowIdHolder(dom::CPOWManagerGetter *managerGetter, const InfallibleTArray &cpows);
    -
    -    bool ToObject(JSContext *cx, JS::MutableHandleObject objp);
    -
    -  private:
    -    JavaScriptShared *js_;
    -    const InfallibleTArray &cpows_;
    -};
    -
     // DefaultHasher requires that T coerce to an integral type. We could make
     // ObjectId do that, but doing so would weaken our type invariants, so we just
     // reimplement it manually.
    @@ -144,7 +132,7 @@
     
     class Logging;
     
    -class JavaScriptShared
    +class JavaScriptShared : public CPOWManager
     {
       public:
         explicit JavaScriptShared(JSRuntime *rt);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/ipc/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/ipc/moz.build
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/ipc/moz.build	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/ipc/moz.build	2015-02-03 14:33:24.000000000 +0000
    @@ -26,6 +26,11 @@
     
     DEFINES['BIN_SUFFIX'] = '"%s"' % CONFIG['BIN_SUFFIX']
     
    +EXPORTS.mozilla.jsipc = [
    +    'CpowHolder.h',
    +    'CrossProcessObjectWrappers.h',
    +]
    +
     LOCAL_INCLUDES += [
         '/dom/base',
         '/js/ipc',
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/ipc/WrapperOwner.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/ipc/WrapperOwner.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/ipc/WrapperOwner.h	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/ipc/WrapperOwner.h	2015-02-03 14:33:24.000000000 +0000
    @@ -10,14 +10,10 @@
     
     #include "JavaScriptShared.h"
     #include "mozilla/ipc/ProtocolUtils.h"
    +#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
     #include "js/Class.h"
     #include "jsproxy.h"
     
    -#ifdef XP_WIN
    -#undef GetClassName
    -#undef GetClassInfo
    -#endif
    -
     namespace mozilla {
     namespace jsipc {
     
    @@ -149,21 +145,6 @@
                                        ReturnStatus *rs, bool *instanceof) = 0;
     };
     
    -bool
    -IsCPOW(JSObject *obj);
    -
    -bool
    -IsWrappedCPOW(JSObject *obj);
    -
    -nsresult
    -InstanceOf(JSObject *obj, const nsID *id, bool *bp);
    -
    -bool
    -DOMInstanceOf(JSContext *cx, JSObject *obj, int prototypeID, int depth, bool *bp);
    -
    -void
    -GetWrappedCPOWTag(JSObject *obj, nsACString &out);
    -
     } // jsipc
     } // mozilla
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/public/Class.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/public/Class.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/public/Class.h	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/public/Class.h	2015-02-03 14:33:24.000000000 +0000
    @@ -162,49 +162,24 @@
     namespace js {
     
     typedef bool
    -(* LookupGenericOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
    -                    JS::MutableHandleObject objp, JS::MutableHandle propp);
    +(* LookupPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
    +                     JS::MutableHandleObject objp, JS::MutableHandle propp);
     typedef bool
    -(* LookupPropOp)(JSContext *cx, JS::HandleObject obj, JS::Handle name,
    -                 JS::MutableHandleObject objp, JS::MutableHandle propp);
    +(* DefinePropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue value,
    +                     JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs);
     typedef bool
    -(* LookupElementOp)(JSContext *cx, JS::HandleObject obj, uint32_t index,
    -                    JS::MutableHandleObject objp, JS::MutableHandle propp);
    +(* GetPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleObject receiver, JS::HandleId id,
    +                  JS::MutableHandleValue vp);
     typedef bool
    -(* DefineGenericOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue value,
    -                    JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs);
    -typedef bool
    -(* DefinePropOp)(JSContext *cx, JS::HandleObject obj, JS::Handle name,
    -                 JS::HandleValue value, JSPropertyOp getter, JSStrictPropertyOp setter,
    -                 unsigned attrs);
    -typedef bool
    -(* DefineElementOp)(JSContext *cx, JS::HandleObject obj, uint32_t index, JS::HandleValue value,
    -                    JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs);
    -typedef bool
    -(* GenericIdOp)(JSContext *cx, JS::HandleObject obj, JS::HandleObject receiver, JS::HandleId id,
    -                JS::MutableHandleValue vp);
    -typedef bool
    -(* PropertyIdOp)(JSContext *cx, JS::HandleObject obj, JS::HandleObject receiver,
    -                 JS::Handle name, JS::MutableHandleValue vp);
    -typedef bool
    -(* ElementIdOp)(JSContext *cx, JS::HandleObject obj, JS::HandleObject receiver, uint32_t index,
    -                JS::MutableHandleValue vp);
    -typedef bool
    -(* StrictGenericIdOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
    -                      JS::MutableHandleValue vp, bool strict);
    -typedef bool
    -(* StrictPropertyIdOp)(JSContext *cx, JS::HandleObject obj, JS::Handle name,
    -                       JS::MutableHandleValue vp, bool strict);
    -typedef bool
    -(* StrictElementIdOp)(JSContext *cx, JS::HandleObject obj, uint32_t index,
    -                      JS::MutableHandleValue vp, bool strict);
    +(* SetPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
    +                  JS::MutableHandleValue vp, bool strict);
     typedef bool
     (* GetOwnPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
                          JS::MutableHandle desc);
     typedef bool
    -(* GenericAttributesOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, unsigned *attrsp);
    +(* SetAttributesOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, unsigned *attrsp);
     typedef bool
    -(* DeleteGenericOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *succeeded);
    +(* DeletePropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *succeeded);
     
     typedef bool
     (* WatchOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable);
    @@ -366,21 +341,13 @@
     
     struct ObjectOps
     {
    -    LookupGenericOp     lookupGeneric;
    -    LookupPropOp        lookupProperty;
    -    LookupElementOp     lookupElement;
    -    DefineGenericOp     defineGeneric;
    -    DefinePropOp        defineProperty;
    -    DefineElementOp     defineElement;
    -    GenericIdOp         getGeneric;
    -    PropertyIdOp        getProperty;
    -    ElementIdOp         getElement;
    -    StrictGenericIdOp   setGeneric;
    -    StrictPropertyIdOp  setProperty;
    -    StrictElementIdOp   setElement;
    +    LookupPropertyOp    lookupProperty;
    +    DefinePropertyOp    defineProperty;
    +    GetPropertyOp       getProperty;
    +    SetPropertyOp       setProperty;
         GetOwnPropertyOp    getOwnPropertyDescriptor;
    -    GenericAttributesOp setGenericAttributes;
    -    DeleteGenericOp     deleteGeneric;
    +    SetAttributesOp     setAttributes;
    +    DeletePropertyOp    deleteProperty;
         WatchOp             watch;
         UnwatchOp           unwatch;
         GetElementsOp       getElements;
    @@ -390,7 +357,6 @@
     
     #define JS_NULL_OBJECT_OPS                                                    \
         {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,  \
    -     nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,  \
          nullptr, nullptr, nullptr, nullptr}
     
     } // namespace js
    @@ -402,7 +368,7 @@
     struct JSClass {
         JS_CLASS_MEMBERS(JSFinalizeOp);
     
    -    void                *reserved[32];
    +    void                *reserved[24];
     };
     
     #define JSCLASS_HAS_PRIVATE             (1<<0)  // objects have private slot
    @@ -584,7 +550,11 @@
     enum ESClassValue {
         ESClass_Object, ESClass_Array, ESClass_Number, ESClass_String,
         ESClass_Boolean, ESClass_RegExp, ESClass_ArrayBuffer, ESClass_SharedArrayBuffer,
    -    ESClass_Date, ESClass_Set, ESClass_Map
    +    ESClass_Date, ESClass_Set, ESClass_Map,
    +
    +    // Special snowflake for the ES6 IsArray method.
    +    // Please don't use it without calling that function.
    +    ESClass_IsArray
     };
     
     /*
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/public/Conversions.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/public/Conversions.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/public/Conversions.h	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/public/Conversions.h	2015-02-03 14:33:24.000000000 +0000
    @@ -15,10 +15,228 @@
     
     #include 
     
    +#include "jspubtd.h"
    +
    +#include "js/RootingAPI.h"
    +#include "js/Value.h"
    +
    +struct JSContext;
    +
    +namespace js {
    +
    +/* DO NOT CALL THIS. Use JS::ToBoolean. */
    +extern JS_PUBLIC_API(bool)
    +ToBooleanSlow(JS::HandleValue v);
    +
    +/* DO NOT CALL THIS.  Use JS::ToNumber. */
    +extern JS_PUBLIC_API(bool)
    +ToNumberSlow(JSContext *cx, JS::Value v, double *dp);
    +
    +/* DO NOT CALL THIS. Use JS::ToInt32. */
    +extern JS_PUBLIC_API(bool)
    +ToInt32Slow(JSContext *cx, JS::HandleValue v, int32_t *out);
    +
    +/* DO NOT CALL THIS. Use JS::ToUint32. */
    +extern JS_PUBLIC_API(bool)
    +ToUint32Slow(JSContext *cx, JS::HandleValue v, uint32_t *out);
    +
    +/* DO NOT CALL THIS. Use JS::ToUint16. */
    +extern JS_PUBLIC_API(bool)
    +ToUint16Slow(JSContext *cx, JS::HandleValue v, uint16_t *out);
    +
    +/* DO NOT CALL THIS. Use JS::ToInt64. */
    +extern JS_PUBLIC_API(bool)
    +ToInt64Slow(JSContext *cx, JS::HandleValue v, int64_t *out);
    +
    +/* DO NOT CALL THIS. Use JS::ToUint64. */
    +extern JS_PUBLIC_API(bool)
    +ToUint64Slow(JSContext *cx, JS::HandleValue v, uint64_t *out);
    +
    +/* DO NOT CALL THIS. Use JS::ToString. */
    +extern JS_PUBLIC_API(JSString*)
    +ToStringSlow(JSContext *cx, JS::HandleValue v);
    +
    +/* DO NOT CALL THIS. Use JS::ToObject. */
    +extern JS_PUBLIC_API(JSObject*)
    +ToObjectSlow(JSContext *cx, JS::HandleValue v, bool reportScanStack);
    +
    +} // namespace js
    +
     namespace JS {
     
     namespace detail {
     
    +#ifdef JS_DEBUG
    +/*
    + * Assert that we're not doing GC on cx, that we're in a request as
    + * needed, and that the compartments for cx and v are correct.
    + * Also check that GC would be safe at this point.
    + */
    +extern JS_PUBLIC_API(void)
    +AssertArgumentsAreSane(JSContext *cx, HandleValue v);
    +#else
    +inline void AssertArgumentsAreSane(JSContext *cx, HandleValue v)
    +{}
    +#endif /* JS_DEBUG */
    +
    +} // namespace detail
    +
    +/*
    + * ES6 draft 20141224, 7.1.1, second algorithm.
    + *
    + * Most users shouldn't call this -- use JS::ToBoolean, ToNumber, or ToString
    + * instead.  This will typically only be called from custom convert hooks that
    + * wish to fall back to the ES6 default conversion behavior shared by most
    + * objects in JS, codified as OrdinaryToPrimitive.
    + */
    +extern JS_PUBLIC_API(bool)
    +OrdinaryToPrimitive(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp);
    +
    +/* ES6 draft 20141224, 7.1.2. */
    +MOZ_ALWAYS_INLINE bool
    +ToBoolean(HandleValue v)
    +{
    +    if (v.isBoolean())
    +        return v.toBoolean();
    +    if (v.isInt32())
    +        return v.toInt32() != 0;
    +    if (v.isNullOrUndefined())
    +        return false;
    +    if (v.isDouble()) {
    +        double d = v.toDouble();
    +        return !mozilla::IsNaN(d) && d != 0;
    +    }
    +    if (v.isSymbol())
    +        return true;
    +
    +    /* The slow path handles strings and objects. */
    +    return js::ToBooleanSlow(v);
    +}
    +
    +/* ES6 draft 20141224, 7.1.3. */
    +MOZ_ALWAYS_INLINE bool
    +ToNumber(JSContext *cx, HandleValue v, double *out)
    +{
    +    detail::AssertArgumentsAreSane(cx, v);
    +
    +    if (v.isNumber()) {
    +        *out = v.toNumber();
    +        return true;
    +    }
    +    return js::ToNumberSlow(cx, v, out);
    +}
    +
    +/* ES6 draft 20141224, ToInteger (specialized for doubles). */
    +inline double
    +ToInteger(double d)
    +{
    +    if (d == 0)
    +        return d;
    +
    +    if (!mozilla::IsFinite(d)) {
    +        if (mozilla::IsNaN(d))
    +            return 0;
    +        return d;
    +    }
    +
    +    return d < 0 ? ceil(d) : floor(d);
    +}
    +
    +/* ES6 draft 20141224, 7.1.5. */
    +MOZ_ALWAYS_INLINE bool
    +ToInt32(JSContext *cx, JS::HandleValue v, int32_t *out)
    +{
    +    detail::AssertArgumentsAreSane(cx, v);
    +
    +    if (v.isInt32()) {
    +        *out = v.toInt32();
    +        return true;
    +    }
    +    return js::ToInt32Slow(cx, v, out);
    +}
    +
    +/* ES6 draft 20141224, 7.1.6. */
    +MOZ_ALWAYS_INLINE bool
    +ToUint32(JSContext *cx, HandleValue v, uint32_t *out)
    +{
    +    detail::AssertArgumentsAreSane(cx, v);
    +
    +    if (v.isInt32()) {
    +        *out = uint32_t(v.toInt32());
    +        return true;
    +    }
    +    return js::ToUint32Slow(cx, v, out);
    +}
    +
    +/* ES6 draft 20141224, 7.1.8. */
    +MOZ_ALWAYS_INLINE bool
    +ToUint16(JSContext *cx, HandleValue v, uint16_t *out)
    +{
    +    detail::AssertArgumentsAreSane(cx, v);
    +
    +    if (v.isInt32()) {
    +        *out = uint16_t(v.toInt32());
    +        return true;
    +    }
    +    return js::ToUint16Slow(cx, v, out);
    +}
    +
    +/*
    + * Non-standard, with behavior similar to that of ToInt32, except in its
    + * producing an int64_t.
    + */
    +MOZ_ALWAYS_INLINE bool
    +ToInt64(JSContext *cx, HandleValue v, int64_t *out)
    +{
    +    detail::AssertArgumentsAreSane(cx, v);
    +
    +    if (v.isInt32()) {
    +        *out = int64_t(v.toInt32());
    +        return true;
    +    }
    +    return js::ToInt64Slow(cx, v, out);
    +}
    +
    +/*
    + * Non-standard, with behavior similar to that of ToUint32, except in its
    + * producing a uint64_t.
    + */
    +MOZ_ALWAYS_INLINE bool
    +ToUint64(JSContext *cx, HandleValue v, uint64_t *out)
    +{
    +    detail::AssertArgumentsAreSane(cx, v);
    +
    +    if (v.isInt32()) {
    +        *out = uint64_t(v.toInt32());
    +        return true;
    +    }
    +    return js::ToUint64Slow(cx, v, out);
    +}
    +
    +/* ES6 draft 20141224, 7.1.12. */
    +MOZ_ALWAYS_INLINE JSString*
    +ToString(JSContext *cx, HandleValue v)
    +{
    +    detail::AssertArgumentsAreSane(cx, v);
    +
    +    if (v.isString())
    +        return v.toString();
    +    return js::ToStringSlow(cx, v);
    +}
    +
    +/* ES6 draft 20141224, 7.1.13. */
    +inline JSObject *
    +ToObject(JSContext *cx, HandleValue v)
    +{
    +    detail::AssertArgumentsAreSane(cx, v);
    +
    +    if (v.isObject())
    +        return &v.toObject();
    +    return js::ToObjectSlow(cx, v, false);
    +}
    +
    +namespace detail {
    +
     /*
      * Convert a double value to ResultType (an unsigned integral type) using
      * ECMAScript-style semantics (that is, in like manner to how ECMAScript's
    @@ -278,22 +496,6 @@
         return detail::ToUintWidth(d);
     }
     
    -/* ES5 9.4 ToInteger (specialized for doubles). */
    -inline double
    -ToInteger(double d)
    -{
    -    if (d == 0)
    -        return d;
    -
    -    if (!mozilla::IsFinite(d)) {
    -        if (mozilla::IsNaN(d))
    -            return 0;
    -        return d;
    -    }
    -
    -    return d < 0 ? ceil(d) : floor(d);
    -}
    -
     } // namespace JS
     
     #endif /* js_Conversions_h */
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/public/GCAPI.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/public/GCAPI.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/public/GCAPI.h	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/public/GCAPI.h	2015-02-03 14:33:24.000000000 +0000
    @@ -303,21 +303,6 @@
     IsIncrementalGCEnabled(JSRuntime *rt);
     
     /*
    - * Compacting GC defaults to enabled, but may be disabled for testing or in
    - * embeddings that have not implemented the necessary object moved hooks or weak
    - * pointer callbacks.  There is not currently a way to re-enable compacting GC
    - * once it has been disabled on the runtime.
    - */
    -extern JS_PUBLIC_API(void)
    -DisableCompactingGC(JSRuntime *rt);
    -
    -/*
    - * Returns true if compacting GC is enabled.
    - */
    -extern JS_PUBLIC_API(bool)
    -IsCompactingGCEnabled(JSRuntime *rt);
    -
    -/*
      * Returns true while an incremental GC is ongoing, both when actively
      * collecting and between slices.
      */
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/public/HashTable.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/public/HashTable.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/public/HashTable.h	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/public/HashTable.h	2015-02-03 14:33:24.000000000 +0000
    @@ -253,10 +253,13 @@
                 rekeyAs(old_key, new_key, new_key);
         }
     
    -    // Infallibly rekey one entry, if present.
    -    void rekeyAs(const Lookup &old_lookup, const Lookup &new_lookup, const Key &new_key) {
    -        if (Ptr p = lookup(old_lookup))
    +    // Infallibly rekey one entry if present, and return whether that happened.
    +    bool rekeyAs(const Lookup &old_lookup, const Lookup &new_lookup, const Key &new_key) {
    +        if (Ptr p = lookup(old_lookup)) {
                 impl.rekeyAndMaybeRehash(p, new_lookup, new_key);
    +            return true;
    +        }
    +        return false;
         }
     
         // HashMap is movable
    @@ -471,10 +474,13 @@
                 rekeyAs(old_value, new_value, new_value);
         }
     
    -    // Infallibly rekey one entry, if present.
    -    void rekeyAs(const Lookup &old_lookup, const Lookup &new_lookup, const T &new_value) {
    -        if (Ptr p = lookup(old_lookup))
    +    // Infallibly rekey one entry if present, and return whether that happened.
    +    bool rekeyAs(const Lookup &old_lookup, const Lookup &new_lookup, const T &new_value) {
    +        if (Ptr p = lookup(old_lookup)) {
                 impl.rekeyAndMaybeRehash(p, new_lookup, new_value);
    +            return true;
    +        }
    +        return false;
         }
     
         // Infallibly rekey one entry with a new key that is equivalent.
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/public/MemoryMetrics.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/public/MemoryMetrics.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/public/MemoryMetrics.h	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/public/MemoryMetrics.h	2015-02-03 14:33:24.000000000 +0000
    @@ -444,8 +444,8 @@
         macro(Other,   IsLiveGCThing,  lazyScriptsGCHeap) \
         macro(Other,   NotLiveGCThing, lazyScriptsMallocHeap) \
         macro(Other,   IsLiveGCThing,  jitCodesGCHeap) \
    -    macro(Other,   IsLiveGCThing,  typeObjectsGCHeap) \
    -    macro(Other,   NotLiveGCThing, typeObjectsMallocHeap) \
    +    macro(Other,   IsLiveGCThing,  objectGroupsGCHeap) \
    +    macro(Other,   NotLiveGCThing, objectGroupsMallocHeap) \
         macro(Other,   NotLiveGCThing, typePool) \
         macro(Other,   NotLiveGCThing, baselineStubsOptimized) \
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/public/ProfilingStack.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/public/ProfilingStack.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/public/ProfilingStack.h	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/public/ProfilingStack.h	2015-02-03 14:33:24.000000000 +0000
    @@ -7,8 +7,6 @@
     #ifndef js_ProfilingStack_h
     #define js_ProfilingStack_h
     
    -#include "mozilla/TypedEnum.h"
    -
     #include "jsbytecode.h"
     #include "jstypes.h"
     
    @@ -73,7 +71,7 @@
         };
     
         // Keep these in sync with browser/devtools/profiler/utils/global.js
    -    MOZ_BEGIN_NESTED_ENUM_CLASS(Category, uint32_t)
    +    enum class Category : uint32_t {
             OTHER    = 0x10,
             CSS      = 0x20,
             JS       = 0x40,
    @@ -86,7 +84,7 @@
     
             FIRST    = OTHER,
             LAST     = EVENTS
    -    MOZ_END_NESTED_ENUM_CLASS(Category)
    +    };
     
         // All of these methods are marked with the 'volatile' keyword because SPS's
         // representation of the stack is stored such that all ProfileEntry
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/public/TracingAPI.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/public/TracingAPI.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/public/TracingAPI.h	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/public/TracingAPI.h	2015-02-03 14:33:24.000000000 +0000
    @@ -51,9 +51,9 @@
         JSTRACE_BASE_SHAPE = 0x0F,
         JSTRACE_JITCODE = 0x1F,
         JSTRACE_LAZY_SCRIPT = 0x2F,
    -    JSTRACE_TYPE_OBJECT = 0x3F,
    +    JSTRACE_OBJECT_GROUP = 0x3F,
     
    -    JSTRACE_LAST = JSTRACE_TYPE_OBJECT
    +    JSTRACE_LAST = JSTRACE_OBJECT_GROUP
     };
     
     namespace JS {
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/public/Utility.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/public/Utility.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/public/Utility.h	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/public/Utility.h	2015-02-03 14:33:24.000000000 +0000
    @@ -53,7 +53,7 @@
     #define JS_STATIC_ASSERT(cond)           static_assert(cond, "JS_STATIC_ASSERT")
     #define JS_STATIC_ASSERT_IF(cond, expr)  MOZ_STATIC_ASSERT_IF(cond, expr, "JS_STATIC_ASSERT_IF")
     
    -extern MOZ_NORETURN JS_PUBLIC_API(void)
    +extern MOZ_NORETURN MOZ_COLD JS_PUBLIC_API(void)
     JS_Assert(const char *s, const char *file, int ln);
     
     /*
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/asmjs/AsmJSModule.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/asmjs/AsmJSModule.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/asmjs/AsmJSModule.cpp	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/asmjs/AsmJSModule.cpp	2015-02-03 14:33:24.000000000 +0000
    @@ -453,7 +453,7 @@
     static void
     AsmJSReportOverRecursed()
     {
    -    JSContext *cx = PerThreadData::innermostAsmJSActivation()->cx();
    +    JSContext *cx = JSRuntime::innermostAsmJSActivation()->cx();
         js_ReportOverRecursed(cx);
     }
     
    @@ -461,14 +461,14 @@
     OnDetached()
     {
         // See hasDetachedHeap comment in LinkAsmJS.
    -    JSContext *cx = PerThreadData::innermostAsmJSActivation()->cx();
    +    JSContext *cx = JSRuntime::innermostAsmJSActivation()->cx();
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
     }
     
     static bool
     AsmJSHandleExecutionInterrupt()
     {
    -    AsmJSActivation *act = PerThreadData::innermostAsmJSActivation();
    +    AsmJSActivation *act = JSRuntime::innermostAsmJSActivation();
         act->module().setInterrupted(true);
         bool ret = CheckForInterrupt(act->cx());
         act->module().setInterrupted(false);
    @@ -478,7 +478,7 @@
     static int32_t
     CoerceInPlace_ToInt32(MutableHandleValue val)
     {
    -    JSContext *cx = PerThreadData::innermostAsmJSActivation()->cx();
    +    JSContext *cx = JSRuntime::innermostAsmJSActivation()->cx();
     
         int32_t i32;
         if (!ToInt32(cx, val, &i32))
    @@ -491,7 +491,7 @@
     static int32_t
     CoerceInPlace_ToNumber(MutableHandleValue val)
     {
    -    JSContext *cx = PerThreadData::innermostAsmJSActivation()->cx();
    +    JSContext *cx = JSRuntime::innermostAsmJSActivation()->cx();
     
         double dbl;
         if (!ToNumber(cx, val, &dbl))
    @@ -570,7 +570,7 @@
     static int32_t
     InvokeFromAsmJS_Ignore(int32_t exitIndex, int32_t argc, Value *argv)
     {
    -    AsmJSActivation *activation = PerThreadData::innermostAsmJSActivation();
    +    AsmJSActivation *activation = JSRuntime::innermostAsmJSActivation();
         JSContext *cx = activation->cx();
     
         RootedValue rval(cx);
    @@ -582,7 +582,7 @@
     static int32_t
     InvokeFromAsmJS_ToInt32(int32_t exitIndex, int32_t argc, Value *argv)
     {
    -    AsmJSActivation *activation = PerThreadData::innermostAsmJSActivation();
    +    AsmJSActivation *activation = JSRuntime::innermostAsmJSActivation();
         JSContext *cx = activation->cx();
     
         RootedValue rval(cx);
    @@ -602,7 +602,7 @@
     static int32_t
     InvokeFromAsmJS_ToNumber(int32_t exitIndex, int32_t argc, Value *argv)
     {
    -    AsmJSActivation *activation = PerThreadData::innermostAsmJSActivation();
    +    AsmJSActivation *activation = JSRuntime::innermostAsmJSActivation();
         JSContext *cx = activation->cx();
     
         RootedValue rval(cx);
    @@ -784,13 +784,13 @@
                 // i.e. ptr >= heapLength + 1 - data-type-byte-size
                 // (Note that we need >= as this is what codegen uses.)
                 size_t scalarByteSize = TypedArrayElemSize(access.type());
    -            X86Assembler::setPointer(access.patchLengthAt(code_),
    -                                     (void*)(heap->byteLength() + 1 - scalarByteSize));
    +            X86Encoding::SetPointer(access.patchLengthAt(code_),
    +                                    (void*)(heap->byteLength() + 1 - scalarByteSize));
             }
             void *addr = access.patchOffsetAt(code_);
    -        uint32_t disp = reinterpret_cast(X86Assembler::getPointer(addr));
    +        uint32_t disp = reinterpret_cast(X86Encoding::GetPointer(addr));
             MOZ_ASSERT(disp <= INT32_MAX);
    -        X86Assembler::setPointer(addr, (void *)(heapOffset + disp));
    +        X86Encoding::SetPointer(addr, (void *)(heapOffset + disp));
         }
     #elif defined(JS_CODEGEN_X64)
         // Even with signal handling being used for most bounds checks, there may be
    @@ -806,7 +806,7 @@
             if (access.hasLengthCheck()) {
                 // See comment above for x86 codegen.
                 size_t scalarByteSize = TypedArrayElemSize(access.type());
    -            X86Assembler::setInt32(access.patchLengthAt(code_), heapLength + 1 - scalarByteSize);
    +            X86Encoding::SetInt32(access.patchLengthAt(code_), heapLength + 1 - scalarByteSize);
             }
         }
     #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
    @@ -828,9 +828,9 @@
             for (unsigned i = 0; i < heapAccesses_.length(); i++) {
                 const jit::AsmJSHeapAccess &access = heapAccesses_[i];
                 void *addr = access.patchOffsetAt(code_);
    -            uint8_t *ptr = reinterpret_cast(X86Assembler::getPointer(addr));
    +            uint8_t *ptr = reinterpret_cast(X86Encoding::GetPointer(addr));
                 MOZ_ASSERT(ptr >= ptrBase);
    -            X86Assembler::setPointer(addr, (void *)(ptr - ptrBase));
    +            X86Encoding::SetPointer(addr, (void *)(ptr - ptrBase));
             }
         }
     #endif
    @@ -1678,7 +1678,7 @@
     
             uint8_t *callerRetAddr = code_ + cs.returnAddressOffset();
     #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
    -        void *callee = X86Assembler::getRel32Target(callerRetAddr);
    +        void *callee = X86Encoding::GetRel32Target(callerRetAddr);
     #elif defined(JS_CODEGEN_ARM)
             uint8_t *caller = callerRetAddr - 4;
             Instruction *callerInsn = reinterpret_cast(caller);
    @@ -1706,7 +1706,7 @@
             uint8_t *newCallee = enabled ? profilingEntry : entry;
     
     #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
    -        X86Assembler::setRel32(callerRetAddr, newCallee);
    +        X86Encoding::SetRel32(callerRetAddr, newCallee);
     #elif defined(JS_CODEGEN_ARM)
             new (caller) InstBLImm(BOffImm(newCallee - caller), Assembler::Always);
     #elif defined(JS_CODEGEN_MIPS)
    @@ -1919,13 +1919,13 @@
     
       public:
         static uint32_t beginOffset(AsmJSParser &parser) {
    -      return parser.pc->maybeFunction->pn_pos.begin;
    +        return parser.pc->maybeFunction->pn_pos.begin;
         }
     
         static uint32_t endOffset(AsmJSParser &parser) {
    -      TokenPos pos;
    -      MOZ_ALWAYS_TRUE(parser.tokenStream.peekTokenPos(&pos));
    -      return pos.end;
    +        TokenPos pos(0, 0);  // initialize to silence GCC warning
    +        MOZ_ALWAYS_TRUE(parser.tokenStream.peekTokenPos(&pos));
    +        return pos.end;
         }
     };
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/asmjs/AsmJSModule.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/asmjs/AsmJSModule.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/asmjs/AsmJSModule.h	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/asmjs/AsmJSModule.h	2015-02-03 14:33:24.000000000 +0000
    @@ -147,6 +147,9 @@
         static AsmJSNumLit Create(Which w, Value v) {
             AsmJSNumLit lit;
             lit.which_ = w;
    +        // Force float32 coercion, as the caller may have not done it.
    +        if (w == Float)
    +            v = Float32Value(v.toNumber());
             lit.value.scalar_ = v;
             MOZ_ASSERT(!lit.isSimd());
             return lit;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/asmjs/AsmJSSignalHandlers.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/asmjs/AsmJSSignalHandlers.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/asmjs/AsmJSSignalHandlers.cpp	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/asmjs/AsmJSSignalHandlers.cpp	2015-02-03 14:33:24.000000000 +0000
    @@ -382,42 +382,42 @@
     {
         if (reg.isFloat()) {
             switch (reg.fpu().code()) {
    -          case X86Registers::xmm0:  SetXMMRegToNaN(viewType, &XMM_sig(context, 0)); break;
    -          case X86Registers::xmm1:  SetXMMRegToNaN(viewType, &XMM_sig(context, 1)); break;
    -          case X86Registers::xmm2:  SetXMMRegToNaN(viewType, &XMM_sig(context, 2)); break;
    -          case X86Registers::xmm3:  SetXMMRegToNaN(viewType, &XMM_sig(context, 3)); break;
    -          case X86Registers::xmm4:  SetXMMRegToNaN(viewType, &XMM_sig(context, 4)); break;
    -          case X86Registers::xmm5:  SetXMMRegToNaN(viewType, &XMM_sig(context, 5)); break;
    -          case X86Registers::xmm6:  SetXMMRegToNaN(viewType, &XMM_sig(context, 6)); break;
    -          case X86Registers::xmm7:  SetXMMRegToNaN(viewType, &XMM_sig(context, 7)); break;
    -          case X86Registers::xmm8:  SetXMMRegToNaN(viewType, &XMM_sig(context, 8)); break;
    -          case X86Registers::xmm9:  SetXMMRegToNaN(viewType, &XMM_sig(context, 9)); break;
    -          case X86Registers::xmm10: SetXMMRegToNaN(viewType, &XMM_sig(context, 10)); break;
    -          case X86Registers::xmm11: SetXMMRegToNaN(viewType, &XMM_sig(context, 11)); break;
    -          case X86Registers::xmm12: SetXMMRegToNaN(viewType, &XMM_sig(context, 12)); break;
    -          case X86Registers::xmm13: SetXMMRegToNaN(viewType, &XMM_sig(context, 13)); break;
    -          case X86Registers::xmm14: SetXMMRegToNaN(viewType, &XMM_sig(context, 14)); break;
    -          case X86Registers::xmm15: SetXMMRegToNaN(viewType, &XMM_sig(context, 15)); break;
    +          case X86Encoding::xmm0:  SetXMMRegToNaN(viewType, &XMM_sig(context, 0)); break;
    +          case X86Encoding::xmm1:  SetXMMRegToNaN(viewType, &XMM_sig(context, 1)); break;
    +          case X86Encoding::xmm2:  SetXMMRegToNaN(viewType, &XMM_sig(context, 2)); break;
    +          case X86Encoding::xmm3:  SetXMMRegToNaN(viewType, &XMM_sig(context, 3)); break;
    +          case X86Encoding::xmm4:  SetXMMRegToNaN(viewType, &XMM_sig(context, 4)); break;
    +          case X86Encoding::xmm5:  SetXMMRegToNaN(viewType, &XMM_sig(context, 5)); break;
    +          case X86Encoding::xmm6:  SetXMMRegToNaN(viewType, &XMM_sig(context, 6)); break;
    +          case X86Encoding::xmm7:  SetXMMRegToNaN(viewType, &XMM_sig(context, 7)); break;
    +          case X86Encoding::xmm8:  SetXMMRegToNaN(viewType, &XMM_sig(context, 8)); break;
    +          case X86Encoding::xmm9:  SetXMMRegToNaN(viewType, &XMM_sig(context, 9)); break;
    +          case X86Encoding::xmm10: SetXMMRegToNaN(viewType, &XMM_sig(context, 10)); break;
    +          case X86Encoding::xmm11: SetXMMRegToNaN(viewType, &XMM_sig(context, 11)); break;
    +          case X86Encoding::xmm12: SetXMMRegToNaN(viewType, &XMM_sig(context, 12)); break;
    +          case X86Encoding::xmm13: SetXMMRegToNaN(viewType, &XMM_sig(context, 13)); break;
    +          case X86Encoding::xmm14: SetXMMRegToNaN(viewType, &XMM_sig(context, 14)); break;
    +          case X86Encoding::xmm15: SetXMMRegToNaN(viewType, &XMM_sig(context, 15)); break;
               default: MOZ_CRASH();
             }
         } else {
             switch (reg.gpr().code()) {
    -          case X86Registers::eax: RAX_sig(context) = 0; break;
    -          case X86Registers::ecx: RCX_sig(context) = 0; break;
    -          case X86Registers::edx: RDX_sig(context) = 0; break;
    -          case X86Registers::ebx: RBX_sig(context) = 0; break;
    -          case X86Registers::esp: RSP_sig(context) = 0; break;
    -          case X86Registers::ebp: RBP_sig(context) = 0; break;
    -          case X86Registers::esi: RSI_sig(context) = 0; break;
    -          case X86Registers::edi: RDI_sig(context) = 0; break;
    -          case X86Registers::r8:  R8_sig(context)  = 0; break;
    -          case X86Registers::r9:  R9_sig(context)  = 0; break;
    -          case X86Registers::r10: R10_sig(context) = 0; break;
    -          case X86Registers::r11: R11_sig(context) = 0; break;
    -          case X86Registers::r12: R12_sig(context) = 0; break;
    -          case X86Registers::r13: R13_sig(context) = 0; break;
    -          case X86Registers::r14: R14_sig(context) = 0; break;
    -          case X86Registers::r15: R15_sig(context) = 0; break;
    +          case X86Encoding::rax: RAX_sig(context) = 0; break;
    +          case X86Encoding::rcx: RCX_sig(context) = 0; break;
    +          case X86Encoding::rdx: RDX_sig(context) = 0; break;
    +          case X86Encoding::rbx: RBX_sig(context) = 0; break;
    +          case X86Encoding::rsp: RSP_sig(context) = 0; break;
    +          case X86Encoding::rbp: RBP_sig(context) = 0; break;
    +          case X86Encoding::rsi: RSI_sig(context) = 0; break;
    +          case X86Encoding::rdi: RDI_sig(context) = 0; break;
    +          case X86Encoding::r8:  R8_sig(context)  = 0; break;
    +          case X86Encoding::r9:  R9_sig(context)  = 0; break;
    +          case X86Encoding::r10: R10_sig(context) = 0; break;
    +          case X86Encoding::r11: R11_sig(context) = 0; break;
    +          case X86Encoding::r12: R12_sig(context) = 0; break;
    +          case X86Encoding::r13: R13_sig(context) = 0; break;
    +          case X86Encoding::r14: R14_sig(context) = 0; break;
    +          case X86Encoding::r15: R15_sig(context) = 0; break;
               default: MOZ_CRASH();
             }
         }
    @@ -448,7 +448,7 @@
             return false;
         AutoSetHandlingSignal handling(rt);
     
    -    AsmJSActivation *activation = rt->mainThread.asmJSActivationStack();
    +    AsmJSActivation *activation = rt->asmJSActivationStack();
         if (!activation)
             return false;
     
    @@ -551,22 +551,22 @@
     
             Scalar::Type viewType = heapAccess.type();
             switch (heapAccess.loadedReg().fpu().code()) {
    -          case X86Registers::xmm0:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm0); break;
    -          case X86Registers::xmm1:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm1); break;
    -          case X86Registers::xmm2:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm2); break;
    -          case X86Registers::xmm3:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm3); break;
    -          case X86Registers::xmm4:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm4); break;
    -          case X86Registers::xmm5:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm5); break;
    -          case X86Registers::xmm6:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm6); break;
    -          case X86Registers::xmm7:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm7); break;
    -          case X86Registers::xmm8:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm8); break;
    -          case X86Registers::xmm9:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm9); break;
    -          case X86Registers::xmm10: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm10); break;
    -          case X86Registers::xmm11: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm11); break;
    -          case X86Registers::xmm12: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm12); break;
    -          case X86Registers::xmm13: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm13); break;
    -          case X86Registers::xmm14: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm14); break;
    -          case X86Registers::xmm15: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm15); break;
    +          case X86Encoding::xmm0:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm0); break;
    +          case X86Encoding::xmm1:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm1); break;
    +          case X86Encoding::xmm2:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm2); break;
    +          case X86Encoding::xmm3:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm3); break;
    +          case X86Encoding::xmm4:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm4); break;
    +          case X86Encoding::xmm5:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm5); break;
    +          case X86Encoding::xmm6:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm6); break;
    +          case X86Encoding::xmm7:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm7); break;
    +          case X86Encoding::xmm8:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm8); break;
    +          case X86Encoding::xmm9:  SetXMMRegToNaN(viewType, &fstate.__fpu_xmm9); break;
    +          case X86Encoding::xmm10: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm10); break;
    +          case X86Encoding::xmm11: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm11); break;
    +          case X86Encoding::xmm12: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm12); break;
    +          case X86Encoding::xmm13: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm13); break;
    +          case X86Encoding::xmm14: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm14); break;
    +          case X86Encoding::xmm15: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm15); break;
               default: MOZ_CRASH();
             }
     
    @@ -575,22 +575,22 @@
                 return false;
         } else {
             switch (heapAccess.loadedReg().gpr().code()) {
    -          case X86Registers::eax: state.__rax = 0; break;
    -          case X86Registers::ecx: state.__rcx = 0; break;
    -          case X86Registers::edx: state.__rdx = 0; break;
    -          case X86Registers::ebx: state.__rbx = 0; break;
    -          case X86Registers::esp: state.__rsp = 0; break;
    -          case X86Registers::ebp: state.__rbp = 0; break;
    -          case X86Registers::esi: state.__rsi = 0; break;
    -          case X86Registers::edi: state.__rdi = 0; break;
    -          case X86Registers::r8:  state.__r8  = 0; break;
    -          case X86Registers::r9:  state.__r9  = 0; break;
    -          case X86Registers::r10: state.__r10 = 0; break;
    -          case X86Registers::r11: state.__r11 = 0; break;
    -          case X86Registers::r12: state.__r12 = 0; break;
    -          case X86Registers::r13: state.__r13 = 0; break;
    -          case X86Registers::r14: state.__r14 = 0; break;
    -          case X86Registers::r15: state.__r15 = 0; break;
    +          case X86Encoding::rax: state.__rax = 0; break;
    +          case X86Encoding::rcx: state.__rcx = 0; break;
    +          case X86Encoding::rdx: state.__rdx = 0; break;
    +          case X86Encoding::rbx: state.__rbx = 0; break;
    +          case X86Encoding::rsp: state.__rsp = 0; break;
    +          case X86Encoding::rbp: state.__rbp = 0; break;
    +          case X86Encoding::rsi: state.__rsi = 0; break;
    +          case X86Encoding::rdi: state.__rdi = 0; break;
    +          case X86Encoding::r8:  state.__r8  = 0; break;
    +          case X86Encoding::r9:  state.__r9  = 0; break;
    +          case X86Encoding::r10: state.__r10 = 0; break;
    +          case X86Encoding::r11: state.__r11 = 0; break;
    +          case X86Encoding::r12: state.__r12 = 0; break;
    +          case X86Encoding::r13: state.__r13 = 0; break;
    +          case X86Encoding::r14: state.__r14 = 0; break;
    +          case X86Encoding::r15: state.__r15 = 0; break;
               default: MOZ_CRASH();
             }
         }
    @@ -647,7 +647,7 @@
         if (request.body.exception != EXC_BAD_ACCESS || request.body.codeCnt != 2)
             return false;
     
    -    AsmJSActivation *activation = rt->mainThread.asmJSActivationStack();
    +    AsmJSActivation *activation = rt->asmJSActivationStack();
         if (!activation)
             return false;
     
    @@ -861,7 +861,7 @@
             return false;
         AutoSetHandlingSignal handling(rt);
     
    -    AsmJSActivation *activation = rt->mainThread.asmJSActivationStack();
    +    AsmJSActivation *activation = rt->asmJSActivationStack();
         if (!activation)
             return false;
     
    @@ -947,12 +947,12 @@
     {
         RedirectIonBackedgesToInterruptCheck(rt);
     
    -    if (AsmJSActivation *activation = rt->mainThread.asmJSActivationStack()) {
    +    if (AsmJSActivation *activation = rt->asmJSActivationStack()) {
             const AsmJSModule &module = activation->module();
     
     #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
    -        if (module.containsFunctionPC((void*)rt->mainThread.simulator()->get_pc()))
    -            rt->mainThread.simulator()->set_resume_pc(int32_t(module.interruptExit()));
    +        if (module.containsFunctionPC((void*)rt->simulator()->get_pc()))
    +            rt->simulator()->set_resume_pc(int32_t(module.interruptExit()));
     #endif
     
             uint8_t **ppc = ContextToPC(context);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/asmjs/AsmJSValidate.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/asmjs/AsmJSValidate.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/asmjs/AsmJSValidate.cpp	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/asmjs/AsmJSValidate.cpp	2015-02-03 14:33:24.000000000 +0000
    @@ -635,6 +635,7 @@
         RetType() : which_(Which(-1)) {}
         MOZ_IMPLICIT RetType(Which w) : which_(w) {}
         MOZ_IMPLICIT RetType(AsmJSCoercion coercion) {
    +        which_ = Which(-1);  // initialize to silence GCC warning
             switch (coercion) {
               case AsmJS_ToInt32: which_ = Signed; break;
               case AsmJS_ToNumber: which_ = Double; break;
    @@ -3031,7 +3032,7 @@
             uint32_t line, column;
             m_.tokenStream().srcCoords.lineNumAndColumnIndex(call.node_->pn_pos.begin, &line, &column);
     
    -        CallSiteDesc::Kind kind;
    +        CallSiteDesc::Kind kind = CallSiteDesc::Kind(-1);  // initialize to silence GCC warning
             switch (callee.which()) {
               case MAsmJSCall::Callee::Internal: kind = CallSiteDesc::Relative; break;
               case MAsmJSCall::Callee::Dynamic:  kind = CallSiteDesc::Register; break;
    @@ -7292,7 +7293,7 @@
         if (ParseNode *elseStmt = TernaryKid3(stmtIter))
             return m.fail(elseStmt, "unexpected else statement");
     
    -    uint32_t mask, min, max;
    +    uint32_t mask, min = 0, max;  // initialize min to silence GCC warning
         if (!CheckHeapLengthCondition(m, cond, newBufferName, &mask, &min, &max))
             return false;
     
    @@ -7416,7 +7417,7 @@
         // the backing LifoAlloc after parsing/compiling each function.
         AsmJSParser::Mark mark = m.parser().mark();
     
    -    ParseNode *fn;
    +    ParseNode *fn = nullptr;  // initialize to silence GCC warning
         if (!ParseFunction(m, &fn))
             return false;
     
    @@ -8504,25 +8505,21 @@
     
             // The following is inlined:
             //   JSContext *cx = activation->cx();
    -        //   Activation *act = cx->mainThread().activation();
    +        //   Activation *act = cx->runtime()->activation();
             //   act.active_ = true;
    -        //   act.prevJitTop_ = cx->mainThread().jitTop;
    -        //   act.prevJitJSContext_ = cx->mainThread().jitJSContext;
    -        //   cx->mainThread().jitJSContext = cx;
    -        //   act.prevJitActivation_ = cx->mainThread().jitActivation;
    -        //   cx->mainThread().jitActivation = act;
    -        //   act.prevProfilingActivation_ = cx->mainThread().profilingActivation;
    -        //   cx->mainThread().profilingActivation_ = act;
    +        //   act.prevJitTop_ = cx->runtime()->jitTop;
    +        //   act.prevJitJSContext_ = cx->runtime()->jitJSContext;
    +        //   cx->runtime()->jitJSContext = cx;
    +        //   act.prevJitActivation_ = cx->runtime()->jitActivation;
    +        //   cx->runtime()->jitActivation = act;
    +        //   act.prevProfilingActivation_ = cx->runtime()->profilingActivation;
    +        //   cx->runtime()->profilingActivation_ = act;
             // On the ARM store8() uses the secondScratchReg (lr) as a temp.
    -        size_t offsetOfActivation = offsetof(JSRuntime, mainThread) +
    -                                    PerThreadData::offsetOfActivation();
    -        size_t offsetOfJitTop = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, jitTop);
    -        size_t offsetOfJitJSContext = offsetof(JSRuntime, mainThread) +
    -                                      offsetof(PerThreadData, jitJSContext);
    -        size_t offsetOfJitActivation = offsetof(JSRuntime, mainThread) +
    -                                       offsetof(PerThreadData, jitActivation);
    -        size_t offsetOfProfilingActivation = offsetof(JSRuntime, mainThread) +
    -                                             PerThreadData::offsetOfProfilingActivation();
    +        size_t offsetOfActivation = JSRuntime::offsetOfActivation();
    +        size_t offsetOfJitTop = offsetof(JSRuntime, jitTop);
    +        size_t offsetOfJitJSContext = offsetof(JSRuntime, jitJSContext);
    +        size_t offsetOfJitActivation = offsetof(JSRuntime, jitActivation);
    +        size_t offsetOfProfilingActivation = JSRuntime::offsetOfProfilingActivation();
             masm.loadAsmJSActivation(reg0);
             masm.loadPtr(Address(reg0, AsmJSActivation::offsetOfContext()), reg3);
             masm.loadPtr(Address(reg3, JSContext::offsetOfRuntime()), reg0);
    @@ -8531,26 +8528,26 @@
             //   act.active_ = true;
             masm.store8(Imm32(1), Address(reg1, JitActivation::offsetOfActiveUint8()));
     
    -        //   act.prevJitTop_ = cx->mainThread().jitTop;
    +        //   act.prevJitTop_ = cx->runtime()->jitTop;
             masm.loadPtr(Address(reg0, offsetOfJitTop), reg2);
             masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitTop()));
     
    -        //   act.prevJitJSContext_ = cx->mainThread().jitJSContext;
    +        //   act.prevJitJSContext_ = cx->runtime()->jitJSContext;
             masm.loadPtr(Address(reg0, offsetOfJitJSContext), reg2);
             masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitJSContext()));
    -        //   cx->mainThread().jitJSContext = cx;
    +        //   cx->runtime()->jitJSContext = cx;
             masm.storePtr(reg3, Address(reg0, offsetOfJitJSContext));
     
    -        //   act.prevJitActivation_ = cx->mainThread().jitActivation;
    +        //   act.prevJitActivation_ = cx->runtime()->jitActivation;
             masm.loadPtr(Address(reg0, offsetOfJitActivation), reg2);
             masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitActivation()));
    -        //   cx->mainThread().jitActivation = act;
    +        //   cx->runtime()->jitActivation = act;
             masm.storePtr(reg1, Address(reg0, offsetOfJitActivation));
     
    -        //   act.prevProfilingActivation_ = cx->mainThread().profilingActivation;
    +        //   act.prevProfilingActivation_ = cx->runtime()->profilingActivation;
             masm.loadPtr(Address(reg0, offsetOfProfilingActivation), reg2);
             masm.storePtr(reg2, Address(reg1, Activation::offsetOfPrevProfiling()));
    -        //   cx->mainThread().profilingActivation_ = act;
    +        //   cx->runtime()->profilingActivation_ = act;
             masm.storePtr(reg1, Address(reg0, offsetOfProfilingActivation));
         }
     
    @@ -8571,41 +8568,37 @@
             Register reg2 = AsmJSIonExitRegD2;
     
             // The following is inlined:
    -        //   rt->mainThread.profilingActivation = prevProfilingActivation_;
    -        //   rt->mainThread.activation()->active_ = false;
    -        //   rt->mainThread.jitTop = prevJitTop_;
    -        //   rt->mainThread.jitJSContext = prevJitJSContext_;
    -        //   rt->mainThread.jitActivation = prevJitActivation_;
    +        //   rt->profilingActivation = prevProfilingActivation_;
    +        //   rt->activation()->active_ = false;
    +        //   rt->jitTop = prevJitTop_;
    +        //   rt->jitJSContext = prevJitJSContext_;
    +        //   rt->jitActivation = prevJitActivation_;
             // On the ARM store8() uses the secondScratchReg (lr) as a temp.
    -        size_t offsetOfActivation = offsetof(JSRuntime, mainThread) +
    -                                    PerThreadData::offsetOfActivation();
    -        size_t offsetOfJitTop = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, jitTop);
    -        size_t offsetOfJitJSContext = offsetof(JSRuntime, mainThread) +
    -                                      offsetof(PerThreadData, jitJSContext);
    -        size_t offsetOfJitActivation = offsetof(JSRuntime, mainThread) +
    -                                       offsetof(PerThreadData, jitActivation);
    -        size_t offsetOfProfilingActivation = offsetof(JSRuntime, mainThread) +
    -                                             PerThreadData::offsetOfProfilingActivation();
    +        size_t offsetOfActivation = JSRuntime::offsetOfActivation();
    +        size_t offsetOfJitTop = offsetof(JSRuntime, jitTop);
    +        size_t offsetOfJitJSContext = offsetof(JSRuntime, jitJSContext);
    +        size_t offsetOfJitActivation = offsetof(JSRuntime, jitActivation);
    +        size_t offsetOfProfilingActivation = JSRuntime::offsetOfProfilingActivation();
     
             masm.movePtr(AsmJSImmPtr(AsmJSImm_Runtime), reg0);
             masm.loadPtr(Address(reg0, offsetOfActivation), reg1);
     
    -        //   rt->mainThread.jitTop = prevJitTop_;
    +        //   rt->jitTop = prevJitTop_;
             masm.loadPtr(Address(reg1, JitActivation::offsetOfPrevJitTop()), reg2);
             masm.storePtr(reg2, Address(reg0, offsetOfJitTop));
     
    -        //   rt->mainThread.profilingActivation = rt->mainThread.activation()->prevProfiling_;
    +        //   rt->profilingActivation = rt->activation()->prevProfiling_;
             masm.loadPtr(Address(reg1, Activation::offsetOfPrevProfiling()), reg2);
             masm.storePtr(reg2, Address(reg0, offsetOfProfilingActivation));
     
    -        //   rt->mainThread.activation()->active_ = false;
    +        //   rt->activation()->active_ = false;
             masm.store8(Imm32(0), Address(reg1, JitActivation::offsetOfActiveUint8()));
     
    -        //   rt->mainThread.jitJSContext = prevJitJSContext_;
    +        //   rt->jitJSContext = prevJitJSContext_;
             masm.loadPtr(Address(reg1, JitActivation::offsetOfPrevJitJSContext()), reg2);
             masm.storePtr(reg2, Address(reg0, offsetOfJitJSContext));
     
    -        //   rt->mainThread.jitActivation = prevJitActivation_;
    +        //   rt->jitActivation = prevJitActivation_;
             masm.loadPtr(Address(reg1, JitActivation::offsetOfPrevJitActivation()), reg2);
             masm.storePtr(reg2, Address(reg0, offsetOfJitActivation));
         }
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/Eval.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/Eval.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/Eval.cpp	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/Eval.cpp	2015-02-03 14:33:24.000000000 +0000
    @@ -509,8 +509,7 @@
             if (!script)
                 return false;
     
    -        Rooted global(cx, script->compileAndGo() ? &script->global() : nullptr);
    -        Debugger::onNewScript(cx, script, global);
    +        Debugger::onNewScript(cx, script);
         }
     
         RootedObject scope(cx, JS_NewPlainObject(cx));
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/Intl.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/Intl.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/Intl.cpp	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/Intl.cpp	2015-02-03 14:33:24.000000000 +0000
    @@ -669,9 +669,15 @@
     static void
     collator_finalize(FreeOp *fop, JSObject *obj)
     {
    -    UCollator *coll = static_cast(obj->as().getReservedSlot(UCOLLATOR_SLOT).toPrivate());
    -    if (coll)
    -        ucol_close(coll);
    +    // This is-undefined check shouldn't be necessary, but for internal
    +    // brokenness in object allocation code.  For the moment, hack around it by
    +    // explicitly guarding against the possibility of the reserved slot not
    +    // containing a private.  See bug 949220.
    +    const Value &slot = obj->as().getReservedSlot(UCOLLATOR_SLOT);
    +    if (!slot.isUndefined()) {
    +        if (UCollator *coll = static_cast(slot.toPrivate()))
    +            ucol_close(coll);
    +    }
     }
     
     static JSObject *
    @@ -1156,10 +1162,15 @@
     static void
     numberFormat_finalize(FreeOp *fop, JSObject *obj)
     {
    -    UNumberFormat *nf =
    -        static_cast(obj->as().getReservedSlot(UNUMBER_FORMAT_SLOT).toPrivate());
    -    if (nf)
    -        unum_close(nf);
    +    // This is-undefined check shouldn't be necessary, but for internal
    +    // brokenness in object allocation code.  For the moment, hack around it by
    +    // explicitly guarding against the possibility of the reserved slot not
    +    // containing a private.  See bug 949220.
    +    const Value &slot = obj->as().getReservedSlot(UNUMBER_FORMAT_SLOT);
    +    if (!slot.isUndefined()) {
    +        if (UNumberFormat *nf = static_cast(slot.toPrivate()))
    +            unum_close(nf);
    +    }
     }
     
     static JSObject *
    @@ -1610,9 +1621,15 @@
     static void
     dateTimeFormat_finalize(FreeOp *fop, JSObject *obj)
     {
    -    UDateFormat *df = static_cast(obj->as().getReservedSlot(UDATE_FORMAT_SLOT).toPrivate());
    -    if (df)
    -        udat_close(df);
    +    // This is-undefined check shouldn't be necessary, but for internal
    +    // brokenness in object allocation code.  For the moment, hack around it by
    +    // explicitly guarding against the possibility of the reserved slot not
    +    // containing a private.  See bug 949220.
    +    const Value &slot = obj->as().getReservedSlot(UDATE_FORMAT_SLOT);
    +    if (!slot.isUndefined()) {
    +        if (UDateFormat *df = static_cast(slot.toPrivate()))
    +            udat_close(df);
    +    }
     }
     
     static JSObject *
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/MapObject.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/MapObject.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/MapObject.cpp	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/MapObject.cpp	2015-02-03 14:33:24.000000000 +0000
    @@ -1077,14 +1077,9 @@
     
             // Define its alias.
             RootedValue funval(cx, ObjectValue(*fun));
    -#if JS_HAS_SYMBOLS
             RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator));
             if (!JS_DefinePropertyById(cx, proto, iteratorId, funval, 0))
                 return nullptr;
    -#else
    -        if (!JS_DefineProperty(cx, proto, js_std_iterator_str, funval, 0))
    -            return nullptr;
    -#endif
         }
         return proto;
     }
    @@ -1310,12 +1305,25 @@
         return v.isObject() && v.toObject().hasClass(&class_) && v.toObject().as().getPrivate();
     }
     
    +bool
    +MapObject::is(HandleObject o)
    +{
    +    return o->hasClass(&class_) && o->as().getPrivate();
    +}
    +
     #define ARG0_KEY(cx, args, key)                                               \
         AutoHashableValueRooter key(cx);                                          \
         if (args.length() > 0 && !key.setValue(cx, args[0]))                      \
             return false
     
     ValueMap &
    +MapObject::extract(HandleObject o)
    +{
    +    MOZ_ASSERT(o->hasClass(&MapObject::class_));
    +    return *o->as().getData();
    +}
    +
    +ValueMap &
     MapObject::extract(CallReceiver call)
     {
         MOZ_ASSERT(call.thisv().isObject());
    @@ -1323,15 +1331,21 @@
         return *call.thisv().toObject().as().getData();
     }
     
    -bool
    -MapObject::size_impl(JSContext *cx, CallArgs args)
    +uint32_t
    +MapObject::size(JSContext *cx, HandleObject obj)
     {
    -    MOZ_ASSERT(MapObject::is(args.thisv()));
    -
    -    ValueMap &map = extract(args);
    +    MOZ_ASSERT(MapObject::is(obj));
    +    ValueMap &map = extract(obj);
         static_assert(sizeof(map.count()) <= sizeof(uint32_t),
                       "map count must be precisely representable as a JS number");
    -    args.rval().setNumber(map.count());
    +    return map.count();
    +}
    +
    +bool
    +MapObject::size_impl(JSContext *cx, CallArgs args)
    +{
    +    RootedObject obj(cx, &args.thisv().toObject());
    +    args.rval().setNumber(size(cx, obj));
         return true;
     }
     
    @@ -1343,21 +1357,33 @@
     }
     
     bool
    -MapObject::get_impl(JSContext *cx, CallArgs args)
    +MapObject::get(JSContext *cx, HandleObject obj,
    +               HandleValue key, MutableHandleValue rval)
     {
    -    MOZ_ASSERT(MapObject::is(args.thisv()));
    +    MOZ_ASSERT(MapObject::is(obj));
     
    -    ValueMap &map = extract(args);
    -    ARG0_KEY(cx, args, key);
    +    ValueMap &map = extract(obj);
    +    AutoHashableValueRooter k(cx);
     
    -    if (ValueMap::Entry *p = map.get(key))
    -        args.rval().set(p->value);
    +    if (!k.setValue(cx, key))
    +        return false;
    +
    +    if (ValueMap::Entry *p = map.get(k))
    +        rval.set(p->value);
         else
    -        args.rval().setUndefined();
    +        rval.setUndefined();
    +
         return true;
     }
     
     bool
    +MapObject::get_impl(JSContext *cx, CallArgs args)
    +{
    +    RootedObject obj(cx, &args.thisv().toObject());
    +    return get(cx, obj, args.get(0), args.rval());
    +}
    +
    +bool
     MapObject::get(JSContext *cx, unsigned argc, Value *vp)
     {
         CallArgs args = CallArgsFromVp(argc, vp);
    @@ -1365,17 +1391,33 @@
     }
     
     bool
    -MapObject::has_impl(JSContext *cx, CallArgs args)
    +MapObject::has(JSContext *cx, HandleObject obj, HandleValue key, bool *rval)
     {
    -    MOZ_ASSERT(MapObject::is(args.thisv()));
    +    MOZ_ASSERT(MapObject::is(obj));
     
    -    ValueMap &map = extract(args);
    -    ARG0_KEY(cx, args, key);
    -    args.rval().setBoolean(map.has(key));
    +    ValueMap &map = extract(obj);
    +    AutoHashableValueRooter k(cx);
    +
    +    if (!k.setValue(cx, key))
    +        return false;
    +
    +    *rval = map.has(k);
         return true;
     }
     
     bool
    +MapObject::has_impl(JSContext *cx, CallArgs args)
    +{
    +    bool found;
    +    RootedObject obj(cx, &args.thisv().toObject());
    +    if (has(cx, obj, args.get(0), &found)) {
    +        args.rval().setBoolean(found);
    +        return true;
    +    }
    +    return false;
    +}
    +
    +bool
     MapObject::has(JSContext *cx, unsigned argc, Value *vp)
     {
         CallArgs args = CallArgsFromVp(argc, vp);
    @@ -1439,15 +1481,20 @@
     }
     
     bool
    +MapObject::iterator(JSContext *cx, IteratorKind kind,
    +                    HandleObject obj, MutableHandleValue iter)
    +{
    +    MOZ_ASSERT(MapObject::is(obj));
    +    ValueMap &map = extract(obj);
    +    Rooted iterobj(cx, MapIteratorObject::create(cx, obj, &map, kind));
    +    return iterobj && (iter.setObject(*iterobj), true);
    +}
    +
    +bool
     MapObject::iterator_impl(JSContext *cx, CallArgs args, IteratorKind kind)
     {
    -    Rooted mapobj(cx, &args.thisv().toObject().as());
    -    ValueMap &map = *mapobj->getData();
    -    Rooted iterobj(cx, MapIteratorObject::create(cx, mapobj, &map, kind));
    -    if (!iterobj)
    -        return false;
    -    args.rval().setObject(*iterobj);
    -    return true;
    +    RootedObject obj(cx, &args.thisv().toObject());
    +    return iterator(cx, kind, obj, args.rval());
     }
     
     bool
    @@ -1492,13 +1539,9 @@
     bool
     MapObject::clear_impl(JSContext *cx, CallArgs args)
     {
    -    Rooted mapobj(cx, &args.thisv().toObject().as());
    -    if (!mapobj->getData()->clear()) {
    -        js_ReportOutOfMemory(cx);
    -        return false;
    -    }
    +    RootedObject obj(cx, &args.thisv().toObject());
         args.rval().setUndefined();
    -    return true;
    +    return clear(cx, obj);
     }
     
     bool
    @@ -1508,6 +1551,18 @@
         return CallNonGenericMethod(cx, is, clear_impl, args);
     }
     
    +bool
    +MapObject::clear(JSContext *cx, HandleObject obj)
    +{
    +    MOZ_ASSERT(MapObject::is(obj));
    +    ValueMap &map = extract(obj);
    +    if (!map.clear()) {
    +        js_ReportOutOfMemory(cx);
    +        return false;
    +    }
    +    return true;
    +}
    +
     JSObject *
     js_InitMapClass(JSContext *cx, HandleObject obj)
     {
    @@ -1730,14 +1785,9 @@
             if (!JS_DefineProperty(cx, proto, "keys", funval, 0))
                 return nullptr;
     
    -#if JS_HAS_SYMBOLS
             RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator));
             if (!JS_DefinePropertyById(cx, proto, iteratorId, funval, 0))
                 return nullptr;
    -#else
    -        if (!JS_DefineProperty(cx, proto, js_std_iterator_str, funval, 0))
    -            return nullptr;
    -#endif
         }
         return proto;
     }
    @@ -2043,3 +2093,75 @@
     {
         return JS_DefineFunctions(cx, obj, selfhosting_collection_iterator_methods);
     }
    +
    +/*** JS public APIs **********************************************************/
    +
    +JS_PUBLIC_API(JSObject *)
    +JS::NewMapObject(JSContext *cx)
    +{
    +    return MapObject::create(cx);
    +}
    +
    +JS_PUBLIC_API(uint32_t)
    +JS::MapSize(JSContext *cx, HandleObject obj)
    +{
    +    CHECK_REQUEST(cx);
    +    return MapObject::size(cx, obj);
    +}
    +
    +JS_PUBLIC_API(bool)
    +JS::MapGet(JSContext *cx, HandleObject obj,
    +           HandleValue key, MutableHandleValue rval)
    +{
    +    CHECK_REQUEST(cx);
    +    assertSameCompartment(cx, key, rval);
    +    return MapObject::get(cx, obj, key, rval);
    +}
    +
    +JS_PUBLIC_API(bool)
    +JS::MapHas(JSContext *cx, HandleObject obj, HandleValue key, bool *rval)
    +{
    +    CHECK_REQUEST(cx);
    +    assertSameCompartment(cx, key);
    +    return MapObject::has(cx, obj, key, rval);
    +}
    +
    +JS_PUBLIC_API(bool)
    +JS::MapSet(JSContext *cx, HandleObject obj,
    +           HandleValue key, HandleValue val)
    +{
    +    CHECK_REQUEST(cx);
    +    assertSameCompartment(cx, key, val);
    +    return MapObject::set(cx, obj, key, val);
    +}
    +
    +JS_PUBLIC_API(bool)
    +JS::MapClear(JSContext *cx, HandleObject obj)
    +{
    +    CHECK_REQUEST(cx);
    +    return MapObject::clear(cx, obj);
    +}
    +
    +JS_PUBLIC_API(bool)
    +JS::MapKeys(JSContext *cx, HandleObject obj, MutableHandleValue rval)
    +{
    +    CHECK_REQUEST(cx);
    +    assertSameCompartment(cx, rval);
    +    return MapObject::iterator(cx, MapObject::Keys, obj, rval);
    +}
    +
    +JS_PUBLIC_API(bool)
    +JS::MapValues(JSContext *cx, HandleObject obj, MutableHandleValue rval)
    +{
    +    CHECK_REQUEST(cx);
    +    assertSameCompartment(cx, rval);
    +    return MapObject::iterator(cx, MapObject::Values, obj, rval);
    +}
    +
    +JS_PUBLIC_API(bool)
    +JS::MapEntries(JSContext *cx, HandleObject obj, MutableHandleValue rval)
    +{
    +    CHECK_REQUEST(cx);
    +    assertSameCompartment(cx, rval);
    +    return MapObject::iterator(cx, MapObject::Entries, obj, rval);
    +}
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/MapObject.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/MapObject.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/MapObject.h	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/MapObject.h	2015-02-03 14:33:24.000000000 +0000
    @@ -95,20 +95,28 @@
         static bool getKeysAndValuesInterleaved(JSContext *cx, HandleObject obj,
                                                 JS::AutoValueVector *entries);
         static bool entries(JSContext *cx, unsigned argc, Value *vp);
    -    static bool set(JSContext *cx, HandleObject obj, HandleValue key, HandleValue value);
         static bool has(JSContext *cx, unsigned argc, Value *vp);
         static MapObject* create(JSContext *cx);
     
    +    static uint32_t size(JSContext *cx, HandleObject obj);
    +    static bool get(JSContext *cx, HandleObject obj, HandleValue key, MutableHandleValue rval);
    +    static bool has(JSContext *cx, HandleObject obj, HandleValue key, bool* rval);
    +    static bool set(JSContext *cx, HandleObject obj, HandleValue key, HandleValue val);
    +    static bool clear(JSContext *cx, HandleObject obj);
    +    static bool iterator(JSContext *cx, IteratorKind kind, HandleObject obj, MutableHandleValue iter);
    +
       private:
         static const JSPropertySpec properties[];
         static const JSFunctionSpec methods[];
         ValueMap *getData() { return static_cast(getPrivate()); }
    +    static ValueMap & extract(HandleObject o);
         static ValueMap & extract(CallReceiver call);
         static void mark(JSTracer *trc, JSObject *obj);
         static void finalize(FreeOp *fop, JSObject *obj);
         static bool construct(JSContext *cx, unsigned argc, Value *vp);
     
         static bool is(HandleValue v);
    +    static bool is(HandleObject o);
     
         static bool iterator_impl(JSContext *cx, CallArgs args, IteratorKind kind);
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/Object.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/Object.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/Object.cpp	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/Object.cpp	2015-02-03 14:33:24.000000000 +0000
    @@ -623,7 +623,8 @@
     }
     
     PlainObject *
    -js::ObjectCreateImpl(JSContext *cx, HandleObject proto, NewObjectKind newKind, HandleTypeObject type)
    +js::ObjectCreateImpl(JSContext *cx, HandleObject proto, NewObjectKind newKind,
    +                     HandleObjectGroup group)
     {
         // Give the new object a small number of fixed slots, like we do for empty
         // object literals ({}).
    @@ -631,20 +632,20 @@
     
         if (!proto) {
             // Object.create(null) is common, optimize it by using an allocation
    -        // site specific TypeObject. Because GetTypeCallerInitObject is pretty
    -        // slow, the caller can pass in the type if it's known and we use that
    +        // site specific ObjectGroup. Because GetCallerInitGroup is pretty
    +        // slow, the caller can pass in the group if it's known and we use that
             // instead.
    -        RootedTypeObject ntype(cx, type);
    -        if (!ntype) {
    -            ntype = GetTypeCallerInitObject(cx, JSProto_Null);
    -            if (!ntype)
    +        RootedObjectGroup ngroup(cx, group);
    +        if (!ngroup) {
    +            ngroup = GetCallerInitGroup(cx, JSProto_Null);
    +            if (!ngroup)
                     return nullptr;
             }
     
    -        MOZ_ASSERT(!ntype->proto().toObjectOrNull());
    +        MOZ_ASSERT(!ngroup->proto().toObjectOrNull());
     
    -        return NewObjectWithType(cx, ntype, cx->global(), allocKind,
    -                                              newKind);
    +        return NewObjectWithGroup(cx, ngroup, cx->global(), allocKind,
    +                                               newKind);
         }
     
         return NewObjectWithGivenProto(cx, proto, cx->global(), allocKind, newKind);
    @@ -654,8 +655,8 @@
     js::ObjectCreateWithTemplate(JSContext *cx, HandlePlainObject templateObj)
     {
         RootedObject proto(cx, templateObj->getProto());
    -    RootedTypeObject type(cx, templateObj->type());
    -    return ObjectCreateImpl(cx, proto, GenericObject, type);
    +    RootedObjectGroup group(cx, templateObj->group());
    +    return ObjectCreateImpl(cx, proto, GenericObject, group);
     }
     
     /* ES5 15.2.3.5: Object.create(O [, Properties]) */
    @@ -1149,7 +1150,7 @@
          * to have unknown properties, to simplify handling of e.g. heterogenous
          * objects in JSON and script literals.
          */
    -    if (!JSObject::setNewTypeUnknown(cx, &PlainObject::class_, objectProto))
    +    if (!JSObject::setNewGroupUnknown(cx, &PlainObject::class_, objectProto))
             return nullptr;
     
         return objectProto;
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/Object.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/Object.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/Object.h	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/Object.h	2015-02-03 14:33:24.000000000 +0000
    @@ -25,7 +25,7 @@
     
     PlainObject *
     ObjectCreateImpl(JSContext *cx, HandleObject proto, NewObjectKind newKind = GenericObject,
    -                 HandleTypeObject type = js::NullPtr());
    +                 HandleObjectGroup group = js::NullPtr());
     
     PlainObject *
     ObjectCreateWithTemplate(JSContext *cx, HandlePlainObject templateObj);
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/TestingFunctions.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/TestingFunctions.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/TestingFunctions.cpp	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/TestingFunctions.cpp	2015-02-03 14:33:24.000000000 +0000
    @@ -1219,6 +1219,89 @@
     }
     
     static bool
    +ReadSPSProfilingStack(JSContext *cx, unsigned argc, jsval *vp)
    +{
    +    CallArgs args = CallArgsFromVp(argc, vp);
    +    args.rval().setUndefined();
    +
    +    if (!cx->runtime()->spsProfiler.enabled())
    +        args.rval().setBoolean(false);
    +
    +    // Array holding physical jit stack frames.
    +    RootedObject stack(cx, NewDenseEmptyArray(cx));
    +    if (!stack)
    +        return false;
    +
    +    RootedObject inlineStack(cx);
    +    RootedObject inlineFrameInfo(cx);
    +    RootedString frameKind(cx);
    +    RootedString frameLabel(cx);
    +    RootedId idx(cx);
    +
    +    JS::ProfilingFrameIterator::RegisterState state;
    +    uint32_t physicalFrameNo = 0;
    +    const unsigned propAttrs = JSPROP_ENUMERATE;
    +    for (JS::ProfilingFrameIterator i(cx->runtime(), state); !i.done(); ++i, ++physicalFrameNo) {
    +        MOZ_ASSERT(i.stackAddress() != nullptr);
    +
    +        // Array holding all inline frames in a single physical jit stack frame.
    +        inlineStack = NewDenseEmptyArray(cx);
    +        if (!inlineStack)
    +            return false;
    +
    +        JS::ProfilingFrameIterator::Frame frames[16];
    +        uint32_t nframes = i.extractStack(frames, 0, 16);
    +        for (uint32_t inlineFrameNo = 0; inlineFrameNo < nframes; inlineFrameNo++) {
    +
    +            // Object holding frame info.
    +            inlineFrameInfo = NewBuiltinClassInstance(cx);
    +            if (!inlineFrameInfo)
    +                return false;
    +
    +            const char *frameKindStr = nullptr;
    +            switch (frames[inlineFrameNo].kind) {
    +              case JS::ProfilingFrameIterator::Frame_Baseline:
    +                frameKindStr = "baseline";
    +                break;
    +              case JS::ProfilingFrameIterator::Frame_Ion:
    +                frameKindStr = "ion";
    +                break;
    +              case JS::ProfilingFrameIterator::Frame_AsmJS:
    +                frameKindStr = "asmjs";
    +                break;
    +              default:
    +                frameKindStr = "unknown";
    +            }
    +            frameKind = NewStringCopyZ(cx, frameKindStr);
    +            if (!frameKind)
    +                return false;
    +
    +            if (!JS_DefineProperty(cx, inlineFrameInfo, "kind", frameKind, propAttrs))
    +                return false;
    +
    +            frameLabel = NewStringCopyZ(cx, frames[inlineFrameNo].label);
    +            if (!frameLabel)
    +                return false;
    +
    +            if (!JS_DefineProperty(cx, inlineFrameInfo, "label", frameLabel, propAttrs))
    +                return false;
    +
    +            idx = INT_TO_JSID(inlineFrameNo);
    +            if (!JS_DefinePropertyById(cx, inlineStack, idx, inlineFrameInfo, 0))
    +                return false;
    +        }
    +
    +        // Push inline array into main array.
    +        idx = INT_TO_JSID(physicalFrameNo);
    +        if (!JS_DefinePropertyById(cx, stack, idx, inlineStack, 0))
    +            return false;
    +    }
    +
    +    args.rval().setObject(*stack);
    +    return true;
    +}
    +
    +static bool
     EnableOsiPointRegisterChecks(JSContext *, unsigned argc, jsval *vp)
     {
         CallArgs args = CallArgsFromVp(argc, vp);
    @@ -1353,11 +1436,11 @@
     }
     
     static bool
    -TestingFunc_assertValidJitStack(JSContext *cx, unsigned argc, jsval *vp)
    +TestingFunc_assertJitStackInvariants(JSContext *cx, unsigned argc, jsval *vp)
     {
         CallArgs args = CallArgsFromVp(argc, vp);
     
    -    jit::AssertValidJitStack(cx);
    +    jit::AssertJitStackInvariants(cx);
         args.rval().setUndefined();
         return true;
     }
    @@ -1465,7 +1548,7 @@
         static const Class class_;
     
         static CloneBufferObject *Create(JSContext *cx) {
    -        RootedObject obj(cx, JS_NewObject(cx, Jsvalify(&class_), JS::NullPtr(), JS::NullPtr()));
    +        RootedObject obj(cx, JS_NewObject(cx, Jsvalify(&class_)));
             if (!obj)
                 return nullptr;
             obj->as().setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
    @@ -2435,6 +2518,10 @@
     "disableSPSProfiling()",
     "  Disables SPS instrumentation"),
     
    +    JS_FN_HELP("readSPSProfilingStack", ReadSPSProfilingStack, 0, 0,
    +"readSPSProfilingStack()",
    +"  Reads the jit stack using ProfilingFrameIterator."),
    +
         JS_FN_HELP("enableOsiPointRegisterChecks", EnableOsiPointRegisterChecks, 0, 0,
     "enableOsiPointRegisterChecks()",
     "Emit extra code to verify live regs at the start of a VM call are not\n"
    @@ -2499,8 +2586,8 @@
     "bailout()",
     "  Force a bailout out of ionmonkey (if running in ionmonkey)."),
     
    -    JS_FN_HELP("assertValidJitStack", TestingFunc_assertValidJitStack, 0, 0,
    -"assertValidJitStack()",
    +    JS_FN_HELP("assertJitStackInvariants", TestingFunc_assertJitStackInvariants, 0, 0,
    +"assertJitStackInvariants()",
     "  Iterates the Jit stack and check that stack invariants hold."),
     
         JS_FN_HELP("setJitCompilerOption", SetJitCompilerOption, 2, 0,
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/TypedArray.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/TypedArray.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/TypedArray.js	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/TypedArray.js	2015-02-03 14:33:24.000000000 +0000
    @@ -170,6 +170,41 @@
         return -1;
     }
     
    +// ES6 draft rev31 (2015-01-15) 22.1.3.10 %TypedArray%.prototype.forEach(callbackfn[,thisArg])
    +function TypedArrayForEach(callbackfn, thisArg = undefined) {
    +    // This function is not generic.
    +    if (!IsObject(this) || !IsTypedArray(this)) {
    +	return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
    +			    "TypedArrayForEach");
    +    }
    +
    +    // Step 1-2.
    +    var O = this;
    +
    +    // Step 3-4.
    +    var len = TypedArrayLength(O);
    +
    +    // Step 5.
    +    if (arguments.length === 0)
    +	ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'TypedArray.prototype.forEach');
    +    if (!IsCallable(callbackfn))
    +	ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
    +
    +    // Step 6.
    +    var T = thisArg;
    +
    +    // Step 7-8.
    +    // Step 7, 8a (implicit) and 8e.
    +    for (var k = 0; k < len; k++) {
    +	// Step 8b-8c are unnecessary since the condition always holds true for TypedArray.
    +	// Step 8d.
    +	callFunction(callbackfn, T, O[k], k, O);
    +    }
    +
    +    // Step 9.
    +    return undefined;
    +}
    +
     // ES6 draft rev29 (2014/12/06) 22.2.3.13 %TypedArray%.prototype.indexOf(searchElement[, fromIndex]).
     function TypedArrayIndexOf(searchElement, fromIndex = 0) {
         // This function is not generic.
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/TypedObject.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/TypedObject.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/TypedObject.cpp	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/TypedObject.cpp	2015-02-03 14:33:24.000000000 +0000
    @@ -1495,13 +1495,13 @@
         MOZ_ASSERT(clasp == &OutlineTransparentTypedObject::class_ ||
                    clasp == &OutlineOpaqueTypedObject::class_);
     
    -    RootedTypeObject type(cx, cx->getNewType(clasp, TaggedProto(&descr->typedProto()), descr));
    -    if (!type)
    +    RootedObjectGroup group(cx, cx->getNewGroup(clasp, TaggedProto(&descr->typedProto()), descr));
    +    if (!group)
             return nullptr;
     
         NewObjectKind newKind = (heap == gc::TenuredHeap) ? MaybeSingletonObject : GenericObject;
    -    OutlineTypedObject *obj = NewObjectWithType(cx, type, cx->global(),
    -                                                                    gc::FINALIZE_OBJECT0, newKind);
    +    OutlineTypedObject *obj = NewObjectWithGroup(cx, group, cx->global(),
    +                                                                     gc::FINALIZE_OBJECT0, newKind);
         if (!obj)
             return nullptr;
     
    @@ -1681,8 +1681,8 @@
     }
     
     bool
    -TypedObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
    -                              MutableHandleObject objp, MutableHandleShape propp)
    +TypedObject::obj_lookupProperty(JSContext *cx, HandleObject obj, HandleId id,
    +                                MutableHandleObject objp, MutableHandleShape propp)
     {
         MOZ_ASSERT(obj->is());
     
    @@ -1700,7 +1700,7 @@
                 return obj_lookupElement(cx, obj, index, objp, propp);
     
             if (JSID_IS_ATOM(id, cx->names().length)) {
    -            MarkNonNativePropertyFound(propp);
    +            MarkNonNativePropertyFound(propp);
                 objp.set(obj);
                 return true;
             }
    @@ -1712,7 +1712,7 @@
             StructTypeDescr &structDescr = descr->as();
             size_t index;
             if (structDescr.fieldIndex(id, &index)) {
    -            MarkNonNativePropertyFound(propp);
    +            MarkNonNativePropertyFound(propp);
                 objp.set(obj);
                 return true;
             }
    @@ -1731,22 +1731,11 @@
     }
     
     bool
    -TypedObject::obj_lookupProperty(JSContext *cx,
    -                                HandleObject obj,
    -                                HandlePropertyName name,
    -                                MutableHandleObject objp,
    -                                MutableHandleShape propp)
    -{
    -    RootedId id(cx, NameToId(name));
    -    return obj_lookupGeneric(cx, obj, id, objp, propp);
    -}
    -
    -bool
     TypedObject::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
    -                                MutableHandleObject objp, MutableHandleShape propp)
    +                               MutableHandleObject objp, MutableHandleShape propp)
     {
         MOZ_ASSERT(obj->is());
    -    MarkNonNativePropertyFound(propp);
    +    MarkNonNativePropertyFound(propp);
         objp.set(obj);
         return true;
     }
    @@ -1772,35 +1761,15 @@
     }
     
     bool
    -TypedObject::obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
    -                              PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
    +TypedObject::obj_defineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
    +                                PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
     {
         return ReportPropertyError(cx, JSMSG_UNDEFINED_PROP, id);
     }
     
     bool
    -TypedObject::obj_defineProperty(JSContext *cx, HandleObject obj,
    -                               HandlePropertyName name, HandleValue v,
    -                               PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
    -{
    -    Rooted id(cx, NameToId(name));
    -    return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
    -}
    -
    -bool
    -TypedObject::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
    -                               PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
    -{
    -    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
    -    Rooted id(cx);
    -    if (!IndexToId(cx, index, &id))
    -        return false;
    -    return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
    -}
    -
    -bool
    -TypedObject::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
    -                           HandleId id, MutableHandleValue vp)
    +TypedObject::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
    +                             HandleId id, MutableHandleValue vp)
     {
         MOZ_ASSERT(obj->is());
         Rooted typedObj(cx, &obj->as());
    @@ -1857,16 +1826,8 @@
     }
     
     bool
    -TypedObject::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
    -                              HandlePropertyName name, MutableHandleValue vp)
    -{
    -    RootedId id(cx, NameToId(name));
    -    return obj_getGeneric(cx, obj, receiver, id, vp);
    -}
    -
    -bool
     TypedObject::obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
    -                             uint32_t index, MutableHandleValue vp)
    +                            uint32_t index, MutableHandleValue vp)
     {
         MOZ_ASSERT(obj->is());
         Rooted typedObj(cx, &obj->as());
    @@ -1894,10 +1855,10 @@
     
     /*static*/ bool
     TypedObject::obj_getArrayElement(JSContext *cx,
    -                                Handle typedObj,
    -                                Handle typeDescr,
    -                                uint32_t index,
    -                                MutableHandleValue vp)
    +                                 Handle typedObj,
    +                                 Handle typeDescr,
    +                                 uint32_t index,
    +                                 MutableHandleValue vp)
     {
         if (index >= (size_t) typedObj->length()) {
             vp.setUndefined();
    @@ -1910,8 +1871,8 @@
     }
     
     bool
    -TypedObject::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
    -                           MutableHandleValue vp, bool strict)
    +TypedObject::obj_setProperty(JSContext *cx, HandleObject obj, HandleId id,
    +                             MutableHandleValue vp, bool strict)
     {
         MOZ_ASSERT(obj->is());
         Rooted typedObj(cx, &obj->as());
    @@ -1954,17 +1915,8 @@
     }
     
     bool
    -TypedObject::obj_setProperty(JSContext *cx, HandleObject obj,
    -                             HandlePropertyName name, MutableHandleValue vp,
    -                             bool strict)
    -{
    -    RootedId id(cx, NameToId(name));
    -    return obj_setGeneric(cx, obj, id, vp, strict);
    -}
    -
    -bool
     TypedObject::obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
    -                           MutableHandleValue vp, bool strict)
    +                            MutableHandleValue vp, bool strict)
     {
         MOZ_ASSERT(obj->is());
         Rooted typedObj(cx, &obj->as());
    @@ -2087,8 +2039,8 @@
     }
     
     bool
    -TypedObject::obj_setGenericAttributes(JSContext *cx, HandleObject obj,
    -                                       HandleId id, unsigned *attrsp)
    +TypedObject::obj_setPropertyAttributes(JSContext *cx, HandleObject obj, HandleId id,
    +                                       unsigned *attrsp)
     {
         if (IsOwnId(cx, obj, id))
             return ReportPropertyError(cx, JSMSG_CANT_REDEFINE_PROP, id);
    @@ -2103,7 +2055,7 @@
     }
     
     bool
    -TypedObject::obj_deleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
    +TypedObject::obj_deleteProperty(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
     {
         if (IsOwnId(cx, obj, id))
             return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
    @@ -2179,12 +2131,12 @@
                              ? &InlineOpaqueTypedObject::class_
                              : &InlineTransparentTypedObject::class_;
     
    -    RootedTypeObject type(cx, cx->getNewType(clasp, TaggedProto(&descr->typedProto()), descr));
    -    if (!type)
    +    RootedObjectGroup group(cx, cx->getNewGroup(clasp, TaggedProto(&descr->typedProto()), descr));
    +    if (!group)
             return nullptr;
     
         NewObjectKind newKind = (heap == gc::TenuredHeap) ? MaybeSingletonObject : GenericObject;
    -    return NewObjectWithType(cx, type, cx->global(), allocKind, newKind);
    +    return NewObjectWithGroup(cx, group, cx->global(), allocKind, newKind);
     }
     
     /* static */ InlineTypedObject *
    @@ -2370,21 +2322,13 @@
             JS_NULL_CLASS_SPEC,                              \
             JS_NULL_CLASS_EXT,                               \
             {                                                \
    -            TypedObject::obj_lookupGeneric,              \
                 TypedObject::obj_lookupProperty,             \
    -            TypedObject::obj_lookupElement,              \
    -            TypedObject::obj_defineGeneric,              \
                 TypedObject::obj_defineProperty,             \
    -            TypedObject::obj_defineElement,              \
    -            TypedObject::obj_getGeneric,                 \
                 TypedObject::obj_getProperty,                \
    -            TypedObject::obj_getElement,                 \
    -            TypedObject::obj_setGeneric,                 \
                 TypedObject::obj_setProperty,                \
    -            TypedObject::obj_setElement,                 \
                 TypedObject::obj_getOwnPropertyDescriptor,   \
    -            TypedObject::obj_setGenericAttributes,       \
    -            TypedObject::obj_deleteGeneric,              \
    +            TypedObject::obj_setPropertyAttributes,      \
    +            TypedObject::obj_deleteProperty,             \
                 nullptr, nullptr, /* watch/unwatch */        \
                 nullptr,   /* getElements */                 \
                 TypedObject::obj_enumerate,                  \
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/TypedObject.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/TypedObject.h
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/builtin/TypedObject.h	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/builtin/TypedObject.h	2015-02-03 14:33:24.000000000 +0000
    @@ -525,52 +525,35 @@
                                         MutableHandleValue vp);
     
       protected:
    -    static bool obj_lookupGeneric(JSContext *cx, HandleObject obj,
    -                                  HandleId id, MutableHandleObject objp,
    -                                  MutableHandleShape propp);
    -
         static bool obj_lookupProperty(JSContext *cx, HandleObject obj,
    -                                   HandlePropertyName name,
    -                                   MutableHandleObject objp,
    +                                   HandleId id, MutableHandleObject objp,
                                        MutableHandleShape propp);
     
    -    static bool obj_lookupElement(JSContext *cx, HandleObject obj,
    -                                  uint32_t index, MutableHandleObject objp,
    -                                  MutableHandleShape propp);
    -
    -    static bool obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
    -                                  PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
    +    static bool obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
    +                                  MutableHandleObject objp, MutableHandleShape propp);
     
    -    static bool obj_defineProperty(JSContext *cx, HandleObject obj,
    -                                   HandlePropertyName name, HandleValue v,
    +    static bool obj_defineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
                                        PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
     
    -    static bool obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
    -                                  PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
    -
    -    static bool obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
    -                               HandleId id, MutableHandleValue vp);
    -
         static bool obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
    -                                HandlePropertyName name, MutableHandleValue vp);
    +                                HandleId id, MutableHandleValue vp);
     
         static bool obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
                                    uint32_t index, MutableHandleValue vp);
     
    -    static bool obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
    -                               MutableHandleValue vp, bool strict);
    -    static bool obj_setProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
    +    static bool obj_setProperty(JSContext *cx, HandleObject obj, HandleId id,
                                     MutableHandleValue vp, bool strict);
    +
         static bool obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
                                    MutableHandleValue vp, bool strict);
     
         static bool obj_getOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id,
                                                  MutableHandle desc);
     
    -    static bool obj_setGenericAttributes(JSContext *cx, HandleObject obj,
    -                                         HandleId id, unsigned *attrsp);
    +    static bool obj_setPropertyAttributes(JSContext *cx, HandleObject obj,
    +                                          HandleId id, unsigned *attrsp);
     
    -    static bool obj_deleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded);
    +    static bool obj_deleteProperty(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded);
     
         static bool obj_enumerate(JSContext *cx, HandleObject obj, AutoIdVector &properties);
     
    @@ -584,7 +567,7 @@
         }
     
         TypeDescr &typeDescr() const {
    -        return type()->typeDescr();
    +        return group()->typeDescr();
         }
     
         TypeDescr &maybeForwardedTypeDescr() const {
    @@ -727,20 +710,13 @@
         uint8_t data_[1];
     
       public:
    -    static const size_t MaximumSize =
    -        sizeof(NativeObject) - sizeof(TypedObject) + NativeObject::MAX_FIXED_SLOTS * sizeof(Value);
    +    static const size_t MaximumSize = JSObject::MAX_BYTE_SIZE - sizeof(TypedObject);
     
         static gc::AllocKind allocKindForTypeDescriptor(TypeDescr *descr) {
             size_t nbytes = descr->size();
             MOZ_ASSERT(nbytes <= MaximumSize);
     
    -        if (nbytes <= sizeof(NativeObject) - sizeof(TypedObject))
    -            return gc::FINALIZE_OBJECT0;
    -        nbytes -= sizeof(NativeObject) - sizeof(TypedObject);
    -
    -        size_t dataSlots = AlignBytes(nbytes, sizeof(Value)) / sizeof(Value);
    -        MOZ_ASSERT(nbytes <= dataSlots * sizeof(Value));
    -        return gc::GetGCObjectKind(dataSlots);
    +        return gc::GetGCObjectKindForBytes(nbytes + sizeof(TypedObject));
         }
     
         uint8_t *inlineTypedMem() const {
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/configure.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/configure.in
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/configure.in	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/configure.in	2015-02-03 14:33:24.000000000 +0000
    @@ -419,9 +419,26 @@
     
             if test "$_CC_MAJOR_VERSION" = "18"; then
                 _CC_SUITE=12
    +            MSVC_C_RUNTIME_DLL=msvcr120.dll
    +            MSVC_CXX_RUNTIME_DLL=msvcp120.dll
    +        elif test "$_CC_MAJOR_VERSION" = "19"; then
    +            _CC_SUITE=14
    +            MSVC_C_RUNTIME_DLL=vcruntime140.dll
    +            MSVC_CXX_RUNTIME_DLL=msvcp140.dll
    +            MSVC_APPCRT_DLL=appcrt140.dll
    +            MSVC_DESKTOPCRT_DLL=desktopcrt140.dll
    +
    +            # -Wv:18 disables all warnings introduced after VS2013
    +            # See http://blogs.msdn.com/b/vcblog/archive/2014/11/12/improvements-to-warnings-in-the-c-compiler.aspx
    +            CFLAGS="$CFLAGS -Wv:18"
    +            CXXFLAGS="$CXXFLAGS -Wv:18"
             else
                 AC_MSG_ERROR([This version ($CC_VERSION) of the MSVC compiler is unsupported. See https://developer.mozilla.org/en/Windows_Build_Prerequisites.])
             fi
    +        AC_SUBST(MSVC_C_RUNTIME_DLL)
    +        AC_SUBST(MSVC_CXX_RUNTIME_DLL)
    +        AC_SUBST(MSVC_APPCRT_DLL)
    +        AC_SUBST(MSVC_DESKTOPCRT_DLL)
     
             dnl Ensure that mt.exe is 'Microsoft (R) Manifest Tool',
             dnl not something else like "magnetic tape manipulation utility".
    @@ -3253,11 +3270,11 @@
     fi
     
     dnl ========================================================
    -dnl JS opt-mode assertions and minidump instrumentation
    +dnl JS opt-mode assertions and heap poisoning
     dnl ========================================================
     MOZ_ARG_ENABLE_BOOL(js-diagnostics,
     [  --enable-js-diagnostics
    -                          Enable JS diagnostic assertions and breakpad data],
    +                          Enable JS diagnostic assertions heap poisoning],
         JS_CRASH_DIAGNOSTICS=1,
         JS_CRASH_DIAGNOSTICS= )
     if test -n "$JS_CRASH_DIAGNOSTICS"; then
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/ctypes/CTypes.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/ctypes/CTypes.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/ctypes/CTypes.cpp	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/ctypes/CTypes.cpp	2015-02-03 14:33:24.000000000 +0000
    @@ -909,7 +909,7 @@
       MOZ_ASSERT(fnproto);
     
       // Set up ctypes.CType.prototype.
    -  RootedObject prototype(cx, JS_NewObject(cx, &sCTypeProtoClass, fnproto, parent));
    +  RootedObject prototype(cx, JS_NewObjectWithGivenProto(cx, &sCTypeProtoClass, fnproto, parent));
       if (!prototype)
         return nullptr;
     
    @@ -965,7 +965,7 @@
         return nullptr;
     
       // Set up ctypes.CData.prototype.
    -  RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, NullPtr(), parent));
    +  RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, parent));
       if (!prototype)
         return nullptr;
     
    @@ -1029,7 +1029,7 @@
         return false;
     
       // Set up the .prototype and .prototype.constructor properties.
    -  typeProto.set(JS_NewObject(cx, &sCTypeProtoClass, CTypeProto, parent));
    +  typeProto.set(JS_NewObjectWithGivenProto(cx, &sCTypeProtoClass, CTypeProto, parent));
       if (!typeProto)
         return false;
     
    @@ -1056,7 +1056,7 @@
       // created from the given type constructor. This has ctypes.CData.prototype
       // as its prototype, such that it inherits the properties and functions
       // common to all CDatas.
    -  dataProto.set(JS_NewObject(cx, &sCDataProtoClass, CDataProto, parent));
    +  dataProto.set(JS_NewObjectWithGivenProto(cx, &sCDataProtoClass, CDataProto, parent));
       if (!dataProto)
         return false;
     
    @@ -1363,7 +1363,7 @@
     JS_InitCTypesClass(JSContext* cx, HandleObject global)
     {
       // attach ctypes property to global object
    -  RootedObject ctypes(cx, JS_NewObject(cx, &sCTypesGlobalClass, NullPtr(), NullPtr()));
    +  RootedObject ctypes(cx, JS_NewObject(cx, &sCTypesGlobalClass));
       if (!ctypes)
         return false;
     
    @@ -1386,7 +1386,7 @@
       if (!GetObjectProperty(cx, ctypes, "CDataFinalizer", &ctor))
         return false;
     
    -  RootedObject prototype(cx, JS_NewObject(cx, &sCDataFinalizerProtoClass, NullPtr(), ctypes));
    +  RootedObject prototype(cx, JS_NewObject(cx, &sCDataFinalizerProtoClass, ctypes));
       if (!prototype)
         return false;
     
    @@ -3301,7 +3301,7 @@
       //     * 'constructor' property === 't'
       //     * Additional properties specified by 'ps', as appropriate for the
       //       specific type instance 't'.
    -  RootedObject typeObj(cx, JS_NewObject(cx, &sCTypeClass, typeProto, parent));
    +  RootedObject typeObj(cx, JS_NewObjectWithGivenProto(cx, &sCTypeClass, typeProto, parent));
       if (!typeObj)
         return nullptr;
     
    @@ -3316,7 +3316,7 @@
     
       if (dataProto) {
         // Set up the 'prototype' and 'prototype.constructor' properties.
    -    RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, dataProto, parent));
    +    RootedObject prototype(cx, JS_NewObjectWithGivenProto(cx, &sCDataProtoClass, dataProto, parent));
         if (!prototype)
           return nullptr;
     
    @@ -4868,7 +4868,7 @@
       // Set up the 'prototype' and 'prototype.constructor' properties.
       // The prototype will reflect the struct fields as properties on CData objects
       // created from this type.
    -  RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, dataProto, NullPtr()));
    +  RootedObject prototype(cx, JS_NewObjectWithGivenProto(cx, &sCDataProtoClass, dataProto, NullPtr()));
       if (!prototype)
         return false;
     
    @@ -6075,7 +6075,7 @@
       RootedValue errVal(cx, errVal_);
       MOZ_ASSERT(fnObj);
     
    -  RootedObject result(cx, JS_NewObject(cx, &sCClosureClass, NullPtr(), NullPtr()));
    +  RootedObject result(cx, JS_NewObject(cx, &sCClosureClass));
       if (!result)
         return nullptr;
     
    @@ -6377,7 +6377,7 @@
       RootedObject parent(cx, JS_GetParent(typeObj));
       MOZ_ASSERT(parent);
     
    -  RootedObject dataObj(cx, JS_NewObject(cx, &sCDataClass, proto, parent));
    +  RootedObject dataObj(cx, JS_NewObjectWithGivenProto(cx, &sCDataClass, proto, parent));
       if (!dataObj)
         return nullptr;
     
    @@ -6958,7 +6958,8 @@
     
       // Get arguments
       if (args.length() == 0) { // Special case: the empty (already finalized) object
    -    JSObject *objResult = JS_NewObject(cx, &sCDataFinalizerClass, objProto, NullPtr());
    +    JSObject *objResult = JS_NewObjectWithGivenProto(cx, &sCDataFinalizerClass, objProto,
    +                                                     NullPtr());
         args.rval().setObject(*objResult);
         return true;
       }
    @@ -7057,7 +7058,7 @@
     
       // 5. Create |objResult|
     
    -  JSObject *objResult = JS_NewObject(cx, &sCDataFinalizerClass, objProto, NullPtr());
    +  JSObject *objResult = JS_NewObjectWithGivenProto(cx, &sCDataFinalizerClass, objProto, NullPtr());
       if (!objResult) {
         return false;
       }
    @@ -7357,7 +7358,7 @@
     {
       const JSClass* clasp = isUnsigned ? &sUInt64Class : &sInt64Class;
       RootedObject parent(cx, JS_GetParent(proto));
    -  RootedObject result(cx, JS_NewObject(cx, clasp, proto, parent));
    +  RootedObject result(cx, JS_NewObjectWithGivenProto(cx, clasp, proto, parent));
       if (!result)
         return nullptr;
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/ctypes/Library.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/ctypes/Library.cpp
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/ctypes/Library.cpp	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/ctypes/Library.cpp	2015-02-03 14:33:24.000000000 +0000
    @@ -83,8 +83,7 @@
     Library::Create(JSContext* cx, jsval path_, const JSCTypesCallbacks* callbacks)
     {
       RootedValue path(cx, path_);
    -  RootedObject libraryObj(cx,
    -                          JS_NewObject(cx, &sLibraryClass, NullPtr(), NullPtr()));
    +  RootedObject libraryObj(cx, JS_NewObject(cx, &sLibraryClass));
       if (!libraryObj)
         return nullptr;
     
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/bigTextNodes.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/bigTextNodes.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/bigTextNodes.js	1970-01-01 00:00:00.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/bigTextNodes.js	2015-02-03 14:33:25.000000000 +0000
    @@ -0,0 +1,25 @@
    +window.tests.set('bigTextNodes', (function() {
    +var garbage = [];
    +var garbageIndex = 0;
    +return {
    +    description: "var foo = [ textNode, textNode, ... ]",
    +
    +    load: (N) => { garbage = new Array(N); },
    +    unload: () => { garbage = []; garbageIndex = 0; },
    +
    +    defaultGarbagePerFrame: "8",
    +    defaultGarbageTotal: "8",
    +
    +    makeGarbage: (N) => {
    +        var a = [];
    +        var s = "x";
    +        for (var i = 0; i < 16; i++)
    +            s = s + s;
    +        for (var i = 0; i < N; i++)
    +            a.push(document.createTextNode(s));
    +        garbage[garbageIndex++] = a;
    +        if (garbageIndex == garbage.length)
    +            garbageIndex = 0;
    +    }
    +};
    +})());
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/events.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/events.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/events.js	1970-01-01 00:00:00.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/events.js	2015-02-03 14:33:25.000000000 +0000
    @@ -0,0 +1,25 @@
    +window.tests.set('events', (function() {
    +var garbage = [];
    +var garbageIndex = 0;
    +return {
    +    description: "var foo = [ textNode, textNode, ... ]",
    +
    +    load: (N) => { garbage = new Array(N); },
    +    unload: () => { garbage = []; garbageIndex = 0; },
    +
    +    defaultGarbagePerFrame: "100K",
    +    defaultGarbageTotal: "8",
    +
    +    makeGarbage: (N) => {
    +        var a = [];
    +        for (var i = 0; i < N; i++) {
    +            var e = document.createEvent("Events");
    +            e.initEvent("TestEvent", true, true);
    +            a.push(e);
    +        }
    +        garbage[garbageIndex++] = a;
    +        if (garbageIndex == garbage.length)
    +            garbageIndex = 0;
    +    }
    +};
    +})());
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/expandoEvents.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/expandoEvents.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/expandoEvents.js	1970-01-01 00:00:00.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/expandoEvents.js	2015-02-03 14:33:25.000000000 +0000
    @@ -0,0 +1,26 @@
    +window.tests.set('expandoEvents', (function() {
    +var garbage = [];
    +var garbageIndex = 0;
    +return {
    +    description: "var foo = [ textNode, textNode, ... ]",
    +
    +    load: (N) => { garbage = new Array(N); },
    +    unload: () => { garbage = []; garbageIndex = 0; },
    +
    +    defaultGarbagePerFrame: "100K",
    +    defaultGarbageTotal: "8",
    +
    +    makeGarbage: (N) => {
    +        var a = [];
    +        for (var i = 0; i < N; i++) {
    +            var e = document.createEvent("Events");
    +            e.initEvent("TestEvent", true, true);
    +            e.color = ["tuna"];
    +            a.push(e);
    +        }
    +        garbage[garbageIndex++] = a;
    +        if (garbageIndex == garbage.length)
    +            garbageIndex = 0;
    +    }
    +};
    +})());
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/globalArrayBuffer.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/globalArrayBuffer.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/globalArrayBuffer.js	1970-01-01 00:00:00.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/globalArrayBuffer.js	2015-02-03 14:33:25.000000000 +0000
    @@ -0,0 +1,23 @@
    +window.tests.set('globalArrayBuffer', (function() {
    +var garbage = [];
    +var garbageIndex = 0;
    +return {
    +    description: "var foo = ArrayBuffer(N); # (large malloc data)",
    +
    +    load: (N) => { garbage = new Array(N); },
    +    unload: () => { garbage = []; garbageIndex = 0; },
    +
    +    defaultGarbageTotal: "8K",
    +    defaultGarbagePerFrame: "4M",
    +
    +    makeGarbage: (N) => {
    +        var ab = new ArrayBuffer(N);
    +        var view = new Uint8Array(ab);
    +        view[0] = 1;
    +        view[N - 1] = 2;
    +        garbage[garbageIndex++] = ab;
    +        if (garbageIndex == garbage.length)
    +            garbageIndex = 0;
    +    }
    +};
    +})());
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/globalArrayFgFinalized.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/globalArrayFgFinalized.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/globalArrayFgFinalized.js	1970-01-01 00:00:00.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/globalArrayFgFinalized.js	2015-02-03 14:33:25.000000000 +0000
    @@ -0,0 +1,23 @@
    +window.tests.set('globalArrayFgFinalized', (function() {
    +var garbage = [];
    +var garbageIndex = 0;
    +return {
    +    description: "var foo = [ new Map, new Map, ... ]; # (foreground finalized)",
    +
    +    load: (N) => { garbage = new Array(N); },
    +    unload: () => { garbage = []; garbageIndex = 0; },
    +
    +    defaultGarbageTotal: "8K",
    +    defaultGarbagePerFrame: "1M",
    +
    +    makeGarbage: (N) => {
    +        var arr = [];
    +        for (var i = 0; i < N; i++) {
    +            arr.push(new Map);
    +        }
    +        garbage[garbageIndex++] = arr;
    +        if (garbageIndex == garbage.length)
    +            garbageIndex = 0;
    +    }
    +};
    +})());
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/globalArrayLargeObject.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/globalArrayLargeObject.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/globalArrayLargeObject.js	1970-01-01 00:00:00.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/globalArrayLargeObject.js	2015-02-03 14:33:25.000000000 +0000
    @@ -0,0 +1,23 @@
    +window.tests.set('globalArrayLargeObject', (function() {
    +var garbage = [];
    +var garbageIndex = 0;
    +return {
    +    description: "var foo = { LARGE }; # (large slots)",
    +
    +    load: (N) => { garbage = new Array(N); },
    +    unload: () => { garbage = []; garbageIndex = 0; },
    +
    +    defaultGarbageTotal: "8K",
    +    defaultGarbagePerFrame: "200K",
    +
    +    makeGarbage: (N) => {
    +        var obj = {};
    +        for (var i = 0; i < N; i++) {
    +            obj["key" + i] = i;
    +        }
    +        garbage[garbageIndex++] = obj;
    +        if (garbageIndex == garbage.length)
    +            garbageIndex = 0;
    +    }
    +};
    +})());
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/pairCyclicWeakMap.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/pairCyclicWeakMap.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/pairCyclicWeakMap.js	1970-01-01 00:00:00.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/pairCyclicWeakMap.js	2015-02-03 14:33:25.000000000 +0000
    @@ -0,0 +1,33 @@
    +window.tests.set('pairCyclicWeakMap', (function() {
    +var garbage = [];
    +var garbageIndex = 0;
    +return {
    +    description: "wm1[k1] = k2; wm2[k2] = k3; wm1[k3] = k4; wm2[k4] = ...",
    +
    +    defaultGarbagePerFrame: "1K",
    +    defaultGarbageTotal: "1K",
    +
    +    load: (N) => { garbage = new Array(N); },
    +
    +    unload: () => { garbage = []; garbageIndex = 0; },
    +
    +    makeGarbage: (M) => {
    +        var wm1 = new WeakMap();
    +        var wm2 = new WeakMap();
    +        var initialKey = {};
    +        var key = initialKey;
    +        var value = {};
    +        for (var i = 0; i < M/2; i++) {
    +            wm1.set(key, value);
    +            key = value;
    +            value = {};
    +            wm2.set(key, value);
    +            key = value;
    +            value = {};
    +        }
    +        garbage[garbageIndex++] = [ initialKey, wm1, wm2 ];
    +        if (garbageIndex == garbage.length)
    +            garbageIndex = 0;
    +    }
    +};
    +})());
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/selfCyclicWeakMap.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/selfCyclicWeakMap.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/selfCyclicWeakMap.js	1970-01-01 00:00:00.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/selfCyclicWeakMap.js	2015-02-03 14:33:25.000000000 +0000
    @@ -0,0 +1,29 @@
    +window.tests.set('selfCyclicWeakMap', (function() {
    +var garbage = [];
    +var garbageIndex = 0;
    +return {
    +    description: "var wm = new WeakMap(); wm[k1] = k2; wm[k2] = k3; ...",
    +
    +    defaultGarbagePerFrame: "1K",
    +    defaultGarbageTotal: "1K",
    +
    +    load: (N) => { garbage = new Array(N); },
    +
    +    unload: () => { garbage = []; garbageIndex = 0; },
    +
    +    makeGarbage: (M) => {
    +        var wm = new WeakMap();
    +        var initialKey = {};
    +        var key = initialKey;
    +        var value = {};
    +        for (var i = 0; i < M; i++) {
    +            wm.set(key, value);
    +            key = value;
    +            value = {};
    +        }
    +        garbage[garbageIndex++] = [ initialKey, wm ];
    +        if (garbageIndex == garbage.length)
    +            garbageIndex = 0;
    +    }
    +};
    +})());
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/textNodes.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/textNodes.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/benchmarks/textNodes.js	1970-01-01 00:00:00.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/benchmarks/textNodes.js	2015-02-03 14:33:25.000000000 +0000
    @@ -0,0 +1,23 @@
    +window.tests.set('textNodes', (function() {
    +var garbage = [];
    +var garbageIndex = 0;
    +return {
    +    description: "var foo = [ textNode, textNode, ... ]",
    +
    +    load: (N) => { garbage = new Array(N); },
    +    unload: () => { garbage = []; garbageIndex = 0; },
    +
    +    defaultGarbagePerFrame: "100K",
    +    defaultGarbageTotal: "8",
    +
    +    makeGarbage: (N) => {
    +        var a = [];
    +        for (var i = 0; i < N; i++) {
    +            a.push(document.createTextNode("t" + i));
    +        }
    +        garbage[garbageIndex++] = a;
    +        if (garbageIndex == garbage.length)
    +            garbageIndex = 0;
    +    }
    +};
    +})());
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/harness.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/harness.js
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/harness.js	1970-01-01 00:00:00.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/harness.js	2015-02-03 14:33:25.000000000 +0000
    @@ -0,0 +1,414 @@
    +// Per-frame time sampling infra. Also GC'd: hopefully will not perturb things too badly.
    +var numSamples = 500;
    +var delays = new Array(numSamples);
    +var sampleIndex = 0;
    +var sampleTime = 16; // ms
    +var gHistogram = new Map(); // {ms: count}
    +
    +// Draw state.
    +var stopped = 0;
    +var start;
    +var prev;
    +var ctx;
    +
    +// Current test state.
    +var activeTest = undefined;
    +var testDuration = undefined; // ms
    +var testState = 'idle';  // One of 'idle' or 'running'.
    +var testStart = undefined; // ms
    +var testQueue = [];
    +
    +// Global defaults
    +var globalDefaultGarbageTotal = "8M";
    +var globalDefaultGarbagePerFrame = "8K";
    +
    +function xpos(index)
    +{
    +    return index * 2;
    +}
    +
    +function ypos(delay)
    +{
    +    var r = 525 - Math.log(delay) * 64;
    +    if (r < 5) return 5;
    +    return r;
    +}
    +
    +function drawHBar(delay, color, label)
    +{
    +    ctx.fillStyle = color;
    +    ctx.strokeStyle = color;
    +    ctx.fillText(label, xpos(numSamples) + 4, ypos(delay) + 3);
    +
    +    ctx.beginPath();
    +    ctx.moveTo(xpos(0), ypos(delay));
    +    ctx.lineTo(xpos(numSamples), ypos(delay));
    +    ctx.stroke();
    +    ctx.strokeStyle = 'rgb(0,0,0)';
    +    ctx.fillStyle = 'rgb(0,0,0)';
    +}
    +
    +function drawScale(delay)
    +{
    +    drawHBar(delay, 'rgb(150,150,150)', `${delay}ms`);
    +}
    +
    +function draw60fps() {
    +    drawHBar(1000/60, '#00cf61', '60fps');
    +}
    +
    +function draw30fps() {
    +    drawHBar(1000/30, '#cf0061', '30fps');
    +}
    +
    +function drawGraph()
    +{
    +    ctx.clearRect(0, 0, 1100, 550);
    +
    +    drawScale(10);
    +    draw60fps();
    +    drawScale(20);
    +    drawScale(30);
    +    draw30fps();
    +    drawScale(50);
    +    drawScale(100);
    +    drawScale(200);
    +    drawScale(400);
    +    drawScale(800);
    +
    +    var worst = 0, worstpos = 0;
    +    ctx.beginPath();
    +    for (var i = 0; i < numSamples; i++) {
    +        ctx.lineTo(xpos(i), ypos(delays[i]));
    +        if (delays[i] >= worst) {
    +            worst = delays[i];
    +            worstpos = i;
    +        }
    +    }
    +    ctx.stroke();
    +
    +    ctx.fillStyle = 'rgb(255,0,0)';
    +    if (worst)
    +        ctx.fillText(''+worst.toFixed(2)+'ms', xpos(worstpos) - 10, ypos(worst) - 14);
    +
    +    ctx.beginPath();
    +    var where = sampleIndex % numSamples;
    +    ctx.arc(xpos(where), ypos(delays[where]), 5, 0, Math.PI*2, true);
    +    ctx.fill();
    +    ctx.fillStyle = 'rgb(0,0,0)';
    +
    +    ctx.fillText('Time', 550, 420);
    +    ctx.save();
    +    ctx.rotate(Math.PI/2);
    +    ctx.fillText('Pause between frames (log scale)', 150, -1060);
    +    ctx.restore();
    +}
    +
    +function stopstart()
    +{
    +    if (stopped) {
    +        window.requestAnimationFrame(handler);
    +        prev = performance.now();
    +        start += prev - stopped;
    +        document.getElementById('stop').value = 'Pause';
    +        stopped = 0;
    +    } else {
    +        document.getElementById('stop').value = 'Resume';
    +        stopped = performance.now();
    +    }
    +}
    +
    +function handler(timestamp)
    +{
    +    if (stopped)
    +        return;
    +
    +    if (testState === 'running' && (timestamp - testStart) > testDuration)
    +        end_test(timestamp);
    +
    +    if (testState == 'running')
    +        document.getElementById("test-progress").textContent = ((testDuration - (timestamp - testStart))/1000).toFixed(1) + " sec";
    +
    +    activeTest.makeGarbage(activeTest.garbagePerFrame);
    +
    +    var elt = document.getElementById('data');
    +    var delay = timestamp - prev;
    +    prev = timestamp;
    +
    +    // Take the histogram at 10us intervals so that we have enough resolution to capture.
    +    // a 16.66[666] target with adequate accuracy.
    +    update_histogram(gHistogram, Math.round(delay * 100));
    +
    +    var t = timestamp - start;
    +    var newIndex = Math.round(t / sampleTime);
    +    while (sampleIndex < newIndex) {
    +        sampleIndex++;
    +        delays[sampleIndex % numSamples] = delay;
    +    }
    +
    +    drawGraph();
    +    window.requestAnimationFrame(handler);
    +}
    +
    +function update_histogram(histogram, delay)
    +{
    +    var current = histogram.has(delay) ? histogram.get(delay) : 0;
    +    histogram.set(delay, ++current);
    +}
    +
    +function reset_draw_state()
    +{
    +    for (var i = 0; i < numSamples; i++)
    +        delays[i] = 0;
    +    start = prev = performance.now();
    +    sampleIndex = 0;
    +}
    +
    +function onunload()
    +{
    +    if (activeTest)
    +        activeTest.unload();
    +    activeTest = undefined;
    +}
    +
    +function onload()
    +{
    +    // Load initial test duration.
    +    duration_changed();
    +
    +    // Load initial garbage size.
    +    garbage_total_changed();
    +    garbage_per_frame_changed();
    +
    +    // Populate the test selection dropdown.
    +    var select = document.getElementById("test-selection");
    +    for (var [name, test] of tests) {
    +        test.name = name;
    +        var option = document.createElement("option");
    +        option.id = name;
    +        option.text = name;
    +        option.title = test.description;
    +        select.add(option);
    +    }
    +
    +    // Load the initial test.
    +    change_active_test('noAllocation');
    +
    +    // Polyfill rAF.
    +    var requestAnimationFrame =
    +        window.requestAnimationFrame || window.mozRequestAnimationFrame ||
    +        window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
    +    window.requestAnimationFrame = requestAnimationFrame;
    +
    +    // Acquire our canvas.
    +    var canvas = document.getElementById('graph');
    +    ctx = canvas.getContext('2d');
    +
    +    // Start drawing.
    +    reset_draw_state();
    +    window.requestAnimationFrame(handler);
    +}
    +
    +function run_one_test()
    +{
    +    start_test_cycle([activeTest.name]);
    +}
    +
    +function run_all_tests()
    +{
    +    start_test_cycle(tests.keys());
    +}
    +
    +function start_test_cycle(tests_to_run)
    +{
    +    // Convert from an iterable to an array for pop.
    +    testQueue = [];
    +    for (var key of tests_to_run)
    +        testQueue.push(key);
    +    testState = 'running';
    +    testStart = performance.now();
    +    gHistogram.clear();
    +
    +    start_test(testQueue.shift());
    +    reset_draw_state();
    +}
    +
    +function start_test(testName)
    +{
    +    change_active_test(testName);
    +    console.log(`Running test: ${testName}`);
    +    document.getElementById("test-selection").value = testName;
    +}
    +
    +function end_test(timestamp)
    +{
    +    document.getElementById("test-progress").textContent = "(not running)";
    +    report_test_result(activeTest, gHistogram);
    +    gHistogram.clear();
    +    console.log(`Ending test ${activeTest.name}`);
    +    if (testQueue.length) {
    +        start_test(testQueue.shift());
    +        testStart = timestamp;
    +    } else {
    +        testState = 'idle';
    +        testStart = 0;
    +    }
    +    reset_draw_state();
    +}
    +
    +function report_test_result(test, histogram)
    +{
    +    var resultList = document.getElementById('results-display');
    +    var resultElem = document.createElement("div");
    +    var score = compute_test_score(histogram);
    +    var sparks = compute_test_spark_histogram(histogram);
    +    var params = `(${format_units(test.garbagePerFrame)},${format_units(test.garbageTotal)})`;
    +    resultElem.innerHTML = `${score.toFixed(3)} ms/s : ${sparks} : ${test.name}${params} - ${test.description}`;
    +    resultList.appendChild(resultElem);
    +}
    +
    +// Compute a score based on the total ms we missed frames by per second.
    +function compute_test_score(histogram)
    +{
    +    var score = 0;
    +    for (var [delay, count] of histogram) {
    +        delay = delay / 100;
    +        score += Math.abs((delay - 16.66) * count);
    +    }
    +    score = score / (testDuration / 1000);
    +    return Math.round(score * 1000) / 1000;
    +}
    +
    +// Build a spark-lines histogram for the test results to show with the aggregate score.
    +function compute_test_spark_histogram(histogram)
    +{
    +    var ranges = [
    +        [-99999999, 16.6],
    +        [16.6, 16.8],
    +        [16.8, 25],
    +        [25, 33.4],
    +        [33.4, 60],
    +        [60, 100],
    +        [100, 300],
    +        [300, 99999999],
    +    ];
    +    var rescaled = new Map();
    +    for (var [delay, count] of histogram) {
    +        delay = delay / 100;
    +        for (var i = 0; i < ranges.length; ++i) {
    +            var low = ranges[i][0];
    +            var high = ranges[i][1];
    +            if (low <= delay && delay < high) {
    +                update_histogram(rescaled, i);
    +                break;
    +            }
    +        }
    +    }
    +    var total = 0;
    +    for (var [i, count] of rescaled)
    +        total += count;
    +    var sparks = "▁▂▃▄▅▆▇█";
    +    var colors = ['#aaaa00', '#007700', '#dd0000', '#ff0000',
    +                  '#ff0000', '#ff0000', '#ff0000', '#ff0000'];
    +    var line = "";
    +    for (var i = 0; i < ranges.length; ++i) {
    +        var amt = rescaled.has(i) ? rescaled.get(i) : 0;
    +        var spark = sparks.charAt(parseInt(amt/total*8));
    +        line += `${spark}`;
    +    }
    +    return line;
    +}
    +
    +function reload_active_test()
    +{
    +    activeTest.unload();
    +    activeTest.load(activeTest.garbageTotal);
    +}
    +
    +function change_active_test(new_test_name)
    +{
    +    if (activeTest)
    +        activeTest.unload();
    +    activeTest = tests.get(new_test_name);
    +
    +    if (!activeTest.garbagePerFrame)
    +        activeTest.garbagePerFrame = activeTest.defaultGarbagePerFrame || globalDefaultGarbagePerFrame;
    +    if (!activeTest.garbageTotal)
    +        activeTest.garbageTotal = activeTest.defaultGarbageTotal || globalDefaultGarbageTotal;
    +
    +    document.getElementById("garbage-per-frame").value = format_units(activeTest.garbagePerFrame);
    +    document.getElementById("garbage-total").value = format_units(activeTest.garbageTotal);
    +
    +    activeTest.load(activeTest.garbageTotal);
    +}
    +
    +function duration_changed()
    +{
    +    var durationInput = document.getElementById('test-duration');
    +    testDuration = parseInt(durationInput.value) * 1000;
    +    console.log(`Updated test duration to: ${testDuration / 1000} seconds`);
    +}
    +
    +function test_changed()
    +{
    +    var select = document.getElementById("test-selection");
    +    console.log(`Switching to test: ${select.value}`);
    +    change_active_test(select.value);
    +    gHistogram.clear();
    +    reset_draw_state();
    +}
    +
    +function parse_units(v)
    +{
    +    if (v.length == 0)
    +        return NaN;
    +    var lastChar = v[v.length - 1].toLowerCase();
    +    if (!isNaN(parseFloat(lastChar)))
    +        return parseFloat(v);
    +    var units = parseFloat(v.substr(0, v.length - 1));
    +    if (lastChar == "k")
    +        return units * 1e3;
    +    if (lastChar == "m")
    +        return units * 1e6;
    +    if (lastChar == "g")
    +        return units * 1e9;
    +    return NaN;
    +}
    +
    +function format_units(n)
    +{
    +    n = String(n);
    +    if (n.length > 9 && n.substr(-9) == "000000000")
    +        return n.substr(0, n.length - 9) + "G";
    +    else if (n.length > 9 && n.substr(-6) == "000000")
    +        return n.substr(0, n.length - 6) + "M";
    +    else if (n.length > 3 && n.substr(-3) == "000")
    +        return n.substr(0, n.length - 3) + "K";
    +    else
    +        return String(n);
    +}
    +
    +function garbage_total_changed()
    +{
    +    var value = parse_units(document.getElementById('garbage-total').value);
    +    if (isNaN(value))
    +        return;
    +    if (activeTest) {
    +        activeTest.garbageTotal = value;
    +        console.log(`Updated garbage-total to ${activeTest.garbageTotal} items`);
    +        reload_active_test();
    +    }
    +    gHistogram.clear();
    +    reset_draw_state();
    +}
    +
    +function garbage_per_frame_changed()
    +{
    +    var value = parse_units(document.getElementById('garbage-per-frame').value);
    +    if (isNaN(value))
    +        return;
    +    if (activeTest) {
    +        activeTest.garbagePerFrame = value;
    +        console.log(`Updated garbage-per-frame to ${activeTest.garbagePerFrame} items`);
    +    }
    +}
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/index.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/index.html
    --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/gc-ubench/index.html	2015-01-25 22:24:28.000000000 +0000
    +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/gc-ubench/index.html	2015-02-03 14:33:25.000000000 +0000
    @@ -2,385 +2,30 @@
     
       GC uBench
       
    -
    -
     
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +
    +  
    +
    +
     
    -
    +
     
     
     
    @@ -389,35 +34,29 @@
     
     
     
    - Duration: s + Duration: s
    - Currently running test: - + Currently running test load: +
    - Garbage items per frame: - - +     Time remaining: (not running) +
    +     Garbage items per frame: +
    - Garbage heap size in items: - - +     Garbage piles: +
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/rootAnalysis/README.txt thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/rootAnalysis/README.txt --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/rootAnalysis/README.txt 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/rootAnalysis/README.txt 2015-02-03 14:33:25.000000000 +0000 @@ -1,39 +1,49 @@ +# Spidermonkey JSAPI rooting analysis + This directory contains scripts and a makefile for running Brian Hackett's static GC rooting analysis on a JS source directory. -To use it: +To use it on SpiderMonkey: + +1. Be on Fedora/CentOS/RedHat Linux x86_64. + + (Specifically, the prebuilt GCC **won't work on Ubuntu** + without the `CFLAGS` and `CXXFLAGS` settings from + http://trac.wildfiregames.com/wiki/StaticRootingAnalysis .) + +2. Have the Gecko build prerequisites installed. + +3. In this directory, run these commands. + + mkdir builddir + cd builddir + ../run-analysis.sh + +`run-analysis.sh` is kind of like `configure` and `make` combined: +the build directory can be wherever you want +and you can name it whatever you want. +(You could just run it right here in the source tree, and it would work, +but don't do that -- it spits out files all over the place and +then you'd have to clean up your source tree later.) + +Output goes to `hazards.txt` in the builddir. + +To use this analysis on any other codebase, +make a copy of `run-analysis.sh` and adapt it for your code. + + +## Overview of what is going on here + +So what does `run-analysis.sh` actually do? -1. Download and compile sixgill. Make sure the gcc plugin is enabled. (The - configure output will tell you.) +1. **It insecurely downloads software over HTTP.** Yeah. + See `run-analysis.sh` for details. - - [sfink] I needed a couple of patches to get it work on Fedora 16/17/18. - Ask me if you need them. +2. It runs `run_complete`, a Perl script, which builds the target + codebase with a custom hacked GCC, generating a few database files + containing (among other data) the full call graph. -2. Compile an optimized JS shell that includes the patch at - . This does not - need to be in the same source tree as you are running these scripts from. - Remember the full path to the resulting JS binary; we'll call it $JS_SHELL - below. - -3. |make clean| in the objdir of the JS source tree that you're going to be - analyzing. (These analysis scripts will default to the tree they are within, - but you can point them at another tree.) - -4. in $objdir/js/src/devtools/analysis, |make JS=$JS_SHELL - SIXGILL=.../path/to/sixgill...|. You may need one or more of the following - additional settings in addition to the |JS| already given: - - - JSOBJDIR: if you are analyzing a different source tree, set this to the - objdir of the tree you want to analyze. - - - ANALYSIS_SCRIPT_DIR: by default, the *.js files within the directory - containing this README will be used, but you can point to a different - directory full. At the time of this writing, there are some changes not in - bhackett's git repo that are necessary, and you'll also need the - gen-hazards.sh shell script. - - - JOBS: set this to the number of parallel jobs you'd like to run the final - analysis pass with. This defaults to 6, somewhat randomly, which gave me a - large speedup even on a machine with only 2 cores. +3. Then it runs `analyze.py`, a Python script, which runs all the scripts + which actually perform the analysis -- the tricky parts. + (Those scripts are written in JS.) -The results will be in rootingHazards.txt diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/rootAnalysis/run-analysis.sh thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/rootAnalysis/run-analysis.sh --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/devtools/rootAnalysis/run-analysis.sh 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/devtools/rootAnalysis/run-analysis.sh 2015-02-03 14:33:25.000000000 +0000 @@ -0,0 +1,85 @@ +# setup.sh - Run the rooting analysis on SpiderMonkey. See `README.txt` for usage. +# +# This script is based on the wiki page: +# http://trac.wildfiregames.com/wiki/StaticRootingAnalysis + +set -eu + +BUILD_DIR="$PWD" +ANALYSIS_SCRIPTDIR="$(dirname $0)" +MOZILLA_SRCDIR="$(cd $ANALYSIS_SCRIPTDIR && (hg root || git rev-parse --show-toplevel))" + + +# Requirements +# ============ +# +# Download and unpack the Sixgill plugin binaries. +# (`wget -c` skips the download if you've already got the file.) +# +# This insecurely downloads software over HTTP. Sorry. +# +# The alternative is building your own Sixgill. That can be a pain and you may +# need some patches to get it to work on your Linux distribution. Ask sfink for +# details. + +mkdir -p downloads +(cd downloads && wget -c http://people.mozilla.org/~sfink/data/hazards-sixgill.tar.xz) +tar xf downloads/hazards-sixgill.tar.xz + +# Download and unpack GCC binaries compatible with the Sixgill plugin. +(cd downloads && wget -c http://people.mozilla.org/~sfink/data/hazards-gcc4.7.tar.xz) +tar xf downloads/hazards-gcc4.7.tar.xz + + +# Generate raw data (.xdb files) +# ============================== +# +# The first step is to generate the .xdb files that contain the information +# needed by the analysis. This is done by compiling SpiderMonkey with the +# sixgill plugin enabled. The plugin creates .xdb files which the analysis +# consumes. + +PATH=$BUILD_DIR/sixgill/usr/bin:$PATH +export PATH +GCCDIR=$BUILD_DIR/gcc/bin +export GCCDIR + +# Create a SpiderMonkey build directory and run configure. +mkdir -p spidermonkey-analysis +(cd spidermonkey-analysis && \ + $MOZILLA_SRCDIR/js/src/configure --enable-optimize) + +# Make SpiderMonkey. +$MOZILLA_SRCDIR/js/src/devtools/rootAnalysis/run_complete \ + --build-root=$BUILD_DIR/spidermonkey-analysis \ + --binaries=$BUILD_DIR/sixgill/usr/bin \ + --wrap-dir=$BUILD_DIR/sixgill/usr/libexec/sixgill/scripts/wrap_gcc \ + --buildcommand='make' \ + --foreground \ + --no-logs \ + . + + +# Run the analysis +# ================ + +# Build *another* copy of SpiderMonkey, using the system C++ compiler, without +# Sixgill. This is what we use to run the analysis. (We don't let you skip this +# step by setting a $JS environment variable or something, because you need +# ctypes. Relax and spin a build. Get yourself a cup of tea.) +mkdir -p spidermonkey-opt +(cd spidermonkey-opt && \ + $MOZILLA_SRCDIR/js/src/configure --enable-optimize --enable-ctypes --enable-nspr-build && \ + make -j8) +JS="$BUILD_DIR/spidermonkey-opt/dist/bin/js" + +# Write a config file used by analyze.py. +rm -f defaults.py +echo "objdir = '${BUILD_DIR}/spidermonkey-analysis'" >> defaults.py +echo "sixgill = '${BUILD_DIR}/sixgill/usr/libexec/sixgill'" >> defaults.py +echo "sixgill_bin = '${BUILD_DIR}/sixgill/usr/bin'" >> defaults.py +echo "js = '${JS}'" >> defaults.py +echo "analysis_scriptdir = '${ANALYSIS_SCRIPTDIR}'" >> defaults.py + +# Run the script that runs the scripts that do the analysis. +python2.7 "${MOZILLA_SRCDIR}/js/src/devtools/rootAnalysis/analyze.py" -j 8 callgraph diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/ds/LifoAlloc.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/ds/LifoAlloc.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/ds/LifoAlloc.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/ds/LifoAlloc.h 2015-02-03 14:33:26.000000000 +0000 @@ -148,9 +148,6 @@ } // namespace detail -MOZ_NORETURN void -CrashAtUnhandlableOOM(const char *reason); - // LIFO bump allocator: used for phase-oriented and fast LIFO allocations. // // Note: |latest| is not necessary "last". We leave BumpChunks latent in the diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/frontend/BytecodeCompiler.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/frontend/BytecodeCompiler.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/frontend/BytecodeCompiler.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/frontend/BytecodeCompiler.cpp 2015-02-03 14:33:26.000000000 +0000 @@ -468,7 +468,7 @@ CompileOptions options(cx, lazy->version()); options.setMutedErrors(lazy->mutedErrors()) - .setFileAndLine(lazy->source()->filename(), lazy->lineno()) + .setFileAndLine(lazy->filename(), lazy->lineno()) .setColumn(lazy->column()) .setCompileAndGo(true) .setNoScriptRval(false) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/frontend/BytecodeEmitter.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/frontend/BytecodeEmitter.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/frontend/BytecodeEmitter.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/frontend/BytecodeEmitter.cpp 2015-02-03 14:33:26.000000000 +0000 @@ -2197,10 +2197,7 @@ // Lazy scripts are never top level (despite always being invoked with a // nullptr parent), and so the hook should never be fired. if (emitterMode != LazyFunction && !parent) { - GlobalObject *compileAndGoGlobal = nullptr; - if (script->compileAndGo()) - compileAndGoGlobal = &script->global(); - Debugger::onNewScript(cx->asJSContext(), script, compileAndGoGlobal); + Debugger::onNewScript(cx->asJSContext(), script); } } @@ -3101,6 +3098,7 @@ if (bce->script->bindingIsAliased(bi)) { ScopeCoordinate sc; sc.setHops(0); + sc.setSlot(0); // initialize to silence GCC warning JS_ALWAYS_TRUE(LookupAliasedNameSlot(bce, bce->script, cx->names().arguments, &sc)); if (!EmitAliasedVarOp(cx, JSOP_SETALIASEDVAR, sc, DontCheckLexical, bce)) return false; @@ -4297,7 +4295,7 @@ } MOZ_ASSERT(idx == count); - types::FixArrayType(cx, obj); + types::FixArrayGroup(cx, obj); vp.setObject(*obj); return true; } @@ -4360,7 +4358,7 @@ } } - types::FixObjectType(cx, obj); + types::FixObjectGroup(cx, obj); vp.setObject(*obj); return true; } @@ -4378,7 +4376,7 @@ return false; RootedNativeObject obj(cx, &value.toObject().as()); - if (!obj->is() && !JSObject::setSingletonType(cx, obj)) + if (!obj->is() && !JSObject::setSingleton(cx, obj)) return false; ObjectBox *objbox = bce->parser->newObjectBox(obj); @@ -4883,15 +4881,10 @@ // Convert iterable to iterator. if (Emit1(cx, bce, JSOP_DUP) < 0) // OBJ OBJ return false; -#ifdef JS_HAS_SYMBOLS if (Emit2(cx, bce, JSOP_SYMBOL, jsbytecode(JS::SymbolCode::iterator)) < 0) // OBJ OBJ @@ITERATOR return false; if (!EmitElemOpBase(cx, bce, JSOP_CALLELEM)) // OBJ ITERFN return false; -#else - if (!EmitAtomOp(cx, cx->names().std_iterator, JSOP_CALLPROP, bce)) // OBJ ITERFN - return false; -#endif if (Emit1(cx, bce, JSOP_SWAP) < 0) // ITERFN OBJ return false; if (EmitCall(cx, bce, JSOP_CALL, 0) < 0) // ITER diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/frontend/FoldConstants.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/frontend/FoldConstants.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/frontend/FoldConstants.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/frontend/FoldConstants.cpp 2015-02-03 14:33:26.000000000 +0000 @@ -7,7 +7,6 @@ #include "frontend/FoldConstants.h" #include "mozilla/FloatingPoint.h" -#include "mozilla/TypedEnum.h" #include "jslibmath.h" @@ -252,7 +251,7 @@ // Expressions that appear in a few specific places are treated specially // during constant folding. This enum tells where a parse node appears. -MOZ_BEGIN_ENUM_CLASS(SyntacticContext, int) +enum class SyntacticContext : int { // pn is an expression, and it appears in a context where only its side // effects and truthiness matter: the condition of an if statement, // conditional expression, while loop, or for(;;) loop; or an operand of && @@ -264,7 +263,7 @@ // Any other syntactic context. Other -MOZ_END_ENUM_CLASS(SyntacticContext) +}; static SyntacticContext condIf(const ParseNode *pn, ParseNodeKind kind) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/frontend/FullParseHandler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/frontend/FullParseHandler.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/frontend/FullParseHandler.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/frontend/FullParseHandler.h 2015-02-03 14:33:26.000000000 +0000 @@ -7,6 +7,7 @@ #ifndef frontend_FullParseHandler_h #define frontend_FullParseHandler_h +#include "mozilla/Attributes.h" #include "mozilla/PodOperations.h" #include "frontend/ParseNode.h" @@ -381,10 +382,7 @@ if (!initialYield) return false; - initialYield->pn_next = stmtList->pn_head; - stmtList->pn_head = initialYield; - stmtList->pn_count++; - + stmtList->prepend(initialYield); return true; } @@ -520,7 +518,10 @@ ParseNode *catchName, ParseNode *catchGuard, ParseNode *catchBody); inline void setLastFunctionArgumentDefault(ParseNode *funcpn, ParseNode *pn); - inline ParseNode *newFunctionDefinition(); + + ParseNode *newFunctionDefinition() { + return new_(pos()); + } void setFunctionBody(ParseNode *pn, ParseNode *kid) { pn->pn_body = kid; } @@ -532,11 +533,37 @@ pn->pn_body->append(argpn); } - inline ParseNode *newLexicalScope(ObjectBox *blockbox); - inline void setLexicalScopeBody(ParseNode *block, ParseNode *body); + ParseNode *newLexicalScope(ObjectBox *blockBox) { + return new_(blockBox, pos()); + } + void setLexicalScopeBody(ParseNode *block, ParseNode *body) { + block->pn_expr = body; + } + + ParseNode *newAssignment(ParseNodeKind kind, ParseNode *lhs, ParseNode *rhs, + ParseContext *pc, JSOp op) + { + return newBinaryOrAppend(kind, lhs, rhs, pc, op); + } + + bool isUnparenthesizedYieldExpression(ParseNode *node) { + return node->isKind(PNK_YIELD) && !node->isInParens(); + } - bool isOperationWithoutParens(ParseNode *pn, ParseNodeKind kind) { - return pn->isKind(kind) && !pn->isInParens(); + bool isUnparenthesizedCommaExpression(ParseNode *node) { + return node->isKind(PNK_COMMA) && !node->isInParens(); + } + + bool isUnparenthesizedAssignment(Node node) { + if (node->isKind(PNK_ASSIGN) && !node->isInParens()) { + // PNK_ASSIGN is also (mis)used for things like |var name = expr;|. + // But this method is only called on actual expressions, so we can + // just assert the node's op is the one used for plain assignment. + MOZ_ASSERT(node->isOp(JSOP_NOP)); + return true; + } + + return false; } inline bool finishInitializerAssignment(ParseNode *pn, ParseNode *init, JSOp op); @@ -564,24 +591,22 @@ return pn->pn_pos; } - ParseNode *newList(ParseNodeKind kind, ParseNode *kid = nullptr, JSOp op = JSOP_NOP) { - ParseNode *pn = ListNode::create(kind, this); - if (!pn) - return nullptr; - pn->setOp(op); - pn->makeEmpty(); - if (kid) { - pn->pn_pos.begin = kid->pn_pos.begin; - pn->append(kid); - } - return pn; + ParseNode *newList(ParseNodeKind kind, JSOp op = JSOP_NOP) { + return new_(kind, op, pos()); + } + + /* New list with one initial child node. kid must be non-null. */ + ParseNode *newList(ParseNodeKind kind, ParseNode *kid, JSOp op = JSOP_NOP) { + return new_(kind, op, kid); } - void addList(ParseNode *pn, ParseNode *kid) { - pn->append(kid); + + + ParseNode *newCommaExpressionList(ParseNode *kid) { + return newList(PNK_COMMA, kid, JSOP_NOP); } - bool isUnparenthesizedYield(ParseNode *pn) { - return pn->isKind(PNK_YIELD) && !pn->isInParens(); + void addList(ParseNode *list, ParseNode *kid) { + list->append(kid); } void setOp(ParseNode *pn, JSOp op) { @@ -597,12 +622,12 @@ MOZ_ASSERT(pn->isArity(PN_LIST)); pn->pn_xflags |= flag; } - ParseNode *setInParens(ParseNode *pn) { + MOZ_WARN_UNUSED_RESULT ParseNode *parenthesize(ParseNode *pn) { pn->setInParens(true); return pn; } - ParseNode *setLikelyIIFE(ParseNode *pn) { - return setInParens(pn); + MOZ_WARN_UNUSED_RESULT ParseNode *setLikelyIIFE(ParseNode *pn) { + return parenthesize(pn); } void setPrologue(ParseNode *pn) { pn->pn_prologue = true; @@ -717,38 +742,6 @@ arg->pn_expr = defaultValue; } -inline ParseNode * -FullParseHandler::newFunctionDefinition() -{ - ParseNode *pn = CodeNode::create(PNK_FUNCTION, this); - if (!pn) - return nullptr; - pn->pn_body = nullptr; - pn->pn_funbox = nullptr; - pn->pn_cookie.makeFree(); - pn->pn_dflags = 0; - return pn; -} - -inline ParseNode * -FullParseHandler::newLexicalScope(ObjectBox *blockbox) -{ - ParseNode *pn = LexicalScopeNode::create(PNK_LEXICALSCOPE, this); - if (!pn) - return nullptr; - - pn->pn_objbox = blockbox; - pn->pn_cookie.makeFree(); - pn->pn_dflags = 0; - return pn; -} - -inline void -FullParseHandler::setLexicalScopeBody(ParseNode *block, ParseNode *kid) -{ - block->pn_expr = kid; -} - inline bool FullParseHandler::finishInitializerAssignment(ParseNode *pn, ParseNode *init, JSOp op) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/frontend/ParseNode.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/frontend/ParseNode.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/frontend/ParseNode.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/frontend/ParseNode.cpp 2015-02-03 14:33:26.000000000 +0000 @@ -244,15 +244,6 @@ return p; } -/* used only by static create methods of subclasses */ - -ParseNode * -ParseNode::create(ParseNodeKind kind, ParseNodeArity arity, FullParseHandler *handler) -{ - const Token &tok = handler->currentToken(); - return handler->new_(kind, JSOP_NOP, arity, tok.pos); -} - ParseNode * ParseNode::append(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right, FullParseHandler *handler) @@ -360,31 +351,35 @@ break; case PN_TERNARY: - NULLCHECK(pn->pn_kid1 = cloneParseTree(opn->pn_kid1)); - NULLCHECK(pn->pn_kid2 = cloneParseTree(opn->pn_kid2)); - NULLCHECK(pn->pn_kid3 = cloneParseTree(opn->pn_kid3)); + if (opn->pn_kid1) + NULLCHECK(pn->pn_kid1 = cloneParseTree(opn->pn_kid1)); + if (opn->pn_kid2) + NULLCHECK(pn->pn_kid2 = cloneParseTree(opn->pn_kid2)); + if (opn->pn_kid3) + NULLCHECK(pn->pn_kid3 = cloneParseTree(opn->pn_kid3)); break; case PN_BINARY: - NULLCHECK(pn->pn_left = cloneParseTree(opn->pn_left)); - if (opn->pn_right != opn->pn_left) - NULLCHECK(pn->pn_right = cloneParseTree(opn->pn_right)); - else - pn->pn_right = pn->pn_left; - pn->pn_iflags = opn->pn_iflags; - break; - case PN_BINARY_OBJ: - NULLCHECK(pn->pn_left = cloneParseTree(opn->pn_left)); - if (opn->pn_right != opn->pn_left) - NULLCHECK(pn->pn_right = cloneParseTree(opn->pn_right)); - else - pn->pn_right = pn->pn_left; - pn->pn_binary_obj = opn->pn_binary_obj; + if (opn->pn_left) + NULLCHECK(pn->pn_left = cloneParseTree(opn->pn_left)); + if (opn->pn_right) { + if (opn->pn_right != opn->pn_left) + NULLCHECK(pn->pn_right = cloneParseTree(opn->pn_right)); + else + pn->pn_right = pn->pn_left; + } + if (opn->isArity(PN_BINARY)) { + pn->pn_iflags = opn->pn_iflags; + } else { + MOZ_ASSERT(opn->isArity(PN_BINARY_OBJ)); + pn->pn_binary_obj = opn->pn_binary_obj; + } break; case PN_UNARY: - NULLCHECK(pn->pn_kid = cloneParseTree(opn->pn_kid)); + if (opn->pn_kid) + NULLCHECK(pn->pn_kid = cloneParseTree(opn->pn_kid)); break; case PN_NAME: diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/frontend/ParseNode.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/frontend/ParseNode.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/frontend/ParseNode.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/frontend/ParseNode.h 2015-02-03 14:33:26.000000000 +0000 @@ -638,8 +638,6 @@ pn_next = pn_link = nullptr; } - static ParseNode *create(ParseNodeKind kind, ParseNodeArity arity, FullParseHandler *handler); - public: /* * Append right to left, forming a list node. |left| must have the given @@ -850,6 +848,15 @@ pn_count++; } + void prepend(ParseNode *pn) { + MOZ_ASSERT(pn_arity == PN_LIST); + pn->pn_next = pn_head; + pn_head = pn; + if (pn_tail == &pn_head) + pn_tail = &pn->pn_next; + pn_count++; + } + void checkListConsistency() #ifndef DEBUG {} @@ -922,10 +929,6 @@ pn_kid = kid; } - static inline UnaryNode *create(ParseNodeKind kind, FullParseHandler *handler) { - return (UnaryNode *) ParseNode::create(kind, PN_UNARY, handler); - } - static bool test(const ParseNode &node) { return node.isArity(PN_UNARY); } @@ -951,10 +954,6 @@ pn_right = right; } - static inline BinaryNode *create(ParseNodeKind kind, FullParseHandler *handler) { - return (BinaryNode *) ParseNode::create(kind, PN_BINARY, handler); - } - static bool test(const ParseNode &node) { return node.isArity(PN_BINARY); } @@ -975,10 +974,6 @@ pn_binary_obj = objbox; } - static inline BinaryObjNode *create(ParseNodeKind kind, FullParseHandler *handler) { - return (BinaryObjNode *) ParseNode::create(kind, PN_BINARY_OBJ, handler); - } - static bool test(const ParseNode &node) { return node.isArity(PN_BINARY_OBJ); } @@ -1009,10 +1004,6 @@ pn_kid3 = kid3; } - static inline TernaryNode *create(ParseNodeKind kind, FullParseHandler *handler) { - return (TernaryNode *) ParseNode::create(kind, PN_TERNARY, handler); - } - static bool test(const ParseNode &node) { return node.isArity(PN_TERNARY); } @@ -1030,16 +1021,18 @@ makeEmpty(); } + ListNode(ParseNodeKind kind, JSOp op, const TokenPos &pos) + : ParseNode(kind, op, PN_LIST, pos) + { + makeEmpty(); + } + ListNode(ParseNodeKind kind, JSOp op, ParseNode *kid) : ParseNode(kind, op, PN_LIST, kid->pn_pos) { initList(kid); } - static inline ListNode *create(ParseNodeKind kind, FullParseHandler *handler) { - return (ListNode *) ParseNode::create(kind, PN_LIST, handler); - } - static bool test(const ParseNode &node) { return node.isArity(PN_LIST); } @@ -1051,8 +1044,13 @@ struct CodeNode : public ParseNode { - static inline CodeNode *create(ParseNodeKind kind, FullParseHandler *handler) { - return (CodeNode *) ParseNode::create(kind, PN_CODE, handler); + explicit CodeNode(const TokenPos &pos) + : ParseNode(PNK_FUNCTION, JSOP_NOP, PN_CODE, pos) + { + MOZ_ASSERT(!pn_body); + MOZ_ASSERT(!pn_funbox); + MOZ_ASSERT(pn_dflags == 0); + pn_cookie.makeFree(); } static bool test(const ParseNode &node) { @@ -1089,8 +1087,22 @@ struct LexicalScopeNode : public ParseNode { - static inline LexicalScopeNode *create(ParseNodeKind kind, FullParseHandler *handler) { - return (LexicalScopeNode *) ParseNode::create(kind, PN_NAME, handler); + LexicalScopeNode(ObjectBox *blockBox, const TokenPos &pos) + : ParseNode(PNK_LEXICALSCOPE, JSOP_NOP, PN_NAME, pos) + { + MOZ_ASSERT(!pn_expr); + MOZ_ASSERT(pn_dflags == 0); + MOZ_ASSERT(pn_blockid == 0); + pn_objbox = blockBox; + pn_cookie.makeFree(); + } + + LexicalScopeNode(ObjectBox *blockBox, ParseNode *blockNode) + : ParseNode(PNK_LEXICALSCOPE, JSOP_NOP, PN_NAME, blockNode->pn_pos) + { + pn_objbox = blockBox; + pn_expr = blockNode; + pn_blockid = blockNode->pn_blockid; } }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/frontend/Parser.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/frontend/Parser.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/frontend/Parser.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/frontend/Parser.cpp 2015-02-03 14:33:26.000000000 +0000 @@ -828,11 +828,9 @@ if (!fn) return null(); - ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &handler); + ParseNode *argsbody = handler.newList(PNK_ARGSBODY); if (!argsbody) return null(); - argsbody->setOp(JSOP_NOP); - argsbody->makeEmpty(); fn->pn_body = argsbody; FunctionBox *funbox = newFunctionBox(fn, fun, /* outerpc = */ nullptr, inheritedDirectives, @@ -1626,7 +1624,7 @@ bool hasDefaults = false; Node duplicatedArg = null(); Node list = null(); - bool disallowDuplicateArgs = false; + bool disallowDuplicateArgs = kind == Arrow; if (type == Getter) { report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s"); @@ -2243,33 +2241,25 @@ if (!body->isArity(PN_LIST)) { ParseNode *block; - block = ListNode::create(PNK_SEQ, &handler); + block = handler.newList(PNK_SEQ, body); if (!block) return false; - block->pn_pos = body->pn_pos; - block->initList(body); - body = block; } - ParseNode *item = UnaryNode::create(PNK_SEMI, &handler); + ParseNode *item = handler.new_(PNK_SEMI, JSOP_NOP, + TokenPos(body->pn_pos.begin, body->pn_pos.begin), + prelude); if (!item) return false; - item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin; - item->pn_kid = prelude; - item->pn_next = body->pn_head; - body->pn_head = item; - if (body->pn_tail == &body->pn_head) - body->pn_tail = &item->pn_next; - ++body->pn_count; + body->prepend(item); body->pn_xflags |= PNX_DESTRUCT; } MOZ_ASSERT(pn->pn_funbox == funbox); MOZ_ASSERT(pn->pn_body->isKind(PNK_ARGSBODY)); pn->pn_body->append(body); - pn->pn_body->pn_pos = body->pn_pos; return true; } @@ -2890,10 +2880,9 @@ MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND); /* Check for (a = b) and warn about possible (a == b) mistype. */ - if (handler.isOperationWithoutParens(pn, PNK_ASSIGN) && - !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN)) - { - return null(); + if (handler.isUnparenthesizedAssignment(pn)) { + if (!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN)) + return null(); } return pn; } @@ -3825,7 +3814,7 @@ else if (kind == PNK_GLOBALCONST) op = JSOP_DEFCONST; - Node pn = handler.newList(kind, null(), op); + Node pn = handler.newList(kind, op); if (!pn) return null(); @@ -4086,14 +4075,9 @@ #endif /* Create a new lexical scope node for these statements. */ - ParseNode *pn1 = LexicalScopeNode::create(PNK_LEXICALSCOPE, &handler); + ParseNode *pn1 = handler.new_(blockbox, pc->blockNode); if (!pn1) return null(); - - pn1->pn_pos = pc->blockNode->pn_pos; - pn1->pn_objbox = blockbox; - pn1->pn_expr = pc->blockNode; - pn1->pn_blockid = pc->blockNode->pn_blockid; pc->blockNode = pn1; } @@ -5928,11 +5912,11 @@ if (!tokenStream.matchToken(&matched, TOK_COMMA)) return null(); if (matched) { - Node seq = handler.newList(PNK_COMMA, pn); + Node seq = handler.newCommaExpressionList(pn); if (!seq) return null(); while (true) { - if (handler.isUnparenthesizedYield(pn)) { + if (handler.isUnparenthesizedYieldExpression(pn)) { report(ParseError, false, pn, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str); return null(); } @@ -6307,7 +6291,7 @@ if (!rhs) return null(); - return handler.newBinaryOrAppend(kind, lhs, rhs, pc, op); + return handler.newAssignment(kind, lhs, rhs, pc, op); } static const char incop_name_str[][10] = {"increment", "decrement"}; @@ -6744,7 +6728,7 @@ } unsigned adjust; - ParseNode *pn, *pn2, *pn3, **pnp; + ParseNode *pn, *pn3, **pnp; StmtInfoPC stmtInfo(context); BindData data(context); TokenKind tt; @@ -6811,11 +6795,11 @@ * index to count each block-local let-variable on the left-hand side * of the in/of. */ - pn2 = BinaryNode::create(PNK_FOR, &handler); + ParseNode *pn2 = handler.new_(PNK_FOR, JSOP_ITER, pos(), + nullptr, nullptr); if (!pn2) return null(); - pn2->setOp(JSOP_ITER); pn2->pn_iflags = JSITER_ENUMERATE; if (allowsForEachIn()) { bool matched; @@ -6935,13 +6919,9 @@ * These are lets to tell the bytecode emitter to emit initialization * code for the temporal dead zone. */ - ParseNode *lets = ListNode::create(PNK_LET, &handler); + ParseNode *lets = handler.newList(PNK_LET, pn3); if (!lets) return null(); - lets->setOp(JSOP_NOP); - lets->pn_pos = pn3->pn_pos; - lets->makeEmpty(); - lets->append(pn3); lets->pn_xflags |= PNX_POPVAR; /* Definitions can't be passed directly to EmitAssignment as lhs. */ @@ -6966,14 +6946,15 @@ if (!tokenStream.matchToken(&matched, TOK_IF)) return null(); if (matched) { - pn2 = TernaryNode::create(PNK_IF, &handler); - if (!pn2) + ParseNode *cond = condition(); + if (!cond) return null(); - pn2->pn_kid1 = condition(); - if (!pn2->pn_kid1) + ParseNode *ifNode = handler.new_(PNK_IF, JSOP_NOP, cond, nullptr, nullptr, + cond->pn_pos); + if (!ifNode) return null(); - *pnp = pn2; - pnp = &pn2->pn_kid2; + *pnp = ifNode; + pnp = &ifNode->pn_kid2; } ParseNode *bodyStmt; @@ -7183,22 +7164,14 @@ { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); - /* Make a new node for the desugared generator function. */ + // Make a new node for the desugared generator function. ParseNode *genfn = generatorComprehensionLambda(LegacyGenerator, expr->pn_pos.begin, expr); if (!genfn) return null(); - /* - * Our result is a call expression that invokes the anonymous generator - * function object. - */ - ParseNode *result = ListNode::create(PNK_GENEXP, &handler); - if (!result) - return null(); - result->setOp(JSOP_CALL); - result->pn_pos.begin = genfn->pn_pos.begin; - result->initList(genfn); - return result; + // Our result is a call expression that invokes the anonymous generator + // function object. + return handler.newList(PNK_GENEXP, genfn, JSOP_CALL); } template <> @@ -7256,7 +7229,7 @@ Node lhs = newName(name); if (!lhs) return null(); - Node decls = handler.newList(PNK_LET, lhs, JSOP_NOP); + Node decls = handler.newList(PNK_LET, lhs); if (!decls) return null(); data.pn = lhs; @@ -7302,10 +7275,9 @@ MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND); /* Check for (a = b) and warn about possible (a == b) mistype. */ - if (handler.isOperationWithoutParens(cond, PNK_ASSIGN) && - !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN)) - { - return null(); + if (handler.isUnparenthesizedAssignment(cond)) { + if (!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN)) + return null(); } Node then = comprehensionTail(comprehensionKind); @@ -7345,7 +7317,7 @@ Node yieldExpr = newYieldExpression(begin, bodyExpr); if (!yieldExpr) return null(); - handler.setInParens(yieldExpr); + yieldExpr = handler.parenthesize(yieldExpr); return handler.newExprStatement(yieldExpr, pos().end); } @@ -7468,7 +7440,7 @@ return false; } - if (handler.isOperationWithoutParens(argNode, PNK_YIELD)) { + if (handler.isUnparenthesizedYieldExpression(argNode)) { TokenKind tt; if (!tokenStream.peekToken(&tt)) return false; @@ -7538,7 +7510,7 @@ /* Check for new expression first. */ if (tt == TOK_NEW) { - lhs = handler.newList(PNK_NEW, null(), JSOP_NEW); + lhs = handler.newList(PNK_NEW, JSOP_NEW); if (!lhs) return null(); @@ -7600,11 +7572,7 @@ tt == TOK_NO_SUBS_TEMPLATE) { JSOp op = JSOP_CALL; - if (tt == TOK_LP) - nextMember = handler.newList(PNK_CALL, null(), JSOP_CALL); - else - nextMember = handler.newList(PNK_TAGGED_TEMPLATE, null(), JSOP_CALL); - + nextMember = handler.newList(tt == TOK_LP ? PNK_CALL : PNK_TAGGED_TEMPLATE, JSOP_CALL); if (!nextMember) return null(); @@ -7948,18 +7916,13 @@ case TOK_NAME: { atom = tokenStream.currentName(); - if (atom == context->names().get) { - if (isGenerator) { - report(ParseError, false, null(), JSMSG_BAD_PROP_ID); - return null(); - } - op = JSOP_INITPROP_GETTER; - } else if (atom == context->names().set) { - if (isGenerator) { - report(ParseError, false, null(), JSMSG_BAD_PROP_ID); - return null(); - } - op = JSOP_INITPROP_SETTER; + // Do not look for accessor syntax on generators + if (!isGenerator && + (atom == context->names().get || + atom == context->names().set)) + { + op = atom == context->names().get ? JSOP_INITPROP_GETTER + : JSOP_INITPROP_SETTER; } else { propname = handler.newIdentifier(atom, pos()); if (!propname) @@ -8310,7 +8273,7 @@ JSMSG_BAD_GENEXP_BODY, js_yield_str); return null(); } - if (handler.isOperationWithoutParens(pn, PNK_COMMA)) { + if (handler.isUnparenthesizedCommaExpression(pn)) { report(ParseError, false, null(), JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str); return null(); @@ -8328,12 +8291,11 @@ return null(); } handler.setEndPosition(pn, pos().end); - handler.setInParens(pn); - return pn; + return handler.parenthesize(pn); } #endif /* JS_HAS_GENERATOR_EXPRS */ - pn = handler.setInParens(pn); + pn = handler.parenthesize(pn); MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); @@ -8389,7 +8351,7 @@ JSMSG_BAD_GENEXP_BODY, js_yield_str); return null(); } - if (handler.isOperationWithoutParens(pn, PNK_COMMA)) { + if (handler.isUnparenthesizedCommaExpression(pn)) { report(ParseError, false, null(), JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str); return null(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/frontend/SyntaxParseHandler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/frontend/SyntaxParseHandler.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/frontend/SyntaxParseHandler.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/frontend/SyntaxParseHandler.h 2015-02-03 14:33:26.000000000 +0000 @@ -7,6 +7,8 @@ #ifndef frontend_SyntaxParseHandler_h #define frontend_SyntaxParseHandler_h +#include "mozilla/Attributes.h" + #include "frontend/ParseNode.h" #include "frontend/TokenStream.h" @@ -39,12 +41,61 @@ NodeGeneric, NodeName, NodeGetProp, - NodeString, NodeStringExprStatement, - NodeLValue + NodeLValue, + + // In rare cases a parenthesized |node| doesn't have the same semantics + // as |node|. Each such node has a special Node value, and we use a + // different Node value to represent the parenthesized form. See also + // isUnparenthesized*(Node), newExprStatement(Node, uint32_t), + // parenthesize(Node), and meaningMightChangeIfParenthesized(Node). + + // The directive prologue at the start of a FunctionBody or ScriptBody + // is the longest sequence (possibly empty) of string literal + // expression statements at the start of a function. Thus we need this + // to treat |"use strict";| as a possible Use Strict Directive and + // |("use strict");| as a useless statement. + NodeUnparenthesizedString, + + // Legacy generator expressions of the form |(expr for (...))| and + // array comprehensions of the form |[expr for (...)]|) don't permit + // |expr| to be a comma expression. Thus we need this to treat + // |(a(), b for (x in []))| as a syntax error and + // |((a(), b) for (x in []))| as a generator that calls |a| and then + // yields |b| each time it's resumed. + NodeUnparenthesizedCommaExpr, + + // Yield expressions currently (but not in ES6 -- a SpiderMonkey bug to + // fix) must generally be parenthesized. (See the uses of + // isUnparenthesizedYieldExpression in Parser.cpp for the rare + // exceptions.) Thus we need this to treat |yield 1, 2;| as a syntax + // error and |(yield 1), 2;| as a comma expression that will yield 1, + // then evaluate to 2. + NodeUnparenthesizedYieldExpr, + + // Assignment expressions in condition contexts could be typos for + // equality checks. (Think |if (x = y)| versus |if (x == y)|.) Thus + // we need this to treat |if (x = y)| as a possible typo and + // |if ((x = y))| as a deliberate assignment within a condition. + // + // (Technically this isn't needed, as these are *only* extraWarnings + // warnings, and parsing with that option disables syntax parsing. But + // it seems best to be consistent, and perhaps the syntax parser will + // eventually enforce extraWarnings and will require this then.) + NodeUnparenthesizedAssignment }; typedef Definition::Kind DefinitionNode; + private: + static bool meaningMightChangeIfParenthesized(Node node) { + return node == NodeUnparenthesizedString || + node == NodeUnparenthesizedCommaExpr || + node == NodeUnparenthesizedYieldExpr || + node == NodeUnparenthesizedAssignment; + } + + + public: SyntaxParseHandler(ExclusiveContext *cx, LifoAlloc &alloc, TokenStream &tokenStream, bool foldConstants, Parser *syntaxParser, LazyScript *lazyOuterFunction) @@ -69,14 +120,14 @@ return Definition::PLACEHOLDER; } - Node newIdentifier(JSAtom *atom, const TokenPos &pos) { return NodeString; } + Node newIdentifier(JSAtom *atom, const TokenPos &pos) { return NodeName; } Node newNumber(double value, DecimalPoint decimalPoint, const TokenPos &pos) { return NodeGeneric; } Node newBooleanLiteral(bool cond, const TokenPos &pos) { return NodeGeneric; } Node newStringLiteral(JSAtom *atom, const TokenPos &pos) { lastAtom = atom; lastStringPos = pos; - return NodeString; + return NodeUnparenthesizedString; } Node newTemplateStringLiteral(JSAtom *atom, const TokenPos &pos) { @@ -135,7 +186,7 @@ bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; } bool addPropertyDefinition(Node literal, Node name, Node expr, bool isShorthand = false) { return true; } bool addMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; } - Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; } + Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeUnparenthesizedYieldExpr; } Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; } // Statements @@ -146,7 +197,7 @@ Node newEmptyStatement(const TokenPos &pos) { return NodeGeneric; } Node newExprStatement(Node expr, uint32_t end) { - return expr == NodeString ? NodeStringExprStatement : NodeGeneric; + return expr == NodeUnparenthesizedString ? NodeStringExprStatement : NodeGeneric; } Node newIfStatement(uint32_t begin, Node cond, Node then, Node else_) { return NodeGeneric; } @@ -195,13 +246,6 @@ Node newLexicalScope(ObjectBox *blockbox) { return NodeGeneric; } void setLexicalScopeBody(Node block, Node body) {} - bool isOperationWithoutParens(Node pn, ParseNodeKind kind) { - // It is OK to return false here, callers should only use this method - // for reporting strict option warnings and parsing code which the - // syntax parser does not handle. - return false; - } - bool finishInitializerAssignment(Node pn, Node init, JSOp op) { return true; } void setBeginPosition(Node pn, Node oth) {} @@ -216,22 +260,54 @@ return tokenStream.currentToken().pos; } - Node newList(ParseNodeKind kind, Node kid = NodeGeneric, JSOp op = JSOP_NOP) { + Node newList(ParseNodeKind kind, JSOp op = JSOP_NOP) { return NodeGeneric; } - void addList(Node pn, Node kid) {} - bool isUnparenthesizedYield(Node pn) { return false; } + Node newList(ParseNodeKind kind, Node kid, JSOp op = JSOP_NOP) { + return NodeGeneric; + } + + Node newCommaExpressionList(Node kid) { + return NodeUnparenthesizedCommaExpr; + } + + void addList(Node list, Node kid) { + MOZ_ASSERT(list == NodeGeneric || list == NodeUnparenthesizedCommaExpr); + } + + Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs, + ParseContext *pc, JSOp op) + { + if (kind == PNK_ASSIGN) + return NodeUnparenthesizedAssignment; + return newBinaryOrAppend(kind, lhs, rhs, pc, op); + } + + bool isUnparenthesizedYieldExpression(Node node) { + return node == NodeUnparenthesizedYieldExpr; + } + + bool isUnparenthesizedCommaExpression(Node node) { + return node == NodeUnparenthesizedCommaExpr; + } + + bool isUnparenthesizedAssignment(Node node) { + return node == NodeUnparenthesizedAssignment; + } void setOp(Node pn, JSOp op) {} void setBlockId(Node pn, unsigned blockid) {} void setFlag(Node pn, unsigned flag) {} void setListFlag(Node pn, unsigned flag) {} - Node setInParens(Node pn) { - // String literals enclosed by parentheses are ignored during - // strict mode parsing. - return (pn == NodeString) ? NodeGeneric : pn; + MOZ_WARN_UNUSED_RESULT Node parenthesize(Node node) { + if (meaningMightChangeIfParenthesized(node)) + return NodeGeneric; + + // In all other cases, the parenthesized form of |node| is equivalent + // to the unparenthesized form: return |node| unchanged. + return node; } - Node setLikelyIIFE(Node pn) { + MOZ_WARN_UNUSED_RESULT Node setLikelyIIFE(Node pn) { return pn; // Remain in syntax-parse mode. } void setPrologue(Node pn) {} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Barrier.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Barrier.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Barrier.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Barrier.h 2015-02-03 14:33:26.000000000 +0000 @@ -183,7 +183,7 @@ class UnownedBaseShape; namespace types { -struct TypeObject; +struct ObjectGroup; } namespace jit { @@ -232,7 +232,7 @@ template <> struct MapTypeToTraceKind{ static const JSGCTraceKind kind = JSTRACE_OBJECT; }; template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; }; template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_JITCODE; }; -template <> struct MapTypeToTraceKind{ static const JSGCTraceKind kind = JSTRACE_TYPE_OBJECT; }; +template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_OBJECT_GROUP; }; // Direct value access used by the write barriers and the jits. void @@ -806,7 +806,7 @@ typedef HeapPtr HeapPtrShape; typedef HeapPtr HeapPtrUnownedBaseShape; typedef HeapPtr HeapPtrJitCode; -typedef HeapPtr HeapPtrTypeObject; +typedef HeapPtr HeapPtrObjectGroup; typedef PreBarriered PreBarrieredValue; typedef RelocatablePtr RelocatableValue; @@ -827,7 +827,7 @@ typedef ReadBarriered ReadBarrieredShape; typedef ReadBarriered ReadBarrieredUnownedBaseShape; typedef ReadBarriered ReadBarrieredJitCode; -typedef ReadBarriered ReadBarrieredTypeObject; +typedef ReadBarriered ReadBarrieredObjectGroup; typedef ReadBarriered ReadBarrieredAtom; typedef ReadBarriered ReadBarrieredSymbol; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/GCRuntime.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/GCRuntime.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/GCRuntime.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/GCRuntime.h 2015-02-03 14:33:26.000000000 +0000 @@ -820,7 +820,13 @@ void releaseHeldRelocatedArenas(); private: - void minorGCImpl(JS::gcreason::Reason reason, Nursery::TypeObjectList *pretenureTypes); + enum IncrementalProgress + { + NotFinished = 0, + Finished + }; + + void minorGCImpl(JS::gcreason::Reason reason, Nursery::ObjectGroupList *pretenureGroups); // For ArenaLists::allocateFromArena() friend class ArenaLists; @@ -860,7 +866,7 @@ bool shouldPreserveJITCode(JSCompartment *comp, int64_t currentTime, JS::gcreason::Reason reason); void bufferGrayRoots(); - bool drainMarkStack(SliceBudget &sliceBudget, gcstats::Phase phase); + IncrementalProgress drainMarkStack(SliceBudget &sliceBudget, gcstats::Phase phase); template void markWeakReferences(gcstats::Phase phase); void markWeakReferencesInCurrentGroup(gcstats::Phase phase); template void markGrayReferences(gcstats::Phase phase); @@ -876,7 +882,7 @@ void beginSweepingZoneGroup(); bool shouldReleaseObservedTypes(); void endSweepingZoneGroup(); - bool sweepPhase(SliceBudget &sliceBudget); + IncrementalProgress sweepPhase(SliceBudget &sliceBudget); void endSweepPhase(bool lastGC); void sweepZones(FreeOp *fop, bool lastGC); void decommitAllWithoutUnlocking(const AutoLockGC &lock); @@ -886,7 +892,7 @@ void sweepBackgroundThings(ZoneList &zones, LifoAlloc &freeBlocks, ThreadType threadType); void assertBackgroundSweepingFinished(); bool shouldCompact(); - bool compactPhase(bool lastGC); + IncrementalProgress compactPhase(bool lastGC); void sweepTypesAfterCompacting(Zone *zone); void sweepZoneAfterCompacting(Zone *zone); ArenaHeader *relocateArenas(); @@ -905,8 +911,6 @@ void validateIncrementalMarking(); void finishMarkingValidation(); - void markConservativeStackRoots(JSTracer *trc, bool useSavedRoots); - #ifdef DEBUG void checkForCompartmentMismatches(); #endif @@ -1093,10 +1097,15 @@ unsigned generationalDisabled; /* - * Some code cannot tolerate compacting GC so it can be disabled with this - * counter. + * Whether compacting GC can is enabled globally. + */ + bool compactingEnabled; + + /* + * Some code cannot tolerate compacting GC so it can be disabled temporarily + * with AutoDisableCompactingGC which uses this counter. */ - unsigned compactingDisabled; + unsigned compactingDisabledCount; /* * This is true if we are in the middle of a brain transplant (e.g., diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/GCTrace.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/GCTrace.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/GCTrace.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/GCTrace.cpp 2015-02-03 14:33:26.000000000 +0000 @@ -25,7 +25,7 @@ static FILE *gcTraceFile = nullptr; static HashSet, SystemAllocPolicy> tracedClasses; -static HashSet, SystemAllocPolicy> tracedTypes; +static HashSet, SystemAllocPolicy> tracedGroups; static inline void WriteWord(uint64_t data) @@ -156,26 +156,26 @@ } static void -MaybeTraceType(TypeObject *type) +MaybeTraceGroup(ObjectGroup *group) { - if (tracedTypes.has(type)) + if (tracedGroups.has(group)) return; - MaybeTraceClass(type->clasp()); - TraceEvent(TraceEventTypeInfo, uint64_t(type)); - TraceAddress(type->clasp()); - TraceInt(type->flags()); + MaybeTraceClass(group->clasp()); + TraceEvent(TraceEventGroupInfo, uint64_t(group)); + TraceAddress(group->clasp()); + TraceInt(group->flags()); - MOZ_ALWAYS_TRUE(tracedTypes.put(type)); + MOZ_ALWAYS_TRUE(tracedGroups.put(group)); } void -js::gc::TraceTypeNewScript(TypeObject *type) +js::gc::TraceTypeNewScript(ObjectGroup *group) { const size_t bufLength = 128; static char buffer[bufLength]; - MOZ_ASSERT(type->hasNewScript()); - JSAtom *funName = type->newScript()->fun->displayAtom(); + MOZ_ASSERT(group->hasNewScript()); + JSAtom *funName = group->newScript()->fun->displayAtom(); if (!funName) return; @@ -184,7 +184,7 @@ CopyChars(reinterpret_cast(buffer), *funName); buffer[length] = 0; - TraceEvent(TraceEventTypeNewScript, uint64_t(type)); + TraceEvent(TraceEventTypeNewScript, uint64_t(group)); TraceString(buffer); } @@ -194,10 +194,10 @@ if (!gcTraceFile) return; - TypeObject *type = object->type(); - MaybeTraceType(type); + ObjectGroup *group = object->group(); + MaybeTraceGroup(group); TraceEvent(TraceEventCreateObject, uint64_t(object)); - TraceAddress(type); + TraceAddress(group); } void @@ -230,8 +230,8 @@ { if (!gcTraceFile) return; - if (thing->tenuredGetAllocKind() == FINALIZE_TYPE_OBJECT) - tracedTypes.remove(static_cast(thing)); + if (thing->tenuredGetAllocKind() == FINALIZE_OBJECT_GROUP) + tracedGroups.remove(static_cast(thing)); TraceEvent(TraceEventTenuredFinalize, uint64_t(thing)); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/GCTrace.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/GCTrace.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/GCTrace.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/GCTrace.h 2015-02-03 14:33:26.000000000 +0000 @@ -11,7 +11,7 @@ namespace js { -namespace types { struct TypeObject; } +namespace types { struct ObjectGroup; } namespace gc { @@ -29,7 +29,7 @@ extern void TraceMajorGCStart(); extern void TraceTenuredFinalize(Cell *thing); extern void TraceMajorGCEnd(); -extern void TraceTypeNewScript(js::types::TypeObject *type); +extern void TraceTypeNewScript(js::types::ObjectGroup *group); #else @@ -45,7 +45,7 @@ inline void TraceMajorGCStart() {} inline void TraceTenuredFinalize(Cell *thing) {} inline void TraceMajorGCEnd() {} -inline void TraceTypeNewScript(js::types::TypeObject *type) {} +inline void TraceTypeNewScript(js::types::ObjectGroup *group) {} #endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Heap.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Heap.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Heap.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Heap.h 2015-02-03 14:33:26.000000000 +0000 @@ -94,7 +94,7 @@ FINALIZE_SHAPE, FINALIZE_ACCESSOR_SHAPE, FINALIZE_BASE_SHAPE, - FINALIZE_TYPE_OBJECT, + FINALIZE_OBJECT_GROUP, FINALIZE_FAT_INLINE_STRING, FINALIZE_STRING, FINALIZE_EXTERNAL_STRING, @@ -110,29 +110,29 @@ MapAllocToTraceKind(AllocKind kind) { static const JSGCTraceKind map[] = { - JSTRACE_OBJECT, /* FINALIZE_OBJECT0 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT0_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT2 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT2_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT4 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT4_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT8 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT8_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT12 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT12_BACKGROUND */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT16 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT16_BACKGROUND */ - JSTRACE_SCRIPT, /* FINALIZE_SCRIPT */ - JSTRACE_LAZY_SCRIPT,/* FINALIZE_LAZY_SCRIPT */ - JSTRACE_SHAPE, /* FINALIZE_SHAPE */ - JSTRACE_SHAPE, /* FINALIZE_ACCESSOR_SHAPE */ - JSTRACE_BASE_SHAPE, /* FINALIZE_BASE_SHAPE */ - JSTRACE_TYPE_OBJECT,/* FINALIZE_TYPE_OBJECT */ - JSTRACE_STRING, /* FINALIZE_FAT_INLINE_STRING */ - JSTRACE_STRING, /* FINALIZE_STRING */ - JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING */ - JSTRACE_SYMBOL, /* FINALIZE_SYMBOL */ - JSTRACE_JITCODE, /* FINALIZE_JITCODE */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT0 */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT0_BACKGROUND */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT2 */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT2_BACKGROUND */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT4 */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT4_BACKGROUND */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT8 */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT8_BACKGROUND */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT12 */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT12_BACKGROUND */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT16 */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT16_BACKGROUND */ + JSTRACE_SCRIPT, /* FINALIZE_SCRIPT */ + JSTRACE_LAZY_SCRIPT, /* FINALIZE_LAZY_SCRIPT */ + JSTRACE_SHAPE, /* FINALIZE_SHAPE */ + JSTRACE_SHAPE, /* FINALIZE_ACCESSOR_SHAPE */ + JSTRACE_BASE_SHAPE, /* FINALIZE_BASE_SHAPE */ + JSTRACE_OBJECT_GROUP, /* FINALIZE_OBJECT_GROUP */ + JSTRACE_STRING, /* FINALIZE_FAT_INLINE_STRING */ + JSTRACE_STRING, /* FINALIZE_STRING */ + JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING */ + JSTRACE_SYMBOL, /* FINALIZE_SYMBOL */ + JSTRACE_JITCODE, /* FINALIZE_JITCODE */ }; static_assert(MOZ_ARRAY_LENGTH(map) == FINALIZE_LIMIT, @@ -528,14 +528,12 @@ /* * One of AllocKind constants or FINALIZE_LIMIT when the arena does not * contain any GC things and is on the list of empty arenas in the GC - * chunk. The latter allows to quickly check if the arena is allocated - * during the conservative GC scanning without searching the arena in the - * list. + * chunk. * * We use 8 bits for the allocKind so the compiler can use byte-level memory * instructions to access it. */ - size_t allocKind : 8; + size_t allocKind : 8; /* * When collecting we sometimes need to keep an auxillary list of arenas, @@ -561,7 +559,7 @@ * flags. */ public: - size_t hasDelayedMarking : 1; + size_t hasDelayedMarking : 1; size_t allocatedDuringIncremental : 1; size_t markOverflow : 1; size_t auxNextLink : JS_BITS_PER_WORD - 8 - 1 - 1 - 1; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Marking.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Marking.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Marking.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Marking.cpp 2015-02-03 14:33:26.000000000 +0000 @@ -19,6 +19,7 @@ #include "vm/Shape.h" #include "vm/Symbol.h" #include "vm/TypedArrayObject.h" +#include "vm/UnboxedObject.h" #include "jscompartmentinlines.h" #include "jsinferinlines.h" @@ -86,7 +87,7 @@ PushMarkStack(GCMarker *gcmarker, JS::Symbol *sym); static inline void -PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing); +PushMarkStack(GCMarker *gcmarker, types::ObjectGroup *thing); namespace js { namespace gc { @@ -97,7 +98,7 @@ static void MarkChildren(JSTracer *trc, LazyScript *lazy); static void MarkChildren(JSTracer *trc, Shape *shape); static void MarkChildren(JSTracer *trc, BaseShape *base); -static void MarkChildren(JSTracer *trc, types::TypeObject *type); +static void MarkChildren(JSTracer *trc, types::ObjectGroup *group); static void MarkChildren(JSTracer *trc, jit::JitCode *code); } /* namespace gc */ @@ -613,7 +614,7 @@ DeclMarkerImpl(String, JSLinearString) DeclMarkerImpl(String, PropertyName) DeclMarkerImpl(Symbol, JS::Symbol) -DeclMarkerImpl(TypeObject, js::types::TypeObject) +DeclMarkerImpl(ObjectGroup, js::types::ObjectGroup) } /* namespace gc */ } /* namespace js */ @@ -653,8 +654,8 @@ case JSTRACE_SHAPE: MarkInternal(trc, reinterpret_cast(thingp)); break; - case JSTRACE_TYPE_OBJECT: - MarkInternal(trc, reinterpret_cast(thingp)); + case JSTRACE_OBJECT_GROUP: + MarkInternal(trc, reinterpret_cast(thingp)); break; default: MOZ_CRASH("Invalid trace kind in MarkKind."); @@ -790,14 +791,14 @@ { JS_ROOT_MARKING_ASSERT(trc); trc->setTracingName(name); - if (v->isSingleObject()) { - JSObject *obj = v->singleObject(); + if (v->isSingleton()) { + JSObject *obj = v->singleton(); MarkInternal(trc, &obj); *v = types::Type::ObjectType(obj); - } else if (v->isTypeObject()) { - types::TypeObject *typeObj = v->typeObject(); - MarkInternal(trc, &typeObj); - *v = types::Type::ObjectType(typeObj); + } else if (v->isGroup()) { + types::ObjectGroup *group = v->group(); + MarkInternal(trc, &group); + *v = types::Type::ObjectType(group); } } @@ -1065,7 +1066,7 @@ } static void -PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing) +PushMarkStack(GCMarker *gcmarker, types::ObjectGroup *thing) { JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing); MOZ_ASSERT(!IsInsideNursery(thing)); @@ -1421,57 +1422,63 @@ } static void -ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type) +ScanObjectGroup(GCMarker *gcmarker, types::ObjectGroup *group) { - unsigned count = type->getPropertyCount(); + unsigned count = group->getPropertyCount(); for (unsigned i = 0; i < count; i++) { - if (types::Property *prop = type->getProperty(i)) - MarkId(gcmarker, &prop->id, "TypeObject property id"); + if (types::Property *prop = group->getProperty(i)) + MarkId(gcmarker, &prop->id, "ObjectGroup property id"); } - if (type->proto().isObject()) - PushMarkStack(gcmarker, type->proto().toObject()); + if (group->proto().isObject()) + PushMarkStack(gcmarker, group->proto().toObject()); - if (type->singleton() && !type->lazy()) - PushMarkStack(gcmarker, type->singleton()); + if (group->singleton() && !group->lazy()) + PushMarkStack(gcmarker, group->singleton()); - if (type->newScript()) - type->newScript()->trace(gcmarker); + if (group->newScript()) + group->newScript()->trace(gcmarker); - if (TypeDescr *descr = type->maybeTypeDescr()) + if (group->maybeUnboxedLayout()) + group->unboxedLayout().trace(gcmarker); + + if (TypeDescr *descr = group->maybeTypeDescr()) PushMarkStack(gcmarker, descr); - if (JSFunction *fun = type->maybeInterpretedFunction()) + if (JSFunction *fun = group->maybeInterpretedFunction()) PushMarkStack(gcmarker, fun); } static void -gc::MarkChildren(JSTracer *trc, types::TypeObject *type) +gc::MarkChildren(JSTracer *trc, types::ObjectGroup *group) { - unsigned count = type->getPropertyCount(); + unsigned count = group->getPropertyCount(); for (unsigned i = 0; i < count; i++) { - types::Property *prop = type->getProperty(i); + types::Property *prop = group->getProperty(i); if (prop) - MarkId(trc, &prop->id, "type_prop"); + MarkId(trc, &prop->id, "group_property"); } - if (type->proto().isObject()) - MarkObject(trc, &type->protoRaw(), "type_proto"); + if (group->proto().isObject()) + MarkObject(trc, &group->protoRaw(), "group_proto"); + + if (group->singleton() && !group->lazy()) + MarkObject(trc, &group->singletonRaw(), "group_singleton"); - if (type->singleton() && !type->lazy()) - MarkObject(trc, &type->singletonRaw(), "type_singleton"); + if (group->newScript()) + group->newScript()->trace(trc); - if (type->newScript()) - type->newScript()->trace(trc); + if (group->maybeUnboxedLayout()) + group->unboxedLayout().trace(trc); - if (JSObject *descr = type->maybeTypeDescr()) { - MarkObjectUnbarriered(trc, &descr, "type_descr"); - type->setTypeDescr(&descr->as()); + if (JSObject *descr = group->maybeTypeDescr()) { + MarkObjectUnbarriered(trc, &descr, "group_type_descr"); + group->setTypeDescr(&descr->as()); } - if (JSObject *fun = type->maybeInterpretedFunction()) { - MarkObjectUnbarriered(trc, &fun, "type_function"); - type->setInterpretedFunction(&fun->as()); + if (JSObject *fun = group->maybeInterpretedFunction()) { + MarkObjectUnbarriered(trc, &fun, "group_function"); + group->setInterpretedFunction(&fun->as()); } } @@ -1525,8 +1532,8 @@ PushArenaTyped(gcmarker, aheader); break; - case JSTRACE_TYPE_OBJECT: - PushArenaTyped(gcmarker, aheader); + case JSTRACE_OBJECT_GROUP: + PushArenaTyped(gcmarker, aheader); break; default: @@ -1644,8 +1651,8 @@ void GCMarker::processMarkStackOther(uintptr_t tag, uintptr_t addr) { - if (tag == TypeTag) { - ScanTypeObject(this, reinterpret_cast(addr)); + if (tag == GroupTag) { + ScanObjectGroup(this, reinterpret_cast(addr)); } else if (tag == SavedValueArrayTag) { MOZ_ASSERT(!(addr & CellMask)); NativeObject *obj = reinterpret_cast(addr); @@ -1700,6 +1707,9 @@ HeapSlot *vp, *end; JSObject *obj; + const int32_t *unboxedTraceList; + uint8_t *unboxedMemory; + uintptr_t addr = stack.pop(); uintptr_t tag = addr & StackTagMask; addr &= ~StackTagMask; @@ -1745,28 +1755,23 @@ } return; - scan_typed_obj: + scan_unboxed: { - TypeDescr *descr = &obj->as().typeDescr(); - if (!descr->hasTraceList()) - return; - const int32_t *list = descr->traceList(); - uint8_t *memory = obj->as().inlineTypedMem(); - while (*list != -1) { - JSString *str = *reinterpret_cast(memory + *list); + while (*unboxedTraceList != -1) { + JSString *str = *reinterpret_cast(unboxedMemory + *unboxedTraceList); markAndScanString(obj, str); - list++; + unboxedTraceList++; } - list++; - while (*list != -1) { - JSObject *obj2 = *reinterpret_cast(memory + *list); + unboxedTraceList++; + while (*unboxedTraceList != -1) { + JSObject *obj2 = *reinterpret_cast(unboxedMemory + *unboxedTraceList); if (obj2 && markObject(obj, obj2)) pushObject(obj2); - list++; + unboxedTraceList++; } - list++; - while (*list != -1) { - const Value &v = *reinterpret_cast(memory + *list); + unboxedTraceList++; + while (*unboxedTraceList != -1) { + const Value &v = *reinterpret_cast(unboxedMemory + *unboxedTraceList); if (v.isString()) { markAndScanString(obj, v.toString()); } else if (v.isObject()) { @@ -1776,7 +1781,7 @@ } else if (v.isSymbol()) { markAndScanSymbol(obj, v.toSymbol()); } - list++; + unboxedTraceList++; } return; } @@ -1791,14 +1796,14 @@ return; } - types::TypeObject *type = obj->typeFromGC(); - PushMarkStack(this, type); + types::ObjectGroup *group = obj->groupFromGC(); + PushMarkStack(this, group); Shape *shape = obj->lastProperty(); PushMarkStack(this, shape); /* Call the trace hook if necessary. */ - const Class *clasp = type->clasp(); + const Class *clasp = group->clasp(); if (clasp->trace) { // Global objects all have the same trace hook. That hook is safe without barriers // if the global has no custom trace hook of its own, or has been moved to a different @@ -1806,8 +1811,22 @@ MOZ_ASSERT_IF(!(clasp->trace == JS_GlobalObjectTraceHook && (!obj->compartment()->options().getTrace() || !obj->isOwnGlobal())), clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS); - if (clasp->trace == InlineTypedObject::obj_trace) - goto scan_typed_obj; + if (clasp->trace == InlineTypedObject::obj_trace) { + TypeDescr *descr = &obj->as().typeDescr(); + if (!descr->hasTraceList()) + return; + unboxedTraceList = descr->traceList(); + unboxedMemory = obj->as().inlineTypedMem(); + goto scan_unboxed; + } + if (clasp == &UnboxedPlainObject::class_) { + const UnboxedLayout &layout = obj->as().layout(); + unboxedTraceList = layout.traceList(); + if (!unboxedTraceList) + return; + unboxedMemory = obj->as().data(); + goto scan_unboxed; + } clasp->trace(this, obj); } @@ -1931,8 +1950,8 @@ MarkChildren(trc, static_cast(thing)); break; - case JSTRACE_TYPE_OBJECT: - MarkChildren(trc, (types::TypeObject *)thing); + case JSTRACE_OBJECT_GROUP: + MarkChildren(trc, (types::ObjectGroup *)thing); break; default: diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Marking.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Marking.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Marking.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Marking.h 2015-02-03 14:33:26.000000000 +0000 @@ -132,7 +132,7 @@ DeclMarker(String, JSLinearString) DeclMarker(String, PropertyName) DeclMarker(Symbol, JS::Symbol) -DeclMarker(TypeObject, types::TypeObject) +DeclMarker(ObjectGroup, types::ObjectGroup) #undef DeclMarker diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Memory.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Memory.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Memory.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Memory.cpp 2015-02-03 14:33:26.000000000 +0000 @@ -379,7 +379,7 @@ MapMemoryAt(void *desired, size_t length, int prot = PROT_READ | PROT_WRITE, int flags = MAP_PRIVATE | MAP_ANON, int fd = -1, off_t offset = 0) { -#if defined(__ia64__) +#if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__)) MOZ_ASSERT(0xffff800000000000ULL & (uintptr_t(desired) + length - 1) == 0); #endif void *region = mmap(desired, length, prot, flags, fd, offset); @@ -402,7 +402,7 @@ MapMemory(size_t length, int prot = PROT_READ | PROT_WRITE, int flags = MAP_PRIVATE | MAP_ANON, int fd = -1, off_t offset = 0) { -#if defined(__ia64__) +#if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__)) /* * The JS engine assumes that all allocated pointers have their high 17 bits clear, * which ia64's mmap doesn't support directly. However, we can emulate it by passing diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Nursery.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Nursery.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Nursery.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Nursery.cpp 2015-02-03 14:33:26.000000000 +0000 @@ -164,10 +164,10 @@ if (overlay->isForwarded()) obj = static_cast(overlay->forwardingAddress()); MOZ_ASSERT(obj); - MOZ_ASSERT(obj->type()); - MOZ_ASSERT(obj->type()->clasp()); - MOZ_ASSERT(obj->type()->clasp()->finalize); - MOZ_ASSERT(obj->type()->clasp()->flags & JSCLASS_FINALIZE_FROM_NURSERY); + MOZ_ASSERT(obj->group()); + MOZ_ASSERT(obj->group()->clasp()); + MOZ_ASSERT(obj->group()->clasp()->finalize); + MOZ_ASSERT(obj->group()->clasp()->flags & JSCLASS_FINALIZE_FROM_NURSERY); } #endif // DEBUG } @@ -420,6 +420,12 @@ // Proxies have finalizers and are not nursery allocated. MOZ_ASSERT(!IsProxy(obj)); + // Unboxed plain objects are sized according to the data they store. + if (obj->is()) { + size_t nbytes = obj->as().layout().size(); + return GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + nbytes); + } + // Inlined typed objects are followed by their data, so make sure we copy // it all over to the new object. if (obj->is()) { @@ -435,7 +441,7 @@ if (obj->is()) return FINALIZE_OBJECT0; - // The only non-native objects in existence are proxies and typed objects. + // All nursery allocatable non-native objects are handled above. MOZ_ASSERT(obj->isNative()); AllocKind kind = GetGCObjectFixedSlotsKind(obj->as().numFixedSlots()); @@ -527,15 +533,15 @@ MOZ_ASSERT(IsWriteableAddress(*pSlotsElems)); } -// Structure for counting how many times objects of a particular type have been -// tenured during a minor collection. +// Structure for counting how many times objects in a particular group have +// been tenured during a minor collection. struct TenureCount { - types::TypeObject *type; + types::ObjectGroup *group; int count; }; -// Keep rough track of how many times we tenure objects of particular types +// Keep rough track of how many times we tenure objects in particular groups // during minor collections, using a fixed size hash for efficiency at the cost // of potential collisions. struct Nursery::TenureCountCache @@ -544,8 +550,8 @@ TenureCountCache() { PodZero(this); } - TenureCount &findEntry(types::TypeObject *type) { - return entries[PointerHasher::hash(type) % ArrayLength(entries)]; + TenureCount &findEntry(types::ObjectGroup *group) { + return entries[PointerHasher::hash(group) % ArrayLength(entries)]; } }; @@ -556,11 +562,11 @@ JSObject *obj = static_cast(p->forwardingAddress()); traceObject(trc, obj); - TenureCount &entry = tenureCounts.findEntry(obj->type()); - if (entry.type == obj->type()) { + TenureCount &entry = tenureCounts.findEntry(obj->group()); + if (entry.group == obj->group()) { entry.count++; - } else if (!entry.type) { - entry.type = obj->type(); + } else if (!entry.group) { + entry.group = obj->group(); entry.count = 1; } } @@ -759,7 +765,7 @@ #define TIME_TOTAL(name) (timstampEnd_##name - timstampStart_##name) void -js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList *pretenureTypes) +js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason, ObjectGroupList *pretenureGroups) { if (rt->mainThread.suppressGC) return; @@ -859,7 +865,7 @@ // Update any slot or element pointers whose destination has been tenured. TIME_START(updateJitActivations); - js::jit::UpdateJitActivationsForMinorGC(&rt->mainThread, &trc); + js::jit::UpdateJitActivationsForMinorGC(rt, &trc); forwardedBuffers.finish(); TIME_END(updateJitActivations); @@ -891,14 +897,14 @@ // If we are promoting the nursery, or exhausted the store buffer with // pointers to nursery things, which will force a collection well before - // the nursery is full, look for object types that are getting promoted + // the nursery is full, look for object groups that are getting promoted // excessively and try to pretenure them. TIME_START(pretenure); - if (pretenureTypes && (promotionRate > 0.8 || reason == JS::gcreason::FULL_STORE_BUFFER)) { + if (pretenureGroups && (promotionRate > 0.8 || reason == JS::gcreason::FULL_STORE_BUFFER)) { for (size_t i = 0; i < ArrayLength(tenureCounts.entries); i++) { const TenureCount &entry = tenureCounts.entries[i]; if (entry.count >= 3000) - pretenureTypes->append(entry.type); // ignore alloc failure + pretenureGroups->append(entry.group); // ignore alloc failure } } TIME_END(pretenure); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Nursery.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Nursery.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Nursery.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Nursery.h 2015-02-03 14:33:26.000000000 +0000 @@ -40,7 +40,7 @@ } /* namespace gc */ namespace types { -struct TypeObject; +struct ObjectGroup; } namespace jit { @@ -117,13 +117,13 @@ /* Free a slots array. */ void freeSlots(HeapSlot *slots); - typedef Vector TypeObjectList; + typedef Vector ObjectGroupList; /* - * Do a minor collection, optionally specifying a list to store types which + * Do a minor collection, optionally specifying a list to store groups which * should be pretenured afterwards. */ - void collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList *pretenureTypes); + void collect(JSRuntime *rt, JS::gcreason::Reason reason, ObjectGroupList *pretenureGroups); /* * Check if the thing at |*ref| in the Nursery has been forwarded. If so, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Rooting.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Rooting.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Rooting.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Rooting.h 2015-02-03 14:33:26.000000000 +0000 @@ -21,13 +21,13 @@ class ScriptSourceObject; class Shape; -namespace types { struct TypeObject; } +namespace types { struct ObjectGroup; } // These are internal counterparts to the public types such as HandleObject. typedef JS::Handle HandleNativeObject; typedef JS::Handle HandleShape; -typedef JS::Handle HandleTypeObject; +typedef JS::Handle HandleObjectGroup; typedef JS::Handle HandleAtom; typedef JS::Handle HandleLinearString; typedef JS::Handle HandlePropertyName; @@ -41,7 +41,7 @@ typedef JS::Rooted RootedNativeObject; typedef JS::Rooted RootedShape; -typedef JS::Rooted RootedTypeObject; +typedef JS::Rooted RootedObjectGroup; typedef JS::Rooted RootedAtom; typedef JS::Rooted RootedLinearString; typedef JS::Rooted RootedPropertyName; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/RootMarking.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/RootMarking.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/RootMarking.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/RootMarking.cpp 2015-02-03 14:33:26.000000000 +0000 @@ -103,8 +103,8 @@ MarkExactStackRootList(trc, context, "exact-object"); MarkExactStackRootList(trc, context, "exact-shape"); MarkExactStackRootList(trc, context, "exact-baseshape"); - MarkExactStackRootList( - trc, context, "exact-typeobject"); + MarkExactStackRootList( + trc, context, "exact-objectgroup"); MarkExactStackRootList(trc, context, "exact-string"); MarkExactStackRootList(trc, context, "exact-symbol"); MarkExactStackRootList(trc, context, "exact-jitcode"); @@ -509,9 +509,9 @@ c->lazyArrayBuffers->trace(trc); } - MarkInterpreterActivations(&rt->mainThread, trc); + MarkInterpreterActivations(rt, trc); - jit::MarkJitActivations(&rt->mainThread, trc); + jit::MarkJitActivations(rt, trc); if (!isHeapMinorCollecting()) { gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_EMBEDDING); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Statistics.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Statistics.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Statistics.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Statistics.cpp 2015-02-03 14:33:26.000000000 +0000 @@ -14,7 +14,6 @@ #include #include -#include "jscrashreport.h" #include "jsprf.h" #include "jsutil.h" #include "prmjtime.h" @@ -580,6 +579,8 @@ UniqueChars Statistics::formatDescription() { + const double bytesPerMiB = 1024 * 1024; + int64_t sccTotal, sccLongest; sccDurations(&sccTotal, &sccLongest); @@ -599,6 +600,7 @@ SCC Sweep Total (MaxPause): %.3fms (%.3fms)\n\ HeapSize: %.3f MiB\n\ Chunk Delta (magnitude): %+d (%d)\n\ + Arenas Relocated: %.3f MiB\n\ "; char buffer[1024]; memset(buffer, 0, sizeof(buffer)); @@ -613,9 +615,10 @@ counts[STAT_STOREBUFFER_OVERFLOW], mmu20 * 100., mmu50 * 100., t(sccTotal), t(sccLongest), - double(preBytes) / 1024. / 1024., + double(preBytes) / bytesPerMiB, counts[STAT_NEW_CHUNK] - counts[STAT_DESTROY_CHUNK], counts[STAT_NEW_CHUNK] + - counts[STAT_DESTROY_CHUNK]); + counts[STAT_DESTROY_CHUNK], + double(ArenaSize * counts[STAT_ARENA_RELOCATED]) / bytesPerMiB); return make_string_copy(buffer); } @@ -928,8 +931,6 @@ void Statistics::endGC() { - crash::SnapshotGCStack(); - for (size_t j = 0; j < MAX_MULTIPARENT_PHASES + 1; j++) for (int i = 0; i < PHASE_LIMIT; i++) phaseTotals[j][i] += phaseTimes[j][i]; @@ -983,7 +984,6 @@ if (!slices.append(data)) { // OOM testing fails if we CrashAtUnhandlableOOM here. abortSlices = true; - slices.clear(); return; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Statistics.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Statistics.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Statistics.h 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Statistics.h 2015-02-03 14:33:26.000000000 +0000 @@ -95,6 +95,9 @@ // compaction STAT_STOREBUFFER_OVERFLOW, + // Number of arenas relocated by compacting GC. + STAT_ARENA_RELOCATED, + STAT_LIMIT }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/StoreBuffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/StoreBuffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/StoreBuffer.h 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/StoreBuffer.h 2015-02-03 14:33:26.000000000 +0000 @@ -19,10 +19,6 @@ #include "js/MemoryMetrics.h" namespace js { - -MOZ_NORETURN void -CrashAtUnhandlableOOM(const char *reason); - namespace gc { /* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Tracer.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Tracer.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Tracer.cpp 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Tracer.cpp 2015-02-03 14:33:26.000000000 +0000 @@ -233,8 +233,8 @@ name = "shape"; break; - case JSTRACE_TYPE_OBJECT: - name = "type_object"; + case JSTRACE_OBJECT_GROUP: + name = "object_group"; break; default: diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Tracer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Tracer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Tracer.h 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Tracer.h 2015-02-03 14:33:26.000000000 +0000 @@ -23,7 +23,7 @@ class JitCode; } namespace types { -struct TypeObject; +struct ObjectGroup; } static const size_t NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY = 4096; @@ -143,8 +143,8 @@ pushTaggedPtr(ObjectTag, obj); } - void pushType(types::TypeObject *type) { - pushTaggedPtr(TypeTag, type); + void pushType(types::ObjectGroup *group) { + pushTaggedPtr(GroupTag, group); } void pushJitCode(jit::JitCode *code) { @@ -231,7 +231,7 @@ enum StackTag { ValueArrayTag, ObjectTag, - TypeTag, + GroupTag, XmlTag, SavedValueArrayTag, JitCodeTag, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Verifier.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Verifier.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Verifier.cpp 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Verifier.cpp 2015-02-03 14:33:26.000000000 +0000 @@ -219,7 +219,6 @@ /* Create the root node. */ trc->curnode = MakeNode(trc, nullptr, JSGCTraceKind(0)); - /* We want MarkRuntime to save the roots to gcSavedRoots. */ incrementalState = MARK_ROOTS; /* Make all the roots be edges emanating from the root node. */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Zone.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Zone.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/gc/Zone.h 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/gc/Zone.h 2015-02-03 14:33:26.000000000 +0000 @@ -103,9 +103,7 @@ // shapes within it are alive. // // We always guarantee that a zone has at least one live compartment by refusing -// to delete the last compartment in a live zone. (This could happen, for -// example, if the conservative scanner marks a string in an otherwise dead -// zone.) +// to delete the last compartment in a live zone. struct Zone : public JS::shadow::Zone, public js::gc::GraphNodeBase, public js::MallocProvider diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/irregexp/NativeRegExpMacroAssembler.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/irregexp/NativeRegExpMacroAssembler.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/irregexp/NativeRegExpMacroAssembler.cpp 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/irregexp/NativeRegExpMacroAssembler.cpp 2015-02-03 14:33:26.000000000 +0000 @@ -152,7 +152,7 @@ // Check if we have space on the stack. Label stack_ok; - void *stack_limit = runtime->mainThread.addressOfJitStackLimit(); + void *stack_limit = runtime->addressOfJitStackLimit(); masm.branchPtr(Assembler::Below, AbsoluteAddress(stack_limit), StackPointer, &stack_ok); // Exit with an exception. There is not enough space on the stack @@ -262,7 +262,7 @@ } // Initialize backtrack stack pointer. - masm.loadPtr(AbsoluteAddress(runtime->mainThread.regexpStack.addressOfBase()), backtrack_stack_pointer); + masm.loadPtr(AbsoluteAddress(runtime->regexpStack.addressOfBase()), backtrack_stack_pointer); masm.storePtr(backtrack_stack_pointer, Address(StackPointer, offsetof(FrameData, backtrackStackBase))); masm.jump(&start_label_); @@ -422,7 +422,7 @@ Address backtrackStackBaseAddress(temp2, offsetof(FrameData, backtrackStackBase)); masm.subPtr(backtrackStackBaseAddress, backtrack_stack_pointer); - masm.loadPtr(AbsoluteAddress(runtime->mainThread.regexpStack.addressOfBase()), temp1); + masm.loadPtr(AbsoluteAddress(runtime->regexpStack.addressOfBase()), temp1); masm.storePtr(temp1, backtrackStackBaseAddress); masm.addPtr(temp1, backtrack_stack_pointer); @@ -1050,7 +1050,7 @@ { JitSpew(SPEW_PREFIX "CheckBacktrackStackLimit"); - const void *limitAddr = runtime->mainThread.regexpStack.addressOfLimit(); + const void *limitAddr = runtime->regexpStack.addressOfLimit(); Label no_stack_overflow; masm.branchPtr(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr), diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/irregexp/RegExpStack.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/irregexp/RegExpStack.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/irregexp/RegExpStack.cpp 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/irregexp/RegExpStack.cpp 2015-02-03 14:33:26.000000000 +0000 @@ -36,7 +36,7 @@ using namespace js::irregexp; RegExpStackScope::RegExpStackScope(JSRuntime *rt) - : regexp_stack(&rt->mainThread.regexpStack) + : regexp_stack(&rt->regexpStack) {} RegExpStackScope::~RegExpStackScope() @@ -47,7 +47,7 @@ int irregexp::GrowBacktrackStack(JSRuntime *rt) { - return rt->mainThread.regexpStack.grow(); + return rt->regexpStack.grow(); } RegExpStack::RegExpStack() diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/Architecture-arm.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/Architecture-arm.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/Architecture-arm.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/Architecture-arm.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -16,7 +16,7 @@ #include "jit/arm/Assembler-arm.h" #include "jit/RegisterSets.h" -#if defined(ANDROID) || defined(JS_ARM_SIMULATOR) +#if !defined(__linux__) || defined(ANDROID) || defined(JS_ARM_SIMULATOR) // The Android NDK and B2G do not include the hwcap.h kernel header, and it is not // defined when building the simulator, so inline the header defines we need. # define HWCAP_VFP (1 << 6) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/Assembler-arm.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/Assembler-arm.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/Assembler-arm.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/Assembler-arm.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -805,6 +805,12 @@ const void *prior = Assembler::GetPtr32Target(iter, &dest, &rs); void *ptr = const_cast(prior); + // The low bit shouldn't be set. If it is, we probably got a dummy + // pointer inserted by CodeGenerator::visitNurseryObject, but we + // shouldn't be able to trigger GC before those are patched to their + // real values. + MOZ_ASSERT(!(uintptr_t(ptr) & 0x1)); + // No barrier needed since these are constants. gc::MarkGCThingUnbarriered(trc, &ptr, "ion-masm-ptr"); @@ -847,6 +853,51 @@ } void +Assembler::FixupNurseryObjects(JSContext *cx, JitCode *code, CompactBufferReader &reader, + const ObjectVector &nurseryObjects) +{ + MOZ_ASSERT(!nurseryObjects.empty()); + + uint8_t *buffer = code->raw(); + bool hasNurseryPointers = false; + MacroAssemblerARM *masm = static_cast(Dummy); + + while (reader.more()) { + size_t offset = reader.readUnsigned(); + InstructionIterator iter((Instruction*)(buffer + offset)); + Instruction *ins = iter.cur(); + Register dest; + Assembler::RelocStyle rs; + const void *prior = Assembler::GetPtr32Target(&iter, &dest, &rs); + void *ptr = const_cast(prior); + uintptr_t word = reinterpret_cast(ptr); + + if (!(word & 0x1)) + continue; + + uint32_t index = word >> 1; + JSObject *obj = nurseryObjects[index]; + masm->ma_movPatchable(Imm32(int32_t(obj)), dest, Assembler::Always, rs, ins); + + if (rs != Assembler::L_LDR) { + // L_LDR won't cause any instructions to be updated. + AutoFlushICache::flush(uintptr_t(ins), 4); + AutoFlushICache::flush(uintptr_t(ins->next()), 4); + } + + // Either all objects are still in the nursery, or all objects are + // tenured. + MOZ_ASSERT_IF(hasNurseryPointers, IsInsideNursery(obj)); + + if (!hasNurseryPointers && IsInsideNursery(obj)) + hasNurseryPointers = true; + } + + if (hasNurseryPointers) + cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(code); +} + +void Assembler::copyJumpRelocationTable(uint8_t *dest) { if (jumpRelocations_.length()) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/Assembler-arm.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/Assembler-arm.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/Assembler-arm.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/Assembler-arm.h 2015-02-03 14:33:32.000000000 +0000 @@ -1197,7 +1197,6 @@ CompactBufferWriter jumpRelocations_; CompactBufferWriter dataRelocations_; - CompactBufferWriter relocations_; CompactBufferWriter preBarriers_; ARMBuffer m_buffer; @@ -1597,6 +1596,9 @@ static void TraceJumpRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader); static void TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader); + static void FixupNurseryObjects(JSContext *cx, JitCode *code, CompactBufferReader &reader, + const ObjectVector &nurseryObjects); + static bool SupportsFloatingPoint() { return HasVFP(); } @@ -1799,6 +1801,12 @@ bool bailed() { return m_buffer.bail(); } + + void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, + const Disassembler::HeapAccess &heapAccess) + { + // Implement this if we implement a disassembler. + } }; // Assembler // An Instruction is a structure for both encoding and decoding any and all ARM diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/CodeGenerator-arm.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/CodeGenerator-arm.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/CodeGenerator-arm.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/CodeGenerator-arm.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -1654,13 +1654,13 @@ } void -CodeGeneratorARM::visitGuardObjectType(LGuardObjectType *guard) +CodeGeneratorARM::visitGuardObjectGroup(LGuardObjectGroup *guard) { Register obj = ToRegister(guard->input()); Register tmp = ToRegister(guard->tempInt()); - masm.ma_ldr(DTRAddr(obj, DtrOffImm(JSObject::offsetOfType())), tmp); - masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->typeObject())); + masm.ma_ldr(DTRAddr(obj, DtrOffImm(JSObject::offsetOfGroup())), tmp); + masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->group())); Assembler::Condition cond = guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/CodeGenerator-arm.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/CodeGenerator-arm.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/CodeGenerator-arm.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/CodeGenerator-arm.h 2015-02-03 14:33:32.000000000 +0000 @@ -195,7 +195,7 @@ void visitFloat32(LFloat32 *ins); void visitGuardShape(LGuardShape *guard); - void visitGuardObjectType(LGuardObjectType *guard); + void visitGuardObjectGroup(LGuardObjectGroup *guard); void visitGuardClass(LGuardClass *guard); void visitNegI(LNegI *lir); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/LIR-arm.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/LIR-arm.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/LIR-arm.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/LIR-arm.h 2015-02-03 14:33:32.000000000 +0000 @@ -380,17 +380,17 @@ } }; -class LGuardObjectType : public LInstructionHelper<0, 1, 1> +class LGuardObjectGroup : public LInstructionHelper<0, 1, 1> { public: - LIR_HEADER(GuardObjectType); + LIR_HEADER(GuardObjectGroup); - LGuardObjectType(const LAllocation &in, const LDefinition &temp) { + LGuardObjectGroup(const LAllocation &in, const LDefinition &temp) { setOperand(0, in); setTemp(0, temp); } - const MGuardObjectType *mir() const { - return mir_->toGuardObjectType(); + const MGuardObjectGroup *mir() const { + return mir_->toGuardObjectGroup(); } const LDefinition *tempInt() { return getTemp(0); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/Lowering-arm.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/Lowering-arm.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/Lowering-arm.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/Lowering-arm.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -387,13 +387,13 @@ } void -LIRGeneratorARM::visitGuardObjectType(MGuardObjectType *ins) +LIRGeneratorARM::visitGuardObjectGroup(MGuardObjectGroup *ins) { MOZ_ASSERT(ins->obj()->type() == MIRType_Object); LDefinition tempObj = temp(LDefinition::OBJECT); - LGuardObjectType *guard = new(alloc()) LGuardObjectType(useRegister(ins->obj()), tempObj); - assignSnapshot(guard, Bailout_ObjectIdentityOrTypeGuard); + LGuardObjectGroup *guard = new(alloc()) LGuardObjectGroup(useRegister(ins->obj()), tempObj); + assignSnapshot(guard, ins->bailoutKind()); add(guard, ins); redefine(ins, ins->obj()); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/Lowering-arm.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/Lowering-arm.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/Lowering-arm.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/Lowering-arm.h 2015-02-03 14:33:32.000000000 +0000 @@ -91,7 +91,7 @@ void visitReturn(MReturn *ret); void lowerPhi(MPhi *phi); void visitGuardShape(MGuardShape *ins); - void visitGuardObjectType(MGuardObjectType *ins); + void visitGuardObjectGroup(MGuardObjectGroup *ins); void visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins); void visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32 *ins); void visitAsmJSLoadHeap(MAsmJSLoadHeap *ins); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/MacroAssembler-arm.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/MacroAssembler-arm.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/MacroAssembler-arm.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/MacroAssembler-arm.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -2686,6 +2686,20 @@ } void +MacroAssemblerARMCompat::cmpPtr(const Address &lhs, ImmGCPtr rhs) +{ + loadPtr(lhs, secondScratchReg_); + ma_cmp(secondScratchReg_, rhs); +} + +void +MacroAssemblerARMCompat::cmpPtr(const Address &lhs, Imm32 rhs) +{ + loadPtr(lhs, secondScratchReg_); + ma_cmp(secondScratchReg_, rhs); +} + +void MacroAssemblerARMCompat::setStackArg(Register reg, uint32_t arg) { ma_dataTransferN(IsStore, 32, true, sp, Imm32(arg * sizeof(intptr_t)), reg); @@ -4160,6 +4174,8 @@ case Args_Double_DoubleDouble: case Args_Double_IntDouble: case Args_Int_IntDouble: + case Args_Double_DoubleDoubleDouble: + case Args_Double_DoubleDoubleDoubleDouble: break; default: MOZ_CRASH("Unexpected type"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/MacroAssembler-arm.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/MacroAssembler-arm.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/MacroAssembler-arm.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/MacroAssembler-arm.h 2015-02-03 14:33:32.000000000 +0000 @@ -38,6 +38,7 @@ // address. Register secondScratchReg_; + public: // Higher level tag testing code. Operand ToPayload(Operand base) { return Operand(Register::FromCode(base.base()), base.disp()); @@ -45,6 +46,8 @@ Address ToPayload(Address base) { return ToPayload(Operand(base)).toAddress(); } + + protected: Operand ToType(Operand base) { return Operand(Register::FromCode(base.base()), base.disp() + sizeof(void *)); } @@ -1126,6 +1129,19 @@ void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T &dest, MIRType slotType); + template + void storeUnboxedPayload(ValueOperand value, T address, size_t nbytes) { + switch (nbytes) { + case 4: + storePtr(value.payloadReg(), address); + return; + case 1: + store8(value.payloadReg(), address); + return; + default: MOZ_CRASH("Bad payload width"); + } + } + void moveValue(const Value &val, const ValueOperand &dest); void moveValue(const ValueOperand &src, const ValueOperand &dest) { @@ -1619,14 +1635,16 @@ void cmp32(const Operand &lhs, Imm32 rhs); void cmp32(const Operand &lhs, Register rhs); + void cmpPtr(Register lhs, Register rhs); void cmpPtr(Register lhs, ImmWord rhs); void cmpPtr(Register lhs, ImmPtr rhs); - void cmpPtr(Register lhs, Register rhs); void cmpPtr(Register lhs, ImmGCPtr rhs); void cmpPtr(Register lhs, Imm32 rhs); void cmpPtr(const Address &lhs, Register rhs); void cmpPtr(const Address &lhs, ImmWord rhs); void cmpPtr(const Address &lhs, ImmPtr rhs); + void cmpPtr(const Address &lhs, ImmGCPtr rhs); + void cmpPtr(const Address &lhs, Imm32 rhs); void subPtr(Imm32 imm, const Register dest); void subPtr(const Address &addr, const Register dest); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/MoveEmitter-arm.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/MoveEmitter-arm.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/MoveEmitter-arm.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/MoveEmitter-arm.h 2015-02-03 14:33:32.000000000 +0000 @@ -56,6 +56,8 @@ ~MoveEmitterARM(); void emit(const MoveResolver &moves); void finish(); + + void setScratchRegister(Register reg) {} }; typedef MoveEmitterARM MoveEmitter; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/Simulator-arm.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/Simulator-arm.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/Simulator-arm.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/Simulator-arm.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -352,97 +352,19 @@ char validity_map_[kValidityMapSize]; // One byte per line. }; -class Redirection; - -class SimulatorRuntime -{ - friend class AutoLockSimulatorRuntime; - - Redirection *redirection_; - - // ICache checking. - struct ICacheHasher { - typedef void *Key; - typedef void *Lookup; - static HashNumber hash(const Lookup &l); - static bool match(const Key &k, const Lookup &l); - }; - - public: - typedef HashMap ICacheMap; - - protected: - ICacheMap icache_; - - // Synchronize access between main thread and compilation threads. - PRLock *lock_; - mozilla::DebugOnly lockOwner_; - - public: - SimulatorRuntime() - : redirection_(nullptr), - lock_(nullptr), - lockOwner_(nullptr) - {} - ~SimulatorRuntime(); - bool init() { - lock_ = PR_NewLock(); - if (!lock_) - return false; - if (!icache_.init()) - return false; - return true; - } - ICacheMap &icache() { - MOZ_ASSERT(lockOwner_ == PR_GetCurrentThread()); - return icache_; - } - Redirection *redirection() const { - MOZ_ASSERT(lockOwner_ == PR_GetCurrentThread()); - return redirection_; - } - void setRedirection(js::jit::Redirection *redirection) { - MOZ_ASSERT(lockOwner_ == PR_GetCurrentThread()); - redirection_ = redirection; - } -}; - -class AutoLockSimulatorRuntime -{ - protected: - SimulatorRuntime *srt_; - - public: - AutoLockSimulatorRuntime(SimulatorRuntime *srt) - : srt_(srt) - { - PR_Lock(srt_->lock_); - MOZ_ASSERT(!srt_->lockOwner_); -#ifdef DEBUG - srt_->lockOwner_ = PR_GetCurrentThread(); -#endif - } - - ~AutoLockSimulatorRuntime() { - MOZ_ASSERT(srt_->lockOwner_ == PR_GetCurrentThread()); - srt_->lockOwner_ = nullptr; - PR_Unlock(srt_->lock_); - } -}; - bool Simulator::ICacheCheckingEnabled = false; int64_t Simulator::StopSimAt = -1L; -SimulatorRuntime * -CreateSimulatorRuntime() +Simulator * +Simulator::Create() { - SimulatorRuntime *srt = js_new(); - if (!srt) + Simulator *sim = js_new(); + if (!sim) return nullptr; - if (!srt->init()) { - js_delete(srt); + if (!sim->init()) { + js_delete(sim); return nullptr; } @@ -456,13 +378,13 @@ Simulator::StopSimAt = stopAt; } - return srt; + return sim; } void -DestroySimulatorRuntime(SimulatorRuntime *srt) +Simulator::Destroy(Simulator *sim) { - js_delete(srt); + js_delete(sim); } // The ArmDebugger class is used by the simulator while debugging simulated ARM @@ -1000,11 +922,11 @@ } static CachePage * -GetCachePage(SimulatorRuntime::ICacheMap &i_cache, void *page) +GetCachePage(Simulator::ICacheMap &i_cache, void *page) { MOZ_ASSERT(Simulator::ICacheCheckingEnabled); - SimulatorRuntime::ICacheMap::AddPtr p = i_cache.lookupForAdd(page); + Simulator::ICacheMap::AddPtr p = i_cache.lookupForAdd(page); if (p) return p->value(); @@ -1016,7 +938,7 @@ // Flush from start up to and not including start + size. static void -FlushOnePage(SimulatorRuntime::ICacheMap &i_cache, intptr_t start, int size) +FlushOnePage(Simulator::ICacheMap &i_cache, intptr_t start, int size) { MOZ_ASSERT(size <= CachePage::kPageSize); MOZ_ASSERT(AllOnOnePage(start, size - 1)); @@ -1031,7 +953,7 @@ } static void -FlushICache(SimulatorRuntime::ICacheMap &i_cache, void *start_addr, size_t size) +FlushICache(Simulator::ICacheMap &i_cache, void *start_addr, size_t size) { intptr_t start = reinterpret_cast(start_addr); int intra_line = (start & CachePage::kLineMask); @@ -1052,7 +974,7 @@ } static void -CheckICache(SimulatorRuntime::ICacheMap &i_cache, SimInstruction *instr) +CheckICache(Simulator::ICacheMap &i_cache, SimInstruction *instr) { intptr_t address = reinterpret_cast(instr); void *page = reinterpret_cast(address & (~CachePage::kPageMask)); @@ -1075,13 +997,13 @@ } HashNumber -SimulatorRuntime::ICacheHasher::hash(const Lookup &l) +Simulator::ICacheHasher::hash(const Lookup &l) { return static_cast(reinterpret_cast(l)) >> 2; } bool -SimulatorRuntime::ICacheHasher::match(const Key &k, const Lookup &l) +Simulator::ICacheHasher::match(const Key &k, const Lookup &l) { MOZ_ASSERT((reinterpret_cast(k) & CachePage::kPageMask) == 0); MOZ_ASSERT((reinterpret_cast(l) & CachePage::kPageMask) == 0); @@ -1101,30 +1023,19 @@ JitSpewCont(JitSpew_CacheFlush, "[%p %zx]", start_addr, size); if (!Simulator::ICacheCheckingEnabled) return; - SimulatorRuntime *srt = TlsPerThreadData.get()->simulatorRuntime(); - AutoLockSimulatorRuntime alsr(srt); - js::jit::FlushICache(srt->icache(), start_addr, size); + js::jit::FlushICache(Simulator::Current()->icache(), start_addr, size); } -Simulator::~Simulator() -{ - js_free(stack_); -} - -Simulator::Simulator(SimulatorRuntime *srt) - : srt_(srt) +Simulator::Simulator() { // Set up simulator support first. Some of this information is needed to // setup the architecture state. - // Allocate 2MB for the stack. Note that we will only use 1MB, see also - // Simulator::stackLimit(). - static const size_t stackSize = 2 * 1024*1024; - stack_ = reinterpret_cast(js_malloc(stackSize)); - if (!stack_) { - MOZ_ReportAssertionFailure("[unhandlable oom] Simulator stack", __FILE__, __LINE__); - MOZ_CRASH(); - } + // Note, allocation and anything that depends on allocated memory is + // deferred until init(), in order to handle OOM properly. + + stack_ = nullptr; + stackLimit_ = 0; pc_modified_ = false; icount_ = 0L; resume_pc_ = 0; @@ -1161,17 +1072,38 @@ underflow_vfp_flag_ = false; inexact_vfp_flag_ = false; - // The sp is initialized to point to the bottom (high address) of the - // allocated stack area. To be safe in potential stack underflows we leave - // some buffer below. - registers_[sp] = reinterpret_cast(stack_) + stackSize - 64; - // The lr and pc are initialized to a known bad value that will cause an // access violation if the simulator ever tries to execute it. registers_[pc] = bad_lr; registers_[lr] = bad_lr; lastDebuggerInput_ = nullptr; + + redirection_ = nullptr; +} + +bool +Simulator::init() +{ + if (!icache_.init()) + return false; + + // Allocate 2MB for the stack. Note that we will only use 1MB, see below. + static const size_t stackSize = 2 * 1024*1024; + stack_ = reinterpret_cast(js_malloc(stackSize)); + if (!stack_) + return false; + + // Leave a safety margin of 1MB to prevent overrunning the stack when + // pushing values (total stack size is 2MB). + stackLimit_ = reinterpret_cast(stack_) + 1024 * 1024; + + // The sp is initialized to point to the bottom (high address) of the + // allocated stack area. To be safe in potential stack underflows we leave + // some buffer below. + registers_[sp] = reinterpret_cast(stack_) + stackSize - 64; + + return true; } // When the generated code calls a VM function (masm.callWithABI) we need to @@ -1182,18 +1114,18 @@ // offset from the svc instruction so the simulator knows what to call. class Redirection { - friend class SimulatorRuntime; + friend class Simulator; - Redirection(void *nativeFunction, ABIFunctionType type, SimulatorRuntime *srt) + Redirection(void *nativeFunction, ABIFunctionType type, Simulator *sim) : nativeFunction_(nativeFunction), swiInstruction_(Assembler::AL | (0xf * (1 << 24)) | kCallRtRedirected), type_(type), next_(nullptr) { - next_ = srt->redirection(); + next_ = sim->redirection(); if (Simulator::ICacheCheckingEnabled) - FlushICache(srt->icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize); - srt->setRedirection(this); + FlushICache(sim->icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize); + sim->setRedirection(this); } public: @@ -1202,13 +1134,8 @@ ABIFunctionType type() const { return type_; } static Redirection *Get(void *nativeFunction, ABIFunctionType type) { - PerThreadData *pt = TlsPerThreadData.get(); - SimulatorRuntime *srt = pt->simulatorRuntime(); - AutoLockSimulatorRuntime alsr(srt); - - MOZ_ASSERT_IF(pt->simulator(), pt->simulator()->srt_ == srt); - - Redirection *current = srt->redirection(); + Simulator *sim = Simulator::Current(); + Redirection *current = sim->redirection(); for (; current != nullptr; current = current->next_) { if (current->nativeFunction_ == nativeFunction) { MOZ_ASSERT(current->type() == type); @@ -1222,7 +1149,7 @@ __FILE__, __LINE__); MOZ_CRASH(); } - new(redir) Redirection(nativeFunction, type, srt); + new(redir) Redirection(nativeFunction, type, sim); return redir; } @@ -1239,23 +1166,22 @@ Redirection *next_; }; -/* static */ void * -Simulator::RedirectNativeFunction(void *nativeFunction, ABIFunctionType type) -{ - Redirection *redirection = Redirection::Get(nativeFunction, type); - return redirection->addressOfSwiInstruction(); -} - -SimulatorRuntime::~SimulatorRuntime() +Simulator::~Simulator() { + js_free(stack_); Redirection *r = redirection_; while (r) { Redirection *next = r->next_; js_delete(r); r = next; } - if (lock_) - PR_DestroyLock(lock_); +} + +/* static */ void * +Simulator::RedirectNativeFunction(void *nativeFunction, ABIFunctionType type) +{ + Redirection *redirection = Redirection::Get(nativeFunction, type); + return redirection->addressOfSwiInstruction(); } // Sets the register in the architecture state. It will also deal with updating @@ -1450,6 +1376,15 @@ } void +Simulator::getFpFromStack(int32_t *stack, double *x) +{ + MOZ_ASSERT(stack && x); + char buffer[2 * sizeof(stack[0])]; + memcpy(buffer, stack, 2 * sizeof(stack[0])); + memcpy(x, buffer, 2 * sizeof(stack[0])); +} + +void Simulator::setCallResultDouble(double result) { // The return value is either in r0/r1 or d0. @@ -1621,9 +1556,13 @@ uintptr_t Simulator::stackLimit() const { - // Leave a safety margin of 1MB to prevent overrunning the stack when - // pushing values (total stack size is 2MB). - return reinterpret_cast(stack_) + 1024 * 1024; + return stackLimit_; +} + +uintptr_t * +Simulator::addressOfStackLimit() +{ + return &stackLimit_; } bool @@ -2078,6 +2017,9 @@ typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1); typedef int32_t (*Prototype_Int_IntDouble)(int32_t arg0, double arg1); +typedef double (*Prototype_Double_DoubleDoubleDouble)(double arg0, double arg1, double arg2); +typedef double (*Prototype_Double_DoubleDoubleDoubleDouble)(double arg0, double arg1, + double arg2, double arg3); // Fill the volatile registers with scratch values. // @@ -2299,6 +2241,31 @@ set_register(r0, result); break; } + case Args_Double_DoubleDoubleDouble: { + double dval0, dval1, dval2; + int32_t ival; + getFpArgs(&dval0, &dval1, &ival); + // the last argument is on stack + getFpFromStack(stack_pointer, &dval2); + Prototype_Double_DoubleDoubleDouble target = reinterpret_cast(external); + double dresult = target(dval0, dval1, dval2); + scratchVolatileRegisters(/* scratchFloat = true */); + setCallResultDouble(dresult); + break; + } + case Args_Double_DoubleDoubleDoubleDouble: { + double dval0, dval1, dval2, dval3; + int32_t ival; + getFpArgs(&dval0, &dval1, &ival); + // the two last arguments are on stack + getFpFromStack(stack_pointer, &dval2); + getFpFromStack(stack_pointer + 2, &dval3); + Prototype_Double_DoubleDoubleDoubleDouble target = reinterpret_cast(external); + double dresult = target(dval0, dval1, dval2, dval3); + scratchVolatileRegisters(/* scratchFloat = true */); + setCallResultDouble(dresult); + break; + } default: MOZ_CRASH("call"); } @@ -4133,10 +4100,8 @@ void Simulator::instructionDecode(SimInstruction *instr) { - if (Simulator::ICacheCheckingEnabled) { - AutoLockSimulatorRuntime alsr(srt_); - CheckICache(srt_->icache(), instr); - } + if (Simulator::ICacheCheckingEnabled) + CheckICache(icache(), instr); pc_modified_ = false; @@ -4226,7 +4191,7 @@ int32_t rpc = resume_pc_; if (MOZ_UNLIKELY(rpc != 0)) { // AsmJS signal handler ran and we have to adjust the pc. - PerThreadData::innermostAsmJSActivation()->setResumePC((void *)get_pc()); + JSRuntime::innermostAsmJSActivation()->setResumePC((void *)get_pc()); set_pc(rpc); resume_pc_ = 0; } @@ -4404,53 +4369,26 @@ Simulator * Simulator::Current() { - PerThreadData *pt = TlsPerThreadData.get(); - Simulator *sim = pt->simulator(); - if (!sim) { - sim = js_new(pt->simulatorRuntime()); - pt->setSimulator(sim); - } - - return sim; + return TlsPerThreadData.get()->simulator(); } } // namespace jit } // namespace js js::jit::Simulator * -js::PerThreadData::simulator() const +JSRuntime::simulator() const { return simulator_; } -void -js::PerThreadData::setSimulator(js::jit::Simulator *sim) -{ - simulator_ = sim; - simulatorStackLimit_ = sim->stackLimit(); -} - -js::jit::SimulatorRuntime * -js::PerThreadData::simulatorRuntime() const -{ - return runtime_->simulatorRuntime(); -} - uintptr_t * -js::PerThreadData::addressOfSimulatorStackLimit() -{ - return &simulatorStackLimit_; -} - -js::jit::SimulatorRuntime * -JSRuntime::simulatorRuntime() const +JSRuntime::addressOfSimulatorStackLimit() { - return simulatorRuntime_; + return simulator_->addressOfStackLimit(); } -void -JSRuntime::setSimulatorRuntime(js::jit::SimulatorRuntime *srt) +js::jit::Simulator * +js::PerThreadData::simulator() const { - MOZ_ASSERT(!simulatorRuntime_); - simulatorRuntime_ = srt; + return runtime_->simulator(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/Simulator-arm.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/Simulator-arm.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/arm/Simulator-arm.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/arm/Simulator-arm.h 2015-02-03 14:33:32.000000000 +0000 @@ -38,9 +38,8 @@ namespace jit { class Simulator; -class SimulatorRuntime; -SimulatorRuntime *CreateSimulatorRuntime(); -void DestroySimulatorRuntime(SimulatorRuntime *srt); +class Redirection; +class CachePage; // When the SingleStepCallback is called, the simulator is about to execute // sim->get_pc() and the current machine state represents the completed @@ -95,7 +94,13 @@ num_q_registers = 16 }; - explicit Simulator(SimulatorRuntime *srt); + // Returns nullptr on OOM. + static Simulator *Create(); + + static void Destroy(Simulator *simulator); + + // Constructor/destructor are for internal use only; use the static methods above. + Simulator(); ~Simulator(); // The currently executing Simulator instance. Potentially there can be one @@ -106,6 +111,8 @@ return Simulator::Current()->stackLimit(); } + uintptr_t *addressOfStackLimit(); + // Accessors for register state. Reading the pc value adheres to the ARM // architecture specification and is off by a 8 from the currently executing // instruction. @@ -188,6 +195,8 @@ end_sim_pc = -2 }; + bool init(); + // Checks if the current instruction should be executed based on its // condition bits. inline bool conditionallyExecute(SimInstruction* instr); @@ -289,6 +298,7 @@ private: // Handle arguments and return value for runtime FP functions. void getFpArgs(double *x, double *y, int32_t *z); + void getFpFromStack(int32_t *stack, double *x1); void setCallResultDouble(double result); void setCallResultFloat(float result); void setCallResult(int64_t res); @@ -332,6 +342,7 @@ // Simulator support. char *stack_; + uintptr_t stackLimit_; bool pc_modified_; int64_t icount_; @@ -349,8 +360,6 @@ SingleStepCallback single_step_callback_; void *single_step_callback_arg_; - SimulatorRuntime *srt_; - // A stop is watched if its code is less than kNumOfWatchedStops. // Only watched stops support enabling/disabling and the counter feature. static const uint32_t kNumOfWatchedStops = 256; @@ -373,11 +382,40 @@ return icount_; } + private: + Redirection *redirection_; + + // ICache checking. + struct ICacheHasher { + typedef void *Key; + typedef void *Lookup; + static HashNumber hash(const Lookup &l); + static bool match(const Key &k, const Lookup &l); + }; + + public: + typedef HashMap ICacheMap; + + protected: + ICacheMap icache_; + + public: + ICacheMap &icache() { + return icache_; + } + + Redirection *redirection() const { + return redirection_; + } + + void setRedirection(js::jit::Redirection *redirection) { + redirection_ = redirection; + } }; #define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror) \ JS_BEGIN_MACRO \ - if (cx->mainThread().simulator()->overRecursedWithExtra(extra)) { \ + if (cx->runtime()->simulator()->overRecursedWithExtra(extra)) { \ js_ReportOverRecursed(cx); \ onerror; \ } \ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/BacktrackingAllocator.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/BacktrackingAllocator.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/BacktrackingAllocator.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/BacktrackingAllocator.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -101,6 +101,7 @@ dumpRegisterGroups(); JitSpew(JitSpew_RegAlloc, "Beginning main allocation loop"); + // Allocate, spill and split register intervals until finished. while (!allocationQueue.empty()) { if (mir->shouldCancel("Backtracking Allocation")) @@ -112,10 +113,25 @@ } JitSpew(JitSpew_RegAlloc, "Main allocation loop complete"); + if (!pickStackSlots()) + return false; + if (JitSpewEnabled(JitSpew_RegAlloc)) dumpAllocations(); - return resolveControlFlow() && reifyAllocations() && populateSafepoints(); + if (!resolveControlFlow()) + return false; + + if (!reifyAllocations()) + return false; + + if (!populateSafepoints()) + return false; + + if (!annotateMoveGroups()) + return false; + + return true; } static bool @@ -155,6 +171,14 @@ return true; } +static bool +IsThisSlotDefinition(LDefinition *def) +{ + return def->policy() == LDefinition::FIXED && + def->output()->isArgument() && + def->output()->toArgument()->index() < THIS_FRAME_ARGSLOT + sizeof(Value); +} + bool BacktrackingAllocator::tryGroupRegisters(uint32_t vreg0, uint32_t vreg1) { @@ -166,6 +190,15 @@ if (!reg0->isCompatibleVReg(*reg1)) return true; + // Registers which might spill to the frame's |this| slot can only be + // grouped with other such registers. The frame's |this| slot must always + // hold the |this| value, as required by JitFrame tracing and by the Ion + // constructor calling convention. + if (IsThisSlotDefinition(reg0->def()) || IsThisSlotDefinition(reg1->def())) { + if (*reg0->def()->output() != *reg1->def()->output()) + return true; + } + VirtualRegisterGroup *group0 = reg0->group(), *group1 = reg1->group(); if (!group0 && group1) @@ -312,6 +345,31 @@ bool BacktrackingAllocator::groupAndQueueRegisters() { + // If there is an OSR block, group parameters in that block with the + // corresponding parameters in the initial block. + if (MBasicBlock *osr = graph.mir().osrBlock()) { + size_t originalVreg = 1; + for (LInstructionIterator iter = osr->lir()->begin(); iter != osr->lir()->end(); iter++) { + if (iter->isParameter()) { + for (size_t i = 0; i < iter->numDefs(); i++) { + DebugOnly found = false; + uint32_t paramVreg = iter->getDef(i)->virtualRegister(); + for (; originalVreg < paramVreg; originalVreg++) { + if (*vregs[originalVreg].def()->output() == *iter->getDef(i)->output()) { + MOZ_ASSERT(vregs[originalVreg].ins()->isParameter()); + if (!tryGroupRegisters(originalVreg, paramVreg)) + return false; + MOZ_ASSERT(vregs[originalVreg].group() == vregs[paramVreg].group()); + found = true; + break; + } + } + MOZ_ASSERT(found); + } + } + } + } + // Try to group registers with their reused inputs. // Virtual register number 0 is unused. MOZ_ASSERT(vregs[0u].numIntervals() == 0); @@ -354,17 +412,15 @@ if (!reg.numIntervals()) continue; - // Disable this for now; see bugs 906858, 931487, and 932465. -#if 0 // Eagerly set the canonical spill slot for registers which are fixed // for that slot, and reuse it for other registers in the group. LDefinition *def = reg.def(); if (def->policy() == LDefinition::FIXED && !def->output()->isRegister()) { + MOZ_ASSERT(!def->output()->isStackSlot()); reg.setCanonicalSpill(*def->output()); if (reg.group() && reg.group()->spill.isUse()) reg.group()->spill = *def->output(); } -#endif // Place all intervals for this register on the allocation queue. // During initial queueing use single queue items for groups of @@ -908,10 +964,11 @@ } } - uint32_t stackSlot = stackSlotAllocator.allocateSlot(reg->type()); - MOZ_ASSERT(stackSlot <= stackSlotAllocator.stackHeight()); + uint32_t virtualSlot = numVirtualStackSlots++; - LStackSlot alloc(stackSlot); + // Count virtual stack slots down from the maximum representable value, so + // that virtual slots are more obviously distinguished from real slots. + LStackSlot alloc(LAllocation::DATA_MASK - virtualSlot); interval->setAllocation(alloc); JitSpew(JitSpew_RegAlloc, " Allocating spill location %s", alloc.toString()); @@ -923,6 +980,179 @@ } } +bool +BacktrackingAllocator::pickStackSlots() +{ + for (size_t i = 1; i < graph.numVirtualRegisters(); i++) { + BacktrackingVirtualRegister *reg = &vregs[i]; + + if (mir->shouldCancel("Backtracking Pick Stack Slots")) + return false; + + for (size_t j = 0; j < reg->numIntervals(); j++) { + LiveInterval *interval = reg->getInterval(j); + if (!pickStackSlot(interval)) + return false; + } + } + + return true; +} + +bool +BacktrackingAllocator::pickStackSlot(LiveInterval *interval) +{ + LAllocation alloc = *interval->getAllocation(); + MOZ_ASSERT(!alloc.isUse()); + + if (!isVirtualStackSlot(alloc)) + return true; + + BacktrackingVirtualRegister ® = vregs[interval->vreg()]; + + // Get a list of all the intervals which will share this stack slot. + LiveIntervalVector commonIntervals; + + if (!commonIntervals.append(interval)) + return false; + + if (reg.canonicalSpill() && alloc == *reg.canonicalSpill()) { + // Look for other intervals in the vreg using this spill slot. + for (size_t i = 0; i < reg.numIntervals(); i++) { + LiveInterval *ninterval = reg.getInterval(i); + if (ninterval != interval && *ninterval->getAllocation() == alloc) { + if (!commonIntervals.append(ninterval)) + return false; + } + } + + // Look for intervals in other registers with the same group using this + // spill slot. + if (reg.group() && alloc == reg.group()->spill) { + for (size_t i = 0; i < reg.group()->registers.length(); i++) { + uint32_t nvreg = reg.group()->registers[i]; + if (nvreg == interval->vreg()) + continue; + BacktrackingVirtualRegister &nreg = vregs[nvreg]; + for (size_t j = 0; j < nreg.numIntervals(); j++) { + LiveInterval *ninterval = nreg.getInterval(j); + if (*ninterval->getAllocation() == alloc) { + if (!commonIntervals.append(ninterval)) + return false; + } + } + } + } + } else { + MOZ_ASSERT_IF(reg.group(), alloc != reg.group()->spill); + } + + if (!reuseOrAllocateStackSlot(commonIntervals, reg.type(), &alloc)) + return false; + + MOZ_ASSERT(!isVirtualStackSlot(alloc)); + + // Set the physical stack slot for each of the intervals found earlier. + for (size_t i = 0; i < commonIntervals.length(); i++) + commonIntervals[i]->setAllocation(alloc); + + return true; +} + +bool +BacktrackingAllocator::reuseOrAllocateStackSlot(const LiveIntervalVector &intervals, LDefinition::Type type, + LAllocation *palloc) +{ + SpillSlotList *slotList; + switch (StackSlotAllocator::width(type)) { + case 4: slotList = &normalSlots; break; + case 8: slotList = &doubleSlots; break; + case 16: slotList = &quadSlots; break; + default: + MOZ_CRASH("Bad width"); + } + + // Maximum number of existing spill slots we will look at before giving up + // and allocating a new slot. + static const size_t MAX_SEARCH_COUNT = 10; + + if (!slotList->empty()) { + size_t searches = 0; + SpillSlot *stop = nullptr; + while (true) { + SpillSlot *spill = *slotList->begin(); + if (!stop) { + stop = spill; + } else if (stop == spill) { + // We looked through every slot in the list. + break; + } + + bool success = true; + for (size_t i = 0; i < intervals.length() && success; i++) { + LiveInterval *interval = intervals[i]; + for (size_t j = 0; j < interval->numRanges(); j++) { + AllocatedRange range(interval, interval->getRange(j)), existing; + if (spill->allocated.contains(range, &existing)) { + success = false; + break; + } + } + } + if (success) { + // We can reuse this physical stack slot for the new intervals. + // Update the allocated ranges for the slot. + if (!insertAllRanges(spill->allocated, intervals)) + return false; + *palloc = spill->alloc; + return true; + } + + // On a miss, move the spill to the end of the list. This will cause us + // to make fewer attempts to allocate from slots with a large and + // highly contended range. + slotList->popFront(); + slotList->pushBack(spill); + + if (++searches == MAX_SEARCH_COUNT) + break; + } + } + + // We need a new physical stack slot. + uint32_t stackSlot = stackSlotAllocator.allocateSlot(type); + + // Make sure the virtual and physical stack slots don't start overlapping. + if (isVirtualStackSlot(LStackSlot(stackSlot))) + return false; + + SpillSlot *spill = new(alloc()) SpillSlot(stackSlot, alloc().lifoAlloc()); + if (!spill) + return false; + + if (!insertAllRanges(spill->allocated, intervals)) + return false; + + *palloc = spill->alloc; + + slotList->pushFront(spill); + return true; +} + +bool +BacktrackingAllocator::insertAllRanges(AllocatedRangeSet &set, const LiveIntervalVector &intervals) +{ + for (size_t i = 0; i < intervals.length(); i++) { + LiveInterval *interval = intervals[i]; + for (size_t j = 0; j < interval->numRanges(); j++) { + AllocatedRange range(interval, interval->getRange(j)); + if (!set.insert(range)) + return false; + } + } + return true; +} + // Add moves to resolve conflicting assignments between a block and its // predecessors. XXX try to common this with LinearScanAllocator. bool @@ -957,7 +1187,7 @@ CodePosition start = interval->start(); LNode *ins = insData[start]; - if (interval->start() > entryOf(ins->block())) { + if (start > entryOf(ins->block())) { MOZ_ASSERT(start == inputOf(ins) || start == outputOf(ins)); LiveInterval *prevInterval = reg->intervalFor(start.previous()); @@ -1226,6 +1456,63 @@ return true; } + +bool +BacktrackingAllocator::annotateMoveGroups() +{ + // Annotate move groups in the LIR graph with any register that is not + // allocated at that point and can be used as a scratch register. This is + // only required for x86, as other platforms always have scratch registers + // available for use. +#ifdef JS_CODEGEN_X86 + for (size_t i = 0; i < graph.numBlocks(); i++) { + if (mir->shouldCancel("Backtracking Annotate Move Groups")) + return false; + + LBlock *block = graph.getBlock(i); + LInstruction *last = nullptr; + for (LInstructionIterator iter = block->begin(); iter != block->end(); ++iter) { + if (iter->isMoveGroup()) { + CodePosition from = last ? outputOf(last) : entryOf(block); + LiveInterval::Range range(from, from.next()); + AllocatedRange search(nullptr, &range), existing; + + for (size_t i = 0; i < AnyRegister::Total; i++) { + PhysicalRegister ® = registers[i]; + if (reg.reg.isFloat() || !reg.allocatable) + continue; + + // This register is unavailable for use if (a) it is in use + // by some live interval immediately before the move group, + // or (b) it is an operand in one of the group's moves. The + // latter case handles live intervals which end immediately + // before the move group or start immediately after. + + bool found = false; + LGeneralReg alloc(reg.reg.gpr()); + for (size_t j = 0; j < iter->toMoveGroup()->numMoves(); j++) { + LMove move = iter->toMoveGroup()->getMove(j); + if (*move.from() == alloc || *move.to() == alloc) { + found = true; + break; + } + } + + if (found || reg.allocations.contains(search, &existing)) + continue; + + iter->toMoveGroup()->setScratchRegister(reg.reg.gpr()); + break; + } + } else { + last = *iter; + } + } + } +#endif + + return true; +} void BacktrackingAllocator::dumpRegisterGroups() diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/BacktrackingAllocator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/BacktrackingAllocator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/BacktrackingAllocator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/BacktrackingAllocator.h 2015-02-03 14:33:30.000000000 +0000 @@ -176,9 +176,28 @@ // should be prioritized. AllocatedRangeSet hotcode; + // During register allocation, virtual stack slots are used for spills. + // These are converted to actual spill locations + size_t numVirtualStackSlots; + + // Information about an allocated stack slot. + struct SpillSlot : public TempObject, public InlineForwardListNode { + LStackSlot alloc; + AllocatedRangeSet allocated; + + SpillSlot(uint32_t slot, LifoAlloc *alloc) + : alloc(slot), allocated(alloc) + {} + }; + typedef InlineForwardList SpillSlotList; + + // All allocated slots of each width. + SpillSlotList normalSlots, doubleSlots, quadSlots; + public: BacktrackingAllocator(MIRGenerator *mir, LIRGenerator *lir, LIRGraph &graph) - : LiveRangeAllocator(mir, lir, graph) + : LiveRangeAllocator(mir, lir, graph), + numVirtualStackSlots(0) { } bool go(); @@ -213,10 +232,16 @@ bool addLiveInterval(LiveIntervalVector &intervals, uint32_t vreg, LiveInterval *spillInterval, CodePosition from, CodePosition to); + bool pickStackSlot(LiveInterval *interval); + bool reuseOrAllocateStackSlot(const LiveIntervalVector &intervals, LDefinition::Type type, + LAllocation *palloc); + bool insertAllRanges(AllocatedRangeSet &set, const LiveIntervalVector &intervals); + bool pickStackSlots(); bool resolveControlFlow(); bool reifyAllocations(); bool populateSafepoints(); + bool annotateMoveGroups(); void dumpRegisterGroups(); void dumpFixedRanges(); @@ -249,6 +274,11 @@ bool compilingAsmJS() { return mir->info().compilingAsmJS(); } + + bool isVirtualStackSlot(LAllocation alloc) { + return alloc.isStackSlot() && + LAllocation::DATA_MASK - alloc.toStackSlot()->slot() < numVirtualStackSlots; + } }; } // namespace jit diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Bailouts.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Bailouts.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Bailouts.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Bailouts.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -34,7 +34,7 @@ MOZ_ASSERT(IsInRange(FAKE_JIT_TOP_FOR_BAILOUT, 0, 0x1000) && IsInRange(FAKE_JIT_TOP_FOR_BAILOUT + sizeof(CommonFrameLayout), 0, 0x1000), "Fake jitTop pointer should be within the first page."); - cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT; + cx->runtime()->jitTop = FAKE_JIT_TOP_FOR_BAILOUT; JitActivationIterator jitActivations(cx->runtime()); BailoutFrameInfo bailoutData(jitActivations, sp); @@ -94,7 +94,7 @@ // In both cases, we want to temporarily set the |lastProfilingFrame| // to the current frame being bailed out, and then fix it up later. if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) - cx->mainThread().jitActivation->setLastProfilingFrame(currentFramePtr); + cx->runtime()->jitActivation->setLastProfilingFrame(currentFramePtr); return retval; } @@ -108,7 +108,7 @@ JSContext *cx = GetJSContextFromJitCode(); // We don't have an exit frame. - cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT; + cx->runtime()->jitTop = FAKE_JIT_TOP_FOR_BAILOUT; JitActivationIterator jitActivations(cx->runtime()); BailoutFrameInfo bailoutData(jitActivations, sp); @@ -168,7 +168,7 @@ // Make the frame being bailed out the top profiled frame. if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) - cx->mainThread().jitActivation->setLastProfilingFrame(currentFramePtr); + cx->runtime()->jitActivation->setLastProfilingFrame(currentFramePtr); return retval; } @@ -197,7 +197,7 @@ // operation callback like a timeout handler. MOZ_ASSERT_IF(!excInfo.propagatingIonExceptionForDebugMode(), cx->isExceptionPending()); - cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT; + cx->runtime()->jitTop = FAKE_JIT_TOP_FOR_BAILOUT; gc::AutoSuppressGC suppress(cx); JitActivationIterator jitActivations(cx->runtime()); @@ -238,7 +238,7 @@ // Make the frame being bailed out the top profiled frame. if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) - cx->mainThread().jitActivation->setLastProfilingFrame(currentFramePtr); + cx->runtime()->jitActivation->setLastProfilingFrame(currentFramePtr); return retval; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/BaselineBailouts.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/BaselineBailouts.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/BaselineBailouts.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/BaselineBailouts.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -1654,7 +1654,7 @@ // (which must be a baseline frame), and set it as the last profiling // frame. if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) - cx->mainThread().jitActivation->setLastProfilingFrame(iter.prevFp()); + cx->runtime()->jitActivation->setLastProfilingFrame(iter.prevFp()); uint32_t frameno = 0; while (frameno < numFrames) { @@ -1707,7 +1707,7 @@ // values into the baseline frame. We need to do this even when debug mode // is off, as we should respect the mutations made while debug mode was // on. - JitActivation *act = cx->mainThread().activation()->asJit(); + JitActivation *act = cx->runtime()->activation()->asJit(); if (act->hasRematerializedFrame(outerFp)) { JitFrameIterator iter(cx); size_t inlineDepth = numFrames; @@ -1754,6 +1754,8 @@ case Bailout_NonObjectInput: case Bailout_NonStringInput: case Bailout_NonSymbolInput: + case Bailout_NonSimdInt32x4Input: + case Bailout_NonSimdFloat32x4Input: case Bailout_InitialState: case Bailout_Debugger: // Do nothing. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/BaselineCompiler.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/BaselineCompiler.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/BaselineCompiler.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/BaselineCompiler.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -512,7 +512,7 @@ BaselineCompiler::emitStackCheck(bool earlyCheck) { Label skipCall; - void *limitAddr = cx->runtime()->mainThread.addressOfJitStackLimit(); + void *limitAddr = cx->runtime()->addressOfJitStackLimit(); uint32_t slotsSize = script->nslots() * sizeof(Value); uint32_t tolerance = earlyCheck ? slotsSize : 0; @@ -1721,21 +1721,21 @@ frame.syncStack(0); uint32_t length = GET_UINT24(pc); - RootedTypeObject type(cx); - if (!types::UseNewTypeForInitializer(script, pc, JSProto_Array)) { - type = types::TypeScript::InitObject(cx, script, pc, JSProto_Array); - if (!type) + RootedObjectGroup group(cx); + if (!types::UseSingletonForInitializer(script, pc, JSProto_Array)) { + group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Array); + if (!group) return false; } - // Pass length in R0, type in R1. + // Pass length in R0, group in R1. masm.move32(Imm32(length), R0.scratchReg()); - masm.movePtr(ImmGCPtr(type), R1.scratchReg()); + masm.movePtr(ImmGCPtr(group), R1.scratchReg()); ArrayObject *templateObject = NewDenseUnallocatedArray(cx, length, nullptr, TenuredObject); if (!templateObject) return false; - templateObject->setType(type); + templateObject->setGroup(group); ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject); if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) @@ -1796,10 +1796,10 @@ { frame.syncStack(0); - RootedTypeObject type(cx); - if (!types::UseNewTypeForInitializer(script, pc, JSProto_Object)) { - type = types::TypeScript::InitObject(cx, script, pc, JSProto_Object); - if (!type) + RootedObjectGroup group(cx); + if (!types::UseSingletonForInitializer(script, pc, JSProto_Object)) { + group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Object); + if (!group) return false; } @@ -1808,21 +1808,21 @@ if (!templateObject) return false; - if (type) { - templateObject->setType(type); + if (group) { + templateObject->setGroup(group); } else { - if (!JSObject::setSingletonType(cx, templateObject)) + if (!JSObject::setSingleton(cx, templateObject)) return false; } // Try to do the allocation inline. Label done; - if (type && !type->shouldPreTenure() && !templateObject->hasDynamicSlots()) { + if (group && !group->shouldPreTenure() && !templateObject->hasDynamicSlots()) { Label slowPath; Register objReg = R0.scratchReg(); Register tempReg = R1.scratchReg(); - masm.movePtr(ImmGCPtr(type), tempReg); - masm.branchTest32(Assembler::NonZero, Address(tempReg, types::TypeObject::offsetOfFlags()), + masm.movePtr(ImmGCPtr(group), tempReg); + masm.branchTest32(Assembler::NonZero, Address(tempReg, types::ObjectGroup::offsetOfFlags()), Imm32(types::OBJECT_FLAG_PRE_TENURE), &slowPath); masm.branchPtr(Assembler::NotEqual, AbsoluteAddress(cx->compartment()->addressOfMetadataCallback()), ImmWord(0), &slowPath); @@ -1847,22 +1847,22 @@ frame.syncStack(0); JSProtoKey key = JSProtoKey(GET_UINT8(pc)); - RootedTypeObject type(cx); - if (!types::UseNewTypeForInitializer(script, pc, key)) { - type = types::TypeScript::InitObject(cx, script, pc, key); - if (!type) + RootedObjectGroup group(cx); + if (!types::UseSingletonForInitializer(script, pc, key)) { + group = types::TypeScript::InitGroup(cx, script, pc, key); + if (!group) return false; } if (key == JSProto_Array) { - // Pass length in R0, type in R1. + // Pass length in R0, group in R1. masm.move32(Imm32(0), R0.scratchReg()); - masm.movePtr(ImmGCPtr(type), R1.scratchReg()); + masm.movePtr(ImmGCPtr(group), R1.scratchReg()); ArrayObject *templateObject = NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject); if (!templateObject) return false; - templateObject->setType(type); + templateObject->setGroup(group); ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject); if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) @@ -1875,10 +1875,10 @@ if (!templateObject) return false; - if (type) { - templateObject->setType(type); + if (group) { + templateObject->setGroup(group); } else { - if (!JSObject::setSingletonType(cx, templateObject)) + if (!JSObject::setSingleton(cx, templateObject)) return false; } @@ -3644,7 +3644,7 @@ Label skip; AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled()); masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skip); - masm.loadPtr(AbsoluteAddress(cx->mainThread().addressOfProfilingActivation()), scratchReg); + masm.loadPtr(AbsoluteAddress(cx->runtime()->addressOfProfilingActivation()), scratchReg); masm.storePtr(BaselineStackReg, Address(scratchReg, JitActivation::offsetOfLastProfilingFrame())); masm.bind(&skip); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/BaselineIC.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/BaselineIC.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/BaselineIC.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/BaselineIC.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -14,6 +14,7 @@ #include "jstypes.h" #include "builtin/Eval.h" +#include "builtin/SIMD.h" #include "jit/BaselineDebugModeOSR.h" #include "jit/BaselineHelpers.h" #include "jit/BaselineJIT.h" @@ -253,12 +254,12 @@ case ICStub::SetElem_Dense: { ICSetElem_Dense *setElemStub = toSetElem_Dense(); MarkShape(trc, &setElemStub->shape(), "baseline-getelem-dense-shape"); - MarkTypeObject(trc, &setElemStub->type(), "baseline-setelem-dense-type"); + MarkObjectGroup(trc, &setElemStub->group(), "baseline-setelem-dense-group"); break; } case ICStub::SetElem_DenseAdd: { ICSetElem_DenseAdd *setElemStub = toSetElem_DenseAdd(); - MarkTypeObject(trc, &setElemStub->type(), "baseline-setelem-denseadd-type"); + MarkObjectGroup(trc, &setElemStub->group(), "baseline-setelem-denseadd-group"); JS_STATIC_ASSERT(ICSetElem_DenseAdd::MAX_PROTO_CHAIN_DEPTH == 4); @@ -279,22 +280,22 @@ } case ICStub::TypeMonitor_SingleObject: { ICTypeMonitor_SingleObject *monitorStub = toTypeMonitor_SingleObject(); - MarkObject(trc, &monitorStub->object(), "baseline-monitor-singleobject"); + MarkObject(trc, &monitorStub->object(), "baseline-monitor-singleton"); break; } - case ICStub::TypeMonitor_TypeObject: { - ICTypeMonitor_TypeObject *monitorStub = toTypeMonitor_TypeObject(); - MarkTypeObject(trc, &monitorStub->type(), "baseline-monitor-typeobject"); + case ICStub::TypeMonitor_ObjectGroup: { + ICTypeMonitor_ObjectGroup *monitorStub = toTypeMonitor_ObjectGroup(); + MarkObjectGroup(trc, &monitorStub->group(), "baseline-monitor-group"); break; } case ICStub::TypeUpdate_SingleObject: { ICTypeUpdate_SingleObject *updateStub = toTypeUpdate_SingleObject(); - MarkObject(trc, &updateStub->object(), "baseline-update-singleobject"); + MarkObject(trc, &updateStub->object(), "baseline-update-singleton"); break; } - case ICStub::TypeUpdate_TypeObject: { - ICTypeUpdate_TypeObject *updateStub = toTypeUpdate_TypeObject(); - MarkTypeObject(trc, &updateStub->type(), "baseline-update-typeobject"); + case ICStub::TypeUpdate_ObjectGroup: { + ICTypeUpdate_ObjectGroup *updateStub = toTypeUpdate_ObjectGroup(); + MarkObjectGroup(trc, &updateStub->group(), "baseline-update-group"); break; } case ICStub::GetName_Global: { @@ -362,6 +363,11 @@ } break; } + case ICStub::GetProp_Unboxed: { + ICGetProp_Unboxed *propStub = toGetProp_Unboxed(); + MarkObjectGroup(trc, &propStub->group(), "baseline-getprop-unboxed-stub-group"); + break; + } case ICStub::GetProp_TypedObject: { ICGetProp_TypedObject *propStub = toGetProp_TypedObject(); MarkShape(trc, &propStub->shape(), "baseline-getprop-typedobject-stub-shape"); @@ -416,15 +422,15 @@ case ICStub::SetProp_Native: { ICSetProp_Native *propStub = toSetProp_Native(); MarkShape(trc, &propStub->shape(), "baseline-setpropnative-stub-shape"); - MarkTypeObject(trc, &propStub->type(), "baseline-setpropnative-stub-type"); + MarkObjectGroup(trc, &propStub->group(), "baseline-setpropnative-stub-group"); break; } case ICStub::SetProp_NativeAdd: { ICSetProp_NativeAdd *propStub = toSetProp_NativeAdd(); - MarkTypeObject(trc, &propStub->type(), "baseline-setpropnativeadd-stub-type"); + MarkObjectGroup(trc, &propStub->group(), "baseline-setpropnativeadd-stub-group"); MarkShape(trc, &propStub->newShape(), "baseline-setpropnativeadd-stub-newshape"); - if (propStub->newType()) - MarkTypeObject(trc, &propStub->newType(), "baseline-setpropnativeadd-stub-new-type"); + if (propStub->newGroup()) + MarkObjectGroup(trc, &propStub->newGroup(), "baseline-setpropnativeadd-stub-new-group"); JS_STATIC_ASSERT(ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH == 4); switch (propStub->protoChainDepth()) { case 0: propStub->toImpl<0>()->traceShapes(trc); break; @@ -436,10 +442,15 @@ } break; } + case ICStub::SetProp_Unboxed: { + ICSetProp_Unboxed *propStub = toSetProp_Unboxed(); + MarkObjectGroup(trc, &propStub->group(), "baseline-setprop-unboxed-stub-group"); + break; + } case ICStub::SetProp_TypedObject: { ICSetProp_TypedObject *propStub = toSetProp_TypedObject(); MarkShape(trc, &propStub->shape(), "baseline-setprop-typedobject-stub-shape"); - MarkTypeObject(trc, &propStub->type(), "baseline-setprop-typedobject-stub-type"); + MarkObjectGroup(trc, &propStub->group(), "baseline-setprop-typedobject-stub-group"); break; } case ICStub::SetProp_CallScripted: { @@ -1013,7 +1024,7 @@ Label checkOk; AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled()); masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &checkOk); - masm.loadPtr(AbsoluteAddress((void*)&cx->mainThread().jitActivation), scratchReg); + masm.loadPtr(AbsoluteAddress((void*)&cx->runtime()->jitActivation), scratchReg); masm.loadPtr(Address(scratchReg, JitActivation::offsetOfLastProfilingFrame()), scratchReg); // It may be the case that we entered the baseline frame with @@ -1087,7 +1098,7 @@ addOptimizedMonitorStub(stub); } - } else if (val.toObject().hasSingletonType()) { + } else if (val.toObject().isSingleton()) { RootedObject obj(cx, &val.toObject()); // Check for existing TypeMonitor stub. @@ -1112,26 +1123,26 @@ addOptimizedMonitorStub(stub); } else { - RootedTypeObject type(cx, val.toObject().type()); + RootedObjectGroup group(cx, val.toObject().group()); // Check for existing TypeMonitor stub. for (ICStubConstIterator iter(firstMonitorStub()); !iter.atEnd(); iter++) { - if (iter->isTypeMonitor_TypeObject() && - iter->toTypeMonitor_TypeObject()->type() == type) + if (iter->isTypeMonitor_ObjectGroup() && + iter->toTypeMonitor_ObjectGroup()->group() == group) { return true; } } - ICTypeMonitor_TypeObject::Compiler compiler(cx, type); + ICTypeMonitor_ObjectGroup::Compiler compiler(cx, group); ICStub *stub = compiler.getStub(compiler.getStubSpace(script)); if (!stub) { js_ReportOutOfMemory(cx); return false; } - JitSpew(JitSpew_BaselineIC, " Added TypeMonitor stub %p for TypeObject %p", - stub, type.get()); + JitSpew(JitSpew_BaselineIC, " Added TypeMonitor stub %p for ObjectGroup %p", + stub, group.get()); addOptimizedMonitorStub(stub); } @@ -1277,17 +1288,17 @@ } bool -ICTypeMonitor_TypeObject::Compiler::generateStubCode(MacroAssembler &masm) +ICTypeMonitor_ObjectGroup::Compiler::generateStubCode(MacroAssembler &masm) { Label failure; masm.branchTestObject(Assembler::NotEqual, R0, &failure); - // Guard on the object's TypeObject. + // Guard on the object's ObjectGroup. Register obj = masm.extractObject(R0, ExtractTemp0); - masm.loadPtr(Address(obj, JSObject::offsetOfType()), R1.scratchReg()); + masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), R1.scratchReg()); - Address expectedType(BaselineStubReg, ICTypeMonitor_TypeObject::offsetOfType()); - masm.branchPtr(Assembler::NotEqual, expectedType, R1.scratchReg(), &failure); + Address expectedGroup(BaselineStubReg, ICTypeMonitor_ObjectGroup::offsetOfGroup()); + masm.branchPtr(Assembler::NotEqual, expectedGroup, R1.scratchReg(), &failure); EmitReturnFromIC(masm); @@ -1339,7 +1350,7 @@ JitSpew(JitSpew_BaselineIC, " %s TypeUpdate stub %p for primitive type %d", existingStub ? "Modified existing" : "Created new", stub, type); - } else if (val.toObject().hasSingletonType()) { + } else if (val.toObject().isSingleton()) { RootedObject obj(cx, &val.toObject()); // Check for existing TypeUpdate stub. @@ -1361,24 +1372,24 @@ addOptimizedUpdateStub(stub); } else { - RootedTypeObject type(cx, val.toObject().type()); + RootedObjectGroup group(cx, val.toObject().group()); // Check for existing TypeUpdate stub. for (ICStubConstIterator iter(firstUpdateStub_); !iter.atEnd(); iter++) { - if (iter->isTypeUpdate_TypeObject() && - iter->toTypeUpdate_TypeObject()->type() == type) + if (iter->isTypeUpdate_ObjectGroup() && + iter->toTypeUpdate_ObjectGroup()->group() == group) { return true; } } - ICTypeUpdate_TypeObject::Compiler compiler(cx, type); + ICTypeUpdate_ObjectGroup::Compiler compiler(cx, group); ICStub *stub = compiler.getStub(compiler.getStubSpace(script)); if (!stub) return false; - JitSpew(JitSpew_BaselineIC, " Added TypeUpdate stub %p for TypeObject %p", - stub, type.get()); + JitSpew(JitSpew_BaselineIC, " Added TypeUpdate stub %p for ObjectGroup %p", + stub, group.get()); addOptimizedUpdateStub(stub); } @@ -1409,8 +1420,9 @@ break; } case ICStub::SetProp_Native: - case ICStub::SetProp_NativeAdd: { - MOZ_ASSERT(obj->isNative()); + case ICStub::SetProp_NativeAdd: + case ICStub::SetProp_Unboxed: { + MOZ_ASSERT(obj->isNative() || obj->is()); jsbytecode *pc = stub->getChainFallback()->icEntry()->pc(script); if (*pc == JSOP_SETALIASEDVAR || *pc == JSOP_INITALIASEDLEXICAL) id = NameToId(ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc)); @@ -1526,19 +1538,19 @@ } bool -ICTypeUpdate_TypeObject::Compiler::generateStubCode(MacroAssembler &masm) +ICTypeUpdate_ObjectGroup::Compiler::generateStubCode(MacroAssembler &masm) { Label failure; masm.branchTestObject(Assembler::NotEqual, R0, &failure); - // Guard on the object's TypeObject. + // Guard on the object's ObjectGroup. Register obj = masm.extractObject(R0, R1.scratchReg()); - masm.loadPtr(Address(obj, JSObject::offsetOfType()), R1.scratchReg()); + masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), R1.scratchReg()); - Address expectedType(BaselineStubReg, ICTypeUpdate_TypeObject::offsetOfType()); - masm.branchPtr(Assembler::NotEqual, expectedType, R1.scratchReg(), &failure); + Address expectedGroup(BaselineStubReg, ICTypeUpdate_ObjectGroup::offsetOfGroup()); + masm.branchPtr(Assembler::NotEqual, expectedGroup, R1.scratchReg(), &failure); - // Type matches, load true into R1.scratchReg() and return. + // Group matches, load true into R1.scratchReg() and return. masm.mov(ImmWord(1), R1.scratchReg()); EmitReturnFromIC(masm); @@ -1613,11 +1625,11 @@ static bool DoNewArray(JSContext *cx, ICNewArray_Fallback *stub, uint32_t length, - HandleTypeObject type, MutableHandleValue res) + HandleObjectGroup group, MutableHandleValue res) { FallbackICSpew(cx, stub, "NewArray"); - JSObject *obj = NewDenseArray(cx, length, type, NewArray_FullyAllocating); + JSObject *obj = NewDenseArray(cx, length, group, NewArray_FullyAllocating); if (!obj) return false; @@ -1625,7 +1637,7 @@ return true; } -typedef bool(*DoNewArrayFn)(JSContext *, ICNewArray_Fallback *, uint32_t, HandleTypeObject, +typedef bool(*DoNewArrayFn)(JSContext *, ICNewArray_Fallback *, uint32_t, HandleObjectGroup, MutableHandleValue); static const VMFunction DoNewArrayInfo = FunctionInfo(DoNewArray, TailCall); @@ -2049,8 +2061,8 @@ // obj != undefined only where !obj->getClass()->emulatesUndefined() Label emulatesUndefined; Register obj = masm.extractObject(objectOperand, ExtractTemp0); - masm.loadPtr(Address(obj, JSObject::offsetOfType()), obj); - masm.loadPtr(Address(obj, types::TypeObject::offsetOfClasp()), obj); + masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), obj); + masm.loadPtr(Address(obj, types::ObjectGroup::offsetOfClasp()), obj); masm.branchTest32(Assembler::NonZero, Address(obj, Class::offsetOfFlags()), Imm32(JSCLASS_EMULATES_UNDEFINED), @@ -3333,14 +3345,6 @@ return false; JSFunction *func = &shape->getterObject()->as(); - - // Information from get prop call ICs may be used directly from Ion code, - // and should not be nursery allocated. - if (IsInsideNursery(holder) || IsInsideNursery(func)) { - *isTemporarilyUnoptimizable = true; - return false; - } - if (func->isNative()) { *isScripted = false; return true; @@ -3456,13 +3460,6 @@ JSFunction *func = &shape->setterObject()->as(); - // Information from set prop call ICs may be used directly from Ion code, - // and should not be nursery allocated. - if (IsInsideNursery(holder) || IsInsideNursery(func)) { - *isTemporarilyUnoptimizable = true; - return false; - } - if (func->isNative()) { *isScripted = false; return true; @@ -4523,8 +4520,8 @@ break; case Layout_OutlineTypedObject: case Layout_InlineTypedObject: - masm.loadPtr(Address(obj, JSObject::offsetOfType()), result); - masm.loadPtr(Address(result, types::TypeObject::offsetOfAddendum()), result); + masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), result); + masm.loadPtr(Address(result, types::ObjectGroup::offsetOfAddendum()), result); masm.unboxInt32(Address(result, ArrayTypeDescr::offsetOfLength()), result); break; default: @@ -4834,13 +4831,13 @@ for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) { if (kind == ICStub::SetElem_Dense && iter->isSetElem_Dense()) { ICSetElem_Dense *dense = iter->toSetElem_Dense(); - if (obj->lastProperty() == dense->shape() && obj->getType(cx) == dense->type()) + if (obj->lastProperty() == dense->shape() && obj->getGroup(cx) == dense->group()) return true; } if (kind == ICStub::SetElem_DenseAdd && iter->isSetElem_DenseAdd()) { ICSetElem_DenseAdd *dense = iter->toSetElem_DenseAdd(); - if (obj->getType(cx) == dense->type() && SetElemDenseAddHasSameShapes(dense, obj)) + if (obj->getGroup(cx) == dense->group() && SetElemDenseAddHasSameShapes(dense, obj)) return true; } } @@ -5026,15 +5023,15 @@ &addingCase, &protoDepth)) { RootedShape shape(cx, obj->lastProperty()); - RootedTypeObject type(cx, obj->getType(cx)); - if (!type) + RootedObjectGroup group(cx, obj->getGroup(cx)); + if (!group) return false; if (addingCase && !DenseSetElemStubExists(cx, ICStub::SetElem_DenseAdd, stub, obj)) { JitSpew(JitSpew_BaselineIC, " Generating SetElem_DenseAdd stub " - "(shape=%p, type=%p, protoDepth=%u)", - obj->lastProperty(), type.get(), protoDepth); + "(shape=%p, group=%p, protoDepth=%u)", + obj->lastProperty(), group.get(), protoDepth); ICSetElemDenseAddCompiler compiler(cx, obj, protoDepth); ICUpdatedStub *denseStub = compiler.getStub(compiler.getStubSpace(script)); if (!denseStub) @@ -5047,9 +5044,9 @@ !DenseSetElemStubExists(cx, ICStub::SetElem_Dense, stub, obj)) { JitSpew(JitSpew_BaselineIC, - " Generating SetElem_Dense stub (shape=%p, type=%p)", - obj->lastProperty(), type.get()); - ICSetElem_Dense::Compiler compiler(cx, shape, type); + " Generating SetElem_Dense stub (shape=%p, group=%p)", + obj->lastProperty(), group.get()); + ICSetElem_Dense::Compiler compiler(cx, shape, group); ICUpdatedStub *denseStub = compiler.getStub(compiler.getStubSpace(script)); if (!denseStub) return false; @@ -5196,10 +5193,10 @@ regs = availableGeneralRegs(0); regs.take(R0); - // Guard that the type object matches. + // Guard that the object group matches. Register typeReg = regs.takeAny(); - masm.loadPtr(Address(BaselineStubReg, ICSetElem_Dense::offsetOfType()), typeReg); - masm.branchPtr(Assembler::NotEqual, Address(obj, JSObject::offsetOfType()), typeReg, + masm.loadPtr(Address(BaselineStubReg, ICSetElem_Dense::offsetOfGroup()), typeReg); + masm.branchPtr(Assembler::NotEqual, Address(obj, JSObject::offsetOfGroup()), typeReg, &failureUnstow); regs.add(typeReg); @@ -5364,10 +5361,10 @@ regs = availableGeneralRegs(0); regs.take(R0); - // Guard that the type object matches. + // Guard that the object group matches. Register typeReg = regs.takeAny(); - masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseAdd::offsetOfType()), typeReg); - masm.branchPtr(Assembler::NotEqual, Address(obj, JSObject::offsetOfType()), typeReg, + masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseAdd::offsetOfGroup()), typeReg); + masm.branchPtr(Assembler::NotEqual, Address(obj, JSObject::offsetOfGroup()), typeReg, &failureUnstow); regs.add(typeReg); @@ -6362,10 +6359,10 @@ static bool HasUnanalyzedNewScript(JSObject *obj) { - if (obj->hasSingletonType()) + if (obj->isSingleton()) return false; - types::TypeNewScript *newScript = obj->type()->newScript(); + types::TypeNewScript *newScript = obj->group()->newScript(); if (newScript && !newScript->analyzed()) return true; @@ -6538,6 +6535,10 @@ isTemporarilyUnoptimizable, isDOMProxy); } + // Try handling JSNative getters. + if (!cacheableCall || isScripted) + return true; + if (!shape || !shape->hasGetterValue() || !shape->getterValue().isObject() || !shape->getterObject()->is()) { @@ -6545,72 +6546,100 @@ } RootedFunction callee(cx, &shape->getterObject()->as()); + MOZ_ASSERT(callee->isNative()); if (outerClass && (!callee->jitInfo() || callee->jitInfo()->needsOuterizedThisObject())) return true; - // Try handling JSNative getters. - if (cacheableCall && !isScripted) { #if JS_HAS_NO_SUCH_METHOD - // It's unlikely that a getter function will be used to generate functions for calling - // in CALLPROP locations. Just don't attach stubs in that case to avoid issues with - // __noSuchMethod__ handling. - if (isCallProp) - return true; + // It's unlikely that a getter function will be used to generate functions for calling + // in CALLPROP locations. Just don't attach stubs in that case to avoid issues with + // __noSuchMethod__ handling. + if (isCallProp) + return true; #endif - MOZ_ASSERT(callee->isNative()); + JitSpew(JitSpew_BaselineIC, " Generating GetProp(%s%s/NativeGetter %p) stub", + isDOMProxy ? "DOMProxyObj" : "NativeObj", + isDOMProxy && domProxyHasGeneration ? "WithGeneration" : "", + callee->native()); - JitSpew(JitSpew_BaselineIC, " Generating GetProp(%s%s/NativeGetter %p) stub", - isDOMProxy ? "DOMProxyObj" : "NativeObj", - isDOMProxy && domProxyHasGeneration ? "WithGeneration" : "", - callee->native()); - - ICStub *newStub = nullptr; - if (isDOMProxy) { - MOZ_ASSERT(obj != holder); - ICStub::Kind kind; - if (domProxyHasGeneration) { - if (UpdateExistingGenerationalDOMProxyStub(stub, obj)) { - *attached = true; - return true; - } - kind = ICStub::GetProp_CallDOMProxyWithGenerationNative; - } else { - kind = ICStub::GetProp_CallDOMProxyNative; - } - Rooted proxy(cx, &obj->as()); - ICGetPropCallDOMProxyNativeCompiler - compiler(cx, kind, monitorStub, proxy, holder, callee, script->pcToOffset(pc)); - newStub = compiler.getStub(compiler.getStubSpace(script)); - } else if (obj == holder) { - if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallNative, - obj, JS::NullPtr(), callee)) { + ICStub *newStub = nullptr; + if (isDOMProxy) { + MOZ_ASSERT(obj != holder); + ICStub::Kind kind; + if (domProxyHasGeneration) { + if (UpdateExistingGenerationalDOMProxyStub(stub, obj)) { *attached = true; return true; } - - ICGetProp_CallNative::Compiler compiler(cx, monitorStub, obj, callee, - script->pcToOffset(pc), outerClass); - newStub = compiler.getStub(compiler.getStubSpace(script)); + kind = ICStub::GetProp_CallDOMProxyWithGenerationNative; } else { - if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallNativePrototype, - holder, obj, callee)) { - *attached = true; - return true; - } + kind = ICStub::GetProp_CallDOMProxyNative; + } + Rooted proxy(cx, &obj->as()); + ICGetPropCallDOMProxyNativeCompiler compiler(cx, kind, monitorStub, proxy, holder, callee, + script->pcToOffset(pc)); + newStub = compiler.getStub(compiler.getStubSpace(script)); + } else if (obj == holder) { + if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallNative, + obj, JS::NullPtr(), callee)) { + *attached = true; + return true; + } - ICGetProp_CallNativePrototype::Compiler compiler(cx, monitorStub, obj, holder, callee, - script->pcToOffset(pc), outerClass); - newStub = compiler.getStub(compiler.getStubSpace(script)); + ICGetProp_CallNative::Compiler compiler(cx, monitorStub, obj, callee, + script->pcToOffset(pc), outerClass); + newStub = compiler.getStub(compiler.getStubSpace(script)); + } else { + if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallNativePrototype, + holder, obj, callee)) { + *attached = true; + return true; } - if (!newStub) - return false; - stub->addNewStub(newStub); - *attached = true; - return true; + + ICGetProp_CallNativePrototype::Compiler compiler(cx, monitorStub, obj, holder, callee, + script->pcToOffset(pc), outerClass); + newStub = compiler.getStub(compiler.getStubSpace(script)); } + if (!newStub) + return false; + stub->addNewStub(newStub); + *attached = true; + return true; +} + +static bool +TryAttachUnboxedGetPropStub(JSContext *cx, HandleScript script, + ICGetProp_Fallback *stub, HandlePropertyName name, HandleValue val, + bool *attached) +{ + MOZ_ASSERT(!*attached); + + if (!cx->runtime()->jitSupportsFloatingPoint) + return true; + + if (!val.isObject() || !val.toObject().is()) + return true; + Rooted obj(cx, &val.toObject().as()); + + const UnboxedLayout::Property *property = obj->layout().lookup(name); + if (!property) + return true; + + ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); + + ICGetProp_Unboxed::Compiler compiler(cx, monitorStub, obj->group(), + property->offset + UnboxedPlainObject::offsetOfData(), + property->type); + ICStub *newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + stub->addNewStub(newStub); + StripPreliminaryObjectStubs(cx, stub); + + *attached = true; return true; } @@ -6861,6 +6890,11 @@ if (attached) return true; + if (!TryAttachUnboxedGetPropStub(cx, script, stub, name, val, &attached)) + return false; + if (attached) + return true; + if (!TryAttachTypedObjectGetPropStub(cx, script, stub, name, val, &attached)) return false; if (attached) @@ -7532,6 +7566,8 @@ Value expandoVal; if (kind == ICStub::GetProp_CallDOMProxyNative) { expandoVal = expandoSlot; + expandoAndGeneration = nullptr; // initialize to silence GCC warning + generation = 0; // initialize to silence GCC warning } else { MOZ_ASSERT(kind == ICStub::GetProp_CallDOMProxyWithGenerationNative); MOZ_ASSERT(!expandoSlot.isObject() && !expandoSlot.isUndefined()); @@ -7770,6 +7806,39 @@ } bool +ICGetProp_Unboxed::Compiler::generateStubCode(MacroAssembler &masm) +{ + Label failure; + + GeneralRegisterSet regs(availableGeneralRegs(1)); + + Register scratch = regs.takeAnyExcluding(BaselineTailCallReg); + + // Object and group guard. + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + Register object = masm.extractObject(R0, ExtractTemp0); + masm.loadPtr(Address(BaselineStubReg, ICGetProp_Unboxed::offsetOfGroup()), scratch); + masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), scratch, + &failure); + + // Get the address being read from. + masm.load32(Address(BaselineStubReg, ICGetProp_Unboxed::offsetOfFieldOffset()), scratch); + + masm.loadUnboxedProperty(BaseIndex(object, scratch, TimesOne), fieldType_, TypedOrValueRegister(R0)); + + // Only monitor the result if its type might change. + if (fieldType_ == JSVAL_TYPE_OBJECT) + EmitEnterTypeMonitorIC(masm); + else + EmitReturnFromIC(masm); + + masm.bind(&failure); + EmitStubGuardFailure(masm); + + return true; +} + +bool ICGetProp_TypedObject::Compiler::generateStubCode(MacroAssembler &masm) { Label failure; @@ -7863,7 +7932,7 @@ // value property. static bool TryAttachSetValuePropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetProp_Fallback *stub, - HandleObject obj, HandleShape oldShape, HandleTypeObject oldType, uint32_t oldSlots, + HandleObject obj, HandleShape oldShape, HandleObjectGroup oldGroup, uint32_t oldSlots, HandlePropertyName name, HandleId id, HandleValue rhs, bool *attached) { MOZ_ASSERT(!*attached); @@ -7886,7 +7955,7 @@ // script properties analysis hasn't been performed for yet, as there // may be a shape change required here afterwards. Pretend we attached // a stub, though, so the access is not marked as unoptimizable. - if (oldType->newScript() && !oldType->newScript()->analyzed()) { + if (oldGroup->newScript() && !oldGroup->newScript()->analyzed()) { *attached = true; return true; } @@ -7896,7 +7965,7 @@ GetFixedOrDynamicSlotOffset(&obj->as(), shape->slot(), &isFixedSlot, &offset); JitSpew(JitSpew_BaselineIC, " Generating SetProp(NativeObject.ADD) stub"); - ICSetPropNativeAddCompiler compiler(cx, obj, oldShape, oldType, + ICSetPropNativeAddCompiler compiler(cx, obj, oldShape, oldGroup, chainDepth, isFixedSlot, offset); ICUpdatedStub *newStub = compiler.getStub(compiler.getStubSpace(script)); if (!newStub) @@ -8024,6 +8093,40 @@ } static bool +TryAttachUnboxedSetPropStub(JSContext *cx, HandleScript script, + ICSetProp_Fallback *stub, HandleId id, + HandleObject obj, HandleValue rhs, bool *attached) +{ + MOZ_ASSERT(!*attached); + + if (!cx->runtime()->jitSupportsFloatingPoint) + return true; + + if (!obj->is()) + return true; + + const UnboxedLayout::Property *property = obj->as().layout().lookup(id); + if (!property) + return true; + + ICSetProp_Unboxed::Compiler compiler(cx, obj->group(), + property->offset + UnboxedPlainObject::offsetOfData(), + property->type); + ICUpdatedStub *newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + if (compiler.needsUpdateStubs() && !newStub->addUpdateStubForValue(cx, script, obj, id, rhs)) + return false; + + stub->addNewStub(newStub); + + StripPreliminaryObjectStubs(cx, stub); + + *attached = true; + return true; +} + +static bool TryAttachTypedObjectSetPropStub(JSContext *cx, HandleScript script, ICSetProp_Fallback *stub, HandleId id, HandleObject obj, HandleValue rhs, bool *attached) @@ -8051,7 +8154,7 @@ uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex); - ICSetProp_TypedObject::Compiler compiler(cx, obj->lastProperty(), obj->type(), fieldOffset, + ICSetProp_TypedObject::Compiler compiler(cx, obj->lastProperty(), obj->group(), fieldOffset, &fieldDescr->as()); ICUpdatedStub *newStub = compiler.getStub(compiler.getStubSpace(script)); if (!newStub) @@ -8098,8 +8201,8 @@ if (!obj) return false; RootedShape oldShape(cx, obj->lastProperty()); - RootedTypeObject oldType(cx, obj->getType(cx)); - if (!oldType) + RootedObjectGroup oldGroup(cx, obj->getGroup(cx)); + if (!oldGroup) return false; uint32_t oldSlots = obj->isNative() ? obj->as().numDynamicSlots() : 0; @@ -8159,7 +8262,16 @@ if (!attached && lhs.isObject() && !TryAttachSetValuePropStub(cx, script, pc, stub, obj, oldShape, - oldType, oldSlots, name, id, rhs, &attached)) + oldGroup, oldSlots, name, id, rhs, &attached)) + { + return false; + } + if (attached) + return true; + + if (!attached && + lhs.isObject() && + !TryAttachUnboxedSetPropStub(cx, script, stub, id, obj, rhs, &attached)) { return false; } @@ -8251,9 +8363,9 @@ masm.loadPtr(Address(BaselineStubReg, ICSetProp_Native::offsetOfShape()), scratch); masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure); - // Guard that the type object matches. - masm.loadPtr(Address(BaselineStubReg, ICSetProp_Native::offsetOfType()), scratch); - masm.branchPtr(Assembler::NotEqual, Address(objReg, JSObject::offsetOfType()), scratch, + // Guard that the object group matches. + masm.loadPtr(Address(BaselineStubReg, ICSetProp_Native::offsetOfGroup()), scratch); + masm.branchPtr(Assembler::NotEqual, Address(objReg, JSObject::offsetOfGroup()), scratch, &failure); // Stow both R0 and R1 (object and value). @@ -8347,9 +8459,9 @@ masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAddImpl<0>::offsetOfShape(0)), scratch); masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure); - // Guard that the type object matches. - masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfType()), scratch); - masm.branchPtr(Assembler::NotEqual, Address(objReg, JSObject::offsetOfType()), scratch, + // Guard that the object group matches. + masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfGroup()), scratch); + masm.branchPtr(Assembler::NotEqual, Address(objReg, JSObject::offsetOfGroup()), scratch, &failure); // Stow both R0 and R1 (object and value). @@ -8388,29 +8500,29 @@ masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch); masm.storePtr(scratch, shapeAddr); - // Try to change the object's type. - Label noTypeChange; + // Try to change the object's group. + Label noGroupChange; - // Check if the cache has a new type to change to. - masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewType()), scratch); - masm.branchTestPtr(Assembler::Zero, scratch, scratch, &noTypeChange); + // Check if the cache has a new group to change to. + masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch); + masm.branchTestPtr(Assembler::Zero, scratch, scratch, &noGroupChange); - // Check if the old type still has a newScript. - masm.loadPtr(Address(objReg, JSObject::offsetOfType()), scratch); + // Check if the old group still has a newScript. + masm.loadPtr(Address(objReg, JSObject::offsetOfGroup()), scratch); masm.branchPtr(Assembler::Equal, - Address(scratch, types::TypeObject::offsetOfAddendum()), + Address(scratch, types::ObjectGroup::offsetOfAddendum()), ImmWord(0), - &noTypeChange); + &noGroupChange); - // Reload the new type from the cache. - masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewType()), scratch); + // Reload the new group from the cache. + masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch); - // Change the object's type. - Address typeAddr(objReg, JSObject::offsetOfType()); - EmitPreBarrier(masm, typeAddr, MIRType_TypeObject); - masm.storePtr(scratch, typeAddr); + // Change the object's group. + Address groupAddr(objReg, JSObject::offsetOfGroup()); + EmitPreBarrier(masm, groupAddr, MIRType_ObjectGroup); + masm.storePtr(scratch, groupAddr); - masm.bind(&noTypeChange); + masm.bind(&noGroupChange); Register holderReg; regs.add(R0); @@ -8451,6 +8563,75 @@ } bool +ICSetProp_Unboxed::Compiler::generateStubCode(MacroAssembler &masm) +{ + Label failure; + + // Guard input is an object. + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + + GeneralRegisterSet regs(availableGeneralRegs(2)); + Register scratch = regs.takeAny(); + + // Unbox and group guard. + Register object = masm.extractObject(R0, ExtractTemp0); + masm.loadPtr(Address(BaselineStubReg, ICSetProp_Unboxed::offsetOfGroup()), scratch); + masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), scratch, + &failure); + + if (needsUpdateStubs()) { + // Stow both R0 and R1 (object and value). + masm.push(object); + masm.push(BaselineStubReg); + EmitStowICValues(masm, 2); + + // Move RHS into R0 for TypeUpdate check. + masm.moveValue(R1, R0); + + // Call the type update stub. + if (!callTypeUpdateIC(masm, sizeof(Value))) + return false; + + // Unstow R0 and R1 (object and key) + EmitUnstowICValues(masm, 2); + masm.pop(BaselineStubReg); + masm.pop(object); + + // Trigger post barriers here on the values being written. Fields which + // objects can be written to also need update stubs. + GeneralRegisterSet saveRegs; + saveRegs.add(R0); + saveRegs.add(R1); + saveRegs.addUnchecked(object); + saveRegs.add(BaselineStubReg); + emitPostWriteBarrierSlot(masm, object, R1, scratch, saveRegs); + } + + // Compute the address being written to. + masm.load32(Address(BaselineStubReg, ICSetProp_Unboxed::offsetOfFieldOffset()), scratch); + BaseIndex address(object, scratch, TimesOne); + + if (fieldType_ == JSVAL_TYPE_OBJECT) + EmitPreBarrier(masm, address, MIRType_Object); + else if (fieldType_ == JSVAL_TYPE_STRING) + EmitPreBarrier(masm, address, MIRType_String); + else + MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(fieldType_)); + + masm.storeUnboxedProperty(address, fieldType_, + ConstantOrRegister(TypedOrValueRegister(R1)), &failure); + + // The RHS has to be in R0. + masm.moveValue(R1, R0); + + EmitReturnFromIC(masm); + + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +bool ICSetProp_TypedObject::Compiler::generateStubCode(MacroAssembler &masm) { Label failure; @@ -8468,9 +8649,9 @@ masm.loadPtr(Address(BaselineStubReg, ICSetProp_TypedObject::offsetOfShape()), scratch); masm.branchTestObjShape(Assembler::NotEqual, object, scratch, &failure); - // Guard that the type object matches. - masm.loadPtr(Address(BaselineStubReg, ICSetProp_TypedObject::offsetOfType()), scratch); - masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfType()), scratch, + // Guard that the object group matches. + masm.loadPtr(Address(BaselineStubReg, ICSetProp_TypedObject::offsetOfGroup()), scratch); + masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), scratch, &failure); if (needsUpdateStubs()) { @@ -8840,7 +9021,7 @@ static bool GetTemplateObjectForNative(JSContext *cx, HandleScript script, jsbytecode *pc, - Native native, const CallArgs &args, MutableHandleNativeObject res) + Native native, const CallArgs &args, MutableHandleObject res) { // Check for natives to which template objects can be attached. This is // done to provide templates to Ion for inlining these natives later on. @@ -8858,10 +9039,10 @@ if (!res) return false; - types::TypeObject *type = types::TypeScript::InitObject(cx, script, pc, JSProto_Array); - if (!type) + types::ObjectGroup *group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Array); + if (!group) return false; - res->setType(type); + res->setGroup(group); return true; } @@ -8870,21 +9051,21 @@ if (!res) return false; - types::TypeObject *type = types::TypeScript::InitObject(cx, script, pc, JSProto_Array); - if (!type) + types::ObjectGroup *group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Array); + if (!group) return false; - res->setType(type); + res->setGroup(group); return true; } if (native == js::array_concat) { if (args.thisv().isObject() && args.thisv().toObject().is() && - !args.thisv().toObject().hasSingletonType()) + !args.thisv().toObject().isSingleton()) { res.set(NewDenseEmptyArray(cx, args.thisv().toObject().getProto(), TenuredObject)); if (!res) return false; - res->setType(args.thisv().toObject().type()); + res->setGroup(args.thisv().toObject().group()); return true; } } @@ -8894,10 +9075,10 @@ if (!res) return false; - types::TypeObject *type = types::TypeScript::InitObject(cx, script, pc, JSProto_Array); - if (!type) + types::ObjectGroup *group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Array); + if (!group) return false; - res->setType(type); + res->setGroup(group); return true; } @@ -8917,6 +9098,14 @@ return true; } + if (native == js::simd_int32x4_add && JitSupportsSimd()) { + Rooted descr(cx, &Int32x4::GetTypeDescr(*cx->global())); + res.set(TypedObject::createZeroed(cx, descr, 0, gc::TenuredHeap)); + if (!res) + return false; + return true; + } + return true; } @@ -8967,9 +9156,9 @@ static bool TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsbytecode *pc, JSOp op, uint32_t argc, Value *vp, bool constructing, bool isSpread, - bool useNewType) + bool createSingleton) { - if (useNewType || op == JSOP_EVAL || op == JSOP_STRICTEVAL) + if (createSingleton || op == JSOP_EVAL || op == JSOP_STRICTEVAL) return true; if (stub->numOptimizedStubs() >= ICCall_Fallback::MAX_OPTIMIZED_STUBS) { @@ -9000,7 +9189,7 @@ if (obj->is()) return true; if (JSNative hook = constructing ? obj->constructHook() : obj->callHook()) { - if (op != JSOP_FUNAPPLY && !isSpread && !useNewType) { + if (op != JSOP_FUNAPPLY && !isSpread && !createSingleton) { RootedObject templateObject(cx); CallArgs args = CallArgsFromVp(argc, vp); if (!GetTemplateObjectForClassHook(cx, hook, args, &templateObject)) @@ -9072,25 +9261,28 @@ // Remember the template object associated with any script being called // as a constructor, for later use during Ion compilation. - RootedPlainObject templateObject(cx); + RootedObject templateObject(cx); if (constructing) { - templateObject = CreateThisForFunction(cx, fun, MaybeSingletonObject); - if (!templateObject) + JSObject *thisObject = CreateThisForFunction(cx, fun, MaybeSingletonObject); + if (!thisObject) return false; - // If we are calling a constructor for which the new script - // properties analysis has not been performed yet, don't attach a - // stub. After the analysis is performed, CreateThisForFunction may - // start returning objects with a different type, and the Ion - // compiler might get confused. - if (templateObject->type()->newScript() && - !templateObject->type()->newScript()->analyzed()) - { - // Clear the object just created from the preliminary objects - // on the TypeNewScript, as it will not be used or filled in by - // running code. - templateObject->type()->newScript()->unregisterNewObject(templateObject); - return true; + if (thisObject->is() || thisObject->is()) { + templateObject = thisObject; + + // If we are calling a constructor for which the new script + // properties analysis has not been performed yet, don't attach a + // stub. After the analysis is performed, CreateThisForFunction may + // start returning objects with a different type, and the Ion + // compiler might get confused. + types::TypeNewScript *newScript = templateObject->group()->newScript(); + if (newScript && !newScript->analyzed()) { + // Clear the object just created from the preliminary objects + // on the TypeNewScript, as it will not be used or filled in by + // running code. + newScript->unregisterNewObject(&templateObject->as()); + return true; + } } } @@ -9151,7 +9343,7 @@ return true; } - RootedNativeObject templateObject(cx); + RootedObject templateObject(cx); if (MOZ_LIKELY(!isSpread)) { CallArgs args = CallArgsFromVp(argc, vp); if (!GetTemplateObjectForNative(cx, script, pc, fun->native(), args, &templateObject)) @@ -9181,15 +9373,15 @@ uint32_t length = obj->as().length(); MOZ_ASSERT(obj->getDenseInitializedLength() == length); - RootedTypeObject type(cx, obj->getType(cx)); - if (!type) + RootedObjectGroup group(cx, obj->getGroup(cx)); + if (!group) return false; RootedArrayObject newObj(cx, NewDenseFullyAllocatedArray(cx, length, nullptr, TenuredObject)); if (!newObj) return false; - newObj->setType(type); + newObj->setGroup(group); newObj->setDenseInitializedLength(length); newObj->initDenseElements(0, obj->getDenseElements(), length); result.setObject(*newObj); @@ -9281,12 +9473,12 @@ return false; } - // Compute construcing and useNewType flags. + // Compute construcing and useNewGroup flags. bool constructing = (op == JSOP_NEW); - bool newType = types::UseNewType(cx, script, pc); + bool createSingleton = types::UseSingletonForNewObject(cx, script, pc); // Try attaching a call stub. - if (!TryAttachCallStub(cx, stub, script, pc, op, argc, vp, constructing, false, newType)) + if (!TryAttachCallStub(cx, stub, script, pc, op, argc, vp, constructing, false, createSingleton)) return false; if (!MaybeCloneFunctionAtCallsite(cx, &callee, script, pc)) @@ -11278,9 +11470,9 @@ obj_(obj) { } -ICTypeMonitor_TypeObject::ICTypeMonitor_TypeObject(JitCode *stubCode, HandleTypeObject type) - : ICStub(TypeMonitor_TypeObject, stubCode), - type_(type) +ICTypeMonitor_ObjectGroup::ICTypeMonitor_ObjectGroup(JitCode *stubCode, HandleObjectGroup group) + : ICStub(TypeMonitor_ObjectGroup, stubCode), + group_(group) { } ICTypeUpdate_SingleObject::ICTypeUpdate_SingleObject(JitCode *stubCode, HandleObject obj) @@ -11288,9 +11480,9 @@ obj_(obj) { } -ICTypeUpdate_TypeObject::ICTypeUpdate_TypeObject(JitCode *stubCode, HandleTypeObject type) - : ICStub(TypeUpdate_TypeObject, stubCode), - type_(type) +ICTypeUpdate_ObjectGroup::ICTypeUpdate_ObjectGroup(JitCode *stubCode, HandleObjectGroup group) + : ICStub(TypeUpdate_ObjectGroup, stubCode), + group_(group) { } ICGetElemNativeStub::ICGetElemNativeStub(ICStub::Kind kind, JitCode *stubCode, @@ -11399,16 +11591,16 @@ return New(space, other.jitCode(), firstMonitorStub, other.which()); } -ICSetElem_Dense::ICSetElem_Dense(JitCode *stubCode, HandleShape shape, HandleTypeObject type) +ICSetElem_Dense::ICSetElem_Dense(JitCode *stubCode, HandleShape shape, HandleObjectGroup group) : ICUpdatedStub(SetElem_Dense, stubCode), shape_(shape), - type_(type) + group_(group) { } -ICSetElem_DenseAdd::ICSetElem_DenseAdd(JitCode *stubCode, types::TypeObject *type, +ICSetElem_DenseAdd::ICSetElem_DenseAdd(JitCode *stubCode, types::ObjectGroup *group, size_t protoChainDepth) : ICUpdatedStub(SetElem_DenseAdd, stubCode), - type_(type) + group_(group) { MOZ_ASSERT(protoChainDepth <= MAX_PROTO_CHAIN_DEPTH); extra_ = protoChainDepth; @@ -11418,11 +11610,11 @@ ICUpdatedStub * ICSetElemDenseAddCompiler::getStubSpecific(ICStubSpace *space, const AutoShapeVector *shapes) { - RootedTypeObject objType(cx, obj_->getType(cx)); - if (!objType) + RootedObjectGroup group(cx, obj_->getGroup(cx)); + if (!group) return nullptr; Rooted stubCode(cx, getStubCode()); - return ICSetElem_DenseAddImpl::New(space, stubCode, objType, shapes); + return ICSetElem_DenseAddImpl::New(space, stubCode, group, shapes); } ICSetElem_TypedArray::ICSetElem_TypedArray(JitCode *stubCode, HandleShape shape, Scalar::Type type, @@ -11611,10 +11803,10 @@ holderShape, getter, other.pcOffset_); } -ICSetProp_Native::ICSetProp_Native(JitCode *stubCode, HandleTypeObject type, HandleShape shape, +ICSetProp_Native::ICSetProp_Native(JitCode *stubCode, HandleObjectGroup group, HandleShape shape, uint32_t offset) : ICUpdatedStub(SetProp_Native, stubCode), - type_(type), + group_(group), shape_(shape), offset_(offset) { } @@ -11622,26 +11814,26 @@ ICSetProp_Native * ICSetProp_Native::Compiler::getStub(ICStubSpace *space) { - RootedTypeObject type(cx, obj_->getType(cx)); - if (!type) + RootedObjectGroup group(cx, obj_->getGroup(cx)); + if (!group) return nullptr; RootedShape shape(cx, obj_->lastProperty()); - ICSetProp_Native *stub = ICSetProp_Native::New(space, getStubCode(), type, shape, offset_); + ICSetProp_Native *stub = ICSetProp_Native::New(space, getStubCode(), group, shape, offset_); if (!stub || !stub->initUpdatingChain(cx, space)) return nullptr; return stub; } -ICSetProp_NativeAdd::ICSetProp_NativeAdd(JitCode *stubCode, HandleTypeObject type, +ICSetProp_NativeAdd::ICSetProp_NativeAdd(JitCode *stubCode, HandleObjectGroup group, size_t protoChainDepth, HandleShape newShape, - HandleTypeObject newType, + HandleObjectGroup newGroup, uint32_t offset) : ICUpdatedStub(SetProp_NativeAdd, stubCode), - type_(type), + group_(group), newShape_(newShape), - newType_(newType), + newGroup_(newGroup), offset_(offset) { MOZ_ASSERT(protoChainDepth <= MAX_PROTO_CHAIN_DEPTH); @@ -11650,12 +11842,12 @@ template ICSetProp_NativeAddImpl::ICSetProp_NativeAddImpl(JitCode *stubCode, - HandleTypeObject type, + HandleObjectGroup group, const AutoShapeVector *shapes, HandleShape newShape, - HandleTypeObject newType, + HandleObjectGroup newGroup, uint32_t offset) - : ICSetProp_NativeAdd(stubCode, type, ProtoChainDepth, newShape, newType, offset) + : ICSetProp_NativeAdd(stubCode, group, ProtoChainDepth, newShape, newGroup, offset) { MOZ_ASSERT(shapes->length() == NumShapes); for (size_t i = 0; i < NumShapes; i++) @@ -11664,14 +11856,14 @@ ICSetPropNativeAddCompiler::ICSetPropNativeAddCompiler(JSContext *cx, HandleObject obj, HandleShape oldShape, - HandleTypeObject oldType, + HandleObjectGroup oldGroup, size_t protoChainDepth, bool isFixedSlot, uint32_t offset) : ICStubCompiler(cx, ICStub::SetProp_NativeAdd), obj_(cx, obj), oldShape_(cx, oldShape), - oldType_(cx, oldType), + oldGroup_(cx, oldGroup), protoChainDepth_(protoChainDepth), isFixedSlot_(isFixedSlot), offset_(offset) @@ -11715,7 +11907,7 @@ } ICCall_Scripted::ICCall_Scripted(JitCode *stubCode, ICStub *firstMonitorStub, - HandleScript calleeScript, HandleNativeObject templateObject, + HandleScript calleeScript, HandleObject templateObject, uint32_t pcOffset) : ICMonitoredStub(ICStub::Call_Scripted, stubCode, firstMonitorStub), calleeScript_(calleeScript), @@ -11728,7 +11920,7 @@ ICCall_Scripted &other) { RootedScript calleeScript(cx, other.calleeScript_); - RootedNativeObject templateObject(cx, other.templateObject_); + RootedObject templateObject(cx, other.templateObject_); return New(space, other.jitCode(), firstMonitorStub, calleeScript, templateObject, other.pcOffset_); } @@ -11741,7 +11933,7 @@ } ICCall_Native::ICCall_Native(JitCode *stubCode, ICStub *firstMonitorStub, - HandleFunction callee, HandleNativeObject templateObject, + HandleFunction callee, HandleObject templateObject, uint32_t pcOffset) : ICMonitoredStub(ICStub::Call_Native, stubCode, firstMonitorStub), callee_(callee), @@ -11762,7 +11954,7 @@ ICCall_Native &other) { RootedFunction callee(cx, other.callee_); - RootedNativeObject templateObject(cx, other.templateObject_); + RootedObject templateObject(cx, other.templateObject_); return New(space, other.jitCode(), firstMonitorStub, callee, templateObject, other.pcOffset_); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/BaselineIC.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/BaselineIC.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/BaselineIC.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/BaselineIC.h 2015-02-03 14:33:31.000000000 +0000 @@ -142,8 +142,8 @@ // between stubs on an IC, but instead are kept track of on a per-stub basis. // // This is because the main stubs for the operation will each identify a potentially -// different TypeObject to update. New input types must be tracked on a typeobject-to- -// typeobject basis. +// different ObjectGroup to update. New input types must be tracked on a group-to- +// group basis. // // Type-update ICs cannot be called in tail position (they must return to the // the stub that called them so that the stub may continue to perform its original @@ -336,12 +336,12 @@ \ _(TypeMonitor_Fallback) \ _(TypeMonitor_SingleObject) \ - _(TypeMonitor_TypeObject) \ + _(TypeMonitor_ObjectGroup) \ _(TypeMonitor_PrimitiveSet) \ \ _(TypeUpdate_Fallback) \ _(TypeUpdate_SingleObject) \ - _(TypeUpdate_TypeObject) \ + _(TypeUpdate_ObjectGroup) \ _(TypeUpdate_PrimitiveSet) \ \ _(This_Fallback) \ @@ -430,6 +430,7 @@ _(GetProp_Native) \ _(GetProp_NativeDoesNotExist) \ _(GetProp_NativePrototype) \ + _(GetProp_Unboxed) \ _(GetProp_TypedObject) \ _(GetProp_CallScripted) \ _(GetProp_CallNative) \ @@ -444,6 +445,7 @@ _(SetProp_Fallback) \ _(SetProp_Native) \ _(SetProp_NativeAdd) \ + _(SetProp_Unboxed) \ _(SetProp_TypedObject) \ _(SetProp_CallScripted) \ _(SetProp_CallNative) \ @@ -1560,44 +1562,44 @@ }; }; -class ICTypeMonitor_TypeObject : public ICStub +class ICTypeMonitor_ObjectGroup : public ICStub { friend class ICStubSpace; - HeapPtrTypeObject type_; + HeapPtrObjectGroup group_; - ICTypeMonitor_TypeObject(JitCode *stubCode, HandleTypeObject type); + ICTypeMonitor_ObjectGroup(JitCode *stubCode, HandleObjectGroup group); public: - static inline ICTypeMonitor_TypeObject *New( - ICStubSpace *space, JitCode *code, HandleTypeObject type) + static inline ICTypeMonitor_ObjectGroup *New( + ICStubSpace *space, JitCode *code, HandleObjectGroup group) { if (!code) return nullptr; - return space->allocate(code, type); + return space->allocate(code, group); } - HeapPtrTypeObject &type() { - return type_; + HeapPtrObjectGroup &group() { + return group_; } - static size_t offsetOfType() { - return offsetof(ICTypeMonitor_TypeObject, type_); + static size_t offsetOfGroup() { + return offsetof(ICTypeMonitor_ObjectGroup, group_); } class Compiler : public ICStubCompiler { protected: - HandleTypeObject type_; + HandleObjectGroup group_; bool generateStubCode(MacroAssembler &masm); public: - Compiler(JSContext *cx, HandleTypeObject type) - : ICStubCompiler(cx, TypeMonitor_TypeObject), - type_(type) + Compiler(JSContext *cx, HandleObjectGroup group) + : ICStubCompiler(cx, TypeMonitor_ObjectGroup), + group_(group) { } - ICTypeMonitor_TypeObject *getStub(ICStubSpace *space) { - return ICTypeMonitor_TypeObject::New(space, getStubCode(), type_); + ICTypeMonitor_ObjectGroup *getStub(ICStubSpace *space) { + return ICTypeMonitor_ObjectGroup::New(space, getStubCode(), group_); } }; }; @@ -1723,45 +1725,45 @@ }; }; -// Type update stub to handle a single TypeObject. -class ICTypeUpdate_TypeObject : public ICStub +// Type update stub to handle a single ObjectGroup. +class ICTypeUpdate_ObjectGroup : public ICStub { friend class ICStubSpace; - HeapPtrTypeObject type_; + HeapPtrObjectGroup group_; - ICTypeUpdate_TypeObject(JitCode *stubCode, HandleTypeObject type); + ICTypeUpdate_ObjectGroup(JitCode *stubCode, HandleObjectGroup group); public: - static inline ICTypeUpdate_TypeObject *New(ICStubSpace *space, JitCode *code, - HandleTypeObject type) + static inline ICTypeUpdate_ObjectGroup *New(ICStubSpace *space, JitCode *code, + HandleObjectGroup group) { if (!code) return nullptr; - return space->allocate(code, type); + return space->allocate(code, group); } - HeapPtrTypeObject &type() { - return type_; + HeapPtrObjectGroup &group() { + return group_; } - static size_t offsetOfType() { - return offsetof(ICTypeUpdate_TypeObject, type_); + static size_t offsetOfGroup() { + return offsetof(ICTypeUpdate_ObjectGroup, group_); } class Compiler : public ICStubCompiler { protected: - HandleTypeObject type_; + HandleObjectGroup group_; bool generateStubCode(MacroAssembler &masm); public: - Compiler(JSContext *cx, HandleTypeObject type) - : ICStubCompiler(cx, TypeUpdate_TypeObject), - type_(type) + Compiler(JSContext *cx, HandleObjectGroup group) + : ICStubCompiler(cx, TypeUpdate_ObjectGroup), + group_(group) { } - ICTypeUpdate_TypeObject *getStub(ICStubSpace *space) { - return ICTypeUpdate_TypeObject::New(space, getStubCode(), type_); + ICTypeUpdate_ObjectGroup *getStub(ICStubSpace *space) { + return ICTypeUpdate_ObjectGroup::New(space, getStubCode(), group_); } }; }; @@ -3510,51 +3512,51 @@ friend class ICStubSpace; HeapPtrShape shape_; - HeapPtrTypeObject type_; + HeapPtrObjectGroup group_; - ICSetElem_Dense(JitCode *stubCode, HandleShape shape, HandleTypeObject type); + ICSetElem_Dense(JitCode *stubCode, HandleShape shape, HandleObjectGroup group); public: static inline ICSetElem_Dense *New(ICStubSpace *space, JitCode *code, HandleShape shape, - HandleTypeObject type) { + HandleObjectGroup group) { if (!code) return nullptr; - return space->allocate(code, shape, type); + return space->allocate(code, shape, group); } static size_t offsetOfShape() { return offsetof(ICSetElem_Dense, shape_); } - static size_t offsetOfType() { - return offsetof(ICSetElem_Dense, type_); + static size_t offsetOfGroup() { + return offsetof(ICSetElem_Dense, group_); } HeapPtrShape &shape() { return shape_; } - HeapPtrTypeObject &type() { - return type_; + HeapPtrObjectGroup &group() { + return group_; } class Compiler : public ICStubCompiler { RootedShape shape_; // Compiler is only live on stack during compilation, it should - // outlive any RootedTypeObject it's passed. So it can just + // outlive any RootedObjectGroup it's passed. So it can just // use the handle. - HandleTypeObject type_; + HandleObjectGroup group_; bool generateStubCode(MacroAssembler &masm); public: - Compiler(JSContext *cx, Shape *shape, HandleTypeObject type) + Compiler(JSContext *cx, Shape *shape, HandleObjectGroup group) : ICStubCompiler(cx, ICStub::SetElem_Dense), shape_(cx, shape), - type_(type) + group_(group) {} ICUpdatedStub *getStub(ICStubSpace *space) { - ICSetElem_Dense *stub = ICSetElem_Dense::New(space, getStubCode(), shape_, type_); + ICSetElem_Dense *stub = ICSetElem_Dense::New(space, getStubCode(), shape_, group_); if (!stub || !stub->initUpdatingChain(cx, space)) return nullptr; return stub; @@ -3572,17 +3574,17 @@ static const size_t MAX_PROTO_CHAIN_DEPTH = 4; protected: - HeapPtrTypeObject type_; + HeapPtrObjectGroup group_; - ICSetElem_DenseAdd(JitCode *stubCode, types::TypeObject *type, size_t protoChainDepth); + ICSetElem_DenseAdd(JitCode *stubCode, types::ObjectGroup *group, size_t protoChainDepth); public: - static size_t offsetOfType() { - return offsetof(ICSetElem_DenseAdd, type_); + static size_t offsetOfGroup() { + return offsetof(ICSetElem_DenseAdd, group_); } - HeapPtrTypeObject &type() { - return type_; + HeapPtrObjectGroup &group() { + return group_; } size_t protoChainDepth() const { MOZ_ASSERT(extra_ <= MAX_PROTO_CHAIN_DEPTH); @@ -3609,9 +3611,9 @@ static const size_t NumShapes = ProtoChainDepth + 1; mozilla::Array shapes_; - ICSetElem_DenseAddImpl(JitCode *stubCode, types::TypeObject *type, + ICSetElem_DenseAddImpl(JitCode *stubCode, types::ObjectGroup *group, const AutoShapeVector *shapes) - : ICSetElem_DenseAdd(stubCode, type, ProtoChainDepth) + : ICSetElem_DenseAdd(stubCode, group, ProtoChainDepth) { MOZ_ASSERT(shapes->length() == NumShapes); for (size_t i = 0; i < NumShapes; i++) @@ -3620,12 +3622,12 @@ public: static inline ICSetElem_DenseAddImpl *New(ICStubSpace *space, JitCode *code, - types::TypeObject *type, + types::ObjectGroup *group, const AutoShapeVector *shapes) { if (!code) return nullptr; - return space->allocate >(code, type, shapes); + return space->allocate >(code, group, shapes); } void traceShapes(JSTracer *trc) { @@ -4523,6 +4525,72 @@ ICStub *getStub(ICStubSpace *space); }; +class ICGetProp_Unboxed : public ICMonitoredStub +{ + friend class ICStubSpace; + + HeapPtrObjectGroup group_; + uint32_t fieldOffset_; + + ICGetProp_Unboxed(JitCode *stubCode, ICStub *firstMonitorStub, HandleObjectGroup group, + uint32_t fieldOffset) + : ICMonitoredStub(ICStub::GetProp_Unboxed, stubCode, firstMonitorStub), + group_(group), fieldOffset_(fieldOffset) + { + (void) fieldOffset_; // Silence clang warning + } + + public: + static inline ICGetProp_Unboxed *New(ICStubSpace *space, JitCode *code, + ICStub *firstMonitorStub, HandleObjectGroup group, + uint32_t fieldOffset) + { + if (!code) + return nullptr; + return space->allocate(code, firstMonitorStub, group, fieldOffset); + } + + HeapPtrObjectGroup &group() { + return group_; + } + + static size_t offsetOfGroup() { + return offsetof(ICGetProp_Unboxed, group_); + } + static size_t offsetOfFieldOffset() { + return offsetof(ICGetProp_Unboxed, fieldOffset_); + } + + class Compiler : public ICStubCompiler { + protected: + ICStub *firstMonitorStub_; + RootedObjectGroup group_; + uint32_t fieldOffset_; + JSValueType fieldType_; + + bool generateStubCode(MacroAssembler &masm); + + virtual int32_t getKey() const { + return static_cast(kind) | (static_cast(fieldType_)) << 16; + } + + public: + Compiler(JSContext *cx, ICStub *firstMonitorStub, + types::ObjectGroup *group, uint32_t fieldOffset, JSValueType fieldType) + : ICStubCompiler(cx, ICStub::GetProp_Unboxed), + firstMonitorStub_(firstMonitorStub), + group_(cx, group), + fieldOffset_(fieldOffset), + fieldType_(fieldType) + {} + + ICStub *getStub(ICStubSpace *space) { + return ICGetProp_Unboxed::New(space, getStubCode(), firstMonitorStub_, + group_, fieldOffset_); + } + }; +}; + static uint32_t SimpleTypeDescrKey(SimpleTypeDescr *descr) { @@ -5230,22 +5298,22 @@ friend class ICStubSpace; protected: // Protected to silence Clang warning. - HeapPtrTypeObject type_; + HeapPtrObjectGroup group_; HeapPtrShape shape_; uint32_t offset_; - ICSetProp_Native(JitCode *stubCode, HandleTypeObject type, HandleShape shape, uint32_t offset); + ICSetProp_Native(JitCode *stubCode, HandleObjectGroup group, HandleShape shape, uint32_t offset); public: - static inline ICSetProp_Native *New(ICStubSpace *space, JitCode *code, HandleTypeObject type, + static inline ICSetProp_Native *New(ICStubSpace *space, JitCode *code, HandleObjectGroup group, HandleShape shape, uint32_t offset) { if (!code) return nullptr; - return space->allocate(code, type, shape, offset); + return space->allocate(code, group, shape, offset); } - HeapPtrTypeObject &type() { - return type_; + HeapPtrObjectGroup &group() { + return group_; } HeapPtrShape &shape() { return shape_; @@ -5256,8 +5324,8 @@ bool hasPreliminaryObject() const { return extra_; } - static size_t offsetOfType() { - return offsetof(ICSetProp_Native, type_); + static size_t offsetOfGroup() { + return offsetof(ICSetProp_Native, group_); } static size_t offsetOfShape() { return offsetof(ICSetProp_Native, shape_); @@ -5299,26 +5367,26 @@ static const size_t MAX_PROTO_CHAIN_DEPTH = 4; protected: // Protected to silence Clang warning. - HeapPtrTypeObject type_; + HeapPtrObjectGroup group_; HeapPtrShape newShape_; - HeapPtrTypeObject newType_; + HeapPtrObjectGroup newGroup_; uint32_t offset_; - ICSetProp_NativeAdd(JitCode *stubCode, HandleTypeObject type, size_t protoChainDepth, - HandleShape newShape, HandleTypeObject newType, uint32_t offset); + ICSetProp_NativeAdd(JitCode *stubCode, HandleObjectGroup group, size_t protoChainDepth, + HandleShape newShape, HandleObjectGroup newGroup, uint32_t offset); public: size_t protoChainDepth() const { return extra_; } - HeapPtrTypeObject &type() { - return type_; + HeapPtrObjectGroup &group() { + return group_; } HeapPtrShape &newShape() { return newShape_; } - HeapPtrTypeObject &newType() { - return newType_; + HeapPtrObjectGroup &newGroup() { + return newGroup_; } template @@ -5327,14 +5395,14 @@ return static_cast *>(this); } - static size_t offsetOfType() { - return offsetof(ICSetProp_NativeAdd, type_); + static size_t offsetOfGroup() { + return offsetof(ICSetProp_NativeAdd, group_); } static size_t offsetOfNewShape() { return offsetof(ICSetProp_NativeAdd, newShape_); } - static size_t offsetOfNewType() { - return offsetof(ICSetProp_NativeAdd, newType_); + static size_t offsetOfNewGroup() { + return offsetof(ICSetProp_NativeAdd, newGroup_); } static size_t offsetOfOffset() { return offsetof(ICSetProp_NativeAdd, offset_); @@ -5349,20 +5417,20 @@ static const size_t NumShapes = ProtoChainDepth + 1; mozilla::Array shapes_; - ICSetProp_NativeAddImpl(JitCode *stubCode, HandleTypeObject type, + ICSetProp_NativeAddImpl(JitCode *stubCode, HandleObjectGroup group, const AutoShapeVector *shapes, - HandleShape newShape, HandleTypeObject newType, uint32_t offset); + HandleShape newShape, HandleObjectGroup newGroup, uint32_t offset); public: static inline ICSetProp_NativeAddImpl *New( - ICStubSpace *space, JitCode *code, HandleTypeObject type, + ICStubSpace *space, JitCode *code, HandleObjectGroup group, const AutoShapeVector *shapes, HandleShape newShape, - HandleTypeObject newType, uint32_t offset) + HandleObjectGroup newGroup, uint32_t offset) { if (!code) return nullptr; return space->allocate >( - code, type, shapes, newShape, newType, offset); + code, group, shapes, newShape, newGroup, offset); } void traceShapes(JSTracer *trc) { @@ -5379,7 +5447,7 @@ { RootedObject obj_; RootedShape oldShape_; - RootedTypeObject oldType_; + RootedObjectGroup oldGroup_; size_t protoChainDepth_; bool isFixedSlot_; uint32_t offset_; @@ -5394,45 +5462,116 @@ public: ICSetPropNativeAddCompiler(JSContext *cx, HandleObject obj, - HandleShape oldShape, HandleTypeObject oldType, + HandleShape oldShape, HandleObjectGroup oldGroup, size_t protoChainDepth, bool isFixedSlot, uint32_t offset); template ICUpdatedStub *getStubSpecific(ICStubSpace *space, const AutoShapeVector *shapes) { - RootedTypeObject newType(cx, obj_->getType(cx)); - if (!newType) + RootedObjectGroup newGroup(cx, obj_->getGroup(cx)); + if (!newGroup) return nullptr; - // Only specify newType when the object's type changes due to the + // Only specify newGroup when the object's group changes due to the // object becoming fully initialized per the acquired properties // analysis. - if (newType == oldType_) - newType = nullptr; + if (newGroup == oldGroup_) + newGroup = nullptr; RootedShape newShape(cx, obj_->lastProperty()); return ICSetProp_NativeAddImpl::New( - space, getStubCode(), oldType_, shapes, newShape, newType, offset_); + space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_); } ICUpdatedStub *getStub(ICStubSpace *space); }; +class ICSetProp_Unboxed : public ICUpdatedStub +{ + friend class ICStubSpace; + + HeapPtrObjectGroup group_; + uint32_t fieldOffset_; + + ICSetProp_Unboxed(JitCode *stubCode, HandleObjectGroup group, uint32_t fieldOffset) + : ICUpdatedStub(ICStub::SetProp_Unboxed, stubCode), + group_(group), + fieldOffset_(fieldOffset) + { + (void) fieldOffset_; // Silence clang warning + } + + public: + static inline ICSetProp_Unboxed *New(ICStubSpace *space, JitCode *code, + HandleObjectGroup group, uint32_t fieldOffset) + { + if (!code) + return nullptr; + return space->allocate(code, group, fieldOffset); + } + + HeapPtrObjectGroup &group() { + return group_; + } + + static size_t offsetOfGroup() { + return offsetof(ICSetProp_Unboxed, group_); + } + static size_t offsetOfFieldOffset() { + return offsetof(ICSetProp_Unboxed, fieldOffset_); + } + + class Compiler : public ICStubCompiler { + protected: + RootedObjectGroup group_; + uint32_t fieldOffset_; + JSValueType fieldType_; + + bool generateStubCode(MacroAssembler &masm); + + virtual int32_t getKey() const { + return static_cast(kind) | + (static_cast(fieldType_) << 16); + } + + public: + Compiler(JSContext *cx, types::ObjectGroup *group, uint32_t fieldOffset, + JSValueType fieldType) + : ICStubCompiler(cx, ICStub::SetProp_Unboxed), + group_(cx, group), + fieldOffset_(fieldOffset), + fieldType_(fieldType) + {} + + ICUpdatedStub *getStub(ICStubSpace *space) { + ICUpdatedStub *stub = ICSetProp_Unboxed::New(space, getStubCode(), + group_, fieldOffset_); + if (!stub || !stub->initUpdatingChain(cx, space)) + return nullptr; + return stub; + } + + bool needsUpdateStubs() { + return fieldType_ == JSVAL_TYPE_OBJECT; + } + }; +}; + class ICSetProp_TypedObject : public ICUpdatedStub { friend class ICStubSpace; HeapPtrShape shape_; - HeapPtrTypeObject type_; + HeapPtrObjectGroup group_; uint32_t fieldOffset_; bool isObjectReference_; - ICSetProp_TypedObject(JitCode *stubCode, HandleShape shape, HandleTypeObject type, + ICSetProp_TypedObject(JitCode *stubCode, HandleShape shape, HandleObjectGroup group, uint32_t fieldOffset, bool isObjectReference) : ICUpdatedStub(ICStub::SetProp_TypedObject, stubCode), shape_(shape), - type_(type), + group_(group), fieldOffset_(fieldOffset), isObjectReference_(isObjectReference) { @@ -5441,20 +5580,20 @@ public: static inline ICSetProp_TypedObject *New(ICStubSpace *space, JitCode *code, - HandleShape shape, HandleTypeObject type, + HandleShape shape, HandleObjectGroup group, uint32_t fieldOffset, bool isObjectReference) { if (!code) return nullptr; - return space->allocate(code, shape, type, + return space->allocate(code, shape, group, fieldOffset, isObjectReference); } HeapPtrShape &shape() { return shape_; } - HeapPtrTypeObject &type() { - return type_; + HeapPtrObjectGroup &group() { + return group_; } bool isObjectReference() { return isObjectReference_; @@ -5463,8 +5602,8 @@ static size_t offsetOfShape() { return offsetof(ICSetProp_TypedObject, shape_); } - static size_t offsetOfType() { - return offsetof(ICSetProp_TypedObject, type_); + static size_t offsetOfGroup() { + return offsetof(ICSetProp_TypedObject, group_); } static size_t offsetOfFieldOffset() { return offsetof(ICSetProp_TypedObject, fieldOffset_); @@ -5473,7 +5612,7 @@ class Compiler : public ICStubCompiler { protected: RootedShape shape_; - RootedTypeObject type_; + RootedObjectGroup group_; uint32_t fieldOffset_; TypedThingLayout layout_; Rooted fieldDescr_; @@ -5487,11 +5626,11 @@ } public: - Compiler(JSContext *cx, Shape *shape, types::TypeObject *type, uint32_t fieldOffset, + Compiler(JSContext *cx, Shape *shape, types::ObjectGroup *group, uint32_t fieldOffset, SimpleTypeDescr *fieldDescr) : ICStubCompiler(cx, ICStub::SetProp_TypedObject), shape_(cx, shape), - type_(cx, type), + group_(cx, group), fieldOffset_(fieldOffset), layout_(GetTypedThingLayout(shape->getObjectClass())), fieldDescr_(cx, fieldDescr) @@ -5501,7 +5640,7 @@ bool isObjectReference = fieldDescr_->is() && fieldDescr_->as().type() == ReferenceTypeDescr::TYPE_OBJECT; - ICUpdatedStub *stub = ICSetProp_TypedObject::New(space, getStubCode(), shape_, type_, + ICUpdatedStub *stub = ICSetProp_TypedObject::New(space, getStubCode(), shape_, group_, fieldOffset_, isObjectReference); if (!stub || !stub->initUpdatingChain(cx, space)) return nullptr; @@ -5799,17 +5938,17 @@ protected: HeapPtrScript calleeScript_; - HeapPtrNativeObject templateObject_; + HeapPtrObject templateObject_; uint32_t pcOffset_; ICCall_Scripted(JitCode *stubCode, ICStub *firstMonitorStub, - HandleScript calleeScript, HandleNativeObject templateObject, + HandleScript calleeScript, HandleObject templateObject, uint32_t pcOffset); public: static inline ICCall_Scripted *New( ICStubSpace *space, JitCode *code, ICStub *firstMonitorStub, - HandleScript calleeScript, HandleNativeObject templateObject, + HandleScript calleeScript, HandleObject templateObject, uint32_t pcOffset) { if (!code) @@ -5824,7 +5963,7 @@ HeapPtrScript &calleeScript() { return calleeScript_; } - HeapPtrNativeObject &templateObject() { + HeapPtrObject &templateObject() { return templateObject_; } @@ -5872,7 +6011,7 @@ bool isConstructing_; bool isSpread_; RootedScript calleeScript_; - RootedNativeObject templateObject_; + RootedObject templateObject_; uint32_t pcOffset_; bool generateStubCode(MacroAssembler &masm); @@ -5883,7 +6022,7 @@ public: ICCallScriptedCompiler(JSContext *cx, ICStub *firstMonitorStub, - HandleScript calleeScript, HandleNativeObject templateObject, + HandleScript calleeScript, HandleObject templateObject, bool isConstructing, bool isSpread, uint32_t pcOffset) : ICCallStubCompiler(cx, ICStub::Call_Scripted), firstMonitorStub_(firstMonitorStub), @@ -5921,7 +6060,7 @@ protected: HeapPtrFunction callee_; - HeapPtrNativeObject templateObject_; + HeapPtrObject templateObject_; uint32_t pcOffset_; #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) @@ -5929,12 +6068,12 @@ #endif ICCall_Native(JitCode *stubCode, ICStub *firstMonitorStub, - HandleFunction callee, HandleNativeObject templateObject, + HandleFunction callee, HandleObject templateObject, uint32_t pcOffset); public: static inline ICCall_Native *New(ICStubSpace *space, JitCode *code, ICStub *firstMonitorStub, - HandleFunction callee, HandleNativeObject templateObject, + HandleFunction callee, HandleObject templateObject, uint32_t pcOffset) { if (!code) @@ -5949,7 +6088,7 @@ HeapPtrFunction &callee() { return callee_; } - HeapPtrNativeObject &templateObject() { + HeapPtrObject &templateObject() { return templateObject_; } @@ -5973,7 +6112,7 @@ bool isConstructing_; bool isSpread_; RootedFunction callee_; - RootedNativeObject templateObject_; + RootedObject templateObject_; uint32_t pcOffset_; bool generateStubCode(MacroAssembler &masm); @@ -5984,7 +6123,7 @@ public: Compiler(JSContext *cx, ICStub *firstMonitorStub, - HandleFunction callee, HandleNativeObject templateObject, + HandleFunction callee, HandleObject templateObject, bool isConstructing, bool isSpread, uint32_t pcOffset) : ICCallStubCompiler(cx, ICStub::Call_Native), firstMonitorStub_(firstMonitorStub), diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/BaselineInspector.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/BaselineInspector.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/BaselineInspector.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/BaselineInspector.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -80,12 +80,15 @@ } bool -BaselineInspector::maybeShapesForPropertyOp(jsbytecode *pc, ShapeVector &shapes) -{ - // Return a list of shapes seen by the baseline IC for the current op. - // An empty list indicates no shapes are known, or there was an uncacheable - // access. - MOZ_ASSERT(shapes.empty()); +BaselineInspector::maybeInfoForPropertyOp(jsbytecode *pc, + ShapeVector &nativeShapes, + ObjectGroupVector &unboxedGroups) +{ + // Return lists of native shapes and unboxed objects seen by the baseline + // IC for the current op. Empty lists indicate no shapes/types are known, + // or there was an uncacheable access. + MOZ_ASSERT(nativeShapes.empty()); + MOZ_ASSERT(unboxedGroups.empty()); if (!hasBaselineScript()) return true; @@ -95,43 +98,66 @@ ICStub *stub = entry.firstStub(); while (stub->next()) { - Shape *shape; + Shape *shape = nullptr; + types::ObjectGroup *group = nullptr; if (stub->isGetProp_Native()) { shape = stub->toGetProp_Native()->shape(); } else if (stub->isSetProp_Native()) { shape = stub->toSetProp_Native()->shape(); + } else if (stub->isGetProp_Unboxed()) { + group = stub->toGetProp_Unboxed()->group(); + } else if (stub->isSetProp_Unboxed()) { + group = stub->toSetProp_Unboxed()->group(); } else { - shapes.clear(); + nativeShapes.clear(); + unboxedGroups.clear(); return true; } - // Don't add the same shape twice (this can happen if there are multiple - // SetProp_Native stubs with different TypeObject's). - bool found = false; - for (size_t i = 0; i < shapes.length(); i++) { - if (shapes[i] == shape) { - found = true; - break; + // Don't add the same shape/group twice (this can happen if there are + // multiple SetProp_Native stubs with different ObjectGroups). + if (shape) { + bool found = false; + for (size_t i = 0; i < nativeShapes.length(); i++) { + if (nativeShapes[i] == shape) { + found = true; + break; + } + } + if (!found && !nativeShapes.append(shape)) + return false; + } else { + bool found = false; + for (size_t i = 0; i < unboxedGroups.length(); i++) { + if (unboxedGroups[i] == group) { + found = true; + break; + } } + if (!found && !unboxedGroups.append(group)) + return false; } - if (!found && !shapes.append(shape)) - return false; - stub = stub->next(); } if (stub->isGetProp_Fallback()) { - if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) - shapes.clear(); + if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) { + nativeShapes.clear(); + unboxedGroups.clear(); + } } else { - if (stub->toSetProp_Fallback()->hadUnoptimizableAccess()) - shapes.clear(); + if (stub->toSetProp_Fallback()->hadUnoptimizableAccess()) { + nativeShapes.clear(); + unboxedGroups.clear(); + } } - // Don't inline if there are more than 5 shapes. - if (shapes.length() > 5) - shapes.clear(); + // Don't inline if there are more than 5 shapes/groups. + if (nativeShapes.length() + unboxedGroups.length() > 5) { + nativeShapes.clear(); + unboxedGroups.clear(); + } return true; } @@ -413,7 +439,7 @@ return false; } -NativeObject * +JSObject * BaselineInspector::getTemplateObject(jsbytecode *pc) { if (!hasBaselineScript()) @@ -429,7 +455,7 @@ case ICStub::Rest_Fallback: return stub->toRest_Fallback()->templateObject(); case ICStub::Call_Scripted: - if (NativeObject *obj = stub->toCall_Scripted()->templateObject()) + if (JSObject *obj = stub->toCall_Scripted()->templateObject()) return obj; break; default: @@ -440,7 +466,7 @@ return nullptr; } -NativeObject * +JSObject * BaselineInspector::getTemplateObjectForNative(jsbytecode *pc, Native native) { if (!hasBaselineScript()) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/BaselineInspector.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/BaselineInspector.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/BaselineInspector.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/BaselineInspector.h 2015-02-03 14:33:31.000000000 +0000 @@ -93,7 +93,10 @@ public: typedef Vector ShapeVector; - bool maybeShapesForPropertyOp(jsbytecode *pc, ShapeVector &shapes); + typedef Vector ObjectGroupVector; + bool maybeInfoForPropertyOp(jsbytecode *pc, + ShapeVector &nativeShapes, + ObjectGroupVector &unboxedGroups); SetElemICInspector setElemICInspector(jsbytecode *pc) { return makeICInspector(pc, ICStub::SetElem_Fallback); @@ -109,8 +112,8 @@ bool hasSeenDoubleResult(jsbytecode *pc); bool hasSeenNonStringIterMore(jsbytecode *pc); - NativeObject *getTemplateObject(jsbytecode *pc); - NativeObject *getTemplateObjectForNative(jsbytecode *pc, Native native); + JSObject *getTemplateObject(jsbytecode *pc); + JSObject *getTemplateObjectForNative(jsbytecode *pc, Native native); JSObject *getTemplateObjectForClassHook(jsbytecode *pc, const Class *clasp); DeclEnvObject *templateDeclEnvObject(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/CodeGenerator.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/CodeGenerator.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/CodeGenerator.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/CodeGenerator.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -723,9 +723,9 @@ for (size_t i = 0; i < casesWithFallback - 1; i++) { MOZ_ASSERT(i < mir->numCases()); LBlock *target = skipTrivialBlocks(mir->getCaseBlock(i))->lir(); - if (types::TypeObject *funcType = mir->getCaseTypeObject(i)) { - masm.branchPtr(Assembler::Equal, Address(input, JSObject::offsetOfType()), - ImmGCPtr(funcType), target->label()); + if (types::ObjectGroup *funcGroup = mir->getCaseObjectGroup(i)) { + masm.branchPtr(Assembler::Equal, Address(input, JSObject::offsetOfGroup()), + ImmGCPtr(funcGroup), target->label()); } else { JSFunction *func = mir->getCase(i); masm.branchPtr(Assembler::Equal, input, ImmGCPtr(func), target->label()); @@ -737,17 +737,17 @@ } void -CodeGenerator::visitTypeObjectDispatch(LTypeObjectDispatch *lir) +CodeGenerator::visitObjectGroupDispatch(LObjectGroupDispatch *lir) { - MTypeObjectDispatch *mir = lir->mir(); + MObjectGroupDispatch *mir = lir->mir(); Register input = ToRegister(lir->input()); Register temp = ToRegister(lir->temp()); - // Hold the incoming TypeObject. + // Hold the incoming ObjectGroup. - masm.loadPtr(Address(input, JSObject::offsetOfType()), temp); + masm.loadPtr(Address(input, JSObject::offsetOfGroup()), temp); - // Compare TypeObjects. + // Compare ObjectGroups. MacroAssembler::BranchGCPtr lastBranch; LBlock *lastBlock = nullptr; @@ -764,8 +764,8 @@ if (lastBranch.isInitialized()) lastBranch.emit(masm); - types::TypeObject *typeObj = propTable->getTypeObject(j); - lastBranch = MacroAssembler::BranchGCPtr(Assembler::Equal, temp, ImmGCPtr(typeObj), + types::ObjectGroup *group = propTable->getObjectGroup(j); + lastBranch = MacroAssembler::BranchGCPtr(Assembler::Equal, temp, ImmGCPtr(group), target->label()); lastBlock = target; found = true; @@ -1704,7 +1704,7 @@ (ArgList(), ImmGCPtr(info.fun), scopeChain, thisv), StoreRegisterTo(output)); - MOZ_ASSERT(!info.useNewTypeForClone); + MOZ_ASSERT(!info.useSingletonForClone); if (info.singletonType) { // If the function has a singleton type, this instruction will only be @@ -2133,6 +2133,8 @@ masm.propagateOOM(resolver.resolve()); MoveEmitter emitter(masm); + if (group->maybeScratchRegister().isGeneralReg()) + emitter.setScratchRegister(group->maybeScratchRegister().toGeneralReg()->reg()); emitter.emit(resolver); emitter.finish(); } @@ -2153,6 +2155,19 @@ } void +CodeGenerator::visitNurseryObject(LNurseryObject *lir) +{ + Register output = ToRegister(lir->output()); + uint32_t index = lir->mir()->index(); + + // Store a dummy JSObject pointer. We will fix it up on the main thread, + // in JitCode::fixupNurseryObjects. The low bit is set to distinguish + // it from a real JSObject pointer. + JSObject *ptr = reinterpret_cast((uintptr_t(index) << 1) | 1); + masm.movePtr(ImmGCPtr(IonNurseryPtr(ptr)), output); +} + +void CodeGenerator::visitSlots(LSlots *lir) { Address slots(ToRegister(lir->object()), NativeObject::offsetOfSlots()); @@ -2218,33 +2233,62 @@ const TypedOrValueRegister &output) { MGetPropertyPolymorphic *mir = ins->mirRaw()->toGetPropertyPolymorphic(); - MOZ_ASSERT(mir->numShapes() > 1); - masm.loadObjShape(obj, scratch); + size_t total = mir->numUnboxedGroups() + mir->numShapes(); + MOZ_ASSERT(total > 1); + + bool groupInScratch = mir->numUnboxedGroups() > 1; + bool shapeInScratch = mir->numShapes() > 1; Label done; - for (size_t i = 0; i < mir->numShapes(); i++) { + + for (size_t i = 0; i < total; i++) { + bool unboxedGroup = i < mir->numUnboxedGroups(); + + ImmGCPtr comparePtr = unboxedGroup + ? ImmGCPtr(mir->unboxedGroup(i)) + : ImmGCPtr(mir->objShape(i - mir->numUnboxedGroups())); + Address addr(obj, unboxedGroup ? JSObject::offsetOfGroup() : JSObject::offsetOfShape()); + + if ((i == 0 && groupInScratch) || (i == mir->numUnboxedGroups() && shapeInScratch)) + masm.loadPtr(addr, scratch); + + bool inScratch = unboxedGroup ? groupInScratch : shapeInScratch; + Label next; - if (i == mir->numShapes() - 1) { - bailoutCmpPtr(Assembler::NotEqual, scratch, ImmGCPtr(mir->objShape(i)), - ins->snapshot()); + if (i == total - 1) { + if (inScratch) + bailoutCmpPtr(Assembler::NotEqual, scratch, comparePtr, ins->snapshot()); + else + bailoutCmpPtr(Assembler::NotEqual, addr, comparePtr, ins->snapshot()); } else { - masm.branchPtr(Assembler::NotEqual, scratch, ImmGCPtr(mir->objShape(i)), &next); + if (inScratch) + masm.branchPtr(Assembler::NotEqual, scratch, comparePtr, &next); + else + masm.branchPtr(Assembler::NotEqual, addr, comparePtr, &next); } - Shape *shape = mir->shape(i); - if (shape->slot() < shape->numFixedSlots()) { - // Fixed slot. - masm.loadTypedOrValue(Address(obj, NativeObject::getFixedSlotOffset(shape->slot())), - output); + if (unboxedGroup) { + const UnboxedLayout::Property *property = + mir->unboxedGroup(i)->unboxedLayout().lookup(mir->name()); + Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset); + + masm.loadUnboxedProperty(propertyAddr, property->type, output); } else { - // Dynamic slot. - uint32_t offset = (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value); - masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch); - masm.loadTypedOrValue(Address(scratch, offset), output); + Shape *shape = mir->shape(i - mir->numUnboxedGroups()); + if (shape->slot() < shape->numFixedSlots()) { + // Fixed slot. + masm.loadTypedOrValue(Address(obj, NativeObject::getFixedSlotOffset(shape->slot())), + output); + } else { + // Dynamic slot. + uint32_t offset = (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value); + masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch); + masm.loadTypedOrValue(Address(scratch, offset), output); + } } - if (i != mir->numShapes() - 1) + if (i != total - 1) masm.jump(&done); masm.bind(&next); } @@ -2276,37 +2320,72 @@ const ConstantOrRegister &value) { MSetPropertyPolymorphic *mir = ins->mirRaw()->toSetPropertyPolymorphic(); - MOZ_ASSERT(mir->numShapes() > 1); - masm.loadObjShape(obj, scratch); + size_t total = mir->numUnboxedGroups() + mir->numShapes(); + MOZ_ASSERT(total > 1); + + bool groupInScratch = mir->numUnboxedGroups() > 1; + bool shapeInScratch = mir->numShapes() > 1; Label done; - for (size_t i = 0; i < mir->numShapes(); i++) { + for (size_t i = 0; i < total; i++) { + bool unboxedGroup = i < mir->numUnboxedGroups(); + + ImmGCPtr comparePtr = unboxedGroup + ? ImmGCPtr(mir->unboxedGroup(i)) + : ImmGCPtr(mir->objShape(i - mir->numUnboxedGroups())); + Address addr(obj, unboxedGroup ? JSObject::offsetOfGroup() : JSObject::offsetOfShape()); + + if ((i == 0 && groupInScratch) || (i == mir->numUnboxedGroups() && shapeInScratch)) + masm.loadPtr(addr, scratch); + + bool inScratch = unboxedGroup ? groupInScratch : shapeInScratch; + Label next; - if (i == mir->numShapes() - 1) { - bailoutCmpPtr(Assembler::NotEqual, scratch, ImmGCPtr(mir->objShape(i)), - ins->snapshot()); + if (i == total - 1) { + if (inScratch) + bailoutCmpPtr(Assembler::NotEqual, scratch, comparePtr, ins->snapshot()); + else + bailoutCmpPtr(Assembler::NotEqual, addr, comparePtr, ins->snapshot()); } else { - masm.branchPtr(Assembler::NotEqual, scratch, ImmGCPtr(mir->objShape(i)), &next); + if (inScratch) + masm.branchPtr(Assembler::NotEqual, scratch, comparePtr, &next); + else + masm.branchPtr(Assembler::NotEqual, addr, comparePtr, &next); } - Shape *shape = mir->shape(i); - if (shape->slot() < shape->numFixedSlots()) { - // Fixed slot. - Address addr(obj, NativeObject::getFixedSlotOffset(shape->slot())); - if (mir->needsBarrier()) - emitPreBarrier(addr); - masm.storeConstantOrRegister(value, addr); + if (unboxedGroup) { + const UnboxedLayout::Property *property = + mir->unboxedGroup(i)->unboxedLayout().lookup(mir->name()); + Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset); + + if (property->type == JSVAL_TYPE_OBJECT) + masm.patchableCallPreBarrier(propertyAddr, MIRType_Object); + else if (property->type == JSVAL_TYPE_STRING) + masm.patchableCallPreBarrier(propertyAddr, MIRType_String); + else + MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(property->type)); + + masm.storeUnboxedProperty(propertyAddr, property->type, value, nullptr); } else { - // Dynamic slot. - masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch); - Address addr(scratch, (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value)); - if (mir->needsBarrier()) - emitPreBarrier(addr); - masm.storeConstantOrRegister(value, addr); + Shape *shape = mir->shape(i - mir->numUnboxedGroups()); + if (shape->slot() < shape->numFixedSlots()) { + // Fixed slot. + Address addr(obj, NativeObject::getFixedSlotOffset(shape->slot())); + if (mir->needsBarrier()) + emitPreBarrier(addr); + masm.storeConstantOrRegister(value, addr); + } else { + // Dynamic slot. + masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch); + Address addr(scratch, (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value)); + if (mir->needsBarrier()) + emitPreBarrier(addr); + masm.storeConstantOrRegister(value, addr); + } } - if (i != mir->numShapes() - 1) + if (i != total - 1) masm.jump(&done); masm.bind(&next); } @@ -3075,25 +3154,17 @@ masm.checkStackAlignment(); - // If the function is known to be uncompilable, only emit the call to InvokeFunction. - if (apply->hasSingleTarget()) { - JSFunction *target = apply->getSingleTarget(); - if (target->isNative()) { - emitCallInvokeFunction(apply, copyreg); - emitPopArguments(apply, copyreg); - return; - } + // If the function is native, only emit the call to InvokeFunction. + if (apply->hasSingleTarget() && apply->getSingleTarget()->isNative()) { + emitCallInvokeFunction(apply, copyreg); + emitPopArguments(apply, copyreg); + return; } Label end, invoke; - // Guard that calleereg is an interpreted function with a JSScript: - if (!apply->hasSingleTarget()) { - masm.branchIfFunctionHasNoScript(calleereg, &invoke); - } else { - // Native single targets are handled by LCallNative. - MOZ_ASSERT(!apply->getSingleTarget()->isNative()); - } + // Guard that calleereg is an interpreted function with a JSScript. + masm.branchIfFunctionHasNoScript(calleereg, &invoke); // Knowing that calleereg is a non-native function, load the JSScript. masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg); @@ -3605,11 +3676,11 @@ masm.bind(&miss); - // Type set guards might miss when an object's type changes and its + // Type set guards might miss when an object's group changes and its // properties become unknown, so check for this case. - masm.loadPtr(Address(output, JSObject::offsetOfType()), temp); + masm.loadPtr(Address(output, JSObject::offsetOfGroup()), temp); masm.branchTestPtr(Assembler::NonZero, - Address(temp, types::TypeObject::offsetOfFlags()), + Address(temp, types::ObjectGroup::offsetOfFlags()), Imm32(types::OBJECT_FLAG_UNKNOWN_PROPERTIES), &ok); masm.assumeUnreachable("MIR instruction returned object with unexpected type"); @@ -3682,14 +3753,14 @@ masm.bind(&miss); - // Type set guards might miss when an object's type changes and its + // Type set guards might miss when an object's group changes and its // properties become unknown, so check for this case. Label realMiss; masm.branchTestObject(Assembler::NotEqual, output, &realMiss); Register payload = masm.extractObject(output, temp1); - masm.loadPtr(Address(payload, JSObject::offsetOfType()), temp1); + masm.loadPtr(Address(payload, JSObject::offsetOfGroup()), temp1); masm.branchTestPtr(Assembler::NonZero, - Address(temp1, types::TypeObject::offsetOfFlags()), + Address(temp1, types::ObjectGroup::offsetOfFlags()), Imm32(types::OBJECT_FLAG_UNKNOWN_PROPERTIES), &ok); masm.bind(&realMiss); @@ -3814,10 +3885,20 @@ if (!addNativeToBytecodeEntry(iter->mirRaw()->trackedSite())) return false; } + + // Track the start native offset of optimizations. + if (iter->mirRaw()->trackedOptimizations()) { + if (!addTrackedOptimizationsEntry(iter->mirRaw()->trackedOptimizations())) + return false; + } } iter->accept(this); + // Track the end native offset of optimizations. + if (iter->mirRaw() && iter->mirRaw()->trackedOptimizations()) + extendTrackedOptimizationsEntry(iter->mirRaw()->trackedOptimizations()); + #ifdef DEBUG if (!counts) emitDebugResultChecks(*iter); @@ -3853,7 +3934,7 @@ } }; -typedef ArrayObject *(*NewDenseArrayFn)(ExclusiveContext *, uint32_t, HandleTypeObject, +typedef ArrayObject *(*NewDenseArrayFn)(ExclusiveContext *, uint32_t, HandleObjectGroup, AllocatingBehaviour); static const VMFunction NewDenseArrayInfo = FunctionInfo(NewDenseArray); @@ -3866,11 +3947,11 @@ saveLive(lir); JSObject *templateObject = lir->mir()->templateObject(); - types::TypeObject *type = - templateObject->hasSingletonType() ? nullptr : templateObject->type(); + types::ObjectGroup *group = + templateObject->isSingleton() ? nullptr : templateObject->group(); pushArg(Imm32(lir->mir()->allocatingBehaviour())); - pushArg(ImmGCPtr(type)); + pushArg(ImmGCPtr(group)); pushArg(Imm32(lir->mir()->count())); callVM(NewDenseArrayInfo, lir); @@ -3916,14 +3997,25 @@ CodeGenerator::visitHypot(LHypot *lir) { Register temp = ToRegister(lir->temp()); - FloatRegister x = ToFloatRegister(lir->x()); - FloatRegister y = ToFloatRegister(lir->y()); + uint32_t numArgs = lir->numArgs(); + masm.setupUnalignedABICall(numArgs, temp); - masm.setupUnalignedABICall(2, temp); - masm.passABIArg(x, MoveOp::DOUBLE); - masm.passABIArg(y, MoveOp::DOUBLE); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaHypot), MoveOp::DOUBLE); + for (uint32_t i = 0 ; i < numArgs; ++i) + masm.passABIArg(ToFloatRegister(lir->getOperand(i)), MoveOp::DOUBLE); + switch(numArgs) { + case 2: + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaHypot), MoveOp::DOUBLE); + break; + case 3: + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, hypot3), MoveOp::DOUBLE); + break; + case 4: + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, hypot4), MoveOp::DOUBLE); + break; + default: + MOZ_CRASH("Unexpected number of arguments to hypot function."); + } MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg); } @@ -3975,7 +4067,7 @@ masm.bind(ool->rejoin()); } -typedef ArrayObject *(*ArrayConstructorOneArgFn)(JSContext *, HandleTypeObject, int32_t length); +typedef ArrayObject *(*ArrayConstructorOneArgFn)(JSContext *, HandleObjectGroup, int32_t length); static const VMFunction ArrayConstructorOneArgInfo = FunctionInfo(ArrayConstructorOneArg); @@ -3990,7 +4082,7 @@ gc::InitialHeap initialHeap = lir->mir()->initialHeap(); OutOfLineCode *ool = oolCallVM(ArrayConstructorOneArgInfo, lir, - (ArgList(), ImmGCPtr(templateObject->type()), lengthReg), + (ArgList(), ImmGCPtr(templateObject->group()), lengthReg), StoreRegisterTo(objReg)); size_t numSlots = gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()); @@ -4003,7 +4095,7 @@ // use the template object and not allocate the elements, but it's more // efficient to do a single big allocation than (repeatedly) reallocating // the array later on when filling it. - if (!templateObject->hasSingletonType() && templateObject->length() <= inlineLength) + if (!templateObject->isSingleton() && templateObject->length() <= inlineLength) masm.branch32(Assembler::Above, lengthReg, Imm32(templateObject->length()), ool->entry()); else masm.jump(ool->entry()); @@ -4233,6 +4325,74 @@ } } +void +CodeGenerator::visitSimdUnbox(LSimdUnbox *lir) +{ + Register object = ToRegister(lir->input()); + FloatRegister simd = ToFloatRegister(lir->output()); + Register temp = ToRegister(lir->temp()); + Label bail; + + // obj->group() + masm.loadPtr(Address(object, JSObject::offsetOfGroup()), temp); + + // Guard that the object has the same representation as the one produced for + // SIMD value-type. + Address clasp(temp, types::ObjectGroup::offsetOfClasp()); + static_assert(!SimdTypeDescr::Opaque, "SIMD objects are transparent"); + masm.branchPtr(Assembler::NotEqual, clasp, ImmPtr(&InlineTransparentTypedObject::class_), + &bail); + + // obj->type()->typeDescr() + // The previous class pointer comparison implies that the addendumKind is + // Addendum_TypeDescr. + masm.loadPtr(Address(temp, types::ObjectGroup::offsetOfAddendum()), temp); + + // Check for the /Kind/ reserved slot of the TypeDescr. This is an Int32 + // Value which is equivalent to the object class check. + static_assert(JS_DESCR_SLOT_KIND < NativeObject::MAX_FIXED_SLOTS, "Load from fixed slots"); + Address typeDescrKind(temp, NativeObject::getFixedSlotOffset(JS_DESCR_SLOT_KIND)); + masm.assertTestInt32(Assembler::Equal, typeDescrKind, + "MOZ_ASSERT(obj->type()->typeDescr()->getReservedSlot(JS_DESCR_SLOT_KIND).isInt32())"); + masm.branch32(Assembler::NotEqual, masm.ToPayload(typeDescrKind), Imm32(js::type::Simd), &bail); + + // Convert the SIMD MIRType to a SimdTypeDescr::Type. + js::SimdTypeDescr::Type type; + switch (lir->mir()->type()) { + case MIRType_Int32x4: + type = js::SimdTypeDescr::TYPE_INT32; + break; + case MIRType_Float32x4: + type = js::SimdTypeDescr::TYPE_FLOAT32; + break; + default: + MOZ_CRASH("Unexpected SIMD Type."); + } + + // Check if the SimdTypeDescr /Type/ match the specialization of this + // MSimdUnbox instruction. + static_assert(JS_DESCR_SLOT_TYPE < NativeObject::MAX_FIXED_SLOTS, "Load from fixed slots"); + Address typeDescrType(temp, NativeObject::getFixedSlotOffset(JS_DESCR_SLOT_TYPE)); + masm.assertTestInt32(Assembler::Equal, typeDescrType, + "MOZ_ASSERT(obj->type()->typeDescr()->getReservedSlot(JS_DESCR_SLOT_TYPE).isInt32())"); + masm.branch32(Assembler::NotEqual, masm.ToPayload(typeDescrType), Imm32(type), &bail); + + // Load the value from the data of the InlineTypedObject. + Address objectData(object, InlineTypedObject::offsetOfDataStart()); + switch (lir->mir()->type()) { + case MIRType_Int32x4: + masm.loadUnalignedInt32x4(objectData, simd); + break; + case MIRType_Float32x4: + masm.loadUnalignedFloat32x4(objectData, simd); + break; + default: + MOZ_CRASH("The impossible happened!"); + } + + bailoutFrom(&bail, lir->snapshot()); +} + typedef js::DeclEnvObject *(*NewDeclEnvObjectFn)(JSContext *, HandleFunction, gc::InitialHeap); static const VMFunction NewDeclEnvObjectInfo = FunctionInfo(DeclEnvObject::createTemplateObject); @@ -4258,7 +4418,7 @@ masm.bind(ool->rejoin()); } -typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleShape, HandleTypeObject, uint32_t); +typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleShape, HandleObjectGroup, uint32_t); static const VMFunction NewCallObjectInfo = FunctionInfo(NewCallObject); @@ -4274,7 +4434,7 @@ uint32_t lexicalBegin = script->bindings.aliasedBodyLevelLexicalBegin(); OutOfLineCode *ool = oolCallVM(NewCallObjectInfo, lir, (ArgList(), ImmGCPtr(templateObj->lastProperty()), - ImmGCPtr(templateObj->type()), + ImmGCPtr(templateObj->group()), Imm32(lexicalBegin)), StoreRegisterTo(objReg)); @@ -4477,10 +4637,10 @@ void CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate *lir) { - PlainObject *templateObject = lir->mir()->templateObject(); + JSObject *templateObject = lir->mir()->templateObject(); gc::AllocKind allocKind = templateObject->asTenured().getAllocKind(); gc::InitialHeap initialHeap = lir->mir()->initialHeap(); - const js::Class *clasp = templateObject->type()->clasp(); + const js::Class *clasp = templateObject->getClass(); Register objReg = ToRegister(lir->output()); Register tempReg = ToRegister(lir->temp()); @@ -4495,7 +4655,8 @@ // Initialize based on the templateObject. masm.bind(ool->rejoin()); - bool initFixedSlots = ShouldInitFixedSlots(lir, templateObject); + bool initFixedSlots = !templateObject->is() || + ShouldInitFixedSlots(lir, &templateObject->as()); masm.initGCThing(objReg, tempReg, templateObject, initFixedSlots); } @@ -4648,8 +4809,8 @@ Register obj = ToRegister(lir->object()); Register out = ToRegister(lir->output()); - masm.loadPtr(Address(obj, JSObject::offsetOfType()), out); - masm.loadPtr(Address(out, types::TypeObject::offsetOfAddendum()), out); + masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), out); + masm.loadPtr(Address(out, types::ObjectGroup::offsetOfAddendum()), out); } void @@ -5872,7 +6033,7 @@ masm.bind(ool->rejoin()); } -typedef JSObject *(*StringSplitFn)(JSContext *, HandleTypeObject, HandleString, HandleString); +typedef JSObject *(*StringSplitFn)(JSContext *, HandleObjectGroup, HandleString, HandleString); static const VMFunction StringSplitInfo = FunctionInfo(js::str_split_string); void @@ -5880,7 +6041,7 @@ { pushArg(ToRegister(lir->separator())); pushArg(ToRegister(lir->string())); - pushArg(ImmGCPtr(lir->mir()->typeObject())); + pushArg(ImmGCPtr(lir->mir()->group())); callVM(StringSplitInfo, lir); } @@ -7164,6 +7325,28 @@ // nativeToBytecodeScriptList_ is no longer needed. js_free(nativeToBytecodeScriptList_); + // Generate the tracked optimizations map. + if (isOptimizationTrackingEnabled()) { + // Treat OOMs and failures as if optimization tracking were turned off. + types::TypeSet::TypeList *allTypes = cx->new_(); + if (allTypes && generateCompactTrackedOptimizationsMap(cx, code, allTypes)) { + const uint8_t *optsRegionTableAddr = trackedOptimizationsMap_ + + trackedOptimizationsRegionTableOffset_; + const IonTrackedOptimizationsRegionTable *optsRegionTable = + (const IonTrackedOptimizationsRegionTable *) optsRegionTableAddr; + const uint8_t *optsTypesTableAddr = trackedOptimizationsMap_ + + trackedOptimizationsTypesTableOffset_; + const IonTrackedOptimizationsTypesTable *optsTypesTable = + (const IonTrackedOptimizationsTypesTable *) optsTypesTableAddr; + const uint8_t *optsAttemptsTableAddr = trackedOptimizationsMap_ + + trackedOptimizationsAttemptsTableOffset_; + const IonTrackedOptimizationsAttemptsTable *optsAttemptsTable = + (const IonTrackedOptimizationsAttemptsTable *) optsAttemptsTableAddr; + entry.initTrackedOptimizations(optsRegionTable, optsTypesTable, optsAttemptsTable, + allTypes); + } + } + // Add entry to the global table. JitcodeGlobalTable *globalTable = cx->runtime()->jitRuntime()->getJitcodeGlobalTable(); if (!globalTable->addEntry(entry, cx->runtime())) { @@ -7278,6 +7461,9 @@ } #endif + // Replace dummy JSObject pointers embedded by LNurseryObject. + code->fixupNurseryObjects(cx, gen->nurseryObjects()); + // The correct state for prebarriers is unknown until the end of compilation, // since a GC can occur during code generation. All barriers are emitted // off-by-default, and are toggled on here if necessary. @@ -8252,8 +8438,8 @@ bool bailOnNull; int32_t offsetAdjustment; if (lir->mir()->isLoadUnboxedObjectOrNull()) { - MOZ_ASSERT(lir->mir()->toLoadUnboxedObjectOrNull()->bailOnNull()); - bailOnNull = true; + bailOnNull = lir->mir()->toLoadUnboxedObjectOrNull()->nullBehavior() != + MLoadUnboxedObjectOrNull::NullNotPossible; offsetAdjustment = lir->mir()->toLoadUnboxedObjectOrNull()->offsetAdjustment(); } else if (lir->mir()->isLoadUnboxedString()) { bailOnNull = false; @@ -8290,11 +8476,13 @@ Label fail; if (lir->index()->isConstant()) { Address source(elements, ToInt32(lir->index()) * width + lir->mir()->offsetAdjustment()); - masm.loadFromTypedArray(arrayType, source, out, temp, &fail); + masm.loadFromTypedArray(arrayType, source, out, temp, &fail, + lir->mir()->canonicalizeDoubles()); } else { BaseIndex source(elements, ToRegister(lir->index()), ScaleFromElemWidth(width), lir->mir()->offsetAdjustment()); - masm.loadFromTypedArray(arrayType, source, out, temp, &fail); + masm.loadFromTypedArray(arrayType, source, out, temp, &fail, + lir->mir()->canonicalizeDoubles()); } if (fail.used()) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/CodeGenerator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/CodeGenerator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/CodeGenerator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/CodeGenerator.h 2015-02-03 14:33:31.000000000 +0000 @@ -92,7 +92,7 @@ void visitTestOAndBranch(LTestOAndBranch *lir); void visitTestVAndBranch(LTestVAndBranch *lir); void visitFunctionDispatch(LFunctionDispatch *lir); - void visitTypeObjectDispatch(LTypeObjectDispatch *lir); + void visitObjectGroupDispatch(LObjectGroupDispatch *lir); void visitBooleanToString(LBooleanToString *lir); void emitIntToString(Register input, Register output, Label *ool); void visitIntToString(LIntToString *lir); @@ -111,6 +111,7 @@ void visitLambdaArrow(LLambdaArrow *lir); void visitLambdaForSingleton(LLambdaForSingleton *lir); void visitPointer(LPointer *lir); + void visitNurseryObject(LNurseryObject *lir); void visitSlots(LSlots *lir); void visitLoadSlotT(LLoadSlotT *lir); void visitLoadSlotV(LLoadSlotV *lir); @@ -156,6 +157,7 @@ void visitOutOfLineNewObject(OutOfLineNewObject *ool); void visitNewTypedObject(LNewTypedObject *lir); void visitSimdBox(LSimdBox *lir); + void visitSimdUnbox(LSimdUnbox *lir); void visitNewDeclEnvObject(LNewDeclEnvObject *lir); void visitNewCallObject(LNewCallObject *lir); void visitNewSingletonCallObject(LNewSingletonCallObject *lir); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/CompileInfo.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/CompileInfo.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/CompileInfo.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/CompileInfo.h 2015-02-03 14:33:31.000000000 +0000 @@ -16,6 +16,8 @@ namespace js { namespace jit { +class TrackedOptimizations; + inline unsigned StartArgSlot(JSScript *script) { @@ -130,13 +132,16 @@ // Bytecode address within innermost active function. jsbytecode *pc_; + // Optimization information at the pc. + TrackedOptimizations *optimizations_; + public: BytecodeSite() - : tree_(nullptr), pc_(nullptr) + : tree_(nullptr), pc_(nullptr), optimizations_(nullptr) {} BytecodeSite(InlineScriptTree *tree, jsbytecode *pc) - : tree_(tree), pc_(pc) + : tree_(tree), pc_(pc), optimizations_(nullptr) { MOZ_ASSERT(tree_ != nullptr); MOZ_ASSERT(pc_ != nullptr); @@ -153,6 +158,19 @@ JSScript *script() const { return tree_ ? tree_->script() : nullptr; } + + bool hasOptimizations() const { + return !!optimizations_; + } + + TrackedOptimizations *optimizations() const { + MOZ_ASSERT(hasOptimizations()); + return optimizations_; + } + + void setOptimizations(TrackedOptimizations *optimizations) { + optimizations_ = optimizations; + } }; enum AnalysisMode { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/CompileWrappers.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/CompileWrappers.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/CompileWrappers.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/CompileWrappers.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -37,37 +37,37 @@ const void * CompileRuntime::addressOfJitTop() { - return &runtime()->mainThread.jitTop; + return &runtime()->jitTop; } const void * CompileRuntime::addressOfJitActivation() { - return &runtime()->mainThread.jitActivation; + return &runtime()->jitActivation; } const void * CompileRuntime::addressOfProfilingActivation() { - return (const void *) &runtime()->mainThread.profilingActivation_; + return (const void *) &runtime()->profilingActivation_; } const void * CompileRuntime::addressOfJitStackLimit() { - return runtime()->mainThread.addressOfJitStackLimit(); + return runtime()->addressOfJitStackLimit(); } const void * CompileRuntime::addressOfJSContext() { - return &runtime()->mainThread.jitJSContext; + return &runtime()->jitJSContext; } const void * CompileRuntime::addressOfActivation() { - return runtime()->mainThread.addressOfActivation(); + return runtime()->addressOfActivation(); } const void * diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/CompileWrappers.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/CompileWrappers.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/CompileWrappers.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/CompileWrappers.h 2015-02-03 14:33:31.000000000 +0000 @@ -31,22 +31,22 @@ js::PerThreadData *mainThread(); - // &mainThread.jitTop + // &runtime()->jitTop const void *addressOfJitTop(); - // &mainThread.jitActivation + // &runtime()->jitActivation const void *addressOfJitActivation(); - // &mainThread.profilingActivation + // &runtime()->profilingActivation const void *addressOfProfilingActivation(); - // rt->mainThread.jitStackLimit; + // rt->runtime()->jitStackLimit; const void *addressOfJitStackLimit(); - // &mainThread.jitJSContext + // &runtime()->jitJSContext const void *addressOfJSContext(); - // &mainThread.activation_ + // &runtime()->activation_ const void *addressOfActivation(); // &GetJitContext()->runtime->nativeIterCache.last diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Disassembler.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Disassembler.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Disassembler.cpp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Disassembler.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 "jit/Disassembler.h" + +using namespace js; +using namespace js::jit; +using namespace js::jit::Disassembler; + +#ifdef DEBUG +bool +Disassembler::ComplexAddress::operator==(const ComplexAddress &other) const +{ + return base_ == other.base_ && + index_ == other.index_ && + scale_ == other.scale_ && + disp_ == other.disp_ && + isPCRelative_ == other.isPCRelative_; +} + +bool +Disassembler::ComplexAddress::operator!=(const ComplexAddress &other) const +{ + return !operator==(other); +} + +bool +Disassembler::OtherOperand::operator==(const OtherOperand &other) const +{ + if (kind_ != other.kind_) + return false; + switch (kind_) { + case Imm: return u_.imm == other.u_.imm; + case GPR: return u_.gpr == other.u_.gpr; + case FPR: return u_.fpr == other.u_.fpr; + } + MOZ_CRASH("Unexpected OtherOperand kind"); +} + +bool +Disassembler::OtherOperand::operator!=(const OtherOperand &other) const +{ + return !operator==(other); +} + +bool +Disassembler::HeapAccess::operator==(const HeapAccess &other) const +{ + return kind_ == other.kind_ && + size_ == other.size_ && + address_ == other.address_ && + otherOperand_ == other.otherOperand_; +} + +bool +Disassembler::HeapAccess::operator!=(const HeapAccess &other) const +{ + return !operator==(other); +} + +#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Disassembler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Disassembler.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Disassembler.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Disassembler.h 2015-02-03 14:33:31.000000000 +0000 @@ -0,0 +1,264 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 jit_Disassembler_h +#define jit_Disassembler_h + +#include "jit/Registers.h" + +namespace js { +namespace jit { + +namespace Disassembler { + +class ComplexAddress { + int32_t disp_; + Register::Code base_ : 8; + Register::Code index_ : 8; + int8_t scale_; // log2 encoding + bool isPCRelative_; + + public: + ComplexAddress() + : disp_(0), + base_(Registers::Invalid), + index_(Registers::Invalid), + scale_(0), + isPCRelative_(false) + { + MOZ_ASSERT(*this == *this); + } + + ComplexAddress(int32_t disp, Register::Code base) + : disp_(disp), + base_(base), + index_(Registers::Invalid), + scale_(0), + isPCRelative_(false) + { + MOZ_ASSERT(*this == *this); + MOZ_ASSERT(base != Registers::Invalid); + MOZ_ASSERT(base_ == base); + } + + ComplexAddress(int32_t disp, Register::Code base, Register::Code index, int scale) + : disp_(disp), + base_(base), + index_(index), + scale_(scale), + isPCRelative_(false) + { + MOZ_ASSERT(scale >= 0 && scale < 4); + MOZ_ASSERT_IF(index == Registers::Invalid, scale == 0); + MOZ_ASSERT(*this == *this); + MOZ_ASSERT(base_ == base); + MOZ_ASSERT(index_ == index); + } + + explicit ComplexAddress(const void *addr) + : disp_(static_cast(reinterpret_cast(addr))), + base_(Registers::Invalid), + index_(Registers::Invalid), + scale_(0), + isPCRelative_(false) + { + MOZ_ASSERT(*this == *this); + MOZ_ASSERT(reinterpret_cast(uintptr_t(disp_)) == addr); + } + + explicit ComplexAddress(const Operand &op) { +#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) + switch (op.kind()) { + case Operand::MEM_REG_DISP: + *this = ComplexAddress(op.disp(), op.base()); + return; + case Operand::MEM_SCALE: + *this = ComplexAddress(op.disp(), op.base(), op.index(), op.scale()); + return; + default: + break; + } +#endif + MOZ_CRASH("Unexpected Operand kind"); + } + + bool isPCRelative() const { + return isPCRelative_; + } + + int32_t disp() const { + return disp_; + } + + Register::Code base() const { + return base_; + } + + Register::Code index() const { + return index_; + } + + uint32_t scale() const { + return scale_; + } + +#ifdef DEBUG + bool operator==(const ComplexAddress &other) const; + bool operator!=(const ComplexAddress &other) const; +#endif +}; + +// An operand other than a memory operand -- a register or an immediate. +class OtherOperand { + public: + enum Kind { + Imm, + GPR, + FPR, + }; + + private: + Kind kind_; + union { + int32_t imm; + Register::Code gpr; + FloatRegister::Code fpr; + } u_; + + public: + OtherOperand() + : kind_(Imm) + { + u_.imm = 0; + MOZ_ASSERT(*this == *this); + } + + explicit OtherOperand(int32_t imm) + : kind_(Imm) + { + u_.imm = imm; + MOZ_ASSERT(*this == *this); + } + + explicit OtherOperand(Register::Code gpr) + : kind_(GPR) + { + u_.gpr = gpr; + MOZ_ASSERT(*this == *this); + } + + explicit OtherOperand(FloatRegister::Code fpr) + : kind_(FPR) + { + u_.fpr = fpr; + MOZ_ASSERT(*this == *this); + } + + Kind kind() const { + return kind_; + } + + int32_t imm() const { + MOZ_ASSERT(kind_ == Imm); + return u_.imm; + } + + Register::Code gpr() const { + MOZ_ASSERT(kind_ == GPR); + return u_.gpr; + } + + FloatRegister::Code fpr() const { + MOZ_ASSERT(kind_ == FPR); + return u_.fpr; + } + +#ifdef DEBUG + bool operator==(const OtherOperand &other) const; + bool operator!=(const OtherOperand &other) const; +#endif +}; + +class HeapAccess { + public: + enum Kind { + Unknown, + Load, // any bits not covered by the load are zeroed + LoadSext32, // like Load, but sign-extend to 32 bits + Store + }; + + private: + Kind kind_; + size_t size_; // The number of bytes of memory accessed + ComplexAddress address_; + OtherOperand otherOperand_; + + public: + HeapAccess() + : kind_(Unknown), + size_(0) + { + MOZ_ASSERT(*this == *this); + } + + HeapAccess(Kind kind, size_t size, const ComplexAddress &address, const OtherOperand &otherOperand) + : kind_(kind), + size_(size), + address_(address), + otherOperand_(otherOperand) + { + MOZ_ASSERT(kind != Unknown); + MOZ_ASSERT_IF(kind == LoadSext32, otherOperand.kind() != OtherOperand::FPR); + MOZ_ASSERT_IF(kind == Load || kind == LoadSext32, otherOperand.kind() != OtherOperand::Imm); + MOZ_ASSERT(*this == *this); + } + + Kind kind() const { + return kind_; + } + + size_t size() const { + MOZ_ASSERT(kind_ != Unknown); + return size_; + } + + const ComplexAddress &address() const { + return address_; + } + + const OtherOperand &otherOperand() const { + return otherOperand_; + } + +#ifdef DEBUG + bool operator==(const HeapAccess &other) const; + bool operator!=(const HeapAccess &other) const; +#endif +}; + +MOZ_COLD uint8_t *DisassembleHeapAccess(uint8_t *ptr, HeapAccess *access); + +#ifdef DEBUG +void DumpHeapAccess(const HeapAccess &access); + +inline void +VerifyHeapAccess(uint8_t *begin, uint8_t *end, const HeapAccess &expected) +{ + HeapAccess disassembled; + uint8_t *e = DisassembleHeapAccess(begin, &disassembled); + MOZ_ASSERT(e == end); + MOZ_ASSERT(disassembled == expected); +} + +#endif + +} // namespace Disassembler + +} // namespace jit +} // namespace js + +#endif /* jit_Disassembler_h */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/ExecutableAllocator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/ExecutableAllocator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/ExecutableAllocator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/ExecutableAllocator.h 2015-02-03 14:33:31.000000000 +0000 @@ -405,6 +405,11 @@ _flush_cache(reinterpret_cast(code), size, BCACHE); #endif } +#elif defined(JS_CODEGEN_ARM) && (defined(__FreeBSD__) || defined(__NetBSD__)) + static void cacheFlush(void* code, size_t size) + { + __clear_cache(code, reinterpret_cast(code) + size); + } #elif defined(JS_CODEGEN_ARM) && (defined(__linux__) || defined(ANDROID)) && defined(__GNUC__) static void cacheFlush(void* code, size_t size) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/IonAnalysis.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/IonAnalysis.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/IonAnalysis.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/IonAnalysis.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -2064,7 +2064,7 @@ case MIRType_Elements: case MIRType_Pointer: case MIRType_Shape: - case MIRType_TypeObject: + case MIRType_ObjectGroup: case MIRType_Float32x4: case MIRType_Int32x4: case MIRType_Doublex2: @@ -2880,7 +2880,7 @@ } static bool -AnalyzePoppedThis(JSContext *cx, types::TypeObject *type, +AnalyzePoppedThis(JSContext *cx, types::ObjectGroup *group, MDefinition *thisValue, MInstruction *ins, bool definitelyExecuted, HandlePlainObject baseobj, Vector *initializerList, @@ -2929,7 +2929,7 @@ return true; RootedId id(cx, NameToId(setprop->name())); - if (!types::AddClearDefiniteGetterSetterForPrototypeChain(cx, type, id)) { + if (!types::AddClearDefiniteGetterSetterForPrototypeChain(cx, group, id)) { // The prototype chain already contains a getter/setter for this // property, or type information is too imprecise. return true; @@ -2989,7 +2989,7 @@ if (!baseobj->lookup(cx, id) && !accessedProperties->append(get->name())) return false; - if (!types::AddClearDefiniteGetterSetterForPrototypeChain(cx, type, id)) { + if (!types::AddClearDefiniteGetterSetterForPrototypeChain(cx, group, id)) { // The |this| value can escape if any property reads it does go // through a getter. return true; @@ -3016,7 +3016,7 @@ bool jit::AnalyzeNewScriptDefiniteProperties(JSContext *cx, JSFunction *fun, - types::TypeObject *type, HandlePlainObject baseobj, + types::ObjectGroup *group, HandlePlainObject baseobj, Vector *initializerList) { MOZ_ASSERT(cx->zone()->types.activeAnalysis); @@ -3053,7 +3053,7 @@ return true; } - types::TypeScript::SetThis(cx, script, types::Type::ObjectType(type)); + types::TypeScript::SetThis(cx, script, types::Type::ObjectType(group)); MIRGraph graph(&temp); InlineScriptTree *inlineScriptTree = InlineScriptTree::New(&temp, nullptr, nullptr, script); @@ -3158,7 +3158,7 @@ bool handled = false; size_t slotSpan = baseobj->slotSpan(); - if (!AnalyzePoppedThis(cx, type, thisValue, ins, definitelyExecuted, + if (!AnalyzePoppedThis(cx, group, thisValue, ins, definitelyExecuted, baseobj, initializerList, &accessedProperties, &handled)) { return false; @@ -3186,7 +3186,7 @@ if (MResumePoint *rp = block->callerResumePoint()) { if (block->numPredecessors() == 1 && block->getPredecessor(0) == rp->block()) { JSScript *script = rp->block()->info().script(); - if (!types::AddClearDefiniteFunctionUsesInScript(cx, type, script, block->info().script())) + if (!types::AddClearDefiniteFunctionUsesInScript(cx, group, script, block->info().script())) return false; } } @@ -3482,9 +3482,7 @@ size_t headerId = header->id(); size_t inLoopId = headerId; size_t notInLoopId = inLoopId + numMarked; - size_t numOSRDominated = 0; ReversePostorderIterator i = graph.rpoBegin(header); - MBasicBlock *osrBlock = graph.osrBlock(); for (;;) { MBasicBlock *block = *i++; MOZ_ASSERT(block->id() >= header->id() && block->id() <= backedge->id(), @@ -3497,15 +3495,6 @@ // If we've reached the loop backedge, we're done! if (block == backedge) break; - } else if (osrBlock && osrBlock->dominates(block)) { - // This block is not in the loop, but since it's dominated by the - // OSR entry, the block was probably in the loop before some - // folding. This probably means that the block has outgoing paths - // which re-enter the loop in the middle. And that, finally, means - // that we can't move this block to the end, since it could create a - // backwards branch to a block which is not the loop header. - block->setId(inLoopId++); - ++numOSRDominated; } else { // This block is not in the loop. Move it to the end. graph.moveBlockBefore(insertPt, block); @@ -3513,11 +3502,8 @@ } } MOZ_ASSERT(header->id() == headerId, "Loop header id changed"); - MOZ_ASSERT(inLoopId == headerId + numMarked + numOSRDominated, - "Wrong number of blocks kept in loop"); - MOZ_ASSERT(notInLoopId == (insertIter != graph.rpoEnd() - ? insertPt->id() - : graph.numBlocks()) - numOSRDominated, + MOZ_ASSERT(inLoopId == headerId + numMarked, "Wrong number of blocks kept in loop"); + MOZ_ASSERT(notInLoopId == (insertIter != graph.rpoEnd() ? insertPt->id() : graph.numBlocks()), "Wrong number of blocks moved out of loop"); } @@ -3539,6 +3525,13 @@ if (numMarked == 0) continue; + // If there's an OSR block entering the loop in the middle, it's tricky, + // so don't try to handle it, for now. + if (canOsr) { + UnmarkLoopBlocks(graph, header); + continue; + } + // Move all blocks between header and backedge that aren't marked to // the end of the loop, making the loop itself contiguous. MakeLoopContiguous(graph, header, numMarked); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/IonAnalysis.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/IonAnalysis.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/IonAnalysis.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/IonAnalysis.h 2015-02-03 14:33:31.000000000 +0000 @@ -170,7 +170,7 @@ bool AnalyzeNewScriptDefiniteProperties(JSContext *cx, JSFunction *fun, - types::TypeObject *type, HandlePlainObject baseobj, + types::ObjectGroup *group, HandlePlainObject baseobj, Vector *initializerList); bool diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/IonBuilder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/IonBuilder.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/IonBuilder.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/IonBuilder.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -70,7 +70,7 @@ inspector->thisType = types::GetMaybeUntrackedValueType(frame->thisValue()); - if (frame->scopeChain()->hasSingletonType()) + if (frame->scopeChain()->isSingleton()) inspector->singletonScopeChain = frame->scopeChain(); JSScript *script = frame->script(); @@ -116,6 +116,9 @@ uint32_t loopDepth) : MIRGenerator(comp, options, temp, graph, info, optimizationInfo), backgroundCodegen_(nullptr), + actionableAbortScript_(nullptr), + actionableAbortPc_(nullptr), + actionableAbortMessage_(nullptr), analysisContext(analysisContext), baselineFrame_(baselineFrame), constraints_(constraints), @@ -126,6 +129,7 @@ typeArrayHint(0), bytecodeTypeMap(nullptr), loopDepth_(loopDepth), + trackedOptimizationSites_(*temp), lexicalCheck_(nullptr), callerResumePoint_(nullptr), callerBuilder_(nullptr), @@ -181,10 +185,29 @@ va_end(ap); JitSpew(JitSpew_IonAbort, "aborted @ %s:%d", script()->filename(), PCToLineNumber(script(), pc)); #endif + trackActionableAbort(message); return false; } void +IonBuilder::trackActionableAbort(const char *message) +{ + if (!isOptimizationTrackingEnabled()) + return; + + IonBuilder *topBuilder = this; + while (topBuilder->callerBuilder_) + topBuilder = topBuilder->callerBuilder_; + + if (topBuilder->hadActionableAbort()) + return; + + topBuilder->actionableAbortScript_ = script(); + topBuilder->actionableAbortPc_ = pc; + topBuilder->actionableAbortMessage_ = message; +} + +void IonBuilder::spew(const char *message) { // Don't call PCToLineNumber in release builds. @@ -193,6 +216,37 @@ #endif } +MInstruction * +IonBuilder::constantMaybeNursery(JSObject *obj) +{ + MOZ_ASSERT(obj); + if (!IsInsideNursery(obj)) + return constant(ObjectValue(*obj)); + + // If |obj| is in the nursery, we have to add it to the list of nursery + // objects that get traced during off-thread compilation. We use + // MNurseryObject to ensure we will patch the code with the right + // pointer after codegen is done. + + size_t index = UINT32_MAX; + for (size_t i = 0, len = nurseryObjects_.length(); i < len; i++) { + if (nurseryObjects_[i] == obj) { + index = i; + break; + } + } + + if (index == UINT32_MAX) { + if (!nurseryObjects_.append(obj)) + return nullptr; + index = nurseryObjects_.length() - 1; + } + + MNurseryObject *ins = MNurseryObject::New(alloc(), obj, index, constraints()); + current->add(ins); + return ins; +} + static inline int32_t GetJumpOffset(jsbytecode *pc) { @@ -262,7 +316,7 @@ if (!calleeTypes) return nullptr; - JSObject *obj = calleeTypes->getSingleton(); + JSObject *obj = calleeTypes->maybeSingleton(); if (!obj || !obj->is()) return nullptr; @@ -289,21 +343,21 @@ if (!targets.reserve(objCount)) return false; for (unsigned i = 0; i < objCount; i++) { - JSObject *obj = calleeTypes->getSingleObject(i); + JSObject *obj = calleeTypes->getSingleton(i); if (obj) { - MOZ_ASSERT(obj->hasSingletonType()); + MOZ_ASSERT(obj->isSingleton()); } else { - types::TypeObject *typeObj = calleeTypes->getTypeObject(i); - if (!typeObj) + types::ObjectGroup *group = calleeTypes->getGroup(i); + if (!group) continue; - obj = typeObj->maybeInterpretedFunction(); + obj = group->maybeInterpretedFunction(); if (!obj) { targets.clear(); return true; } - MOZ_ASSERT(!obj->hasSingletonType()); + MOZ_ASSERT(!obj->isSingleton()); } // Don't optimize if the callee is not callable or constructable per @@ -336,16 +390,20 @@ IonBuilder::InliningDecision IonBuilder::canInlineTarget(JSFunction *target, CallInfo &callInfo) { - if (!optimizationInfo().inlineInterpreted()) + if (!optimizationInfo().inlineInterpreted()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric); return InliningDecision_DontInline; + } if (TraceLogTextIdEnabled(TraceLogger_InlinedScripts)) { return DontInline(nullptr, "Tracelogging of inlined scripts is enabled" "but Tracelogger cannot do that yet."); } - if (!target->isInterpreted()) + if (!target->isInterpreted()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNotInterpreted); return DontInline(nullptr, "Non-interpreted target"); + } // Allow constructing lazy scripts when performing the definite properties // analysis, as baseline has not been used to warm the caller up yet. @@ -358,58 +416,84 @@ MethodStatus status = BaselineCompile(analysisContext, script); if (status == Method_Error) return InliningDecision_Error; - if (status != Method_Compiled) + if (status != Method_Compiled) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNoBaseline); return InliningDecision_DontInline; + } } } - if (!target->hasScript()) + if (!target->hasScript()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineLazy); return DontInline(nullptr, "Lazy script"); + } JSScript *inlineScript = target->nonLazyScript(); - if (callInfo.constructing() && !target->isInterpretedConstructor()) + if (callInfo.constructing() && !target->isInterpretedConstructor()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNotConstructor); return DontInline(inlineScript, "Callee is not a constructor"); + } AnalysisMode analysisMode = info().analysisMode(); - if (!CanIonCompile(inlineScript, analysisMode)) + if (!CanIonCompile(inlineScript, analysisMode)) { + trackOptimizationOutcome(TrackedOutcome::CantInlineDisabledIon); return DontInline(inlineScript, "Disabled Ion compilation"); + } // Don't inline functions which don't have baseline scripts. - if (!inlineScript->hasBaselineScript()) + if (!inlineScript->hasBaselineScript()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNoBaseline); return DontInline(inlineScript, "No baseline jitcode"); + } - if (TooManyFormalArguments(target->nargs())) + if (TooManyFormalArguments(target->nargs())) { + trackOptimizationOutcome(TrackedOutcome::CantInlineTooManyArgs); return DontInline(inlineScript, "Too many args"); + } // We check the number of actual arguments against the maximum number of // formal arguments as we do not want to encode all actual arguments in the // callerResumePoint. - if (TooManyFormalArguments(callInfo.argc())) + if (TooManyFormalArguments(callInfo.argc())) { + trackOptimizationOutcome(TrackedOutcome::CantInlineTooManyArgs); return DontInline(inlineScript, "Too many actual args"); + } // Allow inlining of recursive calls, but only one level deep. IonBuilder *builder = callerBuilder_; while (builder) { - if (builder->script() == inlineScript) + if (builder->script() == inlineScript) { + trackOptimizationOutcome(TrackedOutcome::CantInlineRecursive); return DontInline(inlineScript, "Recursive call"); + } builder = builder->callerBuilder_; } - if (target->isHeavyweight()) + if (target->isHeavyweight()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineHeavyweight); return DontInline(inlineScript, "Heavyweight function"); + } - if (inlineScript->uninlineable()) + if (inlineScript->uninlineable()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric); return DontInline(inlineScript, "Uninlineable script"); + } - if (inlineScript->needsArgsObj()) + if (inlineScript->needsArgsObj()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNeedsArgsObj); return DontInline(inlineScript, "Script that needs an arguments object"); + } - if (inlineScript->isDebuggee()) + if (inlineScript->isDebuggee()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineDebuggee); return DontInline(inlineScript, "Script is debuggee"); + } - types::TypeObjectKey *targetType = types::TypeObjectKey::get(target); - if (targetType->unknownProperties()) + types::ObjectGroupKey *targetKey = types::ObjectGroupKey::get(target); + if (targetKey->unknownProperties()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineUnknownProps); return DontInline(inlineScript, "Target type has unknown properties"); + } return InliningDecision_Inline; } @@ -762,7 +846,7 @@ if (!processIterators()) return false; - if (!abortedNewScriptPropertiesTypes().empty()) { + if (!abortedNewScriptPropertiesGroups().empty()) { MOZ_ASSERT(!info().isAnalysis()); abortReason_ = AbortReason_NewScriptProperties; return false; @@ -917,7 +1001,7 @@ // Discard unreferenced & pre-allocated resume points. replaceMaybeFallbackFunctionGetter(nullptr); - if (!abortedNewScriptPropertiesTypes().empty()) { + if (!abortedNewScriptPropertiesGroups().empty()) { MOZ_ASSERT(!info().isAnalysis()); abortReason_ = AbortReason_NewScriptProperties; return false; @@ -1842,10 +1926,14 @@ return jsop_debugger(); default: + // Track a simpler message, since the actionable abort message is a + // static string, and the internal opcode name isn't an actionable + // thing anyways. + trackActionableAbort("Unsupported bytecode"); #ifdef DEBUG - return abort("Unsupported opcode: %s (line %d)", js_CodeName[op], info().lineno(pc)); + return abort("Unsupported opcode: %s", js_CodeName[op]); #else - return abort("Unsupported opcode: %d (line %d)", op, info().lineno(pc)); + return abort("Unsupported opcode: %d", op); #endif } } @@ -3366,7 +3454,7 @@ if (altersNull) flags |= types::TYPE_FLAG_NULL; - types::TemporaryTypeSet base(flags, static_cast(nullptr)); + types::TemporaryTypeSet base(flags, static_cast(nullptr)); type = types::TypeSet::intersectSets(&base, subject->resultTypeSet(), alloc_->lifoAlloc()); } @@ -3495,7 +3583,7 @@ return true; } - types::TemporaryTypeSet base(flags, static_cast(nullptr)); + types::TemporaryTypeSet base(flags, static_cast(nullptr)); type = types::TypeSet::intersectSets(&base, oldType, alloc_->lifoAlloc()); } @@ -4403,10 +4491,10 @@ } else if (inlineBuilder.abortReason_ == AbortReason_Inlining) { abortReason_ = AbortReason_Inlining; } else if (inlineBuilder.abortReason_ == AbortReason_NewScriptProperties) { - const TypeObjectVector &types = inlineBuilder.abortedNewScriptPropertiesTypes(); - MOZ_ASSERT(!types.empty()); - for (size_t i = 0; i < types.length(); i++) - addAbortedNewScriptPropertiesType(types[i]); + const ObjectGroupVector &groups = inlineBuilder.abortedNewScriptPropertiesGroups(); + MOZ_ASSERT(!groups.empty()); + for (size_t i = 0; i < groups.length(); i++) + addAbortedNewScriptPropertiesGroup(groups[i]); abortReason_ = AbortReason_NewScriptProperties; } @@ -4548,8 +4636,10 @@ IonBuilder::makeInliningDecision(JSObject *targetArg, CallInfo &callInfo) { // When there is no target, inlining is impossible. - if (targetArg == nullptr) + if (targetArg == nullptr) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNoTarget); return InliningDecision_DontInline; + } // Inlining non-function targets is handled by inlineNonFunctionCall(). if (!targetArg->is()) @@ -4577,11 +4667,15 @@ if (!targetScript->shouldInline()) { // Cap the inlining depth. if (js_JitOptions.isSmallFunction(targetScript)) { - if (inliningDepth_ >= optimizationInfo().smallFunctionMaxInlineDepth()) + if (inliningDepth_ >= optimizationInfo().smallFunctionMaxInlineDepth()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineExceededDepth); return DontInline(targetScript, "Vetoed: exceeding allowed inline depth"); + } } else { - if (inliningDepth_ >= optimizationInfo().maxInlineDepth()) + if (inliningDepth_ >= optimizationInfo().maxInlineDepth()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineExceededDepth); return DontInline(targetScript, "Vetoed: exceeding allowed inline depth"); + } if (targetScript->hasLoops()) { // Currently, we are not inlining function which have loops because @@ -4608,19 +4702,25 @@ hasOpportunities = arg->isLambda() || arg->isConstantValue(); } - if (!hasOpportunities) + if (!hasOpportunities) { + trackOptimizationOutcome(TrackedOutcome::CantInlineBigLoop); return DontInline(targetScript, "Vetoed: big function that contains a loop"); + } } // Caller must not be excessively large. - if (script()->length() >= optimizationInfo().inliningMaxCallerBytecodeLength()) + if (script()->length() >= optimizationInfo().inliningMaxCallerBytecodeLength()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineBigCaller); return DontInline(targetScript, "Vetoed: caller excessively large"); + } } // Callee must not be excessively large. // This heuristic also applies to the callsite as a whole. - if (targetScript->length() > optimizationInfo().inlineMaxTotalBytecodeLength()) + if (targetScript->length() > optimizationInfo().inlineMaxTotalBytecodeLength()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineBigCallee); return DontInline(targetScript, "Vetoed: callee excessively large"); + } // Callee must have been called a few times to have somewhat stable // type information, except for definite properties analysis, @@ -4629,6 +4729,7 @@ !targetScript->baselineScript()->ionCompiledOrInlined() && info().analysisMode() != Analysis_DefiniteProperties) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNotHot); JitSpew(JitSpew_Inlining, "Cannot inline %s:%u: callee is insufficiently hot.", targetScript->filename(), targetScript->lineno()); return InliningDecision_WarmUpCountTooLow; @@ -4636,8 +4737,8 @@ } // TI calls ObjectStateChange to trigger invalidation of the caller. - types::TypeObjectKey *targetType = types::TypeObjectKey::get(target); - targetType->watchStateChangeForInlinedCall(constraints()); + types::ObjectGroupKey *targetKey = types::ObjectGroupKey::get(target); + targetKey->watchStateChangeForInlinedCall(constraints()); return InliningDecision_Inline; } @@ -4660,6 +4761,10 @@ for (size_t i = 0; i < targets.length(); i++) { JSObject *target = targets[i]; + + trackOptimizationAttempt(TrackedStrategy::Call_Inline); + trackTypeInfo(TrackedTypeSite::Call_Target, target); + bool inlineable; InliningDecision decision = makeInliningDecision(target, callInfo); switch (decision) { @@ -4693,6 +4798,18 @@ *numInlineable += 1; } + // If optimization tracking is turned on and one of the inlineable targets + // is a native, track the type info of the call. Most native inlinings + // depend on the types of the arguments and the return value. + if (isOptimizationTrackingEnabled()) { + for (size_t i = 0; i < targets.length(); i++) { + if (choiceSet[i] && targets[i]->as().isNative()) { + trackTypeInfo(callInfo); + break; + } + } + } + MOZ_ASSERT(choiceSet.length() == targets.length()); return true; } @@ -4747,10 +4864,10 @@ } // This function returns the cache given to the constructor if the - // GetPropertyCache can be moved into the TypeObject fallback path. + // GetPropertyCache can be moved into the ObjectGroup fallback path. MGetPropertyCache *moveableCache(bool hasTypeBarrier, MDefinition *thisDef) { // If we have unhandled uses of the MGetPropertyCache, then we cannot - // move it to the TypeObject fallback path. + // move it to the ObjectGroup fallback path. if (!hasTypeBarrier) { if (cache_->hasUses()) return nullptr; @@ -4764,7 +4881,7 @@ // If the this-object is not identical to the object of the // MGetPropertyCache, then we cannot use the InlinePropertyTable, or if - // we do not yet have enough information from the TypeObject. + // we do not yet have enough information from the ObjectGroup. if (!CanInlineGetPropertyCache(cache_, thisDef)) return nullptr; @@ -4815,13 +4932,22 @@ IonBuilder::InliningStatus IonBuilder::inlineSingleCall(CallInfo &callInfo, JSObject *targetArg) { - if (!targetArg->is()) - return inlineNonFunctionCall(callInfo, targetArg); + if (!targetArg->is()) { + InliningStatus status = inlineNonFunctionCall(callInfo, targetArg); + trackInlineSuccess(status); + return status; + } JSFunction *target = &targetArg->as(); - if (target->isNative()) - return inlineNativeCall(callInfo, target); + if (target->isNative()) { + InliningStatus status = inlineNativeCall(callInfo, target); + trackInlineSuccess(status); + return status; + } + // Track success now, as inlining a scripted call makes a new return block + // which has a different pc than the current call pc. + trackInlineSuccess(); if (!inlineScriptedCall(callInfo, target)) return InliningStatus_Error; return InliningStatus_Inlined; @@ -4831,12 +4957,15 @@ IonBuilder::inlineCallsite(const ObjectVector &targets, ObjectVector &originals, CallInfo &callInfo) { - if (targets.empty()) + if (targets.empty()) { + trackOptimizationAttempt(TrackedStrategy::Call_Inline); + trackOptimizationOutcome(TrackedOutcome::CantInlineNoTarget); return InliningStatus_NotInlined; + } // Is the function provided by an MGetPropertyCache? // If so, the cache may be movable to a fallback path, with a dispatch - // instruction guarding on the incoming TypeObject. + // instruction guarding on the incoming ObjectGroup. WrapMGetPropertyCache propCache(getInlineableGetPropertyCache(callInfo)); keepFallbackFunctionGetter(propCache.get()); @@ -4844,6 +4973,10 @@ // avoiding the cache and guarding is still faster. if (!propCache.get() && targets.length() == 1) { JSObject *target = targets[0]; + + trackOptimizationAttempt(TrackedStrategy::Call_Inline); + trackTypeInfo(TrackedTypeSite::Call_Target, target); + InliningDecision decision = makeInliningDecision(target, callInfo); switch (decision) { case InliningDecision_Error: @@ -4864,7 +4997,7 @@ // If the callee is not going to be a lambda (which may vary across // different invocations), then the callee definition can be replaced by a // constant. - if (target->hasSingletonType()) { + if (target->isSingleton()) { // Replace the function with an MConstant. MConstant *constFun = constant(ObjectValue(*target)); callInfo.setFun(constFun); @@ -4914,8 +5047,8 @@ } bool -IonBuilder::inlineTypeObjectFallback(CallInfo &callInfo, MBasicBlock *dispatchBlock, - MTypeObjectDispatch *dispatch, MGetPropertyCache *cache, +IonBuilder::inlineObjectGroupFallback(CallInfo &callInfo, MBasicBlock *dispatchBlock, + MObjectGroupDispatch *dispatch, MGetPropertyCache *cache, MBasicBlock **fallbackTarget) { // Getting here implies the following: @@ -4923,7 +5056,7 @@ // followed by an MTypeBarrier. MOZ_ASSERT(callInfo.fun()->isGetPropertyCache() || callInfo.fun()->isTypeBarrier()); - // 2. The MGetPropertyCache has inlineable cases by guarding on the TypeObject. + // 2. The MGetPropertyCache has inlineable cases by guarding on the ObjectGroup. MOZ_ASSERT(dispatch->numCases() > 0); // 3. The MGetPropertyCache (and, if applicable, MTypeBarrier) only @@ -5044,7 +5177,7 @@ // Generate a dispatch based on guard kind. MDispatchInstruction *dispatch; if (maybeCache) { - dispatch = MTypeObjectDispatch::New(alloc(), maybeCache->object(), maybeCache->propTable()); + dispatch = MObjectGroupDispatch::New(alloc(), maybeCache->object(), maybeCache->propTable()); callInfo.fun()->setImplicitlyUsedUnchecked(); } else { dispatch = MFunctionDispatch::New(alloc(), callInfo.fun()); @@ -5078,7 +5211,7 @@ retPhi->reserveLength(count); // During inlining the 'this' value is assigned a type set which is - // specialized to the type objects which can generate that inlining target. + // specialized to the groups which can generate that inlining target. // After inlining the original type set is restored. types::TemporaryTypeSet *cacheObjectTypeSet = maybeCache ? maybeCache->object()->resultTypeSet() : nullptr; @@ -5090,6 +5223,10 @@ if (!choiceSet[i]) continue; + // Even though we made one round of inline decisions already, we may + // be amending them below. + amendOptimizationAttempt(i); + // When original != target, the target is a callsite clone. The // original should be used for guards, and the target should be the // actual function inlined. @@ -5099,6 +5236,7 @@ // Target must be reachable by the MDispatchInstruction. if (maybeCache && !maybeCache->propTable()->hasFunction(original)) { choiceSet[i] = false; + trackOptimizationOutcome(TrackedOutcome::CantInlineNotInDispatch); continue; } @@ -5110,7 +5248,7 @@ // can't use a constant, add a no-op MPolyInlineGuard, to prevent // hoisting scope chain gets above the dispatch instruction. MInstruction *funcDef; - if (target->hasSingletonType()) + if (target->isSingleton()) funcDef = MConstant::New(alloc(), ObjectValue(*target), constraints()); else funcDef = MPolyInlineGuard::New(alloc(), callInfo.fun()); @@ -5163,8 +5301,8 @@ // // Note that guarding is on the original function pointer even // if there is a clone, since cloning occurs at the callsite. - types::TypeObject *funType = original->hasSingletonType() ? nullptr : original->type(); - dispatch->addCase(original, funType, inlineBlock); + types::ObjectGroup *funcGroup = original->isSingleton() ? nullptr : original->group(); + dispatch->addCase(original, funcGroup, inlineBlock); MDefinition *retVal = inlineReturnBlock->peek(-1); retPhi->addInput(retVal); @@ -5190,12 +5328,12 @@ } // If necessary, generate a fallback path. - // MTypeObjectDispatch always uses a fallback path. + // MObjectGroupDispatch always uses a fallback path. if (maybeCache || dispatch->numCases() < targets.length()) { // Generate fallback blocks, and set |current| to the fallback return block. if (maybeCache) { MBasicBlock *fallbackTarget; - if (!inlineTypeObjectFallback(callInfo, dispatchBlock, (MTypeObjectDispatch *)dispatch, + if (!inlineObjectGroupFallback(callInfo, dispatchBlock, (MObjectGroupDispatch *)dispatch, maybeCache, &fallbackTarget)) { return false; @@ -5216,7 +5354,7 @@ if (targets[i]->is()) { JSFunction *target = &targets[i]->as(); - if (target->hasSingletonType()) { + if (target->isSingleton()) { remaining = target; clonedAtCallsite = target != originals[i]; } @@ -5357,12 +5495,12 @@ JSObject * IonBuilder::getSingletonPrototype(JSFunction *target) { - types::TypeObjectKey *targetType = types::TypeObjectKey::get(target); - if (targetType->unknownProperties()) + types::ObjectGroupKey *targetKey = types::ObjectGroupKey::get(target); + if (targetKey->unknownProperties()) return nullptr; jsid protoid = NameToId(names().prototype); - types::HeapTypeSetKey protoProperty = targetType->property(protoid); + types::HeapTypeSetKey protoProperty = targetKey->property(protoid); return protoProperty.singleton(constraints()); } @@ -5376,11 +5514,17 @@ return nullptr; JSObject *templateObject = inspector->getTemplateObject(pc); - if (!templateObject || !templateObject->is()) + if (!templateObject) + return nullptr; + if (!templateObject->is() && !templateObject->is()) return nullptr; if (!templateObject->hasTenuredProto() || templateObject->getProto() != proto) return nullptr; + types::ObjectGroupKey *templateObjectKey = types::ObjectGroupKey::get(templateObject->group()); + if (templateObjectKey->hasFlags(constraints(), types::OBJECT_FLAG_NEW_SCRIPT_CLEARED)) + return nullptr; + types::StackTypeSet *thisTypes = types::TypeScript::ThisTypes(target->nonLazyScript()); if (!thisTypes || !thisTypes->hasType(types::Type::ObjectType(templateObject))) return nullptr; @@ -5390,7 +5534,7 @@ MConstant *templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); MCreateThisWithTemplate *createThis = MCreateThisWithTemplate::New(alloc(), constraints(), templateConst, - templateObject->type()->initialHeap(constraints())); + templateObject->group()->initialHeap(constraints())); current->add(templateConst); current->add(createThis); @@ -5630,6 +5774,8 @@ bool IonBuilder::jsop_call(uint32_t argc, bool constructing) { + startTrackingOptimizations(); + // If this call has never executed, try to seed the observed type set // based on how the call result is used. types::TemporaryTypeSet *observed = bytecodeTypes(pc); @@ -5725,6 +5871,9 @@ IonBuilder::testShouldDOMCall(types::TypeSet *inTypes, JSFunction *func, JSJitInfo::OpType opType) { + if (IsInsideNursery(func)) + return false; + if (!func->isNative() || !func->jitInfo()) return false; @@ -5739,14 +5888,14 @@ return false; for (unsigned i = 0; i < inTypes->getObjectCount(); i++) { - types::TypeObjectKey *curType = inTypes->getObject(i); - if (!curType) + types::ObjectGroupKey *key = inTypes->getObject(i); + if (!key) continue; - if (!curType->hasStableClassAndProto(constraints())) + if (!key->hasStableClassAndProto(constraints())) return false; - if (!instanceChecker(curType->clasp(), jinfo->protoID, jinfo->depth)) + if (!instanceChecker(key->clasp(), jinfo->protoID, jinfo->depth)) return false; } @@ -6034,7 +6183,7 @@ bool IonBuilder::jsop_newarray(uint32_t count) { - NativeObject *templateObject = inspector->getTemplateObject(pc); + JSObject *templateObject = inspector->getTemplateObject(pc); if (!templateObject) { if (info().analysisMode() == Analysis_ArgumentsUsage) { MUnknownValue *unknown = MUnknownValue::New(alloc()); @@ -6046,7 +6195,7 @@ } MOZ_ASSERT(templateObject->is()); - if (templateObject->type()->unknownProperties()) { + if (templateObject->group()->unknownProperties()) { if (info().analysisMode() == Analysis_ArgumentsUsage) { MUnknownValue *unknown = MUnknownValue::New(alloc()); current->add(unknown); @@ -6054,7 +6203,7 @@ return true; } // We will get confused in jsop_initelem_array if we can't find the - // type object being initialized. + // object group being initialized. return abort("New array has unknown properties"); } @@ -6062,7 +6211,7 @@ current->add(templateConst); MNewArray *ins = MNewArray::New(alloc(), constraints(), count, templateConst, - templateObject->type()->initialHeap(constraints()), + templateObject->group()->initialHeap(constraints()), NewArray_FullyAllocating); current->add(ins); current->push(ins); @@ -6071,9 +6220,9 @@ ins->resultTypeSet()->convertDoubleElements(constraints()); if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles) - templateObject->setShouldConvertDoubleElements(); + templateObject->as().setShouldConvertDoubleElements(); else - templateObject->clearShouldConvertDoubleElements(); + templateObject->as().clearShouldConvertDoubleElements(); return true; } @@ -6087,11 +6236,11 @@ // analysis the baseline compiler hasn't run yet, however, though in this // case the template object's type doesn't matter. MOZ_ASSERT_IF(info().analysisMode() != Analysis_ArgumentsUsage, - templateObject->type()->hasAnyFlags(types::OBJECT_FLAG_COPY_ON_WRITE)); + templateObject->group()->hasAnyFlags(types::OBJECT_FLAG_COPY_ON_WRITE)); MNewArrayCopyOnWrite *ins = MNewArrayCopyOnWrite::New(alloc(), constraints(), templateObject, - templateObject->type()->initialHeap(constraints())); + templateObject->group()->initialHeap(constraints())); current->add(ins); current->push(ins); @@ -6117,9 +6266,9 @@ MConstant *templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); current->add(templateConst); MNewObject *ins = MNewObject::New(alloc(), constraints(), templateConst, - templateObject->hasSingletonType() + templateObject->isSingleton() ? gc::TenuredHeap - : templateObject->type()->initialHeap(constraints()), + : templateObject->group()->initialHeap(constraints()), MNewObject::ObjectLiteral); current->add(ins); @@ -6154,7 +6303,7 @@ if (obj->isUnknownValue()) { needStub = true; } else { - types::TypeObjectKey *initializer = obj->resultTypeSet()->getObject(0); + types::ObjectGroupKey *initializer = obj->resultTypeSet()->getObject(0); if (value->type() == MIRType_MagicHole) { if (!initializer->hasFlags(constraints(), types::OBJECT_FLAG_NON_PACKED)) needStub = true; @@ -6664,7 +6813,8 @@ static bool ClassHasEffectlessLookup(const Class *clasp, PropertyName *name) { - return clasp->isNative() && !clasp->ops.lookupGeneric; + return (clasp == &UnboxedPlainObject::class_) || + (clasp->isNative() && !clasp->ops.lookupProperty); } static bool @@ -6737,16 +6887,16 @@ if (!ClassHasEffectlessLookup(obj->getClass(), name)) return nullptr; - types::TypeObjectKey *objType = types::TypeObjectKey::get(obj); + types::ObjectGroupKey *objKey = types::ObjectGroupKey::get(obj); if (analysisContext) - objType->ensureTrackedProperty(analysisContext, NameToId(name)); + objKey->ensureTrackedProperty(analysisContext, NameToId(name)); - if (objType->unknownProperties()) + if (objKey->unknownProperties()) return nullptr; - types::HeapTypeSetKey property = objType->property(NameToId(name)); + types::HeapTypeSetKey property = objKey->property(NameToId(name)); if (property.isOwnProperty(constraints())) { - if (obj->hasSingletonType()) + if (obj->isSingleton()) return property.singleton(constraints()); return nullptr; } @@ -6777,7 +6927,7 @@ if (types && types->unknownObject()) return false; - JSObject *objectSingleton = types ? types->getSingleton() : nullptr; + JSObject *objectSingleton = types ? types->maybeSingleton() : nullptr; if (objectSingleton) return testSingletonProperty(objectSingleton, name) == singleton; @@ -6818,24 +6968,24 @@ // find a prototype common to all the objects; if that prototype // has the singleton property, the access will not be on a missing property. for (unsigned i = 0; i < types->getObjectCount(); i++) { - types::TypeObjectKey *object = types->getObject(i); - if (!object) + types::ObjectGroupKey *key = types->getObject(i); + if (!key) continue; if (analysisContext) - object->ensureTrackedProperty(analysisContext, NameToId(name)); + key->ensureTrackedProperty(analysisContext, NameToId(name)); - const Class *clasp = object->clasp(); + const Class *clasp = key->clasp(); if (!ClassHasEffectlessLookup(clasp, name) || ClassHasResolveHook(compartment, clasp, name)) return false; - if (object->unknownProperties()) + if (key->unknownProperties()) return false; - types::HeapTypeSetKey property = object->property(NameToId(name)); + types::HeapTypeSetKey property = key->property(NameToId(name)); if (property.isOwnProperty(constraints())) return false; - if (!object->hasTenuredProto()) + if (!key->hasTenuredProto()) return false; - if (JSObject *proto = object->proto().toObjectOrNull()) { + if (JSObject *proto = key->proto().toObjectOrNull()) { // Test this type. if (testSingletonProperty(proto, name) != singleton) return false; @@ -7021,7 +7171,7 @@ jsid id = NameToId(name); MOZ_ASSERT(staticObject->is() || staticObject->is()); - MOZ_ASSERT(staticObject->hasSingletonType()); + MOZ_ASSERT(staticObject->isSingleton()); *psucceeded = true; @@ -7047,16 +7197,16 @@ return true; } - types::TypeObjectKey *staticType = types::TypeObjectKey::get(staticObject); + types::ObjectGroupKey *staticKey = types::ObjectGroupKey::get(staticObject); if (analysisContext) - staticType->ensureTrackedProperty(analysisContext, NameToId(name)); + staticKey->ensureTrackedProperty(analysisContext, NameToId(name)); - if (staticType->unknownProperties()) { + if (staticKey->unknownProperties()) { *psucceeded = false; return true; } - types::HeapTypeSetKey property = staticType->property(id); + types::HeapTypeSetKey property = staticKey->property(id); if (!property.maybeTypes() || !property.maybeTypes()->definiteProperty() || property.nonData(constraints())) @@ -7068,10 +7218,10 @@ } types::TemporaryTypeSet *types = bytecodeTypes(pc); - BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticType, + BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticKey, name, types, /* updateObserved = */ true); - JSObject *singleton = types->getSingleton(); + JSObject *singleton = types->maybeSingleton(); MIRType knownType = types->getKnownMIRType(); if (barrier == BarrierKind::NoBarrier) { @@ -7151,11 +7301,11 @@ MDefinition *value = current->peek(-1); - types::TypeObjectKey *staticType = types::TypeObjectKey::get(staticObject); - if (staticType->unknownProperties()) + types::ObjectGroupKey *staticKey = types::ObjectGroupKey::get(staticObject); + if (staticKey->unknownProperties()) return jsop_setprop(name); - types::HeapTypeSetKey property = staticType->property(id); + types::HeapTypeSetKey property = staticKey->property(id); if (!property.maybeTypes() || !property.maybeTypes()->definiteProperty() || property.nonData(constraints()) || @@ -7304,9 +7454,14 @@ bool IonBuilder::jsop_getelem() { + startTrackingOptimizations(); + MDefinition *index = current->pop(); MDefinition *obj = current->pop(); + trackTypeInfo(TrackedTypeSite::Receiver, obj->type(), obj->resultTypeSet()); + trackTypeInfo(TrackedTypeSite::Index, index->type(), index->resultTypeSet()); + // Always use a call if we are performing analysis and not actually // emitting code, to simplify later analysis. if (info().isAnalysis()) { @@ -7324,30 +7479,38 @@ bool emitted = false; + trackOptimizationAttempt(TrackedStrategy::GetElem_TypedObject); if (!getElemTryTypedObject(&emitted, obj, index) || emitted) return emitted; + trackOptimizationAttempt(TrackedStrategy::GetElem_Dense); if (!getElemTryDense(&emitted, obj, index) || emitted) return emitted; + trackOptimizationAttempt(TrackedStrategy::GetElem_TypedStatic); if (!getElemTryTypedStatic(&emitted, obj, index) || emitted) return emitted; + trackOptimizationAttempt(TrackedStrategy::GetElem_TypedArray); if (!getElemTryTypedArray(&emitted, obj, index) || emitted) return emitted; + trackOptimizationAttempt(TrackedStrategy::GetElem_String); if (!getElemTryString(&emitted, obj, index) || emitted) return emitted; + trackOptimizationAttempt(TrackedStrategy::GetElem_Arguments); if (!getElemTryArguments(&emitted, obj, index) || emitted) return emitted; + trackOptimizationAttempt(TrackedStrategy::GetElem_ArgumentsInlined); if (!getElemTryArgumentsInlined(&emitted, obj, index) || emitted) return emitted; if (script()->argumentsHasVarBinding() && obj->mightBeType(MIRType_MagicOptimizedArguments)) return abort("Type is not definitely lazy arguments."); + trackOptimizationAttempt(TrackedStrategy::GetElem_InlineCache); if (!getElemTryCache(&emitted, obj, index) || emitted) return emitted; @@ -7369,6 +7532,10 @@ { MOZ_ASSERT(*emitted == false); + // The next several failures are all due to types not predicting that we + // are definitely doing a getelem access on a typed object. + trackOptimizationOutcome(TrackedOutcome::AccessNotTypedObject); + TypedObjectPrediction objPrediction = typedObjectPrediction(obj); if (objPrediction.isUseless()) return true; @@ -7387,6 +7554,7 @@ switch (elemPrediction.kind()) { case type::Simd: // FIXME (bug 894105): load into a MIRType_float32x4 etc + trackOptimizationOutcome(TrackedOutcome::GenericFailure); return true; case type::Struct: @@ -7441,10 +7609,13 @@ // If we are not loading the length from the object itself, only // optimize if the array buffer can't have been neutered. - types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global()); - if (globalType->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + types::ObjectGroupKey *globalKey = types::ObjectGroupKey::get(&script()->global()); + if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) { + trackOptimizationOutcome(TrackedOutcome::TypedObjectNeutered); return false; + } } else { + trackOptimizationOutcome(TrackedOutcome::TypedObjectArrayRange); return false; } @@ -7471,6 +7642,7 @@ if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset)) return true; + trackOptimizationSuccess(); *emitted = true; return pushScalarLoadFromTypedObject(obj, indexAsByteOffset, elemType); @@ -7492,6 +7664,7 @@ if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset)) return true; + trackOptimizationSuccess(); *emitted = true; return pushReferenceLoadFromTypedObject(obj, indexAsByteOffset, elemType, nullptr); @@ -7552,7 +7725,7 @@ types::TemporaryTypeSet *observedTypes = bytecodeTypes(pc); - MInstruction *load; + MInstruction *load = nullptr; // initialize to silence GCC warning BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), typedObj, name, observedTypes); @@ -7571,9 +7744,13 @@ // there is no other barrier needed we include the null bailout with // MLoadUnboxedObjectOrNull, which avoids the need to box the result // for a type barrier instruction. - bool bailOnNull = barrier == BarrierKind::NoBarrier && - !observedTypes->hasType(types::Type::NullType()); - load = MLoadUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, bailOnNull, adjustment); + MLoadUnboxedObjectOrNull::NullBehavior nullBehavior; + if (barrier == BarrierKind::NoBarrier && !observedTypes->hasType(types::Type::NullType())) + nullBehavior = MLoadUnboxedObjectOrNull::BailOnNull; + else + nullBehavior = MLoadUnboxedObjectOrNull::HandleNull; + load = MLoadUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, nullBehavior, + adjustment); break; } case ReferenceTypeDescr::TYPE_STRING: { @@ -7657,11 +7834,10 @@ const Class *observedClass = observedTypes->getKnownClass(constraints()); JSObject *observedProto = observedTypes->getCommonPrototype(constraints()); - // If expectedClass/expectedProto are both non-null (and hence - // known), we can predict precisely what TI type object - // derivedTypedObj will have. Therefore, if we observe that this - // TI type object is already contained in the set of - // observedTypes, we can skip the barrier. + // If expectedClass/expectedProto are both non-null (and hence known), we + // can predict precisely what object group derivedTypedObj will have. + // Therefore, if we observe that this group is already contained in the set + // of observedTypes, we can skip the barrier. // // Barriers still wind up being needed in some relatively // rare cases: @@ -7684,6 +7860,7 @@ return false; } + trackOptimizationSuccess(); *emitted = true; return true; } @@ -7693,57 +7870,88 @@ { MOZ_ASSERT(*emitted == false); - if (!ElementAccessIsDenseNative(constraints(), obj, index)) + if (!ElementAccessIsDenseNative(constraints(), obj, index)) { + trackOptimizationOutcome(TrackedOutcome::AccessNotDense); return true; + } // Don't generate a fast path if there have been bounds check failures // and this access might be on a sparse property. - if (ElementAccessHasExtraIndexedProperty(constraints(), obj) && failedBoundsCheck_) + if (ElementAccessHasExtraIndexedProperty(constraints(), obj) && failedBoundsCheck_) { + trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); return true; + } // Don't generate a fast path if this pc has seen negative indexes accessed, // which will not appear to be extra indexed properties. - if (inspector->hasSeenNegativeIndexGetElement(pc)) + if (inspector->hasSeenNegativeIndexGetElement(pc)) { + trackOptimizationOutcome(TrackedOutcome::ArraySeenNegativeIndex); return true; + } // Emit dense getelem variant. if (!jsop_getelem_dense(obj, index)) return false; + trackOptimizationSuccess(); *emitted = true; return true; } -bool -IonBuilder::getElemTryTypedStatic(bool *emitted, MDefinition *obj, MDefinition *index) +JSObject * +IonBuilder::getStaticTypedArrayObject(MDefinition *obj, MDefinition *index) { - MOZ_ASSERT(*emitted == false); - Scalar::Type arrayType; - if (!ElementAccessIsAnyTypedArray(constraints(), obj, index, &arrayType)) - return true; + if (!ElementAccessIsAnyTypedArray(constraints(), obj, index, &arrayType)) { + trackOptimizationOutcome(TrackedOutcome::AccessNotTypedArray); + return nullptr; + } - if (!LIRGenerator::allowStaticTypedArrayAccesses()) - return true; + if (!LIRGenerator::allowStaticTypedArrayAccesses()) { + trackOptimizationOutcome(TrackedOutcome::Disabled); + return nullptr; + } - if (ElementAccessHasExtraIndexedProperty(constraints(), obj)) - return true; + if (ElementAccessHasExtraIndexedProperty(constraints(), obj)) { + trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); + return nullptr; + } - if (!obj->resultTypeSet()) - return true; + if (!obj->resultTypeSet()) { + trackOptimizationOutcome(TrackedOutcome::NoTypeInfo); + return nullptr; + } - JSObject *tarrObj = obj->resultTypeSet()->getSingleton(); - if (!tarrObj) - return true; + JSObject *tarrObj = obj->resultTypeSet()->maybeSingleton(); + if (!tarrObj) { + trackOptimizationOutcome(TrackedOutcome::NotSingleton); + return nullptr; + } + + types::ObjectGroupKey *tarrKey = types::ObjectGroupKey::get(tarrObj); + if (tarrKey->unknownProperties()) { + trackOptimizationOutcome(TrackedOutcome::UnknownProperties); + return nullptr; + } + + return tarrObj; +} + +bool +IonBuilder::getElemTryTypedStatic(bool *emitted, MDefinition *obj, MDefinition *index) +{ + MOZ_ASSERT(*emitted == false); - types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarrObj); - if (tarrType->unknownProperties()) + JSObject *tarrObj = getStaticTypedArrayObject(obj, index); + if (!tarrObj) return true; // LoadTypedArrayElementStatic currently treats uint32 arrays as int32. Scalar::Type viewType = AnyTypedArrayType(tarrObj); - if (viewType == Scalar::Uint32) + if (viewType == Scalar::Uint32) { + trackOptimizationOutcome(TrackedOutcome::StaticTypedArrayUint32); return true; + } MDefinition *ptr = convertShiftToMaskForStaticTypedArray(index, viewType); if (!ptr) @@ -7751,8 +7959,10 @@ // Emit LoadTypedArrayElementStatic. - if (tarrObj->is()) - tarrType->watchStateChangeForTypedArrayData(constraints()); + if (tarrObj->is()) { + types::ObjectGroupKey *tarrKey = types::ObjectGroupKey::get(tarrObj); + tarrKey->watchStateChangeForTypedArrayData(constraints()); + } obj->setImplicitlyUsedUnchecked(); index->setImplicitlyUsedUnchecked(); @@ -7776,6 +7986,7 @@ load->setInfallible(); } + trackOptimizationSuccess(); *emitted = true; return true; } @@ -7786,13 +7997,16 @@ MOZ_ASSERT(*emitted == false); Scalar::Type arrayType; - if (!ElementAccessIsAnyTypedArray(constraints(), obj, index, &arrayType)) + if (!ElementAccessIsAnyTypedArray(constraints(), obj, index, &arrayType)) { + trackOptimizationOutcome(TrackedOutcome::AccessNotTypedArray); return true; + } // Emit typed getelem variant. if (!jsop_getelem_typed(obj, index, arrayType)) return false; + trackOptimizationSuccess(); *emitted = true; return true; } @@ -7802,13 +8016,17 @@ { MOZ_ASSERT(*emitted == false); - if (obj->type() != MIRType_String || !IsNumberType(index->type())) + if (obj->type() != MIRType_String || !IsNumberType(index->type())) { + trackOptimizationOutcome(TrackedOutcome::AccessNotString); return true; + } // If the index is expected to be out-of-bounds, don't optimize to avoid // frequent bailouts. - if (bytecodeTypes(pc)->hasType(types::Type::UndefinedType())) + if (bytecodeTypes(pc)->hasType(types::Type::UndefinedType())) { + trackOptimizationOutcome(TrackedOutcome::OutOfBounds); return true; + } // Emit fast path for string[index]. MInstruction *idInt32 = MToInt32::New(alloc(), index); @@ -7827,6 +8045,7 @@ current->add(result); current->push(result); + trackOptimizationSuccess(); *emitted = true; return true; } @@ -7870,6 +8089,7 @@ if (!pushTypeBarrier(load, types, BarrierKind::TypeSet)) return false; + trackOptimizationSuccess(); *emitted = true; return true; } @@ -7902,6 +8122,7 @@ else pushConstant(UndefinedValue()); + trackOptimizationSuccess(); *emitted = true; return true; } @@ -7916,26 +8137,33 @@ MOZ_ASSERT(*emitted == false); // Make sure we have at least an object. - if (!obj->mightBeType(MIRType_Object)) + if (!obj->mightBeType(MIRType_Object)) { + trackOptimizationOutcome(TrackedOutcome::NotObject); return true; + } // Don't cache for strings. - if (obj->mightBeType(MIRType_String)) + if (obj->mightBeType(MIRType_String)) { + trackOptimizationOutcome(TrackedOutcome::GetElemStringNotCached); return true; + } // Index should be integer, string, or symbol if (!index->mightBeType(MIRType_Int32) && !index->mightBeType(MIRType_String) && !index->mightBeType(MIRType_Symbol)) { + trackOptimizationOutcome(TrackedOutcome::IndexType); return true; } // Turn off cacheing if the element is int32 and we've seen non-native objects as the target // of this getelem. bool nonNativeGetElement = inspector->hasSeenNonNativeGetElement(pc); - if (index->mightBeType(MIRType_Int32) && nonNativeGetElement) + if (index->mightBeType(MIRType_Int32) && nonNativeGetElement) { + trackOptimizationOutcome(TrackedOutcome::NonNativeReceiver); return true; + } // Emit GetElementCache. @@ -7968,6 +8196,7 @@ if (!pushTypeBarrier(ins, types, barrier)) return false; + trackOptimizationSuccess(); *emitted = true; return true; } @@ -8072,20 +8301,20 @@ if (obj->isConstantValue() && obj->constantValue().isObject()) tarr = &obj->constantValue().toObject(); else if (obj->resultTypeSet()) - tarr = obj->resultTypeSet()->getSingleton(); + tarr = obj->resultTypeSet()->maybeSingleton(); if (tarr) { void *data = AnyTypedArrayViewData(tarr); // Bug 979449 - Optimistically embed the elements and use TI to // invalidate if we move them. bool isTenured = !tarr->runtimeFromMainThread()->gc.nursery.isInside(data); - if (isTenured && tarr->hasSingletonType()) { + if (isTenured && tarr->isSingleton()) { // The 'data' pointer of TypedArrayObject can change in rare circumstances // (ArrayBufferObject::changeContents). - types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarr); - if (!tarrType->unknownProperties()) { + types::ObjectGroupKey *tarrKey = types::ObjectGroupKey::get(tarr); + if (!tarrKey->unknownProperties()) { if (tarr->is()) - tarrType->watchStateChangeForTypedArrayData(constraints()); + tarrKey->watchStateChangeForTypedArrayData(constraints()); obj->setImplicitlyUsedUnchecked(); @@ -8121,6 +8350,8 @@ IonBuilder::convertShiftToMaskForStaticTypedArray(MDefinition *id, Scalar::Type viewType) { + trackOptimizationOutcome(TrackedOutcome::StaticTypedArrayCantComputeMask); + // No shifting is necessary if the typed array has single byte elements. if (TypedArrayShift(viewType) == 0) return id; @@ -8263,23 +8494,33 @@ IonBuilder::jsop_setelem() { bool emitted = false; + startTrackingOptimizations(); MDefinition *value = current->pop(); MDefinition *index = current->pop(); MDefinition *object = current->pop(); + trackTypeInfo(TrackedTypeSite::Receiver, object->type(), object->resultTypeSet()); + trackTypeInfo(TrackedTypeSite::Index, index->type(), index->resultTypeSet()); + trackTypeInfo(TrackedTypeSite::Value, value->type(), value->resultTypeSet()); + + trackOptimizationAttempt(TrackedStrategy::SetElem_TypedObject); if (!setElemTryTypedObject(&emitted, object, index, value) || emitted) return emitted; + trackOptimizationAttempt(TrackedStrategy::SetElem_TypedStatic); if (!setElemTryTypedStatic(&emitted, object, index, value) || emitted) return emitted; + trackOptimizationAttempt(TrackedStrategy::SetElem_TypedArray); if (!setElemTryTypedArray(&emitted, object, index, value) || emitted) return emitted; + trackOptimizationAttempt(TrackedStrategy::SetElem_Dense); if (!setElemTryDense(&emitted, object, index, value) || emitted) return emitted; + trackOptimizationAttempt(TrackedStrategy::SetElem_Arguments); if (!setElemTryArguments(&emitted, object, index, value) || emitted) return emitted; @@ -8290,6 +8531,7 @@ return abort("Type is not definitely lazy arguments."); } + trackOptimizationAttempt(TrackedStrategy::SetElem_InlineCache); if (!setElemTryCache(&emitted, object, index, value) || emitted) return emitted; @@ -8307,6 +8549,10 @@ { MOZ_ASSERT(*emitted == false); + // The next several failures are all due to types not predicting that we + // are definitely doing a getelem access on a typed object. + trackOptimizationOutcome(TrackedOutcome::AccessNotTypedObject); + TypedObjectPrediction objPrediction = typedObjectPrediction(obj); if (objPrediction.isUseless()) return true; @@ -8325,6 +8571,7 @@ switch (elemPrediction.kind()) { case type::Simd: // FIXME (bug 894105): store a MIRType_float32x4 etc + trackOptimizationOutcome(TrackedOutcome::GenericFailure); return true; case type::Reference: @@ -8343,6 +8590,7 @@ case type::Struct: case type::Array: // Not yet optimized. + trackOptimizationOutcome(TrackedOutcome::GenericFailure); return true; } @@ -8369,6 +8617,7 @@ current->push(value); + trackOptimizationSuccess(); *emitted = true; return true; } @@ -8396,6 +8645,7 @@ current->push(value); + trackOptimizationSuccess(); *emitted = true; return true; } @@ -8406,29 +8656,13 @@ { MOZ_ASSERT(*emitted == false); - Scalar::Type arrayType; - if (!ElementAccessIsAnyTypedArray(constraints(), object, index, &arrayType)) - return true; - - if (!LIRGenerator::allowStaticTypedArrayAccesses()) - return true; - - if (ElementAccessHasExtraIndexedProperty(constraints(), object)) - return true; - - if (!object->resultTypeSet()) - return true; - JSObject *tarrObj = object->resultTypeSet()->getSingleton(); + JSObject *tarrObj = getStaticTypedArrayObject(object, index); if (!tarrObj) return true; if (tarrObj->runtimeFromMainThread()->gc.nursery.isInside(AnyTypedArrayViewData(tarrObj))) return true; - types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarrObj); - if (tarrType->unknownProperties()) - return true; - Scalar::Type viewType = AnyTypedArrayType(tarrObj); MDefinition *ptr = convertShiftToMaskForStaticTypedArray(index, viewType); if (!ptr) @@ -8436,8 +8670,10 @@ // Emit StoreTypedArrayElementStatic. - if (tarrObj->is()) - tarrType->watchStateChangeForTypedArrayData(constraints()); + if (tarrObj->is()) { + types::ObjectGroupKey *tarrKey = types::ObjectGroupKey::get(tarrObj); + tarrKey->watchStateChangeForTypedArrayData(constraints()); + } object->setImplicitlyUsedUnchecked(); index->setImplicitlyUsedUnchecked(); @@ -8456,6 +8692,7 @@ if (!resumeAfter(store)) return false; + trackOptimizationSuccess(); *emitted = true; return true; } @@ -8467,13 +8704,16 @@ MOZ_ASSERT(*emitted == false); Scalar::Type arrayType; - if (!ElementAccessIsAnyTypedArray(constraints(), object, index, &arrayType)) + if (!ElementAccessIsAnyTypedArray(constraints(), object, index, &arrayType)) { + trackOptimizationOutcome(TrackedOutcome::AccessNotTypedArray); return true; + } // Emit typed setelem variant. if (!jsop_setelem_typed(arrayType, SetElem_Normal, object, index, value)) return false; + trackOptimizationSuccess(); *emitted = true; return true; } @@ -8484,15 +8724,22 @@ { MOZ_ASSERT(*emitted == false); - if (!ElementAccessIsDenseNative(constraints(), object, index)) + if (!ElementAccessIsDenseNative(constraints(), object, index)) { + trackOptimizationOutcome(TrackedOutcome::AccessNotDense); return true; + } + if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &object, nullptr, &value, /* canModify = */ true)) { + trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier); return true; } - if (!object->resultTypeSet()) + + if (!object->resultTypeSet()) { + trackOptimizationOutcome(TrackedOutcome::NoTypeInfo); return true; + } types::TemporaryTypeSet::DoubleConversion conversion = object->resultTypeSet()->convertDoubleElements(constraints()); @@ -8501,18 +8748,22 @@ if (conversion == types::TemporaryTypeSet::AmbiguousDoubleConversion && value->type() != MIRType_Int32) { + trackOptimizationOutcome(TrackedOutcome::ArrayDoubleConversion); return true; } // Don't generate a fast path if there have been bounds check failures // and this access might be on a sparse property. - if (ElementAccessHasExtraIndexedProperty(constraints(), object) && failedBoundsCheck_) + if (ElementAccessHasExtraIndexedProperty(constraints(), object) && failedBoundsCheck_) { + trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); return true; + } // Emit dense setelem variant. if (!jsop_setelem_dense(conversion, SetElem_Normal, object, index, value)) return false; + trackOptimizationSuccess(); *emitted = true; return true; } @@ -8536,13 +8787,16 @@ { MOZ_ASSERT(*emitted == false); - if (!object->mightBeType(MIRType_Object)) + if (!object->mightBeType(MIRType_Object)) { + trackOptimizationOutcome(TrackedOutcome::NotObject); return true; + } if (!index->mightBeType(MIRType_Int32) && !index->mightBeType(MIRType_String) && !index->mightBeType(MIRType_Symbol)) { + trackOptimizationOutcome(TrackedOutcome::IndexType); return true; } @@ -8550,12 +8804,15 @@ // Temporary disable the cache if non dense native, // until the cache supports more ics SetElemICInspector icInspect(inspector->setElemICInspector(pc)); - if (!icInspect.sawDenseWrite() && !icInspect.sawTypedArrayWrite()) + if (!icInspect.sawDenseWrite() && !icInspect.sawTypedArrayWrite()) { + trackOptimizationOutcome(TrackedOutcome::SetElemNonDenseNonTANotCached); return true; + } if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &object, nullptr, &value, /* canModify = */ true)) { + trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier); return true; } @@ -8580,6 +8837,7 @@ if (!resumeAfter(ins)) return false; + trackOptimizationSuccess(); *emitted = true; return true; } @@ -8821,8 +9079,8 @@ // Compute the length for array typed objects. TypedObjectPrediction prediction = typedObjectPrediction(obj); if (!prediction.isUseless()) { - types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global()); - if (globalType->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + types::ObjectGroupKey *globalKey = types::ObjectGroupKey::get(&script()->global()); + if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return false; MInstruction *length; @@ -8884,7 +9142,7 @@ current->add(templateConst); MNewArray *array = MNewArray::New(alloc(), constraints(), numRest, templateConst, - templateObject->type()->initialHeap(constraints()), + templateObject->group()->initialHeap(constraints()), NewArray_FullyAllocating); current->add(array); @@ -8932,8 +9190,10 @@ uint32_t IonBuilder::getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name) { - if (!types || types->unknownObject()) + if (!types || types->unknownObject()) { + trackOptimizationOutcome(TrackedOutcome::NoTypeInfo); return UINT32_MAX; + } // Watch for types which the new script properties analysis has not been // performed on yet. Normally this is done after a small number of the @@ -8947,13 +9207,14 @@ // objects, which often have a different number of fixed slots from // subsequent objects. for (size_t i = 0; i < types->getObjectCount(); i++) { - types::TypeObjectKey *type = types->getObject(i); - if (!type) + types::ObjectGroupKey *key = types->getObject(i); + if (!key) continue; - if (types::TypeObject *typeObject = type->maybeType()) { - if (typeObject->newScript() && !typeObject->newScript()->analyzed()) { - addAbortedNewScriptPropertiesType(typeObject); + if (types::ObjectGroup *group = key->maybeGroup()) { + if (group->newScript() && !group->newScript()->analyzed()) { + addAbortedNewScriptPropertiesGroup(group); + trackOptimizationOutcome(TrackedOutcome::NoAnalysisInfo); return UINT32_MAX; } } @@ -8962,31 +9223,93 @@ uint32_t slot = UINT32_MAX; for (size_t i = 0; i < types->getObjectCount(); i++) { - types::TypeObjectKey *type = types->getObject(i); - if (!type) + types::ObjectGroupKey *key = types->getObject(i); + if (!key) continue; - if (type->unknownProperties() || type->singleton()) + if (key->unknownProperties()) { + trackOptimizationOutcome(TrackedOutcome::UnknownProperties); + return UINT32_MAX; + } + + if (key->isSingleton()) { + trackOptimizationOutcome(TrackedOutcome::Singleton); return UINT32_MAX; + } - types::HeapTypeSetKey property = type->property(NameToId(name)); + types::HeapTypeSetKey property = key->property(NameToId(name)); if (!property.maybeTypes() || !property.maybeTypes()->definiteProperty() || property.nonData(constraints())) { + trackOptimizationOutcome(TrackedOutcome::NotFixedSlot); return UINT32_MAX; } uint32_t propertySlot = property.maybeTypes()->definiteSlot(); - if (slot == UINT32_MAX) + if (slot == UINT32_MAX) { slot = propertySlot; - else if (slot != propertySlot) + } else if (slot != propertySlot) { + trackOptimizationOutcome(TrackedOutcome::InconsistentFixedSlot); return UINT32_MAX; + } } return slot; } +uint32_t +IonBuilder::getUnboxedOffset(types::TemporaryTypeSet *types, PropertyName *name, JSValueType *punboxedType) +{ + if (!types || types->unknownObject()) { + trackOptimizationOutcome(TrackedOutcome::NoTypeInfo); + return UINT32_MAX; + } + + uint32_t offset = UINT32_MAX; + + for (size_t i = 0; i < types->getObjectCount(); i++) { + types::ObjectGroupKey *key = types->getObject(i); + if (!key) + continue; + + if (key->unknownProperties()) { + trackOptimizationOutcome(TrackedOutcome::UnknownProperties); + return UINT32_MAX; + } + + if (key->isSingleton()) { + trackOptimizationOutcome(TrackedOutcome::Singleton); + return UINT32_MAX; + } + + UnboxedLayout *layout = key->group()->maybeUnboxedLayout(); + if (!layout) { + trackOptimizationOutcome(TrackedOutcome::NotUnboxed); + return UINT32_MAX; + } + + const UnboxedLayout::Property *property = layout->lookup(name); + if (!property) { + trackOptimizationOutcome(TrackedOutcome::StructNoField); + return UINT32_MAX; + } + + if (offset == UINT32_MAX) { + offset = property->offset; + *punboxedType = property->type; + } else if (offset != property->offset) { + trackOptimizationOutcome(TrackedOutcome::InconsistentFieldOffset); + return UINT32_MAX; + } else if (*punboxedType != property->type) { + trackOptimizationOutcome(TrackedOutcome::InconsistentFieldType); + return UINT32_MAX; + } + } + + return offset; +} + bool IonBuilder::jsop_runonce() { @@ -9022,21 +9345,21 @@ *guardGlobal = false; for (unsigned i = 0; i < types->getObjectCount(); i++) { - if (types->getSingleObject(i) == foundProto) + if (types->getSingleton(i) == foundProto) continue; - types::TypeObjectKey *type = types->getObject(i); - if (!type) + types::ObjectGroupKey *key = types->getObject(i); + if (!key) continue; - while (type) { - if (type->unknownProperties()) + while (key) { + if (key->unknownProperties()) return false; - const Class *clasp = type->clasp(); + const Class *clasp = key->clasp(); if (!ClassHasEffectlessLookup(clasp, name)) return false; - JSObject *singleton = type->singleton(); + JSObject *singleton = key->isSingleton() ? key->singleton() : nullptr; if (ClassHasResolveHook(compartment, clasp, name)) { if (!singleton || !singleton->is()) return false; @@ -9044,17 +9367,17 @@ } // Look for a getter/setter on the class itself which may need - // to be called. Ignore the getGeneric hook for typed arrays, it + // to be called. Ignore the getProperty op for typed arrays, it // only handles integers and forwards names to the prototype. - if (isGetter && clasp->ops.getGeneric && !IsAnyTypedArrayClass(clasp)) + if (isGetter && clasp->ops.getProperty && !IsAnyTypedArrayClass(clasp)) return false; - if (!isGetter && clasp->ops.setGeneric) + if (!isGetter && clasp->ops.setProperty) return false; // Test for isOwnProperty() without freezing. If we end up // optimizing, freezePropertiesForCommonPropFunc will freeze the // property type sets later on. - types::HeapTypeSetKey property = type->property(NameToId(name)); + types::HeapTypeSetKey property = key->property(NameToId(name)); if (types::TypeSet *types = property.maybeTypes()) { if (!types->empty() || types->nonDataProperty()) return false; @@ -9066,9 +9389,7 @@ } } - if (!type->hasTenuredProto()) - return false; - JSObject *proto = type->proto().toObjectOrNull(); + JSObject *proto = key->protoMaybeInNursery().toObjectOrNull(); if (proto == foundProto) break; if (!proto) { @@ -9076,7 +9397,7 @@ // object's prototype chain. return false; } - type = types::TypeObjectKey::get(type->proto().toObjectOrNull()); + key = types::ObjectGroupKey::get(proto); } } @@ -9091,28 +9412,28 @@ for (unsigned i = 0; i < types->getObjectCount(); i++) { // If we found a Singleton object's own-property, there's nothing to // freeze. - if (types->getSingleObject(i) == foundProto) + if (types->getSingleton(i) == foundProto) continue; - types::TypeObjectKey *type = types->getObject(i); - if (!type) + types::ObjectGroupKey *key = types->getObject(i); + if (!key) continue; while (true) { - types::HeapTypeSetKey property = type->property(NameToId(name)); + types::HeapTypeSetKey property = key->property(NameToId(name)); JS_ALWAYS_TRUE(!property.isOwnProperty(constraints(), allowEmptyTypesforGlobal)); // Don't mark the proto. It will be held down by the shape // guard. This allows us to use properties found on prototypes // with properties unknown to TI. - if (type->proto() == TaggedProto(foundProto)) + if (key->protoMaybeInNursery() == TaggedProto(foundProto)) break; - type = types::TypeObjectKey::get(type->proto().toObjectOrNull()); + key = types::ObjectGroupKey::get(key->protoMaybeInNursery().toObjectOrNull()); } } } -inline bool +bool IonBuilder::testCommonGetterSetter(types::TemporaryTypeSet *types, PropertyName *name, bool isGetter, JSObject *foundProto, Shape *lastProperty, MDefinition **guard, @@ -9126,6 +9447,7 @@ if (!objectsHaveCommonPrototype(types, name, isGetter, foundProto, &guardGlobal) || (guardGlobal && !globalShape)) { + trackOptimizationOutcome(TrackedOutcome::MultiProtoPaths); return false; } @@ -9153,7 +9475,7 @@ return true; } - MInstruction *wrapper = constant(ObjectValue(*foundProto)); + MInstruction *wrapper = constantMaybeNursery(foundProto); *guard = addShapeGuard(wrapper, lastProperty, Bailout_ShapeGuard); return true; } @@ -9178,7 +9500,7 @@ return true; for (unsigned i = 0; i < pushedTypes->getObjectCount(); i++) { - if (pushedTypes->getTypeObject(i) != nullptr) + if (pushedTypes->getGroup(i) != nullptr) return true; } @@ -9194,25 +9516,25 @@ if (!inlinePropTable) return false; - // Ensure that the relevant property typeset for each type object is + // Ensure that the relevant property typeset for each group is // is a single-object typeset containing a JSFunction for (unsigned int i = 0; i < objCount; i++) { - types::TypeObject *baseTypeObj = objTypes->getTypeObject(i); - if (!baseTypeObj) + types::ObjectGroup *group = objTypes->getGroup(i); + if (!group) continue; - types::TypeObjectKey *typeObj = types::TypeObjectKey::get(baseTypeObj); - if (typeObj->unknownProperties() || !typeObj->hasTenuredProto() || !typeObj->proto().isObject()) + types::ObjectGroupKey *key = types::ObjectGroupKey::get(group); + if (key->unknownProperties() || !key->hasTenuredProto() || !key->proto().isObject()) continue; - const Class *clasp = typeObj->clasp(); + const Class *clasp = key->clasp(); if (!ClassHasEffectlessLookup(clasp, name) || ClassHasResolveHook(compartment, clasp, name)) continue; - types::HeapTypeSetKey ownTypes = typeObj->property(NameToId(name)); + types::HeapTypeSetKey ownTypes = key->property(NameToId(name)); if (ownTypes.isOwnProperty(constraints())) continue; - JSObject *singleton = testSingletonProperty(typeObj->proto().toObject(), name); + JSObject *singleton = testSingletonProperty(key->proto().toObject(), name); if (!singleton || !singleton->is()) continue; @@ -9220,7 +9542,7 @@ if (!pushedTypes->hasType(types::Type::ObjectType(singleton))) continue; - if (!inlinePropTable->addEntry(alloc(), baseTypeObj, &singleton->as())) + if (!inlinePropTable->addEntry(alloc(), group, &singleton->as())) return false; } @@ -9338,19 +9660,23 @@ IonBuilder::jsop_getprop(PropertyName *name) { bool emitted = false; + startTrackingOptimizations(); MDefinition *obj = current->pop(); types::TemporaryTypeSet *types = bytecodeTypes(pc); + trackTypeInfo(TrackedTypeSite::Receiver, obj->type(), obj->resultTypeSet()); + if (!info().isAnalysis()) { // The calls below can abort compilation, so we only try this if we're // not analyzing. - // Try to optimize arguments.length. + trackOptimizationAttempt(TrackedStrategy::GetProp_ArgumentsLength); if (!getPropTryArgumentsLength(&emitted, obj) || emitted) return emitted; // Try to optimize arguments.callee. + trackOptimizationAttempt(TrackedStrategy::GetProp_ArgumentsCallee); if (!getPropTryArgumentsCallee(&emitted, obj, name) || emitted) return emitted; } @@ -9359,9 +9685,12 @@ obj, name, types); // Try to optimize to a specific constant. + trackOptimizationAttempt(TrackedStrategy::GetProp_InferredConstant); if (barrier == BarrierKind::NoBarrier) { if (!getPropTryInferredConstant(&emitted, obj, name, types) || emitted) return emitted; + } else { + trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier); } // Always use a call if we are performing analysis and @@ -9369,6 +9698,14 @@ // analysis if there are no known types for this operation, as it will // always invalidate when executing. if (info().isAnalysis() || types->empty()) { + if (types->empty()) { + // Since no further optimizations will be tried, use the IC + // strategy, which would have been the last one to be tried, as a + // sentinel value for why everything failed. + trackOptimizationAttempt(TrackedStrategy::GetProp_InlineCache); + trackOptimizationOutcome(TrackedOutcome::NoTypeInfo); + } + MCallGetProperty *call = MCallGetProperty::New(alloc(), obj, name, *pc == JSOP_CALLPROP); current->add(call); @@ -9386,30 +9723,42 @@ } // Try to hardcode known constants. + trackOptimizationAttempt(TrackedStrategy::GetProp_Constant); if (!getPropTryConstant(&emitted, obj, name, types) || emitted) return emitted; // Try to emit loads from known binary data blocks + trackOptimizationAttempt(TrackedStrategy::GetProp_TypedObject); if (!getPropTryTypedObject(&emitted, obj, name) || emitted) return emitted; // Try to emit loads from definite slots. + trackOptimizationAttempt(TrackedStrategy::GetProp_DefiniteSlot); if (!getPropTryDefiniteSlot(&emitted, obj, name, barrier, types) || emitted) return emitted; + // Try to emit loads from unboxed objects. + trackOptimizationAttempt(TrackedStrategy::GetProp_Unboxed); + if (!getPropTryUnboxed(&emitted, obj, name, barrier, types) || emitted) + return emitted; + // Try to inline a common property getter, or make a call. + trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter); if (!getPropTryCommonGetter(&emitted, obj, name, types) || emitted) return emitted; // Try to emit a monomorphic/polymorphic access based on baseline caches. + trackOptimizationAttempt(TrackedStrategy::GetProp_InlineAccess); if (!getPropTryInlineAccess(&emitted, obj, name, barrier, types) || emitted) return emitted; // Try to optimize accesses on outer window proxies, for example window.foo. + trackOptimizationAttempt(TrackedStrategy::GetProp_Innerize); if (!getPropTryInnerize(&emitted, obj, name, types) || emitted) return emitted; // Try to emit a polymorphic cache. + trackOptimizationAttempt(TrackedStrategy::GetProp_InlineCache); if (!getPropTryCache(&emitted, obj, name, barrier, types) || emitted) return emitted; @@ -9449,18 +9798,24 @@ // Need a result typeset to optimize. types::TemporaryTypeSet *objTypes = obj->resultTypeSet(); - if (!objTypes) + if (!objTypes) { + trackOptimizationOutcome(TrackedOutcome::NoTypeInfo); return true; + } - JSObject *singleton = objTypes->getSingleton(); - if (!singleton) + JSObject *singleton = objTypes->maybeSingleton(); + if (!singleton) { + trackOptimizationOutcome(TrackedOutcome::NotSingleton); return true; + } - types::TypeObjectKey *type = types::TypeObjectKey::get(singleton); - if (type->unknownProperties()) + types::ObjectGroupKey *key = types::ObjectGroupKey::get(singleton); + if (key->unknownProperties()) { + trackOptimizationOutcome(TrackedOutcome::UnknownProperties); return true; + } - types::HeapTypeSetKey property = type->property(NameToId(name)); + types::HeapTypeSetKey property = key->property(NameToId(name)); Value constantValue = UndefinedValue(); if (property.constant(constraints(), &constantValue)) { @@ -9469,6 +9824,7 @@ if (!pushConstant(constantValue)) return false; types->addType(types::GetValueType(constantValue), alloc_->lifoAlloc()); + trackOptimizationSuccess(); *emitted = true; } @@ -9489,6 +9845,7 @@ if (JSOp(*pc) != JSOP_LENGTH) return true; + trackOptimizationSuccess(); *emitted = true; obj->setImplicitlyUsedUnchecked(); @@ -9524,6 +9881,7 @@ obj->setImplicitlyUsedUnchecked(); current->push(getCallee()); + trackOptimizationSuccess(); *emitted = true; return true; } @@ -9533,9 +9891,12 @@ types::TemporaryTypeSet *types) { MOZ_ASSERT(*emitted == false); - JSObject *singleton = types ? types->getSingleton() : nullptr; - if (!singleton) + + JSObject *singleton = types ? types->maybeSingleton() : nullptr; + if (!singleton) { + trackOptimizationOutcome(TrackedOutcome::NotSingleton); return true; + } bool testObject, testString; if (!testSingletonPropertyTypes(obj, singleton, name, &testObject, &testString)) @@ -9552,6 +9913,7 @@ pushConstant(ObjectValue(*singleton)); + trackOptimizationSuccess(); *emitted = true; return true; } @@ -9606,10 +9968,11 @@ Scalar::Type fieldType = fieldPrediction.scalarType(); // Don't optimize if the typed object might be neutered. - types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global()); - if (globalType->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + types::ObjectGroupKey *globalKey = types::ObjectGroupKey::get(&script()->global()); + if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return true; + trackOptimizationSuccess(); *emitted = true; LinearSum byteOffset(alloc()); @@ -9627,10 +9990,11 @@ { ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType(); - types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global()); - if (globalType->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + types::ObjectGroupKey *globalKey = types::ObjectGroupKey::get(&script()->global()); + if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return true; + trackOptimizationSuccess(); *emitted = true; LinearSum byteOffset(alloc()); @@ -9648,8 +10012,8 @@ size_t fieldIndex) { // Don't optimize if the typed object might be neutered. - types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global()); - if (globalType->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + types::ObjectGroupKey *globalKey = types::ObjectGroupKey::get(&script()->global()); + if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return true; // OK, perform the optimization @@ -9671,6 +10035,7 @@ BarrierKind barrier, types::TemporaryTypeSet *types) { MOZ_ASSERT(*emitted == false); + uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), name); if (slot == UINT32_MAX) return true; @@ -9700,6 +10065,94 @@ if (!pushTypeBarrier(load, types, barrier)) return false; + trackOptimizationSuccess(); + *emitted = true; + return true; +} + +MInstruction * +IonBuilder::loadUnboxedProperty(MDefinition *obj, size_t offset, JSValueType unboxedType, + BarrierKind barrier, types::TemporaryTypeSet *types) +{ + size_t scaledOffsetConstant = offset / UnboxedTypeSize(unboxedType); + MInstruction *scaledOffset = MConstant::New(alloc(), Int32Value(scaledOffsetConstant)); + current->add(scaledOffset); + + MInstruction *load; + switch (unboxedType) { + case JSVAL_TYPE_BOOLEAN: + load = MLoadTypedArrayElement::New(alloc(), obj, scaledOffset, Scalar::Uint8, + DoesNotRequireMemoryBarrier, + UnboxedPlainObject::offsetOfData()); + load->setResultType(MIRType_Boolean); + break; + + case JSVAL_TYPE_INT32: + load = MLoadTypedArrayElement::New(alloc(), obj, scaledOffset, Scalar::Int32, + DoesNotRequireMemoryBarrier, + UnboxedPlainObject::offsetOfData()); + load->setResultType(MIRType_Int32); + break; + + case JSVAL_TYPE_DOUBLE: + load = MLoadTypedArrayElement::New(alloc(), obj, scaledOffset, Scalar::Float64, + DoesNotRequireMemoryBarrier, + UnboxedPlainObject::offsetOfData(), + /* canonicalizeDoubles = */ false); + load->setResultType(MIRType_Double); + break; + + case JSVAL_TYPE_STRING: + load = MLoadUnboxedString::New(alloc(), obj, scaledOffset, + UnboxedPlainObject::offsetOfData()); + break; + + case JSVAL_TYPE_OBJECT: { + MLoadUnboxedObjectOrNull::NullBehavior nullBehavior; + if (types->hasType(types::Type::NullType()) || barrier != BarrierKind::NoBarrier) + nullBehavior = MLoadUnboxedObjectOrNull::HandleNull; + else + nullBehavior = MLoadUnboxedObjectOrNull::NullNotPossible; + load = MLoadUnboxedObjectOrNull::New(alloc(), obj, scaledOffset, nullBehavior, + UnboxedPlainObject::offsetOfData()); + break; + } + + default: + MOZ_CRASH(); + } + + current->add(load); + return load; +} + +bool +IonBuilder::getPropTryUnboxed(bool *emitted, MDefinition *obj, PropertyName *name, + BarrierKind barrier, types::TemporaryTypeSet *types) +{ + MOZ_ASSERT(*emitted == false); + + JSValueType unboxedType; + uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), name, &unboxedType); + if (offset == UINT32_MAX) + return true; + + if (obj->type() != MIRType_Object) { + MGuardObject *guard = MGuardObject::New(alloc(), obj); + current->add(guard); + obj = guard; + } + + if (unboxedType != JSVAL_TYPE_OBJECT) + barrier = BarrierKind::NoBarrier; + + MInstruction *load = loadUnboxedProperty(obj, offset, unboxedType, barrier, types); + current->push(load); + + if (!pushTypeBarrier(load, types, barrier)) + return false; + + trackOptimizationSuccess(); *emitted = true; return true; } @@ -9736,7 +10189,7 @@ // If our object is a singleton and we know the property is // constant (which is true if and only if the get doesn't alias // anything), we can just read the slot here and use that constant. - JSObject *singleton = objTypes->getSingleton(); + JSObject *singleton = objTypes->maybeSingleton(); if (singleton && jitinfo->aliasSet() == JSJitInfo::AliasNone) { size_t slot = jitinfo->slotIndex; *emitted = true; @@ -9761,6 +10214,7 @@ if (!pushDOMTypeBarrier(get, types, commonGetter)) return false; + trackOptimizationOutcome(TrackedOutcome::DOM); *emitted = true; return true; } @@ -9777,7 +10231,7 @@ // Make sure there's enough room if (!current->ensureHasSlots(2)) return false; - pushConstant(ObjectValue(*commonGetter)); + current->push(constantMaybeNursery(commonGetter)); current->push(obj); @@ -9794,13 +10248,13 @@ case InliningStatus_NotInlined: break; case InliningStatus_Inlined: + trackOptimizationOutcome(TrackedOutcome::Inlined); *emitted = true; return true; } } // Inline if we can, otherwise, forget it and just generate a call. - bool inlineable = false; if (commonGetter->isInterpreted()) { InliningDecision decision = makeInliningDecision(commonGetter, callInfo); switch (decision) { @@ -9810,33 +10264,45 @@ case InliningDecision_WarmUpCountTooLow: break; case InliningDecision_Inline: - inlineable = true; - break; + if (!inlineScriptedCall(callInfo, commonGetter)) + return false; + *emitted = true; + return true; } } - if (inlineable) { - if (!inlineScriptedCall(callInfo, commonGetter)) - return false; - } else { - if (!makeCall(commonGetter, callInfo, false)) - return false; - } + JSFunction *tenuredCommonGetter = IsInsideNursery(commonGetter) ? nullptr : commonGetter; + if (!makeCall(tenuredCommonGetter, callInfo, false)) + return false; + + // If the getter could have been inlined, don't track success. The call to + // makeInliningDecision above would have tracked a specific reason why we + // couldn't inline. + if (!commonGetter->isInterpreted()) + trackOptimizationSuccess(); *emitted = true; return true; } -static bool -CanInlinePropertyOpShapes(const BaselineInspector::ShapeVector &shapes) +bool +IonBuilder::canInlinePropertyOpShapes(const BaselineInspector::ShapeVector &nativeShapes, + const BaselineInspector::ObjectGroupVector &unboxedGroups) { - for (size_t i = 0; i < shapes.length(); i++) { + if (nativeShapes.empty() && unboxedGroups.empty()) { + trackOptimizationOutcome(TrackedOutcome::NoShapeInfo); + return false; + } + + for (size_t i = 0; i < nativeShapes.length(); i++) { // We inline the property access as long as the shape is not in // dictionary mode. We cannot be sure that the shape is still a // lastProperty, and calling Shape::search() on dictionary mode // shapes that aren't lastProperty is invalid. - if (shapes[i]->inDictionary()) + if (nativeShapes[i]->inDictionary()) { + trackOptimizationOutcome(TrackedOutcome::InDictionaryMode); return false; + } } return true; @@ -9846,7 +10312,6 @@ GetPropertyShapes(jsid id, const BaselineInspector::ShapeVector &shapes, BaselineInspector::ShapeVector &propShapes, bool *sameSlot) { - MOZ_ASSERT(shapes.length() > 1); MOZ_ASSERT(propShapes.empty()); if (!propShapes.reserve(shapes.length())) @@ -9876,26 +10341,30 @@ BarrierKind barrier, types::TemporaryTypeSet *types) { MOZ_ASSERT(*emitted == false); - if (obj->type() != MIRType_Object) + + if (obj->type() != MIRType_Object) { + trackOptimizationOutcome(TrackedOutcome::NotObject); return true; + } - BaselineInspector::ShapeVector shapes(alloc()); - if (!inspector->maybeShapesForPropertyOp(pc, shapes)) + BaselineInspector::ShapeVector nativeShapes(alloc()); + BaselineInspector::ObjectGroupVector unboxedGroups(alloc()); + if (!inspector->maybeInfoForPropertyOp(pc, nativeShapes, unboxedGroups)) return false; - if (shapes.empty() || !CanInlinePropertyOpShapes(shapes)) + if (!canInlinePropertyOpShapes(nativeShapes, unboxedGroups)) return true; MIRType rvalType = types->getKnownMIRType(); if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType)) rvalType = MIRType_Value; - if (shapes.length() == 1) { + if (nativeShapes.length() == 1 && unboxedGroups.empty()) { // In the monomorphic case, use separate ShapeGuard and LoadSlot // instructions. spew("Inlining monomorphic GETPROP"); - Shape *objShape = shapes[0]; + Shape *objShape = nativeShapes[0]; obj = addShapeGuard(obj, objShape, Bailout_ShapeGuard); Shape *shape = objShape->searchLinear(NameToId(name)); @@ -9904,19 +10373,44 @@ if (!loadSlot(obj, shape, rvalType, barrier, types)) return false; + trackOptimizationOutcome(TrackedOutcome::Monomorphic); + *emitted = true; + return true; + } + + if (nativeShapes.empty() && unboxedGroups.length() == 1) { + spew("Inlining monomorphic unboxed GETPROP"); + + types::ObjectGroup *group = unboxedGroups[0]; + + // Failures in this group guard should be treated the same as a shape guard failure. + obj = MGuardObjectGroup::New(alloc(), obj, group, /* bailOnEquality = */ false, + Bailout_ShapeGuard); + current->add(obj->toInstruction()); + + if (failedShapeGuard_) + obj->toGuardObjectGroup()->setNotMovable(); + + const UnboxedLayout::Property *property = group->unboxedLayout().lookup(name); + MInstruction *load = loadUnboxedProperty(obj, property->offset, property->type, barrier, types); + current->push(load); + + if (!pushTypeBarrier(load, types, barrier)) + return false; + *emitted = true; return true; } - MOZ_ASSERT(shapes.length() > 1); + MOZ_ASSERT(nativeShapes.length() + unboxedGroups.length() > 1); spew("Inlining polymorphic GETPROP"); BaselineInspector::ShapeVector propShapes(alloc()); bool sameSlot; - if (!GetPropertyShapes(NameToId(name), shapes, propShapes, &sameSlot)) + if (!GetPropertyShapes(NameToId(name), nativeShapes, propShapes, &sameSlot)) return false; - if (sameSlot) { + if (sameSlot && unboxedGroups.empty()) { MGuardShapePolymorphic *guard = MGuardShapePolymorphic::New(alloc(), obj); current->add(guard); obj = guard; @@ -9924,14 +10418,15 @@ if (failedShapeGuard_) guard->setNotMovable(); - for (size_t i = 0; i < shapes.length(); i++) { - if (!guard->addShape(shapes[i])) + for (size_t i = 0; i < nativeShapes.length(); i++) { + if (!guard->addShape(nativeShapes[i])) return false; } if (!loadSlot(obj, propShapes[0], rvalType, barrier, types)) return false; + trackOptimizationOutcome(TrackedOutcome::Polymorphic); *emitted = true; return true; } @@ -9940,8 +10435,13 @@ current->add(load); current->push(load); - for (size_t i = 0; i < shapes.length(); i++) { - if (!load->addShape(shapes[i], propShapes[i])) + for (size_t i = 0; i < nativeShapes.length(); i++) { + if (!load->addShape(nativeShapes[i], propShapes[i])) + return false; + } + + for (size_t i = 0; i < unboxedGroups.length(); i++) { + if (!load->addUnboxedGroup(unboxedGroups[i])) return false; } @@ -9952,6 +10452,7 @@ if (!pushTypeBarrier(load, types, barrier)) return false; + trackOptimizationOutcome(TrackedOutcome::Polymorphic); *emitted = true; return true; } @@ -9966,8 +10467,10 @@ // that it can be safely unboxed to an object. if (obj->type() != MIRType_Object) { types::TemporaryTypeSet *types = obj->resultTypeSet(); - if (!types || !types->objectOrSentinel()) + if (!types || !types->objectOrSentinel()) { + trackOptimizationOutcome(TrackedOutcome::NoTypeInfo); return true; + } } // Since getters have no guaranteed return values, we must barrier in order to be @@ -10005,7 +10508,7 @@ // If this GetPropertyCache is idempotent, then we can dispatch to the right // function only by checking the typed object, instead of querying the value // of the property. Thus this GetPropertyCache can be moved into the - // fallback path (see inlineTypeObjectFallback). Otherwise, we always have + // fallback path (see inlineObjectGroupFallback). Otherwise, we always have // to do the GetPropertyCache, and we can dispatch based on the JSFunction // value. if (JSOp(*pc) == JSOP_CALLPROP && load->idempotent()) { @@ -10027,6 +10530,7 @@ if (!pushTypeBarrier(load, types, barrier)) return false; + trackOptimizationSuccess(); *emitted = true; return true; } @@ -10047,7 +10551,7 @@ if (!types) return obj; - JSObject *singleton = types->getSingleton(); + JSObject *singleton = types->maybeSingleton(); if (!singleton) return obj; @@ -10056,10 +10560,10 @@ return obj; // When we navigate, the outer object is brain transplanted and we'll mark - // its TypeObject as having unknown properties. The type constraint we add + // its ObjectGroup as having unknown properties. The type constraint we add // here will invalidate JIT code when this happens. - types::TypeObjectKey *objType = types::TypeObjectKey::get(singleton); - if (objType->hasFlags(constraints(), types::OBJECT_FLAG_UNKNOWN_PROPERTIES)) + types::ObjectGroupKey *key = types::ObjectGroupKey::get(singleton); + if (key->hasFlags(constraints(), types::OBJECT_FLAG_UNKNOWN_PROPERTIES)) return obj; obj->setImplicitlyUsedUnchecked(); @@ -10109,6 +10613,9 @@ MDefinition *obj = current->pop(); bool emitted = false; + startTrackingOptimizations(); + trackTypeInfo(TrackedTypeSite::Receiver, obj->type(), obj->resultTypeSet()); + trackTypeInfo(TrackedTypeSite::Value, value->type(), value->resultTypeSet()); // Always use a call if we are doing the definite properties analysis and // not actually emitting code, to simplify later analysis. @@ -10120,15 +10627,13 @@ return resumeAfter(ins); } - // Add post barrier if needed. - if (NeedsPostBarrier(info(), value)) - current->add(MPostWriteBarrier::New(alloc(), obj, value)); - // Try to inline a common property setter, or make a call. + trackOptimizationAttempt(TrackedStrategy::SetProp_CommonSetter); if (!setPropTryCommonSetter(&emitted, obj, name, value) || emitted) return emitted; // Try to emit stores to known binary data blocks + trackOptimizationAttempt(TrackedStrategy::SetProp_TypedObject); if (!setPropTryTypedObject(&emitted, obj, name, value) || emitted) return emitted; @@ -10136,15 +10641,25 @@ bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value, /* canModify = */ true); - if (!barrier) { - // Try to emit store from definite slots. - if (!setPropTryDefiniteSlot(&emitted, obj, name, value, objTypes) || emitted) - return emitted; + // Try to emit stores to unboxed objects. + trackOptimizationAttempt(TrackedStrategy::SetProp_Unboxed); + if (!setPropTryUnboxed(&emitted, obj, name, value, barrier, objTypes) || emitted) + return emitted; - // Try to emit a monomorphic/polymorphic store based on baseline caches. - if (!setPropTryInlineAccess(&emitted, obj, name, value, objTypes) || emitted) - return emitted; - } + // Add post barrier if needed. The instructions above manage any post + // barriers they need directly. + if (NeedsPostBarrier(info(), value)) + current->add(MPostWriteBarrier::New(alloc(), obj, value)); + + // Try to emit store from definite slots. + trackOptimizationAttempt(TrackedStrategy::SetProp_DefiniteSlot); + if (!setPropTryDefiniteSlot(&emitted, obj, name, value, barrier, objTypes) || emitted) + return emitted; + + // Try to emit a monomorphic/polymorphic store based on baseline caches. + trackOptimizationAttempt(TrackedStrategy::SetProp_InlineAccess); + if (!setPropTryInlineAccess(&emitted, obj, name, value, barrier, objTypes) || emitted) + return emitted; // Emit a polymorphic cache. return setPropTryCache(&emitted, obj, name, value, barrier, objTypes); @@ -10159,8 +10674,10 @@ Shape *lastProperty = nullptr; JSFunction *commonSetter = nullptr; JSObject *foundProto = inspector->commonSetPropFunction(pc, &lastProperty, &commonSetter); - if (!foundProto) + if (!foundProto) { + trackOptimizationOutcome(TrackedOutcome::NoProtoFound); return true; + } types::TemporaryTypeSet *objTypes = obj->resultTypeSet(); MDefinition *guard = nullptr; @@ -10182,8 +10699,10 @@ if (!setPropTryCommonDOMSetter(emitted, obj, value, commonSetter, isDOM)) return false; - if (*emitted) + if (*emitted) { + trackOptimizationOutcome(TrackedOutcome::DOM); return true; + } // Don't call the setter with a primitive value. if (objTypes->getKnownMIRType() != MIRType_Object) { @@ -10197,8 +10716,7 @@ if (!current->ensureHasSlots(3)) return false; - pushConstant(ObjectValue(*commonSetter)); - + current->push(constantMaybeNursery(commonSetter)); current->push(obj); current->push(value); @@ -10228,7 +10746,8 @@ } } - MCall *call = makeCallHelper(commonSetter, callInfo, false); + JSFunction *tenuredCommonSetter = IsInsideNursery(commonSetter) ? nullptr : commonSetter; + MCall *call = makeCallHelper(tenuredCommonSetter, callInfo, false); if (!call) return false; @@ -10236,6 +10755,12 @@ if (!resumeAfter(call)) return false; + // If the setter could have been inlined, don't track success. The call to + // makeInliningDecision above would have tracked a specific reason why we + // couldn't inline. + if (!commonSetter->isInterpreted()) + trackOptimizationSuccess(); + *emitted = true; return true; } @@ -10309,8 +10834,8 @@ { ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType(); - types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global()); - if (globalType->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + types::ObjectGroupKey *globalKey = types::ObjectGroupKey::get(&script()->global()); + if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return true; LinearSum byteOffset(alloc()); @@ -10322,6 +10847,7 @@ current->push(value); + trackOptimizationSuccess(); *emitted = true; return true; } @@ -10337,8 +10863,8 @@ Scalar::Type fieldType = fieldPrediction.scalarType(); // Don't optimize if the typed object might be neutered. - types::TypeObjectKey *globalType = types::TypeObjectKey::get(&script()->global()); - if (globalType->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + types::ObjectGroupKey *globalKey = types::ObjectGroupKey::get(&script()->global()); + if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return true; LinearSum byteOffset(alloc()); @@ -10350,6 +10876,7 @@ current->push(value); + trackOptimizationSuccess(); *emitted = true; return true; } @@ -10357,23 +10884,30 @@ bool IonBuilder::setPropTryDefiniteSlot(bool *emitted, MDefinition *obj, PropertyName *name, MDefinition *value, - types::TemporaryTypeSet *objTypes) + bool barrier, types::TemporaryTypeSet *objTypes) { MOZ_ASSERT(*emitted == false); + if (barrier) { + trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier); + return true; + } + uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), name); if (slot == UINT32_MAX) return true; bool writeBarrier = false; for (size_t i = 0; i < obj->resultTypeSet()->getObjectCount(); i++) { - types::TypeObjectKey *type = obj->resultTypeSet()->getObject(i); - if (!type) + types::ObjectGroupKey *key = obj->resultTypeSet()->getObject(i); + if (!key) continue; - types::HeapTypeSetKey property = type->property(NameToId(name)); - if (property.nonWritable(constraints())) + types::HeapTypeSetKey property = key->property(NameToId(name)); + if (property.nonWritable(constraints())) { + trackOptimizationOutcome(TrackedOutcome::NonWritableProperty); return true; + } writeBarrier |= property.needsBarrier(constraints()); } @@ -10397,35 +10931,119 @@ if (!resumeAfter(store)) return false; + trackOptimizationSuccess(); *emitted = true; return true; } +MInstruction * +IonBuilder::storeUnboxedProperty(MDefinition *obj, size_t offset, JSValueType unboxedType, + MDefinition *value) +{ + size_t scaledOffsetConstant = offset / UnboxedTypeSize(unboxedType); + MInstruction *scaledOffset = MConstant::New(alloc(), Int32Value(scaledOffsetConstant)); + current->add(scaledOffset); + + MInstruction *store; + switch (unboxedType) { + case JSVAL_TYPE_BOOLEAN: + store = MStoreTypedArrayElement::New(alloc(), obj, scaledOffset, value, Scalar::Uint8, + DoesNotRequireMemoryBarrier, + UnboxedPlainObject::offsetOfData()); + break; + + case JSVAL_TYPE_INT32: + store = MStoreTypedArrayElement::New(alloc(), obj, scaledOffset, value, Scalar::Int32, + DoesNotRequireMemoryBarrier, + UnboxedPlainObject::offsetOfData()); + break; + + case JSVAL_TYPE_DOUBLE: + store = MStoreTypedArrayElement::New(alloc(), obj, scaledOffset, value, Scalar::Float64, + DoesNotRequireMemoryBarrier, + UnboxedPlainObject::offsetOfData()); + break; + + case JSVAL_TYPE_STRING: + store = MStoreUnboxedString::New(alloc(), obj, scaledOffset, value, + UnboxedPlainObject::offsetOfData()); + break; + + case JSVAL_TYPE_OBJECT: + store = MStoreUnboxedObjectOrNull::New(alloc(), obj, scaledOffset, value, obj, + UnboxedPlainObject::offsetOfData()); + break; + + default: + MOZ_CRASH(); + } + + current->add(store); + return store; +} + bool -IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj, - PropertyName *name, MDefinition *value, - types::TemporaryTypeSet *objTypes) +IonBuilder::setPropTryUnboxed(bool *emitted, MDefinition *obj, + PropertyName *name, MDefinition *value, + bool barrier, types::TemporaryTypeSet *objTypes) { MOZ_ASSERT(*emitted == false); - BaselineInspector::ShapeVector shapes(alloc()); - if (!inspector->maybeShapesForPropertyOp(pc, shapes)) + if (barrier) { + trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier); + return true; + } + + JSValueType unboxedType; + uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), name, &unboxedType); + if (offset == UINT32_MAX) + return true; + + if (obj->type() != MIRType_Object) { + MGuardObject *guard = MGuardObject::New(alloc(), obj); + current->add(guard); + obj = guard; + } + + MInstruction *store = storeUnboxedProperty(obj, offset, unboxedType, value); + + current->push(value); + + if (!resumeAfter(store)) return false; - if (shapes.empty()) + *emitted = true; + return true; +} + +bool +IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj, + PropertyName *name, MDefinition *value, + bool barrier, types::TemporaryTypeSet *objTypes) +{ + MOZ_ASSERT(*emitted == false); + + if (barrier) { + trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier); return true; + } - if (!CanInlinePropertyOpShapes(shapes)) + BaselineInspector::ShapeVector nativeShapes(alloc()); + BaselineInspector::ObjectGroupVector unboxedGroups(alloc()); + if (!inspector->maybeInfoForPropertyOp(pc, nativeShapes, unboxedGroups)) + return false; + + if (!canInlinePropertyOpShapes(nativeShapes, unboxedGroups)) return true; - if (shapes.length() == 1) { + if (nativeShapes.length() == 1 && unboxedGroups.empty()) { spew("Inlining monomorphic SETPROP"); // The Baseline IC was monomorphic, so we inline the property access as // long as the shape is not in dictionary mode. We cannot be sure // that the shape is still a lastProperty, and calling Shape::search // on dictionary mode shapes that aren't lastProperty is invalid. - Shape *objShape = shapes[0]; + Shape *objShape = nativeShapes[0]; obj = addShapeGuard(obj, objShape, Bailout_ShapeGuard); Shape *shape = objShape->searchLinear(NameToId(name)); @@ -10435,19 +11053,42 @@ if (!storeSlot(obj, shape, value, needsBarrier)) return false; + trackOptimizationOutcome(TrackedOutcome::Monomorphic); + *emitted = true; + return true; + } + + if (nativeShapes.empty() && unboxedGroups.length() == 1) { + spew("Inlining monomorphic unboxed SETPROP"); + + types::ObjectGroup *group = unboxedGroups[0]; + + // Failures in this group guard should be treated the same as a shape guard failure. + obj = MGuardObjectGroup::New(alloc(), obj, group, /* bailOnEquality = */ false, + Bailout_ShapeGuard); + current->add(obj->toInstruction()); + + if (failedShapeGuard_) + obj->toGuardObjectGroup()->setNotMovable(); + + const UnboxedLayout::Property *property = group->unboxedLayout().lookup(name); + storeUnboxedProperty(obj, property->offset, property->type, value); + + current->push(value); + *emitted = true; return true; } - MOZ_ASSERT(shapes.length() > 1); + MOZ_ASSERT(nativeShapes.length() + unboxedGroups.length() > 1); spew("Inlining polymorphic SETPROP"); BaselineInspector::ShapeVector propShapes(alloc()); bool sameSlot; - if (!GetPropertyShapes(NameToId(name), shapes, propShapes, &sameSlot)) + if (!GetPropertyShapes(NameToId(name), nativeShapes, propShapes, &sameSlot)) return false; - if (sameSlot) { + if (sameSlot && unboxedGroups.empty()) { MGuardShapePolymorphic *guard = MGuardShapePolymorphic::New(alloc(), obj); current->add(guard); obj = guard; @@ -10455,8 +11096,8 @@ if (failedShapeGuard_) guard->setNotMovable(); - for (size_t i = 0; i < shapes.length(); i++) { - if (!guard->addShape(shapes[i])) + for (size_t i = 0; i < nativeShapes.length(); i++) { + if (!guard->addShape(nativeShapes[i])) return false; } @@ -10464,28 +11105,35 @@ if (!storeSlot(obj, propShapes[0], value, needsBarrier)) return false; + trackOptimizationOutcome(TrackedOutcome::Polymorphic); *emitted = true; return true; } - MSetPropertyPolymorphic *ins = MSetPropertyPolymorphic::New(alloc(), obj, value); + MSetPropertyPolymorphic *ins = MSetPropertyPolymorphic::New(alloc(), obj, value, name); current->add(ins); current->push(value); - for (size_t i = 0; i < shapes.length(); i++) { - Shape *objShape = shapes[i]; + for (size_t i = 0; i < nativeShapes.length(); i++) { + Shape *objShape = nativeShapes[i]; Shape *shape = objShape->searchLinear(NameToId(name)); MOZ_ASSERT(shape); if (!ins->addShape(objShape, shape)) return false; } + for (size_t i = 0; i < unboxedGroups.length(); i++) { + if (!ins->addUnboxedGroup(unboxedGroups[i])) + return false; + } + if (objTypes->propertyNeedsBarrier(constraints(), NameToId(name))) ins->setNeedsBarrier(); if (!resumeAfter(ins)) return false; + trackOptimizationOutcome(TrackedOutcome::Polymorphic); *emitted = true; return true; } @@ -10555,8 +11203,8 @@ // avoid cloning in this case. bool mustClone = true; - types::TypeObjectKey *typeObj = types::TypeObjectKey::get(&script()->global()); - if (!typeObj->hasFlags(constraints(), types::OBJECT_FLAG_REGEXP_FLAGS_SET)) { + types::ObjectGroupKey *globalKey = types::ObjectGroupKey::get(&script()->global()); + if (!globalKey->hasFlags(constraints(), types::OBJECT_FLAG_REGEXP_FLAGS_SET)) { #ifdef DEBUG // Only compare the statics if the one on script()->global() has been // instantiated. @@ -10943,9 +11591,9 @@ if (!outerScript || !outerScript->treatAsRunOnce()) return false; - types::TypeObjectKey *funType = - types::TypeObjectKey::get(outerScript->functionNonDelazifying()); - if (funType->hasFlags(constraints(), types::OBJECT_FLAG_RUNONCE_INVALIDATED)) + types::ObjectGroupKey *funKey = + types::ObjectGroupKey::get(outerScript->functionNonDelazifying()); + if (funKey->hasFlags(constraints(), types::OBJECT_FLAG_RUNONCE_INVALIDATED)) return false; // The script this aliased var operation is accessing will run only once, @@ -10966,7 +11614,7 @@ !environment->as().isForEval() && environment->as().callee().nonLazyScript() == outerScript) { - MOZ_ASSERT(environment->hasSingletonType()); + MOZ_ASSERT(environment->isSingleton()); *pcall = environment; return true; } @@ -10984,7 +11632,7 @@ singletonScope->is() && singletonScope->as().callee().nonLazyScript() == outerScript) { - MOZ_ASSERT(singletonScope->hasSingletonType()); + MOZ_ASSERT(singletonScope->isSingleton()); *pcall = singletonScope; return true; } @@ -11146,20 +11794,20 @@ } static bool -HasOnProtoChain(types::CompilerConstraintList *constraints, types::TypeObjectKey *object, +HasOnProtoChain(types::CompilerConstraintList *constraints, types::ObjectGroupKey *key, JSObject *protoObject, bool *hasOnProto) { MOZ_ASSERT(protoObject); while (true) { - if (!object->hasStableClassAndProto(constraints) || - !object->clasp()->isNative() || - !object->hasTenuredProto()) + if (!key->hasStableClassAndProto(constraints) || + !key->clasp()->isNative() || + !key->hasTenuredProto()) { return false; } - JSObject *proto = object->proto().toObjectOrNull(); + JSObject *proto = key->proto().toObjectOrNull(); if (!proto) { *hasOnProto = false; return true; @@ -11170,7 +11818,7 @@ return true; } - object = types::TypeObjectKey::get(proto); + key = types::ObjectGroupKey::get(proto); } MOZ_CRASH("Unreachable"); @@ -11198,12 +11846,12 @@ bool knownIsInstance = false; for (unsigned i = 0; i < lhsTypes->getObjectCount(); i++) { - types::TypeObjectKey *object = lhsTypes->getObject(i); - if (!object) + types::ObjectGroupKey *key = lhsTypes->getObject(i); + if (!key) continue; bool isInstance; - if (!HasOnProtoChain(constraints(), object, protoObject, &isInstance)) + if (!HasOnProtoChain(constraints(), key, protoObject, &isInstance)) return false; if (isFirst) { @@ -11241,16 +11889,16 @@ // exact function and prototype object being tested for, use a typed path. do { types::TemporaryTypeSet *rhsTypes = rhs->resultTypeSet(); - JSObject *rhsObject = rhsTypes ? rhsTypes->getSingleton() : nullptr; + JSObject *rhsObject = rhsTypes ? rhsTypes->maybeSingleton() : nullptr; if (!rhsObject || !rhsObject->is() || rhsObject->isBoundFunction()) break; - types::TypeObjectKey *rhsType = types::TypeObjectKey::get(rhsObject); - if (rhsType->unknownProperties()) + types::ObjectGroupKey *rhsKey = types::ObjectGroupKey::get(rhsObject); + if (rhsKey->unknownProperties()) break; types::HeapTypeSetKey protoProperty = - rhsType->property(NameToId(names().prototype)); + rhsKey->property(NameToId(names().prototype)); JSObject *protoObject = protoProperty.singleton(constraints()); if (!protoObject) break; @@ -11393,14 +12041,14 @@ TypedObjectPrediction out; for (uint32_t i = 0; i < types->getObjectCount(); i++) { - types::TypeObject *type = types->getTypeObject(i); - if (!type || !types::TypeObjectKey::get(type)->hasStableClassAndProto(constraints())) + types::ObjectGroup *group = types->getGroup(i); + if (!group || !types::ObjectGroupKey::get(group)->hasStableClassAndProto(constraints())) return TypedObjectPrediction(); - if (!IsTypedObjectClass(type->clasp())) + if (!IsTypedObjectClass(group->clasp())) return TypedObjectPrediction(); - out.addDescr(type->typeDescr()); + out.addDescr(group->typeDescr()); } return out; @@ -11524,16 +12172,26 @@ size_t *fieldIndex) { TypedObjectPrediction objPrediction = typedObjectPrediction(typedObj); - if (objPrediction.isUseless()) + if (objPrediction.isUseless()) { + trackOptimizationOutcome(TrackedOutcome::AccessNotTypedObject); return false; + } // Must be accessing a struct. - if (objPrediction.kind() != type::Struct) + if (objPrediction.kind() != type::Struct) { + trackOptimizationOutcome(TrackedOutcome::NotStruct); return false; + } // Determine the type/offset of the field `name`, if any. - return objPrediction.hasFieldNamed(NameToId(name), fieldOffset, - fieldPrediction, fieldIndex); + if (!objPrediction.hasFieldNamed(NameToId(name), fieldOffset, + fieldPrediction, fieldIndex)) + { + trackOptimizationOutcome(TrackedOutcome::StructNoField); + return false; + } + + return true; } MDefinition * @@ -11624,6 +12282,7 @@ if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &typedObj, name, &value, /* canModify = */ true, implicitType)) { + trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier); return false; } } @@ -11634,7 +12293,7 @@ size_t alignment = ReferenceTypeDescr::alignment(type); loadTypedObjectElements(typedObj, byteOffset, alignment, &elements, &scaledOffset, &adjustment); - MInstruction *store; + MInstruction *store = nullptr; // initialize to silence GCC warning switch (type) { case ReferenceTypeDescr::TYPE_ANY: if (NeedsPostBarrier(info(), value)) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/IonBuilder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/IonBuilder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/IonBuilder.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/IonBuilder.h 2015-02-03 14:33:31.000000000 +0000 @@ -12,19 +12,20 @@ #include "mozilla/LinkedList.h" +#include "jit/BaselineInspector.h" #include "jit/BytecodeAnalysis.h" #include "jit/IonAnalysis.h" #include "jit/IonOptimizationLevels.h" #include "jit/MIR.h" #include "jit/MIRGenerator.h" #include "jit/MIRGraph.h" +#include "jit/OptimizationTracking.h" namespace js { namespace jit { class CodeGenerator; class CallInfo; -class BaselineInspector; class BaselineFrameInspector; // Records information about a baseline frame for compilation that is stable @@ -234,8 +235,11 @@ uint32_t readIndex(jsbytecode *pc); JSAtom *readAtom(jsbytecode *pc); bool abort(const char *message, ...); + void trackActionableAbort(const char *message); void spew(const char *message); + MInstruction *constantMaybeNursery(JSObject *obj); + JSFunction *getSingleCallTarget(types::TemporaryTypeSet *calleeTypes); bool getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constructing, ObjectVector &targets, uint32_t maxTargets); @@ -419,6 +423,8 @@ types::TemporaryTypeSet *types); bool getPropTryDefiniteSlot(bool *emitted, MDefinition *obj, PropertyName *name, BarrierKind barrier, types::TemporaryTypeSet *types); + bool getPropTryUnboxed(bool *emitted, MDefinition *obj, PropertyName *name, + BarrierKind barrier, types::TemporaryTypeSet *types); bool getPropTryCommonGetter(bool *emitted, MDefinition *obj, PropertyName *name, types::TemporaryTypeSet *types); bool getPropTryInlineAccess(bool *emitted, MDefinition *obj, PropertyName *name, @@ -448,10 +454,13 @@ bool isDOM); bool setPropTryDefiniteSlot(bool *emitted, MDefinition *obj, PropertyName *name, MDefinition *value, - types::TemporaryTypeSet *objTypes); + bool barrier, types::TemporaryTypeSet *objTypes); + bool setPropTryUnboxed(bool *emitted, MDefinition *obj, + PropertyName *name, MDefinition *value, + bool barrier, types::TemporaryTypeSet *objTypes); bool setPropTryInlineAccess(bool *emitted, MDefinition *obj, PropertyName *name, MDefinition *value, - types::TemporaryTypeSet *objTypes); + bool barrier, types::TemporaryTypeSet *objTypes); bool setPropTryTypedObject(bool *emitted, MDefinition *obj, PropertyName *name, MDefinition *value); bool setPropTryReferencePropOfTypedObject(bool *emitted, @@ -518,6 +527,7 @@ ReferenceTypeDescr::Type type, PropertyName *name); MDefinition *neuterCheck(MDefinition *obj); + JSObject *getStaticTypedArrayObject(MDefinition *obj, MDefinition *index); // jsop_setelem() helpers. bool setElemTryTypedArray(bool *emitted, MDefinition *object, @@ -788,7 +798,11 @@ bool elementAccessIsTypedObjectArrayOfScalarType(MDefinition* obj, MDefinition* id, ScalarTypeDescr::Type *arrayType); InliningStatus inlineConstructTypedObject(CallInfo &callInfo, TypeDescr *target); + + // SIMD intrinsics and natives. InliningStatus inlineConstructSimdObject(CallInfo &callInfo, SimdTypeDescr *target); + InliningStatus inlineSimdInt32x4BinaryArith(CallInfo &callInfo, JSNative native, + MSimdBinaryArith::Operation op); // Utility intrinsics. InliningStatus inlineIsCallable(CallInfo &callInfo); @@ -827,9 +841,9 @@ // Inlining helpers. bool inlineGenericFallback(JSFunction *target, CallInfo &callInfo, MBasicBlock *dispatchBlock, bool clonedAtCallsite); - bool inlineTypeObjectFallback(CallInfo &callInfo, MBasicBlock *dispatchBlock, - MTypeObjectDispatch *dispatch, MGetPropertyCache *cache, - MBasicBlock **fallbackTarget); + bool inlineObjectGroupFallback(CallInfo &callInfo, MBasicBlock *dispatchBlock, + MObjectGroupDispatch *dispatch, MGetPropertyCache *cache, + MBasicBlock **fallbackTarget); bool atomicsMeetsPreconditions(CallInfo &callInfo, Scalar::Type *arrayElementType); void atomicsCheckBounds(CallInfo &callInfo, MInstruction **elements, MDefinition **index); @@ -869,8 +883,16 @@ bool testSingletonPropertyTypes(MDefinition *obj, JSObject *singleton, PropertyName *name, bool *testObject, bool *testString); uint32_t getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name); + uint32_t getUnboxedOffset(types::TemporaryTypeSet *types, PropertyName *name, + JSValueType *punboxedType); + MInstruction *loadUnboxedProperty(MDefinition *obj, size_t offset, JSValueType unboxedType, + BarrierKind barrier, types::TemporaryTypeSet *types); + MInstruction *storeUnboxedProperty(MDefinition *obj, size_t offset, JSValueType unboxedType, + MDefinition *value); bool freezePropTypeSets(types::TemporaryTypeSet *types, JSObject *foundProto, PropertyName *name); + bool canInlinePropertyOpShapes(const BaselineInspector::ShapeVector &nativeShapes, + const BaselineInspector::ObjectGroupVector &unboxedGroups); types::TemporaryTypeSet *bytecodeTypes(jsbytecode *pc); @@ -900,6 +922,14 @@ // performed by FinishOffThreadBuilder(). CodeGenerator *backgroundCodegen_; + // Some aborts are actionable (e.g., using an unsupported bytecode). When + // optimization tracking is enabled, the location and message of the abort + // are recorded here so they may be propagated to the script's + // corresponding JitcodeGlobalEntry::BaselineEntry. + JSScript *actionableAbortScript_; + jsbytecode *actionableAbortPc_; + const char *actionableAbortMessage_; + public: void clearForBackEnd(); @@ -918,6 +948,21 @@ const JSAtomState &names() { return compartment->runtime()->names(); } + bool hadActionableAbort() const { + MOZ_ASSERT(!actionableAbortScript_ || + (actionableAbortPc_ && actionableAbortMessage_)); + return actionableAbortScript_ != nullptr; + } + + void actionableAbortLocationAndMessage(JSScript **abortScript, jsbytecode **abortPc, + const char **abortMessage) + { + MOZ_ASSERT(hadActionableAbort()); + *abortScript = actionableAbortScript_; + *abortPc = actionableAbortPc_; + *abortMessage = actionableAbortMessage_; + } + private: bool init(); @@ -944,11 +989,20 @@ MBasicBlock *current; uint32_t loopDepth_; + Vector trackedOptimizationSites_; + BytecodeSite *bytecodeSite(jsbytecode *pc) { MOZ_ASSERT(info().inlineScriptTree()->script()->containsPC(pc)); + // See comment in maybeTrackedOptimizationSite. + if (isOptimizationTrackingEnabled()) { + if (BytecodeSite *site = maybeTrackedOptimizationSite(pc)) + return site; + } return new(alloc()) BytecodeSite(info().inlineScriptTree(), pc); } + BytecodeSite *maybeTrackedOptimizationSite(jsbytecode *pc); + MDefinition *lexicalCheck_; void setLexicalCheck(MDefinition *lexical) { @@ -1022,7 +1076,7 @@ // In such cases we do not have read the property, except when the type // object is unknown. // - // As an optimization, we can dispatch a call based on the type object, + // As an optimization, we can dispatch a call based on the object group, // without doing the MGetPropertyCache. This is what is achieved by // |IonBuilder::inlineCalls|. As we might not know all the functions, we // are adding a fallback path, where this MGetPropertyCache would be moved @@ -1047,6 +1101,59 @@ } MGetPropertyCache *maybeFallbackFunctionGetter_; + + // Used in tracking outcomes of optimization strategies for devtools. + void startTrackingOptimizations(); + + // The track* methods below are called often. Do not combine them with the + // unchecked variants, despite the unchecked variants having no other + // callers. + void trackTypeInfo(TrackedTypeSite site, MIRType mirType, + types::TemporaryTypeSet *typeSet) + { + if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations())) + trackTypeInfoUnchecked(site, mirType, typeSet); + } + void trackTypeInfo(TrackedTypeSite site, JSObject *obj) { + if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations())) + trackTypeInfoUnchecked(site, obj); + } + void trackTypeInfo(CallInfo &callInfo) { + if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations())) + trackTypeInfoUnchecked(callInfo); + } + void trackOptimizationAttempt(TrackedStrategy strategy) { + if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations())) + trackOptimizationAttemptUnchecked(strategy); + } + void amendOptimizationAttempt(uint32_t index) { + if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations())) + amendOptimizationAttemptUnchecked(index); + } + void trackOptimizationOutcome(TrackedOutcome outcome) { + if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations())) + trackOptimizationOutcomeUnchecked(outcome); + } + void trackOptimizationSuccess() { + if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations())) + trackOptimizationSuccessUnchecked(); + } + void trackInlineSuccess(InliningStatus status = InliningStatus_Inlined) { + if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations())) + trackInlineSuccessUnchecked(status); + } + + // Out-of-line variants that don't check if optimization tracking is + // enabled. + void trackTypeInfoUnchecked(TrackedTypeSite site, MIRType mirType, + types::TemporaryTypeSet *typeSet); + void trackTypeInfoUnchecked(TrackedTypeSite site, JSObject *obj); + void trackTypeInfoUnchecked(CallInfo &callInfo); + void trackOptimizationAttemptUnchecked(TrackedStrategy strategy); + void amendOptimizationAttemptUnchecked(uint32_t index); + void trackOptimizationOutcomeUnchecked(TrackedOutcome outcome); + void trackOptimizationSuccessUnchecked(); + void trackInlineSuccessUnchecked(InliningStatus status); }; class CallInfo diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/IonCaches.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/IonCaches.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/IonCaches.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/IonCaches.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -487,8 +487,8 @@ if (obj->hasUncacheableProto()) { // Note: objectReg and scratchReg may be the same register, so we cannot // use objectReg in the rest of this function. - masm.loadPtr(Address(objectReg, JSObject::offsetOfType()), scratchReg); - Address proto(scratchReg, types::TypeObject::offsetOfProto()); + masm.loadPtr(Address(objectReg, JSObject::offsetOfGroup()), scratchReg); + Address proto(scratchReg, types::ObjectGroup::offsetOfProto()); masm.branchPtr(Assembler::NotEqual, proto, ImmMaybeNurseryPtr(obj->getProto()), failures); } @@ -500,10 +500,10 @@ return; while (pobj != holder) { if (pobj->hasUncacheableProto()) { - MOZ_ASSERT(!pobj->hasSingletonType()); + MOZ_ASSERT(!pobj->isSingleton()); masm.movePtr(ImmMaybeNurseryPtr(pobj), scratchReg); - Address objType(scratchReg, JSObject::offsetOfType()); - masm.branchPtr(Assembler::NotEqual, objType, ImmGCPtr(pobj->type()), failures); + Address groupAddr(scratchReg, JSObject::offsetOfGroup()); + masm.branchPtr(Assembler::NotEqual, groupAddr, ImmGCPtr(pobj->group()), failures); } pobj = pobj->getProto(); } @@ -769,7 +769,6 @@ Shape *shape, Register object, TypedOrValueRegister output, Label *failures = nullptr) { - MOZ_ASSERT(obj->isNative()); // If there's a single jump to |failures|, we can patch the shape guard // jump directly. Otherwise, jump to the end of the stub, so there's a // common point to patch. @@ -781,11 +780,18 @@ if (multipleFailureJumps && !failures) failures = &failures_; - // Guard on the shape of the object. - attacher.branchNextStubOrLabel(masm, Assembler::NotEqual, - Address(object, JSObject::offsetOfShape()), - ImmGCPtr(obj->lastProperty()), - failures); + // Guard on the shape or type of the object, depending on whether it is native. + if (obj->isNative()) { + attacher.branchNextStubOrLabel(masm, Assembler::NotEqual, + Address(object, JSObject::offsetOfShape()), + ImmGCPtr(obj->lastProperty()), + failures); + } else { + attacher.branchNextStubOrLabel(masm, Assembler::NotEqual, + Address(object, JSObject::offsetOfGroup()), + ImmGCPtr(obj->group()), + failures); + } // If we need a scratch register, use either an output register or the // object register. After this point, we cannot jump directly to @@ -876,6 +882,24 @@ } +static void +GenerateReadUnboxed(JSContext *cx, IonScript *ion, MacroAssembler &masm, + IonCache::StubAttacher &attacher, JSObject *obj, + const UnboxedLayout::Property *property, + Register object, TypedOrValueRegister output) +{ + // Guard on the type of the object. + attacher.branchNextStub(masm, Assembler::NotEqual, + Address(object, JSObject::offsetOfGroup()), + ImmGCPtr(obj->group())); + + Address address(object, UnboxedPlainObject::offsetOfData() + property->offset); + + masm.loadUnboxedProperty(address, property->type, output); + + attacher.jumpRejoin(masm); +} + static bool EmitGetterCall(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher, JSObject *obj, @@ -1170,16 +1194,24 @@ MutableHandleNativeObject holder, MutableHandleShape shape, bool skipArrayLen = false) { - if (!obj || !obj->isNative()) + if (!obj) return GetPropertyIC::CanAttachNone; // The lookup needs to be universally pure, otherwise we risk calling hooks out // of turn. We don't mind doing this even when purity isn't required, because we // only miss out on shape hashification, which is only a temporary perf cost. // The limits were arbitrarily set, anyways. - if (!LookupPropertyPure(cx, obj, NameToId(name), holder.address(), shape.address())) + JSObject *baseHolder = nullptr; + if (!LookupPropertyPure(cx, obj, NameToId(name), &baseHolder, shape.address())) return GetPropertyIC::CanAttachNone; + MOZ_ASSERT(!holder); + if (baseHolder) { + if (!baseHolder->isNative()) + return GetPropertyIC::CanAttachNone; + holder.set(&baseHolder->as()); + } + RootedScript script(cx); jsbytecode *pc; cache.getScriptedLocation(&script, &pc); @@ -1298,6 +1330,30 @@ } bool +GetPropertyIC::tryAttachUnboxed(JSContext *cx, HandleScript outerScript, IonScript *ion, + HandleObject obj, HandlePropertyName name, + void *returnAddr, bool *emitted) +{ + MOZ_ASSERT(canAttachStub()); + MOZ_ASSERT(!*emitted); + MOZ_ASSERT(outerScript->ionScript() == ion); + + if (!obj->is()) + return true; + const UnboxedLayout::Property *property = obj->as().layout().lookup(name); + if (!property) + return true; + + *emitted = true; + + MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_); + + RepatchStubAppender attacher(*this); + GenerateReadUnboxed(cx, ion, masm, attacher, obj, property, object(), output()); + return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed"); +} + +bool GetPropertyIC::tryAttachTypedArrayLength(JSContext *cx, HandleScript outerScript, IonScript *ion, HandleObject obj, HandlePropertyName name, bool *emitted) { @@ -1721,9 +1777,15 @@ if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, name, returnAddr, emitted)) return false; + if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, name, returnAddr, emitted)) + return false; + if (!*emitted && !tryAttachTypedArrayLength(cx, outerScript, ion, obj, name, emitted)) return false; + if (!*emitted) + JitSpew(JitSpew_IonIC, "Failed to attach GETPROP cache"); + return true; } @@ -1828,6 +1890,26 @@ { } +// Jump to failure if a value being written is not a property for obj/id. +// This might clobber |object|. +static void +CheckTypeSetForWrite(MacroAssembler &masm, JSObject *obj, jsid id, + Register object, ConstantOrRegister value, Label *failure) +{ + TypedOrValueRegister valReg = value.reg(); + types::ObjectGroup *group = obj->group(); + if (group->unknownProperties()) + return; + types::HeapTypeSet *propTypes = group->maybeGetProperty(id); + MOZ_ASSERT(propTypes); + + // guardTypeSet can read from type sets without triggering read barriers. + types::TypeSet::readBarrier(propTypes); + + Register scratch = object; + masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratch, failure); +} + static void GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher, NativeObject *obj, Shape *shape, Register object, ConstantOrRegister value, @@ -1846,25 +1928,15 @@ // We can't do anything that would change the HeapTypeSet, so // just guard that it's already there. - // Obtain and guard on the TypeObject of the object. - types::TypeObject *type = obj->type(); + // Obtain and guard on the ObjectGroup of the object. + types::ObjectGroup *group = obj->group(); masm.branchPtr(Assembler::NotEqual, - Address(object, JSObject::offsetOfType()), - ImmGCPtr(type), &failures); + Address(object, JSObject::offsetOfGroup()), + ImmGCPtr(group), &failures); if (checkTypeset) { - TypedOrValueRegister valReg = value.reg(); - types::HeapTypeSet *propTypes = type->maybeGetProperty(shape->propid()); - MOZ_ASSERT(propTypes); - MOZ_ASSERT(!propTypes->unknown()); - - // guardTypeSet can read from type sets without triggering read barriers. - types::TypeSet::readBarrier(propTypes); - - Register scratchReg = object; - masm.push(scratchReg); - - masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratchReg, &barrierFailure); + masm.push(object); + CheckTypeSetForWrite(masm, obj, shape->propid(), object, value, &barrierFailure); masm.pop(object); } } @@ -2391,7 +2463,7 @@ static void GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher, - NativeObject *obj, Shape *oldShape, types::TypeObject *oldType, + NativeObject *obj, Shape *oldShape, types::ObjectGroup *oldGroup, Register object, ConstantOrRegister value, bool checkTypeset) { @@ -2400,8 +2472,8 @@ Label failures; // Guard the type of the object - masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfType()), - ImmGCPtr(oldType), &failures); + masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), + ImmGCPtr(oldGroup), &failures); // Guard shapes along prototype chain. masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, &failures); @@ -2412,17 +2484,7 @@ // Guard that the incoming value is in the type set for the property // if a type barrier is required. if (checkTypeset) { - TypedOrValueRegister valReg = value.reg(); - types::TypeObject *type = obj->type(); - types::HeapTypeSet *propTypes = type->maybeGetProperty(obj->lastProperty()->propid()); - MOZ_ASSERT(propTypes); - MOZ_ASSERT(!propTypes->unknown()); - - // guardTypeSet can read from type sets without triggering read barriers. - types::TypeSet::readBarrier(propTypes); - - Register scratchReg = object; - masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratchReg, &failuresPopObject); + CheckTypeSetForWrite(masm, obj, obj->lastProperty()->propid(), object, value, &failuresPopObject); masm.loadPtr(Address(StackPointer, 0), object); } @@ -2449,24 +2511,24 @@ masm.callPreBarrier(shapeAddr, MIRType_Shape); masm.storePtr(ImmGCPtr(newShape), shapeAddr); - if (oldType != obj->type()) { - // Changing object's type from a partially to fully initialized type, - // per the acquired properties analysis. Only change the type if the - // old type still has a newScript. + if (oldGroup != obj->group()) { + // Changing object's group from a partially to fully initialized group, + // per the acquired properties analysis. Only change the group if the + // old group still has a newScript. Label noTypeChange, skipPop; masm.push(object); - masm.loadPtr(Address(object, JSObject::offsetOfType()), object); + masm.loadPtr(Address(object, JSObject::offsetOfGroup()), object); masm.branchPtr(Assembler::Equal, - Address(object, types::TypeObject::offsetOfAddendum()), + Address(object, types::ObjectGroup::offsetOfAddendum()), ImmWord(0), &noTypeChange); masm.pop(object); - Address typeAddr(object, JSObject::offsetOfType()); + Address groupAddr(object, JSObject::offsetOfGroup()); if (cx->zone()->needsIncrementalBarrier()) - masm.callPreBarrier(typeAddr, MIRType_TypeObject); - masm.storePtr(ImmGCPtr(obj->type()), typeAddr); + masm.callPreBarrier(groupAddr, MIRType_ObjectGroup); + masm.storePtr(ImmGCPtr(obj->group()), groupAddr); masm.jump(&skipPop); masm.bind(&noTypeChange); @@ -2502,14 +2564,14 @@ bool SetPropertyIC::attachAddSlot(JSContext *cx, HandleScript outerScript, IonScript *ion, - HandleNativeObject obj, HandleShape oldShape, HandleTypeObject oldType, + HandleNativeObject obj, HandleShape oldShape, HandleObjectGroup oldGroup, bool checkTypeset) { MOZ_ASSERT_IF(!needsTypeBarrier(), !checkTypeset); MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_); RepatchStubAppender attacher(*this); - GenerateAddSlot(cx, masm, attacher, obj, oldShape, oldType, object(), value(), checkTypeset); + GenerateAddSlot(cx, masm, attacher, obj, oldShape, oldGroup, object(), value(), checkTypeset); return linkAndAttachStub(cx, masm, attacher, ion, "adding"); } @@ -2517,13 +2579,13 @@ CanInlineSetPropTypeCheck(JSObject *obj, jsid id, ConstantOrRegister val, bool *checkTypeset) { bool shouldCheck = false; - types::TypeObject *type = obj->type(); - if (!type->unknownProperties()) { - types::HeapTypeSet *propTypes = type->maybeGetProperty(id); + types::ObjectGroup *group = obj->group(); + if (!group->unknownProperties()) { + types::HeapTypeSet *propTypes = group->maybeGetProperty(id); if (!propTypes) return false; if (!propTypes->unknown()) { - if (obj->hasSingletonType() && !propTypes->nonConstantProperty()) + if (obj->isSingleton() && !propTypes->nonConstantProperty()) return false; shouldCheck = true; if (val.constant()) { @@ -2633,7 +2695,7 @@ // Don't attach if we are adding a property to an object which the new // script properties analysis hasn't been performed for yet, as there // may be a shape change required here afterwards. - if (obj->type()->newScript() && !obj->type()->newScript()->analyzed()) + if (obj->group()->newScript() && !obj->group()->newScript()->analyzed()) return false; if (needsTypeBarrier) @@ -2645,7 +2707,7 @@ static SetPropertyIC::NativeSetPropCacheability CanAttachNativeSetProp(JSContext *cx, HandleObject obj, HandleId id, ConstantOrRegister val, - bool needsTypeBarrier, MutableHandleNativeObject holder, + bool needsTypeBarrier, MutableHandleObject holder, MutableHandleShape shape, bool *checkTypeset) { if (!obj->isNative()) @@ -2676,6 +2738,85 @@ return SetPropertyIC::CanAttachNone; } +static void +GenerateSetUnboxed(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher, + JSObject *obj, jsid id, uint32_t unboxedOffset, JSValueType unboxedType, + Register object, ConstantOrRegister value, bool checkTypeset) +{ + Label failure, failurePopObject; + + // Guard on the type of the object. + masm.branchPtr(Assembler::NotEqual, + Address(object, JSObject::offsetOfGroup()), + ImmGCPtr(obj->group()), &failure); + + if (checkTypeset) { + masm.push(object); + CheckTypeSetForWrite(masm, obj, id, object, value, &failurePopObject); + masm.pop(object); + } + + Address address(object, UnboxedPlainObject::offsetOfData() + unboxedOffset); + + if (cx->zone()->needsIncrementalBarrier()) { + if (unboxedType == JSVAL_TYPE_OBJECT) + masm.callPreBarrier(address, MIRType_Object); + else if (unboxedType == JSVAL_TYPE_STRING) + masm.callPreBarrier(address, MIRType_String); + else + MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(unboxedType)); + } + + // If the unboxed object's type has known properties, then instances have + // never been converted to native objects and the type set check performed + // above ensures the value being written can be stored in the unboxed + // object. + Label *storeFailure = obj->group()->unknownProperties() ? &failure : nullptr; + + masm.storeUnboxedProperty(address, unboxedType, value, storeFailure); + + attacher.jumpRejoin(masm); + + masm.bind(&failurePopObject); + masm.pop(object); + masm.bind(&failure); + + attacher.jumpNextStub(masm); +} + +bool +SetPropertyIC::attachSetUnboxed(JSContext *cx, HandleScript outerScript, IonScript *ion, + HandleObject obj, HandleId id, + uint32_t unboxedOffset, JSValueType unboxedType, + bool checkTypeset) +{ + MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_); + RepatchStubAppender attacher(*this); + GenerateSetUnboxed(cx, masm, attacher, obj, id, unboxedOffset, unboxedType, + object(), value(), needsTypeBarrier()); + return linkAndAttachStub(cx, masm, attacher, ion, "set_unboxed"); +} + +static bool +CanAttachSetUnboxed(JSContext *cx, HandleObject obj, HandleId id, ConstantOrRegister val, + bool needsTypeBarrier, bool *checkTypeset, + uint32_t *unboxedOffset, JSValueType *unboxedType) +{ + if (!obj->is()) + return false; + + const UnboxedLayout::Property *property = obj->as().layout().lookup(id); + if (property) { + if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset)) + return false; + *unboxedOffset = property->offset; + *unboxedType = property->type; + return true; + } + + return false; +} + bool SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue value) @@ -2687,8 +2828,8 @@ RootedPropertyName name(cx, cache.name()); RootedId id(cx, AtomToId(name)); - RootedTypeObject oldType(cx, obj->getType(cx)); - if (!oldType) + RootedObjectGroup oldGroup(cx, obj->getGroup(cx)); + if (!oldGroup) return false; // Stop generating new stubs once we hit the stub count limit, see @@ -2723,7 +2864,7 @@ } RootedShape shape(cx); - RootedNativeObject holder(cx); + RootedObject holder(cx); bool checkTypeset; canCache = CanAttachNativeSetProp(cx, obj, id, cache.value(), cache.needsTypeBarrier(), &holder, &shape, &checkTypeset); @@ -2740,6 +2881,21 @@ return false; addedSetterStub = true; } + + checkTypeset = false; + uint32_t unboxedOffset; + JSValueType unboxedType; + if (!addedSetterStub && CanAttachSetUnboxed(cx, obj, id, cache.value(), + cache.needsTypeBarrier(), + &checkTypeset, &unboxedOffset, &unboxedType)) + { + if (!cache.attachSetUnboxed(cx, script, ion, obj, id, unboxedOffset, unboxedType, + checkTypeset)) + { + return false; + } + addedSetterStub = true; + } } uint32_t oldSlots = obj->is() ? obj->as().numDynamicSlots() : 0; @@ -2757,10 +2913,14 @@ &checkTypeset)) { RootedNativeObject nobj(cx, &obj->as()); - if (!cache.attachAddSlot(cx, script, ion, nobj, oldShape, oldType, checkTypeset)) + if (!cache.attachAddSlot(cx, script, ion, nobj, oldShape, oldGroup, checkTypeset)) return false; + addedSetterStub = true; } + if (!addedSetterStub) + JitSpew(JitSpew_IonIC, "Failed to attach SETPROP cache"); + return true; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/IonCaches.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/IonCaches.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/IonCaches.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/IonCaches.h 2015-02-03 14:33:31.000000000 +0000 @@ -669,6 +669,10 @@ HandleObject obj, HandlePropertyName name, void *returnAddr, bool *emitted); + bool tryAttachUnboxed(JSContext *cx, HandleScript outerScript, IonScript *ion, + HandleObject obj, HandlePropertyName name, + void *returnAddr, bool *emitted); + bool tryAttachTypedArrayLength(JSContext *cx, HandleScript outerScript, IonScript *ion, HandleObject obj, HandlePropertyName name, bool *emitted); @@ -744,9 +748,14 @@ void *returnAddr); bool attachAddSlot(JSContext *cx, HandleScript outerScript, IonScript *ion, - HandleNativeObject obj, HandleShape oldShape, HandleTypeObject oldType, + HandleNativeObject obj, HandleShape oldShape, HandleObjectGroup oldGroup, bool checkTypeset); + bool attachSetUnboxed(JSContext *cx, HandleScript outerScript, IonScript *ion, + HandleObject obj, HandleId id, + uint32_t unboxedOffset, JSValueType unboxedType, + bool checkTypeset); + bool attachGenericProxy(JSContext *cx, HandleScript outerScript, IonScript *ion, void *returnAddr); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/IonCode.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/IonCode.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/IonCode.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/IonCode.h 2015-02-03 14:33:31.000000000 +0000 @@ -32,6 +32,8 @@ class PatchableBackedge; class IonBuilder; +typedef Vector ObjectVector; + class JitCode : public gc::TenuredCell { protected: @@ -112,6 +114,8 @@ invalidated_ = true; } + void fixupNurseryObjects(JSContext *cx, const ObjectVector &nurseryObjects); + void setHasBytecodeMap() { hasBytecodeMap_ = true; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Ion.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Ion.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Ion.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Ion.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -163,7 +163,8 @@ osrTempData_(nullptr), mutatingBackedgeList_(false), ionReturnOverride_(MagicValue(JS_ARG_POISON)), - jitcodeGlobalTable_(nullptr) + jitcodeGlobalTable_(nullptr), + hasIonNurseryObjects_(false) { } @@ -282,9 +283,9 @@ if (!shapePreBarrier_) return false; - JitSpew(JitSpew_Codegen, "# Emitting Pre Barrier for TypeObject"); - typeObjectPreBarrier_ = generatePreBarrier(cx, MIRType_TypeObject); - if (!typeObjectPreBarrier_) + JitSpew(JitSpew_Codegen, "# Emitting Pre Barrier for ObjectGroup"); + objectGroupPreBarrier_ = generatePreBarrier(cx, MIRType_ObjectGroup); + if (!objectGroupPreBarrier_) return false; JitSpew(JitSpew_Codegen, "# Emitting malloc stub"); @@ -654,6 +655,17 @@ } void +JitCode::fixupNurseryObjects(JSContext *cx, const ObjectVector &nurseryObjects) +{ + if (nurseryObjects.empty() || !dataRelocTableBytes_) + return; + + uint8_t *start = code_ + dataRelocTableOffset(); + CompactBufferReader reader(start, start + dataRelocTableBytes_); + MacroAssembler::FixupNurseryObjects(cx, this, reader, nurseryObjects); +} + +void JitCode::finalize(FreeOp *fop) { JSRuntime *rt = fop->runtime(); @@ -1671,6 +1683,62 @@ } } +void +MIRGenerator::traceNurseryObjects(JSTracer *trc) +{ + MarkObjectRootRange(trc, nurseryObjects_.length(), nurseryObjects_.begin(), "ion-nursery-objects"); +} + +class MarkOffThreadNurseryObjects : public gc::BufferableRef +{ + public: + void mark(JSTracer *trc); +}; + +void +MarkOffThreadNurseryObjects::mark(JSTracer *trc) +{ + JSRuntime *rt = trc->runtime(); + + MOZ_ASSERT(rt->jitRuntime()->hasIonNurseryObjects()); + rt->jitRuntime()->setHasIonNurseryObjects(false); + + AutoLockHelperThreadState lock; + if (!HelperThreadState().threads) + return; + + // Trace nursery objects of any builders which haven't started yet. + GlobalHelperThreadState::IonBuilderVector &worklist = HelperThreadState().ionWorklist(); + for (size_t i = 0; i < worklist.length(); i++) { + jit::IonBuilder *builder = worklist[i]; + if (builder->script()->runtimeFromAnyThread() == rt) + builder->traceNurseryObjects(trc); + } + + // Trace nursery objects of in-progress entries. + for (size_t i = 0; i < HelperThreadState().threadCount; i++) { + HelperThread &helper = HelperThreadState().threads[i]; + if (helper.ionBuilder && helper.ionBuilder->script()->runtimeFromAnyThread() == rt) + helper.ionBuilder->traceNurseryObjects(trc); + } + + // Trace nursery objects of any completed entries. + GlobalHelperThreadState::IonBuilderVector &finished = HelperThreadState().ionFinishedList(); + for (size_t i = 0; i < finished.length(); i++) { + jit::IonBuilder *builder = finished[i]; + if (builder->script()->runtimeFromAnyThread() == rt) + builder->traceNurseryObjects(trc); + } + + // Trace nursery objects of lazy-linked builders. + jit::IonBuilder *builder = HelperThreadState().ionLazyLinkList().getFirst(); + while (builder) { + if (builder->script()->runtimeFromAnyThread() == rt) + builder->traceNurseryObjects(trc); + builder = builder->getNext(); + } +} + static inline bool OffThreadCompilationAvailable(JSContext *cx) { @@ -1687,7 +1755,7 @@ static void TrackAllProperties(JSContext *cx, JSObject *obj) { - MOZ_ASSERT(obj->hasSingletonType()); + MOZ_ASSERT(obj->isSingleton()); for (Shape::Range range(obj->lastProperty()); !range.empty(); range.popFront()) types::EnsureTrackPropertyTypes(cx, obj, range.front().propid()); @@ -1705,18 +1773,42 @@ : nullptr; while (environment && !environment->is()) { - if (environment->is() && environment->hasSingletonType()) + if (environment->is() && environment->isSingleton()) TrackAllProperties(cx, environment); environment = environment->enclosingScope(); } if (baselineFrame) { JSObject *scope = baselineFrame->scopeChain(); - if (scope->is() && scope->hasSingletonType()) + if (scope->is() && scope->isSingleton()) TrackAllProperties(cx, scope); } } +static void +TrackIonAbort(JSContext *cx, JSScript *script, jsbytecode *pc, const char *message) +{ + if (!cx->runtime()->jitRuntime()->isOptimizationTrackingEnabled(cx->runtime())) + return; + + // Only bother tracking aborts of functions we're attempting to + // Ion-compile after successfully running in Baseline. + if (!script->hasBaselineScript()) + return; + + JitcodeGlobalTable *table = cx->runtime()->jitRuntime()->getJitcodeGlobalTable(); + JitcodeGlobalEntry entry; + table->lookupInfallible(script->baselineScript()->method()->raw(), &entry, cx->runtime()); + entry.baselineEntry().trackIonAbort(pc, message); +} + +static void +TrackAndSpewIonAbort(JSContext *cx, JSScript *script, const char *message) +{ + JitSpew(JitSpew_IonAbort, message); + TrackIonAbort(cx, script, script->code(), message); +} + static AbortReason IonCompile(JSContext *cx, JSScript *script, BaselineFrame *baselineFrame, jsbytecode *osrPc, bool constructing, @@ -1816,12 +1908,21 @@ // Some type was accessed which needs the new script properties // analysis to be performed. Do this now and we will try to build // again shortly. - const MIRGenerator::TypeObjectVector &types = builder->abortedNewScriptPropertiesTypes(); - for (size_t i = 0; i < types.length(); i++) { - if (!types[i]->newScript()->maybeAnalyze(cx, types[i], nullptr, /* force = */ true)) + const MIRGenerator::ObjectGroupVector &groups = builder->abortedNewScriptPropertiesGroups(); + for (size_t i = 0; i < groups.length(); i++) { + if (!groups[i]->newScript()->maybeAnalyze(cx, groups[i], nullptr, /* force = */ true)) return AbortReason_Alloc; } } + + if (builder->hadActionableAbort()) { + JSScript *abortScript; + jsbytecode *abortPc; + const char *abortMessage; + builder->actionableAbortLocationAndMessage(&abortScript, &abortPc, &abortMessage); + TrackIonAbort(cx, abortScript, abortPc, abortMessage); + } + return reason; } @@ -1833,6 +1934,15 @@ JitSpew(JitSpew_IonLogs, "Can't log script %s:%d. (Compiled on background thread.)", builderScript->filename(), builderScript->lineno()); + JSRuntime *rt = cx->runtime(); + if (!builder->nurseryObjects().empty() && !rt->jitRuntime()->hasIonNurseryObjects()) { + // Ensure the builder's nursery objects are marked when a nursery + // GC happens on the main thread. + MarkOffThreadNurseryObjects mark; + rt->gc.storeBuffer.putGeneric(mark); + rt->jitRuntime()->setHasIonNurseryObjects(true); + } + if (!StartOffThreadIonCompile(cx, builder)) { JitSpew(JitSpew_IonAbort, "Unable to start off-thread ion compilation."); return AbortReason_Alloc; @@ -1861,7 +1971,7 @@ } static bool -CheckFrame(BaselineFrame *frame) +CheckFrame(JSContext *cx, BaselineFrame *frame) { MOZ_ASSERT(!frame->script()->isGenerator()); MOZ_ASSERT(!frame->isDebuggerEvalFrame()); @@ -1869,12 +1979,12 @@ // This check is to not overrun the stack. if (frame->isFunctionFrame()) { if (TooManyActualArguments(frame->numActualArgs())) { - JitSpew(JitSpew_IonAbort, "too many actual args"); + TrackAndSpewIonAbort(cx, frame->script(), "too many actual arguments"); return false; } if (TooManyFormalArguments(frame->numFormalArgs())) { - JitSpew(JitSpew_IonAbort, "too many args"); + TrackAndSpewIonAbort(cx, frame->script(), "too many arguments"); return false; } } @@ -1889,12 +1999,12 @@ // Eval frames are not yet supported. Supporting this will require new // logic in pushBailoutFrame to deal with linking prev. // Additionally, JSOP_DEFVAR support will require baking in isEvalFrame(). - JitSpew(JitSpew_IonAbort, "eval script"); + TrackAndSpewIonAbort(cx, script, "eval script"); return false; } if (script->isGenerator()) { - JitSpew(JitSpew_IonAbort, "generator script"); + TrackAndSpewIonAbort(cx, script, "generator script"); return false; } @@ -1902,7 +2012,7 @@ // Support non-CNG functions but not other scripts. For global scripts, // IonBuilder currently uses the global object as scope chain, this is // not valid for non-CNG code. - JitSpew(JitSpew_IonAbort, "not compile-and-go"); + TrackAndSpewIonAbort(cx, script, "not compile-and-go"); return false; } @@ -1923,6 +2033,7 @@ if (!OffThreadCompilationAvailable(cx)) { JitSpew(JitSpew_IonAbort, "Script too large (%u bytes) (%u locals/args)", script->length(), numLocalsAndArgs); + TrackIonAbort(cx, script, script->code(), "too large"); return Method_CantCompile; } } @@ -1957,7 +2068,7 @@ return Method_Skipped; if (script->isDebuggee() || (osrFrame && osrFrame->isDebuggee())) { - JitSpew(JitSpew_IonAbort, "debugging"); + TrackAndSpewIonAbort(cx, script, "debugging"); return Method_Skipped; } @@ -2045,7 +2156,7 @@ return Method_Skipped; // Mark as forbidden if frame can't be handled. - if (!CheckFrame(osrFrame)) { + if (!CheckFrame(cx, osrFrame)) { ForbidCompilation(cx, script); return Method_CantCompile; } @@ -2112,13 +2223,13 @@ InvokeState &invoke = *state.asInvoke(); if (TooManyActualArguments(invoke.args().length())) { - JitSpew(JitSpew_IonAbort, "too many actual args"); + TrackAndSpewIonAbort(cx, script, "too many actual args"); ForbidCompilation(cx, script); return Method_CantCompile; } if (TooManyFormalArguments(invoke.args().callee().as().nargs())) { - JitSpew(JitSpew_IonAbort, "too many args"); + TrackAndSpewIonAbort(cx, script, "too many args"); ForbidCompilation(cx, script); return Method_CantCompile; } @@ -2157,7 +2268,7 @@ MOZ_ASSERT(frame->isFunctionFrame()); // Mark as forbidden if frame can't be handled. - if (!CheckFrame(frame)) { + if (!CheckFrame(cx, frame)) { ForbidCompilation(cx, script); return Method_CantCompile; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/IonTypes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/IonTypes.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/IonTypes.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/IonTypes.h 2015-02-03 14:33:31.000000000 +0000 @@ -8,7 +8,6 @@ #define jit_IonTypes_h #include "mozilla/HashFunctions.h" -#include "mozilla/TypedEnum.h" #include "jstypes.h" @@ -101,6 +100,10 @@ Bailout_NonStringInput, Bailout_NonSymbolInput, + // SIMD Unbox expects a given type, bails out if it doesn't match. + Bailout_NonSimdInt32x4Input, + Bailout_NonSimdFloat32x4Input, + // For the initial snapshot when entering a function. Bailout_InitialState, @@ -194,6 +197,10 @@ return "Bailout_NonStringInput"; case Bailout_NonSymbolInput: return "Bailout_NonSymbolInput"; + case Bailout_NonSimdInt32x4Input: + return "Bailout_NonSimdInt32x4Input"; + case Bailout_NonSimdFloat32x4Input: + return "Bailout_NonSimdFloat32x4Input"; case Bailout_InitialState: return "Bailout_InitialState"; case Bailout_Debugger: @@ -372,8 +379,8 @@ MIRType_Elements, // An elements vector MIRType_Pointer, // An opaque pointer that receives no special treatment MIRType_Shape, // A Shape pointer. - MIRType_TypeObject, // A TypeObject pointer. - MIRType_Last = MIRType_TypeObject, + MIRType_ObjectGroup, // An ObjectGroup pointer. + MIRType_Last = MIRType_ObjectGroup, MIRType_Float32x4 = MIRType_Float32 | (2 << VECTOR_SCALE_SHIFT), MIRType_Int32x4 = MIRType_Int32 | (2 << VECTOR_SCALE_SHIFT), MIRType_Doublex2 = MIRType_Double | (1 << VECTOR_SCALE_SHIFT) @@ -629,10 +636,17 @@ // int f(int, double) Args_Int_IntDouble = Args_General0 | (ArgType_Double << (ArgType_Shift * 1)) | - (ArgType_General << (ArgType_Shift * 2)) + (ArgType_General << (ArgType_Shift * 2)), + + // double f(double, double, double) + Args_Double_DoubleDoubleDouble = Args_Double_DoubleDouble | (ArgType_Double << (ArgType_Shift * 3)), + + // double f(double, double, double, double) + Args_Double_DoubleDoubleDoubleDouble = Args_Double_DoubleDoubleDouble | (ArgType_Double << (ArgType_Shift * 4)) + }; -MOZ_BEGIN_ENUM_CLASS(BarrierKind, uint32_t) +enum class BarrierKind : uint32_t { // No barrier is needed. NoBarrier, @@ -643,7 +657,7 @@ // Check if the value is in the TypeSet, including the object type if it's // an object. TypeSet -MOZ_END_ENUM_CLASS(BarrierKind) +}; } // namespace jit } // namespace js diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/JitcodeMap.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/JitcodeMap.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/JitcodeMap.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/JitcodeMap.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -109,6 +109,19 @@ // Free the script list js_free(scriptList_); scriptList_ = nullptr; + + // The optimizations region and attempts table is in the same block of + // memory, the beginning of which is pointed to by + // optimizationsRegionTable_->payloadStart(). + if (optsRegionTable_) { + MOZ_ASSERT(optsAttemptsTable_); + js_free((void *) optsRegionTable_->payloadStart()); + } + optsRegionTable_ = nullptr; + optsTypesTable_ = nullptr; + optsAttemptsTable_ = nullptr; + js_delete(optsAllTypes_); + optsAllTypes_ = nullptr; } bool diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/JitcodeMap.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/JitcodeMap.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/JitcodeMap.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/JitcodeMap.h 2015-02-03 14:33:31.000000000 +0000 @@ -10,6 +10,7 @@ #include "ds/SplayTree.h" #include "jit/CompactBuffer.h" #include "jit/CompileInfo.h" +#include "jit/OptimizationTracking.h" #include "jit/shared/CodeGenerator-shared.h" namespace js { @@ -106,6 +107,23 @@ // of the memory space. JitcodeIonTable *regionTable_; + // optsRegionTable_ points to the table within the compact + // optimizations map indexing all regions that have tracked + // optimization attempts. optsTypesTable_ is the tracked typed info + // associated with the attempts vectors; it is the same length as the + // attempts table. optsAttemptsTable_ is the table indexing those + // attempts vectors. + // + // All pointers point into the same block of memory; the beginning of + // the block is optimizationRegionTable_->payloadStart(). + const IonTrackedOptimizationsRegionTable *optsRegionTable_; + const IonTrackedOptimizationsTypesTable *optsTypesTable_; + const IonTrackedOptimizationsAttemptsTable *optsAttemptsTable_; + + // The types table above records type sets, which have been gathered + // into one vector here. + types::TypeSet::TypeList *optsAllTypes_; + struct ScriptNamePair { JSScript *script; char *str; @@ -136,6 +154,21 @@ BaseEntry::init(Ion, nativeStartAddr, nativeEndAddr); regionTable_ = regionTable; scriptList_ = scriptList; + optsRegionTable_ = nullptr; + optsTypesTable_ = nullptr; + optsAllTypes_ = nullptr; + optsAttemptsTable_ = nullptr; + } + + void initTrackedOptimizations(const IonTrackedOptimizationsRegionTable *regionTable, + const IonTrackedOptimizationsTypesTable *typesTable, + const IonTrackedOptimizationsAttemptsTable *attemptsTable, + types::TypeSet::TypeList *allTypes) + { + optsRegionTable_ = regionTable; + optsTypesTable_ = typesTable; + optsAttemptsTable_ = attemptsTable; + optsAllTypes_ = allTypes; } SizedScriptList *sizedScriptList() const { @@ -176,6 +209,12 @@ uint32_t callStackAtAddr(JSRuntime *rt, void *ptr, const char **results, uint32_t maxResults) const; + + bool hasTrackedOptimizations() const { + return !!optsRegionTable_; + } + + bool optimizationAttemptsAtAddr(void *ptr, mozilla::Maybe &attempts); }; struct BaselineEntry : public BaseEntry @@ -183,6 +222,12 @@ JSScript *script_; const char *str_; + // Last location that caused Ion to abort compilation and the reason + // therein, if any. Only actionable aborts are tracked. Internal + // errors like OOMs are not. + jsbytecode *ionAbortPc_; + const char *ionAbortMessage_; + void init(void *nativeStartAddr, void *nativeEndAddr, JSScript *script, const char *str) { MOZ_ASSERT(script != nullptr); @@ -199,6 +244,18 @@ return str_; } + void trackIonAbort(jsbytecode *pc, const char *message) { + MOZ_ASSERT(script_->containsPC(pc)); + MOZ_ASSERT(message); + ionAbortPc_ = pc; + ionAbortMessage_ = message; + } + + bool hadIonAbort() const { + MOZ_ASSERT(!ionAbortPc_ || ionAbortMessage_); + return ionAbortPc_ != nullptr; + } + void destroy(); bool callStackAtAddr(JSRuntime *rt, void *ptr, BytecodeLocationVector &results, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/JitCompartment.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/JitCompartment.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/JitCompartment.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/JitCompartment.h 2015-02-03 14:33:31.000000000 +0000 @@ -186,7 +186,7 @@ JitCode *stringPreBarrier_; JitCode *objectPreBarrier_; JitCode *shapePreBarrier_; - JitCode *typeObjectPreBarrier_; + JitCode *objectGroupPreBarrier_; // Thunk to call malloc/free. JitCode *mallocStub_; @@ -235,6 +235,8 @@ // Global table of jitcode native address => bytecode address mappings. JitcodeGlobalTable *jitcodeGlobalTable_; + bool hasIonNurseryObjects_; + private: JitCode *generateLazyLinkStub(JSContext *cx); JitCode *generateProfilerExitFrameTailStub(JSContext *cx); @@ -359,7 +361,7 @@ case MIRType_String: return stringPreBarrier_; case MIRType_Object: return objectPreBarrier_; case MIRType_Shape: return shapePreBarrier_; - case MIRType_TypeObject: return typeObjectPreBarrier_; + case MIRType_ObjectGroup: return objectGroupPreBarrier_; default: MOZ_CRASH(); } } @@ -390,6 +392,13 @@ ionReturnOverride_ = v; } + bool hasIonNurseryObjects() const { + return hasIonNurseryObjects_; + } + void setHasIonNurseryObjects(bool b) { + hasIonNurseryObjects_ = b; + } + bool hasJitcodeGlobalTable() const { return jitcodeGlobalTable_ != nullptr; } @@ -402,6 +411,10 @@ bool isProfilerInstrumentationEnabled(JSRuntime *rt) { return rt->spsProfiler.enabled(); } + + bool isOptimizationTrackingEnabled(JSRuntime *rt) { + return isProfilerInstrumentationEnabled(rt); + } }; class JitZone diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/JitFrameIterator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/JitFrameIterator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/JitFrameIterator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/JitFrameIterator.h 2015-02-03 14:33:31.000000000 +0000 @@ -89,8 +89,9 @@ class JitActivation; // Iterate over the JIT stack to assert that all invariants are respected. -// - Check that all entry frames are aligned on StackAlignment. -void AssertValidJitStack(JSContext *cx); +// - Check that all entry frames are aligned on JitStackAlignment. +// - Check that all rectifier frames keep the JitStackAlignment. +void AssertJitStackInvariants(JSContext *cx); class JitFrameIterator { @@ -278,7 +279,8 @@ inline JitFrameLayout *framePtr(); inline JSScript *frameScript(); bool tryInitWithPC(void *pc); - bool tryInitWithTable(JitcodeGlobalTable *table, void *pc, JSRuntime *rt); + bool tryInitWithTable(JitcodeGlobalTable *table, void *pc, JSRuntime *rt, + bool forLastCallSite); public: JitProfilingFrameIterator(JSRuntime *rt, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/JitFrames.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/JitFrames.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/JitFrames.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/JitFrames.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -98,12 +98,12 @@ } JitFrameIterator::JitFrameIterator(JSContext *cx) - : current_(cx->perThreadData->jitTop), + : current_(cx->runtime()->jitTop), type_(JitFrame_Exit), returnAddressToFp_(nullptr), frameSize_(0), cachedSafepointIndex_(nullptr), - activation_(cx->perThreadData->activation()->asJit()) + activation_(cx->runtime()->activation()->asJit()) { if (activation_->bailoutData()) { current_ = activation_->bailoutData()->fp(); @@ -389,10 +389,10 @@ // debuggee of a Debugger with a live onExceptionUnwind hook, or if a // Debugger has observed this frame (e.g., for onPop). bool shouldBail = Debugger::hasLiveHook(cx->global(), Debugger::OnExceptionUnwind); + RematerializedFrame *rematFrame = nullptr; if (!shouldBail) { - JitActivation *act = cx->mainThread().activation()->asJit(); - RematerializedFrame *rematFrame = - act->lookupRematerializedFrame(frame.frame().fp(), frame.frameNo()); + JitActivation *act = cx->runtime()->activation()->asJit(); + rematFrame = act->lookupRematerializedFrame(frame.frame().fp(), frame.frameNo()); shouldBail = rematFrame && rematFrame->isDebuggee(); } @@ -414,7 +414,19 @@ uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, propagateInfo, overrecursed); if (retval == BAILOUT_RETURN_OK) return; + + // If bailout failed (e.g., due to overrecursion), clean up any + // Debugger.Frame instances here. Normally this should happen + // inside the debug epilogue, but due to bailout failure, we + // cannot honor any Debugger hooks. + if (rematFrame) + Debugger::handleUnrecoverableIonBailoutError(cx, rematFrame); } + +#ifdef DEBUG + if (rematFrame) + Debugger::assertNotInFrameMaps(rematFrame); +#endif } if (!script->hasTrynotes()) @@ -693,10 +705,10 @@ if (!cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) return; - MOZ_ASSERT(cx->mainThread().jitActivation == cx->mainThread().profilingActivation()); + MOZ_ASSERT(cx->runtime()->jitActivation == cx->runtime()->profilingActivation()); void *lastProfilingFrame = getLastProfilingFrame(); - cx->mainThread().jitActivation->setLastProfilingFrame(lastProfilingFrame); + cx->runtime()->jitActivation->setLastProfilingFrame(lastProfilingFrame); } void *getLastProfilingFrame() { @@ -740,7 +752,7 @@ if (cx->runtime()->jitRuntime()->hasIonReturnOverride()) cx->runtime()->jitRuntime()->takeIonReturnOverride(); - JitActivation *activation = cx->mainThread().activation()->asJit(); + JitActivation *activation = cx->runtime()->activation()->asJit(); // The Debugger onExceptionUnwind hook (reachable via // HandleExceptionBaseline below) may cause on-stack recompilation of @@ -863,7 +875,7 @@ // not crash when accessing an IonScript that's destroyed by the // ionScript->decref call. EnsureExitFrame(current); - cx->mainThread().jitTop = (uint8_t *)current; + cx->runtime()->jitTop = (uint8_t *)current; } if (overrecursed) { @@ -960,23 +972,24 @@ #endif static void -MarkExtraActualArguments(JSTracer *trc, const JitFrameIterator &frame) +MarkThisAndExtraActualArguments(JSTracer *trc, const JitFrameIterator &frame) { - // Mark any extra actual arguments for an Ion frame. Marking of |this| and - // the formal arguments is taken care of by the frame's safepoint/snapshot. + // Mark |this| and any extra actual arguments for an Ion frame. Marking of + // formal arguments is taken care of by the frame's safepoint/snapshot. JitFrameLayout *layout = frame.jsFrame(); - if (!CalleeTokenIsFunction(layout->calleeToken())) { - MOZ_ASSERT(frame.numActualArgs() == 0); - return; - } - size_t nargs = frame.numActualArgs(); - size_t nformals = CalleeTokenToFunction(layout->calleeToken())->nargs(); + size_t nformals = 0; + if (CalleeTokenIsFunction(layout->calleeToken())) + nformals = CalleeTokenToFunction(layout->calleeToken())->nargs(); - // Trace actual arguments. Note + 1 for thisv. Value *argv = layout->argv(); + + // Trace |this|. + gc::MarkValueRoot(trc, argv, "ion-thisv"); + + // Trace actual arguments beyond the formals. Note + 1 for thisv. for (size_t i = nformals + 1; i < nargs + 1; i++) gc::MarkValueRoot(trc, &argv[i], "ion-argv"); } @@ -1011,7 +1024,7 @@ ionScript = frame.ionScriptFromCalleeToken(); } - MarkExtraActualArguments(trc, frame); + MarkThisAndExtraActualArguments(trc, frame); const SafepointIndex *si = ionScript->getSafepointIndex(frame.returnAddressToFp()); @@ -1070,7 +1083,7 @@ // We have to mark the list of actual arguments, as only formal arguments // are represented in the Snapshot. - MarkExtraActualArguments(trc, frame); + MarkThisAndExtraActualArguments(trc, frame); // Under a bailout, do not have a Safepoint to only iterate over GC-things. // Thus we use a SnapshotIterator to trace all the locations which would be @@ -1442,9 +1455,9 @@ } void -MarkJitActivations(PerThreadData *ptd, JSTracer *trc) +MarkJitActivations(JSRuntime *rt, JSTracer *trc) { - for (JitActivationIterator activations(ptd); !activations.done(); ++activations) + for (JitActivationIterator activations(rt); !activations.done(); ++activations) MarkJitActivation(trc, activations); } @@ -1460,10 +1473,10 @@ return nullptr; } -void UpdateJitActivationsForMinorGC(PerThreadData *ptd, JSTracer *trc) +void UpdateJitActivationsForMinorGC(JSRuntime *rt, JSTracer *trc) { MOZ_ASSERT(trc->runtime()->isHeapMinorCollecting()); - for (JitActivationIterator activations(ptd); !activations.done(); ++activations) { + for (JitActivationIterator activations(rt); !activations.done(); ++activations) { for (JitFrameIterator frames(activations); !frames.done(); ++frames) { if (frames.type() == JitFrame_IonJS) UpdateIonJSFrameForMinorGC(trc, frames); @@ -2737,16 +2750,16 @@ { // If no profilingActivation is live, initialize directly to // end-of-iteration state. - if (!rt->mainThread.profilingActivation()) { + if (!rt->profilingActivation()) { type_ = JitFrame_Entry; fp_ = nullptr; returnAddressToFp_ = nullptr; return; } - MOZ_ASSERT(rt->mainThread.profilingActivation()->isJit()); + MOZ_ASSERT(rt->profilingActivation()->isJit()); - JitActivation *act = rt->mainThread.profilingActivation()->asJit(); + JitActivation *act = rt->profilingActivation()->asJit(); // If the top JitActivation has a null lastProfilingFrame, assume that // it's a trivially empty activation, and initialize directly @@ -2767,15 +2780,12 @@ // Profiler sampling must NOT be suppressed if we are here. MOZ_ASSERT(rt->isProfilerSamplingEnabled()); - // Since the frame is on stack, and is a jit frame, it MUST have Baseline jitcode. - MOZ_ASSERT(frameScript()->hasBaselineScript()); - // Try initializing with sampler pc if (tryInitWithPC(state.pc)) return; // Try initializing with sampler pc using native=>bytecode table. - if (tryInitWithTable(table, state.pc, rt)) + if (tryInitWithTable(table, state.pc, rt, /* forLastCallSite = */ false)) return; // Try initializing with lastProfilingCallSite pc @@ -2784,15 +2794,24 @@ return; // Try initializing with lastProfilingCallSite pc using native=>bytecode table. - if (tryInitWithTable(table, lastCallSite, rt)) + if (tryInitWithTable(table, lastCallSite, rt, /* forLastCallSite = */ true)) return; } + // In some rare cases (e.g. baseline eval frame), the callee script may + // not have a baselineScript. Treat this is an empty frame-sequence and + // move on. + if (!frameScript()->hasBaselineScript()) { + type_ = JitFrame_Entry; + fp_ = nullptr; + returnAddressToFp_ = nullptr; + return; + } + // If nothing matches, for now just assume we are at the start of the last frame's // baseline jit code. type_ = JitFrame_BaselineJS; returnAddressToFp_ = frameScript()->baselineScript()->method()->raw(); - //++(*this); } template @@ -2849,7 +2868,7 @@ } // Check for containment in Baseline jitcode second. - if (callee->baselineScript()->method()->containsNativePC(pc)) { + if (callee->hasBaselineScript() && callee->baselineScript()->method()->containsNativePC(pc)) { type_ = JitFrame_BaselineJS; returnAddressToFp_ = pc; return true; @@ -2859,7 +2878,8 @@ } bool -JitProfilingFrameIterator::tryInitWithTable(JitcodeGlobalTable *table, void *pc, JSRuntime *rt) +JitProfilingFrameIterator::tryInitWithTable(JitcodeGlobalTable *table, void *pc, JSRuntime *rt, + bool forLastCallSite) { if (!pc) return false; @@ -2883,7 +2903,7 @@ if (entry.isBaseline()) { // If looked-up callee doesn't match frame callee, don't accept lastProfilingCallSite - if (entry.baselineEntry().script() != callee) + if (forLastCallSite && entry.baselineEntry().script() != callee) return false; type_ = JitFrame_BaselineJS; @@ -3026,12 +3046,30 @@ } void -AssertValidJitStack(JSContext *cx) +AssertJitStackInvariants(JSContext *cx) { for (JitActivationIterator activations(cx->runtime()); !activations.done(); ++activations) { JitFrameIterator frames(activations); - for (; !frames.done(); ++frames) - continue; + for (; !frames.done(); ++frames) { + + if (frames.prevType() == JitFrame_Rectifier) { + size_t calleeFp = reinterpret_cast(frames.fp()); + size_t callerFp = reinterpret_cast(frames.prevFp()); + MOZ_ASSERT(callerFp >= calleeFp); + size_t frameSize = callerFp - calleeFp; + + MOZ_RELEASE_ASSERT(frameSize % JitStackAlignment == 0, + "The rectifier frame should keep the alignment"); + + size_t expectedFrameSize = 0 + + sizeof(Value) * (frames.callee()->nargs() + 1 /* |this| argument */ ) + + sizeof(JitFrameLayout); + MOZ_RELEASE_ASSERT(frameSize >= expectedFrameSize, + "The frame is large enough to hold all arguments"); + MOZ_RELEASE_ASSERT(expectedFrameSize + JitStackAlignment > frameSize, + "The frame size is optimal"); + } + } MOZ_RELEASE_ASSERT(frames.type() == JitFrame_Entry, "The first frame of a Jit activation should be an entry frame"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/JitFrames.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/JitFrames.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/JitFrames.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/JitFrames.h 2015-02-03 14:33:31.000000000 +0000 @@ -273,13 +273,13 @@ void EnsureExitFrame(CommonFrameLayout *frame); -void MarkJitActivations(PerThreadData *ptd, JSTracer *trc); +void MarkJitActivations(JSRuntime *rt, JSTracer *trc); void MarkIonCompilerRoots(JSTracer *trc); JSCompartment * TopmostIonActivationCompartment(JSRuntime *rt); -void UpdateJitActivationsForMinorGC(PerThreadData *ptd, JSTracer *trc); +void UpdateJitActivationsForMinorGC(JSRuntime *rt, JSTracer *trc); static inline uint32_t MakeFrameDescriptor(uint32_t frameSize, FrameType type) @@ -475,6 +475,18 @@ class IonOOLProxyExitFrameLayout; class IonDOMExitFrameLayout; +enum ExitFrameTokenValues +{ + NativeExitFrameLayoutToken = 0x0, + IonDOMExitFrameLayoutGetterToken = 0x1, + IonDOMExitFrameLayoutSetterToken = 0x2, + IonDOMMethodExitFrameLayoutToken = 0x3, + IonOOLNativeExitFrameLayoutToken = 0x4, + IonOOLPropertyOpExitFrameLayoutToken = 0x5, + IonOOLProxyExitFrameLayoutToken = 0x6, + ExitFrameLayoutBareToken = 0xFF +}; + // this is the frame layout when we are exiting ion code, and about to enter platform ABI code class ExitFrameLayout : public CommonFrameLayout { @@ -485,7 +497,7 @@ public: // Pushed for "bare" fake exit frames that have no GC things on stack to be // marked. - static JitCode *BareToken() { return (JitCode *)0xFF; } + static JitCode *BareToken() { return (JitCode *)ExitFrameLayoutBareToken; } static inline size_t Size() { return sizeof(ExitFrameLayout); @@ -539,7 +551,7 @@ uint32_t hiCalleeResult_; public: - static JitCode *Token() { return (JitCode *)0x0; } + static JitCode *Token() { return (JitCode *)NativeExitFrameLayoutToken; } static inline size_t Size() { return sizeof(NativeExitFrameLayout); @@ -577,7 +589,7 @@ uint32_t hiThis_; public: - static JitCode *Token() { return (JitCode *)0x4; } + static JitCode *Token() { return (JitCode *)IonOOLNativeExitFrameLayoutToken; } static inline size_t Size(size_t argc) { // The frame accounts for the callee/result and |this|, so we only need args. @@ -623,7 +635,7 @@ JitCode *stubCode_; public: - static JitCode *Token() { return (JitCode *)0x5; } + static JitCode *Token() { return (JitCode *)IonOOLPropertyOpExitFrameLayoutToken; } static inline size_t Size() { return sizeof(IonOOLPropertyOpExitFrameLayout); @@ -675,7 +687,7 @@ JitCode *stubCode_; public: - static JitCode *Token() { return (JitCode *)0x6; } + static JitCode *Token() { return (JitCode *)IonOOLProxyExitFrameLayoutToken; } static inline size_t Size() { return sizeof(IonOOLProxyExitFrameLayout); @@ -715,8 +727,8 @@ uint32_t hiCalleeResult_; public: - static JitCode *GetterToken() { return (JitCode *)0x1; } - static JitCode *SetterToken() { return (JitCode *)0x2; } + static JitCode *GetterToken() { return (JitCode *)IonDOMExitFrameLayoutGetterToken; } + static JitCode *SetterToken() { return (JitCode *)IonDOMExitFrameLayoutSetterToken; } static inline size_t Size() { return sizeof(IonDOMExitFrameLayout); @@ -755,7 +767,7 @@ friend struct IonDOMMethodExitFrameLayoutTraits; public: - static JitCode *Token() { return (JitCode *)0x3; } + static JitCode *Token() { return (JitCode *)IonDOMMethodExitFrameLayoutToken; } static inline size_t Size() { return sizeof(IonDOMMethodExitFrameLayout); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/JitSpewer.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/JitSpewer.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/JitSpewer.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/JitSpewer.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -255,6 +255,7 @@ " unroll Loop unrolling\n" " logs C1 and JSON visualization logging\n" " profiling Profiling-related information\n" + " trackopts Optimization tracking information\n" " all Everything\n" "\n" " bl-aborts Baseline compiler abort messages\n" @@ -315,6 +316,8 @@ EnableIonDebugLogging(); if (ContainsFlag(env, "profiling")) EnableChannel(JitSpew_Profiling); + if (ContainsFlag(env, "trackopts")) + EnableChannel(JitSpew_OptimizationTracking); if (ContainsFlag(env, "all")) LoggingBits = uint32_t(-1); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/JitSpewer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/JitSpewer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/JitSpewer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/JitSpewer.h 2015-02-03 14:33:31.000000000 +0000 @@ -46,6 +46,8 @@ _(Pools) \ /* Profiling-related information */ \ _(Profiling) \ + /* Information of tracked opt strats */ \ + _(OptimizationTracking) \ /* Debug info about the I$ */ \ _(CacheFlush) \ \ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/LinearScan.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/LinearScan.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/LinearScan.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/LinearScan.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -484,15 +484,11 @@ bool LinearScanAllocator::populateSafepoints() { - // Populate all safepoints with this/argument slots. These are never changed + // Populate all safepoints with argument slots. These are never changed // by the allocator and are not necessarily populated by the code below. size_t nargs = graph.getBlock(0)->mir()->info().nargs(); for (size_t i = 0; i < graph.numSafepoints(); i++) { LSafepoint *safepoint = graph.getSafepoint(i)->safepoint(); - - if (!safepoint->addValueSlot(/* stack = */ false, THIS_FRAME_ARGSLOT * sizeof(Value))) - return false; - for (size_t j = 0; j < nargs; j++) { if (!safepoint->addValueSlot(/* stack = */ false, (j + 1) * sizeof(Value))) return false; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/LIR-Common.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/LIR-Common.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/LIR-Common.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/LIR-Common.h 2015-02-03 14:33:31.000000000 +0000 @@ -108,6 +108,11 @@ { js::Vector moves_; +#ifdef JS_CODEGEN_X86 + // Optional general register available for use when executing moves. + LAllocation scratchRegister_; +#endif + explicit LMoveGroup(TempAllocator &alloc) : moves_(alloc) { } @@ -133,6 +138,19 @@ const LMove &getMove(size_t i) const { return moves_[i]; } + +#ifdef JS_CODEGEN_X86 + void setScratchRegister(Register reg) { + scratchRegister_ = LGeneralReg(reg); + } +#endif + LAllocation maybeScratchRegister() { +#ifdef JS_CODEGEN_X86 + return scratchRegister_; +#else + return LAllocation(); +#endif + } }; @@ -157,6 +175,26 @@ } }; +class LSimdUnbox : public LInstructionHelper<1, 1, 1> +{ + public: + LIR_HEADER(SimdUnbox) + + LSimdUnbox(const LAllocation &obj, const LDefinition &temp) + { + setOperand(0, obj); + setTemp(0, temp); + } + + const LDefinition *temp() { + return getTemp(0); + } + + MSimdUnbox *mir() const { + return mir_->toSimdUnbox(); + } +}; + // Constructs a SIMD value with 4 equal components (e.g. int32x4, float32x4). class LSimdSplatX4 : public LInstructionHelper<1, 1, 0> { @@ -645,6 +683,16 @@ } }; +class LNurseryObject : public LInstructionHelper<1, 0, 0> +{ + public: + LIR_HEADER(NurseryObject); + + MNurseryObject *mir() const { + return mir_->toNurseryObject(); + } +}; + // Clone an object literal such as we are not modifying the object contained in // the sources. class LCloneLiteral : public LCallInstructionHelper<1, 1, 0> @@ -2050,15 +2098,15 @@ } }; -class LTypeObjectDispatch : public LInstructionHelper<0, 1, 1> +class LObjectGroupDispatch : public LInstructionHelper<0, 1, 1> { - // Dispatch is performed based on a TypeObject -> block + // Dispatch is performed based on an ObjectGroup -> block // map inferred by the MIR. public: - LIR_HEADER(TypeObjectDispatch); + LIR_HEADER(ObjectGroupDispatch); - LTypeObjectDispatch(const LAllocation &in, const LDefinition &temp) { + LObjectGroupDispatch(const LAllocation &in, const LDefinition &temp) { setOperand(0, in); setTemp(0, temp); } @@ -2067,8 +2115,8 @@ return getTemp(0); } - MTypeObjectDispatch *mir() { - return mir_->toTypeObjectDispatch(); + MObjectGroupDispatch *mir() { + return mir_->toObjectGroupDispatch(); } }; @@ -2945,16 +2993,40 @@ } }; -class LHypot : public LCallInstructionHelper<1, 2, 1> +class LHypot : public LCallInstructionHelper<1, 4, 1> { + uint32_t numOperands_; public: LIR_HEADER(Hypot) - LHypot(const LAllocation &x, const LAllocation &y, const LDefinition &temp) { + LHypot(const LAllocation &x, const LAllocation &y, const LDefinition &temp) + : numOperands_(2) + { setOperand(0, x); setOperand(1, y); setTemp(0, temp); } + LHypot(const LAllocation &x, const LAllocation &y, const LAllocation &z, const LDefinition &temp) + : numOperands_(3) + { + setOperand(0, x); + setOperand(1, y); + setOperand(2, z); + setTemp(0, temp); + } + + LHypot(const LAllocation &x, const LAllocation &y, const LAllocation &z, const LAllocation &w, const LDefinition &temp) + : numOperands_(4) + { + setOperand(0, x); + setOperand(1, y); + setOperand(2, z); + setOperand(3, w); + setTemp(0, temp); + } + + uint32_t numArgs() const { return numOperands_; } + const LAllocation *x() { return getOperand(0); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/LIR.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/LIR.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/LIR.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/LIR.h 2015-02-03 14:33:31.000000000 +0000 @@ -67,7 +67,6 @@ protected: static const uintptr_t DATA_BITS = (sizeof(uint32_t) * 8) - KIND_BITS; static const uintptr_t DATA_SHIFT = KIND_SHIFT + KIND_BITS; - static const uintptr_t DATA_MASK = (1 << DATA_BITS) - 1; public: enum Kind { @@ -80,6 +79,8 @@ ARGUMENT_SLOT // Argument slot. }; + static const uintptr_t DATA_MASK = (1 << DATA_BITS) - 1; + protected: uint32_t data() const { return uint32_t(bits_) >> DATA_SHIFT; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/LiveRangeAllocator.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/LiveRangeAllocator.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/LiveRangeAllocator.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/LiveRangeAllocator.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -47,7 +47,7 @@ char *cursor = buf; char *end = cursor + sizeof(buf); - int n; + int n = -1; // initialize to silence GCC warning switch (kind()) { case NONE: return "none"; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/LOpcodes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/LOpcodes.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/LOpcodes.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/LOpcodes.h 2015-02-03 14:33:31.000000000 +0000 @@ -18,6 +18,7 @@ _(Double) \ _(Float32) \ _(SimdBox) \ + _(SimdUnbox) \ _(SimdSplatX4) \ _(Int32x4) \ _(Float32x4) \ @@ -102,7 +103,7 @@ _(TestVAndBranch) \ _(TestOAndBranch) \ _(FunctionDispatch) \ - _(TypeObjectDispatch) \ + _(ObjectGroupDispatch) \ _(Compare) \ _(CompareAndBranch) \ _(CompareD) \ @@ -200,7 +201,7 @@ _(StoreSlotT) \ _(GuardShape) \ _(GuardShapePolymorphic) \ - _(GuardObjectType) \ + _(GuardObjectGroup) \ _(GuardObjectIdentity) \ _(GuardClass) \ _(TypeBarrierV) \ @@ -334,6 +335,7 @@ _(AssertRangeV) \ _(LexicalCheck) \ _(ThrowUninitializedLexical) \ + _(NurseryObject) \ _(Debugger) #if defined(JS_CODEGEN_X86) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Lowering.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Lowering.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Lowering.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Lowering.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -201,7 +201,7 @@ LIRGenerator::visitNewCallObject(MNewCallObject *ins) { LInstruction *lir; - if (ins->templateObject()->hasSingletonType()) { + if (ins->templateObject()->isSingleton()) { LNewSingletonCallObject *singletonLir = new(alloc()) LNewSingletonCallObject(temp()); define(singletonLir, ins); lir = singletonLir; @@ -844,9 +844,9 @@ } void -LIRGenerator::visitTypeObjectDispatch(MTypeObjectDispatch *ins) +LIRGenerator::visitObjectGroupDispatch(MObjectGroupDispatch *ins) { - LTypeObjectDispatch *lir = new(alloc()) LTypeObjectDispatch(useRegister(ins->input()), temp()); + LObjectGroupDispatch *lir = new(alloc()) LObjectGroupDispatch(useRegister(ins->input()), temp()); add(lir, ins); } @@ -1306,13 +1306,34 @@ void LIRGenerator::visitHypot(MHypot *ins) { - MDefinition *x = ins->x(); - MOZ_ASSERT(x->type() == MIRType_Double); - - MDefinition *y = ins->y(); - MOZ_ASSERT(y->type() == MIRType_Double); + LHypot *lir = nullptr; + uint32_t length = ins->numOperands(); + for (uint32_t i = 0; i < length; ++i) + MOZ_ASSERT(ins->getOperand(i)->type() == MIRType_Double); + + switch(length) { + case 2: + lir = new(alloc()) LHypot(useRegisterAtStart(ins->getOperand(0)), + useRegisterAtStart(ins->getOperand(1)), + tempFixed(CallTempReg0)); + break; + case 3: + lir = new(alloc()) LHypot(useRegisterAtStart(ins->getOperand(0)), + useRegisterAtStart(ins->getOperand(1)), + useRegisterAtStart(ins->getOperand(2)), + tempFixed(CallTempReg0)); + break; + case 4: + lir = new(alloc()) LHypot(useRegisterAtStart(ins->getOperand(0)), + useRegisterAtStart(ins->getOperand(1)), + useRegisterAtStart(ins->getOperand(2)), + useRegisterAtStart(ins->getOperand(3)), + tempFixed(CallTempReg0)); + break; + default: + MOZ_CRASH("Unexpected number of arguments to LHypot."); + } - LHypot *lir = new(alloc()) LHypot(useRegisterAtStart(x), useRegisterAtStart(y), tempFixed(CallTempReg0)); defineReturn(lir, ins); } @@ -2103,11 +2124,11 @@ void LIRGenerator::visitLambda(MLambda *ins) { - if (ins->info().singletonType || ins->info().useNewTypeForClone) { + if (ins->info().singletonType || ins->info().useSingletonForClone) { // If the function has a singleton type, this instruction will only be // executed once so we don't bother inlining it. // - // If UseNewTypeForClone is true, we will assign a singleton type to + // If UseSingletonForClone is true, we will assign a singleton type to // the clone and we have to clone the script, we can't do that inline. LLambdaForSingleton *lir = new(alloc()) LLambdaForSingleton(useRegisterAtStart(ins->scopeChain())); defineReturn(lir, ins); @@ -2291,7 +2312,7 @@ return; } - // Handle typebarrier with specific TypeObject/SingleObjects. + // Handle typebarrier with specific ObjectGroup/SingleObjects. if (inputType == MIRType_Object && !types->hasType(types::Type::AnyObjectType()) && ins->barrierKind() != BarrierKind::TypeTagOnly) { @@ -2598,12 +2619,12 @@ if (ins->type() == MIRType_Object) { LLoadUnboxedPointerT *lir = new(alloc()) LLoadUnboxedPointerT(useRegister(ins->elements()), useRegisterOrConstant(ins->index())); - if (ins->bailOnNull()) + if (ins->nullBehavior() == MLoadUnboxedObjectOrNull::BailOnNull) assignSnapshot(lir, Bailout_TypeBarrierO); define(lir, ins); } else { MOZ_ASSERT(ins->type() == MIRType_Value); - MOZ_ASSERT(!ins->bailOnNull()); + MOZ_ASSERT(ins->nullBehavior() != MLoadUnboxedObjectOrNull::BailOnNull); LLoadUnboxedPointerV *lir = new(alloc()) LLoadUnboxedPointerV(useRegister(ins->elements()), useRegisterOrConstant(ins->index())); @@ -2827,7 +2848,7 @@ const LUse elements = useRegister(ins->elements()); const LAllocation index = useRegisterOrConstant(ins->index()); - MOZ_ASSERT(IsNumberType(ins->type())); + MOZ_ASSERT(IsNumberType(ins->type()) || ins->type() == MIRType_Boolean); // We need a temp register for Uint32Array with known double result. LDefinition tempDef = LDefinition::BogusTemp(); @@ -3735,6 +3756,30 @@ } void +LIRGenerator::visitSimdUnbox(MSimdUnbox *ins) +{ + MOZ_ASSERT(ins->input()->type() == MIRType_Object); + MOZ_ASSERT(IsSimdType(ins->type())); + LUse in = useRegister(ins->input()); + + BailoutKind kind; + switch (ins->type()) { + case MIRType_Int32x4: + kind = Bailout_NonSimdInt32x4Input; + break; + case MIRType_Float32x4: + kind = Bailout_NonSimdFloat32x4Input; + break; + default: + MOZ_CRASH("Unexpected SIMD Type."); + }; + + LSimdUnbox *lir = new(alloc()) LSimdUnbox(in, temp()); + assignSnapshot(lir, kind); + define(lir, ins); +} + +void LIRGenerator::visitSimdConstant(MSimdConstant *ins) { MOZ_ASSERT(IsSimdType(ins->type())); @@ -3958,6 +4003,12 @@ add(lir, ins); } +void +LIRGenerator::visitNurseryObject(MNurseryObject *ins) +{ + define(new(alloc()) LNurseryObject(), ins); +} + static void SpewResumePoint(MBasicBlock *block, MInstruction *ins, MResumePoint *resumePoint) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Lowering.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Lowering.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Lowering.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Lowering.h 2015-02-03 14:33:32.000000000 +0000 @@ -109,7 +109,7 @@ void visitTest(MTest *test); void visitGotoWithFake(MGotoWithFake *ins); void visitFunctionDispatch(MFunctionDispatch *ins); - void visitTypeObjectDispatch(MTypeObjectDispatch *ins); + void visitObjectGroupDispatch(MObjectGroupDispatch *ins); void visitCompare(MCompare *comp); void visitTypeOf(MTypeOf *ins); void visitToId(MToId *ins); @@ -266,6 +266,7 @@ void visitRecompileCheck(MRecompileCheck *ins); void visitMemoryBarrier(MMemoryBarrier *ins); void visitSimdBox(MSimdBox *ins); + void visitSimdUnbox(MSimdUnbox *ins); void visitSimdExtractElement(MSimdExtractElement *ins); void visitSimdInsertElement(MSimdInsertElement *ins); void visitSimdSignMask(MSimdSignMask *ins); @@ -286,6 +287,7 @@ void visitLexicalCheck(MLexicalCheck *ins); void visitThrowUninitializedLexical(MThrowUninitializedLexical *ins); void visitDebugger(MDebugger *ins); + void visitNurseryObject(MNurseryObject *ins); }; } // namespace jit diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MacroAssembler.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MacroAssembler.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MacroAssembler.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MacroAssembler.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -55,14 +55,14 @@ return 0; return 1; } - inline JSObject *getSingleObjectNoBarrier(unsigned) const { - if (t_.isSingleObject()) - return t_.singleObjectNoBarrier(); + inline JSObject *getSingletonNoBarrier(unsigned) const { + if (t_.isSingleton()) + return t_.singletonNoBarrier(); return nullptr; } - inline types::TypeObject *getTypeObjectNoBarrier(unsigned) const { - if (t_.isTypeObject()) - return t_.typeObjectNoBarrier(); + inline types::ObjectGroup *getGroupNoBarrier(unsigned) const { + if (t_.isGroup()) + return t_.groupNoBarrier(); return nullptr; } }; @@ -144,9 +144,9 @@ // properties become unknown, so check for this case. if (obj == scratch) extractObject(address, scratch); - loadPtr(Address(obj, JSObject::offsetOfType()), scratch); + loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch); branchTestPtr(Assembler::NonZero, - Address(scratch, types::TypeObject::offsetOfFlags()), + Address(scratch, types::ObjectGroup::offsetOfFlags()), Imm32(types::OBJECT_FLAG_UNKNOWN_PROPERTIES), &matched); assumeUnreachable("Unexpected object type"); @@ -175,22 +175,22 @@ BranchGCPtr lastBranch; MOZ_ASSERT(!lastBranch.isInitialized()); - bool hasTypeObjects = false; + bool hasObjectGroups = false; unsigned count = types->getObjectCount(); for (unsigned i = 0; i < count; i++) { - if (!types->getSingleObjectNoBarrier(i)) { - hasTypeObjects = hasTypeObjects || types->getTypeObjectNoBarrier(i); + if (!types->getSingletonNoBarrier(i)) { + hasObjectGroups = hasObjectGroups || types->getGroupNoBarrier(i); continue; } if (lastBranch.isInitialized()) lastBranch.emit(*this); - JSObject *object = types->getSingleObjectNoBarrier(i); + JSObject *object = types->getSingletonNoBarrier(i); lastBranch = BranchGCPtr(Equal, obj, ImmGCPtr(object), &matched); } - if (hasTypeObjects) { + if (hasObjectGroups) { // We are possibly going to overwrite the obj register. So already // emit the branch, since branch depends on previous value of obj // register and there is definitely a branch following. So no need @@ -201,17 +201,17 @@ // Note: Some platforms give the same register for obj and scratch. // Make sure when writing to scratch, the obj register isn't used anymore! - loadPtr(Address(obj, JSObject::offsetOfType()), scratch); + loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch); for (unsigned i = 0; i < count; i++) { - if (!types->getTypeObjectNoBarrier(i)) + if (!types->getGroupNoBarrier(i)) continue; if (lastBranch.isInitialized()) lastBranch.emit(*this); - types::TypeObject *object = types->getTypeObjectNoBarrier(i); - lastBranch = BranchGCPtr(Equal, scratch, ImmGCPtr(object), &matched); + types::ObjectGroup *group = types->getGroupNoBarrier(i); + lastBranch = BranchGCPtr(Equal, scratch, ImmGCPtr(group), &matched); } } @@ -306,7 +306,7 @@ template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const T &src, AnyRegister dest, Register temp, - Label *fail) + Label *fail, bool canonicalizeDoubles) { switch (arrayType) { case Scalar::Int8: @@ -344,7 +344,8 @@ break; case Scalar::Float64: loadDouble(src, dest.fpu()); - canonicalizeDouble(dest.fpu()); + if (canonicalizeDoubles) + canonicalizeDouble(dest.fpu()); break; default: MOZ_CRASH("Invalid typed array type"); @@ -352,9 +353,9 @@ } template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const Address &src, AnyRegister dest, - Register temp, Label *fail); + Register temp, Label *fail, bool canonicalizeDoubles); template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const BaseIndex &src, AnyRegister dest, - Register temp, Label *fail); + Register temp, Label *fail, bool canonicalizeDoubles); template void @@ -621,6 +622,214 @@ const Register &value, const BaseIndex &mem, Register temp1, Register temp2, AnyRegister output); +template +void +MacroAssembler::loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output) +{ + switch (type) { + case JSVAL_TYPE_BOOLEAN: + case JSVAL_TYPE_INT32: + case JSVAL_TYPE_STRING: { + Register outReg; + if (output.hasValue()) { + outReg = output.valueReg().scratchReg(); + } else { + MOZ_ASSERT(output.type() == MIRTypeFromValueType(type)); + outReg = output.typedReg().gpr(); + } + + switch (type) { + case JSVAL_TYPE_BOOLEAN: + load8ZeroExtend(address, outReg); + break; + case JSVAL_TYPE_INT32: + load32(address, outReg); + break; + case JSVAL_TYPE_STRING: + loadPtr(address, outReg); + break; + default: + MOZ_CRASH(); + } + + if (output.hasValue()) + tagValue(type, outReg, output.valueReg()); + break; + } + + case JSVAL_TYPE_OBJECT: + if (output.hasValue()) { + Register scratch = output.valueReg().scratchReg(); + loadPtr(address, scratch); + + Label notNull, done; + branchPtr(Assembler::NotEqual, scratch, ImmWord(0), ¬Null); + + moveValue(NullValue(), output.valueReg()); + jump(&done); + + bind(¬Null); + tagValue(JSVAL_TYPE_OBJECT, scratch, output.valueReg()); + + bind(&done); + } else { + // Reading null can't be possible here, as otherwise the result + // would be a value (either because null has been read before or + // because there is a barrier). + Register reg = output.typedReg().gpr(); + loadPtr(address, reg); +#ifdef DEBUG + Label ok; + branchTestPtr(Assembler::NonZero, reg, reg, &ok); + assumeUnreachable("Null not possible"); + bind(&ok); +#endif + } + break; + + case JSVAL_TYPE_DOUBLE: + // Note: doubles in unboxed objects are not accessed through other + // views and do not need canonicalization. + if (output.hasValue()) + loadValue(address, output.valueReg()); + else + loadDouble(address, output.typedReg().fpu()); + break; + + default: + MOZ_CRASH(); + } +} + +template void +MacroAssembler::loadUnboxedProperty(Address address, JSValueType type, + TypedOrValueRegister output); + +template void +MacroAssembler::loadUnboxedProperty(BaseIndex address, JSValueType type, + TypedOrValueRegister output); + +template +void +MacroAssembler::storeUnboxedProperty(T address, JSValueType type, + ConstantOrRegister value, Label *failure) +{ + switch (type) { + case JSVAL_TYPE_BOOLEAN: + if (value.constant()) { + if (value.value().isBoolean()) + store8(Imm32(value.value().toBoolean()), address); + else + jump(failure); + } else if (value.reg().hasTyped()) { + if (value.reg().type() == MIRType_Boolean) + store8(value.reg().typedReg().gpr(), address); + else + jump(failure); + } else { + if (failure) + branchTestBoolean(Assembler::NotEqual, value.reg().valueReg(), failure); + storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ 1); + } + break; + + case JSVAL_TYPE_INT32: + if (value.constant()) { + if (value.value().isInt32()) + store32(Imm32(value.value().toInt32()), address); + else + jump(failure); + } else if (value.reg().hasTyped()) { + if (value.reg().type() == MIRType_Int32) + store32(value.reg().typedReg().gpr(), address); + else + jump(failure); + } else { + if (failure) + branchTestInt32(Assembler::NotEqual, value.reg().valueReg(), failure); + storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ 4); + } + break; + + case JSVAL_TYPE_DOUBLE: + if (value.constant()) { + if (value.value().isNumber()) { + loadConstantDouble(value.value().toNumber(), ScratchDoubleReg); + storeDouble(ScratchDoubleReg, address); + } else { + jump(failure); + } + } else if (value.reg().hasTyped()) { + if (value.reg().type() == MIRType_Int32) { + convertInt32ToDouble(value.reg().typedReg().gpr(), ScratchDoubleReg); + storeDouble(ScratchDoubleReg, address); + } else if (value.reg().type() == MIRType_Double) { + storeDouble(value.reg().typedReg().fpu(), address); + } else { + jump(failure); + } + } else { + if (failure) + branchTestNumber(Assembler::NotEqual, value.reg().valueReg(), failure); + unboxValue(value.reg().valueReg(), AnyRegister(ScratchDoubleReg)); + storeDouble(ScratchDoubleReg, address); + } + break; + + case JSVAL_TYPE_OBJECT: + if (value.constant()) { + if (value.value().isObjectOrNull()) + storePtr(ImmGCPtr(value.value().toObjectOrNull()), address); + else + jump(failure); + } else if (value.reg().hasTyped()) { + MOZ_ASSERT(value.reg().type() != MIRType_Null); + if (value.reg().type() == MIRType_Object) + storePtr(value.reg().typedReg().gpr(), address); + else + jump(failure); + } else { + if (failure) { + Label ok; + branchTestNull(Assembler::Equal, value.reg().valueReg(), &ok); + branchTestObject(Assembler::NotEqual, value.reg().valueReg(), failure); + bind(&ok); + } + storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ sizeof(uintptr_t)); + } + break; + + case JSVAL_TYPE_STRING: + if (value.constant()) { + if (value.value().isString()) + storePtr(ImmGCPtr(value.value().toString()), address); + else + jump(failure); + } else if (value.reg().hasTyped()) { + if (value.reg().type() == MIRType_String) + storePtr(value.reg().typedReg().gpr(), address); + else + jump(failure); + } else { + if (failure) + branchTestString(Assembler::NotEqual, value.reg().valueReg(), failure); + storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ sizeof(uintptr_t)); + } + break; + + default: + MOZ_CRASH(); + } +} + +template void +MacroAssembler::storeUnboxedProperty(Address address, JSValueType type, + ConstantOrRegister value, Label *failure); + +template void +MacroAssembler::storeUnboxedProperty(BaseIndex address, JSValueType type, + ConstantOrRegister value, Label *failure); + // Inlined version of gc::CheckAllocatorState that checks the bare essentials // and bails for anything that cannot be handled with our jit allocators. void @@ -780,22 +989,22 @@ callFreeStub(slots); jump(fail); - bind(&success); + breakpoint(); } void -MacroAssembler::newGCThing(Register result, Register temp, NativeObject *templateObj, - gc::InitialHeap initialHeap, Label *fail) +MacroAssembler::newGCThing(Register result, Register temp, JSObject *templateObj, + gc::InitialHeap initialHeap, Label *fail) { // This method does not initialize the object: if external slots get // allocated into |temp|, there is no easy way for us to ensure the caller // frees them. Instead just assert this case does not happen. - MOZ_ASSERT(!templateObj->numDynamicSlots()); + MOZ_ASSERT_IF(templateObj->isNative(), !templateObj->as().numDynamicSlots()); gc::AllocKind allocKind = templateObj->asTenured().getAllocKind(); MOZ_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST); - allocateObject(result, temp, allocKind, templateObj->numDynamicSlots(), initialHeap, fail); + allocateObject(result, temp, allocKind, 0, initialHeap, fail); } void @@ -983,7 +1192,7 @@ // Fast initialization of an empty object returned by allocateObject(). storePtr(ImmGCPtr(templateObj->lastProperty()), Address(obj, JSObject::offsetOfShape())); - storePtr(ImmGCPtr(templateObj->type()), Address(obj, JSObject::offsetOfType())); + storePtr(ImmGCPtr(templateObj->group()), Address(obj, JSObject::offsetOfGroup())); if (templateObj->isNative()) { NativeObject *ntemplate = &templateObj->as(); @@ -1030,18 +1239,37 @@ } } } else if (templateObj->is()) { - InlineTypedObject *ntemplate = &templateObj->as(); + size_t nbytes = templateObj->as().size(); + const uint8_t *memory = templateObj->as().inlineTypedMem(); // Memcpy the contents of the template object to the new object. - size_t nbytes = ntemplate->size(); size_t offset = 0; while (nbytes) { - uintptr_t value = *(uintptr_t *)(ntemplate->inlineTypedMem() + offset); + uintptr_t value = *(uintptr_t *)(memory + offset); storePtr(ImmWord(value), Address(obj, InlineTypedObject::offsetOfDataStart() + offset)); nbytes = (nbytes < sizeof(uintptr_t)) ? 0 : nbytes - sizeof(uintptr_t); offset += sizeof(uintptr_t); } + } else if (templateObj->is()) { + const UnboxedLayout &layout = templateObj->as().layout(); + + // Initialize reference fields of the object, per UnboxedPlainObject::create. + if (const int32_t *list = layout.traceList()) { + while (*list != -1) { + storePtr(ImmGCPtr(GetJitContext()->runtime->names().empty), + Address(obj, UnboxedPlainObject::offsetOfData() + *list)); + list++; + } + list++; + while (*list != -1) { + storePtr(ImmWord(0), + Address(obj, UnboxedPlainObject::offsetOfData() + *list)); + list++; + } + // Unboxed objects don't have Values to initialize. + MOZ_ASSERT(*(list + 1) == -1); + } } else { MOZ_CRASH("Unknown object"); } @@ -1356,6 +1584,20 @@ breakpoint(); } +template +void +MacroAssembler::assertTestInt32(Condition cond, const T &value, const char *output) +{ +#ifdef DEBUG + Label ok; + branchTestInt32(cond, value, &ok); + assumeUnreachable(output); + bind(&ok); +#endif +} + +template void MacroAssembler::assertTestInt32(Condition, const Address &, const char *); + static void Printf0_(const char *output) { // Use stderr instead of stdout because this is only used for debug diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MacroAssembler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MacroAssembler.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MacroAssembler.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MacroAssembler.h 2015-02-03 14:33:32.000000000 +0000 @@ -289,13 +289,13 @@ loadPtr(Address(dest, Shape::offsetOfBase()), dest); } void loadObjClass(Register objReg, Register dest) { - loadPtr(Address(objReg, JSObject::offsetOfType()), dest); - loadPtr(Address(dest, types::TypeObject::offsetOfClasp()), dest); + loadPtr(Address(objReg, JSObject::offsetOfGroup()), dest); + loadPtr(Address(dest, types::ObjectGroup::offsetOfClasp()), dest); } void branchTestObjClass(Condition cond, Register obj, Register scratch, const js::Class *clasp, Label *label) { - loadPtr(Address(obj, JSObject::offsetOfType()), scratch); - branchPtr(cond, Address(scratch, types::TypeObject::offsetOfClasp()), ImmPtr(clasp), label); + loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch); + branchPtr(cond, Address(scratch, types::ObjectGroup::offsetOfClasp()), ImmPtr(clasp), label); } void branchTestObjShape(Condition cond, Register obj, const Shape *shape, Label *label) { branchPtr(cond, Address(obj, JSObject::offsetOfShape()), ImmGCPtr(shape), label); @@ -347,8 +347,8 @@ } void loadObjProto(Register obj, Register dest) { - loadPtr(Address(obj, JSObject::offsetOfType()), dest); - loadPtr(Address(dest, types::TypeObject::offsetOfProto()), dest); + loadPtr(Address(obj, JSObject::offsetOfGroup()), dest); + loadPtr(Address(dest, types::ObjectGroup::offsetOfProto()), dest); } void loadStringLength(Register str, Register dest) { @@ -694,7 +694,8 @@ } template - void loadFromTypedArray(Scalar::Type arrayType, const T &src, AnyRegister dest, Register temp, Label *fail); + void loadFromTypedArray(Scalar::Type arrayType, const T &src, AnyRegister dest, Register temp, Label *fail, + bool canonicalizeDoubles = true); template void loadFromTypedArray(Scalar::Type arrayType, const T &src, const ValueOperand &dest, bool allowDouble, @@ -732,6 +733,17 @@ void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const BaseIndex &dest); void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const Address &dest); + // Load a property from an UnboxedPlainObject. + template + void loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output); + + // Store a property to an UnboxedPlainObject, without triggering barriers. + // If failure is null, the value definitely has a type suitable for storing + // in the property. + template + void storeUnboxedProperty(T address, JSValueType type, + ConstantOrRegister value, Label *failure); + Register extractString(const Address &address, Register scratch) { return extractObject(address, scratch); } @@ -804,7 +816,7 @@ void createGCObject(Register result, Register temp, JSObject *templateObj, gc::InitialHeap initialHeap, Label *fail, bool initFixedSlots = true); - void newGCThing(Register result, Register temp, NativeObject *templateObj, + void newGCThing(Register result, Register temp, JSObject *templateObj, gc::InitialHeap initialHeap, Label *fail); void initGCThing(Register obj, Register temp, JSObject *templateObj, bool initFixedSlots = true); @@ -976,6 +988,10 @@ void link(JitCode *code); void assumeUnreachable(const char *output); + + template + void assertTestInt32(Condition cond, const T &value, const char *output); + void printf(const char *output); void printf(const char *output, Register value); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MCallOptimize.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MCallOptimize.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MCallOptimize.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MCallOptimize.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -7,6 +7,7 @@ #include "jsmath.h" #include "builtin/AtomicsObject.h" +#include "builtin/SIMD.h" #include "builtin/TestingFunctions.h" #include "builtin/TypedObject.h" #include "jit/BaselineInspector.h" @@ -32,8 +33,13 @@ MOZ_ASSERT(target->isNative()); JSNative native = target->native(); - if (!optimizationInfo().inlineNative()) + if (!optimizationInfo().inlineNative()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineDisabledIon); return InliningStatus_NotInlined; + } + + // Default failure reason is observing an unsupported type. + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadType); // Atomic natives. if (native == atomics_compareExchange) @@ -246,6 +252,10 @@ if (native == js::CallOrConstructBoundFunction) return inlineBoundFunction(callInfo, target); + // Simd functions + if (native == js::simd_int32x4_add) + return inlineSimdInt32x4BinaryArith(callInfo, native, MSimdBinaryArith::Add); + return InliningStatus_NotInlined; } @@ -349,19 +359,21 @@ uint32_t initLength = 0; AllocatingBehaviour allocating = NewArray_Unallocating; - NativeObject *templateObject = inspector->getTemplateObjectForNative(pc, js_Array); - if (!templateObject) + JSObject *templateObject = inspector->getTemplateObjectForNative(pc, js_Array); + if (!templateObject) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoTemplateObj); return InliningStatus_NotInlined; - MOZ_ASSERT(templateObject->is()); + } + ArrayObject *templateArray = &templateObject->as(); // Multiple arguments imply array initialization, not just construction. if (callInfo.argc() >= 2) { initLength = callInfo.argc(); allocating = NewArray_FullyAllocating; - types::TypeObjectKey *type = types::TypeObjectKey::get(templateObject); - if (!type->unknownProperties()) { - types::HeapTypeSetKey elemTypes = type->property(JSID_VOID); + types::ObjectGroupKey *key = types::ObjectGroupKey::get(templateArray); + if (!key->unknownProperties()) { + types::HeapTypeSetKey elemTypes = key->property(JSID_VOID); for (uint32_t i = 0; i < initLength; i++) { MDefinition *value = callInfo.getArg(i); @@ -376,9 +388,9 @@ types::TemporaryTypeSet::DoubleConversion conversion = getInlineReturnTypeSet()->convertDoubleElements(constraints()); if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles) - templateObject->setShouldConvertDoubleElements(); + templateArray->setShouldConvertDoubleElements(); else - templateObject->clearShouldConvertDoubleElements(); + templateArray->clearShouldConvertDoubleElements(); // A single integer argument denotes initial length. if (callInfo.argc() == 1) { @@ -388,16 +400,18 @@ MDefinition *arg = callInfo.getArg(0); if (!arg->isConstantValue()) { callInfo.setImplicitlyUsedUnchecked(); - ArrayObject *templateArray = &templateObject->as(); MNewArrayDynamicLength *ins = MNewArrayDynamicLength::New(alloc(), constraints(), templateArray, - templateArray->type()->initialHeap(constraints()), + templateArray->group()->initialHeap(constraints()), arg); current->add(ins); current->push(ins); return InliningStatus_Inlined; } + // The next several checks all may fail due to range conditions. + trackOptimizationOutcome(TrackedOutcome::ArrayRange); + // Negative lengths generate a RangeError, unhandled by the inline path. initLength = arg->constantValue().toInt32(); if (initLength >= NativeObject::NELEMENTS_LIMIT) @@ -406,7 +420,7 @@ // Make sure initLength matches the template object's length. This is // not guaranteed to be the case, for instance if we're inlining the // MConstant may come from an outer script. - if (initLength != templateObject->as().length()) + if (initLength != templateArray->as().length()) return InliningStatus_NotInlined; // Don't inline large allocations. @@ -418,11 +432,11 @@ callInfo.setImplicitlyUsedUnchecked(); - MConstant *templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); + MConstant *templateConst = MConstant::NewConstraintlessObject(alloc(), templateArray); current->add(templateConst); MNewArray *ins = MNewArray::New(alloc(), constraints(), initLength, templateConst, - templateObject->type()->initialHeap(constraints()), + templateArray->group()->initialHeap(constraints()), allocating); current->add(ins); current->push(ins); @@ -472,8 +486,10 @@ IonBuilder::InliningStatus IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode) { - if (callInfo.constructing()) + if (callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } MIRType returnType = getInlineReturnType(); if (returnType == MIRType_Undefined || returnType == MIRType_Null) @@ -484,7 +500,7 @@ // Pop and shift are only handled for dense arrays that have never been // used in an iterator: popping elements does not account for suppressing // deleted properties in active iterators. - types::TypeObjectFlags unhandledFlags = + types::ObjectGroupFlags unhandledFlags = types::OBJECT_FLAG_SPARSE_INDEXES | types::OBJECT_FLAG_LENGTH_OVERFLOW | types::OBJECT_FLAG_ITERATED; @@ -493,11 +509,15 @@ types::TemporaryTypeSet *thisTypes = obj->resultTypeSet(); if (!thisTypes || thisTypes->getKnownClass(constraints()) != &ArrayObject::class_) return InliningStatus_NotInlined; - if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) + if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) { + trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); return InliningStatus_NotInlined; + } - if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) + if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) { + trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); return InliningStatus_NotInlined; + } callInfo.setImplicitlyUsedUnchecked(); @@ -529,8 +549,10 @@ IonBuilder::InliningStatus IonBuilder::inlineArraySplice(CallInfo &callInfo) { - if (callInfo.argc() != 2 || callInfo.constructing()) + if (callInfo.argc() != 2 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } // Ensure |this|, argument and result are objects. if (getInlineReturnType() != MIRType_Object) @@ -546,8 +568,10 @@ // Specialize arr.splice(start, deleteCount) with unused return value and // avoid creating the result array in this case. - if (!BytecodeIsPopped(pc)) + if (!BytecodeIsPopped(pc)) { + trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric); return InliningStatus_NotInlined; + } MArraySplice *ins = MArraySplice::New(alloc(), callInfo.thisArg(), @@ -565,8 +589,10 @@ IonBuilder::InliningStatus IonBuilder::inlineArrayJoin(CallInfo &callInfo) { - if (callInfo.argc() != 1 || callInfo.constructing()) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } if (getInlineReturnType() != MIRType_String) return InliningStatus_NotInlined; @@ -588,14 +614,17 @@ IonBuilder::InliningStatus IonBuilder::inlineArrayPush(CallInfo &callInfo) { - if (callInfo.argc() != 1 || callInfo.constructing()) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } MDefinition *obj = callInfo.thisArg(); MDefinition *value = callInfo.getArg(0); if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, nullptr, &value, /* canModify = */ false)) { + trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier); return InliningStatus_NotInlined; } MOZ_ASSERT(obj == callInfo.thisArg() && value == callInfo.getArg(0)); @@ -611,16 +640,21 @@ if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | types::OBJECT_FLAG_LENGTH_OVERFLOW)) { + trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); return InliningStatus_NotInlined; } - if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) + if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) { + trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); return InliningStatus_NotInlined; + } types::TemporaryTypeSet::DoubleConversion conversion = thisTypes->convertDoubleElements(constraints()); - if (conversion == types::TemporaryTypeSet::AmbiguousDoubleConversion) + if (conversion == types::TemporaryTypeSet::AmbiguousDoubleConversion) { + trackOptimizationOutcome(TrackedOutcome::ArrayDoubleConversion); return InliningStatus_NotInlined; + } callInfo.setImplicitlyUsedUnchecked(); value = callInfo.getArg(0); @@ -650,8 +684,10 @@ IonBuilder::InliningStatus IonBuilder::inlineArrayConcat(CallInfo &callInfo) { - if (callInfo.argc() != 1 || callInfo.constructing()) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } // Ensure |this|, argument and result are objects. if (getInlineReturnType() != MIRType_Object) @@ -672,6 +708,7 @@ if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | types::OBJECT_FLAG_LENGTH_OVERFLOW)) { + trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); return InliningStatus_NotInlined; } @@ -680,23 +717,26 @@ if (argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | types::OBJECT_FLAG_LENGTH_OVERFLOW)) { + trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); return InliningStatus_NotInlined; } // Watch out for indexed properties on the prototype. - if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) + if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) { + trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); return InliningStatus_NotInlined; + } // Require the 'this' types to have a specific type matching the current // global, so we can create the result object inline. if (thisTypes->getObjectCount() != 1) return InliningStatus_NotInlined; - types::TypeObject *baseThisType = thisTypes->getTypeObject(0); - if (!baseThisType) + types::ObjectGroup *thisGroup = thisTypes->getGroup(0); + if (!thisGroup) return InliningStatus_NotInlined; - types::TypeObjectKey *thisType = types::TypeObjectKey::get(baseThisType); - if (thisType->unknownProperties()) + types::ObjectGroupKey *thisKey = types::ObjectGroupKey::get(thisGroup); + if (thisKey->unknownProperties()) return InliningStatus_NotInlined; // Don't inline if 'this' is packed and the argument may not be packed @@ -704,34 +744,35 @@ if (!thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED) && argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED)) { + trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); return InliningStatus_NotInlined; } // Constraints modeling this concat have not been generated by inference, // so check that type information already reflects possible side effects of // this call. - types::HeapTypeSetKey thisElemTypes = thisType->property(JSID_VOID); + types::HeapTypeSetKey thisElemTypes = thisKey->property(JSID_VOID); types::TemporaryTypeSet *resTypes = getInlineReturnTypeSet(); - if (!resTypes->hasType(types::Type::ObjectType(thisType))) + if (!resTypes->hasType(types::Type::ObjectType(thisKey))) return InliningStatus_NotInlined; for (unsigned i = 0; i < argTypes->getObjectCount(); i++) { - types::TypeObjectKey *argType = argTypes->getObject(i); - if (!argType) + types::ObjectGroupKey *key = argTypes->getObject(i); + if (!key) continue; - if (argType->unknownProperties()) + if (key->unknownProperties()) return InliningStatus_NotInlined; - types::HeapTypeSetKey elemTypes = argType->property(JSID_VOID); + types::HeapTypeSetKey elemTypes = key->property(JSID_VOID); if (!elemTypes.knownSubset(constraints(), thisElemTypes)) return InliningStatus_NotInlined; } // Inline the call. - NativeObject *templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat); - if (!templateObj || templateObj->type() != baseThisType) + JSObject *templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat); + if (!templateObj || templateObj->group() != thisGroup) return InliningStatus_NotInlined; MOZ_ASSERT(templateObj->is()); @@ -739,7 +780,7 @@ MArrayConcat *ins = MArrayConcat::New(alloc(), constraints(), callInfo.thisArg(), callInfo.getArg(0), &templateObj->as(), - templateObj->type()->initialHeap(constraints())); + templateObj->group()->initialHeap(constraints())); current->add(ins); current->push(ins); @@ -751,11 +792,10 @@ IonBuilder::InliningStatus IonBuilder::inlineMathAbs(CallInfo &callInfo) { - if (callInfo.constructing()) - return InliningStatus_NotInlined; - - if (callInfo.argc() != 1) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } MIRType returnType = getInlineReturnType(); MIRType argType = callInfo.getArg(0)->type(); @@ -786,11 +826,10 @@ IonBuilder::InliningStatus IonBuilder::inlineMathFloor(CallInfo &callInfo) { - if (callInfo.constructing()) - return InliningStatus_NotInlined; - - if (callInfo.argc() != 1) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } MIRType argType = callInfo.getArg(0)->type(); MIRType returnType = getInlineReturnType(); @@ -831,11 +870,10 @@ IonBuilder::InliningStatus IonBuilder::inlineMathCeil(CallInfo &callInfo) { - if (callInfo.constructing()) - return InliningStatus_NotInlined; - - if (callInfo.argc() != 1) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } MIRType argType = callInfo.getArg(0)->type(); MIRType returnType = getInlineReturnType(); @@ -876,11 +914,10 @@ IonBuilder::InliningStatus IonBuilder::inlineMathClz32(CallInfo &callInfo) { - if (callInfo.constructing()) - return InliningStatus_NotInlined; - - if (callInfo.argc() != 1) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } MIRType returnType = getInlineReturnType(); if (returnType != MIRType_Int32) @@ -901,11 +938,10 @@ IonBuilder::InliningStatus IonBuilder::inlineMathRound(CallInfo &callInfo) { - if (callInfo.constructing()) - return InliningStatus_NotInlined; - - if (callInfo.argc() != 1) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } MIRType returnType = getInlineReturnType(); MIRType argType = callInfo.getArg(0)->type(); @@ -946,11 +982,10 @@ IonBuilder::InliningStatus IonBuilder::inlineMathSqrt(CallInfo &callInfo) { - if (callInfo.constructing()) - return InliningStatus_NotInlined; - - if (callInfo.argc() != 1) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } MIRType argType = callInfo.getArg(0)->type(); if (getInlineReturnType() != MIRType_Double) @@ -969,11 +1004,10 @@ IonBuilder::InliningStatus IonBuilder::inlineMathAtan2(CallInfo &callInfo) { - if (callInfo.constructing()) - return InliningStatus_NotInlined; - - if (callInfo.argc() != 2) + if (callInfo.argc() != 2 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } if (getInlineReturnType() != MIRType_Double) return InliningStatus_NotInlined; @@ -995,24 +1029,37 @@ IonBuilder::InliningStatus IonBuilder::inlineMathHypot(CallInfo &callInfo) { - if (callInfo.constructing()) + if (callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } - if (callInfo.argc() != 2) + uint32_t argc = callInfo.argc(); + if (argc < 2 || argc > 4) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } if (getInlineReturnType() != MIRType_Double) return InliningStatus_NotInlined; - MIRType argType0 = callInfo.getArg(0)->type(); - MIRType argType1 = callInfo.getArg(1)->type(); - - if (!IsNumberType(argType0) || !IsNumberType(argType1)) + MDefinitionVector vector(alloc()); + if (!vector.reserve(argc)) return InliningStatus_NotInlined; + for (uint32_t i = 0; i < argc; ++i) { + MDefinition * arg = callInfo.getArg(i); + if (!IsNumberType(arg->type())) + return InliningStatus_NotInlined; + vector.infallibleAppend(arg); + } + callInfo.setImplicitlyUsedUnchecked(); + MHypot *hypot = MHypot::New(alloc(), vector); + + if (!hypot) + return InliningStatus_NotInlined; - MHypot *hypot = MHypot::New(alloc(), callInfo.getArg(0), callInfo.getArg(1)); current->add(hypot); current->push(hypot); return InliningStatus_Inlined; @@ -1021,11 +1068,10 @@ IonBuilder::InliningStatus IonBuilder::inlineMathPow(CallInfo &callInfo) { - if (callInfo.constructing()) - return InliningStatus_NotInlined; - - if (callInfo.argc() != 2) + if (callInfo.argc() != 2 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } // Typechecking. MIRType baseType = callInfo.getArg(0)->type(); @@ -1127,8 +1173,10 @@ IonBuilder::InliningStatus IonBuilder::inlineMathRandom(CallInfo &callInfo) { - if (callInfo.constructing()) + if (callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } if (getInlineReturnType() != MIRType_Double) return InliningStatus_NotInlined; @@ -1144,8 +1192,10 @@ IonBuilder::InliningStatus IonBuilder::inlineMathImul(CallInfo &callInfo) { - if (callInfo.argc() != 2 || callInfo.constructing()) + if (callInfo.argc() != 2 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } MIRType returnType = getInlineReturnType(); if (returnType != MIRType_Int32) @@ -1173,8 +1223,10 @@ IonBuilder::InliningStatus IonBuilder::inlineMathFRound(CallInfo &callInfo) { - if (callInfo.argc() != 1 || callInfo.constructing()) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } // MIRType can't be Float32, as this point, as getInlineReturnType uses JSVal types // to infer the returned MIR type. @@ -1204,8 +1256,10 @@ IonBuilder::InliningStatus IonBuilder::inlineMathMinMax(CallInfo &callInfo, bool max) { - if (callInfo.argc() < 1 || callInfo.constructing()) + if (callInfo.argc() < 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } MIRType returnType = getInlineReturnType(); if (!IsNumberType(returnType)) @@ -1273,8 +1327,10 @@ IonBuilder::InliningStatus IonBuilder::inlineStringObject(CallInfo &callInfo) { - if (callInfo.argc() != 1 || !callInfo.constructing()) + if (callInfo.argc() != 1 || !callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } // ConvertToString doesn't support objects. if (callInfo.getArg(0)->mightBeType(MIRType_Object)) @@ -1300,8 +1356,11 @@ IonBuilder::InliningStatus IonBuilder::inlineStringSplit(CallInfo &callInfo) { - if (callInfo.argc() != 1 || callInfo.constructing()) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } + if (callInfo.thisArg()->type() != MIRType_String) return InliningStatus_NotInlined; if (callInfo.getArg(0)->type() != MIRType_String) @@ -1312,11 +1371,11 @@ return InliningStatus_NotInlined; MOZ_ASSERT(templateObject->is()); - types::TypeObjectKey *retType = types::TypeObjectKey::get(templateObject); - if (retType->unknownProperties()) + types::ObjectGroupKey *retKey = types::ObjectGroupKey::get(templateObject); + if (retKey->unknownProperties()) return InliningStatus_NotInlined; - types::HeapTypeSetKey key = retType->property(JSID_VOID); + types::HeapTypeSetKey key = retKey->property(JSID_VOID); if (!key.maybeTypes()) return InliningStatus_NotInlined; @@ -1340,8 +1399,10 @@ IonBuilder::InliningStatus IonBuilder::inlineStrCharCodeAt(CallInfo &callInfo) { - if (callInfo.argc() != 1 || callInfo.constructing()) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } if (getInlineReturnType() != MIRType_Int32) return InliningStatus_NotInlined; @@ -1376,11 +1437,10 @@ IonBuilder::InliningStatus IonBuilder::inlineConstantCharCodeAt(CallInfo &callInfo) { - if (!callInfo.thisArg()->isConstantValue()) - return InliningStatus_NotInlined; - - if (!callInfo.getArg(0)->isConstantValue()) + if (!callInfo.thisArg()->isConstantValue() || !callInfo.getArg(0)->isConstantValue()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric); return InliningStatus_NotInlined; + } const js::Value *strval = callInfo.thisArg()->constantVp(); const js::Value *idxval = callInfo.getArg(0)->constantVp(); @@ -1389,12 +1449,16 @@ return InliningStatus_NotInlined; JSString *str = strval->toString(); - if (!str->isLinear()) + if (!str->isLinear()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric); return InliningStatus_NotInlined; + } int32_t idx = idxval->toInt32(); - if (idx < 0 || (uint32_t(idx) >= str->length())) + if (idx < 0 || (uint32_t(idx) >= str->length())) { + trackOptimizationOutcome(TrackedOutcome::OutOfBounds); return InliningStatus_NotInlined; + } callInfo.setImplicitlyUsedUnchecked(); @@ -1409,8 +1473,10 @@ IonBuilder::InliningStatus IonBuilder::inlineStrFromCharCode(CallInfo &callInfo) { - if (callInfo.argc() != 1 || callInfo.constructing()) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } if (getInlineReturnType() != MIRType_String) return InliningStatus_NotInlined; @@ -1431,8 +1497,10 @@ IonBuilder::InliningStatus IonBuilder::inlineStrCharAt(CallInfo &callInfo) { - if (callInfo.argc() != 1 || callInfo.constructing()) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } if (getInlineReturnType() != MIRType_String) return InliningStatus_NotInlined; @@ -1465,8 +1533,10 @@ IonBuilder::InliningStatus IonBuilder::inlineRegExpExec(CallInfo &callInfo) { - if (callInfo.argc() != 1 || callInfo.constructing()) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } if (callInfo.thisArg()->type() != MIRType_Object) return InliningStatus_NotInlined; @@ -1501,8 +1571,10 @@ IonBuilder::InliningStatus IonBuilder::inlineRegExpTest(CallInfo &callInfo) { - if (callInfo.argc() != 1 || callInfo.constructing()) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } // TI can infer a nullptr return type of regexp_test with eager compilation. if (CallResultEscapes(pc) && getInlineReturnType() != MIRType_Boolean) @@ -1535,8 +1607,10 @@ IonBuilder::InliningStatus IonBuilder::inlineStrReplace(CallInfo &callInfo) { - if (callInfo.argc() != 2 || callInfo.constructing()) + if (callInfo.argc() != 2 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } // Return: String. if (getInlineReturnType() != MIRType_String) @@ -1611,12 +1685,12 @@ if (callInfo.argc() != 1 || callInfo.constructing()) return InliningStatus_NotInlined; - NativeObject *templateObject = inspector->getTemplateObjectForNative(pc, obj_create); + JSObject *templateObject = inspector->getTemplateObjectForNative(pc, obj_create); if (!templateObject) return InliningStatus_NotInlined; MOZ_ASSERT(templateObject->is()); - MOZ_ASSERT(!templateObject->hasSingletonType()); + MOZ_ASSERT(!templateObject->isSingleton()); // Ensure the argument matches the template object's prototype. MDefinition *arg = callInfo.getArg(0); @@ -1625,7 +1699,7 @@ return InliningStatus_NotInlined; types::TemporaryTypeSet *types = arg->resultTypeSet(); - if (!types || types->getSingleton() != proto) + if (!types || types->maybeSingleton() != proto) return InliningStatus_NotInlined; MOZ_ASSERT(types->getKnownMIRType() == MIRType_Object); @@ -1639,7 +1713,7 @@ MConstant *templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); current->add(templateConst); MNewObject *ins = MNewObject::New(alloc(), constraints(), templateConst, - templateObject->type()->initialHeap(constraints()), + templateObject->group()->initialHeap(constraints()), MNewObject::ObjectCreate); current->add(ins); current->push(ins); @@ -1653,8 +1727,10 @@ IonBuilder::inlineUnsafePutElements(CallInfo &callInfo) { uint32_t argc = callInfo.argc(); - if (argc < 3 || (argc % 3) != 0 || callInfo.constructing()) + if (argc < 3 || (argc % 3) != 0 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } /* Important: * @@ -1821,8 +1897,10 @@ const Class *clasp1, const Class *clasp2, const Class *clasp3, const Class *clasp4) { - if (callInfo.constructing() || callInfo.argc() != 1) + if (callInfo.constructing() || callInfo.argc() != 1) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } if (callInfo.getArg(0)->type() != MIRType_Object) return InliningStatus_NotInlined; @@ -1929,8 +2007,10 @@ IonBuilder::InliningStatus IonBuilder::inlineObjectIsTypeDescr(CallInfo &callInfo) { - if (callInfo.constructing() || callInfo.argc() != 1) + if (callInfo.constructing() || callInfo.argc() != 1) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } if (callInfo.getArg(0)->type() != MIRType_Object) return InliningStatus_NotInlined; @@ -1966,8 +2046,10 @@ IonBuilder::InliningStatus IonBuilder::inlineSetTypedObjectOffset(CallInfo &callInfo) { - if (callInfo.argc() != 2 || callInfo.constructing()) + if (callInfo.argc() != 2 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } MDefinition *typedObj = callInfo.getArg(0); MDefinition *offset = callInfo.getArg(1); @@ -2008,8 +2090,10 @@ IonBuilder::InliningStatus IonBuilder::inlineUnsafeSetReservedSlot(CallInfo &callInfo) { - if (callInfo.argc() != 3 || callInfo.constructing()) + if (callInfo.argc() != 3 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } if (getInlineReturnType() != MIRType_Undefined) return InliningStatus_NotInlined; if (callInfo.getArg(0)->type() != MIRType_Object) @@ -2038,8 +2122,10 @@ IonBuilder::InliningStatus IonBuilder::inlineUnsafeGetReservedSlot(CallInfo &callInfo, MIRType knownValueType) { - if (callInfo.argc() != 2 || callInfo.constructing()) + if (callInfo.argc() != 2 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } if (callInfo.getArg(0)->type() != MIRType_Object) return InliningStatus_NotInlined; if (callInfo.getArg(1)->type() != MIRType_Int32) @@ -2078,8 +2164,10 @@ IonBuilder::InliningStatus IonBuilder::inlineIsCallable(CallInfo &callInfo) { - if (callInfo.argc() != 1 || callInfo.constructing()) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } if (getInlineReturnType() != MIRType_Boolean) return InliningStatus_NotInlined; @@ -2121,8 +2209,10 @@ IonBuilder::InliningStatus IonBuilder::inlineIsObject(CallInfo &callInfo) { - if (callInfo.argc() != 1 || callInfo.constructing()) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } if (getInlineReturnType() != MIRType_Boolean) return InliningStatus_NotInlined; @@ -2140,8 +2230,10 @@ IonBuilder::InliningStatus IonBuilder::inlineToObject(CallInfo &callInfo) { - if (callInfo.argc() != 1 || callInfo.constructing()) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } // If we know the input type is an object, nop ToObject. if (getInlineReturnType() != MIRType_Object) @@ -2159,8 +2251,10 @@ IonBuilder::InliningStatus IonBuilder::inlineToInteger(CallInfo &callInfo) { - if (callInfo.argc() != 1 || callInfo.constructing()) + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } MDefinition *input = callInfo.getArg(0); @@ -2240,8 +2334,10 @@ IonBuilder::InliningStatus IonBuilder::inlineBoundFunction(CallInfo &nativeCallInfo, JSFunction *target) { - if (!target->getBoundFunctionTarget()->is()) - return InliningStatus_NotInlined; + trackOptimizationOutcome(TrackedOutcome::CantInlineBound); + + if (!target->getBoundFunctionTarget()->is()) + return InliningStatus_NotInlined; JSFunction *scriptedTarget = &(target->getBoundFunctionTarget()->as()); @@ -2294,8 +2390,10 @@ IonBuilder::InliningStatus IonBuilder::inlineAtomicsCompareExchange(CallInfo &callInfo) { - if (callInfo.argc() != 4 || callInfo.constructing()) + if (callInfo.argc() != 4 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } Scalar::Type arrayType; if (!atomicsMeetsPreconditions(callInfo, &arrayType)) @@ -2340,8 +2438,10 @@ IonBuilder::InliningStatus IonBuilder::inlineAtomicsLoad(CallInfo &callInfo) { - if (callInfo.argc() != 2 || callInfo.constructing()) + if (callInfo.argc() != 2 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } Scalar::Type arrayType; if (!atomicsMeetsPreconditions(callInfo, &arrayType)) @@ -2366,8 +2466,10 @@ IonBuilder::InliningStatus IonBuilder::inlineAtomicsStore(CallInfo &callInfo) { - if (callInfo.argc() != 3 || callInfo.constructing()) + if (callInfo.argc() != 3 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } Scalar::Type arrayType; if (!atomicsMeetsPreconditions(callInfo, &arrayType)) @@ -2400,8 +2502,10 @@ IonBuilder::InliningStatus IonBuilder::inlineAtomicsFence(CallInfo &callInfo) { - if (callInfo.argc() != 0 || callInfo.constructing()) + if (callInfo.argc() != 0 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } callInfo.setImplicitlyUsedUnchecked(); @@ -2415,8 +2519,10 @@ IonBuilder::InliningStatus IonBuilder::inlineAtomicsBinop(CallInfo &callInfo, JSFunction *target) { - if (callInfo.argc() != 3 || callInfo.constructing()) + if (callInfo.argc() != 3 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } Scalar::Type arrayType; if (!atomicsMeetsPreconditions(callInfo, &arrayType)) @@ -2539,8 +2645,10 @@ IonBuilder::inlineConstructTypedObject(CallInfo &callInfo, TypeDescr *descr) { // Only inline default constructors for now. - if (callInfo.argc() != 0) + if (callInfo.argc() != 0) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } if (size_t(descr->size()) > InlineTypedObject::MaximumSize) return InliningStatus_NotInlined; @@ -2556,7 +2664,7 @@ callInfo.setImplicitlyUsedUnchecked(); MNewTypedObject *ins = MNewTypedObject::New(alloc(), constraints(), templateObject, - templateObject->type()->initialHeap(constraints())); + templateObject->group()->initialHeap(constraints())); current->add(ins); current->push(ins); @@ -2570,7 +2678,7 @@ return InliningStatus_NotInlined; // Generic constructor of SIMD valuesX4. - MIRType simdType; + MIRType simdType = MIRType(-1); // initialize to silence GCC warning switch (descr->type()) { case SimdTypeDescr::TYPE_INT32: simdType = MIRType_Int32x4; @@ -2605,7 +2713,39 @@ current->add(values); MSimdBox *obj = MSimdBox::New(alloc(), constraints(), values, inlineTypedObject, - inlineTypedObject->type()->initialHeap(constraints())); + inlineTypedObject->group()->initialHeap(constraints())); + current->add(obj); + current->push(obj); + + callInfo.setImplicitlyUsedUnchecked(); + return InliningStatus_Inlined; +} + +IonBuilder::InliningStatus +IonBuilder::inlineSimdInt32x4BinaryArith(CallInfo &callInfo, JSNative native, + MSimdBinaryArith::Operation op) +{ + if (callInfo.argc() != 2) + return InliningStatus_NotInlined; + + JSObject *templateObject = inspector->getTemplateObjectForNative(pc, native); + if (!templateObject) + return InliningStatus_NotInlined; + + InlineTypedObject *inlineTypedObject = &templateObject->as(); + MOZ_ASSERT(inlineTypedObject->typeDescr().as().type() == js::Int32x4::type); + + // If the type of any of the arguments is neither a SIMD type, an Object + // type, or a Value, then the applyTypes phase will add a fallible box & + // unbox sequence. This does not matter much as the binary arithmetic + // instruction is supposed to produce a TypeError once it is called. + MSimdBinaryArith *ins = MSimdBinaryArith::New(alloc(), callInfo.getArg(0), callInfo.getArg(1), + op, MIRType_Int32x4); + + MSimdBox *obj = MSimdBox::New(alloc(), constraints(), ins, inlineTypedObject, + inlineTypedObject->group()->initialHeap(constraints())); + + current->add(ins); current->add(obj); current->push(obj); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/Assembler-mips.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/Assembler-mips.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/Assembler-mips.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/Assembler-mips.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -288,6 +288,12 @@ void *ptr = (void *)Assembler::ExtractLuiOriValue(inst, inst->next()); void *prior = ptr; + // The low bit shouldn't be set. If it is, we probably got a dummy + // pointer inserted by CodeGenerator::visitNurseryObject, but we + // shouldn't be able to trigger GC before those are patched to their + // real values. + MOZ_ASSERT(!(uintptr_t(ptr) & 0x1)); + // No barrier needed since these are constants. gc::MarkGCThingUnbarriered(trc, reinterpret_cast(&ptr), "ion-masm-ptr"); if (ptr != prior) { @@ -323,6 +329,43 @@ } void +Assembler::FixupNurseryObjects(JSContext *cx, JitCode *code, CompactBufferReader &reader, + const ObjectVector &nurseryObjects) +{ + MOZ_ASSERT(!nurseryObjects.empty()); + + uint8_t *buffer = code->raw(); + bool hasNurseryPointers = false; + + while (reader.more()) { + size_t offset = reader.readUnsigned(); + Instruction *inst = (Instruction*)(buffer + offset); + + void *ptr = (void *)Assembler::ExtractLuiOriValue(inst, inst->next()); + uintptr_t word = uintptr_t(ptr); + + if (!(word & 0x1)) + continue; + + uint32_t index = word >> 1; + JSObject *obj = nurseryObjects[index]; + + Assembler::UpdateLuiOriValue(inst, inst->next(), uint32_t(obj)); + AutoFlushICache::flush(uintptr_t(inst), 8); + + // Either all objects are still in the nursery, or all objects are + // tenured. + MOZ_ASSERT_IF(hasNurseryPointers, IsInsideNursery(obj)); + + if (!hasNurseryPointers && IsInsideNursery(obj)) + hasNurseryPointers = true; + } + + if (hasNurseryPointers) + cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(code); +} + +void Assembler::copyJumpRelocationTable(uint8_t *dest) { if (jumpRelocations_.length()) @@ -969,7 +1012,7 @@ BufferOffset Assembler::as_ins(Register rt, Register rs, uint16_t pos, uint16_t size) { - MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size >= 32); + MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size <= 32); Register rd; rd = Register::FromCode(pos + size - 1); return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ins).encode()); @@ -978,7 +1021,7 @@ BufferOffset Assembler::as_ext(Register rt, Register rs, uint16_t pos, uint16_t size) { - MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size >= 32); + MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size <= 32); Register rd; rd = Register::FromCode(size - 1); return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ext).encode()); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/Assembler-mips.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/Assembler-mips.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/Assembler-mips.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/Assembler-mips.h 2015-02-03 14:33:32.000000000 +0000 @@ -745,7 +745,6 @@ CompactBufferWriter jumpRelocations_; CompactBufferWriter dataRelocations_; - CompactBufferWriter relocations_; CompactBufferWriter preBarriers_; MIPSBuffer m_buffer; @@ -1012,6 +1011,9 @@ static void TraceJumpRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader); static void TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader); + static void FixupNurseryObjects(JSContext *cx, JitCode *code, CompactBufferReader &reader, + const ObjectVector &nurseryObjects); + static bool SupportsFloatingPoint() { #if (defined(__mips_hard_float) && !defined(__mips_single_float)) || defined(JS_MIPS_SIMULATOR) return true; @@ -1086,6 +1088,12 @@ bool bailed() { return m_buffer.bail(); } + + void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, + const Disassembler::HeapAccess &heapAccess) + { + // Implement this if we implement a disassembler. + } }; // Assembler // sll zero, zero, 0 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/CodeGenerator-mips.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/CodeGenerator-mips.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/CodeGenerator-mips.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/CodeGenerator-mips.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -1744,16 +1744,16 @@ } void -CodeGeneratorMIPS::visitGuardObjectType(LGuardObjectType *guard) +CodeGeneratorMIPS::visitGuardObjectGroup(LGuardObjectGroup *guard) { Register obj = ToRegister(guard->input()); Register tmp = ToRegister(guard->tempInt()); - masm.loadPtr(Address(obj, JSObject::offsetOfType()), tmp); + masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), tmp); Assembler::Condition cond = guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual; - bailoutCmpPtr(cond, tmp, ImmGCPtr(guard->mir()->typeObject()), guard->snapshot()); + bailoutCmpPtr(cond, tmp, ImmGCPtr(guard->mir()->group()), guard->snapshot()); } void diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/CodeGenerator-mips.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/CodeGenerator-mips.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/CodeGenerator-mips.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/CodeGenerator-mips.h 2015-02-03 14:33:32.000000000 +0000 @@ -244,7 +244,7 @@ void visitFloat32(LFloat32 *ins); void visitGuardShape(LGuardShape *guard); - void visitGuardObjectType(LGuardObjectType *guard); + void visitGuardObjectGroup(LGuardObjectGroup *guard); void visitGuardClass(LGuardClass *guard); void visitNegI(LNegI *lir); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/LIR-mips.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/LIR-mips.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/LIR-mips.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/LIR-mips.h 2015-02-03 14:33:32.000000000 +0000 @@ -328,17 +328,17 @@ } }; -class LGuardObjectType : public LInstructionHelper<0, 1, 1> +class LGuardObjectGroup : public LInstructionHelper<0, 1, 1> { public: - LIR_HEADER(GuardObjectType); + LIR_HEADER(GuardObjectGroup); - LGuardObjectType(const LAllocation &in, const LDefinition &temp) { + LGuardObjectGroup(const LAllocation &in, const LDefinition &temp) { setOperand(0, in); setTemp(0, temp); } - const MGuardObjectType *mir() const { - return mir_->toGuardObjectType(); + const MGuardObjectGroup *mir() const { + return mir_->toGuardObjectGroup(); } const LDefinition *tempInt() { return getTemp(0); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/Lowering-mips.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/Lowering-mips.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/Lowering-mips.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/Lowering-mips.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -378,13 +378,13 @@ } void -LIRGeneratorMIPS::visitGuardObjectType(MGuardObjectType *ins) +LIRGeneratorMIPS::visitGuardObjectGroup(MGuardObjectGroup *ins) { MOZ_ASSERT(ins->obj()->type() == MIRType_Object); LDefinition tempObj = temp(LDefinition::OBJECT); - LGuardObjectType *guard = new(alloc()) LGuardObjectType(useRegister(ins->obj()), tempObj); - assignSnapshot(guard, Bailout_ObjectIdentityOrTypeGuard); + LGuardObjectGroup *guard = new(alloc()) LGuardObjectGroup(useRegister(ins->obj()), tempObj); + assignSnapshot(guard, ins->bailoutKind()); add(guard, ins); redefine(ins, ins->obj()); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/Lowering-mips.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/Lowering-mips.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/Lowering-mips.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/Lowering-mips.h 2015-02-03 14:33:32.000000000 +0000 @@ -91,7 +91,7 @@ void visitReturn(MReturn *ret); void lowerPhi(MPhi *phi); void visitGuardShape(MGuardShape *ins); - void visitGuardObjectType(MGuardObjectType *ins); + void visitGuardObjectGroup(MGuardObjectGroup *ins); void visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins); void visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32 *ins); void visitAsmJSLoadHeap(MAsmJSLoadHeap *ins); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/MacroAssembler-mips.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/MacroAssembler-mips.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/MacroAssembler-mips.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/MacroAssembler-mips.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -3472,6 +3472,8 @@ case Args_Double_DoubleDouble: case Args_Double_IntDouble: case Args_Int_IntDouble: + case Args_Double_DoubleDoubleDouble: + case Args_Double_DoubleDoubleDoubleDouble: break; default: MOZ_CRASH("Unexpected type"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/MacroAssembler-mips.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/MacroAssembler-mips.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/MacroAssembler-mips.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/MacroAssembler-mips.h 2015-02-03 14:33:32.000000000 +0000 @@ -67,12 +67,13 @@ class MacroAssemblerMIPS : public Assembler { - protected: + public: // higher level tag testing code Operand ToPayload(Operand base); Address ToPayload(Address base) { return ToPayload(Operand(base)).toAddress(); } + protected: Operand ToType(Operand base); Address ToType(Address base) { return ToType(Operand(base)).toAddress(); @@ -231,7 +232,7 @@ void ma_b(Register lhs, ImmGCPtr imm, Label *l, Condition c, JumpKind jumpKind = LongJump) { MOZ_ASSERT(lhs != ScratchRegister); ma_li(ScratchRegister, imm); - ma_b(lhs, Imm32(uint32_t(imm.value)), l, c, jumpKind); + ma_b(lhs, ScratchRegister, l, c, jumpKind); } void ma_b(Register lhs, Address addr, Label *l, Condition c, JumpKind jumpKind = LongJump); void ma_b(Address addr, Imm32 imm, Label *l, Condition c, JumpKind jumpKind = LongJump); @@ -868,6 +869,19 @@ void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T &dest, MIRType slotType); + template + void storeUnboxedPayload(ValueOperand value, T address, size_t nbytes) { + switch (nbytes) { + case 4: + storePtr(value.payloadReg(), address); + return; + case 1: + store8(value.payloadReg(), address); + return; + default: MOZ_CRASH("Bad payload width"); + } + } + void moveValue(const Value &val, const ValueOperand &dest); void moveValue(const ValueOperand &src, const ValueOperand &dest) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/MoveEmitter-mips.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/MoveEmitter-mips.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/MoveEmitter-mips.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/MoveEmitter-mips.h 2015-02-03 14:33:32.000000000 +0000 @@ -56,6 +56,8 @@ ~MoveEmitterMIPS(); void emit(const MoveResolver &moves); void finish(); + + void setScratchRegister(Register reg) {} }; typedef MoveEmitterMIPS MoveEmitter; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/Simulator-mips.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/Simulator-mips.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/Simulator-mips.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/Simulator-mips.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -486,96 +486,20 @@ char validity_map_[kValidityMapSize]; // One byte per line. }; -class Redirection; - -class SimulatorRuntime -{ - friend class AutoLockSimulatorRuntime; - - Redirection *redirection_; - - // ICache checking. - struct ICacheHasher { - typedef void *Key; - typedef void *Lookup; - static HashNumber hash(const Lookup &l); - static bool match(const Key &k, const Lookup &l); - }; - - public: - typedef HashMap ICacheMap; - - protected: - ICacheMap icache_; - - // Synchronize access between main thread and compilation threads. - PRLock *lock_; - mozilla::DebugOnly lockOwner_; - - public: - SimulatorRuntime() - : redirection_(nullptr), - lock_(nullptr), - lockOwner_(nullptr) {} - ~SimulatorRuntime(); - bool init() { - lock_ = PR_NewLock(); - if (!lock_) - return false; - if (!icache_.init()) - return false; - return true; - } - ICacheMap &icache() { - MOZ_ASSERT(lockOwner_ == PR_GetCurrentThread()); - return icache_; - } - Redirection *redirection() const { - MOZ_ASSERT(lockOwner_ == PR_GetCurrentThread()); - return redirection_; - } - void setRedirection(js::jit::Redirection *redirection) { - MOZ_ASSERT(lockOwner_ == PR_GetCurrentThread()); - redirection_ = redirection; - } -}; - -class AutoLockSimulatorRuntime -{ - protected: - SimulatorRuntime *srt_; - - public: - AutoLockSimulatorRuntime(SimulatorRuntime *srt) - : srt_(srt) { - PR_Lock(srt_->lock_); - MOZ_ASSERT(!srt_->lockOwner_); -#ifdef DEBUG - srt_->lockOwner_ = PR_GetCurrentThread(); -#endif - } - - ~AutoLockSimulatorRuntime() { - MOZ_ASSERT(srt_->lockOwner_ == PR_GetCurrentThread()); - srt_->lockOwner_ = nullptr; - PR_Unlock(srt_->lock_); - } -}; - bool Simulator::ICacheCheckingEnabled = false; int Simulator::StopSimAt = -1; -SimulatorRuntime * -CreateSimulatorRuntime() +Simulator * +Simulator::Create() { - SimulatorRuntime *srt = js_new(); - if (!srt) + Simulator *sim = js_new(); + if (!sim) return nullptr; - if (!srt->init()) { - js_delete(srt); - return nullptr; + if (!sim->icache_.init()) { + js_delete(sim); + return false; } if (getenv("MIPS_SIM_ICACHE_CHECKS")) @@ -588,13 +512,13 @@ Simulator::StopSimAt = stopAt; } - return srt; + return sim; } void -DestroySimulatorRuntime(SimulatorRuntime *srt) +Simulator::Destroy(Simulator *sim) { - js_delete(srt); + js_delete(sim); } // The MipsDebugger class is used by the simulator while debugging simulated @@ -1193,9 +1117,9 @@ } static CachePage * -GetCachePage(SimulatorRuntime::ICacheMap &i_cache, void *page) +GetCachePage(Simulator::ICacheMap &i_cache, void *page) { - SimulatorRuntime::ICacheMap::AddPtr p = i_cache.lookupForAdd(page); + Simulator::ICacheMap::AddPtr p = i_cache.lookupForAdd(page); if (p) return p->value(); @@ -1207,7 +1131,7 @@ // Flush from start up to and not including start + size. static void -FlushOnePage(SimulatorRuntime::ICacheMap &i_cache, intptr_t start, int size) +FlushOnePage(Simulator::ICacheMap &i_cache, intptr_t start, int size) { MOZ_ASSERT(size <= CachePage::kPageSize); MOZ_ASSERT(AllOnOnePage(start, size - 1)); @@ -1221,7 +1145,7 @@ } static void -FlushICache(SimulatorRuntime::ICacheMap &i_cache, void *start_addr, size_t size) +FlushICache(Simulator::ICacheMap &i_cache, void *start_addr, size_t size) { intptr_t start = reinterpret_cast(start_addr); int intra_line = (start & CachePage::kLineMask); @@ -1243,7 +1167,7 @@ } static void -CheckICache(SimulatorRuntime::ICacheMap &i_cache, SimInstruction *instr) +CheckICache(Simulator::ICacheMap &i_cache, SimInstruction *instr) { intptr_t address = reinterpret_cast(instr); void *page = reinterpret_cast(address & (~CachePage::kPageMask)); @@ -1266,13 +1190,13 @@ } HashNumber -SimulatorRuntime::ICacheHasher::hash(const Lookup &l) +Simulator::ICacheHasher::hash(const Lookup &l) { return static_cast(reinterpret_cast(l)) >> 2; } bool -SimulatorRuntime::ICacheHasher::match(const Key &k, const Lookup &l) +Simulator::ICacheHasher::match(const Key &k, const Lookup &l) { MOZ_ASSERT((reinterpret_cast(k) & CachePage::kPageMask) == 0); MOZ_ASSERT((reinterpret_cast(l) & CachePage::kPageMask) == 0); @@ -1282,30 +1206,24 @@ void Simulator::FlushICache(void *start_addr, size_t size) { - SimulatorRuntime *srt = TlsPerThreadData.get()->simulatorRuntime(); - AutoLockSimulatorRuntime alsr(srt); - js::jit::FlushICache(srt->icache(), start_addr, size); + js::jit::FlushICache(Simulator::Current()->icache(), start_addr, size); } -Simulator::~Simulator() -{ - js_free(stack_); -} - -Simulator::Simulator(SimulatorRuntime *srt) - : srt_(srt) +Simulator::Simulator() { // Set up simulator support first. Some of this information is needed to // setup the architecture state. - // Allocate 2MB for the stack. Note that we will only use 1MB, see also - // Simulator::stackLimit(). + // Allocate 2MB for the stack. Note that we will only use 1MB, see below. static const size_t stackSize = 2 * 1024 * 1024; stack_ = static_cast(js_malloc(stackSize)); if (!stack_) { MOZ_ReportAssertionFailure("[unhandlable oom] Simulator stack", __FILE__, __LINE__); MOZ_CRASH(); } + // Leave a safety margin of 1MB to prevent overrunning the stack when + // pushing values (total stack size is 2MB). + stackLimit_ = reinterpret_cast(stack_) + 1024 * 1024; pc_modified_ = false; icount_ = 0; break_count_ = 0; @@ -1336,6 +1254,8 @@ exceptions[i] = 0; lastDebuggerInput_ = nullptr; + + redirection_ = nullptr; } // When the generated code calls an external reference we need to catch that in @@ -1347,18 +1267,18 @@ // offset from the swi instruction so the simulator knows what to call. class Redirection { - friend class SimulatorRuntime; + friend class Simulator; - Redirection(void* nativeFunction, ABIFunctionType type, SimulatorRuntime *srt) + Redirection(void* nativeFunction, ABIFunctionType type, Simulator *sim) : nativeFunction_(nativeFunction), swiInstruction_(kCallRedirInstr), type_(type), next_(nullptr) { - next_ = srt->redirection(); + next_ = sim->redirection(); if (Simulator::ICacheCheckingEnabled) - FlushICache(srt->icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize); - srt->setRedirection(this); + FlushICache(sim->icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize); + sim->setRedirection(this); } public: @@ -1367,13 +1287,8 @@ ABIFunctionType type() const { return type_; } static Redirection *Get(void *nativeFunction, ABIFunctionType type) { - PerThreadData *pt = TlsPerThreadData.get(); - SimulatorRuntime *srt = pt->simulatorRuntime(); - AutoLockSimulatorRuntime alsr(srt); - - MOZ_ASSERT_IF(pt->simulator(), pt->simulator()->srt_ == srt); - - Redirection *current = srt->redirection(); + Simulator *sim = Simulator::Current(); + Redirection *current = sim->redirection(); for (; current != nullptr; current = current->next_) { if (current->nativeFunction_ == nativeFunction) { MOZ_ASSERT(current->type() == type); @@ -1387,7 +1302,7 @@ __FILE__, __LINE__); MOZ_CRASH(); } - new(redir) Redirection(nativeFunction, type, srt); + new(redir) Redirection(nativeFunction, type, sim); return redir; } @@ -1404,37 +1319,29 @@ Redirection *next_; }; -/* static */ void * -Simulator::RedirectNativeFunction(void *nativeFunction, ABIFunctionType type) -{ - Redirection *redirection = Redirection::Get(nativeFunction, type); - return redirection->addressOfSwiInstruction(); -} - -SimulatorRuntime::~SimulatorRuntime() +Simulator::~Simulator() { + js_free(stack_); Redirection *r = redirection_; while (r) { Redirection *next = r->next_; js_delete(r); r = next; } - if (lock_) - PR_DestroyLock(lock_); +} + +/* static */ void * +Simulator::RedirectNativeFunction(void *nativeFunction, ABIFunctionType type) +{ + Redirection *redirection = Redirection::Get(nativeFunction, type); + return redirection->addressOfSwiInstruction(); } // Get the active Simulator for the current thread. Simulator * Simulator::Current() { - PerThreadData *pt = TlsPerThreadData.get(); - Simulator *sim = pt->simulator(); - if (!sim) { - sim = js_new(pt->simulatorRuntime()); - pt->setSimulator(sim); - } - - return sim; + return TlsPerThreadData.get()->simulator(); } // Sets the register in the architecture state. It will also deal with updating @@ -1551,6 +1458,14 @@ } void +Simulator::getFpFromStack(int32_t *stack, double *x) +{ + MOZ_ASSERT(stack); + MOZ_ASSERT(x); + memcpy(x, stack, sizeof(double)); +} + +void Simulator::setCallResultDouble(double result) { setFpuRegisterDouble(f0, result); @@ -1800,9 +1715,13 @@ uintptr_t Simulator::stackLimit() const { - // Leave a safety margin of 1MB to prevent overrunning the stack when - // pushing values (total stack size is 2MB). - return reinterpret_cast(stack_) + 1024 * 1024; + return stackLimit_; +} + +uintptr_t * +Simulator::addressOfStackLimit() +{ + return &stackLimit_; } bool @@ -1857,6 +1776,10 @@ typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1); typedef int32_t (*Prototype_Int_IntDouble)(int32_t arg0, double arg1); +typedef double (*Prototype_Double_DoubleDoubleDouble)(double arg0, double arg1, double arg2); +typedef double (*Prototype_Double_DoubleDoubleDoubleDouble)(double arg0, double arg1, + double arg2, double arg3); + // Software interrupt instructions are used by the simulator to call into C++. void Simulator::softwareInterrupt(SimInstruction *instr) @@ -2022,6 +1945,29 @@ setRegister(v0, result); break; } + case Args_Double_DoubleDoubleDouble: { + double dval0, dval1, dval2; + int32_t ival; + getFpArgs(&dval0, &dval1, &ival); + // the last argument is on stack + getFpFromStack(stack_pointer + 4, &dval2); + Prototype_Double_DoubleDoubleDouble target = reinterpret_cast(external); + double dresult = target(dval0, dval1, dval2); + setCallResultDouble(dresult); + break; + } + case Args_Double_DoubleDoubleDoubleDouble: { + double dval0, dval1, dval2, dval3; + int32_t ival; + getFpArgs(&dval0, &dval1, &ival); + // the two last arguments are on stack + getFpFromStack(stack_pointer + 4, &dval2); + getFpFromStack(stack_pointer + 6, &dval3); + Prototype_Double_DoubleDoubleDoubleDouble target = reinterpret_cast(external); + double dresult = target(dval0, dval1, dval2, dval3); + setCallResultDouble(dresult); + break; + } default: MOZ_CRASH("call"); } @@ -3267,10 +3213,8 @@ void Simulator::instructionDecode(SimInstruction *instr) { - if (Simulator::ICacheCheckingEnabled) { - AutoLockSimulatorRuntime alsr(srt_); - CheckICache(srt_->icache(), instr); - } + if (Simulator::ICacheCheckingEnabled) + CheckICache(icache(), instr); pc_modified_ = false; switch (instr->instructionType()) { @@ -3312,7 +3256,7 @@ // Get the PC to simulate. Cannot use the accessor here as we need the // raw PC value and not the one used as input to arithmetic instructions. int program_counter = get_pc(); - AsmJSActivation *activation = TlsPerThreadData.get()->asmJSActivationStack(); + AsmJSActivation *activation = TlsPerThreadData.get()->runtimeFromMainThread()->asmJSActivationStack(); while (program_counter != end_sim_pc) { if (enableStopSimAt && (icount_ == Simulator::StopSimAt)) { @@ -3470,39 +3414,20 @@ } // namespace js js::jit::Simulator * -js::PerThreadData::simulator() const +JSRuntime::simulator() const { return simulator_; } -void -js::PerThreadData::setSimulator(js::jit::Simulator *sim) -{ - simulator_ = sim; - simulatorStackLimit_ = sim->stackLimit(); -} - -js::jit::SimulatorRuntime * -js::PerThreadData::simulatorRuntime() const +js::jit::Simulator * +js::PerThreadData::simulator() const { - return runtime_->simulatorRuntime(); + return runtime_->simulator(); } uintptr_t * -js::PerThreadData::addressOfSimulatorStackLimit() +JSRuntime::addressOfSimulatorStackLimit() { - return &simulatorStackLimit_; + return simulator_->addressOfStackLimit(); } -js::jit::SimulatorRuntime * -JSRuntime::simulatorRuntime() const -{ - return simulatorRuntime_; -} - -void -JSRuntime::setSimulatorRuntime(js::jit::SimulatorRuntime *srt) -{ - MOZ_ASSERT(!simulatorRuntime_); - simulatorRuntime_ = srt; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/Simulator-mips.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/Simulator-mips.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/mips/Simulator-mips.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/mips/Simulator-mips.h 2015-02-03 14:33:32.000000000 +0000 @@ -36,9 +36,9 @@ namespace js { namespace jit { -class SimulatorRuntime; -SimulatorRuntime *CreateSimulatorRuntime(); -void DestroySimulatorRuntime(SimulatorRuntime *srt); +class Simulator; +class Redirection; +class CachePage; const intptr_t kPointerAlignment = 4; const intptr_t kPointerAlignmentMask = kPointerAlignment - 1; @@ -137,7 +137,11 @@ kNumFPURegisters }; - explicit Simulator(SimulatorRuntime *srt); + static Simulator *Create(); + static void Destroy(Simulator *simulator); + + // Constructor/destructor are for internal use only; use the static methods above. + Simulator(); ~Simulator(); // The currently executing Simulator instance. Potentially there can be one @@ -148,6 +152,8 @@ return Simulator::Current()->stackLimit(); } + uintptr_t *addressOfStackLimit(); + // Accessors for register state. Reading the pc value adheres to the MIPS // architecture specification and is off by a 8 from the currently executing // instruction. @@ -297,6 +303,7 @@ // Handle arguments and return value for runtime FP functions. void getFpArgs(double *x, double *y, int32_t *z); + void getFpFromStack(int32_t *stack, double *x); void setCallResultDouble(double result); void setCallResultFloat(float result); @@ -314,6 +321,7 @@ // Simulator support. char *stack_; + uintptr_t stackLimit_; bool pc_modified_; int icount_; int break_count_; @@ -327,8 +335,6 @@ SimInstruction *break_pc_; Instr break_instr_; - SimulatorRuntime *srt_; - // A stop is watched if its code is less than kNumOfWatchedStops. // Only watched stops support enabling/disabling and the counter feature. static const uint32_t kNumOfWatchedStops = 256; @@ -346,6 +352,36 @@ char *desc_; }; StopCountAndDesc watchedStops_[kNumOfWatchedStops]; + + private: + Redirection *redirection_; + + // ICache checking. + struct ICacheHasher { + typedef void *Key; + typedef void *Lookup; + static HashNumber hash(const Lookup &l); + static bool match(const Key &k, const Lookup &l); + }; + + public: + typedef HashMap ICacheMap; + + protected: + ICacheMap icache_; + + public: + ICacheMap &icache() { + return icache_; + } + + Redirection *redirection() const { + return redirection_; + } + + void setRedirection(js::jit::Redirection *redirection) { + redirection_ = redirection; + } }; #define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror) \ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MIR.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MIR.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MIR.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MIR.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -650,13 +650,13 @@ types::TemporaryTypeSet * jit::MakeSingletonTypeSet(types::CompilerConstraintList *constraints, JSObject *obj) { - // Invalidate when this object's TypeObject gets unknown properties. This + // Invalidate when this object's ObjectGroup gets unknown properties. This // happens for instance when we mutate an object's __proto__, in this case // we want to invalidate and mark this TypeSet as containing AnyObject - // (because mutating __proto__ will change an object's TypeObject). + // (because mutating __proto__ will change an object's ObjectGroup). MOZ_ASSERT(constraints); - types::TypeObjectKey *objType = types::TypeObjectKey::get(obj); - objType->hasStableClassAndProto(constraints); + types::ObjectGroupKey *key = types::ObjectGroupKey::get(obj); + key->hasStableClassAndProto(constraints); LifoAlloc *alloc = GetJitContext()->temp->lifoAlloc(); return alloc->new_(alloc, types::Type::ObjectType(obj)); @@ -676,6 +676,7 @@ if (vp.isObject()) { // Create a singleton type set for the object. This isn't necessary for // other types as the result type encodes all needed information. + MOZ_ASSERT(!IsInsideNursery(&vp.toObject())); setResultTypeSet(MakeSingletonTypeSet(constraints, &vp.toObject())); } if (vp.isMagic() && vp.whyMagic() == JS_UNINITIALIZED_LEXICAL) { @@ -695,6 +696,7 @@ MConstant::MConstant(JSObject *obj) : value_(ObjectValue(*obj)) { + MOZ_ASSERT(!IsInsideNursery(obj)); setResultType(MIRType_Object); setMovable(); } @@ -804,6 +806,39 @@ return true; } +MNurseryObject::MNurseryObject(JSObject *obj, uint32_t index, types::CompilerConstraintList *constraints) + : index_(index) +{ + setResultType(MIRType_Object); + + MOZ_ASSERT(IsInsideNursery(obj)); + MOZ_ASSERT(!obj->isSingleton()); + setResultTypeSet(MakeSingletonTypeSet(constraints, obj)); + + setMovable(); +} + +MNurseryObject * +MNurseryObject::New(TempAllocator &alloc, JSObject *obj, uint32_t index, + types::CompilerConstraintList *constraints) +{ + return new(alloc) MNurseryObject(obj, index, constraints); +} + +HashNumber +MNurseryObject::valueHash() const +{ + return HashNumber(index_); +} + +bool +MNurseryObject::congruentTo(const MDefinition *ins) const +{ + if (!ins->isNurseryObject()) + return false; + return ins->toNurseryObject()->index_ == index_; +} + MDefinition* MSimdValueX4::foldsTo(TempAllocator &alloc) { @@ -2058,18 +2093,17 @@ // The folded MConstant should maintain the same MIRType with // the original MMinMax. - MConstant *constant; if (type() == MIRType_Int32) { - int32_t cast = static_cast(result); - MOZ_ASSERT(cast == result); - constant = MConstant::New(alloc, Int32Value(cast)); + int32_t cast; + if (mozilla::NumberEqualsInt32(result, &cast)) + return MConstant::New(alloc, Int32Value(cast)); } else { MOZ_ASSERT(IsFloatingPointType(type())); - constant = MConstant::New(alloc, DoubleValue(result)); + MConstant *constant = MConstant::New(alloc, DoubleValue(result)); if (type() == MIRType_Float32) constant->setResultType(MIRType_Float32); + return constant; } - return constant; } MDefinition *operand = lhs()->isConstantValue() ? rhs() : lhs(); @@ -2231,6 +2265,18 @@ setPolicyType(MIRType_Float32); } +MHypot *MHypot::New(TempAllocator &alloc, const MDefinitionVector & vector) +{ + uint32_t length = vector.length(); + MHypot * hypot = new(alloc) MHypot; + if (!hypot->init(alloc, length)) + return nullptr; + + for (uint32_t i = 0; i < length; ++i) + hypot->initOperand(i, vector[i]); + return hypot; +} + bool MAdd::fallible() const { @@ -3033,6 +3079,32 @@ MToInt32::foldsTo(TempAllocator &alloc) { MDefinition *input = getOperand(0); + + // Fold this operation if the input operand is constant. + if (input->isConstant()) { + Value val = input->toConstant()->value(); + DebugOnly convert = conversion(); + switch (input->type()) { + case MIRType_Null: + MOZ_ASSERT(convert == MacroAssembler::IntConversion_Any); + return MConstant::New(alloc, Int32Value(0)); + case MIRType_Boolean: + MOZ_ASSERT(convert == MacroAssembler::IntConversion_Any || + convert == MacroAssembler::IntConversion_NumbersOrBoolsOnly); + return MConstant::New(alloc, Int32Value(val.toBoolean())); + case MIRType_Int32: + return MConstant::New(alloc, Int32Value(val.toInt32())); + case MIRType_Float32: + case MIRType_Double: + int32_t ival; + // Only the value within the range of Int32 can be substitued as constant. + if (mozilla::NumberEqualsInt32(val.toNumber(), &ival)) + return MConstant::New(alloc, Int32Value(ival)); + default: + break; + } + } + if (input->type() == MIRType_Int32) return input; return this; @@ -3569,14 +3641,15 @@ MNewObject::shouldUseVM() const { PlainObject *obj = templateObject(); - return obj->hasSingletonType() || obj->hasDynamicSlots(); + return obj->isSingleton() || obj->hasDynamicSlots(); } bool MCreateThisWithTemplate::canRecoverOnBailout() const { - MOZ_ASSERT(!templateObject()->denseElementsAreCopyOnWrite()); - MOZ_ASSERT(!templateObject()->is()); + MOZ_ASSERT(templateObject()->is() || templateObject()->is()); + MOZ_ASSERT_IF(templateObject()->is(), + !templateObject()->as().denseElementsAreCopyOnWrite()); return true; } @@ -3589,7 +3662,7 @@ if (obj->isNewObject()) templateObject = obj->toNewObject()->templateObject(); else if (obj->isCreateThisWithTemplate()) - templateObject = obj->toCreateThisWithTemplate()->templateObject(); + templateObject = &obj->toCreateThisWithTemplate()->templateObject()->as(); else templateObject = obj->toNewCallObject()->templateObject(); numSlots_ = templateObject->slotSpan(); @@ -3686,7 +3759,7 @@ // immediately, but only when data doesn't fit the available array slots. bool allocating = allocatingBehaviour() != NewArray_Unallocating && count() > arraySlots; - return templateObject()->hasSingletonType() || allocating; + return templateObject()->isSingleton() || allocating; } bool @@ -3966,7 +4039,7 @@ return nullptr; for (size_t i = 0; i < numEntries(); i++) { if (entries_[i]->func == func) - types->addType(types::Type::ObjectType(entries_[i]->typeObj), alloc); + types->addType(types::Type::ObjectType(entries_[i]->group), alloc); } return types; } @@ -4278,14 +4351,14 @@ unsigned count = types->getObjectCount(); for (unsigned i = 0; i < count; i++) { - types::TypeObjectKey *object = types->getObject(i); - if (!object) + types::ObjectGroupKey *key = types->getObject(i); + if (!key) continue; - if (object->unknownProperties()) + if (key->unknownProperties()) return MIRType_None; - types::HeapTypeSetKey elementTypes = object->property(JSID_VOID); + types::HeapTypeSetKey elementTypes = key->property(JSID_VOID); MIRType type = elementTypes.knownMIRType(constraints); if (type == MIRType_None) @@ -4302,7 +4375,7 @@ static BarrierKind PropertyReadNeedsTypeBarrier(types::CompilerConstraintList *constraints, - types::TypeObjectKey *object, PropertyName *name, + types::ObjectGroupKey *key, PropertyName *name, types::TypeSet *observed) { // If the object being read from has types for the property which haven't @@ -4313,14 +4386,14 @@ // // We also need a barrier if the object is a proxy, because then all bets // are off, just as if it has unknown properties. - if (object->unknownProperties() || observed->empty() || - object->clasp()->isProxy()) + if (key->unknownProperties() || observed->empty() || + key->clasp()->isProxy()) { return BarrierKind::TypeSet; } jsid id = name ? NameToId(name) : JSID_VOID; - types::HeapTypeSetKey property = object->property(id); + types::HeapTypeSetKey property = key->property(id); if (property.maybeTypes()) { if (!TypeSetIncludes(observed, MIRType_Value, property.maybeTypes())) { // If all possible objects have been observed, we don't have to @@ -4337,7 +4410,8 @@ // initial 'undefined' value for properties, in particular global // variables declared with 'var'. Until the property is assigned a value // other than undefined, a barrier is required. - if (JSObject *obj = object->singleton()) { + if (key->isSingleton()) { + JSObject *obj = key->singleton(); if (name && types::CanHaveEmptyPropertyTypesForOwnProperty(obj) && (!property.maybeTypes() || property.maybeTypes()->empty())) { @@ -4352,17 +4426,17 @@ BarrierKind jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx, types::CompilerConstraintList *constraints, - types::TypeObjectKey *object, PropertyName *name, + types::ObjectGroupKey *key, PropertyName *name, types::TemporaryTypeSet *observed, bool updateObserved) { // If this access has never executed, try to add types to the observed set // according to any property which exists on the object or its prototype. if (updateObserved && observed->empty() && name) { JSObject *obj; - if (object->singleton()) - obj = object->singleton(); - else if (object->hasTenuredProto()) - obj = object->proto().toObjectOrNull(); + if (key->isSingleton()) + obj = key->singleton(); + else if (key->hasTenuredProto()) + obj = key->proto().toObjectOrNull(); else obj = nullptr; @@ -4370,12 +4444,12 @@ if (!obj->getClass()->isNative()) break; - types::TypeObjectKey *typeObj = types::TypeObjectKey::get(obj); + types::ObjectGroupKey *key = types::ObjectGroupKey::get(obj); if (propertycx) - typeObj->ensureTrackedProperty(propertycx, NameToId(name)); + key->ensureTrackedProperty(propertycx, NameToId(name)); - if (!typeObj->unknownProperties()) { - types::HeapTypeSetKey property = typeObj->property(NameToId(name)); + if (!key->unknownProperties()) { + types::HeapTypeSetKey property = key->property(NameToId(name)); if (property.maybeTypes()) { types::TypeSet::TypeList types; if (!property.maybeTypes()->enumerateTypes(&types)) @@ -4394,7 +4468,7 @@ } } - return PropertyReadNeedsTypeBarrier(constraints, object, name, observed); + return PropertyReadNeedsTypeBarrier(constraints, key, name, observed); } BarrierKind @@ -4414,9 +4488,9 @@ bool updateObserved = types->getObjectCount() == 1; for (size_t i = 0; i < types->getObjectCount(); i++) { - types::TypeObjectKey *object = types->getObject(i); - if (object) { - BarrierKind kind = PropertyReadNeedsTypeBarrier(propertycx, constraints, object, name, + types::ObjectGroupKey *key = types->getObject(i); + if (key) { + BarrierKind kind = PropertyReadNeedsTypeBarrier(propertycx, constraints, key, name, observed, updateObserved); if (kind == BarrierKind::TypeSet) return BarrierKind::TypeSet; @@ -4448,16 +4522,16 @@ BarrierKind res = BarrierKind::NoBarrier; for (size_t i = 0; i < types->getObjectCount(); i++) { - types::TypeObjectKey *object = types->getObject(i); - if (!object) + types::ObjectGroupKey *key = types->getObject(i); + if (!key) continue; while (true) { - if (!object->hasStableClassAndProto(constraints) || !object->hasTenuredProto()) + if (!key->hasStableClassAndProto(constraints) || !key->hasTenuredProto()) return BarrierKind::TypeSet; - if (!object->proto().isObject()) + if (!key->proto().isObject()) break; - object = types::TypeObjectKey::get(object->proto().toObject()); - BarrierKind kind = PropertyReadNeedsTypeBarrier(constraints, object, name, observed); + key = types::ObjectGroupKey::get(key->proto().toObject()); + BarrierKind kind = PropertyReadNeedsTypeBarrier(constraints, key, name, observed); if (kind == BarrierKind::TypeSet) return BarrierKind::TypeSet; @@ -4484,13 +4558,13 @@ return false; for (size_t i = 0; i < types->getObjectCount(); i++) { - types::TypeObjectKey *object = types->getObject(i); - if (object) { - if (object->unknownProperties()) + types::ObjectGroupKey *key = types->getObject(i); + if (key) { + if (key->unknownProperties()) return false; // Check if the property has been reconfigured or is a getter. - types::HeapTypeSetKey property = object->property(NameToId(name)); + types::HeapTypeSetKey property = key->property(NameToId(name)); if (property.nonData(constraints)) return false; } @@ -4515,17 +4589,17 @@ } for (size_t i = 0; i < types->getObjectCount(); i++) { - types::TypeObjectKey *object = types->getObject(i); - if (!object) + types::ObjectGroupKey *key = types->getObject(i); + if (!key) continue; - if (object->unknownProperties()) { + if (key->unknownProperties()) { observed->addType(types::Type::AnyObjectType(), alloc); return; } jsid id = name ? NameToId(name) : JSID_VOID; - types::HeapTypeSetKey property = object->property(id); + types::HeapTypeSetKey property = key->property(id); types::HeapTypeSet *types = property.maybeTypes(); if (!types) continue; @@ -4536,9 +4610,9 @@ } for (size_t i = 0; i < types->getObjectCount(); i++) { - types::TypeObjectKey *object = types->getObject(i); - if (object) - observed->addType(types::Type::ObjectType(object), alloc); + types::ObjectGroupKey *key = types->getObject(i); + if (key) + observed->addType(types::Type::ObjectType(key), alloc); } } } @@ -4578,15 +4652,15 @@ Maybe aggregateProperty; for (size_t i = 0; i < objTypes->getObjectCount(); i++) { - types::TypeObjectKey *object = objTypes->getObject(i); - if (!object) + types::ObjectGroupKey *key = objTypes->getObject(i); + if (!key) continue; - if (object->unknownProperties()) + if (key->unknownProperties()) return false; jsid id = name ? NameToId(name) : JSID_VOID; - types::HeapTypeSetKey property = object->property(id); + types::HeapTypeSetKey property = key->property(id); if (!property.maybeTypes() || property.couldBeConstant(constraints)) return false; @@ -4650,19 +4724,21 @@ } static MInstruction * -AddTypeGuard(TempAllocator &alloc, MBasicBlock *current, MDefinition *obj, - types::TypeObjectKey *type, bool bailOnEquality) +AddGroupGuard(TempAllocator &alloc, MBasicBlock *current, MDefinition *obj, + types::ObjectGroupKey *key, bool bailOnEquality) { MInstruction *guard; - if (type->isTypeObject()) - guard = MGuardObjectType::New(alloc, obj, type->asTypeObject(), bailOnEquality); - else - guard = MGuardObjectIdentity::New(alloc, obj, type->asSingleObject(), bailOnEquality); + if (key->isGroup()) { + guard = MGuardObjectGroup::New(alloc, obj, key->group(), bailOnEquality, + Bailout_ObjectIdentityOrTypeGuard); + } else { + guard = MGuardObjectIdentity::New(alloc, obj, key->singleton(), bailOnEquality); + } current->add(guard); - // For now, never move type object guards. + // For now, never move object group / identity guards. guard->setNotMovable(); return guard; @@ -4702,17 +4778,17 @@ bool success = true; for (size_t i = 0; i < types->getObjectCount(); i++) { - types::TypeObjectKey *object = types->getObject(i); - if (!object || object->unknownProperties()) + types::ObjectGroupKey *key = types->getObject(i); + if (!key || key->unknownProperties()) continue; - // TI doesn't track TypedArray objects and should never insert a type + // TI doesn't track TypedArray indexes and should never insert a type // barrier for them. - if (!name && IsAnyTypedArrayClass(object->clasp())) + if (!name && IsAnyTypedArrayClass(key->clasp())) continue; jsid id = name ? NameToId(name) : JSID_VOID; - types::HeapTypeSetKey property = object->property(id); + types::HeapTypeSetKey property = key->property(id); if (!CanWriteProperty(alloc, constraints, property, *pvalue, implicitType)) { // Either pobj or pvalue needs to be modified to filter out the // types which the value could have but are not in the property, @@ -4736,26 +4812,26 @@ if (types->getObjectCount() <= 1) return true; - types::TypeObjectKey *excluded = nullptr; + types::ObjectGroupKey *excluded = nullptr; for (size_t i = 0; i < types->getObjectCount(); i++) { - types::TypeObjectKey *object = types->getObject(i); - if (!object || object->unknownProperties()) + types::ObjectGroupKey *key = types->getObject(i); + if (!key || key->unknownProperties()) continue; - if (!name && IsAnyTypedArrayClass(object->clasp())) + if (!name && IsAnyTypedArrayClass(key->clasp())) continue; jsid id = name ? NameToId(name) : JSID_VOID; - types::HeapTypeSetKey property = object->property(id); + types::HeapTypeSetKey property = key->property(id); if (CanWriteProperty(alloc, constraints, property, *pvalue, implicitType)) continue; if ((property.maybeTypes() && !property.maybeTypes()->empty()) || excluded) return true; - excluded = object; + excluded = key; } MOZ_ASSERT(excluded); - *pobj = AddTypeGuard(alloc, current, *pobj, excluded, /* bailOnEquality = */ true); + *pobj = AddGroupGuard(alloc, current, *pobj, excluded, /* bailOnEquality = */ true); return false; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MIRGenerator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MIRGenerator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MIRGenerator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MIRGenerator.h 2015-02-03 14:33:32.000000000 +0000 @@ -87,6 +87,10 @@ return !compilingAsmJS() && instrumentedProfiling(); } + bool isOptimizationTrackingEnabled() { + return isProfilerInstrumentationEnabled() && !info().isAnalysis(); + } + // Whether the main thread is trying to cancel this build. bool shouldCancel(const char *why) { maybePause(); @@ -150,12 +154,12 @@ return modifiesFrameArguments_; } - typedef Vector TypeObjectVector; + typedef Vector ObjectGroupVector; // When abortReason() == AbortReason_NewScriptProperties, all types which // the new script properties analysis hasn't been performed on yet. - const TypeObjectVector &abortedNewScriptPropertiesTypes() const { - return abortedNewScriptPropertiesTypes_; + const ObjectGroupVector &abortedNewScriptPropertiesGroups() const { + return abortedNewScriptPropertiesGroups_; } public: @@ -170,7 +174,7 @@ MIRGraph *graph_; AbortReason abortReason_; bool shouldForceAbort_; // Force AbortReason_Disable - TypeObjectVector abortedNewScriptPropertiesTypes_; + ObjectGroupVector abortedNewScriptPropertiesGroups_; bool error_; mozilla::Atomic *pauseBuild_; mozilla::Atomic cancelBuild_; @@ -189,7 +193,13 @@ bool instrumentedProfiling_; bool instrumentedProfilingIsCached_; - void addAbortedNewScriptPropertiesType(types::TypeObject *type); + // List of nursery objects used by this compilation. Can be traced by a + // minor GC while compilation happens off-thread. This Vector should only + // be accessed on the main thread (IonBuilder, nursery GC or + // CodeGenerator::link). + ObjectVector nurseryObjects_; + + void addAbortedNewScriptPropertiesGroup(types::ObjectGroup *type); void setForceAbort() { shouldForceAbort_ = true; } @@ -206,6 +216,12 @@ public: const JitCompileOptions options; + + void traceNurseryObjects(JSTracer *trc); + + const ObjectVector &nurseryObjects() const { + return nurseryObjects_; + } }; } // namespace jit diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MIRGraph.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MIRGraph.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MIRGraph.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MIRGraph.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -27,7 +27,7 @@ graph_(graph), abortReason_(AbortReason_NoAbort), shouldForceAbort_(false), - abortedNewScriptPropertiesTypes_(*alloc_), + abortedNewScriptPropertiesGroups_(*alloc_), error_(false), pauseBuild_(nullptr), cancelBuild_(false), @@ -39,6 +39,7 @@ modifiesFrameArguments_(false), instrumentedProfiling_(false), instrumentedProfilingIsCached_(false), + nurseryObjects_(*alloc), options(options) { } @@ -92,14 +93,14 @@ } void -MIRGenerator::addAbortedNewScriptPropertiesType(types::TypeObject *type) +MIRGenerator::addAbortedNewScriptPropertiesGroup(types::ObjectGroup *group) { - for (size_t i = 0; i < abortedNewScriptPropertiesTypes_.length(); i++) { - if (type == abortedNewScriptPropertiesTypes_[i]) + for (size_t i = 0; i < abortedNewScriptPropertiesGroups_.length(); i++) { + if (group == abortedNewScriptPropertiesGroups_[i]) return; } - if (!abortedNewScriptPropertiesTypes_.append(type)) - CrashAtUnhandlableOOM("addAbortedNewScriptPropertiesType"); + if (!abortedNewScriptPropertiesGroups_.append(group)) + CrashAtUnhandlableOOM("addAbortedNewScriptPropertiesGroup"); } void @@ -204,7 +205,7 @@ MBasicBlock * MBasicBlock::New(MIRGraph &graph, BytecodeAnalysis *analysis, CompileInfo &info, - MBasicBlock *pred, const BytecodeSite *site, Kind kind) + MBasicBlock *pred, BytecodeSite *site, Kind kind) { MOZ_ASSERT(site->pc() != nullptr); @@ -220,7 +221,7 @@ MBasicBlock * MBasicBlock::NewPopN(MIRGraph &graph, CompileInfo &info, - MBasicBlock *pred, const BytecodeSite *site, Kind kind, uint32_t popped) + MBasicBlock *pred, BytecodeSite *site, Kind kind, uint32_t popped) { MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, site, kind); if (!block->init()) @@ -234,7 +235,7 @@ MBasicBlock * MBasicBlock::NewWithResumePoint(MIRGraph &graph, CompileInfo &info, - MBasicBlock *pred, const BytecodeSite *site, + MBasicBlock *pred, BytecodeSite *site, MResumePoint *resumePoint) { MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, site, NORMAL); @@ -256,7 +257,7 @@ MBasicBlock * MBasicBlock::NewPendingLoopHeader(MIRGraph &graph, CompileInfo &info, - MBasicBlock *pred, const BytecodeSite *site, + MBasicBlock *pred, BytecodeSite *site, unsigned stackPhiCount) { MOZ_ASSERT(site->pc() != nullptr); @@ -324,7 +325,7 @@ return block; } -MBasicBlock::MBasicBlock(MIRGraph &graph, CompileInfo &info, const BytecodeSite *site, Kind kind) +MBasicBlock::MBasicBlock(MIRGraph &graph, CompileInfo &info, BytecodeSite *site, Kind kind) : unreachable_(false), graph_(graph), info_(info), diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MIRGraph.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MIRGraph.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MIRGraph.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MIRGraph.h 2015-02-03 14:33:32.000000000 +0000 @@ -46,7 +46,7 @@ }; private: - MBasicBlock(MIRGraph &graph, CompileInfo &info, const BytecodeSite *site, Kind kind); + MBasicBlock(MIRGraph &graph, CompileInfo &info, BytecodeSite *site, Kind kind); bool init(); void copySlots(MBasicBlock *from); bool inherit(TempAllocator &alloc, BytecodeAnalysis *analysis, MBasicBlock *pred, @@ -107,14 +107,14 @@ // Creates a new basic block for a MIR generator. If |pred| is not nullptr, // its slots and stack depth are initialized from |pred|. static MBasicBlock *New(MIRGraph &graph, BytecodeAnalysis *analysis, CompileInfo &info, - MBasicBlock *pred, const BytecodeSite *site, Kind kind); + MBasicBlock *pred, BytecodeSite *site, Kind kind); static MBasicBlock *NewPopN(MIRGraph &graph, CompileInfo &info, - MBasicBlock *pred, const BytecodeSite *site, Kind kind, uint32_t popn); + MBasicBlock *pred, BytecodeSite *site, Kind kind, uint32_t popn); static MBasicBlock *NewWithResumePoint(MIRGraph &graph, CompileInfo &info, - MBasicBlock *pred, const BytecodeSite *site, + MBasicBlock *pred, BytecodeSite *site, MResumePoint *resumePoint); static MBasicBlock *NewPendingLoopHeader(MIRGraph &graph, CompileInfo &info, - MBasicBlock *pred, const BytecodeSite *site, + MBasicBlock *pred, BytecodeSite *site, unsigned loopStateSlots); static MBasicBlock *NewSplitEdge(MIRGraph &graph, CompileInfo &info, MBasicBlock *pred); static MBasicBlock *NewAsmJS(MIRGraph &graph, CompileInfo &info, @@ -602,13 +602,14 @@ void dump(FILE *fp); void dump(); - // Track bailouts by storing the current pc in MIR instruction added at this - // cycle. This is also used for tracking calls when profiling. - void updateTrackedSite(const BytecodeSite *site) { + // Track bailouts by storing the current pc in MIR instruction added at + // this cycle. This is also used for tracking calls and optimizations when + // profiling. + void updateTrackedSite(BytecodeSite *site) { MOZ_ASSERT(site->tree() == trackedSite_->tree()); trackedSite_ = site; } - const BytecodeSite *trackedSite() const { + BytecodeSite *trackedSite() const { return trackedSite_; } jsbytecode *trackedPc() const { @@ -657,7 +658,7 @@ Vector immediatelyDominated_; MBasicBlock *immediateDominator_; - const BytecodeSite *trackedSite_; + BytecodeSite *trackedSite_; #if defined(JS_ION_PERF) || defined(DEBUG) unsigned lineno_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MIR.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MIR.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MIR.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MIR.h 2015-02-03 14:33:32.000000000 +0000 @@ -456,6 +456,11 @@ InlineScriptTree *trackedTree() const { return trackedSite_ ? trackedSite_->tree() : nullptr; } + TrackedOptimizations *trackedOptimizations() const { + return trackedSite_ && trackedSite_->hasOptimizations() + ? trackedSite_->optimizations() + : nullptr; + } JSScript *profilerLeaveScript() const { return trackedTree()->outermostCaller()->script(); @@ -1324,6 +1329,33 @@ ALLOW_CLONE(MConstant) }; +class MNurseryObject : public MNullaryInstruction +{ + // Index in MIRGenerator::nurseryObjects_. + uint32_t index_; + + protected: + MNurseryObject(JSObject *obj, uint32_t index, types::CompilerConstraintList *constraints); + + public: + INSTRUCTION_HEADER(NurseryObject) + static MNurseryObject *New(TempAllocator &alloc, JSObject *obj, uint32_t index, + types::CompilerConstraintList *constraints = nullptr); + + HashNumber valueHash() const MOZ_OVERRIDE; + bool congruentTo(const MDefinition *ins) const MOZ_OVERRIDE; + + uint32_t index() const { + return index_; + } + + AliasSet getAliasSet() const MOZ_OVERRIDE { + return AliasSet::None(); + } + + ALLOW_CLONE(MNurseryObject) +}; + // Generic constructor of SIMD valuesX4. class MSimdValueX4 : public MQuaternaryInstruction, @@ -1922,7 +1954,7 @@ class MSimdBinaryArith : public MBinaryInstruction, - public NoTypePolicy::Data + public MixPolicy, SimdSameAsReturnedTypePolicy<1> >::Data { public: enum Operation { @@ -1958,8 +1990,6 @@ { MOZ_ASSERT_IF(type == MIRType_Int32x4, op == Add || op == Sub || op == Mul); MOZ_ASSERT(IsSimdType(type)); - MOZ_ASSERT(left->type() == right->type()); - MOZ_ASSERT(left->type() == type); setResultType(type); setMovable(); if (op == Add || op == Mul || op == Min || op == Max) @@ -1968,10 +1998,18 @@ public: INSTRUCTION_HEADER(SimdBinaryArith) + static MSimdBinaryArith *New(TempAllocator &alloc, MDefinition *left, MDefinition *right, + Operation op, MIRType t) + { + return new(alloc) MSimdBinaryArith(left, right, op, t); + } + static MSimdBinaryArith *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right, Operation op, MIRType t) { - return new(alloc) MSimdBinaryArith(left, right, op, t); + MOZ_ASSERT(left->type() == right->type()); + MOZ_ASSERT(left->type() == t); + return New(alloc, left, right, op, t); } AliasSet getAliasSet() const MOZ_OVERRIDE { @@ -2654,7 +2692,7 @@ { ArrayObject *obj = templateObject(); setResultType(MIRType_Object); - if (!obj->hasSingletonType()) + if (!obj->isSingleton()) setResultTypeSet(MakeSingletonTypeSet(constraints, obj)); } @@ -2716,7 +2754,7 @@ : templateObject_(templateObject), initialHeap_(initialHeap) { - MOZ_ASSERT(!templateObject->hasSingletonType()); + MOZ_ASSERT(!templateObject->isSingleton()); setResultType(MIRType_Object); setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject)); } @@ -2760,7 +2798,7 @@ { setGuard(); // Need to throw if length is negative. setResultType(MIRType_Object); - if (!templateObject->hasSingletonType()) + if (!templateObject->isSingleton()) setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject)); } @@ -2809,7 +2847,7 @@ PlainObject *obj = templateObject(); MOZ_ASSERT_IF(mode != ObjectLiteral, !shouldUseVM()); setResultType(MIRType_Object); - if (!obj->hasSingletonType()) + if (!obj->isSingleton()) setResultTypeSet(MakeSingletonTypeSet(constraints, obj)); // The constant is kept separated in a MConstant, this way we can safely @@ -2984,6 +3022,33 @@ } }; +class MSimdUnbox + : public MUnaryInstruction, + public SingleObjectPolicy::Data +{ + protected: + MSimdUnbox(MDefinition *op, MIRType type) + : MUnaryInstruction(op) + { + MOZ_ASSERT(IsSimdType(type)); + setMovable(); + setResultType(type); + } + + public: + INSTRUCTION_HEADER(SimdUnbox) + ALLOW_CLONE(MSimdUnbox) + + static MSimdUnbox *New(TempAllocator &alloc, MDefinition *op, MIRType type) + { + return new(alloc) MSimdUnbox(op, type); + } + + AliasSet getAliasSet() const MOZ_OVERRIDE { + return AliasSet::None(); + } +}; + // Creates a new derived type object. At runtime, this is just a call // to `BinaryBlock::createDerived()`. That is, the MIR itself does not // compile to particularly optimized code. However, using a distinct @@ -4208,8 +4273,8 @@ } // Template for |this|, provided by TI. - PlainObject *templateObject() const { - return &getOperand(0)->toConstant()->value().toObject().as(); + JSObject *templateObject() const { + return &getOperand(0)->toConstant()->value().toObject(); } gc::InitialHeap initialHeap() const { @@ -4761,8 +4826,8 @@ } #endif - bool writeRecoverData(CompactBufferWriter &writer) const; - bool canRecoverOnBailout() const { + bool writeRecoverData(CompactBufferWriter &writer) const MOZ_OVERRIDE; + bool canRecoverOnBailout() const MOZ_OVERRIDE { return input()->type() < MIRType_Symbol; } @@ -5474,11 +5539,10 @@ // Inline implementation of Math.hypot(). class MHypot - : public MBinaryInstruction, - public MixPolicy, DoublePolicy<1> >::Data + : public MVariadicInstruction, + public AllDoublePolicy::Data { - MHypot(MDefinition *y, MDefinition *x) - : MBinaryInstruction(x, y) + MHypot() { setResultType(MIRType_Double); setMovable(); @@ -5486,17 +5550,7 @@ public: INSTRUCTION_HEADER(Hypot) - static MHypot *New(TempAllocator &alloc, MDefinition *x, MDefinition *y) { - return new(alloc) MHypot(y, x); - } - - MDefinition *x() const { - return getOperand(0); - } - - MDefinition *y() const { - return getOperand(1); - } + static MHypot *New(TempAllocator &alloc, const MDefinitionVector &vector); bool congruentTo(const MDefinition *ins) const MOZ_OVERRIDE { return congruentIfOperandsEqual(ins); @@ -5515,7 +5569,14 @@ return true; } - ALLOW_CLONE(MHypot) + bool canClone() const MOZ_OVERRIDE { + return true; + } + + MInstruction *clone(TempAllocator &alloc, + const MDefinitionVector &inputs) const MOZ_OVERRIDE { + return MHypot::New(alloc, inputs); + } }; // Inline implementation of Math.pow(). @@ -6252,8 +6313,8 @@ JSObject *templateObject() const { return &getOperand(2)->toConstant()->value().toObject(); } - types::TypeObject *typeObject() const { - return templateObject()->type(); + types::ObjectGroup *group() const { + return templateObject()->group(); } bool possiblyCalls() const MOZ_OVERRIDE { return true; @@ -7091,22 +7152,22 @@ uint16_t flags; gc::Cell *scriptOrLazyScript; bool singletonType; - bool useNewTypeForClone; + bool useSingletonForClone; explicit LambdaFunctionInfo(JSFunction *fun) : fun(fun), flags(fun->flags()), scriptOrLazyScript(fun->hasScript() ? (gc::Cell *) fun->nonLazyScript() : (gc::Cell *) fun->lazyScript()), - singletonType(fun->hasSingletonType()), - useNewTypeForClone(types::UseNewTypeForClone(fun)) + singletonType(fun->isSingleton()), + useSingletonForClone(types::UseSingletonForClone(fun)) {} LambdaFunctionInfo(const LambdaFunctionInfo &info) : fun((JSFunction *) info.fun), flags(info.flags), scriptOrLazyScript(info.scriptOrLazyScript), singletonType(info.singletonType), - useNewTypeForClone(info.useNewTypeForClone) + useSingletonForClone(info.useSingletonForClone) {} }; @@ -7120,7 +7181,7 @@ : MBinaryInstruction(scopeChain, cst), info_(&cst->value().toObject().as()) { setResultType(MIRType_Object); - if (!info().fun->hasSingletonType() && !types::UseNewTypeForClone(info().fun)) + if (!info().fun->isSingleton() && !types::UseSingletonForClone(info().fun)) setResultTypeSet(MakeSingletonTypeSet(constraints, info().fun)); } @@ -7158,8 +7219,8 @@ : MBinaryInstruction(scopeChain, this_), info_(fun) { setResultType(MIRType_Object); - MOZ_ASSERT(!types::UseNewTypeForClone(fun)); - if (!fun->hasSingletonType()) + MOZ_ASSERT(!types::UseSingletonForClone(fun)); + if (!fun->isSingleton()) setResultTypeSet(MakeSingletonTypeSet(constraints, fun)); } @@ -7998,21 +8059,29 @@ : public MBinaryInstruction, public SingleObjectPolicy::Data { - bool bailOnNull_; + public: + enum NullBehavior { + HandleNull, + BailOnNull, + NullNotPossible + }; + + private: + NullBehavior nullBehavior_; int32_t offsetAdjustment_; MLoadUnboxedObjectOrNull(MDefinition *elements, MDefinition *index, - bool bailOnNull, int32_t offsetAdjustment) + NullBehavior nullBehavior, int32_t offsetAdjustment) : MBinaryInstruction(elements, index), - bailOnNull_(bailOnNull), + nullBehavior_(nullBehavior), offsetAdjustment_(offsetAdjustment) { - if (bailOnNull) { + if (nullBehavior == BailOnNull) { // Don't eliminate loads which bail out on a null pointer, for the // same reason as MLoadElement. setGuard(); } - setResultType(bailOnNull ? MIRType_Object : MIRType_Value); + setResultType(nullBehavior == HandleNull ? MIRType_Value : MIRType_Object); setMovable(); MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment)); MOZ_ASSERT(index->type() == MIRType_Int32); @@ -8023,8 +8092,9 @@ static MLoadUnboxedObjectOrNull *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index, - bool bailOnNull, int32_t offsetAdjustment) { - return new(alloc) MLoadUnboxedObjectOrNull(elements, index, bailOnNull, offsetAdjustment); + NullBehavior nullBehavior, int32_t offsetAdjustment) { + return new(alloc) MLoadUnboxedObjectOrNull(elements, index, nullBehavior, + offsetAdjustment); } MDefinition *elements() const { @@ -8033,20 +8103,20 @@ MDefinition *index() const { return getOperand(1); } - bool bailOnNull() const { - return bailOnNull_; + NullBehavior nullBehavior() const { + return nullBehavior_; } int32_t offsetAdjustment() const { return offsetAdjustment_; } bool fallible() const { - return bailOnNull(); + return nullBehavior() == BailOnNull; } bool congruentTo(const MDefinition *ins) const MOZ_OVERRIDE { if (!ins->isLoadUnboxedObjectOrNull()) return false; const MLoadUnboxedObjectOrNull *other = ins->toLoadUnboxedObjectOrNull(); - if (bailOnNull() != other->bailOnNull()) + if (nullBehavior() != other->nullBehavior()) return false; if (offsetAdjustment() != other->offsetAdjustment()) return false; @@ -8079,7 +8149,8 @@ INSTRUCTION_HEADER(LoadUnboxedString) static MLoadUnboxedString *New(TempAllocator &alloc, - MDefinition *elements, MDefinition *index, int32_t offsetAdjustment) { + MDefinition *elements, MDefinition *index, + int32_t offsetAdjustment = 0) { return new(alloc) MLoadUnboxedString(elements, index, offsetAdjustment); } @@ -8270,7 +8341,7 @@ static MStoreUnboxedObjectOrNull *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index, MDefinition *value, MDefinition *typedObj, - int32_t offsetAdjustment) { + int32_t offsetAdjustment = 0) { return new(alloc) MStoreUnboxedObjectOrNull(elements, index, value, typedObj, offsetAdjustment); } @@ -8325,7 +8396,7 @@ static MStoreUnboxedString *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index, - MDefinition *value, int32_t offsetAdjustment) { + MDefinition *value, int32_t offsetAdjustment = 0) { return new(alloc) MStoreUnboxedString(elements, index, value, offsetAdjustment); } MDefinition *elements() const { @@ -8520,14 +8591,16 @@ Scalar::Type arrayType_; bool requiresBarrier_; int32_t offsetAdjustment_; + bool canonicalizeDoubles_; MLoadTypedArrayElement(MDefinition *elements, MDefinition *index, Scalar::Type arrayType, MemoryBarrierRequirement requiresBarrier, - int32_t offsetAdjustment) + int32_t offsetAdjustment, bool canonicalizeDoubles) : MBinaryInstruction(elements, index), arrayType_(arrayType), requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier), - offsetAdjustment_(offsetAdjustment) + offsetAdjustment_(offsetAdjustment), + canonicalizeDoubles_(canonicalizeDoubles) { setResultType(MIRType_Value); if (requiresBarrier_) @@ -8545,10 +8618,12 @@ static MLoadTypedArrayElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index, Scalar::Type arrayType, MemoryBarrierRequirement requiresBarrier=DoesNotRequireMemoryBarrier, - int32_t offsetAdjustment = 0) + int32_t offsetAdjustment = 0, + bool canonicalizeDoubles = true) { return new(alloc) MLoadTypedArrayElement(elements, index, arrayType, - requiresBarrier, offsetAdjustment); + requiresBarrier, offsetAdjustment, + canonicalizeDoubles); } Scalar::Type arrayType() const { @@ -8561,6 +8636,9 @@ bool requiresMemoryBarrier() const { return requiresBarrier_; } + bool canonicalizeDoubles() const { + return canonicalizeDoubles_; + } MDefinition *elements() const { return getOperand(0); } @@ -8588,6 +8666,8 @@ return false; if (offsetAdjustment() != other->offsetAdjustment()) return false; + if (canonicalizeDoubles() != other->canonicalizeDoubles()) + return false; return congruentIfOperandsEqual(other); } @@ -9115,11 +9195,11 @@ class InlinePropertyTable : public TempObject { struct Entry : public TempObject { - AlwaysTenured typeObj; + AlwaysTenured group; AlwaysTenuredFunction func; - Entry(types::TypeObject *typeObj, JSFunction *func) - : typeObj(typeObj), func(func) + Entry(types::ObjectGroup *group, JSFunction *func) + : group(group), func(func) { } }; @@ -9146,17 +9226,17 @@ return pc_; } - bool addEntry(TempAllocator &alloc, types::TypeObject *typeObj, JSFunction *func) { - return entries_.append(new(alloc) Entry(typeObj, func)); + bool addEntry(TempAllocator &alloc, types::ObjectGroup *group, JSFunction *func) { + return entries_.append(new(alloc) Entry(group, func)); } size_t numEntries() const { return entries_.length(); } - types::TypeObject *getTypeObject(size_t i) const { + types::ObjectGroup *getObjectGroup(size_t i) const { MOZ_ASSERT(i < numEntries()); - return entries_[i]->typeObj; + return entries_[i]->group; } JSFunction *getFunction(size_t i) const { @@ -9279,8 +9359,8 @@ bool updateForReplacement(MDefinition *ins) MOZ_OVERRIDE; }; -// Emit code to load a value from an object's slots if its shape matches -// one of the shapes observed by the baseline IC, else bails out. +// Emit code to load a value from an object if its shape/group matches one of +// the shapes/groups observed by the baseline IC, else bails out. class MGetPropertyPolymorphic : public MUnaryInstruction, public SingleObjectPolicy::Data @@ -9293,12 +9373,14 @@ Shape *shape; }; - Vector shapes_; + Vector nativeShapes_; + Vector unboxedGroups_; AlwaysTenuredPropertyName name_; MGetPropertyPolymorphic(TempAllocator &alloc, MDefinition *obj, PropertyName *name) : MUnaryInstruction(obj), - shapes_(alloc), + nativeShapes_(alloc), + unboxedGroups_(alloc), name_(name) { setGuard(); @@ -9306,10 +9388,6 @@ setResultType(MIRType_Value); } - PropertyName *name() const { - return name_; - } - public: INSTRUCTION_HEADER(GetPropertyPolymorphic) @@ -9329,47 +9407,65 @@ Entry entry; entry.objShape = objShape; entry.shape = shape; - return shapes_.append(entry); + return nativeShapes_.append(entry); + } + bool addUnboxedGroup(types::ObjectGroup *group) { + return unboxedGroups_.append(group); } size_t numShapes() const { - return shapes_.length(); + return nativeShapes_.length(); } Shape *objShape(size_t i) const { - return shapes_[i].objShape; + return nativeShapes_[i].objShape; } Shape *shape(size_t i) const { - return shapes_[i].shape; + return nativeShapes_[i].shape; + } + size_t numUnboxedGroups() const { + return unboxedGroups_.length(); + } + types::ObjectGroup *unboxedGroup(size_t i) const { + return unboxedGroups_[i]; + } + PropertyName *name() const { + return name_; } MDefinition *obj() const { return getOperand(0); } AliasSet getAliasSet() const MOZ_OVERRIDE { - return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | AliasSet::DynamicSlot); + return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | AliasSet::DynamicSlot | + (unboxedGroups_.empty() ? 0 : (AliasSet::TypedArrayElement | AliasSet::Element))); } bool mightAlias(const MDefinition *store) const MOZ_OVERRIDE; }; -// Emit code to store a value to an object's slots if its shape matches -// one of the shapes observed by the baseline IC, else bails out. +// Emit code to store a value to an object's slots if its shape/group matches +// one of the shapes/groups observed by the baseline IC, else bails out. class MSetPropertyPolymorphic : public MBinaryInstruction, - public SingleObjectPolicy::Data + public MixPolicy >::Data { struct Entry { // The shape to guard against. Shape *objShape; - // The property to laod. + // The property to load. Shape *shape; }; - Vector shapes_; + Vector nativeShapes_; + Vector unboxedGroups_; + AlwaysTenuredPropertyName name_; bool needsBarrier_; - MSetPropertyPolymorphic(TempAllocator &alloc, MDefinition *obj, MDefinition *value) + MSetPropertyPolymorphic(TempAllocator &alloc, MDefinition *obj, MDefinition *value, + PropertyName *name) : MBinaryInstruction(obj, value), - shapes_(alloc), + nativeShapes_(alloc), + unboxedGroups_(alloc), + name_(name), needsBarrier_(false) { } @@ -9377,24 +9473,37 @@ public: INSTRUCTION_HEADER(SetPropertyPolymorphic) - static MSetPropertyPolymorphic *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value) { - return new(alloc) MSetPropertyPolymorphic(alloc, obj, value); + static MSetPropertyPolymorphic *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value, + PropertyName *name) { + return new(alloc) MSetPropertyPolymorphic(alloc, obj, value, name); } bool addShape(Shape *objShape, Shape *shape) { Entry entry; entry.objShape = objShape; entry.shape = shape; - return shapes_.append(entry); + return nativeShapes_.append(entry); + } + bool addUnboxedGroup(types::ObjectGroup *group) { + return unboxedGroups_.append(group); } size_t numShapes() const { - return shapes_.length(); + return nativeShapes_.length(); } Shape *objShape(size_t i) const { - return shapes_[i].objShape; + return nativeShapes_[i].objShape; } Shape *shape(size_t i) const { - return shapes_[i].shape; + return nativeShapes_[i].shape; + } + size_t numUnboxedGroups() const { + return unboxedGroups_.length(); + } + types::ObjectGroup *unboxedGroup(size_t i) const { + return unboxedGroups_[i]; + } + PropertyName *name() const { + return name_; } MDefinition *obj() const { return getOperand(0); @@ -9409,7 +9518,8 @@ needsBarrier_ = true; } AliasSet getAliasSet() const MOZ_OVERRIDE { - return AliasSet::Store(AliasSet::ObjectFields | AliasSet::FixedSlot | AliasSet::DynamicSlot); + return AliasSet::Store(AliasSet::ObjectFields | AliasSet::FixedSlot | AliasSet::DynamicSlot | + (unboxedGroups_.empty() ? 0 : (AliasSet::TypedArrayElement | AliasSet::Element))); } }; @@ -9420,14 +9530,14 @@ // Map from JSFunction* -> MBasicBlock. struct Entry { JSFunction *func; - // If |func| has a singleton type, |funcType| is null. Otherwise, - // |funcType| holds the TypeObject for |func|, and dispatch guards - // on the type instead of directly on the function. - types::TypeObject *funcType; + // If |func| has a singleton group, |funcGroup| is null. Otherwise, + // |funcGroup| holds the ObjectGroup for |func|, and dispatch guards + // on the group instead of directly on the function. + types::ObjectGroup *funcGroup; MBasicBlock *block; - Entry(JSFunction *func, types::TypeObject *funcType, MBasicBlock *block) - : func(func), funcType(funcType), block(block) + Entry(JSFunction *func, types::ObjectGroup *funcGroup, MBasicBlock *block) + : func(func), funcGroup(funcGroup), block(block) { } }; Vector map_; @@ -9495,8 +9605,8 @@ } public: - void addCase(JSFunction *func, types::TypeObject *funcType, MBasicBlock *block) { - map_.append(Entry(func, funcType, block)); + void addCase(JSFunction *func, types::ObjectGroup *funcGroup, MBasicBlock *block) { + map_.append(Entry(func, funcGroup, block)); } uint32_t numCases() const { return map_.length(); @@ -9504,8 +9614,8 @@ JSFunction *getCase(uint32_t i) const { return map_[i].func; } - types::TypeObject *getCaseTypeObject(uint32_t i) const { - return map_[i].funcType; + types::ObjectGroup *getCaseObjectGroup(uint32_t i) const { + return map_[i].funcGroup; } MBasicBlock *getCaseBlock(uint32_t i) const { return map_[i].block; @@ -9529,24 +9639,24 @@ } }; -// Polymorphic dispatch for inlining, keyed off incoming TypeObject. -class MTypeObjectDispatch : public MDispatchInstruction +// Polymorphic dispatch for inlining, keyed off incoming ObjectGroup. +class MObjectGroupDispatch : public MDispatchInstruction { - // Map TypeObject (of CallProp's Target Object) -> JSFunction (yielded by the CallProp). + // Map ObjectGroup (of CallProp's Target Object) -> JSFunction (yielded by the CallProp). InlinePropertyTable *inlinePropertyTable_; - MTypeObjectDispatch(TempAllocator &alloc, MDefinition *input, InlinePropertyTable *table) + MObjectGroupDispatch(TempAllocator &alloc, MDefinition *input, InlinePropertyTable *table) : MDispatchInstruction(alloc, input), inlinePropertyTable_(table) { } public: - INSTRUCTION_HEADER(TypeObjectDispatch) + INSTRUCTION_HEADER(ObjectGroupDispatch) - static MTypeObjectDispatch *New(TempAllocator &alloc, MDefinition *ins, - InlinePropertyTable *table) + static MObjectGroupDispatch *New(TempAllocator &alloc, MDefinition *ins, + InlinePropertyTable *table) { - return new(alloc) MTypeObjectDispatch(alloc, ins, table); + return new(alloc) MObjectGroupDispatch(alloc, ins, table); } InlinePropertyTable *propTable() const { @@ -9737,18 +9847,21 @@ } }; -// Guard on an object's type, inclusively or exclusively. -class MGuardObjectType +// Guard on an object's group, inclusively or exclusively. +class MGuardObjectGroup : public MUnaryInstruction, public SingleObjectPolicy::Data { - AlwaysTenured typeObject_; + AlwaysTenured group_; bool bailOnEquality_; + BailoutKind bailoutKind_; - MGuardObjectType(MDefinition *obj, types::TypeObject *typeObject, bool bailOnEquality) + MGuardObjectGroup(MDefinition *obj, types::ObjectGroup *group, bool bailOnEquality, + BailoutKind bailoutKind) : MUnaryInstruction(obj), - typeObject_(typeObject), - bailOnEquality_(bailOnEquality) + group_(group), + bailOnEquality_(bailOnEquality), + bailoutKind_(bailoutKind) { setGuard(); setMovable(); @@ -9756,28 +9869,33 @@ } public: - INSTRUCTION_HEADER(GuardObjectType) + INSTRUCTION_HEADER(GuardObjectGroup) - static MGuardObjectType *New(TempAllocator &alloc, MDefinition *obj, types::TypeObject *typeObject, - bool bailOnEquality) { - return new(alloc) MGuardObjectType(obj, typeObject, bailOnEquality); + static MGuardObjectGroup *New(TempAllocator &alloc, MDefinition *obj, types::ObjectGroup *group, + bool bailOnEquality, BailoutKind bailoutKind) { + return new(alloc) MGuardObjectGroup(obj, group, bailOnEquality, bailoutKind); } MDefinition *obj() const { return getOperand(0); } - const types::TypeObject *typeObject() const { - return typeObject_; + const types::ObjectGroup *group() const { + return group_; } bool bailOnEquality() const { return bailOnEquality_; } + BailoutKind bailoutKind() const { + return bailoutKind_; + } bool congruentTo(const MDefinition *ins) const MOZ_OVERRIDE { - if (!ins->isGuardObjectType()) + if (!ins->isGuardObjectGroup()) + return false; + if (group() != ins->toGuardObjectGroup()->group()) return false; - if (typeObject() != ins->toGuardObjectType()->typeObject()) + if (bailOnEquality() != ins->toGuardObjectGroup()->bailOnEquality()) return false; - if (bailOnEquality() != ins->toGuardObjectType()->bailOnEquality()) + if (bailoutKind() != ins->toGuardObjectGroup()->bailoutKind()) return false; return congruentIfOperandsEqual(ins); } @@ -9791,7 +9909,7 @@ : public MUnaryInstruction, public SingleObjectPolicy::Data { - AlwaysTenured singleObject_; + AlwaysTenuredObject singleObject_; bool bailOnEquality_; MGuardObjectIdentity(MDefinition *obj, JSObject *singleObject, bool bailOnEquality) @@ -12574,7 +12692,7 @@ MIRType DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj); BarrierKind PropertyReadNeedsTypeBarrier(JSContext *propertycx, types::CompilerConstraintList *constraints, - types::TypeObjectKey *object, PropertyName *name, + types::ObjectGroupKey *object, PropertyName *name, types::TemporaryTypeSet *observed, bool updateObserved); BarrierKind PropertyReadNeedsTypeBarrier(JSContext *propertycx, types::CompilerConstraintList *constraints, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MOpcodes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MOpcodes.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MOpcodes.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MOpcodes.h 2015-02-03 14:33:32.000000000 +0000 @@ -12,7 +12,9 @@ #define MIR_OPCODE_LIST(_) \ _(Constant) \ + _(NurseryObject) \ _(SimdBox) \ + _(SimdUnbox) \ _(SimdValueX4) \ _(SimdSplatX4) \ _(SimdConstant) \ @@ -37,7 +39,7 @@ _(Goto) \ _(Test) \ _(GotoWithFake) \ - _(TypeObjectDispatch) \ + _(ObjectGroupDispatch) \ _(FunctionDispatch) \ _(Compare) \ _(Phi) \ @@ -158,7 +160,7 @@ _(BindNameCache) \ _(GuardShape) \ _(GuardShapePolymorphic) \ - _(GuardObjectType) \ + _(GuardObjectGroup) \ _(GuardObjectIdentity) \ _(GuardClass) \ _(ArrayLength) \ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MoveResolver.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MoveResolver.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MoveResolver.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MoveResolver.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -84,15 +84,7 @@ InlineList stack; // This is a depth-first-search without recursion, which tries to find - // cycles in a list of moves. The output is not entirely optimal for cases - // where a source has multiple destinations, i.e.: - // [stack0] -> A - // [stack0] -> B - // - // These moves may not occur next to each other in the list, making it - // harder for the emitter to optimize memory to memory traffic. However, we - // expect duplicate sources to be rare in greedy allocation, and indicative - // of an error in LSRA. + // cycles in a list of moves. // // Algorithm. // @@ -160,7 +152,7 @@ // complete and not participating in a cycle. The resulting // move can safely be added to the ordered move list. PendingMove *done = stack.popBack(); - if (!orderedMoves_.append(*done)) + if (!addOrderedMove(*done)) return false; movePool_.free(done); } @@ -175,3 +167,48 @@ return true; } + +bool +MoveResolver::addOrderedMove(const MoveOp &move) +{ + // Sometimes the register allocator generates move groups where multiple + // moves have the same source. Try to optimize these cases when the source + // is in memory and the target of one of the moves is in a register. + MOZ_ASSERT(!move.from().aliases(move.to())); + + if (!move.from().isMemory() || move.isCycleBegin() || move.isCycleEnd()) + return orderedMoves_.append(move); + + // Look for an earlier move with the same source, where no intervening move + // touches either the source or destination of the new move. + for (int i = orderedMoves_.length() - 1; i >= 0; i--) { + const MoveOp &existing = orderedMoves_[i]; + + if (existing.from() == move.from() && + !existing.to().aliases(move.to()) && + existing.type() == move.type() && + !existing.isCycleBegin() && + !existing.isCycleEnd()) + { + MoveOp *after = orderedMoves_.begin() + i + 1; + if (existing.to().isGeneralReg() || existing.to().isFloatReg()) { + MoveOp nmove(existing.to(), move.to(), move.type()); + return orderedMoves_.insert(after, nmove); + } else if (move.to().isGeneralReg() || move.to().isFloatReg()) { + MoveOp nmove(move.to(), existing.to(), move.type()); + orderedMoves_[i] = move; + return orderedMoves_.insert(after, nmove); + } + } + + if (existing.to().aliases(move.from()) || + existing.to().aliases(move.to()) || + existing.from().aliases(move.to()) || + existing.from().aliases(move.from())) + { + break; + } + } + + return orderedMoves_.append(move); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MoveResolver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MoveResolver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/MoveResolver.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/MoveResolver.h 2015-02-03 14:33:32.000000000 +0000 @@ -240,6 +240,8 @@ PendingMove *findBlockingMove(const PendingMove *last); PendingMove *findCycledMove(PendingMoveIterator *stack, PendingMoveIterator end, const PendingMove *first); + bool addOrderedMove(const MoveOp &move); + // Internal reset function. Does not clear lists. void resetState(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/none/LIR-none.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/none/LIR-none.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/none/LIR-none.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/none/LIR-none.h 2015-02-03 14:33:32.000000000 +0000 @@ -44,7 +44,7 @@ }; class LGuardShape : public LInstruction {}; -class LGuardObjectType : public LInstruction {}; +class LGuardObjectGroup : public LInstruction {}; class LMulI : public LInstruction {}; } // namespace jit diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/none/Lowering-none.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/none/Lowering-none.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/none/Lowering-none.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/none/Lowering-none.h 2015-02-03 14:33:32.000000000 +0000 @@ -70,7 +70,7 @@ void visitPowHalf(MPowHalf *) { MOZ_CRASH(); } void visitAsmJSNeg(MAsmJSNeg *) { MOZ_CRASH(); } void visitGuardShape(MGuardShape *ins) { MOZ_CRASH(); } - void visitGuardObjectType(MGuardObjectType *ins) { MOZ_CRASH(); } + void visitGuardObjectGroup(MGuardObjectGroup *ins) { MOZ_CRASH(); } void visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins) { MOZ_CRASH(); } void visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32 *ins) { MOZ_CRASH(); } void visitAsmJSLoadHeap(MAsmJSLoadHeap *ins) { MOZ_CRASH(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/none/MacroAssembler-none.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/none/MacroAssembler-none.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/none/MacroAssembler-none.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/none/MacroAssembler-none.h 2015-02-03 14:33:32.000000000 +0000 @@ -156,6 +156,12 @@ static void TraceJumpRelocations(JSTracer *, JitCode *, CompactBufferReader &) { MOZ_CRASH(); } static void TraceDataRelocations(JSTracer *, JitCode *, CompactBufferReader &) { MOZ_CRASH(); } + static void FixupNurseryObjects(JSContext *, JitCode *, CompactBufferReader &, + const ObjectVector &) + { + MOZ_CRASH(); + } + static bool SupportsFloatingPoint() { return false; } static bool SupportsSimd() { return false; } @@ -394,6 +400,7 @@ template void loadUnboxedValue(T, MIRType, AnyRegister) { MOZ_CRASH(); } template void storeUnboxedValue(ConstantOrRegister, MIRType, T, MIRType) { MOZ_CRASH(); } + template void storeUnboxedPayload(ValueOperand value, T, size_t) { MOZ_CRASH(); } void rshiftPtr(Imm32, Register) { MOZ_CRASH(); } void rshiftPtrArithmetic(Imm32, Register) { MOZ_CRASH(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/none/MoveEmitter-none.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/none/MoveEmitter-none.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/none/MoveEmitter-none.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/none/MoveEmitter-none.h 2015-02-03 14:33:32.000000000 +0000 @@ -19,6 +19,7 @@ MoveEmitterNone(MacroAssemblerNone &) { MOZ_CRASH(); } void emit(const MoveResolver &) { MOZ_CRASH(); } void finish() { MOZ_CRASH(); } + void setScratchRegister(Register) { MOZ_CRASH(); } }; typedef MoveEmitterNone MoveEmitter; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/OptimizationTracking.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/OptimizationTracking.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/OptimizationTracking.cpp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/OptimizationTracking.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,1031 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 "jit/OptimizationTracking.h" + +#include "ds/Sort.h" +#include "jit/IonBuilder.h" +#include "jit/JitcodeMap.h" +#include "jit/JitSpewer.h" + +using namespace js; +using namespace js::jit; + +using mozilla::Maybe; +using mozilla::Some; +using mozilla::Nothing; + +typedef CodeGeneratorShared::NativeToTrackedOptimizations NativeToTrackedOptimizations; + +bool +TrackedOptimizations::trackTypeInfo(TrackedTypeInfo &&ty) +{ + return types_.append(mozilla::Move(ty)); +} + +bool +TrackedOptimizations::trackAttempt(TrackedStrategy strategy) +{ + OptimizationAttempt attempt(strategy, TrackedOutcome::GenericFailure); + currentAttempt_ = attempts_.length(); + return attempts_.append(attempt); +} + +void +TrackedOptimizations::amendAttempt(uint32_t index) +{ + currentAttempt_ = index; +} + +void +TrackedOptimizations::trackOutcome(TrackedOutcome outcome) +{ + attempts_[currentAttempt_].setOutcome(outcome); +} + +void +TrackedOptimizations::trackSuccess() +{ + attempts_[currentAttempt_].setOutcome(TrackedOutcome::GenericSuccess); +} + +template +static bool +VectorContentsMatch(const Vec *xs, const Vec *ys) +{ + if (xs->length() != ys->length()) + return false; + for (auto x = xs->begin(), y = ys->begin(); x != xs->end(); x++, y++) { + MOZ_ASSERT(y != ys->end()); + if (*x != *y) + return false; + } + return true; +} + +bool +TrackedOptimizations::matchTypes(const TempTrackedTypeInfoVector &other) const +{ + return VectorContentsMatch(&types_, &other); +} + +bool +TrackedOptimizations::matchAttempts(const TempAttemptsVector &other) const +{ + return VectorContentsMatch(&attempts_, &other); +} + +#ifdef DEBUG +static const char * +StrategyString(TrackedStrategy strategy) +{ + switch (strategy) { +#define STRATEGY_CASE(name, msg) \ + case TrackedStrategy::name: \ + return msg; + TRACKED_STRATEGY_LIST(STRATEGY_CASE) +#undef STRATEGY_CASE + + default: + MOZ_CRASH("bad strategy"); + } +} + +static const char * +OutcomeString(TrackedOutcome outcome) +{ + switch (outcome) { +#define OUTCOME_CASE(name, msg) \ + case TrackedOutcome::name: \ + return msg; + TRACKED_OUTCOME_LIST(OUTCOME_CASE) +#undef OUTCOME_CASE + + default: + MOZ_CRASH("bad outcome"); + } +} + +static const char * +TypeSiteString(TrackedTypeSite kind) +{ + switch (kind) { +#define TYPESITE_CASE(name, msg) \ + case TrackedTypeSite::name: \ + return msg; + TRACKED_TYPESITE_LIST(TYPESITE_CASE) +#undef TYPESITE_CASE + + default: + MOZ_CRASH("bad type site"); + } +} +#endif // DEBUG + +void +SpewTempTrackedTypeInfoVector(const TempTrackedTypeInfoVector *types, const char *indent = nullptr) +{ +#ifdef DEBUG + for (const TrackedTypeInfo *t = types->begin(); t != types->end(); t++) { + JitSpewStart(JitSpew_OptimizationTracking, " %s%s of type %s, type set", indent ? indent : "", + TypeSiteString(t->site()), StringFromMIRType(t->mirType())); + for (uint32_t i = 0; i < t->types().length(); i++) + JitSpewCont(JitSpew_OptimizationTracking, " %s", types::TypeString(t->types()[i])); + JitSpewFin(JitSpew_OptimizationTracking); + } +#endif +} + +void +SpewTempAttemptsVector(const TempAttemptsVector *attempts, const char *indent = nullptr) +{ +#ifdef DEBUG + for (const OptimizationAttempt *a = attempts->begin(); a != attempts->end(); a++) { + JitSpew(JitSpew_OptimizationTracking, " %s%s: %s", indent ? indent : "", + StrategyString(a->strategy()), OutcomeString(a->outcome())); + } +#endif +} + +void +TrackedOptimizations::spew() const +{ +#ifdef DEBUG + SpewTempTrackedTypeInfoVector(&types_); + SpewTempAttemptsVector(&attempts_); +#endif +} + +bool +TrackedTypeInfo::trackTypeSet(types::TemporaryTypeSet *typeSet) +{ + if (!typeSet) + return true; + return typeSet->enumerateTypes(&types_); +} + +bool +TrackedTypeInfo::trackType(types::Type type) +{ + return types_.append(type); +} + +bool +TrackedTypeInfo::operator ==(const TrackedTypeInfo &other) const +{ + return site_ == other.site_ && mirType_ == other.mirType_ && + VectorContentsMatch(&types_, &other.types_); +} + +bool +TrackedTypeInfo::operator !=(const TrackedTypeInfo &other) const +{ + return !(*this == other); +} + +static inline HashNumber +CombineHash(HashNumber h, HashNumber n) +{ + h += n; + h += (h << 10); + h ^= (h >> 6); + return h; +} + +static inline HashNumber +HashType(types::Type ty) +{ + if (ty.isObjectUnchecked()) + return PointerHasher::hash(ty.objectKey()); + return HashNumber(ty.raw()); +} + +static HashNumber +HashTypeList(const types::TypeSet::TypeList &types) +{ + HashNumber h = 0; + for (uint32_t i = 0; i < types.length(); i++) + h = CombineHash(h, HashType(types[i])); + return h; +} + +HashNumber +TrackedTypeInfo::hash() const +{ + return ((HashNumber(site_) << 24) + (HashNumber(mirType_) << 16)) ^ HashTypeList(types_); +} + +template +static HashNumber +HashVectorContents(const Vec *xs, HashNumber h) +{ + for (auto x = xs->begin(); x != xs->end(); x++) + h = CombineHash(h, x->hash()); + return h; +} + +/* static */ HashNumber +UniqueTrackedOptimizations::Key::hash(const Lookup &lookup) +{ + HashNumber h = HashVectorContents(lookup.types, 0); + h = HashVectorContents(lookup.attempts, h); + h += (h << 3); + h ^= (h >> 11); + h += (h << 15); + return h; +} + +/* static */ bool +UniqueTrackedOptimizations::Key::match(const Key &key, const Lookup &lookup) +{ + return VectorContentsMatch(key.attempts, lookup.attempts) && + VectorContentsMatch(key.types, lookup.types); +} + +bool +UniqueTrackedOptimizations::add(const TrackedOptimizations *optimizations) +{ + MOZ_ASSERT(!sorted()); + Key key; + key.types = &optimizations->types_; + key.attempts = &optimizations->attempts_; + AttemptsMap::AddPtr p = map_.lookupForAdd(key); + if (p) { + p->value().frequency++; + return true; + } + Entry entry; + entry.index = UINT8_MAX; + entry.frequency = 1; + return map_.add(p, key, entry); +} + +struct FrequencyComparator +{ + bool operator()(const UniqueTrackedOptimizations::SortEntry &a, + const UniqueTrackedOptimizations::SortEntry &b, + bool *lessOrEqualp) + { + *lessOrEqualp = b.frequency <= a.frequency; + return true; + } +}; + +bool +UniqueTrackedOptimizations::sortByFrequency(JSContext *cx) +{ + MOZ_ASSERT(!sorted()); + + JitSpew(JitSpew_OptimizationTracking, "=> Sorting unique optimizations by frequency"); + + // Sort by frequency. + Vector entries(cx); + for (AttemptsMap::Range r = map_.all(); !r.empty(); r.popFront()) { + SortEntry entry; + entry.types = r.front().key().types; + entry.attempts = r.front().key().attempts; + entry.frequency = r.front().value().frequency; + if (!entries.append(entry)) + return false; + } + + // The compact table stores indices as a max of uint8_t. In practice each + // script has fewer unique optimization attempts than UINT8_MAX. + if (entries.length() >= UINT8_MAX - 1) + return false; + + Vector scratch(cx); + if (!scratch.resize(entries.length())) + return false; + + FrequencyComparator comparator; + MOZ_ALWAYS_TRUE(MergeSort(entries.begin(), entries.length(), scratch.begin(), comparator)); + + // Update map entries' indices. + for (size_t i = 0; i < entries.length(); i++) { + Key key; + key.types = entries[i].types; + key.attempts = entries[i].attempts; + AttemptsMap::Ptr p = map_.lookup(key); + MOZ_ASSERT(p); + p->value().index = sorted_.length(); + + JitSpew(JitSpew_OptimizationTracking, " Entry %u has frequency %u", + sorted_.length(), p->value().frequency); + + if (!sorted_.append(entries[i])) + return false; + } + + return true; +} + +uint8_t +UniqueTrackedOptimizations::indexOf(const TrackedOptimizations *optimizations) const +{ + MOZ_ASSERT(sorted()); + Key key; + key.types = &optimizations->types_; + key.attempts = &optimizations->attempts_; + AttemptsMap::Ptr p = map_.lookup(key); + MOZ_ASSERT(p); + MOZ_ASSERT(p->value().index != UINT8_MAX); + return p->value().index; +} + +// Assigns each unique tracked type an index; outputs a compact list. +class jit::UniqueTrackedTypes +{ + public: + struct TypeHasher + { + typedef types::Type Lookup; + + static HashNumber hash(const Lookup &ty) { return HashType(ty); } + static bool match(const types::Type &ty1, const types::Type &ty2) { return ty1 == ty2; } + }; + + private: + // Map of unique types::Types to indices. + typedef HashMap TypesMap; + TypesMap map_; + + Vector list_; + + public: + explicit UniqueTrackedTypes(JSContext *cx) + : map_(cx), + list_(cx) + { } + + bool init() { return map_.init(); } + bool getIndexOf(types::Type ty, uint8_t *indexp); + + uint32_t count() const { MOZ_ASSERT(map_.count() == list_.length()); return list_.length(); } + bool enumerate(types::TypeSet::TypeList *types) const; +}; + +bool +UniqueTrackedTypes::getIndexOf(types::Type ty, uint8_t *indexp) +{ + TypesMap::AddPtr p = map_.lookupForAdd(ty); + if (p) { + *indexp = p->value(); + return true; + } + + // Store indices as max of uint8_t. In practice each script has fewer than + // UINT8_MAX of unique observed types. + if (count() >= UINT8_MAX) + return false; + + uint8_t index = (uint8_t) count(); + if (!map_.add(p, ty, index)) + return false; + if (!list_.append(ty)) + return false; + *indexp = index; + return true; +} + +bool +UniqueTrackedTypes::enumerate(types::TypeSet::TypeList *types) const +{ + return types->append(list_.begin(), list_.end()); +} + +void +IonTrackedOptimizationsRegion::unpackHeader() +{ + CompactBufferReader reader(start_, end_); + startOffset_ = reader.readUnsigned(); + endOffset_ = reader.readUnsigned(); + rangesStart_ = reader.currentPosition(); + MOZ_ASSERT(startOffset_ < endOffset_); +} + +void +IonTrackedOptimizationsRegion::RangeIterator::readNext(uint32_t *startOffset, uint32_t *endOffset, + uint8_t *index) +{ + MOZ_ASSERT(more()); + + CompactBufferReader reader(cur_, end_); + + // The very first entry isn't delta-encoded. + if (cur_ == start_) { + *startOffset = firstStartOffset_; + *endOffset = prevEndOffset_ = reader.readUnsigned(); + *index = reader.readByte(); + cur_ = reader.currentPosition(); + MOZ_ASSERT(cur_ <= end_); + return; + } + + // Otherwise, read a delta. + uint32_t startDelta, length; + ReadDelta(reader, &startDelta, &length, index); + *startOffset = prevEndOffset_ + startDelta; + *endOffset = prevEndOffset_ = *startOffset + length; + cur_ = reader.currentPosition(); + MOZ_ASSERT(cur_ <= end_); +} + +bool +JitcodeGlobalEntry::IonEntry::optimizationAttemptsAtAddr(void *ptr, + Maybe &attempts) +{ + MOZ_ASSERT(!attempts); + MOZ_ASSERT(containsPointer(ptr)); + uint32_t ptrOffset = ((uint8_t *) ptr) - ((uint8_t *) nativeStartAddr()); + Maybe region = optsRegionTable_->findRegion(ptrOffset); + if (region.isNothing()) + return true; + Maybe attemptsIdx = region->findAttemptsIndex(ptrOffset); + if (attemptsIdx.isNothing()) + return true; + IonTrackedOptimizationsAttempts attemptsEntry = optsAttemptsTable_->entry(*attemptsIdx); + attempts.emplace(); + if (!attemptsEntry.readVector(attempts.ptr())) + return false; + return true; +} + +Maybe +IonTrackedOptimizationsRegion::findAttemptsIndex(uint32_t offset) const +{ + if (offset < startOffset_ || offset >= endOffset_) + return Nothing(); + + // Linear search through the run. + RangeIterator iter = ranges(); + while (iter.more()) { + uint32_t startOffset, endOffset; + uint8_t index; + iter.readNext(&startOffset, &endOffset, &index); + if (startOffset <= offset && offset < endOffset) + return Some(index); + } + return Nothing(); +} + +Maybe +IonTrackedOptimizationsRegionTable::findRegion(uint32_t offset) const +{ + static const uint32_t LINEAR_SEARCH_THRESHOLD = 8; + uint32_t regions = numEntries(); + MOZ_ASSERT(regions > 0); + + // For small numbers of regions, do linear search. + if (regions <= LINEAR_SEARCH_THRESHOLD) { + for (uint32_t i = 0; i < regions; i++) { + IonTrackedOptimizationsRegion region = entry(i); + if (region.startOffset() <= offset && offset < region.endOffset()) { + return Some(entry(i)); + } + } + return Nothing(); + } + + // Otherwise, do binary search. + uint32_t i = 0; + while (regions > 1) { + uint32_t step = regions / 2; + uint32_t mid = i + step; + IonTrackedOptimizationsRegion region = entry(mid); + + if (offset < region.startOffset()) { + // Entry is below mid. + regions = step; + } else if (offset >= region.endOffset()) { + // Entry is above mid. + i = mid; + regions -= step; + } else { + // Entry is in mid. + return Some(entry(i)); + } + } + return Nothing(); +} + +/* static */ uint32_t +IonTrackedOptimizationsRegion::ExpectedRunLength(const NativeToTrackedOptimizations *start, + const NativeToTrackedOptimizations *end) +{ + MOZ_ASSERT(start < end); + + // A run always has at least 1 entry, which is not delta encoded. + uint32_t runLength = 1; + uint32_t prevEndOffset = start->endOffset.offset(); + + for (const NativeToTrackedOptimizations *entry = start + 1; entry != end; entry++) { + uint32_t startOffset = entry->startOffset.offset(); + uint32_t endOffset = entry->endOffset.offset(); + uint32_t startDelta = startOffset - prevEndOffset; + uint32_t length = endOffset - startOffset; + + if (!IsDeltaEncodeable(startDelta, length)) + break; + + runLength++; + if (runLength == MAX_RUN_LENGTH) + break; + + prevEndOffset = endOffset; + } + + return runLength; +} + +void +OptimizationAttempt::writeCompact(CompactBufferWriter &writer) const +{ + writer.writeUnsigned((uint32_t) strategy_); + writer.writeUnsigned((uint32_t) outcome_); +} + +bool +TrackedTypeInfo::writeCompact(CompactBufferWriter &writer, + UniqueTrackedTypes &uniqueTypes) const +{ + writer.writeUnsigned((uint32_t) site_); + writer.writeUnsigned((uint32_t) mirType_); + writer.writeUnsigned(types_.length()); + for (uint32_t i = 0; i < types_.length(); i++) { + uint8_t index; + if (!uniqueTypes.getIndexOf(types_[i], &index)) + return false; + writer.writeByte(index); + } + return true; +} + +/* static */ void +IonTrackedOptimizationsRegion::ReadDelta(CompactBufferReader &reader, + uint32_t *startDelta, uint32_t *length, + uint8_t *index) +{ + // 2 bytes + // SSSS-SSSL LLLL-LII0 + const uint32_t firstByte = reader.readByte(); + const uint32_t secondByte = reader.readByte(); + if ((firstByte & ENC1_MASK) == ENC1_MASK_VAL) { + uint32_t encVal = firstByte | secondByte << 8; + *startDelta = encVal >> ENC1_START_DELTA_SHIFT; + *length = (encVal >> ENC1_LENGTH_SHIFT) & ENC1_LENGTH_MAX; + *index = (encVal >> ENC1_INDEX_SHIFT) & ENC1_INDEX_MAX; + MOZ_ASSERT(length != 0); + return; + } + + // 3 bytes + // SSSS-SSSS SSSS-LLLL LLII-II01 + const uint32_t thirdByte = reader.readByte(); + if ((firstByte & ENC2_MASK) == ENC2_MASK_VAL) { + uint32_t encVal = firstByte | secondByte << 8 | thirdByte << 16; + *startDelta = encVal >> ENC2_START_DELTA_SHIFT; + *length = (encVal >> ENC2_LENGTH_SHIFT) & ENC2_LENGTH_MAX; + *index = (encVal >> ENC2_INDEX_SHIFT) & ENC2_INDEX_MAX; + MOZ_ASSERT(length != 0); + return; + } + + // 4 bytes + // SSSS-SSSS SSSL-LLLL LLLL-LIII IIII-I011 + const uint32_t fourthByte = reader.readByte(); + if ((firstByte & ENC3_MASK) == ENC3_MASK_VAL) { + uint32_t encVal = firstByte | secondByte << 8 | thirdByte << 16 | fourthByte << 24; + *startDelta = encVal >> ENC3_START_DELTA_SHIFT; + *length = (encVal >> ENC3_LENGTH_SHIFT) & ENC3_LENGTH_MAX; + *index = (encVal >> ENC3_INDEX_SHIFT) & ENC3_INDEX_MAX; + MOZ_ASSERT(length != 0); + return; + } + + // 5 bytes + // SSSS-SSSS SSSS-SSSL LLLL-LLLL LLLL-LIII IIII-I111 + MOZ_ASSERT((firstByte & ENC4_MASK) == ENC4_MASK_VAL); + uint64_t fifthByte = reader.readByte(); + uint64_t encVal = firstByte | secondByte << 8 | thirdByte << 16 | fourthByte << 24 | + fifthByte << 32; + *startDelta = encVal >> ENC4_START_DELTA_SHIFT; + *length = (encVal >> ENC4_LENGTH_SHIFT) & ENC4_LENGTH_MAX; + *index = (encVal >> ENC4_INDEX_SHIFT) & ENC4_INDEX_MAX; + MOZ_ASSERT(length != 0); +} + +/* static */ void +IonTrackedOptimizationsRegion::WriteDelta(CompactBufferWriter &writer, + uint32_t startDelta, uint32_t length, + uint8_t index) +{ + // 2 bytes + // SSSS-SSSL LLLL-LII0 + if (startDelta <= ENC1_START_DELTA_MAX && + length <= ENC1_LENGTH_MAX && + index <= ENC1_INDEX_MAX) + { + uint16_t val = ENC1_MASK_VAL | + (startDelta << ENC1_START_DELTA_SHIFT) | + (length << ENC1_LENGTH_SHIFT) | + (index << ENC1_INDEX_SHIFT); + writer.writeByte(val & 0xff); + writer.writeByte((val >> 8) & 0xff); + return; + } + + // 3 bytes + // SSSS-SSSS SSSS-LLLL LLII-II01 + if (startDelta <= ENC2_START_DELTA_MAX && + length <= ENC2_LENGTH_MAX && + index <= ENC2_INDEX_MAX) + { + uint32_t val = ENC2_MASK_VAL | + (startDelta << ENC2_START_DELTA_SHIFT) | + (length << ENC2_LENGTH_SHIFT) | + (index << ENC2_INDEX_SHIFT); + writer.writeByte(val & 0xff); + writer.writeByte((val >> 8) & 0xff); + writer.writeByte((val >> 16) & 0xff); + return; + } + + // 4 bytes + // SSSS-SSSS SSSL-LLLL LLLL-LIII IIII-I011 + if (startDelta <= ENC3_START_DELTA_MAX && + length <= ENC3_LENGTH_MAX) + { + // index always fits because it's an uint8_t; change this if + // ENC3_INDEX_MAX changes. + MOZ_ASSERT(ENC3_INDEX_MAX == UINT8_MAX); + uint32_t val = ENC3_MASK_VAL | + (startDelta << ENC3_START_DELTA_SHIFT) | + (length << ENC3_LENGTH_SHIFT) | + (index << ENC3_INDEX_SHIFT); + writer.writeByte(val & 0xff); + writer.writeByte((val >> 8) & 0xff); + writer.writeByte((val >> 16) & 0xff); + writer.writeByte((val >> 24) & 0xff); + return; + } + + // 5 bytes + // SSSS-SSSS SSSS-SSSL LLLL-LLLL LLLL-LIII IIII-I111 + if (startDelta <= ENC4_START_DELTA_MAX && + length <= ENC4_LENGTH_MAX) + { + // index always fits because it's an uint8_t; change this if + // ENC4_INDEX_MAX changes. + MOZ_ASSERT(ENC4_INDEX_MAX == UINT8_MAX); + uint64_t val = ENC4_MASK_VAL | + (((uint64_t) startDelta) << ENC4_START_DELTA_SHIFT) | + (((uint64_t) length) << ENC4_LENGTH_SHIFT) | + (((uint64_t) index) << ENC4_INDEX_SHIFT); + writer.writeByte(val & 0xff); + writer.writeByte((val >> 8) & 0xff); + writer.writeByte((val >> 16) & 0xff); + writer.writeByte((val >> 24) & 0xff); + writer.writeByte((val >> 32) & 0xff); + return; + } + + MOZ_CRASH("startDelta,length,index triple too large to encode."); +} + +/* static */ bool +IonTrackedOptimizationsRegion::WriteRun(CompactBufferWriter &writer, + const NativeToTrackedOptimizations *start, + const NativeToTrackedOptimizations *end, + const UniqueTrackedOptimizations &unique) +{ + // Write the header, which is the range that this whole run encompasses. + JitSpew(JitSpew_OptimizationTracking, " Header: [%u, %u)", + start->startOffset.offset(), (end - 1)->endOffset.offset()); + writer.writeUnsigned(start->startOffset.offset()); + writer.writeUnsigned((end - 1)->endOffset.offset()); + + // Write the first entry of the run, which is not delta-encoded. + JitSpew(JitSpew_OptimizationTracking, + " [%6u, %6u) vector %3u, offset %4u", + start->startOffset.offset(), start->endOffset.offset(), + unique.indexOf(start->optimizations), writer.length()); + uint32_t prevEndOffset = start->endOffset.offset(); + writer.writeUnsigned(prevEndOffset); + writer.writeByte(unique.indexOf(start->optimizations)); + + // Delta encode the run. + for (const NativeToTrackedOptimizations *entry = start + 1; entry != end; entry++) { + uint32_t startOffset = entry->startOffset.offset(); + uint32_t endOffset = entry->endOffset.offset(); + + uint32_t startDelta = startOffset - prevEndOffset; + uint32_t length = endOffset - startOffset; + uint8_t index = unique.indexOf(entry->optimizations); + + JitSpew(JitSpew_OptimizationTracking, + " [%6u, %6u) delta [+%5u, +%5u) vector %3u, offset %4u", + startOffset, endOffset, startDelta, length, index, writer.length()); + + WriteDelta(writer, startDelta, length, index); + + prevEndOffset = endOffset; + } + + if (writer.oom()) + return false; + + return true; +} + +TrackedTypeInfo::TrackedTypeInfo(CompactBufferReader &reader) + : site_(TrackedTypeSite(reader.readUnsigned())), + mirType_(MIRType(reader.readUnsigned())) +{ + MOZ_ASSERT(site_ < TrackedTypeSite::Count); +} + +bool +TrackedTypeInfo::readTypes(CompactBufferReader &reader, const types::TypeSet::TypeList *allTypes) +{ + uint32_t length = reader.readUnsigned(); + for (uint32_t i = 0; i < length; i++) { + if (!types_.append((*allTypes)[reader.readByte()])) + return false; + } + return true; +} + +OptimizationAttempt::OptimizationAttempt(CompactBufferReader &reader) + : strategy_(TrackedStrategy(reader.readUnsigned())), + outcome_(TrackedOutcome(reader.readUnsigned())) +{ + MOZ_ASSERT(strategy_ < TrackedStrategy::Count); + MOZ_ASSERT(outcome_ < TrackedOutcome::Count); +} + +static bool +WriteOffsetsTable(CompactBufferWriter &writer, const Vector &offsets, + uint32_t *tableOffsetp) +{ + // 4-byte align for the uint32s. + uint32_t padding = sizeof(uint32_t) - (writer.length() % sizeof(uint32_t)); + if (padding == sizeof(uint32_t)) + padding = 0; + JitSpew(JitSpew_OptimizationTracking, " Padding %u byte%s", + padding, padding == 1 ? "" : "s"); + for (uint32_t i = 0; i < padding; i++) + writer.writeByte(0); + + // Record the start of the table to compute reverse offsets for entries. + uint32_t tableOffset = writer.length(); + + // Write how many bytes were padded and numEntries. + writer.writeNativeEndianUint32_t(padding); + writer.writeNativeEndianUint32_t(offsets.length()); + + // Write entry offset table. + for (size_t i = 0; i < offsets.length(); i++) { + JitSpew(JitSpew_OptimizationTracking, " Entry %u reverse offset %u", + i, tableOffset - padding - offsets[i]); + writer.writeNativeEndianUint32_t(tableOffset - padding - offsets[i]); + } + + if (writer.oom()) + return false; + + *tableOffsetp = tableOffset; + return true; +} + +bool +jit::WriteIonTrackedOptimizationsTable(JSContext *cx, CompactBufferWriter &writer, + const NativeToTrackedOptimizations *start, + const NativeToTrackedOptimizations *end, + const UniqueTrackedOptimizations &unique, + uint32_t *numRegions, + uint32_t *regionTableOffsetp, + uint32_t *typesTableOffsetp, + uint32_t *optimizationTableOffsetp, + types::TypeSet::TypeList *allTypes) +{ + MOZ_ASSERT(unique.sorted()); + +#ifdef DEBUG + // Spew training data, which may be fed into a script to determine a good + // encoding strategy. + if (JitSpewEnabled(JitSpew_OptimizationTracking)) { + JitSpewStart(JitSpew_OptimizationTracking, "=> Training data: "); + for (const NativeToTrackedOptimizations *entry = start; entry != end; entry++) { + JitSpewCont(JitSpew_OptimizationTracking, "%u,%u,%u ", + entry->startOffset.offset(), entry->endOffset.offset(), + unique.indexOf(entry->optimizations)); + } + JitSpewFin(JitSpew_OptimizationTracking); + } +#endif + + Vector offsets(cx); + const NativeToTrackedOptimizations *entry = start; + + // Write out region offloads, partitioned into runs. + JitSpew(JitSpew_Profiling, "=> Writing regions"); + while (entry != end) { + uint32_t runLength = IonTrackedOptimizationsRegion::ExpectedRunLength(entry, end); + JitSpew(JitSpew_OptimizationTracking, " Run at entry %u, length %u, offset %u", + entry - start, runLength, writer.length()); + + if (!offsets.append(writer.length())) + return false; + + if (!IonTrackedOptimizationsRegion::WriteRun(writer, entry, entry + runLength, unique)) + return false; + + entry += runLength; + } + + // Write out the table indexing into the payloads. 4-byte align for the uint32s. + if (!WriteOffsetsTable(writer, offsets, regionTableOffsetp)) + return false; + + *numRegions = offsets.length(); + + // Clear offsets so that it may be reused below for the unique + // optimizations table. + offsets.clear(); + + const UniqueTrackedOptimizations::SortedVector &vec = unique.sortedVector(); + JitSpew(JitSpew_OptimizationTracking, "=> Writing unique optimizations table with %u entr%s", + vec.length(), vec.length() == 1 ? "y" : "ies"); + + // Write out type info payloads. + UniqueTrackedTypes uniqueTypes(cx); + if (!uniqueTypes.init()) + return false; + + for (const UniqueTrackedOptimizations::SortEntry *p = vec.begin(); p != vec.end(); p++) { + const TempTrackedTypeInfoVector *v = p->types; + JitSpew(JitSpew_OptimizationTracking, " Type info entry %u of length %u, offset %u", + p - vec.begin(), v->length(), writer.length()); + SpewTempTrackedTypeInfoVector(v, " "); + + if (!offsets.append(writer.length())) + return false; + + for (const TrackedTypeInfo *t = v->begin(); t != v->end(); t++) { + if (!t->writeCompact(writer, uniqueTypes)) + return false; + } + } + + // Copy the unique type list into the outparam TypeList. + if (!uniqueTypes.enumerate(allTypes)) + return false; + + if (!WriteOffsetsTable(writer, offsets, typesTableOffsetp)) + return false; + offsets.clear(); + + // Write out attempts payloads. + for (const UniqueTrackedOptimizations::SortEntry *p = vec.begin(); p != vec.end(); p++) { + const TempAttemptsVector *v = p->attempts; + JitSpew(JitSpew_OptimizationTracking, " Attempts entry %u of length %u, offset %u", + p - vec.begin(), v->length(), writer.length()); + SpewTempAttemptsVector(v, " "); + + if (!offsets.append(writer.length())) + return false; + + for (const OptimizationAttempt *a = v->begin(); a != v->end(); a++) + a->writeCompact(writer); + } + + return WriteOffsetsTable(writer, offsets, optimizationTableOffsetp); +} + + +BytecodeSite * +IonBuilder::maybeTrackedOptimizationSite(jsbytecode *pc) +{ + // BytecodeSites that track optimizations need to be 1-1 with the pc + // when optimization tracking is enabled, so that all MIR generated by + // a single pc are tracked at one place, even across basic blocks. + // + // Alternatively, we could make all BytecodeSites 1-1 with the pc, but + // there is no real need as optimization tracking is a toggled + // feature. + // + // Since sites that track optimizations should be sparse, just do a + // reverse linear search, as we're most likely advancing in pc. + MOZ_ASSERT(isOptimizationTrackingEnabled()); + for (size_t i = trackedOptimizationSites_.length(); i != 0; i--) { + BytecodeSite *site = trackedOptimizationSites_[i - 1]; + if (site->pc() == pc) { + MOZ_ASSERT(site->tree() == info().inlineScriptTree()); + return site; + } + } + return nullptr; +} + +void +IonBuilder::startTrackingOptimizations() +{ + if (isOptimizationTrackingEnabled()) { + BytecodeSite *site = maybeTrackedOptimizationSite(current->trackedSite()->pc()); + + if (!site) { + site = current->trackedSite(); + site->setOptimizations(new(alloc()) TrackedOptimizations(alloc())); + // OOMs are handled as if optimization tracking were turned off. + if (!trackedOptimizationSites_.append(site)) + site = nullptr; + } + + if (site) + current->updateTrackedSite(site); + } +} + +void +IonBuilder::trackTypeInfoUnchecked(TrackedTypeSite kind, MIRType mirType, + types::TemporaryTypeSet *typeSet) +{ + BytecodeSite *site = current->trackedSite(); + // OOMs are handled as if optimization tracking were turned off. + TrackedTypeInfo typeInfo(kind, mirType); + if (!typeInfo.trackTypeSet(typeSet)) { + site->setOptimizations(nullptr); + return; + } + if (!site->optimizations()->trackTypeInfo(mozilla::Move(typeInfo))) + site->setOptimizations(nullptr); +} + +void +IonBuilder::trackTypeInfoUnchecked(TrackedTypeSite kind, JSObject *obj) +{ + BytecodeSite *site = current->trackedSite(); + // OOMs are handled as if optimization tracking were turned off. + TrackedTypeInfo typeInfo(kind, MIRType_Object); + if (!typeInfo.trackType(types::Type::ObjectType(obj))) + return; + if (!site->optimizations()->trackTypeInfo(mozilla::Move(typeInfo))) + site->setOptimizations(nullptr); +} + +void +IonBuilder::trackTypeInfoUnchecked(CallInfo &callInfo) +{ + MDefinition *thisArg = callInfo.thisArg(); + trackTypeInfoUnchecked(TrackedTypeSite::Call_This, thisArg->type(), thisArg->resultTypeSet()); + + for (uint32_t i = 0; i < callInfo.argc(); i++) { + MDefinition *arg = callInfo.getArg(i); + trackTypeInfoUnchecked(TrackedTypeSite::Call_Arg, arg->type(), arg->resultTypeSet()); + } + + types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet(); + trackTypeInfoUnchecked(TrackedTypeSite::Call_Return, returnTypes->getKnownMIRType(), + returnTypes); +} + +void +IonBuilder::trackOptimizationAttemptUnchecked(TrackedStrategy strategy) +{ + BytecodeSite *site = current->trackedSite(); + // OOMs are handled as if optimization tracking were turned off. + if (!site->optimizations()->trackAttempt(strategy)) + site->setOptimizations(nullptr); +} + +void +IonBuilder::amendOptimizationAttemptUnchecked(uint32_t index) +{ + const BytecodeSite *site = current->trackedSite(); + site->optimizations()->amendAttempt(index); +} + +void +IonBuilder::trackOptimizationOutcomeUnchecked(TrackedOutcome outcome) +{ + const BytecodeSite *site = current->trackedSite(); + site->optimizations()->trackOutcome(outcome); +} + +void +IonBuilder::trackOptimizationSuccessUnchecked() +{ + const BytecodeSite *site = current->trackedSite(); + site->optimizations()->trackSuccess(); +} + +void +IonBuilder::trackInlineSuccessUnchecked(InliningStatus status) +{ + if (status == InliningStatus_Inlined) + trackOptimizationOutcome(TrackedOutcome::Inlined); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/OptimizationTracking.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/OptimizationTracking.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/OptimizationTracking.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/OptimizationTracking.h 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,776 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 jit_OptimizationTracking_h +#define jit_OptimizationTracking_h + +#include "mozilla/Maybe.h" + +#include "jsinfer.h" +#include "jit/CompactBuffer.h" +#include "jit/CompileInfo.h" +#include "jit/JitAllocPolicy.h" +#include "jit/shared/CodeGenerator-shared.h" + +namespace js { + +namespace jit { + +#define TRACKED_STRATEGY_LIST(_) \ + _(GetProp_ArgumentsLength, \ + "getprop arguments.length") \ + _(GetProp_ArgumentsCallee, \ + "getprop arguments.callee") \ + _(GetProp_InferredConstant, \ + "getprop inferred constant") \ + _(GetProp_Constant, \ + "getprop constant") \ + _(GetProp_TypedObject, \ + "getprop TypedObject") \ + _(GetProp_DefiniteSlot, \ + "getprop definite slot") \ + _(GetProp_Unboxed, \ + "getprop unboxed object") \ + _(GetProp_CommonGetter, \ + "getprop common getter") \ + _(GetProp_InlineAccess, \ + "getprop inline access") \ + _(GetProp_Innerize, \ + "getprop innerize (access on global window)") \ + _(GetProp_InlineCache, \ + "getprop IC") \ + \ + _(SetProp_CommonSetter, \ + "setprop common setter") \ + _(SetProp_TypedObject, \ + "setprop TypedObject") \ + _(SetProp_DefiniteSlot, \ + "setprop definite slot") \ + _(SetProp_Unboxed, \ + "setprop unboxed object") \ + _(SetProp_InlineAccess, \ + "setprop inline access") \ + \ + _(GetElem_TypedObject, \ + "getprop TypedObject") \ + _(GetElem_Dense, \ + "getelem dense") \ + _(GetElem_TypedStatic, \ + "getelem TypedArray static") \ + _(GetElem_TypedArray, \ + "getelem TypedArray") \ + _(GetElem_String, \ + "getelem string") \ + _(GetElem_Arguments, \ + "getelem arguments") \ + _(GetElem_ArgumentsInlined, \ + "getelem arguments inlined") \ + _(GetElem_InlineCache, \ + "getelem IC") \ + \ + _(SetElem_TypedObject, \ + "setelem TypedObject") \ + _(SetElem_TypedStatic, \ + "setelem TypedArray static") \ + _(SetElem_TypedArray, \ + "setelem TypedArray") \ + _(SetElem_Dense, \ + "setelem dense") \ + _(SetElem_Arguments, \ + "setelem arguments") \ + _(SetElem_InlineCache, \ + "setelem IC") \ + \ + _(Call_Inline, \ + "call inline") + + +// Ordering is important below. All outcomes before GenericSuccess will be +// considered failures, and all outcomes after GenericSuccess will be +// considered successes. +#define TRACKED_OUTCOME_LIST(_) \ + _(GenericFailure, \ + "failure") \ + _(Disabled, \ + "disabled") \ + _(NoTypeInfo, \ + "no type info") \ + _(NoAnalysisInfo, \ + "no newscript analysis") \ + _(NoShapeInfo, \ + "cannot determine shape") \ + _(UnknownObject, \ + "unknown object") \ + _(UnknownProperties, \ + "unknown properties") \ + _(Singleton, \ + "is singleton") \ + _(NotSingleton, \ + "is not singleton") \ + _(NotFixedSlot, \ + "property not in fixed slot") \ + _(InconsistentFixedSlot, \ + "property not in a consistent fixed slot") \ + _(NotObject, \ + "not definitely an object") \ + _(NotStruct, \ + "not definitely a TypedObject struct") \ + _(NotUnboxed, \ + "not definitely an unboxed object") \ + _(StructNoField, \ + "struct doesn't definitely have field") \ + _(InconsistentFieldType, \ + "unboxed property does not consistent type") \ + _(InconsistentFieldOffset, \ + "unboxed property does not consistent offset") \ + _(NeedsTypeBarrier, \ + "needs type barrier") \ + _(InDictionaryMode, \ + "object in dictionary mode") \ + _(NoProtoFound, \ + "no proto found") \ + _(MultiProtoPaths, \ + "not all paths to property go through same proto") \ + _(NonWritableProperty, \ + "non-writable property") \ + _(ProtoIndexedProps, \ + "prototype has indexed properties") \ + _(ArrayBadFlags, \ + "array observed to be sparse, overflowed .length, or has been iterated") \ + _(ArrayDoubleConversion, \ + "array has ambiguous double conversion") \ + _(ArrayRange, \ + "array range issue (.length problems)") \ + _(ArraySeenNegativeIndex, \ + "has seen array access with negative index") \ + _(TypedObjectNeutered, \ + "TypedObject might have been neutered") \ + _(TypedObjectArrayRange, \ + "TypedObject array of unknown length") \ + _(AccessNotDense, \ + "access not on dense native (check receiver, index, and result types)") \ + _(AccessNotTypedObject, \ + "access not on typed array (check receiver and index types)") \ + _(AccessNotTypedArray, \ + "access not on typed array (check receiver, index, and result types)") \ + _(AccessNotString, \ + "getelem not on string (check receiver and index types)") \ + _(StaticTypedArrayUint32, \ + "static uint32 arrays currently cannot be optimized") \ + _(StaticTypedArrayCantComputeMask, \ + "can't compute mask for static typed array access (index isn't constant or not int32)") \ + _(OutOfBounds, \ + "observed out of bounds access") \ + _(GetElemStringNotCached, \ + "getelem on strings is not inline cached") \ + _(NonNativeReceiver, \ + "observed non-native receiver") \ + _(IndexType, \ + "index type must be int32, string, or symbol") \ + _(SetElemNonDenseNonTANotCached, \ + "setelem on non-dense non-TAs are not inline cached") \ + \ + _(CantInlineGeneric, \ + "can't inline") \ + _(CantInlineNoTarget, \ + "can't inline: no target") \ + _(CantInlineNotInterpreted, \ + "can't inline: not interpreted") \ + _(CantInlineNoBaseline, \ + "can't inline: no baseline code") \ + _(CantInlineLazy, \ + "can't inline: lazy script") \ + _(CantInlineNotConstructor, \ + "can't inline: calling non-constructor with 'new'") \ + _(CantInlineDisabledIon, \ + "can't inline: ion disabled for callee") \ + _(CantInlineTooManyArgs, \ + "can't inline: too many arguments") \ + _(CantInlineRecursive, \ + "can't inline: recursive") \ + _(CantInlineHeavyweight, \ + "can't inline: heavyweight") \ + _(CantInlineNeedsArgsObj, \ + "can't inline: needs arguments object") \ + _(CantInlineDebuggee, \ + "can't inline: debuggee") \ + _(CantInlineUnknownProps, \ + "can't inline: type has unknown properties") \ + _(CantInlineExceededDepth, \ + "can't inline: exceeded inlining depth") \ + _(CantInlineBigLoop, \ + "can't inline: big function with a loop") \ + _(CantInlineBigCaller, \ + "can't inline: big caller") \ + _(CantInlineBigCallee, \ + "can't inline: big callee") \ + _(CantInlineNotHot, \ + "can't inline: not hot enough") \ + _(CantInlineNotInDispatch, \ + "can't inline: not in dispatch table") \ + _(CantInlineNativeBadForm, \ + "can't inline native: bad form (arity mismatch/constructing)") \ + _(CantInlineNativeBadType, \ + "can't inline native: bad argument or return type observed") \ + _(CantInlineNativeNoTemplateObj, \ + "can't inline native: no template object") \ + _(CantInlineBound, \ + "can't inline bound function invocation") \ + \ + _(GenericSuccess, \ + "success") \ + _(Inlined, \ + "inlined") \ + _(DOM, \ + "DOM") \ + _(Monomorphic, \ + "monomorphic") \ + _(Polymorphic, \ + "polymorphic") + +#define TRACKED_TYPESITE_LIST(_) \ + _(Receiver, \ + "receiver object") \ + _(Index, \ + "index") \ + _(Value, \ + "value") \ + _(Call_Target, \ + "call target") \ + _(Call_This, \ + "call 'this'") \ + _(Call_Arg, \ + "call argument") \ + _(Call_Return, \ + "call return") + +enum class TrackedStrategy : uint32_t { +#define STRATEGY_OP(name, msg) name, + TRACKED_STRATEGY_LIST(STRATEGY_OP) +#undef STRATEGY_OPT + + Count +}; + +enum class TrackedOutcome : uint32_t { +#define OUTCOME_OP(name, msg) name, + TRACKED_OUTCOME_LIST(OUTCOME_OP) +#undef OUTCOME_OP + + Count +}; + +enum class TrackedTypeSite : uint32_t { +#define TYPESITE_OP(name, msg) name, + TRACKED_TYPESITE_LIST(TYPESITE_OP) +#undef TYPESITE_OP + + Count +}; + +class OptimizationAttempt +{ + TrackedStrategy strategy_; + TrackedOutcome outcome_; + + public: + OptimizationAttempt(TrackedStrategy strategy, TrackedOutcome outcome) + : strategy_(strategy), + outcome_(outcome) + { } + + void setOutcome(TrackedOutcome outcome) { outcome_ = outcome; } + bool succeeded() const { return outcome_ >= TrackedOutcome::GenericSuccess; } + bool failed() const { return outcome_ < TrackedOutcome::GenericSuccess; } + TrackedStrategy strategy() const { return strategy_; } + TrackedOutcome outcome() const { return outcome_; } + + bool operator ==(const OptimizationAttempt &other) const { + return strategy_ == other.strategy_ && outcome_ == other.outcome_; + } + bool operator !=(const OptimizationAttempt &other) const { + return strategy_ != other.strategy_ || outcome_ != other.outcome_; + } + HashNumber hash() const { + return (HashNumber(strategy_) << 8) + HashNumber(outcome_); + } + + explicit OptimizationAttempt(CompactBufferReader &reader); + void writeCompact(CompactBufferWriter &writer) const; +}; + +typedef Vector TempAttemptsVector; +typedef Vector AttemptsVector; + +class UniqueTrackedTypes; + +class TrackedTypeInfo +{ + TrackedTypeSite site_; + MIRType mirType_; + types::TypeSet::TypeList types_; + + public: + TrackedTypeInfo(TrackedTypeInfo &&other) + : site_(other.site_), + mirType_(other.mirType_), + types_(mozilla::Move(other.types_)) + { } + + TrackedTypeInfo(TrackedTypeSite site, MIRType mirType) + : site_(site), + mirType_(mirType) + { } + + bool trackTypeSet(types::TemporaryTypeSet *typeSet); + bool trackType(types::Type type); + + TrackedTypeSite site() const { return site_; } + MIRType mirType() const { return mirType_; } + const types::TypeSet::TypeList &types() const { return types_; } + + bool operator ==(const TrackedTypeInfo &other) const; + bool operator !=(const TrackedTypeInfo &other) const; + + HashNumber hash() const; + + // This constructor is designed to be used in conjunction with readTypes + // below it. The same reader must be passed to readTypes after + // instantiating the TrackedTypeInfo. + explicit TrackedTypeInfo(CompactBufferReader &reader); + bool readTypes(CompactBufferReader &reader, const types::TypeSet::TypeList *allTypes); + bool writeCompact(CompactBufferWriter &writer, UniqueTrackedTypes &uniqueTypes) const; +}; + +typedef Vector TempTrackedTypeInfoVector; +typedef Vector TrackedTypeInfoVector; + +// Tracks the optimization attempts made at a bytecode location. +class TrackedOptimizations : public TempObject +{ + friend class UniqueTrackedOptimizations; + TempTrackedTypeInfoVector types_; + TempAttemptsVector attempts_; + uint32_t currentAttempt_; + + public: + explicit TrackedOptimizations(TempAllocator &alloc) + : types_(alloc), + attempts_(alloc), + currentAttempt_(UINT32_MAX) + { } + + bool trackTypeInfo(TrackedTypeInfo &&ty); + + bool trackAttempt(TrackedStrategy strategy); + void amendAttempt(uint32_t index); + void trackOutcome(TrackedOutcome outcome); + void trackSuccess(); + + bool matchTypes(const TempTrackedTypeInfoVector &other) const; + bool matchAttempts(const TempAttemptsVector &other) const; + + void spew() const; +}; + +// Assigns each unique sequence of optimization attempts an index; outputs a +// compact table. +class UniqueTrackedOptimizations +{ + public: + struct SortEntry + { + const TempTrackedTypeInfoVector *types; + const TempAttemptsVector *attempts; + uint32_t frequency; + }; + typedef Vector SortedVector; + + private: + struct Key + { + const TempTrackedTypeInfoVector *types; + const TempAttemptsVector *attempts; + + typedef Key Lookup; + static HashNumber hash(const Lookup &lookup); + static bool match(const Key &key, const Lookup &lookup); + static void rekey(Key &key, const Key &newKey) { + key = newKey; + } + }; + + struct Entry + { + uint8_t index; + uint32_t frequency; + }; + + // Map of unique (TempTrackedTypeInfoVector, TempAttemptsVector) pairs to + // indices. + typedef HashMap AttemptsMap; + AttemptsMap map_; + + // TempAttemptsVectors sorted by frequency. + SortedVector sorted_; + + public: + explicit UniqueTrackedOptimizations(JSContext *cx) + : map_(cx), + sorted_(cx) + { } + + bool init() { return map_.init(); } + bool add(const TrackedOptimizations *optimizations); + + bool sortByFrequency(JSContext *cx); + bool sorted() const { return !sorted_.empty(); } + uint32_t count() const { MOZ_ASSERT(sorted()); return sorted_.length(); } + const SortedVector &sortedVector() const { MOZ_ASSERT(sorted()); return sorted_; } + uint8_t indexOf(const TrackedOptimizations *optimizations) const; +}; + +// A compact table of tracked optimization information. Pictorially, +// +// +------------------------------------------------+ +// | Region 1 | | +// |------------------------------------------------| | +// | Region 2 | | +// |------------------------------------------------| |-- PayloadR of list-of-list of +// | ... | | range triples (see below) +// |------------------------------------------------| | +// | Region M | | +// +================================================+ <- IonTrackedOptimizationsRegionTable +// | uint32_t numRegions_ = M | | +// +------------------------------------------------+ | +// | Region 1 | | +// | uint32_t regionOffset = size(PayloadR) | | +// +------------------------------------------------+ |-- Table +// | ... | | +// +------------------------------------------------+ | +// | Region M | | +// | uint32_t regionOffset | | +// +================================================+ +// | Optimization type info 1 | | +// |------------------------------------------------| | +// | Optimization type info 2 | |-- PayloadT of list of +// |------------------------------------------------| | IonTrackedOptimizationTypeInfo in +// | ... | | order of decreasing frequency +// |------------------------------------------------| | +// | Optimization type info N | | +// +================================================+ <- IonTrackedOptimizationsTypesTable +// | uint32_t numEntries_ = N | | +// +------------------------------------------------+ | +// | Optimization type info 1 | | +// | uint32_t entryOffset = size(PayloadT) | | +// +------------------------------------------------+ |-- Table +// | ... | | +// +------------------------------------------------+ | +// | Optimization type info N | | +// | uint32_t entryOffset | | +// +================================================+ +// | Optimization attempts 1 | | +// |------------------------------------------------| | +// | Optimization attempts 2 | |-- PayloadA of list of +// |------------------------------------------------| | IonTrackedOptimizationAttempts in +// | ... | | order of decreasing frequency +// |------------------------------------------------| | +// | Optimization attempts N | | +// +================================================+ <- IonTrackedOptimizationsAttemptsTable +// | uint32_t numEntries_ = N | | +// +------------------------------------------------+ | +// | Optimization attempts 1 | | +// | uint32_t entryOffset = size(PayloadA) | | +// +------------------------------------------------+ |-- Table +// | ... | | +// +------------------------------------------------+ | +// | Optimization attempts N | | +// | uint32_t entryOffset | | +// +------------------------------------------------+ +// +// Abstractly, each region in the PayloadR section is a list of triples of the +// following, in order of ascending startOffset: +// +// (startOffset, endOffset, optimization attempts index) +// +// The range of [startOffset, endOffset) is the native machine code offsets +// for which the optimization attempts referred to by the index applies. +// +// Concretely, each region starts with a header of: +// +// { startOffset : 32, endOffset : 32 } +// +// followed by an (endOffset, index) pair, then by delta-encoded variants +// triples described below. +// +// Each list of type infos in the PayloadT section is a list of triples: +// +// (kind, MIR type, type set) +// +// The type set is separately in another vector, and what is encoded instead +// is the (offset, length) pair needed to index into that vector. +// +// Each list of optimization attempts in the PayloadA section is a list of +// pairs: +// +// (strategy, outcome) +// +// Both tail tables for PayloadR and PayloadA use reverse offsets from the +// table pointers. + +class IonTrackedOptimizationsRegion +{ + const uint8_t *start_; + const uint8_t *end_; + + // Unpacked state. + uint32_t startOffset_; + uint32_t endOffset_; + const uint8_t *rangesStart_; + + void unpackHeader(); + + public: + IonTrackedOptimizationsRegion(const uint8_t *start, const uint8_t *end) + : start_(start), end_(end), + startOffset_(0), endOffset_(0), rangesStart_(nullptr) + { + MOZ_ASSERT(start < end); + unpackHeader(); + } + + // Offsets for the entire range that this region covers. + // + // This, as well as the offsets for the deltas, is open at the ending + // address: [startOffset, endOffset). + uint32_t startOffset() const { return startOffset_; } + uint32_t endOffset() const { return endOffset_; } + + class RangeIterator { + const uint8_t *cur_; + const uint8_t *start_; + const uint8_t *end_; + + uint32_t firstStartOffset_; + uint32_t prevEndOffset_; + + public: + RangeIterator(const uint8_t *start, const uint8_t *end, uint32_t startOffset) + : cur_(start), start_(start), end_(end), + firstStartOffset_(startOffset), prevEndOffset_(0) + { } + + bool more() const { return cur_ < end_; } + void readNext(uint32_t *startOffset, uint32_t *endOffset, uint8_t *index); + }; + + RangeIterator ranges() const { return RangeIterator(rangesStart_, end_, startOffset_); } + + mozilla::Maybe findAttemptsIndex(uint32_t offset) const; + + // For the variants below, S stands for startDelta, L for length, and I + // for index. These were automatically generated from training on the + // Octane benchmark. + // + // byte 1 byte 0 + // SSSS-SSSL LLLL-LII0 + // startDelta max 127, length max 63, index max 3 + + static const uint32_t ENC1_MASK = 0x1; + static const uint32_t ENC1_MASK_VAL = 0x0; + + static const uint32_t ENC1_START_DELTA_MAX = 0x7f; + static const uint32_t ENC1_START_DELTA_SHIFT = 9; + + static const uint32_t ENC1_LENGTH_MAX = 0x3f; + static const uint32_t ENC1_LENGTH_SHIFT = 3; + + static const uint32_t ENC1_INDEX_MAX = 0x3; + static const uint32_t ENC1_INDEX_SHIFT = 1; + + // byte 2 byte 1 byte 0 + // SSSS-SSSS SSSS-LLLL LLII-II01 + // startDelta max 4095, length max 63, index max 15 + + static const uint32_t ENC2_MASK = 0x3; + static const uint32_t ENC2_MASK_VAL = 0x1; + + static const uint32_t ENC2_START_DELTA_MAX = 0xfff; + static const uint32_t ENC2_START_DELTA_SHIFT = 12; + + static const uint32_t ENC2_LENGTH_MAX = 0x3f; + static const uint32_t ENC2_LENGTH_SHIFT = 6; + + static const uint32_t ENC2_INDEX_MAX = 0xf; + static const uint32_t ENC2_INDEX_SHIFT = 2; + + // byte 3 byte 2 byte 1 byte 0 + // SSSS-SSSS SSSL-LLLL LLLL-LIII IIII-I011 + // startDelta max 2047, length max 1023, index max 255 + + static const uint32_t ENC3_MASK = 0x7; + static const uint32_t ENC3_MASK_VAL = 0x3; + + static const uint32_t ENC3_START_DELTA_MAX = 0x7ff; + static const uint32_t ENC3_START_DELTA_SHIFT = 21; + + static const uint32_t ENC3_LENGTH_MAX = 0x3ff; + static const uint32_t ENC3_LENGTH_SHIFT = 11; + + static const uint32_t ENC3_INDEX_MAX = 0xff; + static const uint32_t ENC3_INDEX_SHIFT = 3; + + // byte 4 byte 3 byte 2 byte 1 byte 0 + // SSSS-SSSS SSSS-SSSL LLLL-LLLL LLLL-LIII IIII-I111 + // startDelta max 32767, length max 16383, index max 255 + + static const uint32_t ENC4_MASK = 0x7; + static const uint32_t ENC4_MASK_VAL = 0x7; + + static const uint32_t ENC4_START_DELTA_MAX = 0x7fff; + static const uint32_t ENC4_START_DELTA_SHIFT = 25; + + static const uint32_t ENC4_LENGTH_MAX = 0x3fff; + static const uint32_t ENC4_LENGTH_SHIFT = 11; + + static const uint32_t ENC4_INDEX_MAX = 0xff; + static const uint32_t ENC4_INDEX_SHIFT = 3; + + static bool IsDeltaEncodeable(uint32_t startDelta, uint32_t length) { + MOZ_ASSERT(length != 0); + return startDelta <= ENC4_START_DELTA_MAX && length <= ENC4_LENGTH_MAX; + } + + static const uint32_t MAX_RUN_LENGTH = 100; + + typedef CodeGeneratorShared::NativeToTrackedOptimizations NativeToTrackedOptimizations; + static uint32_t ExpectedRunLength(const NativeToTrackedOptimizations *start, + const NativeToTrackedOptimizations *end); + + static void ReadDelta(CompactBufferReader &reader, uint32_t *startDelta, uint32_t *length, + uint8_t *index); + static void WriteDelta(CompactBufferWriter &writer, uint32_t startDelta, uint32_t length, + uint8_t index); + static bool WriteRun(CompactBufferWriter &writer, + const NativeToTrackedOptimizations *start, + const NativeToTrackedOptimizations *end, + const UniqueTrackedOptimizations &unique); +}; + +class IonTrackedOptimizationsAttempts +{ + const uint8_t *start_; + const uint8_t *end_; + + public: + IonTrackedOptimizationsAttempts(const uint8_t *start, const uint8_t *end) + : start_(start), end_(end) + { + // Cannot be empty. + MOZ_ASSERT(start < end); + } + + template + bool readVector(T *attempts) { + CompactBufferReader reader(start_, end_); + const uint8_t *cur = start_; + while (cur != end_) { + if (!attempts->append(OptimizationAttempt(reader))) + return false; + cur = reader.currentPosition(); + MOZ_ASSERT(cur <= end_); + } + return true; + } +}; + +class IonTrackedOptimizationsTypeInfo +{ + const uint8_t *start_; + const uint8_t *end_; + + public: + IonTrackedOptimizationsTypeInfo(const uint8_t *start, const uint8_t *end) + : start_(start), end_(end) + { + // Can be empty; i.e., no type info was tracked. + } + + bool empty() const { return start_ == end_; } + + template + bool readVector(T *types, const types::TypeSet::TypeList *allTypes) { + CompactBufferReader reader(start_, end_); + const uint8_t *cur = start_; + while (cur != end_) { + TrackedTypeInfo ty(reader); + if (!ty.readTypes(reader, allTypes)) + return false; + if (!types->append(mozilla::Move(ty))) + return false; + cur = reader.currentPosition(); + MOZ_ASSERT(cur <= end_); + } + return true; + } +}; + +template +class IonTrackedOptimizationsOffsetsTable +{ + uint32_t padding_; + uint32_t numEntries_; + uint32_t entryOffsets_[1]; + + protected: + const uint8_t *payloadEnd() const { + return (uint8_t *)(this) - padding_; + } + + public: + uint32_t numEntries() const { return numEntries_; } + uint32_t entryOffset(uint32_t index) const { + MOZ_ASSERT(index < numEntries()); + return entryOffsets_[index]; + } + + Entry entry(uint32_t index) const { + const uint8_t *start = payloadEnd() - entryOffset(index); + const uint8_t *end = payloadEnd(); + if (index < numEntries() - 1) + end -= entryOffset(index + 1); + return Entry(start, end); + } +}; + +class IonTrackedOptimizationsRegionTable + : public IonTrackedOptimizationsOffsetsTable +{ + public: + mozilla::Maybe findRegion(uint32_t offset) const; + + const uint8_t *payloadStart() const { return payloadEnd() - entryOffset(0); } +}; + +typedef IonTrackedOptimizationsOffsetsTable + IonTrackedOptimizationsAttemptsTable; + +typedef IonTrackedOptimizationsOffsetsTable + IonTrackedOptimizationsTypesTable; + +bool +WriteIonTrackedOptimizationsTable(JSContext *cx, CompactBufferWriter &writer, + const CodeGeneratorShared::NativeToTrackedOptimizations *start, + const CodeGeneratorShared::NativeToTrackedOptimizations *end, + const UniqueTrackedOptimizations &unique, + uint32_t *numRegions, uint32_t *regionTableOffsetp, + uint32_t *typesTableOffsetp, uint32_t *attemptsTableOffsetp, + types::TypeSet::TypeList *allTypes); + +} // namespace jit +} // namespace js + +#endif // jit_OptimizationTracking_h diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/RangeAnalysis.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/RangeAnalysis.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/RangeAnalysis.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/RangeAnalysis.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -3218,6 +3218,8 @@ { *shouldRemoveDeadCode = false; + MDefinitionVector deadConditions(alloc()); + for (ReversePostorderIterator iter(graph_.rpoBegin()); iter != graph_.rpoEnd(); iter++) { MBasicBlock *block = *iter; @@ -3232,6 +3234,7 @@ // chosen based which of the successors has the unreachable flag which is // added by MBeta::computeRange on its own block. MTest *test = cond->toTest(); + MDefinition *condition = test->input(); MConstant *constant = nullptr; if (block == test->ifTrue()) { constant = MConstant::New(alloc(), BooleanValue(false)); @@ -3239,7 +3242,15 @@ MOZ_ASSERT(block == test->ifFalse()); constant = MConstant::New(alloc(), BooleanValue(true)); } + + if (DeadIfUnused(condition) && !condition->isInWorklist()) { + condition->setInWorklist(); + if (!deadConditions.append(condition)) + return false; + } + test->block()->insertBefore(test, constant); + test->replaceOperand(0, constant); JitSpew(JitSpew_Range, "Update condition of %d to reflect unreachable branches.", test->id()); @@ -3247,5 +3258,55 @@ *shouldRemoveDeadCode = true; } + // Flag all fallible instructions which were indirectly used in the + // computation of the condition, such that we do not ignore + // bailout-paths which are used to shrink the input range of the + // operands of the condition. + for (size_t i = 0; i < deadConditions.length(); i++) { + MDefinition *cond = deadConditions[i]; + + // If this instruction is a guard, then there is not need to continue on + // this instruction. + if (cond->isGuard()) + continue; + + if (cond->range()) { + // Filter the range of the instruction based on its MIRType. + Range typeFilteredRange(cond); + + // If the filtered range is updated by adding the original range, + // then the MIRType act as an effectful filter. As we do not know if + // this filtered Range might change or not the result of the + // previous comparison, we have to keep this instruction as a guard + // because it has to bailout in order to restrict the Range to its + // MIRType. + if (typeFilteredRange.update(cond->range())) { + cond->setGuard(); + continue; + } + } + + for (size_t op = 0, e = cond->numOperands(); op < e; op++) { + MDefinition *operand = cond->getOperand(op); + if (!DeadIfUnused(operand) || operand->isInWorklist()) + continue; + + // If the operand has no range, then its range is always infered + // from its MIRType, so it cannot be used change the result deduced + // by Range Analysis. + if (!operand->range()) + continue; + + operand->setInWorklist(); + if (!deadConditions.append(operand)) + return false; + } + } + + while (!deadConditions.empty()) { + MDefinition *cond = deadConditions.popCopy(); + cond->setNotInWorklist(); + } + return true; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Recover.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Recover.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Recover.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Recover.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -6,6 +6,7 @@ #include "jit/Recover.h" +#include "jsapi.h" #include "jscntxt.h" #include "jsmath.h" #include "jsobj.h" @@ -874,10 +875,12 @@ { MOZ_ASSERT(canRecoverOnBailout()); writer.writeUnsigned(uint32_t(RInstruction::Recover_Hypot)); + writer.writeUnsigned(uint32_t(numOperands())); return true; } RHypot::RHypot(CompactBufferReader &reader) + : numOperands_(reader.readUnsigned()) { } bool @@ -885,12 +888,11 @@ { JS::AutoValueVector vec(cx); - // currently, only 2 args can be saved in MIR - if (!vec.reserve(2)) + if (!vec.reserve(numOperands_)) return false; - vec.infallibleAppend(iter.read()); - vec.infallibleAppend(iter.read()); + for (uint32_t i = 0 ; i < numOperands_ ; ++i) + vec.infallibleAppend(iter.read()); RootedValue result(cx); @@ -969,10 +971,10 @@ { RootedString str(cx, iter.read().toString()); RootedString sep(cx, iter.read().toString()); - RootedTypeObject typeObj(cx, iter.read().toObject().type()); + RootedObjectGroup group(cx, iter.read().toObject().group()); RootedValue result(cx); - JSObject *res = str_split_string(cx, typeObj, str, sep); + JSObject *res = str_split_string(cx, group, str, sep); if (!res) return false; @@ -1141,12 +1143,11 @@ RootedValue value(cx, iter.read()); RootedValue result(cx); - double in; - if (!ToNumber(cx, value, &in)) + int32_t trunc; + if (!JS::ToInt32(cx, value, &trunc)) return false; - int out = ToInt32(in); - result.setInt32(out); + result.setInt32(trunc); iter.storeInstructionResult(result); return true; } @@ -1210,13 +1211,13 @@ { RootedObject templateObject(cx, &iter.read().toObject()); RootedValue result(cx); - RootedTypeObject type(cx); + RootedObjectGroup group(cx); // See CodeGenerator::visitNewArrayCallVM - if (!templateObject->hasSingletonType()) - type = templateObject->type(); + if (!templateObject->isSingleton()) + group = templateObject->group(); - JSObject *resultObject = NewDenseArray(cx, count_, type, allocatingBehaviour_); + JSObject *resultObject = NewDenseArray(cx, count_, group, allocatingBehaviour_); if (!resultObject) return false; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Recover.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Recover.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Recover.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Recover.h 2015-02-03 14:33:32.000000000 +0000 @@ -477,11 +477,14 @@ class RHypot MOZ_FINAL : public RInstruction { + private: + uint32_t numOperands_; + public: RINSTRUCTION_HEADER_(Hypot) virtual uint32_t numOperands() const { - return 2; + return numOperands_; } bool recover(JSContext *cx, SnapshotIterator &iter) const; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/RegisterAllocator.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/RegisterAllocator.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/RegisterAllocator.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/RegisterAllocator.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -278,6 +278,10 @@ MOZ_ASSERT(safepoint->liveRegs().has(reg)); } + // The |this| argument slot is implicitly included in all safepoints. + if (alloc.isArgument() && alloc.toArgument()->index() < THIS_FRAME_ARGSLOT + sizeof(Value)) + return true; + LDefinition::Type type = virtualRegisters[vreg] ? virtualRegisters[vreg]->type() : LDefinition::GENERAL; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/RegisterSets.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/RegisterSets.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/RegisterSets.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/RegisterSets.h 2015-02-03 14:33:32.000000000 +0000 @@ -405,7 +405,7 @@ #error "Bad architecture" #endif } - // Determemine if some register are still allocated. This function should + // Determine if some register are still allocated. This function should // be used with the set of allocatable registers used for the initialization // of the current set. bool someAllocated(const TypedRegisterSet &allocatable) const { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/RematerializedFrame.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/RematerializedFrame.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/RematerializedFrame.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/RematerializedFrame.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -8,6 +8,7 @@ #include "jit/JitFrames.h" #include "vm/ArgumentsObject.h" +#include "vm/Debugger.h" #include "jsscriptinlines.h" #include "jit/JitFrames-inl.h" @@ -95,6 +96,7 @@ { for (size_t i = 0; i < frames.length(); i++) { RematerializedFrame *f = frames[i]; + Debugger::assertNotInFrameMaps(f); f->RematerializedFrame::~RematerializedFrame(); js_free(f); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/ScalarReplacement.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/ScalarReplacement.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/ScalarReplacement.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/ScalarReplacement.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -112,6 +112,10 @@ else obj = objDefault; + // Don't optimize unboxed objects, which aren't handled by MObjectState. + if (obj->is()) + return true; + // Check if the object is escaped. If the object is not the first argument // of either a known Store / Load, then we consider it as escaped. This is a // cheap and conservative escape analysis. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/AssemblerBuffer-x86-shared.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/AssemblerBuffer-x86-shared.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/AssemblerBuffer-x86-shared.cpp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/AssemblerBuffer-x86-shared.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,24 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 "jit/shared/AssemblerBuffer-x86-shared.h" + +#include "jsopcode.h" + +void js::jit::GenericAssembler::spew(const char *fmt, va_list va) +{ + // Buffer to hold the formatted string. Note that this may contain + // '%' characters, so do not pass it directly to printf functions. + char buf[200]; + + int i = vsnprintf(buf, sizeof(buf), fmt, va); + + if (i > -1) { + if (printer) + printer->printf("%s\n", buf); + js::jit::JitSpew(js::jit::JitSpew_Codegen, "%s", buf); + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/AssemblerBuffer-x86-shared.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/AssemblerBuffer-x86-shared.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/AssemblerBuffer-x86-shared.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/AssemblerBuffer-x86-shared.h 2015-02-03 14:33:32.000000000 +0000 @@ -30,17 +30,11 @@ #ifndef jit_shared_AssemblerBuffer_x86_shared_h #define jit_shared_AssemblerBuffer_x86_shared_h -#include #include #include -#include "jsfriendapi.h" -#include "jsopcode.h" -#include "jsutil.h" - #include "jit/ExecutableAllocator.h" #include "jit/JitSpewer.h" -#include "js/RootingAPI.h" // Spew formatting helpers. #define PRETTYHEX(x) (((x)<0)?"-":""),(((x)<0)?-(x):(x)) @@ -54,117 +48,98 @@ #define MEM_o32s MEM_o32 "(,%s,%d)" #define MEM_o32b MEM_o32 "(%s)" #define MEM_o32bs MEM_o32 "(%s,%s,%d)" +#define MEM_o32r ".Lfrom%d(%%rip)" #define ADDR_o(offset) PRETTYHEX(offset) -#define ADDR_os(offset, index, scale) ADDR_o(offset), nameIReg((index)), (1<<(scale)) -#define ADDR_ob(offset, base) ADDR_o(offset), nameIReg((base)) -#define ADDR_obs(offset, base, index, scale) ADDR_ob(offset, base), nameIReg((index)), (1<<(scale)) +#define ADDR_os(offset, index, scale) ADDR_o(offset), GPRegName((index)), (1<<(scale)) +#define ADDR_ob(offset, base) ADDR_o(offset), GPRegName((base)) +#define ADDR_obs(offset, base, index, scale) ADDR_ob(offset, base), GPRegName((index)), (1<<(scale)) #define ADDR_o32(offset) ADDR_o(offset) #define ADDR_o32s(offset, index, scale) ADDR_os(offset, index, scale) #define ADDR_o32b(offset, base) ADDR_ob(offset, base) #define ADDR_o32bs(offset, base, index, scale) ADDR_obs(offset, base, index, scale) - -#define FIXME_INSN_PRINTING \ - do { \ - spew("FIXME insn printing %s:%d", \ - __FILE__, __LINE__); \ - } while (0) +#define ADDR_o32r(offset) (offset) namespace js { + + class Sprinter; + namespace jit { class AssemblerBuffer { - static const size_t inlineCapacity = 256; public: AssemblerBuffer() - : m_buffer(m_inlineBuffer) - , m_capacity(inlineCapacity) - , m_size(0) - , m_allocSize(0) - , m_oom(false) - { - } - - ~AssemblerBuffer() + : m_oom(false) { - if (m_buffer != m_inlineBuffer) - js_free(m_buffer); } void ensureSpace(size_t space) { - if (m_size > m_capacity - space) - grow(); + if (MOZ_UNLIKELY(!m_buffer.reserve(m_buffer.length() + space))) + oomDetected(); } bool isAligned(size_t alignment) const { - return !(m_size & (alignment - 1)); + return !(m_buffer.length() & (alignment - 1)); } void putByteUnchecked(int value) { - MOZ_ASSERT(!(m_size > m_capacity - 4)); - m_buffer[m_size] = char(value); - m_size++; + m_buffer.infallibleAppend(char(value)); } void putByte(int value) { - if (m_size > m_capacity - 4) - grow(); - putByteUnchecked(value); + if (MOZ_UNLIKELY(!m_buffer.append(char(value)))) + oomDetected(); } void putShortUnchecked(int value) { - MOZ_ASSERT(!(m_size > m_capacity - 4)); - *reinterpret_cast(&m_buffer[m_size]) = short(value); - m_size += 2; + m_buffer.infallibleGrowByUninitialized(2); + memcpy(m_buffer.end() - 2, &value, 2); } void putShort(int value) { - if (m_size > m_capacity - 4) - grow(); - putShortUnchecked(value); + if (MOZ_UNLIKELY(!m_buffer.growByUninitialized(2))) { + oomDetected(); + return; + } + memcpy(m_buffer.end() - 2, &value, 2); } void putIntUnchecked(int value) { - MOZ_ASSERT(!(m_size > m_capacity - 4)); - *reinterpret_cast(&m_buffer[m_size]) = value; - m_size += 4; + m_buffer.infallibleGrowByUninitialized(4); + memcpy(m_buffer.end() - 4, &value, 4); } void putInt64Unchecked(int64_t value) { - MOZ_ASSERT(!(m_size > m_capacity - 8)); - *reinterpret_cast(&m_buffer[m_size]) = value; - m_size += 8; + m_buffer.infallibleGrowByUninitialized(8); + memcpy(m_buffer.end() - 8, &value, 8); } void putInt(int value) { - if (m_size > m_capacity - 4) - grow(); - putIntUnchecked(value); + if (MOZ_UNLIKELY(!m_buffer.growByUninitialized(4))) { + oomDetected(); + return; + } + memcpy(m_buffer.end() - 4, &value, 4); } - void* data() const + unsigned char *data() { - return m_buffer; + return m_buffer.begin(); } size_t size() const { - return m_size; - } - - size_t allocSize() const - { - return m_allocSize; + return m_buffer.length(); } bool oom() const @@ -172,134 +147,46 @@ return m_oom; } - /* - * The user must check for a NULL return value, which means - * no code was generated, or there was an OOM. - */ - void* executableAllocAndCopy(js::jit::ExecutableAllocator* allocator, - js::jit::ExecutablePool** poolp, js::jit::CodeKind kind) - { - if (m_oom || m_size == 0) { - *poolp = NULL; - return 0; - } - - m_allocSize = js::AlignBytes(m_size, sizeof(void *)); - - void* result = allocator->alloc(m_allocSize, poolp, kind); - if (!result) { - *poolp = NULL; - return 0; - } - MOZ_ASSERT(*poolp); - - js::jit::ExecutableAllocator::makeWritable(result, m_size); - - return memcpy(result, m_buffer, m_size); - } - - unsigned char *buffer() const { + const unsigned char *buffer() const { MOZ_ASSERT(!m_oom); - return reinterpret_cast(m_buffer); + return m_buffer.begin(); } protected: - void append(const char* data, size_t size) - { - if (m_size > m_capacity - size) - grow(size); - - // If we OOM and size > inlineCapacity, this would crash. - if (m_oom) - return; - memcpy(m_buffer + m_size, data, size); - m_size += size; - } - /* - * OOM handling: This class can OOM in the grow() method trying to - * allocate a new buffer. In response to an OOM, we need to avoid + * OOM handling: This class can OOM in the ensureSpace() method trying + * to allocate a new buffer. In response to an OOM, we need to avoid * crashing and report the error. We also want to make it so that * users of this class need to check for OOM only at certain points * and not after every operation. * - * Our strategy for handling an OOM is to set m_oom, and then set - * m_size to 0, preserving the current buffer. This way, the user + * Our strategy for handling an OOM is to set m_oom, and then clear (but + * not free) m_buffer, preserving the current buffer. This way, the user * can continue assembling into the buffer, deferring OOM checking * until the user wants to read code out of the buffer. * - * See also the |executableAllocAndCopy| and |buffer| methods. + * See also the |buffer| method. */ - - void grow(size_t extraCapacity = 0) - { - char* newBuffer; - - /* - * If |extraCapacity| is zero (as it almost always is) this is an - * allocator-friendly doubling growth strategy. - */ - size_t doubleCapacity = m_capacity + m_capacity; - - // Check for overflow. - if (doubleCapacity < m_capacity) { - m_size = 0; - m_oom = true; - return; - } - - size_t newCapacity = doubleCapacity + extraCapacity; - - // Check for overflow. - if (newCapacity < doubleCapacity) { - m_size = 0; - m_oom = true; - return; - } - - if (m_buffer == m_inlineBuffer) { - newBuffer = static_cast(js_malloc(newCapacity)); - if (!newBuffer) { - m_size = 0; - m_oom = true; - return; - } - memcpy(newBuffer, m_buffer, m_size); - } else { - newBuffer = static_cast(js_realloc(m_buffer, newCapacity)); - if (!newBuffer) { - m_size = 0; - m_oom = true; - return; - } - } - - m_buffer = newBuffer; - m_capacity = newCapacity; + void oomDetected() { + m_oom = true; + m_buffer.clear(); } - char m_inlineBuffer[inlineCapacity]; - char* m_buffer; - size_t m_capacity; - size_t m_size; - size_t m_allocSize; + mozilla::Vector m_buffer; bool m_oom; }; class GenericAssembler { - js::Sprinter *printer; + Sprinter *printer; public: - bool isOOLPath; - GenericAssembler() : printer(NULL) - , isOOLPath(false) {} - void setPrinter(js::Sprinter *sp) { + void setPrinter(Sprinter *sp) { printer = sp; } @@ -308,41 +195,15 @@ __attribute__ ((format (printf, 2, 3))) #endif { - if (printer || js::jit::JitSpewEnabled(js::jit::JitSpew_Codegen)) { - // Buffer to hold the formatted string. Note that this may contain - // '%' characters, so do not pass it directly to printf functions. - char buf[200]; - + if (MOZ_UNLIKELY(printer || JitSpewEnabled(JitSpew_Codegen))) { va_list va; va_start(va, fmt); - int i = vsnprintf(buf, sizeof(buf), fmt, va); + spew(fmt, va); va_end(va); - - if (i > -1) { - if (printer) - printer->printf("%s\n", buf); - js::jit::JitSpew(js::jit::JitSpew_Codegen, "%s", buf); - } } } - static void staticSpew(const char *fmt, ...) -#ifdef __GNUC__ - __attribute__ ((format (printf, 1, 2))) -#endif - { - if (js::jit::JitSpewEnabled(js::jit::JitSpew_Codegen)) { - char buf[200]; - - va_list va; - va_start(va, fmt); - int i = vsnprintf(buf, sizeof(buf), fmt, va); - va_end(va); - - if (i > -1) - js::jit::JitSpew(js::jit::JitSpew_Codegen, "%s", buf); - } - } + MOZ_COLD void spew(const char *fmt, va_list va); }; } // namespace jit diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Assembler-shared.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Assembler-shared.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Assembler-shared.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Assembler-shared.h 2015-02-03 14:33:32.000000000 +0000 @@ -30,6 +30,10 @@ namespace js { namespace jit { +namespace Disassembler { +class HeapAccess; +}; + static const uint32_t Simd128DataSize = 4 * sizeof(int32_t); static_assert(Simd128DataSize == 4 * sizeof(int32_t), "SIMD data should be able to contain int32x4"); static_assert(Simd128DataSize == 4 * sizeof(float), "SIMD data should be able to contain float32x4"); @@ -229,6 +233,22 @@ } }; +// Dummy value used for nursery pointers during Ion compilation, see +// LNurseryObject. +class IonNurseryPtr +{ + const gc::Cell *ptr; + + public: + friend class ImmGCPtr; + + explicit IonNurseryPtr(const gc::Cell *ptr) : ptr(ptr) + { + MOZ_ASSERT(ptr); + MOZ_ASSERT(uintptr_t(ptr) & 0x1); + } +}; + // Used for immediates which require relocation. class ImmGCPtr { @@ -242,6 +262,15 @@ // asm.js shouldn't be creating GC things MOZ_ASSERT(!IsCompilingAsmJS()); + } + + explicit ImmGCPtr(IonNurseryPtr ptr) : value(ptr.ptr) + { + MOZ_ASSERT(!IsPoisonedPtr(value)); + MOZ_ASSERT(value); + + // asm.js shouldn't be creating GC things + MOZ_ASSERT(!IsCompilingAsmJS()); } private: diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Assembler-x86-shared.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Assembler-x86-shared.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Assembler-x86-shared.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Assembler-x86-shared.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "gc/Marking.h" +#include "jit/Disassembler.h" #include "jit/JitCompartment.h" #if defined(JS_CODEGEN_X86) # include "jit/x86/MacroAssembler-x86.h" @@ -50,7 +51,7 @@ { while (reader.more()) { size_t offset = reader.readUnsigned(); - void **ptr = X86Assembler::getPointerRef(buffer + offset); + void **ptr = X86Encoding::GetPointerRef(buffer + offset); #ifdef JS_PUNBOX64 // All pointers on x64 will have the top bits cleared. If those bits @@ -66,8 +67,14 @@ } #endif + // The low bit shouldn't be set. If it is, we probably got a dummy + // pointer inserted by CodeGenerator::visitNurseryObject, but we + // shouldn't be able to trigger GC before those are patched to their + // real values. + MOZ_ASSERT(!(*reinterpret_cast(ptr) & 0x1)); + // No barrier needed since these are constants. - gc::MarkGCThingUnbarriered(trc, reinterpret_cast(ptr), "ion-masm-ptr"); + gc::MarkGCThingUnbarriered(trc, ptr, "ion-masm-ptr"); } } @@ -79,6 +86,45 @@ } void +AssemblerX86Shared::FixupNurseryObjects(JSContext *cx, JitCode *code, CompactBufferReader &reader, + const ObjectVector &nurseryObjects) +{ + MOZ_ASSERT(!nurseryObjects.empty()); + + uint8_t *buffer = code->raw(); + bool hasNurseryPointers = false; + + while (reader.more()) { + size_t offset = reader.readUnsigned(); + void **ptr = X86Encoding::GetPointerRef(buffer + offset); + + uintptr_t *word = reinterpret_cast(ptr); + +#ifdef JS_PUNBOX64 + if (*word >> JSVAL_TAG_SHIFT) + continue; // This is a Value. +#endif + + if (!(*word & 0x1)) + continue; + + uint32_t index = *word >> 1; + JSObject *obj = nurseryObjects[index]; + *word = uintptr_t(obj); + + // Either all objects are still in the nursery, or all objects are + // tenured. + MOZ_ASSERT_IF(hasNurseryPointers, IsInsideNursery(obj)); + + if (!hasNurseryPointers && IsInsideNursery(obj)) + hasNurseryPointers = true; + } + + if (hasNurseryPointers) + cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(code); +} + +void AssemblerX86Shared::trace(JSTracer *trc) { for (size_t i = 0; i < jumps_.length(); i++) { @@ -91,7 +137,7 @@ } if (dataRelocations_.length()) { CompactBufferReader reader(dataRelocations_); - ::TraceDataRelocations(trc, masm.buffer(), reader); + ::TraceDataRelocations(trc, masm.data(), reader); } } @@ -139,6 +185,15 @@ } } +void +AssemblerX86Shared::verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, + const Disassembler::HeapAccess &heapAccess) +{ +#ifdef DEBUG + Disassembler::VerifyHeapAccess(masm.data() + begin, masm.data() + end, heapAccess); +#endif +} + CPUInfo::SSEVersion CPUInfo::maxSSEVersion = UnknownSSE; CPUInfo::SSEVersion CPUInfo::maxEnabledSSEVersion = UnknownSSE; bool CPUInfo::avxPresent = false; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Assembler-x86-shared.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Assembler-x86-shared.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Assembler-x86-shared.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Assembler-x86-shared.h 2015-02-03 14:33:32.000000000 +0000 @@ -68,7 +68,7 @@ { } explicit Operand(AbsoluteAddress address) : kind_(MEM_ADDRESS32), - disp_(X86Assembler::addressImmediate(address.addr)) + disp_(X86Encoding::AddressImmediate(address.addr)) { } Address toAddress() const { @@ -216,10 +216,10 @@ } protected: - X86Assembler masm; + X86Encoding::BaseAssembler masm; - typedef X86Assembler::JmpSrc JmpSrc; - typedef X86Assembler::JmpDst JmpDst; + typedef X86Encoding::JmpSrc JmpSrc; + typedef X86Encoding::JmpDst JmpDst; public: AssemblerX86Shared() @@ -229,23 +229,23 @@ } enum Condition { - Equal = X86Assembler::ConditionE, - NotEqual = X86Assembler::ConditionNE, - Above = X86Assembler::ConditionA, - AboveOrEqual = X86Assembler::ConditionAE, - Below = X86Assembler::ConditionB, - BelowOrEqual = X86Assembler::ConditionBE, - GreaterThan = X86Assembler::ConditionG, - GreaterThanOrEqual = X86Assembler::ConditionGE, - LessThan = X86Assembler::ConditionL, - LessThanOrEqual = X86Assembler::ConditionLE, - Overflow = X86Assembler::ConditionO, - Signed = X86Assembler::ConditionS, - NotSigned = X86Assembler::ConditionNS, - Zero = X86Assembler::ConditionE, - NonZero = X86Assembler::ConditionNE, - Parity = X86Assembler::ConditionP, - NoParity = X86Assembler::ConditionNP + Equal = X86Encoding::ConditionE, + NotEqual = X86Encoding::ConditionNE, + Above = X86Encoding::ConditionA, + AboveOrEqual = X86Encoding::ConditionAE, + Below = X86Encoding::ConditionB, + BelowOrEqual = X86Encoding::ConditionBE, + GreaterThan = X86Encoding::ConditionG, + GreaterThanOrEqual = X86Encoding::ConditionGE, + LessThan = X86Encoding::ConditionL, + LessThanOrEqual = X86Encoding::ConditionLE, + Overflow = X86Encoding::ConditionO, + Signed = X86Encoding::ConditionS, + NotSigned = X86Encoding::ConditionNS, + Zero = X86Encoding::ConditionE, + NonZero = X86Encoding::ConditionNE, + Parity = X86Encoding::ConditionP, + NoParity = X86Encoding::ConditionNP }; // If this bit is set, the vucomisd operands have to be inverted. @@ -327,6 +327,9 @@ static void TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader); + static void FixupNurseryObjects(JSContext *cx, JitCode *code, CompactBufferReader &reader, + const ObjectVector &nurseryObjects); + // MacroAssemblers hold onto gcthings, so they are traced by the GC. void trace(JSTracer *trc); @@ -788,10 +791,10 @@ void jSrc(Condition cond, Label *label) { if (label->bound()) { // The jump can be immediately encoded to the correct destination. - masm.jCC_i(static_cast(cond), JmpDst(label->offset())); + masm.jCC_i(static_cast(cond), JmpDst(label->offset())); } else { // Thread the jump list through the unpatched jump targets. - JmpSrc j = masm.jCC(static_cast(cond)); + JmpSrc j = masm.jCC(static_cast(cond)); JmpSrc prev = JmpSrc(label->use(j.offset())); masm.setNextJump(j, prev); } @@ -823,7 +826,7 @@ } JmpSrc jSrc(Condition cond, RepatchLabel *label) { - JmpSrc j = masm.jCC(static_cast(cond)); + JmpSrc j = masm.jCC(static_cast(cond)); if (label->bound()) { // The jump can be immediately patched to the correct destination. masm.linkJump(j, JmpDst(label->offset())); @@ -869,12 +872,12 @@ } void cmpEAX(Label *label) { cmpSrc(label); } void bind(Label *label) { - X86Assembler::JmpDst dst(masm.label()); + JmpDst dst(masm.label()); if (label->used()) { bool more; - X86Assembler::JmpSrc jmp(label->offset()); + JmpSrc jmp(label->offset()); do { - X86Assembler::JmpSrc next; + JmpSrc next; more = masm.nextJump(jmp, &next); masm.linkJump(jmp, dst); jmp = next; @@ -883,9 +886,9 @@ label->bind(dst.offset()); } void bind(RepatchLabel *label) { - X86Assembler::JmpDst dst(masm.label()); + JmpDst dst(masm.label()); if (label->used()) { - X86Assembler::JmpSrc jmp(label->offset()); + JmpSrc jmp(label->offset()); masm.linkJump(jmp, dst); } label->bind(dst.offset()); @@ -898,9 +901,9 @@ void retarget(Label *label, Label *target) { if (label->used()) { bool more; - X86Assembler::JmpSrc jmp(label->offset()); + JmpSrc jmp(label->offset()); do { - X86Assembler::JmpSrc next; + JmpSrc next; more = masm.nextJump(jmp, &next); if (target->bound()) { @@ -922,15 +925,15 @@ if (label->used()) { intptr_t src = label->offset(); do { - intptr_t next = reinterpret_cast(X86Assembler::getPointer(raw + src)); - X86Assembler::setPointer(raw + src, address); + intptr_t next = reinterpret_cast(X86Encoding::GetPointer(raw + src)); + X86Encoding::SetPointer(raw + src, address); src = next; } while (src != AbsoluteLabel::INVALID_OFFSET); } label->bind(); } - // See Bind and X86Assembler::setPointer. + // See Bind and X86Encoding::setPointer. size_t labelOffsetToPatchOffset(size_t offset) { return offset - sizeof(void*); } @@ -952,12 +955,12 @@ } } void call(Register reg) { - masm.call(reg.code()); + masm.call_r(reg.code()); } void call(const Operand &op) { switch (op.kind()) { case Operand::REG: - masm.call(op.reg()); + masm.call_r(op.reg()); break; case Operand::MEM_REG_DISP: masm.call_m(op.disp(), op.base()); @@ -1040,7 +1043,7 @@ masm.cmpw_rr(rhs.code(), lhs.code()); } void setCC(Condition cond, Register r) { - masm.setCC_r(static_cast(cond), r.code()); + masm.setCC_r(static_cast(cond), r.code()); } void testb(Register rhs, Register lhs) { MOZ_ASSERT(GeneralRegisterSet(Registers::SingleByteRegs).has(rhs)); @@ -1694,19 +1697,19 @@ } } void vcmpeqps(const Operand &src1, FloatRegister src0, FloatRegister dest) { - vcmpps(X86Assembler::ConditionCmp_EQ, src1, src0, dest); + vcmpps(X86Encoding::ConditionCmp_EQ, src1, src0, dest); } void vcmpltps(const Operand &src1, FloatRegister src0, FloatRegister dest) { - vcmpps(X86Assembler::ConditionCmp_LT, src1, src0, dest); + vcmpps(X86Encoding::ConditionCmp_LT, src1, src0, dest); } void vcmpleps(const Operand &src1, FloatRegister src0, FloatRegister dest) { - vcmpps(X86Assembler::ConditionCmp_LE, src1, src0, dest); + vcmpps(X86Encoding::ConditionCmp_LE, src1, src0, dest); } void vcmpunordps(const Operand &src1, FloatRegister src0, FloatRegister dest) { - vcmpps(X86Assembler::ConditionCmp_UNORD, src1, src0, dest); + vcmpps(X86Encoding::ConditionCmp_UNORD, src1, src0, dest); } void vcmpneqps(const Operand &src1, FloatRegister src0, FloatRegister dest) { - vcmpps(X86Assembler::ConditionCmp_NEQ, src1, src0, dest); + vcmpps(X86Encoding::ConditionCmp_NEQ, src1, src0, dest); } void vrcpps(const Operand &src, FloatRegister dest) { MOZ_ASSERT(HasSSE2()); @@ -2333,11 +2336,11 @@ MOZ_ASSERT(HasSSE2()); masm.vsqrtss_rr(src1.code(), src0.code(), dest.code()); } - void vroundsd(X86Assembler::RoundingMode mode, FloatRegister src1, FloatRegister src0, FloatRegister dest) { + void vroundsd(X86Encoding::RoundingMode mode, FloatRegister src1, FloatRegister src0, FloatRegister dest) { MOZ_ASSERT(HasSSE41()); masm.vroundsd_irr(mode, src1.code(), src0.code(), dest.code()); } - void vroundss(X86Assembler::RoundingMode mode, FloatRegister src1, FloatRegister src0, FloatRegister dest) { + void vroundss(X86Encoding::RoundingMode mode, FloatRegister src1, FloatRegister src0, FloatRegister dest) { MOZ_ASSERT(HasSSE41()); masm.vroundss_irr(mode, src1.code(), src0.code(), dest.code()); } @@ -2594,6 +2597,9 @@ *ptr == 0xE8); // CALL *ptr = enabled ? 0xE8 : 0x3D; } + + MOZ_COLD void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, + const Disassembler::HeapAccess &heapAccess); }; } // namespace jit diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/BaseAssembler-x86-shared.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/BaseAssembler-x86-shared.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/BaseAssembler-x86-shared.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/BaseAssembler-x86-shared.h 2015-02-03 14:33:32.000000000 +0000 @@ -32,570 +32,26 @@ #include "mozilla/IntegerPrintfMacros.h" -#include - #include "jit/shared/AssemblerBuffer-x86-shared.h" - -#include "js/Vector.h" +#include "jit/shared/Encoding-x86-shared.h" +#include "jit/shared/Patching-x86-shared.h" namespace js { namespace jit { -inline bool CAN_SIGN_EXTEND_8_32(int32_t value) { return value == (int32_t)(int8_t)value; } -inline bool CAN_SIGN_EXTEND_16_32(int32_t value) { return value == (int32_t)(int16_t)value; } -inline bool CAN_ZERO_EXTEND_8_32(int32_t value) { return value == (int32_t)(uint8_t)value; } -inline bool CAN_ZERO_EXTEND_8H_32(int32_t value) { return value == (value & 0xff00); } -inline bool CAN_ZERO_EXTEND_16_32(int32_t value) { return value == (int32_t)(uint16_t)value; } -inline bool CAN_ZERO_EXTEND_32_64(int32_t value) { return value >= 0; } - -namespace X86Registers { - enum RegisterID { - eax, - ecx, - edx, - ebx, - esp, - ebp, - esi, - edi - -#ifdef JS_CODEGEN_X64 - ,r8, - r9, - r10, - r11, - r12, - r13, - r14, - r15 -#endif - ,invalid_reg - }; - - enum XMMRegisterID { - xmm0, - xmm1, - xmm2, - xmm3, - xmm4, - xmm5, - xmm6, - xmm7 -#ifdef JS_CODEGEN_X64 - ,xmm8, - xmm9, - xmm10, - xmm11, - xmm12, - xmm13, - xmm14, - xmm15 -#endif - ,invalid_xmm - }; - - static const char* nameFPReg(XMMRegisterID fpreg) - { - static const char* const xmmnames[16] - = { "%xmm0", "%xmm1", "%xmm2", "%xmm3", - "%xmm4", "%xmm5", "%xmm6", "%xmm7", - "%xmm8", "%xmm9", "%xmm10", "%xmm11", - "%xmm12", "%xmm13", "%xmm14", "%xmm15" }; - int off = (XMMRegisterID)fpreg - (XMMRegisterID)xmm0; - MOZ_ASSERT(off >= 0 && size_t(off) < mozilla::ArrayLength(xmmnames)); - return xmmnames[off]; - } - - static const char* nameIReg(int szB, RegisterID reg) - { - static const char* const r64names[16] - = { "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi", - "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15" }; - static const char* const r32names[16] - = { "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi", - "%r8d", "%r9d", "%r10d", "%r11d", "%r12d", "%r13d", "%r14d", "%r15d" }; - static const char* const r16names[16] - = { "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di", - "%r8w", "%r9w", "%r10w", "%r11w", "%r12w", "%r13w", "%r14w", "%r15w" }; - static const char* const r8names[16] - = { "%al", "%cl", "%dl", "%bl", "%spl", "%bpl", "%sil", "%dil", - "%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b" }; - int off = (RegisterID)reg - (RegisterID)eax; - MOZ_ASSERT(off >= 0 && size_t(off) < mozilla::ArrayLength(r64names)); - const char* const* tab = r64names; - switch (szB) { - case 1: tab = r8names; break; - case 2: tab = r16names; break; - case 4: tab = r32names; break; - } - return tab[off]; - } - - static const char* nameI8Reg_norex(RegisterID reg) - { - // The same as r8names above, except that %spl through %dil are replaced - // by %ah through %bh since there is to be no REX prefix. - static const char* const r8names_norex[16] - = { "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh", - "%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b" }; - int off = (RegisterID)reg - (RegisterID)eax; - MOZ_ASSERT(off >= 0 && size_t(off) < mozilla::ArrayLength(r8names_norex)); - return r8names_norex[off]; - } - - static const char* nameIReg(RegisterID reg) - { -# ifdef JS_CODEGEN_X64 - return nameIReg(8, reg); -# else - return nameIReg(4, reg); -# endif - } - - inline bool hasSubregL(RegisterID reg) - { -# ifdef JS_CODEGEN_X64 - // In 64-bit mode, all registers have an 8-bit lo subreg. - return true; -# else - // In 32-bit mode, only the first four registers do. - return reg <= ebx; -# endif - } - - inline bool hasSubregH(RegisterID reg) - { - // The first four registers always have h registers. However, note that - // on x64, h registers may not be used in instructions using REX - // prefixes. Also note that this may depend on what other registers are - // used! - return reg <= ebx; - } - - inline RegisterID getSubregH(RegisterID reg) { - MOZ_ASSERT(hasSubregH(reg)); - return RegisterID(reg + 4); - } - -} /* namespace X86Registers */ - -// Byte operand register spl & above require a REX prefix (to prevent -// the 'H' registers be accessed). -static inline bool -ByteRegRequiresRex(int reg) -{ - return (reg >= X86Registers::esp); -} - -class X86Assembler : public GenericAssembler { -public: - typedef X86Registers::RegisterID RegisterID; - typedef X86Registers::XMMRegisterID XMMRegisterID; - typedef XMMRegisterID FPRegisterID; - - enum Condition { - ConditionO, - ConditionNO, - ConditionB, - ConditionAE, - ConditionE, - ConditionNE, - ConditionBE, - ConditionA, - ConditionS, - ConditionNS, - ConditionP, - ConditionNP, - ConditionL, - ConditionGE, - ConditionLE, - ConditionG, - - ConditionC = ConditionB, - ConditionNC = ConditionAE - }; - - // Conditions for CMP instructions (CMPSS, CMPSD, CMPPS, CMPPD, etc). - enum ConditionCmp { - ConditionCmp_EQ = 0x0, - ConditionCmp_LT = 0x1, - ConditionCmp_LE = 0x2, - ConditionCmp_UNORD = 0x3, - ConditionCmp_NEQ = 0x4, - ConditionCmp_NLT = 0x5, - ConditionCmp_NLE = 0x6, - ConditionCmp_ORD = 0x7, - }; - - static const char* nameCC(Condition cc) - { - static const char* const names[16] - = { "o ", "no", "b ", "ae", "e ", "ne", "be", "a ", - "s ", "ns", "p ", "np", "l ", "ge", "le", "g " }; - int ix = (int)cc; - MOZ_ASSERT(ix >= 0 && size_t(ix) < mozilla::ArrayLength(names)); - return names[ix]; - } - - // Rounding modes for ROUNDSD. - enum RoundingMode { - RoundToNearest = 0x0, - RoundDown = 0x1, - RoundUp = 0x2, - RoundToZero = 0x3 - }; - -private: - enum OneByteOpcodeID { - OP_ADD_EbGb = 0x00, - OP_ADD_EvGv = 0x01, - OP_ADD_GvEv = 0x03, - OP_ADD_EAXIv = 0x05, - OP_OR_EbGb = 0x08, - OP_OR_EvGv = 0x09, - OP_OR_GvEv = 0x0B, - OP_OR_EAXIv = 0x0D, - OP_2BYTE_ESCAPE = 0x0F, - OP_AND_EbGb = 0x20, - OP_AND_EvGv = 0x21, - OP_AND_GvEv = 0x23, - OP_AND_EAXIv = 0x25, - OP_SUB_EbGb = 0x28, - OP_SUB_EvGv = 0x29, - OP_SUB_GvEv = 0x2B, - OP_SUB_EAXIv = 0x2D, - PRE_PREDICT_BRANCH_NOT_TAKEN = 0x2E, - OP_XOR_EbGb = 0x30, - OP_XOR_EvGv = 0x31, - OP_XOR_GvEv = 0x33, - OP_XOR_EAXIv = 0x35, - OP_CMP_EvGv = 0x39, - OP_CMP_GvEv = 0x3B, - OP_CMP_EAXIv = 0x3D, -#ifdef JS_CODEGEN_X64 - PRE_REX = 0x40, -#endif - OP_PUSH_EAX = 0x50, - OP_POP_EAX = 0x58, -#ifdef JS_CODEGEN_X86 - OP_PUSHA = 0x60, - OP_POPA = 0x61, -#endif -#ifdef JS_CODEGEN_X64 - OP_MOVSXD_GvEv = 0x63, -#endif - PRE_OPERAND_SIZE = 0x66, - PRE_SSE_66 = 0x66, - OP_PUSH_Iz = 0x68, - OP_IMUL_GvEvIz = 0x69, - OP_PUSH_Ib = 0x6a, - OP_IMUL_GvEvIb = 0x6b, - OP_JCC_rel8 = 0x70, - OP_GROUP1_EbIb = 0x80, - OP_GROUP1_EvIz = 0x81, - OP_GROUP1_EvIb = 0x83, - OP_TEST_EbGb = 0x84, - OP_TEST_EvGv = 0x85, - OP_XCHG_GvEv = 0x87, - OP_MOV_EbGv = 0x88, - OP_MOV_EvGv = 0x89, - OP_MOV_GvEb = 0x8A, - OP_MOV_GvEv = 0x8B, - OP_LEA = 0x8D, - OP_GROUP1A_Ev = 0x8F, - OP_NOP = 0x90, - OP_PUSHFLAGS = 0x9C, - OP_POPFLAGS = 0x9D, - OP_CDQ = 0x99, - OP_MOV_EAXOv = 0xA1, - OP_MOV_OvEAX = 0xA3, - OP_TEST_EAXIb = 0xA8, - OP_TEST_EAXIv = 0xA9, - OP_MOV_EAXIv = 0xB8, - OP_GROUP2_EvIb = 0xC1, - OP_RET_Iz = 0xC2, - PRE_VEX_C4 = 0xC4, - PRE_VEX_C5 = 0xC5, - OP_RET = 0xC3, - OP_GROUP11_EvIb = 0xC6, - OP_GROUP11_EvIz = 0xC7, - OP_INT3 = 0xCC, - OP_GROUP2_Ev1 = 0xD1, - OP_GROUP2_EvCL = 0xD3, - OP_FPU6 = 0xDD, - OP_FPU6_F32 = 0xD9, - OP_CALL_rel32 = 0xE8, - OP_JMP_rel32 = 0xE9, - OP_JMP_rel8 = 0xEB, - PRE_LOCK = 0xF0, - PRE_SSE_F2 = 0xF2, - PRE_SSE_F3 = 0xF3, - OP_HLT = 0xF4, - OP_GROUP3_EbIb = 0xF6, - OP_GROUP3_Ev = 0xF7, - OP_GROUP3_EvIz = 0xF7, // OP_GROUP3_Ev has an immediate, when instruction is a test. - OP_GROUP5_Ev = 0xFF - }; - - enum ShiftID { - Shift_vpsrld = 2, - Shift_vpsrlq = 2, - Shift_vpsrldq = 3, - Shift_vpsrad = 4, - Shift_vpslld = 6, - Shift_vpsllq = 6 - }; - - enum TwoByteOpcodeID { - OP2_UD2 = 0x0B, - OP2_MOVSD_VsdWsd = 0x10, - OP2_MOVPS_VpsWps = 0x10, - OP2_MOVSD_WsdVsd = 0x11, - OP2_MOVPS_WpsVps = 0x11, - OP2_MOVHLPS_VqUq = 0x12, - OP2_MOVSLDUP_VpsWps = 0x12, - OP2_UNPCKLPS_VsdWsd = 0x14, - OP2_UNPCKHPS_VsdWsd = 0x15, - OP2_MOVLHPS_VqUq = 0x16, - OP2_MOVSHDUP_VpsWps = 0x16, - OP2_MOVAPD_VsdWsd = 0x28, - OP2_MOVAPS_VsdWsd = 0x28, - OP2_MOVAPS_WsdVsd = 0x29, - OP2_CVTSI2SD_VsdEd = 0x2A, - OP2_CVTTSD2SI_GdWsd = 0x2C, - OP2_UCOMISD_VsdWsd = 0x2E, - OP2_MOVMSKPD_EdVd = 0x50, - OP2_ANDPS_VpsWps = 0x54, - OP2_ANDNPS_VpsWps = 0x55, - OP2_ORPS_VpsWps = 0x56, - OP2_XORPS_VpsWps = 0x57, - OP2_ADDSD_VsdWsd = 0x58, - OP2_ADDPS_VpsWps = 0x58, - OP2_MULSD_VsdWsd = 0x59, - OP2_MULPS_VpsWps = 0x59, - OP2_CVTSS2SD_VsdEd = 0x5A, - OP2_CVTSD2SS_VsdEd = 0x5A, - OP2_CVTTPS2DQ_VdqWps = 0x5B, - OP2_CVTDQ2PS_VpsWdq = 0x5B, - OP2_SUBSD_VsdWsd = 0x5C, - OP2_SUBPS_VpsWps = 0x5C, - OP2_MINSD_VsdWsd = 0x5D, - OP2_MINSS_VssWss = 0x5D, - OP2_MINPS_VpsWps = 0x5D, - OP2_DIVSD_VsdWsd = 0x5E, - OP2_DIVPS_VpsWps = 0x5E, - OP2_MAXSD_VsdWsd = 0x5F, - OP2_MAXSS_VssWss = 0x5F, - OP2_MAXPS_VpsWps = 0x5F, - OP2_SQRTSD_VsdWsd = 0x51, - OP2_SQRTSS_VssWss = 0x51, - OP2_SQRTPS_VpsWps = 0x51, - OP2_RSQRTPS_VpsWps = 0x52, - OP2_RCPPS_VpsWps = 0x53, - OP2_ANDPD_VpdWpd = 0x54, - OP2_ORPD_VpdWpd = 0x56, - OP2_XORPD_VpdWpd = 0x57, - OP2_PCMPGTD_VdqWdq = 0x66, - OP2_MOVD_VdEd = 0x6E, - OP2_MOVDQ_VsdWsd = 0x6F, - OP2_MOVDQ_VdqWdq = 0x6F, - OP2_PSHUFD_VdqWdqIb = 0x70, - OP2_PSLLD_UdqIb = 0x72, - OP2_PSRAD_UdqIb = 0x72, - OP2_PSRLD_UdqIb = 0x72, - OP2_PSRLDQ_Vd = 0x73, - OP2_PCMPEQW = 0x75, - OP2_PCMPEQD_VdqWdq = 0x76, - OP2_MOVD_EdVd = 0x7E, - OP2_MOVDQ_WdqVdq = 0x7F, - OP2_JCC_rel32 = 0x80, - OP_SETCC = 0x90, - OP_FENCE = 0xAE, - OP2_IMUL_GvEv = 0xAF, - OP2_CMPXCHG_GvEb = 0xB0, - OP2_CMPXCHG_GvEw = 0xB1, - OP2_BSR_GvEv = 0xBD, - OP2_MOVSX_GvEb = 0xBE, - OP2_MOVSX_GvEw = 0xBF, - OP2_MOVZX_GvEb = 0xB6, - OP2_MOVZX_GvEw = 0xB7, - OP2_XADD_EbGb = 0xC0, - OP2_XADD_EvGv = 0xC1, - OP2_CMPPS_VpsWps = 0xC2, - OP2_PEXTRW_GdUdIb = 0xC5, - OP2_SHUFPS_VpsWpsIb = 0xC6, - OP2_PSRLD_VdqWdq = 0xD2, - OP2_PANDDQ_VdqWdq = 0xDB, - OP2_PANDNDQ_VdqWdq = 0xDF, - OP2_PSRAD_VdqWdq = 0xE2, - OP2_PORDQ_VdqWdq = 0xEB, - OP2_PXORDQ_VdqWdq = 0xEF, - OP2_PSLLD_VdqWdq = 0xF2, - OP2_PMULUDQ_VdqWdq = 0xF4, - OP2_PSUBD_VdqWdq = 0xFA, - OP2_PADDD_VdqWdq = 0xFE - }; - - // Test whether the given opcode should be printed with its operands reversed. - static inline bool IsXMMReversedOperands(TwoByteOpcodeID opcode) { - switch (opcode) { - case OP2_MOVSD_WsdVsd: // also OP2_MOVPS_WpsVps - case OP2_MOVAPS_WsdVsd: - case OP2_MOVDQ_WdqVdq: - case OP3_PEXTRD_EdVdqIb: - return true; - default: - break; - } - return false; - } - - enum ThreeByteOpcodeID { - OP3_ROUNDSS_VsdWsd = 0x0A, - OP3_ROUNDSD_VsdWsd = 0x0B, - OP3_BLENDVPS_VdqWdq = 0x14, - OP3_PEXTRD_EdVdqIb = 0x16, - OP3_BLENDPS_VpsWpsIb = 0x0C, - OP3_PTEST_VdVd = 0x17, - OP3_INSERTPS_VpsUps = 0x21, - OP3_PINSRD_VdqEdIb = 0x22, - OP3_PMULLD_VdqWdq = 0x40, - OP3_VBLENDVPS_VdqWdq = 0x4A - }; - - enum ThreeByteEscape { - ESCAPE_BLENDVPS = 0x38, - ESCAPE_PMULLD = 0x38, - ESCAPE_PTEST = 0x38, - ESCAPE_PINSRD = 0x3A, - ESCAPE_PEXTRD = 0x3A, - ESCAPE_ROUNDSD = 0x3A, - ESCAPE_INSERTPS = 0x3A, - ESCAPE_BLENDPS = 0x3A, - ESCAPE_VBLENDVPS = 0x3A - }; - - enum VexOperandType { - VEX_PS = 0, - VEX_PD = 1, - VEX_SS = 2, - VEX_SD = 3 - }; - - OneByteOpcodeID jccRel8(Condition cond) - { - return (OneByteOpcodeID)(OP_JCC_rel8 + cond); - } - TwoByteOpcodeID jccRel32(Condition cond) - { - return (TwoByteOpcodeID)(OP2_JCC_rel32 + cond); - } - - TwoByteOpcodeID setccOpcode(Condition cond) - { - return (TwoByteOpcodeID)(OP_SETCC + cond); - } - - enum GroupOpcodeID { - GROUP1_OP_ADD = 0, - GROUP1_OP_OR = 1, - GROUP1_OP_ADC = 2, - GROUP1_OP_AND = 4, - GROUP1_OP_SUB = 5, - GROUP1_OP_XOR = 6, - GROUP1_OP_CMP = 7, - - GROUP1A_OP_POP = 0, - - GROUP2_OP_SHL = 4, - GROUP2_OP_SHR = 5, - GROUP2_OP_SAR = 7, - - GROUP3_OP_TEST = 0, - GROUP3_OP_NOT = 2, - GROUP3_OP_NEG = 3, - GROUP3_OP_IMUL = 5, - GROUP3_OP_DIV = 6, - GROUP3_OP_IDIV = 7, - - GROUP5_OP_INC = 0, - GROUP5_OP_DEC = 1, - GROUP5_OP_CALLN = 2, - GROUP5_OP_JMPN = 4, - GROUP5_OP_PUSH = 6, - - FPU6_OP_FLD = 0, - FPU6_OP_FISTTP = 1, - FPU6_OP_FSTP = 3, - - GROUP11_MOV = 0 - }; +namespace X86Encoding { - class X86InstructionFormatter; +class BaseAssembler : public GenericAssembler { public: - X86Assembler() + BaseAssembler() : useVEX_(true) { } void disableVEX() { useVEX_ = false; } - class JmpSrc { - friend class X86Assembler; - friend class X86InstructionFormatter; - public: - JmpSrc() - : m_offset(-1) - { - } - - explicit JmpSrc(int32_t offset) - : m_offset(offset) - { - } - - int32_t offset() const { - return m_offset; - } - - bool isSet() const { - return m_offset != -1; - } - - private: - int m_offset; - }; - - class JmpDst { - friend class X86Assembler; - friend class X86InstructionFormatter; - public: - JmpDst() - : m_offset(-1) - , m_used(false) - { - } - - bool isUsed() const { return m_used; } - void used() { m_used = true; } - bool isValid() const { return m_offset != -1; } - - explicit JmpDst(int32_t offset) - : m_offset(offset) - , m_used(false) - { - MOZ_ASSERT(m_offset == offset); - } - int32_t offset() const { - return m_offset; - } - private: - signed int m_offset : 31; - bool m_used : 1; - }; - size_t size() const { return m_formatter.size(); } - size_t allocSize() const { return m_formatter.allocSize(); } - unsigned char *buffer() const { return m_formatter.buffer(); } + const unsigned char *buffer() const { return m_formatter.buffer(); } + unsigned char *data() { return m_formatter.data(); } bool oom() const { return m_formatter.oom(); } void nop() @@ -615,13 +71,13 @@ void push_r(RegisterID reg) { - spew("push %s", nameIReg(reg)); + spew("push %s", GPRegName(reg)); m_formatter.oneByteOp(OP_PUSH_EAX, reg); } void pop_r(RegisterID reg) { - spew("pop %s", nameIReg(reg)); + spew("pop %s", GPRegName(reg)); m_formatter.oneByteOp(OP_POP_EAX, reg); } @@ -673,7 +129,7 @@ #ifdef JS_CODEGEN_X86 void adcl_im(int32_t imm, const void* addr) { - FIXME_INSN_PRINTING; + spew("adcl %d, %p", imm, addr); if (CAN_SIGN_EXTEND_8_32(imm)) { m_formatter.oneByteOp(OP_GROUP1_EvIb, addr, GROUP1_OP_ADC); m_formatter.immediate8s(imm); @@ -686,30 +142,30 @@ void addl_rr(RegisterID src, RegisterID dst) { - spew("addl %s, %s", nameIReg(4,src), nameIReg(4,dst)); + spew("addl %s, %s", GPReg32Name(src), GPReg32Name(dst)); m_formatter.oneByteOp(OP_ADD_GvEv, src, dst); } void addl_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("addl " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(4,dst)); + spew("addl " MEM_ob ", %s", ADDR_ob(offset, base), GPReg32Name(dst)); m_formatter.oneByteOp(OP_ADD_GvEv, offset, base, dst); } void addl_rm(RegisterID src, int32_t offset, RegisterID base) { - spew("addl %s, " MEM_ob, nameIReg(4,src), ADDR_ob(offset, base)); + spew("addl %s, " MEM_ob, GPReg32Name(src), ADDR_ob(offset, base)); m_formatter.oneByteOp(OP_ADD_EvGv, offset, base, src); } void addl_ir(int32_t imm, RegisterID dst) { - spew("addl $%d, %s", imm, nameIReg(4,dst)); + spew("addl $%d, %s", imm, GPReg32Name(dst)); if (CAN_SIGN_EXTEND_8_32(imm)) { m_formatter.oneByteOp(OP_GROUP1_EvIb, dst, GROUP1_OP_ADD); m_formatter.immediate8s(imm); } else { - if (dst == X86Registers::eax) + if (dst == rax) m_formatter.oneByteOp(OP_ADD_EAXIv); else m_formatter.oneByteOp(OP_GROUP1_EvIz, dst, GROUP1_OP_ADD); @@ -719,8 +175,8 @@ void addl_i32r(int32_t imm, RegisterID dst) { // 32-bit immediate always, for patching. - spew("addl $0x%04x, %s", imm, nameIReg(4,dst)); - if (dst == X86Registers::eax) + spew("addl $0x%04x, %s", imm, GPReg32Name(dst)); + if (dst == rax) m_formatter.oneByteOp(OP_ADD_EAXIv); else m_formatter.oneByteOp(OP_GROUP1_EvIz, dst, GROUP1_OP_ADD); @@ -742,30 +198,30 @@ #ifdef JS_CODEGEN_X64 void addq_rr(RegisterID src, RegisterID dst) { - spew("addq %s, %s", nameIReg(8,src), nameIReg(8,dst)); + spew("addq %s, %s", GPReg64Name(src), GPReg64Name(dst)); m_formatter.oneByteOp64(OP_ADD_GvEv, src, dst); } void addq_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("addq " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(8,dst)); + spew("addq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst)); m_formatter.oneByteOp64(OP_ADD_GvEv, offset, base, dst); } void addq_mr(const void* addr, RegisterID dst) { - spew("addq %p, %s", addr, nameIReg(8, dst)); + spew("addq %p, %s", addr, GPReg64Name(dst)); m_formatter.oneByteOp64(OP_ADD_GvEv, addr, dst); } void addq_ir(int32_t imm, RegisterID dst) { - spew("addq $%d, %s", imm, nameIReg(8,dst)); + spew("addq $%d, %s", imm, GPReg64Name(dst)); if (CAN_SIGN_EXTEND_8_32(imm)) { m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_ADD); m_formatter.immediate8s(imm); } else { - if (dst == X86Registers::eax) + if (dst == rax) m_formatter.oneByteOp64(OP_ADD_EAXIv); else m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_ADD); @@ -811,28 +267,28 @@ void lock_xaddb_rm(RegisterID srcdest, int32_t offset, RegisterID base) { - spew("lock xaddl %s, " MEM_ob, nameIReg(1, srcdest), ADDR_ob(offset, base)); + spew("lock xaddl %s, " MEM_ob, GPReg8Name(srcdest), ADDR_ob(offset, base)); m_formatter.oneByteOp(PRE_LOCK); m_formatter.twoByteOp(OP2_XADD_EbGb, offset, base, srcdest); } void lock_xaddb_rm(RegisterID srcdest, int32_t offset, RegisterID base, RegisterID index, int scale) { - spew("lock xaddl %s, " MEM_obs, nameIReg(1, srcdest), ADDR_obs(offset, base, index, scale)); + spew("lock xaddl %s, " MEM_obs, GPReg8Name(srcdest), ADDR_obs(offset, base, index, scale)); m_formatter.oneByteOp(PRE_LOCK); m_formatter.twoByteOp(OP2_XADD_EbGb, offset, base, index, scale, srcdest); } void lock_xaddl_rm(RegisterID srcdest, int32_t offset, RegisterID base) { - spew("lock xaddl %s, " MEM_ob, nameIReg(4,srcdest), ADDR_ob(offset, base)); + spew("lock xaddl %s, " MEM_ob, GPReg32Name(srcdest), ADDR_ob(offset, base)); m_formatter.oneByteOp(PRE_LOCK); m_formatter.twoByteOp(OP2_XADD_EvGv, offset, base, srcdest); } void lock_xaddl_rm(RegisterID srcdest, int32_t offset, RegisterID base, RegisterID index, int scale) { - spew("lock xaddl %s, " MEM_obs, nameIReg(4, srcdest), ADDR_obs(offset, base, index, scale)); + spew("lock xaddl %s, " MEM_obs, GPReg32Name(srcdest), ADDR_obs(offset, base, index, scale)); m_formatter.oneByteOp(PRE_LOCK); m_formatter.twoByteOp(OP2_XADD_EvGv, offset, base, index, scale, srcdest); } @@ -874,15 +330,15 @@ void vpmulld_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { - threeByteOpSimd("vpmulld", VEX_PD, OP3_PMULLD_VdqWdq, ESCAPE_PMULLD, src1, src0, dst); + threeByteOpSimd("vpmulld", VEX_PD, OP3_PMULLD_VdqWdq, ESCAPE_38, src1, src0, dst); } void vpmulld_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) { - threeByteOpSimd("vpmulld", VEX_PD, OP3_PMULLD_VdqWdq, ESCAPE_PMULLD, offset, base, src0, dst); + threeByteOpSimd("vpmulld", VEX_PD, OP3_PMULLD_VdqWdq, ESCAPE_38, offset, base, src0, dst); } void vpmulld_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) { - threeByteOpSimd("vpmulld", VEX_PD, OP3_PMULLD_VdqWdq, ESCAPE_PMULLD, address, src0, dst); + threeByteOpSimd("vpmulld", VEX_PD, OP3_PMULLD_VdqWdq, ESCAPE_38, address, src0, dst); } void vaddps_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) @@ -965,30 +421,30 @@ void andl_rr(RegisterID src, RegisterID dst) { - spew("andl %s, %s", nameIReg(4,src), nameIReg(4,dst)); + spew("andl %s, %s", GPReg32Name(src), GPReg32Name(dst)); m_formatter.oneByteOp(OP_AND_GvEv, src, dst); } void andl_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("andl " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(4,dst)); + spew("andl " MEM_ob ", %s", ADDR_ob(offset, base), GPReg32Name(dst)); m_formatter.oneByteOp(OP_AND_GvEv, offset, base, dst); } void andl_rm(RegisterID src, int32_t offset, RegisterID base) { - spew("andl %s, " MEM_ob, nameIReg(4,src), ADDR_ob(offset, base)); + spew("andl %s, " MEM_ob, GPReg32Name(src), ADDR_ob(offset, base)); m_formatter.oneByteOp(OP_AND_EvGv, offset, base, src); } void andl_ir(int32_t imm, RegisterID dst) { - spew("andl $0x%x, %s", imm, nameIReg(4,dst)); + spew("andl $0x%x, %s", imm, GPReg32Name(dst)); if (CAN_SIGN_EXTEND_8_32(imm)) { m_formatter.oneByteOp(OP_GROUP1_EvIb, dst, GROUP1_OP_AND); m_formatter.immediate8s(imm); } else { - if (dst == X86Registers::eax) + if (dst == rax) m_formatter.oneByteOp(OP_AND_EAXIv); else m_formatter.oneByteOp(OP_GROUP1_EvIz, dst, GROUP1_OP_AND); @@ -1011,48 +467,48 @@ #ifdef JS_CODEGEN_X64 void andq_rr(RegisterID src, RegisterID dst) { - spew("andq %s, %s", nameIReg(8,src), nameIReg(8,dst)); + spew("andq %s, %s", GPReg64Name(src), GPReg64Name(dst)); m_formatter.oneByteOp64(OP_AND_GvEv, src, dst); } void andq_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("andq " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(8,dst)); + spew("andq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst)); m_formatter.oneByteOp64(OP_AND_GvEv, offset, base, dst); } void andq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst) { - spew("andq " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), nameIReg(8,dst)); + spew("andq " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst)); m_formatter.oneByteOp64(OP_AND_GvEv, offset, base, index, scale, dst); } void andq_mr(const void *addr, RegisterID dst) { - spew("andq %p, %s", addr, nameIReg(8,dst)); + spew("andq %p, %s", addr, GPReg64Name(dst)); m_formatter.oneByteOp64(OP_AND_GvEv, addr, dst); } void orq_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("orq " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(8,dst)); + spew("orq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst)); m_formatter.oneByteOp64(OP_OR_GvEv, offset, base, dst); } void orq_mr(const void* addr, RegisterID dst) { - spew("orq %p, %s", addr, nameIReg(8, dst)); + spew("orq %p, %s", addr, GPReg64Name(dst)); m_formatter.oneByteOp64(OP_OR_GvEv, addr, dst); } void andq_ir(int32_t imm, RegisterID dst) { - spew("andq $0x%" PRIx64 ", %s", int64_t(imm), nameIReg(8,dst)); + spew("andq $0x%" PRIx64 ", %s", int64_t(imm), GPReg64Name(dst)); if (CAN_SIGN_EXTEND_8_32(imm)) { m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_AND); m_formatter.immediate8s(imm); } else { - if (dst == X86Registers::eax) + if (dst == rax) m_formatter.oneByteOp64(OP_AND_EAXIv); else m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_AND); @@ -1101,54 +557,54 @@ void negl_r(RegisterID dst) { - spew("negl %s", nameIReg(4,dst)); + spew("negl %s", GPReg32Name(dst)); m_formatter.oneByteOp(OP_GROUP3_Ev, dst, GROUP3_OP_NEG); } void negl_m(int32_t offset, RegisterID base) { - FIXME_INSN_PRINTING; + spew("negl " MEM_ob, ADDR_ob(offset, base)); m_formatter.oneByteOp(OP_GROUP3_Ev, offset, base, GROUP3_OP_NEG); } void notl_r(RegisterID dst) { - spew("notl %s", nameIReg(4,dst)); + spew("notl %s", GPReg32Name(dst)); m_formatter.oneByteOp(OP_GROUP3_Ev, dst, GROUP3_OP_NOT); } void notl_m(int32_t offset, RegisterID base) { - FIXME_INSN_PRINTING; + spew("notl " MEM_ob, ADDR_ob(offset, base)); m_formatter.oneByteOp(OP_GROUP3_Ev, offset, base, GROUP3_OP_NOT); } void orl_rr(RegisterID src, RegisterID dst) { - spew("orl %s, %s", nameIReg(4,src), nameIReg(4,dst)); + spew("orl %s, %s", GPReg32Name(src), GPReg32Name(dst)); m_formatter.oneByteOp(OP_OR_GvEv, src, dst); } void orl_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("orl " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(4,dst)); + spew("orl " MEM_ob ", %s", ADDR_ob(offset, base), GPReg32Name(dst)); m_formatter.oneByteOp(OP_OR_GvEv, offset, base, dst); } void orl_rm(RegisterID src, int32_t offset, RegisterID base) { - spew("orl %s, " MEM_ob, nameIReg(4,src), ADDR_ob(offset, base)); + spew("orl %s, " MEM_ob, GPReg32Name(src), ADDR_ob(offset, base)); m_formatter.oneByteOp(OP_OR_EvGv, offset, base, src); } void orl_ir(int32_t imm, RegisterID dst) { - spew("orl $0x%x, %s", imm, nameIReg(4,dst)); + spew("orl $0x%x, %s", imm, GPReg32Name(dst)); if (CAN_SIGN_EXTEND_8_32(imm)) { m_formatter.oneByteOp(OP_GROUP1_EvIb, dst, GROUP1_OP_OR); m_formatter.immediate8s(imm); } else { - if (dst == X86Registers::eax) + if (dst == rax) m_formatter.oneByteOp(OP_OR_EAXIv); else m_formatter.oneByteOp(OP_GROUP1_EvIz, dst, GROUP1_OP_OR); @@ -1171,24 +627,24 @@ #ifdef JS_CODEGEN_X64 void negq_r(RegisterID dst) { - spew("negq %s", nameIReg(8,dst)); + spew("negq %s", GPReg64Name(dst)); m_formatter.oneByteOp64(OP_GROUP3_Ev, dst, GROUP3_OP_NEG); } void orq_rr(RegisterID src, RegisterID dst) { - spew("orq %s, %s", nameIReg(8,src), nameIReg(8,dst)); + spew("orq %s, %s", GPReg64Name(src), GPReg64Name(dst)); m_formatter.oneByteOp64(OP_OR_GvEv, src, dst); } void orq_ir(int32_t imm, RegisterID dst) { - spew("orq $0x%" PRIx64 ", %s", int64_t(imm), nameIReg(8,dst)); + spew("orq $0x%" PRIx64 ", %s", int64_t(imm), GPReg64Name(dst)); if (CAN_SIGN_EXTEND_8_32(imm)) { m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_OR); m_formatter.immediate8s(imm); } else { - if (dst == X86Registers::eax) + if (dst == rax) m_formatter.oneByteOp64(OP_OR_EAXIv); else m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_OR); @@ -1198,13 +654,13 @@ void notq_r(RegisterID dst) { - spew("notq %s", nameIReg(8,dst)); + spew("notq %s", GPReg64Name(dst)); m_formatter.oneByteOp64(OP_GROUP3_Ev, dst, GROUP3_OP_NOT); } #else void orl_im(int32_t imm, const void* addr) { - FIXME_INSN_PRINTING; + spew("orl $0x%x, %p", imm, addr); if (CAN_SIGN_EXTEND_8_32(imm)) { m_formatter.oneByteOp(OP_GROUP1_EvIb, addr, GROUP1_OP_OR); m_formatter.immediate8s(imm); @@ -1217,30 +673,30 @@ void subl_rr(RegisterID src, RegisterID dst) { - spew("subl %s, %s", nameIReg(4,src), nameIReg(4,dst)); + spew("subl %s, %s", GPReg32Name(src), GPReg32Name(dst)); m_formatter.oneByteOp(OP_SUB_GvEv, src, dst); } void subl_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("subl " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(4,dst)); + spew("subl " MEM_ob ", %s", ADDR_ob(offset, base), GPReg32Name(dst)); m_formatter.oneByteOp(OP_SUB_GvEv, offset, base, dst); } void subl_rm(RegisterID src, int32_t offset, RegisterID base) { - spew("subl %s, " MEM_ob, nameIReg(4,src), ADDR_ob(offset, base)); + spew("subl %s, " MEM_ob, GPReg32Name(src), ADDR_ob(offset, base)); m_formatter.oneByteOp(OP_SUB_EvGv, offset, base, src); } void subl_ir(int32_t imm, RegisterID dst) { - spew("subl $%d, %s", imm, nameIReg(4, dst)); + spew("subl $%d, %s", imm, GPReg32Name(dst)); if (CAN_SIGN_EXTEND_8_32(imm)) { m_formatter.oneByteOp(OP_GROUP1_EvIb, dst, GROUP1_OP_SUB); m_formatter.immediate8s(imm); } else { - if (dst == X86Registers::eax) + if (dst == rax) m_formatter.oneByteOp(OP_SUB_EAXIv); else m_formatter.oneByteOp(OP_GROUP1_EvIz, dst, GROUP1_OP_SUB); @@ -1263,36 +719,36 @@ #ifdef JS_CODEGEN_X64 void subq_rr(RegisterID src, RegisterID dst) { - spew("subq %s, %s", nameIReg(8,src), nameIReg(8,dst)); + spew("subq %s, %s", GPReg64Name(src), GPReg64Name(dst)); m_formatter.oneByteOp64(OP_SUB_GvEv, src, dst); } void subq_rm(RegisterID src, int32_t offset, RegisterID base) { - spew("subq %s, " MEM_ob, nameIReg(8,src), ADDR_ob(offset, base)); + spew("subq %s, " MEM_ob, GPReg64Name(src), ADDR_ob(offset, base)); m_formatter.oneByteOp64(OP_SUB_EvGv, offset, base, src); } void subq_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("subq " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(8,dst)); + spew("subq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst)); m_formatter.oneByteOp64(OP_SUB_GvEv, offset, base, dst); } void subq_mr(const void* addr, RegisterID dst) { - spew("subq %p, %s", addr, nameIReg(8, dst)); + spew("subq %p, %s", addr, GPReg64Name(dst)); m_formatter.oneByteOp64(OP_SUB_GvEv, addr, dst); } void subq_ir(int32_t imm, RegisterID dst) { - spew("subq $%d, %s", imm, nameIReg(8,dst)); + spew("subq $%d, %s", imm, GPReg64Name(dst)); if (CAN_SIGN_EXTEND_8_32(imm)) { m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_SUB); m_formatter.immediate8s(imm); } else { - if (dst == X86Registers::eax) + if (dst == rax) m_formatter.oneByteOp64(OP_SUB_EAXIv); else m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_SUB); @@ -1302,7 +758,7 @@ #else void subl_im(int32_t imm, const void* addr) { - FIXME_INSN_PRINTING; + spew("subl $%d, %p", imm, addr); if (CAN_SIGN_EXTEND_8_32(imm)) { m_formatter.oneByteOp(OP_GROUP1_EvIb, addr, GROUP1_OP_SUB); m_formatter.immediate8s(imm); @@ -1315,19 +771,19 @@ void xorl_rr(RegisterID src, RegisterID dst) { - spew("xorl %s, %s", nameIReg(4,src), nameIReg(4,dst)); + spew("xorl %s, %s", GPReg32Name(src), GPReg32Name(dst)); m_formatter.oneByteOp(OP_XOR_GvEv, src, dst); } void xorl_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("xorl " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(4,dst)); + spew("xorl " MEM_ob ", %s", ADDR_ob(offset, base), GPReg32Name(dst)); m_formatter.oneByteOp(OP_XOR_GvEv, offset, base, dst); } void xorl_rm(RegisterID src, int32_t offset, RegisterID base) { - spew("xorl %s, " MEM_ob, nameIReg(4,src), ADDR_ob(offset, base)); + spew("xorl %s, " MEM_ob, GPReg32Name(src), ADDR_ob(offset, base)); m_formatter.oneByteOp(OP_XOR_EvGv, offset, base, src); } @@ -1345,12 +801,12 @@ void xorl_ir(int32_t imm, RegisterID dst) { - spew("xorl $%d, %s", imm, nameIReg(4,dst)); + spew("xorl $%d, %s", imm, GPReg32Name(dst)); if (CAN_SIGN_EXTEND_8_32(imm)) { m_formatter.oneByteOp(OP_GROUP1_EvIb, dst, GROUP1_OP_XOR); m_formatter.immediate8s(imm); } else { - if (dst == X86Registers::eax) + if (dst == rax) m_formatter.oneByteOp(OP_XOR_EAXIv); else m_formatter.oneByteOp(OP_GROUP1_EvIz, dst, GROUP1_OP_XOR); @@ -1361,18 +817,18 @@ #ifdef JS_CODEGEN_X64 void xorq_rr(RegisterID src, RegisterID dst) { - spew("xorq %s, %s", nameIReg(8,src), nameIReg(8, dst)); + spew("xorq %s, %s", GPReg64Name(src), GPReg64Name(dst)); m_formatter.oneByteOp64(OP_XOR_GvEv, src, dst); } void xorq_ir(int32_t imm, RegisterID dst) { - spew("xorq $0x%" PRIx64 ", %s", int64_t(imm), nameIReg(8,dst)); + spew("xorq $0x%" PRIx64 ", %s", int64_t(imm), GPReg64Name(dst)); if (CAN_SIGN_EXTEND_8_32(imm)) { m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_XOR); m_formatter.immediate8s(imm); } else { - if (dst == X86Registers::eax) + if (dst == rax) m_formatter.oneByteOp64(OP_XOR_EAXIv); else m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_XOR); @@ -1384,7 +840,7 @@ void sarl_ir(int32_t imm, RegisterID dst) { MOZ_ASSERT(imm < 32); - spew("sarl $%d, %s", imm, nameIReg(4, dst)); + spew("sarl $%d, %s", imm, GPReg32Name(dst)); if (imm == 1) m_formatter.oneByteOp(OP_GROUP2_Ev1, dst, GROUP2_OP_SAR); else { @@ -1395,14 +851,14 @@ void sarl_CLr(RegisterID dst) { - spew("sarl %%cl, %s", nameIReg(4, dst)); + spew("sarl %%cl, %s", GPReg32Name(dst)); m_formatter.oneByteOp(OP_GROUP2_EvCL, dst, GROUP2_OP_SAR); } void shrl_ir(int32_t imm, RegisterID dst) { MOZ_ASSERT(imm < 32); - spew("shrl $%d, %s", imm, nameIReg(4, dst)); + spew("shrl $%d, %s", imm, GPReg32Name(dst)); if (imm == 1) m_formatter.oneByteOp(OP_GROUP2_Ev1, dst, GROUP2_OP_SHR); else { @@ -1413,14 +869,14 @@ void shrl_CLr(RegisterID dst) { - spew("shrl %%cl, %s", nameIReg(4, dst)); + spew("shrl %%cl, %s", GPReg32Name(dst)); m_formatter.oneByteOp(OP_GROUP2_EvCL, dst, GROUP2_OP_SHR); } void shll_ir(int32_t imm, RegisterID dst) { MOZ_ASSERT(imm < 32); - spew("shll $%d, %s", imm, nameIReg(4, dst)); + spew("shll $%d, %s", imm, GPReg32Name(dst)); if (imm == 1) m_formatter.oneByteOp(OP_GROUP2_Ev1, dst, GROUP2_OP_SHL); else { @@ -1431,21 +887,21 @@ void shll_CLr(RegisterID dst) { - spew("shll %%cl, %s", nameIReg(4, dst)); + spew("shll %%cl, %s", GPReg32Name(dst)); m_formatter.oneByteOp(OP_GROUP2_EvCL, dst, GROUP2_OP_SHL); } #ifdef JS_CODEGEN_X64 void sarq_CLr(RegisterID dst) { - FIXME_INSN_PRINTING; + spew("sarq %%cl, %s", GPReg64Name(dst)); m_formatter.oneByteOp64(OP_GROUP2_EvCL, dst, GROUP2_OP_SAR); } void sarq_ir(int32_t imm, RegisterID dst) { MOZ_ASSERT(imm < 64); - spew("sarq $%d, %s", imm, nameIReg(8, dst)); + spew("sarq $%d, %s", imm, GPReg64Name(dst)); if (imm == 1) m_formatter.oneByteOp64(OP_GROUP2_Ev1, dst, GROUP2_OP_SAR); else { @@ -1457,7 +913,7 @@ void shlq_ir(int32_t imm, RegisterID dst) { MOZ_ASSERT(imm < 64); - spew("shlq $%d, %s", imm, nameIReg(8, dst)); + spew("shlq $%d, %s", imm, GPReg64Name(dst)); if (imm == 1) m_formatter.oneByteOp64(OP_GROUP2_Ev1, dst, GROUP2_OP_SHL); else { @@ -1469,7 +925,7 @@ void shrq_ir(int32_t imm, RegisterID dst) { MOZ_ASSERT(imm < 64); - spew("shrq $%d, %s", imm, nameIReg(8, dst)); + spew("shrq $%d, %s", imm, GPReg64Name(dst)); if (imm == 1) m_formatter.oneByteOp64(OP_GROUP2_Ev1, dst, GROUP2_OP_SHR); else { @@ -1481,31 +937,31 @@ void bsr_rr(RegisterID src, RegisterID dst) { - spew("bsr %s, %s", nameIReg(4, src), nameIReg(4, dst)); + spew("bsr %s, %s", GPReg32Name(src), GPReg32Name(dst)); m_formatter.twoByteOp(OP2_BSR_GvEv, src, dst); } void imull_rr(RegisterID src, RegisterID dst) { - spew("imull %s, %s", nameIReg(4,src), nameIReg(4, dst)); + spew("imull %s, %s", GPReg32Name(src), GPReg32Name(dst)); m_formatter.twoByteOp(OP2_IMUL_GvEv, src, dst); } void imull_r(RegisterID multiplier) { - spew("imull %s", nameIReg(4, multiplier)); + spew("imull %s", GPReg32Name(multiplier)); m_formatter.oneByteOp(OP_GROUP3_Ev, multiplier, GROUP3_OP_IMUL); } void imull_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("imull " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(4,dst)); + spew("imull " MEM_ob ", %s", ADDR_ob(offset, base), GPReg32Name(dst)); m_formatter.twoByteOp(OP2_IMUL_GvEv, offset, base, dst); } void imull_ir(int32_t value, RegisterID src, RegisterID dst) { - spew("imull $%d, %s, %s", value, nameIReg(4, src), nameIReg(4, dst)); + spew("imull $%d, %s, %s", value, GPReg32Name(src), GPReg32Name(dst)); if (CAN_SIGN_EXTEND_8_32(value)) { m_formatter.oneByteOp(OP_IMUL_GvEvIb, src, dst); m_formatter.immediate8s(value); @@ -1517,13 +973,13 @@ void idivl_r(RegisterID divisor) { - spew("idivl %s", nameIReg(4, divisor)); + spew("idivl %s", GPReg32Name(divisor)); m_formatter.oneByteOp(OP_GROUP3_Ev, divisor, GROUP3_OP_IDIV); } void divl_r(RegisterID divisor) { - spew("div %s", nameIReg(4, divisor)); + spew("div %s", GPReg32Name(divisor)); m_formatter.oneByteOp(OP_GROUP3_Ev, divisor, GROUP3_OP_DIV); } @@ -1557,34 +1013,34 @@ void cmpxchg8(RegisterID src, int32_t offset, RegisterID base) { - spew("cmpxchg8 %s, " MEM_ob, nameIReg(src), ADDR_ob(offset, base)); + spew("cmpxchg8 %s, " MEM_ob, GPRegName(src), ADDR_ob(offset, base)); m_formatter.twoByteOp(OP2_CMPXCHG_GvEb, offset, base, src); } void cmpxchg8(RegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale) { - spew("cmpxchg8 %s, " MEM_obs, nameIReg(src), ADDR_obs(offset, base, index, scale)); + spew("cmpxchg8 %s, " MEM_obs, GPRegName(src), ADDR_obs(offset, base, index, scale)); m_formatter.twoByteOp(OP2_CMPXCHG_GvEb, offset, base, index, scale, src); } void cmpxchg16(RegisterID src, int32_t offset, RegisterID base) { - spew("cmpxchg16 %s, " MEM_ob, nameIReg(src), ADDR_ob(offset, base)); + spew("cmpxchg16 %s, " MEM_ob, GPRegName(src), ADDR_ob(offset, base)); m_formatter.prefix(PRE_OPERAND_SIZE); m_formatter.twoByteOp(OP2_CMPXCHG_GvEw, offset, base, src); } void cmpxchg16(RegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale) { - spew("cmpxchg16 %s, " MEM_obs, nameIReg(src), ADDR_obs(offset, base, index, scale)); + spew("cmpxchg16 %s, " MEM_obs, GPRegName(src), ADDR_obs(offset, base, index, scale)); m_formatter.prefix(PRE_OPERAND_SIZE); m_formatter.twoByteOp(OP2_CMPXCHG_GvEw, offset, base, index, scale, src); } void cmpxchg32(RegisterID src, int32_t offset, RegisterID base) { - spew("cmpxchg32 %s, " MEM_ob, nameIReg(src), ADDR_ob(offset, base)); + spew("cmpxchg32 %s, " MEM_ob, GPRegName(src), ADDR_ob(offset, base)); m_formatter.twoByteOp(OP2_CMPXCHG_GvEw, offset, base, src); } void cmpxchg32(RegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale) { - spew("cmpxchg32 %s, " MEM_obs, nameIReg(src), ADDR_obs(offset, base, index, scale)); + spew("cmpxchg32 %s, " MEM_obs, GPRegName(src), ADDR_obs(offset, base, index, scale)); m_formatter.twoByteOp(OP2_CMPXCHG_GvEw, offset, base, index, scale, src); } @@ -1593,41 +1049,41 @@ void cmpl_rr(RegisterID rhs, RegisterID lhs) { - spew("cmpl %s, %s", nameIReg(4, rhs), nameIReg(4, lhs)); + spew("cmpl %s, %s", GPReg32Name(rhs), GPReg32Name(lhs)); m_formatter.oneByteOp(OP_CMP_GvEv, rhs, lhs); } void cmpl_rm(RegisterID rhs, int32_t offset, RegisterID base) { - spew("cmpl %s, " MEM_ob, nameIReg(4, rhs), ADDR_ob(offset, base)); + spew("cmpl %s, " MEM_ob, GPReg32Name(rhs), ADDR_ob(offset, base)); m_formatter.oneByteOp(OP_CMP_EvGv, offset, base, rhs); } void cmpl_mr(int32_t offset, RegisterID base, RegisterID lhs) { - spew("cmpl " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(4, lhs)); + spew("cmpl " MEM_ob ", %s", ADDR_ob(offset, base), GPReg32Name(lhs)); m_formatter.oneByteOp(OP_CMP_GvEv, offset, base, lhs); } void cmpl_mr(const void *address, RegisterID lhs) { - spew("cmpl %p, %s", address, nameIReg(4, lhs)); + spew("cmpl %p, %s", address, GPReg32Name(lhs)); m_formatter.oneByteOp(OP_CMP_GvEv, address, lhs); } - void cmpl_ir(int rhs, RegisterID lhs) + void cmpl_ir(int32_t rhs, RegisterID lhs) { if (rhs == 0) { testl_rr(lhs, lhs); return; } - spew("cmpl $0x%x, %s", rhs, nameIReg(4, lhs)); + spew("cmpl $0x%x, %s", rhs, GPReg32Name(lhs)); if (CAN_SIGN_EXTEND_8_32(rhs)) { m_formatter.oneByteOp(OP_GROUP1_EvIb, lhs, GROUP1_OP_CMP); m_formatter.immediate8s(rhs); } else { - if (lhs == X86Registers::eax) + if (lhs == rax) m_formatter.oneByteOp(OP_CMP_EAXIv); else m_formatter.oneByteOp(OP_GROUP1_EvIz, lhs, GROUP1_OP_CMP); @@ -1635,17 +1091,17 @@ } } - void cmpl_i32r(int rhs, RegisterID lhs) + void cmpl_i32r(int32_t rhs, RegisterID lhs) { - spew("cmpl $0x%04x, %s", rhs, nameIReg(4, lhs)); - if (lhs == X86Registers::eax) + spew("cmpl $0x%04x, %s", rhs, GPReg32Name(lhs)); + if (lhs == rax) m_formatter.oneByteOp(OP_CMP_EAXIv); else m_formatter.oneByteOp(OP_GROUP1_EvIz, lhs, GROUP1_OP_CMP); m_formatter.immediate32(rhs); } - void cmpl_im(int rhs, int32_t offset, RegisterID base) + void cmpl_im(int32_t rhs, int32_t offset, RegisterID base) { spew("cmpl $0x%x, " MEM_ob, rhs, ADDR_ob(offset, base)); if (CAN_SIGN_EXTEND_8_32(rhs)) { @@ -1657,21 +1113,21 @@ } } - void cmpb_im(int rhs, int32_t offset, RegisterID base) + void cmpb_im(int32_t rhs, int32_t offset, RegisterID base) { spew("cmpb $0x%x, " MEM_ob, rhs, ADDR_ob(offset, base)); m_formatter.oneByteOp(OP_GROUP1_EbIb, offset, base, GROUP1_OP_CMP); m_formatter.immediate8(rhs); } - void cmpb_im(int rhs, int32_t offset, RegisterID base, RegisterID index, int scale) + void cmpb_im(int32_t rhs, int32_t offset, RegisterID base, RegisterID index, int scale) { spew("cmpb $0x%x, " MEM_obs, rhs, ADDR_obs(offset, base, index, scale)); m_formatter.oneByteOp(OP_GROUP1_EbIb, offset, base, index, scale, GROUP1_OP_CMP); m_formatter.immediate8(rhs); } - void cmpl_im(int rhs, int32_t offset, RegisterID base, RegisterID index, int scale) + void cmpl_im(int32_t rhs, int32_t offset, RegisterID base, RegisterID index, int scale) { spew("cmpl $0x%x, " MEM_o32b, rhs, ADDR_o32b(offset, base)); if (CAN_SIGN_EXTEND_8_32(rhs)) { @@ -1684,7 +1140,7 @@ } MOZ_WARN_UNUSED_RESULT JmpSrc - cmpl_im_disp32(int rhs, int32_t offset, RegisterID base) + cmpl_im_disp32(int32_t rhs, int32_t offset, RegisterID base) { spew("cmpl $0x%x, " MEM_o32b, rhs, ADDR_o32b(offset, base)); JmpSrc r; @@ -1701,7 +1157,7 @@ } MOZ_WARN_UNUSED_RESULT JmpSrc - cmpl_im_disp32(int rhs, const void *addr) + cmpl_im_disp32(int32_t rhs, const void *addr) { spew("cmpl $0x%x, %p", rhs, addr); JmpSrc r; @@ -1717,14 +1173,14 @@ return r; } - void cmpl_i32m(int rhs, int32_t offset, RegisterID base) + void cmpl_i32m(int32_t rhs, int32_t offset, RegisterID base) { spew("cmpl $0x%04x, " MEM_ob, rhs, ADDR_ob(offset, base)); m_formatter.oneByteOp(OP_GROUP1_EvIz, offset, base, GROUP1_OP_CMP); m_formatter.immediate32(rhs); } - void cmpl_i32m(int rhs, const void *addr) + void cmpl_i32m(int32_t rhs, const void *addr) { spew("cmpl $0x%04x, %p", rhs, addr); m_formatter.oneByteOp(OP_GROUP1_EvIz, addr, GROUP1_OP_CMP); @@ -1734,35 +1190,35 @@ #ifdef JS_CODEGEN_X64 void cmpq_rr(RegisterID rhs, RegisterID lhs) { - spew("cmpq %s, %s", nameIReg(8, rhs), nameIReg(8, lhs)); + spew("cmpq %s, %s", GPReg64Name(rhs), GPReg64Name(lhs)); m_formatter.oneByteOp64(OP_CMP_GvEv, rhs, lhs); } void cmpq_rm(RegisterID rhs, int32_t offset, RegisterID base) { - spew("cmpq %s, " MEM_ob, nameIReg(8, rhs), ADDR_ob(offset, base)); + spew("cmpq %s, " MEM_ob, GPReg64Name(rhs), ADDR_ob(offset, base)); m_formatter.oneByteOp64(OP_CMP_EvGv, offset, base, rhs); } void cmpq_mr(int32_t offset, RegisterID base, RegisterID lhs) { - spew("cmpq " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(8, lhs)); + spew("cmpq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(lhs)); m_formatter.oneByteOp64(OP_CMP_GvEv, offset, base, lhs); } - void cmpq_ir(int rhs, RegisterID lhs) + void cmpq_ir(int32_t rhs, RegisterID lhs) { if (rhs == 0) { testq_rr(lhs, lhs); return; } - spew("cmpq $0x%" PRIx64 ", %s", int64_t(rhs), nameIReg(8, lhs)); + spew("cmpq $0x%" PRIx64 ", %s", int64_t(rhs), GPReg64Name(lhs)); if (CAN_SIGN_EXTEND_8_32(rhs)) { m_formatter.oneByteOp64(OP_GROUP1_EvIb, lhs, GROUP1_OP_CMP); m_formatter.immediate8s(rhs); } else { - if (lhs == X86Registers::eax) + if (lhs == rax) m_formatter.oneByteOp64(OP_CMP_EAXIv); else m_formatter.oneByteOp64(OP_GROUP1_EvIz, lhs, GROUP1_OP_CMP); @@ -1770,7 +1226,7 @@ } } - void cmpq_im(int rhs, int32_t offset, RegisterID base) + void cmpq_im(int32_t rhs, int32_t offset, RegisterID base) { spew("cmpq $0x%" PRIx64 ", " MEM_ob, int64_t(rhs), ADDR_ob(offset, base)); if (CAN_SIGN_EXTEND_8_32(rhs)) { @@ -1782,9 +1238,9 @@ } } - void cmpq_im(int rhs, int32_t offset, RegisterID base, RegisterID index, int scale) + void cmpq_im(int32_t rhs, int32_t offset, RegisterID base, RegisterID index, int scale) { - FIXME_INSN_PRINTING; + spew("cmpq $0x%x, " MEM_obs, rhs, ADDR_obs(offset, base, index, scale)); if (CAN_SIGN_EXTEND_8_32(rhs)) { m_formatter.oneByteOp64(OP_GROUP1_EvIb, offset, base, index, scale, GROUP1_OP_CMP); m_formatter.immediate8s(rhs); @@ -1793,7 +1249,7 @@ m_formatter.immediate32(rhs); } } - void cmpq_im(int rhs, const void* addr) + void cmpq_im(int32_t rhs, const void* addr) { spew("cmpq $0x%" PRIx64 ", %p", int64_t(rhs), addr); if (CAN_SIGN_EXTEND_8_32(rhs)) { @@ -1806,23 +1262,23 @@ } void cmpq_rm(RegisterID rhs, const void* addr) { - spew("cmpq %s, %p", nameIReg(8, rhs), addr); + spew("cmpq %s, %p", GPReg64Name(rhs), addr); m_formatter.oneByteOp64(OP_CMP_EvGv, addr, rhs); } #endif void cmpl_rm(RegisterID rhs, const void* addr) { - spew("cmpl %s, %p", nameIReg(4, rhs), addr); + spew("cmpl %s, %p", GPReg32Name(rhs), addr); m_formatter.oneByteOp(OP_CMP_EvGv, addr, rhs); } void cmpl_rm_disp32(RegisterID rhs, const void* addr) { - spew("cmpl %s, %p", nameIReg(4, rhs), addr); + spew("cmpl %s, %p", GPReg32Name(rhs), addr); m_formatter.oneByteOp_disp32(OP_CMP_EvGv, addr, rhs); } - void cmpl_im(int rhs, const void* addr) + void cmpl_im(int32_t rhs, const void* addr) { spew("cmpl $0x%x, %p", rhs, addr); if (CAN_SIGN_EXTEND_8_32(rhs)) { @@ -1836,21 +1292,21 @@ void cmpw_rr(RegisterID rhs, RegisterID lhs) { - spew("cmpw %s, %s", nameIReg(2, rhs), nameIReg(2, lhs)); + spew("cmpw %s, %s", GPReg16Name(rhs), GPReg16Name(lhs)); m_formatter.prefix(PRE_OPERAND_SIZE); m_formatter.oneByteOp(OP_CMP_GvEv, rhs, lhs); } void cmpw_rm(RegisterID rhs, int32_t offset, RegisterID base, RegisterID index, int scale) { - FIXME_INSN_PRINTING; + spew("cmpw %s, " MEM_obs, GPReg16Name(rhs), ADDR_obs(offset, base, index, scale)); m_formatter.prefix(PRE_OPERAND_SIZE); m_formatter.oneByteOp(OP_CMP_EvGv, offset, base, index, scale, rhs); } void cmpw_im(int32_t imm, int32_t offset, RegisterID base, RegisterID index, int scale) { - FIXME_INSN_PRINTING; + spew("cmpw $%d, " MEM_obs, imm, ADDR_obs(offset, base, index, scale)); if (CAN_SIGN_EXTEND_8_32(imm)) { m_formatter.prefix(PRE_OPERAND_SIZE); m_formatter.oneByteOp(OP_GROUP1_EvIb, offset, base, index, scale, GROUP1_OP_CMP); @@ -1864,69 +1320,69 @@ void testl_rr(RegisterID rhs, RegisterID lhs) { - spew("testl %s, %s", nameIReg(4, rhs), nameIReg(4, lhs)); + spew("testl %s, %s", GPReg32Name(rhs), GPReg32Name(lhs)); m_formatter.oneByteOp(OP_TEST_EvGv, lhs, rhs); } void testb_rr(RegisterID rhs, RegisterID lhs) { - spew("testb %s, %s", nameIReg(1, rhs), nameIReg(1, lhs)); + spew("testb %s, %s", GPReg8Name(rhs), GPReg8Name(lhs)); m_formatter.oneByteOp(OP_TEST_EbGb, lhs, rhs); } - void testl_ir(int rhs, RegisterID lhs) + void testl_ir(int32_t rhs, RegisterID lhs) { // If the mask fits in an 8-bit immediate, we can use testb with an // 8-bit subreg. - if (CAN_ZERO_EXTEND_8_32(rhs) && X86Registers::hasSubregL(lhs)) { + if (CAN_ZERO_EXTEND_8_32(rhs) && HasSubregL(lhs)) { testb_ir(rhs, lhs); return; } // If the mask is a subset of 0xff00, we can use testb with an h reg, if // one happens to be available. - if (CAN_ZERO_EXTEND_8H_32(rhs) && X86Registers::hasSubregH(lhs)) { - testb_ir_norex(rhs >> 8, X86Registers::getSubregH(lhs)); + if (CAN_ZERO_EXTEND_8H_32(rhs) && HasSubregH(lhs)) { + testb_ir_norex(rhs >> 8, GetSubregH(lhs)); return; } - spew("testl $0x%x, %s", rhs, nameIReg(4, lhs)); - if (lhs == X86Registers::eax) + spew("testl $0x%x, %s", rhs, GPReg32Name(lhs)); + if (lhs == rax) m_formatter.oneByteOp(OP_TEST_EAXIv); else m_formatter.oneByteOp(OP_GROUP3_EvIz, lhs, GROUP3_OP_TEST); m_formatter.immediate32(rhs); } - void testl_i32m(int rhs, int32_t offset, RegisterID base) + void testl_i32m(int32_t rhs, int32_t offset, RegisterID base) { spew("testl $0x%x, " MEM_ob, rhs, ADDR_ob(offset, base)); m_formatter.oneByteOp(OP_GROUP3_EvIz, offset, base, GROUP3_OP_TEST); m_formatter.immediate32(rhs); } - void testl_i32m(int rhs, const void *addr) + void testl_i32m(int32_t rhs, const void *addr) { spew("testl $0x%x, %p", rhs, addr); m_formatter.oneByteOp(OP_GROUP3_EvIz, addr, GROUP3_OP_TEST); m_formatter.immediate32(rhs); } - void testb_im(int rhs, int32_t offset, RegisterID base) + void testb_im(int32_t rhs, int32_t offset, RegisterID base) { - FIXME_INSN_PRINTING; + spew("testb $0x%x, " MEM_ob, rhs, ADDR_ob(offset, base)); m_formatter.oneByteOp(OP_GROUP3_EbIb, offset, base, GROUP3_OP_TEST); m_formatter.immediate8(rhs); } - void testb_im(int rhs, int32_t offset, RegisterID base, RegisterID index, int scale) + void testb_im(int32_t rhs, int32_t offset, RegisterID base, RegisterID index, int scale) { - FIXME_INSN_PRINTING; + spew("testb $0x%x, " MEM_obs, rhs, ADDR_obs(offset, base, index, scale)); m_formatter.oneByteOp(OP_GROUP3_EbIb, offset, base, index, scale, GROUP3_OP_TEST); m_formatter.immediate8(rhs); } - void testl_i32m(int rhs, int32_t offset, RegisterID base, RegisterID index, int scale) + void testl_i32m(int32_t rhs, int32_t offset, RegisterID base, RegisterID index, int scale) { - FIXME_INSN_PRINTING; + spew("testl $0x%4x, " MEM_obs, rhs, ADDR_obs(offset, base, index, scale)); m_formatter.oneByteOp(OP_GROUP3_EvIz, offset, base, index, scale, GROUP3_OP_TEST); m_formatter.immediate32(rhs); } @@ -1934,11 +1390,11 @@ #ifdef JS_CODEGEN_X64 void testq_rr(RegisterID rhs, RegisterID lhs) { - spew("testq %s, %s", nameIReg(8, rhs), nameIReg(8, lhs)); + spew("testq %s, %s", GPReg64Name(rhs), GPReg64Name(lhs)); m_formatter.oneByteOp64(OP_TEST_EvGv, lhs, rhs); } - void testq_ir(int rhs, RegisterID lhs) + void testq_ir(int32_t rhs, RegisterID lhs) { // If the mask fits in a 32-bit immediate, we can use testl with a // 32-bit subreg. @@ -1946,24 +1402,24 @@ testl_ir(rhs, lhs); return; } - spew("testq $0x%" PRIx64 ", %s", int64_t(rhs), nameIReg(8, lhs)); - if (lhs == X86Registers::eax) + spew("testq $0x%" PRIx64 ", %s", int64_t(rhs), GPReg64Name(lhs)); + if (lhs == rax) m_formatter.oneByteOp64(OP_TEST_EAXIv); else m_formatter.oneByteOp64(OP_GROUP3_EvIz, lhs, GROUP3_OP_TEST); m_formatter.immediate32(rhs); } - void testq_i32m(int rhs, int32_t offset, RegisterID base) + void testq_i32m(int32_t rhs, int32_t offset, RegisterID base) { spew("testq $0x%" PRIx64 ", " MEM_ob, int64_t(rhs), ADDR_ob(offset, base)); m_formatter.oneByteOp64(OP_GROUP3_EvIz, offset, base, GROUP3_OP_TEST); m_formatter.immediate32(rhs); } - void testq_i32m(int rhs, int32_t offset, RegisterID base, RegisterID index, int scale) + void testq_i32m(int32_t rhs, int32_t offset, RegisterID base, RegisterID index, int scale) { - FIXME_INSN_PRINTING; + spew("testq $0x%4x, " MEM_obs, rhs, ADDR_obs(offset, base, index, scale)); m_formatter.oneByteOp64(OP_GROUP3_EvIz, offset, base, index, scale, GROUP3_OP_TEST); m_formatter.immediate32(rhs); } @@ -1971,15 +1427,15 @@ void testw_rr(RegisterID rhs, RegisterID lhs) { - FIXME_INSN_PRINTING; + spew("testw %s, %s", GPReg16Name(rhs), GPReg16Name(lhs)); m_formatter.prefix(PRE_OPERAND_SIZE); m_formatter.oneByteOp(OP_TEST_EvGv, lhs, rhs); } - void testb_ir(int rhs, RegisterID lhs) + void testb_ir(int32_t rhs, RegisterID lhs) { - spew("testb $0x%x, %s", rhs, nameIReg(1, lhs)); - if (lhs == X86Registers::eax) + spew("testb $0x%x, %s", rhs, GPReg8Name(lhs)); + if (lhs == rax) m_formatter.oneByteOp8(OP_TEST_EAXIb); else m_formatter.oneByteOp8(OP_GROUP3_EbIb, lhs, GROUP3_OP_TEST); @@ -1988,16 +1444,16 @@ // Like testb_ir, but never emits a REX prefix. This may be used to // reference ah..bh. - void testb_ir_norex(int rhs, RegisterID lhs) + void testb_ir_norex(int32_t rhs, HRegisterID lhs) { - spew("testb $0x%x, %s", rhs, nameI8Reg_norex(lhs)); + spew("testb $0x%x, %s", rhs, HRegName8(lhs)); m_formatter.oneByteOp8_norex(OP_GROUP3_EbIb, lhs, GROUP3_OP_TEST); m_formatter.immediate8(rhs); } void setCC_r(Condition cond, RegisterID lhs) { - spew("set%s %s", nameCC(cond), nameIReg(1, lhs)); + spew("set%s %s", CCName(cond), GPReg8Name(lhs)); m_formatter.twoByteOp8(setccOpcode(cond), lhs, (GroupOpcodeID)0); } @@ -2031,75 +1487,75 @@ void xchgl_rr(RegisterID src, RegisterID dst) { - spew("xchgl %s, %s", nameIReg(4,src), nameIReg(4,dst)); + spew("xchgl %s, %s", GPReg32Name(src), GPReg32Name(dst)); m_formatter.oneByteOp(OP_XCHG_GvEv, src, dst); } #ifdef JS_CODEGEN_X64 void xchgq_rr(RegisterID src, RegisterID dst) { - spew("xchgq %s, %s", nameIReg(8,src), nameIReg(8,dst)); + spew("xchgq %s, %s", GPReg64Name(src), GPReg64Name(dst)); m_formatter.oneByteOp64(OP_XCHG_GvEv, src, dst); } #endif void movl_rr(RegisterID src, RegisterID dst) { - spew("movl %s, %s", nameIReg(4,src), nameIReg(4,dst)); + spew("movl %s, %s", GPReg32Name(src), GPReg32Name(dst)); m_formatter.oneByteOp(OP_MOV_GvEv, src, dst); } void movw_rm(RegisterID src, int32_t offset, RegisterID base) { - spew("movw %s, " MEM_ob, nameIReg(2,src), ADDR_ob(offset, base)); + spew("movw %s, " MEM_ob, GPReg16Name(src), ADDR_ob(offset, base)); m_formatter.prefix(PRE_OPERAND_SIZE); m_formatter.oneByteOp(OP_MOV_EvGv, offset, base, src); } void movw_rm_disp32(RegisterID src, int32_t offset, RegisterID base) { - spew("movw %s, " MEM_o32b, nameIReg(2,src), ADDR_o32b(offset, base)); + spew("movw %s, " MEM_o32b, GPReg16Name(src), ADDR_o32b(offset, base)); m_formatter.prefix(PRE_OPERAND_SIZE); m_formatter.oneByteOp_disp32(OP_MOV_EvGv, offset, base, src); } void movw_rm(RegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale) { - spew("movw %s, " MEM_obs, nameIReg(2, src), ADDR_obs(offset, base, index, scale)); + spew("movw %s, " MEM_obs, GPReg16Name(src), ADDR_obs(offset, base, index, scale)); m_formatter.prefix(PRE_OPERAND_SIZE); m_formatter.oneByteOp(OP_MOV_EvGv, offset, base, index, scale, src); } void movw_rm(RegisterID src, const void* addr) { - spew("movw %s, %p", nameIReg(2, src), addr); + spew("movw %s, %p", GPReg16Name(src), addr); m_formatter.prefix(PRE_OPERAND_SIZE); m_formatter.oneByteOp_disp32(OP_MOV_EvGv, addr, src); } void movl_rm(RegisterID src, int32_t offset, RegisterID base) { - spew("movl %s, " MEM_ob, nameIReg(4,src), ADDR_ob(offset, base)); + spew("movl %s, " MEM_ob, GPReg32Name(src), ADDR_ob(offset, base)); m_formatter.oneByteOp(OP_MOV_EvGv, offset, base, src); } void movl_rm_disp32(RegisterID src, int32_t offset, RegisterID base) { - spew("movl %s, " MEM_o32b, nameIReg(4,src), ADDR_o32b(offset, base)); + spew("movl %s, " MEM_o32b, GPReg32Name(src), ADDR_o32b(offset, base)); m_formatter.oneByteOp_disp32(OP_MOV_EvGv, offset, base, src); } void movl_rm(RegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale) { - spew("movl %s, " MEM_obs, nameIReg(4, src), ADDR_obs(offset, base, index, scale)); + spew("movl %s, " MEM_obs, GPReg32Name(src), ADDR_obs(offset, base, index, scale)); m_formatter.oneByteOp(OP_MOV_EvGv, offset, base, index, scale, src); } void movl_mEAX(const void* addr) { #ifdef JS_CODEGEN_X64 - if (isAddressImmediate(addr)) { - movl_mr(addr, X86Registers::eax); + if (IsAddressImmediate(addr)) { + movl_mr(addr, rax); return; } #endif @@ -2113,41 +1569,41 @@ #ifdef JS_CODEGEN_X64 m_formatter.immediate64(reinterpret_cast(addr)); #else - m_formatter.immediate32(reinterpret_cast(addr)); + m_formatter.immediate32(reinterpret_cast(addr)); #endif } void movl_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("movl " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(4, dst)); + spew("movl " MEM_ob ", %s", ADDR_ob(offset, base), GPReg32Name(dst)); m_formatter.oneByteOp(OP_MOV_GvEv, offset, base, dst); } void movl_mr_disp32(int32_t offset, RegisterID base, RegisterID dst) { - spew("movl " MEM_o32b ", %s", ADDR_o32b(offset, base), nameIReg(4,dst)); + spew("movl " MEM_o32b ", %s", ADDR_o32b(offset, base), GPReg32Name(dst)); m_formatter.oneByteOp_disp32(OP_MOV_GvEv, offset, base, dst); } void movl_mr(const void* base, RegisterID index, int scale, RegisterID dst) { - int32_t disp = addressImmediate(base); + int32_t disp = AddressImmediate(base); - spew("movl " MEM_os ", %s", ADDR_os(disp, index, scale), nameIReg(4, dst)); + spew("movl " MEM_os ", %s", ADDR_os(disp, index, scale), GPReg32Name(dst)); m_formatter.oneByteOp_disp32(OP_MOV_GvEv, disp, index, scale, dst); } void movl_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst) { - spew("movl " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), nameIReg(4, dst)); + spew("movl " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg32Name(dst)); m_formatter.oneByteOp(OP_MOV_GvEv, offset, base, index, scale, dst); } void movl_mr(const void* addr, RegisterID dst) { - if (dst == X86Registers::eax + if (dst == rax #ifdef JS_CODEGEN_X64 - && !isAddressImmediate(addr) + && !IsAddressImmediate(addr) #endif ) { @@ -2155,20 +1611,20 @@ return; } - spew("movl %p, %s", addr, nameIReg(4, dst)); + spew("movl %p, %s", addr, GPReg32Name(dst)); m_formatter.oneByteOp(OP_MOV_GvEv, addr, dst); } void movl_i32r(int32_t imm, RegisterID dst) { - spew("movl $0x%x, %s", imm, nameIReg(4, dst)); + spew("movl $0x%x, %s", imm, GPReg32Name(dst)); m_formatter.oneByteOp(OP_MOV_EAXIv, dst); m_formatter.immediate32(imm); } void movb_ir(int32_t imm, RegisterID reg) { - spew("movb $0x%x, %s", imm, nameIReg(1, reg)); + spew("movb $0x%x, %s", imm, GPReg8Name(reg)); m_formatter.oneByteOp(OP_MOV_EbGv, reg); m_formatter.immediate8(imm); } @@ -2235,8 +1691,8 @@ void movl_EAXm(const void* addr) { #ifdef JS_CODEGEN_X64 - if (isAddressImmediate(addr)) { - movl_rm(X86Registers::eax, addr); + if (IsAddressImmediate(addr)) { + movl_rm(rax, addr); return; } #endif @@ -2246,50 +1702,50 @@ #ifdef JS_CODEGEN_X64 m_formatter.immediate64(reinterpret_cast(addr)); #else - m_formatter.immediate32(reinterpret_cast(addr)); + m_formatter.immediate32(reinterpret_cast(addr)); #endif } #ifdef JS_CODEGEN_X64 void movq_rr(RegisterID src, RegisterID dst) { - spew("movq %s, %s", nameIReg(8,src), nameIReg(8,dst)); + spew("movq %s, %s", GPReg64Name(src), GPReg64Name(dst)); m_formatter.oneByteOp64(OP_MOV_GvEv, src, dst); } void movq_rm(RegisterID src, int32_t offset, RegisterID base) { - spew("movq %s, " MEM_ob, nameIReg(8,src), ADDR_ob(offset, base)); + spew("movq %s, " MEM_ob, GPReg64Name(src), ADDR_ob(offset, base)); m_formatter.oneByteOp64(OP_MOV_EvGv, offset, base, src); } void movq_rm_disp32(RegisterID src, int32_t offset, RegisterID base) { - FIXME_INSN_PRINTING; + spew("movq %s, " MEM_o32b, GPReg64Name(src), ADDR_o32b(offset, base)); m_formatter.oneByteOp64_disp32(OP_MOV_EvGv, offset, base, src); } void movq_rm(RegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale) { - spew("movq %s, " MEM_obs, nameIReg(8,src), ADDR_obs(offset, base, index, scale)); + spew("movq %s, " MEM_obs, GPReg64Name(src), ADDR_obs(offset, base, index, scale)); m_formatter.oneByteOp64(OP_MOV_EvGv, offset, base, index, scale, src); } void movq_rm(RegisterID src, const void* addr) { - if (src == X86Registers::eax && !isAddressImmediate(addr)) { + if (src == rax && !IsAddressImmediate(addr)) { movq_EAXm(addr); return; } - spew("movq %s, %p", nameIReg(8, src), addr); + spew("movq %s, %p", GPReg64Name(src), addr); m_formatter.oneByteOp64(OP_MOV_EvGv, addr, src); } void movq_mEAX(const void* addr) { - if (isAddressImmediate(addr)) { - movq_mr(addr, X86Registers::eax); + if (IsAddressImmediate(addr)) { + movq_mr(addr, rax); return; } @@ -2300,8 +1756,8 @@ void movq_EAXm(const void* addr) { - if (isAddressImmediate(addr)) { - movq_rm(X86Registers::eax, addr); + if (IsAddressImmediate(addr)) { + movq_rm(rax, addr); return; } @@ -2312,36 +1768,36 @@ void movq_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("movq " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(8,dst)); + spew("movq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst)); m_formatter.oneByteOp64(OP_MOV_GvEv, offset, base, dst); } void movq_mr_disp32(int32_t offset, RegisterID base, RegisterID dst) { - FIXME_INSN_PRINTING; + spew("movq " MEM_o32b ", %s", ADDR_o32b(offset, base), GPReg64Name(dst)); m_formatter.oneByteOp64_disp32(OP_MOV_GvEv, offset, base, dst); } void movq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst) { - spew("movq " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), nameIReg(8,dst)); + spew("movq " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst)); m_formatter.oneByteOp64(OP_MOV_GvEv, offset, base, index, scale, dst); } void movq_mr(const void* addr, RegisterID dst) { - if (dst == X86Registers::eax && !isAddressImmediate(addr)) { + if (dst == rax && !IsAddressImmediate(addr)) { movq_mEAX(addr); return; } - spew("movq %p, %s", addr, nameIReg(8, dst)); + spew("movq %p, %s", addr, GPReg64Name(dst)); m_formatter.oneByteOp64(OP_MOV_GvEv, addr, dst); } void leaq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst) { - spew("leaq " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), nameIReg(8,dst)), + spew("leaq " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst)), m_formatter.oneByteOp64(OP_LEA, offset, base, index, scale, dst); } @@ -2372,60 +1828,63 @@ // movl_i32r *zero*-extends its 32-bit immediate, and it has smaller code // size, so it's preferred for values which could use either. void movq_i32r(int32_t imm, RegisterID dst) { - spew("movq $%d, %s", imm, nameIReg(dst)); + spew("movq $%d, %s", imm, GPRegName(dst)); m_formatter.oneByteOp64(OP_GROUP11_EvIz, dst, GROUP11_MOV); m_formatter.immediate32(imm); } void movq_i64r(int64_t imm, RegisterID dst) { - spew("movabsq $0x%" PRIx64 ", %s", imm, nameIReg(8, dst)); + spew("movabsq $0x%" PRIx64 ", %s", imm, GPReg64Name(dst)); m_formatter.oneByteOp64(OP_MOV_EAXIv, dst); m_formatter.immediate64(imm); } void movsxd_rr(RegisterID src, RegisterID dst) { - spew("movsxd %s, %s", nameIReg(4, src), nameIReg(8, dst)); + spew("movsxd %s, %s", GPReg32Name(src), GPReg64Name(dst)); m_formatter.oneByteOp64(OP_MOVSXD_GvEv, src, dst); } - JmpSrc movl_ripr(RegisterID dst) + MOZ_WARN_UNUSED_RESULT JmpSrc + movl_ripr(RegisterID dst) { m_formatter.oneByteRipOp(OP_MOV_GvEv, 0, (RegisterID)dst); JmpSrc label(m_formatter.size()); - spew("movl .Lfrom%d(%%rip), %s", label.offset(), nameIReg(4, dst)); + spew("movl " MEM_o32r ", %s", ADDR_o32r(label.offset()), GPReg32Name(dst)); return label; } - JmpSrc movl_rrip(RegisterID src) + MOZ_WARN_UNUSED_RESULT JmpSrc + movl_rrip(RegisterID src) { m_formatter.oneByteRipOp(OP_MOV_EvGv, 0, (RegisterID)src); JmpSrc label(m_formatter.size()); - spew("movl %s, .Lfrom%d(%%rip)", nameIReg(4, src), label.offset()); + spew("movl %s, " MEM_o32r "", GPReg32Name(src), ADDR_o32r(label.offset())); return label; } - JmpSrc movq_ripr(RegisterID dst) + MOZ_WARN_UNUSED_RESULT JmpSrc + movq_ripr(RegisterID dst) { m_formatter.oneByteRipOp64(OP_MOV_GvEv, 0, dst); JmpSrc label(m_formatter.size()); - spew("movq .Lfrom%d(%%rip), %s", label.offset(), nameIReg(dst)); + spew("movq " MEM_o32r ", %s", ADDR_o32r(label.offset()), GPRegName(dst)); return label; } #endif void movl_rm(RegisterID src, const void* addr) { - if (src == X86Registers::eax + if (src == rax #ifdef JS_CODEGEN_X64 - && !isAddressImmediate(addr) + && !IsAddressImmediate(addr) #endif ) { movl_EAXm(addr); return; } - spew("movl %s, %p", nameIReg(4, src), addr); + spew("movl %s, %p", GPReg32Name(src), addr); m_formatter.oneByteOp(OP_MOV_EvGv, addr, src); } @@ -2438,203 +1897,203 @@ void movb_rm(RegisterID src, int32_t offset, RegisterID base) { - spew("movb %s, " MEM_ob, nameIReg(1, src), ADDR_ob(offset, base)); + spew("movb %s, " MEM_ob, GPReg8Name(src), ADDR_ob(offset, base)); m_formatter.oneByteOp8(OP_MOV_EbGv, offset, base, src); } void movb_rm_disp32(RegisterID src, int32_t offset, RegisterID base) { - spew("movb %s, " MEM_o32b, nameIReg(1, src), ADDR_o32b(offset, base)); + spew("movb %s, " MEM_o32b, GPReg8Name(src), ADDR_o32b(offset, base)); m_formatter.oneByteOp8_disp32(OP_MOV_EbGv, offset, base, src); } void movb_rm(RegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale) { - spew("movb %s, " MEM_obs, nameIReg(1, src), ADDR_obs(offset, base, index, scale)); + spew("movb %s, " MEM_obs, GPReg8Name(src), ADDR_obs(offset, base, index, scale)); m_formatter.oneByteOp8(OP_MOV_EbGv, offset, base, index, scale, src); } void movb_rm(RegisterID src, const void* addr) { - spew("movb %s, %p", nameIReg(1, src), addr); + spew("movb %s, %p", GPReg8Name(src), addr); m_formatter.oneByteOp8(OP_MOV_EbGv, addr, src); } void movb_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("movb " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(1, dst)); + spew("movb " MEM_ob ", %s", ADDR_ob(offset, base), GPReg8Name(dst)); m_formatter.oneByteOp(OP_MOV_GvEb, offset, base, dst); } void movb_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst) { - spew("movb " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), nameIReg(1, dst)); + spew("movb " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg8Name(dst)); m_formatter.oneByteOp(OP_MOV_GvEb, offset, base, index, scale, dst); } void movzbl_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("movzbl " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(4, dst)); + spew("movzbl " MEM_ob ", %s", ADDR_ob(offset, base), GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVZX_GvEb, offset, base, dst); } void movzbl_mr_disp32(int32_t offset, RegisterID base, RegisterID dst) { - spew("movzbl " MEM_o32b ", %s", ADDR_o32b(offset, base), nameIReg(4, dst)); + spew("movzbl " MEM_o32b ", %s", ADDR_o32b(offset, base), GPReg32Name(dst)); m_formatter.twoByteOp_disp32(OP2_MOVZX_GvEb, offset, base, dst); } void movzbl_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst) { - spew("movzbl " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), nameIReg(4, dst)); + spew("movzbl " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVZX_GvEb, offset, base, index, scale, dst); } void movzbl_mr(const void* addr, RegisterID dst) { - spew("movzbl %p, %s", addr, nameIReg(4, dst)); + spew("movzbl %p, %s", addr, GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVZX_GvEb, addr, dst); } void movsbl_rr(RegisterID src, RegisterID dst) { - spew("movsbl %s, %s", nameIReg(1,src), nameIReg(4,dst)); + spew("movsbl %s, %s", GPReg8Name(src), GPReg32Name(dst)); m_formatter.twoByteOp8_movx(OP2_MOVSX_GvEb, src, dst); } void movsbl_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("movsbl " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(4, dst)); + spew("movsbl " MEM_ob ", %s", ADDR_ob(offset, base), GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVSX_GvEb, offset, base, dst); } void movsbl_mr_disp32(int32_t offset, RegisterID base, RegisterID dst) { - spew("movsbl " MEM_o32b ", %s", ADDR_o32b(offset, base), nameIReg(4, dst)); + spew("movsbl " MEM_o32b ", %s", ADDR_o32b(offset, base), GPReg32Name(dst)); m_formatter.twoByteOp_disp32(OP2_MOVSX_GvEb, offset, base, dst); } void movsbl_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst) { - spew("movsbl " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), nameIReg(4, dst)); + spew("movsbl " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVSX_GvEb, offset, base, index, scale, dst); } void movsbl_mr(const void* addr, RegisterID dst) { - spew("movsbl %p, %s", addr, nameIReg(4, dst)); + spew("movsbl %p, %s", addr, GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVSX_GvEb, addr, dst); } void movzwl_rr(RegisterID src, RegisterID dst) { - spew("movzwl %s, %s", nameIReg(2, src), nameIReg(4, dst)); + spew("movzwl %s, %s", GPReg16Name(src), GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVZX_GvEw, src, dst); } void movzwl_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("movzwl " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(4, dst)); + spew("movzwl " MEM_ob ", %s", ADDR_ob(offset, base), GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVZX_GvEw, offset, base, dst); } void movzwl_mr_disp32(int32_t offset, RegisterID base, RegisterID dst) { - spew("movzwl " MEM_o32b ", %s", ADDR_o32b(offset, base), nameIReg(4, dst)); + spew("movzwl " MEM_o32b ", %s", ADDR_o32b(offset, base), GPReg32Name(dst)); m_formatter.twoByteOp_disp32(OP2_MOVZX_GvEw, offset, base, dst); } void movzwl_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst) { - spew("movzwl " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), nameIReg(4, dst)); + spew("movzwl " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVZX_GvEw, offset, base, index, scale, dst); } void movzwl_mr(const void* addr, RegisterID dst) { - spew("movzwl %p, %s", addr, nameIReg(4, dst)); + spew("movzwl %p, %s", addr, GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVZX_GvEw, addr, dst); } void movswl_rr(RegisterID src, RegisterID dst) { - spew("movswl %s, %s", nameIReg(2, src), nameIReg(4, dst)); + spew("movswl %s, %s", GPReg16Name(src), GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVSX_GvEw, src, dst); } void movswl_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("movswl " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(4, dst)); + spew("movswl " MEM_ob ", %s", ADDR_ob(offset, base), GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVSX_GvEw, offset, base, dst); } void movswl_mr_disp32(int32_t offset, RegisterID base, RegisterID dst) { - spew("movswl " MEM_o32b ", %s", ADDR_o32b(offset, base), nameIReg(4, dst)); + spew("movswl " MEM_o32b ", %s", ADDR_o32b(offset, base), GPReg32Name(dst)); m_formatter.twoByteOp_disp32(OP2_MOVSX_GvEw, offset, base, dst); } void movswl_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst) { - spew("movswl " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), nameIReg(4, dst)); + spew("movswl " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVSX_GvEw, offset, base, index, scale, dst); } void movswl_mr(const void* addr, RegisterID dst) { - spew("movswl %p, %s", addr, nameIReg(4, dst)); + spew("movswl %p, %s", addr, GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVSX_GvEw, addr, dst); } void movzbl_rr(RegisterID src, RegisterID dst) { - spew("movzbl %s, %s", nameIReg(1,src), nameIReg(4,dst)); + spew("movzbl %s, %s", GPReg8Name(src), GPReg32Name(dst)); m_formatter.twoByteOp8_movx(OP2_MOVZX_GvEb, src, dst); } void leal_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst) { - spew("leal " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), nameIReg(4, dst)); + spew("leal " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg32Name(dst)); m_formatter.oneByteOp(OP_LEA, offset, base, index, scale, dst); } void leal_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("leal " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(4,dst)); + spew("leal " MEM_ob ", %s", ADDR_ob(offset, base), GPReg32Name(dst)); m_formatter.oneByteOp(OP_LEA, offset, base, dst); } #ifdef JS_CODEGEN_X64 void leaq_mr(int32_t offset, RegisterID base, RegisterID dst) { - spew("leaq " MEM_ob ", %s", ADDR_ob(offset, base), nameIReg(8,dst)); + spew("leaq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst)); m_formatter.oneByteOp64(OP_LEA, offset, base, dst); } - JmpSrc leaq_rip(RegisterID dst) + MOZ_WARN_UNUSED_RESULT JmpSrc + leaq_rip(RegisterID dst) { m_formatter.oneByteRipOp64(OP_LEA, 0, dst); JmpSrc label(m_formatter.size()); - spew("leaq .Lfrom%d(%%rip), %s", label.offset(), nameIReg(dst)); + spew("leaq " MEM_o32r ", %s", ADDR_o32r(label.offset()), GPRegName(dst)); return label; } #endif // Flow control: - JmpSrc call() + MOZ_WARN_UNUSED_RESULT JmpSrc + call() { m_formatter.oneByteOp(OP_CALL_rel32); JmpSrc r = m_formatter.immediateRel32(); - spew("call .Lfrom%d", r.m_offset); + spew("call .Lfrom%d", r.offset()); return r; } - JmpSrc call(RegisterID dst) + void call_r(RegisterID dst) { m_formatter.oneByteOp(OP_GROUP5_Ev, dst, GROUP5_OP_CALLN); - JmpSrc r = JmpSrc(m_formatter.size()); - spew("call *%s", nameIReg(dst)); - return r; + spew("call *%s", GPRegName(dst)); } void call_m(int32_t offset, RegisterID base) @@ -2646,11 +2105,12 @@ // Comparison of EAX against a 32-bit immediate. The immediate is patched // in as if it were a jump target. The intention is to toggle the first // byte of the instruction between a CMP and a JMP to produce a pseudo-NOP. - JmpSrc cmp_eax() + MOZ_WARN_UNUSED_RESULT JmpSrc + cmp_eax() { m_formatter.oneByteOp(OP_CMP_EAXIv); JmpSrc r = m_formatter.immediateRel32(); - spew("cmpl %%eax, .Lfrom%d", r.m_offset); + spew("cmpl %%eax, .Lfrom%d", r.offset()); return r; } @@ -2675,18 +2135,14 @@ { m_formatter.oneByteOp(OP_JMP_rel32); JmpSrc r = m_formatter.immediateRel32(); - spew("jmp .Lfrom%d", r.m_offset); + spew("jmp .Lfrom%d", r.offset()); return r; } - // Return a JmpSrc so we have a label to the jump, so we can use this - // To make a tail recursive call on x86-64. The MacroAssembler - // really shouldn't wrap this as a Jump, since it can't be linked. :-/ - JmpSrc jmp_r(RegisterID dst) + void jmp_r(RegisterID dst) { - spew("jmp *%s", nameIReg(dst)); + spew("jmp *%s", GPRegName(dst)); m_formatter.oneByteOp(OP_GROUP5_Ev, dst, GROUP5_OP_JMPN); - return JmpSrc(m_formatter.size()); } void jmp_m(int32_t offset, RegisterID base) @@ -2717,7 +2173,7 @@ void jCC_i(Condition cond, JmpDst dst) { int32_t diff = dst.offset() - m_formatter.size(); - spew("j%s .Llabel%d", nameCC(cond), dst.offset()); + spew("j%s .Llabel%d", CCName(cond), dst.offset()); // The jump immediate is an offset from the end of the jump instruction. // A conditional jump instruction is either 1 byte opcode and 1 byte @@ -2736,7 +2192,7 @@ { m_formatter.twoByteOp(jccRel32(cond)); JmpSrc r = m_formatter.immediateRel32(); - spew("j%s .Lfrom%d", nameCC(cond), r.m_offset); + spew("j%s .Lfrom%d", CCName(cond), r.offset()); return r; } @@ -2787,33 +2243,33 @@ } void vrcpps_rr(XMMRegisterID src, XMMRegisterID dst) { - twoByteOpSimd("vrcpps", VEX_PS, OP2_RCPPS_VpsWps, src, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vrcpps", VEX_PS, OP2_RCPPS_VpsWps, src, invalid_xmm, dst); } void vrcpps_mr(int32_t offset, RegisterID base, XMMRegisterID dst) { - twoByteOpSimd("vrcpps", VEX_PS, OP2_RCPPS_VpsWps, offset, base, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vrcpps", VEX_PS, OP2_RCPPS_VpsWps, offset, base, invalid_xmm, dst); } void vrcpps_mr(const void* address, XMMRegisterID dst) { - twoByteOpSimd("vrcpps", VEX_PS, OP2_RCPPS_VpsWps, address, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vrcpps", VEX_PS, OP2_RCPPS_VpsWps, address, invalid_xmm, dst); } void vrsqrtps_rr(XMMRegisterID src, XMMRegisterID dst) { - twoByteOpSimd("vrsqrtps", VEX_PS, OP2_RSQRTPS_VpsWps, src, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vrsqrtps", VEX_PS, OP2_RSQRTPS_VpsWps, src, invalid_xmm, dst); } void vrsqrtps_mr(int32_t offset, RegisterID base, XMMRegisterID dst) { - twoByteOpSimd("vrsqrtps", VEX_PS, OP2_RSQRTPS_VpsWps, offset, base, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vrsqrtps", VEX_PS, OP2_RSQRTPS_VpsWps, offset, base, invalid_xmm, dst); } void vrsqrtps_mr(const void* address, XMMRegisterID dst) { - twoByteOpSimd("vrsqrtps", VEX_PS, OP2_RSQRTPS_VpsWps, address, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vrsqrtps", VEX_PS, OP2_RSQRTPS_VpsWps, address, invalid_xmm, dst); } void vsqrtps_rr(XMMRegisterID src, XMMRegisterID dst) { - twoByteOpSimd("vsqrtps", VEX_PS, OP2_SQRTPS_VpsWps, src, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vsqrtps", VEX_PS, OP2_SQRTPS_VpsWps, src, invalid_xmm, dst); } void vsqrtps_mr(int32_t offset, RegisterID base, XMMRegisterID dst) { - twoByteOpSimd("vsqrtps", VEX_PS, OP2_SQRTPS_VpsWps, offset, base, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vsqrtps", VEX_PS, OP2_SQRTPS_VpsWps, offset, base, invalid_xmm, dst); } void vsqrtps_mr(const void* address, XMMRegisterID dst) { - twoByteOpSimd("vsqrtps", VEX_PS, OP2_SQRTPS_VpsWps, address, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vsqrtps", VEX_PS, OP2_SQRTPS_VpsWps, address, invalid_xmm, dst); } void vaddsd_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) @@ -2867,12 +2323,12 @@ void vcvttps2dq_rr(XMMRegisterID src, XMMRegisterID dst) { - twoByteOpSimd("vcvttps2dq", VEX_SS, OP2_CVTTPS2DQ_VdqWps, src, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vcvttps2dq", VEX_SS, OP2_CVTTPS2DQ_VdqWps, src, invalid_xmm, dst); } void vcvtdq2ps_rr(XMMRegisterID src, XMMRegisterID dst) { - twoByteOpSimd("vcvtdq2ps", VEX_PS, OP2_CVTDQ2PS_VpsWdq, src, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vcvtdq2ps", VEX_PS, OP2_CVTDQ2PS_VpsWdq, src, invalid_xmm, dst); } #ifdef JS_CODEGEN_X64 @@ -3012,15 +2468,15 @@ void vpshufd_irr(uint32_t mask, XMMRegisterID src, XMMRegisterID dst) { - twoByteOpImmSimd("vpshufd", VEX_PD, OP2_PSHUFD_VdqWdqIb, mask, src, X86Registers::invalid_xmm, dst); + twoByteOpImmSimd("vpshufd", VEX_PD, OP2_PSHUFD_VdqWdqIb, mask, src, invalid_xmm, dst); } void vpshufd_imr(uint32_t mask, int32_t offset, RegisterID base, XMMRegisterID dst) { - twoByteOpImmSimd("vpshufd", VEX_PD, OP2_PSHUFD_VdqWdqIb, mask, offset, base, X86Registers::invalid_xmm, dst); + twoByteOpImmSimd("vpshufd", VEX_PD, OP2_PSHUFD_VdqWdqIb, mask, offset, base, invalid_xmm, dst); } void vpshufd_imr(uint32_t mask, const void* address, XMMRegisterID dst) { - twoByteOpImmSimd("vpshufd", VEX_PD, OP2_PSHUFD_VdqWdqIb, mask, address, X86Registers::invalid_xmm, dst); + twoByteOpImmSimd("vpshufd", VEX_PD, OP2_PSHUFD_VdqWdqIb, mask, address, invalid_xmm, dst); } void vshufps_irr(uint32_t mask, XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) @@ -3049,19 +2505,19 @@ void vpsrldq_ir(uint32_t count, XMMRegisterID src, XMMRegisterID dst) { MOZ_ASSERT(count < 16); - shiftOpImmSimd("vpsrldq", OP2_PSRLDQ_Vd, Shift_vpsrldq, count, src, dst); + shiftOpImmSimd("vpsrldq", OP2_PSRLDQ_Vd, ShiftID::vpsrldq, count, src, dst); } void vpsllq_ir(uint32_t count, XMMRegisterID src, XMMRegisterID dst) { MOZ_ASSERT(count < 64); - shiftOpImmSimd("vpsllq", OP2_PSRLDQ_Vd, Shift_vpsllq, count, src, dst); + shiftOpImmSimd("vpsllq", OP2_PSRLDQ_Vd, ShiftID::vpsllq, count, src, dst); } void vpsrlq_ir(uint32_t count, XMMRegisterID src, XMMRegisterID dst) { MOZ_ASSERT(count < 64); - shiftOpImmSimd("vpsrlq", OP2_PSRLDQ_Vd, Shift_vpsrlq, count, src, dst); + shiftOpImmSimd("vpsrlq", OP2_PSRLDQ_Vd, ShiftID::vpsrlq, count, src, dst); } void vpslld_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) @@ -3072,7 +2528,7 @@ void vpslld_ir(uint32_t count, XMMRegisterID src, XMMRegisterID dst) { MOZ_ASSERT(count < 32); - shiftOpImmSimd("vpslld", OP2_PSLLD_UdqIb, Shift_vpslld, count, src, dst); + shiftOpImmSimd("vpslld", OP2_PSLLD_UdqIb, ShiftID::vpslld, count, src, dst); } void vpsrad_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) @@ -3083,7 +2539,7 @@ void vpsrad_ir(int32_t count, XMMRegisterID src, XMMRegisterID dst) { MOZ_ASSERT(count < 32); - shiftOpImmSimd("vpsrad", OP2_PSRAD_UdqIb, Shift_vpsrad, count, src, dst); + shiftOpImmSimd("vpsrad", OP2_PSRAD_UdqIb, ShiftID::vpsrad, count, src, dst); } void vpsrld_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) @@ -3094,7 +2550,7 @@ void vpsrld_ir(uint32_t count, XMMRegisterID src, XMMRegisterID dst) { MOZ_ASSERT(count < 32); - shiftOpImmSimd("vpsrld", OP2_PSRLD_UdqIb, Shift_vpsrld, count, src, dst); + shiftOpImmSimd("vpsrld", OP2_PSRLD_UdqIb, ShiftID::vpsrld, count, src, dst); } void vmovmskpd_rr(XMMRegisterID src, RegisterID dst) @@ -3108,7 +2564,7 @@ } void vptest_rr(XMMRegisterID rhs, XMMRegisterID lhs) { - threeByteOpSimd("vptest", VEX_PD, OP3_PTEST_VdVd, ESCAPE_PTEST, rhs, X86Registers::invalid_xmm, lhs); + threeByteOpSimd("vptest", VEX_PD, OP3_PTEST_VdVd, ESCAPE_38, rhs, invalid_xmm, lhs); } void vmovd_rr(XMMRegisterID src, RegisterID dst) @@ -3118,7 +2574,7 @@ void vmovd_rr(RegisterID src, XMMRegisterID dst) { - twoByteOpInt32Simd("vmovd", VEX_PD, OP2_MOVD_VdEd, src, X86Registers::invalid_xmm, dst); + twoByteOpInt32Simd("vmovd", VEX_PD, OP2_MOVD_VdEd, src, invalid_xmm, dst); } #ifdef JS_CODEGEN_X64 @@ -3129,68 +2585,68 @@ void vmovq_rr(RegisterID src, XMMRegisterID dst) { - twoByteOpInt64Simd("vmovq", VEX_PD, OP2_MOVD_VdEd, src, X86Registers::invalid_xmm, dst); + twoByteOpInt64Simd("vmovq", VEX_PD, OP2_MOVD_VdEd, src, invalid_xmm, dst); } #endif void vmovsd_rm(XMMRegisterID src, int32_t offset, RegisterID base) { - twoByteOpSimd("vmovsd", VEX_SD, OP2_MOVSD_WsdVsd, offset, base, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovsd", VEX_SD, OP2_MOVSD_WsdVsd, offset, base, invalid_xmm, src); } void vmovsd_rm_disp32(XMMRegisterID src, int32_t offset, RegisterID base) { - twoByteOpSimd_disp32("vmovsd", VEX_SD, OP2_MOVSD_WsdVsd, offset, base, X86Registers::invalid_xmm, src); + twoByteOpSimd_disp32("vmovsd", VEX_SD, OP2_MOVSD_WsdVsd, offset, base, invalid_xmm, src); } void vmovss_rm(XMMRegisterID src, int32_t offset, RegisterID base) { - twoByteOpSimd("vmovss", VEX_SS, OP2_MOVSD_WsdVsd, offset, base, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovss", VEX_SS, OP2_MOVSD_WsdVsd, offset, base, invalid_xmm, src); } void vmovss_rm_disp32(XMMRegisterID src, int32_t offset, RegisterID base) { - twoByteOpSimd_disp32("vmovss", VEX_SS, OP2_MOVSD_WsdVsd, offset, base, X86Registers::invalid_xmm, src); + twoByteOpSimd_disp32("vmovss", VEX_SS, OP2_MOVSD_WsdVsd, offset, base, invalid_xmm, src); } void vmovss_mr(int32_t offset, RegisterID base, XMMRegisterID dst) { - twoByteOpSimd("vmovss", VEX_SS, OP2_MOVSD_VsdWsd, offset, base, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovss", VEX_SS, OP2_MOVSD_VsdWsd, offset, base, invalid_xmm, dst); } void vmovss_mr_disp32(int32_t offset, RegisterID base, XMMRegisterID dst) { - twoByteOpSimd_disp32("vmovss", VEX_SS, OP2_MOVSD_VsdWsd, offset, base, X86Registers::invalid_xmm, dst); + twoByteOpSimd_disp32("vmovss", VEX_SS, OP2_MOVSD_VsdWsd, offset, base, invalid_xmm, dst); } void vmovsd_rm(XMMRegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale) { - twoByteOpSimd("vmovsd", VEX_SD, OP2_MOVSD_WsdVsd, offset, base, index, scale, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovsd", VEX_SD, OP2_MOVSD_WsdVsd, offset, base, index, scale, invalid_xmm, src); } void vmovss_rm(XMMRegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale) { - twoByteOpSimd("vmovss", VEX_SS, OP2_MOVSD_WsdVsd, offset, base, index, scale, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovss", VEX_SS, OP2_MOVSD_WsdVsd, offset, base, index, scale, invalid_xmm, src); } void vmovss_mr(int32_t offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst) { - twoByteOpSimd("vmovss", VEX_SS, OP2_MOVSD_VsdWsd, offset, base, index, scale, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovss", VEX_SS, OP2_MOVSD_VsdWsd, offset, base, index, scale, invalid_xmm, dst); } void vmovsd_mr(int32_t offset, RegisterID base, XMMRegisterID dst) { - twoByteOpSimd("vmovsd", VEX_SD, OP2_MOVSD_VsdWsd, offset, base, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovsd", VEX_SD, OP2_MOVSD_VsdWsd, offset, base, invalid_xmm, dst); } void vmovsd_mr_disp32(int32_t offset, RegisterID base, XMMRegisterID dst) { - twoByteOpSimd_disp32("vmovsd", VEX_SD, OP2_MOVSD_VsdWsd, offset, base, X86Registers::invalid_xmm, dst); + twoByteOpSimd_disp32("vmovsd", VEX_SD, OP2_MOVSD_VsdWsd, offset, base, invalid_xmm, dst); } void vmovsd_mr(int32_t offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst) { - twoByteOpSimd("vmovsd", VEX_SD, OP2_MOVSD_VsdWsd, offset, base, index, scale, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovsd", VEX_SD, OP2_MOVSD_VsdWsd, offset, base, index, scale, invalid_xmm, dst); } // Note that the register-to-register form of vmovsd does not write to the @@ -3210,77 +2666,83 @@ void vmovsd_mr(const void* address, XMMRegisterID dst) { - twoByteOpSimd("vmovsd", VEX_SD, OP2_MOVSD_VsdWsd, address, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovsd", VEX_SD, OP2_MOVSD_VsdWsd, address, invalid_xmm, dst); } void vmovss_mr(const void* address, XMMRegisterID dst) { - twoByteOpSimd("vmovss", VEX_SS, OP2_MOVSD_VsdWsd, address, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovss", VEX_SS, OP2_MOVSD_VsdWsd, address, invalid_xmm, dst); } void vmovups_mr(const void* address, XMMRegisterID dst) { - twoByteOpSimd("vmovups", VEX_PS, OP2_MOVPS_VpsWps, address, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovups", VEX_PS, OP2_MOVPS_VpsWps, address, invalid_xmm, dst); } void vmovdqu_mr(const void* address, XMMRegisterID dst) { - twoByteOpSimd("vmovdqu", VEX_SS, OP2_MOVDQ_VdqWdq, address, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovdqu", VEX_SS, OP2_MOVDQ_VdqWdq, address, invalid_xmm, dst); } void vmovsd_rm(XMMRegisterID src, const void* address) { - twoByteOpSimd("vmovsd", VEX_SD, OP2_MOVSD_WsdVsd, address, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovsd", VEX_SD, OP2_MOVSD_WsdVsd, address, invalid_xmm, src); } void vmovss_rm(XMMRegisterID src, const void* address) { - twoByteOpSimd("vmovss", VEX_SS, OP2_MOVSD_WsdVsd, address, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovss", VEX_SS, OP2_MOVSD_WsdVsd, address, invalid_xmm, src); } void vmovdqa_rm(XMMRegisterID src, const void* address) { - twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_WdqVdq, address, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_WdqVdq, address, invalid_xmm, src); } void vmovaps_rm(XMMRegisterID src, const void* address) { - twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_WsdVsd, address, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_WsdVsd, address, invalid_xmm, src); } void vmovdqu_rm(XMMRegisterID src, const void* address) { - twoByteOpSimd("vmovdqu", VEX_SS, OP2_MOVDQ_WdqVdq, address, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovdqu", VEX_SS, OP2_MOVDQ_WdqVdq, address, invalid_xmm, src); } void vmovups_rm(XMMRegisterID src, const void* address) { - twoByteOpSimd("vmovups", VEX_PS, OP2_MOVPS_WpsVps, address, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovups", VEX_PS, OP2_MOVPS_WpsVps, address, invalid_xmm, src); } #ifdef JS_CODEGEN_X64 - JmpSrc vmovsd_ripr(XMMRegisterID dst) + MOZ_WARN_UNUSED_RESULT JmpSrc + vmovsd_ripr(XMMRegisterID dst) { - return twoByteRipOpSimd("vmovsd", VEX_SD, OP2_MOVSD_VsdWsd, 0, X86Registers::invalid_xmm, dst); + return twoByteRipOpSimd("vmovsd", VEX_SD, OP2_MOVSD_VsdWsd, invalid_xmm, dst); } - JmpSrc vmovss_ripr(XMMRegisterID dst) + MOZ_WARN_UNUSED_RESULT JmpSrc + vmovss_ripr(XMMRegisterID dst) { - return twoByteRipOpSimd("vmovss", VEX_SS, OP2_MOVSD_VsdWsd, 0, X86Registers::invalid_xmm, dst); + return twoByteRipOpSimd("vmovss", VEX_SS, OP2_MOVSD_VsdWsd, invalid_xmm, dst); } - JmpSrc vmovsd_rrip(XMMRegisterID src) + MOZ_WARN_UNUSED_RESULT JmpSrc + vmovsd_rrip(XMMRegisterID src) { - return twoByteRipOpSimd("vmovsd", VEX_SD, OP2_MOVSD_WsdVsd, 0, X86Registers::invalid_xmm, src); + return twoByteRipOpSimd("vmovsd", VEX_SD, OP2_MOVSD_WsdVsd, invalid_xmm, src); } - JmpSrc vmovss_rrip(XMMRegisterID src) + MOZ_WARN_UNUSED_RESULT JmpSrc + vmovss_rrip(XMMRegisterID src) { - return twoByteRipOpSimd("vmovss", VEX_SS, OP2_MOVSD_WsdVsd, 0, X86Registers::invalid_xmm, src); + return twoByteRipOpSimd("vmovss", VEX_SS, OP2_MOVSD_WsdVsd, invalid_xmm, src); } - JmpSrc vmovdqa_rrip(XMMRegisterID src) + MOZ_WARN_UNUSED_RESULT JmpSrc + vmovdqa_rrip(XMMRegisterID src) { - return twoByteRipOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_WdqVdq, 0, X86Registers::invalid_xmm, src); + return twoByteRipOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_WdqVdq, invalid_xmm, src); } - JmpSrc vmovaps_rrip(XMMRegisterID src) + MOZ_WARN_UNUSED_RESULT JmpSrc + vmovaps_rrip(XMMRegisterID src) { - return twoByteRipOpSimd("vmovdqa", VEX_PS, OP2_MOVAPS_WsdVsd, 0, X86Registers::invalid_xmm, src); + return twoByteRipOpSimd("vmovdqa", VEX_PS, OP2_MOVAPS_WsdVsd, invalid_xmm, src); } #endif @@ -3291,53 +2753,53 @@ // one register in [xmm8,xmm15] and one in [xmm0,xmm7], use the // opcode which swaps the operands, as that way we can get a two-byte // VEX in that case. - if (src >= X86Registers::xmm8 && dst < X86Registers::xmm8) { - twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_WsdVsd, dst, X86Registers::invalid_xmm, src); + if (src >= xmm8 && dst < xmm8) { + twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_WsdVsd, dst, invalid_xmm, src); return; } #endif - twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_VsdWsd, src, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_VsdWsd, src, invalid_xmm, dst); } void vmovaps_rm(XMMRegisterID src, int32_t offset, RegisterID base) { - twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_WsdVsd, offset, base, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_WsdVsd, offset, base, invalid_xmm, src); } void vmovaps_rm(XMMRegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale) { - twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_WsdVsd, offset, base, index, scale, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_WsdVsd, offset, base, index, scale, invalid_xmm, src); } void vmovaps_mr(int32_t offset, RegisterID base, XMMRegisterID dst) { - twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_VsdWsd, offset, base, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_VsdWsd, offset, base, invalid_xmm, dst); } void vmovaps_mr(int32_t offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst) { - twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_VsdWsd, offset, base, index, scale, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_VsdWsd, offset, base, index, scale, invalid_xmm, dst); } void vmovups_rm(XMMRegisterID src, int32_t offset, RegisterID base) { - twoByteOpSimd("vmovups", VEX_PS, OP2_MOVPS_WpsVps, offset, base, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovups", VEX_PS, OP2_MOVPS_WpsVps, offset, base, invalid_xmm, src); } void vmovups_rm_disp32(XMMRegisterID src, int32_t offset, RegisterID base) { - twoByteOpSimd_disp32("vmovups", VEX_PS, OP2_MOVPS_WpsVps, offset, base, X86Registers::invalid_xmm, src); + twoByteOpSimd_disp32("vmovups", VEX_PS, OP2_MOVPS_WpsVps, offset, base, invalid_xmm, src); } void vmovups_rm(XMMRegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale) { - twoByteOpSimd("vmovups", VEX_PS, OP2_MOVPS_WpsVps, offset, base, index, scale, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovups", VEX_PS, OP2_MOVPS_WpsVps, offset, base, index, scale, invalid_xmm, src); } void vmovups_mr(int32_t offset, RegisterID base, XMMRegisterID dst) { - twoByteOpSimd("vmovups", VEX_PS, OP2_MOVPS_VpsWps, offset, base, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovups", VEX_PS, OP2_MOVPS_VpsWps, offset, base, invalid_xmm, dst); } void vmovups_mr_disp32(int32_t offset, RegisterID base, XMMRegisterID dst) { - twoByteOpSimd_disp32("vmovups", VEX_PS, OP2_MOVPS_VpsWps, offset, base, X86Registers::invalid_xmm, dst); + twoByteOpSimd_disp32("vmovups", VEX_PS, OP2_MOVPS_VpsWps, offset, base, invalid_xmm, dst); } void vmovups_mr(int32_t offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst) { - twoByteOpSimd("vmovups", VEX_PS, OP2_MOVPS_VpsWps, offset, base, index, scale, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovups", VEX_PS, OP2_MOVPS_VpsWps, offset, base, index, scale, invalid_xmm, dst); } void vmovapd_rr(XMMRegisterID src, XMMRegisterID dst) @@ -3347,64 +2809,66 @@ // one register in [xmm8,xmm15] and one in [xmm0,xmm7], use the // opcode which swaps the operands, as that way we can get a two-byte // VEX in that case. - if (src >= X86Registers::xmm8 && dst < X86Registers::xmm8) { - twoByteOpSimd("vmovapd", VEX_PD, OP2_MOVAPS_WsdVsd, dst, X86Registers::invalid_xmm, src); + if (src >= xmm8 && dst < xmm8) { + twoByteOpSimd("vmovapd", VEX_PD, OP2_MOVAPS_WsdVsd, dst, invalid_xmm, src); return; } #endif - twoByteOpSimd("vmovapd", VEX_PD, OP2_MOVAPD_VsdWsd, src, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovapd", VEX_PD, OP2_MOVAPD_VsdWsd, src, invalid_xmm, dst); } #ifdef JS_CODEGEN_X64 - JmpSrc vmovaps_ripr(XMMRegisterID dst) + MOZ_WARN_UNUSED_RESULT JmpSrc + vmovaps_ripr(XMMRegisterID dst) { - return twoByteRipOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_VsdWsd, 0, X86Registers::invalid_xmm, dst); + return twoByteRipOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_VsdWsd, invalid_xmm, dst); } - JmpSrc vmovdqa_ripr(XMMRegisterID dst) + MOZ_WARN_UNUSED_RESULT JmpSrc + vmovdqa_ripr(XMMRegisterID dst) { - return twoByteRipOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, 0, X86Registers::invalid_xmm, dst); + return twoByteRipOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, invalid_xmm, dst); } #else void vmovaps_mr(const void* address, XMMRegisterID dst) { - twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_VsdWsd, address, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_VsdWsd, address, invalid_xmm, dst); } void vmovdqa_mr(const void* address, XMMRegisterID dst) { - twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, address, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, address, invalid_xmm, dst); } #endif // JS_CODEGEN_X64 void vmovdqu_rm(XMMRegisterID src, int32_t offset, RegisterID base) { - twoByteOpSimd("vmovdqu", VEX_SS, OP2_MOVDQ_WdqVdq, offset, base, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovdqu", VEX_SS, OP2_MOVDQ_WdqVdq, offset, base, invalid_xmm, src); } void vmovdqu_rm_disp32(XMMRegisterID src, int32_t offset, RegisterID base) { - twoByteOpSimd_disp32("vmovdqu", VEX_SS, OP2_MOVDQ_WdqVdq, offset, base, X86Registers::invalid_xmm, src); + twoByteOpSimd_disp32("vmovdqu", VEX_SS, OP2_MOVDQ_WdqVdq, offset, base, invalid_xmm, src); } void vmovdqu_rm(XMMRegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale) { - twoByteOpSimd("vmovdqu", VEX_SS, OP2_MOVDQ_WdqVdq, offset, base, index, scale, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovdqu", VEX_SS, OP2_MOVDQ_WdqVdq, offset, base, index, scale, invalid_xmm, src); } void vmovdqu_mr(int32_t offset, RegisterID base, XMMRegisterID dst) { - twoByteOpSimd("vmovdqu", VEX_SS, OP2_MOVDQ_VdqWdq, offset, base, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovdqu", VEX_SS, OP2_MOVDQ_VdqWdq, offset, base, invalid_xmm, dst); } void vmovdqu_mr_disp32(int32_t offset, RegisterID base, XMMRegisterID dst) { - twoByteOpSimd_disp32("vmovdqu", VEX_SS, OP2_MOVDQ_VdqWdq, offset, base, X86Registers::invalid_xmm, dst); + twoByteOpSimd_disp32("vmovdqu", VEX_SS, OP2_MOVDQ_VdqWdq, offset, base, invalid_xmm, dst); } void vmovdqu_mr(int32_t offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst) { - twoByteOpSimd("vmovdqu", VEX_SS, OP2_MOVDQ_VdqWdq, offset, base, index, scale, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovdqu", VEX_SS, OP2_MOVDQ_VdqWdq, offset, base, index, scale, invalid_xmm, dst); } void vmovdqa_rr(XMMRegisterID src, XMMRegisterID dst) @@ -3414,33 +2878,33 @@ // one register in [xmm8,xmm15] and one in [xmm0,xmm7], use the // opcode which swaps the operands, as that way we can get a two-byte // VEX in that case. - if (src >= X86Registers::xmm8 && dst < X86Registers::xmm8) { - twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_WdqVdq, dst, X86Registers::invalid_xmm, src); + if (src >= xmm8 && dst < xmm8) { + twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_WdqVdq, dst, invalid_xmm, src); return; } #endif - twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, src, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, src, invalid_xmm, dst); } void vmovdqa_rm(XMMRegisterID src, int32_t offset, RegisterID base) { - twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_WdqVdq, offset, base, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_WdqVdq, offset, base, invalid_xmm, src); } void vmovdqa_rm(XMMRegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale) { - twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_WdqVdq, offset, base, index, scale, X86Registers::invalid_xmm, src); + twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_WdqVdq, offset, base, index, scale, invalid_xmm, src); } void vmovdqa_mr(int32_t offset, RegisterID base, XMMRegisterID dst) { - twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, offset, base, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, offset, base, invalid_xmm, dst); } void vmovdqa_mr(int32_t offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst) { - twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, offset, base, index, scale, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, offset, base, index, scale, invalid_xmm, dst); } void vmulsd_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) @@ -3611,47 +3075,47 @@ void vroundsd_irr(RoundingMode mode, XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { - threeByteOpImmSimd("vroundsd", VEX_PD, OP3_ROUNDSD_VsdWsd, ESCAPE_ROUNDSD, mode, src1, src0, dst); + threeByteOpImmSimd("vroundsd", VEX_PD, OP3_ROUNDSD_VsdWsd, ESCAPE_3A, mode, src1, src0, dst); } void vroundss_irr(RoundingMode mode, XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { - threeByteOpImmSimd("vroundss", VEX_PD, OP3_ROUNDSS_VsdWsd, ESCAPE_ROUNDSD, mode, src1, src0, dst); + threeByteOpImmSimd("vroundss", VEX_PD, OP3_ROUNDSS_VsdWsd, ESCAPE_3A, mode, src1, src0, dst); } void vinsertps_irr(uint32_t mask, XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { - threeByteOpImmSimd("vinsertps", VEX_PD, OP3_INSERTPS_VpsUps, ESCAPE_INSERTPS, mask, src1, src0, dst); + threeByteOpImmSimd("vinsertps", VEX_PD, OP3_INSERTPS_VpsUps, ESCAPE_3A, mask, src1, src0, dst); } void vinsertps_imr(uint32_t mask, int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) { - threeByteOpImmSimd("vinsertps", VEX_PD, OP3_INSERTPS_VpsUps, ESCAPE_INSERTPS, mask, offset, base, src0, dst); + threeByteOpImmSimd("vinsertps", VEX_PD, OP3_INSERTPS_VpsUps, ESCAPE_3A, mask, offset, base, src0, dst); } void vpinsrd_irr(unsigned lane, RegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { MOZ_ASSERT(lane < 4); - threeByteOpImmInt32Simd("vpinsrd", VEX_PD, OP3_PINSRD_VdqEdIb, ESCAPE_PINSRD, lane, src1, src0, dst); + threeByteOpImmInt32Simd("vpinsrd", VEX_PD, OP3_PINSRD_VdqEdIb, ESCAPE_3A, lane, src1, src0, dst); } void vpinsrd_imr(unsigned lane, int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) { MOZ_ASSERT(lane < 4); - threeByteOpImmInt32Simd("vpinsrd", VEX_PD, OP3_PINSRD_VdqEdIb, ESCAPE_PINSRD, lane, offset, base, src0, dst); + threeByteOpImmInt32Simd("vpinsrd", VEX_PD, OP3_PINSRD_VdqEdIb, ESCAPE_3A, lane, offset, base, src0, dst); } void vpextrd_irr(unsigned lane, XMMRegisterID src, RegisterID dst) { MOZ_ASSERT(lane < 4); - threeByteOpImmSimdInt32("vpextrd", VEX_PD, OP3_PEXTRD_EdVdqIb, ESCAPE_PEXTRD, lane, (XMMRegisterID)dst, (RegisterID)src); + threeByteOpImmSimdInt32("vpextrd", VEX_PD, OP3_PEXTRD_EdVdqIb, ESCAPE_3A, lane, (XMMRegisterID)dst, (RegisterID)src); } void vpextrd_irm(unsigned lane, XMMRegisterID src, int32_t offset, RegisterID base) { MOZ_ASSERT(lane < 4); - spew("pextrd $0x%x, %s, " MEM_ob, lane, nameFPReg(src), ADDR_ob(offset, base)); + spew("pextrd $0x%x, %s, " MEM_ob, lane, XMMRegName(src), ADDR_ob(offset, base)); m_formatter.prefix(PRE_SSE_66); - m_formatter.threeByteOp(OP3_PEXTRD_EdVdqIb, ESCAPE_PEXTRD, offset, base, (RegisterID)src); + m_formatter.threeByteOp(OP3_PEXTRD_EdVdqIb, ESCAPE_3A, offset, base, (RegisterID)src); m_formatter.immediate8u(lane); } @@ -3659,14 +3123,14 @@ { MOZ_ASSERT(imm < 16); // Despite being a "ps" instruction, vblendps is encoded with the "pd" prefix. - threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_BLENDPS, imm, src1, src0, dst); + threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, src1, src0, dst); } void vblendps_imr(unsigned imm, int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) { MOZ_ASSERT(imm < 16); // Despite being a "ps" instruction, vblendps is encoded with the "pd" prefix. -threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_BLENDPS, imm, offset, base, src0, dst); +threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, offset, base, src0, dst); } void vblendvps_rr(XMMRegisterID mask, XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { @@ -3678,20 +3142,20 @@ void vmovsldup_rr(XMMRegisterID src, XMMRegisterID dst) { - twoByteOpSimd("vmovsldup", VEX_SS, OP2_MOVSLDUP_VpsWps, src, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovsldup", VEX_SS, OP2_MOVSLDUP_VpsWps, src, invalid_xmm, dst); } void vmovsldup_mr(int32_t offset, RegisterID base, XMMRegisterID dst) { - twoByteOpSimd("vmovsldup", VEX_SS, OP2_MOVSLDUP_VpsWps, offset, base, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovsldup", VEX_SS, OP2_MOVSLDUP_VpsWps, offset, base, invalid_xmm, dst); } void vmovshdup_rr(XMMRegisterID src, XMMRegisterID dst) { - twoByteOpSimd("vmovshdup", VEX_SS, OP2_MOVSHDUP_VpsWps, src, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovshdup", VEX_SS, OP2_MOVSHDUP_VpsWps, src, invalid_xmm, dst); } void vmovshdup_mr(int32_t offset, RegisterID base, XMMRegisterID dst) { - twoByteOpSimd("vmovshdup", VEX_SS, OP2_MOVSHDUP_VpsWps, offset, base, X86Registers::invalid_xmm, dst); + twoByteOpSimd("vmovshdup", VEX_SS, OP2_MOVSHDUP_VpsWps, offset, base, invalid_xmm, dst); } void vminsd_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) @@ -3749,12 +3213,6 @@ m_formatter.immediate16u(imm); } - void predictNotTaken() - { - FIXME_INSN_PRINTING; - m_formatter.prefix(PRE_PREDICT_BRANCH_NOT_TAKEN); - } - #ifdef JS_CODEGEN_X86 void pusha() { @@ -3779,7 +3237,7 @@ JmpDst label() { JmpDst r = JmpDst(m_formatter.size()); - spew(".set .Llabel%d, .", r.m_offset); + spew(".set .Llabel%d, .", r.offset()); return r; } @@ -3789,21 +3247,23 @@ static JmpDst labelFor(JmpSrc jump, intptr_t offset = 0) { - return JmpDst(jump.m_offset + offset); + return JmpDst(jump.offset() + offset); } - JmpDst align(int alignment) + void align(int alignment) { spew(".balign %d, 0x%x # hlt", alignment, OP_HLT); while (!m_formatter.isAligned(alignment)) m_formatter.oneByteOp(OP_HLT); - - return label(); } void jumpTablePointer(uintptr_t ptr) { - spew("#jumpTablePointer %llu", (unsigned long long)ptr); +#ifdef JS_CODEGEN_X64 + spew(".quad 0x%" PRIxPTR, ptr); +#else + spew(".int 0x%" PRIxPTR, ptr); +#endif m_formatter.jumpTablePointer(ptr); } @@ -3838,17 +3298,7 @@ } // Linking & patching: - // - // 'link' and 'patch' methods are for use on unprotected code - such as the - // code within the AssemblerBuffer, and code being patched by the patch - // buffer. Once code has been finalized it is (platform support permitting) - // within a non- writable region of memory; to modify the code in an - // execute-only execuable pool the 'repatch' and 'relink' methods should be - // used. - - // Like Lua's emitter, we thread jump lists through the unpatched target - // field, which will get fixed up when the label (which has a pointer to the - // head of the jump list) is bound. + bool nextJump(const JmpSrc& from, JmpSrc* next) { // Sanity check - if the assembler has OOM'd, it will start overwriting @@ -3856,8 +3306,8 @@ if (oom()) return false; - char* code = reinterpret_cast(m_formatter.data()); - int32_t offset = getInt32(code + from.m_offset); + const unsigned char* code = m_formatter.data(); + int32_t offset = GetInt32(code + from.offset()); if (offset == -1) return false; *next = JmpSrc(offset); @@ -3870,141 +3320,23 @@ if (oom()) return; - char* code = reinterpret_cast(m_formatter.data()); - setInt32(code + from.m_offset, to.m_offset); + unsigned char* code = m_formatter.data(); + SetInt32(code + from.offset(), to.offset()); } void linkJump(JmpSrc from, JmpDst to) { - MOZ_ASSERT(from.m_offset != -1); - MOZ_ASSERT(to.m_offset != -1); + MOZ_ASSERT(from.offset() != -1); + MOZ_ASSERT(to.offset() != -1); // Sanity check - if the assembler has OOM'd, it will start overwriting // its internal buffer and thus our links could be garbage. if (oom()) return; - spew("##link ((%d)) jumps to ((%d))", from.m_offset, to.m_offset); - char* code = reinterpret_cast(m_formatter.data()); - setRel32(code + from.m_offset, code + to.m_offset); - } - - static void linkJump(void* code, JmpSrc from, void* to) - { - MOZ_ASSERT(from.m_offset != -1); - - staticSpew("##link ((%d)) jumps to ((%p))", from.m_offset, to); - setRel32(reinterpret_cast(code) + from.m_offset, to); - } - - static void linkCall(void* code, JmpSrc from, void* to) - { - MOZ_ASSERT(from.m_offset != -1); - - staticSpew("##linkCall"); - setRel32(reinterpret_cast(code) + from.m_offset, to); - } - - static void linkPointer(void* code, JmpDst where, void* value) - { - MOZ_ASSERT(where.m_offset != -1); - - staticSpew("##linkPointer"); - setPointer(reinterpret_cast(code) + where.m_offset, value); - } - - static void relinkJump(void* from, void* to) - { - staticSpew("##relinkJump ((from=%p)) ((to=%p))", from, to); - setRel32(from, to); - } - - static bool canRelinkJump(void* from, void* to) - { - intptr_t offset = reinterpret_cast(to) - reinterpret_cast(from); - return (offset == static_cast(offset)); - } - - static void relinkCall(void* from, void* to) - { - staticSpew("##relinkCall ((from=%p)) ((to=%p))", from, to); - setRel32(from, to); - } - - static void repatchInt32(void* where, int32_t value) - { - staticSpew("##relinkInt32 ((where=%p)) ((value=%d))", where, value); - setInt32(where, value); - } - - static void repatchPointer(void* where, const void* value) - { - staticSpew("##repatchPtr ((where=%p)) ((value=%p))", where, value); - setPointer(where, value); - } - - static void repatchLoadPtrToLEA(void* where) - { - staticSpew("##repatchLoadPtrToLEA ((where=%p))", where); - -#ifdef JS_CODEGEN_X64 - // On x86-64 pointer memory accesses require a 64-bit operand, and as - // such a REX prefix. Skip over the prefix byte. - where = reinterpret_cast(where) + 1; -#endif - *reinterpret_cast(where) = static_cast(OP_LEA); - } - - static void repatchLEAToLoadPtr(void* where) - { - staticSpew("##repatchLEAToLoadPtr ((where=%p))", where); -#ifdef JS_CODEGEN_X64 - // On x86-64 pointer memory accesses require a 64-bit operand, and as - // such a REX prefix. Skip over the prefix byte. - where = reinterpret_cast(where) + 1; -#endif - *reinterpret_cast(where) = static_cast(OP_MOV_GvEv); - } - - static unsigned getCallReturnOffset(JmpSrc call) - { - MOZ_ASSERT(call.m_offset >= 0); - return call.m_offset; - } - - static void* getRelocatedAddress(void* code, JmpSrc jump) - { - MOZ_ASSERT(jump.m_offset != -1); - - return reinterpret_cast(reinterpret_cast(code) + jump.m_offset); - } - - static void* getRelocatedAddress(void* code, JmpDst destination) - { - MOZ_ASSERT(destination.m_offset != -1); - - return reinterpret_cast(reinterpret_cast(code) + destination.m_offset); - } - - static int getDifferenceBetweenLabels(JmpDst src, JmpDst dst) - { - return dst.m_offset - src.m_offset; - } - - static int getDifferenceBetweenLabels(JmpDst src, JmpSrc dst) - { - return dst.m_offset - src.m_offset; - } - - static int getDifferenceBetweenLabels(JmpSrc src, JmpDst dst) - { - return dst.m_offset - src.m_offset; - } - - void* executableAllocAndCopy(js::jit::ExecutableAllocator* allocator, - js::jit::ExecutablePool **poolp, js::jit::CodeKind kind) - { - return m_formatter.executableAllocAndCopy(allocator, poolp, kind); + spew(".set .Lfrom%d, .Llabel%d", from.offset(), to.offset()); + unsigned char* code = m_formatter.data(); + SetRel32(code + from.offset(), code + to.offset()); } void executableCopy(void* buffer) @@ -4012,66 +3344,14 @@ memcpy(buffer, m_formatter.buffer(), size()); } - static void setRel32(void* from, void* to) - { - intptr_t offset = reinterpret_cast(to) - reinterpret_cast(from); - MOZ_ASSERT(offset == static_cast(offset), - "offset is too great for a 32-bit relocation"); - if (offset != static_cast(offset)) - MOZ_CRASH("offset is too great for a 32-bit relocation"); - - staticSpew("##setRel32 ((from=%p)) ((to=%p))", from, to); - setInt32(from, offset); - } - - static void *getRel32Target(void* where) - { - int32_t rel = getInt32(where); - return (char *)where + rel; - } - - static void *getPointer(void* where) - { - return reinterpret_cast(where)[-1]; - } - - static void **getPointerRef(void* where) - { - return &reinterpret_cast(where)[-1]; - } - - static void setPointer(void* where, const void* value) - { - staticSpew("##setPtr ((where=%p)) ((value=%p))", where, value); - reinterpret_cast(where)[-1] = value; - } - - // Test whether the given address will fit in an address immediate field. - // This is always true on x86, but on x64 it's only true for addreses which - // fit in the 32-bit immediate field. - static bool isAddressImmediate(const void *address) { - intptr_t value = reinterpret_cast(address); - int32_t immediate = static_cast(value); - return value == immediate; - } - - // Convert the given address to a 32-bit immediate field value. This is a - // no-op on x86, but on x64 it asserts that the address is actually a valid - // address immediate. - static int32_t addressImmediate(const void *address) { -#ifdef JS_CODEGEN_X64 - // x64's 64-bit addresses don't all fit in the 32-bit immediate. - MOZ_ASSERT(isAddressImmediate(address)); -#endif - return static_cast(reinterpret_cast(address)); - } - - static void setInt32(void* where, int32_t value) - { - reinterpret_cast(where)[-1] = value; - } + private: + static bool CAN_SIGN_EXTEND_8_32(int32_t value) { return value == (int32_t)(int8_t)value; } + static bool CAN_SIGN_EXTEND_16_32(int32_t value) { return value == (int32_t)(int16_t)value; } + static bool CAN_ZERO_EXTEND_8_32(int32_t value) { return value == (int32_t)(uint8_t)value; } + static bool CAN_ZERO_EXTEND_8H_32(int32_t value) { return value == (value & 0xff00); } + static bool CAN_ZERO_EXTEND_16_32(int32_t value) { return value == (int32_t)(uint16_t)value; } + static bool CAN_ZERO_EXTEND_32_64(int32_t value) { return value >= 0; } -private: // Methods for encoding SIMD instructions via either legacy SSE encoding or // VEX encoding. @@ -4079,7 +3359,7 @@ { // If we don't have AVX or it's disabled, use the legacy SSE encoding. if (!useVEX_) { - MOZ_ASSERT(src0 == X86Registers::invalid_xmm || src0 == dst, + MOZ_ASSERT(src0 == invalid_xmm || src0 == dst, "Legacy SSE (pre-AVX) encoding requires the output register to be " "the same as the src0 input register"); return true; @@ -4100,13 +3380,13 @@ MOZ_ASSERT(src0 == dst, "Legacy SSE (pre-AVX) encoding requires the output register to be " "the same as the src0 input register"); - MOZ_ASSERT(mask == X86Registers::xmm0, + MOZ_ASSERT(mask == xmm0, "Legacy SSE (pre-AVX) encoding for blendv requires the mask to be " "in xmm0"); return true; } - return src0 == dst && mask == X86Registers::xmm0; + return src0 == dst && mask == xmm0; } bool useLegacySSEEncodingForOtherOutput() @@ -4121,29 +3401,30 @@ } #ifdef JS_CODEGEN_X64 - JmpSrc twoByteRipOpSimd(const char *name, VexOperandType ty, TwoByteOpcodeID opcode, - int ripOffset, XMMRegisterID src0, XMMRegisterID dst) + MOZ_WARN_UNUSED_RESULT JmpSrc + twoByteRipOpSimd(const char *name, VexOperandType ty, TwoByteOpcodeID opcode, + XMMRegisterID src0, XMMRegisterID dst) { if (useLegacySSEEncoding(src0, dst)) { m_formatter.legacySSEPrefix(ty); - m_formatter.twoByteRipOp(opcode, ripOffset, dst); + m_formatter.twoByteRipOp(opcode, 0, dst); JmpSrc label(m_formatter.size()); if (IsXMMReversedOperands(opcode)) - spew("%-11s%s, .Lfrom%d%+d(%%rip)", legacySSEOpName(name), nameFPReg(dst), label.offset(), ripOffset); + spew("%-11s%s, " MEM_o32r "", legacySSEOpName(name), XMMRegName(dst), ADDR_o32r(label.offset())); else - spew("%-11s.Lfrom%d%+d(%%rip), %s", legacySSEOpName(name), label.offset(), ripOffset, nameFPReg(dst)); + spew("%-11s" MEM_o32r ", %s", legacySSEOpName(name), ADDR_o32r(label.offset()), XMMRegName(dst)); return label; } - m_formatter.twoByteRipOpVex(ty, opcode, ripOffset, src0, dst); + m_formatter.twoByteRipOpVex(ty, opcode, 0, src0, dst); JmpSrc label(m_formatter.size()); - if (src0 == X86Registers::invalid_xmm) { + if (src0 == invalid_xmm) { if (IsXMMReversedOperands(opcode)) - spew("%-11s%s, .Lfrom%d%+d(%%rip)", name, nameFPReg(dst), label.offset(), ripOffset); + spew("%-11s%s, " MEM_o32r "", name, XMMRegName(dst), ADDR_o32r(label.offset())); else - spew("%-11s.Lfrom%d%+d(%%rip), %s", name, label.offset(), ripOffset, nameFPReg(dst)); + spew("%-11s" MEM_o32r ", %s", name, ADDR_o32r(label.offset()), XMMRegName(dst)); } else { - spew("%-11s.Lfrom%d%+d(%%rip), %s, %s", name, label.offset(), ripOffset, nameFPReg(src0), nameFPReg(dst)); + spew("%-11s" MEM_o32r ", %s, %s", name, ADDR_o32r(label.offset()), XMMRegName(src0), XMMRegName(dst)); } return label; } @@ -4154,21 +3435,21 @@ { if (useLegacySSEEncoding(src0, dst)) { if (IsXMMReversedOperands(opcode)) - spew("%-11s%s, %s", legacySSEOpName(name), nameFPReg(dst), nameFPReg(rm)); + spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName(dst), XMMRegName(rm)); else - spew("%-11s%s, %s", legacySSEOpName(name), nameFPReg(rm), nameFPReg(dst)); + spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName(rm), XMMRegName(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.twoByteOp(opcode, (RegisterID)rm, dst); return; } - if (src0 == X86Registers::invalid_xmm) { + if (src0 == invalid_xmm) { if (IsXMMReversedOperands(opcode)) - spew("%-11s%s, %s", name, nameFPReg(dst), nameFPReg(rm)); + spew("%-11s%s, %s", name, XMMRegName(dst), XMMRegName(rm)); else - spew("%-11s%s, %s", name, nameFPReg(rm), nameFPReg(dst)); + spew("%-11s%s, %s", name, XMMRegName(rm), XMMRegName(dst)); } else { - spew("%-11s%s, %s, %s", name, nameFPReg(rm), nameFPReg(src0), nameFPReg(dst)); + spew("%-11s%s, %s, %s", name, XMMRegName(rm), XMMRegName(src0), XMMRegName(dst)); } m_formatter.twoByteOpVex(ty, opcode, (RegisterID)rm, src0, dst); } @@ -4177,17 +3458,17 @@ uint32_t imm, XMMRegisterID rm, XMMRegisterID src0, XMMRegisterID dst) { if (useLegacySSEEncoding(src0, dst)) { - spew("%-11s$0x%x, %s, %s", legacySSEOpName(name), imm, nameFPReg(rm), nameFPReg(dst)); + spew("%-11s$0x%x, %s, %s", legacySSEOpName(name), imm, XMMRegName(rm), XMMRegName(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.twoByteOp(opcode, (RegisterID)rm, dst); m_formatter.immediate8u(imm); return; } - if (src0 == X86Registers::invalid_xmm) - spew("%-11s$0x%x, %s, %s", name, imm, nameFPReg(rm), nameFPReg(dst)); + if (src0 == invalid_xmm) + spew("%-11s$0x%x, %s, %s", name, imm, XMMRegName(rm), XMMRegName(dst)); else - spew("%-11s$0x%x, %s, %s, %s", name, imm, nameFPReg(rm), nameFPReg(src0), nameFPReg(dst)); + spew("%-11s$0x%x, %s, %s, %s", name, imm, XMMRegName(rm), XMMRegName(src0), XMMRegName(dst)); m_formatter.twoByteOpVex(ty, opcode, (RegisterID)rm, src0, dst); m_formatter.immediate8u(imm); } @@ -4198,24 +3479,24 @@ if (useLegacySSEEncoding(src0, dst)) { if (IsXMMReversedOperands(opcode)) { spew("%-11s%s, " MEM_ob, legacySSEOpName(name), - nameFPReg(dst), ADDR_ob(offset, base)); + XMMRegName(dst), ADDR_ob(offset, base)); } else { spew("%-11s" MEM_ob ", %s", legacySSEOpName(name), - ADDR_ob(offset, base), nameFPReg(dst)); + ADDR_ob(offset, base), XMMRegName(dst)); } m_formatter.legacySSEPrefix(ty); m_formatter.twoByteOp(opcode, offset, base, dst); return; } - if (src0 == X86Registers::invalid_xmm) { + if (src0 == invalid_xmm) { if (IsXMMReversedOperands(opcode)) - spew("%-11s%s, " MEM_ob, name, nameFPReg(dst), ADDR_ob(offset, base)); + spew("%-11s%s, " MEM_ob, name, XMMRegName(dst), ADDR_ob(offset, base)); else - spew("%-11s" MEM_ob ", %s", name, ADDR_ob(offset, base), nameFPReg(dst)); + spew("%-11s" MEM_ob ", %s", name, ADDR_ob(offset, base), XMMRegName(dst)); } else { spew("%-11s" MEM_ob ", %s, %s", name, - ADDR_ob(offset, base), nameFPReg(src0), nameFPReg(dst)); + ADDR_ob(offset, base), XMMRegName(src0), XMMRegName(dst)); } m_formatter.twoByteOpVex(ty, opcode, offset, base, src0, dst); } @@ -4225,22 +3506,22 @@ { if (useLegacySSEEncoding(src0, dst)) { if (IsXMMReversedOperands(opcode)) - spew("%-11s%s, " MEM_o32b, legacySSEOpName(name), nameFPReg(dst), ADDR_o32b(offset, base)); + spew("%-11s%s, " MEM_o32b, legacySSEOpName(name), XMMRegName(dst), ADDR_o32b(offset, base)); else - spew("%-11s" MEM_o32b ", %s", legacySSEOpName(name), ADDR_o32b(offset, base), nameFPReg(dst)); + spew("%-11s" MEM_o32b ", %s", legacySSEOpName(name), ADDR_o32b(offset, base), XMMRegName(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.twoByteOp_disp32(opcode, offset, base, dst); return; } - if (src0 == X86Registers::invalid_xmm) { + if (src0 == invalid_xmm) { if (IsXMMReversedOperands(opcode)) - spew("%-11s%s, " MEM_o32b, name, nameFPReg(dst), ADDR_o32b(offset, base)); + spew("%-11s%s, " MEM_o32b, name, XMMRegName(dst), ADDR_o32b(offset, base)); else - spew("%-11s" MEM_o32b ", %s", name, ADDR_o32b(offset, base), nameFPReg(dst)); + spew("%-11s" MEM_o32b ", %s", name, ADDR_o32b(offset, base), XMMRegName(dst)); } else { spew("%-11s" MEM_o32b ", %s, %s", name, - ADDR_o32b(offset, base), nameFPReg(src0), nameFPReg(dst)); + ADDR_o32b(offset, base), XMMRegName(src0), XMMRegName(dst)); } m_formatter.twoByteOpVex_disp32(ty, opcode, offset, base, src0, dst); } @@ -4250,7 +3531,7 @@ { if (useLegacySSEEncoding(src0, dst)) { spew("%-11s$0x%x, " MEM_ob ", %s", legacySSEOpName(name), imm, - ADDR_ob(offset, base), nameFPReg(dst)); + ADDR_ob(offset, base), XMMRegName(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.twoByteOp(opcode, offset, base, dst); m_formatter.immediate8u(imm); @@ -4258,7 +3539,7 @@ } spew("%-11s$0x%x, " MEM_ob ", %s, %s", name, imm, ADDR_ob(offset, base), - nameFPReg(src0), nameFPReg(dst)); + XMMRegName(src0), XMMRegName(dst)); m_formatter.twoByteOpVex(ty, opcode, offset, base, src0, dst); m_formatter.immediate8u(imm); } @@ -4270,27 +3551,27 @@ if (useLegacySSEEncoding(src0, dst)) { if (IsXMMReversedOperands(opcode)) { spew("%-11s%s, " MEM_obs, legacySSEOpName(name), - nameFPReg(dst), ADDR_obs(offset, base, index, scale)); + XMMRegName(dst), ADDR_obs(offset, base, index, scale)); } else { spew("%-11s" MEM_obs ", %s", legacySSEOpName(name), - ADDR_obs(offset, base, index, scale), nameFPReg(dst)); + ADDR_obs(offset, base, index, scale), XMMRegName(dst)); } m_formatter.legacySSEPrefix(ty); m_formatter.twoByteOp(opcode, offset, base, index, scale, dst); return; } - if (src0 == X86Registers::invalid_xmm) { + if (src0 == invalid_xmm) { if (IsXMMReversedOperands(opcode)) { - spew("%-11s%s, " MEM_obs, name, nameFPReg(dst), + spew("%-11s%s, " MEM_obs, name, XMMRegName(dst), ADDR_obs(offset, base, index, scale)); } else { spew("%-11s" MEM_obs ", %s", name, ADDR_obs(offset, base, index, scale), - nameFPReg(dst)); + XMMRegName(dst)); } } else { spew("%-11s" MEM_obs ", %s, %s", name, ADDR_obs(offset, base, index, scale), - nameFPReg(src0), nameFPReg(dst)); + XMMRegName(src0), XMMRegName(dst)); } m_formatter.twoByteOpVex(ty, opcode, offset, base, index, scale, src0, dst); } @@ -4300,21 +3581,21 @@ { if (useLegacySSEEncoding(src0, dst)) { if (IsXMMReversedOperands(opcode)) - spew("%-11s%s, %p", legacySSEOpName(name), nameFPReg(dst), address); + spew("%-11s%s, %p", legacySSEOpName(name), XMMRegName(dst), address); else - spew("%-11s%p, %s", legacySSEOpName(name), address, nameFPReg(dst)); + spew("%-11s%p, %s", legacySSEOpName(name), address, XMMRegName(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.twoByteOp(opcode, address, dst); return; } - if (src0 == X86Registers::invalid_xmm) { + if (src0 == invalid_xmm) { if (IsXMMReversedOperands(opcode)) - spew("%-11s%s, %p", name, nameFPReg(dst), address); + spew("%-11s%s, %p", name, XMMRegName(dst), address); else - spew("%-11s%p, %s", name, address, nameFPReg(dst)); + spew("%-11s%p, %s", name, address, XMMRegName(dst)); } else { - spew("%-11s%p, %s, %s", name, address, nameFPReg(src0), nameFPReg(dst)); + spew("%-11s%p, %s, %s", name, address, XMMRegName(src0), XMMRegName(dst)); } m_formatter.twoByteOpVex(ty, opcode, address, src0, dst); } @@ -4323,14 +3604,14 @@ uint32_t imm, const void *address, XMMRegisterID src0, XMMRegisterID dst) { if (useLegacySSEEncoding(src0, dst)) { - spew("%-11s$0x%x, %p, %s", legacySSEOpName(name), imm, address, nameFPReg(dst)); + spew("%-11s$0x%x, %p, %s", legacySSEOpName(name), imm, address, XMMRegName(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.twoByteOp(opcode, address, dst); m_formatter.immediate8u(imm); return; } - spew("%-11s$0x%x, %p, %s, %s", name, imm, address, nameFPReg(src0), nameFPReg(dst)); + spew("%-11s$0x%x, %p, %s, %s", name, imm, address, XMMRegName(src0), XMMRegName(dst)); m_formatter.twoByteOpVex(ty, opcode, address, src0, dst); m_formatter.immediate8u(imm); } @@ -4340,21 +3621,21 @@ { if (useLegacySSEEncoding(src0, dst)) { if (IsXMMReversedOperands(opcode)) - spew("%-11s%s, %s", legacySSEOpName(name), nameFPReg(dst), nameIReg(4, rm)); + spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName(dst), GPReg32Name(rm)); else - spew("%-11s%s, %s", legacySSEOpName(name), nameIReg(4, rm), nameFPReg(dst)); + spew("%-11s%s, %s", legacySSEOpName(name), GPReg32Name(rm), XMMRegName(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.twoByteOp(opcode, rm, dst); return; } - if (src0 == X86Registers::invalid_xmm) { + if (src0 == invalid_xmm) { if (IsXMMReversedOperands(opcode)) - spew("%-11s%s, %s", name, nameFPReg(dst), nameIReg(4, rm)); + spew("%-11s%s, %s", name, XMMRegName(dst), GPReg32Name(rm)); else - spew("%-11s%s, %s", name, nameIReg(4, rm), nameFPReg(dst)); + spew("%-11s%s, %s", name, GPReg32Name(rm), XMMRegName(dst)); } else { - spew("%-11s%s, %s, %s", name, nameIReg(4, rm), nameFPReg(src0), nameFPReg(dst)); + spew("%-11s%s, %s, %s", name, GPReg32Name(rm), XMMRegName(src0), XMMRegName(dst)); } m_formatter.twoByteOpVex(ty, opcode, rm, src0, dst); } @@ -4365,21 +3646,21 @@ { if (useLegacySSEEncoding(src0, dst)) { if (IsXMMReversedOperands(opcode)) - spew("%-11s%s, %s", legacySSEOpName(name), nameFPReg(dst), nameIReg(rm)); + spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName(dst), GPRegName(rm)); else - spew("%-11s%s, %s", legacySSEOpName(name), nameIReg(rm), nameFPReg(dst)); + spew("%-11s%s, %s", legacySSEOpName(name), GPRegName(rm), XMMRegName(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.twoByteOp64(opcode, rm, dst); return; } - if (src0 == X86Registers::invalid_xmm) { + if (src0 == invalid_xmm) { if (IsXMMReversedOperands(opcode)) - spew("%-11s%s, %s", name, nameFPReg(dst), nameIReg(rm)); + spew("%-11s%s, %s", name, XMMRegName(dst), GPRegName(rm)); else - spew("%-11s%s, %s", name, nameIReg(rm), nameFPReg(dst)); + spew("%-11s%s, %s", name, GPRegName(rm), XMMRegName(dst)); } else { - spew("%-11s%s, %s, %s", name, nameIReg(rm), nameFPReg(src0), nameFPReg(dst)); + spew("%-11s%s, %s, %s", name, GPRegName(rm), XMMRegName(src0), XMMRegName(dst)); } m_formatter.twoByteOpVex64(ty, opcode, rm, src0, dst); } @@ -4390,38 +3671,38 @@ { if (useLegacySSEEncodingForOtherOutput()) { if (IsXMMReversedOperands(opcode)) - spew("%-11s%s, %s", legacySSEOpName(name), nameIReg(4, dst), nameFPReg(rm)); + spew("%-11s%s, %s", legacySSEOpName(name), GPReg32Name(dst), XMMRegName(rm)); else if (opcode == OP2_MOVD_EdVd) - spew("%-11s%s, %s", legacySSEOpName(name), nameFPReg((XMMRegisterID)dst), nameIReg(4, (RegisterID)rm)); + spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName((XMMRegisterID)dst), GPReg32Name((RegisterID)rm)); else - spew("%-11s%s, %s", legacySSEOpName(name), nameFPReg(rm), nameIReg(4, dst)); + spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName(rm), GPReg32Name(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.twoByteOp(opcode, (RegisterID)rm, dst); return; } if (IsXMMReversedOperands(opcode)) - spew("%-11s%s, %s", name, nameIReg(4, dst), nameFPReg(rm)); + spew("%-11s%s, %s", name, GPReg32Name(dst), XMMRegName(rm)); else if (opcode == OP2_MOVD_EdVd) - spew("%-11s%s, %s", name, nameFPReg((XMMRegisterID)dst), nameIReg(4, (RegisterID)rm)); + spew("%-11s%s, %s", name, XMMRegName((XMMRegisterID)dst), GPReg32Name((RegisterID)rm)); else - spew("%-11s%s, %s", name, nameFPReg(rm), nameIReg(4, dst)); - m_formatter.twoByteOpVex(ty, opcode, (RegisterID)rm, X86Registers::invalid_xmm, dst); + spew("%-11s%s, %s", name, XMMRegName(rm), GPReg32Name(dst)); + m_formatter.twoByteOpVex(ty, opcode, (RegisterID)rm, invalid_xmm, dst); } void twoByteOpImmSimdInt32(const char *name, VexOperandType ty, TwoByteOpcodeID opcode, uint32_t imm, XMMRegisterID rm, RegisterID dst) { if (useLegacySSEEncodingForOtherOutput()) { - spew("%-11s$0x%x, %s, %s", legacySSEOpName(name), imm, nameFPReg(rm), nameIReg(4, dst)); + spew("%-11s$0x%x, %s, %s", legacySSEOpName(name), imm, XMMRegName(rm), GPReg32Name(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.twoByteOp(opcode, (RegisterID)rm, dst); m_formatter.immediate8u(imm); return; } - spew("%-11s$0x%x, %s, %s", name, imm, nameFPReg(rm), nameIReg(4, dst)); - m_formatter.twoByteOpVex(ty, opcode, (RegisterID)rm, X86Registers::invalid_xmm, dst); + spew("%-11s$0x%x, %s, %s", name, imm, XMMRegName(rm), GPReg32Name(dst)); + m_formatter.twoByteOpVex(ty, opcode, (RegisterID)rm, invalid_xmm, dst); m_formatter.immediate8u(imm); } @@ -4431,23 +3712,23 @@ { if (useLegacySSEEncodingForOtherOutput()) { if (IsXMMReversedOperands(opcode)) - spew("%-11s%s, %s", legacySSEOpName(name), nameIReg(dst), nameFPReg(rm)); + spew("%-11s%s, %s", legacySSEOpName(name), GPRegName(dst), XMMRegName(rm)); else if (opcode == OP2_MOVD_EdVd) - spew("%-11s%s, %s", legacySSEOpName(name), nameFPReg((XMMRegisterID)dst), nameIReg((RegisterID)rm)); + spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName((XMMRegisterID)dst), GPRegName((RegisterID)rm)); else - spew("%-11s%s, %s", legacySSEOpName(name), nameFPReg(rm), nameIReg(dst)); + spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName(rm), GPRegName(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.twoByteOp64(opcode, (RegisterID)rm, dst); return; } if (IsXMMReversedOperands(opcode)) - spew("%-11s%s, %s", name, nameIReg(dst), nameFPReg(rm)); + spew("%-11s%s, %s", name, GPRegName(dst), XMMRegName(rm)); else if (opcode == OP2_MOVD_EdVd) - spew("%-11s%s, %s", name, nameFPReg((XMMRegisterID)dst), nameIReg((RegisterID)rm)); + spew("%-11s%s, %s", name, XMMRegName((XMMRegisterID)dst), GPRegName((RegisterID)rm)); else - spew("%-11s%s, %s", name, nameFPReg(rm), nameIReg(dst)); - m_formatter.twoByteOpVex64(ty, opcode, (RegisterID)rm, X86Registers::invalid_xmm, (XMMRegisterID)dst); + spew("%-11s%s, %s", name, XMMRegName(rm), GPRegName(dst)); + m_formatter.twoByteOpVex64(ty, opcode, (RegisterID)rm, invalid_xmm, (XMMRegisterID)dst); } #endif @@ -4455,14 +3736,14 @@ XMMRegisterID rm, XMMRegisterID reg) { if (useLegacySSEEncodingForOtherOutput()) { - spew("%-11s%s, %s", legacySSEOpName(name), nameFPReg(rm), nameFPReg(reg)); + spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName(rm), XMMRegName(reg)); m_formatter.legacySSEPrefix(ty); m_formatter.twoByteOp(opcode, (RegisterID)rm, reg); return; } - spew("%-11s%s, %s", name, nameFPReg(rm), nameFPReg(reg)); - m_formatter.twoByteOpVex(ty, opcode, (RegisterID)rm, X86Registers::invalid_xmm, (XMMRegisterID)reg); + spew("%-11s%s, %s", name, XMMRegName(rm), XMMRegName(reg)); + m_formatter.twoByteOpVex(ty, opcode, (RegisterID)rm, invalid_xmm, (XMMRegisterID)reg); } void twoByteOpSimdFlags(const char *name, VexOperandType ty, TwoByteOpcodeID opcode, @@ -4470,15 +3751,15 @@ { if (useLegacySSEEncodingForOtherOutput()) { spew("%-11s" MEM_ob ", %s", legacySSEOpName(name), - ADDR_ob(offset, base), nameFPReg(reg)); + ADDR_ob(offset, base), XMMRegName(reg)); m_formatter.legacySSEPrefix(ty); m_formatter.twoByteOp(opcode, offset, base, reg); return; } spew("%-11s" MEM_ob ", %s", name, - ADDR_ob(offset, base), nameFPReg(reg)); - m_formatter.twoByteOpVex(ty, opcode, offset, base, X86Registers::invalid_xmm, (XMMRegisterID)reg); + ADDR_ob(offset, base), XMMRegName(reg)); + m_formatter.twoByteOpVex(ty, opcode, offset, base, invalid_xmm, (XMMRegisterID)reg); } void threeByteOpSimd(const char *name, VexOperandType ty, ThreeByteOpcodeID opcode, @@ -4486,13 +3767,13 @@ XMMRegisterID rm, XMMRegisterID src0, XMMRegisterID dst) { if (useLegacySSEEncoding(src0, dst)) { - spew("%-11s%s, %s", legacySSEOpName(name), nameFPReg(rm), nameFPReg(dst)); + spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName(rm), XMMRegName(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.threeByteOp(opcode, escape, (RegisterID)rm, dst); return; } - spew("%-11s%s, %s, %s", name, nameFPReg(rm), nameFPReg(src0), nameFPReg(dst)); + spew("%-11s%s, %s, %s", name, XMMRegName(rm), XMMRegName(src0), XMMRegName(dst)); m_formatter.threeByteOpVex(ty, opcode, escape, (RegisterID)rm, src0, dst); } @@ -4501,14 +3782,14 @@ uint32_t imm, XMMRegisterID rm, XMMRegisterID src0, XMMRegisterID dst) { if (useLegacySSEEncoding(src0, dst)) { - spew("%-11s$0x%x, %s, %s", legacySSEOpName(name), imm, nameFPReg(rm), nameFPReg(dst)); + spew("%-11s$0x%x, %s, %s", legacySSEOpName(name), imm, XMMRegName(rm), XMMRegName(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.threeByteOp(opcode, escape, (RegisterID)rm, dst); m_formatter.immediate8u(imm); return; } - spew("%-11s$0x%x, %s, %s, %s", name, imm, nameFPReg(rm), nameFPReg(src0), nameFPReg(dst)); + spew("%-11s$0x%x, %s, %s, %s", name, imm, XMMRegName(rm), XMMRegName(src0), XMMRegName(dst)); m_formatter.threeByteOpVex(ty, opcode, escape, (RegisterID)rm, src0, dst); m_formatter.immediate8u(imm); } @@ -4519,14 +3800,14 @@ { if (useLegacySSEEncoding(src0, dst)) { spew("%-11s" MEM_ob ", %s", legacySSEOpName(name), - ADDR_ob(offset, base), nameFPReg(dst)); + ADDR_ob(offset, base), XMMRegName(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.threeByteOp(opcode, escape, offset, base, dst); return; } spew("%-11s" MEM_ob ", %s, %s", name, - ADDR_ob(offset, base), nameFPReg(src0), nameFPReg(dst)); + ADDR_ob(offset, base), XMMRegName(src0), XMMRegName(dst)); m_formatter.threeByteOpVex(ty, opcode, escape, offset, base, src0, dst); } @@ -4536,7 +3817,7 @@ { if (useLegacySSEEncoding(src0, dst)) { spew("%-11s$0x%x, " MEM_ob ", %s", legacySSEOpName(name), imm, - ADDR_ob(offset, base), nameFPReg(dst)); + ADDR_ob(offset, base), XMMRegName(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.threeByteOp(opcode, escape, offset, base, dst); m_formatter.immediate8u(imm); @@ -4544,7 +3825,7 @@ } spew("%-11s$0x%x, " MEM_ob ", %s, %s", name, imm, ADDR_ob(offset, base), - nameFPReg(src0), nameFPReg(dst)); + XMMRegName(src0), XMMRegName(dst)); m_formatter.threeByteOpVex(ty, opcode, escape, offset, base, src0, dst); m_formatter.immediate8u(imm); } @@ -4554,13 +3835,13 @@ const void *address, XMMRegisterID src0, XMMRegisterID dst) { if (useLegacySSEEncoding(src0, dst)) { - spew("%-11s%p, %s", legacySSEOpName(name), address, nameFPReg(dst)); + spew("%-11s%p, %s", legacySSEOpName(name), address, XMMRegName(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.threeByteOp(opcode, escape, address, dst); return; } - spew("%-11s%p, %s, %s", name, address, nameFPReg(src0), nameFPReg(dst)); + spew("%-11s%p, %s, %s", name, address, XMMRegName(src0), XMMRegName(dst)); m_formatter.threeByteOpVex(ty, opcode, escape, address, src0, dst); } @@ -4569,14 +3850,14 @@ RegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { if (useLegacySSEEncoding(src0, dst)) { - spew("%-11s$0x%x, %s, %s", legacySSEOpName(name), imm, nameIReg(4, src1), nameFPReg(dst)); + spew("%-11s$0x%x, %s, %s", legacySSEOpName(name), imm, GPReg32Name(src1), XMMRegName(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.threeByteOp(opcode, escape, src1, dst); m_formatter.immediate8u(imm); return; } - spew("%-11s$0x%x, %s, %s, %s", name, imm, nameIReg(4, src1), nameFPReg(src0), nameFPReg(dst)); + spew("%-11s$0x%x, %s, %s, %s", name, imm, GPReg32Name(src1), XMMRegName(src0), XMMRegName(dst)); m_formatter.threeByteOpVex(ty, opcode, escape, src1, src0, dst); m_formatter.immediate8u(imm); } @@ -4586,14 +3867,14 @@ int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) { if (useLegacySSEEncoding(src0, dst)) { - spew("%-11s$0x%x, " MEM_ob ", %s", legacySSEOpName(name), imm, ADDR_ob(offset, base), nameFPReg(dst)); + spew("%-11s$0x%x, " MEM_ob ", %s", legacySSEOpName(name), imm, ADDR_ob(offset, base), XMMRegName(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.threeByteOp(opcode, escape, offset, base, dst); m_formatter.immediate8u(imm); return; } - spew("%-11s$0x%x, " MEM_ob ", %s, %s", name, imm, ADDR_ob(offset, base), nameFPReg(src0), nameFPReg(dst)); + spew("%-11s$0x%x, " MEM_ob ", %s, %s", name, imm, ADDR_ob(offset, base), XMMRegName(src0), XMMRegName(dst)); m_formatter.threeByteOpVex(ty, opcode, escape, offset, base, src0, dst); m_formatter.immediate8u(imm); } @@ -4603,7 +3884,7 @@ XMMRegisterID src, RegisterID dst) { if (useLegacySSEEncodingForOtherOutput()) { - spew("%-11s$0x%x, %s, %s", legacySSEOpName(name), imm, nameFPReg(src), nameIReg(4, dst)); + spew("%-11s$0x%x, %s, %s", legacySSEOpName(name), imm, XMMRegName(src), GPReg32Name(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.threeByteOp(opcode, escape, (RegisterID)src, dst); m_formatter.immediate8u(imm); @@ -4611,10 +3892,10 @@ } if (opcode == OP3_PEXTRD_EdVdqIb) - spew("%-11s$0x%x, %s, %s", name, imm, nameFPReg((XMMRegisterID)dst), nameIReg(4, (RegisterID)src)); + spew("%-11s$0x%x, %s, %s", name, imm, XMMRegName((XMMRegisterID)dst), GPReg32Name((RegisterID)src)); else - spew("%-11s$0x%x, %s, %s", name, imm, nameFPReg(src), nameIReg(4, dst)); - m_formatter.threeByteOpVex(ty, opcode, escape, (RegisterID)src, X86Registers::invalid_xmm, dst); + spew("%-11s$0x%x, %s, %s", name, imm, XMMRegName(src), GPReg32Name(dst)); + m_formatter.threeByteOpVex(ty, opcode, escape, (RegisterID)src, invalid_xmm, dst); m_formatter.immediate8u(imm); } @@ -4623,15 +3904,15 @@ int32_t offset, RegisterID base, RegisterID dst) { if (useLegacySSEEncodingForOtherOutput()) { - spew("%-11s$0x%x, " MEM_ob ", %s", legacySSEOpName(name), imm, ADDR_ob(offset, base), nameIReg(4, dst)); + spew("%-11s$0x%x, " MEM_ob ", %s", legacySSEOpName(name), imm, ADDR_ob(offset, base), GPReg32Name(dst)); m_formatter.legacySSEPrefix(ty); m_formatter.threeByteOp(opcode, escape, offset, base, dst); m_formatter.immediate8u(imm); return; } - spew("%-11s$0x%x, " MEM_ob ", %s", name, imm, ADDR_ob(offset, base), nameIReg(4, dst)); - m_formatter.threeByteOpVex(ty, opcode, escape, offset, base, X86Registers::invalid_xmm, dst); + spew("%-11s$0x%x, " MEM_ob ", %s", name, imm, ADDR_ob(offset, base), GPReg32Name(dst)); + m_formatter.threeByteOpVex(ty, opcode, escape, offset, base, invalid_xmm, dst); m_formatter.immediate8u(imm); } @@ -4640,34 +3921,34 @@ void vblendvOpSimd(XMMRegisterID mask, XMMRegisterID rm, XMMRegisterID src0, XMMRegisterID dst) { if (useLegacySSEEncodingForVblendv(mask, src0, dst)) { - spew("blendvps %s, %s", nameFPReg(rm), nameFPReg(dst)); + spew("blendvps %s, %s", XMMRegName(rm), XMMRegName(dst)); // Even though a "ps" instruction, vblendv is encoded with the "pd" prefix. m_formatter.legacySSEPrefix(VEX_PD); - m_formatter.threeByteOp(OP3_BLENDVPS_VdqWdq, ESCAPE_BLENDVPS, (RegisterID)rm, dst); + m_formatter.threeByteOp(OP3_BLENDVPS_VdqWdq, ESCAPE_3A, (RegisterID)rm, dst); return; } spew("vblendvps %s, %s, %s, %s", - nameFPReg(mask), nameFPReg(rm), nameFPReg(src0), nameFPReg(dst)); + XMMRegName(mask), XMMRegName(rm), XMMRegName(src0), XMMRegName(dst)); // Even though a "ps" instruction, vblendv is encoded with the "pd" prefix. - m_formatter.vblendvOpVex(VEX_PD, OP3_VBLENDVPS_VdqWdq, ESCAPE_VBLENDVPS, + m_formatter.vblendvOpVex(VEX_PD, OP3_VBLENDVPS_VdqWdq, ESCAPE_3A, mask, (RegisterID)rm, src0, dst); } void vblendvOpSimd(XMMRegisterID mask, int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) { if (useLegacySSEEncodingForVblendv(mask, src0, dst)) { - spew("blendvps " MEM_ob ", %s", ADDR_ob(offset, base), nameFPReg(dst)); + spew("blendvps " MEM_ob ", %s", ADDR_ob(offset, base), XMMRegName(dst)); // Even though a "ps" instruction, vblendv is encoded with the "pd" prefix. m_formatter.legacySSEPrefix(VEX_PD); - m_formatter.threeByteOp(OP3_BLENDVPS_VdqWdq, ESCAPE_BLENDVPS, offset, base, dst); + m_formatter.threeByteOp(OP3_BLENDVPS_VdqWdq, ESCAPE_3A, offset, base, dst); return; } spew("vblendvps %s, " MEM_ob ", %s, %s", - nameFPReg(mask), ADDR_ob(offset, base), nameFPReg(src0), nameFPReg(dst)); + XMMRegName(mask), ADDR_ob(offset, base), XMMRegName(src0), XMMRegName(dst)); // Even though a "ps" instruction, vblendv is encoded with the "pd" prefix. - m_formatter.vblendvOpVex(VEX_PD, OP3_VBLENDVPS_VdqWdq, ESCAPE_VBLENDVPS, + m_formatter.vblendvOpVex(VEX_PD, OP3_VBLENDVPS_VdqWdq, ESCAPE_3A, mask, offset, base, src0, dst); } @@ -4675,27 +3956,20 @@ uint32_t imm, XMMRegisterID src, XMMRegisterID dst) { if (useLegacySSEEncoding(src, dst)) { - spew("%-11s$%d, %s", legacySSEOpName(name), imm, nameFPReg(dst)); + spew("%-11s$%d, %s", legacySSEOpName(name), imm, XMMRegName(dst)); m_formatter.legacySSEPrefix(VEX_PD); m_formatter.twoByteOp(opcode, (RegisterID)dst, (int)shiftKind); m_formatter.immediate8u(imm); return; } - spew("%-11s$%d, %s, %s", name, imm, nameFPReg(src), nameFPReg(dst)); + spew("%-11s$%d, %s, %s", name, imm, XMMRegName(src), XMMRegName(dst)); m_formatter.twoByteOpVex(VEX_PD, opcode, (RegisterID)dst, src, (int)shiftKind); m_formatter.immediate8u(imm); } - static int32_t getInt32(void* where) - { - return reinterpret_cast(where)[-1]; - } - class X86InstructionFormatter { - static const int maxInstructionSize = 16; - public: // Legacy prefix bytes: // @@ -4734,20 +4008,20 @@ void oneByteOp(OneByteOpcodeID opcode) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); m_buffer.putByteUnchecked(opcode); } void oneByteOp(OneByteOpcodeID opcode, RegisterID reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(0, 0, reg); m_buffer.putByteUnchecked(opcode + (reg & 7)); } void oneByteOp(OneByteOpcodeID opcode, RegisterID rm, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, rm); m_buffer.putByteUnchecked(opcode); registerModRM(rm, reg); @@ -4755,7 +4029,7 @@ void oneByteOp(OneByteOpcodeID opcode, int32_t offset, RegisterID base, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, base); m_buffer.putByteUnchecked(opcode); memoryModRM(offset, base, reg); @@ -4763,7 +4037,7 @@ void oneByteOp_disp32(OneByteOpcodeID opcode, int32_t offset, RegisterID base, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, base); m_buffer.putByteUnchecked(opcode); memoryModRM_disp32(offset, base, reg); @@ -4771,7 +4045,7 @@ void oneByteOp(OneByteOpcodeID opcode, int32_t offset, RegisterID base, RegisterID index, int scale, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, index, base); m_buffer.putByteUnchecked(opcode); memoryModRM(offset, base, index, scale, reg); @@ -4779,7 +4053,7 @@ void oneByteOp_disp32(OneByteOpcodeID opcode, int32_t offset, RegisterID index, int scale, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, index, 0); m_buffer.putByteUnchecked(opcode); memoryModRM_disp32(offset, index, scale, reg); @@ -4787,7 +4061,7 @@ void oneByteOp(OneByteOpcodeID opcode, const void* address, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, 0); m_buffer.putByteUnchecked(opcode); memoryModRM_disp32(address, reg); @@ -4795,7 +4069,7 @@ void oneByteOp_disp32(OneByteOpcodeID opcode, const void* address, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, 0); m_buffer.putByteUnchecked(opcode); memoryModRM_disp32(address, reg); @@ -4803,7 +4077,7 @@ #ifdef JS_CODEGEN_X64 void oneByteRipOp(OneByteOpcodeID opcode, int ripOffset, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, 0); m_buffer.putByteUnchecked(opcode); putModRm(ModRmMemoryNoDisp, noBase, reg); @@ -4812,7 +4086,7 @@ void oneByteRipOp64(OneByteOpcodeID opcode, int ripOffset, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexW(reg, 0, 0); m_buffer.putByteUnchecked(opcode); putModRm(ModRmMemoryNoDisp, noBase, reg); @@ -4821,7 +4095,7 @@ void twoByteRipOp(TwoByteOpcodeID opcode, int ripOffset, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, 0); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(opcode); @@ -4843,14 +4117,14 @@ void twoByteOp(TwoByteOpcodeID opcode) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(opcode); } void twoByteOp(TwoByteOpcodeID opcode, RegisterID rm, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, rm); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(opcode); @@ -4869,7 +4143,7 @@ void twoByteOp(TwoByteOpcodeID opcode, int32_t offset, RegisterID base, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, base); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(opcode); @@ -4888,7 +4162,7 @@ void twoByteOp_disp32(TwoByteOpcodeID opcode, int32_t offset, RegisterID base, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, base); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(opcode); @@ -4907,7 +4181,7 @@ void twoByteOp(TwoByteOpcodeID opcode, int32_t offset, RegisterID base, RegisterID index, int scale, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, index, base); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(opcode); @@ -4927,7 +4201,7 @@ void twoByteOp(TwoByteOpcodeID opcode, const void* address, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, 0); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(opcode); @@ -4946,7 +4220,7 @@ void threeByteOp(ThreeByteOpcodeID opcode, ThreeByteEscape escape, RegisterID rm, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, rm); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(escape); @@ -4960,8 +4234,8 @@ int r = (reg >> 3), x = 0, b = (rm >> 3); int m = 0, w = 0, v = src0, l = 0; switch (escape) { - case 0x38: m = 2; break; // 0x0F 0x38 - case 0x3A: m = 3; break; // 0x0F 0x3A + case ESCAPE_38: m = 2; break; + case ESCAPE_3A: m = 3; break; default: MOZ_CRASH("unexpected escape"); } threeOpVex(ty, r, x, b, m, w, v, l, opcode); @@ -4970,7 +4244,7 @@ void threeByteOp(ThreeByteOpcodeID opcode, ThreeByteEscape escape, int32_t offset, RegisterID base, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, base); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(escape); @@ -4984,8 +4258,8 @@ int r = (reg >> 3), x = 0, b = (base >> 3); int m = 0, w = 0, v = src0, l = 0; switch (escape) { - case 0x38: m = 2; break; // 0x0F 0x38 - case 0x3A: m = 3; break; // 0x0F 0x3A + case ESCAPE_38: m = 2; break; + case ESCAPE_3A: m = 3; break; default: MOZ_CRASH("unexpected escape"); } threeOpVex(ty, r, x, b, m, w, v, l, opcode); @@ -4994,7 +4268,7 @@ void threeByteOp(ThreeByteOpcodeID opcode, ThreeByteEscape escape, const void* address, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, 0); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(escape); @@ -5008,8 +4282,8 @@ int r = (reg >> 3), x = 0, b = 0; int m = 0, w = 0, v = src0, l = 0; switch (escape) { - case 0x38: m = 2; break; // 0x0F 0x38 - case 0x3A: m = 3; break; // 0x0F 0x3A + case ESCAPE_38: m = 2; break; + case ESCAPE_3A: m = 3; break; default: MOZ_CRASH("unexpected escape"); } threeOpVex(ty, r, x, b, m, w, v, l, opcode); @@ -5022,8 +4296,8 @@ int r = (reg >> 3), x = 0, b = (rm >> 3); int m = 0, w = 0, v = src0, l = 0; switch (escape) { - case 0x38: m = 2; break; // 0x0F 0x38 - case 0x3A: m = 3; break; // 0x0F 0x3A + case ESCAPE_38: m = 2; break; + case ESCAPE_3A: m = 3; break; default: MOZ_CRASH("unexpected escape"); } threeOpVex(ty, r, x, b, m, w, v, l, opcode); @@ -5037,8 +4311,8 @@ int r = (reg >> 3), x = 0, b = (base >> 3); int m = 0, w = 0, v = src0, l = 0; switch (escape) { - case 0x38: m = 2; break; // 0x0F 0x38 - case 0x3A: m = 3; break; // 0x0F 0x3A + case ESCAPE_38: m = 2; break; + case ESCAPE_3A: m = 3; break; default: MOZ_CRASH("unexpected escape"); } threeOpVex(ty, r, x, b, m, w, v, l, opcode); @@ -5055,21 +4329,21 @@ void oneByteOp64(OneByteOpcodeID opcode) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexW(0, 0, 0); m_buffer.putByteUnchecked(opcode); } void oneByteOp64(OneByteOpcodeID opcode, RegisterID reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexW(0, 0, reg); m_buffer.putByteUnchecked(opcode + (reg & 7)); } void oneByteOp64(OneByteOpcodeID opcode, RegisterID rm, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexW(reg, 0, rm); m_buffer.putByteUnchecked(opcode); registerModRM(rm, reg); @@ -5077,7 +4351,7 @@ void oneByteOp64(OneByteOpcodeID opcode, int32_t offset, RegisterID base, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexW(reg, 0, base); m_buffer.putByteUnchecked(opcode); memoryModRM(offset, base, reg); @@ -5085,7 +4359,7 @@ void oneByteOp64_disp32(OneByteOpcodeID opcode, int32_t offset, RegisterID base, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexW(reg, 0, base); m_buffer.putByteUnchecked(opcode); memoryModRM_disp32(offset, base, reg); @@ -5093,7 +4367,7 @@ void oneByteOp64(OneByteOpcodeID opcode, int32_t offset, RegisterID base, RegisterID index, int scale, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexW(reg, index, base); m_buffer.putByteUnchecked(opcode); memoryModRM(offset, base, index, scale, reg); @@ -5101,7 +4375,7 @@ void oneByteOp64(OneByteOpcodeID opcode, const void* address, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexW(reg, 0, 0); m_buffer.putByteUnchecked(opcode); memoryModRM(address, reg); @@ -5109,7 +4383,7 @@ void twoByteOp64(TwoByteOpcodeID opcode, RegisterID rm, int reg) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); emitRexW(reg, 0, rm); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(opcode); @@ -5140,83 +4414,70 @@ // byte of the second four registers (spl..dil). // // Address operands should still be checked using regRequiresRex(), - // while ByteRegRequiresRex() is provided to check byte register + // while byteRegRequiresRex() is provided to check byte register // operands. void oneByteOp8(OneByteOpcodeID opcode) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); m_buffer.putByteUnchecked(opcode); } void oneByteOp8(OneByteOpcodeID opcode, RegisterID rm, GroupOpcodeID groupOp) { -#ifdef JS_CODEGEN_X86 - MOZ_ASSERT(!ByteRegRequiresRex(rm)); -#endif - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(ByteRegRequiresRex(rm), 0, 0, rm); + m_buffer.ensureSpace(MaxInstructionSize); + emitRexIf(byteRegRequiresRex(rm), 0, 0, rm); m_buffer.putByteUnchecked(opcode); registerModRM(rm, groupOp); } // Like oneByteOp8, but never emits a REX prefix. - void oneByteOp8_norex(OneByteOpcodeID opcode, RegisterID rm, GroupOpcodeID groupOp) + void oneByteOp8_norex(OneByteOpcodeID opcode, HRegisterID rm, GroupOpcodeID groupOp) { - MOZ_ASSERT(!regRequiresRex(rm)); - m_buffer.ensureSpace(maxInstructionSize); + MOZ_ASSERT(!regRequiresRex(RegisterID(rm))); + m_buffer.ensureSpace(MaxInstructionSize); m_buffer.putByteUnchecked(opcode); - registerModRM(rm, groupOp); + registerModRM(RegisterID(rm), groupOp); } - void oneByteOp8(OneByteOpcodeID opcode, int32_t offset, RegisterID base, int reg) + void oneByteOp8(OneByteOpcodeID opcode, int32_t offset, RegisterID base, RegisterID reg) { -#ifdef JS_CODEGEN_X86 - MOZ_ASSERT(!ByteRegRequiresRex(reg)); -#endif - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(ByteRegRequiresRex(reg), reg, 0, base); + m_buffer.ensureSpace(MaxInstructionSize); + emitRexIf(byteRegRequiresRex(reg), reg, 0, base); m_buffer.putByteUnchecked(opcode); memoryModRM(offset, base, reg); } - void oneByteOp8_disp32(OneByteOpcodeID opcode, int32_t offset, RegisterID base, int reg) + void oneByteOp8_disp32(OneByteOpcodeID opcode, int32_t offset, RegisterID base, + RegisterID reg) { -#ifdef JS_CODEGEN_X86 - MOZ_ASSERT(!ByteRegRequiresRex(reg)); -#endif - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(ByteRegRequiresRex(reg), reg, 0, base); + m_buffer.ensureSpace(MaxInstructionSize); + emitRexIf(byteRegRequiresRex(reg), reg, 0, base); m_buffer.putByteUnchecked(opcode); memoryModRM_disp32(offset, base, reg); } - void oneByteOp8(OneByteOpcodeID opcode, int32_t offset, RegisterID base, RegisterID index, int scale, int reg) + void oneByteOp8(OneByteOpcodeID opcode, int32_t offset, RegisterID base, + RegisterID index, int scale, RegisterID reg) { -#ifdef JS_CODEGEN_X86 - MOZ_ASSERT(!ByteRegRequiresRex(reg)); -#endif - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(ByteRegRequiresRex(reg), reg, index, base); + m_buffer.ensureSpace(MaxInstructionSize); + emitRexIf(byteRegRequiresRex(reg), reg, index, base); m_buffer.putByteUnchecked(opcode); memoryModRM(offset, base, index, scale, reg); } - void oneByteOp8(OneByteOpcodeID opcode, const void* address, int reg) + void oneByteOp8(OneByteOpcodeID opcode, const void* address, RegisterID reg) { -#ifdef JS_CODEGEN_X86 - MOZ_ASSERT(!ByteRegRequiresRex(reg)); -#endif - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(ByteRegRequiresRex(reg), reg, 0, 0); + m_buffer.ensureSpace(MaxInstructionSize); + emitRexIf(byteRegRequiresRex(reg), reg, 0, 0); m_buffer.putByteUnchecked(opcode); memoryModRM_disp32(address, reg); } void twoByteOp8(TwoByteOpcodeID opcode, RegisterID rm, RegisterID reg) { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(ByteRegRequiresRex(reg)|ByteRegRequiresRex(rm), reg, 0, rm); + m_buffer.ensureSpace(MaxInstructionSize); + emitRexIf(byteRegRequiresRex(reg)|byteRegRequiresRex(rm), reg, 0, rm); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(opcode); registerModRM(rm, reg); @@ -5228,8 +4489,8 @@ // prefix to disambiguate it from ah..bh. void twoByteOp8_movx(TwoByteOpcodeID opcode, RegisterID rm, RegisterID reg) { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(regRequiresRex(reg)|ByteRegRequiresRex(rm), reg, 0, rm); + m_buffer.ensureSpace(MaxInstructionSize); + emitRexIf(regRequiresRex(reg)|byteRegRequiresRex(rm), reg, 0, rm); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(opcode); registerModRM(rm, reg); @@ -5237,8 +4498,8 @@ void twoByteOp8(TwoByteOpcodeID opcode, RegisterID rm, GroupOpcodeID groupOp) { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(ByteRegRequiresRex(rm), 0, 0, rm); + m_buffer.ensureSpace(MaxInstructionSize); + emitRexIf(byteRegRequiresRex(rm), 0, 0, rm); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(opcode); registerModRM(rm, groupOp); @@ -5302,7 +4563,8 @@ m_buffer.putInt64Unchecked(imm); } - JmpSrc immediateRel32() + MOZ_WARN_UNUSED_RESULT JmpSrc + immediateRel32() { m_buffer.putIntUnchecked(0); return JmpSrc(m_buffer.size()); @@ -5323,23 +4585,13 @@ void doubleConstant(double d) { m_buffer.ensureSpace(sizeof(double)); - union { - uint64_t u64; - double d; - } u; - u.d = d; - m_buffer.putInt64Unchecked(u.u64); + m_buffer.putInt64Unchecked(mozilla::BitwiseCast(d)); } void floatConstant(float f) { m_buffer.ensureSpace(sizeof(float)); - union { - uint32_t u32; - float f; - } u; - u.f = f; - m_buffer.putIntUnchecked(u.u32); + m_buffer.putIntUnchecked(mozilla::BitwiseCast(f)); } void int32x4Constant(const int32_t s[4]) @@ -5369,78 +4621,82 @@ // Administrative methods: size_t size() const { return m_buffer.size(); } - size_t allocSize() const { return m_buffer.allocSize(); } - unsigned char *buffer() const { return m_buffer.buffer(); } + const unsigned char *buffer() const { return m_buffer.buffer(); } bool oom() const { return m_buffer.oom(); } bool isAligned(int alignment) const { return m_buffer.isAligned(alignment); } - void* data() const { return m_buffer.data(); } - void* executableAllocAndCopy(js::jit::ExecutableAllocator* allocator, - js::jit::ExecutablePool** poolp, js::jit::CodeKind kind) { - return m_buffer.executableAllocAndCopy(allocator, poolp, kind); - } + unsigned char *data() { return m_buffer.data(); } private: // Internals; ModRm and REX formatters. - static const RegisterID noBase = X86Registers::ebp; - static const RegisterID hasSib = X86Registers::esp; - static const RegisterID noIndex = X86Registers::esp; + // Byte operand register spl & above requir a REX prefix, which precludes + // use of the h registers in the same instruction. + static bool byteRegRequiresRex(RegisterID reg) + { #ifdef JS_CODEGEN_X64 - static const RegisterID noBase2 = X86Registers::r13; - static const RegisterID hasSib2 = X86Registers::r12; + return reg >= rsp; +#else + return false; +#endif + } - // Registers r8 & above require a REX prefixe. - inline bool regRequiresRex(int reg) + // For non-byte sizes, registers r8 & above always require a REX prefix. + static bool regRequiresRex(RegisterID reg) { - return (reg >= X86Registers::r8); +#ifdef JS_CODEGEN_X64 + return reg >= r8; +#else + return false; +#endif } +#ifdef JS_CODEGEN_X64 // Format a REX prefix byte. - inline void emitRex(bool w, int r, int x, int b) + void emitRex(bool w, int r, int x, int b) { m_buffer.putByteUnchecked(PRE_REX | ((int)w << 3) | ((r>>3)<<2) | ((x>>3)<<1) | (b>>3)); } // Used to plant a REX byte with REX.w set (for 64-bit operations). - inline void emitRexW(int r, int x, int b) + void emitRexW(int r, int x, int b) { emitRex(true, r, x, b); } - // Used for operations with byte operands - use ByteRegRequiresRex() to + // Used for operations with byte operands - use byteRegRequiresRex() to // check register operands, regRequiresRex() to check other registers // (i.e. address base & index). // // NB: WebKit's use of emitRexIf() is limited such that the // reqRequiresRex() checks are not needed. SpiderMonkey extends // oneByteOp8 functionality such that r, x, and b can all be used. - inline void emitRexIf(bool condition, int r, int x, int b) + void emitRexIf(bool condition, int r, int x, int b) { - if (condition || regRequiresRex(r) || regRequiresRex(x) || regRequiresRex(b)) + if (condition || + regRequiresRex(RegisterID(r)) || + regRequiresRex(RegisterID(x)) || + regRequiresRex(RegisterID(b))) + { emitRex(false, r, x, b); + } } // Used for word sized operations, will plant a REX prefix if necessary // (if any register is r8 or above). - inline void emitRexIfNeeded(int r, int x, int b) + void emitRexIfNeeded(int r, int x, int b) { - emitRexIf(regRequiresRex(r) || regRequiresRex(x) || regRequiresRex(b), r, x, b); + emitRexIf(false, r, x, b); } #else // No REX prefix bytes on 32-bit x86. - inline bool regRequiresRex(int) { return false; } - inline void emitRexIf(bool, int, int, int) {} - inline void emitRexIfNeeded(int, int, int) {} + void emitRexIf(bool condition, int, int, int) + { + MOZ_ASSERT(!condition, "32-bit x86 should never use a REX prefix"); + } + void emitRexIfNeeded(int, int, int) {} #endif - enum ModRmMode { - ModRmMemoryNoDisp, - ModRmMemoryDisp8, - ModRmMemoryDisp32, - ModRmRegister - }; - void putModRm(ModRmMode mode, RegisterID rm, int reg) { m_buffer.putByteUnchecked((mode << 6) | ((reg & 7) << 3) | (rm & 7)); @@ -5556,7 +4812,7 @@ void memoryModRM_disp32(const void* address, int reg) { - int32_t disp = addressImmediate(address); + int32_t disp = AddressImmediate(address); #ifdef JS_CODEGEN_X64 // On x64-64, non-RIP-relative absolute mode requires a SIB. @@ -5576,9 +4832,9 @@ void threeOpVex(VexOperandType p, int r, int x, int b, int m, int w, int v, int l, int opcode) { - m_buffer.ensureSpace(maxInstructionSize); + m_buffer.ensureSpace(MaxInstructionSize); - if (v == X86Registers::invalid_xmm) + if (v == invalid_xmm) v = XMMRegisterID(0); if (x == 0 && b == 0 && m == 1 && w == 0) { @@ -5601,6 +4857,8 @@ bool useVEX_; }; +} // namespace X86Encoding + } // namespace jit } // namespace js diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/CodeGenerator-shared.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/CodeGenerator-shared.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/CodeGenerator-shared.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/CodeGenerator-shared.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -15,6 +15,7 @@ #include "jit/MacroAssembler.h" #include "jit/MIR.h" #include "jit/MIRGenerator.h" +#include "jit/OptimizationTracking.h" #include "js/Conversions.h" #include "vm/TraceLogging.h" @@ -59,6 +60,11 @@ nativeToBytecodeNumRegions_(0), nativeToBytecodeScriptList_(nullptr), nativeToBytecodeScriptListLength_(0), + trackedOptimizationsMap_(nullptr), + trackedOptimizationsMapSize_(0), + trackedOptimizationsRegionTableOffset_(0), + trackedOptimizationsTypesTableOffset_(0), + trackedOptimizationsAttemptsTableOffset_(0), osrEntryOffset_(0), skipArgCheckEntryOffset_(0), #ifdef CHECK_OSIPOINT_REGISTERS @@ -261,6 +267,53 @@ #endif } +bool +CodeGeneratorShared::addTrackedOptimizationsEntry(const TrackedOptimizations *optimizations) +{ + if (!isOptimizationTrackingEnabled()) + return true; + + MOZ_ASSERT(optimizations); + + uint32_t nativeOffset = masm.currentOffset(); + + if (!trackedOptimizations_.empty()) { + NativeToTrackedOptimizations &lastEntry = trackedOptimizations_.back(); + MOZ_ASSERT(nativeOffset >= lastEntry.endOffset.offset()); + + // If we're still generating code for the same set of optimizations, + // we are done. + if (lastEntry.optimizations == optimizations) + return true; + } + + // If we're generating code for a new set of optimizations, add a new + // entry. + NativeToTrackedOptimizations entry; + entry.startOffset = CodeOffsetLabel(nativeOffset); + entry.endOffset = CodeOffsetLabel(nativeOffset); + entry.optimizations = optimizations; + return trackedOptimizations_.append(entry); +} + +void +CodeGeneratorShared::extendTrackedOptimizationsEntry(const TrackedOptimizations *optimizations) +{ + if (!isOptimizationTrackingEnabled()) + return; + + uint32_t nativeOffset = masm.currentOffset(); + NativeToTrackedOptimizations &entry = trackedOptimizations_.back(); + MOZ_ASSERT(entry.optimizations == optimizations); + MOZ_ASSERT(nativeOffset >= entry.endOffset.offset()); + + entry.endOffset = CodeOffsetLabel(nativeOffset); + + // If we generated no code, remove the last entry. + if (nativeOffset == entry.startOffset.offset()) + trackedOptimizations_.popBack(); +} + // see OffsetOfFrameSlot static inline int32_t ToStackIndex(LAllocation *a) @@ -722,6 +775,159 @@ #endif // DEBUG } +bool +CodeGeneratorShared::generateCompactTrackedOptimizationsMap(JSContext *cx, JitCode *code, + types::TypeSet::TypeList *allTypes) +{ + MOZ_ASSERT(trackedOptimizationsMap_ == nullptr); + MOZ_ASSERT(trackedOptimizationsMapSize_ == 0); + MOZ_ASSERT(trackedOptimizationsRegionTableOffset_ == 0); + MOZ_ASSERT(trackedOptimizationsTypesTableOffset_ == 0); + MOZ_ASSERT(trackedOptimizationsAttemptsTableOffset_ == 0); + + if (trackedOptimizations_.empty()) + return true; + + UniqueTrackedOptimizations unique(cx); + if (!unique.init()) + return false; + + // Iterate through all entries, fix up their masm offsets and deduplicate + // their optimization attempts. + for (size_t i = 0; i < trackedOptimizations_.length(); i++) { + NativeToTrackedOptimizations &entry = trackedOptimizations_[i]; + entry.startOffset = CodeOffsetLabel(masm.actualOffset(entry.startOffset.offset())); + entry.endOffset = CodeOffsetLabel(masm.actualOffset(entry.endOffset.offset())); + if (!unique.add(entry.optimizations)) + return false; + } + + // Sort the unique optimization attempts by frequency to stabilize the + // attempts' indices in the compact table we will write later. + if (!unique.sortByFrequency(cx)) + return false; + + // Write out the ranges and the table. + CompactBufferWriter writer; + uint32_t numRegions; + uint32_t regionTableOffset; + uint32_t typesTableOffset; + uint32_t attemptsTableOffset; + if (!WriteIonTrackedOptimizationsTable(cx, writer, + trackedOptimizations_.begin(), + trackedOptimizations_.end(), + unique, &numRegions, + ®ionTableOffset, &typesTableOffset, + &attemptsTableOffset, allTypes)) + { + return false; + } + + MOZ_ASSERT(regionTableOffset > 0); + MOZ_ASSERT(typesTableOffset > 0); + MOZ_ASSERT(attemptsTableOffset > 0); + MOZ_ASSERT(typesTableOffset > regionTableOffset); + MOZ_ASSERT(attemptsTableOffset > typesTableOffset); + + // Copy over the table out of the writer's buffer. + uint8_t *data = cx->runtime()->pod_malloc(writer.length()); + if (!data) + return false; + + memcpy(data, writer.buffer(), writer.length()); + trackedOptimizationsMap_ = data; + trackedOptimizationsMapSize_ = writer.length(); + trackedOptimizationsRegionTableOffset_ = regionTableOffset; + trackedOptimizationsTypesTableOffset_ = typesTableOffset; + trackedOptimizationsAttemptsTableOffset_ = attemptsTableOffset; + + verifyCompactTrackedOptimizationsMap(code, numRegions, unique, allTypes); + + JitSpew(JitSpew_OptimizationTracking, + "== Compact Native To Optimizations Map [%p-%p] size %u", + data, data + trackedOptimizationsMapSize_, trackedOptimizationsMapSize_); + JitSpew(JitSpew_OptimizationTracking, + " with type list of length %u, size %u", + allTypes->length(), allTypes->length() * sizeof(types::Type)); + + return true; +} + +void +CodeGeneratorShared::verifyCompactTrackedOptimizationsMap(JitCode *code, uint32_t numRegions, + const UniqueTrackedOptimizations &unique, + const types::TypeSet::TypeList *allTypes) +{ +#ifdef DEBUG + MOZ_ASSERT(trackedOptimizationsMap_ != nullptr); + MOZ_ASSERT(trackedOptimizationsMapSize_ > 0); + MOZ_ASSERT(trackedOptimizationsRegionTableOffset_ > 0); + MOZ_ASSERT(trackedOptimizationsTypesTableOffset_ > 0); + MOZ_ASSERT(trackedOptimizationsAttemptsTableOffset_ > 0); + + // Table pointers must all be 4-byte aligned. + const uint8_t *regionTableAddr = trackedOptimizationsMap_ + + trackedOptimizationsRegionTableOffset_; + const uint8_t *typesTableAddr = trackedOptimizationsMap_ + + trackedOptimizationsTypesTableOffset_; + const uint8_t *attemptsTableAddr = trackedOptimizationsMap_ + + trackedOptimizationsAttemptsTableOffset_; + MOZ_ASSERT(uintptr_t(regionTableAddr) % sizeof(uint32_t) == 0); + MOZ_ASSERT(uintptr_t(typesTableAddr) % sizeof(uint32_t) == 0); + MOZ_ASSERT(uintptr_t(attemptsTableAddr) % sizeof(uint32_t) == 0); + + // Assert that the number of entries matches up for the tables. + const IonTrackedOptimizationsRegionTable *regionTable = + (const IonTrackedOptimizationsRegionTable *) regionTableAddr; + MOZ_ASSERT(regionTable->numEntries() == numRegions); + const IonTrackedOptimizationsTypesTable *typesTable = + (const IonTrackedOptimizationsTypesTable *) typesTableAddr; + MOZ_ASSERT(typesTable->numEntries() == unique.count()); + const IonTrackedOptimizationsAttemptsTable *attemptsTable = + (const IonTrackedOptimizationsAttemptsTable *) attemptsTableAddr; + MOZ_ASSERT(attemptsTable->numEntries() == unique.count()); + + // Verify each region. + uint32_t trackedIdx = 0; + for (uint32_t regionIdx = 0; regionIdx < regionTable->numEntries(); regionIdx++) { + // Check reverse offsets are within bounds. + MOZ_ASSERT(regionTable->entryOffset(regionIdx) <= trackedOptimizationsRegionTableOffset_); + MOZ_ASSERT_IF(regionIdx > 0, regionTable->entryOffset(regionIdx) < + regionTable->entryOffset(regionIdx - 1)); + + IonTrackedOptimizationsRegion region = regionTable->entry(regionIdx); + + // Check the region range is covered by jitcode. + MOZ_ASSERT(region.startOffset() <= code->instructionsSize()); + MOZ_ASSERT(region.endOffset() <= code->instructionsSize()); + + IonTrackedOptimizationsRegion::RangeIterator iter = region.ranges(); + while (iter.more()) { + // Assert that the offsets are correctly decoded from the delta. + uint32_t startOffset, endOffset; + uint8_t index; + iter.readNext(&startOffset, &endOffset, &index); + NativeToTrackedOptimizations &entry = trackedOptimizations_[trackedIdx++]; + MOZ_ASSERT(startOffset == entry.startOffset.offset()); + MOZ_ASSERT(endOffset == entry.endOffset.offset()); + MOZ_ASSERT(index == unique.indexOf(entry.optimizations)); + + // Assert that the type info and attempts vector are correctly + // decoded. Since this is a DEBUG-only verification, crash on OOM. + IonTrackedOptimizationsTypeInfo typeInfo = typesTable->entry(index); + TempTrackedTypeInfoVector tvec(alloc()); + MOZ_ALWAYS_TRUE(typeInfo.readVector(&tvec, allTypes)); + MOZ_ASSERT(entry.optimizations->matchTypes(tvec)); + + IonTrackedOptimizationsAttempts attempts = attemptsTable->entry(index); + TempAttemptsVector avec(alloc()); + MOZ_ALWAYS_TRUE(attempts.readVector(&avec)); + MOZ_ASSERT(entry.optimizations->matchAttempts(avec)); + } + } +#endif +} + void CodeGeneratorShared::markSafepoint(LInstruction *ins) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/CodeGenerator-shared.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/CodeGenerator-shared.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/CodeGenerator-shared.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/CodeGenerator-shared.h 2015-02-03 14:33:32.000000000 +0000 @@ -25,6 +25,7 @@ class CodeGenerator; class MacroAssembler; class IonCache; +class UniqueTrackedOptimizations; template class OutOfLineCallVM; @@ -114,6 +115,26 @@ return gen->isProfilerInstrumentationEnabled(); } + public: + struct NativeToTrackedOptimizations { + // [startOffset, endOffset) + CodeOffsetLabel startOffset; + CodeOffsetLabel endOffset; + const TrackedOptimizations *optimizations; + }; + + protected: + js::Vector trackedOptimizations_; + uint8_t *trackedOptimizationsMap_; + uint32_t trackedOptimizationsMapSize_; + uint32_t trackedOptimizationsRegionTableOffset_; + uint32_t trackedOptimizationsTypesTableOffset_; + uint32_t trackedOptimizationsAttemptsTableOffset_; + + bool isOptimizationTrackingEnabled() { + return gen->isOptimizationTrackingEnabled(); + } + protected: // The offset of the first instruction of the OSR entry block from the // beginning of the code buffer. @@ -243,6 +264,9 @@ void dumpNativeToBytecodeEntries(); void dumpNativeToBytecodeEntry(uint32_t idx); + bool addTrackedOptimizationsEntry(const TrackedOptimizations *optimizations); + void extendTrackedOptimizationsEntry(const TrackedOptimizations *optimizations); + public: MIRGenerator &mirGen() const { return *gen; @@ -313,6 +337,12 @@ bool generateCompactNativeToBytecodeMap(JSContext *cx, JitCode *code); void verifyCompactNativeToBytecodeMap(JitCode *code); + bool generateCompactTrackedOptimizationsMap(JSContext *cx, JitCode *code, + types::TypeSet::TypeList *allTypes); + void verifyCompactTrackedOptimizationsMap(JitCode *code, uint32_t numRegions, + const UniqueTrackedOptimizations &unique, + const types::TypeSet::TypeList *allTypes); + // Mark the safepoint on |ins| as corresponding to the current assembler location. // The location should be just after a call. void markSafepoint(LInstruction *ins); @@ -515,6 +545,10 @@ emitTracelogScriptStop(); #endif } + + inline void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, bool isLoad, + Scalar::Type type, const Operand &mem, + LAllocation alloc); }; // An out-of-line path is generated at the end of the function. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/CodeGenerator-shared-inl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/CodeGenerator-shared-inl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/CodeGenerator-shared-inl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/CodeGenerator-shared-inl.h 2015-02-03 14:33:32.000000000 +0000 @@ -8,6 +8,7 @@ #define jit_shared_CodeGenerator_shared_inl_h #include "jit/shared/CodeGenerator-shared.h" +#include "jit/Disassembler.h" namespace js { namespace jit { @@ -183,6 +184,57 @@ masm.PopRegsInMask(regs); } +void +CodeGeneratorShared::verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, bool isLoad, + Scalar::Type type, const Operand &mem, + LAllocation alloc) +{ +#ifdef DEBUG + using namespace Disassembler; + + OtherOperand op; + Disassembler::HeapAccess::Kind kind = isLoad ? HeapAccess::Load : HeapAccess::Store; + switch (type) { + case Scalar::Int8: + case Scalar::Int16: + if (kind == HeapAccess::Load) + kind = HeapAccess::LoadSext32; + // FALL THROUGH + case Scalar::Uint8: + case Scalar::Uint16: + case Scalar::Int32: + case Scalar::Uint32: + if (!alloc.isConstant()) { + op = OtherOperand(ToRegister(alloc).code()); + } else { + int32_t i = ToInt32(&alloc); + + // Sign-extend the immediate value out to 32 bits. We do this even + // for unsigned element types so that we match what the disassembly + // code does, as it doesn't know about signedness of stores. + unsigned shift = 32 - TypedArrayElemSize(type) * 8; + i = i << shift >> shift; + + op = OtherOperand(i); + } + break; + case Scalar::Float32: + case Scalar::Float64: + case Scalar::Float32x4: + case Scalar::Int32x4: + op = OtherOperand(ToFloatRegister(alloc).code()); + break; + case Scalar::Uint8Clamped: + case Scalar::MaxTypedArrayViewType: + MOZ_CRASH("Unexpected array type"); + } + + masm.verifyHeapAccessDisassembly(begin, end, + HeapAccess(kind, TypedArrayElemSize(type), + ComplexAddress(mem), op)); +#endif +} + } // ion } // js diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/CodeGenerator-x86-shared.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/CodeGenerator-x86-shared.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/CodeGenerator-x86-shared.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/CodeGenerator-x86-shared.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -1601,7 +1601,7 @@ bailoutFrom(&bailout, lir->snapshot()); // Round toward -Infinity. - masm.vroundsd(X86Assembler::RoundDown, input, scratch, scratch); + masm.vroundsd(X86Encoding::RoundDown, input, scratch, scratch); bailoutCvttsd2si(scratch, output, lir->snapshot()); } else { @@ -1658,7 +1658,7 @@ bailoutFrom(&bailout, lir->snapshot()); // Round toward -Infinity. - masm.vroundss(X86Assembler::RoundDown, input, scratch, scratch); + masm.vroundss(X86Encoding::RoundDown, input, scratch, scratch); bailoutCvttss2si(scratch, output, lir->snapshot()); } else { @@ -1723,7 +1723,7 @@ // x <= -1 or x > -0 masm.bind(&lessThanMinusOne); // Round toward +Infinity. - masm.vroundsd(X86Assembler::RoundUp, input, scratch, scratch); + masm.vroundsd(X86Encoding::RoundUp, input, scratch, scratch); bailoutCvttsd2si(scratch, output, lir->snapshot()); return; } @@ -1775,7 +1775,7 @@ // x <= -1 or x > -0 masm.bind(&lessThanMinusOne); // Round toward +Infinity. - masm.vroundss(X86Assembler::RoundUp, input, scratch, scratch); + masm.vroundss(X86Encoding::RoundUp, input, scratch, scratch); bailoutCvttss2si(scratch, output, lir->snapshot()); return; } @@ -1850,7 +1850,7 @@ // Add 0.5 and round toward -Infinity. The result is stored in the temp // register (currently contains 0.5). masm.addDouble(input, temp); - masm.vroundsd(X86Assembler::RoundDown, temp, scratch, scratch); + masm.vroundsd(X86Encoding::RoundDown, temp, scratch, scratch); // Truncate. bailoutCvttsd2si(scratch, output, lir->snapshot()); @@ -1933,7 +1933,7 @@ // Add 0.5 and round toward -Infinity. The result is stored in the temp // register (currently contains 0.5). masm.addFloat32(input, temp); - masm.vroundss(X86Assembler::RoundDown, temp, scratch, scratch); + masm.vroundss(X86Encoding::RoundDown, temp, scratch, scratch); // Truncate. bailoutCvttss2si(scratch, output, lir->snapshot()); @@ -1978,10 +1978,10 @@ } void -CodeGeneratorX86Shared::visitGuardObjectType(LGuardObjectType *guard) +CodeGeneratorX86Shared::visitGuardObjectGroup(LGuardObjectGroup *guard) { Register obj = ToRegister(guard->input()); - masm.cmpPtr(Operand(obj, JSObject::offsetOfType()), ImmGCPtr(guard->mir()->typeObject())); + masm.cmpPtr(Operand(obj, JSObject::offsetOfGroup()), ImmGCPtr(guard->mir()->group())); Assembler::Condition cond = guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual; @@ -1994,8 +1994,8 @@ Register obj = ToRegister(guard->input()); Register tmp = ToRegister(guard->tempInt()); - masm.loadPtr(Address(obj, JSObject::offsetOfType()), tmp); - masm.cmpPtr(Operand(tmp, types::TypeObject::offsetOfClasp()), ImmPtr(guard->mir()->getClass())); + masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), tmp); + masm.cmpPtr(Operand(tmp, types::ObjectGroup::offsetOfClasp()), ImmPtr(guard->mir()->getClass())); bailoutIf(Assembler::NotEqual, guard->snapshot()); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/CodeGenerator-x86-shared.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/CodeGenerator-x86-shared.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/CodeGenerator-x86-shared.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/CodeGenerator-x86-shared.h 2015-02-03 14:33:32.000000000 +0000 @@ -198,7 +198,7 @@ virtual void visitRound(LRound *lir); virtual void visitRoundF(LRoundF *lir); virtual void visitGuardShape(LGuardShape *guard); - virtual void visitGuardObjectType(LGuardObjectType *guard); + virtual void visitGuardObjectGroup(LGuardObjectGroup *guard); virtual void visitGuardClass(LGuardClass *guard); virtual void visitEffectiveAddress(LEffectiveAddress *ins); virtual void visitUDivOrMod(LUDivOrMod *ins); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Constants-x86-shared.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Constants-x86-shared.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Constants-x86-shared.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Constants-x86-shared.h 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,222 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 jit_shared_Constants_x86_shared_h +#define jit_shared_Constants_x86_shared_h + +namespace js { +namespace jit { + +namespace X86Encoding { + +enum RegisterID { + rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi +#ifdef JS_CODEGEN_X64 + ,r8, r9, r10, r11, r12, r13, r14, r15 +#endif + ,invalid_reg +}; + +enum HRegisterID { + ah = rsp, + ch = rbp, + dh = rsi, + bh = rdi +}; + +enum XMMRegisterID { + xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 +#ifdef JS_CODEGEN_X64 + ,xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15 +#endif + ,invalid_xmm +}; + +inline const char *XMMRegName(XMMRegisterID reg) +{ + static const char *const names[] = { + "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7" +#ifdef JS_CODEGEN_X64 + ,"%xmm8", "%xmm9", "%xmm10", "%xmm11", "%xmm12", "%xmm13", "%xmm14", "%xmm15" +#endif + }; + MOZ_ASSERT(size_t(reg) < mozilla::ArrayLength(names)); + return names[reg]; +} + +#ifdef JS_CODEGEN_X64 +inline const char *GPReg64Name(RegisterID reg) +{ + static const char *const names[] = { + "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi" +#ifdef JS_CODEGEN_X64 + ,"%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15" +#endif + }; + MOZ_ASSERT(size_t(reg) < mozilla::ArrayLength(names)); + return names[reg]; +} +#endif + +inline const char *GPReg32Name(RegisterID reg) +{ + static const char *const names[] = { + "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi" +#ifdef JS_CODEGEN_X64 + ,"%r8d", "%r9d", "%r10d", "%r11d", "%r12d", "%r13d", "%r14d", "%r15d" +#endif + }; + MOZ_ASSERT(size_t(reg) < mozilla::ArrayLength(names)); + return names[reg]; +} + +inline const char *GPReg16Name(RegisterID reg) +{ + static const char *const names[] = { + "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di" +#ifdef JS_CODEGEN_X64 + ,"%r8w", "%r9w", "%r10w", "%r11w", "%r12w", "%r13w", "%r14w", "%r15w" +#endif + }; + MOZ_ASSERT(size_t(reg) < mozilla::ArrayLength(names)); + return names[reg]; +} + +inline const char *GPReg8Name(RegisterID reg) +{ + static const char *const names[] = { + "%al", "%cl", "%dl", "%bl" +#ifdef JS_CODEGEN_X64 + ,"%spl", "%bpl", "%sil", "%dil", + "%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b" +#endif + }; + MOZ_ASSERT(size_t(reg) < mozilla::ArrayLength(names)); + return names[reg]; +} + +inline const char *GPRegName(RegisterID reg) +{ +#ifdef JS_CODEGEN_X64 + return GPReg64Name(reg); +#else + return GPReg32Name(reg); +#endif +} + +inline bool HasSubregL(RegisterID reg) +{ +#ifdef JS_CODEGEN_X64 + // In 64-bit mode, all registers have an 8-bit lo subreg. + return true; +#else + // In 32-bit mode, only the first four registers do. + return reg <= rbx; +#endif +} + +inline bool HasSubregH(RegisterID reg) +{ + // The first four registers always have h registers. However, note that + // on x64, h registers may not be used in instructions using REX + // prefixes. Also note that this may depend on what other registers are + // used! + return reg <= rbx; +} + +inline HRegisterID GetSubregH(RegisterID reg) +{ + MOZ_ASSERT(HasSubregH(reg)); + return HRegisterID(reg + 4); +} + +inline const char *HRegName8(HRegisterID reg) +{ + static const char *const names[] = { + "%ah", "%ch", "%dh", "%bh" + }; + size_t index = reg - GetSubregH(rax); + MOZ_ASSERT(index < mozilla::ArrayLength(names)); + return names[index]; +} + +enum Condition { + ConditionO, + ConditionNO, + ConditionB, + ConditionAE, + ConditionE, + ConditionNE, + ConditionBE, + ConditionA, + ConditionS, + ConditionNS, + ConditionP, + ConditionNP, + ConditionL, + ConditionGE, + ConditionLE, + ConditionG, + + ConditionC = ConditionB, + ConditionNC = ConditionAE +}; + +inline const char *CCName(Condition cc) +{ + static const char *const names[] = { + "o ", "no", "b ", "ae", "e ", "ne", "be", "a ", + "s ", "ns", "p ", "np", "l ", "ge", "le", "g " + }; + MOZ_ASSERT(size_t(cc) < mozilla::ArrayLength(names)); + return names[cc]; +} + +// Conditions for CMP instructions (CMPSS, CMPSD, CMPPS, CMPPD, etc). +enum ConditionCmp { + ConditionCmp_EQ = 0x0, + ConditionCmp_LT = 0x1, + ConditionCmp_LE = 0x2, + ConditionCmp_UNORD = 0x3, + ConditionCmp_NEQ = 0x4, + ConditionCmp_NLT = 0x5, + ConditionCmp_NLE = 0x6, + ConditionCmp_ORD = 0x7, +}; + +// Rounding modes for ROUNDSD. +enum RoundingMode { + RoundToNearest = 0x0, + RoundDown = 0x1, + RoundUp = 0x2, + RoundToZero = 0x3 +}; + +// Test whether the given address will fit in an address immediate field. +// This is always true on x86, but on x64 it's only true for addreses which +// fit in the 32-bit immediate field. +inline bool IsAddressImmediate(const void *address) +{ + intptr_t value = reinterpret_cast(address); + int32_t immediate = static_cast(value); + return value == immediate; +} + +// Convert the given address to a 32-bit immediate field value. This is a +// no-op on x86, but on x64 it asserts that the address is actually a valid +// address immediate. +inline int32_t AddressImmediate(const void *address) +{ + MOZ_ASSERT(IsAddressImmediate(address)); + return static_cast(reinterpret_cast(address)); +} + +} // namespace X86Encoding + +} // namespace jit +} // namespace js + +#endif /* jit_shared_Constants_x86_shared_h */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Disassembler-x86-shared.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Disassembler-x86-shared.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Disassembler-x86-shared.cpp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Disassembler-x86-shared.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,525 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 "jit/Disassembler.h" + +#include "jit/shared/Encoding-x86-shared.h" + +using namespace js; +using namespace js::jit; +using namespace js::jit::X86Encoding; +using namespace js::jit::Disassembler; + +static bool REX_W(uint8_t rex) { return (rex >> 3) & 0x1; } +static bool REX_R(uint8_t rex) { return (rex >> 2) & 0x1; } +static bool REX_X(uint8_t rex) { return (rex >> 1) & 0x1; } +static bool REX_B(uint8_t rex) { return (rex >> 0) & 0x1; } + +static uint8_t +MakeREXFlags(bool w, bool r, bool x, bool b) +{ + uint8_t rex = (w << 3) | (r << 2) | (x << 1) | (b << 0); + MOZ_ASSERT(REX_W(rex) == w); + MOZ_ASSERT(REX_R(rex) == r); + MOZ_ASSERT(REX_X(rex) == x); + MOZ_ASSERT(REX_B(rex) == b); + return rex; +} + +static ModRmMode +ModRM_Mode(uint8_t modrm) +{ + return ModRmMode((modrm >> 6) & 0x3); +} + +static uint8_t +ModRM_Reg(uint8_t modrm) +{ + return (modrm >> 3) & 0x7; +} + +static uint8_t +ModRM_RM(uint8_t modrm) +{ + return (modrm >> 0) & 0x7; +} + +static bool +ModRM_hasSIB(uint8_t modrm) +{ + return ModRM_Mode(modrm) != ModRmRegister && ModRM_RM(modrm) == hasSib; +} +static bool +ModRM_hasDisp8(uint8_t modrm) +{ + return ModRM_Mode(modrm) == ModRmMemoryDisp8; +} +static bool +ModRM_hasRIP(uint8_t modrm) +{ +#ifdef JS_CODEGEN_X64 + return ModRM_Mode(modrm) == ModRmMemoryNoDisp && ModRM_RM(modrm) == noBase; +#else + return false; +#endif +} +static bool +ModRM_hasDisp32(uint8_t modrm) +{ + return ModRM_Mode(modrm) == ModRmMemoryDisp32 || + ModRM_hasRIP(modrm); +} + +static uint8_t +SIB_SS(uint8_t sib) +{ + return (sib >> 6) & 0x3; +} + +static uint8_t +SIB_Index(uint8_t sib) +{ + return (sib >> 3) & 0x7; +} + +static uint8_t +SIB_Base(uint8_t sib) +{ + return (sib >> 0) & 0x7; +} + +static bool +SIB_hasRIP(uint8_t sib) +{ + return SIB_Base(sib) == noBase && SIB_Index(sib) == noIndex; +} + +static bool +HasRIP(uint8_t modrm, uint8_t sib, uint8_t rex) +{ + return ModRM_hasRIP(modrm) && SIB_hasRIP(sib); +} + +static bool +HasDisp8(uint8_t modrm) +{ + return ModRM_hasDisp8(modrm); +} + +static bool +HasDisp32(uint8_t modrm, uint8_t sib) +{ + return ModRM_hasDisp32(modrm) || + (SIB_Base(sib) == noBase && + SIB_Index(sib) == noIndex && + ModRM_Mode(modrm) == ModRmMemoryNoDisp); +} + +static uint32_t +Reg(uint8_t modrm, uint8_t sib, uint8_t rex) +{ + return ModRM_Reg(modrm) | (REX_R(rex) << 3); +} + +static bool +HasBase(uint8_t modrm, uint8_t sib) +{ + return !ModRM_hasSIB(modrm) || + SIB_Base(sib) != noBase || + SIB_Index(sib) != noIndex || + ModRM_Mode(modrm) != ModRmMemoryNoDisp; +} + +static RegisterID +DecodeBase(uint8_t modrm, uint8_t sib, uint8_t rex) +{ + return HasBase(modrm, sib) + ? RegisterID((ModRM_hasSIB(modrm) ? SIB_Base(sib) : ModRM_RM(modrm)) | (REX_B(rex) << 3)) + : invalid_reg; +} + +static RegisterID +DecodeIndex(uint8_t modrm, uint8_t sib, uint8_t rex) +{ + RegisterID index = RegisterID(SIB_Index(sib) | (REX_X(rex) << 3)); + return ModRM_hasSIB(modrm) && index != noIndex ? index : invalid_reg; +} + +static uint32_t +DecodeScale(uint8_t modrm, uint8_t sib, uint8_t rex) +{ + return ModRM_hasSIB(modrm) ? SIB_SS(sib) : 0; +} + +#define PackOpcode(op0, op1, op2) ((op0) | ((op1) << 8) | ((op2) << 16)) +#define Pack2ByteOpcode(op1) PackOpcode(OP_2BYTE_ESCAPE, op1, 0) +#define Pack3ByteOpcode(op1, op2) PackOpcode(OP_2BYTE_ESCAPE, op1, op2) + +uint8_t * +js::jit::Disassembler::DisassembleHeapAccess(uint8_t *ptr, HeapAccess *access) +{ + VexOperandType type = VEX_PS; + uint32_t opcode = OP_HLT; + uint8_t modrm = 0; + uint8_t sib = 0; + uint8_t rex = 0; + int32_t disp = 0; + int32_t imm = 0; + bool haveImm = false; + int opsize = 4; + + // Legacy prefixes + switch (*ptr) { + case PRE_LOCK: + case PRE_PREDICT_BRANCH_NOT_TAKEN: // (obsolete), aka %cs + case 0x3E: // aka predict-branch-taken (obsolete) + case 0x36: // %ss + case 0x26: // %es + case 0x64: // %fs + case 0x65: // %gs + case 0x67: // address-size override + MOZ_CRASH("Unable to disassemble instruction"); + case PRE_SSE_F2: // aka REPNZ/REPNE + type = VEX_SD; + ptr++; + break; + case PRE_SSE_F3: // aka REP/REPE/REPZ + type = VEX_SS; + ptr++; + break; + case PRE_SSE_66: // aka PRE_OPERAND_SIZE + type = VEX_PD; + opsize = 2; + ptr++; + break; + default: + break; + } + + // REX and VEX prefixes + { + int x = 0, b = 0, m = 1, w = 0; + int r, l, p; + switch (*ptr) { +#ifdef JS_CODEGEN_X64 + case PRE_REX | 0x0: case PRE_REX | 0x1: case PRE_REX | 0x2: case PRE_REX | 0x3: + case PRE_REX | 0x4: case PRE_REX | 0x5: case PRE_REX | 0x6: case PRE_REX | 0x7: + case PRE_REX | 0x8: case PRE_REX | 0x9: case PRE_REX | 0xa: case PRE_REX | 0xb: + case PRE_REX | 0xc: case PRE_REX | 0xd: case PRE_REX | 0xe: case PRE_REX | 0xf: + rex = *ptr++ & 0xf; + goto rex_done; +#endif + case PRE_VEX_C4: { + if (type != VEX_PS) + MOZ_CRASH("Unable to disassemble instruction"); + ++ptr; + uint8_t c4a = *ptr++ ^ 0xe0; + uint8_t c4b = *ptr++ ^ 0x78; + r = (c4a >> 7) & 0x1; + x = (c4a >> 6) & 0x1; + b = (c4a >> 5) & 0x1; + m = (c4a >> 0) & 0x1f; + w = (c4b >> 7) & 0x1; + l = (c4b >> 2) & 0x1; + p = (c4b >> 0) & 0x3; + break; + } + case PRE_VEX_C5: { + if (type != VEX_PS) + MOZ_CRASH("Unable to disassemble instruction"); + ++ptr; + uint8_t c5 = *ptr++ ^ 0xf8; + r = (c5 >> 7) & 0x1; + l = (c5 >> 2) & 0x1; + p = (c5 >> 0) & 0x3; + break; + } + default: + goto rex_done; + } + type = VexOperandType(p); + rex = MakeREXFlags(w, r, x, b); + switch (m) { + case 0x1: + opcode = Pack2ByteOpcode(*ptr++); + goto opcode_done; + case 0x2: + opcode = Pack3ByteOpcode(ESCAPE_38, *ptr++); + goto opcode_done; + case 0x3: + opcode = Pack3ByteOpcode(ESCAPE_3A, *ptr++); + goto opcode_done; + default: + MOZ_CRASH("Unable to disassemble instruction"); + } + if (l != 0) // 256-bit SIMD + MOZ_CRASH("Unable to disassemble instruction"); + } + rex_done:; + if (REX_W(rex)) + opsize = 8; + + // Opcode. + opcode = *ptr++; + switch (opcode) { +#ifdef JS_CODEGEN_X64 + case OP_PUSH_EAX + 0: case OP_PUSH_EAX + 1: case OP_PUSH_EAX + 2: case OP_PUSH_EAX + 3: + case OP_PUSH_EAX + 4: case OP_PUSH_EAX + 5: case OP_PUSH_EAX + 6: case OP_PUSH_EAX + 7: + case OP_POP_EAX + 0: case OP_POP_EAX + 1: case OP_POP_EAX + 2: case OP_POP_EAX + 3: + case OP_POP_EAX + 4: case OP_POP_EAX + 5: case OP_POP_EAX + 6: case OP_POP_EAX + 7: + case OP_PUSH_Iz: + case OP_PUSH_Ib: + opsize = 8; + break; +#endif + case OP_2BYTE_ESCAPE: + opcode |= *ptr << 8; + switch (*ptr++) { + case ESCAPE_38: + case ESCAPE_3A: + opcode |= *ptr++ << 16; + break; + default: + break; + } + break; + default: + break; + } + opcode_done:; + + // ModR/M + modrm = *ptr++; + + // SIB + if (ModRM_hasSIB(modrm)) + sib = *ptr++; + + // Address Displacement + if (HasDisp8(modrm)) { + disp = int8_t(*ptr++); + } else if (HasDisp32(modrm, sib)) { + memcpy(&disp, ptr, sizeof(int32_t)); + ptr += sizeof(int32_t); + } + + // Immediate operand + switch (opcode) { + case OP_PUSH_Ib: + case OP_IMUL_GvEvIb: + case OP_GROUP1_EbIb: + case OP_GROUP1_EvIb: + case OP_TEST_EAXIb: + case OP_GROUP2_EvIb: + case OP_GROUP11_EvIb: + case OP_GROUP3_EbIb: + case Pack2ByteOpcode(OP2_PSHUFD_VdqWdqIb): + case Pack2ByteOpcode(OP2_PSLLD_UdqIb): // aka OP2_PSRAD_UdqIb, aka OP2_PSRLD_UdqIb + case Pack2ByteOpcode(OP2_PEXTRW_GdUdIb): + case Pack2ByteOpcode(OP2_SHUFPS_VpsWpsIb): + case Pack3ByteOpcode(ESCAPE_3A, OP3_PEXTRD_EdVdqIb): + case Pack3ByteOpcode(ESCAPE_3A, OP3_BLENDPS_VpsWpsIb): + case Pack3ByteOpcode(ESCAPE_3A, OP3_PINSRD_VdqEdIb): + // 8-bit signed immediate + imm = int8_t(*ptr++); + haveImm = true; + break; + case OP_RET_Iz: + // 16-bit unsigned immediate + memcpy(&imm, ptr, sizeof(int16_t)); + ptr += sizeof(int16_t); + haveImm = true; + break; + case OP_ADD_EAXIv: + case OP_OR_EAXIv: + case OP_AND_EAXIv: + case OP_SUB_EAXIv: + case OP_XOR_EAXIv: + case OP_CMP_EAXIv: + case OP_PUSH_Iz: + case OP_IMUL_GvEvIz: + case OP_GROUP1_EvIz: + case OP_TEST_EAXIv: + case OP_MOV_EAXIv: + case OP_GROUP3_EvIz: + // 32-bit signed immediate + memcpy(&imm, ptr, sizeof(int32_t)); + ptr += sizeof(int32_t); + haveImm = true; + break; + case OP_GROUP11_EvIz: + // opsize-sized signed immediate + memcpy(&imm, ptr, opsize); + imm = (imm << (32 - opsize * 8)) >> (32 - opsize * 8); + ptr += opsize; + haveImm = true; + break; + default: + break; + } + + // Interpret the opcode. + if (HasRIP(modrm, sib, rex)) + MOZ_CRASH("Unable to disassemble instruction"); + size_t memSize = 0; + OtherOperand otherOperand(imm); + HeapAccess::Kind kind = HeapAccess::Unknown; + RegisterID gpr(RegisterID(Reg(modrm, sib, rex))); + XMMRegisterID xmm(XMMRegisterID(Reg(modrm, sib, rex))); + ComplexAddress addr(disp, + DecodeBase(modrm, sib, rex), + DecodeIndex(modrm, sib, rex), + DecodeScale(modrm, sib, rex)); + switch (opcode) { + case OP_GROUP11_EvIb: + if (gpr != RegisterID(GROUP11_MOV)) + MOZ_CRASH("Unable to disassemble instruction"); + MOZ_ASSERT(haveImm); + memSize = 1; + kind = HeapAccess::Store; + break; + case OP_GROUP11_EvIz: + if (gpr != RegisterID(GROUP11_MOV)) + MOZ_CRASH("Unable to disassemble instruction"); + MOZ_ASSERT(haveImm); + memSize = opsize; + kind = HeapAccess::Store; + break; + case OP_MOV_GvEv: + MOZ_ASSERT(!haveImm); + otherOperand = OtherOperand(gpr); + memSize = opsize; + kind = HeapAccess::Load; + break; + case OP_MOV_GvEb: + MOZ_ASSERT(!haveImm); + otherOperand = OtherOperand(gpr); + memSize = 1; + kind = HeapAccess::Load; + break; + case OP_MOV_EvGv: + if (!haveImm) + otherOperand = OtherOperand(gpr); + memSize = opsize; + kind = HeapAccess::Store; + break; + case OP_MOV_EbGv: + if (!haveImm) + otherOperand = OtherOperand(gpr); + memSize = 1; + kind = HeapAccess::Store; + break; + case Pack2ByteOpcode(OP2_MOVZX_GvEb): + MOZ_ASSERT(!haveImm); + otherOperand = OtherOperand(gpr); + memSize = 1; + kind = HeapAccess::Load; + break; + case Pack2ByteOpcode(OP2_MOVZX_GvEw): + MOZ_ASSERT(!haveImm); + otherOperand = OtherOperand(gpr); + memSize = 2; + kind = HeapAccess::Load; + break; + case Pack2ByteOpcode(OP2_MOVSX_GvEb): + MOZ_ASSERT(!haveImm); + otherOperand = OtherOperand(gpr); + memSize = 1; + kind = HeapAccess::LoadSext32; + break; + case Pack2ByteOpcode(OP2_MOVSX_GvEw): + MOZ_ASSERT(!haveImm); + otherOperand = OtherOperand(gpr); + memSize = 2; + kind = HeapAccess::LoadSext32; + break; + case Pack2ByteOpcode(OP2_MOVDQ_VdqWdq): // aka OP2_MOVDQ_VsdWsd + case Pack2ByteOpcode(OP2_MOVAPS_VsdWsd): + MOZ_ASSERT(!haveImm); + otherOperand = OtherOperand(xmm); + memSize = 16; + kind = HeapAccess::Load; + break; + case Pack2ByteOpcode(OP2_MOVSD_VsdWsd): // aka OP2_MOVPS_VpsWps + MOZ_ASSERT(!haveImm); + otherOperand = OtherOperand(xmm); + switch (type) { + case VEX_SS: memSize = 4; break; + case VEX_SD: memSize = 8; break; + case VEX_PS: + case VEX_PD: memSize = 16; break; + default: MOZ_CRASH("Unexpected VEX type"); + } + kind = HeapAccess::Load; + break; + case Pack2ByteOpcode(OP2_MOVDQ_WdqVdq): + MOZ_ASSERT(!haveImm); + otherOperand = OtherOperand(xmm); + memSize = 16; + kind = HeapAccess::Store; + break; + case Pack2ByteOpcode(OP2_MOVSD_WsdVsd): // aka OP2_MOVPS_WpsVps + MOZ_ASSERT(!haveImm); + otherOperand = OtherOperand(xmm); + switch (type) { + case VEX_SS: memSize = 4; break; + case VEX_SD: memSize = 8; break; + case VEX_PS: + case VEX_PD: memSize = 16; break; + default: MOZ_CRASH("Unexpected VEX type"); + } + kind = HeapAccess::Store; + break; + default: + MOZ_CRASH("Unable to disassemble instruction"); + } + + *access = HeapAccess(kind, memSize, addr, otherOperand); + return ptr; +} + +#ifdef DEBUG +void +js::jit::Disassembler::DumpHeapAccess(const HeapAccess &access) +{ + switch (access.kind()) { + case HeapAccess::Store: fprintf(stderr, "store"); break; + case HeapAccess::Load: fprintf(stderr, "load"); break; + case HeapAccess::LoadSext32: fprintf(stderr, "loadSext32"); break; + default: fprintf(stderr, "unknown"); break; + } + fprintf(stderr, "%u ", unsigned(access.size())); + + switch (access.otherOperand().kind()) { + case OtherOperand::Imm: fprintf(stderr, "imm %d", access.otherOperand().imm()); break; + case OtherOperand::GPR: fprintf(stderr, "gpr %s", X86Encoding::GPRegName(access.otherOperand().gpr())); break; + case OtherOperand::FPR: fprintf(stderr, "fpr %s", X86Encoding::XMMRegName(access.otherOperand().fpr())); break; + default: fprintf(stderr, "unknown"); + } + + fprintf(stderr, " @ "); + + if (access.address().isPCRelative()) { + fprintf(stderr, MEM_o32r " ", ADDR_o32r(access.address().disp())); + } else if (access.address().index() != X86Encoding::invalid_reg) { + if (access.address().base() != X86Encoding::invalid_reg) { + fprintf(stderr, MEM_obs " ", + ADDR_obs(access.address().disp(), access.address().base(), + access.address().index(), access.address().scale())); + } else { + fprintf(stderr, MEM_os " ", + ADDR_os(access.address().disp(), + access.address().index(), access.address().scale())); + } + } else if (access.address().base() != X86Encoding::invalid_reg) { + fprintf(stderr, MEM_ob " ", ADDR_ob(access.address().disp(), access.address().base())); + } else { + fprintf(stderr, MEM_o " ", ADDR_o(access.address().disp())); + } + + fprintf(stderr, "\n"); +} +#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Encoding-x86-shared.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Encoding-x86-shared.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Encoding-x86-shared.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Encoding-x86-shared.h 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,317 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 jit_shared_Encoding_x86_shared_h +#define jit_shared_Encoding_x86_shared_h + +#include "jit/shared/Constants-x86-shared.h" + +namespace js { +namespace jit { + +namespace X86Encoding { + +static const size_t MaxInstructionSize = 16; + +enum OneByteOpcodeID { + OP_ADD_EbGb = 0x00, + OP_ADD_EvGv = 0x01, + OP_ADD_GvEv = 0x03, + OP_ADD_EAXIv = 0x05, + OP_OR_EbGb = 0x08, + OP_OR_EvGv = 0x09, + OP_OR_GvEv = 0x0B, + OP_OR_EAXIv = 0x0D, + OP_2BYTE_ESCAPE = 0x0F, + OP_AND_EbGb = 0x20, + OP_AND_EvGv = 0x21, + OP_AND_GvEv = 0x23, + OP_AND_EAXIv = 0x25, + OP_SUB_EbGb = 0x28, + OP_SUB_EvGv = 0x29, + OP_SUB_GvEv = 0x2B, + OP_SUB_EAXIv = 0x2D, + PRE_PREDICT_BRANCH_NOT_TAKEN = 0x2E, + OP_XOR_EbGb = 0x30, + OP_XOR_EvGv = 0x31, + OP_XOR_GvEv = 0x33, + OP_XOR_EAXIv = 0x35, + OP_CMP_EvGv = 0x39, + OP_CMP_GvEv = 0x3B, + OP_CMP_EAXIv = 0x3D, +#ifdef JS_CODEGEN_X64 + PRE_REX = 0x40, +#endif + OP_PUSH_EAX = 0x50, + OP_POP_EAX = 0x58, +#ifdef JS_CODEGEN_X86 + OP_PUSHA = 0x60, + OP_POPA = 0x61, +#endif +#ifdef JS_CODEGEN_X64 + OP_MOVSXD_GvEv = 0x63, +#endif + PRE_OPERAND_SIZE = 0x66, + PRE_SSE_66 = 0x66, + OP_PUSH_Iz = 0x68, + OP_IMUL_GvEvIz = 0x69, + OP_PUSH_Ib = 0x6a, + OP_IMUL_GvEvIb = 0x6b, + OP_JCC_rel8 = 0x70, + OP_GROUP1_EbIb = 0x80, + OP_GROUP1_EvIz = 0x81, + OP_GROUP1_EvIb = 0x83, + OP_TEST_EbGb = 0x84, + OP_TEST_EvGv = 0x85, + OP_XCHG_GvEv = 0x87, + OP_MOV_EbGv = 0x88, + OP_MOV_EvGv = 0x89, + OP_MOV_GvEb = 0x8A, + OP_MOV_GvEv = 0x8B, + OP_LEA = 0x8D, + OP_GROUP1A_Ev = 0x8F, + OP_NOP = 0x90, + OP_PUSHFLAGS = 0x9C, + OP_POPFLAGS = 0x9D, + OP_CDQ = 0x99, + OP_MOV_EAXOv = 0xA1, + OP_MOV_OvEAX = 0xA3, + OP_TEST_EAXIb = 0xA8, + OP_TEST_EAXIv = 0xA9, + OP_MOV_EAXIv = 0xB8, + OP_GROUP2_EvIb = 0xC1, + OP_RET_Iz = 0xC2, + PRE_VEX_C4 = 0xC4, + PRE_VEX_C5 = 0xC5, + OP_RET = 0xC3, + OP_GROUP11_EvIb = 0xC6, + OP_GROUP11_EvIz = 0xC7, + OP_INT3 = 0xCC, + OP_GROUP2_Ev1 = 0xD1, + OP_GROUP2_EvCL = 0xD3, + OP_FPU6 = 0xDD, + OP_FPU6_F32 = 0xD9, + OP_CALL_rel32 = 0xE8, + OP_JMP_rel32 = 0xE9, + OP_JMP_rel8 = 0xEB, + PRE_LOCK = 0xF0, + PRE_SSE_F2 = 0xF2, + PRE_SSE_F3 = 0xF3, + OP_HLT = 0xF4, + OP_GROUP3_EbIb = 0xF6, + OP_GROUP3_Ev = 0xF7, + OP_GROUP3_EvIz = 0xF7, // OP_GROUP3_Ev has an immediate, when instruction is a test. + OP_GROUP5_Ev = 0xFF +}; + +enum class ShiftID { + vpsrld = 2, + vpsrlq = 2, + vpsrldq = 3, + vpsrad = 4, + vpslld = 6, + vpsllq = 6 +}; + +enum TwoByteOpcodeID { + OP2_UD2 = 0x0B, + OP2_MOVSD_VsdWsd = 0x10, + OP2_MOVPS_VpsWps = 0x10, + OP2_MOVSD_WsdVsd = 0x11, + OP2_MOVPS_WpsVps = 0x11, + OP2_MOVHLPS_VqUq = 0x12, + OP2_MOVSLDUP_VpsWps = 0x12, + OP2_UNPCKLPS_VsdWsd = 0x14, + OP2_UNPCKHPS_VsdWsd = 0x15, + OP2_MOVLHPS_VqUq = 0x16, + OP2_MOVSHDUP_VpsWps = 0x16, + OP2_MOVAPD_VsdWsd = 0x28, + OP2_MOVAPS_VsdWsd = 0x28, + OP2_MOVAPS_WsdVsd = 0x29, + OP2_CVTSI2SD_VsdEd = 0x2A, + OP2_CVTTSD2SI_GdWsd = 0x2C, + OP2_UCOMISD_VsdWsd = 0x2E, + OP2_MOVMSKPD_EdVd = 0x50, + OP2_ANDPS_VpsWps = 0x54, + OP2_ANDNPS_VpsWps = 0x55, + OP2_ORPS_VpsWps = 0x56, + OP2_XORPS_VpsWps = 0x57, + OP2_ADDSD_VsdWsd = 0x58, + OP2_ADDPS_VpsWps = 0x58, + OP2_MULSD_VsdWsd = 0x59, + OP2_MULPS_VpsWps = 0x59, + OP2_CVTSS2SD_VsdEd = 0x5A, + OP2_CVTSD2SS_VsdEd = 0x5A, + OP2_CVTTPS2DQ_VdqWps = 0x5B, + OP2_CVTDQ2PS_VpsWdq = 0x5B, + OP2_SUBSD_VsdWsd = 0x5C, + OP2_SUBPS_VpsWps = 0x5C, + OP2_MINSD_VsdWsd = 0x5D, + OP2_MINSS_VssWss = 0x5D, + OP2_MINPS_VpsWps = 0x5D, + OP2_DIVSD_VsdWsd = 0x5E, + OP2_DIVPS_VpsWps = 0x5E, + OP2_MAXSD_VsdWsd = 0x5F, + OP2_MAXSS_VssWss = 0x5F, + OP2_MAXPS_VpsWps = 0x5F, + OP2_SQRTSD_VsdWsd = 0x51, + OP2_SQRTSS_VssWss = 0x51, + OP2_SQRTPS_VpsWps = 0x51, + OP2_RSQRTPS_VpsWps = 0x52, + OP2_RCPPS_VpsWps = 0x53, + OP2_ANDPD_VpdWpd = 0x54, + OP2_ORPD_VpdWpd = 0x56, + OP2_XORPD_VpdWpd = 0x57, + OP2_PCMPGTD_VdqWdq = 0x66, + OP2_MOVD_VdEd = 0x6E, + OP2_MOVDQ_VsdWsd = 0x6F, + OP2_MOVDQ_VdqWdq = 0x6F, + OP2_PSHUFD_VdqWdqIb = 0x70, + OP2_PSLLD_UdqIb = 0x72, + OP2_PSRAD_UdqIb = 0x72, + OP2_PSRLD_UdqIb = 0x72, + OP2_PSRLDQ_Vd = 0x73, + OP2_PCMPEQW = 0x75, + OP2_PCMPEQD_VdqWdq = 0x76, + OP2_MOVD_EdVd = 0x7E, + OP2_MOVDQ_WdqVdq = 0x7F, + OP2_JCC_rel32 = 0x80, + OP_SETCC = 0x90, + OP_FENCE = 0xAE, + OP2_IMUL_GvEv = 0xAF, + OP2_CMPXCHG_GvEb = 0xB0, + OP2_CMPXCHG_GvEw = 0xB1, + OP2_BSR_GvEv = 0xBD, + OP2_MOVSX_GvEb = 0xBE, + OP2_MOVSX_GvEw = 0xBF, + OP2_MOVZX_GvEb = 0xB6, + OP2_MOVZX_GvEw = 0xB7, + OP2_XADD_EbGb = 0xC0, + OP2_XADD_EvGv = 0xC1, + OP2_CMPPS_VpsWps = 0xC2, + OP2_PEXTRW_GdUdIb = 0xC5, + OP2_SHUFPS_VpsWpsIb = 0xC6, + OP2_PSRLD_VdqWdq = 0xD2, + OP2_PANDDQ_VdqWdq = 0xDB, + OP2_PANDNDQ_VdqWdq = 0xDF, + OP2_PSRAD_VdqWdq = 0xE2, + OP2_PORDQ_VdqWdq = 0xEB, + OP2_PXORDQ_VdqWdq = 0xEF, + OP2_PSLLD_VdqWdq = 0xF2, + OP2_PMULUDQ_VdqWdq = 0xF4, + OP2_PSUBD_VdqWdq = 0xFA, + OP2_PADDD_VdqWdq = 0xFE +}; + +enum ThreeByteOpcodeID { + OP3_ROUNDSS_VsdWsd = 0x0A, + OP3_ROUNDSD_VsdWsd = 0x0B, + OP3_BLENDVPS_VdqWdq = 0x14, + OP3_PEXTRD_EdVdqIb = 0x16, + OP3_BLENDPS_VpsWpsIb = 0x0C, + OP3_PTEST_VdVd = 0x17, + OP3_INSERTPS_VpsUps = 0x21, + OP3_PINSRD_VdqEdIb = 0x22, + OP3_PMULLD_VdqWdq = 0x40, + OP3_VBLENDVPS_VdqWdq = 0x4A +}; + +// Test whether the given opcode should be printed with its operands reversed. +inline bool IsXMMReversedOperands(TwoByteOpcodeID opcode) +{ + switch (opcode) { + case OP2_MOVSD_WsdVsd: // also OP2_MOVPS_WpsVps + case OP2_MOVAPS_WsdVsd: + case OP2_MOVDQ_WdqVdq: + case OP3_PEXTRD_EdVdqIb: + return true; + default: + break; + } + return false; +} + +enum ThreeByteEscape { + ESCAPE_38 = 0x38, + ESCAPE_3A = 0x3A +}; + +enum VexOperandType { + VEX_PS = 0, + VEX_PD = 1, + VEX_SS = 2, + VEX_SD = 3 +}; + +inline OneByteOpcodeID jccRel8(Condition cond) +{ + return OneByteOpcodeID(OP_JCC_rel8 + cond); +} +inline TwoByteOpcodeID jccRel32(Condition cond) +{ + return TwoByteOpcodeID(OP2_JCC_rel32 + cond); +} +inline TwoByteOpcodeID setccOpcode(Condition cond) +{ + return TwoByteOpcodeID(OP_SETCC + cond); +} + +enum GroupOpcodeID { + GROUP1_OP_ADD = 0, + GROUP1_OP_OR = 1, + GROUP1_OP_ADC = 2, + GROUP1_OP_AND = 4, + GROUP1_OP_SUB = 5, + GROUP1_OP_XOR = 6, + GROUP1_OP_CMP = 7, + + GROUP1A_OP_POP = 0, + + GROUP2_OP_SHL = 4, + GROUP2_OP_SHR = 5, + GROUP2_OP_SAR = 7, + + GROUP3_OP_TEST = 0, + GROUP3_OP_NOT = 2, + GROUP3_OP_NEG = 3, + GROUP3_OP_IMUL = 5, + GROUP3_OP_DIV = 6, + GROUP3_OP_IDIV = 7, + + GROUP5_OP_INC = 0, + GROUP5_OP_DEC = 1, + GROUP5_OP_CALLN = 2, + GROUP5_OP_JMPN = 4, + GROUP5_OP_PUSH = 6, + + FPU6_OP_FLD = 0, + FPU6_OP_FISTTP = 1, + FPU6_OP_FSTP = 3, + + GROUP11_MOV = 0 +}; + +static const RegisterID noBase = rbp; +static const RegisterID hasSib = rsp; +static const RegisterID noIndex = rsp; +#ifdef JS_CODEGEN_X64 +static const RegisterID noBase2 = r13; +static const RegisterID hasSib2 = r12; +#endif + +enum ModRmMode { + ModRmMemoryNoDisp, + ModRmMemoryDisp8, + ModRmMemoryDisp32, + ModRmRegister +}; + +} // namespace X86Encoding + +} // namespace jit +} // namespace js + +#endif /* jit_shared_Encoding_x86_shared_h */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/LIR-x86-shared.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/LIR-x86-shared.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/LIR-x86-shared.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/LIR-x86-shared.h 2015-02-03 14:33:32.000000000 +0000 @@ -282,16 +282,16 @@ } }; -class LGuardObjectType : public LInstructionHelper<0, 1, 0> +class LGuardObjectGroup : public LInstructionHelper<0, 1, 0> { public: - LIR_HEADER(GuardObjectType) + LIR_HEADER(GuardObjectGroup) - explicit LGuardObjectType(const LAllocation &in) { + explicit LGuardObjectGroup(const LAllocation &in) { setOperand(0, in); } - const MGuardObjectType *mir() const { - return mir_->toGuardObjectType(); + const MGuardObjectGroup *mir() const { + return mir_->toGuardObjectGroup(); } }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Lowering-x86-shared.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Lowering-x86-shared.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Lowering-x86-shared.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Lowering-x86-shared.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -44,12 +44,12 @@ } void -LIRGeneratorX86Shared::visitGuardObjectType(MGuardObjectType *ins) +LIRGeneratorX86Shared::visitGuardObjectGroup(MGuardObjectGroup *ins) { MOZ_ASSERT(ins->obj()->type() == MIRType_Object); - LGuardObjectType *guard = new(alloc()) LGuardObjectType(useRegisterAtStart(ins->obj())); - assignSnapshot(guard, Bailout_ObjectIdentityOrTypeGuard); + LGuardObjectGroup *guard = new(alloc()) LGuardObjectGroup(useRegisterAtStart(ins->obj())); + assignSnapshot(guard, ins->bailoutKind()); add(guard, ins); redefine(ins, ins->obj()); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Lowering-x86-shared.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Lowering-x86-shared.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Lowering-x86-shared.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Lowering-x86-shared.h 2015-02-03 14:33:32.000000000 +0000 @@ -24,7 +24,7 @@ LTableSwitchV *newLTableSwitchV(MTableSwitch *ins); void visitGuardShape(MGuardShape *ins); - void visitGuardObjectType(MGuardObjectType *ins); + void visitGuardObjectGroup(MGuardObjectGroup *ins); void visitPowHalf(MPowHalf *ins); void lowerForShift(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs, MDefinition *rhs); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/MacroAssembler-x86-shared.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/MacroAssembler-x86-shared.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/MacroAssembler-x86-shared.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/MacroAssembler-x86-shared.h 2015-02-03 14:33:32.000000000 +0000 @@ -213,17 +213,17 @@ } void atomic_cmpxchg8(Register newval, const Operand &addr, Register oldval_and_result) { // %eax must be explicitly provided for calling clarity. - MOZ_ASSERT(oldval_and_result.code() == X86Registers::eax); + MOZ_ASSERT(oldval_and_result.code() == X86Encoding::rax); lock_cmpxchg8(newval, addr); } void atomic_cmpxchg16(Register newval, const Operand &addr, Register oldval_and_result) { // %eax must be explicitly provided for calling clarity. - MOZ_ASSERT(oldval_and_result.code() == X86Registers::eax); + MOZ_ASSERT(oldval_and_result.code() == X86Encoding::rax); lock_cmpxchg16(newval, addr); } void atomic_cmpxchg32(Register newval, const Operand &addr, Register oldval_and_result) { // %eax must be explicitly provided for calling clarity. - MOZ_ASSERT(oldval_and_result.code() == X86Registers::eax); + MOZ_ASSERT(oldval_and_result.code() == X86Encoding::rax); lock_cmpxchg32(newval, addr); } @@ -650,6 +650,44 @@ Condition cond = testDoubleTruthy(truthy, reg); j(cond, label); } + + // Class which ensures that registers used in byte ops are compatible with + // such instructions, even if the original register passed in wasn't. This + // only applies to x86, as on x64 all registers are valid single byte regs. + // This doesn't lead to great code but helps to simplify code generation. + class AutoEnsureByteRegister { + MacroAssemblerX86Shared *masm; + Register reg_; + bool read_; + + public: + template + AutoEnsureByteRegister(MacroAssemblerX86Shared *masm, T address, Register reg, bool read) + : masm(masm), reg_(reg), read_(read) + { + if (!GeneralRegisterSet(Registers::SingleByteRegs).has(reg_)) { + MOZ_ASSERT(address.base != StackPointer); + masm->push(eax); + if (read) + masm->mov(reg_, eax); + } + } + + ~AutoEnsureByteRegister() { + if (!GeneralRegisterSet(Registers::SingleByteRegs).has(reg_)) { + if (!read_) + masm->mov(eax, reg_); + masm->pop(eax); + } + } + + Register reg() { + if (!GeneralRegisterSet(Registers::SingleByteRegs).has(reg_)) + return eax; + return reg_; + } + }; + void load8ZeroExtend(const Address &src, Register dest) { movzbl(Operand(src), dest); } @@ -662,11 +700,16 @@ void load8SignExtend(const BaseIndex &src, Register dest) { movsbl(Operand(src), dest); } - template - void store8(const S &src, const T &dest) { + template + void store8(Imm32 src, const T &dest) { movb(src, Operand(dest)); } template + void store8(Register src, const T &dest) { + AutoEnsureByteRegister ensure(this, dest, src, /* read = */ true); + movb(ensure.reg(), Operand(dest)); + } + template void compareExchange8ZeroExtend(const T &mem, Register oldval, Register newval, Register output) { MOZ_ASSERT(output == eax); MOZ_ASSERT(newval == ebx || newval == ecx || newval == edx); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/MoveEmitter-x86-shared.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/MoveEmitter-x86-shared.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/MoveEmitter-x86-shared.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/MoveEmitter-x86-shared.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -97,6 +97,12 @@ void MoveEmitterX86::emit(const MoveResolver &moves) { +#if defined(JS_CODEGEN_X86) && defined(DEBUG) + // Clobber any scratch register we have, to make regalloc bugs more visible. + if (hasScratchRegister()) + masm.mov(ImmWord(0xdeadbeef), scratchRegister()); +#endif + for (size_t i = 0; i < moves.numMoves(); i++) { const MoveOp &move = moves.getMove(i); const MoveOperand &from = move.from(); @@ -365,15 +371,15 @@ } else { // Memory to memory gpr move. MOZ_ASSERT(from.isMemory()); -#ifdef JS_CODEGEN_X64 - // x64 has a ScratchReg. Use it. - masm.load32(toAddress(from), ScratchReg); - masm.move32(ScratchReg, toOperand(to)); -#else - // No ScratchReg; bounce it off the stack. - masm.Push(toOperand(from)); - masm.Pop(toPopOperand(to)); -#endif + if (hasScratchRegister()) { + Register reg = scratchRegister(); + masm.load32(toAddress(from), reg); + masm.move32(reg, toOperand(to)); + } else { + // No scratch register available; bounce it off the stack. + masm.Push(toOperand(from)); + masm.Pop(toPopOperand(to)); + } } } @@ -390,30 +396,30 @@ masm.lea(toOperand(from), to.reg()); } else if (from.isMemory()) { // Memory to memory gpr move. -#ifdef JS_CODEGEN_X64 - // x64 has a ScratchReg. Use it. - masm.loadPtr(toAddress(from), ScratchReg); - masm.mov(ScratchReg, toOperand(to)); -#else - // No ScratchReg; bounce it off the stack. - masm.Push(toOperand(from)); - masm.Pop(toPopOperand(to)); -#endif + if (hasScratchRegister()) { + Register reg = scratchRegister(); + masm.loadPtr(toAddress(from), reg); + masm.mov(reg, toOperand(to)); + } else { + // No scratch register available; bounce it off the stack. + masm.Push(toOperand(from)); + masm.Pop(toPopOperand(to)); + } } else { // Effective address to memory move. MOZ_ASSERT(from.isEffectiveAddress()); -#ifdef JS_CODEGEN_X64 - // x64 has a ScratchReg. Use it. - masm.lea(toOperand(from), ScratchReg); - masm.mov(ScratchReg, toOperand(to)); -#else - // This is tricky without a ScratchReg. We can't do an lea. Bounce the - // base register off the stack, then add the offset in place. Note that - // this clobbers FLAGS! - masm.Push(from.base()); - masm.Pop(toPopOperand(to)); - masm.addPtr(Imm32(from.disp()), toOperand(to)); -#endif + if (hasScratchRegister()) { + Register reg = scratchRegister(); + masm.lea(toOperand(from), reg); + masm.mov(reg, toOperand(to)); + } else { + // This is tricky without a scratch reg. We can't do an lea. Bounce the + // base register off the stack, then add the offset in place. Note that + // this clobbers FLAGS! + masm.Push(from.base()); + masm.Pop(toPopOperand(to)); + masm.addPtr(Imm32(from.disp()), toOperand(to)); + } } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/MoveEmitter-x86-shared.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/MoveEmitter-x86-shared.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/MoveEmitter-x86-shared.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/MoveEmitter-x86-shared.h 2015-02-03 14:33:32.000000000 +0000 @@ -33,6 +33,11 @@ // codegen->framePushed_ at the time it is allocated. -1 if not allocated. int32_t pushedAtCycle_; +#ifdef JS_CODEGEN_X86 + // Optional scratch register for performing moves. + mozilla::Maybe scratchRegister_; +#endif + void assertDone(); Address cycleSlot(); Address toAddress(const MoveOperand &operand) const; @@ -57,6 +62,29 @@ ~MoveEmitterX86(); void emit(const MoveResolver &moves); void finish(); + + void setScratchRegister(Register reg) { +#ifdef JS_CODEGEN_X86 + scratchRegister_.emplace(reg); +#endif + } + + bool hasScratchRegister() { +#ifdef JS_CODEGEN_X86 + return scratchRegister_.isSome(); +#else + return true; +#endif + } + + Register scratchRegister() { + MOZ_ASSERT(hasScratchRegister()); +#ifdef JS_CODEGEN_X86 + return scratchRegister_.value(); +#else + return ScratchReg; +#endif + } }; typedef MoveEmitterX86 MoveEmitter; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Patching-x86-shared.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Patching-x86-shared.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/shared/Patching-x86-shared.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/shared/Patching-x86-shared.h 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 jit_shared_Patching_x86_shared_h +#define jit_shared_Patching_x86_shared_h + +namespace js { +namespace jit { + +namespace X86Encoding { + +inline void * +GetPointer(const void* where) +{ + return reinterpret_cast(where)[-1]; +} + +inline void ** +GetPointerRef(void* where) +{ + return &reinterpret_cast(where)[-1]; +} + +inline void +SetPointer(void* where, const void* value) +{ + reinterpret_cast(where)[-1] = value; +} + +inline int32_t +GetInt32(const void* where) +{ + return reinterpret_cast(where)[-1]; +} + +inline void +SetInt32(void* where, int32_t value) +{ + reinterpret_cast(where)[-1] = value; +} + +inline void +AddInt32(void* where, int32_t value) +{ +#ifdef DEBUG + uint32_t x = reinterpret_cast(where)[-1]; + uint32_t y = x + uint32_t(value); + MOZ_ASSERT(value >= 0 ? (int32_t(y) >= int32_t(x)) : (int32_t(y) < int32_t(x))); +#endif + reinterpret_cast(where)[-1] += uint32_t(value); +} + +inline void +SetRel32(void* from, void* to) +{ + intptr_t offset = reinterpret_cast(to) - reinterpret_cast(from); + MOZ_ASSERT(offset == static_cast(offset), + "offset is too great for a 32-bit relocation"); + if (offset != static_cast(offset)) + MOZ_CRASH("offset is too great for a 32-bit relocation"); + + SetInt32(from, offset); +} + +inline void * +GetRel32Target(void* where) +{ + int32_t rel = GetInt32(where); + return (char *)where + rel; +} + +class JmpSrc { + public: + JmpSrc() + : offset_(-1) + { + } + + explicit JmpSrc(int32_t offset) + : offset_(offset) + { + } + + int32_t offset() const { + return offset_; + } + + bool isSet() const { + return offset_ != -1; + } + + private: + int offset_; +}; + +class JmpDst { + public: + JmpDst() + : offset_(-1) + , used_(false) + { + } + + bool isUsed() const { return used_; } + void used() { used_ = true; } + bool isValid() const { return offset_ != -1; } + + explicit JmpDst(int32_t offset) + : offset_(offset) + , used_(false) + { + MOZ_ASSERT(offset_ == offset); + } + int32_t offset() const { + return offset_; + } + private: + int32_t offset_ : 31; + bool used_ : 1; +}; + +inline bool +CanRelinkJump(void* from, void* to) +{ + intptr_t offset = static_cast(to) - static_cast(from); + return (offset == static_cast(offset)); +} + +} // namespace X86Encoding + +} // namespace jit +} // namespace js + +#endif /* jit_shared_Patching_x86_shared_h */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Snapshots.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Snapshots.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/Snapshots.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/Snapshots.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -19,10 +19,38 @@ using namespace js; using namespace js::jit; +// Encodings: +// [ptr] A fixed-size pointer. +// [vwu] A variable-width unsigned integer. +// [vws] A variable-width signed integer. +// [u8] An 8-bit unsigned integer. +// [u8'] An 8-bit unsigned integer which is potentially extended with packed +// data. +// [u8"] Packed data which is stored and packed in the previous [u8']. +// [vwu*] A list of variable-width unsigned integers. +// [pld] Payload of Recover Value Allocation: +// PAYLOAD_NONE: +// There is no payload. +// +// PAYLOAD_INDEX: +// [vwu] Index, such as the constant pool index. +// +// PAYLOAD_STACK_OFFSET: +// [vws] Stack offset based on the base of the Ion frame. +// +// PAYLOAD_GPR: +// [u8] Code of the general register. +// +// PAYLOAD_FPU: +// [u8] Code of the FPU register. +// +// PAYLOAD_PACKED_TAG: +// [u8"] Bits 5-7: JSValueType is encoded on the low bits of the Mode +// of the RValueAllocation. +// // Snapshot header: // -// [vwu] bits ((n+1)-31]: frame count -// bit n+1: resume after +// [vwu] bits ((n+1)-31]: recover instruction offset // bits [0,n): bailout kind (n = SNAPSHOT_BAILOUTKIND_BITS) // // Snapshot body, repeated "frame count" times, from oldest frame to newest frame. @@ -92,35 +120,6 @@ // Value with statically known type, which payload is stored at an // offset on the stack. // -// Encodings: -// [ptr] A fixed-size pointer. -// [vwu] A variable-width unsigned integer. -// [vws] A variable-width signed integer. -// [u8] An 8-bit unsigned integer. -// [u8'] An 8-bit unsigned integer which is potentially extended with packed -// data. -// [u8"] Packed data which is stored and packed in the previous [u8']. -// [vwu*] A list of variable-width unsigned integers. -// [pld] Payload of Recover Value Allocation: -// PAYLOAD_NONE: -// There is no payload. -// -// PAYLOAD_INDEX: -// [vwu] Index, such as the constant pool index. -// -// PAYLOAD_STACK_OFFSET: -// [vws] Stack offset based on the base of the Ion frame. -// -// PAYLOAD_GPR: -// [u8] Code of the general register. -// -// PAYLOAD_FPU: -// [u8] Code of the FPU register. -// -// PAYLOAD_PACKED_TAG: -// [u8"] Bits 5-7: JSValueType is encoded on the low bits of the Mode -// of the RValueAllocation. -// const RValueAllocation::Layout & RValueAllocation::layoutFromMode(Mode mode) @@ -498,7 +497,7 @@ // Details of snapshot header packing. static const uint32_t SNAPSHOT_BAILOUTKIND_SHIFT = 0; -static const uint32_t SNAPSHOT_BAILOUTKIND_BITS = 5; +static const uint32_t SNAPSHOT_BAILOUTKIND_BITS = 6; static const uint32_t SNAPSHOT_BAILOUTKIND_MASK = COMPUTE_MASK_(SNAPSHOT_BAILOUTKIND); static const uint32_t SNAPSHOT_ROFFSET_SHIFT = COMPUTE_SHIFT_AFTER_(SNAPSHOT_BAILOUTKIND); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/StackSlotAllocator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/StackSlotAllocator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/StackSlotAllocator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/StackSlotAllocator.h 2015-02-03 14:33:32.000000000 +0000 @@ -75,7 +75,7 @@ StackSlotAllocator() : height_(0) { } - void freeSlot(LDefinition::Type type, uint32_t index) { + static uint32_t width(LDefinition::Type type) { switch (type) { #if JS_BITS_PER_WORD == 32 case LDefinition::GENERAL: @@ -83,7 +83,7 @@ case LDefinition::SLOTS: #endif case LDefinition::INT32: - case LDefinition::FLOAT32: return freeSlot(index); + case LDefinition::FLOAT32: return 4; #if JS_BITS_PER_WORD == 64 case LDefinition::GENERAL: case LDefinition::OBJECT: @@ -96,39 +96,29 @@ case LDefinition::TYPE: case LDefinition::PAYLOAD: #endif - case LDefinition::DOUBLE: return freeDoubleSlot(index); + case LDefinition::DOUBLE: return 8; case LDefinition::FLOAT32X4: - case LDefinition::INT32X4: return freeQuadSlot(index); + case LDefinition::INT32X4: return 16; } MOZ_CRASH("Unknown slot type"); } + void freeSlot(LDefinition::Type type, uint32_t index) { + switch (width(type)) { + case 4: return freeSlot(index); + case 8: return freeDoubleSlot(index); + case 16: return freeQuadSlot(index); + } + MOZ_CRASH("Unknown slot width"); + } + uint32_t allocateSlot(LDefinition::Type type) { - switch (type) { -#if JS_BITS_PER_WORD == 32 - case LDefinition::GENERAL: - case LDefinition::OBJECT: - case LDefinition::SLOTS: -#endif - case LDefinition::INT32: - case LDefinition::FLOAT32: return allocateSlot(); -#if JS_BITS_PER_WORD == 64 - case LDefinition::GENERAL: - case LDefinition::OBJECT: - case LDefinition::SLOTS: -#endif -#ifdef JS_PUNBOX64 - case LDefinition::BOX: -#endif -#ifdef JS_NUNBOX32 - case LDefinition::TYPE: - case LDefinition::PAYLOAD: -#endif - case LDefinition::DOUBLE: return allocateDoubleSlot(); - case LDefinition::FLOAT32X4: - case LDefinition::INT32X4: return allocateQuadSlot(); + switch (width(type)) { + case 4: return allocateSlot(); + case 8: return allocateDoubleSlot(); + case 16: return allocateQuadSlot(); } - MOZ_CRASH("Unknown slot type"); + MOZ_CRASH("Unknown slot width"); } uint32_t stackHeight() const { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/TypePolicy.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/TypePolicy.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/TypePolicy.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/TypePolicy.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -95,6 +95,26 @@ } bool +AllDoublePolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins) +{ + for (size_t i = 0, e = ins->numOperands(); i < e; i++) { + MDefinition *in = ins->getOperand(i); + if (in->type() == MIRType_Double) + continue; + + MInstruction *replace = MToDouble::New(alloc, in); + + ins->block()->insertBefore(ins, replace); + ins->replaceOperand(i, replace); + + if (!replace->typePolicy()->adjustInputs(alloc, replace)) + return false; + } + + return true; +} + +bool ComparePolicy::adjustInputs(TempAllocator &alloc, MInstruction *def) { MOZ_ASSERT(def->isCompare()); @@ -717,6 +737,29 @@ template bool ObjectPolicy<2>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins); template bool ObjectPolicy<3>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins); +template +bool +SimdSameAsReturnedTypePolicy::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins) +{ + MIRType type = ins->type(); + MOZ_ASSERT(IsSimdType(type)); + + MDefinition *in = ins->getOperand(Op); + if (in->type() == type) + return true; + + MSimdUnbox *replace = MSimdUnbox::New(alloc, in, type); + ins->block()->insertBefore(ins, replace); + ins->replaceOperand(Op, replace); + + return replace->typePolicy()->adjustInputs(alloc, replace); +} + +template bool +SimdSameAsReturnedTypePolicy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins); +template bool +SimdSameAsReturnedTypePolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins); + bool CallPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins) { @@ -1002,6 +1045,7 @@ _(StoreTypedArrayPolicy) \ _(StoreUnboxedObjectOrNullPolicy) \ _(TestPolicy) \ + _(AllDoublePolicy) \ _(ToDoublePolicy) \ _(ToInt32Policy) \ _(ToStringPolicy) \ @@ -1042,6 +1086,7 @@ _(MixPolicy, StringPolicy<1> >) \ _(MixPolicy, ConvertToStringPolicy<2> >) \ _(MixPolicy, ConvertToStringPolicy<0> >) \ + _(MixPolicy, SimdSameAsReturnedTypePolicy<1> >) \ _(MixPolicy, IntPolicy<1> >) \ _(MixPolicy, StringPolicy<1> >) \ _(NoFloatPolicy<0>) \ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/TypePolicy.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/TypePolicy.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/TypePolicy.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/TypePolicy.h 2015-02-03 14:33:32.000000000 +0000 @@ -93,6 +93,13 @@ virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE; }; +class AllDoublePolicy MOZ_FINAL : public TypePolicy +{ + public: + EMPTY_DATA_; + bool adjustInputs(TempAllocator &alloc, MInstruction *def); +}; + class BitwisePolicy MOZ_FINAL : public TypePolicy { public: @@ -317,6 +324,19 @@ } }; +// SIMD value-type policy, use the returned type of the instruction to determine +// how to unbox its operand. +template +class SimdSameAsReturnedTypePolicy MOZ_FINAL : public TypePolicy +{ + public: + EMPTY_DATA_; + static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *ins); + virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE { + return staticAdjustInputs(alloc, ins); + } +}; + template class BoxPolicy MOZ_FINAL : public TypePolicy { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/VMFunctions.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/VMFunctions.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/VMFunctions.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/VMFunctions.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -287,16 +287,16 @@ JSObject* NewInitObject(JSContext *cx, HandlePlainObject templateObject) { - NewObjectKind newKind = templateObject->hasSingletonType() ? SingletonObject : GenericObject; - if (!templateObject->hasLazyType() && templateObject->type()->shouldPreTenure()) + NewObjectKind newKind = templateObject->isSingleton() ? SingletonObject : GenericObject; + if (!templateObject->hasLazyGroup() && templateObject->group()->shouldPreTenure()) newKind = TenuredObject; RootedObject obj(cx, CopyInitializerObject(cx, templateObject, newKind)); if (!obj) return nullptr; - if (!templateObject->hasSingletonType()) - obj->setType(templateObject->type()); + if (!templateObject->isSingleton()) + obj->setGroup(templateObject->group()); return obj; } @@ -519,9 +519,9 @@ } JSObject * -NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type, uint32_t lexicalBegin) +NewCallObject(JSContext *cx, HandleShape shape, HandleObjectGroup group, uint32_t lexicalBegin) { - JSObject *obj = CallObject::create(cx, shape, type, lexicalBegin); + JSObject *obj = CallObject::create(cx, shape, group, lexicalBegin); if (!obj) return nullptr; @@ -561,16 +561,8 @@ OperatorIn(JSContext *cx, HandleValue key, HandleObject obj, bool *out) { RootedId id(cx); - if (!ValueToId(cx, key, &id)) - return false; - - RootedObject obj2(cx); - RootedShape prop(cx); - if (!LookupProperty(cx, obj, id, &obj2, &prop)) - return false; - - *out = !!prop; - return true; + return ValueToId(cx, key, &id) && + HasProperty(cx, obj, id, out); } bool @@ -777,7 +769,7 @@ JitFrameLayout *prefix = frame->framePrefix(); EnsureExitFrame(prefix); - cx->mainThread().jitTop = (uint8_t *)prefix; + cx->runtime()->jitTop = (uint8_t *)prefix; return false; } @@ -921,7 +913,7 @@ Rooted arrRes(cx, &objRes->as()); MOZ_ASSERT(!arrRes->getDenseInitializedLength()); - MOZ_ASSERT(arrRes->type() == templateObj->type()); + MOZ_ASSERT(arrRes->group() == templateObj->group()); // Fast path: we managed to allocate the array inline; initialize the // slots. @@ -935,12 +927,12 @@ return arrRes; } - NewObjectKind newKind = templateObj->type()->shouldPreTenure() + NewObjectKind newKind = templateObj->group()->shouldPreTenure() ? TenuredObject : GenericObject; ArrayObject *arrRes = NewDenseCopiedArray(cx, length, rest, nullptr, newKind); if (arrRes) - arrRes->setType(templateObj->type()); + arrRes->setGroup(templateObj->group()); return arrRes; } @@ -1185,8 +1177,8 @@ MOZ_ASSERT(obj->compartment() == cx->compartment()); MOZ_ASSERT(obj->runtimeFromMainThread() == cx->runtime()); - MOZ_ASSERT_IF(!obj->hasLazyType(), - obj->type()->clasp() == obj->lastProperty()->getObjectClass()); + MOZ_ASSERT_IF(!obj->hasLazyGroup(), + obj->group()->clasp() == obj->lastProperty()->getObjectClass()); if (obj->isTenured()) { MOZ_ASSERT(obj->isAligned()); @@ -1296,9 +1288,9 @@ } void -MarkTypeObjectFromIon(JSRuntime *rt, types::TypeObject **typep) +MarkObjectGroupFromIon(JSRuntime *rt, types::ObjectGroup **groupp) { - gc::MarkTypeObjectUnbarriered(&rt->gc.marker, typep, "write barrier"); + gc::MarkObjectGroupUnbarriered(&rt->gc.marker, groupp, "write barrier"); } bool diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/VMFunctions.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/VMFunctions.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/VMFunctions.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/VMFunctions.h 2015-02-03 14:33:32.000000000 +0000 @@ -344,8 +344,8 @@ template <> struct TypeToArgProperties { static const uint32_t result = TypeToArgProperties::result | VMFunction::ByRef; }; -template <> struct TypeToArgProperties { - static const uint32_t result = TypeToArgProperties::result | VMFunction::ByRef; +template <> struct TypeToArgProperties { + static const uint32_t result = TypeToArgProperties::result | VMFunction::ByRef; }; // Convert argument type to whether or not it should be passed in a float @@ -382,7 +382,7 @@ template <> struct TypeToRootType { static const uint32_t result = VMFunction::RootCell; }; -template <> struct TypeToRootType { +template <> struct TypeToRootType { static const uint32_t result = VMFunction::RootCell; }; template <> struct TypeToRootType { @@ -674,7 +674,7 @@ bool InterruptCheck(JSContext *cx); void *MallocWrapper(JSRuntime *rt, size_t nbytes); -JSObject *NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type, +JSObject *NewCallObject(JSContext *cx, HandleShape shape, HandleObjectGroup group, uint32_t lexicalBegin); JSObject *NewSingletonCallObject(JSContext *cx, HandleShape shape, uint32_t lexicalBegin); JSObject *NewStringObject(JSContext *cx, HandleString str); @@ -759,7 +759,7 @@ void MarkStringFromIon(JSRuntime *rt, JSString **stringp); void MarkObjectFromIon(JSRuntime *rt, JSObject **objp); void MarkShapeFromIon(JSRuntime *rt, Shape **shapep); -void MarkTypeObjectFromIon(JSRuntime *rt, types::TypeObject **typep); +void MarkObjectGroupFromIon(JSRuntime *rt, types::ObjectGroup **groupp); // Helper for generatePreBarrier. inline void * @@ -774,8 +774,8 @@ return JS_FUNC_TO_DATA_PTR(void *, MarkObjectFromIon); case MIRType_Shape: return JS_FUNC_TO_DATA_PTR(void *, MarkShapeFromIon); - case MIRType_TypeObject: - return JS_FUNC_TO_DATA_PTR(void *, MarkTypeObjectFromIon); + case MIRType_ObjectGroup: + return JS_FUNC_TO_DATA_PTR(void *, MarkObjectGroupFromIon); default: MOZ_CRASH(); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x64/Architecture-x64.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x64/Architecture-x64.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x64/Architecture-x64.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x64/Architecture-x64.h 2015-02-03 14:33:32.000000000 +0000 @@ -7,7 +7,7 @@ #ifndef jit_x64_Architecture_x64_h #define jit_x64_Architecture_x64_h -#include "jit/shared/BaseAssembler-x86-shared.h" +#include "jit/shared/Constants-x86-shared.h" namespace js { namespace jit { @@ -26,7 +26,7 @@ class Registers { public: - typedef X86Registers::RegisterID Code; + typedef X86Encoding::RegisterID Code; typedef uint32_t SetType; static uint32_t SetSize(SetType x) { static_assert(sizeof(SetType) == 4, "SetType must be 32 bits"); @@ -54,8 +54,8 @@ return Invalid; } - static const Code StackPointer = X86Registers::esp; - static const Code Invalid = X86Registers::invalid_reg; + static const Code StackPointer = X86Encoding::rsp; + static const Code Invalid = X86Encoding::invalid_reg; static const uint32_t Total = 16; static const uint32_t TotalPhys = 16; @@ -65,59 +65,59 @@ static const uint32_t ArgRegMask = # if !defined(_WIN64) - (1 << X86Registers::edi) | - (1 << X86Registers::esi) | + (1 << X86Encoding::rdi) | + (1 << X86Encoding::rsi) | # endif - (1 << X86Registers::edx) | - (1 << X86Registers::ecx) | - (1 << X86Registers::r8) | - (1 << X86Registers::r9); + (1 << X86Encoding::rdx) | + (1 << X86Encoding::rcx) | + (1 << X86Encoding::r8) | + (1 << X86Encoding::r9); static const uint32_t VolatileMask = - (1 << X86Registers::eax) | - (1 << X86Registers::ecx) | - (1 << X86Registers::edx) | + (1 << X86Encoding::rax) | + (1 << X86Encoding::rcx) | + (1 << X86Encoding::rdx) | # if !defined(_WIN64) - (1 << X86Registers::esi) | - (1 << X86Registers::edi) | + (1 << X86Encoding::rsi) | + (1 << X86Encoding::rdi) | # endif - (1 << X86Registers::r8) | - (1 << X86Registers::r9) | - (1 << X86Registers::r10) | - (1 << X86Registers::r11); + (1 << X86Encoding::r8) | + (1 << X86Encoding::r9) | + (1 << X86Encoding::r10) | + (1 << X86Encoding::r11); static const uint32_t NonVolatileMask = - (1 << X86Registers::ebx) | + (1 << X86Encoding::rbx) | #if defined(_WIN64) - (1 << X86Registers::esi) | - (1 << X86Registers::edi) | + (1 << X86Encoding::rsi) | + (1 << X86Encoding::rdi) | #endif - (1 << X86Registers::ebp) | - (1 << X86Registers::r12) | - (1 << X86Registers::r13) | - (1 << X86Registers::r14) | - (1 << X86Registers::r15); + (1 << X86Encoding::rbp) | + (1 << X86Encoding::r12) | + (1 << X86Encoding::r13) | + (1 << X86Encoding::r14) | + (1 << X86Encoding::r15); static const uint32_t WrapperMask = VolatileMask; static const uint32_t SingleByteRegs = VolatileMask | NonVolatileMask; static const uint32_t NonAllocatableMask = - (1 << X86Registers::esp) | - (1 << X86Registers::r11); // This is ScratchReg. + (1 << X86Encoding::rsp) | + (1 << X86Encoding::r11); // This is ScratchReg. + + static const uint32_t AllocatableMask = AllMask & ~NonAllocatableMask; // Registers that can be allocated without being saved, generally. static const uint32_t TempMask = VolatileMask & ~NonAllocatableMask; - static const uint32_t AllocatableMask = AllMask & ~NonAllocatableMask; - // Registers returned from a JS -> JS call. static const uint32_t JSCallMask = - (1 << X86Registers::ecx); + (1 << X86Encoding::rcx); // Registers returned from a JS -> C call. static const uint32_t CallMask = - (1 << X86Registers::eax); + (1 << X86Encoding::rax); }; // Smallest integer type that can hold a register bitmask. @@ -125,14 +125,10 @@ class FloatRegisters { public: - typedef X86Registers::XMMRegisterID Code; + typedef X86Encoding::XMMRegisterID Code; typedef uint32_t SetType; static const char *GetName(Code code) { - static const char * const Names[] = { "xmm0", "xmm1", "xmm2", "xmm3", - "xmm4", "xmm5", "xmm6", "xmm7", - "xmm8", "xmm9", "xmm10", "xmm11", - "xmm12", "xmm13", "xmm14", "xmm15" }; - return Names[code]; + return X86Encoding::XMMRegName(code); } static Code FromName(const char *name) { @@ -143,7 +139,7 @@ return Invalid; } - static const Code Invalid = X86Registers::invalid_xmm; + static const Code Invalid = X86Encoding::invalid_xmm; static const uint32_t Total = 16; static const uint32_t TotalPhys = 16; @@ -154,12 +150,12 @@ static const uint32_t AllDoubleMask = AllMask; static const uint32_t VolatileMask = #if defined(_WIN64) - (1 << X86Registers::xmm0) | - (1 << X86Registers::xmm1) | - (1 << X86Registers::xmm2) | - (1 << X86Registers::xmm3) | - (1 << X86Registers::xmm4) | - (1 << X86Registers::xmm5); + (1 << X86Encoding::xmm0) | + (1 << X86Encoding::xmm1) | + (1 << X86Encoding::xmm2) | + (1 << X86Encoding::xmm3) | + (1 << X86Encoding::xmm4) | + (1 << X86Encoding::xmm5); #else AllMask; #endif @@ -169,10 +165,9 @@ static const uint32_t WrapperMask = VolatileMask; static const uint32_t NonAllocatableMask = - (1 << X86Registers::xmm15); // This is ScratchFloatReg. + (1 << X86Encoding::xmm15); // This is ScratchDoubleReg. static const uint32_t AllocatableMask = AllMask & ~NonAllocatableMask; - }; template @@ -221,7 +216,6 @@ uint32_t numAliased() const { return 1; } - // N.B. FloatRegister is an explicit outparam here because msvc-2010 // miscompiled it on win64 when the value was simply returned void aliased(uint32_t aliasIdx, FloatRegister *ret) { @@ -239,7 +233,7 @@ uint32_t size() const { return sizeof(double); } - uint32_t numAlignedAliased() { + uint32_t numAlignedAliased() const { return 1; } void alignedAliased(uint32_t aliasIdx, FloatRegister *ret) { @@ -250,7 +244,6 @@ static uint32_t GetSizeInBytes(const TypedRegisterSet &s); static uint32_t GetPushSizeInBytes(const TypedRegisterSet &s); uint32_t getRegisterDumpOffsetInBytes(); - }; // Arm/D32 has double registers that can NOT be treated as float32 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x64/Assembler-x64.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x64/Assembler-x64.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x64/Assembler-x64.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x64/Assembler-x64.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -101,11 +101,11 @@ } // Avoid r11, which is the MacroAssembler's ScratchReg. -const Register ABIArgGenerator::NonArgReturnReg0 = r10; -const Register ABIArgGenerator::NonArgReturnReg1 = r12; -const Register ABIArgGenerator::NonVolatileReg = r13; -const Register ABIArgGenerator::NonArg_VolatileReg = rax; -const Register ABIArgGenerator::NonReturn_VolatileReg0 = rcx; +const Register ABIArgGenerator::NonArgReturnReg0 = jit::r10; +const Register ABIArgGenerator::NonArgReturnReg1 = jit::r12; +const Register ABIArgGenerator::NonVolatileReg = jit::r13; +const Register ABIArgGenerator::NonArg_VolatileReg = jit::rax; +const Register ABIArgGenerator::NonReturn_VolatileReg0 = jit::rcx; void Assembler::writeRelocation(JmpSrc src, Relocation::Kind reloc) @@ -216,8 +216,8 @@ // to jump to a different code block. continue; } - if (X86Assembler::canRelinkJump(src, rp.target)) { - X86Assembler::setRel32(src, rp.target); + if (X86Encoding::CanRelinkJump(src, rp.target)) { + X86Encoding::SetRel32(src, rp.target); } else { // An extended jump table must exist, and its offset must be in // range. @@ -226,11 +226,11 @@ // Patch the jump to go to the extended jump entry. uint8_t *entry = buffer + extendedJumpTable_ + i * SizeOfJumpTableEntry; - X86Assembler::setRel32(src, entry); + X86Encoding::SetRel32(src, entry); // Now patch the pointer, note that we need to align it to // *after* the extended jump, i.e. after the 64-bit immedate. - X86Assembler::repatchPointer(entry + SizeOfExtendedJump, rp.target); + X86Encoding::SetPointer(entry + SizeOfExtendedJump, rp.target); } } } @@ -268,13 +268,13 @@ JitCode * Assembler::CodeFromJump(JitCode *code, uint8_t *jump) { - uint8_t *target = (uint8_t *)X86Assembler::getRel32Target(jump); + uint8_t *target = (uint8_t *)X86Encoding::GetRel32Target(jump); if (target >= code->raw() && target < code->raw() + code->instructionsSize()) { // This jump is within the code buffer, so it has been redirected to // the extended jump table. MOZ_ASSERT(target + SizeOfJumpTableEntry <= code->raw() + code->instructionsSize()); - target = (uint8_t *)X86Assembler::getPointer(target + SizeOfExtendedJump); + target = (uint8_t *)X86Encoding::GetPointer(target + SizeOfExtendedJump); } return JitCode::FromExecutable(target); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x64/Assembler-x64.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x64/Assembler-x64.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x64/Assembler-x64.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x64/Assembler-x64.h 2015-02-03 14:33:32.000000000 +0000 @@ -16,39 +16,39 @@ namespace js { namespace jit { -static MOZ_CONSTEXPR_VAR Register rax = { X86Registers::eax }; -static MOZ_CONSTEXPR_VAR Register rbx = { X86Registers::ebx }; -static MOZ_CONSTEXPR_VAR Register rcx = { X86Registers::ecx }; -static MOZ_CONSTEXPR_VAR Register rdx = { X86Registers::edx }; -static MOZ_CONSTEXPR_VAR Register rsi = { X86Registers::esi }; -static MOZ_CONSTEXPR_VAR Register rdi = { X86Registers::edi }; -static MOZ_CONSTEXPR_VAR Register rbp = { X86Registers::ebp }; -static MOZ_CONSTEXPR_VAR Register r8 = { X86Registers::r8 }; -static MOZ_CONSTEXPR_VAR Register r9 = { X86Registers::r9 }; -static MOZ_CONSTEXPR_VAR Register r10 = { X86Registers::r10 }; -static MOZ_CONSTEXPR_VAR Register r11 = { X86Registers::r11 }; -static MOZ_CONSTEXPR_VAR Register r12 = { X86Registers::r12 }; -static MOZ_CONSTEXPR_VAR Register r13 = { X86Registers::r13 }; -static MOZ_CONSTEXPR_VAR Register r14 = { X86Registers::r14 }; -static MOZ_CONSTEXPR_VAR Register r15 = { X86Registers::r15 }; -static MOZ_CONSTEXPR_VAR Register rsp = { X86Registers::esp }; - -static MOZ_CONSTEXPR_VAR FloatRegister xmm0 = { X86Registers::xmm0 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm1 = { X86Registers::xmm1 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm2 = { X86Registers::xmm2 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm3 = { X86Registers::xmm3 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm4 = { X86Registers::xmm4 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm5 = { X86Registers::xmm5 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm6 = { X86Registers::xmm6 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm7 = { X86Registers::xmm7 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm8 = { X86Registers::xmm8 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm9 = { X86Registers::xmm9 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm10 = { X86Registers::xmm10 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm11 = { X86Registers::xmm11 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm12 = { X86Registers::xmm12 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm13 = { X86Registers::xmm13 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm14 = { X86Registers::xmm14 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm15 = { X86Registers::xmm15 }; +static MOZ_CONSTEXPR_VAR Register rax = { X86Encoding::rax }; +static MOZ_CONSTEXPR_VAR Register rbx = { X86Encoding::rbx }; +static MOZ_CONSTEXPR_VAR Register rcx = { X86Encoding::rcx }; +static MOZ_CONSTEXPR_VAR Register rdx = { X86Encoding::rdx }; +static MOZ_CONSTEXPR_VAR Register rsi = { X86Encoding::rsi }; +static MOZ_CONSTEXPR_VAR Register rdi = { X86Encoding::rdi }; +static MOZ_CONSTEXPR_VAR Register rbp = { X86Encoding::rbp }; +static MOZ_CONSTEXPR_VAR Register r8 = { X86Encoding::r8 }; +static MOZ_CONSTEXPR_VAR Register r9 = { X86Encoding::r9 }; +static MOZ_CONSTEXPR_VAR Register r10 = { X86Encoding::r10 }; +static MOZ_CONSTEXPR_VAR Register r11 = { X86Encoding::r11 }; +static MOZ_CONSTEXPR_VAR Register r12 = { X86Encoding::r12 }; +static MOZ_CONSTEXPR_VAR Register r13 = { X86Encoding::r13 }; +static MOZ_CONSTEXPR_VAR Register r14 = { X86Encoding::r14 }; +static MOZ_CONSTEXPR_VAR Register r15 = { X86Encoding::r15 }; +static MOZ_CONSTEXPR_VAR Register rsp = { X86Encoding::rsp }; + +static MOZ_CONSTEXPR_VAR FloatRegister xmm0 = { X86Encoding::xmm0 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm1 = { X86Encoding::xmm1 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm2 = { X86Encoding::xmm2 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm3 = { X86Encoding::xmm3 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm4 = { X86Encoding::xmm4 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm5 = { X86Encoding::xmm5 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm6 = { X86Encoding::xmm6 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm7 = { X86Encoding::xmm7 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm8 = { X86Encoding::xmm8 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm9 = { X86Encoding::xmm9 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm10 = { X86Encoding::xmm10 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm11 = { X86Encoding::xmm11 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm12 = { X86Encoding::xmm12 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm13 = { X86Encoding::xmm13 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm14 = { X86Encoding::xmm14 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm15 = { X86Encoding::xmm15 }; // X86-common synonyms. static MOZ_CONSTEXPR_VAR Register eax = rax; @@ -60,8 +60,8 @@ static MOZ_CONSTEXPR_VAR Register ebp = rbp; static MOZ_CONSTEXPR_VAR Register esp = rsp; -static MOZ_CONSTEXPR_VAR Register InvalidReg = { X86Registers::invalid_reg }; -static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg = { X86Registers::invalid_xmm }; +static MOZ_CONSTEXPR_VAR Register InvalidReg = { X86Encoding::invalid_reg }; +static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg = { X86Encoding::invalid_xmm }; static MOZ_CONSTEXPR_VAR Register StackPointer = rsp; static MOZ_CONSTEXPR_VAR Register FramePointer = rbp; @@ -717,7 +717,7 @@ } void j(Condition cond, ImmPtr target, Relocation::Kind reloc = Relocation::HARDCODED) { - JmpSrc src = masm.jCC(static_cast(cond)); + JmpSrc src = masm.jCC(static_cast(cond)); addPendingJump(src, target, reloc); } @@ -767,10 +767,10 @@ static inline void PatchJump(CodeLocationJump jump, CodeLocationLabel label) { - if (X86Assembler::canRelinkJump(jump.raw(), label.raw())) { - X86Assembler::setRel32(jump.raw(), label.raw()); + if (X86Encoding::CanRelinkJump(jump.raw(), label.raw())) { + X86Encoding::SetRel32(jump.raw(), label.raw()); } else { - X86Assembler::setRel32(jump.raw(), jump.jumpTableEntry()); + X86Encoding::SetRel32(jump.raw(), jump.jumpTableEntry()); Assembler::PatchJumpEntry(jump.jumpTableEntry(), label.raw()); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x64/CodeGenerator-x64.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x64/CodeGenerator-x64.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x64/CodeGenerator-x64.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x64/CodeGenerator-x64.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -300,6 +300,7 @@ MOZ_CRASH("unexpected array type"); } uint32_t after = masm.size(); + verifyHeapAccessDisassembly(before, after, /*isLoad=*/true, vt, srcAddr, *out->output()); if (ool) masm.bind(ool->rejoin()); memoryBarrier(ins->mir()->barrierAfter()); @@ -366,6 +367,7 @@ } } uint32_t after = masm.size(); + verifyHeapAccessDisassembly(before, after, /*isLoad=*/false, vt, dstAddr, *ins->value()); if (rejoin.used()) masm.bind(&rejoin); memoryBarrier(ins->mir()->barrierAfter()); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x64/Lowering-x64.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x64/Lowering-x64.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x64/Lowering-x64.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x64/Lowering-x64.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -171,7 +171,7 @@ ? useRegisterAtStart(ptr) : useRegisterOrNonNegativeConstantAtStart(ptr); - LAsmJSStoreHeap *lir; + LAsmJSStoreHeap *lir = nullptr; // initialize to silence GCC warning switch (ins->viewType()) { case Scalar::Int8: case Scalar::Uint8: diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x64/MacroAssembler-x64.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x64/MacroAssembler-x64.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x64/MacroAssembler-x64.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x64/MacroAssembler-x64.h 2015-02-03 14:33:32.000000000 +0000 @@ -161,6 +161,10 @@ ///////////////////////////////////////////////////////////////// // X86/X64-common interface. ///////////////////////////////////////////////////////////////// + Address ToPayload(Address value) { + return value; + } + void storeValue(ValueOperand val, Operand dest) { movq(val.valueReg(), dest); } @@ -631,7 +635,7 @@ } void branch32(Condition cond, AbsoluteAddress lhs, Imm32 rhs, Label *label) { - if (X86Assembler::isAddressImmediate(lhs.addr)) { + if (X86Encoding::IsAddressImmediate(lhs.addr)) { branch32(cond, Operand(lhs), rhs, label); } else { mov(ImmPtr(lhs.addr), ScratchReg); @@ -643,7 +647,7 @@ branch32(cond, Address(ScratchReg, 0), rhs, label); } void branch32(Condition cond, AbsoluteAddress lhs, Register rhs, Label *label) { - if (X86Assembler::isAddressImmediate(lhs.addr)) { + if (X86Encoding::IsAddressImmediate(lhs.addr)) { branch32(cond, Operand(lhs), rhs, label); } else { mov(ImmPtr(lhs.addr), ScratchReg); @@ -651,7 +655,7 @@ } } void branchTest32(Condition cond, AbsoluteAddress address, Imm32 imm, Label *label) { - if (X86Assembler::isAddressImmediate(address.addr)) { + if (X86Encoding::IsAddressImmediate(address.addr)) { test32(Operand(address), imm); } else { mov(ImmPtr(address.addr), ScratchReg); @@ -663,7 +667,7 @@ // Specialization for AbsoluteAddress. void branchPtr(Condition cond, AbsoluteAddress addr, Register ptr, Label *label) { MOZ_ASSERT(ptr != ScratchReg); - if (X86Assembler::isAddressImmediate(addr.addr)) { + if (X86Encoding::IsAddressImmediate(addr.addr)) { branchPtr(cond, Operand(addr), ptr, label); } else { mov(ImmPtr(addr.addr), ScratchReg); @@ -671,7 +675,7 @@ } } void branchPtr(Condition cond, AbsoluteAddress addr, ImmWord ptr, Label *label) { - if (X86Assembler::isAddressImmediate(addr.addr)) { + if (X86Encoding::IsAddressImmediate(addr.addr)) { branchPtr(cond, Operand(addr), ptr, label); } else { mov(ImmPtr(addr.addr), ScratchReg); @@ -763,7 +767,7 @@ movePtr(noteMaybeNurseryPtr(imm), dest); } void loadPtr(AbsoluteAddress address, Register dest) { - if (X86Assembler::isAddressImmediate(address.addr)) { + if (X86Encoding::IsAddressImmediate(address.addr)) { movq(Operand(address), dest); } else { mov(ImmPtr(address.addr), ScratchReg); @@ -784,7 +788,7 @@ shlq(Imm32(1), dest); } void load32(AbsoluteAddress address, Register dest) { - if (X86Assembler::isAddressImmediate(address.addr)) { + if (X86Encoding::IsAddressImmediate(address.addr)) { movl(Operand(address), dest); } else { mov(ImmPtr(address.addr), ScratchReg); @@ -819,7 +823,7 @@ movq(src, dest); } void storePtr(Register src, AbsoluteAddress address) { - if (X86Assembler::isAddressImmediate(address.addr)) { + if (X86Encoding::IsAddressImmediate(address.addr)) { movq(src, Operand(address)); } else { mov(ImmPtr(address.addr), ScratchReg); @@ -827,7 +831,7 @@ } } void store32(Register src, AbsoluteAddress address) { - if (X86Assembler::isAddressImmediate(address.addr)) { + if (X86Encoding::IsAddressImmediate(address.addr)) { movl(src, Operand(address)); } else { mov(ImmPtr(address.addr), ScratchReg); @@ -1338,6 +1342,23 @@ template void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T &dest, MIRType slotType); + template + void storeUnboxedPayload(ValueOperand value, T address, size_t nbytes) { + switch (nbytes) { + case 8: + unboxNonDouble(value, ScratchReg); + storePtr(ScratchReg, address); + return; + case 4: + store32(value.valueReg(), address); + return; + case 1: + store8(value.valueReg(), address); + return; + default: MOZ_CRASH("Bad payload width"); + } + } + void loadInstructionPointerAfterCall(Register dest) { loadPtr(Address(StackPointer, 0x0), dest); } @@ -1351,7 +1372,7 @@ } void inc64(AbsoluteAddress dest) { - if (X86Assembler::isAddressImmediate(dest.addr)) { + if (X86Encoding::IsAddressImmediate(dest.addr)) { addPtr(Imm32(1), Operand(dest)); } else { mov(ImmPtr(dest.addr), ScratchReg); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x64/Trampoline-x64.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x64/Trampoline-x64.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x64/Trampoline-x64.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x64/Trampoline-x64.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -380,18 +380,47 @@ // Do not erase the frame pointer in this function. MacroAssembler masm(cx); + // Caller: + // [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- rsp + // '--- #r8 ---' // ArgumentsRectifierReg contains the |nargs| pushed onto the current frame. // Including |this|, there are (|nargs| + 1) arguments to copy. MOZ_ASSERT(ArgumentsRectifierReg == r8); - // Load the number of |undefined|s to push into %rcx. + // Add |this|, in the counter of known arguments. + masm.addl(Imm32(1), r8); + + // Load |nformals| into %rcx. masm.loadPtr(Address(rsp, RectifierFrameLayout::offsetOfCalleeToken()), rax); masm.mov(rax, rcx); masm.andq(Imm32(uint32_t(CalleeTokenMask)), rcx); masm.movzwl(Operand(rcx, JSFunction::offsetOfNargs()), rcx); + + // Including |this|, there are (|nformals| + 1) arguments to push to the + // stack. Then we push a JitFrameLayout. We compute the padding expressed + // in the number of extra |undefined| values to push on the stack. + static_assert(sizeof(JitFrameLayout) % JitStackAlignment == 0, + "No need to consider the JitFrameLayout for aligning the stack"); + static_assert(JitStackAlignment % sizeof(Value) == 0, + "Ensure that we can pad the stack by pushing extra UndefinedValue"); + + const uint32_t alignment = JitStackAlignment / sizeof(Value); + MOZ_ASSERT(IsPowerOfTwo(alignment)); + masm.addl(Imm32(alignment - 1 /* for padding */ + 1 /* for |this| */), rcx); + masm.andl(Imm32(~(alignment - 1)), rcx); + + // Load the number of |undefined|s to push into %rcx. masm.subq(r8, rcx); + // Caller: + // [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- rsp <- r9 + // '------ #r8 -------' + // + // Rectifier frame: + // [undef] [undef] [undef] [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] + // '------- #rcx --------' '------ #r8 -------' + // Copy the number of actual arguments masm.loadPtr(Address(rsp, RectifierFrameLayout::offsetOfNumActualArgs()), rdx); @@ -399,7 +428,7 @@ masm.movq(rsp, r9); // Save %rsp. - // Push undefined. + // Push undefined. (including the padding) { Label undefLoopTop; masm.bind(&undefLoopTop); @@ -410,11 +439,14 @@ } // Get the topmost argument. - BaseIndex b = BaseIndex(r9, r8, TimesEight, sizeof(RectifierFrameLayout)); + static_assert(sizeof(Value) == 8, "TimesEight is used to skip arguments"); + + // | - sizeof(Value)| is used to put rcx such that we can read the last + // argument, and not the value which is after. + BaseIndex b = BaseIndex(r9, r8, TimesEight, sizeof(RectifierFrameLayout) - sizeof(Value)); masm.lea(Operand(b), rcx); - // Push arguments, |nargs| + 1 times (to include |this|). - masm.addl(Imm32(1), r8); + // Copy & Push arguments, |nargs| + 1 times (to include |this|). { Label copyLoopTop; @@ -425,6 +457,14 @@ masm.j(Assembler::NonZero, ©LoopTop); } + // Caller: + // [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- r9 + // + // + // Rectifier frame: + // [undef] [undef] [undef] [arg2] [arg1] [this] <- rsp [[argc] [callee] [descr] [raddr]] + // + // Construct descriptor. masm.subq(rsp, r9); masm.makeFrameDescriptor(r9, JitFrame_Rectifier); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x86/Architecture-x86.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x86/Architecture-x86.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x86/Architecture-x86.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x86/Architecture-x86.h 2015-02-03 14:33:32.000000000 +0000 @@ -7,7 +7,7 @@ #ifndef jit_x86_Architecture_x86_h #define jit_x86_Architecture_x86_h -#include "jit/shared/BaseAssembler-x86-shared.h" +#include "jit/shared/Constants-x86-shared.h" namespace js { namespace jit { @@ -35,7 +35,7 @@ class Registers { public: - typedef X86Registers::RegisterID Code; + typedef X86Encoding::RegisterID Code; typedef uint8_t SetType; static uint32_t SetSize(SetType x) { static_assert(sizeof(SetType) == 1, "SetType must be 8 bits"); @@ -48,9 +48,7 @@ return 31 - mozilla::CountLeadingZeroes32(x); } static const char *GetName(Code code) { - static const char * const Names[] = { "eax", "ecx", "edx", "ebx", - "esp", "ebp", "esi", "edi" }; - return Names[code]; + return X86Encoding::GPRegName(code); } static Code FromName(const char *name) { @@ -61,8 +59,8 @@ return Invalid; } - static const Code StackPointer = X86Registers::esp; - static const Code Invalid = X86Registers::invalid_reg; + static const Code StackPointer = X86Encoding::rsp; + static const Code Invalid = X86Encoding::invalid_reg; static const uint32_t Total = 8; static const uint32_t TotalPhys = 8; @@ -73,28 +71,28 @@ static const uint32_t ArgRegMask = 0; static const uint32_t VolatileMask = - (1 << X86Registers::eax) | - (1 << X86Registers::ecx) | - (1 << X86Registers::edx); + (1 << X86Encoding::rax) | + (1 << X86Encoding::rcx) | + (1 << X86Encoding::rdx); static const uint32_t NonVolatileMask = - (1 << X86Registers::ebx) | - (1 << X86Registers::esi) | - (1 << X86Registers::edi) | - (1 << X86Registers::ebp); + (1 << X86Encoding::rbx) | + (1 << X86Encoding::rsi) | + (1 << X86Encoding::rdi) | + (1 << X86Encoding::rbp); static const uint32_t WrapperMask = VolatileMask | - (1 << X86Registers::ebx); + (1 << X86Encoding::rbx); static const uint32_t SingleByteRegs = - (1 << X86Registers::eax) | - (1 << X86Registers::ecx) | - (1 << X86Registers::edx) | - (1 << X86Registers::ebx); + (1 << X86Encoding::rax) | + (1 << X86Encoding::rcx) | + (1 << X86Encoding::rdx) | + (1 << X86Encoding::rbx); static const uint32_t NonAllocatableMask = - (1 << X86Registers::esp); + (1 << X86Encoding::rsp); static const uint32_t AllocatableMask = AllMask & ~NonAllocatableMask; @@ -103,12 +101,12 @@ // Registers returned from a JS -> JS call. static const uint32_t JSCallMask = - (1 << X86Registers::ecx) | - (1 << X86Registers::edx); + (1 << X86Encoding::rcx) | + (1 << X86Encoding::rdx); // Registers returned from a JS -> C call. static const uint32_t CallMask = - (1 << X86Registers::eax); + (1 << X86Encoding::rax); }; // Smallest integer type that can hold a register bitmask. @@ -116,12 +114,10 @@ class FloatRegisters { public: - typedef X86Registers::XMMRegisterID Code; + typedef X86Encoding::XMMRegisterID Code; typedef uint32_t SetType; static const char *GetName(Code code) { - static const char * const Names[] = { "xmm0", "xmm1", "xmm2", "xmm3", - "xmm4", "xmm5", "xmm6", "xmm7" }; - return Names[code]; + return X86Encoding::XMMRegName(code); } static Code FromName(const char *name) { @@ -132,7 +128,7 @@ return Invalid; } - static const Code Invalid = X86Registers::invalid_xmm; + static const Code Invalid = X86Encoding::invalid_xmm; static const uint32_t Total = 8; static const uint32_t TotalPhys = 8; @@ -146,7 +142,7 @@ static const uint32_t WrapperMask = VolatileMask; static const uint32_t NonAllocatableMask = - (1 << X86Registers::xmm7); + (1 << X86Encoding::xmm7); // This is ScratchDoubleReg. static const uint32_t AllocatableMask = AllMask & ~NonAllocatableMask; }; @@ -185,10 +181,10 @@ bool volatile_() const { return !!((1 << code()) & FloatRegisters::VolatileMask); } - bool operator != (FloatRegister other) const { + bool operator !=(FloatRegister other) const { return other.code_ != code_; } - bool operator == (FloatRegister other) const { + bool operator ==(FloatRegister other) const { return other.code_ == code_; } bool aliases(FloatRegister other) const { @@ -225,8 +221,6 @@ static uint32_t GetSizeInBytes(const TypedRegisterSet &s); static uint32_t GetPushSizeInBytes(const TypedRegisterSet &s); uint32_t getRegisterDumpOffsetInBytes(); - - }; // Arm/D32 has double registers that can NOT be treated as float32 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x86/Assembler-x86.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x86/Assembler-x86.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x86/Assembler-x86.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x86/Assembler-x86.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -58,7 +58,7 @@ for (size_t i = 0; i < jumps_.length(); i++) { RelativePatch &rp = jumps_[i]; - X86Assembler::setRel32(buffer + rp.offset, rp.target); + X86Encoding::SetRel32(buffer + rp.offset, rp.target); } } @@ -87,7 +87,7 @@ static inline JitCode * CodeFromJump(uint8_t *jump) { - uint8_t *target = (uint8_t *)X86Assembler::getRel32Target(jump); + uint8_t *target = (uint8_t *)X86Encoding::GetRel32Target(jump); return JitCode::FromExecutable(target); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x86/Assembler-x86.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x86/Assembler-x86.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x86/Assembler-x86.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x86/Assembler-x86.h 2015-02-03 14:33:32.000000000 +0000 @@ -13,31 +13,31 @@ #include "jit/IonCode.h" #include "jit/JitCompartment.h" #include "jit/shared/Assembler-shared.h" -#include "jit/shared/BaseAssembler-x86-shared.h" +#include "jit/shared/Constants-x86-shared.h" namespace js { namespace jit { -static MOZ_CONSTEXPR_VAR Register eax = { X86Registers::eax }; -static MOZ_CONSTEXPR_VAR Register ecx = { X86Registers::ecx }; -static MOZ_CONSTEXPR_VAR Register edx = { X86Registers::edx }; -static MOZ_CONSTEXPR_VAR Register ebx = { X86Registers::ebx }; -static MOZ_CONSTEXPR_VAR Register esp = { X86Registers::esp }; -static MOZ_CONSTEXPR_VAR Register ebp = { X86Registers::ebp }; -static MOZ_CONSTEXPR_VAR Register esi = { X86Registers::esi }; -static MOZ_CONSTEXPR_VAR Register edi = { X86Registers::edi }; - -static MOZ_CONSTEXPR_VAR FloatRegister xmm0 = { X86Registers::xmm0 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm1 = { X86Registers::xmm1 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm2 = { X86Registers::xmm2 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm3 = { X86Registers::xmm3 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm4 = { X86Registers::xmm4 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm5 = { X86Registers::xmm5 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm6 = { X86Registers::xmm6 }; -static MOZ_CONSTEXPR_VAR FloatRegister xmm7 = { X86Registers::xmm7 }; +static MOZ_CONSTEXPR_VAR Register eax = { X86Encoding::rax }; +static MOZ_CONSTEXPR_VAR Register ecx = { X86Encoding::rcx }; +static MOZ_CONSTEXPR_VAR Register edx = { X86Encoding::rdx }; +static MOZ_CONSTEXPR_VAR Register ebx = { X86Encoding::rbx }; +static MOZ_CONSTEXPR_VAR Register esp = { X86Encoding::rsp }; +static MOZ_CONSTEXPR_VAR Register ebp = { X86Encoding::rbp }; +static MOZ_CONSTEXPR_VAR Register esi = { X86Encoding::rsi }; +static MOZ_CONSTEXPR_VAR Register edi = { X86Encoding::rdi }; + +static MOZ_CONSTEXPR_VAR FloatRegister xmm0 = { X86Encoding::xmm0 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm1 = { X86Encoding::xmm1 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm2 = { X86Encoding::xmm2 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm3 = { X86Encoding::xmm3 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm4 = { X86Encoding::xmm4 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm5 = { X86Encoding::xmm5 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm6 = { X86Encoding::xmm6 }; +static MOZ_CONSTEXPR_VAR FloatRegister xmm7 = { X86Encoding::xmm7 }; -static MOZ_CONSTEXPR_VAR Register InvalidReg = { X86Registers::invalid_reg }; -static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg = { X86Registers::invalid_xmm }; +static MOZ_CONSTEXPR_VAR Register InvalidReg = { X86Encoding::invalid_reg }; +static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg = { X86Encoding::invalid_xmm }; static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = ecx; static MOZ_CONSTEXPR_VAR Register JSReturnReg_Data = edx; @@ -164,7 +164,7 @@ MOZ_ASSERT(((*x >= 0x80 && *x <= 0x8F) && *(x - 1) == 0x0F) || (*x == 0xE9)); #endif - X86Assembler::setRel32(jump.raw(), label.raw()); + X86Encoding::SetRel32(jump.raw(), label.raw()); } static inline void PatchBackedge(CodeLocationJump &jump_, CodeLocationLabel label, JitRuntime::BackedgeTarget target) @@ -381,7 +381,7 @@ } void j(Condition cond, ImmPtr target, Relocation::Kind reloc = Relocation::HARDCODED) { - JmpSrc src = masm.jCC(static_cast(cond)); + JmpSrc src = masm.jCC(static_cast(cond)); addPendingJump(src, target, reloc); } @@ -423,9 +423,9 @@ void retarget(Label *label, ImmPtr target, Relocation::Kind reloc) { if (label->used()) { bool more; - X86Assembler::JmpSrc jmp(label->offset()); + X86Encoding::JmpSrc jmp(label->offset()); do { - X86Assembler::JmpSrc next; + X86Encoding::JmpSrc next; more = masm.nextJump(jmp, &next); addPendingJump(jmp, target, reloc); jmp = next; @@ -630,7 +630,7 @@ } static bool canUseInSingleByteInstruction(Register reg) { - return !ByteRegRequiresRex(reg.code()); + return X86Encoding::HasSubregL(reg.code()); } }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x86/MacroAssembler-x86.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x86/MacroAssembler-x86.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x86/MacroAssembler-x86.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x86/MacroAssembler-x86.h 2015-02-03 14:33:32.000000000 +0000 @@ -550,6 +550,9 @@ void cmpPtr(const Address &lhs, const ImmPtr rhs) { cmpPtr(lhs, ImmWord(uintptr_t(rhs.value))); } + void cmpPtr(const Address &lhs, const ImmGCPtr rhs) { + cmpPtr(Operand(lhs), rhs); + } void cmpPtr(Register lhs, Register rhs) { cmp32(lhs, rhs); } @@ -1065,6 +1068,19 @@ void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T &dest, MIRType slotType); + template + void storeUnboxedPayload(ValueOperand value, T address, size_t nbytes) { + switch (nbytes) { + case 4: + storePtr(value.payloadReg(), address); + return; + case 1: + store8(value.payloadReg(), address); + return; + default: MOZ_CRASH("Bad payload width"); + } + } + void rshiftPtr(Imm32 imm, Register dest) { shrl(imm, dest); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x86/Trampoline-x86.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x86/Trampoline-x86.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit/x86/Trampoline-x86.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit/x86/Trampoline-x86.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -370,6 +370,9 @@ JitRuntime::generateArgumentsRectifier(JSContext *cx, void **returnAddrOut) { MacroAssembler masm(cx); + // Caller: + // [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- esp + // '-- #esi ---' // ArgumentsRectifierReg contains the |nargs| pushed onto the current frame. // Including |this|, there are (|nargs| + 1) arguments to copy. @@ -380,6 +383,22 @@ masm.mov(eax, ecx); masm.andl(Imm32(CalleeTokenMask), ecx); masm.movzwl(Operand(ecx, JSFunction::offsetOfNargs()), ecx); + + // The frame pointer and its padding are pushed on the stack. + // Including |this|, there are (|nformals| + 1) arguments to push to the + // stack. Then we push a JitFrameLayout. We compute the padding expressed + // in the number of extra |undefined| values to push on the stack. + static_assert(sizeof(JitFrameLayout) % JitStackAlignment == 0, + "No need to consider the JitFrameLayout for aligning the stack"); + static_assert((sizeof(Value) + 2 * sizeof(void *)) % JitStackAlignment == 0, + "No need to consider |this| and the frame pointer and its padding for aligning the stack"); + static_assert(JitStackAlignment % sizeof(Value) == 0, + "Ensure that we can pad the stack by pushing extra UndefinedValue"); + + const uint32_t alignment = JitStackAlignment / sizeof(Value); + MOZ_ASSERT(IsPowerOfTwo(alignment)); + masm.addl(Imm32(alignment - 1 /* for padding */), ecx); + masm.andl(Imm32(~(alignment - 1)), ecx); masm.subl(esi, ecx); // Copy the number of actual arguments. @@ -393,6 +412,17 @@ // BaselineJIT.cpp/InitFromBailout. Check for the |#if defined(JS_CODEGEN_X86)| portions. masm.push(FramePointer); masm.movl(esp, FramePointer); // Save %esp. + masm.push(FramePointer /* padding */); + + // Caller: + // [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] + // '-- #esi ---' + // + // Rectifier frame: + // [ebp'] <- ebp [padding] <- esp [undef] [undef] [arg2] [arg1] [this] + // '--- #ecx ----' '-- #esi ---' + // + // [[argc] [callee] [descr] [raddr]] // Push undefined. { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/jit_test.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/jit_test.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/jit_test.py 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/jit_test.py 2015-02-03 14:33:26.000000000 +0000 @@ -10,7 +10,7 @@ def add_libdir_to_path(): from os.path import dirname, exists, join, realpath js_src_dir = dirname(dirname(realpath(sys.argv[0]))) - assert exists(join(js_src_dir,'jsapi.h')) + assert exists(join(js_src_dir, 'jsapi.h')) sys.path.insert(0, join(js_src_dir, 'lib')) sys.path.insert(0, join(js_src_dir, 'tests', 'lib')) @@ -49,20 +49,26 @@ op.add_option('-s', '--show-cmd', dest='show_cmd', action='store_true', help='show js shell command run') op.add_option('-f', '--show-failed-cmd', dest='show_failed', - action='store_true', help='show command lines of failed tests') - op.add_option('-o', '--show-output', dest='show_output', action='store_true', + action='store_true', + help='show command lines of failed tests') + op.add_option('-o', '--show-output', dest='show_output', + action='store_true', help='show output from js shell') - op.add_option('-F', '--failed-only', dest='failed_only', action='store_true', - help="if --show-output is given, only print output for failed tests") - op.add_option('--no-show-failed', dest='no_show_failed', action='store_true', - help="don't print output for failed tests (no-op with --show-output)") + op.add_option('-F', '--failed-only', dest='failed_only', + action='store_true', + help="if --show-output is given, only print output for" + " failed tests") + op.add_option('--no-show-failed', dest='no_show_failed', + action='store_true', + help="don't print output for failed tests" + " (no-op with --show-output)") op.add_option('-x', '--exclude', dest='exclude', action='append', help='exclude given test dir or path') op.add_option('--slow', dest='run_slow', action='store_true', help='also run tests marked as slow') op.add_option('--no-slow', dest='run_slow', action='store_false', help='do not run tests marked as slow (the default)') - op.add_option('-t', '--timeout', dest='timeout', type=float, default=150.0, + op.add_option('-t', '--timeout', dest='timeout', type=float, default=150.0, help='set test timeout in seconds') op.add_option('--no-progress', dest='hide_progress', action='store_true', help='hide progress bar') @@ -70,7 +76,8 @@ help='Tinderbox-parseable output format') op.add_option('--args', dest='shell_args', default='', help='extra args to pass to the JS shell') - op.add_option('-w', '--write-failures', dest='write_failures', metavar='FILE', + op.add_option('-w', '--write-failures', dest='write_failures', + metavar='FILE', help='Write a list of failed tests to [FILE]') op.add_option('-r', '--read-tests', dest='read_tests', metavar='FILE', help='Run test files listed in [FILE]') @@ -83,17 +90,23 @@ op.add_option('--valgrind-all', dest='valgrind_all', action='store_true', help='Run all tests with valgrind, if valgrind is in $PATH.') op.add_option('--jitflags', dest='jitflags', default='', - help='Example: --jitflags=m,mn to run each test with "-m" and "-m -n" [default="%default"]. ' + - 'Long flags, such as "--ion-eager", should be set using --args.') + help='Example: --jitflags=m,mn to run each test with "-m"' + ' and "-m -n" [default="%default"]. Long flags, such as' + ' "--ion-eager", should be set using --args.') op.add_option('--avoid-stdio', dest='avoid_stdio', action='store_true', help='Use js-shell file indirection instead of piping stdio.') - op.add_option('--write-failure-output', dest='write_failure_output', action='store_true', - help='With --write-failures=FILE, additionally write the output of failed tests to [FILE]') + op.add_option('--write-failure-output', dest='write_failure_output', + action='store_true', + help='With --write-failures=FILE, additionally write the' + ' output of failed tests to [FILE]') op.add_option('--ion', dest='ion', action='store_true', - help='Run tests once with --ion-eager and once with --baseline-eager (ignores --jitflags)') + help='Run tests once with --ion-eager and once with' + ' --baseline-eager (ignores --jitflags)') op.add_option('--tbpl', dest='tbpl', action='store_true', - help='Run tests with all IonMonkey option combinations (ignores --jitflags)') - op.add_option('-j', '--worker-count', dest='max_jobs', type=int, default=max_jobs_default, + help='Run tests with all IonMonkey option combinations' + ' (ignores --jitflags)') + op.add_option('-j', '--worker-count', dest='max_jobs', type=int, + default=max_jobs_default, help='Number of tests to run in parallel (default %default)') op.add_option('--remote', action='store_true', help='Run tests on a remote device') @@ -108,13 +121,16 @@ help='ADB device serial number of remote device to test') op.add_option('--deviceTransport', action='store', type='string', dest='device_transport', default='sut', - help='The transport to use to communicate with device: [adb|sut]; default=sut') + help='The transport to use to communicate with device:' + ' [adb|sut]; default=sut') op.add_option('--remoteTestRoot', dest='remote_test_root', action='store', type='string', default='/data/local/tests', - help='The remote directory to use as test root (eg. /data/local/tests)') + help='The remote directory to use as test root' + ' (eg. /data/local/tests)') op.add_option('--localLib', dest='local_lib', action='store', type='string', - help='The location of libraries to push -- preferably stripped') + help='The location of libraries to push -- preferably' + ' stripped') op.add_option('--repeat', type=int, default=1, help='Repeat tests the given number of times.') op.add_option('--this-chunk', type=int, default=1, @@ -160,14 +176,15 @@ try: f = open(options.read_tests) for line in f: - test_list.append(os.path.join(jittests.TEST_DIR, line.strip('\n'))) + test_list.append(os.path.join(jittests.TEST_DIR, + line.strip('\n'))) f.close() except IOError: if options.retest: read_all = True else: - sys.stderr.write("Exception thrown trying to read test file '%s'\n"% - options.read_tests) + sys.stderr.write("Exception thrown trying to read test file" + " '{}'\n".format(options.read_tests)) traceback.print_exc() sys.stderr.write('---\n') @@ -178,16 +195,18 @@ exclude_list = [] for exclude in options.exclude: exclude_list += jittests.find_tests(exclude) - test_list = [ test for test in test_list if test not in set(exclude_list) ] + test_list = [test for test in test_list + if test not in set(exclude_list)] if not test_list: - print("No tests found matching command line arguments.", file=sys.stderr) + print("No tests found matching command line arguments.", + file=sys.stderr) sys.exit(0) test_list = [jittests.Test.from_file(_, options) for _ in test_list] if not options.run_slow: - test_list = [ _ for _ in test_list if not _.slow ] + test_list = [_ for _ in test_list if not _.slow] # If chunking is enabled, determine which tests are part of this chunk. # This code was adapted from testing/mochitest/runtestsremote.py. @@ -202,20 +221,24 @@ job_list = [] test_flags = [] if options.tbpl: - # Running all bits would take forever. Instead, we test a few interesting combinations. + # Running all bits would take forever. Instead, we test a few + # interesting combinations. test_flags = TBPL_FLAGS elif options.ion: - test_flags = [['--baseline-eager'], ['--ion-eager', '--ion-offthread-compile=off']] + test_flags = [['--baseline-eager'], + ['--ion-eager', '--ion-offthread-compile=off']] else: test_flags = jittests.parse_jitflags(options) - job_list = [ _ for test in test_list for _ in test.copy_variants(test_flags) ] + job_list = [_ for test in test_list + for _ in test.copy_variants(test_flags)] if options.ignore_timeouts: read_all = False try: with open(options.ignore_timeouts) as f: - options.ignore_timeouts = set([line.strip('\n') for line in f.readlines()]) + options.ignore_timeouts = set( + [line.strip('\n') for line in f.readlines()]) except IOError: sys.exit("Error reading file: " + options.ignore_timeouts) else: @@ -224,7 +247,8 @@ prefix = [which(args[0])] + shlex.split(options.shell_args) prolog = os.path.join(jittests.LIB_DIR, 'prolog.js') if options.remote: - prolog = posixpath.join(options.remote_test_root, 'jit-tests', 'jit-tests', 'lib', 'prolog.js') + prolog = posixpath.join(options.remote_test_root, + 'jit-tests', 'jit-tests', 'lib', 'prolog.js') prefix += ['-f', prolog] @@ -234,9 +258,10 @@ if options.debug: if len(job_list) > 1: - print('Multiple tests match command line arguments, debugger can only run one') + print('Multiple tests match command line' + ' arguments, debugger can only run one') for tc in job_list: - print(' %s' % tc.path) + print(' {}'.format(tc.path)) sys.exit(1) tc = job_list[0] @@ -256,7 +281,8 @@ sys.exit(2) except OSError: if not os.path.exists(prefix[0]): - print("JS shell argument: file does not exist: '%s'" % prefix[0], file=sys.stderr) + print("JS shell argument: file does not exist:" + " '{}'".format(prefix[0]), file=sys.stderr) sys.exit(1) else: raise diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/lib/iteration.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/lib/iteration.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/lib/iteration.js 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/lib/iteration.js 2015-02-03 14:33:26.000000000 +0000 @@ -5,9 +5,6 @@ load(libdir + "asserts.js"); -const JS_HAS_SYMBOLS = typeof Symbol === "function"; -const std_iterator = JS_HAS_SYMBOLS ? Symbol.iterator : '@@iterator'; - if (typeof assertIteratorResult === 'undefined') { var assertIteratorResult = function assertIteratorResult(result, value, done) { assertEq(typeof result, "object"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/arguments/destructuring-exprbody.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/arguments/destructuring-exprbody.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/arguments/destructuring-exprbody.js 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/arguments/destructuring-exprbody.js 2015-02-03 14:33:26.000000000 +0000 @@ -2,7 +2,7 @@ load(libdir + "iteration.js"); function f([a]) a var i = 0; -var o = {[std_iterator]: function () { i++; return { +var o = {[Symbol.iterator]: function () { i++; return { next: function () { i++; return {value: 42, done: false}; }}}}; assertEq(f(o), 42); assertEq(i, 2); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/asm.js/bug1122338.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/asm.js/bug1122338.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/asm.js/bug1122338.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/asm.js/bug1122338.js 2015-02-03 14:33:26.000000000 +0000 @@ -0,0 +1,41 @@ +// The bug was that the asm.js linker did not catch that an unshared +// view was created on a shared buffer. (Nor did it catch the vice +// versa case.) As a consequence the code was not rejected (and run +// as plain JS) as it should be. That gave rise to a difference in +// output. + +if (!this.SharedArrayBuffer) + quit(0); + +// Original test + +g = (function(stdlib, n, heap) { + "use asm"; + var Float32ArrayView = new stdlib.Float32Array(heap); + function f() { + return +Float32ArrayView[0] + } + return f +})(this, {}, new SharedArrayBuffer(4096)); +assertEq(g(), NaN); + +// Additional test: vice versa case. + +try { + g = (function(stdlib, n, heap) { + "use asm"; + var Float32ArrayView = new stdlib.SharedFloat32Array(heap); + function f() { + return +Float32ArrayView[0] + } + return f + })(this, {}, new ArrayBuffer(4096)); + // If the code runs, as it would with the bug present, it must return NaN. + assertEq(g(), NaN); +} +catch (e) { + // If the code throws then that's OK too. The current (January 2015) + // semantics is for the SharedFloat32Array constructor to throw if + // handed an ArrayBuffer, but asm.js does not execute that constructor + // and before the fix it would not throw. +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/asm.js/bug1126251.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/asm.js/bug1126251.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/asm.js/bug1126251.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/asm.js/bug1126251.js 2015-02-03 14:33:26.000000000 +0000 @@ -0,0 +1,36 @@ +load(libdir + "asm.js"); + +var v = asmLink(asmCompile('global', ` + "use asm"; + var frd = global.Math.fround; + function e() { + var x = frd(.1e+71); + x = frd(x / x); + return +x; + } + return e; +`), this)(); + +assertEq(v, NaN); + +if (!isSimdAvailable() || typeof SIMD === 'undefined') { + quit(0); +} + +var v = asmLink(asmCompile('global', ` + "use asm"; + var frd = global.Math.fround; + var float32x4 = global.SIMD.float32x4; + var splat = float32x4.splat; + function e() { + var v = float32x4(0,0,0,0); + var x = frd(0.); + v = splat(.1e+71); + x = v.x; + x = frd(x / x); + return +x; + } + return e; +`), this)(); + +assertEq(v, NaN); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/asm.js/testProfiling.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/asm.js/testProfiling.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/asm.js/testProfiling.js 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/asm.js/testProfiling.js 2015-02-03 14:33:26.000000000 +0000 @@ -56,10 +56,10 @@ var ffi = function(enable) { if (enable == +1) enableSPSProfiling(); - if (enable == -1) - disableSPSProfiling(); enableSingleStepProfiling(); stacks = disableSingleStepProfiling(); + if (enable == -1) + disableSPSProfiling(); } var f = asmLink(asmCompile('global','ffis',USE_ASM + "var ffi=ffis.ffi; function g(i) { i=i|0; ffi(i|0) } function f(i) { i=i|0; g(i|0) } return f"), null, {ffi}); f(0); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/bug770952.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/bug770952.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/bug770952.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/bug770952.js 2015-02-03 14:33:28.000000000 +0000 @@ -2,6 +2,6 @@ load(libdir + "iteration.js"); eval("var x; typeof x") -Array.prototype[std_iterator] = function () { for(y in x); }; +Array.prototype[Symbol.iterator] = function () { for(y in x); }; for (var v of ['a', 'b', 'c', 'd']) s = v; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/destructuring-iterator.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/destructuring-iterator.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/destructuring-iterator.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/destructuring-iterator.js 2015-02-03 14:33:28.000000000 +0000 @@ -9,7 +9,7 @@ var nextcalls = 0, donecalls = 0, valuecalls = 0; var doneafter = 0; var iterable = {}; -iterable[std_iterator] = function () { +iterable[Symbol.iterator] = function () { return { next: function () { assertEq(arguments.length, 0, 'iterator.next() should be called with no arguments'); @@ -50,8 +50,8 @@ [3,4]); var arraycalls = 0; -var ArrayIterator = Array.prototype[std_iterator]; -Array.prototype[std_iterator] = function () { +var ArrayIterator = Array.prototype[Symbol.iterator]; +Array.prototype[Symbol.iterator] = function () { arraycalls++; return ArrayIterator.apply(this, arguments); }; @@ -72,14 +72,14 @@ // destructuring assignment should always use iterators and not optimize // to a "group assignment" -delete Array.prototype[std_iterator]; +delete Array.prototype[Symbol.iterator]; assertThrowsInstanceOf(() => { var [a,b] = [1,2]; }, TypeError); -Array.prototype[std_iterator] = ArrayIterator; +Array.prototype[Symbol.iterator] = ArrayIterator; // observe the binding order a = undefined, b = undefined, c = undefined; var obj = {}; -obj[std_iterator] = function* () { +obj[Symbol.iterator] = function* () { // normal fields should be initialized right after |.next()| yield 1; assertEq(a, 1); @@ -99,11 +99,11 @@ assertThrowsValue(function () { try { - Array.prototype[std_iterator] = function () { throw 'from iterator'; }; + Array.prototype[Symbol.iterator] = function () { throw 'from iterator'; }; throw [1, 2]; } catch ([x, y]) { throw 'not reached'; } }, 'from iterator'); -Array.prototype[std_iterator] = ArrayIterator; +Array.prototype[Symbol.iterator] = ArrayIterator; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/expression-autopsy.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/expression-autopsy.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/expression-autopsy.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/expression-autopsy.js 2015-02-03 14:33:28.000000000 +0000 @@ -110,10 +110,9 @@ // A few one off tests check_one("6", (function () { 6() }), " is not a function"); check_one("Array.prototype.reverse.call(...)", (function () { Array.prototype.reverse.call('123'); }), " is read-only"); -var ITERATOR = JS_HAS_SYMBOLS ? "Symbol.iterator" : "'@@iterator'"; -check_one(`(intermediate value)[${ITERATOR}](...).next(...).value`, +check_one(`(intermediate value)[Symbol.iterator](...).next(...).value`, function () { var [{ x }] = [null, {}]; }, " is null"); -check_one(`(intermediate value)[${ITERATOR}](...).next(...).value`, +check_one(`(intermediate value)[Symbol.iterator](...).next(...).value`, function () { ieval("let (x) { var [a, b, [c0, c1]] = [x, x, x]; }") }, " is undefined"); // Check fallback behavior diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/hypot-approx.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/hypot-approx.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/hypot-approx.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/hypot-approx.js 2015-02-03 14:33:28.000000000 +0000 @@ -21,5 +21,14 @@ assertNear(Math.hypot(1e-300, 1e300), 1e300); assertNear(Math.hypot(1e3, 1e-3, 1e3, 1e-3), 1414.2135623738021555); -for (var i = 1, j = 1; i < 2; i += 0.05, j += 0.05) +assertNear(Math.hypot(1e1, 1e2, 1e3), Math.sqrt(1e2 + 1e4 + 1e6)); +assertNear(Math.hypot(1e1, 1e2, 1e3, 1e4), Math.sqrt(1e2 + 1e4 + 1e6 + 1e8)); + +for (var i = 1, j = 2; i < 2; i += 0.05, j += 0.05) assertNear(Math.hypot(i, j), Math.sqrt(i * i + j * j)); + +for (var i = 1, j = 2, k = 3; i < 2; i += 0.05, j += 0.05, k += 0.05) + assertNear(Math.hypot(i, j, k), Math.sqrt(i * i + j * j + k * k)); + +for (var i = 1, j = 2, k = 3, l = 4; i < 2; i += 0.05, j += 0.05, k += 0.05, l += 0.5) + assertNear(Math.hypot(i, j, k, l), Math.sqrt(i * i + j * j + k * k + l * l)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/hypot-exact.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/hypot-exact.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/hypot-exact.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/hypot-exact.js 2015-02-03 14:33:28.000000000 +0000 @@ -28,6 +28,11 @@ assertEq(Math.hypot(inf, NaN, NaN), Infinity); assertEq(Math.hypot(NaN, inf, NaN), Infinity); assertEq(Math.hypot(NaN, NaN, inf), Infinity); + + assertEq(Math.hypot(inf, NaN, NaN, NaN), Infinity); + assertEq(Math.hypot(NaN, inf, NaN, NaN), Infinity); + assertEq(Math.hypot(NaN, NaN, inf, NaN), Infinity); + assertEq(Math.hypot(NaN, NaN, NaN, inf), Infinity); } // If no argument is +∞ or −∞, and any argument is NaN, the result is NaN. @@ -42,7 +47,13 @@ assertEq(Math.hypot(0, NaN, 0), NaN); assertEq(Math.hypot(0, 0, NaN), NaN); +assertEq(Math.hypot(NaN, 0, 0, 0), NaN); +assertEq(Math.hypot(0, NaN, 0, 0), NaN); +assertEq(Math.hypot(0, 0, NaN, 0), NaN); +assertEq(Math.hypot(0, 0, 0, NaN), NaN); + assertEq(Math.hypot(Number.MAX_VALUE, Number.MIN_VALUE, NaN), NaN); +assertEq(Math.hypot(Number.MAX_VALUE, Number.MIN_VALUE, Number.MIN_VALUE, NaN), NaN); // If all arguments are either +0 or -0, the result is +0. assertEq(Math.hypot(-0, -0), +0); @@ -53,5 +64,11 @@ assertEq(Math.hypot(-0, +0, -0), +0); assertEq(Math.hypot(+0, +0, -0), +0); +assertEq(Math.hypot(-0, -0, -0, -0), +0); +assertEq(Math.hypot(+0, -0, -0, -0), +0); +assertEq(Math.hypot(-0, -0, +0, -0), +0); +assertEq(Math.hypot(+0, +0, +0, -0), +0); +assertEq(Math.hypot(-0, -0, -0, +0), +0); + // The length property of the hypot function is 2. assertEq(Math.hypot.length, 2); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/mutable-proto-teleporting.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/mutable-proto-teleporting.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/mutable-proto-teleporting.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/mutable-proto-teleporting.js 2015-02-03 14:33:28.000000000 +0000 @@ -0,0 +1,18 @@ +// The teleporting optimization should work correctly +// when we modify an object's proto. + +var A = {x: 1}; +var B = Object.create(A); + +var C = {}; +C.__proto__ = B; + +function f() { + for (var i=0; i<25; i++) { + assertEq(C.x, (i <= 20) ? 1 : 3); + if (i === 20) { + B.x = 3; + } + } +} +f(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/spread-array.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/spread-array.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/spread-array.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/spread-array.js 2015-02-03 14:33:28.000000000 +0000 @@ -18,11 +18,11 @@ // other iterable objects assertEqArray([...new Int32Array([1, 2, 3])], [1, 2, 3]); assertEqArray([..."abc"], ["a", "b", "c"]); -assertEqArray([...[1, 2, 3][std_iterator]()], [1, 2, 3]); +assertEqArray([...[1, 2, 3][Symbol.iterator]()], [1, 2, 3]); assertEqArray([...Set([1, 2, 3])], [1, 2, 3]); assertEqArray([...Map([["a", "A"], ["b", "B"], ["c", "C"]])].map(([k, v]) => k + v), ["aA", "bB", "cC"]); let itr = {}; -itr[std_iterator] = function () { +itr[Symbol.iterator] = function () { return { i: 1, next: function() { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/spread-call-eval.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/spread-call-eval.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/spread-call-eval.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/spread-call-eval.js 2015-02-03 14:33:28.000000000 +0000 @@ -24,10 +24,10 @@ } // other iterable objects -assertEq(eval(...["a + b"][std_iterator]()), 11); +assertEq(eval(...["a + b"][Symbol.iterator]()), 11); assertEq(eval(...Set(["a + b"])), 11); let itr = {}; -itr[std_iterator] = function() { +itr[Symbol.iterator] = function() { return { i: 0, next: function() { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/spread-call-funapply.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/spread-call-funapply.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/spread-call-funapply.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/spread-call-funapply.js 2015-02-03 14:33:28.000000000 +0000 @@ -10,9 +10,9 @@ // other iterable objects assertEqArray(f.apply(...Set([null, [1, 2, 3]])), [1, 2, 3]); - assertEqArray(f.apply(...[null, [1, 2, 3]][std_iterator]()), [1, 2, 3]); + assertEqArray(f.apply(...[null, [1, 2, 3]][Symbol.iterator]()), [1, 2, 3]); let itr = {}; - itr[std_iterator] = function() { + itr[Symbol.iterator] = function() { return { i: 0, next: function() { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/spread-call.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/spread-call.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/spread-call.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/spread-call.js 2015-02-03 14:33:28.000000000 +0000 @@ -16,11 +16,11 @@ // other iterable objects assertEqArray(makeFn("...arg")(f, new Int32Array([1, 2, 3])), [1, 2, 3]); assertEqArray(makeFn("...arg")(f, "abc"), ["a", "b", "c"]); - assertEqArray(makeFn("...arg")(f, [1, 2, 3][std_iterator]()), [1, 2, 3]); + assertEqArray(makeFn("...arg")(f, [1, 2, 3][Symbol.iterator]()), [1, 2, 3]); assertEqArray(makeFn("...arg")(f, Set([1, 2, 3])), [1, 2, 3]); assertEqArray(makeFn("...arg")(f, Map([["a", "A"], ["b", "B"], ["c", "C"]])).map(([k, v]) => k + v), ["aA", "bB", "cC"]); let itr = {}; - itr[std_iterator] = function() { + itr[Symbol.iterator] = function() { return { i: 1, next: function() { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/spread-call-length.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/spread-call-length.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/spread-call-length.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/spread-call-length.js 2015-02-03 14:33:28.000000000 +0000 @@ -21,11 +21,11 @@ // other iterable objects assertEq(makeFn("...arg")(f, new Int32Array([1, 2, 3])), 3); assertEq(makeFn("...arg")(f, "abc"), 3); - assertEq(makeFn("...arg")(f, [1, 2, 3][std_iterator]()), 3); + assertEq(makeFn("...arg")(f, [1, 2, 3][Symbol.iterator]()), 3); assertEq(makeFn("...arg")(f, Set([1, 2, 3])), 3); assertEq(makeFn("...arg")(f, Map([["a", "A"], ["b", "B"], ["c", "C"]])), 3); let itr = {}; - itr[std_iterator] = function() { + itr[Symbol.iterator] = function() { return { i: 1, next: function() { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/spread-call-not-iterable.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/spread-call-not-iterable.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/spread-call-not-iterable.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/spread-call-not-iterable.js 2015-02-03 14:33:28.000000000 +0000 @@ -11,16 +11,16 @@ assertThrowsInstanceOf(() => Math.sin(...{}), TypeError); var foo = {} -foo[std_iterator] = 10; +foo[Symbol.iterator] = 10; assertThrowsInstanceOf(() => Math.sin(...foo), TypeError); -foo[std_iterator] = function() undefined; +foo[Symbol.iterator] = function() undefined; assertThrowsInstanceOf(() => Math.sin(...foo), TypeError); -foo[std_iterator] = function() this; +foo[Symbol.iterator] = function() this; assertThrowsInstanceOf(() => Math.sin(...foo), TypeError); -foo[std_iterator] = function() this; +foo[Symbol.iterator] = function() this; foo.next = function() { throw 10; }; assertThrowsValue(() => Math.sin(...foo), 10); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/syntax-error-illegal-character.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/syntax-error-illegal-character.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/syntax-error-illegal-character.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/syntax-error-illegal-character.js 2015-02-03 14:33:28.000000000 +0000 @@ -660,6 +660,20 @@ test("({ *[m]() {} @"); test("({ *[m]() {}, @"); +test("({ * get @"); +test("({ * get ( @"); +test("({ * get () @"); +test("({ * get () { @"); +test("({ * get () {} @"); +test("({ * get () {}, @"); + +test("({ * set @"); +test("({ * set ( @"); +test("({ * set () @"); +test("({ * set () { @"); +test("({ * set () {} @"); +test("({ * set () {}, @"); + // Regular expression literal test("/a/ @"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/teleporting-mutable-proto.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/teleporting-mutable-proto.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/teleporting-mutable-proto.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/teleporting-mutable-proto.js 2015-02-03 14:33:28.000000000 +0000 @@ -0,0 +1,14 @@ +var A = {x: 3}; +var B = Object.create(A); +var C = Object.create(B); +var D = Object.create(C); + +function f() { + for (var i=0; i<30; i++) { + assertEq(D.x, (i <= 20) ? 3 : 10); + if (i === 20) { + C.__proto__ = {x: 10}; + } + } +} +f(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/testBug1126754.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/testBug1126754.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/testBug1126754.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/testBug1126754.js 2015-02-03 14:33:28.000000000 +0000 @@ -0,0 +1,9 @@ +// |jit-test| error:SyntaxError + +(function() { + with ({}) {} + function f() { + ({ *h(){} }) + } + function f +})() diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/typed-array-sealed-frozen.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/typed-array-sealed-frozen.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/typed-array-sealed-frozen.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/typed-array-sealed-frozen.js 2015-02-03 14:33:28.000000000 +0000 @@ -72,7 +72,7 @@ // Don't define old properties. Object.defineProperty(a, 5, {value: 3}); - assertEq(a[5], 0); + assertEq(a[5], 3); // Don't define new properties. Object.defineProperty(a, 20, {value: 3}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/unboxed-object-clear-new-script.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/unboxed-object-clear-new-script.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/unboxed-object-clear-new-script.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/unboxed-object-clear-new-script.js 2015-02-03 14:33:28.000000000 +0000 @@ -0,0 +1,49 @@ + +function Foo(a, b) { + this.a = a; + this.b = b; +} + +function invalidate_foo() { + var a = []; + var counter = 0; + for (var i = 0; i < 50; i++) + a.push(new Foo(i, i + 1)); + Object.defineProperty(Foo.prototype, "a", {configurable: true, set: function() { counter++; }}); + for (var i = 0; i < 50; i++) + a.push(new Foo(i, i + 1)); + delete Foo.prototype.a; + var total = 0; + for (var i = 0; i < a.length; i++) { + assertEq('a' in a[i], i < 50); + total += a[i].b; + } + assertEq(total, 2550); + assertEq(counter, 50); +} +invalidate_foo(); + +function Bar(a, b, fn) { + this.a = a; + if (b == 30) + Object.defineProperty(Bar.prototype, "b", {configurable: true, set: fn}); + this.b = b; +} + +function invalidate_bar() { + var a = []; + var counter = 0; + function fn() { counter++; } + for (var i = 0; i < 50; i++) + a.push(new Bar(i, i + 1, fn)); + delete Bar.prototype.b; + var total = 0; + for (var i = 0; i < a.length; i++) { + assertEq('a' in a[i], true); + assertEq('b' in a[i], i < 29); + total += a[i].a; + } + assertEq(total, 1225); + assertEq(counter, 21); +} +invalidate_bar(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/unboxed-object-convert-to-native.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/unboxed-object-convert-to-native.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/basic/unboxed-object-convert-to-native.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/basic/unboxed-object-convert-to-native.js 2015-02-03 14:33:28.000000000 +0000 @@ -0,0 +1,47 @@ + +// Test various ways of converting an unboxed object to native. + +function Foo(a, b) { + this.a = a; + this.b = b; +} + +var proxyObj = { + get: function(recipient, name) { + return recipient[name] + 2; + } +}; + +function f() { + var a = []; + for (var i = 0; i < 50; i++) + a.push(new Foo(i, i + 1)); + + var prop = "a"; + + i = 0; + for (; i < 5; i++) + a[i].c = i; + for (; i < 10; i++) + Object.defineProperty(a[i], 'c', {value: i}); + for (; i < 15; i++) + a[i] = new Proxy(a[i], proxyObj); + for (; i < 20; i++) + a[i].a = 3.5; + for (; i < 25; i++) + delete a[i].b; + for (; i < 30; i++) + a[prop] = 4; + + var total = 0; + for (i = 0; i < a.length; i++) { + if ('a' in a[i]) + total += a[i].a; + if ('b' in a[i]) + total += a[i].b; + if ('c' in a[i]) + total += a[i].c; + } + assertEq(total, 2382.5); +} +f(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/iterator-1.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/iterator-1.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/iterator-1.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/iterator-1.js 2015-02-03 14:33:28.000000000 +0000 @@ -3,7 +3,7 @@ load(libdir + "iteration.js"); function test(obj, name) { - var iter = obj[std_iterator](); + var iter = obj[Symbol.iterator](); assertEq(typeof iter, "object"); assertEq(iter instanceof Iterator, true); assertEq(iter.toString(), "[object " + obj.constructor.name + " Iterator]"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/iterator-gc.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/iterator-gc.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/iterator-gc.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/iterator-gc.js 2015-02-03 14:33:28.000000000 +0000 @@ -6,7 +6,7 @@ var key = {}; function test(obj, edgeName) { - var iter = obj[std_iterator](); + var iter = obj[Symbol.iterator](); referencesVia(iter, "**UNKNOWN SLOT 0**", obj); referencesVia(obj, edgeName, key); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/iterator-proto-1.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/iterator-proto-1.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/iterator-proto-1.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/iterator-proto-1.js 2015-02-03 14:33:28.000000000 +0000 @@ -4,7 +4,7 @@ load(libdir + "iteration.js"); function test(obj0, obj1) { - var iter0 = obj0[std_iterator](), iter1 = obj1[std_iterator](); + var iter0 = obj0[Symbol.iterator](), iter1 = obj1[Symbol.iterator](); var proto = Object.getPrototypeOf(iter0); assertEq(Object.getPrototypeOf(iter1), proto); assertEq(Object.getPrototypeOf(proto), Iterator.prototype); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/iterator-proto-2.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/iterator-proto-2.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/iterator-proto-2.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/iterator-proto-2.js 2015-02-03 14:33:28.000000000 +0000 @@ -2,9 +2,9 @@ load(libdir + "iteration.js"); -var aproto = Object.getPrototypeOf(Array()[std_iterator]()); -var mproto = Object.getPrototypeOf(Map()[std_iterator]()); -var sproto = Object.getPrototypeOf(Set()[std_iterator]()); +var aproto = Object.getPrototypeOf(Array()[Symbol.iterator]()); +var mproto = Object.getPrototypeOf(Map()[Symbol.iterator]()); +var sproto = Object.getPrototypeOf(Set()[Symbol.iterator]()); assertEq(aproto !== mproto, true); assertEq(aproto !== sproto, true); assertEq(mproto !== sproto, true); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/iterator-proto-surfaces.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/iterator-proto-surfaces.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/iterator-proto-surfaces.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/iterator-proto-surfaces.js 2015-02-03 14:33:28.000000000 +0000 @@ -4,18 +4,18 @@ load(libdir + "iteration.js"); function test(constructor) { - var proto = Object.getPrototypeOf(constructor()[std_iterator]()); + var proto = Object.getPrototypeOf(constructor()[Symbol.iterator]()); var names = Object.getOwnPropertyNames(proto); names.sort(); - assertDeepEq(names, JS_HAS_SYMBOLS ? ['next'] : ['@@iterator', 'next']); - assertEq(proto.hasOwnProperty(std_iterator), true); + assertDeepEq(names, ['next']); + assertEq(proto.hasOwnProperty(Symbol.iterator), true); var desc = Object.getOwnPropertyDescriptor(proto, 'next'); assertEq(desc.configurable, true); assertEq(desc.enumerable, false); assertEq(desc.writable, true); - assertEq(proto[std_iterator](), proto); + assertEq(proto[Symbol.iterator](), proto); assertIteratorDone(proto, undefined); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-clear-iterators-1.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-clear-iterators-1.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-clear-iterators-1.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-clear-iterators-1.js 2015-02-03 14:33:28.000000000 +0000 @@ -3,12 +3,12 @@ load(libdir + "iteration.js"); var m = Map(); -var it = m[std_iterator](); +var it = m[Symbol.iterator](); m.clear(); assertIteratorDone(it, undefined); m = Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]); -it = m[std_iterator](); +it = m[Symbol.iterator](); assertIteratorNext(it, ["a", 1]); m.clear(); assertIteratorDone(it, undefined); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-clear-iterators-2.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-clear-iterators-2.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-clear-iterators-2.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-clear-iterators-2.js 2015-02-03 14:33:28.000000000 +0000 @@ -4,7 +4,7 @@ load(libdir + "iteration.js"); var m = Map([["a", 1]]); -var it = m[std_iterator](); +var it = m[Symbol.iterator](); assertIteratorNext(it, ["a", 1]); m.clear(); m.set("b", 2); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-constructor-set.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-constructor-set.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-constructor-set.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-constructor-set.js 2015-02-03 14:33:28.000000000 +0000 @@ -106,7 +106,7 @@ var proxy_arr = new Proxy(arr, { get: function(target, name) { - if (name == std_iterator) { + if (name == Symbol.iterator) { modified = true; Map.prototype.set = function() { called = true; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-forEach.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-forEach.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-forEach.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-forEach.js 2015-02-03 14:33:28.000000000 +0000 @@ -17,7 +17,7 @@ initialMap.forEach(callback); // test that both the Maps are equal and are in same order -var iterator = initialMap[std_iterator](); +var iterator = initialMap[Symbol.iterator](); var count = 0; for (var [k, v] of testMap) { assertEq(initialMap.has(k), true); @@ -53,7 +53,7 @@ // StopIteration exception is thrown var m = new Map([["one", 1]]); -Object.getPrototypeOf(m[std_iterator]()).next = function () { throw "FAIL"; }; +Object.getPrototypeOf(m[Symbol.iterator]()).next = function () { throw "FAIL"; }; assertThrowsInstanceOf(function () { m.forEach(function () { throw StopIteration; }); }, StopIteration, "Map.prototype.forEach should use intrinsic next method."); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-add-2.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-add-2.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-add-2.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-add-2.js 2015-02-03 14:33:28.000000000 +0000 @@ -3,7 +3,7 @@ load(libdir + "iteration.js"); var map = Map(); -var iter0 = map[std_iterator](), iter1 = map[std_iterator](); +var iter0 = map[Symbol.iterator](), iter1 = map[Symbol.iterator](); assertIteratorDone(iter0, undefined); // closes iter0 map.set(1, 2); assertIteratorDone(iter0, undefined); // already closed diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-pairs-1.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-pairs-1.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-pairs-1.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-pairs-1.js 2015-02-03 14:33:28.000000000 +0000 @@ -4,7 +4,7 @@ var key = {}; var map = Map([[key, 'value']]); -var entry = map[std_iterator]().next().value; +var entry = map[Symbol.iterator]().next().value; assertEq(Array.isArray(entry), true); assertEq(Object.getPrototypeOf(entry), Array.prototype); assertEq(Object.isExtensible(entry), true); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-pairs-2.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-pairs-2.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-pairs-2.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-pairs-2.js 2015-02-03 14:33:28.000000000 +0000 @@ -3,11 +3,11 @@ load(libdir + "iteration.js"); var map = Map([['a', 1], ['b', 2]]); -var iter = map[std_iterator](); +var iter = map[Symbol.iterator](); var a = iter.next(), b = iter.next(); assertIteratorResult(a, ['a', 1], false); assertIteratorResult(b, ['b', 2], false); assertEq(a.value !== b.value, true); -var a1 = map[std_iterator](); +var a1 = map[Symbol.iterator](); assertIteratorNext(a1, ['a', 1]); assertEq(a.value !== a1.value, true); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-pairs-3.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-pairs-3.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-pairs-3.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-pairs-3.js 2015-02-03 14:33:28.000000000 +0000 @@ -3,7 +3,7 @@ load(libdir + "iteration.js"); var map = Map([['a', 1]]); -var res = map[std_iterator]().next(); +var res = map[Symbol.iterator]().next(); assertIteratorResult(res, ['a', 1], false); res.value[0] = 'b'; res.value[1] = 2; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-proxies-2.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-proxies-2.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-proxies-2.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-proxies-2.js 2015-02-03 14:33:28.000000000 +0000 @@ -6,16 +6,16 @@ var g = newGlobal(); -var iterator_fn = Map.prototype[std_iterator]; +var iterator_fn = Map.prototype[Symbol.iterator]; assertThrowsInstanceOf(function () { iterator_fn.call({}); }, TypeError); assertThrowsInstanceOf(function () { iterator_fn.call(Set()); }, TypeError); var mapw = g.eval("Map([['x', 1], ['y', 2]])"); assertEqArray(iterator_fn.call(mapw).next().value, ["x", 1]); -var next_fn = Map()[std_iterator]().next; +var next_fn = Map()[Symbol.iterator]().next; assertThrowsInstanceOf(function () { next_fn.call({}); }, TypeError); -assertThrowsInstanceOf(function () { next_fn.call(Set()[std_iterator]()); }, TypeError); -var iterw = mapw[std_iterator](); +assertThrowsInstanceOf(function () { next_fn.call(Set()[Symbol.iterator]()); }, TypeError); +var iterw = mapw[Symbol.iterator](); assertEqArray(next_fn.call(iterw).value, ["x", 1]); assertEqArray(next_fn.call(iterw).value, ["y", 2]); assertEq(next_fn.call(iterw).done, true); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-remove-2.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-remove-2.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-remove-2.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-remove-2.js 2015-02-03 14:33:28.000000000 +0000 @@ -3,7 +3,7 @@ load(libdir + "iteration.js"); var map = Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]); -var iter = map[std_iterator](); +var iter = map[Symbol.iterator](); var log = ''; for (let [k, v] of iter) { log += k + v; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-remove-3.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-remove-3.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-remove-3.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-remove-3.js 2015-02-03 14:33:28.000000000 +0000 @@ -4,7 +4,7 @@ load(libdir + "iteration.js"); var map = Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]); -var iter = map[std_iterator](); +var iter = map[Symbol.iterator](); assertIteratorNext(iter, ['a', 0]); assertIteratorNext(iter, ['b', 1]); map.delete('c'); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-remove-4.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-remove-4.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-remove-4.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-remove-4.js 2015-02-03 14:33:28.000000000 +0000 @@ -12,7 +12,7 @@ var NITERS = 5; var iters = []; for (var i = 0; i < NITERS; i++) { - var iter = map[std_iterator](); + var iter = map[Symbol.iterator](); assertIteratorNext(iter, [0, 0]); assertIteratorNext(iter, [1, 1]); iters[i] = iter; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-remove-6.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-remove-6.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterator-remove-6.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterator-remove-6.js 2015-02-03 14:33:28.000000000 +0000 @@ -7,7 +7,7 @@ var map = Map(); for (var i = 0; i < 32; i++) map.set(i, i); -var iter = map[std_iterator](); +var iter = map[Symbol.iterator](); assertIteratorNext(iter, [0, 0]); for (var i = 0; i < 30; i++) map.delete(i); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterators-3.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterators-3.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-iterators-3.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-iterators-3.js 2015-02-03 14:33:28.000000000 +0000 @@ -3,7 +3,7 @@ load(libdir + "iteration.js"); var m = Map(); -var it = m[std_iterator](); +var it = m[Symbol.iterator](); assertIteratorDone(it, undefined); // close the iterator m.clear(); m.set("a", 1); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-surfaces-1.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-surfaces-1.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Map-surfaces-1.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Map-surfaces-1.js 2015-02-03 14:33:28.000000000 +0000 @@ -46,4 +46,4 @@ checkMethod("clear", 0); // Map.prototype[@@iterator] and .entries are the same function object. -assertEq(Map.prototype[std_iterator], Map.prototype.entries); +assertEq(Map.prototype[Symbol.iterator], Map.prototype.entries); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-clear-iterators-1.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-clear-iterators-1.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-clear-iterators-1.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-clear-iterators-1.js 2015-02-03 14:33:28.000000000 +0000 @@ -3,12 +3,12 @@ load(libdir + "iteration.js"); var s = Set(); -var it = s[std_iterator](); +var it = s[Symbol.iterator](); s.clear(); assertIteratorDone(it, undefined); s = Set(["a", "b", "c", "d"]); -it = s[std_iterator](); +it = s[Symbol.iterator](); assertIteratorNext(it, "a"); s.clear(); assertIteratorDone(it, undefined); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-clear-iterators-2.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-clear-iterators-2.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-clear-iterators-2.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-clear-iterators-2.js 2015-02-03 14:33:28.000000000 +0000 @@ -3,7 +3,7 @@ load(libdir + "iteration.js"); var s = Set(["a"]); -var it = s[std_iterator](); +var it = s[Symbol.iterator](); assertIteratorNext(it, "a"); s.clear(); s.add("b"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-clear-iterators-3.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-clear-iterators-3.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-clear-iterators-3.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-clear-iterators-3.js 2015-02-03 14:33:28.000000000 +0000 @@ -3,7 +3,7 @@ load(libdir + "iteration.js"); var s = Set(); -var it = s[std_iterator](); +var it = s[Symbol.iterator](); assertIteratorDone(it, undefined); // close the iterator s.clear(); s.add("a"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-constructor-add.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-constructor-add.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-constructor-add.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-constructor-add.js 2015-02-03 14:33:28.000000000 +0000 @@ -93,7 +93,7 @@ var proxy_arr = new Proxy(arr, { get: function(target, name) { - if (name == std_iterator) { + if (name == Symbol.iterator) { modified = true; Set.prototype.add = function() { called = true; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-forEach.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-forEach.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-forEach.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-forEach.js 2015-02-03 14:33:28.000000000 +0000 @@ -17,7 +17,7 @@ initialSet.forEach(callback); // test that both the Sets are equal and are in same order -var iterator = initialSet[std_iterator](); +var iterator = initialSet[Symbol.iterator](); var count = 0; for (var v of testSet) { assertEq(initialSet.has(v), true); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-iterator-add-2.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-iterator-add-2.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-iterator-add-2.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-iterator-add-2.js 2015-02-03 14:33:28.000000000 +0000 @@ -3,7 +3,7 @@ load(libdir + "iteration.js"); var set = Set(); -var iter0 = set[std_iterator](), iter1 = set[std_iterator](); +var iter0 = set[Symbol.iterator](), iter1 = set[Symbol.iterator](); assertIteratorDone(iter0, undefined); // closes iter0 set.add("x"); assertIteratorDone(iter0, undefined); // already closed diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-iterator-gc-1.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-iterator-gc-1.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-iterator-gc-1.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-iterator-gc-1.js 2015-02-03 14:33:28.000000000 +0000 @@ -5,6 +5,6 @@ var key = {}; var set = Set([key]); -var iter = set[std_iterator](); +var iter = set[Symbol.iterator](); referencesVia(iter, "**UNKNOWN SLOT 0**", set); referencesVia(set, "key", key); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-iterator-proxies-2.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-iterator-proxies-2.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-iterator-proxies-2.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-iterator-proxies-2.js 2015-02-03 14:33:28.000000000 +0000 @@ -5,16 +5,16 @@ var g = newGlobal(); -var iterator_fn = Set.prototype[std_iterator]; +var iterator_fn = Set.prototype[Symbol.iterator]; assertThrowsInstanceOf(function () { iterator_fn.call({}); }, TypeError); assertThrowsInstanceOf(function () { iterator_fn.call(Map()); }, TypeError); var setw = g.eval("Set(['x', 'y'])"); assertIteratorNext(iterator_fn.call(setw), "x"); -var next_fn = Set()[std_iterator]().next; +var next_fn = Set()[Symbol.iterator]().next; assertThrowsInstanceOf(function () { next_fn.call({}); }, TypeError); -assertThrowsInstanceOf(function () { next_fn.call(Map()[std_iterator]()); }, TypeError); -var iterw = setw[std_iterator](); +assertThrowsInstanceOf(function () { next_fn.call(Map()[Symbol.iterator]()); }, TypeError); +var iterw = setw[Symbol.iterator](); assertIteratorResult(next_fn.call(iterw), "x", false); assertIteratorResult(next_fn.call(iterw), "y", false); assertIteratorResult(next_fn.call(iterw), undefined, true); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-iterator-remove-2.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-iterator-remove-2.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-iterator-remove-2.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-iterator-remove-2.js 2015-02-03 14:33:28.000000000 +0000 @@ -3,7 +3,7 @@ load(libdir + "iteration.js"); var set = Set("abcd"); -var iter = set[std_iterator](); +var iter = set[Symbol.iterator](); var log = ""; for (let x of iter) { log += x; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-iterator-remove-3.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-iterator-remove-3.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-iterator-remove-3.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-iterator-remove-3.js 2015-02-03 14:33:28.000000000 +0000 @@ -3,7 +3,7 @@ load(libdir + "iteration.js"); var set = Set("abcd"); -var iter = set[std_iterator](); +var iter = set[Symbol.iterator](); assertIteratorNext(iter, "a"); assertIteratorNext(iter, "b"); set.delete("c"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-iterator-remove-4.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-iterator-remove-4.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-iterator-remove-4.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-iterator-remove-4.js 2015-02-03 14:33:28.000000000 +0000 @@ -12,7 +12,7 @@ var NITERS = 5; var iters = []; for (var i = 0; i < NITERS; i++) { - var iter = set[std_iterator](); + var iter = set[Symbol.iterator](); assertIteratorNext(iter, 0); assertIteratorNext(iter, 1); iters[i] = iter; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-iterator-remove-6.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-iterator-remove-6.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-iterator-remove-6.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-iterator-remove-6.js 2015-02-03 14:33:28.000000000 +0000 @@ -7,7 +7,7 @@ var set = Set(); for (var i = 0; i < 32; i++) set.add(i); -var iter = set[std_iterator](); +var iter = set[Symbol.iterator](); assertIteratorNext(iter, 0); for (var i = 0; i < 30; i++) set.delete(i); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-surfaces-1.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-surfaces-1.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/Set-surfaces-1.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/Set-surfaces-1.js 2015-02-03 14:33:28.000000000 +0000 @@ -45,4 +45,4 @@ // Set.prototype.keys, .values, and .iterator are the same function object assertEq(Set.prototype.keys, Set.prototype.values); -assertEq(Set.prototype[std_iterator], Set.prototype.values); +assertEq(Set.prototype[Symbol.iterator], Set.prototype.values); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/WeakMap-constructor-iterable.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/WeakMap-constructor-iterable.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/WeakMap-constructor-iterable.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/WeakMap-constructor-iterable.js 2015-02-03 14:33:28.000000000 +0000 @@ -11,7 +11,7 @@ var done = false; var iterable = {}; -iterable[std_iterator] = function*() { +iterable[Symbol.iterator] = function*() { yield [k1, v1]; yield [k2, v2]; done = true; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/WeakMap-constructor-non-iterable.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/WeakMap-constructor-non-iterable.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/WeakMap-constructor-non-iterable.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/WeakMap-constructor-non-iterable.js 2015-02-03 14:33:28.000000000 +0000 @@ -4,10 +4,10 @@ load(libdir + "iteration.js"); var non_iterable1 = {}; -non_iterable1[std_iterator] = {}; +non_iterable1[Symbol.iterator] = {}; assertThrowsInstanceOf(() => new WeakMap(non_iterable1), TypeError); var non_iterable2 = {}; -non_iterable2[std_iterator] = function() { +non_iterable2[Symbol.iterator] = function() { }; assertThrowsInstanceOf(() => new WeakMap(non_iterable2), TypeError); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/WeakMap-constructor-set.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/WeakMap-constructor-set.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/WeakMap-constructor-set.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/WeakMap-constructor-set.js 2015-02-03 14:33:28.000000000 +0000 @@ -103,7 +103,7 @@ var proxy_arr = new Proxy(arr, { get: function(target, name) { - if (name == std_iterator) { + if (name == Symbol.iterator) { modified = true; WeakMap.prototype.set = function() { called = true; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/WeakSet-constructor-add.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/WeakSet-constructor-add.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/WeakSet-constructor-add.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/WeakSet-constructor-add.js 2015-02-03 14:33:28.000000000 +0000 @@ -90,7 +90,7 @@ var proxy_arr = new Proxy(arr, { get: function(target, name) { - if (name == std_iterator) { + if (name == Symbol.iterator) { modified = true; WeakSet.prototype.add = function() { called = true; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/WeakSet-error.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/WeakSet-error.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/collections/WeakSet-error.js 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/collections/WeakSet-error.js 2015-02-03 14:33:28.000000000 +0000 @@ -16,7 +16,7 @@ testMethod("clear"); assertThrowsInstanceOf(function() { var ws = new WeakSet(); ws.add(1); }, TypeError); -assertThrowsInstanceOf(function() { new WeakSet({[std_iterator]: 2}) }, TypeError); -assertEq(typeof [][std_iterator], "function"); +assertThrowsInstanceOf(function() { new WeakSet({[Symbol.iterator]: 2}) }, TypeError); +assertEq(typeof [][Symbol.iterator], "function"); assertThrowsInstanceOf(function() { WeakSet(); }, TypeError); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/debug/Memory-takeCensus-02.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/debug/Memory-takeCensus-02.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/debug/Memory-takeCensus-02.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/debug/Memory-takeCensus-02.js 2015-02-03 14:33:28.000000000 +0000 @@ -20,8 +20,24 @@ function pointCheck(label, lhs, rhs, objComp, funComp) { print(label); - assertEq(objComp(lhs.objects.Object.count, rhs.objects.Object.count), true); - assertEq(funComp(lhs.objects.Function.count, rhs.objects.Function.count), true); + try { + assertEq(objComp(lhs.objects.Object.count, rhs.objects.Object.count), true); + assertEq(funComp(lhs.objects.Function.count, rhs.objects.Function.count), true); + } catch (ex) { + print("pointCheck failed: " + ex); + print("lhs: " + JSON.stringify(lhs, undefined, 2)); + print("rhs: " + JSON.stringify(rhs, undefined, 2)); + + // Do it again, and put the result where Mozilla's continuous integration + // code will find it. + var upload_dir = os.getenv("MOZ_UPLOAD_DIR") || "."; + redirect(upload_dir + "/Memory-takeCensus-02.txt"); + print("pointCheck failed: " + ex); + print("lhs: " + JSON.stringify(lhs, undefined, 2)); + print("rhs: " + JSON.stringify(rhs, undefined, 2)); + + throw ex; + } } function eq(lhs, rhs) { return lhs === rhs; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/debug/resumption-error-01.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/debug/resumption-error-01.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/debug/resumption-error-01.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/debug/resumption-error-01.js 2015-02-03 14:33:29.000000000 +0000 @@ -0,0 +1,7 @@ +// A resumption value can't have both {return:} and {throw:} properties. + +var g = newGlobal(); +var dbg = Debugger(g); +dbg.onDebuggerStatement = stack => ({return: 1, throw: 2}); +dbg.uncaughtExceptionHook = exc => ({return: "corrected"}); +assertEq(g.eval("debugger; false;"), "corrected"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/debug/resumption-error-02.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/debug/resumption-error-02.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/debug/resumption-error-02.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/debug/resumption-error-02.js 2015-02-03 14:33:29.000000000 +0000 @@ -0,0 +1,16 @@ +// Error handling if parsing a resumption value throws. + +var g = newGlobal(); +var dbg = Debugger(g); +var rv; +dbg.onDebuggerStatement = stack => rv; +dbg.uncaughtExceptionHook = function (exc) { + assertEq(exc, "BANG"); + return {return: "recovered"}; +}; + +rv = {get throw() { throw "BANG"; }}; +assertEq(g.eval("debugger; false;"), "recovered"); + +rv = new Proxy({}, {has() { throw "BANG"; }}); +assertEq(g.eval("debugger; false;"), "recovered"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/arguments-1.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/arguments-1.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/arguments-1.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/arguments-1.js 2015-02-03 14:33:29.000000000 +0000 @@ -4,7 +4,7 @@ // Arguments objects do not have a .@@iterator() method by default. // Install one on Object.prototype. -Object.prototype[std_iterator] = Array.prototype[std_iterator]; +Object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; var s; function test() { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/arguments-2.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/arguments-2.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/arguments-2.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/arguments-2.js 2015-02-03 14:33:29.000000000 +0000 @@ -8,7 +8,7 @@ var s = ''; var args = f('a', 'b', 'c'); -Object.prototype[std_iterator] = Array.prototype[std_iterator]; +Object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; for (var v of args) s += v; assertEq(s, 'abc'); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/arguments-3.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/arguments-3.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/arguments-3.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/arguments-3.js 2015-02-03 14:33:29.000000000 +0000 @@ -2,7 +2,7 @@ load(libdir + "iteration.js"); -Object.prototype[std_iterator] = Array.prototype[std_iterator]; +Object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; var s; function test() { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/arguments-4.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/arguments-4.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/arguments-4.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/arguments-4.js 2015-02-03 14:33:29.000000000 +0000 @@ -2,7 +2,7 @@ load(libdir + "iteration.js"); -Object.prototype[std_iterator] = Array.prototype[std_iterator]; +Object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; var s; function g(obj) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/arguments-5.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/arguments-5.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/arguments-5.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/arguments-5.js 2015-02-03 14:33:29.000000000 +0000 @@ -2,7 +2,7 @@ load(libdir + "iteration.js"); -Object.prototype[std_iterator] = Array.prototype[std_iterator]; +Object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; var s; function g(obj) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/arguments-6.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/arguments-6.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/arguments-6.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/arguments-6.js 2015-02-03 14:33:29.000000000 +0000 @@ -2,7 +2,7 @@ load(libdir + "iteration.js"); -Object.prototype[std_iterator] = Array.prototype[std_iterator]; +Object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; var s; function f() { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/arguments-7.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/arguments-7.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/arguments-7.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/arguments-7.js 2015-02-03 14:33:29.000000000 +0000 @@ -2,7 +2,7 @@ load(libdir + "iteration.js"); -Object.prototype[std_iterator] = Array.prototype[std_iterator]; +Object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; var s; function f() { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-holes-4.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-holes-4.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-holes-4.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-holes-4.js 2015-02-03 14:33:29.000000000 +0000 @@ -6,7 +6,7 @@ var a = [0, , 2, 3]; a.__proto__ = m; var log = []; -Object.prototype[std_iterator] = Array.prototype[std_iterator]; +Object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; for (var x of a) log.push(x); assertEq(log[1], 'peek'); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-changing.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-changing.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-changing.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-changing.js 2015-02-03 14:33:29.000000000 +0000 @@ -4,7 +4,7 @@ load(libdir + "iteration.js"); var arr = [0, 1, 2]; -var it = arr[std_iterator](); +var it = arr[Symbol.iterator](); arr[0] = 1000; arr[2] = 2000; assertIteratorNext(it, 1000); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-gc.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-gc.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-gc.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-gc.js 2015-02-03 14:33:29.000000000 +0000 @@ -4,7 +4,7 @@ load(libdir + "iteration.js"); function test(obj) { - var it = Array.prototype[std_iterator].call(obj); + var it = Array.prototype[Symbol.iterator].call(obj); assertEq(referencesVia(it, "**UNKNOWN SLOT 0**", obj), true); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-generic.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-generic.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-generic.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-generic.js 2015-02-03 14:33:29.000000000 +0000 @@ -5,7 +5,7 @@ load(libdir + "iteration.js"); function test(obj) { - var it = Array.prototype[std_iterator].call(obj); + var it = Array.prototype[Symbol.iterator].call(obj); var ki = Array.prototype.keys.call(obj); var ei = Array.prototype.entries.call(obj); for (var i = 0; i < (obj.length >>> 0); i++) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-growing-1.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-growing-1.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-growing-1.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-growing-1.js 2015-02-03 14:33:29.000000000 +0000 @@ -4,7 +4,7 @@ load(libdir + "iteration.js"); var arr = [0, 1]; -var it = arr[std_iterator](); +var it = arr[Symbol.iterator](); var ki = arr.keys(); var ei = arr.entries(); assertIteratorNext(it, 0); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-null.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-null.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-null.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-null.js 2015-02-03 14:33:29.000000000 +0000 @@ -5,7 +5,7 @@ for (var v of [undefined, null]) { // ES6 draft 2013-09-05 section 22.1.5.1. - assertThrowsInstanceOf(function () { Array.prototype[std_iterator].call(v); }, TypeError); + assertThrowsInstanceOf(function () { Array.prototype[Symbol.iterator].call(v); }, TypeError); assertThrowsInstanceOf(function () { Array.prototype.keys.call(v); }, TypeError); assertThrowsInstanceOf(function () { Array.prototype.entries.call(v); }, TypeError); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-proxy.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-proxy.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-proxy.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-proxy.js 2015-02-03 14:33:29.000000000 +0000 @@ -17,7 +17,7 @@ } }; -var it = Array.prototype[std_iterator].call(Proxy.create(proxyObj)); +var it = Array.prototype[Symbol.iterator].call(Proxy.create(proxyObj)); assertIteratorNext(it, "0"); s += ' '; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-shrinking.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-shrinking.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-shrinking.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-shrinking.js 2015-02-03 14:33:29.000000000 +0000 @@ -4,7 +4,7 @@ load(libdir + "iteration.js"); var arr = [0, 1, 2]; -var it = arr[std_iterator](); +var it = arr[Symbol.iterator](); var ki = arr.keys(); var ei = arr.entries(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-surfaces-1.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-surfaces-1.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-surfaces-1.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-surfaces-1.js 2015-02-03 14:33:29.000000000 +0000 @@ -4,13 +4,13 @@ var constructors = [Array, String, Uint8Array, Uint8ClampedArray]; for (var c of constructors) { - assertEq(c.prototype[std_iterator].length, 0); + assertEq(c.prototype[Symbol.iterator].length, 0); var loc = (c === Array || c === String) ? c.prototype : Object.getPrototypeOf(c.prototype); - var desc = Object.getOwnPropertyDescriptor(loc, std_iterator); + var desc = Object.getOwnPropertyDescriptor(loc, Symbol.iterator); assertEq(desc.configurable, true); assertEq(desc.enumerable, false); assertEq(desc.writable, true); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js 2015-02-03 14:33:29.000000000 +0000 @@ -2,7 +2,7 @@ load(libdir + "iteration.js"); -var proto = Object.getPrototypeOf([][std_iterator]()); +var proto = Object.getPrototypeOf([][Symbol.iterator]()); assertEq(Object.getPrototypeOf(proto), Iterator.prototype); proto = Object.getPrototypeOf([].keys()); assertEq(Object.getPrototypeOf(proto), Iterator.prototype); @@ -13,7 +13,7 @@ assertEq(typeof it, 'object'); assertEq(Object.getPrototypeOf(it), proto); assertEq(Object.getOwnPropertyNames(it).length, 0); - assertEq(it[std_iterator](), it); + assertEq(it[Symbol.iterator](), it); // for-in enumerates the iterator's properties. it.x = 0; @@ -23,8 +23,8 @@ assertEq(s, 'x.'); } -check([][std_iterator]()); -check(Array.prototype[std_iterator].call({})); +check([][Symbol.iterator]()); +check(Array.prototype[Symbol.iterator].call({})); check([].keys()); check(Array.prototype.keys.call({})); check([].entries()); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/next-2.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/next-2.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/next-2.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/next-2.js 2015-02-03 14:33:29.000000000 +0000 @@ -3,6 +3,6 @@ load(libdir + "asserts.js"); load(libdir + "iteration.js"); -var it = [1, 2][std_iterator](); +var it = [1, 2][Symbol.iterator](); var v = Object.create(it); assertThrowsInstanceOf(function () { Iterator.prototype.next.call(v); }, TypeError); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/next-3.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/next-3.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/next-3.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/next-3.js 2015-02-03 14:33:29.000000000 +0000 @@ -5,6 +5,6 @@ load(libdir + "iteration.js"); var g = newGlobal(); -g.eval(`var it = [1, 2][${uneval(std_iterator)}]();`); +g.eval(`var it = [1, 2][Symbol.iterator]();`); assertIteratorNext(g.it, 1); -assertDeepEq([][std_iterator]().next.call(g.it), { value: 2, done: false }) +assertDeepEq([][Symbol.iterator]().next.call(g.it), { value: 2, done: false }) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/next-arity.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/next-arity.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/next-arity.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/next-arity.js 2015-02-03 14:33:29.000000000 +0000 @@ -12,7 +12,7 @@ return { get value() { throw 42; }, done: true } } - this[std_iterator] = function () { return this; } + this[Symbol.iterator] = function () { return this; } this.next = next; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/proxy-3.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/proxy-3.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/proxy-3.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/proxy-3.js 2015-02-03 14:33:29.000000000 +0000 @@ -5,7 +5,7 @@ var p = Proxy.create({ getPropertyDescriptor: function (name) { - if (name == std_iterator) + if (name == Symbol.iterator) throw "fit"; return undefined; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-01.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-01.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-01.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-01.js 2015-02-03 14:33:29.000000000 +0000 @@ -9,5 +9,5 @@ load(libdir + "asserts.js"); load(libdir + "iteration.js"); -delete Array.prototype[std_iterator]; +delete Array.prototype[Symbol.iterator]; assertThrowsInstanceOf(function () { for (var x of []) ; }, TypeError); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-02.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-02.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-02.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-02.js 2015-02-03 14:33:29.000000000 +0000 @@ -4,7 +4,7 @@ load(libdir + "iteration.js"); function test(v) { - Array.prototype[std_iterator] = v; + Array.prototype[Symbol.iterator] = v; assertThrowsInstanceOf(function () { for (var x of []) ; }, TypeError); } test(undefined); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-03.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-03.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-03.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-03.js 2015-02-03 14:33:29.000000000 +0000 @@ -2,7 +2,7 @@ load(libdir + "iteration.js"); -Array.prototype[std_iterator] = function* () { +Array.prototype[Symbol.iterator] = function* () { for (var i = this.length; --i >= 0; ) yield this[i]; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-04.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-04.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-04.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-04.js 2015-02-03 14:33:29.000000000 +0000 @@ -4,7 +4,7 @@ load(libdir + "iteration.js"); var a = []; -a[std_iterator] = function* () { +a[Symbol.iterator] = function* () { yield 'o'; yield 'k'; }; @@ -13,5 +13,5 @@ s += v; assertEq(s, 'ok'); -a[std_iterator] = undefined; +a[Symbol.iterator] = undefined; assertThrowsInstanceOf(function () { for (var v of a) ; }, TypeError); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-05.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-05.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-05.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-05.js 2015-02-03 14:33:29.000000000 +0000 @@ -3,6 +3,6 @@ load(libdir + "asserts.js"); load(libdir + "iteration.js"); -delete String.prototype[std_iterator]; +delete String.prototype[Symbol.iterator]; assertThrowsInstanceOf(function () { for (var v of "abc") ; }, TypeError); assertThrowsInstanceOf(function () { for (var v of new String("abc")) ; }, TypeError); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-06.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-06.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-06.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-06.js 2015-02-03 14:33:29.000000000 +0000 @@ -3,6 +3,6 @@ load(libdir + "asserts.js"); load(libdir + "iteration.js"); -var iterProto = Object.getPrototypeOf([][std_iterator]()); +var iterProto = Object.getPrototypeOf([][Symbol.iterator]()); delete iterProto.next; assertThrowsInstanceOf(function () { for (var v of []) ; }, TypeError); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-07.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-07.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-07.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-07.js 2015-02-03 14:33:29.000000000 +0000 @@ -4,7 +4,7 @@ load(libdir + "asserts.js"); load(libdir + "iteration.js"); -var iterProto = Object.getPrototypeOf([][std_iterator]()); +var iterProto = Object.getPrototypeOf([][Symbol.iterator]()); var s = ''; assertThrowsInstanceOf(function () { for (var v of ['duck', 'duck', 'duck', 'goose', 'FAIL']) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-08.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-08.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-08.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-08.js 2015-02-03 14:33:29.000000000 +0000 @@ -5,7 +5,7 @@ var g = newGlobal(); g.eval(` var obj = {}; - obj[${uneval(std_iterator)}] = function () { return this; }; + obj[Symbol.iterator] = function () { return this; }; obj.next = function () { return { done: true }; }; `); for (x of g.obj) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-11.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-11.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/semantics-11.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/semantics-11.js 2015-02-03 14:33:29.000000000 +0000 @@ -31,7 +31,7 @@ var obj = Proxy.create({ get: function (receiver, name) { - assertEq(name, std_iterator); + assertEq(name, Symbol.iterator); s += "I"; return iterator_fn; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/string-iterator-generic.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/string-iterator-generic.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/string-iterator-generic.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/string-iterator-generic.js 2015-02-03 14:33:29.000000000 +0000 @@ -5,7 +5,7 @@ load(libdir + "string.js"); function test(obj) { - var it = String.prototype[std_iterator].call(obj); + var it = String.prototype[Symbol.iterator].call(obj); var s = String(obj); for (var i = 0, length = s.length; i < length;) { var r = s[i++]; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/string-iterator-surfaces.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/string-iterator-surfaces.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/string-iterator-surfaces.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/string-iterator-surfaces.js 2015-02-03 14:33:29.000000000 +0000 @@ -47,29 +47,28 @@ // String.prototype[@@iterator] is a built-in function -assertBuiltinFunction(String.prototype, std_iterator, 0); +assertBuiltinFunction(String.prototype, Symbol.iterator, 0); // Test StringIterator.prototype surface -var iter = ""[std_iterator](); +var iter = ""[Symbol.iterator](); var iterProto = Object.getPrototypeOf(iter); // StringIterator.prototype inherits from Object.prototype assertEq(Object.getPrototypeOf(iterProto), Object.prototype); -// Own properties for StringIterator.prototype: "next" and @@iterator -arraysEqual(Object.getOwnPropertyNames(iterProto).sort(), - JS_HAS_SYMBOLS ? ["next"] : ["@@iterator", "next"]); -assertEq(iterProto.hasOwnProperty(std_iterator), true); +// Own properties for StringIterator.prototype: "next" +arraysEqual(Object.getOwnPropertyNames(iterProto).sort(), ["next"]); +assertEq(iterProto.hasOwnProperty(Symbol.iterator), true); // StringIterator.prototype[@@iterator] is a built-in function -assertBuiltinFunction(iterProto, std_iterator, 0); +assertBuiltinFunction(iterProto, Symbol.iterator, 0); // StringIterator.prototype.next is a built-in function assertBuiltinFunction(iterProto, "next", 0); // StringIterator.prototype[@@iterator] is generic and returns |this| for (var v of [void 0, null, true, false, "", 0, 1, {}, [], iter, iterProto]) { - assertEq(iterProto[std_iterator].call(v), v); + assertEq(iterProto[Symbol.iterator].call(v), v); } // StringIterator.prototype.next is not generic diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/value-done-access.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/value-done-access.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/for-of/value-done-access.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/for-of/value-done-access.js 2015-02-03 14:33:29.000000000 +0000 @@ -13,7 +13,7 @@ } } - this[std_iterator] = function() { return this; }; + this[Symbol.iterator] = function() { return this; }; this.next = next; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/gc/bug-1124563.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/gc/bug-1124563.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/gc/bug-1124563.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/gc/bug-1124563.js 2015-02-03 14:33:29.000000000 +0000 @@ -0,0 +1,4 @@ +try { + gc(0, 'shrinking')({x: 0}) +} catch (e) {} +eval("({x: 0, x: 0})[{x: 0}]") diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/gc/bug-1124653.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/gc/bug-1124653.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/gc/bug-1124653.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/gc/bug-1124653.js 2015-02-03 14:33:29.000000000 +0000 @@ -0,0 +1,5 @@ +var o = {}; +gczeal(14); +for (var i = 0; i < 200; i++) { + with (o) { } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/generators/bug931414.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/generators/bug931414.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/generators/bug931414.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/generators/bug931414.js 2015-02-03 14:33:29.000000000 +0000 @@ -4,7 +4,7 @@ function iterable() { var iterable = {}; - iterable[std_iterator] = () => ({next: () => void 0}); + iterable[Symbol.iterator] = () => ({next: () => void 0}); return iterable; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/heap-analysis/findPath.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/heap-analysis/findPath.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/heap-analysis/findPath.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/heap-analysis/findPath.js 2015-02-03 14:33:29.000000000 +0000 @@ -21,8 +21,8 @@ function C() {} C.prototype.obj = {}; var c = new C; -Match.Pattern([{node: {}, edge: "type"}, - {node: Match.Pattern.ANY, edge: "type_proto"}, +Match.Pattern([{node: {}, edge: "group"}, + {node: Match.Pattern.ANY, edge: "group_proto"}, {node: { constructor: Match.Pattern.ANY }, edge: "obj"}]) .assert(findPath(c, c.obj)); print(uneval(findPath(c, c.obj))); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/bug1123064.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/bug1123064.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/bug1123064.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/bug1123064.js 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,30 @@ + + +function toint32() { + + // The test case to trigger MToInt32 operation. + var ToInteger = getSelfHostedValue("ToInteger"); + + // Case1: The input operand is constant int32. + var result = ToInteger(1); + assertEq(result, 1); + + // Case2: The input operand is constant double. + result = ToInteger(0.12); + assertEq(result, 0); + + // Case3: The input operand is constant float. + result = ToInteger(Math.fround(0.13)); + assertEq(result, 0); + + // Case4: The input operand is constant boolean. + result = ToInteger(true); + assertEq(result, 1); + + // Case5: The input operand is null. + result = ToInteger(null); + assertEq(result, 0); +} + +toint32(); +toint32(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/dce-with-rinstructions.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/dce-with-rinstructions.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/dce-with-rinstructions.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/dce-with-rinstructions.js 2015-02-03 14:33:30.000000000 +0000 @@ -1062,16 +1062,32 @@ return i; } -var uceFault_hypot_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_number')); -function rhypot_number(i) { +var uceFault_hypot_number_2args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_number_2args')); +function rhypot_number_2args(i) { var x = Math.hypot(i, i + 1); - if (uceFault_hypot_number(i) || uceFault_hypot_number(i)) + if (uceFault_hypot_number_2args(i) || uceFault_hypot_number_2args(i)) assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1))); return i; } -var uceFault_hypot_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_object')); -function rhypot_object(i) { +var uceFault_hypot_number_3args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_number_3args')); +function rhypot_number_3args(i) { + var x = Math.hypot(i, i + 1, i + 2); + if (uceFault_hypot_number_3args(i) || uceFault_hypot_number_3args(i)) + assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1) + (i + 2) * (i + 2))); + return i; +} + +var uceFault_hypot_number_4args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_number_4args')); +function rhypot_number_4args(i) { + var x = Math.hypot(i, i + 1, i + 2, i + 3); + if (uceFault_hypot_number_4args(i) || uceFault_hypot_number_4args(i)) + assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1) + (i + 2) * (i + 2) + (i + 3) * (i + 3))); + return i; +} + +var uceFault_hypot_object_2args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_object_2args')); +function rhypot_object_2args(i) { var t0 = i; var t1 = i + 1; var o0 = { valueOf: function () { return t0; } }; @@ -1079,11 +1095,48 @@ var x = Math.hypot(o0, o1); t0 = 1000; t1 = 2000; - if (uceFault_hypot_object(i) || uceFault_hypot_object(i) ) + if (uceFault_hypot_object_2args(i) || uceFault_hypot_object_2args(i) ) assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1))); return i; } +var uceFault_hypot_object_3args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_object_3args')); +function rhypot_object_3args(i) { + var t0 = i; + var t1 = i + 1; + var t2 = i + 2; + var o0 = { valueOf: function () { return t0; } }; + var o1 = { valueOf: function () { return t1; } }; + var o2 = { valueOf: function () { return t2; } }; + var x = Math.hypot(o0, o1, o2); + t0 = 1000; + t1 = 2000; + t2 = 3000; + if (uceFault_hypot_object_3args(i) || uceFault_hypot_object_3args(i) ) + assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1) + (i + 2) * (i + 2))); + return i; +} + +var uceFault_hypot_object_4args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_object_4args')); +function rhypot_object_4args(i) { + var t0 = i; + var t1 = i + 1; + var t2 = i + 2; + var t3 = i + 3; + var o0 = { valueOf: function () { return t0; } }; + var o1 = { valueOf: function () { return t1; } }; + var o2 = { valueOf: function () { return t2; } }; + var o3 = { valueOf: function () { return t3; } }; + var x = Math.hypot(o0, o1, o2, o3); + t0 = 1000; + t1 = 2000; + t2 = 3000; + t3 = 4000; + if (uceFault_hypot_object_4args(i) || uceFault_hypot_object_4args(i) ) + assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1) + (i + 2) * (i + 2) + (i + 3) * (i + 3))); + return i; +} + var uceFault_sin_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_sin_number')); function rsin_number(i) { var x = Math.sin(i); @@ -1224,8 +1277,12 @@ rtrunc_to_int32_number(i); rtrunc_to_int32_object(i); rtrunc_to_int32_string(i); - rhypot_number(i); - rhypot_object(i); + rhypot_number_2args(i); + rhypot_number_3args(i); + rhypot_number_4args(i); + rhypot_object_2args(i); + rhypot_object_3args(i); + rhypot_object_4args(i); rsin_number(i); rsin_object(i); rlog_number(i); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/nursery-getter-setter.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/nursery-getter-setter.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/nursery-getter-setter.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/nursery-getter-setter.js 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,17 @@ +var threshold = getJitCompilerOptions()["ion.warmup.trigger"] + 101; +function bar(i) { + if (!i) + with (this) {}; // Don't inline. + if (i === threshold) + minorgc(); +} + +function f() { + var o2 = Object.create({get foo() { return this.x; }, set foo(x) { this.x = x + 1; }}); + for (var i=0; i<2000; i++) { + o2.foo = i; + assertEq(o2.foo, i + 1); + bar(i); + } +} +f(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/range-analysis-bug1122402.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/range-analysis-bug1122402.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/range-analysis-bug1122402.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/range-analysis-bug1122402.js 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,11 @@ +// |jit-test| error:TypeError + +(function() { + let r + g = function(x) { + ((-0x80000000 + (x >>> 0)) != 0) ? 0 : x() + } +})() + +g(NaN) +g(0x80000000); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/range-analysis-bug1124448.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/range-analysis-bug1124448.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/range-analysis-bug1124448.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/range-analysis-bug1124448.js 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,8 @@ + +function f(y) { + var x1 = Math.max(-2147483649 >> 0, y >>> 0); + var x2 = x1 | 0; + return (x2 >= 0) ? 1 : 0; +} +assertEq(f(0), 1); +assertEq(f(-1), 0); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/setpropertypolymorphic-float32.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/setpropertypolymorphic-float32.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/setpropertypolymorphic-float32.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/setpropertypolymorphic-float32.js 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,24 @@ +function loop(f32, arr) { + for (var i = 0; i < 2000; i++) { + var j = i % 20; + arr[j].k = f32[j]; + } +} + +function f() { + var obj = {k: null, m: null}; + var obj2 = {m: null, k: 42, l: null}; + var f32 = new Float32Array(20); + var arr = []; + for (var i = 0; i < 10; i++) { + arr.push(obj); + arr.push(obj2); + + } + loop(f32, arr); + for(var i = 0; i < 20; i++) { + assertEq(arr[i].k, f32[i]); + } +} + +f(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/simd-bug1121299.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/simd-bug1121299.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/simd-bug1121299.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/simd-bug1121299.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -setJitCompilerOption("baseline.warmup.trigger", 10); -setJitCompilerOption("ion.warmup.trigger", 30); - -function test_1(i) { - if (i >= 40) - return; - var a = SIMD.float32x4(1.1, 2.2, 3.3, 4.6); - SIMD.int32x4.fromFloat32x4(a); - test_1(i + 1); -} -test_1(0); - - -var float32x4 = SIMD.float32x4; -function test_2() { - var Array = float32x4.array(3); - var array = new Array([ - float32x4(1, 2, 3, 4), - float32x4(5, 6, 7, 8), - float32x4(9, 10, 11, 12) - ]); - if (typeof reportCompare === "function") - reportCompare(true, true); -} -test_2(); -evaluate("test_2(); test_2();", { - compileAndGo: true -}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/simd-bug1123631.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/simd-bug1123631.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/simd-bug1123631.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/simd-bug1123631.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -if (!this.hasOwnProperty("SIMD")) - quit(); - -var float64x2 = SIMD.float64x2; -function test() { - var a = float64x2(1, 2); -} -test(); -test(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/stack-alignment.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/stack-alignment.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/ion/stack-alignment.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/ion/stack-alignment.js 2015-02-03 14:33:30.000000000 +0000 @@ -4,11 +4,33 @@ // Check that an entry frame is always aligned properly. function entryFrame_1() { - assertValidJitStack(); + assertJitStackInvariants(); +} + +// Check rectifier frames are keeping the same alignment. +function rectifierFrame_verify(a, b, c, d) { + assertJitStackInvariants(); +} + +function rectifierFrame_1(i) { + rectifierFrame_verify(); +} +function rectifierFrame_2(i) { + rectifierFrame_verify(i); +} +function rectifierFrame_3(i) { + rectifierFrame_verify(i, i); +} +function rectifierFrame_4(i) { + rectifierFrame_verify(i, i, i); } for (i = 0; i < 40; i++) { entryFrame_1(); entryFrame_1(0); entryFrame_1(0, 1); + rectifierFrame_1(i); + rectifierFrame_2(i); + rectifierFrame_3(i); + rectifierFrame_4(i); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/parser/bug-1090096.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/parser/bug-1090096.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/parser/bug-1090096.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/parser/bug-1090096.js 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,12 @@ +load(libdir + "asserts.js"); + +assertThrowsInstanceOf( + () => Function(` +for (let { + [ + function(x) {; + } + ]: {} +} in 0 +`), + SyntaxError) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/profiler/test-baseline-eval-frame-profiling.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/profiler/test-baseline-eval-frame-profiling.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/profiler/test-baseline-eval-frame-profiling.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/profiler/test-baseline-eval-frame-profiling.js 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,11 @@ + +setJitCompilerOption("baseline.warmup.trigger", 10); + +function main() { + for (var i = 0; i < 50; i++) + eval("for (var j = 0; j < 50; j++) readSPSProfilingStack();"); +} + +gczeal(2, 10000); +enableSPSProfilingWithSlowAssertions(); +main(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/proxy/function-toString.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/proxy/function-toString.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/proxy/function-toString.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/proxy/function-toString.js 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,10 @@ +load(libdir + 'asserts.js'); + +// Function.prototype.toString doesn't accept ES6 proxies. + +var proxy = new Proxy(function() {}, {}); +assertThrowsInstanceOf(() => Function.prototype.toString.call(proxy), TypeError); +var o = Proxy.revocable(function() {}, {}); +assertThrowsInstanceOf(() => Function.prototype.toString.call(o.proxy), TypeError); +o.revoke(); +assertThrowsInstanceOf(() => Function.prototype.toString.call(o.proxy), TypeError); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/proxy/operations-on-revoked.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/proxy/operations-on-revoked.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/proxy/operations-on-revoked.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/proxy/operations-on-revoked.js 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,18 @@ +load(libdir + 'asserts.js'); + +var r = Proxy.revocable({}, {}); +var r2 = Proxy.revocable(function(){}, {}); +r.revoke(); +r2.revoke(); + +var p = r.proxy; +var p2 = r2.proxy; + +assertThrowsInstanceOf(() => ({} instanceof p), TypeError); +assertThrowsInstanceOf(() => ({} instanceof p2), TypeError); + +assertEq(Object.prototype.toString.call(p), "[object Object]"); +assertEq(Object.prototype.toString.call(p2), "[object Function]"); + +assertThrowsInstanceOf(() => RegExp.prototype.exec.call(p, ""), TypeError); +assertThrowsInstanceOf(() => RegExp.prototype.exec.call(p2, ""), TypeError); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/proxy/testDirectProxyConstructor.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/proxy/testDirectProxyConstructor.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/proxy/testDirectProxyConstructor.js 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/proxy/testDirectProxyConstructor.js 2015-02-03 14:33:30.000000000 +0000 @@ -1,23 +1,19 @@ load(libdir + "asserts.js"); +// Throw a TypeError if Proxy is not called as a constructor +assertThrowsInstanceOf(function () { Proxy({}, {}); }, TypeError); + // Throw a TypeError if Proxy is called with less than two arguments -assertThrowsInstanceOf(function () { Proxy(); }, TypeError); assertThrowsInstanceOf(function () { new Proxy(); }, TypeError); -assertThrowsInstanceOf(function () { Proxy({}); }, TypeError); assertThrowsInstanceOf(function () { new Proxy({}); }, TypeError); // Throw a TypeError if the first argument is not a non-null object -assertThrowsInstanceOf(function () { Proxy(0, {}); }, TypeError); assertThrowsInstanceOf(function () { new Proxy(0, {}); }, TypeError); -assertThrowsInstanceOf(function () { Proxy(null, {}); }, TypeError); assertThrowsInstanceOf(function () { new Proxy(null, {}); }, TypeError); // Throw a TypeError if the second argument is not a non-null object -assertThrowsInstanceOf(function () { Proxy({}, 0); }, TypeError); assertThrowsInstanceOf(function () { new Proxy({}, 0); }, TypeError); -assertThrowsInstanceOf(function () { Proxy({}, null); }, TypeError); assertThrowsInstanceOf(function () { new Proxy({}, null); }, TypeError); // Result of the call should be an object -assertEq(typeof Proxy({}, {}), 'object'); assertEq(typeof new Proxy({}, {}), 'object'); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/proxy/testDirectProxyGetInherited1.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/proxy/testDirectProxyGetInherited1.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/proxy/testDirectProxyGetInherited1.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/proxy/testDirectProxyGetInherited1.js 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,16 @@ +// Getting a property that exists on an ordinary object +// does not touch a proxy on its proto chain. + +load(libdir + "asserts.js"); + +var angryHandler = new Proxy({}, { + get(t, id) { throw new Error("angryHandler should not be queried (" + id + ")"); } +}); +var angryProto = new Proxy({}, angryHandler); +var obj = Object.create(angryProto, { + x: {value: 3}, + y: {get: () => 4} +}); +assertThrowsInstanceOf(() => obj.z, Error); // check that angryProto works +assertEq(obj.x, 3); +assertEq(obj.y, 4); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/proxy/testDirectProxyGetInherited2.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/proxy/testDirectProxyGetInherited2.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/proxy/testDirectProxyGetInherited2.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/proxy/testDirectProxyGetInherited2.js 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,31 @@ +// Getting a property that's inherted from a proxy calls the proxy's get handler. + +var handler = { + get(t, id, r) { + assertEq(this, handler); + assertEq(t, target); + assertEq(id, "foo"); + assertEq(r, obj); + return "bar"; + }, + getOwnPropertyDescriptor(t, id) { + throw "FAIL"; + } +}; + +var target = {}; +var proto = new Proxy(target, handler); +var obj = Object.create(proto); +assertEq(obj.foo, "bar"); + +// Longer proto chain: same result. +var origObj = obj; +for (var i = 0; i < 4; i++) + obj = Object.create(obj); +assertEq(obj.foo, "bar"); + +// Chain of transparent proxy wrappers: same result. +obj = origObj; +for (var i = 0; i < 4; i++) + obj = new Proxy(obj, {}); +assertEq(obj.foo, "bar"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/proxy/testDirectProxyGetInherited3.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/proxy/testDirectProxyGetInherited3.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/proxy/testDirectProxyGetInherited3.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/proxy/testDirectProxyGetInherited3.js 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,21 @@ +// Recursion through the get hook works; runaway recursion is checked. + +load(libdir + "asserts.js"); + +var hits = 0, limit = 10; +var proto = new Proxy({}, { + get(t, id, r) { + assertEq(r, obj); + if (hits++ >= limit) + return "ding"; + return obj[id]; + } +}); + +var obj = Object.create(proto); +assertEq(obj.prop, "ding"); + +hits = 0; +limit = Infinity; +assertThrowsInstanceOf(() => obj.prop, InternalError); +assertEq(hits > 10, true); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/proxy/testDirectProxyGetInherited4.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/proxy/testDirectProxyGetInherited4.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/proxy/testDirectProxyGetInherited4.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/proxy/testDirectProxyGetInherited4.js 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,6 @@ +// A proxy P whose target is an object X whose prototype is an array V inherits V.length. + +var V = [1, 2, 3]; +var X = Object.create(V); +var P = new Proxy(X, {}); +assertEq(P.length, 3); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/proxy/testWrapperGetInherited.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/proxy/testWrapperGetInherited.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/proxy/testWrapperGetInherited.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/proxy/testWrapperGetInherited.js 2015-02-03 14:33:30.000000000 +0000 @@ -0,0 +1,18 @@ +// Getting a property O.X, inherited from a transparent cross-compartment wrapper W +// that wraps a Proxy P. + +var g = newGlobal(); +var target = {} +var P = new Proxy(target, { + get(t, id, r) { + assertEq(t, target); + assertEq(id, "X"); + assertEq(r, wO); + return "vega"; + } +}); + +g.W = P; +g.eval("var O = Object.create(W);"); +var wO = g.O; +assertEq(g.eval("O.X"), "vega"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/SIMD/bug1121299.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/SIMD/bug1121299.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/SIMD/bug1121299.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/SIMD/bug1121299.js 2015-02-03 14:33:26.000000000 +0000 @@ -0,0 +1,31 @@ +if (!this.hasOwnProperty("SIMD")) + quit(); + +setJitCompilerOption("baseline.warmup.trigger", 10); +setJitCompilerOption("ion.warmup.trigger", 30); + +function test_1(i) { + if (i >= 40) + return; + var a = SIMD.float32x4(1.1, 2.2, 3.3, 4.6); + SIMD.int32x4.fromFloat32x4(a); + test_1(i + 1); +} +test_1(0); + + +var float32x4 = SIMD.float32x4; +function test_2() { + var Array = float32x4.array(3); + var array = new Array([ + float32x4(1, 2, 3, 4), + float32x4(5, 6, 7, 8), + float32x4(9, 10, 11, 12) + ]); + if (typeof reportCompare === "function") + reportCompare(true, true); +} +test_2(); +evaluate("test_2(); test_2();", { + compileAndGo: true +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/SIMD/bug1123631.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/SIMD/bug1123631.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/SIMD/bug1123631.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/SIMD/bug1123631.js 2015-02-03 14:33:26.000000000 +0000 @@ -0,0 +1,9 @@ +if (!this.hasOwnProperty("SIMD")) + quit(); + +var float64x2 = SIMD.float64x2; +function test() { + var a = float64x2(1, 2); +} +test(); +test(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/SIMD/unbox.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/SIMD/unbox.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jit-test/tests/SIMD/unbox.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jit-test/tests/SIMD/unbox.js 2015-02-03 14:33:26.000000000 +0000 @@ -0,0 +1,108 @@ +if (typeof SIMD === "undefined") + quit(); + +setJitCompilerOption("baseline.warmup.trigger", 10); +setJitCompilerOption("ion.warmup.trigger", 30); +var max = 40, pivot = 35; + +var i32x4 = SIMD.int32x4; +var f32x4 = SIMD.float32x4; +var i32x4Add = SIMD.int32x4.add; + +var FakeSIMDType = function (o) { this.x = o.x; this.y = o.y; this.z = o.z; this.w = o.w; }; +if (this.hasOwnProperty("TypedObject")) { + var TO = TypedObject; + FakeSIMDType = new TO.StructType({ x: TO.int32, y: TO.int32, z: TO.int32, w: TO.int32 }); +} + + +function simdunbox_bail_undef(i, lhs, rhs) { + return i32x4Add(lhs, rhs); +} + +function simdunbox_bail_object(i, lhs, rhs) { + return i32x4Add(lhs, rhs); +} + +function simdunbox_bail_typeobj(i, lhs, rhs) { + return i32x4Add(lhs, rhs); +} + +function simdunbox_bail_badsimd(i, lhs, rhs) { + return i32x4Add(lhs, rhs); +} + +var arr_undef = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ]; +var fail_undef = 0; +var arr_object = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ]; +var fail_object = 0; +var arr_typeobj = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ]; +var fail_typeobj = 0; +var arr_badsimd = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ]; +var fail_badsimd = 0; +for (var i = 0; i < max; i++) { + try { + arr_undef[i + 2] = simdunbox_bail_undef(i, arr_undef[i], arr_undef[i + 1]); + } catch (x) { + arr_undef[i + 2] = arr_undef[i - 1]; + fail_undef++; + } + + try { + arr_object[i + 2] = simdunbox_bail_object(i, arr_object[i], arr_object[i + 1]); + } catch (x) { + arr_object[i + 2] = arr_object[i - 1]; + fail_object++; + } + + try { + arr_typeobj[i + 2] = simdunbox_bail_typeobj(i, arr_typeobj[i], arr_typeobj[i + 1]); + } catch (x) { + arr_typeobj[i + 2] = arr_typeobj[i - 1]; + fail_typeobj++; + } + + try { + arr_badsimd[i + 2] = simdunbox_bail_badsimd(i, arr_badsimd[i], arr_badsimd[i + 1]); + } catch (x) { + arr_badsimd[i + 2] = arr_badsimd[i - 1]; + fail_badsimd++; + } + + if (i + 2 == pivot) { + arr_undef[pivot] = undefined; + arr_object[pivot] = { x: 0, y: 1, z: 2, w: 3 }; + arr_typeobj[pivot] = new FakeSIMDType({ x: 0, y: 1, z: 2, w: 3 }); + arr_badsimd[pivot] = f32x4(0, 1, 2, 3); + } +} + +assertEq(fail_undef, 2); +assertEq(fail_object, 2); +assertEq(fail_typeobj, 2); +assertEq(fail_badsimd, 2); + +// Assert that all SIMD values are correct. +function assertEqX4(real, expected, assertFunc) { + if (typeof assertFunc === 'undefined') + assertFunc = assertEq; + + assertFunc(real.x, expected[0]); + assertFunc(real.y, expected[1]); + assertFunc(real.z, expected[2]); + assertFunc(real.w, expected[3]); +} + +var fib = [0, 1]; +for (i = 0; i < max + 5; i++) + fib[i+2] = (fib[i] + fib[i+1]) | 0; + +for (i = 0; i < max; i++) { + if (i == pivot) + continue; + var ref = fib.slice(i < pivot ? i : i - 3); + assertEqX4(arr_undef[i], ref); + assertEqX4(arr_object[i], ref); + assertEqX4(arr_typeobj[i], ref); + assertEqX4(arr_badsimd[i], ref); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -452,18 +452,6 @@ return mozilla::NumberIsInt32(d, ip); } -JS_PUBLIC_API(int32_t) -JS_DoubleToInt32(double d) -{ - return ToInt32(d); -} - -JS_PUBLIC_API(uint32_t) -JS_DoubleToUint32(double d) -{ - return ToUint32(d); -} - JS_PUBLIC_API(JSType) JS_TypeOfValue(JSContext *cx, HandleValue value) { @@ -1732,7 +1720,7 @@ SetNativeStackQuotaAndLimit(rt, StackForTrustedScript, trustedScriptStackSize); SetNativeStackQuotaAndLimit(rt, StackForUntrustedScript, untrustedScriptStackSize); - rt->mainThread.initJitStackLimit(); + rt->initJitStackLimit(); } /************************************************************************/ @@ -2044,12 +2032,12 @@ } JS_PUBLIC_API(JSObject *) -JS_NewObject(JSContext *cx, const JSClass *jsclasp, HandleObject proto, HandleObject parent) +JS_NewObject(JSContext *cx, const JSClass *jsclasp, HandleObject parent) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); - assertSameCompartment(cx, proto, parent); + assertSameCompartment(cx, parent); const Class *clasp = Valueify(jsclasp); if (!clasp) @@ -2058,7 +2046,7 @@ MOZ_ASSERT(clasp != &JSFunction::class_); MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL)); - JSObject *obj = NewObjectWithClassProto(cx, clasp, proto, parent); + JSObject *obj = NewObjectWithClassProto(cx, clasp, nullptr, parent); MOZ_ASSERT_IF(obj, obj->getParent()); return obj; } @@ -2078,10 +2066,7 @@ MOZ_ASSERT(clasp != &JSFunction::class_); MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL)); - JSObject *obj = NewObjectWithGivenProto(cx, clasp, proto, parent); - if (obj) - MarkTypeObjectUnknownProperties(cx, obj->type()); - return obj; + return NewObjectWithGivenProto(cx, clasp, proto, parent); } JS_PUBLIC_API(JSObject *) @@ -4285,8 +4270,7 @@ if (!script) return false; - Rooted global(cx, script->compileAndGo() ? &script->global() : nullptr); - js::Debugger::onNewScript(cx, script, global); + js::Debugger::onNewScript(cx, script); } return ExecuteScript(cx, obj, script, nullptr); } @@ -5894,7 +5878,7 @@ // If there's no accessible activation on the stack, we'll return null from // DescribeScriptedCaller anyway, so there's no need to annotate anything. - Activation *act = cx->runtime()->mainThread.activation(); + Activation *act = cx->runtime()->activation(); if (!act) return; act->hideScriptedCaller(); @@ -5903,7 +5887,7 @@ JS_PUBLIC_API(void) UnhideScriptedCaller(JSContext *cx) { - Activation *act = cx->runtime()->mainThread.activation(); + Activation *act = cx->runtime()->activation(); if (!act) return; act->unhideScriptedCaller(); @@ -5942,15 +5926,15 @@ *stackTop = this; } -#ifdef DEBUG +#ifdef JS_DEBUG JS_PUBLIC_API(void) -JS::AssertArgumentsAreSane(JSContext *cx, HandleValue value) +JS::detail::AssertArgumentsAreSane(JSContext *cx, HandleValue value) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, value); } -#endif /* DEBUG */ +#endif /* JS_DEBUG */ JS_PUBLIC_API(void *) JS_EncodeScript(JSContext *cx, HandleScript scriptArg, uint32_t *lengthp) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi.h 2015-02-03 14:33:32.000000000 +0000 @@ -13,7 +13,6 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/Range.h" #include "mozilla/RangedPtr.h" -#include "mozilla/TypedEnum.h" #include #include @@ -62,20 +61,6 @@ #endif /* JS_DEBUG */ -#ifdef JS_DEBUG -/* - * Assert that we're not doing GC on cx, that we're in a request as - * needed, and that the compartments for cx and v are correct. - * Also check that GC would be safe at this point. - */ -JS_PUBLIC_API(void) -AssertArgumentsAreSane(JSContext *cx, JS::HandleValue v); -#else -inline void AssertArgumentsAreSane(JSContext *cx, JS::HandleValue v) { - /* Do nothing */ -} -#endif /* JS_DEBUG */ - /* AutoValueArray roots an internal fixed-size array of Values. */ template class AutoValueArray : public AutoGCRooter @@ -1111,197 +1096,9 @@ extern JS_PUBLIC_API(JSString *) JS_ValueToSource(JSContext *cx, JS::Handle v); -namespace js { -/* - * DO NOT CALL THIS. Use JS::ToNumber - */ -extern JS_PUBLIC_API(bool) -ToNumberSlow(JSContext *cx, JS::Value v, double *dp); - -/* - * DO NOT CALL THIS. Use JS::ToBoolean - */ -extern JS_PUBLIC_API(bool) -ToBooleanSlow(JS::HandleValue v); - -/* - * DO NOT CALL THIS. Use JS::ToString - */ -extern JS_PUBLIC_API(JSString*) -ToStringSlow(JSContext *cx, JS::HandleValue v); - -/* - * DO NOT CALL THIS. Use JS::ToObject. - */ -extern JS_PUBLIC_API(JSObject*) -ToObjectSlow(JSContext *cx, JS::HandleValue vp, bool reportScanStack); - -} /* namespace js */ - -namespace JS { - -/* ES5 9.3 ToNumber. */ -MOZ_ALWAYS_INLINE bool -ToNumber(JSContext *cx, HandleValue v, double *out) -{ - AssertArgumentsAreSane(cx, v); - - if (v.isNumber()) { - *out = v.toNumber(); - return true; - } - return js::ToNumberSlow(cx, v, out); -} - -MOZ_ALWAYS_INLINE bool -ToBoolean(HandleValue v) -{ - if (v.isBoolean()) - return v.toBoolean(); - if (v.isInt32()) - return v.toInt32() != 0; - if (v.isNullOrUndefined()) - return false; - if (v.isDouble()) { - double d = v.toDouble(); - return !mozilla::IsNaN(d) && d != 0; - } - if (v.isSymbol()) - return true; - - /* The slow path handles strings and objects. */ - return js::ToBooleanSlow(v); -} - -MOZ_ALWAYS_INLINE JSString* -ToString(JSContext *cx, HandleValue v) -{ - if (v.isString()) - return v.toString(); - return js::ToStringSlow(cx, v); -} - -/* ES5 9.9 ToObject. */ -MOZ_ALWAYS_INLINE JSObject* -ToObject(JSContext *cx, HandleValue vp) -{ - if (vp.isObject()) - return &vp.toObject(); - return js::ToObjectSlow(cx, vp, false); -} - -/* - * Implements ES6 draft rev 28 (2014 Oct 14) 7.1.1, second algorithm. - * - * Most users should not call this -- use JS::ToNumber, ToBoolean, or ToString - * instead. This should only be called from custom convert hooks. It implements - * the default conversion behavior shared by most objects in JS, so it's useful - * as a fallback. - */ -extern JS_PUBLIC_API(bool) -OrdinaryToPrimitive(JSContext *cx, JS::HandleObject obj, JSType type, - JS::MutableHandleValue vp); - -} /* namespace JS */ - extern JS_PUBLIC_API(bool) JS_DoubleIsInt32(double d, int32_t *ip); -extern JS_PUBLIC_API(int32_t) -JS_DoubleToInt32(double d); - -extern JS_PUBLIC_API(uint32_t) -JS_DoubleToUint32(double d); - - -namespace js { -/* DO NOT CALL THIS. Use JS::ToUint16. */ -extern JS_PUBLIC_API(bool) -ToUint16Slow(JSContext *cx, JS::HandleValue v, uint16_t *out); - -/* DO NOT CALL THIS. Use JS::ToInt32. */ -extern JS_PUBLIC_API(bool) -ToInt32Slow(JSContext *cx, JS::HandleValue v, int32_t *out); - -/* DO NOT CALL THIS. Use JS::ToUint32. */ -extern JS_PUBLIC_API(bool) -ToUint32Slow(JSContext *cx, JS::HandleValue v, uint32_t *out); - -/* DO NOT CALL THIS. Use JS::ToInt64. */ -extern JS_PUBLIC_API(bool) -ToInt64Slow(JSContext *cx, JS::HandleValue v, int64_t *out); - -/* DO NOT CALL THIS. Use JS::ToUint64. */ -extern JS_PUBLIC_API(bool) -ToUint64Slow(JSContext *cx, JS::HandleValue v, uint64_t *out); -} /* namespace js */ - -namespace JS { - -MOZ_ALWAYS_INLINE bool -ToUint16(JSContext *cx, JS::HandleValue v, uint16_t *out) -{ - AssertArgumentsAreSane(cx, v); - - if (v.isInt32()) { - *out = uint16_t(v.toInt32()); - return true; - } - return js::ToUint16Slow(cx, v, out); -} - -MOZ_ALWAYS_INLINE bool -ToInt32(JSContext *cx, JS::HandleValue v, int32_t *out) -{ - AssertArgumentsAreSane(cx, v); - - if (v.isInt32()) { - *out = v.toInt32(); - return true; - } - return js::ToInt32Slow(cx, v, out); -} - -MOZ_ALWAYS_INLINE bool -ToUint32(JSContext *cx, JS::HandleValue v, uint32_t *out) -{ - AssertArgumentsAreSane(cx, v); - - if (v.isInt32()) { - *out = uint32_t(v.toInt32()); - return true; - } - return js::ToUint32Slow(cx, v, out); -} - -MOZ_ALWAYS_INLINE bool -ToInt64(JSContext *cx, JS::HandleValue v, int64_t *out) -{ - AssertArgumentsAreSane(cx, v); - - if (v.isInt32()) { - *out = int64_t(v.toInt32()); - return true; - } - return js::ToInt64Slow(cx, v, out); -} - -MOZ_ALWAYS_INLINE bool -ToUint64(JSContext *cx, JS::HandleValue v, uint64_t *out) -{ - AssertArgumentsAreSane(cx, v); - - if (v.isInt32()) { - /* Account for sign extension of negatives into the longer 64bit space. */ - *out = uint64_t(int64_t(v.toInt32())); - return true; - } - return js::ToUint64Slow(cx, v, out); -} - - -} /* namespace JS */ - extern JS_PUBLIC_API(JSType) JS_TypeOfValue(JSContext *cx, JS::Handle v); @@ -1498,6 +1295,7 @@ ion_(false), asmJS_(false), nativeRegExp_(false), + unboxedObjects_(false), werror_(false), strictMode_(false), extraWarnings_(false), @@ -1541,6 +1339,12 @@ return *this; } + bool unboxedObjects() const { return unboxedObjects_; } + RuntimeOptions &setUnboxedObjects(bool flag) { + unboxedObjects_ = flag; + return *this; + } + bool werror() const { return werror_; } RuntimeOptions &setWerror(bool flag) { werror_ = flag; @@ -1586,6 +1390,7 @@ bool ion_ : 1; bool asmJS_ : 1; bool nativeRegExp_ : 1; + bool unboxedObjects_ : 1; bool werror_ : 1; bool strictMode_ : 1; bool extraWarnings_ : 1; @@ -1952,16 +1757,6 @@ JS_SetCTypesCallbacks(JSObject *ctypesObj, const JSCTypesCallbacks *callbacks); #endif -typedef bool -(* JSEnumerateDiagnosticMemoryCallback)(void *ptr, size_t length); - -/* - * Enumerate memory regions that contain diagnostic information - * intended to be included in crash report minidumps. - */ -extern JS_PUBLIC_API(void) -JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback); - extern JS_PUBLIC_API(void *) JS_malloc(JSContext *cx, size_t nbytes); @@ -2152,7 +1947,10 @@ JSGC_MIN_EMPTY_CHUNK_COUNT = 21, /* We never keep more than this many unused chunks in the free chunk pool. */ - JSGC_MAX_EMPTY_CHUNK_COUNT = 22 + JSGC_MAX_EMPTY_CHUNK_COUNT = 22, + + /* Whether compacting GC is enabled. */ + JSGC_COMPACTING_ENABLED = 23 } JSGCParamKey; extern JS_PUBLIC_API(void) @@ -2483,8 +2281,7 @@ * * The _SYM variants allow defining a function with a symbol key rather than a * string key. For example, use JS_SYM_FN(iterator, ...) to define an - * @@iterator method. (In builds without ES6 symbols, it defines a method with - * the string id "@@iterator".) + * @@iterator method. */ #define JS_FS(name,call,nargs,flags) \ JS_FNSPEC(name, call, nullptr, nargs, flags, nullptr) @@ -2498,17 +2295,10 @@ JS_FNSPEC(name, nullptr, nullptr, nargs, flags, selfHostedName) #define JS_SELF_HOSTED_SYM_FN(symbol, selfHostedName, nargs, flags) \ JS_SYM_FNSPEC(symbol, nullptr, nullptr, nargs, flags, selfHostedName) - -#ifdef JS_HAS_SYMBOLS #define JS_SYM_FNSPEC(symbol, call, info, nargs, flags, selfHostedName) \ JS_FNSPEC(reinterpret_cast( \ uint32_t(::JS::SymbolCode::symbol) + 1), \ call, info, nargs, flags, selfHostedName) -#else -#define JS_SYM_FNSPEC(symbol, call, info, nargs, flags, selfHostedName) \ - JS_FNSPEC("@@" #symbol, call, info, nargs, flags, selfHostedName) -#endif - #define JS_FNSPEC(name,call,info,nargs,flags,selfHostedName) \ {name, {call, info}, nargs, flags, selfHostedName} @@ -2772,8 +2562,7 @@ JS_FireOnNewGlobalObject(JSContext *cx, JS::HandleObject global); extern JS_PUBLIC_API(JSObject *) -JS_NewObject(JSContext *cx, const JSClass *clasp, JS::Handle proto, - JS::Handle parent); +JS_NewObject(JSContext *cx, const JSClass *clasp, JS::Handle parent = JS::NullPtr()); /* Queries the [[Extensible]] property of the object. */ extern JS_PUBLIC_API(bool) @@ -2786,8 +2575,8 @@ JS_GetObjectRuntime(JSObject *obj); /* - * Unlike JS_NewObject, JS_NewObjectWithGivenProto does not compute a default - * proto if proto's actual parameter value is null. + * Unlike JS_NewObject, JS_NewObjectWithGivenProto does not compute a default proto. + * If proto is JS::NullPtr, the JS object will have `null` as [[Prototype]]. */ extern JS_PUBLIC_API(JSObject *) JS_NewObjectWithGivenProto(JSContext *cx, const JSClass *clasp, JS::Handle proto, @@ -4480,11 +4269,11 @@ GetSymbolDescription(HandleSymbol symbol); /* Well-known symbols. */ -MOZ_BEGIN_ENUM_CLASS(SymbolCode, uint32_t) +enum class SymbolCode : uint32_t { iterator, // well-known Symbol.iterator InSymbolRegistry = 0xfffffffe, // created by Symbol.for() or JS::GetSymbolFor() UniqueSymbol = 0xffffffff // created by Symbol() or JS::NewSymbol() -MOZ_END_ENUM_CLASS(SymbolCode) +}; /* For use in loops that iterate over the well-known symbols. */ const size_t WellKnownSymbolLimit = 1; @@ -4761,6 +4550,37 @@ SetWeakMapEntry(JSContext *cx, JS::HandleObject mapObj, JS::HandleObject key, JS::HandleValue val); +/* + * Map + */ +extern JS_PUBLIC_API(JSObject *) +NewMapObject(JSContext *cx); + +extern JS_PUBLIC_API(uint32_t) +MapSize(JSContext *cx, HandleObject obj); + +extern JS_PUBLIC_API(bool) +MapGet(JSContext *cx, HandleObject obj, + HandleValue key, MutableHandleValue rval); + +extern JS_PUBLIC_API(bool) +MapHas(JSContext *cx, HandleObject obj, HandleValue key, bool *rval); + +extern JS_PUBLIC_API(bool) +MapSet(JSContext *cx, HandleObject obj, HandleValue key, HandleValue val); + +extern JS_PUBLIC_API(bool) +MapClear(JSContext *cx, HandleObject obj); + +extern JS_PUBLIC_API(bool) +MapKeys(JSContext *cx, HandleObject obj, MutableHandleValue rval); + +extern JS_PUBLIC_API(bool) +MapValues(JSContext *cx, HandleObject obj, MutableHandleValue rval); + +extern JS_PUBLIC_API(bool) +MapEntries(JSContext *cx, HandleObject obj, MutableHandleValue rval); + } /* namespace JS */ /* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testAddPropertyPropcache.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testAddPropertyPropcache.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testAddPropertyPropcache.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testAddPropertyPropcache.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -46,7 +46,7 @@ JS::RootedObject arrObj(cx, &arr.toObject()); for (int i = 0; i < ExpectedCount; ++i) { - obj = JS_NewObject(cx, &AddPropertyClass, JS::NullPtr(), JS::NullPtr()); + obj = JS_NewObject(cx, &AddPropertyClass); CHECK(obj); CHECK(JS_DefineElement(cx, arrObj, i, obj, JSPROP_ENUMERATE, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testCallNonGenericMethodOnProxy.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testCallNonGenericMethodOnProxy.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testCallNonGenericMethodOnProxy.cpp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testCallNonGenericMethodOnProxy.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -40,7 +40,7 @@ JS::RootedObject globalA(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook)); CHECK(globalA); - JS::RootedObject customA(cx, JS_NewObject(cx, &CustomClass, JS::NullPtr(), JS::NullPtr())); + JS::RootedObject customA(cx, JS_NewObject(cx, &CustomClass)); CHECK(customA); JS_SetReservedSlot(customA, CUSTOM_SLOT, Int32Value(17)); @@ -60,7 +60,7 @@ // ...and enter it. JSAutoCompartment enter(cx, globalB); - JS::RootedObject customB(cx, JS_NewObject(cx, &CustomClass, JS::NullPtr(), JS::NullPtr())); + JS::RootedObject customB(cx, JS_NewObject(cx, &CustomClass)); CHECK(customB); JS_SetReservedSlot(customB, CUSTOM_SLOT, Int32Value(42)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testForOfIterator.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testForOfIterator.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testForOfIterator.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testForOfIterator.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -7,18 +7,12 @@ #include "jsapi-tests/tests.h" -#ifdef JS_HAS_SYMBOLS -#define STD_ITERATOR "Symbol.iterator" -#else -#define STD_ITERATOR "'@@iterator'" -#endif - BEGIN_TEST(testForOfIterator_basicNonIterable) { JS::RootedValue v(cx); // Hack to make it simple to produce an object that has a property // named Symbol.iterator. - EVAL("({[" STD_ITERATOR "]: 5})", &v); + EVAL("({[Symbol.iterator]: 5})", &v); JS::ForOfIterator iter(cx); bool ok = iter.init(v); CHECK(!ok); @@ -33,7 +27,7 @@ // Hack to make it simple to produce an object that has a property // named Symbol.iterator. - EVAL("({[" STD_ITERATOR "]: 5})", &v); + EVAL("({[Symbol.iterator]: 5})", &v); JS::ForOfIterator iter(cx); bool ok = iter.init(v, JS::ForOfIterator::AllowNonIterable); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testGCAllocator.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testGCAllocator.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testGCAllocator.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testGCAllocator.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -257,7 +257,7 @@ void * mapMemoryAt(void *desired, size_t length) { -#if defined(__ia64__) +#if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__)) MOZ_RELEASE_ASSERT(0xffff800000000000ULL & (uintptr_t(desired) + length - 1) == 0); #endif void *region = mmap(desired, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); @@ -275,13 +275,13 @@ mapMemory(size_t length) { void *hint = nullptr; -#if defined(__ia64__) +#if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__)) hint = (void*)0x0000070000000000ULL; #endif void *region = mmap(hint, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); if (region == MAP_FAILED) return nullptr; -#if defined(__ia64__) +#if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__)) if ((uintptr_t(region) + (length - 1)) & 0xffff800000000000ULL) { if (munmap(region, length)) MOZ_RELEASE_ASSERT(errno == ENOMEM); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testGCNursery.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testGCNursery.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testGCNursery.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testGCNursery.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -60,9 +60,15 @@ BEGIN_TEST(testGCNurseryFinalizer) { +#ifdef JS_GC_ZEAL + // Running extra GCs during this test will make us get incorrect + // finalization counts. + AutoLeaveZeal nozeal(cx); +#endif /* JS_GC_ZEAL */ + JS::RootedObject obj(cx); - obj = JS_NewObject(cx, Jsvalify(&TenuredClass), JS::NullPtr(), JS::NullPtr()); + obj = JS_NewObject(cx, Jsvalify(&TenuredClass)); CHECK(!js::gc::IsInsideNursery(obj)); // Null finalization list with empty nursery. @@ -79,7 +85,7 @@ CHECK(ranFinalizer == 0); // Single finalizable nursery thing. - obj = JS_NewObject(cx, Jsvalify(&NurseryClass), JS::NullPtr(), JS::NullPtr()); + obj = JS_NewObject(cx, Jsvalify(&NurseryClass)); CHECK(js::gc::IsInsideNursery(obj)); obj = nullptr; rt->gc.minorGC(JS::gcreason::EVICT_NURSERY); @@ -87,9 +93,9 @@ ranFinalizer = 0; // Multiple finalizable nursery things. - obj = JS_NewObject(cx, Jsvalify(&NurseryClass), JS::NullPtr(), JS::NullPtr()); - obj = JS_NewObject(cx, Jsvalify(&NurseryClass), JS::NullPtr(), JS::NullPtr()); - obj = JS_NewObject(cx, Jsvalify(&NurseryClass), JS::NullPtr(), JS::NullPtr()); + obj = JS_NewObject(cx, Jsvalify(&NurseryClass)); + obj = JS_NewObject(cx, Jsvalify(&NurseryClass)); + obj = JS_NewObject(cx, Jsvalify(&NurseryClass)); CHECK(js::gc::IsInsideNursery(obj)); obj = nullptr; rt->gc.minorGC(JS::gcreason::EVICT_NURSERY); @@ -98,13 +104,13 @@ // Interleaved finalizable things in nursery. obj = JS_NewPlainObject(cx); - obj = JS_NewObject(cx, Jsvalify(&NurseryClass), JS::NullPtr(), JS::NullPtr()); + obj = JS_NewObject(cx, Jsvalify(&NurseryClass)); obj = JS_NewPlainObject(cx); - obj = JS_NewObject(cx, Jsvalify(&NurseryClass), JS::NullPtr(), JS::NullPtr()); + obj = JS_NewObject(cx, Jsvalify(&NurseryClass)); obj = JS_NewPlainObject(cx); - obj = JS_NewObject(cx, Jsvalify(&NurseryClass), JS::NullPtr(), JS::NullPtr()); + obj = JS_NewObject(cx, Jsvalify(&NurseryClass)); obj = JS_NewPlainObject(cx); - obj = JS_NewObject(cx, Jsvalify(&NurseryClass), JS::NullPtr(), JS::NullPtr()); + obj = JS_NewObject(cx, Jsvalify(&NurseryClass)); obj = JS_NewPlainObject(cx); CHECK(js::gc::IsInsideNursery(obj)); obj = nullptr; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testLookup.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testLookup.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testLookup.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testLookup.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -56,8 +56,7 @@ if (!flatStr) return false; if (JS_FlatStringEqualsAscii(flatStr, "all")) { - JS::Rooted docAll(cx, - JS_NewObject(cx, &DocumentAllClass, JS::NullPtr(), JS::NullPtr())); + JS::Rooted docAll(cx, JS_NewObject(cx, &DocumentAllClass)); if (!docAll) return false; @@ -82,7 +81,7 @@ BEGIN_TEST(testLookup_bug570195) { - JS::RootedObject obj(cx, JS_NewObject(cx, &document_class, JS::NullPtr(), JS::NullPtr())); + JS::RootedObject obj(cx, JS_NewObject(cx, &document_class)); CHECK(obj); CHECK(JS_DefineProperty(cx, global, "document", obj, 0)); JS::RootedValue v(cx); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testNewObject.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testNewObject.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testNewObject.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testNewObject.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -100,7 +100,7 @@ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, constructHook }; - JS::RootedObject ctor(cx, JS_NewObject(cx, &cls, JS::NullPtr(), JS::NullPtr())); + JS::RootedObject ctor(cx, JS_NewObject(cx, &cls)); CHECK(ctor); JS::RootedValue rt2(cx, OBJECT_TO_JSVAL(ctor)); obj = JS_New(cx, ctor, JS::HandleValueArray::subarray(argv, 0, 3)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testOps.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testOps.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testOps.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testOps.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -33,7 +33,7 @@ //JS_GC(context); //<- if we make GC here, all is ok - JSObject* myObject = JS_NewObject(context, &myClass, JS::NullPtr(), JS::NullPtr()); + JSObject* myObject = JS_NewObject(context, &myClass); *vp = OBJECT_TO_JSVAL(myObject); JS_EndRequest(context); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testPersistentRooted.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testPersistentRooted.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testPersistentRooted.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testPersistentRooted.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -55,7 +55,7 @@ MOZ_NEVER_INLINE static Kennel * Allocate(JSContext *cx) { - RootedObject barker(cx, JS_NewObject(cx, &BarkWhenTracedClass::class_, JS::NullPtr(), JS::NullPtr())); + RootedObject barker(cx, JS_NewObject(cx, &BarkWhenTracedClass::class_)); if (!barker) return nullptr; @@ -196,7 +196,7 @@ CHECK(!gGlobalRoot.initialized()); { - RootedObject barker(cx, JS_NewObject(cx, &BarkWhenTracedClass::class_, JS::NullPtr(), JS::NullPtr())); + RootedObject barker(cx, JS_NewObject(cx, &BarkWhenTracedClass::class_)); CHECK(barker); gGlobalRoot.init(cx, barker); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testResolveRecursion.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testResolveRecursion.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testResolveRecursion.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testResolveRecursion.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -25,9 +25,9 @@ my_resolve }; - obj1.init(cx, JS_NewObject(cx, &my_resolve_class, JS::NullPtr(), JS::NullPtr())); + obj1.init(cx, JS_NewObject(cx, &my_resolve_class)); CHECK(obj1); - obj2.init(cx, JS_NewObject(cx, &my_resolve_class, JS::NullPtr(), JS::NullPtr())); + obj2.init(cx, JS_NewObject(cx, &my_resolve_class)); CHECK(obj2); JS_SetPrivate(obj1, this); JS_SetPrivate(obj2, this); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testSymbol.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testSymbol.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testSymbol.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testSymbol.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -4,8 +4,6 @@ #include "jsapi-tests/tests.h" -#ifdef JS_HAS_SYMBOLS - BEGIN_TEST(testSymbol_New) { using namespace JS; @@ -81,5 +79,3 @@ return true; } END_TEST(testSymbol_GetWellKnownSymbol) - -#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testWeakMap.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testWeakMap.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsapi-tests/testWeakMap.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsapi-tests/testWeakMap.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -164,11 +164,7 @@ JS_NULL_OBJECT_OPS }; - JS::RootedObject key(cx); - key = JS_NewObject(cx, - Jsvalify(&keyClass), - JS::NullPtr(), - JS::NullPtr()); + JS::RootedObject key(cx, JS_NewObject(cx, Jsvalify(&keyClass))); if (!key) return nullptr; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsarray.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsarray.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsarray.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsarray.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -179,19 +179,17 @@ if (!ToId(cx, index, &id)) return false; - RootedObject obj2(cx); - RootedShape prop(cx); - if (!LookupProperty(cx, obj, id, &obj2, &prop)) + bool found; + if (!HasProperty(cx, obj, id, &found)) return false; - if (!prop) { - vp.setUndefined(); - *hole = true; - } else { + if (found) { if (!GetProperty(cx, obj, receiver, id, vp)) return false; - *hole = false; + } else { + vp.setUndefined(); } + *hole = !found; return true; } @@ -1215,12 +1213,12 @@ } static inline bool -InitArrayTypes(JSContext *cx, TypeObject *type, const Value *vector, unsigned count) +InitArrayTypes(JSContext *cx, ObjectGroup *group, const Value *vector, unsigned count) { - if (!type->unknownProperties()) { + if (!group->unknownProperties()) { AutoEnterAnalysis enter(cx); - HeapTypeSet *types = type->getProperty(cx, JSID_VOID); + HeapTypeSet *types = group->getProperty(cx, JSID_VOID); if (!types) return false; @@ -1249,10 +1247,10 @@ if (count == 0) return true; - types::TypeObject *type = obj->getType(cx); - if (!type) + types::ObjectGroup *group = obj->getGroup(cx); + if (!group) return false; - if (updateTypes && !InitArrayTypes(cx, type, vector, count)) + if (updateTypes && !InitArrayTypes(cx, group, vector, count)) return false; /* @@ -2321,17 +2319,17 @@ } static inline void -TryReuseArrayType(JSObject *obj, ArrayObject *narr) +TryReuseArrayGroup(JSObject *obj, ArrayObject *narr) { /* - * Try to change the type of a newly created array narr to the same type + * Try to change the group of a newly created array narr to the same group * as obj. This can only be performed if the original object is an array * and has the same prototype. */ - MOZ_ASSERT(narr->getProto()->hasNewType(&ArrayObject::class_, narr->type())); + MOZ_ASSERT(narr->getProto()->hasNewGroup(&ArrayObject::class_, narr->group())); - if (obj->is() && !obj->hasSingletonType() && obj->getProto() == narr->getProto()) - narr->setType(obj->type()); + if (obj->is() && !obj->isSingleton() && obj->getProto() == narr->getProto()) + narr->setGroup(obj->group()); } /* @@ -2365,8 +2363,8 @@ * case can't happen, because any dense array used as the prototype of * another object is first slowified, for type inference's sake. */ - types::TypeObject *arrType = arr->getType(cx); - if (MOZ_UNLIKELY(!arrType || arrType->hasAllFlags(OBJECT_FLAG_ITERATED))) + types::ObjectGroup *arrGroup = arr->getGroup(cx); + if (MOZ_UNLIKELY(!arrGroup || arrGroup->hasAllFlags(OBJECT_FLAG_ITERATED))) return false; /* @@ -2437,13 +2435,13 @@ arr = NewDenseCopiedArray(cx, actualDeleteCount, obj.as(), actualStart); if (!arr) return false; - TryReuseArrayType(obj, arr); + TryReuseArrayGroup(obj, arr); } } else { arr = NewDenseFullyAllocatedArray(cx, actualDeleteCount); if (!arr) return false; - TryReuseArrayType(obj, arr); + TryReuseArrayGroup(obj, arr); RootedValue fromValue(cx); for (uint32_t k = 0; k < actualDeleteCount; k++) { @@ -2654,7 +2652,7 @@ narr = NewDenseCopiedArray(cx, initlen, aobj.as(), 0); if (!narr) return false; - TryReuseArrayType(aobj, narr); + TryReuseArrayGroup(aobj, narr); narr->setLength(cx, length); args.rval().setObject(*narr); if (argc == 0) @@ -2676,7 +2674,8 @@ HandleValue v = HandleValue::fromMarkedLocation(&p[i]); if (v.isObject()) { RootedObject obj(cx, &v.toObject()); - if (ObjectClassIs(obj, ESClass_Array, cx)) { + // This should be IsConcatSpreadable + if (IsArray(obj, cx)) { uint32_t alength; if (!GetLengthProperty(cx, obj, &alength)) return false; @@ -2716,7 +2715,7 @@ // Returns all indexed properties in the range [begin, end) found on |obj| or // its proto chain. This function does not handle proxies, objects with -// resolve/lookupGeneric hooks or indexed getters, as those can introduce +// resolve/lookupProperty hooks or indexed getters, as those can introduce // new properties. In those cases, *success is set to |false|. static bool GetIndexedPropertiesInRange(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end, @@ -2728,7 +2727,7 @@ // properties. JSObject *pobj = obj; do { - if (!pobj->isNative() || pobj->getClass()->resolve || pobj->getOps()->lookupGeneric) + if (!pobj->isNative() || pobj->getClass()->resolve || pobj->getOps()->lookupProperty) return true; } while ((pobj = pobj->getProto())); @@ -2901,7 +2900,7 @@ narr = NewDenseFullyAllocatedArray(cx, end - begin); if (!narr) return false; - TryReuseArrayType(obj, narr); + TryReuseArrayGroup(obj, narr); ArrayObject *aobj = &obj->as(); if (aobj->getDenseInitializedLength() > begin) { @@ -2917,7 +2916,7 @@ narr = NewDensePartlyAllocatedArray(cx, end - begin); if (!narr) return false; - TryReuseArrayType(obj, narr); + TryReuseArrayGroup(obj, narr); if (js::GetElementsOp op = obj->getOps()->getElements) { // Ensure that we have dense elements, so that ElementAdder::append can @@ -2983,10 +2982,10 @@ RootedObject arr(cx, NewDenseFullyAllocatedArray(cx, 0)); if (!arr) return false; - TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array); - if (!newtype) + ObjectGroup *newGroup = GetCallerInitGroup(cx, JSProto_Array); + if (!newGroup) return false; - arr->setType(newtype); + arr->setGroup(newGroup); /* Step 7. */ uint32_t k = 0; @@ -3039,7 +3038,11 @@ array_isArray(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - bool isArray = args.length() > 0 && IsObjectWithClass(args[0], ESClass_Array, cx); + bool isArray = false; + if (args.get(0).isObject()) { + RootedObject obj(cx, &args[0].toObject()); + isArray = IsArray(obj, cx); + } args.rval().setBoolean(isArray); return true; } @@ -3057,16 +3060,16 @@ } static bool -ArrayFromCallArgs(JSContext *cx, HandleTypeObject type, CallArgs &args) +ArrayFromCallArgs(JSContext *cx, HandleObjectGroup group, CallArgs &args) { - if (!InitArrayTypes(cx, type, args.array(), args.length())) + if (!InitArrayTypes(cx, group, args.array(), args.length())) return false; JSObject *obj = (args.length() == 0) ? NewDenseEmptyArray(cx) : NewDenseCopiedArray(cx, args.length(), args.array()); if (!obj) return false; - obj->setType(type); + obj->setGroup(group); args.rval().setObject(*obj); return true; } @@ -3079,10 +3082,10 @@ if (IsArrayConstructor(args.thisv()) || !IsConstructor(args.thisv())) { // IsArrayConstructor(this) will usually be true in practice. This is // the most common path. - RootedTypeObject type(cx, GetTypeCallerInitObject(cx, JSProto_Array)); - if (!type) + RootedObjectGroup group(cx, GetCallerInitGroup(cx, JSProto_Array)); + if (!group) return false; - return ArrayFromCallArgs(cx, type, args); + return ArrayFromCallArgs(cx, group, args); } // Step 4. @@ -3186,12 +3189,12 @@ js_Array(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - RootedTypeObject type(cx, GetTypeCallerInitObject(cx, JSProto_Array)); - if (!type) + RootedObjectGroup group(cx, GetCallerInitGroup(cx, JSProto_Array)); + if (!group) return false; if (args.length() != 1 || !args[0].isNumber()) - return ArrayFromCallArgs(cx, type, args); + return ArrayFromCallArgs(cx, group, args); uint32_t length; if (args[0].isInt32()) { @@ -3217,7 +3220,7 @@ AllocatingBehaviour allocating = (length <= ArrayObject::EagerAllocationMaxLength) ? NewArray_FullyAllocating : NewArray_PartlyAllocating; - RootedObject obj(cx, NewDenseArray(cx, length, type, allocating)); + RootedObject obj(cx, NewDenseArray(cx, length, group, allocating)); if (!obj) return false; @@ -3226,7 +3229,7 @@ } ArrayObject * -js::ArrayConstructorOneArg(JSContext *cx, HandleTypeObject type, int32_t lengthInt) +js::ArrayConstructorOneArg(JSContext *cx, HandleObjectGroup group, int32_t lengthInt) { if (lengthInt < 0) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); @@ -3237,7 +3240,7 @@ AllocatingBehaviour allocating = (length <= ArrayObject::EagerAllocationMaxLength) ? NewArray_FullyAllocating : NewArray_PartlyAllocating; - return NewDenseArray(cx, length, type, allocating); + return NewDenseArray(cx, length, group, allocating); } static JSObject * @@ -3248,8 +3251,8 @@ if (!proto) return nullptr; - RootedTypeObject type(cx, cx->getNewType(&ArrayObject::class_, TaggedProto(proto))); - if (!type) + RootedObjectGroup group(cx, cx->getNewGroup(&ArrayObject::class_, TaggedProto(proto))); + if (!group) return nullptr; JSObject *metadata = nullptr; @@ -3263,21 +3266,21 @@ return nullptr; RootedArrayObject arrayProto(cx, ArrayObject::createArray(cx, gc::FINALIZE_OBJECT4, - gc::TenuredHeap, shape, type, 0)); + gc::TenuredHeap, shape, group, 0)); if (!arrayProto || - !JSObject::setSingletonType(cx, arrayProto) || + !JSObject::setSingleton(cx, arrayProto) || !AddLengthProperty(cx, arrayProto)) { return nullptr; } /* - * The default 'new' type of Array.prototype is required by type inference + * The default 'new' group of Array.prototype is required by type inference * to have unknown properties, to simplify handling of e.g. heterogenous * arrays in JSON and script literals and allows setDenseArrayElement to * be used without updating the indexed type set for such default arrays. */ - if (!JSObject::setNewTypeUnknown(cx, &ArrayObject::class_, arrayProto)) + if (!JSObject::setNewGroupUnknown(cx, &ArrayObject::class_, arrayProto)) return nullptr; return arrayProto; @@ -3376,8 +3379,8 @@ if (!proto && !GetBuiltinPrototype(cxArg, JSProto_Array, &proto)) return nullptr; - RootedTypeObject type(cxArg, cxArg->getNewType(&ArrayObject::class_, TaggedProto(proto))); - if (!type) + RootedObjectGroup group(cxArg, cxArg->getNewGroup(&ArrayObject::class_, TaggedProto(proto))); + if (!group) return nullptr; JSObject *metadata = nullptr; @@ -3396,7 +3399,7 @@ RootedArrayObject arr(cxArg, ArrayObject::createArray(cxArg, allocKind, GetInitialHeap(newKind, &ArrayObject::class_), - shape, type, length)); + shape, group, length)); if (!arr) return nullptr; @@ -3407,7 +3410,7 @@ EmptyShape::insertInitialShape(cxArg, shape, proto); } - if (newKind == SingletonObject && !JSObject::setSingletonType(cxArg, arr)) + if (newKind == SingletonObject && !JSObject::setSingleton(cxArg, arr)) return nullptr; if (entry != -1 && @@ -3455,11 +3458,11 @@ } ArrayObject * -js::NewDenseArray(ExclusiveContext *cx, uint32_t length, HandleTypeObject type, +js::NewDenseArray(ExclusiveContext *cx, uint32_t length, HandleObjectGroup group, AllocatingBehaviour allocating) { - NewObjectKind newKind = !type ? SingletonObject : GenericObject; - if (type && type->shouldPreTenure()) + NewObjectKind newKind = !group ? SingletonObject : GenericObject; + if (group && group->shouldPreTenure()) newKind = TenuredObject; ArrayObject *arr; @@ -3474,11 +3477,11 @@ if (!arr) return nullptr; - if (type) - arr->setType(type); + if (group) + arr->setGroup(group); // If the length calculation overflowed, make sure that is marked for the - // new type. + // new group. if (arr->length() > INT32_MAX) arr->setLength(cx, arr->length()); @@ -3532,12 +3535,12 @@ MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_)); allocKind = GetBackgroundAllocKind(allocKind); - RootedTypeObject type(cx, templateObject->type()); + RootedObjectGroup group(cx, templateObject->group()); RootedShape shape(cx, templateObject->lastProperty()); gc::InitialHeap heap = GetInitialHeap(GenericObject, &ArrayObject::class_); Rooted arr(cx, ArrayObject::createArray(cx, allocKind, - heap, shape, type, length)); + heap, shape, group, length)); if (!arr) return nullptr; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsarray.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsarray.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsarray.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsarray.h 2015-02-03 14:33:32.000000000 +0000 @@ -80,7 +80,7 @@ * contents if the length is not excessive. */ extern ArrayObject * -NewDenseArray(ExclusiveContext *cx, uint32_t length, HandleTypeObject type, +NewDenseArray(ExclusiveContext *cx, uint32_t length, HandleObjectGroup group, AllocatingBehaviour allocating); /* Create a dense array with a copy of the dense array elements in src. */ @@ -193,7 +193,7 @@ NewbornArrayPush(JSContext *cx, HandleObject obj, const Value &v); extern ArrayObject * -ArrayConstructorOneArg(JSContext *cx, HandleTypeObject type, int32_t lengthInt); +ArrayConstructorOneArg(JSContext *cx, HandleObjectGroup group, int32_t lengthInt); } /* namespace js */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jscntxt.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jscntxt.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jscntxt.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jscntxt.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -116,7 +116,7 @@ { MOZ_ASSERT(fun->nonLazyScript()->shouldCloneAtCallsite()); MOZ_ASSERT(!fun->nonLazyScript()->enclosingStaticScope()); - MOZ_ASSERT(types::UseNewTypeForClone(fun)); + MOZ_ASSERT(types::UseSingletonForClone(fun)); /* * If we start allocating function objects in the nursery, then the callsite @@ -1047,7 +1047,7 @@ if (!savedFrameChains_.append(SavedFrameChain(compartment(), enterCompartmentDepth_))) return false; - if (Activation *act = mainThread().activation()) + if (Activation *act = runtime()->activation()) act->saveFrameChain(); setCompartment(nullptr); @@ -1065,7 +1065,7 @@ setCompartment(sfc.compartment); enterCompartmentDepth_ = sfc.enterCompartmentCount; - if (Activation *act = mainThread().activation()) + if (Activation *act = runtime()->activation()) act->restoreFrameChain(); } @@ -1190,9 +1190,10 @@ ExclusiveContext::stackLimitAddressForJitCode(StackKind kind) { #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) - return runtime_->mainThread.addressOfSimulatorStackLimit(); -#endif + return runtime_->addressOfSimulatorStackLimit(); +#else return stackLimitAddress(kind); +#endif } JSVersion diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jscntxt.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jscntxt.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jscntxt.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jscntxt.h 2015-02-03 14:33:32.000000000 +0000 @@ -241,6 +241,7 @@ void *runtimeAddressOfInterruptUint32() { return runtime_->addressOfInterruptUint32(); } void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; } void *stackLimitAddressForJitCode(StackKind kind); + uintptr_t stackLimit(StackKind kind) { return runtime_->mainThread.nativeStackLimit[kind]; } size_t gcSystemPageSize() { return gc::SystemPageSize(); } bool canUseSignalHandlers() const { return runtime_->canUseSignalHandlers(); } bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; } @@ -300,9 +301,9 @@ } // Zone local methods that can be used freely from an ExclusiveContext. - types::TypeObject *getNewType(const Class *clasp, TaggedProto proto, - JSObject *associated = nullptr); - types::TypeObject *getSingletonType(const Class *clasp, TaggedProto proto); + types::ObjectGroup *getNewGroup(const Class *clasp, TaggedProto proto, + JSObject *associated = nullptr); + types::ObjectGroup *getLazySingletonGroup(const Class *clasp, TaggedProto proto); inline js::LifoAlloc &typeLifoAlloc(); // Current global. This is only safe to use within the scope of the @@ -435,16 +436,16 @@ bool currentlyRunning() const; bool currentlyRunningInInterpreter() const { - return mainThread().activation()->isInterpreter(); + return runtime_->activation()->isInterpreter(); } bool currentlyRunningInJit() const { - return mainThread().activation()->isJit(); + return runtime_->activation()->isJit(); } js::InterpreterFrame *interpreterFrame() const { - return mainThread().activation()->asInterpreter()->current(); + return runtime_->activation()->asInterpreter()->current(); } js::InterpreterRegs &interpreterRegs() const { - return mainThread().activation()->asInterpreter()->regs(); + return runtime_->activation()->asInterpreter()->regs(); } /* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jscntxtinlines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jscntxtinlines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jscntxtinlines.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jscntxtinlines.h 2015-02-03 14:33:32.000000000 +0000 @@ -448,7 +448,7 @@ if (ppc) *ppc = nullptr; - js::Activation *act = mainThread().activation(); + js::Activation *act = runtime()->activation(); while (act && (act->cx() != this || (act->isJit() && !act->asJit()->isActive()))) act = act->prev(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jscompartment.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jscompartment.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jscompartment.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jscompartment.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -545,10 +545,10 @@ } void -JSCompartment::sweepTypeObjectTables() +JSCompartment::sweepObjectGroupTables() { - sweepNewTypeObjectTable(newTypeObjects); - sweepNewTypeObjectTable(lazyTypeObjects); + sweepNewObjectGroupTable(newObjectGroups); + sweepNewObjectGroupTable(lazyObjectGroups); } void @@ -652,9 +652,10 @@ void JSCompartment::fixupAfterMovingGC() { fixupGlobal(); - fixupNewTypeObjectTable(newTypeObjects); - fixupNewTypeObjectTable(lazyTypeObjects); + fixupNewObjectGroupTable(newObjectGroups); + fixupNewObjectGroupTable(lazyObjectGroups); fixupInitialShapeTable(); + fixupBaseShapeTable(); } void @@ -692,10 +693,10 @@ baseShapes.clear(); if (initialShapes.initialized()) initialShapes.clear(); - if (newTypeObjects.initialized()) - newTypeObjects.clear(); - if (lazyTypeObjects.initialized()) - lazyTypeObjects.clear(); + if (newObjectGroups.initialized()) + newObjectGroups.clear(); + if (lazyObjectGroups.initialized()) + lazyObjectGroups.clear(); if (savedStacks_.initialized()) savedStacks_.clear(); } @@ -819,8 +820,8 @@ tiArrayTypeTables, tiObjectTypeTables); *compartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf) + initialShapes.sizeOfExcludingThis(mallocSizeOf) - + newTypeObjects.sizeOfExcludingThis(mallocSizeOf) - + lazyTypeObjects.sizeOfExcludingThis(mallocSizeOf); + + newObjectGroups.sizeOfExcludingThis(mallocSizeOf) + + lazyObjectGroups.sizeOfExcludingThis(mallocSizeOf); *innerViewsArg += innerViews.sizeOfExcludingThis(mallocSizeOf); if (lazyArrayBuffers) *lazyArrayBuffersArg += lazyArrayBuffers->sizeOfIncludingThis(mallocSizeOf); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jscompartment.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jscompartment.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jscompartment.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jscompartment.h 2015-02-03 14:33:32.000000000 +0000 @@ -276,16 +276,17 @@ js::InitialShapeSet initialShapes; void sweepInitialShapeTable(); - /* Set of default 'new' or lazy types in the compartment. */ - js::types::NewTypeObjectTable newTypeObjects; - js::types::NewTypeObjectTable lazyTypeObjects; - void sweepNewTypeObjectTable(js::types::NewTypeObjectTable &table); + /* Set of default 'new' or lazy groups in the compartment. */ + js::types::NewObjectGroupTable newObjectGroups; + js::types::NewObjectGroupTable lazyObjectGroups; + void sweepNewObjectGroupTable(js::types::NewObjectGroupTable &table); #ifdef JSGC_HASH_TABLE_CHECKS - void checkTypeObjectTablesAfterMovingGC(); - void checkTypeObjectTableAfterMovingGC(js::types::NewTypeObjectTable &table); + void checkObjectGroupTablesAfterMovingGC(); + void checkObjectGroupTableAfterMovingGC(js::types::NewObjectGroupTable &table); void checkInitialShapesTableAfterMovingGC(); void checkWrapperMapAfterMovingGC(); + void checkBaseShapeTableAfterMovingGC(); #endif /* @@ -385,7 +386,7 @@ void sweepInnerViews(); void sweepCrossCompartmentWrappers(); - void sweepTypeObjectTables(); + void sweepObjectGroupTables(); void sweepSavedStacks(); void sweepGlobalObject(js::FreeOp *fop); void sweepSelfHostingScriptSource(); @@ -399,9 +400,10 @@ void clearTables(); void fixupInitialShapeTable(); - void fixupNewTypeObjectTable(js::types::NewTypeObjectTable &table); + void fixupNewObjectGroupTable(js::types::NewObjectGroupTable &table); void fixupAfterMovingGC(); void fixupGlobal(); + void fixupBaseShapeTable(); bool hasObjectMetadataCallback() const { return objectMetadataCallback; } void setObjectMetadataCallback(js::ObjectMetadataCallback callback); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/js-config.h.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/js-config.h.in --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/js-config.h.in 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/js-config.h.in 2015-02-03 14:33:32.000000000 +0000 @@ -31,6 +31,9 @@ /* Define to 1 if SpiderMonkey should use small chunks. */ #undef JS_GC_SMALL_CHUNK_SIZE +/* Define to 1 to perform extra assertions and heap poisoning. */ +#undef JS_CRASH_DIAGNOSTICS + /* Define to 1 if the header is present and useable. See jscpucfg.h. */ #undef JS_HAVE_ENDIAN_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jscrashformat.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jscrashformat.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jscrashformat.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jscrashformat.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * 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 jscrashformat_h -#define jscrashformat_h - -namespace js { -namespace crash { - -static const int crash_cookie_len = 16; -static const char crash_cookie[crash_cookie_len] = "*J*S*CRASHDATA*"; - -/* These values are used for CrashHeader::id. */ -enum { - JS_CRASH_STACK_GC = 0x400, - JS_CRASH_STACK_ERROR = 0x401, - JS_CRASH_RING = 0x800 -}; - -/* - * All the data here will be stored directly in the minidump, so we use - * platform-independent types. We also ensure that the size of every field is a - * multiple of 8 bytes, to guarantee that they won't be padded. - */ - -struct CrashHeader -{ - char cookie[crash_cookie_len]; - - /* id of the crash data, chosen from the enum above. */ - uint64_t id; - - explicit CrashHeader(uint64_t id) : id(id) { memcpy(cookie, crash_cookie, crash_cookie_len); } -}; - -struct CrashRegisters -{ - uint64_t ip, sp, bp; -}; - -static const int crash_buffer_size = 32 * 1024; - -struct CrashStack -{ - explicit CrashStack(uint64_t id) : header(id) {} - - CrashHeader header; - uint64_t snaptime; /* Unix time when the stack was snapshotted. */ - CrashRegisters regs; /* Register contents for the snapshot. */ - uint64_t stack_base; /* Base address of stack at the time of snapshot. */ - uint64_t stack_len; /* Extent of the stack. */ - char stack[crash_buffer_size]; /* Contents of the stack. */ -}; - -struct CrashRing -{ - explicit CrashRing(uint64_t id) : header(id), offset(0) { memset(buffer, 0, sizeof(buffer)); } - - CrashHeader header; - uint64_t offset; /* Next byte to be written in the buffer. */ - char buffer[crash_buffer_size]; -}; - -/* These are the tag values for each entry in the CrashRing. */ -enum { - JS_CRASH_TAG_GC = 0x200 -}; - -} /* namespace crash */ -} /* namespace js */ - -#endif /* jscrashformat_h */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jscrashreport.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jscrashreport.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jscrashreport.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jscrashreport.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,266 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * 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 "jscrashreport.h" - -#include - -#include "jsapi.h" -#include "jscrashformat.h" -#include "jsutil.h" - -using namespace js; -using namespace js::crash; - -#if defined(XP_WIN) - -static const int stack_snapshot_max_size = 32768; - -#include - -static bool -GetStack(uint64_t *stack, uint64_t *stack_len, CrashRegisters *regs, char *buffer, size_t size) -{ - /* Try to figure out how big the stack is. */ - char dummy; - MEMORY_BASIC_INFORMATION info; - if (VirtualQuery(reinterpret_cast(&dummy), &info, sizeof(info)) == 0) - return false; - if (info.State != MEM_COMMIT) - return false; - - /* 256 is a fudge factor to account for the rest of GetStack's frame. */ - uint64_t p = uint64_t(&dummy) - 256; - uint64_t len = stack_snapshot_max_size; - - if (p + len > uint64_t(info.BaseAddress) + info.RegionSize) - len = uint64_t(info.BaseAddress) + info.RegionSize - p; - - if (len > size) - len = size; - - *stack = p; - *stack_len = len; - - /* Get the register state. */ -#if defined(_MSC_VER) && defined(_M_IX86) - /* ASM version for win2k that doesn't support RtlCaptureContext */ - uint32_t vip, vsp, vbp; - __asm { - MyLabel: - mov [vbp], ebp; - mov [vsp], esp; - mov eax, [MyLabel]; - mov [vip], eax; - } - regs->ip = vip; - regs->sp = vsp; - regs->bp = vbp; -#else - CONTEXT context; - RtlCaptureContext(&context); -#if defined(_M_IX86) - regs->ip = context.Eip; - regs->sp = context.Esp; - regs->bp = context.Ebp; -#elif defined(_M_X64) - regs->ip = context.Rip; - regs->sp = context.Rsp; - regs->bp = context.Rbp; -#else -#error unknown cpu architecture -#endif -#endif - - js_memcpy(buffer, (void *)p, len); - - return true; -} - -#elif 0 - -#include -#include -#include - -static bool -GetStack(uint64_t *stack, uint64_t *stack_len, CrashRegisters *regs, char *buffer, size_t size) -{ - /* 256 is a fudge factor to account for the rest of GetStack's frame. */ - char dummy; - uint64_t p = uint64_t(&dummy) - 256; - uint64_t pgsz = getpagesize(); - uint64_t len = stack_snapshot_max_size; - p &= ~(pgsz - 1); - - /* Try to figure out how big the stack is. */ - while (len > 0) { - if (mlock((const void *)p, len) == 0) { - munlock((const void *)p, len); - break; - } - len -= pgsz; - } - - if (len > size) - len = size; - - *stack = p; - *stack_len = len; - - /* Get the register state. */ - ucontext_t context; - if (getcontext(&context) != 0) - return false; - -#if defined(__x86_64__) - regs->sp = (uint64_t)context.uc_mcontext.gregs[REG_RSP]; - regs->bp = (uint64_t)context.uc_mcontext.gregs[REG_RBP]; - regs->ip = (uint64_t)context.uc_mcontext.gregs[REG_RIP]; -#elif defined(__i386__) - regs->sp = (uint64_t)context.uc_mcontext.gregs[REG_ESP]; - regs->bp = (uint64_t)context.uc_mcontext.gregs[REG_EBP]; - regs->ip = (uint64_t)context.uc_mcontext.gregs[REG_EIP]; -#else -#error unknown cpu architecture -#endif - - js_memcpy(buffer, (void *)p, len); - - return true; -} - -#else - -static bool -GetStack(uint64_t *stack, uint64_t *stack_len, CrashRegisters *regs, char *buffer, size_t size) -{ - return false; -} - -#endif - -namespace js { -namespace crash { - -class Stack : private CrashStack -{ -public: - explicit Stack(uint64_t id); - - bool snapshot(); -}; - -Stack::Stack(uint64_t id) - : CrashStack(id) -{ -} - -bool -Stack::snapshot() -{ - snaptime = time(nullptr); - return GetStack(&stack_base, &stack_len, ®s, stack, sizeof(stack)); -} - -class Ring : private CrashRing -{ -public: - explicit Ring(uint64_t id); - - void push(uint64_t tag, void *data, size_t size); - -private: - size_t bufferSize() { return crash_buffer_size; } - void copyBytes(void *data, size_t size); -}; - -Ring::Ring(uint64_t id) - : CrashRing(id) -{ -} - -void -Ring::push(uint64_t tag, void *data, size_t size) -{ - uint64_t t = time(nullptr); - - copyBytes(&tag, sizeof(uint64_t)); - copyBytes(&t, sizeof(uint64_t)); - copyBytes(data, size); - uint64_t mysize = size; - copyBytes(&mysize, sizeof(uint64_t)); -} - -void -Ring::copyBytes(void *data, size_t size) -{ - if (size >= bufferSize()) - size = bufferSize(); - - if (offset + size > bufferSize()) { - size_t first = bufferSize() - offset; - size_t second = size - first; - js_memcpy(&buffer[offset], data, first); - js_memcpy(buffer, (char *)data + first, second); - offset = second; - } else { - js_memcpy(&buffer[offset], data, size); - offset += size; - } -} - -} /* namespace crash */ -} /* namespace js */ - -#ifdef JS_CRASH_DIAGNOSTICS -static bool gInitialized; - -static Stack gGCStack(JS_CRASH_STACK_GC); -static Stack gErrorStack(JS_CRASH_STACK_ERROR); -static Ring gRingBuffer(JS_CRASH_RING); -#endif - -void -js::crash::SnapshotGCStack() -{ -#ifdef JS_CRASH_DIAGNOSTICS - if (gInitialized) - gGCStack.snapshot(); -#endif -} - -void -js::crash::SnapshotErrorStack() -{ -#ifdef JS_CRASH_DIAGNOSTICS - if (gInitialized) - gErrorStack.snapshot(); -#endif -} - -void -js::crash::SaveCrashData(uint64_t tag, void *ptr, size_t size) -{ -#ifdef JS_CRASH_DIAGNOSTICS - if (gInitialized) - gRingBuffer.push(tag, ptr, size); -#endif -} - -JS_PUBLIC_API(void) -JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback) -{ -#ifdef JS_CRASH_DIAGNOSTICS - if (!gInitialized) { - gInitialized = true; - (*callback)(&gGCStack, sizeof(gGCStack)); - (*callback)(&gErrorStack, sizeof(gErrorStack)); - (*callback)(&gRingBuffer, sizeof(gRingBuffer)); - } -#endif -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jscrashreport.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jscrashreport.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jscrashreport.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jscrashreport.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * 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 jscrashreport_h -#define jscrashreport_h - -#include "mozilla/GuardObjects.h" - -#include - -namespace js { -namespace crash { - -void -SnapshotGCStack(); - -void -SnapshotErrorStack(); - -void -SaveCrashData(uint64_t tag, void *ptr, size_t size); - -template -class StackBuffer -{ - public: - explicit StackBuffer(void *data - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - - buffer[0] = marker; - buffer[1] = '['; - - for (size_t i = 0; i < size; i++) { - if (data) - buffer[i + 2] = static_cast(data)[i]; - else - buffer[i + 2] = 0; - } - - buffer[size - 2] = ']'; - buffer[size - 1] = marker; - } - - private: - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER - volatile unsigned char buffer[size + 4]; -}; - -} /* namespace crash */ -} /* namespace js */ - -#endif /* jscrashreport_h */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsdate.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsdate.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsdate.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsdate.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -2384,6 +2384,20 @@ int(msFromTime(utctime))); } +static void +print_iso_extended_string(char* buf, size_t size, double utctime) +{ + MOZ_ASSERT(TimeClip(utctime) == utctime); + JS_snprintf(buf, size, "%+.6d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ", + int(YearFromTime(utctime)), + int(MonthFromTime(utctime)) + 1, + int(DateFromTime(utctime)), + int(HourFromTime(utctime)), + int(MinFromTime(utctime)), + int(SecFromTime(utctime)), + int(msFromTime(utctime))); +} + /* ES5 B.2.6. */ MOZ_ALWAYS_INLINE bool date_toGMTString_impl(JSContext *cx, CallArgs args) @@ -2403,7 +2417,6 @@ return true; } -/* ES5 15.9.5.43. */ static bool date_toGMTString(JSContext *cx, unsigned argc, Value *vp) { @@ -2411,6 +2424,7 @@ return CallNonGenericMethod(cx, args); } +/* ES6 draft 2015-01-15 20.3.4.36. */ MOZ_ALWAYS_INLINE bool date_toISOString_impl(JSContext *cx, CallArgs args) { @@ -2421,7 +2435,11 @@ } char buf[100]; - print_iso_string(buf, sizeof buf, utctime); + int year = int(YearFromTime(utctime)); + if (year < 0 || year > 9999) + print_iso_extended_string(buf, sizeof buf, utctime); + else + print_iso_string(buf, sizeof buf, utctime); JSString *str = JS_NewStringCopyZ(cx, buf); if (!str) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsdtoa.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsdtoa.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsdtoa.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsdtoa.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -509,3 +509,6 @@ { destroydtoa(state); } + +/* Cleanup pollution from dtoa.c */ +#undef Bias diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsfriendapi.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsfriendapi.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsfriendapi.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsfriendapi.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -122,7 +122,7 @@ */ CHECK_REQUEST(cx); - if (!obj->hasSingletonType()) { + if (!obj->isSingleton()) { /* * We can see non-singleton objects when trying to splice prototypes * due to mutable __proto__ (ugh). @@ -140,9 +140,9 @@ { /* * Create our object with a null proto and then splice in the correct proto - * after we setSingletonType, so that we don't pollute the default - * TypeObject attached to our proto with information about our object, since - * we're not going to be using that TypeObject anyway. + * after we setSingleton, so that we don't pollute the default + * ObjectGroup attached to our proto with information about our object, since + * we're not going to be using that ObjectGroup anyway. */ RootedObject obj(cx, NewObjectWithGivenProto(cx, (const js::Class *)clasp, nullptr, parent, SingletonObject)); @@ -477,7 +477,7 @@ if (IsProxy(obj)) return JS_GetPrototype(cx, obj, proto); - proto.set(reinterpret_cast(obj.get())->type->proto); + proto.set(reinterpret_cast(obj.get())->group->proto); return true; } @@ -707,7 +707,7 @@ RootedFunction fun(cx, iter.maybeCallee(cx)); RootedString funname(cx); if (fun) - funname = fun->atom(); + funname = fun->displayAtom(); RootedValue thisVal(cx); if (iter.hasUsableAbstractFramePtr() && iter.computeThis(cx)) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsfriendapi.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsfriendapi.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsfriendapi.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsfriendapi.h 2015-02-03 14:33:32.000000000 +0000 @@ -9,7 +9,6 @@ #include "mozilla/Casting.h" #include "mozilla/MemoryReporting.h" -#include "mozilla/TypedEnum.h" #include "mozilla/UniquePtr.h" #include "jsapi.h" // For JSAutoByteString. See bug 1033916. @@ -298,21 +297,13 @@ JS_NULL_CLASS_SPEC, \ ext, \ { \ - js::proxy_LookupGeneric, \ js::proxy_LookupProperty, \ - js::proxy_LookupElement, \ - js::proxy_DefineGeneric, \ js::proxy_DefineProperty, \ - js::proxy_DefineElement, \ - js::proxy_GetGeneric, \ js::proxy_GetProperty, \ - js::proxy_GetElement, \ - js::proxy_SetGeneric, \ js::proxy_SetProperty, \ - js::proxy_SetElement, \ js::proxy_GetOwnPropertyDescriptor, \ - js::proxy_SetGenericAttributes, \ - js::proxy_DeleteGeneric, \ + js::proxy_SetPropertyAttributes, \ + js::proxy_DeleteProperty, \ js::proxy_Watch, js::proxy_Unwatch, \ js::proxy_GetElements, \ nullptr, /* enumerate */ \ @@ -336,49 +327,25 @@ */ extern JS_FRIEND_API(bool) -proxy_LookupGeneric(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleObject objp, +proxy_LookupProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleObject objp, JS::MutableHandle propp); extern JS_FRIEND_API(bool) -proxy_LookupProperty(JSContext *cx, JS::HandleObject obj, JS::Handle name, - JS::MutableHandleObject objp, JS::MutableHandle propp); +proxy_DefineProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue value, + JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs); extern JS_FRIEND_API(bool) -proxy_LookupElement(JSContext *cx, JS::HandleObject obj, uint32_t index, JS::MutableHandleObject objp, - JS::MutableHandle propp); -extern JS_FRIEND_API(bool) -proxy_DefineGeneric(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue value, - JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs); -extern JS_FRIEND_API(bool) -proxy_DefineProperty(JSContext *cx, JS::HandleObject obj, JS::Handle name, - JS::HandleValue value, JSPropertyOp getter, JSStrictPropertyOp setter, - unsigned attrs); -extern JS_FRIEND_API(bool) -proxy_DefineElement(JSContext *cx, JS::HandleObject obj, uint32_t index, JS::HandleValue value, - JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs); -extern JS_FRIEND_API(bool) -proxy_GetGeneric(JSContext *cx, JS::HandleObject obj, JS::HandleObject receiver, JS::HandleId id, - JS::MutableHandleValue vp); +proxy_GetProperty(JSContext *cx, JS::HandleObject obj, JS::HandleObject receiver, JS::HandleId id, + JS::MutableHandleValue vp); extern JS_FRIEND_API(bool) -proxy_GetProperty(JSContext *cx, JS::HandleObject obj, JS::HandleObject receiver, - JS::Handle name, JS::MutableHandleValue vp); -extern JS_FRIEND_API(bool) -proxy_GetElement(JSContext *cx, JS::HandleObject obj, JS::HandleObject receiver, uint32_t index, - JS::MutableHandleValue vp); -extern JS_FRIEND_API(bool) -proxy_SetGeneric(JSContext *cx, JS::HandleObject obj, JS::HandleId id, - JS::MutableHandleValue bp, bool strict); -extern JS_FRIEND_API(bool) -proxy_SetProperty(JSContext *cx, JS::HandleObject obj, JS::Handle name, +proxy_SetProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue bp, bool strict); extern JS_FRIEND_API(bool) -proxy_SetElement(JSContext *cx, JS::HandleObject obj, uint32_t index, JS::MutableHandleValue vp, - bool strict); -extern JS_FRIEND_API(bool) proxy_GetOwnPropertyDescriptor(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandle desc); extern JS_FRIEND_API(bool) -proxy_SetGenericAttributes(JSContext *cx, JS::HandleObject obj, JS::HandleId id, unsigned *attrsp); +proxy_SetPropertyAttributes(JSContext *cx, JS::HandleObject obj, JS::HandleId id, + unsigned *attrsp); extern JS_FRIEND_API(bool) -proxy_DeleteGeneric(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *succeeded); +proxy_DeleteProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *succeeded); extern JS_FRIEND_API(void) proxy_Trace(JSTracer *trc, JSObject *obj); @@ -552,7 +519,7 @@ */ namespace shadow { -struct TypeObject { +struct ObjectGroup { const Class *clasp; JSObject *proto; }; @@ -574,12 +541,12 @@ }; // This layout is shared by all objects except for Typed Objects (which still -// have a shape and type). +// have a shape and group). struct Object { - shadow::Shape *shape; - shadow::TypeObject *type; - JS::Value *slots; - void *_1; + shadow::Shape *shape; + shadow::ObjectGroup *group; + JS::Value *slots; + void *_1; size_t numFixedSlots() const { return shape->slotInfo >> Shape::FIXED_SLOTS_SHIFT; } JS::Value *fixedSlots() const { @@ -629,7 +596,7 @@ inline const js::Class * GetObjectClass(JSObject *obj) { - return reinterpret_cast(obj)->type->clasp; + return reinterpret_cast(obj)->group->clasp; } inline const JSClass * diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsfun.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsfun.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsfun.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsfun.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -585,7 +585,7 @@ script = fun->nonLazyScript(); } - if (fun->hasSingletonType()) + if (fun->isSingleton()) firstword |= HasSingletonType; atom = fun->displayAtom(); @@ -594,7 +594,7 @@ // The environment of any function which is not reused will always be // null, it is later defined when a function is cloned or reused to // mirror the scope chain. - MOZ_ASSERT_IF(fun->hasSingletonType() && + MOZ_ASSERT_IF(fun->isSingleton() && !((lazy && lazy->hasBeenCloned()) || (script && script->hasBeenCloned())), fun->environment() == nullptr); } @@ -873,19 +873,19 @@ return nullptr; functionProto->initScript(script); - types::TypeObject* protoType = functionProto->getType(cx); - if (!protoType) + types::ObjectGroup* protoGroup = functionProto->getGroup(cx); + if (!protoGroup) return nullptr; - protoType->setInterpretedFunction(functionProto); + protoGroup->setInterpretedFunction(functionProto); script->setFunction(functionProto); /* - * The default 'new' type of Function.prototype is required by type + * The default 'new' group of Function.prototype is required by type * inference to have unknown properties, to simplify handling of e.g. * CloneFunctionObject. */ - if (!JSObject::setNewTypeUnknown(cx, &JSFunction::class_, functionProto)) + if (!JSObject::setNewGroupUnknown(cx, &JSFunction::class_, functionProto)) return nullptr; // Construct the unique [[%ThrowTypeError%]] function object, used only for @@ -1481,11 +1481,11 @@ return true; } - MOZ_ASSERT(lazy->source()->hasSourceData()); + MOZ_ASSERT(lazy->scriptSource()->hasSourceData()); // Parse and compile the script from source. UncompressedSourceCache::AutoHoldEntry holder; - const char16_t *chars = lazy->source()->chars(cx, holder); + const char16_t *chars = lazy->scriptSource()->chars(cx, holder); if (!chars) return false; @@ -1996,11 +1996,11 @@ if (funobj) { MOZ_ASSERT(funobj->is()); MOZ_ASSERT(funobj->getParent() == parent); - MOZ_ASSERT_IF(native, funobj->hasSingletonType()); + MOZ_ASSERT_IF(native, funobj->isSingleton()); } else { - // Don't give asm.js module functions a singleton type since they - // are cloned (via CloneFunctionObjectIfNotSingleton) which assumes - // that hasSingletonType implies isInterpreted. + // Don't mark asm.js module functions as singleton since they are + // cloned (via CloneFunctionObjectIfNotSingleton) which assumes that + // isSingleton implies isInterpreted. if (native && !IsAsmJSModuleNative(native)) newKind = SingletonObject; funobj = NewObjectWithClassProto(cx, &JSFunction::class_, proto, @@ -2036,8 +2036,8 @@ js::CloneFunctionObjectUseSameScript(JSCompartment *compartment, HandleFunction fun) { return compartment == fun->compartment() && - !fun->hasSingletonType() && - !types::UseNewTypeForClone(fun); + !fun->isSingleton() && + !types::UseSingletonForClone(fun); } JSFunction * @@ -2094,11 +2094,11 @@ if (useSameScript) { /* - * Clone the function, reusing its script. We can use the same type as + * Clone the function, reusing its script. We can use the same group as * the original function provided that its prototype is correct. */ if (fun->getProto() == clone->getProto()) - clone->setType(fun->type()); + clone->setGroup(fun->group()); return clone; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsfuninlines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsfuninlines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsfuninlines.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsfuninlines.h 2015-02-03 14:33:32.000000000 +0000 @@ -35,7 +35,7 @@ inline bool CanReuseFunctionForClone(JSContext *cx, HandleFunction fun) { - if (!fun->hasSingletonType()) + if (!fun->isSingleton()) return false; if (fun->isInterpretedLazy()) { LazyScript *lazy = fun->lazyScript(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsgc.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsgc.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsgc.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsgc.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -286,7 +286,7 @@ sizeof(Shape), /* FINALIZE_SHAPE */ sizeof(AccessorShape), /* FINALIZE_ACCESSOR_SHAPE */ sizeof(BaseShape), /* FINALIZE_BASE_SHAPE */ - sizeof(types::TypeObject), /* FINALIZE_TYPE_OBJECT */ + sizeof(types::ObjectGroup), /* FINALIZE_OBJECT_GROUP */ sizeof(JSFatInlineString), /* FINALIZE_FAT_INLINE_STRING */ sizeof(JSString), /* FINALIZE_STRING */ sizeof(JSExternalString), /* FINALIZE_EXTERNAL_STRING */ @@ -317,7 +317,7 @@ OFFSET(Shape), /* FINALIZE_SHAPE */ OFFSET(AccessorShape), /* FINALIZE_ACCESSOR_SHAPE */ OFFSET(BaseShape), /* FINALIZE_BASE_SHAPE */ - OFFSET(types::TypeObject), /* FINALIZE_TYPE_OBJECT */ + OFFSET(types::ObjectGroup), /* FINALIZE_OBJECT_GROUP */ OFFSET(JSFatInlineString), /* FINALIZE_FAT_INLINE_STRING */ OFFSET(JSString), /* FINALIZE_STRING */ OFFSET(JSExternalString), /* FINALIZE_EXTERNAL_STRING */ @@ -382,7 +382,7 @@ FINALIZE_SHAPE, FINALIZE_ACCESSOR_SHAPE, FINALIZE_BASE_SHAPE, - FINALIZE_TYPE_OBJECT + FINALIZE_OBJECT_GROUP }; static const FinalizePhase BackgroundFinalizePhases[] = { @@ -608,8 +608,8 @@ return FinalizeTypedArenas(fop, src, dest, thingKind, budget, keepArenas); case FINALIZE_BASE_SHAPE: return FinalizeTypedArenas(fop, src, dest, thingKind, budget, keepArenas); - case FINALIZE_TYPE_OBJECT: - return FinalizeTypedArenas(fop, src, dest, thingKind, budget, keepArenas); + case FINALIZE_OBJECT_GROUP: + return FinalizeTypedArenas(fop, src, dest, thingKind, budget, keepArenas); case FINALIZE_STRING: return FinalizeTypedArenas(fop, src, dest, thingKind, budget, keepArenas); case FINALIZE_FAT_INLINE_STRING: @@ -1112,7 +1112,8 @@ sliceBudget(SliceBudget::Unlimited), incrementalAllowed(true), generationalDisabled(0), - compactingDisabled(0), + compactingEnabled(true), + compactingDisabledCount(0), manipulatingDeadZones(false), objectsMarkedInDeadZones(0), poked(false), @@ -1287,10 +1288,12 @@ GCRuntime::finish() { /* - * Wait until the background finalization stops and the helper thread - * shuts down before we forcefully release any remaining GC memory. + * Wait until the background finalization and allocation stops and the + * helper thread shuts down before we forcefully release any remaining GC + * memory. */ helperState.finish(); + allocTask.cancel(GCParallelTask::CancelAndWait); #ifdef JS_GC_ZEAL /* Free memory associated with GC verification. */ @@ -1371,6 +1374,9 @@ mode == JSGC_MODE_COMPARTMENT || mode == JSGC_MODE_INCREMENTAL); break; + case JSGC_COMPACTING_ENABLED: + compactingEnabled = value != 0; + break; default: tunables.setParameter(key, value); } @@ -1412,10 +1418,10 @@ MOZ_ASSERT(lowFrequencyHeapGrowth_ / 0.9 > 1.0); break; case JSGC_DYNAMIC_HEAP_GROWTH: - dynamicHeapGrowthEnabled_ = value; + dynamicHeapGrowthEnabled_ = value != 0; break; case JSGC_DYNAMIC_MARK_SLICE: - dynamicMarkSliceEnabled_ = value; + dynamicMarkSliceEnabled_ = value != 0; break; case JSGC_ALLOCATION_THRESHOLD: gcZoneAllocThresholdBase_ = value * 1024 * 1024; @@ -1481,6 +1487,8 @@ return tunables.minEmptyChunkCount(); case JSGC_MAX_EMPTY_CHUNK_COUNT: return tunables.maxEmptyChunkCount(); + case JSGC_COMPACTING_ENABLED: + return compactingEnabled; default: MOZ_ASSERT(key == JSGC_NUMBER); return uint32_t(number); @@ -1886,22 +1894,22 @@ GCRuntime::disableCompactingGC() { MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); - ++rt->gc.compactingDisabled; + ++compactingDisabledCount; } void GCRuntime::enableCompactingGC() { MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); - MOZ_ASSERT(compactingDisabled > 0); - --compactingDisabled; + MOZ_ASSERT(compactingDisabledCount > 0); + --compactingDisabledCount; } bool GCRuntime::isCompactingGCEnabled() { MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); - return rt->gc.compactingDisabled == 0; + return compactingEnabled && compactingDisabledCount == 0; } AutoDisableCompactingGC::AutoDisableCompactingGC(JSRuntime *rt) @@ -2121,7 +2129,8 @@ * relocate each cell within it, then add it to a list of relocated arenas. */ ArenaHeader * -ArenaList::relocateArenas(ArenaHeader *toRelocate, ArenaHeader *relocated) +ArenaList::relocateArenas(ArenaHeader *toRelocate, ArenaHeader *relocated, + gcstats::Statistics& stats) { check(); @@ -2131,6 +2140,7 @@ // Prepend to list of relocated arenas arena->next = relocated; relocated = arena; + stats.count(gcstats::STAT_ARENA_RELOCATED); } check(); @@ -2139,7 +2149,7 @@ } ArenaHeader * -ArenaLists::relocateArenas(ArenaHeader *relocatedList) +ArenaLists::relocateArenas(ArenaHeader *relocatedList, gcstats::Statistics& stats) { // Flush all the freeLists back into the arena headers purge(); @@ -2150,7 +2160,7 @@ ArenaList &al = arenaLists[i]; ArenaHeader *toRelocate = al.pickArenasToRelocate(runtime_); if (toRelocate) - relocatedList = al.relocateArenas(toRelocate, relocatedList); + relocatedList = al.relocateArenas(toRelocate, relocatedList, stats); } } @@ -2178,8 +2188,8 @@ if (CanRelocateZone(rt, zone)) { zone->setGCState(Zone::Compact); - StopAllOffThreadCompilations(zone); - relocatedList = zone->arenas.relocateArenas(relocatedList); + jit::StopAllOffThreadCompilations(zone); + relocatedList = zone->arenas.relocateArenas(relocatedList, stats); } } @@ -2215,9 +2225,9 @@ script->maybeSweepTypes(&oom); } - for (ZoneCellIterUnderGC i(zone, FINALIZE_TYPE_OBJECT); !i.done(); i.next()) { - types::TypeObject *object = i.get(); - object->maybeSweep(&oom); + for (ZoneCellIterUnderGC i(zone, FINALIZE_OBJECT_GROUP); !i.done(); i.next()) { + types::ObjectGroup *group = i.get(); + group->maybeSweep(&oom); } zone->types.endSweep(rt); @@ -2237,7 +2247,7 @@ c->sweepCrossCompartmentWrappers(); c->sweepBaseShapeTable(); c->sweepInitialShapeTable(); - c->sweepTypeObjectTables(); + c->sweepObjectGroupTables(); c->sweepRegExps(); c->sweepCallsiteClones(); c->sweepSavedStacks(); @@ -2300,8 +2310,8 @@ case FINALIZE_BASE_SHAPE: UpdateCellPointersTyped(trc, arena, traceKind); return; - case FINALIZE_TYPE_OBJECT: - UpdateCellPointersTyped(trc, arena, traceKind); + case FINALIZE_OBJECT_GROUP: + UpdateCellPointersTyped(trc, arena, traceKind); return; case FINALIZE_JITCODE: UpdateCellPointersTyped(trc, arena, traceKind); @@ -2323,7 +2333,7 @@ }; ArenasToUpdate(JSRuntime *rt, KindsToUpdate kinds); bool done() { return initialized && arena == nullptr; } - ArenaHeader* next(); + ArenaHeader* next(AutoLockHelperThreadState& lock); ArenaHeader *getArenasToUpdate(AutoLockHelperThreadState& lock, unsigned max); private: @@ -2347,7 +2357,7 @@ return false; } - if (kind > FINALIZE_OBJECT_LAST || js::gc::IsBackgroundFinalized(AllocKind(kind))) + if (js::gc::IsBackgroundFinalized(AllocKind(kind))) return (kinds & BACKGROUND) != 0; else return (kinds & FOREGROUND) != 0; @@ -2360,7 +2370,7 @@ } ArenaHeader * -ArenasToUpdate::next() +ArenasToUpdate::next(AutoLockHelperThreadState& lock) { // Find the next arena to update. // @@ -2405,7 +2415,7 @@ ArenaHeader *tail = nullptr; for (unsigned i = 0; i < count; ++i) { - ArenaHeader *arena = next(); + ArenaHeader *arena = next(lock); if (!arena) break; @@ -2490,31 +2500,46 @@ const size_t minTasks = 2; const size_t maxTasks = 8; - unsigned taskCount = Min(Max(HelperThreadState().cpuCount / 2, minTasks) + 1, - maxTasks); - UpdateCellPointersTask updateTasks[maxTasks]; + size_t targetTaskCount = HelperThreadState().cpuCount / 2; + size_t taskCount = Min(Max(targetTaskCount, minTasks), maxTasks); + UpdateCellPointersTask bgTasks[maxTasks]; + UpdateCellPointersTask fgTask; ArenasToUpdate fgArenas(rt, ArenasToUpdate::FOREGROUND); ArenasToUpdate bgArenas(rt, ArenasToUpdate::BACKGROUND); - AutoLockHelperThreadState lock; - unsigned i; - for (i = 0; i < taskCount && !bgArenas.done(); ++i) { - ArenasToUpdate *source = i == 0 ? &fgArenas : &bgArenas; - updateTasks[i].init(rt, source, lock); - startTask(updateTasks[i], gcstats::PHASE_COMPACT_UPDATE_CELLS); + + unsigned tasksStarted = 0; + { + AutoLockHelperThreadState lock; + unsigned i; + for (i = 0; i < taskCount && !bgArenas.done(); ++i) { + bgTasks[i].init(rt, &bgArenas, lock); + startTask(bgTasks[i], gcstats::PHASE_COMPACT_UPDATE_CELLS); + } + tasksStarted = i; + + fgTask.init(rt, &fgArenas, lock); } - unsigned tasksStarted = i; - for (i = 0; i < tasksStarted; ++i) - joinTask(updateTasks[i], gcstats::PHASE_COMPACT_UPDATE_CELLS); + fgTask.runFromMainThread(rt); + + { + AutoLockHelperThreadState lock; + for (unsigned i = 0; i < tasksStarted; ++i) + joinTask(bgTasks[i], gcstats::PHASE_COMPACT_UPDATE_CELLS); + } } void GCRuntime::updateAllCellPointersSerial(MovingTracer *trc) { - ArenasToUpdate allArenas(rt, ArenasToUpdate::ALL); - while (ArenaHeader *arena = allArenas.next()) - UpdateCellPointers(trc, arena); + UpdateCellPointersTask task; + { + AutoLockHelperThreadState lock; + ArenasToUpdate allArenas(rt, ArenasToUpdate::ALL); + task.init(rt, &allArenas, lock); + } + task.runFromMainThread(rt); } /* @@ -2894,7 +2919,7 @@ { gcShapeArenasToUpdate = arenaListsToSweep[FINALIZE_SHAPE]; gcAccessorShapeArenasToUpdate = arenaListsToSweep[FINALIZE_ACCESSOR_SHAPE]; - gcTypeObjectArenasToUpdate = arenaListsToSweep[FINALIZE_TYPE_OBJECT]; + gcObjectGroupArenasToUpdate = arenaListsToSweep[FINALIZE_OBJECT_GROUP]; gcScriptArenasToUpdate = arenaListsToSweep[FINALIZE_SCRIPT]; } @@ -2957,10 +2982,11 @@ return thing; // Even if allocateFromArena failed due to OOM, a background - // finalization task may be running (freeing more memory); wait for it - // to finish, then try to allocate again in case it freed up the memory - // we need. - rt->gc.waitBackgroundSweepEnd(); + // finalization or allocation task may be running freeing more memory + // or adding more available memory to our free pool; wait for them to + // finish, then try to allocate again in case they made more memory + // available. + rt->gc.waitBackgroundSweepOrAllocEnd(); thing = arenas->allocateFromArena(zone, thingKind, maybeStartBGAlloc); if (MOZ_LIKELY(thing)) @@ -4804,7 +4830,7 @@ MAKE_GC_PARALLEL_TASK(SweepCCWrappersTask); MAKE_GC_PARALLEL_TASK(SweepBaseShapesTask); MAKE_GC_PARALLEL_TASK(SweepInitialShapesTask); -MAKE_GC_PARALLEL_TASK(SweepTypeObjectsTask); +MAKE_GC_PARALLEL_TASK(SweepObjectGroupsTask); MAKE_GC_PARALLEL_TASK(SweepRegExpsTask); MAKE_GC_PARALLEL_TASK(SweepMiscTask); @@ -4843,10 +4869,10 @@ } /* virtual */ void -SweepTypeObjectsTask::run() +SweepObjectGroupsTask::run() { for (GCCompartmentGroupIter c(runtime); !c.done(); c.next()) - c->sweepTypeObjectTables(); + c->sweepObjectGroupTables(); } /* virtual */ void @@ -4917,7 +4943,7 @@ SweepCCWrappersTask sweepCCWrappersTask(rt); SweepBaseShapesTask sweepBaseShapesTask(rt); SweepInitialShapesTask sweepInitialShapesTask(rt); - SweepTypeObjectsTask sweepTypeObjectsTask(rt); + SweepObjectGroupsTask sweepObjectGroupsTask(rt); SweepRegExpsTask sweepRegExpsTask(rt); SweepMiscTask sweepMiscTask(rt); @@ -4942,7 +4968,7 @@ startTask(sweepCCWrappersTask, gcstats::PHASE_SWEEP_CC_WRAPPER); startTask(sweepBaseShapesTask, gcstats::PHASE_SWEEP_BASE_SHAPE); startTask(sweepInitialShapesTask, gcstats::PHASE_SWEEP_INITIAL_SHAPE); - startTask(sweepTypeObjectsTask, gcstats::PHASE_SWEEP_TYPE_OBJECT); + startTask(sweepObjectGroupsTask, gcstats::PHASE_SWEEP_TYPE_OBJECT); startTask(sweepRegExpsTask, gcstats::PHASE_SWEEP_REGEXP); startTask(sweepMiscTask, gcstats::PHASE_SWEEP_MISC); } @@ -5012,7 +5038,7 @@ joinTask(sweepCCWrappersTask, gcstats::PHASE_SWEEP_CC_WRAPPER); joinTask(sweepBaseShapesTask, gcstats::PHASE_SWEEP_BASE_SHAPE); joinTask(sweepInitialShapesTask, gcstats::PHASE_SWEEP_INITIAL_SHAPE); - joinTask(sweepTypeObjectsTask, gcstats::PHASE_SWEEP_TYPE_OBJECT); + joinTask(sweepObjectGroupsTask, gcstats::PHASE_SWEEP_TYPE_OBJECT); joinTask(sweepRegExpsTask, gcstats::PHASE_SWEEP_REGEXP); joinTask(sweepMiscTask, gcstats::PHASE_SWEEP_MISC); } @@ -5149,55 +5175,65 @@ return true; } -bool +GCRuntime::IncrementalProgress GCRuntime::drainMarkStack(SliceBudget &sliceBudget, gcstats::Phase phase) { /* Run a marking slice and return whether the stack is now empty. */ gcstats::AutoPhase ap(stats, phase); - return marker.drainMarkStack(sliceBudget); + return marker.drainMarkStack(sliceBudget) ? Finished : NotFinished; } -// Advance to the next entry in a list of arenas, returning false if the -// mutator should resume running. -static bool -AdvanceArenaList(ArenaHeader **list, AllocKind kind, SliceBudget &sliceBudget) +static void +SweepThing(Shape *shape) +{ + if (!shape->isMarked()) + shape->sweep(); +} + +static void +SweepThing(JSScript *script, types::AutoClearTypeInferenceStateOnOOM *oom) { - *list = (*list)->next; - sliceBudget.step(Arena::thingsPerArena(Arena::thingSize(kind))); - return !sliceBudget.isOverBudget(); + script->maybeSweepTypes(oom); } +static void +SweepThing(types::ObjectGroup *group, types::AutoClearTypeInferenceStateOnOOM *oom) +{ + group->maybeSweep(oom); +} + +template static bool -SweepShapes(ArenaHeader **arenasToSweep, AllocKind kind, SliceBudget &sliceBudget) +SweepArenaList(ArenaHeader **arenasToSweep, SliceBudget &sliceBudget, Args... args) { while (ArenaHeader *arena = *arenasToSweep) { - for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) { - Shape *shape = i.get(); - if (!shape->isMarked()) - shape->sweep(); - } + for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) + SweepThing(i.get(), args...); - if (!AdvanceArenaList(arenasToSweep, kind, sliceBudget)) + *arenasToSweep = (*arenasToSweep)->next; + AllocKind kind = MapTypeToFinalizeKind::kind; + sliceBudget.step(Arena::thingsPerArena(Arena::thingSize(kind))); + if (sliceBudget.isOverBudget()) return false; } return true; } -bool +GCRuntime::IncrementalProgress GCRuntime::sweepPhase(SliceBudget &sliceBudget) { gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP); FreeOp fop(rt); - bool finished = drainMarkStack(sliceBudget, gcstats::PHASE_SWEEP_MARK); - if (!finished) - return false; + if (drainMarkStack(sliceBudget, gcstats::PHASE_SWEEP_MARK) == NotFinished) + return NotFinished; + for (;;) { - // Sweep dead type information stored in scripts and type objects, but + // Sweep dead type information stored in scripts and object groups, but // don't finalize them yet. We have to sweep dead information from both - // live and dead scripts and type objects, so that no dead references + // live and dead scripts and object groups, so that no dead references // remain in them. Type inference can end up crawling these zones // again, such as for TypeCompartment::markSetsUnknown, and if this // happens after sweeping for the zone group finishes we won't be able @@ -5211,30 +5247,13 @@ types::AutoClearTypeInferenceStateOnOOM oom(sweepZone); - while (ArenaHeader *arena = al.gcScriptArenasToUpdate) { - for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) { - JSScript *script = i.get(); - script->maybeSweepTypes(&oom); - } - - if (!AdvanceArenaList(&al.gcScriptArenasToUpdate, - FINALIZE_SCRIPT, sliceBudget)) - { - return false; - } - } + if (!SweepArenaList(&al.gcScriptArenasToUpdate, sliceBudget, &oom)) + return NotFinished; - while (ArenaHeader *arena = al.gcTypeObjectArenasToUpdate) { - for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) { - types::TypeObject *object = i.get(); - object->maybeSweep(&oom); - } - - if (!AdvanceArenaList(&al.gcTypeObjectArenasToUpdate, - FINALIZE_TYPE_OBJECT, sliceBudget)) - { - return false; - } + if (!SweepArenaList( + &al.gcObjectGroupArenasToUpdate, sliceBudget, &oom)) + { + return NotFinished; } // Finish sweeping type information in the zone. @@ -5269,7 +5288,7 @@ if (!zone->arenas.foregroundFinalize(&fop, kind, sliceBudget, incrementalSweepList)) - return false; /* Yield to the mutator. */ + return NotFinished; /* Reset the slots of the sweep list that we used. */ incrementalSweepList.reset(thingsPerArena); @@ -5286,21 +5305,21 @@ gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_SHAPE); for (; sweepZone; sweepZone = sweepZone->nextNodeInGroup()) { - Zone *zone = sweepZone; - if (!SweepShapes(&zone->arenas.gcShapeArenasToUpdate, FINALIZE_SHAPE, sliceBudget)) - return false; /* Yield to the mutator. */ - if (!SweepShapes(&zone->arenas.gcAccessorShapeArenasToUpdate, - FINALIZE_ACCESSOR_SHAPE, sliceBudget)) - { - return false; /* Yield to the mutator. */ - } + ArenaLists &al = sweepZone->arenas; + + if (!SweepArenaList(&al.gcShapeArenasToUpdate, sliceBudget)) + return NotFinished; + + if (!SweepArenaList(&al.gcAccessorShapeArenasToUpdate, sliceBudget)) + return NotFinished; } } endSweepingZoneGroup(); getNextZoneGroup(); if (!currentZoneGroup) - return true; /* We're finished. */ + return Finished; + endMarkingZoneGroup(); beginSweepingZoneGroup(); } @@ -5420,7 +5439,7 @@ #endif } -bool +GCRuntime::IncrementalProgress GCRuntime::compactPhase(bool lastGC) { gcstats::AutoPhase ap(stats, gcstats::PHASE_COMPACT); @@ -5429,7 +5448,7 @@ // Poll for end of background sweeping AutoLockGC lock(rt); if (isBackgroundSweeping()) - return false; + return NotFinished; } else { waitBackgroundSweepEnd(); } @@ -5487,7 +5506,7 @@ } } #endif - return true; + return Finished; } void @@ -5806,15 +5825,14 @@ /* fall through */ - case MARK: { + case MARK: /* If we needed delayed marking for gray roots, then collect until done. */ if (!marker.hasBufferedGrayRoots()) { budget.makeUnlimited(); isIncremental = false; } - bool finished = drainMarkStack(budget, gcstats::PHASE_MARK); - if (!finished) + if (drainMarkStack(budget, gcstats::PHASE_MARK) == NotFinished) break; MOZ_ASSERT(marker.isDrained()); @@ -5850,14 +5868,10 @@ break; /* fall through */ - } case SWEEP: - { - bool finished = sweepPhase(budget); - if (!finished) - break; - } + if (sweepPhase(budget) == NotFinished) + break; endSweepPhase(lastGC); @@ -5868,12 +5882,8 @@ break; case COMPACT: - if (shouldCompact()) { - bool finished = compactPhase(lastGC); - if (!finished) - break; - } - + if (shouldCompact() && compactPhase(lastGC) == NotFinished) + break; finishCollection(); incrementalState = NO_INCREMENTAL; break; @@ -6362,27 +6372,27 @@ } void -GCRuntime::minorGCImpl(JS::gcreason::Reason reason, Nursery::TypeObjectList *pretenureTypes) +GCRuntime::minorGCImpl(JS::gcreason::Reason reason, Nursery::ObjectGroupList *pretenureGroups) { minorGCTriggerReason = JS::gcreason::NO_REASON; TraceLoggerThread *logger = TraceLoggerForMainThread(rt); AutoTraceLog logMinorGC(logger, TraceLogger_MinorGC); - nursery.collect(rt, reason, pretenureTypes); + nursery.collect(rt, reason, pretenureGroups); MOZ_ASSERT_IF(!rt->mainThread.suppressGC, nursery.isEmpty()); } -// Alternate to the runtime-taking form that allows marking type objects as +// Alternate to the runtime-taking form that allows marking object groups as // needing pretenuring. void GCRuntime::minorGC(JSContext *cx, JS::gcreason::Reason reason) { gcstats::AutoPhase ap(stats, gcstats::PHASE_MINOR_GC); - Nursery::TypeObjectList pretenureTypes; - minorGCImpl(reason, &pretenureTypes); - for (size_t i = 0; i < pretenureTypes.length(); i++) { - if (pretenureTypes[i]->canPreTenure()) - pretenureTypes[i]->setShouldPreTenure(cx); + Nursery::ObjectGroupList pretenureGroups; + minorGCImpl(reason, &pretenureGroups); + for (size_t i = 0; i < pretenureGroups.length(); i++) { + if (pretenureGroups[i]->canPreTenure()) + pretenureGroups[i]->setShouldPreTenure(cx); } } @@ -6531,9 +6541,9 @@ base->compartment_ = target; } - for (ZoneCellIter iter(source->zone(), FINALIZE_TYPE_OBJECT); !iter.done(); iter.next()) { - types::TypeObject *type = iter.get(); - type->setGeneration(target->zone()->types.generation); + for (ZoneCellIter iter(source->zone(), FINALIZE_OBJECT_GROUP); !iter.done(); iter.next()) { + types::ObjectGroup *group = iter.get(); + group->setGeneration(target->zone()->types.generation); } // Fixup zone pointers in source's zone to refer to target's zone. @@ -6922,7 +6932,7 @@ case JSTRACE_BASE_SHAPE: return "BaseShape"; case JSTRACE_LAZY_SCRIPT: return "LazyScript"; case JSTRACE_JITCODE: return "JitCode"; - case JSTRACE_TYPE_OBJECT: return "TypeObject"; + case JSTRACE_OBJECT_GROUP: return "ObjectGroup"; default: return "Invalid"; } } @@ -6957,9 +6967,10 @@ * that have been moved. */ for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { - c->checkTypeObjectTablesAfterMovingGC(); + c->checkObjectGroupTablesAfterMovingGC(); c->checkInitialShapesTableAfterMovingGC(); c->checkWrapperMapAfterMovingGC(); + c->checkBaseShapeTableAfterMovingGC(); if (c->debugScopes) c->debugScopes->checkHashTablesAfterMovingGC(rt); } @@ -7064,18 +7075,6 @@ return rt->gc.isIncrementalGCEnabled(); } -JS_PUBLIC_API(void) -JS::DisableCompactingGC(JSRuntime *rt) -{ - rt->gc.disableCompactingGC(); -} - -JS_PUBLIC_API(bool) -JS::IsCompactingGCEnabled(JSRuntime *rt) -{ - return rt->gc.isCompactingGCEnabled(); -} - JS_PUBLIC_API(bool) JS::IsIncrementalGCInProgress(JSRuntime *rt) { @@ -7123,8 +7122,8 @@ return Shape::writeBarrierPre(static_cast(thing.asCell())); case JSTRACE_BASE_SHAPE: return BaseShape::writeBarrierPre(static_cast(thing.asCell())); - case JSTRACE_TYPE_OBJECT: - return types::TypeObject::writeBarrierPre(static_cast(thing.asCell())); + case JSTRACE_OBJECT_GROUP: + return types::ObjectGroup::writeBarrierPre(static_cast(thing.asCell())); default: MOZ_CRASH("Invalid trace kind in IncrementalReferenceBarrier."); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsgc.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsgc.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsgc.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsgc.h 2015-02-03 14:33:32.000000000 +0000 @@ -45,6 +45,10 @@ class JitCode; } +namespace gcstats { +struct Statistics; +} + namespace gc { struct FinalizePhase; @@ -64,7 +68,7 @@ template <> struct MapTypeToFinalizeKind { static const AllocKind kind = FINALIZE_SHAPE; }; template <> struct MapTypeToFinalizeKind { static const AllocKind kind = FINALIZE_ACCESSOR_SHAPE; }; template <> struct MapTypeToFinalizeKind { static const AllocKind kind = FINALIZE_BASE_SHAPE; }; -template <> struct MapTypeToFinalizeKind { static const AllocKind kind = FINALIZE_TYPE_OBJECT; }; +template <> struct MapTypeToFinalizeKind { static const AllocKind kind = FINALIZE_OBJECT_GROUP; }; template <> struct MapTypeToFinalizeKind { static const AllocKind kind = FINALIZE_FAT_INLINE_STRING; }; template <> struct MapTypeToFinalizeKind { static const AllocKind kind = FINALIZE_STRING; }; template <> struct MapTypeToFinalizeKind { static const AllocKind kind = FINALIZE_EXTERNAL_STRING; }; @@ -93,7 +97,7 @@ false, /* FINALIZE_SHAPE */ false, /* FINALIZE_ACCESSOR_SHAPE */ false, /* FINALIZE_BASE_SHAPE */ - false, /* FINALIZE_TYPE_OBJECT */ + false, /* FINALIZE_OBJECT_GROUP */ false, /* FINALIZE_FAT_INLINE_STRING */ false, /* FINALIZE_STRING */ false, /* FINALIZE_EXTERNAL_STRING */ @@ -126,7 +130,7 @@ true, /* FINALIZE_SHAPE */ true, /* FINALIZE_ACCESSOR_SHAPE */ true, /* FINALIZE_BASE_SHAPE */ - true, /* FINALIZE_TYPE_OBJECT */ + true, /* FINALIZE_OBJECT_GROUP */ true, /* FINALIZE_FAT_INLINE_STRING */ true, /* FINALIZE_STRING */ false, /* FINALIZE_EXTERNAL_STRING */ @@ -192,6 +196,22 @@ return slotsToThingKind[numFixedSlots]; } +// Get the best kind to use when allocating an object that needs a specific +// number of bytes. +static inline AllocKind +GetGCObjectKindForBytes(size_t nbytes) +{ + MOZ_ASSERT(nbytes <= JSObject::MAX_BYTE_SIZE); + + if (nbytes <= sizeof(NativeObject)) + return FINALIZE_OBJECT0; + nbytes -= sizeof(NativeObject); + + size_t dataSlots = AlignBytes(nbytes, sizeof(Value)) / sizeof(Value); + MOZ_ASSERT(nbytes <= dataSlots * sizeof(Value)); + return GetGCObjectKind(dataSlots); +} + static inline AllocKind GetBackgroundAllocKind(AllocKind kind) { @@ -456,7 +476,8 @@ ArenaHeader *removeRemainingArenas(ArenaHeader **arenap, const AutoLockGC &lock); ArenaHeader *pickArenasToRelocate(JSRuntime *runtime); - ArenaHeader *relocateArenas(ArenaHeader *toRelocate, ArenaHeader *relocated); + ArenaHeader *relocateArenas(ArenaHeader *toRelocate, ArenaHeader *relocated, + gcstats::Statistics& stats); }; /* @@ -584,7 +605,7 @@ ArenaHeader *gcShapeArenasToUpdate; ArenaHeader *gcAccessorShapeArenasToUpdate; ArenaHeader *gcScriptArenasToUpdate; - ArenaHeader *gcTypeObjectArenasToUpdate; + ArenaHeader *gcObjectGroupArenasToUpdate; // While sweeping type information, these lists save the arenas for the // objects which have already been finalized in the foreground (which must @@ -605,7 +626,7 @@ gcShapeArenasToUpdate = nullptr; gcAccessorShapeArenasToUpdate = nullptr; gcScriptArenasToUpdate = nullptr; - gcTypeObjectArenasToUpdate = nullptr; + gcObjectGroupArenasToUpdate = nullptr; savedEmptyObjectArenas = nullptr; } @@ -783,7 +804,7 @@ MOZ_ASSERT(freeLists[kind].isEmpty()); } - ArenaHeader *relocateArenas(ArenaHeader *relocatedList); + ArenaHeader *relocateArenas(ArenaHeader *relocatedList, gcstats::Statistics& stats); void queueForegroundObjectsForSweep(FreeOp *fop); void queueForegroundThingsForSweep(FreeOp *fop); @@ -1044,9 +1065,6 @@ : thing(thing), kind(kind) {} }; -void -MarkStackRangeConservatively(JSTracer *trc, Value *begin, Value *end); - typedef void (*IterateChunkCallback)(JSRuntime *rt, void *data, gc::Chunk *chunk); typedef void (*IterateZoneCallback)(JSRuntime *rt, void *data, JS::Zone *zone); typedef void (*IterateArenaCallback)(JSRuntime *rt, void *data, gc::Arena *arena, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsgcinlines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsgcinlines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsgcinlines.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsgcinlines.h 2015-02-03 14:33:32.000000000 +0000 @@ -614,10 +614,10 @@ } inline -types::TypeObject * -NewTypeObject(js::ExclusiveContext *cx) +types::ObjectGroup * +NewObjectGroup(js::ExclusiveContext *cx) { - return gc::AllocateNonObject(cx); + return gc::AllocateNonObject(cx); } template diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsinfer.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsinfer.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsinfer.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsinfer.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -30,6 +30,7 @@ #include "vm/HelperThreads.h" #include "vm/Opcodes.h" #include "vm/Shape.h" +#include "vm/UnboxedObject.h" #include "jsatominlines.h" #include "jsgcinlines.h" @@ -202,18 +203,18 @@ static unsigned which = 0; which = (which + 1) & 3; - if (type.isSingleObject()) - JS_snprintf(bufs[which], 40, "<0x%p>", (void *) type.singleObject()); + if (type.isSingleton()) + JS_snprintf(bufs[which], 40, "<0x%p>", (void *) type.singleton()); else - JS_snprintf(bufs[which], 40, "[0x%p]", (void *) type.typeObject()); + JS_snprintf(bufs[which], 40, "[0x%p]", (void *) type.group()); return bufs[which]; } const char * -types::TypeObjectString(TypeObject *type) +types::ObjectGroupString(ObjectGroup *group) { - return TypeString(Type::ObjectType(type)); + return TypeString(Type::ObjectType(group)); } unsigned JSScript::id() { @@ -240,13 +241,13 @@ } bool -types::TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value) +types::TypeHasProperty(JSContext *cx, ObjectGroup *group, jsid id, const Value &value) { /* * Check the correctness of the type information in the object's property * against an actual value. */ - if (!obj->unknownProperties() && !value.isUndefined()) { + if (!group->unknownProperties() && !value.isUndefined()) { id = IdToTypeId(id); /* Watch for properties which inference does not monitor. */ @@ -255,11 +256,11 @@ Type type = GetValueType(value); - // Type set guards might miss when an object's type changes and its + // Type set guards might miss when an object's group changes and its // properties become unknown. if (value.isObject() && - !value.toObject().hasLazyType() && - value.toObject().type()->flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES) + !value.toObject().hasLazyGroup() && + value.toObject().group()->flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES) { return true; } @@ -271,13 +272,13 @@ * haven't yet been accessed during analysis of the inheriting object. * Don't do the property instantiation now. */ - TypeSet *types = obj->maybeGetProperty(id); + TypeSet *types = group->maybeGetProperty(id); if (!types) return true; if (!types->hasType(type)) { TypeFailure(cx, "Missing type in object %s %s: %s", - TypeObjectString(obj), TypeIdString(id), TypeString(type)); + ObjectGroupString(group), TypeIdString(id), TypeString(type)); } } return true; @@ -319,16 +320,16 @@ flags |= TYPE_FLAG_INT32; } else if (type.isAnyObject()) { flags |= TYPE_FLAG_ANYOBJECT; - } else if (type.isTypeObject() && type.typeObject()->unknownProperties()) { + } else if (type.isGroup() && type.group()->unknownProperties()) { flags |= TYPE_FLAG_ANYOBJECT; } else { setBaseObjectCount(1); - objectSet = reinterpret_cast(type.objectKey()); + objectSet = reinterpret_cast(type.objectKey()); - if (type.isTypeObject()) { - TypeObject *nobject = type.typeObject(); - if (nobject->newScript() && nobject->newScript()->initializedType()) - addType(Type::ObjectType(nobject->newScript()->initializedType()), alloc); + if (type.isGroup()) { + ObjectGroup *ngroup = type.group(); + if (ngroup->newScript() && ngroup->newScript()->initializedGroup()) + addType(Type::ObjectType(ngroup->newScript()->initializedGroup()), alloc); } } } @@ -387,10 +388,10 @@ return false; for (unsigned i = 0; i < getObjectCount(); i++) { - TypeObjectKey *obj = getObject(i); - if (!obj) + ObjectGroupKey *key = getObject(i); + if (!key) continue; - if (!other->hasType(Type::ObjectType(obj))) + if (!other->hasType(Type::ObjectType(key))) return false; } @@ -407,10 +408,10 @@ MOZ_ASSERT(other->unknownObject()); } else { for (unsigned i = 0; i < getObjectCount(); i++) { - TypeObjectKey *obj = getObject(i); - if (!obj) + ObjectGroupKey *key = getObject(i); + if (!key) continue; - if (!other->hasType(Type::ObjectType(obj))) + if (!other->hasType(Type::ObjectType(key))) return false; } } @@ -419,7 +420,7 @@ } bool -TypeSet::enumerateTypes(TypeList *list) +TypeSet::enumerateTypes(TypeList *list) const { /* If any type is possible, there's no need to worry about specifics. */ if (flags & TYPE_FLAG_UNKNOWN) @@ -441,9 +442,9 @@ /* Enqueue specific object types. */ unsigned count = getObjectCount(); for (unsigned i = 0; i < count; i++) { - TypeObjectKey *object = getObject(i); - if (object) { - if (!list->append(Type::ObjectType(object))) + ObjectGroupKey *key = getObject(i); + if (key) { + if (!list->append(Type::ObjectType(key))) return false; } } @@ -532,14 +533,14 @@ { uint32_t objectCount = baseObjectCount(); - TypeObjectKey *object = type.objectKey(); - TypeObjectKey **pentry = HashSetInsert - (*alloc, objectSet, objectCount, object); + ObjectGroupKey *key = type.objectKey(); + ObjectGroupKey **pentry = HashSetInsert + (*alloc, objectSet, objectCount, key); if (!pentry) goto unknownObject; if (*pentry) return; - *pentry = object; + *pentry = key; setBaseObjectCount(objectCount); @@ -560,7 +561,7 @@ } // Make sure the newly added object is also a DOM object. - if (!object->clasp()->isDOMClass()) + if (!key->clasp()->isDOMClass()) goto unknownObject; // Limit the number of DOM objects. @@ -569,17 +570,17 @@ } } - if (type.isTypeObject()) { - TypeObject *nobject = type.typeObject(); - MOZ_ASSERT(!nobject->singleton()); - if (nobject->unknownProperties()) + if (type.isGroup()) { + ObjectGroup *ngroup = type.group(); + MOZ_ASSERT(!ngroup->singleton()); + if (ngroup->unknownProperties()) goto unknownObject; - // If we add a partially initialized type to a type set, add the - // corresponding fully initialized type, as an object's type may change + // If we add a partially initialized group to a type set, add the + // corresponding fully initialized group, as an object's group may change // from the former to the latter via the acquired properties analysis. - if (nobject->newScript() && nobject->newScript()->initializedType()) - addType(Type::ObjectType(nobject->newScript()->initializedType()), alloc); + if (ngroup->newScript() && ngroup->newScript()->initializedGroup()) + addType(Type::ObjectType(ngroup->newScript()->initializedGroup()), alloc); } if (false) { @@ -663,9 +664,9 @@ unsigned count = getObjectCount(); for (unsigned i = 0; i < count; i++) { - TypeObjectKey *object = getObject(i); - if (object) - fprintf(stderr, " %s", TypeString(Type::ObjectType(object))); + ObjectGroupKey *key = getObject(i); + if (key) + fprintf(stderr, " %s", TypeString(Type::ObjectType(key))); } } } @@ -677,11 +678,11 @@ return; for (unsigned i = 0; i < types->getObjectCount(); i++) { - if (TypeObjectKey *object = types->getObject(i)) { - if (object->isSingleObject()) - (void) object->asSingleObject(); + if (ObjectGroupKey *key = types->getObject(i)) { + if (key->isSingleton()) + (void) key->singleton(); else - (void) object->asTypeObject(); + (void) key->group(); } } } @@ -694,9 +695,9 @@ unsigned objectCount = baseObjectCount(); unsigned capacity = (objectCount >= 2) ? HashSetCapacity(objectCount) : 0; - TypeObjectKey **newSet; + ObjectGroupKey **newSet; if (capacity) { - newSet = alloc->newArray(capacity); + newSet = alloc->newArray(capacity); if (!newSet) return false; PodCopy(newSet, objectSet, capacity); @@ -759,17 +760,17 @@ TypeSet::unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc) { TemporaryTypeSet *res = alloc->new_(a->baseFlags() | b->baseFlags(), - static_cast(nullptr)); + static_cast(nullptr)); if (!res) return nullptr; if (!res->unknownObject()) { for (size_t i = 0; i < a->getObjectCount() && !res->unknownObject(); i++) { - if (TypeObjectKey *key = a->getObject(i)) + if (ObjectGroupKey *key = a->getObject(i)) res->addType(Type::ObjectType(key), alloc); } for (size_t i = 0; i < b->getObjectCount() && !res->unknownObject(); i++) { - if (TypeObjectKey *key = b->getObject(i)) + if (ObjectGroupKey *key = b->getObject(i)) res->addType(Type::ObjectType(key), alloc); } } @@ -782,7 +783,7 @@ { TemporaryTypeSet *res; res = alloc->new_(a->baseFlags() & b->baseFlags(), - static_cast(nullptr)); + static_cast(nullptr)); if (!res) return nullptr; @@ -1025,11 +1026,11 @@ cx->zone()->types.addPendingRecompile(cx, compilation); } - void newObjectState(JSContext *cx, TypeObject *object) { + void newObjectState(JSContext *cx, ObjectGroup *group) { // Note: Once the object has unknown properties, no more notifications // will be sent on changes to its state, so always invalidate any // associated compilations. - if (object->unknownProperties() || data.invalidateOnNewObjectState(object)) + if (group->unknownProperties() || data.invalidateOnNewObjectState(group)) cx->zone()->types.addPendingRecompile(cx, compilation); } @@ -1061,85 +1062,86 @@ } /* anonymous namespace */ const Class * -TypeObjectKey::clasp() +ObjectGroupKey::clasp() { - return isTypeObject() ? asTypeObject()->clasp() : asSingleObject()->getClass(); + return isGroup() ? group()->clasp() : singleton()->getClass(); } TaggedProto -TypeObjectKey::proto() +ObjectGroupKey::proto() { MOZ_ASSERT(hasTenuredProto()); - return isTypeObject() ? asTypeObject()->proto() : asSingleObject()->getTaggedProto(); + return isGroup() ? group()->proto() : singleton()->getTaggedProto(); } -bool -JSObject::hasTenuredProto() const +TaggedProto +ObjectGroupKey::protoMaybeInNursery() { - return type_->hasTenuredProto(); + return isGroup() ? group()->proto() : singleton()->getTaggedProto(); } bool -TypeObjectKey::hasTenuredProto() +JSObject::hasTenuredProto() const { - return isTypeObject() ? asTypeObject()->hasTenuredProto() : asSingleObject()->hasTenuredProto(); + return group_->hasTenuredProto(); } -JSObject * -TypeObjectKey::singleton() +bool +ObjectGroupKey::hasTenuredProto() { - return isTypeObject() ? asTypeObject()->singleton() : asSingleObject(); + return isGroup() ? group()->hasTenuredProto() : singleton()->hasTenuredProto(); } TypeNewScript * -TypeObjectKey::newScript() +ObjectGroupKey::newScript() { - if (isTypeObject() && asTypeObject()->newScript()) - return asTypeObject()->newScript(); + if (isGroup() && group()->newScript()) + return group()->newScript(); return nullptr; } -TypeObject * -TypeObjectKey::maybeType() +ObjectGroup * +ObjectGroupKey::maybeGroup() { - if (isTypeObject()) - return asTypeObject(); - if (asSingleObject()->hasLazyType()) - return nullptr; - return asSingleObject()->type(); + if (isGroup()) + return group(); + if (!singleton()->hasLazyGroup()) + return singleton()->group(); + return nullptr; } bool -TypeObjectKey::unknownProperties() +ObjectGroupKey::unknownProperties() { - if (TypeObject *type = maybeType()) - return type->unknownProperties(); + if (ObjectGroup *group = maybeGroup()) + return group->unknownProperties(); return false; } HeapTypeSetKey -TypeObjectKey::property(jsid id) +ObjectGroupKey::property(jsid id) { MOZ_ASSERT(!unknownProperties()); HeapTypeSetKey property; property.object_ = this; property.id_ = id; - if (TypeObject *type = maybeType()) - property.maybeTypes_ = type->maybeGetProperty(id); + if (ObjectGroup *group = maybeGroup()) + property.maybeTypes_ = group->maybeGetProperty(id); return property; } void -TypeObjectKey::ensureTrackedProperty(JSContext *cx, jsid id) +ObjectGroupKey::ensureTrackedProperty(JSContext *cx, jsid id) { // If we are accessing a lazily defined property which actually exists in // the VM and has not been instantiated yet, instantiate it now if we are // on the main thread and able to do so. if (!JSID_IS_VOID(id) && !JSID_IS_EMPTY(id)) { MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime())); - if (JSObject *obj = singleton()) { + if (isSingleton()) { + JSObject *obj = singleton(); if (obj->isNative() && obj->as().containsPure(id)) EnsureTrackPropertyTypes(cx, obj, id); } @@ -1151,11 +1153,11 @@ { if (maybeTypes()) return true; - if (object()->isSingleObject() && !object()->asSingleObject()->getType(cx)) { + if (object()->isSingleton() && !object()->singleton()->getGroup(cx)) { cx->clearPendingException(); return false; } - maybeTypes_ = object()->maybeType()->getProperty(cx, id()); + maybeTypes_ = object()->maybeGroup()->getProperty(cx, id()); return maybeTypes_ != nullptr; } @@ -1384,7 +1386,7 @@ bool invalidateOnNewType(Type type) { return true; } bool invalidateOnNewPropertyState(TypeSet *property) { return true; } - bool invalidateOnNewObjectState(TypeObject *object) { return false; } + bool invalidateOnNewObjectState(ObjectGroup *group) { return false; } bool constraintHolds(JSContext *cx, const HeapTypeSetKey &property, TemporaryTypeSet *expected) @@ -1496,11 +1498,13 @@ { if (maybeTypes() && (!maybeTypes()->empty() || maybeTypes()->nonDataProperty())) return true; - JSObject *obj = object()->singleton(); - MOZ_ASSERT_IF(obj, CanHaveEmptyPropertyTypesForOwnProperty(obj) == obj->is()); - if (obj && !allowEmptyTypesForGlobal) { - if (CanHaveEmptyPropertyTypesForOwnProperty(obj)) - return true; + if (object()->isSingleton()) { + JSObject *obj = object()->singleton(); + MOZ_ASSERT(CanHaveEmptyPropertyTypesForOwnProperty(obj) == obj->is()); + if (!allowEmptyTypesForGlobal) { + if (CanHaveEmptyPropertyTypesForOwnProperty(obj)) + return true; + } } freeze(constraints); return false; @@ -1520,12 +1524,12 @@ } JSObject * -TemporaryTypeSet::getSingleton() +TemporaryTypeSet::maybeSingleton() { if (baseFlags() != 0 || baseObjectCount() != 1) return nullptr; - return getSingleObject(0); + return getSingleton(0); } JSObject * @@ -1536,7 +1540,7 @@ if (!types || types->nonDataProperty() || types->baseFlags() != 0 || types->getObjectCount() != 1) return nullptr; - JSObject *obj = types->getSingleObject(0); + JSObject *obj = types->getSingleton(0); if (obj) freeze(constraints); @@ -1565,9 +1569,9 @@ { public: // Flags we are watching for on this object. - TypeObjectFlags flags; + ObjectGroupFlags flags; - explicit ConstraintDataFreezeObjectFlags(TypeObjectFlags flags) + explicit ConstraintDataFreezeObjectFlags(ObjectGroupFlags flags) : flags(flags) { MOZ_ASSERT(flags); @@ -1577,14 +1581,14 @@ bool invalidateOnNewType(Type type) { return false; } bool invalidateOnNewPropertyState(TypeSet *property) { return false; } - bool invalidateOnNewObjectState(TypeObject *object) { - return object->hasAnyFlags(flags); + bool invalidateOnNewObjectState(ObjectGroup *group) { + return group->hasAnyFlags(flags); } bool constraintHolds(JSContext *cx, const HeapTypeSetKey &property, TemporaryTypeSet *expected) { - return !invalidateOnNewObjectState(property.object()->maybeType()); + return !invalidateOnNewObjectState(property.object()->maybeGroup()); } bool shouldSweep() { return false; } @@ -1593,12 +1597,12 @@ } /* anonymous namespace */ bool -TypeObjectKey::hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags) +ObjectGroupKey::hasFlags(CompilerConstraintList *constraints, ObjectGroupFlags flags) { MOZ_ASSERT(flags); - if (TypeObject *type = maybeType()) { - if (type->hasAnyFlags(flags)) + if (ObjectGroup *group = maybeGroup()) { + if (group->hasAnyFlags(flags)) return true; } @@ -1611,13 +1615,13 @@ } bool -TypeObjectKey::hasStableClassAndProto(CompilerConstraintList *constraints) +ObjectGroupKey::hasStableClassAndProto(CompilerConstraintList *constraints) { return !hasFlags(constraints, OBJECT_FLAG_UNKNOWN_PROPERTIES); } bool -TemporaryTypeSet::hasObjectFlags(CompilerConstraintList *constraints, TypeObjectFlags flags) +TemporaryTypeSet::hasObjectFlags(CompilerConstraintList *constraints, ObjectGroupFlags flags) { if (unknownObject()) return true; @@ -1631,8 +1635,8 @@ unsigned count = getObjectCount(); for (unsigned i = 0; i < count; i++) { - TypeObjectKey *object = getObject(i); - if (object && object->hasFlags(constraints, flags)) + ObjectGroupKey *key = getObject(i); + if (key && key->hasFlags(constraints, flags)) return true; } @@ -1640,7 +1644,7 @@ } gc::InitialHeap -TypeObject::initialHeap(CompilerConstraintList *constraints) +ObjectGroup::initialHeap(CompilerConstraintList *constraints) { // If this object is not required to be pretenured but could be in the // future, add a constraint to trigger recompilation if the requirement @@ -1652,7 +1656,7 @@ if (!canPreTenure()) return gc::DefaultHeap; - HeapTypeSetKey objectProperty = TypeObjectKey::get(this)->property(JSID_EMPTY); + HeapTypeSetKey objectProperty = ObjectGroupKey::get(this)->property(JSID_EMPTY); LifoAlloc *alloc = constraints->alloc(); typedef CompilerConstraintInstance T; @@ -1677,7 +1681,7 @@ bool invalidateOnNewType(Type type) { return false; } bool invalidateOnNewPropertyState(TypeSet *property) { return false; } - bool invalidateOnNewObjectState(TypeObject *object) { + bool invalidateOnNewObjectState(ObjectGroup *group) { // We don't keep track of the exact dependencies the caller has on its // inlined scripts' type sets, so always invalidate the caller. return true; @@ -1709,15 +1713,15 @@ bool invalidateOnNewType(Type type) { return false; } bool invalidateOnNewPropertyState(TypeSet *property) { return false; } - bool invalidateOnNewObjectState(TypeObject *object) { - TypedArrayObject &tarray = object->singleton()->as(); + bool invalidateOnNewObjectState(ObjectGroup *group) { + TypedArrayObject &tarray = group->singleton()->as(); return tarray.viewData() != viewData || tarray.length() != length; } bool constraintHolds(JSContext *cx, const HeapTypeSetKey &property, TemporaryTypeSet *expected) { - return !invalidateOnNewObjectState(property.object()->maybeType()); + return !invalidateOnNewObjectState(property.object()->maybeGroup()); } bool shouldSweep() { @@ -1729,7 +1733,7 @@ } /* anonymous namespace */ void -TypeObjectKey::watchStateChangeForInlinedCall(CompilerConstraintList *constraints) +ObjectGroupKey::watchStateChangeForInlinedCall(CompilerConstraintList *constraints) { HeapTypeSetKey objectProperty = property(JSID_EMPTY); LifoAlloc *alloc = constraints->alloc(); @@ -1739,9 +1743,9 @@ } void -TypeObjectKey::watchStateChangeForTypedArrayData(CompilerConstraintList *constraints) +ObjectGroupKey::watchStateChangeForTypedArrayData(CompilerConstraintList *constraints) { - TypedArrayObject &tarray = asSingleObject()->as(); + TypedArrayObject &tarray = singleton()->as(); HeapTypeSetKey objectProperty = property(JSID_EMPTY); LifoAlloc *alloc = constraints->alloc(); @@ -1751,23 +1755,23 @@ } static void -ObjectStateChange(ExclusiveContext *cxArg, TypeObject *object, bool markingUnknown) +ObjectStateChange(ExclusiveContext *cxArg, ObjectGroup *group, bool markingUnknown) { - if (object->unknownProperties()) + if (group->unknownProperties()) return; /* All constraints listening to state changes are on the empty id. */ - HeapTypeSet *types = object->maybeGetProperty(JSID_EMPTY); + HeapTypeSet *types = group->maybeGetProperty(JSID_EMPTY); /* Mark as unknown after getting the types, to avoid assertion. */ if (markingUnknown) - object->addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES); + group->addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES); if (types) { if (JSContext *cx = cxArg->maybeJSContext()) { TypeConstraint *constraint = types->constraintList; while (constraint) { - constraint->newObjectState(cx, object); + constraint->newObjectState(cx, group); constraint = constraint->next; } } else { @@ -1798,7 +1802,7 @@ ? property->nonDataProperty() : property->nonWritableProperty(); } - bool invalidateOnNewObjectState(TypeObject *object) { return false; } + bool invalidateOnNewObjectState(ObjectGroup *group) { return false; } bool constraintHolds(JSContext *cx, const HeapTypeSetKey &property, TemporaryTypeSet *expected) @@ -1852,7 +1856,7 @@ bool invalidateOnNewPropertyState(TypeSet *property) { return property->nonConstantProperty(); } - bool invalidateOnNewObjectState(TypeObject *object) { return false; } + bool invalidateOnNewObjectState(ObjectGroup *group) { return false; } bool constraintHolds(JSContext *cx, const HeapTypeSetKey &property, TemporaryTypeSet *expected) @@ -1912,7 +1916,7 @@ bool invalidateOnNewType(Type type) { return false; } bool invalidateOnNewPropertyState(TypeSet *property) { return false; } - bool invalidateOnNewObjectState(TypeObject *object) { return false; } + bool invalidateOnNewObjectState(ObjectGroup *group) { return false; } bool constraintHolds(JSContext *cx, const HeapTypeSetKey &property, TemporaryTypeSet *expected) @@ -1927,7 +1931,7 @@ HeapTypeSetKey::couldBeConstant(CompilerConstraintList *constraints) { // Only singleton object properties can be marked as constants. - if (!object()->singleton()) + if (!object()->isSingleton()) return false; if (!maybeTypes() || !maybeTypes()->nonConstantProperty()) @@ -1935,7 +1939,7 @@ // It is possible for a property that was not marked as constant to // 'become' one, if we throw away the type property during a GC and - // regenerate it with the constant flag set. TypeObject::sweep only removes + // regenerate it with the constant flag set. ObjectGroup::sweep only removes // type properties if they have no constraints attached to them, so add // inert constraints to pin these properties in place. @@ -1962,7 +1966,7 @@ return unknownObject(); for (size_t i = 0; i < other->getObjectCount(); i++) { - TypeObjectKey *key = other->getObject(i); + ObjectGroupKey *key = other->getObject(i); if (key) { Type type = Type::ObjectType(key); if (type != filteredType && !hasType(type)) @@ -1984,16 +1988,16 @@ bool dontConvert = false; for (unsigned i = 0; i < getObjectCount(); i++) { - TypeObjectKey *type = getObject(i); - if (!type) + ObjectGroupKey *key = getObject(i); + if (!key) continue; - if (type->unknownProperties()) { + if (key->unknownProperties()) { alwaysConvert = false; continue; } - HeapTypeSetKey property = type->property(JSID_VOID); + HeapTypeSetKey property = key->property(JSID_VOID); property.freeze(constraints); // We can't convert to double elements for objects which do not have @@ -2002,7 +2006,7 @@ // may point to emptyObjectElements, which cannot be converted). if (!property.maybeTypes() || !property.maybeTypes()->hasType(Type::DoubleType()) || - type->clasp() != &ArrayObject::class_) + key->clasp() != &ArrayObject::class_) { dontConvert = true; alwaysConvert = false; @@ -2013,7 +2017,7 @@ // element types are int or double. Other arrays require type tests // when elements are accessed regardless of the conversion. if (property.knownMIRType(constraints) == jit::MIRType_Double && - !type->hasFlags(constraints, OBJECT_FLAG_NON_PACKED)) + !key->hasFlags(constraints, OBJECT_FLAG_NON_PACKED)) { maybeConvert = true; } else { @@ -2056,8 +2060,8 @@ if (clasp) { for (unsigned i = 0; i < count; i++) { - TypeObjectKey *type = getObject(i); - if (type && !type->hasStableClassAndProto(constraints)) + ObjectGroupKey *key = getObject(i); + if (key && !key->hasStableClassAndProto(constraints)) return nullptr; } } @@ -2198,14 +2202,14 @@ unsigned count = getObjectCount(); for (unsigned i = 0; i < count; i++) { - TypeObjectKey *object = getObject(i); - if (!object) + ObjectGroupKey *key = getObject(i); + if (!key) continue; - if (object->unknownProperties() || !object->hasTenuredProto()) + if (key->unknownProperties() || !key->hasTenuredProto()) return nullptr; - TaggedProto nproto = object->proto(); + TaggedProto nproto = key->proto(); if (proto) { if (nproto != TaggedProto(proto)) return nullptr; @@ -2218,9 +2222,9 @@ // Guard against mutating __proto__. for (unsigned i = 0; i < count; i++) { - TypeObjectKey *object = getObject(i); - if (object) - JS_ALWAYS_TRUE(object->hasStableClassAndProto(constraints)); + ObjectGroupKey *key = getObject(i); + if (key) + JS_ALWAYS_TRUE(key->hasStableClassAndProto(constraints)); } return proto; @@ -2233,14 +2237,14 @@ return true; for (unsigned i = 0; i < getObjectCount(); i++) { - TypeObjectKey *type = getObject(i); - if (!type) + ObjectGroupKey *key = getObject(i); + if (!key) continue; - if (type->unknownProperties()) + if (key->unknownProperties()) return true; - HeapTypeSetKey property = type->property(id); + HeapTypeSetKey property = key->property(id); if (property.needsBarrier(constraints)) return true; } @@ -2257,9 +2261,9 @@ PodZero(this); } -TypeObject * -TypeCompartment::newTypeObject(ExclusiveContext *cx, const Class *clasp, Handle proto, - TypeObjectFlags initialFlags) +ObjectGroup * +TypeCompartment::newObjectGroup(ExclusiveContext *cx, const Class *clasp, Handle proto, + ObjectGroupFlags initialFlags) { MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject())); @@ -2268,16 +2272,16 @@ initialFlags |= OBJECT_FLAG_NURSERY_PROTO; } - TypeObject *object = js::NewTypeObject(cx); - if (!object) + ObjectGroup *group = js::NewObjectGroup(cx); + if (!group) return nullptr; - new(object) TypeObject(clasp, proto, initialFlags); + new(group) ObjectGroup(clasp, proto, initialFlags); - return object; + return group; } -TypeObject * -TypeCompartment::addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key) +ObjectGroup * +TypeCompartment::addAllocationSiteObjectGroup(JSContext *cx, AllocationSiteKey key) { AutoEnterAnalysis enter(cx); @@ -2285,6 +2289,7 @@ allocationSiteTable = cx->new_(); if (!allocationSiteTable || !allocationSiteTable->init()) { js_delete(allocationSiteTable); + allocationSiteTable = nullptr; return nullptr; } } @@ -2292,7 +2297,7 @@ AllocationSiteTable::AddPtr p = allocationSiteTable->lookupForAdd(key); MOZ_ASSERT(!p); - TypeObject *res = nullptr; + ObjectGroup *res = nullptr; jsbytecode *pc = key.script->offsetToPC(key.offset); RootedScript keyScript(cx, key.script); @@ -2303,7 +2308,7 @@ return nullptr; Rooted tagged(cx, TaggedProto(proto)); - res = newTypeObject(cx, GetClassForProtoKey(key.kind), tagged, OBJECT_FLAG_FROM_ALLOCATION_SITE); + res = newObjectGroup(cx, GetClassForProtoKey(key.kind), tagged, OBJECT_FLAG_FROM_ALLOCATION_SITE); if (!res) return nullptr; key.script = keyScript; @@ -2335,11 +2340,11 @@ } bool -types::UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc) +types::UseSingletonForNewObject(JSContext *cx, JSScript *script, jsbytecode *pc) { /* * Make a heuristic guess at a use of JSOP_NEW that the constructed object - * should have a fresh type object. We do this when the NEW is immediately + * should have a fresh group. We do this when the NEW is immediately * followed by a simple assignment to an object's .prototype field. * This is designed to catch common patterns for subclassing in JS: * @@ -2350,7 +2355,7 @@ * Sub1.prototype = new Super(); * Sub2.prototype = new Super(); * - * Using distinct type objects for the particular prototypes of Sub1 and + * Using distinct groups for the particular prototypes of Sub1 and * Sub2 lets us continue to distinguish the two subclasses and any extra * properties added to those prototype objects. */ @@ -2369,8 +2374,12 @@ } NewObjectKind -types::UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key) +types::UseSingletonForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key) { + // The return value of this method can either be tested like a boolean or + // passed to a NewObject method. + JS_STATIC_ASSERT(GenericObject == 0); + /* * Objects created outside loops in global and eval scripts should have * singleton types. For now this is only done for plain objects and typed @@ -2414,17 +2423,17 @@ } NewObjectKind -types::UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp) +types::UseSingletonForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp) { - return UseNewTypeForInitializer(script, pc, JSCLASS_CACHED_PROTO_KEY(clasp)); + return UseSingletonForInitializer(script, pc, JSCLASS_CACHED_PROTO_KEY(clasp)); } static inline bool ClassCanHaveExtraProperties(const Class *clasp) { return clasp->resolve - || clasp->ops.lookupGeneric - || clasp->ops.getGeneric + || clasp->ops.lookupProperty + || clasp->ops.getProperty || IsAnyTypedArrayClass(clasp); } @@ -2432,12 +2441,12 @@ PrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSObject *obj) { do { - TypeObjectKey *type = TypeObjectKey::get(obj); - if (ClassCanHaveExtraProperties(type->clasp())) + ObjectGroupKey *key = ObjectGroupKey::get(obj); + if (ClassCanHaveExtraProperties(key->clasp())) return true; - if (type->unknownProperties()) + if (key->unknownProperties()) return true; - HeapTypeSetKey index = type->property(JSID_VOID); + HeapTypeSetKey index = key->property(JSID_VOID); if (index.nonData(constraints) || index.isOwnProperty(constraints)) return true; if (!obj->hasTenuredProto()) @@ -2532,8 +2541,8 @@ // When one script is inlined into another the caller listens to state // changes on the callee's script, so trigger these to force recompilation // of any such callers. - if (script->functionNonDelazifying() && !script->functionNonDelazifying()->hasLazyType()) - ObjectStateChange(cx, script->functionNonDelazifying()->type(), false); + if (script->functionNonDelazifying() && !script->functionNonDelazifying()->hasLazyGroup()) + ObjectStateChange(cx, script->functionNonDelazifying()->group(), false); } void @@ -2555,9 +2564,9 @@ script->types()->printTypes(cx, script); } - for (gc::ZoneCellIter i(zone, gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) { - TypeObject *object = i.get(); - object->print(); + for (gc::ZoneCellIter i(zone, gc::FINALIZE_OBJECT_GROUP); !i.done(); i.next()) { + ObjectGroup *group = i.get(); + group->print(); } #endif } @@ -2568,14 +2577,14 @@ /* * The arrayTypeTable and objectTypeTable are per-compartment tables for making - * common type objects to model the contents of large script singletons and - * JSON objects. These are vanilla Arrays and native Objects, so we distinguish - * the types of different ones by looking at the types of their properties. + * common groups to model the contents of large script singletons and JSON + * objects. These are vanilla Arrays and native Objects, so we distinguish the + * types of different ones by looking at the types of their properties. * * All singleton/JSON arrays which have the same prototype, are homogenous and - * of the same element type will share a type object. All singleton/JSON - * objects which have the same shape and property types will also share a type - * object. We don't try to collate arrays or objects that have type mismatches. + * of the same element type will share a group. All singleton/JSON objects + * which have the same shape and property types will also share a group. + * We don't try to collate arrays or objects that have type mismatches. */ static inline bool @@ -2594,7 +2603,7 @@ GetValueTypeForTable(const Value &v) { Type type = GetValueType(v); - MOZ_ASSERT(!type.isSingleObject()); + MOZ_ASSERT(!type.isSingleton()); return type; } @@ -2645,32 +2654,32 @@ ArrayTableKey key(elementType, obj->getProto()); DependentAddPtr p(cx, *arrayTypeTable, key); if (p) { - obj->setType(p->value()); + obj->setGroup(p->value()); } else { /* Make a new type to use for future arrays with the same elements. */ RootedObject objProto(cx, obj->getProto()); Rooted taggedProto(cx, TaggedProto(objProto)); - TypeObject *objType = newTypeObject(cx, &ArrayObject::class_, taggedProto); - if (!objType) + ObjectGroup *group = newObjectGroup(cx, &ArrayObject::class_, taggedProto); + if (!group) return; - obj->setType(objType); + obj->setGroup(group); - AddTypePropertyId(cx, objType, JSID_VOID, elementType); + AddTypePropertyId(cx, group, JSID_VOID, elementType); key.proto = objProto; - (void) p.add(cx, *arrayTypeTable, key, objType); + (void) p.add(cx, *arrayTypeTable, key, group); } } void -TypeCompartment::fixArrayType(ExclusiveContext *cx, ArrayObject *obj) +TypeCompartment::fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj) { AutoEnterAnalysis enter(cx); /* - * If the array is of homogenous type, pick a type object which will be + * If the array is of homogenous type, pick a group which will be * shared with all other singleton/JSON arrays of the same type. - * If the array is heterogenous, keep the existing type object, which has + * If the array is heterogenous, keep the existing group, which has * unknown properties. */ @@ -2752,7 +2761,7 @@ struct types::ObjectTableEntry { - ReadBarrieredTypeObject object; + ReadBarrieredObjectGroup group; ReadBarrieredShape shape; Type *types; }; @@ -2761,7 +2770,7 @@ UpdateObjectTableEntryTypes(ExclusiveContext *cx, ObjectTableEntry &entry, IdValuePair *properties, size_t nproperties) { - if (entry.object->unknownProperties()) + if (entry.group->unknownProperties()) return; for (size_t i = 0; i < nproperties; i++) { Type type = entry.types[i]; @@ -2779,13 +2788,13 @@ /* Include 'double' in the property types to avoid the update below later. */ entry.types[i] = Type::DoubleType(); } - AddTypePropertyId(cx, entry.object, IdToTypeId(properties[i].id), ntype); + AddTypePropertyId(cx, entry.group, IdToTypeId(properties[i].id), ntype); } } } void -TypeCompartment::fixObjectType(ExclusiveContext *cx, PlainObject *obj) +TypeCompartment::fixObjectGroup(ExclusiveContext *cx, PlainObject *obj) { AutoEnterAnalysis enter(cx); @@ -2799,7 +2808,7 @@ } /* - * Use the same type object for all singleton/JSON objects with the same + * Use the same group for all singleton/JSON objects with the same * base shape, i.e. the same fields written in the same order. * * Exclude some objects we can't readily associate common types for based on their @@ -2825,35 +2834,35 @@ ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup); if (p) { - MOZ_ASSERT(obj->getProto() == p->value().object->proto().toObject()); + MOZ_ASSERT(obj->getProto() == p->value().group->proto().toObject()); MOZ_ASSERT(obj->lastProperty() == p->value().shape); UpdateObjectTableEntryTypes(cx, p->value(), properties.begin(), properties.length()); - obj->setType(p->value().object); + obj->setGroup(p->value().group); return; } /* Make a new type to use for the object and similar future ones. */ Rooted objProto(cx, obj->getTaggedProto()); - TypeObject *objType = newTypeObject(cx, &PlainObject::class_, objProto); - if (!objType || !objType->addDefiniteProperties(cx, obj->lastProperty())) + ObjectGroup *group = newObjectGroup(cx, &PlainObject::class_, objProto); + if (!group || !group->addDefiniteProperties(cx, obj->lastProperty())) return; if (obj->isIndexed()) - objType->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES); + group->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES); - ScopedJSFreePtr ids(objType->zone()->pod_calloc(properties.length())); + ScopedJSFreePtr ids(group->zone()->pod_calloc(properties.length())); if (!ids) return; - ScopedJSFreePtr types(objType->zone()->pod_calloc(properties.length())); + ScopedJSFreePtr types(group->zone()->pod_calloc(properties.length())); if (!types) return; for (size_t i = 0; i < properties.length(); i++) { ids[i] = properties[i].id; types[i] = GetValueTypeForTable(obj->getSlot(i)); - AddTypePropertyId(cx, objType, IdToTypeId(ids[i]), types[i]); + AddTypePropertyId(cx, group, IdToTypeId(ids[i]), types[i]); } ObjectTableKey key; @@ -2863,11 +2872,11 @@ MOZ_ASSERT(ObjectTableKey::match(key, lookup)); ObjectTableEntry entry; - entry.object.set(objType); + entry.group.set(group); entry.shape.set(obj->lastProperty()); entry.types = types; - obj->setType(objType); + obj->setGroup(group); p = objectTypeTable->lookupForAdd(lookup); if (objectTypeTable->add(p, key, entry)) { @@ -2891,16 +2900,16 @@ } /* - * Use the object type table to allocate an object with the specified + * Use the object group table to allocate an object with the specified * properties, filling in its final type and shape and failing if no cache * entry could be found for the properties. */ /* - * Filter out a few cases where we don't want to use the object type table. + * Filter out a few cases where we don't want to use the object group table. * Note that if the properties contain any duplicates or dense indexes, * the lookup below will fail as such arrays of properties cannot be stored - * in the object type table --- fixObjectType populates the table with + * in the object group table --- fixObjectGroup populates the table with * properties read off its input object, which cannot be duplicates, and * ignores objects with dense indexes. */ @@ -2921,7 +2930,7 @@ cx->clearPendingException(); return nullptr; } - MOZ_ASSERT(obj->getProto() == p->value().object->proto().toObject()); + MOZ_ASSERT(obj->getProto() == p->value().group->proto().toObject()); RootedShape shape(cx, p->value().shape); if (!NativeObject::setLastProperty(cx, obj, shape)) { @@ -2934,16 +2943,16 @@ for (size_t i = 0; i < nproperties; i++) obj->setSlot(i, properties[i].value); - obj->setType(p->value().object); + obj->setGroup(p->value().group); return obj; } ///////////////////////////////////////////////////////////////////// -// TypeObject +// ObjectGroup ///////////////////////////////////////////////////////////////////// void -TypeObject::setProto(JSContext *cx, TaggedProto proto) +ObjectGroup::setProto(JSContext *cx, TaggedProto proto) { MOZ_ASSERT(singleton()); @@ -2957,7 +2966,7 @@ UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, NativeObject *obj, Shape *shape, bool indexed) { - MOZ_ASSERT(obj->hasSingletonType() && !obj->hasLazyType()); + MOZ_ASSERT(obj->isSingleton() && !obj->hasLazyGroup()); if (!shape->writable()) types->setNonWritableProperty(cx); @@ -2992,17 +3001,17 @@ } else { InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s - setConstant", InferSpewColor(types), types, InferSpewColorReset(), - TypeObjectString(obj->type()), TypeIdString(shape->propid())); + ObjectGroupString(obj->group()), TypeIdString(shape->propid())); } } } void -TypeObject::updateNewPropertyTypes(ExclusiveContext *cx, jsid id, HeapTypeSet *types) +ObjectGroup::updateNewPropertyTypes(ExclusiveContext *cx, jsid id, HeapTypeSet *types) { InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s", InferSpewColor(types), types, InferSpewColorReset(), - TypeObjectString(this), TypeIdString(id)); + ObjectGroupString(this), TypeIdString(id)); if (!singleton() || !singleton()->isNative()) { types->setNonConstantProperty(cx); @@ -3052,12 +3061,12 @@ } bool -TypeObject::addDefiniteProperties(ExclusiveContext *cx, Shape *shape) +ObjectGroup::addDefiniteProperties(ExclusiveContext *cx, Shape *shape) { if (unknownProperties()) return true; - /* Mark all properties of shape as definite properties of this type. */ + // Mark all properties of shape as definite properties of this group. AutoEnterAnalysis enter(cx); while (!shape->isEmptyShape()) { @@ -3079,7 +3088,7 @@ } bool -TypeObject::matchDefiniteProperties(HandleObject obj) +ObjectGroup::matchDefiniteProperties(HandleObject obj) { unsigned count = getPropertyCount(); for (unsigned i = 0; i < count; i++) { @@ -3107,16 +3116,16 @@ } void -types::AddTypePropertyId(ExclusiveContext *cx, TypeObject *obj, jsid id, Type type) +types::AddTypePropertyId(ExclusiveContext *cx, ObjectGroup *group, jsid id, Type type) { MOZ_ASSERT(id == IdToTypeId(id)); - if (obj->unknownProperties()) + if (group->unknownProperties()) return; AutoEnterAnalysis enter(cx); - HeapTypeSet *types = obj->getProperty(cx, id); + HeapTypeSet *types = group->getProperty(cx, id); if (!types) return; @@ -3131,29 +3140,29 @@ return; InferSpew(ISpewOps, "externalType: property %s %s: %s", - TypeObjectString(obj), TypeIdString(id), TypeString(type)); + ObjectGroupString(group), TypeIdString(id), TypeString(type)); types->addType(cx, type); - // Propagate new types from partially initialized types to fully - // initialized types for the acquired properties analysis. Note that we + // Propagate new types from partially initialized groups to fully + // initialized groups for the acquired properties analysis. Note that we // don't need to do this for other property changes, as these will also be // reflected via shape changes on the object that will prevent the object - // from acquiring the fully initialized type. - if (obj->newScript() && obj->newScript()->initializedType()) { + // from acquiring the fully initialized group. + if (group->newScript() && group->newScript()->initializedGroup()) { if (type.isObjectUnchecked() && types->unknownObject()) type = Type::AnyObjectType(); - AddTypePropertyId(cx, obj->newScript()->initializedType(), id, type); + AddTypePropertyId(cx, group->newScript()->initializedGroup(), id, type); } } void -types::AddTypePropertyId(ExclusiveContext *cx, TypeObject *obj, jsid id, const Value &value) +types::AddTypePropertyId(ExclusiveContext *cx, ObjectGroup *group, jsid id, const Value &value) { - AddTypePropertyId(cx, obj, id, GetValueType(value)); + AddTypePropertyId(cx, group, id, GetValueType(value)); } void -TypeObject::markPropertyNonData(ExclusiveContext *cx, jsid id) +ObjectGroup::markPropertyNonData(ExclusiveContext *cx, jsid id) { AutoEnterAnalysis enter(cx); @@ -3165,7 +3174,7 @@ } void -TypeObject::markPropertyNonWritable(ExclusiveContext *cx, jsid id) +ObjectGroup::markPropertyNonWritable(ExclusiveContext *cx, jsid id) { AutoEnterAnalysis enter(cx); @@ -3177,7 +3186,7 @@ } bool -TypeObject::isPropertyNonData(jsid id) +ObjectGroup::isPropertyNonData(jsid id) { TypeSet *types = maybeGetProperty(id); if (types) @@ -3186,7 +3195,7 @@ } bool -TypeObject::isPropertyNonWritable(jsid id) +ObjectGroup::isPropertyNonWritable(jsid id) { TypeSet *types = maybeGetProperty(id); if (types) @@ -3195,7 +3204,7 @@ } void -TypeObject::markStateChange(ExclusiveContext *cxArg) +ObjectGroup::markStateChange(ExclusiveContext *cxArg) { if (unknownProperties()) return; @@ -3216,7 +3225,7 @@ } void -TypeObject::setFlags(ExclusiveContext *cx, TypeObjectFlags flags) +ObjectGroup::setFlags(ExclusiveContext *cx, ObjectGroupFlags flags) { if (hasAllFlags(flags)) return; @@ -3231,25 +3240,25 @@ addFlags(flags); - InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeObjectString(this), flags); + InferSpew(ISpewOps, "%s: setFlags 0x%x", ObjectGroupString(this), flags); ObjectStateChange(cx, this, false); - // Propagate flag changes from partially to fully initialized types for the + // Propagate flag changes from partially to fully initialized groups for the // acquired properties analysis. - if (newScript() && newScript()->initializedType()) - newScript()->initializedType()->setFlags(cx, flags); + if (newScript() && newScript()->initializedGroup()) + newScript()->initializedGroup()->setFlags(cx, flags); } void -TypeObject::markUnknown(ExclusiveContext *cx) +ObjectGroup::markUnknown(ExclusiveContext *cx) { AutoEnterAnalysis enter(cx); MOZ_ASSERT(cx->zone()->types.activeAnalysis); MOZ_ASSERT(!unknownProperties()); - InferSpew(ISpewOps, "UnknownProperties: %s", TypeObjectString(this)); + InferSpew(ISpewOps, "UnknownProperties: %s", ObjectGroupString(this)); clearNewScript(cx); ObjectStateChange(cx, this, true); @@ -3273,64 +3282,102 @@ } } +TypeNewScript * +ObjectGroup::anyNewScript() +{ + if (newScript()) + return newScript(); + if (maybeUnboxedLayout()) + return unboxedLayout().newScript(); + return nullptr; +} + +void +ObjectGroup::detachNewScript(bool writeBarrier) +{ + // Clear the TypeNewScript from this ObjectGroup and, if it has been + // analyzed, remove it from the newObjectGroups table so that it will not be + // produced by calling 'new' on the associated function anymore. + // The TypeNewScript is not actually destroyed. + TypeNewScript *newScript = anyNewScript(); + MOZ_ASSERT(newScript); + + if (newScript->analyzed()) { + NewObjectGroupTable &newObjectGroups = newScript->function()->compartment()->newObjectGroups; + NewObjectGroupTable::Ptr p = + newObjectGroups.lookup(NewObjectGroupTable::Lookup(nullptr, proto(), newScript->function())); + MOZ_ASSERT(p->group == this); + + newObjectGroups.remove(p); + } + + if (this->newScript()) + setAddendum(Addendum_None, nullptr, writeBarrier); + else + unboxedLayout().setNewScript(nullptr, writeBarrier); +} + void -TypeObject::maybeClearNewScriptOnOOM() +ObjectGroup::maybeClearNewScriptOnOOM() { MOZ_ASSERT(zone()->isGCSweepingOrCompacting()); if (!isMarked()) return; - if (!newScript()) + TypeNewScript *newScript = anyNewScript(); + if (!newScript) return; - for (unsigned i = 0; i < getPropertyCount(); i++) { - Property *prop = getProperty(i); - if (!prop) - continue; - if (prop->types.definiteProperty()) - prop->types.setNonDataPropertyIgnoringConstraints(); - } + addFlags(OBJECT_FLAG_NEW_SCRIPT_CLEARED); + + // This method is called during GC sweeping, so don't trigger pre barriers. + detachNewScript(/* writeBarrier = */ false); - // This method is called during GC sweeping, so there is no write barrier - // that needs to be triggered. - js_delete(newScript()); - addendum_ = nullptr; + js_delete(newScript); } void -TypeObject::clearNewScript(ExclusiveContext *cx) +ObjectGroup::clearNewScript(ExclusiveContext *cx) { - if (!newScript()) + TypeNewScript *newScript = anyNewScript(); + if (!newScript) return; - TypeNewScript *newScript = this->newScript(); - setNewScript(nullptr); - AutoEnterAnalysis enter(cx); - /* - * Any definite properties we added due to analysis of the new script when - * the type object was created are now invalid: objects with the same type - * can be created by using 'new' on a different script or through some - * other mechanism (e.g. Object.create). Rather than clear out the definite - * bits on the object's properties, just mark such properties as having - * been deleted/reconfigured, which will have the same effect on JITs - * wanting to use the definite bits to optimize property accesses. - */ - for (unsigned i = 0; i < getPropertyCount(); i++) { - Property *prop = getProperty(i); - if (!prop) - continue; - if (prop->types.definiteProperty()) - prop->types.setNonDataProperty(cx); - } + // Invalidate any Ion code constructing objects of this type. + setFlags(cx, OBJECT_FLAG_NEW_SCRIPT_CLEARED); + + // Mark the constructing function as having its 'new' script cleared, so we + // will not try to construct another one later. + if (!newScript->function()->setNewScriptCleared(cx)) + cx->recoverFromOutOfMemory(); + + detachNewScript(/* writeBarrier = */ true); if (cx->isJSContext()) { - newScript->rollbackPartiallyInitializedObjects(cx->asJSContext(), this); + bool found = newScript->rollbackPartiallyInitializedObjects(cx->asJSContext(), this); + + // If we managed to rollback any partially initialized objects, then + // any definite properties we added due to analysis of the new script + // are now invalid, so remove them. If there weren't any partially + // initialized objects then we don't need to change type information, + // as no more objects of this type will be created and the 'new' script + // analysis was still valid when older objects were created. + if (found) { + for (unsigned i = 0; i < getPropertyCount(); i++) { + Property *prop = getProperty(i); + if (!prop) + continue; + if (prop->types.definiteProperty()) + prop->types.setNonDataProperty(cx); + } + } } else { // Threads with an ExclusiveContext are not allowed to run scripts. - MOZ_ASSERT(!cx->perThreadData->activation()); + MOZ_ASSERT(!cx->perThreadData->runtimeIfOnOwnerThread() || + !cx->perThreadData->runtimeIfOnOwnerThread()->activation()); } js_delete(newScript); @@ -3338,11 +3385,11 @@ } void -TypeObject::print() +ObjectGroup::print() { TaggedProto tagged(proto()); fprintf(stderr, "%s : %s", - TypeObjectString(this), + ObjectGroupString(this), tagged.isObject() ? TypeString(Type::ObjectType(tagged.toObject())) : (tagged.isLazy() ? "(lazy)" : "(null)")); @@ -3374,9 +3421,9 @@ if (newScript()->analyzed()) { fprintf(stderr, "\n newScript %d properties", (int) newScript()->templateObject()->slotSpan()); - if (newScript()->initializedType()) { - fprintf(stderr, " initializedType %p with %d properties", - newScript()->initializedType(), (int) newScript()->initializedShape()->slotSpan()); + if (newScript()->initializedGroup()) { + fprintf(stderr, " initializedGroup %p with %d properties", + newScript()->initializedGroup(), (int) newScript()->initializedShape()->slotSpan()); } } else { fprintf(stderr, "\n newScript unanalyzed"); @@ -3405,10 +3452,10 @@ class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint { public: - TypeObject *object; + ObjectGroup *group; - explicit TypeConstraintClearDefiniteGetterSetter(TypeObject *object) - : object(object) + explicit TypeConstraintClearDefiniteGetterSetter(ObjectGroup *group) + : group(group) {} const char *kind() { return "clearDefiniteGetterSetter"; } @@ -3420,72 +3467,72 @@ * non-writable. */ if (source->nonDataProperty() || source->nonWritableProperty()) - object->clearNewScript(cx); + group->clearNewScript(cx); } void newType(JSContext *cx, TypeSet *source, Type type) {} bool sweep(TypeZone &zone, TypeConstraint **res) { - if (IsTypeObjectAboutToBeFinalized(&object)) + if (IsObjectGroupAboutToBeFinalized(&group)) return false; - *res = zone.typeLifoAlloc.new_(object); + *res = zone.typeLifoAlloc.new_(group); return true; } }; bool -types::AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *type, HandleId id) +types::AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, ObjectGroup *group, HandleId id) { /* * Ensure that if the properties named here could have a getter, setter or * a permanent property in any transitive prototype, the definite - * properties get cleared from the type. + * properties get cleared from the group. */ - RootedObject parent(cx, type->proto().toObjectOrNull()); - while (parent) { - TypeObject *parentObject = parent->getType(cx); - if (!parentObject || parentObject->unknownProperties()) + RootedObject proto(cx, group->proto().toObjectOrNull()); + while (proto) { + ObjectGroup *protoGroup = proto->getGroup(cx); + if (!protoGroup || protoGroup->unknownProperties()) return false; - HeapTypeSet *parentTypes = parentObject->getProperty(cx, id); - if (!parentTypes || parentTypes->nonDataProperty() || parentTypes->nonWritableProperty()) + HeapTypeSet *protoTypes = protoGroup->getProperty(cx, id); + if (!protoTypes || protoTypes->nonDataProperty() || protoTypes->nonWritableProperty()) return false; - if (!parentTypes->addConstraint(cx, cx->typeLifoAlloc().new_(type))) + if (!protoTypes->addConstraint(cx, cx->typeLifoAlloc().new_(group))) return false; - parent = parent->getProto(); + proto = proto->getProto(); } return true; } /* - * Constraint which clears definite properties on an object should a type set + * Constraint which clears definite properties on a group should a type set * contain any types other than a single object. */ class TypeConstraintClearDefiniteSingle : public TypeConstraint { public: - TypeObject *object; + ObjectGroup *group; - explicit TypeConstraintClearDefiniteSingle(TypeObject *object) - : object(object) + explicit TypeConstraintClearDefiniteSingle(ObjectGroup *group) + : group(group) {} const char *kind() { return "clearDefiniteSingle"; } void newType(JSContext *cx, TypeSet *source, Type type) { if (source->baseFlags() || source->getObjectCount() > 1) - object->clearNewScript(cx); + group->clearNewScript(cx); } bool sweep(TypeZone &zone, TypeConstraint **res) { - if (IsTypeObjectAboutToBeFinalized(&object)) + if (IsObjectGroupAboutToBeFinalized(&group)) return false; - *res = zone.typeLifoAlloc.new_(object); + *res = zone.typeLifoAlloc.new_(group); return true; } }; bool -types::AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type, +types::AddClearDefiniteFunctionUsesInScript(JSContext *cx, ObjectGroup *group, JSScript *script, JSScript *calleeScript) { // Look for any uses of the specified calleeScript in type sets for @@ -3496,7 +3543,7 @@ // contain a single object, as IonBuilder does not inline polymorphic sites // during the definite properties analysis. - TypeObjectKey *calleeKey = Type::ObjectType(calleeScript->functionNonDelazifying()).objectKey(); + ObjectGroupKey *calleeKey = Type::ObjectType(calleeScript->functionNonDelazifying()).objectKey(); unsigned count = TypeScript::NumTypeSets(script); StackTypeSet *typeArray = script->types()->typeArray(); @@ -3508,7 +3555,7 @@ // Also check if the object is the Function.call or // Function.apply native. IonBuilder uses the presence of these // functions during inlining. - JSObject *singleton = types->getSingleObject(0); + JSObject *singleton = types->getSingleton(0); if (!singleton || !singleton->is()) continue; JSFunction *fun = &singleton->as(); @@ -3519,7 +3566,7 @@ } // This is a type set that might have been used when inlining // |calleeScript| into |script|. - if (!types->addConstraint(cx, cx->typeLifoAlloc().new_(type))) + if (!types->addConstraint(cx, cx->typeLifoAlloc().new_(group))) return false; } } @@ -3556,13 +3603,13 @@ } static inline bool -IsAboutToBeFinalized(TypeObjectKey **keyp) +IsAboutToBeFinalized(ObjectGroupKey **keyp) { - /* Mask out the low bit indicating whether this is a type or JS object. */ + // Mask out the low bit indicating whether this is a group or JS object. uintptr_t flagBit = uintptr_t(*keyp) & 1; gc::Cell *tmp = reinterpret_cast(uintptr_t(*keyp) & ~1); bool isAboutToBeFinalized = IsCellAboutToBeFinalized(&tmp); - *keyp = reinterpret_cast(uintptr_t(tmp) | flagBit); + *keyp = reinterpret_cast(uintptr_t(tmp) | flagBit); return isAboutToBeFinalized; } @@ -3589,25 +3636,25 @@ RootedArrayObject obj(cx, &script->getObject(GET_UINT32_INDEX(pc))->as()); MOZ_ASSERT(obj->denseElementsAreCopyOnWrite()); - if (obj->type()->fromAllocationSite()) { - MOZ_ASSERT(obj->type()->hasAnyFlags(OBJECT_FLAG_COPY_ON_WRITE)); + if (obj->group()->fromAllocationSite()) { + MOZ_ASSERT(obj->group()->hasAnyFlags(OBJECT_FLAG_COPY_ON_WRITE)); return obj; } - RootedTypeObject type(cx, TypeScript::InitObject(cx, script, pc, JSProto_Array)); - if (!type) + RootedObjectGroup group(cx, TypeScript::InitGroup(cx, script, pc, JSProto_Array)); + if (!group) return nullptr; - type->addFlags(OBJECT_FLAG_COPY_ON_WRITE); + group->addFlags(OBJECT_FLAG_COPY_ON_WRITE); - // Update type information in the initializer object type. + // Update type information in the initializer object group. MOZ_ASSERT(obj->slotSpan() == 0); for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) { const Value &v = obj->getDenseElement(i); - AddTypePropertyId(cx, type, JSID_VOID, v); + AddTypePropertyId(cx, group, JSID_VOID, v); } - obj->setType(type); + obj->setGroup(group); return obj; } @@ -3615,7 +3662,7 @@ types::GetCopyOnWriteObject(JSScript *script, jsbytecode *pc) { // GetOrFixupCopyOnWriteObject should already have been called for - // script/pc, ensuring that the template object has a type with the + // script/pc, ensuring that the template object has a group with the // COPY_ON_WRITE flag. We don't assert this here, due to a corner case // where this property doesn't hold. See jsop_newarray_copyonwrite in // IonBuilder. @@ -3648,7 +3695,7 @@ } bool -types::UseNewTypeForClone(JSFunction *fun) +types::UseSingletonForClone(JSFunction *fun) { if (!fun->isInterpreted()) return false; @@ -3659,7 +3706,7 @@ if (fun->isArrow()) return false; - if (fun->hasSingletonType()) + if (fun->isSingleton()) return false; /* @@ -3751,53 +3798,109 @@ bool singleton /* = false */) { if (singleton) { - if (!setSingletonType(cx, fun)) + if (!setSingleton(cx, fun)) return false; } else { RootedObject funProto(cx, fun->getProto()); Rooted taggedProto(cx, TaggedProto(funProto)); - TypeObject *type = - cx->compartment()->types.newTypeObject(cx, &JSFunction::class_, taggedProto); - if (!type) + ObjectGroup *group = + cx->compartment()->types.newObjectGroup(cx, &JSFunction::class_, taggedProto); + if (!group) return false; - fun->setType(type); - type->setInterpretedFunction(fun); + fun->setGroup(group); + group->setInterpretedFunction(fun); } return true; } ///////////////////////////////////////////////////////////////////// +// PreliminaryObjectArray +///////////////////////////////////////////////////////////////////// + +void +PreliminaryObjectArray::registerNewObject(JSObject *res) +{ + // The preliminary object pointers are weak, and won't be swept properly + // during nursery collections, so the preliminary objects need to be + // initially tenured. + MOZ_ASSERT(!IsInsideNursery(res)); + + for (size_t i = 0; i < COUNT; i++) { + if (!objects[i]) { + objects[i] = res; + return; + } + } + + MOZ_CRASH("There should be room for registering the new object"); +} + +void +PreliminaryObjectArray::unregisterNewObject(JSObject *res) +{ + for (size_t i = 0; i < COUNT; i++) { + if (objects[i] == res) { + objects[i] = nullptr; + return; + } + } + + MOZ_CRASH("The object should be one of the preliminary objects"); +} + +bool +PreliminaryObjectArray::full() const +{ + for (size_t i = 0; i < COUNT; i++) { + if (!objects[i]) + return false; + } + return true; +} + +void +PreliminaryObjectArray::sweep() +{ + // All objects in the array are weak, so clear any that are about to be + // destroyed. + for (size_t i = 0; i < COUNT; i++) { + JSObject **ptr = &objects[i]; + if (*ptr && IsObjectAboutToBeFinalized(ptr)) + *ptr = nullptr; + } +} + +///////////////////////////////////////////////////////////////////// // TypeNewScript ///////////////////////////////////////////////////////////////////// -// Make a TypeNewScript for |type|, and set it up to hold the initial -// PRELIMINARY_OBJECT_COUNT objects created with the type. +// Make a TypeNewScript for |group|, and set it up to hold the initial +// PRELIMINARY_OBJECT_COUNT objects created with the group. /* static */ void -TypeNewScript::make(JSContext *cx, TypeObject *type, JSFunction *fun) +TypeNewScript::make(JSContext *cx, ObjectGroup *group, JSFunction *fun) { MOZ_ASSERT(cx->zone()->types.activeAnalysis); - MOZ_ASSERT(!type->newScript()); + MOZ_ASSERT(!group->newScript()); + MOZ_ASSERT(!group->maybeUnboxedLayout()); - if (type->unknownProperties()) + if (group->unknownProperties()) return; ScopedJSDeletePtr newScript(cx->new_()); if (!newScript) return; - newScript->fun = fun; + newScript->function_ = fun; - PlainObject **preliminaryObjects = - type->zone()->pod_calloc(PRELIMINARY_OBJECT_COUNT); - if (!preliminaryObjects) + newScript->preliminaryObjects = group->zone()->new_(); + if (!newScript->preliminaryObjects) return; - newScript->preliminaryObjects = preliminaryObjects; - type->setNewScript(newScript.forget()); + group->setNewScript(newScript.forget()); - gc::TraceTypeNewScript(type); + gc::TraceTypeNewScript(group); } size_t @@ -3814,40 +3917,19 @@ { MOZ_ASSERT(!analyzed()); - // The preliminary object pointers are weak, and won't be swept properly - // during nursery collections, so the preliminary objects need to be - // initially tenured. - MOZ_ASSERT(!IsInsideNursery(res)); - // New script objects must have the maximum number of fixed slots, so that // we can adjust their shape later to match the number of fixed slots used // by the template object we eventually create. MOZ_ASSERT(res->numFixedSlots() == NativeObject::MAX_FIXED_SLOTS); - for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) { - if (!preliminaryObjects[i]) { - preliminaryObjects[i] = res; - return; - } - } - - MOZ_CRASH("There should be room for registering the new object"); + preliminaryObjects->registerNewObject(res); } void TypeNewScript::unregisterNewObject(PlainObject *res) { MOZ_ASSERT(!analyzed()); - - for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) { - if (preliminaryObjects[i] == res) { - preliminaryObjects[i] = nullptr; - return; - } - } - - // The object should be one of the preliminary objects. - MOZ_CRASH(); + preliminaryObjects->unregisterNewObject(res); } // Return whether shape consists entirely of plain data properties. @@ -3899,8 +3981,8 @@ // Make a clone of the object, with the new allocation kind. RootedShape oldShape(cx, obj->lastProperty()); - RootedTypeObject type(cx, obj->type()); - JSObject *clone = NewReshapedObject(cx, type, obj->getParent(), allocKind, oldShape); + RootedObjectGroup group(cx, obj->group()); + JSObject *clone = NewReshapedObject(cx, group, obj->getParent(), allocKind, oldShape); if (!clone) return false; @@ -3913,31 +3995,31 @@ struct DestroyTypeNewScript { JSContext *cx; - TypeObject *type; + ObjectGroup *group; - DestroyTypeNewScript(JSContext *cx, TypeObject *type) - : cx(cx), type(type) + DestroyTypeNewScript(JSContext *cx, ObjectGroup *group) + : cx(cx), group(group) {} ~DestroyTypeNewScript() { - if (type) - type->clearNewScript(cx); + if (group) + group->clearNewScript(cx); } }; } // anonymous namespace bool -TypeNewScript::maybeAnalyze(JSContext *cx, TypeObject *type, bool *regenerate, bool force) +TypeNewScript::maybeAnalyze(JSContext *cx, ObjectGroup *group, bool *regenerate, bool force) { // Perform the new script properties analysis if necessary, returning - // whether the new type table was updated and type needs to be refreshed. - MOZ_ASSERT(this == type->newScript()); + // whether the new group table was updated and group needs to be refreshed. + MOZ_ASSERT(this == group->newScript()); // Make sure there aren't dead references in preliminaryObjects. This can // clear out the new script information on OOM. - type->maybeSweep(nullptr); - if (!type->newScript()) + group->maybeSweep(nullptr); + if (!group->newScript()) return true; if (regenerate) @@ -3948,34 +4030,36 @@ return true; } - if (!force) { - // Don't perform the analyses until sufficient preliminary objects have - // been allocated. - for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) { - if (!preliminaryObjects[i]) - return true; - } - } + // Don't perform the analyses until sufficient preliminary objects have + // been allocated. + if (!force && !preliminaryObjects->full()) + return true; AutoEnterAnalysis enter(cx); // Any failures after this point will clear out this TypeNewScript. - DestroyTypeNewScript destroyNewScript(cx, type); + DestroyTypeNewScript destroyNewScript(cx, group); // Compute the greatest common shape prefix and the largest slot span of // the preliminary objects. Shape *prefixShape = nullptr; size_t maxSlotSpan = 0; - for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) { - PlainObject *obj = preliminaryObjects[i]; - if (!obj) + for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { + JSObject *objBase = preliminaryObjects->get(i); + if (!objBase) continue; + PlainObject *obj = &objBase->as(); // For now, we require all preliminary objects to have only simple // lineages of plain data properties. Shape *shape = obj->lastProperty(); - if (shape->inDictionary() || !OnlyHasDataProperties(shape)) + if (shape->inDictionary() || + !OnlyHasDataProperties(shape) || + shape->getObjectFlags() != 0 || + shape->getObjectMetadata() != nullptr) + { return true; + } maxSlotSpan = Max(maxSlotSpan, obj->slotSpan()); @@ -4005,10 +4089,11 @@ // template object. Also recompute the prefix shape, as it reflects the // old number of fixed slots. Shape *newPrefixShape = nullptr; - for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) { - PlainObject *obj = preliminaryObjects[i]; - if (!obj) + for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { + JSObject *objBase = preliminaryObjects->get(i); + if (!objBase) continue; + PlainObject *obj = &objBase->as(); if (!ChangeObjectFixedSlotCount(cx, obj, kind)) return false; if (newPrefixShape) { @@ -4022,18 +4107,19 @@ prefixShape = newPrefixShape; } - RootedTypeObject typeRoot(cx, type); - templateObject_ = NewObjectWithType(cx, typeRoot, cx->global(), kind, MaybeSingletonObject); + RootedObjectGroup groupRoot(cx, group); + templateObject_ = NewObjectWithGroup(cx, groupRoot, cx->global(), kind, + MaybeSingletonObject); if (!templateObject_) return false; Vector initializerVector(cx); RootedPlainObject templateRoot(cx, templateObject()); - if (!jit::AnalyzeNewScriptDefiniteProperties(cx, fun, type, templateRoot, &initializerVector)) + if (!jit::AnalyzeNewScriptDefiniteProperties(cx, function(), group, templateRoot, &initializerVector)) return false; - if (!type->newScript()) + if (!group->newScript()) return true; MOZ_ASSERT(OnlyHasDataProperties(templateObject()->lastProperty())); @@ -4069,72 +4155,91 @@ if (!initializerVector.append(done)) return false; - initializerList = type->zone()->pod_calloc(initializerVector.length()); + initializerList = group->zone()->pod_calloc(initializerVector.length()); if (!initializerList) return false; PodCopy(initializerList, initializerVector.begin(), initializerVector.length()); } - js_free(preliminaryObjects); + // Try to use an unboxed representation for the group. + if (!TryConvertToUnboxedLayout(cx, templateObject()->lastProperty(), group, preliminaryObjects)) + return false; + + js_delete(preliminaryObjects); preliminaryObjects = nullptr; + if (group->maybeUnboxedLayout()) { + // An unboxed layout was constructed for the group, and this has already + // been hooked into it. + MOZ_ASSERT(group->unboxedLayout().newScript() == this); + destroyNewScript.group = nullptr; + + // Clear out the template object. This is not used for TypeNewScripts + // with an unboxed layout, and additionally this template is now a + // mutant object with a non-native class and native shape, and must be + // collected by the next GC. + templateObject_ = nullptr; + + return true; + } + if (prefixShape->slotSpan() == templateObject()->slotSpan()) { // The definite properties analysis found exactly the properties that // are held in common by the preliminary objects. No further analysis // is needed. - if (!type->addDefiniteProperties(cx, templateObject()->lastProperty())) + if (!group->addDefiniteProperties(cx, templateObject()->lastProperty())) return false; - destroyNewScript.type = nullptr; + destroyNewScript.group = nullptr; return true; } - // There are more properties consistently added to objects of this type + // There are more properties consistently added to objects of this group // than were discovered by the definite properties analysis. Use the - // existing type to represent fully initialized objects with all - // definite properties in the prefix shape, and make a new type object - // to represent partially initialized objects. + // existing group to represent fully initialized objects with all + // definite properties in the prefix shape, and make a new group to + // represent partially initialized objects. MOZ_ASSERT(prefixShape->slotSpan() > templateObject()->slotSpan()); - TypeObjectFlags initialFlags = type->flags() & OBJECT_FLAG_DYNAMIC_MASK; + ObjectGroupFlags initialFlags = group->flags() & OBJECT_FLAG_DYNAMIC_MASK; - Rooted protoRoot(cx, type->proto()); - TypeObject *initialType = - cx->compartment()->types.newTypeObject(cx, type->clasp(), protoRoot, initialFlags); - if (!initialType) + Rooted protoRoot(cx, group->proto()); + ObjectGroup *initialGroup = + cx->compartment()->types.newObjectGroup(cx, group->clasp(), protoRoot, initialFlags); + if (!initialGroup) return false; - if (!initialType->addDefiniteProperties(cx, templateObject()->lastProperty())) + if (!initialGroup->addDefiniteProperties(cx, templateObject()->lastProperty())) return false; - if (!type->addDefiniteProperties(cx, prefixShape)) + if (!group->addDefiniteProperties(cx, prefixShape)) return false; - NewTypeObjectTable &table = cx->compartment()->newTypeObjects; - NewTypeObjectTable::Lookup lookup(type->clasp(), type->proto(), fun); + NewObjectGroupTable &table = cx->compartment()->newObjectGroups; + NewObjectGroupTable::Lookup lookup(nullptr, group->proto(), function()); - MOZ_ASSERT(table.lookup(lookup)->object == type); + MOZ_ASSERT(table.lookup(lookup)->group == group); table.remove(lookup); - table.putNew(lookup, NewTypeObjectEntry(initialType, fun)); + table.putNew(lookup, NewObjectGroupEntry(initialGroup, function())); - templateObject()->setType(initialType); + templateObject()->setGroup(initialGroup); - // Transfer this TypeNewScript from the fully initialized type to the - // partially initialized type. - type->setNewScript(nullptr); - initialType->setNewScript(this); + // Transfer this TypeNewScript from the fully initialized group to the + // partially initialized group. + group->setNewScript(nullptr); + initialGroup->setNewScript(this); initializedShape_ = prefixShape; - initializedType_ = type; + initializedGroup_ = group; - destroyNewScript.type = nullptr; + destroyNewScript.group = nullptr; if (regenerate) *regenerate = true; return true; } -void -TypeNewScript::rollbackPartiallyInitializedObjects(JSContext *cx, TypeObject *type) +bool +TypeNewScript::rollbackPartiallyInitializedObjects(JSContext *cx, ObjectGroup *group) { // If we cleared this new script while in the middle of initializing an // object, it will still have the new script's shape and reflect the no @@ -4142,27 +4247,35 @@ // We can't detect the possibility of this statically while remaining // robust, but the new script keeps track of where each property is // initialized so we can walk the stack and fix up any such objects. + // Return whether any objects were modified. if (!initializerList) - return; + return false; - RootedFunction function(cx, fun); + bool found = false; + + RootedFunction function(cx, this->function()); Vector pcOffsets(cx); for (ScriptFrameIter iter(cx); !iter.done(); ++iter) { pcOffsets.append(iter.script()->pcToOffset(iter.pc())); - // This frame has no this. - if (!iter.isConstructing() || iter.matchCallee(cx, function)) + if (!iter.isConstructing() || !iter.matchCallee(cx, function)) continue; Value thisv = iter.thisv(cx); if (!thisv.isObject() || - thisv.toObject().hasLazyType() || - thisv.toObject().type() != type) + thisv.toObject().hasLazyGroup() || + thisv.toObject().group() != group) { continue; } + if (thisv.toObject().is() && + !thisv.toObject().as().convertToNative(cx)) + { + CrashAtUnhandlableOOM("rollbackPartiallyInitializedObjects"); + } + // Found a matching frame. RootedPlainObject obj(cx, &thisv.toObject().as()); @@ -4215,15 +4328,19 @@ } } - if (!finished) + if (!finished) { (void) NativeObject::rollbackProperties(cx, obj, numProperties); + found = true; + } } + + return found; } void TypeNewScript::trace(JSTracer *trc) { - MarkObject(trc, &fun, "TypeNewScript_function"); + MarkObject(trc, &function_, "TypeNewScript_function"); if (templateObject_) MarkObject(trc, &templateObject_, "TypeNewScript_templateObject"); @@ -4231,22 +4348,15 @@ if (initializedShape_) MarkShape(trc, &initializedShape_, "TypeNewScript_initializedShape"); - if (initializedType_) - MarkTypeObject(trc, &initializedType_, "TypeNewScript_initializedType"); + if (initializedGroup_) + MarkObjectGroup(trc, &initializedGroup_, "TypeNewScript_initializedGroup"); } void TypeNewScript::sweep() { - // preliminaryObjects only holds weak pointers, so clear any objects that - // are about to be destroyed. - if (preliminaryObjects) { - for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) { - PlainObject **ptr = &preliminaryObjects[i]; - if (*ptr && IsObjectAboutToBeFinalized(ptr)) - *ptr = nullptr; - } - } + if (preliminaryObjects) + preliminaryObjects->sweep(); } ///////////////////////////////////////////////////////////////////// @@ -4264,7 +4374,7 @@ */ if (getProto() != nullptr) return false; - return hasSingletonType(); + return isSingleton(); } bool @@ -4275,38 +4385,38 @@ RootedObject self(cx, this); /* - * For singleton types representing only a single JSObject, the proto + * For singleton groups representing only a single JSObject, the proto * can be rearranged as needed without destroying type information for * the old or new types. */ - MOZ_ASSERT(self->hasSingletonType()); + MOZ_ASSERT(self->isSingleton()); - /* Inner objects may not appear on prototype chains. */ + // Inner objects may not appear on prototype chains. MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject); - /* - * Force type instantiation when splicing lazy types. This may fail, - * in which case inference will be disabled for the compartment. - */ - Rooted type(cx, self->getType(cx)); - if (!type) + if (proto.isObject() && !proto.toObject()->setDelegate(cx)) return false; - Rooted protoType(cx, nullptr); + + // Force type instantiation when splicing lazy group. + RootedObjectGroup group(cx, self->getGroup(cx)); + if (!group) + return false; + RootedObjectGroup protoGroup(cx, nullptr); if (proto.isObject()) { - protoType = proto.toObject()->getType(cx); - if (!protoType) + protoGroup = proto.toObject()->getGroup(cx); + if (!protoGroup) return false; } - type->setClasp(clasp); - type->setProto(cx, proto); + group->setClasp(clasp); + group->setProto(cx, proto); return true; } -/* static */ TypeObject * -JSObject::makeLazyType(JSContext *cx, HandleObject obj) +/* static */ ObjectGroup * +JSObject::makeLazyGroup(JSContext *cx, HandleObject obj) { - MOZ_ASSERT(obj->hasLazyType()); + MOZ_ASSERT(obj->hasLazyGroup()); MOZ_ASSERT(cx->compartment() == obj->compartment()); /* De-lazification of functions can GC, so we need to do it up here. */ @@ -4318,7 +4428,7 @@ // Find flags which need to be specified immediately on the object. // Don't track whether singletons are packed. - TypeObjectFlags initialFlags = OBJECT_FLAG_NON_PACKED; + ObjectGroupFlags initialFlags = OBJECT_FLAG_NON_PACKED; if (obj->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON)) initialFlags |= OBJECT_FLAG_ITERATED; @@ -4330,26 +4440,27 @@ initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW; Rooted proto(cx, obj->getTaggedProto()); - TypeObject *type = cx->compartment()->types.newTypeObject(cx, obj->getClass(), proto, initialFlags); - if (!type) + ObjectGroup *group = cx->compartment()->types.newObjectGroup(cx, obj->getClass(), proto, + initialFlags); + if (!group) return nullptr; AutoEnterAnalysis enter(cx); /* Fill in the type according to the state of this object. */ - type->initSingleton(obj); + group->initSingleton(obj); if (obj->is() && obj->as().isInterpreted()) - type->setInterpretedFunction(&obj->as()); + group->setInterpretedFunction(&obj->as()); - obj->type_ = type; + obj->group_ = group; - return type; + return group; } /* static */ inline HashNumber -NewTypeObjectEntry::hash(const Lookup &lookup) +NewObjectGroupEntry::hash(const Lookup &lookup) { return PointerHasher::hash(lookup.hashProto.raw()) ^ PointerHasher::hash(lookup.clasp) ^ @@ -4357,87 +4468,84 @@ } /* static */ inline bool -NewTypeObjectEntry::match(const NewTypeObjectEntry &key, const Lookup &lookup) +NewObjectGroupEntry::match(const NewObjectGroupEntry &key, const Lookup &lookup) { - return key.object->proto() == lookup.matchProto && - key.object->clasp() == lookup.clasp && + return key.group->proto() == lookup.matchProto && + (!lookup.clasp || key.group->clasp() == lookup.clasp) && key.associated == lookup.associated; } #ifdef DEBUG bool -JSObject::hasNewType(const Class *clasp, TypeObject *type) +JSObject::hasNewGroup(const Class *clasp, ObjectGroup *group) { - NewTypeObjectTable &table = compartment()->newTypeObjects; + NewObjectGroupTable &table = compartment()->newObjectGroups; if (!table.initialized()) return false; - NewTypeObjectTable::Ptr p = - table.lookup(NewTypeObjectTable::Lookup(clasp, TaggedProto(this), nullptr)); - return p && p->object == type; + NewObjectGroupTable::Ptr p = + table.lookup(NewObjectGroupTable::Lookup(clasp, TaggedProto(this), nullptr)); + return p && p->group == group; } #endif /* DEBUG */ /* static */ bool -JSObject::setNewTypeUnknown(JSContext *cx, const Class *clasp, HandleObject obj) +JSObject::setNewGroupUnknown(JSContext *cx, const Class *clasp, HandleObject obj) { - if (!obj->setFlag(cx, js::BaseShape::NEW_TYPE_UNKNOWN)) + if (!obj->setFlag(cx, js::BaseShape::NEW_GROUP_UNKNOWN)) return false; - /* - * If the object already has a new type, mark that type as unknown. It will - * not have the SETS_MARKED_UNKNOWN bit set, so may require a type set - * crawl if prototypes of the object change dynamically in the future. - */ - NewTypeObjectTable &table = cx->compartment()->newTypeObjects; + // If the object already has a new group, mark that group as unknown. + NewObjectGroupTable &table = cx->compartment()->newObjectGroups; if (table.initialized()) { Rooted taggedProto(cx, TaggedProto(obj)); - NewTypeObjectTable::Ptr p = - table.lookup(NewTypeObjectTable::Lookup(clasp, taggedProto, nullptr)); + NewObjectGroupTable::Ptr p = + table.lookup(NewObjectGroupTable::Lookup(clasp, taggedProto, nullptr)); if (p) - MarkTypeObjectUnknownProperties(cx, p->object); + MarkObjectGroupUnknownProperties(cx, p->group); } return true; } /* - * This class is used to add a post barrier on the newTypeObjects set, as the + * This class is used to add a post barrier on the newObjectGroups set, as the * key is calculated from a prototype object which may be moved by generational * GC. */ -class NewTypeObjectsSetRef : public BufferableRef +class NewObjectGroupsSetRef : public BufferableRef { - NewTypeObjectTable *set; + NewObjectGroupTable *set; const Class *clasp; JSObject *proto; JSObject *associated; public: - NewTypeObjectsSetRef(NewTypeObjectTable *s, const Class *clasp, JSObject *proto, JSObject *associated) + NewObjectGroupsSetRef(NewObjectGroupTable *s, const Class *clasp, JSObject *proto, JSObject *associated) : set(s), clasp(clasp), proto(proto), associated(associated) {} void mark(JSTracer *trc) { JSObject *prior = proto; trc->setTracingLocation(&*prior); - Mark(trc, &proto, "newTypeObjects set prototype"); + Mark(trc, &proto, "newObjectGroups set prototype"); if (prior == proto) return; - NewTypeObjectTable::Ptr p = - set->lookup(NewTypeObjectTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto), - associated)); - MOZ_ASSERT(p); // newTypeObjects set must still contain original entry. + NewObjectGroupTable::Ptr p = + set->lookup(NewObjectGroupTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto), + associated)); + if (!p) + return; - set->rekeyAs(NewTypeObjectTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto), associated), - NewTypeObjectTable::Lookup(clasp, TaggedProto(proto), associated), *p); + set->rekeyAs(NewObjectGroupTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto), associated), + NewObjectGroupTable::Lookup(clasp, TaggedProto(proto), associated), *p); } }; static void -TypeObjectTablePostBarrier(ExclusiveContext *cx, NewTypeObjectTable *table, +ObjectGroupTablePostBarrier(ExclusiveContext *cx, NewObjectGroupTable *table, const Class *clasp, TaggedProto proto, JSObject *associated) { MOZ_ASSERT_IF(associated, !IsInsideNursery(associated)); @@ -4452,24 +4560,31 @@ if (IsInsideNursery(proto.toObject())) { StoreBuffer &sb = cx->asJSContext()->runtime()->gc.storeBuffer; - sb.putGeneric(NewTypeObjectsSetRef(table, clasp, proto.toObject(), associated)); + sb.putGeneric(NewObjectGroupsSetRef(table, clasp, proto.toObject(), associated)); } } -TypeObject * -ExclusiveContext::getNewType(const Class *clasp, TaggedProto proto, JSObject *associated) +ObjectGroup * +ExclusiveContext::getNewGroup(const Class *clasp, TaggedProto proto, JSObject *associated) { MOZ_ASSERT_IF(associated, proto.isObject()); MOZ_ASSERT_IF(associated, associated->is() || associated->is()); MOZ_ASSERT_IF(proto.isObject(), isInsideCurrentCompartment(proto.toObject())); - NewTypeObjectTable &newTypeObjects = compartment()->newTypeObjects; + // A null lookup clasp is used for 'new' groups with an associated + // function. The group starts out as a plain object but might mutate into an + // unboxed plain object. + MOZ_ASSERT(!clasp == (associated && associated->is())); - if (!newTypeObjects.initialized() && !newTypeObjects.init()) + NewObjectGroupTable &newObjectGroups = compartment()->newObjectGroups; + + if (!newObjectGroups.initialized() && !newObjectGroups.init()) return nullptr; - // Canonicalize new functions to use the original one associated with its script. if (associated && associated->is()) { + MOZ_ASSERT(!clasp); + + // Canonicalize new functions to use the original one associated with its script. JSFunction *fun = &associated->as(); if (fun->hasScript()) associated = fun->nonLazyScript()->functionNonDelazifying(); @@ -4477,15 +4592,25 @@ associated = fun->lazyScript()->functionNonDelazifying(); else associated = nullptr; + + // If we have previously cleared the 'new' script information for this + // function, don't try to construct another one. + if (associated && associated->wasNewScriptCleared()) + associated = nullptr; + + if (!associated) + clasp = &PlainObject::class_; } - NewTypeObjectTable::AddPtr p = - newTypeObjects.lookupForAdd(NewTypeObjectTable::Lookup(clasp, proto, associated)); + NewObjectGroupTable::AddPtr p = + newObjectGroups.lookupForAdd(NewObjectGroupTable::Lookup(clasp, proto, associated)); if (p) { - TypeObject *type = p->object; - MOZ_ASSERT(type->clasp() == clasp); - MOZ_ASSERT(type->proto() == proto); - return type; + ObjectGroup *group = p->group; + MOZ_ASSERT_IF(clasp, group->clasp() == clasp); + MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_ || + group->clasp() == &UnboxedPlainObject::class_); + MOZ_ASSERT(group->proto() == proto); + return group; } AutoEnterAnalysis enter(this); @@ -4493,28 +4618,30 @@ if (proto.isObject() && !proto.toObject()->setDelegate(this)) return nullptr; - TypeObjectFlags initialFlags = 0; - if (!proto.isObject() || proto.toObject()->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN)) + ObjectGroupFlags initialFlags = 0; + if (!proto.isObject() || proto.toObject()->isNewGroupUnknown()) initialFlags = OBJECT_FLAG_DYNAMIC_MASK; Rooted protoRoot(this, proto); - TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot, initialFlags); - if (!type) + ObjectGroup *group = compartment()->types.newObjectGroup(this, + clasp ? clasp : &PlainObject::class_, + protoRoot, initialFlags); + if (!group) return nullptr; - if (!newTypeObjects.add(p, NewTypeObjectEntry(type, associated))) + if (!newObjectGroups.add(p, NewObjectGroupEntry(group, associated))) return nullptr; - TypeObjectTablePostBarrier(this, &newTypeObjects, clasp, proto, associated); + ObjectGroupTablePostBarrier(this, &newObjectGroups, clasp, proto, associated); if (proto.isObject()) { RootedObject obj(this, proto.toObject()); if (associated) { if (associated->is()) - TypeNewScript::make(asJSContext(), type, &associated->as()); + TypeNewScript::make(asJSContext(), group, &associated->as()); else - type->setTypeDescr(&associated->as()); + group->setTypeDescr(&associated->as()); } /* @@ -4525,62 +4652,62 @@ */ if (obj->is()) { - AddTypePropertyId(this, type, NameToId(names().source), Type::StringType()); - AddTypePropertyId(this, type, NameToId(names().global), Type::BooleanType()); - AddTypePropertyId(this, type, NameToId(names().ignoreCase), Type::BooleanType()); - AddTypePropertyId(this, type, NameToId(names().multiline), Type::BooleanType()); - AddTypePropertyId(this, type, NameToId(names().sticky), Type::BooleanType()); - AddTypePropertyId(this, type, NameToId(names().lastIndex), Type::Int32Type()); + AddTypePropertyId(this, group, NameToId(names().source), Type::StringType()); + AddTypePropertyId(this, group, NameToId(names().global), Type::BooleanType()); + AddTypePropertyId(this, group, NameToId(names().ignoreCase), Type::BooleanType()); + AddTypePropertyId(this, group, NameToId(names().multiline), Type::BooleanType()); + AddTypePropertyId(this, group, NameToId(names().sticky), Type::BooleanType()); + AddTypePropertyId(this, group, NameToId(names().lastIndex), Type::Int32Type()); } if (obj->is()) - AddTypePropertyId(this, type, NameToId(names().length), Type::Int32Type()); + AddTypePropertyId(this, group, NameToId(names().length), Type::Int32Type()); if (obj->is()) { - AddTypePropertyId(this, type, NameToId(names().fileName), Type::StringType()); - AddTypePropertyId(this, type, NameToId(names().lineNumber), Type::Int32Type()); - AddTypePropertyId(this, type, NameToId(names().columnNumber), Type::Int32Type()); - AddTypePropertyId(this, type, NameToId(names().stack), Type::StringType()); + AddTypePropertyId(this, group, NameToId(names().fileName), Type::StringType()); + AddTypePropertyId(this, group, NameToId(names().lineNumber), Type::Int32Type()); + AddTypePropertyId(this, group, NameToId(names().columnNumber), Type::Int32Type()); + AddTypePropertyId(this, group, NameToId(names().stack), Type::StringType()); } } - return type; + return group; } -TypeObject * -ExclusiveContext::getSingletonType(const Class *clasp, TaggedProto proto) +ObjectGroup * +ExclusiveContext::getLazySingletonGroup(const Class *clasp, TaggedProto proto) { MOZ_ASSERT_IF(proto.isObject(), compartment() == proto.toObject()->compartment()); AutoEnterAnalysis enter(this); - NewTypeObjectTable &table = compartment()->lazyTypeObjects; + NewObjectGroupTable &table = compartment()->lazyObjectGroups; if (!table.initialized() && !table.init()) return nullptr; - NewTypeObjectTable::AddPtr p = table.lookupForAdd(NewTypeObjectTable::Lookup(clasp, proto, nullptr)); + NewObjectGroupTable::AddPtr p = table.lookupForAdd(NewObjectGroupTable::Lookup(clasp, proto, nullptr)); if (p) { - TypeObject *type = p->object; - MOZ_ASSERT(type->lazy()); + ObjectGroup *group = p->group; + MOZ_ASSERT(group->lazy()); - return type; + return group; } Rooted protoRoot(this, proto); - TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot); - if (!type) + ObjectGroup *group = compartment()->types.newObjectGroup(this, clasp, protoRoot); + if (!group) return nullptr; - if (!table.add(p, NewTypeObjectEntry(type, nullptr))) + if (!table.add(p, NewObjectGroupEntry(group, nullptr))) return nullptr; - TypeObjectTablePostBarrier(this, &table, clasp, proto, nullptr); + ObjectGroupTablePostBarrier(this, &table, clasp, proto, nullptr); - type->initSingleton((JSObject *) TypeObject::LAZY_SINGLETON); - MOZ_ASSERT(type->singleton(), "created type must be a proper singleton"); + group->initSingleton((JSObject *) ObjectGroup::LAZY_SINGLETON); + MOZ_ASSERT(group->singleton(), "created group must be a proper singleton"); - return type; + return group; } ///////////////////////////////////////////////////////////////////// @@ -4597,7 +4724,7 @@ MOZ_ASSERT(!zone->runtimeFromMainThread()->isHeapMinorCollecting()); /* - * Purge references to type objects that are no longer live. Type sets hold + * Purge references to objects that are no longer live. Type sets hold * only weak references. For type sets containing more than one object, * live entries in the object hash need to be copied to the zone's * new arena. @@ -4605,18 +4732,20 @@ unsigned objectCount = baseObjectCount(); if (objectCount >= 2) { unsigned oldCapacity = HashSetCapacity(objectCount); - TypeObjectKey **oldArray = objectSet; + ObjectGroupKey **oldArray = objectSet; clearObjects(); objectCount = 0; for (unsigned i = 0; i < oldCapacity; i++) { - TypeObjectKey *object = oldArray[i]; - if (object && !IsAboutToBeFinalized(&object)) { - TypeObjectKey **pentry = - HashSetInsert - (zone->types.typeLifoAlloc, objectSet, objectCount, object); + ObjectGroupKey *key = oldArray[i]; + if (!key) + continue; + if (!IsAboutToBeFinalized(&key)) { + ObjectGroupKey **pentry = + HashSetInsert + (zone->types.typeLifoAlloc, objectSet, objectCount, key); if (pentry) { - *pentry = object; + *pentry = key; } else { oom.setOOM(); flags |= TYPE_FLAG_ANYOBJECT; @@ -4624,16 +4753,28 @@ objectCount = 0; break; } + } else if (key->isGroup() && key->group()->unknownProperties()) { + // Object sets containing objects with unknown properties might + // not be complete. Mark the type set as unknown, which it will + // be treated as during Ion compilation. + flags |= TYPE_FLAG_ANYOBJECT; + clearObjects(); + objectCount = 0; + break; } } setBaseObjectCount(objectCount); } else if (objectCount == 1) { - TypeObjectKey *object = (TypeObjectKey *) objectSet; - if (IsAboutToBeFinalized(&object)) { + ObjectGroupKey *key = (ObjectGroupKey *) objectSet; + if (!IsAboutToBeFinalized(&key)) { + objectSet = reinterpret_cast(key); + } else { + // As above, mark type sets containing objects with unknown + // properties as unknown. + if (key->isGroup() && key->group()->unknownProperties()) + flags |= TYPE_FLAG_ANYOBJECT; objectSet = nullptr; setBaseObjectCount(0); - } else { - objectSet = reinterpret_cast(object); } } @@ -4658,7 +4799,7 @@ } inline void -TypeObject::clearProperties() +ObjectGroup::clearProperties() { setBasePropertyCount(0); propertySet = nullptr; @@ -4666,7 +4807,7 @@ #ifdef DEBUG bool -TypeObject::needsSweep() +ObjectGroup::needsSweep() { // Note: this can be called off thread during compacting GCs, in which case // nothing will be running on the main thread. @@ -4689,14 +4830,14 @@ } /* - * Before sweeping the arenas themselves, scan all type objects in a - * compartment to fixup weak references: property type sets referencing dead - * JS and type objects, and singleton JS objects whose type is not referenced - * elsewhere. This is done either incrementally as part of the sweep, or on - * demand as type objects are accessed before their contents have been swept. + * Before sweeping the arenas themselves, scan all groups in a compartment to + * fixup weak references: property type sets referencing dead JS and type + * objects, and singleton JS objects whose type is not referenced elsewhere. + * This is done either incrementally as part of the sweep, or on demand as type + * objects are accessed before their contents have been swept. */ void -TypeObject::maybeSweep(AutoClearTypeInferenceStateOnOOM *oom) +ObjectGroup::maybeSweep(AutoClearTypeInferenceStateOnOOM *oom) { if (generation() == zoneFromAnyThread()->types.generation) { // No sweeping required. @@ -4711,6 +4852,9 @@ Maybe fallbackOOM; EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM); + if (maybeUnboxedLayout() && unboxedLayout().newScript()) + unboxedLayout().newScript()->sweep(); + if (newScript()) newScript()->sweep(); @@ -4794,29 +4938,29 @@ TypeCompartment::sweep(FreeOp *fop) { /* - * Iterate through the array/object type tables and remove all entries + * Iterate through the array/object group tables and remove all entries * referencing collected data. These tables only hold weak references. */ if (arrayTypeTable) { for (ArrayTypeTable::Enum e(*arrayTypeTable); !e.empty(); e.popFront()) { ArrayTableKey key = e.front().key(); - MOZ_ASSERT(key.type.isUnknown() || !key.type.isSingleObject()); + MOZ_ASSERT(key.type.isUnknown() || !key.type.isSingleton()); bool remove = false; - if (!key.type.isUnknown() && key.type.isTypeObject()) { - TypeObject *typeObject = key.type.typeObjectNoBarrier(); - if (IsTypeObjectAboutToBeFinalized(&typeObject)) + if (!key.type.isUnknown() && key.type.isGroup()) { + ObjectGroup *group = key.type.groupNoBarrier(); + if (IsObjectGroupAboutToBeFinalized(&group)) remove = true; else - key.type = Type::ObjectType(typeObject); + key.type = Type::ObjectType(group); } if (key.proto && key.proto != TaggedProto::LazyProto && IsObjectAboutToBeFinalized(&key.proto)) { remove = true; } - if (IsTypeObjectAboutToBeFinalized(e.front().value().unsafeGet())) + if (IsObjectGroupAboutToBeFinalized(e.front().value().unsafeGet())) remove = true; if (remove) @@ -4832,7 +4976,7 @@ ObjectTableEntry &entry = e.front().value(); bool remove = false; - if (IsTypeObjectAboutToBeFinalized(entry.object.unsafeGet())) + if (IsObjectGroupAboutToBeFinalized(entry.group.unsafeGet())) remove = true; if (IsShapeAboutToBeFinalized(entry.shape.unsafeGet())) remove = true; @@ -4848,14 +4992,14 @@ remove = true; } - MOZ_ASSERT(!entry.types[i].isSingleObject()); - TypeObject *typeObject = nullptr; - if (entry.types[i].isTypeObject()) { - typeObject = entry.types[i].typeObjectNoBarrier(); - if (IsTypeObjectAboutToBeFinalized(&typeObject)) + MOZ_ASSERT(!entry.types[i].isSingleton()); + ObjectGroup *group = nullptr; + if (entry.types[i].isGroup()) { + group = entry.types[i].groupNoBarrier(); + if (IsObjectGroupAboutToBeFinalized(&group)) remove = true; - else if (typeObject != entry.types[i].typeObjectNoBarrier()) - entry.types[i] = Type::ObjectType(typeObject); + else if (group != entry.types[i].groupNoBarrier()) + entry.types[i] = Type::ObjectType(group); } } @@ -4871,7 +5015,7 @@ for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) { AllocationSiteKey key = e.front().key(); bool keyDying = IsScriptAboutToBeFinalized(&key.script); - bool valDying = IsTypeObjectAboutToBeFinalized(e.front().value().unsafeGet()); + bool valDying = IsObjectGroupAboutToBeFinalized(e.front().value().unsafeGet()); if (keyDying || valDying) e.removeFront(); else if (key.script != e.front().key().script) @@ -4881,19 +5025,19 @@ } void -JSCompartment::sweepNewTypeObjectTable(NewTypeObjectTable &table) +JSCompartment::sweepNewObjectGroupTable(NewObjectGroupTable &table) { MOZ_ASSERT(zone()->runtimeFromAnyThread()->isHeapCollecting()); if (table.initialized()) { - for (NewTypeObjectTable::Enum e(table); !e.empty(); e.popFront()) { - NewTypeObjectEntry entry = e.front(); - if (IsTypeObjectAboutToBeFinalizedFromAnyThread(entry.object.unsafeGet()) || + for (NewObjectGroupTable::Enum e(table); !e.empty(); e.popFront()) { + NewObjectGroupEntry entry = e.front(); + if (IsObjectGroupAboutToBeFinalizedFromAnyThread(entry.group.unsafeGet()) || (entry.associated && IsObjectAboutToBeFinalizedFromAnyThread(&entry.associated))) { e.removeFront(); } else { - /* Any rekeying necessary is handled by fixupNewTypeObjectTable() below. */ - MOZ_ASSERT(entry.object.unbarrieredGet() == e.front().object.unbarrieredGet()); + /* Any rekeying necessary is handled by fixupNewObjectGroupTable() below. */ + MOZ_ASSERT(entry.group.unbarrieredGet() == e.front().group.unbarrieredGet()); MOZ_ASSERT(entry.associated == e.front().associated); } } @@ -4901,22 +5045,22 @@ } void -JSCompartment::fixupNewTypeObjectTable(NewTypeObjectTable &table) +JSCompartment::fixupNewObjectGroupTable(NewObjectGroupTable &table) { /* * Each entry's hash depends on the object's prototype and we can't tell - * whether that has been moved or not in sweepNewTypeObjectTable(). + * whether that has been moved or not in sweepNewObjectGroupTable(). */ MOZ_ASSERT(zone()->isCollecting()); if (table.initialized()) { - for (NewTypeObjectTable::Enum e(table); !e.empty(); e.popFront()) { - NewTypeObjectEntry entry = e.front(); + for (NewObjectGroupTable::Enum e(table); !e.empty(); e.popFront()) { + NewObjectGroupEntry entry = e.front(); bool needRekey = false; - if (IsForwarded(entry.object.get())) { - entry.object.set(Forwarded(entry.object.get())); + if (IsForwarded(entry.group.get())) { + entry.group.set(Forwarded(entry.group.get())); needRekey = true; } - TaggedProto proto = entry.object->proto(); + TaggedProto proto = entry.group->proto(); if (proto.isObject() && IsForwarded(proto.toObject())) { proto = TaggedProto(Forwarded(proto.toObject())); needRekey = true; @@ -4926,64 +5070,27 @@ needRekey = true; } if (needRekey) { - NewTypeObjectTable::Lookup lookup(entry.object->clasp(), - proto, - entry.associated); + const Class *clasp = entry.group->clasp(); + if (entry.associated && entry.associated->is()) + clasp = nullptr; + NewObjectGroupTable::Lookup lookup(clasp, proto, entry.associated); e.rekeyFront(lookup, entry); } } } } -void -TypeNewScript::fixupAfterMovingGC() -{ - if (fun && IsForwarded(fun.get())) - fun = Forwarded(fun.get()); - /* preliminaryObjects are handled by sweep(). */ - if (templateObject_ && IsForwarded(templateObject_.get())) - templateObject_ = Forwarded(templateObject_.get()); - if (initializedShape_ && IsForwarded(initializedShape_.get())) - initializedShape_ = Forwarded(initializedShape_.get()); -} - -void -TypeObject::fixupAfterMovingGC() -{ - if (proto().isObject() && IsForwarded(proto_.get())) - proto_ = Forwarded(proto_.get()); - if (singleton_ && !lazy() && IsForwarded(singleton_.get())) - singleton_ = Forwarded(singleton_.get()); - if (addendum_) { - switch (addendumKind()) { - case Addendum_NewScript: - newScript()->fixupAfterMovingGC(); - break; - case Addendum_TypeDescr: - if (IsForwarded(&typeDescr())) - addendum_ = Forwarded(&typeDescr()); - break; - case Addendum_InterpretedFunction: - if (IsForwarded(maybeInterpretedFunction())) - addendum_ = Forwarded(maybeInterpretedFunction()); - break; - default: - MOZ_CRASH(); - } - } -} - #ifdef JSGC_HASH_TABLE_CHECKS void -JSCompartment::checkTypeObjectTablesAfterMovingGC() +JSCompartment::checkObjectGroupTablesAfterMovingGC() { - checkTypeObjectTableAfterMovingGC(newTypeObjects); - checkTypeObjectTableAfterMovingGC(lazyTypeObjects); + checkObjectGroupTableAfterMovingGC(newObjectGroups); + checkObjectGroupTableAfterMovingGC(lazyObjectGroups); } void -JSCompartment::checkTypeObjectTableAfterMovingGC(NewTypeObjectTable &table) +JSCompartment::checkObjectGroupTableAfterMovingGC(NewObjectGroupTable &table) { /* * Assert that nothing points into the nursery or needs to be relocated, and @@ -4992,17 +5099,20 @@ if (!table.initialized()) return; - for (NewTypeObjectTable::Enum e(table); !e.empty(); e.popFront()) { - NewTypeObjectEntry entry = e.front(); - CheckGCThingAfterMovingGC(entry.object.get()); - TaggedProto proto = entry.object->proto(); + for (NewObjectGroupTable::Enum e(table); !e.empty(); e.popFront()) { + NewObjectGroupEntry entry = e.front(); + CheckGCThingAfterMovingGC(entry.group.get()); + TaggedProto proto = entry.group->proto(); if (proto.isObject()) CheckGCThingAfterMovingGC(proto.toObject()); CheckGCThingAfterMovingGC(entry.associated); - NewTypeObjectTable::Lookup - lookup(entry.object->clasp(), proto, entry.associated); - NewTypeObjectTable::Ptr ptr = table.lookup(lookup); + const Class *clasp = entry.group->clasp(); + if (entry.associated && entry.associated->is()) + clasp = nullptr; + + NewObjectGroupTable::Lookup lookup(clasp, proto, entry.associated); + NewObjectGroupTable::Ptr ptr = table.lookup(lookup); MOZ_ASSERT(ptr.found() && &*ptr == &e.front()); } } @@ -5108,10 +5218,14 @@ } size_t -TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const +ObjectGroup::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { - TypeNewScript *newScript = newScriptDontCheckGeneration(); - return newScript ? newScript->sizeOfIncludingThis(mallocSizeOf) : 0; + size_t n = 0; + if (TypeNewScript *newScript = newScriptDontCheckGeneration()) + n += newScript->sizeOfIncludingThis(mallocSizeOf); + if (UnboxedLayout *layout = maybeUnboxedLayoutDontCheckGeneration()) + n += layout->sizeOfIncludingThis(mallocSizeOf); + return n; } TypeZone::TypeZone(Zone *zone) @@ -5198,12 +5312,12 @@ void TypeZone::clearAllNewScriptsOnOOM() { - for (gc::ZoneCellIterUnderGC iter(zone(), gc::FINALIZE_TYPE_OBJECT); + for (gc::ZoneCellIterUnderGC iter(zone(), gc::FINALIZE_OBJECT_GROUP); !iter.done(); iter.next()) { - TypeObject *object = iter.get(); - if (!IsTypeObjectAboutToBeFinalized(&object)) - object->maybeClearNewScriptOnOOM(); + ObjectGroup *group = iter.get(); + if (!IsObjectGroupAboutToBeFinalized(&group)) + group->maybeClearNewScriptOnOOM(); } } @@ -5275,19 +5389,21 @@ #endif /* DEBUG */ void -TypeObject::setAddendum(AddendumKind kind, void *addendum) +ObjectGroup::setAddendum(AddendumKind kind, void *addendum, bool writeBarrier /* = true */) { MOZ_ASSERT(!needsSweep()); MOZ_ASSERT(kind <= (OBJECT_FLAG_ADDENDUM_MASK >> OBJECT_FLAG_ADDENDUM_SHIFT)); - MOZ_ASSERT(addendumKind() == 0 || addendumKind() == kind); - // Manually trigger barriers if we are clearing a TypeNewScript. Other - // kinds of addendums are immutable. - if (newScript()) { - MOZ_ASSERT(kind == Addendum_NewScript); - TypeNewScript::writeBarrierPre(newScript()); + if (writeBarrier) { + // Manually trigger barriers if we are clearing a TypeNewScript. Other + // kinds of addendums are immutable. + if (newScript()) + TypeNewScript::writeBarrierPre(newScript()); + else + MOZ_ASSERT(addendumKind() == Addendum_None || addendumKind() == kind); } + flags_ &= ~OBJECT_FLAG_ADDENDUM_MASK; flags_ |= kind << OBJECT_FLAG_ADDENDUM_SHIFT; addendum_ = addendum; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsinfer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsinfer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsinfer.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsinfer.h 2015-02-03 14:33:32.000000000 +0000 @@ -10,7 +10,6 @@ #define jsinfer_h #include "mozilla/MemoryReporting.h" -#include "mozilla/TypedEnum.h" #include "jsalloc.h" #include "jsfriendapi.h" @@ -28,6 +27,7 @@ namespace js { class TypeDescr; +class UnboxedLayout; class TaggedProto { @@ -127,12 +127,12 @@ struct TypeZone; class TypeSet; -struct TypeObjectKey; +struct ObjectGroupKey; /* * Information about a single concrete type. We pack this into a single word, * where small values are particular primitive or other singleton types, and - * larger values are either specific JS objects or type objects. + * larger values are either specific JS objects or object groups. */ class Type { @@ -173,7 +173,7 @@ return data == JSVAL_TYPE_UNKNOWN; } - /* Accessors for types that are either JSObject or TypeObject. */ + /* Accessors for types that are either JSObject or ObjectGroup. */ bool isObject() const { MOZ_ASSERT(!isAnyObject() && !isUnknown()); @@ -184,25 +184,25 @@ return data > JSVAL_TYPE_UNKNOWN; } - inline TypeObjectKey *objectKey() const; + inline ObjectGroupKey *objectKey() const; /* Accessors for JSObject types */ - bool isSingleObject() const { + bool isSingleton() const { return isObject() && !!(data & 1); } - inline JSObject *singleObject() const; - inline JSObject *singleObjectNoBarrier() const; + inline JSObject *singleton() const; + inline JSObject *singletonNoBarrier() const; - /* Accessors for TypeObject types */ + /* Accessors for ObjectGroup types */ - bool isTypeObject() const { + bool isGroup() const { return isObject() && !(data & 1); } - inline TypeObject *typeObject() const; - inline TypeObject *typeObjectNoBarrier() const; + inline ObjectGroup *group() const; + inline ObjectGroup *groupNoBarrier() const; bool operator == (Type o) const { return data == o.data; } bool operator != (Type o) const { return data != o.data; } @@ -224,8 +224,8 @@ } static inline Type ObjectType(JSObject *obj); - static inline Type ObjectType(TypeObject *obj); - static inline Type ObjectType(TypeObjectKey *obj); + static inline Type ObjectType(ObjectGroup *obj); + static inline Type ObjectType(ObjectGroupKey *obj); static js::ThingRootKind rootKind() { return js::THING_ROOT_TYPE; } }; @@ -283,7 +283,7 @@ * indicate a change in one of the object's dynamic property flags or other * state. */ - virtual void newObjectState(JSContext *cx, TypeObject *object) {} + virtual void newObjectState(JSContext *cx, ObjectGroup *group) {} /* * If the data this constraint refers to is still live, copy it into the @@ -351,9 +351,9 @@ }; typedef uint32_t TypeFlags; -/* Flags and other state stored in TypeObject::flags */ +/* Flags and other state stored in ObjectGroup::flags */ enum : uint32_t { - /* Whether this type object is associated with some allocation site. */ + /* Whether this group is associated with some allocation site. */ OBJECT_FLAG_FROM_ALLOCATION_SITE = 0x1, /* @@ -407,25 +407,28 @@ /* Whether objects with this type might have copy on write elements. */ OBJECT_FLAG_COPY_ON_WRITE = 0x01000000, + /* Whether this type has had its 'new' script cleared in the past. */ + OBJECT_FLAG_NEW_SCRIPT_CLEARED = 0x02000000, + /* * Whether all properties of this object are considered unknown. * If set, all other flags in DYNAMIC_MASK will also be set. */ - OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x02000000, + OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x04000000, /* Flags which indicate dynamic properties of represented objects. */ - OBJECT_FLAG_DYNAMIC_MASK = 0x03ff0000, + OBJECT_FLAG_DYNAMIC_MASK = 0x07ff0000, - // Mask/shift for the kind of addendum attached to this type object. - OBJECT_FLAG_ADDENDUM_MASK = 0x0c000000, - OBJECT_FLAG_ADDENDUM_SHIFT = 26, - - // Mask/shift for this type object's generation. If out of sync with the - // TypeZone's generation, this TypeObject hasn't been swept yet. - OBJECT_FLAG_GENERATION_MASK = 0x10000000, - OBJECT_FLAG_GENERATION_SHIFT = 28, + // Mask/shift for the kind of addendum attached to this group. + OBJECT_FLAG_ADDENDUM_MASK = 0x38000000, + OBJECT_FLAG_ADDENDUM_SHIFT = 27, + + // Mask/shift for this group's generation. If out of sync with the + // TypeZone's generation, this group hasn't been swept yet. + OBJECT_FLAG_GENERATION_MASK = 0x40000000, + OBJECT_FLAG_GENERATION_SHIFT = 30, }; -typedef uint32_t TypeObjectFlags; +typedef uint32_t ObjectGroupFlags; class StackTypeSet; class HeapTypeSet; @@ -439,7 +442,7 @@ * observed at property reads. These are implicitly frozen on compilation * and do not have constraints attached to them. * - * - HeapTypeSet are associated with the properties of TypeObjects. These + * - HeapTypeSet are associated with the properties of ObjectGroups. These * may have constraints added to them to trigger invalidation of compiled * code. * @@ -460,7 +463,7 @@ TypeFlags flags; /* Possible objects this type set can represent. */ - TypeObjectKey **objectSet; + ObjectGroupKey **objectSet; public: @@ -508,7 +511,7 @@ /* Get a list of all types in this set. */ typedef Vector TypeList; - bool enumerateTypes(TypeList *list); + bool enumerateTypes(TypeList *list) const; /* * Iterate through the objects in this set. getObjectCount overapproximates @@ -516,11 +519,11 @@ * may return nullptr. */ inline unsigned getObjectCount() const; - inline TypeObjectKey *getObject(unsigned i) const; - inline JSObject *getSingleObject(unsigned i) const; - inline TypeObject *getTypeObject(unsigned i) const; - inline JSObject *getSingleObjectNoBarrier(unsigned i) const; - inline TypeObject *getTypeObjectNoBarrier(unsigned i) const; + inline ObjectGroupKey *getObject(unsigned i) const; + inline JSObject *getSingleton(unsigned i) const; + inline ObjectGroup *getGroup(unsigned i) const; + inline JSObject *getSingletonNoBarrier(unsigned i) const; + inline ObjectGroup *getGroupNoBarrier(unsigned i) const; /* The Class of an object in this set. */ inline const Class *getObjectClass(unsigned i) const; @@ -636,7 +639,6 @@ public: /* Mark this type set as representing a non-data property. */ inline void setNonDataProperty(ExclusiveContext *cx); - inline void setNonDataPropertyIgnoringConstraints(); // Variant for use during GC. /* Mark this type set as representing a non-writable property. */ inline void setNonWritableProperty(ExclusiveContext *cx); @@ -656,7 +658,7 @@ TemporaryTypeSet() {} TemporaryTypeSet(LifoAlloc *alloc, Type type); - TemporaryTypeSet(uint32_t flags, TypeObjectKey **objectSet) { + TemporaryTypeSet(uint32_t flags, ObjectGroupKey **objectSet) { this->flags = flags; this->objectSet = objectSet; } @@ -692,7 +694,7 @@ } /* Whether the type set contains objects with any of a set of flags. */ - bool hasObjectFlags(CompilerConstraintList *constraints, TypeObjectFlags flags); + bool hasObjectFlags(CompilerConstraintList *constraints, ObjectGroupFlags flags); /* Get the class shared by all objects in this set, or nullptr. */ const Class *getKnownClass(CompilerConstraintList *constraints); @@ -732,7 +734,7 @@ bool maybeEmulatesUndefined(CompilerConstraintList *constraints); /* Get the single value which can appear in this type set, otherwise nullptr. */ - JSObject *getSingleton(); + JSObject *maybeSingleton(); /* Whether any objects in the type set needs a barrier on id. */ bool propertyNeedsBarrier(CompilerConstraintList *constraints, jsid id); @@ -765,10 +767,10 @@ }; bool -AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *type, HandleId id); +AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, ObjectGroup *group, HandleId id); bool -AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type, +AddClearDefiniteFunctionUsesInScript(JSContext *cx, ObjectGroup *group, JSScript *script, JSScript *calleeScript); /* Is this a reasonable PC to be doing inlining on? */ @@ -795,17 +797,49 @@ static jsid getKey(Property *p) { return p->id; } }; +// For types where only a small number of objects have been allocated, this +// structure keeps track of all objects with the type in existence. Once +// COUNT objects have been allocated, this structure is cleared and the objects +// are analyzed, to perform the new script properties analyses or determine if +// an unboxed representation can be used. +class PreliminaryObjectArray +{ + public: + static const uint32_t COUNT = 20; + + private: + // All objects with the type which have been allocated. The pointers in + // this array are weak. + JSObject *objects[COUNT]; + + public: + PreliminaryObjectArray() { + mozilla::PodZero(this); + } + + void registerNewObject(JSObject *res); + void unregisterNewObject(JSObject *res); + + JSObject *get(size_t i) const { + MOZ_ASSERT(i < COUNT); + return objects[i]; + } + + bool full() const; + void sweep(); +}; + // New script properties analyses overview. // // When constructing objects using 'new' on a script, we attempt to determine // the properties which that object will eventually have. This is done via two // analyses. One of these, the definite properties analysis, is static, and the // other, the acquired properties analysis, is dynamic. As objects are -// constructed using 'new' on some script to create objects of type T, our +// constructed using 'new' on some script to create objects of group G, our // analysis strategy is as follows: // // - When the first objects are created, no analysis is immediately performed. -// Instead, all objects of type T are accumulated in an array. +// Instead, all objects of group G are accumulated in an array. // // - After a certain number of such objects have been created, the definite // properties analysis is performed. This analyzes the body of the @@ -818,9 +852,9 @@ // than S, the acquired properties analysis is performed. // // - The acquired properties analysis marks all properties in P as definite -// in T, and creates a new type object IT for objects which are partially -// initialized. Objects of type IT are initially created with shape S, and if -// they are later given shape P, their type can be changed to T. +// in G, and creates a new group IG for objects which are partially +// initialized. Objects of group IG are initially created with shape S, and if +// they are later given shape P, their group can be changed to G. // // For objects which are rarely created, the definite properties analysis can // be triggered after only one or a few objects have been allocated, when code @@ -850,23 +884,20 @@ private: // Scripted function which this information was computed for. - // If instances of the associated type object are created without calling + // If instances of the associated group are created without calling // 'new' on this function, the new script information is cleared. - HeapPtrFunction fun; + HeapPtrFunction function_; - // If fewer than PRELIMINARY_OBJECT_COUNT instances of the type are - // created, this array holds pointers to each of those objects. When the - // threshold has been reached, the definite and acquired properties - // analyses are performed and this array is cleared. The pointers in this - // array are weak. - static const uint32_t PRELIMINARY_OBJECT_COUNT = 20; - PlainObject **preliminaryObjects; + // Any preliminary objects with the type. The analyses are not performed + // until this array is cleared. + PreliminaryObjectArray *preliminaryObjects; // After the new script properties analyses have been performed, a template // object to use for newly constructed objects. The shape of this object // reflects all definite properties the object will have, and the - // allocation kind to use. Note that this is actually a PlainObject, but is - // JSObject here to avoid cyclic include dependencies. + // allocation kind to use. This is null if the new objects have an unboxed + // layout, in which case the UnboxedLayout provides the initial structure + // of the object. HeapPtrPlainObject templateObject_; // Order in which definite properties become initialized. We need this in @@ -882,33 +913,25 @@ // If there are additional properties found by the acquired properties // analysis which were not found by the definite properties analysis, this // shape contains all such additional properties (plus the definite - // properties). When an object of this type acquires this shape, it is - // fully initialized and its type can be changed to initializedType. + // properties). When an object of this group acquires this shape, it is + // fully initialized and its group can be changed to initializedGroup. HeapPtrShape initializedShape_; - // Type object with definite properties set for all properties found by + // Group with definite properties set for all properties found by // both the definite and acquired properties analyses. - HeapPtrTypeObject initializedType_; + HeapPtrObjectGroup initializedGroup_; public: TypeNewScript() { mozilla::PodZero(this); } ~TypeNewScript() { - js_free(preliminaryObjects); + js_delete(preliminaryObjects); js_free(initializerList); } static inline void writeBarrierPre(TypeNewScript *newScript); bool analyzed() const { - if (preliminaryObjects) { - MOZ_ASSERT(!templateObject()); - MOZ_ASSERT(!initializerList); - MOZ_ASSERT(!initializedShape()); - MOZ_ASSERT(!initializedType()); - return false; - } - MOZ_ASSERT(templateObject()); - return true; + return preliminaryObjects == nullptr; } PlainObject *templateObject() const { @@ -919,34 +942,37 @@ return initializedShape_; } - TypeObject *initializedType() const { - return initializedType_; + ObjectGroup *initializedGroup() const { + return initializedGroup_; + } + + JSFunction *function() const { + return function_; } void trace(JSTracer *trc); void sweep(); - void fixupAfterMovingGC(); void registerNewObject(PlainObject *res); void unregisterNewObject(PlainObject *res); - bool maybeAnalyze(JSContext *cx, TypeObject *type, bool *regenerate, bool force = false); + bool maybeAnalyze(JSContext *cx, ObjectGroup *group, bool *regenerate, bool force = false); - void rollbackPartiallyInitializedObjects(JSContext *cx, TypeObject *type); + bool rollbackPartiallyInitializedObjects(JSContext *cx, ObjectGroup *group); - static void make(JSContext *cx, TypeObject *type, JSFunction *fun); + static void make(JSContext *cx, ObjectGroup *group, JSFunction *fun); size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; }; /* - * Lazy type objects overview. + * Lazy object groups overview. * - * Type objects which represent at most one JS object are constructed lazily. - * These include types for native functions, standard classes, scripted + * Object groups which represent at most one JS object are constructed lazily. + * These include groups for native functions, standard classes, scripted * functions defined at the top level of global/eval scripts, and in some * other cases. Typical web workloads often create many windows (and many * copies of standard natives) and many scripts, with comparatively few - * non-singleton types. + * non-singleton groups. * * We can recover the type information for the object from examining it, * so don't normally track the possible types of its properties as it is @@ -955,23 +981,23 @@ * property off the object or another which delegates to it, and the analysis * information is sensitive to changes in the property's type. Future changes * to the property (whether those uncovered by analysis or those occurring - * in the VM) will treat these properties like those of any other type object. + * in the VM) will treat these properties like those of any other object group. */ /* Type information about an object accessed by a script. */ -struct TypeObject : public gc::TenuredCell +struct ObjectGroup : public gc::TenuredCell { private: - /* Class shared by object using this type. */ + /* Class shared by objects in this group. */ const Class *clasp_; - /* Prototype shared by objects using this type. */ + /* Prototype shared by objects in this group. */ HeapPtrObject proto_; /* - * Whether there is a singleton JS object with this type. That JS object + * Whether there is a singleton JS object with this group. That JS object * must appear in type sets instead of this; we include the back reference - * here to allow reverting the JS object to a lazy type. + * here to allow reverting the JS object to a lazy group. */ HeapPtrObject singleton_; @@ -982,7 +1008,6 @@ } void setClasp(const Class *clasp) { - MOZ_ASSERT(singleton()); clasp_ = clasp; } @@ -1008,17 +1033,17 @@ } /* - * Value held by singleton if this is a standin type for a singleton JS - * object whose type has not been constructed yet. + * Value held by singleton if this is a standin group for a singleton JS + * object whose group has not been constructed yet. */ static const size_t LAZY_SINGLETON = 1; bool lazy() const { return singleton() == (JSObject *) LAZY_SINGLETON; } private: - /* Flags for this object. */ - TypeObjectFlags flags_; + /* Flags for this group. */ + ObjectGroupFlags flags_; - // Kinds of addendums which can be attached to TypeObjects. + // Kinds of addendums which can be attached to ObjectGroups. enum AddendumKind { Addendum_None, @@ -1026,10 +1051,15 @@ // canonical JSFunction object. Addendum_InterpretedFunction, - // When used by the 'new' type when constructing an interpreted + // When used by the 'new' group when constructing an interpreted // function, the addendum stores a TypeNewScript. Addendum_NewScript, + // When objects in this group have an unboxed representation, the + // addendum stores an UnboxedLayout (which might have a TypeNewScript + // as well, if the group is also constructed using 'new'). + Addendum_UnboxedLayout, + // When used by typed objects, the addendum stores a TypeDescr. Addendum_TypeDescr }; @@ -1038,7 +1068,7 @@ // format is indicated by the object's addendum kind. void *addendum_; - void setAddendum(AddendumKind kind, void *addendum); + void setAddendum(AddendumKind kind, void *addendum, bool writeBarrier = true); AddendumKind addendumKind() const { return (AddendumKind) @@ -1046,24 +1076,33 @@ } TypeNewScript *newScriptDontCheckGeneration() const { - return addendumKind() == Addendum_NewScript - ? reinterpret_cast(addendum_) - : nullptr; + if (addendumKind() == Addendum_NewScript) + return reinterpret_cast(addendum_); + return nullptr; + } + + UnboxedLayout *maybeUnboxedLayoutDontCheckGeneration() const { + if (addendumKind() == Addendum_UnboxedLayout) + return reinterpret_cast(addendum_); + return nullptr; } + TypeNewScript *anyNewScript(); + void detachNewScript(bool writeBarrier); + public: - TypeObjectFlags flags() { + ObjectGroupFlags flags() { maybeSweep(nullptr); return flags_; } - void addFlags(TypeObjectFlags flags) { + void addFlags(ObjectGroupFlags flags) { maybeSweep(nullptr); flags_ |= flags; } - void clearFlags(TypeObjectFlags flags) { + void clearFlags(ObjectGroupFlags flags) { maybeSweep(nullptr); flags_ &= ~flags; } @@ -1077,6 +1116,20 @@ setAddendum(Addendum_NewScript, newScript); } + UnboxedLayout *maybeUnboxedLayout() { + maybeSweep(nullptr); + return maybeUnboxedLayoutDontCheckGeneration(); + } + + UnboxedLayout &unboxedLayout() { + MOZ_ASSERT(addendumKind() == Addendum_UnboxedLayout); + return *maybeUnboxedLayout(); + } + + void setUnboxedLayout(UnboxedLayout *layout) { + setAddendum(Addendum_UnboxedLayout, layout); + } + TypeDescr *maybeTypeDescr() { // Note: there is no need to sweep when accessing the type descriptor // of an object, as it is strongly held and immutable. @@ -1112,21 +1165,21 @@ * types of all integer indexes of the object, and/or JSID_EMPTY, holding * constraints listening to changes to the object's state. * - * The type sets in the properties of a type object describe the possible - * values that can be read out of that property in actual JS objects. - * In native objects, property types account for plain data properties - * (those with a slot and no getter or setter hook) and dense elements. - * In typed objects, property types account for object and value properties - * and elements in the object. + * The type sets in the properties of a group describe the possible values + * that can be read out of that property in actual JS objects. In native + * objects, property types account for plain data properties (those with a + * slot and no getter or setter hook) and dense elements. In typed objects + * and unboxed objects, property types account for object and value + * properties and elements in the object. * * For accesses on these properties, the correspondence is as follows: * - * 1. If the type has unknownProperties(), the possible properties and + * 1. If the group has unknownProperties(), the possible properties and * value types for associated JSObjects are unknown. * - * 2. Otherwise, for any JSObject obj with TypeObject type, and any jsid id - * which is a property in obj, before obj->getProperty(id) the property - * in type for id must reflect the result of the getProperty. + * 2. Otherwise, for any |obj| in |group|, and any |id| which is a property + * in |obj|, before obj->getProperty(id) the property in |group| for + * |id| must reflect the result of the getProperty. * * There are several exceptions to this: * @@ -1140,9 +1193,10 @@ * 2. Array lengths are special cased by the compiler and VM and are not * reflected in property types. * - * 3. In typed objects, the initial values of properties (null pointers and - * undefined values) are not reflected in the property types. These - * values are always possible when reading the property. + * 3. In typed objects (but not unboxed objects), the initial values of + * properties (null pointers and undefined values) are not reflected in + * the property types. These values are always possible when reading the + * property. * * We establish these by using write barriers on calls to setProperty and * defineProperty which are on native properties, and on any jitcode which @@ -1151,13 +1205,13 @@ Property **propertySet; public: - inline TypeObject(const Class *clasp, TaggedProto proto, TypeObjectFlags initialFlags); + inline ObjectGroup(const Class *clasp, TaggedProto proto, ObjectGroupFlags initialFlags); - bool hasAnyFlags(TypeObjectFlags flags) { + bool hasAnyFlags(ObjectGroupFlags flags) { MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); return !!(this->flags() & flags); } - bool hasAllFlags(TypeObjectFlags flags) { + bool hasAllFlags(ObjectGroupFlags flags) { MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); return (this->flags() & flags) == flags; } @@ -1208,11 +1262,10 @@ void updateNewPropertyTypes(ExclusiveContext *cx, jsid id, HeapTypeSet *types); bool addDefiniteProperties(ExclusiveContext *cx, Shape *shape); bool matchDefiniteProperties(HandleObject obj); - void addPrototype(JSContext *cx, TypeObject *proto); void markPropertyNonData(ExclusiveContext *cx, jsid id); void markPropertyNonWritable(ExclusiveContext *cx, jsid id); void markStateChange(ExclusiveContext *cx); - void setFlags(ExclusiveContext *cx, TypeObjectFlags flags); + void setFlags(ExclusiveContext *cx, ObjectGroupFlags flags); void markUnknown(ExclusiveContext *cx); void maybeClearNewScriptOnOOM(); void clearNewScript(ExclusiveContext *cx); @@ -1240,28 +1293,27 @@ flags_ |= generation << OBJECT_FLAG_GENERATION_SHIFT; } - void fixupAfterMovingGC(); - size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; inline void finalize(FreeOp *fop); + void fixupAfterMovingGC() {} - static inline ThingRootKind rootKind() { return THING_ROOT_TYPE_OBJECT; } + static inline ThingRootKind rootKind() { return THING_ROOT_OBJECT_GROUP; } static inline uint32_t offsetOfClasp() { - return offsetof(TypeObject, clasp_); + return offsetof(ObjectGroup, clasp_); } static inline uint32_t offsetOfProto() { - return offsetof(TypeObject, proto_); + return offsetof(ObjectGroup, proto_); } static inline uint32_t offsetOfAddendum() { - return offsetof(TypeObject, addendum_); + return offsetof(ObjectGroup, addendum_); } static inline uint32_t offsetOfFlags() { - return offsetof(TypeObject, flags_); + return offsetof(ObjectGroup, flags_); } private: @@ -1269,28 +1321,28 @@ inline void setBasePropertyCount(uint32_t count); static void staticAsserts() { - JS_STATIC_ASSERT(offsetof(TypeObject, proto_) == offsetof(js::shadow::TypeObject, proto)); + JS_STATIC_ASSERT(offsetof(ObjectGroup, proto_) == offsetof(js::shadow::ObjectGroup, proto)); } }; /* - * Entries for the per-compartment set of type objects which are the default + * Entries for the per-compartment set of groups which are the default * types to use for some prototype. An optional associated object is used which - * allows multiple type objects to be created with the same prototype. The + * allows multiple groups to be created with the same prototype. The * associated object may be a function (for types constructed with 'new') or a * type descriptor (for typed objects). These entries are also used for the set - * of lazy type objects in the compartment, which use a null associated object + * of lazy groups in the compartment, which use a null associated object * (though there are only a few of these per compartment). */ -struct NewTypeObjectEntry +struct NewObjectGroupEntry { - ReadBarrieredTypeObject object; + ReadBarrieredObjectGroup group; // Note: This pointer is only used for equality and does not need a read barrier. JSObject *associated; - NewTypeObjectEntry(TypeObject *object, JSObject *associated) - : object(object), associated(associated) + NewObjectGroupEntry(ObjectGroup *group, JSObject *associated) + : group(group), associated(associated) {} struct Lookup { @@ -1314,17 +1366,18 @@ }; static inline HashNumber hash(const Lookup &lookup); - static inline bool match(const NewTypeObjectEntry &key, const Lookup &lookup); - static void rekey(NewTypeObjectEntry &k, const NewTypeObjectEntry& newKey) { k = newKey; } + static inline bool match(const NewObjectGroupEntry &key, const Lookup &lookup); + static void rekey(NewObjectGroupEntry &k, const NewObjectGroupEntry& newKey) { k = newKey; } }; -typedef HashSet NewTypeObjectTable; +typedef HashSet NewObjectGroupTable; -/* Whether to use a new type object when calling 'new' at script/pc. */ +// Whether to make a singleton when calling 'new' at script/pc. bool -UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc); +UseSingletonForNewObject(JSContext *cx, JSScript *script, jsbytecode *pc); +// Whether to make a deep cloned singleton when cloning fun. bool -UseNewTypeForClone(JSFunction *fun); +UseSingletonForClone(JSFunction *fun); /* * Whether Array.prototype, or an object on its proto chain, has an @@ -1373,8 +1426,8 @@ static inline TYPESET *BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMap, uint32_t *hint, TYPESET *typeArray); - /* Get a type object for an allocation site in this script. */ - static inline TypeObject *InitObject(JSContext *cx, JSScript *script, jsbytecode *pc, + /* Get a group for an allocation site in this script. */ + static inline ObjectGroup *InitGroup(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey kind); /* @@ -1446,7 +1499,7 @@ struct ArrayTableKey; typedef HashMap ArrayTypeTable; @@ -1456,55 +1509,49 @@ struct AllocationSiteKey; typedef HashMap AllocationSiteTable; class HeapTypeSetKey; -// Type set entry for either a JSObject with singleton type or a non-singleton TypeObject. -struct TypeObjectKey +// Type set entry for either a JSObject with singleton type or a non-singleton ObjectGroup. +struct ObjectGroupKey { - static intptr_t keyBits(TypeObjectKey *obj) { return (intptr_t) obj; } - static TypeObjectKey *getKey(TypeObjectKey *obj) { return obj; } + static intptr_t keyBits(ObjectGroupKey *obj) { return (intptr_t) obj; } + static ObjectGroupKey *getKey(ObjectGroupKey *obj) { return obj; } - static TypeObjectKey *get(JSObject *obj) { - MOZ_ASSERT(obj); - return (TypeObjectKey *) (uintptr_t(obj) | 1); - } - static TypeObjectKey *get(TypeObject *obj) { - MOZ_ASSERT(obj); - return (TypeObjectKey *) obj; - } + static inline ObjectGroupKey *get(JSObject *obj); + static inline ObjectGroupKey *get(ObjectGroup *group); - bool isTypeObject() { + bool isGroup() { return (uintptr_t(this) & 1) == 0; } - bool isSingleObject() { + bool isSingleton() { return (uintptr_t(this) & 1) != 0; } - inline TypeObject *asTypeObject(); - inline JSObject *asSingleObject(); + inline ObjectGroup *group(); + inline JSObject *singleton(); - inline TypeObject *asTypeObjectNoBarrier(); - inline JSObject *asSingleObjectNoBarrier(); + inline ObjectGroup *groupNoBarrier(); + inline JSObject *singletonNoBarrier(); const Class *clasp(); TaggedProto proto(); + TaggedProto protoMaybeInNursery(); bool hasTenuredProto(); - JSObject *singleton(); TypeNewScript *newScript(); bool unknownProperties(); - bool hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags); + bool hasFlags(CompilerConstraintList *constraints, ObjectGroupFlags flags); bool hasStableClassAndProto(CompilerConstraintList *constraints); void watchStateChangeForInlinedCall(CompilerConstraintList *constraints); void watchStateChangeForTypedArrayData(CompilerConstraintList *constraints); HeapTypeSetKey property(jsid id); void ensureTrackedProperty(JSContext *cx, jsid id); - TypeObject *maybeType(); + ObjectGroup *maybeGroup(); }; // Representation of a heap type property which may or may not be instantiated. @@ -1517,10 +1564,10 @@ // during generation of baseline caches. class HeapTypeSetKey { - friend struct TypeObjectKey; + friend struct ObjectGroupKey; // Object and property being accessed. - TypeObjectKey *object_; + ObjectGroupKey *object_; jsid id_; // If instantiated, the underlying heap type set. @@ -1531,7 +1578,7 @@ : object_(nullptr), id_(JSID_EMPTY), maybeTypes_(nullptr) {} - TypeObjectKey *object() const { return object_; } + ObjectGroupKey *object() const { return object_; } jsid id() const { return id_; } HeapTypeSet *maybeTypes() const { return maybeTypes_; } @@ -1653,8 +1700,8 @@ void setTypeToHomogenousArray(ExclusiveContext *cx, JSObject *obj, Type type); public: - void fixArrayType(ExclusiveContext *cx, ArrayObject *obj); - void fixObjectType(ExclusiveContext *cx, PlainObject *obj); + void fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj); + void fixObjectGroup(ExclusiveContext *cx, PlainObject *obj); void fixRestArgumentsType(ExclusiveContext *cx, ArrayObject *obj); JSObject *newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties); @@ -1664,20 +1711,14 @@ inline JSCompartment *compartment(); - /* Prints results of this compartment if spew is enabled or force is set. */ + // Prints results of this compartment if spew is enabled or force is set. void print(JSContext *cx, bool force); - /* - * Make a function or non-function object associated with an optional - * script. The 'key' parameter here may be an array, typed array, function - * or JSProto_Object to indicate a type whose class is unknown (not just - * js_ObjectClass). - */ - TypeObject *newTypeObject(ExclusiveContext *cx, const Class *clasp, Handle proto, - TypeObjectFlags initialFlags = 0); + ObjectGroup *newObjectGroup(ExclusiveContext *cx, const Class *clasp, Handle proto, + ObjectGroupFlags initialFlags = 0); - /* Get or make an object for an allocation site, and add to the allocation site table. */ - TypeObject *addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key); + // Get or make a group for an allocation site, and add to the allocation site table. + ObjectGroup *addAllocationSiteObjectGroup(JSContext *cx, AllocationSiteKey key); void clearTables(); void sweep(FreeOp *fop); @@ -1757,10 +1798,10 @@ void InferSpew(SpewChannel which, const char *fmt, ...); const char * TypeString(Type type); -const char * TypeObjectString(TypeObject *type); +const char * ObjectGroupString(ObjectGroup *group); -/* Check that the type property for id in obj contains value. */ -bool TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value); +/* Check that the type property for id in group contains value. */ +bool TypeHasProperty(JSContext *cx, ObjectGroup *group, jsid id, const Value &value); #else @@ -1769,21 +1810,21 @@ inline const char * InferSpewColor(TypeSet *types) { return nullptr; } inline void InferSpew(SpewChannel which, const char *fmt, ...) {} inline const char * TypeString(Type type) { return nullptr; } -inline const char * TypeObjectString(TypeObject *type) { return nullptr; } +inline const char * ObjectGroupString(ObjectGroup *group) { return nullptr; } #endif /* Print a warning, dump state and abort the program. */ -MOZ_NORETURN void TypeFailure(JSContext *cx, const char *fmt, ...); +MOZ_NORETURN MOZ_COLD void TypeFailure(JSContext *cx, const char *fmt, ...); } /* namespace types */ } /* namespace js */ -// JS::ubi::Nodes can point to js::LazyScripts; they're js::gc::Cell instances +// JS::ubi::Nodes can point to object groups; they're js::gc::Cell instances // with no associated compartment. namespace JS { namespace ubi { -template<> struct Concrete : TracerConcrete { }; +template<> struct Concrete : TracerConcrete { }; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsinferinlines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsinferinlines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsinferinlines.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsinferinlines.h 2015-02-03 14:33:32.000000000 +0000 @@ -22,6 +22,7 @@ #include "vm/SharedTypedArrayObject.h" #include "vm/StringObject.h" #include "vm/TypedArrayObject.h" +#include "vm/UnboxedObject.h" #include "jscntxtinlines.h" @@ -91,32 +92,50 @@ // Types ///////////////////////////////////////////////////////////////////// -inline TypeObject * -TypeObjectKey::asTypeObjectNoBarrier() +/* static */ inline ObjectGroupKey * +ObjectGroupKey::get(JSObject *obj) { - MOZ_ASSERT(isTypeObject()); - return (TypeObject *) this; + MOZ_ASSERT(obj); + if (obj->isSingleton()) + return (ObjectGroupKey *) (uintptr_t(obj) | 1); + return (ObjectGroupKey *) obj->group(); +} + +/* static */ inline ObjectGroupKey * +ObjectGroupKey::get(ObjectGroup *group) +{ + MOZ_ASSERT(group); + if (group->singleton()) + return (ObjectGroupKey *) (uintptr_t(group->singleton()) | 1); + return (ObjectGroupKey *) group; +} + +inline ObjectGroup * +ObjectGroupKey::groupNoBarrier() +{ + MOZ_ASSERT(isGroup()); + return (ObjectGroup *) this; } inline JSObject * -TypeObjectKey::asSingleObjectNoBarrier() +ObjectGroupKey::singletonNoBarrier() { - MOZ_ASSERT(isSingleObject()); + MOZ_ASSERT(isSingleton()); return (JSObject *) (uintptr_t(this) & ~1); } -inline TypeObject * -TypeObjectKey::asTypeObject() +inline ObjectGroup * +ObjectGroupKey::group() { - TypeObject *res = asTypeObjectNoBarrier(); - TypeObject::readBarrier(res); + ObjectGroup *res = groupNoBarrier(); + ObjectGroup::readBarrier(res); return res; } inline JSObject * -TypeObjectKey::asSingleObject() +ObjectGroupKey::singleton() { - JSObject *res = asSingleObjectNoBarrier(); + JSObject *res = singletonNoBarrier(); JSObject::readBarrier(res); return res; } @@ -124,21 +143,21 @@ /* static */ inline Type Type::ObjectType(JSObject *obj) { - if (obj->hasSingletonType()) + if (obj->isSingleton()) return Type(uintptr_t(obj) | 1); - return Type(uintptr_t(obj->type())); + return Type(uintptr_t(obj->group())); } /* static */ inline Type -Type::ObjectType(TypeObject *obj) +Type::ObjectType(ObjectGroup *group) { - if (obj->singleton()) - return Type(uintptr_t(obj->singleton()) | 1); - return Type(uintptr_t(obj)); + if (group->singleton()) + return Type(uintptr_t(group->singleton()) | 1); + return Type(uintptr_t(group)); } /* static */ inline Type -Type::ObjectType(TypeObjectKey *obj) +Type::ObjectType(ObjectGroupKey *obj) { return Type(uintptr_t(obj)); } @@ -250,9 +269,8 @@ * intermediate types (i.e. JITs) can use this to ensure that intermediate * information is not collected and does not change. * - * Pins inference results so that intermediate type information, TypeObjects - * and JSScripts won't be collected during GC. Does additional sanity checking - * that inference is not reentrant and that recompilations occur properly. + * Ensures that GC cannot occur. Does additional sanity checking that inference + * is not reentrant and that recompilations occur properly. */ struct AutoEnterAnalysis { @@ -363,27 +381,27 @@ } /* - * Get the default 'new' object for a given standard class, per the currently + * Get the default 'new' group for a given standard class, per the currently * active global. */ -inline TypeObject * -GetTypeNewObject(JSContext *cx, JSProtoKey key) +inline ObjectGroup * +GetNewObjectGroup(JSContext *cx, JSProtoKey key) { RootedObject proto(cx); if (key != JSProto_Null && !GetBuiltinPrototype(cx, key, &proto)) return nullptr; - return cx->getNewType(GetClassForProtoKey(key), TaggedProto(proto.get())); + return cx->getNewGroup(GetClassForProtoKey(key), TaggedProto(proto.get())); } -/* Get a type object for the immediate allocation site within a native. */ -inline TypeObject * -GetTypeCallerInitObject(JSContext *cx, JSProtoKey key) +/* Get a group for the immediate allocation site within a native. */ +inline ObjectGroup * +GetCallerInitGroup(JSContext *cx, JSProtoKey key) { jsbytecode *pc; RootedScript script(cx, cx->currentScript(&pc)); if (script) - return TypeScript::InitObject(cx, script, pc, key); - return GetTypeNewObject(cx, key); + return TypeScript::InitGroup(cx, script, pc, key); + return GetNewObjectGroup(cx, key); } void MarkIteratorUnknownSlow(JSContext *cx); @@ -408,10 +426,10 @@ inline bool TrackPropertyTypes(ExclusiveContext *cx, JSObject *obj, jsid id) { - if (obj->hasLazyType() || obj->type()->unknownProperties()) + if (obj->hasLazyGroup() || obj->group()->unknownProperties()) return false; - if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(id)) + if (obj->isSingleton() && !obj->group()->maybeGetProperty(id)) return false; return true; @@ -422,19 +440,19 @@ { id = IdToTypeId(id); - if (obj->hasSingletonType()) { + if (obj->isSingleton()) { AutoEnterAnalysis enter(cx); - if (obj->hasLazyType() && !obj->getType(cx)) { - CrashAtUnhandlableOOM("Could not allocate TypeObject in EnsureTrackPropertyTypes"); + if (obj->hasLazyGroup() && !obj->getGroup(cx)) { + CrashAtUnhandlableOOM("Could not allocate ObjectGroup in EnsureTrackPropertyTypes"); return; } - if (!obj->type()->unknownProperties() && !obj->type()->getProperty(cx, id)) { - MOZ_ASSERT(obj->type()->unknownProperties()); + if (!obj->group()->unknownProperties() && !obj->group()->getProperty(cx, id)) { + MOZ_ASSERT(obj->group()->unknownProperties()); return; } } - MOZ_ASSERT(obj->type()->unknownProperties() || TrackPropertyTypes(cx, obj, id)); + MOZ_ASSERT(obj->group()->unknownProperties() || TrackPropertyTypes(cx, obj, id)); } inline bool @@ -450,26 +468,26 @@ PropertyHasBeenMarkedNonConstant(JSObject *obj, jsid id) { // Non-constant properties are only relevant for singleton objects. - if (!obj->hasSingletonType()) + if (!obj->isSingleton()) return true; // EnsureTrackPropertyTypes must have been called on this object. - if (obj->type()->unknownProperties()) + if (obj->group()->unknownProperties()) return true; - HeapTypeSet *types = obj->type()->maybeGetProperty(IdToTypeId(id)); + HeapTypeSet *types = obj->group()->maybeGetProperty(IdToTypeId(id)); return types->nonConstantProperty(); } inline bool HasTypePropertyId(JSObject *obj, jsid id, Type type) { - if (obj->hasLazyType()) + if (obj->hasLazyGroup()) return true; - if (obj->type()->unknownProperties()) + if (obj->group()->unknownProperties()) return true; - if (HeapTypeSet *types = obj->type()->maybeGetProperty(IdToTypeId(id))) + if (HeapTypeSet *types = obj->group()->maybeGetProperty(IdToTypeId(id))) return types->hasType(type); return false; @@ -481,8 +499,8 @@ return HasTypePropertyId(obj, id, GetValueType(value)); } -void AddTypePropertyId(ExclusiveContext *cx, TypeObject *obj, jsid id, Type type); -void AddTypePropertyId(ExclusiveContext *cx, TypeObject *obj, jsid id, const Value &value); +void AddTypePropertyId(ExclusiveContext *cx, ObjectGroup *group, jsid id, Type type); +void AddTypePropertyId(ExclusiveContext *cx, ObjectGroup *group, jsid id, const Value &value); /* Add a possible type for a property of obj. */ inline void @@ -490,7 +508,7 @@ { id = IdToTypeId(id); if (TrackPropertyTypes(cx, obj, id)) - AddTypePropertyId(cx, obj->type(), id, type); + AddTypePropertyId(cx, obj->group(), id, type); } inline void @@ -498,19 +516,18 @@ { id = IdToTypeId(id); if (TrackPropertyTypes(cx, obj, id)) - AddTypePropertyId(cx, obj->type(), id, value); + AddTypePropertyId(cx, obj->group(), id, value); } -/* Set one or more dynamic flags on a type object. */ inline void -MarkTypeObjectFlags(ExclusiveContext *cx, JSObject *obj, TypeObjectFlags flags) +MarkObjectGroupFlags(ExclusiveContext *cx, JSObject *obj, ObjectGroupFlags flags) { - if (!obj->hasLazyType() && !obj->type()->hasAllFlags(flags)) - obj->type()->setFlags(cx, flags); + if (!obj->hasLazyGroup() && !obj->group()->hasAllFlags(flags)) + obj->group()->setFlags(cx, flags); } inline void -MarkTypeObjectUnknownProperties(JSContext *cx, TypeObject *obj) +MarkObjectGroupUnknownProperties(JSContext *cx, ObjectGroup *obj) { if (!obj->unknownProperties()) obj->markUnknown(cx); @@ -521,7 +538,7 @@ { id = IdToTypeId(id); if (TrackPropertyTypes(cx, obj, id)) - obj->type()->markPropertyNonData(cx, id); + obj->group()->markPropertyNonData(cx, id); } inline void @@ -529,27 +546,27 @@ { id = IdToTypeId(id); if (TrackPropertyTypes(cx, obj, id)) - obj->type()->markPropertyNonWritable(cx, id); + obj->group()->markPropertyNonWritable(cx, id); } inline bool IsTypePropertyIdMarkedNonData(JSObject *obj, jsid id) { - return obj->type()->isPropertyNonData(id); + return obj->group()->isPropertyNonData(id); } inline bool IsTypePropertyIdMarkedNonWritable(JSObject *obj, jsid id) { - return obj->type()->isPropertyNonWritable(id); + return obj->group()->isPropertyNonWritable(id); } /* Mark a state change on a particular object. */ inline void MarkObjectStateChange(ExclusiveContext *cx, JSObject *obj) { - if (!obj->hasLazyType() && !obj->type()->unknownProperties()) - obj->type()->markStateChange(cx); + if (!obj->hasLazyGroup() && !obj->group()->unknownProperties()) + obj->group()->markStateChange(cx); } /* @@ -558,15 +575,15 @@ */ inline void -FixArrayType(ExclusiveContext *cx, ArrayObject *obj) +FixArrayGroup(ExclusiveContext *cx, ArrayObject *obj) { - cx->compartment()->types.fixArrayType(cx, obj); + cx->compartment()->types.fixArrayGroup(cx, obj); } inline void -FixObjectType(ExclusiveContext *cx, PlainObject *obj) +FixObjectGroup(ExclusiveContext *cx, PlainObject *obj) { - cx->compartment()->types.fixObjectType(cx, obj); + cx->compartment()->types.fixObjectGroup(cx, obj); } /* Interface helpers for JSScript*. */ @@ -681,22 +698,22 @@ } }; -/* Whether to use a new type object for an initializer opcode at script/pc. */ +/* Whether to use a singleton kind for an initializer opcode at script/pc. */ js::NewObjectKind -UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key); +UseSingletonForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key); js::NewObjectKind -UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp); +UseSingletonForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp); -/* static */ inline TypeObject * -TypeScript::InitObject(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey kind) +/* static */ inline ObjectGroup * +TypeScript::InitGroup(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey kind) { - MOZ_ASSERT(!UseNewTypeForInitializer(script, pc, kind)); + MOZ_ASSERT(!UseSingletonForInitializer(script, pc, kind)); uint32_t offset = script->pcToOffset(pc); if (offset >= AllocationSiteKey::OFFSET_LIMIT) - return GetTypeNewObject(cx, kind); + return GetNewObjectGroup(cx, kind); AllocationSiteKey key; key.script = script; @@ -704,25 +721,26 @@ key.kind = kind; if (!cx->compartment()->types.allocationSiteTable) - return cx->compartment()->types.addAllocationSiteTypeObject(cx, key); + return cx->compartment()->types.addAllocationSiteObjectGroup(cx, key); AllocationSiteTable::Ptr p = cx->compartment()->types.allocationSiteTable->lookup(key); if (p) return p->value(); - return cx->compartment()->types.addAllocationSiteTypeObject(cx, key); + return cx->compartment()->types.addAllocationSiteObjectGroup(cx, key); } -/* Set the type to use for obj according to the site it was allocated at. */ +/* Set the group to use for obj according to the site it was allocated at. */ static inline bool -SetInitializerObjectType(JSContext *cx, HandleScript script, jsbytecode *pc, HandleObject obj, NewObjectKind kind) +SetInitializerObjectGroup(JSContext *cx, HandleScript script, jsbytecode *pc, HandleObject obj, + NewObjectKind kind) { JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass()); MOZ_ASSERT(key != JSProto_Null); - MOZ_ASSERT(kind == UseNewTypeForInitializer(script, pc, key)); + MOZ_ASSERT(kind == UseSingletonForInitializer(script, pc, key)); if (kind == SingletonObject) { - MOZ_ASSERT(obj->hasSingletonType()); + MOZ_ASSERT(obj->isSingleton()); /* * Inference does not account for types of run-once initializer @@ -731,10 +749,10 @@ */ TypeScript::Monitor(cx, script, pc, ObjectValue(*obj)); } else { - types::TypeObject *type = TypeScript::InitObject(cx, script, pc, key); - if (!type) + types::ObjectGroup *group = TypeScript::InitGroup(cx, script, pc, key); + if (!group) return false; - obj->uninlinedSetType(type); + obj->uninlinedSetGroup(group); } return true; @@ -757,7 +775,7 @@ /* static */ inline void TypeScript::MonitorAssign(JSContext *cx, HandleObject obj, jsid id) { - if (!obj->hasSingletonType()) { + if (!obj->isSingleton()) { /* * Mark as unknown any object which has had dynamic assignments to * non-integer properties at SETELEM opcodes. This avoids making large @@ -773,10 +791,10 @@ // But if we don't have too many properties yet, don't do anything. The // idea here is that normal object initialization should not trigger // deoptimization in most cases, while actual usage as a hashmap should. - TypeObject* type = obj->type(); - if (type->getPropertyCount() < 128) + ObjectGroup* group = obj->group(); + if (group->getPropertyCount() < 128) return; - MarkTypeObjectUnknownProperties(cx, type); + MarkObjectGroupUnknownProperties(cx, group); } } @@ -1008,35 +1026,35 @@ return nullptr; } -inline TypeObjectKey * +inline ObjectGroupKey * Type::objectKey() const { MOZ_ASSERT(isObject()); - return (TypeObjectKey *) data; + return (ObjectGroupKey *) data; } inline JSObject * -Type::singleObject() const +Type::singleton() const { - return objectKey()->asSingleObject(); + return objectKey()->singleton(); } -inline TypeObject * -Type::typeObject() const +inline ObjectGroup * +Type::group() const { - return objectKey()->asTypeObject(); + return objectKey()->group(); } inline JSObject * -Type::singleObjectNoBarrier() const +Type::singletonNoBarrier() const { - return objectKey()->asSingleObjectNoBarrier(); + return objectKey()->singletonNoBarrier(); } -inline TypeObject * -Type::typeObjectNoBarrier() const +inline ObjectGroup * +Type::groupNoBarrier() const { - return objectKey()->asTypeObjectNoBarrier(); + return objectKey()->groupNoBarrier(); } inline bool @@ -1053,7 +1071,7 @@ return !!(flags & TYPE_FLAG_ANYOBJECT); } else { return !!(flags & TYPE_FLAG_ANYOBJECT) || - HashSetLookup + HashSetLookup (objectSet, baseObjectCount(), type.objectKey()) != nullptr; } } @@ -1082,18 +1100,12 @@ } inline void -HeapTypeSet::setNonDataPropertyIgnoringConstraints() -{ - flags |= TYPE_FLAG_NON_DATA_PROPERTY; -} - -inline void HeapTypeSet::setNonDataProperty(ExclusiveContext *cx) { if (flags & TYPE_FLAG_NON_DATA_PROPERTY) return; - setNonDataPropertyIgnoringConstraints(); + flags |= TYPE_FLAG_NON_DATA_PROPERTY; newPropertyState(cx); } @@ -1127,60 +1139,61 @@ return count; } -inline TypeObjectKey * +inline ObjectGroupKey * TypeSet::getObject(unsigned i) const { MOZ_ASSERT(i < getObjectCount()); if (baseObjectCount() == 1) { MOZ_ASSERT(i == 0); - return (TypeObjectKey *) objectSet; + return (ObjectGroupKey *) objectSet; } return objectSet[i]; } inline JSObject * -TypeSet::getSingleObject(unsigned i) const +TypeSet::getSingleton(unsigned i) const { - TypeObjectKey *key = getObject(i); - return (key && key->isSingleObject()) ? key->asSingleObject() : nullptr; + ObjectGroupKey *key = getObject(i); + return (key && key->isSingleton()) ? key->singleton() : nullptr; } -inline TypeObject * -TypeSet::getTypeObject(unsigned i) const +inline ObjectGroup * +TypeSet::getGroup(unsigned i) const { - TypeObjectKey *key = getObject(i); - return (key && key->isTypeObject()) ? key->asTypeObject() : nullptr; + ObjectGroupKey *key = getObject(i); + return (key && key->isGroup()) ? key->group() : nullptr; } inline JSObject * -TypeSet::getSingleObjectNoBarrier(unsigned i) const +TypeSet::getSingletonNoBarrier(unsigned i) const { - TypeObjectKey *key = getObject(i); - return (key && key->isSingleObject()) ? key->asSingleObjectNoBarrier() : nullptr; + ObjectGroupKey *key = getObject(i); + return (key && key->isSingleton()) ? key->singletonNoBarrier() : nullptr; } -inline TypeObject * -TypeSet::getTypeObjectNoBarrier(unsigned i) const +inline ObjectGroup * +TypeSet::getGroupNoBarrier(unsigned i) const { - TypeObjectKey *key = getObject(i); - return (key && key->isTypeObject()) ? key->asTypeObjectNoBarrier() : nullptr; + ObjectGroupKey *key = getObject(i); + return (key && key->isGroup()) ? key->groupNoBarrier() : nullptr; } inline const Class * TypeSet::getObjectClass(unsigned i) const { - if (JSObject *object = getSingleObject(i)) + if (JSObject *object = getSingleton(i)) return object->getClass(); - if (TypeObject *object = getTypeObject(i)) - return object->clasp(); + if (ObjectGroup *group = getGroup(i)) + return group->clasp(); return nullptr; } ///////////////////////////////////////////////////////////////////// -// TypeObject +// ObjectGroup ///////////////////////////////////////////////////////////////////// -inline TypeObject::TypeObject(const Class *clasp, TaggedProto proto, TypeObjectFlags initialFlags) +inline +ObjectGroup::ObjectGroup(const Class *clasp, TaggedProto proto, ObjectGroupFlags initialFlags) { mozilla::PodZero(this); @@ -1193,23 +1206,24 @@ setGeneration(zone()->types.generation); - InferSpew(ISpewOps, "newObject: %s", TypeObjectString(this)); + InferSpew(ISpewOps, "newGroup: %s", ObjectGroupString(this)); } inline void -TypeObject::finalize(FreeOp *fop) +ObjectGroup::finalize(FreeOp *fop) { fop->delete_(newScriptDontCheckGeneration()); + fop->delete_(maybeUnboxedLayoutDontCheckGeneration()); } inline uint32_t -TypeObject::basePropertyCount() +ObjectGroup::basePropertyCount() { return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT; } inline void -TypeObject::setBasePropertyCount(uint32_t count) +ObjectGroup::setBasePropertyCount(uint32_t count) { // Note: Callers must ensure they are performing threadsafe operations. MOZ_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT); @@ -1218,7 +1232,7 @@ } inline HeapTypeSet * -TypeObject::getProperty(ExclusiveContext *cx, jsid id) +ObjectGroup::getProperty(ExclusiveContext *cx, jsid id) { MOZ_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id) || JSID_IS_SYMBOL(id)); MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); @@ -1259,7 +1273,7 @@ } inline HeapTypeSet * -TypeObject::maybeGetProperty(jsid id) +ObjectGroup::maybeGetProperty(jsid id) { MOZ_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id) || JSID_IS_SYMBOL(id)); MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); @@ -1272,7 +1286,7 @@ } inline unsigned -TypeObject::getPropertyCount() +ObjectGroup::getPropertyCount() { uint32_t count = basePropertyCount(); if (count > SET_ARRAY_SIZE) @@ -1281,7 +1295,7 @@ } inline Property * -TypeObject::getProperty(unsigned i) +ObjectGroup::getProperty(unsigned i) { MOZ_ASSERT(i < getPropertyCount()); if (basePropertyCount() == 1) { @@ -1294,10 +1308,10 @@ inline void TypeNewScript::writeBarrierPre(TypeNewScript *newScript) { - if (!newScript->fun->runtimeFromAnyThread()->needsIncrementalBarrier()) + if (!newScript->function()->runtimeFromAnyThread()->needsIncrementalBarrier()) return; - JS::Zone *zone = newScript->fun->zoneFromAnyThread(); + JS::Zone *zone = newScript->function()->zoneFromAnyThread(); if (zone->needsIncrementalBarrier()) newScript->trace(zone->barrierTracer()); } @@ -1324,8 +1338,8 @@ { static types::Type initial() { return types::Type::UnknownType(); } static bool poisoned(const types::Type &v) { - return (v.isTypeObject() && IsPoisonedPtr(v.typeObject())) - || (v.isSingleObject() && IsPoisonedPtr(v.singleObject())); + return (v.isGroup() && IsPoisonedPtr(v.group())) + || (v.isSingleton() && IsPoisonedPtr(v.singleton())); } }; @@ -1334,8 +1348,8 @@ { static types::Type initial() { return types::Type::UnknownType(); } static bool poisoned(const types::Type &v) { - return (v.isTypeObject() && IsPoisonedPtr(v.typeObject())) - || (v.isSingleObject() && IsPoisonedPtr(v.singleObject())); + return (v.isGroup() && IsPoisonedPtr(v.group())) + || (v.isSingleton() && IsPoisonedPtr(v.singleton())); } }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsiter.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsiter.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsiter.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsiter.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -483,8 +483,9 @@ NewPropertyIteratorObject(JSContext *cx, unsigned flags) { if (flags & JSITER_ENUMERATE) { - RootedTypeObject type(cx, cx->getNewType(&PropertyIteratorObject::class_, TaggedProto(nullptr))); - if (!type) + RootedObjectGroup group(cx, cx->getNewGroup(&PropertyIteratorObject::class_, + TaggedProto(nullptr))); + if (!group) return nullptr; JSObject *metadata = nullptr; @@ -498,7 +499,7 @@ return nullptr; JSObject *obj = JSObject::create(cx, ITERATOR_FINALIZE_KIND, - GetInitialHeap(GenericObject, clasp), shape, type); + GetInitialHeap(GenericObject, clasp), shape, group); if (!obj) return nullptr; PropertyIteratorObject *res = &obj->as(); @@ -577,9 +578,9 @@ { MOZ_ASSERT(!(flags & JSITER_FOREACH)); - if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx)) + if (obj->isSingleton() && !obj->setIteratedSingleton(cx)) return false; - types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED); + types::MarkObjectGroupFlags(cx, obj, types::OBJECT_FLAG_ITERATED); Rooted iterobj(cx, NewPropertyIteratorObject(cx, flags)); if (!iterobj) @@ -620,9 +621,9 @@ { MOZ_ASSERT(flags & JSITER_FOREACH); - if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx)) + if (obj->isSingleton() && !obj->setIteratedSingleton(cx)) return false; - types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED); + types::MarkObjectGroupFlags(cx, obj, types::OBJECT_FLAG_ITERATED); Rooted iterobj(cx, NewPropertyIteratorObject(cx, flags)); if (!iterobj) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsmath.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsmath.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsmath.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsmath.cpp 2015-02-03 14:33:32.000000000 +0000 @@ -1378,6 +1378,50 @@ return hypot(x, y); } +static inline +void +hypot_step(double &scale, double &sumsq, double x) +{ + double xabs = mozilla::Abs(x); + if (scale < xabs) { + sumsq = 1 + sumsq * (scale / xabs) * (scale / xabs); + scale = xabs; + } else if (scale != 0) { + sumsq += (xabs / scale) * (xabs / scale); + } +} + +double +js::hypot4(double x, double y, double z, double w) +{ + /* Check for infinity or NaNs so that we can return immediatelly. + * Does not need to be WIN_XP specific as ecmaHypot + */ + if (mozilla::IsInfinite(x) || mozilla::IsInfinite(y) || + mozilla::IsInfinite(z) || mozilla::IsInfinite(w)) + return mozilla::PositiveInfinity(); + + if (mozilla::IsNaN(x) || mozilla::IsNaN(y) || mozilla::IsNaN(z) || + mozilla::IsNaN(w)) + return GenericNaN(); + + double scale = 0; + double sumsq = 1; + + hypot_step(scale, sumsq, x); + hypot_step(scale, sumsq, y); + hypot_step(scale, sumsq, z); + hypot_step(scale, sumsq, w); + + return scale * sqrt(sumsq); +} + +double +js::hypot3(double x, double y, double z) +{ + return hypot4(x, y, z, 0.0); +} + bool js::math_hypot(JSContext *cx, unsigned argc, Value *vp) { @@ -1418,14 +1462,7 @@ if (isInfinite || isNaN) continue; - double xabs = mozilla::Abs(x); - - if (scale < xabs) { - sumsq = 1 + sumsq * (scale / xabs) * (scale / xabs); - scale = xabs; - } else if (scale != 0) { - sumsq += (xabs / scale) * (xabs / scale); - } + hypot_step(scale, sumsq, x); } double result = isInfinite ? PositiveInfinity() : diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsmath.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsmath.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsmath.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsmath.h 2015-02-03 14:33:32.000000000 +0000 @@ -245,6 +245,12 @@ extern double ecmaHypot(double x, double y); +extern double +hypot3(double x, double y, double z); + +extern double +hypot4(double x, double y, double z, double w); + extern bool math_hypot(JSContext *cx, unsigned argc, Value *vp); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsobj.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsobj.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsobj.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsobj.cpp 2015-02-03 14:33:33.000000000 +0000 @@ -538,7 +538,7 @@ { /* 8.12.9 step 1. */ RootedShape shape(cx); - MOZ_ASSERT(!obj->getOps()->lookupGeneric); + MOZ_ASSERT(!obj->getOps()->lookupProperty); if (!NativeLookupOwnProperty(cx, obj, id, &shape)) return false; @@ -892,6 +892,61 @@ return DefinePropertyOnObject(cx, arr, id, desc, throwError, rval); } +// ES6 draft rev31 9.4.5.3 [[DefineOwnProperty]] +static bool +DefinePropertyOnTypedArray(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc, + bool throwError, bool *rval) +{ + + MOZ_ASSERT(IsAnyTypedArray(obj)); + // Steps 3.a-c. + uint64_t index; + if (IsTypedArrayIndex(id, &index)) { + // These are all substeps of 3.c. + // Steps i-vi. + // We (wrongly) ignore out of range defines with a value. + if (index >= AnyTypedArrayLength(obj)) { + *rval = true; + return true; + } + + // Step vii. + if (desc.isAccessorDescriptor()) + return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval); + + // Step viii. + if (desc.hasConfigurable() && desc.configurable()) + return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval); + + // Step ix. + if (desc.hasEnumerable() && !desc.enumerable()) + return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval); + + // Step x. + if (desc.hasWritable() && !desc.writable()) + return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval); + + // Step xi. + if (desc.hasValue()) { + double d; + if (!ToNumber(cx, desc.value(), &d)) + return false; + + if (obj->is()) + TypedArrayObject::setElement(obj->as(), index, d); + else + SharedTypedArrayObject::setElement(obj->as(), index, d); + } + + // Step xii. + *rval = true; + return true; + } + + // Step 4. + return DefinePropertyOnObject(cx, obj.as(), id, desc, throwError, rval); +} + bool js::StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc, bool throwError, bool *rval) @@ -901,7 +956,13 @@ return DefinePropertyOnArray(cx, arr, id, desc, throwError, rval); } - if (obj->getOps()->lookupGeneric) { + if (IsAnyTypedArray(obj)) + return DefinePropertyOnTypedArray(cx, obj, id, desc, throwError, rval); + + if (obj->is() && !obj->as().convertToNative(cx)) + return false; + + if (obj->getOps()->lookupProperty) { if (obj->is()) { Rooted pd(cx); desc.populatePropertyDescriptor(obj, &pd); @@ -963,7 +1024,19 @@ return true; } - if (obj->getOps()->lookupGeneric) { + if (IsAnyTypedArray(obj)) { + bool dummy; + for (size_t i = 0, len = ids.length(); i < len; i++) { + if (!DefinePropertyOnTypedArray(cx, obj, ids[i], descs[i], true, &dummy)) + return false; + } + return true; + } + + if (obj->is() && !obj->as().convertToNative(cx)) + return false; + + if (obj->getOps()->lookupProperty) { if (obj->is()) { Rooted pd(cx); for (size_t i = 0, len = ids.length(); i < len; i++) { @@ -986,16 +1059,16 @@ return true; } -js::types::TypeObject* -JSObject::uninlinedGetType(JSContext *cx) +js::types::ObjectGroup* +JSObject::uninlinedGetGroup(JSContext *cx) { - return getType(cx); + return getGroup(cx); } void -JSObject::uninlinedSetType(js::types::TypeObject *newType) +JSObject::uninlinedSetGroup(js::types::ObjectGroup *group) { - setType(newType); + setGroup(group); } @@ -1102,7 +1175,7 @@ // Ordinarily ArraySetLength handles this, but we're going behind its back // right now, so we must do this manually. Neither the custom property - // tree mutations nor the setGenericAttributes call in the above code will + // tree mutations nor the setPropertyAttributes call in the above code will // do this for us. // // ArraySetLength also implements the capacity <= length invariant for @@ -1195,17 +1268,17 @@ } static inline JSObject * -NewObject(ExclusiveContext *cx, types::TypeObject *type_, JSObject *parent, gc::AllocKind kind, +NewObject(ExclusiveContext *cx, types::ObjectGroup *groupArg, JSObject *parent, gc::AllocKind kind, NewObjectKind newKind) { - const Class *clasp = type_->clasp(); + const Class *clasp = groupArg->clasp(); MOZ_ASSERT(clasp != &ArrayObject::class_); MOZ_ASSERT_IF(clasp == &JSFunction::class_, kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind); MOZ_ASSERT_IF(parent, &parent->global() == cx->global()); - RootedTypeObject type(cx, type_); + RootedObjectGroup group(cx, groupArg); JSObject *metadata = nullptr; if (!NewObjectMetadata(cx, &metadata)) @@ -1218,19 +1291,19 @@ ? GetGCKindSlots(gc::GetGCObjectKind(clasp), clasp) : GetGCKindSlots(kind, clasp); - RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, type->proto(), + RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, group->proto(), parent, metadata, nfixed)); if (!shape) return nullptr; gc::InitialHeap heap = GetInitialHeap(newKind, clasp); - JSObject *obj = JSObject::create(cx, kind, heap, shape, type); + JSObject *obj = JSObject::create(cx, kind, heap, shape, group); if (!obj) return nullptr; if (newKind == SingletonObject) { RootedObject nobj(cx, obj); - if (!JSObject::setSingletonType(cx, nobj)) + if (!JSObject::setSingleton(cx, nobj)) return nullptr; obj = nobj; } @@ -1294,8 +1367,8 @@ Rooted proto(cxArg, protoArg); RootedObject parent(cxArg, parentArg); - types::TypeObject *type = cxArg->getNewType(clasp, proto, nullptr); - if (!type) + types::ObjectGroup *group = cxArg->getNewGroup(clasp, proto, nullptr); + if (!group) return nullptr; /* @@ -1305,7 +1378,7 @@ if (!parent && proto.isObject()) parent = proto.toObject()->getParent(); - RootedObject obj(cxArg, NewObject(cxArg, type, parent, allocKind, newKind)); + RootedObject obj(cxArg, NewObject(cxArg, group, parent, allocKind, newKind)); if (!obj) return nullptr; @@ -1331,6 +1404,90 @@ return JSProto_Null; } +static inline bool +NativeGetPureInline(NativeObject *pobj, Shape *shape, MutableHandleValue vp) +{ + if (shape->hasSlot()) { + vp.set(pobj->getSlot(shape->slot())); + MOZ_ASSERT(!vp.isMagic()); + } else { + vp.setUndefined(); + } + + /* Fail if we have a custom getter. */ + return shape->hasDefaultGetter(); +} + +static bool +FindClassPrototype(ExclusiveContext *cx, MutableHandleObject protop, const Class *clasp) +{ + protop.set(nullptr); + + JSAtom *atom = Atomize(cx, clasp->name, strlen(clasp->name)); + if (!atom) + return false; + RootedId id(cx, AtomToId(atom)); + + RootedObject pobj(cx); + RootedShape shape(cx); + if (!NativeLookupProperty(cx, cx->global(), id, &pobj, &shape)) + return false; + + RootedObject ctor(cx); + if (shape && pobj->isNative()) { + if (shape->hasSlot()) { + RootedValue v(cx, pobj->as().getSlot(shape->slot())); + if (v.isObject()) + ctor.set(&v.toObject()); + } + } + + if (ctor && ctor->is()) { + JSFunction *nctor = &ctor->as(); + RootedValue v(cx); + if (cx->isJSContext()) { + if (!GetProperty(cx->asJSContext(), ctor, ctor, cx->names().prototype, &v)) + return false; + } else { + Shape *shape = nctor->lookup(cx, cx->names().prototype); + if (!shape || !NativeGetPureInline(nctor, shape, &v)) + return false; + } + if (v.isObject()) + protop.set(&v.toObject()); + } + return true; +} + +// Find the appropriate proto for a class. There are three different ways to achieve this: +// 1. Built-in classes have a cached proto and anonymous classes get Object.prototype. +// 2. Lookup global[clasp->name].prototype +// 3. Fallback to Object.prototype +// +// Step 2 is in some circumstances an observable operation, which is probably wrong +// as a matter of specifications. It's legacy garbage that we're working to remove eventually. +static bool +FindProto(ExclusiveContext *cx, const js::Class *clasp, MutableHandleObject proto) +{ + JSProtoKey protoKey = ClassProtoKeyOrAnonymousOrNull(clasp); + if (protoKey != JSProto_Null) + return GetBuiltinPrototype(cx, protoKey, proto); + + if (!FindClassPrototype(cx, proto, clasp)) + return false; + + if (!proto) { + // We're looking for the prototype of a class that is currently being + // resolved; the global object's resolve hook is on the + // stack. js::FindClassPrototype detects this goofy case and returns + // true with proto null. Fall back on Object.prototype. + MOZ_ASSERT(JSCLASS_CACHED_PROTO_KEY(clasp) == JSProto_Null); + return GetBuiltinPrototype(cx, JSProto_Object, proto); + } + return true; +} + + JSObject * js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg, const js::Class *clasp, JSObject *protoArg, JSObject *parentArg, @@ -1392,11 +1549,11 @@ return nullptr; Rooted taggedProto(cxArg, TaggedProto(proto)); - types::TypeObject *type = cxArg->getNewType(clasp, taggedProto); - if (!type) + types::ObjectGroup *group = cxArg->getNewGroup(clasp, taggedProto); + if (!group) return nullptr; - JSObject *obj = NewObject(cxArg, type, parent, allocKind, newKind); + JSObject *obj = NewObject(cxArg, group, parent, allocKind, newKind); if (!obj) return nullptr; @@ -1412,45 +1569,45 @@ } /* - * Create a plain object with the specified type. This bypasses getNewType to + * Create a plain object with the specified group. This bypasses getNewGroup to * avoid losing creation site information for objects made by scripted 'new'. */ JSObject * -js::NewObjectWithTypeCommon(JSContext *cx, HandleTypeObject type, JSObject *parent, gc::AllocKind allocKind, - NewObjectKind newKind) +js::NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, JSObject *parent, + gc::AllocKind allocKind, NewObjectKind newKind) { MOZ_ASSERT(parent); MOZ_ASSERT(allocKind <= gc::FINALIZE_OBJECT_LAST); - if (CanBeFinalizedInBackground(allocKind, type->clasp())) + if (CanBeFinalizedInBackground(allocKind, group->clasp())) allocKind = GetBackgroundAllocKind(allocKind); NewObjectCache &cache = cx->runtime()->newObjectCache; NewObjectCache::EntryIndex entry = -1; - if (type->proto().isObject() && - parent == type->proto().toObject()->getParent() && + if (group->proto().isObject() && + parent == group->proto().toObject()->getParent() && newKind == GenericObject && - type->clasp()->isNative() && + group->clasp()->isNative() && !cx->compartment()->hasObjectMetadataCallback()) { - if (cache.lookupType(type, allocKind, &entry)) { - JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, type->clasp())); + if (cache.lookupGroup(group, allocKind, &entry)) { + JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, group->clasp())); if (obj) { return obj; } else { - obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, type->clasp())); - parent = type->proto().toObject()->getParent(); + obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, group->clasp())); + parent = group->proto().toObject()->getParent(); } } } - JSObject *obj = NewObject(cx, type, parent, allocKind, newKind); + JSObject *obj = NewObject(cx, group, parent, allocKind, newKind); if (!obj) return nullptr; if (entry != -1 && !obj->as().hasDynamicSlots()) - cache.fillType(entry, type, allocKind, &obj->as()); + cache.fillGroup(entry, group, allocKind, &obj->as()); return obj; } @@ -1462,15 +1619,15 @@ RootedScript script(cx, cx->currentScript(&pc)); gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_); NewObjectKind newKind = script - ? UseNewTypeForInitializer(script, pc, &PlainObject::class_) + ? UseSingletonForInitializer(script, pc, &PlainObject::class_) : GenericObject; RootedObject obj(cx, NewBuiltinClassInstance(cx, allocKind, newKind)); if (!obj) return false; if (script) { - /* Try to specialize the type of the object to the scripted call site. */ - if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind)) + /* Try to specialize the group of the object to the scripted call site. */ + if (!types::SetInitializerObjectGroup(cx, script, pc, obj, newKind)) return false; } @@ -1491,17 +1648,20 @@ return NewObjectWithClassProto(cx, newclasp, proto, parent, kind); } -static inline PlainObject * -CreateThisForFunctionWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, - NewObjectKind newKind) +static inline JSObject * +CreateThisForFunctionWithGroup(JSContext *cx, HandleObjectGroup group, JSObject *parent, + NewObjectKind newKind) { - if (types::TypeNewScript *newScript = type->newScript()) { + if (group->maybeUnboxedLayout() && newKind != SingletonObject) + return UnboxedPlainObject::create(cx, group, newKind); + + if (types::TypeNewScript *newScript = group->newScript()) { if (newScript->analyzed()) { // The definite properties analysis has been performed for this - // type, so get the shape and finalize kind to use from the + // group, so get the shape and finalize kind to use from the // TypeNewScript's template. RootedPlainObject templateObject(cx, newScript->templateObject()); - MOZ_ASSERT(templateObject->type() == type); + MOZ_ASSERT(templateObject->group() == group); RootedPlainObject res(cx, CopyInitializerObject(cx, templateObject, newKind)); if (!res) @@ -1512,7 +1672,7 @@ if (!res->splicePrototype(cx, &PlainObject::class_, proto)) return nullptr; } else { - res->setType(type); + res->setGroup(group); } return res; } @@ -1522,11 +1682,11 @@ if (newKind == GenericObject) newKind = MaybeSingletonObject; - // Not enough objects with this type have been created yet, so make a - // plain object and register it with the type. Use the maximum number + // Not enough objects with this group have been created yet, so make a + // plain object and register it with the group. Use the maximum number // of fixed slots, as is also required by the TypeNewScript. gc::AllocKind allocKind = GuessObjectGCKind(NativeObject::MAX_FIXED_SLOTS); - PlainObject *res = NewObjectWithType(cx, type, parent, allocKind, newKind); + PlainObject *res = NewObjectWithGroup(cx, group, parent, allocKind, newKind); if (!res) return nullptr; @@ -1537,35 +1697,35 @@ } gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_); - return NewObjectWithType(cx, type, parent, allocKind, newKind); + return NewObjectWithGroup(cx, group, parent, allocKind, newKind); } -PlainObject * +JSObject * js::CreateThisForFunctionWithProto(JSContext *cx, HandleObject callee, JSObject *proto, NewObjectKind newKind /* = GenericObject */) { - RootedPlainObject res(cx); + RootedObject res(cx); if (proto) { - RootedTypeObject type(cx, cx->getNewType(&PlainObject::class_, TaggedProto(proto), - &callee->as())); - if (!type) + RootedObjectGroup group(cx, cx->getNewGroup(nullptr, TaggedProto(proto), + &callee->as())); + if (!group) return nullptr; - if (type->newScript() && !type->newScript()->analyzed()) { + if (group->newScript() && !group->newScript()->analyzed()) { bool regenerate; - if (!type->newScript()->maybeAnalyze(cx, type, ®enerate)) + if (!group->newScript()->maybeAnalyze(cx, group, ®enerate)) return nullptr; if (regenerate) { // The script was analyzed successfully and may have changed - // the new type table, so refetch the type. - type = cx->getNewType(&PlainObject::class_, TaggedProto(proto), - &callee->as()); - MOZ_ASSERT(type && type->newScript()); + // the new type table, so refetch the group. + group = cx->getNewGroup(nullptr, TaggedProto(proto), + &callee->as()); + MOZ_ASSERT(group && group->newScript()); } } - res = CreateThisForFunctionWithType(cx, type, callee->getParent(), newKind); + res = CreateThisForFunctionWithGroup(cx, group, callee->getParent(), newKind); } else { gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_); res = NewObjectWithProto(cx, proto, callee->getParent(), allocKind, newKind); @@ -1581,7 +1741,7 @@ return res; } -PlainObject * +JSObject * js::CreateThisForFunction(JSContext *cx, HandleObject callee, NewObjectKind newKind) { RootedValue protov(cx); @@ -1592,10 +1752,10 @@ proto = &protov.toObject(); else proto = nullptr; - PlainObject *obj = CreateThisForFunctionWithProto(cx, callee, proto, newKind); + JSObject *obj = CreateThisForFunctionWithProto(cx, callee, proto, newKind); if (obj && newKind == SingletonObject) { - RootedPlainObject nobj(cx, obj); + RootedPlainObject nobj(cx, &obj->as()); /* Reshape the singleton before passing it as the 'this' value. */ NativeObject::clear(cx, nobj); @@ -1620,34 +1780,22 @@ } if (obj->is()) return Proxy::set(cx, obj, receiver, id, strict, vp); - return obj->getOps()->setGeneric(cx, obj, id, vp, strict); + return obj->getOps()->setProperty(cx, obj, id, vp, strict); } /* static */ bool JSObject::nonNativeSetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index, MutableHandleValue vp, bool strict) { - if (MOZ_UNLIKELY(obj->watched())) { - RootedId id(cx); - if (!IndexToId(cx, index, &id)) - return false; - - WatchpointMap *wpmap = cx->compartment()->watchpointMap; - if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp)) - return false; - } - if (obj->is()) { - RootedId id(cx); - return IndexToId(cx, index, &id) && - Proxy::set(cx, obj, receiver, id, strict, vp); - } - return obj->getOps()->setElement(cx, obj, index, vp, strict); + RootedId id(cx); + if (!IndexToId(cx, index, &id)) + return false; + return nonNativeSetProperty(cx, obj, receiver, id, vp, strict); } JS_FRIEND_API(bool) JS_CopyPropertyFrom(JSContext *cx, HandleId id, HandleObject target, - HandleObject obj, - PropertyCopyBehavior copyBehavior) + HandleObject obj, PropertyCopyBehavior copyBehavior) { // |obj| and |cx| are generally not same-compartment with |target| here. assertSameCompartment(cx, obj, id); @@ -1764,7 +1912,7 @@ js::DeepCloneObjectLiteral(JSContext *cx, HandleNativeObject obj, NewObjectKind newKind) { /* NB: Keep this in sync with XDRObjectLiteral. */ - MOZ_ASSERT_IF(obj->hasSingletonType(), + MOZ_ASSERT_IF(obj->isSingleton(), JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates()); MOZ_ASSERT(obj->is() || obj->is()); @@ -1781,12 +1929,12 @@ // Object literals are tenured by default as holded by the JSScript. MOZ_ASSERT(obj->isTenured()); AllocKind kind = obj->asTenured().getAllocKind(); - Rooted typeObj(cx, obj->getType(cx)); - if (!typeObj) + RootedObjectGroup group(cx, obj->getGroup(cx)); + if (!group) return nullptr; RootedObject parent(cx, obj->getParent()); clone = NewNativeObjectWithGivenProto(cx, &PlainObject::class_, - TaggedProto(typeObj->proto().toObject()), + TaggedProto(group->proto().toObject()), parent, kind, newKind); } @@ -1828,13 +1976,13 @@ clone->setSlot(i, v); } - if (obj->hasSingletonType()) { - if (!JSObject::setSingletonType(cx, clone)) + if (obj->isSingleton()) { + if (!JSObject::setSingleton(cx, clone)) return nullptr; } else if (obj->is()) { - FixArrayType(cx, &clone->as()); + FixArrayGroup(cx, &clone->as()); } else { - FixObjectType(cx, &clone->as()); + FixObjectGroup(cx, &clone->as()); } if (obj->is() && obj->denseElementsAreCopyOnWrite()) { @@ -1852,7 +2000,7 @@ /* NB: Keep this in sync with DeepCloneObjectLiteral. */ JSContext *cx = xdr->cx(); - MOZ_ASSERT_IF(mode == XDR_ENCODE && obj->hasSingletonType(), + MOZ_ASSERT_IF(mode == XDR_ENCODE && obj->isSingleton(), JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates()); // Distinguish between objects and array classes. @@ -2033,18 +2181,18 @@ // Fix up types, distinguishing singleton-typed objects. uint32_t isSingletonTyped; if (mode == XDR_ENCODE) - isSingletonTyped = obj->hasSingletonType() ? 1 : 0; + isSingletonTyped = obj->isSingleton() ? 1 : 0; if (!xdr->codeUint32(&isSingletonTyped)) return false; if (mode == XDR_DECODE) { if (isSingletonTyped) { - if (!JSObject::setSingletonType(cx, obj)) + if (!JSObject::setSingleton(cx, obj)) return false; } else if (isArray) { - FixArrayType(cx, &obj->as()); + FixArrayGroup(cx, &obj->as()); } else { - FixObjectType(cx, &obj->as()); + FixObjectGroup(cx, &obj->as()); } } @@ -2095,12 +2243,12 @@ JSObject *proto = cx->global()->getOrCreateObjectPrototype(cx); if (!proto) return nullptr; - Rooted typeObj(cx, cx->getNewType(&PlainObject::class_, TaggedProto(proto))); - if (!typeObj) + RootedObjectGroup group(cx, cx->getNewGroup(&PlainObject::class_, TaggedProto(proto))); + if (!group) return nullptr; RootedShape shape(cx, srcObj->lastProperty()); - return NewReshapedObject(cx, typeObj, parent, kind, shape); + return NewReshapedObject(cx, group, parent, kind, shape); } RootedArrayObject srcArray(cx, &srcObj->as()); @@ -2189,9 +2337,9 @@ AutoCompartment ac(cx, a); - if (!a->getType(cx)) + if (!a->getGroup(cx)) CrashAtUnhandlableOOM("JSObject::swap"); - if (!b->getType(cx)) + if (!b->getGroup(cx)) CrashAtUnhandlableOOM("JSObject::swap"); /* @@ -2281,8 +2429,8 @@ // Swapping the contents of two objects invalidates type sets which contain // either of the objects, so mark all such sets as unknown. - MarkTypeObjectUnknownProperties(cx, a->type()); - MarkTypeObjectUnknownProperties(cx, b->type()); + MarkObjectGroupUnknownProperties(cx, a->group()); + MarkObjectGroupUnknownProperties(cx, b->group()); /* * We need a write barrier here. If |a| was marked and |b| was not, then @@ -2544,56 +2692,64 @@ js::SetClassAndProto(JSContext *cx, HandleObject obj, const Class *clasp, Handle proto) { - /* - * Regenerate shapes for all of the scopes along the old prototype chain, - * in case any entries were filled by looking up through obj. Stop when a - * non-native object is found, prototype lookups will not be cached across - * these. - * - * How this shape change is done is very delicate; the change can be made - * either by marking the object's prototype as uncacheable (such that the - * property cache and JIT'ed ICs cannot assume the shape determines the - * prototype) or by just generating a new shape for the object. Choosing - * the former is bad if the object is on the prototype chain of other - * objects, as the uncacheable prototype can inhibit iterator caches on - * those objects and slow down prototype accesses. Choosing the latter is - * bad if there are many similar objects to this one which will have their - * prototype mutated, as the generateOwnShape forces the object into - * dictionary mode and similar property lineages will be repeatedly cloned. - * - * :XXX: bug 707717 make this code less brittle. - */ + // Regenerate the object's shape. If the object is a proto (isDelegate()), + // we also need to regenerate shapes for all of the objects along the old + // prototype chain, in case any entries were filled by looking up through + // obj. Stop when a non-native object is found, prototype lookups will not + // be cached across these. + // + // How this shape change is done is very delicate; the change can be made + // either by marking the object's prototype as uncacheable (such that the + // JIT'ed ICs cannot assume the shape determines the prototype) or by just + // generating a new shape for the object. Choosing the former is bad if the + // object is on the prototype chain of other objects, as the uncacheable + // prototype can inhibit iterator caches on those objects and slow down + // prototype accesses. Choosing the latter is bad if there are many similar + // objects to this one which will have their prototype mutated, as the + // generateOwnShape forces the object into dictionary mode and similar + // property lineages will be repeatedly cloned. + // + // :XXX: bug 707717 make this code less brittle. RootedObject oldproto(cx, obj); while (oldproto && oldproto->isNative()) { - if (oldproto->hasSingletonType()) { + if (oldproto->isSingleton()) { if (!oldproto->as().generateOwnShape(cx)) return false; } else { if (!oldproto->setUncacheableProto(cx)) return false; } + if (!obj->isDelegate()) { + // If |obj| is not a proto of another object, we don't need to + // reshape the whole proto chain. + MOZ_ASSERT(obj == oldproto); + break; + } oldproto = oldproto->getProto(); } - if (obj->hasSingletonType()) { + if (proto.isObject() && !proto.toObject()->setDelegate(cx)) + return false; + + if (obj->isSingleton()) { /* * Just splice the prototype, but mark the properties as unknown for * consistent behavior. */ if (!obj->splicePrototype(cx, clasp, proto)) return false; - MarkTypeObjectUnknownProperties(cx, obj->type()); + MarkObjectGroupUnknownProperties(cx, obj->group()); return true; } if (proto.isObject()) { RootedObject protoObj(cx, proto.toObject()); - if (!JSObject::setNewTypeUnknown(cx, clasp, protoObj)) + if (!JSObject::setNewGroupUnknown(cx, clasp, protoObj)) return false; } - TypeObject *type = cx->getNewType(clasp, proto); - if (!type) + ObjectGroup *group = cx->getNewGroup(clasp, proto); + if (!group) return false; /* @@ -2603,10 +2759,10 @@ * type but not the new type of the object, so we need to treat all such * type sets as unknown. */ - MarkTypeObjectUnknownProperties(cx, obj->type()); - MarkTypeObjectUnknownProperties(cx, type); + MarkObjectGroupUnknownProperties(cx, obj->group()); + MarkObjectGroupUnknownProperties(cx, group); - obj->setType(type); + obj->setGroup(group); return true; } @@ -2704,35 +2860,6 @@ } bool -js::FindClassObject(ExclusiveContext *cx, MutableHandleObject protop, const Class *clasp) -{ - JSProtoKey protoKey = ClassProtoKeyOrAnonymousOrNull(clasp); - if (protoKey != JSProto_Null) { - MOZ_ASSERT(JSProto_Null < protoKey); - MOZ_ASSERT(protoKey < JSProto_LIMIT); - return GetBuiltinConstructor(cx, protoKey, protop); - } - - JSAtom *atom = Atomize(cx, clasp->name, strlen(clasp->name)); - if (!atom) - return false; - RootedId id(cx, AtomToId(atom)); - - RootedObject pobj(cx); - RootedShape shape(cx); - if (!NativeLookupProperty(cx, cx->global(), id, &pobj, &shape)) - return false; - RootedValue v(cx); - if (shape && pobj->isNative()) { - if (shape->hasSlot()) - v = pobj->as().getSlot(shape->slot()); - } - if (v.isObject()) - protop.set(&v.toObject()); - return true; -} - -bool JSObject::isCallable() const { if (is()) @@ -2786,11 +2913,11 @@ js::LookupProperty(JSContext *cx, HandleObject obj, js::HandleId id, MutableHandleObject objp, MutableHandleShape propp) { - /* NB: The logic of lookupGeneric is implicitly reflected in + /* NB: The logic of lookupProperty is implicitly reflected in * BaselineIC.cpp's |EffectlesslyLookupProperty| logic. * If this changes, please remember to update the logic there as well. */ - if (LookupGenericOp op = obj->getOps()->lookupGeneric) + if (LookupPropertyOp op = obj->getOps()->lookupProperty) return op(cx, obj, id, objp, propp); return NativeLookupProperty(cx, obj.as(), id, objp, propp); } @@ -2825,7 +2952,7 @@ MOZ_ASSERT(!*objp && !*pobjp && !*propp); for (JSObject *scope = scopeChain; scope; scope = scope->enclosingScope()) { - if (scope->getOps()->lookupGeneric) + if (scope->getOps()->lookupProperty) return false; if (!LookupPropertyInline(cx, &scope->as(), NameToId(name), pobjp, propp)) return false; @@ -2908,93 +3035,73 @@ return true; } -static MOZ_ALWAYS_INLINE bool -LookupPropertyPureInline(ExclusiveContext *cx, JSObject *obj, jsid id, NativeObject **objp, - Shape **propp) +bool +js::LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, JSObject **objp, + Shape **propp) { - if (!obj->isNative()) - return false; - - NativeObject *current = &obj->as(); - while (true) { - /* Search for a native dense element, typed array element, or property. */ + do { + if (obj->isNative()) { + /* Search for a native dense element, typed array element, or property. */ - if (JSID_IS_INT(id) && current->containsDenseElement(JSID_TO_INT(id))) { - *objp = current; - MarkDenseOrTypedArrayElementFound(propp); - return true; - } + if (JSID_IS_INT(id) && obj->as().containsDenseElement(JSID_TO_INT(id))) { + *objp = obj; + MarkDenseOrTypedArrayElementFound(propp); + return true; + } - if (IsAnyTypedArray(current)) { - uint64_t index; - if (IsTypedArrayIndex(id, &index)) { - if (index < AnyTypedArrayLength(obj)) { - *objp = current; - MarkDenseOrTypedArrayElementFound(propp); - } else { - *objp = nullptr; - *propp = nullptr; + if (IsAnyTypedArray(obj)) { + uint64_t index; + if (IsTypedArrayIndex(id, &index)) { + if (index < AnyTypedArrayLength(obj)) { + *objp = obj; + MarkDenseOrTypedArrayElementFound(propp); + } else { + *objp = nullptr; + *propp = nullptr; + } + return true; } - return true; } - } - if (Shape *shape = current->lookupPure(id)) { - *objp = current; - *propp = shape; - return true; - } + if (Shape *shape = obj->as().lookupPure(id)) { + *objp = obj; + *propp = shape; + return true; + } - // Fail if there's a resolve hook. We allow the JSFunction resolve hook - // if we know it will never add a property with this name or str_resolve - // with a non-integer property. - do { - const Class *clasp = current->getClass(); - if (!clasp->resolve) - break; - if (clasp->resolve == fun_resolve && !FunctionHasResolveHook(cx->names(), id)) + // Fail if there's a resolve hook. We allow the JSFunction resolve hook + // if we know it will never add a property with this name or str_resolve + // with a non-integer property. + do { + const Class *clasp = obj->getClass(); + if (!clasp->resolve) break; - if (clasp->resolve == str_resolve && !JSID_IS_INT(id)) - break; - return false; - } while (0); - - JSObject *proto = current->getProto(); - - if (!proto) - break; - if (!proto->isNative()) - return false; + if (clasp->resolve == fun_resolve && !FunctionHasResolveHook(cx->names(), id)) + break; + if (clasp->resolve == str_resolve && !JSID_IS_INT(id)) + break; + return false; + } while (0); + } else { + // Search for a property on an unboxed object. Other non-native objects + // are not handled here. + if (!obj->is()) + return false; + if (obj->as().layout().lookup(id)) { + *objp = obj; + MarkNonNativePropertyFound(propp); + return true; + } + } - current = &proto->as(); - } + obj = obj->getProto(); + } while (obj); *objp = nullptr; *propp = nullptr; return true; } -static MOZ_ALWAYS_INLINE bool -NativeGetPureInline(NativeObject *pobj, Shape *shape, Value *vp) -{ - if (shape->hasSlot()) { - *vp = pobj->getSlot(shape->slot()); - MOZ_ASSERT(!vp->isMagic()); - } else { - vp->setUndefined(); - } - - /* Fail if we have a custom getter. */ - return shape->hasDefaultGetter(); -} - -bool -js::LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, NativeObject **objp, - Shape **propp) -{ - return LookupPropertyPureInline(cx, obj, id, objp, propp); -} - bool JSObject::reportReadOnly(JSContext *cx, jsid id, unsigned report) { @@ -3182,7 +3289,7 @@ MOZ_ASSERT(setter != JS_StrictPropertyStub); MOZ_ASSERT(!(attrs & JSPROP_PROPOP_ACCESSORS)); - DefineGenericOp op = obj->getOps()->defineGeneric; + DefinePropertyOp op = obj->getOps()->defineProperty; if (op) { if (!cx->shouldBeJSContext()) return false; @@ -3207,13 +3314,10 @@ MOZ_ASSERT(getter != JS_PropertyStub); MOZ_ASSERT(setter != JS_StrictPropertyStub); - DefineElementOp op = obj->getOps()->defineElement; - if (op) { - if (!cx->shouldBeJSContext()) - return false; - return op(cx->asJSContext(), obj, index, value, getter, setter, attrs); - } - return NativeDefineElement(cx, obj.as(), index, value, getter, setter, attrs); + RootedId id(cx); + if (!IndexToId(cx, index, &id)) + return false; + return DefineProperty(cx, obj, id, value, getter, setter, attrs); } @@ -3495,39 +3599,6 @@ return nullptr; } -/* - * The first part of this function has been hand-expanded and optimized into - * NewBuiltinClassInstance in jsobjinlines.h. - */ -bool -js::FindClassPrototype(ExclusiveContext *cx, MutableHandleObject protop, const Class *clasp) -{ - protop.set(nullptr); - JSProtoKey protoKey = ClassProtoKeyOrAnonymousOrNull(clasp); - if (protoKey != JSProto_Null) - return GetBuiltinPrototype(cx, protoKey, protop); - - RootedObject ctor(cx); - if (!FindClassObject(cx, &ctor, clasp)) - return false; - - if (ctor && ctor->is()) { - JSFunction *nctor = &ctor->as(); - RootedValue v(cx); - if (cx->isJSContext()) { - if (!GetProperty(cx->asJSContext(), ctor, ctor, cx->names().prototype, &v)) - return false; - } else { - Shape *shape = nctor->lookup(cx, cx->names().prototype); - if (!shape || !NativeGetPureInline(nctor, shape, v.address())) - return false; - } - if (v.isObject()) - protop.set(&v.toObject()); - } - return true; -} - JSObject * js::PrimitiveToObject(JSContext *cx, const Value &v) { @@ -3551,7 +3622,7 @@ * Callers must handle the already-object case. */ JSObject * -js::ToObjectSlow(JSContext *cx, HandleValue val, bool reportScanStack) +js::ToObjectSlow(JSContext *cx, JS::HandleValue val, bool reportScanStack) { MOZ_ASSERT(!val.isMagic()); MOZ_ASSERT(!val.isObject()); @@ -3793,9 +3864,10 @@ if (obj->isUnqualifiedVarObj()) fprintf(stderr, " unqualified_varobj"); if (obj->watched()) fprintf(stderr, " watched"); if (obj->isIteratedSingleton()) fprintf(stderr, " iterated_singleton"); - if (obj->isNewTypeUnknown()) fprintf(stderr, " new_type_unknown"); + if (obj->isNewGroupUnknown()) fprintf(stderr, " new_type_unknown"); if (obj->hasUncacheableProto()) fprintf(stderr, " has_uncacheable_proto"); if (obj->hadElementsAccess()) fprintf(stderr, " had_elements_access"); + if (obj->wasNewScriptCleared()) fprintf(stderr, " new_script_cleared"); if (obj->isNative()) { NativeObject *nobj = &obj->as(); @@ -4044,7 +4116,7 @@ if (resolve && resolve != js::fun_resolve && resolve != js::str_resolve) return false; - if (obj->getOps()->lookupProperty || obj->getOps()->lookupGeneric || obj->getOps()->lookupElement) + if (obj->getOps()->lookupProperty) return false; obj = obj->getProto(); @@ -4056,11 +4128,11 @@ void JSObject::markChildren(JSTracer *trc) { - MarkTypeObject(trc, &type_, "type"); + MarkObjectGroup(trc, &group_, "group"); MarkShape(trc, &shape_, "shape"); - const Class *clasp = type_->clasp(); + const Class *clasp = group_->clasp(); if (clasp->trace) clasp->trace(trc, this); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsobj.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsobj.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsobj.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsobj.h 2015-02-03 14:33:33.000000000 +0000 @@ -20,6 +20,7 @@ #include "gc/Barrier.h" #include "gc/Marking.h" +#include "js/Conversions.h" #include "js/GCAPI.h" #include "js/HeapAPI.h" #include "vm/Shape.h" @@ -94,7 +95,7 @@ * - The |shape_| member stores the shape of the object, which includes the * object's class and the layout of all its properties. * - * - The |type_| member stores the type of the object, which contains its + * - The |group_| member stores the group of the object, which contains its * prototype object and the possible types of its properties. * * Subclasses of JSObject --- mainly NativeObject and JSFunction --- add more @@ -103,18 +104,8 @@ class JSObject : public js::gc::Cell { protected: - /* - * Shape of the object, encodes the layout of the object's properties and - * all other information about its structure. See vm/Shape.h. - */ js::HeapPtrShape shape_; - - /* - * The object's type and prototype. For objects with the LAZY_TYPE flag - * set, this is the prototype's default 'new' type and can only be used - * to get that prototype. - */ - js::HeapPtrTypeObject type_; + js::HeapPtrObjectGroup group_; private: friend class js::Shape; @@ -126,8 +117,8 @@ friend bool js::SetImmutablePrototype(js::ExclusiveContext *cx, JS::HandleObject obj, bool *succeeded); - /* Make the type object to use for LAZY_TYPE objects. */ - static js::types::TypeObject *makeLazyType(JSContext *cx, js::HandleObject obj); + // Make a new group to use for a singleton object. + static js::types::ObjectGroup *makeLazyGroup(JSContext *cx, js::HandleObject obj); public: js::Shape * lastProperty() const { @@ -140,7 +131,7 @@ } const js::Class *getClass() const { - return type_->clasp(); + return group_->clasp(); } const JSClass *getJSClass() const { return Jsvalify(getClass()); @@ -152,29 +143,29 @@ return &getClass()->ops; } - js::types::TypeObject *type() const { - MOZ_ASSERT(!hasLazyType()); - return typeRaw(); + js::types::ObjectGroup *group() const { + MOZ_ASSERT(!hasLazyGroup()); + return groupRaw(); } - js::types::TypeObject *typeRaw() const { - return type_; + js::types::ObjectGroup *groupRaw() const { + return group_; } /* - * Whether this is the only object which has its specified type. This - * object will have its type constructed lazily as needed by analysis. + * Whether this is the only object which has its specified gbroup. This + * object will have its group constructed lazily as needed by analysis. */ - bool hasSingletonType() const { - return !!type_->singleton(); + bool isSingleton() const { + return !!group_->singleton(); } /* - * Whether the object's type has not been constructed yet. If an object - * might have a lazy type, use getType() below, otherwise type(). + * Whether the object's group has not been constructed yet. If an object + * might have a lazy group, use getGroup() below, otherwise group(). */ - bool hasLazyType() const { - return type_->lazy(); + bool hasLazyGroup() const { + return group_->lazy(); } JSCompartment *compartment() const { @@ -189,7 +180,7 @@ js::gc::AllocKind kind, js::gc::InitialHeap heap, js::HandleShape shape, - js::HandleTypeObject type); + js::HandleObjectGroup group); // Set the initial slots and elements of an object. These pointers are only // valid for native objects, but during initialization are set for all @@ -327,24 +318,24 @@ bool hasIdempotentProtoChain() const; /* - * Marks this object as having a singleton type, and leave the type lazy. + * Marks this object as having a singleton type, and leave the group lazy. * Constructs a new, unique shape for the object. */ - static inline bool setSingletonType(js::ExclusiveContext *cx, js::HandleObject obj); + static inline bool setSingleton(js::ExclusiveContext *cx, js::HandleObject obj); - // uninlinedGetType() is the same as getType(), but not inlined. - inline js::types::TypeObject* getType(JSContext *cx); - js::types::TypeObject* uninlinedGetType(JSContext *cx); + // uninlinedGetGroup() is the same as getGroup(), but not inlined. + inline js::types::ObjectGroup* getGroup(JSContext *cx); + js::types::ObjectGroup* uninlinedGetGroup(JSContext *cx); - const js::HeapPtrTypeObject &typeFromGC() const { + const js::HeapPtrObjectGroup &groupFromGC() const { /* Direct field access for use by GC. */ - return type_; + return group_; } /* * We allow the prototype of an object to be lazily computed if the object * is a proxy. In the lazy case, we store (JSObject *)0x1 in the proto field - * of the object's TypeObject. We offer three ways of getting the prototype: + * of the object's group. We offer three ways of getting the prototype: * * 1. obj->getProto() returns the prototype, but asserts if obj is a proxy. * 2. obj->getTaggedProto() returns a TaggedProto, which can be tested to @@ -355,7 +346,7 @@ */ js::TaggedProto getTaggedProto() const { - return type_->proto(); + return group_->proto(); } bool hasTenuredProto() const; @@ -364,7 +355,9 @@ JSObject *getProto() const { MOZ_ASSERT(!uninlinedIsProxy()); - return getTaggedProto().toObjectOrNull(); + JSObject *proto = getTaggedProto().toObjectOrNull(); + MOZ_ASSERT_IF(proto && proto->isNative(), proto->isDelegate()); + return proto; } // Normal objects and a subset of proxies have uninteresting [[Prototype]]. @@ -397,12 +390,12 @@ return lastProperty()->hasObjectFlag(js::BaseShape::IMMUTABLE_PROTOTYPE); } - // uninlinedSetType() is the same as setType(), but not inlined. - inline void setType(js::types::TypeObject *newType); - void uninlinedSetType(js::types::TypeObject *newType); + // uninlinedSetGroup() is the same as setGroup(), but not inlined. + inline void setGroup(js::types::ObjectGroup *group); + void uninlinedSetGroup(js::types::ObjectGroup *group); #ifdef DEBUG - bool hasNewType(const js::Class *clasp, js::types::TypeObject *newType); + bool hasNewGroup(const js::Class *clasp, js::types::ObjectGroup *group); #endif /* @@ -421,10 +414,18 @@ * Mark an object as requiring its default 'new' type to have unknown * properties. */ - bool isNewTypeUnknown() const { - return lastProperty()->hasObjectFlag(js::BaseShape::NEW_TYPE_UNKNOWN); + bool isNewGroupUnknown() const { + return lastProperty()->hasObjectFlag(js::BaseShape::NEW_GROUP_UNKNOWN); + } + static bool setNewGroupUnknown(JSContext *cx, const js::Class *clasp, JS::HandleObject obj); + + // Mark an object as having its 'new' script information cleared. + bool wasNewScriptCleared() const { + return lastProperty()->hasObjectFlag(js::BaseShape::NEW_SCRIPT_CLEARED); + } + bool setNewScriptCleared(js::ExclusiveContext *cx) { + return setFlag(cx, js::BaseShape::NEW_SCRIPT_CLEARED); } - static bool setNewTypeUnknown(JSContext *cx, const js::Class *clasp, JS::HandleObject obj); /* Set a new prototype for an object with a singleton type. */ bool splicePrototype(JSContext *cx, const js::Class *clasp, js::Handle proto); @@ -483,10 +484,6 @@ inline js::GlobalObject &global() const; inline bool isOwnGlobal() const; - /* Remove the type (and prototype) or parent from a new object. */ - static inline bool clearType(JSContext *cx, js::HandleObject obj); - static bool clearParent(JSContext *cx, js::HandleObject obj); - /* * ES5 meta-object properties and operations. */ @@ -594,10 +591,10 @@ /* JIT Accessors */ static size_t offsetOfShape() { return offsetof(JSObject, shape_); } - js::HeapPtrShape *addressOfShape() { return &shape_; } + static size_t offsetOfGroup() { return offsetof(JSObject, group_); } - static size_t offsetOfType() { return offsetof(JSObject, type_); } - js::HeapPtrTypeObject *addressOfType() { return &type_; } + // Maximum size in bytes of a JSObject. + static const size_t MAX_BYTE_SIZE = 4 * sizeof(void *) + 16 * sizeof(JS::Value); private: JSObject() = delete; @@ -1080,18 +1077,6 @@ SetClassAndProto(JSContext *cx, HandleObject obj, const Class *clasp, Handle proto); -/* - * Property-lookup-based access to interface and prototype objects for classes. - * If the class is built-in (hhas a non-null JSProtoKey), these forward to - * GetClass{Object,Prototype}. - */ - -bool -FindClassObject(ExclusiveContext *cx, MutableHandleObject protop, const Class *clasp); - -extern bool -FindClassPrototype(ExclusiveContext *cx, MutableHandleObject protop, const Class *clasp); - } /* namespace js */ /* @@ -1154,12 +1139,12 @@ // Specialized call for constructing |this| with a known function callee, // and a known prototype. -extern PlainObject * +extern JSObject * CreateThisForFunctionWithProto(JSContext *cx, js::HandleObject callee, JSObject *proto, NewObjectKind newKind = GenericObject); // Specialized call for constructing |this| with a known function callee. -extern PlainObject * +extern JSObject * CreateThisForFunction(JSContext *cx, js::HandleObject callee, NewObjectKind newKind); // Generic call for constructing |this|. @@ -1224,7 +1209,7 @@ namespace js { bool -LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, NativeObject **objp, +LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, JSObject **objp, Shape **propp); bool diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsobjinlines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsobjinlines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsobjinlines.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsobjinlines.h 2015-02-03 14:33:33.000000000 +0000 @@ -71,51 +71,38 @@ } /* static */ inline bool -JSObject::setSingletonType(js::ExclusiveContext *cx, js::HandleObject obj) +JSObject::setSingleton(js::ExclusiveContext *cx, js::HandleObject obj) { MOZ_ASSERT_IF(cx->isJSContext(), !IsInsideNursery(obj)); - js::types::TypeObject *type = cx->getSingletonType(obj->getClass(), obj->getTaggedProto()); - if (!type) + js::types::ObjectGroup *group = cx->getLazySingletonGroup(obj->getClass(), + obj->getTaggedProto()); + if (!group) return false; - obj->type_ = type; + obj->group_ = group; return true; } -inline js::types::TypeObject* -JSObject::getType(JSContext *cx) +inline js::types::ObjectGroup* +JSObject::getGroup(JSContext *cx) { MOZ_ASSERT(cx->compartment() == compartment()); - if (hasLazyType()) { + if (hasLazyGroup()) { JS::RootedObject self(cx, this); if (cx->compartment() != compartment()) MOZ_CRASH(); - return makeLazyType(cx, self); + return makeLazyGroup(cx, self); } - return static_cast(type_); -} - -/* static */ inline bool -JSObject::clearType(JSContext *cx, js::HandleObject obj) -{ - MOZ_ASSERT(!obj->hasSingletonType()); - MOZ_ASSERT(cx->compartment() == obj->compartment()); - - js::types::TypeObject *type = cx->getNewType(obj->getClass(), js::TaggedProto(nullptr)); - if (!type) - return false; - - obj->type_ = type; - return true; + return group_; } inline void -JSObject::setType(js::types::TypeObject *newType) +JSObject::setGroup(js::types::ObjectGroup *group) { - MOZ_ASSERT(newType); - MOZ_ASSERT(!hasSingletonType()); - type_ = newType; + MOZ_ASSERT(group); + MOZ_ASSERT(!isSingleton()); + group_ = group; } @@ -170,9 +157,6 @@ js::GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index, MutableHandleValue vp) { - if (ElementIdOp op = obj->getOps()->getElement) - return op(cx, obj, receiver, index, vp); - RootedId id(cx); if (!IndexToId(cx, index, &id)) return false; @@ -182,7 +166,7 @@ inline bool js::GetElementNoGC(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp) { - if (obj->getOps()->getElement) + if (obj->getOps()->getProperty) return false; if (index > JSID_INT_MAX) @@ -194,7 +178,7 @@ js::DeleteProperty(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded) { types::MarkTypePropertyNonData(cx, obj, id); - if (DeleteGenericOp op = obj->getOps()->deleteGeneric) + if (DeletePropertyOp op = obj->getOps()->deleteProperty) return op(cx, obj, id, succeeded); return NativeDeleteProperty(cx, obj.as(), id, succeeded); } @@ -215,7 +199,7 @@ js::SetPropertyAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp) { types::MarkTypePropertyNonData(cx, obj, id); - GenericAttributesOp op = obj->getOps()->setGenericAttributes; + SetAttributesOp op = obj->getOps()->setAttributes; if (op) return op(cx, obj, id, attrsp); return NativeSetPropertyAttributes(cx, obj.as(), id, attrsp); @@ -256,26 +240,28 @@ /* static */ inline JSObject * JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap, - js::HandleShape shape, js::HandleTypeObject type) + js::HandleShape shape, js::HandleObjectGroup group) { - MOZ_ASSERT(shape && type); - MOZ_ASSERT(type->clasp() == shape->getObjectClass()); - MOZ_ASSERT(type->clasp() != &js::ArrayObject::class_); - MOZ_ASSERT_IF(!js::ClassCanHaveFixedData(type->clasp()), - js::gc::GetGCKindSlots(kind, type->clasp()) == shape->numFixedSlots()); - MOZ_ASSERT_IF(type->clasp()->flags & JSCLASS_BACKGROUND_FINALIZE, IsBackgroundFinalized(kind)); - MOZ_ASSERT_IF(type->clasp()->finalize, heap == js::gc::TenuredHeap || - (type->clasp()->flags & JSCLASS_FINALIZE_FROM_NURSERY)); + MOZ_ASSERT(shape && group); + MOZ_ASSERT(group->clasp() == shape->getObjectClass()); + MOZ_ASSERT(group->clasp() != &js::ArrayObject::class_); + MOZ_ASSERT_IF(!js::ClassCanHaveFixedData(group->clasp()), + js::gc::GetGCKindSlots(kind, group->clasp()) == shape->numFixedSlots()); + MOZ_ASSERT_IF(group->clasp()->flags & JSCLASS_BACKGROUND_FINALIZE, + IsBackgroundFinalized(kind)); + MOZ_ASSERT_IF(group->clasp()->finalize, + heap == js::gc::TenuredHeap || + (group->clasp()->flags & JSCLASS_FINALIZE_FROM_NURSERY)); // Non-native classes cannot have reserved slots or private data, and the // objects can't have any fixed slots, for compatibility with // GetReservedOrProxyPrivateSlot. - MOZ_ASSERT_IF(!type->clasp()->isNative(), JSCLASS_RESERVED_SLOTS(type->clasp()) == 0); - MOZ_ASSERT_IF(!type->clasp()->isNative(), !type->clasp()->hasPrivate()); - MOZ_ASSERT_IF(!type->clasp()->isNative(), shape->numFixedSlots() == 0); - MOZ_ASSERT_IF(!type->clasp()->isNative(), shape->slotSpan() == 0); + MOZ_ASSERT_IF(!group->clasp()->isNative(), JSCLASS_RESERVED_SLOTS(group->clasp()) == 0); + MOZ_ASSERT_IF(!group->clasp()->isNative(), !group->clasp()->hasPrivate()); + MOZ_ASSERT_IF(!group->clasp()->isNative(), shape->numFixedSlots() == 0); + MOZ_ASSERT_IF(!group->clasp()->isNative(), shape->slotSpan() == 0); - const js::Class *clasp = type->clasp(); + const js::Class *clasp = group->clasp(); size_t nDynamicSlots = js::NativeObject::dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), clasp); @@ -284,7 +270,7 @@ return nullptr; obj->shape_.init(shape); - obj->type_.init(type); + obj->group_.init(group); // Note: slots are created and assigned internally by NewGCObject. obj->setInitialElementsMaybeNonNative(js::emptyObjectElements); @@ -296,7 +282,7 @@ obj->as().initializeSlotRange(0, span); // JSFunction's fixed slots expect POD-style initialization. - if (type->clasp()->isJSFunction()) + if (group->clasp()->isJSFunction()) memset(obj->as().fixedSlots(), 0, sizeof(js::HeapSlot) * GetGCKindSlots(kind)); js::gc::TraceCreateObject(obj); @@ -550,27 +536,11 @@ NewObjectWithGivenProto(ExclusiveContext *cx, JSObject *proto, JSObject *parent, gc::AllocKind allocKind, NewObjectKind newKind = GenericObject) { - JSObject *obj = NewObjectWithGivenProto(cx, &T::class_, TaggedProto(proto), parent, newKind); + JSObject *obj = NewObjectWithGivenProto(cx, &T::class_, TaggedProto(proto), parent, allocKind, + newKind); return obj ? &obj->as() : nullptr; } -inline bool -FindProto(ExclusiveContext *cx, const js::Class *clasp, MutableHandleObject proto) -{ - if (!FindClassPrototype(cx, proto, clasp)) - return false; - - if (!proto) { - // We're looking for the prototype of a class that is currently being - // resolved; the global object's resolve hook is on the - // stack. js::FindClassPrototype detects this goofy case and returns - // true with proto null. Fall back on Object.prototype. - MOZ_ASSERT(JSCLASS_CACHED_PROTO_KEY(clasp) == JSProto_Null); - return GetBuiltinPrototype(cx, JSProto_Object, proto); - } - return true; -} - /* * Make an object with the prototype set according to the specified prototype or class: * @@ -664,29 +634,29 @@ NewObjectScriptedCall(JSContext *cx, MutableHandleObject obj); JSObject * -NewObjectWithTypeCommon(JSContext *cx, HandleTypeObject type, JSObject *parent, - gc::AllocKind allocKind, NewObjectKind newKind); +NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, JSObject *parent, + gc::AllocKind allocKind, NewObjectKind newKind); template inline T * -NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, - gc::AllocKind allocKind, NewObjectKind newKind = GenericObject) +NewObjectWithGroup(JSContext *cx, HandleObjectGroup group, JSObject *parent, + gc::AllocKind allocKind, NewObjectKind newKind = GenericObject) { - JSObject *obj = NewObjectWithTypeCommon(cx, type, parent, allocKind, newKind); + JSObject *obj = NewObjectWithGroupCommon(cx, group, parent, allocKind, newKind); return obj ? &obj->as() : nullptr; } template inline T * -NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, - NewObjectKind newKind = GenericObject) +NewObjectWithGroup(JSContext *cx, HandleObjectGroup group, JSObject *parent, + NewObjectKind newKind = GenericObject) { - gc::AllocKind allocKind = gc::GetGCObjectKind(type->clasp()); - return NewObjectWithType(cx, type, parent, allocKind, newKind); + gc::AllocKind allocKind = gc::GetGCObjectKind(group->clasp()); + return NewObjectWithGroup(cx, group, parent, allocKind, newKind); } JSObject * -NewReshapedObject(JSContext *cx, HandleTypeObject type, JSObject *parent, +NewReshapedObject(JSContext *cx, HandleObjectGroup group, JSObject *parent, gc::AllocKind allocKind, HandleShape shape, NewObjectKind newKind = GenericObject); @@ -719,7 +689,10 @@ switch (classValue) { case ESClass_Object: return obj->is(); - case ESClass_Array: return obj->is(); + case ESClass_Array: + case ESClass_IsArray: + // There difference between those is only relevant for proxies. + return obj->is(); case ESClass_Number: return obj->is(); case ESClass_String: return obj->is(); case ESClass_Boolean: return obj->is(); @@ -742,6 +715,16 @@ return ObjectClassIs(obj, classValue, cx); } +// ES6 7.2.2 +inline bool +IsArray(HandleObject obj, JSContext *cx) +{ + if (obj->is()) + return true; + + return ObjectClassIs(obj, ESClass_IsArray, cx); +} + inline bool Unbox(JSContext *cx, HandleObject obj, MutableHandleValue vp) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/json.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/json.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/json.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/json.cpp 2015-02-03 14:33:33.000000000 +0000 @@ -322,7 +322,7 @@ Maybe ids; const AutoIdVector *props; if (scx->replacer && !scx->replacer->isCallable()) { - MOZ_ASSERT(JS_IsArrayObject(cx, scx->replacer)); + MOZ_ASSERT(IsArray(scx->replacer, cx)); props = &scx->propertyList; } else { MOZ_ASSERT_IF(scx->replacer, scx->propertyList.length() == 0); @@ -507,7 +507,7 @@ scx->depth++; bool ok; - if (ObjectClassIs(obj, ESClass_Array, cx)) + if (IsArray(obj, cx)) ok = JA(cx, obj, scx); else ok = JO(cx, obj, scx); @@ -529,7 +529,7 @@ if (replacer) { if (replacer->isCallable()) { /* Step 4a(i): use replacer to transform values. */ - } else if (ObjectClassIs(replacer, ESClass_Array, cx)) { + } else if (IsArray(replacer, cx)) { /* * Step 4b: The spec algorithm is unhelpfully vague about the exact * steps taken when the replacer is an array, regarding the exact @@ -560,7 +560,8 @@ /* Step 4b(ii). */ uint32_t len; - JS_ALWAYS_TRUE(GetLengthProperty(cx, replacer, &len)); + if (!GetLengthProperty(cx, replacer, &len)) + return false; if (replacer->is() && !replacer->isIndexed()) len = Min(len, replacer->as().getDenseInitializedLength()); @@ -693,7 +694,7 @@ if (val.isObject()) { RootedObject obj(cx, &val.toObject()); - if (ObjectClassIs(obj, ESClass_Array, cx)) { + if (IsArray(obj, cx)) { /* Step 2a(ii). */ uint32_t length; if (!GetLengthProperty(cx, obj, &length)) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsprototypes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsprototypes.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsprototypes.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsprototypes.h 2015-02-03 14:33:33.000000000 +0000 @@ -56,12 +56,6 @@ #define IF_SAB(real,imaginary) imaginary #endif -#ifdef JS_HAS_SYMBOLS -#define IF_SYMBOLS(real,imaginary) real -#else -#define IF_SYMBOLS(real,imaginary) imaginary -#endif - #define JS_FOR_PROTOTYPES(real,imaginary) \ imaginary(Null, 0, js_InitNullClass, dummy) \ real(Object, 1, js_InitViaClassSpec, OCLASP(Plain)) \ @@ -99,7 +93,7 @@ real(Map, 33, js_InitMapClass, OCLASP(Map)) \ real(Set, 34, js_InitSetClass, OCLASP(Set)) \ real(DataView, 35, js_InitDataViewClass, OCLASP(DataView)) \ -IF_SYMBOLS(real,imaginary)(Symbol, 36, js_InitSymbolClass, &js::SymbolObject::class_) \ + real(Symbol, 36, js_InitSymbolClass, OCLASP(Symbol)) \ IF_SAB(real,imaginary)(SharedArrayBuffer, 37, js_InitSharedArrayBufferClass, &js::SharedArrayBufferObject::protoClass) \ IF_INTL(real,imaginary) (Intl, 38, js_InitIntlClass, CLASP(Intl)) \ IF_BDATA(real,imaginary)(TypedObject, 39, js_InitTypedObjectModuleObject, OCLASP(TypedObjectModule)) \ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jspubtd.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jspubtd.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jspubtd.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jspubtd.h 2015-02-03 14:33:33.000000000 +0000 @@ -286,7 +286,7 @@ THING_ROOT_OBJECT, THING_ROOT_SHAPE, THING_ROOT_BASE_SHAPE, - THING_ROOT_TYPE_OBJECT, + THING_ROOT_OBJECT_GROUP, THING_ROOT_STRING, THING_ROOT_SYMBOL, THING_ROOT_JIT_CODE, @@ -454,7 +454,7 @@ } /* Limit pointer for checking native stack consumption. */ - uintptr_t nativeStackLimit[StackKindCount]; + uintptr_t nativeStackLimit[js::StackKindCount]; static const size_t RuntimeMainThreadOffset = offsetof(RuntimeDummy, mainThread); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsscript.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsscript.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsscript.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsscript.cpp 2015-02-03 14:33:33.000000000 +0000 @@ -1090,10 +1090,8 @@ scriptp.set(script); /* see BytecodeEmitter::tellDebuggerAboutCompiledScript */ - if (!fun) { - RootedGlobalObject global(cx, script->compileAndGo() ? &script->global() : nullptr); - Debugger::onNewScript(cx, script, global); - } + if (!fun) + Debugger::onNewScript(cx, script); } return true; @@ -3191,8 +3189,7 @@ cscript->setFunction(clone); script = clone->nonLazyScript(); - RootedGlobalObject global(cx, script->compileAndGo() ? &script->global() : nullptr); - Debugger::onNewScript(cx, script, global); + Debugger::onNewScript(cx, script); return true; } @@ -3740,6 +3737,12 @@ return sourceObject_ ? &sourceObject_->as() : nullptr; } +ScriptSource * +LazyScript::maybeForwardedScriptSource() const +{ + return UncheckedUnwrap(MaybeForwarded(sourceObject()))->as().source(); +} + /* static */ LazyScript * LazyScript::CreateRaw(ExclusiveContext *cx, HandleFunction fun, uint64_t packedFields, uint32_t begin, uint32_t end, @@ -3964,7 +3967,7 @@ if (!scriptChars) return false; - const char16_t *lazyChars = lazy->source()->chars(cx, holder); + const char16_t *lazyChars = lazy->scriptSource()->chars(cx, holder); if (!lazyChars) return false; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsscript.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsscript.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsscript.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsscript.h 2015-02-03 14:33:33.000000000 +0000 @@ -1955,6 +1955,7 @@ ScriptSource *scriptSource() const { return sourceObject()->source(); } + ScriptSource *maybeForwardedScriptSource() const; bool mutedErrors() const { return scriptSource()->mutedErrors(); } @@ -2052,8 +2053,8 @@ p_.treatAsRunOnce = true; } - ScriptSource *source() const { - return sourceObject()->source(); + const char *filename() const { + return scriptSource()->filename(); } uint32_t begin() const { return begin_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsstr.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsstr.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsstr.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsstr.cpp 2015-02-03 14:33:33.000000000 +0000 @@ -182,7 +182,7 @@ return false; ScopedJSFreePtr newChars; - uint32_t newLength; + uint32_t newLength = 0; // initialize to silence GCC warning if (str->hasLatin1Chars()) { AutoCheckCannotGC nogc; newChars = Escape(cx, str->latin1Chars(nogc), str->length(), &newLength); @@ -3553,7 +3553,7 @@ template static ArrayObject * SplitHelper(JSContext *cx, HandleLinearString str, uint32_t limit, const Matcher &splitMatch, - Handle type) + HandleObjectGroup group) { size_t strLength = str->length(); SplitMatchResult result; @@ -3657,7 +3657,7 @@ return nullptr; } else { /* Only string entries have been accounted for so far. */ - AddTypePropertyId(cx, type, JSID_VOID, UndefinedValue()); + AddTypePropertyId(cx, group, JSID_VOID, UndefinedValue()); if (!splits.append(UndefinedValue())) return nullptr; } @@ -3785,10 +3785,10 @@ if (!str) return false; - RootedTypeObject type(cx, GetTypeCallerInitObject(cx, JSProto_Array)); - if (!type) + RootedObjectGroup group(cx, GetCallerInitGroup(cx, JSProto_Array)); + if (!group) return false; - AddTypePropertyId(cx, type, JSID_VOID, Type::StringType()); + AddTypePropertyId(cx, group, JSID_VOID, Type::StringType()); /* Step 5: Use the second argument as the split limit, if given. */ uint32_t limit; @@ -3822,7 +3822,7 @@ JSObject *aobj = NewDenseEmptyArray(cx); if (!aobj) return false; - aobj->setType(type); + aobj->setGroup(group); args.rval().setObject(*aobj); return true; } @@ -3833,7 +3833,7 @@ JSObject *aobj = NewDenseCopiedArray(cx, 1, v.address()); if (!aobj) return false; - aobj->setType(type); + aobj->setGroup(group); args.rval().setObject(*aobj); return true; } @@ -3848,26 +3848,26 @@ aobj = CharSplitHelper(cx, linearStr, limit); } else { SplitStringMatcher matcher(cx, sepstr); - aobj = SplitHelper(cx, linearStr, limit, matcher, type); + aobj = SplitHelper(cx, linearStr, limit, matcher, group); } } else { RegExpStatics *res = cx->global()->getRegExpStatics(cx); if (!res) return false; SplitRegExpMatcher matcher(*re, res); - aobj = SplitHelper(cx, linearStr, limit, matcher, type); + aobj = SplitHelper(cx, linearStr, limit, matcher, group); } if (!aobj) return false; /* Step 16. */ - aobj->setType(type); + aobj->setGroup(group); args.rval().setObject(*aobj); return true; } JSObject * -js::str_split_string(JSContext *cx, HandleTypeObject type, HandleString str, HandleString sep) +js::str_split_string(JSContext *cx, HandleObjectGroup group, HandleString str, HandleString sep) { RootedLinearString linearStr(cx, str->ensureLinear(cx)); if (!linearStr) @@ -3884,13 +3884,13 @@ aobj = CharSplitHelper(cx, linearStr, limit); } else { SplitStringMatcher matcher(cx, linearSep); - aobj = SplitHelper(cx, linearStr, limit, matcher, type); + aobj = SplitHelper(cx, linearStr, limit, matcher, group); } if (!aobj) return nullptr; - aobj->setType(type); + aobj->setGroup(group); return aobj; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsstr.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsstr.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsstr.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsstr.h 2015-02-03 14:33:33.000000000 +0000 @@ -416,7 +416,7 @@ str_split(JSContext *cx, unsigned argc, Value *vp); JSObject * -str_split_string(JSContext *cx, HandleTypeObject type, HandleString str, HandleString sep); +str_split_string(JSContext *cx, HandleObjectGroup group, HandleString str, HandleString sep); bool str_resolve(JSContext *cx, HandleObject obj, HandleId id, bool *resolvedp); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsutil.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsutil.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsutil.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsutil.cpp 2015-02-03 14:33:33.000000000 +0000 @@ -50,7 +50,7 @@ // This function calls all the vanilla heap allocation functions. It is never // called, and exists purely to help config/check_vanilla_allocations.py. See // that script for more details. -extern void +extern MOZ_COLD void AllTheNonBasicVanillaNewAllocations() { // posix_memalign and aligned_alloc aren't available on all Linux diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsutil.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsutil.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsutil.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsutil.h 2015-02-03 14:33:33.000000000 +0000 @@ -43,6 +43,9 @@ namespace js { +MOZ_NORETURN MOZ_COLD void +CrashAtUnhandlableOOM(const char *reason); + template struct AlignmentTestStruct { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsversion.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsversion.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/jsversion.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/jsversion.h 2015-02-03 14:33:33.000000000 +0000 @@ -41,7 +41,4 @@ */ #define JS_OLD_GETTER_SETTER_METHODS 1 -/* Support for ES6 Symbols. */ -#define JS_HAS_SYMBOLS 1 - #endif /* jsversion_h */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/moz.build 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/moz.build 2015-02-03 14:33:33.000000000 +0000 @@ -154,6 +154,7 @@ 'jit/C1Spewer.cpp', 'jit/CodeGenerator.cpp', 'jit/CompileWrappers.cpp', + 'jit/Disassembler.cpp', 'jit/EdgeCaseAnalysis.cpp', 'jit/EffectiveAddressAnalysis.cpp', 'jit/ExecutableAllocator.cpp', @@ -178,6 +179,7 @@ 'jit/MIR.cpp', 'jit/MIRGraph.cpp', 'jit/MoveResolver.cpp', + 'jit/OptimizationTracking.cpp', 'jit/PerfSpewer.cpp', 'jit/RangeAnalysis.cpp', 'jit/Recover.cpp', @@ -200,7 +202,6 @@ 'jsbool.cpp', 'jscntxt.cpp', 'jscompartment.cpp', - 'jscrashreport.cpp', 'jsdate.cpp', 'jsdtoa.cpp', 'jsexn.cpp', @@ -271,6 +272,7 @@ 'vm/Symbol.cpp', 'vm/TypedArrayObject.cpp', 'vm/UbiNode.cpp', + 'vm/UnboxedObject.cpp', 'vm/Unicode.cpp', 'vm/Value.cpp', 'vm/WeakMapPtr.cpp', @@ -320,9 +322,11 @@ elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']: UNIFIED_SOURCES += [ 'jit/shared/Assembler-x86-shared.cpp', + 'jit/shared/AssemblerBuffer-x86-shared.cpp', 'jit/shared/BaselineCompiler-x86-shared.cpp', 'jit/shared/BaselineIC-x86-shared.cpp', 'jit/shared/CodeGenerator-x86-shared.cpp', + 'jit/shared/Disassembler-x86-shared.cpp', 'jit/shared/Lowering-x86-shared.cpp', 'jit/shared/MacroAssembler-x86-shared.cpp', 'jit/shared/MoveEmitter-x86-shared.cpp', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/proxy/Proxy.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/proxy/Proxy.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/proxy/Proxy.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/proxy/Proxy.cpp 2015-02-03 14:33:34.000000000 +0000 @@ -551,15 +551,15 @@ } bool -js::proxy_LookupGeneric(JSContext *cx, HandleObject obj, HandleId id, - MutableHandleObject objp, MutableHandleShape propp) +js::proxy_LookupProperty(JSContext *cx, HandleObject obj, HandleId id, + MutableHandleObject objp, MutableHandleShape propp) { bool found; if (!Proxy::has(cx, obj, id, &found)) return false; if (found) { - MarkNonNativePropertyFound(propp); + MarkNonNativePropertyFound(propp); objp.set(obj); } else { objp.set(nullptr); @@ -569,26 +569,8 @@ } bool -js::proxy_LookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, - MutableHandleObject objp, MutableHandleShape propp) -{ - RootedId id(cx, NameToId(name)); - return proxy_LookupGeneric(cx, obj, id, objp, propp); -} - -bool -js::proxy_LookupElement(JSContext *cx, HandleObject obj, uint32_t index, - MutableHandleObject objp, MutableHandleShape propp) -{ - RootedId id(cx); - if (!IndexToId(cx, index, &id)) - return false; - return proxy_LookupGeneric(cx, obj, id, objp, propp); -} - -bool -js::proxy_DefineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue value, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs) +js::proxy_DefineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue value, + PropertyOp getter, StrictPropertyOp setter, unsigned attrs) { Rooted desc(cx); desc.object().set(obj); @@ -600,71 +582,17 @@ } bool -js::proxy_DefineProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs) -{ - Rooted id(cx, NameToId(name)); - return proxy_DefineGeneric(cx, obj, id, value, getter, setter, attrs); -} - -bool -js::proxy_DefineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue value, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs) -{ - RootedId id(cx); - if (!IndexToId(cx, index, &id)) - return false; - return proxy_DefineGeneric(cx, obj, id, value, getter, setter, attrs); -} - -bool -js::proxy_GetGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, - MutableHandleValue vp) -{ - return Proxy::get(cx, obj, receiver, id, vp); -} - -bool -js::proxy_GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandlePropertyName name, +js::proxy_GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, MutableHandleValue vp) { - Rooted id(cx, NameToId(name)); - return proxy_GetGeneric(cx, obj, receiver, id, vp); -} - -bool -js::proxy_GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index, - MutableHandleValue vp) -{ - RootedId id(cx); - if (!IndexToId(cx, index, &id)) - return false; - return proxy_GetGeneric(cx, obj, receiver, id, vp); -} - -bool -js::proxy_SetGeneric(JSContext *cx, HandleObject obj, HandleId id, - MutableHandleValue vp, bool strict) -{ - return Proxy::set(cx, obj, obj, id, strict, vp); + return Proxy::get(cx, obj, receiver, id, vp); } bool -js::proxy_SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, +js::proxy_SetProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp, bool strict) { - Rooted id(cx, NameToId(name)); - return proxy_SetGeneric(cx, obj, id, vp, strict); -} - -bool -js::proxy_SetElement(JSContext *cx, HandleObject obj, uint32_t index, - MutableHandleValue vp, bool strict) -{ - RootedId id(cx); - if (!IndexToId(cx, index, &id)) - return false; - return proxy_SetGeneric(cx, obj, id, vp, strict); + return Proxy::set(cx, obj, obj, id, strict, vp); } bool @@ -675,7 +603,7 @@ } bool -js::proxy_SetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp) +js::proxy_SetPropertyAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp) { /* Lookup the current property descriptor so we have setter/getter/value. */ Rooted desc(cx); @@ -686,7 +614,7 @@ } bool -js::proxy_DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded) +js::proxy_DeleteProperty(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded) { bool deleted; if (!Proxy::delete_(cx, obj, id, &deleted)) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/proxy/ScriptedDirectProxyHandler.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/proxy/ScriptedDirectProxyHandler.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/proxy/ScriptedDirectProxyHandler.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/proxy/ScriptedDirectProxyHandler.cpp 2015-02-03 14:33:34.000000000 +0000 @@ -1095,6 +1095,82 @@ } bool +ScriptedDirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, + CallArgs args) const +{ + ReportIncompatible(cx, args); + return false; +} + +bool +ScriptedDirectProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, + bool *bp) const +{ + RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); + if (!handler) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); + return false; + } + + return DirectProxyHandler::hasInstance(cx, proxy, v, bp); +} + +bool +ScriptedDirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue, + JSContext *cx) const +{ + // Special case IsArray. In every other instance ES wants to have exactly + // one object type and not a proxy around it, so return false. + if (classValue != ESClass_IsArray) + return false; + + // In ES6 IsArray is supposed to poke at the Proxy target, instead we do this here. + // The reason for this is that we have proxies for which looking at the target might + // be impossible. So instead we use our little objectClassIs function that just works + // already across different wrappers. + RootedObject target(cx, proxy->as().target()); + if (!target) + return false; + + return IsArray(target, cx); +} + +const char * +ScriptedDirectProxyHandler::className(JSContext *cx, HandleObject proxy) const +{ + // Right now the caller is not prepared to handle failures. + RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); + if (!handler) + return BaseProxyHandler::className(cx, proxy); + + return DirectProxyHandler::className(cx, proxy); +} +JSString * +ScriptedDirectProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, + unsigned indent) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, + js_Function_str, js_toString_str, "object"); + return nullptr; +} + +bool +ScriptedDirectProxyHandler::regexp_toShared(JSContext *cx, HandleObject proxy, + RegExpGuard *g) const +{ + MOZ_CRASH("Should not end up in ScriptedDirectProxyHandler::regexp_toShared"); + return false; +} + +bool +ScriptedDirectProxyHandler::boxedValue_unbox(JSContext *cx, HandleObject proxy, + MutableHandleValue vp) const +{ + MOZ_CRASH("Should not end up in ScriptedDirectProxyHandler::boxedValue_unbox"); + return false; +} + +bool ScriptedDirectProxyHandler::isCallable(JSObject *obj) const { MOZ_ASSERT(obj->as().handler() == &ScriptedDirectProxyHandler::singleton); @@ -1113,13 +1189,12 @@ const char ScriptedDirectProxyHandler::family = 0; const ScriptedDirectProxyHandler ScriptedDirectProxyHandler::singleton; -bool -js::proxy(JSContext *cx, unsigned argc, jsval *vp) +static bool +NewScriptedProxy(JSContext *cx, CallArgs &args, const char *callerName) { - CallArgs args = CallArgsFromVp(argc, vp); if (args.length() < 2) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, - "Proxy", "1", "s"); + callerName, "1", "s"); return false; } RootedObject target(cx, NonNullObject(cx, args[0])); @@ -1146,6 +1221,19 @@ return true; } +bool +js::proxy(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + if (!args.isConstructing()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_FUNCTION, "Proxy"); + return false; + } + + return NewScriptedProxy(cx, args, "Proxy"); +} + static bool RevokeProxy(JSContext *cx, unsigned argc, Value *vp) { @@ -1170,9 +1258,9 @@ bool js::proxy_revocable(JSContext *cx, unsigned argc, Value *vp) { - CallReceiver args = CallReceiverFromVp(vp); + CallArgs args = CallArgsFromVp(argc, vp); - if (!proxy(cx, argc, vp)) + if (!NewScriptedProxy(cx, args, "Proxy.revocable")) return false; RootedValue proxyVal(cx, args.rval()); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/proxy/ScriptedDirectProxyHandler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/proxy/ScriptedDirectProxyHandler.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/proxy/ScriptedDirectProxyHandler.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/proxy/ScriptedDirectProxyHandler.h 2015-02-03 14:33:34.000000000 +0000 @@ -64,6 +64,21 @@ return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props); } + // A scripted proxy should not be treated as generic in most contexts. + virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, + CallArgs args) const MOZ_OVERRIDE; + virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, + bool *bp) const MOZ_OVERRIDE; + virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, + JSContext *cx) const MOZ_OVERRIDE; + virtual const char *className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; + virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, + unsigned indent) const MOZ_OVERRIDE; + virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, + RegExpGuard *g) const MOZ_OVERRIDE; + virtual bool boxedValue_unbox(JSContext *cx, HandleObject proxy, + MutableHandleValue vp) const MOZ_OVERRIDE; + virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE; virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/shell/js.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/shell/js.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/shell/js.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/shell/js.cpp 2015-02-03 14:33:34.000000000 +0000 @@ -1035,7 +1035,7 @@ return false; } - RootedObject obj(cx, JS_NewObject(cx, &CacheEntry_class, JS::NullPtr(), JS::NullPtr())); + RootedObject obj(cx, JS_NewObject(cx, &CacheEntry_class)); if (!obj) return false; @@ -4039,7 +4039,7 @@ JSCLASS_EMULATES_UNDEFINED }; - RootedObject obj(cx, JS_NewObject(cx, &cls, JS::NullPtr(), JS::NullPtr())); + RootedObject obj(cx, JS_NewObject(cx, &cls)); if (!obj) return false; args.rval().setObject(*obj); @@ -4171,6 +4171,10 @@ { JSRuntime *rt = reinterpret_cast(arg); + // If profiling is not enabled, don't do anything. + if (!rt->spsProfiler.enabled()) + return; + JS::ProfilingFrameIterator::RegisterState state; state.pc = pc; state.sp = (void*)sim->get_register(jit::Simulator::sp); @@ -4209,7 +4213,7 @@ #if defined(JS_ARM_SIMULATOR) CallArgs args = CallArgsFromVp(argc, vp); - jit::Simulator *sim = cx->runtime()->mainThread.simulator(); + jit::Simulator *sim = cx->runtime()->simulator(); sim->enable_single_stepping(SingleStepCallback, cx->runtime()); args.rval().setUndefined(); @@ -4226,7 +4230,7 @@ #if defined(JS_ARM_SIMULATOR) CallArgs args = CallArgsFromVp(argc, vp); - jit::Simulator *sim = cx->runtime()->mainThread.simulator(); + jit::Simulator *sim = cx->runtime()->simulator(); sim->disable_single_stepping(); AutoValueVector elems(cx); @@ -4426,9 +4430,9 @@ " themselves from roots that keep |thing| from being collected. (We could make\n" " this distinction if it is useful.)\n" "\n" -" If any references are found by the conservative scanner, the references\n" -" object will have a property named \"edge: machine stack\"; the referrers will\n" -" be 'null', because they are roots."), +" If there are any references on the native stack, the references\n" +" object will have properties named like \"edge: exact-value \"; the referrers\n" +" will be 'null', because they are roots."), #endif JS_FN_HELP("build", BuildDate, 0, 0, @@ -5092,7 +5096,7 @@ } RootedObject proto(cx, &protov.toObject()); - RootedObject domObj(cx, JS_NewObject(cx, &dom_class, proto, JS::NullPtr())); + RootedObject domObj(cx, JS_NewObjectWithGivenProto(cx, &dom_class, proto, JS::NullPtr())); if (!domObj) return false; @@ -5529,11 +5533,13 @@ bool enableIon = !op.getBoolOption("no-ion"); bool enableAsmJS = !op.getBoolOption("no-asmjs"); bool enableNativeRegExp = !op.getBoolOption("no-native-regexp"); + bool enableUnboxedObjects = op.getBoolOption("unboxed-objects"); JS::RuntimeOptionsRef(rt).setBaseline(enableBaseline) .setIon(enableIon) .setAsmJS(enableAsmJS) - .setNativeRegExp(enableNativeRegExp); + .setNativeRegExp(enableNativeRegExp) + .setUnboxedObjects(enableUnboxedObjects); if (const char *str = op.getStringOption("ion-scalar-replacement")) { if (strcmp(str, "on") == 0) @@ -5875,6 +5881,7 @@ || !op.addBoolOption('\0', "no-ion", "Disable IonMonkey") || !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation") || !op.addBoolOption('\0', "no-native-regexp", "Disable native regexp compilation") + || !op.addBoolOption('\0', "unboxed-objects", "Allow creating unboxed objects") || !op.addStringOption('\0', "ion-scalar-replacement", "on/off", "Scalar Replacement (default: on, off to disable)") || !op.addStringOption('\0', "ion-gvn", "[mode]", @@ -5935,6 +5942,7 @@ "unnecessarily entrained by inner functions") #endif || !op.addBoolOption('\0', "no-ggc", "Disable Generational GC") + || !op.addBoolOption('\0', "no-cgc", "Disable Compacting GC") || !op.addBoolOption('\0', "no-incremental-gc", "Disable Incremental GC") || !op.addIntOption('\0', "available-memory", "SIZE", "Select GC settings based on available memory (MB)", 0) @@ -6043,10 +6051,15 @@ gInterruptFunc.init(rt, NullValue()); JS_SetGCParameter(rt, JSGC_MAX_BYTES, 0xffffffff); + Maybe noggc; if (op.getBoolOption("no-ggc")) noggc.emplace(rt); + Maybe nocgc; + if (op.getBoolOption("no-cgc")) + nocgc.emplace(rt); + size_t availMem = op.getIntOption("available-memory"); if (availMem > 0) JS_SetGCParametersBasedOnAvailableMemory(rt, availMem); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/shell/OSObject.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/shell/OSObject.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/shell/OSObject.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/shell/OSObject.cpp 2015-02-03 14:33:34.000000000 +0000 @@ -21,6 +21,8 @@ // For JSFunctionSpecWithHelp #include "jsfriendapi.h" +#include "js/Conversions.h" + using namespace JS; static bool diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/compare_bench.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/compare_bench.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/compare_bench.py 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/compare_bench.py 2015-02-03 14:33:34.000000000 +0000 @@ -2,9 +2,10 @@ """usage: %progname candidate_path baseline_path """ +from __future__ import print_function + import json import optparse -from contextlib import nested from operator import itemgetter @@ -18,29 +19,32 @@ try: baseline_result = baseline[key] except KeyError: - print key, 'missing from baseline' + print(key, 'missing from baseline') continue val_getter = itemgetter('average_ms', 'stddev_ms') base_avg, base_stddev = val_getter(baseline_result) current_avg, current_stddev = val_getter(current_result) - t_best, t_worst = current_avg - current_stddev, current_avg + current_stddev - base_t_best, base_t_worst = base_avg - base_stddev, base_avg + base_stddev - fmt = '%30s: %s' - if t_worst < base_t_best: # Worst takes less time (better) than baseline's best. + t_best = current_avg - current_stddev + t_worst = current_avg + current_stddev + base_t_best = base_avg - base_stddev + base_t_worst = base_avg + base_stddev + if t_worst < base_t_best: + # Worst takes less time (better) than baseline's best. speedup = -((t_worst - base_t_best) / base_t_best) * 100 - result = 'faster: %6.2fms < baseline %6.2fms (%+6.2f%%)' % \ - (t_worst, base_t_best, speedup) + result = 'faster: {:6.2f}ms < baseline {:6.2f}ms ({:+6.2f}%)'.format( + t_worst, base_t_best, speedup) percent_speedups.append(speedup) - elif t_best > base_t_worst: # Best takes more time (worse) than baseline's worst. + elif t_best > base_t_worst: + # Best takes more time (worse) than baseline's worst. slowdown = -((t_best - base_t_worst) / base_t_worst) * 100 - result = 'SLOWER: %6.2fms > baseline %6.2fms (%+6.2f%%) ' % \ - (t_best, base_t_worst, slowdown) + result = 'SLOWER: {:6.2f}ms > baseline {:6.2f}ms ({:+6.2f}%) '.format( + t_best, base_t_worst, slowdown) percent_speedups.append(slowdown) else: result = 'Meh.' - print '%30s: %s' % (key, result) + print('{:30s}: {}'.format(key, result)) if percent_speedups: - print 'Average speedup: %.2f%%' % avg(percent_speedups) + print('Average speedup: {:.2f}%'.format(avg(percent_speedups))) def compare_immediate(current_map, baseline_path): diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_5/misc/bug1126318.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_5/misc/bug1126318.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_5/misc/bug1126318.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_5/misc/bug1126318.js 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,13 @@ +if (typeof window === "undefined") + window = this; + +Object.defineProperty(window, "foo", { + get: function() { return 5; }, + configurable: true +}); + +for (var i = 0; i < 100; ++i) + assertEq(window.foo, 5); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Array/for_of_2.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Array/for_of_2.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Array/for_of_2.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Array/for_of_2.js 2015-02-03 14:33:36.000000000 +0000 @@ -25,7 +25,7 @@ return M2; } - var iter = ([])[std_iterator](); + var iter = ([])[Symbol.iterator](); var iterProto = Object.getPrototypeOf(iter); var OldNext = iterProto.next; var NewNext = function () { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Array/for_of_3.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Array/for_of_3.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Array/for_of_3.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Array/for_of_3.js 2015-02-03 14:33:36.000000000 +0000 @@ -27,7 +27,7 @@ return M2; } - var iter = ([])[std_iterator](); + var iter = ([])[Symbol.iterator](); var iterProto = Object.getPrototypeOf(iter); var OldNext = iterProto.next; var NewNext = function () { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Array/for_of_4.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Array/for_of_4.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Array/for_of_4.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Array/for_of_4.js 2015-02-03 14:33:36.000000000 +0000 @@ -26,7 +26,7 @@ return M2; } - var iter = ([])[std_iterator](); + var iter = ([])[Symbol.iterator](); var iterProto = Object.getPrototypeOf(iter); var OldNext = iterProto.next; var NewNext = function () { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Array/from_errors.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Array/from_errors.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Array/from_errors.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Array/from_errors.js 2015-02-03 14:33:36.000000000 +0000 @@ -141,7 +141,7 @@ for (var primitive of [undefined, null, 17]) { assertThrowsInstanceOf( () => Array.from({ - [std_iterator]() { + [Symbol.iterator]() { return {next() { return primitive; }}; } }), diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Array/from_iterable.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Array/from_iterable.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Array/from_iterable.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Array/from_iterable.js 2015-02-03 14:33:36.000000000 +0000 @@ -8,7 +8,7 @@ // If an object has both .length and [@@iterator] properties, [@@iterator] is used. var a = ['a', 'e', 'i', 'o', 'u']; -a[std_iterator] = function* () { +a[Symbol.iterator] = function* () { for (var i = 5; i--; ) yield this[i]; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Array/from_proxy.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Array/from_proxy.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Array/from_proxy.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Array/from_proxy.js 2015-02-03 14:33:36.000000000 +0000 @@ -36,7 +36,7 @@ // calls handler.get on it. log = []; assertDeepEq(Array.from(new LoggingProxy([3, 4, 5])), [3, 4, 5]); -assertDeepEq(log, ["get", std_iterator, +assertDeepEq(log, ["get", Symbol.iterator, "get", "length", "get", "0", "get", "length", "get", "1", "get", "length", "get", "2", @@ -45,9 +45,9 @@ // Array-like iteration only gets the length once. log = []; var arr = [5, 6, 7]; -arr[std_iterator] = undefined; +arr[Symbol.iterator] = undefined; assertDeepEq(Array.from(new LoggingProxy(arr)), [5, 6, 7]); -assertDeepEq(log, ["get", std_iterator, +assertDeepEq(log, ["get", Symbol.iterator, "get", "length", "get", "0", "get", "1", "get", "2"]); if (typeof reportCompare === 'function') diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Array/from_string.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Array/from_string.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Array/from_string.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Array/from_string.js 2015-02-03 14:33:36.000000000 +0000 @@ -11,11 +11,11 @@ assertDeepEq(Array.from(gclef + " G"), [gclef, " ", "G"]); // Array.from on a string calls the @@iterator method. -String.prototype[std_iterator] = function* () { yield 1; yield 2; }; +String.prototype[Symbol.iterator] = function* () { yield 1; yield 2; }; assertDeepEq(Array.from("anything"), [1, 2]); // If the iterator method is deleted, Strings are still arraylike. -delete String.prototype[std_iterator]; +delete String.prototype[Symbol.iterator]; assertDeepEq(Array.from("works"), ['w', 'o', 'r', 'k', 's']); assertDeepEq(Array.from(gclef), ['\uD834', '\uDD1E']); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Comprehensions/error-messages.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Comprehensions/error-messages.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Comprehensions/error-messages.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Comprehensions/error-messages.js 2015-02-03 14:33:36.000000000 +0000 @@ -31,62 +31,64 @@ const cases = [ // Expressions involving yield without a value, not currently implemented. Many -// of these errors would need to be updated. -//{ expr: "yield", top: TOP_YIELD, fun: null, gen: GENEXP_YIELD, desc: "simple yield" }, -//{ expr: "1, yield", top: TOP_YIELD, fun: null, gen: GENEXP_YIELD, desc: "simple yield at end of list" }, -//{ expr: "yield, 1", top: TOP_YIELD, fun: YIELD_PAREN, gen: YIELD_PAREN, desc: "simple yield in list" }, -//{ expr: "(yield)", top: TOP_YIELD, fun: null, gen: GENEXP_YIELD, desc: "simple yield, parenthesized" }, -//{ expr: "(1, yield)", top: TOP_YIELD, fun: null, gen: GENEXP_YIELD, desc: "simple yield at end of list, parenthesized" }, -//{ expr: "(yield, 1)", top: TOP_YIELD, fun: YIELD_PAREN, gen: YIELD_PAREN, desc: "simple yield in list, parenthesized" }, -//{ expr: "((((yield))))", top: TOP_YIELD, fun: null, gen: GENEXP_YIELD, desc: "deeply nested yield" }, -//{ expr: "(for (x of []) yield)", top: TOP_YIELD, fun: GENERIC, gen: GENERIC, desc: "simple yield in genexp" }, -//{ expr: "(for (x of []) yield, 1)", top: TOP_YIELD, fun: YIELD_PAREN, gen: YIELD_PAREN, desc: "simple yield in list in genexp" }, -//{ expr: "(for (x of []) 1, yield)", top: TOP_YIELD, fun: GENERIC, gen: GENERIC, desc: "simple yield at end of list in genexp" }, -//{ expr: "(for (x of []) (yield))", top: TOP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, desc: "simple yield, parenthesized in genexp" }, -//{ expr: "(for (x of []) 1, (yield))", top: TOP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, desc: "simple yield, parenthesized in list in genexp" }, -//{ expr: "(for (x of []) (1, yield))", top: TOP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, desc: "simple yield at end of list, parenthesized in genexp" }, -//{ expr: "(for (x of []) 1, (2, yield))", top: TOP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, desc: "simple yield at end of list, parenthesized in list in genexp" }, -//{ expr: "(for (x of []) (yield, 1))", top: TOP_YIELD, fun: YIELD_PAREN, gen: YIELD_PAREN, desc: "simple yield in list, parenthesized in genexp" }, -//{ expr: "(for (x of []) 1, (yield, 2))", top: TOP_YIELD, fun: YIELD_PAREN, gen: YIELD_PAREN, desc: "simple yield in list, parenthesized in list in genexp" }, +// of these errors would need to be updated. (Note: line numbers below might be +// mere placeholders, not actually the expected correct behavior -- check before +// blindly uncommenting.) +//{ expr: "yield", top: [TOP_YIELD, 777], fun: null, gen: [GENEXP_YIELD, 2], desc: "simple yield" }, +//{ expr: "1, yield", top: [TOP_YIELD, 777], fun: null, gen: [GENEXP_YIELD, 2], desc: "simple yield at end of list" }, +//{ expr: "yield, 1", top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2], desc: "simple yield in list" }, +//{ expr: "(yield)", top: [TOP_YIELD, 777], fun: null, gen: [GENEXP_YIELD, 2], desc: "simple yield, parenthesized" }, +//{ expr: "(1, yield)", top: [TOP_YIELD, 777], fun: null, gen: [GENEXP_YIELD, 2], desc: "simple yield at end of list, parenthesized" }, +//{ expr: "(yield, 1)", top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2], desc: "simple yield in list, parenthesized" }, +//{ expr: "((((yield))))", top: [TOP_YIELD, 777], fun: null, gen: [GENEXP_YIELD, 2], desc: "deeply nested yield" }, +//{ expr: "(for (x of []) yield)", top: [TOP_YIELD, 777], fun: [GENERIC, 777], gen: [GENERIC, 777], desc: "simple yield in genexp" }, +//{ expr: "(for (x of []) yield, 1)", top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2], desc: "simple yield in list in genexp" }, +//{ expr: "(for (x of []) 1, yield)", top: [TOP_YIELD, 777], fun: [GENERIC, 777], gen: [GENERIC, 777], desc: "simple yield at end of list in genexp" }, +//{ expr: "(for (x of []) (yield))", top: [TOP_YIELD, 777], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], desc: "simple yield, parenthesized in genexp" }, +//{ expr: "(for (x of []) 1, (yield))", top: [TOP_YIELD, 777], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], desc: "simple yield, parenthesized in list in genexp" }, +//{ expr: "(for (x of []) (1, yield))", top: [TOP_YIELD, 777], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], desc: "simple yield at end of list, parenthesized in genexp" }, +//{ expr: "(for (x of []) 1, (2, yield))", top: [TOP_YIELD, 777], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], desc: "simple yield at end of list, parenthesized in list in genexp" }, +//{ expr: "(for (x of []) (yield, 1))", top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2], desc: "simple yield in list, parenthesized in genexp" }, +//{ expr: "(for (x of []) 1, (yield, 2))", top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2], desc: "simple yield in list, parenthesized in list in genexp" }, //{ expr: "(for (x of []) (function*() { yield }))", top: null, fun: null, gen: null, desc: "legal yield in nested function" }, // yield expressions - { expr: "yield 1", top: MISSING_SEMI, fun: MISSING_SEMI, gen: null, genexp: GENEXP_YIELD, desc: "yield w/ arg" }, - { expr: "1, yield 2", top: MISSING_SEMI, fun: MISSING_SEMI, gen: null, genexp: FOR_OF_PAREN, desc: "yield w/ arg at end of list" }, - { expr: "yield 1, 2", top: MISSING_SEMI, fun: MISSING_SEMI, gen: null, genexp: FOR_OF_PAREN, desc: "yield w/ arg in list" }, - { expr: "(yield 1)", top: MISSING_PAREN, fun: MISSING_PAREN, gen: null, genexp: GENEXP_YIELD, desc: "yield w/ arg, parenthesized" }, - { expr: "(1, yield 2)", top: MISSING_PAREN, fun: MISSING_PAREN, gen: null, genexp: GENEXP_YIELD, desc: "yield w/ arg at end of list, parenthesized" }, - { expr: "(yield 1, 2)", top: MISSING_PAREN, fun: MISSING_PAREN, gen: null, genexp: YIELD_PAREN, desc: "yield w/ arg in list, parenthesized" }, + { expr: "yield 1", top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg" }, + { expr: "1, yield 2", top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: null, genexp: [FOR_OF_PAREN, 1], desc: "yield w/ arg at end of list" }, + { expr: "yield 1, 2", top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: [YIELD_PAREN, 3], genexp: [FOR_OF_PAREN, 3], desc: "yield w/ arg in list" }, + { expr: "(yield 1)", top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg, parenthesized" }, + { expr: "(1, yield 2)", top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg at end of list, parenthesized" }, + { expr: "(yield 1, 2)", top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: [YIELD_PAREN, 3], genexp: [YIELD_PAREN, 2], desc: "yield w/ arg in list, parenthesized" }, // deeply nested yield expressions - { expr: "((((yield 1))))", top: MISSING_PAREN, fun: MISSING_PAREN, gen: null, genexp: GENEXP_YIELD, desc: "deeply nested yield w/ arg" }, + { expr: "((((yield 1))))", top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "deeply nested yield w/ arg" }, // arguments { expr: "arguments", top: null, fun: null, gen: null, genexp: null, desc: "arguments in list" }, - { expr: "1, arguments", top: null, fun: null, gen: null, genexp: FOR_OF_PAREN, desc: "arguments in list" }, + { expr: "1, arguments", top: null, fun: null, gen: null, genexp: [FOR_OF_PAREN, 1], desc: "arguments in list" }, // yield in generator expressions - { expr: "(for (x of []) yield 1)", top: GENEXP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "yield w/ arg in genexp" }, - { expr: "(for (x of []) yield 1, 2)", top: GENEXP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "yield w/ arg in list in genexp" }, - { expr: "(for (x of []) 1, yield 2)", top: PAREN_PAREN, fun: PAREN_PAREN, gen: PAREN_PAREN, genexp: PAREN_PAREN, desc: "yield w/ arg at end of list in genexp" }, - { expr: "(for (x of []) (yield 1))", top: GENEXP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "yield w/ arg, parenthesized in genexp" }, - { expr: "(for (x of []) 1, (yield 2))", top: PAREN_PAREN, fun: PAREN_PAREN, gen: PAREN_PAREN, genexp: PAREN_PAREN, desc: "yield w/ arg, parenthesized in list in genexp" }, - { expr: "(for (x of []) (1, yield 2))", top: GENEXP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "yield w/ arg at end of list, parenthesized in genexp" }, - { expr: "(for (x of []) 1, (2, yield 3))", top: PAREN_PAREN, fun: PAREN_PAREN, gen: PAREN_PAREN, genexp: PAREN_PAREN, desc: "yield w/ arg at end of list, parenthesized in list in genexp" }, - { expr: "(for (x of []) (yield 1, 2))", top: YIELD_PAREN, fun: YIELD_PAREN, gen: YIELD_PAREN, genexp: YIELD_PAREN, desc: "yield w/ arg in list, parenthesized in genexp" }, - { expr: "(for (x of []) 1, (yield 2, 3))", top: PAREN_PAREN, fun: PAREN_PAREN, gen: PAREN_PAREN, genexp: PAREN_PAREN, desc: "yield w/ arg in list, parenthesized in list in genexp" }, + { expr: "(for (x of []) yield 1)", top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg in genexp" }, + { expr: "(for (x of []) yield 1, 2)", top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg in list in genexp" }, + { expr: "(for (x of []) 1, yield 2)", top: [PAREN_PAREN, 1], fun: [PAREN_PAREN, 1], gen: [PAREN_PAREN, 1], genexp: [PAREN_PAREN, 1], desc: "yield w/ arg at end of list in genexp" }, + { expr: "(for (x of []) (yield 1))", top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg, parenthesized in genexp" }, + { expr: "(for (x of []) 1, (yield 2))", top: [PAREN_PAREN, 1], fun: [PAREN_PAREN, 1], gen: [PAREN_PAREN, 1], genexp: [PAREN_PAREN, 1], desc: "yield w/ arg, parenthesized in list in genexp" }, + { expr: "(for (x of []) (1, yield 2))", top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg at end of list, parenthesized in genexp" }, + { expr: "(for (x of []) 1, (2, yield 3))", top: [PAREN_PAREN, 1], fun: [PAREN_PAREN, 1], gen: [PAREN_PAREN, 1], genexp: [PAREN_PAREN, 1], desc: "yield w/ arg at end of list, parenthesized in list in genexp" }, + { expr: "(for (x of []) (yield 1, 2))", top: [YIELD_PAREN, 2], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2], genexp: [YIELD_PAREN, 2], desc: "yield w/ arg in list, parenthesized in genexp" }, + { expr: "(for (x of []) 1, (yield 2, 3))", top: [PAREN_PAREN, 1], fun: [PAREN_PAREN, 1], gen: [PAREN_PAREN, 1], genexp: [PAREN_PAREN, 1], desc: "yield w/ arg in list, parenthesized in list in genexp" }, // deeply nested yield in generator expressions - { expr: "(for (x of []) (((1, yield 2))))", top: GENEXP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "deeply nested yield in genexp" }, - { expr: "(for (y of []) (for (x of []) ((1, yield 2))))", top: GENEXP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "deeply nested yield in multiple genexps" }, + { expr: "(for (x of []) (((1, yield 2))))", top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "deeply nested yield in genexp" }, + { expr: "(for (y of []) (for (x of []) ((1, yield 2))))", top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "deeply nested yield in multiple genexps" }, // arguments in generator expressions { expr: "(for (x of []) arguments)", top: null, fun: null, gen: null, genexp: null, desc: "simple arguments in genexp" }, - { expr: "(for (x of []) 1, arguments)", top: BAD_GENERATOR_SYNTAX, fun: BAD_GENERATOR_SYNTAX, gen: BAD_GENERATOR_SYNTAX, genexp: BAD_GENERATOR_SYNTAX, desc: "arguments in list in genexp" }, + { expr: "(for (x of []) 1, arguments)", top: [BAD_GENERATOR_SYNTAX, 1], fun: [BAD_GENERATOR_SYNTAX, 1], gen: [BAD_GENERATOR_SYNTAX, 1], genexp: [BAD_GENERATOR_SYNTAX, 1], desc: "arguments in list in genexp" }, { expr: "(for (x of []) (arguments))", top: null, fun: null, gen: null, genexp: null, desc: "arguments, parenthesized in genexp" }, - { expr: "(for (x of []) 1, (arguments))", top: BAD_GENERATOR_SYNTAX, fun: BAD_GENERATOR_SYNTAX, gen: BAD_GENERATOR_SYNTAX, genexp: BAD_GENERATOR_SYNTAX, desc: "arguments, parenthesized in list in genexp" }, + { expr: "(for (x of []) 1, (arguments))", top: [BAD_GENERATOR_SYNTAX, 1], fun: [BAD_GENERATOR_SYNTAX, 1], gen: [BAD_GENERATOR_SYNTAX, 1], genexp: [BAD_GENERATOR_SYNTAX, 1], desc: "arguments, parenthesized in list in genexp" }, { expr: "(for (x of []) (1, arguments))", top: null, fun: null, gen: null, genexp: null, desc: "arguments in list, parenthesized in genexp" }, - { expr: "(for (x of []) 1, (2, arguments))", top: BAD_GENERATOR_SYNTAX, fun: BAD_GENERATOR_SYNTAX, gen: BAD_GENERATOR_SYNTAX, genexp: BAD_GENERATOR_SYNTAX, desc: "arguments in list, parenthesized in list in genexp" }, + { expr: "(for (x of []) 1, (2, arguments))", top: [BAD_GENERATOR_SYNTAX, 1], fun: [BAD_GENERATOR_SYNTAX, 1], gen: [BAD_GENERATOR_SYNTAX, 1], genexp: [BAD_GENERATOR_SYNTAX, 1], desc: "arguments in list, parenthesized in list in genexp" }, // deeply nested arguments in generator expressions { expr: "(for (x of []) (((1, arguments))))", top: null, fun: null, gen: null, genexp: null, desc: "deeply nested arguments in genexp" }, @@ -110,15 +112,14 @@ replace(/arguments/, '\narguments\n'); } -function expectError1(err, ctx, msg) { +function expectError1(err, ctx, msg, lineNumber) { reportCompare('object', typeof err, 'exn for: ' + msg); reportCompare(ctx, err.message, 'exn message for: ' + msg); - if (ctx !== BAD_GENERATOR_SYNTAX && ctx != PAREN_PAREN && ctx != FOR_OF_PAREN) - reportCompare(2, err.lineNumber, 'exn token for: ' + msg); + reportCompare(lineNumber, err.lineNumber, 'exn token for: ' + msg); } -function expectError(expr, wrapCtx, expect, msg) { - expectError1(error(wrapCtx(expr)), expect, msg); +function expectError(expr, wrapCtx, [expect, lineNumber], msg) { + expectError1(error(wrapCtx(expr)), expect, msg, lineNumber); } function expectSuccess(err, msg) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Date/toISOString.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Date/toISOString.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Date/toISOString.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Date/toISOString.js 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,53 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 730831; +var summary = 'Date.prototype.toISOString returns an invalid ISO-8601 string'; + +print(BUGNUMBER + ": " + summary); + +function iso(t) { + return new Date(t).toISOString(); +} + +function utc(year, month, day, hour, minute, second, millis) { + var date = new Date(0); + date.setUTCFullYear(year, month - 1, day); + date.setUTCHours(hour, minute, second, millis); + return date.getTime(); +} + + +// Values around maximum date for simple iso format. +var maxDateSimple = utc(9999, 12, 31, 23, 59, 59, 999); +assertEq(iso(maxDateSimple - 1), "9999-12-31T23:59:59.998Z"); +assertEq(iso(maxDateSimple ), "9999-12-31T23:59:59.999Z"); +assertEq(iso(maxDateSimple + 1), "+010000-01-01T00:00:00.000Z"); + + +// Values around minimum date for simple iso format. +var minDateSimple = utc(0, 1, 1, 0, 0, 0, 0); +assertEq(iso(minDateSimple - 1), "-000001-12-31T23:59:59.999Z"); +assertEq(iso(minDateSimple ), "0000-01-01T00:00:00.000Z"); +assertEq(iso(minDateSimple + 1), "0000-01-01T00:00:00.001Z"); + + +// Values around maximum date for extended iso format. +var maxDateExtended = utc(+275760, 9, 13, 0, 0, 0, 0); +assertEq(maxDateExtended, +8.64e15); +assertEq(iso(maxDateExtended - 1), "+275760-09-12T23:59:59.999Z"); +assertEq(iso(maxDateExtended ), "+275760-09-13T00:00:00.000Z"); +assertThrowsInstanceOf(() => iso(maxDateExtended + 1), RangeError); + + +// Values around minimum date for extended iso format. +var minDateExtended = utc(-271821, 4, 20, 0, 0, 0, 0); +assertEq(minDateExtended, -8.64e15); +assertThrowsInstanceOf(() => iso(minDateExtended - 1), RangeError); +assertEq(iso(minDateExtended ), "-271821-04-20T00:00:00.000Z"); +assertEq(iso(minDateExtended + 1), "-271821-04-20T00:00:00.001Z"); + + +if (typeof reportCompare === "function") + reportCompare(true, true); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Expressions/delete-name-parenthesized-early-error-strict-mode.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Expressions/delete-name-parenthesized-early-error-strict-mode.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Expressions/delete-name-parenthesized-early-error-strict-mode.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Expressions/delete-name-parenthesized-early-error-strict-mode.js 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,76 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 1111101; +var summary = + "delete (foo), delete ((foo)), and so on are strict mode early errors"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +function checkSyntaxError(code) +{ + function helper(maker) + { + var earlyError = false; + try + { + var f = maker(code); + + var error = "no early error, created a function with code <" + code + ">"; + try + { + f(); + error += ", and the function can be called without error"; + } + catch (e) + { + error +=", and calling the function throws " + e; + } + + throw new Error(error); + } + catch (e) + { + assertEq(e instanceof SyntaxError, true, + "expected syntax error, got " + e); + } + } + + helper(Function); + helper(eval); +} + +checkSyntaxError("function f() { 'use strict'; delete escape; } f();"); +checkSyntaxError("function f() { 'use strict'; delete escape; }"); +checkSyntaxError("function f() { 'use strict'; delete (escape); } f();"); +checkSyntaxError("function f() { 'use strict'; delete (escape); }"); +checkSyntaxError("function f() { 'use strict'; delete ((escape)); } f();"); +checkSyntaxError("function f() { 'use strict'; delete ((escape)); }"); + +// Meanwhile, non-strict all of these should work + +function checkFine(code) +{ + Function(code); + (1, eval)(code); // indirect, to be consistent w/above +} + +checkFine("function f() { delete escape; } f();"); +checkFine("function f() { delete escape; }"); +checkFine("function f() { delete (escape); } f();"); +checkFine("function f() { delete (escape); }"); +checkFine("function f() { delete ((escape)); } f();"); +checkFine("function f() { delete ((escape)); }"); + + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Function/arrow-has-duplicated.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Function/arrow-has-duplicated.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Function/arrow-has-duplicated.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Function/arrow-has-duplicated.js 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,15 @@ +// Make sure duplicated name is allowed in non-strict. +function f0(a, a) { +} + +// SyntaxError should be thrown if arrow function has duplicated name. +assertThrowsInstanceOf(() => eval(` +(a, a) => { +}; +`), SyntaxError); +assertThrowsInstanceOf(() => eval(` +(a, ...a) => { +}; +`), SyntaxError); + +reportCompare(0, 0, 'ok'); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-11.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-11.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-11.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-11.js 2015-02-03 14:33:36.000000000 +0000 @@ -8,7 +8,7 @@ } this.next = next; - this[std_iterator] = function () { return this; } + this[Symbol.iterator] = function () { return this; } } function* delegate(iter) { return yield* iter; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-12.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-12.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-12.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-12.js 2015-02-03 14:33:36.000000000 +0000 @@ -19,9 +19,9 @@ function IterableWrapper(iterable) { var ret = {}; - ret[std_iterator] = function () { + ret[Symbol.iterator] = function () { log += 'i'; - return IteratorWrapper(iterable[std_iterator]()); + return IteratorWrapper(iterable[Symbol.iterator]()); } return ret; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-1.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-1.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-1.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-1.js 2015-02-03 14:33:36.000000000 +0000 @@ -12,7 +12,7 @@ } var iter = { next: next } var ret = {}; - ret[std_iterator] = function () { return iter; } + ret[Symbol.iterator] = function () { return iter; } return ret; } @@ -23,7 +23,7 @@ function collect_results(iterable) { var ret = []; var result; - var iter = iterable[std_iterator](); + var iter = iterable[Symbol.iterator](); do { result = iter.next(); ret.push(result); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-5.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-5.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-5.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-5.js 2015-02-03 14:33:36.000000000 +0000 @@ -8,7 +8,7 @@ } var iter = { next: next }; var ret = {}; - ret[std_iterator] = function () { return iter; } + ret[Symbol.iterator] = function () { return iter; } return ret; } @@ -19,7 +19,7 @@ function collect_results(iterable) { var ret = []; var result; - var iter = iterable[std_iterator](); + var iter = iterable[Symbol.iterator](); do { result = iter.next(); ret.push(result); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-6.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-6.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-6.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-6.js 2015-02-03 14:33:36.000000000 +0000 @@ -28,7 +28,7 @@ } this.next = next; - this[std_iterator] = iterator; + this[Symbol.iterator] = iterator; } function* delegate(iter) { return yield* iter; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-7.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-7.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-7.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/delegating-yield-7.js 2015-02-03 14:33:36.000000000 +0000 @@ -9,12 +9,12 @@ return results[i++]; } var ret = { next: next } - ret[std_iterator] = iterator; + ret[Symbol.iterator] = iterator; return ret; } function* yield_results(expected) { - return yield* Proxy(results(expected), {}); + return yield* new Proxy(results(expected), {}); } function collect_results(iter) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/runtime.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/runtime.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/runtime.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/runtime.js 2015-02-03 14:33:36.000000000 +0000 @@ -65,8 +65,6 @@ GeneratorObjectPrototype); var expected_property_names = ["next", "return", "throw", "constructor"]; - if (!JS_HAS_SYMBOLS) - expected_property_names.push(std_iterator); var found_property_names = Object.getOwnPropertyNames(GeneratorObjectPrototype); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/shell.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/shell.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/shell.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/shell.js 2015-02-03 14:33:36.000000000 +0000 @@ -2,17 +2,6 @@ * 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/. */ - -var std_iterator = (function() { - try { - for (var _ of new Proxy({}, { get: function(_, name) { throw name; } })) - break; - } catch (name) { - return name; - } - throw 'wat'; -})(); - function assertFalse(a) { assertEq(a, false) } function assertTrue(a) { assertEq(a, true) } function assertNotEq(found, not_expected) { assertFalse(found === expected) } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/syntax.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/syntax.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Generators/syntax.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Generators/syntax.js 2015-02-03 14:33:36.000000000 +0000 @@ -34,7 +34,7 @@ function* g() { (yield) } function* g() { [yield] } function* g() { {yield} } -function* g() { yield, yield } +function* g() { (yield), (yield) } function* g() { yield; yield } function* g() { (yield) ? yield : yield } function* g() { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Symbol/property-reflection.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Symbol/property-reflection.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/Symbol/property-reflection.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/Symbol/property-reflection.js 2015-02-03 14:33:36.000000000 +0000 @@ -66,8 +66,8 @@ assertEq(descs.hasOwnProperty(s1), true); assertEq(descs.hasOwnProperty(s2), true); assertEq(descs.hasOwnProperty(s3), false); - assertEq([].hasOwnProperty(std_iterator), false); - assertEq(Array.prototype.hasOwnProperty(std_iterator), true); + assertEq([].hasOwnProperty(Symbol.iterator), false); + assertEq(Array.prototype.hasOwnProperty(Symbol.iterator), true); // Object.prototype.propertyIsEnumerable assertEq(n.propertyIsEnumerable(s1), true); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/entries.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/entries.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/entries.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/entries.js 2015-02-03 14:33:36.000000000 +0000 @@ -35,14 +35,13 @@ } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.entries.call(invalidReceiver); }, TypeError, "Assert that entries fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.entries.call(new Proxy(new constructor(), {})); } if (typeof reportCompare === "function") diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/every-and-some.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/every-and-some.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/every-and-some.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/every-and-some.js 2015-02-03 14:33:36.000000000 +0000 @@ -111,14 +111,13 @@ } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.every.call(invalidReceiver, () => true); }, TypeError, "Assert that every fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.every.call(new Proxy(new constructor(), {}), () => true); // Test that the length getter is never called. assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", { @@ -237,14 +236,13 @@ } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.some.call(invalidReceiver, () => true); }, TypeError, "Assert that some fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.some.call(new Proxy(new constructor(), {}), () => false); // Test that the length getter is never called. assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", { @@ -258,4 +256,4 @@ assertEq(new Float64Array([undefined, , NaN]).some(v => v === v), false); if (typeof reportCompare === "function") - reportCompare(true, true); \ No newline at end of file + reportCompare(true, true); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/fill.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/fill.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/fill.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/fill.js 2015-02-03 14:33:36.000000000 +0000 @@ -53,14 +53,13 @@ } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./] + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.fill.call(invalidReceiver, 1); }, TypeError); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.fill.call(new Proxy(new constructor(), {})); // Test that the length getter is never called. Object.defineProperty(new constructor([1, 2, 3]), "length", { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/forEach.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/forEach.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/forEach.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/forEach.js 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,105 @@ +const constructors = [ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array +]; + +// Tests for TypedArray#forEach +for (var constructor of constructors) { + assertEq(constructor.prototype.forEach.length, 1); + + var arr = new constructor([1, 2, 3, 4, 5]); + // Tests for `thisArg` argument. + function assertThisArg(thisArg, thisValue) { + // In sloppy mode, `this` could be global object or a wrapper of `thisArg`. + arr.forEach(function() { + assertDeepEq(this, thisValue); + return false; + }, thisArg); + + // In strict mode, `this` strictly equals `thisArg`. + arr.forEach(function() { + "use strict"; + assertDeepEq(this, thisArg); + return false; + }, thisArg); + + // Passing `thisArg` has no effect if callback is an arrow function. + var self = this; + arr.forEach(() => { + assertEq(this, self); + return false; + }, thisArg); + } + assertThisArg([1, 2, 3], [1, 2, 3]); + assertThisArg(Object, Object); + assertThisArg(1, Object(1)); + assertThisArg("1", Object("1")); + assertThisArg(false, Object(false)); + assertThisArg(undefined, this); + assertThisArg(null, this); + + // Throw an exception in the callback. + var sum = 0; + var count = 0; + var thrown = false; + try { + assertEq(arr.forEach((v) => { + count++; + sum += v; + if (v === 3) { + throw "forEach"; + } + }), undefined) + } catch(e) { + assertEq(e, "forEach"); + thrown = true; + } + assertEq(thrown, true); + assertEq(sum, 6); + assertEq(count, 3); + + // There is no callback or callback is not a function. + assertThrowsInstanceOf(() => { + arr.forEach(); + }, TypeError); + var invalidCallbacks = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + invalidCallbacks.forEach(callback => { + assertThrowsInstanceOf(() => { + arr.forEach(callback); + }, TypeError); + }) + + // Callback is a generator. + arr.forEach(function*(){ + throw "This line will not be executed"; + }); + + // Called from other globals. + if (typeof newGlobal === "function") { + var forEach = newGlobal()[constructor.name].prototype.forEach; + var sum = 0; + forEach.call(new constructor([1, 2, 3]), v => { + sum += v; + }); + assertEq(sum, 6); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.forEach.call(invalidReceiver, () => true); + }, TypeError, "Assert that some fails if this value is not a TypedArray"); + }); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/includes.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/includes.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/includes.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/includes.js 2015-02-03 14:33:36.000000000 +0000 @@ -35,14 +35,13 @@ } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.includes.call(invalidReceiver); }, TypeError, "Assert that reverse fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.includes.call(new Proxy(new constructor(), {})); // Test that the length getter is never called. assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/indexOf-and-lastIndexOf.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/indexOf-and-lastIndexOf.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/indexOf-and-lastIndexOf.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/indexOf-and-lastIndexOf.js 2015-02-03 14:33:36.000000000 +0000 @@ -41,14 +41,13 @@ assertEq(new constructor([1, 2, 1, 2, 1]).indexOf(1, -2), 4); // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.indexOf.call(invalidReceiver); }, TypeError, "Assert that indexOf fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.indexOf.call(new Proxy(new constructor(), {})); // test that this.length is never called assertEq(Object.defineProperty(new constructor([0, 1, 2, 3, 5]), "length", { @@ -94,14 +93,13 @@ assertEq(new constructor([1, 2, 1, 2, 1]).lastIndexOf(1, -2), 2); // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.lastIndexOf.call(invalidReceiver); }, TypeError, "Assert that lastIndexOf fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.lastIndexOf.call(new Proxy(new constructor(), {})); // Test that the length getter is never called. assertEq(Object.defineProperty(new constructor([0, 1, 2, 3, 5]), "length", { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/join.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/join.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/join.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/join.js 2015-02-03 14:33:36.000000000 +0000 @@ -36,14 +36,13 @@ } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.join.call(invalidReceiver); }, TypeError, "Assert that join fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.join.call(new Proxy(new constructor(), {})); // Test that the length getter is never called. assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/keys.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/keys.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/keys.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/keys.js 2015-02-03 14:33:36.000000000 +0000 @@ -35,14 +35,13 @@ } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.keys.call(invalidReceiver); }, TypeError, "Assert that keys fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.keys.call(new Proxy(new constructor(), {})); } if (typeof reportCompare === "function") diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/object-defineproperty.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/object-defineproperty.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/object-defineproperty.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/object-defineproperty.js 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,80 @@ +var obj = new Int32Array(2); +obj[0] = 100; + +var throws = [ + // Disallow accessors + {get: undefined}, + {set: undefined}, + {get: undefined, set: undefined}, + {get: function() {}}, + {set: function() {}}, + {get: function() {}, set: function() {}}, + + {configurable: true}, + {enumerable: false}, + {writable: false}, + + {configurable: true, writable: true}, + {enumerable: false, configurable: false}, + + {configurable: true, value: 15} +]; + +for (var desc of throws) { + assertThrowsInstanceOf(function() { Object.defineProperty(obj, 0, desc); }, TypeError); + assertThrowsInstanceOf(function() { Object.defineProperties(obj, {0: desc}); }, TypeError); +} + +Object.defineProperty(obj, 0, {}); +Object.defineProperty(obj, 0, {configurable: false}); +Object.defineProperty(obj, 0, {enumerable: true}); +Object.defineProperty(obj, 0, {writable: true}); + +assertEq(obj[0], 100); + +Object.defineProperty(obj, 0, {configurable: false, value: 15}); +assertEq(obj[0], 15); +Object.defineProperty(obj, 0, {enumerable: true, value: 16}); +assertEq(obj[0], 16); +Object.defineProperty(obj, 0, {writable: true, value: 17}); +assertEq(obj[0], 17); +Object.defineProperty(obj, 0, {value: 18}); +assertEq(obj[0], 18); + +var desc = Object.getOwnPropertyDescriptor(obj, 0); +assertEq(desc.configurable, false); +assertEq(desc.enumerable, true); +assertEq(desc.writable, true); +assertEq(desc.value, 18); +assertEq('get' in desc, false); +assertEq('set' in desc, false); + +Object.defineProperties(obj, {0: {value: 20}, 1: {value: 42}}); +assertEq(obj[0], 20); +assertEq(obj[1], 42); + +const constructors = [ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array +]; + +constructors.forEach(constructor => { + var obj = new constructor(4); + obj[0] = 100; + obj[1] = 200; + + for (var v of [20, 300, -10, Math.pow(2, 32), -Math.pow(2, 32), NaN]) { + Object.defineProperty(obj, 0, {value: v}); + obj[1] = v; + assertEq(obj[0], obj[1]); + } +}); + +reportCompare(true, true, "test"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/reduce-and-reduceRight.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/reduce-and-reduceRight.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/reduce-and-reduceRight.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/reduce-and-reduceRight.js 2015-02-03 14:33:36.000000000 +0000 @@ -87,14 +87,13 @@ } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(3), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.reduce.call(invalidReceiver, () => {}); }, TypeError, "Assert that reduce fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.reduce.call(new Proxy(new constructor(3), {}), () => {}); // Test that the length getter is never called. assertEq(Object.defineProperty(arr, "length", { @@ -181,14 +180,13 @@ } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(3), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.reduceRight.call(invalidReceiver, () => {}); }, TypeError, "Assert that reduceRight fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.reduceRight.call(new Proxy(new constructor(3), {}), () => {}); // Test that the length getter is never called. assertEq(Object.defineProperty(arr, "length", { @@ -199,4 +197,4 @@ } if (typeof reportCompare === "function") - reportCompare(true, true); \ No newline at end of file + reportCompare(true, true); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/reverse.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/reverse.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/reverse.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/reverse.js 2015-02-03 14:33:36.000000000 +0000 @@ -30,14 +30,13 @@ } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.reverse.call(invalidReceiver); }, TypeError, "Assert that reverse fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.reverse.call(new Proxy(new constructor(), {})); // Test that the length getter is never called. Object.defineProperty(new constructor([1, 2, 3]), "length", { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/values.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/values.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/ecma_6/TypedArray/values.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/ecma_6/TypedArray/values.js 2015-02-03 14:33:36.000000000 +0000 @@ -36,14 +36,13 @@ } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.values.call(invalidReceiver); }, TypeError, "Assert that values fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.values.call(new Proxy(new constructor(), {})); } if (typeof reportCompare === "function") diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/js1_8/regress/regress-469625-03.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/js1_8/regress/regress-469625-03.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/js1_8/regress/regress-469625-03.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/js1_8/regress/regress-469625-03.js 2015-02-03 14:33:38.000000000 +0000 @@ -26,8 +26,7 @@ var [a, b, [c0, c1]] = [x, x, x]; } - var ITERATOR = JS_HAS_SYMBOLS ? "Symbol.iterator" : "'@@iterator'"; - expect = `TypeError: (intermediate value)[${ITERATOR}](...).next(...).value is null`; + expect = `TypeError: (intermediate value)[Symbol.iterator](...).next(...).value is null`; actual = 'No Error'; try { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/js1_8_5/extensions/decompile-for-of.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/js1_8_5/extensions/decompile-for-of.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/js1_8_5/extensions/decompile-for-of.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/js1_8_5/extensions/decompile-for-of.js 2015-02-03 14:33:38.000000000 +0000 @@ -9,8 +9,7 @@ s = exc.message; } - var ITERATOR = JS_HAS_SYMBOLS ? "Symbol.iterator" : "'@@iterator'"; - assertEq(s, `x[${ITERATOR}] is not a function`); + assertEq(s, `x[Symbol.iterator] is not a function`); } x = {}; @@ -18,10 +17,10 @@ check("[...x]"); check("Math.hypot(...x)"); -x[std_iterator] = "potato"; +x[Symbol.iterator] = "potato"; check("for (var v of x) throw fit;"); -x[std_iterator] = {}; +x[Symbol.iterator] = {}; check("for (var v of x) throw fit;"); if (typeof reportCompare === "function") diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/js1_8_5/extensions/reflect-parse.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/js1_8_5/extensions/reflect-parse.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/js1_8_5/extensions/reflect-parse.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/js1_8_5/extensions/reflect-parse.js 2015-02-03 14:33:38.000000000 +0000 @@ -389,7 +389,6 @@ { type: "Property", key: ident("__proto__"), method: true }, { type: "PrototypeMutation", value: lit(null) }])); - // Bug 571617: eliminate constant-folding assertExpr("2 + 3", binExpr("+", lit(2), lit(3))); @@ -435,6 +434,25 @@ objExpr([{ key: computedName(ident("meth")), value: funExpr(null, [ident("a")], blockStmt([])), method: false, kind: "set"}]))); +// Bug 1073809 - Getter/setter syntax with generators +assertExpr("({*get() { }})", objExpr([{ type: "Property", key: ident("get"), method: true, + value: genFunExpr(ident("get"), [], blockStmt([]))}])); +assertExpr("({*set() { }})", objExpr([{ type: "Property", key: ident("set"), method: true, + value: genFunExpr(ident("set"), [], blockStmt([]))}])); +assertError("({*get foo() { }})", SyntaxError); +assertError("({*set foo() { }})", SyntaxError); + +assertError("({ *get 1() {} })", SyntaxError); +assertError("({ *get \"\"() {} })", SyntaxError); +assertError("({ *get foo() {} })", SyntaxError); +assertError("({ *get []() {} })", SyntaxError); +assertError("({ *get [1]() {} })", SyntaxError); + +assertError("({ *set 1() {} })", SyntaxError); +assertError("({ *set \"\"() {} })", SyntaxError); +assertError("({ *set foo() {} })", SyntaxError); +assertError("({ *set []() {} })", SyntaxError); +assertError("({ *set [1]() {} })", SyntaxError); // statements assertStmt("throw 42", throwStmt(lit(42))); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/jstests.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/jstests.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/jstests.py 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/jstests.py 2015-02-03 14:33:39.000000000 +0000 @@ -17,15 +17,14 @@ from lib.results import ResultsSink from lib.progressbar import ProgressBar -if (sys.platform.startswith('linux') or - sys.platform.startswith('darwin') - ): +if sys.platform.startswith('linux') or sys.platform.startswith('darwin'): from lib.tasks_unix import run_all_tests else: from lib.tasks_win import run_all_tests def run_tests(options, tests, results): - """Run the given tests, sending raw results to the given results accumulator.""" + """Run the given tests, sending raw results to the given results + accumulator.""" try: completed = run_all_tests(tests, results, options) except KeyboardInterrupt: @@ -42,7 +41,7 @@ try: import multiprocessing return multiprocessing.cpu_count() - except (ImportError,NotImplementedError): + except (ImportError, NotImplementedError): pass # POSIX @@ -50,7 +49,7 @@ res = int(os.sysconf('SC_NPROCESSORS_ONLN')) if res > 0: return res - except (AttributeError,ValueError): + except (AttributeError, ValueError): pass # Windows @@ -79,24 +78,38 @@ Shell output format: [ pass | fail | timeout | skip ] progress | time """).strip()) op.add_option('--xul-info', dest='xul_info_src', - help='config data for xulRuntime (avoids search for config/autoconf.mk)') + help='config data for xulRuntime' + ' (avoids search for config/autoconf.mk)') - harness_og = OptionGroup(op, "Harness Controls", "Control how tests are run.") - harness_og.add_option('-j', '--worker-count', type=int, default=max(1, get_cpu_count()), - help='Number of tests to run in parallel (default %default)') + harness_og = OptionGroup(op, "Harness Controls", + "Control how tests are run.") + harness_og.add_option('-j', '--worker-count', type=int, + default=max(1, get_cpu_count()), + help='Number of tests to run in parallel' + ' (default %default)') harness_og.add_option('-t', '--timeout', type=float, default=150.0, - help='Set maximum time a test is allows to run (in seconds).') + help='Set maximum time a test is allows to run' + ' (in seconds).') harness_og.add_option('-a', '--args', dest='shell_args', default='', help='Extra args to pass to the JS shell.') - harness_og.add_option('--jitflags', default='', help="Obsolete. Does nothing.") + harness_og.add_option('--jitflags', default='', + help="Obsolete. Does nothing.") harness_og.add_option('--tbpl', action='store_true', - help='Runs each test in all configurations tbpl tests.') - harness_og.add_option('-g', '--debug', action='store_true', help='Run a test in debugger.') - harness_og.add_option('--debugger', default='gdb -q --args', help='Debugger command.') - harness_og.add_option('-J', '--jorendb', action='store_true', help='Run under JS debugger.') - harness_og.add_option('--passthrough', action='store_true', help='Run tests with stdin/stdout attached to caller.') - harness_og.add_option('--valgrind', action='store_true', help='Run tests in valgrind.') - harness_og.add_option('--valgrind-args', default='', help='Extra args to pass to valgrind.') + help='Runs each test in all configurations tbpl' + ' tests.') + harness_og.add_option('-g', '--debug', action='store_true', + help='Run a test in debugger.') + harness_og.add_option('--debugger', default='gdb -q --args', + help='Debugger command.') + harness_og.add_option('-J', '--jorendb', action='store_true', + help='Run under JS debugger.') + harness_og.add_option('--passthrough', action='store_true', + help='Run tests with stdin/stdout attached to' + ' caller.') + harness_og.add_option('--valgrind', action='store_true', + help='Run tests in valgrind.') + harness_og.add_option('--valgrind-args', default='', + help='Extra args to pass to valgrind.') op.add_option_group(harness_og) input_og = OptionGroup(op, "Inputs", "Change what tests are run.") @@ -104,36 +117,47 @@ help='Get tests from the given file.') input_og.add_option('-x', '--exclude-file', action='append', help='Exclude tests from the given file.') - input_og.add_option('-d', '--exclude-random', dest='random', action='store_false', + input_og.add_option('-d', '--exclude-random', dest='random', + action='store_false', help='Exclude tests marked as "random."') - input_og.add_option('--run-skipped', action='store_true', help='Run tests marked as "skip."') - input_og.add_option('--run-only-skipped', action='store_true', help='Run only tests marked as "skip."') + input_og.add_option('--run-skipped', action='store_true', + help='Run tests marked as "skip."') + input_og.add_option('--run-only-skipped', action='store_true', + help='Run only tests marked as "skip."') input_og.add_option('--run-slow-tests', action='store_true', help='Do not skip tests marked as "slow."') input_og.add_option('--no-extensions', action='store_true', - help='Run only tests conforming to the ECMAScript 5 standard.') + help='Run only tests conforming to the ECMAScript 5' + ' standard.') op.add_option_group(input_og) - output_og = OptionGroup(op, "Output", "Modify the harness and tests output.") + output_og = OptionGroup(op, "Output", + "Modify the harness and tests output.") output_og.add_option('-s', '--show-cmd', action='store_true', help='Show exact commandline used to run each test.') output_og.add_option('-o', '--show-output', action='store_true', - help="Print each test's output to the file given by --output-file.") + help="Print each test's output to the file given by" + " --output-file.") output_og.add_option('-F', '--failed-only', action='store_true', - help="If a --show-* option is given, only print output for failed tests.") + help="If a --show-* option is given, only print" + " output for failed tests.") output_og.add_option('--no-show-failed', action='store_true', - help="Don't print output for failed tests (no-op with --show-output).") + help="Don't print output for failed tests" + " (no-op with --show-output).") output_og.add_option('-O', '--output-file', - help='Write all output to the given file (default: stdout).') + help='Write all output to the given file' + ' (default: stdout).') output_og.add_option('--failure-file', help='Write all not-passed tests to the given file.') - output_og.add_option('--no-progress', dest='hide_progress', action='store_true', + output_og.add_option('--no-progress', dest='hide_progress', + action='store_true', help='Do not show the progress bar.') output_og.add_option('--tinderbox', action='store_true', help='Use tinderbox-parseable output format.') op.add_option_group(output_og) - special_og = OptionGroup(op, "Special", "Special modes that do not run tests.") + special_og = OptionGroup(op, "Special", + "Special modes that do not run tests.") special_og.add_option('--make-manifests', metavar='BASE_TEST_PATH', help='Generate reftest manifest files.') op.add_option_group(special_og) @@ -167,15 +191,18 @@ options.passthrough = True options.hide_progress = True options.worker_count = 1 - debugger_path = realpath(os.path.join(abspath(dirname(abspath(__file__))), '..', '..', 'examples', 'jorendb.js')) - js_cmd_args.extend([ '-d', '-f', debugger_path, '--' ]) + debugger_path = realpath(os.path.join( + abspath(dirname(abspath(__file__))), + '..', '..', 'examples', 'jorendb.js')) + js_cmd_args.extend(['-d', '-f', debugger_path, '--']) TestCase.set_js_cmd_prefix(options.js_shell, js_cmd_args, prefix) # If files with lists of tests to run were specified, add them to the # requested tests set. if options.test_file: for test_file in options.test_file: - requested_paths |= set([line.strip() for line in open(test_file).readlines()]) + requested_paths |= set( + [line.strip() for line in open(test_file).readlines()]) # If files with lists of tests to exclude were specified, add them to the # excluded tests set. @@ -229,7 +256,8 @@ xul_tester = manifest.XULInfoTester(xul_info, options.js_shell) test_dir = dirname(abspath(__file__)) - test_list = manifest.load(test_dir, requested_paths, excluded_paths, xul_tester) + test_list = manifest.load(test_dir, requested_paths, excluded_paths, + xul_tester) skip_list = [] if options.make_manifests: @@ -249,31 +277,33 @@ test_list = new_test_list if options.jitflags: - print("Warning: the --jitflags option is obsolete and does nothing now.") + print("Warning: the --jitflags option is obsolete and does nothing" + " now.") if options.test_file: paths = set() for test_file in options.test_file: - paths |= set([ line.strip() for line in open(test_file).readlines()]) - test_list = [ _ for _ in test_list if _.path in paths ] + paths |= set( + [line.strip() for line in open(test_file).readlines()]) + test_list = [_ for _ in test_list if _.path in paths] if options.no_extensions: pattern = os.sep + 'extensions' + os.sep test_list = [_ for _ in test_list if pattern not in _.path] if not options.random: - test_list = [ _ for _ in test_list if not _.random ] + test_list = [_ for _ in test_list if not _.random] if options.run_only_skipped: options.run_skipped = True - test_list = [ _ for _ in test_list if not _.enable ] + test_list = [_ for _ in test_list if not _.enable] if not options.run_slow_tests: - test_list = [ _ for _ in test_list if not _.slow ] + test_list = [_ for _ in test_list if not _.slow] if not options.run_skipped: - skip_list = [ _ for _ in test_list if not _.enable ] - test_list = [ _ for _ in test_list if _.enable ] + skip_list = [_ for _ in test_list if not _.enable] + test_list = [_ for _ in test_list if _.enable] return skip_list, test_list @@ -292,9 +322,10 @@ if options.debug: if len(test_list) > 1: - print('Multiple tests match command line arguments, debugger can only run one') + print('Multiple tests match command line arguments,' + ' debugger can only run one') for tc in test_list: - print(' %s'%tc.path) + print(' {}'.format(tc.path)) return 2 cmd = test_list[0].get_command(TestCase.js_cmd_prefix) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/jittests.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/jittests.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/jittests.py 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/jittests.py 2015-02-03 14:33:39.000000000 +0000 @@ -108,12 +108,14 @@ self.jitflags = [] # jit flags to enable self.slow = False # True means the test is slow-running self.allow_oom = False # True means that OOM is not considered a failure - self.allow_unhandlable_oom = False # True means CrashAtUnhandlableOOM is not considered a failure + self.allow_unhandlable_oom = False # True means CrashAtUnhandlableOOM + # is not considered a failure self.allow_overrecursed = False # True means that hitting recursion the # limits is not considered a failure. self.valgrind = False # True means run under valgrind self.tz_pacific = False # True means force Pacific time for the test - self.test_also_noasmjs = False # True means run with and without asm.js enabled. + self.test_also_noasmjs = False # True means run with and without asm.js + # enabled. self.expect_error = '' # Errors to expect and consider passing self.expect_status = 0 # Exit status to expect from shell @@ -137,15 +139,15 @@ return t def copy_variants(self, variants): - # If the tests are flagged with the |jit-test| test-also-noasmjs flags, then - # we duplicate the variants such that the test can be used both with the - # interpreter and asmjs. This is a simple way to check for differential - # behaviour. + # If the tests are flagged with the |jit-test| test-also-noasmjs flags, + # then we duplicate the variants such that the test can be used both + # with the interpreter and asmjs. This is a simple way to check for + # differential behaviour. if self.test_also_noasmjs: variants = variants + [['--no-asmjs']] # For each list of jit flags, make a copy of the test. - return [ self.copy_and_extend_jitflags(v) for v in variants ] + return [self.copy_and_extend_jitflags(v) for v in variants] COOKIE = '|jit-test|' @@ -171,16 +173,20 @@ test.expect_error = value elif name == 'exitstatus': try: - test.expect_status = int(value, 0); + test.expect_status = int(value, 0) except ValueError: - print("warning: couldn't parse exit status %s" % value) + print("warning: couldn't parse exit status" + " {}".format(value)) elif name == 'thread-count': try: - test.jitflags.append('--thread-count=' + int(value, 0)); + test.jitflags.append('--thread-count={}'.format( + int(value, 0))) except ValueError: - print("warning: couldn't parse thread-count %s" % value) + print("warning: couldn't parse thread-count" + " {}".format(value)) else: - print('%s: warning: unrecognized |jit-test| attribute %s' % (path, part)) + print('{}: warning: unrecognized |jit-test| attribute' + ' {}'.format(path, part)) else: if name == 'slow': test.slow = True @@ -200,10 +206,12 @@ test.jitflags.append('--ion-eager') elif name == 'dump-bytecode': test.jitflags.append('--dump-bytecode') - elif name.startswith('--'): # // |jit-test| --ion-gvn=off; --no-sse4 + elif name.startswith('--'): + # // |jit-test| --ion-gvn=off; --no-sse4 test.jitflags.append(name) else: - print('%s: warning: unrecognized |jit-test| attribute %s' % (path, part)) + print('{}: warning: unrecognized |jit-test| attribute' + ' {}'.format(path, part)) if options.valgrind_all: test.valgrind = True @@ -215,7 +223,7 @@ if remote_prefix: path = self.path.replace(TEST_DIR, remote_prefix) - scriptdir_var = os.path.dirname(path); + scriptdir_var = os.path.dirname(path) if not scriptdir_var.endswith('/'): scriptdir_var += '/' @@ -227,10 +235,10 @@ quotechar = '"' else: quotechar = "'" - expr = ("const platform=%s; const libdir=%s; const scriptdir=%s" - % (js_quote(quotechar, sys.platform), - js_quote(quotechar, libdir), - js_quote(quotechar, scriptdir_var))) + expr = "const platform={}; const libdir={}; const scriptdir={}".format( + js_quote(quotechar, sys.platform), + js_quote(quotechar, libdir), + js_quote(quotechar, scriptdir_var)) # We may have specified '-a' or '-d' twice: once via --jitflags, once # via the "|jit-test|" line. Remove dups because they are toggles. @@ -253,7 +261,8 @@ if filename in ('shell.js', 'browser.js', 'jsref.js'): continue test = os.path.join(dirpath, filename) - if substring is None or substring in os.path.relpath(test, TEST_DIR): + if substring is None \ + or substring in os.path.relpath(test, TEST_DIR): ans.append(test) return ans @@ -280,7 +289,7 @@ l[1] = (out, err, p.returncode) def run_timeout_cmd(cmdline, options, timeout=60.0): - l = [ None, None ] + l = [None, None] timed_out = False th = Thread(target=th_run_cmd, args=(cmdline, options, l)) @@ -290,17 +299,18 @@ # when we are signaled to exit. sigint_handler = signal.getsignal(signal.SIGINT) sigterm_handler = signal.getsignal(signal.SIGTERM) - if (sigint_handler == signal.SIG_IGN): + if sigint_handler == signal.SIG_IGN: def handleChildSignal(sig, frame): try: if sys.platform != 'win32': os.kill(l[0].pid, signal.SIGKILL) else: import ctypes - ctypes.windll.kernel32.TerminateProcess(int(l[0]._handle), -1) + ctypes.windll.kernel32.TerminateProcess(int(l[0]._handle), + -1) except OSError: pass - if (sig == signal.SIGTERM): + if sig == signal.SIGTERM: sys.exit(0) signal.signal(signal.SIGINT, handleChildSignal) signal.signal(signal.SIGTERM, handleChildSignal) @@ -315,7 +325,8 @@ os.kill(l[0].pid, signal.SIGKILL) else: import ctypes - ctypes.windll.kernel32.TerminateProcess(int(l[0]._handle), -1) + ctypes.windll.kernel32.TerminateProcess(int(l[0]._handle), + -1) time.sleep(.1) timed_out = True except OSError: @@ -324,7 +335,7 @@ th.join() # Restore old signal handlers - if (sigint_handler == signal.SIG_IGN): + if sigint_handler == signal.SIG_IGN: signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGTERM, sigterm_handler) @@ -333,13 +344,13 @@ return (out, err, code, timed_out) def run_cmd(cmdline, env, timeout): - return run_timeout_cmd(cmdline, { 'env': env }, timeout) + return run_timeout_cmd(cmdline, {'env': env}, timeout) def run_cmd_avoid_stdio(cmdline, env, timeout): stdoutPath, stderrPath = tmppath('jsstdout'), tmppath('jsstderr') env['JS_STDOUT'] = stdoutPath env['JS_STDERR'] = stderrPath - _, __, code = run_timeout_cmd(cmdline, { 'env': env }, timeout) + _, __, code = run_timeout_cmd(cmdline, {'env': env}, timeout) return read_and_unlink(stdoutPath), read_and_unlink(stderrPath), code def run_test(test, prefix, options): @@ -367,7 +378,7 @@ if pathvar: bin_dir = os.path.dirname(cmd[0]) if pathvar in env: - env[pathvar] = '%s%s%s' % (bin_dir, os.pathsep, env[pathvar]) + env[pathvar] = '{}{}{}'.format(bin_dir, os.pathsep, env[pathvar]) else: env[pathvar] = bin_dir @@ -375,7 +386,9 @@ return TestOutput(test, cmd, out, err, code, None, timed_out) def run_test_remote(test, device, prefix, options): - cmd = test.command(prefix, posixpath.join(options.remote_test_root, 'lib/'), posixpath.join(options.remote_test_root, 'tests')) + cmd = test.command(prefix, + posixpath.join(options.remote_test_root, 'lib/'), + posixpath.join(options.remote_test_root, 'tests')) if options.show_cmd: print(subprocess.list2cmdline(cmd)) @@ -445,12 +458,14 @@ # Allow a non-zero exit code if we want to allow unhandlable OOM, but # only if we actually got unhandlable OOM. - if test.allow_unhandlable_oom and 'Assertion failure: [unhandlable oom]' in err: + if test.allow_unhandlable_oom \ + and 'Assertion failure: [unhandlable oom]' in err: return True # Allow a non-zero exit code if we want to all too-much-recursion and # the test actually over-recursed. - if test.allow_overrecursed and 'too much recursion' in err and 'Assertion failure' not in err: + if test.allow_overrecursed and 'too much recursion' in err \ + and 'Assertion failure' not in err: return True return False @@ -475,7 +490,7 @@ message = "Success" if ok else res.describe_failure() jitflags = " ".join(res.test.jitflags) print("{} | {} | {} (code {}, args \"{}\")".format( - result, res.test.relpath_top, message, res.rc, jitflags)) + result, res.test.relpath_top, message, res.rc, jitflags)) # For failed tests, print as much information as we have, to aid debugging. if ok: @@ -512,7 +527,8 @@ total_tests = len(tests) * options.repeat result_process_return_queue = queue_manager.Queue() result_process = Process(target=process_test_results_parallel, - args=(async_test_result_queue, result_process_return_queue, + args=(async_test_result_queue, + result_process_return_queue, notify_queue, total_tests, options)) result_process.start() @@ -542,10 +558,13 @@ # For every item in the notify queue, start one new worker. # Every completed worker adds a new item to this queue. while notify_queue.get(): - if (testcnt < total_tests): + if testcnt < total_tests: # Start one new worker test = tests[testcnt % len(tests)] - worker_process = Process(target=wrap_parallel_run_test, args=(test, prefix, async_test_result_queue, options)) + worker_process = Process(target=wrap_parallel_run_test, + args=(test, prefix, + async_test_result_queue, + options)) worker_processes.append(worker_process) worker_process.start() testcnt += 1 @@ -559,7 +578,8 @@ while len(worker_processes) > 0: worker_processes = remove_completed_workers(worker_processes) - # Signal completion to result processor, then wait for it to complete on its own + # Signal completion to result processor, then wait for it to complete + # on its own async_test_result_queue.put(None) result_process.join() @@ -568,7 +588,7 @@ except (Exception, KeyboardInterrupt) as e: # Print the exception if it's not an interrupt, # might point to a bug or other faulty condition - if not isinstance(e,KeyboardInterrupt): + if not isinstance(e, KeyboardInterrupt): traceback.print_exc() for worker in worker_processes: @@ -586,7 +606,7 @@ async_test_result = async_test_result_queue.get() # Check if we are supposed to terminate - if (async_test_result == None): + if async_test_result == None: return # Notify parent that we got a result @@ -594,7 +614,8 @@ yield async_test_result -def process_test_results_parallel(async_test_result_queue, return_queue, notify_queue, num_tests, options): +def process_test_results_parallel(async_test_result_queue, return_queue, + notify_queue, num_tests, options): gen = get_parallel_results(async_test_result_queue, notify_queue) ok = process_test_results(gen, num_tests, options) return_queue.put(ok) @@ -604,11 +625,13 @@ if options.write_failures: try: out = open(options.write_failures, 'w') - # Don't write duplicate entries when we are doing multiple failures per job. + # Don't write duplicate entries when we are doing multiple + # failures per job. written = set() for res in failures: if res.test.path not in written: - out.write(os.path.relpath(res.test.path, TEST_DIR) + '\n') + out.write(os.path.relpath(res.test.path, TEST_DIR) + + '\n') if options.write_failure_output: out.write(res.out) out.write(res.err) @@ -616,8 +639,8 @@ written.add(res.test.path) out.close() except IOError: - sys.stderr.write("Exception thrown trying to write failure file '%s'\n"% - options.write_failures) + sys.stderr.write("Exception thrown trying to write failure" + " file '{}'\n".format(options.write_failures)) traceback.print_exc() sys.stderr.write('---\n') @@ -637,13 +660,15 @@ if res.timed_out: show_test(res) else: - print('PASSED ALL' + ('' if complete else ' (partial run -- interrupted by user %s)' % doing)) + print('PASSED ALL' + + ('' if complete + else ' (partial run -- interrupted by user {})'.format(doing))) if options.tinderbox: num_failures = len(failures) if failures else 0 print('Result summary:') - print('Passed: %d' % (num_tests - num_failures)) - print('Failed: %d' % num_failures) + print('Passed: {:d}'.format(num_tests - num_failures)) + print('Failed: {:d}'.format(num_failures)) return not failures @@ -659,7 +684,8 @@ complete = True return print_test_summary(num_tests, failures, complete, doing, options) - if not options.hide_progress and not options.show_cmd and ProgressBar.conservative_isatty(): + if not options.hide_progress and not options.show_cmd \ + and ProgressBar.conservative_isatty(): fmt = [ {'value': 'PASS', 'color': 'green'}, {'value': 'FAIL', 'color': 'red'}, @@ -670,7 +696,8 @@ try: for i, res in enumerate(results): - ok = check_output(res.out, res.err, res.rc, res.timed_out, res.test, options) + ok = check_output(res.out, res.err, res.rc, res.timed_out, + res.test, options) if ok: show_output = options.show_output and not options.failed_only @@ -681,20 +708,20 @@ pb.beginline() sys.stdout.write(res.out) sys.stdout.write(res.err) - sys.stdout.write('Exit code: %s\n' % res.rc) + sys.stdout.write('Exit code: {}\n'.format(res.rc)) if res.test.valgrind and not show_output: pb.beginline() sys.stdout.write(res.err) - doing = 'after %s' % res.test.relpath_tests + doing = 'after {}'.format(res.test.relpath_tests) if not ok: failures.append(res) if res.timed_out: - pb.message("TIMEOUT - %s" % res.test.relpath_tests) + pb.message("TIMEOUT - {}".format(res.test.relpath_tests)) timeouts += 1 else: - pb.message("FAIL - %s" % res.test.relpath_tests) + pb.message("FAIL - {}".format(res.test.relpath_tests)) if options.tinderbox: print_tinderbox(ok, res) @@ -704,8 +731,8 @@ 'PASS': n - len(failures), 'FAIL': len(failures), 'TIMEOUT': timeouts, - 'SKIP': 0} - ) + 'SKIP': 0 + }) complete = True except KeyboardInterrupt: print("TEST-UNEXPECTED-FAIL | jit_test.py" + @@ -735,12 +762,13 @@ # After a devicemanager error, the device is typically in a # state where all further tests will fail so there is no point in # continuing here. - sys.stderr.write("Error running remote tests: %s" % e.message) + sys.stderr.write("Error running remote tests: {}".format(e.message)) def push_libs(options, device): # This saves considerable time in pushing unnecessary libraries # to the device but needs to be updated if the dependencies change. - required_libs = ['libnss3.so', 'libmozglue.so', 'libnspr4.so', 'libplc4.so', 'libplds4.so'] + required_libs = ['libnss3.so', 'libmozglue.so', 'libnspr4.so', + 'libplc4.so', 'libplds4.so'] for file in os.listdir(options.local_lib): if file in required_libs: @@ -749,22 +777,33 @@ def push_progs(options, device, progs): for local_file in progs: - remote_file = posixpath.join(options.remote_test_root, os.path.basename(local_file)) + remote_file = posixpath.join(options.remote_test_root, + os.path.basename(local_file)) device.pushFile(local_file, remote_file) def run_tests_remote(tests, prefix, options): # Setup device with everything needed to run our tests. - from mozdevice import devicemanager, devicemanagerADB, devicemanagerSUT + from mozdevice import devicemanagerADB, devicemanagerSUT if options.device_transport == 'adb': if options.device_ip: - dm = devicemanagerADB.DeviceManagerADB(options.device_ip, options.device_port, deviceSerial=options.device_serial, packageName=None, deviceRoot=options.remote_test_root) + dm = devicemanagerADB.DeviceManagerADB( + options.device_ip, options.device_port, + deviceSerial=options.device_serial, + packageName=None, + deviceRoot=options.remote_test_root) else: - dm = devicemanagerADB.DeviceManagerADB(deviceSerial=options.device_serial, packageName=None, deviceRoot=options.remote_test_root) + dm = devicemanagerADB.DeviceManagerADB( + deviceSerial=options.device_serial, + packageName=None, + deviceRoot=options.remote_test_root) else: - dm = devicemanagerSUT.DeviceManagerSUT(options.device_ip, options.device_port, deviceRoot=options.remote_test_root) + dm = devicemanagerSUT.DeviceManagerSUT( + options.device_ip, options.device_port, + deviceRoot=options.remote_test_root) if options.device_ip == None: - print('Error: you must provide a device IP to connect to via the --device option') + print('Error: you must provide a device IP to connect to via the' + ' --device option') sys.exit(1) # Update the test root to point to our test directory. @@ -782,9 +821,11 @@ Test.CacheDir = posixpath.join(options.remote_test_root, '.js-cache') dm.mkDir(Test.CacheDir) - dm.pushDir(JS_TESTS_DIR, posixpath.join(jit_tests_dir, 'tests'), timeout=600) + dm.pushDir(JS_TESTS_DIR, posixpath.join(jit_tests_dir, 'tests'), + timeout=600) - dm.pushDir(os.path.dirname(TEST_DIR), options.remote_test_root, timeout=600) + dm.pushDir(os.path.dirname(TEST_DIR), options.remote_test_root, + timeout=600) prefix[0] = os.path.join(options.remote_test_root, 'js') # Run all tests. @@ -793,12 +834,12 @@ return ok def parse_jitflags(options): - jitflags = [ [ '-' + flag for flag in flags ] - for flags in options.jitflags.split(',') ] + jitflags = [['-' + flag for flag in flags] + for flags in options.jitflags.split(',')] for flags in jitflags: for flag in flags: if flag not in ('-m', '-a', '-p', '-d', '-n'): - print('Invalid jit flag: "%s"' % flag) + print('Invalid jit flag: "{}"'.format(flag)) sys.exit(1) return jitflags diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/manifest.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/manifest.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/manifest.py 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/manifest.py 2015-02-03 14:33:39.000000000 +0000 @@ -31,13 +31,14 @@ """Return JS that when executed sets up variables so that JS expression predicates on XUL build info evaluate properly.""" - return ('var xulRuntime = { OS: "%s", XPCOMABI: "%s", shell: true };' + - 'var isDebugBuild=%s; var Android=%s; var browserIsRemote=%s') % ( - self.os, - self.abi, - str(self.isdebug).lower(), - str(self.os == "Android").lower(), - str(self.browserIsRemote).lower()) + return ('var xulRuntime = {{ OS: "{}", XPCOMABI: "{}", shell: true }};' + 'var isDebugBuild={}; var Android={}; ' + 'var browserIsRemote={}'.format( + self.os, + self.abi, + str(self.isdebug).lower(), + str(self.os == "Android").lower(), + str(self.browserIsRemote).lower())) @classmethod def create(cls, jsdir): @@ -57,13 +58,13 @@ break if path == None: - print ("Can't find config/autoconf.mk on a directory containing the JS shell" - " (searched from %s)") % jsdir + print("Can't find config/autoconf.mk on a directory containing" + " the JS shell (searched from {})".format(jsdir)) sys.exit(1) # Read the values. val_re = re.compile(r'(TARGET_XPCOM_ABI|OS_TARGET|MOZ_DEBUG)\s*=\s*(.*)') - kw = { 'isdebug': False } + kw = {'isdebug': False} for line in open(path): m = val_re.match(line) if m: @@ -88,7 +89,11 @@ """Test a XUL predicate condition against this local info.""" ans = self.cache.get(cond, None) if ans is None: - cmd = [ self.js_bin, '-e', self.js_prolog, '-e', 'print(!!(%s))'%cond ] + cmd = [ + self.js_bin, + '-e', self.js_prolog, + '-e', 'print(!!({}))'.format(cond) + ] p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) out, err = p.communicate() if out in ('true\n', 'true\r\n'): @@ -96,9 +101,9 @@ elif out in ('false\n', 'false\r\n'): ans = False else: - raise Exception(("Failed to test XUL condition %r;" - + " output was %r, stderr was %r") - % (cond, out, err)) + raise Exception("Failed to test XUL condition {!r};" + " output was {!r}, stderr was {!r}".format( + cond, out, err)) self.cache[cond] = ans return ans @@ -148,7 +153,8 @@ testcase.expect = testcase.enable = False pos += 1 else: - print('warning: invalid manifest line element "%s"'%parts[pos]) + print('warning: invalid manifest line element "{}"'.format( + parts[pos])) pos += 1 def _build_manifest_script_entry(script_name, test): @@ -208,8 +214,8 @@ # If we have tests, we have to set the url-prefix so reftest can find them. if numTestFiles > 0: - manifest = (["url-prefix %sjsreftest.html?test=%s/" % ('../' * depth, relative)] - + manifest) + manifest = ["url-prefix {}jsreftest.html?test={}/".format( + '../' * depth, relative)] + manifest fp = open(filename, 'w') try: @@ -279,7 +285,8 @@ continue matches = manifest_re.match(line) if not matches: - print('warning: unrecognized line in jstests.list: {0}'.format(line)) + print('warning: unrecognized line in jstests.list:' + ' {0}'.format(line)) continue path = os.path.normpath(os.path.join(relpath, matches.group(3))) @@ -291,9 +298,11 @@ assert(path.endswith('jstests.list')) path = path[:-len('jstests.list')] - entries.append({'path': path, 'terms': matches.group(1), 'comment': comment.strip()}) + entries.append({'path': path, 'terms': matches.group(1), + 'comment': comment.strip()}) - # if one directory name is a prefix of another, we want the shorter one first + # if one directory name is a prefix of another, we want the shorter one + # first entries.sort(key=lambda x: x["path"]) return entries @@ -314,7 +323,7 @@ testcase.comment = entry["comment"] _parse_one(testcase, xul_tester) -def load(location, requested_paths, excluded_paths, xul_tester, reldir = ''): +def load(location, requested_paths, excluded_paths, xul_tester, reldir=''): """ Locates all tests by walking the filesystem starting at |location|. Uses xul_tester to evaluate any test conditions in the test header. @@ -351,7 +360,8 @@ fullpath = os.path.join(location, filename) # If any tests are requested by name, skip tests that do not match. - if requested_paths and not any(req in filename for req in requested_paths): + if requested_paths \ + and not any(req in filename for req in requested_paths): continue # Skip excluded tests. @@ -364,7 +374,8 @@ continue testcase = TestCase(os.path.join(reldir, filename)) - _apply_external_manifests(filename, testcase, externalManifestEntries, xul_tester) + _apply_external_manifests(filename, testcase, externalManifestEntries, + xul_tester) _parse_test_header(fullpath, testcase, xul_tester) tests.append(testcase) return tests diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/progressbar.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/progressbar.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/progressbar.py 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/progressbar.py 2015-02-03 14:33:39.000000000 +0000 @@ -49,7 +49,8 @@ sys.stdout.write('\r[') for layout in self.counters_fmt: Terminal.set_color(layout['color']) - sys.stdout.write(('%' + str(self.limit_digits) + 'd') % data[layout['value']]) + sys.stdout.write(('{:' + str(self.limit_digits) + 'd}').format( + data[layout['value']])) Terminal.reset_color() if layout != self.counters_fmt[-1]: sys.stdout.write('|') @@ -58,7 +59,7 @@ # Build the bar. pct = int(100.0 * current / self.limit) - sys.stdout.write('%3d%% ' % pct) + sys.stdout.write('{:3d}% '.format(pct)) barlen = int(1.0 * self.barlen * current / self.limit) - 1 bar = '=' * barlen + '>' + ' ' * (self.barlen - barlen - 1) @@ -67,7 +68,7 @@ # Update the bar. dt = datetime.now() - self.t0 dt = dt.seconds + dt.microseconds * 1e-6 - sys.stdout.write('%6.1fs' % dt) + sys.stdout.write('{:6.1f}s'.format(dt)) Terminal.clear_right() # Force redisplay, since we didn't write a \n. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/results.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/results.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/results.py 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/results.py 2015-02-03 14:33:39.000000000 +0000 @@ -6,7 +6,7 @@ # subprocess.list2cmdline does not properly escape for sh-like shells def escape_cmdline(args): - return ' '.join([ pipes.quote(a) for a in args ]) + return ' '.join([pipes.quote(a) for a in args]) class TestOutput: """Output from a test run.""" @@ -76,7 +76,8 @@ msg = line[len(' PASSED! '):] results.append((cls.PASS, msg)) else: - m = re.match('--- NOTE: IN THIS TESTCASE, WE EXPECT EXIT CODE ((?:-|\\d)+) ---', line) + m = re.match('--- NOTE: IN THIS TESTCASE, WE EXPECT EXIT CODE' + ' ((?:-|\\d)+) ---', line) if m: expected_rcs.append(int(m.group(1))) @@ -118,7 +119,9 @@ self.counts['TIMEOUT'] += 1 if isinstance(output, NullTestOutput): if self.options.tinderbox: - self.print_tinderbox_result('TEST-KNOWN-FAIL', output.test.path, time=output.dt, skip=True) + self.print_tinderbox_result( + 'TEST-KNOWN-FAIL', output.test.path, time=output.dt, + skip=True) self.counts['SKIP'] += 1 self.n += 1 else: @@ -130,22 +133,26 @@ self.groups.setdefault(dev_label, []).append(result.test.path) if dev_label == 'REGRESSIONS': - show_output = self.options.show_output or not self.options.no_show_failed + show_output = self.options.show_output \ + or not self.options.no_show_failed elif dev_label == 'TIMEOUTS': show_output = self.options.show_output else: - show_output = self.options.show_output and not self.options.failed_only + show_output = self.options.show_output \ + and not self.options.failed_only if dev_label in ('REGRESSIONS', 'TIMEOUTS'): show_cmd = self.options.show_cmd else: - show_cmd = self.options.show_cmd and not self.options.failed_only + show_cmd = self.options.show_cmd \ + and not self.options.failed_only if show_output or show_cmd: self.pb.beginline() if show_output: - print('## %s: rc = %d, run time = %f' % (output.test.path, output.rc, output.dt), file=self.fp) + print('## {}: rc = {:d}, run time = {:f}'.format( + output.test.path, output.rc, output.dt), file=self.fp) if show_cmd: print(escape_cmdline(output.cmd), file=self.fp) @@ -166,19 +173,23 @@ if self.options.tinderbox: if len(result.results) > 1: for sub_ok, msg in result.results: - label = self.LABELS[(sub_ok, result.test.expect, result.test.random)][0] + tup = (sub_ok, result.test.expect, result.test.random) + label = self.LABELS[tup][0] if label == 'TEST-UNEXPECTED-PASS': label = 'TEST-PASS (EXPECTED RANDOM)' - self.print_tinderbox_result(label, result.test.path, time=output.dt, message=msg) - self.print_tinderbox_result(self.LABELS[ - (result.result, result.test.expect, result.test.random)][0], - result.test.path, time=output.dt) + self.print_tinderbox_result( + label, result.test.path, time=output.dt, + message=msg) + tup = (result.result, result.test.expect, result.test.random) + self.print_tinderbox_result( + self.LABELS[tup][0], result.test.path, time=output.dt) return if dev_label: def singular(label): return "FIXED" if label == "FIXES" else label[:-1] - self.pb.message("%s - %s" % (singular(dev_label), output.test.path)) + self.pb.message("{} - {}".format(singular(dev_label), + output.test.path)) self.pb.update(self.n, self.counts) @@ -214,18 +225,18 @@ print(label) for path in paths: - print(' %s' % path) + print(' {}'.format(path)) if self.options.failure_file: - failure_file = open(self.options.failure_file, 'w') - if not self.all_passed(): - if 'REGRESSIONS' in self.groups: - for path in self.groups['REGRESSIONS']: - print(path, file=failure_file) - if 'TIMEOUTS' in self.groups: - for path in self.groups['TIMEOUTS']: - print(path, file=failure_file) - failure_file.close() + failure_file = open(self.options.failure_file, 'w') + if not self.all_passed(): + if 'REGRESSIONS' in self.groups: + for path in self.groups['REGRESSIONS']: + print(path, file=failure_file) + if 'TIMEOUTS' in self.groups: + for path in self.groups['TIMEOUTS']: + print(path, file=failure_file) + failure_file.close() suffix = '' if completed else ' (partial run -- interrupted by user)' if self.all_passed(): @@ -236,7 +247,8 @@ def all_passed(self): return 'REGRESSIONS' not in self.groups and 'TIMEOUTS' not in self.groups - def print_tinderbox_result(self, label, path, message=None, skip=False, time=None): + def print_tinderbox_result(self, label, path, message=None, skip=False, + time=None): result = label result += " | " + path result += " |" + self.options.shell_args diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/tasks_unix.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/tasks_unix.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/tasks_unix.py 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/tasks_unix.py 2015-02-03 14:33:39.000000000 +0000 @@ -17,7 +17,7 @@ self.out = [] self.err = [] -def spawn_test(test, passthrough = False): +def spawn_test(test, passthrough=False): """Spawn one child, return a task struct.""" if not passthrough: (rout, wout) = os.pipe() @@ -45,7 +45,8 @@ """ Return the total number of seconds contained in the duration as a float """ - return (float(td.microseconds) + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6 + return (float(td.microseconds) \ + + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6 def get_max_wait(tasks, results, timeout): """ @@ -116,7 +117,7 @@ index = i break else: - raise KeyError("No such pid: %s" % pid) + raise KeyError("No such pid: {}".format(pid)) out = tasks[index] tasks.pop(index) @@ -161,13 +162,13 @@ returncode = -os.WTERMSIG(status) out = TestOutput( - ended.test, - ended.cmd, - ''.join(ended.out), - ''.join(ended.err), - returncode, - total_seconds(datetime.now() - ended.start), - timed_out(ended, timeout)) + ended.test, + ended.cmd, + ''.join(ended.out), + ''.join(ended.err), + returncode, + total_seconds(datetime.now() - ended.start), + timed_out(ended, timeout)) results.push(out) return tasks diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/tasks_win.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/tasks_win.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/tasks_win.py 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/tasks_win.py 2015-02-03 14:33:39.000000000 +0000 @@ -7,7 +7,7 @@ from datetime import datetime class Source: - def __init__(self, task_list, results, timeout, verbose = False): + def __init__(self, task_list, results, timeout, verbose=False): self.tasks = Queue() for task in task_list: self.tasks.put_nowait(task) @@ -20,7 +20,9 @@ t0 = datetime.now() sink = Sink(self.results) - self.workers = [ Worker(_+1, self.tasks, sink, self.timeout, self.verbose) for _ in range(worker_count) ] + self.workers = [Worker(_ + 1, self.tasks, sink, self.timeout, + self.verbose) + for _ in range(worker_count)] if self.verbose: print('[P] Starting workers.') for w in self.workers: @@ -65,12 +67,13 @@ self.thread = None self.stop = False + self.t0 = 0 def log(self, msg): if self.verbose: dd = datetime.now() - self.t0 dt = dd.seconds + 1e-6 * dd.microseconds - print('[W%d %.3f] %s' % (self.id, dt, msg)) + print('[W{:d} {:.3f}] {}'.format(self.id, dt, msg)) def run(self): try: @@ -79,7 +82,7 @@ break self.log('Get next task.') task = self.tasks.get(False) - self.log('Start task %s.'%str(task)) + self.log('Start task {}.'.format(str(task))) result = task.run(task.js_cmd_prefix, self.timeout) self.log('Finished task.') self.sink.push(result) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/terminal_unix.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/terminal_unix.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/terminal_unix.py 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/terminal_unix.py 2015-02-03 14:33:39.000000000 +0000 @@ -26,7 +26,8 @@ color = color[len('bright'):] color_code = Terminal.COLOR[color] - sys.stdout.write(cls.ESCAPE + color_code + cls.SEPARATOR + mod + cls.COLOR_CODE) + sys.stdout.write(cls.ESCAPE + color_code + cls.SEPARATOR + mod + + cls.COLOR_CODE) @classmethod def reset_color(cls): diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/terminal_win.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/terminal_win.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/terminal_win.py 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/terminal_win.py 2015-02-03 14:33:39.000000000 +0000 @@ -15,27 +15,27 @@ WORD = c_ushort class COORD(Structure): - """struct in wincon.h.""" - _fields_ = [ - ("X", SHORT), - ("Y", SHORT)] + """struct in wincon.h.""" + _fields_ = [ + ("X", SHORT), + ("Y", SHORT)] class SMALL_RECT(Structure): - """struct in wincon.h.""" - _fields_ = [ - ("Left", SHORT), - ("Top", SHORT), - ("Right", SHORT), - ("Bottom", SHORT)] + """struct in wincon.h.""" + _fields_ = [ + ("Left", SHORT), + ("Top", SHORT), + ("Right", SHORT), + ("Bottom", SHORT)] class CONSOLE_SCREEN_BUFFER_INFO(Structure): - """struct in wincon.h.""" - _fields_ = [ - ("dwSize", COORD), - ("dwCursorPosition", COORD), - ("wAttributes", WORD), - ("srWindow", SMALL_RECT), - ("dwMaximumWindowSize", COORD)] + """struct in wincon.h.""" + _fields_ = [ + ("dwSize", COORD), + ("dwCursorPosition", COORD), + ("wAttributes", WORD), + ("srWindow", SMALL_RECT), + ("dwMaximumWindowSize", COORD)] # winbase.h STD_INPUT_HANDLE = -10 @@ -68,9 +68,9 @@ GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo def get_text_attr(): - csbi = CONSOLE_SCREEN_BUFFER_INFO() - GetConsoleScreenBufferInfo(stdout_handle, byref(csbi)) - return csbi.wAttributes + csbi = CONSOLE_SCREEN_BUFFER_INFO() + GetConsoleScreenBufferInfo(stdout_handle, byref(csbi)) + return csbi.wAttributes DEFAULT_COLORS = get_text_attr() diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/tests.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/tests.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/lib/tests.py 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/lib/tests.py 2015-02-03 14:33:39.000000000 +0000 @@ -9,18 +9,20 @@ from results import TestOutput -# When run on tbpl, we run each test multiple times with the following arguments. +# When run on tbpl, we run each test multiple times with the following +# arguments. TBPL_FLAGS = [ [], # no flags, normal baseline and ion ['--ion-eager', '--ion-offthread-compile=off'], # implies --baseline-eager - ['--ion-eager', '--ion-offthread-compile=off', '--ion-check-range-analysis', '--no-sse3', '--no-threads'], + ['--ion-eager', '--ion-offthread-compile=off', + '--ion-check-range-analysis', '--no-sse3', '--no-threads'], ['--baseline-eager'], ['--baseline-eager', '--no-fpu'], ['--no-baseline', '--no-ion'], ] def do_run_cmd(cmd): - l = [ None, None ] + l = [None, None] th_run_cmd(cmd, l) return l[1] @@ -55,7 +57,7 @@ if timeout is None: return do_run_cmd(cmd) - l = [ None, None ] + l = [None, None] timed_out = False th = Thread(target=th_run_cmd, args=(cmd, l)) th.start() @@ -78,19 +80,23 @@ class Test(object): """A runnable test.""" def __init__(self, path): - self.path = path # str: path of JS file relative to tests root dir + self.path = path # str: path of JS file relative to tests root dir + self.options = [] # [str]: Extra options to pass to the shell @staticmethod def prefix_command(path): - """Return the '-f shell.js' options needed to run a test with the given path.""" + """Return the '-f shell.js' options needed to run a test with the given + path.""" if path == '': - return [ '-f', 'shell.js' ] + return ['-f', 'shell.js'] head, base = os.path.split(path) - return Test.prefix_command(head) + [ '-f', os.path.join(path, 'shell.js') ] + return Test.prefix_command(head) \ + + ['-f', os.path.join(path, 'shell.js')] def get_command(self, js_cmd_prefix): dirname, filename = os.path.split(self.path) - cmd = js_cmd_prefix + self.options + Test.prefix_command(dirname) + [ '-f', self.path ] + cmd = js_cmd_prefix + self.options + Test.prefix_command(dirname) \ + + ['-f', self.path] return cmd def run(self, js_cmd_prefix, timeout=30.0): @@ -108,7 +114,6 @@ self.expect = True # bool: expected result, True => pass self.random = False # bool: True => ignore output as 'random' self.slow = False # bool: True => test may run slowly - self.options = [] # [str]: Extra options to pass to the shell # The terms parsed to produce the above properties. self.terms = None diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/parsemark.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/parsemark.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/parsemark.py 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/parsemark.py 2015-02-03 14:33:39.000000000 +0000 @@ -9,7 +9,7 @@ "Faster" means that: t_baseline_goodrun = (t_baseline_avg - t_baseline_stddev) - t_current_badrun = (t_current_avg + t_current_stddev) + t_current_badrun = (t_current_avg + t_current_stddev) t_current_badrun < t_baseline_goodrun Effectively, a bad run from the current data is better than a good run from the @@ -72,7 +72,8 @@ """Return a list of milliseconds for the counted runs.""" assert '"' not in filepath code = JS_CODE_TEMPLATE.substitute(filepath=filepath, - warmup_run_count=warmup_runs, real_run_count=counted_runs) + warmup_run_count=warmup_runs, + real_run_count=counted_runs) proc = subp.Popen([shellpath, '-e', code], stdout=subp.PIPE) stdout, _ = proc.communicate() milliseconds = [float(val) for val in stdout.split(',')] @@ -81,7 +82,8 @@ if not stfu: print('Runs:', [int(ms) for ms in milliseconds]) print('Mean:', mean) - print('Stddev: %.2f (%.2f%% of mean)' % (sigma, sigma / mean * 100)) + print('Stddev: {:.2f} ({:.2f}% of mean)'.format( + sigma, sigma / mean * 100)) return mean, sigma @@ -91,34 +93,40 @@ for filepath in filepaths: filename = os.path.split(filepath)[-1] if not stfu: - print('Parsemarking %s...' % filename) + print('Parsemarking {}...'.format(filename)) bench_map[filename] = fbench(filepath) print('{') for i, (filename, (avg, stddev)) in enumerate(bench_map.iteritems()): assert '"' not in filename - fmt = ' %30s: {"average_ms": %6.2f, "stddev_ms": %6.2f}' + fmt = ' {:30s}: {{"average_ms": {:6.2f}, "stddev_ms": {:6.2f}}}' if i != len(bench_map) - 1: fmt += ',' - filename_str = '"%s"' % filename - print(fmt % (filename_str, avg, stddev)) + filename_str = '"{}"'.format(filename) + print(fmt.format(filename_str, avg, stddev)) print('}') return dict((filename, dict(average_ms=avg, stddev_ms=stddev)) - for filename, (avg, stddev) in bench_map.iteritems()) + for filename, (avg, stddev) in bench_map.iteritems()) def main(): parser = optparse.OptionParser(usage=__doc__.strip()) parser.add_option('-w', '--warmup-runs', metavar='COUNT', type=int, - default=5, help='used to minimize test instability [%default]') + default=5, + help='used to minimize test instability [%default]') parser.add_option('-c', '--counted-runs', metavar='COUNT', type=int, - default=50, help='timed data runs that count towards the average [%default]') - parser.add_option('-s', '--shell', metavar='PATH', help='explicit shell ' - 'location; when omitted, will look in likely places') + default=50, + help='timed data runs that count towards the average' + ' [%default]') + parser.add_option('-s', '--shell', metavar='PATH', + help='explicit shell location; when omitted, will look' + ' in likely places') parser.add_option('-b', '--baseline', metavar='JSON_PATH', - dest='baseline_path', help='json file with baseline values to ' - 'compare against') + dest='baseline_path', + help='json file with baseline values to ' + 'compare against') parser.add_option('-q', '--quiet', dest='stfu', action='store_true', - default=False, help='only print JSON to stdout [%default]') + default=False, + help='only print JSON to stdout [%default]') options, args = parser.parse_args() try: shellpath = args.pop(0) @@ -142,10 +150,13 @@ print('error: baseline file does not exist', file=sys.stderr) return -1 if not compare_bench: - print('error: JSON support is missing, cannot compare benchmarks', file=sys.stderr) + print('error: JSON support is missing, cannot compare benchmarks', + file=sys.stderr) return -1 benchfile = lambda filepath: bench(shellpath, filepath, - options.warmup_runs, options.counted_runs, stfu=options.stfu) + options.warmup_runs, + options.counted_runs, + stfu=options.stfu) bench_map = parsemark(gen_filepaths(dirpath), benchfile, options.stfu) if options.baseline_path: compare_bench.compare_immediate(bench_map, options.baseline_path) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/shell.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/shell.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/tests/shell.js 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/tests/shell.js 2015-02-03 14:33:39.000000000 +0000 @@ -876,6 +876,3 @@ cx.setOptimizationLevel(i); } /* end of Rhino functions */ - -var JS_HAS_SYMBOLS = typeof Symbol === "function"; -var std_iterator = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator"; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ArgumentsObject.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ArgumentsObject.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ArgumentsObject.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ArgumentsObject.cpp 2015-02-03 14:33:44.000000000 +0000 @@ -167,8 +167,8 @@ bool strict = callee->strict(); const Class *clasp = strict ? &StrictArgumentsObject::class_ : &NormalArgumentsObject::class_; - RootedTypeObject type(cx, cx->getNewType(clasp, TaggedProto(proto.get()))); - if (!type) + RootedObjectGroup group(cx, cx->getNewGroup(clasp, TaggedProto(proto.get()))); + if (!group) return nullptr; JSObject *metadata = nullptr; @@ -196,7 +196,7 @@ Rooted obj(cx); JSObject *base = JSObject::create(cx, FINALIZE_KIND, GetInitialHeap(GenericObject, clasp), - shape, type); + shape, group); if (!base) { js_free(data); return nullptr; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ArrayBufferObject.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ArrayBufferObject.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ArrayBufferObject.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ArrayBufferObject.cpp 2015-02-03 14:33:44.000000000 +0000 @@ -519,11 +519,11 @@ // performed. This is done by setting a compartment wide flag indicating // that buffers with typed object views have been neutered. if (buffer->hasTypedObjectViews()) { - // Make sure the global object's type has been instantiated, so the + // Make sure the global object's group has been instantiated, so the // flag change will be observed. - if (!cx->global()->getType(cx)) + if (!cx->global()->getGroup(cx)) CrashAtUnhandlableOOM("ArrayBufferObject::neuter"); - types::MarkTypeObjectFlags(cx, cx->global(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED); + types::MarkObjectGroupFlags(cx, cx->global(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED); cx->compartment()->neuteredTypedObjects = 1; } @@ -1551,8 +1551,8 @@ RootedId byteLengthId(cx, NameToId(cx->names().byteLength)); unsigned attrs = JSPROP_SHARED | JSPROP_GETTER; - JSObject *getter = NewFunction(cx, NullPtr(), ArrayBufferObject::byteLengthGetter, 0, - JSFunction::NATIVE_FUN, global, NullPtr()); + JSObject *getter = NewFunction(cx, js::NullPtr(), ArrayBufferObject::byteLengthGetter, 0, + JSFunction::NATIVE_FUN, global, js::NullPtr()); if (!getter) return nullptr; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ArrayObject.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ArrayObject.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ArrayObject.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ArrayObject.h 2015-02-03 14:33:44.000000000 +0000 @@ -44,7 +44,7 @@ gc::AllocKind kind, gc::InitialHeap heap, HandleShape shape, - HandleTypeObject type, + HandleObjectGroup group, uint32_t length); // Make an array object with the specified initial state and elements. @@ -52,7 +52,7 @@ createArray(ExclusiveContext *cx, gc::InitialHeap heap, HandleShape shape, - HandleTypeObject type, + HandleObjectGroup group, HeapSlot *elements); // Make a copy-on-write array object which shares the elements of an @@ -70,7 +70,7 @@ gc::AllocKind kind, gc::InitialHeap heap, HandleShape shape, - HandleTypeObject type); + HandleObjectGroup group); static inline ArrayObject * finishCreateArray(ArrayObject *obj, HandleShape shape); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ArrayObject-inl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ArrayObject-inl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ArrayObject-inl.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ArrayObject-inl.h 2015-02-03 14:33:44.000000000 +0000 @@ -22,7 +22,7 @@ if (length > INT32_MAX) { /* Track objects with overflowing lengths in type information. */ - types::MarkTypeObjectFlags(cx, this, types::OBJECT_FLAG_LENGTH_OVERFLOW); + types::MarkObjectGroupFlags(cx, this, types::OBJECT_FLAG_LENGTH_OVERFLOW); } getElementsHeader()->length = length; @@ -30,25 +30,25 @@ /* static */ inline ArrayObject * ArrayObject::createArrayInternal(ExclusiveContext *cx, gc::AllocKind kind, gc::InitialHeap heap, - HandleShape shape, HandleTypeObject type) + HandleShape shape, HandleObjectGroup group) { // Create a new array and initialize everything except for its elements. - MOZ_ASSERT(shape && type); - MOZ_ASSERT(type->clasp() == shape->getObjectClass()); - MOZ_ASSERT(type->clasp() == &ArrayObject::class_); - MOZ_ASSERT_IF(type->clasp()->finalize, heap == gc::TenuredHeap); + MOZ_ASSERT(shape && group); + MOZ_ASSERT(group->clasp() == shape->getObjectClass()); + MOZ_ASSERT(group->clasp() == &ArrayObject::class_); + MOZ_ASSERT_IF(group->clasp()->finalize, heap == gc::TenuredHeap); // Arrays can use their fixed slots to store elements, so can't have shapes // which allow named properties to be stored in the fixed slots. MOZ_ASSERT(shape->numFixedSlots() == 0); - size_t nDynamicSlots = dynamicSlotsCount(0, shape->slotSpan(), type->clasp()); - JSObject *obj = NewGCObject(cx, kind, nDynamicSlots, heap, type->clasp()); + size_t nDynamicSlots = dynamicSlotsCount(0, shape->slotSpan(), group->clasp()); + JSObject *obj = NewGCObject(cx, kind, nDynamicSlots, heap, group->clasp()); if (!obj) return nullptr; static_cast(obj)->shape_.init(shape); - static_cast(obj)->type_.init(type); + static_cast(obj)->group_.init(group); return &obj->as(); } @@ -67,10 +67,10 @@ /* static */ inline ArrayObject * ArrayObject::createArray(ExclusiveContext *cx, gc::AllocKind kind, gc::InitialHeap heap, - HandleShape shape, HandleTypeObject type, + HandleShape shape, HandleObjectGroup group, uint32_t length) { - ArrayObject *obj = createArrayInternal(cx, kind, heap, shape, type); + ArrayObject *obj = createArrayInternal(cx, kind, heap, shape, group); if (!obj) return nullptr; @@ -84,7 +84,7 @@ /* static */ inline ArrayObject * ArrayObject::createArray(ExclusiveContext *cx, gc::InitialHeap heap, - HandleShape shape, HandleTypeObject type, + HandleShape shape, HandleObjectGroup group, HeapSlot *elements) { // Use the smallest allocation kind for the array, as it can't have any @@ -92,7 +92,7 @@ // its fixed elements. gc::AllocKind kind = gc::FINALIZE_OBJECT0_BACKGROUND; - ArrayObject *obj = createArrayInternal(cx, kind, heap, shape, type); + ArrayObject *obj = createArrayInternal(cx, kind, heap, shape, group); if (!obj) return nullptr; @@ -114,8 +114,8 @@ // its fixed elements. gc::AllocKind kind = gc::FINALIZE_OBJECT0_BACKGROUND; - RootedTypeObject type(cx, sharedElementsOwner->type()); - ArrayObject *obj = createArrayInternal(cx, kind, heap, shape, type); + RootedObjectGroup group(cx, sharedElementsOwner->group()); + ArrayObject *obj = createArrayInternal(cx, kind, heap, shape, group); if (!obj) return nullptr; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/CommonPropertyNames.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/CommonPropertyNames.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/CommonPropertyNames.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/CommonPropertyNames.h 2015-02-03 14:33:44.000000000 +0000 @@ -47,7 +47,6 @@ macro(count, count, "count") \ macro(currency, currency, "currency") \ macro(currencyDisplay, currencyDisplay, "currencyDisplay") \ - macro(std_iterator, std_iterator, "@@iterator") \ macro(DateTimeFormat, DateTimeFormat, "DateTimeFormat") \ macro(DateTimeFormatFormatGet, DateTimeFormatFormatGet, "Intl_DateTimeFormat_format_get") \ macro(decodeURI, decodeURI, "decodeURI") \ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Debugger.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Debugger.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Debugger.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Debugger.cpp 2015-02-03 14:33:44.000000000 +0000 @@ -664,30 +664,11 @@ } /* - * Clean up all Debugger.Frame instances. Use a fresh FrameRange, as one - * debugger's onPop handler could have caused another debugger to create its - * own Debugger.Frame instance. + * Clean up all Debugger.Frame instances. This call creates a fresh + * FrameRange, as one debugger's onPop handler could have caused another + * debugger to create its own Debugger.Frame instance. */ - for (FrameRange r(frame, global); !r.empty(); r.popFront()) { - RootedNativeObject frameobj(cx, r.frontFrame()); - Debugger *dbg = r.frontDebugger(); - MOZ_ASSERT(dbg == Debugger::fromChildJSObject(frameobj)); - - FreeOp *fop = cx->runtime()->defaultFreeOp(); - DebuggerFrame_freeScriptFrameIterData(fop, frameobj); - DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, frame, frameobj); - - dbg->frames.remove(frame); - } - - /* - * If this is an eval frame, then from the debugger's perspective the - * script is about to be destroyed. Remove any breakpoints in it. - */ - if (frame.isEvalFrame()) { - RootedScript script(cx, frame.script()); - script->clearBreakpointsIn(cx->runtime()->defaultFreeOp(), nullptr, nullptr); - } + removeFromFrameMapsAndClearBreakpointsIn(cx, frame); /* Establish (status, value) as our resumption value. */ switch (status) { @@ -1084,6 +1065,42 @@ return newCompletionValue(cx, status, value, vp); } +static bool +GetStatusProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, JSTrapStatus status, + JSTrapStatus *statusOut, MutableHandleValue vp, int *hits) +{ + bool found; + if (!HasProperty(cx, obj, name, &found)) + return false; + if (found) { + ++*hits; + *statusOut = status; + if (!GetProperty(cx, obj, obj, name, vp)) + return false; + } + return true; +} + +static bool +ParseResumptionValueAsObject(JSContext *cx, HandleValue rv, JSTrapStatus *statusp, + MutableHandleValue vp) +{ + int hits = 0; + if (rv.isObject()) { + RootedObject obj(cx, &rv.toObject()); + if (!GetStatusProperty(cx, obj, cx->names().return_, JSTRAP_RETURN, statusp, vp, &hits)) + return false; + if (!GetStatusProperty(cx, obj, cx->names().throw_, JSTRAP_THROW, statusp, vp, &hits)) + return false; + } + + if (hits != 1) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_RESUMPTION); + return false; + } + return true; +} + JSTrapStatus Debugger::parseResumptionValue(Maybe &ac, bool ok, const Value &rv, MutableHandleValue vp, bool callHook) @@ -1100,35 +1117,16 @@ return JSTRAP_ERROR; } - /* Check that rv is {return: val} or {throw: val}. */ JSContext *cx = ac->context()->asJSContext(); - Rooted obj(cx); - RootedShape shape(cx); - RootedId returnId(cx, NameToId(cx->names().return_)); - RootedId throwId(cx, NameToId(cx->names().throw_)); - bool okResumption = rv.isObject(); - if (okResumption) { - obj = &rv.toObject(); - okResumption = obj->is(); - } - if (okResumption) { - shape = obj->lastProperty(); - okResumption = shape->previous() && - !shape->previous()->previous() && - (shape->propid() == returnId || shape->propid() == throwId) && - shape->isDataDescriptor(); - } - if (!okResumption) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_RESUMPTION); + JSTrapStatus status = JSTRAP_CONTINUE; + RootedValue v(cx); + RootedValue rvRoot(cx, rv); + if (!ParseResumptionValueAsObject(cx, rvRoot, &status, &v) || + !unwrapDebuggeeValue(cx, &v)) + { return handleUncaughtException(ac, vp, callHook); } - HandleNativeObject nobj = obj.as(); - - RootedValue v(cx, vp.get()); - if (!NativeGetExistingProperty(cx, obj, nobj, shape, &v) || !unwrapDebuggeeValue(cx, &v)) - return handleUncaughtException(ac, &v, callHook); - ac.reset(); if (!cx->compartment()->wrap(cx, &v)) { vp.setUndefined(); @@ -1136,7 +1134,7 @@ } vp.set(v); - return shape->propid() == returnId ? JSTRAP_RETURN : JSTRAP_THROW; + return status; } static bool @@ -1304,47 +1302,27 @@ return JSTRAP_CONTINUE; } -static bool -AddNewScriptRecipients(GlobalObject::DebuggerVector *src, HandleScript script, - AutoValueVector *dest) -{ - bool wasEmpty = dest->length() == 0; - for (Debugger **p = src->begin(); p != src->end(); p++) { - Debugger *dbg = *p; - Value v = ObjectValue(*dbg->toJSObject()); - if (dbg->observesScript(script) && dbg->observesNewScript() && - (wasEmpty || Find(dest->begin(), dest->end(), v) == dest->end()) && - !dest->append(v)) - { - return false; - } - } - return true; -} - void -Debugger::slowPathOnNewScript(JSContext *cx, HandleScript script, GlobalObject *compileAndGoGlobal_) +Debugger::slowPathOnNewScript(JSContext *cx, HandleScript script) { - Rooted compileAndGoGlobal(cx, compileAndGoGlobal_); - - MOZ_ASSERT(script->compileAndGo() == !!compileAndGoGlobal); + Rooted global(cx, &script->global()); /* * Build the list of recipients based on the debuggers observing the * script's compartment. - * - * TODO bug 1064079 will simplify this logic. The meaning of - * compile-and-go has changed over the years and is no longer relevant to - * Debugger. */ AutoValueVector triggered(cx); - GlobalObject::DebuggerVector *debuggers = - (script->compileAndGo() - ? compileAndGoGlobal->getDebuggers() - : script->compartment()->maybeGlobal()->getDebuggers()); + GlobalObject::DebuggerVector *debuggers = global->getDebuggers(); if (debuggers) { - if (!AddNewScriptRecipients(debuggers, script, &triggered)) - return; + for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++) { + Debugger *dbg = *p; + if (dbg->observesNewScript() && dbg->observesScript(script)) { + if (!triggered.append(ObjectValue(*dbg->toJSObject()))) { + js_ReportOutOfMemory(cx); + return; + } + } + } } /* @@ -1353,8 +1331,10 @@ */ for (Value *p = triggered.begin(); p != triggered.end(); p++) { Debugger *dbg = Debugger::fromJSObject(&p->toObject()); - if ((!compileAndGoGlobal || dbg->debuggees.has(compileAndGoGlobal)) && - dbg->enabled && dbg->getHook(OnNewScript)) { + if (dbg->debuggees.has(global) && + dbg->enabled && + dbg->getHook(OnNewScript)) + { dbg->fireNewScript(cx, script); } } @@ -4849,6 +4829,42 @@ return true; } +/* static */ void +Debugger::assertNotInFrameMaps(AbstractFramePtr frame) +{ +#ifdef DEBUG + FrameRange r(frame); + MOZ_ASSERT(r.empty()); +#endif +} + +/* static */ void +Debugger::removeFromFrameMapsAndClearBreakpointsIn(JSContext *cx, AbstractFramePtr frame) +{ + Handle global = cx->global(); + + for (FrameRange r(frame, global); !r.empty(); r.popFront()) { + RootedNativeObject frameobj(cx, r.frontFrame()); + Debugger *dbg = r.frontDebugger(); + MOZ_ASSERT(dbg == Debugger::fromChildJSObject(frameobj)); + + FreeOp *fop = cx->runtime()->defaultFreeOp(); + DebuggerFrame_freeScriptFrameIterData(fop, frameobj); + DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, frame, frameobj); + + dbg->frames.remove(frame); + } + + /* + * If this is an eval frame, then from the debugger's perspective the + * script is about to be destroyed. Remove any breakpoints in it. + */ + if (frame.isEvalFrame()) { + RootedScript script(cx, frame.script()); + script->clearBreakpointsIn(cx->runtime()->defaultFreeOp(), nullptr, nullptr); + } +} + /* static */ bool Debugger::handleBaselineOsr(JSContext *cx, InterpreterFrame *from, jit::BaselineFrame *to) { @@ -4875,6 +4891,15 @@ } /* static */ void +Debugger::handleUnrecoverableIonBailoutError(JSContext *cx, jit::RematerializedFrame *frame) +{ + // Ion bailout can fail due to overrecursion. In such cases we cannot + // honor any further Debugger hooks on the frame, and need to ensure that + // its Debugger.Frame entry is cleaned up. + removeFromFrameMapsAndClearBreakpointsIn(cx, frame); +} + +/* static */ void Debugger::propagateForcedReturn(JSContext *cx, AbstractFramePtr frame, HandleValue rval) { // Invoking the interrupt handler is considered a step and invokes the @@ -5402,7 +5427,7 @@ jit::JitFrameLayout *jsFrame = (jit::JitFrameLayout *)frame->top(); jit::JitActivation *activation = iter.activation()->asJit(); - ActivationIterator activationIter(activation->cx()->perThreadData); + ActivationIterator activationIter(activation->cx()->runtime()); while (activationIter.activation() != activation) ++activationIter; @@ -5948,8 +5973,6 @@ Rooted staticScope(cx, StaticEvalObject::create(cx, js::NullPtr())); if (!staticScope) return false; - if (frame && frame.script()->strict()) - staticScope->setStrict(); CompileOptions options(cx); options.setCompileAndGo(true) .setForEval(true) @@ -5967,6 +5990,9 @@ if (!script) return false; + if (script->strict()) + staticScope->setStrict(); + script->setActiveEval(); ExecuteType type = !frame ? EXECUTE_DEBUG_GLOBAL : EXECUTE_DEBUG; return ExecuteKernel(cx, script, *env, thisv, type, frame, rval.address()); @@ -7354,12 +7380,11 @@ /* This can trigger resolve hooks. */ ErrorCopier ec(ac); - RootedShape prop(cx); - RootedObject pobj(cx); - for (; env && !prop; env = env->enclosingScope()) { - if (!LookupProperty(cx, env, id, &pobj, &prop)) + bool found; + for (; env; env = env->enclosingScope()) { + if (!HasProperty(cx, env, id, &found)) return false; - if (prop) + if (found) break; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Debugger.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Debugger.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Debugger.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Debugger.h 2015-02-03 14:33:44.000000000 +0000 @@ -425,7 +425,7 @@ static const JSPropertySpec properties[]; static const JSFunctionSpec methods[]; - static bool getNewestAbstractFramePtr(JSContext *cx); + static void removeFromFrameMapsAndClearBreakpointsIn(JSContext *cx, AbstractFramePtr frame); static bool updateExecutionObservabilityOfFrames(JSContext *cx, const ExecutionObservableSet &obs, IsObserving observing); static bool updateExecutionObservabilityOfScripts(JSContext *cx, const ExecutionObservableSet &obs, @@ -454,8 +454,7 @@ static bool slowPathOnLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool ok); static JSTrapStatus slowPathOnDebuggerStatement(JSContext *cx, AbstractFramePtr frame); static JSTrapStatus slowPathOnExceptionUnwind(JSContext *cx, AbstractFramePtr frame); - static void slowPathOnNewScript(JSContext *cx, HandleScript script, - GlobalObject *compileAndGoGlobal); + static void slowPathOnNewScript(JSContext *cx, HandleScript script); static void slowPathOnNewGlobalObject(JSContext *cx, Handle global); static bool slowPathOnLogAllocationSite(JSContext *cx, HandleSavedFrame frame, int64_t when, GlobalObject::DebuggerVector &dbgs); @@ -600,15 +599,17 @@ */ static inline bool onLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool ok); - static inline void onNewScript(JSContext *cx, HandleScript script, GlobalObject *compileAndGoGlobal); + static inline void onNewScript(JSContext *cx, HandleScript script); static inline void onNewGlobalObject(JSContext *cx, Handle global); static inline bool onLogAllocationSite(JSContext *cx, HandleSavedFrame frame, int64_t when); static JSTrapStatus onTrap(JSContext *cx, MutableHandleValue vp); static JSTrapStatus onSingleStep(JSContext *cx, MutableHandleValue vp); static bool handleBaselineOsr(JSContext *cx, InterpreterFrame *from, jit::BaselineFrame *to); static bool handleIonBailout(JSContext *cx, jit::RematerializedFrame *from, jit::BaselineFrame *to); + static void handleUnrecoverableIonBailoutError(JSContext *cx, jit::RematerializedFrame *frame); static void propagateForcedReturn(JSContext *cx, AbstractFramePtr frame, HandleValue rval); static bool hasLiveHook(GlobalObject *global, Hook which); + static void assertNotInFrameMaps(AbstractFramePtr frame); /************************************* Functions for use by Debugger.cpp. */ @@ -876,18 +877,15 @@ } /* static */ void -Debugger::onNewScript(JSContext *cx, HandleScript script, GlobalObject *compileAndGoGlobal) +Debugger::onNewScript(JSContext *cx, HandleScript script) { - MOZ_ASSERT_IF(script->compileAndGo(), compileAndGoGlobal); - MOZ_ASSERT_IF(script->compileAndGo(), compileAndGoGlobal == &script->uninlinedGlobal()); // We early return in slowPathOnNewScript for self-hosted scripts, so we can // ignore those in our assertion here. MOZ_ASSERT_IF(!script->compartment()->options().invisibleToDebugger() && !script->selfHosted(), script->compartment()->firedOnNewGlobalObject); - MOZ_ASSERT_IF(!script->compileAndGo(), !compileAndGoGlobal); if (script->compartment()->isDebuggee()) - slowPathOnNewScript(cx, script, compileAndGoGlobal); + slowPathOnNewScript(cx, script); } /* static */ void diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Debugger-inl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Debugger-inl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Debugger-inl.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Debugger-inl.h 2015-02-03 14:33:44.000000000 +0000 @@ -22,6 +22,7 @@ MOZ_ASSERT_IF(evalTraps, frame.isDebuggee()); if (frame.isDebuggee()) ok = slowPathOnLeaveFrame(cx, frame, ok); + assertNotInFrameMaps(frame); return ok; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ForOfIterator.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ForOfIterator.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ForOfIterator.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ForOfIterator.cpp 2015-02-03 14:33:44.000000000 +0000 @@ -54,14 +54,9 @@ args.setThis(ObjectValue(*iterableObj)); RootedValue callee(cx); -#ifdef JS_HAS_SYMBOLS RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator)); if (!GetProperty(cx, iterableObj, iterableObj, iteratorId, &callee)) return false; -#else - if (!GetProperty(cx, iterableObj, iterableObj, cx->names().std_iterator, &callee)) - return false; -#endif // If obj[@@iterator] is undefined and we were asked to allow non-iterables, // bail out now without setting iterator. This will make valueIsIterable(), diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/GlobalObject.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/GlobalObject.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/GlobalObject.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/GlobalObject.cpp 2015-02-03 14:33:44.000000000 +0000 @@ -376,11 +376,7 @@ // Define a top-level property 'std_iterator' with the name of the method // used by for-of loops to create an iterator. RootedValue std_iterator(cx); -#ifdef JS_HAS_SYMBOLS std_iterator.setSymbol(cx->wellKnownSymbols().get(JS::SymbolCode::iterator)); -#else - std_iterator.setString(cx->names().std_iterator); -#endif if (!JS_DefineProperty(cx, global, "std_iterator", std_iterator, JSPROP_PERMANENT | JSPROP_READONLY)) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/HelperThreads.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/HelperThreads.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/HelperThreads.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/HelperThreads.cpp 2015-02-03 14:33:44.000000000 +0000 @@ -891,12 +891,12 @@ // to the corresponding prototype in the new compartment. This will briefly // create cross compartment pointers, which will be fixed by the // MergeCompartments call below. - for (gc::ZoneCellIter iter(parseTask->cx->zone(), gc::FINALIZE_TYPE_OBJECT); + for (gc::ZoneCellIter iter(parseTask->cx->zone(), gc::FINALIZE_OBJECT_GROUP); !iter.done(); iter.next()) { - types::TypeObject *object = iter.get(); - TaggedProto proto(object->proto()); + types::ObjectGroup *group = iter.get(); + TaggedProto proto(group->proto()); if (!proto.isObject()) continue; @@ -910,7 +910,7 @@ JSObject *newProto = GetBuiltinPrototypePure(global, key); MOZ_ASSERT(newProto); - object->setProtoUnchecked(TaggedProto(newProto)); + group->setProtoUnchecked(TaggedProto(newProto)); } // Move the parsed script and all its contents into the desired compartment. @@ -932,10 +932,7 @@ if (script) { // The Debugger only needs to be told about the topmost script that was compiled. - GlobalObject *compileAndGoGlobal = nullptr; - if (script->compileAndGo()) - compileAndGoGlobal = &script->global(); - Debugger::onNewScript(cx, script, compileAndGoGlobal); + Debugger::onNewScript(cx, script); // Update the compressed source table with the result. This is normally // called by setCompressedSource when compilation occurs on the main thread. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Interpreter.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Interpreter.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Interpreter.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Interpreter.cpp 2015-02-03 14:33:44.000000000 +0000 @@ -380,7 +380,7 @@ InvokeState &invoke = *asInvoke(); if (invoke.constructing() && invoke.args().thisv().isPrimitive()) { RootedObject callee(cx, &invoke.args().callee()); - NewObjectKind newKind = invoke.useNewType() ? SingletonObject : GenericObject; + NewObjectKind newKind = invoke.createSingleton() ? SingletonObject : GenericObject; JSObject *obj = CreateThisForFunction(cx, callee, newKind); if (!obj) return false; @@ -503,14 +503,14 @@ /* Run function until JSOP_RETRVAL, JSOP_RETURN or error. */ InvokeState state(cx, args, initial); - // Check to see if useNewType flag should be set for this frame. + // Check to see if createSingleton flag should be set for this frame. if (construct) { FrameIter iter(cx); if (!iter.done() && iter.hasScript()) { JSScript *script = iter.script(); jsbytecode *pc = iter.pc(); - if (UseNewType(cx, script, pc)) - state.setUseNewType(); + if (UseSingletonForNewObject(cx, script, pc)) + state.setCreateSingleton(); } } @@ -1495,7 +1495,6 @@ RootedObject rootObject0(cx), rootObject1(cx), rootObject2(cx); RootedNativeObject rootNativeObject0(cx); RootedFunction rootFunction0(cx); - RootedTypeObject rootType0(cx); RootedPropertyName rootName0(cx); RootedId rootId0(cx); RootedShape rootShape0(cx); @@ -1881,15 +1880,12 @@ obj = &rref.toObject(); RootedId &id = rootId0; FETCH_ELEMENT_ID(-2, id); - RootedObject &obj2 = rootObject1; - RootedShape &prop = rootShape0; - if (!LookupProperty(cx, obj, id, &obj2, &prop)) + bool found; + if (!HasProperty(cx, obj, id, &found)) goto error; - bool cond = prop != nullptr; - prop = nullptr; - TRY_BRANCH_AFTER_COND(cond, 2); + TRY_BRANCH_AFTER_COND(found, 2); REGS.sp--; - REGS.sp[-1].setBoolean(cond); + REGS.sp[-1].setBoolean(found); } END_CASE(JSOP_IN) @@ -2568,16 +2564,16 @@ } InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE; - bool newType = UseNewType(cx, script, REGS.pc); + bool createSingleton = UseSingletonForNewObject(cx, script, REGS.pc); TypeMonitorCall(cx, args, construct); { InvokeState state(cx, args, initial); - if (newType) - state.setUseNewType(); + if (createSingleton) + state.setCreateSingleton(); - if (!newType && jit::IsIonEnabled(cx)) { + if (!createSingleton && jit::IsIonEnabled(cx)) { jit::MethodStatus status = jit::CanEnter(cx, state); if (status == jit::Method_Error) goto error; @@ -2608,8 +2604,8 @@ if (!activation.pushInlineFrame(args, funScript, initial)) goto error; - if (newType) - REGS.fp()->setUseNewType(); + if (createSingleton) + REGS.fp()->setCreateSingleton(); SET_SCRIPT(REGS.fp()->script()); @@ -3092,14 +3088,14 @@ RootedObject &obj = rootObject0; NewObjectKind newKind; if (i == JSProto_Array) { - newKind = UseNewTypeForInitializer(script, REGS.pc, &ArrayObject::class_); + newKind = UseSingletonForInitializer(script, REGS.pc, &ArrayObject::class_); obj = NewDenseEmptyArray(cx, nullptr, newKind); } else { gc::AllocKind allocKind = GuessObjectGCKind(0); - newKind = UseNewTypeForInitializer(script, REGS.pc, &PlainObject::class_); + newKind = UseSingletonForInitializer(script, REGS.pc, &PlainObject::class_); obj = NewBuiltinClassInstance(cx, allocKind, newKind); } - if (!obj || !SetInitializerObjectType(cx, script, REGS.pc, obj, newKind)) + if (!obj || !SetInitializerObjectGroup(cx, script, REGS.pc, obj, newKind)) goto error; PUSH_OBJECT(*obj); @@ -3110,9 +3106,9 @@ { unsigned count = GET_UINT24(REGS.pc); RootedObject &obj = rootObject0; - NewObjectKind newKind = UseNewTypeForInitializer(script, REGS.pc, &ArrayObject::class_); + NewObjectKind newKind = UseSingletonForInitializer(script, REGS.pc, &ArrayObject::class_); obj = NewDenseFullyAllocatedArray(cx, count, nullptr, newKind); - if (!obj || !SetInitializerObjectType(cx, script, REGS.pc, obj, newKind)) + if (!obj || !SetInitializerObjectGroup(cx, script, REGS.pc, obj, newKind)) goto error; PUSH_OBJECT(*obj); @@ -3141,9 +3137,9 @@ baseobj = script->getObject(REGS.pc); RootedObject &obj = rootObject1; - NewObjectKind newKind = UseNewTypeForInitializer(script, REGS.pc, baseobj->getClass()); + NewObjectKind newKind = UseSingletonForInitializer(script, REGS.pc, baseobj->getClass()); obj = CopyInitializerObject(cx, baseobj.as(), newKind); - if (!obj || !SetInitializerObjectType(cx, script, REGS.pc, obj, newKind)) + if (!obj || !SetInitializerObjectGroup(cx, script, REGS.pc, obj, newKind)) goto error; PUSH_OBJECT(*obj); @@ -3963,13 +3959,13 @@ return true; } - // Force instantiation of the script's function's type to ensure the flag + // Force instantiation of the script's function's group to ensure the flag // is preserved in type information. - if (!script->functionNonDelazifying()->getType(cx)) + if (!script->functionNonDelazifying()->getGroup(cx)) return false; - types::MarkTypeObjectFlags(cx, script->functionNonDelazifying(), - types::OBJECT_FLAG_RUNONCE_INVALIDATED); + types::MarkObjectGroupFlags(cx, script->functionNonDelazifying(), + types::OBJECT_FLAG_RUNONCE_INVALIDATED); return true; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Interpreter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Interpreter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Interpreter.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Interpreter.h 2015-02-03 14:33:44.000000000 +0000 @@ -194,18 +194,18 @@ { CallArgs &args_; InitialFrameFlags initial_; - bool useNewType_; + bool createSingleton_; public: InvokeState(JSContext *cx, CallArgs &args, InitialFrameFlags initial) : RunState(cx, Invoke, args.callee().as().nonLazyScript()), args_(args), initial_(initial), - useNewType_(false) + createSingleton_(false) { } - bool useNewType() const { return useNewType_; } - void setUseNewType() { useNewType_ = true; } + bool createSingleton() const { return createSingleton_; } + void setCreateSingleton() { createSingleton_ = true; } bool constructing() const { return InitialFrameFlagsAreConstructing(initial_); } CallArgs &args() const { return args_; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Interpreter-inl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Interpreter-inl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Interpreter-inl.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Interpreter-inl.h 2015-02-03 14:33:44.000000000 +0000 @@ -293,7 +293,7 @@ // Avoid computing the name if no type updates are needed, as this may be // expensive on scopes with large numbers of variables. - PropertyName *name = obj.hasSingletonType() + PropertyName *name = obj.isSingleton() ? ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc) : nullptr; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/JSONParser.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/JSONParser.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/JSONParser.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/JSONParser.cpp 2015-02-03 14:33:44.000000000 +0000 @@ -612,10 +612,10 @@ /* * Try to assign a new type to the object with type information for its - * properties, and update the initializer type object cache with this + * properties, and update the initializer object group cache with this * object's final shape. */ - cx->compartment()->types.fixObjectType(cx, obj); + cx->compartment()->types.fixObjectGroup(cx, obj); return obj; } @@ -646,7 +646,7 @@ return false; /* Try to assign a new type to the array according to its elements. */ - cx->compartment()->types.fixArrayType(cx, obj); + cx->compartment()->types.fixArrayGroup(cx, obj); vp.setObject(*obj); if (!freeElements.append(&elements)) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/make_opcode_doc.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/make_opcode_doc.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/make_opcode_doc.py 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/make_opcode_doc.py 2015-02-03 14:33:45.000000000 +0000 @@ -24,8 +24,6 @@ def get_xdr_version(dir): subtrahend_pat = re.compile('XDR_BYTECODE_VERSION_SUBTRAHEND\s*=\s*(\d+);', re.S) version_expr_pat = re.compile('XDR_BYTECODE_VERSION\s*=\s*uint32_t\(0xb973c0de\s*-\s*(.+?)\);', re.S) - # FIXME: Bug 1066322 - Enable ES6 symbols in all builds. - bug1066322_pat = re.compile('#ifdef\s+JS_HAS_SYMBOLS\s+\+\s*1*\s+#endif', re.S) with open('{dir}/js/src/vm/Xdr.h'.format(dir=dir), 'r') as f: data = f.read() @@ -40,14 +38,7 @@ if not m: error('XDR_BYTECODE_VERSION is not recognized.') - version_expr = m.group(1) - - bug1066322 = False - m = bug1066322_pat.search(version_expr) - if m: - bug1066322 = True - - return (subtrahend, bug1066322) + return subtrahend quoted_pat = re.compile(r"([^A-Za-z0-9]|^)'([^']+)'") js_pat = re.compile(r"([^A-Za-z0-9]|^)(JS[A-Z0-9_\*]+)") @@ -321,29 +312,33 @@ print("""
    {names}
    + +
    Value{values}
    Operands{operands}
    Length{length}
    Stack Uses{stack_uses}
    Stack Defs{stack_defs}
    {desc} """.format(names='
    '.join(names), values='
    '.join(values), - operands=escape(opcode.operands), + operands=escape(opcode.operands) or " ", length=escape(override(opcode.length, opcode.length_override)), - stack_uses=escape(opcode.stack_uses), - stack_defs=escape(opcode.stack_defs), + stack_uses=escape(opcode.stack_uses) or " ", + stack_defs=escape(opcode.stack_defs) or " ", desc=opcode.desc)) # desc is already escaped def make_element_id(name): return name.replace(' ', '-') -def print_doc(version, bug1066322, index): - print("""

    Bytecode Listing

    +def print_doc(version, index): + print("""
    {{{{SpiderMonkeySidebar("Internals")}}}}
    + +

    Bytecode Listing

    This document is automatically generated from Opcodes.h and @@ -356,15 +351,6 @@ version=version, actual_version=0xb973c0de - version)) - if bug1066322: - symbol_version = version + 1 - print(""" -

    Until {{{{bug(1066322)}}}} is fixed, JSOP_SYMBOL exists only in Nightly, and it uses different version.

    -

    Bytecode version with JSOP_SYMBOL: {version} -(0x{actual_version:08x}).

    -""".format(version=symbol_version, - actual_version=0xb973c0de - symbol_version)) - for (category_name, types) in index: print('

    {name}

    '.format(name=category_name, id=make_element_id(category_name))) @@ -384,6 +370,6 @@ file=sys.stderr) sys.exit(1) dir = sys.argv[1] - (version, bug1066322) = get_xdr_version(dir) + version = get_xdr_version(dir) index = get_opcodes(dir) - print_doc(version, bug1066322, index) + print_doc(version, index) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/MemoryMetrics.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/MemoryMetrics.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/MemoryMetrics.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/MemoryMetrics.cpp 2015-02-03 14:33:44.000000000 +0000 @@ -549,10 +549,10 @@ break; } - case JSTRACE_TYPE_OBJECT: { - types::TypeObject *obj = static_cast(thing); - zStats->typeObjectsGCHeap += thingSize; - zStats->typeObjectsMallocHeap += obj->sizeOfExcludingThis(rtStats->mallocSizeOf_); + case JSTRACE_OBJECT_GROUP: { + types::ObjectGroup *group = static_cast(thing); + zStats->objectGroupsGCHeap += thingSize; + zStats->objectGroupsMallocHeap += group->sizeOfExcludingThis(rtStats->mallocSizeOf_); break; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/NativeObject.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/NativeObject.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/NativeObject.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/NativeObject.cpp 2015-02-03 14:33:44.000000000 +0000 @@ -332,6 +332,7 @@ MOZ_ASSERT(!shape->inDictionary()); MOZ_ASSERT(shape->compartment() == obj->compartment()); MOZ_ASSERT(shape->numFixedSlots() == obj->numFixedSlots()); + MOZ_ASSERT(shape->getObjectClass() == obj->getClass()); size_t oldSpan = obj->lastProperty()->slotSpan(); size_t newSpan = shape->slotSpan(); @@ -355,6 +356,7 @@ MOZ_ASSERT(!shape->inDictionary()); MOZ_ASSERT(shape->compartment() == compartment()); MOZ_ASSERT(lastProperty()->slotSpan() == shape->slotSpan()); + MOZ_ASSERT(shape->getObjectClass() == getClass()); DebugOnly oldFixed = numFixedSlots(); DebugOnly newFixed = shape->numFixedSlots(); @@ -367,6 +369,44 @@ shape_ = shape; } +void +NativeObject::setLastPropertyMakeNonNative(Shape *shape) +{ + MOZ_ASSERT(!inDictionaryMode()); + MOZ_ASSERT(!shape->getObjectClass()->isNative()); + MOZ_ASSERT(shape->compartment() == compartment()); + MOZ_ASSERT(shape->slotSpan() == 0); + MOZ_ASSERT(shape->numFixedSlots() == 0); + MOZ_ASSERT(!hasDynamicElements()); + MOZ_ASSERT(!hasDynamicSlots()); + + shape_ = shape; +} + +/* static */ void +NativeObject::setLastPropertyMakeNative(ExclusiveContext *cx, HandleNativeObject obj, + HandleShape shape) +{ + MOZ_ASSERT(obj->getClass()->isNative()); + MOZ_ASSERT(!obj->lastProperty()->isNative()); + MOZ_ASSERT(shape->isNative()); + MOZ_ASSERT(!obj->inDictionaryMode()); + MOZ_ASSERT(!shape->inDictionary()); + MOZ_ASSERT(shape->compartment() == obj->compartment()); + + obj->shape_ = shape; + obj->slots_ = nullptr; + obj->elements_ = emptyObjectElements; + + size_t oldSpan = shape->numFixedSlots(); + size_t newSpan = shape->slotSpan(); + + // A failures at this point will leave the object as a mutant, and we + // can't recover. + if (oldSpan != newSpan && !updateSlotsForSpan(cx, obj, oldSpan, newSpan)) + CrashAtUnhandlableOOM("NativeObject::setLastPropertyMakeNative"); +} + /* static */ bool NativeObject::setSlotSpan(ExclusiveContext *cx, HandleNativeObject obj, uint32_t span) { @@ -1077,10 +1117,10 @@ // Per the acquired properties analysis, when the shape of a partially // initialized object is changed to its fully initialized shape, its - // type can be updated as well. - if (types::TypeNewScript *newScript = obj->typeRaw()->newScript()) { + // group can be updated as well. + if (types::TypeNewScript *newScript = obj->groupRaw()->newScript()) { if (newScript->initializedShape() == shape) - obj->setType(newScript->initializedType()); + obj->setGroup(newScript->initializedGroup()); } } if (!shape->hasSlot() || !shape->hasDefaultGetter() || !shape->hasDefaultSetter()) @@ -1415,15 +1455,22 @@ attrs = ApplyOrDefaultAttributes(attrs, shape); - /* Keep everything from the shape that isn't the things we're changing */ - unsigned attrMask = ~(JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); - shape = NativeObject::changeProperty(cx, obj, shape, attrs, attrMask, - shape->getter(), shape->setter()); - if (!shape) - return false; - if (shape->hasSlot()) - updateValue = obj->getSlot(shape->slot()); - shouldDefine = false; + if (shape->isAccessorDescriptor() && !(attrs & JSPROP_IGNORE_READONLY)) { + // ES6 draft 2014-10-14 9.1.6.3 step 7.c: Since [[Writable]] + // is present, change the existing accessor property to a data + // property. + updateValue = UndefinedValue(); + } else { + // We are at most changing some attributes, and cannot convert + // from data descriptor to accessor, or vice versa. Take + // everything from the shape that we aren't changing. + uint32_t propMask = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT; + attrs = (shape->attributes() & ~propMask) | (attrs & propMask); + getter = shape->getter(); + setter = shape->setter(); + if (shape->hasSlot()) + updateValue = obj->getSlot(shape->slot()); + } } } @@ -1440,7 +1487,7 @@ // relevant, just clear it. attrs = ApplyOrDefaultAttributes(attrs) & ~JSPROP_IGNORE_VALUE; return DefinePropertyOrElement(cx, obj, id, getter, setter, - attrs, value, false, false); + attrs, updateValue, false, false); } MOZ_ASSERT(shape); @@ -1518,22 +1565,38 @@ return NativeDefineProperty(cx, obj, id, value, getter, setter, attrs); } + +/*** [[Get]] *************************************************************************************/ + +static inline bool +CallGetter(JSContext* cx, HandleObject receiver, HandleShape shape, MutableHandleValue vp) +{ + MOZ_ASSERT(!shape->hasDefaultGetter()); + + if (shape->hasGetterValue()) { + Value fval = shape->getterValue(); + return InvokeGetterOrSetter(cx, receiver, fval, 0, 0, vp); + } + + RootedId id(cx, shape->propid()); + return CallJSPropertyOp(cx, shape->getterOp(), receiver, id, vp); +} + template static MOZ_ALWAYS_INLINE bool -NativeGetExistingPropertyInline(JSContext *cx, - typename MaybeRooted::HandleType obj, - typename MaybeRooted::HandleType receiver, - typename MaybeRooted::HandleType pobj, - typename MaybeRooted::HandleType shape, - typename MaybeRooted::MutableHandleType vp) +GetExistingProperty(JSContext *cx, + typename MaybeRooted::HandleType receiver, + typename MaybeRooted::HandleType obj, + typename MaybeRooted::HandleType shape, + typename MaybeRooted::MutableHandleType vp) { if (shape->hasSlot()) { - vp.set(pobj->getSlot(shape->slot())); + vp.set(obj->getSlot(shape->slot())); MOZ_ASSERT_IF(!vp.isMagic(JS_UNINITIALIZED_LEXICAL) && - !pobj->hasSingletonType() && - !pobj->template is() && + !obj->isSingleton() && + !obj->template is() && shape->hasDefaultGetter(), - js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), vp)); + js::types::TypeHasProperty(cx, obj->group(), shape->propid(), vp)); } else { vp.setUndefined(); } @@ -1559,40 +1622,41 @@ if (!allowGC) return false; - if (!shape->get(cx, + if (!CallGetter(cx, MaybeRooted::toHandle(receiver), - MaybeRooted::toHandle(obj), - MaybeRooted::toHandle(pobj), + MaybeRooted::toHandle(shape), MaybeRooted::toMutableHandle(vp))) { return false; } - /* Update slotful shapes according to the value produced by the getter. */ - if (shape->hasSlot() && pobj->contains(cx, shape)) - pobj->setSlot(shape->slot(), vp); + // Ancient nonstandard extension: via the JSAPI it's possible to create a + // data property that has both a slot and a getter. In that case, copy the + // value returned by the getter back into the slot. + if (shape->hasSlot() && obj->contains(cx, shape)) + obj->setSlot(shape->slot(), vp); return true; } bool -js::NativeGetExistingProperty(JSContext *cx, HandleObject obj, HandleNativeObject pobj, +js::NativeGetExistingProperty(JSContext *cx, HandleObject receiver, HandleNativeObject obj, HandleShape shape, MutableHandleValue vp) { - return NativeGetExistingPropertyInline(cx, obj, obj, pobj, shape, vp); + return GetExistingProperty(cx, receiver, obj, shape, vp); } /* * Given pc pointing after a property accessing bytecode, return true if the - * access is "object-detecting" in the sense used by web scripts, e.g., when - * checking whether document.all is defined. + * access is "property-detecting" -- that is, if we shouldn't warn about it + * even if no such property is found and strict warnings are enabled. */ static bool Detecting(JSContext *cx, JSScript *script, jsbytecode *pc) { MOZ_ASSERT(script->containsPC(pc)); - /* General case: a branch or equality op follows the access. */ + // General case: a branch or equality op follows the access. JSOp op = JSOp(*pc); if (js_CodeSpec[op].format & JOF_DETECTING) return true; @@ -1600,10 +1664,7 @@ jsbytecode *endpc = script->codeEnd(); if (op == JSOP_NULL) { - /* - * Special case #1: handle (document.all == null). Don't sweat - * about JS1.2's revision of the equality operators here. - */ + // Special case #1: don't warn about (obj.prop == null). if (++pc < endpc) { op = JSOp(*pc); return op == JSOP_EQ || op == JSOP_NE; @@ -1612,11 +1673,7 @@ } if (op == JSOP_GETGNAME || op == JSOP_GETNAME) { - /* - * Special case #2: handle (document.all == undefined). Don't worry - * about a local variable named |undefined| shadowing the immutable - * global binding...because, really? - */ + // Special case #2: don't warn about (obj.prop == undefined). JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc)); if (atom == cx->names().undefined && (pc += js_CodeSpec[op].length) < endpc) { @@ -1628,149 +1685,185 @@ return false; } -template -static MOZ_ALWAYS_INLINE bool -GetPropertyHelperInline(JSContext *cx, - typename MaybeRooted::HandleType obj, - typename MaybeRooted::HandleType receiver, - typename MaybeRooted::HandleType id, - typename MaybeRooted::MutableHandleType vp) +/* + * Finish getting the property `receiver[id]` after looking at every object on + * the prototype chain and not finding any such property. + * + * Per the spec, this should just set the result to `undefined` and call it a + * day. However: + * + * 1. We add support for the nonstandard JSClass::getProperty hook. + * + * 2. This function also runs when we're evaluating an expression that's an + * Identifier (that is, an unqualified name lookup), so we need to figure + * out if that's what's happening and throw a ReferenceError if so. + * + * 3. We also emit an optional warning for this. (It's not super useful on the + * web, as there are too many false positives, but anecdotally useful in + * Gecko code.) + */ +static bool +GetNonexistentProperty(JSContext *cx, HandleNativeObject obj, HandleId id, + HandleObject receiver, MutableHandleValue vp) { - /* This call site is hot -- use the always-inlined variant of LookupNativeProperty(). */ - typename MaybeRooted::RootType obj2(cx); - typename MaybeRooted::RootType shape(cx); - if (!LookupPropertyInline(cx, obj, id, &obj2, &shape)) - return false; + vp.setUndefined(); - if (!shape) { - if (!allowGC) + // Non-standard extension: Call the getProperty hook. If it sets vp to a + // value other than undefined, we're done. If not, fall through to the + // warning/error checks below. + if (JSPropertyOp getProperty = obj->getClass()->getProperty) { + if (!CallJSPropertyOp(cx, getProperty, obj, id, vp)) return false; - vp.setUndefined(); + if (!vp.isUndefined()) + return true; + } - if (JSPropertyOp getProperty = obj->getClass()->getProperty) { - if (!CallJSPropertyOp(cx, getProperty, - MaybeRooted::toHandle(obj), - MaybeRooted::toHandle(id), - MaybeRooted::toMutableHandle(vp))) - { - return false; - } - } + // If we are doing a name lookup, this is a ReferenceError. + jsbytecode *pc = nullptr; + RootedScript script(cx, cx->currentScript(&pc)); + if (!pc) + return true; + JSOp op = (JSOp) *pc; + if (op == JSOP_GETXPROP) { + JSAutoByteString printable; + if (js_ValueToPrintable(cx, IdToValue(id), &printable)) + js_ReportIsNotDefined(cx, printable.ptr()); + return false; + } - /* - * Give a strict warning if foo.bar is evaluated by a script for an - * object foo with no property named 'bar'. - */ - if (vp.isUndefined()) { - jsbytecode *pc = nullptr; - RootedScript script(cx, cx->currentScript(&pc)); - if (!pc) - return true; - JSOp op = (JSOp) *pc; + // Give a strict warning if foo.bar is evaluated by a script for an object + // foo with no property named 'bar'. + // + // Don't warn if extra warnings not enabled or for random getprop + // operations. + if (!cx->compartment()->options().extraWarnings(cx) || (op != JSOP_GETPROP && op != JSOP_GETELEM)) + return true; - if (op == JSOP_GETXPROP) { - /* Undefined property during a name lookup, report an error. */ - JSAutoByteString printable; - if (js_ValueToPrintable(cx, IdToValue(id), &printable)) - js_ReportIsNotDefined(cx, printable.ptr()); - return false; - } + // Don't warn repeatedly for the same script. + if (!script || script->warnedAboutUndefinedProp()) + return true; - /* Don't warn if extra warnings not enabled or for random getprop operations. */ - if (!cx->compartment()->options().extraWarnings(cx) || (op != JSOP_GETPROP && op != JSOP_GETELEM)) - return true; + // Don't warn in self-hosted code (where the further presence of + // JS::RuntimeOptions::werror() would result in impossible-to-avoid + // errors to entirely-innocent client code). + if (script->selfHosted()) + return true; - /* Don't warn repeatedly for the same script. */ - if (!script || script->warnedAboutUndefinedProp()) - return true; + // We may just be checking if that object has an iterator. + if (JSID_IS_ATOM(id, cx->names().iteratorIntrinsic)) + return true; - /* - * Don't warn in self-hosted code (where the further presence of - * JS::RuntimeOptions::werror() would result in impossible-to-avoid - * errors to entirely-innocent client code). - */ - if (script->selfHosted()) - return true; + // Do not warn about tests like (obj[prop] == undefined). + pc += js_CodeSpec[op].length; + if (Detecting(cx, script, pc)) + return true; - /* We may just be checking if that object has an iterator. */ - if (JSID_IS_ATOM(id, cx->names().iteratorIntrinsic)) - return true; + unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT; + script->setWarnedAboutUndefinedProp(); - /* Do not warn about tests like (obj[prop] == undefined). */ - pc += js_CodeSpec[op].length; - if (Detecting(cx, script, pc)) - return true; + // Ok, bad undefined property reference: whine about it. + RootedValue val(cx, IdToValue(id)); + return js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP, JSDVG_IGNORE_STACK, val, + js::NullPtr(), nullptr, nullptr); +} - unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT; - script->setWarnedAboutUndefinedProp(); +/* The NoGC version of GetNonexistentProperty, present only to make types line up. */ +bool +GetNonexistentProperty(JSContext *cx, NativeObject *obj, jsid id, JSObject *receiver, + FakeMutableHandle vp) +{ + return false; +} - /* Ok, bad undefined property reference: whine about it. */ - RootedValue val(cx, IdToValue(id)); - if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP, - JSDVG_IGNORE_STACK, val, js::NullPtr(), - nullptr, nullptr)) - { - return false; - } - } - return true; - } +static inline bool +GeneralizedGetProperty(JSContext *cx, HandleObject obj, HandleId id, HandleObject receiver, + MutableHandleValue vp) +{ + JS_CHECK_RECURSION(cx, return false); + return GetProperty(cx, obj, receiver, id, vp); +} + +static inline bool +GeneralizedGetProperty(JSContext *cx, JSObject *obj, jsid id, JSObject *receiver, + FakeMutableHandle vp) +{ + JS_CHECK_RECURSION_DONT_REPORT(cx, return false); + return GetPropertyNoGC(cx, obj, receiver, id, vp.address()); +} + +template +static MOZ_ALWAYS_INLINE bool +NativeGetPropertyInline(JSContext *cx, + typename MaybeRooted::HandleType obj, + typename MaybeRooted::HandleType receiver, + typename MaybeRooted::HandleType id, + typename MaybeRooted::MutableHandleType vp) +{ + typename MaybeRooted::RootType pobj(cx, obj); + typename MaybeRooted::RootType shape(cx); - if (!obj2->isNative()) { - if (!allowGC) + // This loop isn't explicit in the spec algorithm. See the comment on step + // 4.d below. + for (;;) { + // Steps 2-3. ('done' is a SpiderMonkey-specific thing, used below.) + bool done; + if (!LookupOwnPropertyInline(cx, pobj, id, &shape, &done)) return false; - HandleObject obj2Handle = MaybeRooted::toHandle(obj2); - HandleObject receiverHandle = MaybeRooted::toHandle(receiver); - HandleId idHandle = MaybeRooted::toHandle(id); - MutableHandleValue vpHandle = MaybeRooted::toMutableHandle(vp); - return obj2->template is() - ? Proxy::get(cx, obj2Handle, receiverHandle, idHandle, vpHandle) - : GetProperty(cx, obj2Handle, obj2Handle, idHandle, vpHandle); - } - typename MaybeRooted::HandleType nobj2 = - MaybeRooted::template downcastHandle(obj2); + if (shape) { + // Steps 5-8. Special case for dense elements because + // GetExistingProperty doesn't support those. + if (IsImplicitDenseOrTypedArrayElement(shape)) { + vp.set(pobj->getDenseOrTypedArrayElement(JSID_TO_INT(id))); + return true; + } + return GetExistingProperty(cx, receiver, pobj, shape, vp); + } - if (IsImplicitDenseOrTypedArrayElement(shape)) { - vp.set(nobj2->getDenseOrTypedArrayElement(JSID_TO_INT(id))); - return true; - } + // Steps 4.a-b. The check for 'done' on this next line is tricky. + // done can be true in exactly these unlikely-sounding cases: + // - We're looking up an element, and pobj is a TypedArray that + // doesn't have that many elements. + // - We're being called from a resolve hook to assign to the property + // being resolved. + // What they all have in common is we do not want to keep walking + // the prototype chain. + RootedObject proto(cx, done ? nullptr : pobj->getProto()); - // This call site is hot -- use the always-inlined variant of - // NativeGetExistingProperty(). - if (!NativeGetExistingPropertyInline(cx, obj, receiver, nobj2, shape, vp)) - return false; + // Step 4.c. The spec algorithm simply returns undefined if proto is + // null, but see the comment on GetNonexistentProperty. + if (!proto) + return GetNonexistentProperty(cx, obj, id, receiver, vp); - return true; + // Step 4.d. If the prototype is also native, this step is a + // recursive tail call, and we don't need to go through all the + // plumbing of JSObject::getGeneric; the top of the loop is where + // we're going to end up anyway. But if pobj is non-native, + // that optimization would be incorrect. + if (!proto->isNative()) + return GeneralizedGetProperty(cx, proto, id, receiver, vp); + + pobj = &proto->as(); + } } bool js::NativeGetProperty(JSContext *cx, HandleNativeObject obj, HandleObject receiver, HandleId id, MutableHandleValue vp) { - /* This call site is hot -- use the always-inlined variant of GetPropertyHelper(). */ - return GetPropertyHelperInline(cx, obj, receiver, id, vp); + return NativeGetPropertyInline(cx, obj, receiver, id, vp); } bool js::NativeGetPropertyNoGC(JSContext *cx, NativeObject *obj, JSObject *receiver, jsid id, Value *vp) { - AutoAssertNoException nogc(cx); - return GetPropertyHelperInline(cx, obj, receiver, id, vp); + AutoAssertNoException noexc(cx); + return NativeGetPropertyInline(cx, obj, receiver, id, vp); } -bool -js::NativeGetElement(JSContext *cx, HandleNativeObject obj, HandleObject receiver, uint32_t index, - MutableHandleValue vp) -{ - RootedId id(cx); - if (!IndexToId(cx, index, &id)) - return false; - /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */ - return GetPropertyHelperInline(cx, obj, receiver, id, vp); -} +/*** [[Set]] *************************************************************************************/ static bool MaybeReportUndeclaredVarAssignment(JSContext *cx, JSString *propname) @@ -1796,9 +1889,6 @@ JSMSG_UNDECLARED_VAR, bytes.ptr()); } - -/*** [[Set]] *************************************************************************************/ - /* * When a [[Set]] operation finds no existing property with the given id * or finds a writable data property on the prototype chain, we end up here. @@ -2069,7 +2159,8 @@ RootedNativeObject pobj(cx, obj); // This loop isn't explicit in the spec algorithm. See the comment on step - // 4.c.i below. + // 4.c.i below. (There's a very similar loop in the NativeGetProperty + // implementation, but unfortunately not similar enough to common up.) for (;;) { // Steps 2-3. ('done' is a SpiderMonkey-specific thing, used below.) bool done; @@ -2105,10 +2196,10 @@ // at all, but they do go through this function. So check for // unqualified assignment to a nonexistent global (a strict error). if (!qualified) { - RootedObject pobj(cx); - if (!LookupProperty(cx, proto, id, &pobj, &shape)) + bool found; + if (!HasProperty(cx, proto, id, &found)) return false; - if (!shape) + if (!found) return SetNonexistentProperty(cx, obj, receiver, id, qualified, vp, strict); } @@ -2128,6 +2219,9 @@ return NativeSetProperty(cx, obj, receiver, id, Qualified, vp, strict); } + +/* * */ + bool js::NativeSetPropertyAttributes(JSContext *cx, HandleNativeObject obj, HandleId id, unsigned *attrsp) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/NativeObject.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/NativeObject.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/NativeObject.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/NativeObject.h 2015-02-03 14:33:44.000000000 +0000 @@ -364,7 +364,7 @@ static_assert(offsetof(NativeObject, shape_) == offsetof(shadow::Object, shape), "shadow shape must match actual shape"); - static_assert(offsetof(NativeObject, type_) == offsetof(shadow::Object, type), + static_assert(offsetof(NativeObject, group_) == offsetof(shadow::Object, group), "shadow type must match actual type"); static_assert(offsetof(NativeObject, slots_) == offsetof(shadow::Object, slots), "shadow slots must match actual slots"); @@ -373,6 +373,8 @@ static_assert(MAX_FIXED_SLOTS <= Shape::FIXED_SLOTS_MAX, "verify numFixedSlots() bitfield is big enough"); + static_assert(sizeof(NativeObject) + MAX_FIXED_SLOTS * sizeof(Value) == JSObject::MAX_BYTE_SIZE, + "inconsistent maximum object size"); } public: @@ -410,6 +412,17 @@ // the new properties. void setLastPropertyShrinkFixedSlots(Shape *shape); + // As for setLastProperty(), but changes the class associated with the + // object to a non-native one. This leaves the object with a type and shape + // that are (temporarily) inconsistent. + void setLastPropertyMakeNonNative(Shape *shape); + + // As for setLastProperty(), but changes the class associated with the + // object to a native one. The object's type has already been changed, and + // this brings the shape into sync with it. + static void setLastPropertyMakeNative(ExclusiveContext *cx, HandleNativeObject obj, + HandleShape shape); + protected: #ifdef DEBUG void checkShapeConsistency(); @@ -801,8 +814,6 @@ static bool rollbackProperties(ExclusiveContext *cx, HandleNativeObject obj, uint32_t slotSpan); - inline bool setSlotIfHasType(Shape *shape, const Value &value, - bool overwriting = true); inline void setSlotWithType(ExclusiveContext *cx, Shape *shape, const Value &value, bool overwriting = true); @@ -1322,8 +1333,15 @@ NativeLookupElement(JSContext *cx, HandleNativeObject obj, uint32_t index, MutableHandleObject objp, MutableHandleShape propp); +/* + * Get a property from `receiver`, after having already done a lookup and found + * the property on a native object `obj`. + * + * `shape` must not be null and must not be an implicit dense property. It must + * be present in obj's shape chain. + */ extern bool -NativeGetExistingProperty(JSContext *cx, HandleObject obj, HandleNativeObject pobj, +NativeGetExistingProperty(JSContext *cx, HandleObject receiver, HandleNativeObject obj, HandleShape shape, MutableHandle vp); extern bool @@ -1369,8 +1387,7 @@ js::GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, MutableHandleValue vp) { - MOZ_ASSERT(!!obj->getOps()->getGeneric == !!obj->getOps()->getProperty); - if (GenericIdOp op = obj->getOps()->getGeneric) + if (GetPropertyOp op = obj->getOps()->getProperty) return op(cx, obj, receiver, id, vp); return NativeGetProperty(cx, obj.as(), receiver, id, vp); } @@ -1378,7 +1395,7 @@ inline bool js::GetPropertyNoGC(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp) { - if (obj->getOps()->getGeneric) + if (obj->getOps()->getProperty) return false; return NativeGetPropertyNoGC(cx, &obj->as(), receiver, id, vp); } @@ -1387,7 +1404,7 @@ js::SetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, MutableHandleValue vp, bool strict) { - if (obj->getOps()->setGeneric) + if (obj->getOps()->setProperty) return JSObject::nonNativeSetProperty(cx, obj, receiver, id, vp, strict); return NativeSetProperty(cx, obj.as(), receiver, id, Qualified, vp, strict); } @@ -1396,7 +1413,7 @@ js::SetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index, MutableHandleValue vp, bool strict) { - if (obj->getOps()->setElement) + if (obj->getOps()->setProperty) return JSObject::nonNativeSetElement(cx, obj, receiver, index, vp, strict); return NativeSetElement(cx, obj.as(), receiver, index, vp, strict); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/NativeObject-inl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/NativeObject-inl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/NativeObject-inl.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/NativeObject-inl.h 2015-02-03 14:33:44.000000000 +0000 @@ -99,7 +99,7 @@ inline void NativeObject::setDenseElementHole(ExclusiveContext *cx, uint32_t index) { - types::MarkTypeObjectFlags(cx, this, types::OBJECT_FLAG_NON_PACKED); + types::MarkObjectGroupFlags(cx, this, types::OBJECT_FLAG_NON_PACKED); setDenseElement(index, MagicValue(JS_ELEMENTS_HOLE)); } @@ -107,9 +107,9 @@ NativeObject::removeDenseElementForSparseIndex(ExclusiveContext *cx, HandleNativeObject obj, uint32_t index) { - types::MarkTypeObjectFlags(cx, obj, - types::OBJECT_FLAG_NON_PACKED | - types::OBJECT_FLAG_SPARSE_INDEXES); + types::MarkObjectGroupFlags(cx, obj, + types::OBJECT_FLAG_NON_PACKED | + types::OBJECT_FLAG_SPARSE_INDEXES); if (obj->containsDenseElement(index)) obj->setDenseElement(index, MagicValue(JS_ELEMENTS_HOLE)); } @@ -124,7 +124,7 @@ NativeObject::markDenseElementsNotPacked(ExclusiveContext *cx) { MOZ_ASSERT(isNative()); - MarkTypeObjectFlags(cx, this, types::OBJECT_FLAG_NON_PACKED); + MarkObjectGroupFlags(cx, this, types::OBJECT_FLAG_NON_PACKED); } inline void @@ -283,10 +283,10 @@ HandleNativeObject templateObject) { RootedShape shape(cx, templateObject->lastProperty()); - RootedTypeObject type(cx, templateObject->type()); + RootedObjectGroup group(cx, templateObject->group()); MOZ_ASSERT(!templateObject->denseElementsAreCopyOnWrite()); - JSObject *baseObj = create(cx, kind, heap, shape, type); + JSObject *baseObj = create(cx, kind, heap, shape, group); if (!baseObj) return nullptr; NativeObject *obj = &baseObj->as(); @@ -311,19 +311,6 @@ return obj; } -inline bool -NativeObject::setSlotIfHasType(Shape *shape, const Value &value, bool overwriting) -{ - if (!types::HasTypePropertyId(this, shape->propid(), value)) - return false; - setSlot(shape->slot(), value); - - if (overwriting) - shape->setOverwritten(); - - return true; -} - inline void NativeObject::setSlotWithType(ExclusiveContext *cx, Shape *shape, const Value &value, bool overwriting) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/PIC.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/PIC.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/PIC.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/PIC.cpp 2015-02-03 14:33:44.000000000 +0000 @@ -19,12 +19,6 @@ using namespace js; using namespace js::gc; -#ifdef JS_HAS_SYMBOLS -#define STD_ITERATOR_ID SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator) -#else -#define STD_ITERATOR_ID ::js::NameToId(cx->names().std_iterator) -#endif - bool js::ForOfPIC::Chain::initialize(JSContext *cx) { @@ -52,7 +46,7 @@ disabled_ = true; // Look up Array.prototype[@@iterator], ensure it's a slotful shape. - Shape *iterShape = arrayProto->lookup(cx, STD_ITERATOR_ID); + Shape *iterShape = arrayProto->lookup(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator)); if (!iterShape || !iterShape->hasSlot() || !iterShape->hasDefaultGetter()) return true; @@ -151,7 +145,7 @@ return true; // Ensure array doesn't define @@iterator directly. - if (array->lookup(cx, STD_ITERATOR_ID)) + if (array->lookup(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator))) return true; // Good to optimize now, create stub to add. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ProxyObject.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ProxyObject.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ProxyObject.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ProxyObject.cpp 2015-02-03 14:33:45.000000000 +0000 @@ -35,7 +35,7 @@ */ if (proto.isObject() && !options.singleton() && !clasp->isDOMClass()) { RootedObject protoObj(cx, proto.toObject()); - if (!JSObject::setNewTypeUnknown(cx, clasp, protoObj)) + if (!JSObject::setNewGroupUnknown(cx, clasp, protoObj)) return nullptr; } @@ -66,7 +66,7 @@ /* Don't track types of properties of non-DOM and non-singleton proxies. */ if (newKind != SingletonObject && !clasp->isDOMClass()) - MarkTypeObjectUnknownProperties(cx, proxy->type()); + MarkObjectGroupUnknownProperties(cx, proxy->group()); return proxy; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/RegExpObject.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/RegExpObject.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/RegExpObject.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/RegExpObject.cpp 2015-02-03 14:33:45.000000000 +0000 @@ -62,16 +62,16 @@ } bool -RegExpObjectBuilder::getOrCreateClone(HandleTypeObject type) +RegExpObjectBuilder::getOrCreateClone(HandleObjectGroup group) { MOZ_ASSERT(!reobj_); - MOZ_ASSERT(type->clasp() == &RegExpObject::class_); + MOZ_ASSERT(group->clasp() == &RegExpObject::class_); - JSObject *parent = type->proto().toObject()->getParent(); + JSObject *parent = group->proto().toObject()->getParent(); // Note: RegExp objects are always allocated in the tenured heap. This is // not strictly required, but simplifies embedding them in jitcode. - reobj_ = NewObjectWithType(cx->asJSContext(), type, parent, TenuredObject); + reobj_ = NewObjectWithGroup(cx->asJSContext(), group, parent, TenuredObject); if (!reobj_) return false; reobj_->initPrivate(nullptr); @@ -104,8 +104,8 @@ RegExpObject * RegExpObjectBuilder::clone(Handle other) { - RootedTypeObject type(cx, other->type()); - if (!getOrCreateClone(type)) + RootedObjectGroup group(cx, other->group()); + if (!getOrCreateClone(group)) return nullptr; /* @@ -706,13 +706,13 @@ if (!templateObject) return matchResultTemplateObject_; // = nullptr - // Create a new type for the template. + // Create a new group for the template. Rooted proto(cx, templateObject->getTaggedProto()); - types::TypeObject *type = - cx->compartment()->types.newTypeObject(cx, templateObject->getClass(), proto); - if (!type) + types::ObjectGroup *group = + cx->compartment()->types.newObjectGroup(cx, templateObject->getClass(), proto); + if (!group) return matchResultTemplateObject_; // = nullptr - templateObject->setType(type); + templateObject->setGroup(group); /* Set dummy index property */ RootedValue index(cx, Int32Value(0)); @@ -859,7 +859,7 @@ RegExpObjectBuilder builder(cx); Rooted regex(cx, &obj_->as()); JSObject *res = builder.clone(regex); - MOZ_ASSERT_IF(res, res->type() == regex->type()); + MOZ_ASSERT_IF(res, res->group() == regex->group()); return res; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/RegExpObject.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/RegExpObject.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/RegExpObject.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/RegExpObject.h 2015-02-03 14:33:45.000000000 +0000 @@ -68,7 +68,7 @@ Rooted reobj_; bool getOrCreate(); - bool getOrCreateClone(HandleTypeObject type); + bool getOrCreateClone(HandleObjectGroup group); public: explicit RegExpObjectBuilder(ExclusiveContext *cx, RegExpObject *reobj = nullptr); @@ -489,6 +489,7 @@ { if (obj->is()) return obj->as().getShared(cx, g); + MOZ_ASSERT(Proxy::objectClassIs(obj, ESClass_RegExp, cx)); return Proxy::regexp_toShared(cx, obj, g); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/RegExpStatics.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/RegExpStatics.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/RegExpStatics.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/RegExpStatics.cpp 2015-02-03 14:33:45.000000000 +0000 @@ -76,7 +76,7 @@ // always be performed). MOZ_ASSERT_IF(cx->global()->hasRegExpStatics(), this == cx->global()->getRegExpStatics(cx)); - types::MarkTypeObjectFlags(cx, cx->global(), types::OBJECT_FLAG_REGEXP_FLAGS_SET); + types::MarkObjectGroupFlags(cx, cx->global(), types::OBJECT_FLAG_REGEXP_FLAGS_SET); } bool diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Runtime.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Runtime.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Runtime.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Runtime.cpp 2015-02-03 14:33:45.000000000 +0000 @@ -73,21 +73,10 @@ PerThreadData::PerThreadData(JSRuntime *runtime) : PerThreadDataFriendFields(), runtime_(runtime), - jitTop(nullptr), - jitJSContext(nullptr), - jitActivation(nullptr), - jitStackLimit_(0xbad), #ifdef JS_TRACE_LOGGING traceLogger(nullptr), #endif - activation_(nullptr), - profilingActivation_(nullptr), - asmJSActivationStack_(nullptr), autoFlushICache_(nullptr), -#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) - simulator_(nullptr), - simulatorStackLimit_(0), -#endif dtoaState(nullptr), suppressGC(0), #ifdef DEBUG @@ -100,10 +89,6 @@ { if (dtoaState) js_DestroyDtoaState(dtoaState); - -#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) - js_delete(simulator_); -#endif } bool @@ -113,9 +98,6 @@ if (!dtoaState) return false; - if (!regexpStack.init()) - return false; - return true; } @@ -132,6 +114,13 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime) : mainThread(this), + jitTop(nullptr), + jitJSContext(nullptr), + jitActivation(nullptr), + jitStackLimit_(0xbad), + activation_(nullptr), + profilingActivation_(nullptr), + asmJSActivationStack_(nullptr), parentRuntime(parentRuntime), interrupt_(false), telemetryCallback(nullptr), @@ -168,7 +157,7 @@ gc(thisFromCtor()), gcInitialized(false), #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) - simulatorRuntime_(nullptr), + simulator_(nullptr), #endif scriptAndCountsVector(nullptr), NaNValue(DoubleNaNValue()), @@ -274,6 +263,9 @@ if (!mainThread.init()) return false; + if (!regexpStack.init()) + return false; + js::TlsPerThreadData.set(&mainThread); if (CanUseExtraThreads()) @@ -324,8 +316,8 @@ dateTimeInfo.updateTimeZoneAdjustment(); #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) - simulatorRuntime_ = js::jit::CreateSimulatorRuntime(); - if (!simulatorRuntime_) + simulator_ = js::jit::Simulator::Create(); + if (!simulator_) return false; #endif @@ -441,7 +433,7 @@ gc.nursery.disable(); #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) - js::jit::DestroySimulatorRuntime(simulatorRuntime_); + js::jit::Simulator::Destroy(simulator_); #endif DebugOnly oldCount = liveRuntimesCount--; @@ -594,7 +586,7 @@ } void -PerThreadData::resetJitStackLimit() +JSRuntime::resetJitStackLimit() { // Note that, for now, we use the untrusted limit for ion. This is fine, // because it's the most conservative limit, and if we hit it, we'll bail @@ -602,12 +594,12 @@ #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) jitStackLimit_ = jit::Simulator::StackLimit(); #else - jitStackLimit_ = nativeStackLimit[StackForUntrustedScript]; + jitStackLimit_ = mainThread.nativeStackLimit[StackForUntrustedScript]; #endif } void -PerThreadData::initJitStackLimit() +JSRuntime::initJitStackLimit() { resetJitStackLimit(); } @@ -616,7 +608,7 @@ JSRuntime::requestInterrupt(InterruptMode mode) { interrupt_ = true; - mainThread.jitStackLimit_ = UINTPTR_MAX; + jitStackLimit_ = UINTPTR_MAX; if (mode == JSRuntime::RequestInterruptUrgent) InterruptRunningJitCode(this); @@ -626,9 +618,9 @@ JSRuntime::handleInterrupt(JSContext *cx) { MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime())); - if (interrupt_ || mainThread.jitStackLimit_ == UINTPTR_MAX) { + if (interrupt_ || jitStackLimit_ == UINTPTR_MAX) { interrupt_ = false; - mainThread.resetJitStackLimit(); + resetJitStackLimit(); return InvokeInterruptCallback(cx); } return true; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Runtime.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Runtime.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Runtime.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Runtime.h 2015-02-03 14:33:45.000000000 +0000 @@ -63,13 +63,13 @@ struct DtoaState; -extern void +extern MOZ_COLD void js_ReportOutOfMemory(js::ExclusiveContext *cx); -extern void +extern MOZ_COLD void js_ReportAllocationOverflow(js::ExclusiveContext *maybecx); -extern void +extern MOZ_COLD void js_ReportOverRecursed(js::ExclusiveContext *cx); namespace js { @@ -85,7 +85,6 @@ class JitActivation; struct PcScriptCache; class Simulator; -class SimulatorRuntime; struct AutoFlushICache; class CompileRuntime; } @@ -288,8 +287,8 @@ inline bool lookupGlobal(const Class *clasp, js::GlobalObject *global, gc::AllocKind kind, EntryIndex *pentry); - bool lookupType(js::types::TypeObject *type, gc::AllocKind kind, EntryIndex *pentry) { - return lookup(type->clasp(), type, kind, pentry); + bool lookupGroup(js::types::ObjectGroup *group, gc::AllocKind kind, EntryIndex *pentry) { + return lookup(group->clasp(), group, kind, pentry); } /* @@ -307,11 +306,11 @@ inline void fillGlobal(EntryIndex entry, const Class *clasp, js::GlobalObject *global, gc::AllocKind kind, NativeObject *obj); - void fillType(EntryIndex entry, js::types::TypeObject *type, gc::AllocKind kind, - NativeObject *obj) + void fillGroup(EntryIndex entry, js::types::ObjectGroup *group, gc::AllocKind kind, + NativeObject *obj) { - MOZ_ASSERT(obj->type() == type); - return fill(entry, type->clasp(), type, kind, obj); + MOZ_ASSERT(obj->group() == group); + return fill(entry, group->clasp(), group, kind, obj); } /* Invalidate any entries which might produce an object with shape/proto. */ @@ -348,7 +347,7 @@ static void copyCachedToObject(JSObject *dst, JSObject *src, gc::AllocKind kind) { js_memcpy(dst, src, gc::Arena::thingSize(kind)); Shape::writeBarrierPost(dst->shape_, &dst->shape_); - types::TypeObject::writeBarrierPost(dst->type_, &dst->type_); + types::ObjectGroup::writeBarrierPost(dst->group_, &dst->group_); } }; @@ -481,6 +480,11 @@ */ class PerThreadData : public PerThreadDataFriendFields { +#ifdef DEBUG + // Grant access to runtime_. + friend void js::AssertCurrentThreadCanLock(RuntimeLock which); +#endif + /* * Backpointer to the full shared JSRuntime* with which this * thread is associated. This is private because accessing the @@ -491,21 +495,93 @@ JSRuntime *runtime_; public: +#ifdef JS_TRACE_LOGGING + TraceLoggerThread *traceLogger; +#endif + + /* Pointer to the current AutoFlushICache. */ + js::jit::AutoFlushICache *autoFlushICache_; + + public: + /* State used by jsdtoa.cpp. */ + DtoaState *dtoaState; + /* - * We save all conservative scanned roots in this vector so that - * conservative scanning can be "replayed" deterministically. In DEBUG mode, - * this allows us to run a non-incremental GC after every incremental GC to - * ensure that no objects were missed. + * When this flag is non-zero, any attempt to GC will be skipped. It is used + * to suppress GC when reporting an OOM (see js_ReportOutOfMemory) and in + * debugging facilities that cannot tolerate a GC and would rather OOM + * immediately, such as utilities exposed to GDB. Setting this flag is + * extremely dangerous and should only be used when in an OOM situation or + * in non-exposed debugging facilities. */ + int32_t suppressGC; + #ifdef DEBUG - struct SavedGCRoot { - void *thing; - JSGCTraceKind kind; + // Whether this thread is actively Ion compiling. + bool ionCompiling; +#endif + + // Number of active bytecode compilation on this thread. + unsigned activeCompilations; + + explicit PerThreadData(JSRuntime *runtime); + ~PerThreadData(); + + bool init(); + + bool associatedWith(const JSRuntime *rt) { return runtime_ == rt; } + inline JSRuntime *runtimeFromMainThread(); + inline JSRuntime *runtimeIfOnOwnerThread(); + + inline bool exclusiveThreadsPresent(); + inline void addActiveCompilation(); + inline void removeActiveCompilation(); + + // For threads which may be associated with different runtimes, depending + // on the work they are doing. + class MOZ_STACK_CLASS AutoEnterRuntime + { + PerThreadData *pt; + + public: + AutoEnterRuntime(PerThreadData *pt, JSRuntime *rt) + : pt(pt) + { + MOZ_ASSERT(!pt->runtime_); + pt->runtime_ = rt; + } - SavedGCRoot(void *thing, JSGCTraceKind kind) : thing(thing), kind(kind) {} + ~AutoEnterRuntime() { + pt->runtime_ = nullptr; + } }; - js::Vector gcSavedRoots; + + js::jit::AutoFlushICache *autoFlushICache() const; + void setAutoFlushICache(js::jit::AutoFlushICache *afc); + +#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) + js::jit::Simulator *simulator() const; #endif +}; + +class AutoLockForExclusiveAccess; + +} // namespace js + +struct JSRuntime : public JS::shadow::Runtime, + public js::MallocProvider +{ + /* + * Per-thread data for the main thread that is associated with + * this JSRuntime, as opposed to any worker threads used in + * parallel sections. See definition of |PerThreadData| struct + * above for more details. + * + * NB: This field is statically asserted to be at offset + * sizeof(js::shadow::Runtime). See + * PerThreadDataFriendFields::getMainThread. + */ + js::PerThreadData mainThread; /* * If Baseline or Ion code is on the stack, and has called into C++, this @@ -530,7 +606,7 @@ private: mozilla::Atomic jitStackLimit_; void resetJitStackLimit(); - friend struct ::JSRuntime; + public: void initJitStackLimit(); @@ -538,14 +614,10 @@ // For read-only JIT use: void *addressOfJitStackLimit() { return &jitStackLimit_; } - static size_t offsetOfJitStackLimit() { return offsetof(PerThreadData, jitStackLimit_); } + static size_t offsetOfJitStackLimit() { return offsetof(JSRuntime, jitStackLimit_); } // Information about the heap allocated backtrack stack used by RegExp JIT code. - irregexp::RegExpStack regexpStack; - -#ifdef JS_TRACE_LOGGING - TraceLoggerThread *traceLogger; -#endif + js::irregexp::RegExpStack regexpStack; private: friend class js::Activation; @@ -554,7 +626,7 @@ friend class js::AsmJSActivation; friend class js::jit::CompileRuntime; #ifdef DEBUG - friend void js::AssertCurrentThreadCanLock(RuntimeLock which); + friend void js::AssertCurrentThreadCanLock(js::RuntimeLock which); #endif /* @@ -572,23 +644,12 @@ /* See AsmJSActivation comment. */ js::AsmJSActivation * volatile asmJSActivationStack_; - /* Pointer to the current AutoFlushICache. */ - js::jit::AutoFlushICache *autoFlushICache_; - -#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) - js::jit::Simulator *simulator_; - uintptr_t simulatorStackLimit_; -#endif - public: js::Activation *const *addressOfActivation() const { return &activation_; } - static unsigned offsetOfAsmJSActivationStackReadOnly() { - return offsetof(PerThreadData, asmJSActivationStack_); - } static unsigned offsetOfActivation() { - return offsetof(PerThreadData, activation_); + return offsetof(JSRuntime, activation_); } js::Activation *profilingActivation() const { @@ -598,115 +659,21 @@ return (void*) &profilingActivation_; } static unsigned offsetOfProfilingActivation() { - return offsetof(PerThreadData, profilingActivation_); + return offsetof(JSRuntime, profilingActivation_); } js::AsmJSActivation *asmJSActivationStack() const { return asmJSActivationStack_; } static js::AsmJSActivation *innermostAsmJSActivation() { - PerThreadData *ptd = TlsPerThreadData.get(); - return ptd ? ptd->asmJSActivationStack_ : nullptr; + js::PerThreadData *ptd = js::TlsPerThreadData.get(); + return ptd ? ptd->runtimeFromMainThread()->asmJSActivationStack_ : nullptr; } js::Activation *activation() const { return activation_; } - /* State used by jsdtoa.cpp. */ - DtoaState *dtoaState; - - /* - * When this flag is non-zero, any attempt to GC will be skipped. It is used - * to suppress GC when reporting an OOM (see js_ReportOutOfMemory) and in - * debugging facilities that cannot tolerate a GC and would rather OOM - * immediately, such as utilities exposed to GDB. Setting this flag is - * extremely dangerous and should only be used when in an OOM situation or - * in non-exposed debugging facilities. - */ - int32_t suppressGC; - -#ifdef DEBUG - // Whether this thread is actively Ion compiling. - bool ionCompiling; -#endif - - // Number of active bytecode compilation on this thread. - unsigned activeCompilations; - - explicit PerThreadData(JSRuntime *runtime); - ~PerThreadData(); - - bool init(); - - bool associatedWith(const JSRuntime *rt) { return runtime_ == rt; } - inline JSRuntime *runtimeFromMainThread(); - inline JSRuntime *runtimeIfOnOwnerThread(); - - inline bool exclusiveThreadsPresent(); - inline void addActiveCompilation(); - inline void removeActiveCompilation(); - - // For threads which may be associated with different runtimes, depending - // on the work they are doing. - class MOZ_STACK_CLASS AutoEnterRuntime - { - PerThreadData *pt; - - public: - AutoEnterRuntime(PerThreadData *pt, JSRuntime *rt) - : pt(pt) - { - MOZ_ASSERT(!pt->runtime_); - pt->runtime_ = rt; -#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) - // The simulator has a pointer to its SimulatorRuntime, but helper threads - // don't have a simulator as they don't run JIT code so this pointer need not - // be updated. All the paths that the helper threads use access the - // SimulatorRuntime via the PerThreadData. - MOZ_ASSERT(!pt->simulator_); -#endif - } - - ~AutoEnterRuntime() { - pt->runtime_ = nullptr; -#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) - // Check that helper threads have not run JIT code and/or added a simulator. - MOZ_ASSERT(!pt->simulator_); -#endif - } - }; - - js::jit::AutoFlushICache *autoFlushICache() const; - void setAutoFlushICache(js::jit::AutoFlushICache *afc); - -#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) - js::jit::Simulator *simulator() const; - void setSimulator(js::jit::Simulator *sim); - js::jit::SimulatorRuntime *simulatorRuntime() const; - uintptr_t *addressOfSimulatorStackLimit(); -#endif -}; - -class AutoLockForExclusiveAccess; - -} // namespace js - -struct JSRuntime : public JS::shadow::Runtime, - public js::MallocProvider -{ - /* - * Per-thread data for the main thread that is associated with - * this JSRuntime, as opposed to any worker threads used in - * parallel sections. See definition of |PerThreadData| struct - * above for more details. - * - * NB: This field is statically asserted to be at offset - * sizeof(js::shadow::Runtime). See - * PerThreadDataFriendFields::getMainThread. - */ - js::PerThreadData mainThread; - /* * If non-null, another runtime guaranteed to outlive this one and whose * permanent data may be used by this one where possible. @@ -988,7 +955,7 @@ } #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) - js::jit::SimulatorRuntime *simulatorRuntime_; + js::jit::Simulator *simulator_; #endif public: @@ -997,8 +964,8 @@ } #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) - js::jit::SimulatorRuntime *simulatorRuntime() const; - void setSimulatorRuntime(js::jit::SimulatorRuntime *srt); + js::jit::Simulator *simulator() const; + uintptr_t *addressOfSimulatorStackLimit(); #endif /* Strong references on scripts held for PCCount profiling API. */ @@ -1438,7 +1405,7 @@ static inline JSContext * GetJSContextFromJitCode() { - JSContext *cx = TlsPerThreadData.get()->jitJSContext; + JSContext *cx = js::TlsPerThreadData.get()->runtimeFromMainThread()->jitJSContext; MOZ_ASSERT(cx); return cx; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Runtime-inl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Runtime-inl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Runtime-inl.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Runtime-inl.h 2015-02-03 14:33:45.000000000 +0000 @@ -50,11 +50,11 @@ JSObject *templateObj = reinterpret_cast(&entry->templateObject); - // Do an end run around JSObject::type() to avoid doing AutoUnprotectCell + // Do an end run around JSObject::group() to avoid doing AutoUnprotectCell // on the templateObj, which is not a GC thing and can't use runtimeFromAnyThread. - types::TypeObject *type = templateObj->type_; + types::ObjectGroup *group = templateObj->group_; - if (type->shouldPreTenure()) + if (group->shouldPreTenure()) heap = gc::TenuredHeap; if (cx->runtime()->gc.upcomingZealousGC()) @@ -64,13 +64,13 @@ // so that we trigger the right kind of GC automatically. if (allowGC) { mozilla::DebugOnly obj = - js::gc::AllocateObjectForCacheHit(cx, entry->kind, heap, type->clasp()); + js::gc::AllocateObjectForCacheHit(cx, entry->kind, heap, group->clasp()); MOZ_ASSERT(!obj); return nullptr; } MOZ_ASSERT(allowGC == NoGC); - JSObject *obj = js::gc::AllocateObjectForCacheHit(cx, entry->kind, heap, type->clasp()); + JSObject *obj = js::gc::AllocateObjectForCacheHit(cx, entry->kind, heap, group->clasp()); if (obj) { copyCachedToObject(obj, templateObj, entry->kind); probes::CreateObject(cx, obj); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ScopeObject.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ScopeObject.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ScopeObject.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ScopeObject.cpp 2015-02-03 14:33:45.000000000 +0000 @@ -129,16 +129,16 @@ } CallObject * -CallObject::create(JSContext *cx, HandleShape shape, HandleTypeObject type, uint32_t lexicalBegin) +CallObject::create(JSContext *cx, HandleShape shape, HandleObjectGroup group, uint32_t lexicalBegin) { - MOZ_ASSERT(!type->singleton(), - "passed a singleton type to create() (use createSingleton() " + MOZ_ASSERT(!group->singleton(), + "passed a singleton group to create() (use createSingleton() " "instead)"); gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots()); MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_)); kind = gc::GetBackgroundAllocKind(kind); - JSObject *obj = JSObject::create(cx, kind, gc::DefaultHeap, shape, type); + JSObject *obj = JSObject::create(cx, kind, gc::DefaultHeap, shape, group); if (!obj) return nullptr; @@ -153,15 +153,15 @@ MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_)); kind = gc::GetBackgroundAllocKind(kind); - RootedTypeObject type(cx, cx->getSingletonType(&class_, TaggedProto(nullptr))); - if (!type) + RootedObjectGroup group(cx, cx->getLazySingletonGroup(&class_, TaggedProto(nullptr))); + if (!group) return nullptr; - RootedObject obj(cx, JSObject::create(cx, kind, gc::TenuredHeap, shape, type)); + RootedObject obj(cx, JSObject::create(cx, kind, gc::TenuredHeap, shape, group)); if (!obj) return nullptr; - MOZ_ASSERT(obj->hasSingletonType(), - "type created inline above must be a singleton"); + MOZ_ASSERT(obj->isSingleton(), + "group created inline above must be a singleton"); obj->as().initRemainingSlotsToUninitializedLexicals(lexicalBegin); return &obj->as(); @@ -178,15 +178,15 @@ RootedShape shape(cx, script->bindings.callObjShape()); MOZ_ASSERT(shape->getObjectClass() == &class_); - RootedTypeObject type(cx, cx->getNewType(&class_, TaggedProto(nullptr))); - if (!type) + RootedObjectGroup group(cx, cx->getNewGroup(&class_, TaggedProto(nullptr))); + if (!group) return nullptr; gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots()); MOZ_ASSERT(CanBeFinalizedInBackground(kind, &class_)); kind = gc::GetBackgroundAllocKind(kind); - JSObject *obj = JSObject::create(cx, kind, heap, shape, type); + JSObject *obj = JSObject::create(cx, kind, heap, shape, group); if (!obj) return nullptr; @@ -216,7 +216,7 @@ if (script->treatAsRunOnce()) { Rooted ncallobj(cx, callobj); - if (!JSObject::setSingletonType(cx, ncallobj)) + if (!JSObject::setSingleton(cx, ncallobj)) return nullptr; return ncallobj; } @@ -326,8 +326,8 @@ { MOZ_ASSERT(IsNurseryAllocable(FINALIZE_KIND)); - RootedTypeObject type(cx, cx->getNewType(&class_, TaggedProto(nullptr))); - if (!type) + RootedObjectGroup group(cx, cx->getNewGroup(&class_, TaggedProto(nullptr))); + if (!group) return nullptr; RootedShape emptyDeclEnvShape(cx); @@ -338,7 +338,7 @@ return nullptr; RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND, heap, - emptyDeclEnvShape, type))); + emptyDeclEnvShape, group))); if (!obj) return nullptr; @@ -400,8 +400,8 @@ StaticWithObject * StaticWithObject::create(ExclusiveContext *cx) { - RootedTypeObject type(cx, cx->getNewType(&class_, TaggedProto(nullptr))); - if (!type) + RootedObjectGroup group(cx, cx->getNewGroup(&class_, TaggedProto(nullptr))); + if (!group) return nullptr; RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr), @@ -409,7 +409,7 @@ if (!shape) return nullptr; - RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, shape, type)); + RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, shape, group)); if (!obj) return nullptr; @@ -433,8 +433,8 @@ HandleObject staticWith, WithKind kind) { MOZ_ASSERT(staticWith->is()); - RootedTypeObject type(cx, cx->getNewType(&class_, TaggedProto(staticWith.get()))); - if (!type) + RootedObjectGroup group(cx, cx->getNewGroup(&class_, TaggedProto(staticWith.get()))); + if (!group) return nullptr; RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(staticWith), @@ -444,7 +444,7 @@ return nullptr; RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND, - gc::DefaultHeap, shape, type))); + gc::DefaultHeap, shape, group))); if (!obj) return nullptr; @@ -461,105 +461,35 @@ } static bool -with_LookupGeneric(JSContext *cx, HandleObject obj, HandleId id, - MutableHandleObject objp, MutableHandleShape propp) +with_LookupProperty(JSContext *cx, HandleObject obj, HandleId id, + MutableHandleObject objp, MutableHandleShape propp) { RootedObject actual(cx, &obj->as().object()); return LookupProperty(cx, actual, id, objp, propp); } static bool -with_LookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, - MutableHandleObject objp, MutableHandleShape propp) -{ - Rooted id(cx, NameToId(name)); - return with_LookupGeneric(cx, obj, id, objp, propp); -} - -static bool -with_LookupElement(JSContext *cx, HandleObject obj, uint32_t index, - MutableHandleObject objp, MutableHandleShape propp) -{ - RootedId id(cx); - if (!IndexToId(cx, index, &id)) - return false; - return with_LookupGeneric(cx, obj, id, objp, propp); -} - -static bool -with_DefineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue value, - JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) +with_DefineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue value, + JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) { RootedObject actual(cx, &obj->as().object()); return DefineProperty(cx, actual, id, value, getter, setter, attrs); } static bool -with_DefineProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value, - JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) -{ - Rooted id(cx, NameToId(name)); - return with_DefineGeneric(cx, obj, id, value, getter, setter, attrs); -} - -static bool -with_DefineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue value, - JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) -{ - RootedId id(cx); - if (!IndexToId(cx, index, &id)) - return false; - return with_DefineGeneric(cx, obj, id, value, getter, setter, attrs); -} - -static bool -with_GetGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, - MutableHandleValue vp) -{ - RootedObject actual(cx, &obj->as().object()); - return GetProperty(cx, actual, actual, id, vp); -} - -static bool -with_GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandlePropertyName name, +with_GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, MutableHandleValue vp) { - RootedId id(cx, NameToId(name)); - return with_GetGeneric(cx, obj, receiver, id, vp); -} - -static bool -with_GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index, - MutableHandleValue vp) -{ - RootedId id(cx); - if (!IndexToId(cx, index, &id)) - return false; - return with_GetGeneric(cx, obj, receiver, id, vp); -} - -static bool -with_SetGeneric(JSContext *cx, HandleObject obj, HandleId id, - MutableHandleValue vp, bool strict) -{ RootedObject actual(cx, &obj->as().object()); - return SetProperty(cx, actual, actual, id, vp, strict); + return GetProperty(cx, actual, actual, id, vp); } static bool -with_SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, +with_SetProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp, bool strict) { RootedObject actual(cx, &obj->as().object()); - return SetProperty(cx, actual, actual, name, vp, strict); -} - -static bool -with_SetElement(JSContext *cx, HandleObject obj, uint32_t index, - MutableHandleValue vp, bool strict) -{ - RootedObject actual(cx, &obj->as().object()); - return SetElement(cx, actual, actual, index, vp, strict); + return SetProperty(cx, actual, actual, id, vp, strict); } static bool @@ -571,14 +501,14 @@ } static bool -with_SetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp) +with_SetPropertyAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp) { RootedObject actual(cx, &obj->as().object()); return SetPropertyAttributes(cx, actual, id, attrsp); } static bool -with_DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded) +with_DeleteProperty(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded) { RootedObject actual(cx, &obj->as().object()); return DeleteProperty(cx, actual, id, succeeded); @@ -616,21 +546,13 @@ JS_NULL_CLASS_SPEC, JS_NULL_CLASS_EXT, { - with_LookupGeneric, with_LookupProperty, - with_LookupElement, - with_DefineGeneric, with_DefineProperty, - with_DefineElement, - with_GetGeneric, with_GetProperty, - with_GetElement, - with_SetGeneric, with_SetProperty, - with_SetElement, with_GetOwnPropertyDescriptor, - with_SetGenericAttributes, - with_DeleteGeneric, + with_SetPropertyAttributes, + with_DeleteProperty, nullptr, nullptr, /* watch/unwatch */ nullptr, /* getElements */ nullptr, /* enumerate (native enumeration of target doesn't work) */ @@ -641,8 +563,8 @@ /* static */ StaticEvalObject * StaticEvalObject::create(JSContext *cx, HandleObject enclosing) { - RootedTypeObject type(cx, cx->getNewType(&class_, TaggedProto(nullptr))); - if (!type) + RootedObjectGroup group(cx, cx->getNewGroup(&class_, TaggedProto(nullptr))); + if (!group) return nullptr; RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr), @@ -652,7 +574,7 @@ return nullptr; RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND, - gc::TenuredHeap, shape, type))); + gc::TenuredHeap, shape, group))); if (!obj) return nullptr; @@ -674,14 +596,14 @@ { MOZ_ASSERT(block->getClass() == &BlockObject::class_); - RootedTypeObject type(cx, cx->getNewType(&BlockObject::class_, TaggedProto(block.get()))); - if (!type) + RootedObjectGroup group(cx, cx->getNewGroup(&BlockObject::class_, TaggedProto(block.get()))); + if (!group) return nullptr; RootedShape shape(cx, block->lastProperty()); RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND, - gc::TenuredHeap, shape, type))); + gc::TenuredHeap, shape, group))); if (!obj) return nullptr; @@ -746,8 +668,8 @@ StaticBlockObject * StaticBlockObject::create(ExclusiveContext *cx) { - RootedTypeObject type(cx, cx->getNewType(&BlockObject::class_, TaggedProto(nullptr))); - if (!type) + RootedObjectGroup group(cx, cx->getNewGroup(&BlockObject::class_, TaggedProto(nullptr))); + if (!group) return nullptr; RootedShape emptyBlockShape(cx); @@ -756,7 +678,7 @@ if (!emptyBlockShape) return nullptr; - JSObject *obj = JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, emptyBlockShape, type); + JSObject *obj = JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, emptyBlockShape, group); if (!obj) return nullptr; @@ -958,8 +880,8 @@ /* static */ UninitializedLexicalObject * UninitializedLexicalObject::create(JSContext *cx, HandleObject enclosing) { - RootedTypeObject type(cx, cx->getNewType(&class_, TaggedProto(nullptr))); - if (!type) + RootedObjectGroup group(cx, cx->getNewGroup(&class_, TaggedProto(nullptr))); + if (!group) return nullptr; RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr), @@ -967,7 +889,7 @@ if (!shape) return nullptr; - RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::DefaultHeap, shape, type)); + RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::DefaultHeap, shape, group)); if (!obj) return nullptr; @@ -988,84 +910,30 @@ } static bool -uninitialized_LookupGeneric(JSContext *cx, HandleObject obj, HandleId id, - MutableHandleObject objp, MutableHandleShape propp) +uninitialized_LookupProperty(JSContext *cx, HandleObject obj, HandleId id, + MutableHandleObject objp, MutableHandleShape propp) { ReportUninitializedLexicalId(cx, id); return false; } static bool -uninitialized_LookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, - MutableHandleObject objp, MutableHandleShape propp) -{ - Rooted id(cx, NameToId(name)); - return uninitialized_LookupGeneric(cx, obj, id, objp, propp); -} - -static bool -uninitialized_LookupElement(JSContext *cx, HandleObject obj, uint32_t index, - MutableHandleObject objp, MutableHandleShape propp) -{ - RootedId id(cx); - if (!IndexToId(cx, index, &id)) - return false; - return uninitialized_LookupGeneric(cx, obj, id, objp, propp); -} - -static bool -uninitialized_GetGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, - MutableHandleValue vp) +uninitialized_GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, + MutableHandleValue vp) { ReportUninitializedLexicalId(cx, id); return false; } static bool -uninitialized_GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, - HandlePropertyName name, MutableHandleValue vp) -{ - RootedId id(cx, NameToId(name)); - return uninitialized_GetGeneric(cx, obj, receiver, id, vp); -} - -static bool -uninitialized_GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index, - MutableHandleValue vp) -{ - RootedId id(cx); - if (!IndexToId(cx, index, &id)) - return false; - return uninitialized_GetGeneric(cx, obj, receiver, id, vp); -} - -static bool -uninitialized_SetGeneric(JSContext *cx, HandleObject obj, HandleId id, - MutableHandleValue vp, bool strict) +uninitialized_SetProperty(JSContext *cx, HandleObject obj, HandleId id, + MutableHandleValue vp, bool strict) { ReportUninitializedLexicalId(cx, id); return false; } static bool -uninitialized_SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, - MutableHandleValue vp, bool strict) -{ - RootedId id(cx, NameToId(name)); - return uninitialized_SetGeneric(cx, obj, id, vp, strict); -} - -static bool -uninitialized_SetElement(JSContext *cx, HandleObject obj, uint32_t index, - MutableHandleValue vp, bool strict) -{ - RootedId id(cx); - if (!IndexToId(cx, index, &id)) - return false; - return uninitialized_SetGeneric(cx, obj, id, vp, strict); -} - -static bool uninitialized_GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, MutableHandle desc) { @@ -1074,14 +942,14 @@ } static bool -uninitialized_SetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp) +uninitialized_SetPropertyAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp) { ReportUninitializedLexicalId(cx, id); return false; } static bool -uninitialized_DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded) +uninitialized_DeleteProperty(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded) { ReportUninitializedLexicalId(cx, id); return false; @@ -1106,21 +974,13 @@ JS_NULL_CLASS_SPEC, JS_NULL_CLASS_EXT, { - uninitialized_LookupGeneric, uninitialized_LookupProperty, - uninitialized_LookupElement, - nullptr, /* defineGeneric */ nullptr, /* defineProperty */ - nullptr, /* defineElement */ - uninitialized_GetGeneric, uninitialized_GetProperty, - uninitialized_GetElement, - uninitialized_SetGeneric, uninitialized_SetProperty, - uninitialized_SetElement, uninitialized_GetOwnPropertyDescriptor, - uninitialized_SetGenericAttributes, - uninitialized_DeleteGeneric, + uninitialized_SetPropertyAttributes, + uninitialized_DeleteProperty, nullptr, nullptr, /* watch/unwatch */ nullptr, /* getElements */ nullptr, /* enumerate (native enumeration of target doesn't work) */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ScopeObject.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ScopeObject.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ScopeObject.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ScopeObject.h 2015-02-03 14:33:45.000000000 +0000 @@ -262,14 +262,14 @@ /* * Construct a bare-bones call object given a shape and a non-singleton - * type. The call object must be further initialized to be usable. + * group. The call object must be further initialized to be usable. */ static CallObject * - create(JSContext *cx, HandleShape shape, HandleTypeObject type, uint32_t lexicalBegin); + create(JSContext *cx, HandleShape shape, HandleObjectGroup group, uint32_t lexicalBegin); /* - * Construct a bare-bones call object given a shape and make it have - * singleton type. The call object must be initialized to be usable. + * Construct a bare-bones call object given a shape and make it into + * a singleton. The call object must be initialized to be usable. */ static CallObject * createSingleton(JSContext *cx, HandleShape shape, uint32_t lexicalBegin); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ScopeObject-inl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ScopeObject-inl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/ScopeObject-inl.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/ScopeObject-inl.h 2015-02-03 14:33:45.000000000 +0000 @@ -20,9 +20,9 @@ JS_STATIC_ASSERT(CallObject::RESERVED_SLOTS == BlockObject::RESERVED_SLOTS); // name may be null if we don't need to track side effects on the object. - MOZ_ASSERT_IF(hasSingletonType(), name); + MOZ_ASSERT_IF(isSingleton(), name); - if (hasSingletonType()) { + if (isSingleton()) { MOZ_ASSERT(name); types::AddTypePropertyId(cx, this, NameToId(name), v); @@ -41,7 +41,7 @@ { MOZ_ASSERT(name == fi->name()); setSlot(fi.scopeSlot(), v); - if (hasSingletonType()) + if (isSingleton()) types::AddTypePropertyId(cx, this, NameToId(name), v); } @@ -49,7 +49,7 @@ CallObject::setAliasedVarFromArguments(JSContext *cx, const Value &argsValue, jsid id, const Value &v) { setSlot(ArgumentsObject::SlotFromMagicScopeSlotValue(argsValue), v); - if (hasSingletonType()) + if (isSingleton()) types::AddTypePropertyId(cx, this, id, v); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/SelfHosting.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/SelfHosting.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/SelfHosting.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/SelfHosting.cpp 2015-02-03 14:33:45.000000000 +0000 @@ -324,10 +324,10 @@ if (!buffer) return false; - types::TypeObject *newtype = types::GetTypeCallerInitObject(cx, JSProto_Array); - if (!newtype) + types::ObjectGroup *newgroup = types::GetCallerInitGroup(cx, JSProto_Array); + if (!newgroup) return false; - buffer->setType(newtype); + buffer->setGroup(newgroup); NativeObject::EnsureDenseResult edr = buffer->ensureDenseElements(cx, length, 0); switch (edr) { @@ -506,8 +506,8 @@ MOZ_ASSERT(args[0].isObject()); JSObject *obj = &args[0].toObject(); - bool isPacked = obj->is() && !obj->hasLazyType() && - !obj->type()->hasAllFlags(types::OBJECT_FLAG_NON_PACKED) && + bool isPacked = obj->is() && !obj->hasLazyGroup() && + !obj->group()->hasAllFlags(types::OBJECT_FLAG_NON_PACKED) && obj->as().getDenseInitializedLength() == obj->as().length(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Shape.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Shape.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Shape.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Shape.cpp 2015-02-03 14:33:45.000000000 +0000 @@ -614,10 +614,10 @@ } JSObject * -js::NewReshapedObject(JSContext *cx, HandleTypeObject type, JSObject *parent, +js::NewReshapedObject(JSContext *cx, HandleObjectGroup group, JSObject *parent, gc::AllocKind allocKind, HandleShape shape, NewObjectKind newKind) { - RootedPlainObject res(cx, NewObjectWithType(cx, type, parent, allocKind, newKind)); + RootedPlainObject res(cx, NewObjectWithGroup(cx, group, parent, allocKind, newKind)); if (!res) return nullptr; @@ -640,7 +640,12 @@ /* Construct the new shape, without updating type information. */ RootedId id(cx); - RootedShape newShape(cx, res->lastProperty()); + RootedShape newShape(cx, EmptyShape::getInitialShape(cx, res->getClass(), + res->getTaggedProto(), + res->getParent(), + res->getMetadata(), + res->numFixedSlots(), + shape->getObjectFlags())); for (unsigned i = 0; i < ids.length(); i++) { id = ids[i]; MOZ_ASSERT(!res->contains(cx, id)); @@ -1078,7 +1083,6 @@ uint32_t slot = obj->lastProperty()->slot(); if (slot < slotSpan) break; - MOZ_ASSERT(obj->getSlot(slot).isUndefined()); } if (!obj->removeProperty(cx, obj->lastProperty()->propid())) return false; @@ -1150,12 +1154,6 @@ } /* static */ bool -JSObject::clearParent(JSContext *cx, HandleObject obj) -{ - return setParent(cx, obj, NullPtr()); -} - -/* static */ bool JSObject::setParent(JSContext *cx, HandleObject obj, HandleObject parent) { if (parent && !parent->setDelegate(cx)) @@ -1293,22 +1291,22 @@ } /* static */ inline HashNumber -StackBaseShape::hash(const StackBaseShape *base) +StackBaseShape::hash(const Lookup& lookup) { - HashNumber hash = base->flags; - hash = RotateLeft(hash, 4) ^ (uintptr_t(base->clasp) >> 3); - hash = RotateLeft(hash, 4) ^ (uintptr_t(base->parent) >> 3); - hash = RotateLeft(hash, 4) ^ (uintptr_t(base->metadata) >> 3); + HashNumber hash = lookup.flags; + hash = RotateLeft(hash, 4) ^ (uintptr_t(lookup.clasp) >> 3); + hash = RotateLeft(hash, 4) ^ (uintptr_t(lookup.hashParent) >> 3); + hash = RotateLeft(hash, 4) ^ (uintptr_t(lookup.hashMetadata) >> 3); return hash; } /* static */ inline bool -StackBaseShape::match(UnownedBaseShape *key, const StackBaseShape *lookup) +StackBaseShape::match(UnownedBaseShape *key, const Lookup& lookup) { - return key->flags == lookup->flags - && key->clasp_ == lookup->clasp - && key->parent == lookup->parent - && key->metadata == lookup->metadata; + return key->flags == lookup.flags + && key->clasp_ == lookup.clasp + && key->parent == lookup.matchParent + && key->metadata == lookup.matchMetadata; } void @@ -1321,6 +1319,61 @@ gc::MarkObjectRoot(trc, (JSObject**)&metadata, "StackBaseShape metadata"); } +/* + * This class is used to add a post barrier on the baseShapes set, as the key is + * calculated based on objects which may be moved by generational GC. + */ +class BaseShapeSetRef : public BufferableRef +{ + BaseShapeSet *set; + UnownedBaseShape *base; + JSObject *parentPrior; + JSObject *metadataPrior; + + public: + BaseShapeSetRef(BaseShapeSet *set, UnownedBaseShape *base) + : set(set), + base(base), + parentPrior(base->getObjectParent()), + metadataPrior(base->getObjectMetadata()) + { + MOZ_ASSERT(!base->isOwned()); + } + + void mark(JSTracer *trc) { + JSObject *parent = parentPrior; + if (parent) + Mark(trc, &parent, "baseShapes set parent"); + JSObject *metadata = metadataPrior; + if (metadata) + Mark(trc, &metadata, "baseShapes set metadata"); + if (parent == parentPrior && metadata == metadataPrior) + return; + + StackBaseShape::Lookup lookupPrior(base->getObjectFlags(), + base->clasp(), + parentPrior, parent, + metadataPrior, metadata); + ReadBarriered b(base); + MOZ_ALWAYS_TRUE(set->rekeyAs(lookupPrior, base, b)); + } +}; + +static void +BaseShapesTablePostBarrier(ExclusiveContext *cx, BaseShapeSet *table, UnownedBaseShape *base) +{ + if (!cx->isJSContext()) { + MOZ_ASSERT(!IsInsideNursery(base->getObjectParent())); + MOZ_ASSERT(!IsInsideNursery(base->getObjectMetadata())); + return; + } + + if (IsInsideNursery(base->getObjectParent()) || IsInsideNursery(base->getObjectMetadata())) { + StoreBuffer &sb = cx->asJSContext()->runtime()->gc.storeBuffer; + sb.putGeneric(BaseShapeSetRef(table, base)); + } +} + /* static */ UnownedBaseShape* BaseShape::getUnowned(ExclusiveContext *cx, StackBaseShape &base) { @@ -1329,7 +1382,7 @@ if (!table.initialized() && !table.init()) return nullptr; - DependentAddPtr p(cx, table, &base); + DependentAddPtr p(cx, table, base); if (p) return *p; @@ -1339,13 +1392,15 @@ if (!nbase_) return nullptr; - new (nbase_) BaseShape(*root); + new (nbase_) BaseShape(base); UnownedBaseShape *nbase = static_cast(nbase_); - if (!p.add(cx, table, root, nbase)) + if (!p.add(cx, table, base, nbase)) return nullptr; + BaseShapesTablePostBarrier(cx, &table, nbase); + return nbase; } @@ -1362,23 +1417,78 @@ #endif } +bool +BaseShape::fixupBaseShapeTableEntry() +{ + bool updated = false; + if (parent && IsForwarded(parent.get())) { + parent = Forwarded(parent.get()); + updated = true; + } + if (metadata && IsForwarded(metadata.get())) { + metadata = Forwarded(metadata.get()); + updated = true; + } + return updated; +} + +void +JSCompartment::fixupBaseShapeTable() +{ + if (!baseShapes.initialized()) + return; + + for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) { + UnownedBaseShape *base = e.front().unbarrieredGet(); + if (base->fixupBaseShapeTableEntry()) { + ReadBarriered b(base); + e.rekeyFront(base, b); + } + } +} + void JSCompartment::sweepBaseShapeTable() { - if (baseShapes.initialized()) { - for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) { - UnownedBaseShape *base = e.front().unbarrieredGet(); - if (IsBaseShapeAboutToBeFinalizedFromAnyThread(&base)) { - e.removeFront(); - } else if (base != e.front().unbarrieredGet()) { - StackBaseShape sbase(base); - ReadBarriered b(base); - e.rekeyFront(&sbase, b); - } + if (!baseShapes.initialized()) + return; + + for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) { + UnownedBaseShape *base = e.front().unbarrieredGet(); + MOZ_ASSERT_IF(base->getObjectParent(), !IsForwarded(base->getObjectParent())); + MOZ_ASSERT_IF(base->getObjectMetadata(), !IsForwarded(base->getObjectMetadata())); + if (IsBaseShapeAboutToBeFinalizedFromAnyThread(&base)) { + e.removeFront(); + } else if (base != e.front().unbarrieredGet()) { + ReadBarriered b(base); + e.rekeyFront(base, b); } } } +#ifdef JSGC_HASH_TABLE_CHECKS + +void +JSCompartment::checkBaseShapeTableAfterMovingGC() +{ + if (!baseShapes.initialized()) + return; + + for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) { + UnownedBaseShape *base = e.front().unbarrieredGet(); + CheckGCThingAfterMovingGC(base); + if (base->getObjectParent()) + CheckGCThingAfterMovingGC(base->getObjectParent()); + if (base->getObjectMetadata()) + CheckGCThingAfterMovingGC(base->getObjectMetadata()); + + BaseShapeSet::Ptr ptr = baseShapes.lookup(base); + MOZ_ASSERT(ptr.found() && &*ptr == &e.front()); + } +} + +#endif // JSGC_HASH_TABLE_CHECKS + void BaseShape::finalize(FreeOp *fop) { @@ -1614,14 +1724,14 @@ kind = GetBackgroundAllocKind(kind); Rooted global(cx, &shape->getObjectParent()->global()); - Rooted type(cx, cx->getNewType(clasp, TaggedProto(proto))); + RootedObjectGroup group(cx, cx->getNewGroup(clasp, TaggedProto(proto))); EntryIndex entry; if (lookupGlobal(clasp, global, kind, &entry)) PodZero(&entries[entry]); if (!proto->is() && lookupProto(clasp, proto, kind, &entry)) PodZero(&entries[entry]); - if (lookupType(type, kind, &entry)) + if (lookupGroup(group, kind, &entry)) PodZero(&entries[entry]); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Shape.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Shape.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Shape.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Shape.h 2015-02-03 14:33:45.000000000 +0000 @@ -86,7 +86,7 @@ * * To find the Shape for a particular property of an object initially requires * a linear search. But if the number of searches starting at any particular - * Shape in the property tree exceeds MAX_LINEAR_SEARCHES and the Shape's + * Shape in the property tree exceeds LINEAR_SEARCHES_MAX and the Shape's * lineage has (excluding the EmptyShape) at least MIN_ENTRIES, we create an * auxiliary hash table -- the ShapeTable -- that allows faster lookup. * Furthermore, a ShapeTable is always created for dictionary mode lists, @@ -391,7 +391,7 @@ HAD_ELEMENTS_ACCESS = 0x80, WATCHED = 0x100, ITERATED_SINGLETON = 0x200, - NEW_TYPE_UNKNOWN = 0x400, + NEW_GROUP_UNKNOWN = 0x400, UNCACHEABLE_PROTO = 0x800, IMMUTABLE_PROTOTYPE = 0x1000, @@ -406,7 +406,11 @@ QUALIFIED_VAROBJ = 0x2000, UNQUALIFIED_VAROBJ = 0x4000, - OBJECT_FLAG_MASK = 0x7ff8 + // For a function used as an interpreted constructor, whether a 'new' + // type had constructor information cleared. + NEW_SCRIPT_CLEARED = 0x8000, + + OBJECT_FLAG_MASK = 0xfff8 }; private: @@ -529,6 +533,7 @@ } void fixupAfterMovingGC(); + bool fixupBaseShapeTableEntry(); private: static void staticAsserts() { @@ -577,8 +582,6 @@ /* Entries for the per-compartment baseShapes set of unowned base shapes. */ struct StackBaseShape : public DefaultHasher { - typedef const StackBaseShape *Lookup; - uint32_t flags; const Class *clasp; JSObject *parent; @@ -597,8 +600,50 @@ JSObject *parent, JSObject *metadata, uint32_t objectFlags); explicit inline StackBaseShape(Shape *shape); - static inline HashNumber hash(const StackBaseShape *lookup); - static inline bool match(UnownedBaseShape *key, const StackBaseShape *lookup); + struct Lookup + { + uint32_t flags; + const Class *clasp; + JSObject *hashParent; + JSObject *matchParent; + JSObject *hashMetadata; + JSObject *matchMetadata; + + MOZ_IMPLICIT Lookup(const StackBaseShape &base) + : flags(base.flags), + clasp(base.clasp), + hashParent(base.parent), + matchParent(base.parent), + hashMetadata(base.metadata), + matchMetadata(base.metadata) + {} + + MOZ_IMPLICIT Lookup(UnownedBaseShape *base) + : flags(base->getObjectFlags()), + clasp(base->clasp()), + hashParent(base->getObjectParent()), + matchParent(base->getObjectParent()), + hashMetadata(base->getObjectMetadata()), + matchMetadata(base->getObjectMetadata()) + { + MOZ_ASSERT(!base->isOwned()); + } + + // For use by generational GC post barriers. + Lookup(uint32_t flags, const Class *clasp, + JSObject *hashParent, JSObject *matchParent, + JSObject *hashMetadata, JSObject *matchMetadata) + : flags(flags), + clasp(clasp), + hashParent(hashParent), + matchParent(matchParent), + hashMetadata(hashMetadata), + matchMetadata(matchMetadata) + {} + }; + + static inline HashNumber hash(const Lookup& lookup); + static inline bool match(UnownedBaseShape *key, const Lookup& lookup); // For RootedGeneric void trace(JSTracer *trc); @@ -917,7 +962,6 @@ setter() == rawSetter; } - bool get(JSContext* cx, HandleObject receiver, JSObject *obj, JSObject *pobj, MutableHandleValue vp); bool set(JSContext* cx, HandleObject obj, HandleObject receiver, bool strict, MutableHandleValue vp); BaseShape *base() const { return base_.get(); } @@ -1153,11 +1197,11 @@ * shape if none was found. */ static Shape *getInitialShape(ExclusiveContext *cx, const Class *clasp, - TaggedProto proto, JSObject *metadata, - JSObject *parent, size_t nfixed, uint32_t objectFlags = 0); + TaggedProto proto, JSObject *parent, + JSObject *metadata, size_t nfixed, uint32_t objectFlags = 0); static Shape *getInitialShape(ExclusiveContext *cx, const Class *clasp, - TaggedProto proto, JSObject *metadata, - JSObject *parent, gc::AllocKind kind, uint32_t objectFlags = 0); + TaggedProto proto, JSObject *parent, + JSObject *metadata, gc::AllocKind kind, uint32_t objectFlags = 0); /* * Reinsert an alternate initial shape, to be returned by future @@ -1491,8 +1535,9 @@ // properties of non-native objects, and dense elements for native objects. // Use separate APIs for these two cases. +template static inline void -MarkNonNativePropertyFound(MutableHandleShape propp) +MarkNonNativePropertyFound(typename MaybeRooted::MutableHandleType propp) { propp.set(reinterpret_cast(1)); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Shape-inl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Shape-inl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Shape-inl.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Shape-inl.h 2015-02-03 14:33:45.000000000 +0000 @@ -33,21 +33,6 @@ compartment(cx->compartment_) {} -inline bool -Shape::get(JSContext* cx, HandleObject receiver, JSObject* obj, JSObject *pobj, - MutableHandleValue vp) -{ - MOZ_ASSERT(!hasDefaultGetter()); - - if (hasGetterValue()) { - Value fval = getterValue(); - return InvokeGetterOrSetter(cx, receiver, fval, 0, 0, vp); - } - - RootedId id(cx, propid()); - return CallJSPropertyOp(cx, getterOp(), receiver, id, vp); -} - inline Shape * Shape::search(ExclusiveContext *cx, jsid id) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/SharedTypedArrayObject.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/SharedTypedArrayObject.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/SharedTypedArrayObject.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/SharedTypedArrayObject.cpp 2015-02-03 14:33:45.000000000 +0000 @@ -135,10 +135,10 @@ if (!obj) return nullptr; - types::TypeObject *type = cx->getNewType(obj->getClass(), TaggedProto(proto.get())); - if (!type) + types::ObjectGroup *group = cx->getNewGroup(obj->getClass(), TaggedProto(proto.get())); + if (!group) return nullptr; - obj->setType(type); + obj->setGroup(group); return &obj->as(); } @@ -157,14 +157,14 @@ jsbytecode *pc; RootedScript script(cx, cx->currentScript(&pc)); NewObjectKind newKind = script - ? UseNewTypeForInitializer(script, pc, instanceClass()) + ? UseSingletonForInitializer(script, pc, instanceClass()) : GenericObject; RootedObject obj(cx, NewBuiltinClassInstance(cx, instanceClass(), allocKind, newKind)); if (!obj) return nullptr; if (script) { - if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind)) + if (!types::SetInitializerObjectGroup(cx, script, pc, obj, newKind)) return nullptr; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/SPSProfiler.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/SPSProfiler.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/SPSProfiler.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/SPSProfiler.cpp 2015-02-03 14:33:45.000000000 +0000 @@ -99,9 +99,9 @@ /* Update lastProfilingFrame to point to the top-most JS jit-frame currently on * stack. */ - if (rt->mainThread.jitActivation) { - void *lastProfilingFrame = GetTopProfilingJitFrame(rt->mainThread.jitTop); - rt->mainThread.jitActivation->setLastProfilingFrame(lastProfilingFrame); + if (rt->jitActivation) { + void *lastProfilingFrame = GetTopProfilingJitFrame(rt->jitTop); + rt->jitActivation->setLastProfilingFrame(lastProfilingFrame); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Stack.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Stack.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Stack.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Stack.cpp 2015-02-03 14:33:45.000000000 +0000 @@ -218,7 +218,7 @@ if (isConstructing() && functionThis().isPrimitive()) { RootedObject callee(cx, &this->callee()); JSObject *obj = CreateThisForFunction(cx, callee, - useNewType() ? SingletonObject : GenericObject); + createSingleton() ? SingletonObject : GenericObject); if (!obj) return false; functionThis() = ObjectValue(*obj); @@ -405,9 +405,9 @@ } void -js::MarkInterpreterActivations(PerThreadData *ptd, JSTracer *trc) +js::MarkInterpreterActivations(JSRuntime *rt, JSTracer *trc) { - for (ActivationIterator iter(ptd); !iter.done(); ++iter) { + for (ActivationIterator iter(rt); !iter.done(); ++iter) { Activation *act = iter.activation(); if (act->isInterpreter()) MarkInterpreterActivation(trc, act->asInterpreter()); @@ -589,7 +589,7 @@ principals_(principals), pc_(nullptr), interpFrames_(nullptr), - activations_(cx->perThreadData), + activations_(cx->runtime()), jitFrames_(), ionInlineFrameNo_(0), asmJSFrames_() @@ -1040,7 +1040,7 @@ // ActivationIterator::jitTop_ may be invalid, so create a new // activation iterator. - data_.activations_ = ActivationIterator(data_.cx_->perThreadData); + data_.activations_ = ActivationIterator(data_.cx_->runtime()); while (data_.activations_.activation() != activation) ++data_.activations_; @@ -1388,11 +1388,11 @@ lastProfilingCallSite_(nullptr) { if (active) { - prevJitTop_ = cx->mainThread().jitTop; - prevJitJSContext_ = cx->mainThread().jitJSContext; - prevJitActivation_ = cx->mainThread().jitActivation; - cx->mainThread().jitJSContext = cx; - cx->mainThread().jitActivation = this; + prevJitTop_ = cx->runtime()->jitTop; + prevJitJSContext_ = cx->runtime()->jitJSContext; + prevJitActivation_ = cx->runtime()->jitActivation; + cx->runtime()->jitJSContext = cx; + cx->runtime()->jitActivation = this; registerProfiling(); } else { @@ -1408,9 +1408,9 @@ if (isProfiling()) unregisterProfiling(); - cx_->perThreadData->jitTop = prevJitTop_; - cx_->perThreadData->jitJSContext = prevJitJSContext_; - cx_->perThreadData->jitActivation = prevJitActivation_; + cx_->runtime()->jitTop = prevJitTop_; + cx_->runtime()->jitJSContext = prevJitJSContext_; + cx_->runtime()->jitActivation = prevJitActivation_; } // All reocvered value are taken from activation during the bailout. @@ -1453,25 +1453,25 @@ { // Only allowed to deactivate/activate if activation is top. // (Not tested and will probably fail in other situations.) - MOZ_ASSERT(cx->mainThread().activation_ == this); + MOZ_ASSERT(cx->runtime()->activation_ == this); MOZ_ASSERT(active != active_); if (active) { *((volatile bool *) active_) = true; - prevJitTop_ = cx->mainThread().jitTop; - prevJitJSContext_ = cx->mainThread().jitJSContext; - prevJitActivation_ = cx->mainThread().jitActivation; - cx->mainThread().jitJSContext = cx; - cx->mainThread().jitActivation = this; + prevJitTop_ = cx->runtime()->jitTop; + prevJitJSContext_ = cx->runtime()->jitJSContext; + prevJitActivation_ = cx->runtime()->jitActivation; + cx->runtime()->jitJSContext = cx; + cx->runtime()->jitActivation = this; registerProfiling(); } else { unregisterProfiling(); - cx->mainThread().jitTop = prevJitTop_; - cx->mainThread().jitJSContext = prevJitJSContext_; - cx->mainThread().jitActivation = prevJitActivation_; + cx->runtime()->jitTop = prevJitTop_; + cx->runtime()->jitJSContext = prevJitJSContext_; + cx->runtime()->jitActivation = prevJitActivation_; *((volatile bool *) active_) = false; } @@ -1626,8 +1626,8 @@ prevAsmJSForModule_ = module.activation(); module.activation() = this; - prevAsmJS_ = cx->mainThread().asmJSActivationStack_; - cx->mainThread().asmJSActivationStack_ = this; + prevAsmJS_ = cx->runtime()->asmJSActivationStack_; + cx->runtime()->asmJSActivationStack_ = this; // Now that the AsmJSActivation is fully initialized, make it visible to // asynchronous profiling. @@ -1645,9 +1645,9 @@ module_.activation() = prevAsmJSForModule_; JSContext *cx = cx_->asJSContext(); - MOZ_ASSERT(cx->mainThread().asmJSActivationStack_ == this); + MOZ_ASSERT(cx->runtime()->asmJSActivationStack_ == this); - cx->mainThread().asmJSActivationStack_ = prevAsmJS_; + cx->runtime()->asmJSActivationStack_ = prevAsmJS_; } InterpreterFrameIterator & @@ -1670,33 +1670,26 @@ Activation::registerProfiling() { MOZ_ASSERT(isProfiling()); - cx_->perThreadData->profilingActivation_ = this; + cx_->runtime()->profilingActivation_ = this; } void Activation::unregisterProfiling() { MOZ_ASSERT(isProfiling()); - MOZ_ASSERT(cx_->perThreadData->profilingActivation_ == this); + MOZ_ASSERT(cx_->runtime()->profilingActivation_ == this); // There may be a non-active jit activation in the linked list. Skip past it. Activation *prevProfiling = prevProfiling_; while (prevProfiling && prevProfiling->isJit() && !prevProfiling->asJit()->isActive()) prevProfiling = prevProfiling->prevProfiling_; - cx_->perThreadData->profilingActivation_ = prevProfiling; + cx_->runtime()->profilingActivation_ = prevProfiling; } ActivationIterator::ActivationIterator(JSRuntime *rt) - : jitTop_(rt->mainThread.jitTop), - activation_(rt->mainThread.activation_) -{ - settle(); -} - -ActivationIterator::ActivationIterator(PerThreadData *perThreadData) - : jitTop_(perThreadData->jitTop), - activation_(perThreadData->activation_) + : jitTop_(rt->jitTop), + activation_(rt->activation_) { settle(); } @@ -1723,7 +1716,7 @@ JS::ProfilingFrameIterator::ProfilingFrameIterator(JSRuntime *rt, const RegisterState &state) : rt_(rt), - activation_(rt->mainThread.profilingActivation()), + activation_(rt->profilingActivation()), savedPrevJitTop_(nullptr) { if (!activation_) @@ -1795,7 +1788,7 @@ if (activation_->isAsmJS()) { new (storage_.addr()) AsmJSProfilingFrameIterator(*activation_->asAsmJS(), state); // Set savedPrevJitTop_ to the actual jitTop_ from the runtime. - savedPrevJitTop_ = activation_->cx()->perThreadData->jitTop; + savedPrevJitTop_ = activation_->cx()->runtime()->jitTop; return; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Stack.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Stack.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Stack.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Stack.h 2015-02-03 14:33:45.000000000 +0000 @@ -207,7 +207,7 @@ inline bool hasArgsObj() const; inline ArgumentsObject &argsObj() const; inline void initArgsObj(ArgumentsObject &argsobj) const; - inline bool useNewType() const; + inline bool createSingleton() const; inline bool copyRawFrameSlots(AutoValueVector *vec) const; @@ -318,7 +318,7 @@ RUNNING_IN_JIT = 0x20000, /* Miscellaneous state. */ - USE_NEW_TYPE = 0x40000 /* Use new type for constructed |this| object. */ + CREATE_SINGLETON = 0x40000 /* Constructed |this| object should be singleton. */ }; private: @@ -814,13 +814,13 @@ return flags_ & HAS_ARGS_OBJ; } - void setUseNewType() { + void setCreateSingleton() { MOZ_ASSERT(isConstructing()); - flags_ |= USE_NEW_TYPE; + flags_ |= CREATE_SINGLETON; } - bool useNewType() const { + bool createSingleton() const { MOZ_ASSERT(isConstructing()); - return flags_ & USE_NEW_TYPE; + return flags_ & CREATE_SINGLETON; } bool isDebuggerEvalFrame() const { @@ -1001,7 +1001,7 @@ } }; -void MarkInterpreterActivations(PerThreadData *ptd, JSTracer *trc); +void MarkInterpreterActivations(JSRuntime *rt, JSTracer *trc); /*****************************************************************************/ @@ -1220,7 +1220,6 @@ public: explicit ActivationIterator(JSRuntime *rt); - explicit ActivationIterator(PerThreadData *perThreadData); ActivationIterator &operator++(); @@ -1414,12 +1413,6 @@ { settle(); } - - explicit JitActivationIterator(PerThreadData *perThreadData) - : ActivationIterator(perThreadData) - { - settle(); - } JitActivationIterator &operator++() { ActivationIterator::operator++(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Stack-inl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Stack-inl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Stack-inl.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Stack-inl.h 2015-02-03 14:33:45.000000000 +0000 @@ -535,10 +535,10 @@ } inline bool -AbstractFramePtr::useNewType() const +AbstractFramePtr::createSingleton() const { if (isInterpreterFrame()) - return asInterpreterFrame()->useNewType(); + return asInterpreterFrame()->createSingleton(); return false; } @@ -822,21 +822,21 @@ Activation::Activation(JSContext *cx, Kind kind) : cx_(cx), compartment_(cx->compartment()), - prev_(cx->perThreadData->activation_), + prev_(cx->runtime_->activation_), prevProfiling_(prev_ ? prev_->mostRecentProfiling() : nullptr), savedFrameChain_(0), hideScriptedCallerCount_(0), kind_(kind) { - cx->perThreadData->activation_ = this; + cx->runtime_->activation_ = this; } Activation::~Activation() { - MOZ_ASSERT_IF(isProfiling(), this != cx_->perThreadData->profilingActivation_); - MOZ_ASSERT(cx_->perThreadData->activation_ == this); + MOZ_ASSERT_IF(isProfiling(), this != cx_->runtime()->profilingActivation_); + MOZ_ASSERT(cx_->runtime_->activation_ == this); MOZ_ASSERT(hideScriptedCallerCount_ == 0); - cx_->perThreadData->activation_ = prev_; + cx_->runtime_->activation_ = prev_; } bool diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/String.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/String.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/String.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/String.cpp 2015-02-03 14:33:45.000000000 +0000 @@ -485,8 +485,8 @@ ? JSInlineString::lengthFits(wholeLength) : JSInlineString::lengthFits(wholeLength); if (canUseInline && cx->isJSContext()) { - Latin1Char *latin1Buf; - char16_t *twoByteBuf; + Latin1Char *latin1Buf = nullptr; // initialize to silence GCC warning + char16_t *twoByteBuf = nullptr; // initialize to silence GCC warning JSInlineString *str = isLatin1 ? AllocateInlineString(cx, wholeLength, &latin1Buf) : AllocateInlineString(cx, wholeLength, &twoByteBuf); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/StructuredClone.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/StructuredClone.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/StructuredClone.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/StructuredClone.cpp 2015-02-03 14:33:45.000000000 +0000 @@ -31,7 +31,6 @@ #include "mozilla/Endian.h" #include "mozilla/FloatingPoint.h" -#include "mozilla/TypedEnum.h" #include diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/TypedArrayObject.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/TypedArrayObject.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/TypedArrayObject.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/TypedArrayObject.cpp 2015-02-03 14:33:45.000000000 +0000 @@ -305,10 +305,10 @@ if (!obj) return nullptr; - types::TypeObject *type = cx->getNewType(obj->getClass(), TaggedProto(proto.get())); - if (!type) + types::ObjectGroup *group = cx->getNewGroup(obj->getClass(), TaggedProto(proto.get())); + if (!group) return nullptr; - obj->setType(type); + obj->setGroup(group); return &obj->as(); } @@ -317,7 +317,7 @@ makeTypedInstance(JSContext *cx, uint32_t len, gc::AllocKind allocKind) { const Class *clasp = instanceClass(); - if (len * sizeof(NativeType) >= TypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH) { + if (len * sizeof(NativeType) >= TypedArrayObject::SINGLETON_BYTE_LENGTH) { JSObject *obj = NewBuiltinClassInstance(cx, clasp, allocKind, SingletonObject); if (!obj) return nullptr; @@ -327,14 +327,14 @@ jsbytecode *pc; RootedScript script(cx, cx->currentScript(&pc)); NewObjectKind newKind = script - ? UseNewTypeForInitializer(script, pc, clasp) + ? UseSingletonForInitializer(script, pc, clasp) : GenericObject; RootedObject obj(cx, NewBuiltinClassInstance(cx, clasp, allocKind, newKind)); if (!obj) return nullptr; if (script) { - if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind)) + if (!types::SetInitializerObjectGroup(cx, script, pc, obj, newKind)) return nullptr; } @@ -707,14 +707,9 @@ if (!DefineProperty(cx, proto, cx->names().values, funValue, nullptr, nullptr, 0)) return false; -#ifdef JS_HAS_SYMBOLS RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator)); if (!DefineProperty(cx, proto, iteratorId, funValue, nullptr, nullptr, 0)) return false; -#else - if (!DefineProperty(cx, proto, cx->names().std_iterator, funValue, nullptr, nullptr, 0)) - return false; -#endif return true; } @@ -810,6 +805,7 @@ JS_SELF_HOSTED_FN("fill", "TypedArrayFill", 3, 0), JS_SELF_HOSTED_FN("find", "TypedArrayFind", 2, 0), JS_SELF_HOSTED_FN("findIndex", "TypedArrayFindIndex", 2, 0), + JS_SELF_HOSTED_FN("forEach", "TypedArrayForEach", 2, 0), JS_SELF_HOSTED_FN("indexOf", "TypedArrayIndexOf", 2, 0), JS_SELF_HOSTED_FN("join", "TypedArrayJoin", 1, 0), JS_SELF_HOSTED_FN("lastIndexOf", "TypedArrayLastIndexOf", 2, 0), @@ -972,13 +968,13 @@ static NewObjectKind DataViewNewObjectKind(JSContext *cx, uint32_t byteLength, JSObject *proto) { - if (!proto && byteLength >= TypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH) + if (!proto && byteLength >= TypedArrayObject::SINGLETON_BYTE_LENGTH) return SingletonObject; jsbytecode *pc; JSScript *script = cx->currentScript(&pc); if (!script) return GenericObject; - return types::UseNewTypeForInitializer(script, pc, &DataViewObject::class_); + return types::UseSingletonForInitializer(script, pc, &DataViewObject::class_); } inline DataViewObject * @@ -1004,17 +1000,17 @@ return nullptr; if (proto) { - types::TypeObject *type = cx->getNewType(&class_, TaggedProto(proto)); - if (!type) + types::ObjectGroup *group = cx->getNewGroup(&class_, TaggedProto(proto)); + if (!group) return nullptr; - obj->setType(type); - } else if (byteLength >= TypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH) { - MOZ_ASSERT(obj->hasSingletonType()); + obj->setGroup(group); + } else if (byteLength >= TypedArrayObject::SINGLETON_BYTE_LENGTH) { + MOZ_ASSERT(obj->isSingleton()); } else { jsbytecode *pc; RootedScript script(cx, cx->currentScript(&pc)); if (script) { - if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind)) + if (!types::SetInitializerObjectGroup(cx, script, pc, obj, newKind)) return nullptr; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/TypedArrayObject.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/TypedArrayObject.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/TypedArrayObject.h 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/TypedArrayObject.h 2015-02-03 14:33:45.000000000 +0000 @@ -170,7 +170,7 @@ * Byte length above which created typed arrays and data views will have * singleton types regardless of the context in which they are created. */ - static const uint32_t SINGLETON_TYPE_BYTE_LENGTH = 1024 * 1024 * 10; + static const uint32_t SINGLETON_BYTE_LENGTH = 1024 * 1024 * 10; static bool isOriginalLengthGetter(Native native); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/UbiNode.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/UbiNode.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/UbiNode.cpp 2015-01-25 22:24:36.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/UbiNode.cpp 2015-02-03 14:33:45.000000000 +0000 @@ -68,7 +68,7 @@ case JSTRACE_JITCODE: construct(static_cast(ptr)); break; case JSTRACE_LAZY_SCRIPT: construct(static_cast(ptr)); break; case JSTRACE_SHAPE: construct(static_cast(ptr)); break; - case JSTRACE_TYPE_OBJECT: construct(static_cast(ptr)); break; + case JSTRACE_OBJECT_GROUP: construct(static_cast(ptr)); break; default: MOZ_CRASH("bad JSGCTraceKind passed to JS::ubi::Node::Node"); @@ -239,8 +239,8 @@ MOZ_UTF16("js::Shape"); template<> const char16_t TracerConcrete::concreteTypeName[] = MOZ_UTF16("js::BaseShape"); -template<> const char16_t TracerConcrete::concreteTypeName[] = - MOZ_UTF16("js::types::TypeObject"); +template<> const char16_t TracerConcrete::concreteTypeName[] = + MOZ_UTF16("js::types::ObjectGroup"); // Instantiate all the TracerConcrete and templates here, where @@ -255,7 +255,7 @@ template class TracerConcrete; template class TracerConcreteWithCompartment; template class TracerConcreteWithCompartment; -template class TracerConcrete; +template class TracerConcrete; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/UnboxedObject.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/UnboxedObject.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/UnboxedObject.cpp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/UnboxedObject.cpp 2015-02-03 14:33:45.000000000 +0000 @@ -0,0 +1,601 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "vm/UnboxedObject.h" + +#include "jsinferinlines.h" +#include "jsobjinlines.h" + +#include "vm/Shape-inl.h" + +using mozilla::ArrayLength; +using mozilla::DebugOnly; +using mozilla::PodCopy; + +using namespace js; + +///////////////////////////////////////////////////////////////////// +// UnboxedLayout +///////////////////////////////////////////////////////////////////// + +void +UnboxedLayout::trace(JSTracer *trc) +{ + for (size_t i = 0; i < properties_.length(); i++) + MarkStringUnbarriered(trc, &properties_[i].name, "unboxed_layout_name"); + + if (newScript()) + newScript()->trace(trc); +} + +size_t +UnboxedLayout::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) +{ + return mallocSizeOf(this) + + properties_.sizeOfExcludingThis(mallocSizeOf) + + (newScript() ? newScript()->sizeOfIncludingThis(mallocSizeOf) : 0) + + mallocSizeOf(traceList()); +} + +void +UnboxedLayout::setNewScript(types::TypeNewScript *newScript, bool writeBarrier /* = true */) +{ + if (newScript_ && writeBarrier) + types::TypeNewScript::writeBarrierPre(newScript_); + newScript_ = newScript; +} + +///////////////////////////////////////////////////////////////////// +// UnboxedPlainObject +///////////////////////////////////////////////////////////////////// + +bool +UnboxedPlainObject::setValue(JSContext *cx, const UnboxedLayout::Property &property, const Value &v) +{ + uint8_t *p = &data_[property.offset]; + + switch (property.type) { + case JSVAL_TYPE_BOOLEAN: + if (v.isBoolean()) { + *p = v.toBoolean(); + return true; + } + return false; + + case JSVAL_TYPE_INT32: + if (v.isInt32()) { + *reinterpret_cast(p) = v.toInt32(); + return true; + } + return false; + + case JSVAL_TYPE_DOUBLE: + if (v.isNumber()) { + *reinterpret_cast(p) = v.toNumber(); + return true; + } + return false; + + case JSVAL_TYPE_STRING: + if (v.isString()) { + *reinterpret_cast(p) = v.toString(); + return true; + } + return false; + + case JSVAL_TYPE_OBJECT: + if (v.isObjectOrNull()) { + // Update property types when writing object properties. Types for + // other properties were captured when the unboxed layout was + // created. + types::AddTypePropertyId(cx, this, NameToId(property.name), v); + + *reinterpret_cast(p) = v.toObjectOrNull(); + return true; + } + return false; + + default: + MOZ_CRASH("Invalid type for unboxed value"); + } +} + +Value +UnboxedPlainObject::getValue(const UnboxedLayout::Property &property) +{ + uint8_t *p = &data_[property.offset]; + + switch (property.type) { + case JSVAL_TYPE_BOOLEAN: + return BooleanValue(*p != 0); + + case JSVAL_TYPE_INT32: + return Int32Value(*reinterpret_cast(p)); + + case JSVAL_TYPE_DOUBLE: + return DoubleValue(*reinterpret_cast(p)); + + case JSVAL_TYPE_STRING: + return StringValue(*reinterpret_cast(p)); + + case JSVAL_TYPE_OBJECT: + return ObjectOrNullValue(*reinterpret_cast(p)); + + default: + MOZ_CRASH("Invalid type for unboxed value"); + } +} + +void +UnboxedPlainObject::trace(JSTracer *trc, JSObject *obj) +{ + const UnboxedLayout &layout = obj->as().layout(); + const int32_t *list = layout.traceList(); + if (!list) + return; + + uint8_t *data = obj->as().data(); + while (*list != -1) { + HeapPtrString *heap = reinterpret_cast(data + *list); + MarkString(trc, heap, "unboxed_string"); + list++; + } + list++; + while (*list != -1) { + HeapPtrObject *heap = reinterpret_cast(data + *list); + if (*heap) + MarkObject(trc, heap, "unboxed_object"); + list++; + } + + // Unboxed objects don't have Values to trace. + MOZ_ASSERT(*(list + 1) == -1); +} + +bool +UnboxedPlainObject::convertToNative(JSContext *cx) +{ + // Immediately clear any new script on this object's group, + // as rollbackPartiallyInitializedObjects() will be confused by the type + // changes we make in this function. + group()->clearNewScript(cx); + + // clearNewScript() can reentrantly invoke this method. + if (!is()) + return true; + + Rooted obj(cx, this); + Rooted proto(cx, getTaggedProto()); + + size_t nfixed = gc::GetGCKindSlots(obj->layout().getAllocKind()); + + AutoValueVector values(cx); + RootedShape shape(cx, EmptyShape::getInitialShape(cx, &PlainObject::class_, proto, + getParent(), getMetadata(), nfixed, + lastProperty()->getObjectFlags())); + if (!shape) + return false; + + for (size_t i = 0; i < obj->layout().properties().length(); i++) { + const UnboxedLayout::Property &property = obj->layout().properties()[i]; + + if (!values.append(obj->getValue(property))) + return false; + + StackShape unrootedChild(shape->base()->unowned(), NameToId(property.name), i, + JSPROP_ENUMERATE, 0); + RootedGeneric child(cx, &unrootedChild); + shape = cx->compartment()->propertyTree.getChild(cx, shape, *child); + if (!shape) + return false; + } + + if (!SetClassAndProto(cx, obj, &PlainObject::class_, proto)) + return false; + + RootedNativeObject nobj(cx, &obj->as()); + NativeObject::setLastPropertyMakeNative(cx, nobj, shape); + + for (size_t i = 0; i < values.length(); i++) + nobj->initSlotUnchecked(i, values[i]); + + return true; +} + +/* static */ +UnboxedPlainObject * +UnboxedPlainObject::create(JSContext *cx, HandleObjectGroup group, NewObjectKind newKind) +{ + MOZ_ASSERT(group->clasp() == &class_); + gc::AllocKind allocKind = group->unboxedLayout().getAllocKind(); + + UnboxedPlainObject *res = NewObjectWithGroup(cx, group, cx->global(), + allocKind, newKind); + if (!res) + return nullptr; + + // Initialize reference fields of the object. All fields in the object will + // be overwritten shortly, but references need to be safe for the GC. + const int32_t *list = res->layout().traceList(); + if (list) { + uint8_t *data = res->data(); + while (*list != -1) { + HeapPtrString *heap = reinterpret_cast(data + *list); + heap->init(cx->names().empty); + list++; + } + list++; + while (*list != -1) { + HeapPtrObject *heap = reinterpret_cast(data + *list); + heap->init(nullptr); + list++; + } + // Unboxed objects don't have Values to initialize. + MOZ_ASSERT(*(list + 1) == -1); + } + + return res; +} + +/* static */ bool +UnboxedPlainObject::obj_lookupProperty(JSContext *cx, HandleObject obj, + HandleId id, MutableHandleObject objp, + MutableHandleShape propp) +{ + if (obj->as().layout().lookup(id)) { + MarkNonNativePropertyFound(propp); + objp.set(obj); + return true; + } + + RootedObject proto(cx, obj->getProto()); + if (!proto) { + objp.set(nullptr); + propp.set(nullptr); + return true; + } + + return LookupProperty(cx, proto, id, objp, propp); +} + +/* static */ bool +UnboxedPlainObject::obj_defineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue v, + PropertyOp getter, StrictPropertyOp setter, unsigned attrs) +{ + if (!obj->as().convertToNative(cx)) + return false; + + return DefineProperty(cx, obj, id, v, getter, setter, attrs); +} + +/* static */ bool +UnboxedPlainObject::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver, + HandleId id, MutableHandleValue vp) +{ + const UnboxedLayout &layout = obj->as().layout(); + + if (const UnboxedLayout::Property *property = layout.lookup(id)) { + vp.set(obj->as().getValue(*property)); + return true; + } + + RootedObject proto(cx, obj->getProto()); + if (!proto) { + vp.setUndefined(); + return true; + } + + return GetProperty(cx, proto, receiver, id, vp); +} + +/* static */ bool +UnboxedPlainObject::obj_setProperty(JSContext *cx, HandleObject obj, HandleId id, + MutableHandleValue vp, bool strict) +{ + const UnboxedLayout &layout = obj->as().layout(); + + if (const UnboxedLayout::Property *property = layout.lookup(id)) { + if (obj->as().setValue(cx, *property, vp)) + return true; + + if (!obj->as().convertToNative(cx)) + return false; + return SetProperty(cx, obj, obj, id, vp, strict); + } + + RootedObject proto(cx, obj->getProto()); + if (!proto) { + if (!obj->as().convertToNative(cx)) + return false; + return SetProperty(cx, obj, obj, id, vp, strict); + } + + return SetProperty(cx, proto, obj, id, vp, strict); +} + +/* static */ bool +UnboxedPlainObject::obj_getOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, + MutableHandle desc) +{ + const UnboxedLayout &layout = obj->as().layout(); + + if (const UnboxedLayout::Property *property = layout.lookup(id)) { + desc.value().set(obj->as().getValue(*property)); + desc.setAttributes(JSPROP_ENUMERATE); + desc.object().set(obj); + return true; + } + + desc.object().set(nullptr); + return true; +} + +/* static */ bool +UnboxedPlainObject::obj_setPropertyAttributes(JSContext *cx, HandleObject obj, + HandleId id, unsigned *attrsp) +{ + if (!obj->as().convertToNative(cx)) + return false; + return SetPropertyAttributes(cx, obj, id, attrsp); +} + +/* static */ bool +UnboxedPlainObject::obj_deleteProperty(JSContext *cx, HandleObject obj, HandleId id, + bool *succeeded) +{ + if (!obj->as().convertToNative(cx)) + return false; + return DeleteProperty(cx, obj, id, succeeded); +} + +/* static */ bool +UnboxedPlainObject::obj_watch(JSContext *cx, HandleObject obj, HandleId id, HandleObject callable) +{ + if (!obj->as().convertToNative(cx)) + return false; + return WatchProperty(cx, obj, id, callable); +} + +/* static */ bool +UnboxedPlainObject::obj_enumerate(JSContext *cx, HandleObject obj, AutoIdVector &properties) +{ + const UnboxedLayout::PropertyVector &unboxed = obj->as().layout().properties(); + for (size_t i = 0; i < unboxed.length(); i++) { + if (!properties.append(NameToId(unboxed[i].name))) + return false; + } + return true; +} + +const Class UnboxedPlainObject::class_ = { + "Object", + Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS, + nullptr, /* addProperty */ + nullptr, /* delProperty */ + nullptr, /* getProperty */ + nullptr, /* setProperty */ + nullptr, /* enumerate */ + nullptr, /* resolve */ + nullptr, /* convert */ + nullptr, /* finalize */ + nullptr, /* call */ + nullptr, /* hasInstance */ + nullptr, /* construct */ + UnboxedPlainObject::trace, + JS_NULL_CLASS_SPEC, + JS_NULL_CLASS_EXT, + { + UnboxedPlainObject::obj_lookupProperty, + UnboxedPlainObject::obj_defineProperty, + UnboxedPlainObject::obj_getProperty, + UnboxedPlainObject::obj_setProperty, + UnboxedPlainObject::obj_getOwnPropertyDescriptor, + UnboxedPlainObject::obj_setPropertyAttributes, + UnboxedPlainObject::obj_deleteProperty, + UnboxedPlainObject::obj_watch, + nullptr, /* No unwatch needed, as watch() converts the object to native */ + nullptr, /* getElements */ + UnboxedPlainObject::obj_enumerate, + nullptr, /* thisObject */ + } +}; + +///////////////////////////////////////////////////////////////////// +// API +///////////////////////////////////////////////////////////////////// + +static bool +UnboxedTypeIncludes(JSValueType supertype, JSValueType subtype) +{ + if (supertype == JSVAL_TYPE_DOUBLE && subtype == JSVAL_TYPE_INT32) + return true; + if (supertype == JSVAL_TYPE_OBJECT && subtype == JSVAL_TYPE_NULL) + return true; + return false; +} + +bool +js::TryConvertToUnboxedLayout(JSContext *cx, Shape *templateShape, + types::ObjectGroup *group, types::PreliminaryObjectArray *objects) +{ + if (!cx->runtime()->options().unboxedObjects()) + return true; + + if (templateShape->slotSpan() == 0) + return true; + + UnboxedLayout::PropertyVector properties; + if (!properties.appendN(UnboxedLayout::Property(), templateShape->slotSpan())) + return false; + + size_t objectCount = 0; + for (size_t i = 0; i < types::PreliminaryObjectArray::COUNT; i++) { + JSObject *obj = objects->get(i); + if (!obj) + continue; + + objectCount++; + + // All preliminary objects must have been created with the largest + // allocation kind possible, which will allow their unboxed data to be + // filled in inline. + MOZ_ASSERT(gc::GetGCKindSlots(obj->asTenured().getAllocKind()) == + NativeObject::MAX_FIXED_SLOTS); + + if (obj->as().lastProperty() != templateShape || + obj->as().hasDynamicElements()) + { + // Only use an unboxed representation if all created objects match + // the template shape exactly. + return true; + } + + for (size_t i = 0; i < templateShape->slotSpan(); i++) { + Value val = obj->as().getSlot(i); + + JSValueType &existing = properties[i].type; + JSValueType type = val.isDouble() ? JSVAL_TYPE_DOUBLE : val.extractNonDoubleType(); + + if (existing == JSVAL_TYPE_MAGIC || existing == type || UnboxedTypeIncludes(type, existing)) + existing = type; + else if (!UnboxedTypeIncludes(existing, type)) + return true; + } + } + + if (objectCount <= 1) { + // If only one of the objects has been created, it is more likely to + // have new properties added later. + return true; + } + + for (size_t i = 0; i < templateShape->slotSpan(); i++) { + // We can't use an unboxed representation if e.g. all the objects have + // a null value for one of the properties, as we can't decide what type + // it is supposed to have. + if (UnboxedTypeSize(properties[i].type) == 0) + return true; + } + + // Fill in the names for all the object's properties. + for (Shape::Range r(templateShape); !r.empty(); r.popFront()) { + size_t slot = r.front().slot(); + MOZ_ASSERT(!properties[slot].name); + properties[slot].name = JSID_TO_ATOM(r.front().propid())->asPropertyName(); + } + + // Fill in all the unboxed object's property offsets, ordering fields from the + // largest down to avoid alignment issues. + uint32_t offset = 0; + + static const size_t typeSizes[] = { 8, 4, 1 }; + + Vector objectOffsets, stringOffsets; + + DebugOnly addedProperties = 0; + for (size_t i = 0; i < ArrayLength(typeSizes); i++) { + size_t size = typeSizes[i]; + for (size_t j = 0; j < templateShape->slotSpan(); j++) { + JSValueType type = properties[j].type; + if (UnboxedTypeSize(type) == size) { + if (type == JSVAL_TYPE_OBJECT) { + if (!objectOffsets.append(offset)) + return false; + } else if (type == JSVAL_TYPE_STRING) { + if (!stringOffsets.append(offset)) + return false; + } + addedProperties++; + properties[j].offset = offset; + offset += size; + } + } + } + MOZ_ASSERT(addedProperties == templateShape->slotSpan()); + + // The entire object must be allocatable inline. + if (sizeof(JSObject) + offset > JSObject::MAX_BYTE_SIZE) + return true; + + UnboxedLayout *layout = group->zone()->new_(properties, offset); + if (!layout) + return false; + + // Construct the layout's trace list. + if (!objectOffsets.empty() || !stringOffsets.empty()) { + Vector entries; + if (!entries.appendAll(stringOffsets) || + !entries.append(-1) || + !entries.appendAll(objectOffsets) || + !entries.append(-1) || + !entries.append(-1)) + { + return false; + } + int32_t *traceList = group->zone()->pod_malloc(entries.length()); + if (!traceList) + return false; + PodCopy(traceList, entries.begin(), entries.length()); + layout->setTraceList(traceList); + } + + // We've determined that all the preliminary objects can use the new layout + // just constructed, so convert the existing group to be an + // UnboxedPlainObject rather than a PlainObject, and update the preliminary + // objects to use the new layout. Do the fallible stuff first before + // modifying any objects. + + // Get an empty shape which we can use for the preliminary objects. + Shape *newShape = EmptyShape::getInitialShape(cx, &UnboxedPlainObject::class_, + group->proto(), + templateShape->getObjectParent(), + templateShape->getObjectMetadata(), + templateShape->getObjectFlags()); + if (!newShape) { + cx->clearPendingException(); + return false; + } + + // Accumulate a list of all the properties in each preliminary object, and + // update their shapes. + Vector values; + if (!values.reserve(objectCount * templateShape->slotSpan())) + return false; + for (size_t i = 0; i < types::PreliminaryObjectArray::COUNT; i++) { + if (!objects->get(i)) + continue; + + RootedNativeObject obj(cx, &objects->get(i)->as()); + for (size_t j = 0; j < templateShape->slotSpan(); j++) + values.infallibleAppend(obj->getSlot(j)); + + // Clear the object to remove any dynamically allocated information. + NativeObject::clear(cx, obj); + + obj->setLastPropertyMakeNonNative(newShape); + } + + if (types::TypeNewScript *newScript = group->newScript()) + layout->setNewScript(newScript); + + group->setClasp(&UnboxedPlainObject::class_); + group->setUnboxedLayout(layout); + + size_t valueCursor = 0; + for (size_t i = 0; i < types::PreliminaryObjectArray::COUNT; i++) { + if (!objects->get(i)) + continue; + UnboxedPlainObject *obj = &objects->get(i)->as(); + memset(obj->data(), 0, layout->size()); + for (size_t j = 0; j < templateShape->slotSpan(); j++) { + Value v = values[valueCursor++]; + JS_ALWAYS_TRUE(obj->setValue(cx, properties[j], v)); + } + } + + MOZ_ASSERT(valueCursor == values.length()); + return true; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/UnboxedObject.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/UnboxedObject.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/UnboxedObject.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/UnboxedObject.h 2015-02-03 14:33:45.000000000 +0000 @@ -0,0 +1,195 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef vm_UnboxedObject_h +#define vm_UnboxedObject_h + +#include "jsgc.h" +#include "jsinfer.h" +#include "jsobj.h" + +namespace js { + +// Memory required for an unboxed value of a given type. Returns zero for types +// which can't be used for unboxed objects. +static inline size_t +UnboxedTypeSize(JSValueType type) +{ + switch (type) { + case JSVAL_TYPE_BOOLEAN: return 1; + case JSVAL_TYPE_INT32: return 4; + case JSVAL_TYPE_DOUBLE: return 8; + case JSVAL_TYPE_STRING: return sizeof(void *); + case JSVAL_TYPE_OBJECT: return sizeof(void *); + default: return 0; + } +} + +static inline bool +UnboxedTypeNeedsPreBarrier(JSValueType type) +{ + return type == JSVAL_TYPE_STRING || type == JSVAL_TYPE_OBJECT; +} + +// Class describing the layout of an UnboxedPlainObject. +class UnboxedLayout +{ + public: + struct Property { + PropertyName *name; + uint32_t offset; + JSValueType type; + + Property() + : name(nullptr), offset(0), type(JSVAL_TYPE_MAGIC) + {} + }; + + typedef Vector PropertyVector; + + private: + // All properties on objects with this layout, in enumeration order. + PropertyVector properties_; + + // Byte size of the data for objects with this layout. + size_t size_; + + // Any 'new' script information associated with this layout. + types::TypeNewScript *newScript_; + + // List for use in tracing objects with this layout. This has the same + // structure as the trace list on a TypeDescr. + int32_t *traceList_; + + public: + UnboxedLayout(const PropertyVector &properties, size_t size) + : size_(size), newScript_(nullptr), traceList_(nullptr) + { + properties_.appendAll(properties); + } + + ~UnboxedLayout() { + js_delete(newScript_); + js_free(traceList_); + } + + const PropertyVector &properties() const { + return properties_; + } + + types::TypeNewScript *newScript() const { + return newScript_; + } + + void setNewScript(types::TypeNewScript *newScript, bool writeBarrier = true); + + const int32_t *traceList() const { + return traceList_; + } + + void setTraceList(int32_t *traceList) { + traceList_ = traceList; + } + + const Property *lookup(JSAtom *atom) const { + for (size_t i = 0; i < properties_.length(); i++) { + if (properties_[i].name == atom) + return &properties_[i]; + } + return nullptr; + } + + const Property *lookup(jsid id) const { + if (JSID_IS_STRING(id)) + return lookup(JSID_TO_ATOM(id)); + return nullptr; + } + + size_t size() const { + return size_; + } + + inline gc::AllocKind getAllocKind() const; + + void trace(JSTracer *trc); + + size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); +}; + +// Class for a plain object using an unboxed representation. The physical +// layout of these objects is identical to that of an InlineTypedObject, though +// these objects use an UnboxedLayout instead of a TypeDescr to keep track of +// how their properties are stored. +class UnboxedPlainObject : public JSObject +{ + // Start of the inline data, which immediately follows the shape and type. + uint8_t data_[1]; + + public: + static const Class class_; + + static bool obj_lookupProperty(JSContext *cx, HandleObject obj, + HandleId id, MutableHandleObject objp, + MutableHandleShape propp); + + static bool obj_defineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue v, + PropertyOp getter, StrictPropertyOp setter, unsigned attrs); + + static bool obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver, + HandleId id, MutableHandleValue vp); + + static bool obj_setProperty(JSContext *cx, HandleObject obj, HandleId id, + MutableHandleValue vp, bool strict); + + static bool obj_getOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, + MutableHandle desc); + + static bool obj_setPropertyAttributes(JSContext *cx, HandleObject obj, + HandleId id, unsigned *attrsp); + + static bool obj_deleteProperty(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded); + + static bool obj_enumerate(JSContext *cx, HandleObject obj, AutoIdVector &properties); + static bool obj_watch(JSContext *cx, HandleObject obj, HandleId id, HandleObject callable); + + const UnboxedLayout &layout() const { + return group()->unboxedLayout(); + } + + uint8_t *data() { + return &data_[0]; + } + + bool setValue(JSContext *cx, const UnboxedLayout::Property &property, const Value &v); + Value getValue(const UnboxedLayout::Property &property); + + bool convertToNative(JSContext *cx); + + static UnboxedPlainObject *create(JSContext *cx, HandleObjectGroup group, NewObjectKind newKind); + + static void trace(JSTracer *trc, JSObject *object); + + static size_t offsetOfData() { + return offsetof(UnboxedPlainObject, data_[0]); + } +}; + +// Try to construct an UnboxedLayout for each of the preliminary objects, +// provided they all match the template shape. If successful, converts the +// preliminary objects and their group to the new unboxed representation. +bool +TryConvertToUnboxedLayout(JSContext *cx, Shape *templateShape, + types::ObjectGroup *group, types::PreliminaryObjectArray *objects); + +inline gc::AllocKind +UnboxedLayout::getAllocKind() const +{ + return gc::GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + size()); +} + +} // namespace js + +#endif /* vm_UnboxedObject_h */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Xdr.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Xdr.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/src/vm/Xdr.h 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/src/vm/Xdr.h 2015-02-03 14:33:45.000000000 +0000 @@ -28,21 +28,10 @@ * this wiki page: * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode - * - * === GREETINGS, FELLOW SUBTRAHEND INCREMENTER! === - * For the time being, please increment the subtrahend by 2 each time it - * changes, because we have two flavors of bytecode: with JSOP_SYMBOL (in - * Nightly) and without (all others). FIXME: Bug 1066322 - Enable ES6 symbols - * in all builds. */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 230; -static_assert(XDR_BYTECODE_VERSION_SUBTRAHEND % 2 == 0, "see the comment above"); +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 232; static const uint32_t XDR_BYTECODE_VERSION = - uint32_t(0xb973c0de - (XDR_BYTECODE_VERSION_SUBTRAHEND -#ifdef JS_HAS_SYMBOLS - + 1 -#endif - )); + uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); static_assert(JSErr_Limit == 367, "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or " diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/loader/mozJSComponentLoader.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/loader/mozJSComponentLoader.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/loader/mozJSComponentLoader.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/loader/mozJSComponentLoader.cpp 2015-02-03 14:33:46.000000000 +0000 @@ -588,7 +588,7 @@ if (aReuseLoaderGlobal) { // If we're reusing the loader global, we don't actually use the // global, but rather we use a different object as the 'this' object. - obj = JS_NewObject(aCx, &kFakeBackstagePassJSClass, NullPtr(), NullPtr()); + obj = JS_NewObject(aCx, &kFakeBackstagePassJSClass); NS_ENSURE_TRUE(obj, nullptr); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/ExportHelpers.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/ExportHelpers.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/ExportHelpers.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/ExportHelpers.cpp 2015-02-03 14:33:46.000000000 +0000 @@ -464,7 +464,7 @@ RootedObject obj(cx); { JSAutoCompartment ac(cx, scope); - obj = JS_NewObject(cx, nullptr, JS::NullPtr(), scope); + obj = JS_NewObject(cx, nullptr, scope); if (!obj) return false; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/moz.build 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/moz.build 2015-02-03 14:33:46.000000000 +0000 @@ -66,7 +66,6 @@ '/dom/html', '/dom/svg', '/dom/workers', - '/js/ipc', '/layout/base', '/layout/style', '/xpcom/reflect/xptinfo', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCComponents.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCComponents.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCComponents.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCComponents.cpp 2015-02-03 14:33:46.000000000 +0000 @@ -13,10 +13,10 @@ #include "nsJSUtils.h" #include "mozJSComponentLoader.h" #include "nsContentUtils.h" -#include "JavaScriptParent.h" #include "jsfriendapi.h" #include "js/StructuredClone.h" #include "mozilla/Attributes.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "mozilla/Preferences.h" #include "nsJSEnvironment.h" #include "mozilla/TimeStamp.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCConvert.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCConvert.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCConvert.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCConvert.cpp 2015-02-03 14:33:46.000000000 +0000 @@ -21,11 +21,11 @@ #include "jsfriendapi.h" #include "js/CharacterEncoding.h" #include "jsprf.h" -#include "JavaScriptParent.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/DOMException.h" #include "mozilla/dom/PrimitiveConversions.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" using namespace xpc; using namespace mozilla; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCJSID.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCJSID.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCJSID.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCJSID.cpp 2015-02-03 14:33:46.000000000 +0000 @@ -9,7 +9,7 @@ #include "xpcprivate.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/Attributes.h" -#include "JavaScriptParent.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "mozilla/StaticPtr.h" using namespace mozilla::dom; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCJSRuntime.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCJSRuntime.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCJSRuntime.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCJSRuntime.cpp 2015-02-03 14:33:46.000000000 +0000 @@ -1928,13 +1928,13 @@ zStats.jitCodesGCHeap, "References to executable code pools used by the JITs."); - ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-objects/gc-heap"), - zStats.typeObjectsGCHeap, - "Type inference information about objects."); - - ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-objects/malloc-heap"), - zStats.typeObjectsMallocHeap, - "Type object addenda."); + ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("object-groups/gc-heap"), + zStats.objectGroupsGCHeap, + "Classification and type inference information about objects."); + + ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("object-groups/malloc-heap"), + zStats.objectGroupsMallocHeap, + "Object group addenda."); ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-pool"), zStats.typePool, @@ -2989,14 +2989,6 @@ } // namespace xpc -#ifdef MOZ_CRASHREPORTER -static bool -DiagnosticMemoryCallback(void *ptr, size_t size) -{ - return CrashReporter::RegisterAppMemory(ptr, size) == NS_OK; -} -#endif - static void AccumulateTelemetryCallback(int id, uint32_t sample, const char *key) { @@ -3292,7 +3284,7 @@ // 1MB is the default stack size on Windows, so use 900k. // Windows PGO stack frames have unfortunately gotten pretty large lately. :-( const size_t kStackQuota = 900 * 1024; - const size_t kTrustedScriptBuffer = (sizeof(size_t) == 8) ? 120 * 1024 + const size_t kTrustedScriptBuffer = (sizeof(size_t) == 8) ? 140 * 1024 : 80 * 1024; // The following two configurations are linux-only. Given the numbers above, // we use 50k and 100k trusted buffers on 32-bit and 64-bit respectively. @@ -3324,9 +3316,6 @@ JS_AddWeakPointerCallback(runtime, WeakPointerCallback, this); JS_SetWrapObjectCallbacks(runtime, &WrapObjectCallbacks); js::SetPreserveWrapperCallback(runtime, PreserveWrapper); -#ifdef MOZ_CRASHREPORTER - JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback); -#endif #ifdef MOZ_ENABLE_PROFILER_SPS if (PseudoStack *stack = mozilla_get_pseudo_stack()) stack->sampleRuntime(runtime); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCMaps.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCMaps.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCMaps.h 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCMaps.h 2015-02-03 14:33:46.000000000 +0000 @@ -111,10 +111,8 @@ inline XPCWrappedNative* Find(nsISupports* Obj) { NS_PRECONDITION(Obj,"bad param"); - Entry* entry = (Entry*) PL_DHashTableLookup(mTable, Obj); - if (PL_DHASH_ENTRY_IS_FREE(entry)) - return nullptr; - return entry->value; + Entry* entry = (Entry*) PL_DHashTableSearch(mTable, Obj); + return entry ? entry->value : nullptr; } inline XPCWrappedNative* Add(XPCWrappedNative* wrapper) @@ -179,10 +177,8 @@ inline nsXPCWrappedJSClass* Find(REFNSIID iid) { - Entry* entry = (Entry*) PL_DHashTableLookup(mTable, &iid); - if (PL_DHASH_ENTRY_IS_FREE(entry)) - return nullptr; - return entry->value; + Entry* entry = (Entry*) PL_DHashTableSearch(mTable, &iid); + return entry ? entry->value : nullptr; } inline nsXPCWrappedJSClass* Add(nsXPCWrappedJSClass* clazz) @@ -234,10 +230,8 @@ inline XPCNativeInterface* Find(REFNSIID iid) { - Entry* entry = (Entry*) PL_DHashTableLookup(mTable, &iid); - if (PL_DHASH_ENTRY_IS_FREE(entry)) - return nullptr; - return entry->value; + Entry* entry = (Entry*) PL_DHashTableSearch(mTable, &iid); + return entry ? entry->value : nullptr; } inline XPCNativeInterface* Add(XPCNativeInterface* iface) @@ -292,10 +286,8 @@ inline XPCNativeSet* Find(nsIClassInfo* info) { - Entry* entry = (Entry*) PL_DHashTableLookup(mTable, info); - if (PL_DHASH_ENTRY_IS_FREE(entry)) - return nullptr; - return entry->value; + Entry* entry = (Entry*) PL_DHashTableSearch(mTable, info); + return entry ? entry->value : nullptr; } inline XPCNativeSet* Add(nsIClassInfo* info, XPCNativeSet* set) @@ -350,10 +342,8 @@ inline XPCWrappedNativeProto* Find(nsIClassInfo* info) { - Entry* entry = (Entry*) PL_DHashTableLookup(mTable, info); - if (PL_DHASH_ENTRY_IS_FREE(entry)) - return nullptr; - return entry->value; + Entry* entry = (Entry*) PL_DHashTableSearch(mTable, info); + return entry ? entry->value : nullptr; } inline XPCWrappedNativeProto* Add(nsIClassInfo* info, XPCWrappedNativeProto* proto) @@ -413,10 +403,8 @@ inline XPCNativeSet* Find(XPCNativeSetKey* key) { - Entry* entry = (Entry*) PL_DHashTableLookup(mTable, key); - if (PL_DHASH_ENTRY_IS_FREE(entry)) - return nullptr; - return entry->key_value; + Entry* entry = (Entry*) PL_DHashTableSearch(mTable, key); + return entry ? entry->key_value : nullptr; } inline XPCNativeSet* Add(const XPCNativeSetKey* key, XPCNativeSet* set) @@ -488,10 +476,8 @@ inline nsIXPCFunctionThisTranslator* Find(REFNSIID iid) { - Entry* entry = (Entry*) PL_DHashTableLookup(mTable, &iid); - if (PL_DHASH_ENTRY_IS_FREE(entry)) - return nullptr; - return entry->value; + Entry* entry = (Entry*) PL_DHashTableSearch(mTable, &iid); + return entry ? entry->value : nullptr; } inline nsIXPCFunctionThisTranslator* Add(REFNSIID iid, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/xpcprivate.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/xpcprivate.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/xpcprivate.h 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/xpcprivate.h 2015-02-03 14:33:46.000000000 +0000 @@ -973,21 +973,13 @@ // Macros to initialize Object or Function like XPC_WN classes #define XPC_WN_WithCall_ObjectOps \ { \ - nullptr, /* lookupGeneric */ \ nullptr, /* lookupProperty */ \ - nullptr, /* lookupElement */ \ - nullptr, /* defineGeneric */ \ nullptr, /* defineProperty */ \ - nullptr, /* defineElement */ \ - nullptr, /* getGeneric */ \ nullptr, /* getProperty */ \ - nullptr, /* getElement */ \ - nullptr, /* setGeneric */ \ nullptr, /* setProperty */ \ - nullptr, /* setElement */ \ nullptr, /* getOwnPropertyDescriptor */ \ - nullptr, /* setGenericAttributes */ \ - nullptr, /* deleteGeneric */ \ + nullptr, /* setPropertyAttributes */ \ + nullptr, /* deleteProperty */ \ nullptr, nullptr, /* watch/unwatch */ \ nullptr, /* getElements */ \ nullptr, /* enumerate */ \ @@ -996,21 +988,13 @@ #define XPC_WN_NoCall_ObjectOps \ { \ - nullptr, /* lookupGeneric */ \ nullptr, /* lookupProperty */ \ - nullptr, /* lookupElement */ \ - nullptr, /* defineGeneric */ \ nullptr, /* defineProperty */ \ - nullptr, /* defineElement */ \ - nullptr, /* getGeneric */ \ nullptr, /* getProperty */ \ - nullptr, /* getElement */ \ - nullptr, /* setGeneric */ \ nullptr, /* setProperty */ \ - nullptr, /* setElement */ \ nullptr, /* getOwnPropertyDescriptor */ \ - nullptr, /* setGenericAttributes */ \ - nullptr, /* deleteGeneric */ \ + nullptr, /* setPropertyAttributes */ \ + nullptr, /* deleteProperty */ \ nullptr, nullptr, /* watch/unwatch */ \ nullptr, /* getElements */ \ nullptr, /* enumerate */ \ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCWrappedJSClass.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCWrappedJSClass.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCWrappedJSClass.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCWrappedJSClass.cpp 2015-02-03 14:33:46.000000000 +0000 @@ -13,11 +13,11 @@ #include "nsWrapperCache.h" #include "AccessCheck.h" #include "nsJSUtils.h" -#include "JavaScriptParent.h" #include "mozilla/Attributes.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/DOMException.h" #include "mozilla/dom/DOMExceptionBinding.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "jsapi.h" #include "jsfriendapi.h" @@ -1467,7 +1467,7 @@ xpc::NewOutObject(JSContext* cx, JSObject* scope) { RootedObject global(cx, JS_GetGlobalForObject(cx, scope)); - return JS_NewObject(cx, nullptr, NullPtr(), global); + return JS_NewObject(cx, nullptr, global); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCWrappedJS.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCWrappedJS.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCWrappedJS.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCWrappedJS.cpp 2015-02-03 14:33:46.000000000 +0000 @@ -8,10 +8,10 @@ #include "xpcprivate.h" #include "jsprf.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "nsCCUncollectableMarker.h" #include "nsContentUtils.h" #include "nsThreadUtils.h" -#include "JavaScriptParent.h" using namespace mozilla; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCWrappedNative.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCWrappedNative.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCWrappedNative.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCWrappedNative.cpp 2015-02-03 14:33:46.000000000 +0000 @@ -7,6 +7,7 @@ /* Wrapper object for reflecting native xpcom objects into JavaScript. */ #include "xpcprivate.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "nsWrapperCacheInlines.h" #include "XPCLog.h" #include "jsprf.h" @@ -807,7 +808,7 @@ return false; } - mFlatJSObject = JS_NewObject(cx, jsclazz, protoJSObject, parent); + mFlatJSObject = JS_NewObjectWithGivenProto(cx, jsclazz, protoJSObject, parent); if (!mFlatJSObject) { mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID); return false; @@ -1572,9 +1573,7 @@ AutoJSContext cx; RootedObject parent(cx, mFlatJSObject); - RootedObject proto(cx, JS_GetObjectPrototype(cx, parent)); - JSObject* obj = JS_NewObject(cx, Jsvalify(&XPC_WN_Tearoff_JSClass), - proto, parent); + JSObject* obj = JS_NewObject(cx, Jsvalify(&XPC_WN_Tearoff_JSClass), parent); if (!obj) return false; @@ -2171,6 +2170,21 @@ return false; } + // Don't allow CPOWs to be passed to native code (in case they try to cast + // to a concrete type). + if (src.isObject() && + jsipc::IsWrappedCPOW(&src.toObject()) && + type_tag == nsXPTType::T_INTERFACE && + !param_iid.Equals(NS_GET_IID(nsISupports))) + { + // Allow passing CPOWs to XPCWrappedJS. + nsCOMPtr wrappedJS(do_QueryInterface(mCallee)); + if (!wrappedJS) { + ThrowBadParam(NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE, i, mCallContext); + return false; + } + } + nsresult err; if (!XPCConvert::JSData2Native(&dp->val, src, type, ¶m_iid, &err)) { ThrowBadParam(err, i, mCallContext); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCWrappedNativeJSOps.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCWrappedNativeJSOps.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCWrappedNativeJSOps.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCWrappedNativeJSOps.cpp 2015-02-03 14:33:46.000000000 +0000 @@ -681,21 +681,13 @@ // ObjectOps { - nullptr, // lookupGeneric nullptr, // lookupProperty - nullptr, // lookupElement - nullptr, // defineGeneric nullptr, // defineProperty - nullptr, // defineElement - nullptr, // getGeneric nullptr, // getProperty - nullptr, // getElement - nullptr, // setGeneric nullptr, // setProperty - nullptr, // setElement nullptr, // getOwnPropertyDescriptor - nullptr, // setGenericAttributes - nullptr, // deleteGeneric + nullptr, // setPropertyAttributes + nullptr, // deleteProperty nullptr, nullptr, // watch/unwatch nullptr, // getElements nullptr, // enumerate diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCWrappedNativeScope.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCWrappedNativeScope.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/src/XPCWrappedNativeScope.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/src/XPCWrappedNativeScope.cpp 2015-02-03 14:33:46.000000000 +0000 @@ -41,8 +41,10 @@ // may themselves be involved in cycles. We need to drop these strong // references before the cycle collector shuts down. Otherwise we'll // leak. This observer always runs before CC shutdown. - if (gInterpositionMap) + if (gInterpositionMap) { delete gInterpositionMap; + gInterpositionMap = nullptr; + } nsContentUtils::UnregisterShutdownObserver(this); return NS_OK; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/tests/chrome/chrome.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/tests/chrome/chrome.ini --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/tests/chrome/chrome.ini 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/tests/chrome/chrome.ini 2015-02-03 14:33:46.000000000 +0000 @@ -68,6 +68,7 @@ [test_bug1065185.html] [test_bug1074863.html] [test_bug1092477.xul] +[test_bug1124898.html] [test_xrayToJS.xul] skip-if = buildapp == 'mulet' [test_chrometoSource.xul] diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/tests/chrome/test_bug1124898.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/tests/chrome/test_bug1124898.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/tests/chrome/test_bug1124898.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/tests/chrome/test_bug1124898.html 2015-02-03 14:33:46.000000000 +0000 @@ -0,0 +1,47 @@ + + + + + + Test for Bug 1124898 + + + + + + +Mozilla Bug 1124898 +

    + +
    +
    + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/tests/chrome/test_xrayToJS.xul thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/tests/chrome/test_xrayToJS.xul --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/tests/chrome/test_xrayToJS.xul 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/tests/chrome/test_xrayToJS.xul 2015-02-03 14:33:46.000000000 +0000 @@ -145,8 +145,6 @@ var version = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).version; var isNightlyBuild = version.endsWith("a1"); var isReleaseBuild = !version.contains("a"); - const jsHasSymbols = typeof Symbol === "function"; - const kIteratorSymbol = jsHasSymbols ? Symbol.iterator : "@@iterator"; var gPrototypeProperties = {}; gPrototypeProperties['Date'] = ["getTime", "getTimezoneOffset", "getYear", "getFullYear", "getUTCFullYear", @@ -169,7 +167,7 @@ ["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push", "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf", "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find", - "findIndex", "copyWithin", "fill", kIteratorSymbol, "entries", "keys", "constructor"]; + "findIndex", "copyWithin", "fill", Symbol.iterator, "entries", "keys", "constructor"]; if (isNightlyBuild) { gPrototypeProperties['Array'].push('includes'); } @@ -177,8 +175,8 @@ gPrototypeProperties[c] = ["constructor", "BYTES_PER_ELEMENT"]; } gPrototypeProperties['TypedArray'] = - ["length", "buffer", "byteLength", "byteOffset", kIteratorSymbol, "subarray", - "set", "copyWithin", "find", "findIndex", "indexOf", "lastIndexOf", "reverse", + ["length", "buffer", "byteLength", "byteOffset", Symbol.iterator, "subarray", + "set", "copyWithin", "find", "findIndex", "forEach","indexOf", "lastIndexOf", "reverse", "join", "every", "some", "reduce", "reduceRight", "entries", "keys", "values"]; if (isNightlyBuild) { gPrototypeProperties['TypedArray'].push('includes'); @@ -275,12 +273,10 @@ gPrototypeProperties[classname].filter(id => typeof id === "string").toSource(), "A property on the " + classname + " prototype has changed! You need a security audit from an XPConnect peer"); - if (jsHasSymbols) { - is(Object.getOwnPropertySymbols(localProto).map(uneval).sort().toSource(), - gPrototypeProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(), - "A symbol-keyed property on the " + classname + - " prototype has been changed! You need a security audit from an XPConnect peer"); - } + is(Object.getOwnPropertySymbols(localProto).map(uneval).sort().toSource(), + gPrototypeProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(), + "A symbol-keyed property on the " + classname + + " prototype has been changed! You need a security audit from an XPConnect peer"); let protoProps = filterOut(desiredProtoProps, propsToSkip); let protoCallables = protoProps.filter(name => propertyIsGetter(localProto, name, classname) || @@ -294,11 +290,9 @@ testProtoCallables(protoCallables, xray, xrayProto, localProto); is(Object.getOwnPropertyNames(xrayProto).sort().toSource(), protoProps.toSource(), "getOwnPropertyNames works"); - if (jsHasSymbols) { - is(Object.getOwnPropertySymbols(xrayProto).map(uneval).sort().toSource(), - gPrototypeProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(), - protoProps.toSource(), "getOwnPropertySymbols works"); - } + is(Object.getOwnPropertySymbols(xrayProto).map(uneval).sort().toSource(), + gPrototypeProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(), + protoProps.toSource(), "getOwnPropertySymbols works"); is(xrayProto.constructor, iwin[classname], "constructor property works"); @@ -330,11 +324,9 @@ // Construct an object full of tricky things. let symbolProps = ''; - if (jsHasSymbols) { - uniqueSymbol = iwin.eval('var uniqueSymbol = Symbol("uniqueSymbol"); uniqueSymbol'); - symbolProps = `, [uniqueSymbol]: 43, - [Symbol.for("registrySymbolProp")]: 44`; - } + uniqueSymbol = iwin.eval('var uniqueSymbol = Symbol("uniqueSymbol"); uniqueSymbol'); + symbolProps = `, [uniqueSymbol]: 43, + [Symbol.for("registrySymbolProp")]: 44`; var trickyObject = iwin.eval(`(function() { var o = new Object({ @@ -366,11 +358,9 @@ testXray('Array', new iwin.Array(20), new iwin.Array(), propsToSkip); let symbolProps = ''; - if (jsHasSymbols) { - uniqueSymbol = iwin.eval('var uniqueSymbol = Symbol("uniqueSymbol"); uniqueSymbol'); - symbolProps = `trickyArray[uniqueSymbol] = 43; - trickyArray[Symbol.for("registrySymbolProp")] = 44;`; - } + uniqueSymbol = iwin.eval('var uniqueSymbol = Symbol("uniqueSymbol"); uniqueSymbol'); + symbolProps = `trickyArray[uniqueSymbol] = 43; + trickyArray[Symbol.for("registrySymbolProp")] = 44;`; var trickyArray = iwin.eval(`var trickyArray = []; trickyArray.primitiveProp = 42; @@ -423,21 +413,17 @@ expectedNames.push('length'); is(Object.getOwnPropertyNames(trickyObject).sort().toSource(), expectedNames.sort().toSource(), "getOwnPropertyNames should be filtered correctly"); - if (jsHasSymbols) { - var expectedSymbols = [Symbol.for("registrySymbolProp"), uniqueSymbol]; - is(Object.getOwnPropertySymbols(trickyObject).map(uneval).sort().toSource(), - expectedSymbols.map(uneval).sort().toSource(), - "getOwnPropertySymbols should be filtered correctly"); - } + var expectedSymbols = [Symbol.for("registrySymbolProp"), uniqueSymbol]; + is(Object.getOwnPropertySymbols(trickyObject).map(uneval).sort().toSource(), + expectedSymbols.map(uneval).sort().toSource(), + "getOwnPropertySymbols should be filtered correctly"); // Test that cloning uses the Xray view. var cloned = Cu.cloneInto(trickyObject, this); is(Object.getOwnPropertyNames(cloned).sort().toSource(), expectedNames.sort().toSource(), "structured clone should use the Xray view"); - if (jsHasSymbols) { - is(Object.getOwnPropertySymbols(cloned).map(uneval).sort().toSource(), - "[]", "structured cloning doesn't clone symbol-keyed properties yet"); - } + is(Object.getOwnPropertySymbols(cloned).map(uneval).sort().toSource(), + "[]", "structured cloning doesn't clone symbol-keyed properties yet"); // Test iteration and in-place modification. Beware of 'expando', which is the property // we placed on the xray proto. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/tests/mochitest/file_empty.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/tests/mochitest/file_empty.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/tests/mochitest/file_empty.html 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/tests/mochitest/file_empty.html 2015-02-03 14:33:46.000000000 +0000 @@ -1,2 +1,3 @@ -Nothing to see here + +empty test pageNothing to see here diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/wrappers/AccessCheck.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/wrappers/AccessCheck.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/wrappers/AccessCheck.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/wrappers/AccessCheck.cpp 2015-02-03 14:33:46.000000000 +0000 @@ -8,7 +8,6 @@ #include "nsJSPrincipals.h" #include "nsGlobalWindow.h" -#include "JavaScriptParent.h" #include "XPCWrapper.h" #include "XrayWrapper.h" @@ -17,6 +16,7 @@ #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/LocationBinding.h" #include "mozilla/dom/WindowBinding.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "nsIDOMWindowCollection.h" #include "nsJSUtils.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/wrappers/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/wrappers/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/wrappers/moz.build 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/wrappers/moz.build 2015-02-03 14:33:46.000000000 +0000 @@ -39,6 +39,5 @@ LOCAL_INCLUDES += [ '../../../dom/base', '../src', - '/js/ipc', ] diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/wrappers/WrapperFactory.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/wrappers/WrapperFactory.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/js/xpconnect/wrappers/WrapperFactory.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/js/xpconnect/wrappers/WrapperFactory.cpp 2015-02-03 14:33:46.000000000 +0000 @@ -16,8 +16,8 @@ #include "xpcprivate.h" #include "XPCMaps.h" #include "mozilla/dom/BindingUtils.h" -#include "JavaScriptParent.h" #include "jsfriendapi.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "mozilla/Likely.h" #include "nsContentUtils.h" #include "nsXULAppAPI.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/FrameLayerBuilder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/FrameLayerBuilder.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/FrameLayerBuilder.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/FrameLayerBuilder.cpp 2015-02-03 14:33:46.000000000 +0000 @@ -2491,6 +2491,7 @@ mVisibleRegion.Or(mVisibleRegion, aVisibleRect); mVisibleRegion.SimplifyOutward(4); + mDrawRegion.Or(mDrawRegion, mVisibleRegion); mDrawRegion.Or(mDrawRegion, aDrawRect); mDrawRegion.SimplifyOutward(4); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/FrameLayerBuilder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/FrameLayerBuilder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/FrameLayerBuilder.h 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/FrameLayerBuilder.h 2015-02-03 14:33:46.000000000 +0000 @@ -312,12 +312,6 @@ const nsPoint& aTopLeft); /** - * Gets the frame property descriptor for the given manager, or for the current - * widget layer manager if nullptr is passed. - */ - static const FramePropertyDescriptor* GetDescriptorForManager(LayerManager* aManager); - - /** * Calls GetOldLayerForFrame on the underlying frame of the display item, * and each subsequent merged frame if no layer is found for the underlying * frame. @@ -652,10 +646,6 @@ void ComputeGeometryChangeForItem(DisplayItemData* aData); protected: - void RemoveThebesItemsAndOwnerDataForLayerSubtree(Layer* aLayer, - bool aRemoveThebesItems, - bool aRemoveOwnerData); - static PLDHashOperator ProcessRemovedDisplayItems(nsRefPtrHashKey* aEntry, void* aUserArg); static PLDHashOperator RestoreDisplayItemData(nsRefPtrHashKey* aEntry, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsBidiPresUtils.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsBidiPresUtils.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsBidiPresUtils.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsBidiPresUtils.cpp 2015-02-03 14:33:47.000000000 +0000 @@ -2220,7 +2220,7 @@ uint32_t srcLength = aSource.Length(); if (srcLength == 0) return; - if (!aDest.SetLength(srcLength, fallible_t())) { + if (!aDest.SetLength(srcLength, fallible)) { return; } nsAString::const_iterator fromBegin, fromEnd; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsCaret.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsCaret.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsCaret.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsCaret.cpp 2015-02-03 14:33:48.000000000 +0000 @@ -702,7 +702,7 @@ { nsPeekOffsetStruct pos(eSelectBeginLine, eDirPrevious, 0, nsPoint(0, 0), false, true, false, - true); + true, false); if (NS_SUCCEEDED(frameAfter->PeekOffset(&pos))) { theFrame = pos.mResultFrame; theFrameOffset = pos.mContentOffset; @@ -737,7 +737,7 @@ { nsPeekOffsetStruct pos(eSelectEndLine, eDirNext, 0, nsPoint(0, 0), false, true, false, - true); + true, false); if (NS_SUCCEEDED(frameBefore->PeekOffset(&pos))) { theFrame = pos.mResultFrame; theFrameOffset = pos.mContentOffset; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsDisplayList.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsDisplayList.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsDisplayList.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsDisplayList.cpp 2015-02-03 14:33:48.000000000 +0000 @@ -362,10 +362,11 @@ aLayer->AddAnimation(); const AnimationTiming& timing = aPlayer->GetSource()->Timing(); - animation->startTime() = aPlayer->GetStartTime().IsNull() - ? TimeStamp() - : aPlayer->Timeline()->ToTimeStamp( - aPlayer->GetStartTime().Value() + timing.mDelay); + Nullable startTime = aPlayer->GetCurrentOrPendingStartTime(); + animation->startTime() = startTime.IsNull() + ? TimeStamp() + : aPlayer->Timeline()->ToTimeStamp( + startTime.Value() + timing.mDelay); animation->initialCurrentTime() = aPlayer->GetCurrentTime().Value() - timing.mDelay; animation->duration() = timing.mIterationDuration; @@ -581,13 +582,15 @@ mAllowMergingAndFlattening(true), mWillComputePluginGeometry(false), mInTransform(false), + mIsInRootChromeDocument(false), mSyncDecodeImages(false), mIsPaintingToWindow(false), mIsCompositingCheap(false), mContainsPluginItem(false), mAncestorHasTouchEventHandler(false), mAncestorHasScrollEventHandler(false), - mHaveScrollableDisplayPort(false) + mHaveScrollableDisplayPort(false), + mWindowDraggingAllowed(false) { MOZ_COUNT_CTOR(nsDisplayListBuilder); PL_InitArenaPool(&mPool, "displayListArena", 1024, @@ -697,7 +700,6 @@ Layer* aLayer, ViewID aScrollParentId, const nsRect& aViewport, - bool aForceNullScrollId, bool aIsRoot, const ContainerLayerParameters& aContainerParameters) { @@ -711,9 +713,7 @@ ViewID scrollId = FrameMetrics::NULL_SCROLL_ID; nsIContent* content = aScrollFrame ? aScrollFrame->GetContent() : nullptr; if (content) { - if (!aForceNullScrollId) { - scrollId = nsLayoutUtils::FindOrCreateIDFor(content); - } + scrollId = nsLayoutUtils::FindOrCreateIDFor(content); nsRect dp; if (nsLayoutUtils::GetDisplayPort(content, &dp)) { metrics.SetDisplayPort(CSSRect::FromAppUnits(dp)); @@ -1005,6 +1005,11 @@ mFramesMarkedForDisplay.AppendElement(state->mCaretFrame); MarkFrameForDisplay(state->mCaretFrame, nullptr); } + + nsPresContext* pc = aReferenceFrame->PresContext(); + pc->GetDocShell()->GetWindowDraggingAllowed(&mWindowDraggingAllowed); + + mIsInRootChromeDocument = !IsInSubdocument() && pc->IsChrome(); } void @@ -1013,8 +1018,15 @@ NS_ASSERTION(CurrentPresShellState()->mPresShell == aReferenceFrame->PresContext()->PresShell(), "Presshell mismatch"); + ResetMarkedFramesForDisplayList(); mPresShellStates.SetLength(mPresShellStates.Length() - 1); + + if (!mPresShellStates.IsEmpty()) { + nsPresContext* pc = CurrentPresContext(); + pc->GetDocShell()->GetWindowDraggingAllowed(&mWindowDraggingAllowed); + mIsInRootChromeDocument = !IsInSubdocument() && pc->IsChrome(); + } } void @@ -1236,7 +1248,7 @@ void nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame) { - if (!IsForPainting() || IsInSubdocument()) { + if (!mWindowDraggingAllowed || !IsForPainting()) { return; } @@ -1623,7 +1635,7 @@ nsDisplayScrollLayer::ComputeFrameMetrics(frame, rootScrollFrame, aBuilder->FindReferenceFrameFor(frame), root, FrameMetrics::NULL_SCROLL_ID, viewport, - !isRoot, isRoot, containerParameters)); + isRoot, containerParameters)); } // NS_WARNING is debug-only, so don't even bother checking the conditions in @@ -1676,7 +1688,7 @@ aBuilder->SetIsCompositingCheap(temp); layerBuilder->DidEndTransaction(); - if (document) { + if (document && widgetTransaction) { StartPendingAnimations(document, layerManager->GetAnimationReadyTime()); } @@ -2097,7 +2109,7 @@ static void RegisterThemeGeometry(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) { - if (!aBuilder->IsInSubdocument() && !aBuilder->IsInTransform()) { + if (aBuilder->IsInRootChromeDocument() && !aBuilder->IsInTransform()) { nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame); nsRect borderBox(aFrame->GetOffsetTo(displayRoot), aFrame->GetSize()); aBuilder->RegisterThemeGeometry(aFrame->StyleDisplay()->mAppearance, @@ -2236,7 +2248,8 @@ if (isThemed) { nsITheme* theme = presContext->GetTheme(); - if (theme->NeedToClearBackgroundBehindWidget(aFrame->StyleDisplay()->mAppearance)) { + if (theme->NeedToClearBackgroundBehindWidget(aFrame->StyleDisplay()->mAppearance) && + aBuilder->IsInRootChromeDocument() && !aBuilder->IsInTransform()) { bgItemList.AppendNewToTop( new (aBuilder) nsDisplayClearBackground(aBuilder, aFrame)); } @@ -3735,10 +3748,6 @@ nsDisplayOpacity::BuildLayer(nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) { - if (mOpacity == 0 && mFrame->GetContent() && - !nsLayoutUtils::HasAnimations(mFrame->GetContent(), eCSSProperty_opacity)) { - return nullptr; - } nsRefPtr container = aManager->GetLayerBuilder()-> BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList, aContainerParameters, nullptr); @@ -4092,7 +4101,7 @@ return MakeUnique( nsDisplayScrollLayer::ComputeFrameMetrics(mFrame, rootScrollFrame, ReferenceFrame(), aLayer, mScrollParentId, viewport, - false, isRootContentDocument, params)); + isRootContentDocument, params)); } static bool @@ -4426,7 +4435,7 @@ return UniquePtr(new FrameMetrics( ComputeFrameMetrics(mScrolledFrame, mScrollFrame, ReferenceFrame(), aLayer, - mScrollParentId, viewport, false, false, params))); + mScrollParentId, viewport, false, params))); } bool @@ -5214,11 +5223,13 @@ aAppUnitsPerPixel, nullptr, aOutAncestor, !frame->IsTransformed()); - result.ChangeBasis(offsetBetweenOrigins); - result = result * parent; if (aOffsetByOrigin) { - result.Translate(roundedOrigin); + result.Translate(-aProperties.mToTransformOrigin); + result.TranslatePost(offsetBetweenOrigins); + } else { + result.ChangeBasis(offsetBetweenOrigins); } + result = result * parent; return result; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsDisplayList.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsDisplayList.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsDisplayList.h 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsDisplayList.h 2015-02-03 14:33:48.000000000 +0000 @@ -424,6 +424,12 @@ bool IsInSubdocument() { return mPresShellStates.Length() > 1; } /** + * Return true if we're currently building a display list for the root + * presshell which is the presshell of a chrome document. + */ + bool IsInRootChromeDocument() { return mIsInRootChromeDocument; } + + /** * @return true if images have been set to decode synchronously. */ bool ShouldSyncDecodeImages() { return mSyncDecodeImages; } @@ -897,6 +903,7 @@ // True when we're building a display list that's directly or indirectly // under an nsDisplayTransform bool mInTransform; + bool mIsInRootChromeDocument; bool mSyncDecodeImages; bool mIsPaintingToWindow; bool mIsCompositingCheap; @@ -907,6 +914,7 @@ // display list has a display port. An async-scrollable scroll frame is one // which WantsAsyncScroll(). bool mHaveScrollableDisplayPort; + bool mWindowDraggingAllowed; }; class nsDisplayItem; @@ -3171,7 +3179,6 @@ Layer* aLayer, ViewID aScrollParentId, const nsRect& aViewport, - bool aForceNullScrollId, bool aIsRoot, const ContainerLayerParameters& aContainerParameters); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsDocumentViewer.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsDocumentViewer.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsDocumentViewer.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsDocumentViewer.cpp 2015-02-03 14:33:48.000000000 +0000 @@ -1930,14 +1930,13 @@ NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE); mBounds = aBounds; - if (mWindow) { - if (!mAttachedToParent) { - // Don't have the widget repaint. Layout will generate repaint requests - // during reflow. - mWindow->Resize(aBounds.x, aBounds.y, - aBounds.width, aBounds.height, - false); - } + + if (mWindow && !mAttachedToParent) { + // Resize the widget, but don't trigger repaint. Layout will generate + // repaint requests during reflow. + mWindow->Resize(aBounds.x, aBounds.y, + aBounds.width, aBounds.height, + false); } else if (mPresContext && mViewManager) { int32_t p2a = mPresContext->AppUnitsPerDevPixel(); mViewManager->SetWindowDimensions(NSIntPixelsToAppUnits(mBounds.width, p2a), diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsFrameManager.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsFrameManager.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsFrameManager.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsFrameManager.cpp 2015-02-03 14:33:48.000000000 +0000 @@ -155,9 +155,9 @@ if (mPlaceholderMap.IsInitialized()) { PlaceholderMapEntry *entry = static_cast - (PL_DHashTableLookup(const_cast(&mPlaceholderMap), + (PL_DHashTableSearch(const_cast(&mPlaceholderMap), aFrame)); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + if (entry) { return entry->placeholderFrame; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsLayoutUtils.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsLayoutUtils.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsLayoutUtils.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsLayoutUtils.cpp 2015-02-03 14:33:48.000000000 +0000 @@ -908,7 +908,7 @@ // screen resolution; since this is what Layout does most of the time, // this is a good approximation. A proper solution would involve moving the // choosing of the resolution to display-list building time. - if (alignmentX > 0 && alignmentY > 0) { + if (gfxPrefs::LayersTilesEnabled() && (alignmentX > 0 && alignmentY > 0)) { // Inflate the rectangle by 1 so that we always push to the next tile // boundary. This is desirable to stop from having a rectangle with a // moving origin occasionally being smaller when it coincidentally lines @@ -1912,13 +1912,13 @@ return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); return GetEventCoordinatesRelativeTo(aEvent, - LayoutDeviceIntPoint::ToUntyped(aEvent->AsGUIEvent()->refPoint), + aEvent->AsGUIEvent()->refPoint, aFrame); } nsPoint nsLayoutUtils::GetEventCoordinatesRelativeTo(const WidgetEvent* aEvent, - const nsIntPoint aPoint, + const LayoutDeviceIntPoint& aPoint, nsIFrame* aFrame) { if (!aFrame) { @@ -1935,7 +1935,7 @@ nsPoint nsLayoutUtils::GetEventCoordinatesRelativeTo(nsIWidget* aWidget, - const nsIntPoint aPoint, + const LayoutDeviceIntPoint& aPoint, nsIFrame* aFrame) { if (!aFrame || !aWidget) { @@ -2658,7 +2658,7 @@ nsPoint nsLayoutUtils::TranslateWidgetToView(nsPresContext* aPresContext, - nsIWidget* aWidget, nsIntPoint aPt, + nsIWidget* aWidget, const LayoutDeviceIntPoint& aPt, nsView* aView) { nsPoint viewOffset; @@ -2667,7 +2667,8 @@ return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); } - nsIntPoint widgetPoint = aPt + WidgetToWidgetOffset(aWidget, viewWidget); + LayoutDeviceIntPoint widgetPoint = aPt + + LayoutDeviceIntPoint::FromUntyped(WidgetToWidgetOffset(aWidget, viewWidget)); nsPoint widgetAppUnits(aPresContext->DevPixelsToAppUnits(widgetPoint.x), aPresContext->DevPixelsToAppUnits(widgetPoint.y)); return widgetAppUnits - viewOffset; @@ -3252,7 +3253,17 @@ // We could instead have the compositor send back an equivalent to WillPaintWindow, // but it should be close enough to now not to matter. if (layerManager && !layerManager->NeedsWidgetInvalidation()) { - rootPresContext->ApplyPluginGeometryUpdates(); +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + if (XRE_GetProcessType() == GeckoProcessType_Content) { + // If this is a remotely managed widget (PluginWidgetProxy in content) + // store this information in the compositor, which ships this + // over to chrome for application when we paint. + rootPresContext->CollectPluginGeometryUpdates(layerManager); + } else +#endif + { + rootPresContext->ApplyPluginGeometryUpdates(); + } } // We told the compositor thread not to composite when it received the transaction because @@ -7636,15 +7647,8 @@ /* static */ bool nsLayoutUtils::WantSubAPZC() { - // TODO Turn this on for inprocess OMTC on all platforms - bool wantSubAPZC = gfxPrefs::AsyncPanZoomEnabled() && - gfxPrefs::APZSubframeEnabled(); -#ifdef MOZ_WIDGET_GONK - if (XRE_GetProcessType() != GeckoProcessType_Content) { - wantSubAPZC = false; - } -#endif - return wantSubAPZC; + return gfxPrefs::AsyncPanZoomEnabled() && + gfxPrefs::APZSubframeEnabled(); } /* static */ bool diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsLayoutUtils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsLayoutUtils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsLayoutUtils.h 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsLayoutUtils.h 2015-02-03 14:33:48.000000000 +0000 @@ -163,10 +163,10 @@ */ static bool GetDisplayPort(nsIContent* aContent, nsRect *aResult = nullptr); - MOZ_BEGIN_NESTED_ENUM_CLASS(RepaintMode, uint8_t) + enum class RepaintMode : uint8_t { Repaint, DoNotRepaint - MOZ_END_NESTED_ENUM_CLASS(RepaintMode) + }; /** * Set the display port margins for a content element to be used with a @@ -679,7 +679,7 @@ */ static nsPoint GetEventCoordinatesRelativeTo( const mozilla::WidgetEvent* aEvent, - const nsIntPoint aPoint, + const mozilla::LayoutDeviceIntPoint& aPoint, nsIFrame* aFrame); /** @@ -693,7 +693,7 @@ * the event is not a GUI event). */ static nsPoint GetEventCoordinatesRelativeTo(nsIWidget* aWidget, - const nsIntPoint aPoint, + const mozilla::LayoutDeviceIntPoint& aPoint, nsIFrame* aFrame); /** @@ -716,7 +716,8 @@ * @return the point in the view's coordinates */ static nsPoint TranslateWidgetToView(nsPresContext* aPresContext, - nsIWidget* aWidget, nsIntPoint aPt, + nsIWidget* aWidget, + const mozilla::LayoutDeviceIntPoint& aPt, nsView* aView); /** @@ -2577,8 +2578,6 @@ static bool IsAPZTestLoggingEnabled(); }; -MOZ_FINISH_NESTED_ENUM_CLASS(nsLayoutUtils::RepaintMode) - template /* static */ bool nsLayoutUtils::PointIsCloserToRect(PointType aPoint, const RectType& aRect, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsPresContext.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsPresContext.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsPresContext.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsPresContext.cpp 2015-02-03 14:33:48.000000000 +0000 @@ -56,6 +56,7 @@ #include "mozilla/dom/TabParent.h" #include "nsRefreshDriver.h" #include "Layers.h" +#include "ClientLayerManager.h" #include "nsIDOMEvent.h" #include "gfxPrefs.h" #include "nsIDOMChromeWindow.h" @@ -3082,6 +3083,28 @@ mRegisteredPlugins.EnumerateEntries(PluginDidSetGeometryEnumerator, nullptr); } +void +nsRootPresContext::CollectPluginGeometryUpdates(LayerManager* aLayerManager) +{ +#ifndef XP_MACOSX + // Collect and pass plugin widget configurations down to the compositor + // for transmission to the chrome process. + NS_ASSERTION(aLayerManager, "layer manager is invalid!"); + mozilla::layers::ClientLayerManager* clm = aLayerManager->AsClientLayerManager(); + PluginGetGeometryUpdateClosure closure; + mRegisteredPlugins.EnumerateEntries(PluginGetGeometryUpdate, &closure); + if (closure.mConfigurations.IsEmpty()) { + mRegisteredPlugins.EnumerateEntries(PluginDidSetGeometryEnumerator, nullptr); + return; + } + SortConfigurations(&closure.mConfigurations); + if (clm) { + clm->StorePluginWidgetConfigurations(closure.mConfigurations); + } + mRegisteredPlugins.EnumerateEntries(PluginDidSetGeometryEnumerator, nullptr); +#endif // #ifndef XP_MACOSX +} + static void NotifyDidPaintForSubtreeCallback(nsITimer *aTimer, void *aClosure) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsPresContext.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsPresContext.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsPresContext.h 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsPresContext.h 2015-02-03 14:33:48.000000000 +0000 @@ -76,6 +76,7 @@ } namespace layers { class ContainerLayer; +class LayerManager; } } @@ -1483,6 +1484,12 @@ */ void ApplyPluginGeometryUpdates(); + /** + * Transfer stored plugin geometry updates to the compositor. Called during + * reflow, data is shipped over with layer updates. e10s specific. + */ + void CollectPluginGeometryUpdates(mozilla::layers::LayerManager* aLayerManager); + virtual bool IsRoot() MOZ_OVERRIDE { return true; } /** diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsPresShell.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsPresShell.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsPresShell.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsPresShell.cpp 2015-02-03 14:33:48.000000000 +0000 @@ -6753,8 +6753,7 @@ if (!rootFrame) { nsView* rootView = mViewManager->GetRootView(); mMouseLocation = nsLayoutUtils::TranslateWidgetToView(mPresContext, - aEvent->widget, LayoutDeviceIntPoint::ToUntyped(aEvent->refPoint), - rootView); + aEvent->widget, aEvent->refPoint, rootView); } else { mMouseLocation = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, rootFrame); @@ -8518,12 +8517,12 @@ } // see if we should use the caret position for the popup - nsIntPoint caretPoint; + LayoutDeviceIntPoint caretPoint; // Beware! This may flush notifications via synchronous // ScrollSelectionIntoView. if (PrepareToUseCaretPosition(aEvent->widget, caretPoint)) { // caret position is good - aEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(caretPoint); + aEvent->refPoint = caretPoint; return true; } @@ -8564,7 +8563,8 @@ // relative to. The returned point is in device pixels realtive to the // widget passed in. bool -PresShell::PrepareToUseCaretPosition(nsIWidget* aEventWidget, nsIntPoint& aTargetPt) +PresShell::PrepareToUseCaretPosition(nsIWidget* aEventWidget, + LayoutDeviceIntPoint& aTargetPt) { nsresult rv; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsPresShell.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsPresShell.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsPresShell.h 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsPresShell.h 2015-02-03 14:33:48.000000000 +0000 @@ -690,7 +690,8 @@ bool AdjustContextMenuKeyEvent(mozilla::WidgetMouseEvent* aEvent); // - bool PrepareToUseCaretPosition(nsIWidget* aEventWidget, nsIntPoint& aTargetPt); + bool PrepareToUseCaretPosition(nsIWidget* aEventWidget, + mozilla::LayoutDeviceIntPoint& aTargetPt); // Get the selected item and coordinates in device pixels relative to root // document's root view for element, first ensuring the element is onscreen diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsRefreshDriver.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsRefreshDriver.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/nsRefreshDriver.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/nsRefreshDriver.cpp 2015-02-03 14:33:48.000000000 +0000 @@ -314,6 +314,8 @@ public: explicit RefreshDriverVsyncObserver(VsyncRefreshDriverTimer* aVsyncRefreshDriverTimer) : mVsyncRefreshDriverTimer(aVsyncRefreshDriverTimer) + , mRefreshTickLock("RefreshTickLock") + , mProcessedVsync(true) { MOZ_ASSERT(NS_IsMainThread()); } @@ -321,6 +323,19 @@ virtual bool NotifyVsync(TimeStamp aVsyncTimestamp) MOZ_OVERRIDE { if (!NS_IsMainThread()) { + MOZ_ASSERT(XRE_IsParentProcess()); + // Compress vsync notifications such that only 1 may run at a time + // This is so that we don't flood the refresh driver with vsync messages + // if the main thread is blocked for long periods of time + { // scope lock + MonitorAutoLock lock(mRefreshTickLock); + mRecentVsync = aVsyncTimestamp; + if (!mProcessedVsync) { + return true; + } + mProcessedVsync = false; + } + nsCOMPtr vsyncEvent = NS_NewRunnableMethodWithArg(this, &RefreshDriverVsyncObserver::TickRefreshDriver, @@ -346,6 +361,12 @@ { MOZ_ASSERT(NS_IsMainThread()); + if (XRE_IsParentProcess()) { + MonitorAutoLock lock(mRefreshTickLock); + aVsyncTimestamp = mRecentVsync; + mProcessedVsync = true; + } + // We might have a problem that we call ~VsyncRefreshDriverTimer() before // the scheduled TickRefreshDriver() runs. Check mVsyncRefreshDriverTimer // before use. @@ -358,6 +379,9 @@ // be always available before Shutdown(). We can just use the raw pointer // here. VsyncRefreshDriverTimer* mVsyncRefreshDriverTimer; + Monitor mRefreshTickLock; + TimeStamp mRecentVsync; + bool mProcessedVsync; }; // RefreshDriverVsyncObserver virtual ~VsyncRefreshDriverTimer() @@ -416,9 +440,13 @@ Tick(vsyncJsNow, aTimeStamp); } - nsRefPtr mVsyncDispatcher; nsRefPtr mVsyncObserver; - VsyncChild* mVsyncChild; + // Used for parent process. + nsRefPtr mVsyncDispatcher; + // Used for child process. + // The mVsyncChild will be always available before VsncChild::ActorDestroy(). + // After ActorDestroy(), StartTimer() and StopTimer() calls will be non-op. + nsRefPtr mVsyncChild; }; // VsyncRefreshDriverTimer /* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/PositionedEventTargeting.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/PositionedEventTargeting.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/PositionedEventTargeting.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/PositionedEventTargeting.cpp 2015-02-03 14:33:46.000000000 +0000 @@ -75,6 +75,7 @@ bool mRegistered; bool mTouchOnly; bool mRepositionEventCoords; + bool mTouchClusterDetection; }; static EventRadiusPrefs sMouseEventRadiusPrefs; @@ -121,6 +122,9 @@ nsPrintfCString repositionPref("ui.%s.radius.reposition", prefBranch); Preferences::AddBoolVarCache(&prefs->mRepositionEventCoords, repositionPref.get(), false); + + nsPrintfCString touchClusterPref("ui.zoomedview.enabled", prefBranch); + Preferences::AddBoolVarCache(&prefs->mTouchClusterDetection, touchClusterPref.get(), false); } return prefs; @@ -316,7 +320,8 @@ static nsIFrame* GetClosest(nsIFrame* aRoot, const nsPoint& aPointRelativeToRootFrame, const nsRect& aTargetRect, const EventRadiusPrefs* aPrefs, - nsIFrame* aRestrictToDescendants, nsTArray& aCandidates) + nsIFrame* aRestrictToDescendants, nsTArray& aCandidates, + int32_t* aElementsInCluster) { nsIFrame* bestTarget = nullptr; // Lower is better; distance is in appunits @@ -358,6 +363,8 @@ continue; } + (*aElementsInCluster)++; + // distance is in appunits float distance = ComputeDistanceFromRegion(aPointRelativeToRootFrame, region); nsIContent* content = f->GetContent(); @@ -424,10 +431,18 @@ return target; } + int32_t elementsInCluster = 0; + nsIFrame* closestClickable = GetClosest(aRootFrame, aPointRelativeToRootFrame, targetRect, prefs, - restrictToDescendants, candidates); + restrictToDescendants, candidates, &elementsInCluster); if (closestClickable) { + if (prefs->mTouchClusterDetection && elementsInCluster > 1) { + if (aEvent->mClass == eMouseEventClass) { + WidgetMouseEventBase* mouseEventBase = aEvent->AsMouseEventBase(); + mouseEventBase->hitCluster = true; + } + } target = closestClickable; } PET_LOG("Final target is %p\n", target); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/SelectionCarets.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/SelectionCarets.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/SelectionCarets.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/SelectionCarets.cpp 2015-02-03 14:33:46.000000000 +0000 @@ -78,6 +78,7 @@ , mCaretCenterToDownPointOffsetY(0) , mDragMode(NONE) , mAsyncPanZoomEnabled(false) + , mInAsyncPanZoomGesture(false) , mEndCaretVisible(false) , mStartCaretVisible(false) , mSelectionVisibleInScrollFrames(true) @@ -160,7 +161,7 @@ } WidgetTouchEvent *touchEvent = aEvent->AsTouchEvent(); - nsIntPoint movePoint; + LayoutDeviceIntPoint movePoint; int32_t nowTouchId = -1; if (touchEvent && !touchEvent->touches.IsEmpty()) { // If touch happened, just grab event with same identifier @@ -182,7 +183,7 @@ nowTouchId = touchEvent->touches[0]->Identifier(); } } else if (mouseEvent) { - movePoint = LayoutDeviceIntPoint::ToUntyped(mouseEvent->AsGUIEvent()->refPoint); + movePoint = mouseEvent->AsGUIEvent()->refPoint; } // Get event coordinate relative to root frame @@ -697,6 +698,7 @@ true, true, //limit on scrolled views false, + false, false); nsresult rv = theFrame->PeekOffset(&pos); if (NS_FAILED(rv)) { @@ -771,8 +773,10 @@ return nsEventStatus_eConsumeNoDefault; } + // Limit the drag behavior not to cross the end of last selection range + // when drag the start frame and vice versa nsRefPtr range = mDragMode == START_FRAME ? - selection->GetRangeAt(0) : selection->GetRangeAt(rangeCount - 1); + selection->GetRangeAt(rangeCount - 1) : selection->GetRangeAt(0); if (!CompareRangeWithContentOffset(range, fs, offsets, mDragMode)) { return nsEventStatus_eConsumeNoDefault; } @@ -1180,6 +1184,7 @@ void SelectionCarets::AsyncPanZoomStarted(const mozilla::CSSIntPoint aScrollPos) { + mInAsyncPanZoomGesture = true; SetVisibility(false); SELECTIONCARETS_LOG("Dispatch scroll started with position x=%d, y=%d", @@ -1190,6 +1195,7 @@ void SelectionCarets::AsyncPanZoomStopped(const mozilla::CSSIntPoint aScrollPos) { + mInAsyncPanZoomGesture = false; SELECTIONCARETS_LOG("Update selection carets after APZ is stopped!"); UpdateSelectionCarets(); @@ -1206,12 +1212,20 @@ void SelectionCarets::ScrollPositionChanged() { - if (!mAsyncPanZoomEnabled && mVisible) { - SetVisibility(false); - //TODO: handling scrolling for selection bubble when APZ is off + if (mVisible) { + if (!mAsyncPanZoomEnabled) { + SetVisibility(false); + //TODO: handling scrolling for selection bubble when APZ is off - SELECTIONCARETS_LOG("Launch scroll end detector"); - LaunchScrollEndDetector(); + SELECTIONCARETS_LOG("Launch scroll end detector"); + LaunchScrollEndDetector(); + } else { + if (!mInAsyncPanZoomGesture) { + UpdateSelectionCarets(); + DispatchSelectionStateChangedEvent(GetSelection(), + SelectionState::Updateposition); + } + } } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/SelectionCarets.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/SelectionCarets.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/SelectionCarets.h 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/SelectionCarets.h 2015-02-03 14:33:46.000000000 +0000 @@ -260,6 +260,8 @@ // True if AsyncPanZoom is enabled bool mAsyncPanZoomEnabled; + // True if AsyncPanZoom is started + bool mInAsyncPanZoomGesture; bool mEndCaretVisible; bool mStartCaretVisible; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/bug613807-1.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/bug613807-1.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/bug613807-1.html 2015-01-25 22:24:38.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/bug613807-1.html 2015-02-03 14:33:48.000000000 +0000 @@ -46,15 +46,12 @@ var domWindowUtils = SpecialPowers.getDOMWindowUtils(window); - // start composition - synthesizeComposition({ type: "compositionstart" }); - // input raw characters synthesizeCompositionChange( { composition: { string: "\u306D", clauses: [ - { length: 1, attr: domWindowUtils.COMPOSITION_ATTR_RAWINPUT } + { length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE } ] }, caret: { start: 1, length: 0 } @@ -63,7 +60,7 @@ { composition: { string: "\u306D\u3053", clauses: [ - { length: 2, attr: domWindowUtils.COMPOSITION_ATTR_RAWINPUT } + { length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE } ] }, caret: { start: 2, length: 0 } @@ -74,7 +71,7 @@ { composition: { string: "\u732B", clauses: [ - { length: 1, attr: domWindowUtils.COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT } + { length: 1, attr: COMPOSITION_ATTR_SELECTED_CLAUSE } ] }, caret: { start: 1, length: 0 } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/bug989012-1.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/bug989012-1.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/bug989012-1.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/bug989012-1.html 2015-02-03 14:33:48.000000000 +0000 @@ -0,0 +1,24 @@ + + + + + + +
    fooIMAGEbar
    + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/bug989012-1-ref.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/bug989012-1-ref.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/bug989012-1-ref.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/bug989012-1-ref.html 2015-02-03 14:33:48.000000000 +0000 @@ -0,0 +1,21 @@ + + + + + + +
    fooIMAGEbar
    + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/bug989012-2.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/bug989012-2.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/bug989012-2.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/bug989012-2.html 2015-02-03 14:33:48.000000000 +0000 @@ -0,0 +1,29 @@ + + + + + + + +
    foobar
    + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/bug989012-2-ref.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/bug989012-2-ref.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/bug989012-2-ref.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/bug989012-2-ref.html 2015-02-03 14:33:48.000000000 +0000 @@ -0,0 +1,26 @@ + + + + + + + +
    foobar
    + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/bug989012-3.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/bug989012-3.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/bug989012-3.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/bug989012-3.html 2015-02-03 14:33:48.000000000 +0000 @@ -0,0 +1,31 @@ + + + + + + + +
    foobar
    + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/bug989012-3-ref.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/bug989012-3-ref.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/bug989012-3-ref.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/bug989012-3-ref.html 2015-02-03 14:33:48.000000000 +0000 @@ -0,0 +1,28 @@ + + + + + + + +
    foobar
    + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/marionette/test_selectioncarets.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/marionette/test_selectioncarets.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/marionette/test_selectioncarets.py 2015-01-25 22:24:38.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/marionette/test_selectioncarets.py 2015-02-03 14:33:48.000000000 +0000 @@ -17,6 +17,7 @@ _textarea_rtl_selector = (By.ID, 'textarea_rtl') _contenteditable_selector = (By.ID, 'contenteditable') _content_selector = (By.ID, 'content') + _contenteditable2_selector = (By.ID, 'contenteditable2') def setUp(self): # Code to execute before a tests are run. @@ -38,6 +39,7 @@ self._textarea_rtl = self.marionette.find_element(*self._textarea_rtl_selector) self._contenteditable = self.marionette.find_element(*self._contenteditable_selector) self._content = self.marionette.find_element(*self._content_selector) + self._contenteditable2 = self.marionette.find_element(*self._contenteditable2_selector) def _first_word_location(self, el): '''Get the location (x, y) of the first word in el. @@ -115,6 +117,12 @@ words = original_content.split() self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.') + # Get the location of the selection carets at the end of the content for + # later use. + sel.select_all() + (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() + el.tap() + # Goal: Select the first character. target_content = original_content[0] @@ -126,6 +134,10 @@ x, y = self._first_word_location(el) self._long_press_to_select(el, x, y) + # Move the right caret to the end of the content. + (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() + self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() + # Move the right caret to the position of the left caret. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y,).perform() @@ -309,3 +321,11 @@ self.openTestHtml(enabled=True) self._test_focus_obtained_by_long_press(self._contenteditable, self._content) + ######################################################################## + #
    contenteditable2 test cases with selection carets enabled + ######################################################################## + def test_contenteditable_minimum_select_one_character(self): + self.openTestHtml(enabled=True) + self._test_minimum_select_one_character(self._contenteditable2, self.assertEqual) + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/mochitest.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/mochitest.ini --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/mochitest.ini 2015-01-25 22:24:38.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/mochitest.ini 2015-02-03 14:33:48.000000000 +0000 @@ -53,6 +53,12 @@ bug570378-persian-5.html bug570378-persian-5-ref.html bug644768.html + bug989012-1.html + bug989012-1-ref.html + bug989012-2.html + bug989012-2-ref.html + bug989012-3.html + bug989012-3-ref.html bug1061468.html bug1061468-ref.html bug1109968-1-ref.html diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/test_reftests_with_caret.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/test_reftests_with_caret.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/tests/test_reftests_with_caret.html 2015-01-25 22:24:38.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/tests/test_reftests_with_caret.html 2015-02-03 14:33:48.000000000 +0000 @@ -107,6 +107,9 @@ [ 'bug106855-2.html' , 'bug106855-1-ref.html' ] , [ 'bug389321-2.html' , 'bug389321-2-ref.html' ] , [ 'bug613807-1.html' , 'bug613807-1-ref.html' ] , + [ 'bug989012-1.html' , 'bug989012-1-ref.html' ] , + [ 'bug989012-2.html' , 'bug989012-2-ref.html' ] , + [ 'bug989012-3.html' , 'bug989012-3-ref.html' ] , [ 'bug1082486-1.html', 'bug1082486-1-ref.html'] , [ 'bug1082486-2.html', 'bug1082486-2-ref.html'] , // The following test cases are all involving with one sending diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/TouchCaret.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/TouchCaret.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/TouchCaret.cpp 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/TouchCaret.cpp 2015-02-03 14:33:46.000000000 +0000 @@ -458,7 +458,8 @@ return false; } - if (!nsLayoutUtils::IsRectVisibleInScrollFrames(focusFrame, focusRect)) { + if (mState != TOUCHCARET_TOUCHDRAG_ACTIVE && + !nsLayoutUtils::IsRectVisibleInScrollFrames(focusFrame, focusRect)) { TOUCHCARET_LOG("Caret does not show in the scrollable frame!"); return false; } @@ -628,7 +629,7 @@ if (aEvent->touches[i]->mIdentifier == aIdentifier) { // Get event coordinate relative to canvas frame. nsIFrame* canvasFrame = GetCanvasFrame(); - nsIntPoint touchIntPoint = aEvent->touches[i]->mRefPoint; + LayoutDeviceIntPoint touchIntPoint = aEvent->touches[i]->mRefPoint; return nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, touchIntPoint, canvasFrame); @@ -642,8 +643,7 @@ { // Get event coordinate relative to canvas frame. nsIFrame* canvasFrame = GetCanvasFrame(); - nsIntPoint mouseIntPoint = - LayoutDeviceIntPoint::ToUntyped(aEvent->AsGUIEvent()->refPoint); + LayoutDeviceIntPoint mouseIntPoint = aEvent->AsGUIEvent()->refPoint; return nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, mouseIntPoint, canvasFrame); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/UnitTransforms.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/UnitTransforms.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/base/UnitTransforms.h 2015-01-25 22:24:37.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/base/UnitTransforms.h 2015-02-03 14:33:46.000000000 +0000 @@ -19,7 +19,7 @@ // Feel free to add more justifications to PixelCastJustification, along with // a comment that explains under what circumstances it is appropriate to use. -MOZ_BEGIN_ENUM_CLASS(PixelCastJustification, uint8_t) +enum class PixelCastJustification : uint8_t { // For the root layer, Screen Pixel = Parent Layer Pixel. ScreenIsParentLayerForRoot, // For the root composition size we want to view it as layer pixels in any layer @@ -31,7 +31,7 @@ // systems is not available (for example, because the object that stores it // is being destroyed), so fall back to the identity. TransformNotAvailable -MOZ_END_ENUM_CLASS(PixelCastJustification) +}; template gfx::SizeTyped ViewAs(const gfx::SizeTyped& aSize, PixelCastJustification) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/build/nsLayoutModule.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/build/nsLayoutModule.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/build/nsLayoutModule.cpp 2015-01-25 22:24:38.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/build/nsLayoutModule.cpp 2015-02-03 14:33:48.000000000 +0000 @@ -189,7 +189,6 @@ #endif /* MOZ_XUL */ #include "inDeepTreeWalker.h" -#include "inFlasher.h" #include "inCSSValueSearch.h" #include "inDOMUtils.h" @@ -250,6 +249,8 @@ #include "mozilla/dom/presentation/PresentationDeviceManager.h" +#include "mozilla/TextInputProcessor.h" + using namespace mozilla; using namespace mozilla::dom; using mozilla::dom::alarm::AlarmHalService; @@ -377,6 +378,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(TVChannelData) NS_GENERIC_FACTORY_CONSTRUCTOR(TVProgramData) NS_GENERIC_FACTORY_CONSTRUCTOR(PresentationDeviceManager) +NS_GENERIC_FACTORY_CONSTRUCTOR(TextInputProcessor) //----------------------------------------------------------------------------- @@ -522,7 +524,6 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(inDOMView) #endif NS_GENERIC_FACTORY_CONSTRUCTOR(inDeepTreeWalker) -NS_GENERIC_FACTORY_CONSTRUCTOR(inFlasher) NS_GENERIC_FACTORY_CONSTRUCTOR(inCSSValueSearch) NS_GENERIC_FACTORY_CONSTRUCTOR(inDOMUtils) @@ -676,7 +677,6 @@ NS_DEFINE_NAMED_CID(IN_DOMVIEW_CID); #endif NS_DEFINE_NAMED_CID(IN_DEEPTREEWALKER_CID); -NS_DEFINE_NAMED_CID(IN_FLASHER_CID); NS_DEFINE_NAMED_CID(IN_CSSVALUESEARCH_CID); NS_DEFINE_NAMED_CID(IN_DOMUTILS_CID); NS_DEFINE_NAMED_CID(NS_CONTENT_VIEWER_CID); @@ -824,6 +824,8 @@ NS_DEFINE_NAMED_CID(PRESENTATION_DEVICE_MANAGER_CID); +NS_DEFINE_NAMED_CID(TEXT_INPUT_PROCESSOR_CID); + static nsresult CreateWindowCommandTableConstructor(nsISupports *aOuter, REFNSIID aIID, void **aResult) @@ -972,7 +974,6 @@ { &kIN_DOMVIEW_CID, false, nullptr, inDOMViewConstructor }, #endif { &kIN_DEEPTREEWALKER_CID, false, nullptr, inDeepTreeWalkerConstructor }, - { &kIN_FLASHER_CID, false, nullptr, inFlasherConstructor }, { &kIN_CSSVALUESEARCH_CID, false, nullptr, inCSSValueSearchConstructor }, { &kIN_DOMUTILS_CID, false, nullptr, inDOMUtilsConstructor }, { &kNS_CONTENT_VIEWER_CID, false, nullptr, CreateContentViewer }, @@ -1113,6 +1114,7 @@ { &kTV_CHANNEL_DATA_CID, false, nullptr, TVChannelDataConstructor }, { &kTV_PROGRAM_DATA_CID, false, nullptr, TVProgramDataConstructor }, { &kPRESENTATION_DEVICE_MANAGER_CID, false, nullptr, PresentationDeviceManagerConstructor }, + { &kTEXT_INPUT_PROCESSOR_CID, false, nullptr, TextInputProcessorConstructor }, { nullptr } }; @@ -1131,7 +1133,6 @@ { "@mozilla.org/inspector/dom-view;1", &kIN_DOMVIEW_CID }, #endif { "@mozilla.org/inspector/deep-tree-walker;1", &kIN_DEEPTREEWALKER_CID }, - { "@mozilla.org/inspector/flasher;1", &kIN_FLASHER_CID }, { "@mozilla.org/inspector/search;1?type=cssvalue", &kIN_CSSVALUESEARCH_CID }, { IN_DOMUTILS_CONTRACTID, &kIN_DOMUTILS_CID }, { "@mozilla.org/xml/xml-document;1", &kNS_XMLDOCUMENT_CID }, @@ -1273,6 +1274,7 @@ { NS_MOBILE_CONNECTION_SERVICE_CONTRACTID, &kNS_MOBILE_CONNECTION_SERVICE_CID }, { NS_VOICEMAIL_SERVICE_CONTRACTID, &kNS_VOICEMAIL_SERVICE_CID }, { PRESENTATION_DEVICE_MANAGER_CONTRACTID, &kPRESENTATION_DEVICE_MANAGER_CID }, + { "@mozilla.org/text-input-processor;1", &kTEXT_INPUT_PROCESSOR_CID }, { nullptr } }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/forms/nsButtonFrameRenderer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/forms/nsButtonFrameRenderer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/forms/nsButtonFrameRenderer.h 2015-01-25 22:24:38.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/forms/nsButtonFrameRenderer.h 2015-02-03 14:33:49.000000000 +0000 @@ -74,7 +74,6 @@ private: // cached styles for focus and outline. - nsRefPtr mBorderStyle; nsRefPtr mInnerFocusStyle; nsRefPtr mOuterFocusStyle; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/forms/nsNumberControlFrame.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/forms/nsNumberControlFrame.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/forms/nsNumberControlFrame.cpp 2015-01-25 22:24:38.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/forms/nsNumberControlFrame.cpp 2015-02-03 14:33:49.000000000 +0000 @@ -585,8 +585,7 @@ LayoutDeviceIntPoint absPoint = aEvent->refPoint; nsPoint point = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, - LayoutDeviceIntPoint::ToUntyped(absPoint), - mSpinBox->GetPrimaryFrame()); + absPoint, mSpinBox->GetPrimaryFrame()); if (point != nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) { if (point.y < mSpinBox->GetPrimaryFrame()->GetSize().height / 2) { return eSpinButtonUp; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/forms/nsRangeFrame.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/forms/nsRangeFrame.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/forms/nsRangeFrame.cpp 2015-01-25 22:24:38.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/forms/nsRangeFrame.cpp 2015-02-03 14:33:49.000000000 +0000 @@ -492,14 +492,12 @@ if (aEvent->mClass == eTouchEventClass) { MOZ_ASSERT(aEvent->AsTouchEvent()->touches.Length() == 1, "Unexpected number of touches"); - absPoint = LayoutDeviceIntPoint::FromUntyped( - aEvent->AsTouchEvent()->touches[0]->mRefPoint); + absPoint = aEvent->AsTouchEvent()->touches[0]->mRefPoint; } else { absPoint = aEvent->refPoint; } nsPoint point = - nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, - LayoutDeviceIntPoint::ToUntyped(absPoint), this); + nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, absPoint, this); if (point == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) { // We don't want to change the current value for this error state. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/crashtests/385526.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/crashtests/385526.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/crashtests/385526.html 2015-01-25 22:24:38.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/crashtests/385526.html 2015-02-03 14:33:50.000000000 +0000 @@ -1,4 +1,4 @@ - + @@ -104,10 +104,13 @@ mmmmmmmmmmmmmmmmmmmmmmmmmm - \ No newline at end of file + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/crashtests/crashtests.list thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/crashtests/crashtests.list --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/crashtests/crashtests.list 2015-01-25 22:24:38.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/crashtests/crashtests.list 2015-02-03 14:33:50.000000000 +0000 @@ -125,7 +125,7 @@ load 385414-1.html load 385414-2.html load 385426-1.html -skip-if(B2G) skip-if(Android&&AndroidVersion==10) load 385526.html # Bug 891347 +load 385526.html load 385681.html load 385885-1.xul load 386799-1.html diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsBlockFrame.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsBlockFrame.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsBlockFrame.cpp 2015-01-25 22:24:38.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsBlockFrame.cpp 2015-02-03 14:33:50.000000000 +0000 @@ -2921,10 +2921,22 @@ } static inline bool -IsNonAutoNonZeroHeight(const nsStyleCoord& aCoord) +IsNonAutoNonZeroBSize(const nsStyleCoord& aCoord) { - if (aCoord.GetUnit() == eStyleUnit_Auto) + nsStyleUnit unit = aCoord.GetUnit(); + if (unit == eStyleUnit_Auto || + // The enumerated values were originally aimed at inline-size + // (or width, as it was before logicalization). For now, let them + // return false here, so we treat them like 'auto' pending a + // real implementation. (See bug 1126420.) + // + // FIXME (bug 567039, bug 527285) + // This isn't correct for the 'fill' value, which should more + // likely (but not necessarily, depending on the available space) + // be returning true. + unit == eStyleUnit_Enumerated) { return false; + } if (aCoord.IsCoordPercentCalcUnit()) { // If we evaluate the length/percent/calc at a percentage basis of // both nscoord_MAX and 0, and it's zero both ways, then it's a zero @@ -2951,13 +2963,13 @@ bool vertical = GetWritingMode().IsVertical(); if (vertical) { - if (IsNonAutoNonZeroHeight(position->mMinWidth) || - IsNonAutoNonZeroHeight(position->mWidth)) { + if (IsNonAutoNonZeroBSize(position->mMinWidth) || + IsNonAutoNonZeroBSize(position->mWidth)) { return false; } } else { - if (IsNonAutoNonZeroHeight(position->mMinHeight) || - IsNonAutoNonZeroHeight(position->mHeight)) { + if (IsNonAutoNonZeroBSize(position->mMinHeight) || + IsNonAutoNonZeroBSize(position->mHeight)) { return false; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsCanvasFrame.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsCanvasFrame.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsCanvasFrame.cpp 2015-01-25 22:24:38.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsCanvasFrame.cpp 2015-02-03 14:33:50.000000000 +0000 @@ -53,6 +53,8 @@ NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) +NS_IMPL_ISUPPORTS(nsCanvasFrame::DummyTouchListener, nsIDOMEventListener) + void nsCanvasFrame::ShowCustomContentContainer() { @@ -101,6 +103,12 @@ classValue.AppendLiteral("moz-touchcaret hidden"); rv = mTouchCaretElement->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, classValue, true); + + if (!mDummyTouchListener) { + mDummyTouchListener = new DummyTouchListener(); + } + mTouchCaretElement->AddEventListener(NS_LITERAL_STRING("touchstart"), + mDummyTouchListener, false); NS_ENSURE_SUCCESS(rv, rv); } @@ -124,6 +132,14 @@ rv = mSelectionCaretsEndElement->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, NS_LITERAL_STRING("moz-selectioncaret-right hidden"), true); + + if (!mDummyTouchListener) { + mDummyTouchListener = new DummyTouchListener(); + } + mSelectionCaretsStartElement->AddEventListener(NS_LITERAL_STRING("touchstart"), + mDummyTouchListener, false); + mSelectionCaretsEndElement->AddEventListener(NS_LITERAL_STRING("touchstart"), + mDummyTouchListener, false); NS_ENSURE_SUCCESS(rv, rv); } @@ -179,6 +195,21 @@ sf->RemoveScrollPositionListener(this); } + if (mTouchCaretElement) { + mTouchCaretElement->RemoveEventListener(NS_LITERAL_STRING("touchstart"), + mDummyTouchListener, false); + } + + if (mSelectionCaretsStartElement) { + mSelectionCaretsStartElement->RemoveEventListener(NS_LITERAL_STRING("touchstart"), + mDummyTouchListener, false); + } + + if (mSelectionCaretsEndElement) { + mSelectionCaretsEndElement->RemoveEventListener(NS_LITERAL_STRING("touchstart"), + mDummyTouchListener, false); + } + nsContentUtils::DestroyAnonymousContent(&mTouchCaretElement); nsContentUtils::DestroyAnonymousContent(&mSelectionCaretsStartElement); nsContentUtils::DestroyAnonymousContent(&mSelectionCaretsEndElement); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsCanvasFrame.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsCanvasFrame.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsCanvasFrame.h 2015-01-25 22:24:38.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsCanvasFrame.h 2015-02-03 14:33:50.000000000 +0000 @@ -14,6 +14,7 @@ #include "nsIScrollPositionListener.h" #include "nsDisplayList.h" #include "nsIAnonymousContentCreator.h" +#include "nsIDOMEventListener.h" class nsPresContext; class nsRenderingContext; @@ -168,6 +169,24 @@ nsCOMPtr mSelectionCaretsStartElement; nsCOMPtr mSelectionCaretsEndElement; nsCOMPtr mCustomContentContainer; + + class DummyTouchListener MOZ_FINAL : public nsIDOMEventListener + { + public: + NS_DECL_ISUPPORTS + + NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) MOZ_OVERRIDE + { + return NS_OK; + } + private: + ~DummyTouchListener() {} + }; + + /** + * A no-op touch-listener used for APZ purposes. + */ + nsRefPtr mDummyTouchListener; }; /** diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsFirstLetterFrame.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsFirstLetterFrame.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsFirstLetterFrame.cpp 2015-01-25 22:24:38.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsFirstLetterFrame.cpp 2015-02-03 14:33:50.000000000 +0000 @@ -226,6 +226,15 @@ aMetrics.SetSize(wm, convertedSize); aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() + bp.BStart(wm)); + + // Ensure that the overflow rect contains the child textframe's + // overflow rect. + // Note that if this is floating, the overline/underline drawable + // area is in the overflow rect of the child textframe. + aMetrics.UnionOverflowAreasWithDesiredBounds(); + ConsiderChildOverflow(aMetrics.mOverflowAreas, kid); + + FinishAndStoreOverflow(&aMetrics); } else { // Pretend we are a span and reflow the child frame @@ -247,12 +256,6 @@ nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics, bp, lineWM, wm); } - // Ensure that the overflow rect contains the child textframe's overflow rect. - // Note that if this is floating, the overline/underline drawable area is in - // the overflow rect of the child textframe. - aMetrics.UnionOverflowAreasWithDesiredBounds(); - ConsiderChildOverflow(aMetrics.mOverflowAreas, kid); - if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) { // Create a continuation or remove existing continuations based on // the reflow completion status. @@ -287,8 +290,6 @@ } } - FinishAndStoreOverflow(&aMetrics); - NS_FRAME_SET_TRUNCATION(aReflowStatus, aReflowState, aMetrics); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsFlexContainerFrame.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsFlexContainerFrame.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsFlexContainerFrame.cpp 2015-01-25 22:24:38.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsFlexContainerFrame.cpp 2015-02-03 14:33:50.000000000 +0000 @@ -3885,7 +3885,11 @@ "We gave flex item unconstrained available height, so it " "should be complete"); - childReflowState.ApplyRelativePositioning(&aFramePos, aContainerWidth); + LogicalMargin offsets = + childReflowState.ComputedLogicalOffsets().ConvertTo(outerWM, wm); + nsHTMLReflowState::ApplyRelativePositioning(aItem.Frame(), outerWM, + offsets, &aFramePos, + aContainerWidth); FinishReflowChild(aItem.Frame(), aPresContext, childDesiredSize, &childReflowState, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsFrame.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsFrame.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsFrame.cpp 2015-01-25 22:24:38.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsFrame.cpp 2015-02-03 14:33:50.000000000 +0000 @@ -1952,10 +1952,13 @@ // we're painting, and we're not animating opacity. Don't do this // if we're going to compute plugin geometry, since opacity-0 plugins // need to have display items built for them. + bool needEventRegions = aBuilder->IsBuildingLayerEventRegions() && + StyleVisibility()->GetEffectivePointerEvents(this) != NS_STYLE_POINTER_EVENTS_NONE; if (disp->mOpacity == 0.0 && aBuilder->IsForPainting() && !aBuilder->WillComputePluginGeometry() && !(disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) && - !nsLayoutUtils::HasAnimations(mContent, eCSSProperty_opacity)) { + !nsLayoutUtils::HasAnimations(mContent, eCSSProperty_opacity) && + !needEventRegions) { return; } @@ -2058,6 +2061,7 @@ aBuilder->SetLayerEventRegions(eventRegions); set.BorderBackground()->AppendNewToTop(eventRegions); } + aBuilder->AdjustWindowDraggingRegion(this); BuildDisplayList(aBuilder, dirtyRect, set); } @@ -2453,6 +2457,7 @@ if (eventRegions) { eventRegions->AddFrame(aBuilder, child); } + aBuilder->AdjustWindowDraggingRegion(child); child->BuildDisplayList(aBuilder, dirty, aLists); aBuilder->DisplayCaret(child, dirty, aLists.Content()); #ifdef DEBUG @@ -2472,6 +2477,7 @@ aBuilder->SetLayerEventRegions(eventRegions); pseudoStack.BorderBackground()->AppendNewToTop(eventRegions); } + aBuilder->AdjustWindowDraggingRegion(child); child->BuildDisplayList(aBuilder, dirty, pseudoStack); aBuilder->DisplayCaret(child, dirty, pseudoStack.Content()); @@ -3110,6 +3116,7 @@ aJumpLines, true, //limit on scrolled views false, + false, false); rv = PeekOffset(&pos); if (NS_SUCCEEDED(rv)) { @@ -3126,6 +3133,7 @@ aJumpLines, true, //limit on scrolled views false, + false, false); rv = baseFrame->PeekOffset(&startpos); if (NS_FAILED(rv)) @@ -3138,6 +3146,7 @@ aJumpLines, true, //limit on scrolled views false, + false, false); rv = PeekOffset(&endpos); if (NS_FAILED(rv)) @@ -6502,10 +6511,12 @@ movedOverNonSelectableText |= (peekSearchState == CONTINUE_UNSELECTABLE); if (peekSearchState != FOUND) { + bool movedOverNonSelectable = false; result = current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual, aPos->mJumpLines, aPos->mScrollViewStop, - ¤t, &offset, &jumpedLine); + ¤t, &offset, &jumpedLine, + &movedOverNonSelectable); if (NS_FAILED(result)) return result; @@ -6513,11 +6524,18 @@ // to eat non-renderable content on the new line. if (jumpedLine) eatingNonRenderableWS = true; + + // Remember if we moved over non-selectable text when finding another frame. + if (movedOverNonSelectable) { + movedOverNonSelectableText = true; + } } // Found frame, but because we moved over non selectable text we want the offset - // to be at the frame edge. - if (peekSearchState == FOUND && movedOverNonSelectableText) + // to be at the frame edge. Note that if we are extending the selection, this + // doesn't matter. + if (peekSearchState == FOUND && movedOverNonSelectableText && + !aPos->mExtend) { int32_t start, end; current->GetOffsets(start, end); @@ -6595,11 +6613,12 @@ if (!done) { nsIFrame* nextFrame; int32_t nextFrameOffset; - bool jumpedLine; + bool jumpedLine, movedOverNonSelectableText; result = current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual, aPos->mJumpLines, aPos->mScrollViewStop, - &nextFrame, &nextFrameOffset, &jumpedLine); + &nextFrame, &nextFrameOffset, &jumpedLine, + &movedOverNonSelectableText); // We can't jump lines if we're looking for whitespace following // non-whitespace, and we already encountered non-whitespace. if (NS_FAILED(result) || @@ -6947,8 +6966,9 @@ nsresult nsIFrame::GetFrameFromDirection(nsDirection aDirection, bool aVisual, - bool aJumpLines, bool aScrollViewStop, - nsIFrame** aOutFrame, int32_t* aOutOffset, bool* aOutJumpedLine) + bool aJumpLines, bool aScrollViewStop, + nsIFrame** aOutFrame, int32_t* aOutOffset, + bool* aOutJumpedLine, bool* aOutMovedOverNonSelectableText) { nsresult result; @@ -6959,6 +6979,7 @@ *aOutFrame = nullptr; *aOutOffset = 0; *aOutJumpedLine = false; + *aOutMovedOverNonSelectableText = false; // Find the prev/next selectable frame bool selectable = false; @@ -7042,12 +7063,17 @@ traversedFrame = frameTraversal->CurrentItem(); - // Skip anonymous elements + // Skip anonymous elements, but watch out for generated content if (!traversedFrame || - traversedFrame->GetContent()->IsRootOfNativeAnonymousSubtree()) + (!traversedFrame->IsGeneratedContentFrame() && + traversedFrame->GetContent()->IsRootOfNativeAnonymousSubtree())) { return NS_ERROR_FAILURE; + } traversedFrame->IsSelectable(&selectable, nullptr); + if (!selectable) { + *aOutMovedOverNonSelectableText = true; + } } // while (!selectable) *aOutOffset = (aDirection == eDirNext) ? 0 : -1; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsFrameSelection.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsFrameSelection.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsFrameSelection.h 2015-01-25 22:24:39.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsFrameSelection.h 2015-02-03 14:33:50.000000000 +0000 @@ -68,6 +68,7 @@ bool aScrollViewStop, bool aIsKeyboardSelect, bool aVisual, + bool aExtend, mozilla::EWordMovementType aWordMovementType = mozilla::eDefaultBehavior); // Note: Most arguments (input and output) are only used with certain values @@ -123,6 +124,9 @@ // Used with: eSelectCharacter, eSelectWord, eSelectBeginLine, eSelectEndLine. bool mVisual; + // mExtend: Whether the selection is being extended or moved. + bool mExtend; + /*** Output arguments ***/ // mResultContent: Content reached as a result of the peek. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsGfxScrollFrame.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsGfxScrollFrame.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsGfxScrollFrame.cpp 2015-01-25 22:24:39.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsGfxScrollFrame.cpp 2015-02-03 14:33:50.000000000 +0000 @@ -3179,7 +3179,7 @@ *aOutput->AppendElement() = nsDisplayScrollLayer::ComputeFrameMetrics(mScrolledFrame, mOuter, aContainerReferenceFrame, aLayer, mScrollParentID, - scrollport, false, isRoot, aParameters); + scrollport, isRoot, aParameters); } bool diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsIFrame.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsIFrame.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsIFrame.h 2015-01-25 22:24:39.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsIFrame.h 2015-02-03 14:33:50.000000000 +0000 @@ -28,7 +28,6 @@ #include "nsFrameList.h" #include "mozilla/layout/FrameChildList.h" #include "FramePropertyTable.h" -#include "mozilla/TypedEnum.h" #include "nsDirection.h" #include "WritingModes.h" #include @@ -316,10 +315,10 @@ /** * DidReflow status values. */ -MOZ_BEGIN_ENUM_CLASS(nsDidReflowStatus, uint32_t) +enum class nsDidReflowStatus : uint32_t { NOT_FINISHED, FINISHED -MOZ_END_ENUM_CLASS(nsDidReflowStatus) +}; /** * When there is no scrollable overflow rect, the visual overflow rect @@ -2451,10 +2450,13 @@ * @param aOutOffset [out] 0 indicates that we arrived at the beginning of the output frame; * -1 indicates that we arrived at its end. * @param aOutJumpedLine [out] whether this frame and the returned frame are on different lines + * @param aOutMovedOverNonSelectableText [out] whether we jumped over a non-selectable + * frame during the search */ nsresult GetFrameFromDirection(nsDirection aDirection, bool aVisual, - bool aJumpLines, bool aScrollViewStop, - nsIFrame** aOutFrame, int32_t* aOutOffset, bool* aOutJumpedLine); + bool aJumpLines, bool aScrollViewStop, + nsIFrame** aOutFrame, int32_t* aOutOffset, + bool* aOutJumpedLine, bool* aOutMovedOverNonSelectableText); /** * called to see if the children of the frame are visible from indexstart to index end. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsPluginFrame.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsPluginFrame.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsPluginFrame.cpp 2015-01-25 22:24:39.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsPluginFrame.cpp 2015-02-03 14:33:50.000000000 +0000 @@ -87,6 +87,8 @@ #include "GLContext.h" #endif +#include "mozilla/dom/TabChild.h" + #ifdef CreateEvent // Thank you MS. #undef CreateEvent #endif @@ -395,6 +397,34 @@ } void +nsPluginFrame::GetWidgetConfiguration(nsTArray* aConfigurations) +{ + if (!mWidget) { + return; + } + + if (!mWidget->GetParent()) { + // Plugin widgets should not be toplevel except when they're out of the + // document, in which case the plugin should not be registered for + // geometry updates and this should not be called. But apparently we + // have bugs where mWidget sometimes is toplevel here. Bail out. + NS_ERROR("Plugin widgets registered for geometry updates should not be toplevel"); + return; + } + + nsIWidget::Configuration* configuration = aConfigurations->AppendElement(); + configuration->mChild = mWidget; + configuration->mBounds = mNextConfigurationBounds; + configuration->mClipRegion = mNextConfigurationClipRegion; +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + if (XRE_GetProcessType() == GeckoProcessType_Content) { + configuration->mWindowID = (uintptr_t)mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT); + configuration->mVisible = mWidget->IsVisible(); + } +#endif // defined(XP_WIN) || defined(MOZ_WIDGET_GTK) +} + +void nsPluginFrame::GetDesiredSize(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aMetrics) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsPluginFrame.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsPluginFrame.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsPluginFrame.h 2015-01-25 22:24:39.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsPluginFrame.h 2015-02-03 14:33:50.000000000 +0000 @@ -121,23 +121,7 @@ /** * Append the desired widget configuration to aConfigurations. */ - void GetWidgetConfiguration(nsTArray* aConfigurations) - { - if (mWidget) { - if (!mWidget->GetParent()) { - // Plugin widgets should not be toplevel except when they're out of the - // document, in which case the plugin should not be registered for - // geometry updates and this should not be called. But apparently we - // have bugs where mWidget sometimes is toplevel here. Bail out. - NS_ERROR("Plugin widgets registered for geometry updates should not be toplevel"); - return; - } - nsIWidget::Configuration* configuration = aConfigurations->AppendElement(); - configuration->mChild = mWidget; - configuration->mBounds = mNextConfigurationBounds; - configuration->mClipRegion = mNextConfigurationClipRegion; - } - } + void GetWidgetConfiguration(nsTArray* aConfigurations); nsIntRect GetWidgetlessClipRect() { return RegionFromArray(mNextConfigurationClipRegion).GetBounds(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsSelection.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsSelection.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsSelection.cpp 2015-01-25 22:24:39.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsSelection.cpp 2015-02-03 14:33:51.000000000 +0000 @@ -112,6 +112,7 @@ bool aScrollViewStop, bool aIsKeyboardSelect, bool aVisual, + bool aExtend, EWordMovementType aWordMovementType) : mAmount(aAmount) , mDirection(aDirection) @@ -122,6 +123,7 @@ , mScrollViewStop(aScrollViewStop) , mIsKeyboardSelect(aIsKeyboardSelect) , mVisual(aVisual) + , mExtend(aExtend) , mResultContent() , mResultFrame(nullptr) , mContentOffset(0) @@ -852,7 +854,8 @@ //set data using mLimiter to stop on scroll views. If we have a limiter then we stop peeking //when we hit scrollable views. If no limiter then just let it go ahead nsPeekOffsetStruct pos(aAmount, eDirPrevious, offsetused, desiredPos, - true, mLimiter != nullptr, true, visualMovement); + true, mLimiter != nullptr, true, visualMovement, + aContinueSelection); nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(frame); @@ -1139,10 +1142,11 @@ nsIFrame *newFrame; int32_t offset; - bool jumpedLine; + bool jumpedLine, movedOverNonSelectableText; nsresult rv = currentFrame->GetFrameFromDirection(direction, false, aJumpLines, true, - &newFrame, &offset, &jumpedLine); + &newFrame, &offset, &jumpedLine, + &movedOverNonSelectableText); if (NS_FAILED(rv)) newFrame = nullptr; @@ -1444,7 +1448,7 @@ // first move one character forward. nsPeekOffsetStruct charPos(eSelectCharacter, eDirNext, offset, nsPoint(0, 0), false, mLimiter != nullptr, - false, false); + false, false, false); if (NS_SUCCEEDED(frame->PeekOffset(&charPos))) { frame = charPos.mResultFrame; offset = charPos.mContentOffset; @@ -1452,7 +1456,7 @@ } nsPeekOffsetStruct pos(amount, direction, offset, nsPoint(0, 0), - false, mLimiter != nullptr, false, false); + false, mLimiter != nullptr, false, false, false); if (frame && NS_SUCCEEDED(frame->PeekOffset(&pos)) && pos.mResultContent) { offsets.content = pos.mResultContent; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsTextFrame.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsTextFrame.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsTextFrame.cpp 2015-01-25 22:24:39.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsTextFrame.cpp 2015-02-03 14:33:51.000000000 +0000 @@ -8,8 +8,10 @@ #include "nsTextFrame.h" #include "gfx2DGlue.h" +#include "gfxUtils.h" #include "mozilla/Attributes.h" #include "mozilla/DebugOnly.h" +#include "mozilla/gfx/2D.h" #include "mozilla/Likely.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/TextEvents.h" @@ -136,6 +138,21 @@ } }; +/** + * Helper that is useful to help port the code is this file to typed rects. + * The code here is particularly horrible because it uses gfxRect to + * store app unit values (because we want fractional app unit values), but + * virtually everywhere else gfxRect is in device pixels. :-/ + */ +LayoutDeviceRect AppUnitGfxRectToDevRect(gfxRect aRect, + int32_t aAppUnitsPerDevPixel) +{ + return LayoutDeviceRect(aRect.x / aAppUnitsPerDevPixel, + aRect.y / aAppUnitsPerDevPixel, + aRect.width / aAppUnitsPerDevPixel, + aRect.height / aAppUnitsPerDevPixel); +} + } // namespace void @@ -4727,28 +4744,20 @@ } static void -PaintSelectionBackground(gfxContext* aCtx, nsPresContext* aPresContext, - nscolor aColor, const gfxRect& aDirtyRect, - const gfxRect& aRect, +PaintSelectionBackground(DrawTarget& aDrawTarget, + nscolor aColor, + const LayoutDeviceRect& aDirtyRect, + const LayoutDeviceRect& aRect, nsTextFrame::DrawPathCallbacks* aCallbacks) { - if (aCallbacks) { - aCallbacks->NotifyBeforeSelectionBackground(aColor); - } - - gfxRect r = aRect.Intersect(aDirtyRect); - // For now, we need to put this in pixel coordinates - int32_t app = aPresContext->AppUnitsPerDevPixel(); - aCtx->NewPath(); - // pixel-snap - aCtx->Rectangle(gfxRect(r.X() / app, r.Y() / app, - r.Width() / app, r.Height() / app), true); + Rect rect = aRect.Intersect(aDirtyRect).ToUnknownRect(); + MaybeSnapToDevicePixels(rect, aDrawTarget); if (aCallbacks) { - aCallbacks->NotifySelectionBackgroundPathEmitted(); + aCallbacks->NotifySelectionBackgroundNeedsFill(rect, aColor, aDrawTarget); } else { - aCtx->SetColor(gfxRGBA(aColor)); - aCtx->Fill(); + ColorPattern color(ToDeviceColor(aColor)); + aDrawTarget.FillRect(rect, color); } } @@ -5534,7 +5543,8 @@ const gfxPoint& aFramePt, const gfxPoint& aTextBaselinePt, gfxContext* aCtx, const nscolor& aForegroundColor, const nsCharClipDisplayItem::ClipEdges& aClipEdges, - nscoord aLeftSideOffset, gfxRect& aBoundingBox) + nscoord aLeftSideOffset, gfxRect& aBoundingBox, + uint32_t aBlurFlags) { PROFILER_LABEL("nsTextFrame", "PaintOneShadow", js::ProfileEntry::Category::GRAPHICS); @@ -5555,7 +5565,8 @@ nsContextBoxBlur contextBoxBlur; gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, 0, blurRadius, PresContext()->AppUnitsPerDevPixel(), - aCtx, aDirtyRect, nullptr); + aCtx, aDirtyRect, nullptr, + aBlurFlags); if (!shadowContext) return; @@ -5659,6 +5670,8 @@ TextRangeStyle rangeStyle; // Draw background colors if (anyBackgrounds) { + int32_t appUnitsPerDevPixel = aTextPaintStyle.PresContext()->AppUnitsPerDevPixel(); + LayoutDeviceRect dirtyRect = AppUnitGfxRectToDevRect(aDirtyRect, appUnitsPerDevPixel); SelectionIterator iterator(prevailingSelections, aContentOffset, aContentLength, aProvider, mTextRun, startIOffset); while (iterator.GetNextSegment(&iOffset, &offset, &length, &hyphenWidth, @@ -5679,9 +5692,9 @@ bgRect = gfxRect(aFramePt.x + offs, aFramePt.y, advance, GetSize().height); } - PaintSelectionBackground(aCtx, aTextPaintStyle.PresContext(), - background, aDirtyRect, - bgRect, aCallbacks); + PaintSelectionBackground(*aCtx->GetDrawTarget(), background, dirtyRect, + AppUnitGfxRectToDevRect(bgRect, appUnitsPerDevPixel), + aCallbacks); } iterator.UpdateWithAdvance(advance); } @@ -6065,13 +6078,28 @@ shadowMetrics.mAdvanceWidth, shadowMetrics.mAscent + shadowMetrics.mDescent); shadowMetrics.mBoundingBox.UnionRect(shadowMetrics.mBoundingBox, decorationRect); + + // If the textrun uses any color or SVG fonts, we need to force use of a mask + // for shadow rendering even if blur radius is zero. + uint32_t blurFlags = 0; + uint32_t numGlyphRuns; + const gfxTextRun::GlyphRun* run = mTextRun->GetGlyphRuns(&numGlyphRuns); + while (numGlyphRuns-- > 0) { + if (run->mFont->AlwaysNeedsMaskForShadow()) { + blurFlags = nsContextBoxBlur::FORCE_MASK; + break; + } + run++; + } + for (uint32_t i = aShadow->Length(); i > 0; --i) { PaintOneShadow(aOffset, aLength, aShadow->ShadowAt(i - 1), &aProvider, aDirtyRect, aFramePt, aTextBaselinePt, aCtx, aForegroundColor, aClipEdges, aLeftEdgeOffset, - shadowMetrics.mBoundingBox); + shadowMetrics.mBoundingBox, + blurFlags); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsTextFrame.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsTextFrame.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/generic/nsTextFrame.h 2015-01-25 22:24:39.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/generic/nsTextFrame.h 2015-02-03 14:33:51.000000000 +0000 @@ -7,6 +7,7 @@ #define nsTextFrame_h__ #include "mozilla/Attributes.h" +#include "mozilla/gfx/2D.h" #include "nsFrame.h" #include "nsSplittableFrame.h" #include "nsLineBox.h" @@ -37,6 +38,9 @@ }; class nsTextFrame : public nsTextFrameBase { + typedef mozilla::gfx::DrawTarget DrawTarget; + typedef mozilla::gfx::Rect Rect; + public: NS_DECL_QUERYFRAME_TARGET(nsTextFrame) NS_DECL_FRAMEARENA_HELPERS @@ -300,7 +304,7 @@ * * Callbacks are invoked in the following order: * - * (NotifyBeforeSelectionBackground NotifySelectionBackgroundPathEmitted)? + * (NotifySelectionBackgroundNeedsFill)? * (NotifyBeforeDecorationLine NotifyDecorationLinePathEmitted)* * NotifyBeforeText * (NotifyGlyphPathEmitted | @@ -326,6 +330,14 @@ } /** + * Called to have the selection highlight drawn before the text is drawn + * over the top. + */ + virtual void NotifySelectionBackgroundNeedsFill(const Rect& aBackgroundRect, + nscolor aColor, + DrawTarget& aDrawTarget) { } + + /** * Called just before any paths have been emitted to the gfxContext * for the glyphs of the frame's text. */ @@ -338,18 +350,6 @@ virtual void NotifyAfterText() { } /** - * Called just before a path corresponding to the selection background - * has been emitted to the gfxContext. - */ - virtual void NotifyBeforeSelectionBackground(nscolor aColor) { } - - /** - * Called just after a path corresponding to the selection background - * has been emitted to the gfxContext. - */ - virtual void NotifySelectionBackgroundPathEmitted() { } - - /** * Called just before a path corresponding to a text decoration line * has been emitted to the gfxContext. */ @@ -582,7 +582,8 @@ const nscolor& aForegroundColor, const nsCharClipDisplayItem::ClipEdges& aClipEdges, nscoord aLeftSideOffset, - gfxRect& aBoundingBox); + gfxRect& aBoundingBox, + uint32_t aBlurFlags); void PaintShadows(nsCSSShadowArray* aShadow, uint32_t aOffset, uint32_t aLength, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/inspector/inFlasher.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/inspector/inFlasher.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/inspector/inFlasher.cpp 2015-01-25 22:24:39.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/inspector/inFlasher.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,135 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "inFlasher.h" -#include "inLayoutUtils.h" - -#include "nsIDOMElement.h" -#include "nsIServiceManager.h" -#include "nsIPresShell.h" -#include "nsIFrame.h" -#include "nsIWidget.h" -#include "nsReadableUtils.h" -#include "nsIDOMWindow.h" -#include "nsIContent.h" - -#include "prprf.h" - -/////////////////////////////////////////////////////////////////////////////// - -inFlasher::inFlasher() : - mColor(NS_RGB(0,0,0)), - mThickness(0), - mInvert(false) -{ -} - -inFlasher::~inFlasher() -{ -} - -NS_IMPL_ISUPPORTS(inFlasher, inIFlasher) - -/////////////////////////////////////////////////////////////////////////////// -// inIFlasher - -NS_IMETHODIMP -inFlasher::GetColor(nsAString& aColor) -{ - // Copied from nsGenericHTMLElement::ColorToString() - char buf[10]; - PR_snprintf(buf, sizeof(buf), "#%02x%02x%02x", - NS_GET_R(mColor), NS_GET_G(mColor), NS_GET_B(mColor)); - CopyASCIItoUTF16(buf, aColor); - - return NS_OK; -} - -NS_IMETHODIMP -inFlasher::SetColor(const nsAString& aColor) -{ - NS_ENSURE_FALSE(aColor.IsEmpty(), NS_ERROR_ILLEGAL_VALUE); - - nsAutoString colorStr; - colorStr.Assign(aColor); - - if (colorStr.CharAt(0) != '#') { - if (NS_ColorNameToRGB(colorStr, &mColor)) { - return NS_OK; - } - } - else { - colorStr.Cut(0, 1); - if (NS_HexToRGB(colorStr, &mColor)) { - return NS_OK; - } - } - - return NS_ERROR_ILLEGAL_VALUE; -} - -NS_IMETHODIMP -inFlasher::GetThickness(uint16_t *aThickness) -{ - NS_PRECONDITION(aThickness, "Null pointer"); - *aThickness = mThickness; - return NS_OK; -} - -NS_IMETHODIMP -inFlasher::SetThickness(uint16_t aThickness) -{ - mThickness = aThickness; - return NS_OK; -} - -NS_IMETHODIMP -inFlasher::GetInvert(bool *aInvert) -{ - NS_PRECONDITION(aInvert, "Null pointer"); - *aInvert = mInvert; - return NS_OK; -} - -NS_IMETHODIMP -inFlasher::SetInvert(bool aInvert) -{ - mInvert = aInvert; - return NS_OK; -} - -NS_IMETHODIMP -inFlasher::RepaintElement(nsIDOMElement* aElement) -{ - return NS_OK; -} - -NS_IMETHODIMP -inFlasher::DrawElementOutline(nsIDOMElement* aElement) -{ - return NS_OK; -} - -NS_IMETHODIMP -inFlasher::ScrollElementIntoView(nsIDOMElement *aElement) -{ - NS_ENSURE_ARG_POINTER(aElement); - nsCOMPtr window = inLayoutUtils::GetWindowFor(aElement); - if (!window) { - return NS_OK; - } - - nsCOMPtr presShell = inLayoutUtils::GetPresShellFor(window); - if (!presShell) { - return NS_OK; - } - - nsCOMPtr content = do_QueryInterface(aElement); - presShell->ScrollContentIntoView(content, - nsIPresShell::ScrollAxis(), - nsIPresShell::ScrollAxis(), - nsIPresShell::SCROLL_OVERFLOW_HIDDEN); - - return NS_OK; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/inspector/inFlasher.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/inspector/inFlasher.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/inspector/inFlasher.h 2015-01-25 22:24:39.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/inspector/inFlasher.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef __inFlasher_h__ -#define __inFlasher_h__ - -#include "inIFlasher.h" -#include "nsCoord.h" -#include "nsColor.h" - -#define BOUND_INNER 0 -#define BOUND_OUTER 1 - -#define DIR_VERTICAL 0 -#define DIR_HORIZONTAL 1 - -class inFlasher MOZ_FINAL : public inIFlasher -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_INIFLASHER - - inFlasher(); - -protected: - virtual ~inFlasher(); - - nscolor mColor; - - uint16_t mThickness; - bool mInvert; -}; - -// {9286E71A-621A-4b91-851E-9984C1A2E81A} -#define IN_FLASHER_CID \ -{ 0x9286e71a, 0x621a, 0x4b91, { 0x85, 0x1e, 0x99, 0x84, 0xc1, 0xa2, 0xe8, 0x1a } } - -#endif // __inFlasher_h__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/inspector/inIFlasher.idl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/inspector/inIFlasher.idl --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/inspector/inIFlasher.idl 2015-01-25 22:24:39.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/inspector/inIFlasher.idl 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -interface nsIDOMElement; - -/** - * This class will be removed in gecko v33. See comments below for alternatives. - * - * @status DEPRECATED - see comments below. - */ -[scriptable, uuid(7B4A099F-6F6E-4565-977B-FB622ADBFF49)] -interface inIFlasher : nsISupports -{ - attribute DOMString color; - attribute boolean invert; - attribute unsigned short thickness; - - /** - * This function now does nothing at all. Use the :-moz-devtools-highlighted - * pseudo-class instead. For example, see the "HIGHLIGHTED_PSEUDO_CLASS" and - * "INVERT" lines in: - * https://hg.mozilla.org/dom-inspector/file/tip/resources/content/Flasher.js - * - * @status DEPRECATED - */ - void drawElementOutline(in nsIDOMElement aElement); - - /** - * This function now does nothing at all. - * - * @status DEPRECATED - */ - void repaintElement(in nsIDOMElement aElement); - - /** - * As of gecko v33 you should use inIDOMUtils::scrollElementIntoView instead - * of this function. - * - * @status DEPRECATED - */ - void scrollElementIntoView(in nsIDOMElement aElement); -}; - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/inspector/inLayoutUtils.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/inspector/inLayoutUtils.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/inspector/inLayoutUtils.cpp 2015-01-25 22:24:39.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/inspector/inLayoutUtils.cpp 2015-02-03 14:33:51.000000000 +0000 @@ -20,38 +20,6 @@ /////////////////////////////////////////////////////////////////////////////// -nsIDOMWindow* -inLayoutUtils::GetWindowFor(nsIDOMNode* aNode) -{ - nsCOMPtr doc1; - aNode->GetOwnerDocument(getter_AddRefs(doc1)); - return GetWindowFor(doc1.get()); -} - -nsIDOMWindow* -inLayoutUtils::GetWindowFor(nsIDOMDocument* aDoc) -{ - nsCOMPtr window; - aDoc->GetDefaultView(getter_AddRefs(window)); - return window; -} - -nsIPresShell* -inLayoutUtils::GetPresShellFor(nsISupports* aThing) -{ - nsCOMPtr window = do_QueryInterface(aThing); - - return window->GetDocShell()->GetPresShell(); -} - -/*static*/ -nsIFrame* -inLayoutUtils::GetFrameFor(nsIDOMElement* aElement) -{ - nsCOMPtr content = do_QueryInterface(aElement); - return content->GetPrimaryFrame(); -} - EventStateManager* inLayoutUtils::GetEventStateManagerFor(nsIDOMElement *aElement) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/inspector/inLayoutUtils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/inspector/inLayoutUtils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/inspector/inLayoutUtils.h 2015-01-25 22:24:39.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/inspector/inLayoutUtils.h 2015-02-03 14:33:51.000000000 +0000 @@ -22,10 +22,6 @@ class inLayoutUtils { public: - static nsIDOMWindow* GetWindowFor(nsIDOMNode* aNode); - static nsIDOMWindow* GetWindowFor(nsIDOMDocument* aDoc); - static nsIPresShell* GetPresShellFor(nsISupports* aThing); - static nsIFrame* GetFrameFor(nsIDOMElement* aElement); static mozilla::EventStateManager* GetEventStateManagerFor(nsIDOMElement *aElement); static nsIDOMDocument* GetSubDocumentFor(nsIDOMNode* aNode); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/inspector/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/inspector/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/inspector/moz.build 2015-01-25 22:24:39.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/inspector/moz.build 2015-02-03 14:33:51.000000000 +0000 @@ -13,7 +13,6 @@ 'inIDeepTreeWalker.idl', 'inIDOMUtils.idl', 'inIDOMView.idl', - 'inIFlasher.idl', 'inISearchObserver.idl', 'inISearchProcess.idl', 'nsIDOMFontFace.idl', @@ -31,7 +30,6 @@ 'inCSSValueSearch.cpp', 'inDeepTreeWalker.cpp', 'inDOMUtils.cpp', - 'inFlasher.cpp', 'inLayoutUtils.cpp', 'inSearchLoop.cpp', 'nsFontFace.cpp', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/ipc/PVsync.ipdl thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/ipc/PVsync.ipdl --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/ipc/PVsync.ipdl 2015-01-25 22:24:39.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/ipc/PVsync.ipdl 2015-02-03 14:33:52.000000000 +0000 @@ -21,7 +21,7 @@ child: // Send vsync event from chrome to content process. - async Notify(TimeStamp aVsyncTimestamp); + async Notify(TimeStamp aVsyncTimestamp) compress; parent: // Content process use these messages to acquire the vsync event. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/ipc/VsyncChild.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/ipc/VsyncChild.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/ipc/VsyncChild.cpp 2015-01-25 22:24:39.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/ipc/VsyncChild.cpp 2015-02-03 14:33:52.000000000 +0000 @@ -13,6 +13,7 @@ VsyncChild::VsyncChild() : mObservingVsync(false) + , mIsShutdown(false) { MOZ_ASSERT(NS_IsMainThread()); } @@ -26,9 +27,9 @@ VsyncChild::SendObserve() { MOZ_ASSERT(NS_IsMainThread()); - if (!mObservingVsync) { - PVsyncChild::SendObserve(); + if (!mObservingVsync && !mIsShutdown) { mObservingVsync = true; + PVsyncChild::SendObserve(); } return true; } @@ -37,9 +38,9 @@ VsyncChild::SendUnobserve() { MOZ_ASSERT(NS_IsMainThread()); - if (mObservingVsync) { - PVsyncChild::SendUnobserve(); + if (mObservingVsync && !mIsShutdown) { mObservingVsync = false; + PVsyncChild::SendUnobserve(); } return true; } @@ -48,6 +49,8 @@ VsyncChild::ActorDestroy(ActorDestroyReason aActorDestroyReason) { MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mIsShutdown); + mIsShutdown = true; mObserver = nullptr; } @@ -55,6 +58,7 @@ VsyncChild::RecvNotify(const TimeStamp& aVsyncTimestamp) { MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mIsShutdown); if (mObservingVsync && mObserver) { mObserver->NotifyVsync(aVsyncTimestamp); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/ipc/VsyncChild.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/ipc/VsyncChild.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/ipc/VsyncChild.h 2015-01-25 22:24:39.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/ipc/VsyncChild.h 2015-02-03 14:33:52.000000000 +0000 @@ -7,6 +7,7 @@ #define mozilla_layout_ipc_VsyncChild_h #include "mozilla/layout/PVsyncChild.h" +#include "nsISupportsImpl.h" #include "nsRefPtr.h" namespace mozilla { @@ -25,6 +26,8 @@ // PVsyncParent actor dies. class VsyncChild MOZ_FINAL : public PVsyncChild { + NS_INLINE_DECL_REFCOUNTING(VsyncChild) + friend class mozilla::ipc::BackgroundChildImpl; public: @@ -44,6 +47,7 @@ virtual void ActorDestroy(ActorDestroyReason aActorDestroyReason) MOZ_OVERRIDE; bool mObservingVsync; + bool mIsShutdown; // The content side vsync observer. nsRefPtr mObserver; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1120431-1.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1120431-1.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1120431-1.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1120431-1.html 2015-02-03 14:33:54.000000000 +0000 @@ -0,0 +1,205 @@ + + + + + + + +
    + +
    +
      +
    • + A +
    • +
    • + B +
    • +
    • + C +
    • +
    • + D +
    • +
    • + E +
    • +
    +
      +
    • + F +
    • +
    • + G +
    • +
    • + H +
    • +
    • + I +
    • +
    • + J +
    • +
    +
      +
    • + K +
    • +
    • + L +
    • +
    • + M +
    • +
    • + N +
    • +
    • + O +
    • +
    +
      +
    • + P +
    • +
    • + R +
    • +
    • + S +
    • +
    • + T +
    • +
    • + V +
    • +
    +
      +
    • + W +
    • +
    • + Y +
    • +
    • + Z +
    • +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    • + Word +
    • +
    • + Word +
    • +
    • + Word +
    • +
    • + Word +
    • +
    +
    +
    + ­
    +
    + +
    +
    +
    +
    +
    + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1120431-1-ref.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1120431-1-ref.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1120431-1-ref.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1120431-1-ref.html 2015-02-03 14:33:54.000000000 +0000 @@ -0,0 +1,202 @@ + + + + + + + +
    + +
    +
      +
    • + A +
    • +
    • + B +
    • +
    • + C +
    • +
    • + D +
    • +
    • + E +
    • +
    +
      +
    • + F +
    • +
    • + G +
    • +
    • + H +
    • +
    • + I +
    • +
    • + J +
    • +
    +
      +
    • + K +
    • +
    • + L +
    • +
    • + M +
    • +
    • + N +
    • +
    • + O +
    • +
    +
      +
    • + P +
    • +
    • + R +
    • +
    • + S +
    • +
    • + T +
    • +
    • + V +
    • +
    +
      +
    • + W +
    • +
    • + Y +
    • +
    • + Z +
    • +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    • + Word +
    • +
    • + Word +
    • +
    • + Word +
    • +
    • + Word +
    • +
    +
    + +
    +
    +
    +
    +
    + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1120431-2.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1120431-2.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1120431-2.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1120431-2.html 2015-02-03 14:33:54.000000000 +0000 @@ -0,0 +1,207 @@ + + + + + + + +
    + +
    +
      +
    • + A +
    • +
    • + B +
    • +
    • + C +
    • +
    • + D +
    • +
    • + E +
    • +
    +
      +
    • + F +
    • +
    • + G +
    • +
    • + H +
    • +
    • + I +
    • +
    • + J +
    • +
    +
      +
    • + K +
    • +
    • + L +
    • +
    • + M +
    • +
    • + N +
    • +
    • + O +
    • +
    +
      +
    • + P +
    • +
    • + R +
    • +
    • + S +
    • +
    • + T +
    • +
    • + V +
    • +
    +
      +
    • + W +
    • +
    • + Y +
    • +
    • + Z +
    • +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    • + Word +
    • +
    • + Word +
    • +
    • + Word +
    • +
    • + Word +
    • +
    +
    +
    + ­
    +
    + +
    +
    +
    +
    +
    + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1120431-2-ref.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1120431-2-ref.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1120431-2-ref.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1120431-2-ref.html 2015-02-03 14:33:54.000000000 +0000 @@ -0,0 +1,204 @@ + + + + + + + +
    + +
    +
      +
    • + A +
    • +
    • + B +
    • +
    • + C +
    • +
    • + D +
    • +
    • + E +
    • +
    +
      +
    • + F +
    • +
    • + G +
    • +
    • + H +
    • +
    • + I +
    • +
    • + J +
    • +
    +
      +
    • + K +
    • +
    • + L +
    • +
    • + M +
    • +
    • + N +
    • +
    • + O +
    • +
    +
      +
    • + P +
    • +
    • + R +
    • +
    • + S +
    • +
    • + T +
    • +
    • + V +
    • +
    +
      +
    • + W +
    • +
    • + Y +
    • +
    • + Z +
    • +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    • + Word +
    • +
    • + Word +
    • +
    • + Word +
    • +
    • + Word +
    • +
    +
    + +
    +
    +
    +
    +
    + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1127107-1a-nowrap.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1127107-1a-nowrap.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1127107-1a-nowrap.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1127107-1a-nowrap.html 2015-02-03 14:33:54.000000000 +0000 @@ -0,0 +1,28 @@ + + + + + + +
    + Does this text wrap? Does this text wrap? Does this text wrap? + Does this text wrap? +
    + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1127107-1b-pre.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1127107-1b-pre.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1127107-1b-pre.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1127107-1b-pre.html 2015-02-03 14:33:54.000000000 +0000 @@ -0,0 +1,25 @@ + + + + + + +
    Does this text wrap? Does this text wrap? Does this text wrap? Does this text wrap?
    + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1127107-1-ref.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1127107-1-ref.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1127107-1-ref.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1127107-1-ref.html 2015-02-03 14:33:54.000000000 +0000 @@ -0,0 +1,21 @@ + + + + + + +
    + Does this text wrap? Does this text wrap? Does this text wrap? + Does this text wrap? +
    + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1127107-2-capitalize.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1127107-2-capitalize.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1127107-2-capitalize.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1127107-2-capitalize.html 2015-02-03 14:33:54.000000000 +0000 @@ -0,0 +1,25 @@ + + + + + + +
    + Does this text wrap? Does this text wrap? Does this text wrap? + Does this text wrap? +
    + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1127107-2-capitalize-ref.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1127107-2-capitalize-ref.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1127107-2-capitalize-ref.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1127107-2-capitalize-ref.html 2015-02-03 14:33:54.000000000 +0000 @@ -0,0 +1,21 @@ + + + + + + +
    + Does This Text Wrap? Does This Text Wrap? Does This Text Wrap? + Does This Text Wrap? +
    + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1127679-1a-inline-flex-relpos.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1127679-1a-inline-flex-relpos.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1127679-1a-inline-flex-relpos.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1127679-1a-inline-flex-relpos.html 2015-02-03 14:33:54.000000000 +0000 @@ -0,0 +1,22 @@ + + + + + + + +
    foobar + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1127679-1b-inline-flex-relpos.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1127679-1b-inline-flex-relpos.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/1127679-1b-inline-flex-relpos.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/1127679-1b-inline-flex-relpos.html 2015-02-03 14:33:54.000000000 +0000 @@ -0,0 +1,22 @@ + + + + + + + +
    foobar + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/reftest.list thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/reftest.list --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/bugs/reftest.list 2015-01-25 22:24:21.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/bugs/reftest.list 2015-02-03 14:33:10.000000000 +0000 @@ -916,7 +916,7 @@ == 407016-1-b.html 407016-1-ref.html == 407078-1.html 407078-1-ref.html == 407095-1.html 407095-1-ref.html -== 407111-1.html 407111-1-ref.html +fuzzy-if(Android,13,9) == 407111-1.html 407111-1-ref.html # Bug 1128229 == 407227-1.html 407227-1-ref.html == 407243-1.html 407243-1-ref.html == 407419-1.html 407419-1-ref.html @@ -971,7 +971,7 @@ == 413286-5.html 413286-5-ref.html == 413286-6.html 413286-6-ref.html skip-if(cocoaWidget) == 413292-1.html 413292-1-ref.html # disabling due to failure loading on some mac tinderboxes. See bug 432954 -fuzzy-if(Android&&AndroidVersion>=15,11,15) == 413361-1.html 413361-1-ref.html +fuzzy-if(B2G||Android,11,15) == 413361-1.html 413361-1-ref.html # bug 1128229 == 413840-background-unchanged.html 413840-background-unchanged-ref.html == 413840-ltr-offsets.html 413840-ltr-offsets-ref.html == 413840-rtl-offsets.html 413840-rtl-offsets-ref.html @@ -1592,8 +1592,8 @@ == 589615-1b.html 589615-1-ref.html == 589672-1.html 589672-1-ref.html != 589682-1.html 589682-1-notref.html -skip-if(!browserIsRemote) == 593243-1.html 593243-1-ref.html # bug 593168 -skip-if(!browserIsRemote) == 593243-2.html 593243-2-ref.html # bug 593168 +skip-if(!asyncPanZoom) == 593243-1.html 593243-1-ref.html # bug 593168 +skip-if(!asyncPanZoom) == 593243-2.html 593243-2-ref.html # bug 593168 == 593544-1.html 593544-1-ref.html random-if(Android) == 594333-1.html 594333-1-ref.html fuzzy-if(B2G,1,40000) == 594624-1.html 594624-1-ref.html @@ -1763,7 +1763,7 @@ == 836844-1.html 836844-1-ref.html == 841192-1.html 841192-1-ref.html == 844178.html 844178-ref.html -== 846144-1.html 846144-1-ref.html +fuzzy-if(OSX,1,364) == 846144-1.html 846144-1-ref.html == 847850-1.html 847850-1-ref.html == 848421-1.html 848421-1-ref.html random-if(B2G) == 849407-1.html 849407-1-ref.html @@ -1848,7 +1848,7 @@ == 1069716-1.html 1069716-1-ref.html == 1078262-1.html about:blank test-pref(layout.testing.overlay-scrollbars.always-visible,false) == 1081072-1.html 1081072-1-ref.html -fuzzy-if(winWidget&&!layersGPUAccelerated,1,31) fuzzy-if(B2G,128,75) == 1081185-1.html 1081185-1-ref.html # fuzzy with event-regions, see bug 1107843 +fuzzy-if(B2G,128,75) == 1081185-1.html 1081185-1-ref.html # fuzzy with event-regions, see bug 1107843 == 1097437-1.html 1097437-1-ref.html == 1103258-1.html 1103258-1-ref.html # assertion crash test with layers culling test == 1105137-1.html 1105137-1-ref.html @@ -1856,3 +1856,9 @@ == 1111753-1.html about:blank == 1119117-1a.html 1119117-1-ref.html == 1119117-1b.html 1119117-1-ref.html +== 1120431-1.html 1120431-1-ref.html +== 1120431-2.html 1120431-2-ref.html +== 1127107-1a-nowrap.html 1127107-1-ref.html +== 1127107-1b-pre.html 1127107-1-ref.html +== 1127107-2-capitalize.html 1127107-2-capitalize-ref.html +== 1127679-1a-inline-flex-relpos.html 1127679-1b-inline-flex-relpos.html diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/css-ruby/dynamic-removal-3.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/css-ruby/dynamic-removal-3.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/css-ruby/dynamic-removal-3.html 2015-01-25 22:24:22.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/css-ruby/dynamic-removal-3.html 2015-02-03 14:33:12.000000000 +0000 @@ -30,7 +30,8 @@

    abxy

    -

    abxy

    + +

    abxy

    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/css-ruby/dynamic-removal-3-ref.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/css-ruby/dynamic-removal-3-ref.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/css-ruby/dynamic-removal-3-ref.html 2015-01-25 22:24:22.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/css-ruby/dynamic-removal-3-ref.html 2015-02-03 14:33:12.000000000 +0000 @@ -20,7 +20,7 @@

    'ab' should be paried with 'xy':

    abxy

    -

    abxy

    +

    abxy

    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/css-ruby/reftest.list thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/css-ruby/reftest.list --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/css-ruby/reftest.list 2015-01-25 22:24:22.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/css-ruby/reftest.list 2015-02-03 14:33:12.000000000 +0000 @@ -17,7 +17,7 @@ == dynamic-insertion-3.html dynamic-insertion-3-ref.html == dynamic-removal-1.html dynamic-removal-1-ref.html == dynamic-removal-2.html dynamic-removal-2-ref.html -fuzzy-if(winWidget,35,1) == dynamic-removal-3.html dynamic-removal-3-ref.html # bug 1111891 +== dynamic-removal-3.html dynamic-removal-3-ref.html == float-handling.html float-handling-ref.html == inlinize-blocks-1.html inlinize-blocks-1-ref.html == inlinize-blocks-2.html inlinize-blocks-2-ref.html diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-float-nooverflow.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-float-nooverflow.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-float-nooverflow.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-float-nooverflow.html 2015-02-03 14:33:12.000000000 +0000 @@ -0,0 +1,15 @@ +::first-letter and overflow + + +
    Hello world, testing, testing, testing, testing
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-float-nooverflow-ref.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-float-nooverflow-ref.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-float-nooverflow-ref.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-float-nooverflow-ref.html 2015-02-03 14:33:12.000000000 +0000 @@ -0,0 +1,14 @@ +::first-letter and overflow + + +
    Hello world, testing, testing, testing, testing
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-float-overflow.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-float-overflow.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-float-overflow.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-float-overflow.html 2015-02-03 14:33:12.000000000 +0000 @@ -0,0 +1,15 @@ +::first-letter and overflow + + +
    Hello
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-float-overflow-notref.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-float-overflow-notref.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-float-overflow-notref.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-float-overflow-notref.html 2015-02-03 14:33:12.000000000 +0000 @@ -0,0 +1,14 @@ +::first-letter and overflow + + +
    Hello
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-inline-nooverflow.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-inline-nooverflow.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-inline-nooverflow.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-inline-nooverflow.html 2015-02-03 14:33:12.000000000 +0000 @@ -0,0 +1,15 @@ +::first-letter and overflow + + +
    Hello world, testing, testing, testing, testing
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-inline-nooverflow-ref.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-inline-nooverflow-ref.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-inline-nooverflow-ref.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-inline-nooverflow-ref.html 2015-02-03 14:33:12.000000000 +0000 @@ -0,0 +1,14 @@ +::first-letter and overflow + + +
    Hello world, testing, testing, testing, testing
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-inline-overflow.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-inline-overflow.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-inline-overflow.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-inline-overflow.html 2015-02-03 14:33:12.000000000 +0000 @@ -0,0 +1,15 @@ +::first-letter and overflow + + +
    Hello
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-inline-overflow-notref.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-inline-overflow-notref.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-inline-overflow-notref.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-inline-overflow-notref.html 2015-02-03 14:33:12.000000000 +0000 @@ -0,0 +1,14 @@ +::first-letter and overflow + + +
    Hello
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-inline-overflow-ref.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-inline-overflow-ref.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/overflow-inline-overflow-ref.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/overflow-inline-overflow-ref.html 2015-02-03 14:33:12.000000000 +0000 @@ -0,0 +1,15 @@ +::first-letter and overflow + + +
    Hello
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/reftest.list thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/reftest.list --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/first-letter/reftest.list 2015-01-25 22:24:22.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/first-letter/reftest.list 2015-02-03 14:33:12.000000000 +0000 @@ -67,3 +67,8 @@ fails-if(gtk2Widget) random-if(winWidget&&!d2d) == font-text-styles-floater.html font-text-styles-floater-ref.html # bug 992846 == inline-height-empty.html inline-height-empty-ref.html HTTP(..) == indic-clusters-1.html indic-clusters-1-ref.html +== overflow-float-nooverflow.html overflow-float-nooverflow-ref.html +== overflow-float-overflow.html overflow-float-overflow-notref.html +== overflow-inline-nooverflow.html overflow-inline-nooverflow-ref.html +!= overflow-inline-overflow.html overflow-inline-overflow-notref.html +== overflow-inline-overflow.html overflow-inline-overflow-ref.html diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/image/image-object-position-with-background-1-ref.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/image/image-object-position-with-background-1-ref.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/image/image-object-position-with-background-1-ref.html 2015-01-25 22:24:24.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/image/image-object-position-with-background-1-ref.html 2015-02-03 14:33:16.000000000 +0000 @@ -7,21 +7,36 @@ - - - - +
    + +
    +
    + +
    +
    + +
    +
    + +
    diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/reftest-sanity/reftest.list thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/reftest-sanity/reftest.list --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/reftest-sanity/reftest.list 2015-01-25 22:24:25.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/reftest-sanity/reftest.list 2015-02-03 14:33:18.000000000 +0000 @@ -90,7 +90,6 @@ needs-focus == data:text/plain, about:blank # Sanity check of viewport+displayport overrides -random-if(!browserIsRemote) == test-displayport.html test-displayport-ref.html # bug 593168 skip-if(!browserIsRemote) != test-displayport-2.html test-displayport-ref.html # bug 593168 skip-if(!browserIsRemote) fails-if(OSX&&layersGPUAccelerated) fuzzy-if(layersOMTC,1,1390) random-if(Android&&AndroidVersion<15) random-if(B2G||B2GDT) == 647192-1.html 647192-1-ref.html skip-if(!browserIsRemote||(B2G&&browserIsRemote)) == 656041-1.html 656041-1-ref.html diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/reftest-sanity/test-displayport.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/reftest-sanity/test-displayport.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/reftest-sanity/test-displayport.html 2015-01-25 22:24:25.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/reftest-sanity/test-displayport.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ - - - -
    - - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/svg/mask-transformed-child-01-ref.svg thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/svg/mask-transformed-child-01-ref.svg --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/svg/mask-transformed-child-01-ref.svg 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/svg/mask-transformed-child-01-ref.svg 2015-02-03 14:33:20.000000000 +0000 @@ -0,0 +1,8 @@ + + + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/svg/mask-transformed-child-01.svg thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/svg/mask-transformed-child-01.svg --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/svg/mask-transformed-child-01.svg 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/svg/mask-transformed-child-01.svg 2015-02-03 14:33:20.000000000 +0000 @@ -0,0 +1,11 @@ + + + + + + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/svg/reftest.list thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/svg/reftest.list --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/svg/reftest.list 2015-01-25 22:24:26.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/svg/reftest.list 2015-02-03 14:33:20.000000000 +0000 @@ -209,6 +209,7 @@ == mask-containing-masked-content-01.svg pass.svg == mask-transformed-01.svg mask-transformed-01-ref.svg == mask-transformed-02.svg pass.svg +== mask-transformed-child-01.svg mask-transformed-child-01-ref.svg pref(layout.css.masking.enabled,true) fuzzy-if(d2d,1,6400) == mask-type-01.svg mask-type-01-ref.svg pref(layout.css.masking.enabled,true) fuzzy-if(d2d,1,6400) == mask-type-02.svg mask-type-01-ref.svg pref(layout.css.masking.enabled,true) fuzzy-if(d2d,1,6400) == mask-type-03.svg mask-type-01-ref.svg diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/text/fallback-mark-stacking-1.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/text/fallback-mark-stacking-1.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/text/fallback-mark-stacking-1.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/text/fallback-mark-stacking-1.html 2015-02-03 14:33:22.000000000 +0000 @@ -0,0 +1,23 @@ + + + + + + + +These examples should NOT look the same: +
    x̂̃ x̃̂
    + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/text/fallback-mark-stacking-1-notref.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/text/fallback-mark-stacking-1-notref.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/text/fallback-mark-stacking-1-notref.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/text/fallback-mark-stacking-1-notref.html 2015-02-03 14:33:22.000000000 +0000 @@ -0,0 +1,23 @@ + + + + + + + +These examples should NOT look the same: +
    x̃̂ x̂̃
    + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/text/reftest.list thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/text/reftest.list --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/text/reftest.list 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/text/reftest.list 2015-02-03 14:33:22.000000000 +0000 @@ -163,6 +163,8 @@ fails-if(cocoaWidget||Android||B2G) HTTP(..) == arabic-fallback-3.html arabic-fallback-3-ref.html fails-if(!cocoaWidget&&!Android&&!B2G) HTTP(..) != arabic-fallback-4.html arabic-fallback-4-notref.html == arabic-marks-1.html arabic-marks-1-ref.html +# harfbuzz fallback mark stacking in the absence of GPOS: +HTTP(..) != fallback-mark-stacking-1.html fallback-mark-stacking-1-notref.html == 726392-1.html 726392-1-ref.html == 726392-2.html 726392-2-ref.html diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/text-overflow/reftest.list thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/text-overflow/reftest.list --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/text-overflow/reftest.list 2015-01-25 22:24:27.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/text-overflow/reftest.list 2015-02-03 14:33:22.000000000 +0000 @@ -1,6 +1,6 @@ skip-if(B2G) == ellipsis-font-fallback.html ellipsis-font-fallback-ref.html == line-clipping.html line-clipping-ref.html -skip-if(B2G) HTTP(..) == marker-basic.html marker-basic-ref.html +fuzzy-if(Android,16,244) skip-if(B2G) HTTP(..) == marker-basic.html marker-basic-ref.html # Bug 1128229 skip-if(B2G) HTTP(..) == marker-string.html marker-string-ref.html skip-if(Android||B2G) HTTP(..) == bidi-simple.html bidi-simple-ref.html # Fails on Android due to anti-aliasing skip-if(!gtk2Widget) fuzzy-if(gtk2Widget,1,104) HTTP(..) == bidi-simple-scrolled.html bidi-simple-scrolled-ref.html # Fails on Windows and OSX due to anti-aliasing diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/w3c-css/submitted/ui3/reftest.list thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/w3c-css/submitted/ui3/reftest.list --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/reftests/w3c-css/submitted/ui3/reftest.list 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/reftests/w3c-css/submitted/ui3/reftest.list 2015-02-03 14:33:24.000000000 +0000 @@ -9,5 +9,5 @@ == box-sizing-padding-box-002.xht box-sizing-padding-box-002-ref.xht == box-sizing-padding-box-003.xht box-sizing-padding-box-003-ref.xht random-if(Android) skip-if(B2G&&browserIsRemote) == box-sizing-replaced-001.xht box-sizing-replaced-001-ref.xht #bug 982547 -random-if(B2G&&browserIsRemote) == box-sizing-replaced-002.xht box-sizing-replaced-002-ref.xht -random-if(B2G&&browserIsRemote) == box-sizing-replaced-003.xht box-sizing-replaced-003-ref.xht +fuzzy-if(Android,14,112) random-if(B2G&&browserIsRemote) == box-sizing-replaced-002.xht box-sizing-replaced-002-ref.xht # Bug 1128229 +fuzzy-if(Android,14,813) random-if(B2G&&browserIsRemote) == box-sizing-replaced-003.xht box-sizing-replaced-003-ref.xht # Bug 1128229 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/style/FontFaceSet.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/style/FontFaceSet.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/style/FontFaceSet.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/style/FontFaceSet.cpp 2015-02-03 14:33:24.000000000 +0000 @@ -410,16 +410,14 @@ return NS_ERROR_OUT_OF_MEMORY; #ifdef PR_LOGGING - if (PR_LOG_TEST(nsFontFaceLoader::GetFontDownloaderLog(), - PR_LOG_DEBUG)) { + if (LOG_ENABLED()) { nsAutoCString fontURI, referrerURI; aFontFaceSrc->mURI->GetSpec(fontURI); if (aFontFaceSrc->mReferrer) aFontFaceSrc->mReferrer->GetSpec(referrerURI); - PR_LOG(nsFontFaceLoader::GetFontDownloaderLog(), PR_LOG_DEBUG, - ("fontdownloader (%p) download start - font uri: (%s) " - "referrer uri: (%s)\n", - fontLoader.get(), fontURI.get(), referrerURI.get())); + LOG(("userfonts (%p) download start - font uri: (%s) " + "referrer uri: (%s)\n", + fontLoader.get(), fontURI.get(), referrerURI.get())); } #endif @@ -612,10 +610,12 @@ mUserFontSet->mLocalRulesUsed = false; #if PR_LOGGING - LOG(("userfonts (%p) userfont rules update (%s) rule count: %d", - mUserFontSet.get(), - (modified ? "modified" : "not modified"), - mRuleFaces.Length())); + if (LOG_ENABLED() && !mRuleFaces.IsEmpty()) { + LOG(("userfonts (%p) userfont rules update (%s) rule count: %d", + mUserFontSet.get(), + (modified ? "modified" : "not modified"), + mRuleFaces.Length())); + } #endif return modified; @@ -1073,9 +1073,8 @@ message.Append(fontURI); #ifdef PR_LOGGING - if (PR_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), PR_LOG_DEBUG)) { - PR_LOG(gfxUserFontSet::GetUserFontsLog(), PR_LOG_DEBUG, - ("userfonts (%p) %s", mUserFontSet.get(), message.get())); + if (LOG_ENABLED()) { + LOG(("userfonts (%p) %s", mUserFontSet.get(), message.get())); } #endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/style/nsCSSParser.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/style/nsCSSParser.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/style/nsCSSParser.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/style/nsCSSParser.cpp 2015-02-03 14:33:25.000000000 +0000 @@ -74,14 +74,14 @@ // End-of-array marker for mask arguments to ParseBitmaskValues #define MASK_END_VALUE (-1) -MOZ_BEGIN_ENUM_CLASS(CSSParseResult, int32_t) +enum class CSSParseResult : int32_t { // Parsed something successfully: Ok, // Did not find what we were looking for, but did not consume any token: NotFound, // Unexpected token or token value, too late for UngetToken() to be enough: Error -MOZ_END_ENUM_CLASS(CSSParseResult) +}; namespace { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/style/nsCSSRuleProcessor.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/style/nsCSSRuleProcessor.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/style/nsCSSRuleProcessor.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/style/nsCSSRuleProcessor.cpp 2015-02-03 14:33:25.000000000 +0000 @@ -53,7 +53,6 @@ #include "mozilla/Preferences.h" #include "mozilla/LookAndFeel.h" #include "mozilla/Likely.h" -#include "mozilla/TypedEnum.h" #include "mozilla/TypedEnumBits.h" using namespace mozilla; @@ -701,24 +700,24 @@ // universal rules within the namespace if (kNameSpaceID_Unknown != nameSpace && mNameSpaceTable.IsInitialized()) { RuleHashTableEntry *entry = static_cast - (PL_DHashTableLookup(&mNameSpaceTable, NS_INT32_TO_PTR(nameSpace))); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + (PL_DHashTableSearch(&mNameSpaceTable, NS_INT32_TO_PTR(nameSpace))); + if (entry) { mEnumList[valueCount++] = ToEnumData(entry->mRules); RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementNameSpaceCalls); } } if (mTagTable.IsInitialized()) { RuleHashTableEntry *entry = static_cast - (PL_DHashTableLookup(&mTagTable, tag)); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + (PL_DHashTableSearch(&mTagTable, tag)); + if (entry) { mEnumList[valueCount++] = ToEnumData(entry->mRules); RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementTagCalls); } } if (id && mIdTable.IsInitialized()) { RuleHashTableEntry *entry = static_cast - (PL_DHashTableLookup(&mIdTable, id)); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + (PL_DHashTableSearch(&mIdTable, id)); + if (entry) { mEnumList[valueCount++] = ToEnumData(entry->mRules); RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementIdCalls); } @@ -726,8 +725,8 @@ if (mClassTable.IsInitialized()) { for (int32_t index = 0; index < classCount; ++index) { RuleHashTableEntry *entry = static_cast - (PL_DHashTableLookup(&mClassTable, classList->AtomAt(index))); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + (PL_DHashTableSearch(&mClassTable, classList->AtomAt(index))); + if (entry) { mEnumList[valueCount++] = ToEnumData(entry->mRules); RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementClassCalls); } @@ -1407,7 +1406,7 @@ * Additional information about a selector (without combinators) that is * being matched. */ -MOZ_BEGIN_ENUM_CLASS(SelectorMatchesFlags, uint8_t) +enum class SelectorMatchesFlags : uint8_t { NONE = 0, // The selector's flags are unknown. This happens when you don't know @@ -1424,7 +1423,7 @@ // The selector is part of an argument to a functional pseudo-class or // pseudo-element. IS_PSEUDO_CLASS_ARGUMENT = 1 << 2 -MOZ_END_ENUM_CLASS(SelectorMatchesFlags) +}; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(SelectorMatchesFlags) static bool ValueIncludes(const nsSubstring& aValueList, @@ -2583,8 +2582,8 @@ if (cascade && cascade->mAnonBoxRules.EntryCount()) { RuleHashTagTableEntry* entry = static_cast - (PL_DHashTableLookup(&cascade->mAnonBoxRules, aData->mPseudoTag)); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + (PL_DHashTableSearch(&cascade->mAnonBoxRules, aData->mPseudoTag)); + if (entry) { nsTArray& rules = entry->mRules; for (RuleValue *value = rules.Elements(), *end = value + rules.Length(); value != end; ++value) { @@ -2603,8 +2602,8 @@ if (cascade && cascade->mXULTreeRules.EntryCount()) { RuleHashTagTableEntry* entry = static_cast - (PL_DHashTableLookup(&cascade->mXULTreeRules, aData->mPseudoTag)); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + (PL_DHashTableSearch(&cascade->mXULTreeRules, aData->mPseudoTag)); + if (entry) { NodeMatchContext nodeContext(EventStates(), nsCSSRuleProcessor::IsLink(aData->mElement)); nsTArray& rules = entry->mRules; @@ -2838,15 +2837,15 @@ if (id) { AtomSelectorEntry *entry = static_cast - (PL_DHashTableLookup(&cascade->mIdSelectors, id)); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + (PL_DHashTableSearch(&cascade->mIdSelectors, id)); + if (entry) { EnumerateSelectors(entry->mSelectors, &data); } } EnumerateSelectors(cascade->mPossiblyNegatedIDSelectors, &data); } - + if (aData->mAttribute == nsGkAtoms::_class) { const nsAttrValue* elementClasses = aData->mElement->GetClasses(); if (elementClasses) { @@ -2855,9 +2854,9 @@ nsIAtom* curClass = elementClasses->AtomAt(i); AtomSelectorEntry *entry = static_cast - (PL_DHashTableLookup(&cascade->mClassSelectors, + (PL_DHashTableSearch(&cascade->mClassSelectors, curClass)); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + if (entry) { EnumerateSelectors(entry->mSelectors, &data); } } @@ -2868,9 +2867,9 @@ AtomSelectorEntry *entry = static_cast - (PL_DHashTableLookup(&cascade->mAttributeSelectors, + (PL_DHashTableSearch(&cascade->mAttributeSelectors, aData->mAttribute)); - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + if (entry) { EnumerateSelectors(entry->mSelectors, &data); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/style/nsFontFaceLoader.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/style/nsFontFaceLoader.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/style/nsFontFaceLoader.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/style/nsFontFaceLoader.cpp 2015-02-03 14:33:25.000000000 +0000 @@ -25,20 +25,9 @@ using namespace mozilla; -#ifdef PR_LOGGING -PRLogModuleInfo* -nsFontFaceLoader::GetFontDownloaderLog() -{ - static PRLogModuleInfo* sLog; - if (!sLog) - sLog = PR_NewLogModule("fontdownloader"); - return sLog; -} -#endif /* PR_LOGGING */ - -#define LOG(args) PR_LOG(GetFontDownloaderLog(), PR_LOG_DEBUG, args) -#define LOG_ENABLED() PR_LOG_TEST(GetFontDownloaderLog(), PR_LOG_DEBUG) - +#define LOG(args) PR_LOG(gfxUserFontSet::GetUserFontsLog(), PR_LOG_DEBUG, args) +#define LOG_ENABLED() PR_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), \ + PR_LOG_DEBUG) nsFontFaceLoader::nsFontFaceLoader(gfxUserFontEntry* aUserFontEntry, nsIURI* aFontURI, @@ -119,7 +108,7 @@ delay >> 1, nsITimer::TYPE_ONE_SHOT); updateUserFontSet = false; - LOG(("fontdownloader (%p) 75%% done, resetting timer\n", loader)); + LOG(("userfonts (%p) 75%% done, resetting timer\n", loader)); } } @@ -133,7 +122,7 @@ if (ctx) { loader->mFontFaceSet->IncrementGeneration(); ctx->UserFontSetUpdated(); - LOG(("fontdownloader (%p) timeout reflow\n", loader)); + LOG(("userfonts (%p) timeout reflow\n", loader)); } } } @@ -159,10 +148,10 @@ nsAutoCString fontURI; mFontURI->GetSpec(fontURI); if (NS_SUCCEEDED(aStatus)) { - LOG(("fontdownloader (%p) download completed - font uri: (%s)\n", + LOG(("userfonts (%p) download completed - font uri: (%s)\n", this, fontURI.get())); } else { - LOG(("fontdownloader (%p) download failed - font uri: (%s) error: %8.8x\n", + LOG(("userfonts (%p) download failed - font uri: (%s) error: %8.8x\n", this, fontURI.get(), aStatus)); } } @@ -205,7 +194,7 @@ // Update layout for the presence of the new font. Since this is // asynchronous, reflows will coalesce. ctx->UserFontSetUpdated(); - LOG(("fontdownloader (%p) reflow\n", this)); + LOG(("userfonts (%p) reflow\n", this)); } // done with font set diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/style/nsFontFaceLoader.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/style/nsFontFaceLoader.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/style/nsFontFaceLoader.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/style/nsFontFaceLoader.h 2015-02-03 14:33:25.000000000 +0000 @@ -46,10 +46,6 @@ nsIURI* aTargetURI, nsISupports* aContext); -#ifdef PR_LOGGING - static PRLogModuleInfo* GetFontDownloaderLog(); -#endif - protected: virtual ~nsFontFaceLoader(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/style/StyleRule.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/style/StyleRule.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/style/StyleRule.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/style/StyleRule.cpp 2015-02-03 14:33:24.000000000 +0000 @@ -1480,18 +1480,32 @@ str.AppendLiteral(" "); } - nsAutoString buffer; if (mSelector) { + nsAutoString buffer; mSelector->ToString(buffer, GetStyleSheet()); AppendUTF16toUTF8(buffer, str); str.Append(' '); } if (nullptr != mDeclaration) { + nsAutoString buffer; str.AppendLiteral("{ "); mDeclaration->ToString(buffer); AppendUTF16toUTF8(buffer, str); str.Append('}'); + CSSStyleSheet* sheet = GetStyleSheet(); + if (sheet) { + nsIURI* uri = sheet->GetSheetURI(); + if (uri) { + nsAutoCString uristr; + str.Append(" /* "); + uri->GetSpec(uristr); + str.Append(uristr); + str.Append(':'); + str.AppendInt(mLineNumber); + str.Append(" */"); + } + } } else { str.AppendLiteral("{ null declaration }"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/style/test/mochitest.ini thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/style/test/mochitest.ini --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/style/test/mochitest.ini 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/style/test/mochitest.ini 2015-02-03 14:33:26.000000000 +0000 @@ -88,6 +88,7 @@ [test_bug534804.html] [test_bug573255.html] [test_bug580685.html] +[test_bug621351.html] [test_bug635286.html] [test_bug652486.html] [test_bug657143.html] diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/style/test/test_bug621351.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/style/test/test_bug621351.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/style/test/test_bug621351.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/style/test/test_bug621351.html 2015-02-03 14:33:26.000000000 +0000 @@ -0,0 +1,35 @@ + + +Test for Bug 160403 + + + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/svg/nsSVGMaskFrame.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/svg/nsSVGMaskFrame.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/svg/nsSVGMaskFrame.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/svg/nsSVGMaskFrame.cpp 2015-02-03 14:33:26.000000000 +0000 @@ -277,7 +277,7 @@ m = static_cast(kid->GetContent())-> PrependLocalTransformsTo(m); } - nsSVGUtils::PaintFrameWithEffects(kid, *tmpCtx, mMatrixForChildren); + nsSVGUtils::PaintFrameWithEffects(kid, *tmpCtx, m); } RefPtr maskSnapshot = maskDT->Snapshot(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/svg/SVGTextFrame.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/svg/SVGTextFrame.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/svg/SVGTextFrame.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/svg/SVGTextFrame.cpp 2015-02-03 14:33:26.000000000 +0000 @@ -2723,13 +2723,14 @@ { } + void NotifySelectionBackgroundNeedsFill(const Rect& aBackgroundRect, + nscolor aColor, + DrawTarget& aDrawTarget) MOZ_OVERRIDE; void NotifyBeforeText(nscolor aColor) MOZ_OVERRIDE; void NotifyGlyphPathEmitted() MOZ_OVERRIDE; void NotifyBeforeSVGGlyphPainted() MOZ_OVERRIDE; void NotifyAfterSVGGlyphPainted() MOZ_OVERRIDE; void NotifyAfterText() MOZ_OVERRIDE; - void NotifyBeforeSelectionBackground(nscolor aColor) MOZ_OVERRIDE; - void NotifySelectionBackgroundPathEmitted() MOZ_OVERRIDE; void NotifyBeforeDecorationLine(nscolor aColor) MOZ_OVERRIDE; void NotifyDecorationLinePathEmitted() MOZ_OVERRIDE; void NotifyBeforeSelectionDecorationLine(nscolor aColor) MOZ_OVERRIDE; @@ -2787,6 +2788,27 @@ }; void +SVGTextDrawPathCallbacks::NotifySelectionBackgroundNeedsFill( + const Rect& aBackgroundRect, + nscolor aColor, + DrawTarget& aDrawTarget) +{ + if (IsClipPathChild()) { + // Don't paint selection backgrounds when in a clip path. + return; + } + + mColor = aColor; // currently needed by MakeFillPattern + + GeneralPattern fillPattern; + MakeFillPattern(&fillPattern); + if (fillPattern.GetPattern()) { + DrawOptions drawOptions(aColor == NS_40PERCENT_FOREGROUND_COLOR ? 0.4 : 1.0); + aDrawTarget.FillRect(aBackgroundRect, fillPattern, drawOptions); + } +} + +void SVGTextDrawPathCallbacks::NotifyBeforeText(nscolor aColor) { mColor = aColor; @@ -2820,41 +2842,6 @@ gfx->Restore(); } -void -SVGTextDrawPathCallbacks::NotifyBeforeSelectionBackground(nscolor aColor) -{ - if (IsClipPathChild()) { - // Don't paint selection backgrounds when in a clip path. - return; - } - - mColor = aColor; - gfx->Save(); -} - -void -SVGTextDrawPathCallbacks::NotifySelectionBackgroundPathEmitted() -{ - if (IsClipPathChild()) { - // Don't paint selection backgrounds when in a clip path. - return; - } - - GeneralPattern fillPattern; - MakeFillPattern(&fillPattern); - if (fillPattern.GetPattern()) { - RefPtr path = gfx->GetPath(); - FillRule fillRule = nsSVGUtils::ToFillRule(mFrame->StyleSVG()->mFillRule); - if (fillRule != path->GetFillRule()) { - RefPtr builder = path->CopyToBuilder(fillRule); - path = builder->Finish(); - } - DrawOptions drawOptions(mColor == NS_40PERCENT_FOREGROUND_COLOR ? 0.4 : 1.0); - gfx->GetDrawTarget()->Fill(path, fillPattern, drawOptions); - } - gfx->Restore(); -} - void SVGTextDrawPathCallbacks::NotifyBeforeDecorationLine(nscolor aColor) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/tools/reftest/reftest-preferences.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/tools/reftest/reftest-preferences.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/tools/reftest/reftest-preferences.js 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/tools/reftest/reftest-preferences.js 2015-02-03 14:33:26.000000000 +0000 @@ -24,6 +24,10 @@ branch.setIntPref("urlclassifier.updateinterval", 172800); // Disable high-quality downscaling, since it makes reftests more difficult. branch.setBoolPref("image.high_quality_downscaling.enabled", false); + // Disable the single-color optimization, since it can cause intermittent + // oranges and it causes many of our tests to test a different code path + // than the one that normal images on the web use. + branch.setBoolPref("image.single-color-optimization.enabled", false); // Checking whether two files are the same is slow on Windows. // Setting this pref makes tests run much faster there. branch.setBoolPref("security.fileuri.strict_origin_policy", false); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/xul/nsBoxFrame.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/xul/nsBoxFrame.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/xul/nsBoxFrame.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/xul/nsBoxFrame.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -1332,8 +1332,6 @@ aBuilder->AddWindowExcludeGlassRegion( nsRect(aBuilder->ToReferenceFrame(this), GetSize())); } - - aBuilder->AdjustWindowDraggingRegion(this); } nsDisplayListCollection tempLists; @@ -2095,14 +2093,15 @@ bool nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, nsPoint &aPoint) { - nsIntPoint refPoint; + LayoutDeviceIntPoint refPoint; bool res = GetEventPoint(aEvent, refPoint); - aPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, refPoint, this); + aPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo( + aEvent, refPoint, this); return res; } bool -nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, nsIntPoint &aPoint) { +nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, LayoutDeviceIntPoint& aPoint) { NS_ENSURE_TRUE(aEvent, false); WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); @@ -2119,7 +2118,7 @@ } aPoint = touch->mRefPoint; } else { - aPoint = LayoutDeviceIntPoint::ToUntyped(aEvent->refPoint); + aPoint = aEvent->refPoint; } return true; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/xul/nsBoxFrame.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/xul/nsBoxFrame.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/xul/nsBoxFrame.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/xul/nsBoxFrame.h 2015-02-03 14:33:27.000000000 +0000 @@ -209,7 +209,8 @@ bool GetEventPoint(mozilla::WidgetGUIEvent* aEvent, nsPoint& aPoint); // Gets the event coordinates relative to the widget offset associated with // this frame. Return true if a single valid point was found. - bool GetEventPoint(mozilla::WidgetGUIEvent* aEvent, nsIntPoint& aPoint); + bool GetEventPoint(mozilla::WidgetGUIEvent* aEvent, + mozilla::LayoutDeviceIntPoint& aPoint); protected: void RegUnregAccessKey(bool aDoReg); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/xul/nsMenuPopupFrame.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/xul/nsMenuPopupFrame.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/xul/nsMenuPopupFrame.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/xul/nsMenuPopupFrame.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -1477,7 +1477,9 @@ return screenRect; } -void nsMenuPopupFrame::CanAdjustEdges(int8_t aHorizontalSide, int8_t aVerticalSide, nsIntPoint& aChange) +void nsMenuPopupFrame::CanAdjustEdges(int8_t aHorizontalSide, + int8_t aVerticalSide, + LayoutDeviceIntPoint& aChange) { int8_t popupAlign(mPopupAlignment); if (IsDirectionRTL()) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/xul/nsMenuPopupFrame.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/xul/nsMenuPopupFrame.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/xul/nsMenuPopupFrame.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/xul/nsMenuPopupFrame.h 2015-02-03 14:33:27.000000000 +0000 @@ -366,7 +366,9 @@ // Later, when bug 357725 is implemented, we can make this adjust aChange by // the amount that the side can be resized, so that minimums and maximums // can be taken into account. - void CanAdjustEdges(int8_t aHorizontalSide, int8_t aVerticalSide, nsIntPoint& aChange); + void CanAdjustEdges(int8_t aHorizontalSide, + int8_t aVerticalSide, + mozilla::LayoutDeviceIntPoint& aChange); // Return true if the popup is positioned relative to an anchor. bool IsAnchored() const { return mScreenXPos == -1 && mScreenYPos == -1; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/xul/nsResizerFrame.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/xul/nsResizerFrame.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/xul/nsResizerFrame.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/xul/nsResizerFrame.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -114,10 +114,11 @@ } // remember current mouse coordinates - nsIntPoint refPoint; + LayoutDeviceIntPoint refPoint; if (!GetEventPoint(aEvent, refPoint)) return NS_OK; - mMouseDownPoint = refPoint + aEvent->widget->WidgetToScreenOffset(); + mMouseDownPoint = refPoint + + LayoutDeviceIntPoint::FromUntyped(aEvent->widget->WidgetToScreenOffset()); // we're tracking mTrackingMouseMove = true; @@ -162,11 +163,12 @@ // retrieve the offset of the mousemove event relative to the mousedown. // The difference is how much the resize needs to be - nsIntPoint refPoint; + LayoutDeviceIntPoint refPoint; if (!GetEventPoint(aEvent, refPoint)) return NS_OK; - nsIntPoint screenPoint(refPoint + aEvent->widget->WidgetToScreenOffset()); - nsIntPoint mouseMove(screenPoint - mMouseDownPoint); + LayoutDeviceIntPoint screenPoint = refPoint + + LayoutDeviceIntPoint::FromUntyped(aEvent->widget->WidgetToScreenOffset()); + LayoutDeviceIntPoint mouseMove(screenPoint - mMouseDownPoint); // Determine which direction to resize by checking the dir attribute. // For windows and menus, ensure that it can be resized in that direction. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/xul/nsResizerFrame.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/xul/nsResizerFrame.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/xul/nsResizerFrame.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/xul/nsResizerFrame.h 2015-02-03 14:33:27.000000000 +0000 @@ -66,8 +66,8 @@ static void RestoreOriginalSize(nsIContent* aContent); protected: - nsIntRect mMouseDownRect; - nsIntPoint mMouseDownPoint; + nsIntRect mMouseDownRect; + LayoutDeviceIntPoint mMouseDownPoint; }; // class nsResizerFrame #endif /* nsResizerFrame_h___ */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/xul/nsXULPopupManager.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/xul/nsXULPopupManager.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/layout/xul/nsXULPopupManager.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/layout/xul/nsXULPopupManager.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -205,10 +205,19 @@ ConsumeOutsideClicksResult consumeResult = item->Frame()->ConsumeOutsideClicks(); consume = (consumeResult == ConsumeOutsideClicks_True); - // If ConsumeOutsideClicks_ParentOnly was returned, then only consume the - // click is it was over the anchor. This way, clicking on a menu doesn't + bool rollup = true; + + // If norolluponanchor is true, then don't rollup when clicking the anchor. + // This would be used to allow adjusting the caret position in an + // autocomplete field without hiding the popup for example. + bool noRollupOnAnchor = (!consume && pos && + item->Frame()->GetContent()->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::norolluponanchor, nsGkAtoms::_true, eCaseMatters)); + + // When ConsumeOutsideClicks_ParentOnly is used, always consume the click + // when the click was over the anchor. This way, clicking on a menu doesn't // reopen the menu. - if (consumeResult == ConsumeOutsideClicks_ParentOnly && pos) { + if ((consumeResult == ConsumeOutsideClicks_ParentOnly || noRollupOnAnchor) && pos) { nsCOMPtr anchor = item->Frame()->GetAnchor(); // Check if the anchor has indicated another node to use for checking @@ -234,33 +243,41 @@ // event will get consumed, so here only a quick coordinates check is // done rather than a slower complete check of what is at that location. if (anchor->GetPrimaryFrame()->GetScreenRect().Contains(*pos)) { - consume = true; + if (consumeResult == ConsumeOutsideClicks_ParentOnly) { + consume = true; + } + + if (noRollupOnAnchor) { + rollup = false; + } } } } - // if a number of popups to close has been specified, determine the last - // popup to close - nsIContent* lastPopup = nullptr; - if (aCount != UINT32_MAX) { - nsMenuChainItem* last = item; - while (--aCount && last->GetParent()) { - last = last->GetParent(); - } - if (last) { - lastPopup = last->Content(); + if (rollup) { + // if a number of popups to close has been specified, determine the last + // popup to close + nsIContent* lastPopup = nullptr; + if (aCount != UINT32_MAX) { + nsMenuChainItem* last = item; + while (--aCount && last->GetParent()) { + last = last->GetParent(); + } + if (last) { + lastPopup = last->Content(); + } } - } - nsPresContext* presContext = item->Frame()->PresContext(); - nsRefPtr viewManager = presContext->PresShell()->GetViewManager(); + nsPresContext* presContext = item->Frame()->PresContext(); + nsRefPtr viewManager = presContext->PresShell()->GetViewManager(); - HidePopup(item->Content(), true, true, false, true, lastPopup); + HidePopup(item->Content(), true, true, false, true, lastPopup); - if (aFlush) { - // The popup's visibility doesn't update until the minimize animation has - // finished, so call UpdateWidgetGeometry to update it right away. - viewManager->UpdateWidgetGeometry(); + if (aFlush) { + // The popup's visibility doesn't update until the minimize animation has + // finished, so call UpdateWidgetGeometry to update it right away. + viewManager->UpdateWidgetGeometry(); + } } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/AnnexB.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/AnnexB.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/AnnexB.cpp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/AnnexB.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -0,0 +1,65 @@ +/* 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 "AnnexB.h" +#include "mozilla/Endian.h" + +using mozilla::BigEndian; + +static const uint8_t kAnnexBDelimiter[] = { 0, 0, 0, 1 }; + +/* static */ void +AnnexB::ConvertFrameInPlace(std::vector& aBuffer) +{ + for (size_t i = 0; i < aBuffer.size() - 4 - sizeof(kAnnexBDelimiter) + 1; ) { + uint32_t nalLen = BigEndian::readUint32(&aBuffer[i]); + memcpy(&aBuffer[i], kAnnexBDelimiter, sizeof(kAnnexBDelimiter)); + i += nalLen + 4; + } +} + +static void +ConvertParamSetToAnnexB(std::vector::const_iterator& aIter, + size_t aCount, + std::vector& aOutAnnexB) +{ + for (size_t i = 0; i < aCount; i++) { + aOutAnnexB.insert(aOutAnnexB.end(), kAnnexBDelimiter, + kAnnexBDelimiter + sizeof(kAnnexBDelimiter)); + + uint16_t len = BigEndian::readUint16(&*aIter); aIter += 2; + aOutAnnexB.insert(aOutAnnexB.end(), aIter, aIter + len); aIter += len; + } +} + +/* static */ void +AnnexB::ConvertConfig(const std::vector& aBuffer, + std::vector& aOutAnnexB) +{ + // Skip past irrelevant headers + auto& it = aBuffer.begin() + 5; + + if (it >= aBuffer.end()) { + return; + } + + size_t count = *(it++) & 31; + + // Check that we have enough bytes for the Annex B conversion + // and the next size field. Bail if not. + if (it + count * 2 >= aBuffer.end()) { + return; + } + + ConvertParamSetToAnnexB(it, count, aOutAnnexB); + + // Check that we have enough bytes for the Annex B conversion. + count = *(it++); + if (it + count * 2 > aBuffer.end()) { + aOutAnnexB.clear(); + return; + } + + ConvertParamSetToAnnexB(it, count, aOutAnnexB); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/AnnexB.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/AnnexB.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/AnnexB.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/AnnexB.h 2015-02-03 14:33:27.000000000 +0000 @@ -0,0 +1,20 @@ +/* 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 __AnnexB_h__ +#define __AnnexB_h__ + +#include +#include + +class AnnexB +{ +public: + static void ConvertFrameInPlace(std::vector& aBuffer); + + static void ConvertConfig(const std::vector& aBuffer, + std::vector& aOutAnnexB); +}; + +#endif // __AnnexB_h__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/AudioDecoder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/AudioDecoder.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/AudioDecoder.cpp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/AudioDecoder.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -0,0 +1,264 @@ +/* + * 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. + */ + +#include +#include + +#include "AudioDecoder.h" +#include "ClearKeyDecryptionManager.h" +#include "ClearKeyUtils.h" +#include "gmp-task-utils.h" + +using namespace wmf; + +AudioDecoder::AudioDecoder(GMPAudioHost *aHostAPI) + : mHostAPI(aHostAPI) + , mCallback(nullptr) + , mWorkerThread(nullptr) + , mMutex(nullptr) + , mNumInputTasks(0) +{ +} + +AudioDecoder::~AudioDecoder() +{ + mMutex->Destroy(); +} + +void +AudioDecoder::InitDecode(const GMPAudioCodec& aConfig, + GMPAudioDecoderCallback* aCallback) +{ + mCallback = aCallback; + assert(mCallback); + mDecoder = new WMFAACDecoder(); + HRESULT hr = mDecoder->Init(aConfig.mChannelCount, + aConfig.mSamplesPerSecond, + (BYTE*)aConfig.mExtraData, + aConfig.mExtraDataLen); + LOG("[%p] AudioDecoder::InitializeAudioDecoder() hr=0x%x\n", this, hr); + if (FAILED(hr)) { + mCallback->Error(GMPGenericErr); + return; + } + auto err = GetPlatform()->createmutex(&mMutex); + if (GMP_FAILED(err)) { + mCallback->Error(GMPGenericErr); + return; + } +} + +void +AudioDecoder::EnsureWorker() +{ + if (!mWorkerThread) { + GetPlatform()->createthread(&mWorkerThread); + if (!mWorkerThread) { + mCallback->Error(GMPAllocErr); + return; + } + } +} + +void +AudioDecoder::Decode(GMPAudioSamples* aInput) +{ + EnsureWorker(); + { + AutoLock lock(mMutex); + mNumInputTasks++; + } + mWorkerThread->Post(WrapTask(this, + &AudioDecoder::DecodeTask, + aInput)); +} + +void +AudioDecoder::DecodeTask(GMPAudioSamples* aInput) +{ + HRESULT hr; + + { + AutoLock lock(mMutex); + mNumInputTasks--; + assert(mNumInputTasks >= 0); + } + + if (!aInput || !mHostAPI || !mDecoder) { + LOG("Decode job not set up correctly!"); + return; + } + + const uint8_t* inBuffer = aInput->Buffer(); + if (!inBuffer) { + LOG("No buffer for encoded samples!\n"); + return; + } + + const GMPEncryptedBufferMetadata* crypto = aInput->GetDecryptionData(); + std::vector buffer(inBuffer, inBuffer + aInput->Size()); + if (crypto) { + // Plugin host should have set up its decryptor/key sessions + // before trying to decode! + GMPErr rv = + ClearKeyDecryptionManager::Get()->Decrypt(&buffer[0], buffer.size(), crypto); + + if (GMP_FAILED(rv)) { + CK_LOGE("Failed to decrypt with key id %08x...", *(uint32_t*)crypto->KeyId()); + GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::Error, rv)); + return; + } + } + + hr = mDecoder->Input(&buffer[0], + buffer.size(), + aInput->TimeStamp()); + + // We must delete the input sample! + GetPlatform()->runonmainthread(WrapTask(aInput, &GMPAudioSamples::Destroy)); + + SAMPLE_LOG("AudioDecoder::DecodeTask() Input ret hr=0x%x\n", hr); + if (FAILED(hr)) { + LOG("AudioDecoder::DecodeTask() decode failed ret=0x%x%s\n", + hr, + ((hr == MF_E_NOTACCEPTING) ? " (MF_E_NOTACCEPTING)" : "")); + return; + } + + while (hr == S_OK) { + CComPtr output; + hr = mDecoder->Output(&output); + SAMPLE_LOG("AudioDecoder::DecodeTask() output ret=0x%x\n", hr); + if (hr == S_OK) { + ReturnOutput(output); + } + if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { + AutoLock lock(mMutex); + if (mNumInputTasks == 0) { + // We have run all input tasks. We *must* notify Gecko so that it will + // send us more data. + GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::InputDataExhausted)); + } + } else if (FAILED(hr)) { + LOG("AudioDecoder::DecodeTask() output failed hr=0x%x\n", hr); + } + } +} + +void +AudioDecoder::ReturnOutput(IMFSample* aSample) +{ + SAMPLE_LOG("[%p] AudioDecoder::ReturnOutput()\n", this); + assert(aSample); + + HRESULT hr; + + GMPAudioSamples* samples = nullptr; + mHostAPI->CreateSamples(kGMPAudioIS16Samples, &samples); + if (!samples) { + LOG("Failed to create i420 frame!\n"); + return; + } + + hr = MFToGMPSample(aSample, samples); + if (FAILED(hr)) { + samples->Destroy(); + LOG("Failed to prepare output sample!"); + return; + } + ENSURE(SUCCEEDED(hr), /*void*/); + + GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::Decoded, samples)); +} + +HRESULT +AudioDecoder::MFToGMPSample(IMFSample* aInput, + GMPAudioSamples* aOutput) +{ + ENSURE(aInput != nullptr, E_POINTER); + ENSURE(aOutput != nullptr, E_POINTER); + + HRESULT hr; + CComPtr mediaBuffer; + + hr = aInput->ConvertToContiguousBuffer(&mediaBuffer); + ENSURE(SUCCEEDED(hr), hr); + + BYTE* data = nullptr; // Note: *data will be owned by the IMFMediaBuffer, we don't need to free it. + DWORD maxLength = 0, currentLength = 0; + hr = mediaBuffer->Lock(&data, &maxLength, ¤tLength); + ENSURE(SUCCEEDED(hr), hr); + + auto err = aOutput->SetBufferSize(currentLength); + ENSURE(GMP_SUCCEEDED(err), E_FAIL); + + memcpy(aOutput->Buffer(), data, currentLength); + + mediaBuffer->Unlock(); + + LONGLONG hns = 0; + hr = aInput->GetSampleTime(&hns); + ENSURE(SUCCEEDED(hr), hr); + aOutput->SetTimeStamp(HNsToUsecs(hns)); + aOutput->SetChannels(mDecoder->Channels()); + aOutput->SetRate(mDecoder->Rate()); + + return S_OK; +} + +void +AudioDecoder::Reset() +{ + mDecoder->Reset(); + mCallback->ResetComplete(); +} + +void +AudioDecoder::DrainTask() +{ + if (FAILED(mDecoder->Drain())) { + GetPlatform()->syncrunonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::DrainComplete)); + } + + // Return any pending output. + HRESULT hr = S_OK; + while (hr == S_OK) { + CComPtr output; + hr = mDecoder->Output(&output); + SAMPLE_LOG("AudioDecoder::DrainTask() output ret=0x%x\n", hr); + if (hr == S_OK) { + ReturnOutput(output); + } + } + GetPlatform()->syncrunonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::DrainComplete)); +} + +void +AudioDecoder::Drain() +{ + EnsureWorker(); + mWorkerThread->Post(WrapTask(this, + &AudioDecoder::DrainTask)); +} + +void +AudioDecoder::DecodingComplete() +{ + if (mWorkerThread) { + mWorkerThread->Join(); + } + delete this; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/AudioDecoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/AudioDecoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/AudioDecoder.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/AudioDecoder.h 2015-02-03 14:33:27.000000000 +0000 @@ -0,0 +1,65 @@ +/* + * 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 __AudioDecoder_h__ +#define __AudioDecoder_h__ + +#include "gmp-audio-decode.h" +#include "gmp-audio-host.h" +#include "WMFAACDecoder.h" + +#include "mfobjects.h" + +class AudioDecoder : public GMPAudioDecoder +{ +public: + AudioDecoder(GMPAudioHost *aHostAPI); + + virtual ~AudioDecoder(); + + virtual void InitDecode(const GMPAudioCodec& aCodecSettings, + GMPAudioDecoderCallback* aCallback) override; + + virtual void Decode(GMPAudioSamples* aEncodedSamples); + + virtual void Reset() override; + + virtual void Drain() override; + + virtual void DecodingComplete() override; + +private: + + void EnsureWorker(); + + void DecodeTask(GMPAudioSamples* aEncodedSamples); + void DrainTask(); + + void ReturnOutput(IMFSample* aSample); + + HRESULT MFToGMPSample(IMFSample* aSample, + GMPAudioSamples* aAudioFrame); + + GMPAudioHost *mHostAPI; // host-owned, invalid at DecodingComplete + GMPAudioDecoderCallback* mCallback; // host-owned, invalid at DecodingComplete + GMPThread* mWorkerThread; + GMPMutex* mMutex; + wmf::AutoPtr mDecoder; + + int32_t mNumInputTasks; +}; + +#endif // __AudioDecoder_h__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -19,7 +19,7 @@ bool HasKey() const { return !!mKey.size(); } GMPErr Decrypt(uint8_t* aBuffer, uint32_t aBufferSize, - GMPEncryptedBufferMetadata* aMetadata); + const GMPEncryptedBufferMetadata* aMetadata); const Key& DecryptionKey() const { return mKey; } @@ -61,14 +61,14 @@ bool ClearKeyDecryptionManager::HasSeenKeyId(const KeyId& aKeyId) const { - CK_LOGD("ClearKeyDecryptionManager::HasSeenKeyId"); + CK_LOGD("ClearKeyDecryptionManager::SeenKeyId %s", mDecryptors.find(aKeyId) != mDecryptors.end() ? "t" : "f"); return mDecryptors.find(aKeyId) != mDecryptors.end(); } bool ClearKeyDecryptionManager::IsExpectingKeyForKeyId(const KeyId& aKeyId) const { - CK_LOGD("ClearKeyDecryptionManager::IsExpectingKeyForKeyId"); + CK_LOGD("ClearKeyDecryptionManager::IsExpectingKeyForId %08x...", *(uint32_t*)&aKeyId[0]); const auto& decryptor = mDecryptors.find(aKeyId); return decryptor != mDecryptors.end() && !decryptor->second->HasKey(); } @@ -91,7 +91,7 @@ void ClearKeyDecryptionManager::InitKey(KeyId aKeyId, Key aKey) { - CK_LOGD("ClearKeyDecryptionManager::InitKey"); + CK_LOGD("ClearKeyDecryptionManager::InitKey %08x...", *(uint32_t*)&aKeyId[0]); if (IsExpectingKeyForKeyId(aKeyId)) { mDecryptors[aKeyId]->InitKey(aKey); } @@ -100,7 +100,7 @@ void ClearKeyDecryptionManager::ExpectKeyId(KeyId aKeyId) { - CK_LOGD("ClearKeyDecryptionManager::ExpectKeyId"); + CK_LOGD("ClearKeyDecryptionManager::ExpectKeyId %08x...", *(uint32_t*)&aKeyId[0]); if (!HasSeenKeyId(aKeyId)) { mDecryptors[aKeyId] = new ClearKeyDecryptor(); } @@ -111,6 +111,8 @@ ClearKeyDecryptionManager::ReleaseKeyId(KeyId aKeyId) { CK_LOGD("ClearKeyDecryptionManager::ReleaseKeyId"); + MOZ_ASSERT(HasKeyForKeyId(aKeyId)); + ClearKeyDecryptor* decryptor = mDecryptors[aKeyId]; if (!decryptor->Release()) { mDecryptors.erase(aKeyId); @@ -119,7 +121,7 @@ GMPErr ClearKeyDecryptionManager::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize, - GMPEncryptedBufferMetadata* aMetadata) + const GMPEncryptedBufferMetadata* aMetadata) { CK_LOGD("ClearKeyDecryptionManager::Decrypt"); KeyId keyId(aMetadata->KeyId(), aMetadata->KeyId() + aMetadata->KeyIdSize()); @@ -138,7 +140,7 @@ ClearKeyDecryptor::~ClearKeyDecryptor() { - CK_LOGD("ClearKeyDecryptor dtor; key ID = %08x...", *(uint32_t*)&mKey[0]); + CK_LOGD("ClearKeyDecryptor dtor; key = %08x...", *(uint32_t*)&mKey[0]); } void @@ -149,7 +151,7 @@ GMPErr ClearKeyDecryptor::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize, - GMPEncryptedBufferMetadata* aMetadata) + const GMPEncryptedBufferMetadata* aMetadata) { CK_LOGD("ClearKeyDecryptor::Decrypt"); // If the sample is split up into multiple encrypted subsamples, we need to diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h 2015-02-03 14:33:27.000000000 +0000 @@ -34,7 +34,7 @@ void ReleaseKeyId(KeyId aKeyId); GMPErr Decrypt(uint8_t* aBuffer, uint32_t aBufferSize, - GMPEncryptedBufferMetadata* aMetadata); + const GMPEncryptedBufferMetadata* aMetadata); void Shutdown(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/clearkey.info thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/clearkey.info --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/clearkey.info 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/clearkey.info 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -Name: clearkey -Description: ClearKey decrypt-only GMP plugin -Version: 0.1 -APIs: eme-decrypt-v3[org.w3.clearkey] diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/clearkey.info.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/clearkey.info.in --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/clearkey.info.in 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/clearkey.info.in 2015-02-03 14:33:27.000000000 +0000 @@ -0,0 +1,10 @@ +Name: clearkey +Description: ClearKey decrypt-only GMP plugin +Version: 0.1 +#ifdef ENABLE_WMF +APIs: eme-decrypt-v6[org.w3.clearkey], decode-audio[aac:org.w3.clearkey], decode-video[h264:org.w3.clearkey] +Libraries: dxva2.dll, d3d9.dll, msmpeg2vdec.dll, msmpeg2adec.dll, MSAudDecMFT.dll +#else +APIs: eme-decrypt-v6[org.w3.clearkey] +Libraries: +#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -9,6 +9,7 @@ #include "mozilla/RefPtr.h" #include +#include #include #include #include diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/ClearKeySession.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/ClearKeySession.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/ClearKeySession.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/ClearKeySession.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -2,6 +2,7 @@ * 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 "ClearKeyDecryptionManager.h" #include "ClearKeySession.h" #include "ClearKeyUtils.h" #include "ClearKeyStorage.h" @@ -26,6 +27,16 @@ ClearKeySession::~ClearKeySession() { CK_LOGD("ClearKeySession dtor %p", this); + + auto& keyIds = GetKeyIds(); + for (auto it = keyIds.begin(); it != keyIds.end(); it++) { + MOZ_ASSERT(ClearKeyDecryptionManager::Get()->HasKeyForKeyId(*it)); + + ClearKeyDecryptionManager::Get()->ReleaseKeyId(*it); + mCallback->KeyStatusChanged(&mSessionId[0], mSessionId.size(), + &(*it)[0], it->size(), + kGMPUnknown); + } } void diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -20,7 +20,7 @@ ClearKeySessionManager::ClearKeySessionManager() : mDecryptionManager(ClearKeyDecryptionManager::Get()) { - CK_LOGD("ClearKeySessionManager ctor"); + CK_LOGD("ClearKeySessionManager ctor %p", this); AddRef(); if (GetPlatform()->createthread(&mThread) != GMPNoErr) { @@ -31,17 +31,28 @@ ClearKeySessionManager::~ClearKeySessionManager() { - CK_LOGD("ClearKeySessionManager dtor"); + CK_LOGD("ClearKeySessionManager dtor %p", this); MOZ_ASSERT(!mRefCount); } +static bool +CanDecode() +{ + return +#if defined(ENABLE_WMF) + wmf::EnsureLibs() || +#endif + false; +} + void ClearKeySessionManager::Init(GMPDecryptorCallback* aCallback) { CK_LOGD("ClearKeySessionManager::Init"); mCallback = aCallback; - mCallback->SetCapabilities(GMP_EME_CAP_DECRYPT_AUDIO | - GMP_EME_CAP_DECRYPT_VIDEO); + mCallback->SetCapabilities(CanDecode() ? + GMP_EME_CAP_DECRYPT_AND_DECODE_AUDIO | GMP_EME_CAP_DECRYPT_AND_DECODE_VIDEO : + GMP_EME_CAP_DECRYPT_AUDIO | GMP_EME_CAP_DECRYPT_VIDEO); ClearKeyPersistence::EnsureInitialized(); } @@ -167,8 +178,9 @@ mDecryptionManager->ExpectKeyId(keyId); mDecryptionManager->InitKey(keyId, key); mKeyIds.insert(key); - mCallback->KeyIdUsable(&aSessionId[0], aSessionId.size(), - &keyId[0], keyId.size()); + mCallback->KeyStatusChanged(&aSessionId[0], aSessionId.size(), + &keyId[0], keyId.size(), + kGMPUsable); } mCallback->ResolveLoadSessionPromise(aPromiseId, true); @@ -203,8 +215,9 @@ for (auto it = keyPairs.begin(); it != keyPairs.end(); it++) { mDecryptionManager->InitKey(it->mKeyId, it->mKey); mKeyIds.insert(it->mKeyId); - mCallback->KeyIdUsable(aSessionId, aSessionIdLength, - &it->mKeyId[0], it->mKeyId.size()); + mCallback->KeyStatusChanged(aSessionId, aSessionIdLength, + &it->mKeyId[0], it->mKeyId.size(), + kGMPUsable); } if (session->Type() != kGMPPersistentSession) { @@ -271,18 +284,6 @@ void ClearKeySessionManager::ClearInMemorySessionData(ClearKeySession* aSession) { - MOZ_ASSERT(aSession); - - const vector& keyIds = aSession->GetKeyIds(); - for (auto it = keyIds.begin(); it != keyIds.end(); it++) { - MOZ_ASSERT(mDecryptionManager->HasKeyForKeyId(*it)); - mDecryptionManager->ReleaseKeyId(*it); - - const string& sessionId = aSession->Id(); - mCallback->KeyIdNotUsable(&sessionId[0], sessionId.size(), - &(*it)[0], it->size()); - } - mSessions.erase(aSession->Id()); delete aSession; } @@ -371,7 +372,7 @@ void ClearKeySessionManager::Shutdown() { - CK_LOGD("ClearKeySessionManager::Shutdown"); + CK_LOGD("ClearKeySessionManager::Shutdown %p", this); for (auto it = mSessions.begin(); it != mSessions.end(); it++) { delete it->second; @@ -382,7 +383,7 @@ void ClearKeySessionManager::DecryptingComplete() { - CK_LOGD("ClearKeySessionManager::DecryptingComplete"); + CK_LOGD("ClearKeySessionManager::DecryptingComplete %p", this); GMPThread* thread = mThread; thread->Join(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/ClearKeySessionManager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/ClearKeySessionManager.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/ClearKeySessionManager.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/ClearKeySessionManager.h 2015-02-03 14:33:27.000000000 +0000 @@ -14,8 +14,8 @@ #include "ClearKeySession.h" #include "ClearKeyUtils.h" #include "gmp-api/gmp-decryption.h" +#include "mozilla/Attributes.h" #include "mozilla/RefPtr.h" -#include "ScopedNSSTypes.h" #include "RefCounted.h" class ClearKeySessionManager MOZ_FINAL : public GMPDecryptor diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/ClearKeyUtils.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/ClearKeyUtils.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/ClearKeyUtils.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/ClearKeyUtils.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -35,6 +35,7 @@ va_end(ap); printf("\n"); + fflush(stdout); } static void @@ -64,7 +65,7 @@ oaes_encrypt(aes, &aIV[0], CLEARKEY_KEY_LEN, &enc[0], &encLen); MOZ_ASSERT(encLen >= 2 * OAES_BLOCK_SIZE + CLEARKEY_KEY_LEN); - size_t blockLen = std::min(aData.size() - i, CLEARKEY_KEY_LEN); + size_t blockLen = min(aData.size() - i, CLEARKEY_KEY_LEN); for (size_t j = 0; j < blockLen; j++) { aData[i + j] ^= enc[2 * OAES_BLOCK_SIZE + j]; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/gmp-clearkey.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/gmp-clearkey.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/gmp-clearkey.cpp 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/gmp-clearkey.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -2,10 +2,14 @@ * 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 #include #include #include "ClearKeySessionManager.h" +#if defined(ENABLE_WMF) +#include "WMFUtils.h" +#endif #include "gmp-api/gmp-decryption.h" #include "gmp-api/gmp-platform.h" @@ -30,18 +34,32 @@ MOZ_EXPORT GMPErr GMPGetAPI(const char* aApiName, void* aHostAPI, void** aPluginAPI) { - if (strcmp(aApiName, GMP_API_DECRYPTOR)) { - return GMPNotImplementedErr; - } + CK_LOGD("ClearKey GMPGetAPI |%s|", aApiName); + assert(!*aPluginAPI); - *aPluginAPI = new ClearKeySessionManager(); + if (!strcmp(aApiName, GMP_API_DECRYPTOR)) { + *aPluginAPI = new ClearKeySessionManager(); + } +#if defined(ENABLE_WMF) + else if (wmf::EnsureLibs()) { + if (!strcmp(aApiName, GMP_API_AUDIO_DECODER)) { + *aPluginAPI = new AudioDecoder(static_cast(aHostAPI)); + } else if (!strcmp(aApiName, GMP_API_VIDEO_DECODER)) { + *aPluginAPI = new VideoDecoder(static_cast(aHostAPI)); + } + } +#endif + else { + CK_LOGE("GMPGetAPI couldn't resolve API name |%s|\n", aApiName); + } - return GMPNoErr; + return *aPluginAPI ? GMPNoErr : GMPNotImplementedErr; } MOZ_EXPORT GMPErr GMPShutdown(void) { + CK_LOGD("ClearKey GMPShutdown"); return GMPNoErr; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/Makefile.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/Makefile.in --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/Makefile.in 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/Makefile.in 2015-02-03 14:33:27.000000000 +0000 @@ -2,9 +2,9 @@ # 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/. -INSTALL_TARGETS += CLEARKEY_CDM - -CLEARKEY_CDM_DEST = $(DEPTH)/dist/bin/gmp-clearkey/0.1 -CLEARKEY_CDM_FILES = \ - clearkey.info \ +CLEARKEY_CDM_PATH := $(DEPTH)/dist/bin/gmp-clearkey/0.1 +CLEARKEY_CDM := \ + clearkey.info.in \ $(NULL) + +PP_TARGETS := CLEARKEY_CDM diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/moz.build 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/moz.build 2015-02-03 14:33:27.000000000 +0000 @@ -16,9 +16,28 @@ 'ClearKeyStorage.cpp', 'ClearKeyUtils.cpp', 'gmp-clearkey.cpp', +] + +SOURCES += [ 'openaes/oaes_lib.c', ] +if CONFIG['OS_ARCH'] == 'WINNT': + UNIFIED_SOURCES += [ + 'AnnexB.cpp', + 'AudioDecoder.cpp', + 'VideoDecoder.cpp', + 'WMFAACDecoder.cpp', + 'WMFH264Decoder.cpp', + ] + + SOURCES += [ + 'WMFUtils.cpp', + ] + + DEFINES['ENABLE_WMF'] = True + + LOCAL_INCLUDES += [ '/dom/media/gmp', ] diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/openaes/oaes_lib.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/openaes/oaes_lib.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/openaes/oaes_lib.c 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/openaes/oaes_lib.c 2015-02-03 14:33:27.000000000 +0000 @@ -629,7 +629,10 @@ _key->data = (uint8_t *) calloc( key_size, sizeof( uint8_t )); if( NULL == _key->data ) + { + oaes_key_destroy( &_key ); return OAES_RET_MEM; + } for( _i = 0; _i < key_size; _i++ ) _key->data[_i] = (uint8_t) OAES_RAND(_ctx->rctx); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/openaes/standard.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/openaes/standard.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/openaes/standard.h 2015-01-25 22:24:28.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/openaes/standard.h 2015-02-03 14:33:27.000000000 +0000 @@ -38,18 +38,18 @@ #define bis(target,mask) ((target) |= (mask)) #define bic(target,mask) ((target) &= ~(mask)) #define bit(target,mask) ((target) & (mask)) -#ifndef min -# define min(a,b) (((a)<(b)) ? (a) : (b)) -#endif /* min */ -#ifndef max -# define max(a,b) (((a)<(b)) ? (b) : (a)) -#endif /* max */ +// #ifndef min +// # define min(a,b) (((a)<(b)) ? (a) : (b)) +// #endif /* min */ +// #ifndef max +// # define max(a,b) (((a)<(b)) ? (b) : (a)) +// #endif /* max */ #ifndef align # define align(a) (((ub4)a+(sizeof(void *)-1))&(~(sizeof(void *)-1))) #endif /* align */ -#ifndef abs -# define abs(a) (((a)>0) ? (a) : -(a)) -#endif +// #ifndef abs +// # define abs(a) (((a)>0) ? (a) : -(a)) +// #endif #define TRUE 1 #define FALSE 0 #define SUCCESS 0 /* 1 on VAX */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/VideoDecoder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/VideoDecoder.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/VideoDecoder.cpp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/VideoDecoder.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -0,0 +1,352 @@ +/* + * 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. + */ + +#include +#include + +#include "AnnexB.h" +#include "ClearKeyDecryptionManager.h" +#include "ClearKeyUtils.h" +#include "gmp-task-utils.h" +#include "mozilla/Endian.h" +#include "VideoDecoder.h" + +using namespace wmf; + +VideoDecoder::VideoDecoder(GMPVideoHost *aHostAPI) + : mHostAPI(aHostAPI) + , mCallback(nullptr) + , mWorkerThread(nullptr) + , mMutex(nullptr) + , mNumInputTasks(0) + , mSentExtraData(false) +{ +} + +VideoDecoder::~VideoDecoder() +{ + mMutex->Destroy(); +} + +void +VideoDecoder::InitDecode(const GMPVideoCodec& aCodecSettings, + const uint8_t* aCodecSpecific, + uint32_t aCodecSpecificLength, + GMPVideoDecoderCallback* aCallback, + int32_t aCoreCount) +{ + mCallback = aCallback; + assert(mCallback); + mDecoder = new WMFH264Decoder(); + HRESULT hr = mDecoder->Init(); + if (FAILED(hr)) { + mCallback->Error(GMPGenericErr); + return; + } + + auto err = GetPlatform()->createmutex(&mMutex); + if (GMP_FAILED(err)) { + mCallback->Error(GMPGenericErr); + return; + } + + // The first byte is mPacketizationMode, which is only relevant for + // WebRTC/OpenH264 usecase. + const uint8_t* avcc = aCodecSpecific + 1; + const uint8_t* avccEnd = aCodecSpecific + aCodecSpecificLength; + mExtraData.insert(mExtraData.end(), avcc, avccEnd); + + AnnexB::ConvertConfig(mExtraData, mAnnexB); +} + +void +VideoDecoder::EnsureWorker() +{ + if (!mWorkerThread) { + GetPlatform()->createthread(&mWorkerThread); + if (!mWorkerThread) { + mCallback->Error(GMPAllocErr); + return; + } + } +} + +void +VideoDecoder::Decode(GMPVideoEncodedFrame* aInputFrame, + bool aMissingFrames, + const uint8_t* aCodecSpecificInfo, + uint32_t aCodecSpecificInfoLength, + int64_t aRenderTimeMs) +{ + if (aInputFrame->BufferType() != GMP_BufferLength32) { + // Gecko should only send frames with 4 byte NAL sizes to GMPs. + mCallback->Error(GMPGenericErr); + return; + } + + EnsureWorker(); + + { + AutoLock lock(mMutex); + mNumInputTasks++; + } + + // Note: we don't need the codec specific info on a per-frame basis. + // It's mostly useful for WebRTC use cases. + + mWorkerThread->Post(WrapTask(this, + &VideoDecoder::DecodeTask, + aInputFrame)); +} + +void +VideoDecoder::DecodeTask(GMPVideoEncodedFrame* aInput) +{ + CK_LOGD("VideoDecoder::DecodeTask"); + HRESULT hr; + + { + AutoLock lock(mMutex); + mNumInputTasks--; + assert(mNumInputTasks >= 0); + } + + if (!aInput || !mHostAPI || !mDecoder) { + CK_LOGE("Decode job not set up correctly!"); + return; + } + + const uint8_t* inBuffer = aInput->Buffer(); + if (!inBuffer) { + CK_LOGE("No buffer for encoded frame!\n"); + return; + } + + const GMPEncryptedBufferMetadata* crypto = aInput->GetDecryptionData(); + std::vector buffer(inBuffer, inBuffer + aInput->Size()); + if (crypto) { + // Plugin host should have set up its decryptor/key sessions + // before trying to decode! + GMPErr rv = + ClearKeyDecryptionManager::Get()->Decrypt(&buffer[0], buffer.size(), crypto); + + if (GMP_FAILED(rv)) { + GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPVideoDecoderCallback::Error, rv)); + return; + } + } + + AnnexB::ConvertFrameInPlace(buffer); + + if (aInput->FrameType() == kGMPKeyFrame) { + // We must send the SPS and PPS to Windows Media Foundation's decoder. + // Note: We do this *after* decryption, otherwise the subsample info + // would be incorrect. + buffer.insert(buffer.begin(), mAnnexB.begin(), mAnnexB.end()); + } + + hr = mDecoder->Input(buffer.data(), + buffer.size(), + aInput->TimeStamp(), + aInput->Duration()); + + // We must delete the input sample! + GetPlatform()->runonmainthread(WrapTask(aInput, &GMPVideoEncodedFrame::Destroy)); + + CK_LOGD("VideoDecoder::DecodeTask() Input ret hr=0x%x\n", hr); + if (FAILED(hr)) { + CK_LOGE("VideoDecoder::DecodeTask() decode failed ret=0x%x%s\n", + hr, + ((hr == MF_E_NOTACCEPTING) ? " (MF_E_NOTACCEPTING)" : "")); + return; + } + + while (hr == S_OK) { + CComPtr output; + hr = mDecoder->Output(&output); + CK_LOGD("VideoDecoder::DecodeTask() output ret=0x%x\n", hr); + if (hr == S_OK) { + ReturnOutput(output); + } + if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { + AutoLock lock(mMutex); + if (mNumInputTasks == 0) { + // We have run all input tasks. We *must* notify Gecko so that it will + // send us more data. + GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPVideoDecoderCallback::InputDataExhausted)); + } + } + if (FAILED(hr)) { + CK_LOGE("VideoDecoder::DecodeTask() output failed hr=0x%x\n", hr); + } + } +} + +void +VideoDecoder::ReturnOutput(IMFSample* aSample) +{ + CK_LOGD("[%p] VideoDecoder::ReturnOutput()\n", this); + assert(aSample); + + HRESULT hr; + + GMPVideoFrame* f = nullptr; + mHostAPI->CreateFrame(kGMPI420VideoFrame, &f); + if (!f) { + CK_LOGE("Failed to create i420 frame!\n"); + return; + } + auto vf = static_cast(f); + + hr = SampleToVideoFrame(aSample, vf); + ENSURE(SUCCEEDED(hr), /*void*/); + + GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPVideoDecoderCallback::Decoded, vf)); + +} + +HRESULT +VideoDecoder::SampleToVideoFrame(IMFSample* aSample, + GMPVideoi420Frame* aVideoFrame) +{ + ENSURE(aSample != nullptr, E_POINTER); + ENSURE(aVideoFrame != nullptr, E_POINTER); + + HRESULT hr; + CComPtr mediaBuffer; + + // Must convert to contiguous mediaBuffer to use IMD2DBuffer interface. + hr = aSample->ConvertToContiguousBuffer(&mediaBuffer); + ENSURE(SUCCEEDED(hr), hr); + + // Try and use the IMF2DBuffer interface if available, otherwise fallback + // to the IMFMediaBuffer interface. Apparently IMF2DBuffer is more efficient, + // but only some systems (Windows 8?) support it. + BYTE* data = nullptr; + LONG stride = 0; + CComPtr twoDBuffer; + hr = mediaBuffer->QueryInterface(static_cast(&twoDBuffer)); + if (SUCCEEDED(hr)) { + hr = twoDBuffer->Lock2D(&data, &stride); + ENSURE(SUCCEEDED(hr), hr); + } else { + hr = mediaBuffer->Lock(&data, NULL, NULL); + ENSURE(SUCCEEDED(hr), hr); + stride = mDecoder->GetStride(); + } + int32_t width = mDecoder->GetFrameWidth(); + int32_t height = mDecoder->GetFrameHeight(); + + // The V and U planes are stored 16-row-aligned, so we need to add padding + // to the row heights to ensure the Y'CbCr planes are referenced properly. + // YV12, planar format: [YYYY....][VVVV....][UUUU....] + // i.e., Y, then V, then U. + uint32_t padding = 0; + if (height % 16 != 0) { + padding = 16 - (height % 16); + } + int32_t y_size = stride * (height + padding); + int32_t v_size = stride * (height + padding) / 4; + int32_t halfStride = (stride + 1) / 2; + int32_t halfHeight = (height + 1) / 2; + int32_t halfWidth = (width + 1) / 2; + int32_t totalSize = y_size + 2 * v_size; + + GetPlatform()->syncrunonmainthread(WrapTask(aVideoFrame, + &GMPVideoi420Frame::CreateEmptyFrame, + stride, height, stride, halfStride, halfStride)); + + auto err = aVideoFrame->SetWidth(width); + ENSURE(GMP_SUCCEEDED(err), E_FAIL); + err = aVideoFrame->SetHeight(height); + ENSURE(GMP_SUCCEEDED(err), E_FAIL); + + uint8_t* outBuffer = aVideoFrame->Buffer(kGMPYPlane); + ENSURE(outBuffer != nullptr, E_FAIL); + assert(aVideoFrame->AllocatedSize(kGMPYPlane) >= stride*height); + memcpy(outBuffer, data, stride*height); + + outBuffer = aVideoFrame->Buffer(kGMPUPlane); + ENSURE(outBuffer != nullptr, E_FAIL); + assert(aVideoFrame->AllocatedSize(kGMPUPlane) >= halfStride*halfHeight); + memcpy(outBuffer, data+y_size, halfStride*halfHeight); + + outBuffer = aVideoFrame->Buffer(kGMPVPlane); + ENSURE(outBuffer != nullptr, E_FAIL); + assert(aVideoFrame->AllocatedSize(kGMPVPlane) >= halfStride*halfHeight); + memcpy(outBuffer, data + y_size + v_size, halfStride*halfHeight); + + if (twoDBuffer) { + twoDBuffer->Unlock2D(); + } else { + mediaBuffer->Unlock(); + } + + LONGLONG hns = 0; + hr = aSample->GetSampleTime(&hns); + ENSURE(SUCCEEDED(hr), hr); + aVideoFrame->SetTimestamp(HNsToUsecs(hns)); + + hr = aSample->GetSampleDuration(&hns); + ENSURE(SUCCEEDED(hr), hr); + aVideoFrame->SetDuration(HNsToUsecs(hns)); + + return S_OK; +} + +void +VideoDecoder::Reset() +{ + mDecoder->Reset(); + mCallback->ResetComplete(); +} + +void +VideoDecoder::DrainTask() +{ + if (FAILED(mDecoder->Drain())) { + GetPlatform()->syncrunonmainthread(WrapTask(mCallback, &GMPVideoDecoderCallback::DrainComplete)); + } + + // Return any pending output. + HRESULT hr = S_OK; + while (hr == S_OK) { + CComPtr output; + hr = mDecoder->Output(&output); + CK_LOGD("VideoDecoder::DrainTask() output ret=0x%x\n", hr); + if (hr == S_OK) { + ReturnOutput(output); + } + } + GetPlatform()->syncrunonmainthread(WrapTask(mCallback, &GMPVideoDecoderCallback::DrainComplete)); +} + +void +VideoDecoder::Drain() +{ + EnsureWorker(); + mWorkerThread->Post(WrapTask(this, + &VideoDecoder::DrainTask)); +} + +void +VideoDecoder::DecodingComplete() +{ + if (mWorkerThread) { + mWorkerThread->Join(); + } + delete this; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/VideoDecoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/VideoDecoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/VideoDecoder.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/VideoDecoder.h 2015-02-03 14:33:27.000000000 +0000 @@ -0,0 +1,77 @@ +/* + * 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 __VideoDecoder_h__ +#define __VideoDecoder_h__ + +#include "gmp-video-decode.h" +#include "gmp-video-host.h" +#include "WMFH264Decoder.h" + +#include "mfobjects.h" + +class VideoDecoder : public GMPVideoDecoder +{ +public: + VideoDecoder(GMPVideoHost *aHostAPI); + + virtual ~VideoDecoder(); + + virtual void InitDecode(const GMPVideoCodec& aCodecSettings, + const uint8_t* aCodecSpecific, + uint32_t aCodecSpecificLength, + GMPVideoDecoderCallback* aCallback, + int32_t aCoreCount) override; + + virtual void Decode(GMPVideoEncodedFrame* aInputFrame, + bool aMissingFrames, + const uint8_t* aCodecSpecific, + uint32_t aCodecSpecificLength, + int64_t aRenderTimeMs = -1); + + virtual void Reset() override; + + virtual void Drain() override; + + virtual void DecodingComplete() override; + +private: + + void EnsureWorker(); + + void DrainTask(); + + void DecodeTask(GMPVideoEncodedFrame* aInputFrame); + + void ReturnOutput(IMFSample* aSample); + + HRESULT SampleToVideoFrame(IMFSample* aSample, + GMPVideoi420Frame* aVideoFrame); + + GMPVideoHost *mHostAPI; // host-owned, invalid at DecodingComplete + GMPVideoDecoderCallback* mCallback; // host-owned, invalid at DecodingComplete + GMPThread* mWorkerThread; + GMPMutex* mMutex; + wmf::AutoPtr mDecoder; + + std::vector mExtraData; + std::vector mAnnexB; + + int32_t mNumInputTasks; + bool mSentExtraData; +}; + +#endif // __VideoDecoder_h__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/WMFAACDecoder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/WMFAACDecoder.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/WMFAACDecoder.cpp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/WMFAACDecoder.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -0,0 +1,367 @@ +/* + * 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. + */ + +#include "WMFAACDecoder.h" + +using std::vector; + +namespace wmf { + +WMFAACDecoder::WMFAACDecoder() + : mDecoder(nullptr) + , mChannels(0) + , mRate(0) +{ + memset(&mInputStreamInfo, 0, sizeof(MFT_INPUT_STREAM_INFO)); + memset(&mOutputStreamInfo, 0, sizeof(MFT_OUTPUT_STREAM_INFO)); +} + +WMFAACDecoder::~WMFAACDecoder() +{ + Reset(); +} + +HRESULT +AACAudioSpecificConfigToUserData(BYTE* aAudioSpecConfig, UINT32 aConfigLength, + BYTE** aOutUserData, UINT32* aOutUserDataLength) +{ + // For MFAudioFormat_AAC, MF_MT_USER_DATA + // Contains the portion of the HEAACWAVEINFO structure that appears + // after the WAVEFORMATEX structure (that is, after the wfx member). + // This is followed by the AudioSpecificConfig() data, as defined + // by ISO/IEC 14496-3. + // ... + // The length of the AudioSpecificConfig() data is 2 bytes for AAC-LC + // or HE-AAC with implicit signaling of SBR/PS. It is more than 2 bytes + // for HE-AAC with explicit signaling of SBR/PS. + // + // The value of audioObjectType as defined in AudioSpecificConfig() + // must be 2, indicating AAC-LC. The value of extensionAudioObjectType + // must be 5 for SBR or 29 for PS. + // + // See: + // http://msdn.microsoft.com/en-us/library/windows/desktop/dd742784%28v=vs.85%29.aspx + // + // HEAACWAVEINFO structure: + // typedef struct heaacwaveinfo_tag { + // WAVEFORMATEX wfx; + // WORD wPayloadType; + // WORD wAudioProfileLevelIndication; + // WORD wStructType; + // WORD wReserved1; + // DWORD dwReserved2; + // } + const UINT32 heeInfoLen = 4 * sizeof(WORD) + sizeof(DWORD); + BYTE heeInfo[heeInfoLen] = {0}; + WORD* w = (WORD*)heeInfo; + w[0] = 0x0; // Payload type raw AAC + w[1] = 0; // Profile level unspecified + + const UINT32 len = heeInfoLen + aConfigLength; + BYTE* data = new BYTE[len]; + memcpy(data, heeInfo, heeInfoLen); + memcpy(data+heeInfoLen, aAudioSpecConfig, aConfigLength); + *aOutUserData = data; + *aOutUserDataLength = len; + return S_OK; +} + +HRESULT +WMFAACDecoder::Init(int32_t aChannelCount, + int32_t aSampleRate, + BYTE* aAACAudioSpecificConfig, + UINT32 aAudioConfigLength) +{ + HRESULT hr; + + // AAC decoder is in msauddecmft on Win8, and msmpeg2adec in earlier versions. + hr = CreateMFT(CLSID_CMSAACDecMFT, + "msauddecmft.dll", + mDecoder); + if (FAILED(hr)) { + hr = CreateMFT(CLSID_CMSAACDecMFT, + "msmpeg2adec.dll", + mDecoder); + if (FAILED(hr)) { + LOG("Failed to create AAC decoder\n"); + return E_FAIL; + } + } + + BYTE* userData = nullptr; + UINT32 userDataLength; + hr = AACAudioSpecificConfigToUserData(aAACAudioSpecificConfig, + aAudioConfigLength, + &userData, + &userDataLength); + ENSURE(SUCCEEDED(hr), hr); + hr = SetDecoderInputType(aChannelCount, aSampleRate, userData, userDataLength); + delete userData; + ENSURE(SUCCEEDED(hr), hr); + + hr = SetDecoderOutputType(); + ENSURE(SUCCEEDED(hr), hr); + + hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0); + ENSURE(SUCCEEDED(hr), hr); + + hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0); + ENSURE(SUCCEEDED(hr), hr); + + hr = mDecoder->GetInputStreamInfo(0, &mInputStreamInfo); + ENSURE(SUCCEEDED(hr), hr); + + hr = mDecoder->GetOutputStreamInfo(0, &mOutputStreamInfo); + ENSURE(SUCCEEDED(hr), hr); + + return S_OK; +} + +HRESULT +WMFAACDecoder::SetDecoderInputType(int32_t aChannelCount, + int32_t aSampleRate, + BYTE* aUserData, + UINT32 aUserDataLength) +{ + HRESULT hr; + + CComPtr type; + hr = MFCreateMediaType(&type); + ENSURE(SUCCEEDED(hr), hr); + + hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); + ENSURE(SUCCEEDED(hr), hr); + + hr = type->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_AAC); + ENSURE(SUCCEEDED(hr), hr); + + mRate = aSampleRate; + hr = type->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, mRate); + ENSURE(SUCCEEDED(hr), hr); + + mChannels = aChannelCount; + hr = type->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, mChannels); + ENSURE(SUCCEEDED(hr), hr); + + hr = type->SetBlob(MF_MT_USER_DATA, aUserData, aUserDataLength); + ENSURE(SUCCEEDED(hr), hr); + + hr = mDecoder->SetInputType(0, type, 0); + ENSURE(SUCCEEDED(hr), hr); + + return S_OK; +} + +HRESULT +WMFAACDecoder::SetDecoderOutputType() +{ + HRESULT hr; + + CComPtr type; + + UINT32 typeIndex = 0; + while (type = nullptr, SUCCEEDED(mDecoder->GetOutputAvailableType(0, typeIndex++, &type))) { + GUID subtype; + hr = type->GetGUID(MF_MT_SUBTYPE, &subtype); + if (FAILED(hr)) { + continue; + } + if (subtype == MFAudioFormat_PCM) { + hr = mDecoder->SetOutputType(0, type, 0); + ENSURE(SUCCEEDED(hr), hr); + + hr = type->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &mChannels); + ENSURE(SUCCEEDED(hr), hr); + + hr = type->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &mRate); + ENSURE(SUCCEEDED(hr), hr); + + return S_OK; + } + } + + return E_FAIL; +} + +HRESULT +WMFAACDecoder::SendMFTMessage(MFT_MESSAGE_TYPE aMsg, UINT32 aData) +{ + ENSURE(mDecoder != nullptr, E_POINTER); + HRESULT hr = mDecoder->ProcessMessage(aMsg, aData); + ENSURE(SUCCEEDED(hr), hr); + return S_OK; +} + +HRESULT +WMFAACDecoder::CreateInputSample(const uint8_t* aData, + uint32_t aDataSize, + Microseconds aTimestamp, + IMFSample** aOutSample) +{ + HRESULT hr; + CComPtr sample = nullptr; + hr = MFCreateSample(&sample); + ENSURE(SUCCEEDED(hr), hr); + + CComPtr buffer = nullptr; + int32_t bufferSize = std::max(uint32_t(mInputStreamInfo.cbSize), aDataSize); + UINT32 alignment = (mInputStreamInfo.cbAlignment > 1) ? mInputStreamInfo.cbAlignment - 1 : 0; + hr = MFCreateAlignedMemoryBuffer(bufferSize, alignment, &buffer); + ENSURE(SUCCEEDED(hr), hr); + + DWORD maxLength = 0; + DWORD currentLength = 0; + BYTE* dst = nullptr; + hr = buffer->Lock(&dst, &maxLength, ¤tLength); + ENSURE(SUCCEEDED(hr), hr); + + // Copy data into sample's buffer. + memcpy(dst, aData, aDataSize); + + hr = buffer->Unlock(); + ENSURE(SUCCEEDED(hr), hr); + + hr = buffer->SetCurrentLength(aDataSize); + ENSURE(SUCCEEDED(hr), hr); + + hr = sample->AddBuffer(buffer); + ENSURE(SUCCEEDED(hr), hr); + + hr = sample->SetSampleTime(UsecsToHNs(aTimestamp)); + ENSURE(SUCCEEDED(hr), hr); + + *aOutSample = sample.Detach(); + + return S_OK; +} + +HRESULT +WMFAACDecoder::CreateOutputSample(IMFSample** aOutSample) +{ + HRESULT hr; + CComPtr sample = nullptr; + hr = MFCreateSample(&sample); + ENSURE(SUCCEEDED(hr), hr); + + CComPtr buffer = nullptr; + int32_t bufferSize = mOutputStreamInfo.cbSize; + UINT32 alignment = (mOutputStreamInfo.cbAlignment > 1) ? mOutputStreamInfo.cbAlignment - 1 : 0; + hr = MFCreateAlignedMemoryBuffer(bufferSize, alignment, &buffer); + ENSURE(SUCCEEDED(hr), hr); + + DWORD maxLength = 0; + DWORD currentLength = 0; + BYTE* dst = nullptr; + + hr = sample->AddBuffer(buffer); + ENSURE(SUCCEEDED(hr), hr); + + *aOutSample = sample.Detach(); + + return S_OK; +} + + +HRESULT +WMFAACDecoder::GetOutputSample(IMFSample** aOutSample) +{ + HRESULT hr; + // We allocate samples for MFT output. + MFT_OUTPUT_DATA_BUFFER output = {0}; + + CComPtr sample = nullptr; + hr = CreateOutputSample(&sample); + ENSURE(SUCCEEDED(hr), hr); + + output.pSample = sample; + + DWORD status = 0; + hr = mDecoder->ProcessOutput(0, 1, &output, &status); + CComPtr events = output.pEvents; // Ensure this is released. + + if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { + // Type change. Probably geometric apperature change. + hr = SetDecoderOutputType(); + ENSURE(SUCCEEDED(hr), hr); + + return GetOutputSample(aOutSample); + } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT || !sample) { + return MF_E_TRANSFORM_NEED_MORE_INPUT; + } + // Treat other errors as fatal. + ENSURE(SUCCEEDED(hr), hr); + + assert(sample); + + *aOutSample = sample.Detach(); + return S_OK; +} + +HRESULT +WMFAACDecoder::Input(const uint8_t* aData, + uint32_t aDataSize, + Microseconds aTimestamp) +{ + CComPtr input = nullptr; + HRESULT hr = CreateInputSample(aData, aDataSize, aTimestamp, &input); + ENSURE(SUCCEEDED(hr) && input!=nullptr, hr); + + hr = mDecoder->ProcessInput(0, input, 0); + if (hr == MF_E_NOTACCEPTING) { + // MFT *already* has enough data to produce a sample. Retrieve it. + LOG("ProcessInput returned MF_E_NOTACCEPTING\n"); + return MF_E_NOTACCEPTING; + } + ENSURE(SUCCEEDED(hr), hr); + + return S_OK; +} + +HRESULT +WMFAACDecoder::Output(IMFSample** aOutput) +{ + CComPtr outputSample = nullptr; + HRESULT hr = GetOutputSample(&outputSample); + if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { + return MF_E_TRANSFORM_NEED_MORE_INPUT; + } + // Treat other errors as fatal. + ENSURE(SUCCEEDED(hr) && outputSample, hr); + + *aOutput = outputSample.Detach(); + + return S_OK; +} + +HRESULT +WMFAACDecoder::Reset() +{ + HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_FLUSH, 0); + ENSURE(SUCCEEDED(hr), hr); + + return S_OK; +} + +HRESULT +WMFAACDecoder::Drain() +{ + HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0); + ENSURE(SUCCEEDED(hr), hr); + + return S_OK; +} + +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/WMFAACDecoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/WMFAACDecoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/WMFAACDecoder.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/WMFAACDecoder.h 2015-02-03 14:33:27.000000000 +0000 @@ -0,0 +1,74 @@ +/* + * 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. + */ + +#if !defined(WMFAACDecoder_h_) +#define WMFAACDecoder_h_ + +#include "WMFUtils.h" + +namespace wmf { + +class WMFAACDecoder { +public: + WMFAACDecoder(); + ~WMFAACDecoder(); + + HRESULT Init(int32_t aChannelCount, + int32_t aSampleRate, + BYTE* aUserData, + UINT32 aUserDataLength); + + HRESULT Input(const uint8_t* aData, + uint32_t aDataSize, + Microseconds aTimestamp); + + HRESULT Output(IMFSample** aOutput); + + HRESULT Reset(); + + HRESULT Drain(); + + UINT32 Channels() const { return mChannels; } + UINT32 Rate() const { return mRate; } + +private: + + HRESULT GetOutputSample(IMFSample** aOutSample); + HRESULT CreateOutputSample(IMFSample** aOutSample); + HRESULT CreateInputSample(const uint8_t* aData, + uint32_t aDataSize, + Microseconds aTimestamp, + IMFSample** aOutSample); + + HRESULT SetDecoderInputType(int32_t aChannelCount, + int32_t aSampleRate, + BYTE* aUserData, + UINT32 aUserDataLength); + HRESULT SetDecoderOutputType(); + HRESULT SendMFTMessage(MFT_MESSAGE_TYPE aMsg, UINT32 aData); + + MFT_INPUT_STREAM_INFO mInputStreamInfo; + MFT_OUTPUT_STREAM_INFO mOutputStreamInfo; + + CComPtr mDecoder; + + UINT32 mChannels; + UINT32 mRate; +}; + +} + +#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/WMFH264Decoder.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/WMFH264Decoder.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/WMFH264Decoder.cpp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/WMFH264Decoder.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -0,0 +1,343 @@ +/* + * 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. + */ + +#include "WMFH264Decoder.h" + +namespace wmf { + +WMFH264Decoder::WMFH264Decoder() + : mDecoder(nullptr) +{ + memset(&mInputStreamInfo, 0, sizeof(MFT_INPUT_STREAM_INFO)); + memset(&mOutputStreamInfo, 0, sizeof(MFT_OUTPUT_STREAM_INFO)); +} + +WMFH264Decoder::~WMFH264Decoder() +{ +} + +HRESULT +WMFH264Decoder::Init() +{ + HRESULT hr; + + hr = CreateMFT(__uuidof(CMSH264DecoderMFT), + "msmpeg2vdec.dll", + mDecoder); + ENSURE(SUCCEEDED(hr), hr); + + hr = SetDecoderInputType(); + ENSURE(SUCCEEDED(hr), hr); + + hr = SetDecoderOutputType(); + ENSURE(SUCCEEDED(hr), hr); + + hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0); + ENSURE(SUCCEEDED(hr), hr); + + hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0); + ENSURE(SUCCEEDED(hr), hr); + + hr = mDecoder->GetInputStreamInfo(0, &mInputStreamInfo); + ENSURE(SUCCEEDED(hr), hr); + + hr = mDecoder->GetOutputStreamInfo(0, &mOutputStreamInfo); + ENSURE(SUCCEEDED(hr), hr); + + return S_OK; +} + +HRESULT +WMFH264Decoder::ConfigureVideoFrameGeometry(IMFMediaType* aMediaType) +{ + ENSURE(aMediaType != nullptr, E_POINTER); + HRESULT hr; + + IntRect pictureRegion; + hr = wmf::GetPictureRegion(aMediaType, pictureRegion); + ENSURE(SUCCEEDED(hr), hr); + + UINT32 width = 0, height = 0; + hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height); + ENSURE(SUCCEEDED(hr), hr); + + // Success! Save state. + GetDefaultStride(aMediaType, (UINT32*)&mStride); + mVideoWidth = width; + mVideoHeight = height; + mPictureRegion = pictureRegion; + + LOG("WMFH264Decoder frame geometry frame=(%u,%u) stride=%u picture=(%d, %d, %d, %d)\n", + width, height, + mStride, + mPictureRegion.x, mPictureRegion.y, mPictureRegion.width, mPictureRegion.height); + + return S_OK; +} + +int32_t +WMFH264Decoder::GetFrameWidth() const +{ + return mVideoWidth; +} + +int32_t +WMFH264Decoder::GetFrameHeight() const +{ + return mVideoHeight; +} + +const IntRect& +WMFH264Decoder::GetPictureRegion() const +{ + return mPictureRegion; +} + +int32_t +WMFH264Decoder::GetStride() const +{ + return mStride; +} + +HRESULT +WMFH264Decoder::SetDecoderInputType() +{ + HRESULT hr; + + CComPtr type; + hr = MFCreateMediaType(&type); + ENSURE(SUCCEEDED(hr), hr); + + hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); + ENSURE(SUCCEEDED(hr), hr); + + hr = type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264); + ENSURE(SUCCEEDED(hr), hr); + + hr = type->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_MixedInterlaceOrProgressive); + ENSURE(SUCCEEDED(hr), hr); + + hr = mDecoder->SetInputType(0, type, 0); + ENSURE(SUCCEEDED(hr), hr); + + return S_OK; +} + +HRESULT +WMFH264Decoder::SetDecoderOutputType() +{ + HRESULT hr; + + CComPtr type; + + UINT32 typeIndex = 0; + while (type = nullptr, SUCCEEDED(mDecoder->GetOutputAvailableType(0, typeIndex++, &type))) { + GUID subtype; + hr = type->GetGUID(MF_MT_SUBTYPE, &subtype); + if (FAILED(hr)) { + continue; + } + if (subtype == MFVideoFormat_I420) { + hr = mDecoder->SetOutputType(0, type, 0); + ENSURE(SUCCEEDED(hr), hr); + + hr = ConfigureVideoFrameGeometry(type); + ENSURE(SUCCEEDED(hr), hr); + + return S_OK; + } + } + + return E_FAIL; +} + +HRESULT +WMFH264Decoder::SendMFTMessage(MFT_MESSAGE_TYPE aMsg, UINT32 aData) +{ + ENSURE(mDecoder != nullptr, E_POINTER); + HRESULT hr = mDecoder->ProcessMessage(aMsg, aData); + ENSURE(SUCCEEDED(hr), hr); + return S_OK; +} + +HRESULT +WMFH264Decoder::CreateInputSample(const uint8_t* aData, + uint32_t aDataSize, + Microseconds aTimestamp, + Microseconds aDuration, + IMFSample** aOutSample) +{ + HRESULT hr; + CComPtr sample; + hr = MFCreateSample(&sample); + ENSURE(SUCCEEDED(hr), hr); + + CComPtr buffer; + int32_t bufferSize = std::max(uint32_t(mInputStreamInfo.cbSize), aDataSize); + UINT32 alignment = (mInputStreamInfo.cbAlignment > 1) ? mInputStreamInfo.cbAlignment - 1 : 0; + hr = MFCreateAlignedMemoryBuffer(bufferSize, alignment, &buffer); + ENSURE(SUCCEEDED(hr), hr); + + DWORD maxLength = 0; + DWORD currentLength = 0; + BYTE* dst = nullptr; + hr = buffer->Lock(&dst, &maxLength, ¤tLength); + ENSURE(SUCCEEDED(hr), hr); + + // Copy data into sample's buffer. + memcpy(dst, aData, aDataSize); + + hr = buffer->Unlock(); + ENSURE(SUCCEEDED(hr), hr); + + hr = buffer->SetCurrentLength(aDataSize); + ENSURE(SUCCEEDED(hr), hr); + + hr = sample->AddBuffer(buffer); + ENSURE(SUCCEEDED(hr), hr); + + hr = sample->SetSampleTime(UsecsToHNs(aTimestamp)); + ENSURE(SUCCEEDED(hr), hr); + + sample->SetSampleDuration(UsecsToHNs(aDuration)); + + *aOutSample = sample.Detach(); + + return S_OK; +} + +HRESULT +WMFH264Decoder::CreateOutputSample(IMFSample** aOutSample) +{ + HRESULT hr; + CComPtr sample; + hr = MFCreateSample(&sample); + ENSURE(SUCCEEDED(hr), hr); + + CComPtr buffer; + int32_t bufferSize = mOutputStreamInfo.cbSize; + UINT32 alignment = (mOutputStreamInfo.cbAlignment > 1) ? mOutputStreamInfo.cbAlignment - 1 : 0; + hr = MFCreateAlignedMemoryBuffer(bufferSize, alignment, &buffer); + ENSURE(SUCCEEDED(hr), hr); + + DWORD maxLength = 0; + DWORD currentLength = 0; + BYTE* dst = nullptr; + + hr = sample->AddBuffer(buffer); + ENSURE(SUCCEEDED(hr), hr); + + *aOutSample = sample.Detach(); + + return S_OK; +} + + +HRESULT +WMFH264Decoder::GetOutputSample(IMFSample** aOutSample) +{ + HRESULT hr; + // We allocate samples for MFT output. + MFT_OUTPUT_DATA_BUFFER output = {0}; + + CComPtr sample; + hr = CreateOutputSample(&sample); + ENSURE(SUCCEEDED(hr), hr); + + output.pSample = sample; + + DWORD status = 0; + hr = mDecoder->ProcessOutput(0, 1, &output, &status); + //LOG(L"WMFH264Decoder::GetOutputSample() ProcessOutput returned 0x%x\n", hr); + CComPtr events = output.pEvents; // Ensure this is released. + + if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { + // Type change. Probably geometric apperature change. + hr = SetDecoderOutputType(); + ENSURE(SUCCEEDED(hr), hr); + + return GetOutputSample(aOutSample); + } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { + return MF_E_TRANSFORM_NEED_MORE_INPUT; + } + // Treat other errors as fatal. + ENSURE(SUCCEEDED(hr), hr); + + assert(sample); + + // output.pSample + *aOutSample = sample.Detach(); // AddRefs + return S_OK; +} + +HRESULT +WMFH264Decoder::Input(const uint8_t* aData, + uint32_t aDataSize, + Microseconds aTimestamp, + Microseconds aDuration) +{ + HRESULT hr; + CComPtr input = nullptr; + hr = CreateInputSample(aData, aDataSize, aTimestamp, aDuration, &input); + ENSURE(SUCCEEDED(hr) && input!=nullptr, hr); + + hr = mDecoder->ProcessInput(0, input, 0); + if (hr == MF_E_NOTACCEPTING) { + // MFT *already* has enough data to produce a sample. Retrieve it. + LOG("ProcessInput returned MF_E_NOTACCEPTING\n"); + return MF_E_NOTACCEPTING; + } + ENSURE(SUCCEEDED(hr), hr); + + return S_OK; +} + +HRESULT +WMFH264Decoder::Output(IMFSample** aOutput) +{ + HRESULT hr; + CComPtr outputSample; + hr = GetOutputSample(&outputSample); + if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { + return MF_E_TRANSFORM_NEED_MORE_INPUT; + } + // Treat other errors as fatal. + ENSURE(SUCCEEDED(hr) && outputSample, hr); + + *aOutput = outputSample.Detach(); + + return S_OK; +} + +HRESULT +WMFH264Decoder::Reset() +{ + HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_FLUSH, 0); + ENSURE(SUCCEEDED(hr), hr); + + return S_OK; +} + +HRESULT +WMFH264Decoder::Drain() +{ + HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0); + ENSURE(SUCCEEDED(hr), hr); + + return S_OK; +} + +} // namespace wmf diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/WMFH264Decoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/WMFH264Decoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/WMFH264Decoder.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/WMFH264Decoder.h 2015-02-03 14:33:27.000000000 +0000 @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#if !defined(WMFH264Decoder_h_) +#define WMFH264Decoder_h_ + +#include "WMFUtils.h" + +namespace wmf { + +class WMFH264Decoder { +public: + WMFH264Decoder(); + ~WMFH264Decoder(); + + HRESULT Init(); + + HRESULT Input(const uint8_t* aData, + uint32_t aDataSize, + Microseconds aTimestamp, + Microseconds aDuration); + + HRESULT Output(IMFSample** aOutput); + + HRESULT Reset(); + + int32_t GetFrameWidth() const; + int32_t GetFrameHeight() const; + const IntRect& GetPictureRegion() const; + int32_t GetStride() const; + + HRESULT Drain(); + +private: + + HRESULT SetDecoderInputType(); + HRESULT SetDecoderOutputType(); + HRESULT SendMFTMessage(MFT_MESSAGE_TYPE aMsg, UINT32 aData); + + HRESULT CreateInputSample(const uint8_t* aData, + uint32_t aDataSize, + Microseconds aTimestamp, + Microseconds aDuration, + IMFSample** aOutSample); + + HRESULT CreateOutputSample(IMFSample** aOutSample); + + HRESULT GetOutputSample(IMFSample** aOutSample); + HRESULT ConfigureVideoFrameGeometry(IMFMediaType* aMediaType); + + MFT_INPUT_STREAM_INFO mInputStreamInfo; + MFT_OUTPUT_STREAM_INFO mOutputStreamInfo; + + CComPtr mDecoder; + + int32_t mVideoWidth; + int32_t mVideoHeight; + IntRect mPictureRegion; + int32_t mStride; + +}; + +} // namespace wmf + +#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/WMFSymbols.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/WMFSymbols.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/WMFSymbols.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/WMFSymbols.h 2015-02-03 14:33:27.000000000 +0000 @@ -0,0 +1,8 @@ +/* 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/. */ + +MFPLAT_FUNC(MFCreateSample); +MFPLAT_FUNC(MFCreateAlignedMemoryBuffer); +MFPLAT_FUNC(MFGetStrideForBitmapInfoHeader); +MFPLAT_FUNC(MFCreateMediaType); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/WMFUtils.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/WMFUtils.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/WMFUtils.cpp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/WMFUtils.cpp 2015-02-03 14:33:27.000000000 +0000 @@ -0,0 +1,223 @@ +/* + * 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. + */ + +#include "WMFUtils.h" + +#include + +#define INITGUID +#include + +#pragma comment(lib, "mfuuid.lib") +#pragma comment(lib, "wmcodecdspuuid") +#pragma comment(lib, "mfplat.lib") + +void LOG(const char* format, ...) +{ +#ifdef WMF_DECODER_LOG + va_list args; + va_start(args, format); + vprintf(format, args); +#endif +} + +#ifdef WMF_MUST_DEFINE_AAC_MFT_CLSID +// Some SDK versions don't define the AAC decoder CLSID. +// {32D186A7-218F-4C75-8876-DD77273A8999} +DEFINE_GUID(CLSID_CMSAACDecMFT, 0x32D186A7, 0x218F, 0x4C75, 0x88, 0x76, 0xDD, 0x77, 0x27, 0x3A, 0x89, 0x99); +#endif + +namespace wmf { + + +#define MFPLAT_FUNC(_func) \ + decltype(::_func)* _func; +#include "WMFSymbols.h" +#undef MFPLAT_FUNC + +static bool +LinkMfplat() +{ + static bool sInitDone = false; + static bool sInitOk = false; + if (!sInitDone) { + sInitDone = true; + auto handle = GetModuleHandle("mfplat.dll"); +#define MFPLAT_FUNC(_func) \ + if (!(_func = (decltype(_func))(GetProcAddress(handle, #_func)))) { \ + return false; \ + } +#include "WMFSymbols.h" +#undef MFPLAT_FUNC + sInitOk = true; + } + return sInitOk; +} + +bool +EnsureLibs() +{ + static bool sInitDone = false; + static bool sInitOk = false; + if (!sInitDone) { + sInitOk = LinkMfplat() && + !!GetModuleHandle("msauddecmft.dll") && + !!GetModuleHandle("msmpeg2adec.dll") && + !!GetModuleHandle("msmpeg2vdec.dll"); + sInitDone = true; + } + return sInitOk; +} + +int32_t +MFOffsetToInt32(const MFOffset& aOffset) +{ + return int32_t(aOffset.value + (aOffset.fract / 65536.0f)); +} + +// Gets the sub-region of the video frame that should be displayed. +// See: http://msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx +HRESULT +GetPictureRegion(IMFMediaType* aMediaType, IntRect& aOutPictureRegion) +{ + // Determine if "pan and scan" is enabled for this media. If it is, we + // only display a region of the video frame, not the entire frame. + BOOL panScan = MFGetAttributeUINT32(aMediaType, MF_MT_PAN_SCAN_ENABLED, FALSE); + + // If pan and scan mode is enabled. Try to get the display region. + HRESULT hr = E_FAIL; + MFVideoArea videoArea; + memset(&videoArea, 0, sizeof(MFVideoArea)); + if (panScan) { + hr = aMediaType->GetBlob(MF_MT_PAN_SCAN_APERTURE, + (UINT8*)&videoArea, + sizeof(MFVideoArea), + NULL); + } + + // If we're not in pan-and-scan mode, or the pan-and-scan region is not set, + // check for a minimimum display aperture. + if (!panScan || hr == MF_E_ATTRIBUTENOTFOUND) { + hr = aMediaType->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, + (UINT8*)&videoArea, + sizeof(MFVideoArea), + NULL); + } + + if (hr == MF_E_ATTRIBUTENOTFOUND) { + // Minimum display aperture is not set, for "backward compatibility with + // some components", check for a geometric aperture. + hr = aMediaType->GetBlob(MF_MT_GEOMETRIC_APERTURE, + (UINT8*)&videoArea, + sizeof(MFVideoArea), + NULL); + } + + if (SUCCEEDED(hr)) { + // The media specified a picture region, return it. + aOutPictureRegion = IntRect(MFOffsetToInt32(videoArea.OffsetX), + MFOffsetToInt32(videoArea.OffsetY), + videoArea.Area.cx, + videoArea.Area.cy); + return S_OK; + } + + // No picture region defined, fall back to using the entire video area. + UINT32 width = 0, height = 0; + hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height); + ENSURE(SUCCEEDED(hr), hr); + aOutPictureRegion = IntRect(0, 0, width, height); + return S_OK; +} + + +HRESULT +GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride) +{ + // Try to get the default stride from the media type. + HRESULT hr = aType->GetUINT32(MF_MT_DEFAULT_STRIDE, aOutStride); + if (SUCCEEDED(hr)) { + return S_OK; + } + + // Stride attribute not set, calculate it. + GUID subtype = GUID_NULL; + uint32_t width = 0; + uint32_t height = 0; + + hr = aType->GetGUID(MF_MT_SUBTYPE, &subtype); + ENSURE(SUCCEEDED(hr), hr); + + hr = MFGetAttributeSize(aType, MF_MT_FRAME_SIZE, &width, &height); + ENSURE(SUCCEEDED(hr), hr); + + hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, (LONG*)(aOutStride)); + ENSURE(SUCCEEDED(hr), hr); + + return hr; +} + +void dump(const uint8_t* data, uint32_t len, const char* filename) +{ + FILE* f = 0; + fopen_s(&f, filename, "wb"); + fwrite(data, len, 1, f); + fclose(f); +} + +HRESULT +CreateMFT(const CLSID& clsid, + const char* aDllName, + CComPtr& aOutMFT) +{ + HMODULE module = ::GetModuleHandle(aDllName); + if (!module) { + LOG("Failed to get %S\n", aDllName); + return E_FAIL; + } + + typedef HRESULT (WINAPI* DllGetClassObjectFnPtr)(const CLSID& clsid, + const IID& iid, + void** object); + + DllGetClassObjectFnPtr GetClassObjPtr = + reinterpret_cast(GetProcAddress(module, "DllGetClassObject")); + if (!GetClassObjPtr) { + LOG("Failed to get DllGetClassObject\n"); + return E_FAIL; + } + + CComPtr classFactory; + HRESULT hr = GetClassObjPtr(clsid, + __uuidof(IClassFactory), + reinterpret_cast(static_cast(&classFactory))); + if (FAILED(hr)) { + LOG("Failed to get H264 IClassFactory\n"); + return E_FAIL; + } + + hr = classFactory->CreateInstance(NULL, + __uuidof(IMFTransform), + reinterpret_cast(static_cast(&aOutMFT))); + if (FAILED(hr)) { + LOG("Failed to get create MFT\n"); + return E_FAIL; + } + + return S_OK; +} + +} // namespace diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/WMFUtils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/WMFUtils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/gmp-clearkey/0.1/WMFUtils.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/gmp-clearkey/0.1/WMFUtils.h 2015-02-03 14:33:27.000000000 +0000 @@ -0,0 +1,247 @@ +/* + * 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 __WMFUtils_h__ +#define __WMFUtils_h__ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "gmp-platform.h" + +void LOG(const char* format, ...); + +#ifdef LOG_SAMPLE_DECODE +#define SAMPLE_LOG LOG +#else +#define SAMPLE_LOG(...) +#endif + +#ifndef CLSID_CMSAACDecMFT +#define WMF_MUST_DEFINE_AAC_MFT_CLSID +extern "C" const CLSID CLSID_CMSAACDecMFT; +#endif + +namespace wmf { + +// Reimplementation of CComPtr to reduce dependence on system +// shared libraries. +template +class CComPtr { +public: + CComPtr() : mPtr(nullptr) { } + CComPtr(T* const & aPtr) : mPtr(aPtr) { } + CComPtr(const nullptr_t& aNullPtr) : mPtr(aNullPtr) { } + T** operator&() { return &mPtr; } + T* operator->(){ return mPtr; } + operator T*() { return mPtr; } + T* operator=(T* const & aPtr) { return mPtr = aPtr; } + T* operator=(const nullptr_t& aPtr) { return mPtr = aPtr; } + + T* Detach() { + T* tmp = mPtr; + mPtr = nullptr; + return tmp; + } + + ~CComPtr() { + if (mPtr) { + mPtr->Release(); + } + mPtr = nullptr; + } + +private: + T* mPtr; +}; + +class IntRect { +public: + IntRect(int32_t _x, int32_t _y, int32_t _w, int32_t _h) + : x(_x), y(_y), width(_w), height(_h) {} + IntRect() + : x(0), y(0), width(0), height(0) {} + int32_t x; + int32_t y; + int32_t width; + int32_t height; +}; + +typedef int64_t Microseconds; + +#ifdef ENSURE +#undef ENSURE +#endif + +#define ENSURE(condition, ret) \ +{ if (!(condition)) { LOG("##condition## FAILED %S:%d\n", __FILE__, __LINE__); return ret; } } + +#define GMP_SUCCEEDED(x) ((x) == GMPNoErr) +#define GMP_FAILED(x) ((x) != GMPNoErr) + +#define MFPLAT_FUNC(_func) \ + extern decltype(::_func)* _func; +#include "WMFSymbols.h" +#undef MFPLAT_FUNC + +bool +EnsureLibs(); + +HRESULT +GetPictureRegion(IMFMediaType* aMediaType, IntRect& aOutPictureRegion); + +HRESULT +GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride); + +// Converts from microseconds to hundreds of nanoseconds. +// We use microseconds for our timestamps, whereas WMF uses +// hundreds of nanoseconds. +inline int64_t +UsecsToHNs(int64_t aUsecs) { + return aUsecs * 10; +} + +// Converts from hundreds of nanoseconds to microseconds. +// We use microseconds for our timestamps, whereas WMF uses +// hundreds of nanoseconds. +inline int64_t +HNsToUsecs(int64_t hNanoSecs) { + return hNanoSecs / 10; +} + +inline std::string narrow(std::wstring &wide) { + std::string ns(wide.begin(), wide.end()); + return ns; +} + +inline std::wstring widen(std::string &narrow) { + std::wstring ws(narrow.begin(), narrow.end()); + return ws; +} + +#define ARRAY_LENGTH(array_) \ + (sizeof(array_)/sizeof(array_[0])) + +template +class AutoPtr { +public: + AutoPtr() + : mPtr(nullptr) + { + } + + AutoPtr(AutoPtr& aPtr) + : mPtr(aPtr.Forget()) + { + } + + AutoPtr(Type* aPtr) + : mPtr(aPtr) + { + } + + ~AutoPtr() { + if (mPtr) { + delete mPtr; + } + } + + Type* Forget() { + Type* rv = mPtr; + mPtr = nullptr; + return rv; + } + + AutoPtr& operator=(Type* aOther) { + Assign(aOther); + return *this; + } + + AutoPtr& operator=(AutoPtr& aOther) { + Assign(aOther.Forget()); + return *this; + } + + Type* Get() const { + return mPtr; + } + + Type* operator->() const { + assert(mPtr); + return Get(); + } + + operator Type*() const { + return Get(); + } + + Type** Receive() { + return &mPtr; + } +private: + + void Assign(Type* aPtr) { + if (mPtr) { + delete mPtr; + } + mPtr = aPtr; + } + + Type* mPtr; +}; + +// Video frame microseconds are (currently) in 90kHz units, as used by RTP. +// Use this to convert to microseconds... +inline Microseconds RTPTimeToMicroseconds(int64_t rtptime) { + return (rtptime * 1000000) / 90000; +} + +inline uint32_t MicrosecondsToRTPTime(Microseconds us) { + return uint32_t(0xffffffff & (us * 90000) / 1000000); +} + +class AutoLock { +public: + AutoLock(GMPMutex* aMutex) + : mMutex(aMutex) + { + assert(aMutex); + mMutex->Acquire(); + } + ~AutoLock() { + mMutex->Release(); + } +private: + GMPMutex* mMutex; +}; + +void dump(const uint8_t* data, uint32_t len, const char* filename); + +HRESULT +CreateMFT(const CLSID& clsid, + const char* aDllName, + CComPtr& aOutMFT); + +} // namespace wmf + +#endif // __WMFUtils_h__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libopus/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libopus/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libopus/moz.build 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libopus/moz.build 2015-02-03 14:33:28.000000000 +0000 @@ -82,6 +82,8 @@ 'silk/fixed', ] SOURCES += silk_sources_fixed +# for webrtc + SOURCES += opus_sources_float if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_AS']: SOURCES += celt_sources_arm diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/Box.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/Box.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/Box.cpp 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/Box.cpp 2015-02-03 14:33:28.000000000 +0000 @@ -12,6 +12,29 @@ namespace mp4_demuxer { +// Returns the offset from the start of the body of a box of type |aType| +// to the start of its first child. +static uint32_t +BoxOffset(AtomType aType) +{ + const uint32_t FULLBOX_OFFSET = 4; + + if (aType == AtomType("mp4a") || aType == AtomType("enca")) { + // AudioSampleEntry; ISO 14496-12, section 8.16 + return 28; + } else if (aType == AtomType("mp4v") || aType == AtomType("encv")) { + // VideoSampleEntry; ISO 14496-12, section 8.16 + return 78; + } else if (aType == AtomType("stsd")) { + // SampleDescriptionBox; ISO 14496-12, section 8.16 + // This is a FullBox, and contains a |count| member before its child + // boxes. + return FULLBOX_OFFSET + 4; + } + + return 0; +} + Box::Box(BoxContext* aContext, uint64_t aOffset, const Box* aParent) : mContext(aContext), mParent(aParent) { @@ -53,11 +76,14 @@ return; } size = BigEndian::readUint64(bigLength); - mChildOffset = bigLengthRange.mEnd; + mBodyOffset = bigLengthRange.mEnd; } else { - mChildOffset = headerRange.mEnd; + mBodyOffset = headerRange.mEnd; } + mType = BigEndian::readUint32(&header[4]); + mChildOffset = mBodyOffset + BoxOffset(mType); + MediaByteRange boxRange(aOffset, aOffset + size); if (mChildOffset > boxRange.mEnd || (mParent && !mParent->mRange.Contains(boxRange)) || @@ -65,7 +91,6 @@ return; } mRange = boxRange; - mType = BigEndian::readUint32(&header[4]); } Box::Box() diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/include/mp4_demuxer/Atom.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/include/mp4_demuxer/Atom.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/include/mp4_demuxer/Atom.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/include/mp4_demuxer/Atom.h 2015-02-03 14:33:28.000000000 +0000 @@ -0,0 +1,27 @@ +/* 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 ATOM_H_ +#define ATOM_H_ + +namespace mp4_demuxer { + +class Atom +{ +public: + Atom() + : mValid(false) + { + } + virtual bool IsValid() + { + return mValid; + } +protected: + bool mValid; +}; + +} + +#endif // ATOM_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/include/mp4_demuxer/AtomType.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/include/mp4_demuxer/AtomType.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/include/mp4_demuxer/AtomType.h 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/include/mp4_demuxer/AtomType.h 2015-02-03 14:33:28.000000000 +0000 @@ -21,6 +21,7 @@ MOZ_IMPLICIT AtomType(uint32_t aType) : mType(aType) { } MOZ_IMPLICIT AtomType(const char* aType) : mType(BigEndian::readUint32(aType)) { } bool operator==(const AtomType& aType) const { return mType == aType.mType; } + bool operator!() const { return !mType; } private: uint32_t mType; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/include/mp4_demuxer/Box.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/include/mp4_demuxer/Box.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/include/mp4_demuxer/Box.h 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/include/mp4_demuxer/Box.h 2015-02-03 14:33:28.000000000 +0000 @@ -54,6 +54,7 @@ bool Contains(MediaByteRange aRange) const; BoxContext* mContext; mozilla::MediaByteRange mRange; + uint64_t mBodyOffset; uint64_t mChildOffset; AtomType mType; const Box* mParent; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h 2015-02-03 14:33:28.000000000 +0000 @@ -5,8 +5,10 @@ #ifndef MOOF_PARSER_H_ #define MOOF_PARSER_H_ +#include "mp4_demuxer/Atom.h" #include "mp4_demuxer/AtomType.h" #include "mp4_demuxer/mp4_demuxer.h" +#include "mp4_demuxer/SinfParser.h" #include "MediaResource.h" namespace mp4_demuxer { @@ -16,21 +18,6 @@ class BoxContext; class Moof; -class Atom -{ -public: - Atom() - : mValid(false) - { - } - virtual bool IsValid() - { - return mValid; - } -protected: - bool mValid; -}; - class Tkhd : public Atom { public: @@ -150,7 +137,7 @@ class Saiz : public Atom { public: - explicit Saiz(Box& aBox); + Saiz(Box& aBox, AtomType aDefaultType); AtomType mAuxInfoType; uint32_t mAuxInfoTypeParameter; @@ -160,7 +147,7 @@ class Saio : public Atom { public: - explicit Saio(Box& aBox); + Saio(Box& aBox, AtomType aDefaultType); AtomType mAuxInfoType; uint32_t mAuxInfoTypeParameter; @@ -181,7 +168,7 @@ class Moof : public Atom { public: - Moof(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts, Microseconds aTimestampOffset); + Moof(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf, Microseconds aTimestampOffset); bool GetAuxInfo(AtomType aType, nsTArray* aByteRanges); void FixRounding(const Moof& aMoof); @@ -194,7 +181,7 @@ nsTArray mSaios; private: - void ParseTraf(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts); + void ParseTraf(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf); void ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt, Mdhd& aMdhd, Edts& aEdts); void ParseSaiz(Box& aBox); void ParseSaio(Box& aBox); @@ -225,6 +212,12 @@ void ParseMdia(Box& aBox, Tkhd& aTkhd); void ParseMvex(Box& aBox); + void ParseMinf(Box& aBox); + void ParseStbl(Box& aBox); + void ParseStsd(Box& aBox); + void ParseEncrypted(Box& aBox); + void ParseSinf(Box& aBox); + bool BlockingReadNextMoof(); mozilla::MediaByteRange mInitRange; @@ -236,6 +229,7 @@ Trex mTrex; Tfdt mTfdt; Edts mEdts; + Sinf mSinf; Monitor* mMonitor; nsTArray& Moofs() { mMonitor->AssertCurrentThreadOwns(); return mMoofs; } private: diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/include/mp4_demuxer/SinfParser.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/include/mp4_demuxer/SinfParser.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/include/mp4_demuxer/SinfParser.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/include/mp4_demuxer/SinfParser.h 2015-02-03 14:33:28.000000000 +0000 @@ -0,0 +1,51 @@ +/* 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 SINF_PARSER_H_ +#define SINF_PARSER_H_ + +#include "mp4_demuxer/Atom.h" +#include "mp4_demuxer/AtomType.h" + +namespace mp4_demuxer { + +class Box; + +class Sinf : public Atom +{ +public: + Sinf() + : mDefaultIVSize(0) + , mDefaultEncryptionType() + {} + explicit Sinf(Box& aBox); + + virtual bool IsValid() MOZ_OVERRIDE + { + return !!mDefaultIVSize && !!mDefaultEncryptionType; + } + + uint8_t mDefaultIVSize; + AtomType mDefaultEncryptionType; + uint8_t mDefaultKeyID[16]; +}; + +class SinfParser +{ +public: + explicit SinfParser(Box& aBox); + + Sinf& GetSinf() { return mSinf; } +private: + void ParseSchm(Box& aBox); + void ParseSchi(Box& aBox); + void ParseTenc(Box& aBox); + + Sinf mSinf; +}; + +} + +#endif // SINF_PARSER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/Index.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/Index.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/Index.cpp 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/Index.cpp 2015-02-03 14:33:28.000000000 +0000 @@ -6,6 +6,7 @@ #include "mp4_demuxer/Index.h" #include "mp4_demuxer/Interval.h" #include "mp4_demuxer/MoofParser.h" +#include "mp4_demuxer/SinfParser.h" #include "media/stagefright/MediaSource.h" #include "MediaResource.h" @@ -108,24 +109,40 @@ } if (!s->mCencRange.IsNull()) { + MoofParser* parser = mIndex->mMoofParser.get(); + + if (!parser || !parser->mSinf.IsValid()) { + return nullptr; + } + + uint8_t ivSize = parser->mSinf.mDefaultIVSize; + // The size comes from an 8 bit field nsAutoTArray cenc; cenc.SetLength(s->mCencRange.Length()); - if (!mIndex->mSource->ReadAt(s->mCencRange.mStart, &cenc[0], cenc.Length(), + if (!mIndex->mSource->ReadAt(s->mCencRange.mStart, cenc.Elements(), cenc.Length(), &bytesRead) || bytesRead != cenc.Length()) { return nullptr; } ByteReader reader(cenc); sample->crypto.valid = true; - reader.ReadArray(sample->crypto.iv, 16); - if (reader.Remaining()) { + sample->crypto.iv_size = ivSize; + + if (!reader.ReadArray(sample->crypto.iv, ivSize)) { + return nullptr; + } + + if (reader.CanRead16()) { uint16_t count = reader.ReadU16(); + + if (reader.Remaining() < count * 6) { + return nullptr; + } + for (size_t i = 0; i < count; i++) { sample->crypto.plain_sizes.AppendElement(reader.ReadU16()); sample->crypto.encrypted_sizes.AppendElement(reader.ReadU32()); } - reader.ReadArray(sample->crypto.iv, 16); - sample->crypto.iv_size = 16; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/MoofParser.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/MoofParser.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/MoofParser.cpp 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/MoofParser.cpp 2015-02-03 14:33:28.000000000 +0000 @@ -4,6 +4,7 @@ #include "mp4_demuxer/MoofParser.h" #include "mp4_demuxer/Box.h" +#include "mp4_demuxer/SinfParser.h" #include namespace mp4_demuxer @@ -28,7 +29,7 @@ mInitRange = MediaByteRange(0, box.Range().mEnd); ParseMoov(box); } else if (box.IsType("moof")) { - Moof moof(box, mTrex, mMdhd, mEdts, mTimestampOffset); + Moof moof(box, mTrex, mMdhd, mEdts, mSinf, mTimestampOffset); if (!mMoofs.IsEmpty()) { // Stitch time ranges together in the case of a (hopefully small) time @@ -147,6 +148,8 @@ for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { if (box.IsType("mdhd")) { mMdhd = Mdhd(box); + } else if (box.IsType("minf")) { + ParseMinf(box); } } } @@ -164,12 +167,59 @@ } } -Moof::Moof(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts, Microseconds aTimestampOffset) : +void +MoofParser::ParseMinf(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("stbl")) { + ParseStbl(box); + } + } +} + +void +MoofParser::ParseStbl(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("stsd")) { + ParseStsd(box); + } + } +} + +void +MoofParser::ParseStsd(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("encv") || box.IsType("enca")) { + ParseEncrypted(box); + } + } +} + +void +MoofParser::ParseEncrypted(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + // Some MP4 files have been found to have multiple sinf boxes in the same + // enc* box. This does not match spec anyway, so just choose the first + // one that parses properly. + if (box.IsType("sinf")) { + mSinf = Sinf(box); + + if (mSinf.IsValid()) { + break; + } + } + } +} + +Moof::Moof(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf, Microseconds aTimestampOffset) : mRange(aBox.Range()), mTimestampOffset(aTimestampOffset), mMaxRoundingError(0) { for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { if (box.IsType("traf")) { - ParseTraf(box, aTrex, aMdhd, aEdts); + ParseTraf(box, aTrex, aMdhd, aEdts, aSinf); } } ProcessCenc(); @@ -240,7 +290,7 @@ } void -Moof::ParseTraf(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts) +Moof::ParseTraf(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf) { Tfhd tfhd(aTrex); Tfdt tfdt; @@ -253,9 +303,9 @@ } else if (box.IsType("trun")) { ParseTrun(box, tfhd, tfdt, aMdhd, aEdts); } else if (box.IsType("saiz")) { - mSaizs.AppendElement(Saiz(box)); + mSaizs.AppendElement(Saiz(box, aSinf.mDefaultEncryptionType)); } else if (box.IsType("saio")) { - mSaios.AppendElement(Saio(box)); + mSaios.AppendElement(Saio(box, aSinf.mDefaultEncryptionType)); } } } @@ -557,7 +607,9 @@ reader->DiscardRemaining(); } -Saiz::Saiz(Box& aBox) : mAuxInfoType("sinf"), mAuxInfoTypeParameter(0) +Saiz::Saiz(Box& aBox, AtomType aDefaultType) + : mAuxInfoType(aDefaultType) + , mAuxInfoTypeParameter(0) { BoxReader reader(aBox); if (!reader->CanReadType()) { @@ -588,7 +640,9 @@ mValid = true; } -Saio::Saio(Box& aBox) : mAuxInfoType("sinf"), mAuxInfoTypeParameter(0) +Saio::Saio(Box& aBox, AtomType aDefaultType) + : mAuxInfoType(aDefaultType) + , mAuxInfoTypeParameter(0) { BoxReader reader(aBox); if (!reader->CanReadType()) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/mp4_demuxer.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/mp4_demuxer.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/mp4_demuxer.cpp 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/mp4_demuxer.cpp 2015-02-03 14:33:28.000000000 +0000 @@ -123,7 +123,7 @@ mSource, mAudioConfig.mTrackId, mTimestampOffset, mMonitor); mPrivate->mIndexes.AppendElement(index); - if (index->IsFragmented() && !mAudioConfig.crypto.valid) { + if (index->IsFragmented()) { mPrivate->mAudioIterator = new SampleIterator(index); } } else if (!mPrivate->mVideo.get() && !strncmp(mimeType, "video/", 6)) { @@ -137,7 +137,7 @@ mSource, mVideoConfig.mTrackId, mTimestampOffset, mMonitor); mPrivate->mIndexes.AppendElement(index); - if (index->IsFragmented() && !mVideoConfig.crypto.valid) { + if (index->IsFragmented()) { mPrivate->mVideoIterator = new SampleIterator(index); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/SinfParser.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/SinfParser.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/binding/SinfParser.cpp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/binding/SinfParser.cpp 2015-02-03 14:33:28.000000000 +0000 @@ -0,0 +1,74 @@ +/* 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 "mozilla/unused.h" +#include "mp4_demuxer/SinfParser.h" +#include "mp4_demuxer/AtomType.h" +#include "mp4_demuxer/Box.h" + +namespace mp4_demuxer { + +Sinf::Sinf(Box& aBox) + : mDefaultIVSize(0) + , mDefaultEncryptionType() +{ + SinfParser parser(aBox); + if (parser.GetSinf().IsValid()) { + *this = parser.GetSinf(); + } +} + +SinfParser::SinfParser(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("schm")) { + ParseSchm(box); + } else if (box.IsType("schi")) { + ParseSchi(box); + } + } +} + +void +SinfParser::ParseSchm(Box& aBox) +{ + BoxReader reader(aBox); + + if (reader->Remaining() < 8) { + return; + } + + mozilla::unused << reader->ReadU32(); // flags -- ignore + mSinf.mDefaultEncryptionType = reader->ReadU32(); + + reader->DiscardRemaining(); +} + +void +SinfParser::ParseSchi(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("tenc")) { + ParseTenc(box); + } + } +} + +void +SinfParser::ParseTenc(Box& aBox) +{ + BoxReader reader(aBox); + + if (reader->Remaining() < 24) { + return; + } + + mozilla::unused << reader->ReadU32(); // flags -- ignore + + uint32_t isEncrypted = reader->ReadU24(); + mSinf.mDefaultIVSize = reader->ReadU8(); + memcpy(mSinf.mDefaultKeyID, reader->Read(16), 16); +} + +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp 2015-02-03 14:33:28.000000000 +0000 @@ -37,6 +37,7 @@ #include #include #include +#include "nsTArray.h" namespace stagefright { @@ -103,6 +104,7 @@ bool mWantsNALFragments; uint8_t *mSrcBuffer; + FallibleTArray mSrcBackend; size_t parseNALSize(const uint8_t *data) const; status_t parseChunk(off64_t *offset); @@ -163,6 +165,8 @@ MPEG4Source(const MPEG4Source &); MPEG4Source &operator=(const MPEG4Source &); + + bool ensureSrcBufferAllocated(int32_t size); }; // This custom data source wraps an existing one and satisfies requests @@ -723,6 +727,13 @@ s->setTo(tmp); } +static bool ValidInputSize(int32_t size) { + // Reject compressed samples larger than an uncompressed UHD + // frame. This is a reasonable cut-off for a lossy codec, + // combined with the current Firefox limit to 5k video. + return (size > 0 && size <= 4 * (1920 * 1080) * 3 / 2); +} + status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { ALOGV("entering parseChunk %lld/%d", *offset, depth); uint32_t hdr[2]; @@ -2548,13 +2559,6 @@ free(mCurrentSampleInfoOffsets); } -static bool ValidInputSize(int32_t size) { - // Reject compressed samples larger than an uncompressed UHD - // frame. This is a reasonable cut-off for a lossy codec, - // combined with the current Firefox limit to 5k video. - return (size > 0 && size <= 4 * (1920 * 1080) * 3 / 2); -} - status_t MPEG4Source::start(MetaData *params) { Mutex::Autolock autoLock(mLock); @@ -2568,14 +2572,7 @@ mWantsNALFragments = false; } - int32_t max_size; - CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size)); - if (!ValidInputSize(max_size)) { - ALOGE("Invalid max input size %d", max_size); - return ERROR_MALFORMED; - } - - mSrcBuffer = new uint8_t[max_size]; + mSrcBuffer = mSrcBackend.Elements(); mStarted = true; @@ -2592,8 +2589,7 @@ mBuffer = NULL; } - delete[] mSrcBuffer; - mSrcBuffer = NULL; + mSrcBackend.Clear(); mStarted = false; mCurrentSampleIndex = 0; @@ -3269,6 +3265,16 @@ } } +bool MPEG4Source::ensureSrcBufferAllocated(int32_t aSize) { + if (mSrcBackend.Length() >= aSize) { + return true; + } + if (!mSrcBackend.SetLength(aSize)) { + return false; + } + mSrcBuffer = mSrcBackend.Elements(); + return true; +} status_t MPEG4Source::read( MediaBuffer **out, const ReadOptions *options) { @@ -3514,6 +3520,9 @@ num_bytes_read = mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size); } else { + if (!ensureSrcBufferAllocated(size)) { + return ERROR_MALFORMED; + } num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size); } @@ -3866,6 +3875,9 @@ num_bytes_read = mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size); } else { + if (!ensureSrcBufferAllocated(size)) { + return ERROR_MALFORMED; + } num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp 2015-02-03 14:33:28.000000000 +0000 @@ -257,7 +257,10 @@ return ERROR_IO; } - CHECK(U32_AT(buffer) >= 1); // chunk index is 1 based in the spec. + if (!U32_AT(buffer)) { + ALOGE("error reading sample to chunk table"); + return ERROR_MALFORMED; // chunk index is 1 based in the spec. + } // We want the chunk index to be 0-based. mSampleToChunkEntries[i].startChunk = U32_AT(buffer) - 1; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/libstagefright/moz.build 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/libstagefright/moz.build 2015-02-03 14:33:28.000000000 +0000 @@ -50,6 +50,7 @@ EXPORTS.mp4_demuxer += [ 'binding/include/mp4_demuxer/Adts.h', 'binding/include/mp4_demuxer/AnnexB.h', + 'binding/include/mp4_demuxer/Atom.h', 'binding/include/mp4_demuxer/AtomType.h', 'binding/include/mp4_demuxer/BufferStream.h', 'binding/include/mp4_demuxer/ByteReader.h', @@ -59,6 +60,7 @@ 'binding/include/mp4_demuxer/Interval.h', 'binding/include/mp4_demuxer/MoofParser.h', 'binding/include/mp4_demuxer/mp4_demuxer.h', + 'binding/include/mp4_demuxer/SinfParser.h', ] SOURCES += [ @@ -80,6 +82,7 @@ 'binding/Index.cpp', 'binding/MoofParser.cpp', 'binding/mp4_demuxer.cpp', + 'binding/SinfParser.cpp', 'frameworks/av/media/libstagefright/DataSource.cpp', 'frameworks/av/media/libstagefright/ESDS.cpp', 'frameworks/av/media/libstagefright/foundation/AAtomizer.cpp', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/nr_socket_prsock.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/nr_socket_prsock.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/nr_socket_prsock.cpp 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/nr_socket_prsock.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -693,9 +693,57 @@ return(_status); } -// NrSocketIpc Implementation -NS_IMPL_ISUPPORTS(NrSocketIpc, nsIUDPSocketInternal) +NS_IMPL_ISUPPORTS(NrSocketIpcProxy, nsIUDPSocketInternal) + +nsresult +NrSocketIpcProxy::Init(const nsRefPtr& socket) +{ + nsresult rv; + sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + MOZ_ASSERT(false, "Failed to get STS thread"); + return rv; + } + + socket_ = socket; + return NS_OK; +} +NrSocketIpcProxy::~NrSocketIpcProxy() +{ + // Send our ref to STS to be released + RUN_ON_THREAD(sts_thread_, + mozilla::WrapRelease(socket_.forget()), + NS_DISPATCH_NORMAL); +} + +// IUDPSocketInternal interfaces +// callback while error happened in UDP socket operation +NS_IMETHODIMP NrSocketIpcProxy::CallListenerError(const nsACString &message, + const nsACString &filename, + uint32_t line_number) { + return socket_->CallListenerError(message, filename, line_number); +} + +// callback while receiving UDP packet +NS_IMETHODIMP NrSocketIpcProxy::CallListenerReceivedData(const nsACString &host, + uint16_t port, + const uint8_t *data, + uint32_t data_length) { + return socket_->CallListenerReceivedData(host, port, data, data_length); +} + +// callback while UDP socket is opened +NS_IMETHODIMP NrSocketIpcProxy::CallListenerOpened() { + return socket_->CallListenerOpened(); +} + +// callback while UDP socket is closed +NS_IMETHODIMP NrSocketIpcProxy::CallListenerClosed() { + return socket_->CallListenerClosed(); +} + +// NrSocketIpc Implementation NrSocketIpc::NrSocketIpc(const nsCOMPtr &main_thread) : err_(false), state_(NR_INIT), @@ -1022,7 +1070,15 @@ socket_child_ = new nsMainThreadPtrHolder(socketChild); socket_child_->SetFilterName(nsCString("stun")); - if (NS_FAILED(socket_child_->Bind(this, host, port, + nsRefPtr proxy(new NrSocketIpcProxy); + rv = proxy->Init(this); + if (NS_FAILED(rv)) { + err_ = true; + mon.NotifyAll(); + return; + } + + if (NS_FAILED(socket_child_->Bind(proxy, host, port, /* reuse = */ false, /* loopback = */ false))) { err_ = true; @@ -1107,7 +1163,7 @@ }; int nr_socket_local_create(nr_transport_addr *addr, nr_socket **sockp) { - NrSocketBase *sock = nullptr; + RefPtr sock; // create IPC bridge for content process if (XRE_GetProcessType() == GeckoProcessType_Default) { @@ -1129,15 +1185,16 @@ if (r) ABORT(r); - // Add a reference so that we can delete it in destroy() - sock->AddRef(); - _status = 0; -abort: - if (_status) { - delete sock; + { + // We will release this reference in destroy(), not exactly the normal + // ownership model, but it is what it is. + NrSocketBase* dummy = sock.forget().take(); + (void)dummy; } + +abort: return _status; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/nr_socket_prsock.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/nr_socket_prsock.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/nr_socket_prsock.h 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/nr_socket_prsock.h 2015-02-03 14:33:30.000000000 +0000 @@ -188,8 +188,7 @@ DISALLOW_COPY_ASSIGN(nr_udp_message); }; -class NrSocketIpc : public NrSocketBase, - public nsIUDPSocketInternal { +class NrSocketIpc : public NrSocketBase { public: enum NrSocketIpcState { @@ -200,8 +199,17 @@ NR_CLOSED, }; - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIUDPSOCKETINTERNAL + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrSocketIpc) + + NS_IMETHODIMP CallListenerError(const nsACString &message, + const nsACString &filename, + uint32_t line_number); + NS_IMETHODIMP CallListenerReceivedData(const nsACString &host, + uint16_t port, + const uint8_t *data, + uint32_t data_length); + NS_IMETHODIMP CallListenerOpened(); + NS_IMETHODIMP CallListenerClosed(); explicit NrSocketIpc(const nsCOMPtr &main_thread); @@ -240,6 +248,22 @@ ReentrantMonitor monitor_; }; +// The socket child holds onto one of these, which just passes callbacks +// through and makes sure the ref to the NrSocketIpc is released on STS. +class NrSocketIpcProxy : public nsIUDPSocketInternal { +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIUDPSOCKETINTERNAL + + nsresult Init(const nsRefPtr& socket); + +private: + virtual ~NrSocketIpcProxy(); + + nsRefPtr socket_; + nsCOMPtr sts_thread_; +}; + int nr_netaddr_to_transport_addr(const net::NetAddr *netaddr, nr_transport_addr *addr, int protocol); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/runnable_utils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/runnable_utils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/runnable_utils.h 2015-01-25 22:24:29.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/runnable_utils.h 2015-02-03 14:33:30.000000000 +0000 @@ -96,6 +96,25 @@ #define ASSERT_ON_THREAD(t) #endif +template +class DispatchedRelease : public detail::runnable_args_base { +public: + explicit DispatchedRelease(already_AddRefed& ref) : ref_(ref) {} + + NS_IMETHOD Run() { + ref_ = nullptr; + return NS_OK; + } +private: + nsRefPtr ref_; +}; + +template +DispatchedRelease* WrapRelease(already_AddRefed&& ref) +{ + return new DispatchedRelease(ref); +} + } /* namespace mozilla */ #endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/test/turn_unittest.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/test/turn_unittest.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/test/turn_unittest.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/test/turn_unittest.cpp 2015-02-03 14:33:30.000000000 +0000 @@ -208,6 +208,19 @@ std::cerr << "Allocation succeeded with addr=" << relay_addr_ << std::endl; } + void Deallocate_s() { + ASSERT_TRUE(turn_ctx_); + + int r = nr_turn_client_deallocate(turn_ctx_); + ASSERT_EQ(0, r); + } + + void Deallocate() { + RUN_ON_THREAD(test_utils->sts_target(), + WrapRunnable(this, &TurnClient::Deallocate_s), + NS_DISPATCH_SYNC); + } + void Readable(NR_SOCKET s, int how, void *arg) { // Re-arm std::cerr << "Socket is readable" << std::endl; @@ -265,7 +278,7 @@ } } - void SendTo_s(const std::string& target) { + void SendTo_s(const std::string& target, bool expect_success) { nr_transport_addr addr; int r; @@ -292,12 +305,15 @@ r = nr_turn_client_send_indication(turn_ctx_, test, sizeof(test), 0, &addr); - ASSERT_EQ(0, r); + if (expect_success) { + ASSERT_EQ(0, r); + } } - void SendTo(const std::string& target) { + void SendTo(const std::string& target, bool expect_success=true) { RUN_ON_THREAD(test_utils->sts_target(), - WrapRunnable(this, &TurnClient::SendTo_s, target), + WrapRunnable(this, &TurnClient::SendTo_s, target, + expect_success), NS_DISPATCH_SYNC); } @@ -336,14 +352,13 @@ TEST_F(TurnClient, AllocateAndHold) { Allocate(); PR_Sleep(20000); + ASSERT_TRUE(turn_ctx_->state == NR_TURN_CLIENT_STATE_ALLOCATED); } TEST_F(TurnClient, SendToSelf) { Allocate(); SendTo(relay_addr_); - ASSERT_TRUE_WAIT(received() == 100, 1000); - PR_Sleep(10000); // Wait 10 seconds to make sure the - // CreatePermission has time to complete/fail. + ASSERT_TRUE_WAIT(received() == 100, 5000); SendTo(relay_addr_); ASSERT_TRUE_WAIT(received() == 200, 1000); } @@ -353,13 +368,38 @@ SetTcp(); Allocate(); SendTo(relay_addr_); - ASSERT_TRUE_WAIT(received() == 100, 1000); - PR_Sleep(10000); // Wait 10 seconds to make sure the - // CreatePermission has time to complete/fail. + ASSERT_TRUE_WAIT(received() == 100, 5000); SendTo(relay_addr_); ASSERT_TRUE_WAIT(received() == 200, 1000); } +TEST_F(TurnClient, DeallocateReceiveFailure) { + Allocate(); + SendTo(relay_addr_); + ASSERT_TRUE_WAIT(received() == 100, 5000); + Deallocate(); + turn_ctx_->state = NR_TURN_CLIENT_STATE_ALLOCATED; + SendTo(relay_addr_, true); + PR_Sleep(1000); + ASSERT_TRUE(received() == 100); +} + +TEST_F(TurnClient, DeallocateReceiveFailureTcp) { + SetTcp(); + Allocate(); + SendTo(relay_addr_); + ASSERT_TRUE_WAIT(received() == 100, 5000); + Deallocate(); + turn_ctx_->state = NR_TURN_CLIENT_STATE_ALLOCATED; + /* Either the connection got closed by the TURN server already, then the send + * is going to fail, which we simply ignore. Or the connection is still alive + * and we cand send the data, but it should not get forwarded to us. In either + * case we should not receive more data. */ + SendTo(relay_addr_, false); + PR_Sleep(1000); + ASSERT_TRUE(received() == 100); +} + TEST_F(TurnClient, AllocateDummyServer) { turn_server_ = kDummyTurnServer; Allocate(false); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c 2015-02-03 14:33:30.000000000 +0000 @@ -292,12 +292,6 @@ return(0); } -void nr_ice_candidate_destroy_cb(NR_SOCKET s, int h, void *cb_arg) - { - nr_ice_candidate *cand=cb_arg; - nr_ice_candidate_destroy(&cand); - } - /* This algorithm is not super-fast, but I don't think we need a hash table just yet and it produces a small foundation string */ static int nr_ice_get_foundation(nr_ice_ctx *ctx,nr_ice_candidate *cand) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/third_party/nICEr/src/ice/ice_candidate.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/third_party/nICEr/src/ice/ice_candidate.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/third_party/nICEr/src/ice/ice_candidate.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/third_party/nICEr/src/ice/ice_candidate.h 2015-02-03 14:33:30.000000000 +0000 @@ -104,7 +104,6 @@ void nr_ice_candidate_compute_codeword(nr_ice_candidate *cand); int nr_ice_candidate_process_stun(nr_ice_candidate *cand, UCHAR *msg, int len, nr_transport_addr *faddr); int nr_ice_candidate_destroy(nr_ice_candidate **candp); -void nr_ice_candidate_destroy_cb(NR_SOCKET s, int h, void *cb_arg); int nr_ice_format_candidate_attribute(nr_ice_candidate *cand, char *attr, int maxlen); int nr_ice_peer_candidate_from_attribute(nr_ice_ctx *ctx,char *attr,nr_ice_media_stream *stream,nr_ice_candidate **candp); int nr_ice_peer_peer_rflx_candidate_create(nr_ice_ctx *ctx,char *label, nr_ice_component *comp,nr_transport_addr *addr, nr_ice_candidate **candp); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c 2015-02-03 14:33:30.000000000 +0000 @@ -109,6 +109,7 @@ if(r=r_data_copy(&clnt->password,pass)) ABORT(r); + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s)/CLIENT(%s): Adding client for %s",ctx->label, client_label, user); clnt->stun_server_cb=cb; clnt->cb_arg=cb_arg; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c 2015-02-03 14:33:30.000000000 +0000 @@ -94,6 +94,9 @@ nr_transport_addr *addr); static void nr_turn_client_refresh_cb(NR_SOCKET s, int how, void *arg); static void nr_turn_client_permissions_cb(NR_SOCKET s, int how, void *cb); +static int nr_turn_client_send_stun_request(nr_turn_client_ctx *ctx, + nr_stun_message *req, + int flags); /* nr_turn_stun_ctx functions */ @@ -370,6 +373,8 @@ if (ctx->label) r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): destroy", ctx->label); + nr_turn_client_deallocate(ctx); + /* Cancel frees the rest of our data */ RFREE(ctx->label); ctx->label = 0; @@ -409,7 +414,7 @@ if (ctx->state == NR_TURN_CLIENT_STATE_CANCELLED || ctx->state == NR_TURN_CLIENT_STATE_FAILED) - return 0; + return(0); if (ctx->label) r_log(NR_LOG_TURN, LOG_INFO, "TURN(%s): cancelling", ctx->label); @@ -430,6 +435,67 @@ return(0); } +int nr_turn_client_send_stun_request(nr_turn_client_ctx *ctx, + nr_stun_message *req, + int flags) +{ + int r,_status; + + if ((r=nr_stun_encode_message(req))) + ABORT(r); + + if ((r=nr_socket_sendto(ctx->sock, + req->buffer, req->length, flags, + &ctx->turn_server_addr))) { + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): Failed sending request", + ctx->label); + ABORT(r); + } + + _status=0; +abort: + return(_status); +} + +int nr_turn_client_deallocate(nr_turn_client_ctx *ctx) +{ + int r,_status; + nr_stun_message *aloc = 0; + nr_stun_client_auth_params auth; + nr_stun_client_refresh_request_params refresh; + + if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) + return(0); + + r_log(NR_LOG_TURN, LOG_INFO, "TURN(%s): deallocating", ctx->label); + + refresh.lifetime_secs = 0; + + auth.username = ctx->username; + INIT_DATA(auth.password, ctx->password->data, ctx->password->len); + + auth.realm = ctx->realm; + auth.nonce = ctx->nonce; + + auth.authenticate = 1; + + if ((r=nr_stun_build_refresh_request(&auth, &refresh, &aloc))) + ABORT(r); + + // We are only sending a single request here because we are in the process of + // shutting everything down. Theoretically we should probably start a seperate + // STUN transaction which outlives the TURN context. + if ((r=nr_turn_client_send_stun_request(ctx, aloc, 0))) + ABORT(r); + + ctx->state = NR_TURN_CLIENT_STATE_DEALLOCATING; + + _status=0; +abort: + nr_stun_message_destroy(&aloc); + return(_status); +} + static int nr_turn_client_failed(nr_turn_client_ctx *ctx) { if (ctx->state == NR_TURN_CLIENT_STATE_FAILED || @@ -733,17 +799,9 @@ if ((r=nr_stun_build_send_indication(¶ms, &ind))) ABORT(r); - if ((r=nr_stun_encode_message(ind))) + if ((r=nr_turn_client_send_stun_request(ctx, ind, flags))) ABORT(r); - if ((r=nr_socket_sendto(ctx->sock, - ind->buffer, ind->length, flags, - &ctx->turn_server_addr))) { - r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): Failed sending send indication", - ctx->label); - ABORT(r); - } - _status=0; abort: nr_stun_message_destroy(&ind); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.h 2015-02-03 14:33:30.000000000 +0000 @@ -78,6 +78,7 @@ #define NR_TURN_CLIENT_STATE_ALLOCATED 3 #define NR_TURN_CLIENT_STATE_FAILED 4 #define NR_TURN_CLIENT_STATE_CANCELLED 5 +#define NR_TURN_CLIENT_STATE_DEALLOCATING 6 char *label; nr_socket *sock; @@ -118,6 +119,7 @@ UCHAR *msg, int len, nr_transport_addr *turn_server_addr); int nr_turn_client_cancel(nr_turn_client_ctx *ctx); +int nr_turn_client_deallocate(nr_turn_client_ctx *ctx); int nr_turn_client_send_indication(nr_turn_client_ctx *ctx, const UCHAR *msg, size_t len, int flags, nr_transport_addr *remote_addr); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/moz.build 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/moz.build 2015-02-03 14:33:31.000000000 +0000 @@ -13,7 +13,7 @@ 'trunk/webrtc/modules/audio_coding/codecs/g722/g722_encode.c', # Because of name clash in the saturate function 'trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_filter.c', # Because of name clash in the kDampFilter variable 'trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_filter_c.c', # Because of name clash in the kDampFilter variable - 'trunk/webrtc/modules/audio_coding/neteq4/audio_vector.cc', # Because of explicit template specializations + 'trunk/webrtc/modules/audio_coding/neteq/audio_vector.cc', # Because of explicit template specializations 'trunk/webrtc/modules/audio_device/linux/audio_device_pulse_linux.cc', # Because of LATE() 'trunk/webrtc/modules/audio_device/linux/audio_mixer_manager_pulse_linux.cc',# Because of LATE() 'trunk/webrtc/modules/audio_device/opensl/opensles_input.cc', # Because of name clash in the kOption variable diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/signaling.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/signaling.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/signaling.gyp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/signaling.gyp 2015-02-03 14:33:31.000000000 +0000 @@ -51,10 +51,6 @@ '../../../dom/media', '../../../media/mtransport', '../trunk', - '../trunk/webrtc', - '../trunk/webrtc/video_engine/include', - '../trunk/webrtc/voice_engine/include', - '../trunk/webrtc/modules/interface', '../../libyuv/include', '../../mtransport/third_party/nrappkit/src/util/libekr', ], @@ -209,7 +205,6 @@ ], 'include_dirs': [ # hack on hack to re-add it after SrtpFlow removes it - '../../webrtc/trunk/webrtc', '../../../dom/media/omx', '../../../gfx/layers/client', ], @@ -250,6 +245,7 @@ 'defines': [ 'OS_LINUX', 'SIP_OS_LINUX', + 'WEBRTC_POSIX', '_GNU_SOURCE', 'LINUX', 'GIPS_VER=3510', @@ -286,6 +282,7 @@ ], 'defines': [ # avoiding pointless ifdef churn + 'WEBRTC_POSIX', 'SIP_OS_OSX', 'OSX', 'SECLIB_OPENSSL', @@ -298,6 +295,7 @@ 'include_dirs': [ ], 'defines': [ + 'WEBRTC_POSIX', 'OS_MACOSX', 'SIP_OS_OSX', 'OSX', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -236,6 +236,9 @@ AddLocalSsrcs(*track->mTrack, &sdp->GetMediaSection(*track->mAssignedMLine)); + + AddLocalIds(*track->mTrack, + &sdp->GetMediaSection(*track->mAssignedMLine)); } while (offerToReceive.isSome() && added < *offerToReceive) { @@ -268,6 +271,133 @@ } } +void +JsepSessionImpl::SetupMsidSemantic(const std::vector& msids, + Sdp* sdp) const +{ + if (!msids.empty()) { + UniquePtr msidSemantics( + new SdpMsidSemanticAttributeList); + msidSemantics->PushEntry("WMS", msids); + sdp->GetAttributeList().SetAttribute(msidSemantics.release()); + } +} + +nsresult +JsepSessionImpl::GetIdsFromMsid(const Sdp& sdp, + const SdpMediaSection& msection, + std::string* streamId, + std::string* trackId) +{ + if (!sdp.GetAttributeList().HasAttribute( + SdpAttribute::kMsidSemanticAttribute)) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& msidSemantics = sdp.GetAttributeList().GetMsidSemantic().mMsidSemantics; + std::vector allMsids; + nsresult rv = GetMsids(msection, &allMsids); + NS_ENSURE_SUCCESS(rv, rv); + + bool allMsidsAreWebrtc = false; + std::set webrtcMsids; + + for (auto i = msidSemantics.begin(); i != msidSemantics.end(); ++i) { + if (i->semantic == "WMS") { + for (auto j = i->msids.begin(); j != i->msids.end(); ++j) { + if (*j == "*") { + allMsidsAreWebrtc = true; + } else { + webrtcMsids.insert(*j); + } + } + break; + } + } + + bool found = false; + + for (auto i = allMsids.begin(); i != allMsids.end(); ++i) { + if (allMsidsAreWebrtc || webrtcMsids.count(i->identifier)) { + // For now, we assume that there is exactly one streamId/trackId pair + // per m-section. Later on, we'll add handling for multiple remote tracks + // per m-section. + if (!found) { + *streamId = i->identifier; + *trackId = i->appdata; + found = true; + } else if ((*streamId != i->identifier) || (*trackId != i->appdata)) { + // Bail if there are multiple stream/track ids for now + JSEP_SET_ERROR("Found multiple different webrtc msids in m-section " + << msection.GetLevel() << ". The behavior here is " + "undefined."); + return NS_ERROR_INVALID_ARG; + } + } + } + + if (!found) { + return NS_ERROR_NOT_AVAILABLE; + } + + return NS_OK; +} + +nsresult +JsepSessionImpl::GetMsids( + const SdpMediaSection& msection, + std::vector* msids) +{ + if (msection.GetAttributeList().HasAttribute(SdpAttribute::kMsidAttribute)) { + *msids = msection.GetAttributeList().GetMsid().mMsids; + } + + // Can we find some additional msids in ssrc attributes? + // (Chrome does not put plain-old msid attributes in its SDP) + if (msection.GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)) { + auto& ssrcs = msection.GetAttributeList().GetSsrc().mSsrcs; + + for (auto i = ssrcs.begin(); i != ssrcs.end(); ++i) { + if (i->attribute.find("msid:") == 0) { + // Would be nice if SdpSsrcAttributeList could parse out the contained + // attribute, but at least the parse here is simple. + size_t streamIdStart = i->attribute.find_first_not_of(" \t", 5); + // We do not assume the appdata token is here, since this is not + // necessarily a webrtc msid + if (streamIdStart == std::string::npos) { + JSEP_SET_ERROR("Malformed source-level msid attribute: " + << i->attribute); + return NS_ERROR_INVALID_ARG; + } + + size_t streamIdEnd = i->attribute.find_first_of(" \t", streamIdStart); + if (streamIdEnd == std::string::npos) { + streamIdEnd = i->attribute.size(); + } + + size_t trackIdStart = + i->attribute.find_first_not_of(" \t", streamIdEnd); + if (trackIdStart == std::string::npos) { + trackIdStart = i->attribute.size(); + } + + size_t trackIdEnd = i->attribute.find_first_of(" \t", trackIdStart); + if (trackIdEnd == std::string::npos) { + trackIdEnd = i->attribute.size(); + } + + size_t streamIdSize = streamIdEnd - streamIdStart; + size_t trackIdSize = trackIdEnd - trackIdStart; + + msids->push_back({i->attribute.substr(streamIdStart, streamIdSize), + i->attribute.substr(trackIdStart, trackIdSize)}); + } + } + } + + return NS_OK; +} + nsresult JsepSessionImpl::CreateOffer(const JsepOfferOptions& options, std::string* offer) @@ -395,6 +525,24 @@ } } +void +JsepSessionImpl::AddLocalIds(const JsepTrack& track, + SdpMediaSection* msection) const +{ + if (track.GetMediaType() == SdpMediaSection::kApplication) { + return; + } + + UniquePtr msids(new SdpMsidAttributeList); + if (msection->GetAttributeList().HasAttribute(SdpAttribute::kMsidAttribute)) { + msids->mMsids = msection->GetAttributeList().GetMsid().mMsids; + } + + msids->PushEntry(track.GetStreamId(), track.GetTrackId()); + + msection->GetAttributeList().SetAttribute(msids.release()); +} + JsepCodecDescription* JsepSessionImpl::FindMatchingCodec(const std::string& fmt, const SdpMediaSection& msection) const @@ -663,6 +811,8 @@ AddLocalSsrcs(*track->mTrack, msection); + AddLocalIds(*track->mTrack, msection); + localDirection = SdpDirectionAttribute::kSendonly; track->mAssignedMLine = Some(mlineIndex); found = true; @@ -1363,6 +1513,8 @@ return NS_ERROR_INVALID_ARG; } + std::set trackIds; + for (size_t i = 0; i < parsed->GetMediaSectionCount(); ++i) { if (parsed->GetMediaSection(i).GetPort() == 0) { // Disabled, let this stuff slide. @@ -1404,6 +1556,26 @@ return NS_ERROR_INVALID_ARG; } } + + std::string streamId; + std::string trackId; + nsresult rv = GetIdsFromMsid(*parsed, + parsed->GetMediaSection(i), + &streamId, + &trackId); + + if (NS_SUCCEEDED(rv)) { + if (trackIds.count(trackId)) { + JSEP_SET_ERROR("track id:" << trackId + << " appears in more than one m-section at level " << i); + return NS_ERROR_INVALID_ARG; + } + + trackIds.insert(trackId); + } else if (rv != NS_ERROR_NOT_AVAILABLE) { + // Error has already been set + return rv; + } } *parsedp = Move(parsed); @@ -1462,7 +1634,7 @@ // TODO(bug 1017888): Suppress new track creation on renegotiation // of existing tracks. if (direction & SdpDirectionAttribute::kSendFlag) { - nsresult rv = CreateReceivingTrack(i, msection); + nsresult rv = CreateReceivingTrack(i, remoteDescription, msection); NS_ENSURE_SUCCESS(rv, rv); } } @@ -1521,20 +1693,31 @@ nsresult JsepSessionImpl::CreateReceivingTrack(size_t mline, + const Sdp& sdp, const SdpMediaSection& msection) { std::string streamId; std::string trackId; - // Generate random track ids. - // TODO(bug 1095218): Pull track and stream IDs out of SDP if available. - if (!mUuidGen->Generate(&trackId)) { - JSEP_SET_ERROR("Failed to generate UUID for JsepTrack"); - return NS_ERROR_FAILURE; + nsresult rv = GetIdsFromMsid(sdp, msection, &streamId, &trackId); + + if (NS_FAILED(rv)) { + if (rv != NS_ERROR_NOT_AVAILABLE) { + // Malformed ssrc attribute, probably + return rv; + } + + streamId = mDefaultRemoteStreamId; + + // Generate random track ids. + if (!mUuidGen->Generate(&trackId)) { + JSEP_SET_ERROR("Failed to generate UUID for JsepTrack"); + return NS_ERROR_FAILURE; + } } JsepTrack* remote = new JsepTrack(msection.GetMediaType(), - mDefaultRemoteStreamId, + streamId, trackId, JsepTrack::kJsepTrackReceiving); @@ -1590,6 +1773,12 @@ iceOpts->PushEntry("trickle"); sdp->GetAttributeList().SetAttribute(iceOpts); + // This assumes content doesn't add a bunch of msid attributes with a + // different semantic in mind. + std::vector msids; + msids.push_back("*"); + SetupMsidSemantic(msids, sdp.get()); + *sdpp = Move(sdp); return NS_OK; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/jsep/JsepSessionImpl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/jsep/JsepSessionImpl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/jsep/JsepSessionImpl.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/jsep/JsepSessionImpl.h 2015-02-03 14:33:31.000000000 +0000 @@ -210,6 +210,7 @@ void AddExtmap(SdpMediaSection* msection) const; void AddMid(const std::string& mid, SdpMediaSection* msection) const; void AddLocalSsrcs(const JsepTrack& track, SdpMediaSection* msection) const; + void AddLocalIds(const JsepTrack& track, SdpMediaSection* msection) const; JsepCodecDescription* FindMatchingCodec( const std::string& pt, const SdpMediaSection& msection) const; @@ -231,7 +232,9 @@ nsresult ValidateLocalDescription(const Sdp& description); nsresult SetRemoteTracksFromDescription(const Sdp& remoteDescription); // Non-const because we use our Uuid generator - nsresult CreateReceivingTrack(size_t mline, const SdpMediaSection& msection); + nsresult CreateReceivingTrack(size_t mline, + const Sdp& sdp, + const SdpMediaSection& msection); nsresult HandleNegotiatedSession(const UniquePtr& local, const UniquePtr& remote); nsresult DetermineSendingDirection(SdpDirectionAttribute::Direction offer, @@ -244,6 +247,13 @@ Maybe offerToReceive, Sdp* sdp); void SetupBundle(Sdp* sdp) const; + void SetupMsidSemantic(const std::vector& msids, Sdp* sdp) const; + nsresult GetIdsFromMsid(const Sdp& sdp, + const SdpMediaSection& msection, + std::string* streamId, + std::string* trackId); + nsresult GetMsids(const SdpMediaSection& msection, + std::vector* msids); nsresult CreateOfferMSection(SdpMediaSection::MediaType type, SdpDirectionAttribute::Direction direction, SdpMediaSection::Protocol proto, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -544,9 +544,9 @@ { CSFLogDebug(logTag, "%s %d %d ", __FUNCTION__, enabled, id); - if (mPtrVoERTP_RTCP->SetRTPAudioLevelIndicationStatus(mChannel, enabled, id) == -1) + if (mPtrVoERTP_RTCP->SetSendAudioLevelIndicationStatus(mChannel, enabled, id) == -1) { - CSFLogError(logTag, "%s SetRTPAudioLevelIndicationStatus Failed", __FUNCTION__); + CSFLogError(logTag, "%s SetSendAudioLevelIndicationStatus Failed", __FUNCTION__); return kMediaConduitUnknownError; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h 2015-02-03 14:33:31.000000000 +0000 @@ -234,7 +234,8 @@ * return: Concrete VideoSessionConduitObject or nullptr in the case * of failure */ - static RefPtr Create(VideoSessionConduit *aOther); + static RefPtr Create(VideoSessionConduit *aOther, + bool receiving); enum FrameRequestType { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -42,13 +42,15 @@ /** * Factory Method for VideoConduit */ -mozilla::RefPtr VideoSessionConduit::Create(VideoSessionConduit *aOther) +mozilla::RefPtr +VideoSessionConduit::Create(VideoSessionConduit *aOther, + bool receiving) { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); CSFLogDebug(logTag, "%s ", __FUNCTION__); WebrtcVideoConduit* obj = new WebrtcVideoConduit(); - if(obj->Init(static_cast(aOther)) != kMediaConduitNoError) + if(obj->Init(static_cast(aOther), receiving) != kMediaConduitNoError) { CSFLogError(logTag, "%s VideoConduit Init Failed ", __FUNCTION__); delete obj; @@ -284,7 +286,9 @@ /** * Performs initialization of the MANDATORY components of the Video Engine */ -MediaConduitErrorCode WebrtcVideoConduit::Init(WebrtcVideoConduit *other) +MediaConduitErrorCode +WebrtcVideoConduit::Init(WebrtcVideoConduit *other, + bool receiving) { CSFLogDebug(logTag, "%s this=%p other=%p", __FUNCTION__, this, other); @@ -332,7 +336,6 @@ MOZ_ASSERT(other->mVideoEngine); mVideoEngine = other->mVideoEngine; } else { - #ifdef MOZ_WIDGET_ANDROID // get the JVM JavaVM *jvm = jsjni_GetVM(); @@ -344,7 +347,8 @@ #endif // Per WebRTC APIs below function calls return nullptr on failure - if( !(mVideoEngine = webrtc::VideoEngine::Create()) ) + mVideoEngine = webrtc::VideoEngine::Create(); + if(!mVideoEngine) { CSFLogError(logTag, "%s Unable to create video engine ", __FUNCTION__); return kMediaConduitSessionNotInited; @@ -445,14 +449,6 @@ __FUNCTION__,mPtrViEBase->LastError()); return kMediaConduitCaptureError; } - - if(mPtrViERender->AddRenderer(mChannel, - webrtc::kVideoI420, - (webrtc::ExternalRenderer*) this) == -1) - { - CSFLogError(logTag, "%s Failed to added external renderer ", __FUNCTION__); - return kMediaConduitInvalidRenderer; - } // Set up some parameters, per juberti. Set MTU. if(mPtrViENetwork->SetMTU(mChannel, 1200) != 0) { @@ -469,6 +465,15 @@ } } + if (receiving) { + if (mPtrViERender->AddRenderer(mChannel, + webrtc::kVideoI420, + (webrtc::ExternalRenderer*) this) == -1) { + CSFLogError(logTag, "%s Failed to added external renderer ", __FUNCTION__); + return kMediaConduitInvalidRenderer; + } + } + CSFLogError(logTag, "%s Initialization Done", __FUNCTION__); return kMediaConduitNoError; } @@ -1238,6 +1243,7 @@ WebrtcVideoConduit::DeliverFrame(unsigned char* buffer, int buffer_size, uint32_t time_stamp, + int64_t ntp_time_ms, int64_t render_time, void *handle) { @@ -1318,7 +1324,8 @@ #ifdef MOZ_WEBRTC_OMX cinst.resolution_divisor = 16; #endif - cinst.codecSpecific.H264.profile = codecInfo->mProfile; + // cinst.codecSpecific.H264.profile = ? + cinst.codecSpecific.H264.profile_byte = codecInfo->mProfile; cinst.codecSpecific.H264.constraints = codecInfo->mConstraints; cinst.codecSpecific.H264.level = codecInfo->mLevel; cinst.codecSpecific.H264.packetizationMode = codecInfo->mPacketizationMode; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/media-conduit/VideoConduit.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/media-conduit/VideoConduit.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/media-conduit/VideoConduit.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/media-conduit/VideoConduit.h 2015-02-03 14:33:31.000000000 +0000 @@ -182,8 +182,8 @@ */ virtual int FrameSizeChange(unsigned int, unsigned int, unsigned int) MOZ_OVERRIDE; - virtual int DeliverFrame(unsigned char*,int, uint32_t , int64_t, - void *handle) MOZ_OVERRIDE; + virtual int DeliverFrame(unsigned char*, int, uint32_t , int64_t, + int64_t, void *handle) MOZ_OVERRIDE; /** * Does DeliverFrame() support a null buffer and non-null handle @@ -226,7 +226,8 @@ WebrtcVideoConduit(); virtual ~WebrtcVideoConduit(); - MediaConduitErrorCode Init(WebrtcVideoConduit *other); + MediaConduitErrorCode Init(WebrtcVideoConduit *other, + bool receiving); int GetChannel() { return mChannel; } webrtc::VideoEngine* GetVideoEngine() { return mVideoEngine; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -446,6 +446,11 @@ return; } + struct nal_entry { + uint32_t offset; + uint32_t size; + }; + nsTArray nals; uint32_t size; // make sure we don't read past the end of the buffer getting the size while (buffer+size_bytes < end) { @@ -484,13 +489,9 @@ buffer+size - end)); return; } - webrtc::EncodedImage unit(buffer, size, size); - unit._frameType = ft; - unit._timeStamp = timestamp; - unit._completeFrame = true; - - mCallback->Encoded(unit, nullptr, nullptr); - + // XXX optimize by making buffer an offset + nal_entry nal = {((uint32_t) (buffer-aEncodedFrame->Buffer())), (uint32_t) size}; + nals.AppendElement(nal); buffer += size; // on last one, buffer == end normally } @@ -498,6 +499,24 @@ // At most 3 bytes can be left over, depending on buffertype LOGD(("GMP plugin returned %td extra bytes", end - buffer)); } + + size_t num_nals = nals.Length(); + if (num_nals > 0) { + webrtc::RTPFragmentationHeader fragmentation; + fragmentation.VerifyAndAllocateFragmentationHeader(num_nals); + for (size_t i = 0; i < num_nals; i++) { + fragmentation.fragmentationOffset[i] = nals[i].offset; + fragmentation.fragmentationLength[i] = nals[i].size; + } + + webrtc::EncodedImage unit(aEncodedFrame->Buffer(), size, size); + unit._frameType = ft; + unit._timeStamp = timestamp; + unit._completeFrame = true; + + mCallback->Encoded(unit, nullptr, &fragmentation); + + } } } @@ -506,7 +525,8 @@ mGMP(nullptr), mHost(nullptr), mCallback(nullptr), - mCachedPluginId(0) {} + mCachedPluginId(0), + mDecoderStatus(GMPNoErr){} static void Decoder_Close_g(GMPVideoDecoderProxy* aGMP) @@ -676,7 +696,10 @@ if (NS_FAILED(rv)) { return WEBRTC_VIDEO_CODEC_ERROR; } - + if(mDecoderStatus != GMPNoErr){ + mDecoderStatus = GMPNoErr; + return WEBRTC_VIDEO_CODEC_ERROR; + } return WEBRTC_VIDEO_CODEC_OK; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h 2015-02-03 14:33:31.000000000 +0000 @@ -147,6 +147,7 @@ } virtual void Error(GMPErr aError) MOZ_OVERRIDE { + mDecoderStatus = aError; } private: @@ -165,6 +166,7 @@ GMPVideoHost* mHost; webrtc::DecodedImageCallback* mCallback; uint64_t mCachedPluginId; + GMPErr mDecoderStatus; }; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/media-conduit/WebrtcOMXH264VideoCodec.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/media-conduit/WebrtcOMXH264VideoCodec.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/media-conduit/WebrtcOMXH264VideoCodec.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/media-conduit/WebrtcOMXH264VideoCodec.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -21,8 +21,8 @@ using namespace android; // WebRTC -#include "common_video/interface/texture_video_frame.h" -#include "video_engine/include/vie_external_codec.h" +#include "webrtc/common_video/interface/texture_video_frame.h" +#include "webrtc/video_engine/include/vie_external_codec.h" #include "runnable_utils.h" // Gecko diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -5,7 +5,6 @@ // Original author: ekr@rtfm.com -#include "logging.h" #include "MediaPipeline.h" #ifndef USE_FAKE_MEDIA_STREAMS @@ -45,8 +44,7 @@ #include "mozilla/gfx/Point.h" #include "mozilla/gfx/Types.h" -#include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" +#include "logging.h" using namespace mozilla; using namespace mozilla::gfx; @@ -547,17 +545,13 @@ return MediaPipeline::Init(); } -void MediaPipelineTransmit::AttachToTrack(TrackID track_id) { - char track_id_string[11]; +void MediaPipelineTransmit::AttachToTrack(const std::string& track_id) { ASSERT_ON_THREAD(main_thread_); - // We can replace this when we are allowed to do streams or std::to_string - PR_snprintf(track_id_string, sizeof(track_id_string), "%d", track_id); - description_ = pc_ + "| "; description_ += conduit_->type() == MediaSessionConduit::AUDIO ? "Transmit audio[" : "Transmit video["; - description_ += track_id_string; + description_ += track_id; description_ += "]"; // TODO(ekr@rtfm.com): Check for errors @@ -613,10 +607,11 @@ } nsresult MediaPipelineTransmit::ReplaceTrack(DOMMediaStream *domstream, - TrackID track_id) { + const std::string& track_id) { // MainThread, checked in calls we make - MOZ_MTLOG(ML_DEBUG, "Reattaching pipeline to stream " - << static_cast(domstream->GetStream()) << " conduit type=" << + MOZ_MTLOG(ML_DEBUG, "Reattaching pipeline " << description_ << " to stream " + << static_cast(domstream->GetStream()) + << " track " << track_id << " conduit type=" << (conduit_->type() == MediaSessionConduit::AUDIO ?"audio":"video")); if (domstream_) { // may be excessive paranoia @@ -624,7 +619,7 @@ } domstream_ = domstream; // Detach clears it stream_ = domstream->GetStream(); - //track_id_ = track_id; not threadsafe to change this; and we don't need to + track_id_ = track_id; AttachToTrack(track_id); return NS_OK; } @@ -1014,7 +1009,6 @@ // Send a black image. nsAutoArrayPtr pixelData; - static const fallible_t fallible = fallible_t(); pixelData = new (fallible) uint8_t[length]; if (pixelData) { // YCrCb black = 0x10 0x80 0x80 @@ -1165,15 +1159,11 @@ #endif nsresult MediaPipelineReceiveAudio::Init() { - char track_id_string[11]; ASSERT_ON_THREAD(main_thread_); MOZ_MTLOG(ML_DEBUG, __FUNCTION__); - // We can replace this when we are allowed to do streams or std::to_string - PR_snprintf(track_id_string, sizeof(track_id_string), "%d", track_id_); - description_ = pc_ + "| Receive audio["; - description_ += track_id_string; + description_ += track_id_; description_ += "]"; listener_->AddSelf(new AudioSegment()); @@ -1336,15 +1326,11 @@ } nsresult MediaPipelineReceiveVideo::Init() { - char track_id_string[11]; ASSERT_ON_THREAD(main_thread_); MOZ_MTLOG(ML_DEBUG, __FUNCTION__); - // We can replace this when we are allowed to do streams or std::to_string - PR_snprintf(track_id_string, sizeof(track_id_string), "%d", track_id_); - description_ = pc_ + "| Receive video["; - description_ += track_id_string; + description_ += track_id_; description_ += "]"; #ifdef MOZILLA_INTERNAL_API diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h 2015-02-03 14:33:31.000000000 +0000 @@ -77,7 +77,7 @@ nsCOMPtr main_thread, nsCOMPtr sts_thread, MediaStream *stream, - TrackID track_id, + const std::string& track_id, int level, RefPtr conduit, RefPtr rtp_transport, @@ -130,9 +130,9 @@ nsAutoPtr filter); virtual Direction direction() const { return direction_; } - virtual TrackID trackid() const { return track_id_; } + virtual const std::string& trackid() const { return track_id_; } virtual int level() const { return level_; } - virtual bool IsVideo() const { return false; } + virtual bool IsVideo() const = 0; bool IsDoingRtcpMux() const { return (rtp_.type_ == MUX); @@ -233,10 +233,10 @@ // Written on the main thread. // Used on STS and MediaStreamGraph threads. // May be changed by rtpSender.replaceTrack() - TrackID track_id_; // The track on the stream. + std::string track_id_; // The track on the stream. // Written and used as with the stream_; // Not used outside initialization in MediaPipelineTransmit - int level_; // The m-line index (starting at 1, to match convention) + int level_; // The m-line index (starting at 0, to match convention) RefPtr conduit_; // Our conduit. Written on the main // thread. Read on STS thread. @@ -352,6 +352,7 @@ nsCOMPtr main_thread, nsCOMPtr sts_thread, DOMMediaStream *domstream, + const std::string& track_id, int level, bool is_video, RefPtr conduit, @@ -359,7 +360,7 @@ RefPtr rtcp_transport, nsAutoPtr filter) : MediaPipeline(pc, TRANSMIT, main_thread, sts_thread, - domstream->GetStream(), TRACK_INVALID, level, + domstream->GetStream(), track_id, level, conduit, rtp_transport, rtcp_transport, filter), listener_(new PipelineListener(conduit)), domstream_(domstream), @@ -369,7 +370,7 @@ // Initialize (stuff here may fail) virtual nsresult Init() MOZ_OVERRIDE; - virtual void AttachToTrack(TrackID track_id); + virtual void AttachToTrack(const std::string& track_id); // Index used to refer to this before we know the TrackID // Note: unlike MediaPipeline::trackid(), this is threadsafe @@ -403,7 +404,7 @@ // track to be part of a different stream (since we don't support // multiple tracks of a type in a stream yet). bug 1056650 virtual nsresult ReplaceTrack(DOMMediaStream *domstream, - TrackID track_id); + const std::string& track_id); // Separate class to allow ref counting @@ -518,7 +519,7 @@ nsCOMPtr main_thread, nsCOMPtr sts_thread, MediaStream *stream, - TrackID track_id, + const std::string& track_id, int level, RefPtr conduit, RefPtr rtp_transport, @@ -547,17 +548,23 @@ nsCOMPtr main_thread, nsCOMPtr sts_thread, MediaStream *stream, - TrackID track_id, + // This comes from an msid attribute. Everywhere + // but MediaStreamGraph uses this. + const std::string& media_stream_track_id, + // This is an integer identifier that is only + // unique within a single DOMMediaStream, which is + // used by MediaStreamGraph + TrackID numeric_track_id, int level, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport, nsAutoPtr filter) : MediaPipelineReceive(pc, main_thread, sts_thread, - stream, track_id, level, conduit, rtp_transport, - rtcp_transport, filter), + stream, media_stream_track_id, level, conduit, + rtp_transport, rtcp_transport, filter), listener_(new PipelineListener(stream->AsSourceStream(), - track_id, conduit)) { + numeric_track_id, conduit)) { } virtual void DetachMediaStream() MOZ_OVERRIDE { @@ -568,6 +575,7 @@ } virtual nsresult Init() MOZ_OVERRIDE; + virtual bool IsVideo() const MOZ_OVERRIDE { return false; } private: // Separate class to allow ref counting @@ -610,17 +618,24 @@ nsCOMPtr main_thread, nsCOMPtr sts_thread, MediaStream *stream, - TrackID track_id, + // This comes from an msid attribute. Everywhere + // but MediaStreamGraph uses this. + const std::string& media_stream_track_id, + // This is an integer identifier that is only + // unique within a single DOMMediaStream, which is + // used by MediaStreamGraph + TrackID numeric_track_id, int level, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport, nsAutoPtr filter) : MediaPipelineReceive(pc, main_thread, sts_thread, - stream, track_id, level, conduit, rtp_transport, - rtcp_transport, filter), + stream, media_stream_track_id, level, conduit, + rtp_transport, rtcp_transport, filter), renderer_(new PipelineRenderer(this)), - listener_(new PipelineListener(stream->AsSourceStream(), track_id)) { + listener_(new PipelineListener(stream->AsSourceStream(), + numeric_track_id)) { } // Called on the main thread. @@ -638,6 +653,7 @@ } virtual nsresult Init() MOZ_OVERRIDE; + virtual bool IsVideo() const MOZ_OVERRIDE { return true; } private: class PipelineRenderer : public VideoRenderer { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -16,6 +16,7 @@ #include "signaling/src/jsep/JsepTransport.h" #ifdef MOZILLA_INTERNAL_API +#include "MediaStreamTrack.h" #include "nsIPrincipal.h" #include "nsIDocument.h" #include "mozilla/Preferences.h" @@ -338,14 +339,18 @@ } } + // We need to choose a numeric track id for MediaStreamGraph to use. Must be + // unique within the MediaStream, so level + 1 should be fine (cannot use 0). + TrackID numericTrackId = aTrackPair.mLevel + 1; + if (aTrack.GetMediaType() == SdpMediaSection::kAudio) { pipeline = new MediaPipelineReceiveAudio( mPC->GetHandle(), mPC->GetMainThread().get(), mPC->GetSTSThread(), stream->GetMediaStream()->GetStream(), - // Use the level + 1 as the track id. 0 is forbidden - aTrackPair.mLevel + 1, + aTrack.GetTrackId(), + numericTrackId, aTrackPair.mLevel, static_cast(aConduit.get()), // Ugly downcast. aRtpFlow, @@ -358,8 +363,8 @@ mPC->GetMainThread().get(), mPC->GetSTSThread(), stream->GetMediaStream()->GetStream(), - // Use the level + 1 as the track id. 0 is forbidden - aTrackPair.mLevel + 1, + aTrack.GetTrackId(), + numericTrackId, aTrackPair.mLevel, static_cast(aConduit.get()), // Ugly downcast. aRtpFlow, @@ -377,7 +382,15 @@ return rv; } - stream->StorePipeline(aTrackPair.mLevel, SdpMediaSection::kVideo, pipeline); + rv = stream->StorePipeline(aTrack.GetTrackId(), + RefPtr(pipeline)); + if (NS_FAILED(rv)) { + MOZ_MTLOG(ML_ERROR, "Couldn't store receiving pipeline " << + static_cast(rv)); + return rv; + } + + stream->SyncPipeline(pipeline); return NS_OK; } @@ -415,10 +428,17 @@ // Now we have all the pieces, create the pipeline RefPtr pipeline = new MediaPipelineTransmit( - mPC->GetHandle(), mPC->GetMainThread().get(), mPC->GetSTSThread(), - stream->GetMediaStream(), aTrackPair.mLevel, - aTrack.GetMediaType() == SdpMediaSection::kVideo, aConduit, aRtpFlow, - aRtcpFlow, filter); + mPC->GetHandle(), + mPC->GetMainThread().get(), + mPC->GetSTSThread(), + stream->GetMediaStream(), + aTrack.GetTrackId(), + aTrackPair.mLevel, + aTrack.GetMediaType() == SdpMediaSection::kVideo, + aConduit, + aRtpFlow, + aRtcpFlow, + filter); #ifdef MOZILLA_INTERNAL_API // implement checking for peerIdentity (where failure == black/silence) @@ -438,7 +458,13 @@ return rv; } - stream->StorePipeline(aTrackPair.mLevel, pipeline); + rv = stream->StorePipeline(aTrack.GetTrackId(), + RefPtr(pipeline)); + if (NS_FAILED(rv)) { + MOZ_MTLOG(ML_ERROR, "Couldn't store receiving pipeline " << + static_cast(rv)); + return rv; + } return NS_OK; } @@ -582,7 +608,7 @@ // The two sides of a send/receive pair of conduits each keep a raw // pointer to the other, and are responsible for cleanly shutting down. RefPtr conduit = VideoSessionConduit::Create( - static_cast(peerConduit.get())); + static_cast(peerConduit.get()), receiving); if (!conduit) { MOZ_MTLOG(ML_ERROR, "Could not create video conduit"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -178,10 +178,18 @@ class TracksAvailableCallback : public DOMMediaStream::OnTracksAvailableCallback { public: - TracksAvailableCallback(DOMMediaStream::TrackTypeHints aTrackTypeHints, + TracksAvailableCallback(const std::list& audioTrackIds, + const std::list& videoTrackIds, + const std::set& preexistingTrackIds, nsRefPtr aObserver) - : DOMMediaStream::OnTracksAvailableCallback(aTrackTypeHints) - , mObserver(aObserver) {} + : DOMMediaStream::OnTracksAvailableCallback( + // Once DOMMediaStream can handle more than one of each, this will change. + (audioTrackIds.empty() ? 0 : DOMMediaStream::HINT_CONTENTS_AUDIO) | + (videoTrackIds.empty() ? 0 : DOMMediaStream::HINT_CONTENTS_VIDEO)) + , mObserver(aObserver) + , mAudioTrackIds(audioTrackIds) + , mVideoTrackIds(videoTrackIds) + , mPreexistingTrackIds(preexistingTrackIds) {} virtual void NotifyTracksAvailable(DOMMediaStream* aStream) MOZ_OVERRIDE { @@ -197,22 +205,68 @@ nsTArray> tracks; aStream->GetTracks(tracks); - for (uint32_t i = 0; i < tracks.Length(); i++) { + + for (size_t i = 0; i < tracks.Length(); i++) { + if (mPreexistingTrackIds.count( + PeerConnectionImpl::GetTrackId(*tracks[i]))) { + continue; + } + + AssignNextIdToTrack(tracks[i]); JSErrorResult rv; mObserver->OnAddTrack(*tracks[i], rv); + CSFLogInfo(logTag, "Calling OnAddTrack(%s)", + PeerConnectionImpl::GetTrackId(*tracks[i]).c_str()); if (rv.Failed()) { - CSFLogError(logTag, ": OnAddTrack(%d) failed! Error: %u", i, - static_cast(rv.ErrorCode())); + CSFLogError(logTag, ": OnAddTrack(%u) failed! Error: %u", + static_cast(i), + static_cast(rv.ErrorCode())); } } + JSErrorResult rv; + CSFLogInfo(logTag, "Calling OnAddStream"); mObserver->OnAddStream(*aStream, rv); if (rv.Failed()) { - CSFLogError(logTag, ": OnAddStream() failed! Error: %u", static_cast(rv.ErrorCode())); + CSFLogError(logTag, ": OnAddStream() failed! Error: %u", + static_cast(rv.ErrorCode())); + } + + if (!mAudioTrackIds.empty() || !mVideoTrackIds.empty()) { + CSFLogError(logTag, "Failed to assign %u audio and %u video tracks!", + static_cast(mAudioTrackIds.size()), + static_cast(mVideoTrackIds.size())); + } + } + + void AssignNextIdToTrack(MediaStreamTrack* track) + { + std::list* trackIds; + + if (track->AsAudioStreamTrack()) { + trackIds = &mAudioTrackIds; + } else if (track->AsVideoStreamTrack()) { + trackIds = &mVideoTrackIds; + } else { + MOZ_ASSERT(false, "Track is neither an AudioStreamTrack nor " + "VideoStreamTrack"); + return; + } + + if (trackIds->empty()) { + MOZ_ASSERT(false, "Too many new MediaStreamTracks were created"); + return; } + + track->AssignId(NS_ConvertUTF8toUTF16(trackIds->front().c_str())); + trackIds->pop_front(); } + private: nsRefPtr mObserver; + std::list mAudioTrackIds; + std::list mVideoTrackIds; + const std::set mPreexistingTrackIds; }; #endif @@ -1592,15 +1646,11 @@ } else { // Add the tracks. This code is pretty complicated because the tracks // come in arbitrary orders and we want to group them by streamId. - // We go through all the tracks and then for each track that represents - // a new stream id, go through the rest of the tracks and deal with - // them at once. size_t numTracks = mJsepSession->GetRemoteTrackCount(); - MOZ_ASSERT(numTracks <= 3); - bool hasAudio = false; - bool hasVideo = false; - std::set streamsToNotify; + // Group tracks by stream id + std::map>> tracksByStreamId; + for (size_t i = 0; i < numTracks; ++i) { RefPtr track; nrv = mJsepSession->GetRemoteTrack(i, &track); @@ -1616,10 +1666,17 @@ continue; } + tracksByStreamId[track->GetStreamId()].push_back(track); + } + + for (auto i = tracksByStreamId.begin(); i != tracksByStreamId.end(); ++i) { + std::string streamId = i->first; + std::vector>& tracks = i->second; + nsRefPtr info = - mMedia->GetRemoteStreamById(track->GetStreamId()); + mMedia->GetRemoteStreamById(streamId); if (!info) { - nsresult nrv = CreateRemoteSourceStreamInfo(&info, track->GetStreamId()); + nsresult nrv = CreateRemoteSourceStreamInfo(&info, streamId); if (NS_FAILED(nrv)) { pco->OnSetRemoteDescriptionError( kInternalError, @@ -1638,31 +1695,35 @@ } } - streamsToNotify.insert(track->GetStreamId()); + // TODO(bug 1017888): Only get new tracks for renegotiation + std::list newAudioTrackIds; + std::list newVideoTrackIds; + // TODO(bug 1017888): Fill in for renegotiation + std::set preexistingTrackIds; + + for (auto j = tracks.begin(); j != tracks.end(); ++j) { + RefPtr track = *j; + if (track->GetMediaType() == SdpMediaSection::kAudio) { + info->AddTrack(track->GetTrackId()); + newAudioTrackIds.push_back(track->GetTrackId()); + } else if (track->GetMediaType() == SdpMediaSection::kVideo) { + info->AddTrack(track->GetTrackId()); + newVideoTrackIds.push_back(track->GetTrackId()); + } else { + MOZ_ASSERT(false); + continue; + } - if (track->GetMediaType() == mozilla::SdpMediaSection::kAudio) { - MOZ_ASSERT(!hasAudio); - (void)hasAudio; - hasAudio = true; - info->mTrackTypeHints |= DOMMediaStream::HINT_CONTENTS_AUDIO; - } else if (track->GetMediaType() == mozilla::SdpMediaSection::kVideo) { - MOZ_ASSERT(!hasVideo); - (void)hasVideo; - hasVideo = true; - info->mTrackTypeHints |= DOMMediaStream::HINT_CONTENTS_VIDEO; } - } - for (auto i = streamsToNotify.begin(); i != streamsToNotify.end(); ++i) { // Now that the streams are all set up, notify about track availability. // TODO(bug 1017888): Suppress on renegotiation when no change. - nsRefPtr info = - mMedia->GetRemoteStreamById(*i); - MOZ_ASSERT(info); - #ifdef MOZILLA_INTERNAL_API TracksAvailableCallback* tracksAvailableCallback = - new TracksAvailableCallback(info->mTrackTypeHints, pco); + new TracksAvailableCallback(newAudioTrackIds, + newVideoTrackIds, + preexistingTrackIds, + pco); info->GetMediaStream()->OnTracksAvailable(tracksAvailableCallback); #else pco->OnAddStream(info->GetMediaStream(), jrv); @@ -1873,6 +1934,18 @@ } #endif +std::string +PeerConnectionImpl::GetTrackId(const MediaStreamTrack& aTrack) +{ +#ifdef MOZILLA_INTERNAL_API + nsString wideTrackId; + aTrack.GetId(wideTrackId); + return NS_ConvertUTF16toUTF8(wideTrackId).get(); +#else + return aTrack.GetId(); +#endif +} + nsresult PeerConnectionImpl::AddTrack(MediaStreamTrack& aTrack, const Sequence>& aStreams) @@ -1894,48 +1967,28 @@ CSFLogError(logTag, "%s: Track is not in stream", __FUNCTION__); return NS_ERROR_FAILURE; } - uint32_t hints = aMediaStream.GetHintContents() & - ((aTrack.AsAudioStreamTrack()? DOMMediaStream::HINT_CONTENTS_AUDIO : 0) | - (aTrack.AsVideoStreamTrack()? DOMMediaStream::HINT_CONTENTS_VIDEO : 0)); - - // XXX Remove this check once addStream has an error callback - // available and/or we have plumbing to handle multiple - // local audio streams. bug 1056650 - if ((hints & DOMMediaStream::HINT_CONTENTS_AUDIO) && - mNumAudioStreams > 0) { - CSFLogError(logTag, "%s: Only one local audio stream is supported for now", - __FUNCTION__); - return NS_ERROR_FAILURE; - } - - // XXX Remove this check once addStream has an error callback - // available and/or we have plumbing to handle multiple - // local video streams. bug 1056650 - if ((hints & DOMMediaStream::HINT_CONTENTS_VIDEO) && - mNumVideoStreams > 0) { - CSFLogError(logTag, "%s: Only one local video stream is supported for now", - __FUNCTION__); - return NS_ERROR_FAILURE; - } - uint32_t num = mMedia->LocalStreamsLength(); std::string streamId; - // TODO(bug 1089798): These ids should really come from the MS. - nsresult res = mMedia->AddStream(&aMediaStream, hints, &streamId); + std::string trackId = PeerConnectionImpl::GetTrackId(aTrack); + // TODO(bug 1089798): streamId should really come from the MS. + nsresult res = mMedia->AddTrack(&aMediaStream, &streamId, trackId); if (NS_FAILED(res)) { return res; } + CSFLogDebug(logTag, "Added track (%s) to stream %p", + trackId.c_str(), &aMediaStream); + if (num != mMedia->LocalStreamsLength()) { aMediaStream.AddPrincipalChangeObserver(this); } - if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) { + if (aTrack.AsAudioStreamTrack()) { res = mJsepSession->AddTrack(new JsepTrack( mozilla::SdpMediaSection::kAudio, streamId, - "audio_track_id", + trackId, JsepTrack::kJsepTrackSending)); if (NS_FAILED(res)) { std::string errorString = mJsepSession->GetLastError(); @@ -1946,11 +1999,19 @@ mNumAudioStreams++; } - if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) { + if (aTrack.AsVideoStreamTrack()) { +#ifdef MOZILLA_INTERNAL_API + if (!Preferences::GetBool("media.peerconnection.video.enabled", true)) { + // Before this code was moved, this would silently ignore just like it + // does now. Is this actually what we want to do? + return NS_OK; + } +#endif + res = mJsepSession->AddTrack(new JsepTrack( mozilla::SdpMediaSection::kVideo, streamId, - "video_track_id", + trackId, JsepTrack::kJsepTrackSending)); if (NS_FAILED(res)) { std::string errorString = mJsepSession->GetLastError(); @@ -1975,6 +2036,15 @@ DOMMediaStream& aStream) { PC_AUTO_ENTER_API_CALL(true); + JSErrorResult jrv; + nsRefPtr pco = do_QueryObjectReferent(mPCObserver); + if (!pco) { + return NS_ERROR_UNEXPECTED; + } + + std::string origTrackId = PeerConnectionImpl::GetTrackId(aThisTrack); + std::string newTrackId = PeerConnectionImpl::GetTrackId(aWithTrack); + // TODO: Do an aStream.HasTrack() check on both track args someday. // // The proposed API will be that both tracks must already be in the same @@ -1982,9 +2052,15 @@ // track per type, we allow replacement with an outside track not already // in the same stream. This works because sync happens receiver-side and // timestamps are tied to capture. - // - // Since a track may be replaced more than once, the track being replaced - // may not be in the stream either, so we check neither arg right now. + + if (!aStream.HasTrack(aThisTrack)) { + CSFLogError(logTag, "Track to replace (%s) is not in stream", + origTrackId.c_str()); + pco->OnReplaceTrackError(kInvalidMediastreamTrack, + ObString("Track to replace is not in stream"), + jrv); + return NS_OK; + } // XXX This MUST be addressed when we add multiple tracks of a type!! // This is needed because the track IDs used by MSG are from TrackUnion @@ -1995,41 +2071,38 @@ // TrackUnionStream's TrackID's, this value won't currently match what is used in // MediaPipelineTransmit. Bug 1056652 // TrackID thisID = aThisTrack.GetTrackID(); - TrackID withID = aWithTrack.GetTrackID(); + // - bool success = false; - for(uint32_t i = 0; i < media()->LocalStreamsLength(); ++i) { - LocalSourceStreamInfo *info = media()->GetLocalStreamByIndex(i); - // XXX use type instead of TrackID - bug 1056650 - int pipeline = info->HasTrackType(&aStream, !!(aThisTrack.AsVideoStreamTrack())); - if (pipeline >= 0) { - // XXX GetStream() will likely be invalid once a track can be in more than one - info->ReplaceTrack(pipeline, aWithTrack.GetStream(), withID); - success = true; - break; - } - } - if (!success) { - return NS_ERROR_FAILURE; - } + nsRefPtr info = + media()->GetLocalStreamByDomStream(aStream); - nsRefPtr pco = do_QueryObjectReferent(mPCObserver); - if (!pco) { - return NS_ERROR_UNEXPECTED; + if (!info || !info->HasTrack(origTrackId)) { + CSFLogError(logTag, "Track to replace (%s) was never added", + origTrackId.c_str()); + pco->OnReplaceTrackError(kInvalidMediastreamTrack, + ObString("Track to replace was never added"), + jrv); + return NS_OK; } - JSErrorResult rv; - if (success) { - pco->OnReplaceTrackSuccess(rv); - } else { + nsresult rv = + info->ReplaceTrack(origTrackId, aWithTrack.GetStream(), newTrackId); + if (NS_FAILED(rv)) { + CSFLogError(logTag, "Failed to replace track (%s)", + origTrackId.c_str()); pco->OnReplaceTrackError(kInternalError, ObString("Failed to replace track"), - rv); + jrv); + return NS_OK; } - if (rv.Failed()) { + + pco->OnReplaceTrackSuccess(jrv); + + if (jrv.Failed()) { CSFLogError(logTag, "Error firing replaceTrack callback"); return NS_ERROR_UNEXPECTED; } + return NS_OK; } @@ -2674,16 +2747,19 @@ // Gather up pipelines from mMedia so they may be inspected on STS + std::string trackId; + if (aSelector) { + trackId = PeerConnectionImpl::GetTrackId(*aSelector); + } + for (int i = 0, len = mMedia->LocalStreamsLength(); i < len; i++) { auto& pipelines = mMedia->GetLocalStreamByIndex(i)->GetPipelines(); if (aSelector) { if (mMedia->GetLocalStreamByIndex(i)->GetMediaStream()-> HasTrack(*aSelector)) { - // XXX use type instead of TrackID - bug 1056650 - for (auto it = pipelines.begin(); it != pipelines.end(); ++it) { - if (it->second->IsVideo() == !!aSelector->AsVideoStreamTrack()) { - query->pipelines.AppendElement(it->second); - } + auto it = pipelines.find(trackId); + if (it != pipelines.end()) { + query->pipelines.AppendElement(it->second); } } } else { @@ -2698,10 +2774,9 @@ if (aSelector) { if (mMedia->GetRemoteStreamByIndex(i)-> GetMediaStream()->HasTrack(*aSelector)) { - for (auto it = pipelines.begin(); it != pipelines.end(); ++it) { - if (it->second->trackid() == aSelector->GetTrackID()) { - query->pipelines.AppendElement(it->second); - } + auto it = pipelines.find(trackId); + if (it != pipelines.end()) { + query->pipelines.AppendElement(it->second); } } } else { @@ -2842,7 +2917,7 @@ NS_LITERAL_STRING("audio") : NS_LITERAL_STRING("video"); nsString idstr = mediaType; idstr.AppendLiteral("_"); - idstr.AppendInt(mp.trackid()); + idstr.AppendInt(mp.level()); // Gather pipeline stats. switch (mp.direction()) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h 2015-02-03 14:33:31.000000000 +0000 @@ -601,6 +601,8 @@ virtual void PrincipalChanged(DOMMediaStream* aMediaStream) MOZ_OVERRIDE; #endif + static std::string GetTrackId(const dom::MediaStreamTrack& track); + private: virtual ~PeerConnectionImpl(); PeerConnectionImpl(const PeerConnectionImpl&rhs); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -43,144 +43,46 @@ namespace mozilla { static const char* logTag = "PeerConnectionMedia"; -static const mozilla::TrackID TRACK_AUDIO = 0; -static const mozilla::TrackID TRACK_VIDEO = 1; -/* If the ExpectAudio hint is on we will add a track at the default first - * audio track ID (0) - * FIX - Do we need to iterate over the tracks instead of taking these hints? - */ -void -LocalSourceStreamInfo::ExpectAudio(const mozilla::TrackID aID) -{ - mAudioTracks.AppendElement(aID); -} - -void -LocalSourceStreamInfo::RemoveAudio(const mozilla::TrackID aID) -{ - mAudioTracks.RemoveElement(aID); -} - -// If the ExpectVideo hint is on we will add a track at the default first -// video track ID (1). -void -LocalSourceStreamInfo::ExpectVideo(const mozilla::TrackID aID) -{ - mVideoTracks.AppendElement(aID); -} - -void -LocalSourceStreamInfo::RemoveVideo(const mozilla::TrackID aID) -{ - mVideoTracks.RemoveElement(aID); -} - -unsigned -LocalSourceStreamInfo::AudioTrackCount() -{ - return mAudioTracks.Length(); -} - -unsigned -LocalSourceStreamInfo::VideoTrackCount() -{ - return mVideoTracks.Length(); -} - -void LocalSourceStreamInfo::DetachTransport_s() +nsresult LocalSourceStreamInfo::ReplaceTrack(const std::string& oldTrackId, + DOMMediaStream* aNewStream, + const std::string& newTrackId) { - ASSERT_ON_THREAD(mParent->GetSTSThread()); - // walk through all the MediaPipelines and call the shutdown - // functions for transport. Must be on the STS thread. - for (std::map >::iterator it = - mPipelines.begin(); it != mPipelines.end(); - ++it) { - it->second->ShutdownTransport_s(); - } -} + mozilla::RefPtr pipeline = mPipelines[oldTrackId]; -void LocalSourceStreamInfo::DetachMedia_m() -{ - ASSERT_ON_THREAD(mParent->GetMainThread()); - // walk through all the MediaPipelines and call the shutdown - // functions. Must be on the main thread. - for (std::map >::iterator it = - mPipelines.begin(); it != mPipelines.end(); - ++it) { - it->second->ShutdownMedia_m(); + if (!pipeline || !mTracks.count(oldTrackId)) { + CSFLogError(logTag, "Failed to find track id %s", oldTrackId.c_str()); + return NS_ERROR_NOT_AVAILABLE; } - mAudioTracks.Clear(); - mVideoTracks.Clear(); - mMediaStream = nullptr; -} -#if 0 -// XXX bug 1056652 makes this not very useful for transmit streams -// NOTE: index is != the trackid in the MediaStream -int LocalSourceStreamInfo::HasTrack(DOMMediaStream* aStream, TrackID aTrack) -{ - if (aStream != mMediaStream) { - return -1; - } - for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) { - if (it->second->trackid_locked() == aTrack) { - return it->first; - } - } - return -1; -} -#endif + nsresult rv = + static_cast(pipeline.get())->ReplaceTrack( + aNewStream, newTrackId); + NS_ENSURE_SUCCESS(rv, rv); -// NOTE: index is != the trackid in the MediaStream -int LocalSourceStreamInfo::HasTrackType(DOMMediaStream* aStream, bool aIsVideo) -{ - if (aStream != mMediaStream) { - return -1; - } - for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) { - if (it->second->IsVideo() == aIsVideo) { - return it->first; - } - } - return -1; -} + mTracks.erase(oldTrackId); + mTracks.insert(newTrackId); -// XXX revisit once we support multiple tracks of a type - bug 1056650 -nsresult LocalSourceStreamInfo::ReplaceTrack(int aMLine, - DOMMediaStream* aNewStream, - TrackID aNewTrack) -{ - // Note aMLine != aOldTrack! - mozilla::RefPtr pipeline = mPipelines[aMLine]; - MOZ_ASSERT(pipeline); - if (NS_SUCCEEDED(static_cast(pipeline.get())->ReplaceTrack(aNewStream, aNewTrack))) { - return NS_OK; - } - return NS_ERROR_FAILURE; + return NS_OK; } -void RemoteSourceStreamInfo::DetachTransport_s() +void SourceStreamInfo::DetachTransport_s() { ASSERT_ON_THREAD(mParent->GetSTSThread()); // walk through all the MediaPipelines and call the shutdown // transport functions. Must be on the STS thread. - for (std::map >::iterator it = - mPipelines.begin(); it != mPipelines.end(); - ++it) { + for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) { it->second->ShutdownTransport_s(); } } -void RemoteSourceStreamInfo::DetachMedia_m() +void SourceStreamInfo::DetachMedia_m() { ASSERT_ON_THREAD(mParent->GetMainThread()); // walk through all the MediaPipelines and call the shutdown // media functions. Must be on the main thread. - for (std::map >::iterator it = - mPipelines.begin(); it != mPipelines.end(); - ++it) { + for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) { it->second->ShutdownMedia_m(); } mMediaStream = nullptr; @@ -213,6 +115,7 @@ CSFLogInfo(logTag, "%s: Proxy Available: %d", __FUNCTION__, (int)result); if (NS_SUCCEEDED(result) && proxyinfo) { + CSFLogInfo(logTag, "%s: Had proxyinfo", __FUNCTION__); nsresult rv; nsCString httpsProxyHost; int32_t httpsProxyPort; @@ -243,7 +146,7 @@ if (result != NS_ERROR_ABORT) { // NS_ERROR_ABORT means that the PeerConnectionMedia is no longer waiting pcm_->mProxyResolveCompleted = true; - pcm_->GatherIfReady(); + pcm_->FlushIceCtxOperationQueueIfReady(); } return NS_OK; @@ -261,7 +164,6 @@ mUuidGen(MakeUnique()), mMainThread(mParent->GetMainThread()), mSTSThread(mParent->GetSTSThread()), - mTransportsUpdated(false), mProxyResolveCompleted(false) { nsresult rv; @@ -400,7 +302,6 @@ // TODO(bug 1017888): Need to deal properly with renegotatiation. // For now just start gathering. - mTransportsUpdated = true; GatherIfReady(); } @@ -417,9 +318,6 @@ return rv; } - // TODO(bug 1099318): We are forced to do receive then transmit, because of - // a bug in the VideoConduit code. This will need to be fixed for - // renegotiation. if (pair->mReceiving) { rv = factory.CreateMediaPipeline(*pair, *pair->mReceiving); if (NS_FAILED(rv)) { @@ -467,16 +365,17 @@ } } - RUN_ON_THREAD(GetSTSThread(), - WrapRunnable( - RefPtr(this), - &PeerConnectionMedia::StartIceChecks_s, - session.IsIceControlling(), - session.RemoteIsIceLite(), - // Copy, just in case API changes to return a ref - std::vector(session.GetIceOptions()), - numComponentsByLevel), - NS_DISPATCH_NORMAL); + nsRefPtr runnable( + WrapRunnable( + RefPtr(this), + &PeerConnectionMedia::StartIceChecks_s, + session.IsIceControlling(), + session.RemoteIsIceLite(), + // Copy, just in case API changes to return a ref + std::vector(session.GetIceOptions()), + numComponentsByLevel)); + + PerformOrEnqueueIceCtxOperation(runnable); } void @@ -557,19 +456,45 @@ } void -PeerConnectionMedia::GatherIfReady() { +PeerConnectionMedia::FlushIceCtxOperationQueueIfReady() +{ ASSERT_ON_THREAD(mMainThread); - if (mTransportsUpdated && mProxyResolveCompleted) { - RUN_ON_THREAD(GetSTSThread(), - WrapRunnable( - RefPtr(this), - &PeerConnectionMedia::EnsureIceGathering_s), - NS_DISPATCH_NORMAL); + if (IsIceCtxReady()) { + for (auto i = mQueuedIceCtxOperations.begin(); + i != mQueuedIceCtxOperations.end(); + ++i) { + GetSTSThread()->Dispatch(*i, NS_DISPATCH_NORMAL); + } + mQueuedIceCtxOperations.clear(); + } +} + +void +PeerConnectionMedia::PerformOrEnqueueIceCtxOperation( + const nsRefPtr& runnable) +{ + ASSERT_ON_THREAD(mMainThread); + + if (IsIceCtxReady()) { + GetSTSThread()->Dispatch(runnable, NS_DISPATCH_NORMAL); + } else { + mQueuedIceCtxOperations.push_back(runnable); } } void +PeerConnectionMedia::GatherIfReady() { + ASSERT_ON_THREAD(mMainThread); + + nsRefPtr runnable(WrapRunnable( + RefPtr(this), + &PeerConnectionMedia::EnsureIceGathering_s)); + + PerformOrEnqueueIceCtxOperation(runnable); +} + +void PeerConnectionMedia::EnsureIceGathering_s() { if (mIceCtx->gathering_state() == NrIceCtx::ICE_CTX_GATHER_INIT) { if (mProxyServer) { @@ -637,9 +562,9 @@ } nsresult -PeerConnectionMedia::AddStream(DOMMediaStream* aMediaStream, - uint32_t hints, - std::string *stream_id) +PeerConnectionMedia::AddTrack(DOMMediaStream* aMediaStream, + std::string* streamId, + const std::string& trackId) { ASSERT_ON_THREAD(mMainThread); @@ -650,38 +575,9 @@ CSFLogDebug(logTag, "%s: MediaStream: %p", __FUNCTION__, aMediaStream); - // Adding tracks here based on nsDOMMediaStream expectation settings -#ifdef MOZILLA_INTERNAL_API - if (!Preferences::GetBool("media.peerconnection.video.enabled", true)) { - hints &= ~(DOMMediaStream::HINT_CONTENTS_VIDEO); - } -#endif + nsRefPtr localSourceStream = + GetLocalStreamByDomStream(*aMediaStream); - if (!(hints & (DOMMediaStream::HINT_CONTENTS_AUDIO | - DOMMediaStream::HINT_CONTENTS_VIDEO))) { - CSFLogDebug(logTag, "Empty Stream !!"); - return NS_OK; - } - - // Now see if we already have this stream or another stream with - // tracks of the same type, since we only allow one track of each type. - // TODO(ekr@rtfm.com): remove this when multiple of each stream - // is allowed bug 1056650 - nsRefPtr localSourceStream = nullptr; - - for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) { - auto& lss = mLocalSourceStreams[u]; - if (((hints & DOMMediaStream::HINT_CONTENTS_AUDIO) && lss->AudioTrackCount()) || - ((hints & DOMMediaStream::HINT_CONTENTS_VIDEO) && lss->VideoTrackCount())) { - CSFLogError(logTag, "Only one stream of any given type allowed"); - return NS_ERROR_FAILURE; - } - if (aMediaStream == lss->GetMediaStream()) { - localSourceStream = lss; - *stream_id = lss->GetId(); - break; - } - } if (!localSourceStream) { std::string id; if (!mUuidGen->Generate(&id)) { @@ -691,50 +587,38 @@ localSourceStream = new LocalSourceStreamInfo(aMediaStream, this, id); mLocalSourceStreams.AppendElement(localSourceStream); - *stream_id = id; - } - - if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) { - localSourceStream->ExpectAudio(TRACK_AUDIO); } - if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) { - localSourceStream->ExpectVideo(TRACK_VIDEO); - } + localSourceStream->AddTrack(trackId); + *streamId = localSourceStream->GetId(); return NS_OK; } nsresult -PeerConnectionMedia::RemoveStream(DOMMediaStream* aMediaStream, - uint32_t hints, - uint32_t *stream_id) +PeerConnectionMedia::RemoveTrack(DOMMediaStream* aMediaStream, + const std::string& trackId) { MOZ_ASSERT(aMediaStream); ASSERT_ON_THREAD(mMainThread); - CSFLogDebug(logTag, "%s: MediaStream: %p", - __FUNCTION__, aMediaStream); - - for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) { - nsRefPtr localSourceStream = mLocalSourceStreams[u]; - if (localSourceStream->GetMediaStream() == aMediaStream) { - *stream_id = u; + CSFLogDebug(logTag, "%s: MediaStream: %p", __FUNCTION__, aMediaStream); - if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) { - localSourceStream->RemoveAudio(TRACK_AUDIO); - } - if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) { - localSourceStream->RemoveAudio(TRACK_VIDEO); - } - if (!(localSourceStream->AudioTrackCount() + - localSourceStream->VideoTrackCount())) { - mLocalSourceStreams.RemoveElementAt(u); - } - return NS_OK; + size_t i; + for (i = 0; i < mLocalSourceStreams.Length(); ++i) { + if (mLocalSourceStreams[i]->GetMediaStream() == aMediaStream) { + break; } } - return NS_ERROR_ILLEGAL_VALUE; + if (i == mLocalSourceStreams.Length()) { + return NS_ERROR_ILLEGAL_VALUE; + } + + mLocalSourceStreams[i]->RemoveTrack(trackId); + if (!(mLocalSourceStreams[i]->GetTrackCount())) { + mLocalSourceStreams.RemoveElementAt(i); + } + return NS_OK; } void @@ -834,6 +718,33 @@ return nullptr; } +LocalSourceStreamInfo* +PeerConnectionMedia::GetLocalStreamByDomStream(const DOMMediaStream& stream) +{ + ASSERT_ON_THREAD(mMainThread); + for (size_t i = 0; i < mLocalSourceStreams.Length(); ++i) { + if (&stream == mLocalSourceStreams[i]->GetMediaStream()) { + return mLocalSourceStreams[i]; + } + } + + return nullptr; +} + +RemoteSourceStreamInfo* +PeerConnectionMedia::GetRemoteStreamByDomStream(const DOMMediaStream& stream) +{ + ASSERT_ON_THREAD(mMainThread); + for (size_t i = 0; i < mRemoteSourceStreams.Length(); ++i) { + if (&stream == mRemoteSourceStreams[i]->GetMediaStream()) { + return mRemoteSourceStreams[i]; + } + } + + MOZ_ASSERT(false); + return nullptr; +} + RemoteSourceStreamInfo* PeerConnectionMedia::GetRemoteStreamByIndex(size_t aIndex) { @@ -879,19 +790,19 @@ bool PeerConnectionMedia::UpdateFilterFromRemoteDescription_m( - int aMLine, + const std::string& trackId, nsAutoPtr filter) { ASSERT_ON_THREAD(mMainThread); RefPtr receive; for (size_t i = 0; !receive && i < mRemoteSourceStreams.Length(); ++i) { - receive = mRemoteSourceStreams[i]->GetPipelineByLevel_m(aMLine); + receive = mRemoteSourceStreams[i]->GetPipelineByTrackId_m(trackId); } RefPtr transmit; for (size_t i = 0; !transmit && i < mLocalSourceStreams.Length(); ++i) { - transmit = mLocalSourceStreams[i]->GetPipelineByLevel_m(aMLine); + transmit = mLocalSourceStreams[i]->GetPipelineByTrackId_m(trackId); } if (receive && transmit) { @@ -909,8 +820,8 @@ NS_DISPATCH_NORMAL); return true; } else { - CSFLogWarn(logTag, "Could not locate level %d to update filter", - static_cast(aMLine)); + CSFLogWarn(logTag, "Could not locate track %s to update filter", + trackId.c_str()); } return false; } @@ -925,27 +836,6 @@ return NS_OK; } -nsresult -PeerConnectionMedia::AddRemoteStreamHint(int aIndex, bool aIsVideo) -{ - if (aIndex < 0 || - static_cast(aIndex) >= mRemoteSourceStreams.Length()) { - return NS_ERROR_ILLEGAL_VALUE; - } - - RemoteSourceStreamInfo *pInfo = mRemoteSourceStreams.ElementAt(aIndex); - MOZ_ASSERT(pInfo); - - if (aIsVideo) { - pInfo->mTrackTypeHints |= DOMMediaStream::HINT_CONTENTS_VIDEO; - } else { - pInfo->mTrackTypeHints |= DOMMediaStream::HINT_CONTENTS_AUDIO; - } - - return NS_OK; -} - - void PeerConnectionMedia::IceGatheringStateChange_s(NrIceCtx* ctx, NrIceCtx::GatheringState state) @@ -1209,7 +1099,7 @@ } bool -LocalSourceStreamInfo::AnyCodecHasPluginID(uint64_t aPluginID) +SourceStreamInfo::AnyCodecHasPluginID(uint64_t aPluginID) { // Scan the videoConduits for this plugin ID for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) { @@ -1220,70 +1110,50 @@ return false; } -bool -RemoteSourceStreamInfo::AnyCodecHasPluginID(uint64_t aPluginID) -{ - // Scan the videoConduits for this plugin ID - for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) { - if (it->second->Conduit()->CodecPluginID() == aPluginID) { - return true; - } - } - return false; -} - -void -LocalSourceStreamInfo::StorePipeline( - int aMLine, mozilla::RefPtr aPipeline) +nsresult +SourceStreamInfo::StorePipeline( + const std::string& trackId, + const mozilla::RefPtr& aPipeline) { - MOZ_ASSERT(mPipelines.find(aMLine) == mPipelines.end()); - if (mPipelines.find(aMLine) != mPipelines.end()) { + MOZ_ASSERT(mPipelines.find(trackId) == mPipelines.end()); + if (mPipelines.find(trackId) != mPipelines.end()) { CSFLogError(logTag, "%s: Storing duplicate track", __FUNCTION__); - return; + return NS_ERROR_FAILURE; } - //TODO: Revisit once we start supporting multiple streams or multiple tracks - // of same type bug 1056650 - mPipelines[aMLine] = aPipeline; + mPipelines[trackId] = aPipeline; + return NS_OK; } void -RemoteSourceStreamInfo::StorePipeline( - int aMLine, bool aIsVideo, +RemoteSourceStreamInfo::SyncPipeline( mozilla::RefPtr aPipeline) { - MOZ_ASSERT(mPipelines.find(aMLine) == mPipelines.end()); - if (mPipelines.find(aMLine) != mPipelines.end()) { - CSFLogError(logTag, "%s: Request to store duplicate track %d", __FUNCTION__, aMLine); - return; - } - CSFLogDebug(logTag, "%s track %d %s = %p", __FUNCTION__, aMLine, aIsVideo ? "video" : "audio", - aPipeline.get()); - // See if we have both audio and video here, and if so cross the streams and sync them - // XXX Needs to be adjusted when we support multiple streams of the same type bug 1056650 - for (std::map::iterator it = mTypes.begin(); it != mTypes.end(); ++it) { - if (it->second != aIsVideo) { + // See if we have both audio and video here, and if so cross the streams and + // sync them + // TODO: Do we need to prevent multiple syncs if there is more than one audio + // or video track in a single media stream? What are we supposed to do in this + // case? + for (auto i = mPipelines.begin(); i != mPipelines.end(); ++i) { + if (i->second->IsVideo() != aPipeline->IsVideo()) { // Ok, we have one video, one non-video - cross the streams! - mozilla::WebrtcAudioConduit *audio_conduit = static_cast - (aIsVideo ? - mPipelines[it->first]->Conduit() : - aPipeline->Conduit()); - mozilla::WebrtcVideoConduit *video_conduit = static_cast - (aIsVideo ? - aPipeline->Conduit() : - mPipelines[it->first]->Conduit()); + mozilla::WebrtcAudioConduit *audio_conduit = + static_cast(aPipeline->IsVideo() ? + i->second->Conduit() : + aPipeline->Conduit()); + mozilla::WebrtcVideoConduit *video_conduit = + static_cast(aPipeline->IsVideo() ? + aPipeline->Conduit() : + i->second->Conduit()); video_conduit->SyncTo(audio_conduit); - CSFLogDebug(logTag, "Syncing %p to %p, %d to %d", video_conduit, audio_conduit, - aMLine, it->first); + CSFLogDebug(logTag, "Syncing %p to %p, %s to %s", + video_conduit, audio_conduit, + i->first.c_str(), aPipeline->trackid().c_str()); } } - //TODO: Revisit once we start supporting multiple streams or multiple tracks - // of same type bug 1056650 - mPipelines[aMLine] = aPipeline; - //TODO: move to attribute on Pipeline - mTypes[aMLine] = aIsVideo; } -RefPtr SourceStreamInfo::GetPipelineByLevel_m(int aMLine) { +RefPtr SourceStreamInfo::GetPipelineByTrackId_m( + const std::string& trackId) { ASSERT_ON_THREAD(mParent->GetMainThread()); // Refuse to hand out references if we're tearing down. @@ -1292,10 +1162,8 @@ // RefPtr, since that reference won't be the last one // standing) if (mMediaStream) { - for (auto p = mPipelines.begin(); p != mPipelines.end(); ++p) { - if (p->second->level() == aMLine) { - return p->second; - } + if (mPipelines.count(trackId)) { + return mPipelines[trackId]; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h 2015-02-03 14:33:31.000000000 +0000 @@ -198,18 +198,38 @@ return mMediaStream; } + nsresult StorePipeline( + const std::string& trackId, + const mozilla::RefPtr& aPipeline); + + void AddTrack(const std::string& trackId) { mTracks.insert(trackId); } + void RemoveTrack(const std::string& trackId) { mTracks.erase(trackId); } + bool HasTrack(const std::string& trackId) const + { + return !!mTracks.count(trackId); + } + size_t GetTrackCount() const { return mTracks.size(); } + // This method exists for stats and the unittests. // It allows visibility into the pipelines and flows. - const std::map>& + const std::map>& GetPipelines() const { return mPipelines; } - mozilla::RefPtr GetPipelineByLevel_m(int aMLine); + mozilla::RefPtr GetPipelineByTrackId_m( + const std::string& trackId); const std::string& GetId() const { return mId; } + void DetachTransport_s(); + void DetachMedia_m(); + bool AnyCodecHasPluginID(uint64_t aPluginID); protected: - std::map> mPipelines; nsRefPtr mMediaStream; PeerConnectionMedia *mParent; const std::string mId; + // These get set up before we generate our local description, the pipelines + // are set up once offer/answer completes. + std::set mTracks; + // Indexed by track id, might contain pipelines for removed tracks + std::map> mPipelines; }; // TODO(ekr@rtfm.com): Refactor {Local,Remote}SourceStreamInfo @@ -224,40 +244,19 @@ const std::string& aId) : SourceStreamInfo(aMediaStream, aParent, aId) {} - // Returns the mPipelines index for the track or -1. -#if 0 - int HasTrack(DOMMediaStream* aStream, mozilla::TrackID aMLine); -#endif - int HasTrackType(DOMMediaStream* aStream, bool aIsVideo); // XXX NOTE: does not change mMediaStream, even if it replaces the last track // in a LocalSourceStreamInfo. Revise when we have support for multiple tracks // of a type. - // Note aMLine != aOldTrack! It's the result of HasTrackType() - nsresult ReplaceTrack(int aMLine, DOMMediaStream* aNewStream, mozilla::TrackID aNewTrack); - - void StorePipeline(int aMLine, - mozilla::RefPtr aPipeline); + nsresult ReplaceTrack(const std::string& oldTrackId, + DOMMediaStream* aNewStream, + const std::string& aNewTrack); #ifdef MOZILLA_INTERNAL_API void UpdateSinkIdentity_m(nsIPrincipal* aPrincipal, const mozilla::PeerIdentity* aSinkIdentity); #endif - void ExpectAudio(const mozilla::TrackID); - void ExpectVideo(const mozilla::TrackID); - void RemoveAudio(const mozilla::TrackID); - void RemoveVideo(const mozilla::TrackID); - unsigned AudioTrackCount(); - unsigned VideoTrackCount(); - void DetachTransport_s(); - void DetachMedia_m(); - - bool AnyCodecHasPluginID(uint64_t aPluginID); - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LocalSourceStreamInfo) -private: - nsTArray mAudioTracks; - nsTArray mVideoTracks; }; class RemoteSourceStreamInfo : public SourceStreamInfo { @@ -266,28 +265,17 @@ RemoteSourceStreamInfo(already_AddRefed aMediaStream, PeerConnectionMedia *aParent, const std::string& aId) - : SourceStreamInfo(aMediaStream, aParent, aId), - mTrackTypeHints(0) { + : SourceStreamInfo(aMediaStream, aParent, aId) + { } - void StorePipeline(int aMLine, bool aIsVideo, - mozilla::RefPtr aPipeline); - - void DetachTransport_s(); - void DetachMedia_m(); + void SyncPipeline(RefPtr aPipeline); #ifdef MOZILLA_INTERNAL_API void UpdatePrincipal_m(nsIPrincipal* aPrincipal); #endif - bool AnyCodecHasPluginID(uint64_t aPluginID); - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteSourceStreamInfo) - -public: - DOMMediaStream::TrackTypeHints mTrackTypeHints; - private: - std::map mTypes; }; class PeerConnectionMedia : public sigslot::has_slots<> { @@ -333,14 +321,18 @@ // Handle complete media pipelines. nsresult UpdateMediaPipelines(const mozilla::JsepSession& session); - // Add a stream (main thread only) - nsresult AddStream(DOMMediaStream* aMediaStream, uint32_t hints, - std::string* stream_id); - - // Remove a stream (main thread only) - nsresult RemoveStream(DOMMediaStream* aMediaStream, - uint32_t hints, - uint32_t *stream_id); + // Add a track (main thread only) + // TODO(bug 1089798): Once DOMMediaStream has an id field, use it instead of + // letting PCMedia choose a streamId + nsresult AddTrack(DOMMediaStream* aMediaStream, + std::string* streamId, + const std::string& trackId); + + // Remove a track (main thread only) + // TODO(bug 1089798): Once DOMMediaStream has an id field, use it instead of + // passing |aMediaStream| + nsresult RemoveTrack(DOMMediaStream* aMediaStream, + const std::string& trackId); // Get a specific local stream uint32_t LocalStreamsLength() @@ -349,6 +341,8 @@ } LocalSourceStreamInfo* GetLocalStreamByIndex(int index); LocalSourceStreamInfo* GetLocalStreamById(const std::string& id); + LocalSourceStreamInfo* GetLocalStreamByDomStream( + const DOMMediaStream& stream); // Get a specific remote stream uint32_t RemoteStreamsLength() @@ -358,14 +352,16 @@ RemoteSourceStreamInfo* GetRemoteStreamByIndex(size_t index); RemoteSourceStreamInfo* GetRemoteStreamById(const std::string& id); + RemoteSourceStreamInfo* GetRemoteStreamByDomStream( + const DOMMediaStream& stream); + bool UpdateFilterFromRemoteDescription_m( - int aMLine, + const std::string& trackId, nsAutoPtr filter); // Add a remote stream. nsresult AddRemoteStream(nsRefPtr aInfo); - nsresult AddRemoteStreamHint(int aIndex, bool aIsVideo); #ifdef MOZILLA_INTERNAL_API // In cases where the peer isn't yet identified, we disable the pipeline (not @@ -466,6 +462,8 @@ const std::string& aPassword, const std::vector& aCandidateList); void GatherIfReady(); + void FlushIceCtxOperationQueueIfReady(); + void PerformOrEnqueueIceCtxOperation(const nsRefPtr& runnable); void EnsureIceGathering_s(); void StartIceChecks_s(bool aIsControlling, bool aIsIceLite, @@ -497,6 +495,9 @@ void EndOfLocalCandidates_m(const std::string& aDefaultAddr, uint16_t aDefaultPort, uint16_t aMLine); + bool IsIceCtxReady() const { + return mProxyResolveCompleted; + } // The parent PC @@ -539,8 +540,11 @@ // The STS thread. nsCOMPtr mSTSThread; - // Used to track when transports are updated and are ready to start gathering - bool mTransportsUpdated; + // Used whenever we need to dispatch a runnable to STS to tweak something + // on our ICE ctx, but are not ready to do so at the moment (eg; we are + // waiting to get a callback with our http proxy config before we start + // gathering or start checking) + std::vector> mQueuedIceCtxOperations; // Used to cancel any ongoing proxy request. nsCOMPtr mProxyRequest; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/SdpAttribute.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/SdpAttribute.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/SdpAttribute.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/SdpAttribute.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -165,6 +165,18 @@ } void +SdpMsidSemanticAttributeList::Serialize(std::ostream& os) const +{ + for (auto i = mMsidSemantics.begin(); i != mMsidSemantics.end(); ++i) { + os << "a=" << mType << ":" << i->semantic; + for (auto j = i->msids.begin(); j != i->msids.end(); ++j) { + os << " " << *j; + } + os << CRLF; + } +} + +void SdpRemoteCandidatesAttribute::Serialize(std::ostream& os) const { if (mCandidates.empty()) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/SdpAttribute.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/SdpAttribute.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/SdpAttribute.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/SdpAttribute.h 2015-02-03 14:33:31.000000000 +0000 @@ -631,6 +631,36 @@ }; /////////////////////////////////////////////////////////////////////////// +// a=msid-semantic, draft-ietf-mmusic-msid +//------------------------------------------------------------------------- +// msid-semantic-attr = "msid-semantic:" msid-semantic msid-list +// msid-semantic = token ; see RFC 4566 +// msid-list = *(" " msid-id) / " *" +class SdpMsidSemanticAttributeList : public SdpAttribute +{ +public: + SdpMsidSemanticAttributeList() : SdpAttribute(kMsidSemanticAttribute) {} + + struct MsidSemantic + { + // TODO: Once we have some more of these, we might want to make an enum + std::string semantic; + std::vector msids; + }; + + void + PushEntry(const std::string& semantic, const std::vector& msids) + { + MsidSemantic value = {semantic, msids}; + mMsidSemantics.push_back(value); + } + + virtual void Serialize(std::ostream& os) const MOZ_OVERRIDE; + + std::vector mMsidSemantics; +}; + +/////////////////////////////////////////////////////////////////////////// // a=remote-candiate, RFC5245 //------------------------------------------------------------------------- // remote-candidate-att = "remote-candidates" ":" remote-candidate diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/SdpAttributeList.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/SdpAttributeList.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/SdpAttributeList.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/SdpAttributeList.h 2015-02-03 14:33:31.000000000 +0000 @@ -70,7 +70,7 @@ virtual const std::string& GetLabel() const = 0; virtual unsigned int GetMaxptime() const = 0; virtual const std::string& GetMid() const = 0; - virtual const std::string& GetMsidSemantic() const = 0; + virtual const SdpMsidSemanticAttributeList& GetMsidSemantic() const = 0; virtual unsigned int GetPtime() const = 0; // This is "special", because it's multiple things diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp_attr_access.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp_attr_access.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp_attr_access.c 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp_attr_access.c 2015-02-03 14:33:31.000000000 +0000 @@ -605,6 +605,20 @@ } break; + case SDP_ATTR_MSID_SEMANTIC: + sstrncpy(dst_attr_p->attr.msid_semantic.semantic, + src_attr_p->attr.msid_semantic.semantic, + SDP_MAX_STRING_LEN+1); + for (i=0; i < SDP_MAX_MEDIA_STREAMS; ++i) { + if (!src_attr_p->attr.msid_semantic.msids[i]) { + break; + } + + dst_attr_p->attr.msid_semantic.msids[i] = + cpr_strdup(src_attr_p->attr.msid_semantic.msids[i]); + } + break; + case SDP_ATTR_SOURCE_FILTER: dst_attr_p->attr.source_filter.mode = src_attr_p->attr.source_filter.mode; @@ -1176,6 +1190,10 @@ for (i = 0; i < attr_p->attr.stream_data.num_group_id; i++) { SDP_FREE(attr_p->attr.stream_data.group_ids[i]); } + } else if (attr_p->type == SDP_ATTR_MSID_SEMANTIC) { + for (i = 0; i < SDP_MAX_MEDIA_STREAMS; ++i) { + SDP_FREE(attr_p->attr.msid_semantic.msids[i]); + } } /* Now free the actual attribute memory. */ @@ -1658,7 +1676,6 @@ (attr_type != SDP_ATTR_X_CONFID) && (attr_type != SDP_ATTR_LABEL) && (attr_type != SDP_ATTR_IDENTITY) && - (attr_type != SDP_ATTR_MSID_SEMANTIC) && (attr_type != SDP_ATTR_ICE_OPTIONS)) { return FALSE; } @@ -10305,7 +10322,7 @@ } sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER); - } else if ((group_num_id == 0) || (group_num_id > SDP_MAX_GROUP_STREAM_ID)){ + } else if ((group_num_id == 0) || (group_num_id > SDP_MAX_MEDIA_STREAMS)){ if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) { CSFLogError(logTag, "%s Number of group id value provided - %u is invalid", sdp_p->debug_str, (unsigned)group_num_id); @@ -10388,7 +10405,7 @@ return (SDP_INVALID_PARAMETER); } else { num_group_id = attr_p->attr.stream_data.num_group_id; - if (num_group_id == SDP_MAX_GROUP_STREAM_ID) { + if (num_group_id == SDP_MAX_MEDIA_STREAMS) { if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) { CSFLogError(logTag, "%s Max number of Group Ids already defined " "for this group line %u", sdp_p->debug_str, (unsigned)level); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c 2015-02-03 14:33:31.000000000 +0000 @@ -4101,7 +4101,7 @@ */ attr_p->attr.stream_data.num_group_id =0; - for (i=0; iattr.msid_semantic.semantic, + sizeof(attr_p->attr.msid_semantic.semantic), + " \t", + &result); + + if (result != SDP_SUCCESS) { + sdp_parse_error(sdp_p, "%s Warning: Bad msid-semantic attribute; " + "missing semantic", + sdp_p->debug_str); + sdp_p->conf_p->num_invalid_param++; + return SDP_INVALID_PARAMETER; + } + + for (i = 0; i < SDP_MAX_MEDIA_STREAMS; ++i) { + /* msid-id can be up to 64 characters long, plus null terminator */ + char temp[65]; + ptr = sdp_getnextstrtok(ptr, temp, sizeof(temp), " \t", &result); + + if (result != SDP_SUCCESS) { + break; + } + + attr_p->attr.msid_semantic.msids[i] = cpr_strdup(temp); + } + + if ((result != SDP_SUCCESS) && (result != SDP_EMPTY_TOKEN)) { + sdp_parse_error(sdp_p, "%s Warning: Bad msid-semantic attribute", + sdp_p->debug_str); + sdp_p->conf_p->num_invalid_param++; + return SDP_INVALID_PARAMETER; + } + + if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) { + SDP_PRINT("%s Parsed a=msid-semantic, %s", sdp_p->debug_str, + attr_p->attr.msid_semantic.semantic); + for (i = 0; i < SDP_MAX_MEDIA_STREAMS; ++i) { + if (!attr_p->attr.msid_semantic.msids[i]) { + break; + } + + SDP_PRINT("%s ... msid %s", sdp_p->debug_str, + attr_p->attr.msid_semantic.msids[i]); + } + } + + return SDP_SUCCESS; +} + + +sdp_result_e sdp_build_attr_msid_semantic(sdp_t *sdp_p, + sdp_attr_t *attr_p, + flex_string *fs) +{ + int i; + flex_string_sprintf(fs, "a=msid-semantic:%s", + attr_p->attr.msid_semantic.semantic); + for (i = 0; i < SDP_MAX_MEDIA_STREAMS; ++i) { + if (!attr_p->attr.msid_semantic.msids[i]) { + break; + } + + flex_string_sprintf(fs, " %s", + attr_p->attr.msid_semantic.msids[i]); + } + flex_string_sprintf(fs, "\r\n"); + return SDP_SUCCESS; +} + sdp_result_e sdp_parse_attr_ssrc(sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp.h 2015-02-03 14:33:31.000000000 +0000 @@ -556,7 +556,7 @@ /* Max number of stream ids that can be grouped together */ -#define SDP_MAX_GROUP_STREAM_ID 10 +#define SDP_MAX_MEDIA_STREAMS 32 #define SDP_MAGIC_NUM 0xabcdabcd @@ -838,9 +838,14 @@ char x_confid[SDP_MAX_STRING_LEN+1]; sdp_group_attr_e group_attr; /* FID or LS */ uint16_t num_group_id; - char * group_ids[SDP_MAX_GROUP_STREAM_ID]; + char * group_ids[SDP_MAX_MEDIA_STREAMS]; } sdp_stream_data_t; +typedef struct sdp_msid_semantic { + char semantic[SDP_MAX_STRING_LEN+1]; + char * msids[SDP_MAX_MEDIA_STREAMS]; +} sdp_msid_semantic_t; + /* * a=source-filter: * = ... @@ -1009,6 +1014,7 @@ sdp_srtp_crypto_context_t srtp_context; sdp_mptime_t mptime; sdp_stream_data_t stream_data; + sdp_msid_semantic_t msid_semantic; char unknown[SDP_MAX_STRING_LEN+1]; sdp_source_filter_t source_filter; sdp_fmtp_fb_t rtcp_fb; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp_main.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp_main.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp_main.c 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp_main.c 2015-02-03 14:33:31.000000000 +0000 @@ -185,7 +185,7 @@ {"msid", sizeof("msid"), sdp_parse_attr_msid, sdp_build_attr_msid}, {"msid-semantic", sizeof("msid-semantic"), - sdp_parse_attr_simple_string, sdp_build_attr_simple_string}, + sdp_parse_attr_msid_semantic, sdp_build_attr_msid_semantic}, {"bundle-only", sizeof("bundle-only"), sdp_parse_attr_simple_flag, sdp_build_attr_simple_flag}, {"end-of-candidates", sizeof("end-of-candidates"), diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp_private.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp_private.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp_private.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/sipcc/sdp_private.h 2015-02-03 14:33:31.000000000 +0000 @@ -80,6 +80,12 @@ const char *ptr); extern sdp_result_e sdp_build_attr_msid(sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs); +extern sdp_result_e sdp_parse_attr_msid_semantic(sdp_t *sdp_p, + sdp_attr_t *attr_p, + const char *ptr); +extern sdp_result_e sdp_build_attr_msid_semantic(sdp_t *sdp_p, + sdp_attr_t *attr_p, + flex_string *fs); extern sdp_result_e sdp_parse_attr_ssrc(sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr); extern sdp_result_e sdp_build_attr_ssrc(sdp_t *sdp_p, sdp_attr_t *attr_p, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -108,8 +108,6 @@ errorHolder); LoadSimpleString(sdp, level, SDP_ATTR_IDENTITY, SdpAttribute::kIdentityAttribute, errorHolder); - LoadSimpleString(sdp, level, SDP_ATTR_MSID_SEMANTIC, - SdpAttribute::kMsidSemanticAttribute, errorHolder); } void @@ -547,6 +545,38 @@ return true; } +bool +SipccSdpAttributeList::LoadMsidSemantics(sdp_t* sdp, uint16_t level, + SdpErrorHolder& errorHolder) +{ + auto msidSemantics = MakeUnique(); + + for (uint16_t i = 1; i < UINT16_MAX; ++i) { + sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_MSID_SEMANTIC, i); + + if (!attr) { + break; + } + + sdp_msid_semantic_t* msid_semantic = &(attr->attr.msid_semantic); + std::vector msids; + for (size_t i = 0; i < SDP_MAX_MEDIA_STREAMS; ++i) { + if (!msid_semantic->msids[i]) { + break; + } + + msids.push_back(msid_semantic->msids[i]); + } + + msidSemantics->PushEntry(msid_semantic->semantic, msids); + } + + if (!msidSemantics->mMsidSemantics.empty()) { + SetAttribute(msidSemantics.release()); + } + return true; +} + void SipccSdpAttributeList::LoadFmtp(sdp_t* sdp, uint16_t level) { @@ -829,6 +859,10 @@ if (!LoadGroups(sdp, level, errorHolder)) { return false; } + + if (!LoadMsidSemantics(sdp, level, errorHolder)) { + return false; + } } else { sdp_media_e mtype = sdp_get_media_type(sdp, level); if (mtype == SDP_MEDIA_APPLICATION) { @@ -1045,14 +1079,14 @@ return *static_cast(attr); } -const std::string& +const SdpMsidSemanticAttributeList& SipccSdpAttributeList::GetMsidSemantic() const { if (!HasAttribute(SdpAttribute::kMsidSemanticAttribute)) { - return kEmptyString; + MOZ_CRASH(); } const SdpAttribute* attr = GetAttribute(SdpAttribute::kMsidSemanticAttribute); - return static_cast(attr)->mValue; + return *static_cast(attr); } uint32_t diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.h 2015-02-03 14:33:31.000000000 +0000 @@ -69,7 +69,8 @@ virtual const std::string& GetLabel() const MOZ_OVERRIDE; virtual unsigned int GetMaxptime() const MOZ_OVERRIDE; virtual const std::string& GetMid() const MOZ_OVERRIDE; - virtual const std::string& GetMsidSemantic() const MOZ_OVERRIDE; + virtual const SdpMsidSemanticAttributeList& GetMsidSemantic() + const MOZ_OVERRIDE; virtual unsigned int GetPtime() const MOZ_OVERRIDE; virtual SdpDirectionAttribute::Direction GetDirection() const MOZ_OVERRIDE; @@ -105,6 +106,9 @@ void LoadSetup(sdp_t* sdp, uint16_t level); void LoadSsrc(sdp_t* sdp, uint16_t level); bool LoadGroups(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder); + bool LoadMsidSemantics(sdp_t* sdp, + uint16_t level, + SdpErrorHolder& errorHolder); void LoadFmtp(sdp_t* sdp, uint16_t level); void LoadMsids(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder); void LoadExtmap(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/test/FakeMediaStreams.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/test/FakeMediaStreams.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/test/FakeMediaStreams.h 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/test/FakeMediaStreams.h 2015-02-03 14:33:31.000000000 +0000 @@ -6,6 +6,8 @@ #define FAKE_MEDIA_STREAM_H_ #include +#include +#include #include "nsNetCID.h" #include "nsITimer.h" @@ -13,6 +15,7 @@ #include "nsIComponentManager.h" #include "nsIComponentRegistrar.h" #include "nsISupportsImpl.h" +#include "nsServiceManagerUtils.h" // #includes from MediaStream.h #include "mozilla/Mutex.h" @@ -220,8 +223,16 @@ public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Fake_MediaStreamTrack) - explicit Fake_MediaStreamTrack(bool aIsVideo) : mIsVideo (aIsVideo) {} + explicit Fake_MediaStreamTrack(bool aIsVideo) : mIsVideo (aIsVideo) + { + static size_t counter = 0; + std::ostringstream os; + os << counter++; + mID = os.str(); + } mozilla::TrackID GetTrackID() { return mIsVideo ? 1 : 0; } + std::string GetId() const { return mID; } + void AssignId(const std::string& id) { mID = id; } Fake_DOMMediaStream *GetStream() { return nullptr; } const Fake_MediaStreamTrack* AsVideoStreamTrack() const { @@ -235,6 +246,7 @@ ~Fake_MediaStreamTrack() {} const bool mIsVideo; + std::string mID; }; class Fake_DOMMediaStream : public nsISupports diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/test/jsep_session_unittest.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/test/jsep_session_unittest.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/test/jsep_session_unittest.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/test/jsep_session_unittest.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -246,6 +246,20 @@ if (checkFlags & CHECK_TRACKS) { // Check that the transports exist. ASSERT_EQ(types.size(), mSessionOff.GetTransportCount()); + for (size_t i = 0; i < types.size(); ++i) { + RefPtr ltrack; + ASSERT_EQ(NS_OK, mSessionOff.GetLocalTrack(i, <rack)); + ASSERT_NE("", ltrack->GetStreamId()); + ASSERT_NE("", ltrack->GetTrackId()); + if (ltrack->GetMediaType() != SdpMediaSection::kApplication) { + std::string msidAttr("a=msid:"); + msidAttr += ltrack->GetStreamId(); + msidAttr += " "; + msidAttr += ltrack->GetTrackId(); + ASSERT_NE(std::string::npos, offer.find(msidAttr)) + << "Did not find " << msidAttr << " in offer"; + } + } } } @@ -265,6 +279,16 @@ RefPtr rtrack; ASSERT_EQ(NS_OK, mSessionAns.GetRemoteTrack(i, &rtrack)); ASSERT_EQ(types[i], rtrack->GetMediaType()); + ASSERT_NE("", rtrack->GetStreamId()); + ASSERT_NE("", rtrack->GetTrackId()); + if (rtrack->GetMediaType() != SdpMediaSection::kApplication) { + std::string msidAttr("a=msid:"); + msidAttr += rtrack->GetStreamId(); + msidAttr += " "; + msidAttr += rtrack->GetTrackId(); + ASSERT_NE(std::string::npos, offer.find(msidAttr)) + << "Did not find " << msidAttr << " in offer"; + } } } } @@ -287,6 +311,21 @@ ASSERT_EQ(types[i], pair->mSending->GetMediaType()); ASSERT_TRUE(pair->mReceiving); ASSERT_EQ(types[i], pair->mReceiving->GetMediaType()); + ASSERT_NE("", pair->mSending->GetStreamId()); + ASSERT_NE("", pair->mSending->GetTrackId()); + // These might have been in the SDP, or might have been randomly + // chosen by JsepSessionImpl + ASSERT_NE("", pair->mReceiving->GetStreamId()); + ASSERT_NE("", pair->mReceiving->GetTrackId()); + + if (pair->mReceiving->GetMediaType() != SdpMediaSection::kApplication) { + std::string msidAttr("a=msid:"); + msidAttr += pair->mSending->GetStreamId(); + msidAttr += " "; + msidAttr += pair->mSending->GetTrackId(); + ASSERT_NE(std::string::npos, answer.find(msidAttr)) + << "Did not find " << msidAttr << " in offer"; + } } } DumpTrackPairs(mSessionOff); @@ -302,14 +341,29 @@ if (checkFlags & CHECK_TRACKS) { // Verify that the right stuff is in the tracks. - ASSERT_EQ(types.size(), mSessionAns.GetNegotiatedTrackPairCount()); + ASSERT_EQ(types.size(), mSessionOff.GetNegotiatedTrackPairCount()); for (size_t i = 0; i < types.size(); ++i) { const JsepTrackPair* pair; - ASSERT_EQ(NS_OK, mSessionAns.GetNegotiatedTrackPair(i, &pair)); + ASSERT_EQ(NS_OK, mSessionOff.GetNegotiatedTrackPair(i, &pair)); ASSERT_TRUE(pair->mSending); ASSERT_EQ(types[i], pair->mSending->GetMediaType()); ASSERT_TRUE(pair->mReceiving); ASSERT_EQ(types[i], pair->mReceiving->GetMediaType()); + ASSERT_NE("", pair->mSending->GetStreamId()); + ASSERT_NE("", pair->mSending->GetTrackId()); + // These might have been in the SDP, or might have been randomly + // chosen by JsepSessionImpl + ASSERT_NE("", pair->mReceiving->GetStreamId()); + ASSERT_NE("", pair->mReceiving->GetTrackId()); + + if (pair->mReceiving->GetMediaType() != SdpMediaSection::kApplication) { + std::string msidAttr("a=msid:"); + msidAttr += pair->mReceiving->GetStreamId(); + msidAttr += " "; + msidAttr += pair->mReceiving->GetTrackId(); + ASSERT_NE(std::string::npos, answer.find(msidAttr)) + << "Did not find " << msidAttr << " in offer"; + } } } DumpTrackPairs(mSessionAns); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/test/mediaconduit_unittests.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/test/mediaconduit_unittests.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/test/mediaconduit_unittests.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/test/mediaconduit_unittests.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -595,6 +595,7 @@ WrapRunnableNMRet( &mozilla::VideoSessionConduit::Create, nullptr, + false, &mVideoSession)); if( !mVideoSession ) ASSERT_NE(mVideoSession, (void*)nullptr); @@ -604,6 +605,7 @@ WrapRunnableNMRet( &mozilla::VideoSessionConduit::Create, nullptr, + true, &mVideoSession2)); if( !mVideoSession2 ) ASSERT_NE(mVideoSession2,(void*)nullptr); @@ -695,6 +697,7 @@ WrapRunnableNMRet( &mozilla::VideoSessionConduit::Create, nullptr, + true, &videoSession)); if( !videoSession ) ASSERT_NE(videoSession, (void*)nullptr); @@ -807,6 +810,7 @@ WrapRunnableNMRet( &mozilla::VideoSessionConduit::Create, nullptr, + false, &mVideoSession)); if( !mVideoSession ) ASSERT_NE(mVideoSession, (void*)nullptr); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/test/mediapipeline_unittest.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/test/mediapipeline_unittest.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/test/mediapipeline_unittest.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/test/mediapipeline_unittest.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -270,6 +270,7 @@ nullptr, test_utils->sts_target(), audio_, + "audio_track_fake_uuid", 1, false, audio_conduit_, @@ -319,7 +320,7 @@ test_pc, nullptr, test_utils->sts_target(), - audio_->GetStream(), 1, 1, + audio_->GetStream(), "audio_track_fake_uuid", 1, 1, static_cast(audio_conduit_.get()), audio_rtp_transport_.flow_, audio_rtcp_transport_.flow_, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/test/sdp_unittests.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/test/sdp_unittests.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/test/sdp_unittests.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/test/sdp_unittests.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -1126,7 +1126,8 @@ "a=ice-pwd:e4cc12a910f106a0a744719425510e17" CRLF "a=ice-lite" CRLF "a=ice-options:trickle foo" CRLF -"a=msid-semantic:WMS plus" CRLF +"a=msid-semantic:WMS stream streama" CRLF +"a=msid-semantic:foo stream" CRLF "a=fingerprint:sha-256 DF:2E:AC:8A:FD:0A:8E:99:BF:5D:E8:3C:E7:FA:FB:08:3B:3C:54:1D:D7:D4:05:77:A0:72:9B:14:08:6D:0F:4C" CRLF "a=identity:blahblahblah foo;bar" CRLF "a=group:BUNDLE first second" CRLF @@ -1149,7 +1150,7 @@ "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level" CRLF "a=setup:actpass" CRLF "a=rtcp-mux" CRLF -"a=msid:track stream" CRLF +"a=msid:stream track" CRLF "a=candidate:0 1 UDP 2130379007 10.0.0.36 62453 typ host" CRLF "a=candidate:2 1 UDP 1694236671 24.6.134.204 62453 typ srflx raddr 10.0.0.36 rport 62453" CRLF "a=candidate:3 1 UDP 100401151 162.222.183.171 49761 typ relay raddr 162.222.183.171 rport 49761" CRLF @@ -1171,8 +1172,8 @@ "a=rtcp-fb:120 ccm fir" CRLF "a=setup:active" CRLF "a=rtcp-mux" CRLF -"a=msid:tracka streama" CRLF -"a=msid:trackb streamb" CRLF +"a=msid:streama tracka" CRLF +"a=msid:streamb trackb" CRLF "a=candidate:0 1 UDP 2130379007 10.0.0.36 59530 typ host" CRLF "a=candidate:0 2 UDP 2130379006 10.0.0.36 64378 typ host" CRLF "a=candidate:2 2 UDP 1694236670 24.6.134.204 64378 typ srflx raddr 10.0.0.36 rport 64378" CRLF @@ -1426,7 +1427,7 @@ "a=ice-ufrag:4a799b2e" CRLF "a=ice-pwd:e4cc12a910f106a0a744719425510e17" CRLF "a=ice-lite" CRLF -"a=msid-semantic:WMS plus" CRLF +"a=msid-semantic:WMS stream streama" CRLF "a=fingerprint:sha-256 DF:2E:AC:8A:FD:0A:8E:99:BF:5D:E8:3C:E7:FA:FB:08:3B:3C:54:1D:D7:D4:05:77:A0:72:9B:14:08:6D:0F:4C" CRLF "a=group:BUNDLE first second" CRLF "a=group:BUNDLE third" CRLF @@ -1448,7 +1449,7 @@ "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level" CRLF "a=setup:actpass" CRLF "a=rtcp-mux" CRLF -"a=msid:track stream" CRLF +"a=msid:stream track" CRLF "a=candidate:0 1 UDP 2130379007 10.0.0.36 62453 typ host" CRLF "a=candidate:2 1 UDP 1694236671 24.6.134.204 62453 typ srflx raddr 10.0.0.36 rport 62453" CRLF "a=candidate:3 1 UDP 100401151 162.222.183.171 49761 typ relay raddr 162.222.183.171 rport 49761" CRLF @@ -1469,8 +1470,8 @@ "a=recvonly" CRLF "a=setup:active" CRLF "a=rtcp-mux" CRLF -"a=msid:tracka streama" CRLF -"a=msid:trackb streamb" CRLF +"a=msid:streama tracka" CRLF +"a=msid:streamb trackb" CRLF "a=candidate:0 1 UDP 2130379007 10.0.0.36 59530 typ host" CRLF "a=candidate:0 2 UDP 2130379006 10.0.0.36 64378 typ host" CRLF "a=candidate:2 2 UDP 1694236670 24.6.134.204 64378 typ srflx raddr 10.0.0.36 rport 64378" CRLF @@ -1672,22 +1673,29 @@ ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(mSdp->GetAttributeList().HasAttribute( SdpAttribute::kMsidSemanticAttribute)); - // note that we lose the extra pieces here - // it's not worth it to save them until they mean something - ASSERT_EQ("WMS", mSdp->GetAttributeList().GetMsidSemantic()); + auto semantics = mSdp->GetAttributeList().GetMsidSemantic().mMsidSemantics; + ASSERT_EQ(2U, semantics.size()); + ASSERT_EQ("WMS", semantics[0].semantic); + ASSERT_EQ(2U, semantics[0].msids.size()); + ASSERT_EQ("stream", semantics[0].msids[0]); + ASSERT_EQ("streama", semantics[0].msids[1]); + ASSERT_EQ("foo", semantics[1].semantic); + ASSERT_EQ(1U, semantics[1].msids.size()); + ASSERT_EQ("stream", semantics[1].msids[0]); + const SdpMsidAttributeList& msids1 = mSdp->GetMediaSection(0).GetAttributeList().GetMsid(); ASSERT_EQ(1U, msids1.mMsids.size()); - ASSERT_EQ("track", msids1.mMsids[0].identifier); - ASSERT_EQ("stream", msids1.mMsids[0].appdata); + ASSERT_EQ("stream", msids1.mMsids[0].identifier); + ASSERT_EQ("track", msids1.mMsids[0].appdata); const SdpMsidAttributeList& msids2 = mSdp->GetMediaSection(1).GetAttributeList().GetMsid(); ASSERT_EQ(2U, msids2.mMsids.size()); - ASSERT_EQ("tracka", msids2.mMsids[0].identifier); - ASSERT_EQ("streama", msids2.mMsids[0].appdata); - ASSERT_EQ("trackb", msids2.mMsids[1].identifier); - ASSERT_EQ("streamb", msids2.mMsids[1].appdata); + ASSERT_EQ("streama", msids2.mMsids[0].identifier); + ASSERT_EQ("tracka", msids2.mMsids[0].appdata); + ASSERT_EQ("streamb", msids2.mMsids[1].identifier); + ASSERT_EQ("trackb", msids2.mMsids[1].appdata); const SdpMsidAttributeList& msids3 = mSdp->GetMediaSection(2).GetAttributeList().GetMsid(); ASSERT_EQ(1U, msids3.mMsids.size()); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/test/signaling_unittests.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/test/signaling_unittests.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/signaling/test/signaling_unittests.cpp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/signaling/test/signaling_unittests.cpp 2015-02-03 14:33:31.000000000 +0000 @@ -1327,8 +1327,14 @@ } } + // Right now we have no convenient way for this unit-test to learn the track + // ids of the tracks, so they can be queried later. We could either expose + // the JsepSessionImpl in some way, or we could parse the identifiers out of + // the SDP. For now, we just specify audio/video, since a given DOMMediaStream + // can have only one of each anyway. Once this is fixed, we will need to + // pass a real track id if we want to test that case. mozilla::RefPtr GetMediaPipeline( - bool local, size_t stream, int track) { + bool local, size_t stream, bool video) { SourceStreamInfo* streamInfo; if (local) { mozilla::SyncRunnable::DispatchToThread( @@ -1348,11 +1354,16 @@ const auto &pipelines = streamInfo->GetPipelines(); - auto it = pipelines.find(track); - return (it == pipelines.end())? nullptr : it->second; + for (auto i = pipelines.begin(); i != pipelines.end(); ++i) { + if (i->second->IsVideo() == video) { + std::cout << "Got MediaPipeline " << i->second->trackid(); + return i->second; + } + } + return nullptr; } - void CheckMediaPipeline(int stream, int track, uint32_t flags, + void CheckMediaPipeline(int stream, bool video, uint32_t flags, VideoSessionConduit::FrameRequestType frameRequestMethod = VideoSessionConduit::FrameRequestNone) { @@ -1361,13 +1372,13 @@ << ((flags & PIPELINE_SEND) ? "sending " : "receiving ") << ((flags & PIPELINE_VIDEO) ? "video" : "audio") << " pipeline (stream " << stream - << ", track " << track << "); expect " + << ", track " << video << "); expect " << ((flags & PIPELINE_RTCP_MUX) ? "MUX, " : "no MUX, ") << ((flags & PIPELINE_RTCP_NACK) ? "NACK." : "no NACK.") << std::endl; mozilla::RefPtr pipeline = - GetMediaPipeline((flags & PIPELINE_LOCAL), stream, track); + GetMediaPipeline((flags & PIPELINE_LOCAL), stream, video); ASSERT_TRUE(pipeline); ASSERT_EQ(pipeline->IsDoingRtcpMux(), !!(flags & PIPELINE_RTCP_MUX)); // We cannot yet test send/recv with video. @@ -1938,12 +1949,12 @@ a2_->CloseReceiveStreams(); // Check caller video settings for remote pipeline - a1_->CheckMediaPipeline(0, 1, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | + a1_->CheckMediaPipeline(0, true, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | PIPELINE_VIDEO | rtcpFbFlags, frameRequestMethod); // Check caller video settings for remote pipeline // (Should use pli and nack, regardless of what was in the offer) - a2_->CheckMediaPipeline(0, 1, + a2_->CheckMediaPipeline(0, true, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | PIPELINE_VIDEO | PIPELINE_SEND | @@ -1978,12 +1989,12 @@ a2_->CloseReceiveStreams(); // Check callee video settings for remote pipeline - a2_->CheckMediaPipeline(0, 1, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | + a2_->CheckMediaPipeline(0, true, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | PIPELINE_VIDEO | rtcpFbFlags, frameRequestMethod); // Check caller video settings for remote pipeline // (Should use pli and nack, regardless of what was in the offer) - a1_->CheckMediaPipeline(0, 1, + a1_->CheckMediaPipeline(0, true, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | PIPELINE_VIDEO | PIPELINE_SEND | @@ -2559,12 +2570,12 @@ // Check the low-level media pipeline // for RTP and RTCP flows // The first Local pipeline gets stored at 0 - a1_->CheckMediaPipeline(0, 0, fRtcpMux ? + a1_->CheckMediaPipeline(0, false, fRtcpMux ? PIPELINE_LOCAL | PIPELINE_RTCP_MUX | PIPELINE_SEND : PIPELINE_LOCAL | PIPELINE_SEND); // The first Remote pipeline gets stored at 0 - a2_->CheckMediaPipeline(0, 0, (fRtcpMux ? PIPELINE_RTCP_MUX : 0)); + a2_->CheckMediaPipeline(0, false, (fRtcpMux ? PIPELINE_RTCP_MUX : 0)); } TEST_P(SignalingTest, FullCallAudioOnly) @@ -3397,10 +3408,8 @@ // Check the low-level media pipeline // for RTP and RTCP flows // The first Local pipeline gets stored at 0 - a1_->CheckMediaPipeline(0, 0, PIPELINE_LOCAL | PIPELINE_SEND); - - // The first Remote pipeline gets stored at 1 - a2_->CheckMediaPipeline(0, 0, 0); + a1_->CheckMediaPipeline(0, false, PIPELINE_LOCAL | PIPELINE_SEND); + a2_->CheckMediaPipeline(0, false, 0); } @@ -3537,18 +3546,18 @@ // Check the low-level media pipeline // for RTP and RTCP flows // The first Local pipeline gets stored at 0 - a1_->CheckMediaPipeline(0, 0, PIPELINE_LOCAL | PIPELINE_SEND); + a1_->CheckMediaPipeline(0, false, PIPELINE_LOCAL | PIPELINE_SEND); // Now check video mux. - a1_->CheckMediaPipeline(0, 1, + a1_->CheckMediaPipeline(0, true, PIPELINE_LOCAL | (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | PIPELINE_SEND | PIPELINE_VIDEO); // The first Remote pipeline gets stored at 0 - a2_->CheckMediaPipeline(0, 0, 0); + a2_->CheckMediaPipeline(0, false, 0); // Now check video mux. - a2_->CheckMediaPipeline(0, 1, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | + a2_->CheckMediaPipeline(0, true, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | PIPELINE_VIDEO | PIPELINE_RTCP_NACK, VideoSessionConduit::FrameRequestPli); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/base/base.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/base/base.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/base/base.gyp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/base/base.gyp 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -# Below are normally provided by Chromium's base.gyp and required for -# libjingle.gyp. -{ - 'targets': [ - { - 'target_name': 'base', - 'type': 'none', - 'direct_dependent_settings': { - 'include_dirs': [ - '..', - ], - }, - 'conditions': [ - ['OS == "linux"', { - 'link_settings': { - 'libraries': [ - # We need rt for clock_gettime() used in libjingle. - '-lrt', - ], - }, - }], - ], - }, - ], -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/base/base.Makefile thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/base/base.Makefile --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/base/base.Makefile 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/base/base.Makefile 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -# This file is generated by gyp; do not edit. - -export builddir_name ?= trunk/base/out -.PHONY: all -all: - $(MAKE) -C .. base diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/base/base.target.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/base/base.target.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/base/base.target.mk 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/base/base.target.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -# This file is generated by gyp; do not edit. - -TOOLSET := target -TARGET := base -### Rules for final target. -$(obj).target/base/base.stamp: TOOLSET := $(TOOLSET) -$(obj).target/base/base.stamp: FORCE_DO_CMD - $(call do_cmd,touch) - -all_deps += $(obj).target/base/base.stamp -# Add target alias -.PHONY: base -base: $(obj).target/base/base.stamp - -# Add target alias to "all" target. -.PHONY: all -all: base - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/peerconnection.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/peerconnection.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/peerconnection.gyp 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/peerconnection.gyp 2015-02-03 14:33:32.000000000 +0000 @@ -35,8 +35,10 @@ 'dependencies': [ 'webrtc/modules/modules.gyp:audio_device', 'webrtc/modules/modules.gyp:video_capture_module', -# 'webrtc/modules/modules.gyp:video_render_module', + 'webrtc/modules/modules.gyp:video_capture_module_internal_impl', + 'webrtc/modules/modules.gyp:video_render_module_impl', # 'webrtc/system_wrappers/source/system_wrappers.gyp:system_wrappers', +# 'webrtc/system_wrappers/source/system_wrappers.gyp:metrics_default', 'webrtc/video_engine/video_engine.gyp:video_engine_core', 'webrtc/voice_engine/voice_engine.gyp:voice_engine', ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncfile.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncfile.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncfile.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncfile.cc 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,21 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/asyncfile.h" + +namespace rtc { + +AsyncFile::AsyncFile() { +} + +AsyncFile::~AsyncFile() { +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncfile.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncfile.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncfile.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncfile.h 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_ASYNCFILE_H__ +#define WEBRTC_BASE_ASYNCFILE_H__ + +#include "webrtc/base/sigslot.h" + +namespace rtc { + +// Provides the ability to perform file I/O asynchronously. +// TODO: Create a common base class with AsyncSocket. +class AsyncFile { + public: + AsyncFile(); + virtual ~AsyncFile(); + + // Determines whether the file will receive read events. + virtual bool readable() = 0; + virtual void set_readable(bool value) = 0; + + // Determines whether the file will receive write events. + virtual bool writable() = 0; + virtual void set_writable(bool value) = 0; + + sigslot::signal1 SignalReadEvent; + sigslot::signal1 SignalWriteEvent; + sigslot::signal2 SignalCloseEvent; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_ASYNCFILE_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asynchttprequest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asynchttprequest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asynchttprequest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asynchttprequest.cc 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,116 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/asynchttprequest.h" + +namespace rtc { + +enum { + MSG_TIMEOUT = SignalThread::ST_MSG_FIRST_AVAILABLE, + MSG_LAUNCH_REQUEST +}; +static const int kDefaultHTTPTimeout = 30 * 1000; // 30 sec + +/////////////////////////////////////////////////////////////////////////////// +// AsyncHttpRequest +/////////////////////////////////////////////////////////////////////////////// + +AsyncHttpRequest::AsyncHttpRequest(const std::string &user_agent) + : start_delay_(0), + firewall_(NULL), + port_(80), + secure_(false), + timeout_(kDefaultHTTPTimeout), + fail_redirect_(false), + factory_(Thread::Current()->socketserver(), user_agent), + pool_(&factory_), + client_(user_agent.c_str(), &pool_), + error_(HE_NONE) { + client_.SignalHttpClientComplete.connect(this, + &AsyncHttpRequest::OnComplete); +} + +AsyncHttpRequest::~AsyncHttpRequest() { +} + +void AsyncHttpRequest::OnWorkStart() { + if (start_delay_ <= 0) { + LaunchRequest(); + } else { + Thread::Current()->PostDelayed(start_delay_, this, MSG_LAUNCH_REQUEST); + } +} + +void AsyncHttpRequest::OnWorkStop() { + // worker is already quitting, no need to explicitly quit + LOG(LS_INFO) << "HttpRequest cancelled"; +} + +void AsyncHttpRequest::OnComplete(HttpClient* client, HttpErrorType error) { + Thread::Current()->Clear(this, MSG_TIMEOUT); + + set_error(error); + if (!error) { + LOG(LS_INFO) << "HttpRequest completed successfully"; + + std::string value; + if (client_.response().hasHeader(HH_LOCATION, &value)) { + response_redirect_ = value.c_str(); + } + } else { + LOG(LS_INFO) << "HttpRequest completed with error: " << error; + } + + worker()->Quit(); +} + +void AsyncHttpRequest::OnMessage(Message* message) { + switch (message->message_id) { + case MSG_TIMEOUT: + LOG(LS_INFO) << "HttpRequest timed out"; + client_.reset(); + worker()->Quit(); + break; + case MSG_LAUNCH_REQUEST: + LaunchRequest(); + break; + default: + SignalThread::OnMessage(message); + break; + } +} + +void AsyncHttpRequest::DoWork() { + // Do nothing while we wait for the request to finish. We only do this so + // that we can be a SignalThread; in the future this class should not be + // a SignalThread, since it does not need to spawn a new thread. + Thread::Current()->ProcessMessages(kForever); +} + +void AsyncHttpRequest::LaunchRequest() { + factory_.SetProxy(proxy_); + if (secure_) + factory_.UseSSL(host_.c_str()); + + bool transparent_proxy = (port_ == 80) && + ((proxy_.type == PROXY_HTTPS) || (proxy_.type == PROXY_UNKNOWN)); + if (transparent_proxy) { + client_.set_proxy(proxy_); + } + client_.set_fail_redirect(fail_redirect_); + client_.set_server(SocketAddress(host_, port_)); + + LOG(LS_INFO) << "HttpRequest start: " << host_ + client_.request().path; + + Thread::Current()->PostDelayed(timeout_, this, MSG_TIMEOUT); + client_.start(); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asynchttprequest.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asynchttprequest.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asynchttprequest.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asynchttprequest.h 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,104 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_ASYNCHTTPREQUEST_H_ +#define WEBRTC_BASE_ASYNCHTTPREQUEST_H_ + +#include +#include "webrtc/base/event.h" +#include "webrtc/base/httpclient.h" +#include "webrtc/base/signalthread.h" +#include "webrtc/base/socketpool.h" +#include "webrtc/base/sslsocketfactory.h" + +namespace rtc { + +class FirewallManager; + +/////////////////////////////////////////////////////////////////////////////// +// AsyncHttpRequest +// Performs an HTTP request on a background thread. Notifies on the foreground +// thread once the request is done (successfully or unsuccessfully). +/////////////////////////////////////////////////////////////////////////////// + +class AsyncHttpRequest : public SignalThread { + public: + explicit AsyncHttpRequest(const std::string &user_agent); + ~AsyncHttpRequest(); + + // If start_delay is less than or equal to zero, this starts immediately. + // Start_delay defaults to zero. + int start_delay() const { return start_delay_; } + void set_start_delay(int delay) { start_delay_ = delay; } + + const ProxyInfo& proxy() const { return proxy_; } + void set_proxy(const ProxyInfo& proxy) { + proxy_ = proxy; + } + void set_firewall(FirewallManager * firewall) { + firewall_ = firewall; + } + + // The DNS name of the host to connect to. + const std::string& host() { return host_; } + void set_host(const std::string& host) { host_ = host; } + + // The port to connect to on the target host. + int port() { return port_; } + void set_port(int port) { port_ = port; } + + // Whether the request should use SSL. + bool secure() { return secure_; } + void set_secure(bool secure) { secure_ = secure; } + + // Time to wait on the download, in ms. + int timeout() { return timeout_; } + void set_timeout(int timeout) { timeout_ = timeout; } + + // Fail redirects to allow analysis of redirect urls, etc. + bool fail_redirect() const { return fail_redirect_; } + void set_fail_redirect(bool redirect) { fail_redirect_ = redirect; } + + // Returns the redirect when redirection occurs + const std::string& response_redirect() { return response_redirect_; } + + HttpRequestData& request() { return client_.request(); } + HttpResponseData& response() { return client_.response(); } + HttpErrorType error() { return error_; } + + protected: + void set_error(HttpErrorType error) { error_ = error; } + virtual void OnWorkStart(); + virtual void OnWorkStop(); + void OnComplete(HttpClient* client, HttpErrorType error); + virtual void OnMessage(Message* message); + virtual void DoWork(); + + private: + void LaunchRequest(); + + int start_delay_; + ProxyInfo proxy_; + FirewallManager* firewall_; + std::string host_; + int port_; + bool secure_; + int timeout_; + bool fail_redirect_; + SslSocketFactory factory_; + ReuseSocketPool pool_; + HttpClient client_; + HttpErrorType error_; + std::string response_redirect_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_ASYNCHTTPREQUEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asynchttprequest_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asynchttprequest_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asynchttprequest_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asynchttprequest_unittest.cc 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,234 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include "webrtc/base/asynchttprequest.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/httpserver.h" +#include "webrtc/base/socketstream.h" +#include "webrtc/base/thread.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace rtc { + +static const SocketAddress kServerAddr("127.0.0.1", 0); +static const SocketAddress kServerHostnameAddr("localhost", 0); +static const char kServerGetPath[] = "/get"; +static const char kServerPostPath[] = "/post"; +static const char kServerResponse[] = "This is a test"; + +class TestHttpServer : public HttpServer, public sigslot::has_slots<> { + public: + TestHttpServer(Thread* thread, const SocketAddress& addr) : + socket_(thread->socketserver()->CreateAsyncSocket(addr.family(), + SOCK_STREAM)) { + socket_->Bind(addr); + socket_->Listen(5); + socket_->SignalReadEvent.connect(this, &TestHttpServer::OnAccept); + } + + SocketAddress address() const { return socket_->GetLocalAddress(); } + void Close() const { socket_->Close(); } + + private: + void OnAccept(AsyncSocket* socket) { + AsyncSocket* new_socket = socket_->Accept(NULL); + if (new_socket) { + HandleConnection(new SocketStream(new_socket)); + } + } + rtc::scoped_ptr socket_; +}; + +class AsyncHttpRequestTest : public testing::Test, + public sigslot::has_slots<> { + public: + AsyncHttpRequestTest() + : started_(false), + done_(false), + server_(Thread::Current(), kServerAddr) { + server_.SignalHttpRequest.connect(this, &AsyncHttpRequestTest::OnRequest); + } + + bool started() const { return started_; } + bool done() const { return done_; } + + AsyncHttpRequest* CreateGetRequest(const std::string& host, int port, + const std::string& path) { + rtc::AsyncHttpRequest* request = + new rtc::AsyncHttpRequest("unittest"); + request->SignalWorkDone.connect(this, + &AsyncHttpRequestTest::OnRequestDone); + request->request().verb = rtc::HV_GET; + request->set_host(host); + request->set_port(port); + request->request().path = path; + request->response().document.reset(new MemoryStream()); + return request; + } + AsyncHttpRequest* CreatePostRequest(const std::string& host, int port, + const std::string& path, + const std::string content_type, + StreamInterface* content) { + rtc::AsyncHttpRequest* request = + new rtc::AsyncHttpRequest("unittest"); + request->SignalWorkDone.connect(this, + &AsyncHttpRequestTest::OnRequestDone); + request->request().verb = rtc::HV_POST; + request->set_host(host); + request->set_port(port); + request->request().path = path; + request->request().setContent(content_type, content); + request->response().document.reset(new MemoryStream()); + return request; + } + + const TestHttpServer& server() const { return server_; } + + protected: + void OnRequest(HttpServer* server, HttpServerTransaction* t) { + started_ = true; + + if (t->request.path == kServerGetPath) { + t->response.set_success("text/plain", new MemoryStream(kServerResponse)); + } else if (t->request.path == kServerPostPath) { + // reverse the data and reply + size_t size; + StreamInterface* in = t->request.document.get(); + StreamInterface* out = new MemoryStream(); + in->GetSize(&size); + for (size_t i = 0; i < size; ++i) { + char ch; + in->SetPosition(size - i - 1); + in->Read(&ch, 1, NULL, NULL); + out->Write(&ch, 1, NULL, NULL); + } + out->Rewind(); + t->response.set_success("text/plain", out); + } else { + t->response.set_error(404); + } + server_.Respond(t); + } + void OnRequestDone(SignalThread* thread) { + done_ = true; + } + + private: + bool started_; + bool done_; + TestHttpServer server_; +}; + +TEST_F(AsyncHttpRequestTest, TestGetSuccess) { + AsyncHttpRequest* req = CreateGetRequest( + kServerHostnameAddr.hostname(), server().address().port(), + kServerGetPath); + EXPECT_FALSE(started()); + req->Start(); + EXPECT_TRUE_WAIT(started(), 5000); // Should have started by now. + EXPECT_TRUE_WAIT(done(), 5000); + std::string response; + EXPECT_EQ(200U, req->response().scode); + ASSERT_TRUE(req->response().document); + req->response().document->Rewind(); + req->response().document->ReadLine(&response); + EXPECT_EQ(kServerResponse, response); + req->Release(); +} + +TEST_F(AsyncHttpRequestTest, TestGetNotFound) { + AsyncHttpRequest* req = CreateGetRequest( + kServerHostnameAddr.hostname(), server().address().port(), + "/bad"); + req->Start(); + EXPECT_TRUE_WAIT(done(), 5000); + size_t size; + EXPECT_EQ(404U, req->response().scode); + ASSERT_TRUE(req->response().document); + req->response().document->GetSize(&size); + EXPECT_EQ(0U, size); + req->Release(); +} + +TEST_F(AsyncHttpRequestTest, TestGetToNonServer) { + AsyncHttpRequest* req = CreateGetRequest( + "127.0.0.1", server().address().port(), + kServerGetPath); + // Stop the server before we send the request. + server().Close(); + req->Start(); + EXPECT_TRUE_WAIT(done(), 10000); + size_t size; + EXPECT_EQ(500U, req->response().scode); + ASSERT_TRUE(req->response().document); + req->response().document->GetSize(&size); + EXPECT_EQ(0U, size); + req->Release(); +} + +TEST_F(AsyncHttpRequestTest, DISABLED_TestGetToInvalidHostname) { + AsyncHttpRequest* req = CreateGetRequest( + "invalid", server().address().port(), + kServerGetPath); + req->Start(); + EXPECT_TRUE_WAIT(done(), 5000); + size_t size; + EXPECT_EQ(500U, req->response().scode); + ASSERT_TRUE(req->response().document); + req->response().document->GetSize(&size); + EXPECT_EQ(0U, size); + req->Release(); +} + +TEST_F(AsyncHttpRequestTest, TestPostSuccess) { + AsyncHttpRequest* req = CreatePostRequest( + kServerHostnameAddr.hostname(), server().address().port(), + kServerPostPath, "text/plain", new MemoryStream("abcd1234")); + req->Start(); + EXPECT_TRUE_WAIT(done(), 5000); + std::string response; + EXPECT_EQ(200U, req->response().scode); + ASSERT_TRUE(req->response().document); + req->response().document->Rewind(); + req->response().document->ReadLine(&response); + EXPECT_EQ("4321dcba", response); + req->Release(); +} + +// Ensure that we shut down properly even if work is outstanding. +TEST_F(AsyncHttpRequestTest, TestCancel) { + AsyncHttpRequest* req = CreateGetRequest( + kServerHostnameAddr.hostname(), server().address().port(), + kServerGetPath); + req->Start(); + req->Destroy(true); +} + +TEST_F(AsyncHttpRequestTest, TestGetSuccessDelay) { + AsyncHttpRequest* req = CreateGetRequest( + kServerHostnameAddr.hostname(), server().address().port(), + kServerGetPath); + req->set_start_delay(10); // Delay 10ms. + req->Start(); + Thread::SleepMs(5); + EXPECT_FALSE(started()); // Should not have started immediately. + EXPECT_TRUE_WAIT(started(), 5000); // Should have started by now. + EXPECT_TRUE_WAIT(done(), 5000); + std::string response; + EXPECT_EQ(200U, req->response().scode); + ASSERT_TRUE(req->response().document); + req->response().document->Rewind(); + req->response().document->ReadLine(&response); + EXPECT_EQ(kServerResponse, response); + req->Release(); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncinvoker.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncinvoker.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncinvoker.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncinvoker.cc 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,91 @@ +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/asyncinvoker.h" + +namespace rtc { + +AsyncInvoker::AsyncInvoker() : destroying_(false) {} + +AsyncInvoker::~AsyncInvoker() { + destroying_ = true; + SignalInvokerDestroyed(); + // Messages for this need to be cleared *before* our destructor is complete. + MessageQueueManager::Clear(this); +} + +void AsyncInvoker::OnMessage(Message* msg) { + // Get the AsyncClosure shared ptr from this message's data. + ScopedRefMessageData* data = + static_cast*>(msg->pdata); + scoped_refptr closure = data->data(); + delete msg->pdata; + msg->pdata = NULL; + + // Execute the closure and trigger the return message if needed. + closure->Execute(); +} + +void AsyncInvoker::Flush(Thread* thread, uint32 id /*= MQID_ANY*/) { + if (destroying_) return; + + // Run this on |thread| to reduce the number of context switches. + if (Thread::Current() != thread) { + thread->Invoke(Bind(&AsyncInvoker::Flush, this, thread, id)); + return; + } + + MessageList removed; + thread->Clear(this, id, &removed); + for (MessageList::iterator it = removed.begin(); it != removed.end(); ++it) { + // This message was pending on this thread, so run it now. + thread->Send(it->phandler, + it->message_id, + it->pdata); + } +} + +void AsyncInvoker::DoInvoke(Thread* thread, AsyncClosure* closure, + uint32 id) { + if (destroying_) { + LOG(LS_WARNING) << "Tried to invoke while destroying the invoker."; + // Since this call transwers ownership of |closure|, we clean it up here. + delete closure; + return; + } + thread->Post(this, id, new ScopedRefMessageData(closure)); +} + +NotifyingAsyncClosureBase::NotifyingAsyncClosureBase(AsyncInvoker* invoker, + Thread* calling_thread) + : invoker_(invoker), calling_thread_(calling_thread) { + calling_thread->SignalQueueDestroyed.connect( + this, &NotifyingAsyncClosureBase::CancelCallback); + invoker->SignalInvokerDestroyed.connect( + this, &NotifyingAsyncClosureBase::CancelCallback); +} + +void NotifyingAsyncClosureBase::TriggerCallback() { + CritScope cs(&crit_); + if (!CallbackCanceled() && !callback_.empty()) { + invoker_->AsyncInvoke(calling_thread_, callback_); + } +} + +void NotifyingAsyncClosureBase::CancelCallback() { + // If the callback is triggering when this is called, block the + // destructor of the dying object here by waiting until the callback + // is done triggering. + CritScope cs(&crit_); + // calling_thread_ == NULL means do not trigger the callback. + calling_thread_ = NULL; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncinvoker.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncinvoker.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncinvoker.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncinvoker.h 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,134 @@ +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_ASYNCINVOKER_H_ +#define WEBRTC_BASE_ASYNCINVOKER_H_ + +#include "webrtc/base/asyncinvoker-inl.h" +#include "webrtc/base/bind.h" +#include "webrtc/base/sigslot.h" +#include "webrtc/base/scopedptrcollection.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +// Invokes function objects (aka functors) asynchronously on a Thread, and +// owns the lifetime of calls (ie, when this object is destroyed, calls in +// flight are cancelled). AsyncInvoker can optionally execute a user-specified +// function when the asynchronous call is complete, or operates in +// fire-and-forget mode otherwise. +// +// AsyncInvoker does not own the thread it calls functors on. +// +// A note about async calls and object lifetimes: users should +// be mindful of object lifetimes when calling functions asynchronously and +// ensure objects used by the function _cannot_ be deleted between the +// invocation and execution of the functor. AsyncInvoker is designed to +// help: any calls in flight will be cancelled when the AsyncInvoker used to +// make the call is destructed, and any calls executing will be allowed to +// complete before AsyncInvoker destructs. +// +// The easiest way to ensure lifetimes are handled correctly is to create a +// class that owns the Thread and AsyncInvoker objects, and then call its +// methods asynchronously as needed. +// +// Example: +// class MyClass { +// public: +// void FireAsyncTaskWithResult(Thread* thread, int x) { +// // Specify a callback to get the result upon completion. +// invoker_.AsyncInvoke( +// thread, Bind(&MyClass::AsyncTaskWithResult, this, x), +// &MyClass::OnTaskComplete, this); +// } +// void FireAnotherAsyncTask(Thread* thread) { +// // No callback specified means fire-and-forget. +// invoker_.AsyncInvoke( +// thread, Bind(&MyClass::AnotherAsyncTask, this)); +// +// private: +// int AsyncTaskWithResult(int x) { +// // Some long running process... +// return x * x; +// } +// void AnotherAsyncTask() { +// // Some other long running process... +// } +// void OnTaskComplete(int result) { result_ = result; } +// +// AsyncInvoker invoker_; +// int result_; +// }; +class AsyncInvoker : public MessageHandler { + public: + AsyncInvoker(); + virtual ~AsyncInvoker(); + + // Call |functor| asynchronously on |thread|, with no callback upon + // completion. Returns immediately. + template + void AsyncInvoke(Thread* thread, + const FunctorT& functor, + uint32 id = 0) { + AsyncClosure* closure = + new RefCountedObject >(functor); + DoInvoke(thread, closure, id); + } + + // Call |functor| asynchronously on |thread|, calling |callback| when done. + template + void AsyncInvoke(Thread* thread, + const FunctorT& functor, + void (HostT::*callback)(ReturnT), + HostT* callback_host, + uint32 id = 0) { + AsyncClosure* closure = + new RefCountedObject >( + this, Thread::Current(), functor, callback, callback_host); + DoInvoke(thread, closure, id); + } + + // Call |functor| asynchronously on |thread|, calling |callback| when done. + // Overloaded for void return. + template + void AsyncInvoke(Thread* thread, + const FunctorT& functor, + void (HostT::*callback)(), + HostT* callback_host, + uint32 id = 0) { + AsyncClosure* closure = + new RefCountedObject >( + this, Thread::Current(), functor, callback, callback_host); + DoInvoke(thread, closure, id); + } + + // Synchronously execute on |thread| all outstanding calls we own + // that are pending on |thread|, and wait for calls to complete + // before returning. Optionally filter by message id. + // The destructor will not wait for outstanding calls, so if that + // behavior is desired, call Flush() before destroying this object. + void Flush(Thread* thread, uint32 id = MQID_ANY); + + // Signaled when this object is destructed. + sigslot::signal0<> SignalInvokerDestroyed; + + private: + virtual void OnMessage(Message* msg); + void DoInvoke(Thread* thread, AsyncClosure* closure, uint32 id); + + bool destroying_; + + DISALLOW_COPY_AND_ASSIGN(AsyncInvoker); +}; + +} // namespace rtc + + +#endif // WEBRTC_BASE_ASYNCINVOKER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncinvoker-inl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncinvoker-inl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncinvoker-inl.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncinvoker-inl.h 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,129 @@ +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_ASYNCINVOKER_INL_H_ +#define WEBRTC_BASE_ASYNCINVOKER_INL_H_ + +#include "webrtc/base/bind.h" +#include "webrtc/base/callback.h" +#include "webrtc/base/criticalsection.h" +#include "webrtc/base/messagehandler.h" +#include "webrtc/base/refcount.h" +#include "webrtc/base/scoped_ref_ptr.h" +#include "webrtc/base/sigslot.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +class AsyncInvoker; + +// Helper class for AsyncInvoker. Runs a task and triggers a callback +// on the calling thread if necessary. Instances are ref-counted so their +// lifetime can be independent of AsyncInvoker. +class AsyncClosure : public RefCountInterface { + public: + virtual ~AsyncClosure() {} + // Runs the asynchronous task, and triggers a callback to the calling + // thread if needed. Should be called from the target thread. + virtual void Execute() = 0; +}; + +// Simple closure that doesn't trigger a callback for the calling thread. +template +class FireAndForgetAsyncClosure : public AsyncClosure { + public: + explicit FireAndForgetAsyncClosure(const FunctorT& functor) + : functor_(functor) {} + virtual void Execute() { + functor_(); + } + private: + FunctorT functor_; +}; + +// Base class for closures that may trigger a callback for the calling thread. +// Listens for the "destroyed" signals from the calling thread and the invoker, +// and cancels the callback to the calling thread if either is destroyed. +class NotifyingAsyncClosureBase : public AsyncClosure, + public sigslot::has_slots<> { + public: + virtual ~NotifyingAsyncClosureBase() { disconnect_all(); } + + protected: + NotifyingAsyncClosureBase(AsyncInvoker* invoker, Thread* calling_thread); + void TriggerCallback(); + void SetCallback(const Callback0& callback) { + CritScope cs(&crit_); + callback_ = callback; + } + bool CallbackCanceled() const { return calling_thread_ == NULL; } + + private: + Callback0 callback_; + CriticalSection crit_; + AsyncInvoker* invoker_; + Thread* calling_thread_; + + void CancelCallback(); +}; + +// Closures that have a non-void return value and require a callback. +template +class NotifyingAsyncClosure : public NotifyingAsyncClosureBase { + public: + NotifyingAsyncClosure(AsyncInvoker* invoker, + Thread* calling_thread, + const FunctorT& functor, + void (HostT::*callback)(ReturnT), + HostT* callback_host) + : NotifyingAsyncClosureBase(invoker, calling_thread), + functor_(functor), + callback_(callback), + callback_host_(callback_host) {} + virtual void Execute() { + ReturnT result = functor_(); + if (!CallbackCanceled()) { + SetCallback(Callback0(Bind(callback_, callback_host_, result))); + TriggerCallback(); + } + } + + private: + FunctorT functor_; + void (HostT::*callback_)(ReturnT); + HostT* callback_host_; +}; + +// Closures that have a void return value and require a callback. +template +class NotifyingAsyncClosure + : public NotifyingAsyncClosureBase { + public: + NotifyingAsyncClosure(AsyncInvoker* invoker, + Thread* calling_thread, + const FunctorT& functor, + void (HostT::*callback)(), + HostT* callback_host) + : NotifyingAsyncClosureBase(invoker, calling_thread), + functor_(functor) { + SetCallback(Callback0(Bind(callback, callback_host))); + } + virtual void Execute() { + functor_(); + TriggerCallback(); + } + + private: + FunctorT functor_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_ASYNCINVOKER_INL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncpacketsocket.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncpacketsocket.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncpacketsocket.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncpacketsocket.h 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,140 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_ASYNCPACKETSOCKET_H_ +#define WEBRTC_BASE_ASYNCPACKETSOCKET_H_ + +#include "webrtc/base/dscp.h" +#include "webrtc/base/sigslot.h" +#include "webrtc/base/socket.h" +#include "webrtc/base/timeutils.h" + +namespace rtc { + +// This structure holds the info needed to update the packet send time header +// extension, including the information needed to update the authentication tag +// after changing the value. +struct PacketTimeUpdateParams { + PacketTimeUpdateParams() + : rtp_sendtime_extension_id(-1), srtp_auth_tag_len(-1), + srtp_packet_index(-1) { + } + + int rtp_sendtime_extension_id; // extension header id present in packet. + std::vector srtp_auth_key; // Authentication key. + int srtp_auth_tag_len; // Authentication tag length. + int64 srtp_packet_index; // Required for Rtp Packet authentication. +}; + +// This structure holds meta information for the packet which is about to send +// over network. +struct PacketOptions { + PacketOptions() : dscp(DSCP_NO_CHANGE) {} + explicit PacketOptions(DiffServCodePoint dscp) : dscp(dscp) {} + + DiffServCodePoint dscp; + PacketTimeUpdateParams packet_time_params; +}; + +// This structure will have the information about when packet is actually +// received by socket. +struct PacketTime { + PacketTime() : timestamp(-1), not_before(-1) {} + PacketTime(int64 timestamp, int64 not_before) + : timestamp(timestamp), not_before(not_before) { + } + + int64 timestamp; // Receive time after socket delivers the data. + int64 not_before; // Earliest possible time the data could have arrived, + // indicating the potential error in the |timestamp| value, + // in case the system, is busy. For example, the time of + // the last select() call. + // If unknown, this value will be set to zero. +}; + +inline PacketTime CreatePacketTime(int64 not_before) { + return PacketTime(TimeMicros(), not_before); +} + +// Provides the ability to receive packets asynchronously. Sends are not +// buffered since it is acceptable to drop packets under high load. +class AsyncPacketSocket : public sigslot::has_slots<> { + public: + enum State { + STATE_CLOSED, + STATE_BINDING, + STATE_BOUND, + STATE_CONNECTING, + STATE_CONNECTED + }; + + AsyncPacketSocket() { } + virtual ~AsyncPacketSocket() { } + + // Returns current local address. Address may be set to NULL if the + // socket is not bound yet (GetState() returns STATE_BINDING). + virtual SocketAddress GetLocalAddress() const = 0; + + // Returns remote address. Returns zeroes if this is not a client TCP socket. + virtual SocketAddress GetRemoteAddress() const = 0; + + // Send a packet. + virtual int Send(const void *pv, size_t cb, const PacketOptions& options) = 0; + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr, + const PacketOptions& options) = 0; + + // Close the socket. + virtual int Close() = 0; + + // Returns current state of the socket. + virtual State GetState() const = 0; + + // Get/set options. + virtual int GetOption(Socket::Option opt, int* value) = 0; + virtual int SetOption(Socket::Option opt, int value) = 0; + + // Get/Set current error. + // TODO: Remove SetError(). + virtual int GetError() const = 0; + virtual void SetError(int error) = 0; + + // Emitted each time a packet is read. Used only for UDP and + // connected TCP sockets. + sigslot::signal5 SignalReadPacket; + + // Emitted when the socket is currently able to send. + sigslot::signal1 SignalReadyToSend; + + // Emitted after address for the socket is allocated, i.e. binding + // is finished. State of the socket is changed from BINDING to BOUND + // (for UDP and server TCP sockets) or CONNECTING (for client TCP + // sockets). + sigslot::signal2 SignalAddressReady; + + // Emitted for client TCP sockets when state is changed from + // CONNECTING to CONNECTED. + sigslot::signal1 SignalConnect; + + // Emitted for client TCP sockets when state is changed from + // CONNECTED to CLOSED. + sigslot::signal2 SignalClose; + + // Used only for listening TCP sockets. + sigslot::signal2 SignalNewConnection; + + private: + DISALLOW_EVIL_CONSTRUCTORS(AsyncPacketSocket); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_ASYNCPACKETSOCKET_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncresolverinterface.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncresolverinterface.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncresolverinterface.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncresolverinterface.h 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,47 @@ +/* + * Copyright 2013 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_ASYNCRESOLVERINTERFACE_H_ +#define WEBRTC_BASE_ASYNCRESOLVERINTERFACE_H_ + +#include "webrtc/base/sigslot.h" +#include "webrtc/base/socketaddress.h" + +namespace rtc { + +// This interface defines the methods to resolve the address asynchronously. +class AsyncResolverInterface { + public: + AsyncResolverInterface() {} + virtual ~AsyncResolverInterface() {} + + // Start address resolve process. + virtual void Start(const SocketAddress& addr) = 0; + // Returns top most resolved address of |family| + virtual bool GetResolvedAddress(int family, SocketAddress* addr) const = 0; + // Returns error from resolver. + virtual int GetError() const = 0; + // Delete the resolver. + virtual void Destroy(bool wait) = 0; + // Returns top most resolved IPv4 address if address is resolved successfully. + // Otherwise returns address set in SetAddress. + SocketAddress address() const { + SocketAddress addr; + GetResolvedAddress(AF_INET, &addr); + return addr; + } + + // This signal is fired when address resolve process is completed. + sigslot::signal1 SignalDone; +}; + +} // namespace rtc + +#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncsocket.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncsocket.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncsocket.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncsocket.cc 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,44 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/asyncsocket.h" + +namespace rtc { + +AsyncSocket::AsyncSocket() { +} + +AsyncSocket::~AsyncSocket() { +} + +AsyncSocketAdapter::AsyncSocketAdapter(AsyncSocket* socket) : socket_(NULL) { + Attach(socket); +} + +AsyncSocketAdapter::~AsyncSocketAdapter() { + delete socket_; +} + +void AsyncSocketAdapter::Attach(AsyncSocket* socket) { + ASSERT(!socket_); + socket_ = socket; + if (socket_) { + socket_->SignalConnectEvent.connect(this, + &AsyncSocketAdapter::OnConnectEvent); + socket_->SignalReadEvent.connect(this, + &AsyncSocketAdapter::OnReadEvent); + socket_->SignalWriteEvent.connect(this, + &AsyncSocketAdapter::OnWriteEvent); + socket_->SignalCloseEvent.connect(this, + &AsyncSocketAdapter::OnCloseEvent); + } +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncsocket.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncsocket.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncsocket.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncsocket.h 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,124 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_ASYNCSOCKET_H_ +#define WEBRTC_BASE_ASYNCSOCKET_H_ + +#include "webrtc/base/common.h" +#include "webrtc/base/sigslot.h" +#include "webrtc/base/socket.h" + +namespace rtc { + +// TODO: Remove Socket and rename AsyncSocket to Socket. + +// Provides the ability to perform socket I/O asynchronously. +class AsyncSocket : public Socket { + public: + AsyncSocket(); + virtual ~AsyncSocket(); + + virtual AsyncSocket* Accept(SocketAddress* paddr) = 0; + + // SignalReadEvent and SignalWriteEvent use multi_threaded_local to allow + // access concurrently from different thread. + // For example SignalReadEvent::connect will be called in AsyncUDPSocket ctor + // but at the same time the SocketDispatcher maybe signaling the read event. + // ready to read + sigslot::signal1 SignalReadEvent; + // ready to write + sigslot::signal1 SignalWriteEvent; + sigslot::signal1 SignalConnectEvent; // connected + sigslot::signal2 SignalCloseEvent; // closed +}; + +class AsyncSocketAdapter : public AsyncSocket, public sigslot::has_slots<> { + public: + // The adapted socket may explicitly be NULL, and later assigned using Attach. + // However, subclasses which support detached mode must override any methods + // that will be called during the detached period (usually GetState()), to + // avoid dereferencing a null pointer. + explicit AsyncSocketAdapter(AsyncSocket* socket); + virtual ~AsyncSocketAdapter(); + void Attach(AsyncSocket* socket); + virtual SocketAddress GetLocalAddress() const { + return socket_->GetLocalAddress(); + } + virtual SocketAddress GetRemoteAddress() const { + return socket_->GetRemoteAddress(); + } + virtual int Bind(const SocketAddress& addr) { + return socket_->Bind(addr); + } + virtual int Connect(const SocketAddress& addr) { + return socket_->Connect(addr); + } + virtual int Send(const void* pv, size_t cb) { + return socket_->Send(pv, cb); + } + virtual int SendTo(const void* pv, size_t cb, const SocketAddress& addr) { + return socket_->SendTo(pv, cb, addr); + } + virtual int Recv(void* pv, size_t cb) { + return socket_->Recv(pv, cb); + } + virtual int RecvFrom(void* pv, size_t cb, SocketAddress* paddr) { + return socket_->RecvFrom(pv, cb, paddr); + } + virtual int Listen(int backlog) { + return socket_->Listen(backlog); + } + virtual AsyncSocket* Accept(SocketAddress* paddr) { + return socket_->Accept(paddr); + } + virtual int Close() { + return socket_->Close(); + } + virtual int GetError() const { + return socket_->GetError(); + } + virtual void SetError(int error) { + return socket_->SetError(error); + } + virtual ConnState GetState() const { + return socket_->GetState(); + } + virtual int EstimateMTU(uint16* mtu) { + return socket_->EstimateMTU(mtu); + } + virtual int GetOption(Option opt, int* value) { + return socket_->GetOption(opt, value); + } + virtual int SetOption(Option opt, int value) { + return socket_->SetOption(opt, value); + } + + protected: + virtual void OnConnectEvent(AsyncSocket* socket) { + SignalConnectEvent(this); + } + virtual void OnReadEvent(AsyncSocket* socket) { + SignalReadEvent(this); + } + virtual void OnWriteEvent(AsyncSocket* socket) { + SignalWriteEvent(this); + } + virtual void OnCloseEvent(AsyncSocket* socket, int err) { + SignalCloseEvent(this, err); + } + + AsyncSocket* socket_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_ASYNCSOCKET_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asynctcpsocket.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asynctcpsocket.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asynctcpsocket.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asynctcpsocket.cc 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,299 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/asynctcpsocket.h" + +#include + +#include "webrtc/base/byteorder.h" +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" + +#if defined(WEBRTC_POSIX) +#include +#endif // WEBRTC_POSIX + +namespace rtc { + +static const size_t kMaxPacketSize = 64 * 1024; + +typedef uint16 PacketLength; +static const size_t kPacketLenSize = sizeof(PacketLength); + +static const size_t kBufSize = kMaxPacketSize + kPacketLenSize; + +static const int kListenBacklog = 5; + +// Binds and connects |socket| +AsyncSocket* AsyncTCPSocketBase::ConnectSocket( + rtc::AsyncSocket* socket, + const rtc::SocketAddress& bind_address, + const rtc::SocketAddress& remote_address) { + rtc::scoped_ptr owned_socket(socket); + if (socket->Bind(bind_address) < 0) { + LOG(LS_ERROR) << "Bind() failed with error " << socket->GetError(); + return NULL; + } + if (socket->Connect(remote_address) < 0) { + LOG(LS_ERROR) << "Connect() failed with error " << socket->GetError(); + return NULL; + } + return owned_socket.release(); +} + +AsyncTCPSocketBase::AsyncTCPSocketBase(AsyncSocket* socket, bool listen, + size_t max_packet_size) + : socket_(socket), + listen_(listen), + insize_(max_packet_size), + inpos_(0), + outsize_(max_packet_size), + outpos_(0) { + inbuf_ = new char[insize_]; + outbuf_ = new char[outsize_]; + + ASSERT(socket_.get() != NULL); + socket_->SignalConnectEvent.connect( + this, &AsyncTCPSocketBase::OnConnectEvent); + socket_->SignalReadEvent.connect(this, &AsyncTCPSocketBase::OnReadEvent); + socket_->SignalWriteEvent.connect(this, &AsyncTCPSocketBase::OnWriteEvent); + socket_->SignalCloseEvent.connect(this, &AsyncTCPSocketBase::OnCloseEvent); + + if (listen_) { + if (socket_->Listen(kListenBacklog) < 0) { + LOG(LS_ERROR) << "Listen() failed with error " << socket_->GetError(); + } + } +} + +AsyncTCPSocketBase::~AsyncTCPSocketBase() { + delete [] inbuf_; + delete [] outbuf_; +} + +SocketAddress AsyncTCPSocketBase::GetLocalAddress() const { + return socket_->GetLocalAddress(); +} + +SocketAddress AsyncTCPSocketBase::GetRemoteAddress() const { + return socket_->GetRemoteAddress(); +} + +int AsyncTCPSocketBase::Close() { + return socket_->Close(); +} + +AsyncTCPSocket::State AsyncTCPSocketBase::GetState() const { + switch (socket_->GetState()) { + case Socket::CS_CLOSED: + return STATE_CLOSED; + case Socket::CS_CONNECTING: + if (listen_) { + return STATE_BOUND; + } else { + return STATE_CONNECTING; + } + case Socket::CS_CONNECTED: + return STATE_CONNECTED; + default: + ASSERT(false); + return STATE_CLOSED; + } +} + +int AsyncTCPSocketBase::GetOption(Socket::Option opt, int* value) { + return socket_->GetOption(opt, value); +} + +int AsyncTCPSocketBase::SetOption(Socket::Option opt, int value) { + return socket_->SetOption(opt, value); +} + +int AsyncTCPSocketBase::GetError() const { + return socket_->GetError(); +} + +void AsyncTCPSocketBase::SetError(int error) { + return socket_->SetError(error); +} + +int AsyncTCPSocketBase::SendTo(const void *pv, size_t cb, + const SocketAddress& addr, + const rtc::PacketOptions& options) { + if (addr == GetRemoteAddress()) + return Send(pv, cb, options); + + ASSERT(false); + socket_->SetError(ENOTCONN); + return -1; +} + +int AsyncTCPSocketBase::SendRaw(const void * pv, size_t cb) { + if (outpos_ + cb > outsize_) { + socket_->SetError(EMSGSIZE); + return -1; + } + + memcpy(outbuf_ + outpos_, pv, cb); + outpos_ += cb; + + return FlushOutBuffer(); +} + +int AsyncTCPSocketBase::FlushOutBuffer() { + int res = socket_->Send(outbuf_, outpos_); + if (res <= 0) { + return res; + } + if (static_cast(res) <= outpos_) { + outpos_ -= res; + } else { + ASSERT(false); + return -1; + } + if (outpos_ > 0) { + memmove(outbuf_, outbuf_ + res, outpos_); + } + return res; +} + +void AsyncTCPSocketBase::AppendToOutBuffer(const void* pv, size_t cb) { + ASSERT(outpos_ + cb < outsize_); + memcpy(outbuf_ + outpos_, pv, cb); + outpos_ += cb; +} + +void AsyncTCPSocketBase::OnConnectEvent(AsyncSocket* socket) { + SignalConnect(this); +} + +void AsyncTCPSocketBase::OnReadEvent(AsyncSocket* socket) { + ASSERT(socket_.get() == socket); + + if (listen_) { + rtc::SocketAddress address; + rtc::AsyncSocket* new_socket = socket->Accept(&address); + if (!new_socket) { + // TODO: Do something better like forwarding the error + // to the user. + LOG(LS_ERROR) << "TCP accept failed with error " << socket_->GetError(); + return; + } + + HandleIncomingConnection(new_socket); + + // Prime a read event in case data is waiting. + new_socket->SignalReadEvent(new_socket); + } else { + int len = socket_->Recv(inbuf_ + inpos_, insize_ - inpos_); + if (len < 0) { + // TODO: Do something better like forwarding the error to the user. + if (!socket_->IsBlocking()) { + LOG(LS_ERROR) << "Recv() returned error: " << socket_->GetError(); + } + return; + } + + inpos_ += len; + + ProcessInput(inbuf_, &inpos_); + + if (inpos_ >= insize_) { + LOG(LS_ERROR) << "input buffer overflow"; + ASSERT(false); + inpos_ = 0; + } + } +} + +void AsyncTCPSocketBase::OnWriteEvent(AsyncSocket* socket) { + ASSERT(socket_.get() == socket); + + if (outpos_ > 0) { + FlushOutBuffer(); + } + + if (outpos_ == 0) { + SignalReadyToSend(this); + } +} + +void AsyncTCPSocketBase::OnCloseEvent(AsyncSocket* socket, int error) { + SignalClose(this, error); +} + +// AsyncTCPSocket +// Binds and connects |socket| and creates AsyncTCPSocket for +// it. Takes ownership of |socket|. Returns NULL if bind() or +// connect() fail (|socket| is destroyed in that case). +AsyncTCPSocket* AsyncTCPSocket::Create( + AsyncSocket* socket, + const SocketAddress& bind_address, + const SocketAddress& remote_address) { + return new AsyncTCPSocket(AsyncTCPSocketBase::ConnectSocket( + socket, bind_address, remote_address), false); +} + +AsyncTCPSocket::AsyncTCPSocket(AsyncSocket* socket, bool listen) + : AsyncTCPSocketBase(socket, listen, kBufSize) { +} + +int AsyncTCPSocket::Send(const void *pv, size_t cb, + const rtc::PacketOptions& options) { + if (cb > kBufSize) { + SetError(EMSGSIZE); + return -1; + } + + // If we are blocking on send, then silently drop this packet + if (!IsOutBufferEmpty()) + return static_cast(cb); + + PacketLength pkt_len = HostToNetwork16(static_cast(cb)); + AppendToOutBuffer(&pkt_len, kPacketLenSize); + AppendToOutBuffer(pv, cb); + + int res = FlushOutBuffer(); + if (res <= 0) { + // drop packet if we made no progress + ClearOutBuffer(); + return res; + } + + // We claim to have sent the whole thing, even if we only sent partial + return static_cast(cb); +} + +void AsyncTCPSocket::ProcessInput(char * data, size_t* len) { + SocketAddress remote_addr(GetRemoteAddress()); + + while (true) { + if (*len < kPacketLenSize) + return; + + PacketLength pkt_len = rtc::GetBE16(data); + if (*len < kPacketLenSize + pkt_len) + return; + + SignalReadPacket(this, data + kPacketLenSize, pkt_len, remote_addr, + CreatePacketTime(0)); + + *len -= kPacketLenSize + pkt_len; + if (*len > 0) { + memmove(data, data + kPacketLenSize + pkt_len, *len); + } + } +} + +void AsyncTCPSocket::HandleIncomingConnection(AsyncSocket* socket) { + SignalNewConnection(this, new AsyncTCPSocket(socket, false)); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asynctcpsocket.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asynctcpsocket.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asynctcpsocket.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asynctcpsocket.h 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,100 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_ASYNCTCPSOCKET_H_ +#define WEBRTC_BASE_ASYNCTCPSOCKET_H_ + +#include "webrtc/base/asyncpacketsocket.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/socketfactory.h" + +namespace rtc { + +// Simulates UDP semantics over TCP. Send and Recv packet sizes +// are preserved, and drops packets silently on Send, rather than +// buffer them in user space. +class AsyncTCPSocketBase : public AsyncPacketSocket { + public: + AsyncTCPSocketBase(AsyncSocket* socket, bool listen, size_t max_packet_size); + virtual ~AsyncTCPSocketBase(); + + // Pure virtual methods to send and recv data. + virtual int Send(const void *pv, size_t cb, + const rtc::PacketOptions& options) = 0; + virtual void ProcessInput(char* data, size_t* len) = 0; + // Signals incoming connection. + virtual void HandleIncomingConnection(AsyncSocket* socket) = 0; + + virtual SocketAddress GetLocalAddress() const; + virtual SocketAddress GetRemoteAddress() const; + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr, + const rtc::PacketOptions& options); + virtual int Close(); + + virtual State GetState() const; + virtual int GetOption(Socket::Option opt, int* value); + virtual int SetOption(Socket::Option opt, int value); + virtual int GetError() const; + virtual void SetError(int error); + + protected: + // Binds and connects |socket| and creates AsyncTCPSocket for + // it. Takes ownership of |socket|. Returns NULL if bind() or + // connect() fail (|socket| is destroyed in that case). + static AsyncSocket* ConnectSocket(AsyncSocket* socket, + const SocketAddress& bind_address, + const SocketAddress& remote_address); + virtual int SendRaw(const void* pv, size_t cb); + int FlushOutBuffer(); + // Add data to |outbuf_|. + void AppendToOutBuffer(const void* pv, size_t cb); + + // Helper methods for |outpos_|. + bool IsOutBufferEmpty() const { return outpos_ == 0; } + void ClearOutBuffer() { outpos_ = 0; } + + private: + // Called by the underlying socket + void OnConnectEvent(AsyncSocket* socket); + void OnReadEvent(AsyncSocket* socket); + void OnWriteEvent(AsyncSocket* socket); + void OnCloseEvent(AsyncSocket* socket, int error); + + scoped_ptr socket_; + bool listen_; + char* inbuf_, * outbuf_; + size_t insize_, inpos_, outsize_, outpos_; + + DISALLOW_EVIL_CONSTRUCTORS(AsyncTCPSocketBase); +}; + +class AsyncTCPSocket : public AsyncTCPSocketBase { + public: + // Binds and connects |socket| and creates AsyncTCPSocket for + // it. Takes ownership of |socket|. Returns NULL if bind() or + // connect() fail (|socket| is destroyed in that case). + static AsyncTCPSocket* Create(AsyncSocket* socket, + const SocketAddress& bind_address, + const SocketAddress& remote_address); + AsyncTCPSocket(AsyncSocket* socket, bool listen); + virtual ~AsyncTCPSocket() {} + + virtual int Send(const void* pv, size_t cb, + const rtc::PacketOptions& options); + virtual void ProcessInput(char* data, size_t* len); + virtual void HandleIncomingConnection(AsyncSocket* socket); + + private: + DISALLOW_EVIL_CONSTRUCTORS(AsyncTCPSocket); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_ASYNCTCPSOCKET_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asynctcpsocket_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asynctcpsocket_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asynctcpsocket_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asynctcpsocket_unittest.cc 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,53 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/asynctcpsocket.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/physicalsocketserver.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/virtualsocketserver.h" + +namespace rtc { + +class AsyncTCPSocketTest + : public testing::Test, + public sigslot::has_slots<> { + public: + AsyncTCPSocketTest() + : pss_(new rtc::PhysicalSocketServer), + vss_(new rtc::VirtualSocketServer(pss_.get())), + socket_(vss_->CreateAsyncSocket(SOCK_STREAM)), + tcp_socket_(new AsyncTCPSocket(socket_, true)), + ready_to_send_(false) { + tcp_socket_->SignalReadyToSend.connect(this, + &AsyncTCPSocketTest::OnReadyToSend); + } + + void OnReadyToSend(rtc::AsyncPacketSocket* socket) { + ready_to_send_ = true; + } + + protected: + scoped_ptr pss_; + scoped_ptr vss_; + AsyncSocket* socket_; + scoped_ptr tcp_socket_; + bool ready_to_send_; +}; + +TEST_F(AsyncTCPSocketTest, OnWriteEvent) { + EXPECT_FALSE(ready_to_send_); + socket_->SignalWriteEvent(socket_); + EXPECT_TRUE(ready_to_send_); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncudpsocket.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncudpsocket.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncudpsocket.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncudpsocket.cc 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,122 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/asyncudpsocket.h" +#include "webrtc/base/logging.h" + +namespace rtc { + +static const int BUF_SIZE = 64 * 1024; + +AsyncUDPSocket* AsyncUDPSocket::Create( + AsyncSocket* socket, + const SocketAddress& bind_address) { + scoped_ptr owned_socket(socket); + if (socket->Bind(bind_address) < 0) { + LOG(LS_ERROR) << "Bind() failed with error " << socket->GetError(); + return NULL; + } + return new AsyncUDPSocket(owned_socket.release()); +} + +AsyncUDPSocket* AsyncUDPSocket::Create(SocketFactory* factory, + const SocketAddress& bind_address) { + AsyncSocket* socket = + factory->CreateAsyncSocket(bind_address.family(), SOCK_DGRAM); + if (!socket) + return NULL; + return Create(socket, bind_address); +} + +AsyncUDPSocket::AsyncUDPSocket(AsyncSocket* socket) + : socket_(socket) { + ASSERT(socket_); + size_ = BUF_SIZE; + buf_ = new char[size_]; + + // The socket should start out readable but not writable. + socket_->SignalReadEvent.connect(this, &AsyncUDPSocket::OnReadEvent); + socket_->SignalWriteEvent.connect(this, &AsyncUDPSocket::OnWriteEvent); +} + +AsyncUDPSocket::~AsyncUDPSocket() { + delete [] buf_; +} + +SocketAddress AsyncUDPSocket::GetLocalAddress() const { + return socket_->GetLocalAddress(); +} + +SocketAddress AsyncUDPSocket::GetRemoteAddress() const { + return socket_->GetRemoteAddress(); +} + +int AsyncUDPSocket::Send(const void *pv, size_t cb, + const rtc::PacketOptions& options) { + return socket_->Send(pv, cb); +} + +int AsyncUDPSocket::SendTo(const void *pv, size_t cb, + const SocketAddress& addr, + const rtc::PacketOptions& options) { + return socket_->SendTo(pv, cb, addr); +} + +int AsyncUDPSocket::Close() { + return socket_->Close(); +} + +AsyncUDPSocket::State AsyncUDPSocket::GetState() const { + return STATE_BOUND; +} + +int AsyncUDPSocket::GetOption(Socket::Option opt, int* value) { + return socket_->GetOption(opt, value); +} + +int AsyncUDPSocket::SetOption(Socket::Option opt, int value) { + return socket_->SetOption(opt, value); +} + +int AsyncUDPSocket::GetError() const { + return socket_->GetError(); +} + +void AsyncUDPSocket::SetError(int error) { + return socket_->SetError(error); +} + +void AsyncUDPSocket::OnReadEvent(AsyncSocket* socket) { + ASSERT(socket_.get() == socket); + + SocketAddress remote_addr; + int len = socket_->RecvFrom(buf_, size_, &remote_addr); + if (len < 0) { + // An error here typically means we got an ICMP error in response to our + // send datagram, indicating the remote address was unreachable. + // When doing ICE, this kind of thing will often happen. + // TODO: Do something better like forwarding the error to the user. + SocketAddress local_addr = socket_->GetLocalAddress(); + LOG(LS_INFO) << "AsyncUDPSocket[" << local_addr.ToSensitiveString() << "] " + << "receive failed with error " << socket_->GetError(); + return; + } + + // TODO: Make sure that we got all of the packet. + // If we did not, then we should resize our buffer to be large enough. + SignalReadPacket(this, buf_, static_cast(len), remote_addr, + CreatePacketTime(0)); +} + +void AsyncUDPSocket::OnWriteEvent(AsyncSocket* socket) { + SignalReadyToSend(this); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncudpsocket.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncudpsocket.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncudpsocket.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncudpsocket.h 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,63 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_ASYNCUDPSOCKET_H_ +#define WEBRTC_BASE_ASYNCUDPSOCKET_H_ + +#include "webrtc/base/asyncpacketsocket.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/socketfactory.h" + +namespace rtc { + +// Provides the ability to receive packets asynchronously. Sends are not +// buffered since it is acceptable to drop packets under high load. +class AsyncUDPSocket : public AsyncPacketSocket { + public: + // Binds |socket| and creates AsyncUDPSocket for it. Takes ownership + // of |socket|. Returns NULL if bind() fails (|socket| is destroyed + // in that case). + static AsyncUDPSocket* Create(AsyncSocket* socket, + const SocketAddress& bind_address); + // Creates a new socket for sending asynchronous UDP packets using an + // asynchronous socket from the given factory. + static AsyncUDPSocket* Create(SocketFactory* factory, + const SocketAddress& bind_address); + explicit AsyncUDPSocket(AsyncSocket* socket); + virtual ~AsyncUDPSocket(); + + virtual SocketAddress GetLocalAddress() const; + virtual SocketAddress GetRemoteAddress() const; + virtual int Send(const void *pv, size_t cb, + const rtc::PacketOptions& options); + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr, + const rtc::PacketOptions& options); + virtual int Close(); + + virtual State GetState() const; + virtual int GetOption(Socket::Option opt, int* value); + virtual int SetOption(Socket::Option opt, int value); + virtual int GetError() const; + virtual void SetError(int error); + + private: + // Called when the underlying socket is ready to be read from. + void OnReadEvent(AsyncSocket* socket); + // Called when the underlying socket is ready to send. + void OnWriteEvent(AsyncSocket* socket); + + scoped_ptr socket_; + char* buf_; + size_t size_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_ASYNCUDPSOCKET_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncudpsocket_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncudpsocket_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/asyncudpsocket_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/asyncudpsocket_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,53 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/asyncudpsocket.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/physicalsocketserver.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/virtualsocketserver.h" + +namespace rtc { + +class AsyncUdpSocketTest + : public testing::Test, + public sigslot::has_slots<> { + public: + AsyncUdpSocketTest() + : pss_(new rtc::PhysicalSocketServer), + vss_(new rtc::VirtualSocketServer(pss_.get())), + socket_(vss_->CreateAsyncSocket(SOCK_DGRAM)), + udp_socket_(new AsyncUDPSocket(socket_)), + ready_to_send_(false) { + udp_socket_->SignalReadyToSend.connect(this, + &AsyncUdpSocketTest::OnReadyToSend); + } + + void OnReadyToSend(rtc::AsyncPacketSocket* socket) { + ready_to_send_ = true; + } + + protected: + scoped_ptr pss_; + scoped_ptr vss_; + AsyncSocket* socket_; + scoped_ptr udp_socket_; + bool ready_to_send_; +}; + +TEST_F(AsyncUdpSocketTest, OnWriteEvent) { + EXPECT_FALSE(ready_to_send_); + socket_->SignalWriteEvent(socket_); + EXPECT_TRUE(ready_to_send_); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/atomicops.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/atomicops.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/atomicops.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/atomicops.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,149 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_ATOMICOPS_H_ +#define WEBRTC_BASE_ATOMICOPS_H_ + +#include + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/scoped_ptr.h" + +namespace rtc { + +// A single-producer, single-consumer, fixed-size queue. +// All methods not ending in Unsafe can be safely called without locking, +// provided that calls to consumer methods (Peek/Pop) or producer methods (Push) +// only happen on a single thread per method type. If multiple threads need to +// read simultaneously or write simultaneously, other synchronization is +// necessary. Synchronization is also required if a call into any Unsafe method +// could happen at the same time as a call to any other method. +template +class FixedSizeLockFreeQueue { + private: +// Atomic primitives and memory barrier +#if defined(__arm__) + typedef uint32 Atomic32; + + // Copied from google3/base/atomicops-internals-arm-v6plus.h + static inline void MemoryBarrier() { + asm volatile("dmb":::"memory"); + } + + // Adapted from google3/base/atomicops-internals-arm-v6plus.h + static inline void AtomicIncrement(volatile Atomic32* ptr) { + Atomic32 str_success, value; + asm volatile ( + "1:\n" + "ldrex %1, [%2]\n" + "add %1, %1, #1\n" + "strex %0, %1, [%2]\n" + "teq %0, #0\n" + "bne 1b" + : "=&r"(str_success), "=&r"(value) + : "r" (ptr) + : "cc", "memory"); + } +#elif !defined(SKIP_ATOMIC_CHECK) +#error "No atomic operations defined for the given architecture." +#endif + + public: + // Constructs an empty queue, with capacity 0. + FixedSizeLockFreeQueue() : pushed_count_(0), + popped_count_(0), + capacity_(0), + data_() {} + // Constructs an empty queue with the given capacity. + FixedSizeLockFreeQueue(size_t capacity) : pushed_count_(0), + popped_count_(0), + capacity_(capacity), + data_(new T[capacity]) {} + + // Pushes a value onto the queue. Returns true if the value was successfully + // pushed (there was space in the queue). This method can be safely called at + // the same time as PeekFront/PopFront. + bool PushBack(T value) { + if (capacity_ == 0) { + LOG(LS_WARNING) << "Queue capacity is 0."; + return false; + } + if (IsFull()) { + return false; + } + + data_[pushed_count_ % capacity_] = value; + // Make sure the data is written before the count is incremented, so other + // threads can't see the value exists before being able to read it. + MemoryBarrier(); + AtomicIncrement(&pushed_count_); + return true; + } + + // Retrieves the oldest value pushed onto the queue. Returns true if there was + // an item to peek (the queue was non-empty). This method can be safely called + // at the same time as PushBack. + bool PeekFront(T* value_out) { + if (capacity_ == 0) { + LOG(LS_WARNING) << "Queue capacity is 0."; + return false; + } + if (IsEmpty()) { + return false; + } + + *value_out = data_[popped_count_ % capacity_]; + return true; + } + + // Retrieves the oldest value pushed onto the queue and removes it from the + // queue. Returns true if there was an item to pop (the queue was non-empty). + // This method can be safely called at the same time as PushBack. + bool PopFront(T* value_out) { + if (PeekFront(value_out)) { + AtomicIncrement(&popped_count_); + return true; + } + return false; + } + + // Clears the current items in the queue and sets the new (fixed) size. This + // method cannot be called at the same time as any other method. + void ClearAndResizeUnsafe(int new_capacity) { + capacity_ = new_capacity; + data_.reset(new T[new_capacity]); + pushed_count_ = 0; + popped_count_ = 0; + } + + // Returns true if there is no space left in the queue for new elements. + int IsFull() const { return pushed_count_ == popped_count_ + capacity_; } + // Returns true if there are no elements in the queue. + int IsEmpty() const { return pushed_count_ == popped_count_; } + // Returns the current number of elements in the queue. This is always in the + // range [0, capacity] + size_t Size() const { return pushed_count_ - popped_count_; } + + // Returns the capacity of the queue (max size). + size_t capacity() const { return capacity_; } + + private: + volatile Atomic32 pushed_count_; + volatile Atomic32 popped_count_; + size_t capacity_; + rtc::scoped_ptr data_; + DISALLOW_COPY_AND_ASSIGN(FixedSizeLockFreeQueue); +}; + +} + +#endif // WEBRTC_BASE_ATOMICOPS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/atomicops_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/atomicops_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/atomicops_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/atomicops_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,79 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if !defined(__arm__) +// For testing purposes, define faked versions of the atomic operations +#include "webrtc/base/basictypes.h" +namespace rtc { +typedef uint32 Atomic32; +static inline void MemoryBarrier() { } +static inline void AtomicIncrement(volatile Atomic32* ptr) { + *ptr = *ptr + 1; +} +} +#define SKIP_ATOMIC_CHECK +#endif + +#include "webrtc/base/atomicops.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/helpers.h" +#include "webrtc/base/logging.h" + +TEST(FixedSizeLockFreeQueueTest, TestDefaultConstruct) { + rtc::FixedSizeLockFreeQueue queue; + EXPECT_EQ(0u, queue.capacity()); + EXPECT_EQ(0u, queue.Size()); + EXPECT_FALSE(queue.PushBack(1)); + int val; + EXPECT_FALSE(queue.PopFront(&val)); +} + +TEST(FixedSizeLockFreeQueueTest, TestConstruct) { + rtc::FixedSizeLockFreeQueue queue(5); + EXPECT_EQ(5u, queue.capacity()); + EXPECT_EQ(0u, queue.Size()); + int val; + EXPECT_FALSE(queue.PopFront(&val)); +} + +TEST(FixedSizeLockFreeQueueTest, TestPushPop) { + rtc::FixedSizeLockFreeQueue queue(2); + EXPECT_EQ(2u, queue.capacity()); + EXPECT_EQ(0u, queue.Size()); + EXPECT_TRUE(queue.PushBack(1)); + EXPECT_EQ(1u, queue.Size()); + EXPECT_TRUE(queue.PushBack(2)); + EXPECT_EQ(2u, queue.Size()); + EXPECT_FALSE(queue.PushBack(3)); + EXPECT_EQ(2u, queue.Size()); + int val; + EXPECT_TRUE(queue.PopFront(&val)); + EXPECT_EQ(1, val); + EXPECT_EQ(1u, queue.Size()); + EXPECT_TRUE(queue.PopFront(&val)); + EXPECT_EQ(2, val); + EXPECT_EQ(0u, queue.Size()); + EXPECT_FALSE(queue.PopFront(&val)); + EXPECT_EQ(0u, queue.Size()); +} + +TEST(FixedSizeLockFreeQueueTest, TestResize) { + rtc::FixedSizeLockFreeQueue queue(2); + EXPECT_EQ(2u, queue.capacity()); + EXPECT_EQ(0u, queue.Size()); + EXPECT_TRUE(queue.PushBack(1)); + EXPECT_EQ(1u, queue.Size()); + + queue.ClearAndResizeUnsafe(5); + EXPECT_EQ(5u, queue.capacity()); + EXPECT_EQ(0u, queue.Size()); + int val; + EXPECT_FALSE(queue.PopFront(&val)); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/autodetectproxy.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/autodetectproxy.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/autodetectproxy.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/autodetectproxy.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,282 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/autodetectproxy.h" +#include "webrtc/base/httpcommon.h" +#include "webrtc/base/httpcommon-inl.h" +#include "webrtc/base/nethelpers.h" + +namespace rtc { + +static const ProxyType TEST_ORDER[] = { + PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN +}; + +static const int kSavedStringLimit = 128; + +static void SaveStringToStack(char *dst, + const std::string &src, + size_t dst_size) { + strncpy(dst, src.c_str(), dst_size - 1); + dst[dst_size - 1] = '\0'; +} + +AutoDetectProxy::AutoDetectProxy(const std::string& user_agent) + : agent_(user_agent), resolver_(NULL), socket_(NULL), next_(0) { +} + +AutoDetectProxy::~AutoDetectProxy() { + if (resolver_) { + resolver_->Destroy(false); + } +} + +void AutoDetectProxy::DoWork() { + // TODO: Try connecting to server_url without proxy first here? + if (!server_url_.empty()) { + LOG(LS_INFO) << "GetProxySettingsForUrl(" << server_url_ << ") - start"; + GetProxyForUrl(agent_.c_str(), server_url_.c_str(), &proxy_); + LOG(LS_INFO) << "GetProxySettingsForUrl - stop"; + } + Url url(proxy_.address.HostAsURIString()); + if (url.valid()) { + LOG(LS_WARNING) << "AutoDetectProxy removing http prefix on proxy host"; + proxy_.address.SetIP(url.host()); + } + LOG(LS_INFO) << "AutoDetectProxy found proxy at " << proxy_.address; + if (proxy_.type == PROXY_UNKNOWN) { + LOG(LS_INFO) << "AutoDetectProxy initiating proxy classification"; + Next(); + // Process I/O until Stop() + Thread::Current()->ProcessMessages(kForever); + // Clean up the autodetect socket, from the thread that created it + delete socket_; + } + // TODO: If we found a proxy, try to use it to verify that it + // works by sending a request to server_url. This could either be + // done here or by the HttpPortAllocator. +} + +void AutoDetectProxy::OnMessage(Message *msg) { + if (MSG_UNRESOLVABLE == msg->message_id) { + // If we can't resolve the proxy, skip straight to failure. + Complete(PROXY_UNKNOWN); + } else if (MSG_TIMEOUT == msg->message_id) { + OnCloseEvent(socket_, ETIMEDOUT); + } else { + // This must be the ST_MSG_WORKER_DONE message that deletes the + // AutoDetectProxy object. We have observed crashes within this stack that + // seem to be highly reproducible for a small subset of users and thus are + // probably correlated with a specific proxy setting, so copy potentially + // relevant information onto the stack to make it available in Windows + // minidumps. + + // Save the user agent and the number of auto-detection passes that we + // needed. + char agent[kSavedStringLimit]; + SaveStringToStack(agent, agent_, sizeof agent); + + int next = next_; + + // Now the detected proxy config (minus the password field, which could be + // sensitive). + ProxyType type = proxy().type; + + char address_hostname[kSavedStringLimit]; + SaveStringToStack(address_hostname, + proxy().address.hostname(), + sizeof address_hostname); + + IPAddress address_ip = proxy().address.ipaddr(); + + uint16 address_port = proxy().address.port(); + + char autoconfig_url[kSavedStringLimit]; + SaveStringToStack(autoconfig_url, + proxy().autoconfig_url, + sizeof autoconfig_url); + + bool autodetect = proxy().autodetect; + + char bypass_list[kSavedStringLimit]; + SaveStringToStack(bypass_list, proxy().bypass_list, sizeof bypass_list); + + char username[kSavedStringLimit]; + SaveStringToStack(username, proxy().username, sizeof username); + + SignalThread::OnMessage(msg); + + // Log the gathered data at a log level that will never actually be enabled + // so that the compiler is forced to retain the data on the stack. + LOG(LS_SENSITIVE) << agent << " " << next << " " << type << " " + << address_hostname << " " << address_ip << " " + << address_port << " " << autoconfig_url << " " + << autodetect << " " << bypass_list << " " << username; + } +} + +void AutoDetectProxy::OnResolveResult(AsyncResolverInterface* resolver) { + if (resolver != resolver_) { + return; + } + int error = resolver_->GetError(); + if (error == 0) { + LOG(LS_VERBOSE) << "Resolved " << proxy_.address << " to " + << resolver_->address(); + proxy_.address = resolver_->address(); + if (!DoConnect()) { + Thread::Current()->Post(this, MSG_TIMEOUT); + } + } else { + LOG(LS_INFO) << "Failed to resolve " << resolver_->address(); + resolver_->Destroy(false); + resolver_ = NULL; + proxy_.address = SocketAddress(); + Thread::Current()->Post(this, MSG_UNRESOLVABLE); + } +} + +void AutoDetectProxy::Next() { + if (TEST_ORDER[next_] >= PROXY_UNKNOWN) { + Complete(PROXY_UNKNOWN); + return; + } + + LOG(LS_VERBOSE) << "AutoDetectProxy connecting to " + << proxy_.address.ToSensitiveString(); + + if (socket_) { + Thread::Current()->Clear(this, MSG_TIMEOUT); + Thread::Current()->Clear(this, MSG_UNRESOLVABLE); + socket_->Close(); + Thread::Current()->Dispose(socket_); + socket_ = NULL; + } + int timeout = 2000; + if (proxy_.address.IsUnresolvedIP()) { + // Launch an asyncresolver. This thread will spin waiting for it. + timeout += 2000; + if (!resolver_) { + resolver_ = new AsyncResolver(); + } + resolver_->SignalDone.connect(this, &AutoDetectProxy::OnResolveResult); + resolver_->Start(proxy_.address); + } else { + if (!DoConnect()) { + Thread::Current()->Post(this, MSG_TIMEOUT); + return; + } + } + Thread::Current()->PostDelayed(timeout, this, MSG_TIMEOUT); +} + +bool AutoDetectProxy::DoConnect() { + if (resolver_) { + resolver_->Destroy(false); + resolver_ = NULL; + } + socket_ = + Thread::Current()->socketserver()->CreateAsyncSocket( + proxy_.address.family(), SOCK_STREAM); + if (!socket_) { + LOG(LS_VERBOSE) << "Unable to create socket for " << proxy_.address; + return false; + } + socket_->SignalConnectEvent.connect(this, &AutoDetectProxy::OnConnectEvent); + socket_->SignalReadEvent.connect(this, &AutoDetectProxy::OnReadEvent); + socket_->SignalCloseEvent.connect(this, &AutoDetectProxy::OnCloseEvent); + socket_->Connect(proxy_.address); + return true; +} + +void AutoDetectProxy::Complete(ProxyType type) { + Thread::Current()->Clear(this, MSG_TIMEOUT); + Thread::Current()->Clear(this, MSG_UNRESOLVABLE); + if (socket_) { + socket_->Close(); + } + + proxy_.type = type; + LoggingSeverity sev = (proxy_.type == PROXY_UNKNOWN) ? LS_ERROR : LS_INFO; + LOG_V(sev) << "AutoDetectProxy detected " + << proxy_.address.ToSensitiveString() + << " as type " << proxy_.type; + + Thread::Current()->Quit(); +} + +void AutoDetectProxy::OnConnectEvent(AsyncSocket * socket) { + std::string probe; + + switch (TEST_ORDER[next_]) { + case PROXY_HTTPS: + probe.assign("CONNECT www.google.com:443 HTTP/1.0\r\n" + "User-Agent: "); + probe.append(agent_); + probe.append("\r\n" + "Host: www.google.com\r\n" + "Content-Length: 0\r\n" + "Proxy-Connection: Keep-Alive\r\n" + "\r\n"); + break; + case PROXY_SOCKS5: + probe.assign("\005\001\000", 3); + break; + default: + ASSERT(false); + return; + } + + LOG(LS_VERBOSE) << "AutoDetectProxy probing type " << TEST_ORDER[next_] + << " sending " << probe.size() << " bytes"; + socket_->Send(probe.data(), probe.size()); +} + +void AutoDetectProxy::OnReadEvent(AsyncSocket * socket) { + char data[257]; + int len = socket_->Recv(data, 256); + if (len > 0) { + data[len] = 0; + LOG(LS_VERBOSE) << "AutoDetectProxy read " << len << " bytes"; + } + + switch (TEST_ORDER[next_]) { + case PROXY_HTTPS: + if ((len >= 2) && (data[0] == '\x05')) { + Complete(PROXY_SOCKS5); + return; + } + if ((len >= 5) && (strncmp(data, "HTTP/", 5) == 0)) { + Complete(PROXY_HTTPS); + return; + } + break; + case PROXY_SOCKS5: + if ((len >= 2) && (data[0] == '\x05')) { + Complete(PROXY_SOCKS5); + return; + } + break; + default: + ASSERT(false); + return; + } + + ++next_; + Next(); +} + +void AutoDetectProxy::OnCloseEvent(AsyncSocket * socket, int error) { + LOG(LS_VERBOSE) << "AutoDetectProxy closed with error: " << error; + ++next_; + Next(); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/autodetectproxy.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/autodetectproxy.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/autodetectproxy.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/autodetectproxy.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,90 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_AUTODETECTPROXY_H_ +#define WEBRTC_BASE_AUTODETECTPROXY_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/cryptstring.h" +#include "webrtc/base/proxydetect.h" +#include "webrtc/base/proxyinfo.h" +#include "webrtc/base/signalthread.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// AutoDetectProxy +/////////////////////////////////////////////////////////////////////////////// + +class AsyncResolverInterface; +class AsyncSocket; + +class AutoDetectProxy : public SignalThread { + public: + explicit AutoDetectProxy(const std::string& user_agent); + + const ProxyInfo& proxy() const { return proxy_; } + + void set_server_url(const std::string& url) { + server_url_ = url; + } + void set_proxy(const SocketAddress& proxy) { + proxy_.type = PROXY_UNKNOWN; + proxy_.address = proxy; + } + void set_auth_info(bool use_auth, const std::string& username, + const CryptString& password) { + if (use_auth) { + proxy_.username = username; + proxy_.password = password; + } + } + // Default implementation of GetProxySettingsForUrl. Override for special + // implementation. + virtual bool GetProxyForUrl(const char* agent, const char* url, + rtc::ProxyInfo* proxy) { + return GetProxySettingsForUrl(agent, url, proxy, true); + } + enum { MSG_TIMEOUT = SignalThread::ST_MSG_FIRST_AVAILABLE, + MSG_UNRESOLVABLE, + ADP_MSG_FIRST_AVAILABLE}; + + protected: + virtual ~AutoDetectProxy(); + + // SignalThread Interface + virtual void DoWork(); + virtual void OnMessage(Message *msg); + + void Next(); + void Complete(ProxyType type); + + void OnConnectEvent(AsyncSocket * socket); + void OnReadEvent(AsyncSocket * socket); + void OnCloseEvent(AsyncSocket * socket, int error); + void OnResolveResult(AsyncResolverInterface* resolver); + bool DoConnect(); + + private: + std::string agent_; + std::string server_url_; + ProxyInfo proxy_; + AsyncResolverInterface* resolver_; + AsyncSocket* socket_; + int next_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(AutoDetectProxy); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_AUTODETECTPROXY_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/autodetectproxy_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/autodetectproxy_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/autodetectproxy_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/autodetectproxy_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,132 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/autodetectproxy.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/httpcommon.h" +#include "webrtc/base/httpcommon-inl.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace rtc { + +static const char kUserAgent[] = ""; +static const char kPath[] = "/"; +static const char kHost[] = "relay.google.com"; +static const uint16 kPort = 443; +static const bool kSecure = true; +// At most, AutoDetectProxy should take ~6 seconds. Each connect step is +// allotted 2 seconds, with the initial resolution + connect given an +// extra 2 seconds. The slowest case is: +// 1) Resolution + HTTPS takes full 4 seconds and fails (but resolution +// succeeds). +// 2) SOCKS5 takes the full 2 seconds. +// Socket creation time seems unbounded, and has been observed to take >1 second +// on a linux machine under load. As such, we allow for 10 seconds for timeout, +// though could still end up with some flakiness. +static const int kTimeoutMs = 10000; + +class AutoDetectProxyTest : public testing::Test, public sigslot::has_slots<> { + public: + AutoDetectProxyTest() : auto_detect_proxy_(NULL), done_(false) {} + + protected: + bool Create(const std::string &user_agent, + const std::string &path, + const std::string &host, + uint16 port, + bool secure, + bool startnow) { + auto_detect_proxy_ = new AutoDetectProxy(user_agent); + EXPECT_TRUE(auto_detect_proxy_ != NULL); + if (!auto_detect_proxy_) { + return false; + } + Url host_url(path, host, port); + host_url.set_secure(secure); + auto_detect_proxy_->set_server_url(host_url.url()); + auto_detect_proxy_->SignalWorkDone.connect( + this, + &AutoDetectProxyTest::OnWorkDone); + if (startnow) { + auto_detect_proxy_->Start(); + } + return true; + } + + bool Run(int timeout_ms) { + EXPECT_TRUE_WAIT(done_, timeout_ms); + return done_; + } + + void SetProxy(const SocketAddress& proxy) { + auto_detect_proxy_->set_proxy(proxy); + } + + void Start() { + auto_detect_proxy_->Start(); + } + + void TestCopesWithProxy(const SocketAddress& proxy) { + // Tests that at least autodetect doesn't crash for a given proxy address. + ASSERT_TRUE(Create(kUserAgent, + kPath, + kHost, + kPort, + kSecure, + false)); + SetProxy(proxy); + Start(); + ASSERT_TRUE(Run(kTimeoutMs)); + } + + private: + void OnWorkDone(rtc::SignalThread *thread) { + AutoDetectProxy *auto_detect_proxy = + static_cast(thread); + EXPECT_TRUE(auto_detect_proxy == auto_detect_proxy_); + auto_detect_proxy_ = NULL; + auto_detect_proxy->Release(); + done_ = true; + } + + AutoDetectProxy *auto_detect_proxy_; + bool done_; +}; + +TEST_F(AutoDetectProxyTest, TestDetectUnresolvedProxy) { + TestCopesWithProxy(rtc::SocketAddress("localhost", 9999)); +} + +TEST_F(AutoDetectProxyTest, TestDetectUnresolvableProxy) { + TestCopesWithProxy(rtc::SocketAddress("invalid", 9999)); +} + +TEST_F(AutoDetectProxyTest, TestDetectIPv6Proxy) { + TestCopesWithProxy(rtc::SocketAddress("::1", 9999)); +} + +TEST_F(AutoDetectProxyTest, TestDetectIPv4Proxy) { + TestCopesWithProxy(rtc::SocketAddress("127.0.0.1", 9999)); +} + +// Test that proxy detection completes successfully. (Does not actually verify +// the correct detection result since we don't know what proxy to expect on an +// arbitrary machine.) +TEST_F(AutoDetectProxyTest, TestProxyDetection) { + ASSERT_TRUE(Create(kUserAgent, + kPath, + kHost, + kPort, + kSecure, + true)); + ASSERT_TRUE(Run(kTimeoutMs)); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bandwidthsmoother.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bandwidthsmoother.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bandwidthsmoother.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bandwidthsmoother.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,84 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/bandwidthsmoother.h" + +#include + +namespace rtc { + +BandwidthSmoother::BandwidthSmoother(int initial_bandwidth_guess, + uint32 time_between_increase, + double percent_increase, + size_t samples_count_to_average, + double min_sample_count_percent) + : time_between_increase_(time_between_increase), + percent_increase_(rtc::_max(1.0, percent_increase)), + time_at_last_change_(0), + bandwidth_estimation_(initial_bandwidth_guess), + accumulator_(samples_count_to_average), + min_sample_count_percent_( + rtc::_min(1.0, + rtc::_max(0.0, min_sample_count_percent))) { +} + +// Samples a new bandwidth measurement +// returns true if the bandwidth estimation changed +bool BandwidthSmoother::Sample(uint32 sample_time, int bandwidth) { + if (bandwidth < 0) { + return false; + } + + accumulator_.AddSample(bandwidth); + + if (accumulator_.count() < static_cast( + accumulator_.max_count() * min_sample_count_percent_)) { + // We have not collected enough samples yet. + return false; + } + + // Replace bandwidth with the mean of sampled bandwidths. + const int mean_bandwidth = static_cast(accumulator_.ComputeMean()); + + if (mean_bandwidth < bandwidth_estimation_) { + time_at_last_change_ = sample_time; + bandwidth_estimation_ = mean_bandwidth; + return true; + } + + const int old_bandwidth_estimation = bandwidth_estimation_; + const double increase_threshold_d = percent_increase_ * bandwidth_estimation_; + if (increase_threshold_d > INT_MAX) { + // If bandwidth goes any higher we would overflow. + return false; + } + + const int increase_threshold = static_cast(increase_threshold_d); + if (mean_bandwidth < increase_threshold) { + time_at_last_change_ = sample_time; + // The value of bandwidth_estimation remains the same if we don't exceed + // percent_increase_ * bandwidth_estimation_ for at least + // time_between_increase_ time. + } else if (sample_time >= time_at_last_change_ + time_between_increase_) { + time_at_last_change_ = sample_time; + if (increase_threshold == 0) { + // Bandwidth_estimation_ must be zero. Assume a jump from zero to a + // positive bandwidth means we have regained connectivity. + bandwidth_estimation_ = mean_bandwidth; + } else { + bandwidth_estimation_ = increase_threshold; + } + } + // Else don't make a change. + + return old_bandwidth_estimation != bandwidth_estimation_; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bandwidthsmoother.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bandwidthsmoother.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bandwidthsmoother.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bandwidthsmoother.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,59 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_BANDWIDTHSMOOTHER_H_ +#define WEBRTC_BASE_BANDWIDTHSMOOTHER_H_ + +#include "webrtc/base/rollingaccumulator.h" +#include "webrtc/base/timeutils.h" + +namespace rtc { + +// The purpose of BandwidthSmoother is to smooth out bandwidth +// estimations so that 'trstate' messages can be triggered when we +// are "sure" there is sufficient bandwidth. To avoid frequent fluctuations, +// we take a slightly pessimistic view of our bandwidth. We only increase +// our estimation when we have sampled bandwidth measurements of values +// at least as large as the current estimation * percent_increase +// for at least time_between_increase time. If a sampled bandwidth +// is less than our current estimation we immediately decrease our estimation +// to that sampled value. +// We retain the initial bandwidth guess as our current bandwidth estimation +// until we have received (min_sample_count_percent * samples_count_to_average) +// number of samples. Min_sample_count_percent must be in range [0, 1]. +class BandwidthSmoother { + public: + BandwidthSmoother(int initial_bandwidth_guess, + uint32 time_between_increase, + double percent_increase, + size_t samples_count_to_average, + double min_sample_count_percent); + + // Samples a new bandwidth measurement. + // bandwidth is expected to be non-negative. + // returns true if the bandwidth estimation changed + bool Sample(uint32 sample_time, int bandwidth); + + int get_bandwidth_estimation() const { + return bandwidth_estimation_; + } + + private: + uint32 time_between_increase_; + double percent_increase_; + uint32 time_at_last_change_; + int bandwidth_estimation_; + RollingAccumulator accumulator_; + double min_sample_count_percent_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_BANDWIDTHSMOOTHER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bandwidthsmoother_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bandwidthsmoother_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bandwidthsmoother_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bandwidthsmoother_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,116 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/bandwidthsmoother.h" +#include "webrtc/base/gunit.h" + +namespace rtc { + +static const int kTimeBetweenIncrease = 10; +static const double kPercentIncrease = 1.1; +static const size_t kSamplesCountToAverage = 2; +static const double kMinSampleCountPercent = 1.0; + +TEST(BandwidthSmootherTest, TestSampleIncrease) { + BandwidthSmoother mon(1000, // initial_bandwidth_guess + kTimeBetweenIncrease, + kPercentIncrease, + kSamplesCountToAverage, + kMinSampleCountPercent); + + int bandwidth_sample = 1000; + EXPECT_EQ(bandwidth_sample, mon.get_bandwidth_estimation()); + bandwidth_sample = + static_cast(bandwidth_sample * kPercentIncrease); + EXPECT_FALSE(mon.Sample(9, bandwidth_sample)); + EXPECT_TRUE(mon.Sample(10, bandwidth_sample)); + EXPECT_EQ(bandwidth_sample, mon.get_bandwidth_estimation()); + int next_expected_est = + static_cast(bandwidth_sample * kPercentIncrease); + bandwidth_sample *= 2; + EXPECT_TRUE(mon.Sample(20, bandwidth_sample)); + EXPECT_EQ(next_expected_est, mon.get_bandwidth_estimation()); +} + +TEST(BandwidthSmootherTest, TestSampleIncreaseFromZero) { + BandwidthSmoother mon(0, // initial_bandwidth_guess + kTimeBetweenIncrease, + kPercentIncrease, + kSamplesCountToAverage, + kMinSampleCountPercent); + + const int kBandwidthSample = 1000; + EXPECT_EQ(0, mon.get_bandwidth_estimation()); + EXPECT_FALSE(mon.Sample(9, kBandwidthSample)); + EXPECT_TRUE(mon.Sample(10, kBandwidthSample)); + EXPECT_EQ(kBandwidthSample, mon.get_bandwidth_estimation()); +} + +TEST(BandwidthSmootherTest, TestSampleDecrease) { + BandwidthSmoother mon(1000, // initial_bandwidth_guess + kTimeBetweenIncrease, + kPercentIncrease, + kSamplesCountToAverage, + kMinSampleCountPercent); + + const int kBandwidthSample = 999; + EXPECT_EQ(1000, mon.get_bandwidth_estimation()); + EXPECT_FALSE(mon.Sample(1, kBandwidthSample)); + EXPECT_EQ(1000, mon.get_bandwidth_estimation()); + EXPECT_TRUE(mon.Sample(2, kBandwidthSample)); + EXPECT_EQ(kBandwidthSample, mon.get_bandwidth_estimation()); +} + +TEST(BandwidthSmootherTest, TestSampleTooFewSamples) { + BandwidthSmoother mon(1000, // initial_bandwidth_guess + kTimeBetweenIncrease, + kPercentIncrease, + 10, // 10 samples. + 0.5); // 5 min samples. + + const int kBandwidthSample = 500; + EXPECT_EQ(1000, mon.get_bandwidth_estimation()); + EXPECT_FALSE(mon.Sample(1, kBandwidthSample)); + EXPECT_FALSE(mon.Sample(2, kBandwidthSample)); + EXPECT_FALSE(mon.Sample(3, kBandwidthSample)); + EXPECT_FALSE(mon.Sample(4, kBandwidthSample)); + EXPECT_EQ(1000, mon.get_bandwidth_estimation()); + EXPECT_TRUE(mon.Sample(5, kBandwidthSample)); + EXPECT_EQ(kBandwidthSample, mon.get_bandwidth_estimation()); +} + +TEST(BandwidthSmootherTest, TestSampleRollover) { + const int kHugeBandwidth = 2000000000; // > INT_MAX/1.1 + BandwidthSmoother mon(kHugeBandwidth, + kTimeBetweenIncrease, + kPercentIncrease, + kSamplesCountToAverage, + kMinSampleCountPercent); + + EXPECT_FALSE(mon.Sample(10, INT_MAX)); + EXPECT_FALSE(mon.Sample(11, INT_MAX)); + EXPECT_EQ(kHugeBandwidth, mon.get_bandwidth_estimation()); +} + +TEST(BandwidthSmootherTest, TestSampleNegative) { + BandwidthSmoother mon(1000, // initial_bandwidth_guess + kTimeBetweenIncrease, + kPercentIncrease, + kSamplesCountToAverage, + kMinSampleCountPercent); + + EXPECT_FALSE(mon.Sample(10, -1)); + EXPECT_FALSE(mon.Sample(11, -1)); + EXPECT_EQ(1000, mon.get_bandwidth_estimation()); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/base64.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/base64.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/base64.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/base64.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,261 @@ + +//********************************************************************* +//* Base64 - a simple base64 encoder and decoder. +//* +//* Copyright (c) 1999, Bob Withers - bwit@pobox.com +//* +//* This code may be freely used for any purpose, either personal +//* or commercial, provided the authors copyright notice remains +//* intact. +//* +//* Enhancements by Stanley Yamane: +//* o reverse lookup table for the decode function +//* o reserve string buffer space in advance +//* +//********************************************************************* + +#include "webrtc/base/base64.h" + +#include + +#include "webrtc/base/common.h" + +using std::vector; + +namespace rtc { + +static const char kPad = '='; +static const unsigned char pd = 0xFD; // Padding +static const unsigned char sp = 0xFE; // Whitespace +static const unsigned char il = 0xFF; // Illegal base64 character + +const char Base64::Base64Table[] = +// 0000000000111111111122222222223333333333444444444455555555556666 +// 0123456789012345678901234567890123456789012345678901234567890123 + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +// Decode Table gives the index of any valid base64 character in the +// Base64 table +// 65 == A, 97 == a, 48 == 0, 43 == +, 47 == / + +const unsigned char Base64::DecodeTable[] = { +// 0 1 2 3 4 5 6 7 8 9 + il,il,il,il,il,il,il,il,il,sp, // 0 - 9 + sp,sp,sp,sp,il,il,il,il,il,il, // 10 - 19 + il,il,il,il,il,il,il,il,il,il, // 20 - 29 + il,il,sp,il,il,il,il,il,il,il, // 30 - 39 + il,il,il,62,il,il,il,63,52,53, // 40 - 49 + 54,55,56,57,58,59,60,61,il,il, // 50 - 59 + il,pd,il,il,il, 0, 1, 2, 3, 4, // 60 - 69 + 5, 6, 7, 8, 9,10,11,12,13,14, // 70 - 79 + 15,16,17,18,19,20,21,22,23,24, // 80 - 89 + 25,il,il,il,il,il,il,26,27,28, // 90 - 99 + 29,30,31,32,33,34,35,36,37,38, // 100 - 109 + 39,40,41,42,43,44,45,46,47,48, // 110 - 119 + 49,50,51,il,il,il,il,il,il,il, // 120 - 129 + il,il,il,il,il,il,il,il,il,il, // 130 - 139 + il,il,il,il,il,il,il,il,il,il, // 140 - 149 + il,il,il,il,il,il,il,il,il,il, // 150 - 159 + il,il,il,il,il,il,il,il,il,il, // 160 - 169 + il,il,il,il,il,il,il,il,il,il, // 170 - 179 + il,il,il,il,il,il,il,il,il,il, // 180 - 189 + il,il,il,il,il,il,il,il,il,il, // 190 - 199 + il,il,il,il,il,il,il,il,il,il, // 200 - 209 + il,il,il,il,il,il,il,il,il,il, // 210 - 219 + il,il,il,il,il,il,il,il,il,il, // 220 - 229 + il,il,il,il,il,il,il,il,il,il, // 230 - 239 + il,il,il,il,il,il,il,il,il,il, // 240 - 249 + il,il,il,il,il,il // 250 - 255 +}; + +bool Base64::IsBase64Char(char ch) { + return (('A' <= ch) && (ch <= 'Z')) || + (('a' <= ch) && (ch <= 'z')) || + (('0' <= ch) && (ch <= '9')) || + (ch == '+') || (ch == '/'); +} + +bool Base64::GetNextBase64Char(char ch, char* next_ch) { + if (next_ch == NULL) { + return false; + } + // Evil due to base/stringutils.h wanting non-standard &char for the second arg + const char* p = strchr(Base64Table, &ch); + if (!p) + return false; + ++p; + *next_ch = (*p) ? *p : Base64Table[0]; + return true; +} + +bool Base64::IsBase64Encoded(const std::string& str) { + for (size_t i = 0; i < str.size(); ++i) { + if (!IsBase64Char(str.at(i))) + return false; + } + return true; +} + +void Base64::EncodeFromArray(const void* data, size_t len, + std::string* result) { + ASSERT(NULL != result); + result->clear(); + result->resize(((len + 2) / 3) * 4); + const unsigned char* byte_data = static_cast(data); + + unsigned char c; + size_t i = 0; + size_t dest_ix = 0; + while (i < len) { + c = (byte_data[i] >> 2) & 0x3f; + (*result)[dest_ix++] = Base64Table[c]; + + c = (byte_data[i] << 4) & 0x3f; + if (++i < len) { + c |= (byte_data[i] >> 4) & 0x0f; + } + (*result)[dest_ix++] = Base64Table[c]; + + if (i < len) { + c = (byte_data[i] << 2) & 0x3f; + if (++i < len) { + c |= (byte_data[i] >> 6) & 0x03; + } + (*result)[dest_ix++] = Base64Table[c]; + } else { + (*result)[dest_ix++] = kPad; + } + + if (i < len) { + c = byte_data[i] & 0x3f; + (*result)[dest_ix++] = Base64Table[c]; + ++i; + } else { + (*result)[dest_ix++] = kPad; + } + } +} + +size_t Base64::GetNextQuantum(DecodeFlags parse_flags, bool illegal_pads, + const char* data, size_t len, size_t* dpos, + unsigned char qbuf[4], bool* padded) +{ + size_t byte_len = 0, pad_len = 0, pad_start = 0; + for (; (byte_len < 4) && (*dpos < len); ++*dpos) { + qbuf[byte_len] = DecodeTable[static_cast(data[*dpos])]; + if ((il == qbuf[byte_len]) || (illegal_pads && (pd == qbuf[byte_len]))) { + if (parse_flags != DO_PARSE_ANY) + break; + // Ignore illegal characters + } else if (sp == qbuf[byte_len]) { + if (parse_flags == DO_PARSE_STRICT) + break; + // Ignore spaces + } else if (pd == qbuf[byte_len]) { + if (byte_len < 2) { + if (parse_flags != DO_PARSE_ANY) + break; + // Ignore unexpected padding + } else if (byte_len + pad_len >= 4) { + if (parse_flags != DO_PARSE_ANY) + break; + // Ignore extra pads + } else { + if (1 == ++pad_len) { + pad_start = *dpos; + } + } + } else { + if (pad_len > 0) { + if (parse_flags != DO_PARSE_ANY) + break; + // Ignore pads which are followed by data + pad_len = 0; + } + ++byte_len; + } + } + for (size_t i = byte_len; i < 4; ++i) { + qbuf[i] = 0; + } + if (4 == byte_len + pad_len) { + *padded = true; + } else { + *padded = false; + if (pad_len) { + // Roll back illegal padding + *dpos = pad_start; + } + } + return byte_len; +} + +bool Base64::DecodeFromArray(const char* data, size_t len, DecodeFlags flags, + std::string* result, size_t* data_used) { + return DecodeFromArrayTemplate( + data, len, flags, result, data_used); +} + +bool Base64::DecodeFromArray(const char* data, size_t len, DecodeFlags flags, + vector* result, size_t* data_used) { + return DecodeFromArrayTemplate >(data, len, flags, result, + data_used); +} + +template +bool Base64::DecodeFromArrayTemplate(const char* data, size_t len, + DecodeFlags flags, T* result, + size_t* data_used) +{ + ASSERT(NULL != result); + ASSERT(flags <= (DO_PARSE_MASK | DO_PAD_MASK | DO_TERM_MASK)); + + const DecodeFlags parse_flags = flags & DO_PARSE_MASK; + const DecodeFlags pad_flags = flags & DO_PAD_MASK; + const DecodeFlags term_flags = flags & DO_TERM_MASK; + ASSERT(0 != parse_flags); + ASSERT(0 != pad_flags); + ASSERT(0 != term_flags); + + result->clear(); + result->reserve(len); + + size_t dpos = 0; + bool success = true, padded; + unsigned char c, qbuf[4]; + while (dpos < len) { + size_t qlen = GetNextQuantum(parse_flags, (DO_PAD_NO == pad_flags), + data, len, &dpos, qbuf, &padded); + c = (qbuf[0] << 2) | ((qbuf[1] >> 4) & 0x3); + if (qlen >= 2) { + result->push_back(c); + c = ((qbuf[1] << 4) & 0xf0) | ((qbuf[2] >> 2) & 0xf); + if (qlen >= 3) { + result->push_back(c); + c = ((qbuf[2] << 6) & 0xc0) | qbuf[3]; + if (qlen >= 4) { + result->push_back(c); + c = 0; + } + } + } + if (qlen < 4) { + if ((DO_TERM_ANY != term_flags) && (0 != c)) { + success = false; // unused bits + } + if ((DO_PAD_YES == pad_flags) && !padded) { + success = false; // expected padding + } + break; + } + } + if ((DO_TERM_BUFFER == term_flags) && (dpos != len)) { + success = false; // unused chars + } + if (data_used) { + *data_used = dpos; + } + return success; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/base64.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/base64.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/base64.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/base64.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,104 @@ + +//********************************************************************* +//* C_Base64 - a simple base64 encoder and decoder. +//* +//* Copyright (c) 1999, Bob Withers - bwit@pobox.com +//* +//* This code may be freely used for any purpose, either personal +//* or commercial, provided the authors copyright notice remains +//* intact. +//********************************************************************* + +#ifndef WEBRTC_BASE_BASE64_H__ +#define WEBRTC_BASE_BASE64_H__ + +#include +#include + +namespace rtc { + +class Base64 +{ +public: + enum DecodeOption { + DO_PARSE_STRICT = 1, // Parse only base64 characters + DO_PARSE_WHITE = 2, // Parse only base64 and whitespace characters + DO_PARSE_ANY = 3, // Parse all characters + DO_PARSE_MASK = 3, + + DO_PAD_YES = 4, // Padding is required + DO_PAD_ANY = 8, // Padding is optional + DO_PAD_NO = 12, // Padding is disallowed + DO_PAD_MASK = 12, + + DO_TERM_BUFFER = 16, // Must termiante at end of buffer + DO_TERM_CHAR = 32, // May terminate at any character boundary + DO_TERM_ANY = 48, // May terminate at a sub-character bit offset + DO_TERM_MASK = 48, + + // Strictest interpretation + DO_STRICT = DO_PARSE_STRICT | DO_PAD_YES | DO_TERM_BUFFER, + + DO_LAX = DO_PARSE_ANY | DO_PAD_ANY | DO_TERM_CHAR, + }; + typedef int DecodeFlags; + + static bool IsBase64Char(char ch); + + // Get the char next to the |ch| from the Base64Table. + // If the |ch| is the last one in the Base64Table then returns + // the first one from the table. + // Expects the |ch| be a base64 char. + // The result will be saved in |next_ch|. + // Returns true on success. + static bool GetNextBase64Char(char ch, char* next_ch); + + // Determines whether the given string consists entirely of valid base64 + // encoded characters. + static bool IsBase64Encoded(const std::string& str); + + static void EncodeFromArray(const void* data, size_t len, + std::string* result); + static bool DecodeFromArray(const char* data, size_t len, DecodeFlags flags, + std::string* result, size_t* data_used); + static bool DecodeFromArray(const char* data, size_t len, DecodeFlags flags, + std::vector* result, size_t* data_used); + + // Convenience Methods + static inline std::string Encode(const std::string& data) { + std::string result; + EncodeFromArray(data.data(), data.size(), &result); + return result; + } + static inline std::string Decode(const std::string& data, DecodeFlags flags) { + std::string result; + DecodeFromArray(data.data(), data.size(), flags, &result, NULL); + return result; + } + static inline bool Decode(const std::string& data, DecodeFlags flags, + std::string* result, size_t* data_used) + { + return DecodeFromArray(data.data(), data.size(), flags, result, data_used); + } + static inline bool Decode(const std::string& data, DecodeFlags flags, + std::vector* result, size_t* data_used) + { + return DecodeFromArray(data.data(), data.size(), flags, result, data_used); + } + +private: + static const char Base64Table[]; + static const unsigned char DecodeTable[]; + + static size_t GetNextQuantum(DecodeFlags parse_flags, bool illegal_pads, + const char* data, size_t len, size_t* dpos, + unsigned char qbuf[4], bool* padded); + template + static bool DecodeFromArrayTemplate(const char* data, size_t len, + DecodeFlags flags, T* result, + size_t* data_used); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_BASE64_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/base64_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/base64_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/base64_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/base64_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,1001 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/common.h" +#include "webrtc/base/base64.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/stringutils.h" +#include "webrtc/base/stream.h" + +#include "webrtc/base/testbase64.h" + +using namespace std; +using namespace rtc; + +static struct { + size_t plain_length; + const char* plaintext; + const char* cyphertext; +} base64_tests[] = { + + // Basic bit patterns; + // values obtained with "echo -n '...' | uuencode -m test" + + { 1, "\000", "AA==" }, + { 1, "\001", "AQ==" }, + { 1, "\002", "Ag==" }, + { 1, "\004", "BA==" }, + { 1, "\010", "CA==" }, + { 1, "\020", "EA==" }, + { 1, "\040", "IA==" }, + { 1, "\100", "QA==" }, + { 1, "\200", "gA==" }, + + { 1, "\377", "/w==" }, + { 1, "\376", "/g==" }, + { 1, "\375", "/Q==" }, + { 1, "\373", "+w==" }, + { 1, "\367", "9w==" }, + { 1, "\357", "7w==" }, + { 1, "\337", "3w==" }, + { 1, "\277", "vw==" }, + { 1, "\177", "fw==" }, + { 2, "\000\000", "AAA=" }, + { 2, "\000\001", "AAE=" }, + { 2, "\000\002", "AAI=" }, + { 2, "\000\004", "AAQ=" }, + { 2, "\000\010", "AAg=" }, + { 2, "\000\020", "ABA=" }, + { 2, "\000\040", "ACA=" }, + { 2, "\000\100", "AEA=" }, + { 2, "\000\200", "AIA=" }, + { 2, "\001\000", "AQA=" }, + { 2, "\002\000", "AgA=" }, + { 2, "\004\000", "BAA=" }, + { 2, "\010\000", "CAA=" }, + { 2, "\020\000", "EAA=" }, + { 2, "\040\000", "IAA=" }, + { 2, "\100\000", "QAA=" }, + { 2, "\200\000", "gAA=" }, + + { 2, "\377\377", "//8=" }, + { 2, "\377\376", "//4=" }, + { 2, "\377\375", "//0=" }, + { 2, "\377\373", "//s=" }, + { 2, "\377\367", "//c=" }, + { 2, "\377\357", "/+8=" }, + { 2, "\377\337", "/98=" }, + { 2, "\377\277", "/78=" }, + { 2, "\377\177", "/38=" }, + { 2, "\376\377", "/v8=" }, + { 2, "\375\377", "/f8=" }, + { 2, "\373\377", "+/8=" }, + { 2, "\367\377", "9/8=" }, + { 2, "\357\377", "7/8=" }, + { 2, "\337\377", "3/8=" }, + { 2, "\277\377", "v/8=" }, + { 2, "\177\377", "f/8=" }, + + { 3, "\000\000\000", "AAAA" }, + { 3, "\000\000\001", "AAAB" }, + { 3, "\000\000\002", "AAAC" }, + { 3, "\000\000\004", "AAAE" }, + { 3, "\000\000\010", "AAAI" }, + { 3, "\000\000\020", "AAAQ" }, + { 3, "\000\000\040", "AAAg" }, + { 3, "\000\000\100", "AABA" }, + { 3, "\000\000\200", "AACA" }, + { 3, "\000\001\000", "AAEA" }, + { 3, "\000\002\000", "AAIA" }, + { 3, "\000\004\000", "AAQA" }, + { 3, "\000\010\000", "AAgA" }, + { 3, "\000\020\000", "ABAA" }, + { 3, "\000\040\000", "ACAA" }, + { 3, "\000\100\000", "AEAA" }, + { 3, "\000\200\000", "AIAA" }, + { 3, "\001\000\000", "AQAA" }, + { 3, "\002\000\000", "AgAA" }, + { 3, "\004\000\000", "BAAA" }, + { 3, "\010\000\000", "CAAA" }, + { 3, "\020\000\000", "EAAA" }, + { 3, "\040\000\000", "IAAA" }, + { 3, "\100\000\000", "QAAA" }, + { 3, "\200\000\000", "gAAA" }, + + { 3, "\377\377\377", "////" }, + { 3, "\377\377\376", "///+" }, + { 3, "\377\377\375", "///9" }, + { 3, "\377\377\373", "///7" }, + { 3, "\377\377\367", "///3" }, + { 3, "\377\377\357", "///v" }, + { 3, "\377\377\337", "///f" }, + { 3, "\377\377\277", "//+/" }, + { 3, "\377\377\177", "//9/" }, + { 3, "\377\376\377", "//7/" }, + { 3, "\377\375\377", "//3/" }, + { 3, "\377\373\377", "//v/" }, + { 3, "\377\367\377", "//f/" }, + { 3, "\377\357\377", "/+//" }, + { 3, "\377\337\377", "/9//" }, + { 3, "\377\277\377", "/7//" }, + { 3, "\377\177\377", "/3//" }, + { 3, "\376\377\377", "/v//" }, + { 3, "\375\377\377", "/f//" }, + { 3, "\373\377\377", "+///" }, + { 3, "\367\377\377", "9///" }, + { 3, "\357\377\377", "7///" }, + { 3, "\337\377\377", "3///" }, + { 3, "\277\377\377", "v///" }, + { 3, "\177\377\377", "f///" }, + + // Random numbers: values obtained with + // + // #! /bin/bash + // dd bs=$1 count=1 if=/dev/random of=/tmp/bar.random + // od -N $1 -t o1 /tmp/bar.random + // uuencode -m test < /tmp/bar.random + // + // where $1 is the number of bytes (2, 3) + + { 2, "\243\361", "o/E=" }, + { 2, "\024\167", "FHc=" }, + { 2, "\313\252", "y6o=" }, + { 2, "\046\041", "JiE=" }, + { 2, "\145\236", "ZZ4=" }, + { 2, "\254\325", "rNU=" }, + { 2, "\061\330", "Mdg=" }, + { 2, "\245\032", "pRo=" }, + { 2, "\006\000", "BgA=" }, + { 2, "\375\131", "/Vk=" }, + { 2, "\303\210", "w4g=" }, + { 2, "\040\037", "IB8=" }, + { 2, "\261\372", "sfo=" }, + { 2, "\335\014", "3Qw=" }, + { 2, "\233\217", "m48=" }, + { 2, "\373\056", "+y4=" }, + { 2, "\247\232", "p5o=" }, + { 2, "\107\053", "Rys=" }, + { 2, "\204\077", "hD8=" }, + { 2, "\276\211", "vok=" }, + { 2, "\313\110", "y0g=" }, + { 2, "\363\376", "8/4=" }, + { 2, "\251\234", "qZw=" }, + { 2, "\103\262", "Q7I=" }, + { 2, "\142\312", "Yso=" }, + { 2, "\067\211", "N4k=" }, + { 2, "\220\001", "kAE=" }, + { 2, "\152\240", "aqA=" }, + { 2, "\367\061", "9zE=" }, + { 2, "\133\255", "W60=" }, + { 2, "\176\035", "fh0=" }, + { 2, "\032\231", "Gpk=" }, + + { 3, "\013\007\144", "Cwdk" }, + { 3, "\030\112\106", "GEpG" }, + { 3, "\047\325\046", "J9Um" }, + { 3, "\310\160\022", "yHAS" }, + { 3, "\131\100\237", "WUCf" }, + { 3, "\064\342\134", "NOJc" }, + { 3, "\010\177\004", "CH8E" }, + { 3, "\345\147\205", "5WeF" }, + { 3, "\300\343\360", "wOPw" }, + { 3, "\061\240\201", "MaCB" }, + { 3, "\225\333\044", "ldsk" }, + { 3, "\215\137\352", "jV/q" }, + { 3, "\371\147\160", "+Wdw" }, + { 3, "\030\320\051", "GNAp" }, + { 3, "\044\174\241", "JHyh" }, + { 3, "\260\127\037", "sFcf" }, + { 3, "\111\045\033", "SSUb" }, + { 3, "\202\114\107", "gkxH" }, + { 3, "\057\371\042", "L/ki" }, + { 3, "\223\247\244", "k6ek" }, + { 3, "\047\216\144", "J45k" }, + { 3, "\203\070\327", "gzjX" }, + { 3, "\247\140\072", "p2A6" }, + { 3, "\124\115\116", "VE1O" }, + { 3, "\157\162\050", "b3Io" }, + { 3, "\357\223\004", "75ME" }, + { 3, "\052\117\156", "Kk9u" }, + { 3, "\347\154\000", "52wA" }, + { 3, "\303\012\142", "wwpi" }, + { 3, "\060\035\362", "MB3y" }, + { 3, "\130\226\361", "WJbx" }, + { 3, "\173\013\071", "ews5" }, + { 3, "\336\004\027", "3gQX" }, + { 3, "\357\366\234", "7/ac" }, + { 3, "\353\304\111", "68RJ" }, + { 3, "\024\264\131", "FLRZ" }, + { 3, "\075\114\251", "PUyp" }, + { 3, "\315\031\225", "zRmV" }, + { 3, "\154\201\276", "bIG+" }, + { 3, "\200\066\072", "gDY6" }, + { 3, "\142\350\267", "Yui3" }, + { 3, "\033\000\166", "GwB2" }, + { 3, "\210\055\077", "iC0/" }, + { 3, "\341\037\124", "4R9U" }, + { 3, "\161\103\152", "cUNq" }, + { 3, "\270\142\131", "uGJZ" }, + { 3, "\337\076\074", "3z48" }, + { 3, "\375\106\362", "/Uby" }, + { 3, "\227\301\127", "l8FX" }, + { 3, "\340\002\234", "4AKc" }, + { 3, "\121\064\033", "UTQb" }, + { 3, "\157\134\143", "b1xj" }, + { 3, "\247\055\327", "py3X" }, + { 3, "\340\142\005", "4GIF" }, + { 3, "\060\260\143", "MLBj" }, + { 3, "\075\203\170", "PYN4" }, + { 3, "\143\160\016", "Y3AO" }, + { 3, "\313\013\063", "ywsz" }, + { 3, "\174\236\135", "fJ5d" }, + { 3, "\103\047\026", "QycW" }, + { 3, "\365\005\343", "9QXj" }, + { 3, "\271\160\223", "uXCT" }, + { 3, "\362\255\172", "8q16" }, + { 3, "\113\012\015", "SwoN" }, + + // various lengths, generated by this python script: + // + // from string import lowercase as lc + // for i in range(27): + // print '{ %2d, "%s",%s "%s" },' % (i, lc[:i], ' ' * (26-i), + // lc[:i].encode('base64').strip()) + + { 0, "abcdefghijklmnopqrstuvwxyz", "" }, + { 1, "abcdefghijklmnopqrstuvwxyz", "YQ==" }, + { 2, "abcdefghijklmnopqrstuvwxyz", "YWI=" }, + { 3, "abcdefghijklmnopqrstuvwxyz", "YWJj" }, + { 4, "abcdefghijklmnopqrstuvwxyz", "YWJjZA==" }, + { 5, "abcdefghijklmnopqrstuvwxyz", "YWJjZGU=" }, + { 6, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVm" }, + { 7, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZw==" }, + { 8, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2g=" }, + { 9, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hp" }, + { 10, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpag==" }, + { 11, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpams=" }, + { 12, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamts" }, + { 13, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbQ==" }, + { 14, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW4=" }, + { 15, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5v" }, + { 16, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcA==" }, + { 17, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHE=" }, + { 18, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFy" }, + { 19, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFycw==" }, + { 20, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3Q=" }, + { 21, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1" }, + { 22, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dg==" }, + { 23, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnc=" }, + { 24, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4" }, + { 25, "abcdefghijklmnopqrstuvwxy", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eQ==" }, + { 26, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=" }, +}; +#if 0 +static struct { + const char* plaintext; + const char* cyphertext; +} base64_strings[] = { + + // The first few Google quotes + // Cyphertext created with "uuencode - GNU sharutils 4.2.1" + { + "Everyone! We're teetering on the brink of disaster." + " - Sergey Brin, 6/24/99, regarding the company's state " + "after the unleashing of Netscape/Google search", + + "RXZlcnlvbmUhICBXZSdyZSB0ZWV0ZXJpbmcgb24gdGhlIGJyaW5rIG9mIGRp" + "c2FzdGVyLiAtIFNlcmdleSBCcmluLCA2LzI0Lzk5LCByZWdhcmRpbmcgdGhl" + "IGNvbXBhbnkncyBzdGF0ZSBhZnRlciB0aGUgdW5sZWFzaGluZyBvZiBOZXRz" + "Y2FwZS9Hb29nbGUgc2VhcmNo" }, + + { + "I'm not sure why we're still alive, but we seem to be." + " - Larry Page, 6/24/99, while hiding in the kitchenette " + "during the Netscape traffic overflow", + + "SSdtIG5vdCBzdXJlIHdoeSB3ZSdyZSBzdGlsbCBhbGl2ZSwgYnV0IHdlIHNl" + "ZW0gdG8gYmUuIC0gTGFycnkgUGFnZSwgNi8yNC85OSwgd2hpbGUgaGlkaW5n" + "IGluIHRoZSBraXRjaGVuZXR0ZSBkdXJpbmcgdGhlIE5ldHNjYXBlIHRyYWZm" + "aWMgb3ZlcmZsb3c" }, + + { + "I think kids want porn." + " - Sergey Brin, 6/99, on why Google shouldn't prioritize a " + "filtered search for children and families", + + "SSB0aGluayBraWRzIHdhbnQgcG9ybi4gLSBTZXJnZXkgQnJpbiwgNi85OSwg" + "b24gd2h5IEdvb2dsZSBzaG91bGRuJ3QgcHJpb3JpdGl6ZSBhIGZpbHRlcmVk" + "IHNlYXJjaCBmb3IgY2hpbGRyZW4gYW5kIGZhbWlsaWVz" }, +}; +#endif +// Compare bytes 0..len-1 of x and y. If not equal, abort with verbose error +// message showing position and numeric value that differed. +// Handles embedded nulls just like any other byte. +// Only added because string.compare() in gcc-3.3.3 seems to misbehave with +// embedded nulls. +// TODO: switch back to string.compare() if/when gcc is fixed +#define EXPECT_EQ_ARRAY(len, x, y, msg) \ + for (size_t j = 0; j < len; ++j) { \ + if (x[j] != y[j]) { \ + LOG(LS_ERROR) << "" # x << " != " # y \ + << " byte " << j << " msg: " << msg; \ + } \ + } + +size_t Base64Escape(const unsigned char *src, size_t szsrc, char *dest, + size_t szdest) { + std::string escaped; + Base64::EncodeFromArray((const char *)src, szsrc, &escaped); + memcpy(dest, escaped.data(), min(escaped.size(), szdest)); + return escaped.size(); +} + +size_t Base64Unescape(const char *src, size_t szsrc, char *dest, + size_t szdest) { + std::string unescaped; + EXPECT_TRUE(Base64::DecodeFromArray(src, szsrc, Base64::DO_LAX, &unescaped, + NULL)); + memcpy(dest, unescaped.data(), min(unescaped.size(), szdest)); + return unescaped.size(); +} + +size_t Base64Unescape(const char *src, size_t szsrc, string *s) { + EXPECT_TRUE(Base64::DecodeFromArray(src, szsrc, Base64::DO_LAX, s, NULL)); + return s->size(); +} + +TEST(Base64, EncodeDecodeBattery) { + LOG(LS_VERBOSE) << "Testing base-64"; + + size_t i; + + // Check the short strings; this tests the math (and boundaries) + for( i = 0; i < sizeof(base64_tests) / sizeof(base64_tests[0]); ++i ) { + char encode_buffer[100]; + size_t encode_length; + char decode_buffer[100]; + size_t decode_length; + size_t cypher_length; + + LOG(LS_VERBOSE) << "B64: " << base64_tests[i].cyphertext; + + const unsigned char* unsigned_plaintext = + reinterpret_cast(base64_tests[i].plaintext); + + cypher_length = strlen(base64_tests[i].cyphertext); + + // The basic escape function: + memset(encode_buffer, 0, sizeof(encode_buffer)); + encode_length = Base64Escape(unsigned_plaintext, + base64_tests[i].plain_length, + encode_buffer, + sizeof(encode_buffer)); + // Is it of the expected length? + EXPECT_EQ(encode_length, cypher_length); + + // Is it the expected encoded value? + EXPECT_STREQ(encode_buffer, base64_tests[i].cyphertext); + + // If we encode it into a buffer of exactly the right length... + memset(encode_buffer, 0, sizeof(encode_buffer)); + encode_length = Base64Escape(unsigned_plaintext, + base64_tests[i].plain_length, + encode_buffer, + cypher_length); + // Is it still of the expected length? + EXPECT_EQ(encode_length, cypher_length); + + // And is the value still correct? (i.e., not losing the last byte) + EXPECT_STREQ(encode_buffer, base64_tests[i].cyphertext); + + // If we decode it back: + memset(decode_buffer, 0, sizeof(decode_buffer)); + decode_length = Base64Unescape(encode_buffer, + cypher_length, + decode_buffer, + sizeof(decode_buffer)); + + // Is it of the expected length? + EXPECT_EQ(decode_length, base64_tests[i].plain_length); + + // Is it the expected decoded value? + EXPECT_EQ(0, memcmp(decode_buffer, base64_tests[i].plaintext, decode_length)); + + // Our decoder treats the padding '=' characters at the end as + // optional. If encode_buffer has any, run some additional + // tests that fiddle with them. + char* first_equals = strchr(encode_buffer, '='); + if (first_equals) { + // How many equals signs does the string start with? + int equals = (*(first_equals+1) == '=') ? 2 : 1; + + // Try chopping off the equals sign(s) entirely. The decoder + // should still be okay with this. + string decoded2("this junk should also be ignored"); + *first_equals = '\0'; + EXPECT_NE(0U, Base64Unescape(encode_buffer, first_equals-encode_buffer, + &decoded2)); + EXPECT_EQ(decoded2.size(), base64_tests[i].plain_length); + EXPECT_EQ_ARRAY(decoded2.size(), decoded2.data(), base64_tests[i].plaintext, i); + + size_t len; + + // try putting some extra stuff after the equals signs, or in between them + if (equals == 2) { + sprintfn(first_equals, 6, " = = "); + len = first_equals - encode_buffer + 5; + } else { + sprintfn(first_equals, 6, " = "); + len = first_equals - encode_buffer + 3; + } + decoded2.assign("this junk should be ignored"); + EXPECT_NE(0U, Base64Unescape(encode_buffer, len, &decoded2)); + EXPECT_EQ(decoded2.size(), base64_tests[i].plain_length); + EXPECT_EQ_ARRAY(decoded2.size(), decoded2, base64_tests[i].plaintext, i); + } + } +} + +// here's a weird case: a giant base64 encoded stream which broke our base64 +// decoding. Let's test it explicitly. +const char SpecificTest[] = + "/9j/4AAQSkZJRgABAgEASABIAAD/4Q0HRXhpZgAATU0AKgAAAAgADAEOAAIAAAAgAAAAngEPAAI\n" + "AAAAFAAAAvgEQAAIAAAAJAAAAwwESAAMAAAABAAEAAAEaAAUAAAABAAAAzAEbAAUAAAABAAAA1A\n" + "EoAAMAAAABAAIAAAExAAIAAAAUAAAA3AEyAAIAAAAUAAAA8AE8AAIAAAAQAAABBAITAAMAAAABA\n" + "AIAAIdpAAQAAAABAAABFAAAAsQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgAFNPTlkA\n" + "RFNDLVAyMDAAAAAASAAAAAEAAABIAAAAAUFkb2JlIFBob3Rvc2hvcCA3LjAAMjAwNzowMTozMCA\n" + "yMzoxMDowNABNYWMgT1MgWCAxMC40LjgAAByCmgAFAAAAAQAAAmqCnQAFAAAAAQAAAnKIIgADAA\n" + "AAAQACAACIJwADAAAAAQBkAACQAAAHAAAABDAyMjCQAwACAAAAFAAAAnqQBAACAAAAFAAAAo6RA\n" + "QAHAAAABAECAwCRAgAFAAAAAQAAAqKSBAAKAAAAAQAAAqqSBQAFAAAAAQAAArKSBwADAAAAAQAF\n" + "AACSCAADAAAAAQAAAACSCQADAAAAAQAPAACSCgAFAAAAAQAAArqgAAAHAAAABDAxMDCgAQADAAA\n" + "AAf//AACgAgAEAAAAAQAAAGSgAwAEAAAAAQAAAGSjAAAHAAAAAQMAAACjAQAHAAAAAQEAAACkAQ\n" + "ADAAAAAQAAAACkAgADAAAAAQAAAACkAwADAAAAAQAAAACkBgADAAAAAQAAAACkCAADAAAAAQAAA\n" + "ACkCQADAAAAAQAAAACkCgADAAAAAQAAAAAAAAAAAAAACgAAAZAAAAAcAAAACjIwMDc6MDE6MjAg\n" + "MjM6MDU6NTIAMjAwNzowMToyMCAyMzowNTo1MgAAAAAIAAAAAQAAAAAAAAAKAAAAMAAAABAAAAB\n" + "PAAAACgAAAAYBAwADAAAAAQAGAAABGgAFAAAAAQAAAxIBGwAFAAAAAQAAAxoBKAADAAAAAQACAA\n" + "ACAQAEAAAAAQAAAyICAgAEAAAAAQAACd0AAAAAAAAASAAAAAEAAABIAAAAAf/Y/+AAEEpGSUYAA\n" + "QIBAEgASAAA/+0ADEFkb2JlX0NNAAL/7gAOQWRvYmUAZIAAAAAB/9sAhAAMCAgICQgMCQkMEQsK\n" + "CxEVDwwMDxUYExMVExMYEQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAQ0LCw0\n" + "ODRAODhAUDg4OFBQODg4OFBEMDAwMDBERDAwMDAwMEQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA\n" + "wMDAz/wAARCABkAGQDASIAAhEBAxEB/90ABAAH/8QBPwAAAQUBAQEBAQEAAAAAAAAAAwABAgQFB\n" + "gcICQoLAQABBQEBAQEBAQAAAAAAAAABAAIDBAUGBwgJCgsQAAEEAQMCBAIFBwYIBQMMMwEAAhED\n" + "BCESMQVBUWETInGBMgYUkaGxQiMkFVLBYjM0coLRQwclklPw4fFjczUWorKDJkSTVGRFwqN0Nhf\n" + "SVeJl8rOEw9N14/NGJ5SkhbSVxNTk9KW1xdXl9VZmdoaWprbG1ub2N0dXZ3eHl6e3x9fn9xEAAg\n" + "IBAgQEAwQFBgcHBgU1AQACEQMhMRIEQVFhcSITBTKBkRShsUIjwVLR8DMkYuFygpJDUxVjczTxJ\n" + "QYWorKDByY1wtJEk1SjF2RFVTZ0ZeLys4TD03Xj80aUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm\n" + "9ic3R1dnd4eXp7fH/9oADAMBAAIRAxEAPwDy7bKNTUXNLz9EaJPDWMjxH4ozhtpYwaACT8ShaaW\n" + "bW0uEc9/JFfjj0Q4Hk/PRDxwX7y47W9z/AN9Cv4+O3ILK2DcRqT2CaSvEbcl1Jbz37KG1dBldLo\n" + "qaS4l9xGjG9v6yoDAdYIaIjUk+AREgo4y5sapirb8Yl0NHHdKvBNm4yA1o5Pc+SPEFvCWqB3HZF\n" + "Hj2SbWQ/afGFP0bHP8ATY0uc4w1o1JPkkimGiS2KvqlnmBkOZQTyydzgPMM9v8A0lp4v1Nx9gF1\n" + "tpdqJaGtH/S3I0i3lISXW/8AMqnd/O2bfg2eUkqVYf/Q8zuncO4Bj7lZ+n7f5Mj5KsJcY8NUZ4d\n" + "uEDVo1HkeU0rg3Om4H2rabCWUN7DQuK1n5FWKW4uCwG92gDRJBS6exhxmMboQI+Cv4WFTQ42Bs2\n" + "fvnkkqEmy2YxoMMbpVzaz6jt+RbpHZs8lzkHqrasKkYOKP0jgDfZ4N/wDM1tNrcWfSPmRyq9uNV\n" + "DnFg2s97i7UkjxKVrq0eVz3spZsja+ASDzwsh9jnOk/JFzb3XZD3v1c4yT8UACTCniKDUnKz5Nj\n" + "G33XV1DV73BrT8dF23SejV4zg9g33cOsPb+SxVvqv9ViwNy8vS0iWs/daf8A0Y5dpTi1sADGxCR\n" + "K1o0YBEmInlXWYbDBcDLdPJXa8f71Yrx2jnUoAqLnfZK5hJaW2vdwEk5a/wD/0fN6Ia/e76IiVf\n" + "xavUL7CPpnT4LNbYXAVjuQt/AqDmNYO/Kjnoy4hr5J8SwMhrRMaeSvbsxrfUazcOw4UX0Cisem2\n" + "SBoD4+Kz8nC6llbSLCRrubJA8kwUWbUDa29X1PMa7aQWjuDC0MXMdbDbhI7eazBiUfZ6GOYRe1s\n" + "WvGgJ8Vbw2+m4Bx9s6JpNHuuGo1FF53r/SHYua61gLse0lzXeBP5rkvqx0o5vVWz7WY49QkiQSP\n" + "oN/tLoevW/ogxv0HA7tJ0AnhT+pdDGYVl/wCdcTPkGn2NU0JWNWvlgAbHV6fEqdu2gR/r2WlWwt\n" + "AA5VXAEsLXTqJafArQY5rRr9LiPBJiZsZCI1pJjxCi0j4oncSICSkWwzwkjeaSch//0vO7sP7Lm\n" + "enO9ogtd5FbPT3Q5pCpZVc4ld3Lmn3O8j9EI2BYdunKjOobMQIyI+rusc2wx4d0eutwGnHh/uQc\n" + "Ha7ladj6mVANGvcqOgz0Go7HJ12/GEHcwvB/dPY6ImbbaMaASGuIBjkN7qofs9Ubg9g7OI9p/t/\n" + "RTSmhTHr0v6eSz6UgCPP2/wAVu9Ex2V49dVY2iACB4BZeVXQ/AJ3gzGnnOi2+kACpru8flUsNmt\n" + "zHRf6xfWCnoeAfTh2ZaQKazx/Ke7+QxcKz61fWA2uuObaC4zGhaPJrXBL64ZFmR124O09ENraPK\n" + "N3/AH5GqxIrZVUyp2K2vfdkENsDnxuex9m4Ox9n82xSgNd9D+p/XR1npgseR9ppOy4Dx/NfH/CL\n" + "oQJGunmvMv8AFq3KHVcq3HkYQbD2nuSf0I/rMavSg6TLjLigQhJ7Z58v9QkmlsTOqSCn/9PzL7R\n" + "d6Qq3n0wZ2zotXpT9xLfFYvkr/S7jXeB8E0jRkhKpC3q8LcJ/kmCrTnkuAPCq4do9Q/ytVbuAeY\n" + "Gg5lQybQK+82GBqEQUA1kOHPYf3LLsoyN36G5w8iUfHxepbXE2l0cApALgLHzBq9UxhTXU5hMC1\n" + "ktnSCup6S4Ctk+C5XqVGcaHPfuiuHkeTTuWz0+9zaKiH6CC0/yXBSQ2a/MxojV57634rq+v2PLY\n" + "be1r2nsYG13/AFKxbfCBMcr0brGAzrGEwCG31ncx0SfBzf7S4+zoHUWWsJq3hz9oLfcBH77R9H+\n" + "0pA13u/qPgDp/Q6ri39JlfpXkDx+h/msWn1L6wdO6bSbcrIbU2Q0xLnSe21kuVejJspbVS5+4bd\n" + "ocBAkD/orG+tP1ar67Wy7GtZTm1SCXfRsb+a18fRe38x6SG3/44H1Z3f0y2I+l6DoSXD/8xPrDs\n" + "3enVu3bdnqN3R+//USSVo//1PLohhce+gRWS0Nsby3lRgFkKxQyW7SgUh3em5Tbq2uB9wWw1wey\n" + "J1XGV2XYdm5k7e4WzidXY9oMwo5RZ4T6Hd1ixwfp96PWbAJBVTHzK7O6Ky5oJB1HZMqmUEFlkGy\n" + "xpa4zI1Hkq31dy7bMN9BAc3HeWAnnbyxEycmuup1jiAGglZ31PyrmZ9tQg1WtNj54EHR3/S2qTH\n" + "1Yc5GgD1FFtzPdWGkd2AyflogZmRmsz6PSrbXbdo+txOrP337f3fzVo15DK2uyrTtqpBOnBKx6b\n" + "7MjJsz7tHWOAYP3WD6LU6cqGjFCNl1MmvLcxv6YtDTLSAqP27LrdtYHXFnJZI+Tp3MWg68OpDPv\n" + "UMUM2lkQBoouKQ6swjE9Nml+1sz1PW+z6xt27zuj+skrX2ZvqR5z8kkuOfdPt43/1fMm/grFG6f\n" + "Lss9JA7JG7tnZs/SfJUrfS3foJ9TvHCopJsV8nWx/t24bJn8Fo/5TjWJXMJIS+i+G36TsZ/7Q9P\n" + "8ATfzfeOFofVSZv2/zvt+O3X/v65dJPjt/BiyfN1/wn0zre79nVej/ADG8ep4x2/6Srjd6TdviF\n" + "52ko8m6/Ht9X1KnftEo+POwxzK8mSTF46vrH6T1/OEl5Okkl//Z/+0uHFBob3Rvc2hvcCAzLjAA\n" + "OEJJTQQEAAAAAAArHAIAAAIAAhwCeAAfICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAA\n" + "4QklNBCUAAAAAABD7Caa9B0wqNp2P4sxXqayFOEJJTQPqAAAAAB2wPD94bWwgdmVyc2lvbj0iMS\n" + "4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUgQ\n" + "29tcHV0ZXIvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Q\n" + "cm9wZXJ0eUxpc3QtMS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk\n" + "+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1Ib3Jpem9udGFsUmVzPC9rZXk+Cgk8ZGljdD\n" + "4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc+Y\n" + "29tLmFwcGxlLnByaW50aW5nbWFuYWdlcjwvc3RyaW5nPgoJCTxrZXk+Y29tLmFwcGxlLnByaW50\n" + "LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk+CgkJCTxkaWN0PgoJCQkJPGtleT5jb20\n" + "uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTUhvcml6b250YWxSZXM8L2tleT4KCQkJCTxyZWFsPj\n" + "cyPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNsaWVudDwva2V5PgoJC\n" + "QkJPHN0cmluZz5jb20uYXBwbGUucHJpbnRpbmdtYW5hZ2VyPC9zdHJpbmc+CgkJCQk8a2V5PmNv\n" + "bS5hcHBsZS5wcmludC50aWNrZXQubW9kRGF0ZTwva2V5PgoJCQkJPGRhdGU+MjAwNy0wMS0zMFQ\n" + "yMjowODo0MVo8L2RhdGU+CgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVGbG\n" + "FnPC9rZXk+CgkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQk8L2RpY3Q+CgkJPC9hcnJheT4KC\n" + "TwvZGljdD4KCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1PcmllbnRhdGlvbjwv\n" + "a2V5PgoJPGRpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4\n" + "KCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbmFnZXI8L3N0cmluZz4KCQk8a2V5PmNvbS\n" + "5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJPGFycmF5PgoJCQk8ZGljdD4KC\n" + "QkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1PcmllbnRhdGlvbjwva2V5PgoJ\n" + "CQkJPGludGVnZXI+MTwvaW50ZWdlcj4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5\n" + "jbGllbnQ8L2tleT4KCQkJCTxzdHJpbmc+Y29tLmFwcGxlLnByaW50aW5nbWFuYWdlcjwvc3RyaW\n" + "5nPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lm1vZERhdGU8L2tleT4KCQkJCTxkY\n" + "XRlPjIwMDctMDEtMzBUMjI6MDg6NDFaPC9kYXRlPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQu\n" + "dGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI+MDwvaW50ZWdlcj4KCQkJPC9kaWN\n" + "0PgoJCTwvYXJyYXk+Cgk8L2RpY3Q+Cgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0Ll\n" + "BNU2NhbGluZzwva2V5PgoJPGRpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZ\n" + "WF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbmFnZXI8L3N0cmluZz4K\n" + "CQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJPGFycmF5Pgo\n" + "JCQk8ZGljdD4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1TY2FsaW5nPC\n" + "9rZXk+CgkJCQk8cmVhbD4xPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0L\n" + "mNsaWVudDwva2V5PgoJCQkJPHN0cmluZz5jb20uYXBwbGUucHJpbnRpbmdtYW5hZ2VyPC9zdHJp\n" + "bmc+CgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQubW9kRGF0ZTwva2V5PgoJCQkJPGR\n" + "hdGU+MjAwNy0wMS0zMFQyMjowODo0MVo8L2RhdGU+CgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC\n" + "50aWNrZXQuc3RhdGVGbGFnPC9rZXk+CgkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQk8L2RpY\n" + "3Q+CgkJPC9hcnJheT4KCTwvZGljdD4KCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQu\n" + "UE1WZXJ0aWNhbFJlczwva2V5PgoJPGRpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V\n" + "0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbmFnZXI8L3N0cm\n" + "luZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJPGFyc\n" + "mF5PgoJCQk8ZGljdD4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1WZXJ0\n" + "aWNhbFJlczwva2V5PgoJCQkJPHJlYWw+NzI8L3JlYWw+CgkJCQk8a2V5PmNvbS5hcHBsZS5wcml\n" + "udC50aWNrZXQuY2xpZW50PC9rZXk+CgkJCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbm\n" + "FnZXI8L3N0cmluZz4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5tb2REYXRlPC9rZ\n" + "Xk+CgkJCQk8ZGF0ZT4yMDA3LTAxLTMwVDIyOjA4OjQxWjwvZGF0ZT4KCQkJCTxrZXk+Y29tLmFw\n" + "cGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI\n" + "+CgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuUG\n" + "FnZUZvcm1hdC5QTVZlcnRpY2FsU2NhbGluZzwva2V5PgoJPGRpY3Q+CgkJPGtleT5jb20uYXBwb\n" + "GUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGlu\n" + "Z21hbmFnZXI8L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF\n" + "5PC9rZXk+CgkJPGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2\n" + "VGb3JtYXQuUE1WZXJ0aWNhbFNjYWxpbmc8L2tleT4KCQkJCTxyZWFsPjE8L3JlYWw+CgkJCQk8a\n" + "2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY2xpZW50PC9rZXk+CgkJCQk8c3RyaW5nPmNvbS5h\n" + "cHBsZS5wcmludGluZ21hbmFnZXI8L3N0cmluZz4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnR\n" + "pY2tldC5tb2REYXRlPC9rZXk+CgkJCQk8ZGF0ZT4yMDA3LTAxLTMwVDIyOjA4OjQxWjwvZGF0ZT\n" + "4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpb\n" + "nRlZ2VyPjA8L2ludGVnZXI+CgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5j\n" + "b20uYXBwbGUucHJpbnQuc3ViVGlja2V0LnBhcGVyX2luZm9fdGlja2V0PC9rZXk+Cgk8ZGljdD4\n" + "KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUmVjdDwva2\n" + "V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5P\n" + "goJCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbmFnZXI8L3N0cmluZz4KCQkJPGtleT5j\n" + "b20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk+CgkJCQk8ZGl\n" + "jdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUm\n" + "VjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw+MC4wPC9yZWFsPgoJCQkJCQk8cmVhb\n" + "D4wLjA8L3JlYWw+CgkJCQkJCTxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw+NTc2PC9yZWFs\n" + "PgoJCQkJCTwvYXJyYXk+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNsaWVudDw\n" + "va2V5PgoJCQkJCTxzdHJpbmc+Y29tLmFwcGxlLnByaW50aW5nbWFuYWdlcjwvc3RyaW5nPgoJCQ\n" + "kJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5tb2REYXRlPC9rZXk+CgkJCQkJPGRhdGU+M\n" + "jAwNy0wMS0zMFQyMjowODo0MVo8L2RhdGU+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlj\n" + "a2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI+CgkJCQk8L2RpY3Q\n" + "+CgkJCTwvYXJyYXk+CgkJPC9kaWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYX\n" + "QuUE1BZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wc\n" + "mludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21h\n" + "bmFnZXI8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTw\n" + "va2V5PgoJCQk8YXJyYXk+CgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYW\n" + "dlRm9ybWF0LlBNQWRqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQkJCQk8YXJyYXk+CgkJCQkJCTxyZ\n" + "WFsPi0xODwvcmVhbD4KCQkJCQkJPHJlYWw+LTE4PC9yZWFsPgoJCQkJCQk8cmVhbD43NzQ8L3Jl\n" + "YWw+CgkJCQkJCTxyZWFsPjU5NDwvcmVhbD4KCQkJCQk8L2FycmF5PgoJCQkJCTxrZXk+Y29tLmF\n" + "wcGxlLnByaW50LnRpY2tldC5jbGllbnQ8L2tleT4KCQkJCQk8c3RyaW5nPmNvbS5hcHBsZS5wcm\n" + "ludGluZ21hbmFnZXI8L3N0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQub\n" + "W9kRGF0ZTwva2V5PgoJCQkJCTxkYXRlPjIwMDctMDEtMzBUMjI6MDg6NDFaPC9kYXRlPgoJCQkJ\n" + "CTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWd\n" + "lcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5Pm\n" + "NvbS5hcHBsZS5wcmludC5QYXBlckluZm8uUE1QYXBlck5hbWU8L2tleT4KCQk8ZGljdD4KCQkJP\n" + "GtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20u\n" + "YXBwbGUucHJpbnQucG0uUG9zdFNjcmlwdDwvc3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcml\n" + "udC50aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZX\n" + "k+Y29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVBhcGVyTmFtZTwva2V5PgoJCQkJCTxzdHJpb\n" + "mc+bmEtbGV0dGVyPC9zdHJpbmc+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNs\n" + "aWVudDwva2V5PgoJCQkJCTxzdHJpbmc+Y29tLmFwcGxlLnByaW50LnBtLlBvc3RTY3JpcHQ8L3N\n" + "0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQubW9kRGF0ZTwva2V5PgoJCQ\n" + "kJCTxkYXRlPjIwMDMtMDctMDFUMTc6NDk6MzZaPC9kYXRlPgoJCQkJCTxrZXk+Y29tLmFwcGxlL\n" + "nByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4xPC9pbnRlZ2VyPgoJ\n" + "CQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5\n" + "QYXBlckluZm8uUE1VbmFkanVzdGVkUGFnZVJlY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb2\n" + "0uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuc\n" + "HJpbnQucG0uUG9zdFNjcmlwdDwvc3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNr\n" + "ZXQuaXRlbUFycmF5PC9rZXk+CgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk+Y29tLmF\n" + "wcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCQkJCTxhcn\n" + "JheT4KCQkJCQkJPHJlYWw+MC4wPC9yZWFsPgoJCQkJCQk8cmVhbD4wLjA8L3JlYWw+CgkJCQkJC\n" + "TxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw+NTc2PC9yZWFsPgoJCQkJCTwvYXJyYXk+CgkJ\n" + "CQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNsaWVudDwva2V5PgoJCQkJCTxzdHJpbmc\n" + "+Y29tLmFwcGxlLnByaW50aW5nbWFuYWdlcjwvc3RyaW5nPgoJCQkJCTxrZXk+Y29tLmFwcGxlLn\n" + "ByaW50LnRpY2tldC5tb2REYXRlPC9rZXk+CgkJCQkJPGRhdGU+MjAwNy0wMS0zMFQyMjowODo0M\n" + "Vo8L2RhdGU+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5\n" + "PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI+CgkJCQk8L2RpY3Q+CgkJCTwvYXJyYXk+CgkJPC9\n" + "kaWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYXBlcl\n" + "JlY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b\n" + "3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUucHJpbnQucG0uUG9zdFNjcmlwdDwvc3RyaW5n\n" + "PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJCTxhcnJ\n" + "heT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYW\n" + "RqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQkJCQk8YXJyYXk+CgkJCQkJCTxyZWFsPi0xODwvcmVhb\n" + "D4KCQkJCQkJPHJlYWw+LTE4PC9yZWFsPgoJCQkJCQk8cmVhbD43NzQ8L3JlYWw+CgkJCQkJCTxy\n" + "ZWFsPjU5NDwvcmVhbD4KCQkJCQk8L2FycmF5PgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnR\n" + "pY2tldC5jbGllbnQ8L2tleT4KCQkJCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbmFnZX\n" + "I8L3N0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQubW9kRGF0ZTwva2V5P\n" + "goJCQkJCTxkYXRlPjIwMDctMDEtMzBUMjI6MDg6NDFaPC9kYXRlPgoJCQkJCTxrZXk+Y29tLmFw\n" + "cGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2V\n" + "yPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcm\n" + "ludC5QYXBlckluZm8ucHBkLlBNUGFwZXJOYW1lPC9rZXk+CgkJPGRpY3Q+CgkJCTxrZXk+Y29tL\n" + "mFwcGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk+CgkJCTxzdHJpbmc+Y29tLmFwcGxlLnBy\n" + "aW50LnBtLlBvc3RTY3JpcHQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V\n" + "0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk+CgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcH\n" + "BsZS5wcmludC5QYXBlckluZm8ucHBkLlBNUGFwZXJOYW1lPC9rZXk+CgkJCQkJPHN0cmluZz5VU\n" + "yBMZXR0ZXI8L3N0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY2xpZW50\n" + "PC9rZXk+CgkJCQkJPHN0cmluZz5jb20uYXBwbGUucHJpbnQucG0uUG9zdFNjcmlwdDwvc3RyaW5\n" + "nPgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5tb2REYXRlPC9rZXk+CgkJCQkJPG\n" + "RhdGU+MjAwMy0wNy0wMVQxNzo0OTozNlo8L2RhdGU+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpb\n" + "nQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjE8L2ludGVnZXI+CgkJCQk8\n" + "L2RpY3Q+CgkJCTwvYXJyYXk+CgkJPC9kaWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2t\n" + "ldC5BUElWZXJzaW9uPC9rZXk+CgkJPHN0cmluZz4wMC4yMDwvc3RyaW5nPgoJCTxrZXk+Y29tLm\n" + "FwcGxlLnByaW50LnRpY2tldC5wcml2YXRlTG9jazwva2V5PgoJCTxmYWxzZS8+CgkJPGtleT5jb\n" + "20uYXBwbGUucHJpbnQudGlja2V0LnR5cGU8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmlu\n" + "dC5QYXBlckluZm9UaWNrZXQ8L3N0cmluZz4KCTwvZGljdD4KCTxrZXk+Y29tLmFwcGxlLnByaW5\n" + "0LnRpY2tldC5BUElWZXJzaW9uPC9rZXk+Cgk8c3RyaW5nPjAwLjIwPC9zdHJpbmc+Cgk8a2V5Pm\n" + "NvbS5hcHBsZS5wcmludC50aWNrZXQucHJpdmF0ZUxvY2s8L2tleT4KCTxmYWxzZS8+Cgk8a2V5P\n" + "mNvbS5hcHBsZS5wcmludC50aWNrZXQudHlwZTwva2V5PgoJPHN0cmluZz5jb20uYXBwbGUucHJp\n" + "bnQuUGFnZUZvcm1hdFRpY2tldDwvc3RyaW5nPgo8L2RpY3Q+CjwvcGxpc3Q+CjhCSU0D6QAAAAA\n" + "AeAADAAAASABIAAAAAALeAkD/7v/uAwYCUgNnBSgD/AACAAAASABIAAAAAALYAigAAQAAAGQAAA\n" + "ABAAMDAwAAAAF//wABAAEAAAAAAAAAAAAAAABoCAAZAZAAAAAAACAAAAAAAAAAAAAAAAAAAAAAA\n" + "AAAAAAAAAAAADhCSU0D7QAAAAAAEABIAAAAAQABAEgAAAABAAE4QklNBCYAAAAAAA4AAAAAAAAA\n" + "AAAAP4AAADhCSU0EDQAAAAAABAAAAB44QklNBBkAAAAAAAQAAAAeOEJJTQPzAAAAAAAJAAAAAAA\n" + "AAAABADhCSU0ECgAAAAAAAQAAOEJJTScQAAAAAAAKAAEAAAAAAAAAAThCSU0D9QAAAAAASAAvZm\n" + "YAAQBsZmYABgAAAAAAAQAvZmYAAQChmZoABgAAAAAAAQAyAAAAAQBaAAAABgAAAAAAAQA1AAAAA\n" + "QAtAAAABgAAAAAAAThCSU0D+AAAAAAAcAAA/////////////////////////////wPoAAAAAP//\n" + "//////////////////////////8D6AAAAAD/////////////////////////////A+gAAAAA///\n" + "//////////////////////////wPoAAA4QklNBAgAAAAAABAAAAABAAACQAAAAkAAAAAAOEJJTQ\n" + "QeAAAAAAAEAAAAADhCSU0EGgAAAAADRQAAAAYAAAAAAAAAAAAAAGQAAABkAAAACABEAFMAQwAwA\n" + "DIAMwAyADUAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAGQAAABkAAAAAAAAAAAA\n" + "AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAEAAAAAAABudWxsAAAAAgAAAAZib3VuZHN\n" + "PYmpjAAAAAQAAAAAAAFJjdDEAAAAEAAAAAFRvcCBsb25nAAAAAAAAAABMZWZ0bG9uZwAAAAAAAA\n" + "AAQnRvbWxvbmcAAABkAAAAAFJnaHRsb25nAAAAZAAAAAZzbGljZXNWbExzAAAAAU9iamMAAAABA\n" + "AAAAAAFc2xpY2UAAAASAAAAB3NsaWNlSURsb25nAAAAAAAAAAdncm91cElEbG9uZwAAAAAAAAAG\n" + "b3JpZ2luZW51bQAAAAxFU2xpY2VPcmlnaW4AAAANYXV0b0dlbmVyYXRlZAAAAABUeXBlZW51bQA\n" + "AAApFU2xpY2VUeXBlAAAAAEltZyAAAAAGYm91bmRzT2JqYwAAAAEAAAAAAABSY3QxAAAABAAAAA\n" + "BUb3AgbG9uZwAAAAAAAAAATGVmdGxvbmcAAAAAAAAAAEJ0b21sb25nAAAAZAAAAABSZ2h0bG9uZ\n" + "wAAAGQAAAADdXJsVEVYVAAAAAEAAAAAAABudWxsVEVYVAAAAAEAAAAAAABNc2dlVEVYVAAAAAEA\n" + "AAAAAAZhbHRUYWdURVhUAAAAAQAAAAAADmNlbGxUZXh0SXNIVE1MYm9vbAEAAAAIY2VsbFRleHR\n" + "URVhUAAAAAQAAAAAACWhvcnpBbGlnbmVudW0AAAAPRVNsaWNlSG9yekFsaWduAAAAB2RlZmF1bH\n" + "QAAAAJdmVydEFsaWduZW51bQAAAA9FU2xpY2VWZXJ0QWxpZ24AAAAHZGVmYXVsdAAAAAtiZ0Nvb\n" + "G9yVHlwZWVudW0AAAARRVNsaWNlQkdDb2xvclR5cGUAAAAATm9uZQAAAAl0b3BPdXRzZXRsb25n\n" + "AAAAAAAAAApsZWZ0T3V0c2V0bG9uZwAAAAAAAAAMYm90dG9tT3V0c2V0bG9uZwAAAAAAAAALcml\n" + "naHRPdXRzZXRsb25nAAAAAAA4QklNBBEAAAAAAAEBADhCSU0EFAAAAAAABAAAAAE4QklNBAwAAA\n" + "AACfkAAAABAAAAZAAAAGQAAAEsAAB1MAAACd0AGAAB/9j/4AAQSkZJRgABAgEASABIAAD/7QAMQ\n" + "WRvYmVfQ00AAv/uAA5BZG9iZQBkgAAAAAH/2wCEAAwICAgJCAwJCQwRCwoLERUPDAwPFRgTExUT\n" + "ExgRDAwMDAwMEQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBDQsLDQ4NEA4OEBQODg4UFA4\n" + "ODg4UEQwMDAwMEREMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDP/AABEIAGQAZA\n" + "MBIgACEQEDEQH/3QAEAAf/xAE/AAABBQEBAQEBAQAAAAAAAAADAAECBAUGBwgJCgsBAAEFAQEBA\n" + "QEBAAAAAAAAAAEAAgMEBQYHCAkKCxAAAQQBAwIEAgUHBggFAwwzAQACEQMEIRIxBUFRYRMicYEy\n" + "BhSRobFCIyQVUsFiMzRygtFDByWSU/Dh8WNzNRaisoMmRJNUZEXCo3Q2F9JV4mXys4TD03Xj80Y\n" + "nlKSFtJXE1OT0pbXF1eX1VmZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3EQACAgECBAQDBAUGBwcGBT\n" + "UBAAIRAyExEgRBUWFxIhMFMoGRFKGxQiPBUtHwMyRi4XKCkkNTFWNzNPElBhaisoMHJjXC0kSTV\n" + "KMXZEVVNnRl4vKzhMPTdePzRpSkhbSVxNTk9KW1xdXl9VZmdoaWprbG1ub2JzdHV2d3h5ent8f/\n" + "2gAMAwEAAhEDEQA/APLtso1NRc0vP0Rok8NYyPEfijOG2ljBoAJPxKFppZtbS4Rz38kV+OPRDge\n" + "T89EPHBfvLjtb3P8A30K/j47cgsrYNxGpPYJpK8RtyXUlvPfsobV0GV0uippLiX3EaMb2/rKgMB\n" + "1ghoiNST4BESCjjLmxqmKtvxiXQ0cd0q8E2bjIDWjk9z5I8QW8JaoHcdkUePZJtZD9p8YU/Rsc/\n" + "wBNjS5zjDWjUk+SSKYaJLYq+qWeYGQ5lBPLJ3OA8wz2/wDSWni/U3H2AXW2l2oloa0f9LcjSLeU\n" + "hJdb/wAyqd387Zt+DZ5SSpVh/9DzO6dw7gGPuVn6ft/kyPkqwlxjw1Rnh24QNWjUeR5TSuDc6bg\n" + "fatpsJZQ3sNC4rWfkVYpbi4LAb3aANEkFLp7GHGYxuhAj4K/hYVNDjYGzZ++eSSoSbLZjGgwxul\n" + "XNrPqO35FukdmzyXOQeqtqwqRg4o/SOAN9ng3/AMzW02txZ9I+ZHKr241UOcWDaz3uLtSSPEpWu\n" + "rR5XPeylmyNr4BIPPCyH2Oc6T8kXNvddkPe/VzjJPxQAJMKeIoNScrPk2MbfddXUNXvcGtPx0Xb\n" + "dJ6NXjOD2Dfdw6w9v5LFW+q/1WLA3Ly9LSJaz91p/wDRjl2lOLWwAMbEJErWjRgESYieVdZhsMF\n" + "wMt08ldrx/vVivHaOdSgCoud9krmElpba93ASTlr/AP/R83ohr97voiJV/Fq9QvsI+mdPgs1thc\n" + "BWO5C38CoOY1g78qOejLiGvknxLAyGtExp5K9uzGt9RrNw7DhRfQKKx6bZIGgPj4rPycLqWVtIs\n" + "JGu5skDyTBRZtQNrb1fU8xrtpBaO4MLQxcx1sNuEjt5rMGJR9noY5hF7Wxa8aAnxVvDb6bgHH2z\n" + "omk0e64ajUUXnev9Idi5rrWAux7SXNd4E/muS+rHSjm9VbPtZjj1CSJBI+g3+0uh69b+iDG/QcD\n" + "u0nQCeFP6l0MZhWX/AJ1xM+QafY1TQlY1a+WABsdXp8Sp27aBH+vZaVbC0ADlVcASwtdOolp8Ct\n" + "BjmtGv0uI8EmJmxkIjWkmPEKLSPiidxIgJKRbDPCSN5pJyH//S87uw/suZ6c72iC13kVs9PdDmk\n" + "KllVziV3cuafc7yP0QjYFh26cqM6hsxAjIj6u6xzbDHh3R663AaceH+5BwdruVp2PqZUA0a9yo6\n" + "DPQajscnXb8YQdzC8H909joiZttoxoBIa4gGOQ3uqh+z1RuD2Ds4j2n+39FNKaFMevS/p5LPpSA\n" + "I8/b/ABW70THZXj11VjaIAIHgFl5VdD8AneDMaec6Lb6QAKmu7x+VSw2a3MdF/rF9YKeh4B9OHZ\n" + "lpAprPH8p7v5DFwrPrV9YDa645toLjMaFo8mtcEvrhkWZHXbg7T0Q2to8o3f8AfkarEitlVTKnY\n" + "ra992QQ2wOfG57H2bg7H2fzbFKA130P6n9dHWemCx5H2mk7LgPH818f8IuhAka6ea8y/wAWrcod\n" + "VyrceRhBsPae5J/Qj+sxq9KDpMuMuKBCEntnny/1CSaWxM6pIKf/0/MvtF3pCrefTBnbOi1elP3\n" + "Et8Vi+Sv9LuNd4HwTSNGSEqkLerwtwn+SYKtOeS4A8Krh2j1D/K1Vu4B5gaDmVDJtAr7zYYGoRB\n" + "QDWQ4c9h/csuyjI3fobnDyJR8fF6ltcTaXRwCkAuAsfMGr1TGFNdTmEwLWS2dIK6npLgK2T4Lle\n" + "pUZxoc9+6K4eR5NO5bPT73NoqIfoILT/JcFJDZr8zGiNXnvrfiur6/Y8tht7WvaexgbXf8AUrFt\n" + "8IExyvRusYDOsYTAIbfWdzHRJ8HN/tLj7OgdRZawmreHP2gt9wEfvtH0f7SkDXe7+o+AOn9DquL\n" + "f0mV+leQPH6H+axafUvrB07ptJtyshtTZDTEudJ7bWS5V6MmyltVLn7ht2hwECQP+isb60/Vqvr\n" + "tbLsa1lObVIJd9Gxv5rXx9F7fzHpIbf/jgfVnd/TLYj6XoOhJcP/zE+sOzd6dW7dt2eo3dH7/9R\n" + "JJWj//U8uiGFx76BFZLQ2xvLeVGAWQrFDJbtKBSHd6blNura4H3BbDXB7InVcZXZdh2bmTt7hbO\n" + "J1dj2gzCjlFnhPod3WLHB+n3o9ZsAkFVMfMrs7orLmgkHUdkyqZQQWWQbLGlrjMjUeSrfV3Ltsw\n" + "30EBzcd5YCedvLETJya66nWOIAaCVnfU/KuZn21CDVa02PngQdHf9LapMfVhzkaAPUUW3M91YaR\n" + "3YDJ+WiBmZGazPo9Kttdt2j63E6s/fft/d/NWjXkMra7KtO2qkE6cErHpvsyMmzPu0dY4Bg/dYP\n" + "otTpyoaMUI2XUya8tzG/pi0NMtICo/bsut21gdcWclkj5OncxaDrw6kM+9QxQzaWRAGii4pDqzC\n" + "MT02aX7WzPU9b7PrG3bvO6P6yStfZm+pHnPySS4590+3jf/V8yb+CsUbp8uyz0kDskbu2dmz9J8\n" + "lSt9Ld+gn1O8cKikmxXydbH+3bhsmfwWj/lONYlcwkhL6L4bfpOxn/tD0/wBN/N944Wh9VJm/b/\n" + "O+347df+/rl0k+O38GLJ83X/CfTOt7v2dV6P8AMbx6njHb/pKuN3pN2+IXnaSjybr8e31fUqd+0\n" + "Sj487DHMryZJMXjq+sfpPX84SXk6SSX/9kAOEJJTQQhAAAAAABVAAAAAQEAAAAPAEEAZABvAGIA\n" + "ZQAgAFAAaABvAHQAbwBzAGgAbwBwAAAAEwBBAGQAbwBiAGUAIABQAGgAbwB0AG8AcwBoAG8AcAA\n" + "gADcALgAwAAAAAQA4QklNBAYAAAAAAAcABQAAAAEBAP/hFWdodHRwOi8vbnMuYWRvYmUuY29tL3\n" + "hhcC8xLjAvADw/eHBhY2tldCBiZWdpbj0n77u/JyBpZD0nVzVNME1wQ2VoaUh6cmVTek5UY3prY\n" + "zlkJz8+Cjw/YWRvYmUteGFwLWZpbHRlcnMgZXNjPSJDUiI/Pgo8eDp4YXBtZXRhIHhtbG5zOng9\n" + "J2Fkb2JlOm5zOm1ldGEvJyB4OnhhcHRrPSdYTVAgdG9vbGtpdCAyLjguMi0zMywgZnJhbWV3b3J\n" + "rIDEuNSc+CjxyZGY6UkRGIHhtbG5zOnJkZj0naHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi\n" + "1yZGYtc3ludGF4LW5zIycgeG1sbnM6aVg9J2h0dHA6Ly9ucy5hZG9iZS5jb20vaVgvMS4wLyc+C\n" + "gogPHJkZjpEZXNjcmlwdGlvbiBhYm91dD0ndXVpZDoyMmQwMmIwYS1iMjQ5LTExZGItOGFmOC05\n" + "MWQ1NDAzZjkyZjknCiAgeG1sbnM6cGRmPSdodHRwOi8vbnMuYWRvYmUuY29tL3BkZi8xLjMvJz4\n" + "KICA8IS0tIHBkZjpTdWJqZWN0IGlzIGFsaWFzZWQgLS0+CiA8L3JkZjpEZXNjcmlwdGlvbj4KCi\n" + "A8cmRmOkRlc2NyaXB0aW9uIGFib3V0PSd1dWlkOjIyZDAyYjBhLWIyNDktMTFkYi04YWY4LTkxZ\n" + "DU0MDNmOTJmOScKICB4bWxuczpwaG90b3Nob3A9J2h0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9z\n" + "aG9wLzEuMC8nPgogIDwhLS0gcGhvdG9zaG9wOkNhcHRpb24gaXMgYWxpYXNlZCAtLT4KIDwvcmR\n" + "mOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gYWJvdXQ9J3V1aWQ6MjJkMDJiMGEtYj\n" + "I0OS0xMWRiLThhZjgtOTFkNTQwM2Y5MmY5JwogIHhtbG5zOnhhcD0naHR0cDovL25zLmFkb2JlL\n" + "mNvbS94YXAvMS4wLyc+CiAgPCEtLSB4YXA6RGVzY3JpcHRpb24gaXMgYWxpYXNlZCAtLT4KIDwv\n" + "cmRmOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gYWJvdXQ9J3V1aWQ6MjJkMDJiMGE\n" + "tYjI0OS0xMWRiLThhZjgtOTFkNTQwM2Y5MmY5JwogIHhtbG5zOnhhcE1NPSdodHRwOi8vbnMuYW\n" + "RvYmUuY29tL3hhcC8xLjAvbW0vJz4KICA8eGFwTU06RG9jdW1lbnRJRD5hZG9iZTpkb2NpZDpwa\n" + "G90b3Nob3A6MjJkMDJiMDYtYjI0OS0xMWRiLThhZjgtOTFkNTQwM2Y5MmY5PC94YXBNTTpEb2N1\n" + "bWVudElEPgogPC9yZGY6RGVzY3JpcHRpb24+CgogPHJkZjpEZXNjcmlwdGlvbiBhYm91dD0ndXV\n" + "pZDoyMmQwMmIwYS1iMjQ5LTExZGItOGFmOC05MWQ1NDAzZjkyZjknCiAgeG1sbnM6ZGM9J2h0dH\n" + "A6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvJz4KICA8ZGM6ZGVzY3JpcHRpb24+CiAgIDxyZ\n" + "GY6QWx0PgogICAgPHJkZjpsaSB4bWw6bGFuZz0neC1kZWZhdWx0Jz4gICAgICAgICAgICAgICAg\n" + "ICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgIDwvcmRmOkFsdD4KICA8L2RjOmRlc2NyaXB0aW9\n" + "uPgogPC9yZGY6RGVzY3JpcHRpb24+Cgo8L3JkZjpSREY+CjwveDp4YXBtZXRhPgogICAgICAgIC\n" + "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n" + "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAg\n" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n" + "gICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n" + "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n" + "CAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n" + "gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n" + "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgI\n" + "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICA\n" + "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n" + "AgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n" + "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n" + "ICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n" + "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA\n" + "ogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n" + "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAg\n" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n" + "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgIC\n" + "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n" + "CAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n" + "gICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n" + "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKI\n" + "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICA\n" + "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n" + "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgI\n" + "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n" + "ICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n" + "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n" + "AgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n" + "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAg\n" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n" + "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgIC\n" + "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n" + "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAg\n" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n" + "gICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n" + "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n" + "CAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICA\n" + "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n" + "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgI\n" + "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICA\n" + "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n" + "AgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n" + "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n" + "ICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n" + "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgIC\n" + "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n" + "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAg\n" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n" + "gICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n" + "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n" + "CAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n" + "gICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n" + "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgI\n" + "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICA\n" + "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n" + "AgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n" + "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n" + "ICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n" + "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n" + "AgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n" + "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAg\n" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n" + "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgIC\n" + "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0ndyc/P\n" + "v/uAA5BZG9iZQBkQAAAAAH/2wCEAAQDAwMDAwQDAwQGBAMEBgcFBAQFBwgGBgcGBggKCAkJCQkI\n" + "CgoMDAwMDAoMDAwMDAwMDAwMDAwMDAwMDAwMDAwBBAUFCAcIDwoKDxQODg4UFA4ODg4UEQwMDAw\n" + "MEREMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDP/AABEIAGQAZAMBEQACEQEDEQ\n" + "H/3QAEAA3/xAGiAAAABwEBAQEBAAAAAAAAAAAEBQMCBgEABwgJCgsBAAICAwEBAQEBAAAAAAAAA\n" + "AEAAgMEBQYHCAkKCxAAAgEDAwIEAgYHAwQCBgJzAQIDEQQABSESMUFRBhNhInGBFDKRoQcVsUIj\n" + "wVLR4TMWYvAkcoLxJUM0U5KismNzwjVEJ5OjszYXVGR0w9LiCCaDCQoYGYSURUaktFbTVSga8uP\n" + "zxNTk9GV1hZWltcXV5fVmdoaWprbG1ub2N0dXZ3eHl6e3x9fn9zhIWGh4iJiouMjY6PgpOUlZaX\n" + "mJmam5ydnp+So6SlpqeoqaqrrK2ur6EQACAgECAwUFBAUGBAgDA20BAAIRAwQhEjFBBVETYSIGc\n" + "YGRMqGx8BTB0eEjQhVSYnLxMyQ0Q4IWklMlomOywgdz0jXiRIMXVJMICQoYGSY2RRonZHRVN/Kj\n" + "s8MoKdPj84SUpLTE1OT0ZXWFlaW1xdXl9UZWZnaGlqa2xtbm9kdXZ3eHl6e3x9fn9zhIWGh4iJi\n" + "ouMjY6Pg5SVlpeYmZqbnJ2en5KjpKWmp6ipqqusra6vr/2gAMAwEAAhEDEQA/APBnplwPAdR+GB\n" + "KY6dYtNG1w39yh4+xb+zIksgEfFaRSSoIx8f7RPRRkSWQimM+lRmwWVXFWYigHxUUVoMiJM+Fj0\n" + "tg0RBegLE0Wu+3c+GTBazFCGI7HtSp9slbFYYzyoBsegw2hY1Afl3wqqRqahk+0tDgKpgu4DAUU\n" + "+HY+GRS2ePiMKtUB3G+KGuONq//Q8OzpFbW5WnxMop4k9crG5ZnZNJkEOn21utVRYw7HxZtz+OR\n" + "vdsrZ2lRtci4aVxFEQA0neg/ZXxJpTITNNuOFss0vSotYNvZ2qGRkPKSTqiU8Sdqk5SZU5Ix8XJ\n" + "NNZ8k6bp8TtM73OputUtYq0Unux/hkRkJOzZLCAN2KR+VpbtSkCBaDnIzdlWu59u+XeJTjeASk8\n" + "+juZOESEAVqx8BvU/PJibScTrTy09560hkWOGFd2YgFnPQKD19zhOSkxw2l8Vm6XAiYb8gg+k5O\n" + "9mnhoon9H3cs5s7WF5pp29OGGMFndyaAKBuTiEEPQLD8h/NDmNdYlttNkYjlbFjcXCr3LLH8II8\n" + "C2WUGviZvon/OPWkm3RNSv72SYllMkKxQRV67CQMSKYQAxMkR/wBC56d61P0heel4cYuVOXWvTp\n" + "h4Qjjf/9Hw5qBYyISaqjBV+QpvkAzKcki4HomnIxck/wBhtlR2bhunvlDywddMUl4zW+kQ9FQ8X\n" + "nfuSewrtmPkycPvc/DhMhvyegXOrWWhmLQPKlsj6xIAiLCoZkY96nv7npmJvI2XOjQFMl0fyRqM\n" + "NoxvZvrGt33wlATwiMnVnY1LEdSfuyXF3KIDmUu88w2XlnTl8raAlb2ZFfVL0jdYRtQnxc7BfDC\n" + "OaJR7nm3me5tdOtjbMvp3ZRXkV6chVQRX79hmVjgZG+jgZ5jHGhzecXF5LPL6jEjstSSaDM51Ka\n" + "6MZ9S1C0sEBe8uZo4YCBXdjxGw60wEWyEqfUHkT8vLXRJFuLdTcaqfhlvWUErtukZ3ABPUjIXTE\n" + "m3rGmeV2Tk5UKz/AG/E/wAcgZKya20C3b02kjYtH8AqCygbkUH0nLYlgUb+gbWtPbpXt/n2ybB/\n" + "/9Lw4oaVxGd+PxH3qBkGaY3KyiSP01IkiUclH8sg+LKydm6INvZvKsFu+kWtvD8LRoFNRup6moO\n" + "aqd277HsGW+XPLmn6XM17FF6l7vW4fd2Zuu+RFls2tmUNrLJb7TSBertGQGqetDkxE0na0pvtHs\n" + "QkszWyiGAG5laYlnkeMVHJj8sA5rPk+SvMepTalqlxd3B5zTOXdj/MxqafLpm5xioh5nPK5kpRG\n" + "pkcKAST0A6k5NpfUP5K/ki1ssHmHzF+71KRQ8Nud/Qibb/kYw6/yjbrXISlSH07YaHbWyxx2kXE\n" + "KACB2zHJtLI7XSelBRvH2xCpvaaTDHXkOTVBPcUG2479RlsdmJVPRtvV+ylenQ0y62FP/9PxRpo\n" + "WG5FxKKxKFDA+GVS5NsebLdFsRePc3siVW4f4QR0QVAGYeSXR2unhtZ6s60K6jt+MMSFwtF2+xX\n" + "wr7eGUGLlRPQMsE2vxQm7itxKg3VCfT2+nb8cDYaCDtfOXmCCcROrQrUhkkCHYn6emRMqZxjbLd\n" + "F1+W/4xajHzjNCtQKMffETWUdngX5p+QZ9A8xS6hbo0ui37NNDPT7DOalHpsCD08Rmyw5ARTpdV\n" + "gIPEF35MeRn80ed4S5EdrpKm9kZ15K0iH92hB7Me/tmS60vt/QrCYyekiBdgSTXcjqV9q9MokFD\n" + "N7S3aFVVR8RoK9zldqndvAY6nffr/AGYQqLhjdpCoIAZW22HavU/LJBUP9WblX0xTw7fOmWsX/9\n" + "Tw7FdvMqWkQ3Z1qfED+mQIbI77PX/LFis9vBajZm2Y+x65rMh3t30Bsze400aVaIbSLk6r8CMRT\n" + "l/NmOcllnGDD9Y8uecNfEEiXrMgDGWAyGOOu5WlB+vMrHODTlxZCdjsyFdB006VpVtLasurQxBL\n" + "64WiLI4/aFT1ANOXemV5piR2b9NiljB4yyHy9CLOVI5GJhB+CvXY9R8xmINzs5HNZ+Z96BZpbxA\n" + "fVJo39UFefwopYgL4nMiMd2qZoIn/AJx00u3t/Lt7qpp9Yv5GLf5MUTERqfbvmzBeezjd9H+VlL\n" + "wSQzBqsvOGQD7L12rXsemPNxmXQSxxIPU2nFV4HYqR1xEUWj4ZAxBryr2G+J2VGDZlLrxUH6KZA\n" + "Fkqb15VFelfwy+2FP8A/9Xxlf6AdA182Yk9eFeLxSjoVfcfSMo4uIOfkweFOnpvlWYrLEwNFAA+\n" + "nMOYdrhFvQLeSO7coBXiK8iKiv07Zj8Ac4QtNrW1njUcKcT+yAR/xGmR4WcsStLpTuPU9IFaEsV\n" + "BP3k4m2AgBzSwyQNcIwNTE1aI3wnam9O2Ug7s5Ckk/NDndeVXa2H78MqqV6jmeBp9+ZWKXqDjZ4\n" + "+gvVvy30qCy0qzsLRBCnBI2VdgUTqPvOZ7y+Q7pz+bn5q6d+VflZxZlJ/NN4ypptk5qtB9qRwDX\n" + "gn/AAx2y2ItpfKFv+eH5qNeTajJ5ovVaVywSqvEtTUKqupAA6D2y0BNPtv/AJx//M5PzL8mJeXT\n" + "L+ndPf6rqarSpkAqsnEAAeoN6DpkJRYci9lROSgSUUH9o9K5Tw0ztfSHnXkOtK9q+PHwydq//9b\n" + "yxrVoZNBtNSA5zRMPXmH8j0CLXuBmHE+qneamHpEuqYeV7pzFVTRgQK5XMNmnlb1vyyY5QA1OwJ\n" + "+eUF2seTOLu5s7azVIVAkpVn/hhnIALG73Yz5jvb1dICqzpDNIqyFD8SxH7R28cxibZCiWOsdJs\n" + "PTM6XNstPhnkjIhcHuJBVfvOCiUSn0TfWrTTLjyw8guA/PifTO3xcxxA8a5ZAbimvJP0m3p/kFF\n" + "WxhmpWQJ9NW3zZPHz5vlb/nIDVbrWfzO1RJhxGnpDaRL/khA1T7ktmSOTAJhZaAUtLawsbayl8v\n" + "xWi3Gpay0cF3HPcFRJJHJMXVrcJ8UaAFG5LWjF8tAYW9H/wCcOo9bTzxrt/owkTyksZW5gkIKvI\n" + "7k26nvyReRJHyyBWT7dWQyOWlbnK2526e1O1MqIUFE84uPLkOdK9RXI0E2/wD/1/DA1bURZLY/W\n" + "ZDZqwb0eXw7dMgIi7bjllVXsz7yNcfWC0Vd3Ip92Y2UOz0cnsPlwyx8xQ/u24sMxCadoJp9LOXk\n" + "VX/uwRUE0BI8cokbLMyoKouHu2MaKGXw7fLDwgoGSkbHpaNZyLLHRSKcFFQQRvUdMlwUFOQyLzr\n" + "ztpCaba6fPau4ijv4OURY8AjVFKV7ZZiO+7Vnh6XvXkSWNbW2WTb92KDxIFMzwHlZc3zX+fuizW\n" + "f5p3ty8XGDU4YLmCQiisyII3+4rvl8UB5ffEghRGvOm7AbnvWvjk1fen/ONPldPKP5aWOpPCfr2\n" + "uE31y6q2wbaMEn+VAMDSdyzrzj+avlHyTp0l/r2rxWFuHWJuIeacu4qFCRgsajfBwsty89/6Gr/\n" + "ACa9an+JL/hSnrfoubhXwpXpjwhaL//Q8E1AqtcAZMs8l6i1nqMa1oSVP0VynKLDmaWdSfQXl69\n" + "jF1Jv8MhDb5rpB3AO7INRRLhhGp4R05FgaGvTMU8200xS70zVDMRp2pTIOvBmB3PgQP15kxIcnD\n" + "LH/EEz0rRvOJhldr9pQtCqyd6VrShGTqw5d4ARv9jHfOGl+ZJNMluLkyenaFbiRdqFYW5nrWuwO\n" + "MKB5MdSMRxnhlu9N8p6lLFpti63FUjCtFJTrDKvse2bEDZ4XJ9RZB+YPli2/Mjy5bxoUi1a0YS2\n" + "85UOwIXiy9jRu+TBppfOF1+V3m22vrdpNPM8cs/oo0VJlUqQPjValR3+IZNNvtLS9Yu9Mi0/TJr\n" + "kyp6QhWVVCIWRATsKBemwwFrDzT87fybs/wA1bW21PRb+DTvNlgGSRp6iC8i3KJJx+y6n7D0Pwm\n" + "hxBZXT55/6Fi/Nf0PW+qWXq+t6X1X67F6vD/ftK04V/wBl344U8b//0fBapxheVh9ocV+nviqY2\n" + "/qQJDew/bioWHiuQ8m0bbvaPKGtQ6jaxSo9JloCK75gZI0Xb4sgkHo8MouoAvP94BsRmGY7uWJU\n" + "gzbypOQpNOvIdK4Nw2WCE2tXulTkjEEbdafgclxMhFBas93dwyQzsWDghlJFONKHJCZtjOFBJfy\n" + "j1y9vPL9zpbIs0WkXL2sUjA8hDXlGCRXtt07ZuYvL5KJeo6bfajbkzWkcToR8dqshZ6in2fhNK/\n" + "PDTUlXmHVvMdr5o0v9H2kdrqGpfu7m0nkY87Uf7tkKAU4/s03ynLkEBbfihx7dGT6va67LbRMNR\n" + "aKOBuUTKgIBXoK1BOYR1M3aQ0mOt9yxUeZNdtJhFapLqMluSXkg5oxJrUMW5KevQ9MmNXXNqOiH\n" + "Rr/Hmv8A1r9I/oj95w+r+j9Yf1+NP5+nXtTD+dF8tkfkOlv/0vC3ph7f0/alcVTbS4A8QibuKb5\n" + "RI05EBYRFpdX3ly79a2qYCavH/EY7TCYyMD5PSdD8+wXUSn1ArDqOhBzFlipz4ZwWbaV5htbsgF\n" + "qg9crMXKErGyYwajFGzxyHlGSePbbwyqg5UZlCaxrFpaWU95LIqrEjMAT4Dp9OShGy1ZslBhv/A\n" + "Dj9rd/a+aL+xUK+m38L3d0HrxRo2HFtu5D8c27y8t30raarbWkU+u6g4gsNORn+EcUaSh2Pc0/4\n" + "lgtAjezzbT9SutY1i782al8Nxdyotqh6xWybIg+jc5q8s+I27bFDgFPQp9RE+nrag70+L6crrZu\n" + "4jajokdv6LW/Dii1Wo61PXKQN3KPK0L+h4/rnD/K5V78a5LhXxd3/0/DMXXtwxVNtL9Xkaf3f7N\n" + "etfbKMjdjtkZ9D6ufrlK0+HpX8coF9HJ26sXvfqXrf7i/U+uften/d/wCyrmQL6uOav0pvpP8Ai\n" + "b1F+rV59+vH6a5XLhcjH4nRmY/xpxHP0/UptWvT6Mx/RbmjxWK+aP8AFf1M/pCv1Kvxen9inavf\n" + "MrFwXtzcLUeLXq5Mv/I3nz1b0v8AjofuKVry9KrUpTanOlf9jmQ68va/zH9b/COn/o7/AI431mP\n" + "65SvLh+zWvbl9rMfNfC34K4kmj9T6lD6FKclp/DNYXZx5srsPrHor6nXvkgxTPS/U+rv6dPU5mt\n" + "fngFN5ulv+l/pL/Lp/scerHo//2Q==\n"; + +static std::string gCommandLine; + +TEST(Base64, LargeSample) { + LOG(LS_VERBOSE) << "Testing specific base64 file"; + + char unescaped[64 * 1024]; + + // unescape that massive blob above + size_t size = Base64Unescape(SpecificTest, + sizeof(SpecificTest), + unescaped, + sizeof(unescaped)); + + EXPECT_EQ(size, sizeof(testbase64)); + EXPECT_EQ(0, memcmp(testbase64, unescaped, sizeof(testbase64))); +} + +bool DecodeTest(const char* encoded, size_t expect_unparsed, + const char* decoded, Base64::DecodeFlags flags) +{ + std::string result; + size_t consumed = 0, encoded_len = strlen(encoded); + bool success = Base64::DecodeFromArray(encoded, encoded_len, flags, + &result, &consumed); + size_t unparsed = encoded_len - consumed; + EXPECT_EQ(expect_unparsed, unparsed) << "\"" << encoded + << "\" -> \"" << decoded + << "\""; + EXPECT_STREQ(decoded, result.c_str()); + return success; +} + +#define Flags(x,y,z) \ + Base64::DO_PARSE_##x | Base64::DO_PAD_##y | Base64::DO_TERM_##z + +TEST(Base64, DecodeParseOptions) { + // Trailing whitespace + EXPECT_TRUE (DecodeTest("YWJjZA== ", 1, "abcd", Flags(STRICT, YES, CHAR))); + EXPECT_TRUE (DecodeTest("YWJjZA== ", 0, "abcd", Flags(WHITE, YES, CHAR))); + EXPECT_TRUE (DecodeTest("YWJjZA== ", 0, "abcd", Flags(ANY, YES, CHAR))); + + // Embedded whitespace + EXPECT_FALSE(DecodeTest("YWJjZA= =", 3, "abcd", Flags(STRICT, YES, CHAR))); + EXPECT_TRUE (DecodeTest("YWJjZA= =", 0, "abcd", Flags(WHITE, YES, CHAR))); + EXPECT_TRUE (DecodeTest("YWJjZA= =", 0, "abcd", Flags(ANY, YES, CHAR))); + + // Embedded non-base64 characters + EXPECT_FALSE(DecodeTest("YWJjZA=*=", 3, "abcd", Flags(STRICT, YES, CHAR))); + EXPECT_FALSE(DecodeTest("YWJjZA=*=", 3, "abcd", Flags(WHITE, YES, CHAR))); + EXPECT_TRUE (DecodeTest("YWJjZA=*=", 0, "abcd", Flags(ANY, YES, CHAR))); + + // Unexpected padding characters + EXPECT_FALSE(DecodeTest("YW=JjZA==", 7, "a", Flags(STRICT, YES, CHAR))); + EXPECT_FALSE(DecodeTest("YW=JjZA==", 7, "a", Flags(WHITE, YES, CHAR))); + EXPECT_TRUE (DecodeTest("YW=JjZA==", 0, "abcd", Flags(ANY, YES, CHAR))); +} + +TEST(Base64, DecodePadOptions) { + // Padding + EXPECT_TRUE (DecodeTest("YWJjZA==", 0, "abcd", Flags(STRICT, YES, CHAR))); + EXPECT_TRUE (DecodeTest("YWJjZA==", 0, "abcd", Flags(STRICT, ANY, CHAR))); + EXPECT_TRUE (DecodeTest("YWJjZA==", 2, "abcd", Flags(STRICT, NO, CHAR))); + + // Incomplete padding + EXPECT_FALSE(DecodeTest("YWJjZA=", 1, "abcd", Flags(STRICT, YES, CHAR))); + EXPECT_TRUE (DecodeTest("YWJjZA=", 1, "abcd", Flags(STRICT, ANY, CHAR))); + EXPECT_TRUE (DecodeTest("YWJjZA=", 1, "abcd", Flags(STRICT, NO, CHAR))); + + // No padding + EXPECT_FALSE(DecodeTest("YWJjZA", 0, "abcd", Flags(STRICT, YES, CHAR))); + EXPECT_TRUE (DecodeTest("YWJjZA", 0, "abcd", Flags(STRICT, ANY, CHAR))); + EXPECT_TRUE (DecodeTest("YWJjZA", 0, "abcd", Flags(STRICT, NO, CHAR))); +} + +TEST(Base64, DecodeTerminateOptions) { + // Complete quantum + EXPECT_TRUE (DecodeTest("YWJj", 0, "abc", Flags(STRICT, NO, BUFFER))); + EXPECT_TRUE (DecodeTest("YWJj", 0, "abc", Flags(STRICT, NO, CHAR))); + EXPECT_TRUE (DecodeTest("YWJj", 0, "abc", Flags(STRICT, NO, ANY))); + + // Complete quantum with trailing data + EXPECT_FALSE(DecodeTest("YWJj*", 1, "abc", Flags(STRICT, NO, BUFFER))); + EXPECT_TRUE (DecodeTest("YWJj*", 1, "abc", Flags(STRICT, NO, CHAR))); + EXPECT_TRUE (DecodeTest("YWJj*", 1, "abc", Flags(STRICT, NO, ANY))); + + // Incomplete quantum + EXPECT_FALSE(DecodeTest("YWJ", 0, "ab", Flags(STRICT, NO, BUFFER))); + EXPECT_FALSE(DecodeTest("YWJ", 0, "ab", Flags(STRICT, NO, CHAR))); + EXPECT_TRUE (DecodeTest("YWJ", 0, "ab", Flags(STRICT, NO, ANY))); +} + +TEST(Base64, GetNextBase64Char) { + // The table looks like this: + // "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + char next_char; + EXPECT_TRUE(Base64::GetNextBase64Char('A', &next_char)); + EXPECT_EQ('B', next_char); + EXPECT_TRUE(Base64::GetNextBase64Char('Z', &next_char)); + EXPECT_EQ('a', next_char); + EXPECT_TRUE(Base64::GetNextBase64Char('/', &next_char)); + EXPECT_EQ('A', next_char); + EXPECT_FALSE(Base64::GetNextBase64Char('&', &next_char)); + EXPECT_FALSE(Base64::GetNextBase64Char('Z', NULL)); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/base.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/base.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/base.gyp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/base.gyp 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,745 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +{ + 'includes': [ '../build/common.gypi', ], + 'conditions': [ + ['os_posix == 1 and OS != "mac" and OS != "ios"', { + 'conditions': [ + ['sysroot!=""', { + 'variables': { + 'pkg-config': '../../../build/linux/pkg-config-wrapper "<(sysroot)" "<(target_arch)"', + }, + }, { + 'variables': { + 'pkg-config': 'pkg-config' + }, + }], + ], + }], + ], + 'targets': [ + { + # Temporary target until Chromium's + # src/third_party/libjingle/libjingle.gyp is updated to use rtc_base. + # TODO(kjellander): Remove when r7140 is rolled into Chromium's DEPS. + 'target_name': 'webrtc_base', + 'type': 'none', + 'dependencies': [ + 'rtc_base', + ], + }, + { + # The subset of rtc_base approved for use outside of libjingle. + 'target_name': 'rtc_base_approved', + 'type': 'static_library', + 'sources': [ + 'checks.cc', + 'checks.h', +# Moved by mozilla +# 'common.cc', +# 'common.h', + 'exp_filter.cc', + 'exp_filter.h', + 'md5.cc', + 'md5.h', + 'md5digest.h', + 'platform_file.cc', + 'platform_file.h', + 'stringencode.cc', + 'stringencode.h', + 'stringutils.cc', + 'stringutils.h', + 'thread_annotations.h', + 'timeutils.cc', + 'timeutils.h', + ], + 'conditions': [ + ['OS=="mac"', { + 'sources': [ + 'macutils.cc', + 'macutils.h', + ], + }], + ], + }, + { + 'target_name': 'rtc_base', + 'type': 'static_library', + 'dependencies': [ + 'rtc_base_approved', + ], + 'defines': [ + 'FEATURE_ENABLE_SSL', + 'LOGGING=1', + 'USE_WEBRTC_DEV_BRANCH', + ], + 'sources': [ + 'asyncfile.cc', + 'asyncfile.h', + 'asynchttprequest.cc', + 'asynchttprequest.h', + 'asyncinvoker.cc', + 'asyncinvoker.h', + 'asyncinvoker-inl.h', + 'asyncpacketsocket.h', + 'asyncresolverinterface.h', + 'asyncsocket.cc', + 'asyncsocket.h', + 'asynctcpsocket.cc', + 'asynctcpsocket.h', + 'asyncudpsocket.cc', + 'asyncudpsocket.h', + 'atomicops.h', + 'autodetectproxy.cc', + 'autodetectproxy.h', + 'bandwidthsmoother.cc', + 'bandwidthsmoother.h', + 'base64.cc', + 'base64.h', + 'basicdefs.h', + 'basictypes.h', + 'bind.h', +# 'bind.h.pump', + 'buffer.h', + 'bytebuffer.cc', + 'bytebuffer.h', + 'byteorder.h', + 'callback.h', +# 'callback.h.pump', + 'constructormagic.h', + 'common.cc', + 'common.h', + 'cpumonitor.cc', + 'cpumonitor.h', + 'crc32.cc', + 'crc32.h', + 'criticalsection.h', + 'cryptstring.h', + 'dbus.cc', + 'dbus.h', + 'diskcache.cc', + 'diskcache.h', + 'diskcache_win32.cc', + 'diskcache_win32.h', + 'event.cc', + 'event.h', + 'filelock.cc', + 'filelock.h', + 'fileutils.cc', + 'fileutils.h', + 'fileutils_mock.h', + 'firewallsocketserver.cc', + 'firewallsocketserver.h', + 'flags.cc', + 'flags.h', + 'gunit_prod.h', + 'helpers.cc', + 'helpers.h', + 'httpbase.cc', + 'httpbase.h', + 'httpclient.cc', + 'httpclient.h', + 'httpcommon-inl.h', + 'httpcommon.cc', + 'httpcommon.h', + 'httprequest.cc', + 'httprequest.h', + 'httpserver.cc', + 'httpserver.h', + 'ifaddrs-android.cc', + 'ifaddrs-android.h', + 'iosfilesystem.mm', + 'ipaddress.cc', + 'ipaddress.h', + 'json.cc', + 'json.h', + 'latebindingsymboltable.cc', +# 'latebindingsymboltable.cc.def', + 'latebindingsymboltable.h', +# 'latebindingsymboltable.h.def', + 'libdbusglibsymboltable.cc', + 'libdbusglibsymboltable.h', + 'linux.cc', + 'linux.h', + 'linuxfdwalk.c', + 'linuxfdwalk.h', + 'linked_ptr.h', + 'logging.cc', + 'logging.h', + 'macasyncsocket.cc', + 'macasyncsocket.h', + 'maccocoasocketserver.h', + 'maccocoasocketserver.mm', + 'maccocoathreadhelper.h', + 'maccocoathreadhelper.mm', + 'macconversion.cc', + 'macconversion.h', + 'macsocketserver.cc', + 'macsocketserver.h', +# moved by mozilla +# 'macutils.cc', +# 'macutils.h', + 'macwindowpicker.cc', + 'macwindowpicker.h', + 'mathutils.h', + 'messagedigest.cc', + 'messagedigest.h', + 'messagehandler.cc', + 'messagehandler.h', + 'messagequeue.cc', + 'messagequeue.h', + 'multipart.cc', + 'multipart.h', + 'natserver.cc', + 'natserver.h', + 'natsocketfactory.cc', + 'natsocketfactory.h', + 'nattypes.cc', + 'nattypes.h', + 'nethelpers.cc', + 'nethelpers.h', + 'network.cc', + 'network.h', + 'nullsocketserver.h', + 'optionsfile.cc', + 'optionsfile.h', + 'pathutils.cc', + 'pathutils.h', + 'physicalsocketserver.cc', + 'physicalsocketserver.h', + 'posix.cc', + 'posix.h', + 'profiler.cc', + 'profiler.h', + 'proxydetect.cc', + 'proxydetect.h', + 'proxyinfo.cc', + 'proxyinfo.h', + 'proxyserver.cc', + 'proxyserver.h', + 'ratelimiter.cc', + 'ratelimiter.h', + 'ratetracker.cc', + 'ratetracker.h', + 'refcount.h', + 'referencecountedsingletonfactory.h', + 'rollingaccumulator.h', + 'safe_conversions.h', + 'safe_conversions_impl.h', + 'schanneladapter.cc', + 'schanneladapter.h', + 'scoped_autorelease_pool.h', + 'scoped_autorelease_pool.mm', + 'scoped_ptr.h', + 'scoped_ref_ptr.h', + 'scopedptrcollection.h', + 'sec_buffer.h', + 'sha1.cc', + 'sha1.h', + 'sha1digest.h', + 'sharedexclusivelock.cc', + 'sharedexclusivelock.h', + 'signalthread.cc', + 'signalthread.h', + 'sigslot.h', + 'sigslotrepeater.h', + 'socket.h', + 'socketadapters.cc', + 'socketadapters.h', + 'socketaddress.cc', + 'socketaddress.h', + 'socketaddresspair.cc', + 'socketaddresspair.h', + 'socketfactory.h', + 'socketpool.cc', + 'socketpool.h', + 'socketserver.h', + 'socketstream.cc', + 'socketstream.h', + 'ssladapter.cc', + 'ssladapter.h', + 'sslconfig.h', + 'sslfingerprint.cc', + 'sslfingerprint.h', + 'sslidentity.cc', + 'sslidentity.h', + 'sslroots.h', + 'sslsocketfactory.cc', + 'sslsocketfactory.h', + 'sslstreamadapter.cc', + 'sslstreamadapter.h', + 'sslstreamadapterhelper.cc', + 'sslstreamadapterhelper.h', + 'stream.cc', + 'stream.h', + 'stringdigest.h', + 'systeminfo.cc', + 'systeminfo.h', + 'task.cc', + 'task.h', + 'taskparent.cc', + 'taskparent.h', + 'taskrunner.cc', + 'taskrunner.h', + 'testclient.cc', + 'testclient.h', + 'thread.cc', + 'thread.h', + 'thread_checker.h', + 'thread_checker_impl.cc', + 'thread_checker_impl.h', + 'timing.cc', + 'timing.h', + 'transformadapter.cc', + 'transformadapter.h', + 'unixfilesystem.cc', + 'unixfilesystem.h', + 'urlencode.cc', + 'urlencode.h', + 'versionparsing.cc', + 'versionparsing.h', + 'virtualsocketserver.cc', + 'virtualsocketserver.h', + 'win32.cc', + 'win32.h', + 'win32filesystem.cc', + 'win32filesystem.h', + 'win32regkey.cc', + 'win32regkey.h', + 'win32securityerrors.cc', + 'win32socketinit.cc', + 'win32socketinit.h', + 'win32socketserver.cc', + 'win32socketserver.h', + 'win32window.cc', + 'win32window.h', + 'win32windowpicker.cc', + 'win32windowpicker.h', + 'window.h', + 'windowpicker.h', + 'windowpickerfactory.h', + 'winfirewall.cc', + 'winfirewall.h', + 'winping.cc', + 'winping.h', + 'worker.cc', + 'worker.h', + 'x11windowpicker.cc', + 'x11windowpicker.h', + '../overrides/webrtc/base/basictypes.h', + '../overrides/webrtc/base/constructormagic.h', + '../overrides/webrtc/base/logging.cc', + '../overrides/webrtc/base/logging.h', + '../overrides/webrtc/base/win32socketinit.cc', + ], + # TODO(henrike): issue 3307, make rtc_base build without disabling + # these flags. + 'cflags!': [ + '-Wextra', + '-Wall', + ], + 'cflags_cc!': [ + '-Wnon-virtual-dtor', + ], + 'direct_dependent_settings': { + 'cflags_cc!': [ + '-Wnon-virtual-dtor', + ], + 'defines': [ + 'FEATURE_ENABLE_SSL', + ], + }, + 'include_dirs': [ + '../../third_party/jsoncpp/overrides/include', + '../../third_party/jsoncpp/source/include', + ], + 'conditions': [ + ['build_with_chromium==1', { + 'include_dirs': [ + '../overrides', + '../../boringssl/src/include', + ], + 'sources!': [ + 'asyncinvoker.cc', + 'asyncinvoker.h', + 'asyncinvoker-inl.h', + 'asyncresolverinterface.h', + 'atomicops.h', + 'bandwidthsmoother.cc', + 'bandwidthsmoother.h', + 'basictypes.h', + 'bind.h', +# 'bind.h.pump', + 'buffer.h', + 'callback.h', +# 'callback.h.pump', + 'constructormagic.h', + 'dbus.cc', + 'dbus.h', + 'diskcache_win32.cc', + 'diskcache_win32.h', + 'filelock.cc', + 'filelock.h', + 'fileutils_mock.h', + 'genericslot.h', +# 'genericslot.h.pump', + 'httpserver.cc', + 'httpserver.h', + 'json.cc', + 'json.h', + 'latebindingsymboltable.cc', +# 'latebindingsymboltable.cc.def', + 'latebindingsymboltable.h', +# 'latebindingsymboltable.h.def', + 'libdbusglibsymboltable.cc', + 'libdbusglibsymboltable.h', + 'linuxfdwalk.c', + 'linuxfdwalk.h', + 'x11windowpicker.cc', + 'x11windowpicker.h', + 'logging.cc', + 'logging.h', + 'macasyncsocket.cc', + 'macasyncsocket.h', + 'maccocoasocketserver.h', + 'maccocoasocketserver.mm', + 'macsocketserver.cc', + 'macsocketserver.h', + 'macwindowpicker.cc', + 'macwindowpicker.h', + 'mathutils.h', + 'multipart.cc', + 'multipart.h', + 'natserver.cc', + 'natserver.h', + 'natsocketfactory.cc', + 'natsocketfactory.h', + 'nattypes.cc', + 'nattypes.h', + 'optionsfile.cc', + 'optionsfile.h', + 'posix.cc', + 'posix.h', + 'profiler.cc', + 'profiler.h', + 'proxyserver.cc', + 'proxyserver.h', + 'refcount.h', + 'referencecountedsingletonfactory.h', + 'rollingaccumulator.h', + 'safe_conversions.h', + 'safe_conversions_impl.h', + 'scopedptrcollection.h', + 'scoped_ref_ptr.h', + 'sec_buffer.h', + 'sharedexclusivelock.cc', + 'sharedexclusivelock.h', + 'sslconfig.h', + 'sslroots.h', + 'stringdigest.h', + 'testbase64.h', + 'testclient.cc', + 'testclient.h', + 'transformadapter.cc', + 'transformadapter.h', + 'versionparsing.cc', + 'versionparsing.h', + 'virtualsocketserver.cc', + 'virtualsocketserver.h', + 'win32regkey.cc', + 'win32regkey.h', + 'win32socketinit.cc', + 'win32socketinit.h', + 'win32socketserver.cc', + 'win32socketserver.h', + 'window.h', + 'windowpickerfactory.h', + 'windowpicker.h', + ], + 'defines': [ + 'NO_MAIN_THREAD_WRAPPING', + ], + 'direct_dependent_settings': { + 'defines': [ + 'NO_MAIN_THREAD_WRAPPING', + ], + }, + }, { + 'conditions': [ + ['build_json==1', { + 'dependencies': [ + '<(DEPTH)/third_party/jsoncpp/jsoncpp.gyp:jsoncpp', + ], + }, { + 'include_dirs': [ + '<(json_root)', + ], + 'defines': [ + # When defined changes the include path for json.h to where it + # is expected to be when building json outside of the standalone + # build. + 'WEBRTC_EXTERNAL_JSON', + ], + }], + ], + 'sources!': [ + '../overrides/webrtc/base/basictypes.h', + '../overrides/webrtc/base/constructormagic.h', + '../overrides/webrtc/base/win32socketinit.cc', + '../overrides/webrtc/base/logging.cc', + '../overrides/webrtc/base/logging.h', + ], + }], + ['use_openssl==1', { + 'defines': [ + 'SSL_USE_OPENSSL', + 'HAVE_OPENSSL_SSL_H', + ], + 'direct_dependent_settings': { + 'defines': [ + 'SSL_USE_OPENSSL', + 'HAVE_OPENSSL_SSL_H', + ], + }, + 'sources': [ + 'openssl.h', + 'openssladapter.cc', + 'openssladapter.h', + 'openssldigest.cc', + 'openssldigest.h', + 'opensslidentity.cc', + 'opensslidentity.h', + 'opensslstreamadapter.cc', + 'opensslstreamadapter.h', + ], + 'conditions': [ + ['build_ssl==1', { + 'dependencies': [ + '<(DEPTH)/third_party/boringssl/boringssl.gyp:boringssl', + ], + }, { + 'include_dirs': [ + '<(ssl_root)', + ], + }], + ], + }, { + 'sources': [ + 'nssidentity.cc', + 'nssidentity.h', + 'nssstreamadapter.cc', + 'nssstreamadapter.h', + ], + 'conditions': [ + ['use_legacy_ssl_defaults!=1', { + 'defines': [ + 'SSL_USE_NSS', + 'HAVE_NSS_SSL_H', + 'SSL_USE_NSS_RNG', + ], + 'direct_dependent_settings': { + 'defines': [ + 'SSL_USE_NSS', + 'HAVE_NSS_SSL_H', + 'SSL_USE_NSS_RNG', + ], + }, + }], + ['build_ssl==1', { + 'conditions': [ + # On some platforms, the rest of NSS is bundled. On others, + # it's pulled from the system. + ['OS == "mac" or OS == "ios" or OS == "win"', { + 'dependencies': [ + '<(DEPTH)/net/third_party/nss/ssl.gyp:libssl', + '<(DEPTH)/third_party/nss/nss.gyp:nspr', + '<(DEPTH)/third_party/nss/nss.gyp:nss', + ], + }], + ['os_posix == 1 and OS != "mac" and OS != "ios" and OS != "android"', { + 'dependencies': [ + '<(DEPTH)/build/linux/system.gyp:ssl', + ], + }], + ], + }, { + 'include_dirs': [ + '<(ssl_root)', + ], + }], + ], + }], + ['OS == "android"', { + 'link_settings': { + 'libraries': [ + '-llog', + '-lGLESv2', + ], + }, + }, { + 'sources!': [ + 'ifaddrs-android.cc', + 'ifaddrs-android.h', + ], + }], + ['OS=="ios"', { + 'all_dependent_settings': { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-framework Foundation', + '-framework Security', + '-framework SystemConfiguration', + '-framework UIKit', + ], + }, + }, + }], + ['use_x11 == 1', { + 'link_settings': { + 'libraries': [ + '-ldl', + '-lrt', + '-lXext', + '-lX11', + '-lXcomposite', + '-lXrender', + ], + }, + }, { + 'sources!': [ + 'x11windowpicker.cc', + 'x11windowpicker.h', + ], + }], + ['OS=="linux"', { + 'link_settings': { + 'libraries': [ + '-ldl', + '-lrt', + ], + }, + }, { + 'sources!': [ + 'dbus.cc', + 'dbus.h', + 'libdbusglibsymboltable.cc', + 'libdbusglibsymboltable.h', + 'linuxfdwalk.c', + ], + }], + ['OS=="mac"', { + 'all_dependent_settings': { + 'link_settings': { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-framework Cocoa', + '-framework Foundation', + '-framework IOKit', + '-framework Security', + '-framework SystemConfiguration', + ], + }, + }, + }, + 'conditions': [ + ['target_arch=="ia32"', { + 'all_dependent_settings': { + 'link_settings': { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-framework Carbon', + ], + }, + }, + }, + }], + ], + }, { + 'sources!': [ + 'macasyncsocket.cc', + 'macasyncsocket.h', + 'maccocoasocketserver.h', + 'maccocoasocketserver.mm', + 'macconversion.cc', + 'macconversion.h', + 'macsocketserver.cc', + 'macsocketserver.h', + 'macutils.cc', + 'macutils.h', + 'macwindowpicker.cc', + 'macwindowpicker.h', + ], + }], + ['OS=="win"', { + 'link_settings': { + 'libraries': [ + '-lcrypt32.lib', + '-liphlpapi.lib', + '-lsecur32.lib', + ], + }, + # Suppress warnings about WIN32_LEAN_AND_MEAN. + 'msvs_disabled_warnings': [4005, 4703], + 'defines': [ + '_CRT_NONSTDC_NO_DEPRECATE', + ], + }, { + 'sources/': [ + ['exclude', 'win32[a-z0-9]*\\.(h|cc)$'], + ], + 'sources!': [ + 'schanneladapter.cc', + 'schanneladapter.h', + 'winping.cc', + 'winping.h', + 'winfirewall.cc', + 'winfirewall.h', + ], + }], + ['os_posix==0', { + 'sources!': [ + 'latebindingsymboltable.cc', + 'latebindingsymboltable.h', + 'posix.cc', + 'posix.h', + 'unixfilesystem.cc', + 'unixfilesystem.h', + ], + }, { + 'configurations': { + 'Debug_Base': { + 'defines': [ + # Chromium's build/common.gypi defines this for all posix + # _except_ for ios & mac. We want it there as well, e.g. + # because ASSERT and friends trigger off of it. + '_DEBUG', + ], + }, + } + }], + ['OS=="ios" or (OS=="mac" and target_arch!="ia32")', { + 'defines': [ + 'CARBON_DEPRECATED=YES', + ], + }], + ['OS!="ios" and OS!="mac"', { + 'sources!': [ + 'scoped_autorelease_pool.mm', + ], + }], + ['OS!="linux" and OS!="android"', { + 'sources!': [ + 'linux.cc', + 'linux.h', + ], + }], + ], + }, + ], +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/base_tests.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/base_tests.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/base_tests.gyp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/base_tests.gyp 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,166 @@ +# Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. +{ + 'includes': [ '../build/common.gypi', ], + 'targets': [ + { + 'target_name': 'rtc_base_tests_utils', + 'type': 'static_library', + 'sources': [ + 'unittest_main.cc', + # Also use this as a convenient dumping ground for misc files that are + # included by multiple targets below. + 'fakecpumonitor.h', + 'fakenetwork.h', + 'fakesslidentity.h', + 'faketaskrunner.h', + 'gunit.h', + 'testbase64.h', + 'testechoserver.h', + 'testutils.h', + 'win32toolhelp.h', + ], + 'defines': [ + 'GTEST_RELATIVE_PATH', + ], + 'dependencies': [ + 'base.gyp:rtc_base', + '<(DEPTH)/testing/gtest.gyp:gtest', + ], + 'direct_dependent_settings': { + 'defines': [ + 'GTEST_RELATIVE_PATH', + ], + }, + 'export_dependent_settings': [ + '<(DEPTH)/testing/gtest.gyp:gtest', + ], + }, + { + 'target_name': 'rtc_base_tests', + 'type': 'none', + 'direct_dependent_settings': { + 'sources': [ + 'asynchttprequest_unittest.cc', + 'atomicops_unittest.cc', + 'autodetectproxy_unittest.cc', + 'bandwidthsmoother_unittest.cc', + 'base64_unittest.cc', + 'basictypes_unittest.cc', + 'bind_unittest.cc', + 'buffer_unittest.cc', + 'bytebuffer_unittest.cc', + 'byteorder_unittest.cc', + 'callback_unittest.cc', + 'cpumonitor_unittest.cc', + 'crc32_unittest.cc', + 'criticalsection_unittest.cc', + 'event_unittest.cc', + 'exp_filter_unittest.cc', + 'filelock_unittest.cc', + 'fileutils_unittest.cc', + 'helpers_unittest.cc', + 'httpbase_unittest.cc', + 'httpcommon_unittest.cc', + 'httpserver_unittest.cc', + 'ipaddress_unittest.cc', + 'logging_unittest.cc', + 'md5digest_unittest.cc', + 'messagedigest_unittest.cc', + #'messagequeue_unittest.cc', + 'multipart_unittest.cc', + 'nat_unittest.cc', + 'network_unittest.cc', + 'nullsocketserver_unittest.cc', + 'optionsfile_unittest.cc', + 'pathutils_unittest.cc', + 'physicalsocketserver_unittest.cc', + 'profiler_unittest.cc', + 'proxy_unittest.cc', + 'proxydetect_unittest.cc', + 'ratelimiter_unittest.cc', + 'ratetracker_unittest.cc', + 'referencecountedsingletonfactory_unittest.cc', + 'rollingaccumulator_unittest.cc', + 'scopedptrcollection_unittest.cc', + 'sha1digest_unittest.cc', + 'sharedexclusivelock_unittest.cc', + 'signalthread_unittest.cc', + 'sigslot_unittest.cc', + 'sigslottester.h', + 'sigslottester.h.pump', + 'socket_unittest.cc', + 'socket_unittest.h', + 'socketaddress_unittest.cc', + 'stream_unittest.cc', + 'stringencode_unittest.cc', + 'stringutils_unittest.cc', + # TODO(ronghuawu): Reenable this test. + # 'systeminfo_unittest.cc', + 'task_unittest.cc', + 'testclient_unittest.cc', + 'thread_checker_unittest.cc', + 'thread_unittest.cc', + 'timeutils_unittest.cc', + 'urlencode_unittest.cc', + 'versionparsing_unittest.cc', + 'virtualsocket_unittest.cc', + # TODO(ronghuawu): Reenable this test. + # 'windowpicker_unittest.cc', + ], + 'conditions': [ + ['OS=="linux"', { + 'sources': [ + 'latebindingsymboltable_unittest.cc', + # TODO(ronghuawu): Reenable this test. + # 'linux_unittest.cc', + 'linuxfdwalk_unittest.cc', + ], + }], + ['OS=="win"', { + 'sources': [ + 'win32_unittest.cc', + 'win32regkey_unittest.cc', + 'win32socketserver_unittest.cc', + 'win32toolhelp_unittest.cc', + 'win32window_unittest.cc', + 'win32windowpicker_unittest.cc', + 'winfirewall_unittest.cc', + ], + 'sources!': [ + # TODO(ronghuawu): Fix TestUdpReadyToSendIPv6 on windows bot + # then reenable these tests. + 'physicalsocketserver_unittest.cc', + 'socket_unittest.cc', + 'win32socketserver_unittest.cc', + 'win32windowpicker_unittest.cc', + ], + }], + ['OS=="mac"', { + 'sources': [ + 'macsocketserver_unittest.cc', + 'macutils_unittest.cc', + ], + }], + ['os_posix==1', { + 'sources': [ + 'ssladapter_unittest.cc', + 'sslidentity_unittest.cc', + 'sslstreamadapter_unittest.cc', + ], + }], + ['OS=="ios" or (OS=="mac" and target_arch!="ia32")', { + 'defines': [ + 'CARBON_DEPRECATED=YES', + ], + }], + ], # conditions + }, + }, + ], +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/basicdefs.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/basicdefs.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/basicdefs.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/basicdefs.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_BASICDEFS_H_ +#define WEBRTC_BASE_BASICDEFS_H_ + +#if HAVE_CONFIG_H +#include "config.h" // NOLINT +#endif + +#define ARRAY_SIZE(x) (static_cast(sizeof(x) / sizeof(x[0]))) + +#endif // WEBRTC_BASE_BASICDEFS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/basictypes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/basictypes.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/basictypes.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/basictypes.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,134 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_BASICTYPES_H_ +#define WEBRTC_BASE_BASICTYPES_H_ + +#include // for NULL, size_t + +#if !(defined(_MSC_VER) && (_MSC_VER < 1600)) +#include // for uintptr_t +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" // NOLINT +#endif + +#include "webrtc/base/constructormagic.h" + +#if !defined(INT_TYPES_DEFINED) +#define INT_TYPES_DEFINED +#ifdef COMPILER_MSVC +typedef unsigned __int64 uint64; +typedef __int64 int64; +#ifndef INT64_C +#define INT64_C(x) x ## I64 +#endif +#ifndef UINT64_C +#define UINT64_C(x) x ## UI64 +#endif +#define INT64_F "I64" +#else // COMPILER_MSVC +// On Mac OS X, cssmconfig.h defines uint64 as uint64_t +// TODO(fbarchard): Use long long for compatibility with chromium on BSD/OSX. +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +typedef uint64_t uint64; +typedef int64_t int64; +#ifndef INT64_C +#define INT64_C(x) x ## LL +#endif +#ifndef UINT64_C +#define UINT64_C(x) x ## ULL +#endif +#define INT64_F "l" +#elif defined(__LP64__) +typedef unsigned long uint64; // NOLINT +typedef long int64; // NOLINT +#ifndef INT64_C +#define INT64_C(x) x ## L +#endif +#ifndef UINT64_C +#define UINT64_C(x) x ## UL +#endif +#define INT64_F "l" +#else // __LP64__ +typedef unsigned long long uint64; // NOLINT +typedef long long int64; // NOLINT +#ifndef INT64_C +#define INT64_C(x) x ## LL +#endif +#ifndef UINT64_C +#define UINT64_C(x) x ## ULL +#endif +#define INT64_F "ll" +#endif // __LP64__ +#endif // COMPILER_MSVC +typedef unsigned int uint32; +typedef int int32; +typedef unsigned short uint16; // NOLINT +typedef short int16; // NOLINT +typedef unsigned char uint8; +typedef signed char int8; +#endif // INT_TYPES_DEFINED + +// Detect compiler is for x86 or x64. +#if defined(__x86_64__) || defined(_M_X64) || \ + defined(__i386__) || defined(_M_IX86) +#define CPU_X86 1 +#endif +// Detect compiler is for arm. +#if defined(__arm__) || defined(_M_ARM) +#define CPU_ARM 1 +#endif +#if defined(CPU_X86) && defined(CPU_ARM) +#error CPU_X86 and CPU_ARM both defined. +#endif +#if !defined(ARCH_CPU_BIG_ENDIAN) && !defined(ARCH_CPU_LITTLE_ENDIAN) +// x86, arm or GCC provided __BYTE_ORDER__ macros +#if CPU_X86 || CPU_ARM || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +#define ARCH_CPU_LITTLE_ENDIAN +#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define ARCH_CPU_BIG_ENDIAN +#else +#error ARCH_CPU_BIG_ENDIAN or ARCH_CPU_LITTLE_ENDIAN should be defined. +#endif +#endif +#if defined(ARCH_CPU_BIG_ENDIAN) && defined(ARCH_CPU_LITTLE_ENDIAN) +#error ARCH_CPU_BIG_ENDIAN and ARCH_CPU_LITTLE_ENDIAN both defined. +#endif + +#if defined(WEBRTC_WIN) +typedef int socklen_t; +#endif + +// The following only works for C++ +#ifdef __cplusplus +namespace rtc { + template inline T _min(T a, T b) { return (a > b) ? b : a; } + template inline T _max(T a, T b) { return (a < b) ? b : a; } + + // For wait functions that take a number of milliseconds, kForever indicates + // unlimited time. + const int kForever = -1; +} + +#define ALIGNP(p, t) \ + (reinterpret_cast(((reinterpret_cast(p) + \ + ((t) - 1)) & ~((t) - 1)))) +#define RTC_IS_ALIGNED(p, a) (!((uintptr_t)(p) & ((a) - 1))) + +// Use these to declare and define a static local variable (static T;) so that +// it is leaked so that its destructors are not called at exit. +#define LIBJINGLE_DEFINE_STATIC_LOCAL(type, name, arguments) \ + static type& name = *new type arguments + +#endif // __cplusplus +#endif // WEBRTC_BASE_BASICTYPES_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/basictypes_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/basictypes_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/basictypes_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/basictypes_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,75 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/basictypes.h" + +#include "webrtc/base/gunit.h" + +namespace rtc { + +TEST(BasicTypesTest, Endian) { + uint16 v16 = 0x1234u; + uint8 first_byte = *reinterpret_cast(&v16); +#if defined(ARCH_CPU_LITTLE_ENDIAN) + EXPECT_EQ(0x34u, first_byte); +#elif defined(ARCH_CPU_BIG_ENDIAN) + EXPECT_EQ(0x12u, first_byte); +#endif +} + +TEST(BasicTypesTest, SizeOfTypes) { + int8 i8 = -1; + uint8 u8 = 1u; + int16 i16 = -1; + uint16 u16 = 1u; + int32 i32 = -1; + uint32 u32 = 1u; + int64 i64 = -1; + uint64 u64 = 1u; + EXPECT_EQ(1u, sizeof(i8)); + EXPECT_EQ(1u, sizeof(u8)); + EXPECT_EQ(2u, sizeof(i16)); + EXPECT_EQ(2u, sizeof(u16)); + EXPECT_EQ(4u, sizeof(i32)); + EXPECT_EQ(4u, sizeof(u32)); + EXPECT_EQ(8u, sizeof(i64)); + EXPECT_EQ(8u, sizeof(u64)); + EXPECT_GT(0, i8); + EXPECT_LT(0u, u8); + EXPECT_GT(0, i16); + EXPECT_LT(0u, u16); + EXPECT_GT(0, i32); + EXPECT_LT(0u, u32); + EXPECT_GT(0, i64); + EXPECT_LT(0u, u64); +} + +TEST(BasicTypesTest, SizeOfConstants) { + EXPECT_EQ(8u, sizeof(INT64_C(0))); + EXPECT_EQ(8u, sizeof(UINT64_C(0))); + EXPECT_EQ(8u, sizeof(INT64_C(0x1234567887654321))); + EXPECT_EQ(8u, sizeof(UINT64_C(0x8765432112345678))); +} + +// Test CPU_ macros +#if !defined(CPU_ARM) && defined(__arm__) +#error expected CPU_ARM to be defined. +#endif +#if !defined(CPU_X86) && (defined(WEBRTC_WIN) || defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)) +#error expected CPU_X86 to be defined. +#endif +#if !defined(ARCH_CPU_LITTLE_ENDIAN) && \ + (defined(WEBRTC_WIN) || defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) || defined(CPU_X86)) +#error expected ARCH_CPU_LITTLE_ENDIAN to be defined. +#endif + +// TODO(fbarchard): Test all macros in basictypes.h + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bind.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bind.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bind.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bind.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,587 @@ +// This file was GENERATED by command: +// pump.py bind.h.pump +// DO NOT EDIT BY HAND!!! + +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// To generate bind.h from bind.h.pump, execute: +// /home/build/google3/third_party/gtest/scripts/pump.py bind.h.pump + +// Bind() is an overloaded function that converts method calls into function +// objects (aka functors). It captures any arguments to the method by value +// when Bind is called, producing a stateful, nullary function object. Care +// should be taken about the lifetime of objects captured by Bind(); the +// returned functor knows nothing about the lifetime of the method's object or +// any arguments passed by pointer, and calling the functor with a destroyed +// object will surely do bad things. +// +// Example usage: +// struct Foo { +// int Test1() { return 42; } +// int Test2() const { return 52; } +// int Test3(int x) { return x*x; } +// float Test4(int x, float y) { return x + y; } +// }; +// +// int main() { +// Foo foo; +// cout << rtc::Bind(&Foo::Test1, &foo)() << endl; +// cout << rtc::Bind(&Foo::Test2, &foo)() << endl; +// cout << rtc::Bind(&Foo::Test3, &foo, 3)() << endl; +// cout << rtc::Bind(&Foo::Test4, &foo, 7, 8.5f)() << endl; +// } + +#ifndef WEBRTC_BASE_BIND_H_ +#define WEBRTC_BASE_BIND_H_ + +#define NONAME + +namespace rtc { +namespace detail { +// This is needed because the template parameters in Bind can't be resolved +// if they're used both as parameters of the function pointer type and as +// parameters to Bind itself: the function pointer parameters are exact +// matches to the function prototype, but the parameters to bind have +// references stripped. This trick allows the compiler to dictate the Bind +// parameter types rather than deduce them. +template struct identity { typedef T type; }; +} // namespace detail + +template +class MethodFunctor0 { + public: + MethodFunctor0(MethodT method, ObjectT* object) + : method_(method), object_(object) {} + R operator()() const { + return (object_->*method_)(); } + private: + MethodT method_; + ObjectT* object_; +}; + +template +class Functor0 { + public: + explicit Functor0(const FunctorT& functor) + : functor_(functor) {} + R operator()() const { + return functor_(); } + private: + FunctorT functor_; +}; + + +#define FP_T(x) R (ObjectT::*x)() + +template +MethodFunctor0 +Bind(FP_T(method), ObjectT* object) { + return MethodFunctor0( + method, object); +} + +#undef FP_T +#define FP_T(x) R (ObjectT::*x)() const + +template +MethodFunctor0 +Bind(FP_T(method), const ObjectT* object) { + return MethodFunctor0( + method, object); +} + +#undef FP_T +#define FP_T(x) R (*x)() + +template +Functor0 +Bind(FP_T(function)) { + return Functor0( + function); +} + +#undef FP_T + +template +class MethodFunctor1 { + public: + MethodFunctor1(MethodT method, ObjectT* object, + P1 p1) + : method_(method), object_(object), + p1_(p1) {} + R operator()() const { + return (object_->*method_)(p1_); } + private: + MethodT method_; + ObjectT* object_; + P1 p1_; +}; + +template +class Functor1 { + public: + Functor1(const FunctorT& functor, P1 p1) + : functor_(functor), + p1_(p1) {} + R operator()() const { + return functor_(p1_); } + private: + FunctorT functor_; + P1 p1_; +}; + + +#define FP_T(x) R (ObjectT::*x)(P1) + +template +MethodFunctor1 +Bind(FP_T(method), ObjectT* object, + typename detail::identity::type p1) { + return MethodFunctor1( + method, object, p1); +} + +#undef FP_T +#define FP_T(x) R (ObjectT::*x)(P1) const + +template +MethodFunctor1 +Bind(FP_T(method), const ObjectT* object, + typename detail::identity::type p1) { + return MethodFunctor1( + method, object, p1); +} + +#undef FP_T +#define FP_T(x) R (*x)(P1) + +template +Functor1 +Bind(FP_T(function), + typename detail::identity::type p1) { + return Functor1( + function, p1); +} + +#undef FP_T + +template +class MethodFunctor2 { + public: + MethodFunctor2(MethodT method, ObjectT* object, + P1 p1, + P2 p2) + : method_(method), object_(object), + p1_(p1), + p2_(p2) {} + R operator()() const { + return (object_->*method_)(p1_, p2_); } + private: + MethodT method_; + ObjectT* object_; + P1 p1_; + P2 p2_; +}; + +template +class Functor2 { + public: + Functor2(const FunctorT& functor, P1 p1, P2 p2) + : functor_(functor), + p1_(p1), + p2_(p2) {} + R operator()() const { + return functor_(p1_, p2_); } + private: + FunctorT functor_; + P1 p1_; + P2 p2_; +}; + + +#define FP_T(x) R (ObjectT::*x)(P1, P2) + +template +MethodFunctor2 +Bind(FP_T(method), ObjectT* object, + typename detail::identity::type p1, + typename detail::identity::type p2) { + return MethodFunctor2( + method, object, p1, p2); +} + +#undef FP_T +#define FP_T(x) R (ObjectT::*x)(P1, P2) const + +template +MethodFunctor2 +Bind(FP_T(method), const ObjectT* object, + typename detail::identity::type p1, + typename detail::identity::type p2) { + return MethodFunctor2( + method, object, p1, p2); +} + +#undef FP_T +#define FP_T(x) R (*x)(P1, P2) + +template +Functor2 +Bind(FP_T(function), + typename detail::identity::type p1, + typename detail::identity::type p2) { + return Functor2( + function, p1, p2); +} + +#undef FP_T + +template +class MethodFunctor3 { + public: + MethodFunctor3(MethodT method, ObjectT* object, + P1 p1, + P2 p2, + P3 p3) + : method_(method), object_(object), + p1_(p1), + p2_(p2), + p3_(p3) {} + R operator()() const { + return (object_->*method_)(p1_, p2_, p3_); } + private: + MethodT method_; + ObjectT* object_; + P1 p1_; + P2 p2_; + P3 p3_; +}; + +template +class Functor3 { + public: + Functor3(const FunctorT& functor, P1 p1, P2 p2, P3 p3) + : functor_(functor), + p1_(p1), + p2_(p2), + p3_(p3) {} + R operator()() const { + return functor_(p1_, p2_, p3_); } + private: + FunctorT functor_; + P1 p1_; + P2 p2_; + P3 p3_; +}; + + +#define FP_T(x) R (ObjectT::*x)(P1, P2, P3) + +template +MethodFunctor3 +Bind(FP_T(method), ObjectT* object, + typename detail::identity::type p1, + typename detail::identity::type p2, + typename detail::identity::type p3) { + return MethodFunctor3( + method, object, p1, p2, p3); +} + +#undef FP_T +#define FP_T(x) R (ObjectT::*x)(P1, P2, P3) const + +template +MethodFunctor3 +Bind(FP_T(method), const ObjectT* object, + typename detail::identity::type p1, + typename detail::identity::type p2, + typename detail::identity::type p3) { + return MethodFunctor3( + method, object, p1, p2, p3); +} + +#undef FP_T +#define FP_T(x) R (*x)(P1, P2, P3) + +template +Functor3 +Bind(FP_T(function), + typename detail::identity::type p1, + typename detail::identity::type p2, + typename detail::identity::type p3) { + return Functor3( + function, p1, p2, p3); +} + +#undef FP_T + +template +class MethodFunctor4 { + public: + MethodFunctor4(MethodT method, ObjectT* object, + P1 p1, + P2 p2, + P3 p3, + P4 p4) + : method_(method), object_(object), + p1_(p1), + p2_(p2), + p3_(p3), + p4_(p4) {} + R operator()() const { + return (object_->*method_)(p1_, p2_, p3_, p4_); } + private: + MethodT method_; + ObjectT* object_; + P1 p1_; + P2 p2_; + P3 p3_; + P4 p4_; +}; + +template +class Functor4 { + public: + Functor4(const FunctorT& functor, P1 p1, P2 p2, P3 p3, P4 p4) + : functor_(functor), + p1_(p1), + p2_(p2), + p3_(p3), + p4_(p4) {} + R operator()() const { + return functor_(p1_, p2_, p3_, p4_); } + private: + FunctorT functor_; + P1 p1_; + P2 p2_; + P3 p3_; + P4 p4_; +}; + + +#define FP_T(x) R (ObjectT::*x)(P1, P2, P3, P4) + +template +MethodFunctor4 +Bind(FP_T(method), ObjectT* object, + typename detail::identity::type p1, + typename detail::identity::type p2, + typename detail::identity::type p3, + typename detail::identity::type p4) { + return MethodFunctor4( + method, object, p1, p2, p3, p4); +} + +#undef FP_T +#define FP_T(x) R (ObjectT::*x)(P1, P2, P3, P4) const + +template +MethodFunctor4 +Bind(FP_T(method), const ObjectT* object, + typename detail::identity::type p1, + typename detail::identity::type p2, + typename detail::identity::type p3, + typename detail::identity::type p4) { + return MethodFunctor4( + method, object, p1, p2, p3, p4); +} + +#undef FP_T +#define FP_T(x) R (*x)(P1, P2, P3, P4) + +template +Functor4 +Bind(FP_T(function), + typename detail::identity::type p1, + typename detail::identity::type p2, + typename detail::identity::type p3, + typename detail::identity::type p4) { + return Functor4( + function, p1, p2, p3, p4); +} + +#undef FP_T + +template +class MethodFunctor5 { + public: + MethodFunctor5(MethodT method, ObjectT* object, + P1 p1, + P2 p2, + P3 p3, + P4 p4, + P5 p5) + : method_(method), object_(object), + p1_(p1), + p2_(p2), + p3_(p3), + p4_(p4), + p5_(p5) {} + R operator()() const { + return (object_->*method_)(p1_, p2_, p3_, p4_, p5_); } + private: + MethodT method_; + ObjectT* object_; + P1 p1_; + P2 p2_; + P3 p3_; + P4 p4_; + P5 p5_; +}; + +template +class Functor5 { + public: + Functor5(const FunctorT& functor, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : functor_(functor), + p1_(p1), + p2_(p2), + p3_(p3), + p4_(p4), + p5_(p5) {} + R operator()() const { + return functor_(p1_, p2_, p3_, p4_, p5_); } + private: + FunctorT functor_; + P1 p1_; + P2 p2_; + P3 p3_; + P4 p4_; + P5 p5_; +}; + + +#define FP_T(x) R (ObjectT::*x)(P1, P2, P3, P4, P5) + +template +MethodFunctor5 +Bind(FP_T(method), ObjectT* object, + typename detail::identity::type p1, + typename detail::identity::type p2, + typename detail::identity::type p3, + typename detail::identity::type p4, + typename detail::identity::type p5) { + return MethodFunctor5( + method, object, p1, p2, p3, p4, p5); +} + +#undef FP_T +#define FP_T(x) R (ObjectT::*x)(P1, P2, P3, P4, P5) const + +template +MethodFunctor5 +Bind(FP_T(method), const ObjectT* object, + typename detail::identity::type p1, + typename detail::identity::type p2, + typename detail::identity::type p3, + typename detail::identity::type p4, + typename detail::identity::type p5) { + return MethodFunctor5( + method, object, p1, p2, p3, p4, p5); +} + +#undef FP_T +#define FP_T(x) R (*x)(P1, P2, P3, P4, P5) + +template +Functor5 +Bind(FP_T(function), + typename detail::identity::type p1, + typename detail::identity::type p2, + typename detail::identity::type p3, + typename detail::identity::type p4, + typename detail::identity::type p5) { + return Functor5( + function, p1, p2, p3, p4, p5); +} + +#undef FP_T + +} // namespace rtc + +#undef NONAME + +#endif // WEBRTC_BASE_BIND_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bind.h.pump thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bind.h.pump --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bind.h.pump 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bind.h.pump 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,138 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// To generate bind.h from bind.h.pump, execute: +// /home/build/google3/third_party/gtest/scripts/pump.py bind.h.pump + +// Bind() is an overloaded function that converts method calls into function +// objects (aka functors). It captures any arguments to the method by value +// when Bind is called, producing a stateful, nullary function object. Care +// should be taken about the lifetime of objects captured by Bind(); the +// returned functor knows nothing about the lifetime of the method's object or +// any arguments passed by pointer, and calling the functor with a destroyed +// object will surely do bad things. +// +// Example usage: +// struct Foo { +// int Test1() { return 42; } +// int Test2() const { return 52; } +// int Test3(int x) { return x*x; } +// float Test4(int x, float y) { return x + y; } +// }; +// +// int main() { +// Foo foo; +// cout << rtc::Bind(&Foo::Test1, &foo)() << endl; +// cout << rtc::Bind(&Foo::Test2, &foo)() << endl; +// cout << rtc::Bind(&Foo::Test3, &foo, 3)() << endl; +// cout << rtc::Bind(&Foo::Test4, &foo, 7, 8.5f)() << endl; +// } + +#ifndef WEBRTC_BASE_BIND_H_ +#define WEBRTC_BASE_BIND_H_ + +#define NONAME + +namespace rtc { +namespace detail { +// This is needed because the template parameters in Bind can't be resolved +// if they're used both as parameters of the function pointer type and as +// parameters to Bind itself: the function pointer parameters are exact +// matches to the function prototype, but the parameters to bind have +// references stripped. This trick allows the compiler to dictate the Bind +// parameter types rather than deduce them. +template struct identity { typedef T type; }; +} // namespace detail + +$var n = 5 +$range i 0..n +$for i [[ +$range j 1..i + +template +class MethodFunctor$i { + public: + MethodFunctor$i(MethodT method, ObjectT* object$for j [[, + P$j p$j]]) + : method_(method), object_(object)$for j [[, + p$(j)_(p$j)]] {} + R operator()() const { + return (object_->*method_)($for j , [[p$(j)_]]); } + private: + MethodT method_; + ObjectT* object_;$for j [[ + + P$j p$(j)_;]] + +}; + +template +class Functor$i { + public: + $if i == 0 [[explicit ]] +Functor$i(const FunctorT& functor$for j [[, P$j p$j]]) + : functor_(functor)$for j [[, + p$(j)_(p$j)]] {} + R operator()() const { + return functor_($for j , [[p$(j)_]]); } + private: + FunctorT functor_;$for j [[ + + P$j p$(j)_;]] + +}; + + +#define FP_T(x) R (ObjectT::*x)($for j , [[P$j]]) + +template +MethodFunctor$i +Bind(FP_T(method), ObjectT* object$for j [[, + typename detail::identity::type p$j]]) { + return MethodFunctor$i( + method, object$for j [[, p$j]]); +} + +#undef FP_T +#define FP_T(x) R (ObjectT::*x)($for j , [[P$j]]) const + +template +MethodFunctor$i +Bind(FP_T(method), const ObjectT* object$for j [[, + typename detail::identity::type p$j]]) { + return MethodFunctor$i( + method, object$for j [[, p$j]]); +} + +#undef FP_T +#define FP_T(x) R (*x)($for j , [[P$j]]) + +template +Functor$i +Bind(FP_T(function)$for j [[, + typename detail::identity::type p$j]]) { + return Functor$i( + function$for j [[, p$j]]); +} + +#undef FP_T + +]] + +} // namespace rtc + +#undef NONAME + +#endif // WEBRTC_BASE_BIND_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bind_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bind_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bind_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bind_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,67 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/bind.h" +#include "webrtc/base/gunit.h" + +namespace rtc { + +namespace { + +struct MethodBindTester { + void NullaryVoid() { ++call_count; } + int NullaryInt() { ++call_count; return 1; } + int NullaryConst() const { ++call_count; return 2; } + void UnaryVoid(int dummy) { ++call_count; } + template T Identity(T value) { ++call_count; return value; } + int UnaryByRef(int& value) const { ++call_count; return ++value; } // NOLINT + int Multiply(int a, int b) const { ++call_count; return a * b; } + mutable int call_count; +}; + +int Return42() { return 42; } +int Negate(int a) { return -a; } +int Multiply(int a, int b) { return a * b; } + +} // namespace + +TEST(BindTest, BindToMethod) { + MethodBindTester object = {0}; + EXPECT_EQ(0, object.call_count); + Bind(&MethodBindTester::NullaryVoid, &object)(); + EXPECT_EQ(1, object.call_count); + EXPECT_EQ(1, Bind(&MethodBindTester::NullaryInt, &object)()); + EXPECT_EQ(2, object.call_count); + EXPECT_EQ(2, Bind(&MethodBindTester::NullaryConst, + static_cast(&object))()); + EXPECT_EQ(3, object.call_count); + Bind(&MethodBindTester::UnaryVoid, &object, 5)(); + EXPECT_EQ(4, object.call_count); + EXPECT_EQ(100, Bind(&MethodBindTester::Identity, &object, 100)()); + EXPECT_EQ(5, object.call_count); + const std::string string_value("test string"); + EXPECT_EQ(string_value, Bind(&MethodBindTester::Identity, + &object, string_value)()); + EXPECT_EQ(6, object.call_count); + int value = 11; + EXPECT_EQ(12, Bind(&MethodBindTester::UnaryByRef, &object, value)()); + EXPECT_EQ(12, value); + EXPECT_EQ(7, object.call_count); + EXPECT_EQ(56, Bind(&MethodBindTester::Multiply, &object, 7, 8)()); + EXPECT_EQ(8, object.call_count); +} + +TEST(BindTest, BindToFunction) { + EXPECT_EQ(42, Bind(&Return42)()); + EXPECT_EQ(3, Bind(&Negate, -3)()); + EXPECT_EQ(56, Bind(&Multiply, 8, 7)()); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/buffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/buffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/buffer.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/buffer.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,102 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_BUFFER_H_ +#define WEBRTC_BASE_BUFFER_H_ + +#include + +#include "webrtc/base/scoped_ptr.h" + +namespace rtc { + +// Basic buffer class, can be grown and shrunk dynamically. +// Unlike std::string/vector, does not initialize data when expanding capacity. +class Buffer { + public: + Buffer() { + Construct(NULL, 0, 0); + } + Buffer(const void* data, size_t length) { + Construct(data, length, length); + } + Buffer(const void* data, size_t length, size_t capacity) { + Construct(data, length, capacity); + } + Buffer(const Buffer& buf) { + Construct(buf.data(), buf.length(), buf.length()); + } + + const char* data() const { return data_.get(); } + char* data() { return data_.get(); } + // TODO: should this be size(), like STL? + size_t length() const { return length_; } + size_t capacity() const { return capacity_; } + + Buffer& operator=(const Buffer& buf) { + if (&buf != this) { + Construct(buf.data(), buf.length(), buf.length()); + } + return *this; + } + bool operator==(const Buffer& buf) const { + return (length_ == buf.length() && + memcmp(data_.get(), buf.data(), length_) == 0); + } + bool operator!=(const Buffer& buf) const { + return !operator==(buf); + } + + void SetData(const void* data, size_t length) { + ASSERT(data != NULL || length == 0); + SetLength(length); + memcpy(data_.get(), data, length); + } + void AppendData(const void* data, size_t length) { + ASSERT(data != NULL || length == 0); + size_t old_length = length_; + SetLength(length_ + length); + memcpy(data_.get() + old_length, data, length); + } + void SetLength(size_t length) { + SetCapacity(length); + length_ = length; + } + void SetCapacity(size_t capacity) { + if (capacity > capacity_) { + rtc::scoped_ptr data(new char[capacity]); + memcpy(data.get(), data_.get(), length_); + data_.swap(data); + capacity_ = capacity; + } + } + + void TransferTo(Buffer* buf) { + ASSERT(buf != NULL); + buf->data_.reset(data_.release()); + buf->length_ = length_; + buf->capacity_ = capacity_; + Construct(NULL, 0, 0); + } + + protected: + void Construct(const void* data, size_t length, size_t capacity) { + data_.reset(new char[capacity_ = capacity]); + SetData(data, length); + } + + scoped_ptr data_; + size_t length_; + size_t capacity_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_BUFFER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/buffer_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/buffer_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/buffer_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/buffer_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,143 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/buffer.h" +#include "webrtc/base/gunit.h" + +namespace rtc { + +static const char kTestData[] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF +}; + +TEST(BufferTest, TestConstructDefault) { + Buffer buf; + EXPECT_EQ(0U, buf.length()); + EXPECT_EQ(0U, buf.capacity()); + EXPECT_EQ(Buffer(), buf); +} + +TEST(BufferTest, TestConstructEmptyWithCapacity) { + Buffer buf(NULL, 0, 256U); + EXPECT_EQ(0U, buf.length()); + EXPECT_EQ(256U, buf.capacity()); + EXPECT_EQ(Buffer(), buf); +} + +TEST(BufferTest, TestConstructData) { + Buffer buf(kTestData, sizeof(kTestData)); + EXPECT_EQ(sizeof(kTestData), buf.length()); + EXPECT_EQ(sizeof(kTestData), buf.capacity()); + EXPECT_EQ(0, memcmp(buf.data(), kTestData, sizeof(kTestData))); + EXPECT_EQ(Buffer(kTestData, sizeof(kTestData)), buf); +} + +TEST(BufferTest, TestConstructDataWithCapacity) { + Buffer buf(kTestData, sizeof(kTestData), 256U); + EXPECT_EQ(sizeof(kTestData), buf.length()); + EXPECT_EQ(256U, buf.capacity()); + EXPECT_EQ(0, memcmp(buf.data(), kTestData, sizeof(kTestData))); + EXPECT_EQ(Buffer(kTestData, sizeof(kTestData)), buf); +} + +TEST(BufferTest, TestConstructCopy) { + Buffer buf1(kTestData, sizeof(kTestData), 256), buf2(buf1); + EXPECT_EQ(sizeof(kTestData), buf2.length()); + EXPECT_EQ(sizeof(kTestData), buf2.capacity()); // capacity isn't copied + EXPECT_EQ(0, memcmp(buf2.data(), kTestData, sizeof(kTestData))); + EXPECT_EQ(buf1, buf2); +} + +TEST(BufferTest, TestAssign) { + Buffer buf1, buf2(kTestData, sizeof(kTestData), 256); + EXPECT_NE(buf1, buf2); + buf1 = buf2; + EXPECT_EQ(sizeof(kTestData), buf1.length()); + EXPECT_EQ(sizeof(kTestData), buf1.capacity()); // capacity isn't copied + EXPECT_EQ(0, memcmp(buf1.data(), kTestData, sizeof(kTestData))); + EXPECT_EQ(buf1, buf2); +} + +TEST(BufferTest, TestSetData) { + Buffer buf; + buf.SetData(kTestData, sizeof(kTestData)); + EXPECT_EQ(sizeof(kTestData), buf.length()); + EXPECT_EQ(sizeof(kTestData), buf.capacity()); + EXPECT_EQ(0, memcmp(buf.data(), kTestData, sizeof(kTestData))); +} + +TEST(BufferTest, TestAppendData) { + Buffer buf(kTestData, sizeof(kTestData)); + buf.AppendData(kTestData, sizeof(kTestData)); + EXPECT_EQ(2 * sizeof(kTestData), buf.length()); + EXPECT_EQ(2 * sizeof(kTestData), buf.capacity()); + EXPECT_EQ(0, memcmp(buf.data(), kTestData, sizeof(kTestData))); + EXPECT_EQ(0, memcmp(buf.data() + sizeof(kTestData), + kTestData, sizeof(kTestData))); +} + +TEST(BufferTest, TestSetLengthSmaller) { + Buffer buf; + buf.SetData(kTestData, sizeof(kTestData)); + buf.SetLength(sizeof(kTestData) / 2); + EXPECT_EQ(sizeof(kTestData) / 2, buf.length()); + EXPECT_EQ(sizeof(kTestData), buf.capacity()); + EXPECT_EQ(0, memcmp(buf.data(), kTestData, sizeof(kTestData) / 2)); +} + +TEST(BufferTest, TestSetLengthLarger) { + Buffer buf; + buf.SetData(kTestData, sizeof(kTestData)); + buf.SetLength(sizeof(kTestData) * 2); + EXPECT_EQ(sizeof(kTestData) * 2, buf.length()); + EXPECT_EQ(sizeof(kTestData) * 2, buf.capacity()); + EXPECT_EQ(0, memcmp(buf.data(), kTestData, sizeof(kTestData))); +} + +TEST(BufferTest, TestSetCapacitySmaller) { + Buffer buf; + buf.SetData(kTestData, sizeof(kTestData)); + buf.SetCapacity(sizeof(kTestData) / 2); // should be ignored + EXPECT_EQ(sizeof(kTestData), buf.length()); + EXPECT_EQ(sizeof(kTestData), buf.capacity()); + EXPECT_EQ(0, memcmp(buf.data(), kTestData, sizeof(kTestData))); +} + +TEST(BufferTest, TestSetCapacityLarger) { + Buffer buf(kTestData, sizeof(kTestData)); + buf.SetCapacity(sizeof(kTestData) * 2); + EXPECT_EQ(sizeof(kTestData), buf.length()); + EXPECT_EQ(sizeof(kTestData) * 2, buf.capacity()); + EXPECT_EQ(0, memcmp(buf.data(), kTestData, sizeof(kTestData))); +} + +TEST(BufferTest, TestSetCapacityThenSetLength) { + Buffer buf(kTestData, sizeof(kTestData)); + buf.SetCapacity(sizeof(kTestData) * 4); + memcpy(buf.data() + sizeof(kTestData), kTestData, sizeof(kTestData)); + buf.SetLength(sizeof(kTestData) * 2); + EXPECT_EQ(sizeof(kTestData) * 2, buf.length()); + EXPECT_EQ(sizeof(kTestData) * 4, buf.capacity()); + EXPECT_EQ(0, memcmp(buf.data(), kTestData, sizeof(kTestData))); + EXPECT_EQ(0, memcmp(buf.data() + sizeof(kTestData), + kTestData, sizeof(kTestData))); +} + +TEST(BufferTest, TestTransfer) { + Buffer buf1(kTestData, sizeof(kTestData), 256U), buf2; + buf1.TransferTo(&buf2); + EXPECT_EQ(0U, buf1.length()); + EXPECT_EQ(0U, buf1.capacity()); + EXPECT_EQ(sizeof(kTestData), buf2.length()); + EXPECT_EQ(256U, buf2.capacity()); // capacity does transfer + EXPECT_EQ(0, memcmp(buf2.data(), kTestData, sizeof(kTestData))); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/BUILD.gn 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,600 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("//build/config/crypto.gni") +import("//build/config/ui.gni") +import("../build/webrtc.gni") + +config("webrtc_base_config") { + include_dirs = [ + "//third_party/jsoncpp/overrides/include", + "//third_party/jsoncpp/source/include", + ] + + defines = [ + "FEATURE_ENABLE_SSL", + "LOGGING=1", + "USE_WEBRTC_DEV_BRANCH", + ] + + # TODO(henrike): issue 3307, make webrtc_base build without disabling + # these flags. + cflags_cc = [ "-Wno-non-virtual-dtor" ] +} + +config("webrtc_base_chromium_config") { + defines = [ + "NO_MAIN_THREAD_WRAPPING", + ] +} + +config("openssl_config") { + defines = [ + "SSL_USE_OPENSSL", + "HAVE_OPENSSL_SSL_H", + ] +} + +config("nss_config") { + defines = [ + "SSL_USE_NSS", + "HAVE_NSS_SSL_H", + "SSL_USE_NSS_RNG", + ] +} + +config("ios_config") { + ldflags = [ + #"Foundation.framework", # Already included in //build/config:default_libs. + "Security.framework", + "SystemConfiguration.framework", + #"UIKit.framework", # Already included in //build/config:default_libs. + ] +} + +config("mac_config") { + ldflags = [ + "Cocoa.framework", + #"Foundation.framework", # Already included in //build/config:default_libs. + #"IOKit.framework", # Already included in //build/config:default_libs. + #"Security.framework", # Already included in //build/config:default_libs. + "SystemConfiguration.framework", + ] +} + +config("mac_x86_config") { + libs = [ + #"Carbon.framework", # Already included in //build/config:default_libs. + ] +} + +if (is_linux && !build_with_chromium) { + # Provides the same functionality as the //crypto:platform target, which + # WebRTC cannot use as we don't sync src/crypto from Chromium. + group("linux_system_ssl") { + if (use_openssl) { + deps = [ "//third_party/boringssl" ] + } else { + deps = [ "//net/third_party/nss/ssl:libssl" ] + + public_configs = [ + "//net/third_party/nss/ssl:ssl_config", + "//third_party/nss:system_nss_no_ssl_config", + ] + } + } +} + +if (rtc_build_ssl == 0) { + config("external_ssl_library") { + assert(rtc_ssl_root != "", + "You must specify rtc_ssl_root when rtc_build_ssl==0.") + include_dirs = [ rtc_ssl_root ] + } +} + +# The subset of rtc_base approved for use outside of libjingle. +static_library("rtc_base_approved") { + configs += [ "..:common_config" ] + public_configs = [ "..:common_inherited_config" ] + + sources = [ + "checks.cc", + "checks.h", + "exp_filter.cc", + "exp_filter.h", + "md5.cc", + "md5.h", + "md5digest.h", + "platform_file.cc", + "platform_file.h", + "stringencode.cc", + "stringencode.h", + "stringutils.cc", + "stringutils.h", + "thread_annotations.h", + "timeutils.cc", + "timeutils.h", + ] +} + +static_library("webrtc_base") { + cflags = [] + cflags_cc = [] + libs = [] + deps = [ + ":rtc_base_approved", + ] + + configs += [ + "..:common_config", + ":webrtc_base_config", + ] + + public_configs = [ + "..:common_inherited_config", + ":webrtc_base_config", + ] + + defines = [ + "LOGGING=1", + "USE_WEBRTC_DEV_BRANCH", + ] + + sources = [ + "asyncfile.cc", + "asyncfile.h", + "asynchttprequest.cc", + "asynchttprequest.h", + "asyncpacketsocket.h", + "asyncsocket.cc", + "asyncsocket.h", + "asynctcpsocket.cc", + "asynctcpsocket.h", + "asyncudpsocket.cc", + "asyncudpsocket.h", + "autodetectproxy.cc", + "autodetectproxy.h", + "base64.cc", + "base64.h", + "basicdefs.h", + "bytebuffer.cc", + "bytebuffer.h", + "byteorder.h", + "common.cc", + "common.h", + "cpumonitor.cc", + "cpumonitor.h", + "crc32.cc", + "crc32.h", + "criticalsection.h", + "cryptstring.h", + "diskcache.cc", + "diskcache.h", + "event.cc", + "event.h", + "fileutils.cc", + "fileutils.h", + "firewallsocketserver.cc", + "firewallsocketserver.h", + "flags.cc", + "flags.h", + "gunit_prod.h", + "helpers.cc", + "helpers.h", + "httpbase.cc", + "httpbase.h", + "httpclient.cc", + "httpclient.h", + "httpcommon-inl.h", + "httpcommon.cc", + "httpcommon.h", + "httprequest.cc", + "httprequest.h", + "iosfilesystem.mm", + "ipaddress.cc", + "ipaddress.h", + "linked_ptr.h", + "mathutils.h", + "messagedigest.cc", + "messagedigest.h", + "messagehandler.cc", + "messagehandler.h", + "messagequeue.cc", + "messagequeue.h", + "nethelpers.cc", + "nethelpers.h", + "network.cc", + "network.h", + "nullsocketserver.h", + "pathutils.cc", + "pathutils.h", + "physicalsocketserver.cc", + "physicalsocketserver.h", + "proxydetect.cc", + "proxydetect.h", + "proxyinfo.cc", + "proxyinfo.h", + "ratelimiter.cc", + "ratelimiter.h", + "ratetracker.cc", + "ratetracker.h", + "safe_conversions.h", + "safe_conversions_impl.h", + "scoped_autorelease_pool.h", + "scoped_autorelease_pool.mm", + "scoped_ptr.h", + "sha1.cc", + "sha1.h", + "sha1digest.h", + "signalthread.cc", + "signalthread.h", + "sigslot.h", + "sigslotrepeater.h", + "socket.h", + "socketadapters.cc", + "socketadapters.h", + "socketaddress.cc", + "socketaddress.h", + "socketaddresspair.cc", + "socketaddresspair.h", + "socketfactory.h", + "socketpool.cc", + "socketpool.h", + "socketserver.h", + "socketstream.cc", + "socketstream.h", + "ssladapter.cc", + "ssladapter.h", + "sslfingerprint.cc", + "sslfingerprint.h", + "sslidentity.cc", + "sslidentity.h", + "sslsocketfactory.cc", + "sslsocketfactory.h", + "sslstreamadapter.cc", + "sslstreamadapter.h", + "sslstreamadapterhelper.cc", + "sslstreamadapterhelper.h", + "stream.cc", + "stream.h", + "systeminfo.cc", + "systeminfo.h", + "task.cc", + "task.h", + "taskparent.cc", + "taskparent.h", + "taskrunner.cc", + "taskrunner.h", + "thread.cc", + "thread.h", + "thread_checker.h", + "thread_checker_impl.cc", + "thread_checker_impl.h", + "timing.cc", + "timing.h", + "urlencode.cc", + "urlencode.h", + "worker.cc", + "worker.h", + ] + + if (is_posix) { + sources += [ + "unixfilesystem.cc", + "unixfilesystem.h", + ] + } + + if (build_with_chromium) { + sources += [ + "../overrides/webrtc/base/basictypes.h", + "../overrides/webrtc/base/constructormagic.h", + "../overrides/webrtc/base/logging.cc", + "../overrides/webrtc/base/logging.h", + ] + + if (is_win) { + sources += [ "../overrides/webrtc/base/win32socketinit.cc" ] + } + + include_dirs = [ + "../overrides", + "../../boringssl/src/include", + ] + + public_configs += [ ":webrtc_base_chromium_config" ] + } else { + sources += [ + "asyncinvoker.cc", + "asyncinvoker.h", + "asyncinvoker-inl.h", + "asyncresolverinterface.h", + "atomicops.h", + "bandwidthsmoother.cc", + "bandwidthsmoother.h", + "basictypes.h", + "bind.h", + "bind.h.pump", + "buffer.h", + "callback.h", + "callback.h.pump", + "constructormagic.h", + "filelock.cc", + "filelock.h", + "fileutils_mock.h", + "genericslot.h", + "genericslot.h.pump", + "httpserver.cc", + "httpserver.h", + "json.cc", + "json.h", + "logging.cc", + "logging.h", + "mathutils.h", + "multipart.cc", + "multipart.h", + "natserver.cc", + "natserver.h", + "natsocketfactory.cc", + "natsocketfactory.h", + "nattypes.cc", + "nattypes.h", + "optionsfile.cc", + "optionsfile.h", + "profiler.cc", + "profiler.h", + "proxyserver.cc", + "proxyserver.h", + "refcount.h", + "referencecountedsingletonfactory.h", + "rollingaccumulator.h", + "scopedptrcollection.h", + "scoped_ref_ptr.h", + "sec_buffer.h", + "sharedexclusivelock.cc", + "sharedexclusivelock.h", + "sslconfig.h", + "sslroots.h", + "stringdigest.h", + "testclient.cc", + "testclient.h", + "transformadapter.cc", + "transformadapter.h", + "versionparsing.cc", + "versionparsing.h", + "virtualsocketserver.cc", + "virtualsocketserver.h", + "window.h", + "windowpickerfactory.h", + "windowpicker.h", + ] + + if (is_posix) { + sources += [ + "latebindingsymboltable.cc", + "latebindingsymboltable.cc.def", + "latebindingsymboltable.h", + "latebindingsymboltable.h.def", + "posix.cc", + "posix.h", + ] + } + + if (is_linux) { + sources += [ + "dbus.cc", + "dbus.h", + "libdbusglibsymboltable.cc", + "libdbusglibsymboltable.h", + "linuxfdwalk.c", + "linuxfdwalk.h", + ] + } + + if (is_mac) { + sources += [ + "macasyncsocket.cc", + "macasyncsocket.h", + "maccocoasocketserver.h", + "maccocoasocketserver.mm", + "macsocketserver.cc", + "macsocketserver.h", + "macwindowpicker.cc", + "macwindowpicker.h", + ] + } + + if (is_win) { + sources += [ + "diskcache_win32.cc", + "diskcache_win32.h", + "win32regkey.cc", + "win32regkey.h", + "win32socketinit.cc", + "win32socketinit.h", + "win32socketserver.cc", + "win32socketserver.h", + ] + } + if (rtc_build_json) { + deps += [ "//third_party/jsoncpp" ] + } else { + include_dirs += [ rtc_jsoncpp_root ] + + # When defined changes the include path for json.h to where it is + # expected to be when building json outside of the standalone build. + defines += [ "WEBRTC_EXTERNAL_JSON" ] + } + } # !build_with_chromium + + if (is_clang) { + # Suppress warnings from the Chrome Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + # TODO(henrike): issue 3307, make webrtc_base build with the Chromium default + # compiler settings. + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + cflags += [ "-Wno-uninitialized" ] + cflags_cc += [ "-Wno-non-virtual-dtor" ] + + if (use_openssl) { + public_configs += [ ":openssl_config" ] + if (rtc_build_ssl) { + deps += [ "//third_party/boringssl" ] + } else { + configs += [ "external_ssl_library" ] + } + sources += [ + "openssl.h", + "openssladapter.cc", + "openssladapter.h", + "openssldigest.cc", + "openssldigest.h", + "opensslidentity.cc", + "opensslidentity.h", + "opensslstreamadapter.cc", + "opensslstreamadapter.h", + ] + } else { + public_configs += [ ":nss_config" ] + if (rtc_build_ssl) { + if (build_with_chromium) { + deps += [ "//crypto:platform" ] + } else { + deps += [ "//net/third_party/nss/ssl:libssl" ] + if (is_linux) { + deps += [ ":linux_system_ssl" ] + } else { + deps += [ + "//third_party/nss:nspr", + "//third_party/nss:nss", + ] + } + } + } else { + configs += [ "external_ssl_library" ] + } + sources += [ + "nssidentity.cc", + "nssidentity.h", + "nssstreamadapter.cc", + "nssstreamadapter.h", + ] + } + + if (is_android) { + sources += [ + "ifaddrs-android.cc", + "ifaddrs-android.h", + ] + + libs += [ + "log", + "GLESv2" + ] + } + + if (is_ios) { + all_dependent_configs += [ ":ios_config" ] + } + + if (use_x11) { + sources += [ + "x11windowpicker.cc", + "x11windowpicker.h", + ] + libs += [ + "dl", + "rt", + "Xext", + "X11", + "Xcomposite", + "Xrender", + ] + } + + if (is_linux) { + libs += [ + "dl", + "rt", + ] + } + + if (is_mac) { + sources += [ + "maccocoathreadhelper.h", + "maccocoathreadhelper.mm", + "macconversion.cc", + "macconversion.h", + "macutils.cc", + "macutils.h", + ] + + all_dependent_configs = [ ":mac_config" ] + + if (cpu_arch == "x86") { + all_dependent_configs += [ ":mac_x86_config" ] + } + } + + if (is_win) { + sources += [ + "schanneladapter.cc", + "schanneladapter.h", + "win32.cc", + "win32.h", + "win32filesystem.cc", + "win32filesystem.h", + "win32securityerrors.cc", + "win32window.cc", + "win32window.h", + "win32windowpicker.cc", + "win32windowpicker.h", + "winfirewall.cc", + "winfirewall.h", + "winping.cc", + "winping.h", + ] + + libs += [ + "crypt32.lib", + "iphlpapi.lib", + "secur32.lib", + ] + + cflags += [ + # Suppress warnings about WIN32_LEAN_AND_MEAN. + "/wd4005", + "/wd4703", + ] + + defines += [ "_CRT_NONSTDC_NO_DEPRECATE" ] + } + + if (is_posix && is_debug) { + # The Chromium build/common.gypi defines this for all posix + # _except_ for ios & mac. We want it there as well, e.g. + # because ASSERT and friends trigger off of it. + defines += [ "_DEBUG" ] + } + + if (is_ios || (is_mac && cpu_arch != "x86")) { + defines += [ "CARBON_DEPRECATED=YES" ] + } + + if (is_linux || is_android) { + sources += [ + "linux.cc", + "linux.h", + ] + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bytebuffer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bytebuffer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bytebuffer.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bytebuffer.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,234 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/bytebuffer.h" + +#include +#include + +#include + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/byteorder.h" + +namespace rtc { + +static const int DEFAULT_SIZE = 4096; + +ByteBuffer::ByteBuffer() { + Construct(NULL, DEFAULT_SIZE, ORDER_NETWORK); +} + +ByteBuffer::ByteBuffer(ByteOrder byte_order) { + Construct(NULL, DEFAULT_SIZE, byte_order); +} + +ByteBuffer::ByteBuffer(const char* bytes, size_t len) { + Construct(bytes, len, ORDER_NETWORK); +} + +ByteBuffer::ByteBuffer(const char* bytes, size_t len, ByteOrder byte_order) { + Construct(bytes, len, byte_order); +} + +ByteBuffer::ByteBuffer(const char* bytes) { + Construct(bytes, strlen(bytes), ORDER_NETWORK); +} + +void ByteBuffer::Construct(const char* bytes, size_t len, + ByteOrder byte_order) { + version_ = 0; + start_ = 0; + size_ = len; + byte_order_ = byte_order; + bytes_ = new char[size_]; + + if (bytes) { + end_ = len; + memcpy(bytes_, bytes, end_); + } else { + end_ = 0; + } +} + +ByteBuffer::~ByteBuffer() { + delete[] bytes_; +} + +bool ByteBuffer::ReadUInt8(uint8* val) { + if (!val) return false; + + return ReadBytes(reinterpret_cast(val), 1); +} + +bool ByteBuffer::ReadUInt16(uint16* val) { + if (!val) return false; + + uint16 v; + if (!ReadBytes(reinterpret_cast(&v), 2)) { + return false; + } else { + *val = (byte_order_ == ORDER_NETWORK) ? NetworkToHost16(v) : v; + return true; + } +} + +bool ByteBuffer::ReadUInt24(uint32* val) { + if (!val) return false; + + uint32 v = 0; + char* read_into = reinterpret_cast(&v); + if (byte_order_ == ORDER_NETWORK || IsHostBigEndian()) { + ++read_into; + } + + if (!ReadBytes(read_into, 3)) { + return false; + } else { + *val = (byte_order_ == ORDER_NETWORK) ? NetworkToHost32(v) : v; + return true; + } +} + +bool ByteBuffer::ReadUInt32(uint32* val) { + if (!val) return false; + + uint32 v; + if (!ReadBytes(reinterpret_cast(&v), 4)) { + return false; + } else { + *val = (byte_order_ == ORDER_NETWORK) ? NetworkToHost32(v) : v; + return true; + } +} + +bool ByteBuffer::ReadUInt64(uint64* val) { + if (!val) return false; + + uint64 v; + if (!ReadBytes(reinterpret_cast(&v), 8)) { + return false; + } else { + *val = (byte_order_ == ORDER_NETWORK) ? NetworkToHost64(v) : v; + return true; + } +} + +bool ByteBuffer::ReadString(std::string* val, size_t len) { + if (!val) return false; + + if (len > Length()) { + return false; + } else { + val->append(bytes_ + start_, len); + start_ += len; + return true; + } +} + +bool ByteBuffer::ReadBytes(char* val, size_t len) { + if (len > Length()) { + return false; + } else { + memcpy(val, bytes_ + start_, len); + start_ += len; + return true; + } +} + +void ByteBuffer::WriteUInt8(uint8 val) { + WriteBytes(reinterpret_cast(&val), 1); +} + +void ByteBuffer::WriteUInt16(uint16 val) { + uint16 v = (byte_order_ == ORDER_NETWORK) ? HostToNetwork16(val) : val; + WriteBytes(reinterpret_cast(&v), 2); +} + +void ByteBuffer::WriteUInt24(uint32 val) { + uint32 v = (byte_order_ == ORDER_NETWORK) ? HostToNetwork32(val) : val; + char* start = reinterpret_cast(&v); + if (byte_order_ == ORDER_NETWORK || IsHostBigEndian()) { + ++start; + } + WriteBytes(start, 3); +} + +void ByteBuffer::WriteUInt32(uint32 val) { + uint32 v = (byte_order_ == ORDER_NETWORK) ? HostToNetwork32(val) : val; + WriteBytes(reinterpret_cast(&v), 4); +} + +void ByteBuffer::WriteUInt64(uint64 val) { + uint64 v = (byte_order_ == ORDER_NETWORK) ? HostToNetwork64(val) : val; + WriteBytes(reinterpret_cast(&v), 8); +} + +void ByteBuffer::WriteString(const std::string& val) { + WriteBytes(val.c_str(), val.size()); +} + +void ByteBuffer::WriteBytes(const char* val, size_t len) { + memcpy(ReserveWriteBuffer(len), val, len); +} + +char* ByteBuffer::ReserveWriteBuffer(size_t len) { + if (Length() + len > Capacity()) + Resize(Length() + len); + + char* start = bytes_ + end_; + end_ += len; + return start; +} + +void ByteBuffer::Resize(size_t size) { + size_t len = _min(end_ - start_, size); + if (size <= size_) { + // Don't reallocate, just move data backwards + memmove(bytes_, bytes_ + start_, len); + } else { + // Reallocate a larger buffer. + size_ = _max(size, 3 * size_ / 2); + char* new_bytes = new char[size_]; + memcpy(new_bytes, bytes_ + start_, len); + delete [] bytes_; + bytes_ = new_bytes; + } + start_ = 0; + end_ = len; + ++version_; +} + +bool ByteBuffer::Consume(size_t size) { + if (size > Length()) + return false; + start_ += size; + return true; +} + +ByteBuffer::ReadPosition ByteBuffer::GetReadPosition() const { + return ReadPosition(start_, version_); +} + +bool ByteBuffer::SetReadPosition(const ReadPosition &position) { + if (position.version_ != version_) { + return false; + } + start_ = position.start_; + return true; +} + +void ByteBuffer::Clear() { + memset(bytes_, 0, size_); + start_ = end_ = 0; + ++version_; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bytebuffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bytebuffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bytebuffer.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bytebuffer.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,119 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_BYTEBUFFER_H_ +#define WEBRTC_BASE_BYTEBUFFER_H_ + +#include + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/constructormagic.h" + +namespace rtc { + +class ByteBuffer { + public: + + enum ByteOrder { + ORDER_NETWORK = 0, // Default, use network byte order (big endian). + ORDER_HOST, // Use the native order of the host. + }; + + // |byte_order| defines order of bytes in the buffer. + ByteBuffer(); + explicit ByteBuffer(ByteOrder byte_order); + ByteBuffer(const char* bytes, size_t len); + ByteBuffer(const char* bytes, size_t len, ByteOrder byte_order); + + // Initializes buffer from a zero-terminated string. + explicit ByteBuffer(const char* bytes); + + ~ByteBuffer(); + + const char* Data() const { return bytes_ + start_; } + size_t Length() const { return end_ - start_; } + size_t Capacity() const { return size_ - start_; } + ByteOrder Order() const { return byte_order_; } + + // Read a next value from the buffer. Return false if there isn't + // enough data left for the specified type. + bool ReadUInt8(uint8* val); + bool ReadUInt16(uint16* val); + bool ReadUInt24(uint32* val); + bool ReadUInt32(uint32* val); + bool ReadUInt64(uint64* val); + bool ReadBytes(char* val, size_t len); + + // Appends next |len| bytes from the buffer to |val|. Returns false + // if there is less than |len| bytes left. + bool ReadString(std::string* val, size_t len); + + // Write value to the buffer. Resizes the buffer when it is + // neccessary. + void WriteUInt8(uint8 val); + void WriteUInt16(uint16 val); + void WriteUInt24(uint32 val); + void WriteUInt32(uint32 val); + void WriteUInt64(uint64 val); + void WriteString(const std::string& val); + void WriteBytes(const char* val, size_t len); + + // Reserves the given number of bytes and returns a char* that can be written + // into. Useful for functions that require a char* buffer and not a + // ByteBuffer. + char* ReserveWriteBuffer(size_t len); + + // Resize the buffer to the specified |size|. This invalidates any remembered + // seek positions. + void Resize(size_t size); + + // Moves current position |size| bytes forward. Returns false if + // there is less than |size| bytes left in the buffer. Consume doesn't + // permanently remove data, so remembered read positions are still valid + // after this call. + bool Consume(size_t size); + + // Clears the contents of the buffer. After this, Length() will be 0. + void Clear(); + + // Used with GetReadPosition/SetReadPosition. + class ReadPosition { + friend class ByteBuffer; + ReadPosition(size_t start, int version) + : start_(start), version_(version) { } + size_t start_; + int version_; + }; + + // Remembers the current read position for a future SetReadPosition. Any + // calls to Shift or Resize in the interim will invalidate the position. + ReadPosition GetReadPosition() const; + + // If the given position is still valid, restores that read position. + bool SetReadPosition(const ReadPosition &position); + + private: + void Construct(const char* bytes, size_t size, ByteOrder byte_order); + + char* bytes_; + size_t size_; + size_t start_; + size_t end_; + int version_; + ByteOrder byte_order_; + + // There are sensible ways to define these, but they aren't needed in our code + // base. + DISALLOW_COPY_AND_ASSIGN(ByteBuffer); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_BYTEBUFFER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bytebuffer_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bytebuffer_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/bytebuffer_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/bytebuffer_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,211 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/bytebuffer.h" +#include "webrtc/base/byteorder.h" +#include "webrtc/base/common.h" +#include "webrtc/base/gunit.h" + +namespace rtc { + +TEST(ByteBufferTest, TestByteOrder) { + uint16 n16 = 1; + uint32 n32 = 1; + uint64 n64 = 1; + + EXPECT_EQ(n16, NetworkToHost16(HostToNetwork16(n16))); + EXPECT_EQ(n32, NetworkToHost32(HostToNetwork32(n32))); + EXPECT_EQ(n64, NetworkToHost64(HostToNetwork64(n64))); + + if (IsHostBigEndian()) { + // The host is the network (big) endian. + EXPECT_EQ(n16, HostToNetwork16(n16)); + EXPECT_EQ(n32, HostToNetwork32(n32)); + EXPECT_EQ(n64, HostToNetwork64(n64)); + + // GetBE converts big endian to little endian here. + EXPECT_EQ(n16 >> 8, GetBE16(&n16)); + EXPECT_EQ(n32 >> 24, GetBE32(&n32)); + EXPECT_EQ(n64 >> 56, GetBE64(&n64)); + } else { + // The host is little endian. + EXPECT_NE(n16, HostToNetwork16(n16)); + EXPECT_NE(n32, HostToNetwork32(n32)); + EXPECT_NE(n64, HostToNetwork64(n64)); + + // GetBE converts little endian to big endian here. + EXPECT_EQ(GetBE16(&n16), HostToNetwork16(n16)); + EXPECT_EQ(GetBE32(&n32), HostToNetwork32(n32)); + EXPECT_EQ(GetBE64(&n64), HostToNetwork64(n64)); + + // GetBE converts little endian to big endian here. + EXPECT_EQ(n16 << 8, GetBE16(&n16)); + EXPECT_EQ(n32 << 24, GetBE32(&n32)); + EXPECT_EQ(n64 << 56, GetBE64(&n64)); + } +} + +TEST(ByteBufferTest, TestBufferLength) { + ByteBuffer buffer; + size_t size = 0; + EXPECT_EQ(size, buffer.Length()); + + buffer.WriteUInt8(1); + ++size; + EXPECT_EQ(size, buffer.Length()); + + buffer.WriteUInt16(1); + size += 2; + EXPECT_EQ(size, buffer.Length()); + + buffer.WriteUInt24(1); + size += 3; + EXPECT_EQ(size, buffer.Length()); + + buffer.WriteUInt32(1); + size += 4; + EXPECT_EQ(size, buffer.Length()); + + buffer.WriteUInt64(1); + size += 8; + EXPECT_EQ(size, buffer.Length()); + + EXPECT_TRUE(buffer.Consume(0)); + EXPECT_EQ(size, buffer.Length()); + + EXPECT_TRUE(buffer.Consume(4)); + size -= 4; + EXPECT_EQ(size, buffer.Length()); +} + +TEST(ByteBufferTest, TestGetSetReadPosition) { + ByteBuffer buffer("ABCDEF", 6); + EXPECT_EQ(6U, buffer.Length()); + ByteBuffer::ReadPosition pos(buffer.GetReadPosition()); + EXPECT_TRUE(buffer.SetReadPosition(pos)); + EXPECT_EQ(6U, buffer.Length()); + std::string read; + EXPECT_TRUE(buffer.ReadString(&read, 3)); + EXPECT_EQ("ABC", read); + EXPECT_EQ(3U, buffer.Length()); + EXPECT_TRUE(buffer.SetReadPosition(pos)); + EXPECT_EQ(6U, buffer.Length()); + read.clear(); + EXPECT_TRUE(buffer.ReadString(&read, 3)); + EXPECT_EQ("ABC", read); + EXPECT_EQ(3U, buffer.Length()); + // For a resize by writing Capacity() number of bytes. + size_t capacity = buffer.Capacity(); + buffer.ReserveWriteBuffer(buffer.Capacity()); + EXPECT_EQ(capacity + 3U, buffer.Length()); + EXPECT_FALSE(buffer.SetReadPosition(pos)); + read.clear(); + EXPECT_TRUE(buffer.ReadString(&read, 3)); + EXPECT_EQ("DEF", read); +} + +TEST(ByteBufferTest, TestReadWriteBuffer) { + ByteBuffer::ByteOrder orders[2] = { ByteBuffer::ORDER_HOST, + ByteBuffer::ORDER_NETWORK }; + for (size_t i = 0; i < ARRAY_SIZE(orders); i++) { + ByteBuffer buffer(orders[i]); + EXPECT_EQ(orders[i], buffer.Order()); + uint8 ru8; + EXPECT_FALSE(buffer.ReadUInt8(&ru8)); + + // Write and read uint8. + uint8 wu8 = 1; + buffer.WriteUInt8(wu8); + EXPECT_TRUE(buffer.ReadUInt8(&ru8)); + EXPECT_EQ(wu8, ru8); + EXPECT_EQ(0U, buffer.Length()); + + // Write and read uint16. + uint16 wu16 = (1 << 8) + 1; + buffer.WriteUInt16(wu16); + uint16 ru16; + EXPECT_TRUE(buffer.ReadUInt16(&ru16)); + EXPECT_EQ(wu16, ru16); + EXPECT_EQ(0U, buffer.Length()); + + // Write and read uint24. + uint32 wu24 = (3 << 16) + (2 << 8) + 1; + buffer.WriteUInt24(wu24); + uint32 ru24; + EXPECT_TRUE(buffer.ReadUInt24(&ru24)); + EXPECT_EQ(wu24, ru24); + EXPECT_EQ(0U, buffer.Length()); + + // Write and read uint32. + uint32 wu32 = (4 << 24) + (3 << 16) + (2 << 8) + 1; + buffer.WriteUInt32(wu32); + uint32 ru32; + EXPECT_TRUE(buffer.ReadUInt32(&ru32)); + EXPECT_EQ(wu32, ru32); + EXPECT_EQ(0U, buffer.Length()); + + // Write and read uint64. + uint32 another32 = (8 << 24) + (7 << 16) + (6 << 8) + 5; + uint64 wu64 = (static_cast(another32) << 32) + wu32; + buffer.WriteUInt64(wu64); + uint64 ru64; + EXPECT_TRUE(buffer.ReadUInt64(&ru64)); + EXPECT_EQ(wu64, ru64); + EXPECT_EQ(0U, buffer.Length()); + + // Write and read string. + std::string write_string("hello"); + buffer.WriteString(write_string); + std::string read_string; + EXPECT_TRUE(buffer.ReadString(&read_string, write_string.size())); + EXPECT_EQ(write_string, read_string); + EXPECT_EQ(0U, buffer.Length()); + + // Write and read bytes + char write_bytes[] = "foo"; + buffer.WriteBytes(write_bytes, 3); + char read_bytes[3]; + EXPECT_TRUE(buffer.ReadBytes(read_bytes, 3)); + for (int i = 0; i < 3; ++i) { + EXPECT_EQ(write_bytes[i], read_bytes[i]); + } + EXPECT_EQ(0U, buffer.Length()); + + // Write and read reserved buffer space + char* write_dst = buffer.ReserveWriteBuffer(3); + memcpy(write_dst, write_bytes, 3); + memset(read_bytes, 0, 3); + EXPECT_TRUE(buffer.ReadBytes(read_bytes, 3)); + for (int i = 0; i < 3; ++i) { + EXPECT_EQ(write_bytes[i], read_bytes[i]); + } + EXPECT_EQ(0U, buffer.Length()); + + // Write and read in order. + buffer.WriteUInt8(wu8); + buffer.WriteUInt16(wu16); + buffer.WriteUInt24(wu24); + buffer.WriteUInt32(wu32); + buffer.WriteUInt64(wu64); + EXPECT_TRUE(buffer.ReadUInt8(&ru8)); + EXPECT_EQ(wu8, ru8); + EXPECT_TRUE(buffer.ReadUInt16(&ru16)); + EXPECT_EQ(wu16, ru16); + EXPECT_TRUE(buffer.ReadUInt24(&ru24)); + EXPECT_EQ(wu24, ru24); + EXPECT_TRUE(buffer.ReadUInt32(&ru32)); + EXPECT_EQ(wu32, ru32); + EXPECT_TRUE(buffer.ReadUInt64(&ru64)); + EXPECT_EQ(wu64, ru64); + EXPECT_EQ(0U, buffer.Length()); + } +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/byteorder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/byteorder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/byteorder.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/byteorder.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,168 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_BYTEORDER_H_ +#define WEBRTC_BASE_BYTEORDER_H_ + +#if defined(WEBRTC_POSIX) && !defined(__native_client__) +#include +#endif + +#if defined(WEBRTC_WIN) +#include +#endif + +#include "webrtc/base/basictypes.h" + +namespace rtc { + +// Reading and writing of little and big-endian numbers from memory +// TODO: Optimized versions, with direct read/writes of +// integers in host-endian format, when the platform supports it. + +inline void Set8(void* memory, size_t offset, uint8 v) { + static_cast(memory)[offset] = v; +} + +inline uint8 Get8(const void* memory, size_t offset) { + return static_cast(memory)[offset]; +} + +inline void SetBE16(void* memory, uint16 v) { + Set8(memory, 0, static_cast(v >> 8)); + Set8(memory, 1, static_cast(v >> 0)); +} + +inline void SetBE32(void* memory, uint32 v) { + Set8(memory, 0, static_cast(v >> 24)); + Set8(memory, 1, static_cast(v >> 16)); + Set8(memory, 2, static_cast(v >> 8)); + Set8(memory, 3, static_cast(v >> 0)); +} + +inline void SetBE64(void* memory, uint64 v) { + Set8(memory, 0, static_cast(v >> 56)); + Set8(memory, 1, static_cast(v >> 48)); + Set8(memory, 2, static_cast(v >> 40)); + Set8(memory, 3, static_cast(v >> 32)); + Set8(memory, 4, static_cast(v >> 24)); + Set8(memory, 5, static_cast(v >> 16)); + Set8(memory, 6, static_cast(v >> 8)); + Set8(memory, 7, static_cast(v >> 0)); +} + +inline uint16 GetBE16(const void* memory) { + return static_cast((Get8(memory, 0) << 8) | + (Get8(memory, 1) << 0)); +} + +inline uint32 GetBE32(const void* memory) { + return (static_cast(Get8(memory, 0)) << 24) | + (static_cast(Get8(memory, 1)) << 16) | + (static_cast(Get8(memory, 2)) << 8) | + (static_cast(Get8(memory, 3)) << 0); +} + +inline uint64 GetBE64(const void* memory) { + return (static_cast(Get8(memory, 0)) << 56) | + (static_cast(Get8(memory, 1)) << 48) | + (static_cast(Get8(memory, 2)) << 40) | + (static_cast(Get8(memory, 3)) << 32) | + (static_cast(Get8(memory, 4)) << 24) | + (static_cast(Get8(memory, 5)) << 16) | + (static_cast(Get8(memory, 6)) << 8) | + (static_cast(Get8(memory, 7)) << 0); +} + +inline void SetLE16(void* memory, uint16 v) { + Set8(memory, 0, static_cast(v >> 0)); + Set8(memory, 1, static_cast(v >> 8)); +} + +inline void SetLE32(void* memory, uint32 v) { + Set8(memory, 0, static_cast(v >> 0)); + Set8(memory, 1, static_cast(v >> 8)); + Set8(memory, 2, static_cast(v >> 16)); + Set8(memory, 3, static_cast(v >> 24)); +} + +inline void SetLE64(void* memory, uint64 v) { + Set8(memory, 0, static_cast(v >> 0)); + Set8(memory, 1, static_cast(v >> 8)); + Set8(memory, 2, static_cast(v >> 16)); + Set8(memory, 3, static_cast(v >> 24)); + Set8(memory, 4, static_cast(v >> 32)); + Set8(memory, 5, static_cast(v >> 40)); + Set8(memory, 6, static_cast(v >> 48)); + Set8(memory, 7, static_cast(v >> 56)); +} + +inline uint16 GetLE16(const void* memory) { + return static_cast((Get8(memory, 0) << 0) | + (Get8(memory, 1) << 8)); +} + +inline uint32 GetLE32(const void* memory) { + return (static_cast(Get8(memory, 0)) << 0) | + (static_cast(Get8(memory, 1)) << 8) | + (static_cast(Get8(memory, 2)) << 16) | + (static_cast(Get8(memory, 3)) << 24); +} + +inline uint64 GetLE64(const void* memory) { + return (static_cast(Get8(memory, 0)) << 0) | + (static_cast(Get8(memory, 1)) << 8) | + (static_cast(Get8(memory, 2)) << 16) | + (static_cast(Get8(memory, 3)) << 24) | + (static_cast(Get8(memory, 4)) << 32) | + (static_cast(Get8(memory, 5)) << 40) | + (static_cast(Get8(memory, 6)) << 48) | + (static_cast(Get8(memory, 7)) << 56); +} + +// Check if the current host is big endian. +inline bool IsHostBigEndian() { + static const int number = 1; + return 0 == *reinterpret_cast(&number); +} + +inline uint16 HostToNetwork16(uint16 n) { + uint16 result; + SetBE16(&result, n); + return result; +} + +inline uint32 HostToNetwork32(uint32 n) { + uint32 result; + SetBE32(&result, n); + return result; +} + +inline uint64 HostToNetwork64(uint64 n) { + uint64 result; + SetBE64(&result, n); + return result; +} + +inline uint16 NetworkToHost16(uint16 n) { + return GetBE16(&n); +} + +inline uint32 NetworkToHost32(uint32 n) { + return GetBE32(&n); +} + +inline uint64 NetworkToHost64(uint64 n) { + return GetBE64(&n); +} + +} // namespace rtc + +#endif // WEBRTC_BASE_BYTEORDER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/byteorder_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/byteorder_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/byteorder_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/byteorder_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,83 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/byteorder.h" + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/gunit.h" + +namespace rtc { + +// Test memory set functions put values into memory in expected order. +TEST(ByteOrderTest, TestSet) { + uint8 buf[8] = { 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u }; + Set8(buf, 0, 0xfb); + Set8(buf, 1, 0x12); + EXPECT_EQ(0xfb, buf[0]); + EXPECT_EQ(0x12, buf[1]); + SetBE16(buf, 0x1234); + EXPECT_EQ(0x12, buf[0]); + EXPECT_EQ(0x34, buf[1]); + SetLE16(buf, 0x1234); + EXPECT_EQ(0x34, buf[0]); + EXPECT_EQ(0x12, buf[1]); + SetBE32(buf, 0x12345678); + EXPECT_EQ(0x12, buf[0]); + EXPECT_EQ(0x34, buf[1]); + EXPECT_EQ(0x56, buf[2]); + EXPECT_EQ(0x78, buf[3]); + SetLE32(buf, 0x12345678); + EXPECT_EQ(0x78, buf[0]); + EXPECT_EQ(0x56, buf[1]); + EXPECT_EQ(0x34, buf[2]); + EXPECT_EQ(0x12, buf[3]); + SetBE64(buf, UINT64_C(0x0123456789abcdef)); + EXPECT_EQ(0x01, buf[0]); + EXPECT_EQ(0x23, buf[1]); + EXPECT_EQ(0x45, buf[2]); + EXPECT_EQ(0x67, buf[3]); + EXPECT_EQ(0x89, buf[4]); + EXPECT_EQ(0xab, buf[5]); + EXPECT_EQ(0xcd, buf[6]); + EXPECT_EQ(0xef, buf[7]); + SetLE64(buf, UINT64_C(0x0123456789abcdef)); + EXPECT_EQ(0xef, buf[0]); + EXPECT_EQ(0xcd, buf[1]); + EXPECT_EQ(0xab, buf[2]); + EXPECT_EQ(0x89, buf[3]); + EXPECT_EQ(0x67, buf[4]); + EXPECT_EQ(0x45, buf[5]); + EXPECT_EQ(0x23, buf[6]); + EXPECT_EQ(0x01, buf[7]); +} + +// Test memory get functions get values from memory in expected order. +TEST(ByteOrderTest, TestGet) { + uint8 buf[8]; + buf[0] = 0x01u; + buf[1] = 0x23u; + buf[2] = 0x45u; + buf[3] = 0x67u; + buf[4] = 0x89u; + buf[5] = 0xabu; + buf[6] = 0xcdu; + buf[7] = 0xefu; + EXPECT_EQ(0x01u, Get8(buf, 0)); + EXPECT_EQ(0x23u, Get8(buf, 1)); + EXPECT_EQ(0x0123u, GetBE16(buf)); + EXPECT_EQ(0x2301u, GetLE16(buf)); + EXPECT_EQ(0x01234567u, GetBE32(buf)); + EXPECT_EQ(0x67452301u, GetLE32(buf)); + EXPECT_EQ(UINT64_C(0x0123456789abcdef), GetBE64(buf)); + EXPECT_EQ(UINT64_C(0xefcdab8967452301), GetLE64(buf)); +} + +} // namespace rtc + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/callback.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/callback.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/callback.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/callback.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,261 @@ +// This file was GENERATED by command: +// pump.py callback.h.pump +// DO NOT EDIT BY HAND!!! + +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// To generate callback.h from callback.h.pump, execute: +// /home/build/google3/third_party/gtest/scripts/pump.py callback.h.pump + +// Callbacks are callable object containers. They can hold a function pointer +// or a function object and behave like a value type. Internally, data is +// reference-counted, making copies and pass-by-value inexpensive. +// +// Callbacks are typed using template arguments. The format is: +// CallbackN +// where N is the number of arguments supplied to the callable object. +// Callbacks are invoked using operator(), just like a function or a function +// object. Default-constructed callbacks are "empty," and executing an empty +// callback does nothing. A callback can be made empty by assigning it from +// a default-constructed callback. +// +// Callbacks are similar in purpose to std::function (which isn't available on +// all platforms we support) and a lightweight alternative to sigslots. Since +// they effectively hide the type of the object they call, they're useful in +// breaking dependencies between objects that need to interact with one another. +// Notably, they can hold the results of Bind(), std::bind*, etc, without +// needing +// to know the resulting object type of those calls. +// +// Sigslots, on the other hand, provide a fuller feature set, such as multiple +// subscriptions to a signal, optional thread-safety, and lifetime tracking of +// slots. When these features are needed, choose sigslots. +// +// Example: +// int sqr(int x) { return x * x; } +// struct AddK { +// int k; +// int operator()(int x) const { return x + k; } +// } add_k = {5}; +// +// Callback1 my_callback; +// cout << my_callback.empty() << endl; // true +// +// my_callback = Callback1(&sqr); +// cout << my_callback.empty() << endl; // false +// cout << my_callback(3) << endl; // 9 +// +// my_callback = Callback1(add_k); +// cout << my_callback(10) << endl; // 15 +// +// my_callback = Callback1(); +// cout << my_callback.empty() << endl; // true + +#ifndef WEBRTC_BASE_CALLBACK_H_ +#define WEBRTC_BASE_CALLBACK_H_ + +#include "webrtc/base/logging.h" +#include "webrtc/base/refcount.h" +#include "webrtc/base/scoped_ref_ptr.h" + +namespace rtc { + +template +class Callback0 { + public: + // Default copy operations are appropriate for this class. + Callback0() {} + template Callback0(const T& functor) + : helper_(new RefCountedObject< HelperImpl >(functor)) {} + R operator()() { + if (empty()) + return R(); + return helper_->Run(); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run() = 0; + }; + template struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run() { + return functor_(); + } + T functor_; + }; + scoped_refptr helper_; +}; + +template +class Callback1 { + public: + // Default copy operations are appropriate for this class. + Callback1() {} + template Callback1(const T& functor) + : helper_(new RefCountedObject< HelperImpl >(functor)) {} + R operator()(P1 p1) { + if (empty()) + return R(); + return helper_->Run(p1); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run(P1 p1) = 0; + }; + template struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run(P1 p1) { + return functor_(p1); + } + T functor_; + }; + scoped_refptr helper_; +}; + +template +class Callback2 { + public: + // Default copy operations are appropriate for this class. + Callback2() {} + template Callback2(const T& functor) + : helper_(new RefCountedObject< HelperImpl >(functor)) {} + R operator()(P1 p1, P2 p2) { + if (empty()) + return R(); + return helper_->Run(p1, p2); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run(P1 p1, P2 p2) = 0; + }; + template struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run(P1 p1, P2 p2) { + return functor_(p1, p2); + } + T functor_; + }; + scoped_refptr helper_; +}; + +template +class Callback3 { + public: + // Default copy operations are appropriate for this class. + Callback3() {} + template Callback3(const T& functor) + : helper_(new RefCountedObject< HelperImpl >(functor)) {} + R operator()(P1 p1, P2 p2, P3 p3) { + if (empty()) + return R(); + return helper_->Run(p1, p2, p3); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run(P1 p1, P2 p2, P3 p3) = 0; + }; + template struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run(P1 p1, P2 p2, P3 p3) { + return functor_(p1, p2, p3); + } + T functor_; + }; + scoped_refptr helper_; +}; + +template +class Callback4 { + public: + // Default copy operations are appropriate for this class. + Callback4() {} + template Callback4(const T& functor) + : helper_(new RefCountedObject< HelperImpl >(functor)) {} + R operator()(P1 p1, P2 p2, P3 p3, P4 p4) { + if (empty()) + return R(); + return helper_->Run(p1, p2, p3, p4); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4) = 0; + }; + template struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4) { + return functor_(p1, p2, p3, p4); + } + T functor_; + }; + scoped_refptr helper_; +}; + +template +class Callback5 { + public: + // Default copy operations are appropriate for this class. + Callback5() {} + template Callback5(const T& functor) + : helper_(new RefCountedObject< HelperImpl >(functor)) {} + R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) { + if (empty()) + return R(); + return helper_->Run(p1, p2, p3, p4, p5); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) = 0; + }; + template struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) { + return functor_(p1, p2, p3, p4, p5); + } + T functor_; + }; + scoped_refptr helper_; +}; +} // namespace rtc + +#endif // WEBRTC_BASE_CALLBACK_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/callback.h.pump thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/callback.h.pump --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/callback.h.pump 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/callback.h.pump 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,103 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// To generate callback.h from callback.h.pump, execute: +// /home/build/google3/third_party/gtest/scripts/pump.py callback.h.pump + +// Callbacks are callable object containers. They can hold a function pointer +// or a function object and behave like a value type. Internally, data is +// reference-counted, making copies and pass-by-value inexpensive. +// +// Callbacks are typed using template arguments. The format is: +// CallbackN +// where N is the number of arguments supplied to the callable object. +// Callbacks are invoked using operator(), just like a function or a function +// object. Default-constructed callbacks are "empty," and executing an empty +// callback does nothing. A callback can be made empty by assigning it from +// a default-constructed callback. +// +// Callbacks are similar in purpose to std::function (which isn't available on +// all platforms we support) and a lightweight alternative to sigslots. Since +// they effectively hide the type of the object they call, they're useful in +// breaking dependencies between objects that need to interact with one another. +// Notably, they can hold the results of Bind(), std::bind*, etc, without needing +// to know the resulting object type of those calls. +// +// Sigslots, on the other hand, provide a fuller feature set, such as multiple +// subscriptions to a signal, optional thread-safety, and lifetime tracking of +// slots. When these features are needed, choose sigslots. +// +// Example: +// int sqr(int x) { return x * x; } +// struct AddK { +// int k; +// int operator()(int x) const { return x + k; } +// } add_k = {5}; +// +// Callback1 my_callback; +// cout << my_callback.empty() << endl; // true +// +// my_callback = Callback1(&sqr); +// cout << my_callback.empty() << endl; // false +// cout << my_callback(3) << endl; // 9 +// +// my_callback = Callback1(add_k); +// cout << my_callback(10) << endl; // 15 +// +// my_callback = Callback1(); +// cout << my_callback.empty() << endl; // true + +#ifndef WEBRTC_BASE_CALLBACK_H_ +#define WEBRTC_BASE_CALLBACK_H_ + +#include "webrtc/base/refcount.h" +#include "webrtc/base/scoped_ref_ptr.h" + +namespace rtc { + +$var n = 5 +$range i 0..n +$for i [[ +$range j 1..i + +template +class Callback$i { + public: + // Default copy operations are appropriate for this class. + Callback$i() {} + template Callback$i(const T& functor) + : helper_(new RefCountedObject< HelperImpl >(functor)) {} + R operator()($for j , [[P$j p$j]]) { + if (empty()) + return R(); + return helper_->Run($for j , [[p$j]]); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run($for j , [[P$j p$j]]) = 0; + }; + template struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run($for j , [[P$j p$j]]) { + return functor_($for j , [[p$j]]); + } + T functor_; + }; + scoped_refptr helper_; +}; + +]] +} // namespace rtc + +#endif // WEBRTC_BASE_CALLBACK_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/callback_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/callback_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/callback_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/callback_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,81 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/bind.h" +#include "webrtc/base/callback.h" +#include "webrtc/base/gunit.h" + +namespace rtc { + +namespace { + +void f() {} +int g() { return 42; } +int h(int x) { return x * x; } +void i(int& x) { x *= x; } // NOLINT: Testing refs + +struct BindTester { + int a() { return 24; } + int b(int x) const { return x * x; } +}; + +} // namespace + +TEST(CallbackTest, VoidReturn) { + Callback0 cb; + EXPECT_TRUE(cb.empty()); + cb(); // Executing an empty callback should not crash. + cb = Callback0(&f); + EXPECT_FALSE(cb.empty()); + cb(); +} + +TEST(CallbackTest, IntReturn) { + Callback0 cb; + EXPECT_TRUE(cb.empty()); + cb = Callback0(&g); + EXPECT_FALSE(cb.empty()); + EXPECT_EQ(42, cb()); + EXPECT_EQ(42, cb()); +} + +TEST(CallbackTest, OneParam) { + Callback1 cb1(&h); + EXPECT_FALSE(cb1.empty()); + EXPECT_EQ(9, cb1(-3)); + EXPECT_EQ(100, cb1(10)); + + // Try clearing a callback. + cb1 = Callback1(); + EXPECT_TRUE(cb1.empty()); + + // Try a callback with a ref parameter. + Callback1 cb2(&i); + int x = 3; + cb2(x); + EXPECT_EQ(9, x); + cb2(x); + EXPECT_EQ(81, x); +} + +TEST(CallbackTest, WithBind) { + BindTester t; + Callback0 cb1 = Bind(&BindTester::a, &t); + EXPECT_EQ(24, cb1()); + EXPECT_EQ(24, cb1()); + cb1 = Bind(&BindTester::b, &t, 10); + EXPECT_EQ(100, cb1()); + EXPECT_EQ(100, cb1()); + cb1 = Bind(&BindTester::b, &t, 5); + EXPECT_EQ(25, cb1()); + EXPECT_EQ(25, cb1()); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/checks.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/checks.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/checks.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/checks.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,132 @@ +/* + * Copyright 2006 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Most of this was borrowed (with minor modifications) from V8's and Chromium's +// src/base/logging.cc. + +// Use the C++ version to provide __GLIBCXX__. +#include +#include +#include + +#if defined(__GLIBC__) && !defined(__UCLIBC__) +#include +#include +#endif + +#if defined(WEBRTC_ANDROID) +#define LOG_TAG "rtc" +#include // NOLINT +#endif + +#include "webrtc/base/checks.h" + +#if defined(_MSC_VER) +// Warning C4722: destructor never returns, potential memory leak. +// FatalMessage's dtor very intentionally aborts. +#pragma warning(disable:4722) +#endif + +namespace rtc { + +void VPrintError(const char* format, va_list args) { +#if defined(WEBRTC_ANDROID) + __android_log_vprint(ANDROID_LOG_ERROR, LOG_TAG, format, args); +#else + vfprintf(stderr, format, args); +#endif +} + +void PrintError(const char* format, ...) { + va_list args; + va_start(args, format); + VPrintError(format, args); + va_end(args); +} + +// TODO(ajm): This works on Mac (although the parsing fails) but I don't seem +// to get usable symbols on Linux. This is copied from V8. Chromium has a more +// advanced stace trace system; also more difficult to copy. +void DumpBacktrace() { +#if defined(__GLIBC__) && !defined(__UCLIBC__) + void* trace[100]; + int size = backtrace(trace, sizeof(trace) / sizeof(*trace)); + char** symbols = backtrace_symbols(trace, size); + PrintError("\n==== C stack trace ===============================\n\n"); + if (size == 0) { + PrintError("(empty)\n"); + } else if (symbols == NULL) { + PrintError("(no symbols)\n"); + } else { + for (int i = 1; i < size; ++i) { + char mangled[201]; + if (sscanf(symbols[i], "%*[^(]%*[(]%200[^)+]", mangled) == 1) { // NOLINT + PrintError("%2d: ", i); + int status; + size_t length; + char* demangled = abi::__cxa_demangle(mangled, NULL, &length, &status); + PrintError("%s\n", demangled != NULL ? demangled : mangled); + free(demangled); + } else { + // If parsing failed, at least print the unparsed symbol. + PrintError("%s\n", symbols[i]); + } + } + } + free(symbols); +#endif +} + +FatalMessage::FatalMessage(const char* file, int line) { + Init(file, line); +} + +FatalMessage::FatalMessage(const char* file, int line, std::string* result) { + Init(file, line); + stream_ << "Check failed: " << *result << std::endl << "# "; + delete result; +} + +NO_RETURN FatalMessage::~FatalMessage() { + fflush(stdout); + fflush(stderr); + stream_ << std::endl << "#" << std::endl; + PrintError(stream_.str().c_str()); + DumpBacktrace(); + fflush(stderr); + abort(); +} + +void FatalMessage::Init(const char* file, int line) { + stream_ << std::endl << std::endl << "#" << std::endl << "# Fatal error in " + << file << ", line " << line << std::endl << "# "; +} + +// Refer to comments in checks.h. +#ifndef WEBRTC_CHROMIUM_BUILD + +// MSVC doesn't like complex extern templates and DLLs. +#if !defined(COMPILER_MSVC) +// Explicit instantiations for commonly used comparisons. +template std::string* MakeCheckOpString( + const int&, const int&, const char* names); +template std::string* MakeCheckOpString( + const unsigned long&, const unsigned long&, const char* names); +template std::string* MakeCheckOpString( + const unsigned long&, const unsigned int&, const char* names); +template std::string* MakeCheckOpString( + const unsigned int&, const unsigned long&, const char* names); +template std::string* MakeCheckOpString( + const std::string&, const std::string&, const char* name); +#endif + +#endif // WEBRTC_CHROMIUM_BUILD + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/checks.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/checks.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/checks.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/checks.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,216 @@ +/* + * Copyright 2006 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_CHECKS_H_ +#define WEBRTC_BASE_CHECKS_H_ + +#include +#include + +#ifdef WEBRTC_CHROMIUM_BUILD +// Include logging.h in a Chromium build to enable the overrides mechanism for +// using Chromium's macros. Otherwise, don't depend on logging.h. +// TODO(ajm): Ideally, checks.h would be combined with logging.h, but +// consolidation with system_wrappers/logging.h should happen first. +#include "webrtc/base/logging.h" +#endif +#include "webrtc/typedefs.h" + +// The macros here print a message to stderr and abort under various +// conditions. All will accept additional stream messages. For example: +// DCHECK_EQ(foo, bar) << "I'm printed when foo != bar."; +// +// - CHECK(x) is an assertion that x is always true, and that if it isn't, it's +// better to terminate the process than to continue. During development, the +// reason that it's better to terminate might simply be that the error +// handling code isn't in place yet; in production, the reason might be that +// the author of the code truly believes that x will always be true, but that +// she recognizes that if she is wrong, abrupt and unpleasant process +// termination is still better than carrying on with the assumption violated. +// +// CHECK always evaluates its argument, so it's OK for x to have side +// effects. +// +// - DCHECK(x) is the same as CHECK(x)---an assertion that x is always +// true---except that x will only be evaluated in debug builds; in production +// builds, x is simply assumed to be true. This is useful if evaluating x is +// expensive and the expected cost of failing to detect the violated +// assumption is acceptable. You should not handle cases where a production +// build fails to spot a violated condition, even those that would result in +// crashes. If the code needs to cope with the error, make it cope, but don't +// call DCHECK; if the condition really can't occur, but you'd sleep better +// at night knowing that the process will suicide instead of carrying on in +// case you were wrong, use CHECK instead of DCHECK. +// +// DCHECK only evaluates its argument in debug builds, so if x has visible +// side effects, you need to write e.g. +// bool w = x; DCHECK(w); +// +// - CHECK_EQ, _NE, _GT, ..., and DCHECK_EQ, _NE, _GT, ... are specialized +// variants of CHECK and DCHECK that print prettier messages if the condition +// doesn't hold. Prefer them to raw CHECK and DCHECK. +// +// - FATAL() aborts unconditionally. + +namespace rtc { + +// The use of overrides/webrtc/base/logging.h in a Chromium build results in +// redefined macro errors. Fortunately, Chromium's macros can be used as drop-in +// replacements for the standalone versions. +#ifndef WEBRTC_CHROMIUM_BUILD + +// Helper macro which avoids evaluating the arguments to a stream if +// the condition doesn't hold. +#define LAZY_STREAM(stream, condition) \ + !(condition) ? static_cast(0) : rtc::FatalMessageVoidify() & (stream) + +// The actual stream used isn't important. +#define EAT_STREAM_PARAMETERS \ + true ? static_cast(0) \ + : rtc::FatalMessageVoidify() & rtc::FatalMessage("", 0).stream() + +// CHECK dies with a fatal error if condition is not true. It is *not* +// controlled by NDEBUG, so the check will be executed regardless of +// compilation mode. +// +// We make sure CHECK et al. always evaluates their arguments, as +// doing CHECK(FunctionWithSideEffect()) is a common idiom. +#define CHECK(condition) \ + LAZY_STREAM(rtc::FatalMessage(__FILE__, __LINE__).stream(), !(condition)) \ + << "Check failed: " #condition << std::endl << "# " + +// Helper macro for binary operators. +// Don't use this macro directly in your code, use CHECK_EQ et al below. +// +// TODO(akalin): Rewrite this so that constructs like if (...) +// CHECK_EQ(...) else { ... } work properly. +#define CHECK_OP(name, op, val1, val2) \ + if (std::string* _result = \ + rtc::Check##name##Impl((val1), (val2), \ + #val1 " " #op " " #val2)) \ + rtc::FatalMessage(__FILE__, __LINE__, _result).stream() + +// Build the error message string. This is separate from the "Impl" +// function template because it is not performance critical and so can +// be out of line, while the "Impl" code should be inline. Caller +// takes ownership of the returned string. +template +std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) { + std::ostringstream ss; + ss << names << " (" << v1 << " vs. " << v2 << ")"; + std::string* msg = new std::string(ss.str()); + return msg; +} + +// MSVC doesn't like complex extern templates and DLLs. +#if !defined(COMPILER_MSVC) +// Commonly used instantiations of MakeCheckOpString<>. Explicitly instantiated +// in logging.cc. +extern template std::string* MakeCheckOpString( + const int&, const int&, const char* names); +extern template +std::string* MakeCheckOpString( + const unsigned long&, const unsigned long&, const char* names); +extern template +std::string* MakeCheckOpString( + const unsigned long&, const unsigned int&, const char* names); +extern template +std::string* MakeCheckOpString( + const unsigned int&, const unsigned long&, const char* names); +extern template +std::string* MakeCheckOpString( + const std::string&, const std::string&, const char* name); +#endif + +// Helper functions for CHECK_OP macro. +// The (int, int) specialization works around the issue that the compiler +// will not instantiate the template version of the function on values of +// unnamed enum type - see comment below. +#define DEFINE_CHECK_OP_IMPL(name, op) \ + template \ + inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \ + const char* names) { \ + if (v1 op v2) return NULL; \ + else return rtc::MakeCheckOpString(v1, v2, names); \ + } \ + inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \ + if (v1 op v2) return NULL; \ + else return rtc::MakeCheckOpString(v1, v2, names); \ + } +DEFINE_CHECK_OP_IMPL(EQ, ==) +DEFINE_CHECK_OP_IMPL(NE, !=) +DEFINE_CHECK_OP_IMPL(LE, <=) +DEFINE_CHECK_OP_IMPL(LT, < ) +DEFINE_CHECK_OP_IMPL(GE, >=) +DEFINE_CHECK_OP_IMPL(GT, > ) +#undef DEFINE_CHECK_OP_IMPL + +#define CHECK_EQ(val1, val2) CHECK_OP(EQ, ==, val1, val2) +#define CHECK_NE(val1, val2) CHECK_OP(NE, !=, val1, val2) +#define CHECK_LE(val1, val2) CHECK_OP(LE, <=, val1, val2) +#define CHECK_LT(val1, val2) CHECK_OP(LT, < , val1, val2) +#define CHECK_GE(val1, val2) CHECK_OP(GE, >=, val1, val2) +#define CHECK_GT(val1, val2) CHECK_OP(GT, > , val1, val2) + +// The DCHECK macro is equivalent to CHECK except that it only generates code in +// debug builds. +#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) +#define DCHECK(condition) CHECK(condition) +#define DCHECK_EQ(v1, v2) CHECK_EQ(v1, v2) +#define DCHECK_NE(v1, v2) CHECK_NE(v1, v2) +#define DCHECK_LE(v1, v2) CHECK_LE(v1, v2) +#define DCHECK_LT(v1, v2) CHECK_LT(v1, v2) +#define DCHECK_GE(v1, v2) CHECK_GE(v1, v2) +#define DCHECK_GT(v1, v2) CHECK_GT(v1, v2) +#else +#define DCHECK(condition) EAT_STREAM_PARAMETERS +#define DCHECK_EQ(v1, v2) EAT_STREAM_PARAMETERS +#define DCHECK_NE(v1, v2) EAT_STREAM_PARAMETERS +#define DCHECK_LE(v1, v2) EAT_STREAM_PARAMETERS +#define DCHECK_LT(v1, v2) EAT_STREAM_PARAMETERS +#define DCHECK_GE(v1, v2) EAT_STREAM_PARAMETERS +#define DCHECK_GT(v1, v2) EAT_STREAM_PARAMETERS +#endif + +// This is identical to LogMessageVoidify but in name. +class FatalMessageVoidify { + public: + FatalMessageVoidify() { } + // This has to be an operator with a precedence lower than << but + // higher than ?: + void operator&(std::ostream&) { } +}; + +#endif // WEBRTC_CHROMIUM_BUILD + +#define FATAL() rtc::FatalMessage(__FILE__, __LINE__).stream() +// TODO(ajm): Consider adding NOTIMPLEMENTED and NOTREACHED macros when +// base/logging.h and system_wrappers/logging.h are consolidated such that we +// can match the Chromium behavior. + +// Like a stripped-down LogMessage from logging.h, except that it aborts. +class FatalMessage { + public: + FatalMessage(const char* file, int line); + // Used for CHECK_EQ(), etc. Takes ownership of the given string. + FatalMessage(const char* file, int line, std::string* result); + NO_RETURN ~FatalMessage(); + + std::ostream& stream() { return stream_; } + + private: + void Init(const char* file, int line); + + std::ostringstream stream_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_CHECKS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/common.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/common.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/common.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/common.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,72 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include +#include +#include + +#if WEBRTC_WIN +#define WIN32_LEAN_AND_MEAN +#include +#endif // WEBRTC_WIN + +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +#include +#endif // WEBRTC_MAC && !defined(WEBRTC_IOS) + +#include +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" + +////////////////////////////////////////////////////////////////////// +// Assertions +////////////////////////////////////////////////////////////////////// + +namespace rtc { + +void Break() { +#if WEBRTC_WIN + ::DebugBreak(); +#else // !WEBRTC_WIN + // On POSIX systems, SIGTRAP signals debuggers to break without killing the + // process. If a debugger isn't attached, the uncaught SIGTRAP will crash the + // app. + raise(SIGTRAP); +#endif + // If a debugger wasn't attached, we will have crashed by this point. If a + // debugger is attached, we'll continue from here. +} + +static AssertLogger custom_assert_logger_ = NULL; + +void SetCustomAssertLogger(AssertLogger logger) { + custom_assert_logger_ = logger; +} + +void LogAssert(const char* function, const char* file, int line, + const char* expression) { + if (custom_assert_logger_) { + custom_assert_logger_(function, file, line, expression); + } else { + LOG(LS_ERROR) << file << "(" << line << ")" << ": ASSERT FAILED: " + << expression << " @ " << function; + } +} + +bool IsOdd(int n) { + return (n & 0x1); +} + +bool IsEven(int n) { + return !IsOdd(n); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/common.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/common.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/common.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/common.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,227 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_COMMON_H_ // NOLINT +#define WEBRTC_BASE_COMMON_H_ + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/constructormagic.h" + +#if defined(_MSC_VER) +// warning C4355: 'this' : used in base member initializer list +#pragma warning(disable:4355) +#endif + +////////////////////////////////////////////////////////////////////// +// General Utilities +////////////////////////////////////////////////////////////////////// + +#ifndef RTC_UNUSED +#define RTC_UNUSED(x) RtcUnused(static_cast(&x)) +#define RTC_UNUSED2(x, y) RtcUnused(static_cast(&x)); \ + RtcUnused(static_cast(&y)) +#define RTC_UNUSED3(x, y, z) RtcUnused(static_cast(&x)); \ + RtcUnused(static_cast(&y)); \ + RtcUnused(static_cast(&z)) +#define RTC_UNUSED4(x, y, z, a) RtcUnused(static_cast(&x)); \ + RtcUnused(static_cast(&y)); \ + RtcUnused(static_cast(&z)); \ + RtcUnused(static_cast(&a)) +#define RTC_UNUSED5(x, y, z, a, b) RtcUnused(static_cast(&x)); \ + RtcUnused(static_cast(&y)); \ + RtcUnused(static_cast(&z)); \ + RtcUnused(static_cast(&a)); \ + RtcUnused(static_cast(&b)) +inline void RtcUnused(const void*) {} +#endif // RTC_UNUSED + +#if !defined(WEBRTC_WIN) + +#ifndef strnicmp +#define strnicmp(x, y, n) strncasecmp(x, y, n) +#endif + +#ifndef stricmp +#define stricmp(x, y) strcasecmp(x, y) +#endif + +// TODO(fbarchard): Remove this. std::max should be used everywhere in the code. +// NOMINMAX must be defined where we include . +#define stdmax(x, y) std::max(x, y) +#else +#define stdmax(x, y) rtc::_max(x, y) +#endif + +#define ARRAY_SIZE(x) (static_cast(sizeof(x) / sizeof(x[0]))) + +///////////////////////////////////////////////////////////////////////////// +// Assertions +///////////////////////////////////////////////////////////////////////////// + +#ifndef ENABLE_DEBUG +#define ENABLE_DEBUG _DEBUG +#endif // !defined(ENABLE_DEBUG) + +// Even for release builds, allow for the override of LogAssert. Though no +// macro is provided, this can still be used for explicit runtime asserts +// and allow applications to override the assert behavior. + +namespace rtc { + + +// If a debugger is attached, triggers a debugger breakpoint. If a debugger is +// not attached, forces program termination. +void Break(); + +// LogAssert writes information about an assertion to the log. It's called by +// Assert (and from the ASSERT macro in debug mode) before any other action +// is taken (e.g. breaking the debugger, abort()ing, etc.). +void LogAssert(const char* function, const char* file, int line, + const char* expression); + +typedef void (*AssertLogger)(const char* function, + const char* file, + int line, + const char* expression); + +// Sets a custom assert logger to be used instead of the default LogAssert +// behavior. To clear the custom assert logger, pass NULL for |logger| and the +// default behavior will be restored. Only one custom assert logger can be set +// at a time, so this should generally be set during application startup and +// only by one component. +void SetCustomAssertLogger(AssertLogger logger); + +bool IsOdd(int n); + +bool IsEven(int n); + +} // namespace rtc + +#if ENABLE_DEBUG + +namespace rtc { + +inline bool Assert(bool result, const char* function, const char* file, + int line, const char* expression) { + if (!result) { + LogAssert(function, file, line, expression); + Break(); + } + return result; +} + +// Same as Assert above, but does not call Break(). Used in assert macros +// that implement their own breaking. +inline bool AssertNoBreak(bool result, const char* function, const char* file, + int line, const char* expression) { + if (!result) + LogAssert(function, file, line, expression); + return result; +} + +} // namespace rtc + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define __FUNCTION__ "" +#endif + +#ifndef ASSERT +#if defined(WIN32) +// Using debugbreak() inline on Windows directly in the ASSERT macro, has the +// benefit of breaking exactly where the failing expression is and not two +// calls up the stack. +#define ASSERT(x) \ + (rtc::AssertNoBreak((x), __FUNCTION__, __FILE__, __LINE__, #x) ? \ + (void)(1) : __debugbreak()) +#else +#define ASSERT(x) \ + (void)rtc::Assert((x), __FUNCTION__, __FILE__, __LINE__, #x) +#endif +#endif + +#ifndef VERIFY +#if defined(WIN32) +#define VERIFY(x) \ + (rtc::AssertNoBreak((x), __FUNCTION__, __FILE__, __LINE__, #x) ? \ + true : (__debugbreak(), false)) +#else +#define VERIFY(x) rtc::Assert((x), __FUNCTION__, __FILE__, __LINE__, #x) +#endif +#endif + +#else // !ENABLE_DEBUG + +namespace rtc { + +inline bool ImplicitCastToBool(bool result) { return result; } + +} // namespace rtc + +#ifndef ASSERT +#define ASSERT(x) (void)0 +#endif + +#ifndef VERIFY +#define VERIFY(x) rtc::ImplicitCastToBool(x) +#endif + +#endif // !ENABLE_DEBUG + +#define COMPILE_TIME_ASSERT(expr) char CTA_UNIQUE_NAME[expr] +#define CTA_UNIQUE_NAME CTA_MAKE_NAME(__LINE__) +#define CTA_MAKE_NAME(line) CTA_MAKE_NAME2(line) +#define CTA_MAKE_NAME2(line) constraint_ ## line + +// Forces compiler to inline, even against its better judgement. Use wisely. +#if defined(__GNUC__) +#define FORCE_INLINE __attribute__((always_inline)) +#elif defined(WEBRTC_WIN) +#define FORCE_INLINE __forceinline +#else +#define FORCE_INLINE +#endif + +// Borrowed from Chromium's base/compiler_specific.h. +// Annotate a virtual method indicating it must be overriding a virtual +// method in the parent class. +// Use like: +// virtual void foo() OVERRIDE; +#if defined(WEBRTC_WIN) +#define OVERRIDE override +#elif defined(__clang__) +// Clang defaults to C++03 and warns about using override. Squelch that. +// Intentionally no push/pop here so all users of OVERRIDE ignore the warning +// too. This is like passing -Wno-c++11-extensions, except that GCC won't die +// (because it won't see this pragma). +#pragma clang diagnostic ignored "-Wc++11-extensions" +#define OVERRIDE override +#elif defined(__GNUC__) && __cplusplus >= 201103 && \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40700 +// GCC 4.7 supports explicit virtual overrides when C++11 support is enabled. +#define OVERRIDE override +#else +#define OVERRIDE +#endif + +// Annotate a function indicating the caller must examine the return value. +// Use like: +// int foo() WARN_UNUSED_RESULT; +// To explicitly ignore a result, see |ignore_result()| in . +// TODO(ajm): Hack to avoid multiple definitions until the base/ of webrtc and +// libjingle are merged. +#if !defined(WARN_UNUSED_RESULT) +#if defined(__GNUC__) +#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define WARN_UNUSED_RESULT +#endif +#endif // WARN_UNUSED_RESULT + +#endif // WEBRTC_BASE_COMMON_H_ // NOLINT diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/compile_assert.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/compile_assert.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/compile_assert.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/compile_assert.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,82 @@ +/* + * Copyright 2013 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// COMPILE_ASSERT macro, borrowed from google3/base/macros.h. +#ifndef WEBRTC_BASE_COMPILE_ASSERT_H_ +#define WEBRTC_BASE_COMPILE_ASSERT_H_ + +// The COMPILE_ASSERT macro can be used to verify that a compile time +// expression is true. For example, you could use it to verify the +// size of a static array: +// +// COMPILE_ASSERT(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES, +// content_type_names_incorrect_size); +// +// or to make sure a struct is smaller than a certain size: +// +// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large); +// +// The second argument to the macro is the name of the variable. If +// the expression is false, most compilers will issue a warning/error +// containing the name of the variable. + +// TODO(ajm): Hack to avoid multiple definitions until the base/ of webrtc and +// libjingle are merged. +#if !defined(COMPILE_ASSERT) +template +struct CompileAssert { +}; + +#define COMPILE_ASSERT(expr, msg) \ + typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] // NOLINT +#endif // COMPILE_ASSERT + +// Implementation details of COMPILE_ASSERT: +// +// - COMPILE_ASSERT works by defining an array type that has -1 +// elements (and thus is invalid) when the expression is false. +// +// - The simpler definition +// +// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1] +// +// does not work, as gcc supports variable-length arrays whose sizes +// are determined at run-time (this is gcc's extension and not part +// of the C++ standard). As a result, gcc fails to reject the +// following code with the simple definition: +// +// int foo; +// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is +// // not a compile-time constant. +// +// - By using the type CompileAssert<(bool(expr))>, we ensures that +// expr is a compile-time constant. (Template arguments must be +// determined at compile-time.) +// +// - The outer parentheses in CompileAssert<(bool(expr))> are necessary +// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written +// +// CompileAssert +// +// instead, these compilers will refuse to compile +// +// COMPILE_ASSERT(5 > 0, some_message); +// +// (They seem to think the ">" in "5 > 0" marks the end of the +// template argument list.) +// +// - The array size is (bool(expr) ? 1 : -1), instead of simply +// +// ((expr) ? 1 : -1). +// +// This is to avoid running into a bug in MS VC 7.1, which +// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. + +#endif // WEBRTC_BASE_COMPILE_ASSERT_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/constructormagic.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/constructormagic.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/constructormagic.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/constructormagic.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,45 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_CONSTRUCTORMAGIC_H_ +#define WEBRTC_BASE_CONSTRUCTORMAGIC_H_ + +// Undefine macros first, just in case. Some third-party includes have their own +// version. + +#undef DISALLOW_ASSIGN +#define DISALLOW_ASSIGN(TypeName) \ + void operator=(const TypeName&) + +// A macro to disallow the evil copy constructor and operator= functions +// This should be used in the private: declarations for a class. +#undef DISALLOW_COPY_AND_ASSIGN +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + DISALLOW_ASSIGN(TypeName) + +// Alternative, less-accurate legacy name. +#undef DISALLOW_EVIL_CONSTRUCTORS +#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \ + DISALLOW_COPY_AND_ASSIGN(TypeName) + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// +// This should be used in the private: declarations for a class +// that wants to prevent anyone from instantiating it. This is +// especially useful for classes containing only static methods. +#undef DISALLOW_IMPLICIT_CONSTRUCTORS +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName(); \ + DISALLOW_EVIL_CONSTRUCTORS(TypeName) + + +#endif // WEBRTC_BASE_CONSTRUCTORMAGIC_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/cpumonitor.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/cpumonitor.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/cpumonitor.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/cpumonitor.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,423 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/cpumonitor.h" + +#include + +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/systeminfo.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/timeutils.h" + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32.h" +#include +#endif + +#if defined(WEBRTC_POSIX) +#include +#endif + +#if defined(WEBRTC_MAC) +#include +#include +#include +#include +#include +#endif // defined(WEBRTC_MAC) + +#if defined(WEBRTC_LINUX) +#include +#include +#include +#include "webrtc/base/fileutils.h" +#include "webrtc/base/pathutils.h" +#endif // defined(WEBRTC_LINUX) + +#if defined(WEBRTC_MAC) +static uint64 TimeValueTToInt64(const time_value_t &time_value) { + return rtc::kNumMicrosecsPerSec * time_value.seconds + + time_value.microseconds; +} +#endif // defined(WEBRTC_MAC) + +// How CpuSampler works +// When threads switch, the time they spent is accumulated to system counters. +// The time can be treated as user, kernel or idle. +// user time is applications. +// kernel time is the OS, including the thread switching code itself. +// typically kernel time indicates IO. +// idle time is a process that wastes time when nothing is ready to run. +// +// User time is broken down by process (application). One of the applications +// is the current process. When you add up all application times, this is +// system time. If only your application is running, system time should be the +// same as process time. +// +// All cores contribute to these accumulators. A dual core process is able to +// process twice as many cycles as a single core. The actual code efficiency +// may be worse, due to contention, but the available cycles is exactly twice +// as many, and the cpu load will reflect the efficiency. Hyperthreads behave +// the same way. The load will reflect 200%, but the actual amount of work +// completed will be much less than a true dual core. +// +// Total available performance is the sum of all accumulators. +// If you tracked this for 1 second, it would essentially give you the clock +// rate - number of cycles per second. +// Speed step / Turbo Boost is not considered, so infact more processing time +// may be available. + +namespace rtc { + +// Note Tests on Windows show 600 ms is minimum stable interval for Windows 7. +static const int32 kDefaultInterval = 950; // Slightly under 1 second. + +CpuSampler::CpuSampler() + : min_load_interval_(kDefaultInterval) +#if defined(WEBRTC_WIN) + , get_system_times_(NULL), + nt_query_system_information_(NULL), + force_fallback_(false) +#endif + { +} + +CpuSampler::~CpuSampler() { +} + +// Set minimum interval in ms between computing new load values. Default 950. +void CpuSampler::set_load_interval(int min_load_interval) { + min_load_interval_ = min_load_interval; +} + +bool CpuSampler::Init() { + sysinfo_.reset(new SystemInfo); + cpus_ = sysinfo_->GetMaxCpus(); + if (cpus_ == 0) { + return false; + } +#if defined(WEBRTC_WIN) + // Note that GetSystemTimes is available in Windows XP SP1 or later. + // http://msdn.microsoft.com/en-us/library/ms724400.aspx + // NtQuerySystemInformation is used as a fallback. + if (!force_fallback_) { + get_system_times_ = GetProcAddress(GetModuleHandle(L"kernel32.dll"), + "GetSystemTimes"); + } + nt_query_system_information_ = GetProcAddress(GetModuleHandle(L"ntdll.dll"), + "NtQuerySystemInformation"); + if ((get_system_times_ == NULL) && (nt_query_system_information_ == NULL)) { + return false; + } +#endif +#if defined(WEBRTC_LINUX) + Pathname sname("/proc/stat"); + sfile_.reset(Filesystem::OpenFile(sname, "rb")); + if (!sfile_) { + LOG_ERR(LS_ERROR) << "open proc/stat failed:"; + return false; + } + if (!sfile_->DisableBuffering()) { + LOG_ERR(LS_ERROR) << "could not disable buffering for proc/stat"; + return false; + } +#endif // defined(WEBRTC_LINUX) + GetProcessLoad(); // Initialize values. + GetSystemLoad(); + // Help next user call return valid data by recomputing load. + process_.prev_load_time_ = 0u; + system_.prev_load_time_ = 0u; + return true; +} + +float CpuSampler::UpdateCpuLoad(uint64 current_total_times, + uint64 current_cpu_times, + uint64 *prev_total_times, + uint64 *prev_cpu_times) { + float result = 0.f; + if (current_total_times < *prev_total_times || + current_cpu_times < *prev_cpu_times) { + LOG(LS_ERROR) << "Inconsistent time values are passed. ignored"; + } else { + const uint64 cpu_diff = current_cpu_times - *prev_cpu_times; + const uint64 total_diff = current_total_times - *prev_total_times; + result = (total_diff == 0ULL ? 0.f : + static_cast(1.0f * cpu_diff / total_diff)); + if (result > static_cast(cpus_)) { + result = static_cast(cpus_); + } + *prev_total_times = current_total_times; + *prev_cpu_times = current_cpu_times; + } + return result; +} + +float CpuSampler::GetSystemLoad() { + uint32 timenow = Time(); + int elapsed = static_cast(TimeDiff(timenow, system_.prev_load_time_)); + if (min_load_interval_ != 0 && system_.prev_load_time_ != 0u && + elapsed < min_load_interval_) { + return system_.prev_load_; + } +#if defined(WEBRTC_WIN) + uint64 total_times, cpu_times; + + typedef BOOL (_stdcall *GST_PROC)(LPFILETIME, LPFILETIME, LPFILETIME); + typedef NTSTATUS (WINAPI *QSI_PROC)(SYSTEM_INFORMATION_CLASS, + PVOID, ULONG, PULONG); + + GST_PROC get_system_times = reinterpret_cast(get_system_times_); + QSI_PROC nt_query_system_information = reinterpret_cast( + nt_query_system_information_); + + if (get_system_times) { + FILETIME idle_time, kernel_time, user_time; + if (!get_system_times(&idle_time, &kernel_time, &user_time)) { + LOG(LS_ERROR) << "::GetSystemTimes() failed: " << ::GetLastError(); + return 0.f; + } + // kernel_time includes Kernel idle time, so no need to + // include cpu_time as total_times + total_times = ToUInt64(kernel_time) + ToUInt64(user_time); + cpu_times = total_times - ToUInt64(idle_time); + + } else { + if (nt_query_system_information) { + ULONG returned_length = 0; + scoped_ptr processor_info( + new SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[cpus_]); + nt_query_system_information( + ::SystemProcessorPerformanceInformation, + reinterpret_cast(processor_info.get()), + cpus_ * sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), + &returned_length); + + if (returned_length != + (cpus_ * sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION))) { + LOG(LS_ERROR) << "NtQuerySystemInformation has unexpected size"; + return 0.f; + } + + uint64 current_idle = 0; + uint64 current_kernel = 0; + uint64 current_user = 0; + for (int ix = 0; ix < cpus_; ++ix) { + current_idle += processor_info[ix].IdleTime.QuadPart; + current_kernel += processor_info[ix].UserTime.QuadPart; + current_user += processor_info[ix].KernelTime.QuadPart; + } + total_times = current_kernel + current_user; + cpu_times = total_times - current_idle; + } else { + return 0.f; + } + } +#endif // WEBRTC_WIN + +#if defined(WEBRTC_MAC) + mach_port_t mach_host = mach_host_self(); + host_cpu_load_info_data_t cpu_info; + mach_msg_type_number_t info_count = HOST_CPU_LOAD_INFO_COUNT; + kern_return_t kr = host_statistics(mach_host, HOST_CPU_LOAD_INFO, + reinterpret_cast(&cpu_info), + &info_count); + mach_port_deallocate(mach_task_self(), mach_host); + if (KERN_SUCCESS != kr) { + LOG(LS_ERROR) << "::host_statistics() failed"; + return 0.f; + } + + const uint64 cpu_times = cpu_info.cpu_ticks[CPU_STATE_NICE] + + cpu_info.cpu_ticks[CPU_STATE_SYSTEM] + + cpu_info.cpu_ticks[CPU_STATE_USER]; + const uint64 total_times = cpu_times + cpu_info.cpu_ticks[CPU_STATE_IDLE]; +#endif // defined(WEBRTC_MAC) + +#if defined(WEBRTC_LINUX) + if (!sfile_) { + LOG(LS_ERROR) << "Invalid handle for proc/stat"; + return 0.f; + } + std::string statbuf; + sfile_->SetPosition(0); + if (!sfile_->ReadLine(&statbuf)) { + LOG_ERR(LS_ERROR) << "Could not read proc/stat file"; + return 0.f; + } + + unsigned long long user; + unsigned long long nice; + unsigned long long system; + unsigned long long idle; + if (sscanf(statbuf.c_str(), "cpu %Lu %Lu %Lu %Lu", + &user, &nice, + &system, &idle) != 4) { + LOG_ERR(LS_ERROR) << "Could not parse cpu info"; + return 0.f; + } + const uint64 cpu_times = nice + system + user; + const uint64 total_times = cpu_times + idle; +#endif // defined(WEBRTC_LINUX) + +#if defined(__native_client__) + // TODO(ryanpetrie): Implement this via PPAPI when it's available. + const uint64 cpu_times = 0; + const uint64 total_times = 0; +#endif // defined(__native_client__) + + system_.prev_load_time_ = timenow; + system_.prev_load_ = UpdateCpuLoad(total_times, + cpu_times * cpus_, + &system_.prev_total_times_, + &system_.prev_cpu_times_); + return system_.prev_load_; +} + +float CpuSampler::GetProcessLoad() { + uint32 timenow = Time(); + int elapsed = static_cast(TimeDiff(timenow, process_.prev_load_time_)); + if (min_load_interval_ != 0 && process_.prev_load_time_ != 0u && + elapsed < min_load_interval_) { + return process_.prev_load_; + } +#if defined(WEBRTC_WIN) + FILETIME current_file_time; + ::GetSystemTimeAsFileTime(¤t_file_time); + + FILETIME create_time, exit_time, kernel_time, user_time; + if (!::GetProcessTimes(::GetCurrentProcess(), + &create_time, &exit_time, &kernel_time, &user_time)) { + LOG(LS_ERROR) << "::GetProcessTimes() failed: " << ::GetLastError(); + return 0.f; + } + + const uint64 total_times = + ToUInt64(current_file_time) - ToUInt64(create_time); + const uint64 cpu_times = + (ToUInt64(kernel_time) + ToUInt64(user_time)); +#endif // WEBRTC_WIN + +#if defined(WEBRTC_POSIX) + // Common to both OSX and Linux. + struct timeval tv; + gettimeofday(&tv, NULL); + const uint64 total_times = tv.tv_sec * kNumMicrosecsPerSec + tv.tv_usec; +#endif + +#if defined(WEBRTC_MAC) + // Get live thread usage. + task_thread_times_info task_times_info; + mach_msg_type_number_t info_count = TASK_THREAD_TIMES_INFO_COUNT; + + if (KERN_SUCCESS != task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, + reinterpret_cast(&task_times_info), + &info_count)) { + LOG(LS_ERROR) << "::task_info(TASK_THREAD_TIMES_INFO) failed"; + return 0.f; + } + + // Get terminated thread usage. + task_basic_info task_term_info; + info_count = TASK_BASIC_INFO_COUNT; + if (KERN_SUCCESS != task_info(mach_task_self(), TASK_BASIC_INFO, + reinterpret_cast(&task_term_info), + &info_count)) { + LOG(LS_ERROR) << "::task_info(TASK_BASIC_INFO) failed"; + return 0.f; + } + + const uint64 cpu_times = (TimeValueTToInt64(task_times_info.user_time) + + TimeValueTToInt64(task_times_info.system_time) + + TimeValueTToInt64(task_term_info.user_time) + + TimeValueTToInt64(task_term_info.system_time)); +#endif // defined(WEBRTC_MAC) + +#if defined(WEBRTC_LINUX) + rusage usage; + if (getrusage(RUSAGE_SELF, &usage) < 0) { + LOG_ERR(LS_ERROR) << "getrusage failed"; + return 0.f; + } + + const uint64 cpu_times = + (usage.ru_utime.tv_sec + usage.ru_stime.tv_sec) * kNumMicrosecsPerSec + + usage.ru_utime.tv_usec + usage.ru_stime.tv_usec; +#endif // defined(WEBRTC_LINUX) + +#if defined(__native_client__) + // TODO(ryanpetrie): Implement this via PPAPI when it's available. + const uint64 cpu_times = 0; +#endif // defined(__native_client__) + + process_.prev_load_time_ = timenow; + process_.prev_load_ = UpdateCpuLoad(total_times, + cpu_times, + &process_.prev_total_times_, + &process_.prev_cpu_times_); + return process_.prev_load_; +} + +int CpuSampler::GetMaxCpus() const { + return cpus_; +} + +int CpuSampler::GetCurrentCpus() { + return sysinfo_->GetCurCpus(); +} + +/////////////////////////////////////////////////////////////////// +// Implementation of class CpuMonitor. +CpuMonitor::CpuMonitor(Thread* thread) + : monitor_thread_(thread) { +} + +CpuMonitor::~CpuMonitor() { + Stop(); +} + +void CpuMonitor::set_thread(Thread* thread) { + ASSERT(monitor_thread_ == NULL || monitor_thread_ == thread); + monitor_thread_ = thread; +} + +bool CpuMonitor::Start(int period_ms) { + if (!monitor_thread_ || !sampler_.Init()) return false; + + monitor_thread_->SignalQueueDestroyed.connect( + this, &CpuMonitor::OnMessageQueueDestroyed); + + period_ms_ = period_ms; + monitor_thread_->PostDelayed(period_ms_, this); + + return true; +} + +void CpuMonitor::Stop() { + if (monitor_thread_) { + monitor_thread_->Clear(this); + } +} + +void CpuMonitor::OnMessage(Message* msg) { + int max_cpus = sampler_.GetMaxCpus(); + int current_cpus = sampler_.GetCurrentCpus(); + float process_load = sampler_.GetProcessLoad(); + float system_load = sampler_.GetSystemLoad(); + SignalUpdate(current_cpus, max_cpus, process_load, system_load); + + if (monitor_thread_) { + monitor_thread_->PostDelayed(period_ms_, this); + } +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/cpumonitor.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/cpumonitor.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/cpumonitor.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/cpumonitor.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,123 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_CPUMONITOR_H_ +#define WEBRTC_BASE_CPUMONITOR_H_ + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/messagehandler.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/sigslot.h" +#if defined(WEBRTC_LINUX) +#include "webrtc/base/stream.h" +#endif // defined(WEBRTC_LINUX) + +namespace rtc { +class Thread; +class SystemInfo; + +struct CpuStats { + CpuStats() + : prev_total_times_(0), + prev_cpu_times_(0), + prev_load_(0.f), + prev_load_time_(0u) { + } + + uint64 prev_total_times_; + uint64 prev_cpu_times_; + float prev_load_; // Previous load value. + uint32 prev_load_time_; // Time previous load value was taken. +}; + +// CpuSampler samples the process and system load. +class CpuSampler { + public: + CpuSampler(); + ~CpuSampler(); + + // Initialize CpuSampler. Returns true if successful. + bool Init(); + + // Set minimum interval in ms between computing new load values. + // Default 950 ms. Set to 0 to disable interval. + void set_load_interval(int min_load_interval); + + // Return CPU load of current process as a float from 0 to 1. + float GetProcessLoad(); + + // Return CPU load of current process as a float from 0 to 1. + float GetSystemLoad(); + + // Return number of cpus. Includes hyperthreads. + int GetMaxCpus() const; + + // Return current number of cpus available to this process. + int GetCurrentCpus(); + + // For testing. Allows forcing of fallback to using NTDLL functions. + void set_force_fallback(bool fallback) { +#if defined(WEBRTC_WIN) + force_fallback_ = fallback; +#endif + } + + private: + float UpdateCpuLoad(uint64 current_total_times, + uint64 current_cpu_times, + uint64 *prev_total_times, + uint64 *prev_cpu_times); + CpuStats process_; + CpuStats system_; + int cpus_; + int min_load_interval_; // Minimum time between computing new load. + scoped_ptr sysinfo_; +#if defined(WEBRTC_WIN) + void* get_system_times_; + void* nt_query_system_information_; + bool force_fallback_; +#endif +#if defined(WEBRTC_LINUX) + // File for reading /proc/stat + scoped_ptr sfile_; +#endif // defined(WEBRTC_LINUX) +}; + +// CpuMonitor samples and signals the CPU load periodically. +class CpuMonitor + : public rtc::MessageHandler, public sigslot::has_slots<> { + public: + explicit CpuMonitor(Thread* thread); + virtual ~CpuMonitor(); + void set_thread(Thread* thread); + + bool Start(int period_ms); + void Stop(); + // Signal parameters are current cpus, max cpus, process load and system load. + sigslot::signal4 SignalUpdate; + + protected: + // Override virtual method of parent MessageHandler. + virtual void OnMessage(rtc::Message* msg); + // Clear the monitor thread and stop sending it messages if the thread goes + // away before our lifetime. + void OnMessageQueueDestroyed() { monitor_thread_ = NULL; } + + private: + Thread* monitor_thread_; + CpuSampler sampler_; + int period_ms_; + + DISALLOW_COPY_AND_ASSIGN(CpuMonitor); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_CPUMONITOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/cpumonitor_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/cpumonitor_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/cpumonitor_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/cpumonitor_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,389 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include +#include + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32.h" +#endif + +#include "webrtc/base/cpumonitor.h" +#include "webrtc/base/flags.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/timeutils.h" +#include "webrtc/base/timing.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace rtc { + +static const int kMaxCpus = 1024; +static const int kSettleTime = 100; // Amount of time to between tests. +static const int kIdleTime = 500; // Amount of time to be idle in ms. +static const int kBusyTime = 1000; // Amount of time to be busy in ms. +static const int kLongInterval = 2000; // Interval longer than busy times + +class BusyThread : public rtc::Thread { + public: + BusyThread(double load, double duration, double interval) : + load_(load), duration_(duration), interval_(interval) { + } + virtual ~BusyThread() { + Stop(); + } + void Run() { + Timing time; + double busy_time = interval_ * load_ / 100.0; + for (;;) { + time.BusyWait(busy_time); + time.IdleWait(interval_ - busy_time); + if (duration_) { + duration_ -= interval_; + if (duration_ <= 0) { + break; + } + } + } + } + private: + double load_; + double duration_; + double interval_; +}; + +class CpuLoadListener : public sigslot::has_slots<> { + public: + CpuLoadListener() + : current_cpus_(0), + cpus_(0), + process_load_(.0f), + system_load_(.0f), + count_(0) { + } + + void OnCpuLoad(int current_cpus, int cpus, float proc_load, float sys_load) { + current_cpus_ = current_cpus; + cpus_ = cpus; + process_load_ = proc_load; + system_load_ = sys_load; + ++count_; + } + + int current_cpus() const { return current_cpus_; } + int cpus() const { return cpus_; } + float process_load() const { return process_load_; } + float system_load() const { return system_load_; } + int count() const { return count_; } + + private: + int current_cpus_; + int cpus_; + float process_load_; + float system_load_; + int count_; +}; + +// Set affinity (which cpu to run on), but respecting FLAG_affinity: +// -1 means no affinity - run on whatever cpu is available. +// 0 .. N means run on specific cpu. The tool will create N threads and call +// SetThreadAffinity on 0 to N - 1 as cpu. FLAG_affinity sets the first cpu +// so the range becomes affinity to affinity + N - 1 +// Note that this function affects Windows scheduling, effectively giving +// the thread with affinity for a specified CPU more priority on that CPU. +bool SetThreadAffinity(BusyThread* t, int cpu, int affinity) { +#if defined(WEBRTC_WIN) + if (affinity >= 0) { + return ::SetThreadAffinityMask(t->GetHandle(), + 1 << (cpu + affinity)) != FALSE; + } +#endif + return true; +} + +bool SetThreadPriority(BusyThread* t, int prio) { + if (!prio) { + return true; + } + bool ok = t->SetPriority(static_cast(prio)); + if (!ok) { + std::cout << "Error setting thread priority." << std::endl; + } + return ok; +} + +int CpuLoad(double cpuload, double duration, int numthreads, + int priority, double interval, int affinity) { + int ret = 0; + std::vector threads; + for (int i = 0; i < numthreads; ++i) { + threads.push_back(new BusyThread(cpuload, duration, interval)); + // NOTE(fbarchard): Priority must be done before Start. + if (!SetThreadPriority(threads[i], priority) || + !threads[i]->Start() || + !SetThreadAffinity(threads[i], i, affinity)) { + ret = 1; + break; + } + } + // Wait on each thread + if (ret == 0) { + for (int i = 0; i < numthreads; ++i) { + threads[i]->Stop(); + } + } + + for (int i = 0; i < numthreads; ++i) { + delete threads[i]; + } + return ret; +} + +// Make 2 CPUs busy +static void CpuTwoBusyLoop(int busytime) { + CpuLoad(100.0, busytime / 1000.0, 2, 1, 0.050, -1); +} + +// Make 1 CPUs busy +static void CpuBusyLoop(int busytime) { + CpuLoad(100.0, busytime / 1000.0, 1, 1, 0.050, -1); +} + +// Make 1 use half CPU time. +static void CpuHalfBusyLoop(int busytime) { + CpuLoad(50.0, busytime / 1000.0, 1, 1, 0.050, -1); +} + +void TestCpuSampler(bool test_proc, bool test_sys, bool force_fallback) { + CpuSampler sampler; + sampler.set_force_fallback(force_fallback); + EXPECT_TRUE(sampler.Init()); + sampler.set_load_interval(100); + int cpus = sampler.GetMaxCpus(); + + // Test1: CpuSampler under idle situation. + Thread::SleepMs(kSettleTime); + sampler.GetProcessLoad(); + sampler.GetSystemLoad(); + + Thread::SleepMs(kIdleTime); + + float proc_idle = 0.f, sys_idle = 0.f; + if (test_proc) { + proc_idle = sampler.GetProcessLoad(); + } + if (test_sys) { + sys_idle = sampler.GetSystemLoad(); + } + if (test_proc) { + LOG(LS_INFO) << "ProcessLoad Idle: " + << std::setiosflags(std::ios_base::fixed) + << std::setprecision(2) << std::setw(6) << proc_idle; + EXPECT_GE(proc_idle, 0.f); + EXPECT_LE(proc_idle, static_cast(cpus)); + } + if (test_sys) { + LOG(LS_INFO) << "SystemLoad Idle: " + << std::setiosflags(std::ios_base::fixed) + << std::setprecision(2) << std::setw(6) << sys_idle; + EXPECT_GE(sys_idle, 0.f); + EXPECT_LE(sys_idle, static_cast(cpus)); + } + + // Test2: CpuSampler with main process at 50% busy. + Thread::SleepMs(kSettleTime); + sampler.GetProcessLoad(); + sampler.GetSystemLoad(); + + CpuHalfBusyLoop(kBusyTime); + + float proc_halfbusy = 0.f, sys_halfbusy = 0.f; + if (test_proc) { + proc_halfbusy = sampler.GetProcessLoad(); + } + if (test_sys) { + sys_halfbusy = sampler.GetSystemLoad(); + } + if (test_proc) { + LOG(LS_INFO) << "ProcessLoad Halfbusy: " + << std::setiosflags(std::ios_base::fixed) + << std::setprecision(2) << std::setw(6) << proc_halfbusy; + EXPECT_GE(proc_halfbusy, 0.f); + EXPECT_LE(proc_halfbusy, static_cast(cpus)); + } + if (test_sys) { + LOG(LS_INFO) << "SystemLoad Halfbusy: " + << std::setiosflags(std::ios_base::fixed) + << std::setprecision(2) << std::setw(6) << sys_halfbusy; + EXPECT_GE(sys_halfbusy, 0.f); + EXPECT_LE(sys_halfbusy, static_cast(cpus)); + } + + // Test3: CpuSampler with main process busy. + Thread::SleepMs(kSettleTime); + sampler.GetProcessLoad(); + sampler.GetSystemLoad(); + + CpuBusyLoop(kBusyTime); + + float proc_busy = 0.f, sys_busy = 0.f; + if (test_proc) { + proc_busy = sampler.GetProcessLoad(); + } + if (test_sys) { + sys_busy = sampler.GetSystemLoad(); + } + if (test_proc) { + LOG(LS_INFO) << "ProcessLoad Busy: " + << std::setiosflags(std::ios_base::fixed) + << std::setprecision(2) << std::setw(6) << proc_busy; + EXPECT_GE(proc_busy, 0.f); + EXPECT_LE(proc_busy, static_cast(cpus)); + } + if (test_sys) { + LOG(LS_INFO) << "SystemLoad Busy: " + << std::setiosflags(std::ios_base::fixed) + << std::setprecision(2) << std::setw(6) << sys_busy; + EXPECT_GE(sys_busy, 0.f); + EXPECT_LE(sys_busy, static_cast(cpus)); + } + + // Test4: CpuSampler with 2 cpus process busy. + if (cpus >= 2) { + Thread::SleepMs(kSettleTime); + sampler.GetProcessLoad(); + sampler.GetSystemLoad(); + + CpuTwoBusyLoop(kBusyTime); + + float proc_twobusy = 0.f, sys_twobusy = 0.f; + if (test_proc) { + proc_twobusy = sampler.GetProcessLoad(); + } + if (test_sys) { + sys_twobusy = sampler.GetSystemLoad(); + } + if (test_proc) { + LOG(LS_INFO) << "ProcessLoad 2 CPU Busy:" + << std::setiosflags(std::ios_base::fixed) + << std::setprecision(2) << std::setw(6) << proc_twobusy; + EXPECT_GE(proc_twobusy, 0.f); + EXPECT_LE(proc_twobusy, static_cast(cpus)); + } + if (test_sys) { + LOG(LS_INFO) << "SystemLoad 2 CPU Busy: " + << std::setiosflags(std::ios_base::fixed) + << std::setprecision(2) << std::setw(6) << sys_twobusy; + EXPECT_GE(sys_twobusy, 0.f); + EXPECT_LE(sys_twobusy, static_cast(cpus)); + } + } + + // Test5: CpuSampler with idle process after being busy. + Thread::SleepMs(kSettleTime); + sampler.GetProcessLoad(); + sampler.GetSystemLoad(); + + Thread::SleepMs(kIdleTime); + + if (test_proc) { + proc_idle = sampler.GetProcessLoad(); + } + if (test_sys) { + sys_idle = sampler.GetSystemLoad(); + } + if (test_proc) { + LOG(LS_INFO) << "ProcessLoad Idle: " + << std::setiosflags(std::ios_base::fixed) + << std::setprecision(2) << std::setw(6) << proc_idle; + EXPECT_GE(proc_idle, 0.f); + EXPECT_LE(proc_idle, proc_busy); + } + if (test_sys) { + LOG(LS_INFO) << "SystemLoad Idle: " + << std::setiosflags(std::ios_base::fixed) + << std::setprecision(2) << std::setw(6) << sys_idle; + EXPECT_GE(sys_idle, 0.f); + EXPECT_LE(sys_idle, static_cast(cpus)); + } +} + +TEST(CpuMonitorTest, TestCpus) { + CpuSampler sampler; + EXPECT_TRUE(sampler.Init()); + int current_cpus = sampler.GetCurrentCpus(); + int cpus = sampler.GetMaxCpus(); + LOG(LS_INFO) << "Current Cpus: " << std::setw(9) << current_cpus; + LOG(LS_INFO) << "Maximum Cpus: " << std::setw(9) << cpus; + EXPECT_GT(cpus, 0); + EXPECT_LE(cpus, kMaxCpus); + EXPECT_GT(current_cpus, 0); + EXPECT_LE(current_cpus, cpus); +} + +#if defined(WEBRTC_WIN) +// Tests overall system CpuSampler using legacy OS fallback code if applicable. +TEST(CpuMonitorTest, TestGetSystemLoadForceFallback) { + TestCpuSampler(false, true, true); +} +#endif + +// Tests both process and system functions in use at same time. +TEST(CpuMonitorTest, TestGetBothLoad) { + TestCpuSampler(true, true, false); +} + +// Tests a query less than the interval produces the same value. +TEST(CpuMonitorTest, TestInterval) { + CpuSampler sampler; + EXPECT_TRUE(sampler.Init()); + + // Test1: Set interval to large value so sampler will not update. + sampler.set_load_interval(kLongInterval); + + sampler.GetProcessLoad(); + sampler.GetSystemLoad(); + + float proc_orig = sampler.GetProcessLoad(); + float sys_orig = sampler.GetSystemLoad(); + + Thread::SleepMs(kIdleTime); + + float proc_halftime = sampler.GetProcessLoad(); + float sys_halftime = sampler.GetSystemLoad(); + + EXPECT_EQ(proc_orig, proc_halftime); + EXPECT_EQ(sys_orig, sys_halftime); +} + +TEST(CpuMonitorTest, TestCpuMonitor) { + CpuMonitor monitor(Thread::Current()); + CpuLoadListener listener; + monitor.SignalUpdate.connect(&listener, &CpuLoadListener::OnCpuLoad); + EXPECT_TRUE(monitor.Start(10)); + // We have checked cpu load more than twice. + EXPECT_TRUE_WAIT(listener.count() > 2, 1000); + EXPECT_GT(listener.current_cpus(), 0); + EXPECT_GT(listener.cpus(), 0); + EXPECT_GE(listener.process_load(), .0f); + EXPECT_GE(listener.system_load(), .0f); + + monitor.Stop(); + // Wait 20 ms to ake sure all signals are delivered. + Thread::Current()->ProcessMessages(20); + int old_count = listener.count(); + Thread::Current()->ProcessMessages(20); + // Verfy no more siganls. + EXPECT_EQ(old_count, listener.count()); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/crc32.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/crc32.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/crc32.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/crc32.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,52 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/crc32.h" + +#include "webrtc/base/basicdefs.h" + +namespace rtc { + +// This implementation is based on the sample implementation in RFC 1952. + +// CRC32 polynomial, in reversed form. +// See RFC 1952, or http://en.wikipedia.org/wiki/Cyclic_redundancy_check +static const uint32 kCrc32Polynomial = 0xEDB88320; +static uint32 kCrc32Table[256] = { 0 }; + +static void EnsureCrc32TableInited() { + if (kCrc32Table[ARRAY_SIZE(kCrc32Table) - 1]) + return; // already inited + for (uint32 i = 0; i < ARRAY_SIZE(kCrc32Table); ++i) { + uint32 c = i; + for (size_t j = 0; j < 8; ++j) { + if (c & 1) { + c = kCrc32Polynomial ^ (c >> 1); + } else { + c >>= 1; + } + } + kCrc32Table[i] = c; + } +} + +uint32 UpdateCrc32(uint32 start, const void* buf, size_t len) { + EnsureCrc32TableInited(); + + uint32 c = start ^ 0xFFFFFFFF; + const uint8* u = static_cast(buf); + for (size_t i = 0; i < len; ++i) { + c = kCrc32Table[(c ^ u[i]) & 0xFF] ^ (c >> 8); + } + return c ^ 0xFFFFFFFF; +} + +} // namespace rtc + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/crc32.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/crc32.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/crc32.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/crc32.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_CRC32_H_ +#define WEBRTC_BASE_CRC32_H_ + +#include + +#include "webrtc/base/basictypes.h" + +namespace rtc { + +// Updates a CRC32 checksum with |len| bytes from |buf|. |initial| holds the +// checksum result from the previous update; for the first call, it should be 0. +uint32 UpdateCrc32(uint32 initial, const void* buf, size_t len); + +// Computes a CRC32 checksum using |len| bytes from |buf|. +inline uint32 ComputeCrc32(const void* buf, size_t len) { + return UpdateCrc32(0, buf, len); +} +inline uint32 ComputeCrc32(const std::string& str) { + return ComputeCrc32(str.c_str(), str.size()); +} + +} // namespace rtc + +#endif // WEBRTC_BASE_CRC32_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/crc32_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/crc32_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/crc32_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/crc32_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,35 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/crc32.h" +#include "webrtc/base/gunit.h" + +#include + +namespace rtc { + +TEST(Crc32Test, TestBasic) { + EXPECT_EQ(0U, ComputeCrc32("")); + EXPECT_EQ(0x352441C2U, ComputeCrc32("abc")); + EXPECT_EQ(0x171A3F5FU, + ComputeCrc32("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")); +} + +TEST(Crc32Test, TestMultipleUpdates) { + std::string input = + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + uint32 c = 0; + for (size_t i = 0; i < input.size(); ++i) { + c = UpdateCrc32(c, &input[i], 1); + } + EXPECT_EQ(0x171A3F5FU, c); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/criticalsection.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/criticalsection.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/criticalsection.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/criticalsection.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,180 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_CRITICALSECTION_H__ +#define WEBRTC_BASE_CRITICALSECTION_H__ + +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/thread_annotations.h" + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32.h" +#endif + +#if defined(WEBRTC_POSIX) +#include +#endif + +#ifdef _DEBUG +#define CS_TRACK_OWNER 1 +#endif // _DEBUG + +#if CS_TRACK_OWNER +#define TRACK_OWNER(x) x +#else // !CS_TRACK_OWNER +#define TRACK_OWNER(x) +#endif // !CS_TRACK_OWNER + +namespace rtc { + +#if defined(WEBRTC_WIN) +class LOCKABLE CriticalSection { + public: + CriticalSection() { + InitializeCriticalSection(&crit_); + // Windows docs say 0 is not a valid thread id + TRACK_OWNER(thread_ = 0); + } + ~CriticalSection() { + DeleteCriticalSection(&crit_); + } + void Enter() EXCLUSIVE_LOCK_FUNCTION() { + EnterCriticalSection(&crit_); + TRACK_OWNER(thread_ = GetCurrentThreadId()); + } + bool TryEnter() EXCLUSIVE_TRYLOCK_FUNCTION(true) { + if (TryEnterCriticalSection(&crit_) != FALSE) { + TRACK_OWNER(thread_ = GetCurrentThreadId()); + return true; + } + return false; + } + void Leave() UNLOCK_FUNCTION() { + TRACK_OWNER(thread_ = 0); + LeaveCriticalSection(&crit_); + } + +#if CS_TRACK_OWNER + bool CurrentThreadIsOwner() const { return thread_ == GetCurrentThreadId(); } +#endif // CS_TRACK_OWNER + + private: + CRITICAL_SECTION crit_; + TRACK_OWNER(DWORD thread_); // The section's owning thread id +}; +#endif // WEBRTC_WIN + +#if defined(WEBRTC_POSIX) +class LOCKABLE CriticalSection { + public: + CriticalSection() { + pthread_mutexattr_t mutex_attribute; + pthread_mutexattr_init(&mutex_attribute); + pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mutex_, &mutex_attribute); + pthread_mutexattr_destroy(&mutex_attribute); + TRACK_OWNER(thread_ = 0); + } + ~CriticalSection() { + pthread_mutex_destroy(&mutex_); + } + void Enter() EXCLUSIVE_LOCK_FUNCTION() { + pthread_mutex_lock(&mutex_); + TRACK_OWNER(thread_ = pthread_self()); + } + bool TryEnter() EXCLUSIVE_TRYLOCK_FUNCTION(true) { + if (pthread_mutex_trylock(&mutex_) == 0) { + TRACK_OWNER(thread_ = pthread_self()); + return true; + } + return false; + } + void Leave() UNLOCK_FUNCTION() { + TRACK_OWNER(thread_ = 0); + pthread_mutex_unlock(&mutex_); + } + +#if CS_TRACK_OWNER + bool CurrentThreadIsOwner() const { return pthread_equal(thread_, pthread_self()); } +#endif // CS_TRACK_OWNER + + private: + pthread_mutex_t mutex_; + TRACK_OWNER(pthread_t thread_); +}; +#endif // WEBRTC_POSIX + +// CritScope, for serializing execution through a scope. +class SCOPED_LOCKABLE CritScope { + public: + explicit CritScope(CriticalSection *pcrit) EXCLUSIVE_LOCK_FUNCTION(pcrit) { + pcrit_ = pcrit; + pcrit_->Enter(); + } + ~CritScope() UNLOCK_FUNCTION() { + pcrit_->Leave(); + } + private: + CriticalSection *pcrit_; + DISALLOW_COPY_AND_ASSIGN(CritScope); +}; + +// Tries to lock a critical section on construction via +// CriticalSection::TryEnter, and unlocks on destruction if the +// lock was taken. Never blocks. +// +// IMPORTANT: Unlike CritScope, the lock may not be owned by this thread in +// subsequent code. Users *must* check locked() to determine if the +// lock was taken. If you're not calling locked(), you're doing it wrong! +class TryCritScope { + public: + explicit TryCritScope(CriticalSection *pcrit) { + pcrit_ = pcrit; + locked_ = pcrit_->TryEnter(); + } + ~TryCritScope() { + if (locked_) { + pcrit_->Leave(); + } + } + bool locked() const { + return locked_; + } + private: + CriticalSection *pcrit_; + bool locked_; + DISALLOW_COPY_AND_ASSIGN(TryCritScope); +}; + +// TODO: Move this to atomicops.h, which can't be done easily because of +// complex compile rules. +class AtomicOps { + public: +#if defined(WEBRTC_WIN) + // Assumes sizeof(int) == sizeof(LONG), which it is on Win32 and Win64. + static int Increment(int* i) { + return ::InterlockedIncrement(reinterpret_cast(i)); + } + static int Decrement(int* i) { + return ::InterlockedDecrement(reinterpret_cast(i)); + } +#else + static int Increment(int* i) { + return __sync_add_and_fetch(i, 1); + } + static int Decrement(int* i) { + return __sync_sub_and_fetch(i, 1); + } +#endif +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_CRITICALSECTION_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/criticalsection_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/criticalsection_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/criticalsection_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/criticalsection_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,147 @@ +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include + +#include "webrtc/base/criticalsection.h" +#include "webrtc/base/event.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/scopedptrcollection.h" +#include "webrtc/base/thread.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace rtc { + +namespace { + +const int kLongTime = 10000; // 10 seconds +const int kNumThreads = 16; +const int kOperationsToRun = 1000; + +template +class AtomicOpRunner : public MessageHandler { + public: + explicit AtomicOpRunner(int initial_value) + : value_(initial_value), + threads_active_(0), + start_event_(true, false), + done_event_(true, false) {} + + int value() const { return value_; } + + bool Run() { + // Signal all threads to start. + start_event_.Set(); + + // Wait for all threads to finish. + return done_event_.Wait(kLongTime); + } + + void SetExpectedThreadCount(int count) { + threads_active_ = count; + } + + virtual void OnMessage(Message* msg) { + std::vector values; + values.reserve(kOperationsToRun); + + // Wait to start. + ASSERT_TRUE(start_event_.Wait(kLongTime)); + + // Generate a bunch of values by updating value_ atomically. + for (int i = 0; i < kOperationsToRun; ++i) { + values.push_back(T::AtomicOp(&value_)); + } + + { // Add them all to the set. + CritScope cs(&all_values_crit_); + for (size_t i = 0; i < values.size(); ++i) { + std::pair::iterator, bool> result = + all_values_.insert(values[i]); + // Each value should only be taken by one thread, so if this value + // has already been added, something went wrong. + EXPECT_TRUE(result.second) + << "Thread=" << Thread::Current() << " value=" << values[i]; + } + } + + // Signal that we're done. + if (AtomicOps::Decrement(&threads_active_) == 0) { + done_event_.Set(); + } + } + + private: + int value_; + int threads_active_; + CriticalSection all_values_crit_; + std::set all_values_; + Event start_event_; + Event done_event_; +}; + +struct IncrementOp { + static int AtomicOp(int* i) { return AtomicOps::Increment(i); } +}; + +struct DecrementOp { + static int AtomicOp(int* i) { return AtomicOps::Decrement(i); } +}; + +void StartThreads(ScopedPtrCollection* threads, + MessageHandler* handler) { + for (int i = 0; i < kNumThreads; ++i) { + Thread* thread = new Thread(); + thread->Start(); + thread->Post(handler); + threads->PushBack(thread); + } +} + +} // namespace + +TEST(AtomicOpsTest, Simple) { + int value = 0; + EXPECT_EQ(1, AtomicOps::Increment(&value)); + EXPECT_EQ(1, value); + EXPECT_EQ(2, AtomicOps::Increment(&value)); + EXPECT_EQ(2, value); + EXPECT_EQ(1, AtomicOps::Decrement(&value)); + EXPECT_EQ(1, value); + EXPECT_EQ(0, AtomicOps::Decrement(&value)); + EXPECT_EQ(0, value); +} + +TEST(AtomicOpsTest, Increment) { + // Create and start lots of threads. + AtomicOpRunner runner(0); + ScopedPtrCollection threads; + StartThreads(&threads, &runner); + runner.SetExpectedThreadCount(kNumThreads); + + // Release the hounds! + EXPECT_TRUE(runner.Run()); + EXPECT_EQ(kOperationsToRun * kNumThreads, runner.value()); +} + +TEST(AtomicOpsTest, Decrement) { + // Create and start lots of threads. + AtomicOpRunner runner(kOperationsToRun * kNumThreads); + ScopedPtrCollection threads; + StartThreads(&threads, &runner); + runner.SetExpectedThreadCount(kNumThreads); + + // Release the hounds! + EXPECT_TRUE(runner.Run()); + EXPECT_EQ(0, runner.value()); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/cryptstring.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/cryptstring.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/cryptstring.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/cryptstring.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,183 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef _WEBRTC_BASE_CRYPTSTRING_H_ +#define _WEBRTC_BASE_CRYPTSTRING_H_ + +#include + +#include +#include + +#include "webrtc/base/linked_ptr.h" +#include "webrtc/base/scoped_ptr.h" + +namespace rtc { + +class CryptStringImpl { +public: + virtual ~CryptStringImpl() {} + virtual size_t GetLength() const = 0; + virtual void CopyTo(char * dest, bool nullterminate) const = 0; + virtual std::string UrlEncode() const = 0; + virtual CryptStringImpl * Copy() const = 0; + virtual void CopyRawTo(std::vector * dest) const = 0; +}; + +class EmptyCryptStringImpl : public CryptStringImpl { +public: + virtual ~EmptyCryptStringImpl() {} + virtual size_t GetLength() const { return 0; } + virtual void CopyTo(char * dest, bool nullterminate) const { + if (nullterminate) { + *dest = '\0'; + } + } + virtual std::string UrlEncode() const { return ""; } + virtual CryptStringImpl * Copy() const { return new EmptyCryptStringImpl(); } + virtual void CopyRawTo(std::vector * dest) const { + dest->clear(); + } +}; + +class CryptString { +public: + CryptString() : impl_(new EmptyCryptStringImpl()) {} + size_t GetLength() const { return impl_->GetLength(); } + void CopyTo(char * dest, bool nullterminate) const { impl_->CopyTo(dest, nullterminate); } + CryptString(const CryptString & other) : impl_(other.impl_->Copy()) {} + explicit CryptString(const CryptStringImpl & impl) : impl_(impl.Copy()) {} + CryptString & operator=(const CryptString & other) { + if (this != &other) { + impl_.reset(other.impl_->Copy()); + } + return *this; + } + void Clear() { impl_.reset(new EmptyCryptStringImpl()); } + std::string UrlEncode() const { return impl_->UrlEncode(); } + void CopyRawTo(std::vector * dest) const { + return impl_->CopyRawTo(dest); + } + +private: + scoped_ptr impl_; +}; + + +// Used for constructing strings where a password is involved and we +// need to ensure that we zero memory afterwards +class FormatCryptString { +public: + FormatCryptString() { + storage_ = new char[32]; + capacity_ = 32; + length_ = 0; + storage_[0] = 0; + } + + void Append(const std::string & text) { + Append(text.data(), text.length()); + } + + void Append(const char * data, size_t length) { + EnsureStorage(length_ + length + 1); + memcpy(storage_ + length_, data, length); + length_ += length; + storage_[length_] = '\0'; + } + + void Append(const CryptString * password) { + size_t len = password->GetLength(); + EnsureStorage(length_ + len + 1); + password->CopyTo(storage_ + length_, true); + length_ += len; + } + + size_t GetLength() { + return length_; + } + + const char * GetData() { + return storage_; + } + + + // Ensures storage of at least n bytes + void EnsureStorage(size_t n) { + if (capacity_ >= n) { + return; + } + + size_t old_capacity = capacity_; + char * old_storage = storage_; + + for (;;) { + capacity_ *= 2; + if (capacity_ >= n) + break; + } + + storage_ = new char[capacity_]; + + if (old_capacity) { + memcpy(storage_, old_storage, length_); + + // zero memory in a way that an optimizer won't optimize it out + old_storage[0] = 0; + for (size_t i = 1; i < old_capacity; i++) { + old_storage[i] = old_storage[i - 1]; + } + delete[] old_storage; + } + } + + ~FormatCryptString() { + if (capacity_) { + storage_[0] = 0; + for (size_t i = 1; i < capacity_; i++) { + storage_[i] = storage_[i - 1]; + } + } + delete[] storage_; + } +private: + char * storage_; + size_t capacity_; + size_t length_; +}; + +class InsecureCryptStringImpl : public CryptStringImpl { + public: + std::string& password() { return password_; } + const std::string& password() const { return password_; } + + virtual ~InsecureCryptStringImpl() {} + virtual size_t GetLength() const { return password_.size(); } + virtual void CopyTo(char * dest, bool nullterminate) const { + memcpy(dest, password_.data(), password_.size()); + if (nullterminate) dest[password_.size()] = 0; + } + virtual std::string UrlEncode() const { return password_; } + virtual CryptStringImpl * Copy() const { + InsecureCryptStringImpl * copy = new InsecureCryptStringImpl; + copy->password() = password_; + return copy; + } + virtual void CopyRawTo(std::vector * dest) const { + dest->resize(password_.size()); + memcpy(&dest->front(), password_.data(), password_.size()); + } + private: + std::string password_; +}; + +} + +#endif // _WEBRTC_BASE_CRYPTSTRING_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/dbus.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/dbus.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/dbus.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/dbus.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,396 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifdef HAVE_DBUS_GLIB + +#include "webrtc/base/dbus.h" + +#include + +#include "webrtc/base/logging.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +// Avoid static object construction/destruction on startup/shutdown. +static pthread_once_t g_dbus_init_once = PTHREAD_ONCE_INIT; +static LibDBusGlibSymbolTable *g_dbus_symbol = NULL; + +// Releases DBus-Glib symbols. +static void ReleaseDBusGlibSymbol() { + if (g_dbus_symbol != NULL) { + delete g_dbus_symbol; + g_dbus_symbol = NULL; + } +} + +// Loads DBus-Glib symbols. +static void InitializeDBusGlibSymbol() { + // This is thread safe. + if (NULL == g_dbus_symbol) { + g_dbus_symbol = new LibDBusGlibSymbolTable(); + + // Loads dbus-glib + if (NULL == g_dbus_symbol || !g_dbus_symbol->Load()) { + LOG(LS_WARNING) << "Failed to load dbus-glib symbol table."; + ReleaseDBusGlibSymbol(); + } else { + // Nothing we can do if atexit() failed. Just ignore its returned value. + atexit(ReleaseDBusGlibSymbol); + } + } +} + +inline static LibDBusGlibSymbolTable *GetSymbols() { + return DBusMonitor::GetDBusGlibSymbolTable(); +} + +// Implementation of class DBusSigMessageData +DBusSigMessageData::DBusSigMessageData(DBusMessage *message) + : TypedMessageData(message) { + GetSymbols()->dbus_message_ref()(data()); +} + +DBusSigMessageData::~DBusSigMessageData() { + GetSymbols()->dbus_message_unref()(data()); +} + +// Implementation of class DBusSigFilter + +// Builds a DBus filter string from given DBus path, interface and member. +std::string DBusSigFilter::BuildFilterString(const std::string &path, + const std::string &interface, + const std::string &member) { + std::string ret(DBUS_TYPE "='" DBUS_SIGNAL "'"); + if (!path.empty()) { + ret += ("," DBUS_PATH "='"); + ret += path; + ret += "'"; + } + if (!interface.empty()) { + ret += ("," DBUS_INTERFACE "='"); + ret += interface; + ret += "'"; + } + if (!member.empty()) { + ret += ("," DBUS_MEMBER "='"); + ret += member; + ret += "'"; + } + return ret; +} + +// Forwards the message to the given instance. +DBusHandlerResult DBusSigFilter::DBusCallback(DBusConnection *dbus_conn, + DBusMessage *message, + void *instance) { + ASSERT(instance); + if (instance) { + return static_cast(instance)->Callback(message); + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +// Posts a message to caller thread. +DBusHandlerResult DBusSigFilter::Callback(DBusMessage *message) { + if (caller_thread_) { + caller_thread_->Post(this, DSM_SIGNAL, new DBusSigMessageData(message)); + } + // Don't "eat" the message here. Let it pop up. + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +// From MessageHandler. +void DBusSigFilter::OnMessage(Message *message) { + if (message != NULL && DSM_SIGNAL == message->message_id) { + DBusSigMessageData *msg = + static_cast(message->pdata); + if (msg) { + ProcessSignal(msg->data()); + delete msg; + } + } +} + +// Definition of private class DBusMonitoringThread. +// It creates a worker-thread to listen signals on DBus. The worker-thread will +// be running in a priate GMainLoop forever until either Stop() has been invoked +// or it hits an error. +class DBusMonitor::DBusMonitoringThread : public rtc::Thread { + public: + explicit DBusMonitoringThread(DBusMonitor *monitor, + GMainContext *context, + GMainLoop *mainloop, + std::vector *filter_list) + : monitor_(monitor), + context_(context), + mainloop_(mainloop), + connection_(NULL), + idle_source_(NULL), + filter_list_(filter_list) { + ASSERT(monitor_); + ASSERT(context_); + ASSERT(mainloop_); + ASSERT(filter_list_); + } + + virtual ~DBusMonitoringThread() { + Stop(); + } + + // Override virtual method of Thread. Context: worker-thread. + virtual void Run() { + ASSERT(NULL == connection_); + + // Setup DBus connection and start monitoring. + monitor_->OnMonitoringStatusChanged(DMS_INITIALIZING); + if (!Setup()) { + LOG(LS_ERROR) << "DBus monitoring setup failed."; + monitor_->OnMonitoringStatusChanged(DMS_FAILED); + CleanUp(); + return; + } + monitor_->OnMonitoringStatusChanged(DMS_RUNNING); + g_main_loop_run(mainloop_); + monitor_->OnMonitoringStatusChanged(DMS_STOPPED); + + // Done normally. Clean up DBus connection. + CleanUp(); + return; + } + + // Override virtual method of Thread. Context: caller-thread. + virtual void Stop() { + ASSERT(NULL == idle_source_); + // Add an idle source and let the gmainloop quit on idle. + idle_source_ = g_idle_source_new(); + if (idle_source_) { + g_source_set_callback(idle_source_, &Idle, this, NULL); + g_source_attach(idle_source_, context_); + } else { + LOG(LS_ERROR) << "g_idle_source_new() failed."; + QuitGMainloop(); // Try to quit anyway. + } + + Thread::Stop(); // Wait for the thread. + } + + private: + // Registers all DBus filters. + void RegisterAllFilters() { + ASSERT(NULL != GetSymbols()->dbus_g_connection_get_connection()( + connection_)); + + for (std::vector::iterator it = filter_list_->begin(); + it != filter_list_->end(); ++it) { + DBusSigFilter *filter = (*it); + if (!filter) { + LOG(LS_ERROR) << "DBusSigFilter list corrupted."; + continue; + } + + GetSymbols()->dbus_bus_add_match()( + GetSymbols()->dbus_g_connection_get_connection()(connection_), + filter->filter().c_str(), NULL); + + if (!GetSymbols()->dbus_connection_add_filter()( + GetSymbols()->dbus_g_connection_get_connection()(connection_), + &DBusSigFilter::DBusCallback, filter, NULL)) { + LOG(LS_ERROR) << "dbus_connection_add_filter() failed." + << "Filter: " << filter->filter(); + continue; + } + } + } + + // Unregisters all DBus filters. + void UnRegisterAllFilters() { + ASSERT(NULL != GetSymbols()->dbus_g_connection_get_connection()( + connection_)); + + for (std::vector::iterator it = filter_list_->begin(); + it != filter_list_->end(); ++it) { + DBusSigFilter *filter = (*it); + if (!filter) { + LOG(LS_ERROR) << "DBusSigFilter list corrupted."; + continue; + } + GetSymbols()->dbus_connection_remove_filter()( + GetSymbols()->dbus_g_connection_get_connection()(connection_), + &DBusSigFilter::DBusCallback, filter); + } + } + + // Sets up the monitoring thread. + bool Setup() { + g_main_context_push_thread_default(context_); + + // Start connection to dbus. + // If dbus daemon is not running, returns false immediately. + connection_ = GetSymbols()->dbus_g_bus_get_private()(monitor_->type_, + context_, NULL); + if (NULL == connection_) { + LOG(LS_ERROR) << "dbus_g_bus_get_private() unable to get connection."; + return false; + } + if (NULL == GetSymbols()->dbus_g_connection_get_connection()(connection_)) { + LOG(LS_ERROR) << "dbus_g_connection_get_connection() returns NULL. " + << "DBus daemon is probably not running."; + return false; + } + + // Application don't exit if DBus daemon die. + GetSymbols()->dbus_connection_set_exit_on_disconnect()( + GetSymbols()->dbus_g_connection_get_connection()(connection_), FALSE); + + // Connect all filters. + RegisterAllFilters(); + + return true; + } + + // Cleans up the monitoring thread. + void CleanUp() { + if (idle_source_) { + // We did an attach() with the GSource, so we need to destroy() it. + g_source_destroy(idle_source_); + // We need to unref() the GSource to end the last reference we got. + g_source_unref(idle_source_); + idle_source_ = NULL; + } + if (connection_) { + if (GetSymbols()->dbus_g_connection_get_connection()(connection_)) { + UnRegisterAllFilters(); + GetSymbols()->dbus_connection_close()( + GetSymbols()->dbus_g_connection_get_connection()(connection_)); + } + GetSymbols()->dbus_g_connection_unref()(connection_); + connection_ = NULL; + } + g_main_loop_unref(mainloop_); + mainloop_ = NULL; + g_main_context_unref(context_); + context_ = NULL; + } + + // Handles callback on Idle. We only add this source when ready to stop. + static gboolean Idle(gpointer data) { + static_cast(data)->QuitGMainloop(); + return TRUE; + } + + // We only hit this when ready to quit. + void QuitGMainloop() { + g_main_loop_quit(mainloop_); + } + + DBusMonitor *monitor_; + + GMainContext *context_; + GMainLoop *mainloop_; + DBusGConnection *connection_; + GSource *idle_source_; + + std::vector *filter_list_; +}; + +// Implementation of class DBusMonitor + +// Returns DBus-Glib symbol handle. Initialize it first if hasn't. +LibDBusGlibSymbolTable *DBusMonitor::GetDBusGlibSymbolTable() { + // This is multi-thread safe. + pthread_once(&g_dbus_init_once, InitializeDBusGlibSymbol); + + return g_dbus_symbol; +}; + +// Creates an instance of DBusMonitor +DBusMonitor *DBusMonitor::Create(DBusBusType type) { + if (NULL == DBusMonitor::GetDBusGlibSymbolTable()) { + return NULL; + } + return new DBusMonitor(type); +} + +DBusMonitor::DBusMonitor(DBusBusType type) + : type_(type), + status_(DMS_NOT_INITIALIZED), + monitoring_thread_(NULL) { + ASSERT(type_ == DBUS_BUS_SYSTEM || type_ == DBUS_BUS_SESSION); +} + +DBusMonitor::~DBusMonitor() { + StopMonitoring(); +} + +bool DBusMonitor::AddFilter(DBusSigFilter *filter) { + if (monitoring_thread_) { + return false; + } + if (!filter) { + return false; + } + filter_list_.push_back(filter); + return true; +} + +bool DBusMonitor::StartMonitoring() { + if (!monitoring_thread_) { + g_type_init(); + g_thread_init(NULL); + GetSymbols()->dbus_g_thread_init()(); + + GMainContext *context = g_main_context_new(); + if (NULL == context) { + LOG(LS_ERROR) << "g_main_context_new() failed."; + return false; + } + + GMainLoop *mainloop = g_main_loop_new(context, FALSE); + if (NULL == mainloop) { + LOG(LS_ERROR) << "g_main_loop_new() failed."; + g_main_context_unref(context); + return false; + } + + monitoring_thread_ = new DBusMonitoringThread(this, context, mainloop, + &filter_list_); + if (monitoring_thread_ == NULL) { + LOG(LS_ERROR) << "Failed to create DBus monitoring thread."; + g_main_context_unref(context); + g_main_loop_unref(mainloop); + return false; + } + monitoring_thread_->Start(); + } + return true; +} + +bool DBusMonitor::StopMonitoring() { + if (monitoring_thread_) { + monitoring_thread_->Stop(); + monitoring_thread_ = NULL; + } + return true; +} + +DBusMonitor::DBusMonitorStatus DBusMonitor::GetStatus() { + return status_; +} + +void DBusMonitor::OnMonitoringStatusChanged(DBusMonitorStatus status) { + status_ = status; +} + +#undef LATE + +} // namespace rtc + +#endif // HAVE_DBUS_GLIB diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/dbus.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/dbus.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/dbus.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/dbus.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,168 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_DBUS_H_ +#define WEBRTC_BASE_DBUS_H_ + +#ifdef HAVE_DBUS_GLIB + +#include + +#include +#include + +#include "webrtc/base/libdbusglibsymboltable.h" +#include "webrtc/base/messagehandler.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +#define DBUS_TYPE "type" +#define DBUS_SIGNAL "signal" +#define DBUS_PATH "path" +#define DBUS_INTERFACE "interface" +#define DBUS_MEMBER "member" + +#ifdef CHROMEOS +#define CROS_PM_PATH "/" +#define CROS_PM_INTERFACE "org.chromium.PowerManager" +#define CROS_SIG_POWERCHANGED "PowerStateChanged" +#define CROS_VALUE_SLEEP "mem" +#define CROS_VALUE_RESUME "on" +#else +#define UP_PATH "/org/freedesktop/UPower" +#define UP_INTERFACE "org.freedesktop.UPower" +#define UP_SIG_SLEEPING "Sleeping" +#define UP_SIG_RESUMING "Resuming" +#endif // CHROMEOS + +// Wraps a DBus messages. +class DBusSigMessageData : public TypedMessageData { + public: + explicit DBusSigMessageData(DBusMessage *message); + ~DBusSigMessageData(); +}; + +// DBusSigFilter is an abstract class that defines the interface of DBus +// signal handling. +// The subclasses implement ProcessSignal() for various purposes. +// When a DBus signal comes, a DSM_SIGNAL message is posted to the caller thread +// which will then invokes ProcessSignal(). +class DBusSigFilter : protected MessageHandler { + public: + enum DBusSigMessage { DSM_SIGNAL }; + + // This filter string should ususally come from BuildFilterString() + explicit DBusSigFilter(const std::string &filter) + : caller_thread_(Thread::Current()), filter_(filter) { + } + + // Builds a DBus monitor filter string from given DBus path, interface, and + // member. + // See http://dbus.freedesktop.org/doc/api/html/group__DBusConnection.html + static std::string BuildFilterString(const std::string &path, + const std::string &interface, + const std::string &member); + + // Handles callback on DBus messages by DBus system. + static DBusHandlerResult DBusCallback(DBusConnection *dbus_conn, + DBusMessage *message, + void *instance); + + // Handles callback on DBus messages to each DBusSigFilter instance. + DBusHandlerResult Callback(DBusMessage *message); + + // From MessageHandler. + virtual void OnMessage(Message *message); + + // Returns the DBus monitor filter string. + const std::string &filter() const { return filter_; } + + private: + // On caller thread. + virtual void ProcessSignal(DBusMessage *message) = 0; + + Thread *caller_thread_; + const std::string filter_; +}; + +// DBusMonitor is a class for DBus signal monitoring. +// +// The caller-thread calls AddFilter() first to add the signals that it wants to +// monitor and then calls StartMonitoring() to start the monitoring. +// This will create a worker-thread which listens on DBus connection and sends +// DBus signals back through the callback. +// The worker-thread will be running forever until either StopMonitoring() is +// called from the caller-thread or the worker-thread hit some error. +// +// Programming model: +// 1. Caller-thread: Creates an object of DBusMonitor. +// 2. Caller-thread: Calls DBusMonitor::AddFilter() one or several times. +// 3. Caller-thread: StartMonitoring(). +// ... +// 4. Worker-thread: DBus signal recieved. Post a message to caller-thread. +// 5. Caller-thread: DBusFilterBase::ProcessSignal() is invoked. +// ... +// 6. Caller-thread: StopMonitoring(). +// +// Assumption: +// AddFilter(), StartMonitoring(), and StopMonitoring() methods are called by +// a single thread. Hence, there is no need to make them thread safe. +class DBusMonitor { + public: + // Status of DBus monitoring. + enum DBusMonitorStatus { + DMS_NOT_INITIALIZED, // Not initialized. + DMS_INITIALIZING, // Initializing the monitoring thread. + DMS_RUNNING, // Monitoring. + DMS_STOPPED, // Not monitoring. Stopped normally. + DMS_FAILED, // Not monitoring. Failed. + }; + + // Returns the DBus-Glib symbol table. + // We should only use this function to access DBus-Glib symbols. + static LibDBusGlibSymbolTable *GetDBusGlibSymbolTable(); + + // Creates an instance of DBusMonitor. + static DBusMonitor *Create(DBusBusType type); + ~DBusMonitor(); + + // Adds a filter to DBusMonitor. + bool AddFilter(DBusSigFilter *filter); + + // Starts DBus message monitoring. + bool StartMonitoring(); + + // Stops DBus message monitoring. + bool StopMonitoring(); + + // Gets the status of DBus monitoring. + DBusMonitorStatus GetStatus(); + + private: + // Forward declaration. Defined in the .cc file. + class DBusMonitoringThread; + + explicit DBusMonitor(DBusBusType type); + + // Updates status_ when monitoring status has changed. + void OnMonitoringStatusChanged(DBusMonitorStatus status); + + DBusBusType type_; + DBusMonitorStatus status_; + DBusMonitoringThread *monitoring_thread_; + std::vector filter_list_; +}; + +} // namespace rtc + +#endif // HAVE_DBUS_GLIB + +#endif // WEBRTC_BASE_DBUS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/dbus_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/dbus_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/dbus_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/dbus_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,232 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifdef HAVE_DBUS_GLIB + +#include "webrtc/base/dbus.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +#define SIG_NAME "NameAcquired" + +static const uint32 kTimeoutMs = 5000U; + +class DBusSigFilterTest : public DBusSigFilter { + public: + // DBusSigFilterTest listens on DBus service itself for "NameAcquired" signal. + // This signal should be received when the application connects to DBus + // service and gains ownership of a name. + // http://dbus.freedesktop.org/doc/dbus-specification.html + DBusSigFilterTest() + : DBusSigFilter(GetFilter()), + message_received_(false) { + } + + bool MessageReceived() { + return message_received_; + } + + private: + static std::string GetFilter() { + return rtc::DBusSigFilter::BuildFilterString("", "", SIG_NAME); + } + + // Implement virtual method of DBusSigFilter. On caller thread. + virtual void ProcessSignal(DBusMessage *message) { + EXPECT_TRUE(message != NULL); + message_received_ = true; + } + + bool message_received_; +}; + +TEST(DBusMonitorTest, StartStopStartStop) { + DBusSigFilterTest filter; + rtc::scoped_ptr monitor; + monitor.reset(rtc::DBusMonitor::Create(DBUS_BUS_SYSTEM)); + if (monitor) { + EXPECT_TRUE(monitor->AddFilter(&filter)); + + EXPECT_TRUE(monitor->StopMonitoring()); + EXPECT_EQ(monitor->GetStatus(), DBusMonitor::DMS_NOT_INITIALIZED); + + EXPECT_TRUE(monitor->StartMonitoring()); + EXPECT_EQ_WAIT(DBusMonitor::DMS_RUNNING, monitor->GetStatus(), kTimeoutMs); + EXPECT_TRUE(monitor->StopMonitoring()); + EXPECT_EQ(monitor->GetStatus(), DBusMonitor::DMS_STOPPED); + EXPECT_TRUE(monitor->StopMonitoring()); + EXPECT_EQ(monitor->GetStatus(), DBusMonitor::DMS_STOPPED); + + EXPECT_TRUE(monitor->StartMonitoring()); + EXPECT_EQ_WAIT(DBusMonitor::DMS_RUNNING, monitor->GetStatus(), kTimeoutMs); + EXPECT_TRUE(monitor->StartMonitoring()); + EXPECT_EQ(monitor->GetStatus(), DBusMonitor::DMS_RUNNING); + EXPECT_TRUE(monitor->StopMonitoring()); + EXPECT_EQ(monitor->GetStatus(), DBusMonitor::DMS_STOPPED); + } else { + LOG(LS_WARNING) << "DBus Monitor not started. Skipping test."; + } +} + +// DBusMonitorTest listens on DBus service itself for "NameAcquired" signal. +// This signal should be received when the application connects to DBus +// service and gains ownership of a name. +// This test is to make sure that we capture the "NameAcquired" signal. +TEST(DBusMonitorTest, ReceivedNameAcquiredSignal) { + DBusSigFilterTest filter; + rtc::scoped_ptr monitor; + monitor.reset(rtc::DBusMonitor::Create(DBUS_BUS_SYSTEM)); + if (monitor) { + EXPECT_TRUE(monitor->AddFilter(&filter)); + + EXPECT_TRUE(monitor->StartMonitoring()); + EXPECT_EQ_WAIT(DBusMonitor::DMS_RUNNING, monitor->GetStatus(), kTimeoutMs); + EXPECT_TRUE_WAIT(filter.MessageReceived(), kTimeoutMs); + EXPECT_TRUE(monitor->StopMonitoring()); + EXPECT_EQ(monitor->GetStatus(), DBusMonitor::DMS_STOPPED); + } else { + LOG(LS_WARNING) << "DBus Monitor not started. Skipping test."; + } +} + +TEST(DBusMonitorTest, ConcurrentMonitors) { + DBusSigFilterTest filter1; + rtc::scoped_ptr monitor1; + monitor1.reset(rtc::DBusMonitor::Create(DBUS_BUS_SYSTEM)); + if (monitor1) { + EXPECT_TRUE(monitor1->AddFilter(&filter1)); + DBusSigFilterTest filter2; + rtc::scoped_ptr monitor2; + monitor2.reset(rtc::DBusMonitor::Create(DBUS_BUS_SYSTEM)); + EXPECT_TRUE(monitor2->AddFilter(&filter2)); + + EXPECT_TRUE(monitor1->StartMonitoring()); + EXPECT_EQ_WAIT(DBusMonitor::DMS_RUNNING, monitor1->GetStatus(), kTimeoutMs); + EXPECT_TRUE(monitor2->StartMonitoring()); + EXPECT_EQ_WAIT(DBusMonitor::DMS_RUNNING, monitor2->GetStatus(), kTimeoutMs); + + EXPECT_TRUE_WAIT(filter2.MessageReceived(), kTimeoutMs); + EXPECT_TRUE(monitor2->StopMonitoring()); + EXPECT_EQ(monitor2->GetStatus(), DBusMonitor::DMS_STOPPED); + + EXPECT_TRUE_WAIT(filter1.MessageReceived(), kTimeoutMs); + EXPECT_TRUE(monitor1->StopMonitoring()); + EXPECT_EQ(monitor1->GetStatus(), DBusMonitor::DMS_STOPPED); + } else { + LOG(LS_WARNING) << "DBus Monitor not started. Skipping test."; + } +} + +TEST(DBusMonitorTest, ConcurrentFilters) { + DBusSigFilterTest filter1; + DBusSigFilterTest filter2; + rtc::scoped_ptr monitor; + monitor.reset(rtc::DBusMonitor::Create(DBUS_BUS_SYSTEM)); + if (monitor) { + EXPECT_TRUE(monitor->AddFilter(&filter1)); + EXPECT_TRUE(monitor->AddFilter(&filter2)); + + EXPECT_TRUE(monitor->StartMonitoring()); + EXPECT_EQ_WAIT(DBusMonitor::DMS_RUNNING, monitor->GetStatus(), kTimeoutMs); + + EXPECT_TRUE_WAIT(filter1.MessageReceived(), kTimeoutMs); + EXPECT_TRUE_WAIT(filter2.MessageReceived(), kTimeoutMs); + + EXPECT_TRUE(monitor->StopMonitoring()); + EXPECT_EQ(monitor->GetStatus(), DBusMonitor::DMS_STOPPED); + } else { + LOG(LS_WARNING) << "DBus Monitor not started. Skipping test."; + } +} + +TEST(DBusMonitorTest, NoAddFilterIfRunning) { + DBusSigFilterTest filter1; + DBusSigFilterTest filter2; + rtc::scoped_ptr monitor; + monitor.reset(rtc::DBusMonitor::Create(DBUS_BUS_SYSTEM)); + if (monitor) { + EXPECT_TRUE(monitor->AddFilter(&filter1)); + + EXPECT_TRUE(monitor->StartMonitoring()); + EXPECT_EQ_WAIT(DBusMonitor::DMS_RUNNING, monitor->GetStatus(), kTimeoutMs); + EXPECT_FALSE(monitor->AddFilter(&filter2)); + + EXPECT_TRUE(monitor->StopMonitoring()); + EXPECT_EQ(monitor->GetStatus(), DBusMonitor::DMS_STOPPED); + } else { + LOG(LS_WARNING) << "DBus Monitor not started. Skipping test."; + } +} + +TEST(DBusMonitorTest, AddFilterAfterStop) { + DBusSigFilterTest filter1; + DBusSigFilterTest filter2; + rtc::scoped_ptr monitor; + monitor.reset(rtc::DBusMonitor::Create(DBUS_BUS_SYSTEM)); + if (monitor) { + EXPECT_TRUE(monitor->AddFilter(&filter1)); + EXPECT_TRUE(monitor->StartMonitoring()); + EXPECT_EQ_WAIT(DBusMonitor::DMS_RUNNING, monitor->GetStatus(), kTimeoutMs); + EXPECT_TRUE_WAIT(filter1.MessageReceived(), kTimeoutMs); + EXPECT_TRUE(monitor->StopMonitoring()); + EXPECT_EQ(monitor->GetStatus(), DBusMonitor::DMS_STOPPED); + + EXPECT_TRUE(monitor->AddFilter(&filter2)); + EXPECT_TRUE(monitor->StartMonitoring()); + EXPECT_EQ_WAIT(DBusMonitor::DMS_RUNNING, monitor->GetStatus(), kTimeoutMs); + EXPECT_TRUE_WAIT(filter1.MessageReceived(), kTimeoutMs); + EXPECT_TRUE_WAIT(filter2.MessageReceived(), kTimeoutMs); + EXPECT_TRUE(monitor->StopMonitoring()); + EXPECT_EQ(monitor->GetStatus(), DBusMonitor::DMS_STOPPED); + } else { + LOG(LS_WARNING) << "DBus Monitor not started. Skipping test."; + } +} + +TEST(DBusMonitorTest, StopRightAfterStart) { + DBusSigFilterTest filter; + rtc::scoped_ptr monitor; + monitor.reset(rtc::DBusMonitor::Create(DBUS_BUS_SYSTEM)); + if (monitor) { + EXPECT_TRUE(monitor->AddFilter(&filter)); + + EXPECT_TRUE(monitor->StartMonitoring()); + EXPECT_TRUE(monitor->StopMonitoring()); + + // Stop the monitoring thread right after it had been started. + // If the monitoring thread got a chance to receive a DBus signal, it would + // post a message to the main thread and signal the main thread wakeup. + // This message will be cleaned out automatically when the filter get + // destructed. Here we also consume the wakeup signal (if there is one) so + // that the testing (main) thread is reset to a clean state. + rtc::Thread::Current()->ProcessMessages(1); + } else { + LOG(LS_WARNING) << "DBus Monitor not started."; + } +} + +TEST(DBusSigFilter, BuildFilterString) { + EXPECT_EQ(DBusSigFilter::BuildFilterString("", "", ""), + (DBUS_TYPE "='" DBUS_SIGNAL "'")); + EXPECT_EQ(DBusSigFilter::BuildFilterString("p", "", ""), + (DBUS_TYPE "='" DBUS_SIGNAL "'," DBUS_PATH "='p'")); + EXPECT_EQ(DBusSigFilter::BuildFilterString("p","i", ""), + (DBUS_TYPE "='" DBUS_SIGNAL "'," DBUS_PATH "='p'," + DBUS_INTERFACE "='i'")); + EXPECT_EQ(DBusSigFilter::BuildFilterString("p","i","m"), + (DBUS_TYPE "='" DBUS_SIGNAL "'," DBUS_PATH "='p'," + DBUS_INTERFACE "='i'," DBUS_MEMBER "='m'")); +} + +} // namespace rtc + +#endif // HAVE_DBUS_GLIB diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/diskcache.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/diskcache.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/diskcache.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/diskcache.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,347 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32.h" +#endif + +#include "webrtc/base/common.h" +#include "webrtc/base/diskcache.h" +#include "webrtc/base/fileutils.h" +#include "webrtc/base/pathutils.h" +#include "webrtc/base/stream.h" +#include "webrtc/base/stringencode.h" +#include "webrtc/base/stringutils.h" + +#ifdef _DEBUG +#define TRANSPARENT_CACHE_NAMES 1 +#else // !_DEBUG +#define TRANSPARENT_CACHE_NAMES 0 +#endif // !_DEBUG + +namespace rtc { + +class DiskCache; + +/////////////////////////////////////////////////////////////////////////////// +// DiskCacheAdapter +/////////////////////////////////////////////////////////////////////////////// + +class DiskCacheAdapter : public StreamAdapterInterface { +public: + DiskCacheAdapter(const DiskCache* cache, const std::string& id, size_t index, + StreamInterface* stream) + : StreamAdapterInterface(stream), cache_(cache), id_(id), index_(index) + { } + virtual ~DiskCacheAdapter() { + Close(); + cache_->ReleaseResource(id_, index_); + } + +private: + const DiskCache* cache_; + std::string id_; + size_t index_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// DiskCache +/////////////////////////////////////////////////////////////////////////////// + +DiskCache::DiskCache() : max_cache_(0), total_size_(0), total_accessors_(0) { +} + +DiskCache::~DiskCache() { + ASSERT(0 == total_accessors_); +} + +bool DiskCache::Initialize(const std::string& folder, size_t size) { + if (!folder_.empty() || !Filesystem::CreateFolder(folder)) + return false; + + folder_ = folder; + max_cache_ = size; + ASSERT(0 == total_size_); + + if (!InitializeEntries()) + return false; + + return CheckLimit(); +} + +bool DiskCache::Purge() { + if (folder_.empty()) + return false; + + if (total_accessors_ > 0) { + LOG_F(LS_WARNING) << "Cache files open"; + return false; + } + + if (!PurgeFiles()) + return false; + + map_.clear(); + return true; +} + +bool DiskCache::LockResource(const std::string& id) { + Entry* entry = GetOrCreateEntry(id, true); + if (LS_LOCKED == entry->lock_state) + return false; + if ((LS_UNLOCKED == entry->lock_state) && (entry->accessors > 0)) + return false; + if ((total_size_ > max_cache_) && !CheckLimit()) { + LOG_F(LS_WARNING) << "Cache overfull"; + return false; + } + entry->lock_state = LS_LOCKED; + return true; +} + +StreamInterface* DiskCache::WriteResource(const std::string& id, size_t index) { + Entry* entry = GetOrCreateEntry(id, false); + if (LS_LOCKED != entry->lock_state) + return NULL; + + size_t previous_size = 0; + std::string filename(IdToFilename(id, index)); + FileStream::GetSize(filename, &previous_size); + ASSERT(previous_size <= entry->size); + if (previous_size > entry->size) { + previous_size = entry->size; + } + + scoped_ptr file(new FileStream); + if (!file->Open(filename, "wb", NULL)) { + LOG_F(LS_ERROR) << "Couldn't create cache file"; + return NULL; + } + + entry->streams = stdmax(entry->streams, index + 1); + entry->size -= previous_size; + total_size_ -= previous_size; + + entry->accessors += 1; + total_accessors_ += 1; + return new DiskCacheAdapter(this, id, index, file.release()); +} + +bool DiskCache::UnlockResource(const std::string& id) { + Entry* entry = GetOrCreateEntry(id, false); + if (LS_LOCKED != entry->lock_state) + return false; + + if (entry->accessors > 0) { + entry->lock_state = LS_UNLOCKING; + } else { + entry->lock_state = LS_UNLOCKED; + entry->last_modified = time(0); + CheckLimit(); + } + return true; +} + +StreamInterface* DiskCache::ReadResource(const std::string& id, + size_t index) const { + const Entry* entry = GetEntry(id); + if (LS_UNLOCKED != entry->lock_state) + return NULL; + if (index >= entry->streams) + return NULL; + + scoped_ptr file(new FileStream); + if (!file->Open(IdToFilename(id, index), "rb", NULL)) + return NULL; + + entry->accessors += 1; + total_accessors_ += 1; + return new DiskCacheAdapter(this, id, index, file.release()); +} + +bool DiskCache::HasResource(const std::string& id) const { + const Entry* entry = GetEntry(id); + return (NULL != entry) && (entry->streams > 0); +} + +bool DiskCache::HasResourceStream(const std::string& id, size_t index) const { + const Entry* entry = GetEntry(id); + if ((NULL == entry) || (index >= entry->streams)) + return false; + + std::string filename = IdToFilename(id, index); + + return FileExists(filename); +} + +bool DiskCache::DeleteResource(const std::string& id) { + Entry* entry = GetOrCreateEntry(id, false); + if (!entry) + return true; + + if ((LS_UNLOCKED != entry->lock_state) || (entry->accessors > 0)) + return false; + + bool success = true; + for (size_t index = 0; index < entry->streams; ++index) { + std::string filename = IdToFilename(id, index); + + if (!FileExists(filename)) + continue; + + if (!DeleteFile(filename)) { + LOG_F(LS_ERROR) << "Couldn't remove cache file: " << filename; + success = false; + } + } + + total_size_ -= entry->size; + map_.erase(id); + return success; +} + +bool DiskCache::CheckLimit() { +#ifdef _DEBUG + // Temporary check to make sure everything is working correctly. + size_t cache_size = 0; + for (EntryMap::iterator it = map_.begin(); it != map_.end(); ++it) { + cache_size += it->second.size; + } + ASSERT(cache_size == total_size_); +#endif // _DEBUG + + // TODO: Replace this with a non-brain-dead algorithm for clearing out the + // oldest resources... something that isn't O(n^2) + while (total_size_ > max_cache_) { + EntryMap::iterator oldest = map_.end(); + for (EntryMap::iterator it = map_.begin(); it != map_.end(); ++it) { + if ((LS_UNLOCKED != it->second.lock_state) || (it->second.accessors > 0)) + continue; + oldest = it; + break; + } + if (oldest == map_.end()) { + LOG_F(LS_WARNING) << "All resources are locked!"; + return false; + } + for (EntryMap::iterator it = oldest++; it != map_.end(); ++it) { + if (it->second.last_modified < oldest->second.last_modified) { + oldest = it; + } + } + if (!DeleteResource(oldest->first)) { + LOG_F(LS_ERROR) << "Couldn't delete from cache!"; + return false; + } + } + return true; +} + +std::string DiskCache::IdToFilename(const std::string& id, size_t index) const { +#ifdef TRANSPARENT_CACHE_NAMES + // This escapes colons and other filesystem characters, so the user can't open + // special devices (like "COM1:"), or access other directories. + size_t buffer_size = id.length()*3 + 1; + char* buffer = new char[buffer_size]; + encode(buffer, buffer_size, id.data(), id.length(), + unsafe_filename_characters(), '%'); + // TODO: ASSERT(strlen(buffer) < FileSystem::MaxBasenameLength()); +#else // !TRANSPARENT_CACHE_NAMES + // We might want to just use a hash of the filename at some point, both for + // obfuscation, and to avoid both filename length and escaping issues. + ASSERT(false); +#endif // !TRANSPARENT_CACHE_NAMES + + char extension[32]; + sprintfn(extension, ARRAY_SIZE(extension), ".%u", index); + + Pathname pathname; + pathname.SetFolder(folder_); + pathname.SetBasename(buffer); + pathname.SetExtension(extension); + +#ifdef TRANSPARENT_CACHE_NAMES + delete [] buffer; +#endif // TRANSPARENT_CACHE_NAMES + + return pathname.pathname(); +} + +bool DiskCache::FilenameToId(const std::string& filename, std::string* id, + size_t* index) const { + Pathname pathname(filename); + unsigned tempdex; + if (1 != sscanf(pathname.extension().c_str(), ".%u", &tempdex)) + return false; + + *index = static_cast(tempdex); + + size_t buffer_size = pathname.basename().length() + 1; + char* buffer = new char[buffer_size]; + decode(buffer, buffer_size, pathname.basename().data(), + pathname.basename().length(), '%'); + id->assign(buffer); + delete [] buffer; + return true; +} + +DiskCache::Entry* DiskCache::GetOrCreateEntry(const std::string& id, + bool create) { + EntryMap::iterator it = map_.find(id); + if (it != map_.end()) + return &it->second; + if (!create) + return NULL; + Entry e; + e.lock_state = LS_UNLOCKED; + e.accessors = 0; + e.size = 0; + e.streams = 0; + e.last_modified = time(0); + it = map_.insert(EntryMap::value_type(id, e)).first; + return &it->second; +} + +void DiskCache::ReleaseResource(const std::string& id, size_t index) const { + const Entry* entry = GetEntry(id); + if (!entry) { + LOG_F(LS_WARNING) << "Missing cache entry"; + ASSERT(false); + return; + } + + entry->accessors -= 1; + total_accessors_ -= 1; + + if (LS_UNLOCKED != entry->lock_state) { + // This is safe, because locked resources only issue WriteResource, which + // is non-const. Think about a better way to handle it. + DiskCache* this2 = const_cast(this); + Entry* entry2 = this2->GetOrCreateEntry(id, false); + + size_t new_size = 0; + std::string filename(IdToFilename(id, index)); + FileStream::GetSize(filename, &new_size); + entry2->size += new_size; + this2->total_size_ += new_size; + + if ((LS_UNLOCKING == entry->lock_state) && (0 == entry->accessors)) { + entry2->last_modified = time(0); + entry2->lock_state = LS_UNLOCKED; + this2->CheckLimit(); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/diskcache.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/diskcache.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/diskcache.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/diskcache.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,125 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_DISKCACHE_H__ +#define WEBRTC_BASE_DISKCACHE_H__ + +#include +#include + +#if defined(WEBRTC_WIN) +#undef UnlockResource +#endif // WEBRTC_WIN + +namespace rtc { + +class StreamInterface; + +/////////////////////////////////////////////////////////////////////////////// +// DiskCache - An LRU cache of streams, stored on disk. +// +// Streams are identified by a unique resource id. Multiple streams can be +// associated with each resource id, distinguished by an index. When old +// resources are flushed from the cache, all streams associated with those +// resources are removed together. +// DiskCache is designed to persist across executions of the program. It is +// safe for use from an arbitrary number of users on a single thread, but not +// from multiple threads or other processes. +/////////////////////////////////////////////////////////////////////////////// + +class DiskCache { +public: + DiskCache(); + virtual ~DiskCache(); + + bool Initialize(const std::string& folder, size_t size); + bool Purge(); + + bool LockResource(const std::string& id); + StreamInterface* WriteResource(const std::string& id, size_t index); + bool UnlockResource(const std::string& id); + + StreamInterface* ReadResource(const std::string& id, size_t index) const; + + bool HasResource(const std::string& id) const; + bool HasResourceStream(const std::string& id, size_t index) const; + bool DeleteResource(const std::string& id); + + protected: + virtual bool InitializeEntries() = 0; + virtual bool PurgeFiles() = 0; + + virtual bool FileExists(const std::string& filename) const = 0; + virtual bool DeleteFile(const std::string& filename) const = 0; + + enum LockState { LS_UNLOCKED, LS_LOCKED, LS_UNLOCKING }; + struct Entry { + LockState lock_state; + mutable size_t accessors; + size_t size; + size_t streams; + time_t last_modified; + }; + typedef std::map EntryMap; + friend class DiskCacheAdapter; + + bool CheckLimit(); + + std::string IdToFilename(const std::string& id, size_t index) const; + bool FilenameToId(const std::string& filename, std::string* id, + size_t* index) const; + + const Entry* GetEntry(const std::string& id) const { + return const_cast(this)->GetOrCreateEntry(id, false); + } + Entry* GetOrCreateEntry(const std::string& id, bool create); + + void ReleaseResource(const std::string& id, size_t index) const; + + std::string folder_; + size_t max_cache_, total_size_; + EntryMap map_; + mutable size_t total_accessors_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// CacheLock - Automatically manage locking and unlocking, with optional +// rollback semantics +/////////////////////////////////////////////////////////////////////////////// + +class CacheLock { +public: + CacheLock(DiskCache* cache, const std::string& id, bool rollback = false) + : cache_(cache), id_(id), rollback_(rollback) + { + locked_ = cache_->LockResource(id_); + } + ~CacheLock() { + if (locked_) { + cache_->UnlockResource(id_); + if (rollback_) { + cache_->DeleteResource(id_); + } + } + } + bool IsLocked() const { return locked_; } + void Commit() { rollback_ = false; } + +private: + DiskCache* cache_; + std::string id_; + bool rollback_, locked_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_DISKCACHE_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/diskcache_win32.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/diskcache_win32.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/diskcache_win32.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/diskcache_win32.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,86 @@ +/* + * Copyright 2006 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/win32.h" +#include +#include +#include + +#include + +#include "webrtc/base/common.h" +#include "webrtc/base/diskcache.h" +#include "webrtc/base/pathutils.h" +#include "webrtc/base/stream.h" +#include "webrtc/base/stringencode.h" +#include "webrtc/base/stringutils.h" + +#include "webrtc/base/diskcache_win32.h" + +namespace rtc { + +bool DiskCacheWin32::InitializeEntries() { + // Note: We could store the cache information in a separate file, for faster + // initialization. Figuring it out empirically works, too. + + std::wstring path16 = ToUtf16(folder_); + path16.append(1, '*'); + + WIN32_FIND_DATA find_data; + HANDLE find_handle = FindFirstFile(path16.c_str(), &find_data); + if (find_handle != INVALID_HANDLE_VALUE) { + do { + size_t index; + std::string id; + if (!FilenameToId(ToUtf8(find_data.cFileName), &id, &index)) + continue; + + Entry* entry = GetOrCreateEntry(id, true); + entry->size += find_data.nFileSizeLow; + total_size_ += find_data.nFileSizeLow; + entry->streams = _max(entry->streams, index + 1); + FileTimeToUnixTime(find_data.ftLastWriteTime, &entry->last_modified); + + } while (FindNextFile(find_handle, &find_data)); + + FindClose(find_handle); + } + + return true; +} + +bool DiskCacheWin32::PurgeFiles() { + std::wstring path16 = ToUtf16(folder_); + path16.append(1, '*'); + path16.append(1, '\0'); + + SHFILEOPSTRUCT file_op = { 0 }; + file_op.wFunc = FO_DELETE; + file_op.pFrom = path16.c_str(); + file_op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT + | FOF_NORECURSION | FOF_FILESONLY; + if (0 != SHFileOperation(&file_op)) { + LOG_F(LS_ERROR) << "Couldn't delete cache files"; + return false; + } + + return true; +} + +bool DiskCacheWin32::FileExists(const std::string& filename) const { + DWORD result = ::GetFileAttributes(ToUtf16(filename).c_str()); + return (INVALID_FILE_ATTRIBUTES != result); +} + +bool DiskCacheWin32::DeleteFile(const std::string& filename) const { + return ::DeleteFile(ToUtf16(filename).c_str()) != 0; +} + +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/diskcache_win32.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/diskcache_win32.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/diskcache_win32.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/diskcache_win32.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright 2006 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_DISKCACHEWIN32_H__ +#define WEBRTC_BASE_DISKCACHEWIN32_H__ + +#include "webrtc/base/diskcache.h" + +namespace rtc { + +class DiskCacheWin32 : public DiskCache { + protected: + virtual bool InitializeEntries(); + virtual bool PurgeFiles(); + + virtual bool FileExists(const std::string& filename) const; + virtual bool DeleteFile(const std::string& filename) const; +}; + +} + +#endif // WEBRTC_BASE_DISKCACHEWIN32_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/dscp.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/dscp.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/dscp.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/dscp.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,45 @@ +/* + * Copyright 2013 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_DSCP_H_ +#define WEBRTC_BASE_DSCP_H_ + +namespace rtc { +// Differentiated Services Code Point. +// See http://tools.ietf.org/html/rfc2474 for details. +enum DiffServCodePoint { + DSCP_NO_CHANGE = -1, + DSCP_DEFAULT = 0, // Same as DSCP_CS0 + DSCP_CS0 = 0, // The default + DSCP_CS1 = 8, // Bulk/background traffic + DSCP_AF11 = 10, + DSCP_AF12 = 12, + DSCP_AF13 = 14, + DSCP_CS2 = 16, + DSCP_AF21 = 18, + DSCP_AF22 = 20, + DSCP_AF23 = 22, + DSCP_CS3 = 24, + DSCP_AF31 = 26, + DSCP_AF32 = 28, + DSCP_AF33 = 30, + DSCP_CS4 = 32, + DSCP_AF41 = 34, // Video + DSCP_AF42 = 36, // Video + DSCP_AF43 = 38, // Video + DSCP_CS5 = 40, // Video + DSCP_EF = 46, // Voice + DSCP_CS6 = 48, // Voice + DSCP_CS7 = 56, // Control messages +}; + +} // namespace rtc + + #endif // WEBRTC_BASE_DSCP_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/event.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/event.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/event.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/event.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,135 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/event.h" + +#if defined(WEBRTC_WIN) +#include +#elif defined(WEBRTC_POSIX) +#include +#include +#include +#else +#error "Must define either WEBRTC_WIN or WEBRTC_POSIX." +#endif + +namespace rtc { + +#if defined(WEBRTC_WIN) + +Event::Event(bool manual_reset, bool initially_signaled) + : is_manual_reset_(manual_reset), + is_initially_signaled_(initially_signaled) { + event_handle_ = ::CreateEvent(NULL, // Security attributes. + is_manual_reset_, + is_initially_signaled_, + NULL); // Name. + ASSERT(event_handle_ != NULL); +} + +Event::~Event() { + CloseHandle(event_handle_); +} + +void Event::Set() { + SetEvent(event_handle_); +} + +void Event::Reset() { + ResetEvent(event_handle_); +} + +bool Event::Wait(int cms) { + DWORD ms = (cms == kForever)? INFINITE : cms; + return (WaitForSingleObject(event_handle_, ms) == WAIT_OBJECT_0); +} + +#elif defined(WEBRTC_POSIX) + +Event::Event(bool manual_reset, bool initially_signaled) + : is_manual_reset_(manual_reset), + event_status_(initially_signaled) { + VERIFY(pthread_mutex_init(&event_mutex_, NULL) == 0); + VERIFY(pthread_cond_init(&event_cond_, NULL) == 0); +} + +Event::~Event() { + pthread_mutex_destroy(&event_mutex_); + pthread_cond_destroy(&event_cond_); +} + +void Event::Set() { + pthread_mutex_lock(&event_mutex_); + event_status_ = true; + pthread_cond_broadcast(&event_cond_); + pthread_mutex_unlock(&event_mutex_); +} + +void Event::Reset() { + pthread_mutex_lock(&event_mutex_); + event_status_ = false; + pthread_mutex_unlock(&event_mutex_); +} + +bool Event::Wait(int cms) { + pthread_mutex_lock(&event_mutex_); + int error = 0; + + if (cms != kForever) { + // Converting from seconds and microseconds (1e-6) plus + // milliseconds (1e-3) to seconds and nanoseconds (1e-9). + + struct timespec ts; +#if HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE + // Use relative time version, which tends to be more efficient for + // pthread implementations where provided (like on Android). + ts.tv_sec = cms / 1000; + ts.tv_nsec = (cms % 1000) * 1000000; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + + ts.tv_sec = tv.tv_sec + (cms / 1000); + ts.tv_nsec = tv.tv_usec * 1000 + (cms % 1000) * 1000000; + + // Handle overflow. + if (ts.tv_nsec >= 1000000000) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000; + } +#endif + + while (!event_status_ && error == 0) { +#if HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE + error = pthread_cond_timedwait_relative_np( + &event_cond_, &event_mutex_, &ts); +#else + error = pthread_cond_timedwait(&event_cond_, &event_mutex_, &ts); +#endif + } + } else { + while (!event_status_ && error == 0) + error = pthread_cond_wait(&event_cond_, &event_mutex_); + } + + // NOTE(liulk): Exactly one thread will auto-reset this event. All + // the other threads will think it's unsignaled. This seems to be + // consistent with auto-reset events in WEBRTC_WIN + if (error == 0 && !is_manual_reset_) + event_status_ = false; + + pthread_mutex_unlock(&event_mutex_); + + return (error == 0); +} + +#endif + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/event.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/event.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/event.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/event.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_EVENT_H__ +#define WEBRTC_BASE_EVENT_H__ + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32.h" // NOLINT: consider this a system header. +#elif defined(WEBRTC_POSIX) +#include +#else +#error "Must define either WEBRTC_WIN or WEBRTC_POSIX." +#endif + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/common.h" + +namespace rtc { + +class Event { + public: + Event(bool manual_reset, bool initially_signaled); + ~Event(); + + void Set(); + void Reset(); + bool Wait(int cms); + + private: + bool is_manual_reset_; + +#if defined(WEBRTC_WIN) + bool is_initially_signaled_; + HANDLE event_handle_; +#elif defined(WEBRTC_POSIX) + bool event_status_; + pthread_mutex_t event_mutex_; + pthread_cond_t event_cond_; +#endif +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_EVENT_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/event_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/event_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/event_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/event_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,42 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/event.h" +#include "webrtc/base/gunit.h" + +namespace rtc { + +TEST(EventTest, InitiallySignaled) { + Event event(false, true); + ASSERT_TRUE(event.Wait(0)); +} + +TEST(EventTest, ManualReset) { + Event event(true, false); + ASSERT_FALSE(event.Wait(0)); + + event.Set(); + ASSERT_TRUE(event.Wait(0)); + ASSERT_TRUE(event.Wait(0)); + + event.Reset(); + ASSERT_FALSE(event.Wait(0)); +} + +TEST(EventTest, AutoReset) { + Event event(false, false); + ASSERT_FALSE(event.Wait(0)); + + event.Set(); + ASSERT_TRUE(event.Wait(0)); + ASSERT_FALSE(event.Wait(0)); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/exp_filter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/exp_filter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/exp_filter.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/exp_filter.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/exp_filter.h" + +#include + +namespace rtc { + +const float ExpFilter::kValueUndefined = -1.0f; + +void ExpFilter::Reset(float alpha) { + alpha_ = alpha; + filtered_ = kValueUndefined; +} + +float ExpFilter::Apply(float exp, float sample) { + if (filtered_ == kValueUndefined) { + // Initialize filtered value. + filtered_ = sample; + } else if (exp == 1.0) { + filtered_ = alpha_ * filtered_ + (1 - alpha_) * sample; + } else { + float alpha = pow(alpha_, exp); + filtered_ = alpha * filtered_ + (1 - alpha) * sample; + } + if (max_ != kValueUndefined && filtered_ > max_) { + filtered_ = max_; + } + return filtered_; +} + +void ExpFilter::UpdateBase(float alpha) { + alpha_ = alpha; +} +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/exp_filter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/exp_filter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/exp_filter.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/exp_filter.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_EXP_FILTER_H_ +#define WEBRTC_BASE_EXP_FILTER_H_ + +namespace rtc { + +// This class can be used, for example, for smoothing the result of bandwidth +// estimation and packet loss estimation. + +class ExpFilter { + public: + static const float kValueUndefined; + + explicit ExpFilter(float alpha, float max = kValueUndefined) + : max_(max) { + Reset(alpha); + } + + // Resets the filter to its initial state, and resets filter factor base to + // the given value |alpha|. + void Reset(float alpha); + + // Applies the filter with a given exponent on the provided sample: + // y(k) = min(alpha_^ exp * y(k-1) + (1 - alpha_^ exp) * sample, max_). + float Apply(float exp, float sample); + + // Returns current filtered value. + float filtered() const { return filtered_; } + + // Changes the filter factor base to the given value |alpha|. + void UpdateBase(float alpha); + + private: + float alpha_; // Filter factor base. + float filtered_; // Current filter output. + const float max_; +}; +} // namespace rtc + +#endif // WEBRTC_BASE_EXP_FILTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/exp_filter_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/exp_filter_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/exp_filter_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/exp_filter_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,71 @@ +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/gunit.h" +#include "webrtc/base/exp_filter.h" + +namespace rtc { + +TEST(ExpFilterTest, FirstTimeOutputEqualInput) { + // No max value defined. + ExpFilter filter = ExpFilter(0.9f); + filter.Apply(100.0f, 10.0f); + + // First time, first argument no effect. + double value = 10.0f; + EXPECT_FLOAT_EQ(value, filter.filtered()); +} + +TEST(ExpFilterTest, SecondTime) { + double value; + + ExpFilter filter = ExpFilter(0.9f); + filter.Apply(100.0f, 10.0f); + + // First time, first argument no effect. + value = 10.0f; + + filter.Apply(10.0f, 20.0f); + double alpha = pow(0.9f, 10.0f); + value = alpha * value + (1.0f - alpha) * 20.0f; + EXPECT_FLOAT_EQ(value, filter.filtered()); +} + +TEST(ExpFilterTest, Reset) { + ExpFilter filter = ExpFilter(0.9f); + filter.Apply(100.0f, 10.0f); + + filter.Reset(0.8f); + filter.Apply(100.0f, 1.0f); + + // Become first time after a reset. + double value = 1.0f; + EXPECT_FLOAT_EQ(value, filter.filtered()); +} + +TEST(ExpfilterTest, OutputLimitedByMax) { + double value; + + // Max value defined. + ExpFilter filter = ExpFilter(0.9f, 1.0f); + filter.Apply(100.0f, 10.0f); + + // Limited to max value. + value = 1.0f; + EXPECT_EQ(value, filter.filtered()); + + filter.Apply(1.0f, 0.0f); + value = 0.9f * value; + EXPECT_FLOAT_EQ(value, filter.filtered()); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/fakecpumonitor.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/fakecpumonitor.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/fakecpumonitor.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/fakecpumonitor.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,32 @@ +/* + * Copyright 2013 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_FAKECPUMONITOR_H_ +#define WEBRTC_BASE_FAKECPUMONITOR_H_ + +#include "webrtc/base/cpumonitor.h" + +namespace rtc { + +class FakeCpuMonitor : public rtc::CpuMonitor { + public: + explicit FakeCpuMonitor(Thread* thread) + : CpuMonitor(thread) { + } + ~FakeCpuMonitor() { + } + + virtual void OnMessage(rtc::Message* msg) { + } +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_FAKECPUMONITOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/fakenetwork.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/fakenetwork.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/fakenetwork.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/fakenetwork.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,119 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_FAKENETWORK_H_ +#define WEBRTC_BASE_FAKENETWORK_H_ + +#include +#include + +#include "webrtc/base/network.h" +#include "webrtc/base/messagehandler.h" +#include "webrtc/base/socketaddress.h" +#include "webrtc/base/stringencode.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +const int kFakeIPv4NetworkPrefixLength = 24; +const int kFakeIPv6NetworkPrefixLength = 64; + +// Fake network manager that allows us to manually specify the IPs to use. +class FakeNetworkManager : public NetworkManagerBase, + public MessageHandler { + public: + FakeNetworkManager() + : thread_(Thread::Current()), + next_index_(0), + started_(false), + sent_first_update_(false) { + } + + typedef std::vector IfaceList; + + void AddInterface(const SocketAddress& iface) { + // ensure a unique name for the interface + SocketAddress address("test" + rtc::ToString(next_index_++), 0); + address.SetResolvedIP(iface.ipaddr()); + ifaces_.push_back(address); + DoUpdateNetworks(); + } + + void RemoveInterface(const SocketAddress& iface) { + for (IfaceList::iterator it = ifaces_.begin(); + it != ifaces_.end(); ++it) { + if (it->EqualIPs(iface)) { + ifaces_.erase(it); + break; + } + } + DoUpdateNetworks(); + } + + virtual void StartUpdating() { + if (started_) { + if (sent_first_update_) + SignalNetworksChanged(); + return; + } + + started_ = true; + sent_first_update_ = false; + thread_->Post(this); + } + + virtual void StopUpdating() { + started_ = false; + } + + // MessageHandler interface. + virtual void OnMessage(Message* msg) { + DoUpdateNetworks(); + } + + private: + void DoUpdateNetworks() { + if (!started_) + return; + std::vector networks; + for (IfaceList::iterator it = ifaces_.begin(); + it != ifaces_.end(); ++it) { + int prefix_length = 0; + if (it->ipaddr().family() == AF_INET) { + prefix_length = kFakeIPv4NetworkPrefixLength; + } else if (it->ipaddr().family() == AF_INET6) { + prefix_length = kFakeIPv6NetworkPrefixLength; + } + IPAddress prefix = TruncateIP(it->ipaddr(), prefix_length); + scoped_ptr net(new Network(it->hostname(), + it->hostname(), + prefix, + prefix_length)); + net->AddIP(it->ipaddr()); + networks.push_back(net.release()); + } + bool changed; + MergeNetworkList(networks, &changed); + if (changed || !sent_first_update_) { + SignalNetworksChanged(); + sent_first_update_ = true; + } + } + + Thread* thread_; + IfaceList ifaces_; + int next_index_; + bool started_; + bool sent_first_update_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_FAKENETWORK_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/fakesslidentity.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/fakesslidentity.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/fakesslidentity.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/fakesslidentity.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,96 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_FAKESSLIDENTITY_H_ +#define WEBRTC_BASE_FAKESSLIDENTITY_H_ + +#include +#include + +#include "webrtc/base/messagedigest.h" +#include "webrtc/base/sslidentity.h" + +namespace rtc { + +class FakeSSLCertificate : public rtc::SSLCertificate { + public: + // SHA-1 is the default digest algorithm because it is available in all build + // configurations used for unit testing. + explicit FakeSSLCertificate(const std::string& data) + : data_(data), digest_algorithm_(DIGEST_SHA_1) {} + explicit FakeSSLCertificate(const std::vector& certs) + : data_(certs.front()), digest_algorithm_(DIGEST_SHA_1) { + std::vector::const_iterator it; + // Skip certs[0]. + for (it = certs.begin() + 1; it != certs.end(); ++it) { + certs_.push_back(FakeSSLCertificate(*it)); + } + } + virtual FakeSSLCertificate* GetReference() const { + return new FakeSSLCertificate(*this); + } + virtual std::string ToPEMString() const { + return data_; + } + virtual void ToDER(Buffer* der_buffer) const { + std::string der_string; + VERIFY(SSLIdentity::PemToDer(kPemTypeCertificate, data_, &der_string)); + der_buffer->SetData(der_string.c_str(), der_string.size()); + } + void set_digest_algorithm(const std::string& algorithm) { + digest_algorithm_ = algorithm; + } + virtual bool GetSignatureDigestAlgorithm(std::string* algorithm) const { + *algorithm = digest_algorithm_; + return true; + } + virtual bool ComputeDigest(const std::string& algorithm, + unsigned char* digest, + size_t size, + size_t* length) const { + *length = rtc::ComputeDigest(algorithm, data_.c_str(), data_.size(), + digest, size); + return (*length != 0); + } + virtual bool GetChain(SSLCertChain** chain) const { + if (certs_.empty()) + return false; + std::vector new_certs(certs_.size()); + std::transform(certs_.begin(), certs_.end(), new_certs.begin(), DupCert); + *chain = new SSLCertChain(new_certs); + std::for_each(new_certs.begin(), new_certs.end(), DeleteCert); + return true; + } + + private: + static FakeSSLCertificate* DupCert(FakeSSLCertificate cert) { + return cert.GetReference(); + } + static void DeleteCert(SSLCertificate* cert) { delete cert; } + std::string data_; + std::vector certs_; + std::string digest_algorithm_; +}; + +class FakeSSLIdentity : public rtc::SSLIdentity { + public: + explicit FakeSSLIdentity(const std::string& data) : cert_(data) {} + explicit FakeSSLIdentity(const FakeSSLCertificate& cert) : cert_(cert) {} + virtual FakeSSLIdentity* GetReference() const { + return new FakeSSLIdentity(*this); + } + virtual const FakeSSLCertificate& certificate() const { return cert_; } + private: + FakeSSLCertificate cert_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_FAKESSLIDENTITY_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/faketaskrunner.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/faketaskrunner.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/faketaskrunner.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/faketaskrunner.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,38 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// A fake TaskRunner for use in unit tests. + +#ifndef WEBRTC_BASE_FAKETASKRUNNER_H_ +#define WEBRTC_BASE_FAKETASKRUNNER_H_ + +#include "webrtc/base/taskparent.h" +#include "webrtc/base/taskrunner.h" + +namespace rtc { + +class FakeTaskRunner : public TaskRunner { + public: + FakeTaskRunner() : current_time_(0) {} + virtual ~FakeTaskRunner() {} + + virtual void WakeTasks() { RunTasks(); } + + virtual int64 CurrentTime() { + // Implement if needed. + return current_time_++; + } + + int64 current_time_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_FAKETASKRUNNER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/filelock.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/filelock.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/filelock.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/filelock.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/filelock.h" + +#include "webrtc/base/fileutils.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/pathutils.h" +#include "webrtc/base/stream.h" + +namespace rtc { + +FileLock::FileLock(const std::string& path, FileStream* file) + : path_(path), file_(file) { +} + +FileLock::~FileLock() { + MaybeUnlock(); +} + +void FileLock::Unlock() { + LOG_F(LS_INFO); + MaybeUnlock(); +} + +void FileLock::MaybeUnlock() { + if (file_) { + LOG(LS_INFO) << "Unlocking:" << path_; + file_->Close(); + Filesystem::DeleteFile(path_); + file_.reset(); + } +} + +FileLock* FileLock::TryLock(const std::string& path) { + FileStream* stream = new FileStream(); + bool ok = false; +#if defined(WEBRTC_WIN) + // Open and lock in a single operation. + ok = stream->OpenShare(path, "a", _SH_DENYRW, NULL); +#else // WEBRTC_LINUX && !WEBRTC_ANDROID and WEBRTC_MAC && !defined(WEBRTC_IOS) + ok = stream->Open(path, "a", NULL) && stream->TryLock(); +#endif + if (ok) { + return new FileLock(path, stream); + } else { + // Something failed, either we didn't succeed to open the + // file or we failed to lock it. Anyway remove the heap + // allocated object and then return NULL to indicate failure. + delete stream; + return NULL; + } +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/filelock.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/filelock.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/filelock.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/filelock.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,53 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_FILELOCK_H_ +#define WEBRTC_BASE_FILELOCK_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/scoped_ptr.h" + +namespace rtc { + +class FileStream; + +// Implements a very simple cross process lock based on a file. +// When Lock(...) is called we try to open/create the file in read/write +// mode without any sharing. (Or locking it with flock(...) on Unix) +// If the process crash the OS will make sure that the file descriptor +// is released and another process can accuire the lock. +// This doesn't work on ancient OSX/Linux versions if used on NFS. +// (Nfs-client before: ~2.6 and Linux Kernel < 2.6.) +class FileLock { + public: + virtual ~FileLock(); + + // Attempts to lock the file. The caller owns the returned + // lock object. Returns NULL if the file already was locked. + static FileLock* TryLock(const std::string& path); + void Unlock(); + + protected: + FileLock(const std::string& path, FileStream* file); + + private: + void MaybeUnlock(); + + std::string path_; + scoped_ptr file_; + + DISALLOW_EVIL_CONSTRUCTORS(FileLock); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_FILELOCK_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/filelock_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/filelock_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/filelock_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/filelock_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,88 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/event.h" +#include "webrtc/base/filelock.h" +#include "webrtc/base/fileutils.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/pathutils.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/thread.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace rtc { + +const static std::string kLockFile = "TestLockFile"; +const static int kTimeoutMS = 5000; + +class FileLockTest : public testing::Test, public Runnable { + public: + FileLockTest() : done_(false, false), thread_lock_failed_(false) { + } + + virtual void Run(Thread* t) { + scoped_ptr lock(FileLock::TryLock(temp_file_.pathname())); + // The lock is already owned by the main thread of + // this test, therefore the TryLock(...) call should fail. + thread_lock_failed_ = lock.get() == NULL; + done_.Set(); + } + + protected: + virtual void SetUp() { + thread_lock_failed_ = false; + Filesystem::GetAppTempFolder(&temp_dir_); + temp_file_ = Pathname(temp_dir_.pathname(), kLockFile); + } + + void LockOnThread() { + locker_.Start(this); + done_.Wait(kTimeoutMS); + } + + Event done_; + Thread locker_; + bool thread_lock_failed_; + Pathname temp_dir_; + Pathname temp_file_; +}; + +TEST_F(FileLockTest, TestLockFileDeleted) { + scoped_ptr lock(FileLock::TryLock(temp_file_.pathname())); + EXPECT_TRUE(lock.get() != NULL); + EXPECT_FALSE(Filesystem::IsAbsent(temp_file_.pathname())); + lock->Unlock(); + EXPECT_TRUE(Filesystem::IsAbsent(temp_file_.pathname())); +} + +TEST_F(FileLockTest, TestLock) { + scoped_ptr lock(FileLock::TryLock(temp_file_.pathname())); + EXPECT_TRUE(lock.get() != NULL); +} + +TEST_F(FileLockTest, TestLockX2) { + scoped_ptr lock1(FileLock::TryLock(temp_file_.pathname())); + EXPECT_TRUE(lock1.get() != NULL); + + scoped_ptr lock2(FileLock::TryLock(temp_file_.pathname())); + EXPECT_TRUE(lock2.get() == NULL); +} + +TEST_F(FileLockTest, TestThreadedLock) { + scoped_ptr lock(FileLock::TryLock(temp_file_.pathname())); + EXPECT_TRUE(lock.get() != NULL); + + LockOnThread(); + EXPECT_TRUE(thread_lock_failed_); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/fileutils.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/fileutils.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/fileutils.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/fileutils.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,275 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/pathutils.h" +#include "webrtc/base/fileutils.h" +#include "webrtc/base/stringutils.h" +#include "webrtc/base/stream.h" + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32filesystem.h" +#else +#include "webrtc/base/unixfilesystem.h" +#endif + +#if !defined(WEBRTC_WIN) +#define MAX_PATH 260 +#endif + +namespace rtc { + +////////////////////////// +// Directory Iterator // +////////////////////////// + +// A DirectoryIterator is created with a given directory. It originally points +// to the first file in the directory, and can be advanecd with Next(). This +// allows you to get information about each file. + + // Constructor +DirectoryIterator::DirectoryIterator() +#ifdef WEBRTC_WIN + : handle_(INVALID_HANDLE_VALUE) { +#else + : dir_(NULL), dirent_(NULL) { +#endif +} + + // Destructor +DirectoryIterator::~DirectoryIterator() { +#if defined(WEBRTC_WIN) + if (handle_ != INVALID_HANDLE_VALUE) + ::FindClose(handle_); +#else + if (dir_) + closedir(dir_); +#endif +} + + // Starts traversing a directory. + // dir is the directory to traverse + // returns true if the directory exists and is valid +bool DirectoryIterator::Iterate(const Pathname &dir) { + directory_ = dir.pathname(); +#if defined(WEBRTC_WIN) + if (handle_ != INVALID_HANDLE_VALUE) + ::FindClose(handle_); + std::string d = dir.pathname() + '*'; + handle_ = ::FindFirstFile(ToUtf16(d).c_str(), &data_); + if (handle_ == INVALID_HANDLE_VALUE) + return false; +#else + if (dir_ != NULL) + closedir(dir_); + dir_ = ::opendir(directory_.c_str()); + if (dir_ == NULL) + return false; + dirent_ = readdir(dir_); + if (dirent_ == NULL) + return false; + + if (::stat(std::string(directory_ + Name()).c_str(), &stat_) != 0) + return false; +#endif + return true; +} + + // Advances to the next file + // returns true if there were more files in the directory. +bool DirectoryIterator::Next() { +#if defined(WEBRTC_WIN) + return ::FindNextFile(handle_, &data_) == TRUE; +#else + dirent_ = ::readdir(dir_); + if (dirent_ == NULL) + return false; + + return ::stat(std::string(directory_ + Name()).c_str(), &stat_) == 0; +#endif +} + + // returns true if the file currently pointed to is a directory +bool DirectoryIterator::IsDirectory() const { +#if defined(WEBRTC_WIN) + return (data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FALSE; +#else + return S_ISDIR(stat_.st_mode); +#endif +} + + // returns the name of the file currently pointed to +std::string DirectoryIterator::Name() const { +#if defined(WEBRTC_WIN) + return ToUtf8(data_.cFileName); +#else + assert(dirent_ != NULL); + return dirent_->d_name; +#endif +} + + // returns the size of the file currently pointed to +size_t DirectoryIterator::FileSize() const { +#if !defined(WEBRTC_WIN) + return stat_.st_size; +#else + return data_.nFileSizeLow; +#endif +} + +bool DirectoryIterator::OlderThan(int seconds) const { + time_t file_modify_time; +#if defined(WEBRTC_WIN) + FileTimeToUnixTime(data_.ftLastWriteTime, &file_modify_time); +#else + file_modify_time = stat_.st_mtime; +#endif + return TimeDiff(time(NULL), file_modify_time) >= seconds; +} + +FilesystemInterface* Filesystem::default_filesystem_ = NULL; + +FilesystemInterface *Filesystem::EnsureDefaultFilesystem() { + if (!default_filesystem_) { +#if defined(WEBRTC_WIN) + default_filesystem_ = new Win32Filesystem(); +#else + default_filesystem_ = new UnixFilesystem(); +#endif + } + return default_filesystem_; +} + +bool FilesystemInterface::CopyFolder(const Pathname &old_path, + const Pathname &new_path) { + bool success = true; + VERIFY(IsFolder(old_path)); + Pathname new_dir; + new_dir.SetFolder(new_path.pathname()); + Pathname old_dir; + old_dir.SetFolder(old_path.pathname()); + if (!CreateFolder(new_dir)) + return false; + DirectoryIterator *di = IterateDirectory(); + if (!di) + return false; + if (di->Iterate(old_dir.pathname())) { + do { + if (di->Name() == "." || di->Name() == "..") + continue; + Pathname source; + Pathname dest; + source.SetFolder(old_dir.pathname()); + dest.SetFolder(new_path.pathname()); + source.SetFilename(di->Name()); + dest.SetFilename(di->Name()); + if (!CopyFileOrFolder(source, dest)) + success = false; + } while (di->Next()); + } + delete di; + return success; +} + +bool FilesystemInterface::DeleteFolderContents(const Pathname &folder) { + bool success = true; + VERIFY(IsFolder(folder)); + DirectoryIterator *di = IterateDirectory(); + if (!di) + return false; + if (di->Iterate(folder)) { + do { + if (di->Name() == "." || di->Name() == "..") + continue; + Pathname subdir; + subdir.SetFolder(folder.pathname()); + if (di->IsDirectory()) { + subdir.AppendFolder(di->Name()); + if (!DeleteFolderAndContents(subdir)) { + success = false; + } + } else { + subdir.SetFilename(di->Name()); + if (!DeleteFile(subdir)) { + success = false; + } + } + } while (di->Next()); + } + delete di; + return success; +} + +bool FilesystemInterface::CleanAppTempFolder() { + Pathname path; + if (!GetAppTempFolder(&path)) + return false; + if (IsAbsent(path)) + return true; + if (!IsTemporaryPath(path)) { + ASSERT(false); + return false; + } + return DeleteFolderContents(path); +} + +Pathname Filesystem::GetCurrentDirectory() { + return EnsureDefaultFilesystem()->GetCurrentDirectory(); +} + +bool CreateUniqueFile(Pathname& path, bool create_empty) { + LOG(LS_INFO) << "Path " << path.pathname() << std::endl; + // If no folder is supplied, use the temporary folder + if (path.folder().empty()) { + Pathname temporary_path; + if (!Filesystem::GetTemporaryFolder(temporary_path, true, NULL)) { + printf("Get temp failed\n"); + return false; + } + path.SetFolder(temporary_path.pathname()); + } + + // If no filename is supplied, use a temporary name + if (path.filename().empty()) { + std::string folder(path.folder()); + std::string filename = Filesystem::TempFilename(folder, "gt"); + path.SetPathname(filename); + if (!create_empty) { + Filesystem::DeleteFile(path.pathname()); + } + return true; + } + + // Otherwise, create a unique name based on the given filename + // foo.txt -> foo-N.txt + const std::string basename = path.basename(); + const size_t MAX_VERSION = 100; + size_t version = 0; + while (version < MAX_VERSION) { + std::string pathname = path.pathname(); + + if (!Filesystem::IsFile(pathname)) { + if (create_empty) { + FileStream* fs = Filesystem::OpenFile(pathname, "w"); + delete fs; + } + return true; + } + version += 1; + char version_base[MAX_PATH]; + sprintfn(version_base, ARRAY_SIZE(version_base), "%s-%u", + basename.c_str(), version); + path.SetBasename(version_base); + } + return true; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/fileutils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/fileutils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/fileutils.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/fileutils.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,440 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_FILEUTILS_H_ +#define WEBRTC_BASE_FILEUTILS_H_ + +#include + +#if !defined(WEBRTC_WIN) +#include +#include +#include +#include +#include +#endif + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/common.h" +#include "webrtc/base/platform_file.h" +#include "webrtc/base/scoped_ptr.h" + +namespace rtc { + +class FileStream; +class Pathname; + +////////////////////////// +// Directory Iterator // +////////////////////////// + +// A DirectoryIterator is created with a given directory. It originally points +// to the first file in the directory, and can be advanecd with Next(). This +// allows you to get information about each file. + +class DirectoryIterator { + friend class Filesystem; + public: + // Constructor + DirectoryIterator(); + // Destructor + virtual ~DirectoryIterator(); + + // Starts traversing a directory + // dir is the directory to traverse + // returns true if the directory exists and is valid + // The iterator will point to the first entry in the directory + virtual bool Iterate(const Pathname &path); + + // Advances to the next file + // returns true if there were more files in the directory. + virtual bool Next(); + + // returns true if the file currently pointed to is a directory + virtual bool IsDirectory() const; + + // returns the name of the file currently pointed to + virtual std::string Name() const; + + // returns the size of the file currently pointed to + virtual size_t FileSize() const; + + // returns true if the file is older than seconds + virtual bool OlderThan(int seconds) const; + + // checks whether current file is a special directory file "." or ".." + bool IsDots() const { + std::string filename(Name()); + return (filename.compare(".") == 0) || (filename.compare("..") == 0); + } + + private: + std::string directory_; +#if defined(WEBRTC_WIN) + WIN32_FIND_DATA data_; + HANDLE handle_; +#else + DIR *dir_; + struct dirent *dirent_; + struct stat stat_; +#endif +}; + +enum FileTimeType { FTT_CREATED, FTT_MODIFIED, FTT_ACCESSED }; + +class FilesystemInterface { + public: + virtual ~FilesystemInterface() {} + + // Returns a DirectoryIterator for a given pathname. + // TODO: Do fancy abstracted stuff + virtual DirectoryIterator *IterateDirectory() { + return new DirectoryIterator(); + } + + // Opens a file. Returns an open StreamInterface if function succeeds. + // Otherwise, returns NULL. + // TODO: Add an error param to indicate failure reason, similar to + // FileStream::Open + virtual FileStream *OpenFile(const Pathname &filename, + const std::string &mode) = 0; + + // Atomically creates an empty file accessible only to the current user if one + // does not already exist at the given path, otherwise fails. This is the only + // secure way to create a file in a shared temp directory (e.g., C:\Temp on + // Windows or /tmp on Linux). + // Note that if it is essential that a file be successfully created then the + // app must generate random names and retry on failure, or else it will be + // vulnerable to a trivial DoS. + virtual bool CreatePrivateFile(const Pathname &filename) = 0; + + // This will attempt to delete the path located at filename. + // It ASSERTS and returns false if the path points to a folder or a + // non-existent file. + virtual bool DeleteFile(const Pathname &filename) = 0; + + // This will attempt to delete the empty folder located at 'folder' + // It ASSERTS and returns false if the path points to a file or a non-existent + // folder. It fails normally if the folder is not empty or can otherwise + // not be deleted. + virtual bool DeleteEmptyFolder(const Pathname &folder) = 0; + + // This will call IterateDirectory, to get a directory iterator, and then + // call DeleteFolderAndContents and DeleteFile on every path contained in this + // folder. If the folder is empty, this returns true. + virtual bool DeleteFolderContents(const Pathname &folder); + + // This deletes the contents of a folder, recursively, and then deletes + // the folder itself. + virtual bool DeleteFolderAndContents(const Pathname &folder) { + return DeleteFolderContents(folder) && DeleteEmptyFolder(folder); + } + + // This will delete whatever is located at path, be it a file or a folder. + // If it is a folder, it will delete it recursively by calling + // DeleteFolderAndContents + bool DeleteFileOrFolder(const Pathname &path) { + if (IsFolder(path)) + return DeleteFolderAndContents(path); + else + return DeleteFile(path); + } + + // Creates a directory. This will call itself recursively to create /foo/bar + // even if /foo does not exist. Returns true if the function succeeds. + virtual bool CreateFolder(const Pathname &pathname) = 0; + + // This moves a file from old_path to new_path, where "old_path" is a + // plain file. This ASSERTs and returns false if old_path points to a + // directory, and returns true if the function succeeds. + // If the new path is on a different volume than the old path, this function + // will attempt to copy and, if that succeeds, delete the old path. + virtual bool MoveFolder(const Pathname &old_path, + const Pathname &new_path) = 0; + + // This moves a directory from old_path to new_path, where "old_path" is a + // directory. This ASSERTs and returns false if old_path points to a plain + // file, and returns true if the function succeeds. + // If the new path is on a different volume, this function will attempt to + // copy and if that succeeds, delete the old path. + virtual bool MoveFile(const Pathname &old_path, const Pathname &new_path) = 0; + + // This attempts to move whatever is located at old_path to new_path, + // be it a file or folder. + bool MoveFileOrFolder(const Pathname &old_path, const Pathname &new_path) { + if (IsFile(old_path)) { + return MoveFile(old_path, new_path); + } else { + return MoveFolder(old_path, new_path); + } + } + + // This copies a file from old_path to new_path. This method ASSERTs and + // returns false if old_path is a folder, and returns true if the copy + // succeeds. + virtual bool CopyFile(const Pathname &old_path, const Pathname &new_path) = 0; + + // This copies a folder from old_path to new_path. + bool CopyFolder(const Pathname &old_path, const Pathname &new_path); + + bool CopyFileOrFolder(const Pathname &old_path, const Pathname &new_path) { + if (IsFile(old_path)) + return CopyFile(old_path, new_path); + else + return CopyFolder(old_path, new_path); + } + + // Returns true if pathname refers to a directory + virtual bool IsFolder(const Pathname& pathname) = 0; + + // Returns true if pathname refers to a file + virtual bool IsFile(const Pathname& pathname) = 0; + + // Returns true if pathname refers to no filesystem object, every parent + // directory either exists, or is also absent. + virtual bool IsAbsent(const Pathname& pathname) = 0; + + // Returns true if pathname represents a temporary location on the system. + virtual bool IsTemporaryPath(const Pathname& pathname) = 0; + + // A folder appropriate for storing temporary files (Contents are + // automatically deleted when the program exits) + virtual bool GetTemporaryFolder(Pathname &path, bool create, + const std::string *append) = 0; + + virtual std::string TempFilename(const Pathname &dir, + const std::string &prefix) = 0; + + // Determines the size of the file indicated by path. + virtual bool GetFileSize(const Pathname& path, size_t* size) = 0; + + // Determines a timestamp associated with the file indicated by path. + virtual bool GetFileTime(const Pathname& path, FileTimeType which, + time_t* time) = 0; + + // Returns the path to the running application. + // Note: This is not guaranteed to work on all platforms. Be aware of the + // limitations before using it, and robustly handle failure. + virtual bool GetAppPathname(Pathname* path) = 0; + + // Get a folder that is unique to the current application, which is suitable + // for sharing data between executions of the app. If the per_user arg is + // true, the folder is also specific to the current user. + virtual bool GetAppDataFolder(Pathname* path, bool per_user) = 0; + + // Get a temporary folder that is unique to the current user and application. + // TODO: Re-evaluate the goals of this function. We probably just need any + // directory that won't collide with another existing directory, and which + // will be cleaned up when the program exits. + virtual bool GetAppTempFolder(Pathname* path) = 0; + + // Delete the contents of the folder returned by GetAppTempFolder + bool CleanAppTempFolder(); + + virtual bool GetDiskFreeSpace(const Pathname& path, int64 *freebytes) = 0; + + // Returns the absolute path of the current directory. + virtual Pathname GetCurrentDirectory() = 0; + + // Note: These might go into some shared config section later, but they're + // used by some methods in this interface, so we're leaving them here for now. + void SetOrganizationName(const std::string& organization) { + organization_name_ = organization; + } + void GetOrganizationName(std::string* organization) { + ASSERT(NULL != organization); + *organization = organization_name_; + } + void SetApplicationName(const std::string& application) { + application_name_ = application; + } + void GetApplicationName(std::string* application) { + ASSERT(NULL != application); + *application = application_name_; + } + + protected: + std::string organization_name_; + std::string application_name_; +}; + +class Filesystem { + public: + static FilesystemInterface *default_filesystem() { + ASSERT(default_filesystem_ != NULL); + return default_filesystem_; + } + + static void set_default_filesystem(FilesystemInterface *filesystem) { + default_filesystem_ = filesystem; + } + + static FilesystemInterface *swap_default_filesystem( + FilesystemInterface *filesystem) { + FilesystemInterface *cur = default_filesystem_; + default_filesystem_ = filesystem; + return cur; + } + + static DirectoryIterator *IterateDirectory() { + return EnsureDefaultFilesystem()->IterateDirectory(); + } + + static bool CreateFolder(const Pathname &pathname) { + return EnsureDefaultFilesystem()->CreateFolder(pathname); + } + + static FileStream *OpenFile(const Pathname &filename, + const std::string &mode) { + return EnsureDefaultFilesystem()->OpenFile(filename, mode); + } + + static bool CreatePrivateFile(const Pathname &filename) { + return EnsureDefaultFilesystem()->CreatePrivateFile(filename); + } + + static bool DeleteFile(const Pathname &filename) { + return EnsureDefaultFilesystem()->DeleteFile(filename); + } + + static bool DeleteEmptyFolder(const Pathname &folder) { + return EnsureDefaultFilesystem()->DeleteEmptyFolder(folder); + } + + static bool DeleteFolderContents(const Pathname &folder) { + return EnsureDefaultFilesystem()->DeleteFolderContents(folder); + } + + static bool DeleteFolderAndContents(const Pathname &folder) { + return EnsureDefaultFilesystem()->DeleteFolderAndContents(folder); + } + + static bool MoveFolder(const Pathname &old_path, const Pathname &new_path) { + return EnsureDefaultFilesystem()->MoveFolder(old_path, new_path); + } + + static bool MoveFile(const Pathname &old_path, const Pathname &new_path) { + return EnsureDefaultFilesystem()->MoveFile(old_path, new_path); + } + + static bool CopyFolder(const Pathname &old_path, const Pathname &new_path) { + return EnsureDefaultFilesystem()->CopyFolder(old_path, new_path); + } + + static bool CopyFile(const Pathname &old_path, const Pathname &new_path) { + return EnsureDefaultFilesystem()->CopyFile(old_path, new_path); + } + + static bool IsFolder(const Pathname& pathname) { + return EnsureDefaultFilesystem()->IsFolder(pathname); + } + + static bool IsFile(const Pathname &pathname) { + return EnsureDefaultFilesystem()->IsFile(pathname); + } + + static bool IsAbsent(const Pathname &pathname) { + return EnsureDefaultFilesystem()->IsAbsent(pathname); + } + + static bool IsTemporaryPath(const Pathname& pathname) { + return EnsureDefaultFilesystem()->IsTemporaryPath(pathname); + } + + static bool GetTemporaryFolder(Pathname &path, bool create, + const std::string *append) { + return EnsureDefaultFilesystem()->GetTemporaryFolder(path, create, append); + } + + static std::string TempFilename(const Pathname &dir, + const std::string &prefix) { + return EnsureDefaultFilesystem()->TempFilename(dir, prefix); + } + + static bool GetFileSize(const Pathname& path, size_t* size) { + return EnsureDefaultFilesystem()->GetFileSize(path, size); + } + + static bool GetFileTime(const Pathname& path, FileTimeType which, + time_t* time) { + return EnsureDefaultFilesystem()->GetFileTime(path, which, time); + } + + static bool GetAppPathname(Pathname* path) { + return EnsureDefaultFilesystem()->GetAppPathname(path); + } + + static bool GetAppDataFolder(Pathname* path, bool per_user) { + return EnsureDefaultFilesystem()->GetAppDataFolder(path, per_user); + } + + static bool GetAppTempFolder(Pathname* path) { + return EnsureDefaultFilesystem()->GetAppTempFolder(path); + } + + static bool CleanAppTempFolder() { + return EnsureDefaultFilesystem()->CleanAppTempFolder(); + } + + static bool GetDiskFreeSpace(const Pathname& path, int64 *freebytes) { + return EnsureDefaultFilesystem()->GetDiskFreeSpace(path, freebytes); + } + + // Definition has to be in the .cc file due to returning forward-declared + // Pathname by value. + static Pathname GetCurrentDirectory(); + + static void SetOrganizationName(const std::string& organization) { + EnsureDefaultFilesystem()->SetOrganizationName(organization); + } + + static void GetOrganizationName(std::string* organization) { + EnsureDefaultFilesystem()->GetOrganizationName(organization); + } + + static void SetApplicationName(const std::string& application) { + EnsureDefaultFilesystem()->SetApplicationName(application); + } + + static void GetApplicationName(std::string* application) { + EnsureDefaultFilesystem()->GetApplicationName(application); + } + + private: + static FilesystemInterface* default_filesystem_; + + static FilesystemInterface *EnsureDefaultFilesystem(); + DISALLOW_IMPLICIT_CONSTRUCTORS(Filesystem); +}; + +class FilesystemScope{ + public: + explicit FilesystemScope(FilesystemInterface *new_fs) { + old_fs_ = Filesystem::swap_default_filesystem(new_fs); + } + ~FilesystemScope() { + Filesystem::set_default_filesystem(old_fs_); + } + private: + FilesystemInterface* old_fs_; + DISALLOW_IMPLICIT_CONSTRUCTORS(FilesystemScope); +}; + +// Generates a unique filename based on the input path. If no path component +// is specified, it uses the temporary directory. If a filename is provided, +// up to 100 variations of form basename-N.extension are tried. When +// create_empty is true, an empty file of this name is created (which +// decreases the chance of a temporary filename collision with another +// process). +bool CreateUniqueFile(Pathname& path, bool create_empty); + +} // namespace rtc + +#endif // WEBRTC_BASE_FILEUTILS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/fileutils_mock.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/fileutils_mock.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/fileutils_mock.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/fileutils_mock.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,253 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_FILEUTILS_MOCK_H_ +#define WEBRTC_BASE_FILEUTILS_MOCK_H_ + +#include +#include +#include + +#include "webrtc/base/fileutils.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/pathutils.h" +#include "webrtc/base/stream.h" + +namespace rtc { + +class FakeFileStream : public FileStream { + public: + explicit FakeFileStream(const std::string & contents) : + string_stream_(contents) + {} + + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + return string_stream_.Read(buffer, buffer_len, read, error); + } + + virtual void Close() { + return string_stream_.Close(); + } + virtual bool GetSize(size_t* size) const { + return string_stream_.GetSize(size); + } + + private: + StringStream string_stream_; +}; + +class FakeDirectoryIterator : public DirectoryIterator { + public: + typedef std::pair File; + + /* + * files should be sorted by directory + * put '/' at the end of file if you want it to be a directory + * + * Sample list: + * /var/dir/file1 + * /var/dir/file2 + * /var/dir/subdir1/ + * /var/dir/subdir2/ + * /var/dir2/file2 + * /var/dir3/ + * + * you can call Iterate for any path: /var, /var/dir, /var/dir2 + * unrelated files will be ignored + */ + explicit FakeDirectoryIterator(const std::vector& all_files) : + all_files_(all_files) {} + + virtual bool Iterate(const Pathname& path) { + path_iterator_ = all_files_.begin(); + path_ = path.pathname(); + + // make sure path ends end with '/' + if (path_.rfind(Pathname::DefaultFolderDelimiter()) != path_.size() - 1) + path_ += Pathname::DefaultFolderDelimiter(); + + return FakeDirectoryIterator::Search(std::string("")); + } + + virtual bool Next() { + std::string current_name = Name(); + path_iterator_++; + return FakeDirectoryIterator::Search(current_name); + } + + bool Search(const std::string& current_name) { + for (; path_iterator_ != all_files_.end(); path_iterator_++) { + if (path_iterator_->first.find(path_) == 0 + && Name().compare(current_name) != 0) { + return true; + } + } + + return false; + } + + virtual bool IsDirectory() const { + std::string sub_path = path_iterator_->first; + + return std::string::npos != + sub_path.find(Pathname::DefaultFolderDelimiter(), path_.size()); + } + + virtual std::string Name() const { + std::string sub_path = path_iterator_->first; + + // path - top level path (ex. /var/lib) + // sub_path - subpath under top level path (ex. /var/lib/dir/dir/file ) + // find shortest non-trivial common path. (ex. /var/lib/dir) + size_t start = path_.size(); + size_t end = sub_path.find(Pathname::DefaultFolderDelimiter(), start); + + if (end != std::string::npos) { + return sub_path.substr(start, end - start); + } else { + return sub_path.substr(start); + } + } + + private: + const std::vector all_files_; + + std::string path_; + std::vector::const_iterator path_iterator_; +}; + +class FakeFileSystem : public FilesystemInterface { + public: + typedef std::pair File; + + explicit FakeFileSystem(const std::vector& all_files) : + all_files_(all_files) {} + + virtual DirectoryIterator *IterateDirectory() { + return new FakeDirectoryIterator(all_files_); + } + + virtual FileStream * OpenFile( + const Pathname &filename, + const std::string &mode) { + std::vector::const_iterator i_files = all_files_.begin(); + std::string path = filename.pathname(); + + for (; i_files != all_files_.end(); i_files++) { + if (i_files->first.compare(path) == 0) { + return new FakeFileStream(i_files->second); + } + } + + return NULL; + } + + bool CreatePrivateFile(const Pathname &filename) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + bool DeleteFile(const Pathname &filename) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + bool DeleteEmptyFolder(const Pathname &folder) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + bool DeleteFolderContents(const Pathname &folder) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + bool DeleteFolderAndContents(const Pathname &folder) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + bool CreateFolder(const Pathname &pathname) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + bool MoveFolder(const Pathname &old_path, const Pathname &new_path) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + bool MoveFile(const Pathname &old_path, const Pathname &new_path) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + bool CopyFile(const Pathname &old_path, const Pathname &new_path) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + bool IsFolder(const Pathname &pathname) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + bool IsFile(const Pathname &pathname) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + bool IsAbsent(const Pathname &pathname) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + bool IsTemporaryPath(const Pathname &pathname) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + bool GetTemporaryFolder(Pathname &path, bool create, + const std::string *append) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + std::string TempFilename(const Pathname &dir, const std::string &prefix) { + EXPECT_TRUE(false) << "Unsupported operation"; + return std::string(); + } + bool GetFileSize(const Pathname &path, size_t *size) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + bool GetFileTime(const Pathname &path, FileTimeType which, + time_t* time) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + bool GetAppPathname(Pathname *path) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + bool GetAppDataFolder(Pathname *path, bool per_user) { + EXPECT_TRUE(per_user) << "Unsupported operation"; +#if defined(WEBRTC_WIN) + path->SetPathname("c:\\Users\\test_user", ""); +#else + path->SetPathname("/home/user/test_user", ""); +#endif + return true; + } + bool GetAppTempFolder(Pathname *path) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + bool GetDiskFreeSpace(const Pathname &path, int64 *freebytes) { + EXPECT_TRUE(false) << "Unsupported operation"; + return false; + } + Pathname GetCurrentDirectory() { + return Pathname(); + } + + private: + const std::vector all_files_; +}; +} // namespace rtc + +#endif // WEBRTC_BASE_FILEUTILS_MOCK_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/fileutils_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/fileutils_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/fileutils_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/fileutils_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,131 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/fileutils.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/pathutils.h" +#include "webrtc/base/stream.h" + +namespace rtc { + +// Make sure we can get a temp folder for the later tests. +TEST(FilesystemTest, GetTemporaryFolder) { + Pathname path; + EXPECT_TRUE(Filesystem::GetTemporaryFolder(path, true, NULL)); +} + +// Test creating a temp file, reading it back in, and deleting it. +TEST(FilesystemTest, TestOpenFile) { + Pathname path; + EXPECT_TRUE(Filesystem::GetTemporaryFolder(path, true, NULL)); + path.SetPathname(Filesystem::TempFilename(path, "ut")); + + FileStream* fs; + char buf[256]; + size_t bytes; + + fs = Filesystem::OpenFile(path, "wb"); + ASSERT_TRUE(fs != NULL); + EXPECT_EQ(SR_SUCCESS, fs->Write("test", 4, &bytes, NULL)); + EXPECT_EQ(4U, bytes); + delete fs; + + EXPECT_TRUE(Filesystem::IsFile(path)); + + fs = Filesystem::OpenFile(path, "rb"); + ASSERT_TRUE(fs != NULL); + EXPECT_EQ(SR_SUCCESS, fs->Read(buf, sizeof(buf), &bytes, NULL)); + EXPECT_EQ(4U, bytes); + delete fs; + + EXPECT_TRUE(Filesystem::DeleteFile(path)); + EXPECT_FALSE(Filesystem::IsFile(path)); +} + +// Test opening a non-existent file. +TEST(FilesystemTest, TestOpenBadFile) { + Pathname path; + EXPECT_TRUE(Filesystem::GetTemporaryFolder(path, true, NULL)); + path.SetFilename("not an actual file"); + + EXPECT_FALSE(Filesystem::IsFile(path)); + + FileStream* fs = Filesystem::OpenFile(path, "rb"); + EXPECT_FALSE(fs != NULL); +} + +// Test that CreatePrivateFile fails for existing files and succeeds for +// non-existent ones. +TEST(FilesystemTest, TestCreatePrivateFile) { + Pathname path; + EXPECT_TRUE(Filesystem::GetTemporaryFolder(path, true, NULL)); + path.SetFilename("private_file_test"); + + // First call should succeed because the file doesn't exist yet. + EXPECT_TRUE(Filesystem::CreatePrivateFile(path)); + // Next call should fail, because now it exists. + EXPECT_FALSE(Filesystem::CreatePrivateFile(path)); + + // Verify that we have permission to open the file for reading and writing. + scoped_ptr fs(Filesystem::OpenFile(path, "wb")); + EXPECT_TRUE(fs.get() != NULL); + // Have to close the file on Windows before it will let us delete it. + fs.reset(); + + // Verify that we have permission to delete the file. + EXPECT_TRUE(Filesystem::DeleteFile(path)); +} + +// Test checking for free disk space. +TEST(FilesystemTest, TestGetDiskFreeSpace) { + // Note that we should avoid picking any file/folder which could be located + // at the remotely mounted drive/device. + Pathname path; + ASSERT_TRUE(Filesystem::GetAppDataFolder(&path, true)); + + int64 free1 = 0; + EXPECT_TRUE(Filesystem::IsFolder(path)); + EXPECT_FALSE(Filesystem::IsFile(path)); + EXPECT_TRUE(Filesystem::GetDiskFreeSpace(path, &free1)); + EXPECT_GT(free1, 0); + + int64 free2 = 0; + path.AppendFolder("this_folder_doesnt_exist"); + EXPECT_FALSE(Filesystem::IsFolder(path)); + EXPECT_TRUE(Filesystem::IsAbsent(path)); + EXPECT_TRUE(Filesystem::GetDiskFreeSpace(path, &free2)); + // These should be the same disk, and disk free space should not have changed + // by more than 1% between the two calls. + EXPECT_LT(static_cast(free1 * .9), free2); + EXPECT_LT(free2, static_cast(free1 * 1.1)); + + int64 free3 = 0; + path.clear(); + EXPECT_TRUE(path.empty()); + EXPECT_TRUE(Filesystem::GetDiskFreeSpace(path, &free3)); + // Current working directory may not be where exe is. + // EXPECT_LT(static_cast(free1 * .9), free3); + // EXPECT_LT(free3, static_cast(free1 * 1.1)); + EXPECT_GT(free3, 0); +} + +// Tests that GetCurrentDirectory() returns something. +TEST(FilesystemTest, TestGetCurrentDirectory) { + EXPECT_FALSE(Filesystem::GetCurrentDirectory().empty()); +} + +// Tests that GetAppPathname returns something. +TEST(FilesystemTest, TestGetAppPathname) { + Pathname path; + EXPECT_TRUE(Filesystem::GetAppPathname(&path)); + EXPECT_FALSE(path.empty()); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/firewallsocketserver.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/firewallsocketserver.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/firewallsocketserver.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/firewallsocketserver.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,239 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/firewallsocketserver.h" + +#include + +#include + +#include "webrtc/base/asyncsocket.h" +#include "webrtc/base/logging.h" + +namespace rtc { + +class FirewallSocket : public AsyncSocketAdapter { + public: + FirewallSocket(FirewallSocketServer* server, AsyncSocket* socket, int type) + : AsyncSocketAdapter(socket), server_(server), type_(type) { + } + + virtual int Connect(const SocketAddress& addr) { + if (type_ == SOCK_STREAM) { + if (!server_->Check(FP_TCP, GetLocalAddress(), addr)) { + LOG(LS_VERBOSE) << "FirewallSocket outbound TCP connection from " + << GetLocalAddress().ToSensitiveString() << " to " + << addr.ToSensitiveString() << " denied"; + // TODO: Handle this asynchronously. + SetError(EHOSTUNREACH); + return SOCKET_ERROR; + } + } + return AsyncSocketAdapter::Connect(addr); + } + virtual int Send(const void* pv, size_t cb) { + return SendTo(pv, cb, GetRemoteAddress()); + } + virtual int SendTo(const void* pv, size_t cb, const SocketAddress& addr) { + if (type_ == SOCK_DGRAM) { + if (!server_->Check(FP_UDP, GetLocalAddress(), addr)) { + LOG(LS_VERBOSE) << "FirewallSocket outbound UDP packet from " + << GetLocalAddress().ToSensitiveString() << " to " + << addr.ToSensitiveString() << " dropped"; + return static_cast(cb); + } + } + return AsyncSocketAdapter::SendTo(pv, cb, addr); + } + virtual int Recv(void* pv, size_t cb) { + SocketAddress addr; + return RecvFrom(pv, cb, &addr); + } + virtual int RecvFrom(void* pv, size_t cb, SocketAddress* paddr) { + if (type_ == SOCK_DGRAM) { + while (true) { + int res = AsyncSocketAdapter::RecvFrom(pv, cb, paddr); + if (res <= 0) + return res; + if (server_->Check(FP_UDP, *paddr, GetLocalAddress())) + return res; + LOG(LS_VERBOSE) << "FirewallSocket inbound UDP packet from " + << paddr->ToSensitiveString() << " to " + << GetLocalAddress().ToSensitiveString() << " dropped"; + } + } + return AsyncSocketAdapter::RecvFrom(pv, cb, paddr); + } + + virtual int Listen(int backlog) { + if (!server_->tcp_listen_enabled()) { + LOG(LS_VERBOSE) << "FirewallSocket listen attempt denied"; + return -1; + } + + return AsyncSocketAdapter::Listen(backlog); + } + virtual AsyncSocket* Accept(SocketAddress* paddr) { + SocketAddress addr; + while (AsyncSocket* sock = AsyncSocketAdapter::Accept(&addr)) { + if (server_->Check(FP_TCP, addr, GetLocalAddress())) { + if (paddr) + *paddr = addr; + return sock; + } + sock->Close(); + delete sock; + LOG(LS_VERBOSE) << "FirewallSocket inbound TCP connection from " + << addr.ToSensitiveString() << " to " + << GetLocalAddress().ToSensitiveString() << " denied"; + } + return 0; + } + + private: + FirewallSocketServer* server_; + int type_; +}; + +FirewallSocketServer::FirewallSocketServer(SocketServer* server, + FirewallManager* manager, + bool should_delete_server) + : server_(server), manager_(manager), + should_delete_server_(should_delete_server), + udp_sockets_enabled_(true), tcp_sockets_enabled_(true), + tcp_listen_enabled_(true) { + if (manager_) + manager_->AddServer(this); +} + +FirewallSocketServer::~FirewallSocketServer() { + if (manager_) + manager_->RemoveServer(this); + + if (server_ && should_delete_server_) { + delete server_; + server_ = NULL; + } +} + +void FirewallSocketServer::AddRule(bool allow, FirewallProtocol p, + FirewallDirection d, + const SocketAddress& addr) { + SocketAddress src, dst; + if (d == FD_IN) { + dst = addr; + } else { + src = addr; + } + AddRule(allow, p, src, dst); +} + + +void FirewallSocketServer::AddRule(bool allow, FirewallProtocol p, + const SocketAddress& src, + const SocketAddress& dst) { + Rule r; + r.allow = allow; + r.p = p; + r.src = src; + r.dst = dst; + CritScope scope(&crit_); + rules_.push_back(r); +} + +void FirewallSocketServer::ClearRules() { + CritScope scope(&crit_); + rules_.clear(); +} + +bool FirewallSocketServer::Check(FirewallProtocol p, + const SocketAddress& src, + const SocketAddress& dst) { + CritScope scope(&crit_); + for (size_t i = 0; i < rules_.size(); ++i) { + const Rule& r = rules_[i]; + if ((r.p != p) && (r.p != FP_ANY)) + continue; + if ((r.src.ipaddr() != src.ipaddr()) && !r.src.IsNil()) + continue; + if ((r.src.port() != src.port()) && (r.src.port() != 0)) + continue; + if ((r.dst.ipaddr() != dst.ipaddr()) && !r.dst.IsNil()) + continue; + if ((r.dst.port() != dst.port()) && (r.dst.port() != 0)) + continue; + return r.allow; + } + return true; +} + +Socket* FirewallSocketServer::CreateSocket(int type) { + return CreateSocket(AF_INET, type); +} + +Socket* FirewallSocketServer::CreateSocket(int family, int type) { + return WrapSocket(server_->CreateAsyncSocket(family, type), type); +} + +AsyncSocket* FirewallSocketServer::CreateAsyncSocket(int type) { + return CreateAsyncSocket(AF_INET, type); +} + +AsyncSocket* FirewallSocketServer::CreateAsyncSocket(int family, int type) { + return WrapSocket(server_->CreateAsyncSocket(family, type), type); +} + +AsyncSocket* FirewallSocketServer::WrapSocket(AsyncSocket* sock, int type) { + if (!sock || + (type == SOCK_STREAM && !tcp_sockets_enabled_) || + (type == SOCK_DGRAM && !udp_sockets_enabled_)) { + LOG(LS_VERBOSE) << "FirewallSocketServer socket creation denied"; + delete sock; + return NULL; + } + return new FirewallSocket(this, sock, type); +} + +FirewallManager::FirewallManager() { +} + +FirewallManager::~FirewallManager() { + assert(servers_.empty()); +} + +void FirewallManager::AddServer(FirewallSocketServer* server) { + CritScope scope(&crit_); + servers_.push_back(server); +} + +void FirewallManager::RemoveServer(FirewallSocketServer* server) { + CritScope scope(&crit_); + servers_.erase(std::remove(servers_.begin(), servers_.end(), server), + servers_.end()); +} + +void FirewallManager::AddRule(bool allow, FirewallProtocol p, + FirewallDirection d, const SocketAddress& addr) { + CritScope scope(&crit_); + for (std::vector::const_iterator it = + servers_.begin(); it != servers_.end(); ++it) { + (*it)->AddRule(allow, p, d, addr); + } +} + +void FirewallManager::ClearRules() { + CritScope scope(&crit_); + for (std::vector::const_iterator it = + servers_.begin(); it != servers_.end(); ++it) { + (*it)->ClearRules(); + } +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/firewallsocketserver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/firewallsocketserver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/firewallsocketserver.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/firewallsocketserver.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,120 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_FIREWALLSOCKETSERVER_H_ +#define WEBRTC_BASE_FIREWALLSOCKETSERVER_H_ + +#include +#include "webrtc/base/socketserver.h" +#include "webrtc/base/criticalsection.h" + +namespace rtc { + +class FirewallManager; + +// This SocketServer shim simulates a rule-based firewall server. + +enum FirewallProtocol { FP_UDP, FP_TCP, FP_ANY }; +enum FirewallDirection { FD_IN, FD_OUT, FD_ANY }; + +class FirewallSocketServer : public SocketServer { + public: + FirewallSocketServer(SocketServer * server, + FirewallManager * manager = NULL, + bool should_delete_server = false); + virtual ~FirewallSocketServer(); + + SocketServer* socketserver() const { return server_; } + void set_socketserver(SocketServer* server) { + if (server_ && should_delete_server_) { + delete server_; + server_ = NULL; + should_delete_server_ = false; + } + server_ = server; + } + + // Settings to control whether CreateSocket or Socket::Listen succeed. + void set_udp_sockets_enabled(bool enabled) { udp_sockets_enabled_ = enabled; } + void set_tcp_sockets_enabled(bool enabled) { tcp_sockets_enabled_ = enabled; } + bool tcp_listen_enabled() const { return tcp_listen_enabled_; } + void set_tcp_listen_enabled(bool enabled) { tcp_listen_enabled_ = enabled; } + + // Rules govern the behavior of Connect/Accept/Send/Recv attempts. + void AddRule(bool allow, FirewallProtocol p = FP_ANY, + FirewallDirection d = FD_ANY, + const SocketAddress& addr = SocketAddress()); + void AddRule(bool allow, FirewallProtocol p, + const SocketAddress& src, const SocketAddress& dst); + void ClearRules(); + + bool Check(FirewallProtocol p, + const SocketAddress& src, const SocketAddress& dst); + + virtual Socket* CreateSocket(int type); + virtual Socket* CreateSocket(int family, int type); + + virtual AsyncSocket* CreateAsyncSocket(int type); + virtual AsyncSocket* CreateAsyncSocket(int family, int type); + + virtual void SetMessageQueue(MessageQueue* queue) { + server_->SetMessageQueue(queue); + } + virtual bool Wait(int cms, bool process_io) { + return server_->Wait(cms, process_io); + } + virtual void WakeUp() { + return server_->WakeUp(); + } + + Socket * WrapSocket(Socket * sock, int type); + AsyncSocket * WrapSocket(AsyncSocket * sock, int type); + + private: + SocketServer * server_; + FirewallManager * manager_; + CriticalSection crit_; + struct Rule { + bool allow; + FirewallProtocol p; + FirewallDirection d; + SocketAddress src; + SocketAddress dst; + }; + std::vector rules_; + bool should_delete_server_; + bool udp_sockets_enabled_; + bool tcp_sockets_enabled_; + bool tcp_listen_enabled_; +}; + +// FirewallManager allows you to manage firewalls in multiple threads together + +class FirewallManager { + public: + FirewallManager(); + ~FirewallManager(); + + void AddServer(FirewallSocketServer * server); + void RemoveServer(FirewallSocketServer * server); + + void AddRule(bool allow, FirewallProtocol p = FP_ANY, + FirewallDirection d = FD_ANY, + const SocketAddress& addr = SocketAddress()); + void ClearRules(); + + private: + CriticalSection crit_; + std::vector servers_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_FIREWALLSOCKETSERVER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/flags.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/flags.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/flags.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/flags.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,296 @@ +/* + * Copyright 2006 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include +#include + + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32.h" +#include +#endif + +#include "webrtc/base/flags.h" + +namespace rtc { +// ----------------------------------------------------------------------------- +// Implementation of Flag + +Flag::Flag(const char* file, const char* name, const char* comment, + Type type, void* variable, FlagValue default__) + : file_(file), + name_(name), + comment_(comment), + type_(type), + variable_(reinterpret_cast(variable)), + default_(default__) { + FlagList::Register(this); +} + + +void Flag::SetToDefault() { + // Note that we cannot simply do '*variable_ = default_;' since + // flag variables are not really of type FlagValue and thus may + // be smaller! The FlagValue union is simply 'overlayed' on top + // of a flag variable for convenient access. Since union members + // are guarantee to be aligned at the beginning, this works. + switch (type_) { + case Flag::BOOL: + variable_->b = default_.b; + return; + case Flag::INT: + variable_->i = default_.i; + return; + case Flag::FLOAT: + variable_->f = default_.f; + return; + case Flag::STRING: + variable_->s = default_.s; + return; + } + FATAL() << "unreachable code"; +} + + +static const char* Type2String(Flag::Type type) { + switch (type) { + case Flag::BOOL: return "bool"; + case Flag::INT: return "int"; + case Flag::FLOAT: return "float"; + case Flag::STRING: return "string"; + } + FATAL() << "unreachable code"; +} + + +static void PrintFlagValue(Flag::Type type, FlagValue* p) { + switch (type) { + case Flag::BOOL: + printf("%s", (p->b ? "true" : "false")); + return; + case Flag::INT: + printf("%d", p->i); + return; + case Flag::FLOAT: + printf("%f", p->f); + return; + case Flag::STRING: + printf("%s", p->s); + return; + } + FATAL() << "unreachable code"; +} + + +void Flag::Print(bool print_current_value) { + printf(" --%s (%s) type: %s default: ", name_, comment_, + Type2String(type_)); + PrintFlagValue(type_, &default_); + if (print_current_value) { + printf(" current value: "); + PrintFlagValue(type_, variable_); + } + printf("\n"); +} + + +// ----------------------------------------------------------------------------- +// Implementation of FlagList + +Flag* FlagList::list_ = NULL; + + +FlagList::FlagList() { + list_ = NULL; +} + +void FlagList::Print(const char* file, bool print_current_value) { + // Since flag registration is likely by file (= C++ file), + // we don't need to sort by file and still get grouped output. + const char* current = NULL; + for (Flag* f = list_; f != NULL; f = f->next()) { + if (file == NULL || file == f->file()) { + if (current != f->file()) { + printf("Flags from %s:\n", f->file()); + current = f->file(); + } + f->Print(print_current_value); + } + } +} + + +Flag* FlagList::Lookup(const char* name) { + Flag* f = list_; + while (f != NULL && strcmp(name, f->name()) != 0) + f = f->next(); + return f; +} + + +void FlagList::SplitArgument(const char* arg, + char* buffer, int buffer_size, + const char** name, const char** value, + bool* is_bool) { + *name = NULL; + *value = NULL; + *is_bool = false; + + if (*arg == '-') { + // find the begin of the flag name + arg++; // remove 1st '-' + if (*arg == '-') + arg++; // remove 2nd '-' + if (arg[0] == 'n' && arg[1] == 'o') { + arg += 2; // remove "no" + *is_bool = true; + } + *name = arg; + + // find the end of the flag name + while (*arg != '\0' && *arg != '=') + arg++; + + // get the value if any + if (*arg == '=') { + // make a copy so we can NUL-terminate flag name + int n = static_cast(arg - *name); + CHECK_LT(n, buffer_size); + memcpy(buffer, *name, n * sizeof(char)); + buffer[n] = '\0'; + *name = buffer; + // get the value + *value = arg + 1; + } + } +} + + +int FlagList::SetFlagsFromCommandLine(int* argc, const char** argv, + bool remove_flags) { + // parse arguments + for (int i = 1; i < *argc; /* see below */) { + int j = i; // j > 0 + const char* arg = argv[i++]; + + // split arg into flag components + char buffer[1024]; + const char* name; + const char* value; + bool is_bool; + SplitArgument(arg, buffer, sizeof buffer, &name, &value, &is_bool); + + if (name != NULL) { + // lookup the flag + Flag* flag = Lookup(name); + if (flag == NULL) { + fprintf(stderr, "Error: unrecognized flag %s\n", arg); + return j; + } + + // if we still need a flag value, use the next argument if available + if (flag->type() != Flag::BOOL && value == NULL) { + if (i < *argc) { + value = argv[i++]; + } else { + fprintf(stderr, "Error: missing value for flag %s of type %s\n", + arg, Type2String(flag->type())); + return j; + } + } + + // set the flag + char empty[] = { '\0' }; + char* endp = empty; + switch (flag->type()) { + case Flag::BOOL: + *flag->bool_variable() = !is_bool; + break; + case Flag::INT: + *flag->int_variable() = strtol(value, &endp, 10); + break; + case Flag::FLOAT: + *flag->float_variable() = strtod(value, &endp); + break; + case Flag::STRING: + *flag->string_variable() = value; + break; + } + + // handle errors + if ((flag->type() == Flag::BOOL && value != NULL) || + (flag->type() != Flag::BOOL && is_bool) || + *endp != '\0') { + fprintf(stderr, "Error: illegal value for flag %s of type %s\n", + arg, Type2String(flag->type())); + return j; + } + + // remove the flag & value from the command + if (remove_flags) + while (j < i) + argv[j++] = NULL; + } + } + + // shrink the argument list + if (remove_flags) { + int j = 1; + for (int i = 1; i < *argc; i++) { + if (argv[i] != NULL) + argv[j++] = argv[i]; + } + *argc = j; + } + + // parsed all flags successfully + return 0; +} + +void FlagList::Register(Flag* flag) { + assert(flag != NULL && strlen(flag->name()) > 0); + CHECK(!Lookup(flag->name())) << "flag " << flag->name() << " declared twice"; + flag->next_ = list_; + list_ = flag; +} + +#if defined(WEBRTC_WIN) +WindowsCommandLineArguments::WindowsCommandLineArguments() { + // start by getting the command line. + LPTSTR command_line = ::GetCommandLine(); + // now, convert it to a list of wide char strings. + LPWSTR *wide_argv = ::CommandLineToArgvW(command_line, &argc_); + // now allocate an array big enough to hold that many string pointers. + argv_ = new char*[argc_]; + + // iterate over the returned wide strings; + for(int i = 0; i < argc_; ++i) { + std::string s = rtc::ToUtf8(wide_argv[i], wcslen(wide_argv[i])); + char *buffer = new char[s.length() + 1]; + rtc::strcpyn(buffer, s.length() + 1, s.c_str()); + + // make sure the argv array has the right string at this point. + argv_[i] = buffer; + } + LocalFree(wide_argv); +} + +WindowsCommandLineArguments::~WindowsCommandLineArguments() { + // need to free each string in the array, and then the array. + for(int i = 0; i < argc_; i++) { + delete[] argv_[i]; + } + + delete[] argv_; +} +#endif // WEBRTC_WIN + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/flags.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/flags.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/flags.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/flags.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,270 @@ +/* + * Copyright 2006 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + + +// Originally comes from shared/commandlineflags/flags.h + +// Flags are defined and declared using DEFINE_xxx and DECLARE_xxx macros, +// where xxx is the flag type. Flags are referred to via FLAG_yyy, +// where yyy is the flag name. For intialization and iteration of flags, +// see the FlagList class. For full programmatic access to any +// flag, see the Flag class. +// +// The implementation only relies and basic C++ functionality +// and needs no special library or STL support. + +#ifndef WEBRTC_BASE_FLAGS_H__ +#define WEBRTC_BASE_FLAGS_H__ + +#include + +#include "webrtc/base/checks.h" +#include "webrtc/base/common.h" + +namespace rtc { + +// Internal use only. +union FlagValue { + // Note: Because in C++ non-bool values are silently converted into + // bool values ('bool b = "false";' results in b == true!), we pass + // and int argument to New_BOOL as this appears to be safer - sigh. + // In particular, it prevents the (not uncommon!) bug where a bool + // flag is defined via: DEFINE_bool(flag, "false", "some comment");. + static FlagValue New_BOOL(int b) { + FlagValue v; + v.b = (b != 0); + return v; + } + + static FlagValue New_INT(int i) { + FlagValue v; + v.i = i; + return v; + } + + static FlagValue New_FLOAT(float f) { + FlagValue v; + v.f = f; + return v; + } + + static FlagValue New_STRING(const char* s) { + FlagValue v; + v.s = s; + return v; + } + + bool b; + int i; + double f; + const char* s; +}; + + +// Each flag can be accessed programmatically via a Flag object. +class Flag { + public: + enum Type { BOOL, INT, FLOAT, STRING }; + + // Internal use only. + Flag(const char* file, const char* name, const char* comment, + Type type, void* variable, FlagValue default_); + + // General flag information + const char* file() const { return file_; } + const char* name() const { return name_; } + const char* comment() const { return comment_; } + + // Flag type + Type type() const { return type_; } + + // Flag variables + bool* bool_variable() const { + assert(type_ == BOOL); + return &variable_->b; + } + + int* int_variable() const { + assert(type_ == INT); + return &variable_->i; + } + + double* float_variable() const { + assert(type_ == FLOAT); + return &variable_->f; + } + + const char** string_variable() const { + assert(type_ == STRING); + return &variable_->s; + } + + // Default values + bool bool_default() const { + assert(type_ == BOOL); + return default_.b; + } + + int int_default() const { + assert(type_ == INT); + return default_.i; + } + + double float_default() const { + assert(type_ == FLOAT); + return default_.f; + } + + const char* string_default() const { + assert(type_ == STRING); + return default_.s; + } + + // Resets a flag to its default value + void SetToDefault(); + + // Iteration support + Flag* next() const { return next_; } + + // Prints flag information. The current flag value is only printed + // if print_current_value is set. + void Print(bool print_current_value); + + private: + const char* file_; + const char* name_; + const char* comment_; + + Type type_; + FlagValue* variable_; + FlagValue default_; + + Flag* next_; + + friend class FlagList; // accesses next_ +}; + + +// Internal use only. +#define DEFINE_FLAG(type, c_type, name, default, comment) \ + /* define and initialize the flag */ \ + c_type FLAG_##name = (default); \ + /* register the flag */ \ + static rtc::Flag Flag_##name(__FILE__, #name, (comment), \ + rtc::Flag::type, &FLAG_##name, \ + rtc::FlagValue::New_##type(default)) + + +// Internal use only. +#define DECLARE_FLAG(c_type, name) \ + /* declare the external flag */ \ + extern c_type FLAG_##name + + +// Use the following macros to define a new flag: +#define DEFINE_bool(name, default, comment) \ + DEFINE_FLAG(BOOL, bool, name, default, comment) +#define DEFINE_int(name, default, comment) \ + DEFINE_FLAG(INT, int, name, default, comment) +#define DEFINE_float(name, default, comment) \ + DEFINE_FLAG(FLOAT, double, name, default, comment) +#define DEFINE_string(name, default, comment) \ + DEFINE_FLAG(STRING, const char*, name, default, comment) + + +// Use the following macros to declare a flag defined elsewhere: +#define DECLARE_bool(name) DECLARE_FLAG(bool, name) +#define DECLARE_int(name) DECLARE_FLAG(int, name) +#define DECLARE_float(name) DECLARE_FLAG(double, name) +#define DECLARE_string(name) DECLARE_FLAG(const char*, name) + + +// The global list of all flags. +class FlagList { + public: + FlagList(); + + // The NULL-terminated list of all flags. Traverse with Flag::next(). + static Flag* list() { return list_; } + + // If file != NULL, prints information for all flags defined in file; + // otherwise prints information for all flags in all files. The current + // flag value is only printed if print_current_value is set. + static void Print(const char* file, bool print_current_value); + + // Lookup a flag by name. Returns the matching flag or NULL. + static Flag* Lookup(const char* name); + + // Helper function to parse flags: Takes an argument arg and splits it into + // a flag name and flag value (or NULL if they are missing). is_bool is set + // if the arg started with "-no" or "--no". The buffer may be used to NUL- + // terminate the name, it must be large enough to hold any possible name. + static void SplitArgument(const char* arg, + char* buffer, int buffer_size, + const char** name, const char** value, + bool* is_bool); + + // Set the flag values by parsing the command line. If remove_flags + // is set, the flags and associated values are removed from (argc, + // argv). Returns 0 if no error occurred. Otherwise, returns the + // argv index > 0 for the argument where an error occurred. In that + // case, (argc, argv) will remain unchanged indepdendent of the + // remove_flags value, and no assumptions about flag settings should + // be made. + // + // The following syntax for flags is accepted (both '-' and '--' are ok): + // + // --flag (bool flags only) + // --noflag (bool flags only) + // --flag=value (non-bool flags only, no spaces around '=') + // --flag value (non-bool flags only) + static int SetFlagsFromCommandLine(int* argc, + const char** argv, + bool remove_flags); + static inline int SetFlagsFromCommandLine(int* argc, + char** argv, + bool remove_flags) { + return SetFlagsFromCommandLine(argc, const_cast(argv), + remove_flags); + } + + // Registers a new flag. Called during program initialization. Not + // thread-safe. + static void Register(Flag* flag); + + private: + static Flag* list_; +}; + +#if defined(WEBRTC_WIN) +// A helper class to translate Windows command line arguments into UTF8, +// which then allows us to just pass them to the flags system. +// This encapsulates all the work of getting the command line and translating +// it to an array of 8-bit strings; all you have to do is create one of these, +// and then call argc() and argv(). +class WindowsCommandLineArguments { + public: + WindowsCommandLineArguments(); + ~WindowsCommandLineArguments(); + + int argc() { return argc_; } + char **argv() { return argv_; } + private: + int argc_; + char **argv_; + + private: + DISALLOW_EVIL_CONSTRUCTORS(WindowsCommandLineArguments); +}; +#endif // WEBRTC_WIN + +} // namespace rtc + +#endif // SHARED_COMMANDLINEFLAGS_FLAGS_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/gunit.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/gunit.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/gunit.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/gunit.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,88 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_GUNIT_H_ +#define WEBRTC_BASE_GUNIT_H_ + +#include "webrtc/base/logging.h" +#include "webrtc/base/thread.h" +#if defined(WEBRTC_ANDROID) || defined(GTEST_RELATIVE_PATH) +#include "testing/gtest/include/gtest/gtest.h" +#else +#include "testing/base/public/gunit.h" +#endif + +// Wait until "ex" is true, or "timeout" expires. +#define WAIT(ex, timeout) \ + for (uint32 start = rtc::Time(); \ + !(ex) && rtc::Time() < start + timeout;) \ + rtc::Thread::Current()->ProcessMessages(1); + +// This returns the result of the test in res, so that we don't re-evaluate +// the expression in the XXXX_WAIT macros below, since that causes problems +// when the expression is only true the first time you check it. +#define WAIT_(ex, timeout, res) \ + do { \ + uint32 start = rtc::Time(); \ + res = (ex); \ + while (!res && rtc::Time() < start + timeout) { \ + rtc::Thread::Current()->ProcessMessages(1); \ + res = (ex); \ + } \ + } while (0); + +// The typical EXPECT_XXXX and ASSERT_XXXXs, but done until true or a timeout. +#define EXPECT_TRUE_WAIT(ex, timeout) \ + do { \ + bool res; \ + WAIT_(ex, timeout, res); \ + if (!res) EXPECT_TRUE(ex); \ + } while (0); + +#define EXPECT_EQ_WAIT(v1, v2, timeout) \ + do { \ + bool res; \ + WAIT_(v1 == v2, timeout, res); \ + if (!res) EXPECT_EQ(v1, v2); \ + } while (0); + +#define ASSERT_TRUE_WAIT(ex, timeout) \ + do { \ + bool res; \ + WAIT_(ex, timeout, res); \ + if (!res) ASSERT_TRUE(ex); \ + } while (0); + +#define ASSERT_EQ_WAIT(v1, v2, timeout) \ + do { \ + bool res; \ + WAIT_(v1 == v2, timeout, res); \ + if (!res) ASSERT_EQ(v1, v2); \ + } while (0); + +// Version with a "soft" timeout and a margin. This logs if the timeout is +// exceeded, but it only fails if the expression still isn't true after the +// margin time passes. +#define EXPECT_TRUE_WAIT_MARGIN(ex, timeout, margin) \ + do { \ + bool res; \ + WAIT_(ex, timeout, res); \ + if (res) { \ + break; \ + } \ + LOG(LS_WARNING) << "Expression " << #ex << " still not true after " << \ + timeout << "ms; waiting an additional " << margin << "ms"; \ + WAIT_(ex, margin, res); \ + if (!res) { \ + EXPECT_TRUE(ex); \ + } \ + } while (0); + +#endif // WEBRTC_BASE_GUNIT_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/gunit_prod.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/gunit_prod.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/gunit_prod.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/gunit_prod.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,24 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_GUNIT_PROD_H_ +#define WEBRTC_BASE_GUNIT_PROD_H_ + +#if defined(WEBRTC_ANDROID) +// Android doesn't use gtest at all, so anything that relies on gtest should +// check this define first. +#define NO_GTEST +#elif defined (GTEST_RELATIVE_PATH) +#include "gtest/gtest_prod.h" +#else +#include "testing/base/gunit_prod.h" +#endif + +#endif // WEBRTC_BASE_GUNIT_PROD_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/helpers.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/helpers.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/helpers.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/helpers.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,277 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/helpers.h" + +#include + +#if defined(FEATURE_ENABLE_SSL) +#include "webrtc/base/sslconfig.h" +#if defined(SSL_USE_OPENSSL) +#include +#elif defined(SSL_USE_NSS_RNG) +#include "pk11func.h" +#else +#if defined(WEBRTC_WIN) +#define WIN32_LEAN_AND_MEAN +#include +#include +#endif // WEBRTC_WIN +#endif // else +#endif // FEATURE_ENABLED_SSL + +#include "webrtc/base/base64.h" +#include "webrtc/base/basictypes.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/timeutils.h" + +// Protect against max macro inclusion. +#undef max + +namespace rtc { + +// Base class for RNG implementations. +class RandomGenerator { + public: + virtual ~RandomGenerator() {} + virtual bool Init(const void* seed, size_t len) = 0; + virtual bool Generate(void* buf, size_t len) = 0; +}; + +#if defined(SSL_USE_OPENSSL) +// The OpenSSL RNG. +class SecureRandomGenerator : public RandomGenerator { + public: + SecureRandomGenerator() {} + ~SecureRandomGenerator() {} + virtual bool Init(const void* seed, size_t len) { + return true; + } + virtual bool Generate(void* buf, size_t len) { + return (RAND_bytes(reinterpret_cast(buf), len) > 0); + } +}; + +#elif defined(SSL_USE_NSS_RNG) +// The NSS RNG. +class SecureRandomGenerator : public RandomGenerator { + public: + SecureRandomGenerator() {} + ~SecureRandomGenerator() {} + virtual bool Init(const void* seed, size_t len) { + return true; + } + virtual bool Generate(void* buf, size_t len) { + return (PK11_GenerateRandom(reinterpret_cast(buf), + static_cast(len)) == SECSuccess); + } +}; + +#else +#if defined(WEBRTC_WIN) +class SecureRandomGenerator : public RandomGenerator { + public: + SecureRandomGenerator() : advapi32_(NULL), rtl_gen_random_(NULL) {} + ~SecureRandomGenerator() { + FreeLibrary(advapi32_); + } + + virtual bool Init(const void* seed, size_t seed_len) { + // We don't do any additional seeding on Win32, we just use the CryptoAPI + // RNG (which is exposed as a hidden function off of ADVAPI32 so that we + // don't need to drag in all of CryptoAPI) + if (rtl_gen_random_) { + return true; + } + + advapi32_ = LoadLibrary(L"advapi32.dll"); + if (!advapi32_) { + return false; + } + + rtl_gen_random_ = reinterpret_cast( + GetProcAddress(advapi32_, "SystemFunction036")); + if (!rtl_gen_random_) { + FreeLibrary(advapi32_); + return false; + } + + return true; + } + virtual bool Generate(void* buf, size_t len) { + if (!rtl_gen_random_ && !Init(NULL, 0)) { + return false; + } + return (rtl_gen_random_(buf, static_cast(len)) != FALSE); + } + + private: + typedef BOOL (WINAPI *RtlGenRandomProc)(PVOID, ULONG); + HINSTANCE advapi32_; + RtlGenRandomProc rtl_gen_random_; +}; + +#elif !defined(FEATURE_ENABLE_SSL) + +// No SSL implementation -- use rand() +class SecureRandomGenerator : public RandomGenerator { + public: + virtual bool Init(const void* seed, size_t len) { + if (len >= 4) { + srand(*reinterpret_cast(seed)); + } else { + srand(*reinterpret_cast(seed)); + } + return true; + } + virtual bool Generate(void* buf, size_t len) { + char* bytes = reinterpret_cast(buf); + for (size_t i = 0; i < len; ++i) { + bytes[i] = static_cast(rand()); + } + return true; + } +}; + +#else + +#error No SSL implementation has been selected! + +#endif // WEBRTC_WIN +#endif + +// A test random generator, for predictable output. +class TestRandomGenerator : public RandomGenerator { + public: + TestRandomGenerator() : seed_(7) { + } + ~TestRandomGenerator() { + } + virtual bool Init(const void* seed, size_t len) { + return true; + } + virtual bool Generate(void* buf, size_t len) { + for (size_t i = 0; i < len; ++i) { + static_cast(buf)[i] = static_cast(GetRandom()); + } + return true; + } + + private: + int GetRandom() { + return ((seed_ = seed_ * 214013L + 2531011L) >> 16) & 0x7fff; + } + int seed_; +}; + +// TODO: Use Base64::Base64Table instead. +static const char BASE64[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; + +namespace { + +// This round about way of creating a global RNG is to safe-guard against +// indeterminant static initialization order. +scoped_ptr& GetGlobalRng() { + LIBJINGLE_DEFINE_STATIC_LOCAL(scoped_ptr, global_rng, + (new SecureRandomGenerator())); + return global_rng; +} + +RandomGenerator& Rng() { + return *GetGlobalRng(); +} + +} // namespace + +void SetRandomTestMode(bool test) { + if (!test) { + GetGlobalRng().reset(new SecureRandomGenerator()); + } else { + GetGlobalRng().reset(new TestRandomGenerator()); + } +} + +bool InitRandom(int seed) { + return InitRandom(reinterpret_cast(&seed), sizeof(seed)); +} + +bool InitRandom(const char* seed, size_t len) { + if (!Rng().Init(seed, len)) { + LOG(LS_ERROR) << "Failed to init random generator!"; + return false; + } + return true; +} + +std::string CreateRandomString(size_t len) { + std::string str; + CreateRandomString(len, &str); + return str; +} + +bool CreateRandomString(size_t len, + const char* table, int table_size, + std::string* str) { + str->clear(); + scoped_ptr bytes(new uint8[len]); + if (!Rng().Generate(bytes.get(), len)) { + LOG(LS_ERROR) << "Failed to generate random string!"; + return false; + } + str->reserve(len); + for (size_t i = 0; i < len; ++i) { + str->push_back(table[bytes[i] % table_size]); + } + return true; +} + +bool CreateRandomString(size_t len, std::string* str) { + return CreateRandomString(len, BASE64, 64, str); +} + +bool CreateRandomString(size_t len, const std::string& table, + std::string* str) { + return CreateRandomString(len, table.c_str(), + static_cast(table.size()), str); +} + +uint32 CreateRandomId() { + uint32 id; + if (!Rng().Generate(&id, sizeof(id))) { + LOG(LS_ERROR) << "Failed to generate random id!"; + } + return id; +} + +uint64 CreateRandomId64() { + return static_cast(CreateRandomId()) << 32 | CreateRandomId(); +} + +uint32 CreateRandomNonZeroId() { + uint32 id; + do { + id = CreateRandomId(); + } while (id == 0); + return id; +} + +double CreateRandomDouble() { + return CreateRandomId() / (std::numeric_limits::max() + + std::numeric_limits::epsilon()); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/helpers.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/helpers.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/helpers.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/helpers.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_HELPERS_H_ +#define WEBRTC_BASE_HELPERS_H_ + +#include +#include "webrtc/base/basictypes.h" + +namespace rtc { + +// For testing, we can return predictable data. +void SetRandomTestMode(bool test); + +// Initializes the RNG, and seeds it with the specified entropy. +bool InitRandom(int seed); +bool InitRandom(const char* seed, size_t len); + +// Generates a (cryptographically) random string of the given length. +// We generate base64 values so that they will be printable. +// WARNING: could silently fail. Use the version below instead. +std::string CreateRandomString(size_t length); + +// Generates a (cryptographically) random string of the given length. +// We generate base64 values so that they will be printable. +// Return false if the random number generator failed. +bool CreateRandomString(size_t length, std::string* str); + +// Generates a (cryptographically) random string of the given length, +// with characters from the given table. Return false if the random +// number generator failed. +bool CreateRandomString(size_t length, const std::string& table, + std::string* str); + +// Generates a random id. +uint32 CreateRandomId(); + +// Generates a 64 bit random id. +uint64 CreateRandomId64(); + +// Generates a random id > 0. +uint32 CreateRandomNonZeroId(); + +// Generates a random double between 0.0 (inclusive) and 1.0 (exclusive). +double CreateRandomDouble(); + +} // namespace rtc + +#endif // WEBRTC_BASE_HELPERS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/helpers_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/helpers_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/helpers_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/helpers_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,69 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/gunit.h" +#include "webrtc/base/helpers.h" +#include "webrtc/base/ssladapter.h" + +namespace rtc { + +class RandomTest : public testing::Test {}; + +TEST_F(RandomTest, TestCreateRandomId) { + CreateRandomId(); +} + +TEST_F(RandomTest, TestCreateRandomDouble) { + for (int i = 0; i < 100; ++i) { + double r = CreateRandomDouble(); + EXPECT_GE(r, 0.0); + EXPECT_LT(r, 1.0); + } +} + +TEST_F(RandomTest, TestCreateNonZeroRandomId) { + EXPECT_NE(0U, CreateRandomNonZeroId()); +} + +TEST_F(RandomTest, TestCreateRandomString) { + std::string random = CreateRandomString(256); + EXPECT_EQ(256U, random.size()); + std::string random2; + EXPECT_TRUE(CreateRandomString(256, &random2)); + EXPECT_NE(random, random2); + EXPECT_EQ(256U, random2.size()); +} + +TEST_F(RandomTest, TestCreateRandomForTest) { + // Make sure we get the output we expect. + SetRandomTestMode(true); + EXPECT_EQ(2154761789U, CreateRandomId()); + EXPECT_EQ("h0ISP4S5SJKH/9EY", CreateRandomString(16)); + + // Reset and make sure we get the same output. + SetRandomTestMode(true); + EXPECT_EQ(2154761789U, CreateRandomId()); + EXPECT_EQ("h0ISP4S5SJKH/9EY", CreateRandomString(16)); + + // Test different character sets. + SetRandomTestMode(true); + std::string str; + EXPECT_TRUE(CreateRandomString(16, "a", &str)); + EXPECT_EQ("aaaaaaaaaaaaaaaa", str); + EXPECT_TRUE(CreateRandomString(16, "abc", &str)); + EXPECT_EQ("acbccaaaabbaacbb", str); + + // Turn off test mode for other tests. + SetRandomTestMode(false); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpbase.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpbase.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpbase.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpbase.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,877 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32.h" +#else // !WEBRTC_WIN +#define SEC_E_CERT_EXPIRED (-2146893016) +#endif // !WEBRTC_WIN + +#include "webrtc/base/common.h" +#include "webrtc/base/httpbase.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/socket.h" +#include "webrtc/base/stringutils.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +////////////////////////////////////////////////////////////////////// +// Helpers +////////////////////////////////////////////////////////////////////// + +bool MatchHeader(const char* str, size_t len, HttpHeader header) { + const char* const header_str = ToString(header); + const size_t header_len = strlen(header_str); + return (len == header_len) && (_strnicmp(str, header_str, header_len) == 0); +} + +enum { + MSG_READ +}; + +////////////////////////////////////////////////////////////////////// +// HttpParser +////////////////////////////////////////////////////////////////////// + +HttpParser::HttpParser() { + reset(); +} + +HttpParser::~HttpParser() { +} + +void +HttpParser::reset() { + state_ = ST_LEADER; + chunked_ = false; + data_size_ = SIZE_UNKNOWN; +} + +HttpParser::ProcessResult +HttpParser::Process(const char* buffer, size_t len, size_t* processed, + HttpError* error) { + *processed = 0; + *error = HE_NONE; + + if (state_ >= ST_COMPLETE) { + ASSERT(false); + return PR_COMPLETE; + } + + while (true) { + if (state_ < ST_DATA) { + size_t pos = *processed; + while ((pos < len) && (buffer[pos] != '\n')) { + pos += 1; + } + if (pos >= len) { + break; // don't have a full header + } + const char* line = buffer + *processed; + size_t len = (pos - *processed); + *processed = pos + 1; + while ((len > 0) && isspace(static_cast(line[len-1]))) { + len -= 1; + } + ProcessResult result = ProcessLine(line, len, error); + LOG(LS_VERBOSE) << "Processed line, result=" << result; + + if (PR_CONTINUE != result) { + return result; + } + } else if (data_size_ == 0) { + if (chunked_) { + state_ = ST_CHUNKTERM; + } else { + return PR_COMPLETE; + } + } else { + size_t available = len - *processed; + if (available <= 0) { + break; // no more data + } + if ((data_size_ != SIZE_UNKNOWN) && (available > data_size_)) { + available = data_size_; + } + size_t read = 0; + ProcessResult result = ProcessData(buffer + *processed, available, read, + error); + LOG(LS_VERBOSE) << "Processed data, result: " << result << " read: " + << read << " err: " << error; + + if (PR_CONTINUE != result) { + return result; + } + *processed += read; + if (data_size_ != SIZE_UNKNOWN) { + data_size_ -= read; + } + } + } + + return PR_CONTINUE; +} + +HttpParser::ProcessResult +HttpParser::ProcessLine(const char* line, size_t len, HttpError* error) { + LOG_F(LS_VERBOSE) << " state: " << state_ << " line: " + << std::string(line, len) << " len: " << len << " err: " + << error; + + switch (state_) { + case ST_LEADER: + state_ = ST_HEADERS; + return ProcessLeader(line, len, error); + + case ST_HEADERS: + if (len > 0) { + const char* value = strchrn(line, len, ':'); + if (!value) { + *error = HE_PROTOCOL; + return PR_COMPLETE; + } + size_t nlen = (value - line); + const char* eol = line + len; + do { + value += 1; + } while ((value < eol) && isspace(static_cast(*value))); + size_t vlen = eol - value; + if (MatchHeader(line, nlen, HH_CONTENT_LENGTH)) { + // sscanf isn't safe with strings that aren't null-terminated, and there + // is no guarantee that |value| is. + // Create a local copy that is null-terminated. + std::string value_str(value, vlen); + unsigned int temp_size; + if (sscanf(value_str.c_str(), "%u", &temp_size) != 1) { + *error = HE_PROTOCOL; + return PR_COMPLETE; + } + data_size_ = static_cast(temp_size); + } else if (MatchHeader(line, nlen, HH_TRANSFER_ENCODING)) { + if ((vlen == 7) && (_strnicmp(value, "chunked", 7) == 0)) { + chunked_ = true; + } else if ((vlen == 8) && (_strnicmp(value, "identity", 8) == 0)) { + chunked_ = false; + } else { + *error = HE_PROTOCOL; + return PR_COMPLETE; + } + } + return ProcessHeader(line, nlen, value, vlen, error); + } else { + state_ = chunked_ ? ST_CHUNKSIZE : ST_DATA; + return ProcessHeaderComplete(chunked_, data_size_, error); + } + break; + + case ST_CHUNKSIZE: + if (len > 0) { + char* ptr = NULL; + data_size_ = strtoul(line, &ptr, 16); + if (ptr != line + len) { + *error = HE_PROTOCOL; + return PR_COMPLETE; + } + state_ = (data_size_ == 0) ? ST_TRAILERS : ST_DATA; + } else { + *error = HE_PROTOCOL; + return PR_COMPLETE; + } + break; + + case ST_CHUNKTERM: + if (len > 0) { + *error = HE_PROTOCOL; + return PR_COMPLETE; + } else { + state_ = chunked_ ? ST_CHUNKSIZE : ST_DATA; + } + break; + + case ST_TRAILERS: + if (len == 0) { + return PR_COMPLETE; + } + // *error = onHttpRecvTrailer(); + break; + + default: + ASSERT(false); + break; + } + + return PR_CONTINUE; +} + +bool +HttpParser::is_valid_end_of_input() const { + return (state_ == ST_DATA) && (data_size_ == SIZE_UNKNOWN); +} + +void +HttpParser::complete(HttpError error) { + if (state_ < ST_COMPLETE) { + state_ = ST_COMPLETE; + OnComplete(error); + } +} + +////////////////////////////////////////////////////////////////////// +// HttpBase::DocumentStream +////////////////////////////////////////////////////////////////////// + +class BlockingMemoryStream : public ExternalMemoryStream { +public: + BlockingMemoryStream(char* buffer, size_t size) + : ExternalMemoryStream(buffer, size) { } + + virtual StreamResult DoReserve(size_t size, int* error) { + return (buffer_length_ >= size) ? SR_SUCCESS : SR_BLOCK; + } +}; + +class HttpBase::DocumentStream : public StreamInterface { +public: + DocumentStream(HttpBase* base) : base_(base), error_(HE_DEFAULT) { } + + virtual StreamState GetState() const { + if (NULL == base_) + return SS_CLOSED; + if (HM_RECV == base_->mode_) + return SS_OPEN; + return SS_OPENING; + } + + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + if (!base_) { + if (error) *error = error_; + return (HE_NONE == error_) ? SR_EOS : SR_ERROR; + } + + if (HM_RECV != base_->mode_) { + return SR_BLOCK; + } + + // DoReceiveLoop writes http document data to the StreamInterface* document + // member of HttpData. In this case, we want this data to be written + // directly to our buffer. To accomplish this, we wrap our buffer with a + // StreamInterface, and replace the existing document with our wrapper. + // When the method returns, we restore the old document. Ideally, we would + // pass our StreamInterface* to DoReceiveLoop, but due to the callbacks + // of HttpParser, we would still need to store the pointer temporarily. + scoped_ptr + stream(new BlockingMemoryStream(reinterpret_cast(buffer), + buffer_len)); + + // Replace the existing document with our wrapped buffer. + base_->data_->document.swap(stream); + + // Pump the I/O loop. DoReceiveLoop is guaranteed not to attempt to + // complete the I/O process, which means that our wrapper is not in danger + // of being deleted. To ensure this, DoReceiveLoop returns true when it + // wants complete to be called. We make sure to uninstall our wrapper + // before calling complete(). + HttpError http_error; + bool complete = base_->DoReceiveLoop(&http_error); + + // Reinstall the original output document. + base_->data_->document.swap(stream); + + // If we reach the end of the receive stream, we disconnect our stream + // adapter from the HttpBase, and further calls to read will either return + // EOS or ERROR, appropriately. Finally, we call complete(). + StreamResult result = SR_BLOCK; + if (complete) { + HttpBase* base = Disconnect(http_error); + if (error) *error = error_; + result = (HE_NONE == error_) ? SR_EOS : SR_ERROR; + base->complete(http_error); + } + + // Even if we are complete, if some data was read we must return SUCCESS. + // Future Reads will return EOS or ERROR based on the error_ variable. + size_t position; + stream->GetPosition(&position); + if (position > 0) { + if (read) *read = position; + result = SR_SUCCESS; + } + return result; + } + + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error) { + if (error) *error = -1; + return SR_ERROR; + } + + virtual void Close() { + if (base_) { + HttpBase* base = Disconnect(HE_NONE); + if (HM_RECV == base->mode_ && base->http_stream_) { + // Read I/O could have been stalled on the user of this DocumentStream, + // so restart the I/O process now that we've removed ourselves. + base->http_stream_->PostEvent(SE_READ, 0); + } + } + } + + virtual bool GetAvailable(size_t* size) const { + if (!base_ || HM_RECV != base_->mode_) + return false; + size_t data_size = base_->GetDataRemaining(); + if (SIZE_UNKNOWN == data_size) + return false; + if (size) + *size = data_size; + return true; + } + + HttpBase* Disconnect(HttpError error) { + ASSERT(NULL != base_); + ASSERT(NULL != base_->doc_stream_); + HttpBase* base = base_; + base_->doc_stream_ = NULL; + base_ = NULL; + error_ = error; + return base; + } + +private: + HttpBase* base_; + HttpError error_; +}; + +////////////////////////////////////////////////////////////////////// +// HttpBase +////////////////////////////////////////////////////////////////////// + +HttpBase::HttpBase() : mode_(HM_NONE), data_(NULL), notify_(NULL), + http_stream_(NULL), doc_stream_(NULL) { +} + +HttpBase::~HttpBase() { + ASSERT(HM_NONE == mode_); +} + +bool +HttpBase::isConnected() const { + return (http_stream_ != NULL) && (http_stream_->GetState() == SS_OPEN); +} + +bool +HttpBase::attach(StreamInterface* stream) { + if ((mode_ != HM_NONE) || (http_stream_ != NULL) || (stream == NULL)) { + ASSERT(false); + return false; + } + http_stream_ = stream; + http_stream_->SignalEvent.connect(this, &HttpBase::OnHttpStreamEvent); + mode_ = (http_stream_->GetState() == SS_OPENING) ? HM_CONNECT : HM_NONE; + return true; +} + +StreamInterface* +HttpBase::detach() { + ASSERT(HM_NONE == mode_); + if (mode_ != HM_NONE) { + return NULL; + } + StreamInterface* stream = http_stream_; + http_stream_ = NULL; + if (stream) { + stream->SignalEvent.disconnect(this); + } + return stream; +} + +void +HttpBase::send(HttpData* data) { + ASSERT(HM_NONE == mode_); + if (mode_ != HM_NONE) { + return; + } else if (!isConnected()) { + OnHttpStreamEvent(http_stream_, SE_CLOSE, HE_DISCONNECTED); + return; + } + + mode_ = HM_SEND; + data_ = data; + len_ = 0; + ignore_data_ = chunk_data_ = false; + + if (data_->document) { + data_->document->SignalEvent.connect(this, &HttpBase::OnDocumentEvent); + } + + std::string encoding; + if (data_->hasHeader(HH_TRANSFER_ENCODING, &encoding) + && (encoding == "chunked")) { + chunk_data_ = true; + } + + len_ = data_->formatLeader(buffer_, sizeof(buffer_)); + len_ += strcpyn(buffer_ + len_, sizeof(buffer_) - len_, "\r\n"); + + header_ = data_->begin(); + if (header_ == data_->end()) { + // We must call this at least once, in the case where there are no headers. + queue_headers(); + } + + flush_data(); +} + +void +HttpBase::recv(HttpData* data) { + ASSERT(HM_NONE == mode_); + if (mode_ != HM_NONE) { + return; + } else if (!isConnected()) { + OnHttpStreamEvent(http_stream_, SE_CLOSE, HE_DISCONNECTED); + return; + } + + mode_ = HM_RECV; + data_ = data; + len_ = 0; + ignore_data_ = chunk_data_ = false; + + reset(); + if (doc_stream_) { + doc_stream_->SignalEvent(doc_stream_, SE_OPEN | SE_READ, 0); + } else { + read_and_process_data(); + } +} + +void +HttpBase::abort(HttpError err) { + if (mode_ != HM_NONE) { + if (http_stream_ != NULL) { + http_stream_->Close(); + } + do_complete(err); + } +} + +StreamInterface* HttpBase::GetDocumentStream() { + if (doc_stream_) + return NULL; + doc_stream_ = new DocumentStream(this); + return doc_stream_; +} + +HttpError HttpBase::HandleStreamClose(int error) { + if (http_stream_ != NULL) { + http_stream_->Close(); + } + if (error == 0) { + if ((mode_ == HM_RECV) && is_valid_end_of_input()) { + return HE_NONE; + } else { + return HE_DISCONNECTED; + } + } else if (error == SOCKET_EACCES) { + return HE_AUTH; + } else if (error == SEC_E_CERT_EXPIRED) { + return HE_CERTIFICATE_EXPIRED; + } + LOG_F(LS_ERROR) << "(" << error << ")"; + return (HM_CONNECT == mode_) ? HE_CONNECT_FAILED : HE_SOCKET_ERROR; +} + +bool HttpBase::DoReceiveLoop(HttpError* error) { + ASSERT(HM_RECV == mode_); + ASSERT(NULL != error); + + // Do to the latency between receiving read notifications from + // pseudotcpchannel, we rely on repeated calls to read in order to acheive + // ideal throughput. The number of reads is limited to prevent starving + // the caller. + + size_t loop_count = 0; + const size_t kMaxReadCount = 20; + bool process_requires_more_data = false; + do { + // The most frequent use of this function is response to new data available + // on http_stream_. Therefore, we optimize by attempting to read from the + // network first (as opposed to processing existing data first). + + if (len_ < sizeof(buffer_)) { + // Attempt to buffer more data. + size_t read; + int read_error; + StreamResult read_result = http_stream_->Read(buffer_ + len_, + sizeof(buffer_) - len_, + &read, &read_error); + switch (read_result) { + case SR_SUCCESS: + ASSERT(len_ + read <= sizeof(buffer_)); + len_ += read; + break; + case SR_BLOCK: + if (process_requires_more_data) { + // We're can't make progress until more data is available. + return false; + } + // Attempt to process the data already in our buffer. + break; + case SR_EOS: + // Clean close, with no error. Fall through to HandleStreamClose. + read_error = 0; + case SR_ERROR: + *error = HandleStreamClose(read_error); + return true; + } + } else if (process_requires_more_data) { + // We have too much unprocessed data in our buffer. This should only + // occur when a single HTTP header is longer than the buffer size (32K). + // Anything longer than that is almost certainly an error. + *error = HE_OVERFLOW; + return true; + } + + // Process data in our buffer. Process is not guaranteed to process all + // the buffered data. In particular, it will wait until a complete + // protocol element (such as http header, or chunk size) is available, + // before processing it in its entirety. Also, it is valid and sometimes + // necessary to call Process with an empty buffer, since the state machine + // may have interrupted state transitions to complete. + size_t processed; + ProcessResult process_result = Process(buffer_, len_, &processed, + error); + ASSERT(processed <= len_); + len_ -= processed; + memmove(buffer_, buffer_ + processed, len_); + switch (process_result) { + case PR_CONTINUE: + // We need more data to make progress. + process_requires_more_data = true; + break; + case PR_BLOCK: + // We're stalled on writing the processed data. + return false; + case PR_COMPLETE: + // *error already contains the correct code. + return true; + } + } while (++loop_count <= kMaxReadCount); + + LOG_F(LS_WARNING) << "danger of starvation"; + return false; +} + +void +HttpBase::read_and_process_data() { + HttpError error; + if (DoReceiveLoop(&error)) { + complete(error); + } +} + +void +HttpBase::flush_data() { + ASSERT(HM_SEND == mode_); + + // When send_required is true, no more buffering can occur without a network + // write. + bool send_required = (len_ >= sizeof(buffer_)); + + while (true) { + ASSERT(len_ <= sizeof(buffer_)); + + // HTTP is inherently sensitive to round trip latency, since a frequent use + // case is for small requests and responses to be sent back and forth, and + // the lack of pipelining forces a single request to take a minimum of the + // round trip time. As a result, it is to our benefit to pack as much data + // into each packet as possible. Thus, we defer network writes until we've + // buffered as much data as possible. + + if (!send_required && (header_ != data_->end())) { + // First, attempt to queue more header data. + send_required = queue_headers(); + } + + if (!send_required && data_->document) { + // Next, attempt to queue document data. + + const size_t kChunkDigits = 8; + size_t offset, reserve; + if (chunk_data_) { + // Reserve characters at the start for X-byte hex value and \r\n + offset = len_ + kChunkDigits + 2; + // ... and 2 characters at the end for \r\n + reserve = offset + 2; + } else { + offset = len_; + reserve = offset; + } + + if (reserve >= sizeof(buffer_)) { + send_required = true; + } else { + size_t read; + int error; + StreamResult result = data_->document->Read(buffer_ + offset, + sizeof(buffer_) - reserve, + &read, &error); + if (result == SR_SUCCESS) { + ASSERT(reserve + read <= sizeof(buffer_)); + if (chunk_data_) { + // Prepend the chunk length in hex. + // Note: sprintfn appends a null terminator, which is why we can't + // combine it with the line terminator. + sprintfn(buffer_ + len_, kChunkDigits + 1, "%.*x", + kChunkDigits, read); + // Add line terminator to the chunk length. + memcpy(buffer_ + len_ + kChunkDigits, "\r\n", 2); + // Add line terminator to the end of the chunk. + memcpy(buffer_ + offset + read, "\r\n", 2); + } + len_ = reserve + read; + } else if (result == SR_BLOCK) { + // Nothing to do but flush data to the network. + send_required = true; + } else if (result == SR_EOS) { + if (chunk_data_) { + // Append the empty chunk and empty trailers, then turn off + // chunking. + ASSERT(len_ + 5 <= sizeof(buffer_)); + memcpy(buffer_ + len_, "0\r\n\r\n", 5); + len_ += 5; + chunk_data_ = false; + } else if (0 == len_) { + // No more data to read, and no more data to write. + do_complete(); + return; + } + // Although we are done reading data, there is still data which needs + // to be flushed to the network. + send_required = true; + } else { + LOG_F(LS_ERROR) << "Read error: " << error; + do_complete(HE_STREAM); + return; + } + } + } + + if (0 == len_) { + // No data currently available to send. + if (!data_->document) { + // If there is no source document, that means we're done. + do_complete(); + } + return; + } + + size_t written; + int error; + StreamResult result = http_stream_->Write(buffer_, len_, &written, &error); + if (result == SR_SUCCESS) { + ASSERT(written <= len_); + len_ -= written; + memmove(buffer_, buffer_ + written, len_); + send_required = false; + } else if (result == SR_BLOCK) { + if (send_required) { + // Nothing more we can do until network is writeable. + return; + } + } else { + ASSERT(result == SR_ERROR); + LOG_F(LS_ERROR) << "error"; + OnHttpStreamEvent(http_stream_, SE_CLOSE, error); + return; + } + } + + ASSERT(false); +} + +bool +HttpBase::queue_headers() { + ASSERT(HM_SEND == mode_); + while (header_ != data_->end()) { + size_t len = sprintfn(buffer_ + len_, sizeof(buffer_) - len_, + "%.*s: %.*s\r\n", + header_->first.size(), header_->first.data(), + header_->second.size(), header_->second.data()); + if (len_ + len < sizeof(buffer_) - 3) { + len_ += len; + ++header_; + } else if (len_ == 0) { + LOG(WARNING) << "discarding header that is too long: " << header_->first; + ++header_; + } else { + // Not enough room for the next header, write to network first. + return true; + } + } + // End of headers + len_ += strcpyn(buffer_ + len_, sizeof(buffer_) - len_, "\r\n"); + return false; +} + +void +HttpBase::do_complete(HttpError err) { + ASSERT(mode_ != HM_NONE); + HttpMode mode = mode_; + mode_ = HM_NONE; + if (data_ && data_->document) { + data_->document->SignalEvent.disconnect(this); + } + data_ = NULL; + if ((HM_RECV == mode) && doc_stream_) { + ASSERT(HE_NONE != err); // We should have Disconnected doc_stream_ already. + DocumentStream* ds = doc_stream_; + ds->Disconnect(err); + ds->SignalEvent(ds, SE_CLOSE, err); + } + if (notify_) { + notify_->onHttpComplete(mode, err); + } +} + +// +// Stream Signals +// + +void +HttpBase::OnHttpStreamEvent(StreamInterface* stream, int events, int error) { + ASSERT(stream == http_stream_); + if ((events & SE_OPEN) && (mode_ == HM_CONNECT)) { + do_complete(); + return; + } + + if ((events & SE_WRITE) && (mode_ == HM_SEND)) { + flush_data(); + return; + } + + if ((events & SE_READ) && (mode_ == HM_RECV)) { + if (doc_stream_) { + doc_stream_->SignalEvent(doc_stream_, SE_READ, 0); + } else { + read_and_process_data(); + } + return; + } + + if ((events & SE_CLOSE) == 0) + return; + + HttpError http_error = HandleStreamClose(error); + if (mode_ == HM_RECV) { + complete(http_error); + } else if (mode_ != HM_NONE) { + do_complete(http_error); + } else if (notify_) { + notify_->onHttpClosed(http_error); + } +} + +void +HttpBase::OnDocumentEvent(StreamInterface* stream, int events, int error) { + ASSERT(stream == data_->document.get()); + if ((events & SE_WRITE) && (mode_ == HM_RECV)) { + read_and_process_data(); + return; + } + + if ((events & SE_READ) && (mode_ == HM_SEND)) { + flush_data(); + return; + } + + if (events & SE_CLOSE) { + LOG_F(LS_ERROR) << "Read error: " << error; + do_complete(HE_STREAM); + return; + } +} + +// +// HttpParser Implementation +// + +HttpParser::ProcessResult +HttpBase::ProcessLeader(const char* line, size_t len, HttpError* error) { + *error = data_->parseLeader(line, len); + return (HE_NONE == *error) ? PR_CONTINUE : PR_COMPLETE; +} + +HttpParser::ProcessResult +HttpBase::ProcessHeader(const char* name, size_t nlen, const char* value, + size_t vlen, HttpError* error) { + std::string sname(name, nlen), svalue(value, vlen); + data_->addHeader(sname, svalue); + return PR_CONTINUE; +} + +HttpParser::ProcessResult +HttpBase::ProcessHeaderComplete(bool chunked, size_t& data_size, + HttpError* error) { + StreamInterface* old_docstream = doc_stream_; + if (notify_) { + *error = notify_->onHttpHeaderComplete(chunked, data_size); + // The request must not be aborted as a result of this callback. + ASSERT(NULL != data_); + } + if ((HE_NONE == *error) && data_->document) { + data_->document->SignalEvent.connect(this, &HttpBase::OnDocumentEvent); + } + if (HE_NONE != *error) { + return PR_COMPLETE; + } + if (old_docstream != doc_stream_) { + // Break out of Process loop, since our I/O model just changed. + return PR_BLOCK; + } + return PR_CONTINUE; +} + +HttpParser::ProcessResult +HttpBase::ProcessData(const char* data, size_t len, size_t& read, + HttpError* error) { + if (ignore_data_ || !data_->document) { + read = len; + return PR_CONTINUE; + } + int write_error = 0; + switch (data_->document->Write(data, len, &read, &write_error)) { + case SR_SUCCESS: + return PR_CONTINUE; + case SR_BLOCK: + return PR_BLOCK; + case SR_EOS: + LOG_F(LS_ERROR) << "Unexpected EOS"; + *error = HE_STREAM; + return PR_COMPLETE; + case SR_ERROR: + default: + LOG_F(LS_ERROR) << "Write error: " << write_error; + *error = HE_STREAM; + return PR_COMPLETE; + } +} + +void +HttpBase::OnComplete(HttpError err) { + LOG_F(LS_VERBOSE); + do_complete(err); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpbase.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpbase.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpbase.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpbase.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,181 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + + +#ifndef WEBRTC_BASE_HTTPBASE_H__ +#define WEBRTC_BASE_HTTPBASE_H__ + +#include "webrtc/base/httpcommon.h" + +namespace rtc { + +class StreamInterface; + +/////////////////////////////////////////////////////////////////////////////// +// HttpParser - Parses an HTTP stream provided via Process and end_of_input, and +// generates events for: +// Structural Elements: Leader, Headers, Document Data +// Events: End of Headers, End of Document, Errors +/////////////////////////////////////////////////////////////////////////////// + +class HttpParser { +public: + enum ProcessResult { PR_CONTINUE, PR_BLOCK, PR_COMPLETE }; + HttpParser(); + virtual ~HttpParser(); + + void reset(); + ProcessResult Process(const char* buffer, size_t len, size_t* processed, + HttpError* error); + bool is_valid_end_of_input() const; + void complete(HttpError err); + + size_t GetDataRemaining() const { return data_size_; } + +protected: + ProcessResult ProcessLine(const char* line, size_t len, HttpError* error); + + // HttpParser Interface + virtual ProcessResult ProcessLeader(const char* line, size_t len, + HttpError* error) = 0; + virtual ProcessResult ProcessHeader(const char* name, size_t nlen, + const char* value, size_t vlen, + HttpError* error) = 0; + virtual ProcessResult ProcessHeaderComplete(bool chunked, size_t& data_size, + HttpError* error) = 0; + virtual ProcessResult ProcessData(const char* data, size_t len, size_t& read, + HttpError* error) = 0; + virtual void OnComplete(HttpError err) = 0; + +private: + enum State { + ST_LEADER, ST_HEADERS, + ST_CHUNKSIZE, ST_CHUNKTERM, ST_TRAILERS, + ST_DATA, ST_COMPLETE + } state_; + bool chunked_; + size_t data_size_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// IHttpNotify +/////////////////////////////////////////////////////////////////////////////// + +enum HttpMode { HM_NONE, HM_CONNECT, HM_RECV, HM_SEND }; + +class IHttpNotify { +public: + virtual ~IHttpNotify() {} + virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size) = 0; + virtual void onHttpComplete(HttpMode mode, HttpError err) = 0; + virtual void onHttpClosed(HttpError err) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// HttpBase - Provides a state machine for implementing HTTP-based components. +// Attach HttpBase to a StreamInterface which represents a bidirectional HTTP +// stream, and then call send() or recv() to initiate sending or receiving one +// side of an HTTP transaction. By default, HttpBase operates as an I/O pump, +// moving data from the HTTP stream to the HttpData object and vice versa. +// However, it can also operate in stream mode, in which case the user of the +// stream interface drives I/O via calls to Read(). +/////////////////////////////////////////////////////////////////////////////// + +class HttpBase +: private HttpParser, + public sigslot::has_slots<> +{ +public: + HttpBase(); + virtual ~HttpBase(); + + void notify(IHttpNotify* notify) { notify_ = notify; } + bool attach(StreamInterface* stream); + StreamInterface* stream() { return http_stream_; } + StreamInterface* detach(); + bool isConnected() const; + + void send(HttpData* data); + void recv(HttpData* data); + void abort(HttpError err); + + HttpMode mode() const { return mode_; } + + void set_ignore_data(bool ignore) { ignore_data_ = ignore; } + bool ignore_data() const { return ignore_data_; } + + // Obtaining this stream puts HttpBase into stream mode until the stream + // is closed. HttpBase can only expose one open stream interface at a time. + // Further calls will return NULL. + StreamInterface* GetDocumentStream(); + +protected: + // Do cleanup when the http stream closes (error may be 0 for a clean + // shutdown), and return the error code to signal. + HttpError HandleStreamClose(int error); + + // DoReceiveLoop acts as a data pump, pulling data from the http stream, + // pushing it through the HttpParser, and then populating the HttpData object + // based on the callbacks from the parser. One of the most interesting + // callbacks is ProcessData, which provides the actual http document body. + // This data is then written to the HttpData::document. As a result, data + // flows from the network to the document, with some incidental protocol + // parsing in between. + // Ideally, we would pass in the document* to DoReceiveLoop, to more easily + // support GetDocumentStream(). However, since the HttpParser is callback + // driven, we are forced to store the pointer somewhere until the callback + // is triggered. + // Returns true if the received document has finished, and + // HttpParser::complete should be called. + bool DoReceiveLoop(HttpError* err); + + void read_and_process_data(); + void flush_data(); + bool queue_headers(); + void do_complete(HttpError err = HE_NONE); + + void OnHttpStreamEvent(StreamInterface* stream, int events, int error); + void OnDocumentEvent(StreamInterface* stream, int events, int error); + + // HttpParser Interface + virtual ProcessResult ProcessLeader(const char* line, size_t len, + HttpError* error); + virtual ProcessResult ProcessHeader(const char* name, size_t nlen, + const char* value, size_t vlen, + HttpError* error); + virtual ProcessResult ProcessHeaderComplete(bool chunked, size_t& data_size, + HttpError* error); + virtual ProcessResult ProcessData(const char* data, size_t len, size_t& read, + HttpError* error); + virtual void OnComplete(HttpError err); + +private: + class DocumentStream; + friend class DocumentStream; + + enum { kBufferSize = 32 * 1024 }; + + HttpMode mode_; + HttpData* data_; + IHttpNotify* notify_; + StreamInterface* http_stream_; + DocumentStream* doc_stream_; + char buffer_[kBufferSize]; + size_t len_; + + bool ignore_data_, chunk_data_; + HttpData::const_iterator header_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_HTTPBASE_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpbase_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpbase_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpbase_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpbase_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,520 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/httpbase.h" +#include "webrtc/base/testutils.h" + +namespace rtc { + +const char* const kHttpResponse = + "HTTP/1.1 200\r\n" + "Connection: Keep-Alive\r\n" + "Content-Type: text/plain\r\n" + "Proxy-Authorization: 42\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "00000008\r\n" + "Goodbye!\r\n" + "0\r\n\r\n"; + +const char* const kHttpEmptyResponse = + "HTTP/1.1 200\r\n" + "Connection: Keep-Alive\r\n" + "Content-Length: 0\r\n" + "Proxy-Authorization: 42\r\n" + "\r\n"; + +const char* const kHttpResponsePrefix = + "HTTP/1.1 200\r\n" + "Connection: Keep-Alive\r\n" + "Content-Type: text/plain\r\n" + "Proxy-Authorization: 42\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "8\r\n" + "Goodbye!\r\n"; + +class HttpBaseTest : public testing::Test, public IHttpNotify { +public: + enum EventType { E_HEADER_COMPLETE, E_COMPLETE, E_CLOSED }; + struct Event { + EventType event; + bool chunked; + size_t data_size; + HttpMode mode; + HttpError err; + }; + HttpBaseTest() : mem(NULL), obtain_stream(false), http_stream(NULL) { } + + virtual void SetUp() { } + virtual void TearDown() { + delete http_stream; + // Avoid an ASSERT, in case a test doesn't clean up properly + base.abort(HE_NONE); + } + + virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size) { + LOG_F(LS_VERBOSE) << "chunked: " << chunked << " size: " << data_size; + Event e = { E_HEADER_COMPLETE, chunked, data_size, HM_NONE, HE_NONE}; + events.push_back(e); + if (obtain_stream) { + ObtainDocumentStream(); + } + return HE_NONE; + } + virtual void onHttpComplete(HttpMode mode, HttpError err) { + LOG_F(LS_VERBOSE) << "mode: " << mode << " err: " << err; + Event e = { E_COMPLETE, false, 0, mode, err }; + events.push_back(e); + } + virtual void onHttpClosed(HttpError err) { + LOG_F(LS_VERBOSE) << "err: " << err; + Event e = { E_CLOSED, false, 0, HM_NONE, err }; + events.push_back(e); + } + + void SetupSource(const char* response); + + void VerifyHeaderComplete(size_t event_count, bool empty_doc); + void VerifyDocumentContents(const char* expected_data, + size_t expected_length = SIZE_UNKNOWN); + + void ObtainDocumentStream(); + void VerifyDocumentStreamIsOpening(); + void VerifyDocumentStreamOpenEvent(); + void ReadDocumentStreamData(const char* expected_data); + void VerifyDocumentStreamIsEOS(); + + void SetupDocument(const char* response); + void VerifySourceContents(const char* expected_data, + size_t expected_length = SIZE_UNKNOWN); + + void VerifyTransferComplete(HttpMode mode, HttpError error); + + HttpBase base; + MemoryStream* mem; + HttpResponseData data; + + // The source of http data, and source events + testing::StreamSource src; + std::vector events; + + // Document stream, and stream events + bool obtain_stream; + StreamInterface* http_stream; + testing::StreamSink sink; +}; + +void HttpBaseTest::SetupSource(const char* http_data) { + LOG_F(LS_VERBOSE) << "Enter"; + + src.SetState(SS_OPENING); + src.QueueString(http_data); + + base.notify(this); + base.attach(&src); + EXPECT_TRUE(events.empty()); + + src.SetState(SS_OPEN); + ASSERT_EQ(1U, events.size()); + EXPECT_EQ(E_COMPLETE, events[0].event); + EXPECT_EQ(HM_CONNECT, events[0].mode); + EXPECT_EQ(HE_NONE, events[0].err); + events.clear(); + + mem = new MemoryStream; + data.document.reset(mem); + LOG_F(LS_VERBOSE) << "Exit"; +} + +void HttpBaseTest::VerifyHeaderComplete(size_t event_count, bool empty_doc) { + LOG_F(LS_VERBOSE) << "Enter"; + + ASSERT_EQ(event_count, events.size()); + EXPECT_EQ(E_HEADER_COMPLETE, events[0].event); + + std::string header; + EXPECT_EQ(HVER_1_1, data.version); + EXPECT_EQ(static_cast(HC_OK), data.scode); + EXPECT_TRUE(data.hasHeader(HH_PROXY_AUTHORIZATION, &header)); + EXPECT_EQ("42", header); + EXPECT_TRUE(data.hasHeader(HH_CONNECTION, &header)); + EXPECT_EQ("Keep-Alive", header); + + if (empty_doc) { + EXPECT_FALSE(events[0].chunked); + EXPECT_EQ(0U, events[0].data_size); + + EXPECT_TRUE(data.hasHeader(HH_CONTENT_LENGTH, &header)); + EXPECT_EQ("0", header); + } else { + EXPECT_TRUE(events[0].chunked); + EXPECT_EQ(SIZE_UNKNOWN, events[0].data_size); + + EXPECT_TRUE(data.hasHeader(HH_CONTENT_TYPE, &header)); + EXPECT_EQ("text/plain", header); + EXPECT_TRUE(data.hasHeader(HH_TRANSFER_ENCODING, &header)); + EXPECT_EQ("chunked", header); + } + LOG_F(LS_VERBOSE) << "Exit"; +} + +void HttpBaseTest::VerifyDocumentContents(const char* expected_data, + size_t expected_length) { + LOG_F(LS_VERBOSE) << "Enter"; + + if (SIZE_UNKNOWN == expected_length) { + expected_length = strlen(expected_data); + } + EXPECT_EQ(mem, data.document.get()); + + size_t length; + mem->GetSize(&length); + EXPECT_EQ(expected_length, length); + EXPECT_TRUE(0 == memcmp(expected_data, mem->GetBuffer(), length)); + LOG_F(LS_VERBOSE) << "Exit"; +} + +void HttpBaseTest::ObtainDocumentStream() { + LOG_F(LS_VERBOSE) << "Enter"; + EXPECT_FALSE(http_stream); + http_stream = base.GetDocumentStream(); + ASSERT_TRUE(NULL != http_stream); + sink.Monitor(http_stream); + LOG_F(LS_VERBOSE) << "Exit"; +} + +void HttpBaseTest::VerifyDocumentStreamIsOpening() { + LOG_F(LS_VERBOSE) << "Enter"; + ASSERT_TRUE(NULL != http_stream); + EXPECT_EQ(0, sink.Events(http_stream)); + EXPECT_EQ(SS_OPENING, http_stream->GetState()); + + size_t read = 0; + char buffer[5] = { 0 }; + EXPECT_EQ(SR_BLOCK, http_stream->Read(buffer, sizeof(buffer), &read, NULL)); + LOG_F(LS_VERBOSE) << "Exit"; +} + +void HttpBaseTest::VerifyDocumentStreamOpenEvent() { + LOG_F(LS_VERBOSE) << "Enter"; + + ASSERT_TRUE(NULL != http_stream); + EXPECT_EQ(SE_OPEN | SE_READ, sink.Events(http_stream)); + EXPECT_EQ(SS_OPEN, http_stream->GetState()); + + // HTTP headers haven't arrived yet + EXPECT_EQ(0U, events.size()); + EXPECT_EQ(static_cast(HC_INTERNAL_SERVER_ERROR), data.scode); + LOG_F(LS_VERBOSE) << "Exit"; +} + +void HttpBaseTest::ReadDocumentStreamData(const char* expected_data) { + LOG_F(LS_VERBOSE) << "Enter"; + + ASSERT_TRUE(NULL != http_stream); + EXPECT_EQ(SS_OPEN, http_stream->GetState()); + + // Pump the HTTP I/O using Read, and verify the results. + size_t verified_length = 0; + const size_t expected_length = strlen(expected_data); + while (verified_length < expected_length) { + size_t read = 0; + char buffer[5] = { 0 }; + size_t amt_to_read = _min(expected_length - verified_length, sizeof(buffer)); + EXPECT_EQ(SR_SUCCESS, http_stream->Read(buffer, amt_to_read, &read, NULL)); + EXPECT_EQ(amt_to_read, read); + EXPECT_TRUE(0 == memcmp(expected_data + verified_length, buffer, read)); + verified_length += read; + } + LOG_F(LS_VERBOSE) << "Exit"; +} + +void HttpBaseTest::VerifyDocumentStreamIsEOS() { + LOG_F(LS_VERBOSE) << "Enter"; + + ASSERT_TRUE(NULL != http_stream); + size_t read = 0; + char buffer[5] = { 0 }; + EXPECT_EQ(SR_EOS, http_stream->Read(buffer, sizeof(buffer), &read, NULL)); + EXPECT_EQ(SS_CLOSED, http_stream->GetState()); + + // When EOS is caused by Read, we don't expect SE_CLOSE + EXPECT_EQ(0, sink.Events(http_stream)); + LOG_F(LS_VERBOSE) << "Exit"; +} + +void HttpBaseTest::SetupDocument(const char* document_data) { + LOG_F(LS_VERBOSE) << "Enter"; + src.SetState(SS_OPEN); + + base.notify(this); + base.attach(&src); + EXPECT_TRUE(events.empty()); + + if (document_data) { + // Note: we could just call data.set_success("text/plain", mem), but that + // won't allow us to use the chunked transfer encoding. + mem = new MemoryStream(document_data); + data.document.reset(mem); + data.setHeader(HH_CONTENT_TYPE, "text/plain"); + data.setHeader(HH_TRANSFER_ENCODING, "chunked"); + } else { + data.setHeader(HH_CONTENT_LENGTH, "0"); + } + data.scode = HC_OK; + data.setHeader(HH_PROXY_AUTHORIZATION, "42"); + data.setHeader(HH_CONNECTION, "Keep-Alive"); + LOG_F(LS_VERBOSE) << "Exit"; +} + +void HttpBaseTest::VerifySourceContents(const char* expected_data, + size_t expected_length) { + LOG_F(LS_VERBOSE) << "Enter"; + if (SIZE_UNKNOWN == expected_length) { + expected_length = strlen(expected_data); + } + std::string contents = src.ReadData(); + EXPECT_EQ(expected_length, contents.length()); + EXPECT_TRUE(0 == memcmp(expected_data, contents.data(), expected_length)); + LOG_F(LS_VERBOSE) << "Exit"; +} + +void HttpBaseTest::VerifyTransferComplete(HttpMode mode, HttpError error) { + LOG_F(LS_VERBOSE) << "Enter"; + // Verify that http operation has completed + ASSERT_TRUE(events.size() > 0); + size_t last_event = events.size() - 1; + EXPECT_EQ(E_COMPLETE, events[last_event].event); + EXPECT_EQ(mode, events[last_event].mode); + EXPECT_EQ(error, events[last_event].err); + LOG_F(LS_VERBOSE) << "Exit"; +} + +// +// Tests +// + +TEST_F(HttpBaseTest, SupportsSend) { + // Queue response document + SetupDocument("Goodbye!"); + + // Begin send + base.send(&data); + + // Send completed successfully + VerifyTransferComplete(HM_SEND, HE_NONE); + VerifySourceContents(kHttpResponse); +} + +TEST_F(HttpBaseTest, SupportsSendNoDocument) { + // Queue response document + SetupDocument(NULL); + + // Begin send + base.send(&data); + + // Send completed successfully + VerifyTransferComplete(HM_SEND, HE_NONE); + VerifySourceContents(kHttpEmptyResponse); +} + +TEST_F(HttpBaseTest, SignalsCompleteOnInterruptedSend) { + // This test is attempting to expose a bug that occurs when a particular + // base objects is used for receiving, and then used for sending. In + // particular, the HttpParser state is different after receiving. Simulate + // that here. + SetupSource(kHttpResponse); + base.recv(&data); + VerifyTransferComplete(HM_RECV, HE_NONE); + + src.Clear(); + data.clear(true); + events.clear(); + base.detach(); + + // Queue response document + SetupDocument("Goodbye!"); + + // Prevent entire response from being sent + const size_t kInterruptedLength = strlen(kHttpResponse) - 1; + src.SetWriteBlock(kInterruptedLength); + + // Begin send + base.send(&data); + + // Document is mostly complete, but no completion signal yet. + EXPECT_TRUE(events.empty()); + VerifySourceContents(kHttpResponse, kInterruptedLength); + + src.SetState(SS_CLOSED); + + // Send completed with disconnect error, and no additional data. + VerifyTransferComplete(HM_SEND, HE_DISCONNECTED); + EXPECT_TRUE(src.ReadData().empty()); +} + +TEST_F(HttpBaseTest, SupportsReceiveViaDocumentPush) { + // Queue response document + SetupSource(kHttpResponse); + + // Begin receive + base.recv(&data); + + // Document completed successfully + VerifyHeaderComplete(2, false); + VerifyTransferComplete(HM_RECV, HE_NONE); + VerifyDocumentContents("Goodbye!"); +} + +TEST_F(HttpBaseTest, SupportsReceiveViaStreamPull) { + // Switch to pull mode + ObtainDocumentStream(); + VerifyDocumentStreamIsOpening(); + + // Queue response document + SetupSource(kHttpResponse); + VerifyDocumentStreamIsOpening(); + + // Begin receive + base.recv(&data); + + // Pull document data + VerifyDocumentStreamOpenEvent(); + ReadDocumentStreamData("Goodbye!"); + VerifyDocumentStreamIsEOS(); + + // Document completed successfully + VerifyHeaderComplete(2, false); + VerifyTransferComplete(HM_RECV, HE_NONE); + VerifyDocumentContents(""); +} + +TEST_F(HttpBaseTest, DISABLED_AllowsCloseStreamBeforeDocumentIsComplete) { + + // TODO: Remove extra logging once test failure is understood + int old_sev = rtc::LogMessage::GetLogToDebug(); + rtc::LogMessage::LogToDebug(LS_VERBOSE); + + + // Switch to pull mode + ObtainDocumentStream(); + VerifyDocumentStreamIsOpening(); + + // Queue response document + SetupSource(kHttpResponse); + VerifyDocumentStreamIsOpening(); + + // Begin receive + base.recv(&data); + + // Pull some of the data + VerifyDocumentStreamOpenEvent(); + ReadDocumentStreamData("Goodb"); + + // We've seen the header by now + VerifyHeaderComplete(1, false); + + // Close the pull stream, this will transition back to push I/O. + http_stream->Close(); + Thread::Current()->ProcessMessages(0); + + // Remainder of document completed successfully + VerifyTransferComplete(HM_RECV, HE_NONE); + VerifyDocumentContents("ye!"); + + rtc::LogMessage::LogToDebug(old_sev); +} + +TEST_F(HttpBaseTest, AllowsGetDocumentStreamInResponseToHttpHeader) { + // Queue response document + SetupSource(kHttpResponse); + + // Switch to pull mode in response to header arrival + obtain_stream = true; + + // Begin receive + base.recv(&data); + + // We've already seen the header, but not data has arrived + VerifyHeaderComplete(1, false); + VerifyDocumentContents(""); + + // Pull the document data + ReadDocumentStreamData("Goodbye!"); + VerifyDocumentStreamIsEOS(); + + // Document completed successfully + VerifyTransferComplete(HM_RECV, HE_NONE); + VerifyDocumentContents(""); +} + +TEST_F(HttpBaseTest, AllowsGetDocumentStreamWithEmptyDocumentBody) { + // Queue empty response document + SetupSource(kHttpEmptyResponse); + + // Switch to pull mode in response to header arrival + obtain_stream = true; + + // Begin receive + base.recv(&data); + + // We've already seen the header, but not data has arrived + VerifyHeaderComplete(1, true); + VerifyDocumentContents(""); + + // The document is still open, until we attempt to read + ASSERT_TRUE(NULL != http_stream); + EXPECT_EQ(SS_OPEN, http_stream->GetState()); + + // Attempt to read data, and discover EOS + VerifyDocumentStreamIsEOS(); + + // Document completed successfully + VerifyTransferComplete(HM_RECV, HE_NONE); + VerifyDocumentContents(""); +} + +TEST_F(HttpBaseTest, SignalsDocumentStreamCloseOnUnexpectedClose) { + // Switch to pull mode + ObtainDocumentStream(); + VerifyDocumentStreamIsOpening(); + + // Queue response document + SetupSource(kHttpResponsePrefix); + VerifyDocumentStreamIsOpening(); + + // Begin receive + base.recv(&data); + + // Pull document data + VerifyDocumentStreamOpenEvent(); + ReadDocumentStreamData("Goodbye!"); + + // Simulate unexpected close + src.SetState(SS_CLOSED); + + // Observe error event on document stream + EXPECT_EQ(testing::SSE_ERROR, sink.Events(http_stream)); + + // Future reads give an error + int error = 0; + char buffer[5] = { 0 }; + EXPECT_EQ(SR_ERROR, http_stream->Read(buffer, sizeof(buffer), NULL, &error)); + EXPECT_EQ(HE_DISCONNECTED, error); + + // Document completed with error + VerifyHeaderComplete(2, false); + VerifyTransferComplete(HM_RECV, HE_DISCONNECTED); + VerifyDocumentContents(""); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpclient.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpclient.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpclient.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpclient.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,832 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/httpcommon-inl.h" + +#include "webrtc/base/asyncsocket.h" +#include "webrtc/base/common.h" +#include "webrtc/base/diskcache.h" +#include "webrtc/base/httpclient.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/pathutils.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/socketstream.h" +#include "webrtc/base/stringencode.h" +#include "webrtc/base/stringutils.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +////////////////////////////////////////////////////////////////////// +// Helpers +////////////////////////////////////////////////////////////////////// + +namespace { + +const size_t kCacheHeader = 0; +const size_t kCacheBody = 1; + +// Convert decimal string to integer +bool HttpStringToUInt(const std::string& str, size_t* val) { + ASSERT(NULL != val); + char* eos = NULL; + *val = strtoul(str.c_str(), &eos, 10); + return (*eos == '\0'); +} + +bool HttpShouldCache(const HttpTransaction& t) { + bool verb_allows_cache = (t.request.verb == HV_GET) + || (t.request.verb == HV_HEAD); + bool is_range_response = t.response.hasHeader(HH_CONTENT_RANGE, NULL); + bool has_expires = t.response.hasHeader(HH_EXPIRES, NULL); + bool request_allows_cache = + has_expires || (std::string::npos != t.request.path.find('?')); + bool response_allows_cache = + has_expires || HttpCodeIsCacheable(t.response.scode); + + bool may_cache = verb_allows_cache + && request_allows_cache + && response_allows_cache + && !is_range_response; + + std::string value; + if (t.response.hasHeader(HH_CACHE_CONTROL, &value)) { + HttpAttributeList directives; + HttpParseAttributes(value.data(), value.size(), directives); + // Response Directives Summary: + // public - always cacheable + // private - do not cache in a shared cache + // no-cache - may cache, but must revalidate whether fresh or stale + // no-store - sensitive information, do not cache or store in any way + // max-age - supplants Expires for staleness + // s-maxage - use as max-age for shared caches, ignore otherwise + // must-revalidate - may cache, but must revalidate after stale + // proxy-revalidate - shared cache must revalidate + if (HttpHasAttribute(directives, "no-store", NULL)) { + may_cache = false; + } else if (HttpHasAttribute(directives, "public", NULL)) { + may_cache = true; + } + } + return may_cache; +} + +enum HttpCacheState { + HCS_FRESH, // In cache, may use + HCS_STALE, // In cache, must revalidate + HCS_NONE // Not in cache +}; + +HttpCacheState HttpGetCacheState(const HttpTransaction& t) { + // Temporaries + std::string s_temp; + time_t u_temp; + + // Current time + time_t now = time(0); + + HttpAttributeList cache_control; + if (t.response.hasHeader(HH_CACHE_CONTROL, &s_temp)) { + HttpParseAttributes(s_temp.data(), s_temp.size(), cache_control); + } + + // Compute age of cache document + time_t date; + if (!t.response.hasHeader(HH_DATE, &s_temp) + || !HttpDateToSeconds(s_temp, &date)) + return HCS_NONE; + + // TODO: Timestamp when cache request sent and response received? + time_t request_time = date; + time_t response_time = date; + + time_t apparent_age = 0; + if (response_time > date) { + apparent_age = response_time - date; + } + + time_t corrected_received_age = apparent_age; + size_t i_temp; + if (t.response.hasHeader(HH_AGE, &s_temp) + && HttpStringToUInt(s_temp, (&i_temp))) { + u_temp = static_cast(i_temp); + corrected_received_age = stdmax(apparent_age, u_temp); + } + + time_t response_delay = response_time - request_time; + time_t corrected_initial_age = corrected_received_age + response_delay; + time_t resident_time = now - response_time; + time_t current_age = corrected_initial_age + resident_time; + + // Compute lifetime of document + time_t lifetime; + if (HttpHasAttribute(cache_control, "max-age", &s_temp)) { + lifetime = atoi(s_temp.c_str()); + } else if (t.response.hasHeader(HH_EXPIRES, &s_temp) + && HttpDateToSeconds(s_temp, &u_temp)) { + lifetime = u_temp - date; + } else if (t.response.hasHeader(HH_LAST_MODIFIED, &s_temp) + && HttpDateToSeconds(s_temp, &u_temp)) { + // TODO: Issue warning 113 if age > 24 hours + lifetime = static_cast(now - u_temp) / 10; + } else { + return HCS_STALE; + } + + return (lifetime > current_age) ? HCS_FRESH : HCS_STALE; +} + +enum HttpValidatorStrength { + HVS_NONE, + HVS_WEAK, + HVS_STRONG +}; + +HttpValidatorStrength +HttpRequestValidatorLevel(const HttpRequestData& request) { + if (HV_GET != request.verb) + return HVS_STRONG; + return request.hasHeader(HH_RANGE, NULL) ? HVS_STRONG : HVS_WEAK; +} + +HttpValidatorStrength +HttpResponseValidatorLevel(const HttpResponseData& response) { + std::string value; + if (response.hasHeader(HH_ETAG, &value)) { + bool is_weak = (strnicmp(value.c_str(), "W/", 2) == 0); + return is_weak ? HVS_WEAK : HVS_STRONG; + } + if (response.hasHeader(HH_LAST_MODIFIED, &value)) { + time_t last_modified, date; + if (HttpDateToSeconds(value, &last_modified) + && response.hasHeader(HH_DATE, &value) + && HttpDateToSeconds(value, &date) + && (last_modified + 60 < date)) { + return HVS_STRONG; + } + return HVS_WEAK; + } + return HVS_NONE; +} + +std::string GetCacheID(const HttpRequestData& request) { + std::string id, url; + id.append(ToString(request.verb)); + id.append("_"); + request.getAbsoluteUri(&url); + id.append(url); + return id; +} + +} // anonymous namespace + +////////////////////////////////////////////////////////////////////// +// Public Helpers +////////////////////////////////////////////////////////////////////// + +bool HttpWriteCacheHeaders(const HttpResponseData* response, + StreamInterface* output, size_t* size) { + size_t length = 0; + // Write all unknown and end-to-end headers to a cache file + for (HttpData::const_iterator it = response->begin(); + it != response->end(); ++it) { + HttpHeader header; + if (FromString(header, it->first) && !HttpHeaderIsEndToEnd(header)) + continue; + length += it->first.length() + 2 + it->second.length() + 2; + if (!output) + continue; + std::string formatted_header(it->first); + formatted_header.append(": "); + formatted_header.append(it->second); + formatted_header.append("\r\n"); + StreamResult result = output->WriteAll(formatted_header.data(), + formatted_header.length(), + NULL, NULL); + if (SR_SUCCESS != result) { + return false; + } + } + if (output && (SR_SUCCESS != output->WriteAll("\r\n", 2, NULL, NULL))) { + return false; + } + length += 2; + if (size) + *size = length; + return true; +} + +bool HttpReadCacheHeaders(StreamInterface* input, HttpResponseData* response, + HttpData::HeaderCombine combine) { + while (true) { + std::string formatted_header; + StreamResult result = input->ReadLine(&formatted_header); + if ((SR_EOS == result) || (1 == formatted_header.size())) { + break; + } + if (SR_SUCCESS != result) { + return false; + } + size_t end_of_name = formatted_header.find(':'); + if (std::string::npos == end_of_name) { + LOG_F(LS_WARNING) << "Malformed cache header"; + continue; + } + size_t start_of_value = end_of_name + 1; + size_t end_of_value = formatted_header.length(); + while ((start_of_value < end_of_value) + && isspace(formatted_header[start_of_value])) + ++start_of_value; + while ((start_of_value < end_of_value) + && isspace(formatted_header[end_of_value-1])) + --end_of_value; + size_t value_length = end_of_value - start_of_value; + + std::string name(formatted_header.substr(0, end_of_name)); + std::string value(formatted_header.substr(start_of_value, value_length)); + response->changeHeader(name, value, combine); + } + return true; +} + +////////////////////////////////////////////////////////////////////// +// HttpClient +////////////////////////////////////////////////////////////////////// + +const size_t kDefaultRetries = 1; +const size_t kMaxRedirects = 5; + +HttpClient::HttpClient(const std::string& agent, StreamPool* pool, + HttpTransaction* transaction) + : agent_(agent), pool_(pool), + transaction_(transaction), free_transaction_(false), + retries_(kDefaultRetries), attempt_(0), redirects_(0), + redirect_action_(REDIRECT_DEFAULT), + uri_form_(URI_DEFAULT), cache_(NULL), cache_state_(CS_READY), + resolver_(NULL) { + base_.notify(this); + if (NULL == transaction_) { + free_transaction_ = true; + transaction_ = new HttpTransaction; + } +} + +HttpClient::~HttpClient() { + base_.notify(NULL); + base_.abort(HE_SHUTDOWN); + if (resolver_) { + resolver_->Destroy(false); + } + release(); + if (free_transaction_) + delete transaction_; +} + +void HttpClient::reset() { + server_.Clear(); + request().clear(true); + response().clear(true); + context_.reset(); + redirects_ = 0; + base_.abort(HE_OPERATION_CANCELLED); +} + +void HttpClient::OnResolveResult(AsyncResolverInterface* resolver) { + if (resolver != resolver_) { + return; + } + int error = resolver_->GetError(); + server_ = resolver_->address(); + resolver_->Destroy(false); + resolver_ = NULL; + if (error != 0) { + LOG(LS_ERROR) << "Error " << error << " resolving name: " + << server_; + onHttpComplete(HM_CONNECT, HE_CONNECT_FAILED); + } else { + connect(); + } +} + +void HttpClient::StartDNSLookup() { + resolver_ = new AsyncResolver(); + resolver_->SignalDone.connect(this, &HttpClient::OnResolveResult); + resolver_->Start(server_); +} + +void HttpClient::set_server(const SocketAddress& address) { + server_ = address; + // Setting 'Host' here allows it to be overridden before starting the request, + // if necessary. + request().setHeader(HH_HOST, HttpAddress(server_, false), true); +} + +StreamInterface* HttpClient::GetDocumentStream() { + return base_.GetDocumentStream(); +} + +void HttpClient::start() { + if (base_.mode() != HM_NONE) { + // call reset() to abort an in-progress request + ASSERT(false); + return; + } + + ASSERT(!IsCacheActive()); + + if (request().hasHeader(HH_TRANSFER_ENCODING, NULL)) { + // Exact size must be known on the client. Instead of using chunked + // encoding, wrap data with auto-caching file or memory stream. + ASSERT(false); + return; + } + + attempt_ = 0; + + // If no content has been specified, using length of 0. + request().setHeader(HH_CONTENT_LENGTH, "0", false); + + if (!agent_.empty()) { + request().setHeader(HH_USER_AGENT, agent_, false); + } + + UriForm uri_form = uri_form_; + if (PROXY_HTTPS == proxy_.type) { + // Proxies require absolute form + uri_form = URI_ABSOLUTE; + request().version = HVER_1_0; + request().setHeader(HH_PROXY_CONNECTION, "Keep-Alive", false); + } else { + request().setHeader(HH_CONNECTION, "Keep-Alive", false); + } + + if (URI_ABSOLUTE == uri_form) { + // Convert to absolute uri form + std::string url; + if (request().getAbsoluteUri(&url)) { + request().path = url; + } else { + LOG(LS_WARNING) << "Couldn't obtain absolute uri"; + } + } else if (URI_RELATIVE == uri_form) { + // Convert to relative uri form + std::string host, path; + if (request().getRelativeUri(&host, &path)) { + request().setHeader(HH_HOST, host); + request().path = path; + } else { + LOG(LS_WARNING) << "Couldn't obtain relative uri"; + } + } + + if ((NULL != cache_) && CheckCache()) { + return; + } + + connect(); +} + +void HttpClient::connect() { + int stream_err; + if (server_.IsUnresolvedIP()) { + StartDNSLookup(); + return; + } + StreamInterface* stream = pool_->RequestConnectedStream(server_, &stream_err); + if (stream == NULL) { + ASSERT(0 != stream_err); + LOG(LS_ERROR) << "RequestConnectedStream error: " << stream_err; + onHttpComplete(HM_CONNECT, HE_CONNECT_FAILED); + } else { + base_.attach(stream); + if (stream->GetState() == SS_OPEN) { + base_.send(&transaction_->request); + } + } +} + +void HttpClient::prepare_get(const std::string& url) { + reset(); + Url purl(url); + set_server(SocketAddress(purl.host(), purl.port())); + request().verb = HV_GET; + request().path = purl.full_path(); +} + +void HttpClient::prepare_post(const std::string& url, + const std::string& content_type, + StreamInterface* request_doc) { + reset(); + Url purl(url); + set_server(SocketAddress(purl.host(), purl.port())); + request().verb = HV_POST; + request().path = purl.full_path(); + request().setContent(content_type, request_doc); +} + +void HttpClient::release() { + if (StreamInterface* stream = base_.detach()) { + pool_->ReturnConnectedStream(stream); + } +} + +bool HttpClient::ShouldRedirect(std::string* location) const { + // TODO: Unittest redirection. + if ((REDIRECT_NEVER == redirect_action_) + || !HttpCodeIsRedirection(response().scode) + || !response().hasHeader(HH_LOCATION, location) + || (redirects_ >= kMaxRedirects)) + return false; + return (REDIRECT_ALWAYS == redirect_action_) + || (HC_SEE_OTHER == response().scode) + || (HV_HEAD == request().verb) + || (HV_GET == request().verb); +} + +bool HttpClient::BeginCacheFile() { + ASSERT(NULL != cache_); + ASSERT(CS_READY == cache_state_); + + std::string id = GetCacheID(request()); + CacheLock lock(cache_, id, true); + if (!lock.IsLocked()) { + LOG_F(LS_WARNING) << "Couldn't lock cache"; + return false; + } + + if (HE_NONE != WriteCacheHeaders(id)) { + return false; + } + + scoped_ptr stream(cache_->WriteResource(id, kCacheBody)); + if (!stream) { + LOG_F(LS_ERROR) << "Couldn't open body cache"; + return false; + } + lock.Commit(); + + // Let's secretly replace the response document with Folgers Crystals, + // er, StreamTap, so that we can mirror the data to our cache. + StreamInterface* output = response().document.release(); + if (!output) { + output = new NullStream; + } + StreamTap* tap = new StreamTap(output, stream.release()); + response().document.reset(tap); + return true; +} + +HttpError HttpClient::WriteCacheHeaders(const std::string& id) { + scoped_ptr stream(cache_->WriteResource(id, kCacheHeader)); + if (!stream) { + LOG_F(LS_ERROR) << "Couldn't open header cache"; + return HE_CACHE; + } + + if (!HttpWriteCacheHeaders(&transaction_->response, stream.get(), NULL)) { + LOG_F(LS_ERROR) << "Couldn't write header cache"; + return HE_CACHE; + } + + return HE_NONE; +} + +void HttpClient::CompleteCacheFile() { + // Restore previous response document + StreamTap* tap = static_cast(response().document.release()); + response().document.reset(tap->Detach()); + + int error; + StreamResult result = tap->GetTapResult(&error); + + // Delete the tap and cache stream (which completes cache unlock) + delete tap; + + if (SR_SUCCESS != result) { + LOG(LS_ERROR) << "Cache file error: " << error; + cache_->DeleteResource(GetCacheID(request())); + } +} + +bool HttpClient::CheckCache() { + ASSERT(NULL != cache_); + ASSERT(CS_READY == cache_state_); + + std::string id = GetCacheID(request()); + if (!cache_->HasResource(id)) { + // No cache file available + return false; + } + + HttpError error = ReadCacheHeaders(id, true); + + if (HE_NONE == error) { + switch (HttpGetCacheState(*transaction_)) { + case HCS_FRESH: + // Cache content is good, read from cache + break; + case HCS_STALE: + // Cache content may be acceptable. Issue a validation request. + if (PrepareValidate()) { + return false; + } + // Couldn't validate, fall through. + case HCS_NONE: + // Cache content is not useable. Issue a regular request. + response().clear(false); + return false; + } + } + + if (HE_NONE == error) { + error = ReadCacheBody(id); + cache_state_ = CS_READY; + } + + if (HE_CACHE == error) { + LOG_F(LS_WARNING) << "Cache failure, continuing with normal request"; + response().clear(false); + return false; + } + + SignalHttpClientComplete(this, error); + return true; +} + +HttpError HttpClient::ReadCacheHeaders(const std::string& id, bool override) { + scoped_ptr stream(cache_->ReadResource(id, kCacheHeader)); + if (!stream) { + return HE_CACHE; + } + + HttpData::HeaderCombine combine = + override ? HttpData::HC_REPLACE : HttpData::HC_AUTO; + + if (!HttpReadCacheHeaders(stream.get(), &transaction_->response, combine)) { + LOG_F(LS_ERROR) << "Error reading cache headers"; + return HE_CACHE; + } + + response().scode = HC_OK; + return HE_NONE; +} + +HttpError HttpClient::ReadCacheBody(const std::string& id) { + cache_state_ = CS_READING; + + HttpError error = HE_NONE; + + size_t data_size; + scoped_ptr stream(cache_->ReadResource(id, kCacheBody)); + if (!stream || !stream->GetAvailable(&data_size)) { + LOG_F(LS_ERROR) << "Unavailable cache body"; + error = HE_CACHE; + } else { + error = OnHeaderAvailable(false, false, data_size); + } + + if ((HE_NONE == error) + && (HV_HEAD != request().verb) + && response().document) { + // Allocate on heap to not explode the stack. + const int array_size = 1024 * 64; + scoped_ptr buffer(new char[array_size]); + StreamResult result = Flow(stream.get(), buffer.get(), array_size, + response().document.get()); + if (SR_SUCCESS != result) { + error = HE_STREAM; + } + } + + return error; +} + +bool HttpClient::PrepareValidate() { + ASSERT(CS_READY == cache_state_); + // At this point, request() contains the pending request, and response() + // contains the cached response headers. Reformat the request to validate + // the cached content. + HttpValidatorStrength vs_required = HttpRequestValidatorLevel(request()); + HttpValidatorStrength vs_available = HttpResponseValidatorLevel(response()); + if (vs_available < vs_required) { + return false; + } + std::string value; + if (response().hasHeader(HH_ETAG, &value)) { + request().addHeader(HH_IF_NONE_MATCH, value); + } + if (response().hasHeader(HH_LAST_MODIFIED, &value)) { + request().addHeader(HH_IF_MODIFIED_SINCE, value); + } + response().clear(false); + cache_state_ = CS_VALIDATING; + return true; +} + +HttpError HttpClient::CompleteValidate() { + ASSERT(CS_VALIDATING == cache_state_); + + std::string id = GetCacheID(request()); + + // Merge cached headers with new headers + HttpError error = ReadCacheHeaders(id, false); + if (HE_NONE != error) { + // Rewrite merged headers to cache + CacheLock lock(cache_, id); + error = WriteCacheHeaders(id); + } + if (HE_NONE != error) { + error = ReadCacheBody(id); + } + return error; +} + +HttpError HttpClient::OnHeaderAvailable(bool ignore_data, bool chunked, + size_t data_size) { + // If we are ignoring the data, this is an intermediate header. + // TODO: don't signal intermediate headers. Instead, do all header-dependent + // processing now, and either set up the next request, or fail outright. + // TODO: by default, only write response documents with a success code. + SignalHeaderAvailable(this, !ignore_data, ignore_data ? 0 : data_size); + if (!ignore_data && !chunked && (data_size != SIZE_UNKNOWN) + && response().document) { + // Attempt to pre-allocate space for the downloaded data. + if (!response().document->ReserveSize(data_size)) { + return HE_OVERFLOW; + } + } + return HE_NONE; +} + +// +// HttpBase Implementation +// + +HttpError HttpClient::onHttpHeaderComplete(bool chunked, size_t& data_size) { + if (CS_VALIDATING == cache_state_) { + if (HC_NOT_MODIFIED == response().scode) { + return CompleteValidate(); + } + // Should we remove conditional headers from request? + cache_state_ = CS_READY; + cache_->DeleteResource(GetCacheID(request())); + // Continue processing response as normal + } + + ASSERT(!IsCacheActive()); + if ((request().verb == HV_HEAD) || !HttpCodeHasBody(response().scode)) { + // HEAD requests and certain response codes contain no body + data_size = 0; + } + if (ShouldRedirect(NULL) + || ((HC_PROXY_AUTHENTICATION_REQUIRED == response().scode) + && (PROXY_HTTPS == proxy_.type))) { + // We're going to issue another request, so ignore the incoming data. + base_.set_ignore_data(true); + } + + HttpError error = OnHeaderAvailable(base_.ignore_data(), chunked, data_size); + if (HE_NONE != error) { + return error; + } + + if ((NULL != cache_) + && !base_.ignore_data() + && HttpShouldCache(*transaction_)) { + if (BeginCacheFile()) { + cache_state_ = CS_WRITING; + } + } + return HE_NONE; +} + +void HttpClient::onHttpComplete(HttpMode mode, HttpError err) { + if (((HE_DISCONNECTED == err) || (HE_CONNECT_FAILED == err) + || (HE_SOCKET_ERROR == err)) + && (HC_INTERNAL_SERVER_ERROR == response().scode) + && (attempt_ < retries_)) { + // If the response code has not changed from the default, then we haven't + // received anything meaningful from the server, so we are eligible for a + // retry. + ++attempt_; + if (request().document && !request().document->Rewind()) { + // Unable to replay the request document. + err = HE_STREAM; + } else { + release(); + connect(); + return; + } + } else if (err != HE_NONE) { + // fall through + } else if (mode == HM_CONNECT) { + base_.send(&transaction_->request); + return; + } else if ((mode == HM_SEND) || HttpCodeIsInformational(response().scode)) { + // If you're interested in informational headers, catch + // SignalHeaderAvailable. + base_.recv(&transaction_->response); + return; + } else { + if (!HttpShouldKeepAlive(response())) { + LOG(LS_VERBOSE) << "HttpClient: closing socket"; + base_.stream()->Close(); + } + std::string location; + if (ShouldRedirect(&location)) { + Url purl(location); + set_server(SocketAddress(purl.host(), purl.port())); + request().path = purl.full_path(); + if (response().scode == HC_SEE_OTHER) { + request().verb = HV_GET; + request().clearHeader(HH_CONTENT_TYPE); + request().clearHeader(HH_CONTENT_LENGTH); + request().document.reset(); + } else if (request().document && !request().document->Rewind()) { + // Unable to replay the request document. + ASSERT(REDIRECT_ALWAYS == redirect_action_); + err = HE_STREAM; + } + if (err == HE_NONE) { + ++redirects_; + context_.reset(); + response().clear(false); + release(); + start(); + return; + } + } else if ((HC_PROXY_AUTHENTICATION_REQUIRED == response().scode) + && (PROXY_HTTPS == proxy_.type)) { + std::string authorization, auth_method; + HttpData::const_iterator begin = response().begin(HH_PROXY_AUTHENTICATE); + HttpData::const_iterator end = response().end(HH_PROXY_AUTHENTICATE); + for (HttpData::const_iterator it = begin; it != end; ++it) { + HttpAuthContext *context = context_.get(); + HttpAuthResult res = HttpAuthenticate( + it->second.data(), it->second.size(), + proxy_.address, + ToString(request().verb), request().path, + proxy_.username, proxy_.password, + context, authorization, auth_method); + context_.reset(context); + if (res == HAR_RESPONSE) { + request().setHeader(HH_PROXY_AUTHORIZATION, authorization); + if (request().document && !request().document->Rewind()) { + err = HE_STREAM; + } else { + // Explicitly do not reset the HttpAuthContext + response().clear(false); + // TODO: Reuse socket when authenticating? + release(); + start(); + return; + } + } else if (res == HAR_IGNORE) { + LOG(INFO) << "Ignoring Proxy-Authenticate: " << auth_method; + continue; + } else { + break; + } + } + } + } + if (CS_WRITING == cache_state_) { + CompleteCacheFile(); + cache_state_ = CS_READY; + } else if (CS_READING == cache_state_) { + cache_state_ = CS_READY; + } + release(); + SignalHttpClientComplete(this, err); +} + +void HttpClient::onHttpClosed(HttpError err) { + // This shouldn't occur, since we return the stream to the pool upon command + // completion. + ASSERT(false); +} + +////////////////////////////////////////////////////////////////////// +// HttpClientDefault +////////////////////////////////////////////////////////////////////// + +HttpClientDefault::HttpClientDefault(SocketFactory* factory, + const std::string& agent, + HttpTransaction* transaction) + : ReuseSocketPool(factory ? factory : Thread::Current()->socketserver()), + HttpClient(agent, NULL, transaction) { + set_pool(this); +} + +////////////////////////////////////////////////////////////////////// + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpclient.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpclient.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpclient.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpclient.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,202 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_HTTPCLIENT_H__ +#define WEBRTC_BASE_HTTPCLIENT_H__ + +#include "webrtc/base/common.h" +#include "webrtc/base/httpbase.h" +#include "webrtc/base/nethelpers.h" +#include "webrtc/base/proxyinfo.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/sigslot.h" +#include "webrtc/base/socketaddress.h" +#include "webrtc/base/socketpool.h" + +namespace rtc { + +////////////////////////////////////////////////////////////////////// +// Client-specific http utilities +////////////////////////////////////////////////////////////////////// + +// Write cache-relevant response headers to output stream. If size is non-null, +// it contains the length of the output in bytes. output may be null if only +// the length is desired. +bool HttpWriteCacheHeaders(const HttpResponseData* response, + StreamInterface* output, size_t* size); +// Read cached headers from a stream, and them merge them into the response +// object using the specified combine operation. +bool HttpReadCacheHeaders(StreamInterface* input, + HttpResponseData* response, + HttpData::HeaderCombine combine); + +////////////////////////////////////////////////////////////////////// +// HttpClient +// Implements an HTTP 1.1 client. +////////////////////////////////////////////////////////////////////// + +class DiskCache; +class HttpClient; +class IPNetPool; + +class SignalThread; +// What to do: Define STRICT_HTTP_ERROR=1 in your makefile. Use HttpError in +// your code (HttpErrorType should only be used for code that is shared +// with groups which have not yet migrated). +#if STRICT_HTTP_ERROR +typedef HttpError HttpErrorType; +#else // !STRICT_HTTP_ERROR +typedef int HttpErrorType; +#endif // !STRICT_HTTP_ERROR + +class HttpClient : private IHttpNotify, public sigslot::has_slots<> { +public: + // If HttpRequestData and HttpResponseData objects are provided, they must + // be freed by the caller. Otherwise, an internal object is allocated. + HttpClient(const std::string& agent, StreamPool* pool, + HttpTransaction* transaction = NULL); + virtual ~HttpClient(); + + void set_pool(StreamPool* pool) { pool_ = pool; } + + void set_agent(const std::string& agent) { agent_ = agent; } + const std::string& agent() const { return agent_; } + + void set_proxy(const ProxyInfo& proxy) { proxy_ = proxy; } + const ProxyInfo& proxy() const { return proxy_; } + + // Request retries occur when the connection closes before the beginning of + // an http response is received. In these cases, the http server may have + // timed out the keepalive connection before it received our request. Note + // that if a request document cannot be rewound, no retry is made. The + // default is 1. + void set_request_retries(size_t retries) { retries_ = retries; } + size_t request_retries() const { return retries_; } + + enum RedirectAction { REDIRECT_DEFAULT, REDIRECT_ALWAYS, REDIRECT_NEVER }; + void set_redirect_action(RedirectAction action) { redirect_action_ = action; } + RedirectAction redirect_action() const { return redirect_action_; } + // Deprecated + void set_fail_redirect(bool fail_redirect) { + redirect_action_ = REDIRECT_NEVER; + } + bool fail_redirect() const { return (REDIRECT_NEVER == redirect_action_); } + + enum UriForm { URI_DEFAULT, URI_ABSOLUTE, URI_RELATIVE }; + void set_uri_form(UriForm form) { uri_form_ = form; } + UriForm uri_form() const { return uri_form_; } + + void set_cache(DiskCache* cache) { ASSERT(!IsCacheActive()); cache_ = cache; } + bool cache_enabled() const { return (NULL != cache_); } + + // reset clears the server, request, and response structures. It will also + // abort an active request. + void reset(); + + void set_server(const SocketAddress& address); + const SocketAddress& server() const { return server_; } + + // Note: in order for HttpClient to retry a POST in response to + // an authentication challenge, a redirect response, or socket disconnection, + // the request document must support 'replaying' by calling Rewind() on it. + // In the case where just a subset of a stream should be used as the request + // document, the stream may be wrapped with the StreamSegment adapter. + HttpTransaction* transaction() { return transaction_; } + const HttpTransaction* transaction() const { return transaction_; } + HttpRequestData& request() { return transaction_->request; } + const HttpRequestData& request() const { return transaction_->request; } + HttpResponseData& response() { return transaction_->response; } + const HttpResponseData& response() const { return transaction_->response; } + + // convenience methods + void prepare_get(const std::string& url); + void prepare_post(const std::string& url, const std::string& content_type, + StreamInterface* request_doc); + + // Convert HttpClient to a pull-based I/O model. + StreamInterface* GetDocumentStream(); + + // After you finish setting up your request, call start. + void start(); + + // Signalled when the header has finished downloading, before the document + // content is processed. You may change the response document in response + // to this signal. The second parameter indicates whether this is an + // intermediate (false) or final (true) header. An intermediate header is + // one that generates another request, such as a redirect or authentication + // challenge. The third parameter indicates the length of the response + // document, or else SIZE_UNKNOWN. Note: Do NOT abort the request in response + // to this signal. + sigslot::signal3 SignalHeaderAvailable; + // Signalled when the current request finishes. On success, err is 0. + sigslot::signal2 SignalHttpClientComplete; + +protected: + void connect(); + void release(); + + bool ShouldRedirect(std::string* location) const; + + bool BeginCacheFile(); + HttpError WriteCacheHeaders(const std::string& id); + void CompleteCacheFile(); + + bool CheckCache(); + HttpError ReadCacheHeaders(const std::string& id, bool override); + HttpError ReadCacheBody(const std::string& id); + + bool PrepareValidate(); + HttpError CompleteValidate(); + + HttpError OnHeaderAvailable(bool ignore_data, bool chunked, size_t data_size); + + void StartDNSLookup(); + void OnResolveResult(AsyncResolverInterface* resolver); + + // IHttpNotify Interface + virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size); + virtual void onHttpComplete(HttpMode mode, HttpError err); + virtual void onHttpClosed(HttpError err); + +private: + enum CacheState { CS_READY, CS_WRITING, CS_READING, CS_VALIDATING }; + bool IsCacheActive() const { return (cache_state_ > CS_READY); } + + std::string agent_; + StreamPool* pool_; + HttpBase base_; + SocketAddress server_; + ProxyInfo proxy_; + HttpTransaction* transaction_; + bool free_transaction_; + size_t retries_, attempt_, redirects_; + RedirectAction redirect_action_; + UriForm uri_form_; + scoped_ptr context_; + DiskCache* cache_; + CacheState cache_state_; + AsyncResolverInterface* resolver_; +}; + +////////////////////////////////////////////////////////////////////// +// HttpClientDefault - Default implementation of HttpClient +////////////////////////////////////////////////////////////////////// + +class HttpClientDefault : public ReuseSocketPool, public HttpClient { +public: + HttpClientDefault(SocketFactory* factory, const std::string& agent, + HttpTransaction* transaction = NULL); +}; + +////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_HTTPCLIENT_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpcommon.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpcommon.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpcommon.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpcommon.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,1047 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#if defined(WEBRTC_WIN) +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#define SECURITY_WIN32 +#include +#endif + +#include "webrtc/base/httpcommon-inl.h" + +#include "webrtc/base/base64.h" +#include "webrtc/base/common.h" +#include "webrtc/base/cryptstring.h" +#include "webrtc/base/httpcommon.h" +#include "webrtc/base/socketaddress.h" +#include "webrtc/base/stringdigest.h" +#include "webrtc/base/stringencode.h" +#include "webrtc/base/stringutils.h" + +namespace rtc { + +#if defined(WEBRTC_WIN) +extern const ConstantLabel SECURITY_ERRORS[]; +#endif + +////////////////////////////////////////////////////////////////////// +// Enum - TODO: expose globally later? +////////////////////////////////////////////////////////////////////// + +bool find_string(size_t& index, const std::string& needle, + const char* const haystack[], size_t max_index) { + for (index=0; index +struct Enum { + static const char** Names; + static size_t Size; + + static inline const char* Name(E val) { return Names[val]; } + static inline bool Parse(E& val, const std::string& name) { + size_t index; + if (!find_string(index, name, Names, Size)) + return false; + val = static_cast(index); + return true; + } + + E val; + + inline operator E&() { return val; } + inline Enum& operator=(E rhs) { val = rhs; return *this; } + + inline const char* name() const { return Name(val); } + inline bool assign(const std::string& name) { return Parse(val, name); } + inline Enum& operator=(const std::string& rhs) { assign(rhs); return *this; } +}; + +#define ENUM(e,n) \ + template<> const char** Enum::Names = n; \ + template<> size_t Enum::Size = sizeof(n)/sizeof(n[0]) + +////////////////////////////////////////////////////////////////////// +// HttpCommon +////////////////////////////////////////////////////////////////////// + +static const char* kHttpVersions[HVER_LAST+1] = { + "1.0", "1.1", "Unknown" +}; +ENUM(HttpVersion, kHttpVersions); + +static const char* kHttpVerbs[HV_LAST+1] = { + "GET", "POST", "PUT", "DELETE", "CONNECT", "HEAD" +}; +ENUM(HttpVerb, kHttpVerbs); + +static const char* kHttpHeaders[HH_LAST+1] = { + "Age", + "Cache-Control", + "Connection", + "Content-Disposition", + "Content-Length", + "Content-Range", + "Content-Type", + "Cookie", + "Date", + "ETag", + "Expires", + "Host", + "If-Modified-Since", + "If-None-Match", + "Keep-Alive", + "Last-Modified", + "Location", + "Proxy-Authenticate", + "Proxy-Authorization", + "Proxy-Connection", + "Range", + "Set-Cookie", + "TE", + "Trailers", + "Transfer-Encoding", + "Upgrade", + "User-Agent", + "WWW-Authenticate", +}; +ENUM(HttpHeader, kHttpHeaders); + +const char* ToString(HttpVersion version) { + return Enum::Name(version); +} + +bool FromString(HttpVersion& version, const std::string& str) { + return Enum::Parse(version, str); +} + +const char* ToString(HttpVerb verb) { + return Enum::Name(verb); +} + +bool FromString(HttpVerb& verb, const std::string& str) { + return Enum::Parse(verb, str); +} + +const char* ToString(HttpHeader header) { + return Enum::Name(header); +} + +bool FromString(HttpHeader& header, const std::string& str) { + return Enum::Parse(header, str); +} + +bool HttpCodeHasBody(uint32 code) { + return !HttpCodeIsInformational(code) + && (code != HC_NO_CONTENT) && (code != HC_NOT_MODIFIED); +} + +bool HttpCodeIsCacheable(uint32 code) { + switch (code) { + case HC_OK: + case HC_NON_AUTHORITATIVE: + case HC_PARTIAL_CONTENT: + case HC_MULTIPLE_CHOICES: + case HC_MOVED_PERMANENTLY: + case HC_GONE: + return true; + default: + return false; + } +} + +bool HttpHeaderIsEndToEnd(HttpHeader header) { + switch (header) { + case HH_CONNECTION: + case HH_KEEP_ALIVE: + case HH_PROXY_AUTHENTICATE: + case HH_PROXY_AUTHORIZATION: + case HH_PROXY_CONNECTION: // Note part of RFC... this is non-standard header + case HH_TE: + case HH_TRAILERS: + case HH_TRANSFER_ENCODING: + case HH_UPGRADE: + return false; + default: + return true; + } +} + +bool HttpHeaderIsCollapsible(HttpHeader header) { + switch (header) { + case HH_SET_COOKIE: + case HH_PROXY_AUTHENTICATE: + case HH_WWW_AUTHENTICATE: + return false; + default: + return true; + } +} + +bool HttpShouldKeepAlive(const HttpData& data) { + std::string connection; + if ((data.hasHeader(HH_PROXY_CONNECTION, &connection) + || data.hasHeader(HH_CONNECTION, &connection))) { + return (_stricmp(connection.c_str(), "Keep-Alive") == 0); + } + return (data.version >= HVER_1_1); +} + +namespace { + +inline bool IsEndOfAttributeName(size_t pos, size_t len, const char * data) { + if (pos >= len) + return true; + if (isspace(static_cast(data[pos]))) + return true; + // The reason for this complexity is that some attributes may contain trailing + // equal signs (like base64 tokens in Negotiate auth headers) + if ((pos+1 < len) && (data[pos] == '=') && + !isspace(static_cast(data[pos+1])) && + (data[pos+1] != '=')) { + return true; + } + return false; +} + +// TODO: unittest for EscapeAttribute and HttpComposeAttributes. + +std::string EscapeAttribute(const std::string& attribute) { + const size_t kMaxLength = attribute.length() * 2 + 1; + char* buffer = STACK_ARRAY(char, kMaxLength); + size_t len = escape(buffer, kMaxLength, attribute.data(), attribute.length(), + "\"", '\\'); + return std::string(buffer, len); +} + +} // anonymous namespace + +void HttpComposeAttributes(const HttpAttributeList& attributes, char separator, + std::string* composed) { + std::stringstream ss; + for (size_t i=0; i 0) { + ss << separator << " "; + } + ss << attributes[i].first; + if (!attributes[i].second.empty()) { + ss << "=\"" << EscapeAttribute(attributes[i].second) << "\""; + } + } + *composed = ss.str(); +} + +void HttpParseAttributes(const char * data, size_t len, + HttpAttributeList& attributes) { + size_t pos = 0; + while (true) { + // Skip leading whitespace + while ((pos < len) && isspace(static_cast(data[pos]))) { + ++pos; + } + + // End of attributes? + if (pos >= len) + return; + + // Find end of attribute name + size_t start = pos; + while (!IsEndOfAttributeName(pos, len, data)) { + ++pos; + } + + HttpAttribute attribute; + attribute.first.assign(data + start, data + pos); + + // Attribute has value? + if ((pos < len) && (data[pos] == '=')) { + ++pos; // Skip '=' + // Check if quoted value + if ((pos < len) && (data[pos] == '"')) { + while (++pos < len) { + if (data[pos] == '"') { + ++pos; + break; + } + if ((data[pos] == '\\') && (pos + 1 < len)) + ++pos; + attribute.second.append(1, data[pos]); + } + } else { + while ((pos < len) && + !isspace(static_cast(data[pos])) && + (data[pos] != ',')) { + attribute.second.append(1, data[pos++]); + } + } + } + + attributes.push_back(attribute); + if ((pos < len) && (data[pos] == ',')) ++pos; // Skip ',' + } +} + +bool HttpHasAttribute(const HttpAttributeList& attributes, + const std::string& name, + std::string* value) { + for (HttpAttributeList::const_iterator it = attributes.begin(); + it != attributes.end(); ++it) { + if (it->first == name) { + if (value) { + *value = it->second; + } + return true; + } + } + return false; +} + +bool HttpHasNthAttribute(HttpAttributeList& attributes, + size_t index, + std::string* name, + std::string* value) { + if (index >= attributes.size()) + return false; + + if (name) + *name = attributes[index].first; + if (value) + *value = attributes[index].second; + return true; +} + +bool HttpDateToSeconds(const std::string& date, time_t* seconds) { + const char* const kTimeZones[] = { + "UT", "GMT", "EST", "EDT", "CST", "CDT", "MST", "MDT", "PST", "PDT", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "K", "L", "M", + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y" + }; + const int kTimeZoneOffsets[] = { + 0, 0, -5, -4, -6, -5, -7, -6, -8, -7, + -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 + }; + + ASSERT(NULL != seconds); + struct tm tval; + memset(&tval, 0, sizeof(tval)); + char month[4], zone[6]; + memset(month, 0, sizeof(month)); + memset(zone, 0, sizeof(zone)); + + if (7 != sscanf(date.c_str(), "%*3s, %d %3s %d %d:%d:%d %5c", + &tval.tm_mday, month, &tval.tm_year, + &tval.tm_hour, &tval.tm_min, &tval.tm_sec, zone)) { + return false; + } + switch (toupper(month[2])) { + case 'N': tval.tm_mon = (month[1] == 'A') ? 0 : 5; break; + case 'B': tval.tm_mon = 1; break; + case 'R': tval.tm_mon = (month[0] == 'M') ? 2 : 3; break; + case 'Y': tval.tm_mon = 4; break; + case 'L': tval.tm_mon = 6; break; + case 'G': tval.tm_mon = 7; break; + case 'P': tval.tm_mon = 8; break; + case 'T': tval.tm_mon = 9; break; + case 'V': tval.tm_mon = 10; break; + case 'C': tval.tm_mon = 11; break; + } + tval.tm_year -= 1900; + time_t gmt, non_gmt = mktime(&tval); + if ((zone[0] == '+') || (zone[0] == '-')) { + if (!isdigit(zone[1]) || !isdigit(zone[2]) + || !isdigit(zone[3]) || !isdigit(zone[4])) { + return false; + } + int hours = (zone[1] - '0') * 10 + (zone[2] - '0'); + int minutes = (zone[3] - '0') * 10 + (zone[4] - '0'); + int offset = (hours * 60 + minutes) * 60; + gmt = non_gmt + ((zone[0] == '+') ? offset : -offset); + } else { + size_t zindex; + if (!find_string(zindex, zone, kTimeZones, ARRAY_SIZE(kTimeZones))) { + return false; + } + gmt = non_gmt + kTimeZoneOffsets[zindex] * 60 * 60; + } + // TODO: Android should support timezone, see b/2441195 +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID) || defined(BSD) + tm *tm_for_timezone = localtime(&gmt); + *seconds = gmt + tm_for_timezone->tm_gmtoff; +#else + *seconds = gmt - timezone; +#endif + return true; +} + +std::string HttpAddress(const SocketAddress& address, bool secure) { + return (address.port() == HttpDefaultPort(secure)) + ? address.hostname() : address.ToString(); +} + +////////////////////////////////////////////////////////////////////// +// HttpData +////////////////////////////////////////////////////////////////////// + +void +HttpData::clear(bool release_document) { + // Clear headers first, since releasing a document may have far-reaching + // effects. + headers_.clear(); + if (release_document) { + document.reset(); + } +} + +void +HttpData::copy(const HttpData& src) { + headers_ = src.headers_; +} + +void +HttpData::changeHeader(const std::string& name, const std::string& value, + HeaderCombine combine) { + if (combine == HC_AUTO) { + HttpHeader header; + // Unrecognized headers are collapsible + combine = !FromString(header, name) || HttpHeaderIsCollapsible(header) + ? HC_YES : HC_NO; + } else if (combine == HC_REPLACE) { + headers_.erase(name); + combine = HC_NO; + } + // At this point, combine is one of (YES, NO, NEW) + if (combine != HC_NO) { + HeaderMap::iterator it = headers_.find(name); + if (it != headers_.end()) { + if (combine == HC_YES) { + it->second.append(","); + it->second.append(value); + } + return; + } + } + headers_.insert(HeaderMap::value_type(name, value)); +} + +size_t HttpData::clearHeader(const std::string& name) { + return headers_.erase(name); +} + +HttpData::iterator HttpData::clearHeader(iterator header) { + iterator deprecated = header++; + headers_.erase(deprecated); + return header; +} + +bool +HttpData::hasHeader(const std::string& name, std::string* value) const { + HeaderMap::const_iterator it = headers_.find(name); + if (it == headers_.end()) { + return false; + } else if (value) { + *value = it->second; + } + return true; +} + +void HttpData::setContent(const std::string& content_type, + StreamInterface* document) { + setHeader(HH_CONTENT_TYPE, content_type); + setDocumentAndLength(document); +} + +void HttpData::setDocumentAndLength(StreamInterface* document) { + // TODO: Consider calling Rewind() here? + ASSERT(!hasHeader(HH_CONTENT_LENGTH, NULL)); + ASSERT(!hasHeader(HH_TRANSFER_ENCODING, NULL)); + ASSERT(document != NULL); + this->document.reset(document); + size_t content_length = 0; + if (this->document->GetAvailable(&content_length)) { + char buffer[32]; + sprintfn(buffer, sizeof(buffer), "%d", content_length); + setHeader(HH_CONTENT_LENGTH, buffer); + } else { + setHeader(HH_TRANSFER_ENCODING, "chunked"); + } +} + +// +// HttpRequestData +// + +void +HttpRequestData::clear(bool release_document) { + verb = HV_GET; + path.clear(); + HttpData::clear(release_document); +} + +void +HttpRequestData::copy(const HttpRequestData& src) { + verb = src.verb; + path = src.path; + HttpData::copy(src); +} + +size_t +HttpRequestData::formatLeader(char* buffer, size_t size) const { + ASSERT(path.find(' ') == std::string::npos); + return sprintfn(buffer, size, "%s %.*s HTTP/%s", ToString(verb), path.size(), + path.data(), ToString(version)); +} + +HttpError +HttpRequestData::parseLeader(const char* line, size_t len) { + unsigned int vmajor, vminor; + int vend, dstart, dend; + // sscanf isn't safe with strings that aren't null-terminated, and there is + // no guarantee that |line| is. Create a local copy that is null-terminated. + std::string line_str(line, len); + line = line_str.c_str(); + if ((sscanf(line, "%*s%n %n%*s%n HTTP/%u.%u", + &vend, &dstart, &dend, &vmajor, &vminor) != 2) + || (vmajor != 1)) { + return HE_PROTOCOL; + } + if (vminor == 0) { + version = HVER_1_0; + } else if (vminor == 1) { + version = HVER_1_1; + } else { + return HE_PROTOCOL; + } + std::string sverb(line, vend); + if (!FromString(verb, sverb.c_str())) { + return HE_PROTOCOL; // !?! HC_METHOD_NOT_SUPPORTED? + } + path.assign(line + dstart, line + dend); + return HE_NONE; +} + +bool HttpRequestData::getAbsoluteUri(std::string* uri) const { + if (HV_CONNECT == verb) + return false; + Url url(path); + if (url.valid()) { + uri->assign(path); + return true; + } + std::string host; + if (!hasHeader(HH_HOST, &host)) + return false; + url.set_address(host); + url.set_full_path(path); + uri->assign(url.url()); + return url.valid(); +} + +bool HttpRequestData::getRelativeUri(std::string* host, + std::string* path) const +{ + if (HV_CONNECT == verb) + return false; + Url url(this->path); + if (url.valid()) { + host->assign(url.address()); + path->assign(url.full_path()); + return true; + } + if (!hasHeader(HH_HOST, host)) + return false; + path->assign(this->path); + return true; +} + +// +// HttpResponseData +// + +void +HttpResponseData::clear(bool release_document) { + scode = HC_INTERNAL_SERVER_ERROR; + message.clear(); + HttpData::clear(release_document); +} + +void +HttpResponseData::copy(const HttpResponseData& src) { + scode = src.scode; + message = src.message; + HttpData::copy(src); +} + +void +HttpResponseData::set_success(uint32 scode) { + this->scode = scode; + message.clear(); + setHeader(HH_CONTENT_LENGTH, "0", false); +} + +void +HttpResponseData::set_success(const std::string& content_type, + StreamInterface* document, + uint32 scode) { + this->scode = scode; + message.erase(message.begin(), message.end()); + setContent(content_type, document); +} + +void +HttpResponseData::set_redirect(const std::string& location, uint32 scode) { + this->scode = scode; + message.clear(); + setHeader(HH_LOCATION, location); + setHeader(HH_CONTENT_LENGTH, "0", false); +} + +void +HttpResponseData::set_error(uint32 scode) { + this->scode = scode; + message.clear(); + setHeader(HH_CONTENT_LENGTH, "0", false); +} + +size_t +HttpResponseData::formatLeader(char* buffer, size_t size) const { + size_t len = sprintfn(buffer, size, "HTTP/%s %lu", ToString(version), scode); + if (!message.empty()) { + len += sprintfn(buffer + len, size - len, " %.*s", + message.size(), message.data()); + } + return len; +} + +HttpError +HttpResponseData::parseLeader(const char* line, size_t len) { + size_t pos = 0; + unsigned int vmajor, vminor, temp_scode; + int temp_pos; + // sscanf isn't safe with strings that aren't null-terminated, and there is + // no guarantee that |line| is. Create a local copy that is null-terminated. + std::string line_str(line, len); + line = line_str.c_str(); + if (sscanf(line, "HTTP %u%n", + &temp_scode, &temp_pos) == 1) { + // This server's response has no version. :( NOTE: This happens for every + // response to requests made from Chrome plugins, regardless of the server's + // behaviour. + LOG(LS_VERBOSE) << "HTTP version missing from response"; + version = HVER_UNKNOWN; + } else if ((sscanf(line, "HTTP/%u.%u %u%n", + &vmajor, &vminor, &temp_scode, &temp_pos) == 3) + && (vmajor == 1)) { + // This server's response does have a version. + if (vminor == 0) { + version = HVER_1_0; + } else if (vminor == 1) { + version = HVER_1_1; + } else { + return HE_PROTOCOL; + } + } else { + return HE_PROTOCOL; + } + scode = temp_scode; + pos = static_cast(temp_pos); + while ((pos < len) && isspace(static_cast(line[pos]))) ++pos; + message.assign(line + pos, len - pos); + return HE_NONE; +} + +////////////////////////////////////////////////////////////////////// +// Http Authentication +////////////////////////////////////////////////////////////////////// + +#define TEST_DIGEST 0 +#if TEST_DIGEST +/* +const char * const DIGEST_CHALLENGE = + "Digest realm=\"testrealm@host.com\"," + " qop=\"auth,auth-int\"," + " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"," + " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""; +const char * const DIGEST_METHOD = "GET"; +const char * const DIGEST_URI = + "/dir/index.html";; +const char * const DIGEST_CNONCE = + "0a4f113b"; +const char * const DIGEST_RESPONSE = + "6629fae49393a05397450978507c4ef1"; +//user_ = "Mufasa"; +//pass_ = "Circle Of Life"; +*/ +const char * const DIGEST_CHALLENGE = + "Digest realm=\"Squid proxy-caching web server\"," + " nonce=\"Nny4QuC5PwiSDixJ\"," + " qop=\"auth\"," + " stale=false"; +const char * const DIGEST_URI = + "/"; +const char * const DIGEST_CNONCE = + "6501d58e9a21cee1e7b5fec894ded024"; +const char * const DIGEST_RESPONSE = + "edffcb0829e755838b073a4a42de06bc"; +#endif + +std::string quote(const std::string& str) { + std::string result; + result.push_back('"'); + for (size_t i=0; iauth_method != auth_method)) + return HAR_IGNORE; + + // BASIC + if (_stricmp(auth_method.c_str(), "basic") == 0) { + if (context) + return HAR_CREDENTIALS; // Bad credentials + if (username.empty()) + return HAR_CREDENTIALS; // Missing credentials + + context = new HttpAuthContext(auth_method); + + // TODO: convert sensitive to a secure buffer that gets securely deleted + //std::string decoded = username + ":" + password; + size_t len = username.size() + password.GetLength() + 2; + char * sensitive = new char[len]; + size_t pos = strcpyn(sensitive, len, username.data(), username.size()); + pos += strcpyn(sensitive + pos, len - pos, ":"); + password.CopyTo(sensitive + pos, true); + + response = auth_method; + response.append(" "); + // TODO: create a sensitive-source version of Base64::encode + response.append(Base64::Encode(sensitive)); + memset(sensitive, 0, len); + delete [] sensitive; + return HAR_RESPONSE; + } + + // DIGEST + if (_stricmp(auth_method.c_str(), "digest") == 0) { + if (context) + return HAR_CREDENTIALS; // Bad credentials + if (username.empty()) + return HAR_CREDENTIALS; // Missing credentials + + context = new HttpAuthContext(auth_method); + + std::string cnonce, ncount; +#if TEST_DIGEST + method = DIGEST_METHOD; + uri = DIGEST_URI; + cnonce = DIGEST_CNONCE; +#else + char buffer[256]; + sprintf(buffer, "%d", static_cast(time(0))); + cnonce = MD5(buffer); +#endif + ncount = "00000001"; + + std::string realm, nonce, qop, opaque; + HttpHasAttribute(args, "realm", &realm); + HttpHasAttribute(args, "nonce", &nonce); + bool has_qop = HttpHasAttribute(args, "qop", &qop); + bool has_opaque = HttpHasAttribute(args, "opaque", &opaque); + + // TODO: convert sensitive to be secure buffer + //std::string A1 = username + ":" + realm + ":" + password; + size_t len = username.size() + realm.size() + password.GetLength() + 3; + char * sensitive = new char[len]; // A1 + size_t pos = strcpyn(sensitive, len, username.data(), username.size()); + pos += strcpyn(sensitive + pos, len - pos, ":"); + pos += strcpyn(sensitive + pos, len - pos, realm.c_str()); + pos += strcpyn(sensitive + pos, len - pos, ":"); + password.CopyTo(sensitive + pos, true); + + std::string A2 = method + ":" + uri; + std::string middle; + if (has_qop) { + qop = "auth"; + middle = nonce + ":" + ncount + ":" + cnonce + ":" + qop; + } else { + middle = nonce; + } + std::string HA1 = MD5(sensitive); + memset(sensitive, 0, len); + delete [] sensitive; + std::string HA2 = MD5(A2); + std::string dig_response = MD5(HA1 + ":" + middle + ":" + HA2); + +#if TEST_DIGEST + ASSERT(strcmp(dig_response.c_str(), DIGEST_RESPONSE) == 0); +#endif + + std::stringstream ss; + ss << auth_method; + ss << " username=" << quote(username); + ss << ", realm=" << quote(realm); + ss << ", nonce=" << quote(nonce); + ss << ", uri=" << quote(uri); + if (has_qop) { + ss << ", qop=" << qop; + ss << ", nc=" << ncount; + ss << ", cnonce=" << quote(cnonce); + } + ss << ", response=\"" << dig_response << "\""; + if (has_opaque) { + ss << ", opaque=" << quote(opaque); + } + response = ss.str(); + return HAR_RESPONSE; + } + +#if defined(WEBRTC_WIN) +#if 1 + bool want_negotiate = (_stricmp(auth_method.c_str(), "negotiate") == 0); + bool want_ntlm = (_stricmp(auth_method.c_str(), "ntlm") == 0); + // SPNEGO & NTLM + if (want_negotiate || want_ntlm) { + const size_t MAX_MESSAGE = 12000, MAX_SPN = 256; + char out_buf[MAX_MESSAGE], spn[MAX_SPN]; + +#if 0 // Requires funky windows versions + DWORD len = MAX_SPN; + if (DsMakeSpn("HTTP", server.HostAsURIString().c_str(), NULL, + server.port(), + 0, &len, spn) != ERROR_SUCCESS) { + LOG_F(WARNING) << "(Negotiate) - DsMakeSpn failed"; + return HAR_IGNORE; + } +#else + sprintfn(spn, MAX_SPN, "HTTP/%s", server.ToString().c_str()); +#endif + + SecBuffer out_sec; + out_sec.pvBuffer = out_buf; + out_sec.cbBuffer = sizeof(out_buf); + out_sec.BufferType = SECBUFFER_TOKEN; + + SecBufferDesc out_buf_desc; + out_buf_desc.ulVersion = 0; + out_buf_desc.cBuffers = 1; + out_buf_desc.pBuffers = &out_sec; + + const ULONG NEG_FLAGS_DEFAULT = + //ISC_REQ_ALLOCATE_MEMORY + ISC_REQ_CONFIDENTIALITY + //| ISC_REQ_EXTENDED_ERROR + //| ISC_REQ_INTEGRITY + | ISC_REQ_REPLAY_DETECT + | ISC_REQ_SEQUENCE_DETECT + //| ISC_REQ_STREAM + //| ISC_REQ_USE_SUPPLIED_CREDS + ; + + ::TimeStamp lifetime; + SECURITY_STATUS ret = S_OK; + ULONG ret_flags = 0, flags = NEG_FLAGS_DEFAULT; + + bool specify_credentials = !username.empty(); + size_t steps = 0; + + //uint32 now = Time(); + + NegotiateAuthContext * neg = static_cast(context); + if (neg) { + const size_t max_steps = 10; + if (++neg->steps >= max_steps) { + LOG(WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) too many retries"; + return HAR_ERROR; + } + steps = neg->steps; + + std::string challenge, decoded_challenge; + if (HttpHasNthAttribute(args, 1, &challenge, NULL) + && Base64::Decode(challenge, Base64::DO_STRICT, + &decoded_challenge, NULL)) { + SecBuffer in_sec; + in_sec.pvBuffer = const_cast(decoded_challenge.data()); + in_sec.cbBuffer = static_cast(decoded_challenge.size()); + in_sec.BufferType = SECBUFFER_TOKEN; + + SecBufferDesc in_buf_desc; + in_buf_desc.ulVersion = 0; + in_buf_desc.cBuffers = 1; + in_buf_desc.pBuffers = &in_sec; + + ret = InitializeSecurityContextA(&neg->cred, &neg->ctx, spn, flags, 0, SECURITY_NATIVE_DREP, &in_buf_desc, 0, &neg->ctx, &out_buf_desc, &ret_flags, &lifetime); + //LOG(INFO) << "$$$ InitializeSecurityContext @ " << TimeSince(now); + if (FAILED(ret)) { + LOG(LS_ERROR) << "InitializeSecurityContext returned: " + << ErrorName(ret, SECURITY_ERRORS); + return HAR_ERROR; + } + } else if (neg->specified_credentials) { + // Try again with default credentials + specify_credentials = false; + delete context; + context = neg = 0; + } else { + return HAR_CREDENTIALS; + } + } + + if (!neg) { + unsigned char userbuf[256], passbuf[256], domainbuf[16]; + SEC_WINNT_AUTH_IDENTITY_A auth_id, * pauth_id = 0; + if (specify_credentials) { + memset(&auth_id, 0, sizeof(auth_id)); + size_t len = password.GetLength()+1; + char * sensitive = new char[len]; + password.CopyTo(sensitive, true); + std::string::size_type pos = username.find('\\'); + if (pos == std::string::npos) { + auth_id.UserLength = static_cast( + _min(sizeof(userbuf) - 1, username.size())); + memcpy(userbuf, username.c_str(), auth_id.UserLength); + userbuf[auth_id.UserLength] = 0; + auth_id.DomainLength = 0; + domainbuf[auth_id.DomainLength] = 0; + auth_id.PasswordLength = static_cast( + _min(sizeof(passbuf) - 1, password.GetLength())); + memcpy(passbuf, sensitive, auth_id.PasswordLength); + passbuf[auth_id.PasswordLength] = 0; + } else { + auth_id.UserLength = static_cast( + _min(sizeof(userbuf) - 1, username.size() - pos - 1)); + memcpy(userbuf, username.c_str() + pos + 1, auth_id.UserLength); + userbuf[auth_id.UserLength] = 0; + auth_id.DomainLength = static_cast( + _min(sizeof(domainbuf) - 1, pos)); + memcpy(domainbuf, username.c_str(), auth_id.DomainLength); + domainbuf[auth_id.DomainLength] = 0; + auth_id.PasswordLength = static_cast( + _min(sizeof(passbuf) - 1, password.GetLength())); + memcpy(passbuf, sensitive, auth_id.PasswordLength); + passbuf[auth_id.PasswordLength] = 0; + } + memset(sensitive, 0, len); + delete [] sensitive; + auth_id.User = userbuf; + auth_id.Domain = domainbuf; + auth_id.Password = passbuf; + auth_id.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; + pauth_id = &auth_id; + LOG(LS_VERBOSE) << "Negotiate protocol: Using specified credentials"; + } else { + LOG(LS_VERBOSE) << "Negotiate protocol: Using default credentials"; + } + + CredHandle cred; + ret = AcquireCredentialsHandleA( + 0, const_cast(want_negotiate ? NEGOSSP_NAME_A : NTLMSP_NAME_A), + SECPKG_CRED_OUTBOUND, 0, pauth_id, 0, 0, &cred, &lifetime); + //LOG(INFO) << "$$$ AcquireCredentialsHandle @ " << TimeSince(now); + if (ret != SEC_E_OK) { + LOG(LS_ERROR) << "AcquireCredentialsHandle error: " + << ErrorName(ret, SECURITY_ERRORS); + return HAR_IGNORE; + } + + //CSecBufferBundle<5, CSecBufferBase::FreeSSPI> sb_out; + + CtxtHandle ctx; + ret = InitializeSecurityContextA(&cred, 0, spn, flags, 0, SECURITY_NATIVE_DREP, 0, 0, &ctx, &out_buf_desc, &ret_flags, &lifetime); + //LOG(INFO) << "$$$ InitializeSecurityContext @ " << TimeSince(now); + if (FAILED(ret)) { + LOG(LS_ERROR) << "InitializeSecurityContext returned: " + << ErrorName(ret, SECURITY_ERRORS); + FreeCredentialsHandle(&cred); + return HAR_IGNORE; + } + + ASSERT(!context); + context = neg = new NegotiateAuthContext(auth_method, cred, ctx); + neg->specified_credentials = specify_credentials; + neg->steps = steps; + } + + if ((ret == SEC_I_COMPLETE_NEEDED) || (ret == SEC_I_COMPLETE_AND_CONTINUE)) { + ret = CompleteAuthToken(&neg->ctx, &out_buf_desc); + //LOG(INFO) << "$$$ CompleteAuthToken @ " << TimeSince(now); + LOG(LS_VERBOSE) << "CompleteAuthToken returned: " + << ErrorName(ret, SECURITY_ERRORS); + if (FAILED(ret)) { + return HAR_ERROR; + } + } + + //LOG(INFO) << "$$$ NEGOTIATE took " << TimeSince(now) << "ms"; + + std::string decoded(out_buf, out_buf + out_sec.cbBuffer); + response = auth_method; + response.append(" "); + response.append(Base64::Encode(decoded)); + return HAR_RESPONSE; + } +#endif +#endif // WEBRTC_WIN + + return HAR_IGNORE; +} + +////////////////////////////////////////////////////////////////////// + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpcommon.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpcommon.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpcommon.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpcommon.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,446 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_HTTPCOMMON_H__ +#define WEBRTC_BASE_HTTPCOMMON_H__ + +#include +#include +#include +#include "webrtc/base/basictypes.h" +#include "webrtc/base/common.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/stringutils.h" +#include "webrtc/base/stream.h" + +namespace rtc { + +class CryptString; +class SocketAddress; + +////////////////////////////////////////////////////////////////////// +// Constants +////////////////////////////////////////////////////////////////////// + +enum HttpCode { + HC_OK = 200, + HC_NON_AUTHORITATIVE = 203, + HC_NO_CONTENT = 204, + HC_PARTIAL_CONTENT = 206, + + HC_MULTIPLE_CHOICES = 300, + HC_MOVED_PERMANENTLY = 301, + HC_FOUND = 302, + HC_SEE_OTHER = 303, + HC_NOT_MODIFIED = 304, + HC_MOVED_TEMPORARILY = 307, + + HC_BAD_REQUEST = 400, + HC_UNAUTHORIZED = 401, + HC_FORBIDDEN = 403, + HC_NOT_FOUND = 404, + HC_PROXY_AUTHENTICATION_REQUIRED = 407, + HC_GONE = 410, + + HC_INTERNAL_SERVER_ERROR = 500, + HC_NOT_IMPLEMENTED = 501, + HC_SERVICE_UNAVAILABLE = 503, +}; + +enum HttpVersion { + HVER_1_0, HVER_1_1, HVER_UNKNOWN, + HVER_LAST = HVER_UNKNOWN +}; + +enum HttpVerb { + HV_GET, HV_POST, HV_PUT, HV_DELETE, HV_CONNECT, HV_HEAD, + HV_LAST = HV_HEAD +}; + +enum HttpError { + HE_NONE, + HE_PROTOCOL, // Received non-valid HTTP data + HE_DISCONNECTED, // Connection closed unexpectedly + HE_OVERFLOW, // Received too much data for internal buffers + HE_CONNECT_FAILED, // The socket failed to connect. + HE_SOCKET_ERROR, // An error occurred on a connected socket + HE_SHUTDOWN, // Http object is being destroyed + HE_OPERATION_CANCELLED, // Connection aborted locally + HE_AUTH, // Proxy Authentication Required + HE_CERTIFICATE_EXPIRED, // During SSL negotiation + HE_STREAM, // Problem reading or writing to the document + HE_CACHE, // Problem reading from cache + HE_DEFAULT +}; + +enum HttpHeader { + HH_AGE, + HH_CACHE_CONTROL, + HH_CONNECTION, + HH_CONTENT_DISPOSITION, + HH_CONTENT_LENGTH, + HH_CONTENT_RANGE, + HH_CONTENT_TYPE, + HH_COOKIE, + HH_DATE, + HH_ETAG, + HH_EXPIRES, + HH_HOST, + HH_IF_MODIFIED_SINCE, + HH_IF_NONE_MATCH, + HH_KEEP_ALIVE, + HH_LAST_MODIFIED, + HH_LOCATION, + HH_PROXY_AUTHENTICATE, + HH_PROXY_AUTHORIZATION, + HH_PROXY_CONNECTION, + HH_RANGE, + HH_SET_COOKIE, + HH_TE, + HH_TRAILERS, + HH_TRANSFER_ENCODING, + HH_UPGRADE, + HH_USER_AGENT, + HH_WWW_AUTHENTICATE, + HH_LAST = HH_WWW_AUTHENTICATE +}; + +const uint16 HTTP_DEFAULT_PORT = 80; +const uint16 HTTP_SECURE_PORT = 443; + +////////////////////////////////////////////////////////////////////// +// Utility Functions +////////////////////////////////////////////////////////////////////// + +inline HttpError mkerr(HttpError err, HttpError def_err = HE_DEFAULT) { + return (err != HE_NONE) ? err : def_err; +} + +const char* ToString(HttpVersion version); +bool FromString(HttpVersion& version, const std::string& str); + +const char* ToString(HttpVerb verb); +bool FromString(HttpVerb& verb, const std::string& str); + +const char* ToString(HttpHeader header); +bool FromString(HttpHeader& header, const std::string& str); + +inline bool HttpCodeIsInformational(uint32 code) { return ((code / 100) == 1); } +inline bool HttpCodeIsSuccessful(uint32 code) { return ((code / 100) == 2); } +inline bool HttpCodeIsRedirection(uint32 code) { return ((code / 100) == 3); } +inline bool HttpCodeIsClientError(uint32 code) { return ((code / 100) == 4); } +inline bool HttpCodeIsServerError(uint32 code) { return ((code / 100) == 5); } + +bool HttpCodeHasBody(uint32 code); +bool HttpCodeIsCacheable(uint32 code); +bool HttpHeaderIsEndToEnd(HttpHeader header); +bool HttpHeaderIsCollapsible(HttpHeader header); + +struct HttpData; +bool HttpShouldKeepAlive(const HttpData& data); + +typedef std::pair HttpAttribute; +typedef std::vector HttpAttributeList; +void HttpComposeAttributes(const HttpAttributeList& attributes, char separator, + std::string* composed); +void HttpParseAttributes(const char * data, size_t len, + HttpAttributeList& attributes); +bool HttpHasAttribute(const HttpAttributeList& attributes, + const std::string& name, + std::string* value); +bool HttpHasNthAttribute(HttpAttributeList& attributes, + size_t index, + std::string* name, + std::string* value); + +// Convert RFC1123 date (DoW, DD Mon YYYY HH:MM:SS TZ) to unix timestamp +bool HttpDateToSeconds(const std::string& date, time_t* seconds); + +inline uint16 HttpDefaultPort(bool secure) { + return secure ? HTTP_SECURE_PORT : HTTP_DEFAULT_PORT; +} + +// Returns the http server notation for a given address +std::string HttpAddress(const SocketAddress& address, bool secure); + +// functional for insensitive std::string compare +struct iless { + bool operator()(const std::string& lhs, const std::string& rhs) const { + return (::_stricmp(lhs.c_str(), rhs.c_str()) < 0); + } +}; + +// put quotes around a string and escape any quotes inside it +std::string quote(const std::string& str); + +////////////////////////////////////////////////////////////////////// +// Url +////////////////////////////////////////////////////////////////////// + +template +class Url { +public: + typedef typename Traits::string string; + + // TODO: Implement Encode/Decode + static int Encode(const CTYPE* source, CTYPE* destination, size_t len); + static int Encode(const string& source, string& destination); + static int Decode(const CTYPE* source, CTYPE* destination, size_t len); + static int Decode(const string& source, string& destination); + + Url(const string& url) { do_set_url(url.c_str(), url.size()); } + Url(const string& path, const string& host, uint16 port = HTTP_DEFAULT_PORT) + : host_(host), port_(port), secure_(HTTP_SECURE_PORT == port) + { set_full_path(path); } + + bool valid() const { return !host_.empty(); } + void clear() { + host_.clear(); + port_ = HTTP_DEFAULT_PORT; + secure_ = false; + path_.assign(1, static_cast('/')); + query_.clear(); + } + + void set_url(const string& val) { + do_set_url(val.c_str(), val.size()); + } + string url() const { + string val; do_get_url(&val); return val; + } + + void set_address(const string& val) { + do_set_address(val.c_str(), val.size()); + } + string address() const { + string val; do_get_address(&val); return val; + } + + void set_full_path(const string& val) { + do_set_full_path(val.c_str(), val.size()); + } + string full_path() const { + string val; do_get_full_path(&val); return val; + } + + void set_host(const string& val) { host_ = val; } + const string& host() const { return host_; } + + void set_port(uint16 val) { port_ = val; } + uint16 port() const { return port_; } + + void set_secure(bool val) { secure_ = val; } + bool secure() const { return secure_; } + + void set_path(const string& val) { + if (val.empty()) { + path_.assign(1, static_cast('/')); + } else { + ASSERT(val[0] == static_cast('/')); + path_ = val; + } + } + const string& path() const { return path_; } + + void set_query(const string& val) { + ASSERT(val.empty() || (val[0] == static_cast('?'))); + query_ = val; + } + const string& query() const { return query_; } + + bool get_attribute(const string& name, string* value) const; + +private: + void do_set_url(const CTYPE* val, size_t len); + void do_set_address(const CTYPE* val, size_t len); + void do_set_full_path(const CTYPE* val, size_t len); + + void do_get_url(string* val) const; + void do_get_address(string* val) const; + void do_get_full_path(string* val) const; + + string host_, path_, query_; + uint16 port_; + bool secure_; +}; + +////////////////////////////////////////////////////////////////////// +// HttpData +////////////////////////////////////////////////////////////////////// + +struct HttpData { + typedef std::multimap HeaderMap; + typedef HeaderMap::const_iterator const_iterator; + typedef HeaderMap::iterator iterator; + + HttpVersion version; + scoped_ptr document; + + HttpData() : version(HVER_1_1) { } + + enum HeaderCombine { HC_YES, HC_NO, HC_AUTO, HC_REPLACE, HC_NEW }; + void changeHeader(const std::string& name, const std::string& value, + HeaderCombine combine); + inline void addHeader(const std::string& name, const std::string& value, + bool append = true) { + changeHeader(name, value, append ? HC_AUTO : HC_NO); + } + inline void setHeader(const std::string& name, const std::string& value, + bool overwrite = true) { + changeHeader(name, value, overwrite ? HC_REPLACE : HC_NEW); + } + // Returns count of erased headers + size_t clearHeader(const std::string& name); + // Returns iterator to next header + iterator clearHeader(iterator header); + + // keep in mind, this may not do what you want in the face of multiple headers + bool hasHeader(const std::string& name, std::string* value) const; + + inline const_iterator begin() const { + return headers_.begin(); + } + inline const_iterator end() const { + return headers_.end(); + } + inline iterator begin() { + return headers_.begin(); + } + inline iterator end() { + return headers_.end(); + } + inline const_iterator begin(const std::string& name) const { + return headers_.lower_bound(name); + } + inline const_iterator end(const std::string& name) const { + return headers_.upper_bound(name); + } + inline iterator begin(const std::string& name) { + return headers_.lower_bound(name); + } + inline iterator end(const std::string& name) { + return headers_.upper_bound(name); + } + + // Convenience methods using HttpHeader + inline void changeHeader(HttpHeader header, const std::string& value, + HeaderCombine combine) { + changeHeader(ToString(header), value, combine); + } + inline void addHeader(HttpHeader header, const std::string& value, + bool append = true) { + addHeader(ToString(header), value, append); + } + inline void setHeader(HttpHeader header, const std::string& value, + bool overwrite = true) { + setHeader(ToString(header), value, overwrite); + } + inline void clearHeader(HttpHeader header) { + clearHeader(ToString(header)); + } + inline bool hasHeader(HttpHeader header, std::string* value) const { + return hasHeader(ToString(header), value); + } + inline const_iterator begin(HttpHeader header) const { + return headers_.lower_bound(ToString(header)); + } + inline const_iterator end(HttpHeader header) const { + return headers_.upper_bound(ToString(header)); + } + inline iterator begin(HttpHeader header) { + return headers_.lower_bound(ToString(header)); + } + inline iterator end(HttpHeader header) { + return headers_.upper_bound(ToString(header)); + } + + void setContent(const std::string& content_type, StreamInterface* document); + void setDocumentAndLength(StreamInterface* document); + + virtual size_t formatLeader(char* buffer, size_t size) const = 0; + virtual HttpError parseLeader(const char* line, size_t len) = 0; + +protected: + virtual ~HttpData() { } + void clear(bool release_document); + void copy(const HttpData& src); + +private: + HeaderMap headers_; +}; + +struct HttpRequestData : public HttpData { + HttpVerb verb; + std::string path; + + HttpRequestData() : verb(HV_GET) { } + + void clear(bool release_document); + void copy(const HttpRequestData& src); + + virtual size_t formatLeader(char* buffer, size_t size) const; + virtual HttpError parseLeader(const char* line, size_t len); + + bool getAbsoluteUri(std::string* uri) const; + bool getRelativeUri(std::string* host, std::string* path) const; +}; + +struct HttpResponseData : public HttpData { + uint32 scode; + std::string message; + + HttpResponseData() : scode(HC_INTERNAL_SERVER_ERROR) { } + void clear(bool release_document); + void copy(const HttpResponseData& src); + + // Convenience methods + void set_success(uint32 scode = HC_OK); + void set_success(const std::string& content_type, StreamInterface* document, + uint32 scode = HC_OK); + void set_redirect(const std::string& location, + uint32 scode = HC_MOVED_TEMPORARILY); + void set_error(uint32 scode); + + virtual size_t formatLeader(char* buffer, size_t size) const; + virtual HttpError parseLeader(const char* line, size_t len); +}; + +struct HttpTransaction { + HttpRequestData request; + HttpResponseData response; +}; + +////////////////////////////////////////////////////////////////////// +// Http Authentication +////////////////////////////////////////////////////////////////////// + +struct HttpAuthContext { + std::string auth_method; + HttpAuthContext(const std::string& auth) : auth_method(auth) { } + virtual ~HttpAuthContext() { } +}; + +enum HttpAuthResult { HAR_RESPONSE, HAR_IGNORE, HAR_CREDENTIALS, HAR_ERROR }; + +// 'context' is used by this function to record information between calls. +// Start by passing a null pointer, then pass the same pointer each additional +// call. When the authentication attempt is finished, delete the context. +HttpAuthResult HttpAuthenticate( + const char * challenge, size_t len, + const SocketAddress& server, + const std::string& method, const std::string& uri, + const std::string& username, const CryptString& password, + HttpAuthContext *& context, std::string& response, std::string& auth_method); + +////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_HTTPCOMMON_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpcommon-inl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpcommon-inl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpcommon-inl.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpcommon-inl.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,131 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_HTTPCOMMON_INL_H__ +#define WEBRTC_BASE_HTTPCOMMON_INL_H__ + +#include "webrtc/base/common.h" +#include "webrtc/base/httpcommon.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// Url +/////////////////////////////////////////////////////////////////////////////// + +template +void Url::do_set_url(const CTYPE* val, size_t len) { + if (ascnicmp(val, "http://", 7) == 0) { + val += 7; len -= 7; + secure_ = false; + } else if (ascnicmp(val, "https://", 8) == 0) { + val += 8; len -= 8; + secure_ = true; + } else { + clear(); + return; + } + const CTYPE* path = strchrn(val, len, static_cast('/')); + if (!path) { + path = val + len; + } + size_t address_length = (path - val); + do_set_address(val, address_length); + do_set_full_path(path, len - address_length); +} + +template +void Url::do_set_address(const CTYPE* val, size_t len) { + if (const CTYPE* at = strchrn(val, len, static_cast('@'))) { + // Everything before the @ is a user:password combo, so skip it. + len -= at - val + 1; + val = at + 1; + } + if (const CTYPE* colon = strchrn(val, len, static_cast(':'))) { + host_.assign(val, colon - val); + // Note: In every case, we're guaranteed that colon is followed by a null, + // or non-numeric character. + port_ = static_cast(::strtoul(colon + 1, NULL, 10)); + // TODO: Consider checking for invalid data following port number. + } else { + host_.assign(val, len); + port_ = HttpDefaultPort(secure_); + } +} + +template +void Url::do_set_full_path(const CTYPE* val, size_t len) { + const CTYPE* query = strchrn(val, len, static_cast('?')); + if (!query) { + query = val + len; + } + size_t path_length = (query - val); + if (0 == path_length) { + // TODO: consider failing in this case. + path_.assign(1, static_cast('/')); + } else { + ASSERT(val[0] == static_cast('/')); + path_.assign(val, path_length); + } + query_.assign(query, len - path_length); +} + +template +void Url::do_get_url(string* val) const { + CTYPE protocol[9]; + asccpyn(protocol, ARRAY_SIZE(protocol), secure_ ? "https://" : "http://"); + val->append(protocol); + do_get_address(val); + do_get_full_path(val); +} + +template +void Url::do_get_address(string* val) const { + val->append(host_); + if (port_ != HttpDefaultPort(secure_)) { + CTYPE format[5], port[32]; + asccpyn(format, ARRAY_SIZE(format), ":%hu"); + sprintfn(port, ARRAY_SIZE(port), format, port_); + val->append(port); + } +} + +template +void Url::do_get_full_path(string* val) const { + val->append(path_); + val->append(query_); +} + +template +bool Url::get_attribute(const string& name, string* value) const { + if (query_.empty()) + return false; + + std::string::size_type pos = query_.find(name, 1); + if (std::string::npos == pos) + return false; + + pos += name.length() + 1; + if ((pos > query_.length()) || (static_cast('=') != query_[pos-1])) + return false; + + std::string::size_type end = query_.find(static_cast('&'), pos); + if (std::string::npos == end) { + end = query_.length(); + } + value->assign(query_.substr(pos, end - pos)); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_HTTPCOMMON_INL_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpcommon_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpcommon_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpcommon_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpcommon_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,165 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/httpcommon-inl.h" +#include "webrtc/base/httpcommon.h" + +namespace rtc { + +#define TEST_PROTOCOL "http://" +#define TEST_HOST "www.google.com" +#define TEST_PATH "/folder/file.html" +#define TEST_QUERY "?query=x&attr=y" +#define TEST_URL TEST_PROTOCOL TEST_HOST TEST_PATH TEST_QUERY + +TEST(Url, DecomposesUrls) { + Url url(TEST_URL); + EXPECT_TRUE(url.valid()); + EXPECT_FALSE(url.secure()); + EXPECT_STREQ(TEST_HOST, url.host().c_str()); + EXPECT_EQ(80, url.port()); + EXPECT_STREQ(TEST_PATH, url.path().c_str()); + EXPECT_STREQ(TEST_QUERY, url.query().c_str()); + EXPECT_STREQ(TEST_HOST, url.address().c_str()); + EXPECT_STREQ(TEST_PATH TEST_QUERY, url.full_path().c_str()); + EXPECT_STREQ(TEST_URL, url.url().c_str()); +} + +TEST(Url, ComposesUrls) { + // Set in constructor + Url url(TEST_PATH TEST_QUERY, TEST_HOST, 80); + EXPECT_TRUE(url.valid()); + EXPECT_FALSE(url.secure()); + EXPECT_STREQ(TEST_HOST, url.host().c_str()); + EXPECT_EQ(80, url.port()); + EXPECT_STREQ(TEST_PATH, url.path().c_str()); + EXPECT_STREQ(TEST_QUERY, url.query().c_str()); + EXPECT_STREQ(TEST_HOST, url.address().c_str()); + EXPECT_STREQ(TEST_PATH TEST_QUERY, url.full_path().c_str()); + EXPECT_STREQ(TEST_URL, url.url().c_str()); + + url.clear(); + EXPECT_FALSE(url.valid()); + EXPECT_FALSE(url.secure()); + EXPECT_STREQ("", url.host().c_str()); + EXPECT_EQ(80, url.port()); + EXPECT_STREQ("/", url.path().c_str()); + EXPECT_STREQ("", url.query().c_str()); + + // Set component-wise + url.set_host(TEST_HOST); + url.set_port(80); + url.set_path(TEST_PATH); + url.set_query(TEST_QUERY); + EXPECT_TRUE(url.valid()); + EXPECT_FALSE(url.secure()); + EXPECT_STREQ(TEST_HOST, url.host().c_str()); + EXPECT_EQ(80, url.port()); + EXPECT_STREQ(TEST_PATH, url.path().c_str()); + EXPECT_STREQ(TEST_QUERY, url.query().c_str()); + EXPECT_STREQ(TEST_HOST, url.address().c_str()); + EXPECT_STREQ(TEST_PATH TEST_QUERY, url.full_path().c_str()); + EXPECT_STREQ(TEST_URL, url.url().c_str()); +} + +TEST(Url, EnsuresNonEmptyPath) { + Url url(TEST_PROTOCOL TEST_HOST); + EXPECT_TRUE(url.valid()); + EXPECT_STREQ("/", url.path().c_str()); + + url.clear(); + EXPECT_STREQ("/", url.path().c_str()); + url.set_path(""); + EXPECT_STREQ("/", url.path().c_str()); + + url.clear(); + EXPECT_STREQ("/", url.path().c_str()); + url.set_full_path(""); + EXPECT_STREQ("/", url.path().c_str()); +} + +TEST(Url, GetQueryAttributes) { + Url url(TEST_URL); + std::string value; + EXPECT_TRUE(url.get_attribute("query", &value)); + EXPECT_STREQ("x", value.c_str()); + value.clear(); + EXPECT_TRUE(url.get_attribute("attr", &value)); + EXPECT_STREQ("y", value.c_str()); + value.clear(); + EXPECT_FALSE(url.get_attribute("Query", &value)); + EXPECT_TRUE(value.empty()); +} + +TEST(Url, SkipsUserAndPassword) { + Url url("https://mail.google.com:pwd@badsite.com:12345/asdf"); + EXPECT_TRUE(url.valid()); + EXPECT_TRUE(url.secure()); + EXPECT_STREQ("badsite.com", url.host().c_str()); + EXPECT_EQ(12345, url.port()); + EXPECT_STREQ("/asdf", url.path().c_str()); + EXPECT_STREQ("badsite.com:12345", url.address().c_str()); +} + +TEST(Url, SkipsUser) { + Url url("https://mail.google.com@badsite.com:12345/asdf"); + EXPECT_TRUE(url.valid()); + EXPECT_TRUE(url.secure()); + EXPECT_STREQ("badsite.com", url.host().c_str()); + EXPECT_EQ(12345, url.port()); + EXPECT_STREQ("/asdf", url.path().c_str()); + EXPECT_STREQ("badsite.com:12345", url.address().c_str()); +} + +TEST(HttpResponseData, parseLeaderHttp1_0) { + static const char kResponseString[] = "HTTP/1.0 200 OK"; + HttpResponseData response; + EXPECT_EQ(HE_NONE, response.parseLeader(kResponseString, + sizeof(kResponseString) - 1)); + EXPECT_EQ(HVER_1_0, response.version); + EXPECT_EQ(200U, response.scode); +} + +TEST(HttpResponseData, parseLeaderHttp1_1) { + static const char kResponseString[] = "HTTP/1.1 200 OK"; + HttpResponseData response; + EXPECT_EQ(HE_NONE, response.parseLeader(kResponseString, + sizeof(kResponseString) - 1)); + EXPECT_EQ(HVER_1_1, response.version); + EXPECT_EQ(200U, response.scode); +} + +TEST(HttpResponseData, parseLeaderHttpUnknown) { + static const char kResponseString[] = "HTTP 200 OK"; + HttpResponseData response; + EXPECT_EQ(HE_NONE, response.parseLeader(kResponseString, + sizeof(kResponseString) - 1)); + EXPECT_EQ(HVER_UNKNOWN, response.version); + EXPECT_EQ(200U, response.scode); +} + +TEST(HttpResponseData, parseLeaderHttpFailure) { + static const char kResponseString[] = "HTTP/1.1 503 Service Unavailable"; + HttpResponseData response; + EXPECT_EQ(HE_NONE, response.parseLeader(kResponseString, + sizeof(kResponseString) - 1)); + EXPECT_EQ(HVER_1_1, response.version); + EXPECT_EQ(503U, response.scode); +} + +TEST(HttpResponseData, parseLeaderHttpInvalid) { + static const char kResponseString[] = "Durrrrr, what's HTTP?"; + HttpResponseData response; + EXPECT_EQ(HE_PROTOCOL, response.parseLeader(kResponseString, + sizeof(kResponseString) - 1)); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httprequest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httprequest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httprequest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httprequest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,110 @@ +/* + * Copyright 2006 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/httprequest.h" + +#include "webrtc/base/common.h" +#include "webrtc/base/firewallsocketserver.h" +#include "webrtc/base/httpclient.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/physicalsocketserver.h" +#include "webrtc/base/socketadapters.h" +#include "webrtc/base/socketpool.h" +#include "webrtc/base/ssladapter.h" + +using namespace rtc; + +/////////////////////////////////////////////////////////////////////////////// +// HttpMonitor +/////////////////////////////////////////////////////////////////////////////// + +HttpMonitor::HttpMonitor(SocketServer *ss) { + ASSERT(Thread::Current() != NULL); + ss_ = ss; + reset(); +} + +void HttpMonitor::Connect(HttpClient *http) { + http->SignalHttpClientComplete.connect(this, + &HttpMonitor::OnHttpClientComplete); +} + +void HttpMonitor::OnHttpClientComplete(HttpClient * http, HttpErrorType error) { + complete_ = true; + error_ = error; + ss_->WakeUp(); +} + +/////////////////////////////////////////////////////////////////////////////// +// HttpRequest +/////////////////////////////////////////////////////////////////////////////// + +const int kDefaultHTTPTimeout = 30 * 1000; // 30 sec + +HttpRequest::HttpRequest(const std::string &user_agent) + : firewall_(0), port_(80), secure_(false), + timeout_(kDefaultHTTPTimeout), fail_redirect_(false), + client_(user_agent.c_str(), NULL), error_(HE_NONE) { +} + +void HttpRequest::Send() { + // TODO: Rewrite this to use the thread's native socket server, and a more + // natural flow? + + PhysicalSocketServer physical; + SocketServer * ss = &physical; + if (firewall_) { + ss = new FirewallSocketServer(ss, firewall_); + } + + SslSocketFactory factory(ss, client_.agent()); + factory.SetProxy(proxy_); + if (secure_) + factory.UseSSL(host_.c_str()); + + //factory.SetLogging("HttpRequest"); + + ReuseSocketPool pool(&factory); + client_.set_pool(&pool); + + bool transparent_proxy = (port_ == 80) && ((proxy_.type == PROXY_HTTPS) || + (proxy_.type == PROXY_UNKNOWN)); + + if (transparent_proxy) { + client_.set_proxy(proxy_); + } + client_.set_fail_redirect(fail_redirect_); + + SocketAddress server(host_, port_); + client_.set_server(server); + + LOG(LS_INFO) << "HttpRequest start: " << host_ + client_.request().path; + + HttpMonitor monitor(ss); + monitor.Connect(&client_); + client_.start(); + ss->Wait(timeout_, true); + if (!monitor.done()) { + LOG(LS_INFO) << "HttpRequest request timed out"; + client_.reset(); + return; + } + + set_error(monitor.error()); + if (error_) { + LOG(LS_INFO) << "HttpRequest request error: " << error_; + return; + } + + std::string value; + if (client_.response().hasHeader(HH_LOCATION, &value)) { + response_redirect_ = value.c_str(); + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httprequest.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httprequest.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httprequest.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httprequest.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,115 @@ +/* + * Copyright 2006 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef _HTTPREQUEST_H_ +#define _HTTPREQUEST_H_ + +#include "webrtc/base/httpclient.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/proxyinfo.h" +#include "webrtc/base/socketserver.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/sslsocketfactory.h" // Deprecated include + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// HttpRequest +/////////////////////////////////////////////////////////////////////////////// + +class FirewallManager; +class MemoryStream; + +class HttpRequest { +public: + HttpRequest(const std::string &user_agent); + + void Send(); + + void set_proxy(const ProxyInfo& proxy) { + proxy_ = proxy; + } + void set_firewall(FirewallManager * firewall) { + firewall_ = firewall; + } + + // The DNS name of the host to connect to. + const std::string& host() { return host_; } + void set_host(const std::string& host) { host_ = host; } + + // The port to connect to on the target host. + int port() { return port_; } + void set_port(int port) { port_ = port; } + + // Whether the request should use SSL. + bool secure() { return secure_; } + void set_secure(bool secure) { secure_ = secure; } + + // Returns the redirect when redirection occurs + const std::string& response_redirect() { return response_redirect_; } + + // Time to wait on the download, in ms. Default is 5000 (5s) + int timeout() { return timeout_; } + void set_timeout(int timeout) { timeout_ = timeout; } + + // Fail redirects to allow analysis of redirect urls, etc. + bool fail_redirect() const { return fail_redirect_; } + void set_fail_redirect(bool fail_redirect) { fail_redirect_ = fail_redirect; } + + HttpRequestData& request() { return client_.request(); } + HttpResponseData& response() { return client_.response(); } + HttpErrorType error() { return error_; } + +protected: + void set_error(HttpErrorType error) { error_ = error; } + +private: + ProxyInfo proxy_; + FirewallManager * firewall_; + std::string host_; + int port_; + bool secure_; + int timeout_; + bool fail_redirect_; + HttpClient client_; + HttpErrorType error_; + std::string response_redirect_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// HttpMonitor +/////////////////////////////////////////////////////////////////////////////// + +class HttpMonitor : public sigslot::has_slots<> { +public: + HttpMonitor(SocketServer *ss); + + void reset() { + complete_ = false; + error_ = HE_DEFAULT; + } + + bool done() const { return complete_; } + HttpErrorType error() const { return error_; } + + void Connect(HttpClient* http); + void OnHttpClientComplete(HttpClient * http, HttpErrorType error); + +private: + bool complete_; + HttpErrorType error_; + SocketServer *ss_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc_ + +#endif // _HTTPREQUEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpserver.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpserver.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpserver.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpserver.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,288 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/httpcommon-inl.h" + +#include "webrtc/base/asyncsocket.h" +#include "webrtc/base/common.h" +#include "webrtc/base/httpserver.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/socketstream.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// HttpServer +/////////////////////////////////////////////////////////////////////////////// + +HttpServer::HttpServer() : next_connection_id_(1), closing_(false) { +} + +HttpServer::~HttpServer() { + if (closing_) { + LOG(LS_WARNING) << "HttpServer::CloseAll has not completed"; + } + for (ConnectionMap::iterator it = connections_.begin(); + it != connections_.end(); + ++it) { + StreamInterface* stream = it->second->EndProcess(); + delete stream; + delete it->second; + } +} + +int +HttpServer::HandleConnection(StreamInterface* stream) { + int connection_id = next_connection_id_++; + ASSERT(connection_id != HTTP_INVALID_CONNECTION_ID); + Connection* connection = new Connection(connection_id, this); + connections_.insert(ConnectionMap::value_type(connection_id, connection)); + connection->BeginProcess(stream); + return connection_id; +} + +void +HttpServer::Respond(HttpServerTransaction* transaction) { + int connection_id = transaction->connection_id(); + if (Connection* connection = Find(connection_id)) { + connection->Respond(transaction); + } else { + delete transaction; + // We may be tempted to SignalHttpComplete, but that implies that a + // connection still exists. + } +} + +void +HttpServer::Close(int connection_id, bool force) { + if (Connection* connection = Find(connection_id)) { + connection->InitiateClose(force); + } +} + +void +HttpServer::CloseAll(bool force) { + if (connections_.empty()) { + SignalCloseAllComplete(this); + return; + } + closing_ = true; + std::list connections; + for (ConnectionMap::const_iterator it = connections_.begin(); + it != connections_.end(); ++it) { + connections.push_back(it->second); + } + for (std::list::const_iterator it = connections.begin(); + it != connections.end(); ++it) { + (*it)->InitiateClose(force); + } +} + +HttpServer::Connection* +HttpServer::Find(int connection_id) { + ConnectionMap::iterator it = connections_.find(connection_id); + if (it == connections_.end()) + return NULL; + return it->second; +} + +void +HttpServer::Remove(int connection_id) { + ConnectionMap::iterator it = connections_.find(connection_id); + if (it == connections_.end()) { + ASSERT(false); + return; + } + Connection* connection = it->second; + connections_.erase(it); + SignalConnectionClosed(this, connection_id, connection->EndProcess()); + delete connection; + if (closing_ && connections_.empty()) { + closing_ = false; + SignalCloseAllComplete(this); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// HttpServer::Connection +/////////////////////////////////////////////////////////////////////////////// + +HttpServer::Connection::Connection(int connection_id, HttpServer* server) + : connection_id_(connection_id), server_(server), + current_(NULL), signalling_(false), close_(false) { +} + +HttpServer::Connection::~Connection() { + // It's possible that an object hosted inside this transaction signalled + // an event which caused the connection to close. + Thread::Current()->Dispose(current_); +} + +void +HttpServer::Connection::BeginProcess(StreamInterface* stream) { + base_.notify(this); + base_.attach(stream); + current_ = new HttpServerTransaction(connection_id_); + if (base_.mode() != HM_CONNECT) + base_.recv(¤t_->request); +} + +StreamInterface* +HttpServer::Connection::EndProcess() { + base_.notify(NULL); + base_.abort(HE_DISCONNECTED); + return base_.detach(); +} + +void +HttpServer::Connection::Respond(HttpServerTransaction* transaction) { + ASSERT(current_ == NULL); + current_ = transaction; + if (current_->response.begin() == current_->response.end()) { + current_->response.set_error(HC_INTERNAL_SERVER_ERROR); + } + bool keep_alive = HttpShouldKeepAlive(current_->request); + current_->response.setHeader(HH_CONNECTION, + keep_alive ? "Keep-Alive" : "Close", + false); + close_ = !HttpShouldKeepAlive(current_->response); + base_.send(¤t_->response); +} + +void +HttpServer::Connection::InitiateClose(bool force) { + bool request_in_progress = (HM_SEND == base_.mode()) || (NULL == current_); + if (!signalling_ && (force || !request_in_progress)) { + server_->Remove(connection_id_); + } else { + close_ = true; + } +} + +// +// IHttpNotify Implementation +// + +HttpError +HttpServer::Connection::onHttpHeaderComplete(bool chunked, size_t& data_size) { + if (data_size == SIZE_UNKNOWN) { + data_size = 0; + } + ASSERT(current_ != NULL); + bool custom_document = false; + server_->SignalHttpRequestHeader(server_, current_, &custom_document); + if (!custom_document) { + current_->request.document.reset(new MemoryStream); + } + return HE_NONE; +} + +void +HttpServer::Connection::onHttpComplete(HttpMode mode, HttpError err) { + if (mode == HM_SEND) { + ASSERT(current_ != NULL); + signalling_ = true; + server_->SignalHttpRequestComplete(server_, current_, err); + signalling_ = false; + if (close_) { + // Force a close + err = HE_DISCONNECTED; + } + } + if (err != HE_NONE) { + server_->Remove(connection_id_); + } else if (mode == HM_CONNECT) { + base_.recv(¤t_->request); + } else if (mode == HM_RECV) { + ASSERT(current_ != NULL); + // TODO: do we need this? + //request_.document_->rewind(); + HttpServerTransaction* transaction = current_; + current_ = NULL; + server_->SignalHttpRequest(server_, transaction); + } else if (mode == HM_SEND) { + Thread::Current()->Dispose(current_->response.document.release()); + current_->request.clear(true); + current_->response.clear(true); + base_.recv(¤t_->request); + } else { + ASSERT(false); + } +} + +void +HttpServer::Connection::onHttpClosed(HttpError err) { + RTC_UNUSED(err); + server_->Remove(connection_id_); +} + +/////////////////////////////////////////////////////////////////////////////// +// HttpListenServer +/////////////////////////////////////////////////////////////////////////////// + +HttpListenServer::HttpListenServer() { + SignalConnectionClosed.connect(this, &HttpListenServer::OnConnectionClosed); +} + +HttpListenServer::~HttpListenServer() { +} + +int HttpListenServer::Listen(const SocketAddress& address) { + AsyncSocket* sock = + Thread::Current()->socketserver()->CreateAsyncSocket(address.family(), + SOCK_STREAM); + if (!sock) { + return SOCKET_ERROR; + } + listener_.reset(sock); + listener_->SignalReadEvent.connect(this, &HttpListenServer::OnReadEvent); + if ((listener_->Bind(address) != SOCKET_ERROR) && + (listener_->Listen(5) != SOCKET_ERROR)) + return 0; + return listener_->GetError(); +} + +bool HttpListenServer::GetAddress(SocketAddress* address) const { + if (!listener_) { + return false; + } + *address = listener_->GetLocalAddress(); + return !address->IsNil(); +} + +void HttpListenServer::StopListening() { + if (listener_) { + listener_->Close(); + } +} + +void HttpListenServer::OnReadEvent(AsyncSocket* socket) { + ASSERT(socket == listener_.get()); + ASSERT(listener_); + AsyncSocket* incoming = listener_->Accept(NULL); + if (incoming) { + StreamInterface* stream = new SocketStream(incoming); + //stream = new LoggingAdapter(stream, LS_VERBOSE, "HttpServer", false); + HandleConnection(stream); + } +} + +void HttpListenServer::OnConnectionClosed(HttpServer* server, + int connection_id, + StreamInterface* stream) { + Thread::Current()->Dispose(stream); +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpserver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpserver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpserver.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpserver.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,137 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_HTTPSERVER_H__ +#define WEBRTC_BASE_HTTPSERVER_H__ + +#include +#include "webrtc/base/httpbase.h" + +namespace rtc { + +class AsyncSocket; +class HttpServer; +class SocketAddress; + +////////////////////////////////////////////////////////////////////// +// HttpServer +////////////////////////////////////////////////////////////////////// + +const int HTTP_INVALID_CONNECTION_ID = 0; + +struct HttpServerTransaction : public HttpTransaction { +public: + HttpServerTransaction(int id) : connection_id_(id) { } + int connection_id() const { return connection_id_; } + +private: + int connection_id_; +}; + +class HttpServer { +public: + HttpServer(); + virtual ~HttpServer(); + + int HandleConnection(StreamInterface* stream); + // Due to sigslot issues, we can't destroy some streams at an arbitrary time. + sigslot::signal3 SignalConnectionClosed; + + // This signal occurs when the HTTP request headers have been received, but + // before the request body is written to the request document. By default, + // the request document is a MemoryStream. By handling this signal, the + // document can be overridden, in which case the third signal argument should + // be set to true. In the case where the request body should be ignored, + // the document can be set to NULL. Note that the transaction object is still + // owened by the HttpServer at this point. + sigslot::signal3 + SignalHttpRequestHeader; + + // An HTTP request has been made, and is available in the transaction object. + // Populate the transaction's response, and then return the object via the + // Respond method. Note that during this time, ownership of the transaction + // object is transferred, so it may be passed between threads, although + // respond must be called on the server's active thread. + sigslot::signal2 SignalHttpRequest; + void Respond(HttpServerTransaction* transaction); + + // If you want to know when a request completes, listen to this event. + sigslot::signal3 + SignalHttpRequestComplete; + + // Stop processing the connection indicated by connection_id. + // Unless force is true, the server will complete sending a response that is + // in progress. + void Close(int connection_id, bool force); + void CloseAll(bool force); + + // After calling CloseAll, this event is signalled to indicate that all + // outstanding connections have closed. + sigslot::signal1 SignalCloseAllComplete; + +private: + class Connection : private IHttpNotify { + public: + Connection(int connection_id, HttpServer* server); + virtual ~Connection(); + + void BeginProcess(StreamInterface* stream); + StreamInterface* EndProcess(); + + void Respond(HttpServerTransaction* transaction); + void InitiateClose(bool force); + + // IHttpNotify Interface + virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size); + virtual void onHttpComplete(HttpMode mode, HttpError err); + virtual void onHttpClosed(HttpError err); + + int connection_id_; + HttpServer* server_; + HttpBase base_; + HttpServerTransaction* current_; + bool signalling_, close_; + }; + + Connection* Find(int connection_id); + void Remove(int connection_id); + + friend class Connection; + typedef std::map ConnectionMap; + + ConnectionMap connections_; + int next_connection_id_; + bool closing_; +}; + +////////////////////////////////////////////////////////////////////// + +class HttpListenServer : public HttpServer, public sigslot::has_slots<> { +public: + HttpListenServer(); + virtual ~HttpListenServer(); + + int Listen(const SocketAddress& address); + bool GetAddress(SocketAddress* address) const; + void StopListening(); + +private: + void OnReadEvent(AsyncSocket* socket); + void OnConnectionClosed(HttpServer* server, int connection_id, + StreamInterface* stream); + + scoped_ptr listener_; +}; + +////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_HTTPSERVER_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpserver_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpserver_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/httpserver_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/httpserver_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,130 @@ +/* + * Copyright 2007 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/httpserver.h" +#include "webrtc/base/testutils.h" + +using namespace testing; + +namespace rtc { + +namespace { + const char* const kRequest = + "GET /index.html HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n"; + + struct HttpServerMonitor : public sigslot::has_slots<> { + HttpServerTransaction* transaction; + bool server_closed, connection_closed; + + HttpServerMonitor(HttpServer* server) + : transaction(NULL), server_closed(false), connection_closed(false) { + server->SignalCloseAllComplete.connect(this, + &HttpServerMonitor::OnClosed); + server->SignalHttpRequest.connect(this, &HttpServerMonitor::OnRequest); + server->SignalHttpRequestComplete.connect(this, + &HttpServerMonitor::OnRequestComplete); + server->SignalConnectionClosed.connect(this, + &HttpServerMonitor::OnConnectionClosed); + } + void OnRequest(HttpServer*, HttpServerTransaction* t) { + ASSERT_FALSE(transaction); + transaction = t; + transaction->response.set_success(); + transaction->response.setHeader(HH_CONNECTION, "Close"); + } + void OnRequestComplete(HttpServer*, HttpServerTransaction* t, int) { + ASSERT_EQ(transaction, t); + transaction = NULL; + } + void OnClosed(HttpServer*) { + server_closed = true; + } + void OnConnectionClosed(HttpServer*, int, StreamInterface* stream) { + connection_closed = true; + delete stream; + } + }; + + void CreateClientConnection(HttpServer& server, + HttpServerMonitor& monitor, + bool send_request) { + StreamSource* client = new StreamSource; + client->SetState(SS_OPEN); + server.HandleConnection(client); + EXPECT_FALSE(monitor.server_closed); + EXPECT_FALSE(monitor.transaction); + + if (send_request) { + // Simulate a request + client->QueueString(kRequest); + EXPECT_FALSE(monitor.server_closed); + } + } +} // anonymous namespace + +TEST(HttpServer, DoesNotSignalCloseUnlessCloseAllIsCalled) { + HttpServer server; + HttpServerMonitor monitor(&server); + // Add an active client connection + CreateClientConnection(server, monitor, true); + // Simulate a response + ASSERT_TRUE(NULL != monitor.transaction); + server.Respond(monitor.transaction); + EXPECT_FALSE(monitor.transaction); + // Connection has closed, but no server close signal + EXPECT_FALSE(monitor.server_closed); + EXPECT_TRUE(monitor.connection_closed); +} + +TEST(HttpServer, SignalsCloseWhenNoConnectionsAreActive) { + HttpServer server; + HttpServerMonitor monitor(&server); + // Add an idle client connection + CreateClientConnection(server, monitor, false); + // Perform graceful close + server.CloseAll(false); + // Connections have all closed + EXPECT_TRUE(monitor.server_closed); + EXPECT_TRUE(monitor.connection_closed); +} + +TEST(HttpServer, SignalsCloseAfterGracefulCloseAll) { + HttpServer server; + HttpServerMonitor monitor(&server); + // Add an active client connection + CreateClientConnection(server, monitor, true); + // Initiate a graceful close + server.CloseAll(false); + EXPECT_FALSE(monitor.server_closed); + // Simulate a response + ASSERT_TRUE(NULL != monitor.transaction); + server.Respond(monitor.transaction); + EXPECT_FALSE(monitor.transaction); + // Connections have all closed + EXPECT_TRUE(monitor.server_closed); + EXPECT_TRUE(monitor.connection_closed); +} + +TEST(HttpServer, SignalsCloseAfterForcedCloseAll) { + HttpServer server; + HttpServerMonitor monitor(&server); + // Add an active client connection + CreateClientConnection(server, monitor, true); + // Initiate a forceful close + server.CloseAll(true); + // Connections have all closed + EXPECT_TRUE(monitor.server_closed); + EXPECT_TRUE(monitor.connection_closed); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ifaddrs-android.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ifaddrs-android.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ifaddrs-android.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ifaddrs-android.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,223 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if defined(WEBRTC_ANDROID) +#include "webrtc/base/ifaddrs-android.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +struct netlinkrequest { + nlmsghdr header; + ifaddrmsg msg; +}; + +const int kMaxReadSize = 4096; + +} // namespace + +namespace rtc { + +int set_ifname(struct ifaddrs* ifaddr, int interface) { + char buf[IFNAMSIZ] = {0}; + char* name = if_indextoname(interface, buf); + if (name == NULL) { + return -1; + } + ifaddr->ifa_name = new char[strlen(name) + 1]; + strncpy(ifaddr->ifa_name, name, strlen(name) + 1); + return 0; +} + +int set_flags(struct ifaddrs* ifaddr) { + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + return -1; + } + ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifaddr->ifa_name, IFNAMSIZ - 1); + int rc = ioctl(fd, SIOCGIFFLAGS, &ifr); + close(fd); + if (rc == -1) { + return -1; + } + ifaddr->ifa_flags = ifr.ifr_flags; + return 0; +} + +int set_addresses(struct ifaddrs* ifaddr, ifaddrmsg* msg, void* data, + size_t len) { + if (msg->ifa_family == AF_INET) { + sockaddr_in* sa = new sockaddr_in; + sa->sin_family = AF_INET; + memcpy(&sa->sin_addr, data, len); + ifaddr->ifa_addr = reinterpret_cast(sa); + } else if (msg->ifa_family == AF_INET6) { + sockaddr_in6* sa = new sockaddr_in6; + sa->sin6_family = AF_INET6; + sa->sin6_scope_id = msg->ifa_index; + memcpy(&sa->sin6_addr, data, len); + ifaddr->ifa_addr = reinterpret_cast(sa); + } else { + return -1; + } + return 0; +} + +int make_prefixes(struct ifaddrs* ifaddr, int family, int prefixlen) { + char* prefix = NULL; + if (family == AF_INET) { + sockaddr_in* mask = new sockaddr_in; + mask->sin_family = AF_INET; + memset(&mask->sin_addr, 0, sizeof(in_addr)); + ifaddr->ifa_netmask = reinterpret_cast(mask); + if (prefixlen > 32) { + prefixlen = 32; + } + prefix = reinterpret_cast(&mask->sin_addr); + } else if (family == AF_INET6) { + sockaddr_in6* mask = new sockaddr_in6; + mask->sin6_family = AF_INET6; + memset(&mask->sin6_addr, 0, sizeof(in6_addr)); + ifaddr->ifa_netmask = reinterpret_cast(mask); + if (prefixlen > 128) { + prefixlen = 128; + } + prefix = reinterpret_cast(&mask->sin6_addr); + } else { + return -1; + } + for (int i = 0; i < (prefixlen / 8); i++) { + *prefix++ = 0xFF; + } + char remainder = 0xff; + remainder <<= (8 - prefixlen % 8); + *prefix = remainder; + return 0; +} + +int populate_ifaddrs(struct ifaddrs* ifaddr, ifaddrmsg* msg, void* bytes, + size_t len) { + if (set_ifname(ifaddr, msg->ifa_index) != 0) { + return -1; + } + if (set_flags(ifaddr) != 0) { + return -1; + } + if (set_addresses(ifaddr, msg, bytes, len) != 0) { + return -1; + } + if (make_prefixes(ifaddr, msg->ifa_family, msg->ifa_prefixlen) != 0) { + return -1; + } + return 0; +} + +int getifaddrs(struct ifaddrs** result) { + int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd < 0) { + return -1; + } + + netlinkrequest ifaddr_request; + memset(&ifaddr_request, 0, sizeof(ifaddr_request)); + ifaddr_request.header.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST; + ifaddr_request.header.nlmsg_type = RTM_GETADDR; + ifaddr_request.header.nlmsg_len = NLMSG_LENGTH(sizeof(ifaddrmsg)); + + ssize_t count = send(fd, &ifaddr_request, ifaddr_request.header.nlmsg_len, 0); + if (static_cast(count) != ifaddr_request.header.nlmsg_len) { + close(fd); + return -1; + } + struct ifaddrs* start = NULL; + struct ifaddrs* current = NULL; + char buf[kMaxReadSize]; + ssize_t amount_read = recv(fd, &buf, kMaxReadSize, 0); + while (amount_read > 0) { + nlmsghdr* header = reinterpret_cast(&buf[0]); + size_t header_size = static_cast(amount_read); + for ( ; NLMSG_OK(header, header_size); + header = NLMSG_NEXT(header, header_size)) { + switch (header->nlmsg_type) { + case NLMSG_DONE: + // Success. Return. + *result = start; + close(fd); + return 0; + case NLMSG_ERROR: + close(fd); + freeifaddrs(start); + return -1; + case RTM_NEWADDR: { + ifaddrmsg* address_msg = + reinterpret_cast(NLMSG_DATA(header)); + rtattr* rta = IFA_RTA(address_msg); + ssize_t payload_len = IFA_PAYLOAD(header); + while (RTA_OK(rta, payload_len)) { + if (rta->rta_type == IFA_ADDRESS) { + int family = address_msg->ifa_family; + if (family == AF_INET || family == AF_INET6) { + ifaddrs* newest = new ifaddrs; + memset(newest, 0, sizeof(ifaddrs)); + if (current) { + current->ifa_next = newest; + } else { + start = newest; + } + if (populate_ifaddrs(newest, address_msg, RTA_DATA(rta), + RTA_PAYLOAD(rta)) != 0) { + freeifaddrs(start); + *result = NULL; + return -1; + } + current = newest; + } + } + rta = RTA_NEXT(rta, payload_len); + } + break; + } + } + } + amount_read = recv(fd, &buf, kMaxReadSize, 0); + } + close(fd); + freeifaddrs(start); + return -1; +} + +void freeifaddrs(struct ifaddrs* addrs) { + struct ifaddrs* last = NULL; + struct ifaddrs* cursor = addrs; + while (cursor) { + delete[] cursor->ifa_name; + delete cursor->ifa_addr; + delete cursor->ifa_netmask; + last = cursor; + cursor = cursor->ifa_next; + delete last; + } +} + +} // namespace rtc +#endif // defined(WEBRTC_ANDROID) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ifaddrs-android.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ifaddrs-android.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ifaddrs-android.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ifaddrs-android.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,39 @@ +/* + * Copyright 2013 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_IFADDRS_ANDROID_H_ +#define WEBRTC_BASE_IFADDRS_ANDROID_H_ + +#include +#include + + +// Implementation of getifaddrs for Android. +// Fills out a list of ifaddr structs (see below) which contain information +// about every network interface available on the host. +// See 'man getifaddrs' on Linux or OS X (nb: it is not a POSIX function). +struct ifaddrs { + struct ifaddrs* ifa_next; + char* ifa_name; + unsigned int ifa_flags; + struct sockaddr* ifa_addr; + struct sockaddr* ifa_netmask; + // Real ifaddrs has broadcast, point to point and data members. + // We don't need them (yet?). +}; + +namespace rtc { + +int getifaddrs(struct ifaddrs** result); +void freeifaddrs(struct ifaddrs* addrs); + +} // namespace rtc + +#endif // WEBRTC_BASE_IFADDRS_ANDROID_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/iosfilesystem.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/iosfilesystem.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/iosfilesystem.mm 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/iosfilesystem.mm 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,53 @@ +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file only exists because various iOS system APIs are only +// available from Objective-C. See unixfilesystem.cc for the only use +// (enforced by a lack of a header file). + +#import +#import +#include + +#include "webrtc/base/common.h" +#include "webrtc/base/pathutils.h" + +// Return a new[]'d |char*| copy of the UTF8 representation of |s|. +// Caller owns the returned memory and must use delete[] on it. +static char* copyString(NSString* s) { + const char* utf8 = [s UTF8String]; + size_t len = strlen(utf8) + 1; + char* copy = new char[len]; + // This uses a new[] + strcpy (instead of strdup) because the + // receiver expects to be able to delete[] the returned pointer + // (instead of free()ing it). + strcpy(copy, utf8); + return copy; +} + +// Return a (leaked) copy of a directory name suitable for application data. +char* IOSDataDirectory() { + NSArray* paths = NSSearchPathForDirectoriesInDomains( + NSApplicationSupportDirectory, NSUserDomainMask, YES); + ASSERT([paths count] == 1); + return copyString([paths objectAtIndex:0]); +} + +// Return a (leaked) copy of a directory name suitable for use as a $TEMP. +char* IOSTempDirectory() { + return copyString(NSTemporaryDirectory()); +} + +// Return the binary's path. +void IOSAppName(rtc::Pathname* path) { + NSProcessInfo *pInfo = [NSProcessInfo processInfo]; + NSString* argv0 = [[pInfo arguments] objectAtIndex:0]; + path->SetPathname([argv0 UTF8String]); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ipaddress.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ipaddress.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ipaddress.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ipaddress.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,484 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if defined(WEBRTC_POSIX) +#include +#include +#include +#ifdef OPENBSD +#include +#endif +#ifndef __native_client__ +#include +#endif +#include +#include +#include +#endif + +#include + +#include "webrtc/base/ipaddress.h" +#include "webrtc/base/byteorder.h" +#include "webrtc/base/nethelpers.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/win32.h" + +namespace rtc { + +// Prefixes used for categorizing IPv6 addresses. +static const in6_addr kV4MappedPrefix = {{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0xFF, 0xFF, 0}}}; +static const in6_addr k6To4Prefix = {{{0x20, 0x02, 0}}}; +static const in6_addr kTeredoPrefix = {{{0x20, 0x01, 0x00, 0x00}}}; +static const in6_addr kV4CompatibilityPrefix = {{{0}}}; +static const in6_addr k6BonePrefix = {{{0x3f, 0xfe, 0}}}; + +bool IPAddress::strip_sensitive_ = false; + +static bool IsPrivateV4(uint32 ip); +static in_addr ExtractMappedAddress(const in6_addr& addr); + +uint32 IPAddress::v4AddressAsHostOrderInteger() const { + if (family_ == AF_INET) { + return NetworkToHost32(u_.ip4.s_addr); + } else { + return 0; + } +} + +size_t IPAddress::Size() const { + switch (family_) { + case AF_INET: + return sizeof(in_addr); + case AF_INET6: + return sizeof(in6_addr); + } + return 0; +} + + +bool IPAddress::operator==(const IPAddress &other) const { + if (family_ != other.family_) { + return false; + } + if (family_ == AF_INET) { + return memcmp(&u_.ip4, &other.u_.ip4, sizeof(u_.ip4)) == 0; + } + if (family_ == AF_INET6) { + return memcmp(&u_.ip6, &other.u_.ip6, sizeof(u_.ip6)) == 0; + } + return family_ == AF_UNSPEC; +} + +bool IPAddress::operator!=(const IPAddress &other) const { + return !((*this) == other); +} + +bool IPAddress::operator >(const IPAddress &other) const { + return (*this) != other && !((*this) < other); +} + +bool IPAddress::operator <(const IPAddress &other) const { + // IPv4 is 'less than' IPv6 + if (family_ != other.family_) { + if (family_ == AF_UNSPEC) { + return true; + } + if (family_ == AF_INET && other.family_ == AF_INET6) { + return true; + } + return false; + } + // Comparing addresses of the same family. + switch (family_) { + case AF_INET: { + return NetworkToHost32(u_.ip4.s_addr) < + NetworkToHost32(other.u_.ip4.s_addr); + } + case AF_INET6: { + return memcmp(&u_.ip6.s6_addr, &other.u_.ip6.s6_addr, 16) < 0; + } + } + // Catches AF_UNSPEC and invalid addresses. + return false; +} + +std::ostream& operator<<(std::ostream& os, const IPAddress& ip) { + os << ip.ToString(); + return os; +} + +in6_addr IPAddress::ipv6_address() const { + return u_.ip6; +} + +in_addr IPAddress::ipv4_address() const { + return u_.ip4; +} + +std::string IPAddress::ToString() const { + if (family_ != AF_INET && family_ != AF_INET6) { + return std::string(); + } + char buf[INET6_ADDRSTRLEN] = {0}; + const void* src = &u_.ip4; + if (family_ == AF_INET6) { + src = &u_.ip6; + } + if (!rtc::inet_ntop(family_, src, buf, sizeof(buf))) { + return std::string(); + } + return std::string(buf); +} + +std::string IPAddress::ToSensitiveString() const { + if (!strip_sensitive_) + return ToString(); + + switch (family_) { + case AF_INET: { + std::string address = ToString(); + size_t find_pos = address.rfind('.'); + if (find_pos == std::string::npos) + return std::string(); + address.resize(find_pos); + address += ".x"; + return address; + } + case AF_INET6: { + // TODO(grunell): Return a string of format 1:2:3:x:x:x:x:x or such + // instead of zeroing out. + return TruncateIP(*this, 128 - 80).ToString(); + } + } + return std::string(); +} + +IPAddress IPAddress::Normalized() const { + if (family_ != AF_INET6) { + return *this; + } + if (!IPIsV4Mapped(*this)) { + return *this; + } + in_addr addr = ExtractMappedAddress(u_.ip6); + return IPAddress(addr); +} + +IPAddress IPAddress::AsIPv6Address() const { + if (family_ != AF_INET) { + return *this; + } + in6_addr v6addr = kV4MappedPrefix; + ::memcpy(&v6addr.s6_addr[12], &u_.ip4.s_addr, sizeof(u_.ip4.s_addr)); + return IPAddress(v6addr); +} + +void IPAddress::set_strip_sensitive(bool enable) { + strip_sensitive_ = enable; +} + +bool InterfaceAddress::operator==(const InterfaceAddress &other) const { + return ipv6_flags_ == other.ipv6_flags() && + static_cast(*this) == other; +} + +bool InterfaceAddress::operator!=(const InterfaceAddress &other) const { + return !((*this) == other); +} + +const InterfaceAddress& InterfaceAddress::operator=( + const InterfaceAddress& other) { + ipv6_flags_ = other.ipv6_flags_; + static_cast(*this) = other; + return *this; +} + +std::ostream& operator<<(std::ostream& os, const InterfaceAddress& ip) { + os << static_cast(ip); + + if (ip.family() == AF_INET6) + os << "|flags:0x" << std::hex << ip.ipv6_flags(); + + return os; +} + +bool IsPrivateV4(uint32 ip_in_host_order) { + return ((ip_in_host_order >> 24) == 127) || + ((ip_in_host_order >> 24) == 10) || + ((ip_in_host_order >> 20) == ((172 << 4) | 1)) || + ((ip_in_host_order >> 16) == ((192 << 8) | 168)) || + ((ip_in_host_order >> 16) == ((169 << 8) | 254)); +} + +in_addr ExtractMappedAddress(const in6_addr& in6) { + in_addr ipv4; + ::memcpy(&ipv4.s_addr, &in6.s6_addr[12], sizeof(ipv4.s_addr)); + return ipv4; +} + +bool IPFromAddrInfo(struct addrinfo* info, IPAddress* out) { + if (!info || !info->ai_addr) { + return false; + } + if (info->ai_addr->sa_family == AF_INET) { + sockaddr_in* addr = reinterpret_cast(info->ai_addr); + *out = IPAddress(addr->sin_addr); + return true; + } else if (info->ai_addr->sa_family == AF_INET6) { + sockaddr_in6* addr = reinterpret_cast(info->ai_addr); + *out = IPAddress(addr->sin6_addr); + return true; + } + return false; +} + +bool IPFromString(const std::string& str, IPAddress* out) { + if (!out) { + return false; + } + in_addr addr; + if (rtc::inet_pton(AF_INET, str.c_str(), &addr) == 0) { + in6_addr addr6; + if (rtc::inet_pton(AF_INET6, str.c_str(), &addr6) == 0) { + *out = IPAddress(); + return false; + } + *out = IPAddress(addr6); + } else { + *out = IPAddress(addr); + } + return true; +} + +bool IPFromString(const std::string& str, int flags, + InterfaceAddress* out) { + IPAddress ip; + if (!IPFromString(str, &ip)) { + return false; + } + + *out = InterfaceAddress(ip, flags); + return true; +} + +bool IPIsAny(const IPAddress& ip) { + switch (ip.family()) { + case AF_INET: + return ip == IPAddress(INADDR_ANY); + case AF_INET6: + return ip == IPAddress(in6addr_any); + case AF_UNSPEC: + return false; + } + return false; +} + +bool IPIsLoopback(const IPAddress& ip) { + switch (ip.family()) { + case AF_INET: { + return ip == IPAddress(INADDR_LOOPBACK); + } + case AF_INET6: { + return ip == IPAddress(in6addr_loopback); + } + } + return false; +} + +bool IPIsPrivate(const IPAddress& ip) { + switch (ip.family()) { + case AF_INET: { + return IsPrivateV4(ip.v4AddressAsHostOrderInteger()); + } + case AF_INET6: { + in6_addr v6 = ip.ipv6_address(); + return (v6.s6_addr[0] == 0xFE && v6.s6_addr[1] == 0x80) || + IPIsLoopback(ip); + } + } + return false; +} + +bool IPIsUnspec(const IPAddress& ip) { + return ip.family() == AF_UNSPEC; +} + +size_t HashIP(const IPAddress& ip) { + switch (ip.family()) { + case AF_INET: { + return ip.ipv4_address().s_addr; + } + case AF_INET6: { + in6_addr v6addr = ip.ipv6_address(); + const uint32* v6_as_ints = + reinterpret_cast(&v6addr.s6_addr); + return v6_as_ints[0] ^ v6_as_ints[1] ^ v6_as_ints[2] ^ v6_as_ints[3]; + } + } + return 0; +} + +IPAddress TruncateIP(const IPAddress& ip, int length) { + if (length < 0) { + return IPAddress(); + } + if (ip.family() == AF_INET) { + if (length > 31) { + return ip; + } + if (length == 0) { + return IPAddress(INADDR_ANY); + } + int mask = (0xFFFFFFFF << (32 - length)); + uint32 host_order_ip = NetworkToHost32(ip.ipv4_address().s_addr); + in_addr masked; + masked.s_addr = HostToNetwork32(host_order_ip & mask); + return IPAddress(masked); + } else if (ip.family() == AF_INET6) { + if (length > 127) { + return ip; + } + if (length == 0) { + return IPAddress(in6addr_any); + } + in6_addr v6addr = ip.ipv6_address(); + int position = length / 32; + int inner_length = 32 - (length - (position * 32)); + // Note: 64bit mask constant needed to allow possible 32-bit left shift. + uint32 inner_mask = 0xFFFFFFFFLL << inner_length; + uint32* v6_as_ints = + reinterpret_cast(&v6addr.s6_addr); + for (int i = 0; i < 4; ++i) { + if (i == position) { + uint32 host_order_inner = NetworkToHost32(v6_as_ints[i]); + v6_as_ints[i] = HostToNetwork32(host_order_inner & inner_mask); + } else if (i > position) { + v6_as_ints[i] = 0; + } + } + return IPAddress(v6addr); + } + return IPAddress(); +} + +int CountIPMaskBits(IPAddress mask) { + uint32 word_to_count = 0; + int bits = 0; + switch (mask.family()) { + case AF_INET: { + word_to_count = NetworkToHost32(mask.ipv4_address().s_addr); + break; + } + case AF_INET6: { + in6_addr v6addr = mask.ipv6_address(); + const uint32* v6_as_ints = + reinterpret_cast(&v6addr.s6_addr); + int i = 0; + for (; i < 4; ++i) { + if (v6_as_ints[i] != 0xFFFFFFFF) { + break; + } + } + if (i < 4) { + word_to_count = NetworkToHost32(v6_as_ints[i]); + } + bits = (i * 32); + break; + } + default: { + return 0; + } + } + if (word_to_count == 0) { + return bits; + } + + // Public domain bit-twiddling hack from: + // http://graphics.stanford.edu/~seander/bithacks.html + // Counts the trailing 0s in the word. + unsigned int zeroes = 32; + word_to_count &= -static_cast(word_to_count); + if (word_to_count) zeroes--; + if (word_to_count & 0x0000FFFF) zeroes -= 16; + if (word_to_count & 0x00FF00FF) zeroes -= 8; + if (word_to_count & 0x0F0F0F0F) zeroes -= 4; + if (word_to_count & 0x33333333) zeroes -= 2; + if (word_to_count & 0x55555555) zeroes -= 1; + + return bits + (32 - zeroes); +} + +bool IPIsHelper(const IPAddress& ip, const in6_addr& tomatch, int length) { + // Helper method for checking IP prefix matches (but only on whole byte + // lengths). Length is in bits. + in6_addr addr = ip.ipv6_address(); + return ::memcmp(&addr, &tomatch, (length >> 3)) == 0; +} + +bool IPIs6Bone(const IPAddress& ip) { + return IPIsHelper(ip, k6BonePrefix, 16); +} + +bool IPIs6To4(const IPAddress& ip) { + return IPIsHelper(ip, k6To4Prefix, 16); +} + +bool IPIsSiteLocal(const IPAddress& ip) { + // Can't use the helper because the prefix is 10 bits. + in6_addr addr = ip.ipv6_address(); + return addr.s6_addr[0] == 0xFE && (addr.s6_addr[1] & 0xC0) == 0xC0; +} + +bool IPIsULA(const IPAddress& ip) { + // Can't use the helper because the prefix is 7 bits. + in6_addr addr = ip.ipv6_address(); + return (addr.s6_addr[0] & 0xFE) == 0xFC; +} + +bool IPIsTeredo(const IPAddress& ip) { + return IPIsHelper(ip, kTeredoPrefix, 32); +} + +bool IPIsV4Compatibility(const IPAddress& ip) { + return IPIsHelper(ip, kV4CompatibilityPrefix, 96); +} + +bool IPIsV4Mapped(const IPAddress& ip) { + return IPIsHelper(ip, kV4MappedPrefix, 96); +} + +int IPAddressPrecedence(const IPAddress& ip) { + // Precedence values from RFC 3484-bis. Prefers native v4 over 6to4/Teredo. + if (ip.family() == AF_INET) { + return 30; + } else if (ip.family() == AF_INET6) { + if (IPIsLoopback(ip)) { + return 60; + } else if (IPIsULA(ip)) { + return 50; + } else if (IPIsV4Mapped(ip)) { + return 30; + } else if (IPIs6To4(ip)) { + return 20; + } else if (IPIsTeredo(ip)) { + return 10; + } else if (IPIsV4Compatibility(ip) || IPIsSiteLocal(ip) || IPIs6Bone(ip)) { + return 1; + } else { + // A 'normal' IPv6 address. + return 40; + } + } + return 0; +} + +} // Namespace talk base diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ipaddress.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ipaddress.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ipaddress.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ipaddress.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,184 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_IPADDRESS_H_ +#define WEBRTC_BASE_IPADDRESS_H_ + +#if defined(WEBRTC_POSIX) +#include +#include +#include +#include +#endif +#if defined(WEBRTC_WIN) +#include +#include +#endif +#include +#include +#include + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/byteorder.h" +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32.h" +#endif + +namespace rtc { + +enum IPv6AddressFlag { + IPV6_ADDRESS_FLAG_NONE = 0x00, + + // Temporary address is dynamic by nature and will not carry MAC + // address. + IPV6_ADDRESS_FLAG_TEMPORARY = 1 << 0, + + // Temporary address could become deprecated once the preferred + // lifetime is reached. It is still valid but just shouldn't be used + // to create new connection. + IPV6_ADDRESS_FLAG_DEPRECATED = 1 << 1, +}; + +// Version-agnostic IP address class, wraps a union of in_addr and in6_addr. +class IPAddress { + public: + IPAddress() : family_(AF_UNSPEC) { + ::memset(&u_, 0, sizeof(u_)); + } + + explicit IPAddress(const in_addr& ip4) : family_(AF_INET) { + memset(&u_, 0, sizeof(u_)); + u_.ip4 = ip4; + } + + explicit IPAddress(const in6_addr& ip6) : family_(AF_INET6) { + u_.ip6 = ip6; + } + + explicit IPAddress(uint32 ip_in_host_byte_order) : family_(AF_INET) { + memset(&u_, 0, sizeof(u_)); + u_.ip4.s_addr = HostToNetwork32(ip_in_host_byte_order); + } + + IPAddress(const IPAddress& other) : family_(other.family_) { + ::memcpy(&u_, &other.u_, sizeof(u_)); + } + + virtual ~IPAddress() {} + + const IPAddress & operator=(const IPAddress& other) { + family_ = other.family_; + ::memcpy(&u_, &other.u_, sizeof(u_)); + return *this; + } + + bool operator==(const IPAddress& other) const; + bool operator!=(const IPAddress& other) const; + bool operator <(const IPAddress& other) const; + bool operator >(const IPAddress& other) const; + friend std::ostream& operator<<(std::ostream& os, const IPAddress& addr); + + int family() const { return family_; } + in_addr ipv4_address() const; + in6_addr ipv6_address() const; + + // Returns the number of bytes needed to store the raw address. + size_t Size() const; + + // Wraps inet_ntop. + std::string ToString() const; + + // Same as ToString but anonymizes it by hiding the last part. + std::string ToSensitiveString() const; + + // Returns an unmapped address from a possibly-mapped address. + // Returns the same address if this isn't a mapped address. + IPAddress Normalized() const; + + // Returns this address as an IPv6 address. + // Maps v4 addresses (as ::ffff:a.b.c.d), returns v6 addresses unchanged. + IPAddress AsIPv6Address() const; + + // For socketaddress' benefit. Returns the IP in host byte order. + uint32 v4AddressAsHostOrderInteger() const; + + static void set_strip_sensitive(bool enable); + + private: + int family_; + union { + in_addr ip4; + in6_addr ip6; + } u_; + + static bool strip_sensitive_; +}; + +// IP class which could represent IPv6 address flags which is only +// meaningful in IPv6 case. +class InterfaceAddress : public IPAddress { + public: + InterfaceAddress() : ipv6_flags_(IPV6_ADDRESS_FLAG_NONE) {} + + InterfaceAddress(IPAddress ip) + : IPAddress(ip), ipv6_flags_(IPV6_ADDRESS_FLAG_NONE) {} + + InterfaceAddress(IPAddress addr, int ipv6_flags) + : IPAddress(addr), ipv6_flags_(ipv6_flags) {} + + InterfaceAddress(const in6_addr& ip6, int ipv6_flags) + : IPAddress(ip6), ipv6_flags_(ipv6_flags) {} + + const InterfaceAddress & operator=(const InterfaceAddress& other); + + bool operator==(const InterfaceAddress& other) const; + bool operator!=(const InterfaceAddress& other) const; + + int ipv6_flags() const { return ipv6_flags_; } + friend std::ostream& operator<<(std::ostream& os, + const InterfaceAddress& addr); + + private: + int ipv6_flags_; +}; + +bool IPFromAddrInfo(struct addrinfo* info, IPAddress* out); +bool IPFromString(const std::string& str, IPAddress* out); +bool IPFromString(const std::string& str, int flags, + InterfaceAddress* out); +bool IPIsAny(const IPAddress& ip); +bool IPIsLoopback(const IPAddress& ip); +bool IPIsPrivate(const IPAddress& ip); +bool IPIsUnspec(const IPAddress& ip); +size_t HashIP(const IPAddress& ip); + +// These are only really applicable for IPv6 addresses. +bool IPIs6Bone(const IPAddress& ip); +bool IPIs6To4(const IPAddress& ip); +bool IPIsSiteLocal(const IPAddress& ip); +bool IPIsTeredo(const IPAddress& ip); +bool IPIsULA(const IPAddress& ip); +bool IPIsV4Compatibility(const IPAddress& ip); +bool IPIsV4Mapped(const IPAddress& ip); + +// Returns the precedence value for this IP as given in RFC3484. +int IPAddressPrecedence(const IPAddress& ip); + +// Returns 'ip' truncated to be 'length' bits long. +IPAddress TruncateIP(const IPAddress& ip, int length); + +// Returns the number of contiguously set bits, counting from the MSB in network +// byte order, in this IPAddress. Bits after the first 0 encountered are not +// counted. +int CountIPMaskBits(IPAddress mask); + +} // namespace rtc + +#endif // WEBRTC_BASE_IPADDRESS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ipaddress_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ipaddress_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ipaddress_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ipaddress_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,896 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/ipaddress.h" + +namespace rtc { + +static const unsigned int kIPv4AddrSize = 4; +static const unsigned int kIPv6AddrSize = 16; +static const unsigned int kIPv4RFC1918Addr = 0xC0A80701; +static const unsigned int kIPv4PublicAddr = 0x01020304; +static const in6_addr kIPv6LinkLocalAddr = {{{0xfe, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x30, 0x5b, 0xff, + 0xfe, 0xe5, 0x00, 0xc3}}}; +static const in6_addr kIPv6PublicAddr = {{{0x24, 0x01, 0xfa, 0x00, + 0x00, 0x04, 0x10, 0x00, + 0xbe, 0x30, 0x5b, 0xff, + 0xfe, 0xe5, 0x00, 0xc3}}}; +static const in6_addr kIPv4MappedAnyAddr = {{{0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00}}}; +static const in6_addr kIPv4MappedRFC1918Addr = {{{0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, + 0xc0, 0xa8, 0x07, 0x01}}}; +static const in6_addr kIPv4MappedPublicAddr = {{{0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, + 0x01, 0x02, 0x03, 0x04}}}; + +static const std::string kIPv4AnyAddrString = "0.0.0.0"; +static const std::string kIPv4LoopbackAddrString = "127.0.0.1"; +static const std::string kIPv4RFC1918AddrString = "192.168.7.1"; +static const std::string kIPv4PublicAddrString = "1.2.3.4"; +static const std::string kIPv4PublicAddrAnonymizedString = "1.2.3.x"; +static const std::string kIPv6AnyAddrString = "::"; +static const std::string kIPv6LoopbackAddrString = "::1"; +static const std::string kIPv6LinkLocalAddrString = "fe80::be30:5bff:fee5:c3"; +static const std::string kIPv6PublicAddrString = + "2401:fa00:4:1000:be30:5bff:fee5:c3"; +static const std::string kIPv6PublicAddrAnonymizedString = "2401:fa00:4::"; +static const std::string kIPv4MappedAnyAddrString = "::ffff:0:0"; +static const std::string kIPv4MappedRFC1918AddrString = "::ffff:c0a8:701"; +static const std::string kIPv4MappedLoopbackAddrString = "::ffff:7f00:1"; +static const std::string kIPv4MappedPublicAddrString = "::ffff:102:0304"; +static const std::string kIPv4MappedV4StyleAddrString = "::ffff:192.168.7.1"; + +static const std::string kIPv4BrokenString1 = "192.168.7."; +static const std::string kIPv4BrokenString2 = "192.168.7.1.1"; +static const std::string kIPv4BrokenString3 = "192.168.7.1:80"; +static const std::string kIPv4BrokenString4 = "192.168.7.ONE"; +static const std::string kIPv4BrokenString5 = "-192.168.7.1"; +static const std::string kIPv4BrokenString6 = "256.168.7.1"; +static const std::string kIPv6BrokenString1 = "2401:fa00:4:1000:be30"; +static const std::string kIPv6BrokenString2 = + "2401:fa00:4:1000:be30:5bff:fee5:c3:1"; +static const std::string kIPv6BrokenString3 = + "[2401:fa00:4:1000:be30:5bff:fee5:c3]:1"; +static const std::string kIPv6BrokenString4 = + "2401::4::be30"; +static const std::string kIPv6BrokenString5 = + "2401:::4:fee5:be30"; +static const std::string kIPv6BrokenString6 = + "2401f:fa00:4:1000:be30:5bff:fee5:c3"; +static const std::string kIPv6BrokenString7 = + "2401:ga00:4:1000:be30:5bff:fee5:c3"; +static const std::string kIPv6BrokenString8 = + "2401:fa000:4:1000:be30:5bff:fee5:c3"; +static const std::string kIPv6BrokenString9 = + "2401:fal0:4:1000:be30:5bff:fee5:c3"; +static const std::string kIPv6BrokenString10 = + "::ffff:192.168.7."; +static const std::string kIPv6BrokenString11 = + "::ffff:192.168.7.1.1.1"; +static const std::string kIPv6BrokenString12 = + "::fffe:192.168.7.1"; +static const std::string kIPv6BrokenString13 = + "::ffff:192.168.7.ff"; +static const std::string kIPv6BrokenString14 = + "0x2401:fa00:4:1000:be30:5bff:fee5:c3"; + +bool AreEqual(const IPAddress& addr, + const IPAddress& addr2) { + if ((IPIsAny(addr) != IPIsAny(addr2)) || + (IPIsLoopback(addr) != IPIsLoopback(addr2)) || + (IPIsPrivate(addr) != IPIsPrivate(addr2)) || + (HashIP(addr) != HashIP(addr2)) || + (addr.Size() != addr2.Size()) || + (addr.family() != addr2.family()) || + (addr.ToString() != addr2.ToString())) { + return false; + } + in_addr v4addr, v4addr2; + v4addr = addr.ipv4_address(); + v4addr2 = addr2.ipv4_address(); + if (0 != memcmp(&v4addr, &v4addr2, sizeof(v4addr))) { + return false; + } + in6_addr v6addr, v6addr2; + v6addr = addr.ipv6_address(); + v6addr2 = addr2.ipv6_address(); + if (0 != memcmp(&v6addr, &v6addr2, sizeof(v6addr))) { + return false; + } + return true; +} + +bool BrokenIPStringFails(const std::string& broken) { + IPAddress addr(0); // Intentionally make it v4. + if (IPFromString(kIPv4BrokenString1, &addr)) { + return false; + } + return addr.family() == AF_UNSPEC; +} + +bool CheckMaskCount(const std::string& mask, int expected_length) { + IPAddress addr; + return IPFromString(mask, &addr) && + (expected_length == CountIPMaskBits(addr)); +} + +bool TryInvalidMaskCount(const std::string& mask) { + // We don't care about the result at all, but we do want to know if + // CountIPMaskBits is going to crash or infinite loop or something. + IPAddress addr; + if (!IPFromString(mask, &addr)) { + return false; + } + CountIPMaskBits(addr); + return true; +} + +bool CheckTruncateIP(const std::string& initial, int truncate_length, + const std::string& expected_result) { + IPAddress addr, expected; + IPFromString(initial, &addr); + IPFromString(expected_result, &expected); + IPAddress truncated = TruncateIP(addr, truncate_length); + return truncated == expected; +} + +TEST(IPAddressTest, TestDefaultCtor) { + IPAddress addr; + EXPECT_FALSE(IPIsAny(addr)); + EXPECT_FALSE(IPIsLoopback(addr)); + EXPECT_FALSE(IPIsPrivate(addr)); + + EXPECT_EQ(0U, addr.Size()); + EXPECT_EQ(AF_UNSPEC, addr.family()); + EXPECT_EQ("", addr.ToString()); +} + +TEST(IPAddressTest, TestInAddrCtor) { + in_addr v4addr; + + // Test V4 Any address. + v4addr.s_addr = INADDR_ANY; + IPAddress addr(v4addr); + EXPECT_TRUE(IPIsAny(addr)); + EXPECT_FALSE(IPIsLoopback(addr)); + EXPECT_FALSE(IPIsPrivate(addr)); + EXPECT_EQ(kIPv4AddrSize, addr.Size()); + EXPECT_EQ(kIPv4AnyAddrString, addr.ToString()); + + // Test a V4 loopback address. + v4addr.s_addr = htonl(INADDR_LOOPBACK); + addr = IPAddress(v4addr); + EXPECT_FALSE(IPIsAny(addr)); + EXPECT_TRUE(IPIsLoopback(addr)); + EXPECT_TRUE(IPIsPrivate(addr)); + EXPECT_EQ(kIPv4AddrSize, addr.Size()); + EXPECT_EQ(kIPv4LoopbackAddrString, addr.ToString()); + + // Test an RFC1918 address. + v4addr.s_addr = htonl(kIPv4RFC1918Addr); + addr = IPAddress(v4addr); + EXPECT_FALSE(IPIsAny(addr)); + EXPECT_FALSE(IPIsLoopback(addr)); + EXPECT_TRUE(IPIsPrivate(addr)); + EXPECT_EQ(kIPv4AddrSize, addr.Size()); + EXPECT_EQ(kIPv4RFC1918AddrString, addr.ToString()); + + // Test a 'normal' v4 address. + v4addr.s_addr = htonl(kIPv4PublicAddr); + addr = IPAddress(v4addr); + EXPECT_FALSE(IPIsAny(addr)); + EXPECT_FALSE(IPIsLoopback(addr)); + EXPECT_FALSE(IPIsPrivate(addr)); + EXPECT_EQ(kIPv4AddrSize, addr.Size()); + EXPECT_EQ(kIPv4PublicAddrString, addr.ToString()); +} + +TEST(IPAddressTest, TestInAddr6Ctor) { + // Test v6 empty. + IPAddress addr(in6addr_any); + EXPECT_TRUE(IPIsAny(addr)); + EXPECT_FALSE(IPIsLoopback(addr)); + EXPECT_FALSE(IPIsPrivate(addr)); + EXPECT_EQ(kIPv6AddrSize, addr.Size()); + EXPECT_EQ(kIPv6AnyAddrString, addr.ToString()); + + // Test v6 loopback. + addr = IPAddress(in6addr_loopback); + EXPECT_FALSE(IPIsAny(addr)); + EXPECT_TRUE(IPIsLoopback(addr)); + EXPECT_TRUE(IPIsPrivate(addr)); + EXPECT_EQ(kIPv6AddrSize, addr.Size()); + EXPECT_EQ(kIPv6LoopbackAddrString, addr.ToString()); + + // Test v6 link-local. + addr = IPAddress(kIPv6LinkLocalAddr); + EXPECT_FALSE(IPIsAny(addr)); + EXPECT_FALSE(IPIsLoopback(addr)); + EXPECT_TRUE(IPIsPrivate(addr)); + EXPECT_EQ(kIPv6AddrSize, addr.Size()); + EXPECT_EQ(kIPv6LinkLocalAddrString, addr.ToString()); + + // Test v6 global address. + addr = IPAddress(kIPv6PublicAddr); + EXPECT_FALSE(IPIsAny(addr)); + EXPECT_FALSE(IPIsLoopback(addr)); + EXPECT_FALSE(IPIsPrivate(addr)); + EXPECT_EQ(kIPv6AddrSize, addr.Size()); + EXPECT_EQ(kIPv6PublicAddrString, addr.ToString()); +} + +TEST(IPAddressTest, TestUint32Ctor) { + // Test V4 Any address. + IPAddress addr(0); + EXPECT_TRUE(IPIsAny(addr)); + EXPECT_FALSE(IPIsLoopback(addr)); + EXPECT_FALSE(IPIsPrivate(addr)); + EXPECT_EQ(kIPv4AddrSize, addr.Size()); + EXPECT_EQ(kIPv4AnyAddrString, addr.ToString()); + + // Test a V4 loopback address. + addr = IPAddress(INADDR_LOOPBACK); + EXPECT_FALSE(IPIsAny(addr)); + EXPECT_TRUE(IPIsLoopback(addr)); + EXPECT_TRUE(IPIsPrivate(addr)); + EXPECT_EQ(kIPv4AddrSize, addr.Size()); + EXPECT_EQ(kIPv4LoopbackAddrString, addr.ToString()); + + // Test an RFC1918 address. + addr = IPAddress(kIPv4RFC1918Addr); + EXPECT_FALSE(IPIsAny(addr)); + EXPECT_FALSE(IPIsLoopback(addr)); + EXPECT_TRUE(IPIsPrivate(addr)); + EXPECT_EQ(kIPv4AddrSize, addr.Size()); + EXPECT_EQ(kIPv4RFC1918AddrString, addr.ToString()); + + // Test a 'normal' v4 address. + addr = IPAddress(kIPv4PublicAddr); + EXPECT_FALSE(IPIsAny(addr)); + EXPECT_FALSE(IPIsLoopback(addr)); + EXPECT_FALSE(IPIsPrivate(addr)); + EXPECT_EQ(kIPv4AddrSize, addr.Size()); + EXPECT_EQ(kIPv4PublicAddrString, addr.ToString()); +} + +TEST(IPAddressTest, TestCopyCtor) { + in_addr v4addr; + v4addr.s_addr = htonl(kIPv4PublicAddr); + IPAddress addr(v4addr); + IPAddress addr2(addr); + + EXPECT_PRED2(AreEqual, addr, addr2); + + addr = IPAddress(INADDR_ANY); + addr2 = IPAddress(addr); + EXPECT_PRED2(AreEqual, addr, addr2); + + addr = IPAddress(INADDR_LOOPBACK); + addr2 = IPAddress(addr); + EXPECT_PRED2(AreEqual, addr, addr2); + + addr = IPAddress(kIPv4PublicAddr); + addr2 = IPAddress(addr); + EXPECT_PRED2(AreEqual, addr, addr2); + + addr = IPAddress(kIPv4RFC1918Addr); + addr2 = IPAddress(addr); + EXPECT_PRED2(AreEqual, addr, addr2); + + addr = IPAddress(in6addr_any); + addr2 = IPAddress(addr); + EXPECT_PRED2(AreEqual, addr, addr2); + + addr = IPAddress(in6addr_loopback); + addr2 = IPAddress(addr); + EXPECT_PRED2(AreEqual, addr, addr2); + + addr = IPAddress(kIPv6LinkLocalAddr); + addr2 = IPAddress(addr); + EXPECT_PRED2(AreEqual, addr, addr2); + + addr = IPAddress(kIPv6PublicAddr); + addr2 = IPAddress(addr); + EXPECT_PRED2(AreEqual, addr, addr2); +} + +TEST(IPAddressTest, TestEquality) { + // Check v4 equality + in_addr v4addr, v4addr2; + v4addr.s_addr = htonl(kIPv4PublicAddr); + v4addr2.s_addr = htonl(kIPv4PublicAddr + 1); + IPAddress addr(v4addr); + IPAddress addr2(v4addr2); + IPAddress addr3(v4addr); + + EXPECT_TRUE(addr == addr); + EXPECT_TRUE(addr2 == addr2); + EXPECT_TRUE(addr3 == addr3); + EXPECT_TRUE(addr == addr3); + EXPECT_TRUE(addr3 == addr); + EXPECT_FALSE(addr2 == addr); + EXPECT_FALSE(addr2 == addr3); + EXPECT_FALSE(addr == addr2); + EXPECT_FALSE(addr3 == addr2); + + // Check v6 equality + IPAddress addr4(kIPv6PublicAddr); + IPAddress addr5(kIPv6LinkLocalAddr); + IPAddress addr6(kIPv6PublicAddr); + + EXPECT_TRUE(addr4 == addr4); + EXPECT_TRUE(addr5 == addr5); + EXPECT_TRUE(addr4 == addr6); + EXPECT_TRUE(addr6 == addr4); + EXPECT_FALSE(addr4 == addr5); + EXPECT_FALSE(addr5 == addr4); + EXPECT_FALSE(addr6 == addr5); + EXPECT_FALSE(addr5 == addr6); + + // Check v4/v6 cross-equality + EXPECT_FALSE(addr == addr4); + EXPECT_FALSE(addr == addr5); + EXPECT_FALSE(addr == addr6); + EXPECT_FALSE(addr4 == addr); + EXPECT_FALSE(addr5 == addr); + EXPECT_FALSE(addr6 == addr); + EXPECT_FALSE(addr2 == addr4); + EXPECT_FALSE(addr2 == addr5); + EXPECT_FALSE(addr2 == addr6); + EXPECT_FALSE(addr4 == addr2); + EXPECT_FALSE(addr5 == addr2); + EXPECT_FALSE(addr6 == addr2); + EXPECT_FALSE(addr3 == addr4); + EXPECT_FALSE(addr3 == addr5); + EXPECT_FALSE(addr3 == addr6); + EXPECT_FALSE(addr4 == addr3); + EXPECT_FALSE(addr5 == addr3); + EXPECT_FALSE(addr6 == addr3); + + // Special cases: loopback and any. + // They're special but they're still not equal. + IPAddress v4loopback(htonl(INADDR_LOOPBACK)); + IPAddress v6loopback(in6addr_loopback); + EXPECT_FALSE(v4loopback == v6loopback); + + IPAddress v4any(0); + IPAddress v6any(in6addr_any); + EXPECT_FALSE(v4any == v6any); +} + +TEST(IPAddressTest, TestComparison) { + // Defined in 'ascending' order. + // v6 > v4, and intra-family sorting is purely numerical + IPAddress addr0; // AF_UNSPEC + IPAddress addr1(INADDR_ANY); // 0.0.0.0 + IPAddress addr2(kIPv4PublicAddr); // 1.2.3.4 + IPAddress addr3(INADDR_LOOPBACK); // 127.0.0.1 + IPAddress addr4(kIPv4RFC1918Addr); // 192.168.7.1. + IPAddress addr5(in6addr_any); // :: + IPAddress addr6(in6addr_loopback); // ::1 + IPAddress addr7(kIPv6PublicAddr); // 2401.... + IPAddress addr8(kIPv6LinkLocalAddr); // fe80.... + + EXPECT_TRUE(addr0 < addr1); + EXPECT_TRUE(addr1 < addr2); + EXPECT_TRUE(addr2 < addr3); + EXPECT_TRUE(addr3 < addr4); + EXPECT_TRUE(addr4 < addr5); + EXPECT_TRUE(addr5 < addr6); + EXPECT_TRUE(addr6 < addr7); + EXPECT_TRUE(addr7 < addr8); + + EXPECT_FALSE(addr0 > addr1); + EXPECT_FALSE(addr1 > addr2); + EXPECT_FALSE(addr2 > addr3); + EXPECT_FALSE(addr3 > addr4); + EXPECT_FALSE(addr4 > addr5); + EXPECT_FALSE(addr5 > addr6); + EXPECT_FALSE(addr6 > addr7); + EXPECT_FALSE(addr7 > addr8); + + EXPECT_FALSE(addr0 > addr0); + EXPECT_FALSE(addr1 > addr1); + EXPECT_FALSE(addr2 > addr2); + EXPECT_FALSE(addr3 > addr3); + EXPECT_FALSE(addr4 > addr4); + EXPECT_FALSE(addr5 > addr5); + EXPECT_FALSE(addr6 > addr6); + EXPECT_FALSE(addr7 > addr7); + EXPECT_FALSE(addr8 > addr8); + + EXPECT_FALSE(addr0 < addr0); + EXPECT_FALSE(addr1 < addr1); + EXPECT_FALSE(addr2 < addr2); + EXPECT_FALSE(addr3 < addr3); + EXPECT_FALSE(addr4 < addr4); + EXPECT_FALSE(addr5 < addr5); + EXPECT_FALSE(addr6 < addr6); + EXPECT_FALSE(addr7 < addr7); + EXPECT_FALSE(addr8 < addr8); +} + +TEST(IPAddressTest, TestFromString) { + IPAddress addr; + IPAddress addr2; + addr2 = IPAddress(INADDR_ANY); + + EXPECT_TRUE(IPFromString(kIPv4AnyAddrString, &addr)); + EXPECT_EQ(addr.ToString(), kIPv4AnyAddrString); + EXPECT_PRED2(AreEqual, addr, addr2); + + addr2 = IPAddress(INADDR_LOOPBACK); + EXPECT_TRUE(IPFromString(kIPv4LoopbackAddrString, &addr)); + EXPECT_EQ(addr.ToString(), kIPv4LoopbackAddrString); + EXPECT_PRED2(AreEqual, addr, addr2); + + addr2 = IPAddress(kIPv4RFC1918Addr); + EXPECT_TRUE(IPFromString(kIPv4RFC1918AddrString, &addr)); + EXPECT_EQ(addr.ToString(), kIPv4RFC1918AddrString); + EXPECT_PRED2(AreEqual, addr, addr2); + + addr2 = IPAddress(kIPv4PublicAddr); + EXPECT_TRUE(IPFromString(kIPv4PublicAddrString, &addr)); + EXPECT_EQ(addr.ToString(), kIPv4PublicAddrString); + EXPECT_PRED2(AreEqual, addr, addr2); + + addr2 = IPAddress(in6addr_any); + EXPECT_TRUE(IPFromString(kIPv6AnyAddrString, &addr)); + EXPECT_EQ(addr.ToString(), kIPv6AnyAddrString); + EXPECT_PRED2(AreEqual, addr, addr2); + + addr2 = IPAddress(in6addr_loopback); + EXPECT_TRUE(IPFromString(kIPv6LoopbackAddrString, &addr)); + EXPECT_EQ(addr.ToString(), kIPv6LoopbackAddrString); + EXPECT_PRED2(AreEqual, addr, addr2); + + addr2 = IPAddress(kIPv6LinkLocalAddr); + EXPECT_TRUE(IPFromString(kIPv6LinkLocalAddrString, &addr)); + EXPECT_EQ(addr.ToString(), kIPv6LinkLocalAddrString); + EXPECT_PRED2(AreEqual, addr, addr2); + + addr2 = IPAddress(kIPv6PublicAddr); + EXPECT_TRUE(IPFromString(kIPv6PublicAddrString, &addr)); + EXPECT_EQ(addr.ToString(), kIPv6PublicAddrString); + EXPECT_PRED2(AreEqual, addr, addr2); + + addr2 = IPAddress(kIPv4MappedRFC1918Addr); + EXPECT_TRUE(IPFromString(kIPv4MappedV4StyleAddrString, &addr)); + EXPECT_PRED2(AreEqual, addr, addr2); + + // Broken cases, should set addr to AF_UNSPEC. + EXPECT_PRED1(BrokenIPStringFails, kIPv4BrokenString1); + EXPECT_PRED1(BrokenIPStringFails, kIPv4BrokenString2); + EXPECT_PRED1(BrokenIPStringFails, kIPv4BrokenString3); + EXPECT_PRED1(BrokenIPStringFails, kIPv4BrokenString4); + EXPECT_PRED1(BrokenIPStringFails, kIPv4BrokenString5); + EXPECT_PRED1(BrokenIPStringFails, kIPv4BrokenString6); + EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString1); + EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString2); + EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString3); + EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString4); + EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString5); + EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString6); + EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString7); + EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString8); + EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString9); + EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString10); + EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString11); + EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString12); + EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString13); + EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString14); +} + +TEST(IPAddressTest, TestIPFromAddrInfo) { + struct sockaddr_in expected4; + struct sockaddr_in6 expected6; + struct addrinfo test_info; + struct addrinfo next_info; + memset(&next_info, 'A', sizeof(next_info)); + test_info.ai_next = &next_info; + // Check that we can get an IPv4 address out. + test_info.ai_addr = reinterpret_cast(&expected4); + expected4.sin_addr.s_addr = HostToNetwork32(kIPv4PublicAddr); + expected4.sin_family = AF_INET; + IPAddress expected(kIPv4PublicAddr); + IPAddress addr; + EXPECT_TRUE(IPFromAddrInfo(&test_info, &addr)); + EXPECT_EQ(expected, addr); + // Check that we can get an IPv6 address out. + expected6.sin6_addr = kIPv6PublicAddr; + expected6.sin6_family = AF_INET6; + expected = IPAddress(kIPv6PublicAddr); + test_info.ai_addr = reinterpret_cast(&expected6); + EXPECT_TRUE(IPFromAddrInfo(&test_info, &addr)); + EXPECT_EQ(expected, addr); + // Check that unspec fails. + expected6.sin6_family = AF_UNSPEC; + EXPECT_FALSE(IPFromAddrInfo(&test_info, &addr)); + // Check a zeroed out addrinfo doesn't crash us. + memset(&next_info, 0, sizeof(next_info)); + EXPECT_FALSE(IPFromAddrInfo(&next_info, &addr)); +} + +TEST(IPAddressTest, TestIsPrivate) { + EXPECT_FALSE(IPIsPrivate(IPAddress(INADDR_ANY))); + EXPECT_FALSE(IPIsPrivate(IPAddress(kIPv4PublicAddr))); + EXPECT_FALSE(IPIsPrivate(IPAddress(in6addr_any))); + EXPECT_FALSE(IPIsPrivate(IPAddress(kIPv6PublicAddr))); + EXPECT_FALSE(IPIsPrivate(IPAddress(kIPv4MappedAnyAddr))); + EXPECT_FALSE(IPIsPrivate(IPAddress(kIPv4MappedPublicAddr))); + + EXPECT_TRUE(IPIsPrivate(IPAddress(kIPv4RFC1918Addr))); + EXPECT_TRUE(IPIsPrivate(IPAddress(INADDR_LOOPBACK))); + EXPECT_TRUE(IPIsPrivate(IPAddress(in6addr_loopback))); + EXPECT_TRUE(IPIsPrivate(IPAddress(kIPv6LinkLocalAddr))); +} + +TEST(IPAddressTest, TestIsLoopback) { + EXPECT_FALSE(IPIsLoopback(IPAddress(INADDR_ANY))); + EXPECT_FALSE(IPIsLoopback(IPAddress(kIPv4PublicAddr))); + EXPECT_FALSE(IPIsLoopback(IPAddress(in6addr_any))); + EXPECT_FALSE(IPIsLoopback(IPAddress(kIPv6PublicAddr))); + EXPECT_FALSE(IPIsLoopback(IPAddress(kIPv4MappedAnyAddr))); + EXPECT_FALSE(IPIsLoopback(IPAddress(kIPv4MappedPublicAddr))); + + EXPECT_TRUE(IPIsLoopback(IPAddress(INADDR_LOOPBACK))); + EXPECT_TRUE(IPIsLoopback(IPAddress(in6addr_loopback))); +} + +TEST(IPAddressTest, TestNormalized) { + // Check normalizing a ::ffff:a.b.c.d address. + IPAddress addr; + EXPECT_TRUE(IPFromString(kIPv4MappedV4StyleAddrString, &addr)); + IPAddress addr2(kIPv4RFC1918Addr); + addr = addr.Normalized(); + EXPECT_EQ(addr2, addr); + + // Check normalizing a ::ffff:aabb:ccdd address. + addr = IPAddress(kIPv4MappedPublicAddr); + addr2 = IPAddress(kIPv4PublicAddr); + addr = addr.Normalized(); + EXPECT_EQ(addr, addr2); + + // Check that a non-mapped v6 addresses isn't altered. + addr = IPAddress(kIPv6PublicAddr); + addr2 = IPAddress(kIPv6PublicAddr); + addr = addr.Normalized(); + EXPECT_EQ(addr, addr2); + + // Check that addresses that look a bit like mapped addresses aren't altered + EXPECT_TRUE(IPFromString("fe80::ffff:0102:0304", &addr)); + addr2 = addr; + addr = addr.Normalized(); + EXPECT_EQ(addr, addr2); + EXPECT_TRUE(IPFromString("::0102:0304", &addr)); + addr2 = addr; + addr = addr.Normalized(); + EXPECT_EQ(addr, addr2); + // This string should 'work' as an IP address but is not a mapped address, + // so it shouldn't change on normalization. + EXPECT_TRUE(IPFromString("::192.168.7.1", &addr)); + addr2 = addr; + addr = addr.Normalized(); + EXPECT_EQ(addr, addr2); + + // Check that v4 addresses aren't altered. + addr = IPAddress(htonl(kIPv4PublicAddr)); + addr2 = IPAddress(htonl(kIPv4PublicAddr)); + addr = addr.Normalized(); + EXPECT_EQ(addr, addr2); +} + +TEST(IPAddressTest, TestAsIPv6Address) { + IPAddress addr(kIPv4PublicAddr); + IPAddress addr2(kIPv4MappedPublicAddr); + addr = addr.AsIPv6Address(); + EXPECT_EQ(addr, addr2); + + addr = IPAddress(kIPv4MappedPublicAddr); + addr2 = IPAddress(kIPv4MappedPublicAddr); + addr = addr.AsIPv6Address(); + EXPECT_EQ(addr, addr2); + + addr = IPAddress(kIPv6PublicAddr); + addr2 = IPAddress(kIPv6PublicAddr); + addr = addr.AsIPv6Address(); + EXPECT_EQ(addr, addr2); +} + +TEST(IPAddressTest, TestCountIPMaskBits) { + IPAddress mask; + // IPv4 on byte boundaries + EXPECT_PRED2(CheckMaskCount, "255.255.255.255", 32); + EXPECT_PRED2(CheckMaskCount, "255.255.255.0", 24); + EXPECT_PRED2(CheckMaskCount, "255.255.0.0", 16); + EXPECT_PRED2(CheckMaskCount, "255.0.0.0", 8); + EXPECT_PRED2(CheckMaskCount, "0.0.0.0", 0); + + // IPv4 not on byte boundaries + EXPECT_PRED2(CheckMaskCount, "128.0.0.0", 1); + EXPECT_PRED2(CheckMaskCount, "224.0.0.0", 3); + EXPECT_PRED2(CheckMaskCount, "255.248.0.0", 13); + EXPECT_PRED2(CheckMaskCount, "255.255.224.0", 19); + EXPECT_PRED2(CheckMaskCount, "255.255.255.252", 30); + + // V6 on byte boundaries + EXPECT_PRED2(CheckMaskCount, "::", 0); + EXPECT_PRED2(CheckMaskCount, "ff00::", 8); + EXPECT_PRED2(CheckMaskCount, "ffff::", 16); + EXPECT_PRED2(CheckMaskCount, "ffff:ff00::", 24); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff::", 32); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ff00::", 40); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff::", 48); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ff00::", 56); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff::", 64); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ff00::", 72); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff::", 80); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ff00::", 88); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff::", 96); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ff00:0000", 104); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:0000", 112); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00", 120); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128); + + // V6 not on byte boundaries. + EXPECT_PRED2(CheckMaskCount, "8000::", 1); + EXPECT_PRED2(CheckMaskCount, "ff80::", 9); + EXPECT_PRED2(CheckMaskCount, "ffff:fe00::", 23); + EXPECT_PRED2(CheckMaskCount, "ffff:fffe::", 31); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:e000::", 35); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffe0::", 43); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:f800::", 53); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:fff8::", 61); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:fc00::", 70); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:fffc::", 78); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:8000::", 81); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ff80::", 89); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:fe00::", 103); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:fffe:0000", 111); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fc00", 118); + EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc", 126); + + // Non-contiguous ranges. These are invalid but lets test them + // to make sure they don't crash anything or infinite loop or something. + EXPECT_PRED1(TryInvalidMaskCount, "217.0.0.0"); + EXPECT_PRED1(TryInvalidMaskCount, "255.185.0.0"); + EXPECT_PRED1(TryInvalidMaskCount, "255.255.251.0"); + EXPECT_PRED1(TryInvalidMaskCount, "255.255.251.255"); + EXPECT_PRED1(TryInvalidMaskCount, "255.255.254.201"); + EXPECT_PRED1(TryInvalidMaskCount, "::1"); + EXPECT_PRED1(TryInvalidMaskCount, "fe80::1"); + EXPECT_PRED1(TryInvalidMaskCount, "ff80::1"); + EXPECT_PRED1(TryInvalidMaskCount, "ffff::1"); + EXPECT_PRED1(TryInvalidMaskCount, "ffff:ff00:1::1"); + EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff::ffff:1"); + EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ff00:1::"); + EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff::ff00"); + EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ff00:1234::"); + EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ffff:0012::ffff"); + EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ffff:ff01::"); + EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ffff:ffff:7f00::"); + EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ffff:ffff:ff7a::"); + EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:7f00:0000"); + EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ff70:0000"); + EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:0211"); + EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff7f"); +} + +TEST(IPAddressTest, TestTruncateIP) { + EXPECT_PRED3(CheckTruncateIP, "255.255.255.255", 24, "255.255.255.0"); + EXPECT_PRED3(CheckTruncateIP, "255.255.255.255", 16, "255.255.0.0"); + EXPECT_PRED3(CheckTruncateIP, "255.255.255.255", 8, "255.0.0.0"); + EXPECT_PRED3(CheckTruncateIP, "202.67.7.255", 24, "202.67.7.0"); + EXPECT_PRED3(CheckTruncateIP, "202.129.65.205", 16, "202.129.0.0"); + EXPECT_PRED3(CheckTruncateIP, "55.25.2.77", 8, "55.0.0.0"); + EXPECT_PRED3(CheckTruncateIP, "74.128.99.254", 1, "0.0.0.0"); + EXPECT_PRED3(CheckTruncateIP, "106.55.99.254", 3, "96.0.0.0"); + EXPECT_PRED3(CheckTruncateIP, "172.167.53.222", 13, "172.160.0.0"); + EXPECT_PRED3(CheckTruncateIP, "255.255.224.0", 18, "255.255.192.0"); + EXPECT_PRED3(CheckTruncateIP, "255.255.255.252", 28, "255.255.255.240"); + + EXPECT_PRED3(CheckTruncateIP, "fe80:1111:2222:3333:4444:5555:6666:7777", 1, + "8000::"); + EXPECT_PRED3(CheckTruncateIP, "fff0:1111:2222:3333:4444:5555:6666:7777", 9, + "ff80::"); + EXPECT_PRED3(CheckTruncateIP, "ffff:ff80:1111:2222:3333:4444:5555:6666", 23, + "ffff:fe00::"); + EXPECT_PRED3(CheckTruncateIP, "ffff:ff80:1111:2222:3333:4444:5555:6666", 32, + "ffff:ff80::"); + EXPECT_PRED3(CheckTruncateIP, "2400:f9af:e456:1111:2222:3333:4444:5555", 35, + "2400:f9af:e000::"); + EXPECT_PRED3(CheckTruncateIP, "9999:1111:2233:4444:5555:6666:7777:8888", 53, + "9999:1111:2233:4000::"); + EXPECT_PRED3(CheckTruncateIP, "9999:1111:2233:4567:5555:6666:7777:8888", 64, + "9999:1111:2233:4567::"); + EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 68, + "1111:2222:3333:4444:5000::"); + EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 92, + "1111:2222:3333:4444:5555:6660::"); + EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 96, + "1111:2222:3333:4444:5555:6666::"); + EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 105, + "1111:2222:3333:4444:5555:6666:7700::"); + EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 124, + "1111:2222:3333:4444:5555:6666:7777:8880"); + + // Slightly degenerate cases + EXPECT_PRED3(CheckTruncateIP, "202.165.33.127", 32, "202.165.33.127"); + EXPECT_PRED3(CheckTruncateIP, "235.105.77.12", 0, "0.0.0.0"); + EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 128, + "1111:2222:3333:4444:5555:6666:7777:8888"); + EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 0, + "::"); +} + +TEST(IPAddressTest, TestCategorizeIPv6) { + // Test determining if an IPAddress is 6Bone/6To4/Teredo/etc. + // IPv4 address, should be none of these (not even v4compat/v4mapped). + IPAddress v4_addr(kIPv4PublicAddr); + EXPECT_FALSE(IPIs6Bone(v4_addr)); + EXPECT_FALSE(IPIs6To4(v4_addr)); + EXPECT_FALSE(IPIsSiteLocal(v4_addr)); + EXPECT_FALSE(IPIsTeredo(v4_addr)); + EXPECT_FALSE(IPIsULA(v4_addr)); + EXPECT_FALSE(IPIsV4Compatibility(v4_addr)); + EXPECT_FALSE(IPIsV4Mapped(v4_addr)); + // Linklocal (fe80::/16) adddress; should be none of these. + IPAddress linklocal_addr(kIPv6LinkLocalAddr); + EXPECT_FALSE(IPIs6Bone(linklocal_addr)); + EXPECT_FALSE(IPIs6To4(linklocal_addr)); + EXPECT_FALSE(IPIsSiteLocal(linklocal_addr)); + EXPECT_FALSE(IPIsTeredo(linklocal_addr)); + EXPECT_FALSE(IPIsULA(linklocal_addr)); + EXPECT_FALSE(IPIsV4Compatibility(linklocal_addr)); + EXPECT_FALSE(IPIsV4Mapped(linklocal_addr)); + // 'Normal' IPv6 address, should also be none of these. + IPAddress normal_addr(kIPv6PublicAddr); + EXPECT_FALSE(IPIs6Bone(normal_addr)); + EXPECT_FALSE(IPIs6To4(normal_addr)); + EXPECT_FALSE(IPIsSiteLocal(normal_addr)); + EXPECT_FALSE(IPIsTeredo(normal_addr)); + EXPECT_FALSE(IPIsULA(normal_addr)); + EXPECT_FALSE(IPIsV4Compatibility(normal_addr)); + EXPECT_FALSE(IPIsV4Mapped(normal_addr)); + // IPv4 mapped address (::ffff:123.123.123.123) + IPAddress v4mapped_addr(kIPv4MappedPublicAddr); + EXPECT_TRUE(IPIsV4Mapped(v4mapped_addr)); + EXPECT_FALSE(IPIsV4Compatibility(v4mapped_addr)); + EXPECT_FALSE(IPIs6Bone(v4mapped_addr)); + EXPECT_FALSE(IPIs6To4(v4mapped_addr)); + EXPECT_FALSE(IPIsSiteLocal(v4mapped_addr)); + EXPECT_FALSE(IPIsTeredo(v4mapped_addr)); + EXPECT_FALSE(IPIsULA(v4mapped_addr)); + // IPv4 compatibility address (::123.123.123.123) + IPAddress v4compat_addr; + IPFromString("::192.168.7.1", &v4compat_addr); + EXPECT_TRUE(IPIsV4Compatibility(v4compat_addr)); + EXPECT_FALSE(IPIs6Bone(v4compat_addr)); + EXPECT_FALSE(IPIs6To4(v4compat_addr)); + EXPECT_FALSE(IPIsSiteLocal(v4compat_addr)); + EXPECT_FALSE(IPIsTeredo(v4compat_addr)); + EXPECT_FALSE(IPIsULA(v4compat_addr)); + EXPECT_FALSE(IPIsV4Mapped(v4compat_addr)); + // 6Bone address (3FFE::/16) + IPAddress sixbone_addr; + IPFromString("3FFE:123:456::789:123", &sixbone_addr); + EXPECT_TRUE(IPIs6Bone(sixbone_addr)); + EXPECT_FALSE(IPIs6To4(sixbone_addr)); + EXPECT_FALSE(IPIsSiteLocal(sixbone_addr)); + EXPECT_FALSE(IPIsTeredo(sixbone_addr)); + EXPECT_FALSE(IPIsULA(sixbone_addr)); + EXPECT_FALSE(IPIsV4Mapped(sixbone_addr)); + EXPECT_FALSE(IPIsV4Compatibility(sixbone_addr)); + // Unique Local Address (FC::/7) + IPAddress ula_addr; + IPFromString("FC00:123:456::789:123", &ula_addr); + EXPECT_TRUE(IPIsULA(ula_addr)); + EXPECT_FALSE(IPIs6Bone(ula_addr)); + EXPECT_FALSE(IPIs6To4(ula_addr)); + EXPECT_FALSE(IPIsSiteLocal(ula_addr)); + EXPECT_FALSE(IPIsTeredo(ula_addr)); + EXPECT_FALSE(IPIsV4Mapped(ula_addr)); + EXPECT_FALSE(IPIsV4Compatibility(ula_addr)); + // 6To4 Address (2002::/16) + IPAddress sixtofour_addr; + IPFromString("2002:123:456::789:123", &sixtofour_addr); + EXPECT_TRUE(IPIs6To4(sixtofour_addr)); + EXPECT_FALSE(IPIs6Bone(sixtofour_addr)); + EXPECT_FALSE(IPIsSiteLocal(sixtofour_addr)); + EXPECT_FALSE(IPIsTeredo(sixtofour_addr)); + EXPECT_FALSE(IPIsULA(sixtofour_addr)); + EXPECT_FALSE(IPIsV4Compatibility(sixtofour_addr)); + EXPECT_FALSE(IPIsV4Mapped(sixtofour_addr)); + // Site Local address (FEC0::/10) + IPAddress sitelocal_addr; + IPFromString("FEC0:123:456::789:123", &sitelocal_addr); + EXPECT_TRUE(IPIsSiteLocal(sitelocal_addr)); + EXPECT_FALSE(IPIs6Bone(sitelocal_addr)); + EXPECT_FALSE(IPIs6To4(sitelocal_addr)); + EXPECT_FALSE(IPIsTeredo(sitelocal_addr)); + EXPECT_FALSE(IPIsULA(sitelocal_addr)); + EXPECT_FALSE(IPIsV4Compatibility(sitelocal_addr)); + EXPECT_FALSE(IPIsV4Mapped(sitelocal_addr)); + // Teredo Address (2001:0000::/32) + IPAddress teredo_addr; + IPFromString("2001:0000:123:456::789:123", &teredo_addr); + EXPECT_TRUE(IPIsTeredo(teredo_addr)); + EXPECT_FALSE(IPIsSiteLocal(teredo_addr)); + EXPECT_FALSE(IPIs6Bone(teredo_addr)); + EXPECT_FALSE(IPIs6To4(teredo_addr)); + EXPECT_FALSE(IPIsULA(teredo_addr)); + EXPECT_FALSE(IPIsV4Compatibility(teredo_addr)); + EXPECT_FALSE(IPIsV4Mapped(teredo_addr)); +} + +TEST(IPAddressTest, TestToSensitiveString) { + IPAddress addr_v4 = IPAddress(kIPv4PublicAddr); + EXPECT_EQ(kIPv4PublicAddrString, addr_v4.ToString()); + EXPECT_EQ(kIPv4PublicAddrString, addr_v4.ToSensitiveString()); + IPAddress::set_strip_sensitive(true); + EXPECT_EQ(kIPv4PublicAddrString, addr_v4.ToString()); + EXPECT_EQ(kIPv4PublicAddrAnonymizedString, addr_v4.ToSensitiveString()); + IPAddress::set_strip_sensitive(false); + + IPAddress addr_v6 = IPAddress(kIPv6PublicAddr); + EXPECT_EQ(kIPv6PublicAddrString, addr_v6.ToString()); + EXPECT_EQ(kIPv6PublicAddrString, addr_v6.ToSensitiveString()); + IPAddress::set_strip_sensitive(true); + EXPECT_EQ(kIPv6PublicAddrString, addr_v6.ToString()); + EXPECT_EQ(kIPv6PublicAddrAnonymizedString, addr_v6.ToSensitiveString()); + IPAddress::set_strip_sensitive(false); +} + +TEST(IPAddressTest, TestInterfaceAddress) { + in6_addr addr; + InterfaceAddress addr1(kIPv6PublicAddr, + IPV6_ADDRESS_FLAG_TEMPORARY); + EXPECT_EQ(addr1.ipv6_flags(), IPV6_ADDRESS_FLAG_TEMPORARY); + EXPECT_EQ(addr1.family(), AF_INET6); + + addr = addr1.ipv6_address(); + EXPECT_TRUE(IN6_ARE_ADDR_EQUAL(&addr, &kIPv6PublicAddr)); + + InterfaceAddress addr2 = addr1; + EXPECT_EQ(addr1, addr2); + EXPECT_EQ(addr2.ipv6_flags(), IPV6_ADDRESS_FLAG_TEMPORARY); + addr = addr2.ipv6_address(); + EXPECT_TRUE(IN6_ARE_ADDR_EQUAL(&addr, &kIPv6PublicAddr)); + + InterfaceAddress addr3(addr1); + EXPECT_EQ(addr1, addr3); + EXPECT_EQ(addr3.ipv6_flags(), IPV6_ADDRESS_FLAG_TEMPORARY); + addr = addr3.ipv6_address(); + EXPECT_TRUE(IN6_ARE_ADDR_EQUAL(&addr, &kIPv6PublicAddr)); + + InterfaceAddress addr4(kIPv6PublicAddr, + IPV6_ADDRESS_FLAG_DEPRECATED); + EXPECT_NE(addr1, addr4); + + // When you compare them as IPAddress, since operator== + // is not virtual, it'll be equal. + IPAddress *paddr1 = &addr1; + IPAddress *paddr4 = &addr4; + EXPECT_EQ(*paddr1, *paddr4); + + InterfaceAddress addr5(kIPv6LinkLocalAddr, + IPV6_ADDRESS_FLAG_TEMPORARY); + EXPECT_NE(addr1, addr5); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/json.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/json.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/json.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/json.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,296 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/json.h" + +#include +#include +#include + +#include + +bool GetStringFromJson(const Json::Value& in, std::string* out) { + if (!in.isString()) { + std::ostringstream s; + if (in.isBool()) { + s << std::boolalpha << in.asBool(); + } else if (in.isInt()) { + s << in.asInt(); + } else if (in.isUInt()) { + s << in.asUInt(); + } else if (in.isDouble()) { + s << in.asDouble(); + } else { + return false; + } + *out = s.str(); + } else { + *out = in.asString(); + } + return true; +} + +bool GetIntFromJson(const Json::Value& in, int* out) { + bool ret; + if (!in.isString()) { + ret = in.isConvertibleTo(Json::intValue); + if (ret) { + *out = in.asInt(); + } + } else { + long val; // NOLINT + const char* c_str = in.asCString(); + char* end_ptr; + errno = 0; + val = strtol(c_str, &end_ptr, 10); // NOLINT + ret = (end_ptr != c_str && *end_ptr == '\0' && !errno && + val >= INT_MIN && val <= INT_MAX); + *out = val; + } + return ret; +} + +bool GetUIntFromJson(const Json::Value& in, unsigned int* out) { + bool ret; + if (!in.isString()) { + ret = in.isConvertibleTo(Json::uintValue); + if (ret) { + *out = in.asUInt(); + } + } else { + unsigned long val; // NOLINT + const char* c_str = in.asCString(); + char* end_ptr; + errno = 0; + val = strtoul(c_str, &end_ptr, 10); // NOLINT + ret = (end_ptr != c_str && *end_ptr == '\0' && !errno && + val <= UINT_MAX); + *out = val; + } + return ret; +} + +bool GetBoolFromJson(const Json::Value& in, bool* out) { + bool ret; + if (!in.isString()) { + ret = in.isConvertibleTo(Json::booleanValue); + if (ret) { + *out = in.asBool(); + } + } else { + if (in.asString() == "true") { + *out = true; + ret = true; + } else if (in.asString() == "false") { + *out = false; + ret = true; + } else { + ret = false; + } + } + return ret; +} + +bool GetDoubleFromJson(const Json::Value& in, double* out) { + bool ret; + if (!in.isString()) { + ret = in.isConvertibleTo(Json::realValue); + if (ret) { + *out = in.asDouble(); + } + } else { + double val; + const char* c_str = in.asCString(); + char* end_ptr; + errno = 0; + val = strtod(c_str, &end_ptr); + ret = (end_ptr != c_str && *end_ptr == '\0' && !errno); + *out = val; + } + return ret; +} + +namespace { +template +bool JsonArrayToVector(const Json::Value& value, + bool (*getter)(const Json::Value& in, T* out), + std::vector *vec) { + vec->clear(); + if (!value.isArray()) { + return false; + } + + for (Json::Value::ArrayIndex i = 0; i < value.size(); ++i) { + T val; + if (!getter(value[i], &val)) { + return false; + } + vec->push_back(val); + } + + return true; +} +// Trivial getter helper +bool GetValueFromJson(const Json::Value& in, Json::Value* out) { + *out = in; + return true; +} +} // unnamed namespace + +bool JsonArrayToValueVector(const Json::Value& in, + std::vector* out) { + return JsonArrayToVector(in, GetValueFromJson, out); +} + +bool JsonArrayToIntVector(const Json::Value& in, + std::vector* out) { + return JsonArrayToVector(in, GetIntFromJson, out); +} + +bool JsonArrayToUIntVector(const Json::Value& in, + std::vector* out) { + return JsonArrayToVector(in, GetUIntFromJson, out); +} + +bool JsonArrayToStringVector(const Json::Value& in, + std::vector* out) { + return JsonArrayToVector(in, GetStringFromJson, out); +} + +bool JsonArrayToBoolVector(const Json::Value& in, + std::vector* out) { + return JsonArrayToVector(in, GetBoolFromJson, out); +} + +bool JsonArrayToDoubleVector(const Json::Value& in, + std::vector* out) { + return JsonArrayToVector(in, GetDoubleFromJson, out); +} + +namespace { +template +Json::Value VectorToJsonArray(const std::vector& vec) { + Json::Value result(Json::arrayValue); + for (size_t i = 0; i < vec.size(); ++i) { + result.append(Json::Value(vec[i])); + } + return result; +} +} // unnamed namespace + +Json::Value ValueVectorToJsonArray(const std::vector& in) { + return VectorToJsonArray(in); +} + +Json::Value IntVectorToJsonArray(const std::vector& in) { + return VectorToJsonArray(in); +} + +Json::Value UIntVectorToJsonArray(const std::vector& in) { + return VectorToJsonArray(in); +} + +Json::Value StringVectorToJsonArray(const std::vector& in) { + return VectorToJsonArray(in); +} + +Json::Value BoolVectorToJsonArray(const std::vector& in) { + return VectorToJsonArray(in); +} + +Json::Value DoubleVectorToJsonArray(const std::vector& in) { + return VectorToJsonArray(in); +} + +bool GetValueFromJsonArray(const Json::Value& in, size_t n, + Json::Value* out) { + if (!in.isArray() || !in.isValidIndex(static_cast(n))) { + return false; + } + + *out = in[static_cast(n)]; + return true; +} + +bool GetIntFromJsonArray(const Json::Value& in, size_t n, + int* out) { + Json::Value x; + return GetValueFromJsonArray(in, n, &x) && GetIntFromJson(x, out); +} + +bool GetUIntFromJsonArray(const Json::Value& in, size_t n, + unsigned int* out) { + Json::Value x; + return GetValueFromJsonArray(in, n, &x) && GetUIntFromJson(x, out); +} + +bool GetStringFromJsonArray(const Json::Value& in, size_t n, + std::string* out) { + Json::Value x; + return GetValueFromJsonArray(in, n, &x) && GetStringFromJson(x, out); +} + +bool GetBoolFromJsonArray(const Json::Value& in, size_t n, + bool* out) { + Json::Value x; + return GetValueFromJsonArray(in, n, &x) && GetBoolFromJson(x, out); +} + +bool GetDoubleFromJsonArray(const Json::Value& in, size_t n, + double* out) { + Json::Value x; + return GetValueFromJsonArray(in, n, &x) && GetDoubleFromJson(x, out); +} + +bool GetValueFromJsonObject(const Json::Value& in, const std::string& k, + Json::Value* out) { + if (!in.isObject() || !in.isMember(k)) { + return false; + } + + *out = in[k]; + return true; +} + +bool GetIntFromJsonObject(const Json::Value& in, const std::string& k, + int* out) { + Json::Value x; + return GetValueFromJsonObject(in, k, &x) && GetIntFromJson(x, out); +} + +bool GetUIntFromJsonObject(const Json::Value& in, const std::string& k, + unsigned int* out) { + Json::Value x; + return GetValueFromJsonObject(in, k, &x) && GetUIntFromJson(x, out); +} + +bool GetStringFromJsonObject(const Json::Value& in, const std::string& k, + std::string* out) { + Json::Value x; + return GetValueFromJsonObject(in, k, &x) && GetStringFromJson(x, out); +} + +bool GetBoolFromJsonObject(const Json::Value& in, const std::string& k, + bool* out) { + Json::Value x; + return GetValueFromJsonObject(in, k, &x) && GetBoolFromJson(x, out); +} + +bool GetDoubleFromJsonObject(const Json::Value& in, const std::string& k, + double* out) { + Json::Value x; + return GetValueFromJsonObject(in, k, &x) && GetDoubleFromJson(x, out); +} + +std::string JsonValueToString(const Json::Value& json) { + Json::FastWriter w; + std::string value = w.write(json); + return value.substr(0, value.size() - 1); // trim trailing newline +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/json.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/json.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/json.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/json.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,89 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_JSON_H_ +#define WEBRTC_BASE_JSON_H_ + +#include +#include + +#if !defined(WEBRTC_EXTERNAL_JSON) +#include "json/json.h" +#else +#include "third_party/jsoncpp/json.h" +#endif + +// TODO: Move to rtc namespace + +/////////////////////////////////////////////////////////////////////////////// +// JSON Helpers +/////////////////////////////////////////////////////////////////////////////// + +// Robust conversion operators, better than the ones in JsonCpp. +bool GetIntFromJson(const Json::Value& in, int* out); +bool GetUIntFromJson(const Json::Value& in, unsigned int* out); +bool GetStringFromJson(const Json::Value& in, std::string* out); +bool GetBoolFromJson(const Json::Value& in, bool* out); +bool GetDoubleFromJson(const Json::Value& in, double* out); + +// Pull values out of a JSON array. +bool GetValueFromJsonArray(const Json::Value& in, size_t n, + Json::Value* out); +bool GetIntFromJsonArray(const Json::Value& in, size_t n, + int* out); +bool GetUIntFromJsonArray(const Json::Value& in, size_t n, + unsigned int* out); +bool GetStringFromJsonArray(const Json::Value& in, size_t n, + std::string* out); +bool GetBoolFromJsonArray(const Json::Value& in, size_t n, + bool* out); +bool GetDoubleFromJsonArray(const Json::Value& in, size_t n, + double* out); + +// Convert json arrays to std::vector +bool JsonArrayToValueVector(const Json::Value& in, + std::vector* out); +bool JsonArrayToIntVector(const Json::Value& in, + std::vector* out); +bool JsonArrayToUIntVector(const Json::Value& in, + std::vector* out); +bool JsonArrayToStringVector(const Json::Value& in, + std::vector* out); +bool JsonArrayToBoolVector(const Json::Value& in, + std::vector* out); +bool JsonArrayToDoubleVector(const Json::Value& in, + std::vector* out); + +// Convert std::vector to json array +Json::Value ValueVectorToJsonArray(const std::vector& in); +Json::Value IntVectorToJsonArray(const std::vector& in); +Json::Value UIntVectorToJsonArray(const std::vector& in); +Json::Value StringVectorToJsonArray(const std::vector& in); +Json::Value BoolVectorToJsonArray(const std::vector& in); +Json::Value DoubleVectorToJsonArray(const std::vector& in); + +// Pull values out of a JSON object. +bool GetValueFromJsonObject(const Json::Value& in, const std::string& k, + Json::Value* out); +bool GetIntFromJsonObject(const Json::Value& in, const std::string& k, + int* out); +bool GetUIntFromJsonObject(const Json::Value& in, const std::string& k, + unsigned int* out); +bool GetStringFromJsonObject(const Json::Value& in, const std::string& k, + std::string* out); +bool GetBoolFromJsonObject(const Json::Value& in, const std::string& k, + bool* out); +bool GetDoubleFromJsonObject(const Json::Value& in, const std::string& k, + double* out); + +// Writes out a Json value as a string. +std::string JsonValueToString(const Json::Value& json); + +#endif // WEBRTC_BASE_JSON_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/json_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/json_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/json_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/json_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,277 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include "webrtc/base/gunit.h" +#include "webrtc/base/json.h" + +static Json::Value in_s("foo"); +static Json::Value in_sn("99"); +static Json::Value in_si("-99"); +static Json::Value in_sb("true"); +static Json::Value in_sd("1.2"); +static Json::Value in_n(12); +static Json::Value in_i(-12); +static Json::Value in_u(34U); +static Json::Value in_b(true); +static Json::Value in_d(1.2); +static Json::Value big_sn("12345678901234567890"); +static Json::Value big_si("-12345678901234567890"); +static Json::Value big_u(0xFFFFFFFF); +static Json::Value bad_a(Json::arrayValue); +static Json::Value bad_o(Json::objectValue); + +TEST(JsonTest, GetString) { + std::string out; + EXPECT_TRUE(GetStringFromJson(in_s, &out)); + EXPECT_EQ("foo", out); + EXPECT_TRUE(GetStringFromJson(in_sn, &out)); + EXPECT_EQ("99", out); + EXPECT_TRUE(GetStringFromJson(in_si, &out)); + EXPECT_EQ("-99", out); + EXPECT_TRUE(GetStringFromJson(in_i, &out)); + EXPECT_EQ("-12", out); + EXPECT_TRUE(GetStringFromJson(in_n, &out)); + EXPECT_EQ("12", out); + EXPECT_TRUE(GetStringFromJson(in_u, &out)); + EXPECT_EQ("34", out); + EXPECT_TRUE(GetStringFromJson(in_b, &out)); + EXPECT_EQ("true", out); + // Not supported here yet. + EXPECT_FALSE(GetStringFromJson(bad_a, &out)); + EXPECT_FALSE(GetStringFromJson(bad_o, &out)); +} + +TEST(JsonTest, GetInt) { + int out; + EXPECT_TRUE(GetIntFromJson(in_sn, &out)); + EXPECT_EQ(99, out); + EXPECT_TRUE(GetIntFromJson(in_si, &out)); + EXPECT_EQ(-99, out); + EXPECT_TRUE(GetIntFromJson(in_n, &out)); + EXPECT_EQ(12, out); + EXPECT_TRUE(GetIntFromJson(in_i, &out)); + EXPECT_EQ(-12, out); + EXPECT_TRUE(GetIntFromJson(in_u, &out)); + EXPECT_EQ(34, out); + EXPECT_TRUE(GetIntFromJson(in_b, &out)); + EXPECT_EQ(1, out); + EXPECT_FALSE(GetIntFromJson(in_s, &out)); + EXPECT_FALSE(GetIntFromJson(big_sn, &out)); + EXPECT_FALSE(GetIntFromJson(big_si, &out)); + EXPECT_FALSE(GetIntFromJson(big_u, &out)); + EXPECT_FALSE(GetIntFromJson(bad_a, &out)); + EXPECT_FALSE(GetIntFromJson(bad_o, &out)); +} + +TEST(JsonTest, GetUInt) { + unsigned int out; + EXPECT_TRUE(GetUIntFromJson(in_sn, &out)); + EXPECT_EQ(99U, out); + EXPECT_TRUE(GetUIntFromJson(in_n, &out)); + EXPECT_EQ(12U, out); + EXPECT_TRUE(GetUIntFromJson(in_u, &out)); + EXPECT_EQ(34U, out); + EXPECT_TRUE(GetUIntFromJson(in_b, &out)); + EXPECT_EQ(1U, out); + EXPECT_TRUE(GetUIntFromJson(big_u, &out)); + EXPECT_EQ(0xFFFFFFFFU, out); + EXPECT_FALSE(GetUIntFromJson(in_s, &out)); + // TODO: Fail reading negative strings. + // EXPECT_FALSE(GetUIntFromJson(in_si, &out)); + EXPECT_FALSE(GetUIntFromJson(in_i, &out)); + EXPECT_FALSE(GetUIntFromJson(big_sn, &out)); + EXPECT_FALSE(GetUIntFromJson(big_si, &out)); + EXPECT_FALSE(GetUIntFromJson(bad_a, &out)); + EXPECT_FALSE(GetUIntFromJson(bad_o, &out)); +} + +TEST(JsonTest, GetBool) { + bool out; + EXPECT_TRUE(GetBoolFromJson(in_sb, &out)); + EXPECT_EQ(true, out); + EXPECT_TRUE(GetBoolFromJson(in_n, &out)); + EXPECT_EQ(true, out); + EXPECT_TRUE(GetBoolFromJson(in_i, &out)); + EXPECT_EQ(true, out); + EXPECT_TRUE(GetBoolFromJson(in_u, &out)); + EXPECT_EQ(true, out); + EXPECT_TRUE(GetBoolFromJson(in_b, &out)); + EXPECT_EQ(true, out); + EXPECT_TRUE(GetBoolFromJson(big_u, &out)); + EXPECT_EQ(true, out); + EXPECT_FALSE(GetBoolFromJson(in_s, &out)); + EXPECT_FALSE(GetBoolFromJson(in_sn, &out)); + EXPECT_FALSE(GetBoolFromJson(in_si, &out)); + EXPECT_FALSE(GetBoolFromJson(big_sn, &out)); + EXPECT_FALSE(GetBoolFromJson(big_si, &out)); + EXPECT_FALSE(GetBoolFromJson(bad_a, &out)); + EXPECT_FALSE(GetBoolFromJson(bad_o, &out)); +} + +TEST(JsonTest, GetDouble) { + double out; + EXPECT_TRUE(GetDoubleFromJson(in_sn, &out)); + EXPECT_EQ(99, out); + EXPECT_TRUE(GetDoubleFromJson(in_si, &out)); + EXPECT_EQ(-99, out); + EXPECT_TRUE(GetDoubleFromJson(in_sd, &out)); + EXPECT_EQ(1.2, out); + EXPECT_TRUE(GetDoubleFromJson(in_n, &out)); + EXPECT_EQ(12, out); + EXPECT_TRUE(GetDoubleFromJson(in_i, &out)); + EXPECT_EQ(-12, out); + EXPECT_TRUE(GetDoubleFromJson(in_u, &out)); + EXPECT_EQ(34, out); + EXPECT_TRUE(GetDoubleFromJson(in_b, &out)); + EXPECT_EQ(1, out); + EXPECT_TRUE(GetDoubleFromJson(in_d, &out)); + EXPECT_EQ(1.2, out); + EXPECT_FALSE(GetDoubleFromJson(in_s, &out)); +} + +TEST(JsonTest, GetFromArray) { + Json::Value a, out; + a.append(in_s); + a.append(in_i); + a.append(in_u); + a.append(in_b); + EXPECT_TRUE(GetValueFromJsonArray(a, 0, &out)); + EXPECT_TRUE(GetValueFromJsonArray(a, 3, &out)); + EXPECT_FALSE(GetValueFromJsonArray(a, 99, &out)); + EXPECT_FALSE(GetValueFromJsonArray(a, 0xFFFFFFFF, &out)); +} + +TEST(JsonTest, GetFromObject) { + Json::Value o, out; + o["string"] = in_s; + o["int"] = in_i; + o["uint"] = in_u; + o["bool"] = in_b; + EXPECT_TRUE(GetValueFromJsonObject(o, "int", &out)); + EXPECT_TRUE(GetValueFromJsonObject(o, "bool", &out)); + EXPECT_FALSE(GetValueFromJsonObject(o, "foo", &out)); + EXPECT_FALSE(GetValueFromJsonObject(o, "", &out)); +} + +namespace { +template +std::vector VecOf3(const T& a, const T& b, const T& c) { + std::vector in; + in.push_back(a); + in.push_back(b); + in.push_back(c); + return in; +} +template +Json::Value JsonVecOf3(const T& a, const T& b, const T& c) { + Json::Value in(Json::arrayValue); + in.append(a); + in.append(b); + in.append(c); + return in; +} +} // unnamed namespace + +TEST(JsonTest, ValueVectorToFromArray) { + std::vector in = VecOf3("a", "b", "c"); + Json::Value out = ValueVectorToJsonArray(in); + EXPECT_EQ(in.size(), out.size()); + for (Json::Value::ArrayIndex i = 0; i < in.size(); ++i) { + EXPECT_EQ(in[i].asString(), out[i].asString()); + } + Json::Value inj = JsonVecOf3("a", "b", "c"); + EXPECT_EQ(inj, out); + std::vector outj; + EXPECT_TRUE(JsonArrayToValueVector(inj, &outj)); + for (Json::Value::ArrayIndex i = 0; i < in.size(); i++) { + EXPECT_EQ(in[i], outj[i]); + } +} + +TEST(JsonTest, IntVectorToFromArray) { + std::vector in = VecOf3(1, 2, 3); + Json::Value out = IntVectorToJsonArray(in); + EXPECT_EQ(in.size(), out.size()); + for (Json::Value::ArrayIndex i = 0; i < in.size(); ++i) { + EXPECT_EQ(in[i], out[i].asInt()); + } + Json::Value inj = JsonVecOf3(1, 2, 3); + EXPECT_EQ(inj, out); + std::vector outj; + EXPECT_TRUE(JsonArrayToIntVector(inj, &outj)); + for (Json::Value::ArrayIndex i = 0; i < in.size(); i++) { + EXPECT_EQ(in[i], outj[i]); + } +} + +TEST(JsonTest, UIntVectorToFromArray) { + std::vector in = VecOf3(1, 2, 3); + Json::Value out = UIntVectorToJsonArray(in); + EXPECT_EQ(in.size(), out.size()); + for (Json::Value::ArrayIndex i = 0; i < in.size(); ++i) { + EXPECT_EQ(in[i], out[i].asUInt()); + } + Json::Value inj = JsonVecOf3(1, 2, 3); + EXPECT_EQ(inj, out); + std::vector outj; + EXPECT_TRUE(JsonArrayToUIntVector(inj, &outj)); + for (Json::Value::ArrayIndex i = 0; i < in.size(); i++) { + EXPECT_EQ(in[i], outj[i]); + } +} + +TEST(JsonTest, StringVectorToFromArray) { + std::vector in = VecOf3("a", "b", "c"); + Json::Value out = StringVectorToJsonArray(in); + EXPECT_EQ(in.size(), out.size()); + for (Json::Value::ArrayIndex i = 0; i < in.size(); ++i) { + EXPECT_EQ(in[i], out[i].asString()); + } + Json::Value inj = JsonVecOf3("a", "b", "c"); + EXPECT_EQ(inj, out); + std::vector outj; + EXPECT_TRUE(JsonArrayToStringVector(inj, &outj)); + for (Json::Value::ArrayIndex i = 0; i < in.size(); i++) { + EXPECT_EQ(in[i], outj[i]); + } +} + +TEST(JsonTest, BoolVectorToFromArray) { + std::vector in = VecOf3(false, true, false); + Json::Value out = BoolVectorToJsonArray(in); + EXPECT_EQ(in.size(), out.size()); + for (Json::Value::ArrayIndex i = 0; i < in.size(); ++i) { + EXPECT_EQ(in[i], out[i].asBool()); + } + Json::Value inj = JsonVecOf3(false, true, false); + EXPECT_EQ(inj, out); + std::vector outj; + EXPECT_TRUE(JsonArrayToBoolVector(inj, &outj)); + for (Json::Value::ArrayIndex i = 0; i < in.size(); i++) { + EXPECT_EQ(in[i], outj[i]); + } +} + +TEST(JsonTest, DoubleVectorToFromArray) { + std::vector in = VecOf3(1.0, 2.0, 3.0); + Json::Value out = DoubleVectorToJsonArray(in); + EXPECT_EQ(in.size(), out.size()); + for (Json::Value::ArrayIndex i = 0; i < in.size(); ++i) { + EXPECT_EQ(in[i], out[i].asDouble()); + } + Json::Value inj = JsonVecOf3(1.0, 2.0, 3.0); + EXPECT_EQ(inj, out); + std::vector outj; + EXPECT_TRUE(JsonArrayToDoubleVector(inj, &outj)); + for (Json::Value::ArrayIndex i = 0; i < in.size(); i++) { + EXPECT_EQ(in[i], outj[i]); + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,156 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/latebindingsymboltable.h" + +#if defined(WEBRTC_POSIX) +#include +#endif + +#include "webrtc/base/logging.h" + +namespace rtc { + +#if defined(WEBRTC_POSIX) +static const DllHandle kInvalidDllHandle = NULL; +#else +#error Not implemented +#endif + +static const char *GetDllError() { +#if defined(WEBRTC_POSIX) + const char *err = dlerror(); + if (err) { + return err; + } else { + return "No error"; + } +#else +#error Not implemented +#endif +} + +static bool LoadSymbol(DllHandle handle, + const char *symbol_name, + void **symbol) { +#if defined(WEBRTC_POSIX) + *symbol = dlsym(handle, symbol_name); + const char *err = dlerror(); + if (err) { + LOG(LS_ERROR) << "Error loading symbol " << symbol_name << ": " << err; + return false; + } else if (!*symbol) { + // ELF allows for symbols to be NULL, but that should never happen for our + // usage. + LOG(LS_ERROR) << "Symbol " << symbol_name << " is NULL"; + return false; + } + return true; +#else +#error Not implemented +#endif +} + +LateBindingSymbolTable::LateBindingSymbolTable(const TableInfo *info, + void **table) + : info_(info), + table_(table), + handle_(kInvalidDllHandle), + undefined_symbols_(false) { + ClearSymbols(); +} + +LateBindingSymbolTable::~LateBindingSymbolTable() { + Unload(); +} + +bool LateBindingSymbolTable::IsLoaded() const { + return handle_ != kInvalidDllHandle; +} + +bool LateBindingSymbolTable::Load() { + ASSERT(info_->dll_name != NULL); + return LoadFromPath(info_->dll_name); +} + +bool LateBindingSymbolTable::LoadFromPath(const char *dll_path) { + if (IsLoaded()) { + return true; + } + if (undefined_symbols_) { + // We do not attempt to load again because repeated attempts are not + // likely to succeed and DLL loading is costly. + LOG(LS_ERROR) << "We know there are undefined symbols"; + return false; + } + +#if defined(WEBRTC_POSIX) + handle_ = dlopen(dll_path, + // RTLD_NOW front-loads symbol resolution so that errors are + // caught early instead of causing a process abort later. + // RTLD_LOCAL prevents other modules from automatically + // seeing symbol definitions in the newly-loaded tree. This + // is necessary for same-named symbols in different ABI + // versions of the same library to not explode. + RTLD_NOW|RTLD_LOCAL +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) && defined(RTLD_DEEPBIND) + // RTLD_DEEPBIND makes symbol dependencies in the + // newly-loaded tree prefer to resolve to definitions within + // that tree (the default on OS X). This is necessary for + // same-named symbols in different ABI versions of the same + // library to not explode. + |RTLD_DEEPBIND +#endif + ); // NOLINT +#else +#error Not implemented +#endif + + if (handle_ == kInvalidDllHandle) { + LOG(LS_WARNING) << "Can't load " << dll_path << ": " + << GetDllError(); + return false; + } +#if defined(WEBRTC_POSIX) + // Clear any old errors. + dlerror(); +#endif + for (int i = 0; i < info_->num_symbols; ++i) { + if (!LoadSymbol(handle_, info_->symbol_names[i], &table_[i])) { + undefined_symbols_ = true; + Unload(); + return false; + } + } + return true; +} + +void LateBindingSymbolTable::Unload() { + if (!IsLoaded()) { + return; + } + +#if defined(WEBRTC_POSIX) + if (dlclose(handle_) != 0) { + LOG(LS_ERROR) << GetDllError(); + } +#else +#error Not implemented +#endif + + handle_ = kInvalidDllHandle; + ClearSymbols(); +} + +void LateBindingSymbolTable::ClearSymbols() { + memset(table_, 0, sizeof(void *) * info_->num_symbols); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable.cc.def thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable.cc.def --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable.cc.def 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable.cc.def 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,69 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file is a supermacro +// (see http://wanderinghorse.net/computing/papers/supermacros_cpp.html) to +// expand a definition of a late-binding symbol table class. +// +// Arguments: +// LATE_BINDING_SYMBOL_TABLE_CLASS_NAME: Name of the class to generate. +// LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST: List of symbols to load from the DLL, +// as an X-Macro list (see http://www.drdobbs.com/blogs/cpp/228700289). +// LATE_BINDING_SYMBOL_TABLE_DLL_NAME: String literal for the DLL file name to +// load. +// +// From a .cc file, include the header file containing your call to the .h.def +// supermacro, and then call this supermacro (optionally from inside the +// namespace for the class to generate, if any). Example: +// +// #include "myclassname.h" +// +// namespace foo { +// +// #define LATE_BINDING_SYMBOL_TABLE_CLASS_NAME MY_CLASS_NAME +// #define LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST MY_SYMBOLS_LIST +// #define LATE_BINDING_SYMBOL_TABLE_DLL_NAME "libdll.so.n" +// #include "webrtc/base/latebindingsymboltable.cc.def" +// +// } + +#ifndef LATE_BINDING_SYMBOL_TABLE_CLASS_NAME +#error You must define LATE_BINDING_SYMBOL_TABLE_CLASS_NAME +#endif + +#ifndef LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST +#error You must define LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST +#endif + +#ifndef LATE_BINDING_SYMBOL_TABLE_DLL_NAME +#error You must define LATE_BINDING_SYMBOL_TABLE_DLL_NAME +#endif + +#define X(sym) #sym, +const char* const LATE_BINDING_SYMBOL_TABLE_CLASS_NAME::kSymbolNames[] = { + LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST +}; +#undef X + +const ::rtc::LateBindingSymbolTable::TableInfo + LATE_BINDING_SYMBOL_TABLE_CLASS_NAME::kTableInfo = { + LATE_BINDING_SYMBOL_TABLE_DLL_NAME, + SYMBOL_TABLE_SIZE, + LATE_BINDING_SYMBOL_TABLE_CLASS_NAME::kSymbolNames +}; + +LATE_BINDING_SYMBOL_TABLE_CLASS_NAME::LATE_BINDING_SYMBOL_TABLE_CLASS_NAME() + : ::rtc::LateBindingSymbolTable(&kTableInfo, table_) {} + +LATE_BINDING_SYMBOL_TABLE_CLASS_NAME::~LATE_BINDING_SYMBOL_TABLE_CLASS_NAME() {} + +#undef LATE_BINDING_SYMBOL_TABLE_CLASS_NAME +#undef LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST +#undef LATE_BINDING_SYMBOL_TABLE_DLL_NAME diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,69 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_LATEBINDINGSYMBOLTABLE_H_ +#define WEBRTC_BASE_LATEBINDINGSYMBOLTABLE_H_ + +#include + +#include "webrtc/base/common.h" + +namespace rtc { + +#if defined(WEBRTC_POSIX) +typedef void *DllHandle; +#else +#error Not implemented for this platform +#endif + +// This is the base class for "symbol table" classes to simplify the dynamic +// loading of symbols from DLLs. Currently the implementation only supports +// Linux and OS X, and pure C symbols (or extern "C" symbols that wrap C++ +// functions). Sub-classes for specific DLLs are generated via the "supermacro" +// files latebindingsymboltable.h.def and latebindingsymboltable.cc.def. See +// talk/sound/pulseaudiosymboltable.(h|cc) for an example. +class LateBindingSymbolTable { + public: + struct TableInfo { + const char *dll_name; + int num_symbols; + // Array of size num_symbols. + const char *const *symbol_names; + }; + + LateBindingSymbolTable(const TableInfo *info, void **table); + ~LateBindingSymbolTable(); + + bool IsLoaded() const; + // Loads the DLL and the symbol table. Returns true iff the DLL and symbol + // table loaded successfully. + bool Load(); + // Like load, but allows overriding the dll path for when the dll path is + // dynamic. + bool LoadFromPath(const char *dll_path); + void Unload(); + + // Gets the raw OS handle to the DLL. Be careful what you do with it. + DllHandle GetDllHandle() const { return handle_; } + + private: + void ClearSymbols(); + + const TableInfo *info_; + void **table_; + DllHandle handle_; + bool undefined_symbols_; + + DISALLOW_COPY_AND_ASSIGN(LateBindingSymbolTable); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_LATEBINDINGSYMBOLTABLE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable.h.def thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable.h.def --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable.h.def 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable.h.def 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,83 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file is a supermacro +// (see http://wanderinghorse.net/computing/papers/supermacros_cpp.html) to +// expand a declaration of a late-binding symbol table class. +// +// Arguments: +// LATE_BINDING_SYMBOL_TABLE_CLASS_NAME: Name of the class to generate. +// LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST: List of symbols to load from the DLL, +// as an X-Macro list (see http://www.drdobbs.com/blogs/cpp/228700289). +// +// From a .h file, include the header(s) for the DLL to late-bind and the +// latebindingsymboltable.h header, and then call this supermacro (optionally +// from inside the namespace for the class to generate, if any). Example: +// +// #include +// +// #include "webrtc/base/latebindingsymboltable.h" +// +// namespace foo { +// +// #define MY_CLASS_NAME DesiredClassName +// #define MY_SYMBOLS_LIST X(acos) X(sin) X(tan) +// +// #define LATE_BINDING_SYMBOL_TABLE_CLASS_NAME MY_CLASS_NAME +// #define LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST MY_SYMBOLS_LIST +// #include "webrtc/base/latebindingsymboltable.h.def" +// +// } + +#ifndef WEBRTC_BASE_LATEBINDINGSYMBOLTABLE_H_ +#error You must first include latebindingsymboltable.h +#endif + +#ifndef LATE_BINDING_SYMBOL_TABLE_CLASS_NAME +#error You must define LATE_BINDING_SYMBOL_TABLE_CLASS_NAME +#endif + +#ifndef LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST +#error You must define LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST +#endif + +class LATE_BINDING_SYMBOL_TABLE_CLASS_NAME : + public ::rtc::LateBindingSymbolTable { + public: + LATE_BINDING_SYMBOL_TABLE_CLASS_NAME(); + ~LATE_BINDING_SYMBOL_TABLE_CLASS_NAME(); + +#define X(sym) \ + typeof(&::sym) sym() const { \ + ASSERT(::rtc::LateBindingSymbolTable::IsLoaded()); \ + return reinterpret_cast(table_[SYMBOL_TABLE_INDEX_##sym]); \ + } +LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST +#undef X + + private: + enum { +#define X(sym) \ + SYMBOL_TABLE_INDEX_##sym, +LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST +#undef X + SYMBOL_TABLE_SIZE + }; + + static const ::rtc::LateBindingSymbolTable::TableInfo kTableInfo; + static const char *const kSymbolNames[]; + + void *table_[SYMBOL_TABLE_SIZE]; + + DISALLOW_COPY_AND_ASSIGN(LATE_BINDING_SYMBOL_TABLE_CLASS_NAME); +}; + +#undef LATE_BINDING_SYMBOL_TABLE_CLASS_NAME +#undef LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/latebindingsymboltable_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,55 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) +#include +#endif + +#include "webrtc/base/gunit.h" +#include "webrtc/base/latebindingsymboltable.h" + +namespace rtc { + +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) + +#define LIBM_SYMBOLS_CLASS_NAME LibmTestSymbolTable +#define LIBM_SYMBOLS_LIST \ + X(acos) \ + X(sin) \ + X(tan) + +#define LATE_BINDING_SYMBOL_TABLE_CLASS_NAME LIBM_SYMBOLS_CLASS_NAME +#define LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST LIBM_SYMBOLS_LIST +#include "webrtc/base/latebindingsymboltable.h.def" + +#define LATE_BINDING_SYMBOL_TABLE_CLASS_NAME LIBM_SYMBOLS_CLASS_NAME +#define LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST LIBM_SYMBOLS_LIST +#define LATE_BINDING_SYMBOL_TABLE_DLL_NAME "libm.so.6" +#include "webrtc/base/latebindingsymboltable.cc.def" + +TEST(LateBindingSymbolTable, libm) { + LibmTestSymbolTable table; + EXPECT_FALSE(table.IsLoaded()); + ASSERT_TRUE(table.Load()); + EXPECT_TRUE(table.IsLoaded()); + EXPECT_EQ(table.acos()(0.5), acos(0.5)); + EXPECT_EQ(table.sin()(0.5), sin(0.5)); + EXPECT_EQ(table.tan()(0.5), tan(0.5)); + // It would be nice to check that the addresses are the same, but the nature + // of dynamic linking and relocation makes them actually be different. + table.Unload(); + EXPECT_FALSE(table.IsLoaded()); +} + +#else +#error Not implemented +#endif + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/libdbusglibsymboltable.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/libdbusglibsymboltable.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/libdbusglibsymboltable.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/libdbusglibsymboltable.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,24 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifdef HAVE_DBUS_GLIB + +#include "webrtc/base/libdbusglibsymboltable.h" + +namespace rtc { + +#define LATE_BINDING_SYMBOL_TABLE_CLASS_NAME LIBDBUS_GLIB_CLASS_NAME +#define LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST LIBDBUS_GLIB_SYMBOLS_LIST +#define LATE_BINDING_SYMBOL_TABLE_DLL_NAME "libdbus-glib-1.so.2" +#include "webrtc/base/latebindingsymboltable.cc.def" + +} // namespace rtc + +#endif // HAVE_DBUS_GLIB diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/libdbusglibsymboltable.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/libdbusglibsymboltable.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/libdbusglibsymboltable.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/libdbusglibsymboltable.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_LIBDBUSGLIBSYMBOLTABLE_H_ +#define WEBRTC_BASE_LIBDBUSGLIBSYMBOLTABLE_H_ + +#ifdef HAVE_DBUS_GLIB + +#include +#include + +#include "webrtc/base/latebindingsymboltable.h" + +namespace rtc { + +#define LIBDBUS_GLIB_CLASS_NAME LibDBusGlibSymbolTable +// The libdbus-glib symbols we need, as an X-Macro list. +// This list must contain precisely every libdbus-glib function that is used in +// dbus.cc. +#define LIBDBUS_GLIB_SYMBOLS_LIST \ + X(dbus_bus_add_match) \ + X(dbus_connection_add_filter) \ + X(dbus_connection_close) \ + X(dbus_connection_remove_filter) \ + X(dbus_connection_set_exit_on_disconnect) \ + X(dbus_g_bus_get) \ + X(dbus_g_bus_get_private) \ + X(dbus_g_connection_get_connection) \ + X(dbus_g_connection_unref) \ + X(dbus_g_thread_init) \ + X(dbus_message_get_interface) \ + X(dbus_message_get_member) \ + X(dbus_message_get_path) \ + X(dbus_message_get_type) \ + X(dbus_message_iter_get_arg_type) \ + X(dbus_message_iter_get_basic) \ + X(dbus_message_iter_init) \ + X(dbus_message_ref) \ + X(dbus_message_unref) + +#define LATE_BINDING_SYMBOL_TABLE_CLASS_NAME LIBDBUS_GLIB_CLASS_NAME +#define LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST LIBDBUS_GLIB_SYMBOLS_LIST +#include "webrtc/base/latebindingsymboltable.h.def" + +} // namespace rtc + +#endif // HAVE_DBUS_GLIB + +#endif // WEBRTC_BASE_LIBDBUSGLIBSYMBOLTABLE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/linked_ptr.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/linked_ptr.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/linked_ptr.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/linked_ptr.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,125 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * linked_ptr - simple reference linked pointer + * (like reference counting, just using a linked list of the references + * instead of their count.) + * + * The implementation stores three pointers for every linked_ptr, but + * does not allocate anything on the free store. + */ + +#ifndef WEBRTC_BASE_LINKED_PTR_H__ +#define WEBRTC_BASE_LINKED_PTR_H__ + +namespace rtc { + +/* For ANSI-challenged compilers, you may want to #define + * NO_MEMBER_TEMPLATES, explicit or mutable */ +#define NO_MEMBER_TEMPLATES + +template class linked_ptr +{ +public: + +#ifndef NO_MEMBER_TEMPLATES +# define TEMPLATE_FUNCTION template + TEMPLATE_FUNCTION friend class linked_ptr; +#else +# define TEMPLATE_FUNCTION + typedef X Y; +#endif + + typedef X element_type; + + explicit linked_ptr(X* p = 0) throw() + : itsPtr(p) {itsPrev = itsNext = this;} + ~linked_ptr() + {release();} + linked_ptr(const linked_ptr& r) throw() + {acquire(r);} + linked_ptr& operator=(const linked_ptr& r) + { + if (this != &r) { + release(); + acquire(r); + } + return *this; + } + +#ifndef NO_MEMBER_TEMPLATES + template friend class linked_ptr; + template linked_ptr(const linked_ptr& r) throw() + {acquire(r);} + template linked_ptr& operator=(const linked_ptr& r) + { + if (this != &r) { + release(); + acquire(r); + } + return *this; + } +#endif // NO_MEMBER_TEMPLATES + + X& operator*() const throw() {return *itsPtr;} + X* operator->() const throw() {return itsPtr;} + X* get() const throw() {return itsPtr;} + bool unique() const throw() {return itsPrev ? itsPrev==this : true;} + +private: + X* itsPtr; + mutable const linked_ptr* itsPrev; + mutable const linked_ptr* itsNext; + + void acquire(const linked_ptr& r) throw() + { // insert this to the list + itsPtr = r.itsPtr; + itsNext = r.itsNext; + itsNext->itsPrev = this; + itsPrev = &r; +#ifndef mutable + r.itsNext = this; +#else // for ANSI-challenged compilers + (const_cast*>(&r))->itsNext = this; +#endif + } + +#ifndef NO_MEMBER_TEMPLATES + template void acquire(const linked_ptr& r) throw() + { // insert this to the list + itsPtr = r.itsPtr; + itsNext = r.itsNext; + itsNext->itsPrev = this; + itsPrev = &r; +#ifndef mutable + r.itsNext = this; +#else // for ANSI-challenged compilers + (const_cast*>(&r))->itsNext = this; +#endif + } +#endif // NO_MEMBER_TEMPLATES + + void release() + { // erase this from the list, delete if unique + if (unique()) delete itsPtr; + else { + itsPrev->itsNext = itsNext; + itsNext->itsPrev = itsPrev; + itsPrev = itsNext = 0; + } + itsPtr = 0; + } +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_LINKED_PTR_H__ + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/linux.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/linux.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/linux.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/linux.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,348 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if defined(WEBRTC_LINUX) +#include "webrtc/base/linux.h" + +#include + +#include +#include +#include + +#include +#include + +#include "webrtc/base/stringencode.h" + +namespace rtc { + +static const char kCpuInfoFile[] = "/proc/cpuinfo"; +static const char kCpuMaxFreqFile[] = + "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"; + +ProcCpuInfo::ProcCpuInfo() { +} + +ProcCpuInfo::~ProcCpuInfo() { +} + +bool ProcCpuInfo::LoadFromSystem() { + ConfigParser procfs; + if (!procfs.Open(kCpuInfoFile)) { + return false; + } + return procfs.Parse(§ions_); +}; + +bool ProcCpuInfo::GetSectionCount(size_t* count) { + if (sections_.empty()) { + return false; + } + if (count) { + *count = sections_.size(); + } + return true; +} + +bool ProcCpuInfo::GetNumCpus(int* num) { + if (sections_.empty()) { + return false; + } + int total_cpus = 0; +#if defined(__arm__) + // Count the number of blocks that have a "processor" key defined. On ARM, + // there may be extra blocks of information that aren't per-processor. + size_t section_count = sections_.size(); + for (size_t i = 0; i < section_count; ++i) { + int processor_id; + if (GetSectionIntValue(i, "processor", &processor_id)) { + ++total_cpus; + } + } + // Single core ARM systems don't include "processor" keys at all, so return + // that we have a single core if we didn't find any explicitly above. + if (total_cpus == 0) { + total_cpus = 1; + } +#else + // On X86, there is exactly one info section per processor. + total_cpus = static_cast(sections_.size()); +#endif + if (num) { + *num = total_cpus; + } + return true; +} + +bool ProcCpuInfo::GetNumPhysicalCpus(int* num) { + if (sections_.empty()) { + return false; + } + // TODO: /proc/cpuinfo only reports cores that are currently + // _online_, so this may underreport the number of physical cores. +#if defined(__arm__) + // ARM (currently) has no hyperthreading, so just return the same value + // as GetNumCpus. + return GetNumCpus(num); +#else + int total_cores = 0; + std::set physical_ids; + size_t section_count = sections_.size(); + for (size_t i = 0; i < section_count; ++i) { + int physical_id; + int cores; + // Count the cores for the physical id only if we have not counted the id. + if (GetSectionIntValue(i, "physical id", &physical_id) && + GetSectionIntValue(i, "cpu cores", &cores) && + physical_ids.find(physical_id) == physical_ids.end()) { + physical_ids.insert(physical_id); + total_cores += cores; + } + } + + if (num) { + *num = total_cores; + } + return true; +#endif +} + +bool ProcCpuInfo::GetCpuFamily(int* id) { + int cpu_family = 0; + +#if defined(__arm__) + // On some ARM platforms, there is no 'cpu family' in '/proc/cpuinfo'. But + // there is 'CPU Architecture' which can be used as 'cpu family'. + // See http://en.wikipedia.org/wiki/ARM_architecture for a good list of + // ARM cpu families, architectures, and their mappings. + // There may be multiple sessions that aren't per-processor. We need to scan + // through each session until we find the first 'CPU architecture'. + size_t section_count = sections_.size(); + for (size_t i = 0; i < section_count; ++i) { + if (GetSectionIntValue(i, "CPU architecture", &cpu_family)) { + // We returns the first one (if there are multiple entries). + break; + }; + } +#else + GetSectionIntValue(0, "cpu family", &cpu_family); +#endif + if (id) { + *id = cpu_family; + } + return true; +} + +bool ProcCpuInfo::GetSectionStringValue(size_t section_num, + const std::string& key, + std::string* result) { + if (section_num >= sections_.size()) { + return false; + } + ConfigParser::SimpleMap::iterator iter = sections_[section_num].find(key); + if (iter == sections_[section_num].end()) { + return false; + } + *result = iter->second; + return true; +} + +bool ProcCpuInfo::GetSectionIntValue(size_t section_num, + const std::string& key, + int* result) { + if (section_num >= sections_.size()) { + return false; + } + ConfigParser::SimpleMap::iterator iter = sections_[section_num].find(key); + if (iter == sections_[section_num].end()) { + return false; + } + return FromString(iter->second, result); +} + +ConfigParser::ConfigParser() {} + +ConfigParser::~ConfigParser() {} + +bool ConfigParser::Open(const std::string& filename) { + FileStream* fs = new FileStream(); + if (!fs->Open(filename, "r", NULL)) { + return false; + } + instream_.reset(fs); + return true; +} + +void ConfigParser::Attach(StreamInterface* stream) { + instream_.reset(stream); +} + +bool ConfigParser::Parse(MapVector* key_val_pairs) { + // Parses the file and places the found key-value pairs into key_val_pairs. + SimpleMap section; + while (ParseSection(§ion)) { + key_val_pairs->push_back(section); + section.clear(); + } + return (!key_val_pairs->empty()); +} + +bool ConfigParser::ParseSection(SimpleMap* key_val_pair) { + // Parses the next section in the filestream and places the found key-value + // pairs into key_val_pair. + std::string key, value; + while (ParseLine(&key, &value)) { + (*key_val_pair)[key] = value; + } + return (!key_val_pair->empty()); +} + +bool ConfigParser::ParseLine(std::string* key, std::string* value) { + // Parses the next line in the filestream and places the found key-value + // pair into key and val. + std::string line; + if ((instream_->ReadLine(&line)) == SR_EOS) { + return false; + } + std::vector tokens; + if (2 != split(line, ':', &tokens)) { + return false; + } + // Removes whitespace at the end of Key name + size_t pos = tokens[0].length() - 1; + while ((pos > 0) && isspace(tokens[0][pos])) { + pos--; + } + tokens[0].erase(pos + 1); + // Removes whitespace at the start of value + pos = 0; + while (pos < tokens[1].length() && isspace(tokens[1][pos])) { + pos++; + } + tokens[1].erase(0, pos); + *key = tokens[0]; + *value = tokens[1]; + return true; +} + +#if !defined(WEBRTC_CHROMIUM_BUILD) +static bool ExpectLineFromStream(FileStream* stream, + std::string* out) { + StreamResult res = stream->ReadLine(out); + if (res != SR_SUCCESS) { + if (res != SR_EOS) { + LOG(LS_ERROR) << "Error when reading from stream"; + } else { + LOG(LS_ERROR) << "Incorrect number of lines in stream"; + } + return false; + } + return true; +} + +static void ExpectEofFromStream(FileStream* stream) { + std::string unused; + StreamResult res = stream->ReadLine(&unused); + if (res == SR_SUCCESS) { + LOG(LS_WARNING) << "Ignoring unexpected extra lines from stream"; + } else if (res != SR_EOS) { + LOG(LS_WARNING) << "Error when checking for extra lines from stream"; + } +} + +// For caching the lsb_release output (reading it invokes a sub-process and +// hence is somewhat expensive). +static std::string lsb_release_string; +static CriticalSection lsb_release_string_critsec; + +std::string ReadLinuxLsbRelease() { + CritScope cs(&lsb_release_string_critsec); + if (!lsb_release_string.empty()) { + // Have cached result from previous call. + return lsb_release_string; + } + // No cached result. Run lsb_release and parse output. + POpenStream lsb_release_output; + if (!lsb_release_output.Open("lsb_release -idrcs", "r", NULL)) { + LOG_ERR(LS_ERROR) << "Can't run lsb_release"; + return lsb_release_string; // empty + } + // Read in the command's output and build the string. + std::ostringstream sstr; + std::string line; + int wait_status; + + if (!ExpectLineFromStream(&lsb_release_output, &line)) { + return lsb_release_string; // empty + } + sstr << "DISTRIB_ID=" << line; + + if (!ExpectLineFromStream(&lsb_release_output, &line)) { + return lsb_release_string; // empty + } + sstr << " DISTRIB_DESCRIPTION=\"" << line << '"'; + + if (!ExpectLineFromStream(&lsb_release_output, &line)) { + return lsb_release_string; // empty + } + sstr << " DISTRIB_RELEASE=" << line; + + if (!ExpectLineFromStream(&lsb_release_output, &line)) { + return lsb_release_string; // empty + } + sstr << " DISTRIB_CODENAME=" << line; + + // Should not be anything left. + ExpectEofFromStream(&lsb_release_output); + + lsb_release_output.Close(); + wait_status = lsb_release_output.GetWaitStatus(); + if (wait_status == -1 || + !WIFEXITED(wait_status) || + WEXITSTATUS(wait_status) != 0) { + LOG(LS_WARNING) << "Unexpected exit status from lsb_release"; + } + + lsb_release_string = sstr.str(); + + return lsb_release_string; +} +#endif + +std::string ReadLinuxUname() { + struct utsname buf; + if (uname(&buf) < 0) { + LOG_ERR(LS_ERROR) << "Can't call uname()"; + return std::string(); + } + std::ostringstream sstr; + sstr << buf.sysname << " " + << buf.release << " " + << buf.version << " " + << buf.machine; + return sstr.str(); +} + +int ReadCpuMaxFreq() { + FileStream fs; + std::string str; + int freq = -1; + if (!fs.Open(kCpuMaxFreqFile, "r", NULL) || + SR_SUCCESS != fs.ReadLine(&str) || + !FromString(str, &freq)) { + return -1; + } + return freq; +} + +} // namespace rtc + +#endif // defined(WEBRTC_LINUX) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/linuxfdwalk.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/linuxfdwalk.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/linuxfdwalk.c 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/linuxfdwalk.c 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,81 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include +#include +#include +#include + +#include "webrtc/base/linuxfdwalk.h" + +// Parses a file descriptor number in base 10, requiring the strict format used +// in /proc/*/fd. Returns the value, or -1 if not a valid string. +static int parse_fd(const char *s) { + if (!*s) { + // Empty string is invalid. + return -1; + } + int val = 0; + do { + if (*s < '0' || *s > '9') { + // Non-numeric characters anywhere are invalid. + return -1; + } + int digit = *s++ - '0'; + val = val * 10 + digit; + } while (*s); + return val; +} + +int fdwalk(void (*func)(void *, int), void *opaque) { + DIR *dir = opendir("/proc/self/fd"); + if (!dir) { + return -1; + } + int opendirfd = dirfd(dir); + int parse_errors = 0; + struct dirent *ent; + // Have to clear errno to distinguish readdir() completion from failure. + while (errno = 0, (ent = readdir(dir)) != NULL) { + if (strcmp(ent->d_name, ".") == 0 || + strcmp(ent->d_name, "..") == 0) { + continue; + } + // We avoid atoi or strtol because those are part of libc and they involve + // locale stuff, which is probably not safe from a post-fork context in a + // multi-threaded app. + int fd = parse_fd(ent->d_name); + if (fd < 0) { + parse_errors = 1; + continue; + } + if (fd != opendirfd) { + (*func)(opaque, fd); + } + } + int saved_errno = errno; + if (closedir(dir) < 0) { + if (!saved_errno) { + // Return the closedir error. + return -1; + } + // Else ignore it because we have a more relevant error to return. + } + if (saved_errno) { + errno = saved_errno; + return -1; + } else if (parse_errors) { + errno = EBADF; + return -1; + } else { + return 0; + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/linuxfdwalk.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/linuxfdwalk.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/linuxfdwalk.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/linuxfdwalk.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_LINUXFDWALK_H_ +#define WEBRTC_BASE_LINUXFDWALK_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// Linux port of SunOS's fdwalk(3) call. It loops over all open file descriptors +// and calls func on each one. Additionally, it is safe to use from the child +// of a fork that hasn't exec'ed yet, so you can use it to close all open file +// descriptors prior to exec'ing a daemon. +// The return value is 0 if successful, or else -1 and errno is set. The +// possible errors include any error that can be returned by opendir(), +// readdir(), or closedir(), plus EBADF if there are problems parsing the +// contents of /proc/self/fd. +// The file descriptors that are enumerated will not include the file descriptor +// used for the enumeration itself. +int fdwalk(void (*func)(void *, int), void *opaque); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBRTC_BASE_LINUXFDWALK_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/linuxfdwalk_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/linuxfdwalk_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/linuxfdwalk_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/linuxfdwalk_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,75 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include + +#include "webrtc/base/gunit.h" +#include "webrtc/base/linuxfdwalk.h" + +#include +#include +#include +#include + +static const int kArbitraryLargeFdNumber = 424; + +static void FdCheckVisitor(void *data, int fd) { + std::set *fds = static_cast *>(data); + EXPECT_EQ(1U, fds->erase(fd)); +} + +static void FdEnumVisitor(void *data, int fd) { + std::set *fds = static_cast *>(data); + EXPECT_TRUE(fds->insert(fd).second); +} + +// Checks that the set of open fds is exactly the given list. +static void CheckOpenFdList(std::set fds) { + EXPECT_EQ(0, fdwalk(&FdCheckVisitor, &fds)); + EXPECT_EQ(0U, fds.size()); +} + +static void GetOpenFdList(std::set *fds) { + fds->clear(); + EXPECT_EQ(0, fdwalk(&FdEnumVisitor, fds)); +} + +TEST(LinuxFdWalk, TestFdWalk) { + std::set fds; + GetOpenFdList(&fds); + std::ostringstream str; + // I have observed that the open set when starting a test is [0, 6]. Leaked + // fds would change that, but so can (e.g.) running under a debugger, so we + // can't really do an EXPECT. :( + str << "File descriptors open in test executable:"; + for (std::set::const_iterator i = fds.begin(); i != fds.end(); ++i) { + str << " " << *i; + } + LOG(LS_INFO) << str.str(); + // Open some files. + int fd1 = open("/dev/null", O_RDONLY); + EXPECT_LE(0, fd1); + int fd2 = open("/dev/null", O_WRONLY); + EXPECT_LE(0, fd2); + int fd3 = open("/dev/null", O_RDWR); + EXPECT_LE(0, fd3); + int fd4 = dup2(fd3, kArbitraryLargeFdNumber); + EXPECT_LE(0, fd4); + EXPECT_TRUE(fds.insert(fd1).second); + EXPECT_TRUE(fds.insert(fd2).second); + EXPECT_TRUE(fds.insert(fd3).second); + EXPECT_TRUE(fds.insert(fd4).second); + CheckOpenFdList(fds); + EXPECT_EQ(0, close(fd1)); + EXPECT_EQ(0, close(fd2)); + EXPECT_EQ(0, close(fd3)); + EXPECT_EQ(0, close(fd4)); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/linux.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/linux.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/linux.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/linux.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,123 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_LINUX_H_ +#define WEBRTC_BASE_LINUX_H_ + +#if defined(WEBRTC_LINUX) +#include +#include +#include + +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/stream.h" + +namespace rtc { + +////////////////////////////////////////////////////////////////////////////// +// ConfigParser parses a FileStream of an ".ini."-type format into a map. +////////////////////////////////////////////////////////////////////////////// + +// Sample Usage: +// ConfigParser parser; +// ConfigParser::MapVector key_val_pairs; +// if (parser.Open(inifile) && parser.Parse(&key_val_pairs)) { +// for (int section_num=0; i < key_val_pairs.size(); ++section_num) { +// std::string val1 = key_val_pairs[section_num][key1]; +// std::string val2 = key_val_pairs[section_num][key2]; +// // Do something with valn; +// } +// } + +class ConfigParser { + public: + typedef std::map SimpleMap; + typedef std::vector MapVector; + + ConfigParser(); + virtual ~ConfigParser(); + + virtual bool Open(const std::string& filename); + virtual void Attach(StreamInterface* stream); + virtual bool Parse(MapVector* key_val_pairs); + virtual bool ParseSection(SimpleMap* key_val_pair); + virtual bool ParseLine(std::string* key, std::string* value); + + private: + scoped_ptr instream_; +}; + +////////////////////////////////////////////////////////////////////////////// +// ProcCpuInfo reads CPU info from the /proc subsystem on any *NIX platform. +////////////////////////////////////////////////////////////////////////////// + +// Sample Usage: +// ProcCpuInfo proc_info; +// int no_of_cpu; +// if (proc_info.LoadFromSystem()) { +// std::string out_str; +// proc_info.GetNumCpus(&no_of_cpu); +// proc_info.GetCpuStringValue(0, "vendor_id", &out_str); +// } +// } + +class ProcCpuInfo { + public: + ProcCpuInfo(); + virtual ~ProcCpuInfo(); + + // Reads the proc subsystem's cpu info into memory. If this fails, this + // returns false; if it succeeds, it returns true. + virtual bool LoadFromSystem(); + + // Obtains the number of logical CPU threads and places the value num. + virtual bool GetNumCpus(int* num); + + // Obtains the number of physical CPU cores and places the value num. + virtual bool GetNumPhysicalCpus(int* num); + + // Obtains the CPU family id. + virtual bool GetCpuFamily(int* id); + + // Obtains the number of sections in /proc/cpuinfo, which may be greater + // than the number of CPUs (e.g. on ARM) + virtual bool GetSectionCount(size_t* count); + + // Looks for the CPU proc item with the given name for the given section + // number and places the string value in result. + virtual bool GetSectionStringValue(size_t section_num, const std::string& key, + std::string* result); + + // Looks for the CPU proc item with the given name for the given section + // number and places the int value in result. + virtual bool GetSectionIntValue(size_t section_num, const std::string& key, + int* result); + + private: + ConfigParser::MapVector sections_; +}; + +#if !defined(WEBRTC_CHROMIUM_BUILD) +// Builds a string containing the info from lsb_release on a single line. +std::string ReadLinuxLsbRelease(); +#endif + +// Returns the output of "uname". +std::string ReadLinuxUname(); + +// Returns the content (int) of +// /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq +// Returns -1 on error. +int ReadCpuMaxFreq(); + +} // namespace rtc + +#endif // defined(WEBRTC_LINUX) +#endif // WEBRTC_BASE_LINUX_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/linux_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/linux_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/linux_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/linux_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,104 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include "webrtc/base/linux.h" +#include "webrtc/base/fileutils.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/gunit.h" + +namespace rtc { + +// These tests running on ARM are fairly specific to the output of the tegra2 +// ARM processor, and so may fail on other ARM-based systems. +TEST(ProcCpuInfo, GetProcInfo) { + ProcCpuInfo proc_info; + EXPECT_TRUE(proc_info.LoadFromSystem()); + + int out_cpus = 0; + EXPECT_TRUE(proc_info.GetNumCpus(&out_cpus)); + LOG(LS_INFO) << "GetNumCpus: " << out_cpus; + EXPECT_GT(out_cpus, 0); + + int out_cpus_phys = 0; + EXPECT_TRUE(proc_info.GetNumPhysicalCpus(&out_cpus_phys)); + LOG(LS_INFO) << "GetNumPhysicalCpus: " << out_cpus_phys; + EXPECT_GT(out_cpus_phys, 0); + EXPECT_LE(out_cpus_phys, out_cpus); + + int out_family = 0; + EXPECT_TRUE(proc_info.GetCpuFamily(&out_family)); + LOG(LS_INFO) << "cpu family: " << out_family; + EXPECT_GE(out_family, 4); + +#if defined(__arm__) + std::string out_processor; + EXPECT_TRUE(proc_info.GetSectionStringValue(0, "Processor", &out_processor)); + LOG(LS_INFO) << "Processor: " << out_processor; + EXPECT_NE(std::string::npos, out_processor.find("ARM")); + + // Most other info, such as model, stepping, vendor, etc. + // is missing on ARM systems. +#else + int out_model = 0; + EXPECT_TRUE(proc_info.GetSectionIntValue(0, "model", &out_model)); + LOG(LS_INFO) << "model: " << out_model; + + int out_stepping = 0; + EXPECT_TRUE(proc_info.GetSectionIntValue(0, "stepping", &out_stepping)); + LOG(LS_INFO) << "stepping: " << out_stepping; + + int out_processor = 0; + EXPECT_TRUE(proc_info.GetSectionIntValue(0, "processor", &out_processor)); + LOG(LS_INFO) << "processor: " << out_processor; + EXPECT_EQ(0, out_processor); + + std::string out_str; + EXPECT_TRUE(proc_info.GetSectionStringValue(0, "vendor_id", &out_str)); + LOG(LS_INFO) << "vendor_id: " << out_str; + EXPECT_FALSE(out_str.empty()); +#endif +} + +TEST(ConfigParser, ParseConfig) { + ConfigParser parser; + MemoryStream *test_stream = new MemoryStream( + "Key1: Value1\n" + "Key2\t: Value2\n" + "Key3:Value3\n" + "\n" + "Key1:Value1\n"); + ConfigParser::MapVector key_val_pairs; + parser.Attach(test_stream); + EXPECT_EQ(true, parser.Parse(&key_val_pairs)); + EXPECT_EQ(2U, key_val_pairs.size()); + EXPECT_EQ("Value1", key_val_pairs[0]["Key1"]); + EXPECT_EQ("Value2", key_val_pairs[0]["Key2"]); + EXPECT_EQ("Value3", key_val_pairs[0]["Key3"]); + EXPECT_EQ("Value1", key_val_pairs[1]["Key1"]); + key_val_pairs.clear(); + EXPECT_EQ(true, parser.Open("/proc/cpuinfo")); + EXPECT_EQ(true, parser.Parse(&key_val_pairs)); +} + +#if !defined(WEBRTC_CHROMIUM_BUILD) +TEST(ReadLinuxLsbRelease, ReturnsSomething) { + std::string str = ReadLinuxLsbRelease(); + // ChromeOS don't have lsb_release + // EXPECT_FALSE(str.empty()); +} +#endif + +TEST(ReadLinuxUname, ReturnsSomething) { + std::string str = ReadLinuxUname(); + EXPECT_FALSE(str.empty()); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/logging.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/logging.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/logging.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/logging.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,618 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if defined(WEBRTC_WIN) +#define WIN32_LEAN_AND_MEAN +#include +#define snprintf _snprintf +#undef ERROR // wingdi.h +#endif + +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +#include +#elif defined(WEBRTC_ANDROID) +#include +static const char kLibjingle[] = "libjingle"; +// Android has a 1024 limit on log inputs. We use 60 chars as an +// approx for the header/tag portion. +// See android/system/core/liblog/logd_write.c +static const int kMaxLogLineSize = 1024 - 60; +#endif // WEBRTC_MAC && !defined(WEBRTC_IOS) || WEBRTC_ANDROID + +#include + +#include +#include +#include +#include + +#include "webrtc/base/logging.h" +#include "webrtc/base/stream.h" +#include "webrtc/base/stringencode.h" +#include "webrtc/base/stringutils.h" +#include "webrtc/base/timeutils.h" + +namespace rtc { + +///////////////////////////////////////////////////////////////////////////// +// Constant Labels +///////////////////////////////////////////////////////////////////////////// + +const char * FindLabel(int value, const ConstantLabel entries[]) { + for (int i = 0; entries[i].label; ++i) { + if (value == entries[i].value) { + return entries[i].label; + } + } + return 0; +} + +std::string ErrorName(int err, const ConstantLabel * err_table) { + if (err == 0) + return "No error"; + + if (err_table != 0) { + if (const char * value = FindLabel(err, err_table)) + return value; + } + + char buffer[16]; + snprintf(buffer, sizeof(buffer), "0x%08x", err); + return buffer; +} + +///////////////////////////////////////////////////////////////////////////// +// LogMessage +///////////////////////////////////////////////////////////////////////////// + +const int LogMessage::NO_LOGGING = LS_ERROR + 1; + +#if _DEBUG +static const int LOG_DEFAULT = LS_INFO; +#else // !_DEBUG +static const int LOG_DEFAULT = LogMessage::NO_LOGGING; +#endif // !_DEBUG + +// Global lock for log subsystem, only needed to serialize access to streams_. +CriticalSection LogMessage::crit_; + +// By default, release builds don't log, debug builds at info level +int LogMessage::min_sev_ = LOG_DEFAULT; +int LogMessage::dbg_sev_ = LOG_DEFAULT; + +// Don't bother printing context for the ubiquitous INFO log messages +int LogMessage::ctx_sev_ = LS_WARNING; + +// The list of logging streams currently configured. +// Note: we explicitly do not clean this up, because of the uncertain ordering +// of destructors at program exit. Let the person who sets the stream trigger +// cleanup by setting to NULL, or let it leak (safe at program exit). +LogMessage::StreamList LogMessage::streams_; + +// Boolean options default to false (0) +bool LogMessage::thread_, LogMessage::timestamp_; + +// If we're in diagnostic mode, we'll be explicitly set that way; default=false. +bool LogMessage::is_diagnostic_mode_ = false; + +LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev, + LogErrorContext err_ctx, int err, const char* module) + : severity_(sev), + warn_slow_logs_delay_(WARN_SLOW_LOGS_DELAY) { + if (timestamp_) { + uint32 time = TimeSince(LogStartTime()); + // Also ensure WallClockStartTime is initialized, so that it matches + // LogStartTime. + WallClockStartTime(); + print_stream_ << "[" << std::setfill('0') << std::setw(3) << (time / 1000) + << ":" << std::setw(3) << (time % 1000) << std::setfill(' ') + << "] "; + } + + if (thread_) { +#if defined(WEBRTC_WIN) + DWORD id = GetCurrentThreadId(); + print_stream_ << "[" << std::hex << id << std::dec << "] "; +#endif // WEBRTC_WIN + } + + if (severity_ >= ctx_sev_) { + print_stream_ << Describe(sev) << "(" << DescribeFile(file) + << ":" << line << "): "; + } + + if (err_ctx != ERRCTX_NONE) { + std::ostringstream tmp; + tmp << "[0x" << std::setfill('0') << std::hex << std::setw(8) << err << "]"; + switch (err_ctx) { + case ERRCTX_ERRNO: + tmp << " " << strerror(err); + break; +#if WEBRTC_WIN + case ERRCTX_HRESULT: { + char msgbuf[256]; + DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM; + HMODULE hmod = GetModuleHandleA(module); + if (hmod) + flags |= FORMAT_MESSAGE_FROM_HMODULE; + if (DWORD len = FormatMessageA( + flags, hmod, err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + msgbuf, sizeof(msgbuf) / sizeof(msgbuf[0]), NULL)) { + while ((len > 0) && + isspace(static_cast(msgbuf[len-1]))) { + msgbuf[--len] = 0; + } + tmp << " " << msgbuf; + } + break; + } +#endif // WEBRTC_WIN +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + case ERRCTX_OSSTATUS: { + tmp << " " << nonnull(GetMacOSStatusErrorString(err), "Unknown error"); + if (const char* desc = GetMacOSStatusCommentString(err)) { + tmp << ": " << desc; + } + break; + } +#endif // WEBRTC_MAC && !defined(WEBRTC_IOS) + default: + break; + } + extra_ = tmp.str(); + } +} + +LogMessage::~LogMessage() { + if (!extra_.empty()) + print_stream_ << " : " << extra_; + print_stream_ << std::endl; + + const std::string& str = print_stream_.str(); + if (severity_ >= dbg_sev_) { + OutputToDebug(str, severity_); + } + + uint32 before = Time(); + // Must lock streams_ before accessing + CritScope cs(&crit_); + for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) { + if (severity_ >= it->second) { + OutputToStream(it->first, str); + } + } + uint32 delay = TimeSince(before); + if (delay >= warn_slow_logs_delay_) { + LogMessage slow_log_warning = + rtc::LogMessage(__FILE__, __LINE__, LS_WARNING); + // If our warning is slow, we don't want to warn about it, because + // that would lead to inifinite recursion. So, give a really big + // number for the delay threshold. + slow_log_warning.warn_slow_logs_delay_ = UINT_MAX; + slow_log_warning.stream() << "Slow log: took " << delay << "ms to write " + << str.size() << " bytes."; + } +} + +uint32 LogMessage::LogStartTime() { + static const uint32 g_start = Time(); + return g_start; +} + +uint32 LogMessage::WallClockStartTime() { + static const uint32 g_start_wallclock = time(NULL); + return g_start_wallclock; +} + +void LogMessage::LogContext(int min_sev) { + ctx_sev_ = min_sev; +} + +void LogMessage::LogThreads(bool on) { + thread_ = on; +} + +void LogMessage::LogTimestamps(bool on) { + timestamp_ = on; +} + +void LogMessage::LogToDebug(int min_sev) { + dbg_sev_ = min_sev; + UpdateMinLogSeverity(); +} + +void LogMessage::LogToStream(StreamInterface* stream, int min_sev) { + CritScope cs(&crit_); + // Discard and delete all previously installed streams + for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) { + delete it->first; + } + streams_.clear(); + // Install the new stream, if specified + if (stream) { + AddLogToStream(stream, min_sev); + } +} + +int LogMessage::GetLogToStream(StreamInterface* stream) { + CritScope cs(&crit_); + int sev = NO_LOGGING; + for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) { + if (!stream || stream == it->first) { + sev = _min(sev, it->second); + } + } + return sev; +} + +void LogMessage::AddLogToStream(StreamInterface* stream, int min_sev) { + CritScope cs(&crit_); + streams_.push_back(std::make_pair(stream, min_sev)); + UpdateMinLogSeverity(); +} + +void LogMessage::RemoveLogToStream(StreamInterface* stream) { + CritScope cs(&crit_); + for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) { + if (stream == it->first) { + streams_.erase(it); + break; + } + } + UpdateMinLogSeverity(); +} + +void LogMessage::ConfigureLogging(const char* params, const char* filename) { + int current_level = LS_VERBOSE; + int debug_level = GetLogToDebug(); + int file_level = GetLogToStream(); + + std::vector tokens; + tokenize(params, ' ', &tokens); + + for (size_t i = 0; i < tokens.size(); ++i) { + if (tokens[i].empty()) + continue; + + // Logging features + if (tokens[i] == "tstamp") { + LogTimestamps(); + } else if (tokens[i] == "thread") { + LogThreads(); + + // Logging levels + } else if (tokens[i] == "sensitive") { + current_level = LS_SENSITIVE; + } else if (tokens[i] == "verbose") { + current_level = LS_VERBOSE; + } else if (tokens[i] == "info") { + current_level = LS_INFO; + } else if (tokens[i] == "warning") { + current_level = LS_WARNING; + } else if (tokens[i] == "error") { + current_level = LS_ERROR; + } else if (tokens[i] == "none") { + current_level = NO_LOGGING; + + // Logging targets + } else if (tokens[i] == "file") { + file_level = current_level; + } else if (tokens[i] == "debug") { + debug_level = current_level; + } + } + +#if defined(WEBRTC_WIN) + if ((NO_LOGGING != debug_level) && !::IsDebuggerPresent()) { + // First, attempt to attach to our parent's console... so if you invoke + // from the command line, we'll see the output there. Otherwise, create + // our own console window. + // Note: These methods fail if a console already exists, which is fine. + bool success = false; + typedef BOOL (WINAPI* PFN_AttachConsole)(DWORD); + if (HINSTANCE kernel32 = ::LoadLibrary(L"kernel32.dll")) { + // AttachConsole is defined on WinXP+. + if (PFN_AttachConsole attach_console = reinterpret_cast + (::GetProcAddress(kernel32, "AttachConsole"))) { + success = (FALSE != attach_console(ATTACH_PARENT_PROCESS)); + } + ::FreeLibrary(kernel32); + } + if (!success) { + ::AllocConsole(); + } + } +#endif // WEBRTC_WIN + + LogToDebug(debug_level); + +#if !defined(__native_client__) // No logging to file in NaCl. + scoped_ptr stream; + if (NO_LOGGING != file_level) { + stream.reset(new FileStream); + if (!stream->Open(filename, "wb", NULL) || !stream->DisableBuffering()) { + stream.reset(); + } + } + + LogToStream(stream.release(), file_level); +#endif +} + +int LogMessage::ParseLogSeverity(const std::string& value) { + int level = NO_LOGGING; + if (value == "LS_SENSITIVE") { + level = LS_SENSITIVE; + } else if (value == "LS_VERBOSE") { + level = LS_VERBOSE; + } else if (value == "LS_INFO") { + level = LS_INFO; + } else if (value == "LS_WARNING") { + level = LS_WARNING; + } else if (value == "LS_ERROR") { + level = LS_ERROR; + } else if (isdigit(value[0])) { + level = atoi(value.c_str()); // NOLINT + } + return level; +} + +void LogMessage::UpdateMinLogSeverity() { + int min_sev = dbg_sev_; + for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) { + min_sev = _min(dbg_sev_, it->second); + } + min_sev_ = min_sev; +} + +const char* LogMessage::Describe(LoggingSeverity sev) { + switch (sev) { + case LS_SENSITIVE: return "Sensitive"; + case LS_VERBOSE: return "Verbose"; + case LS_INFO: return "Info"; + case LS_WARNING: return "Warning"; + case LS_ERROR: return "Error"; + default: return ""; + } +} + +const char* LogMessage::DescribeFile(const char* file) { + const char* end1 = ::strrchr(file, '/'); + const char* end2 = ::strrchr(file, '\\'); + if (!end1 && !end2) + return file; + else + return (end1 > end2) ? end1 + 1 : end2 + 1; +} + +void LogMessage::OutputToDebug(const std::string& str, + LoggingSeverity severity) { + bool log_to_stderr = true; +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && (!defined(DEBUG) || defined(NDEBUG)) + // On the Mac, all stderr output goes to the Console log and causes clutter. + // So in opt builds, don't log to stderr unless the user specifically sets + // a preference to do so. + CFStringRef key = CFStringCreateWithCString(kCFAllocatorDefault, + "logToStdErr", + kCFStringEncodingUTF8); + CFStringRef domain = CFBundleGetIdentifier(CFBundleGetMainBundle()); + if (key != NULL && domain != NULL) { + Boolean exists_and_is_valid; + Boolean should_log = + CFPreferencesGetAppBooleanValue(key, domain, &exists_and_is_valid); + // If the key doesn't exist or is invalid or is false, we will not log to + // stderr. + log_to_stderr = exists_and_is_valid && should_log; + } + if (key != NULL) { + CFRelease(key); + } +#endif +#if defined(WEBRTC_WIN) + // Always log to the debugger. + // Perhaps stderr should be controlled by a preference, as on Mac? + OutputDebugStringA(str.c_str()); + if (log_to_stderr) { + // This handles dynamically allocated consoles, too. + if (HANDLE error_handle = ::GetStdHandle(STD_ERROR_HANDLE)) { + log_to_stderr = false; + DWORD written = 0; + ::WriteFile(error_handle, str.data(), static_cast(str.size()), + &written, 0); + } + } +#endif // WEBRTC_WIN +#if defined(WEBRTC_ANDROID) + // Android's logging facility uses severity to log messages but we + // need to map libjingle's severity levels to Android ones first. + // Also write to stderr which maybe available to executable started + // from the shell. + int prio; + switch (severity) { + case LS_SENSITIVE: + __android_log_write(ANDROID_LOG_INFO, kLibjingle, "SENSITIVE"); + if (log_to_stderr) { + fprintf(stderr, "SENSITIVE"); + fflush(stderr); + } + return; + case LS_VERBOSE: + prio = ANDROID_LOG_VERBOSE; + break; + case LS_INFO: + prio = ANDROID_LOG_INFO; + break; + case LS_WARNING: + prio = ANDROID_LOG_WARN; + break; + case LS_ERROR: + prio = ANDROID_LOG_ERROR; + break; + default: + prio = ANDROID_LOG_UNKNOWN; + } + + int size = str.size(); + int line = 0; + int idx = 0; + const int max_lines = size / kMaxLogLineSize + 1; + if (max_lines == 1) { + __android_log_print(prio, kLibjingle, "%.*s", size, str.c_str()); + } else { + while (size > 0) { + const int len = std::min(size, kMaxLogLineSize); + // Use the size of the string in the format (str may have \0 in the + // middle). + __android_log_print(prio, kLibjingle, "[%d/%d] %.*s", + line + 1, max_lines, + len, str.c_str() + idx); + idx += len; + size -= len; + ++line; + } + } +#endif // WEBRTC_ANDROID + if (log_to_stderr) { + fprintf(stderr, "%s", str.c_str()); + fflush(stderr); + } +} + +void LogMessage::OutputToStream(StreamInterface* stream, + const std::string& str) { + // If write isn't fully successful, what are we going to do, log it? :) + stream->WriteAll(str.data(), str.size(), NULL, NULL); +} + +////////////////////////////////////////////////////////////////////// +// Logging Helpers +////////////////////////////////////////////////////////////////////// + +void LogMultiline(LoggingSeverity level, const char* label, bool input, + const void* data, size_t len, bool hex_mode, + LogMultilineState* state) { + if (!LOG_CHECK_LEVEL_V(level)) + return; + + const char * direction = (input ? " << " : " >> "); + + // NULL data means to flush our count of unprintable characters. + if (!data) { + if (state && state->unprintable_count_[input]) { + LOG_V(level) << label << direction << "## " + << state->unprintable_count_[input] + << " consecutive unprintable ##"; + state->unprintable_count_[input] = 0; + } + return; + } + + // The ctype classification functions want unsigned chars. + const unsigned char* udata = static_cast(data); + + if (hex_mode) { + const size_t LINE_SIZE = 24; + char hex_line[LINE_SIZE * 9 / 4 + 2], asc_line[LINE_SIZE + 1]; + while (len > 0) { + memset(asc_line, ' ', sizeof(asc_line)); + memset(hex_line, ' ', sizeof(hex_line)); + size_t line_len = _min(len, LINE_SIZE); + for (size_t i = 0; i < line_len; ++i) { + unsigned char ch = udata[i]; + asc_line[i] = isprint(ch) ? ch : '.'; + hex_line[i*2 + i/4] = hex_encode(ch >> 4); + hex_line[i*2 + i/4 + 1] = hex_encode(ch & 0xf); + } + asc_line[sizeof(asc_line)-1] = 0; + hex_line[sizeof(hex_line)-1] = 0; + LOG_V(level) << label << direction + << asc_line << " " << hex_line << " "; + udata += line_len; + len -= line_len; + } + return; + } + + size_t consecutive_unprintable = state ? state->unprintable_count_[input] : 0; + + const unsigned char* end = udata + len; + while (udata < end) { + const unsigned char* line = udata; + const unsigned char* end_of_line = strchrn(udata, + end - udata, + '\n'); + if (!end_of_line) { + udata = end_of_line = end; + } else { + udata = end_of_line + 1; + } + + bool is_printable = true; + + // If we are in unprintable mode, we need to see a line of at least + // kMinPrintableLine characters before we'll switch back. + const ptrdiff_t kMinPrintableLine = 4; + if (consecutive_unprintable && ((end_of_line - line) < kMinPrintableLine)) { + is_printable = false; + } else { + // Determine if the line contains only whitespace and printable + // characters. + bool is_entirely_whitespace = true; + for (const unsigned char* pos = line; pos < end_of_line; ++pos) { + if (isspace(*pos)) + continue; + is_entirely_whitespace = false; + if (!isprint(*pos)) { + is_printable = false; + break; + } + } + // Treat an empty line following unprintable data as unprintable. + if (consecutive_unprintable && is_entirely_whitespace) { + is_printable = false; + } + } + if (!is_printable) { + consecutive_unprintable += (udata - line); + continue; + } + // Print out the current line, but prefix with a count of prior unprintable + // characters. + if (consecutive_unprintable) { + LOG_V(level) << label << direction << "## " << consecutive_unprintable + << " consecutive unprintable ##"; + consecutive_unprintable = 0; + } + // Strip off trailing whitespace. + while ((end_of_line > line) && isspace(*(end_of_line-1))) { + --end_of_line; + } + // Filter out any private data + std::string substr(reinterpret_cast(line), end_of_line - line); + std::string::size_type pos_private = substr.find("Email"); + if (pos_private == std::string::npos) { + pos_private = substr.find("Passwd"); + } + if (pos_private == std::string::npos) { + LOG_V(level) << label << direction << substr; + } else { + LOG_V(level) << label << direction << "## omitted for privacy ##"; + } + } + + if (state) { + state->unprintable_count_[input] = consecutive_unprintable; + } +} + +////////////////////////////////////////////////////////////////////// + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/logging.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/logging.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/logging.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/logging.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,349 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// LOG(...) an ostream target that can be used to send formatted +// output to a variety of logging targets, such as debugger console, stderr, +// file, or any StreamInterface. +// The severity level passed as the first argument to the LOGging +// functions is used as a filter, to limit the verbosity of the logging. +// Static members of LogMessage documented below are used to control the +// verbosity and target of the output. +// There are several variations on the LOG macro which facilitate logging +// of common error conditions, detailed below. + +// LOG(sev) logs the given stream at severity "sev", which must be a +// compile-time constant of the LoggingSeverity type, without the namespace +// prefix. +// LOG_V(sev) Like LOG(), but sev is a run-time variable of the LoggingSeverity +// type (basically, it just doesn't prepend the namespace). +// LOG_F(sev) Like LOG(), but includes the name of the current function. +// LOG_T(sev) Like LOG(), but includes the this pointer. +// LOG_T_F(sev) Like LOG_F(), but includes the this pointer. +// LOG_GLE(M)(sev [, mod]) attempt to add a string description of the +// HRESULT returned by GetLastError. The "M" variant allows searching of a +// DLL's string table for the error description. +// LOG_ERRNO(sev) attempts to add a string description of an errno-derived +// error. errno and associated facilities exist on both Windows and POSIX, +// but on Windows they only apply to the C/C++ runtime. +// LOG_ERR(sev) is an alias for the platform's normal error system, i.e. _GLE on +// Windows and _ERRNO on POSIX. +// (The above three also all have _EX versions that let you specify the error +// code, rather than using the last one.) +// LOG_E(sev, ctx, err, ...) logs a detailed error interpreted using the +// specified context. +// LOG_CHECK_LEVEL(sev) (and LOG_CHECK_LEVEL_V(sev)) can be used as a test +// before performing expensive or sensitive operations whose sole purpose is +// to output logging data at the desired level. +// Lastly, PLOG(sev, err) is an alias for LOG_ERR_EX. + +#ifndef WEBRTC_BASE_LOGGING_H_ +#define WEBRTC_BASE_LOGGING_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" // NOLINT +#endif + +#include +#include +#include +#include +#include "webrtc/base/basictypes.h" +#include "webrtc/base/criticalsection.h" + +namespace rtc { + +class StreamInterface; + +/////////////////////////////////////////////////////////////////////////////// +// ConstantLabel can be used to easily generate string names from constant +// values. This can be useful for logging descriptive names of error messages. +// Usage: +// const ConstantLabel LIBRARY_ERRORS[] = { +// KLABEL(SOME_ERROR), +// KLABEL(SOME_OTHER_ERROR), +// ... +// LASTLABEL +// } +// +// int err = LibraryFunc(); +// LOG(LS_ERROR) << "LibraryFunc returned: " +// << ErrorName(err, LIBRARY_ERRORS); + +struct ConstantLabel { int value; const char * label; }; +#define KLABEL(x) { x, #x } +#define TLABEL(x, y) { x, y } +#define LASTLABEL { 0, 0 } + +const char * FindLabel(int value, const ConstantLabel entries[]); +std::string ErrorName(int err, const ConstantLabel* err_table); + +////////////////////////////////////////////////////////////////////// + +// Note that the non-standard LoggingSeverity aliases exist because they are +// still in broad use. The meanings of the levels are: +// LS_SENSITIVE: Information which should only be logged with the consent +// of the user, due to privacy concerns. +// LS_VERBOSE: This level is for data which we do not want to appear in the +// normal debug log, but should appear in diagnostic logs. +// LS_INFO: Chatty level used in debugging for all sorts of things, the default +// in debug builds. +// LS_WARNING: Something that may warrant investigation. +// LS_ERROR: Something that should not have occurred. +enum LoggingSeverity { LS_SENSITIVE, LS_VERBOSE, LS_INFO, LS_WARNING, LS_ERROR, + INFO = LS_INFO, + WARNING = LS_WARNING, + LERROR = LS_ERROR }; + +// LogErrorContext assists in interpreting the meaning of an error value. +enum LogErrorContext { + ERRCTX_NONE, + ERRCTX_ERRNO, // System-local errno + ERRCTX_HRESULT, // Windows HRESULT + ERRCTX_OSSTATUS, // MacOS OSStatus + + // Abbreviations for LOG_E macro + ERRCTX_EN = ERRCTX_ERRNO, // LOG_E(sev, EN, x) + ERRCTX_HR = ERRCTX_HRESULT, // LOG_E(sev, HR, x) + ERRCTX_OS = ERRCTX_OSSTATUS, // LOG_E(sev, OS, x) +}; + +class LogMessage { + public: + static const int NO_LOGGING; + static const uint32 WARN_SLOW_LOGS_DELAY = 50; // ms + + LogMessage(const char* file, int line, LoggingSeverity sev, + LogErrorContext err_ctx = ERRCTX_NONE, int err = 0, + const char* module = NULL); + ~LogMessage(); + + static inline bool Loggable(LoggingSeverity sev) { return (sev >= min_sev_); } + std::ostream& stream() { return print_stream_; } + + // Returns the time at which this function was called for the first time. + // The time will be used as the logging start time. + // If this is not called externally, the LogMessage ctor also calls it, in + // which case the logging start time will be the time of the first LogMessage + // instance is created. + static uint32 LogStartTime(); + + // Returns the wall clock equivalent of |LogStartTime|, in seconds from the + // epoch. + static uint32 WallClockStartTime(); + + // These are attributes which apply to all logging channels + // LogContext: Display the file and line number of the message + static void LogContext(int min_sev); + // LogThreads: Display the thread identifier of the current thread + static void LogThreads(bool on = true); + // LogTimestamps: Display the elapsed time of the program + static void LogTimestamps(bool on = true); + + // These are the available logging channels + // Debug: Debug console on Windows, otherwise stderr + static void LogToDebug(int min_sev); + static int GetLogToDebug() { return dbg_sev_; } + + // Stream: Any non-blocking stream interface. LogMessage takes ownership of + // the stream. Multiple streams may be specified by using AddLogToStream. + // LogToStream is retained for backwards compatibility; when invoked, it + // will discard any previously set streams and install the specified stream. + // GetLogToStream gets the severity for the specified stream, of if none + // is specified, the minimum stream severity. + // RemoveLogToStream removes the specified stream, without destroying it. + static void LogToStream(StreamInterface* stream, int min_sev); + static int GetLogToStream(StreamInterface* stream = NULL); + static void AddLogToStream(StreamInterface* stream, int min_sev); + static void RemoveLogToStream(StreamInterface* stream); + + // Testing against MinLogSeverity allows code to avoid potentially expensive + // logging operations by pre-checking the logging level. + static int GetMinLogSeverity() { return min_sev_; } + + static void SetDiagnosticMode(bool f) { is_diagnostic_mode_ = f; } + static bool IsDiagnosticMode() { return is_diagnostic_mode_; } + + // Parses the provided parameter stream to configure the options above. + // Useful for configuring logging from the command line. If file logging + // is enabled, it is output to the specified filename. + static void ConfigureLogging(const char* params, const char* filename); + + // Convert the string to a LS_ value; also accept numeric values. + static int ParseLogSeverity(const std::string& value); + + private: + typedef std::list > StreamList; + + // Updates min_sev_ appropriately when debug sinks change. + static void UpdateMinLogSeverity(); + + // These assist in formatting some parts of the debug output. + static const char* Describe(LoggingSeverity sev); + static const char* DescribeFile(const char* file); + + // These write out the actual log messages. + static void OutputToDebug(const std::string& msg, LoggingSeverity severity_); + static void OutputToStream(StreamInterface* stream, const std::string& msg); + + // The ostream that buffers the formatted message before output + std::ostringstream print_stream_; + + // The severity level of this message + LoggingSeverity severity_; + + // String data generated in the constructor, that should be appended to + // the message before output. + std::string extra_; + + // If time it takes to write to stream is more than this, log one + // additional warning about it. + uint32 warn_slow_logs_delay_; + + // Global lock for the logging subsystem + static CriticalSection crit_; + + // dbg_sev_ is the thresholds for those output targets + // min_sev_ is the minimum (most verbose) of those levels, and is used + // as a short-circuit in the logging macros to identify messages that won't + // be logged. + // ctx_sev_ is the minimum level at which file context is displayed + static int min_sev_, dbg_sev_, ctx_sev_; + + // The output streams and their associated severities + static StreamList streams_; + + // Flags for formatting options + static bool thread_, timestamp_; + + // are we in diagnostic mode (as defined by the app)? + static bool is_diagnostic_mode_; + + DISALLOW_EVIL_CONSTRUCTORS(LogMessage); +}; + +////////////////////////////////////////////////////////////////////// +// Logging Helpers +////////////////////////////////////////////////////////////////////// + +class LogMultilineState { + public: + size_t unprintable_count_[2]; + LogMultilineState() { + unprintable_count_[0] = unprintable_count_[1] = 0; + } +}; + +// When possible, pass optional state variable to track various data across +// multiple calls to LogMultiline. Otherwise, pass NULL. +void LogMultiline(LoggingSeverity level, const char* label, bool input, + const void* data, size_t len, bool hex_mode, + LogMultilineState* state); + +#ifndef LOG + +// The following non-obvious technique for implementation of a +// conditional log stream was stolen from google3/base/logging.h. + +// This class is used to explicitly ignore values in the conditional +// logging macros. This avoids compiler warnings like "value computed +// is not used" and "statement has no effect". + +class LogMessageVoidify { + public: + LogMessageVoidify() { } + // This has to be an operator with a precedence lower than << but + // higher than ?: + void operator&(std::ostream&) { } +}; + +#define LOG_SEVERITY_PRECONDITION(sev) \ + !(rtc::LogMessage::Loggable(sev)) \ + ? (void) 0 \ + : rtc::LogMessageVoidify() & + +#define LOG(sev) \ + LOG_SEVERITY_PRECONDITION(rtc::sev) \ + rtc::LogMessage(__FILE__, __LINE__, rtc::sev).stream() + +// The _V version is for when a variable is passed in. It doesn't do the +// namespace concatination. +#define LOG_V(sev) \ + LOG_SEVERITY_PRECONDITION(sev) \ + rtc::LogMessage(__FILE__, __LINE__, sev).stream() + +// The _F version prefixes the message with the current function name. +#if (defined(__GNUC__) && defined(_DEBUG)) || defined(WANT_PRETTY_LOG_F) +#define LOG_F(sev) LOG(sev) << __PRETTY_FUNCTION__ << ": " +#define LOG_T_F(sev) LOG(sev) << this << ": " << __PRETTY_FUNCTION__ << ": " +#else +#define LOG_F(sev) LOG(sev) << __FUNCTION__ << ": " +#define LOG_T_F(sev) LOG(sev) << this << ": " << __FUNCTION__ << ": " +#endif + +#define LOG_CHECK_LEVEL(sev) \ + rtc::LogCheckLevel(rtc::sev) +#define LOG_CHECK_LEVEL_V(sev) \ + rtc::LogCheckLevel(sev) +inline bool LogCheckLevel(LoggingSeverity sev) { + return (LogMessage::GetMinLogSeverity() <= sev); +} + +#define LOG_E(sev, ctx, err, ...) \ + LOG_SEVERITY_PRECONDITION(rtc::sev) \ + rtc::LogMessage(__FILE__, __LINE__, rtc::sev, \ + rtc::ERRCTX_ ## ctx, err , ##__VA_ARGS__) \ + .stream() + +#define LOG_T(sev) LOG(sev) << this << ": " + +#define LOG_ERRNO_EX(sev, err) \ + LOG_E(sev, ERRNO, err) +#define LOG_ERRNO(sev) \ + LOG_ERRNO_EX(sev, errno) + +#if defined(WEBRTC_WIN) +#define LOG_GLE_EX(sev, err) \ + LOG_E(sev, HRESULT, err) +#define LOG_GLE(sev) \ + LOG_GLE_EX(sev, GetLastError()) +#define LOG_GLEM(sev, mod) \ + LOG_E(sev, HRESULT, GetLastError(), mod) +#define LOG_ERR_EX(sev, err) \ + LOG_GLE_EX(sev, err) +#define LOG_ERR(sev) \ + LOG_GLE(sev) +#define LAST_SYSTEM_ERROR \ + (::GetLastError()) +#elif __native_client__ +#define LOG_ERR_EX(sev, err) \ + LOG(sev) +#define LOG_ERR(sev) \ + LOG(sev) +#define LAST_SYSTEM_ERROR \ + (0) +#elif defined(WEBRTC_POSIX) +#define LOG_ERR_EX(sev, err) \ + LOG_ERRNO_EX(sev, err) +#define LOG_ERR(sev) \ + LOG_ERRNO(sev) +#define LAST_SYSTEM_ERROR \ + (errno) +#endif // WEBRTC_WIN + +#define PLOG(sev, err) \ + LOG_ERR_EX(sev, err) + +// TODO(?): Add an "assert" wrapper that logs in the same manner. + +#endif // LOG + +} // namespace rtc + +#endif // WEBRTC_BASE_LOGGING_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/logging_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/logging_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/logging_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/logging_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,139 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/fileutils.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/pathutils.h" +#include "webrtc/base/stream.h" +#include "webrtc/base/thread.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace rtc { + +// Test basic logging operation. We should get the INFO log but not the VERBOSE. +// We should restore the correct global state at the end. +TEST(LogTest, SingleStream) { + int sev = LogMessage::GetLogToStream(NULL); + + std::string str; + StringStream stream(str); + LogMessage::AddLogToStream(&stream, LS_INFO); + EXPECT_EQ(LS_INFO, LogMessage::GetLogToStream(&stream)); + + LOG(LS_INFO) << "INFO"; + LOG(LS_VERBOSE) << "VERBOSE"; + EXPECT_NE(std::string::npos, str.find("INFO")); + EXPECT_EQ(std::string::npos, str.find("VERBOSE")); + + LogMessage::RemoveLogToStream(&stream); + EXPECT_EQ(LogMessage::NO_LOGGING, LogMessage::GetLogToStream(&stream)); + + EXPECT_EQ(sev, LogMessage::GetLogToStream(NULL)); +} + +// Test using multiple log streams. The INFO stream should get the INFO message, +// the VERBOSE stream should get the INFO and the VERBOSE. +// We should restore the correct global state at the end. +TEST(LogTest, MultipleStreams) { + int sev = LogMessage::GetLogToStream(NULL); + + std::string str1, str2; + StringStream stream1(str1), stream2(str2); + LogMessage::AddLogToStream(&stream1, LS_INFO); + LogMessage::AddLogToStream(&stream2, LS_VERBOSE); + EXPECT_EQ(LS_INFO, LogMessage::GetLogToStream(&stream1)); + EXPECT_EQ(LS_VERBOSE, LogMessage::GetLogToStream(&stream2)); + + LOG(LS_INFO) << "INFO"; + LOG(LS_VERBOSE) << "VERBOSE"; + + EXPECT_NE(std::string::npos, str1.find("INFO")); + EXPECT_EQ(std::string::npos, str1.find("VERBOSE")); + EXPECT_NE(std::string::npos, str2.find("INFO")); + EXPECT_NE(std::string::npos, str2.find("VERBOSE")); + + LogMessage::RemoveLogToStream(&stream2); + LogMessage::RemoveLogToStream(&stream1); + EXPECT_EQ(LogMessage::NO_LOGGING, LogMessage::GetLogToStream(&stream2)); + EXPECT_EQ(LogMessage::NO_LOGGING, LogMessage::GetLogToStream(&stream1)); + + EXPECT_EQ(sev, LogMessage::GetLogToStream(NULL)); +} + +// Ensure we don't crash when adding/removing streams while threads are going. +// We should restore the correct global state at the end. +class LogThread : public Thread { + public: + virtual ~LogThread() { + Stop(); + } + + private: + void Run() { + // LS_SENSITIVE to avoid cluttering up any real logging going on + LOG(LS_SENSITIVE) << "LOG"; + } +}; + +TEST(LogTest, MultipleThreads) { + int sev = LogMessage::GetLogToStream(NULL); + + LogThread thread1, thread2, thread3; + thread1.Start(); + thread2.Start(); + thread3.Start(); + + NullStream stream1, stream2, stream3; + for (int i = 0; i < 1000; ++i) { + LogMessage::AddLogToStream(&stream1, LS_INFO); + LogMessage::AddLogToStream(&stream2, LS_VERBOSE); + LogMessage::AddLogToStream(&stream3, LS_SENSITIVE); + LogMessage::RemoveLogToStream(&stream1); + LogMessage::RemoveLogToStream(&stream2); + LogMessage::RemoveLogToStream(&stream3); + } + + EXPECT_EQ(sev, LogMessage::GetLogToStream(NULL)); +} + + +TEST(LogTest, WallClockStartTime) { + uint32 time = LogMessage::WallClockStartTime(); + // Expect the time to be in a sensible range, e.g. > 2012-01-01. + EXPECT_GT(time, 1325376000u); +} + +// Test the time required to write 1000 80-character logs to an unbuffered file. +TEST(LogTest, Perf) { + Pathname path; + EXPECT_TRUE(Filesystem::GetTemporaryFolder(path, true, NULL)); + path.SetPathname(Filesystem::TempFilename(path, "ut")); + + FileStream stream; + EXPECT_TRUE(stream.Open(path.pathname(), "wb", NULL)); + stream.DisableBuffering(); + LogMessage::AddLogToStream(&stream, LS_SENSITIVE); + + uint32 start = Time(), finish; + std::string message('X', 80); + for (int i = 0; i < 1000; ++i) { + LOG(LS_SENSITIVE) << message; + } + finish = Time(); + + LogMessage::RemoveLogToStream(&stream); + stream.Close(); + Filesystem::DeleteFile(path); + + LOG(LS_INFO) << "Average log time: " << TimeDiff(finish, start) << " us"; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macasyncsocket.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macasyncsocket.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macasyncsocket.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macasyncsocket.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,477 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +// +// MacAsyncSocket is a kind of AsyncSocket. It does not support the SOCK_DGRAM +// type (yet). It works asynchronously, which means that users of this socket +// should connect to the various events declared in asyncsocket.h to receive +// notifications about this socket. It uses CFSockets for signals, but prefers +// the basic bsd socket operations rather than their CFSocket wrappers when +// possible. + +#include +#include + +#include "webrtc/base/macasyncsocket.h" + +#include "webrtc/base/logging.h" +#include "webrtc/base/macsocketserver.h" + +namespace rtc { + +static const int kCallbackFlags = kCFSocketReadCallBack | + kCFSocketConnectCallBack | + kCFSocketWriteCallBack; + +MacAsyncSocket::MacAsyncSocket(MacBaseSocketServer* ss, int family) + : ss_(ss), + socket_(NULL), + native_socket_(INVALID_SOCKET), + source_(NULL), + current_callbacks_(0), + disabled_(false), + error_(0), + state_(CS_CLOSED), + resolver_(NULL) { + Initialize(family); +} + +MacAsyncSocket::~MacAsyncSocket() { + Close(); +} + +// Returns the address to which the socket is bound. If the socket is not +// bound, then the any-address is returned. +SocketAddress MacAsyncSocket::GetLocalAddress() const { + SocketAddress address; + + // The CFSocket doesn't pick up on implicit binds from the connect call. + // Calling bind in before connect explicitly causes errors, so just query + // the underlying bsd socket. + sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + int result = ::getsockname(native_socket_, + reinterpret_cast(&addr), &addrlen); + if (result >= 0) { + SocketAddressFromSockAddrStorage(addr, &address); + } + return address; +} + +// Returns the address to which the socket is connected. If the socket is not +// connected, then the any-address is returned. +SocketAddress MacAsyncSocket::GetRemoteAddress() const { + SocketAddress address; + + // Use native_socket for consistency with GetLocalAddress. + sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + int result = ::getpeername(native_socket_, + reinterpret_cast(&addr), &addrlen); + if (result >= 0) { + SocketAddressFromSockAddrStorage(addr, &address); + } + return address; +} + +// Bind the socket to a local address. +int MacAsyncSocket::Bind(const SocketAddress& address) { + sockaddr_storage saddr = {0}; + size_t len = address.ToSockAddrStorage(&saddr); + int err = ::bind(native_socket_, reinterpret_cast(&saddr), len); + if (err == SOCKET_ERROR) error_ = errno; + return err; +} + +void MacAsyncSocket::OnResolveResult(SignalThread* thread) { + if (thread != resolver_) { + return; + } + int error = resolver_->GetError(); + if (error == 0) { + error = DoConnect(resolver_->address()); + } else { + Close(); + } + if (error) { + error_ = error; + SignalCloseEvent(this, error_); + } +} + +// Connect to a remote address. +int MacAsyncSocket::Connect(const SocketAddress& addr) { + // TODO(djw): Consolidate all the connect->resolve->doconnect implementations. + if (state_ != CS_CLOSED) { + SetError(EALREADY); + return SOCKET_ERROR; + } + if (addr.IsUnresolved()) { + LOG(LS_VERBOSE) << "Resolving addr in MacAsyncSocket::Connect"; + resolver_ = new AsyncResolver(); + resolver_->SignalWorkDone.connect(this, + &MacAsyncSocket::OnResolveResult); + resolver_->Start(addr); + state_ = CS_CONNECTING; + return 0; + } + return DoConnect(addr); +} + +int MacAsyncSocket::DoConnect(const SocketAddress& addr) { + if (!valid()) { + Initialize(addr.family()); + if (!valid()) + return SOCKET_ERROR; + } + + sockaddr_storage saddr; + size_t len = addr.ToSockAddrStorage(&saddr); + int result = ::connect(native_socket_, reinterpret_cast(&saddr), + len); + + if (result != SOCKET_ERROR) { + state_ = CS_CONNECTED; + } else { + error_ = errno; + if (error_ == EINPROGRESS) { + state_ = CS_CONNECTING; + result = 0; + } + } + return result; +} + +// Send to the remote end we're connected to. +int MacAsyncSocket::Send(const void* buffer, size_t length) { + if (!valid()) { + return SOCKET_ERROR; + } + + int sent = ::send(native_socket_, buffer, length, 0); + + if (sent == SOCKET_ERROR) { + error_ = errno; + + if (IsBlocking()) { + // Reenable the writable callback (once), since we are flow controlled. + CFSocketEnableCallBacks(socket_, kCallbackFlags); + current_callbacks_ = kCallbackFlags; + } + } + return sent; +} + +// Send to the given address. We may or may not be connected to anyone. +int MacAsyncSocket::SendTo(const void* buffer, size_t length, + const SocketAddress& address) { + if (!valid()) { + return SOCKET_ERROR; + } + + sockaddr_storage saddr; + size_t len = address.ToSockAddrStorage(&saddr); + int sent = ::sendto(native_socket_, buffer, length, 0, + reinterpret_cast(&saddr), len); + + if (sent == SOCKET_ERROR) { + error_ = errno; + } + + return sent; +} + +// Read data received from the remote end we're connected to. +int MacAsyncSocket::Recv(void* buffer, size_t length) { + int received = ::recv(native_socket_, reinterpret_cast(buffer), + length, 0); + if (received == SOCKET_ERROR) error_ = errno; + + // Recv should only be called when there is data to read + ASSERT((received != 0) || (length == 0)); + return received; +} + +// Read data received from any remote party +int MacAsyncSocket::RecvFrom(void* buffer, size_t length, + SocketAddress* out_addr) { + sockaddr_storage saddr; + socklen_t addr_len = sizeof(saddr); + int received = ::recvfrom(native_socket_, reinterpret_cast(buffer), + length, 0, reinterpret_cast(&saddr), + &addr_len); + if (received >= 0 && out_addr != NULL) { + SocketAddressFromSockAddrStorage(saddr, out_addr); + } else if (received == SOCKET_ERROR) { + error_ = errno; + } + return received; +} + +int MacAsyncSocket::Listen(int backlog) { + if (!valid()) { + return SOCKET_ERROR; + } + + int res = ::listen(native_socket_, backlog); + if (res != SOCKET_ERROR) + state_ = CS_CONNECTING; + else + error_ = errno; + + return res; +} + +MacAsyncSocket* MacAsyncSocket::Accept(SocketAddress* out_addr) { + sockaddr_storage saddr; + socklen_t addr_len = sizeof(saddr); + + int socket_fd = ::accept(native_socket_, reinterpret_cast(&saddr), + &addr_len); + if (socket_fd == INVALID_SOCKET) { + error_ = errno; + return NULL; + } + + MacAsyncSocket* s = new MacAsyncSocket(ss_, saddr.ss_family, socket_fd); + if (s && s->valid()) { + s->state_ = CS_CONNECTED; + if (out_addr) + SocketAddressFromSockAddrStorage(saddr, out_addr); + } else { + delete s; + s = NULL; + } + return s; +} + +int MacAsyncSocket::Close() { + if (source_ != NULL) { + CFRunLoopSourceInvalidate(source_); + CFRelease(source_); + if (ss_) ss_->UnregisterSocket(this); + source_ = NULL; + } + + if (socket_ != NULL) { + CFSocketInvalidate(socket_); + CFRelease(socket_); + socket_ = NULL; + } + + if (resolver_) { + resolver_->Destroy(false); + resolver_ = NULL; + } + + native_socket_ = INVALID_SOCKET; // invalidates the socket + error_ = 0; + state_ = CS_CLOSED; + return 0; +} + +int MacAsyncSocket::EstimateMTU(uint16* mtu) { + ASSERT(false && "NYI"); + return -1; +} + +int MacAsyncSocket::GetError() const { + return error_; +} + +void MacAsyncSocket::SetError(int error) { + error_ = error; +} + +Socket::ConnState MacAsyncSocket::GetState() const { + return state_; +} + +int MacAsyncSocket::GetOption(Option opt, int* value) { + ASSERT(false && "NYI"); + return -1; +} + +int MacAsyncSocket::SetOption(Option opt, int value) { + ASSERT(false && "NYI"); + return -1; +} + +void MacAsyncSocket::EnableCallbacks() { + if (valid()) { + disabled_ = false; + CFSocketEnableCallBacks(socket_, current_callbacks_); + } +} + +void MacAsyncSocket::DisableCallbacks() { + if (valid()) { + disabled_ = true; + CFSocketDisableCallBacks(socket_, kCallbackFlags); + } +} + +MacAsyncSocket::MacAsyncSocket(MacBaseSocketServer* ss, int family, + int native_socket) + : ss_(ss), + socket_(NULL), + native_socket_(native_socket), + source_(NULL), + current_callbacks_(0), + disabled_(false), + error_(0), + state_(CS_CLOSED), + resolver_(NULL) { + Initialize(family); +} + +// Create a new socket, wrapping the native socket if provided or creating one +// otherwise. In case of any failure, consume the native socket. We assume the +// wrapped socket is in the closed state. If this is not the case you must +// update the state_ field for this socket yourself. +void MacAsyncSocket::Initialize(int family) { + CFSocketContext ctx = { 0 }; + ctx.info = this; + + // First create the CFSocket + CFSocketRef cf_socket = NULL; + bool res = false; + if (native_socket_ == INVALID_SOCKET) { + cf_socket = CFSocketCreate(kCFAllocatorDefault, + family, SOCK_STREAM, IPPROTO_TCP, + kCallbackFlags, MacAsyncSocketCallBack, &ctx); + } else { + cf_socket = CFSocketCreateWithNative(kCFAllocatorDefault, + native_socket_, kCallbackFlags, + MacAsyncSocketCallBack, &ctx); + } + + if (cf_socket) { + res = true; + socket_ = cf_socket; + native_socket_ = CFSocketGetNative(cf_socket); + current_callbacks_ = kCallbackFlags; + } + + if (res) { + // Make the underlying socket asynchronous + res = (-1 != ::fcntl(native_socket_, F_SETFL, + ::fcntl(native_socket_, F_GETFL, 0) | O_NONBLOCK)); + } + + if (res) { + // Add this socket to the run loop, at priority 1 so that it will be + // queued behind any pending signals. + source_ = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket_, 1); + res = (source_ != NULL); + if (!res) errno = EINVAL; + } + + if (res) { + if (ss_) ss_->RegisterSocket(this); + CFRunLoopAddSource(CFRunLoopGetCurrent(), source_, kCFRunLoopCommonModes); + } + + if (!res) { + int error = errno; + Close(); // Clears error_. + error_ = error; + } +} + +// Call CFRelease on the result when done using it +CFDataRef MacAsyncSocket::CopyCFAddress(const SocketAddress& address) { + sockaddr_storage saddr; + size_t len = address.ToSockAddrStorage(&saddr); + + const UInt8* bytes = reinterpret_cast(&saddr); + + CFDataRef cf_address = CFDataCreate(kCFAllocatorDefault, + bytes, len); + + ASSERT(cf_address != NULL); + return cf_address; +} + +void MacAsyncSocket::MacAsyncSocketCallBack(CFSocketRef s, + CFSocketCallBackType callbackType, + CFDataRef address, + const void* data, + void* info) { + MacAsyncSocket* this_socket = + reinterpret_cast(info); + ASSERT(this_socket != NULL && this_socket->socket_ == s); + + // Don't signal any socket messages if the socketserver is not listening on + // them. When we are reenabled they will be requeued and will fire again. + if (this_socket->disabled_) + return; + + switch (callbackType) { + case kCFSocketReadCallBack: + // This callback is invoked in one of 3 situations: + // 1. A new connection is waiting to be accepted. + // 2. The remote end closed the connection (a recv will return 0). + // 3. Data is available to read. + // 4. The connection closed unhappily (recv will return -1). + if (this_socket->state_ == CS_CONNECTING) { + // Case 1. + this_socket->SignalReadEvent(this_socket); + } else { + char ch, amt; + amt = ::recv(this_socket->native_socket_, &ch, 1, MSG_PEEK); + if (amt == 0) { + // Case 2. + this_socket->state_ = CS_CLOSED; + + // Disable additional callbacks or we will signal close twice. + CFSocketDisableCallBacks(this_socket->socket_, kCFSocketReadCallBack); + this_socket->current_callbacks_ &= ~kCFSocketReadCallBack; + this_socket->SignalCloseEvent(this_socket, 0); + } else if (amt > 0) { + // Case 3. + this_socket->SignalReadEvent(this_socket); + } else { + // Case 4. + int error = errno; + if (error == EAGAIN) { + // Observed in practice. Let's hope it's a spurious or out of date + // signal, since we just eat it. + } else { + this_socket->error_ = error; + this_socket->SignalCloseEvent(this_socket, error); + } + } + } + break; + + case kCFSocketConnectCallBack: + if (data != NULL) { + // An error occured in the background while connecting + this_socket->error_ = errno; + this_socket->state_ = CS_CLOSED; + this_socket->SignalCloseEvent(this_socket, this_socket->error_); + } else { + this_socket->state_ = CS_CONNECTED; + this_socket->SignalConnectEvent(this_socket); + } + break; + + case kCFSocketWriteCallBack: + // Update our callback tracking. Write doesn't reenable, so it's off now. + this_socket->current_callbacks_ &= ~kCFSocketWriteCallBack; + this_socket->SignalWriteEvent(this_socket); + break; + + default: + ASSERT(false && "Invalid callback type for socket"); + } +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macasyncsocket.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macasyncsocket.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macasyncsocket.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macasyncsocket.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,97 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +// MacAsyncSocket is a kind of AsyncSocket. It only creates sockets +// of the TCP type, and does not (yet) support listen and accept. It works +// asynchronously, which means that users of this socket should connect to +// the various events declared in asyncsocket.h to receive notifications about +// this socket. + +#ifndef WEBRTC_BASE_MACASYNCSOCKET_H__ +#define WEBRTC_BASE_MACASYNCSOCKET_H__ + +#include + +#include "webrtc/base/asyncsocket.h" +#include "webrtc/base/nethelpers.h" + +namespace rtc { + +class MacBaseSocketServer; + +class MacAsyncSocket : public AsyncSocket, public sigslot::has_slots<> { + public: + MacAsyncSocket(MacBaseSocketServer* ss, int family); + virtual ~MacAsyncSocket(); + + bool valid() const { return source_ != NULL; } + + // Socket interface + virtual SocketAddress GetLocalAddress() const; + virtual SocketAddress GetRemoteAddress() const; + virtual int Bind(const SocketAddress& addr); + virtual int Connect(const SocketAddress& addr); + virtual int Send(const void* buffer, size_t length); + virtual int SendTo(const void* buffer, size_t length, + const SocketAddress& addr); + virtual int Recv(void* buffer, size_t length); + virtual int RecvFrom(void* buffer, size_t length, SocketAddress* out_addr); + virtual int Listen(int backlog); + virtual MacAsyncSocket* Accept(SocketAddress* out_addr); + virtual int Close(); + virtual int GetError() const; + virtual void SetError(int error); + virtual ConnState GetState() const; + virtual int EstimateMTU(uint16* mtu); + virtual int GetOption(Option opt, int* value); + virtual int SetOption(Option opt, int value); + + // For the MacBaseSocketServer to disable callbacks when process_io is false. + void EnableCallbacks(); + void DisableCallbacks(); + + protected: + void OnResolveResult(SignalThread* thread); + int DoConnect(const SocketAddress& addr); + + private: + // Creates an async socket from an existing bsd socket + MacAsyncSocket(MacBaseSocketServer* ss, int family, int native_socket); + + // Attaches the socket to the CFRunloop and sets the wrapped bsd socket + // to async mode + void Initialize(int family); + + // Translate the SocketAddress into a CFDataRef to pass to CF socket + // functions. Caller must call CFRelease on the result when done. + static CFDataRef CopyCFAddress(const SocketAddress& address); + + // Callback for the underlying CFSocketRef. + static void MacAsyncSocketCallBack(CFSocketRef s, + CFSocketCallBackType callbackType, + CFDataRef address, + const void* data, + void* info); + + MacBaseSocketServer* ss_; + CFSocketRef socket_; + int native_socket_; + CFRunLoopSourceRef source_; + int current_callbacks_; + bool disabled_; + int error_; + ConnState state_; + AsyncResolver* resolver_; + + DISALLOW_EVIL_CONSTRUCTORS(MacAsyncSocket); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_MACASYNCSOCKET_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/maccocoasocketserver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/maccocoasocketserver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/maccocoasocketserver.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/maccocoasocketserver.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,48 @@ +/* + * Copyright 2007 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// A libjingle compatible SocketServer for OSX/iOS/Cocoa. + +#ifndef WEBRTC_BASE_MACCOCOASOCKETSERVER_H_ +#define WEBRTC_BASE_MACCOCOASOCKETSERVER_H_ + +#include "webrtc/base/macsocketserver.h" + +#ifdef __OBJC__ +@class NSTimer, MacCocoaSocketServerHelperRtc; +#else +class NSTimer; +class MacCocoaSocketServerHelperRtc; +#endif + +namespace rtc { + +// A socketserver implementation that wraps the main cocoa +// application loop accessed through [NSApp run]. +class MacCocoaSocketServer : public MacBaseSocketServer { + public: + explicit MacCocoaSocketServer(); + virtual ~MacCocoaSocketServer(); + + virtual bool Wait(int cms, bool process_io); + virtual void WakeUp(); + + private: + MacCocoaSocketServerHelperRtc* helper_; + NSTimer* timer_; // Weak. + // The count of how many times we're inside the NSApplication main loop. + int run_count_; + + DISALLOW_EVIL_CONSTRUCTORS(MacCocoaSocketServer); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_MACCOCOASOCKETSERVER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/maccocoasocketserver.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/maccocoasocketserver.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/maccocoasocketserver.mm 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/maccocoasocketserver.mm 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,140 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#import "webrtc/base/maccocoasocketserver.h" + +#import +#import +#include + +#include "webrtc/base/scoped_autorelease_pool.h" + +// MacCocoaSocketServerHelperRtc serves as a delegate to NSMachPort or a target for +// a timeout. +@interface MacCocoaSocketServerHelperRtc : NSObject { + // This is a weak reference. This works fine since the + // rtc::MacCocoaSocketServer owns this object. + rtc::MacCocoaSocketServer* socketServer_; // Weak. +} +@end + +@implementation MacCocoaSocketServerHelperRtc +- (id)initWithSocketServer:(rtc::MacCocoaSocketServer*)ss { + self = [super init]; + if (self) { + socketServer_ = ss; + } + return self; +} + +- (void)timerFired:(NSTimer*)timer { + socketServer_->WakeUp(); +} + +- (void)breakMainloop { + [NSApp stop:self]; + // NSApp stop only exits after finishing processing of the + // current event. Since we're potentially in a timer callback + // and not an NSEvent handler, we need to trigger a dummy one + // and turn the loop over. We may be able to skip this if we're + // on the ss' thread and not inside the app loop already. + NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined + location:NSMakePoint(0,0) + modifierFlags:0 + timestamp:0 + windowNumber:0 + context:nil + subtype:0 + data1:0 + data2:0]; + [NSApp postEvent:event atStart:NO]; +} +@end + +namespace rtc { + +MacCocoaSocketServer::MacCocoaSocketServer() { + helper_ = [[MacCocoaSocketServerHelperRtc alloc] initWithSocketServer:this]; + timer_ = nil; + run_count_ = 0; + + // Initialize the shared NSApplication + [NSApplication sharedApplication]; +} + +MacCocoaSocketServer::~MacCocoaSocketServer() { + [timer_ invalidate]; + [timer_ release]; + [helper_ release]; +} + +// ::Wait is reentrant, for example when blocking on another thread while +// responding to I/O. Calls to [NSApp] MUST be made from the main thread +// only! +bool MacCocoaSocketServer::Wait(int cms, bool process_io) { + rtc::ScopedAutoreleasePool pool; + if (!process_io && cms == 0) { + // No op. + return true; + } + if ([NSApp isRunning]) { + // Only allow reentrant waiting if we're in a blocking send. + ASSERT(!process_io && cms == kForever); + } + + if (!process_io) { + // No way to listen to common modes and not get socket events, unless + // we disable each one's callbacks. + EnableSocketCallbacks(false); + } + + if (kForever != cms) { + // Install a timer that fires wakeup after cms has elapsed. + timer_ = + [NSTimer scheduledTimerWithTimeInterval:cms / 1000.0 + target:helper_ + selector:@selector(timerFired:) + userInfo:nil + repeats:NO]; + [timer_ retain]; + } + + // Run until WakeUp is called, which will call stop and exit this loop. + run_count_++; + [NSApp run]; + run_count_--; + + if (!process_io) { + // Reenable them. Hopefully this won't cause spurious callbacks or + // missing ones while they were disabled. + EnableSocketCallbacks(true); + } + + return true; +} + +// Can be called from any thread. Post a message back to the main thread to +// break out of the NSApp loop. +void MacCocoaSocketServer::WakeUp() { + if (timer_ != nil) { + [timer_ invalidate]; + [timer_ release]; + timer_ = nil; + } + + // [NSApp isRunning] returns unexpected results when called from another + // thread. Maintain our own count of how many times to break the main loop. + if (run_count_ > 0) { + [helper_ performSelectorOnMainThread:@selector(breakMainloop) + withObject:nil + waitUntilDone:false]; + } +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/maccocoasocketserver_unittest.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/maccocoasocketserver_unittest.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/maccocoasocketserver_unittest.mm 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/maccocoasocketserver_unittest.mm 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/maccocoasocketserver.h" + +namespace rtc { + +class WakeThread : public Thread { + public: + WakeThread(SocketServer* ss) : ss_(ss) { + } + virtual ~WakeThread() { + Stop(); + } + void Run() { + ss_->WakeUp(); + } + private: + SocketServer* ss_; +}; + +// Test that MacCocoaSocketServer::Wait works as expected. +TEST(MacCocoaSocketServer, TestWait) { + MacCocoaSocketServer server; + uint32 start = Time(); + server.Wait(1000, true); + EXPECT_GE(TimeSince(start), 1000); +} + +// Test that MacCocoaSocketServer::Wakeup works as expected. +TEST(MacCocoaSocketServer, TestWakeup) { + MacCFSocketServer server; + WakeThread thread(&server); + uint32 start = Time(); + thread.Start(); + server.Wait(10000, true); + EXPECT_LT(TimeSince(start), 10000); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/maccocoathreadhelper.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/maccocoathreadhelper.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/maccocoathreadhelper.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/maccocoathreadhelper.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,27 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Helper function for using Cocoa with Posix threads. This header should be +// included from C/C++ files that want to use some Cocoa functionality without +// using the .mm extension (mostly for files that are compiled on multiple +// platforms). + +#ifndef WEBRTC_BASE_MACCOCOATHREADHELPER_H__ +#define WEBRTC_BASE_MACCOCOATHREADHELPER_H__ + +namespace rtc { + +// Cocoa must be "put into multithreading mode" before Cocoa functionality can +// be used on POSIX threads. This function does that. +void InitCocoaMultiThreading(); + +} // namespace rtc + +#endif // WEBRTC_BASE_MACCOCOATHREADHELPER_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/maccocoathreadhelper.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/maccocoathreadhelper.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/maccocoathreadhelper.mm 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/maccocoathreadhelper.mm 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright 2007 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +// Helper function for using Cocoa with Posix threading. + +#import +#import + +#import "webrtc/base/maccocoathreadhelper.h" + +namespace rtc { + +// Cocoa must be "put into multithreading mode" before Cocoa functionality can +// be used on POSIX threads. The way to do that is to spawn one thread that may +// immediately exit. +void InitCocoaMultiThreading() { + if ([NSThread isMultiThreaded] == NO) { + // The sole purpose of this autorelease pool is to avoid a console + // message on Leopard that tells us we're autoreleasing the thread + // with no autorelease pool in place. + // Doing NSAutoreleasePool* hack = [[NSAutoreleasePool alloc] init]; + // causes unused variable error. + NSAutoreleasePool* hack; + hack = [[NSAutoreleasePool alloc] init]; + [NSThread detachNewThreadSelector:@selector(class) + toTarget:[NSObject class] + withObject:nil]; + [hack drain]; + } + + assert([NSThread isMultiThreaded]); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macconversion.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macconversion.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macconversion.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macconversion.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,159 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + +#include + +#include "webrtc/base/logging.h" +#include "webrtc/base/macconversion.h" + +bool p_convertHostCFStringRefToCPPString( + const CFStringRef cfstr, std::string& cppstr) { + bool result = false; + + // First this must be non-null, + if (NULL != cfstr) { + // it must actually *be* a CFString, and not something just masquerading + // as one, + if (CFGetTypeID(cfstr) == CFStringGetTypeID()) { + // and we must be able to get the characters out of it. + // (The cfstr owns this buffer; it came from somewhere else, + // so someone else gets to take care of getting rid of the cfstr, + // and then this buffer will go away automatically.) + unsigned length = CFStringGetLength(cfstr); + char* buf = new char[1 + length]; + if (CFStringGetCString(cfstr, buf, 1 + length, kCFStringEncodingASCII)) { + if (strlen(buf) == length) { + cppstr.assign(buf); + result = true; + } + } + delete [] buf; + } + } + + return result; +} + +bool p_convertCFNumberToInt(CFNumberRef cfn, int* i) { + bool converted = false; + + // It must not be null. + if (NULL != cfn) { + // It must actually *be* a CFNumber and not something just masquerading + // as one. + if (CFGetTypeID(cfn) == CFNumberGetTypeID()) { + CFNumberType ntype = CFNumberGetType(cfn); + switch (ntype) { + case kCFNumberSInt8Type: + SInt8 sint8; + converted = CFNumberGetValue(cfn, ntype, static_cast(&sint8)); + if (converted) *i = static_cast(sint8); + break; + case kCFNumberSInt16Type: + SInt16 sint16; + converted = CFNumberGetValue(cfn, ntype, static_cast(&sint16)); + if (converted) *i = static_cast(sint16); + break; + case kCFNumberSInt32Type: + SInt32 sint32; + converted = CFNumberGetValue(cfn, ntype, static_cast(&sint32)); + if (converted) *i = static_cast(sint32); + break; + case kCFNumberSInt64Type: + SInt64 sint64; + converted = CFNumberGetValue(cfn, ntype, static_cast(&sint64)); + if (converted) *i = static_cast(sint64); + break; + case kCFNumberFloat32Type: + Float32 float32; + converted = CFNumberGetValue(cfn, ntype, + static_cast(&float32)); + if (converted) *i = static_cast(float32); + break; + case kCFNumberFloat64Type: + Float64 float64; + converted = CFNumberGetValue(cfn, ntype, + static_cast(&float64)); + if (converted) *i = static_cast(float64); + break; + case kCFNumberCharType: + char charvalue; + converted = CFNumberGetValue(cfn, ntype, + static_cast(&charvalue)); + if (converted) *i = static_cast(charvalue); + break; + case kCFNumberShortType: + short shortvalue; + converted = CFNumberGetValue(cfn, ntype, + static_cast(&shortvalue)); + if (converted) *i = static_cast(shortvalue); + break; + case kCFNumberIntType: + int intvalue; + converted = CFNumberGetValue(cfn, ntype, + static_cast(&intvalue)); + if (converted) *i = static_cast(intvalue); + break; + case kCFNumberLongType: + long longvalue; + converted = CFNumberGetValue(cfn, ntype, + static_cast(&longvalue)); + if (converted) *i = static_cast(longvalue); + break; + case kCFNumberLongLongType: + long long llvalue; + converted = CFNumberGetValue(cfn, ntype, + static_cast(&llvalue)); + if (converted) *i = static_cast(llvalue); + break; + case kCFNumberFloatType: + float floatvalue; + converted = CFNumberGetValue(cfn, ntype, + static_cast(&floatvalue)); + if (converted) *i = static_cast(floatvalue); + break; + case kCFNumberDoubleType: + double doublevalue; + converted = CFNumberGetValue(cfn, ntype, + static_cast(&doublevalue)); + if (converted) *i = static_cast(doublevalue); + break; + case kCFNumberCFIndexType: + CFIndex cfindex; + converted = CFNumberGetValue(cfn, ntype, + static_cast(&cfindex)); + if (converted) *i = static_cast(cfindex); + break; + default: + LOG(LS_ERROR) << "got unknown type."; + break; + } + } + } + + return converted; +} + +bool p_isCFNumberTrue(CFNumberRef cfn) { + // We assume it's false until proven otherwise. + bool result = false; + int asInt; + bool converted = p_convertCFNumberToInt(cfn, &asInt); + + if (converted && (0 != asInt)) { + result = true; + } + + return result; +} + +#endif // WEBRTC_MAC && !defined(WEBRTC_IOS) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macconversion.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macconversion.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macconversion.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macconversion.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,39 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_MACCONVERSION_H_ +#define WEBRTC_BASE_MACCONVERSION_H_ + +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + +#include + +#include + +// given a CFStringRef, attempt to convert it to a C++ string. +// returns true if it succeeds, false otherwise. +// We can safely assume, given our context, that the string is +// going to be in ASCII, because it will either be an IP address, +// or a domain name, which is guaranteed to be ASCII-representable. +bool p_convertHostCFStringRefToCPPString(const CFStringRef cfstr, + std::string& cppstr); + +// Convert the CFNumber to an integer, putting the integer in the location +// given, and returhing true, if the conversion succeeds. +// If given a NULL or a non-CFNumber, returns false. +// This is pretty aggresive about trying to convert to int. +bool p_convertCFNumberToInt(CFNumberRef cfn, int* i); + +// given a CFNumberRef, determine if it represents a true value. +bool p_isCFNumberTrue(CFNumberRef cfn); + +#endif // WEBRTC_MAC && !defined(WEBRTC_IOS) + +#endif // WEBRTC_BASE_MACCONVERSION_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macsocketserver.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macsocketserver.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macsocketserver.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macsocketserver.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,378 @@ +/* + * Copyright 2007 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + + +#include "webrtc/base/macsocketserver.h" + +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/macasyncsocket.h" +#include "webrtc/base/macutils.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// MacBaseSocketServer +/////////////////////////////////////////////////////////////////////////////// + +MacBaseSocketServer::MacBaseSocketServer() { +} + +MacBaseSocketServer::~MacBaseSocketServer() { +} + +AsyncSocket* MacBaseSocketServer::CreateAsyncSocket(int type) { + return CreateAsyncSocket(AF_INET, type); +} + +AsyncSocket* MacBaseSocketServer::CreateAsyncSocket(int family, int type) { + if (SOCK_STREAM != type) + return NULL; + + MacAsyncSocket* socket = new MacAsyncSocket(this, family); + if (!socket->valid()) { + delete socket; + return NULL; + } + return socket; +} + +void MacBaseSocketServer::RegisterSocket(MacAsyncSocket* s) { + sockets_.insert(s); +} + +void MacBaseSocketServer::UnregisterSocket(MacAsyncSocket* s) { + VERIFY(1 == sockets_.erase(s)); // found 1 +} + +bool MacBaseSocketServer::SetPosixSignalHandler(int signum, + void (*handler)(int)) { + Dispatcher* dispatcher = signal_dispatcher(); + if (!PhysicalSocketServer::SetPosixSignalHandler(signum, handler)) { + return false; + } + + // Only register the FD once, when the first custom handler is installed. + if (!dispatcher && (dispatcher = signal_dispatcher())) { + CFFileDescriptorContext ctx = { 0 }; + ctx.info = this; + + CFFileDescriptorRef desc = CFFileDescriptorCreate( + kCFAllocatorDefault, + dispatcher->GetDescriptor(), + false, + &MacBaseSocketServer::FileDescriptorCallback, + &ctx); + if (!desc) { + return false; + } + + CFFileDescriptorEnableCallBacks(desc, kCFFileDescriptorReadCallBack); + CFRunLoopSourceRef ref = + CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, desc, 0); + + if (!ref) { + CFRelease(desc); + return false; + } + + CFRunLoopAddSource(CFRunLoopGetCurrent(), ref, kCFRunLoopCommonModes); + CFRelease(desc); + CFRelease(ref); + } + + return true; +} + +// Used to disable socket events from waking our message queue when +// process_io is false. Does not disable signal event handling though. +void MacBaseSocketServer::EnableSocketCallbacks(bool enable) { + for (std::set::iterator it = sockets().begin(); + it != sockets().end(); ++it) { + if (enable) { + (*it)->EnableCallbacks(); + } else { + (*it)->DisableCallbacks(); + } + } +} + +void MacBaseSocketServer::FileDescriptorCallback(CFFileDescriptorRef fd, + CFOptionFlags flags, + void* context) { + MacBaseSocketServer* this_ss = + reinterpret_cast(context); + ASSERT(this_ss); + Dispatcher* signal_dispatcher = this_ss->signal_dispatcher(); + ASSERT(signal_dispatcher); + + signal_dispatcher->OnPreEvent(DE_READ); + signal_dispatcher->OnEvent(DE_READ, 0); + CFFileDescriptorEnableCallBacks(fd, kCFFileDescriptorReadCallBack); +} + + +/////////////////////////////////////////////////////////////////////////////// +// MacCFSocketServer +/////////////////////////////////////////////////////////////////////////////// + +void WakeUpCallback(void* info) { + MacCFSocketServer* server = static_cast(info); + ASSERT(NULL != server); + server->OnWakeUpCallback(); +} + +MacCFSocketServer::MacCFSocketServer() + : run_loop_(CFRunLoopGetCurrent()), + wake_up_(NULL) { + CFRunLoopSourceContext ctx; + memset(&ctx, 0, sizeof(ctx)); + ctx.info = this; + ctx.perform = &WakeUpCallback; + wake_up_ = CFRunLoopSourceCreate(NULL, 0, &ctx); + ASSERT(NULL != wake_up_); + if (wake_up_) { + CFRunLoopAddSource(run_loop_, wake_up_, kCFRunLoopCommonModes); + } +} + +MacCFSocketServer::~MacCFSocketServer() { + if (wake_up_) { + CFRunLoopSourceInvalidate(wake_up_); + CFRelease(wake_up_); + } +} + +bool MacCFSocketServer::Wait(int cms, bool process_io) { + ASSERT(CFRunLoopGetCurrent() == run_loop_); + + if (!process_io && cms == 0) { + // No op. + return true; + } + + if (!process_io) { + // No way to listen to common modes and not get socket events, unless + // we disable each one's callbacks. + EnableSocketCallbacks(false); + } + + SInt32 result; + if (kForever == cms) { + do { + // Would prefer to run in a custom mode that only listens to wake_up, + // but we have qtkit sending work to the main thread which is effectively + // blocked here, causing deadlock. Thus listen to the common modes. + // TODO: If QTKit becomes thread safe, do the above. + result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10000000, false); + } while (result != kCFRunLoopRunFinished && result != kCFRunLoopRunStopped); + } else { + // TODO: In the case of 0ms wait, this will only process one event, so we + // may want to loop until it returns TimedOut. + CFTimeInterval seconds = cms / 1000.0; + result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, false); + } + + if (!process_io) { + // Reenable them. Hopefully this won't cause spurious callbacks or + // missing ones while they were disabled. + EnableSocketCallbacks(true); + } + + if (kCFRunLoopRunFinished == result) { + return false; + } + return true; +} + +void MacCFSocketServer::WakeUp() { + if (wake_up_) { + CFRunLoopSourceSignal(wake_up_); + CFRunLoopWakeUp(run_loop_); + } +} + +void MacCFSocketServer::OnWakeUpCallback() { + ASSERT(run_loop_ == CFRunLoopGetCurrent()); + CFRunLoopStop(run_loop_); +} + +/////////////////////////////////////////////////////////////////////////////// +// MacCarbonSocketServer +/////////////////////////////////////////////////////////////////////////////// +#ifndef CARBON_DEPRECATED + +const UInt32 kEventClassSocketServer = 'MCSS'; +const UInt32 kEventWakeUp = 'WAKE'; +const EventTypeSpec kEventWakeUpSpec[] = { + { kEventClassSocketServer, kEventWakeUp } +}; + +std::string DecodeEvent(EventRef event) { + std::string str; + DecodeFourChar(::GetEventClass(event), &str); + str.push_back(':'); + DecodeFourChar(::GetEventKind(event), &str); + return str; +} + +MacCarbonSocketServer::MacCarbonSocketServer() + : event_queue_(GetCurrentEventQueue()), wake_up_(NULL) { + VERIFY(noErr == CreateEvent(NULL, kEventClassSocketServer, kEventWakeUp, 0, + kEventAttributeUserEvent, &wake_up_)); +} + +MacCarbonSocketServer::~MacCarbonSocketServer() { + if (wake_up_) { + ReleaseEvent(wake_up_); + } +} + +bool MacCarbonSocketServer::Wait(int cms, bool process_io) { + ASSERT(GetCurrentEventQueue() == event_queue_); + + // Listen to all events if we're processing I/O. + // Only listen for our wakeup event if we're not. + UInt32 num_types = 0; + const EventTypeSpec* events = NULL; + if (!process_io) { + num_types = GetEventTypeCount(kEventWakeUpSpec); + events = kEventWakeUpSpec; + } + + EventTargetRef target = GetEventDispatcherTarget(); + EventTimeout timeout = + (kForever == cms) ? kEventDurationForever : cms / 1000.0; + EventTimeout end_time = GetCurrentEventTime() + timeout; + + bool done = false; + while (!done) { + EventRef event; + OSStatus result = ReceiveNextEvent(num_types, events, timeout, true, + &event); + if (noErr == result) { + if (wake_up_ != event) { + LOG_F(LS_VERBOSE) << "Dispatching event: " << DecodeEvent(event); + result = SendEventToEventTarget(event, target); + if ((noErr != result) && (eventNotHandledErr != result)) { + LOG_E(LS_ERROR, OS, result) << "SendEventToEventTarget"; + } + } else { + done = true; + } + ReleaseEvent(event); + } else if (eventLoopTimedOutErr == result) { + ASSERT(cms != kForever); + done = true; + } else if (eventLoopQuitErr == result) { + // Ignore this... we get spurious quits for a variety of reasons. + LOG_E(LS_VERBOSE, OS, result) << "ReceiveNextEvent"; + } else { + // Some strange error occurred. Log it. + LOG_E(LS_WARNING, OS, result) << "ReceiveNextEvent"; + return false; + } + if (kForever != cms) { + timeout = end_time - GetCurrentEventTime(); + } + } + return true; +} + +void MacCarbonSocketServer::WakeUp() { + if (!IsEventInQueue(event_queue_, wake_up_)) { + RetainEvent(wake_up_); + OSStatus result = PostEventToQueue(event_queue_, wake_up_, + kEventPriorityStandard); + if (noErr != result) { + LOG_E(LS_ERROR, OS, result) << "PostEventToQueue"; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// MacCarbonAppSocketServer +/////////////////////////////////////////////////////////////////////////////// + +MacCarbonAppSocketServer::MacCarbonAppSocketServer() + : event_queue_(GetCurrentEventQueue()) { + // Install event handler + VERIFY(noErr == InstallApplicationEventHandler( + NewEventHandlerUPP(WakeUpEventHandler), 1, kEventWakeUpSpec, this, + &event_handler_)); + + // Install a timer and set it idle to begin with. + VERIFY(noErr == InstallEventLoopTimer(GetMainEventLoop(), + kEventDurationForever, + kEventDurationForever, + NewEventLoopTimerUPP(TimerHandler), + this, + &timer_)); +} + +MacCarbonAppSocketServer::~MacCarbonAppSocketServer() { + RemoveEventLoopTimer(timer_); + RemoveEventHandler(event_handler_); +} + +OSStatus MacCarbonAppSocketServer::WakeUpEventHandler( + EventHandlerCallRef next, EventRef event, void *data) { + QuitApplicationEventLoop(); + return noErr; +} + +void MacCarbonAppSocketServer::TimerHandler( + EventLoopTimerRef timer, void *data) { + QuitApplicationEventLoop(); +} + +bool MacCarbonAppSocketServer::Wait(int cms, bool process_io) { + if (!process_io && cms == 0) { + // No op. + return true; + } + if (kForever != cms) { + // Start a timer. + OSStatus error = + SetEventLoopTimerNextFireTime(timer_, cms / 1000.0); + if (error != noErr) { + LOG(LS_ERROR) << "Failed setting next fire time."; + } + } + if (!process_io) { + // No way to listen to common modes and not get socket events, unless + // we disable each one's callbacks. + EnableSocketCallbacks(false); + } + RunApplicationEventLoop(); + if (!process_io) { + // Reenable them. Hopefully this won't cause spurious callbacks or + // missing ones while they were disabled. + EnableSocketCallbacks(true); + } + return true; +} + +void MacCarbonAppSocketServer::WakeUp() { + // TODO: No-op if there's already a WakeUp in flight. + EventRef wake_up; + VERIFY(noErr == CreateEvent(NULL, kEventClassSocketServer, kEventWakeUp, 0, + kEventAttributeUserEvent, &wake_up)); + OSStatus result = PostEventToQueue(event_queue_, wake_up, + kEventPriorityStandard); + if (noErr != result) { + LOG_E(LS_ERROR, OS, result) << "PostEventToQueue"; + } + ReleaseEvent(wake_up); +} + +#endif +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macsocketserver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macsocketserver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macsocketserver.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macsocketserver.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,136 @@ +/* + * Copyright 2007 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef WEBRTC_BASE_MACSOCKETSERVER_H__ +#define WEBRTC_BASE_MACSOCKETSERVER_H__ + +#include +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) // Invalid on IOS +#include +#endif +#include "webrtc/base/physicalsocketserver.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// MacBaseSocketServer +/////////////////////////////////////////////////////////////////////////////// +class MacAsyncSocket; + +class MacBaseSocketServer : public PhysicalSocketServer { + public: + MacBaseSocketServer(); + virtual ~MacBaseSocketServer(); + + // SocketServer Interface + virtual Socket* CreateSocket(int type) { return NULL; } + virtual Socket* CreateSocket(int family, int type) { return NULL; } + + virtual AsyncSocket* CreateAsyncSocket(int type); + virtual AsyncSocket* CreateAsyncSocket(int family, int type); + + virtual bool Wait(int cms, bool process_io) = 0; + virtual void WakeUp() = 0; + + void RegisterSocket(MacAsyncSocket* socket); + void UnregisterSocket(MacAsyncSocket* socket); + + // PhysicalSocketServer Overrides + virtual bool SetPosixSignalHandler(int signum, void (*handler)(int)); + + protected: + void EnableSocketCallbacks(bool enable); + const std::set& sockets() { + return sockets_; + } + + private: + static void FileDescriptorCallback(CFFileDescriptorRef ref, + CFOptionFlags flags, + void* context); + + std::set sockets_; +}; + +// Core Foundation implementation of the socket server. While idle it +// will run the current CF run loop. When the socket server has work +// to do the run loop will be paused. Does not support Carbon or Cocoa +// UI interaction. +class MacCFSocketServer : public MacBaseSocketServer { + public: + MacCFSocketServer(); + virtual ~MacCFSocketServer(); + + // SocketServer Interface + virtual bool Wait(int cms, bool process_io); + virtual void WakeUp(); + void OnWakeUpCallback(); + + private: + CFRunLoopRef run_loop_; + CFRunLoopSourceRef wake_up_; +}; + +#ifndef CARBON_DEPRECATED + +/////////////////////////////////////////////////////////////////////////////// +// MacCarbonSocketServer +/////////////////////////////////////////////////////////////////////////////// + +// Interacts with the Carbon event queue. While idle it will block, +// waiting for events. When the socket server has work to do, it will +// post a 'wake up' event to the queue, causing the thread to exit the +// event loop until the next call to Wait. Other events are dispatched +// to their target. Supports Carbon and Cocoa UI interaction. +class MacCarbonSocketServer : public MacBaseSocketServer { + public: + MacCarbonSocketServer(); + virtual ~MacCarbonSocketServer(); + + // SocketServer Interface + virtual bool Wait(int cms, bool process_io); + virtual void WakeUp(); + + private: + EventQueueRef event_queue_; + EventRef wake_up_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// MacCarbonAppSocketServer +/////////////////////////////////////////////////////////////////////////////// + +// Runs the Carbon application event loop on the current thread while +// idle. When the socket server has work to do, it will post an event +// to the queue, causing the thread to exit the event loop until the +// next call to Wait. Other events are automatically dispatched to +// their target. +class MacCarbonAppSocketServer : public MacBaseSocketServer { + public: + MacCarbonAppSocketServer(); + virtual ~MacCarbonAppSocketServer(); + + // SocketServer Interface + virtual bool Wait(int cms, bool process_io); + virtual void WakeUp(); + + private: + static OSStatus WakeUpEventHandler(EventHandlerCallRef next, EventRef event, + void *data); + static void TimerHandler(EventLoopTimerRef timer, void *data); + + EventQueueRef event_queue_; + EventHandlerRef event_handler_; + EventLoopTimerRef timer_; +}; + +#endif +} // namespace rtc + +#endif // WEBRTC_BASE_MACSOCKETSERVER_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macsocketserver_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macsocketserver_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macsocketserver_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macsocketserver_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,237 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/socket_unittest.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/macsocketserver.h" + +namespace rtc { + +class WakeThread : public Thread { + public: + WakeThread(SocketServer* ss) : ss_(ss) { + } + virtual ~WakeThread() { + Stop(); + } + void Run() { + ss_->WakeUp(); + } + private: + SocketServer* ss_; +}; + +#ifndef CARBON_DEPRECATED + +// Test that MacCFSocketServer::Wait works as expected. +TEST(MacCFSocketServerTest, TestWait) { + MacCFSocketServer server; + uint32 start = Time(); + server.Wait(1000, true); + EXPECT_GE(TimeSince(start), 1000); +} + +// Test that MacCFSocketServer::Wakeup works as expected. +TEST(MacCFSocketServerTest, TestWakeup) { + MacCFSocketServer server; + WakeThread thread(&server); + uint32 start = Time(); + thread.Start(); + server.Wait(10000, true); + EXPECT_LT(TimeSince(start), 10000); +} + +// Test that MacCarbonSocketServer::Wait works as expected. +TEST(MacCarbonSocketServerTest, TestWait) { + MacCarbonSocketServer server; + uint32 start = Time(); + server.Wait(1000, true); + EXPECT_GE(TimeSince(start), 1000); +} + +// Test that MacCarbonSocketServer::Wakeup works as expected. +TEST(MacCarbonSocketServerTest, TestWakeup) { + MacCarbonSocketServer server; + WakeThread thread(&server); + uint32 start = Time(); + thread.Start(); + server.Wait(10000, true); + EXPECT_LT(TimeSince(start), 10000); +} + +// Test that MacCarbonAppSocketServer::Wait works as expected. +TEST(MacCarbonAppSocketServerTest, TestWait) { + MacCarbonAppSocketServer server; + uint32 start = Time(); + server.Wait(1000, true); + EXPECT_GE(TimeSince(start), 1000); +} + +// Test that MacCarbonAppSocketServer::Wakeup works as expected. +TEST(MacCarbonAppSocketServerTest, TestWakeup) { + MacCarbonAppSocketServer server; + WakeThread thread(&server); + uint32 start = Time(); + thread.Start(); + server.Wait(10000, true); + EXPECT_LT(TimeSince(start), 10000); +} + +#endif + +// Test that MacAsyncSocket passes all the generic Socket tests. +class MacAsyncSocketTest : public SocketTest { + protected: + MacAsyncSocketTest() + : server_(CreateSocketServer()), + scope_(server_.get()) {} + // Override for other implementations of MacBaseSocketServer. + virtual MacBaseSocketServer* CreateSocketServer() { + return new MacCFSocketServer(); + }; + rtc::scoped_ptr server_; + SocketServerScope scope_; +}; + +TEST_F(MacAsyncSocketTest, TestConnectIPv4) { + SocketTest::TestConnectIPv4(); +} + +TEST_F(MacAsyncSocketTest, TestConnectIPv6) { + SocketTest::TestConnectIPv6(); +} + +TEST_F(MacAsyncSocketTest, TestConnectWithDnsLookupIPv4) { + SocketTest::TestConnectWithDnsLookupIPv4(); +} + +TEST_F(MacAsyncSocketTest, TestConnectWithDnsLookupIPv6) { + SocketTest::TestConnectWithDnsLookupIPv6(); +} + +// BUG=https://code.google.com/p/webrtc/issues/detail?id=2272 +TEST_F(MacAsyncSocketTest, DISABLED_TestConnectFailIPv4) { + SocketTest::TestConnectFailIPv4(); +} + +TEST_F(MacAsyncSocketTest, TestConnectFailIPv6) { + SocketTest::TestConnectFailIPv6(); +} + +// Reenable once we have mac async dns +TEST_F(MacAsyncSocketTest, DISABLED_TestConnectWithDnsLookupFailIPv4) { + SocketTest::TestConnectWithDnsLookupFailIPv4(); +} + +TEST_F(MacAsyncSocketTest, DISABLED_TestConnectWithDnsLookupFailIPv6) { + SocketTest::TestConnectWithDnsLookupFailIPv6(); +} + +TEST_F(MacAsyncSocketTest, TestConnectWithClosedSocketIPv4) { + SocketTest::TestConnectWithClosedSocketIPv4(); +} + +TEST_F(MacAsyncSocketTest, TestConnectWithClosedSocketIPv6) { + SocketTest::TestConnectWithClosedSocketIPv6(); +} + +// Flaky at the moment (10% failure rate). Seems the client doesn't get +// signalled in a timely manner... +TEST_F(MacAsyncSocketTest, DISABLED_TestServerCloseDuringConnectIPv4) { + SocketTest::TestServerCloseDuringConnectIPv4(); +} + +TEST_F(MacAsyncSocketTest, DISABLED_TestServerCloseDuringConnectIPv6) { + SocketTest::TestServerCloseDuringConnectIPv6(); +} +// Flaky at the moment (0.5% failure rate). Seems the client doesn't get +// signalled in a timely manner... +TEST_F(MacAsyncSocketTest, TestClientCloseDuringConnectIPv4) { + SocketTest::TestClientCloseDuringConnectIPv4(); +} + +TEST_F(MacAsyncSocketTest, TestClientCloseDuringConnectIPv6) { + SocketTest::TestClientCloseDuringConnectIPv6(); +} + +TEST_F(MacAsyncSocketTest, TestServerCloseIPv4) { + SocketTest::TestServerCloseIPv4(); +} + +TEST_F(MacAsyncSocketTest, TestServerCloseIPv6) { + SocketTest::TestServerCloseIPv6(); +} + +TEST_F(MacAsyncSocketTest, TestCloseInClosedCallbackIPv4) { + SocketTest::TestCloseInClosedCallbackIPv4(); +} + +TEST_F(MacAsyncSocketTest, TestCloseInClosedCallbackIPv6) { + SocketTest::TestCloseInClosedCallbackIPv6(); +} + +TEST_F(MacAsyncSocketTest, TestSocketServerWaitIPv4) { + SocketTest::TestSocketServerWaitIPv4(); +} + +TEST_F(MacAsyncSocketTest, TestSocketServerWaitIPv6) { + SocketTest::TestSocketServerWaitIPv6(); +} + +TEST_F(MacAsyncSocketTest, TestTcpIPv4) { + SocketTest::TestTcpIPv4(); +} + +TEST_F(MacAsyncSocketTest, TestTcpIPv6) { + SocketTest::TestTcpIPv6(); +} + +TEST_F(MacAsyncSocketTest, TestSingleFlowControlCallbackIPv4) { + SocketTest::TestSingleFlowControlCallbackIPv4(); +} + +TEST_F(MacAsyncSocketTest, TestSingleFlowControlCallbackIPv6) { + SocketTest::TestSingleFlowControlCallbackIPv6(); +} + +TEST_F(MacAsyncSocketTest, DISABLED_TestUdpIPv4) { + SocketTest::TestUdpIPv4(); +} + +TEST_F(MacAsyncSocketTest, DISABLED_TestUdpIPv6) { + SocketTest::TestUdpIPv6(); +} + +TEST_F(MacAsyncSocketTest, DISABLED_TestGetSetOptionsIPv4) { + SocketTest::TestGetSetOptionsIPv4(); +} + +TEST_F(MacAsyncSocketTest, DISABLED_TestGetSetOptionsIPv6) { + SocketTest::TestGetSetOptionsIPv6(); +} + +#ifndef CARBON_DEPRECATED +class MacCarbonAppAsyncSocketTest : public MacAsyncSocketTest { + virtual MacBaseSocketServer* CreateSocketServer() { + return new MacCarbonAppSocketServer(); + }; +}; + +TEST_F(MacCarbonAppAsyncSocketTest, TestSocketServerWaitIPv4) { + SocketTest::TestSocketServerWaitIPv4(); +} + +TEST_F(MacCarbonAppAsyncSocketTest, TestSocketServerWaitIPv6) { + SocketTest::TestSocketServerWaitIPv6(); +} +#endif +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macutils.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macutils.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macutils.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macutils.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,232 @@ +/* + * Copyright 2007 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#ifndef WEBRTC_MOZILLA_BUILD +#include "webrtc/base/common.h" +#endif +#include "webrtc/base/logging.h" +#include "webrtc/base/macutils.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/stringutils.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// + +bool ToUtf8(const CFStringRef str16, std::string* str8) { + if ((NULL == str16) || (NULL == str8)) { + return false; + } + size_t maxlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str16), + kCFStringEncodingUTF8) + 1; + scoped_ptr buffer(new char[maxlen]); + if (!buffer || !CFStringGetCString(str16, buffer.get(), maxlen, + kCFStringEncodingUTF8)) { + return false; + } + str8->assign(buffer.get()); + return true; +} + +bool ToUtf16(const std::string& str8, CFStringRef* str16) { + if (NULL == str16) { + return false; + } + *str16 = CFStringCreateWithBytes(kCFAllocatorDefault, + reinterpret_cast(str8.data()), + str8.length(), kCFStringEncodingUTF8, + false); + return NULL != *str16; +} + +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +void DecodeFourChar(UInt32 fc, std::string* out) { + std::stringstream ss; + ss << '\''; + bool printable = true; + for (int i = 3; i >= 0; --i) { + char ch = (fc >> (8 * i)) & 0xFF; + if (isprint(static_cast(ch))) { + ss << ch; + } else { + printable = false; + break; + } + } + if (printable) { + ss << '\''; + } else { + ss.str(""); + ss << "0x" << std::hex << fc; + } + out->append(ss.str()); +} + +static bool GetGestalt(OSType ostype, int* value) { +#ifndef WEBRTC_MOZILLA_BUILD + ASSERT(NULL != value); +#endif + SInt32 native_value; + OSStatus result = Gestalt(ostype, &native_value); + if (noErr == result) { + *value = native_value; + return true; + } + std::string str; + DecodeFourChar(ostype, &str); +#ifndef WEBRTC_MOZILLA_BUILD + LOG_E(LS_ERROR, OS, result) << "Gestalt(" << str << ")"; +#endif + return false; +} + +bool GetOSVersion(int* major, int* minor, int* bugfix) { +#ifndef WEBRTC_MOZILLA_BUILD + ASSERT(major && minor && bugfix); +#endif + if (!GetGestalt(gestaltSystemVersion, major)) { + return false; + } + if (*major < 0x1040) { + *bugfix = *major & 0xF; + *minor = (*major >> 4) & 0xF; + *major = (*major >> 8); + return true; + } + return GetGestalt(gestaltSystemVersionMajor, major) && + GetGestalt(gestaltSystemVersionMinor, minor) && + GetGestalt(gestaltSystemVersionBugFix, bugfix); +} + +MacOSVersionName GetOSVersionName() { + int major = 0, minor = 0, bugfix = 0; + if (!GetOSVersion(&major, &minor, &bugfix)) { + return kMacOSUnknown; + } + if (major > 10) { + return kMacOSNewer; + } + if ((major < 10) || (minor < 3)) { + return kMacOSOlder; + } + switch (minor) { + case 3: + return kMacOSPanther; + case 4: + return kMacOSTiger; + case 5: + return kMacOSLeopard; + case 6: + return kMacOSSnowLeopard; + case 7: + return kMacOSLion; + case 8: + return kMacOSMountainLion; + case 9: + return kMacOSMavericks; + } + return kMacOSNewer; +} + +bool GetQuickTimeVersion(std::string* out) { + int ver; + if (!GetGestalt(gestaltQuickTimeVersion, &ver)) { + return false; + } + + std::stringstream ss; + ss << std::hex << ver; + *out = ss.str(); + return true; +} + +#ifndef WEBRTC_MOZILLA_BUILD +bool RunAppleScript(const std::string& script) { + // TODO(thaloun): Add a .mm file that contains something like this: + // NSString source from script + // NSAppleScript* appleScript = [[NSAppleScript alloc] initWithSource:&source] + // if (appleScript != nil) { + // [appleScript executeAndReturnError:nil] + // [appleScript release] +#ifndef CARBON_DEPRECATED + ComponentInstance component = NULL; + AEDesc script_desc; + AEDesc result_data; + OSStatus err; + OSAID script_id, result_id; + + AECreateDesc(typeNull, NULL, 0, &script_desc); + AECreateDesc(typeNull, NULL, 0, &result_data); + script_id = kOSANullScript; + result_id = kOSANullScript; + + component = OpenDefaultComponent(kOSAComponentType, typeAppleScript); + if (component == NULL) { + LOG(LS_ERROR) << "Failed opening Apple Script component"; + return false; + } + err = AECreateDesc(typeUTF8Text, script.data(), script.size(), &script_desc); + if (err != noErr) { + CloseComponent(component); + LOG(LS_ERROR) << "Failed creating Apple Script description"; + return false; + } + + err = OSACompile(component, &script_desc, kOSAModeCanInteract, &script_id); + if (err != noErr) { + AEDisposeDesc(&script_desc); + if (script_id != kOSANullScript) { + OSADispose(component, script_id); + } + CloseComponent(component); + LOG(LS_ERROR) << "Error compiling Apple Script"; + return false; + } + + err = OSAExecute(component, script_id, kOSANullScript, kOSAModeCanInteract, + &result_id); + + if (err == errOSAScriptError) { + LOG(LS_ERROR) << "Error when executing Apple Script: " << script; + AECreateDesc(typeNull, NULL, 0, &result_data); + OSAScriptError(component, kOSAErrorMessage, typeChar, &result_data); + int len = AEGetDescDataSize(&result_data); + char* data = (char*) malloc(len); + if (data != NULL) { + err = AEGetDescData(&result_data, data, len); + LOG(LS_ERROR) << "Script error: " << data; + } + AEDisposeDesc(&script_desc); + AEDisposeDesc(&result_data); + return false; + } + AEDisposeDesc(&script_desc); + if (script_id != kOSANullScript) { + OSADispose(component, script_id); + } + if (result_id != kOSANullScript) { + OSADispose(component, result_id); + } + CloseComponent(component); + return true; +#else + // TODO(thaloun): Support applescripts with the NSAppleScript API. + return false; +#endif // CARBON_DEPRECATED +} +#endif // !WEBRTC_MOZILLA + +#endif // WEBRTC_MAC && !defined(WEBRTC_IOS) + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macutils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macutils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macutils.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macutils.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,61 @@ +/* + * Copyright 2007 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_MACUTILS_H__ +#define WEBRTC_BASE_MACUTILS_H__ + +#include +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +#include +#endif +#include + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// + +// Note that some of these functions work for both iOS and Mac OS X. The ones +// that are specific to Mac are #ifdef'ed as such. + +bool ToUtf8(const CFStringRef str16, std::string* str8); +bool ToUtf16(const std::string& str8, CFStringRef* str16); + +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +void DecodeFourChar(UInt32 fc, std::string* out); + +enum MacOSVersionName { + kMacOSUnknown, // ??? + kMacOSOlder, // 10.2- + kMacOSPanther, // 10.3 + kMacOSTiger, // 10.4 + kMacOSLeopard, // 10.5 + kMacOSSnowLeopard, // 10.6 + kMacOSLion, // 10.7 + kMacOSMountainLion, // 10.8 + kMacOSMavericks, // 10.9 + kMacOSNewer, // 10.10+ +}; + +bool GetOSVersion(int* major, int* minor, int* bugfix); +MacOSVersionName GetOSVersionName(); +bool GetQuickTimeVersion(std::string* version); + +#ifndef WEBRTC_MOZILLA_BUILD +// Runs the given apple script. Only supports scripts that does not +// require user interaction. +bool RunAppleScript(const std::string& script); +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_MACUTILS_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macutils_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macutils_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macutils_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macutils_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,43 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/macutils.h" + +TEST(MacUtilsTest, GetOsVersionName) { + rtc::MacOSVersionName ver = rtc::GetOSVersionName(); + LOG(LS_INFO) << "GetOsVersionName " << ver; + EXPECT_NE(rtc::kMacOSUnknown, ver); +} + +TEST(MacUtilsTest, GetQuickTimeVersion) { + std::string version; + EXPECT_TRUE(rtc::GetQuickTimeVersion(&version)); + LOG(LS_INFO) << "GetQuickTimeVersion " << version; +} + +TEST(MacUtilsTest, RunAppleScriptCompileError) { + std::string script("set value to to 5"); + EXPECT_FALSE(rtc::RunAppleScript(script)); +} + +TEST(MacUtilsTest, RunAppleScriptRuntimeError) { + std::string script("set value to 5 / 0"); + EXPECT_FALSE(rtc::RunAppleScript(script)); +} + +#ifdef CARBON_DEPRECATED +TEST(MacUtilsTest, DISABLED_RunAppleScriptSuccess) { +#else +TEST(MacUtilsTest, RunAppleScriptSuccess) { +#endif + std::string script("set value to 5"); + EXPECT_TRUE(rtc::RunAppleScript(script)); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macwindowpicker.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macwindowpicker.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macwindowpicker.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macwindowpicker.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,256 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "webrtc/base/macwindowpicker.h" + +#include +#include +#include + +#include "webrtc/base/logging.h" +#include "webrtc/base/macutils.h" + +namespace rtc { + +static const char* kCoreGraphicsName = + "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/" + "CoreGraphics.framework/CoreGraphics"; + +static const char* kWindowListCopyWindowInfo = "CGWindowListCopyWindowInfo"; +static const char* kWindowListCreateDescriptionFromArray = + "CGWindowListCreateDescriptionFromArray"; + +// Function pointer for holding the CGWindowListCopyWindowInfo function. +typedef CFArrayRef(*CGWindowListCopyWindowInfoProc)(CGWindowListOption, + CGWindowID); + +// Function pointer for holding the CGWindowListCreateDescriptionFromArray +// function. +typedef CFArrayRef(*CGWindowListCreateDescriptionFromArrayProc)(CFArrayRef); + +MacWindowPicker::MacWindowPicker() : lib_handle_(NULL), get_window_list_(NULL), + get_window_list_desc_(NULL) { +} + +MacWindowPicker::~MacWindowPicker() { + if (lib_handle_ != NULL) { + dlclose(lib_handle_); + } +} + +bool MacWindowPicker::Init() { + // TODO: If this class grows to use more dynamically functions + // from the CoreGraphics framework, consider using + // webrtc/base/latebindingsymboltable.h. + lib_handle_ = dlopen(kCoreGraphicsName, RTLD_NOW); + if (lib_handle_ == NULL) { + LOG(LS_ERROR) << "Could not load CoreGraphics"; + return false; + } + + get_window_list_ = dlsym(lib_handle_, kWindowListCopyWindowInfo); + get_window_list_desc_ = + dlsym(lib_handle_, kWindowListCreateDescriptionFromArray); + if (get_window_list_ == NULL || get_window_list_desc_ == NULL) { + // The CGWindowListCopyWindowInfo and the + // CGWindowListCreateDescriptionFromArray functions was introduced + // in Leopard(10.5) so this is a normal failure on Tiger. + LOG(LS_INFO) << "Failed to load Core Graphics symbols"; + dlclose(lib_handle_); + lib_handle_ = NULL; + return false; + } + + return true; +} + +bool MacWindowPicker::IsVisible(const WindowId& id) { + // Init if we're not already inited. + if (get_window_list_desc_ == NULL && !Init()) { + return false; + } + CGWindowID ids[1]; + ids[0] = id.id(); + CFArrayRef window_id_array = + CFArrayCreate(NULL, reinterpret_cast(&ids), 1, NULL); + + CFArrayRef window_array = + reinterpret_cast( + get_window_list_desc_)(window_id_array); + if (window_array == NULL || 0 == CFArrayGetCount(window_array)) { + // Could not find the window. It might have been closed. + LOG(LS_INFO) << "Window not found"; + CFRelease(window_id_array); + return false; + } + + CFDictionaryRef window = reinterpret_cast( + CFArrayGetValueAtIndex(window_array, 0)); + CFBooleanRef is_visible = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowIsOnscreen)); + + // Check that the window is visible. If not we might crash. + bool visible = false; + if (is_visible != NULL) { + visible = CFBooleanGetValue(is_visible); + } + CFRelease(window_id_array); + CFRelease(window_array); + return visible; +} + +bool MacWindowPicker::MoveToFront(const WindowId& id) { + // Init if we're not already initialized. + if (get_window_list_desc_ == NULL && !Init()) { + return false; + } + CGWindowID ids[1]; + ids[0] = id.id(); + CFArrayRef window_id_array = + CFArrayCreate(NULL, reinterpret_cast(&ids), 1, NULL); + + CFArrayRef window_array = + reinterpret_cast( + get_window_list_desc_)(window_id_array); + if (window_array == NULL || 0 == CFArrayGetCount(window_array)) { + // Could not find the window. It might have been closed. + LOG(LS_INFO) << "Window not found"; + CFRelease(window_id_array); + return false; + } + + CFDictionaryRef window = reinterpret_cast( + CFArrayGetValueAtIndex(window_array, 0)); + CFStringRef window_name_ref = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowName)); + CFNumberRef application_pid = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowOwnerPID)); + + int pid_val; + CFNumberGetValue(application_pid, kCFNumberIntType, &pid_val); + std::string window_name; + ToUtf8(window_name_ref, &window_name); + + // Build an applescript that sets the selected window to front + // within the application. Then set the application to front. + bool result = true; + std::stringstream ss; + ss << "tell application \"System Events\"\n" + << "set proc to the first item of (every process whose unix id is " + << pid_val + << ")\n" + << "tell proc to perform action \"AXRaise\" of window \"" + << window_name + << "\"\n" + << "set the frontmost of proc to true\n" + << "end tell"; + if (!RunAppleScript(ss.str())) { + // This might happen to for example X applications where the X + // server spawns of processes with their own PID but the X server + // is still registered as owner to the application windows. As a + // workaround, we put the X server process to front, meaning that + // all X applications will show up. The drawback with this + // workaround is that the application that we really wanted to set + // to front might be behind another X application. + ProcessSerialNumber psn; + pid_t pid = pid_val; + int res = GetProcessForPID(pid, &psn); + if (res != 0) { + LOG(LS_ERROR) << "Failed getting process for pid"; + result = false; + } + res = SetFrontProcess(&psn); + if (res != 0) { + LOG(LS_ERROR) << "Failed setting process to front"; + result = false; + } + } + CFRelease(window_id_array); + CFRelease(window_array); + return result; +} + +bool MacWindowPicker::GetDesktopList(DesktopDescriptionList* descriptions) { + const uint32_t kMaxDisplays = 128; + CGDirectDisplayID active_displays[kMaxDisplays]; + uint32_t display_count = 0; + + CGError err = CGGetActiveDisplayList(kMaxDisplays, + active_displays, + &display_count); + if (err != kCGErrorSuccess) { + LOG_E(LS_ERROR, OS, err) << "Failed to enumerate the active displays."; + return false; + } + for (uint32_t i = 0; i < display_count; ++i) { + DesktopId id(active_displays[i], static_cast(i)); + // TODO: Figure out an appropriate desktop title. + DesktopDescription desc(id, ""); + desc.set_primary(CGDisplayIsMain(id.id())); + descriptions->push_back(desc); + } + return display_count > 0; +} + +bool MacWindowPicker::GetDesktopDimensions(const DesktopId& id, + int* width, + int* height) { + *width = CGDisplayPixelsWide(id.id()); + *height = CGDisplayPixelsHigh(id.id()); + return true; +} + +bool MacWindowPicker::GetWindowList(WindowDescriptionList* descriptions) { + // Init if we're not already inited. + if (get_window_list_ == NULL && !Init()) { + return false; + } + + // Only get onscreen, non-desktop windows. + CFArrayRef window_array = + reinterpret_cast(get_window_list_)( + kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, + kCGNullWindowID); + if (window_array == NULL) { + return false; + } + + // Check windows to make sure they have an id, title, and use window layer 0. + CFIndex i; + CFIndex count = CFArrayGetCount(window_array); + for (i = 0; i < count; ++i) { + CFDictionaryRef window = reinterpret_cast( + CFArrayGetValueAtIndex(window_array, i)); + CFStringRef window_title = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowName)); + CFNumberRef window_id = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowNumber)); + CFNumberRef window_layer = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowLayer)); + if (window_title != NULL && window_id != NULL && window_layer != NULL) { + std::string title_str; + int id_val, layer_val; + ToUtf8(window_title, &title_str); + CFNumberGetValue(window_id, kCFNumberIntType, &id_val); + CFNumberGetValue(window_layer, kCFNumberIntType, &layer_val); + + // Discard windows without a title. + if (layer_val == 0 && title_str.length() > 0) { + WindowId id(static_cast(id_val)); + WindowDescription desc(id, title_str); + descriptions->push_back(desc); + } + } + } + + CFRelease(window_array); + return true; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macwindowpicker.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macwindowpicker.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macwindowpicker.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macwindowpicker.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef WEBRTC_BASE_MACWINDOWPICKER_H_ +#define WEBRTC_BASE_MACWINDOWPICKER_H_ + +#include "webrtc/base/windowpicker.h" + +namespace rtc { + +class MacWindowPicker : public WindowPicker { + public: + MacWindowPicker(); + ~MacWindowPicker(); + virtual bool Init(); + virtual bool IsVisible(const WindowId& id); + virtual bool MoveToFront(const WindowId& id); + virtual bool GetWindowList(WindowDescriptionList* descriptions); + virtual bool GetDesktopList(DesktopDescriptionList* descriptions); + virtual bool GetDesktopDimensions(const DesktopId& id, int* width, + int* height); + + private: + void* lib_handle_; + void* get_window_list_; + void* get_window_list_desc_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_MACWINDOWPICKER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macwindowpicker_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macwindowpicker_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/macwindowpicker_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/macwindowpicker_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,45 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "webrtc/base/gunit.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/macutils.h" +#include "webrtc/base/macwindowpicker.h" +#include "webrtc/base/windowpicker.h" + +#if !defined(WEBRTC_MAC) || defined(WEBRTC_IOS) +#error Only for WEBRTC_MAC && !WEBRTC_IOS +#endif + +namespace rtc { + +bool IsLeopardOrLater() { + return GetOSVersionName() >= kMacOSLeopard; +} + +// Test that this works on new versions and fails acceptably on old versions. +TEST(MacWindowPickerTest, TestGetWindowList) { + MacWindowPicker picker, picker2; + WindowDescriptionList descriptions; + if (IsLeopardOrLater()) { + EXPECT_TRUE(picker.Init()); + EXPECT_TRUE(picker.GetWindowList(&descriptions)); + EXPECT_TRUE(picker2.GetWindowList(&descriptions)); // Init is optional + } else { + EXPECT_FALSE(picker.Init()); + EXPECT_FALSE(picker.GetWindowList(&descriptions)); + EXPECT_FALSE(picker2.GetWindowList(&descriptions)); + } +} + +// TODO: Add verification of the actual parsing, ie, add +// functionality to inject a fake get_window_array function which +// provide a pre-constructed list of windows. + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/mathutils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/mathutils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/mathutils.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/mathutils.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright 2005 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_MATHUTILS_H_ +#define WEBRTC_BASE_MATHUTILS_H_ + +#include + +#ifndef M_PI +#define M_PI 3.14159265359f +#endif + +#endif // WEBRTC_BASE_MATHUTILS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/md5.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/md5.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/md5.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/md5.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,222 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +// Changes from original C code: +// Ported to C++, type casting, Google code style. + +#include "webrtc/base/md5.h" + +// TODO: Avoid memcmpy - hash directly from memory. +#include // for memcpy(). + +#include "webrtc/base/byteorder.h" // for ARCH_CPU_LITTLE_ENDIAN. + +namespace rtc { + +#ifdef ARCH_CPU_LITTLE_ENDIAN +#define ByteReverse(buf, len) // Nothing. +#else // ARCH_CPU_BIG_ENDIAN +static void ByteReverse(uint32* buf, int len) { + for (int i = 0; i < len; ++i) { + buf[i] = rtc::GetLE32(&buf[i]); + } +} +#endif + +// Start MD5 accumulation. Set bit count to 0 and buffer to mysterious +// initialization constants. +void MD5Init(MD5Context* ctx) { + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +// Update context to reflect the concatenation of another buffer full of bytes. +void MD5Update(MD5Context* ctx, const uint8* buf, size_t len) { + // Update bitcount. + uint32 t = ctx->bits[0]; + if ((ctx->bits[0] = t + (static_cast(len) << 3)) < t) { + ctx->bits[1]++; // Carry from low to high. + } + ctx->bits[1] += static_cast(len >> 29); + t = (t >> 3) & 0x3f; // Bytes already in shsInfo->data. + + // Handle any leading odd-sized chunks. + if (t) { + uint8* p = reinterpret_cast(ctx->in) + t; + + t = 64-t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + ByteReverse(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + } + + // Process data in 64-byte chunks. + while (len >= 64) { + memcpy(ctx->in, buf, 64); + ByteReverse(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + // Handle any remaining bytes of data. + memcpy(ctx->in, buf, len); +} + +// Final wrapup - pad to 64-byte boundary with the bit pattern. +// 1 0* (64-bit count of bits processed, MSB-first) +void MD5Final(MD5Context* ctx, uint8 digest[16]) { + // Compute number of bytes mod 64. + uint32 count = (ctx->bits[0] >> 3) & 0x3F; + + // Set the first char of padding to 0x80. This is safe since there is + // always at least one byte free. + uint8* p = reinterpret_cast(ctx->in) + count; + *p++ = 0x80; + + // Bytes of padding needed to make 64 bytes. + count = 64 - 1 - count; + + // Pad out to 56 mod 64. + if (count < 8) { + // Two lots of padding: Pad the first block to 64 bytes. + memset(p, 0, count); + ByteReverse(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + + // Now fill the next block with 56 bytes. + memset(ctx->in, 0, 56); + } else { + // Pad block to 56 bytes. + memset(p, 0, count - 8); + } + ByteReverse(ctx->in, 14); + + // Append length in bits and transform. + ctx->in[14] = ctx->bits[0]; + ctx->in[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, ctx->in); + ByteReverse(ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(*ctx)); // In case it's sensitive. +} + +// The four core functions - F1 is optimized somewhat. +// #define F1(x, y, z) (x & y | ~x & z) +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +// This is the central step in the MD5 algorithm. +#define MD5STEP(f, w, x, y, z, data, s) \ + (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x) + +// The core of the MD5 algorithm, this alters an existing MD5 hash to +// reflect the addition of 16 longwords of new data. MD5Update blocks +// the data and converts bytes into longwords for this routine. +void MD5Transform(uint32 buf[4], const uint32 in[16]) { + uint32 a = buf[0]; + uint32 b = buf[1]; + uint32 c = buf[2]; + uint32 d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[ 2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[ 7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[ 5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[ 3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[ 1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[ 8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[ 6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[ 4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[ 2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[ 9] + 0xeb86d391, 21); + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/md5digest.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/md5digest.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/md5digest.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/md5digest.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_MD5DIGEST_H_ +#define WEBRTC_BASE_MD5DIGEST_H_ + +#include "webrtc/base/md5.h" +#include "webrtc/base/messagedigest.h" + +namespace rtc { + +// A simple wrapper for our MD5 implementation. +class Md5Digest : public MessageDigest { + public: + enum { kSize = 16 }; + Md5Digest() { + MD5Init(&ctx_); + } + virtual size_t Size() const { + return kSize; + } + virtual void Update(const void* buf, size_t len) { + MD5Update(&ctx_, static_cast(buf), len); + } + virtual size_t Finish(void* buf, size_t len) { + if (len < kSize) { + return 0; + } + MD5Final(&ctx_, static_cast(buf)); + MD5Init(&ctx_); // Reset for next use. + return kSize; + } + private: + MD5_CTX ctx_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_MD5DIGEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/md5digest_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/md5digest_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/md5digest_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/md5digest_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,79 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/md5digest.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/stringencode.h" + +namespace rtc { + +std::string Md5(const std::string& input) { + Md5Digest md5; + return ComputeDigest(&md5, input); +} + +TEST(Md5DigestTest, TestSize) { + Md5Digest md5; + EXPECT_EQ(16, static_cast(Md5Digest::kSize)); + EXPECT_EQ(16U, md5.Size()); +} + +TEST(Md5DigestTest, TestBasic) { + // These are the standard MD5 test vectors from RFC 1321. + EXPECT_EQ("d41d8cd98f00b204e9800998ecf8427e", Md5("")); + EXPECT_EQ("0cc175b9c0f1b6a831c399e269772661", Md5("a")); + EXPECT_EQ("900150983cd24fb0d6963f7d28e17f72", Md5("abc")); + EXPECT_EQ("f96b697d7cb7938d525a2f31aaf161d0", Md5("message digest")); + EXPECT_EQ("c3fcd3d76192e4007dfb496cca67e13b", + Md5("abcdefghijklmnopqrstuvwxyz")); +} + +TEST(Md5DigestTest, TestMultipleUpdates) { + Md5Digest md5; + std::string input = "abcdefghijklmnopqrstuvwxyz"; + char output[Md5Digest::kSize]; + for (size_t i = 0; i < input.size(); ++i) { + md5.Update(&input[i], 1); + } + EXPECT_EQ(md5.Size(), md5.Finish(output, sizeof(output))); + EXPECT_EQ("c3fcd3d76192e4007dfb496cca67e13b", + hex_encode(output, sizeof(output))); +} + +TEST(Md5DigestTest, TestReuse) { + Md5Digest md5; + std::string input = "message digest"; + EXPECT_EQ("f96b697d7cb7938d525a2f31aaf161d0", ComputeDigest(&md5, input)); + input = "abcdefghijklmnopqrstuvwxyz"; + EXPECT_EQ("c3fcd3d76192e4007dfb496cca67e13b", ComputeDigest(&md5, input)); +} + +TEST(Md5DigestTest, TestBufferTooSmall) { + Md5Digest md5; + std::string input = "abcdefghijklmnopqrstuvwxyz"; + char output[Md5Digest::kSize - 1]; + md5.Update(input.c_str(), input.size()); + EXPECT_EQ(0U, md5.Finish(output, sizeof(output))); +} + +TEST(Md5DigestTest, TestBufferConst) { + Md5Digest md5; + const int kLongSize = 1000000; + std::string input(kLongSize, '\0'); + for (int i = 0; i < kLongSize; ++i) { + input[i] = static_cast(i); + } + md5.Update(input.c_str(), input.size()); + for (int i = 0; i < kLongSize; ++i) { + EXPECT_EQ(static_cast(i), input[i]); + } +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/md5.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/md5.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/md5.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/md5.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,45 @@ +/* + * This is the header file for the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + */ + +// Changes(fbarchard): Ported to C++ and Google style guide. +// Made context first parameter in MD5Final for consistency with Sha1. +// Changes(hellner): added rtc namespace + +#ifndef WEBRTC_BASE_MD5_H_ +#define WEBRTC_BASE_MD5_H_ + +#include "webrtc/base/basictypes.h" + +namespace rtc { + +// Canonical name for a MD5 context structure, used in many crypto libs. +typedef struct MD5Context MD5_CTX; + +struct MD5Context { + uint32 buf[4]; + uint32 bits[2]; + uint32 in[16]; +}; + +void MD5Init(MD5Context* context); +void MD5Update(MD5Context* context, const uint8* data, size_t len); +void MD5Final(MD5Context* context, uint8 digest[16]); +void MD5Transform(uint32 buf[4], const uint32 in[16]); + +} // namespace rtc + +#endif // WEBRTC_BASE_MD5_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/messagedigest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/messagedigest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/messagedigest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/messagedigest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,180 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/messagedigest.h" + +#include + +#include "webrtc/base/sslconfig.h" +#if SSL_USE_OPENSSL +#include "webrtc/base/openssldigest.h" +#else +#include "webrtc/base/md5digest.h" +#include "webrtc/base/sha1digest.h" +#endif +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/stringencode.h" + +namespace rtc { + +// From RFC 4572. +const char DIGEST_MD5[] = "md5"; +const char DIGEST_SHA_1[] = "sha-1"; +const char DIGEST_SHA_224[] = "sha-224"; +const char DIGEST_SHA_256[] = "sha-256"; +const char DIGEST_SHA_384[] = "sha-384"; +const char DIGEST_SHA_512[] = "sha-512"; + +static const size_t kBlockSize = 64; // valid for SHA-256 and down + +MessageDigest* MessageDigestFactory::Create(const std::string& alg) { +#if SSL_USE_OPENSSL + MessageDigest* digest = new OpenSSLDigest(alg); + if (digest->Size() == 0) { // invalid algorithm + delete digest; + digest = NULL; + } + return digest; +#else + MessageDigest* digest = NULL; + if (alg == DIGEST_MD5) { + digest = new Md5Digest(); + } else if (alg == DIGEST_SHA_1) { + digest = new Sha1Digest(); + } + return digest; +#endif +} + +bool IsFips180DigestAlgorithm(const std::string& alg) { + // These are the FIPS 180 algorithms. According to RFC 4572 Section 5, + // "Self-signed certificates (for which legacy certificates are not a + // consideration) MUST use one of the FIPS 180 algorithms (SHA-1, + // SHA-224, SHA-256, SHA-384, or SHA-512) as their signature algorithm, + // and thus also MUST use it to calculate certificate fingerprints." + return alg == DIGEST_SHA_1 || + alg == DIGEST_SHA_224 || + alg == DIGEST_SHA_256 || + alg == DIGEST_SHA_384 || + alg == DIGEST_SHA_512; +} + +size_t ComputeDigest(MessageDigest* digest, const void* input, size_t in_len, + void* output, size_t out_len) { + digest->Update(input, in_len); + return digest->Finish(output, out_len); +} + +size_t ComputeDigest(const std::string& alg, const void* input, size_t in_len, + void* output, size_t out_len) { + scoped_ptr digest(MessageDigestFactory::Create(alg)); + return (digest) ? + ComputeDigest(digest.get(), input, in_len, output, out_len) : + 0; +} + +std::string ComputeDigest(MessageDigest* digest, const std::string& input) { + scoped_ptr output(new char[digest->Size()]); + ComputeDigest(digest, input.data(), input.size(), + output.get(), digest->Size()); + return hex_encode(output.get(), digest->Size()); +} + +bool ComputeDigest(const std::string& alg, const std::string& input, + std::string* output) { + scoped_ptr digest(MessageDigestFactory::Create(alg)); + if (!digest) { + return false; + } + *output = ComputeDigest(digest.get(), input); + return true; +} + +std::string ComputeDigest(const std::string& alg, const std::string& input) { + std::string output; + ComputeDigest(alg, input, &output); + return output; +} + +// Compute a RFC 2104 HMAC: H(K XOR opad, H(K XOR ipad, text)) +size_t ComputeHmac(MessageDigest* digest, + const void* key, size_t key_len, + const void* input, size_t in_len, + void* output, size_t out_len) { + // We only handle algorithms with a 64-byte blocksize. + // TODO: Add BlockSize() method to MessageDigest. + size_t block_len = kBlockSize; + if (digest->Size() > 32) { + return 0; + } + // Copy the key to a block-sized buffer to simplify padding. + // If the key is longer than a block, hash it and use the result instead. + scoped_ptr new_key(new uint8[block_len]); + if (key_len > block_len) { + ComputeDigest(digest, key, key_len, new_key.get(), block_len); + memset(new_key.get() + digest->Size(), 0, block_len - digest->Size()); + } else { + memcpy(new_key.get(), key, key_len); + memset(new_key.get() + key_len, 0, block_len - key_len); + } + // Set up the padding from the key, salting appropriately for each padding. + scoped_ptr o_pad(new uint8[block_len]), i_pad(new uint8[block_len]); + for (size_t i = 0; i < block_len; ++i) { + o_pad[i] = 0x5c ^ new_key[i]; + i_pad[i] = 0x36 ^ new_key[i]; + } + // Inner hash; hash the inner padding, and then the input buffer. + scoped_ptr inner(new uint8[digest->Size()]); + digest->Update(i_pad.get(), block_len); + digest->Update(input, in_len); + digest->Finish(inner.get(), digest->Size()); + // Outer hash; hash the outer padding, and then the result of the inner hash. + digest->Update(o_pad.get(), block_len); + digest->Update(inner.get(), digest->Size()); + return digest->Finish(output, out_len); +} + +size_t ComputeHmac(const std::string& alg, const void* key, size_t key_len, + const void* input, size_t in_len, + void* output, size_t out_len) { + scoped_ptr digest(MessageDigestFactory::Create(alg)); + if (!digest) { + return 0; + } + return ComputeHmac(digest.get(), key, key_len, + input, in_len, output, out_len); +} + +std::string ComputeHmac(MessageDigest* digest, const std::string& key, + const std::string& input) { + scoped_ptr output(new char[digest->Size()]); + ComputeHmac(digest, key.data(), key.size(), + input.data(), input.size(), output.get(), digest->Size()); + return hex_encode(output.get(), digest->Size()); +} + +bool ComputeHmac(const std::string& alg, const std::string& key, + const std::string& input, std::string* output) { + scoped_ptr digest(MessageDigestFactory::Create(alg)); + if (!digest) { + return false; + } + *output = ComputeHmac(digest.get(), key, input); + return true; +} + +std::string ComputeHmac(const std::string& alg, const std::string& key, + const std::string& input) { + std::string output; + ComputeHmac(alg, key, input, &output); + return output; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/messagedigest.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/messagedigest.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/messagedigest.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/messagedigest.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,109 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_MESSAGEDIGEST_H_ +#define WEBRTC_BASE_MESSAGEDIGEST_H_ + +#include + +namespace rtc { + +// Definitions for the digest algorithms. +extern const char DIGEST_MD5[]; +extern const char DIGEST_SHA_1[]; +extern const char DIGEST_SHA_224[]; +extern const char DIGEST_SHA_256[]; +extern const char DIGEST_SHA_384[]; +extern const char DIGEST_SHA_512[]; + +// A general class for computing hashes. +class MessageDigest { + public: + enum { kMaxSize = 64 }; // Maximum known size (SHA-512) + virtual ~MessageDigest() {} + // Returns the digest output size (e.g. 16 bytes for MD5). + virtual size_t Size() const = 0; + // Updates the digest with |len| bytes from |buf|. + virtual void Update(const void* buf, size_t len) = 0; + // Outputs the digest value to |buf| with length |len|. + // Returns the number of bytes written, i.e., Size(). + virtual size_t Finish(void* buf, size_t len) = 0; +}; + +// A factory class for creating digest objects. +class MessageDigestFactory { + public: + static MessageDigest* Create(const std::string& alg); +}; + +// A whitelist of approved digest algorithms from RFC 4572 (FIPS 180). +bool IsFips180DigestAlgorithm(const std::string& alg); + +// Functions to create hashes. + +// Computes the hash of |in_len| bytes of |input|, using the |digest| hash +// implementation, and outputs the hash to the buffer |output|, which is +// |out_len| bytes long. Returns the number of bytes written to |output| if +// successful, or 0 if |out_len| was too small. +size_t ComputeDigest(MessageDigest* digest, const void* input, size_t in_len, + void* output, size_t out_len); +// Like the previous function, but creates a digest implementation based on +// the desired digest name |alg|, e.g. DIGEST_SHA_1. Returns 0 if there is no +// digest with the given name. +size_t ComputeDigest(const std::string& alg, const void* input, size_t in_len, + void* output, size_t out_len); +// Computes the hash of |input| using the |digest| hash implementation, and +// returns it as a hex-encoded string. +std::string ComputeDigest(MessageDigest* digest, const std::string& input); +// Like the previous function, but creates a digest implementation based on +// the desired digest name |alg|, e.g. DIGEST_SHA_1. Returns empty string if +// there is no digest with the given name. +std::string ComputeDigest(const std::string& alg, const std::string& input); +// Like the previous function, but returns an explicit result code. +bool ComputeDigest(const std::string& alg, const std::string& input, + std::string* output); + +// Shorthand way to compute a hex-encoded hash using MD5. +inline std::string MD5(const std::string& input) { + return ComputeDigest(DIGEST_MD5, input); +} + +// Functions to compute RFC 2104 HMACs. + +// Computes the HMAC of |in_len| bytes of |input|, using the |digest| hash +// implementation and |key_len| bytes of |key| to key the HMAC, and outputs +// the HMAC to the buffer |output|, which is |out_len| bytes long. Returns the +// number of bytes written to |output| if successful, or 0 if |out_len| was too +// small. +size_t ComputeHmac(MessageDigest* digest, const void* key, size_t key_len, + const void* input, size_t in_len, + void* output, size_t out_len); +// Like the previous function, but creates a digest implementation based on +// the desired digest name |alg|, e.g. DIGEST_SHA_1. Returns 0 if there is no +// digest with the given name. +size_t ComputeHmac(const std::string& alg, const void* key, size_t key_len, + const void* input, size_t in_len, + void* output, size_t out_len); +// Computes the HMAC of |input| using the |digest| hash implementation and |key| +// to key the HMAC, and returns it as a hex-encoded string. +std::string ComputeHmac(MessageDigest* digest, const std::string& key, + const std::string& input); +// Like the previous function, but creates a digest implementation based on +// the desired digest name |alg|, e.g. DIGEST_SHA_1. Returns empty string if +// there is no digest with the given name. +std::string ComputeHmac(const std::string& alg, const std::string& key, + const std::string& input); +// Like the previous function, but returns an explicit result code. +bool ComputeHmac(const std::string& alg, const std::string& key, + const std::string& input, std::string* output); + +} // namespace rtc + +#endif // WEBRTC_BASE_MESSAGEDIGEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/messagedigest_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/messagedigest_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/messagedigest_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/messagedigest_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,151 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/messagedigest.h" +#include "webrtc/base/stringencode.h" + +namespace rtc { + +// Test vectors from RFC 1321. +TEST(MessageDigestTest, TestMd5Digest) { + // Test the string versions of the APIs. + EXPECT_EQ("d41d8cd98f00b204e9800998ecf8427e", + ComputeDigest(DIGEST_MD5, "")); + EXPECT_EQ("900150983cd24fb0d6963f7d28e17f72", + ComputeDigest(DIGEST_MD5, "abc")); + EXPECT_EQ("c3fcd3d76192e4007dfb496cca67e13b", + ComputeDigest(DIGEST_MD5, "abcdefghijklmnopqrstuvwxyz")); + + // Test the raw buffer versions of the APIs; also check output buffer size. + char output[16]; + EXPECT_EQ(sizeof(output), + ComputeDigest(DIGEST_MD5, "abc", 3, output, sizeof(output))); + EXPECT_EQ("900150983cd24fb0d6963f7d28e17f72", + hex_encode(output, sizeof(output))); + EXPECT_EQ(0U, + ComputeDigest(DIGEST_MD5, "abc", 3, output, sizeof(output) - 1)); +} + +// Test vectors from RFC 3174. +TEST(MessageDigestTest, TestSha1Digest) { + // Test the string versions of the APIs. + EXPECT_EQ("da39a3ee5e6b4b0d3255bfef95601890afd80709", + ComputeDigest(DIGEST_SHA_1, "")); + EXPECT_EQ("a9993e364706816aba3e25717850c26c9cd0d89d", + ComputeDigest(DIGEST_SHA_1, "abc")); + EXPECT_EQ("84983e441c3bd26ebaae4aa1f95129e5e54670f1", + ComputeDigest(DIGEST_SHA_1, + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")); + + // Test the raw buffer versions of the APIs; also check output buffer size. + char output[20]; + EXPECT_EQ(sizeof(output), + ComputeDigest(DIGEST_SHA_1, "abc", 3, output, sizeof(output))); + EXPECT_EQ("a9993e364706816aba3e25717850c26c9cd0d89d", + hex_encode(output, sizeof(output))); + EXPECT_EQ(0U, + ComputeDigest(DIGEST_SHA_1, "abc", 3, output, sizeof(output) - 1)); +} + +// Test that we fail properly if a bad digest algorithm is specified. +TEST(MessageDigestTest, TestBadDigest) { + std::string output; + EXPECT_FALSE(ComputeDigest("sha-9000", "abc", &output)); + EXPECT_EQ("", ComputeDigest("sha-9000", "abc")); +} + +// Test vectors from RFC 2202. +TEST(MessageDigestTest, TestMd5Hmac) { + // Test the string versions of the APIs. + EXPECT_EQ("9294727a3638bb1c13f48ef8158bfc9d", + ComputeHmac(DIGEST_MD5, std::string(16, '\x0b'), "Hi There")); + EXPECT_EQ("750c783e6ab0b503eaa86e310a5db738", + ComputeHmac(DIGEST_MD5, "Jefe", "what do ya want for nothing?")); + EXPECT_EQ("56be34521d144c88dbb8c733f0e8b3f6", + ComputeHmac(DIGEST_MD5, std::string(16, '\xaa'), + std::string(50, '\xdd'))); + EXPECT_EQ("697eaf0aca3a3aea3a75164746ffaa79", + ComputeHmac(DIGEST_MD5, + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", + std::string(50, '\xcd'))); + EXPECT_EQ("56461ef2342edc00f9bab995690efd4c", + ComputeHmac(DIGEST_MD5, std::string(16, '\x0c'), + "Test With Truncation")); + EXPECT_EQ("6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd", + ComputeHmac(DIGEST_MD5, std::string(80, '\xaa'), + "Test Using Larger Than Block-Size Key - Hash Key First")); + EXPECT_EQ("6f630fad67cda0ee1fb1f562db3aa53e", + ComputeHmac(DIGEST_MD5, std::string(80, '\xaa'), + "Test Using Larger Than Block-Size Key and Larger " + "Than One Block-Size Data")); + + // Test the raw buffer versions of the APIs; also check output buffer size. + std::string key(16, '\x0b'); + std::string input("Hi There"); + char output[16]; + EXPECT_EQ(sizeof(output), + ComputeHmac(DIGEST_MD5, key.c_str(), key.size(), + input.c_str(), input.size(), output, sizeof(output))); + EXPECT_EQ("9294727a3638bb1c13f48ef8158bfc9d", + hex_encode(output, sizeof(output))); + EXPECT_EQ(0U, + ComputeHmac(DIGEST_MD5, key.c_str(), key.size(), + input.c_str(), input.size(), output, sizeof(output) - 1)); +} + +// Test vectors from RFC 2202. +TEST(MessageDigestTest, TestSha1Hmac) { + // Test the string versions of the APIs. + EXPECT_EQ("b617318655057264e28bc0b6fb378c8ef146be00", + ComputeHmac(DIGEST_SHA_1, std::string(20, '\x0b'), "Hi There")); + EXPECT_EQ("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79", + ComputeHmac(DIGEST_SHA_1, "Jefe", "what do ya want for nothing?")); + EXPECT_EQ("125d7342b9ac11cd91a39af48aa17b4f63f175d3", + ComputeHmac(DIGEST_SHA_1, std::string(20, '\xaa'), + std::string(50, '\xdd'))); + EXPECT_EQ("4c9007f4026250c6bc8414f9bf50c86c2d7235da", + ComputeHmac(DIGEST_SHA_1, + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", + std::string(50, '\xcd'))); + EXPECT_EQ("4c1a03424b55e07fe7f27be1d58bb9324a9a5a04", + ComputeHmac(DIGEST_SHA_1, std::string(20, '\x0c'), + "Test With Truncation")); + EXPECT_EQ("aa4ae5e15272d00e95705637ce8a3b55ed402112", + ComputeHmac(DIGEST_SHA_1, std::string(80, '\xaa'), + "Test Using Larger Than Block-Size Key - Hash Key First")); + EXPECT_EQ("e8e99d0f45237d786d6bbaa7965c7808bbff1a91", + ComputeHmac(DIGEST_SHA_1, std::string(80, '\xaa'), + "Test Using Larger Than Block-Size Key and Larger " + "Than One Block-Size Data")); + + // Test the raw buffer versions of the APIs; also check output buffer size. + std::string key(20, '\x0b'); + std::string input("Hi There"); + char output[20]; + EXPECT_EQ(sizeof(output), + ComputeHmac(DIGEST_SHA_1, key.c_str(), key.size(), + input.c_str(), input.size(), output, sizeof(output))); + EXPECT_EQ("b617318655057264e28bc0b6fb378c8ef146be00", + hex_encode(output, sizeof(output))); + EXPECT_EQ(0U, + ComputeHmac(DIGEST_SHA_1, key.c_str(), key.size(), + input.c_str(), input.size(), output, sizeof(output) - 1)); +} + +TEST(MessageDigestTest, TestBadHmac) { + std::string output; + EXPECT_FALSE(ComputeHmac("sha-9000", "key", "abc", &output)); + EXPECT_EQ("", ComputeHmac("sha-9000", "key", "abc")); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/messagehandler.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/messagehandler.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/messagehandler.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/messagehandler.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/messagehandler.h" +#include "webrtc/base/messagequeue.h" + +namespace rtc { + +MessageHandler::~MessageHandler() { + MessageQueueManager::Clear(this); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/messagehandler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/messagehandler.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/messagehandler.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/messagehandler.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,68 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_MESSAGEHANDLER_H_ +#define WEBRTC_BASE_MESSAGEHANDLER_H_ + +#include "webrtc/base/constructormagic.h" + +namespace rtc { + +struct Message; + +// Messages get dispatched to a MessageHandler + +class MessageHandler { + public: + virtual ~MessageHandler(); + virtual void OnMessage(Message* msg) = 0; + + protected: + MessageHandler() {} + + private: + DISALLOW_COPY_AND_ASSIGN(MessageHandler); +}; + +// Helper class to facilitate executing a functor on a thread. +template +class FunctorMessageHandler : public MessageHandler { + public: + explicit FunctorMessageHandler(const FunctorT& functor) + : functor_(functor) {} + virtual void OnMessage(Message* msg) { + result_ = functor_(); + } + const ReturnT& result() const { return result_; } + + private: + FunctorT functor_; + ReturnT result_; +}; + +// Specialization for ReturnT of void. +template +class FunctorMessageHandler : public MessageHandler { + public: + explicit FunctorMessageHandler(const FunctorT& functor) + : functor_(functor) {} + virtual void OnMessage(Message* msg) { + functor_(); + } + void result() const {} + + private: + FunctorT functor_; +}; + + +} // namespace rtc + +#endif // WEBRTC_BASE_MESSAGEHANDLER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/messagequeue.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/messagequeue.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/messagequeue.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/messagequeue.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,384 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if defined(WEBRTC_POSIX) +#include +#endif + +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/messagequeue.h" +#if defined(__native_client__) +#include "webrtc/base/nullsocketserver.h" +typedef rtc::NullSocketServer DefaultSocketServer; +#else +#include "webrtc/base/physicalsocketserver.h" +typedef rtc::PhysicalSocketServer DefaultSocketServer; +#endif + +namespace rtc { + +const uint32 kMaxMsgLatency = 150; // 150 ms + +//------------------------------------------------------------------ +// MessageQueueManager + +MessageQueueManager* MessageQueueManager::instance_ = NULL; + +MessageQueueManager* MessageQueueManager::Instance() { + // Note: This is not thread safe, but it is first called before threads are + // spawned. + if (!instance_) + instance_ = new MessageQueueManager; + return instance_; +} + +bool MessageQueueManager::IsInitialized() { + return instance_ != NULL; +} + +MessageQueueManager::MessageQueueManager() { +} + +MessageQueueManager::~MessageQueueManager() { +} + +void MessageQueueManager::Add(MessageQueue *message_queue) { + return Instance()->AddInternal(message_queue); +} +void MessageQueueManager::AddInternal(MessageQueue *message_queue) { + // MessageQueueManager methods should be non-reentrant, so we + // ASSERT that is the case. If any of these ASSERT, please + // contact bpm or jbeda. + ASSERT(!crit_.CurrentThreadIsOwner()); + CritScope cs(&crit_); + message_queues_.push_back(message_queue); +} + +void MessageQueueManager::Remove(MessageQueue *message_queue) { + // If there isn't a message queue manager instance, then there isn't a queue + // to remove. + if (!instance_) return; + return Instance()->RemoveInternal(message_queue); +} +void MessageQueueManager::RemoveInternal(MessageQueue *message_queue) { + ASSERT(!crit_.CurrentThreadIsOwner()); // See note above. + // If this is the last MessageQueue, destroy the manager as well so that + // we don't leak this object at program shutdown. As mentioned above, this is + // not thread-safe, but this should only happen at program termination (when + // the ThreadManager is destroyed, and threads are no longer active). + bool destroy = false; + { + CritScope cs(&crit_); + std::vector::iterator iter; + iter = std::find(message_queues_.begin(), message_queues_.end(), + message_queue); + if (iter != message_queues_.end()) { + message_queues_.erase(iter); + } + destroy = message_queues_.empty(); + } + if (destroy) { + instance_ = NULL; + delete this; + } +} + +void MessageQueueManager::Clear(MessageHandler *handler) { + // If there isn't a message queue manager instance, then there aren't any + // queues to remove this handler from. + if (!instance_) return; + return Instance()->ClearInternal(handler); +} +void MessageQueueManager::ClearInternal(MessageHandler *handler) { + ASSERT(!crit_.CurrentThreadIsOwner()); // See note above. + CritScope cs(&crit_); + std::vector::iterator iter; + for (iter = message_queues_.begin(); iter != message_queues_.end(); iter++) + (*iter)->Clear(handler); +} + +//------------------------------------------------------------------ +// MessageQueue + +MessageQueue::MessageQueue(SocketServer* ss) + : ss_(ss), fStop_(false), fPeekKeep_(false), + dmsgq_next_num_(0) { + if (!ss_) { + // Currently, MessageQueue holds a socket server, and is the base class for + // Thread. It seems like it makes more sense for Thread to hold the socket + // server, and provide it to the MessageQueue, since the Thread controls + // the I/O model, and MQ is agnostic to those details. Anyway, this causes + // messagequeue_unittest to depend on network libraries... yuck. + default_ss_.reset(new DefaultSocketServer()); + ss_ = default_ss_.get(); + } + ss_->SetMessageQueue(this); + MessageQueueManager::Add(this); +} + +MessageQueue::~MessageQueue() { + // The signal is done from here to ensure + // that it always gets called when the queue + // is going away. + SignalQueueDestroyed(); + MessageQueueManager::Remove(this); + Clear(NULL); + if (ss_) { + ss_->SetMessageQueue(NULL); + } +} + +void MessageQueue::set_socketserver(SocketServer* ss) { + ss_ = ss ? ss : default_ss_.get(); + ss_->SetMessageQueue(this); +} + +void MessageQueue::Quit() { + fStop_ = true; + ss_->WakeUp(); +} + +bool MessageQueue::IsQuitting() { + return fStop_; +} + +void MessageQueue::Restart() { + fStop_ = false; +} + +bool MessageQueue::Peek(Message *pmsg, int cmsWait) { + if (fPeekKeep_) { + *pmsg = msgPeek_; + return true; + } + if (!Get(pmsg, cmsWait)) + return false; + msgPeek_ = *pmsg; + fPeekKeep_ = true; + return true; +} + +bool MessageQueue::Get(Message *pmsg, int cmsWait, bool process_io) { + // Return and clear peek if present + // Always return the peek if it exists so there is Peek/Get symmetry + + if (fPeekKeep_) { + *pmsg = msgPeek_; + fPeekKeep_ = false; + return true; + } + + // Get w/wait + timer scan / dispatch + socket / event multiplexer dispatch + + int cmsTotal = cmsWait; + int cmsElapsed = 0; + uint32 msStart = Time(); + uint32 msCurrent = msStart; + while (true) { + // Check for sent messages + ReceiveSends(); + + // Check for posted events + int cmsDelayNext = kForever; + bool first_pass = true; + while (true) { + // All queue operations need to be locked, but nothing else in this loop + // (specifically handling disposed message) can happen inside the crit. + // Otherwise, disposed MessageHandlers will cause deadlocks. + { + CritScope cs(&crit_); + // On the first pass, check for delayed messages that have been + // triggered and calculate the next trigger time. + if (first_pass) { + first_pass = false; + while (!dmsgq_.empty()) { + if (TimeIsLater(msCurrent, dmsgq_.top().msTrigger_)) { + cmsDelayNext = TimeDiff(dmsgq_.top().msTrigger_, msCurrent); + break; + } + msgq_.push_back(dmsgq_.top().msg_); + dmsgq_.pop(); + } + } + // Pull a message off the message queue, if available. + if (msgq_.empty()) { + break; + } else { + *pmsg = msgq_.front(); + msgq_.pop_front(); + } + } // crit_ is released here. + + // Log a warning for time-sensitive messages that we're late to deliver. + if (pmsg->ts_sensitive) { + int32 delay = TimeDiff(msCurrent, pmsg->ts_sensitive); + if (delay > 0) { + LOG_F(LS_WARNING) << "id: " << pmsg->message_id << " delay: " + << (delay + kMaxMsgLatency) << "ms"; + } + } + // If this was a dispose message, delete it and skip it. + if (MQID_DISPOSE == pmsg->message_id) { + ASSERT(NULL == pmsg->phandler); + delete pmsg->pdata; + *pmsg = Message(); + continue; + } + return true; + } + + if (fStop_) + break; + + // Which is shorter, the delay wait or the asked wait? + + int cmsNext; + if (cmsWait == kForever) { + cmsNext = cmsDelayNext; + } else { + cmsNext = _max(0, cmsTotal - cmsElapsed); + if ((cmsDelayNext != kForever) && (cmsDelayNext < cmsNext)) + cmsNext = cmsDelayNext; + } + + // Wait and multiplex in the meantime + if (!ss_->Wait(cmsNext, process_io)) + return false; + + // If the specified timeout expired, return + + msCurrent = Time(); + cmsElapsed = TimeDiff(msCurrent, msStart); + if (cmsWait != kForever) { + if (cmsElapsed >= cmsWait) + return false; + } + } + return false; +} + +void MessageQueue::ReceiveSends() { +} + +void MessageQueue::Post(MessageHandler *phandler, uint32 id, + MessageData *pdata, bool time_sensitive) { + if (fStop_) + return; + + // Keep thread safe + // Add the message to the end of the queue + // Signal for the multiplexer to return + + CritScope cs(&crit_); + Message msg; + msg.phandler = phandler; + msg.message_id = id; + msg.pdata = pdata; + if (time_sensitive) { + msg.ts_sensitive = Time() + kMaxMsgLatency; + } + msgq_.push_back(msg); + ss_->WakeUp(); +} + +void MessageQueue::DoDelayPost(int cmsDelay, uint32 tstamp, + MessageHandler *phandler, uint32 id, MessageData* pdata) { + if (fStop_) + return; + + // Keep thread safe + // Add to the priority queue. Gets sorted soonest first. + // Signal for the multiplexer to return. + + CritScope cs(&crit_); + Message msg; + msg.phandler = phandler; + msg.message_id = id; + msg.pdata = pdata; + DelayedMessage dmsg(cmsDelay, tstamp, dmsgq_next_num_, msg); + dmsgq_.push(dmsg); + // If this message queue processes 1 message every millisecond for 50 days, + // we will wrap this number. Even then, only messages with identical times + // will be misordered, and then only briefly. This is probably ok. + VERIFY(0 != ++dmsgq_next_num_); + ss_->WakeUp(); +} + +int MessageQueue::GetDelay() { + CritScope cs(&crit_); + + if (!msgq_.empty()) + return 0; + + if (!dmsgq_.empty()) { + int delay = TimeUntil(dmsgq_.top().msTrigger_); + if (delay < 0) + delay = 0; + return delay; + } + + return kForever; +} + +void MessageQueue::Clear(MessageHandler *phandler, uint32 id, + MessageList* removed) { + CritScope cs(&crit_); + + // Remove messages with phandler + + if (fPeekKeep_ && msgPeek_.Match(phandler, id)) { + if (removed) { + removed->push_back(msgPeek_); + } else { + delete msgPeek_.pdata; + } + fPeekKeep_ = false; + } + + // Remove from ordered message queue + + for (MessageList::iterator it = msgq_.begin(); it != msgq_.end();) { + if (it->Match(phandler, id)) { + if (removed) { + removed->push_back(*it); + } else { + delete it->pdata; + } + it = msgq_.erase(it); + } else { + ++it; + } + } + + // Remove from priority queue. Not directly iterable, so use this approach + + PriorityQueue::container_type::iterator new_end = dmsgq_.container().begin(); + for (PriorityQueue::container_type::iterator it = new_end; + it != dmsgq_.container().end(); ++it) { + if (it->msg_.Match(phandler, id)) { + if (removed) { + removed->push_back(it->msg_); + } else { + delete it->msg_.pdata; + } + } else { + *new_end++ = *it; + } + } + dmsgq_.container().erase(new_end, dmsgq_.container().end()); + dmsgq_.reheap(); +} + +void MessageQueue::Dispatch(Message *pmsg) { + pmsg->phandler->OnMessage(pmsg); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/messagequeue.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/messagequeue.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/messagequeue.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/messagequeue.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,254 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_MESSAGEQUEUE_H_ +#define WEBRTC_BASE_MESSAGEQUEUE_H_ + +#include + +#include +#include +#include +#include + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/criticalsection.h" +#include "webrtc/base/messagehandler.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/scoped_ref_ptr.h" +#include "webrtc/base/sigslot.h" +#include "webrtc/base/socketserver.h" +#include "webrtc/base/timeutils.h" + +namespace rtc { + +struct Message; +class MessageQueue; + +// MessageQueueManager does cleanup of of message queues + +class MessageQueueManager { + public: + static void Add(MessageQueue *message_queue); + static void Remove(MessageQueue *message_queue); + static void Clear(MessageHandler *handler); + + // For testing purposes, we expose whether or not the MessageQueueManager + // instance has been initialized. It has no other use relative to the rest of + // the functions of this class, which auto-initialize the underlying + // MessageQueueManager instance when necessary. + static bool IsInitialized(); + + private: + static MessageQueueManager* Instance(); + + MessageQueueManager(); + ~MessageQueueManager(); + + void AddInternal(MessageQueue *message_queue); + void RemoveInternal(MessageQueue *message_queue); + void ClearInternal(MessageHandler *handler); + + static MessageQueueManager* instance_; + // This list contains all live MessageQueues. + std::vector message_queues_; + CriticalSection crit_; +}; + +// Derive from this for specialized data +// App manages lifetime, except when messages are purged + +class MessageData { + public: + MessageData() {} + virtual ~MessageData() {} +}; + +template +class TypedMessageData : public MessageData { + public: + explicit TypedMessageData(const T& data) : data_(data) { } + const T& data() const { return data_; } + T& data() { return data_; } + private: + T data_; +}; + +// Like TypedMessageData, but for pointers that require a delete. +template +class ScopedMessageData : public MessageData { + public: + explicit ScopedMessageData(T* data) : data_(data) { } + const scoped_ptr& data() const { return data_; } + scoped_ptr& data() { return data_; } + private: + scoped_ptr data_; +}; + +// Like ScopedMessageData, but for reference counted pointers. +template +class ScopedRefMessageData : public MessageData { + public: + explicit ScopedRefMessageData(T* data) : data_(data) { } + const scoped_refptr& data() const { return data_; } + scoped_refptr& data() { return data_; } + private: + scoped_refptr data_; +}; + +template +inline MessageData* WrapMessageData(const T& data) { + return new TypedMessageData(data); +} + +template +inline const T& UseMessageData(MessageData* data) { + return static_cast< TypedMessageData* >(data)->data(); +} + +template +class DisposeData : public MessageData { + public: + explicit DisposeData(T* data) : data_(data) { } + virtual ~DisposeData() { delete data_; } + private: + T* data_; +}; + +const uint32 MQID_ANY = static_cast(-1); +const uint32 MQID_DISPOSE = static_cast(-2); + +// No destructor + +struct Message { + Message() { + memset(this, 0, sizeof(*this)); + } + inline bool Match(MessageHandler* handler, uint32 id) const { + return (handler == NULL || handler == phandler) + && (id == MQID_ANY || id == message_id); + } + MessageHandler *phandler; + uint32 message_id; + MessageData *pdata; + uint32 ts_sensitive; +}; + +typedef std::list MessageList; + +// DelayedMessage goes into a priority queue, sorted by trigger time. Messages +// with the same trigger time are processed in num_ (FIFO) order. + +class DelayedMessage { + public: + DelayedMessage(int delay, uint32 trigger, uint32 num, const Message& msg) + : cmsDelay_(delay), msTrigger_(trigger), num_(num), msg_(msg) { } + + bool operator< (const DelayedMessage& dmsg) const { + return (dmsg.msTrigger_ < msTrigger_) + || ((dmsg.msTrigger_ == msTrigger_) && (dmsg.num_ < num_)); + } + + int cmsDelay_; // for debugging + uint32 msTrigger_; + uint32 num_; + Message msg_; +}; + +class MessageQueue { + public: + explicit MessageQueue(SocketServer* ss = NULL); + virtual ~MessageQueue(); + + SocketServer* socketserver() { return ss_; } + void set_socketserver(SocketServer* ss); + + // Note: The behavior of MessageQueue has changed. When a MQ is stopped, + // futher Posts and Sends will fail. However, any pending Sends and *ready* + // Posts (as opposed to unexpired delayed Posts) will be delivered before + // Get (or Peek) returns false. By guaranteeing delivery of those messages, + // we eliminate the race condition when an MessageHandler and MessageQueue + // may be destroyed independently of each other. + virtual void Quit(); + virtual bool IsQuitting(); + virtual void Restart(); + + // Get() will process I/O until: + // 1) A message is available (returns true) + // 2) cmsWait seconds have elapsed (returns false) + // 3) Stop() is called (returns false) + virtual bool Get(Message *pmsg, int cmsWait = kForever, + bool process_io = true); + virtual bool Peek(Message *pmsg, int cmsWait = 0); + virtual void Post(MessageHandler *phandler, uint32 id = 0, + MessageData *pdata = NULL, bool time_sensitive = false); + virtual void PostDelayed(int cmsDelay, MessageHandler *phandler, + uint32 id = 0, MessageData *pdata = NULL) { + return DoDelayPost(cmsDelay, TimeAfter(cmsDelay), phandler, id, pdata); + } + virtual void PostAt(uint32 tstamp, MessageHandler *phandler, + uint32 id = 0, MessageData *pdata = NULL) { + return DoDelayPost(TimeUntil(tstamp), tstamp, phandler, id, pdata); + } + virtual void Clear(MessageHandler *phandler, uint32 id = MQID_ANY, + MessageList* removed = NULL); + virtual void Dispatch(Message *pmsg); + virtual void ReceiveSends(); + + // Amount of time until the next message can be retrieved + virtual int GetDelay(); + + bool empty() const { return size() == 0u; } + size_t size() const { + CritScope cs(&crit_); // msgq_.size() is not thread safe. + return msgq_.size() + dmsgq_.size() + (fPeekKeep_ ? 1u : 0u); + } + + // Internally posts a message which causes the doomed object to be deleted + template void Dispose(T* doomed) { + if (doomed) { + Post(NULL, MQID_DISPOSE, new DisposeData(doomed)); + } + } + + // When this signal is sent out, any references to this queue should + // no longer be used. + sigslot::signal0<> SignalQueueDestroyed; + + protected: + class PriorityQueue : public std::priority_queue { + public: + container_type& container() { return c; } + void reheap() { make_heap(c.begin(), c.end(), comp); } + }; + + void DoDelayPost(int cmsDelay, uint32 tstamp, MessageHandler *phandler, + uint32 id, MessageData* pdata); + + // The SocketServer is not owned by MessageQueue. + SocketServer* ss_; + // If a server isn't supplied in the constructor, use this one. + scoped_ptr default_ss_; + bool fStop_; + bool fPeekKeep_; + Message msgPeek_; + MessageList msgq_; + PriorityQueue dmsgq_; + uint32 dmsgq_next_num_; + mutable CriticalSection crit_; + + private: + DISALLOW_COPY_AND_ASSIGN(MessageQueue); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_MESSAGEQUEUE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/messagequeue_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/messagequeue_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/messagequeue_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/messagequeue_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,141 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/messagequeue.h" + +#include "webrtc/base/bind.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/timeutils.h" +#include "webrtc/base/nullsocketserver.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +using namespace rtc; + +class MessageQueueTest: public testing::Test, public MessageQueue { + public: + bool IsLocked_Worker() { + if (!crit_.TryEnter()) { + return true; + } + crit_.Leave(); + return false; + } + bool IsLocked() { + // We have to do this on a worker thread, or else the TryEnter will + // succeed, since our critical sections are reentrant. + Thread worker; + worker.Start(); + return worker.Invoke( + rtc::Bind(&MessageQueueTest::IsLocked_Worker, this)); + } +}; + +struct DeletedLockChecker { + DeletedLockChecker(MessageQueueTest* test, bool* was_locked, bool* deleted) + : test(test), was_locked(was_locked), deleted(deleted) { } + ~DeletedLockChecker() { + *deleted = true; + *was_locked = test->IsLocked(); + } + MessageQueueTest* test; + bool* was_locked; + bool* deleted; +}; + +static void DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder( + MessageQueue* q) { + EXPECT_TRUE(q != NULL); + TimeStamp now = Time(); + q->PostAt(now, NULL, 3); + q->PostAt(now - 2, NULL, 0); + q->PostAt(now - 1, NULL, 1); + q->PostAt(now, NULL, 4); + q->PostAt(now - 1, NULL, 2); + + Message msg; + for (size_t i=0; i<5; ++i) { + memset(&msg, 0, sizeof(msg)); + EXPECT_TRUE(q->Get(&msg, 0)); + EXPECT_EQ(i, msg.message_id); + } + + EXPECT_FALSE(q->Get(&msg, 0)); // No more messages +} + +TEST_F(MessageQueueTest, + DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder) { + MessageQueue q; + DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder(&q); + NullSocketServer nullss; + MessageQueue q_nullss(&nullss); + DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder(&q_nullss); +} + +TEST_F(MessageQueueTest, DisposeNotLocked) { + bool was_locked = true; + bool deleted = false; + DeletedLockChecker* d = new DeletedLockChecker(this, &was_locked, &deleted); + Dispose(d); + Message msg; + EXPECT_FALSE(Get(&msg, 0)); + EXPECT_TRUE(deleted); + EXPECT_FALSE(was_locked); +} + +class DeletedMessageHandler : public MessageHandler { + public: + explicit DeletedMessageHandler(bool* deleted) : deleted_(deleted) { } + ~DeletedMessageHandler() { + *deleted_ = true; + } + void OnMessage(Message* msg) { } + private: + bool* deleted_; +}; + +TEST_F(MessageQueueTest, DiposeHandlerWithPostedMessagePending) { + bool deleted = false; + DeletedMessageHandler *handler = new DeletedMessageHandler(&deleted); + // First, post a dispose. + Dispose(handler); + // Now, post a message, which should *not* be returned by Get(). + Post(handler, 1); + Message msg; + EXPECT_FALSE(Get(&msg, 0)); + EXPECT_TRUE(deleted); +} + +struct UnwrapMainThreadScope { + UnwrapMainThreadScope() : rewrap_(Thread::Current() != NULL) { + if (rewrap_) ThreadManager::Instance()->UnwrapCurrentThread(); + } + ~UnwrapMainThreadScope() { + if (rewrap_) ThreadManager::Instance()->WrapCurrentThread(); + } + private: + bool rewrap_; +}; + +TEST(MessageQueueManager, Clear) { + UnwrapMainThreadScope s; + if (MessageQueueManager::IsInitialized()) { + LOG(LS_INFO) << "Unable to run MessageQueueManager::Clear test, since the " + << "MessageQueueManager was already initialized by some " + << "other test in this run."; + return; + } + bool deleted = false; + DeletedMessageHandler* handler = new DeletedMessageHandler(&deleted); + delete handler; + EXPECT_TRUE(deleted); + EXPECT_FALSE(MessageQueueManager::IsInitialized()); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/move.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/move.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/move.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/move.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,213 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef THIRD_PARTY_WEBRTC_FILES_WEBRTC_BASE_MOVE_H_ +#define THIRD_PARTY_WEBRTC_FILES_WEBRTC_BASE_MOVE_H_ + +// Macro with the boilerplate that makes a type move-only in C++03. +// +// USAGE +// +// This macro should be used instead of DISALLOW_COPY_AND_ASSIGN to create +// a "move-only" type. Unlike DISALLOW_COPY_AND_ASSIGN, this macro should be +// the first line in a class declaration. +// +// A class using this macro must call .Pass() (or somehow be an r-value already) +// before it can be: +// +// * Passed as a function argument +// * Used as the right-hand side of an assignment +// * Returned from a function +// +// Each class will still need to define their own "move constructor" and "move +// operator=" to make this useful. Here's an example of the macro, the move +// constructor, and the move operator= from the scoped_ptr class: +// +// template +// class scoped_ptr { +// TALK_MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue) +// public: +// scoped_ptr(RValue& other) : ptr_(other.release()) { } +// scoped_ptr& operator=(RValue& other) { +// swap(other); +// return *this; +// } +// }; +// +// Note that the constructor must NOT be marked explicit. +// +// For consistency, the second parameter to the macro should always be RValue +// unless you have a strong reason to do otherwise. It is only exposed as a +// macro parameter so that the move constructor and move operator= don't look +// like they're using a phantom type. +// +// +// HOW THIS WORKS +// +// For a thorough explanation of this technique, see: +// +// http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Move_Constructor +// +// The summary is that we take advantage of 2 properties: +// +// 1) non-const references will not bind to r-values. +// 2) C++ can apply one user-defined conversion when initializing a +// variable. +// +// The first lets us disable the copy constructor and assignment operator +// by declaring private version of them with a non-const reference parameter. +// +// For l-values, direct initialization still fails like in +// DISALLOW_COPY_AND_ASSIGN because the copy constructor and assignment +// operators are private. +// +// For r-values, the situation is different. The copy constructor and +// assignment operator are not viable due to (1), so we are trying to call +// a non-existent constructor and non-existing operator= rather than a private +// one. Since we have not committed an error quite yet, we can provide an +// alternate conversion sequence and a constructor. We add +// +// * a private struct named "RValue" +// * a user-defined conversion "operator RValue()" +// * a "move constructor" and "move operator=" that take the RValue& as +// their sole parameter. +// +// Only r-values will trigger this sequence and execute our "move constructor" +// or "move operator=." L-values will match the private copy constructor and +// operator= first giving a "private in this context" error. This combination +// gives us a move-only type. +// +// For signaling a destructive transfer of data from an l-value, we provide a +// method named Pass() which creates an r-value for the current instance +// triggering the move constructor or move operator=. +// +// Other ways to get r-values is to use the result of an expression like a +// function call. +// +// Here's an example with comments explaining what gets triggered where: +// +// class Foo { +// TALK_MOVE_ONLY_TYPE_FOR_CPP_03(Foo, RValue); +// +// public: +// ... API ... +// Foo(RValue other); // Move constructor. +// Foo& operator=(RValue rhs); // Move operator= +// }; +// +// Foo MakeFoo(); // Function that returns a Foo. +// +// Foo f; +// Foo f_copy(f); // ERROR: Foo(Foo&) is private in this context. +// Foo f_assign; +// f_assign = f; // ERROR: operator=(Foo&) is private in this context. +// +// +// Foo f(MakeFoo()); // R-value so alternate conversion executed. +// Foo f_copy(f.Pass()); // R-value so alternate conversion executed. +// f = f_copy.Pass(); // R-value so alternate conversion executed. +// +// +// IMPLEMENTATION SUBTLETIES WITH RValue +// +// The RValue struct is just a container for a pointer back to the original +// object. It should only ever be created as a temporary, and no external +// class should ever declare it or use it in a parameter. +// +// It is tempting to want to use the RValue type in function parameters, but +// excluding the limited usage here for the move constructor and move +// operator=, doing so would mean that the function could take both r-values +// and l-values equially which is unexpected. See COMPARED To Boost.Move for +// more details. +// +// An alternate, and incorrect, implementation of the RValue class used by +// Boost.Move makes RValue a fieldless child of the move-only type. RValue& +// is then used in place of RValue in the various operators. The RValue& is +// "created" by doing *reinterpret_cast(this). This has the appeal +// of never creating a temporary RValue struct even with optimizations +// disabled. Also, by virtue of inheritance you can treat the RValue +// reference as if it were the move-only type itself. Unfortunately, +// using the result of this reinterpret_cast<> is actually undefined behavior +// due to C++98 5.2.10.7. In certain compilers (e.g., NaCl) the optimizer +// will generate non-working code. +// +// In optimized builds, both implementations generate the same assembly so we +// choose the one that adheres to the standard. +// +// +// COMPARED TO C++11 +// +// In C++11, you would implement this functionality using an r-value reference +// and our .Pass() method would be replaced with a call to std::move(). +// +// This emulation also has a deficiency where it uses up the single +// user-defined conversion allowed by C++ during initialization. This can +// cause problems in some API edge cases. For instance, in scoped_ptr, it is +// impossible to make a function "void Foo(scoped_ptr p)" accept a +// value of type scoped_ptr even if you add a constructor to +// scoped_ptr<> that would make it look like it should work. C++11 does not +// have this deficiency. +// +// +// COMPARED TO Boost.Move +// +// Our implementation similar to Boost.Move, but we keep the RValue struct +// private to the move-only type, and we don't use the reinterpret_cast<> hack. +// +// In Boost.Move, RValue is the boost::rv<> template. This type can be used +// when writing APIs like: +// +// void MyFunc(boost::rv& f) +// +// that can take advantage of rv<> to avoid extra copies of a type. However you +// would still be able to call this version of MyFunc with an l-value: +// +// Foo f; +// MyFunc(f); // Uh oh, we probably just destroyed |f| w/o calling Pass(). +// +// unless someone is very careful to also declare a parallel override like: +// +// void MyFunc(const Foo& f) +// +// that would catch the l-values first. This was declared unsafe in C++11 and +// a C++11 compiler will explicitly fail MyFunc(f). Unfortunately, we cannot +// ensure this in C++03. +// +// Since we have no need for writing such APIs yet, our implementation keeps +// RValue private and uses a .Pass() method to do the conversion instead of +// trying to write a version of "std::move()." Writing an API like std::move() +// would require the RValue struct to be public. +// +// +// CAVEATS +// +// If you include a move-only type as a field inside a class that does not +// explicitly declare a copy constructor, the containing class's implicit +// copy constructor will change from Containing(const Containing&) to +// Containing(Containing&). This can cause some unexpected errors. +// +// http://llvm.org/bugs/show_bug.cgi?id=11528 +// +// The workaround is to explicitly declare your copy constructor. +// +#define TALK_MOVE_ONLY_TYPE_FOR_CPP_03(type, rvalue_type) \ + private: \ + struct rvalue_type { \ + explicit rvalue_type(type* object) : object(object) {} \ + type* object; \ + }; \ + type(type&); \ + void operator=(type&); \ + public: \ + operator rvalue_type() { return rvalue_type(this); } \ + type Pass() { return type(rvalue_type(this)); } \ + private: + +#endif // THIRD_PARTY_WEBRTC_FILES_WEBRTC_BASE_MOVE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/multipart.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/multipart.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/multipart.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/multipart.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,253 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + + +#include "webrtc/base/common.h" +#include "webrtc/base/httpcommon.h" +#include "webrtc/base/multipart.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// MultipartStream +/////////////////////////////////////////////////////////////////////////////// + +MultipartStream::MultipartStream(const std::string& type, + const std::string& boundary) + : type_(type), + boundary_(boundary), + adding_(true), + current_(0), + position_(0) { + // The content type should be multipart/*. + ASSERT(0 == strncmp(type_.c_str(), "multipart/", 10)); +} + +MultipartStream::~MultipartStream() { + Close(); +} + +void MultipartStream::GetContentType(std::string* content_type) { + ASSERT(NULL != content_type); + content_type->assign(type_); + content_type->append("; boundary="); + content_type->append(boundary_); +} + +bool MultipartStream::AddPart(StreamInterface* data_stream, + const std::string& content_disposition, + const std::string& content_type) { + if (!AddPart("", content_disposition, content_type)) + return false; + parts_.push_back(data_stream); + data_stream->SignalEvent.connect(this, &MultipartStream::OnEvent); + return true; +} + +bool MultipartStream::AddPart(const std::string& data, + const std::string& content_disposition, + const std::string& content_type) { + ASSERT(adding_); + if (!adding_) + return false; + std::stringstream ss; + if (!parts_.empty()) { + ss << "\r\n"; + } + ss << "--" << boundary_ << "\r\n"; + if (!content_disposition.empty()) { + ss << ToString(HH_CONTENT_DISPOSITION) << ": " + << content_disposition << "\r\n"; + } + if (!content_type.empty()) { + ss << ToString(HH_CONTENT_TYPE) << ": " + << content_type << "\r\n"; + } + ss << "\r\n" << data; + parts_.push_back(new MemoryStream(ss.str().data(), ss.str().size())); + return true; +} + +void MultipartStream::EndParts() { + ASSERT(adding_); + if (!adding_) + return; + + std::stringstream ss; + if (!parts_.empty()) { + ss << "\r\n"; + } + ss << "--" << boundary_ << "--" << "\r\n"; + parts_.push_back(new MemoryStream(ss.str().data(), ss.str().size())); + + ASSERT(0 == current_); + ASSERT(0 == position_); + adding_ = false; + SignalEvent(this, SE_OPEN | SE_READ, 0); +} + +size_t MultipartStream::GetPartSize(const std::string& data, + const std::string& content_disposition, + const std::string& content_type) const { + size_t size = 0; + if (!parts_.empty()) { + size += 2; // for "\r\n"; + } + size += boundary_.size() + 4; // for "--boundary_\r\n"; + if (!content_disposition.empty()) { + // for ToString(HH_CONTENT_DISPOSITION): content_disposition\r\n + size += std::string(ToString(HH_CONTENT_DISPOSITION)).size() + 2 + + content_disposition.size() + 2; + } + if (!content_type.empty()) { + // for ToString(HH_CONTENT_TYPE): content_type\r\n + size += std::string(ToString(HH_CONTENT_TYPE)).size() + 2 + + content_type.size() + 2; + } + size += 2 + data.size(); // for \r\ndata + return size; +} + +size_t MultipartStream::GetEndPartSize() const { + size_t size = 0; + if (!parts_.empty()) { + size += 2; // for "\r\n"; + } + size += boundary_.size() + 6; // for "--boundary_--\r\n"; + return size; +} + +// +// StreamInterface +// + +StreamState MultipartStream::GetState() const { + if (adding_) { + return SS_OPENING; + } + return (current_ < parts_.size()) ? SS_OPEN : SS_CLOSED; +} + +StreamResult MultipartStream::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + if (adding_) { + return SR_BLOCK; + } + size_t local_read; + if (!read) read = &local_read; + while (current_ < parts_.size()) { + StreamResult result = parts_[current_]->Read(buffer, buffer_len, read, + error); + if (SR_EOS != result) { + if (SR_SUCCESS == result) { + position_ += *read; + } + return result; + } + ++current_; + } + return SR_EOS; +} + +StreamResult MultipartStream::Write(const void* data, size_t data_len, + size_t* written, int* error) { + if (error) { + *error = -1; + } + return SR_ERROR; +} + +void MultipartStream::Close() { + for (size_t i = 0; i < parts_.size(); ++i) { + delete parts_[i]; + } + parts_.clear(); + adding_ = false; + current_ = 0; + position_ = 0; +} + +bool MultipartStream::SetPosition(size_t position) { + if (adding_) { + return false; + } + size_t part_size, part_offset = 0; + for (size_t i = 0; i < parts_.size(); ++i) { + if (!parts_[i]->GetSize(&part_size)) { + return false; + } + if (part_offset + part_size > position) { + for (size_t j = i+1; j < _min(parts_.size(), current_+1); ++j) { + if (!parts_[j]->Rewind()) { + return false; + } + } + if (!parts_[i]->SetPosition(position - part_offset)) { + return false; + } + current_ = i; + position_ = position; + return true; + } + part_offset += part_size; + } + return false; +} + +bool MultipartStream::GetPosition(size_t* position) const { + if (position) { + *position = position_; + } + return true; +} + +bool MultipartStream::GetSize(size_t* size) const { + size_t part_size, total_size = 0; + for (size_t i = 0; i < parts_.size(); ++i) { + if (!parts_[i]->GetSize(&part_size)) { + return false; + } + total_size += part_size; + } + if (size) { + *size = total_size; + } + return true; +} + +bool MultipartStream::GetAvailable(size_t* size) const { + if (adding_) { + return false; + } + size_t part_size, total_size = 0; + for (size_t i = current_; i < parts_.size(); ++i) { + if (!parts_[i]->GetAvailable(&part_size)) { + return false; + } + total_size += part_size; + } + if (size) { + *size = total_size; + } + return true; +} + +// +// StreamInterface Slots +// + +void MultipartStream::OnEvent(StreamInterface* stream, int events, int error) { + if (adding_ || (current_ >= parts_.size()) || (parts_[current_] != stream)) { + return; + } + SignalEvent(this, events, error); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/multipart.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/multipart.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/multipart.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/multipart.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,79 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_MULTIPART_H__ +#define WEBRTC_BASE_MULTIPART_H__ + +#include +#include + +#include "webrtc/base/sigslot.h" +#include "webrtc/base/stream.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// MultipartStream - Implements an RFC2046 multipart stream by concatenating +// the supplied parts together, and adding the correct boundaries. +/////////////////////////////////////////////////////////////////////////////// + +class MultipartStream : public StreamInterface, public sigslot::has_slots<> { + public: + MultipartStream(const std::string& type, const std::string& boundary); + virtual ~MultipartStream(); + + void GetContentType(std::string* content_type); + + // Note: If content_disposition and/or content_type are the empty string, + // they will be omitted. + bool AddPart(StreamInterface* data_stream, + const std::string& content_disposition, + const std::string& content_type); + bool AddPart(const std::string& data, + const std::string& content_disposition, + const std::string& content_type); + void EndParts(); + + // Calculates the size of a part before actually adding the part. + size_t GetPartSize(const std::string& data, + const std::string& content_disposition, + const std::string& content_type) const; + size_t GetEndPartSize() const; + + // StreamInterface + virtual StreamState GetState() const; + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + virtual void Close(); + virtual bool SetPosition(size_t position); + virtual bool GetPosition(size_t* position) const; + virtual bool GetSize(size_t* size) const; + virtual bool GetAvailable(size_t* size) const; + + private: + typedef std::vector PartList; + + // StreamInterface Slots + void OnEvent(StreamInterface* stream, int events, int error); + + std::string type_, boundary_; + PartList parts_; + bool adding_; + size_t current_; // The index into parts_ of the current read position. + size_t position_; // The current read position in bytes. + + DISALLOW_COPY_AND_ASSIGN(MultipartStream); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_MULTIPART_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/multipart_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/multipart_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/multipart_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/multipart_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,125 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/gunit.h" +#include "webrtc/base/helpers.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/pathutils.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/multipart.h" + +namespace rtc { + +static const std::string kTestMultipartBoundary = "123456789987654321"; +static const std::string kTestContentType = + "multipart/form-data; boundary=123456789987654321"; +static const char kTestData[] = "This is a test."; +static const char kTestStreamContent[] = "This is a test stream."; + +TEST(MultipartTest, TestBasicOperations) { + MultipartStream multipart("multipart/form-data", kTestMultipartBoundary); + std::string content_type; + multipart.GetContentType(&content_type); + EXPECT_EQ(kTestContentType, content_type); + + EXPECT_EQ(rtc::SS_OPENING, multipart.GetState()); + + // The multipart stream contains only --boundary--\r\n + size_t end_part_size = multipart.GetEndPartSize(); + multipart.EndParts(); + EXPECT_EQ(rtc::SS_OPEN, multipart.GetState()); + size_t size; + EXPECT_TRUE(multipart.GetSize(&size)); + EXPECT_EQ(end_part_size, size); + + // Write is not supported. + EXPECT_EQ(rtc::SR_ERROR, + multipart.Write(kTestData, sizeof(kTestData), NULL, NULL)); + + multipart.Close(); + EXPECT_EQ(rtc::SS_CLOSED, multipart.GetState()); + EXPECT_TRUE(multipart.GetSize(&size)); + EXPECT_EQ(0U, size); +} + +TEST(MultipartTest, TestAddAndRead) { + MultipartStream multipart("multipart/form-data", kTestMultipartBoundary); + + size_t part_size = + multipart.GetPartSize(kTestData, "form-data; name=\"text\"", "text"); + EXPECT_TRUE(multipart.AddPart(kTestData, "form-data; name=\"text\"", "text")); + size_t size; + EXPECT_TRUE(multipart.GetSize(&size)); + EXPECT_EQ(part_size, size); + + rtc::scoped_ptr stream( + new rtc::MemoryStream(kTestStreamContent)); + size_t stream_size = 0; + EXPECT_TRUE(stream->GetSize(&stream_size)); + part_size += + multipart.GetPartSize("", "form-data; name=\"stream\"", "stream"); + part_size += stream_size; + + EXPECT_TRUE(multipart.AddPart( + new rtc::MemoryStream(kTestStreamContent), + "form-data; name=\"stream\"", + "stream")); + EXPECT_TRUE(multipart.GetSize(&size)); + EXPECT_EQ(part_size, size); + + // In adding state, block read. + char buffer[1024]; + EXPECT_EQ(rtc::SR_BLOCK, + multipart.Read(buffer, sizeof(buffer), NULL, NULL)); + // Write is not supported. + EXPECT_EQ(rtc::SR_ERROR, + multipart.Write(buffer, sizeof(buffer), NULL, NULL)); + + part_size += multipart.GetEndPartSize(); + multipart.EndParts(); + EXPECT_TRUE(multipart.GetSize(&size)); + EXPECT_EQ(part_size, size); + + // Read the multipart stream into StringStream + std::string str; + rtc::StringStream str_stream(str); + EXPECT_EQ(rtc::SR_SUCCESS, + Flow(&multipart, buffer, sizeof(buffer), &str_stream)); + EXPECT_EQ(size, str.length()); + + // Search three boundaries and two parts in the order. + size_t pos = 0; + pos = str.find(kTestMultipartBoundary); + EXPECT_NE(std::string::npos, pos); + pos += kTestMultipartBoundary.length(); + + pos = str.find(kTestData, pos); + EXPECT_NE(std::string::npos, pos); + pos += sizeof(kTestData); + + pos = str.find(kTestMultipartBoundary, pos); + EXPECT_NE(std::string::npos, pos); + pos += kTestMultipartBoundary.length(); + + pos = str.find(kTestStreamContent, pos); + EXPECT_NE(std::string::npos, pos); + pos += sizeof(kTestStreamContent); + + pos = str.find(kTestMultipartBoundary, pos); + EXPECT_NE(std::string::npos, pos); + pos += kTestMultipartBoundary.length(); + + pos = str.find(kTestMultipartBoundary, pos); + EXPECT_EQ(std::string::npos, pos); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/natserver.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/natserver.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/natserver.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/natserver.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,186 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/natsocketfactory.h" +#include "webrtc/base/natserver.h" +#include "webrtc/base/logging.h" + +namespace rtc { + +RouteCmp::RouteCmp(NAT* nat) : symmetric(nat->IsSymmetric()) { +} + +size_t RouteCmp::operator()(const SocketAddressPair& r) const { + size_t h = r.source().Hash(); + if (symmetric) + h ^= r.destination().Hash(); + return h; +} + +bool RouteCmp::operator()( + const SocketAddressPair& r1, const SocketAddressPair& r2) const { + if (r1.source() < r2.source()) + return true; + if (r2.source() < r1.source()) + return false; + if (symmetric && (r1.destination() < r2.destination())) + return true; + if (symmetric && (r2.destination() < r1.destination())) + return false; + return false; +} + +AddrCmp::AddrCmp(NAT* nat) + : use_ip(nat->FiltersIP()), use_port(nat->FiltersPort()) { +} + +size_t AddrCmp::operator()(const SocketAddress& a) const { + size_t h = 0; + if (use_ip) + h ^= HashIP(a.ipaddr()); + if (use_port) + h ^= a.port() | (a.port() << 16); + return h; +} + +bool AddrCmp::operator()( + const SocketAddress& a1, const SocketAddress& a2) const { + if (use_ip && (a1.ipaddr() < a2.ipaddr())) + return true; + if (use_ip && (a2.ipaddr() < a1.ipaddr())) + return false; + if (use_port && (a1.port() < a2.port())) + return true; + if (use_port && (a2.port() < a1.port())) + return false; + return false; +} + +NATServer::NATServer( + NATType type, SocketFactory* internal, const SocketAddress& internal_addr, + SocketFactory* external, const SocketAddress& external_ip) + : external_(external), external_ip_(external_ip.ipaddr(), 0) { + nat_ = NAT::Create(type); + + server_socket_ = AsyncUDPSocket::Create(internal, internal_addr); + server_socket_->SignalReadPacket.connect(this, &NATServer::OnInternalPacket); + + int_map_ = new InternalMap(RouteCmp(nat_)); + ext_map_ = new ExternalMap(); +} + +NATServer::~NATServer() { + for (InternalMap::iterator iter = int_map_->begin(); + iter != int_map_->end(); + iter++) + delete iter->second; + + delete nat_; + delete server_socket_; + delete int_map_; + delete ext_map_; +} + +void NATServer::OnInternalPacket( + AsyncPacketSocket* socket, const char* buf, size_t size, + const SocketAddress& addr, const PacketTime& packet_time) { + + // Read the intended destination from the wire. + SocketAddress dest_addr; + size_t length = UnpackAddressFromNAT(buf, size, &dest_addr); + + // Find the translation for these addresses (allocating one if necessary). + SocketAddressPair route(addr, dest_addr); + InternalMap::iterator iter = int_map_->find(route); + if (iter == int_map_->end()) { + Translate(route); + iter = int_map_->find(route); + } + ASSERT(iter != int_map_->end()); + + // Allow the destination to send packets back to the source. + iter->second->WhitelistInsert(dest_addr); + + // Send the packet to its intended destination. + rtc::PacketOptions options; + iter->second->socket->SendTo(buf + length, size - length, dest_addr, options); +} + +void NATServer::OnExternalPacket( + AsyncPacketSocket* socket, const char* buf, size_t size, + const SocketAddress& remote_addr, const PacketTime& packet_time) { + + SocketAddress local_addr = socket->GetLocalAddress(); + + // Find the translation for this addresses. + ExternalMap::iterator iter = ext_map_->find(local_addr); + ASSERT(iter != ext_map_->end()); + + // Allow the NAT to reject this packet. + if (ShouldFilterOut(iter->second, remote_addr)) { + LOG(LS_INFO) << "Packet from " << remote_addr.ToSensitiveString() + << " was filtered out by the NAT."; + return; + } + + // Forward this packet to the internal address. + // First prepend the address in a quasi-STUN format. + scoped_ptr real_buf(new char[size + kNATEncodedIPv6AddressSize]); + size_t addrlength = PackAddressForNAT(real_buf.get(), + size + kNATEncodedIPv6AddressSize, + remote_addr); + // Copy the data part after the address. + rtc::PacketOptions options; + memcpy(real_buf.get() + addrlength, buf, size); + server_socket_->SendTo(real_buf.get(), size + addrlength, + iter->second->route.source(), options); +} + +void NATServer::Translate(const SocketAddressPair& route) { + AsyncUDPSocket* socket = AsyncUDPSocket::Create(external_, external_ip_); + + if (!socket) { + LOG(LS_ERROR) << "Couldn't find a free port!"; + return; + } + + TransEntry* entry = new TransEntry(route, socket, nat_); + (*int_map_)[route] = entry; + (*ext_map_)[socket->GetLocalAddress()] = entry; + socket->SignalReadPacket.connect(this, &NATServer::OnExternalPacket); +} + +bool NATServer::ShouldFilterOut(TransEntry* entry, + const SocketAddress& ext_addr) { + return entry->WhitelistContains(ext_addr); +} + +NATServer::TransEntry::TransEntry( + const SocketAddressPair& r, AsyncUDPSocket* s, NAT* nat) + : route(r), socket(s) { + whitelist = new AddressSet(AddrCmp(nat)); +} + +NATServer::TransEntry::~TransEntry() { + delete whitelist; + delete socket; +} + +void NATServer::TransEntry::WhitelistInsert(const SocketAddress& addr) { + CritScope cs(&crit_); + whitelist->insert(addr); +} + +bool NATServer::TransEntry::WhitelistContains(const SocketAddress& ext_addr) { + CritScope cs(&crit_); + return whitelist->find(ext_addr) == whitelist->end(); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/natserver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/natserver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/natserver.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/natserver.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,110 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_NATSERVER_H_ +#define WEBRTC_BASE_NATSERVER_H_ + +#include +#include + +#include "webrtc/base/asyncudpsocket.h" +#include "webrtc/base/socketaddresspair.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/socketfactory.h" +#include "webrtc/base/nattypes.h" + +namespace rtc { + +// Change how routes (socketaddress pairs) are compared based on the type of +// NAT. The NAT server maintains a hashtable of the routes that it knows +// about. So these affect which routes are treated the same. +struct RouteCmp { + explicit RouteCmp(NAT* nat); + size_t operator()(const SocketAddressPair& r) const; + bool operator()( + const SocketAddressPair& r1, const SocketAddressPair& r2) const; + + bool symmetric; +}; + +// Changes how addresses are compared based on the filtering rules of the NAT. +struct AddrCmp { + explicit AddrCmp(NAT* nat); + size_t operator()(const SocketAddress& r) const; + bool operator()(const SocketAddress& r1, const SocketAddress& r2) const; + + bool use_ip; + bool use_port; +}; + +// Implements the NAT device. It listens for packets on the internal network, +// translates them, and sends them out over the external network. + +const int NAT_SERVER_PORT = 4237; + +class NATServer : public sigslot::has_slots<> { + public: + NATServer( + NATType type, SocketFactory* internal, const SocketAddress& internal_addr, + SocketFactory* external, const SocketAddress& external_ip); + ~NATServer(); + + SocketAddress internal_address() const { + return server_socket_->GetLocalAddress(); + } + + // Packets received on one of the networks. + void OnInternalPacket(AsyncPacketSocket* socket, const char* buf, + size_t size, const SocketAddress& addr, + const PacketTime& packet_time); + void OnExternalPacket(AsyncPacketSocket* socket, const char* buf, + size_t size, const SocketAddress& remote_addr, + const PacketTime& packet_time); + + private: + typedef std::set AddressSet; + + /* Records a translation and the associated external socket. */ + struct TransEntry { + TransEntry(const SocketAddressPair& r, AsyncUDPSocket* s, NAT* nat); + ~TransEntry(); + + void WhitelistInsert(const SocketAddress& addr); + bool WhitelistContains(const SocketAddress& ext_addr); + + SocketAddressPair route; + AsyncUDPSocket* socket; + AddressSet* whitelist; + CriticalSection crit_; + }; + + typedef std::map InternalMap; + typedef std::map ExternalMap; + + /* Creates a new entry that translates the given route. */ + void Translate(const SocketAddressPair& route); + + /* Determines whether the NAT would filter out a packet from this address. */ + bool ShouldFilterOut(TransEntry* entry, const SocketAddress& ext_addr); + + NAT* nat_; + SocketFactory* internal_; + SocketFactory* external_; + SocketAddress external_ip_; + AsyncUDPSocket* server_socket_; + AsyncSocket* tcp_server_socket_; + InternalMap* int_map_; + ExternalMap* ext_map_; + DISALLOW_EVIL_CONSTRUCTORS(NATServer); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_NATSERVER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/natsocketfactory.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/natsocketfactory.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/natsocketfactory.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/natsocketfactory.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,487 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/natsocketfactory.h" + +#include "webrtc/base/logging.h" +#include "webrtc/base/natserver.h" +#include "webrtc/base/virtualsocketserver.h" + +namespace rtc { + +// Packs the given socketaddress into the buffer in buf, in the quasi-STUN +// format that the natserver uses. +// Returns 0 if an invalid address is passed. +size_t PackAddressForNAT(char* buf, size_t buf_size, + const SocketAddress& remote_addr) { + const IPAddress& ip = remote_addr.ipaddr(); + int family = ip.family(); + buf[0] = 0; + buf[1] = family; + // Writes the port. + *(reinterpret_cast(&buf[2])) = HostToNetwork16(remote_addr.port()); + if (family == AF_INET) { + ASSERT(buf_size >= kNATEncodedIPv4AddressSize); + in_addr v4addr = ip.ipv4_address(); + memcpy(&buf[4], &v4addr, kNATEncodedIPv4AddressSize - 4); + return kNATEncodedIPv4AddressSize; + } else if (family == AF_INET6) { + ASSERT(buf_size >= kNATEncodedIPv6AddressSize); + in6_addr v6addr = ip.ipv6_address(); + memcpy(&buf[4], &v6addr, kNATEncodedIPv6AddressSize - 4); + return kNATEncodedIPv6AddressSize; + } + return 0U; +} + +// Decodes the remote address from a packet that has been encoded with the nat's +// quasi-STUN format. Returns the length of the address (i.e., the offset into +// data where the original packet starts). +size_t UnpackAddressFromNAT(const char* buf, size_t buf_size, + SocketAddress* remote_addr) { + ASSERT(buf_size >= 8); + ASSERT(buf[0] == 0); + int family = buf[1]; + uint16 port = NetworkToHost16(*(reinterpret_cast(&buf[2]))); + if (family == AF_INET) { + const in_addr* v4addr = reinterpret_cast(&buf[4]); + *remote_addr = SocketAddress(IPAddress(*v4addr), port); + return kNATEncodedIPv4AddressSize; + } else if (family == AF_INET6) { + ASSERT(buf_size >= 20); + const in6_addr* v6addr = reinterpret_cast(&buf[4]); + *remote_addr = SocketAddress(IPAddress(*v6addr), port); + return kNATEncodedIPv6AddressSize; + } + return 0U; +} + + +// NATSocket +class NATSocket : public AsyncSocket, public sigslot::has_slots<> { + public: + explicit NATSocket(NATInternalSocketFactory* sf, int family, int type) + : sf_(sf), family_(family), type_(type), connected_(false), + socket_(NULL), buf_(NULL), size_(0) { + } + + virtual ~NATSocket() { + delete socket_; + delete[] buf_; + } + + virtual SocketAddress GetLocalAddress() const { + return (socket_) ? socket_->GetLocalAddress() : SocketAddress(); + } + + virtual SocketAddress GetRemoteAddress() const { + return remote_addr_; // will be NIL if not connected + } + + virtual int Bind(const SocketAddress& addr) { + if (socket_) { // already bound, bubble up error + return -1; + } + + int result; + socket_ = sf_->CreateInternalSocket(family_, type_, addr, &server_addr_); + result = (socket_) ? socket_->Bind(addr) : -1; + if (result >= 0) { + socket_->SignalConnectEvent.connect(this, &NATSocket::OnConnectEvent); + socket_->SignalReadEvent.connect(this, &NATSocket::OnReadEvent); + socket_->SignalWriteEvent.connect(this, &NATSocket::OnWriteEvent); + socket_->SignalCloseEvent.connect(this, &NATSocket::OnCloseEvent); + } else { + server_addr_.Clear(); + delete socket_; + socket_ = NULL; + } + + return result; + } + + virtual int Connect(const SocketAddress& addr) { + if (!socket_) { // socket must be bound, for now + return -1; + } + + int result = 0; + if (type_ == SOCK_STREAM) { + result = socket_->Connect(server_addr_.IsNil() ? addr : server_addr_); + } else { + connected_ = true; + } + + if (result >= 0) { + remote_addr_ = addr; + } + + return result; + } + + virtual int Send(const void* data, size_t size) { + ASSERT(connected_); + return SendTo(data, size, remote_addr_); + } + + virtual int SendTo(const void* data, size_t size, const SocketAddress& addr) { + ASSERT(!connected_ || addr == remote_addr_); + if (server_addr_.IsNil() || type_ == SOCK_STREAM) { + return socket_->SendTo(data, size, addr); + } + // This array will be too large for IPv4 packets, but only by 12 bytes. + scoped_ptr buf(new char[size + kNATEncodedIPv6AddressSize]); + size_t addrlength = PackAddressForNAT(buf.get(), + size + kNATEncodedIPv6AddressSize, + addr); + size_t encoded_size = size + addrlength; + memcpy(buf.get() + addrlength, data, size); + int result = socket_->SendTo(buf.get(), encoded_size, server_addr_); + if (result >= 0) { + ASSERT(result == static_cast(encoded_size)); + result = result - static_cast(addrlength); + } + return result; + } + + virtual int Recv(void* data, size_t size) { + SocketAddress addr; + return RecvFrom(data, size, &addr); + } + + virtual int RecvFrom(void* data, size_t size, SocketAddress *out_addr) { + if (server_addr_.IsNil() || type_ == SOCK_STREAM) { + return socket_->RecvFrom(data, size, out_addr); + } + // Make sure we have enough room to read the requested amount plus the + // largest possible header address. + SocketAddress remote_addr; + Grow(size + kNATEncodedIPv6AddressSize); + + // Read the packet from the socket. + int result = socket_->RecvFrom(buf_, size_, &remote_addr); + if (result >= 0) { + ASSERT(remote_addr == server_addr_); + + // TODO: we need better framing so we know how many bytes we can + // return before we need to read the next address. For UDP, this will be + // fine as long as the reader always reads everything in the packet. + ASSERT((size_t)result < size_); + + // Decode the wire packet into the actual results. + SocketAddress real_remote_addr; + size_t addrlength = + UnpackAddressFromNAT(buf_, result, &real_remote_addr); + memcpy(data, buf_ + addrlength, result - addrlength); + + // Make sure this packet should be delivered before returning it. + if (!connected_ || (real_remote_addr == remote_addr_)) { + if (out_addr) + *out_addr = real_remote_addr; + result = result - static_cast(addrlength); + } else { + LOG(LS_ERROR) << "Dropping packet from unknown remote address: " + << real_remote_addr.ToString(); + result = 0; // Tell the caller we didn't read anything + } + } + + return result; + } + + virtual int Close() { + int result = 0; + if (socket_) { + result = socket_->Close(); + if (result >= 0) { + connected_ = false; + remote_addr_ = SocketAddress(); + delete socket_; + socket_ = NULL; + } + } + return result; + } + + virtual int Listen(int backlog) { + return socket_->Listen(backlog); + } + virtual AsyncSocket* Accept(SocketAddress *paddr) { + return socket_->Accept(paddr); + } + virtual int GetError() const { + return socket_->GetError(); + } + virtual void SetError(int error) { + socket_->SetError(error); + } + virtual ConnState GetState() const { + return connected_ ? CS_CONNECTED : CS_CLOSED; + } + virtual int EstimateMTU(uint16* mtu) { + return socket_->EstimateMTU(mtu); + } + virtual int GetOption(Option opt, int* value) { + return socket_->GetOption(opt, value); + } + virtual int SetOption(Option opt, int value) { + return socket_->SetOption(opt, value); + } + + void OnConnectEvent(AsyncSocket* socket) { + // If we're NATed, we need to send a request with the real addr to use. + ASSERT(socket == socket_); + if (server_addr_.IsNil()) { + connected_ = true; + SignalConnectEvent(this); + } else { + SendConnectRequest(); + } + } + void OnReadEvent(AsyncSocket* socket) { + // If we're NATed, we need to process the connect reply. + ASSERT(socket == socket_); + if (type_ == SOCK_STREAM && !server_addr_.IsNil() && !connected_) { + HandleConnectReply(); + } else { + SignalReadEvent(this); + } + } + void OnWriteEvent(AsyncSocket* socket) { + ASSERT(socket == socket_); + SignalWriteEvent(this); + } + void OnCloseEvent(AsyncSocket* socket, int error) { + ASSERT(socket == socket_); + SignalCloseEvent(this, error); + } + + private: + // Makes sure the buffer is at least the given size. + void Grow(size_t new_size) { + if (size_ < new_size) { + delete[] buf_; + size_ = new_size; + buf_ = new char[size_]; + } + } + + // Sends the destination address to the server to tell it to connect. + void SendConnectRequest() { + char buf[256]; + size_t length = PackAddressForNAT(buf, ARRAY_SIZE(buf), remote_addr_); + socket_->Send(buf, length); + } + + // Handles the byte sent back from the server and fires the appropriate event. + void HandleConnectReply() { + char code; + socket_->Recv(&code, sizeof(code)); + if (code == 0) { + SignalConnectEvent(this); + } else { + Close(); + SignalCloseEvent(this, code); + } + } + + NATInternalSocketFactory* sf_; + int family_; + int type_; + bool connected_; + SocketAddress remote_addr_; + SocketAddress server_addr_; // address of the NAT server + AsyncSocket* socket_; + char* buf_; + size_t size_; +}; + +// NATSocketFactory +NATSocketFactory::NATSocketFactory(SocketFactory* factory, + const SocketAddress& nat_addr) + : factory_(factory), nat_addr_(nat_addr) { +} + +Socket* NATSocketFactory::CreateSocket(int type) { + return CreateSocket(AF_INET, type); +} + +Socket* NATSocketFactory::CreateSocket(int family, int type) { + return new NATSocket(this, family, type); +} + +AsyncSocket* NATSocketFactory::CreateAsyncSocket(int type) { + return CreateAsyncSocket(AF_INET, type); +} + +AsyncSocket* NATSocketFactory::CreateAsyncSocket(int family, int type) { + return new NATSocket(this, family, type); +} + +AsyncSocket* NATSocketFactory::CreateInternalSocket(int family, int type, + const SocketAddress& local_addr, SocketAddress* nat_addr) { + *nat_addr = nat_addr_; + return factory_->CreateAsyncSocket(family, type); +} + +// NATSocketServer +NATSocketServer::NATSocketServer(SocketServer* server) + : server_(server), msg_queue_(NULL) { +} + +NATSocketServer::Translator* NATSocketServer::GetTranslator( + const SocketAddress& ext_ip) { + return nats_.Get(ext_ip); +} + +NATSocketServer::Translator* NATSocketServer::AddTranslator( + const SocketAddress& ext_ip, const SocketAddress& int_ip, NATType type) { + // Fail if a translator already exists with this extternal address. + if (nats_.Get(ext_ip)) + return NULL; + + return nats_.Add(ext_ip, new Translator(this, type, int_ip, server_, ext_ip)); +} + +void NATSocketServer::RemoveTranslator( + const SocketAddress& ext_ip) { + nats_.Remove(ext_ip); +} + +Socket* NATSocketServer::CreateSocket(int type) { + return CreateSocket(AF_INET, type); +} + +Socket* NATSocketServer::CreateSocket(int family, int type) { + return new NATSocket(this, family, type); +} + +AsyncSocket* NATSocketServer::CreateAsyncSocket(int type) { + return CreateAsyncSocket(AF_INET, type); +} + +AsyncSocket* NATSocketServer::CreateAsyncSocket(int family, int type) { + return new NATSocket(this, family, type); +} + +AsyncSocket* NATSocketServer::CreateInternalSocket(int family, int type, + const SocketAddress& local_addr, SocketAddress* nat_addr) { + AsyncSocket* socket = NULL; + Translator* nat = nats_.FindClient(local_addr); + if (nat) { + socket = nat->internal_factory()->CreateAsyncSocket(family, type); + *nat_addr = (type == SOCK_STREAM) ? + nat->internal_tcp_address() : nat->internal_address(); + } else { + socket = server_->CreateAsyncSocket(family, type); + } + return socket; +} + +// NATSocketServer::Translator +NATSocketServer::Translator::Translator( + NATSocketServer* server, NATType type, const SocketAddress& int_ip, + SocketFactory* ext_factory, const SocketAddress& ext_ip) + : server_(server) { + // Create a new private network, and a NATServer running on the private + // network that bridges to the external network. Also tell the private + // network to use the same message queue as us. + VirtualSocketServer* internal_server = new VirtualSocketServer(server_); + internal_server->SetMessageQueue(server_->queue()); + internal_factory_.reset(internal_server); + nat_server_.reset(new NATServer(type, internal_server, int_ip, + ext_factory, ext_ip)); +} + + +NATSocketServer::Translator* NATSocketServer::Translator::GetTranslator( + const SocketAddress& ext_ip) { + return nats_.Get(ext_ip); +} + +NATSocketServer::Translator* NATSocketServer::Translator::AddTranslator( + const SocketAddress& ext_ip, const SocketAddress& int_ip, NATType type) { + // Fail if a translator already exists with this extternal address. + if (nats_.Get(ext_ip)) + return NULL; + + AddClient(ext_ip); + return nats_.Add(ext_ip, + new Translator(server_, type, int_ip, server_, ext_ip)); +} +void NATSocketServer::Translator::RemoveTranslator( + const SocketAddress& ext_ip) { + nats_.Remove(ext_ip); + RemoveClient(ext_ip); +} + +bool NATSocketServer::Translator::AddClient( + const SocketAddress& int_ip) { + // Fail if a client already exists with this internal address. + if (clients_.find(int_ip) != clients_.end()) + return false; + + clients_.insert(int_ip); + return true; +} + +void NATSocketServer::Translator::RemoveClient( + const SocketAddress& int_ip) { + std::set::iterator it = clients_.find(int_ip); + if (it != clients_.end()) { + clients_.erase(it); + } +} + +NATSocketServer::Translator* NATSocketServer::Translator::FindClient( + const SocketAddress& int_ip) { + // See if we have the requested IP, or any of our children do. + return (clients_.find(int_ip) != clients_.end()) ? + this : nats_.FindClient(int_ip); +} + +// NATSocketServer::TranslatorMap +NATSocketServer::TranslatorMap::~TranslatorMap() { + for (TranslatorMap::iterator it = begin(); it != end(); ++it) { + delete it->second; + } +} + +NATSocketServer::Translator* NATSocketServer::TranslatorMap::Get( + const SocketAddress& ext_ip) { + TranslatorMap::iterator it = find(ext_ip); + return (it != end()) ? it->second : NULL; +} + +NATSocketServer::Translator* NATSocketServer::TranslatorMap::Add( + const SocketAddress& ext_ip, Translator* nat) { + (*this)[ext_ip] = nat; + return nat; +} + +void NATSocketServer::TranslatorMap::Remove( + const SocketAddress& ext_ip) { + TranslatorMap::iterator it = find(ext_ip); + if (it != end()) { + delete it->second; + erase(it); + } +} + +NATSocketServer::Translator* NATSocketServer::TranslatorMap::FindClient( + const SocketAddress& int_ip) { + Translator* nat = NULL; + for (TranslatorMap::iterator it = begin(); it != end() && !nat; ++it) { + nat = it->second->FindClient(int_ip); + } + return nat; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/natsocketfactory.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/natsocketfactory.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/natsocketfactory.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/natsocketfactory.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,166 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_NATSOCKETFACTORY_H_ +#define WEBRTC_BASE_NATSOCKETFACTORY_H_ + +#include +#include +#include + +#include "webrtc/base/natserver.h" +#include "webrtc/base/socketaddress.h" +#include "webrtc/base/socketserver.h" + +namespace rtc { + +const size_t kNATEncodedIPv4AddressSize = 8U; +const size_t kNATEncodedIPv6AddressSize = 20U; + +// Used by the NAT socket implementation. +class NATInternalSocketFactory { + public: + virtual ~NATInternalSocketFactory() {} + virtual AsyncSocket* CreateInternalSocket(int family, int type, + const SocketAddress& local_addr, SocketAddress* nat_addr) = 0; +}; + +// Creates sockets that will send all traffic through a NAT, using an existing +// NATServer instance running at nat_addr. The actual data is sent using sockets +// from a socket factory, given to the constructor. +class NATSocketFactory : public SocketFactory, public NATInternalSocketFactory { + public: + NATSocketFactory(SocketFactory* factory, const SocketAddress& nat_addr); + + // SocketFactory implementation + virtual Socket* CreateSocket(int type); + virtual Socket* CreateSocket(int family, int type); + virtual AsyncSocket* CreateAsyncSocket(int type); + virtual AsyncSocket* CreateAsyncSocket(int family, int type); + + // NATInternalSocketFactory implementation + virtual AsyncSocket* CreateInternalSocket(int family, int type, + const SocketAddress& local_addr, SocketAddress* nat_addr); + + private: + SocketFactory* factory_; + SocketAddress nat_addr_; + DISALLOW_EVIL_CONSTRUCTORS(NATSocketFactory); +}; + +// Creates sockets that will send traffic through a NAT depending on what +// address they bind to. This can be used to simulate a client on a NAT sending +// to a client that is not behind a NAT. +// Note that the internal addresses of clients must be unique. This is because +// there is only one socketserver per thread, and the Bind() address is used to +// figure out which NAT (if any) the socket should talk to. +// +// Example with 3 NATs (2 cascaded), and 3 clients. +// ss->AddTranslator("1.2.3.4", "192.168.0.1", NAT_ADDR_RESTRICTED); +// ss->AddTranslator("99.99.99.99", "10.0.0.1", NAT_SYMMETRIC)-> +// AddTranslator("10.0.0.2", "192.168.1.1", NAT_OPEN_CONE); +// ss->GetTranslator("1.2.3.4")->AddClient("1.2.3.4", "192.168.0.2"); +// ss->GetTranslator("99.99.99.99")->AddClient("10.0.0.3"); +// ss->GetTranslator("99.99.99.99")->GetTranslator("10.0.0.2")-> +// AddClient("192.168.1.2"); +class NATSocketServer : public SocketServer, public NATInternalSocketFactory { + public: + class Translator; + // holds a list of NATs + class TranslatorMap : private std::map { + public: + ~TranslatorMap(); + Translator* Get(const SocketAddress& ext_ip); + Translator* Add(const SocketAddress& ext_ip, Translator*); + void Remove(const SocketAddress& ext_ip); + Translator* FindClient(const SocketAddress& int_ip); + }; + + // a specific NAT + class Translator { + public: + Translator(NATSocketServer* server, NATType type, + const SocketAddress& int_addr, SocketFactory* ext_factory, + const SocketAddress& ext_addr); + + SocketFactory* internal_factory() { return internal_factory_.get(); } + SocketAddress internal_address() const { + return nat_server_->internal_address(); + } + SocketAddress internal_tcp_address() const { + return SocketAddress(); // nat_server_->internal_tcp_address(); + } + + Translator* GetTranslator(const SocketAddress& ext_ip); + Translator* AddTranslator(const SocketAddress& ext_ip, + const SocketAddress& int_ip, NATType type); + void RemoveTranslator(const SocketAddress& ext_ip); + + bool AddClient(const SocketAddress& int_ip); + void RemoveClient(const SocketAddress& int_ip); + + // Looks for the specified client in this or a child NAT. + Translator* FindClient(const SocketAddress& int_ip); + + private: + NATSocketServer* server_; + scoped_ptr internal_factory_; + scoped_ptr nat_server_; + TranslatorMap nats_; + std::set clients_; + }; + + explicit NATSocketServer(SocketServer* ss); + + SocketServer* socketserver() { return server_; } + MessageQueue* queue() { return msg_queue_; } + + Translator* GetTranslator(const SocketAddress& ext_ip); + Translator* AddTranslator(const SocketAddress& ext_ip, + const SocketAddress& int_ip, NATType type); + void RemoveTranslator(const SocketAddress& ext_ip); + + // SocketServer implementation + virtual Socket* CreateSocket(int type); + virtual Socket* CreateSocket(int family, int type); + + virtual AsyncSocket* CreateAsyncSocket(int type); + virtual AsyncSocket* CreateAsyncSocket(int family, int type); + + virtual void SetMessageQueue(MessageQueue* queue) { + msg_queue_ = queue; + server_->SetMessageQueue(queue); + } + virtual bool Wait(int cms, bool process_io) { + return server_->Wait(cms, process_io); + } + virtual void WakeUp() { + server_->WakeUp(); + } + + // NATInternalSocketFactory implementation + virtual AsyncSocket* CreateInternalSocket(int family, int type, + const SocketAddress& local_addr, SocketAddress* nat_addr); + + private: + SocketServer* server_; + MessageQueue* msg_queue_; + TranslatorMap nats_; + DISALLOW_EVIL_CONSTRUCTORS(NATSocketServer); +}; + +// Free-standing NAT helper functions. +size_t PackAddressForNAT(char* buf, size_t buf_size, + const SocketAddress& remote_addr); +size_t UnpackAddressFromNAT(const char* buf, size_t buf_size, + SocketAddress* remote_addr); +} // namespace rtc + +#endif // WEBRTC_BASE_NATSOCKETFACTORY_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nattypes.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nattypes.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nattypes.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nattypes.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,55 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/nattypes.h" + +namespace rtc { + +class SymmetricNAT : public NAT { +public: + bool IsSymmetric() { return true; } + bool FiltersIP() { return true; } + bool FiltersPort() { return true; } +}; + +class OpenConeNAT : public NAT { +public: + bool IsSymmetric() { return false; } + bool FiltersIP() { return false; } + bool FiltersPort() { return false; } +}; + +class AddressRestrictedNAT : public NAT { +public: + bool IsSymmetric() { return false; } + bool FiltersIP() { return true; } + bool FiltersPort() { return false; } +}; + +class PortRestrictedNAT : public NAT { +public: + bool IsSymmetric() { return false; } + bool FiltersIP() { return true; } + bool FiltersPort() { return true; } +}; + +NAT* NAT::Create(NATType type) { + switch (type) { + case NAT_OPEN_CONE: return new OpenConeNAT(); + case NAT_ADDR_RESTRICTED: return new AddressRestrictedNAT(); + case NAT_PORT_RESTRICTED: return new PortRestrictedNAT(); + case NAT_SYMMETRIC: return new SymmetricNAT(); + default: assert(0); return 0; + } +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nattypes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nattypes.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nattypes.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nattypes.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,47 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_NATTYPE_H__ +#define WEBRTC_BASE_NATTYPE_H__ + +namespace rtc { + +/* Identifies each type of NAT that can be simulated. */ +enum NATType { + NAT_OPEN_CONE, + NAT_ADDR_RESTRICTED, + NAT_PORT_RESTRICTED, + NAT_SYMMETRIC +}; + +// Implements the rules for each specific type of NAT. +class NAT { +public: + virtual ~NAT() { } + + // Determines whether this NAT uses both source and destination address when + // checking whether a mapping already exists. + virtual bool IsSymmetric() = 0; + + // Determines whether this NAT drops packets received from a different IP + // the one last sent to. + virtual bool FiltersIP() = 0; + + // Determines whether this NAT drops packets received from a different port + // the one last sent to. + virtual bool FiltersPort() = 0; + + // Returns an implementation of the given type of NAT. + static NAT* Create(NATType type); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_NATTYPE_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nat_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nat_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nat_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nat_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,346 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/gunit.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/natserver.h" +#include "webrtc/base/natsocketfactory.h" +#include "webrtc/base/nethelpers.h" +#include "webrtc/base/network.h" +#include "webrtc/base/physicalsocketserver.h" +#include "webrtc/base/testclient.h" +#include "webrtc/base/virtualsocketserver.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +using namespace rtc; + +bool CheckReceive( + TestClient* client, bool should_receive, const char* buf, size_t size) { + return (should_receive) ? + client->CheckNextPacket(buf, size, 0) : + client->CheckNoPacket(); +} + +TestClient* CreateTestClient( + SocketFactory* factory, const SocketAddress& local_addr) { + AsyncUDPSocket* socket = AsyncUDPSocket::Create(factory, local_addr); + return new TestClient(socket); +} + +// Tests that when sending from internal_addr to external_addrs through the +// NAT type specified by nat_type, all external addrs receive the sent packet +// and, if exp_same is true, all use the same mapped-address on the NAT. +void TestSend( + SocketServer* internal, const SocketAddress& internal_addr, + SocketServer* external, const SocketAddress external_addrs[4], + NATType nat_type, bool exp_same) { + Thread th_int(internal); + Thread th_ext(external); + + SocketAddress server_addr = internal_addr; + server_addr.SetPort(0); // Auto-select a port + NATServer* nat = new NATServer( + nat_type, internal, server_addr, external, external_addrs[0]); + NATSocketFactory* natsf = new NATSocketFactory(internal, + nat->internal_address()); + + TestClient* in = CreateTestClient(natsf, internal_addr); + TestClient* out[4]; + for (int i = 0; i < 4; i++) + out[i] = CreateTestClient(external, external_addrs[i]); + + th_int.Start(); + th_ext.Start(); + + const char* buf = "filter_test"; + size_t len = strlen(buf); + + in->SendTo(buf, len, out[0]->address()); + SocketAddress trans_addr; + EXPECT_TRUE(out[0]->CheckNextPacket(buf, len, &trans_addr)); + + for (int i = 1; i < 4; i++) { + in->SendTo(buf, len, out[i]->address()); + SocketAddress trans_addr2; + EXPECT_TRUE(out[i]->CheckNextPacket(buf, len, &trans_addr2)); + bool are_same = (trans_addr == trans_addr2); + ASSERT_EQ(are_same, exp_same) << "same translated address"; + ASSERT_NE(AF_UNSPEC, trans_addr.family()); + ASSERT_NE(AF_UNSPEC, trans_addr2.family()); + } + + th_int.Stop(); + th_ext.Stop(); + + delete nat; + delete natsf; + delete in; + for (int i = 0; i < 4; i++) + delete out[i]; +} + +// Tests that when sending from external_addrs to internal_addr, the packet +// is delivered according to the specified filter_ip and filter_port rules. +void TestRecv( + SocketServer* internal, const SocketAddress& internal_addr, + SocketServer* external, const SocketAddress external_addrs[4], + NATType nat_type, bool filter_ip, bool filter_port) { + Thread th_int(internal); + Thread th_ext(external); + + SocketAddress server_addr = internal_addr; + server_addr.SetPort(0); // Auto-select a port + NATServer* nat = new NATServer( + nat_type, internal, server_addr, external, external_addrs[0]); + NATSocketFactory* natsf = new NATSocketFactory(internal, + nat->internal_address()); + + TestClient* in = CreateTestClient(natsf, internal_addr); + TestClient* out[4]; + for (int i = 0; i < 4; i++) + out[i] = CreateTestClient(external, external_addrs[i]); + + th_int.Start(); + th_ext.Start(); + + const char* buf = "filter_test"; + size_t len = strlen(buf); + + in->SendTo(buf, len, out[0]->address()); + SocketAddress trans_addr; + EXPECT_TRUE(out[0]->CheckNextPacket(buf, len, &trans_addr)); + + out[1]->SendTo(buf, len, trans_addr); + EXPECT_TRUE(CheckReceive(in, !filter_ip, buf, len)); + + out[2]->SendTo(buf, len, trans_addr); + EXPECT_TRUE(CheckReceive(in, !filter_port, buf, len)); + + out[3]->SendTo(buf, len, trans_addr); + EXPECT_TRUE(CheckReceive(in, !filter_ip && !filter_port, buf, len)); + + th_int.Stop(); + th_ext.Stop(); + + delete nat; + delete natsf; + delete in; + for (int i = 0; i < 4; i++) + delete out[i]; +} + +// Tests that NATServer allocates bindings properly. +void TestBindings( + SocketServer* internal, const SocketAddress& internal_addr, + SocketServer* external, const SocketAddress external_addrs[4]) { + TestSend(internal, internal_addr, external, external_addrs, + NAT_OPEN_CONE, true); + TestSend(internal, internal_addr, external, external_addrs, + NAT_ADDR_RESTRICTED, true); + TestSend(internal, internal_addr, external, external_addrs, + NAT_PORT_RESTRICTED, true); + TestSend(internal, internal_addr, external, external_addrs, + NAT_SYMMETRIC, false); +} + +// Tests that NATServer filters packets properly. +void TestFilters( + SocketServer* internal, const SocketAddress& internal_addr, + SocketServer* external, const SocketAddress external_addrs[4]) { + TestRecv(internal, internal_addr, external, external_addrs, + NAT_OPEN_CONE, false, false); + TestRecv(internal, internal_addr, external, external_addrs, + NAT_ADDR_RESTRICTED, true, false); + TestRecv(internal, internal_addr, external, external_addrs, + NAT_PORT_RESTRICTED, true, true); + TestRecv(internal, internal_addr, external, external_addrs, + NAT_SYMMETRIC, true, true); +} + +bool TestConnectivity(const SocketAddress& src, const IPAddress& dst) { + // The physical NAT tests require connectivity to the selected ip from the + // internal address used for the NAT. Things like firewalls can break that, so + // check to see if it's worth even trying with this ip. + scoped_ptr pss(new PhysicalSocketServer()); + scoped_ptr client(pss->CreateAsyncSocket(src.family(), + SOCK_DGRAM)); + scoped_ptr server(pss->CreateAsyncSocket(src.family(), + SOCK_DGRAM)); + if (client->Bind(SocketAddress(src.ipaddr(), 0)) != 0 || + server->Bind(SocketAddress(dst, 0)) != 0) { + return false; + } + const char* buf = "hello other socket"; + size_t len = strlen(buf); + int sent = client->SendTo(buf, len, server->GetLocalAddress()); + SocketAddress addr; + const size_t kRecvBufSize = 64; + char recvbuf[kRecvBufSize]; + Thread::Current()->SleepMs(100); + int received = server->RecvFrom(recvbuf, kRecvBufSize, &addr); + return received == sent && ::memcmp(buf, recvbuf, len) == 0; +} + +void TestPhysicalInternal(const SocketAddress& int_addr) { + BasicNetworkManager network_manager; + network_manager.set_ipv6_enabled(true); + network_manager.StartUpdating(); + // Process pending messages so the network list is updated. + Thread::Current()->ProcessMessages(0); + + std::vector networks; + network_manager.GetNetworks(&networks); + if (networks.empty()) { + LOG(LS_WARNING) << "Not enough network adapters for test."; + return; + } + + SocketAddress ext_addr1(int_addr); + SocketAddress ext_addr2; + // Find an available IP with matching family. The test breaks if int_addr + // can't talk to ip, so check for connectivity as well. + for (std::vector::iterator it = networks.begin(); + it != networks.end(); ++it) { + const IPAddress& ip = (*it)->GetBestIP(); + if (ip.family() == int_addr.family() && TestConnectivity(int_addr, ip)) { + ext_addr2.SetIP(ip); + break; + } + } + if (ext_addr2.IsNil()) { + LOG(LS_WARNING) << "No available IP of same family as " << int_addr; + return; + } + + LOG(LS_INFO) << "selected ip " << ext_addr2.ipaddr(); + + SocketAddress ext_addrs[4] = { + SocketAddress(ext_addr1), + SocketAddress(ext_addr2), + SocketAddress(ext_addr1), + SocketAddress(ext_addr2) + }; + + scoped_ptr int_pss(new PhysicalSocketServer()); + scoped_ptr ext_pss(new PhysicalSocketServer()); + + TestBindings(int_pss.get(), int_addr, ext_pss.get(), ext_addrs); + TestFilters(int_pss.get(), int_addr, ext_pss.get(), ext_addrs); +} + +TEST(NatTest, TestPhysicalIPv4) { + TestPhysicalInternal(SocketAddress("127.0.0.1", 0)); +} + +TEST(NatTest, TestPhysicalIPv6) { + if (HasIPv6Enabled()) { + TestPhysicalInternal(SocketAddress("::1", 0)); + } else { + LOG(LS_WARNING) << "No IPv6, skipping"; + } +} + +class TestVirtualSocketServer : public VirtualSocketServer { + public: + explicit TestVirtualSocketServer(SocketServer* ss) + : VirtualSocketServer(ss), + ss_(ss) {} + // Expose this publicly + IPAddress GetNextIP(int af) { return VirtualSocketServer::GetNextIP(af); } + + private: + scoped_ptr ss_; +}; + +void TestVirtualInternal(int family) { + scoped_ptr int_vss(new TestVirtualSocketServer( + new PhysicalSocketServer())); + scoped_ptr ext_vss(new TestVirtualSocketServer( + new PhysicalSocketServer())); + + SocketAddress int_addr; + SocketAddress ext_addrs[4]; + int_addr.SetIP(int_vss->GetNextIP(family)); + ext_addrs[0].SetIP(ext_vss->GetNextIP(int_addr.family())); + ext_addrs[1].SetIP(ext_vss->GetNextIP(int_addr.family())); + ext_addrs[2].SetIP(ext_addrs[0].ipaddr()); + ext_addrs[3].SetIP(ext_addrs[1].ipaddr()); + + TestBindings(int_vss.get(), int_addr, ext_vss.get(), ext_addrs); + TestFilters(int_vss.get(), int_addr, ext_vss.get(), ext_addrs); +} + +TEST(NatTest, TestVirtualIPv4) { + TestVirtualInternal(AF_INET); +} + +TEST(NatTest, TestVirtualIPv6) { + if (HasIPv6Enabled()) { + TestVirtualInternal(AF_INET6); + } else { + LOG(LS_WARNING) << "No IPv6, skipping"; + } +} + +// TODO: Finish this test +class NatTcpTest : public testing::Test, public sigslot::has_slots<> { + public: + NatTcpTest() : connected_(false) {} + virtual void SetUp() { + int_vss_ = new TestVirtualSocketServer(new PhysicalSocketServer()); + ext_vss_ = new TestVirtualSocketServer(new PhysicalSocketServer()); + nat_ = new NATServer(NAT_OPEN_CONE, int_vss_, SocketAddress(), + ext_vss_, SocketAddress()); + natsf_ = new NATSocketFactory(int_vss_, nat_->internal_address()); + } + void OnConnectEvent(AsyncSocket* socket) { + connected_ = true; + } + void OnAcceptEvent(AsyncSocket* socket) { + accepted_ = server_->Accept(NULL); + } + void OnCloseEvent(AsyncSocket* socket, int error) { + } + void ConnectEvents() { + server_->SignalReadEvent.connect(this, &NatTcpTest::OnAcceptEvent); + client_->SignalConnectEvent.connect(this, &NatTcpTest::OnConnectEvent); + } + TestVirtualSocketServer* int_vss_; + TestVirtualSocketServer* ext_vss_; + NATServer* nat_; + NATSocketFactory* natsf_; + AsyncSocket* client_; + AsyncSocket* server_; + AsyncSocket* accepted_; + bool connected_; +}; + +TEST_F(NatTcpTest, DISABLED_TestConnectOut) { + server_ = ext_vss_->CreateAsyncSocket(SOCK_STREAM); + server_->Bind(SocketAddress()); + server_->Listen(5); + + client_ = int_vss_->CreateAsyncSocket(SOCK_STREAM); + EXPECT_GE(0, client_->Bind(SocketAddress())); + EXPECT_GE(0, client_->Connect(server_->GetLocalAddress())); + + + ConnectEvents(); + + EXPECT_TRUE_WAIT(connected_, 1000); + EXPECT_EQ(client_->GetRemoteAddress(), server_->GetLocalAddress()); + EXPECT_EQ(client_->GetRemoteAddress(), accepted_->GetLocalAddress()); + EXPECT_EQ(client_->GetLocalAddress(), accepted_->GetRemoteAddress()); + + client_->Close(); +} +//#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nethelpers.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nethelpers.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nethelpers.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nethelpers.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,150 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/nethelpers.h" + +#if defined(WEBRTC_WIN) +#include +#include +#include "webrtc/base/win32.h" +#endif + +#include "webrtc/base/byteorder.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/signalthread.h" + +namespace rtc { + +int ResolveHostname(const std::string& hostname, int family, + std::vector* addresses) { +#ifdef __native_client__ + ASSERT(false); + LOG(LS_WARNING) << "ResolveHostname() is not implemented for NaCl"; + return -1; +#else // __native_client__ + if (!addresses) { + return -1; + } + addresses->clear(); + struct addrinfo* result = NULL; + struct addrinfo hints = {0}; + // TODO(djw): For now this is IPv4 only so existing users remain unaffected. + hints.ai_family = AF_INET; + hints.ai_flags = AI_ADDRCONFIG; + int ret = getaddrinfo(hostname.c_str(), NULL, &hints, &result); + if (ret != 0) { + return ret; + } + struct addrinfo* cursor = result; + for (; cursor; cursor = cursor->ai_next) { + if (family == AF_UNSPEC || cursor->ai_family == family) { + IPAddress ip; + if (IPFromAddrInfo(cursor, &ip)) { + addresses->push_back(ip); + } + } + } + freeaddrinfo(result); + return 0; +#endif // !__native_client__ +} + +// AsyncResolver +AsyncResolver::AsyncResolver() : error_(-1) { +} + +void AsyncResolver::Start(const SocketAddress& addr) { + addr_ = addr; + // SignalThred Start will kickoff the resolve process. + SignalThread::Start(); +} + +bool AsyncResolver::GetResolvedAddress(int family, SocketAddress* addr) const { + if (error_ != 0 || addresses_.empty()) + return false; + + *addr = addr_; + for (size_t i = 0; i < addresses_.size(); ++i) { + if (family == addresses_[i].family()) { + addr->SetResolvedIP(addresses_[i]); + return true; + } + } + return false; +} + +void AsyncResolver::DoWork() { + error_ = ResolveHostname(addr_.hostname().c_str(), addr_.family(), + &addresses_); +} + +void AsyncResolver::OnWorkDone() { + SignalDone(this); +} + +const char* inet_ntop(int af, const void *src, char* dst, socklen_t size) { +#if defined(WEBRTC_WIN) + return win32_inet_ntop(af, src, dst, size); +#else + return ::inet_ntop(af, src, dst, size); +#endif +} + +int inet_pton(int af, const char* src, void *dst) { +#if defined(WEBRTC_WIN) + return win32_inet_pton(af, src, dst); +#else + return ::inet_pton(af, src, dst); +#endif +} + +bool HasIPv6Enabled() { +#if !defined(WEBRTC_WIN) + // We only need to check this for Windows XP (so far). + return true; +#else + if (IsWindowsVistaOrLater()) { + return true; + } + if (!IsWindowsXpOrLater()) { + return false; + } + DWORD protbuff_size = 4096; + scoped_ptr protocols; + LPWSAPROTOCOL_INFOW protocol_infos = NULL; + int requested_protocols[2] = {AF_INET6, 0}; + + int err = 0; + int ret = 0; + // Check for protocols in a do-while loop until we provide a buffer large + // enough. (WSCEnumProtocols sets protbuff_size to its desired value). + // It is extremely unlikely that this will loop more than once. + do { + protocols.reset(new char[protbuff_size]); + protocol_infos = reinterpret_cast(protocols.get()); + ret = WSCEnumProtocols(requested_protocols, protocol_infos, + &protbuff_size, &err); + } while (ret == SOCKET_ERROR && err == WSAENOBUFS); + + if (ret == SOCKET_ERROR) { + return false; + } + + // Even if ret is positive, check specifically for IPv6. + // Non-IPv6 enabled WinXP will still return a RAW protocol. + for (int i = 0; i < ret; ++i) { + if (protocol_infos[i].iAddressFamily == AF_INET6) { + return true; + } + } + return false; +#endif +} +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nethelpers.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nethelpers.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nethelpers.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nethelpers.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,65 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_NETHELPERS_H_ +#define WEBRTC_BASE_NETHELPERS_H_ + +#if defined(WEBRTC_POSIX) +#include +#include +#elif WEBRTC_WIN +#include // NOLINT +#endif + +#include + +#include "webrtc/base/asyncresolverinterface.h" +#include "webrtc/base/signalthread.h" +#include "webrtc/base/sigslot.h" +#include "webrtc/base/socketaddress.h" + +namespace rtc { + +class AsyncResolverTest; + +// AsyncResolver will perform async DNS resolution, signaling the result on +// the SignalDone from AsyncResolverInterface when the operation completes. +class AsyncResolver : public SignalThread, public AsyncResolverInterface { + public: + AsyncResolver(); + virtual ~AsyncResolver() {} + + virtual void Start(const SocketAddress& addr); + virtual bool GetResolvedAddress(int family, SocketAddress* addr) const; + virtual int GetError() const { return error_; } + virtual void Destroy(bool wait) { SignalThread::Destroy(wait); } + + const std::vector& addresses() const { return addresses_; } + void set_error(int error) { error_ = error; } + + protected: + virtual void DoWork(); + virtual void OnWorkDone(); + + private: + SocketAddress addr_; + std::vector addresses_; + int error_; +}; + +// rtc namespaced wrappers for inet_ntop and inet_pton so we can avoid +// the windows-native versions of these. +const char* inet_ntop(int af, const void *src, char* dst, socklen_t size); +int inet_pton(int af, const char* src, void *dst); + +bool HasIPv6Enabled(); +} // namespace rtc + +#endif // WEBRTC_BASE_NETHELPERS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/network.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/network.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/network.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/network.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,713 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "webrtc/base/network.h" + +#if defined(WEBRTC_POSIX) +// linux/if.h can't be included at the same time as the posix sys/if.h, and +// it's transitively required by linux/route.h, so include that version on +// linux instead of the standard posix one. +#if defined(WEBRTC_LINUX) +#include +#include +#elif !defined(__native_client__) +#include +#endif +#include +#include +#include +#include +#include + +#if defined(WEBRTC_ANDROID) +#include "webrtc/base/ifaddrs-android.h" +#elif !defined(__native_client__) +#include +#endif + +#endif // WEBRTC_POSIX + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32.h" +#include +#endif + +#include + +#include + +#include "webrtc/base/logging.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/socket.h" // includes something that makes windows happy +#include "webrtc/base/stream.h" +#include "webrtc/base/stringencode.h" +#include "webrtc/base/thread.h" + +namespace rtc { +namespace { + +const uint32 kUpdateNetworksMessage = 1; +const uint32 kSignalNetworksMessage = 2; + +// Fetch list of networks every two seconds. +const int kNetworksUpdateIntervalMs = 2000; + +const int kHighestNetworkPreference = 127; + +typedef struct { + Network* net; + std::vector ips; +} AddressList; + +bool CompareNetworks(const Network* a, const Network* b) { + if (a->prefix_length() == b->prefix_length()) { + if (a->name() == b->name()) { + return a->prefix() < b->prefix(); + } + } + return a->name() < b->name(); +} + +bool SortNetworks(const Network* a, const Network* b) { + // Network types will be preferred above everything else while sorting + // Networks. + + // Networks are sorted first by type. + if (a->type() != b->type()) { + return a->type() < b->type(); + } + + IPAddress ip_a = a->GetBestIP(); + IPAddress ip_b = b->GetBestIP(); + + // After type, networks are sorted by IP address precedence values + // from RFC 3484-bis + if (IPAddressPrecedence(ip_a) != IPAddressPrecedence(ip_b)) { + return IPAddressPrecedence(ip_a) > IPAddressPrecedence(ip_b); + } + + // TODO(mallinath) - Add VPN and Link speed conditions while sorting. + + // Networks are sorted last by key. + return a->key() > b->key(); +} + +std::string AdapterTypeToString(AdapterType type) { + switch (type) { + case ADAPTER_TYPE_UNKNOWN: + return "Unknown"; + case ADAPTER_TYPE_ETHERNET: + return "Ethernet"; + case ADAPTER_TYPE_WIFI: + return "Wifi"; + case ADAPTER_TYPE_CELLULAR: + return "Cellular"; + case ADAPTER_TYPE_VPN: + return "VPN"; + default: + ASSERT(false); + return std::string(); + } +} + +} // namespace + +std::string MakeNetworkKey(const std::string& name, const IPAddress& prefix, + int prefix_length) { + std::ostringstream ost; + ost << name << "%" << prefix.ToString() << "/" << prefix_length; + return ost.str(); +} + +NetworkManager::NetworkManager() { +} + +NetworkManager::~NetworkManager() { +} + +NetworkManagerBase::NetworkManagerBase() : ipv6_enabled_(true) { +} + +NetworkManagerBase::~NetworkManagerBase() { + for (NetworkMap::iterator i = networks_map_.begin(); + i != networks_map_.end(); ++i) { + delete i->second; + } +} + +void NetworkManagerBase::GetNetworks(NetworkList* result) const { + *result = networks_; +} + +void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks, + bool* changed) { + // AddressList in this map will track IP addresses for all Networks + // with the same key. + std::map consolidated_address_list; + NetworkList list(new_networks); + + // Result of Network merge. Element in this list should have unique key. + NetworkList merged_list; + std::sort(list.begin(), list.end(), CompareNetworks); + + *changed = false; + + if (networks_.size() != list.size()) + *changed = true; + + // First, build a set of network-keys to the ipaddresses. + for (uint32 i = 0; i < list.size(); ++i) { + bool might_add_to_merged_list = false; + std::string key = MakeNetworkKey(list[i]->name(), + list[i]->prefix(), + list[i]->prefix_length()); + if (consolidated_address_list.find(key) == + consolidated_address_list.end()) { + AddressList addrlist; + addrlist.net = list[i]; + consolidated_address_list[key] = addrlist; + might_add_to_merged_list = true; + } + const std::vector& addresses = list[i]->GetIPs(); + AddressList& current_list = consolidated_address_list[key]; + for (std::vector::const_iterator it = addresses.begin(); + it != addresses.end(); + ++it) { + current_list.ips.push_back(*it); + } + if (!might_add_to_merged_list) { + delete list[i]; + } + } + + // Next, look for existing network objects to re-use. + for (std::map::iterator it = + consolidated_address_list.begin(); + it != consolidated_address_list.end(); + ++it) { + const std::string& key = it->first; + Network* net = it->second.net; + NetworkMap::iterator existing = networks_map_.find(key); + if (existing == networks_map_.end()) { + // This network is new. Place it in the network map. + merged_list.push_back(net); + networks_map_[key] = net; + // Also, we might have accumulated IPAddresses from the first + // step, set it here. + net->SetIPs(it->second.ips, true); + *changed = true; + } else { + // This network exists in the map already. Reset its IP addresses. + *changed = existing->second->SetIPs(it->second.ips, *changed); + merged_list.push_back(existing->second); + if (existing->second != net) { + delete net; + } + } + } + networks_ = merged_list; + + // If the network lists changes, we resort it. + if (changed) { + std::sort(networks_.begin(), networks_.end(), SortNetworks); + // Now network interfaces are sorted, we should set the preference value + // for each of the interfaces we are planning to use. + // Preference order of network interfaces might have changed from previous + // sorting due to addition of higher preference network interface. + // Since we have already sorted the network interfaces based on our + // requirements, we will just assign a preference value starting with 127, + // in decreasing order. + int pref = kHighestNetworkPreference; + for (NetworkList::const_iterator iter = networks_.begin(); + iter != networks_.end(); ++iter) { + (*iter)->set_preference(pref); + if (pref > 0) { + --pref; + } else { + LOG(LS_ERROR) << "Too many network interfaces to handle!"; + break; + } + } + } +} + +BasicNetworkManager::BasicNetworkManager() + : thread_(NULL), sent_first_update_(false), start_count_(0), + ignore_non_default_routes_(false) { +} + +BasicNetworkManager::~BasicNetworkManager() { +} + +#if defined(__native_client__) + +bool BasicNetworkManager::CreateNetworks(bool include_ignored, + NetworkList* networks) const { + ASSERT(false); + LOG(LS_WARNING) << "BasicNetworkManager doesn't work on NaCl yet"; + return false; +} + +#elif defined(WEBRTC_POSIX) +void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, + bool include_ignored, + NetworkList* networks) const { + NetworkMap current_networks; + for (struct ifaddrs* cursor = interfaces; + cursor != NULL; cursor = cursor->ifa_next) { + IPAddress prefix; + IPAddress mask; + IPAddress ip; + int scope_id = 0; + + // Some interfaces may not have address assigned. + if (!cursor->ifa_addr || !cursor->ifa_netmask) + continue; + + switch (cursor->ifa_addr->sa_family) { + case AF_INET: { + ip = IPAddress( + reinterpret_cast(cursor->ifa_addr)->sin_addr); + mask = IPAddress( + reinterpret_cast(cursor->ifa_netmask)->sin_addr); + break; + } + case AF_INET6: { + if (ipv6_enabled()) { + ip = IPAddress( + reinterpret_cast(cursor->ifa_addr)->sin6_addr); + mask = IPAddress( + reinterpret_cast(cursor->ifa_netmask)->sin6_addr); + scope_id = + reinterpret_cast(cursor->ifa_addr)->sin6_scope_id; + break; + } else { + continue; + } + } + default: { + continue; + } + } + + int prefix_length = CountIPMaskBits(mask); + prefix = TruncateIP(ip, prefix_length); + std::string key = MakeNetworkKey(std::string(cursor->ifa_name), + prefix, prefix_length); + NetworkMap::iterator existing_network = current_networks.find(key); + if (existing_network == current_networks.end()) { + scoped_ptr network(new Network(cursor->ifa_name, + cursor->ifa_name, + prefix, + prefix_length)); + network->set_scope_id(scope_id); + network->AddIP(ip); + bool ignored = ((cursor->ifa_flags & IFF_LOOPBACK) || + IsIgnoredNetwork(*network)); + network->set_ignored(ignored); + if (include_ignored || !network->ignored()) { + networks->push_back(network.release()); + } + } else { + (*existing_network).second->AddIP(ip); + } + } +} + +bool BasicNetworkManager::CreateNetworks(bool include_ignored, + NetworkList* networks) const { + struct ifaddrs* interfaces; + int error = getifaddrs(&interfaces); + if (error != 0) { + LOG_ERR(LERROR) << "getifaddrs failed to gather interface data: " << error; + return false; + } + + ConvertIfAddrs(interfaces, include_ignored, networks); + + freeifaddrs(interfaces); + return true; +} + +#elif defined(WEBRTC_WIN) + +unsigned int GetPrefix(PIP_ADAPTER_PREFIX prefixlist, + const IPAddress& ip, IPAddress* prefix) { + IPAddress current_prefix; + IPAddress best_prefix; + unsigned int best_length = 0; + while (prefixlist) { + // Look for the longest matching prefix in the prefixlist. + if (prefixlist->Address.lpSockaddr == NULL || + prefixlist->Address.lpSockaddr->sa_family != ip.family()) { + prefixlist = prefixlist->Next; + continue; + } + switch (prefixlist->Address.lpSockaddr->sa_family) { + case AF_INET: { + sockaddr_in* v4_addr = + reinterpret_cast(prefixlist->Address.lpSockaddr); + current_prefix = IPAddress(v4_addr->sin_addr); + break; + } + case AF_INET6: { + sockaddr_in6* v6_addr = + reinterpret_cast(prefixlist->Address.lpSockaddr); + current_prefix = IPAddress(v6_addr->sin6_addr); + break; + } + default: { + prefixlist = prefixlist->Next; + continue; + } + } + if (TruncateIP(ip, prefixlist->PrefixLength) == current_prefix && + prefixlist->PrefixLength > best_length) { + best_prefix = current_prefix; + best_length = prefixlist->PrefixLength; + } + prefixlist = prefixlist->Next; + } + *prefix = best_prefix; + return best_length; +} + +bool BasicNetworkManager::CreateNetworks(bool include_ignored, + NetworkList* networks) const { + NetworkMap current_networks; + // MSDN recommends a 15KB buffer for the first try at GetAdaptersAddresses. + size_t buffer_size = 16384; + scoped_ptr adapter_info(new char[buffer_size]); + PIP_ADAPTER_ADDRESSES adapter_addrs = + reinterpret_cast(adapter_info.get()); + int adapter_flags = (GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST | + GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_INCLUDE_PREFIX); + int ret = 0; + do { + adapter_info.reset(new char[buffer_size]); + adapter_addrs = reinterpret_cast(adapter_info.get()); + ret = GetAdaptersAddresses(AF_UNSPEC, adapter_flags, + 0, adapter_addrs, + reinterpret_cast(&buffer_size)); + } while (ret == ERROR_BUFFER_OVERFLOW); + if (ret != ERROR_SUCCESS) { + return false; + } + int count = 0; + while (adapter_addrs) { + if (adapter_addrs->OperStatus == IfOperStatusUp) { + PIP_ADAPTER_UNICAST_ADDRESS address = adapter_addrs->FirstUnicastAddress; + PIP_ADAPTER_PREFIX prefixlist = adapter_addrs->FirstPrefix; + std::string name; + std::string description; +#ifdef _DEBUG + name = ToUtf8(adapter_addrs->FriendlyName, + wcslen(adapter_addrs->FriendlyName)); +#endif + description = ToUtf8(adapter_addrs->Description, + wcslen(adapter_addrs->Description)); + for (; address; address = address->Next) { +#ifndef _DEBUG + name = rtc::ToString(count); +#endif + + IPAddress ip; + int scope_id = 0; + scoped_ptr network; + switch (address->Address.lpSockaddr->sa_family) { + case AF_INET: { + sockaddr_in* v4_addr = + reinterpret_cast(address->Address.lpSockaddr); + ip = IPAddress(v4_addr->sin_addr); + break; + } + case AF_INET6: { + if (ipv6_enabled()) { + sockaddr_in6* v6_addr = + reinterpret_cast(address->Address.lpSockaddr); + scope_id = v6_addr->sin6_scope_id; + ip = IPAddress(v6_addr->sin6_addr); + break; + } else { + continue; + } + } + default: { + continue; + } + } + + IPAddress prefix; + int prefix_length = GetPrefix(prefixlist, ip, &prefix); + std::string key = MakeNetworkKey(name, prefix, prefix_length); + NetworkMap::iterator existing_network = current_networks.find(key); + if (existing_network == current_networks.end()) { + scoped_ptr network(new Network(name, + description, + prefix, + prefix_length)); + network->set_scope_id(scope_id); + network->AddIP(ip); + bool ignore = ((adapter_addrs->IfType == IF_TYPE_SOFTWARE_LOOPBACK) || + IsIgnoredNetwork(*network)); + network->set_ignored(ignore); + if (include_ignored || !network->ignored()) { + networks->push_back(network.release()); + } + } else { + (*existing_network).second->AddIP(ip); + } + } + // Count is per-adapter - all 'Networks' created from the same + // adapter need to have the same name. + ++count; + } + adapter_addrs = adapter_addrs->Next; + } + return true; +} +#endif // WEBRTC_WIN + +#if defined(WEBRTC_LINUX) +bool IsDefaultRoute(const std::string& network_name) { + FileStream fs; + if (!fs.Open("/proc/net/route", "r", NULL)) { + LOG(LS_WARNING) << "Couldn't read /proc/net/route, skipping default " + << "route check (assuming everything is a default route)."; + return true; + } else { + std::string line; + while (fs.ReadLine(&line) == SR_SUCCESS) { + char iface_name[256]; + unsigned int iface_ip, iface_gw, iface_mask, iface_flags; + if (sscanf(line.c_str(), + "%255s %8X %8X %4X %*d %*u %*d %8X", + iface_name, &iface_ip, &iface_gw, + &iface_flags, &iface_mask) == 5 && + network_name == iface_name && + iface_mask == 0 && + (iface_flags & (RTF_UP | RTF_HOST)) == RTF_UP) { + return true; + } + } + } + return false; +} +#endif + +bool BasicNetworkManager::IsIgnoredNetwork(const Network& network) const { + // Ignore networks on the explicit ignore list. + for (size_t i = 0; i < network_ignore_list_.size(); ++i) { + if (network.name() == network_ignore_list_[i]) { + return true; + } + } +#if defined(WEBRTC_POSIX) + // Filter out VMware interfaces, typically named vmnet1 and vmnet8 + if (strncmp(network.name().c_str(), "vmnet", 5) == 0 || + strncmp(network.name().c_str(), "vnic", 4) == 0) { + return true; + } +#if defined(WEBRTC_LINUX) + // Make sure this is a default route, if we're ignoring non-defaults. + if (ignore_non_default_routes_ && !IsDefaultRoute(network.name())) { + return true; + } +#endif +#elif defined(WEBRTC_WIN) + // Ignore any HOST side vmware adapters with a description like: + // VMware Virtual Ethernet Adapter for VMnet1 + // but don't ignore any GUEST side adapters with a description like: + // VMware Accelerated AMD PCNet Adapter #2 + if (strstr(network.description().c_str(), "VMnet") != NULL) { + return true; + } +#endif + + // Ignore any networks with a 0.x.y.z IP + if (network.prefix().family() == AF_INET) { + return (network.prefix().v4AddressAsHostOrderInteger() < 0x01000000); + } + return false; +} + +void BasicNetworkManager::StartUpdating() { + thread_ = Thread::Current(); + if (start_count_) { + // If network interfaces are already discovered and signal is sent, + // we should trigger network signal immediately for the new clients + // to start allocating ports. + if (sent_first_update_) + thread_->Post(this, kSignalNetworksMessage); + } else { + thread_->Post(this, kUpdateNetworksMessage); + } + ++start_count_; +} + +void BasicNetworkManager::StopUpdating() { + ASSERT(Thread::Current() == thread_); + if (!start_count_) + return; + + --start_count_; + if (!start_count_) { + thread_->Clear(this); + sent_first_update_ = false; + } +} + +void BasicNetworkManager::OnMessage(Message* msg) { + switch (msg->message_id) { + case kUpdateNetworksMessage: { + DoUpdateNetworks(); + break; + } + case kSignalNetworksMessage: { + SignalNetworksChanged(); + break; + } + default: + ASSERT(false); + } +} + +void BasicNetworkManager::DoUpdateNetworks() { + if (!start_count_) + return; + + ASSERT(Thread::Current() == thread_); + + NetworkList list; + if (!CreateNetworks(false, &list)) { + SignalError(); + } else { + bool changed; + MergeNetworkList(list, &changed); + if (changed || !sent_first_update_) { + SignalNetworksChanged(); + sent_first_update_ = true; + } + } + + thread_->PostDelayed(kNetworksUpdateIntervalMs, this, kUpdateNetworksMessage); +} + +void BasicNetworkManager::DumpNetworks(bool include_ignored) { + NetworkList list; + CreateNetworks(include_ignored, &list); + LOG(LS_INFO) << "NetworkManager detected " << list.size() << " networks:"; + for (size_t i = 0; i < list.size(); ++i) { + const Network* network = list[i]; + if (!network->ignored() || include_ignored) { + LOG(LS_INFO) << network->ToString() << ": " + << network->description() + << ((network->ignored()) ? ", Ignored" : ""); + } + } + // Release the network list created previously. + // Do this in a seperated for loop for better readability. + for (size_t i = 0; i < list.size(); ++i) { + delete list[i]; + } +} + +Network::Network(const std::string& name, const std::string& desc, + const IPAddress& prefix, int prefix_length) + : name_(name), description_(desc), prefix_(prefix), + prefix_length_(prefix_length), + key_(MakeNetworkKey(name, prefix, prefix_length)), scope_id_(0), + ignored_(false), type_(ADAPTER_TYPE_UNKNOWN), preference_(0) { +} + +Network::Network(const std::string& name, const std::string& desc, + const IPAddress& prefix, int prefix_length, AdapterType type) + : name_(name), description_(desc), prefix_(prefix), + prefix_length_(prefix_length), + key_(MakeNetworkKey(name, prefix, prefix_length)), scope_id_(0), + ignored_(false), type_(type), preference_(0) { +} + +// Sets the addresses of this network. Returns true if the address set changed. +// Change detection is short circuited if the changed argument is true. +bool Network::SetIPs(const std::vector& ips, bool changed) { + changed = changed || ips.size() != ips_.size(); + // Detect changes with a nested loop; n-squared but we expect on the order + // of 2-3 addresses per network. + for (std::vector::const_iterator it = ips.begin(); + !changed && it != ips.end(); + ++it) { + bool found = false; + for (std::vector::iterator inner_it = ips_.begin(); + !found && inner_it != ips_.end(); + ++inner_it) { + if (*it == *inner_it) { + found = true; + } + } + changed = !found; + } + ips_ = ips; + return changed; +} + +// Select the best IP address to use from this Network. +IPAddress Network::GetBestIP() const { + if (ips_.size() == 0) { + return IPAddress(); + } + + if (prefix_.family() == AF_INET) { + return static_cast(ips_.at(0)); + } + + InterfaceAddress selected_ip, ula_ip; + + for (size_t i = 0; i < ips_.size(); i++) { + // Ignore any address which has been deprecated already. + if (ips_[i].ipv6_flags() & IPV6_ADDRESS_FLAG_DEPRECATED) + continue; + + // ULA address should only be returned when we have no other + // global IP. + if (IPIsULA(static_cast(ips_[i]))) { + ula_ip = ips_[i]; + continue; + } + selected_ip = ips_[i]; + + // Search could stop once a temporary non-deprecated one is found. + if (ips_[i].ipv6_flags() & IPV6_ADDRESS_FLAG_TEMPORARY) + break; + } + + // No proper global IPv6 address found, use ULA instead. + if (IPIsUnspec(selected_ip) && !IPIsUnspec(ula_ip)) { + selected_ip = ula_ip; + } + + return static_cast(selected_ip); +} + +std::string Network::ToString() const { + std::stringstream ss; + // Print out the first space-terminated token of the network desc, plus + // the IP address. + ss << "Net[" << description_.substr(0, description_.find(' ')) + << ":" << prefix_.ToSensitiveString() << "/" << prefix_length_ + << ":" << AdapterTypeToString(type_) << "]"; + return ss.str(); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/network.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/network.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/network.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/network.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,259 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_NETWORK_H_ +#define WEBRTC_BASE_NETWORK_H_ + +#include +#include +#include +#include + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/ipaddress.h" +#include "webrtc/base/messagehandler.h" +#include "webrtc/base/sigslot.h" + +#if defined(WEBRTC_POSIX) +struct ifaddrs; +#endif // defined(WEBRTC_POSIX) + +namespace rtc { + +class Network; +class Thread; + +enum AdapterType { + // This enum resembles the one in Chromium net::ConnectionType. + ADAPTER_TYPE_UNKNOWN = 0, + ADAPTER_TYPE_ETHERNET = 1, + ADAPTER_TYPE_WIFI = 2, + ADAPTER_TYPE_CELLULAR = 3, + ADAPTER_TYPE_VPN = 4 +}; + +// Makes a string key for this network. Used in the network manager's maps. +// Network objects are keyed on interface name, network prefix and the +// length of that prefix. +std::string MakeNetworkKey(const std::string& name, const IPAddress& prefix, + int prefix_length); + +// Generic network manager interface. It provides list of local +// networks. +class NetworkManager { + public: + typedef std::vector NetworkList; + + NetworkManager(); + virtual ~NetworkManager(); + + // Called when network list is updated. + sigslot::signal0<> SignalNetworksChanged; + + // Indicates a failure when getting list of network interfaces. + sigslot::signal0<> SignalError; + + // Start/Stop monitoring of network interfaces + // list. SignalNetworksChanged or SignalError is emitted immidiately + // after StartUpdating() is called. After that SignalNetworksChanged + // is emitted wheneven list of networks changes. + virtual void StartUpdating() = 0; + virtual void StopUpdating() = 0; + + // Returns the current list of networks available on this machine. + // UpdateNetworks() must be called before this method is called. + // It makes sure that repeated calls return the same object for a + // given network, so that quality is tracked appropriately. Does not + // include ignored networks. + virtual void GetNetworks(NetworkList* networks) const = 0; + + // Dumps a list of networks available to LS_INFO. + virtual void DumpNetworks(bool include_ignored) {} +}; + +// Base class for NetworkManager implementations. +class NetworkManagerBase : public NetworkManager { + public: + NetworkManagerBase(); + virtual ~NetworkManagerBase(); + + virtual void GetNetworks(std::vector* networks) const; + bool ipv6_enabled() const { return ipv6_enabled_; } + void set_ipv6_enabled(bool enabled) { ipv6_enabled_ = enabled; } + + protected: + typedef std::map NetworkMap; + // Updates |networks_| with the networks listed in |list|. If + // |network_map_| already has a Network object for a network listed + // in the |list| then it is reused. Accept ownership of the Network + // objects in the |list|. |changed| will be set to true if there is + // any change in the network list. + void MergeNetworkList(const NetworkList& list, bool* changed); + + private: + friend class NetworkTest; + void DoUpdateNetworks(); + + NetworkList networks_; + NetworkMap networks_map_; + bool ipv6_enabled_; +}; + +// Basic implementation of the NetworkManager interface that gets list +// of networks using OS APIs. +class BasicNetworkManager : public NetworkManagerBase, + public MessageHandler { + public: + BasicNetworkManager(); + virtual ~BasicNetworkManager(); + + virtual void StartUpdating(); + virtual void StopUpdating(); + + // Logs the available networks. + virtual void DumpNetworks(bool include_ignored); + + // MessageHandler interface. + virtual void OnMessage(Message* msg); + bool started() { return start_count_ > 0; } + + // Sets the network ignore list, which is empty by default. Any network on + // the ignore list will be filtered from network enumeration results. + void set_network_ignore_list(const std::vector& list) { + network_ignore_list_ = list; + } +#if defined(WEBRTC_LINUX) + // Sets the flag for ignoring non-default routes. + void set_ignore_non_default_routes(bool value) { + ignore_non_default_routes_ = true; + } +#endif + + protected: +#if defined(WEBRTC_POSIX) + // Separated from CreateNetworks for tests. + void ConvertIfAddrs(ifaddrs* interfaces, + bool include_ignored, + NetworkList* networks) const; +#endif // defined(WEBRTC_POSIX) + + // Creates a network object for each network available on the machine. + bool CreateNetworks(bool include_ignored, NetworkList* networks) const; + + // Determines if a network should be ignored. + bool IsIgnoredNetwork(const Network& network) const; + + private: + friend class NetworkTest; + + void DoUpdateNetworks(); + + Thread* thread_; + bool sent_first_update_; + int start_count_; + std::vector network_ignore_list_; + bool ignore_non_default_routes_; +}; + +// Represents a Unix-type network interface, with a name and single address. +class Network { + public: + Network(const std::string& name, const std::string& description, + const IPAddress& prefix, int prefix_length); + + Network(const std::string& name, const std::string& description, + const IPAddress& prefix, int prefix_length, AdapterType type); + + // Returns the name of the interface this network is associated wtih. + const std::string& name() const { return name_; } + + // Returns the OS-assigned name for this network. This is useful for + // debugging but should not be sent over the wire (for privacy reasons). + const std::string& description() const { return description_; } + + // Returns the prefix for this network. + const IPAddress& prefix() const { return prefix_; } + // Returns the length, in bits, of this network's prefix. + int prefix_length() const { return prefix_length_; } + + // |key_| has unique value per network interface. Used in sorting network + // interfaces. Key is derived from interface name and it's prefix. + std::string key() const { return key_; } + + // Returns the Network's current idea of the 'best' IP it has. + // Or return an unset IP if this network has no active addresses. + // Here is the rule on how we mark the IPv6 address as ignorable for WebRTC. + // 1) return all global temporary dynamic and non-deprecrated ones. + // 2) if #1 not available, return global ones. + // 3) if #2 not available, use ULA ipv6 as last resort. (ULA stands + // for unique local address, which is not route-able in open + // internet but might be useful for a close WebRTC deployment. + + // TODO(guoweis): rule #3 actually won't happen at current + // implementation. The reason being that ULA address starting with + // 0xfc 0r 0xfd will be grouped into its own Network. The result of + // that is WebRTC will have one extra Network to generate candidates + // but the lack of rule #3 shouldn't prevent turning on IPv6 since + // ULA should only be tried in a close deployment anyway. + + // Note that when not specifying any flag, it's treated as case global + // IPv6 address + IPAddress GetBestIP() const; + + // Keep the original function here for now. + // TODO(guoweis): Remove this when all callers are migrated to GetBestIP(). + IPAddress ip() const { return GetBestIP(); } + + // Adds an active IP address to this network. Does not check for duplicates. + void AddIP(const InterfaceAddress& ip) { ips_.push_back(ip); } + + // Sets the network's IP address list. Returns true if new IP addresses were + // detected. Passing true to already_changed skips this check. + bool SetIPs(const std::vector& ips, bool already_changed); + // Get the list of IP Addresses associated with this network. + const std::vector& GetIPs() const { return ips_;} + // Clear the network's list of addresses. + void ClearIPs() { ips_.clear(); } + + // Returns the scope-id of the network's address. + // Should only be relevant for link-local IPv6 addresses. + int scope_id() const { return scope_id_; } + void set_scope_id(int id) { scope_id_ = id; } + + // Indicates whether this network should be ignored, perhaps because + // the IP is 0, or the interface is one we know is invalid. + bool ignored() const { return ignored_; } + void set_ignored(bool ignored) { ignored_ = ignored; } + + AdapterType type() const { return type_; } + int preference() const { return preference_; } + void set_preference(int preference) { preference_ = preference; } + + // Debugging description of this network + std::string ToString() const; + + private: + std::string name_; + std::string description_; + IPAddress prefix_; + int prefix_length_; + std::string key_; + std::vector ips_; + int scope_id_; + bool ignored_; + AdapterType type_; + int preference_; + + friend class NetworkManager; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_NETWORK_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/network_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/network_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/network_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/network_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,694 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/network.h" + +#include +#if defined(WEBRTC_POSIX) +#include +#if !defined(WEBRTC_ANDROID) +#include +#else +#include "webrtc/base/ifaddrs-android.h" +#endif +#endif +#include "webrtc/base/gunit.h" +#if defined(WEBRTC_WIN) +#include "webrtc/base/logging.h" // For LOG_GLE +#endif + +namespace rtc { + +class NetworkTest : public testing::Test, public sigslot::has_slots<> { + public: + NetworkTest() : callback_called_(false) {} + + void OnNetworksChanged() { + callback_called_ = true; + } + + void MergeNetworkList(BasicNetworkManager& network_manager, + const NetworkManager::NetworkList& list, + bool* changed ) { + network_manager.MergeNetworkList(list, changed); + } + + bool IsIgnoredNetwork(BasicNetworkManager& network_manager, + const Network& network) { + return network_manager.IsIgnoredNetwork(network); + } + + NetworkManager::NetworkList GetNetworks( + const BasicNetworkManager& network_manager, bool include_ignored) { + NetworkManager::NetworkList list; + network_manager.CreateNetworks(include_ignored, &list); + return list; + } + +#if defined(WEBRTC_POSIX) + // Separated from CreateNetworks for tests. + static void CallConvertIfAddrs(const BasicNetworkManager& network_manager, + struct ifaddrs* interfaces, + bool include_ignored, + NetworkManager::NetworkList* networks) { + network_manager.ConvertIfAddrs(interfaces, include_ignored, networks); + } +#endif // defined(WEBRTC_POSIX) + + protected: + bool callback_called_; +}; + +// Test that the Network ctor works properly. +TEST_F(NetworkTest, TestNetworkConstruct) { + Network ipv4_network1("test_eth0", "Test Network Adapter 1", + IPAddress(0x12345600U), 24); + EXPECT_EQ("test_eth0", ipv4_network1.name()); + EXPECT_EQ("Test Network Adapter 1", ipv4_network1.description()); + EXPECT_EQ(IPAddress(0x12345600U), ipv4_network1.prefix()); + EXPECT_EQ(24, ipv4_network1.prefix_length()); + EXPECT_FALSE(ipv4_network1.ignored()); +} + +// Tests that our ignore function works properly. +TEST_F(NetworkTest, TestNetworkIgnore) { + Network ipv4_network1("test_eth0", "Test Network Adapter 1", + IPAddress(0x12345600U), 24); + Network ipv4_network2("test_eth1", "Test Network Adapter 2", + IPAddress(0x00010000U), 16); + BasicNetworkManager network_manager; + EXPECT_FALSE(IsIgnoredNetwork(network_manager, ipv4_network1)); + EXPECT_TRUE(IsIgnoredNetwork(network_manager, ipv4_network2)); +} + +TEST_F(NetworkTest, TestIgnoreList) { + Network ignore_me("ignore_me", "Ignore me please!", + IPAddress(0x12345600U), 24); + Network include_me("include_me", "Include me please!", + IPAddress(0x12345600U), 24); + BasicNetworkManager network_manager; + EXPECT_FALSE(IsIgnoredNetwork(network_manager, ignore_me)); + EXPECT_FALSE(IsIgnoredNetwork(network_manager, include_me)); + std::vector ignore_list; + ignore_list.push_back("ignore_me"); + network_manager.set_network_ignore_list(ignore_list); + EXPECT_TRUE(IsIgnoredNetwork(network_manager, ignore_me)); + EXPECT_FALSE(IsIgnoredNetwork(network_manager, include_me)); +} + +// Test is failing on Windows opt: b/11288214 +TEST_F(NetworkTest, DISABLED_TestCreateNetworks) { + BasicNetworkManager manager; + NetworkManager::NetworkList result = GetNetworks(manager, true); + // We should be able to bind to any addresses we find. + NetworkManager::NetworkList::iterator it; + for (it = result.begin(); + it != result.end(); + ++it) { + sockaddr_storage storage; + memset(&storage, 0, sizeof(storage)); + IPAddress ip = (*it)->GetBestIP(); + SocketAddress bindaddress(ip, 0); + bindaddress.SetScopeID((*it)->scope_id()); + // TODO(thaloun): Use rtc::AsyncSocket once it supports IPv6. + int fd = static_cast(socket(ip.family(), SOCK_STREAM, IPPROTO_TCP)); + if (fd > 0) { + size_t ipsize = bindaddress.ToSockAddrStorage(&storage); + EXPECT_GE(ipsize, 0U); + int success = ::bind(fd, + reinterpret_cast(&storage), + static_cast(ipsize)); +#if defined(WEBRTC_WIN) + if (success) LOG_GLE(LS_ERROR) << "Socket bind failed."; +#endif + EXPECT_EQ(0, success); +#if defined(WEBRTC_WIN) + closesocket(fd); +#else + close(fd); +#endif + } + delete (*it); + } +} + +// Test that UpdateNetworks succeeds. +TEST_F(NetworkTest, TestUpdateNetworks) { + BasicNetworkManager manager; + manager.SignalNetworksChanged.connect( + static_cast(this), &NetworkTest::OnNetworksChanged); + manager.StartUpdating(); + Thread::Current()->ProcessMessages(0); + EXPECT_TRUE(callback_called_); + callback_called_ = false; + // Callback should be triggered immediately when StartUpdating + // is called, after network update signal is already sent. + manager.StartUpdating(); + EXPECT_TRUE(manager.started()); + Thread::Current()->ProcessMessages(0); + EXPECT_TRUE(callback_called_); + manager.StopUpdating(); + EXPECT_TRUE(manager.started()); + manager.StopUpdating(); + EXPECT_FALSE(manager.started()); + manager.StopUpdating(); + EXPECT_FALSE(manager.started()); + callback_called_ = false; + // Callback should be triggered immediately after StartUpdating is called + // when start_count_ is reset to 0. + manager.StartUpdating(); + Thread::Current()->ProcessMessages(0); + EXPECT_TRUE(callback_called_); +} + +// Verify that MergeNetworkList() merges network lists properly. +TEST_F(NetworkTest, TestBasicMergeNetworkList) { + Network ipv4_network1("test_eth0", "Test Network Adapter 1", + IPAddress(0x12345600U), 24); + Network ipv4_network2("test_eth1", "Test Network Adapter 2", + IPAddress(0x00010000U), 16); + ipv4_network1.AddIP(IPAddress(0x12345678)); + ipv4_network2.AddIP(IPAddress(0x00010004)); + BasicNetworkManager manager; + + // Add ipv4_network1 to the list of networks. + NetworkManager::NetworkList list; + list.push_back(new Network(ipv4_network1)); + bool changed; + MergeNetworkList(manager, list, &changed); + EXPECT_TRUE(changed); + list.clear(); + + manager.GetNetworks(&list); + EXPECT_EQ(1U, list.size()); + EXPECT_EQ(ipv4_network1.ToString(), list[0]->ToString()); + Network* net1 = list[0]; + list.clear(); + + // Replace ipv4_network1 with ipv4_network2. + list.push_back(new Network(ipv4_network2)); + MergeNetworkList(manager, list, &changed); + EXPECT_TRUE(changed); + list.clear(); + + manager.GetNetworks(&list); + EXPECT_EQ(1U, list.size()); + EXPECT_EQ(ipv4_network2.ToString(), list[0]->ToString()); + Network* net2 = list[0]; + list.clear(); + + // Add Network2 back. + list.push_back(new Network(ipv4_network1)); + list.push_back(new Network(ipv4_network2)); + MergeNetworkList(manager, list, &changed); + EXPECT_TRUE(changed); + list.clear(); + + // Verify that we get previous instances of Network objects. + manager.GetNetworks(&list); + EXPECT_EQ(2U, list.size()); + EXPECT_TRUE((net1 == list[0] && net2 == list[1]) || + (net1 == list[1] && net2 == list[0])); + list.clear(); + + // Call MergeNetworkList() again and verify that we don't get update + // notification. + list.push_back(new Network(ipv4_network2)); + list.push_back(new Network(ipv4_network1)); + MergeNetworkList(manager, list, &changed); + EXPECT_FALSE(changed); + list.clear(); + + // Verify that we get previous instances of Network objects. + manager.GetNetworks(&list); + EXPECT_EQ(2U, list.size()); + EXPECT_TRUE((net1 == list[0] && net2 == list[1]) || + (net1 == list[1] && net2 == list[0])); + list.clear(); +} + +// Sets up some test IPv6 networks and appends them to list. +// Four networks are added - public and link local, for two interfaces. +void SetupNetworks(NetworkManager::NetworkList* list) { + IPAddress ip; + IPAddress prefix; + EXPECT_TRUE(IPFromString("fe80::1234:5678:abcd:ef12", &ip)); + EXPECT_TRUE(IPFromString("fe80::", &prefix)); + // First, fake link-locals. + Network ipv6_eth0_linklocalnetwork("test_eth0", "Test NetworkAdapter 1", + prefix, 64); + ipv6_eth0_linklocalnetwork.AddIP(ip); + EXPECT_TRUE(IPFromString("fe80::5678:abcd:ef12:3456", &ip)); + Network ipv6_eth1_linklocalnetwork("test_eth1", "Test NetworkAdapter 2", + prefix, 64); + ipv6_eth1_linklocalnetwork.AddIP(ip); + // Public networks: + EXPECT_TRUE(IPFromString("2401:fa00:4:1000:be30:5bff:fee5:c3", &ip)); + prefix = TruncateIP(ip, 64); + Network ipv6_eth0_publicnetwork1_ip1("test_eth0", "Test NetworkAdapter 1", + prefix, 64); + ipv6_eth0_publicnetwork1_ip1.AddIP(ip); + EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:abcd:efab:cdef", &ip)); + prefix = TruncateIP(ip, 64); + Network ipv6_eth1_publicnetwork1_ip1("test_eth1", "Test NetworkAdapter 1", + prefix, 64); + ipv6_eth1_publicnetwork1_ip1.AddIP(ip); + list->push_back(new Network(ipv6_eth0_linklocalnetwork)); + list->push_back(new Network(ipv6_eth1_linklocalnetwork)); + list->push_back(new Network(ipv6_eth0_publicnetwork1_ip1)); + list->push_back(new Network(ipv6_eth1_publicnetwork1_ip1)); +} + +// Test that the basic network merging case works. +TEST_F(NetworkTest, TestIPv6MergeNetworkList) { + BasicNetworkManager manager; + manager.SignalNetworksChanged.connect( + static_cast(this), &NetworkTest::OnNetworksChanged); + NetworkManager::NetworkList original_list; + SetupNetworks(&original_list); + bool changed = false; + MergeNetworkList(manager, original_list, &changed); + EXPECT_TRUE(changed); + NetworkManager::NetworkList list; + manager.GetNetworks(&list); + EXPECT_EQ(original_list.size(), list.size()); + // Verify that the original members are in the merged list. + for (NetworkManager::NetworkList::iterator it = original_list.begin(); + it != original_list.end(); ++it) { + EXPECT_NE(list.end(), std::find(list.begin(), list.end(), *it)); + } +} + +// Tests that when two network lists that describe the same set of networks are +// merged, that the changed callback is not called, and that the original +// objects remain in the result list. +TEST_F(NetworkTest, TestNoChangeMerge) { + BasicNetworkManager manager; + manager.SignalNetworksChanged.connect( + static_cast(this), &NetworkTest::OnNetworksChanged); + NetworkManager::NetworkList original_list; + SetupNetworks(&original_list); + bool changed = false; + MergeNetworkList(manager, original_list, &changed); + EXPECT_TRUE(changed); + // Second list that describes the same networks but with new objects. + NetworkManager::NetworkList second_list; + SetupNetworks(&second_list); + changed = false; + MergeNetworkList(manager, second_list, &changed); + EXPECT_FALSE(changed); + NetworkManager::NetworkList resulting_list; + manager.GetNetworks(&resulting_list); + EXPECT_EQ(original_list.size(), resulting_list.size()); + // Verify that the original members are in the merged list. + for (NetworkManager::NetworkList::iterator it = original_list.begin(); + it != original_list.end(); ++it) { + EXPECT_NE(resulting_list.end(), + std::find(resulting_list.begin(), resulting_list.end(), *it)); + } + // Doublecheck that the new networks aren't in the list. + for (NetworkManager::NetworkList::iterator it = second_list.begin(); + it != second_list.end(); ++it) { + EXPECT_EQ(resulting_list.end(), + std::find(resulting_list.begin(), resulting_list.end(), *it)); + } +} + +// Test that we can merge a network that is the same as another network but with +// a different IP. The original network should remain in the list, but have its +// IP changed. +TEST_F(NetworkTest, MergeWithChangedIP) { + BasicNetworkManager manager; + manager.SignalNetworksChanged.connect( + static_cast(this), &NetworkTest::OnNetworksChanged); + NetworkManager::NetworkList original_list; + SetupNetworks(&original_list); + // Make a network that we're going to change. + IPAddress ip; + EXPECT_TRUE(IPFromString("2401:fa01:4:1000:be30:faa:fee:faa", &ip)); + IPAddress prefix = TruncateIP(ip, 64); + Network* network_to_change = new Network("test_eth0", + "Test Network Adapter 1", + prefix, 64); + Network* changed_network = new Network(*network_to_change); + network_to_change->AddIP(ip); + IPAddress changed_ip; + EXPECT_TRUE(IPFromString("2401:fa01:4:1000:be30:f00:f00:f00", &changed_ip)); + changed_network->AddIP(changed_ip); + original_list.push_back(network_to_change); + bool changed = false; + MergeNetworkList(manager, original_list, &changed); + NetworkManager::NetworkList second_list; + SetupNetworks(&second_list); + second_list.push_back(changed_network); + changed = false; + MergeNetworkList(manager, second_list, &changed); + EXPECT_TRUE(changed); + NetworkManager::NetworkList list; + manager.GetNetworks(&list); + EXPECT_EQ(original_list.size(), list.size()); + // Make sure the original network is still in the merged list. + EXPECT_NE(list.end(), + std::find(list.begin(), list.end(), network_to_change)); + EXPECT_EQ(changed_ip, network_to_change->GetIPs().at(0)); +} + +// Testing a similar case to above, but checking that a network can be updated +// with additional IPs (not just a replacement). +TEST_F(NetworkTest, TestMultipleIPMergeNetworkList) { + BasicNetworkManager manager; + manager.SignalNetworksChanged.connect( + static_cast(this), &NetworkTest::OnNetworksChanged); + NetworkManager::NetworkList original_list; + SetupNetworks(&original_list); + bool changed = false; + MergeNetworkList(manager, original_list, &changed); + EXPECT_TRUE(changed); + IPAddress ip; + IPAddress check_ip; + IPAddress prefix; + // Add a second IP to the public network on eth0 (2401:fa00:4:1000/64). + EXPECT_TRUE(IPFromString("2401:fa00:4:1000:be30:5bff:fee5:c6", &ip)); + prefix = TruncateIP(ip, 64); + Network ipv6_eth0_publicnetwork1_ip2("test_eth0", "Test NetworkAdapter 1", + prefix, 64); + // This is the IP that already existed in the public network on eth0. + EXPECT_TRUE(IPFromString("2401:fa00:4:1000:be30:5bff:fee5:c3", &check_ip)); + ipv6_eth0_publicnetwork1_ip2.AddIP(ip); + original_list.push_back(new Network(ipv6_eth0_publicnetwork1_ip2)); + changed = false; + MergeNetworkList(manager, original_list, &changed); + EXPECT_TRUE(changed); + // There should still be four networks. + NetworkManager::NetworkList list; + manager.GetNetworks(&list); + EXPECT_EQ(4U, list.size()); + // Check the gathered IPs. + int matchcount = 0; + for (NetworkManager::NetworkList::iterator it = list.begin(); + it != list.end(); ++it) { + if ((*it)->ToString() == original_list[2]->ToString()) { + ++matchcount; + EXPECT_EQ(1, matchcount); + // This should be the same network object as before. + EXPECT_EQ((*it), original_list[2]); + // But with two addresses now. + EXPECT_EQ(2U, (*it)->GetIPs().size()); + EXPECT_NE((*it)->GetIPs().end(), + std::find((*it)->GetIPs().begin(), + (*it)->GetIPs().end(), + check_ip)); + EXPECT_NE((*it)->GetIPs().end(), + std::find((*it)->GetIPs().begin(), + (*it)->GetIPs().end(), + ip)); + } else { + // Check the IP didn't get added anywhere it wasn't supposed to. + EXPECT_EQ((*it)->GetIPs().end(), + std::find((*it)->GetIPs().begin(), + (*it)->GetIPs().end(), + ip)); + } + } +} + +// Test that merge correctly distinguishes multiple networks on an interface. +TEST_F(NetworkTest, TestMultiplePublicNetworksOnOneInterfaceMerge) { + BasicNetworkManager manager; + manager.SignalNetworksChanged.connect( + static_cast(this), &NetworkTest::OnNetworksChanged); + NetworkManager::NetworkList original_list; + SetupNetworks(&original_list); + bool changed = false; + MergeNetworkList(manager, original_list, &changed); + EXPECT_TRUE(changed); + IPAddress ip; + IPAddress prefix; + // A second network for eth0. + EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:5bff:fee5:c3", &ip)); + prefix = TruncateIP(ip, 64); + Network ipv6_eth0_publicnetwork2_ip1("test_eth0", "Test NetworkAdapter 1", + prefix, 64); + ipv6_eth0_publicnetwork2_ip1.AddIP(ip); + original_list.push_back(new Network(ipv6_eth0_publicnetwork2_ip1)); + changed = false; + MergeNetworkList(manager, original_list, &changed); + EXPECT_TRUE(changed); + // There should be five networks now. + NetworkManager::NetworkList list; + manager.GetNetworks(&list); + EXPECT_EQ(5U, list.size()); + // Check the resulting addresses. + for (NetworkManager::NetworkList::iterator it = list.begin(); + it != list.end(); ++it) { + if ((*it)->prefix() == ipv6_eth0_publicnetwork2_ip1.prefix() && + (*it)->name() == ipv6_eth0_publicnetwork2_ip1.name()) { + // Check the new network has 1 IP and that it's the correct one. + EXPECT_EQ(1U, (*it)->GetIPs().size()); + EXPECT_EQ(ip, (*it)->GetIPs().at(0)); + } else { + // Check the IP didn't get added anywhere it wasn't supposed to. + EXPECT_EQ((*it)->GetIPs().end(), + std::find((*it)->GetIPs().begin(), + (*it)->GetIPs().end(), + ip)); + } + } +} + +// Test that DumpNetworks works. +TEST_F(NetworkTest, TestDumpNetworks) { + BasicNetworkManager manager; + manager.DumpNetworks(true); +} + +// Test that we can toggle IPv6 on and off. +TEST_F(NetworkTest, TestIPv6Toggle) { + BasicNetworkManager manager; + bool ipv6_found = false; + NetworkManager::NetworkList list; +#if !defined(WEBRTC_WIN) + // There should be at least one IPv6 network (fe80::/64 should be in there). + // TODO(thaloun): Disabling this test on windows for the moment as the test + // machines don't seem to have IPv6 installed on them at all. + manager.set_ipv6_enabled(true); + list = GetNetworks(manager, true); + for (NetworkManager::NetworkList::iterator it = list.begin(); + it != list.end(); ++it) { + if ((*it)->prefix().family() == AF_INET6) { + ipv6_found = true; + break; + } + } + EXPECT_TRUE(ipv6_found); + for (NetworkManager::NetworkList::iterator it = list.begin(); + it != list.end(); ++it) { + delete (*it); + } +#endif + ipv6_found = false; + manager.set_ipv6_enabled(false); + list = GetNetworks(manager, true); + for (NetworkManager::NetworkList::iterator it = list.begin(); + it != list.end(); ++it) { + if ((*it)->prefix().family() == AF_INET6) { + ipv6_found = true; + break; + } + } + EXPECT_FALSE(ipv6_found); + for (NetworkManager::NetworkList::iterator it = list.begin(); + it != list.end(); ++it) { + delete (*it); + } +} + +TEST_F(NetworkTest, TestNetworkListSorting) { + BasicNetworkManager manager; + Network ipv4_network1("test_eth0", "Test Network Adapter 1", + IPAddress(0x12345600U), 24); + ipv4_network1.AddIP(IPAddress(0x12345600U)); + + IPAddress ip; + IPAddress prefix; + EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:abcd:efab:cdef", &ip)); + prefix = TruncateIP(ip, 64); + Network ipv6_eth1_publicnetwork1_ip1("test_eth1", "Test NetworkAdapter 2", + prefix, 64); + ipv6_eth1_publicnetwork1_ip1.AddIP(ip); + + NetworkManager::NetworkList list; + list.push_back(new Network(ipv4_network1)); + list.push_back(new Network(ipv6_eth1_publicnetwork1_ip1)); + Network* net1 = list[0]; + Network* net2 = list[1]; + + bool changed = false; + MergeNetworkList(manager, list, &changed); + ASSERT_TRUE(changed); + // After sorting IPv6 network should be higher order than IPv4 networks. + EXPECT_TRUE(net1->preference() < net2->preference()); +} + +TEST_F(NetworkTest, TestNetworkAdapterTypes) { + Network wifi("wlan0", "Wireless Adapter", IPAddress(0x12345600U), 24, + ADAPTER_TYPE_WIFI); + EXPECT_EQ(ADAPTER_TYPE_WIFI, wifi.type()); + Network ethernet("eth0", "Ethernet", IPAddress(0x12345600U), 24, + ADAPTER_TYPE_ETHERNET); + EXPECT_EQ(ADAPTER_TYPE_ETHERNET, ethernet.type()); + Network cellular("test_cell", "Cellular Adapter", IPAddress(0x12345600U), 24, + ADAPTER_TYPE_CELLULAR); + EXPECT_EQ(ADAPTER_TYPE_CELLULAR, cellular.type()); + Network vpn("bridge_test", "VPN Adapter", IPAddress(0x12345600U), 24, + ADAPTER_TYPE_VPN); + EXPECT_EQ(ADAPTER_TYPE_VPN, vpn.type()); + Network unknown("test", "Test Adapter", IPAddress(0x12345600U), 24, + ADAPTER_TYPE_UNKNOWN); + EXPECT_EQ(ADAPTER_TYPE_UNKNOWN, unknown.type()); +} + +#if defined(WEBRTC_POSIX) +// Verify that we correctly handle interfaces with no address. +TEST_F(NetworkTest, TestConvertIfAddrsNoAddress) { + ifaddrs list; + memset(&list, 0, sizeof(list)); + list.ifa_name = const_cast("test_iface"); + + NetworkManager::NetworkList result; + BasicNetworkManager manager; + CallConvertIfAddrs(manager, &list, true, &result); + EXPECT_TRUE(result.empty()); +} +#endif // defined(WEBRTC_POSIX) + +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) +// If you want to test non-default routes, you can do the following on a linux +// machine: +// 1) Load the dummy network driver: +// sudo modprobe dummy +// sudo ifconfig dummy0 127.0.0.1 +// 2) Run this test and confirm the output says it found a dummy route (and +// passes). +// 3) When done: +// sudo rmmmod dummy +TEST_F(NetworkTest, TestIgnoreNonDefaultRoutes) { + BasicNetworkManager manager; + NetworkManager::NetworkList list; + list = GetNetworks(manager, false); + bool found_dummy = false; + LOG(LS_INFO) << "Looking for dummy network: "; + for (NetworkManager::NetworkList::iterator it = list.begin(); + it != list.end(); ++it) { + LOG(LS_INFO) << " Network name: " << (*it)->name(); + found_dummy |= (*it)->name().find("dummy0") != std::string::npos; + } + for (NetworkManager::NetworkList::iterator it = list.begin(); + it != list.end(); ++it) { + delete (*it); + } + if (!found_dummy) { + LOG(LS_INFO) << "No dummy found, quitting."; + return; + } + LOG(LS_INFO) << "Found dummy, running again while ignoring non-default " + << "routes."; + manager.set_ignore_non_default_routes(true); + list = GetNetworks(manager, false); + for (NetworkManager::NetworkList::iterator it = list.begin(); + it != list.end(); ++it) { + LOG(LS_INFO) << " Network name: " << (*it)->name(); + EXPECT_TRUE((*it)->name().find("dummy0") == std::string::npos); + } + for (NetworkManager::NetworkList::iterator it = list.begin(); + it != list.end(); ++it) { + delete (*it); + } +} +#endif + +// Test MergeNetworkList successfully combines all IPs for the same +// prefix/length into a single Network. +TEST_F(NetworkTest, TestMergeNetworkList) { + BasicNetworkManager manager; + NetworkManager::NetworkList list; + + // Create 2 IPAddress classes with only last digit different. + IPAddress ip1, ip2; + EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1)); + EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:2", &ip2)); + + // Create 2 networks with the same prefix and length. + Network* net1 = new Network("em1", "em1", TruncateIP(ip1, 64), 64); + Network* net2 = new Network("em1", "em1", TruncateIP(ip1, 64), 64); + + // Add different IP into each. + net1->AddIP(ip1); + net2->AddIP(ip2); + + list.push_back(net1); + list.push_back(net2); + bool changed; + MergeNetworkList(manager, list, &changed); + EXPECT_TRUE(changed); + + NetworkManager::NetworkList list2; + manager.GetNetworks(&list2); + + // Make sure the resulted networklist has only 1 element and 2 + // IPAddresses. + EXPECT_EQ(list2.size(), 1uL); + EXPECT_EQ(list2[0]->GetIPs().size(), 2uL); + EXPECT_EQ(list2[0]->GetIPs()[0], ip1); + EXPECT_EQ(list2[0]->GetIPs()[1], ip2); +} + +// Test that the filtering logic follows the defined ruleset in network.h. +TEST_F(NetworkTest, TestIPv6Selection) { + InterfaceAddress ip; + std::string ipstr; + + ipstr = "2401:fa00:4:1000:be30:5bff:fee5:c3"; + ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_DEPRECATED, &ip)); + + // Create a network with this prefix. + Network ipv6_network( + "test_eth0", "Test NetworkAdapter", TruncateIP(ip, 64), 64); + + // When there is no address added, it should return an unspecified + // address. + EXPECT_EQ(ipv6_network.GetBestIP(), IPAddress()); + EXPECT_TRUE(IPIsUnspec(ipv6_network.GetBestIP())); + + // Deprecated one should not be returned. + ipv6_network.AddIP(ip); + EXPECT_EQ(ipv6_network.GetBestIP(), IPAddress()); + + // Add ULA one. ULA is unique local address which is starting either + // with 0xfc or 0xfd. + ipstr = "fd00:fa00:4:1000:be30:5bff:fee5:c4"; + ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_NONE, &ip)); + ipv6_network.AddIP(ip); + EXPECT_EQ(ipv6_network.GetBestIP(), static_cast(ip)); + + // Add global one. + ipstr = "2401:fa00:4:1000:be30:5bff:fee5:c5"; + ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_NONE, &ip)); + ipv6_network.AddIP(ip); + EXPECT_EQ(ipv6_network.GetBestIP(), static_cast(ip)); + + // Add global dynamic temporary one. + ipstr = "2401:fa00:4:1000:be30:5bff:fee5:c6"; + ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_TEMPORARY, &ip)); + ipv6_network.AddIP(ip); + EXPECT_EQ(ipv6_network.GetBestIP(), static_cast(ip)); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nssidentity.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nssidentity.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nssidentity.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nssidentity.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,521 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include +#include + +#if HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#if HAVE_NSS_SSL_H + +#include "webrtc/base/nssidentity.h" + +#include "cert.h" +#include "cryptohi.h" +#include "keyhi.h" +#include "nss.h" +#include "pk11pub.h" +#include "sechash.h" + +#include "webrtc/base/logging.h" +#include "webrtc/base/helpers.h" +#include "webrtc/base/nssstreamadapter.h" +#include "webrtc/base/safe_conversions.h" + +namespace rtc { + +// Certificate validity lifetime in seconds. +static const int CERTIFICATE_LIFETIME = 60*60*24*30; // 30 days, arbitrarily +// Certificate validity window in seconds. +// This is to compensate for slightly incorrect system clocks. +static const int CERTIFICATE_WINDOW = -60*60*24; + +NSSKeyPair::~NSSKeyPair() { + if (privkey_) + SECKEY_DestroyPrivateKey(privkey_); + if (pubkey_) + SECKEY_DestroyPublicKey(pubkey_); +} + +NSSKeyPair *NSSKeyPair::Generate() { + SECKEYPrivateKey *privkey = NULL; + SECKEYPublicKey *pubkey = NULL; + PK11RSAGenParams rsaparams; + rsaparams.keySizeInBits = 1024; + rsaparams.pe = 0x010001; // 65537 -- a common RSA public exponent. + + privkey = PK11_GenerateKeyPair(NSSContext::GetSlot(), + CKM_RSA_PKCS_KEY_PAIR_GEN, + &rsaparams, &pubkey, PR_FALSE /*permanent*/, + PR_FALSE /*sensitive*/, NULL); + if (!privkey) { + LOG(LS_ERROR) << "Couldn't generate key pair"; + return NULL; + } + + return new NSSKeyPair(privkey, pubkey); +} + +// Just make a copy. +NSSKeyPair *NSSKeyPair::GetReference() { + SECKEYPrivateKey *privkey = SECKEY_CopyPrivateKey(privkey_); + if (!privkey) + return NULL; + + SECKEYPublicKey *pubkey = SECKEY_CopyPublicKey(pubkey_); + if (!pubkey) { + SECKEY_DestroyPrivateKey(privkey); + return NULL; + } + + return new NSSKeyPair(privkey, pubkey); +} + +NSSCertificate::NSSCertificate(CERTCertificate* cert) + : certificate_(CERT_DupCertificate(cert)) { + ASSERT(certificate_ != NULL); +} + +static void DeleteCert(SSLCertificate* cert) { + delete cert; +} + +NSSCertificate::NSSCertificate(CERTCertList* cert_list) { + // Copy the first cert into certificate_. + CERTCertListNode* node = CERT_LIST_HEAD(cert_list); + certificate_ = CERT_DupCertificate(node->cert); + + // Put any remaining certificates into the chain. + node = CERT_LIST_NEXT(node); + std::vector certs; + for (; !CERT_LIST_END(node, cert_list); node = CERT_LIST_NEXT(node)) { + certs.push_back(new NSSCertificate(node->cert)); + } + + if (!certs.empty()) + chain_.reset(new SSLCertChain(certs)); + + // The SSLCertChain constructor copies its input, so now we have to delete + // the originals. + std::for_each(certs.begin(), certs.end(), DeleteCert); +} + +NSSCertificate::NSSCertificate(CERTCertificate* cert, SSLCertChain* chain) + : certificate_(CERT_DupCertificate(cert)) { + ASSERT(certificate_ != NULL); + if (chain) + chain_.reset(chain->Copy()); +} + + +NSSCertificate *NSSCertificate::FromPEMString(const std::string &pem_string) { + std::string der; + if (!SSLIdentity::PemToDer(kPemTypeCertificate, pem_string, &der)) + return NULL; + + SECItem der_cert; + der_cert.data = reinterpret_cast(const_cast( + der.data())); + der_cert.len = checked_cast(der.size()); + CERTCertificate *cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), + &der_cert, NULL, PR_FALSE, PR_TRUE); + + if (!cert) + return NULL; + + NSSCertificate* ret = new NSSCertificate(cert); + CERT_DestroyCertificate(cert); + return ret; +} + +NSSCertificate *NSSCertificate::GetReference() const { + return new NSSCertificate(certificate_, chain_.get()); +} + +std::string NSSCertificate::ToPEMString() const { + return SSLIdentity::DerToPem(kPemTypeCertificate, + certificate_->derCert.data, + certificate_->derCert.len); +} + +void NSSCertificate::ToDER(Buffer* der_buffer) const { + der_buffer->SetData(certificate_->derCert.data, certificate_->derCert.len); +} + +static bool Certifies(CERTCertificate* parent, CERTCertificate* child) { + // TODO(bemasc): Identify stricter validation checks to use here. In the + // context of some future identity standard, it might make sense to check + // the certificates' roles, expiration dates, self-signatures (if + // self-signed), certificate transparency logging, or many other attributes. + // NOTE: Future changes to this validation may reject some previously allowed + // certificate chains. Users should be advised not to deploy chained + // certificates except in controlled environments until the validity + // requirements are finalized. + + // Check that the parent's name is the same as the child's claimed issuer. + SECComparison name_status = + CERT_CompareName(&child->issuer, &parent->subject); + if (name_status != SECEqual) + return false; + + // Extract the parent's public key, or fail if the key could not be read + // (e.g. certificate is corrupted). + SECKEYPublicKey* parent_key = CERT_ExtractPublicKey(parent); + if (!parent_key) + return false; + + // Check that the parent's privkey was actually used to generate the child's + // signature. + SECStatus verified = CERT_VerifySignedDataWithPublicKey( + &child->signatureWrap, parent_key, NULL); + SECKEY_DestroyPublicKey(parent_key); + return verified == SECSuccess; +} + +bool NSSCertificate::IsValidChain(const CERTCertList* cert_list) { + CERTCertListNode* child = CERT_LIST_HEAD(cert_list); + for (CERTCertListNode* parent = CERT_LIST_NEXT(child); + !CERT_LIST_END(parent, cert_list); + child = parent, parent = CERT_LIST_NEXT(parent)) { + if (!Certifies(parent->cert, child->cert)) + return false; + } + return true; +} + +bool NSSCertificate::GetDigestLength(const std::string& algorithm, + size_t* length) { + const SECHashObject *ho; + + if (!GetDigestObject(algorithm, &ho)) + return false; + + *length = ho->length; + + return true; +} + +bool NSSCertificate::GetSignatureDigestAlgorithm(std::string* algorithm) const { + // The function sec_DecodeSigAlg in NSS provides this mapping functionality. + // Unfortunately it is private, so the functionality must be duplicated here. + // See https://bugzilla.mozilla.org/show_bug.cgi?id=925165 . + SECOidTag sig_alg = SECOID_GetAlgorithmTag(&certificate_->signature); + switch (sig_alg) { + case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: + *algorithm = DIGEST_MD5; + break; + case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION: + case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE: + case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE: + case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST: + case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST: + case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE: + case SEC_OID_MISSI_DSS: + case SEC_OID_MISSI_KEA_DSS: + case SEC_OID_MISSI_KEA_DSS_OLD: + case SEC_OID_MISSI_DSS_OLD: + *algorithm = DIGEST_SHA_1; + break; + case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE: + case SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION: + case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST: + *algorithm = DIGEST_SHA_224; + break; + case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE: + case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION: + case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST: + *algorithm = DIGEST_SHA_256; + break; + case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE: + case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION: + *algorithm = DIGEST_SHA_384; + break; + case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE: + case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION: + *algorithm = DIGEST_SHA_512; + break; + default: + // Unknown algorithm. There are several unhandled options that are less + // common and more complex. + algorithm->clear(); + return false; + } + return true; +} + +bool NSSCertificate::ComputeDigest(const std::string& algorithm, + unsigned char* digest, + size_t size, + size_t* length) const { + const SECHashObject *ho; + + if (!GetDigestObject(algorithm, &ho)) + return false; + + if (size < ho->length) // Sanity check for fit + return false; + + SECStatus rv = HASH_HashBuf(ho->type, digest, + certificate_->derCert.data, + certificate_->derCert.len); + if (rv != SECSuccess) + return false; + + *length = ho->length; + + return true; +} + +bool NSSCertificate::GetChain(SSLCertChain** chain) const { + if (!chain_) + return false; + + *chain = chain_->Copy(); + return true; +} + +bool NSSCertificate::Equals(const NSSCertificate *tocompare) const { + if (!certificate_->derCert.len) + return false; + if (!tocompare->certificate_->derCert.len) + return false; + + if (certificate_->derCert.len != tocompare->certificate_->derCert.len) + return false; + + return memcmp(certificate_->derCert.data, + tocompare->certificate_->derCert.data, + certificate_->derCert.len) == 0; +} + + +bool NSSCertificate::GetDigestObject(const std::string &algorithm, + const SECHashObject **hop) { + const SECHashObject *ho; + HASH_HashType hash_type; + + if (algorithm == DIGEST_SHA_1) { + hash_type = HASH_AlgSHA1; + // HASH_AlgSHA224 is not supported in the chromium linux build system. +#if 0 + } else if (algorithm == DIGEST_SHA_224) { + hash_type = HASH_AlgSHA224; +#endif + } else if (algorithm == DIGEST_SHA_256) { + hash_type = HASH_AlgSHA256; + } else if (algorithm == DIGEST_SHA_384) { + hash_type = HASH_AlgSHA384; + } else if (algorithm == DIGEST_SHA_512) { + hash_type = HASH_AlgSHA512; + } else { + return false; + } + + ho = HASH_GetHashObject(hash_type); + + ASSERT(ho->length >= 20); // Can't happen + *hop = ho; + + return true; +} + + +NSSIdentity* NSSIdentity::GenerateInternal(const SSLIdentityParams& params) { + std::string subject_name_string = "CN=" + params.common_name; + CERTName *subject_name = CERT_AsciiToName( + const_cast(subject_name_string.c_str())); + NSSIdentity *identity = NULL; + CERTSubjectPublicKeyInfo *spki = NULL; + CERTCertificateRequest *certreq = NULL; + CERTValidity *validity = NULL; + CERTCertificate *certificate = NULL; + NSSKeyPair *keypair = NSSKeyPair::Generate(); + SECItem inner_der; + SECStatus rv; + PLArenaPool* arena; + SECItem signed_cert; + PRTime now = PR_Now(); + PRTime not_before = + now + static_cast(params.not_before) * PR_USEC_PER_SEC; + PRTime not_after = + now + static_cast(params.not_after) * PR_USEC_PER_SEC; + + inner_der.len = 0; + inner_der.data = NULL; + + if (!keypair) { + LOG(LS_ERROR) << "Couldn't generate key pair"; + goto fail; + } + + if (!subject_name) { + LOG(LS_ERROR) << "Couldn't convert subject name " << subject_name; + goto fail; + } + + spki = SECKEY_CreateSubjectPublicKeyInfo(keypair->pubkey()); + if (!spki) { + LOG(LS_ERROR) << "Couldn't create SPKI"; + goto fail; + } + + certreq = CERT_CreateCertificateRequest(subject_name, spki, NULL); + if (!certreq) { + LOG(LS_ERROR) << "Couldn't create certificate signing request"; + goto fail; + } + + validity = CERT_CreateValidity(not_before, not_after); + if (!validity) { + LOG(LS_ERROR) << "Couldn't create validity"; + goto fail; + } + + unsigned long serial; + // Note: This serial in principle could collide, but it's unlikely + rv = PK11_GenerateRandom(reinterpret_cast(&serial), + sizeof(serial)); + if (rv != SECSuccess) { + LOG(LS_ERROR) << "Couldn't generate random serial"; + goto fail; + } + + certificate = CERT_CreateCertificate(serial, subject_name, validity, certreq); + if (!certificate) { + LOG(LS_ERROR) << "Couldn't create certificate"; + goto fail; + } + + arena = certificate->arena; + + rv = SECOID_SetAlgorithmID(arena, &certificate->signature, + SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, NULL); + if (rv != SECSuccess) + goto fail; + + // Set version to X509v3. + *(certificate->version.data) = 2; + certificate->version.len = 1; + + if (!SEC_ASN1EncodeItem(arena, &inner_der, certificate, + SEC_ASN1_GET(CERT_CertificateTemplate))) + goto fail; + + rv = SEC_DerSignData(arena, &signed_cert, inner_der.data, inner_der.len, + keypair->privkey(), + SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION); + if (rv != SECSuccess) { + LOG(LS_ERROR) << "Couldn't sign certificate"; + goto fail; + } + certificate->derCert = signed_cert; + + identity = new NSSIdentity(keypair, new NSSCertificate(certificate)); + + goto done; + + fail: + delete keypair; + + done: + if (certificate) CERT_DestroyCertificate(certificate); + if (subject_name) CERT_DestroyName(subject_name); + if (spki) SECKEY_DestroySubjectPublicKeyInfo(spki); + if (certreq) CERT_DestroyCertificateRequest(certreq); + if (validity) CERT_DestroyValidity(validity); + return identity; +} + +NSSIdentity* NSSIdentity::Generate(const std::string &common_name) { + SSLIdentityParams params; + params.common_name = common_name; + params.not_before = CERTIFICATE_WINDOW; + params.not_after = CERTIFICATE_LIFETIME; + return GenerateInternal(params); +} + +NSSIdentity* NSSIdentity::GenerateForTest(const SSLIdentityParams& params) { + return GenerateInternal(params); +} + +SSLIdentity* NSSIdentity::FromPEMStrings(const std::string& private_key, + const std::string& certificate) { + std::string private_key_der; + if (!SSLIdentity::PemToDer( + kPemTypeRsaPrivateKey, private_key, &private_key_der)) + return NULL; + + SECItem private_key_item; + private_key_item.data = reinterpret_cast( + const_cast(private_key_der.c_str())); + private_key_item.len = checked_cast(private_key_der.size()); + + const unsigned int key_usage = KU_KEY_ENCIPHERMENT | KU_DATA_ENCIPHERMENT | + KU_DIGITAL_SIGNATURE; + + SECKEYPrivateKey* privkey = NULL; + SECStatus rv = + PK11_ImportDERPrivateKeyInfoAndReturnKey(NSSContext::GetSlot(), + &private_key_item, + NULL, NULL, PR_FALSE, PR_FALSE, + key_usage, &privkey, NULL); + if (rv != SECSuccess) { + LOG(LS_ERROR) << "Couldn't import private key"; + return NULL; + } + + SECKEYPublicKey *pubkey = SECKEY_ConvertToPublicKey(privkey); + if (rv != SECSuccess) { + SECKEY_DestroyPrivateKey(privkey); + LOG(LS_ERROR) << "Couldn't convert private key to public key"; + return NULL; + } + + // Assign to a scoped_ptr so we don't leak on error. + scoped_ptr keypair(new NSSKeyPair(privkey, pubkey)); + + scoped_ptr cert(NSSCertificate::FromPEMString(certificate)); + if (!cert) { + LOG(LS_ERROR) << "Couldn't parse certificate"; + return NULL; + } + + // TODO(ekr@rtfm.com): Check the public key against the certificate. + + return new NSSIdentity(keypair.release(), cert.release()); +} + +NSSIdentity *NSSIdentity::GetReference() const { + NSSKeyPair *keypair = keypair_->GetReference(); + if (!keypair) + return NULL; + + NSSCertificate *certificate = certificate_->GetReference(); + if (!certificate) { + delete keypair; + return NULL; + } + + return new NSSIdentity(keypair, certificate); +} + + +NSSCertificate &NSSIdentity::certificate() const { + return *certificate_; +} + + +} // rtc namespace + +#endif // HAVE_NSS_SSL_H + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nssidentity.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nssidentity.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nssidentity.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nssidentity.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,130 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_NSSIDENTITY_H_ +#define WEBRTC_BASE_NSSIDENTITY_H_ + +#include + +#include "cert.h" +#include "nspr.h" +#include "hasht.h" +#include "keythi.h" + +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/sslidentity.h" + +namespace rtc { + +class NSSKeyPair { + public: + NSSKeyPair(SECKEYPrivateKey* privkey, SECKEYPublicKey* pubkey) : + privkey_(privkey), pubkey_(pubkey) {} + ~NSSKeyPair(); + + // Generate a 1024-bit RSA key pair. + static NSSKeyPair* Generate(); + NSSKeyPair* GetReference(); + + SECKEYPrivateKey* privkey() const { return privkey_; } + SECKEYPublicKey * pubkey() const { return pubkey_; } + + private: + SECKEYPrivateKey* privkey_; + SECKEYPublicKey* pubkey_; + + DISALLOW_EVIL_CONSTRUCTORS(NSSKeyPair); +}; + + +class NSSCertificate : public SSLCertificate { + public: + static NSSCertificate* FromPEMString(const std::string& pem_string); + // The caller retains ownership of the argument to all the constructors, + // and the constructor makes a copy. + explicit NSSCertificate(CERTCertificate* cert); + explicit NSSCertificate(CERTCertList* cert_list); + virtual ~NSSCertificate() { + if (certificate_) + CERT_DestroyCertificate(certificate_); + } + + virtual NSSCertificate* GetReference() const; + + virtual std::string ToPEMString() const; + + virtual void ToDER(Buffer* der_buffer) const; + + virtual bool GetSignatureDigestAlgorithm(std::string* algorithm) const; + + virtual bool ComputeDigest(const std::string& algorithm, + unsigned char* digest, + size_t size, + size_t* length) const; + + virtual bool GetChain(SSLCertChain** chain) const; + + CERTCertificate* certificate() { return certificate_; } + + // Performs minimal checks to determine if the list is a valid chain. This + // only checks that each certificate certifies the preceding certificate, + // and ignores many other certificate features such as expiration dates. + static bool IsValidChain(const CERTCertList* cert_list); + + // Helper function to get the length of a digest + static bool GetDigestLength(const std::string& algorithm, size_t* length); + + // Comparison. Only the certificate itself is considered, not the chain. + bool Equals(const NSSCertificate* tocompare) const; + + private: + NSSCertificate(CERTCertificate* cert, SSLCertChain* chain); + static bool GetDigestObject(const std::string& algorithm, + const SECHashObject** hash_object); + + CERTCertificate* certificate_; + scoped_ptr chain_; + + DISALLOW_EVIL_CONSTRUCTORS(NSSCertificate); +}; + +// Represents a SSL key pair and certificate for NSS. +class NSSIdentity : public SSLIdentity { + public: + static NSSIdentity* Generate(const std::string& common_name); + static NSSIdentity* GenerateForTest(const SSLIdentityParams& params); + static SSLIdentity* FromPEMStrings(const std::string& private_key, + const std::string& certificate); + virtual ~NSSIdentity() { + LOG(LS_INFO) << "Destroying NSS identity"; + } + + virtual NSSIdentity* GetReference() const; + virtual NSSCertificate& certificate() const; + + NSSKeyPair* keypair() const { return keypair_.get(); } + + private: + NSSIdentity(NSSKeyPair* keypair, NSSCertificate* cert) : + keypair_(keypair), certificate_(cert) {} + + static NSSIdentity* GenerateInternal(const SSLIdentityParams& params); + + rtc::scoped_ptr keypair_; + rtc::scoped_ptr certificate_; + + DISALLOW_EVIL_CONSTRUCTORS(NSSIdentity); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_NSSIDENTITY_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nssstreamadapter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nssstreamadapter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nssstreamadapter.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nssstreamadapter.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,1016 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#if HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#if HAVE_NSS_SSL_H + +#include "webrtc/base/nssstreamadapter.h" + +#include "keyhi.h" +#include "nspr.h" +#include "nss.h" +#include "pk11pub.h" +#include "secerr.h" + +#ifdef NSS_SSL_RELATIVE_PATH +#include "ssl.h" +#include "sslerr.h" +#include "sslproto.h" +#else +#include "net/third_party/nss/ssl/ssl.h" +#include "net/third_party/nss/ssl/sslerr.h" +#include "net/third_party/nss/ssl/sslproto.h" +#endif + +#include "webrtc/base/nssidentity.h" +#include "webrtc/base/safe_conversions.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +PRDescIdentity NSSStreamAdapter::nspr_layer_identity = PR_INVALID_IO_LAYER; + +#define UNIMPLEMENTED \ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); \ + LOG(LS_ERROR) \ + << "Call to unimplemented function "<< __FUNCTION__; ASSERT(false) + +#ifdef SRTP_AES128_CM_HMAC_SHA1_80 +#define HAVE_DTLS_SRTP +#endif + +#ifdef HAVE_DTLS_SRTP +// SRTP cipher suite table +struct SrtpCipherMapEntry { + const char* external_name; + PRUint16 cipher_id; +}; + +// This isn't elegant, but it's better than an external reference +static const SrtpCipherMapEntry kSrtpCipherMap[] = { + {"AES_CM_128_HMAC_SHA1_80", SRTP_AES128_CM_HMAC_SHA1_80 }, + {"AES_CM_128_HMAC_SHA1_32", SRTP_AES128_CM_HMAC_SHA1_32 }, + {NULL, 0} +}; +#endif + + +// Implementation of NSPR methods +static PRStatus StreamClose(PRFileDesc *socket) { + ASSERT(!socket->lower); + socket->dtor(socket); + return PR_SUCCESS; +} + +static PRInt32 StreamRead(PRFileDesc *socket, void *buf, PRInt32 length) { + StreamInterface *stream = reinterpret_cast(socket->secret); + size_t read; + int error; + StreamResult result = stream->Read(buf, length, &read, &error); + if (result == SR_SUCCESS) { + return checked_cast(read); + } + + if (result == SR_EOS) { + return 0; + } + + if (result == SR_BLOCK) { + PR_SetError(PR_WOULD_BLOCK_ERROR, 0); + return -1; + } + + PR_SetError(PR_UNKNOWN_ERROR, error); + return -1; +} + +static PRInt32 StreamWrite(PRFileDesc *socket, const void *buf, + PRInt32 length) { + StreamInterface *stream = reinterpret_cast(socket->secret); + size_t written; + int error; + StreamResult result = stream->Write(buf, length, &written, &error); + if (result == SR_SUCCESS) { + return checked_cast(written); + } + + if (result == SR_BLOCK) { + LOG(LS_INFO) << + "NSSStreamAdapter: write to underlying transport would block"; + PR_SetError(PR_WOULD_BLOCK_ERROR, 0); + return -1; + } + + LOG(LS_ERROR) << "Write error"; + PR_SetError(PR_UNKNOWN_ERROR, error); + return -1; +} + +static PRInt32 StreamAvailable(PRFileDesc *socket) { + UNIMPLEMENTED; + return -1; +} + +PRInt64 StreamAvailable64(PRFileDesc *socket) { + UNIMPLEMENTED; + return -1; +} + +static PRStatus StreamSync(PRFileDesc *socket) { + UNIMPLEMENTED; + return PR_FAILURE; +} + +static PROffset32 StreamSeek(PRFileDesc *socket, PROffset32 offset, + PRSeekWhence how) { + UNIMPLEMENTED; + return -1; +} + +static PROffset64 StreamSeek64(PRFileDesc *socket, PROffset64 offset, + PRSeekWhence how) { + UNIMPLEMENTED; + return -1; +} + +static PRStatus StreamFileInfo(PRFileDesc *socket, PRFileInfo *info) { + UNIMPLEMENTED; + return PR_FAILURE; +} + +static PRStatus StreamFileInfo64(PRFileDesc *socket, PRFileInfo64 *info) { + UNIMPLEMENTED; + return PR_FAILURE; +} + +static PRInt32 StreamWritev(PRFileDesc *socket, const PRIOVec *iov, + PRInt32 iov_size, PRIntervalTime timeout) { + UNIMPLEMENTED; + return -1; +} + +static PRStatus StreamConnect(PRFileDesc *socket, const PRNetAddr *addr, + PRIntervalTime timeout) { + UNIMPLEMENTED; + return PR_FAILURE; +} + +static PRFileDesc *StreamAccept(PRFileDesc *sd, PRNetAddr *addr, + PRIntervalTime timeout) { + UNIMPLEMENTED; + return NULL; +} + +static PRStatus StreamBind(PRFileDesc *socket, const PRNetAddr *addr) { + UNIMPLEMENTED; + return PR_FAILURE; +} + +static PRStatus StreamListen(PRFileDesc *socket, PRIntn depth) { + UNIMPLEMENTED; + return PR_FAILURE; +} + +static PRStatus StreamShutdown(PRFileDesc *socket, PRIntn how) { + UNIMPLEMENTED; + return PR_FAILURE; +} + +// Note: this is always nonblocking and ignores the timeout. +// TODO(ekr@rtfm.com): In future verify that the socket is +// actually in non-blocking mode. +// This function does not support peek. +static PRInt32 StreamRecv(PRFileDesc *socket, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime to) { + ASSERT(flags == 0); + + if (flags != 0) { + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return -1; + } + + return StreamRead(socket, buf, amount); +} + +// Note: this is always nonblocking and assumes a zero timeout. +// This function does not support peek. +static PRInt32 StreamSend(PRFileDesc *socket, const void *buf, + PRInt32 amount, PRIntn flags, + PRIntervalTime to) { + ASSERT(flags == 0); + + return StreamWrite(socket, buf, amount); +} + +static PRInt32 StreamRecvfrom(PRFileDesc *socket, void *buf, + PRInt32 amount, PRIntn flags, + PRNetAddr *addr, PRIntervalTime to) { + UNIMPLEMENTED; + return -1; +} + +static PRInt32 StreamSendto(PRFileDesc *socket, const void *buf, + PRInt32 amount, PRIntn flags, + const PRNetAddr *addr, PRIntervalTime to) { + UNIMPLEMENTED; + return -1; +} + +static PRInt16 StreamPoll(PRFileDesc *socket, PRInt16 in_flags, + PRInt16 *out_flags) { + UNIMPLEMENTED; + return -1; +} + +static PRInt32 StreamAcceptRead(PRFileDesc *sd, PRFileDesc **nd, + PRNetAddr **raddr, + void *buf, PRInt32 amount, PRIntervalTime t) { + UNIMPLEMENTED; + return -1; +} + +static PRInt32 StreamTransmitFile(PRFileDesc *sd, PRFileDesc *socket, + const void *headers, PRInt32 hlen, + PRTransmitFileFlags flags, PRIntervalTime t) { + UNIMPLEMENTED; + return -1; +} + +static PRStatus StreamGetPeerName(PRFileDesc *socket, PRNetAddr *addr) { + // TODO(ekr@rtfm.com): Modify to return unique names for each channel + // somehow, as opposed to always the same static address. The current + // implementation messes up the session cache, which is why it's off + // elsewhere + addr->inet.family = PR_AF_INET; + addr->inet.port = 0; + addr->inet.ip = 0; + + return PR_SUCCESS; +} + +static PRStatus StreamGetSockName(PRFileDesc *socket, PRNetAddr *addr) { + UNIMPLEMENTED; + return PR_FAILURE; +} + +static PRStatus StreamGetSockOption(PRFileDesc *socket, PRSocketOptionData *opt) { + switch (opt->option) { + case PR_SockOpt_Nonblocking: + opt->value.non_blocking = PR_TRUE; + return PR_SUCCESS; + default: + UNIMPLEMENTED; + break; + } + + return PR_FAILURE; +} + +// Imitate setting socket options. These are mostly noops. +static PRStatus StreamSetSockOption(PRFileDesc *socket, + const PRSocketOptionData *opt) { + switch (opt->option) { + case PR_SockOpt_Nonblocking: + return PR_SUCCESS; + case PR_SockOpt_NoDelay: + return PR_SUCCESS; + default: + UNIMPLEMENTED; + break; + } + + return PR_FAILURE; +} + +static PRInt32 StreamSendfile(PRFileDesc *out, PRSendFileData *in, + PRTransmitFileFlags flags, PRIntervalTime to) { + UNIMPLEMENTED; + return -1; +} + +static PRStatus StreamConnectContinue(PRFileDesc *socket, PRInt16 flags) { + UNIMPLEMENTED; + return PR_FAILURE; +} + +static PRIntn StreamReserved(PRFileDesc *socket) { + UNIMPLEMENTED; + return -1; +} + +static const struct PRIOMethods nss_methods = { + PR_DESC_LAYERED, + StreamClose, + StreamRead, + StreamWrite, + StreamAvailable, + StreamAvailable64, + StreamSync, + StreamSeek, + StreamSeek64, + StreamFileInfo, + StreamFileInfo64, + StreamWritev, + StreamConnect, + StreamAccept, + StreamBind, + StreamListen, + StreamShutdown, + StreamRecv, + StreamSend, + StreamRecvfrom, + StreamSendto, + StreamPoll, + StreamAcceptRead, + StreamTransmitFile, + StreamGetSockName, + StreamGetPeerName, + StreamReserved, + StreamReserved, + StreamGetSockOption, + StreamSetSockOption, + StreamSendfile, + StreamConnectContinue, + StreamReserved, + StreamReserved, + StreamReserved, + StreamReserved +}; + +NSSStreamAdapter::NSSStreamAdapter(StreamInterface *stream) + : SSLStreamAdapterHelper(stream), + ssl_fd_(NULL), + cert_ok_(false) { +} + +bool NSSStreamAdapter::Init() { + if (nspr_layer_identity == PR_INVALID_IO_LAYER) { + nspr_layer_identity = PR_GetUniqueIdentity("nssstreamadapter"); + } + PRFileDesc *pr_fd = PR_CreateIOLayerStub(nspr_layer_identity, &nss_methods); + if (!pr_fd) + return false; + pr_fd->secret = reinterpret_cast(stream()); + + PRFileDesc *ssl_fd; + if (ssl_mode_ == SSL_MODE_DTLS) { + ssl_fd = DTLS_ImportFD(NULL, pr_fd); + } else { + ssl_fd = SSL_ImportFD(NULL, pr_fd); + } + ASSERT(ssl_fd != NULL); // This should never happen + if (!ssl_fd) { + PR_Close(pr_fd); + return false; + } + + SECStatus rv; + // Turn on security. + rv = SSL_OptionSet(ssl_fd, SSL_SECURITY, PR_TRUE); + if (rv != SECSuccess) { + LOG(LS_ERROR) << "Error enabling security on SSL Socket"; + return false; + } + + // Disable SSLv2. + rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_SSL2, PR_FALSE); + if (rv != SECSuccess) { + LOG(LS_ERROR) << "Error disabling SSL2"; + return false; + } + + // Disable caching. + // TODO(ekr@rtfm.com): restore this when I have the caching + // identity set. + rv = SSL_OptionSet(ssl_fd, SSL_NO_CACHE, PR_TRUE); + if (rv != SECSuccess) { + LOG(LS_ERROR) << "Error disabling cache"; + return false; + } + + // Disable session tickets. + rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_SESSION_TICKETS, PR_FALSE); + if (rv != SECSuccess) { + LOG(LS_ERROR) << "Error enabling tickets"; + return false; + } + + // Disable renegotiation. + rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_RENEGOTIATION, + SSL_RENEGOTIATE_NEVER); + if (rv != SECSuccess) { + LOG(LS_ERROR) << "Error disabling renegotiation"; + return false; + } + + // Disable false start. + rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_FALSE_START, PR_FALSE); + if (rv != SECSuccess) { + LOG(LS_ERROR) << "Error disabling false start"; + return false; + } + + ssl_fd_ = ssl_fd; + + return true; +} + +NSSStreamAdapter::~NSSStreamAdapter() { + if (ssl_fd_) + PR_Close(ssl_fd_); +}; + + +int NSSStreamAdapter::BeginSSL() { + SECStatus rv; + + if (!Init()) { + Error("Init", -1, false); + return -1; + } + + ASSERT(state_ == SSL_CONNECTING); + // The underlying stream has been opened. If we are in peer-to-peer mode + // then a peer certificate must have been specified by now. + ASSERT(!ssl_server_name_.empty() || + peer_certificate_.get() != NULL || + !peer_certificate_digest_algorithm_.empty()); + LOG(LS_INFO) << "BeginSSL: " + << (!ssl_server_name_.empty() ? ssl_server_name_ : + "with peer"); + + if (role_ == SSL_CLIENT) { + LOG(LS_INFO) << "BeginSSL: as client"; + + rv = SSL_GetClientAuthDataHook(ssl_fd_, GetClientAuthDataHook, + this); + if (rv != SECSuccess) { + Error("BeginSSL", -1, false); + return -1; + } + } else { + LOG(LS_INFO) << "BeginSSL: as server"; + NSSIdentity *identity; + + if (identity_.get()) { + identity = static_cast(identity_.get()); + } else { + LOG(LS_ERROR) << "Can't be an SSL server without an identity"; + Error("BeginSSL", -1, false); + return -1; + } + rv = SSL_ConfigSecureServer(ssl_fd_, identity->certificate().certificate(), + identity->keypair()->privkey(), + kt_rsa); + if (rv != SECSuccess) { + Error("BeginSSL", -1, false); + return -1; + } + + // Insist on a certificate from the client + rv = SSL_OptionSet(ssl_fd_, SSL_REQUEST_CERTIFICATE, PR_TRUE); + if (rv != SECSuccess) { + Error("BeginSSL", -1, false); + return -1; + } + + // TODO(juberti): Check for client_auth_enabled() + + rv = SSL_OptionSet(ssl_fd_, SSL_REQUIRE_CERTIFICATE, PR_TRUE); + if (rv != SECSuccess) { + Error("BeginSSL", -1, false); + return -1; + } + } + + // Set the version range. + SSLVersionRange vrange; + vrange.min = (ssl_mode_ == SSL_MODE_DTLS) ? + SSL_LIBRARY_VERSION_TLS_1_1 : + SSL_LIBRARY_VERSION_TLS_1_0; + vrange.max = SSL_LIBRARY_VERSION_TLS_1_1; + + rv = SSL_VersionRangeSet(ssl_fd_, &vrange); + if (rv != SECSuccess) { + Error("BeginSSL", -1, false); + return -1; + } + + // SRTP +#ifdef HAVE_DTLS_SRTP + if (!srtp_ciphers_.empty()) { + rv = SSL_SetSRTPCiphers( + ssl_fd_, &srtp_ciphers_[0], + checked_cast(srtp_ciphers_.size())); + if (rv != SECSuccess) { + Error("BeginSSL", -1, false); + return -1; + } + } +#endif + + // Certificate validation + rv = SSL_AuthCertificateHook(ssl_fd_, AuthCertificateHook, this); + if (rv != SECSuccess) { + Error("BeginSSL", -1, false); + return -1; + } + + // Now start the handshake + rv = SSL_ResetHandshake(ssl_fd_, role_ == SSL_SERVER ? PR_TRUE : PR_FALSE); + if (rv != SECSuccess) { + Error("BeginSSL", -1, false); + return -1; + } + + return ContinueSSL(); +} + +int NSSStreamAdapter::ContinueSSL() { + LOG(LS_INFO) << "ContinueSSL"; + ASSERT(state_ == SSL_CONNECTING); + + // Clear the DTLS timer + Thread::Current()->Clear(this, MSG_DTLS_TIMEOUT); + + SECStatus rv = SSL_ForceHandshake(ssl_fd_); + + if (rv == SECSuccess) { + LOG(LS_INFO) << "Handshake complete"; + + ASSERT(cert_ok_); + if (!cert_ok_) { + Error("ContinueSSL", -1, true); + return -1; + } + + state_ = SSL_CONNECTED; + StreamAdapterInterface::OnEvent(stream(), SE_OPEN|SE_READ|SE_WRITE, 0); + return 0; + } + + PRInt32 err = PR_GetError(); + switch (err) { + case SSL_ERROR_RX_MALFORMED_HANDSHAKE: + if (ssl_mode_ != SSL_MODE_DTLS) { + Error("ContinueSSL", -1, true); + return -1; + } else { + LOG(LS_INFO) << "Malformed DTLS message. Ignoring."; + // Fall through + } + case PR_WOULD_BLOCK_ERROR: + LOG(LS_INFO) << "Would have blocked"; + if (ssl_mode_ == SSL_MODE_DTLS) { + PRIntervalTime timeout; + + SECStatus rv = DTLS_GetHandshakeTimeout(ssl_fd_, &timeout); + if (rv == SECSuccess) { + LOG(LS_INFO) << "Timeout is " << timeout << " ms"; + Thread::Current()->PostDelayed(PR_IntervalToMilliseconds(timeout), + this, MSG_DTLS_TIMEOUT, 0); + } + } + + return 0; + default: + LOG(LS_INFO) << "Error " << err; + break; + } + + Error("ContinueSSL", -1, true); + return -1; +} + +void NSSStreamAdapter::Cleanup() { + if (state_ != SSL_ERROR) { + state_ = SSL_CLOSED; + } + + if (ssl_fd_) { + PR_Close(ssl_fd_); + ssl_fd_ = NULL; + } + + identity_.reset(); + peer_certificate_.reset(); + + Thread::Current()->Clear(this, MSG_DTLS_TIMEOUT); +} + +StreamResult NSSStreamAdapter::Read(void* data, size_t data_len, + size_t* read, int* error) { + // SSL_CONNECTED sanity check. + switch (state_) { + case SSL_NONE: + case SSL_WAIT: + case SSL_CONNECTING: + return SR_BLOCK; + + case SSL_CONNECTED: + break; + + case SSL_CLOSED: + return SR_EOS; + + case SSL_ERROR: + default: + if (error) + *error = ssl_error_code_; + return SR_ERROR; + } + + PRInt32 rv = PR_Read(ssl_fd_, data, checked_cast(data_len)); + + if (rv == 0) { + return SR_EOS; + } + + // Error + if (rv < 0) { + PRInt32 err = PR_GetError(); + + switch (err) { + case PR_WOULD_BLOCK_ERROR: + return SR_BLOCK; + default: + Error("Read", -1, false); + *error = err; // libjingle semantics are that this is impl-specific + return SR_ERROR; + } + } + + // Success + *read = rv; + + return SR_SUCCESS; +} + +StreamResult NSSStreamAdapter::Write(const void* data, size_t data_len, + size_t* written, int* error) { + // SSL_CONNECTED sanity check. + switch (state_) { + case SSL_NONE: + case SSL_WAIT: + case SSL_CONNECTING: + return SR_BLOCK; + + case SSL_CONNECTED: + break; + + case SSL_ERROR: + case SSL_CLOSED: + default: + if (error) + *error = ssl_error_code_; + return SR_ERROR; + } + + PRInt32 rv = PR_Write(ssl_fd_, data, checked_cast(data_len)); + + // Error + if (rv < 0) { + PRInt32 err = PR_GetError(); + + switch (err) { + case PR_WOULD_BLOCK_ERROR: + return SR_BLOCK; + default: + Error("Write", -1, false); + *error = err; // libjingle semantics are that this is impl-specific + return SR_ERROR; + } + } + + // Success + *written = rv; + + return SR_SUCCESS; +} + +void NSSStreamAdapter::OnEvent(StreamInterface* stream, int events, + int err) { + int events_to_signal = 0; + int signal_error = 0; + ASSERT(stream == this->stream()); + if ((events & SE_OPEN)) { + LOG(LS_INFO) << "NSSStreamAdapter::OnEvent SE_OPEN"; + if (state_ != SSL_WAIT) { + ASSERT(state_ == SSL_NONE); + events_to_signal |= SE_OPEN; + } else { + state_ = SSL_CONNECTING; + if (int err = BeginSSL()) { + Error("BeginSSL", err, true); + return; + } + } + } + if ((events & (SE_READ|SE_WRITE))) { + LOG(LS_INFO) << "NSSStreamAdapter::OnEvent" + << ((events & SE_READ) ? " SE_READ" : "") + << ((events & SE_WRITE) ? " SE_WRITE" : ""); + if (state_ == SSL_NONE) { + events_to_signal |= events & (SE_READ|SE_WRITE); + } else if (state_ == SSL_CONNECTING) { + if (int err = ContinueSSL()) { + Error("ContinueSSL", err, true); + return; + } + } else if (state_ == SSL_CONNECTED) { + if (events & SE_WRITE) { + LOG(LS_INFO) << " -- onStreamWriteable"; + events_to_signal |= SE_WRITE; + } + if (events & SE_READ) { + LOG(LS_INFO) << " -- onStreamReadable"; + events_to_signal |= SE_READ; + } + } + } + if ((events & SE_CLOSE)) { + LOG(LS_INFO) << "NSSStreamAdapter::OnEvent(SE_CLOSE, " << err << ")"; + Cleanup(); + events_to_signal |= SE_CLOSE; + // SE_CLOSE is the only event that uses the final parameter to OnEvent(). + ASSERT(signal_error == 0); + signal_error = err; + } + if (events_to_signal) + StreamAdapterInterface::OnEvent(stream, events_to_signal, signal_error); +} + +void NSSStreamAdapter::OnMessage(Message* msg) { + // Process our own messages and then pass others to the superclass + if (MSG_DTLS_TIMEOUT == msg->message_id) { + LOG(LS_INFO) << "DTLS timeout expired"; + ContinueSSL(); + } else { + StreamInterface::OnMessage(msg); + } +} + +// Certificate verification callback. Called to check any certificate +SECStatus NSSStreamAdapter::AuthCertificateHook(void *arg, + PRFileDesc *fd, + PRBool checksig, + PRBool isServer) { + LOG(LS_INFO) << "NSSStreamAdapter::AuthCertificateHook"; + // SSL_PeerCertificate returns a pointer that is owned by the caller, and + // the NSSCertificate constructor copies its argument, so |raw_peer_cert| + // must be destroyed in this function. + CERTCertificate* raw_peer_cert = SSL_PeerCertificate(fd); + NSSCertificate peer_cert(raw_peer_cert); + CERT_DestroyCertificate(raw_peer_cert); + + NSSStreamAdapter *stream = reinterpret_cast(arg); + stream->cert_ok_ = false; + + // Read the peer's certificate chain. + CERTCertList* cert_list = SSL_PeerCertificateChain(fd); + ASSERT(cert_list != NULL); + + // If the peer provided multiple certificates, check that they form a valid + // chain as defined by RFC 5246 Section 7.4.2: "Each following certificate + // MUST directly certify the one preceding it.". This check does NOT + // verify other requirements, such as whether the chain reaches a trusted + // root, self-signed certificates have valid signatures, certificates are not + // expired, etc. + // Even if the chain is valid, the leaf certificate must still match a + // provided certificate or digest. + if (!NSSCertificate::IsValidChain(cert_list)) { + CERT_DestroyCertList(cert_list); + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + return SECFailure; + } + + if (stream->peer_certificate_.get()) { + LOG(LS_INFO) << "Checking against specified certificate"; + + // The peer certificate was specified + if (reinterpret_cast(stream->peer_certificate_.get())-> + Equals(&peer_cert)) { + LOG(LS_INFO) << "Accepted peer certificate"; + stream->cert_ok_ = true; + } + } else if (!stream->peer_certificate_digest_algorithm_.empty()) { + LOG(LS_INFO) << "Checking against specified digest"; + // The peer certificate digest was specified + unsigned char digest[64]; // Maximum size + size_t digest_length; + + if (!peer_cert.ComputeDigest( + stream->peer_certificate_digest_algorithm_, + digest, sizeof(digest), &digest_length)) { + LOG(LS_ERROR) << "Digest computation failed"; + } else { + Buffer computed_digest(digest, digest_length); + if (computed_digest == stream->peer_certificate_digest_value_) { + LOG(LS_INFO) << "Accepted peer certificate"; + stream->cert_ok_ = true; + } + } + } else { + // Other modes, but we haven't implemented yet + // TODO(ekr@rtfm.com): Implement real certificate validation + UNIMPLEMENTED; + } + + if (!stream->cert_ok_ && stream->ignore_bad_cert()) { + LOG(LS_WARNING) << "Ignoring cert error while verifying cert chain"; + stream->cert_ok_ = true; + } + + if (stream->cert_ok_) + stream->peer_certificate_.reset(new NSSCertificate(cert_list)); + + CERT_DestroyCertList(cert_list); + + if (stream->cert_ok_) + return SECSuccess; + + PORT_SetError(SEC_ERROR_UNTRUSTED_CERT); + return SECFailure; +} + + +SECStatus NSSStreamAdapter::GetClientAuthDataHook(void *arg, PRFileDesc *fd, + CERTDistNames *caNames, + CERTCertificate **pRetCert, + SECKEYPrivateKey **pRetKey) { + LOG(LS_INFO) << "Client cert requested"; + NSSStreamAdapter *stream = reinterpret_cast(arg); + + if (!stream->identity_.get()) { + LOG(LS_ERROR) << "No identity available"; + return SECFailure; + } + + NSSIdentity *identity = static_cast(stream->identity_.get()); + // Destroyed internally by NSS + *pRetCert = CERT_DupCertificate(identity->certificate().certificate()); + *pRetKey = SECKEY_CopyPrivateKey(identity->keypair()->privkey()); + + return SECSuccess; +} + +// RFC 5705 Key Exporter +bool NSSStreamAdapter::ExportKeyingMaterial(const std::string& label, + const uint8* context, + size_t context_len, + bool use_context, + uint8* result, + size_t result_len) { + SECStatus rv = SSL_ExportKeyingMaterial( + ssl_fd_, + label.c_str(), + checked_cast(label.size()), + use_context, + context, + checked_cast(context_len), + result, + checked_cast(result_len)); + + return rv == SECSuccess; +} + +bool NSSStreamAdapter::SetDtlsSrtpCiphers( + const std::vector& ciphers) { +#ifdef HAVE_DTLS_SRTP + std::vector internal_ciphers; + if (state_ != SSL_NONE) + return false; + + for (std::vector::const_iterator cipher = ciphers.begin(); + cipher != ciphers.end(); ++cipher) { + bool found = false; + for (const SrtpCipherMapEntry *entry = kSrtpCipherMap; entry->cipher_id; + ++entry) { + if (*cipher == entry->external_name) { + found = true; + internal_ciphers.push_back(entry->cipher_id); + break; + } + } + + if (!found) { + LOG(LS_ERROR) << "Could not find cipher: " << *cipher; + return false; + } + } + + if (internal_ciphers.empty()) + return false; + + srtp_ciphers_ = internal_ciphers; + + return true; +#else + return false; +#endif +} + +bool NSSStreamAdapter::GetDtlsSrtpCipher(std::string* cipher) { +#ifdef HAVE_DTLS_SRTP + ASSERT(state_ == SSL_CONNECTED); + if (state_ != SSL_CONNECTED) + return false; + + PRUint16 selected_cipher; + + SECStatus rv = SSL_GetSRTPCipher(ssl_fd_, &selected_cipher); + if (rv == SECFailure) + return false; + + for (const SrtpCipherMapEntry *entry = kSrtpCipherMap; + entry->cipher_id; ++entry) { + if (selected_cipher == entry->cipher_id) { + *cipher = entry->external_name; + return true; + } + } + + ASSERT(false); // This should never happen +#endif + return false; +} + + +bool NSSContext::initialized; +NSSContext *NSSContext::global_nss_context; + +// Static initialization and shutdown +NSSContext *NSSContext::Instance() { + if (!global_nss_context) { + scoped_ptr new_ctx(new NSSContext()); + new_ctx->slot_ = PK11_GetInternalSlot(); + if (new_ctx->slot_) + global_nss_context = new_ctx.release(); + } + return global_nss_context; +} + + + +bool NSSContext::InitializeSSL(VerificationCallback callback) { + ASSERT(!callback); + + if (!initialized) { + SECStatus rv; + + rv = NSS_NoDB_Init(NULL); + if (rv != SECSuccess) { + LOG(LS_ERROR) << "Couldn't initialize NSS error=" << + PORT_GetError(); + return false; + } + + NSS_SetDomesticPolicy(); + + initialized = true; + } + + return true; +} + +bool NSSContext::InitializeSSLThread() { + // Not needed + return true; +} + +bool NSSContext::CleanupSSL() { + // Not needed + return true; +} + +bool NSSStreamAdapter::HaveDtls() { + return true; +} + +bool NSSStreamAdapter::HaveDtlsSrtp() { +#ifdef HAVE_DTLS_SRTP + return true; +#else + return false; +#endif +} + +bool NSSStreamAdapter::HaveExporter() { + return true; +} + +} // namespace rtc + +#endif // HAVE_NSS_SSL_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nssstreamadapter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nssstreamadapter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nssstreamadapter.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nssstreamadapter.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,111 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_NSSSTREAMADAPTER_H_ +#define WEBRTC_BASE_NSSSTREAMADAPTER_H_ + +#include +#include + +#include "nspr.h" +#include "nss.h" +#include "secmodt.h" + +#include "webrtc/base/buffer.h" +#include "webrtc/base/nssidentity.h" +#include "webrtc/base/ssladapter.h" +#include "webrtc/base/sslstreamadapter.h" +#include "webrtc/base/sslstreamadapterhelper.h" + +namespace rtc { + +// Singleton +class NSSContext { + public: + NSSContext() {} + ~NSSContext() { + } + + static PK11SlotInfo *GetSlot() { + return Instance() ? Instance()->slot_: NULL; + } + + static NSSContext *Instance(); + static bool InitializeSSL(VerificationCallback callback); + static bool InitializeSSLThread(); + static bool CleanupSSL(); + + private: + PK11SlotInfo *slot_; // The PKCS-11 slot + static bool initialized; // Was this initialized? + static NSSContext *global_nss_context; // The global context +}; + + +class NSSStreamAdapter : public SSLStreamAdapterHelper { + public: + explicit NSSStreamAdapter(StreamInterface* stream); + virtual ~NSSStreamAdapter(); + bool Init(); + + virtual StreamResult Read(void* data, size_t data_len, + size_t* read, int* error); + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + void OnMessage(Message *msg); + + // Key Extractor interface + virtual bool ExportKeyingMaterial(const std::string& label, + const uint8* context, + size_t context_len, + bool use_context, + uint8* result, + size_t result_len); + + // DTLS-SRTP interface + virtual bool SetDtlsSrtpCiphers(const std::vector& ciphers); + virtual bool GetDtlsSrtpCipher(std::string* cipher); + + // Capabilities interfaces + static bool HaveDtls(); + static bool HaveDtlsSrtp(); + static bool HaveExporter(); + + protected: + // Override SSLStreamAdapter + virtual void OnEvent(StreamInterface* stream, int events, int err); + + // Override SSLStreamAdapterHelper + virtual int BeginSSL(); + virtual void Cleanup(); + virtual bool GetDigestLength(const std::string& algorithm, size_t* length) { + return NSSCertificate::GetDigestLength(algorithm, length); + } + + private: + int ContinueSSL(); + static SECStatus AuthCertificateHook(void *arg, PRFileDesc *fd, + PRBool checksig, PRBool isServer); + static SECStatus GetClientAuthDataHook(void *arg, PRFileDesc *fd, + CERTDistNames *caNames, + CERTCertificate **pRetCert, + SECKEYPrivateKey **pRetKey); + + PRFileDesc *ssl_fd_; // NSS's SSL file descriptor + static bool initialized; // Was InitializeSSL() called? + bool cert_ok_; // Did we get and check a cert + std::vector srtp_ciphers_; // SRTP cipher list + + static PRDescIdentity nspr_layer_identity; // The NSPR layer identity +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_NSSSTREAMADAPTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nullsocketserver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nullsocketserver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nullsocketserver.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nullsocketserver.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,61 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_NULLSOCKETSERVER_H_ +#define WEBRTC_BASE_NULLSOCKETSERVER_H_ + +#include "webrtc/base/event.h" +#include "webrtc/base/physicalsocketserver.h" + +namespace rtc { + +// NullSocketServer + +class NullSocketServer : public rtc::SocketServer { + public: + NullSocketServer() : event_(false, false) {} + + virtual bool Wait(int cms, bool process_io) { + event_.Wait(cms); + return true; + } + + virtual void WakeUp() { + event_.Set(); + } + + virtual rtc::Socket* CreateSocket(int type) { + ASSERT(false); + return NULL; + } + + virtual rtc::Socket* CreateSocket(int family, int type) { + ASSERT(false); + return NULL; + } + + virtual rtc::AsyncSocket* CreateAsyncSocket(int type) { + ASSERT(false); + return NULL; + } + + virtual rtc::AsyncSocket* CreateAsyncSocket(int family, int type) { + ASSERT(false); + return NULL; + } + + + private: + rtc::Event event_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_NULLSOCKETSERVER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nullsocketserver_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nullsocketserver_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/nullsocketserver_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/nullsocketserver_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,48 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/nullsocketserver.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace rtc { + +static const uint32 kTimeout = 5000U; + +class NullSocketServerTest + : public testing::Test, + public MessageHandler { + public: + NullSocketServerTest() {} + protected: + virtual void OnMessage(Message* message) { + ss_.WakeUp(); + } + NullSocketServer ss_; +}; + +TEST_F(NullSocketServerTest, WaitAndSet) { + Thread thread; + EXPECT_TRUE(thread.Start()); + thread.Post(this, 0); + // The process_io will be ignored. + const bool process_io = true; + EXPECT_TRUE_WAIT(ss_.Wait(rtc::kForever, process_io), kTimeout); +} + +TEST_F(NullSocketServerTest, TestWait) { + uint32 start = Time(); + ss_.Wait(200, true); + // The actual wait time is dependent on the resolution of the timer used by + // the Event class. Allow for the event to signal ~20ms early. + EXPECT_GE(TimeSince(start), 180); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/openssladapter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/openssladapter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/openssladapter.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/openssladapter.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,890 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if HAVE_OPENSSL_SSL_H + +#include "webrtc/base/openssladapter.h" + +#if defined(WEBRTC_POSIX) +#include +#endif + +// Must be included first before openssl headers. +#include "webrtc/base/win32.h" // NOLINT + +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/openssl.h" +#include "webrtc/base/safe_conversions.h" +#include "webrtc/base/sslroots.h" +#include "webrtc/base/stringutils.h" + +// TODO: Use a nicer abstraction for mutex. + +#if defined(WEBRTC_WIN) + #define MUTEX_TYPE HANDLE + #define MUTEX_SETUP(x) (x) = CreateMutex(NULL, FALSE, NULL) + #define MUTEX_CLEANUP(x) CloseHandle(x) + #define MUTEX_LOCK(x) WaitForSingleObject((x), INFINITE) + #define MUTEX_UNLOCK(x) ReleaseMutex(x) + #define THREAD_ID GetCurrentThreadId() +#elif defined(WEBRTC_POSIX) + #define MUTEX_TYPE pthread_mutex_t + #define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL) + #define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x)) + #define MUTEX_LOCK(x) pthread_mutex_lock(&(x)) + #define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x)) + #define THREAD_ID pthread_self() +#else + #error You must define mutex operations appropriate for your platform! +#endif + +struct CRYPTO_dynlock_value { + MUTEX_TYPE mutex; +}; + +////////////////////////////////////////////////////////////////////// +// SocketBIO +////////////////////////////////////////////////////////////////////// + +static int socket_write(BIO* h, const char* buf, int num); +static int socket_read(BIO* h, char* buf, int size); +static int socket_puts(BIO* h, const char* str); +static long socket_ctrl(BIO* h, int cmd, long arg1, void* arg2); +static int socket_new(BIO* h); +static int socket_free(BIO* data); + +static BIO_METHOD methods_socket = { + BIO_TYPE_BIO, + "socket", + socket_write, + socket_read, + socket_puts, + 0, + socket_ctrl, + socket_new, + socket_free, + NULL, +}; + +static BIO_METHOD* BIO_s_socket2() { return(&methods_socket); } + +static BIO* BIO_new_socket(rtc::AsyncSocket* socket) { + BIO* ret = BIO_new(BIO_s_socket2()); + if (ret == NULL) { + return NULL; + } + ret->ptr = socket; + return ret; +} + +static int socket_new(BIO* b) { + b->shutdown = 0; + b->init = 1; + b->num = 0; // 1 means socket closed + b->ptr = 0; + return 1; +} + +static int socket_free(BIO* b) { + if (b == NULL) + return 0; + return 1; +} + +static int socket_read(BIO* b, char* out, int outl) { + if (!out) + return -1; + rtc::AsyncSocket* socket = static_cast(b->ptr); + BIO_clear_retry_flags(b); + int result = socket->Recv(out, outl); + if (result > 0) { + return result; + } else if (result == 0) { + b->num = 1; + } else if (socket->IsBlocking()) { + BIO_set_retry_read(b); + } + return -1; +} + +static int socket_write(BIO* b, const char* in, int inl) { + if (!in) + return -1; + rtc::AsyncSocket* socket = static_cast(b->ptr); + BIO_clear_retry_flags(b); + int result = socket->Send(in, inl); + if (result > 0) { + return result; + } else if (socket->IsBlocking()) { + BIO_set_retry_write(b); + } + return -1; +} + +static int socket_puts(BIO* b, const char* str) { + return socket_write(b, str, rtc::checked_cast(strlen(str))); +} + +static long socket_ctrl(BIO* b, int cmd, long num, void* ptr) { + RTC_UNUSED(num); + RTC_UNUSED(ptr); + + switch (cmd) { + case BIO_CTRL_RESET: + return 0; + case BIO_CTRL_EOF: + return b->num; + case BIO_CTRL_WPENDING: + case BIO_CTRL_PENDING: + return 0; + case BIO_CTRL_FLUSH: + return 1; + default: + return 0; + } +} + +///////////////////////////////////////////////////////////////////////////// +// OpenSSLAdapter +///////////////////////////////////////////////////////////////////////////// + +namespace rtc { + +// This array will store all of the mutexes available to OpenSSL. +static MUTEX_TYPE* mutex_buf = NULL; + +static void locking_function(int mode, int n, const char * file, int line) { + if (mode & CRYPTO_LOCK) { + MUTEX_LOCK(mutex_buf[n]); + } else { + MUTEX_UNLOCK(mutex_buf[n]); + } +} + +static unsigned long id_function() { // NOLINT + // Use old-style C cast because THREAD_ID's type varies with the platform, + // in some cases requiring static_cast, and in others requiring + // reinterpret_cast. + return (unsigned long)THREAD_ID; // NOLINT +} + +static CRYPTO_dynlock_value* dyn_create_function(const char* file, int line) { + CRYPTO_dynlock_value* value = new CRYPTO_dynlock_value; + if (!value) + return NULL; + MUTEX_SETUP(value->mutex); + return value; +} + +static void dyn_lock_function(int mode, CRYPTO_dynlock_value* l, + const char* file, int line) { + if (mode & CRYPTO_LOCK) { + MUTEX_LOCK(l->mutex); + } else { + MUTEX_UNLOCK(l->mutex); + } +} + +static void dyn_destroy_function(CRYPTO_dynlock_value* l, + const char* file, int line) { + MUTEX_CLEANUP(l->mutex); + delete l; +} + +VerificationCallback OpenSSLAdapter::custom_verify_callback_ = NULL; + +bool OpenSSLAdapter::InitializeSSL(VerificationCallback callback) { + if (!InitializeSSLThread() || !SSL_library_init()) + return false; +#if !defined(ADDRESS_SANITIZER) || !defined(WEBRTC_MAC) || defined(WEBRTC_IOS) + // Loading the error strings crashes mac_asan. Omit this debugging aid there. + SSL_load_error_strings(); +#endif + ERR_load_BIO_strings(); + OpenSSL_add_all_algorithms(); + RAND_poll(); + custom_verify_callback_ = callback; + return true; +} + +bool OpenSSLAdapter::InitializeSSLThread() { + mutex_buf = new MUTEX_TYPE[CRYPTO_num_locks()]; + if (!mutex_buf) + return false; + for (int i = 0; i < CRYPTO_num_locks(); ++i) + MUTEX_SETUP(mutex_buf[i]); + + // we need to cast our id_function to return an unsigned long -- pthread_t is + // a pointer + CRYPTO_set_id_callback(id_function); + CRYPTO_set_locking_callback(locking_function); + CRYPTO_set_dynlock_create_callback(dyn_create_function); + CRYPTO_set_dynlock_lock_callback(dyn_lock_function); + CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function); + return true; +} + +bool OpenSSLAdapter::CleanupSSL() { + if (!mutex_buf) + return false; + CRYPTO_set_id_callback(NULL); + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_dynlock_create_callback(NULL); + CRYPTO_set_dynlock_lock_callback(NULL); + CRYPTO_set_dynlock_destroy_callback(NULL); + for (int i = 0; i < CRYPTO_num_locks(); ++i) + MUTEX_CLEANUP(mutex_buf[i]); + delete [] mutex_buf; + mutex_buf = NULL; + return true; +} + +OpenSSLAdapter::OpenSSLAdapter(AsyncSocket* socket) + : SSLAdapter(socket), + state_(SSL_NONE), + ssl_read_needs_write_(false), + ssl_write_needs_read_(false), + restartable_(false), + ssl_(NULL), ssl_ctx_(NULL), + custom_verification_succeeded_(false) { +} + +OpenSSLAdapter::~OpenSSLAdapter() { + Cleanup(); +} + +int +OpenSSLAdapter::StartSSL(const char* hostname, bool restartable) { + if (state_ != SSL_NONE) + return -1; + + ssl_host_name_ = hostname; + restartable_ = restartable; + + if (socket_->GetState() != Socket::CS_CONNECTED) { + state_ = SSL_WAIT; + return 0; + } + + state_ = SSL_CONNECTING; + if (int err = BeginSSL()) { + Error("BeginSSL", err, false); + return err; + } + + return 0; +} + +int +OpenSSLAdapter::BeginSSL() { + LOG(LS_INFO) << "BeginSSL: " << ssl_host_name_; + ASSERT(state_ == SSL_CONNECTING); + + int err = 0; + BIO* bio = NULL; + + // First set up the context + if (!ssl_ctx_) + ssl_ctx_ = SetupSSLContext(); + + if (!ssl_ctx_) { + err = -1; + goto ssl_error; + } + + bio = BIO_new_socket(static_cast(socket_)); + if (!bio) { + err = -1; + goto ssl_error; + } + + ssl_ = SSL_new(ssl_ctx_); + if (!ssl_) { + err = -1; + goto ssl_error; + } + + SSL_set_app_data(ssl_, this); + + SSL_set_bio(ssl_, bio, bio); + SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE | + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + // the SSL object owns the bio now + bio = NULL; + + // Do the connect + err = ContinueSSL(); + if (err != 0) + goto ssl_error; + + return err; + +ssl_error: + Cleanup(); + if (bio) + BIO_free(bio); + + return err; +} + +int +OpenSSLAdapter::ContinueSSL() { + ASSERT(state_ == SSL_CONNECTING); + + int code = SSL_connect(ssl_); + switch (SSL_get_error(ssl_, code)) { + case SSL_ERROR_NONE: + if (!SSLPostConnectionCheck(ssl_, ssl_host_name_.c_str())) { + LOG(LS_ERROR) << "TLS post connection check failed"; + // make sure we close the socket + Cleanup(); + // The connect failed so return -1 to shut down the socket + return -1; + } + + state_ = SSL_CONNECTED; + AsyncSocketAdapter::OnConnectEvent(this); +#if 0 // TODO: worry about this + // Don't let ourselves go away during the callbacks + PRefPtr lock(this); + LOG(LS_INFO) << " -- onStreamReadable"; + AsyncSocketAdapter::OnReadEvent(this); + LOG(LS_INFO) << " -- onStreamWriteable"; + AsyncSocketAdapter::OnWriteEvent(this); +#endif + break; + + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + break; + + case SSL_ERROR_ZERO_RETURN: + default: + LOG(LS_WARNING) << "ContinueSSL -- error " << code; + return (code != 0) ? code : -1; + } + + return 0; +} + +void +OpenSSLAdapter::Error(const char* context, int err, bool signal) { + LOG(LS_WARNING) << "OpenSSLAdapter::Error(" + << context << ", " << err << ")"; + state_ = SSL_ERROR; + SetError(err); + if (signal) + AsyncSocketAdapter::OnCloseEvent(this, err); +} + +void +OpenSSLAdapter::Cleanup() { + LOG(LS_INFO) << "Cleanup"; + + state_ = SSL_NONE; + ssl_read_needs_write_ = false; + ssl_write_needs_read_ = false; + custom_verification_succeeded_ = false; + + if (ssl_) { + SSL_free(ssl_); + ssl_ = NULL; + } + + if (ssl_ctx_) { + SSL_CTX_free(ssl_ctx_); + ssl_ctx_ = NULL; + } +} + +// +// AsyncSocket Implementation +// + +int +OpenSSLAdapter::Send(const void* pv, size_t cb) { + //LOG(LS_INFO) << "OpenSSLAdapter::Send(" << cb << ")"; + + switch (state_) { + case SSL_NONE: + return AsyncSocketAdapter::Send(pv, cb); + + case SSL_WAIT: + case SSL_CONNECTING: + SetError(EWOULDBLOCK); + return SOCKET_ERROR; + + case SSL_CONNECTED: + break; + + case SSL_ERROR: + default: + return SOCKET_ERROR; + } + + // OpenSSL will return an error if we try to write zero bytes + if (cb == 0) + return 0; + + ssl_write_needs_read_ = false; + + int code = SSL_write(ssl_, pv, checked_cast(cb)); + switch (SSL_get_error(ssl_, code)) { + case SSL_ERROR_NONE: + //LOG(LS_INFO) << " -- success"; + return code; + case SSL_ERROR_WANT_READ: + //LOG(LS_INFO) << " -- error want read"; + ssl_write_needs_read_ = true; + SetError(EWOULDBLOCK); + break; + case SSL_ERROR_WANT_WRITE: + //LOG(LS_INFO) << " -- error want write"; + SetError(EWOULDBLOCK); + break; + case SSL_ERROR_ZERO_RETURN: + //LOG(LS_INFO) << " -- remote side closed"; + SetError(EWOULDBLOCK); + // do we need to signal closure? + break; + default: + //LOG(LS_INFO) << " -- error " << code; + Error("SSL_write", (code ? code : -1), false); + break; + } + + return SOCKET_ERROR; +} + +int +OpenSSLAdapter::Recv(void* pv, size_t cb) { + //LOG(LS_INFO) << "OpenSSLAdapter::Recv(" << cb << ")"; + switch (state_) { + + case SSL_NONE: + return AsyncSocketAdapter::Recv(pv, cb); + + case SSL_WAIT: + case SSL_CONNECTING: + SetError(EWOULDBLOCK); + return SOCKET_ERROR; + + case SSL_CONNECTED: + break; + + case SSL_ERROR: + default: + return SOCKET_ERROR; + } + + // Don't trust OpenSSL with zero byte reads + if (cb == 0) + return 0; + + ssl_read_needs_write_ = false; + + int code = SSL_read(ssl_, pv, checked_cast(cb)); + switch (SSL_get_error(ssl_, code)) { + case SSL_ERROR_NONE: + //LOG(LS_INFO) << " -- success"; + return code; + case SSL_ERROR_WANT_READ: + //LOG(LS_INFO) << " -- error want read"; + SetError(EWOULDBLOCK); + break; + case SSL_ERROR_WANT_WRITE: + //LOG(LS_INFO) << " -- error want write"; + ssl_read_needs_write_ = true; + SetError(EWOULDBLOCK); + break; + case SSL_ERROR_ZERO_RETURN: + //LOG(LS_INFO) << " -- remote side closed"; + SetError(EWOULDBLOCK); + // do we need to signal closure? + break; + default: + //LOG(LS_INFO) << " -- error " << code; + Error("SSL_read", (code ? code : -1), false); + break; + } + + return SOCKET_ERROR; +} + +int +OpenSSLAdapter::Close() { + Cleanup(); + state_ = restartable_ ? SSL_WAIT : SSL_NONE; + return AsyncSocketAdapter::Close(); +} + +Socket::ConnState +OpenSSLAdapter::GetState() const { + //if (signal_close_) + // return CS_CONNECTED; + ConnState state = socket_->GetState(); + if ((state == CS_CONNECTED) + && ((state_ == SSL_WAIT) || (state_ == SSL_CONNECTING))) + state = CS_CONNECTING; + return state; +} + +void +OpenSSLAdapter::OnConnectEvent(AsyncSocket* socket) { + LOG(LS_INFO) << "OpenSSLAdapter::OnConnectEvent"; + if (state_ != SSL_WAIT) { + ASSERT(state_ == SSL_NONE); + AsyncSocketAdapter::OnConnectEvent(socket); + return; + } + + state_ = SSL_CONNECTING; + if (int err = BeginSSL()) { + AsyncSocketAdapter::OnCloseEvent(socket, err); + } +} + +void +OpenSSLAdapter::OnReadEvent(AsyncSocket* socket) { + //LOG(LS_INFO) << "OpenSSLAdapter::OnReadEvent"; + + if (state_ == SSL_NONE) { + AsyncSocketAdapter::OnReadEvent(socket); + return; + } + + if (state_ == SSL_CONNECTING) { + if (int err = ContinueSSL()) { + Error("ContinueSSL", err); + } + return; + } + + if (state_ != SSL_CONNECTED) + return; + + // Don't let ourselves go away during the callbacks + //PRefPtr lock(this); // TODO: fix this + if (ssl_write_needs_read_) { + //LOG(LS_INFO) << " -- onStreamWriteable"; + AsyncSocketAdapter::OnWriteEvent(socket); + } + + //LOG(LS_INFO) << " -- onStreamReadable"; + AsyncSocketAdapter::OnReadEvent(socket); +} + +void +OpenSSLAdapter::OnWriteEvent(AsyncSocket* socket) { + //LOG(LS_INFO) << "OpenSSLAdapter::OnWriteEvent"; + + if (state_ == SSL_NONE) { + AsyncSocketAdapter::OnWriteEvent(socket); + return; + } + + if (state_ == SSL_CONNECTING) { + if (int err = ContinueSSL()) { + Error("ContinueSSL", err); + } + return; + } + + if (state_ != SSL_CONNECTED) + return; + + // Don't let ourselves go away during the callbacks + //PRefPtr lock(this); // TODO: fix this + + if (ssl_read_needs_write_) { + //LOG(LS_INFO) << " -- onStreamReadable"; + AsyncSocketAdapter::OnReadEvent(socket); + } + + //LOG(LS_INFO) << " -- onStreamWriteable"; + AsyncSocketAdapter::OnWriteEvent(socket); +} + +void +OpenSSLAdapter::OnCloseEvent(AsyncSocket* socket, int err) { + LOG(LS_INFO) << "OpenSSLAdapter::OnCloseEvent(" << err << ")"; + AsyncSocketAdapter::OnCloseEvent(socket, err); +} + +// This code is taken from the "Network Security with OpenSSL" +// sample in chapter 5 + +bool OpenSSLAdapter::VerifyServerName(SSL* ssl, const char* host, + bool ignore_bad_cert) { + if (!host) + return false; + + // Checking the return from SSL_get_peer_certificate here is not strictly + // necessary. With our setup, it is not possible for it to return + // NULL. However, it is good form to check the return. + X509* certificate = SSL_get_peer_certificate(ssl); + if (!certificate) + return false; + + // Logging certificates is extremely verbose. So it is disabled by default. +#ifdef LOG_CERTIFICATES + { + LOG(LS_INFO) << "Certificate from server:"; + BIO* mem = BIO_new(BIO_s_mem()); + X509_print_ex(mem, certificate, XN_FLAG_SEP_CPLUS_SPC, X509_FLAG_NO_HEADER); + BIO_write(mem, "\0", 1); + char* buffer; + BIO_get_mem_data(mem, &buffer); + LOG(LS_INFO) << buffer; + BIO_free(mem); + + char* cipher_description = + SSL_CIPHER_description(SSL_get_current_cipher(ssl), NULL, 128); + LOG(LS_INFO) << "Cipher: " << cipher_description; + OPENSSL_free(cipher_description); + } +#endif + + bool ok = false; + int extension_count = X509_get_ext_count(certificate); + for (int i = 0; i < extension_count; ++i) { + X509_EXTENSION* extension = X509_get_ext(certificate, i); + int extension_nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension)); + + if (extension_nid == NID_subject_alt_name) { + const X509V3_EXT_METHOD* meth = X509V3_EXT_get(extension); + if (!meth) + break; + + void* ext_str = NULL; + + // We assign this to a local variable, instead of passing the address + // directly to ASN1_item_d2i. + // See http://readlist.com/lists/openssl.org/openssl-users/0/4761.html. + unsigned char* ext_value_data = extension->value->data; + + const unsigned char **ext_value_data_ptr = + (const_cast(&ext_value_data)); + + if (meth->it) { + ext_str = ASN1_item_d2i(NULL, ext_value_data_ptr, + extension->value->length, + ASN1_ITEM_ptr(meth->it)); + } else { + ext_str = meth->d2i(NULL, ext_value_data_ptr, extension->value->length); + } + + STACK_OF(CONF_VALUE)* value = meth->i2v(meth, ext_str, NULL); + + // Cast to size_t to be compilable for both OpenSSL and BoringSSL. + for (size_t j = 0; j < static_cast(sk_CONF_VALUE_num(value)); + ++j) { + CONF_VALUE* nval = sk_CONF_VALUE_value(value, j); + // The value for nval can contain wildcards + if (!strcmp(nval->name, "DNS") && string_match(host, nval->value)) { + ok = true; + break; + } + } + sk_CONF_VALUE_pop_free(value, X509V3_conf_free); + value = NULL; + + if (meth->it) { + ASN1_item_free(reinterpret_cast(ext_str), + ASN1_ITEM_ptr(meth->it)); + } else { + meth->ext_free(ext_str); + } + ext_str = NULL; + } + if (ok) + break; + } + + char data[256]; + X509_NAME* subject; + if (!ok + && ((subject = X509_get_subject_name(certificate)) != NULL) + && (X509_NAME_get_text_by_NID(subject, NID_commonName, + data, sizeof(data)) > 0)) { + data[sizeof(data)-1] = 0; + if (_stricmp(data, host) == 0) + ok = true; + } + + X509_free(certificate); + + // This should only ever be turned on for debugging and development. + if (!ok && ignore_bad_cert) { + LOG(LS_WARNING) << "TLS certificate check FAILED. " + << "Allowing connection anyway."; + ok = true; + } + + return ok; +} + +bool OpenSSLAdapter::SSLPostConnectionCheck(SSL* ssl, const char* host) { + bool ok = VerifyServerName(ssl, host, ignore_bad_cert()); + + if (ok) { + ok = (SSL_get_verify_result(ssl) == X509_V_OK || + custom_verification_succeeded_); + } + + if (!ok && ignore_bad_cert()) { + LOG(LS_INFO) << "Other TLS post connection checks failed."; + ok = true; + } + + return ok; +} + +#if _DEBUG + +// We only use this for tracing and so it is only needed in debug mode + +void +OpenSSLAdapter::SSLInfoCallback(const SSL* s, int where, int ret) { + const char* str = "undefined"; + int w = where & ~SSL_ST_MASK; + if (w & SSL_ST_CONNECT) { + str = "SSL_connect"; + } else if (w & SSL_ST_ACCEPT) { + str = "SSL_accept"; + } + if (where & SSL_CB_LOOP) { + LOG(LS_INFO) << str << ":" << SSL_state_string_long(s); + } else if (where & SSL_CB_ALERT) { + str = (where & SSL_CB_READ) ? "read" : "write"; + LOG(LS_INFO) << "SSL3 alert " << str + << ":" << SSL_alert_type_string_long(ret) + << ":" << SSL_alert_desc_string_long(ret); + } else if (where & SSL_CB_EXIT) { + if (ret == 0) { + LOG(LS_INFO) << str << ":failed in " << SSL_state_string_long(s); + } else if (ret < 0) { + LOG(LS_INFO) << str << ":error in " << SSL_state_string_long(s); + } + } +} + +#endif // _DEBUG + +int +OpenSSLAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) { +#if _DEBUG + if (!ok) { + char data[256]; + X509* cert = X509_STORE_CTX_get_current_cert(store); + int depth = X509_STORE_CTX_get_error_depth(store); + int err = X509_STORE_CTX_get_error(store); + + LOG(LS_INFO) << "Error with certificate at depth: " << depth; + X509_NAME_oneline(X509_get_issuer_name(cert), data, sizeof(data)); + LOG(LS_INFO) << " issuer = " << data; + X509_NAME_oneline(X509_get_subject_name(cert), data, sizeof(data)); + LOG(LS_INFO) << " subject = " << data; + LOG(LS_INFO) << " err = " << err + << ":" << X509_verify_cert_error_string(err); + } +#endif + + // Get our stream pointer from the store + SSL* ssl = reinterpret_cast( + X509_STORE_CTX_get_ex_data(store, + SSL_get_ex_data_X509_STORE_CTX_idx())); + + OpenSSLAdapter* stream = + reinterpret_cast(SSL_get_app_data(ssl)); + + if (!ok && custom_verify_callback_) { + void* cert = + reinterpret_cast(X509_STORE_CTX_get_current_cert(store)); + if (custom_verify_callback_(cert)) { + stream->custom_verification_succeeded_ = true; + LOG(LS_INFO) << "validated certificate using custom callback"; + ok = true; + } + } + + // Should only be used for debugging and development. + if (!ok && stream->ignore_bad_cert()) { + LOG(LS_WARNING) << "Ignoring cert error while verifying cert chain"; + ok = 1; + } + + return ok; +} + +bool OpenSSLAdapter::ConfigureTrustedRootCertificates(SSL_CTX* ctx) { + // Add the root cert that we care about to the SSL context + int count_of_added_certs = 0; + for (int i = 0; i < ARRAY_SIZE(kSSLCertCertificateList); i++) { + const unsigned char* cert_buffer = kSSLCertCertificateList[i]; + size_t cert_buffer_len = kSSLCertCertificateSizeList[i]; + X509* cert = d2i_X509(NULL, &cert_buffer, + checked_cast(cert_buffer_len)); + if (cert) { + int return_value = X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx), cert); + if (return_value == 0) { + LOG(LS_WARNING) << "Unable to add certificate."; + } else { + count_of_added_certs++; + } + X509_free(cert); + } + } + return count_of_added_certs > 0; +} + +SSL_CTX* +OpenSSLAdapter::SetupSSLContext() { + SSL_CTX* ctx = SSL_CTX_new(TLSv1_client_method()); + if (ctx == NULL) { + unsigned long error = ERR_get_error(); // NOLINT: type used by OpenSSL. + LOG(LS_WARNING) << "SSL_CTX creation failed: " + << '"' << ERR_reason_error_string(error) << "\" " + << "(error=" << error << ')'; + return NULL; + } + if (!ConfigureTrustedRootCertificates(ctx)) { + SSL_CTX_free(ctx); + return NULL; + } + +#ifdef _DEBUG + SSL_CTX_set_info_callback(ctx, SSLInfoCallback); +#endif + + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, SSLVerifyCallback); + SSL_CTX_set_verify_depth(ctx, 4); + SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); + + return ctx; +} + +} // namespace rtc + +#endif // HAVE_OPENSSL_SSL_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/openssladapter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/openssladapter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/openssladapter.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/openssladapter.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,88 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_OPENSSLADAPTER_H__ +#define WEBRTC_BASE_OPENSSLADAPTER_H__ + +#include +#include "webrtc/base/ssladapter.h" + +typedef struct ssl_st SSL; +typedef struct ssl_ctx_st SSL_CTX; +typedef struct x509_store_ctx_st X509_STORE_CTX; + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// + +class OpenSSLAdapter : public SSLAdapter { +public: + static bool InitializeSSL(VerificationCallback callback); + static bool InitializeSSLThread(); + static bool CleanupSSL(); + + OpenSSLAdapter(AsyncSocket* socket); + virtual ~OpenSSLAdapter(); + + virtual int StartSSL(const char* hostname, bool restartable); + virtual int Send(const void* pv, size_t cb); + virtual int Recv(void* pv, size_t cb); + virtual int Close(); + + // Note that the socket returns ST_CONNECTING while SSL is being negotiated. + virtual ConnState GetState() const; + +protected: + virtual void OnConnectEvent(AsyncSocket* socket); + virtual void OnReadEvent(AsyncSocket* socket); + virtual void OnWriteEvent(AsyncSocket* socket); + virtual void OnCloseEvent(AsyncSocket* socket, int err); + +private: + enum SSLState { + SSL_NONE, SSL_WAIT, SSL_CONNECTING, SSL_CONNECTED, SSL_ERROR + }; + + int BeginSSL(); + int ContinueSSL(); + void Error(const char* context, int err, bool signal = true); + void Cleanup(); + + static bool VerifyServerName(SSL* ssl, const char* host, + bool ignore_bad_cert); + bool SSLPostConnectionCheck(SSL* ssl, const char* host); +#if _DEBUG + static void SSLInfoCallback(const SSL* s, int where, int ret); +#endif // !_DEBUG + static int SSLVerifyCallback(int ok, X509_STORE_CTX* store); + static VerificationCallback custom_verify_callback_; + friend class OpenSSLStreamAdapter; // for custom_verify_callback_; + + static bool ConfigureTrustedRootCertificates(SSL_CTX* ctx); + static SSL_CTX* SetupSSLContext(); + + SSLState state_; + bool ssl_read_needs_write_; + bool ssl_write_needs_read_; + // If true, socket will retain SSL configuration after Close. + bool restartable_; + + SSL* ssl_; + SSL_CTX* ssl_ctx_; + std::string ssl_host_name_; + + bool custom_verification_succeeded_; +}; + +///////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_OPENSSLADAPTER_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/openssldigest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/openssldigest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/openssldigest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/openssldigest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,122 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if HAVE_OPENSSL_SSL_H + +#include "webrtc/base/openssldigest.h" + +#include "webrtc/base/common.h" +#include "webrtc/base/openssl.h" + +namespace rtc { + +OpenSSLDigest::OpenSSLDigest(const std::string& algorithm) { + EVP_MD_CTX_init(&ctx_); + if (GetDigestEVP(algorithm, &md_)) { + EVP_DigestInit_ex(&ctx_, md_, NULL); + } else { + md_ = NULL; + } +} + +OpenSSLDigest::~OpenSSLDigest() { + EVP_MD_CTX_cleanup(&ctx_); +} + +size_t OpenSSLDigest::Size() const { + if (!md_) { + return 0; + } + return EVP_MD_size(md_); +} + +void OpenSSLDigest::Update(const void* buf, size_t len) { + if (!md_) { + return; + } + EVP_DigestUpdate(&ctx_, buf, len); +} + +size_t OpenSSLDigest::Finish(void* buf, size_t len) { + if (!md_ || len < Size()) { + return 0; + } + unsigned int md_len; + EVP_DigestFinal_ex(&ctx_, static_cast(buf), &md_len); + EVP_DigestInit_ex(&ctx_, md_, NULL); // prepare for future Update()s + ASSERT(md_len == Size()); + return md_len; +} + +bool OpenSSLDigest::GetDigestEVP(const std::string& algorithm, + const EVP_MD** mdp) { + const EVP_MD* md; + if (algorithm == DIGEST_MD5) { + md = EVP_md5(); + } else if (algorithm == DIGEST_SHA_1) { + md = EVP_sha1(); + } else if (algorithm == DIGEST_SHA_224) { + md = EVP_sha224(); + } else if (algorithm == DIGEST_SHA_256) { + md = EVP_sha256(); + } else if (algorithm == DIGEST_SHA_384) { + md = EVP_sha384(); + } else if (algorithm == DIGEST_SHA_512) { + md = EVP_sha512(); + } else { + return false; + } + + // Can't happen + ASSERT(EVP_MD_size(md) >= 16); + *mdp = md; + return true; +} + +bool OpenSSLDigest::GetDigestName(const EVP_MD* md, + std::string* algorithm) { + ASSERT(md != NULL); + ASSERT(algorithm != NULL); + + int md_type = EVP_MD_type(md); + if (md_type == NID_md5) { + *algorithm = DIGEST_MD5; + } else if (md_type == NID_sha1) { + *algorithm = DIGEST_SHA_1; + } else if (md_type == NID_sha224) { + *algorithm = DIGEST_SHA_224; + } else if (md_type == NID_sha256) { + *algorithm = DIGEST_SHA_256; + } else if (md_type == NID_sha384) { + *algorithm = DIGEST_SHA_384; + } else if (md_type == NID_sha512) { + *algorithm = DIGEST_SHA_512; + } else { + algorithm->clear(); + return false; + } + + return true; +} + +bool OpenSSLDigest::GetDigestSize(const std::string& algorithm, + size_t* length) { + const EVP_MD *md; + if (!GetDigestEVP(algorithm, &md)) + return false; + + *length = EVP_MD_size(md); + return true; +} + +} // namespace rtc + +#endif // HAVE_OPENSSL_SSL_H + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/openssldigest.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/openssldigest.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/openssldigest.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/openssldigest.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_OPENSSLDIGEST_H_ +#define WEBRTC_BASE_OPENSSLDIGEST_H_ + +#include + +#include "webrtc/base/messagedigest.h" + +namespace rtc { + +// An implementation of the digest class that uses OpenSSL. +class OpenSSLDigest : public MessageDigest { + public: + // Creates an OpenSSLDigest with |algorithm| as the hash algorithm. + explicit OpenSSLDigest(const std::string& algorithm); + ~OpenSSLDigest(); + // Returns the digest output size (e.g. 16 bytes for MD5). + virtual size_t Size() const; + // Updates the digest with |len| bytes from |buf|. + virtual void Update(const void* buf, size_t len); + // Outputs the digest value to |buf| with length |len|. + virtual size_t Finish(void* buf, size_t len); + + // Helper function to look up a digest's EVP by name. + static bool GetDigestEVP(const std::string &algorithm, + const EVP_MD** md); + // Helper function to look up a digest's name by EVP. + static bool GetDigestName(const EVP_MD* md, + std::string* algorithm); + // Helper function to get the length of a digest. + static bool GetDigestSize(const std::string &algorithm, + size_t* len); + + private: + EVP_MD_CTX ctx_; + const EVP_MD* md_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_OPENSSLDIGEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/openssl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/openssl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/openssl.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/openssl.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright 2013 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_OPENSSL_H_ +#define WEBRTC_BASE_OPENSSL_H_ + +#include + +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) +#error OpenSSL is older than 1.0.0, which is the minimum supported version. +#endif + +#endif // WEBRTC_BASE_OPENSSL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/opensslidentity.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/opensslidentity.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/opensslidentity.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/opensslidentity.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,362 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if HAVE_OPENSSL_SSL_H + +#include "webrtc/base/opensslidentity.h" + +// Must be included first before openssl headers. +#include "webrtc/base/win32.h" // NOLINT + +#include +#include +#include +#include +#include +#include + +#include "webrtc/base/checks.h" +#include "webrtc/base/helpers.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/openssl.h" +#include "webrtc/base/openssldigest.h" + +namespace rtc { + +// We could have exposed a myriad of parameters for the crypto stuff, +// but keeping it simple seems best. + +// Strength of generated keys. Those are RSA. +static const int KEY_LENGTH = 1024; + +// Random bits for certificate serial number +static const int SERIAL_RAND_BITS = 64; + +// Certificate validity lifetime +static const int CERTIFICATE_LIFETIME = 60*60*24*30; // 30 days, arbitrarily +// Certificate validity window. +// This is to compensate for slightly incorrect system clocks. +static const int CERTIFICATE_WINDOW = -60*60*24; + +// Generate a key pair. Caller is responsible for freeing the returned object. +static EVP_PKEY* MakeKey() { + LOG(LS_INFO) << "Making key pair"; + EVP_PKEY* pkey = EVP_PKEY_new(); + // RSA_generate_key is deprecated. Use _ex version. + BIGNUM* exponent = BN_new(); + RSA* rsa = RSA_new(); + if (!pkey || !exponent || !rsa || + !BN_set_word(exponent, 0x10001) || // 65537 RSA exponent + !RSA_generate_key_ex(rsa, KEY_LENGTH, exponent, NULL) || + !EVP_PKEY_assign_RSA(pkey, rsa)) { + EVP_PKEY_free(pkey); + BN_free(exponent); + RSA_free(rsa); + return NULL; + } + // ownership of rsa struct was assigned, don't free it. + BN_free(exponent); + LOG(LS_INFO) << "Returning key pair"; + return pkey; +} + +// Generate a self-signed certificate, with the public key from the +// given key pair. Caller is responsible for freeing the returned object. +static X509* MakeCertificate(EVP_PKEY* pkey, const SSLIdentityParams& params) { + LOG(LS_INFO) << "Making certificate for " << params.common_name; + X509* x509 = NULL; + BIGNUM* serial_number = NULL; + X509_NAME* name = NULL; + + if ((x509=X509_new()) == NULL) + goto error; + + if (!X509_set_pubkey(x509, pkey)) + goto error; + + // serial number + // temporary reference to serial number inside x509 struct + ASN1_INTEGER* asn1_serial_number; + if ((serial_number = BN_new()) == NULL || + !BN_pseudo_rand(serial_number, SERIAL_RAND_BITS, 0, 0) || + (asn1_serial_number = X509_get_serialNumber(x509)) == NULL || + !BN_to_ASN1_INTEGER(serial_number, asn1_serial_number)) + goto error; + + if (!X509_set_version(x509, 0L)) // version 1 + goto error; + + // There are a lot of possible components for the name entries. In + // our P2P SSL mode however, the certificates are pre-exchanged + // (through the secure XMPP channel), and so the certificate + // identification is arbitrary. It can't be empty, so we set some + // arbitrary common_name. Note that this certificate goes out in + // clear during SSL negotiation, so there may be a privacy issue in + // putting anything recognizable here. + if ((name = X509_NAME_new()) == NULL || + !X509_NAME_add_entry_by_NID( + name, NID_commonName, MBSTRING_UTF8, + (unsigned char*)params.common_name.c_str(), -1, -1, 0) || + !X509_set_subject_name(x509, name) || + !X509_set_issuer_name(x509, name)) + goto error; + + if (!X509_gmtime_adj(X509_get_notBefore(x509), params.not_before) || + !X509_gmtime_adj(X509_get_notAfter(x509), params.not_after)) + goto error; + + if (!X509_sign(x509, pkey, EVP_sha1())) + goto error; + + BN_free(serial_number); + X509_NAME_free(name); + LOG(LS_INFO) << "Returning certificate"; + return x509; + + error: + BN_free(serial_number); + X509_NAME_free(name); + X509_free(x509); + return NULL; +} + +// This dumps the SSL error stack to the log. +static void LogSSLErrors(const std::string& prefix) { + char error_buf[200]; + unsigned long err; + + while ((err = ERR_get_error()) != 0) { + ERR_error_string_n(err, error_buf, sizeof(error_buf)); + LOG(LS_ERROR) << prefix << ": " << error_buf << "\n"; + } +} + +OpenSSLKeyPair* OpenSSLKeyPair::Generate() { + EVP_PKEY* pkey = MakeKey(); + if (!pkey) { + LogSSLErrors("Generating key pair"); + return NULL; + } + return new OpenSSLKeyPair(pkey); +} + +OpenSSLKeyPair::~OpenSSLKeyPair() { + EVP_PKEY_free(pkey_); +} + +void OpenSSLKeyPair::AddReference() { + CRYPTO_add(&pkey_->references, 1, CRYPTO_LOCK_EVP_PKEY); +} + +#ifdef _DEBUG +// Print a certificate to the log, for debugging. +static void PrintCert(X509* x509) { + BIO* temp_memory_bio = BIO_new(BIO_s_mem()); + if (!temp_memory_bio) { + LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio"; + return; + } + X509_print_ex(temp_memory_bio, x509, XN_FLAG_SEP_CPLUS_SPC, 0); + BIO_write(temp_memory_bio, "\0", 1); + char* buffer; + BIO_get_mem_data(temp_memory_bio, &buffer); + LOG(LS_VERBOSE) << buffer; + BIO_free(temp_memory_bio); +} +#endif + +OpenSSLCertificate* OpenSSLCertificate::Generate( + OpenSSLKeyPair* key_pair, const SSLIdentityParams& params) { + SSLIdentityParams actual_params(params); + if (actual_params.common_name.empty()) { + // Use a random string, arbitrarily 8chars long. + actual_params.common_name = CreateRandomString(8); + } + X509* x509 = MakeCertificate(key_pair->pkey(), actual_params); + if (!x509) { + LogSSLErrors("Generating certificate"); + return NULL; + } +#ifdef _DEBUG + PrintCert(x509); +#endif + OpenSSLCertificate* ret = new OpenSSLCertificate(x509); + X509_free(x509); + return ret; +} + +OpenSSLCertificate* OpenSSLCertificate::FromPEMString( + const std::string& pem_string) { + BIO* bio = BIO_new_mem_buf(const_cast(pem_string.c_str()), -1); + if (!bio) + return NULL; + BIO_set_mem_eof_return(bio, 0); + X509 *x509 = PEM_read_bio_X509(bio, NULL, NULL, + const_cast("\0")); + BIO_free(bio); // Frees the BIO, but not the pointed-to string. + + if (!x509) + return NULL; + + OpenSSLCertificate* ret = new OpenSSLCertificate(x509); + X509_free(x509); + return ret; +} + +// NOTE: This implementation only functions correctly after InitializeSSL +// and before CleanupSSL. +bool OpenSSLCertificate::GetSignatureDigestAlgorithm( + std::string* algorithm) const { + return OpenSSLDigest::GetDigestName( + EVP_get_digestbyobj(x509_->sig_alg->algorithm), algorithm); +} + +bool OpenSSLCertificate::ComputeDigest(const std::string& algorithm, + unsigned char* digest, + size_t size, + size_t* length) const { + return ComputeDigest(x509_, algorithm, digest, size, length); +} + +bool OpenSSLCertificate::ComputeDigest(const X509* x509, + const std::string& algorithm, + unsigned char* digest, + size_t size, + size_t* length) { + const EVP_MD *md; + unsigned int n; + + if (!OpenSSLDigest::GetDigestEVP(algorithm, &md)) + return false; + + if (size < static_cast(EVP_MD_size(md))) + return false; + + X509_digest(x509, md, digest, &n); + + *length = n; + + return true; +} + +OpenSSLCertificate::~OpenSSLCertificate() { + X509_free(x509_); +} + +std::string OpenSSLCertificate::ToPEMString() const { + BIO* bio = BIO_new(BIO_s_mem()); + if (!bio) { + FATAL() << "unreachable code"; + } + if (!PEM_write_bio_X509(bio, x509_)) { + BIO_free(bio); + FATAL() << "unreachable code"; + } + BIO_write(bio, "\0", 1); + char* buffer; + BIO_get_mem_data(bio, &buffer); + std::string ret(buffer); + BIO_free(bio); + return ret; +} + +void OpenSSLCertificate::ToDER(Buffer* der_buffer) const { + // In case of failure, make sure to leave the buffer empty. + der_buffer->SetData(NULL, 0); + + // Calculates the DER representation of the certificate, from scratch. + BIO* bio = BIO_new(BIO_s_mem()); + if (!bio) { + FATAL() << "unreachable code"; + } + if (!i2d_X509_bio(bio, x509_)) { + BIO_free(bio); + FATAL() << "unreachable code"; + } + char* data; + size_t length = BIO_get_mem_data(bio, &data); + der_buffer->SetData(data, length); + BIO_free(bio); +} + +void OpenSSLCertificate::AddReference() const { + ASSERT(x509_ != NULL); + CRYPTO_add(&x509_->references, 1, CRYPTO_LOCK_X509); +} + +OpenSSLIdentity* OpenSSLIdentity::GenerateInternal( + const SSLIdentityParams& params) { + OpenSSLKeyPair *key_pair = OpenSSLKeyPair::Generate(); + if (key_pair) { + OpenSSLCertificate *certificate = OpenSSLCertificate::Generate( + key_pair, params); + if (certificate) + return new OpenSSLIdentity(key_pair, certificate); + delete key_pair; + } + LOG(LS_INFO) << "Identity generation failed"; + return NULL; +} + +OpenSSLIdentity* OpenSSLIdentity::Generate(const std::string& common_name) { + SSLIdentityParams params; + params.common_name = common_name; + params.not_before = CERTIFICATE_WINDOW; + params.not_after = CERTIFICATE_LIFETIME; + return GenerateInternal(params); +} + +OpenSSLIdentity* OpenSSLIdentity::GenerateForTest( + const SSLIdentityParams& params) { + return GenerateInternal(params); +} + +SSLIdentity* OpenSSLIdentity::FromPEMStrings( + const std::string& private_key, + const std::string& certificate) { + scoped_ptr cert( + OpenSSLCertificate::FromPEMString(certificate)); + if (!cert) { + LOG(LS_ERROR) << "Failed to create OpenSSLCertificate from PEM string."; + return NULL; + } + + BIO* bio = BIO_new_mem_buf(const_cast(private_key.c_str()), -1); + if (!bio) { + LOG(LS_ERROR) << "Failed to create a new BIO buffer."; + return NULL; + } + BIO_set_mem_eof_return(bio, 0); + EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, + const_cast("\0")); + BIO_free(bio); // Frees the BIO, but not the pointed-to string. + + if (!pkey) { + LOG(LS_ERROR) << "Failed to create the private key from PEM string."; + return NULL; + } + + return new OpenSSLIdentity(new OpenSSLKeyPair(pkey), + cert.release()); +} + +bool OpenSSLIdentity::ConfigureIdentity(SSL_CTX* ctx) { + // 1 is the documented success return code. + if (SSL_CTX_use_certificate(ctx, certificate_->x509()) != 1 || + SSL_CTX_use_PrivateKey(ctx, key_pair_->pkey()) != 1) { + LogSSLErrors("Configuring key and certificate"); + return false; + } + return true; +} + +} // namespace rtc + +#endif // HAVE_OPENSSL_SSL_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/opensslidentity.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/opensslidentity.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/opensslidentity.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/opensslidentity.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,150 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_OPENSSLIDENTITY_H_ +#define WEBRTC_BASE_OPENSSLIDENTITY_H_ + +#include +#include + +#include + +#include "webrtc/base/common.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/sslidentity.h" + +typedef struct ssl_ctx_st SSL_CTX; + +namespace rtc { + +// OpenSSLKeyPair encapsulates an OpenSSL EVP_PKEY* keypair object, +// which is reference counted inside the OpenSSL library. +class OpenSSLKeyPair { + public: + explicit OpenSSLKeyPair(EVP_PKEY* pkey) : pkey_(pkey) { + ASSERT(pkey_ != NULL); + } + + static OpenSSLKeyPair* Generate(); + + virtual ~OpenSSLKeyPair(); + + virtual OpenSSLKeyPair* GetReference() { + AddReference(); + return new OpenSSLKeyPair(pkey_); + } + + EVP_PKEY* pkey() const { return pkey_; } + + private: + void AddReference(); + + EVP_PKEY* pkey_; + + DISALLOW_EVIL_CONSTRUCTORS(OpenSSLKeyPair); +}; + +// OpenSSLCertificate encapsulates an OpenSSL X509* certificate object, +// which is also reference counted inside the OpenSSL library. +class OpenSSLCertificate : public SSLCertificate { + public: + // Caller retains ownership of the X509 object. + explicit OpenSSLCertificate(X509* x509) : x509_(x509) { + AddReference(); + } + + static OpenSSLCertificate* Generate(OpenSSLKeyPair* key_pair, + const SSLIdentityParams& params); + static OpenSSLCertificate* FromPEMString(const std::string& pem_string); + + virtual ~OpenSSLCertificate(); + + virtual OpenSSLCertificate* GetReference() const { + return new OpenSSLCertificate(x509_); + } + + X509* x509() const { return x509_; } + + virtual std::string ToPEMString() const; + + virtual void ToDER(Buffer* der_buffer) const; + + // Compute the digest of the certificate given algorithm + virtual bool ComputeDigest(const std::string& algorithm, + unsigned char* digest, + size_t size, + size_t* length) const; + + // Compute the digest of a certificate as an X509 * + static bool ComputeDigest(const X509* x509, + const std::string& algorithm, + unsigned char* digest, + size_t size, + size_t* length); + + virtual bool GetSignatureDigestAlgorithm(std::string* algorithm) const; + + virtual bool GetChain(SSLCertChain** chain) const { + // Chains are not yet supported when using OpenSSL. + // OpenSSLStreamAdapter::SSLVerifyCallback currently requires the remote + // certificate to be self-signed. + return false; + } + + private: + void AddReference() const; + + X509* x509_; + + DISALLOW_EVIL_CONSTRUCTORS(OpenSSLCertificate); +}; + +// Holds a keypair and certificate together, and a method to generate +// them consistently. +class OpenSSLIdentity : public SSLIdentity { + public: + static OpenSSLIdentity* Generate(const std::string& common_name); + static OpenSSLIdentity* GenerateForTest(const SSLIdentityParams& params); + static SSLIdentity* FromPEMStrings(const std::string& private_key, + const std::string& certificate); + virtual ~OpenSSLIdentity() { } + + virtual const OpenSSLCertificate& certificate() const { + return *certificate_; + } + + virtual OpenSSLIdentity* GetReference() const { + return new OpenSSLIdentity(key_pair_->GetReference(), + certificate_->GetReference()); + } + + // Configure an SSL context object to use our key and certificate. + bool ConfigureIdentity(SSL_CTX* ctx); + + private: + OpenSSLIdentity(OpenSSLKeyPair* key_pair, + OpenSSLCertificate* certificate) + : key_pair_(key_pair), certificate_(certificate) { + ASSERT(key_pair != NULL); + ASSERT(certificate != NULL); + } + + static OpenSSLIdentity* GenerateInternal(const SSLIdentityParams& params); + + scoped_ptr key_pair_; + scoped_ptr certificate_; + + DISALLOW_EVIL_CONSTRUCTORS(OpenSSLIdentity); +}; + + +} // namespace rtc + +#endif // WEBRTC_BASE_OPENSSLIDENTITY_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/opensslstreamadapter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/opensslstreamadapter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/opensslstreamadapter.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/opensslstreamadapter.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,881 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#if HAVE_OPENSSL_SSL_H + +#include "webrtc/base/opensslstreamadapter.h" + +#include +#include +#include +#include +#include + +#include + +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/safe_conversions.h" +#include "webrtc/base/stream.h" +#include "webrtc/base/openssl.h" +#include "webrtc/base/openssladapter.h" +#include "webrtc/base/openssldigest.h" +#include "webrtc/base/opensslidentity.h" +#include "webrtc/base/stringutils.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +#if (OPENSSL_VERSION_NUMBER >= 0x10001000L) +#define HAVE_DTLS_SRTP +#endif + +#ifdef HAVE_DTLS_SRTP +// SRTP cipher suite table +struct SrtpCipherMapEntry { + const char* external_name; + const char* internal_name; +}; + +// This isn't elegant, but it's better than an external reference +static SrtpCipherMapEntry SrtpCipherMap[] = { + {"AES_CM_128_HMAC_SHA1_80", "SRTP_AES128_CM_SHA1_80"}, + {"AES_CM_128_HMAC_SHA1_32", "SRTP_AES128_CM_SHA1_32"}, + {NULL, NULL} +}; +#endif + +////////////////////////////////////////////////////////////////////// +// StreamBIO +////////////////////////////////////////////////////////////////////// + +static int stream_write(BIO* h, const char* buf, int num); +static int stream_read(BIO* h, char* buf, int size); +static int stream_puts(BIO* h, const char* str); +static long stream_ctrl(BIO* h, int cmd, long arg1, void* arg2); +static int stream_new(BIO* h); +static int stream_free(BIO* data); + +static BIO_METHOD methods_stream = { + BIO_TYPE_BIO, + "stream", + stream_write, + stream_read, + stream_puts, + 0, + stream_ctrl, + stream_new, + stream_free, + NULL, +}; + +static BIO_METHOD* BIO_s_stream() { return(&methods_stream); } + +static BIO* BIO_new_stream(StreamInterface* stream) { + BIO* ret = BIO_new(BIO_s_stream()); + if (ret == NULL) + return NULL; + ret->ptr = stream; + return ret; +} + +// bio methods return 1 (or at least non-zero) on success and 0 on failure. + +static int stream_new(BIO* b) { + b->shutdown = 0; + b->init = 1; + b->num = 0; // 1 means end-of-stream + b->ptr = 0; + return 1; +} + +static int stream_free(BIO* b) { + if (b == NULL) + return 0; + return 1; +} + +static int stream_read(BIO* b, char* out, int outl) { + if (!out) + return -1; + StreamInterface* stream = static_cast(b->ptr); + BIO_clear_retry_flags(b); + size_t read; + int error; + StreamResult result = stream->Read(out, outl, &read, &error); + if (result == SR_SUCCESS) { + return checked_cast(read); + } else if (result == SR_EOS) { + b->num = 1; + } else if (result == SR_BLOCK) { + BIO_set_retry_read(b); + } + return -1; +} + +static int stream_write(BIO* b, const char* in, int inl) { + if (!in) + return -1; + StreamInterface* stream = static_cast(b->ptr); + BIO_clear_retry_flags(b); + size_t written; + int error; + StreamResult result = stream->Write(in, inl, &written, &error); + if (result == SR_SUCCESS) { + return checked_cast(written); + } else if (result == SR_BLOCK) { + BIO_set_retry_write(b); + } + return -1; +} + +static int stream_puts(BIO* b, const char* str) { + return stream_write(b, str, checked_cast(strlen(str))); +} + +static long stream_ctrl(BIO* b, int cmd, long num, void* ptr) { + RTC_UNUSED(num); + RTC_UNUSED(ptr); + + switch (cmd) { + case BIO_CTRL_RESET: + return 0; + case BIO_CTRL_EOF: + return b->num; + case BIO_CTRL_WPENDING: + case BIO_CTRL_PENDING: + return 0; + case BIO_CTRL_FLUSH: + return 1; + default: + return 0; + } +} + +///////////////////////////////////////////////////////////////////////////// +// OpenSSLStreamAdapter +///////////////////////////////////////////////////////////////////////////// + +OpenSSLStreamAdapter::OpenSSLStreamAdapter(StreamInterface* stream) + : SSLStreamAdapter(stream), + state_(SSL_NONE), + role_(SSL_CLIENT), + ssl_read_needs_write_(false), ssl_write_needs_read_(false), + ssl_(NULL), ssl_ctx_(NULL), + custom_verification_succeeded_(false), + ssl_mode_(SSL_MODE_TLS) { +} + +OpenSSLStreamAdapter::~OpenSSLStreamAdapter() { + Cleanup(); +} + +void OpenSSLStreamAdapter::SetIdentity(SSLIdentity* identity) { + ASSERT(!identity_); + identity_.reset(static_cast(identity)); +} + +void OpenSSLStreamAdapter::SetServerRole(SSLRole role) { + role_ = role; +} + +bool OpenSSLStreamAdapter::GetPeerCertificate(SSLCertificate** cert) const { + if (!peer_certificate_) + return false; + + *cert = peer_certificate_->GetReference(); + return true; +} + +bool OpenSSLStreamAdapter::SetPeerCertificateDigest(const std::string + &digest_alg, + const unsigned char* + digest_val, + size_t digest_len) { + ASSERT(!peer_certificate_); + ASSERT(peer_certificate_digest_algorithm_.size() == 0); + ASSERT(ssl_server_name_.empty()); + size_t expected_len; + + if (!OpenSSLDigest::GetDigestSize(digest_alg, &expected_len)) { + LOG(LS_WARNING) << "Unknown digest algorithm: " << digest_alg; + return false; + } + if (expected_len != digest_len) + return false; + + peer_certificate_digest_value_.SetData(digest_val, digest_len); + peer_certificate_digest_algorithm_ = digest_alg; + + return true; +} + +// Key Extractor interface +bool OpenSSLStreamAdapter::ExportKeyingMaterial(const std::string& label, + const uint8* context, + size_t context_len, + bool use_context, + uint8* result, + size_t result_len) { +#ifdef HAVE_DTLS_SRTP + int i; + + i = SSL_export_keying_material(ssl_, result, result_len, + label.c_str(), label.length(), + const_cast(context), + context_len, use_context); + + if (i != 1) + return false; + + return true; +#else + return false; +#endif +} + +bool OpenSSLStreamAdapter::SetDtlsSrtpCiphers( + const std::vector& ciphers) { +#ifdef HAVE_DTLS_SRTP + std::string internal_ciphers; + + if (state_ != SSL_NONE) + return false; + + for (std::vector::const_iterator cipher = ciphers.begin(); + cipher != ciphers.end(); ++cipher) { + bool found = false; + for (SrtpCipherMapEntry *entry = SrtpCipherMap; entry->internal_name; + ++entry) { + if (*cipher == entry->external_name) { + found = true; + if (!internal_ciphers.empty()) + internal_ciphers += ":"; + internal_ciphers += entry->internal_name; + break; + } + } + + if (!found) { + LOG(LS_ERROR) << "Could not find cipher: " << *cipher; + return false; + } + } + + if (internal_ciphers.empty()) + return false; + + srtp_ciphers_ = internal_ciphers; + return true; +#else + return false; +#endif +} + +bool OpenSSLStreamAdapter::GetDtlsSrtpCipher(std::string* cipher) { +#ifdef HAVE_DTLS_SRTP + ASSERT(state_ == SSL_CONNECTED); + if (state_ != SSL_CONNECTED) + return false; + + SRTP_PROTECTION_PROFILE *srtp_profile = + SSL_get_selected_srtp_profile(ssl_); + + if (!srtp_profile) + return false; + + for (SrtpCipherMapEntry *entry = SrtpCipherMap; + entry->internal_name; ++entry) { + if (!strcmp(entry->internal_name, srtp_profile->name)) { + *cipher = entry->external_name; + return true; + } + } + + ASSERT(false); // This should never happen + + return false; +#else + return false; +#endif +} + +int OpenSSLStreamAdapter::StartSSLWithServer(const char* server_name) { + ASSERT(server_name != NULL && server_name[0] != '\0'); + ssl_server_name_ = server_name; + return StartSSL(); +} + +int OpenSSLStreamAdapter::StartSSLWithPeer() { + ASSERT(ssl_server_name_.empty()); + // It is permitted to specify peer_certificate_ only later. + return StartSSL(); +} + +void OpenSSLStreamAdapter::SetMode(SSLMode mode) { + ASSERT(state_ == SSL_NONE); + ssl_mode_ = mode; +} + +// +// StreamInterface Implementation +// + +StreamResult OpenSSLStreamAdapter::Write(const void* data, size_t data_len, + size_t* written, int* error) { + LOG(LS_VERBOSE) << "OpenSSLStreamAdapter::Write(" << data_len << ")"; + + switch (state_) { + case SSL_NONE: + // pass-through in clear text + return StreamAdapterInterface::Write(data, data_len, written, error); + + case SSL_WAIT: + case SSL_CONNECTING: + return SR_BLOCK; + + case SSL_CONNECTED: + break; + + case SSL_ERROR: + case SSL_CLOSED: + default: + if (error) + *error = ssl_error_code_; + return SR_ERROR; + } + + // OpenSSL will return an error if we try to write zero bytes + if (data_len == 0) { + if (written) + *written = 0; + return SR_SUCCESS; + } + + ssl_write_needs_read_ = false; + + int code = SSL_write(ssl_, data, checked_cast(data_len)); + int ssl_error = SSL_get_error(ssl_, code); + switch (ssl_error) { + case SSL_ERROR_NONE: + LOG(LS_VERBOSE) << " -- success"; + ASSERT(0 < code && static_cast(code) <= data_len); + if (written) + *written = code; + return SR_SUCCESS; + case SSL_ERROR_WANT_READ: + LOG(LS_VERBOSE) << " -- error want read"; + ssl_write_needs_read_ = true; + return SR_BLOCK; + case SSL_ERROR_WANT_WRITE: + LOG(LS_VERBOSE) << " -- error want write"; + return SR_BLOCK; + + case SSL_ERROR_ZERO_RETURN: + default: + Error("SSL_write", (ssl_error ? ssl_error : -1), false); + if (error) + *error = ssl_error_code_; + return SR_ERROR; + } + // not reached +} + +StreamResult OpenSSLStreamAdapter::Read(void* data, size_t data_len, + size_t* read, int* error) { + LOG(LS_VERBOSE) << "OpenSSLStreamAdapter::Read(" << data_len << ")"; + switch (state_) { + case SSL_NONE: + // pass-through in clear text + return StreamAdapterInterface::Read(data, data_len, read, error); + + case SSL_WAIT: + case SSL_CONNECTING: + return SR_BLOCK; + + case SSL_CONNECTED: + break; + + case SSL_CLOSED: + return SR_EOS; + + case SSL_ERROR: + default: + if (error) + *error = ssl_error_code_; + return SR_ERROR; + } + + // Don't trust OpenSSL with zero byte reads + if (data_len == 0) { + if (read) + *read = 0; + return SR_SUCCESS; + } + + ssl_read_needs_write_ = false; + + int code = SSL_read(ssl_, data, checked_cast(data_len)); + int ssl_error = SSL_get_error(ssl_, code); + switch (ssl_error) { + case SSL_ERROR_NONE: + LOG(LS_VERBOSE) << " -- success"; + ASSERT(0 < code && static_cast(code) <= data_len); + if (read) + *read = code; + + if (ssl_mode_ == SSL_MODE_DTLS) { + // Enforce atomic reads -- this is a short read + unsigned int pending = SSL_pending(ssl_); + + if (pending) { + LOG(LS_INFO) << " -- short DTLS read. flushing"; + FlushInput(pending); + if (error) + *error = SSE_MSG_TRUNC; + return SR_ERROR; + } + } + return SR_SUCCESS; + case SSL_ERROR_WANT_READ: + LOG(LS_VERBOSE) << " -- error want read"; + return SR_BLOCK; + case SSL_ERROR_WANT_WRITE: + LOG(LS_VERBOSE) << " -- error want write"; + ssl_read_needs_write_ = true; + return SR_BLOCK; + case SSL_ERROR_ZERO_RETURN: + LOG(LS_VERBOSE) << " -- remote side closed"; + return SR_EOS; + break; + default: + LOG(LS_VERBOSE) << " -- error " << code; + Error("SSL_read", (ssl_error ? ssl_error : -1), false); + if (error) + *error = ssl_error_code_; + return SR_ERROR; + } + // not reached +} + +void OpenSSLStreamAdapter::FlushInput(unsigned int left) { + unsigned char buf[2048]; + + while (left) { + // This should always succeed + int toread = (sizeof(buf) < left) ? sizeof(buf) : left; + int code = SSL_read(ssl_, buf, toread); + + int ssl_error = SSL_get_error(ssl_, code); + ASSERT(ssl_error == SSL_ERROR_NONE); + + if (ssl_error != SSL_ERROR_NONE) { + LOG(LS_VERBOSE) << " -- error " << code; + Error("SSL_read", (ssl_error ? ssl_error : -1), false); + return; + } + + LOG(LS_VERBOSE) << " -- flushed " << code << " bytes"; + left -= code; + } +} + +void OpenSSLStreamAdapter::Close() { + Cleanup(); + ASSERT(state_ == SSL_CLOSED || state_ == SSL_ERROR); + StreamAdapterInterface::Close(); +} + +StreamState OpenSSLStreamAdapter::GetState() const { + switch (state_) { + case SSL_WAIT: + case SSL_CONNECTING: + return SS_OPENING; + case SSL_CONNECTED: + return SS_OPEN; + default: + return SS_CLOSED; + }; + // not reached +} + +void OpenSSLStreamAdapter::OnEvent(StreamInterface* stream, int events, + int err) { + int events_to_signal = 0; + int signal_error = 0; + ASSERT(stream == this->stream()); + if ((events & SE_OPEN)) { + LOG(LS_VERBOSE) << "OpenSSLStreamAdapter::OnEvent SE_OPEN"; + if (state_ != SSL_WAIT) { + ASSERT(state_ == SSL_NONE); + events_to_signal |= SE_OPEN; + } else { + state_ = SSL_CONNECTING; + if (int err = BeginSSL()) { + Error("BeginSSL", err, true); + return; + } + } + } + if ((events & (SE_READ|SE_WRITE))) { + LOG(LS_VERBOSE) << "OpenSSLStreamAdapter::OnEvent" + << ((events & SE_READ) ? " SE_READ" : "") + << ((events & SE_WRITE) ? " SE_WRITE" : ""); + if (state_ == SSL_NONE) { + events_to_signal |= events & (SE_READ|SE_WRITE); + } else if (state_ == SSL_CONNECTING) { + if (int err = ContinueSSL()) { + Error("ContinueSSL", err, true); + return; + } + } else if (state_ == SSL_CONNECTED) { + if (((events & SE_READ) && ssl_write_needs_read_) || + (events & SE_WRITE)) { + LOG(LS_VERBOSE) << " -- onStreamWriteable"; + events_to_signal |= SE_WRITE; + } + if (((events & SE_WRITE) && ssl_read_needs_write_) || + (events & SE_READ)) { + LOG(LS_VERBOSE) << " -- onStreamReadable"; + events_to_signal |= SE_READ; + } + } + } + if ((events & SE_CLOSE)) { + LOG(LS_VERBOSE) << "OpenSSLStreamAdapter::OnEvent(SE_CLOSE, " << err << ")"; + Cleanup(); + events_to_signal |= SE_CLOSE; + // SE_CLOSE is the only event that uses the final parameter to OnEvent(). + ASSERT(signal_error == 0); + signal_error = err; + } + if (events_to_signal) + StreamAdapterInterface::OnEvent(stream, events_to_signal, signal_error); +} + +int OpenSSLStreamAdapter::StartSSL() { + ASSERT(state_ == SSL_NONE); + + if (StreamAdapterInterface::GetState() != SS_OPEN) { + state_ = SSL_WAIT; + return 0; + } + + state_ = SSL_CONNECTING; + if (int err = BeginSSL()) { + Error("BeginSSL", err, false); + return err; + } + + return 0; +} + +int OpenSSLStreamAdapter::BeginSSL() { + ASSERT(state_ == SSL_CONNECTING); + // The underlying stream has open. If we are in peer-to-peer mode + // then a peer certificate must have been specified by now. + ASSERT(!ssl_server_name_.empty() || + !peer_certificate_digest_algorithm_.empty()); + LOG(LS_INFO) << "BeginSSL: " + << (!ssl_server_name_.empty() ? ssl_server_name_ : + "with peer"); + + BIO* bio = NULL; + + // First set up the context + ASSERT(ssl_ctx_ == NULL); + ssl_ctx_ = SetupSSLContext(); + if (!ssl_ctx_) + return -1; + + bio = BIO_new_stream(static_cast(stream())); + if (!bio) + return -1; + + ssl_ = SSL_new(ssl_ctx_); + if (!ssl_) { + BIO_free(bio); + return -1; + } + + SSL_set_app_data(ssl_, this); + + SSL_set_bio(ssl_, bio, bio); // the SSL object owns the bio now. + + SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE | + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + // Specify an ECDH group for ECDHE ciphers, otherwise they cannot be + // negotiated when acting as the server. Use NIST's P-256 which is commonly + // supported. + EC_KEY* ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (ecdh == NULL) + return -1; + SSL_set_options(ssl_, SSL_OP_SINGLE_ECDH_USE); + SSL_set_tmp_ecdh(ssl_, ecdh); + EC_KEY_free(ecdh); + + // Do the connect + return ContinueSSL(); +} + +int OpenSSLStreamAdapter::ContinueSSL() { + LOG(LS_VERBOSE) << "ContinueSSL"; + ASSERT(state_ == SSL_CONNECTING); + + // Clear the DTLS timer + Thread::Current()->Clear(this, MSG_TIMEOUT); + + int code = (role_ == SSL_CLIENT) ? SSL_connect(ssl_) : SSL_accept(ssl_); + int ssl_error; + switch (ssl_error = SSL_get_error(ssl_, code)) { + case SSL_ERROR_NONE: + LOG(LS_VERBOSE) << " -- success"; + + if (!SSLPostConnectionCheck(ssl_, ssl_server_name_.c_str(), NULL, + peer_certificate_digest_algorithm_)) { + LOG(LS_ERROR) << "TLS post connection check failed"; + return -1; + } + + state_ = SSL_CONNECTED; + StreamAdapterInterface::OnEvent(stream(), SE_OPEN|SE_READ|SE_WRITE, 0); + break; + + case SSL_ERROR_WANT_READ: { + LOG(LS_VERBOSE) << " -- error want read"; + struct timeval timeout; + if (DTLSv1_get_timeout(ssl_, &timeout)) { + int delay = timeout.tv_sec * 1000 + timeout.tv_usec/1000; + + Thread::Current()->PostDelayed(delay, this, MSG_TIMEOUT, 0); + } + } + break; + + case SSL_ERROR_WANT_WRITE: + LOG(LS_VERBOSE) << " -- error want write"; + break; + + case SSL_ERROR_ZERO_RETURN: + default: + LOG(LS_VERBOSE) << " -- error " << code; + return (ssl_error != 0) ? ssl_error : -1; + } + + return 0; +} + +void OpenSSLStreamAdapter::Error(const char* context, int err, bool signal) { + LOG(LS_WARNING) << "OpenSSLStreamAdapter::Error(" + << context << ", " << err << ")"; + state_ = SSL_ERROR; + ssl_error_code_ = err; + Cleanup(); + if (signal) + StreamAdapterInterface::OnEvent(stream(), SE_CLOSE, err); +} + +void OpenSSLStreamAdapter::Cleanup() { + LOG(LS_INFO) << "Cleanup"; + + if (state_ != SSL_ERROR) { + state_ = SSL_CLOSED; + ssl_error_code_ = 0; + } + + if (ssl_) { + int ret = SSL_shutdown(ssl_); + if (ret < 0) { + LOG(LS_WARNING) << "SSL_shutdown failed, error = " + << SSL_get_error(ssl_, ret); + } + + SSL_free(ssl_); + ssl_ = NULL; + } + if (ssl_ctx_) { + SSL_CTX_free(ssl_ctx_); + ssl_ctx_ = NULL; + } + identity_.reset(); + peer_certificate_.reset(); + + // Clear the DTLS timer + Thread::Current()->Clear(this, MSG_TIMEOUT); +} + + +void OpenSSLStreamAdapter::OnMessage(Message* msg) { + // Process our own messages and then pass others to the superclass + if (MSG_TIMEOUT == msg->message_id) { + LOG(LS_INFO) << "DTLS timeout expired"; + DTLSv1_handle_timeout(ssl_); + ContinueSSL(); + } else { + StreamInterface::OnMessage(msg); + } +} + +SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() { + SSL_CTX *ctx = NULL; + + if (role_ == SSL_CLIENT) { + ctx = SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ? + DTLSv1_client_method() : TLSv1_client_method()); + } else { + ctx = SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ? + DTLSv1_server_method() : TLSv1_server_method()); + } + if (ctx == NULL) + return NULL; + + if (identity_ && !identity_->ConfigureIdentity(ctx)) { + SSL_CTX_free(ctx); + return NULL; + } + +#ifdef _DEBUG + SSL_CTX_set_info_callback(ctx, OpenSSLAdapter::SSLInfoCallback); +#endif + + int mode = SSL_VERIFY_PEER; + if (client_auth_enabled()) { + // Require a certificate from the client. + // Note: Normally this is always true in production, but it may be disabled + // for testing purposes (e.g. SSLAdapter unit tests). + mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + } + + SSL_CTX_set_verify(ctx, mode, SSLVerifyCallback); + SSL_CTX_set_verify_depth(ctx, 4); + SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); + +#ifdef HAVE_DTLS_SRTP + if (!srtp_ciphers_.empty()) { + if (SSL_CTX_set_tlsext_use_srtp(ctx, srtp_ciphers_.c_str())) { + SSL_CTX_free(ctx); + return NULL; + } + } +#endif + + return ctx; +} + +int OpenSSLStreamAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) { + // Get our SSL structure from the store + SSL* ssl = reinterpret_cast(X509_STORE_CTX_get_ex_data( + store, + SSL_get_ex_data_X509_STORE_CTX_idx())); + OpenSSLStreamAdapter* stream = + reinterpret_cast(SSL_get_app_data(ssl)); + + if (stream->peer_certificate_digest_algorithm_.empty()) { + return 0; + } + X509* cert = X509_STORE_CTX_get_current_cert(store); + int depth = X509_STORE_CTX_get_error_depth(store); + + // For now We ignore the parent certificates and verify the leaf against + // the digest. + // + // TODO(jiayl): Verify the chain is a proper chain and report the chain to + // |stream->peer_certificate_|, like what NSS does. + if (depth > 0) { + LOG(LS_INFO) << "Ignored chained certificate at depth " << depth; + return 1; + } + + unsigned char digest[EVP_MAX_MD_SIZE]; + size_t digest_length; + if (!OpenSSLCertificate::ComputeDigest( + cert, + stream->peer_certificate_digest_algorithm_, + digest, sizeof(digest), + &digest_length)) { + LOG(LS_WARNING) << "Failed to compute peer cert digest."; + return 0; + } + + Buffer computed_digest(digest, digest_length); + if (computed_digest != stream->peer_certificate_digest_value_) { + LOG(LS_WARNING) << "Rejected peer certificate due to mismatched digest."; + return 0; + } + // Ignore any verification error if the digest matches, since there is no + // value in checking the validity of a self-signed cert issued by untrusted + // sources. + LOG(LS_INFO) << "Accepted peer certificate."; + + // Record the peer's certificate. + stream->peer_certificate_.reset(new OpenSSLCertificate(cert)); + return 1; +} + +// This code is taken from the "Network Security with OpenSSL" +// sample in chapter 5 +bool OpenSSLStreamAdapter::SSLPostConnectionCheck(SSL* ssl, + const char* server_name, + const X509* peer_cert, + const std::string + &peer_digest) { + ASSERT(server_name != NULL); + bool ok; + if (server_name[0] != '\0') { // traditional mode + ok = OpenSSLAdapter::VerifyServerName(ssl, server_name, ignore_bad_cert()); + + if (ok) { + ok = (SSL_get_verify_result(ssl) == X509_V_OK || + custom_verification_succeeded_); + } + } else { // peer-to-peer mode + ASSERT((peer_cert != NULL) || (!peer_digest.empty())); + // no server name validation + ok = true; + } + + if (!ok && ignore_bad_cert()) { + LOG(LS_ERROR) << "SSL_get_verify_result(ssl) = " + << SSL_get_verify_result(ssl); + LOG(LS_INFO) << "Other TLS post connection checks failed."; + ok = true; + } + + return ok; +} + +bool OpenSSLStreamAdapter::HaveDtls() { + return true; +} + +bool OpenSSLStreamAdapter::HaveDtlsSrtp() { +#ifdef HAVE_DTLS_SRTP + return true; +#else + return false; +#endif +} + +bool OpenSSLStreamAdapter::HaveExporter() { +#ifdef HAVE_DTLS_SRTP + return true; +#else + return false; +#endif +} + +} // namespace rtc + +#endif // HAVE_OPENSSL_SSL_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/opensslstreamadapter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/opensslstreamadapter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/opensslstreamadapter.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/opensslstreamadapter.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,198 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_OPENSSLSTREAMADAPTER_H__ +#define WEBRTC_BASE_OPENSSLSTREAMADAPTER_H__ + +#include +#include + +#include "webrtc/base/buffer.h" +#include "webrtc/base/sslstreamadapter.h" +#include "webrtc/base/opensslidentity.h" + +typedef struct ssl_st SSL; +typedef struct ssl_ctx_st SSL_CTX; +typedef struct x509_store_ctx_st X509_STORE_CTX; + +namespace rtc { + +// This class was written with OpenSSLAdapter (a socket adapter) as a +// starting point. It has similar structure and functionality, with +// the peer-to-peer mode added. +// +// Static methods to initialize and deinit the SSL library are in +// OpenSSLAdapter. This class also uses +// OpenSSLAdapter::custom_verify_callback_ (a static field). These +// should probably be moved out to a neutral class. +// +// In a few cases I have factored out some OpenSSLAdapter code into +// static methods so it can be reused from this class. Eventually that +// code should probably be moved to a common support +// class. Unfortunately there remain a few duplicated sections of +// code. I have not done more restructuring because I did not want to +// affect existing code that uses OpenSSLAdapter. +// +// This class does not support the SSL connection restart feature +// present in OpenSSLAdapter. I am not entirely sure how the feature +// is useful and I am not convinced that it works properly. +// +// This implementation is careful to disallow data exchange after an +// SSL error, and it has an explicit SSL_CLOSED state. It should not +// be possible to send any data in clear after one of the StartSSL +// methods has been called. + +// Look in sslstreamadapter.h for documentation of the methods. + +class OpenSSLIdentity; + +/////////////////////////////////////////////////////////////////////////////// + +class OpenSSLStreamAdapter : public SSLStreamAdapter { + public: + explicit OpenSSLStreamAdapter(StreamInterface* stream); + virtual ~OpenSSLStreamAdapter(); + + virtual void SetIdentity(SSLIdentity* identity); + + // Default argument is for compatibility + virtual void SetServerRole(SSLRole role = SSL_SERVER); + virtual bool SetPeerCertificateDigest(const std::string& digest_alg, + const unsigned char* digest_val, + size_t digest_len); + + virtual bool GetPeerCertificate(SSLCertificate** cert) const; + + virtual int StartSSLWithServer(const char* server_name); + virtual int StartSSLWithPeer(); + virtual void SetMode(SSLMode mode); + + virtual StreamResult Read(void* data, size_t data_len, + size_t* read, int* error); + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + virtual void Close(); + virtual StreamState GetState() const; + + // Key Extractor interface + virtual bool ExportKeyingMaterial(const std::string& label, + const uint8* context, + size_t context_len, + bool use_context, + uint8* result, + size_t result_len); + + + // DTLS-SRTP interface + virtual bool SetDtlsSrtpCiphers(const std::vector& ciphers); + virtual bool GetDtlsSrtpCipher(std::string* cipher); + + // Capabilities interfaces + static bool HaveDtls(); + static bool HaveDtlsSrtp(); + static bool HaveExporter(); + + protected: + virtual void OnEvent(StreamInterface* stream, int events, int err); + + private: + enum SSLState { + // Before calling one of the StartSSL methods, data flows + // in clear text. + SSL_NONE, + SSL_WAIT, // waiting for the stream to open to start SSL negotiation + SSL_CONNECTING, // SSL negotiation in progress + SSL_CONNECTED, // SSL stream successfully established + SSL_ERROR, // some SSL error occurred, stream is closed + SSL_CLOSED // Clean close + }; + + enum { MSG_TIMEOUT = MSG_MAX+1}; + + // The following three methods return 0 on success and a negative + // error code on failure. The error code may be from OpenSSL or -1 + // on some other error cases, so it can't really be interpreted + // unfortunately. + + // Go from state SSL_NONE to either SSL_CONNECTING or SSL_WAIT, + // depending on whether the underlying stream is already open or + // not. + int StartSSL(); + // Prepare SSL library, state is SSL_CONNECTING. + int BeginSSL(); + // Perform SSL negotiation steps. + int ContinueSSL(); + + // Error handler helper. signal is given as true for errors in + // asynchronous contexts (when an error method was not returned + // through some other method), and in that case an SE_CLOSE event is + // raised on the stream with the specified error. + // A 0 error means a graceful close, otherwise there is not really enough + // context to interpret the error code. + void Error(const char* context, int err, bool signal); + void Cleanup(); + + // Override MessageHandler + virtual void OnMessage(Message* msg); + + // Flush the input buffers by reading left bytes (for DTLS) + void FlushInput(unsigned int left); + + // SSL library configuration + SSL_CTX* SetupSSLContext(); + // SSL verification check + bool SSLPostConnectionCheck(SSL* ssl, const char* server_name, + const X509* peer_cert, + const std::string& peer_digest); + // SSL certification verification error handler, called back from + // the openssl library. Returns an int interpreted as a boolean in + // the C style: zero means verification failure, non-zero means + // passed. + static int SSLVerifyCallback(int ok, X509_STORE_CTX* store); + + SSLState state_; + SSLRole role_; + int ssl_error_code_; // valid when state_ == SSL_ERROR or SSL_CLOSED + // Whether the SSL negotiation is blocked on needing to read or + // write to the wrapped stream. + bool ssl_read_needs_write_; + bool ssl_write_needs_read_; + + SSL* ssl_; + SSL_CTX* ssl_ctx_; + + // Our key and certificate, mostly useful in peer-to-peer mode. + scoped_ptr identity_; + // in traditional mode, the server name that the server's certificate + // must specify. Empty in peer-to-peer mode. + std::string ssl_server_name_; + // The certificate that the peer must present or did present. Initially + // null in traditional mode, until the connection is established. + scoped_ptr peer_certificate_; + // In peer-to-peer mode, the digest of the certificate that + // the peer must present. + Buffer peer_certificate_digest_value_; + std::string peer_certificate_digest_algorithm_; + + // OpenSSLAdapter::custom_verify_callback_ result + bool custom_verification_succeeded_; + + // The DtlsSrtp ciphers + std::string srtp_ciphers_; + + // Do DTLS or not + SSLMode ssl_mode_; +}; + +///////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_OPENSSLSTREAMADAPTER_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/optionsfile.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/optionsfile.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/optionsfile.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/optionsfile.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,184 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/optionsfile.h" + +#include + +#include "webrtc/base/logging.h" +#include "webrtc/base/stream.h" +#include "webrtc/base/stringencode.h" + +namespace rtc { + +OptionsFile::OptionsFile(const std::string &path) : path_(path) { +} + +bool OptionsFile::Load() { + options_.clear(); + // Open file. + FileStream stream; + int err; + if (!stream.Open(path_, "r", &err)) { + LOG_F(LS_WARNING) << "Could not open file, err=" << err; + // We do not consider this an error because we expect there to be no file + // until the user saves a setting. + return true; + } + // Read in all its data. + std::string line; + StreamResult res; + for (;;) { + res = stream.ReadLine(&line); + if (res != SR_SUCCESS) { + break; + } + size_t equals_pos = line.find('='); + if (equals_pos == std::string::npos) { + // We do not consider this an error. Instead we ignore the line and + // keep going. + LOG_F(LS_WARNING) << "Ignoring malformed line in " << path_; + continue; + } + std::string key(line, 0, equals_pos); + std::string value(line, equals_pos + 1, line.length() - (equals_pos + 1)); + options_[key] = value; + } + if (res != SR_EOS) { + LOG_F(LS_ERROR) << "Error when reading from file"; + return false; + } else { + return true; + } +} + +bool OptionsFile::Save() { + // Open file. + FileStream stream; + int err; + if (!stream.Open(path_, "w", &err)) { + LOG_F(LS_ERROR) << "Could not open file, err=" << err; + return false; + } + // Write out all the data. + StreamResult res = SR_SUCCESS; + size_t written; + int error; + for (OptionsMap::const_iterator i = options_.begin(); i != options_.end(); + ++i) { + res = stream.WriteAll(i->first.c_str(), i->first.length(), &written, + &error); + if (res != SR_SUCCESS) { + break; + } + res = stream.WriteAll("=", 1, &written, &error); + if (res != SR_SUCCESS) { + break; + } + res = stream.WriteAll(i->second.c_str(), i->second.length(), &written, + &error); + if (res != SR_SUCCESS) { + break; + } + res = stream.WriteAll("\n", 1, &written, &error); + if (res != SR_SUCCESS) { + break; + } + } + if (res != SR_SUCCESS) { + LOG_F(LS_ERROR) << "Unable to write to file"; + return false; + } else { + return true; + } +} + +bool OptionsFile::IsLegalName(const std::string &name) { + for (size_t pos = 0; pos < name.length(); ++pos) { + if (name[pos] == '\n' || name[pos] == '\\' || name[pos] == '=') { + // Illegal character. + LOG(LS_WARNING) << "Ignoring operation for illegal option " << name; + return false; + } + } + return true; +} + +bool OptionsFile::IsLegalValue(const std::string &value) { + for (size_t pos = 0; pos < value.length(); ++pos) { + if (value[pos] == '\n' || value[pos] == '\\') { + // Illegal character. + LOG(LS_WARNING) << "Ignoring operation for illegal value " << value; + return false; + } + } + return true; +} + +bool OptionsFile::GetStringValue(const std::string& option, + std::string *out_val) const { + LOG(LS_VERBOSE) << "OptionsFile::GetStringValue " + << option; + if (!IsLegalName(option)) { + return false; + } + OptionsMap::const_iterator i = options_.find(option); + if (i == options_.end()) { + return false; + } + *out_val = i->second; + return true; +} + +bool OptionsFile::GetIntValue(const std::string& option, + int *out_val) const { + LOG(LS_VERBOSE) << "OptionsFile::GetIntValue " + << option; + if (!IsLegalName(option)) { + return false; + } + OptionsMap::const_iterator i = options_.find(option); + if (i == options_.end()) { + return false; + } + return FromString(i->second, out_val); +} + +bool OptionsFile::SetStringValue(const std::string& option, + const std::string& value) { + LOG(LS_VERBOSE) << "OptionsFile::SetStringValue " + << option << ":" << value; + if (!IsLegalName(option) || !IsLegalValue(value)) { + return false; + } + options_[option] = value; + return true; +} + +bool OptionsFile::SetIntValue(const std::string& option, + int value) { + LOG(LS_VERBOSE) << "OptionsFile::SetIntValue " + << option << ":" << value; + if (!IsLegalName(option)) { + return false; + } + return ToString(value, &options_[option]); +} + +bool OptionsFile::RemoveValue(const std::string& option) { + LOG(LS_VERBOSE) << "OptionsFile::RemoveValue " << option; + if (!IsLegalName(option)) { + return false; + } + options_.erase(option); + return true; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/optionsfile.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/optionsfile.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/optionsfile.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/optionsfile.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,49 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_OPTIONSFILE_H_ +#define WEBRTC_BASE_OPTIONSFILE_H_ + +#include +#include + +namespace rtc { + +// Implements storage of simple options in a text file on disk. This is +// cross-platform, but it is intended mostly for Linux where there is no +// first-class options storage system. +class OptionsFile { + public: + OptionsFile(const std::string &path); + + // Loads the file from disk, overwriting the in-memory values. + bool Load(); + // Saves the contents in memory, overwriting the on-disk values. + bool Save(); + + bool GetStringValue(const std::string& option, std::string* out_val) const; + bool GetIntValue(const std::string& option, int* out_val) const; + bool SetStringValue(const std::string& option, const std::string& val); + bool SetIntValue(const std::string& option, int val); + bool RemoveValue(const std::string& option); + + private: + typedef std::map OptionsMap; + + static bool IsLegalName(const std::string &name); + static bool IsLegalValue(const std::string &value); + + std::string path_; + OptionsMap options_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_OPTIONSFILE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/optionsfile_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/optionsfile_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/optionsfile_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/optionsfile_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,168 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/fileutils.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/optionsfile.h" +#include "webrtc/base/pathutils.h" + +namespace rtc { + +static const std::string kTestOptionA = "test-option-a"; +static const std::string kTestOptionB = "test-option-b"; +static const std::string kTestString1 = "a string"; +static const std::string kTestString2 = "different string"; +static const std::string kOptionWithEquals = "foo=bar"; +static const std::string kOptionWithNewline = "foo\nbar"; +static const std::string kValueWithEquals = "baz=quux"; +static const std::string kValueWithNewline = "baz\nquux"; +static const std::string kEmptyString = ""; +static const char kOptionWithUtf8[] = {'O', 'p', 't', '\302', '\256', 'i', 'o', + 'n', '\342', '\204', '\242', '\0'}; // Opt(R)io(TM). +static const char kValueWithUtf8[] = {'V', 'a', 'l', '\302', '\256', 'v', 'e', + '\342', '\204', '\242', '\0'}; // Val(R)ue(TM). +static int kTestInt1 = 12345; +static int kTestInt2 = 67890; +static int kNegInt = -634; +static int kZero = 0; + +class OptionsFileTest : public testing::Test { + public: + OptionsFileTest() { + Pathname dir; + ASSERT(Filesystem::GetTemporaryFolder(dir, true, NULL)); + test_file_ = Filesystem::TempFilename(dir, ".testfile"); + OpenStore(); + } + + protected: + void OpenStore() { + store_.reset(new OptionsFile(test_file_)); + } + + rtc::scoped_ptr store_; + + private: + std::string test_file_; +}; + +TEST_F(OptionsFileTest, GetSetString) { + // Clear contents of the file on disk. + EXPECT_TRUE(store_->Save()); + std::string out1, out2; + EXPECT_FALSE(store_->GetStringValue(kTestOptionA, &out1)); + EXPECT_FALSE(store_->GetStringValue(kTestOptionB, &out2)); + EXPECT_TRUE(store_->SetStringValue(kTestOptionA, kTestString1)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->SetStringValue(kTestOptionB, kTestString2)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->GetStringValue(kTestOptionA, &out1)); + EXPECT_TRUE(store_->GetStringValue(kTestOptionB, &out2)); + EXPECT_EQ(kTestString1, out1); + EXPECT_EQ(kTestString2, out2); + EXPECT_TRUE(store_->RemoveValue(kTestOptionA)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->RemoveValue(kTestOptionB)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_FALSE(store_->GetStringValue(kTestOptionA, &out1)); + EXPECT_FALSE(store_->GetStringValue(kTestOptionB, &out2)); +} + +TEST_F(OptionsFileTest, GetSetInt) { + // Clear contents of the file on disk. + EXPECT_TRUE(store_->Save()); + int out1, out2; + EXPECT_FALSE(store_->GetIntValue(kTestOptionA, &out1)); + EXPECT_FALSE(store_->GetIntValue(kTestOptionB, &out2)); + EXPECT_TRUE(store_->SetIntValue(kTestOptionA, kTestInt1)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->SetIntValue(kTestOptionB, kTestInt2)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->GetIntValue(kTestOptionA, &out1)); + EXPECT_TRUE(store_->GetIntValue(kTestOptionB, &out2)); + EXPECT_EQ(kTestInt1, out1); + EXPECT_EQ(kTestInt2, out2); + EXPECT_TRUE(store_->RemoveValue(kTestOptionA)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->RemoveValue(kTestOptionB)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_FALSE(store_->GetIntValue(kTestOptionA, &out1)); + EXPECT_FALSE(store_->GetIntValue(kTestOptionB, &out2)); + EXPECT_TRUE(store_->SetIntValue(kTestOptionA, kNegInt)); + EXPECT_TRUE(store_->GetIntValue(kTestOptionA, &out1)); + EXPECT_EQ(kNegInt, out1); + EXPECT_TRUE(store_->SetIntValue(kTestOptionA, kZero)); + EXPECT_TRUE(store_->GetIntValue(kTestOptionA, &out1)); + EXPECT_EQ(kZero, out1); +} + +TEST_F(OptionsFileTest, Persist) { + // Clear contents of the file on disk. + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->SetStringValue(kTestOptionA, kTestString1)); + EXPECT_TRUE(store_->SetIntValue(kTestOptionB, kNegInt)); + EXPECT_TRUE(store_->Save()); + + // Load the saved contents from above. + OpenStore(); + EXPECT_TRUE(store_->Load()); + std::string out1; + int out2; + EXPECT_TRUE(store_->GetStringValue(kTestOptionA, &out1)); + EXPECT_TRUE(store_->GetIntValue(kTestOptionB, &out2)); + EXPECT_EQ(kTestString1, out1); + EXPECT_EQ(kNegInt, out2); +} + +TEST_F(OptionsFileTest, SpecialCharacters) { + // Clear contents of the file on disk. + EXPECT_TRUE(store_->Save()); + std::string out; + EXPECT_FALSE(store_->SetStringValue(kOptionWithEquals, kTestString1)); + EXPECT_FALSE(store_->GetStringValue(kOptionWithEquals, &out)); + EXPECT_FALSE(store_->SetStringValue(kOptionWithNewline, kTestString1)); + EXPECT_FALSE(store_->GetStringValue(kOptionWithNewline, &out)); + EXPECT_TRUE(store_->SetStringValue(kOptionWithUtf8, kValueWithUtf8)); + EXPECT_TRUE(store_->SetStringValue(kTestOptionA, kTestString1)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->GetStringValue(kTestOptionA, &out)); + EXPECT_EQ(kTestString1, out); + EXPECT_TRUE(store_->GetStringValue(kOptionWithUtf8, &out)); + EXPECT_EQ(kValueWithUtf8, out); + EXPECT_FALSE(store_->SetStringValue(kTestOptionA, kValueWithNewline)); + EXPECT_TRUE(store_->GetStringValue(kTestOptionA, &out)); + EXPECT_EQ(kTestString1, out); + EXPECT_TRUE(store_->SetStringValue(kTestOptionA, kValueWithEquals)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->GetStringValue(kTestOptionA, &out)); + EXPECT_EQ(kValueWithEquals, out); + EXPECT_TRUE(store_->SetStringValue(kEmptyString, kTestString2)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->GetStringValue(kEmptyString, &out)); + EXPECT_EQ(kTestString2, out); + EXPECT_TRUE(store_->SetStringValue(kTestOptionB, kEmptyString)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->GetStringValue(kTestOptionB, &out)); + EXPECT_EQ(kEmptyString, out); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/OWNERS 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,13 @@ +henrika@webrtc.org +henrike@webrtc.org +henrikg@webrtc.org +hta@webrtc.org +jiayl@webrtc.org +juberti@webrtc.org +mflodman@webrtc.org +perkj@webrtc.org +pthatcher@webrtc.org +sergeyu@chromium.org +tommi@webrtc.org + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/pathutils.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/pathutils.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/pathutils.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/pathutils.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,251 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32.h" +#include +#include +#include +#endif // WEBRTC_WIN + +#include "webrtc/base/common.h" +#include "webrtc/base/fileutils.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/pathutils.h" +#include "webrtc/base/stringutils.h" +#include "webrtc/base/urlencode.h" + +namespace rtc { + +static const char EMPTY_STR[] = ""; + +// EXT_DELIM separates a file basename from extension +const char EXT_DELIM = '.'; + +// FOLDER_DELIMS separate folder segments and the filename +const char* const FOLDER_DELIMS = "/\\"; + +// DEFAULT_FOLDER_DELIM is the preferred delimiter for this platform +#if WEBRTC_WIN +const char DEFAULT_FOLDER_DELIM = '\\'; +#else // !WEBRTC_WIN +const char DEFAULT_FOLDER_DELIM = '/'; +#endif // !WEBRTC_WIN + +/////////////////////////////////////////////////////////////////////////////// +// Pathname - parsing of pathnames into components, and vice versa +/////////////////////////////////////////////////////////////////////////////// + +bool Pathname::IsFolderDelimiter(char ch) { + return (NULL != ::strchr(FOLDER_DELIMS, ch)); +} + +char Pathname::DefaultFolderDelimiter() { + return DEFAULT_FOLDER_DELIM; +} + +Pathname::Pathname() + : folder_delimiter_(DEFAULT_FOLDER_DELIM) { +} + +Pathname::Pathname(const std::string& pathname) + : folder_delimiter_(DEFAULT_FOLDER_DELIM) { + SetPathname(pathname); +} + +Pathname::Pathname(const std::string& folder, const std::string& filename) + : folder_delimiter_(DEFAULT_FOLDER_DELIM) { + SetPathname(folder, filename); +} + +void Pathname::SetFolderDelimiter(char delimiter) { + ASSERT(IsFolderDelimiter(delimiter)); + folder_delimiter_ = delimiter; +} + +void Pathname::Normalize() { + for (size_t i=0; i= 2) { + pos = folder_.find_last_of(FOLDER_DELIMS, folder_.length() - 2); + } + if (pos != std::string::npos) { + return folder_.substr(pos + 1); + } else { + return folder_; + } +} + +std::string Pathname::parent_folder() const { + std::string::size_type pos = std::string::npos; + if (folder_.size() >= 2) { + pos = folder_.find_last_of(FOLDER_DELIMS, folder_.length() - 2); + } + if (pos != std::string::npos) { + return folder_.substr(0, pos + 1); + } else { + return EMPTY_STR; + } +} + +void Pathname::SetFolder(const std::string& folder) { + folder_.assign(folder); + // Ensure folder ends in a path delimiter + if (!folder_.empty() && !IsFolderDelimiter(folder_[folder_.length()-1])) { + folder_.push_back(folder_delimiter_); + } +} + +void Pathname::AppendFolder(const std::string& folder) { + folder_.append(folder); + // Ensure folder ends in a path delimiter + if (!folder_.empty() && !IsFolderDelimiter(folder_[folder_.length()-1])) { + folder_.push_back(folder_delimiter_); + } +} + +std::string Pathname::basename() const { + return basename_; +} + +bool Pathname::SetBasename(const std::string& basename) { + if(basename.find_first_of(FOLDER_DELIMS) != std::string::npos) { + return false; + } + basename_.assign(basename); + return true; +} + +std::string Pathname::extension() const { + return extension_; +} + +bool Pathname::SetExtension(const std::string& extension) { + if (extension.find_first_of(FOLDER_DELIMS) != std::string::npos || + extension.find_first_of(EXT_DELIM, 1) != std::string::npos) { + return false; + } + extension_.assign(extension); + // Ensure extension begins with the extension delimiter + if (!extension_.empty() && (extension_[0] != EXT_DELIM)) { + extension_.insert(extension_.begin(), EXT_DELIM); + } + return true; +} + +std::string Pathname::filename() const { + std::string filename(basename_); + filename.append(extension_); + return filename; +} + +bool Pathname::SetFilename(const std::string& filename) { + std::string::size_type pos = filename.rfind(EXT_DELIM); + if ((pos == std::string::npos) || (pos == 0)) { + return SetExtension(EMPTY_STR) && SetBasename(filename); + } else { + return SetExtension(filename.substr(pos)) && SetBasename(filename.substr(0, pos)); + } +} + +#if defined(WEBRTC_WIN) +bool Pathname::GetDrive(char *drive, uint32 bytes) const { + return GetDrive(drive, bytes, folder_); +} + +// static +bool Pathname::GetDrive(char *drive, uint32 bytes, + const std::string& pathname) { + // need at lease 4 bytes to save c: + if (bytes < 4 || pathname.size() < 3) { + return false; + } + + memcpy(drive, pathname.c_str(), 3); + drive[3] = 0; + // sanity checking + return (isalpha(drive[0]) && + drive[1] == ':' && + drive[2] == '\\'); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/pathutils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/pathutils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/pathutils.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/pathutils.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,163 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_PATHUTILS_H__ +#define WEBRTC_BASE_PATHUTILS_H__ + +#include +// Temporary, until deprecated helpers are removed. +#include "webrtc/base/fileutils.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// Pathname - parsing of pathnames into components, and vice versa. +// +// To establish consistent terminology, a filename never contains a folder +// component. A folder never contains a filename. A pathname may include +// a folder and/or filename component. Here are some examples: +// +// pathname() /home/john/example.txt +// folder() /home/john/ +// filename() example.txt +// parent_folder() /home/ +// folder_name() john/ +// basename() example +// extension() .txt +// +// Basename may begin, end, and/or include periods, but no folder delimiters. +// If extension exists, it consists of a period followed by zero or more +// non-period/non-delimiter characters, and basename is non-empty. +/////////////////////////////////////////////////////////////////////////////// + +class Pathname { +public: + // Folder delimiters are slash and backslash + static bool IsFolderDelimiter(char ch); + static char DefaultFolderDelimiter(); + + Pathname(); + Pathname(const std::string& pathname); + Pathname(const std::string& folder, const std::string& filename); + + // Set's the default folder delimiter for this Pathname + char folder_delimiter() const { return folder_delimiter_; } + void SetFolderDelimiter(char delimiter); + + // Normalize changes all folder delimiters to folder_delimiter() + void Normalize(); + + // Reset to the empty pathname + void clear(); + + // Returns true if the pathname is empty. Note: this->pathname().empty() + // is always false. + bool empty() const; + + std::string url() const; + + // Returns the folder and filename components. If the pathname is empty, + // returns a string representing the current directory (as a relative path, + // i.e., "."). + std::string pathname() const; + void SetPathname(const std::string& pathname); + void SetPathname(const std::string& folder, const std::string& filename); + + // Append pathname to the current folder (if any). Any existing filename + // will be discarded. + void AppendPathname(const std::string& pathname); + + std::string folder() const; + std::string folder_name() const; + std::string parent_folder() const; + // SetFolder and AppendFolder will append a folder delimiter, if needed. + void SetFolder(const std::string& folder); + void AppendFolder(const std::string& folder); + + std::string basename() const; + bool SetBasename(const std::string& basename); + + std::string extension() const; + // SetExtension will prefix a period, if needed. + bool SetExtension(const std::string& extension); + + std::string filename() const; + bool SetFilename(const std::string& filename); + +#if defined(WEBRTC_WIN) + bool GetDrive(char *drive, uint32 bytes) const; + static bool GetDrive(char *drive, uint32 bytes,const std::string& pathname); +#endif + +private: + std::string folder_, basename_, extension_; + char folder_delimiter_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Global Helpers (deprecated) +/////////////////////////////////////////////////////////////////////////////// + +inline void SetOrganizationName(const std::string& organization) { + Filesystem::SetOrganizationName(organization); +} +inline void SetApplicationName(const std::string& application) { + Filesystem::SetApplicationName(application); +} +inline void GetOrganizationName(std::string* organization) { + Filesystem::GetOrganizationName(organization); +} +inline void GetApplicationName(std::string* application) { + Filesystem::GetApplicationName(application); +} +inline bool CreateFolder(const Pathname& path) { + return Filesystem::CreateFolder(path); +} +inline bool FinishPath(Pathname& path, bool create, const std::string& append) { + if (!append.empty()) + path.AppendFolder(append); + return !create || CreateFolder(path); +} +// Note: this method uses the convention of / for the temporary +// folder. Filesystem uses /. We will be migrating exclusively +// to // eventually. Since these are temp folders, +// it's probably ok to orphan them during the transition. +inline bool GetTemporaryFolder(Pathname& path, bool create, + const std::string& append) { + std::string application_name; + Filesystem::GetApplicationName(&application_name); + ASSERT(!application_name.empty()); + return Filesystem::GetTemporaryFolder(path, create, &application_name) + && FinishPath(path, create, append); +} +inline bool GetAppDataFolder(Pathname& path, bool create, + const std::string& append) { + ASSERT(!create); // TODO: Support create flag on Filesystem::GetAppDataFolder. + return Filesystem::GetAppDataFolder(&path, true) + && FinishPath(path, create, append); +} +inline bool CleanupTemporaryFolder() { + Pathname path; + if (!GetTemporaryFolder(path, false, "")) + return false; + if (Filesystem::IsAbsent(path)) + return true; + if (!Filesystem::IsTemporaryPath(path)) { + ASSERT(false); + return false; + } + return Filesystem::DeleteFolderContents(path); +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_PATHUTILS_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/pathutils_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/pathutils_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/pathutils_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/pathutils_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,48 @@ +/* + * Copyright 2007 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/pathutils.h" +#include "webrtc/base/gunit.h" + +TEST(Pathname, ReturnsDotForEmptyPathname) { + const std::string kCWD = + std::string(".") + rtc::Pathname::DefaultFolderDelimiter(); + + rtc::Pathname path("/", ""); + EXPECT_FALSE(path.empty()); + EXPECT_FALSE(path.folder().empty()); + EXPECT_TRUE (path.filename().empty()); + EXPECT_FALSE(path.pathname().empty()); + EXPECT_EQ(std::string("/"), path.pathname()); + + path.SetPathname("", "foo"); + EXPECT_FALSE(path.empty()); + EXPECT_TRUE (path.folder().empty()); + EXPECT_FALSE(path.filename().empty()); + EXPECT_FALSE(path.pathname().empty()); + EXPECT_EQ(std::string("foo"), path.pathname()); + + path.SetPathname("", ""); + EXPECT_TRUE (path.empty()); + EXPECT_TRUE (path.folder().empty()); + EXPECT_TRUE (path.filename().empty()); + EXPECT_FALSE(path.pathname().empty()); + EXPECT_EQ(kCWD, path.pathname()); + + path.SetPathname(kCWD, ""); + EXPECT_FALSE(path.empty()); + EXPECT_FALSE(path.folder().empty()); + EXPECT_TRUE (path.filename().empty()); + EXPECT_FALSE(path.pathname().empty()); + EXPECT_EQ(kCWD, path.pathname()); + + rtc::Pathname path2("c:/foo bar.txt"); + EXPECT_EQ(path2.url(), std::string("file:///c:/foo%20bar.txt")); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/physicalsocketserver.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/physicalsocketserver.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/physicalsocketserver.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/physicalsocketserver.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,1670 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif + +#include + +#ifdef MEMORY_SANITIZER +#include +#endif + +#if defined(WEBRTC_POSIX) +#include +#include +#include +#include +#include +#include +#include +#endif + +#if defined(WEBRTC_WIN) +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#undef SetPort +#endif + +#include +#include + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/byteorder.h" +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/nethelpers.h" +#include "webrtc/base/physicalsocketserver.h" +#include "webrtc/base/timeutils.h" +#include "webrtc/base/winping.h" +#include "webrtc/base/win32socketinit.h" + +// stm: this will tell us if we are on OSX +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(WEBRTC_POSIX) +#include // for TCP_NODELAY +#define IP_MTU 14 // Until this is integrated from linux/in.h to netinet/in.h +typedef void* SockOptArg; +#endif // WEBRTC_POSIX + +#if defined(WEBRTC_WIN) +typedef char* SockOptArg; +#endif + +namespace rtc { + +#if defined(WEBRTC_WIN) +// Standard MTUs, from RFC 1191 +const uint16 PACKET_MAXIMUMS[] = { + 65535, // Theoretical maximum, Hyperchannel + 32000, // Nothing + 17914, // 16Mb IBM Token Ring + 8166, // IEEE 802.4 + //4464, // IEEE 802.5 (4Mb max) + 4352, // FDDI + //2048, // Wideband Network + 2002, // IEEE 802.5 (4Mb recommended) + //1536, // Expermental Ethernet Networks + //1500, // Ethernet, Point-to-Point (default) + 1492, // IEEE 802.3 + 1006, // SLIP, ARPANET + //576, // X.25 Networks + //544, // DEC IP Portal + //512, // NETBIOS + 508, // IEEE 802/Source-Rt Bridge, ARCNET + 296, // Point-to-Point (low delay) + 68, // Official minimum + 0, // End of list marker +}; + +static const int IP_HEADER_SIZE = 20u; +static const int IPV6_HEADER_SIZE = 40u; +static const int ICMP_HEADER_SIZE = 8u; +static const int ICMP_PING_TIMEOUT_MILLIS = 10000u; +#endif + +class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { + public: + PhysicalSocket(PhysicalSocketServer* ss, SOCKET s = INVALID_SOCKET) + : ss_(ss), s_(s), enabled_events_(0), error_(0), + state_((s == INVALID_SOCKET) ? CS_CLOSED : CS_CONNECTED), + resolver_(NULL) { +#if defined(WEBRTC_WIN) + // EnsureWinsockInit() ensures that winsock is initialized. The default + // version of this function doesn't do anything because winsock is + // initialized by constructor of a static object. If neccessary libjingle + // users can link it with a different version of this function by replacing + // win32socketinit.cc. See win32socketinit.cc for more details. + EnsureWinsockInit(); +#endif + if (s_ != INVALID_SOCKET) { + enabled_events_ = DE_READ | DE_WRITE; + + int type = SOCK_STREAM; + socklen_t len = sizeof(type); + VERIFY(0 == getsockopt(s_, SOL_SOCKET, SO_TYPE, (SockOptArg)&type, &len)); + udp_ = (SOCK_DGRAM == type); + } + } + + virtual ~PhysicalSocket() { + Close(); + } + + // Creates the underlying OS socket (same as the "socket" function). + virtual bool Create(int family, int type) { + Close(); + s_ = ::socket(family, type, 0); + udp_ = (SOCK_DGRAM == type); + UpdateLastError(); + if (udp_) + enabled_events_ = DE_READ | DE_WRITE; + return s_ != INVALID_SOCKET; + } + + SocketAddress GetLocalAddress() const { + sockaddr_storage addr_storage = {0}; + socklen_t addrlen = sizeof(addr_storage); + sockaddr* addr = reinterpret_cast(&addr_storage); + int result = ::getsockname(s_, addr, &addrlen); + SocketAddress address; + if (result >= 0) { + SocketAddressFromSockAddrStorage(addr_storage, &address); + } else { + LOG(LS_WARNING) << "GetLocalAddress: unable to get local addr, socket=" + << s_; + } + return address; + } + + SocketAddress GetRemoteAddress() const { + sockaddr_storage addr_storage = {0}; + socklen_t addrlen = sizeof(addr_storage); + sockaddr* addr = reinterpret_cast(&addr_storage); + int result = ::getpeername(s_, addr, &addrlen); + SocketAddress address; + if (result >= 0) { + SocketAddressFromSockAddrStorage(addr_storage, &address); + } else { + LOG(LS_WARNING) << "GetRemoteAddress: unable to get remote addr, socket=" + << s_; + } + return address; + } + + int Bind(const SocketAddress& bind_addr) { + sockaddr_storage addr_storage; + size_t len = bind_addr.ToSockAddrStorage(&addr_storage); + sockaddr* addr = reinterpret_cast(&addr_storage); + int err = ::bind(s_, addr, static_cast(len)); + UpdateLastError(); +#ifdef _DEBUG + if (0 == err) { + dbg_addr_ = "Bound @ "; + dbg_addr_.append(GetLocalAddress().ToString()); + } +#endif // _DEBUG + return err; + } + + int Connect(const SocketAddress& addr) { + // TODO: Implicit creation is required to reconnect... + // ...but should we make it more explicit? + if (state_ != CS_CLOSED) { + SetError(EALREADY); + return SOCKET_ERROR; + } + if (addr.IsUnresolved()) { + LOG(LS_VERBOSE) << "Resolving addr in PhysicalSocket::Connect"; + resolver_ = new AsyncResolver(); + resolver_->SignalDone.connect(this, &PhysicalSocket::OnResolveResult); + resolver_->Start(addr); + state_ = CS_CONNECTING; + return 0; + } + + return DoConnect(addr); + } + + int DoConnect(const SocketAddress& connect_addr) { + if ((s_ == INVALID_SOCKET) && + !Create(connect_addr.family(), SOCK_STREAM)) { + return SOCKET_ERROR; + } + sockaddr_storage addr_storage; + size_t len = connect_addr.ToSockAddrStorage(&addr_storage); + sockaddr* addr = reinterpret_cast(&addr_storage); + int err = ::connect(s_, addr, static_cast(len)); + UpdateLastError(); + if (err == 0) { + state_ = CS_CONNECTED; + } else if (IsBlockingError(GetError())) { + state_ = CS_CONNECTING; + enabled_events_ |= DE_CONNECT; + } else { + return SOCKET_ERROR; + } + + enabled_events_ |= DE_READ | DE_WRITE; + return 0; + } + + int GetError() const { + CritScope cs(&crit_); + return error_; + } + + void SetError(int error) { + CritScope cs(&crit_); + error_ = error; + } + + ConnState GetState() const { + return state_; + } + + int GetOption(Option opt, int* value) { + int slevel; + int sopt; + if (TranslateOption(opt, &slevel, &sopt) == -1) + return -1; + socklen_t optlen = sizeof(*value); + int ret = ::getsockopt(s_, slevel, sopt, (SockOptArg)value, &optlen); + if (ret != -1 && opt == OPT_DONTFRAGMENT) { +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) + *value = (*value != IP_PMTUDISC_DONT) ? 1 : 0; +#endif + } + return ret; + } + + int SetOption(Option opt, int value) { + int slevel; + int sopt; + if (TranslateOption(opt, &slevel, &sopt) == -1) + return -1; + if (opt == OPT_DONTFRAGMENT) { +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) + value = (value) ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT; +#endif + } + return ::setsockopt(s_, slevel, sopt, (SockOptArg)&value, sizeof(value)); + } + + int Send(const void *pv, size_t cb) { + int sent = ::send(s_, reinterpret_cast(pv), (int)cb, +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) + // Suppress SIGPIPE. Without this, attempting to send on a socket whose + // other end is closed will result in a SIGPIPE signal being raised to + // our process, which by default will terminate the process, which we + // don't want. By specifying this flag, we'll just get the error EPIPE + // instead and can handle the error gracefully. + MSG_NOSIGNAL +#else + 0 +#endif + ); + UpdateLastError(); + MaybeRemapSendError(); + // We have seen minidumps where this may be false. + ASSERT(sent <= static_cast(cb)); + if ((sent < 0) && IsBlockingError(GetError())) { + enabled_events_ |= DE_WRITE; + } + return sent; + } + + int SendTo(const void* buffer, size_t length, const SocketAddress& addr) { + sockaddr_storage saddr; + size_t len = addr.ToSockAddrStorage(&saddr); + int sent = ::sendto( + s_, static_cast(buffer), static_cast(length), +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) + // Suppress SIGPIPE. See above for explanation. + MSG_NOSIGNAL, +#else + 0, +#endif + reinterpret_cast(&saddr), static_cast(len)); + UpdateLastError(); + MaybeRemapSendError(); + // We have seen minidumps where this may be false. + ASSERT(sent <= static_cast(length)); + if ((sent < 0) && IsBlockingError(GetError())) { + enabled_events_ |= DE_WRITE; + } + return sent; + } + + int Recv(void* buffer, size_t length) { + int received = ::recv(s_, static_cast(buffer), + static_cast(length), 0); + if ((received == 0) && (length != 0)) { + // Note: on graceful shutdown, recv can return 0. In this case, we + // pretend it is blocking, and then signal close, so that simplifying + // assumptions can be made about Recv. + LOG(LS_WARNING) << "EOF from socket; deferring close event"; + // Must turn this back on so that the select() loop will notice the close + // event. + enabled_events_ |= DE_READ; + SetError(EWOULDBLOCK); + return SOCKET_ERROR; + } + UpdateLastError(); + int error = GetError(); + bool success = (received >= 0) || IsBlockingError(error); + if (udp_ || success) { + enabled_events_ |= DE_READ; + } + if (!success) { + LOG_F(LS_VERBOSE) << "Error = " << error; + } + return received; + } + + int RecvFrom(void* buffer, size_t length, SocketAddress *out_addr) { + sockaddr_storage addr_storage; + socklen_t addr_len = sizeof(addr_storage); + sockaddr* addr = reinterpret_cast(&addr_storage); + int received = ::recvfrom(s_, static_cast(buffer), + static_cast(length), 0, addr, &addr_len); + UpdateLastError(); + if ((received >= 0) && (out_addr != NULL)) + SocketAddressFromSockAddrStorage(addr_storage, out_addr); + int error = GetError(); + bool success = (received >= 0) || IsBlockingError(error); + if (udp_ || success) { + enabled_events_ |= DE_READ; + } + if (!success) { + LOG_F(LS_VERBOSE) << "Error = " << error; + } + return received; + } + + int Listen(int backlog) { + int err = ::listen(s_, backlog); + UpdateLastError(); + if (err == 0) { + state_ = CS_CONNECTING; + enabled_events_ |= DE_ACCEPT; +#ifdef _DEBUG + dbg_addr_ = "Listening @ "; + dbg_addr_.append(GetLocalAddress().ToString()); +#endif // _DEBUG + } + return err; + } + + AsyncSocket* Accept(SocketAddress *out_addr) { + sockaddr_storage addr_storage; + socklen_t addr_len = sizeof(addr_storage); + sockaddr* addr = reinterpret_cast(&addr_storage); + SOCKET s = ::accept(s_, addr, &addr_len); + UpdateLastError(); + if (s == INVALID_SOCKET) + return NULL; + enabled_events_ |= DE_ACCEPT; + if (out_addr != NULL) + SocketAddressFromSockAddrStorage(addr_storage, out_addr); + return ss_->WrapSocket(s); + } + + int Close() { + if (s_ == INVALID_SOCKET) + return 0; + int err = ::closesocket(s_); + UpdateLastError(); + s_ = INVALID_SOCKET; + state_ = CS_CLOSED; + enabled_events_ = 0; + if (resolver_) { + resolver_->Destroy(false); + resolver_ = NULL; + } + return err; + } + + int EstimateMTU(uint16* mtu) { + SocketAddress addr = GetRemoteAddress(); + if (addr.IsAny()) { + SetError(ENOTCONN); + return -1; + } + +#if defined(WEBRTC_WIN) + // Gets the interface MTU (TTL=1) for the interface used to reach |addr|. + WinPing ping; + if (!ping.IsValid()) { + SetError(EINVAL); // can't think of a better error ID + return -1; + } + int header_size = ICMP_HEADER_SIZE; + if (addr.family() == AF_INET6) { + header_size += IPV6_HEADER_SIZE; + } else if (addr.family() == AF_INET) { + header_size += IP_HEADER_SIZE; + } + + for (int level = 0; PACKET_MAXIMUMS[level + 1] > 0; ++level) { + int32 size = PACKET_MAXIMUMS[level] - header_size; + WinPing::PingResult result = ping.Ping(addr.ipaddr(), size, + ICMP_PING_TIMEOUT_MILLIS, + 1, false); + if (result == WinPing::PING_FAIL) { + SetError(EINVAL); // can't think of a better error ID + return -1; + } else if (result != WinPing::PING_TOO_LARGE) { + *mtu = PACKET_MAXIMUMS[level]; + return 0; + } + } + + ASSERT(false); + return -1; +#elif defined(WEBRTC_MAC) + // No simple way to do this on Mac OS X. + // SIOCGIFMTU would work if we knew which interface would be used, but + // figuring that out is pretty complicated. For now we'll return an error + // and let the caller pick a default MTU. + SetError(EINVAL); + return -1; +#elif defined(WEBRTC_LINUX) + // Gets the path MTU. + int value; + socklen_t vlen = sizeof(value); + int err = getsockopt(s_, IPPROTO_IP, IP_MTU, &value, &vlen); + if (err < 0) { + UpdateLastError(); + return err; + } + + ASSERT((0 <= value) && (value <= 65536)); + *mtu = value; + return 0; +#elif defined(__native_client__) + // Most socket operations, including this, will fail in NaCl's sandbox. + error_ = EACCES; + return -1; +#endif + } + + SocketServer* socketserver() { return ss_; } + + protected: + void OnResolveResult(AsyncResolverInterface* resolver) { + if (resolver != resolver_) { + return; + } + + int error = resolver_->GetError(); + if (error == 0) { + error = DoConnect(resolver_->address()); + } else { + Close(); + } + + if (error) { + SetError(error); + SignalCloseEvent(this, error); + } + } + + void UpdateLastError() { + SetError(LAST_SYSTEM_ERROR); + } + + void MaybeRemapSendError() { +#if defined(WEBRTC_MAC) + // https://developer.apple.com/library/mac/documentation/Darwin/ + // Reference/ManPages/man2/sendto.2.html + // ENOBUFS - The output queue for a network interface is full. + // This generally indicates that the interface has stopped sending, + // but may be caused by transient congestion. + if (GetError() == ENOBUFS) { + SetError(EWOULDBLOCK); + } +#endif + } + + static int TranslateOption(Option opt, int* slevel, int* sopt) { + switch (opt) { + case OPT_DONTFRAGMENT: +#if defined(WEBRTC_WIN) + *slevel = IPPROTO_IP; + *sopt = IP_DONTFRAGMENT; + break; +#elif defined(WEBRTC_MAC) || defined(BSD) || defined(__native_client__) + LOG(LS_WARNING) << "Socket::OPT_DONTFRAGMENT not supported."; + return -1; +#elif defined(WEBRTC_POSIX) + *slevel = IPPROTO_IP; + *sopt = IP_MTU_DISCOVER; + break; +#endif + case OPT_RCVBUF: + *slevel = SOL_SOCKET; + *sopt = SO_RCVBUF; + break; + case OPT_SNDBUF: + *slevel = SOL_SOCKET; + *sopt = SO_SNDBUF; + break; + case OPT_NODELAY: + *slevel = IPPROTO_TCP; + *sopt = TCP_NODELAY; + break; + case OPT_DSCP: + LOG(LS_WARNING) << "Socket::OPT_DSCP not supported."; + return -1; + case OPT_RTP_SENDTIME_EXTN_ID: + return -1; // No logging is necessary as this not a OS socket option. + default: + ASSERT(false); + return -1; + } + return 0; + } + + PhysicalSocketServer* ss_; + SOCKET s_; + uint8 enabled_events_; + bool udp_; + int error_; + // Protects |error_| that is accessed from different threads. + mutable CriticalSection crit_; + ConnState state_; + AsyncResolver* resolver_; + +#ifdef _DEBUG + std::string dbg_addr_; +#endif // _DEBUG; +}; + +#if defined(WEBRTC_POSIX) +class EventDispatcher : public Dispatcher { + public: + EventDispatcher(PhysicalSocketServer* ss) : ss_(ss), fSignaled_(false) { + if (pipe(afd_) < 0) + LOG(LERROR) << "pipe failed"; + ss_->Add(this); + } + + virtual ~EventDispatcher() { + ss_->Remove(this); + close(afd_[0]); + close(afd_[1]); + } + + virtual void Signal() { + CritScope cs(&crit_); + if (!fSignaled_) { + const uint8 b[1] = { 0 }; + if (VERIFY(1 == write(afd_[1], b, sizeof(b)))) { + fSignaled_ = true; + } + } + } + + virtual uint32 GetRequestedEvents() { + return DE_READ; + } + + virtual void OnPreEvent(uint32 ff) { + // It is not possible to perfectly emulate an auto-resetting event with + // pipes. This simulates it by resetting before the event is handled. + + CritScope cs(&crit_); + if (fSignaled_) { + uint8 b[4]; // Allow for reading more than 1 byte, but expect 1. + VERIFY(1 == read(afd_[0], b, sizeof(b))); + fSignaled_ = false; + } + } + + virtual void OnEvent(uint32 ff, int err) { + ASSERT(false); + } + + virtual int GetDescriptor() { + return afd_[0]; + } + + virtual bool IsDescriptorClosed() { + return false; + } + + private: + PhysicalSocketServer *ss_; + int afd_[2]; + bool fSignaled_; + CriticalSection crit_; +}; + +// These two classes use the self-pipe trick to deliver POSIX signals to our +// select loop. This is the only safe, reliable, cross-platform way to do +// non-trivial things with a POSIX signal in an event-driven program (until +// proper pselect() implementations become ubiquitous). + +class PosixSignalHandler { + public: + // POSIX only specifies 32 signals, but in principle the system might have + // more and the programmer might choose to use them, so we size our array + // for 128. + static const int kNumPosixSignals = 128; + + // There is just a single global instance. (Signal handlers do not get any + // sort of user-defined void * parameter, so they can't access anything that + // isn't global.) + static PosixSignalHandler* Instance() { + LIBJINGLE_DEFINE_STATIC_LOCAL(PosixSignalHandler, instance, ()); + return &instance; + } + + // Returns true if the given signal number is set. + bool IsSignalSet(int signum) const { + ASSERT(signum < ARRAY_SIZE(received_signal_)); + if (signum < ARRAY_SIZE(received_signal_)) { + return received_signal_[signum]; + } else { + return false; + } + } + + // Clears the given signal number. + void ClearSignal(int signum) { + ASSERT(signum < ARRAY_SIZE(received_signal_)); + if (signum < ARRAY_SIZE(received_signal_)) { + received_signal_[signum] = false; + } + } + + // Returns the file descriptor to monitor for signal events. + int GetDescriptor() const { + return afd_[0]; + } + + // This is called directly from our real signal handler, so it must be + // signal-handler-safe. That means it cannot assume anything about the + // user-level state of the process, since the handler could be executed at any + // time on any thread. + void OnPosixSignalReceived(int signum) { + if (signum >= ARRAY_SIZE(received_signal_)) { + // We don't have space in our array for this. + return; + } + // Set a flag saying we've seen this signal. + received_signal_[signum] = true; + // Notify application code that we got a signal. + const uint8 b[1] = { 0 }; + if (-1 == write(afd_[1], b, sizeof(b))) { + // Nothing we can do here. If there's an error somehow then there's + // nothing we can safely do from a signal handler. + // No, we can't even safely log it. + // But, we still have to check the return value here. Otherwise, + // GCC 4.4.1 complains ignoring return value. Even (void) doesn't help. + return; + } + } + + private: + PosixSignalHandler() { + if (pipe(afd_) < 0) { + LOG_ERR(LS_ERROR) << "pipe failed"; + return; + } + if (fcntl(afd_[0], F_SETFL, O_NONBLOCK) < 0) { + LOG_ERR(LS_WARNING) << "fcntl #1 failed"; + } + if (fcntl(afd_[1], F_SETFL, O_NONBLOCK) < 0) { + LOG_ERR(LS_WARNING) << "fcntl #2 failed"; + } + memset(const_cast(static_cast(received_signal_)), + 0, + sizeof(received_signal_)); + } + + ~PosixSignalHandler() { + int fd1 = afd_[0]; + int fd2 = afd_[1]; + // We clobber the stored file descriptor numbers here or else in principle + // a signal that happens to be delivered during application termination + // could erroneously write a zero byte to an unrelated file handle in + // OnPosixSignalReceived() if some other file happens to be opened later + // during shutdown and happens to be given the same file descriptor number + // as our pipe had. Unfortunately even with this precaution there is still a + // race where that could occur if said signal happens to be handled + // concurrently with this code and happens to have already read the value of + // afd_[1] from memory before we clobber it, but that's unlikely. + afd_[0] = -1; + afd_[1] = -1; + close(fd1); + close(fd2); + } + + int afd_[2]; + // These are boolean flags that will be set in our signal handler and read + // and cleared from Wait(). There is a race involved in this, but it is + // benign. The signal handler sets the flag before signaling the pipe, so + // we'll never end up blocking in select() while a flag is still true. + // However, if two of the same signal arrive close to each other then it's + // possible that the second time the handler may set the flag while it's still + // true, meaning that signal will be missed. But the first occurrence of it + // will still be handled, so this isn't a problem. + // Volatile is not necessary here for correctness, but this data _is_ volatile + // so I've marked it as such. + volatile uint8 received_signal_[kNumPosixSignals]; +}; + +class PosixSignalDispatcher : public Dispatcher { + public: + PosixSignalDispatcher(PhysicalSocketServer *owner) : owner_(owner) { + owner_->Add(this); + } + + virtual ~PosixSignalDispatcher() { + owner_->Remove(this); + } + + virtual uint32 GetRequestedEvents() { + return DE_READ; + } + + virtual void OnPreEvent(uint32 ff) { + // Events might get grouped if signals come very fast, so we read out up to + // 16 bytes to make sure we keep the pipe empty. + uint8 b[16]; + ssize_t ret = read(GetDescriptor(), b, sizeof(b)); + if (ret < 0) { + LOG_ERR(LS_WARNING) << "Error in read()"; + } else if (ret == 0) { + LOG(LS_WARNING) << "Should have read at least one byte"; + } + } + + virtual void OnEvent(uint32 ff, int err) { + for (int signum = 0; signum < PosixSignalHandler::kNumPosixSignals; + ++signum) { + if (PosixSignalHandler::Instance()->IsSignalSet(signum)) { + PosixSignalHandler::Instance()->ClearSignal(signum); + HandlerMap::iterator i = handlers_.find(signum); + if (i == handlers_.end()) { + // This can happen if a signal is delivered to our process at around + // the same time as we unset our handler for it. It is not an error + // condition, but it's unusual enough to be worth logging. + LOG(LS_INFO) << "Received signal with no handler: " << signum; + } else { + // Otherwise, execute our handler. + (*i->second)(signum); + } + } + } + } + + virtual int GetDescriptor() { + return PosixSignalHandler::Instance()->GetDescriptor(); + } + + virtual bool IsDescriptorClosed() { + return false; + } + + void SetHandler(int signum, void (*handler)(int)) { + handlers_[signum] = handler; + } + + void ClearHandler(int signum) { + handlers_.erase(signum); + } + + bool HasHandlers() { + return !handlers_.empty(); + } + + private: + typedef std::map HandlerMap; + + HandlerMap handlers_; + // Our owner. + PhysicalSocketServer *owner_; +}; + +class SocketDispatcher : public Dispatcher, public PhysicalSocket { + public: + explicit SocketDispatcher(PhysicalSocketServer *ss) : PhysicalSocket(ss) { + } + SocketDispatcher(SOCKET s, PhysicalSocketServer *ss) : PhysicalSocket(ss, s) { + } + + virtual ~SocketDispatcher() { + Close(); + } + + bool Initialize() { + ss_->Add(this); + fcntl(s_, F_SETFL, fcntl(s_, F_GETFL, 0) | O_NONBLOCK); + return true; + } + + virtual bool Create(int type) { + return Create(AF_INET, type); + } + + virtual bool Create(int family, int type) { + // Change the socket to be non-blocking. + if (!PhysicalSocket::Create(family, type)) + return false; + + return Initialize(); + } + + virtual int GetDescriptor() { + return s_; + } + + virtual bool IsDescriptorClosed() { + // We don't have a reliable way of distinguishing end-of-stream + // from readability. So test on each readable call. Is this + // inefficient? Probably. + char ch; + ssize_t res = ::recv(s_, &ch, 1, MSG_PEEK); + if (res > 0) { + // Data available, so not closed. + return false; + } else if (res == 0) { + // EOF, so closed. + return true; + } else { // error + switch (errno) { + // Returned if we've already closed s_. + case EBADF: + // Returned during ungraceful peer shutdown. + case ECONNRESET: + return true; + default: + // Assume that all other errors are just blocking errors, meaning the + // connection is still good but we just can't read from it right now. + // This should only happen when connecting (and at most once), because + // in all other cases this function is only called if the file + // descriptor is already known to be in the readable state. However, + // it's not necessary a problem if we spuriously interpret a + // "connection lost"-type error as a blocking error, because typically + // the next recv() will get EOF, so we'll still eventually notice that + // the socket is closed. + LOG_ERR(LS_WARNING) << "Assuming benign blocking error"; + return false; + } + } + } + + virtual uint32 GetRequestedEvents() { + return enabled_events_; + } + + virtual void OnPreEvent(uint32 ff) { + if ((ff & DE_CONNECT) != 0) + state_ = CS_CONNECTED; + if ((ff & DE_CLOSE) != 0) + state_ = CS_CLOSED; + } + + virtual void OnEvent(uint32 ff, int err) { + // Make sure we deliver connect/accept first. Otherwise, consumers may see + // something like a READ followed by a CONNECT, which would be odd. + if ((ff & DE_CONNECT) != 0) { + enabled_events_ &= ~DE_CONNECT; + SignalConnectEvent(this); + } + if ((ff & DE_ACCEPT) != 0) { + enabled_events_ &= ~DE_ACCEPT; + SignalReadEvent(this); + } + if ((ff & DE_READ) != 0) { + enabled_events_ &= ~DE_READ; + SignalReadEvent(this); + } + if ((ff & DE_WRITE) != 0) { + enabled_events_ &= ~DE_WRITE; + SignalWriteEvent(this); + } + if ((ff & DE_CLOSE) != 0) { + // The socket is now dead to us, so stop checking it. + enabled_events_ = 0; + SignalCloseEvent(this, err); + } + } + + virtual int Close() { + if (s_ == INVALID_SOCKET) + return 0; + + ss_->Remove(this); + return PhysicalSocket::Close(); + } +}; + +class FileDispatcher: public Dispatcher, public AsyncFile { + public: + FileDispatcher(int fd, PhysicalSocketServer *ss) : ss_(ss), fd_(fd) { + set_readable(true); + + ss_->Add(this); + + fcntl(fd_, F_SETFL, fcntl(fd_, F_GETFL, 0) | O_NONBLOCK); + } + + virtual ~FileDispatcher() { + ss_->Remove(this); + } + + SocketServer* socketserver() { return ss_; } + + virtual int GetDescriptor() { + return fd_; + } + + virtual bool IsDescriptorClosed() { + return false; + } + + virtual uint32 GetRequestedEvents() { + return flags_; + } + + virtual void OnPreEvent(uint32 ff) { + } + + virtual void OnEvent(uint32 ff, int err) { + if ((ff & DE_READ) != 0) + SignalReadEvent(this); + if ((ff & DE_WRITE) != 0) + SignalWriteEvent(this); + if ((ff & DE_CLOSE) != 0) + SignalCloseEvent(this, err); + } + + virtual bool readable() { + return (flags_ & DE_READ) != 0; + } + + virtual void set_readable(bool value) { + flags_ = value ? (flags_ | DE_READ) : (flags_ & ~DE_READ); + } + + virtual bool writable() { + return (flags_ & DE_WRITE) != 0; + } + + virtual void set_writable(bool value) { + flags_ = value ? (flags_ | DE_WRITE) : (flags_ & ~DE_WRITE); + } + + private: + PhysicalSocketServer* ss_; + int fd_; + int flags_; +}; + +AsyncFile* PhysicalSocketServer::CreateFile(int fd) { + return new FileDispatcher(fd, this); +} + +#endif // WEBRTC_POSIX + +#if defined(WEBRTC_WIN) +static uint32 FlagsToEvents(uint32 events) { + uint32 ffFD = FD_CLOSE; + if (events & DE_READ) + ffFD |= FD_READ; + if (events & DE_WRITE) + ffFD |= FD_WRITE; + if (events & DE_CONNECT) + ffFD |= FD_CONNECT; + if (events & DE_ACCEPT) + ffFD |= FD_ACCEPT; + return ffFD; +} + +class EventDispatcher : public Dispatcher { + public: + EventDispatcher(PhysicalSocketServer *ss) : ss_(ss) { + hev_ = WSACreateEvent(); + if (hev_) { + ss_->Add(this); + } + } + + ~EventDispatcher() { + if (hev_ != NULL) { + ss_->Remove(this); + WSACloseEvent(hev_); + hev_ = NULL; + } + } + + virtual void Signal() { + if (hev_ != NULL) + WSASetEvent(hev_); + } + + virtual uint32 GetRequestedEvents() { + return 0; + } + + virtual void OnPreEvent(uint32 ff) { + WSAResetEvent(hev_); + } + + virtual void OnEvent(uint32 ff, int err) { + } + + virtual WSAEVENT GetWSAEvent() { + return hev_; + } + + virtual SOCKET GetSocket() { + return INVALID_SOCKET; + } + + virtual bool CheckSignalClose() { return false; } + +private: + PhysicalSocketServer* ss_; + WSAEVENT hev_; +}; + +class SocketDispatcher : public Dispatcher, public PhysicalSocket { + public: + static int next_id_; + int id_; + bool signal_close_; + int signal_err_; + + SocketDispatcher(PhysicalSocketServer* ss) + : PhysicalSocket(ss), + id_(0), + signal_close_(false) { + } + + SocketDispatcher(SOCKET s, PhysicalSocketServer* ss) + : PhysicalSocket(ss, s), + id_(0), + signal_close_(false) { + } + + virtual ~SocketDispatcher() { + Close(); + } + + bool Initialize() { + ASSERT(s_ != INVALID_SOCKET); + // Must be a non-blocking + u_long argp = 1; + ioctlsocket(s_, FIONBIO, &argp); + ss_->Add(this); + return true; + } + + virtual bool Create(int type) { + return Create(AF_INET, type); + } + + virtual bool Create(int family, int type) { + // Create socket + if (!PhysicalSocket::Create(family, type)) + return false; + + if (!Initialize()) + return false; + + do { id_ = ++next_id_; } while (id_ == 0); + return true; + } + + virtual int Close() { + if (s_ == INVALID_SOCKET) + return 0; + + id_ = 0; + signal_close_ = false; + ss_->Remove(this); + return PhysicalSocket::Close(); + } + + virtual uint32 GetRequestedEvents() { + return enabled_events_; + } + + virtual void OnPreEvent(uint32 ff) { + if ((ff & DE_CONNECT) != 0) + state_ = CS_CONNECTED; + // We set CS_CLOSED from CheckSignalClose. + } + + virtual void OnEvent(uint32 ff, int err) { + int cache_id = id_; + // Make sure we deliver connect/accept first. Otherwise, consumers may see + // something like a READ followed by a CONNECT, which would be odd. + if (((ff & DE_CONNECT) != 0) && (id_ == cache_id)) { + if (ff != DE_CONNECT) + LOG(LS_VERBOSE) << "Signalled with DE_CONNECT: " << ff; + enabled_events_ &= ~DE_CONNECT; +#ifdef _DEBUG + dbg_addr_ = "Connected @ "; + dbg_addr_.append(GetRemoteAddress().ToString()); +#endif // _DEBUG + SignalConnectEvent(this); + } + if (((ff & DE_ACCEPT) != 0) && (id_ == cache_id)) { + enabled_events_ &= ~DE_ACCEPT; + SignalReadEvent(this); + } + if ((ff & DE_READ) != 0) { + enabled_events_ &= ~DE_READ; + SignalReadEvent(this); + } + if (((ff & DE_WRITE) != 0) && (id_ == cache_id)) { + enabled_events_ &= ~DE_WRITE; + SignalWriteEvent(this); + } + if (((ff & DE_CLOSE) != 0) && (id_ == cache_id)) { + signal_close_ = true; + signal_err_ = err; + } + } + + virtual WSAEVENT GetWSAEvent() { + return WSA_INVALID_EVENT; + } + + virtual SOCKET GetSocket() { + return s_; + } + + virtual bool CheckSignalClose() { + if (!signal_close_) + return false; + + char ch; + if (recv(s_, &ch, 1, MSG_PEEK) > 0) + return false; + + state_ = CS_CLOSED; + signal_close_ = false; + SignalCloseEvent(this, signal_err_); + return true; + } +}; + +int SocketDispatcher::next_id_ = 0; + +#endif // WEBRTC_WIN + +// Sets the value of a boolean value to false when signaled. +class Signaler : public EventDispatcher { + public: + Signaler(PhysicalSocketServer* ss, bool* pf) + : EventDispatcher(ss), pf_(pf) { + } + virtual ~Signaler() { } + + void OnEvent(uint32 ff, int err) { + if (pf_) + *pf_ = false; + } + + private: + bool *pf_; +}; + +PhysicalSocketServer::PhysicalSocketServer() + : fWait_(false) { + signal_wakeup_ = new Signaler(this, &fWait_); +#if defined(WEBRTC_WIN) + socket_ev_ = WSACreateEvent(); +#endif +} + +PhysicalSocketServer::~PhysicalSocketServer() { +#if defined(WEBRTC_WIN) + WSACloseEvent(socket_ev_); +#endif +#if defined(WEBRTC_POSIX) + signal_dispatcher_.reset(); +#endif + delete signal_wakeup_; + ASSERT(dispatchers_.empty()); +} + +void PhysicalSocketServer::WakeUp() { + signal_wakeup_->Signal(); +} + +Socket* PhysicalSocketServer::CreateSocket(int type) { + return CreateSocket(AF_INET, type); +} + +Socket* PhysicalSocketServer::CreateSocket(int family, int type) { + PhysicalSocket* socket = new PhysicalSocket(this); + if (socket->Create(family, type)) { + return socket; + } else { + delete socket; + return 0; + } +} + +AsyncSocket* PhysicalSocketServer::CreateAsyncSocket(int type) { + return CreateAsyncSocket(AF_INET, type); +} + +AsyncSocket* PhysicalSocketServer::CreateAsyncSocket(int family, int type) { + SocketDispatcher* dispatcher = new SocketDispatcher(this); + if (dispatcher->Create(family, type)) { + return dispatcher; + } else { + delete dispatcher; + return 0; + } +} + +AsyncSocket* PhysicalSocketServer::WrapSocket(SOCKET s) { + SocketDispatcher* dispatcher = new SocketDispatcher(s, this); + if (dispatcher->Initialize()) { + return dispatcher; + } else { + delete dispatcher; + return 0; + } +} + +void PhysicalSocketServer::Add(Dispatcher *pdispatcher) { + CritScope cs(&crit_); + // Prevent duplicates. This can cause dead dispatchers to stick around. + DispatcherList::iterator pos = std::find(dispatchers_.begin(), + dispatchers_.end(), + pdispatcher); + if (pos != dispatchers_.end()) + return; + dispatchers_.push_back(pdispatcher); +} + +void PhysicalSocketServer::Remove(Dispatcher *pdispatcher) { + CritScope cs(&crit_); + DispatcherList::iterator pos = std::find(dispatchers_.begin(), + dispatchers_.end(), + pdispatcher); + // We silently ignore duplicate calls to Add, so we should silently ignore + // the (expected) symmetric calls to Remove. Note that this may still hide + // a real issue, so we at least log a warning about it. + if (pos == dispatchers_.end()) { + LOG(LS_WARNING) << "PhysicalSocketServer asked to remove a unknown " + << "dispatcher, potentially from a duplicate call to Add."; + return; + } + size_t index = pos - dispatchers_.begin(); + dispatchers_.erase(pos); + for (IteratorList::iterator it = iterators_.begin(); it != iterators_.end(); + ++it) { + if (index < **it) { + --**it; + } + } +} + +#if defined(WEBRTC_POSIX) +bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { + // Calculate timing information + + struct timeval *ptvWait = NULL; + struct timeval tvWait; + struct timeval tvStop; + if (cmsWait != kForever) { + // Calculate wait timeval + tvWait.tv_sec = cmsWait / 1000; + tvWait.tv_usec = (cmsWait % 1000) * 1000; + ptvWait = &tvWait; + + // Calculate when to return in a timeval + gettimeofday(&tvStop, NULL); + tvStop.tv_sec += tvWait.tv_sec; + tvStop.tv_usec += tvWait.tv_usec; + if (tvStop.tv_usec >= 1000000) { + tvStop.tv_usec -= 1000000; + tvStop.tv_sec += 1; + } + } + + // Zero all fd_sets. Don't need to do this inside the loop since + // select() zeros the descriptors not signaled + + fd_set fdsRead; + FD_ZERO(&fdsRead); + fd_set fdsWrite; + FD_ZERO(&fdsWrite); + // Explicitly unpoison these FDs on MemorySanitizer which doesn't handle the + // inline assembly in FD_ZERO. + // http://crbug.com/344505 +#ifdef MEMORY_SANITIZER + __msan_unpoison(&fdsRead, sizeof(fdsRead)); + __msan_unpoison(&fdsWrite, sizeof(fdsWrite)); +#endif + + fWait_ = true; + + while (fWait_) { + int fdmax = -1; + { + CritScope cr(&crit_); + for (size_t i = 0; i < dispatchers_.size(); ++i) { + // Query dispatchers for read and write wait state + Dispatcher *pdispatcher = dispatchers_[i]; + ASSERT(pdispatcher); + if (!process_io && (pdispatcher != signal_wakeup_)) + continue; + int fd = pdispatcher->GetDescriptor(); + if (fd > fdmax) + fdmax = fd; + + uint32 ff = pdispatcher->GetRequestedEvents(); + if (ff & (DE_READ | DE_ACCEPT)) + FD_SET(fd, &fdsRead); + if (ff & (DE_WRITE | DE_CONNECT)) + FD_SET(fd, &fdsWrite); + } + } + + // Wait then call handlers as appropriate + // < 0 means error + // 0 means timeout + // > 0 means count of descriptors ready + int n = select(fdmax + 1, &fdsRead, &fdsWrite, NULL, ptvWait); + + // If error, return error. + if (n < 0) { + if (errno != EINTR) { + LOG_E(LS_ERROR, EN, errno) << "select"; + return false; + } + // Else ignore the error and keep going. If this EINTR was for one of the + // signals managed by this PhysicalSocketServer, the + // PosixSignalDeliveryDispatcher will be in the signaled state in the next + // iteration. + } else if (n == 0) { + // If timeout, return success + return true; + } else { + // We have signaled descriptors + CritScope cr(&crit_); + for (size_t i = 0; i < dispatchers_.size(); ++i) { + Dispatcher *pdispatcher = dispatchers_[i]; + int fd = pdispatcher->GetDescriptor(); + uint32 ff = 0; + int errcode = 0; + + // Reap any error code, which can be signaled through reads or writes. + // TODO: Should we set errcode if getsockopt fails? + if (FD_ISSET(fd, &fdsRead) || FD_ISSET(fd, &fdsWrite)) { + socklen_t len = sizeof(errcode); + ::getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &len); + } + + // Check readable descriptors. If we're waiting on an accept, signal + // that. Otherwise we're waiting for data, check to see if we're + // readable or really closed. + // TODO: Only peek at TCP descriptors. + if (FD_ISSET(fd, &fdsRead)) { + FD_CLR(fd, &fdsRead); + if (pdispatcher->GetRequestedEvents() & DE_ACCEPT) { + ff |= DE_ACCEPT; + } else if (errcode || pdispatcher->IsDescriptorClosed()) { + ff |= DE_CLOSE; + } else { + ff |= DE_READ; + } + } + + // Check writable descriptors. If we're waiting on a connect, detect + // success versus failure by the reaped error code. + if (FD_ISSET(fd, &fdsWrite)) { + FD_CLR(fd, &fdsWrite); + if (pdispatcher->GetRequestedEvents() & DE_CONNECT) { + if (!errcode) { + ff |= DE_CONNECT; + } else { + ff |= DE_CLOSE; + } + } else { + ff |= DE_WRITE; + } + } + + // Tell the descriptor about the event. + if (ff != 0) { + pdispatcher->OnPreEvent(ff); + pdispatcher->OnEvent(ff, errcode); + } + } + } + + // Recalc the time remaining to wait. Doing it here means it doesn't get + // calced twice the first time through the loop + if (ptvWait) { + ptvWait->tv_sec = 0; + ptvWait->tv_usec = 0; + struct timeval tvT; + gettimeofday(&tvT, NULL); + if ((tvStop.tv_sec > tvT.tv_sec) + || ((tvStop.tv_sec == tvT.tv_sec) + && (tvStop.tv_usec > tvT.tv_usec))) { + ptvWait->tv_sec = tvStop.tv_sec - tvT.tv_sec; + ptvWait->tv_usec = tvStop.tv_usec - tvT.tv_usec; + if (ptvWait->tv_usec < 0) { + ASSERT(ptvWait->tv_sec > 0); + ptvWait->tv_usec += 1000000; + ptvWait->tv_sec -= 1; + } + } + } + } + + return true; +} + +static void GlobalSignalHandler(int signum) { + PosixSignalHandler::Instance()->OnPosixSignalReceived(signum); +} + +bool PhysicalSocketServer::SetPosixSignalHandler(int signum, + void (*handler)(int)) { + // If handler is SIG_IGN or SIG_DFL then clear our user-level handler, + // otherwise set one. + if (handler == SIG_IGN || handler == SIG_DFL) { + if (!InstallSignal(signum, handler)) { + return false; + } + if (signal_dispatcher_) { + signal_dispatcher_->ClearHandler(signum); + if (!signal_dispatcher_->HasHandlers()) { + signal_dispatcher_.reset(); + } + } + } else { + if (!signal_dispatcher_) { + signal_dispatcher_.reset(new PosixSignalDispatcher(this)); + } + signal_dispatcher_->SetHandler(signum, handler); + if (!InstallSignal(signum, &GlobalSignalHandler)) { + return false; + } + } + return true; +} + +Dispatcher* PhysicalSocketServer::signal_dispatcher() { + return signal_dispatcher_.get(); +} + +bool PhysicalSocketServer::InstallSignal(int signum, void (*handler)(int)) { + struct sigaction act; + // It doesn't really matter what we set this mask to. + if (sigemptyset(&act.sa_mask) != 0) { + LOG_ERR(LS_ERROR) << "Couldn't set mask"; + return false; + } + act.sa_handler = handler; +#if !defined(__native_client__) + // Use SA_RESTART so that our syscalls don't get EINTR, since we don't need it + // and it's a nuisance. Though some syscalls still return EINTR and there's no + // real standard for which ones. :( + act.sa_flags = SA_RESTART; +#else + act.sa_flags = 0; +#endif + if (sigaction(signum, &act, NULL) != 0) { + LOG_ERR(LS_ERROR) << "Couldn't set sigaction"; + return false; + } + return true; +} +#endif // WEBRTC_POSIX + +#if defined(WEBRTC_WIN) +bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { + int cmsTotal = cmsWait; + int cmsElapsed = 0; + uint32 msStart = Time(); + + fWait_ = true; + while (fWait_) { + std::vector events; + std::vector event_owners; + + events.push_back(socket_ev_); + + { + CritScope cr(&crit_); + size_t i = 0; + iterators_.push_back(&i); + // Don't track dispatchers_.size(), because we want to pick up any new + // dispatchers that were added while processing the loop. + while (i < dispatchers_.size()) { + Dispatcher* disp = dispatchers_[i++]; + if (!process_io && (disp != signal_wakeup_)) + continue; + SOCKET s = disp->GetSocket(); + if (disp->CheckSignalClose()) { + // We just signalled close, don't poll this socket + } else if (s != INVALID_SOCKET) { + WSAEventSelect(s, + events[0], + FlagsToEvents(disp->GetRequestedEvents())); + } else { + events.push_back(disp->GetWSAEvent()); + event_owners.push_back(disp); + } + } + ASSERT(iterators_.back() == &i); + iterators_.pop_back(); + } + + // Which is shorter, the delay wait or the asked wait? + + int cmsNext; + if (cmsWait == kForever) { + cmsNext = cmsWait; + } else { + cmsNext = _max(0, cmsTotal - cmsElapsed); + } + + // Wait for one of the events to signal + DWORD dw = WSAWaitForMultipleEvents(static_cast(events.size()), + &events[0], + false, + cmsNext, + false); + + if (dw == WSA_WAIT_FAILED) { + // Failed? + // TODO: need a better strategy than this! + WSAGetLastError(); + ASSERT(false); + return false; + } else if (dw == WSA_WAIT_TIMEOUT) { + // Timeout? + return true; + } else { + // Figure out which one it is and call it + CritScope cr(&crit_); + int index = dw - WSA_WAIT_EVENT_0; + if (index > 0) { + --index; // The first event is the socket event + event_owners[index]->OnPreEvent(0); + event_owners[index]->OnEvent(0, 0); + } else if (process_io) { + size_t i = 0, end = dispatchers_.size(); + iterators_.push_back(&i); + iterators_.push_back(&end); // Don't iterate over new dispatchers. + while (i < end) { + Dispatcher* disp = dispatchers_[i++]; + SOCKET s = disp->GetSocket(); + if (s == INVALID_SOCKET) + continue; + + WSANETWORKEVENTS wsaEvents; + int err = WSAEnumNetworkEvents(s, events[0], &wsaEvents); + if (err == 0) { + +#if LOGGING + { + if ((wsaEvents.lNetworkEvents & FD_READ) && + wsaEvents.iErrorCode[FD_READ_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_READ_BIT error " + << wsaEvents.iErrorCode[FD_READ_BIT]; + } + if ((wsaEvents.lNetworkEvents & FD_WRITE) && + wsaEvents.iErrorCode[FD_WRITE_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_WRITE_BIT error " + << wsaEvents.iErrorCode[FD_WRITE_BIT]; + } + if ((wsaEvents.lNetworkEvents & FD_CONNECT) && + wsaEvents.iErrorCode[FD_CONNECT_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_CONNECT_BIT error " + << wsaEvents.iErrorCode[FD_CONNECT_BIT]; + } + if ((wsaEvents.lNetworkEvents & FD_ACCEPT) && + wsaEvents.iErrorCode[FD_ACCEPT_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_ACCEPT_BIT error " + << wsaEvents.iErrorCode[FD_ACCEPT_BIT]; + } + if ((wsaEvents.lNetworkEvents & FD_CLOSE) && + wsaEvents.iErrorCode[FD_CLOSE_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_CLOSE_BIT error " + << wsaEvents.iErrorCode[FD_CLOSE_BIT]; + } + } +#endif + uint32 ff = 0; + int errcode = 0; + if (wsaEvents.lNetworkEvents & FD_READ) + ff |= DE_READ; + if (wsaEvents.lNetworkEvents & FD_WRITE) + ff |= DE_WRITE; + if (wsaEvents.lNetworkEvents & FD_CONNECT) { + if (wsaEvents.iErrorCode[FD_CONNECT_BIT] == 0) { + ff |= DE_CONNECT; + } else { + ff |= DE_CLOSE; + errcode = wsaEvents.iErrorCode[FD_CONNECT_BIT]; + } + } + if (wsaEvents.lNetworkEvents & FD_ACCEPT) + ff |= DE_ACCEPT; + if (wsaEvents.lNetworkEvents & FD_CLOSE) { + ff |= DE_CLOSE; + errcode = wsaEvents.iErrorCode[FD_CLOSE_BIT]; + } + if (ff != 0) { + disp->OnPreEvent(ff); + disp->OnEvent(ff, errcode); + } + } + } + ASSERT(iterators_.back() == &end); + iterators_.pop_back(); + ASSERT(iterators_.back() == &i); + iterators_.pop_back(); + } + + // Reset the network event until new activity occurs + WSAResetEvent(socket_ev_); + } + + // Break? + if (!fWait_) + break; + cmsElapsed = TimeSince(msStart); + if ((cmsWait != kForever) && (cmsElapsed >= cmsWait)) { + break; + } + } + + // Done + return true; +} +#endif // WEBRTC_WIN + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/physicalsocketserver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/physicalsocketserver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/physicalsocketserver.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/physicalsocketserver.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,120 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_PHYSICALSOCKETSERVER_H__ +#define WEBRTC_BASE_PHYSICALSOCKETSERVER_H__ + +#include + +#include "webrtc/base/asyncfile.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/socketserver.h" +#include "webrtc/base/criticalsection.h" + +#if defined(WEBRTC_POSIX) +typedef int SOCKET; +#endif // WEBRTC_POSIX + +namespace rtc { + +// Event constants for the Dispatcher class. +enum DispatcherEvent { + DE_READ = 0x0001, + DE_WRITE = 0x0002, + DE_CONNECT = 0x0004, + DE_CLOSE = 0x0008, + DE_ACCEPT = 0x0010, +}; + +class Signaler; +#if defined(WEBRTC_POSIX) +class PosixSignalDispatcher; +#endif + +class Dispatcher { + public: + virtual ~Dispatcher() {} + virtual uint32 GetRequestedEvents() = 0; + virtual void OnPreEvent(uint32 ff) = 0; + virtual void OnEvent(uint32 ff, int err) = 0; +#if defined(WEBRTC_WIN) + virtual WSAEVENT GetWSAEvent() = 0; + virtual SOCKET GetSocket() = 0; + virtual bool CheckSignalClose() = 0; +#elif defined(WEBRTC_POSIX) + virtual int GetDescriptor() = 0; + virtual bool IsDescriptorClosed() = 0; +#endif +}; + +// A socket server that provides the real sockets of the underlying OS. +class PhysicalSocketServer : public SocketServer { + public: + PhysicalSocketServer(); + virtual ~PhysicalSocketServer(); + + // SocketFactory: + virtual Socket* CreateSocket(int type); + virtual Socket* CreateSocket(int family, int type); + + virtual AsyncSocket* CreateAsyncSocket(int type); + virtual AsyncSocket* CreateAsyncSocket(int family, int type); + + // Internal Factory for Accept + AsyncSocket* WrapSocket(SOCKET s); + + // SocketServer: + virtual bool Wait(int cms, bool process_io); + virtual void WakeUp(); + + void Add(Dispatcher* dispatcher); + void Remove(Dispatcher* dispatcher); + +#if defined(WEBRTC_POSIX) + AsyncFile* CreateFile(int fd); + + // Sets the function to be executed in response to the specified POSIX signal. + // The function is executed from inside Wait() using the "self-pipe trick"-- + // regardless of which thread receives the signal--and hence can safely + // manipulate user-level data structures. + // "handler" may be SIG_IGN, SIG_DFL, or a user-specified function, just like + // with signal(2). + // Only one PhysicalSocketServer should have user-level signal handlers. + // Dispatching signals on multiple PhysicalSocketServers is not reliable. + // The signal mask is not modified. It is the caller's responsibily to + // maintain it as desired. + virtual bool SetPosixSignalHandler(int signum, void (*handler)(int)); + + protected: + Dispatcher* signal_dispatcher(); +#endif + + private: + typedef std::vector DispatcherList; + typedef std::vector IteratorList; + +#if defined(WEBRTC_POSIX) + static bool InstallSignal(int signum, void (*handler)(int)); + + scoped_ptr signal_dispatcher_; +#endif + DispatcherList dispatchers_; + IteratorList iterators_; + Signaler* signal_wakeup_; + CriticalSection crit_; + bool fWait_; +#if defined(WEBRTC_WIN) + WSAEVENT socket_ev_; +#endif +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_PHYSICALSOCKETSERVER_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/physicalsocketserver_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/physicalsocketserver_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/physicalsocketserver_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/physicalsocketserver_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,281 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include + +#include "webrtc/base/gunit.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/physicalsocketserver.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/socket_unittest.h" +#include "webrtc/base/testutils.h" +#include "webrtc/base/thread.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace rtc { + +class PhysicalSocketTest : public SocketTest { +}; + +TEST_F(PhysicalSocketTest, TestConnectIPv4) { + SocketTest::TestConnectIPv4(); +} + +TEST_F(PhysicalSocketTest, TestConnectIPv6) { + SocketTest::TestConnectIPv6(); +} + +TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupIPv4) { + SocketTest::TestConnectWithDnsLookupIPv4(); +} + +TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupIPv6) { + SocketTest::TestConnectWithDnsLookupIPv6(); +} + +TEST_F(PhysicalSocketTest, TestConnectFailIPv4) { + SocketTest::TestConnectFailIPv4(); +} + +TEST_F(PhysicalSocketTest, TestConnectFailIPv6) { + SocketTest::TestConnectFailIPv6(); +} + +TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupFailIPv4) { + SocketTest::TestConnectWithDnsLookupFailIPv4(); +} + + +TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupFailIPv6) { + SocketTest::TestConnectWithDnsLookupFailIPv6(); +} + + +TEST_F(PhysicalSocketTest, TestConnectWithClosedSocketIPv4) { + SocketTest::TestConnectWithClosedSocketIPv4(); +} + +TEST_F(PhysicalSocketTest, TestConnectWithClosedSocketIPv6) { + SocketTest::TestConnectWithClosedSocketIPv6(); +} + +TEST_F(PhysicalSocketTest, TestConnectWhileNotClosedIPv4) { + SocketTest::TestConnectWhileNotClosedIPv4(); +} + +TEST_F(PhysicalSocketTest, TestConnectWhileNotClosedIPv6) { + SocketTest::TestConnectWhileNotClosedIPv6(); +} + +TEST_F(PhysicalSocketTest, TestServerCloseDuringConnectIPv4) { + SocketTest::TestServerCloseDuringConnectIPv4(); +} + +TEST_F(PhysicalSocketTest, TestServerCloseDuringConnectIPv6) { + SocketTest::TestServerCloseDuringConnectIPv6(); +} + +TEST_F(PhysicalSocketTest, TestClientCloseDuringConnectIPv4) { + SocketTest::TestClientCloseDuringConnectIPv4(); +} + +TEST_F(PhysicalSocketTest, TestClientCloseDuringConnectIPv6) { + SocketTest::TestClientCloseDuringConnectIPv6(); +} + +TEST_F(PhysicalSocketTest, TestServerCloseIPv4) { + SocketTest::TestServerCloseIPv4(); +} + +TEST_F(PhysicalSocketTest, TestServerCloseIPv6) { + SocketTest::TestServerCloseIPv6(); +} + +TEST_F(PhysicalSocketTest, TestCloseInClosedCallbackIPv4) { + SocketTest::TestCloseInClosedCallbackIPv4(); +} + +TEST_F(PhysicalSocketTest, TestCloseInClosedCallbackIPv6) { + SocketTest::TestCloseInClosedCallbackIPv6(); +} + +TEST_F(PhysicalSocketTest, TestSocketServerWaitIPv4) { + SocketTest::TestSocketServerWaitIPv4(); +} + +TEST_F(PhysicalSocketTest, TestSocketServerWaitIPv6) { + SocketTest::TestSocketServerWaitIPv6(); +} + +TEST_F(PhysicalSocketTest, TestTcpIPv4) { + SocketTest::TestTcpIPv4(); +} + +TEST_F(PhysicalSocketTest, TestTcpIPv6) { + SocketTest::TestTcpIPv6(); +} + +TEST_F(PhysicalSocketTest, TestUdpIPv4) { + SocketTest::TestUdpIPv4(); +} + +TEST_F(PhysicalSocketTest, TestUdpIPv6) { + SocketTest::TestUdpIPv6(); +} + +// Disable for TSan v2, see +// https://code.google.com/p/webrtc/issues/detail?id=3498 for details. +#if !defined(THREAD_SANITIZER) + +TEST_F(PhysicalSocketTest, TestUdpReadyToSendIPv4) { + SocketTest::TestUdpReadyToSendIPv4(); +} + +#endif // if !defined(THREAD_SANITIZER) + +TEST_F(PhysicalSocketTest, TestUdpReadyToSendIPv6) { + SocketTest::TestUdpReadyToSendIPv6(); +} + +TEST_F(PhysicalSocketTest, TestGetSetOptionsIPv4) { + SocketTest::TestGetSetOptionsIPv4(); +} + +TEST_F(PhysicalSocketTest, TestGetSetOptionsIPv6) { + SocketTest::TestGetSetOptionsIPv6(); +} + +#if defined(WEBRTC_POSIX) + +class PosixSignalDeliveryTest : public testing::Test { + public: + static void RecordSignal(int signum) { + signals_received_.push_back(signum); + signaled_thread_ = Thread::Current(); + } + + protected: + void SetUp() { + ss_.reset(new PhysicalSocketServer()); + } + + void TearDown() { + ss_.reset(NULL); + signals_received_.clear(); + signaled_thread_ = NULL; + } + + bool ExpectSignal(int signum) { + if (signals_received_.empty()) { + LOG(LS_ERROR) << "ExpectSignal(): No signal received"; + return false; + } + if (signals_received_[0] != signum) { + LOG(LS_ERROR) << "ExpectSignal(): Received signal " << + signals_received_[0] << ", expected " << signum; + return false; + } + signals_received_.erase(signals_received_.begin()); + return true; + } + + bool ExpectNone() { + bool ret = signals_received_.empty(); + if (!ret) { + LOG(LS_ERROR) << "ExpectNone(): Received signal " << signals_received_[0] + << ", expected none"; + } + return ret; + } + + static std::vector signals_received_; + static Thread *signaled_thread_; + + scoped_ptr ss_; +}; + +std::vector PosixSignalDeliveryTest::signals_received_; +Thread *PosixSignalDeliveryTest::signaled_thread_ = NULL; + +// Test receiving a synchronous signal while not in Wait() and then entering +// Wait() afterwards. +TEST_F(PosixSignalDeliveryTest, RaiseThenWait) { + ASSERT_TRUE(ss_->SetPosixSignalHandler(SIGTERM, &RecordSignal)); + raise(SIGTERM); + EXPECT_TRUE(ss_->Wait(0, true)); + EXPECT_TRUE(ExpectSignal(SIGTERM)); + EXPECT_TRUE(ExpectNone()); +} + +// Test that we can handle getting tons of repeated signals and that we see all +// the different ones. +TEST_F(PosixSignalDeliveryTest, InsanelyManySignals) { + ss_->SetPosixSignalHandler(SIGTERM, &RecordSignal); + ss_->SetPosixSignalHandler(SIGINT, &RecordSignal); + for (int i = 0; i < 10000; ++i) { + raise(SIGTERM); + } + raise(SIGINT); + EXPECT_TRUE(ss_->Wait(0, true)); + // Order will be lowest signal numbers first. + EXPECT_TRUE(ExpectSignal(SIGINT)); + EXPECT_TRUE(ExpectSignal(SIGTERM)); + EXPECT_TRUE(ExpectNone()); +} + +// Test that a signal during a Wait() call is detected. +TEST_F(PosixSignalDeliveryTest, SignalDuringWait) { + ss_->SetPosixSignalHandler(SIGALRM, &RecordSignal); + alarm(1); + EXPECT_TRUE(ss_->Wait(1500, true)); + EXPECT_TRUE(ExpectSignal(SIGALRM)); + EXPECT_TRUE(ExpectNone()); +} + +class RaiseSigTermRunnable : public Runnable { + void Run(Thread *thread) { + thread->socketserver()->Wait(1000, false); + + // Allow SIGTERM. This will be the only thread with it not masked so it will + // be delivered to us. + sigset_t mask; + sigemptyset(&mask); + pthread_sigmask(SIG_SETMASK, &mask, NULL); + + // Raise it. + raise(SIGTERM); + } +}; + +// Test that it works no matter what thread the kernel chooses to give the +// signal to (since it's not guaranteed to be the one that Wait() runs on). +TEST_F(PosixSignalDeliveryTest, SignalOnDifferentThread) { + ss_->SetPosixSignalHandler(SIGTERM, &RecordSignal); + // Mask out SIGTERM so that it can't be delivered to this thread. + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGTERM); + EXPECT_EQ(0, pthread_sigmask(SIG_SETMASK, &mask, NULL)); + // Start a new thread that raises it. It will have to be delivered to that + // thread. Our implementation should safely handle it and dispatch + // RecordSignal() on this thread. + scoped_ptr thread(new Thread()); + scoped_ptr runnable(new RaiseSigTermRunnable()); + thread->Start(runnable.get()); + EXPECT_TRUE(ss_->Wait(1500, true)); + EXPECT_TRUE(ExpectSignal(SIGTERM)); + EXPECT_EQ(Thread::Current(), signaled_thread_); + EXPECT_TRUE(ExpectNone()); +} + +#endif + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/platform_file.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/platform_file.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/platform_file.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/platform_file.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,49 @@ +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/platform_file.h" + +#if defined(WEBRTC_WIN) +#include +#else +#include +#endif + +namespace rtc { + +#if defined(WEBRTC_WIN) +const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE; + +FILE* FdopenPlatformFileForWriting(PlatformFile file) { + if (file == kInvalidPlatformFileValue) + return NULL; + int fd = _open_osfhandle(reinterpret_cast(file), 0); + if (fd < 0) + return NULL; + + return _fdopen(fd, "w"); +} + +bool ClosePlatformFile(PlatformFile file) { + return CloseHandle(file) != 0; +} +#else +const PlatformFile kInvalidPlatformFileValue = -1; + +FILE* FdopenPlatformFileForWriting(PlatformFile file) { + return fdopen(file, "w"); +} + +bool ClosePlatformFile(PlatformFile file) { + return close(file); +} +#endif + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/platform_file.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/platform_file.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/platform_file.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/platform_file.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,44 @@ +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_PLATFORM_FILE_H_ +#define WEBRTC_BASE_PLATFORM_FILE_H_ + +#include + +#if defined(WEBRTC_WIN) +#include +#endif + +namespace rtc { + +#if defined(WEBRTC_WIN) +typedef HANDLE PlatformFile; +#elif defined(WEBRTC_POSIX) +typedef int PlatformFile; +#else +#error Unsupported platform +#endif + +extern const PlatformFile kInvalidPlatformFileValue; + +// Associates a standard FILE stream with an existing PlatformFile. +// Note that after this function has returned a valid FILE stream, +// the PlatformFile should no longer be used. +FILE* FdopenPlatformFileForWriting(PlatformFile file); + +// Closes a PlatformFile. +// Don't use ClosePlatformFile to close a file opened with FdopenPlatformFile. +// Use fclose instead. +bool ClosePlatformFile(PlatformFile file); + +} // namespace rtc + +#endif // WEBRTC_BASE_PLATFORM_FILE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/posix.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/posix.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/posix.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/posix.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,131 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/posix.h" + +#include +#include +#include + +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) +#include "webrtc/base/linuxfdwalk.h" +#endif +#include "webrtc/base/logging.h" + +namespace rtc { + +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) +static void closefds(void *close_errors, int fd) { + if (fd <= 2) { + // We leave stdin/out/err open to the browser's terminal, if any. + return; + } + if (close(fd) < 0) { + *static_cast(close_errors) = true; + } +} +#endif + +enum { + EXIT_FLAG_CHDIR_ERRORS = 1 << 0, +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) + EXIT_FLAG_FDWALK_ERRORS = 1 << 1, + EXIT_FLAG_CLOSE_ERRORS = 1 << 2, +#endif + EXIT_FLAG_SECOND_FORK_FAILED = 1 << 3, +}; + +bool RunAsDaemon(const char *file, const char *const argv[]) { + // Fork intermediate child to daemonize. + pid_t pid = fork(); + if (pid < 0) { + LOG_ERR(LS_ERROR) << "fork()"; + return false; + } else if (!pid) { + // Child. + + // We try to close all fds and change directory to /, but if that fails we + // keep going because it's not critical. + int exit_code = 0; + if (chdir("/") < 0) { + exit_code |= EXIT_FLAG_CHDIR_ERRORS; + } +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) + bool close_errors = false; + if (fdwalk(&closefds, &close_errors) < 0) { + exit_code |= EXIT_FLAG_FDWALK_ERRORS; + } + if (close_errors) { + exit_code |= EXIT_FLAG_CLOSE_ERRORS; + } +#endif + + // Fork again to become a daemon. + pid = fork(); + // It is important that everything here use _exit() and not exit(), because + // exit() would call the destructors of all global variables in the whole + // process, which is both unnecessary and unsafe. + if (pid < 0) { + exit_code |= EXIT_FLAG_SECOND_FORK_FAILED; + _exit(exit_code); // if second fork failed + } else if (!pid) { + // Child. + // Successfully daemonized. Run command. + // WEBRTC_POSIX requires the args to be typed as non-const for historical + // reasons, but it mandates that the actual implementation be const, so + // the cast is safe. + execvp(file, const_cast(argv)); + _exit(255); // if execvp failed + } + + // Parent. + // Successfully spawned process, but report any problems to the parent where + // we can log them. + _exit(exit_code); + } + + // Parent. Reap intermediate child. + int status; + pid_t child = waitpid(pid, &status, 0); + if (child < 0) { + LOG_ERR(LS_ERROR) << "Error in waitpid()"; + return false; + } + if (child != pid) { + // Should never happen (see man page). + LOG(LS_ERROR) << "waitpid() chose wrong child???"; + return false; + } + if (!WIFEXITED(status)) { + LOG(LS_ERROR) << "Intermediate child killed uncleanly"; // Probably crashed + return false; + } + + int exit_code = WEXITSTATUS(status); + if (exit_code & EXIT_FLAG_CHDIR_ERRORS) { + LOG(LS_WARNING) << "Child reported probles calling chdir()"; + } +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) + if (exit_code & EXIT_FLAG_FDWALK_ERRORS) { + LOG(LS_WARNING) << "Child reported problems calling fdwalk()"; + } + if (exit_code & EXIT_FLAG_CLOSE_ERRORS) { + LOG(LS_WARNING) << "Child reported problems calling close()"; + } +#endif + if (exit_code & EXIT_FLAG_SECOND_FORK_FAILED) { + LOG(LS_ERROR) << "Failed to daemonize"; + // This means the command was not launched, so failure. + return false; + } + return true; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/posix.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/posix.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/posix.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/posix.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_POSIX_H_ +#define WEBRTC_BASE_POSIX_H_ + +namespace rtc { + +// Runs the given executable name as a daemon, so that it executes concurrently +// with this process. Upon completion, the daemon process will automatically be +// reaped by init(8), so an error exit status or a failure to start the +// executable are not reported. Returns true if the daemon process was forked +// successfully, else false. +bool RunAsDaemon(const char *file, const char *const argv[]); + +} // namespace rtc + +#endif // WEBRTC_BASE_POSIX_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/profiler.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/profiler.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/profiler.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/profiler.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,186 @@ +/* + * Copyright 2013 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/profiler.h" + +#include + +#include "webrtc/base/timeutils.h" + +namespace { + +// When written to an ostream, FormattedTime chooses an appropriate scale and +// suffix for a time value given in seconds. +class FormattedTime { + public: + explicit FormattedTime(double t) : time_(t) {} + double time() const { return time_; } + private: + double time_; +}; + +std::ostream& operator<<(std::ostream& stream, const FormattedTime& time) { + if (time.time() < 1.0) { + stream << (time.time() * 1000.0) << "ms"; + } else { + stream << time.time() << 's'; + } + return stream; +} + +} // namespace + +namespace rtc { + +ProfilerEvent::ProfilerEvent() + : total_time_(0.0), + mean_(0.0), + sum_of_squared_differences_(0.0), + start_count_(0), + event_count_(0) { +} + +void ProfilerEvent::Start() { + if (start_count_ == 0) { + current_start_time_ = TimeNanos(); + } + ++start_count_; +} + +void ProfilerEvent::Stop(uint64 stop_time) { + --start_count_; + ASSERT(start_count_ >= 0); + if (start_count_ == 0) { + double elapsed = static_cast(stop_time - current_start_time_) / + kNumNanosecsPerSec; + total_time_ += elapsed; + if (event_count_ == 0) { + minimum_ = maximum_ = elapsed; + } else { + minimum_ = _min(minimum_, elapsed); + maximum_ = _max(maximum_, elapsed); + } + // Online variance and mean algorithm: http://en.wikipedia.org/wiki/ + // Algorithms_for_calculating_variance#Online_algorithm + ++event_count_; + double delta = elapsed - mean_; + mean_ = mean_ + delta / event_count_; + sum_of_squared_differences_ += delta * (elapsed - mean_); + } +} + +void ProfilerEvent::Stop() { + Stop(TimeNanos()); +} + +double ProfilerEvent::standard_deviation() const { + if (event_count_ <= 1) return 0.0; + return sqrt(sum_of_squared_differences_ / (event_count_ - 1.0)); +} + +Profiler* Profiler::Instance() { + LIBJINGLE_DEFINE_STATIC_LOCAL(Profiler, instance, ()); + return &instance; +} + +void Profiler::StartEvent(const std::string& event_name) { + lock_.LockShared(); + EventMap::iterator it = events_.find(event_name); + bool needs_insert = (it == events_.end()); + lock_.UnlockShared(); + + if (needs_insert) { + // Need an exclusive lock to modify the map. + ExclusiveScope scope(&lock_); + it = events_.insert( + EventMap::value_type(event_name, ProfilerEvent())).first; + } + + it->second.Start(); +} + +void Profiler::StopEvent(const std::string& event_name) { + // Get the time ASAP, then wait for the lock. + uint64 stop_time = TimeNanos(); + SharedScope scope(&lock_); + EventMap::iterator it = events_.find(event_name); + if (it != events_.end()) { + it->second.Stop(stop_time); + } +} + +void Profiler::ReportToLog(const char* file, int line, + LoggingSeverity severity_to_use, + const std::string& event_prefix) { + if (!LogMessage::Loggable(severity_to_use)) { + return; + } + + SharedScope scope(&lock_); + + { // Output first line. + LogMessage msg(file, line, severity_to_use); + msg.stream() << "=== Profile report "; + if (event_prefix.empty()) { + msg.stream() << "(prefix: '" << event_prefix << "') "; + } + msg.stream() << "==="; + } + for (EventMap::const_iterator it = events_.begin(); + it != events_.end(); ++it) { + if (event_prefix.empty() || it->first.find(event_prefix) == 0) { + LogMessage(file, line, severity_to_use).stream() + << it->first << " " << it->second; + } + } + LogMessage(file, line, severity_to_use).stream() + << "=== End profile report ==="; +} + +void Profiler::ReportAllToLog(const char* file, int line, + LoggingSeverity severity_to_use) { + ReportToLog(file, line, severity_to_use, ""); +} + +const ProfilerEvent* Profiler::GetEvent(const std::string& event_name) const { + SharedScope scope(&lock_); + EventMap::const_iterator it = + events_.find(event_name); + return (it == events_.end()) ? NULL : &it->second; +} + +bool Profiler::Clear() { + ExclusiveScope scope(&lock_); + bool result = true; + // Clear all events that aren't started. + EventMap::iterator it = events_.begin(); + while (it != events_.end()) { + if (it->second.is_started()) { + ++it; // Can't clear started events. + result = false; + } else { + events_.erase(it++); + } + } + return result; +} + +std::ostream& operator<<(std::ostream& stream, + const ProfilerEvent& profiler_event) { + stream << "count=" << profiler_event.event_count() + << " total=" << FormattedTime(profiler_event.total_time()) + << " mean=" << FormattedTime(profiler_event.mean()) + << " min=" << FormattedTime(profiler_event.minimum()) + << " max=" << FormattedTime(profiler_event.maximum()) + << " sd=" << profiler_event.standard_deviation(); + return stream; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/profiler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/profiler.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/profiler.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/profiler.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,161 @@ +/* + * Copyright 2013 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// A simple wall-clock profiler for instrumented code. +// Example: +// void MyLongFunction() { +// PROFILE_F(); // Time the execution of this function. +// // Do something +// { // Time just what is in this scope. +// PROFILE("My event"); +// // Do something else +// } +// } +// Another example: +// void StartAsyncProcess() { +// PROFILE_START("My async event"); +// DoSomethingAsyncAndThenCall(&Callback); +// } +// void Callback() { +// PROFILE_STOP("My async event"); +// // Handle callback. +// } + +#ifndef WEBRTC_BASE_PROFILER_H_ +#define WEBRTC_BASE_PROFILER_H_ + +#include +#include + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/sharedexclusivelock.h" + +// Profiling could be switched via a build flag, but for now, it's always on. +#ifndef ENABLE_PROFILING +#define ENABLE_PROFILING +#endif + +#ifdef ENABLE_PROFILING + +#define UV_HELPER2(x) _uv_ ## x +#define UV_HELPER(x) UV_HELPER2(x) +#define UNIQUE_VAR UV_HELPER(__LINE__) + +// Profiles the current scope. +#define PROFILE(msg) rtc::ProfilerScope UNIQUE_VAR(msg) +// When placed at the start of a function, profiles the current function. +#define PROFILE_F() PROFILE(__FUNCTION__) +// Reports current timings to the log at severity |sev|. +#define PROFILE_DUMP_ALL(sev) \ + rtc::Profiler::Instance()->ReportAllToLog(__FILE__, __LINE__, sev) +// Reports current timings for all events whose names are prefixed by |prefix| +// to the log at severity |sev|. Using a unique event name as |prefix| will +// report only that event. +#define PROFILE_DUMP(sev, prefix) \ + rtc::Profiler::Instance()->ReportToLog(__FILE__, __LINE__, sev, prefix) +// Starts and stops a profile event. Useful when an event is not easily +// captured within a scope (eg, an async call with a callback when done). +#define PROFILE_START(msg) rtc::Profiler::Instance()->StartEvent(msg) +#define PROFILE_STOP(msg) rtc::Profiler::Instance()->StopEvent(msg) +// TODO(ryanpetrie): Consider adding PROFILE_DUMP_EVERY(sev, iterations) + +#undef UV_HELPER2 +#undef UV_HELPER +#undef UNIQUE_VAR + +#else // ENABLE_PROFILING + +#define PROFILE(msg) (void)0 +#define PROFILE_F() (void)0 +#define PROFILE_DUMP_ALL(sev) (void)0 +#define PROFILE_DUMP(sev, prefix) (void)0 +#define PROFILE_START(msg) (void)0 +#define PROFILE_STOP(msg) (void)0 + +#endif // ENABLE_PROFILING + +namespace rtc { + +// Tracks information for one profiler event. +class ProfilerEvent { + public: + ProfilerEvent(); + void Start(); + void Stop(); + void Stop(uint64 stop_time); + double standard_deviation() const; + double total_time() const { return total_time_; } + double mean() const { return mean_; } + double minimum() const { return minimum_; } + double maximum() const { return maximum_; } + int event_count() const { return event_count_; } + bool is_started() const { return start_count_ > 0; } + + private: + uint64 current_start_time_; + double total_time_; + double mean_; + double sum_of_squared_differences_; + double minimum_; + double maximum_; + int start_count_; + int event_count_; +}; + +// Singleton that owns ProfilerEvents and reports results. Prefer to use +// macros, defined above, rather than directly calling Profiler methods. +class Profiler { + public: + void StartEvent(const std::string& event_name); + void StopEvent(const std::string& event_name); + void ReportToLog(const char* file, int line, LoggingSeverity severity_to_use, + const std::string& event_prefix); + void ReportAllToLog(const char* file, int line, + LoggingSeverity severity_to_use); + const ProfilerEvent* GetEvent(const std::string& event_name) const; + // Clears all _stopped_ events. Returns true if _all_ events were cleared. + bool Clear(); + + static Profiler* Instance(); + private: + Profiler() {} + + typedef std::map EventMap; + EventMap events_; + mutable SharedExclusiveLock lock_; + + DISALLOW_COPY_AND_ASSIGN(Profiler); +}; + +// Starts an event on construction and stops it on destruction. +// Used by PROFILE macro. +class ProfilerScope { + public: + explicit ProfilerScope(const std::string& event_name) + : event_name_(event_name) { + Profiler::Instance()->StartEvent(event_name_); + } + ~ProfilerScope() { + Profiler::Instance()->StopEvent(event_name_); + } + private: + std::string event_name_; + + DISALLOW_COPY_AND_ASSIGN(ProfilerScope); +}; + +std::ostream& operator<<(std::ostream& stream, + const ProfilerEvent& profiler_event); + +} // namespace rtc + +#endif // WEBRTC_BASE_PROFILER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/profiler_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/profiler_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/profiler_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/profiler_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,113 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/profiler.h" +#include "webrtc/base/thread.h" + +namespace { + +const int kWaitMs = 250; +const double kWaitSec = 0.250; +const double kTolerance = 0.1; + +const char* TestFunc() { + PROFILE_F(); + rtc::Thread::SleepMs(kWaitMs); + return __FUNCTION__; +} + +} // namespace + +namespace rtc { + +TEST(ProfilerTest, TestFunction) { + ASSERT_TRUE(Profiler::Instance()->Clear()); + + // Profile a long-running function. + const char* function_name = TestFunc(); + const ProfilerEvent* event = Profiler::Instance()->GetEvent(function_name); + ASSERT_TRUE(event != NULL); + EXPECT_FALSE(event->is_started()); + EXPECT_EQ(1, event->event_count()); + EXPECT_NEAR(kWaitSec, event->mean(), kTolerance * 3); + + // Run it a second time. + TestFunc(); + EXPECT_FALSE(event->is_started()); + EXPECT_EQ(2, event->event_count()); + EXPECT_NEAR(kWaitSec, event->mean(), kTolerance); + EXPECT_NEAR(kWaitSec * 2, event->total_time(), kTolerance * 2); + EXPECT_DOUBLE_EQ(event->mean(), event->total_time() / event->event_count()); +} + +TEST(ProfilerTest, TestScopedEvents) { + const std::string kEvent1Name = "Event 1"; + const std::string kEvent2Name = "Event 2"; + const int kEvent2WaitMs = 150; + const double kEvent2WaitSec = 0.150; + const ProfilerEvent* event1; + const ProfilerEvent* event2; + ASSERT_TRUE(Profiler::Instance()->Clear()); + { // Profile a scope. + PROFILE(kEvent1Name); + event1 = Profiler::Instance()->GetEvent(kEvent1Name); + ASSERT_TRUE(event1 != NULL); + EXPECT_TRUE(event1->is_started()); + EXPECT_EQ(0, event1->event_count()); + rtc::Thread::SleepMs(kWaitMs); + EXPECT_TRUE(event1->is_started()); + } + // Check the result. + EXPECT_FALSE(event1->is_started()); + EXPECT_EQ(1, event1->event_count()); + EXPECT_NEAR(kWaitSec, event1->mean(), kTolerance); + { // Profile a second event. + PROFILE(kEvent2Name); + event2 = Profiler::Instance()->GetEvent(kEvent2Name); + ASSERT_TRUE(event2 != NULL); + EXPECT_FALSE(event1->is_started()); + EXPECT_TRUE(event2->is_started()); + rtc::Thread::SleepMs(kEvent2WaitMs); + } + // Check the result. + EXPECT_FALSE(event2->is_started()); + EXPECT_EQ(1, event2->event_count()); + + // The difference here can be as much as 0.33, so we need high tolerance. + EXPECT_NEAR(kEvent2WaitSec, event2->mean(), kTolerance * 4); + // Make sure event1 is unchanged. + EXPECT_FALSE(event1->is_started()); + EXPECT_EQ(1, event1->event_count()); + { // Run another event 1. + PROFILE(kEvent1Name); + EXPECT_TRUE(event1->is_started()); + rtc::Thread::SleepMs(kWaitMs); + } + // Check the result. + EXPECT_FALSE(event1->is_started()); + EXPECT_EQ(2, event1->event_count()); + EXPECT_NEAR(kWaitSec, event1->mean(), kTolerance); + EXPECT_NEAR(kWaitSec * 2, event1->total_time(), kTolerance * 2); + EXPECT_DOUBLE_EQ(event1->mean(), + event1->total_time() / event1->event_count()); +} + +TEST(ProfilerTest, Clear) { + ASSERT_TRUE(Profiler::Instance()->Clear()); + PROFILE_START("event"); + EXPECT_FALSE(Profiler::Instance()->Clear()); + EXPECT_TRUE(Profiler::Instance()->GetEvent("event") != NULL); + PROFILE_STOP("event"); + EXPECT_TRUE(Profiler::Instance()->Clear()); + EXPECT_EQ(NULL, Profiler::Instance()->GetEvent("event")); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/proxydetect.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/proxydetect.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/proxydetect.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/proxydetect.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,1246 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/proxydetect.h" + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32.h" +#include +#endif // WEBRTC_WIN + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +#include +#include +#include +#include +#include "macconversion.h" +#endif + +#include + +#include "webrtc/base/fileutils.h" +#include "webrtc/base/httpcommon.h" +#include "webrtc/base/httpcommon-inl.h" +#include "webrtc/base/pathutils.h" +#include "webrtc/base/stringutils.h" + +#if defined(WEBRTC_WIN) +#define _TRY_WINHTTP 1 +#define _TRY_JSPROXY 0 +#define _TRY_WM_FINDPROXY 0 +#define _TRY_IE_LAN_SETTINGS 1 +#endif // WEBRTC_WIN + +// For all platforms try Firefox. +#define _TRY_FIREFOX 1 + +// Use profiles.ini to find the correct profile for this user. +// If not set, we'll just look for the default one. +#define USE_FIREFOX_PROFILES_INI 1 + +static const size_t kMaxLineLength = 1024; +static const char kFirefoxPattern[] = "Firefox"; +static const char kInternetExplorerPattern[] = "MSIE"; + +struct StringMap { + public: + void Add(const char * name, const char * value) { map_[name] = value; } + const std::string& Get(const char * name, const char * def = "") const { + std::map::const_iterator it = + map_.find(name); + if (it != map_.end()) + return it->second; + def_ = def; + return def_; + } + bool IsSet(const char * name) const { + return (map_.find(name) != map_.end()); + } + private: + std::map map_; + mutable std::string def_; +}; + +enum UserAgent { + UA_FIREFOX, + UA_INTERNETEXPLORER, + UA_OTHER, + UA_UNKNOWN +}; + +#if _TRY_WINHTTP +//#include +// Note: From winhttp.h + +const char WINHTTP[] = "winhttp"; + +typedef LPVOID HINTERNET; + +typedef struct { + DWORD dwAccessType; // see WINHTTP_ACCESS_* types below + LPWSTR lpszProxy; // proxy server list + LPWSTR lpszProxyBypass; // proxy bypass list +} WINHTTP_PROXY_INFO, * LPWINHTTP_PROXY_INFO; + +typedef struct { + DWORD dwFlags; + DWORD dwAutoDetectFlags; + LPCWSTR lpszAutoConfigUrl; + LPVOID lpvReserved; + DWORD dwReserved; + BOOL fAutoLogonIfChallenged; +} WINHTTP_AUTOPROXY_OPTIONS; + +typedef struct { + BOOL fAutoDetect; + LPWSTR lpszAutoConfigUrl; + LPWSTR lpszProxy; + LPWSTR lpszProxyBypass; +} WINHTTP_CURRENT_USER_IE_PROXY_CONFIG; + +extern "C" { + typedef HINTERNET (WINAPI * pfnWinHttpOpen) + ( + IN LPCWSTR pwszUserAgent, + IN DWORD dwAccessType, + IN LPCWSTR pwszProxyName OPTIONAL, + IN LPCWSTR pwszProxyBypass OPTIONAL, + IN DWORD dwFlags + ); + typedef BOOL (STDAPICALLTYPE * pfnWinHttpCloseHandle) + ( + IN HINTERNET hInternet + ); + typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetProxyForUrl) + ( + IN HINTERNET hSession, + IN LPCWSTR lpcwszUrl, + IN WINHTTP_AUTOPROXY_OPTIONS * pAutoProxyOptions, + OUT WINHTTP_PROXY_INFO * pProxyInfo + ); + typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetIEProxyConfig) + ( + IN OUT WINHTTP_CURRENT_USER_IE_PROXY_CONFIG * pProxyConfig + ); + +} // extern "C" + +#define WINHTTP_AUTOPROXY_AUTO_DETECT 0x00000001 +#define WINHTTP_AUTOPROXY_CONFIG_URL 0x00000002 +#define WINHTTP_AUTOPROXY_RUN_INPROCESS 0x00010000 +#define WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY 0x00020000 +#define WINHTTP_AUTO_DETECT_TYPE_DHCP 0x00000001 +#define WINHTTP_AUTO_DETECT_TYPE_DNS_A 0x00000002 +#define WINHTTP_ACCESS_TYPE_DEFAULT_PROXY 0 +#define WINHTTP_ACCESS_TYPE_NO_PROXY 1 +#define WINHTTP_ACCESS_TYPE_NAMED_PROXY 3 +#define WINHTTP_NO_PROXY_NAME NULL +#define WINHTTP_NO_PROXY_BYPASS NULL + +#endif // _TRY_WINHTTP + +#if _TRY_JSPROXY +extern "C" { + typedef BOOL (STDAPICALLTYPE * pfnInternetGetProxyInfo) + ( + LPCSTR lpszUrl, + DWORD dwUrlLength, + LPSTR lpszUrlHostName, + DWORD dwUrlHostNameLength, + LPSTR * lplpszProxyHostName, + LPDWORD lpdwProxyHostNameLength + ); +} // extern "C" +#endif // _TRY_JSPROXY + +#if _TRY_WM_FINDPROXY +#include +#include +#include +#endif // _TRY_WM_FINDPROXY + +#if _TRY_IE_LAN_SETTINGS +#include +#include +#endif // _TRY_IE_LAN_SETTINGS + +namespace rtc { + +////////////////////////////////////////////////////////////////////// +// Utility Functions +////////////////////////////////////////////////////////////////////// + +#if defined(WEBRTC_WIN) +#ifdef _UNICODE + +typedef std::wstring tstring; +std::string Utf8String(const tstring& str) { return ToUtf8(str); } + +#else // !_UNICODE + +typedef std::string tstring; +std::string Utf8String(const tstring& str) { return str; } + +#endif // !_UNICODE +#endif // WEBRTC_WIN + +bool ProxyItemMatch(const Url& url, char * item, size_t len) { + // hostname:443 + if (char * port = ::strchr(item, ':')) { + *port++ = '\0'; + if (url.port() != atol(port)) { + return false; + } + } + + // A.B.C.D or A.B.C.D/24 + int a, b, c, d, m; + int match = sscanf(item, "%d.%d.%d.%d/%d", &a, &b, &c, &d, &m); + if (match >= 4) { + uint32 ip = ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | + (d & 0xFF); + if ((match < 5) || (m > 32)) + m = 32; + else if (m < 0) + m = 0; + uint32 mask = (m == 0) ? 0 : (~0UL) << (32 - m); + SocketAddress addr(url.host(), 0); + // TODO: Support IPv6 proxyitems. This code block is IPv4 only anyway. + return !addr.IsUnresolved() && + ((addr.ipaddr().v4AddressAsHostOrderInteger() & mask) == (ip & mask)); + } + + // .foo.com + if (*item == '.') { + size_t hostlen = url.host().length(); + return (hostlen > len) + && (stricmp(url.host().c_str() + (hostlen - len), item) == 0); + } + + // localhost or www.*.com + if (!string_match(url.host().c_str(), item)) + return false; + + return true; +} + +bool ProxyListMatch(const Url& url, const std::string& proxy_list, + char sep) { + const size_t BUFSIZE = 256; + char buffer[BUFSIZE]; + const char* list = proxy_list.c_str(); + while (*list) { + // Remove leading space + if (isspace(*list)) { + ++list; + continue; + } + // Break on separator + size_t len; + const char * start = list; + if (const char * end = ::strchr(list, sep)) { + len = (end - list); + list += len + 1; + } else { + len = strlen(list); + list += len; + } + // Remove trailing space + while ((len > 0) && isspace(start[len-1])) + --len; + // Check for oversized entry + if (len >= BUFSIZE) + continue; + memcpy(buffer, start, len); + buffer[len] = 0; + if (!ProxyItemMatch(url, buffer, len)) + continue; + return true; + } + return false; +} + +bool Better(ProxyType lhs, const ProxyType rhs) { + // PROXY_NONE, PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN + const int PROXY_VALUE[5] = { 0, 2, 3, 1 }; + return (PROXY_VALUE[lhs] > PROXY_VALUE[rhs]); +} + +bool ParseProxy(const std::string& saddress, ProxyInfo* proxy) { + const size_t kMaxAddressLength = 1024; + // Allow semicolon, space, or tab as an address separator + const char* const kAddressSeparator = " ;\t"; + + ProxyType ptype; + std::string host; + uint16 port; + + const char* address = saddress.c_str(); + while (*address) { + size_t len; + const char * start = address; + if (const char * sep = strchr(address, kAddressSeparator)) { + len = (sep - address); + address += len + 1; + while (*address != '\0' && ::strchr(kAddressSeparator, *address)) { + address += 1; + } + } else { + len = strlen(address); + address += len; + } + + if (len > kMaxAddressLength - 1) { + LOG(LS_WARNING) << "Proxy address too long [" << start << "]"; + continue; + } + + char buffer[kMaxAddressLength]; + memcpy(buffer, start, len); + buffer[len] = 0; + + char * colon = ::strchr(buffer, ':'); + if (!colon) { + LOG(LS_WARNING) << "Proxy address without port [" << buffer << "]"; + continue; + } + + *colon = 0; + char * endptr; + port = static_cast(strtol(colon + 1, &endptr, 0)); + if (*endptr != 0) { + LOG(LS_WARNING) << "Proxy address with invalid port [" << buffer << "]"; + continue; + } + + if (char * equals = ::strchr(buffer, '=')) { + *equals = 0; + host = equals + 1; + if (_stricmp(buffer, "socks") == 0) { + ptype = PROXY_SOCKS5; + } else if (_stricmp(buffer, "https") == 0) { + ptype = PROXY_HTTPS; + } else { + LOG(LS_WARNING) << "Proxy address with unknown protocol [" + << buffer << "]"; + ptype = PROXY_UNKNOWN; + } + } else { + host = buffer; + ptype = PROXY_UNKNOWN; + } + + if (Better(ptype, proxy->type)) { + proxy->type = ptype; + proxy->address.SetIP(host); + proxy->address.SetPort(port); + } + } + + return proxy->type != PROXY_NONE; +} + +UserAgent GetAgent(const char* agent) { + if (agent) { + std::string agent_str(agent); + if (agent_str.find(kFirefoxPattern) != std::string::npos) { + return UA_FIREFOX; + } else if (agent_str.find(kInternetExplorerPattern) != std::string::npos) { + return UA_INTERNETEXPLORER; + } else if (agent_str.empty()) { + return UA_UNKNOWN; + } + } + return UA_OTHER; +} + +bool EndsWith(const std::string& a, const std::string& b) { + if (b.size() > a.size()) { + return false; + } + int result = a.compare(a.size() - b.size(), b.size(), b); + return result == 0; +} + +bool GetFirefoxProfilePath(Pathname* path) { +#if defined(WEBRTC_WIN) + wchar_t w_path[MAX_PATH]; + if (SHGetFolderPath(0, CSIDL_APPDATA, 0, SHGFP_TYPE_CURRENT, w_path) != + S_OK) { + LOG(LS_ERROR) << "SHGetFolderPath failed"; + return false; + } + path->SetFolder(ToUtf8(w_path, wcslen(w_path))); + path->AppendFolder("Mozilla"); + path->AppendFolder("Firefox"); +#elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + FSRef fr; + if (0 != FSFindFolder(kUserDomain, kApplicationSupportFolderType, + kCreateFolder, &fr)) { + LOG(LS_ERROR) << "FSFindFolder failed"; + return false; + } + char buffer[NAME_MAX + 1]; + if (0 != FSRefMakePath(&fr, reinterpret_cast(buffer), + ARRAY_SIZE(buffer))) { + LOG(LS_ERROR) << "FSRefMakePath failed"; + return false; + } + path->SetFolder(std::string(buffer)); + path->AppendFolder("Firefox"); +#else + char* user_home = getenv("HOME"); + if (user_home == NULL) { + return false; + } + path->SetFolder(std::string(user_home)); + path->AppendFolder(".mozilla"); + path->AppendFolder("firefox"); +#endif // WEBRTC_WIN + return true; +} + +bool GetDefaultFirefoxProfile(Pathname* profile_path) { + ASSERT(NULL != profile_path); + Pathname path; + if (!GetFirefoxProfilePath(&path)) { + return false; + } + +#if USE_FIREFOX_PROFILES_INI + // [Profile0] + // Name=default + // IsRelative=1 + // Path=Profiles/2de53ejb.default + // Default=1 + + // Note: we are looking for the first entry with "Default=1", or the last + // entry in the file + path.SetFilename("profiles.ini"); + scoped_ptr fs(Filesystem::OpenFile(path, "r")); + if (!fs) { + return false; + } + Pathname candidate; + bool relative = true; + std::string line; + while (fs->ReadLine(&line) == SR_SUCCESS) { + if (line.length() == 0) { + continue; + } + if (line.at(0) == '[') { + relative = true; + candidate.clear(); + } else if (line.find("IsRelative=") == 0 && + line.length() >= 12) { + // TODO: The initial Linux public launch revealed a fairly + // high number of machines where IsRelative= did not have anything after + // it. Perhaps that is legal profiles.ini syntax? + relative = (line.at(11) != '0'); + } else if (line.find("Path=") == 0 && + line.length() >= 6) { + if (relative) { + candidate = path; + } else { + candidate.clear(); + } + candidate.AppendFolder(line.substr(5)); + } else if (line.find("Default=") == 0 && + line.length() >= 9) { + if ((line.at(8) != '0') && !candidate.empty()) { + break; + } + } + } + fs->Close(); + if (candidate.empty()) { + return false; + } + profile_path->SetPathname(candidate.pathname()); + +#else // !USE_FIREFOX_PROFILES_INI + path.AppendFolder("Profiles"); + DirectoryIterator* it = Filesystem::IterateDirectory(); + it->Iterate(path); + std::string extension(".default"); + while (!EndsWith(it->Name(), extension)) { + if (!it->Next()) { + return false; + } + } + + profile_path->SetPathname(path); + profile->AppendFolder("Profiles"); + profile->AppendFolder(it->Name()); + delete it; + +#endif // !USE_FIREFOX_PROFILES_INI + + return true; +} + +bool ReadFirefoxPrefs(const Pathname& filename, + const char * prefix, + StringMap* settings) { + scoped_ptr fs(Filesystem::OpenFile(filename, "r")); + if (!fs) { + LOG(LS_ERROR) << "Failed to open file: " << filename.pathname(); + return false; + } + + std::string line; + while (fs->ReadLine(&line) == SR_SUCCESS) { + size_t prefix_len = strlen(prefix); + + // Skip blank lines and too long lines. + if ((line.length() == 0) || (line.length() > kMaxLineLength) + || (line.at(0) == '#') || line.compare(0, 2, "/*") == 0 + || line.compare(0, 2, " *") == 0) { + continue; + } + + char buffer[kMaxLineLength]; + strcpyn(buffer, sizeof(buffer), line.c_str()); + int nstart = 0, nend = 0, vstart = 0, vend = 0; + sscanf(buffer, "user_pref(\"%n%*[^\"]%n\", %n%*[^)]%n);", + &nstart, &nend, &vstart, &vend); + if (vend > 0) { + char* name = buffer + nstart; + name[nend - nstart] = 0; + if ((vend - vstart >= 2) && (buffer[vstart] == '"')) { + vstart += 1; + vend -= 1; + } + char* value = buffer + vstart; + value[vend - vstart] = 0; + if ((strncmp(name, prefix, prefix_len) == 0) && *value) { + settings->Add(name + prefix_len, value); + } + } else { + LOG_F(LS_WARNING) << "Unparsed pref [" << buffer << "]"; + } + } + fs->Close(); + return true; +} + +bool GetFirefoxProxySettings(const char* url, ProxyInfo* proxy) { + Url purl(url); + Pathname path; + bool success = false; + if (GetDefaultFirefoxProfile(&path)) { + StringMap settings; + path.SetFilename("prefs.js"); + if (ReadFirefoxPrefs(path, "network.proxy.", &settings)) { + success = true; + proxy->bypass_list = + settings.Get("no_proxies_on", "localhost, 127.0.0.1"); + if (settings.Get("type") == "1") { + // User has manually specified a proxy, try to figure out what + // type it is. + if (ProxyListMatch(purl, proxy->bypass_list.c_str(), ',')) { + // Our url is in the list of url's to bypass proxy. + } else if (settings.Get("share_proxy_settings") == "true") { + proxy->type = PROXY_UNKNOWN; + proxy->address.SetIP(settings.Get("http")); + proxy->address.SetPort(atoi(settings.Get("http_port").c_str())); + } else if (settings.IsSet("socks")) { + proxy->type = PROXY_SOCKS5; + proxy->address.SetIP(settings.Get("socks")); + proxy->address.SetPort(atoi(settings.Get("socks_port").c_str())); + } else if (settings.IsSet("ssl")) { + proxy->type = PROXY_HTTPS; + proxy->address.SetIP(settings.Get("ssl")); + proxy->address.SetPort(atoi(settings.Get("ssl_port").c_str())); + } else if (settings.IsSet("http")) { + proxy->type = PROXY_HTTPS; + proxy->address.SetIP(settings.Get("http")); + proxy->address.SetPort(atoi(settings.Get("http_port").c_str())); + } + } else if (settings.Get("type") == "2") { + // Browser is configured to get proxy settings from a given url. + proxy->autoconfig_url = settings.Get("autoconfig_url").c_str(); + } else if (settings.Get("type") == "4") { + // Browser is configured to auto detect proxy config. + proxy->autodetect = true; + } else { + // No proxy set. + } + } + } + return success; +} + +#if defined(WEBRTC_WIN) // Windows specific implementation for reading Internet + // Explorer proxy settings. + +void LogGetProxyFault() { + LOG_GLEM(LERROR, WINHTTP) << "WinHttpGetProxyForUrl faulted!!"; +} + +BOOL MyWinHttpGetProxyForUrl(pfnWinHttpGetProxyForUrl pWHGPFU, + HINTERNET hWinHttp, LPCWSTR url, + WINHTTP_AUTOPROXY_OPTIONS *options, + WINHTTP_PROXY_INFO *info) { + // WinHttpGetProxyForUrl() can call plugins which can crash. + // In the case of McAfee scriptproxy.dll, it does crash in + // older versions. Try to catch crashes here and treat as an + // error. + BOOL success = FALSE; + +#if (_HAS_EXCEPTIONS == 0) + __try { + success = pWHGPFU(hWinHttp, url, options, info); + } __except(EXCEPTION_EXECUTE_HANDLER) { + // This is a separate function to avoid + // Visual C++ error 2712 when compiling with C++ EH + LogGetProxyFault(); + } +#else + success = pWHGPFU(hWinHttp, url, options, info); +#endif // (_HAS_EXCEPTIONS == 0) + + return success; +} + +bool IsDefaultBrowserFirefox() { + HKEY key; + LONG result = RegOpenKeyEx(HKEY_CLASSES_ROOT, L"http\\shell\\open\\command", + 0, KEY_READ, &key); + if (ERROR_SUCCESS != result) + return false; + + DWORD size, type; + bool success = false; + result = RegQueryValueEx(key, L"", 0, &type, NULL, &size); + if (result == ERROR_SUCCESS && type == REG_SZ) { + wchar_t* value = new wchar_t[size+1]; + BYTE* buffer = reinterpret_cast(value); + result = RegQueryValueEx(key, L"", 0, &type, buffer, &size); + if (result == ERROR_SUCCESS) { + // Size returned by RegQueryValueEx is in bytes, convert to number of + // wchar_t's. + size /= sizeof(value[0]); + value[size] = L'\0'; + for (size_t i = 0; i < size; ++i) { + value[i] = tolowercase(value[i]); + } + success = (NULL != strstr(value, L"firefox.exe")); + } + delete[] value; + } + + RegCloseKey(key); + return success; +} + +bool GetWinHttpProxySettings(const char* url, ProxyInfo* proxy) { + HMODULE winhttp_handle = LoadLibrary(L"winhttp.dll"); + if (winhttp_handle == NULL) { + LOG(LS_ERROR) << "Failed to load winhttp.dll."; + return false; + } + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG iecfg; + memset(&iecfg, 0, sizeof(iecfg)); + Url purl(url); + pfnWinHttpGetIEProxyConfig pWHGIEPC = + reinterpret_cast( + GetProcAddress(winhttp_handle, + "WinHttpGetIEProxyConfigForCurrentUser")); + bool success = false; + if (pWHGIEPC && pWHGIEPC(&iecfg)) { + // We were read proxy config successfully. + success = true; + if (iecfg.fAutoDetect) { + proxy->autodetect = true; + } + if (iecfg.lpszAutoConfigUrl) { + proxy->autoconfig_url = ToUtf8(iecfg.lpszAutoConfigUrl); + GlobalFree(iecfg.lpszAutoConfigUrl); + } + if (iecfg.lpszProxyBypass) { + proxy->bypass_list = ToUtf8(iecfg.lpszProxyBypass); + GlobalFree(iecfg.lpszProxyBypass); + } + if (iecfg.lpszProxy) { + if (!ProxyListMatch(purl, proxy->bypass_list, ';')) { + ParseProxy(ToUtf8(iecfg.lpszProxy), proxy); + } + GlobalFree(iecfg.lpszProxy); + } + } + FreeLibrary(winhttp_handle); + return success; +} + +// Uses the WinHTTP API to auto detect proxy for the given url. Firefox and IE +// have slightly different option dialogs for proxy settings. In Firefox, +// either a location of a proxy configuration file can be specified or auto +// detection can be selected. In IE theese two options can be independently +// selected. For the case where both options are selected (only IE) we try to +// fetch the config file first, and if that fails we'll perform an auto +// detection. +// +// Returns true if we successfully performed an auto detection not depending on +// whether we found a proxy or not. Returns false on error. +bool WinHttpAutoDetectProxyForUrl(const char* agent, const char* url, + ProxyInfo* proxy) { + Url purl(url); + bool success = true; + HMODULE winhttp_handle = LoadLibrary(L"winhttp.dll"); + if (winhttp_handle == NULL) { + LOG(LS_ERROR) << "Failed to load winhttp.dll."; + return false; + } + pfnWinHttpOpen pWHO = + reinterpret_cast(GetProcAddress(winhttp_handle, + "WinHttpOpen")); + pfnWinHttpCloseHandle pWHCH = + reinterpret_cast( + GetProcAddress(winhttp_handle, "WinHttpCloseHandle")); + pfnWinHttpGetProxyForUrl pWHGPFU = + reinterpret_cast( + GetProcAddress(winhttp_handle, "WinHttpGetProxyForUrl")); + if (pWHO && pWHCH && pWHGPFU) { + if (HINTERNET hWinHttp = pWHO(ToUtf16(agent).c_str(), + WINHTTP_ACCESS_TYPE_NO_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, + 0)) { + BOOL result = FALSE; + WINHTTP_PROXY_INFO info; + memset(&info, 0, sizeof(info)); + if (proxy->autodetect) { + // Use DHCP and DNS to try to find any proxy to use. + WINHTTP_AUTOPROXY_OPTIONS options; + memset(&options, 0, sizeof(options)); + options.fAutoLogonIfChallenged = TRUE; + + options.dwFlags |= WINHTTP_AUTOPROXY_AUTO_DETECT; + options.dwAutoDetectFlags |= WINHTTP_AUTO_DETECT_TYPE_DHCP + | WINHTTP_AUTO_DETECT_TYPE_DNS_A; + result = MyWinHttpGetProxyForUrl( + pWHGPFU, hWinHttp, ToUtf16(url).c_str(), &options, &info); + } + if (!result && !proxy->autoconfig_url.empty()) { + // We have the location of a proxy config file. Download it and + // execute it to find proxy settings for our url. + WINHTTP_AUTOPROXY_OPTIONS options; + memset(&options, 0, sizeof(options)); + memset(&info, 0, sizeof(info)); + options.fAutoLogonIfChallenged = TRUE; + + std::wstring autoconfig_url16((ToUtf16)(proxy->autoconfig_url)); + options.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL; + options.lpszAutoConfigUrl = autoconfig_url16.c_str(); + + result = MyWinHttpGetProxyForUrl( + pWHGPFU, hWinHttp, ToUtf16(url).c_str(), &options, &info); + } + if (result) { + // Either the given auto config url was valid or auto + // detection found a proxy on this network. + if (info.lpszProxy) { + // TODO: Does this bypass list differ from the list + // retreived from GetWinHttpProxySettings earlier? + if (info.lpszProxyBypass) { + proxy->bypass_list = ToUtf8(info.lpszProxyBypass); + GlobalFree(info.lpszProxyBypass); + } else { + proxy->bypass_list.clear(); + } + if (!ProxyListMatch(purl, proxy->bypass_list, ';')) { + // Found proxy for this URL. If parsing the address turns + // out ok then we are successful. + success = ParseProxy(ToUtf8(info.lpszProxy), proxy); + } + GlobalFree(info.lpszProxy); + } + } else { + // We could not find any proxy for this url. + LOG(LS_INFO) << "No proxy detected for " << url; + } + pWHCH(hWinHttp); + } + } else { + LOG(LS_ERROR) << "Failed loading WinHTTP functions."; + success = false; + } + FreeLibrary(winhttp_handle); + return success; +} + +#if 0 // Below functions currently not used. + +bool GetJsProxySettings(const char* url, ProxyInfo* proxy) { + Url purl(url); + bool success = false; + + if (HMODULE hModJS = LoadLibrary(_T("jsproxy.dll"))) { + pfnInternetGetProxyInfo pIGPI = + reinterpret_cast( + GetProcAddress(hModJS, "InternetGetProxyInfo")); + if (pIGPI) { + char proxy[256], host[256]; + memset(proxy, 0, sizeof(proxy)); + char * ptr = proxy; + DWORD proxylen = sizeof(proxy); + std::string surl = Utf8String(url); + DWORD hostlen = _snprintf(host, sizeof(host), "http%s://%S", + purl.secure() ? "s" : "", purl.server()); + if (pIGPI(surl.data(), surl.size(), host, hostlen, &ptr, &proxylen)) { + LOG(INFO) << "Proxy: " << proxy; + } else { + LOG_GLE(INFO) << "InternetGetProxyInfo"; + } + } + FreeLibrary(hModJS); + } + return success; +} + +bool GetWmProxySettings(const char* url, ProxyInfo* proxy) { + Url purl(url); + bool success = false; + + INSNetSourceCreator * nsc = 0; + HRESULT hr = CoCreateInstance(CLSID_ClientNetManager, 0, CLSCTX_ALL, + IID_INSNetSourceCreator, (LPVOID *) &nsc); + if (SUCCEEDED(hr)) { + if (SUCCEEDED(hr = nsc->Initialize())) { + VARIANT dispatch; + VariantInit(&dispatch); + if (SUCCEEDED(hr = nsc->GetNetSourceAdminInterface(L"http", &dispatch))) { + IWMSInternalAdminNetSource * ians = 0; + if (SUCCEEDED(hr = dispatch.pdispVal->QueryInterface( + IID_IWMSInternalAdminNetSource, (LPVOID *) &ians))) { + _bstr_t host(purl.server()); + BSTR proxy = 0; + BOOL bProxyEnabled = FALSE; + DWORD port, context = 0; + if (SUCCEEDED(hr = ians->FindProxyForURL( + L"http", host, &bProxyEnabled, &proxy, &port, &context))) { + success = true; + if (bProxyEnabled) { + _bstr_t sproxy = proxy; + proxy->ptype = PT_HTTPS; + proxy->host = sproxy; + proxy->port = port; + } + } + SysFreeString(proxy); + if (FAILED(hr = ians->ShutdownProxyContext(context))) { + LOG(LS_INFO) << "IWMSInternalAdminNetSource::ShutdownProxyContext" + << "failed: " << hr; + } + ians->Release(); + } + } + VariantClear(&dispatch); + if (FAILED(hr = nsc->Shutdown())) { + LOG(LS_INFO) << "INSNetSourceCreator::Shutdown failed: " << hr; + } + } + nsc->Release(); + } + return success; +} + +bool GetIePerConnectionProxySettings(const char* url, ProxyInfo* proxy) { + Url purl(url); + bool success = false; + + INTERNET_PER_CONN_OPTION_LIST list; + INTERNET_PER_CONN_OPTION options[3]; + memset(&list, 0, sizeof(list)); + memset(&options, 0, sizeof(options)); + + list.dwSize = sizeof(list); + list.dwOptionCount = 3; + list.pOptions = options; + options[0].dwOption = INTERNET_PER_CONN_FLAGS; + options[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER; + options[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS; + DWORD dwSize = sizeof(list); + + if (!InternetQueryOption(0, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, + &dwSize)) { + LOG(LS_INFO) << "InternetQueryOption failed: " << GetLastError(); + } else if ((options[0].Value.dwValue & PROXY_TYPE_PROXY) != 0) { + success = true; + if (!ProxyListMatch(purl, nonnull(options[2].Value.pszValue), _T(';'))) { + ParseProxy(nonnull(options[1].Value.pszValue), proxy); + } + } else if ((options[0].Value.dwValue & PROXY_TYPE_DIRECT) != 0) { + success = true; + } else { + LOG(LS_INFO) << "unknown internet access type: " + << options[0].Value.dwValue; + } + if (options[1].Value.pszValue) { + GlobalFree(options[1].Value.pszValue); + } + if (options[2].Value.pszValue) { + GlobalFree(options[2].Value.pszValue); + } + return success; +} + +#endif // 0 + +// Uses the InternetQueryOption function to retrieve proxy settings +// from the registry. This will only give us the 'static' settings, +// ie, not any information about auto config etc. +bool GetIeLanProxySettings(const char* url, ProxyInfo* proxy) { + Url purl(url); + bool success = false; + + wchar_t buffer[1024]; + memset(buffer, 0, sizeof(buffer)); + INTERNET_PROXY_INFO * info = reinterpret_cast(buffer); + DWORD dwSize = sizeof(buffer); + + if (!InternetQueryOption(0, INTERNET_OPTION_PROXY, info, &dwSize)) { + LOG(LS_INFO) << "InternetQueryOption failed: " << GetLastError(); + } else if (info->dwAccessType == INTERNET_OPEN_TYPE_DIRECT) { + success = true; + } else if (info->dwAccessType == INTERNET_OPEN_TYPE_PROXY) { + success = true; + if (!ProxyListMatch(purl, nonnull(reinterpret_cast( + info->lpszProxyBypass)), ' ')) { + ParseProxy(nonnull(reinterpret_cast(info->lpszProxy)), + proxy); + } + } else { + LOG(LS_INFO) << "unknown internet access type: " << info->dwAccessType; + } + return success; +} + +bool GetIeProxySettings(const char* agent, const char* url, ProxyInfo* proxy) { + bool success = GetWinHttpProxySettings(url, proxy); + if (!success) { + // TODO: Should always call this if no proxy were detected by + // GetWinHttpProxySettings? + // WinHttp failed. Try using the InternetOptionQuery method instead. + return GetIeLanProxySettings(url, proxy); + } + return true; +} + +#endif // WEBRTC_WIN + +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) // WEBRTC_MAC && !defined(WEBRTC_IOS) specific implementation for reading system wide + // proxy settings. + +bool p_getProxyInfoForTypeFromDictWithKeys(ProxyInfo* proxy, + ProxyType type, + const CFDictionaryRef proxyDict, + const CFStringRef enabledKey, + const CFStringRef hostKey, + const CFStringRef portKey) { + // whether or not we set up the proxy info. + bool result = false; + + // we use this as a scratch variable for determining if operations + // succeeded. + bool converted = false; + + // the data we need to construct the SocketAddress for the proxy. + std::string hostname; + int port; + + if ((proxyDict != NULL) && + (CFGetTypeID(proxyDict) == CFDictionaryGetTypeID())) { + // CoreFoundation stuff that we'll have to get from + // the dictionaries and interpret or convert into more usable formats. + CFNumberRef enabledCFNum; + CFNumberRef portCFNum; + CFStringRef hostCFStr; + + enabledCFNum = (CFNumberRef)CFDictionaryGetValue(proxyDict, enabledKey); + + if (p_isCFNumberTrue(enabledCFNum)) { + // let's see if we can get the address and port. + hostCFStr = (CFStringRef)CFDictionaryGetValue(proxyDict, hostKey); + converted = p_convertHostCFStringRefToCPPString(hostCFStr, hostname); + if (converted) { + portCFNum = (CFNumberRef)CFDictionaryGetValue(proxyDict, portKey); + converted = p_convertCFNumberToInt(portCFNum, &port); + if (converted) { + // we have something enabled, with a hostname and a port. + // That's sufficient to set up the proxy info. + proxy->type = type; + proxy->address.SetIP(hostname); + proxy->address.SetPort(port); + result = true; + } + } + } + } + + return result; +} + +// Looks for proxy information in the given dictionary, +// return true if it found sufficient information to define one, +// false otherwise. This is guaranteed to not change the values in proxy +// unless a full-fledged proxy description was discovered in the dictionary. +// However, at the present time this does not support username or password. +// Checks first for a SOCKS proxy, then for HTTPS, then HTTP. +bool GetMacProxySettingsFromDictionary(ProxyInfo* proxy, + const CFDictionaryRef proxyDict) { + // the function result. + bool gotProxy = false; + + + // first we see if there's a SOCKS proxy in place. + gotProxy = p_getProxyInfoForTypeFromDictWithKeys(proxy, + PROXY_SOCKS5, + proxyDict, + kSCPropNetProxiesSOCKSEnable, + kSCPropNetProxiesSOCKSProxy, + kSCPropNetProxiesSOCKSPort); + + if (!gotProxy) { + // okay, no SOCKS proxy, let's look for https. + gotProxy = p_getProxyInfoForTypeFromDictWithKeys(proxy, + PROXY_HTTPS, + proxyDict, + kSCPropNetProxiesHTTPSEnable, + kSCPropNetProxiesHTTPSProxy, + kSCPropNetProxiesHTTPSPort); + if (!gotProxy) { + // Finally, try HTTP proxy. Note that flute doesn't + // differentiate between HTTPS and HTTP, hence we are using the + // same flute type here, ie. PROXY_HTTPS. + gotProxy = p_getProxyInfoForTypeFromDictWithKeys( + proxy, PROXY_HTTPS, proxyDict, kSCPropNetProxiesHTTPEnable, + kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort); + } + } + return gotProxy; +} + +// TODO(hughv) Update keychain functions. They work on 10.8, but are depricated. +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +bool p_putPasswordInProxyInfo(ProxyInfo* proxy) { + bool result = true; // by default we assume we're good. + // for all we know there isn't any password. We'll set to false + // if we find a problem. + + // Ask the keychain for an internet password search for the given protocol. + OSStatus oss = 0; + SecKeychainAttributeList attrList; + attrList.count = 3; + SecKeychainAttribute attributes[3]; + attrList.attr = attributes; + + attributes[0].tag = kSecProtocolItemAttr; + attributes[0].length = sizeof(SecProtocolType); + SecProtocolType protocol; + switch (proxy->type) { + case PROXY_HTTPS : + protocol = kSecProtocolTypeHTTPS; + break; + case PROXY_SOCKS5 : + protocol = kSecProtocolTypeSOCKS; + break; + default : + LOG(LS_ERROR) << "asked for proxy password for unknown proxy type."; + result = false; + break; + } + attributes[0].data = &protocol; + + UInt32 port = proxy->address.port(); + attributes[1].tag = kSecPortItemAttr; + attributes[1].length = sizeof(UInt32); + attributes[1].data = &port; + + std::string ip = proxy->address.ipaddr().ToString(); + attributes[2].tag = kSecServerItemAttr; + attributes[2].length = ip.length(); + attributes[2].data = const_cast(ip.c_str()); + + if (result) { + LOG(LS_INFO) << "trying to get proxy username/password"; + SecKeychainSearchRef sref; + oss = SecKeychainSearchCreateFromAttributes(NULL, + kSecInternetPasswordItemClass, + &attrList, &sref); + if (0 == oss) { + LOG(LS_INFO) << "SecKeychainSearchCreateFromAttributes was good"; + // Get the first item, if there is one. + SecKeychainItemRef iref; + oss = SecKeychainSearchCopyNext(sref, &iref); + if (0 == oss) { + LOG(LS_INFO) << "...looks like we have the username/password data"; + // If there is, get the username and the password. + + SecKeychainAttributeInfo attribsToGet; + attribsToGet.count = 1; + UInt32 tag = kSecAccountItemAttr; + UInt32 format = CSSM_DB_ATTRIBUTE_FORMAT_STRING; + void *data; + UInt32 length; + SecKeychainAttributeList *localList; + + attribsToGet.tag = &tag; + attribsToGet.format = &format; + OSStatus copyres = SecKeychainItemCopyAttributesAndData(iref, + &attribsToGet, + NULL, + &localList, + &length, + &data); + if (0 == copyres) { + LOG(LS_INFO) << "...and we can pull it out."; + // now, we know from experimentation (sadly not from docs) + // that the username is in the local attribute list, + // and the password in the data, + // both without null termination but with info on their length. + // grab the password from the data. + std::string password; + password.append(static_cast(data), length); + + // make the password into a CryptString + // huh, at the time of writing, you can't. + // so we'll skip that for now and come back to it later. + + // now put the username in the proxy. + if (1 <= localList->attr->length) { + proxy->username.append( + static_cast(localList->attr->data), + localList->attr->length); + LOG(LS_INFO) << "username is " << proxy->username; + } else { + LOG(LS_ERROR) << "got keychain entry with no username"; + result = false; + } + } else { + LOG(LS_ERROR) << "couldn't copy info from keychain."; + result = false; + } + SecKeychainItemFreeAttributesAndData(localList, data); + } else if (errSecItemNotFound == oss) { + LOG(LS_INFO) << "...username/password info not found"; + } else { + // oooh, neither 0 nor itemNotFound. + LOG(LS_ERROR) << "Couldn't get keychain information, error code" << oss; + result = false; + } + } else if (errSecItemNotFound == oss) { // noop + } else { + // oooh, neither 0 nor itemNotFound. + LOG(LS_ERROR) << "Couldn't get keychain information, error code" << oss; + result = false; + } + } + + return result; +} + +bool GetMacProxySettings(ProxyInfo* proxy) { + // based on the Apple Technical Q&A QA1234 + // http://developer.apple.com/qa/qa2001/qa1234.html + CFDictionaryRef proxyDict = SCDynamicStoreCopyProxies(NULL); + bool result = false; + + if (proxyDict != NULL) { + // sending it off to another function makes it easier to unit test + // since we can make our own dictionary to hand to that function. + result = GetMacProxySettingsFromDictionary(proxy, proxyDict); + + if (result) { + result = p_putPasswordInProxyInfo(proxy); + } + + // We created the dictionary with something that had the + // word 'copy' in it, so we have to release it, according + // to the Carbon memory management standards. + CFRelease(proxyDict); + } else { + LOG(LS_ERROR) << "SCDynamicStoreCopyProxies failed"; + } + + return result; +} +#endif // WEBRTC_MAC && !defined(WEBRTC_IOS) + +bool AutoDetectProxySettings(const char* agent, const char* url, + ProxyInfo* proxy) { +#if defined(WEBRTC_WIN) + return WinHttpAutoDetectProxyForUrl(agent, url, proxy); +#else + LOG(LS_WARNING) << "Proxy auto-detection not implemented for this platform"; + return false; +#endif +} + +bool GetSystemDefaultProxySettings(const char* agent, const char* url, + ProxyInfo* proxy) { +#if defined(WEBRTC_WIN) + return GetIeProxySettings(agent, url, proxy); +#elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + return GetMacProxySettings(proxy); +#else + // TODO: Get System settings if browser is not firefox. + return GetFirefoxProxySettings(url, proxy); +#endif +} + +bool GetProxySettingsForUrl(const char* agent, const char* url, + ProxyInfo* proxy, bool long_operation) { + UserAgent a = GetAgent(agent); + bool result; + switch (a) { + case UA_FIREFOX: { + result = GetFirefoxProxySettings(url, proxy); + break; + } +#if defined(WEBRTC_WIN) + case UA_INTERNETEXPLORER: + result = GetIeProxySettings(agent, url, proxy); + break; + case UA_UNKNOWN: + // Agent not defined, check default browser. + if (IsDefaultBrowserFirefox()) { + result = GetFirefoxProxySettings(url, proxy); + } else { + result = GetIeProxySettings(agent, url, proxy); + } + break; +#endif // WEBRTC_WIN + default: + result = GetSystemDefaultProxySettings(agent, url, proxy); + break; + } + + // TODO: Consider using the 'long_operation' parameter to + // decide whether to do the auto detection. + if (result && (proxy->autodetect || + !proxy->autoconfig_url.empty())) { + // Use WinHTTP to auto detect proxy for us. + result = AutoDetectProxySettings(agent, url, proxy); + if (!result) { + // Either auto detection is not supported or we simply didn't + // find any proxy, reset type. + proxy->type = rtc::PROXY_NONE; + } + } + return result; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/proxydetect.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/proxydetect.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/proxydetect.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/proxydetect.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,31 @@ +/* + * Copyright 2007 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef _PROXYDETECT_H_ +#define _PROXYDETECT_H_ + +#include "webrtc/base/proxyinfo.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +namespace rtc { +// Auto-detect the proxy server. Returns true if a proxy is configured, +// although hostname may be empty if the proxy is not required for +// the given URL. + +bool GetProxySettingsForUrl(const char* agent, const char* url, + rtc::ProxyInfo* proxy, + bool long_operation = false); + +} // namespace rtc + +#endif // _PROXYDETECT_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/proxydetect_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/proxydetect_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/proxydetect_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/proxydetect_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,164 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/fileutils_mock.h" +#include "webrtc/base/proxydetect.h" + +namespace rtc { + +static const std::string kFirefoxProfilesIni = + "[Profile0]\n" + "Name=default\n" + "IsRelative=1\n" + "Path=Profiles/2de53ejb.default\n" + "Default=1\n"; + +static const std::string kFirefoxHeader = + "# Mozilla User Preferences\n" + "\n" + "/* Some Comments\n" + "*\n" + "*/\n" + "\n"; + +static const std::string kFirefoxCorruptHeader = + "iuahueqe32164"; + +static const std::string kProxyAddress = "proxy.net.com"; + +// Mocking out platform specific path to firefox prefs file. +class FirefoxPrefsFileSystem : public FakeFileSystem { + public: + explicit FirefoxPrefsFileSystem(const std::vector& all_files) : + FakeFileSystem(all_files) { + } + virtual FileStream* OpenFile(const Pathname& filename, + const std::string& mode) { + // TODO: We could have a platform dependent check of paths here. + std::string name = filename.basename(); + name.append(filename.extension()); + EXPECT_TRUE(name.compare("prefs.js") == 0 || + name.compare("profiles.ini") == 0); + FileStream* stream = FakeFileSystem::OpenFile(name, mode); + return stream; + } +}; + +class ProxyDetectTest : public testing::Test { +}; + +bool GetProxyInfo(const std::string prefs, ProxyInfo* info) { + std::vector files; + files.push_back(rtc::FakeFileSystem::File("profiles.ini", + kFirefoxProfilesIni)); + files.push_back(rtc::FakeFileSystem::File("prefs.js", prefs)); + rtc::FilesystemScope fs(new rtc::FirefoxPrefsFileSystem(files)); + return GetProxySettingsForUrl("Firefox", "www.google.com", info, false); +} + +// Verifies that an empty Firefox prefs file results in no proxy detected. +TEST_F(ProxyDetectTest, DISABLED_TestFirefoxEmptyPrefs) { + ProxyInfo proxy_info; + EXPECT_TRUE(GetProxyInfo(kFirefoxHeader, &proxy_info)); + EXPECT_EQ(PROXY_NONE, proxy_info.type); +} + +// Verifies that corrupted prefs file results in no proxy detected. +TEST_F(ProxyDetectTest, DISABLED_TestFirefoxCorruptedPrefs) { + ProxyInfo proxy_info; + EXPECT_TRUE(GetProxyInfo(kFirefoxCorruptHeader, &proxy_info)); + EXPECT_EQ(PROXY_NONE, proxy_info.type); +} + +// Verifies that SOCKS5 proxy is detected if configured. SOCKS uses a +// handshake protocol to inform the proxy software about the +// connection that the client is trying to make and may be used for +// any form of TCP or UDP socket connection. +TEST_F(ProxyDetectTest, DISABLED_TestFirefoxProxySocks) { + ProxyInfo proxy_info; + SocketAddress proxy_address("proxy.socks.com", 6666); + std::string prefs(kFirefoxHeader); + prefs.append("user_pref(\"network.proxy.socks\", \"proxy.socks.com\");\n"); + prefs.append("user_pref(\"network.proxy.socks_port\", 6666);\n"); + prefs.append("user_pref(\"network.proxy.type\", 1);\n"); + + EXPECT_TRUE(GetProxyInfo(prefs, &proxy_info)); + + EXPECT_EQ(PROXY_SOCKS5, proxy_info.type); + EXPECT_EQ(proxy_address, proxy_info.address); +} + +// Verified that SSL proxy is detected if configured. SSL proxy is an +// extention of a HTTP proxy to support secure connections. +TEST_F(ProxyDetectTest, DISABLED_TestFirefoxProxySsl) { + ProxyInfo proxy_info; + SocketAddress proxy_address("proxy.ssl.com", 7777); + std::string prefs(kFirefoxHeader); + + prefs.append("user_pref(\"network.proxy.ssl\", \"proxy.ssl.com\");\n"); + prefs.append("user_pref(\"network.proxy.ssl_port\", 7777);\n"); + prefs.append("user_pref(\"network.proxy.type\", 1);\n"); + + EXPECT_TRUE(GetProxyInfo(prefs, &proxy_info)); + + EXPECT_EQ(PROXY_HTTPS, proxy_info.type); + EXPECT_EQ(proxy_address, proxy_info.address); +} + +// Verifies that a HTTP proxy is detected if configured. +TEST_F(ProxyDetectTest, DISABLED_TestFirefoxProxyHttp) { + ProxyInfo proxy_info; + SocketAddress proxy_address("proxy.http.com", 8888); + std::string prefs(kFirefoxHeader); + + prefs.append("user_pref(\"network.proxy.http\", \"proxy.http.com\");\n"); + prefs.append("user_pref(\"network.proxy.http_port\", 8888);\n"); + prefs.append("user_pref(\"network.proxy.type\", 1);\n"); + + EXPECT_TRUE(GetProxyInfo(prefs, &proxy_info)); + + EXPECT_EQ(PROXY_HTTPS, proxy_info.type); + EXPECT_EQ(proxy_address, proxy_info.address); +} + +// Verifies detection of automatic proxy detection. +TEST_F(ProxyDetectTest, DISABLED_TestFirefoxProxyAuto) { + ProxyInfo proxy_info; + std::string prefs(kFirefoxHeader); + + prefs.append("user_pref(\"network.proxy.type\", 4);\n"); + + EXPECT_TRUE(GetProxyInfo(prefs, &proxy_info)); + + EXPECT_EQ(PROXY_NONE, proxy_info.type); + EXPECT_TRUE(proxy_info.autodetect); + EXPECT_TRUE(proxy_info.autoconfig_url.empty()); +} + +// Verifies detection of automatic proxy detection using a static url +// to config file. +TEST_F(ProxyDetectTest, DISABLED_TestFirefoxProxyAutoUrl) { + ProxyInfo proxy_info; + std::string prefs(kFirefoxHeader); + + prefs.append( + "user_pref(\"network.proxy.autoconfig_url\", \"http://a/b.pac\");\n"); + prefs.append("user_pref(\"network.proxy.type\", 2);\n"); + + EXPECT_TRUE(GetProxyInfo(prefs, &proxy_info)); + + EXPECT_FALSE(proxy_info.autodetect); + EXPECT_EQ(PROXY_NONE, proxy_info.type); + EXPECT_EQ(0, proxy_info.autoconfig_url.compare("http://a/b.pac")); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/proxyinfo.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/proxyinfo.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/proxyinfo.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/proxyinfo.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/proxyinfo.h" + +namespace rtc { + +const char * ProxyToString(ProxyType proxy) { + const char * const PROXY_NAMES[] = { "none", "https", "socks5", "unknown" }; + return PROXY_NAMES[proxy]; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/proxyinfo.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/proxyinfo.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/proxyinfo.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/proxyinfo.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,42 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_PROXYINFO_H__ +#define WEBRTC_BASE_PROXYINFO_H__ + +#include +#include "webrtc/base/socketaddress.h" +#include "webrtc/base/cryptstring.h" + +namespace rtc { + +enum ProxyType { + PROXY_NONE, + PROXY_HTTPS, + PROXY_SOCKS5, + PROXY_UNKNOWN +}; +const char * ProxyToString(ProxyType proxy); + +struct ProxyInfo { + ProxyType type; + SocketAddress address; + std::string autoconfig_url; + bool autodetect; + std::string bypass_list; + std::string username; + CryptString password; + + ProxyInfo() : type(PROXY_NONE), autodetect(false) { } +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_PROXYINFO_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/proxyserver.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/proxyserver.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/proxyserver.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/proxyserver.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,144 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/proxyserver.h" + +#include +#include "webrtc/base/socketfactory.h" + +namespace rtc { + +// ProxyServer +ProxyServer::ProxyServer( + SocketFactory* int_factory, const SocketAddress& int_addr, + SocketFactory* ext_factory, const SocketAddress& ext_ip) + : ext_factory_(ext_factory), ext_ip_(ext_ip.ipaddr(), 0), // strip off port + server_socket_(int_factory->CreateAsyncSocket(int_addr.family(), + SOCK_STREAM)) { + ASSERT(server_socket_.get() != NULL); + ASSERT(int_addr.family() == AF_INET || int_addr.family() == AF_INET6); + server_socket_->Bind(int_addr); + server_socket_->Listen(5); + server_socket_->SignalReadEvent.connect(this, &ProxyServer::OnAcceptEvent); +} + +ProxyServer::~ProxyServer() { + for (BindingList::iterator it = bindings_.begin(); + it != bindings_.end(); ++it) { + delete (*it); + } +} + +void ProxyServer::OnAcceptEvent(AsyncSocket* socket) { + ASSERT(socket != NULL && socket == server_socket_.get()); + AsyncSocket* int_socket = socket->Accept(NULL); + AsyncProxyServerSocket* wrapped_socket = WrapSocket(int_socket); + AsyncSocket* ext_socket = ext_factory_->CreateAsyncSocket(ext_ip_.family(), + SOCK_STREAM); + if (ext_socket) { + ext_socket->Bind(ext_ip_); + bindings_.push_back(new ProxyBinding(wrapped_socket, ext_socket)); + } else { + LOG(LS_ERROR) << "Unable to create external socket on proxy accept event"; + } +} + +void ProxyServer::OnBindingDestroyed(ProxyBinding* binding) { + BindingList::iterator it = + std::find(bindings_.begin(), bindings_.end(), binding); + delete (*it); + bindings_.erase(it); +} + +// ProxyBinding +ProxyBinding::ProxyBinding(AsyncProxyServerSocket* int_socket, + AsyncSocket* ext_socket) + : int_socket_(int_socket), ext_socket_(ext_socket), connected_(false), + out_buffer_(kBufferSize), in_buffer_(kBufferSize) { + int_socket_->SignalConnectRequest.connect(this, + &ProxyBinding::OnConnectRequest); + int_socket_->SignalReadEvent.connect(this, &ProxyBinding::OnInternalRead); + int_socket_->SignalWriteEvent.connect(this, &ProxyBinding::OnInternalWrite); + int_socket_->SignalCloseEvent.connect(this, &ProxyBinding::OnInternalClose); + ext_socket_->SignalConnectEvent.connect(this, + &ProxyBinding::OnExternalConnect); + ext_socket_->SignalReadEvent.connect(this, &ProxyBinding::OnExternalRead); + ext_socket_->SignalWriteEvent.connect(this, &ProxyBinding::OnExternalWrite); + ext_socket_->SignalCloseEvent.connect(this, &ProxyBinding::OnExternalClose); +} + +void ProxyBinding::OnConnectRequest(AsyncProxyServerSocket* socket, + const SocketAddress& addr) { + ASSERT(!connected_ && ext_socket_.get() != NULL); + ext_socket_->Connect(addr); + // TODO: handle errors here +} + +void ProxyBinding::OnInternalRead(AsyncSocket* socket) { + Read(int_socket_.get(), &out_buffer_); + Write(ext_socket_.get(), &out_buffer_); +} + +void ProxyBinding::OnInternalWrite(AsyncSocket* socket) { + Write(int_socket_.get(), &in_buffer_); +} + +void ProxyBinding::OnInternalClose(AsyncSocket* socket, int err) { + Destroy(); +} + +void ProxyBinding::OnExternalConnect(AsyncSocket* socket) { + ASSERT(socket != NULL); + connected_ = true; + int_socket_->SendConnectResult(0, socket->GetRemoteAddress()); +} + +void ProxyBinding::OnExternalRead(AsyncSocket* socket) { + Read(ext_socket_.get(), &in_buffer_); + Write(int_socket_.get(), &in_buffer_); +} + +void ProxyBinding::OnExternalWrite(AsyncSocket* socket) { + Write(ext_socket_.get(), &out_buffer_); +} + +void ProxyBinding::OnExternalClose(AsyncSocket* socket, int err) { + if (!connected_) { + int_socket_->SendConnectResult(err, SocketAddress()); + } + Destroy(); +} + +void ProxyBinding::Read(AsyncSocket* socket, FifoBuffer* buffer) { + // Only read if the buffer is empty. + ASSERT(socket != NULL); + size_t size; + int read; + if (buffer->GetBuffered(&size) && size == 0) { + void* p = buffer->GetWriteBuffer(&size); + read = socket->Recv(p, size); + buffer->ConsumeWriteBuffer(_max(read, 0)); + } +} + +void ProxyBinding::Write(AsyncSocket* socket, FifoBuffer* buffer) { + ASSERT(socket != NULL); + size_t size; + int written; + const void* p = buffer->GetReadData(&size); + written = socket->Send(p, size); + buffer->ConsumeReadData(_max(written, 0)); +} + +void ProxyBinding::Destroy() { + SignalDestroyed(this); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/proxyserver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/proxyserver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/proxyserver.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/proxyserver.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,96 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_PROXYSERVER_H_ +#define WEBRTC_BASE_PROXYSERVER_H_ + +#include +#include "webrtc/base/asyncsocket.h" +#include "webrtc/base/socketadapters.h" +#include "webrtc/base/socketaddress.h" +#include "webrtc/base/stream.h" + +namespace rtc { + +class SocketFactory; + +// ProxyServer is a base class that allows for easy construction of proxy +// servers. With its helper class ProxyBinding, it contains all the necessary +// logic for receiving and bridging connections. The specific client-server +// proxy protocol is implemented by an instance of the AsyncProxyServerSocket +// class; children of ProxyServer implement WrapSocket appropriately to return +// the correct protocol handler. + +class ProxyBinding : public sigslot::has_slots<> { + public: + ProxyBinding(AsyncProxyServerSocket* in_socket, AsyncSocket* out_socket); + sigslot::signal1 SignalDestroyed; + + private: + void OnConnectRequest(AsyncProxyServerSocket* socket, + const SocketAddress& addr); + void OnInternalRead(AsyncSocket* socket); + void OnInternalWrite(AsyncSocket* socket); + void OnInternalClose(AsyncSocket* socket, int err); + void OnExternalConnect(AsyncSocket* socket); + void OnExternalRead(AsyncSocket* socket); + void OnExternalWrite(AsyncSocket* socket); + void OnExternalClose(AsyncSocket* socket, int err); + + static void Read(AsyncSocket* socket, FifoBuffer* buffer); + static void Write(AsyncSocket* socket, FifoBuffer* buffer); + void Destroy(); + + static const int kBufferSize = 4096; + scoped_ptr int_socket_; + scoped_ptr ext_socket_; + bool connected_; + FifoBuffer out_buffer_; + FifoBuffer in_buffer_; + DISALLOW_EVIL_CONSTRUCTORS(ProxyBinding); +}; + +class ProxyServer : public sigslot::has_slots<> { + public: + ProxyServer(SocketFactory* int_factory, const SocketAddress& int_addr, + SocketFactory* ext_factory, const SocketAddress& ext_ip); + virtual ~ProxyServer(); + + protected: + void OnAcceptEvent(AsyncSocket* socket); + virtual AsyncProxyServerSocket* WrapSocket(AsyncSocket* socket) = 0; + void OnBindingDestroyed(ProxyBinding* binding); + + private: + typedef std::list BindingList; + SocketFactory* ext_factory_; + SocketAddress ext_ip_; + scoped_ptr server_socket_; + BindingList bindings_; + DISALLOW_EVIL_CONSTRUCTORS(ProxyServer); +}; + +// SocksProxyServer is a simple extension of ProxyServer to implement SOCKS. +class SocksProxyServer : public ProxyServer { + public: + SocksProxyServer(SocketFactory* int_factory, const SocketAddress& int_addr, + SocketFactory* ext_factory, const SocketAddress& ext_ip) + : ProxyServer(int_factory, int_addr, ext_factory, ext_ip) { + } + protected: + AsyncProxyServerSocket* WrapSocket(AsyncSocket* socket) { + return new AsyncSocksProxyServerSocket(socket); + } + DISALLOW_EVIL_CONSTRUCTORS(SocksProxyServer); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_PROXYSERVER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/proxy_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/proxy_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/proxy_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/proxy_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,136 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include "webrtc/base/autodetectproxy.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/httpserver.h" +#include "webrtc/base/proxyserver.h" +#include "webrtc/base/socketadapters.h" +#include "webrtc/base/testclient.h" +#include "webrtc/base/testechoserver.h" +#include "webrtc/base/virtualsocketserver.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +using rtc::Socket; +using rtc::Thread; +using rtc::SocketAddress; + +static const SocketAddress kSocksProxyIntAddr("1.2.3.4", 1080); +static const SocketAddress kSocksProxyExtAddr("1.2.3.5", 0); +static const SocketAddress kHttpsProxyIntAddr("1.2.3.4", 443); +static const SocketAddress kHttpsProxyExtAddr("1.2.3.5", 0); +static const SocketAddress kBogusProxyIntAddr("1.2.3.4", 999); + +// Used to run a proxy detect on the current thread. Otherwise we would need +// to make both threads share the same VirtualSocketServer. +class AutoDetectProxyRunner : public rtc::AutoDetectProxy { + public: + explicit AutoDetectProxyRunner(const std::string& agent) + : AutoDetectProxy(agent) {} + void Run() { + DoWork(); + Thread::Current()->Restart(); // needed to reset the messagequeue + } +}; + +// Sets up a virtual socket server and HTTPS/SOCKS5 proxy servers. +class ProxyTest : public testing::Test { + public: + ProxyTest() : ss_(new rtc::VirtualSocketServer(NULL)) { + Thread::Current()->set_socketserver(ss_.get()); + socks_.reset(new rtc::SocksProxyServer( + ss_.get(), kSocksProxyIntAddr, ss_.get(), kSocksProxyExtAddr)); + https_.reset(new rtc::HttpListenServer()); + https_->Listen(kHttpsProxyIntAddr); + } + ~ProxyTest() { + Thread::Current()->set_socketserver(NULL); + } + + rtc::SocketServer* ss() { return ss_.get(); } + + rtc::ProxyType DetectProxyType(const SocketAddress& address) { + rtc::ProxyType type; + AutoDetectProxyRunner* detect = new AutoDetectProxyRunner("unittest/1.0"); + detect->set_proxy(address); + detect->Run(); // blocks until done + type = detect->proxy().type; + detect->Destroy(false); + return type; + } + + private: + rtc::scoped_ptr ss_; + rtc::scoped_ptr socks_; + // TODO: Make this a real HTTPS proxy server. + rtc::scoped_ptr https_; +}; + +// Tests whether we can use a SOCKS5 proxy to connect to a server. +TEST_F(ProxyTest, TestSocks5Connect) { + rtc::AsyncSocket* socket = + ss()->CreateAsyncSocket(kSocksProxyIntAddr.family(), SOCK_STREAM); + rtc::AsyncSocksProxySocket* proxy_socket = + new rtc::AsyncSocksProxySocket(socket, kSocksProxyIntAddr, + "", rtc::CryptString()); + // TODO: IPv6-ize these tests when proxy supports IPv6. + + rtc::TestEchoServer server(Thread::Current(), + SocketAddress(INADDR_ANY, 0)); + + rtc::AsyncTCPSocket* packet_socket = rtc::AsyncTCPSocket::Create( + proxy_socket, SocketAddress(INADDR_ANY, 0), server.address()); + EXPECT_TRUE(packet_socket != NULL); + rtc::TestClient client(packet_socket); + + EXPECT_EQ(Socket::CS_CONNECTING, proxy_socket->GetState()); + EXPECT_TRUE(client.CheckConnected()); + EXPECT_EQ(Socket::CS_CONNECTED, proxy_socket->GetState()); + EXPECT_EQ(server.address(), client.remote_address()); + client.Send("foo", 3); + EXPECT_TRUE(client.CheckNextPacket("foo", 3, NULL)); + EXPECT_TRUE(client.CheckNoPacket()); +} + +/* +// Tests whether we can use a HTTPS proxy to connect to a server. +TEST_F(ProxyTest, TestHttpsConnect) { + AsyncSocket* socket = ss()->CreateAsyncSocket(SOCK_STREAM); + AsyncHttpsProxySocket* proxy_socket = new AsyncHttpsProxySocket( + socket, "unittest/1.0", kHttpsProxyIntAddress, "", CryptString()); + TestClient client(new AsyncTCPSocket(proxy_socket)); + TestEchoServer server(Thread::Current(), SocketAddress()); + + EXPECT_TRUE(client.Connect(server.address())); + EXPECT_TRUE(client.CheckConnected()); + EXPECT_EQ(server.address(), client.remote_address()); + client.Send("foo", 3); + EXPECT_TRUE(client.CheckNextPacket("foo", 3, NULL)); + EXPECT_TRUE(client.CheckNoPacket()); +} +*/ + +// Tests whether we can autodetect a SOCKS5 proxy. +TEST_F(ProxyTest, TestAutoDetectSocks5) { + EXPECT_EQ(rtc::PROXY_SOCKS5, DetectProxyType(kSocksProxyIntAddr)); +} + +/* +// Tests whether we can autodetect a HTTPS proxy. +TEST_F(ProxyTest, TestAutoDetectHttps) { + EXPECT_EQ(rtc::PROXY_HTTPS, DetectProxyType(kHttpsProxyIntAddr)); +} +*/ + +// Tests whether we fail properly for no proxy. +TEST_F(ProxyTest, TestAutoDetectBogus) { + EXPECT_EQ(rtc::PROXY_UNKNOWN, DetectProxyType(kBogusProxyIntAddr)); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ratelimiter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ratelimiter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ratelimiter.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ratelimiter.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/ratelimiter.h" + +namespace rtc { + +bool RateLimiter::CanUse(size_t desired, double time) { + return ((time > period_end_ && desired <= max_per_period_) || + (used_in_period_ + desired) <= max_per_period_); +} + +void RateLimiter::Use(size_t used, double time) { + if (time > period_end_) { + period_start_ = time; + period_end_ = time + period_length_; + used_in_period_ = 0; + } + used_in_period_ += used; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ratelimiter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ratelimiter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ratelimiter.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ratelimiter.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,63 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_RATELIMITER_H_ +#define WEBRTC_BASE_RATELIMITER_H_ + +#include +#include "webrtc/base/basictypes.h" + +namespace rtc { + +// Limits the rate of use to a certain maximum quantity per period of +// time. Use, for example, for simple bandwidth throttling. +// +// It's implemented like a diet plan: You have so many calories per +// day. If you hit the limit, you can't eat any more until the next +// day. +class RateLimiter { + public: + // For example, 100kb per second. + RateLimiter(size_t max, double period) + : max_per_period_(max), + period_length_(period), + used_in_period_(0), + period_start_(0.0), + period_end_(period) { + } + virtual ~RateLimiter() {} + + // Returns true if if the desired quantity is available in the + // current period (< (max - used)). Once the given time passes the + // end of the period, used is set to zero and more use is available. + bool CanUse(size_t desired, double time); + // Increment the quantity used this period. If past the end of a + // period, a new period is started. + void Use(size_t used, double time); + + size_t used_in_period() const { + return used_in_period_; + } + + size_t max_per_period() const { + return max_per_period_; + } + + private: + size_t max_per_period_; + double period_length_; + size_t used_in_period_; + double period_start_; + double period_end_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_RATELIMITER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ratelimiter_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ratelimiter_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ratelimiter_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ratelimiter_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,59 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/ratelimiter.h" + +namespace rtc { + +TEST(RateLimiterTest, TestCanUse) { + // Diet: Can eat 2,000 calories per day. + RateLimiter limiter = RateLimiter(2000, 1.0); + + double monday = 1.0; + double tuesday = 2.0; + double thursday = 4.0; + + EXPECT_TRUE(limiter.CanUse(0, monday)); + EXPECT_TRUE(limiter.CanUse(1000, monday)); + EXPECT_TRUE(limiter.CanUse(1999, monday)); + EXPECT_TRUE(limiter.CanUse(2000, monday)); + EXPECT_FALSE(limiter.CanUse(2001, monday)); + + limiter.Use(1000, monday); + + EXPECT_TRUE(limiter.CanUse(0, monday)); + EXPECT_TRUE(limiter.CanUse(999, monday)); + EXPECT_TRUE(limiter.CanUse(1000, monday)); + EXPECT_FALSE(limiter.CanUse(1001, monday)); + + limiter.Use(1000, monday); + + EXPECT_TRUE(limiter.CanUse(0, monday)); + EXPECT_FALSE(limiter.CanUse(1, monday)); + + EXPECT_TRUE(limiter.CanUse(0, tuesday)); + EXPECT_TRUE(limiter.CanUse(1, tuesday)); + EXPECT_TRUE(limiter.CanUse(1999, tuesday)); + EXPECT_TRUE(limiter.CanUse(2000, tuesday)); + EXPECT_FALSE(limiter.CanUse(2001, tuesday)); + + limiter.Use(1000, tuesday); + + EXPECT_TRUE(limiter.CanUse(1000, tuesday)); + EXPECT_FALSE(limiter.CanUse(1001, tuesday)); + + limiter.Use(1000, thursday); + + EXPECT_TRUE(limiter.CanUse(1000, tuesday)); + EXPECT_FALSE(limiter.CanUse(1001, tuesday)); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ratetracker.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ratetracker.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ratetracker.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ratetracker.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,63 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/ratetracker.h" +#include "webrtc/base/timeutils.h" + +namespace rtc { + +RateTracker::RateTracker() + : total_units_(0), units_second_(0), + last_units_second_time_(static_cast(-1)), + last_units_second_calc_(0) { +} + +size_t RateTracker::total_units() const { + return total_units_; +} + +size_t RateTracker::units_second() { + // Snapshot units / second calculator. Determine how many seconds have + // elapsed since our last reference point. If over 1 second, establish + // a new reference point that is an integer number of seconds since the + // last one, and compute the units over that interval. + uint32 current_time = Time(); + if (last_units_second_time_ != static_cast(-1)) { + int delta = rtc::TimeDiff(current_time, last_units_second_time_); + if (delta >= 1000) { + int fraction_time = delta % 1000; + int seconds = delta / 1000; + int fraction_units = + static_cast(total_units_ - last_units_second_calc_) * + fraction_time / delta; + // Compute "units received during the interval" / "seconds in interval" + units_second_ = + (total_units_ - last_units_second_calc_ - fraction_units) / seconds; + last_units_second_time_ = current_time - fraction_time; + last_units_second_calc_ = total_units_ - fraction_units; + } + } + if (last_units_second_time_ == static_cast(-1)) { + last_units_second_time_ = current_time; + last_units_second_calc_ = total_units_; + } + + return units_second_; +} + +void RateTracker::Update(size_t units) { + total_units_ += units; +} + +uint32 RateTracker::Time() const { + return rtc::Time(); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ratetracker.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ratetracker.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ratetracker.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ratetracker.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,42 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_RATETRACKER_H_ +#define WEBRTC_BASE_RATETRACKER_H_ + +#include +#include "webrtc/base/basictypes.h" + +namespace rtc { + +// Computes instantaneous units per second. +class RateTracker { + public: + RateTracker(); + virtual ~RateTracker() {} + + size_t total_units() const; + size_t units_second(); + void Update(size_t units); + + protected: + // overrideable for tests + virtual uint32 Time() const; + + private: + size_t total_units_; + size_t units_second_; + uint32 last_units_second_time_; + size_t last_units_second_calc_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_RATETRACKER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ratetracker_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ratetracker_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ratetracker_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ratetracker_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,74 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/ratetracker.h" + +namespace rtc { + +class RateTrackerForTest : public RateTracker { + public: + RateTrackerForTest() : time_(0) {} + virtual uint32 Time() const { return time_; } + void AdvanceTime(uint32 delta) { time_ += delta; } + + private: + uint32 time_; +}; + +TEST(RateTrackerTest, TestBasics) { + RateTrackerForTest tracker; + EXPECT_EQ(0U, tracker.total_units()); + EXPECT_EQ(0U, tracker.units_second()); + + // Add a sample. + tracker.Update(1234); + // Advance the clock by 100 ms. + tracker.AdvanceTime(100); + // total_units should advance, but units_second should stay 0. + EXPECT_EQ(1234U, tracker.total_units()); + EXPECT_EQ(0U, tracker.units_second()); + + // Repeat. + tracker.Update(1234); + tracker.AdvanceTime(100); + EXPECT_EQ(1234U * 2, tracker.total_units()); + EXPECT_EQ(0U, tracker.units_second()); + + // Advance the clock by 800 ms, so we've elapsed a full second. + // units_second should now be filled in properly. + tracker.AdvanceTime(800); + EXPECT_EQ(1234U * 2, tracker.total_units()); + EXPECT_EQ(1234U * 2, tracker.units_second()); + + // Poll the tracker again immediately. The reported rate should stay the same. + EXPECT_EQ(1234U * 2, tracker.total_units()); + EXPECT_EQ(1234U * 2, tracker.units_second()); + + // Do nothing and advance by a second. We should drop down to zero. + tracker.AdvanceTime(1000); + EXPECT_EQ(1234U * 2, tracker.total_units()); + EXPECT_EQ(0U, tracker.units_second()); + + // Send a bunch of data at a constant rate for 5.5 "seconds". + // We should report the rate properly. + for (int i = 0; i < 5500; i += 100) { + tracker.Update(9876U); + tracker.AdvanceTime(100); + } + EXPECT_EQ(9876U * 10, tracker.units_second()); + + // Advance the clock by 500 ms. Since we sent nothing over this half-second, + // the reported rate should be reduced by half. + tracker.AdvanceTime(500); + EXPECT_EQ(9876U * 5, tracker.units_second()); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/refcount.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/refcount.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/refcount.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/refcount.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,78 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef TALK_APP_BASE_REFCOUNT_H_ +#define TALK_APP_BASE_REFCOUNT_H_ + +#include + +#include "webrtc/base/criticalsection.h" + +namespace rtc { + +// Reference count interface. +class RefCountInterface { + public: + virtual int AddRef() = 0; + virtual int Release() = 0; + protected: + virtual ~RefCountInterface() {} +}; + +template +class RefCountedObject : public T { + public: + RefCountedObject() : ref_count_(0) { + } + + template + explicit RefCountedObject(P p) : T(p), ref_count_(0) { + } + + template + RefCountedObject(P1 p1, P2 p2) : T(p1, p2), ref_count_(0) { + } + + template + RefCountedObject(P1 p1, P2 p2, P3 p3) : T(p1, p2, p3), ref_count_(0) { + } + + template + RefCountedObject(P1 p1, P2 p2, P3 p3, P4 p4) + : T(p1, p2, p3, p4), ref_count_(0) { + } + + template + RefCountedObject(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : T(p1, p2, p3, p4, p5), ref_count_(0) { + } + + virtual int AddRef() { + return rtc::AtomicOps::Increment(&ref_count_); + } + + virtual int Release() { + int count = rtc::AtomicOps::Decrement(&ref_count_); + if (!count) { + delete this; + } + return count; + } + + protected: + virtual ~RefCountedObject() { + } + + int ref_count_; +}; + +} // namespace rtc + +#endif // TALK_APP_BASE_REFCOUNT_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/referencecountedsingletonfactory.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/referencecountedsingletonfactory.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/referencecountedsingletonfactory.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/referencecountedsingletonfactory.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,157 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_REFERENCECOUNTEDSINGLETONFACTORY_H_ +#define WEBRTC_BASE_REFERENCECOUNTEDSINGLETONFACTORY_H_ + +#include "webrtc/base/common.h" +#include "webrtc/base/criticalsection.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/scoped_ptr.h" + +namespace rtc { + +template class rcsf_ptr; + +// A ReferenceCountedSingletonFactory is an object which owns another object, +// and doles out the owned object to consumers in a reference-counted manner. +// Thus, the factory owns at most one object of the desired kind, and +// hands consumers a special pointer to it, through which they can access it. +// When the consumers delete the pointer, the reference count goes down, +// and if the reference count hits zero, the factory can throw the object +// away. If a consumer requests the pointer and the factory has none, +// it can create one on the fly and pass it back. +template +class ReferenceCountedSingletonFactory { + friend class rcsf_ptr; + public: + ReferenceCountedSingletonFactory() : ref_count_(0) {} + + virtual ~ReferenceCountedSingletonFactory() { + ASSERT(ref_count_ == 0); + } + + protected: + // Must be implemented in a sub-class. The sub-class may choose whether or not + // to cache the instance across lifetimes by either reset()'ing or not + // reset()'ing the scoped_ptr in CleanupInstance(). + virtual bool SetupInstance() = 0; + virtual void CleanupInstance() = 0; + + scoped_ptr instance_; + + private: + Interface* GetInstance() { + rtc::CritScope cs(&crit_); + if (ref_count_ == 0) { + if (!SetupInstance()) { + LOG(LS_VERBOSE) << "Failed to setup instance"; + return NULL; + } + ASSERT(instance_.get() != NULL); + } + ++ref_count_; + + LOG(LS_VERBOSE) << "Number of references: " << ref_count_; + return instance_.get(); + } + + void ReleaseInstance() { + rtc::CritScope cs(&crit_); + ASSERT(ref_count_ > 0); + ASSERT(instance_.get() != NULL); + --ref_count_; + LOG(LS_VERBOSE) << "Number of references: " << ref_count_; + if (ref_count_ == 0) { + CleanupInstance(); + } + } + + CriticalSection crit_; + int ref_count_; + + DISALLOW_COPY_AND_ASSIGN(ReferenceCountedSingletonFactory); +}; + +template +class rcsf_ptr { + public: + // Create a pointer that uses the factory to get the instance. + // This is lazy - it won't generate the instance until it is requested. + explicit rcsf_ptr(ReferenceCountedSingletonFactory* factory) + : instance_(NULL), + factory_(factory) { + } + + ~rcsf_ptr() { + release(); + } + + Interface& operator*() { + EnsureAcquired(); + return *instance_; + } + + Interface* operator->() { + EnsureAcquired(); + return instance_; + } + + // Gets the pointer, creating the singleton if necessary. May return NULL if + // creation failed. + Interface* get() { + Acquire(); + return instance_; + } + + // Set instance to NULL and tell the factory we aren't using the instance + // anymore. + void release() { + if (instance_) { + instance_ = NULL; + factory_->ReleaseInstance(); + } + } + + // Lets us know whether instance is valid or not right now. + // Even though attempts to use the instance will automatically create it, it + // is advisable to check this because creation can fail. + bool valid() const { + return instance_ != NULL; + } + + // Returns the factory that this pointer is using. + ReferenceCountedSingletonFactory* factory() const { + return factory_; + } + + private: + void EnsureAcquired() { + Acquire(); + ASSERT(instance_ != NULL); + } + + void Acquire() { + // Since we're getting a singleton back, acquire is a noop if instance is + // already populated. + if (!instance_) { + instance_ = factory_->GetInstance(); + } + } + + Interface* instance_; + ReferenceCountedSingletonFactory* factory_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(rcsf_ptr); +}; + +}; // namespace rtc + +#endif // WEBRTC_BASE_REFERENCECOUNTEDSINGLETONFACTORY_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/referencecountedsingletonfactory_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/referencecountedsingletonfactory_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/referencecountedsingletonfactory_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/referencecountedsingletonfactory_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,132 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/referencecountedsingletonfactory.h" + +namespace rtc { + +class MyExistenceWatcher { + public: + MyExistenceWatcher() { create_called_ = true; } + ~MyExistenceWatcher() { delete_called_ = true; } + + static bool create_called_; + static bool delete_called_; +}; + +bool MyExistenceWatcher::create_called_ = false; +bool MyExistenceWatcher::delete_called_ = false; + +class TestReferenceCountedSingletonFactory : + public ReferenceCountedSingletonFactory { + protected: + virtual bool SetupInstance() { + instance_.reset(new MyExistenceWatcher()); + return true; + } + + virtual void CleanupInstance() { + instance_.reset(); + } +}; + +static void DoCreateAndGoOutOfScope( + ReferenceCountedSingletonFactory *factory) { + rcsf_ptr ptr(factory); + ptr.get(); + // and now ptr should go out of scope. +} + +TEST(ReferenceCountedSingletonFactory, ZeroReferenceCountCausesDeletion) { + TestReferenceCountedSingletonFactory factory; + MyExistenceWatcher::delete_called_ = false; + DoCreateAndGoOutOfScope(&factory); + EXPECT_TRUE(MyExistenceWatcher::delete_called_); +} + +TEST(ReferenceCountedSingletonFactory, NonZeroReferenceCountDoesNotDelete) { + TestReferenceCountedSingletonFactory factory; + rcsf_ptr ptr(&factory); + ptr.get(); + MyExistenceWatcher::delete_called_ = false; + DoCreateAndGoOutOfScope(&factory); + EXPECT_FALSE(MyExistenceWatcher::delete_called_); +} + +TEST(ReferenceCountedSingletonFactory, ReturnedPointersReferToSameThing) { + TestReferenceCountedSingletonFactory factory; + rcsf_ptr one(&factory), two(&factory); + + EXPECT_EQ(one.get(), two.get()); +} + +TEST(ReferenceCountedSingletonFactory, Release) { + TestReferenceCountedSingletonFactory factory; + + rcsf_ptr one(&factory); + one.get(); + + MyExistenceWatcher::delete_called_ = false; + one.release(); + EXPECT_TRUE(MyExistenceWatcher::delete_called_); +} + +TEST(ReferenceCountedSingletonFactory, GetWithoutRelease) { + TestReferenceCountedSingletonFactory factory; + rcsf_ptr one(&factory); + one.get(); + + MyExistenceWatcher::create_called_ = false; + one.get(); + EXPECT_FALSE(MyExistenceWatcher::create_called_); +} + +TEST(ReferenceCountedSingletonFactory, GetAfterRelease) { + TestReferenceCountedSingletonFactory factory; + rcsf_ptr one(&factory); + + MyExistenceWatcher::create_called_ = false; + one.release(); + one.get(); + EXPECT_TRUE(MyExistenceWatcher::create_called_); +} + +TEST(ReferenceCountedSingletonFactory, MultipleReleases) { + TestReferenceCountedSingletonFactory factory; + rcsf_ptr one(&factory), two(&factory); + + MyExistenceWatcher::create_called_ = false; + MyExistenceWatcher::delete_called_ = false; + one.release(); + EXPECT_FALSE(MyExistenceWatcher::delete_called_); + one.release(); + EXPECT_FALSE(MyExistenceWatcher::delete_called_); + one.release(); + EXPECT_FALSE(MyExistenceWatcher::delete_called_); + one.get(); + EXPECT_TRUE(MyExistenceWatcher::create_called_); +} + +TEST(ReferenceCountedSingletonFactory, Existentialism) { + TestReferenceCountedSingletonFactory factory; + + rcsf_ptr one(&factory); + + MyExistenceWatcher::create_called_ = false; + MyExistenceWatcher::delete_called_ = false; + + one.get(); + EXPECT_TRUE(MyExistenceWatcher::create_called_); + one.release(); + EXPECT_TRUE(MyExistenceWatcher::delete_called_); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/rollingaccumulator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/rollingaccumulator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/rollingaccumulator.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/rollingaccumulator.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,172 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_ROLLINGACCUMULATOR_H_ +#define WEBRTC_BASE_ROLLINGACCUMULATOR_H_ + +#include + +#include "webrtc/base/common.h" + +namespace rtc { + +// RollingAccumulator stores and reports statistics +// over N most recent samples. +// +// T is assumed to be an int, long, double or float. +template +class RollingAccumulator { + public: + explicit RollingAccumulator(size_t max_count) + : samples_(max_count) { + Reset(); + } + ~RollingAccumulator() { + } + + size_t max_count() const { + return samples_.size(); + } + + size_t count() const { + return count_; + } + + void Reset() { + count_ = 0U; + next_index_ = 0U; + sum_ = 0.0; + sum_2_ = 0.0; + max_ = T(); + max_stale_ = false; + min_ = T(); + min_stale_ = false; + } + + void AddSample(T sample) { + if (count_ == max_count()) { + // Remove oldest sample. + T sample_to_remove = samples_[next_index_]; + sum_ -= sample_to_remove; + sum_2_ -= sample_to_remove * sample_to_remove; + if (sample_to_remove >= max_) { + max_stale_ = true; + } + if (sample_to_remove <= min_) { + min_stale_ = true; + } + } else { + // Increase count of samples. + ++count_; + } + // Add new sample. + samples_[next_index_] = sample; + sum_ += sample; + sum_2_ += sample * sample; + if (count_ == 1 || sample >= max_) { + max_ = sample; + max_stale_ = false; + } + if (count_ == 1 || sample <= min_) { + min_ = sample; + min_stale_ = false; + } + // Update next_index_. + next_index_ = (next_index_ + 1) % max_count(); + } + + T ComputeSum() const { + return static_cast(sum_); + } + + double ComputeMean() const { + if (count_ == 0) { + return 0.0; + } + return sum_ / count_; + } + + T ComputeMax() const { + if (max_stale_) { + ASSERT(count_ > 0 && + "It shouldn't be possible for max_stale_ && count_ == 0"); + max_ = samples_[next_index_]; + for (size_t i = 1u; i < count_; i++) { + max_ = _max(max_, samples_[(next_index_ + i) % max_count()]); + } + max_stale_ = false; + } + return max_; + } + + T ComputeMin() const { + if (min_stale_) { + ASSERT(count_ > 0 && + "It shouldn't be possible for min_stale_ && count_ == 0"); + min_ = samples_[next_index_]; + for (size_t i = 1u; i < count_; i++) { + min_ = _min(min_, samples_[(next_index_ + i) % max_count()]); + } + min_stale_ = false; + } + return min_; + } + + // O(n) time complexity. + // Weights nth sample with weight (learning_rate)^n. Learning_rate should be + // between (0.0, 1.0], otherwise the non-weighted mean is returned. + double ComputeWeightedMean(double learning_rate) const { + if (count_ < 1 || learning_rate <= 0.0 || learning_rate >= 1.0) { + return ComputeMean(); + } + double weighted_mean = 0.0; + double current_weight = 1.0; + double weight_sum = 0.0; + const size_t max_size = max_count(); + for (size_t i = 0; i < count_; ++i) { + current_weight *= learning_rate; + weight_sum += current_weight; + // Add max_size to prevent underflow. + size_t index = (next_index_ + max_size - i - 1) % max_size; + weighted_mean += current_weight * samples_[index]; + } + return weighted_mean / weight_sum; + } + + // Compute estimated variance. Estimation is more accurate + // as the number of samples grows. + double ComputeVariance() const { + if (count_ == 0) { + return 0.0; + } + // Var = E[x^2] - (E[x])^2 + double count_inv = 1.0 / count_; + double mean_2 = sum_2_ * count_inv; + double mean = sum_ * count_inv; + return mean_2 - (mean * mean); + } + + private: + size_t count_; + size_t next_index_; + double sum_; // Sum(x) - double to avoid overflow + double sum_2_; // Sum(x*x) - double to avoid overflow + mutable T max_; + mutable bool max_stale_; + mutable T min_; + mutable bool min_stale_; + std::vector samples_; + + DISALLOW_COPY_AND_ASSIGN(RollingAccumulator); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_ROLLINGACCUMULATOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/rollingaccumulator_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/rollingaccumulator_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/rollingaccumulator_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/rollingaccumulator_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,118 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/rollingaccumulator.h" + +namespace rtc { + +namespace { + +const double kLearningRate = 0.5; + +} // namespace + +TEST(RollingAccumulatorTest, ZeroSamples) { + RollingAccumulator accum(10); + + EXPECT_EQ(0U, accum.count()); + EXPECT_DOUBLE_EQ(0.0, accum.ComputeMean()); + EXPECT_DOUBLE_EQ(0.0, accum.ComputeVariance()); + EXPECT_EQ(0, accum.ComputeMin()); + EXPECT_EQ(0, accum.ComputeMax()); +} + +TEST(RollingAccumulatorTest, SomeSamples) { + RollingAccumulator accum(10); + for (int i = 0; i < 4; ++i) { + accum.AddSample(i); + } + + EXPECT_EQ(4U, accum.count()); + EXPECT_EQ(6, accum.ComputeSum()); + EXPECT_DOUBLE_EQ(1.5, accum.ComputeMean()); + EXPECT_NEAR(2.26666, accum.ComputeWeightedMean(kLearningRate), 0.01); + EXPECT_DOUBLE_EQ(1.25, accum.ComputeVariance()); + EXPECT_EQ(0, accum.ComputeMin()); + EXPECT_EQ(3, accum.ComputeMax()); +} + +TEST(RollingAccumulatorTest, RollingSamples) { + RollingAccumulator accum(10); + for (int i = 0; i < 12; ++i) { + accum.AddSample(i); + } + + EXPECT_EQ(10U, accum.count()); + EXPECT_EQ(65, accum.ComputeSum()); + EXPECT_DOUBLE_EQ(6.5, accum.ComputeMean()); + EXPECT_NEAR(10.0, accum.ComputeWeightedMean(kLearningRate), 0.01); + EXPECT_NEAR(9.0, accum.ComputeVariance(), 1.0); + EXPECT_EQ(2, accum.ComputeMin()); + EXPECT_EQ(11, accum.ComputeMax()); +} + +TEST(RollingAccumulatorTest, ResetSamples) { + RollingAccumulator accum(10); + + for (int i = 0; i < 10; ++i) { + accum.AddSample(100); + } + EXPECT_EQ(10U, accum.count()); + EXPECT_DOUBLE_EQ(100.0, accum.ComputeMean()); + EXPECT_EQ(100, accum.ComputeMin()); + EXPECT_EQ(100, accum.ComputeMax()); + + accum.Reset(); + EXPECT_EQ(0U, accum.count()); + + for (int i = 0; i < 5; ++i) { + accum.AddSample(i); + } + + EXPECT_EQ(5U, accum.count()); + EXPECT_EQ(10, accum.ComputeSum()); + EXPECT_DOUBLE_EQ(2.0, accum.ComputeMean()); + EXPECT_EQ(0, accum.ComputeMin()); + EXPECT_EQ(4, accum.ComputeMax()); +} + +TEST(RollingAccumulatorTest, RollingSamplesDouble) { + RollingAccumulator accum(10); + for (int i = 0; i < 23; ++i) { + accum.AddSample(5 * i); + } + + EXPECT_EQ(10u, accum.count()); + EXPECT_DOUBLE_EQ(875.0, accum.ComputeSum()); + EXPECT_DOUBLE_EQ(87.5, accum.ComputeMean()); + EXPECT_NEAR(105.049, accum.ComputeWeightedMean(kLearningRate), 0.1); + EXPECT_NEAR(229.166667, accum.ComputeVariance(), 25); + EXPECT_DOUBLE_EQ(65.0, accum.ComputeMin()); + EXPECT_DOUBLE_EQ(110.0, accum.ComputeMax()); +} + +TEST(RollingAccumulatorTest, ComputeWeightedMeanCornerCases) { + RollingAccumulator accum(10); + EXPECT_DOUBLE_EQ(0.0, accum.ComputeWeightedMean(kLearningRate)); + EXPECT_DOUBLE_EQ(0.0, accum.ComputeWeightedMean(0.0)); + EXPECT_DOUBLE_EQ(0.0, accum.ComputeWeightedMean(1.1)); + + for (int i = 0; i < 8; ++i) { + accum.AddSample(i); + } + + EXPECT_DOUBLE_EQ(3.5, accum.ComputeMean()); + EXPECT_DOUBLE_EQ(3.5, accum.ComputeWeightedMean(0)); + EXPECT_DOUBLE_EQ(3.5, accum.ComputeWeightedMean(1.1)); + EXPECT_NEAR(6.0, accum.ComputeWeightedMean(kLearningRate), 0.1); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/safe_conversions.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/safe_conversions.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/safe_conversions.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/safe_conversions.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,79 @@ +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Borrowed from Chromium's src/base/numerics/safe_conversions.h. + +#ifndef WEBRTC_BASE_SAFE_CONVERSIONS_H_ +#define WEBRTC_BASE_SAFE_CONVERSIONS_H_ + +#include + +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/safe_conversions_impl.h" + +namespace rtc { + +inline void Check(bool condition) { + if (!condition) { + LOG(LS_ERROR) << "CHECK failed."; + Break(); + // The program should have crashed at this point. + } +} + +// Convenience function that returns true if the supplied value is in range +// for the destination type. +template +inline bool IsValueInRangeForNumericType(Src value) { + return internal::RangeCheck(value) == internal::TYPE_VALID; +} + +// checked_cast<> is analogous to static_cast<> for numeric types, +// except that it CHECKs that the specified numeric conversion will not +// overflow or underflow. NaN source will always trigger a CHECK. +template +inline Dst checked_cast(Src value) { + Check(IsValueInRangeForNumericType(value)); + return static_cast(value); +} + +// saturated_cast<> is analogous to static_cast<> for numeric types, except +// that the specified numeric conversion will saturate rather than overflow or +// underflow. NaN assignment to an integral will trigger a CHECK condition. +template +inline Dst saturated_cast(Src value) { + // Optimization for floating point values, which already saturate. + if (std::numeric_limits::is_iec559) + return static_cast(value); + + switch (internal::RangeCheck(value)) { + case internal::TYPE_VALID: + return static_cast(value); + + case internal::TYPE_UNDERFLOW: + return std::numeric_limits::min(); + + case internal::TYPE_OVERFLOW: + return std::numeric_limits::max(); + + // Should fail only on attempting to assign NaN to a saturated integer. + case internal::TYPE_INVALID: + Check(false); + return std::numeric_limits::max(); + } + + Check(false); // NOTREACHED(); + return static_cast(value); +} + +} // namespace rtc + +#endif // WEBRTC_BASE_SAFE_CONVERSIONS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/safe_conversions_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/safe_conversions_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/safe_conversions_impl.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/safe_conversions_impl.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,190 @@ +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Borrowed from Chromium's src/base/numerics/safe_conversions_impl.h. + +#ifndef WEBRTC_BASE_SAFE_CONVERSIONS_IMPL_H_ +#define WEBRTC_BASE_SAFE_CONVERSIONS_IMPL_H_ + +#include + +#include "webrtc/base/compile_assert.h" + +namespace rtc { +namespace internal { + +enum DstSign { + DST_UNSIGNED, + DST_SIGNED +}; + +enum SrcSign { + SRC_UNSIGNED, + SRC_SIGNED +}; + +enum DstRange { + OVERLAPS_RANGE, + CONTAINS_RANGE +}; + +// Helper templates to statically determine if our destination type can contain +// all values represented by the source type. + +template ::is_signed ? + DST_SIGNED : DST_UNSIGNED, + SrcSign IsSrcSigned = std::numeric_limits::is_signed ? + SRC_SIGNED : SRC_UNSIGNED> +struct StaticRangeCheck {}; + +template +struct StaticRangeCheck { + typedef std::numeric_limits DstLimits; + typedef std::numeric_limits SrcLimits; + // Compare based on max_exponent, which we must compute for integrals. + static const size_t kDstMaxExponent = DstLimits::is_iec559 ? + DstLimits::max_exponent : + (sizeof(Dst) * 8 - 1); + static const size_t kSrcMaxExponent = SrcLimits::is_iec559 ? + SrcLimits::max_exponent : + (sizeof(Src) * 8 - 1); + static const DstRange value = kDstMaxExponent >= kSrcMaxExponent ? + CONTAINS_RANGE : OVERLAPS_RANGE; +}; + +template +struct StaticRangeCheck { + static const DstRange value = sizeof(Dst) >= sizeof(Src) ? + CONTAINS_RANGE : OVERLAPS_RANGE; +}; + +template +struct StaticRangeCheck { + typedef std::numeric_limits DstLimits; + typedef std::numeric_limits SrcLimits; + // Compare based on max_exponent, which we must compute for integrals. + static const size_t kDstMaxExponent = DstLimits::is_iec559 ? + DstLimits::max_exponent : + (sizeof(Dst) * 8 - 1); + static const size_t kSrcMaxExponent = sizeof(Src) * 8; + static const DstRange value = kDstMaxExponent >= kSrcMaxExponent ? + CONTAINS_RANGE : OVERLAPS_RANGE; +}; + +template +struct StaticRangeCheck { + static const DstRange value = OVERLAPS_RANGE; +}; + + +enum RangeCheckResult { + TYPE_VALID = 0, // Value can be represented by the destination type. + TYPE_UNDERFLOW = 1, // Value would overflow. + TYPE_OVERFLOW = 2, // Value would underflow. + TYPE_INVALID = 3 // Source value is invalid (i.e. NaN). +}; + +// This macro creates a RangeCheckResult from an upper and lower bound +// check by taking advantage of the fact that only NaN can be out of range in +// both directions at once. +#define BASE_NUMERIC_RANGE_CHECK_RESULT(is_in_upper_bound, is_in_lower_bound) \ + RangeCheckResult(((is_in_upper_bound) ? 0 : TYPE_OVERFLOW) | \ + ((is_in_lower_bound) ? 0 : TYPE_UNDERFLOW)) + +template ::is_signed ? + DST_SIGNED : DST_UNSIGNED, + SrcSign IsSrcSigned = std::numeric_limits::is_signed ? + SRC_SIGNED : SRC_UNSIGNED, + DstRange IsSrcRangeContained = StaticRangeCheck::value> +struct RangeCheckImpl {}; + +// The following templates are for ranges that must be verified at runtime. We +// split it into checks based on signedness to avoid confusing casts and +// compiler warnings on signed an unsigned comparisons. + +// Dst range always contains the result: nothing to check. +template +struct RangeCheckImpl { + static RangeCheckResult Check(Src value) { + return TYPE_VALID; + } +}; + +// Signed to signed narrowing. +template +struct RangeCheckImpl { + static RangeCheckResult Check(Src value) { + typedef std::numeric_limits DstLimits; + return DstLimits::is_iec559 ? + BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), + value >= static_cast(DstLimits::max() * -1)) : + BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), + value >= static_cast(DstLimits::min())); + } +}; + +// Unsigned to unsigned narrowing. +template +struct RangeCheckImpl { + static RangeCheckResult Check(Src value) { + typedef std::numeric_limits DstLimits; + return BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), true); + } +}; + +// Unsigned to signed. +template +struct RangeCheckImpl { + static RangeCheckResult Check(Src value) { + typedef std::numeric_limits DstLimits; + return sizeof(Dst) > sizeof(Src) ? TYPE_VALID : + BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), true); + } +}; + +// Signed to unsigned. +template +struct RangeCheckImpl { + static RangeCheckResult Check(Src value) { + typedef std::numeric_limits DstLimits; + typedef std::numeric_limits SrcLimits; + // Compare based on max_exponent, which we must compute for integrals. + static const size_t kDstMaxExponent = sizeof(Dst) * 8; + static const size_t kSrcMaxExponent = SrcLimits::is_iec559 ? + SrcLimits::max_exponent : + (sizeof(Src) * 8 - 1); + return (kDstMaxExponent >= kSrcMaxExponent) ? + BASE_NUMERIC_RANGE_CHECK_RESULT(true, value >= static_cast(0)) : + BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), + value >= static_cast(0)); + } +}; + +template +inline RangeCheckResult RangeCheck(Src value) { + COMPILE_ASSERT(std::numeric_limits::is_specialized, + argument_must_be_numeric); + COMPILE_ASSERT(std::numeric_limits::is_specialized, + result_must_be_numeric); + return RangeCheckImpl::Check(value); +} + +} // namespace internal +} // namespace rtc + +#endif // WEBRTC_BASE_SAFE_CONVERSIONS_IMPL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/schanneladapter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/schanneladapter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/schanneladapter.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/schanneladapter.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,703 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/win32.h" +#define SECURITY_WIN32 +#include +#include + +#include +#include + +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/schanneladapter.h" +#include "webrtc/base/sec_buffer.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +///////////////////////////////////////////////////////////////////////////// +// SChannelAdapter +///////////////////////////////////////////////////////////////////////////// + +extern const ConstantLabel SECURITY_ERRORS[]; + +const ConstantLabel SCHANNEL_BUFFER_TYPES[] = { + KLABEL(SECBUFFER_EMPTY), // 0 + KLABEL(SECBUFFER_DATA), // 1 + KLABEL(SECBUFFER_TOKEN), // 2 + KLABEL(SECBUFFER_PKG_PARAMS), // 3 + KLABEL(SECBUFFER_MISSING), // 4 + KLABEL(SECBUFFER_EXTRA), // 5 + KLABEL(SECBUFFER_STREAM_TRAILER), // 6 + KLABEL(SECBUFFER_STREAM_HEADER), // 7 + KLABEL(SECBUFFER_MECHLIST), // 11 + KLABEL(SECBUFFER_MECHLIST_SIGNATURE), // 12 + KLABEL(SECBUFFER_TARGET), // 13 + KLABEL(SECBUFFER_CHANNEL_BINDINGS), // 14 + LASTLABEL +}; + +void DescribeBuffer(LoggingSeverity severity, const char* prefix, + const SecBuffer& sb) { + LOG_V(severity) + << prefix + << "(" << sb.cbBuffer + << ", " << FindLabel(sb.BufferType & ~SECBUFFER_ATTRMASK, + SCHANNEL_BUFFER_TYPES) + << ", " << sb.pvBuffer << ")"; +} + +void DescribeBuffers(LoggingSeverity severity, const char* prefix, + const SecBufferDesc* sbd) { + if (!LOG_CHECK_LEVEL_V(severity)) + return; + LOG_V(severity) << prefix << "("; + for (size_t i=0; icBuffers; ++i) { + DescribeBuffer(severity, " ", sbd->pBuffers[i]); + } + LOG_V(severity) << ")"; +} + +const ULONG SSL_FLAGS_DEFAULT = ISC_REQ_ALLOCATE_MEMORY + | ISC_REQ_CONFIDENTIALITY + | ISC_REQ_EXTENDED_ERROR + | ISC_REQ_INTEGRITY + | ISC_REQ_REPLAY_DETECT + | ISC_REQ_SEQUENCE_DETECT + | ISC_REQ_STREAM; + //| ISC_REQ_USE_SUPPLIED_CREDS; + +typedef std::vector SChannelBuffer; + +struct SChannelAdapter::SSLImpl { + CredHandle cred; + CtxtHandle ctx; + bool cred_init, ctx_init; + SChannelBuffer inbuf, outbuf, readable; + SecPkgContext_StreamSizes sizes; + + SSLImpl() : cred_init(false), ctx_init(false) { } +}; + +SChannelAdapter::SChannelAdapter(AsyncSocket* socket) + : SSLAdapter(socket), state_(SSL_NONE), + restartable_(false), signal_close_(false), message_pending_(false), + impl_(new SSLImpl) { +} + +SChannelAdapter::~SChannelAdapter() { + Cleanup(); +} + +int +SChannelAdapter::StartSSL(const char* hostname, bool restartable) { + if (state_ != SSL_NONE) + return ERROR_ALREADY_INITIALIZED; + + ssl_host_name_ = hostname; + restartable_ = restartable; + + if (socket_->GetState() != Socket::CS_CONNECTED) { + state_ = SSL_WAIT; + return 0; + } + + state_ = SSL_CONNECTING; + if (int err = BeginSSL()) { + Error("BeginSSL", err, false); + return err; + } + + return 0; +} + +int +SChannelAdapter::BeginSSL() { + LOG(LS_VERBOSE) << "BeginSSL: " << ssl_host_name_; + ASSERT(state_ == SSL_CONNECTING); + + SECURITY_STATUS ret; + + SCHANNEL_CRED sc_cred = { 0 }; + sc_cred.dwVersion = SCHANNEL_CRED_VERSION; + //sc_cred.dwMinimumCipherStrength = 128; // Note: use system default + sc_cred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_AUTO_CRED_VALIDATION; + + ret = AcquireCredentialsHandle(NULL, const_cast(UNISP_NAME), + SECPKG_CRED_OUTBOUND, NULL, &sc_cred, NULL, + NULL, &impl_->cred, NULL); + if (ret != SEC_E_OK) { + LOG(LS_ERROR) << "AcquireCredentialsHandle error: " + << ErrorName(ret, SECURITY_ERRORS); + return ret; + } + impl_->cred_init = true; + + if (LOG_CHECK_LEVEL(LS_VERBOSE)) { + SecPkgCred_CipherStrengths cipher_strengths = { 0 }; + ret = QueryCredentialsAttributes(&impl_->cred, + SECPKG_ATTR_CIPHER_STRENGTHS, + &cipher_strengths); + if (SUCCEEDED(ret)) { + LOG(LS_VERBOSE) << "SChannel cipher strength: " + << cipher_strengths.dwMinimumCipherStrength << " - " + << cipher_strengths.dwMaximumCipherStrength; + } + + SecPkgCred_SupportedAlgs supported_algs = { 0 }; + ret = QueryCredentialsAttributes(&impl_->cred, + SECPKG_ATTR_SUPPORTED_ALGS, + &supported_algs); + if (SUCCEEDED(ret)) { + LOG(LS_VERBOSE) << "SChannel supported algorithms:"; + for (DWORD i=0; ipwszName : L"Unknown"; + LOG(LS_VERBOSE) << " " << ToUtf8(alg_name) << " (" << alg_id << ")"; + } + CSecBufferBase::FreeSSPI(supported_algs.palgSupportedAlgs); + } + } + + ULONG flags = SSL_FLAGS_DEFAULT, ret_flags = 0; + if (ignore_bad_cert()) + flags |= ISC_REQ_MANUAL_CRED_VALIDATION; + + CSecBufferBundle<2, CSecBufferBase::FreeSSPI> sb_out; + ret = InitializeSecurityContextA(&impl_->cred, NULL, + const_cast(ssl_host_name_.c_str()), + flags, 0, 0, NULL, 0, + &impl_->ctx, sb_out.desc(), + &ret_flags, NULL); + if (SUCCEEDED(ret)) + impl_->ctx_init = true; + return ProcessContext(ret, NULL, sb_out.desc()); +} + +int +SChannelAdapter::ContinueSSL() { + LOG(LS_VERBOSE) << "ContinueSSL"; + ASSERT(state_ == SSL_CONNECTING); + + SECURITY_STATUS ret; + + CSecBufferBundle<2> sb_in; + sb_in[0].BufferType = SECBUFFER_TOKEN; + sb_in[0].cbBuffer = static_cast(impl_->inbuf.size()); + sb_in[0].pvBuffer = &impl_->inbuf[0]; + //DescribeBuffers(LS_VERBOSE, "Input Buffer ", sb_in.desc()); + + ULONG flags = SSL_FLAGS_DEFAULT, ret_flags = 0; + if (ignore_bad_cert()) + flags |= ISC_REQ_MANUAL_CRED_VALIDATION; + + CSecBufferBundle<2, CSecBufferBase::FreeSSPI> sb_out; + ret = InitializeSecurityContextA(&impl_->cred, &impl_->ctx, + const_cast(ssl_host_name_.c_str()), + flags, 0, 0, sb_in.desc(), 0, + NULL, sb_out.desc(), + &ret_flags, NULL); + return ProcessContext(ret, sb_in.desc(), sb_out.desc()); +} + +int +SChannelAdapter::ProcessContext(long int status, _SecBufferDesc* sbd_in, + _SecBufferDesc* sbd_out) { + if (status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED && + status != SEC_E_INCOMPLETE_MESSAGE) { + LOG(LS_ERROR) + << "InitializeSecurityContext error: " + << ErrorName(status, SECURITY_ERRORS); + } + //if (sbd_in) + // DescribeBuffers(LS_VERBOSE, "Input Buffer ", sbd_in); + //if (sbd_out) + // DescribeBuffers(LS_VERBOSE, "Output Buffer ", sbd_out); + + if (status == SEC_E_INCOMPLETE_MESSAGE) { + // Wait for more input from server. + return Flush(); + } + + if (FAILED(status)) { + // We can't continue. Common errors: + // SEC_E_CERT_EXPIRED - Typically, this means the computer clock is wrong. + return status; + } + + // Note: we check both input and output buffers for SECBUFFER_EXTRA. + // Experience shows it appearing in the input, but the documentation claims + // it should appear in the output. + size_t extra = 0; + if (sbd_in) { + for (size_t i=0; icBuffers; ++i) { + SecBuffer& buffer = sbd_in->pBuffers[i]; + if (buffer.BufferType == SECBUFFER_EXTRA) { + extra += buffer.cbBuffer; + } + } + } + if (sbd_out) { + for (size_t i=0; icBuffers; ++i) { + SecBuffer& buffer = sbd_out->pBuffers[i]; + if (buffer.BufferType == SECBUFFER_EXTRA) { + extra += buffer.cbBuffer; + } else if (buffer.BufferType == SECBUFFER_TOKEN) { + impl_->outbuf.insert(impl_->outbuf.end(), + reinterpret_cast(buffer.pvBuffer), + reinterpret_cast(buffer.pvBuffer) + buffer.cbBuffer); + } + } + } + + if (extra) { + ASSERT(extra <= impl_->inbuf.size()); + size_t consumed = impl_->inbuf.size() - extra; + memmove(&impl_->inbuf[0], &impl_->inbuf[consumed], extra); + impl_->inbuf.resize(extra); + } else { + impl_->inbuf.clear(); + } + + if (SEC_I_CONTINUE_NEEDED == status) { + // Send data to server and wait for response. + // Note: ContinueSSL will result in a Flush, anyway. + return impl_->inbuf.empty() ? Flush() : ContinueSSL(); + } + + if (SEC_E_OK == status) { + LOG(LS_VERBOSE) << "QueryContextAttributes"; + status = QueryContextAttributes(&impl_->ctx, SECPKG_ATTR_STREAM_SIZES, + &impl_->sizes); + if (FAILED(status)) { + LOG(LS_ERROR) << "QueryContextAttributes error: " + << ErrorName(status, SECURITY_ERRORS); + return status; + } + + state_ = SSL_CONNECTED; + + if (int err = DecryptData()) { + return err; + } else if (int err = Flush()) { + return err; + } else { + // If we decrypted any data, queue up a notification here + PostEvent(); + // Signal our connectedness + AsyncSocketAdapter::OnConnectEvent(this); + } + return 0; + } + + if (SEC_I_INCOMPLETE_CREDENTIALS == status) { + // We don't support client authentication in schannel. + return status; + } + + // We don't expect any other codes + ASSERT(false); + return status; +} + +int +SChannelAdapter::DecryptData() { + SChannelBuffer& inbuf = impl_->inbuf; + SChannelBuffer& readable = impl_->readable; + + while (!inbuf.empty()) { + CSecBufferBundle<4> in_buf; + in_buf[0].BufferType = SECBUFFER_DATA; + in_buf[0].cbBuffer = static_cast(inbuf.size()); + in_buf[0].pvBuffer = &inbuf[0]; + + //DescribeBuffers(LS_VERBOSE, "Decrypt In ", in_buf.desc()); + SECURITY_STATUS status = DecryptMessage(&impl_->ctx, in_buf.desc(), 0, 0); + //DescribeBuffers(LS_VERBOSE, "Decrypt Out ", in_buf.desc()); + + // Note: We are explicitly treating SEC_E_OK, SEC_I_CONTEXT_EXPIRED, and + // any other successful results as continue. + if (SUCCEEDED(status)) { + size_t data_len = 0, extra_len = 0; + for (size_t i=0; icBuffers; ++i) { + if (in_buf[i].BufferType == SECBUFFER_DATA) { + data_len += in_buf[i].cbBuffer; + readable.insert(readable.end(), + reinterpret_cast(in_buf[i].pvBuffer), + reinterpret_cast(in_buf[i].pvBuffer) + in_buf[i].cbBuffer); + } else if (in_buf[i].BufferType == SECBUFFER_EXTRA) { + extra_len += in_buf[i].cbBuffer; + } + } + // There is a bug on Win2K where SEC_I_CONTEXT_EXPIRED is misclassified. + if ((data_len == 0) && (inbuf[0] == 0x15)) { + status = SEC_I_CONTEXT_EXPIRED; + } + if (extra_len) { + size_t consumed = inbuf.size() - extra_len; + memmove(&inbuf[0], &inbuf[consumed], extra_len); + inbuf.resize(extra_len); + } else { + inbuf.clear(); + } + // TODO: Handle SEC_I_CONTEXT_EXPIRED to do clean shutdown + if (status != SEC_E_OK) { + LOG(LS_INFO) << "DecryptMessage returned continuation code: " + << ErrorName(status, SECURITY_ERRORS); + } + continue; + } + + if (status == SEC_E_INCOMPLETE_MESSAGE) { + break; + } else { + return status; + } + } + + return 0; +} + +void +SChannelAdapter::Cleanup() { + if (impl_->ctx_init) + DeleteSecurityContext(&impl_->ctx); + if (impl_->cred_init) + FreeCredentialsHandle(&impl_->cred); + delete impl_; +} + +void +SChannelAdapter::PostEvent() { + // Check if there's anything notable to signal + if (impl_->readable.empty() && !signal_close_) + return; + + // Only one post in the queue at a time + if (message_pending_) + return; + + if (Thread* thread = Thread::Current()) { + message_pending_ = true; + thread->Post(this); + } else { + LOG(LS_ERROR) << "No thread context available for SChannelAdapter"; + ASSERT(false); + } +} + +void +SChannelAdapter::Error(const char* context, int err, bool signal) { + LOG(LS_WARNING) << "SChannelAdapter::Error(" + << context << ", " + << ErrorName(err, SECURITY_ERRORS) << ")"; + state_ = SSL_ERROR; + SetError(err); + if (signal) + AsyncSocketAdapter::OnCloseEvent(this, err); +} + +int +SChannelAdapter::Read() { + char buffer[4096]; + SChannelBuffer& inbuf = impl_->inbuf; + while (true) { + int ret = AsyncSocketAdapter::Recv(buffer, sizeof(buffer)); + if (ret > 0) { + inbuf.insert(inbuf.end(), buffer, buffer + ret); + } else if (GetError() == EWOULDBLOCK) { + return 0; // Blocking + } else { + return GetError(); + } + } +} + +int +SChannelAdapter::Flush() { + int result = 0; + size_t pos = 0; + SChannelBuffer& outbuf = impl_->outbuf; + while (pos < outbuf.size()) { + int sent = AsyncSocketAdapter::Send(&outbuf[pos], outbuf.size() - pos); + if (sent > 0) { + pos += sent; + } else if (GetError() == EWOULDBLOCK) { + break; // Blocking + } else { + result = GetError(); + break; + } + } + if (int remainder = static_cast(outbuf.size() - pos)) { + memmove(&outbuf[0], &outbuf[pos], remainder); + outbuf.resize(remainder); + } else { + outbuf.clear(); + } + return result; +} + +// +// AsyncSocket Implementation +// + +int +SChannelAdapter::Send(const void* pv, size_t cb) { + switch (state_) { + case SSL_NONE: + return AsyncSocketAdapter::Send(pv, cb); + + case SSL_WAIT: + case SSL_CONNECTING: + SetError(EWOULDBLOCK); + return SOCKET_ERROR; + + case SSL_CONNECTED: + break; + + case SSL_ERROR: + default: + return SOCKET_ERROR; + } + + size_t written = 0; + SChannelBuffer& outbuf = impl_->outbuf; + while (written < cb) { + const size_t encrypt_len = std::min(cb - written, + impl_->sizes.cbMaximumMessage); + + CSecBufferBundle<4> out_buf; + out_buf[0].BufferType = SECBUFFER_STREAM_HEADER; + out_buf[0].cbBuffer = impl_->sizes.cbHeader; + out_buf[1].BufferType = SECBUFFER_DATA; + out_buf[1].cbBuffer = static_cast(encrypt_len); + out_buf[2].BufferType = SECBUFFER_STREAM_TRAILER; + out_buf[2].cbBuffer = impl_->sizes.cbTrailer; + + size_t packet_len = out_buf[0].cbBuffer + + out_buf[1].cbBuffer + + out_buf[2].cbBuffer; + + SChannelBuffer message; + message.resize(packet_len); + out_buf[0].pvBuffer = &message[0]; + out_buf[1].pvBuffer = &message[out_buf[0].cbBuffer]; + out_buf[2].pvBuffer = &message[out_buf[0].cbBuffer + out_buf[1].cbBuffer]; + + memcpy(out_buf[1].pvBuffer, + static_cast(pv) + written, + encrypt_len); + + //DescribeBuffers(LS_VERBOSE, "Encrypt In ", out_buf.desc()); + SECURITY_STATUS res = EncryptMessage(&impl_->ctx, 0, out_buf.desc(), 0); + //DescribeBuffers(LS_VERBOSE, "Encrypt Out ", out_buf.desc()); + + if (FAILED(res)) { + Error("EncryptMessage", res, false); + return SOCKET_ERROR; + } + + // We assume that the header and data segments do not change length, + // or else encrypting the concatenated packet in-place is wrong. + ASSERT(out_buf[0].cbBuffer == impl_->sizes.cbHeader); + ASSERT(out_buf[1].cbBuffer == static_cast(encrypt_len)); + + // However, the length of the trailer may change due to padding. + ASSERT(out_buf[2].cbBuffer <= impl_->sizes.cbTrailer); + + packet_len = out_buf[0].cbBuffer + + out_buf[1].cbBuffer + + out_buf[2].cbBuffer; + + written += encrypt_len; + outbuf.insert(outbuf.end(), &message[0], &message[packet_len-1]+1); + } + + if (int err = Flush()) { + state_ = SSL_ERROR; + SetError(err); + return SOCKET_ERROR; + } + + return static_cast(written); +} + +int +SChannelAdapter::Recv(void* pv, size_t cb) { + switch (state_) { + case SSL_NONE: + return AsyncSocketAdapter::Recv(pv, cb); + + case SSL_WAIT: + case SSL_CONNECTING: + SetError(EWOULDBLOCK); + return SOCKET_ERROR; + + case SSL_CONNECTED: + break; + + case SSL_ERROR: + default: + return SOCKET_ERROR; + } + + SChannelBuffer& readable = impl_->readable; + if (readable.empty()) { + SetError(EWOULDBLOCK); + return SOCKET_ERROR; + } + size_t read = _min(cb, readable.size()); + memcpy(pv, &readable[0], read); + if (size_t remaining = readable.size() - read) { + memmove(&readable[0], &readable[read], remaining); + readable.resize(remaining); + } else { + readable.clear(); + } + + PostEvent(); + return static_cast(read); +} + +int +SChannelAdapter::Close() { + if (!impl_->readable.empty()) { + LOG(WARNING) << "SChannelAdapter::Close with readable data"; + // Note: this isn't strictly an error, but we're using it temporarily to + // track bugs. + //ASSERT(false); + } + if (state_ == SSL_CONNECTED) { + DWORD token = SCHANNEL_SHUTDOWN; + CSecBufferBundle<1> sb_in; + sb_in[0].BufferType = SECBUFFER_TOKEN; + sb_in[0].cbBuffer = sizeof(token); + sb_in[0].pvBuffer = &token; + ApplyControlToken(&impl_->ctx, sb_in.desc()); + // TODO: In theory, to do a nice shutdown, we need to begin shutdown + // negotiation with more calls to InitializeSecurityContext. Since the + // socket api doesn't support nice shutdown at this point, we don't bother. + } + Cleanup(); + impl_ = new SSLImpl; + state_ = restartable_ ? SSL_WAIT : SSL_NONE; + signal_close_ = false; + message_pending_ = false; + return AsyncSocketAdapter::Close(); +} + +Socket::ConnState +SChannelAdapter::GetState() const { + if (signal_close_) + return CS_CONNECTED; + ConnState state = socket_->GetState(); + if ((state == CS_CONNECTED) + && ((state_ == SSL_WAIT) || (state_ == SSL_CONNECTING))) + state = CS_CONNECTING; + return state; +} + +void +SChannelAdapter::OnConnectEvent(AsyncSocket* socket) { + LOG(LS_VERBOSE) << "SChannelAdapter::OnConnectEvent"; + if (state_ != SSL_WAIT) { + ASSERT(state_ == SSL_NONE); + AsyncSocketAdapter::OnConnectEvent(socket); + return; + } + + state_ = SSL_CONNECTING; + if (int err = BeginSSL()) { + Error("BeginSSL", err); + } +} + +void +SChannelAdapter::OnReadEvent(AsyncSocket* socket) { + if (state_ == SSL_NONE) { + AsyncSocketAdapter::OnReadEvent(socket); + return; + } + + if (int err = Read()) { + Error("Read", err); + return; + } + + if (impl_->inbuf.empty()) + return; + + if (state_ == SSL_CONNECTED) { + if (int err = DecryptData()) { + Error("DecryptData", err); + } else if (!impl_->readable.empty()) { + AsyncSocketAdapter::OnReadEvent(this); + } + } else if (state_ == SSL_CONNECTING) { + if (int err = ContinueSSL()) { + Error("ContinueSSL", err); + } + } +} + +void +SChannelAdapter::OnWriteEvent(AsyncSocket* socket) { + if (state_ == SSL_NONE) { + AsyncSocketAdapter::OnWriteEvent(socket); + return; + } + + if (int err = Flush()) { + Error("Flush", err); + return; + } + + // See if we have more data to write + if (!impl_->outbuf.empty()) + return; + + // Buffer is empty, submit notification + if (state_ == SSL_CONNECTED) { + AsyncSocketAdapter::OnWriteEvent(socket); + } +} + +void +SChannelAdapter::OnCloseEvent(AsyncSocket* socket, int err) { + if ((state_ == SSL_NONE) || impl_->readable.empty()) { + AsyncSocketAdapter::OnCloseEvent(socket, err); + return; + } + + // If readable is non-empty, then we have a pending Message + // that will allow us to signal close (eventually). + signal_close_ = true; +} + +void +SChannelAdapter::OnMessage(Message* pmsg) { + if (!message_pending_) + return; // This occurs when socket is closed + + message_pending_ = false; + if (!impl_->readable.empty()) { + AsyncSocketAdapter::OnReadEvent(this); + } else if (signal_close_) { + signal_close_ = false; + AsyncSocketAdapter::OnCloseEvent(this, 0); // TODO: cache this error? + } +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/schanneladapter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/schanneladapter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/schanneladapter.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/schanneladapter.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,77 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SCHANNELADAPTER_H__ +#define WEBRTC_BASE_SCHANNELADAPTER_H__ + +#include +#include "webrtc/base/ssladapter.h" +#include "webrtc/base/messagequeue.h" +struct _SecBufferDesc; + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// + +class SChannelAdapter : public SSLAdapter, public MessageHandler { +public: + SChannelAdapter(AsyncSocket* socket); + virtual ~SChannelAdapter(); + + virtual int StartSSL(const char* hostname, bool restartable); + virtual int Send(const void* pv, size_t cb); + virtual int Recv(void* pv, size_t cb); + virtual int Close(); + + // Note that the socket returns ST_CONNECTING while SSL is being negotiated. + virtual ConnState GetState() const; + +protected: + enum SSLState { + SSL_NONE, SSL_WAIT, SSL_CONNECTING, SSL_CONNECTED, SSL_ERROR + }; + struct SSLImpl; + + virtual void OnConnectEvent(AsyncSocket* socket); + virtual void OnReadEvent(AsyncSocket* socket); + virtual void OnWriteEvent(AsyncSocket* socket); + virtual void OnCloseEvent(AsyncSocket* socket, int err); + virtual void OnMessage(Message* pmsg); + + int BeginSSL(); + int ContinueSSL(); + int ProcessContext(long int status, _SecBufferDesc* sbd_in, + _SecBufferDesc* sbd_out); + int DecryptData(); + + int Read(); + int Flush(); + void Error(const char* context, int err, bool signal = true); + void Cleanup(); + + void PostEvent(); + +private: + SSLState state_; + std::string ssl_host_name_; + // If true, socket will retain SSL configuration after Close. + bool restartable_; + // If true, we are delaying signalling close until all data is read. + bool signal_close_; + // If true, we are waiting to be woken up to signal readability or closure. + bool message_pending_; + SSLImpl* impl_; +}; + +///////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_SCHANNELADAPTER_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/scoped_autorelease_pool.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/scoped_autorelease_pool.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/scoped_autorelease_pool.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/scoped_autorelease_pool.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,59 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Automatically initialize and and free an autoreleasepool. Never allocate +// an instance of this class using "new" - that will result in a compile-time +// error. Only use it as a stack object. +// +// Note: NSAutoreleasePool docs say that you should not normally need to +// declare an NSAutoreleasePool as a member of an object - but there's nothing +// that indicates it will be a problem, as long as the stack lifetime of the +// pool exactly matches the stack lifetime of the object. + +#ifndef WEBRTC_BASE_SCOPED_AUTORELEASE_POOL_H__ +#define WEBRTC_BASE_SCOPED_AUTORELEASE_POOL_H__ + +#if defined(WEBRTC_MAC) + +#include "webrtc/base/common.h" + +// This header may be included from Obj-C files or C++ files. +#ifdef __OBJC__ +@class NSAutoreleasePool; +#else +class NSAutoreleasePool; +#endif + +namespace rtc { + +class ScopedAutoreleasePool { + public: + ScopedAutoreleasePool(); + ~ScopedAutoreleasePool(); + + private: + // Declaring private overrides of new and delete here enforces the "only use + // as a stack object" discipline. + // + // Note: new is declared as "throw()" to get around a gcc warning about new + // returning NULL, but this method will never get called and therefore will + // never actually throw any exception. + void* operator new(size_t size) throw() { return NULL; } + void operator delete (void* ptr) {} + + NSAutoreleasePool* pool_; + + DISALLOW_EVIL_CONSTRUCTORS(ScopedAutoreleasePool); +}; + +} // namespace rtc + +#endif // WEBRTC_MAC +#endif // WEBRTC_BASE_SCOPED_AUTORELEASE_POOL_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/scoped_autorelease_pool.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/scoped_autorelease_pool.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/scoped_autorelease_pool.mm 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/scoped_autorelease_pool.mm 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import + +#import "webrtc/base/scoped_autorelease_pool.h" + +namespace rtc { + +ScopedAutoreleasePool::ScopedAutoreleasePool() { + pool_ = [[NSAutoreleasePool alloc] init]; +} + +ScopedAutoreleasePool::~ScopedAutoreleasePool() { + [pool_ drain]; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/scopedptrcollection.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/scopedptrcollection.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/scopedptrcollection.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/scopedptrcollection.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,60 @@ +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Stores a collection of pointers that are deleted when the container is +// destructed. + +#ifndef WEBRTC_BASE_SCOPEDPTRCOLLECTION_H_ +#define WEBRTC_BASE_SCOPEDPTRCOLLECTION_H_ + +#include +#include + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/constructormagic.h" + +namespace rtc { + +template +class ScopedPtrCollection { + public: + typedef std::vector VectorT; + + ScopedPtrCollection() { } + ~ScopedPtrCollection() { + for (typename VectorT::iterator it = collection_.begin(); + it != collection_.end(); ++it) { + delete *it; + } + } + + const VectorT& collection() const { return collection_; } + void Reserve(size_t size) { + collection_.reserve(size); + } + void PushBack(T* t) { + collection_.push_back(t); + } + + // Remove |t| from the collection without deleting it. + void Remove(T* t) { + collection_.erase(std::remove(collection_.begin(), collection_.end(), t), + collection_.end()); + } + + private: + VectorT collection_; + + DISALLOW_COPY_AND_ASSIGN(ScopedPtrCollection); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_SCOPEDPTRCOLLECTION_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/scopedptrcollection_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/scopedptrcollection_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/scopedptrcollection_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/scopedptrcollection_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,73 @@ +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/scopedptrcollection.h" +#include "webrtc/base/gunit.h" + +namespace rtc { + +namespace { + +class InstanceCounter { + public: + explicit InstanceCounter(int* num_instances) + : num_instances_(num_instances) { + ++(*num_instances_); + } + ~InstanceCounter() { + --(*num_instances_); + } + + private: + int* num_instances_; + + DISALLOW_COPY_AND_ASSIGN(InstanceCounter); +}; + +} // namespace + +class ScopedPtrCollectionTest : public testing::Test { + protected: + ScopedPtrCollectionTest() + : num_instances_(0), + collection_(new ScopedPtrCollection()) { + } + + int num_instances_; + scoped_ptr > collection_; +}; + +TEST_F(ScopedPtrCollectionTest, PushBack) { + EXPECT_EQ(0u, collection_->collection().size()); + EXPECT_EQ(0, num_instances_); + const int kNum = 100; + for (int i = 0; i < kNum; ++i) { + collection_->PushBack(new InstanceCounter(&num_instances_)); + } + EXPECT_EQ(static_cast(kNum), collection_->collection().size()); + EXPECT_EQ(kNum, num_instances_); + collection_.reset(); + EXPECT_EQ(0, num_instances_); +} + +TEST_F(ScopedPtrCollectionTest, Remove) { + InstanceCounter* ic = new InstanceCounter(&num_instances_); + collection_->PushBack(ic); + EXPECT_EQ(1u, collection_->collection().size()); + collection_->Remove(ic); + EXPECT_EQ(1, num_instances_); + collection_.reset(); + EXPECT_EQ(1, num_instances_); + delete ic; + EXPECT_EQ(0, num_instances_); +} + + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/scoped_ptr.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/scoped_ptr.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/scoped_ptr.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/scoped_ptr.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,595 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Scopers help you manage ownership of a pointer, helping you easily manage the +// a pointer within a scope, and automatically destroying the pointer at the +// end of a scope. There are two main classes you will use, which correspond +// to the operators new/delete and new[]/delete[]. +// +// Example usage (scoped_ptr): +// { +// scoped_ptr foo(new Foo("wee")); +// } // foo goes out of scope, releasing the pointer with it. +// +// { +// scoped_ptr foo; // No pointer managed. +// foo.reset(new Foo("wee")); // Now a pointer is managed. +// foo.reset(new Foo("wee2")); // Foo("wee") was destroyed. +// foo.reset(new Foo("wee3")); // Foo("wee2") was destroyed. +// foo->Method(); // Foo::Method() called. +// foo.get()->Method(); // Foo::Method() called. +// SomeFunc(foo.release()); // SomeFunc takes ownership, foo no longer +// // manages a pointer. +// foo.reset(new Foo("wee4")); // foo manages a pointer again. +// foo.reset(); // Foo("wee4") destroyed, foo no longer +// // manages a pointer. +// } // foo wasn't managing a pointer, so nothing was destroyed. +// +// Example usage (scoped_ptr): +// { +// scoped_ptr foo(new Foo[100]); +// foo.get()->Method(); // Foo::Method on the 0th element. +// foo[10].Method(); // Foo::Method on the 10th element. +// } +// +// These scopers also implement part of the functionality of C++11 unique_ptr +// in that they are "movable but not copyable." You can use the scopers in +// the parameter and return types of functions to signify ownership transfer +// in to and out of a function. When calling a function that has a scoper +// as the argument type, it must be called with the result of an analogous +// scoper's Pass() function or another function that generates a temporary; +// passing by copy will NOT work. Here is an example using scoped_ptr: +// +// void TakesOwnership(scoped_ptr arg) { +// // Do something with arg +// } +// scoped_ptr CreateFoo() { +// // No need for calling Pass() because we are constructing a temporary +// // for the return value. +// return scoped_ptr(new Foo("new")); +// } +// scoped_ptr PassThru(scoped_ptr arg) { +// return arg.Pass(); +// } +// +// { +// scoped_ptr ptr(new Foo("yay")); // ptr manages Foo("yay"). +// TakesOwnership(ptr.Pass()); // ptr no longer owns Foo("yay"). +// scoped_ptr ptr2 = CreateFoo(); // ptr2 owns the return Foo. +// scoped_ptr ptr3 = // ptr3 now owns what was in ptr2. +// PassThru(ptr2.Pass()); // ptr2 is correspondingly NULL. +// } +// +// Notice that if you do not call Pass() when returning from PassThru(), or +// when invoking TakesOwnership(), the code will not compile because scopers +// are not copyable; they only implement move semantics which require calling +// the Pass() function to signify a destructive transfer of state. CreateFoo() +// is different though because we are constructing a temporary on the return +// line and thus can avoid needing to call Pass(). +// +// Pass() properly handles upcast in initialization, i.e. you can use a +// scoped_ptr to initialize a scoped_ptr: +// +// scoped_ptr foo(new Foo()); +// scoped_ptr parent(foo.Pass()); +// +// PassAs<>() should be used to upcast return value in return statement: +// +// scoped_ptr CreateFoo() { +// scoped_ptr result(new FooChild()); +// return result.PassAs(); +// } +// +// Note that PassAs<>() is implemented only for scoped_ptr, but not for +// scoped_ptr. This is because casting array pointers may not be safe. + +#ifndef WEBRTC_BASE_SCOPED_PTR_H__ +#define WEBRTC_BASE_SCOPED_PTR_H__ + +#include // for ptrdiff_t +#include // for free() decl + +#include // For std::swap(). + +#include "webrtc/base/common.h" // for ASSERT +#include "webrtc/base/compile_assert.h" // for COMPILE_ASSERT +#include "webrtc/base/move.h" // for TALK_MOVE_ONLY_TYPE_FOR_CPP_03 +#include "webrtc/base/template_util.h" // for is_convertible, is_array + +#ifdef WEBRTC_WIN +namespace std { using ::ptrdiff_t; }; +#endif // WEBRTC_WIN + +namespace rtc { + +// Function object which deletes its parameter, which must be a pointer. +// If C is an array type, invokes 'delete[]' on the parameter; otherwise, +// invokes 'delete'. The default deleter for scoped_ptr. +template +struct DefaultDeleter { + DefaultDeleter() {} + template DefaultDeleter(const DefaultDeleter& other) { + // IMPLEMENTATION NOTE: C++11 20.7.1.1.2p2 only provides this constructor + // if U* is implicitly convertible to T* and U is not an array type. + // + // Correct implementation should use SFINAE to disable this + // constructor. However, since there are no other 1-argument constructors, + // using a COMPILE_ASSERT() based on is_convertible<> and requiring + // complete types is simpler and will cause compile failures for equivalent + // misuses. + // + // Note, the is_convertible check also ensures that U is not an + // array. T is guaranteed to be a non-array, so any U* where U is an array + // cannot convert to T*. + enum { T_must_be_complete = sizeof(T) }; + enum { U_must_be_complete = sizeof(U) }; + COMPILE_ASSERT((rtc::is_convertible::value), + U_ptr_must_implicitly_convert_to_T_ptr); + } + inline void operator()(T* ptr) const { + enum { type_must_be_complete = sizeof(T) }; + delete ptr; + } +}; + +// Specialization of DefaultDeleter for array types. +template +struct DefaultDeleter { + inline void operator()(T* ptr) const { + enum { type_must_be_complete = sizeof(T) }; + delete[] ptr; + } + + private: + // Disable this operator for any U != T because it is undefined to execute + // an array delete when the static type of the array mismatches the dynamic + // type. + // + // References: + // C++98 [expr.delete]p3 + // http://cplusplus.github.com/LWG/lwg-defects.html#938 + template void operator()(U* array) const; +}; + +template +struct DefaultDeleter { + // Never allow someone to declare something like scoped_ptr. + COMPILE_ASSERT(sizeof(T) == -1, do_not_use_array_with_size_as_type); +}; + +// Function object which invokes 'free' on its parameter, which must be +// a pointer. Can be used to store malloc-allocated pointers in scoped_ptr: +// +// scoped_ptr foo_ptr( +// static_cast(malloc(sizeof(int)))); +struct FreeDeleter { + inline void operator()(void* ptr) const { + free(ptr); + } +}; + +namespace internal { + +// Minimal implementation of the core logic of scoped_ptr, suitable for +// reuse in both scoped_ptr and its specializations. +template +class scoped_ptr_impl { + public: + explicit scoped_ptr_impl(T* p) : data_(p) { } + + // Initializer for deleters that have data parameters. + scoped_ptr_impl(T* p, const D& d) : data_(p, d) {} + + // Templated constructor that destructively takes the value from another + // scoped_ptr_impl. + template + scoped_ptr_impl(scoped_ptr_impl* other) + : data_(other->release(), other->get_deleter()) { + // We do not support move-only deleters. We could modify our move + // emulation to have rtc::subtle::move() and + // rtc::subtle::forward() + // functions that are imperfect emulations of their C++11 equivalents, + // but until there's a requirement, just assume deleters are copyable. + } + + template + void TakeState(scoped_ptr_impl* other) { + // See comment in templated constructor above regarding lack of support + // for move-only deleters. + reset(other->release()); + get_deleter() = other->get_deleter(); + } + + ~scoped_ptr_impl() { + if (data_.ptr != NULL) { + // Not using get_deleter() saves one function call in non-optimized + // builds. + static_cast(data_)(data_.ptr); + } + } + + void reset(T* p) { + // This is a self-reset, which is no longer allowed: http://crbug.com/162971 + if (p != NULL && p == data_.ptr) + abort(); + + // Note that running data_.ptr = p can lead to undefined behavior if + // get_deleter()(get()) deletes this. In order to pevent this, reset() + // should update the stored pointer before deleting its old value. + // + // However, changing reset() to use that behavior may cause current code to + // break in unexpected ways. If the destruction of the owned object + // dereferences the scoped_ptr when it is destroyed by a call to reset(), + // then it will incorrectly dispatch calls to |p| rather than the original + // value of |data_.ptr|. + // + // During the transition period, set the stored pointer to NULL while + // deleting the object. Eventually, this safety check will be removed to + // prevent the scenario initially described from occuring and + // http://crbug.com/176091 can be closed. + T* old = data_.ptr; + data_.ptr = NULL; + if (old != NULL) + static_cast(data_)(old); + data_.ptr = p; + } + + T* get() const { return data_.ptr; } + + D& get_deleter() { return data_; } + const D& get_deleter() const { return data_; } + + void swap(scoped_ptr_impl& p2) { + // Standard swap idiom: 'using std::swap' ensures that std::swap is + // present in the overload set, but we call swap unqualified so that + // any more-specific overloads can be used, if available. + using std::swap; + swap(static_cast(data_), static_cast(p2.data_)); + swap(data_.ptr, p2.data_.ptr); + } + + T* release() { + T* old_ptr = data_.ptr; + data_.ptr = NULL; + return old_ptr; + } + + T** accept() { + reset(NULL); + return &(data_.ptr); + } + + T** use() { + return &(data_.ptr); + } + + private: + // Needed to allow type-converting constructor. + template friend class scoped_ptr_impl; + + // Use the empty base class optimization to allow us to have a D + // member, while avoiding any space overhead for it when D is an + // empty class. See e.g. http://www.cantrip.org/emptyopt.html for a good + // discussion of this technique. + struct Data : public D { + explicit Data(T* ptr_in) : ptr(ptr_in) {} + Data(T* ptr_in, const D& other) : D(other), ptr(ptr_in) {} + T* ptr; + }; + + Data data_; + + DISALLOW_COPY_AND_ASSIGN(scoped_ptr_impl); +}; + +} // namespace internal + +// A scoped_ptr is like a T*, except that the destructor of scoped_ptr +// automatically deletes the pointer it holds (if any). +// That is, scoped_ptr owns the T object that it points to. +// Like a T*, a scoped_ptr may hold either NULL or a pointer to a T object. +// Also like T*, scoped_ptr is thread-compatible, and once you +// dereference it, you get the thread safety guarantees of T. +// +// The size of scoped_ptr is small. On most compilers, when using the +// DefaultDeleter, sizeof(scoped_ptr) == sizeof(T*). Custom deleters will +// increase the size proportional to whatever state they need to have. See +// comments inside scoped_ptr_impl<> for details. +// +// Current implementation targets having a strict subset of C++11's +// unique_ptr<> features. Known deficiencies include not supporting move-only +// deleteres, function pointers as deleters, and deleters with reference +// types. +template > +class scoped_ptr { + TALK_MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue) + + public: + // The element and deleter types. + typedef T element_type; + typedef D deleter_type; + + // Constructor. Defaults to initializing with NULL. + scoped_ptr() : impl_(NULL) { } + + // Constructor. Takes ownership of p. + explicit scoped_ptr(element_type* p) : impl_(p) { } + + // Constructor. Allows initialization of a stateful deleter. + scoped_ptr(element_type* p, const D& d) : impl_(p, d) { } + + // Constructor. Allows construction from a scoped_ptr rvalue for a + // convertible type and deleter. + // + // IMPLEMENTATION NOTE: C++11 unique_ptr<> keeps this constructor distinct + // from the normal move constructor. By C++11 20.7.1.2.1.21, this constructor + // has different post-conditions if D is a reference type. Since this + // implementation does not support deleters with reference type, + // we do not need a separate move constructor allowing us to avoid one + // use of SFINAE. You only need to care about this if you modify the + // implementation of scoped_ptr. + template + scoped_ptr(scoped_ptr other) : impl_(&other.impl_) { + COMPILE_ASSERT(!rtc::is_array::value, U_cannot_be_an_array); + } + + // Constructor. Move constructor for C++03 move emulation of this type. + scoped_ptr(RValue rvalue) : impl_(&rvalue.object->impl_) { } + + // operator=. Allows assignment from a scoped_ptr rvalue for a convertible + // type and deleter. + // + // IMPLEMENTATION NOTE: C++11 unique_ptr<> keeps this operator= distinct from + // the normal move assignment operator. By C++11 20.7.1.2.3.4, this templated + // form has different requirements on for move-only Deleters. Since this + // implementation does not support move-only Deleters, we do not need a + // separate move assignment operator allowing us to avoid one use of SFINAE. + // You only need to care about this if you modify the implementation of + // scoped_ptr. + template + scoped_ptr& operator=(scoped_ptr rhs) { + COMPILE_ASSERT(!rtc::is_array::value, U_cannot_be_an_array); + impl_.TakeState(&rhs.impl_); + return *this; + } + + // Reset. Deletes the currently owned object, if any. + // Then takes ownership of a new object, if given. + void reset(element_type* p = NULL) { impl_.reset(p); } + + // Accessors to get the owned object. + // operator* and operator-> will assert() if there is no current object. + element_type& operator*() const { + ASSERT(impl_.get() != NULL); + return *impl_.get(); + } + element_type* operator->() const { + ASSERT(impl_.get() != NULL); + return impl_.get(); + } + element_type* get() const { return impl_.get(); } + + // Access to the deleter. + deleter_type& get_deleter() { return impl_.get_deleter(); } + const deleter_type& get_deleter() const { return impl_.get_deleter(); } + + // Allow scoped_ptr to be used in boolean expressions, but not + // implicitly convertible to a real bool (which is dangerous). + // + // Note that this trick is only safe when the == and != operators + // are declared explicitly, as otherwise "scoped_ptr1 == + // scoped_ptr2" will compile but do the wrong thing (i.e., convert + // to Testable and then do the comparison). + private: + typedef rtc::internal::scoped_ptr_impl + scoped_ptr::*Testable; + + public: + operator Testable() const { return impl_.get() ? &scoped_ptr::impl_ : NULL; } + + // Comparison operators. + // These return whether two scoped_ptr refer to the same object, not just to + // two different but equal objects. + bool operator==(const element_type* p) const { return impl_.get() == p; } + bool operator!=(const element_type* p) const { return impl_.get() != p; } + + // Swap two scoped pointers. + void swap(scoped_ptr& p2) { + impl_.swap(p2.impl_); + } + + // Release a pointer. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + element_type* release() WARN_UNUSED_RESULT { + return impl_.release(); + } + + // Delete the currently held pointer and return a pointer + // to allow overwriting of the current pointer address. + element_type** accept() WARN_UNUSED_RESULT { + return impl_.accept(); + } + + // Return a pointer to the current pointer address. + element_type** use() WARN_UNUSED_RESULT { + return impl_.use(); + } + + // C++98 doesn't support functions templates with default parameters which + // makes it hard to write a PassAs() that understands converting the deleter + // while preserving simple calling semantics. + // + // Until there is a use case for PassAs() with custom deleters, just ignore + // the custom deleter. + template + scoped_ptr PassAs() { + return scoped_ptr(Pass()); + } + + private: + // Needed to reach into |impl_| in the constructor. + template friend class scoped_ptr; + rtc::internal::scoped_ptr_impl impl_; + + // Forbidden for API compatibility with std::unique_ptr. + explicit scoped_ptr(int disallow_construction_from_null); + + // Forbid comparison of scoped_ptr types. If U != T, it totally + // doesn't make sense, and if U == T, it still doesn't make sense + // because you should never have the same object owned by two different + // scoped_ptrs. + template bool operator==(scoped_ptr const& p2) const; + template bool operator!=(scoped_ptr const& p2) const; +}; + +template +class scoped_ptr { + TALK_MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue) + + public: + // The element and deleter types. + typedef T element_type; + typedef D deleter_type; + + // Constructor. Defaults to initializing with NULL. + scoped_ptr() : impl_(NULL) { } + + // Constructor. Stores the given array. Note that the argument's type + // must exactly match T*. In particular: + // - it cannot be a pointer to a type derived from T, because it is + // inherently unsafe in the general case to access an array through a + // pointer whose dynamic type does not match its static type (eg., if + // T and the derived types had different sizes access would be + // incorrectly calculated). Deletion is also always undefined + // (C++98 [expr.delete]p3). If you're doing this, fix your code. + // - it cannot be NULL, because NULL is an integral expression, not a + // pointer to T. Use the no-argument version instead of explicitly + // passing NULL. + // - it cannot be const-qualified differently from T per unique_ptr spec + // (http://cplusplus.github.com/LWG/lwg-active.html#2118). Users wanting + // to work around this may use implicit_cast(). + // However, because of the first bullet in this comment, users MUST + // NOT use implicit_cast() to upcast the static type of the array. + explicit scoped_ptr(element_type* array) : impl_(array) { } + + // Constructor. Move constructor for C++03 move emulation of this type. + scoped_ptr(RValue rvalue) : impl_(&rvalue.object->impl_) { } + + // operator=. Move operator= for C++03 move emulation of this type. + scoped_ptr& operator=(RValue rhs) { + impl_.TakeState(&rhs.object->impl_); + return *this; + } + + // Reset. Deletes the currently owned array, if any. + // Then takes ownership of a new object, if given. + void reset(element_type* array = NULL) { impl_.reset(array); } + + // Accessors to get the owned array. + element_type& operator[](size_t i) const { + ASSERT(impl_.get() != NULL); + return impl_.get()[i]; + } + element_type* get() const { return impl_.get(); } + + // Access to the deleter. + deleter_type& get_deleter() { return impl_.get_deleter(); } + const deleter_type& get_deleter() const { return impl_.get_deleter(); } + + // Allow scoped_ptr to be used in boolean expressions, but not + // implicitly convertible to a real bool (which is dangerous). + private: + typedef rtc::internal::scoped_ptr_impl + scoped_ptr::*Testable; + + public: + operator Testable() const { return impl_.get() ? &scoped_ptr::impl_ : NULL; } + + // Comparison operators. + // These return whether two scoped_ptr refer to the same object, not just to + // two different but equal objects. + bool operator==(element_type* array) const { return impl_.get() == array; } + bool operator!=(element_type* array) const { return impl_.get() != array; } + + // Swap two scoped pointers. + void swap(scoped_ptr& p2) { + impl_.swap(p2.impl_); + } + + // Release a pointer. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + element_type* release() WARN_UNUSED_RESULT { + return impl_.release(); + } + + // Delete the currently held pointer and return a pointer + // to allow overwriting of the current pointer address. + element_type** accept() WARN_UNUSED_RESULT { + return impl_.accept(); + } + + // Return a pointer to the current pointer address. + element_type** use() WARN_UNUSED_RESULT { + return impl_.use(); + } + + private: + // Force element_type to be a complete type. + enum { type_must_be_complete = sizeof(element_type) }; + + // Actually hold the data. + rtc::internal::scoped_ptr_impl impl_; + + // Disable initialization from any type other than element_type*, by + // providing a constructor that matches such an initialization, but is + // private and has no definition. This is disabled because it is not safe to + // call delete[] on an array whose static type does not match its dynamic + // type. + template explicit scoped_ptr(U* array); + explicit scoped_ptr(int disallow_construction_from_null); + + // Disable reset() from any type other than element_type*, for the same + // reasons as the constructor above. + template void reset(U* array); + void reset(int disallow_reset_from_null); + + // Forbid comparison of scoped_ptr types. If U != T, it totally + // doesn't make sense, and if U == T, it still doesn't make sense + // because you should never have the same object owned by two different + // scoped_ptrs. + template bool operator==(scoped_ptr const& p2) const; + template bool operator!=(scoped_ptr const& p2) const; +}; + +} // namespace rtc + +// Free functions +template +void swap(rtc::scoped_ptr& p1, rtc::scoped_ptr& p2) { + p1.swap(p2); +} + +template +bool operator==(T* p1, const rtc::scoped_ptr& p2) { + return p1 == p2.get(); +} + +template +bool operator!=(T* p1, const rtc::scoped_ptr& p2) { + return p1 != p2.get(); +} + +#endif // #ifndef WEBRTC_BASE_SCOPED_PTR_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/scoped_ref_ptr.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/scoped_ref_ptr.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/scoped_ref_ptr.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/scoped_ref_ptr.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,147 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Originally these classes are from Chromium. +// http://src.chromium.org/viewvc/chrome/trunk/src/base/memory/ref_counted.h?view=markup + +// +// A smart pointer class for reference counted objects. Use this class instead +// of calling AddRef and Release manually on a reference counted object to +// avoid common memory leaks caused by forgetting to Release an object +// reference. Sample usage: +// +// class MyFoo : public RefCounted { +// ... +// }; +// +// void some_function() { +// scoped_refptr foo = new MyFoo(); +// foo->Method(param); +// // |foo| is released when this function returns +// } +// +// void some_other_function() { +// scoped_refptr foo = new MyFoo(); +// ... +// foo = NULL; // explicitly releases |foo| +// ... +// if (foo) +// foo->Method(param); +// } +// +// The above examples show how scoped_refptr acts like a pointer to T. +// Given two scoped_refptr classes, it is also possible to exchange +// references between the two objects, like so: +// +// { +// scoped_refptr a = new MyFoo(); +// scoped_refptr b; +// +// b.swap(a); +// // now, |b| references the MyFoo object, and |a| references NULL. +// } +// +// To make both |a| and |b| in the above example reference the same MyFoo +// object, simply use the assignment operator: +// +// { +// scoped_refptr a = new MyFoo(); +// scoped_refptr b; +// +// b = a; +// // now, |a| and |b| each own a reference to the same MyFoo object. +// } +// + +#ifndef WEBRTC_BASE_SCOPED_REF_PTR_H_ +#define WEBRTC_BASE_SCOPED_REF_PTR_H_ + +#include + +namespace rtc { + +template +class scoped_refptr { + public: + scoped_refptr() : ptr_(NULL) { + } + + scoped_refptr(T* p) : ptr_(p) { + if (ptr_) + ptr_->AddRef(); + } + + scoped_refptr(const scoped_refptr& r) : ptr_(r.ptr_) { + if (ptr_) + ptr_->AddRef(); + } + + template + scoped_refptr(const scoped_refptr& r) : ptr_(r.get()) { + if (ptr_) + ptr_->AddRef(); + } + + ~scoped_refptr() { + if (ptr_) + ptr_->Release(); + } + + T* get() const { return ptr_; } + operator T*() const { return ptr_; } + T* operator->() const { return ptr_; } + + // Release a pointer. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + T* release() { + T* retVal = ptr_; + ptr_ = NULL; + return retVal; + } + + scoped_refptr& operator=(T* p) { + // AddRef first so that self assignment should work + if (p) + p->AddRef(); + if (ptr_ ) + ptr_ ->Release(); + ptr_ = p; + return *this; + } + + scoped_refptr& operator=(const scoped_refptr& r) { + return *this = r.ptr_; + } + + template + scoped_refptr& operator=(const scoped_refptr& r) { + return *this = r.get(); + } + + void swap(T** pp) { + T* p = ptr_; + ptr_ = *pp; + *pp = p; + } + + void swap(scoped_refptr& r) { + swap(&r.ptr_); + } + + protected: + T* ptr_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_SCOPED_REF_PTR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sec_buffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sec_buffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sec_buffer.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sec_buffer.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,156 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// @file Contains utility classes that make it easier to use SecBuffers + +#ifndef WEBRTC_BASE_SEC_BUFFER_H__ +#define WEBRTC_BASE_SEC_BUFFER_H__ + +namespace rtc { + +// A base class for CSecBuffer. Contains +// all implementation that does not require +// template arguments. +class CSecBufferBase : public SecBuffer { + public: + CSecBufferBase() { + Clear(); + } + + // Uses the SSPI to free a pointer, must be + // used for buffers returned from SSPI APIs. + static void FreeSSPI(void *ptr) { + if ( ptr ) { + SECURITY_STATUS status; + status = ::FreeContextBuffer(ptr); + ASSERT(SEC_E_OK == status); // "Freeing context buffer" + } + } + + // Deletes a buffer with operator delete + static void FreeDelete(void *ptr) { + delete [] reinterpret_cast(ptr); + } + + // A noop delete, for buffers over other + // people's memory + static void FreeNone(void *ptr) { + } + + protected: + // Clears the buffer to EMPTY & NULL + void Clear() { + this->BufferType = SECBUFFER_EMPTY; + this->cbBuffer = 0; + this->pvBuffer = NULL; + } +}; + +// Wrapper class for SecBuffer to take care +// of initialization and destruction. +template +class CSecBuffer: public CSecBufferBase { + public: + // Initializes buffer to empty & NULL + CSecBuffer() { + } + + // Frees any allocated memory + ~CSecBuffer() { + Release(); + } + + // Frees the buffer appropriately, and re-nulls + void Release() { + pfnFreeBuffer(this->pvBuffer); + Clear(); + } + + private: + // A placeholder function for compile-time asserts on the class + void CompileAsserts() { + // never invoked... + assert(false); // _T("Notreached") + + // This class must not extend the size of SecBuffer, since + // we use arrays of CSecBuffer in CSecBufferBundle below + cassert(sizeof(CSecBuffer == sizeof(SecBuffer))); + } +}; + +// Contains all generic implementation for the +// SecBufferBundle class +class SecBufferBundleBase { + public: +}; + +// A template class that bundles a SecBufferDesc with +// one or more SecBuffers for convenience. Can take +// care of deallocating buffers appropriately, as indicated +// by pfnFreeBuffer function. +// By default does no deallocation. +template +class CSecBufferBundle : public SecBufferBundleBase { + public: + // Constructs a security buffer bundle with num_buffers + // buffers, all of which are empty and nulled. + CSecBufferBundle() { + desc_.ulVersion = SECBUFFER_VERSION; + desc_.cBuffers = num_buffers; + desc_.pBuffers = buffers_; + } + + // Frees all currently used buffers. + ~CSecBufferBundle() { + Release(); + } + + // Accessor for the descriptor + PSecBufferDesc desc() { + return &desc_; + } + + // Accessor for the descriptor + const PSecBufferDesc desc() const { + return &desc_; + } + + // returns the i-th security buffer + SecBuffer &operator[] (size_t num) { + ASSERT(num < num_buffers); // "Buffer index out of bounds" + return buffers_[num]; + } + + // returns the i-th security buffer + const SecBuffer &operator[] (size_t num) const { + ASSERT(num < num_buffers); // "Buffer index out of bounds" + return buffers_[num]; + } + + // Frees all non-NULL security buffers, + // using the deallocation function + void Release() { + for ( size_t i = 0; i < num_buffers; ++i ) { + buffers_[i].Release(); + } + } + + private: + // Our descriptor + SecBufferDesc desc_; + // Our bundled buffers, each takes care of its own + // initialization and destruction + CSecBuffer buffers_[num_buffers]; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_SEC_BUFFER_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sha1.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sha1.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sha1.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sha1.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,286 @@ +/* + * SHA-1 in C + * By Steve Reid + * 100% Public Domain + * + * ----------------- + * Modified 7/98 + * By James H. Brown + * Still 100% Public Domain + * + * Corrected a problem which generated improper hash values on 16 bit machines + * Routine SHA1Update changed from + * void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int + * len) + * to + * void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned + * long len) + * + * The 'len' parameter was declared an int which works fine on 32 bit machines. + * However, on 16 bit machines an int is too small for the shifts being done + * against + * it. This caused the hash function to generate incorrect values if len was + * greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). + * + * Since the file IO in main() reads 16K at a time, any file 8K or larger would + * be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million + * "a"s). + * + * I also changed the declaration of variables i & j in SHA1Update to + * unsigned long from unsigned int for the same reason. + * + * These changes should make no difference to any 32 bit implementations since + * an + * int and a long are the same size in those environments. + * + * -- + * I also corrected a few compiler warnings generated by Borland C. + * 1. Added #include for exit() prototype + * 2. Removed unused variable 'j' in SHA1Final + * 3. Changed exit(0) to return(0) at end of main. + * + * ALL changes I made can be located by searching for comments containing 'JHB' + * ----------------- + * Modified 8/98 + * By Steve Reid + * Still 100% public domain + * + * 1- Removed #include and used return() instead of exit() + * 2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) + * 3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net + * + * ----------------- + * Modified 4/01 + * By Saul Kravitz + * Still 100% PD + * Modified to run on Compaq Alpha hardware. + * + * ----------------- + * Modified 07/2002 + * By Ralph Giles + * Still 100% public domain + * modified for use with stdint types, autoconf + * code cleanup, removed attribution comments + * switched SHA1Final() argument order for consistency + * use SHA1_ prefix for public api + * move public api to sha1.h + * + * ----------------- + * Modified 02/2012 + * By Justin Uberti + * Remove underscore from SHA1 prefix to avoid conflict with OpenSSL + * Remove test code + * Untabify + * + * ----------------- + * Modified 03/2012 + * By Ronghua Wu + * Change the typedef of uint32(8)_t to uint32(8). We need this because in the + * chromium android build, the stdio.h will include stdint.h which already + * defined uint32(8)_t. + * + * ----------------- + * Modified 04/2012 + * By Frank Barchard + * Ported to C++, Google style, change len to size_t, enable SHA1HANDSOFF + * + * Test Vectors (from FIPS PUB 180-1) + * "abc" + * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D + * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 + * A million repetitions of "a" + * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F + */ + +// Enabling SHA1HANDSOFF preserves the caller's data buffer. +// Disabling SHA1HANDSOFF the buffer will be modified (end swapped). +#define SHA1HANDSOFF + +#include "webrtc/base/sha1.h" + +#include +#include + +namespace rtc { + +void SHA1Transform(uint32 state[5], const uint8 buffer[64]); + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +// blk0() and blk() perform the initial expand. +// I got the idea of expanding during the round function from SSLeay +// FIXME: can we do this in an endian-proof way? +#ifdef ARCH_CPU_BIG_ENDIAN +#define blk0(i) block->l[i] +#else +#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \ + (rol(block->l[i], 8) & 0x00FF00FF)) +#endif +#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \ + block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1)) + +// (R0+R1), R2, R3, R4 are the different operations used in SHA1. +#define R0(v, w, x, y, z, i) \ + z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R1(v, w, x, y, z, i) \ + z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R2(v, w, x, y, z, i) \ + z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5);\ + w = rol(w, 30); +#define R3(v, w, x, y, z, i) \ + z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ + w = rol(w, 30); +#define R4(v, w, x, y, z, i) \ + z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ + w = rol(w, 30); + +#ifdef VERBOSE // SAK +void SHAPrintContext(SHA1_CTX *context, char *msg) { + printf("%s (%d,%d) %x %x %x %x %x\n", + msg, + context->count[0], context->count[1], + context->state[0], + context->state[1], + context->state[2], + context->state[3], + context->state[4]); +} +#endif /* VERBOSE */ + +// Hash a single 512-bit block. This is the core of the algorithm. +void SHA1Transform(uint32 state[5], const uint8 buffer[64]) { + union CHAR64LONG16 { + uint8 c[64]; + uint32 l[16]; + }; +#ifdef SHA1HANDSOFF + static uint8 workspace[64]; + memcpy(workspace, buffer, 64); + CHAR64LONG16* block = reinterpret_cast(workspace); +#else + // Note(fbarchard): This option does modify the user's data buffer. + CHAR64LONG16* block = const_cast( + reinterpret_cast(buffer)); +#endif + + // Copy context->state[] to working vars. + uint32 a = state[0]; + uint32 b = state[1]; + uint32 c = state[2]; + uint32 d = state[3]; + uint32 e = state[4]; + + // 4 rounds of 20 operations each. Loop unrolled. + // Note(fbarchard): The following has lint warnings for multiple ; on + // a line and no space after , but is left as-is to be similar to the + // original code. + R0(a,b,c,d,e,0); R0(e,a,b,c,d,1); R0(d,e,a,b,c,2); R0(c,d,e,a,b,3); + R0(b,c,d,e,a,4); R0(a,b,c,d,e,5); R0(e,a,b,c,d,6); R0(d,e,a,b,c,7); + R0(c,d,e,a,b,8); R0(b,c,d,e,a,9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + // Add the working vars back into context.state[]. + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; +} + +// SHA1Init - Initialize new context. +void SHA1Init(SHA1_CTX* context) { + // SHA1 initialization constants. + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + +// Run your data through this. +void SHA1Update(SHA1_CTX* context, const uint8* data, size_t input_len) { + size_t i = 0; + +#ifdef VERBOSE + SHAPrintContext(context, "before"); +#endif + + // Compute number of bytes mod 64. + size_t index = (context->count[0] >> 3) & 63; + + // Update number of bits. + // TODO: Use uint64 instead of 2 uint32 for count. + // count[0] has low 29 bits for byte count + 3 pad 0's making 32 bits for + // bit count. + // Add bit count to low uint32 + context->count[0] += static_cast(input_len << 3); + if (context->count[0] < static_cast(input_len << 3)) { + ++context->count[1]; // if overlow (carry), add one to high word + } + context->count[1] += static_cast(input_len >> 29); + if ((index + input_len) > 63) { + i = 64 - index; + memcpy(&context->buffer[index], data, i); + SHA1Transform(context->state, context->buffer); + for (; i + 63 < input_len; i += 64) { + SHA1Transform(context->state, data + i); + } + index = 0; + } + memcpy(&context->buffer[index], &data[i], input_len - i); + +#ifdef VERBOSE + SHAPrintContext(context, "after "); +#endif +} + +// Add padding and return the message digest. +void SHA1Final(SHA1_CTX* context, uint8 digest[SHA1_DIGEST_SIZE]) { + uint8 finalcount[8]; + for (int i = 0; i < 8; ++i) { + // Endian independent + finalcount[i] = static_cast( + (context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8) ) & 255); + } + SHA1Update(context, reinterpret_cast("\200"), 1); + while ((context->count[0] & 504) != 448) { + SHA1Update(context, reinterpret_cast("\0"), 1); + } + SHA1Update(context, finalcount, 8); // Should cause a SHA1Transform(). + for (int i = 0; i < SHA1_DIGEST_SIZE; ++i) { + digest[i] = static_cast( + (context->state[i >> 2] >> ((3 - (i & 3)) * 8) ) & 255); + } + + // Wipe variables. + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(finalcount, 0, 8); // SWR + +#ifdef SHA1HANDSOFF // Make SHA1Transform overwrite its own static vars. + SHA1Transform(context->state, context->buffer); +#endif +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sha1digest.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sha1digest.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sha1digest.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sha1digest.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,47 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SHA1DIGEST_H_ +#define WEBRTC_BASE_SHA1DIGEST_H_ + +#include "webrtc/base/messagedigest.h" +#include "webrtc/base/sha1.h" + +namespace rtc { + +// A simple wrapper for our SHA-1 implementation. +class Sha1Digest : public MessageDigest { + public: + enum { kSize = SHA1_DIGEST_SIZE }; + Sha1Digest() { + SHA1Init(&ctx_); + } + virtual size_t Size() const { + return kSize; + } + virtual void Update(const void* buf, size_t len) { + SHA1Update(&ctx_, static_cast(buf), len); + } + virtual size_t Finish(void* buf, size_t len) { + if (len < kSize) { + return 0; + } + SHA1Final(&ctx_, static_cast(buf)); + SHA1Init(&ctx_); // Reset for next use. + return kSize; + } + + private: + SHA1_CTX ctx_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_SHA1DIGEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sha1digest_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sha1digest_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sha1digest_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sha1digest_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,82 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/sha1digest.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/stringencode.h" + +namespace rtc { + +std::string Sha1(const std::string& input) { + Sha1Digest sha1; + return ComputeDigest(&sha1, input); +} + +TEST(Sha1DigestTest, TestSize) { + Sha1Digest sha1; + EXPECT_EQ(20, static_cast(Sha1Digest::kSize)); + EXPECT_EQ(20U, sha1.Size()); +} + +TEST(Sha1DigestTest, TestBasic) { + // Test vectors from sha1.c. + EXPECT_EQ("da39a3ee5e6b4b0d3255bfef95601890afd80709", Sha1("")); + EXPECT_EQ("a9993e364706816aba3e25717850c26c9cd0d89d", Sha1("abc")); + EXPECT_EQ("84983e441c3bd26ebaae4aa1f95129e5e54670f1", + Sha1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")); + std::string a_million_as(1000000, 'a'); + EXPECT_EQ("34aa973cd4c4daa4f61eeb2bdbad27316534016f", Sha1(a_million_as)); +} + +TEST(Sha1DigestTest, TestMultipleUpdates) { + Sha1Digest sha1; + std::string input = + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + char output[Sha1Digest::kSize]; + for (size_t i = 0; i < input.size(); ++i) { + sha1.Update(&input[i], 1); + } + EXPECT_EQ(sha1.Size(), sha1.Finish(output, sizeof(output))); + EXPECT_EQ("84983e441c3bd26ebaae4aa1f95129e5e54670f1", + hex_encode(output, sizeof(output))); +} + +TEST(Sha1DigestTest, TestReuse) { + Sha1Digest sha1; + std::string input = "abc"; + EXPECT_EQ("a9993e364706816aba3e25717850c26c9cd0d89d", + ComputeDigest(&sha1, input)); + input = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + EXPECT_EQ("84983e441c3bd26ebaae4aa1f95129e5e54670f1", + ComputeDigest(&sha1, input)); +} + +TEST(Sha1DigestTest, TestBufferTooSmall) { + Sha1Digest sha1; + std::string input = "abcdefghijklmnopqrstuvwxyz"; + char output[Sha1Digest::kSize - 1]; + sha1.Update(input.c_str(), input.size()); + EXPECT_EQ(0U, sha1.Finish(output, sizeof(output))); +} + +TEST(Sha1DigestTest, TestBufferConst) { + Sha1Digest sha1; + const int kLongSize = 1000000; + std::string input(kLongSize, '\0'); + for (int i = 0; i < kLongSize; ++i) { + input[i] = static_cast(i); + } + sha1.Update(input.c_str(), input.size()); + for (int i = 0; i < kLongSize; ++i) { + EXPECT_EQ(static_cast(i), input[i]); + } +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sha1.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sha1.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sha1.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sha1.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,32 @@ +/* + * SHA-1 in C + * By Steve Reid + * 100% Public Domain + * +*/ + +// Ported to C++, Google style, under namespace rtc and uses basictypes.h + +#ifndef WEBRTC_BASE_SHA1_H_ +#define WEBRTC_BASE_SHA1_H_ + +#include "webrtc/base/basictypes.h" + +namespace rtc { + +struct SHA1_CTX { + uint32 state[5]; + // TODO: Change bit count to uint64. + uint32 count[2]; // Bit count of input. + uint8 buffer[64]; +}; + +#define SHA1_DIGEST_SIZE 20 + +void SHA1Init(SHA1_CTX* context); +void SHA1Update(SHA1_CTX* context, const uint8* data, size_t len); +void SHA1Final(SHA1_CTX* context, uint8 digest[SHA1_DIGEST_SIZE]); + +#endif // WEBRTC_BASE_SHA1_H_ + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sharedexclusivelock.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sharedexclusivelock.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sharedexclusivelock.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sharedexclusivelock.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,44 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/sharedexclusivelock.h" + +namespace rtc { + +SharedExclusiveLock::SharedExclusiveLock() + : shared_count_is_zero_(true, true), + shared_count_(0) { +} + +void SharedExclusiveLock::LockExclusive() { + cs_exclusive_.Enter(); + shared_count_is_zero_.Wait(rtc::kForever); +} + +void SharedExclusiveLock::UnlockExclusive() { + cs_exclusive_.Leave(); +} + +void SharedExclusiveLock::LockShared() { + CritScope exclusive_scope(&cs_exclusive_); + CritScope shared_scope(&cs_shared_); + if (++shared_count_ == 1) { + shared_count_is_zero_.Reset(); + } +} + +void SharedExclusiveLock::UnlockShared() { + CritScope shared_scope(&cs_shared_); + if (--shared_count_ == 0) { + shared_count_is_zero_.Set(); + } +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sharedexclusivelock.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sharedexclusivelock.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sharedexclusivelock.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sharedexclusivelock.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,75 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SHAREDEXCLUSIVELOCK_H_ +#define WEBRTC_BASE_SHAREDEXCLUSIVELOCK_H_ + +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/criticalsection.h" +#include "webrtc/base/event.h" + +namespace rtc { + +// This class provides shared-exclusive lock. It can be used in cases like +// multiple-readers/single-writer model. +class LOCKABLE SharedExclusiveLock { + public: + SharedExclusiveLock(); + + // Locking/unlocking methods. It is encouraged to use SharedScope or + // ExclusiveScope for protection. + void LockExclusive() EXCLUSIVE_LOCK_FUNCTION(); + void UnlockExclusive() UNLOCK_FUNCTION(); + void LockShared(); + void UnlockShared(); + + private: + rtc::CriticalSection cs_exclusive_; + rtc::CriticalSection cs_shared_; + rtc::Event shared_count_is_zero_; + int shared_count_; + + DISALLOW_COPY_AND_ASSIGN(SharedExclusiveLock); +}; + +class SCOPED_LOCKABLE SharedScope { + public: + explicit SharedScope(SharedExclusiveLock* lock) SHARED_LOCK_FUNCTION(lock) + : lock_(lock) { + lock_->LockShared(); + } + + ~SharedScope() UNLOCK_FUNCTION() { lock_->UnlockShared(); } + + private: + SharedExclusiveLock* lock_; + + DISALLOW_COPY_AND_ASSIGN(SharedScope); +}; + +class SCOPED_LOCKABLE ExclusiveScope { + public: + explicit ExclusiveScope(SharedExclusiveLock* lock) + EXCLUSIVE_LOCK_FUNCTION(lock) + : lock_(lock) { + lock_->LockExclusive(); + } + + ~ExclusiveScope() UNLOCK_FUNCTION() { lock_->UnlockExclusive(); } + + private: + SharedExclusiveLock* lock_; + + DISALLOW_COPY_AND_ASSIGN(ExclusiveScope); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_SHAREDEXCLUSIVELOCK_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sharedexclusivelock_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sharedexclusivelock_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sharedexclusivelock_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sharedexclusivelock_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,219 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/common.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/messagehandler.h" +#include "webrtc/base/messagequeue.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/sharedexclusivelock.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/timeutils.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace rtc { + +static const uint32 kMsgRead = 0; +static const uint32 kMsgWrite = 0; +static const int kNoWaitThresholdInMs = 10; +static const int kWaitThresholdInMs = 80; +static const int kProcessTimeInMs = 100; +static const int kProcessTimeoutInMs = 5000; + +class SharedExclusiveTask : public MessageHandler { + public: + SharedExclusiveTask(SharedExclusiveLock* shared_exclusive_lock, + int* value, + bool* done) + : shared_exclusive_lock_(shared_exclusive_lock), + waiting_time_in_ms_(0), + value_(value), + done_(done) { + worker_thread_.reset(new Thread()); + worker_thread_->Start(); + } + + int waiting_time_in_ms() const { return waiting_time_in_ms_; } + + protected: + scoped_ptr worker_thread_; + SharedExclusiveLock* shared_exclusive_lock_; + int waiting_time_in_ms_; + int* value_; + bool* done_; +}; + +class ReadTask : public SharedExclusiveTask { + public: + ReadTask(SharedExclusiveLock* shared_exclusive_lock, int* value, bool* done) + : SharedExclusiveTask(shared_exclusive_lock, value, done) { + } + + void PostRead(int* value) { + worker_thread_->Post(this, kMsgRead, new TypedMessageData(value)); + } + + private: + virtual void OnMessage(Message* message) { + ASSERT(rtc::Thread::Current() == worker_thread_.get()); + ASSERT(message != NULL); + ASSERT(message->message_id == kMsgRead); + + TypedMessageData* message_data = + static_cast*>(message->pdata); + + uint32 start_time = Time(); + { + SharedScope ss(shared_exclusive_lock_); + waiting_time_in_ms_ = TimeDiff(Time(), start_time); + + Thread::SleepMs(kProcessTimeInMs); + *message_data->data() = *value_; + *done_ = true; + } + delete message->pdata; + message->pdata = NULL; + } +}; + +class WriteTask : public SharedExclusiveTask { + public: + WriteTask(SharedExclusiveLock* shared_exclusive_lock, int* value, bool* done) + : SharedExclusiveTask(shared_exclusive_lock, value, done) { + } + + void PostWrite(int value) { + worker_thread_->Post(this, kMsgWrite, new TypedMessageData(value)); + } + + private: + virtual void OnMessage(Message* message) { + ASSERT(rtc::Thread::Current() == worker_thread_.get()); + ASSERT(message != NULL); + ASSERT(message->message_id == kMsgWrite); + + TypedMessageData* message_data = + static_cast*>(message->pdata); + + uint32 start_time = Time(); + { + ExclusiveScope es(shared_exclusive_lock_); + waiting_time_in_ms_ = TimeDiff(Time(), start_time); + + Thread::SleepMs(kProcessTimeInMs); + *value_ = message_data->data(); + *done_ = true; + } + delete message->pdata; + message->pdata = NULL; + } +}; + +// Unit test for SharedExclusiveLock. +class SharedExclusiveLockTest + : public testing::Test { + public: + SharedExclusiveLockTest() : value_(0) { + } + + virtual void SetUp() { + shared_exclusive_lock_.reset(new SharedExclusiveLock()); + } + + protected: + scoped_ptr shared_exclusive_lock_; + int value_; +}; + +// Flaky: https://code.google.com/p/webrtc/issues/detail?id=3318 +TEST_F(SharedExclusiveLockTest, TestSharedShared) { + int value0, value1; + bool done0, done1; + ReadTask reader0(shared_exclusive_lock_.get(), &value_, &done0); + ReadTask reader1(shared_exclusive_lock_.get(), &value_, &done1); + + // Test shared locks can be shared without waiting. + { + SharedScope ss(shared_exclusive_lock_.get()); + value_ = 1; + done0 = false; + done1 = false; + reader0.PostRead(&value0); + reader1.PostRead(&value1); + Thread::SleepMs(kProcessTimeInMs); + } + + EXPECT_TRUE_WAIT(done0, kProcessTimeoutInMs); + EXPECT_EQ(1, value0); + EXPECT_LE(reader0.waiting_time_in_ms(), kNoWaitThresholdInMs); + EXPECT_TRUE_WAIT(done1, kProcessTimeoutInMs); + EXPECT_EQ(1, value1); + EXPECT_LE(reader1.waiting_time_in_ms(), kNoWaitThresholdInMs); +} + +TEST_F(SharedExclusiveLockTest, TestSharedExclusive) { + bool done; + WriteTask writer(shared_exclusive_lock_.get(), &value_, &done); + + // Test exclusive lock needs to wait for shared lock. + { + SharedScope ss(shared_exclusive_lock_.get()); + value_ = 1; + done = false; + writer.PostWrite(2); + Thread::SleepMs(kProcessTimeInMs); + EXPECT_EQ(1, value_); + } + + EXPECT_TRUE_WAIT(done, kProcessTimeoutInMs); + EXPECT_EQ(2, value_); + EXPECT_GE(writer.waiting_time_in_ms(), kWaitThresholdInMs); +} + +TEST_F(SharedExclusiveLockTest, TestExclusiveShared) { + int value; + bool done; + ReadTask reader(shared_exclusive_lock_.get(), &value_, &done); + + // Test shared lock needs to wait for exclusive lock. + { + ExclusiveScope es(shared_exclusive_lock_.get()); + value_ = 1; + done = false; + reader.PostRead(&value); + Thread::SleepMs(kProcessTimeInMs); + value_ = 2; + } + + EXPECT_TRUE_WAIT(done, kProcessTimeoutInMs); + EXPECT_EQ(2, value); + EXPECT_GE(reader.waiting_time_in_ms(), kWaitThresholdInMs); +} + +TEST_F(SharedExclusiveLockTest, TestExclusiveExclusive) { + bool done; + WriteTask writer(shared_exclusive_lock_.get(), &value_, &done); + + // Test exclusive lock needs to wait for exclusive lock. + { + ExclusiveScope es(shared_exclusive_lock_.get()); + value_ = 1; + done = false; + writer.PostWrite(2); + Thread::SleepMs(kProcessTimeInMs); + EXPECT_EQ(1, value_); + } + + EXPECT_TRUE_WAIT(done, kProcessTimeoutInMs); + EXPECT_EQ(2, value_); + EXPECT_GE(writer.waiting_time_in_ms(), kWaitThresholdInMs); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/signalthread.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/signalthread.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/signalthread.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/signalthread.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,149 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/signalthread.h" + +#include "webrtc/base/common.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// SignalThread +/////////////////////////////////////////////////////////////////////////////// + +SignalThread::SignalThread() + : main_(Thread::Current()), + worker_(this), + state_(kInit), + refcount_(1) { + main_->SignalQueueDestroyed.connect(this, + &SignalThread::OnMainThreadDestroyed); + worker_.SetName("SignalThread", this); +} + +SignalThread::~SignalThread() { + ASSERT(refcount_ == 0); +} + +bool SignalThread::SetName(const std::string& name, const void* obj) { + EnterExit ee(this); + ASSERT(main_->IsCurrent()); + ASSERT(kInit == state_); + return worker_.SetName(name, obj); +} + +bool SignalThread::SetPriority(ThreadPriority priority) { + EnterExit ee(this); + ASSERT(main_->IsCurrent()); + ASSERT(kInit == state_); + return worker_.SetPriority(priority); +} + +void SignalThread::Start() { + EnterExit ee(this); + ASSERT(main_->IsCurrent()); + if (kInit == state_ || kComplete == state_) { + state_ = kRunning; + OnWorkStart(); + worker_.Start(); + } else { + ASSERT(false); + } +} + +void SignalThread::Destroy(bool wait) { + EnterExit ee(this); + ASSERT(main_->IsCurrent()); + if ((kInit == state_) || (kComplete == state_)) { + refcount_--; + } else if (kRunning == state_ || kReleasing == state_) { + state_ = kStopping; + // OnWorkStop() must follow Quit(), so that when the thread wakes up due to + // OWS(), ContinueWork() will return false. + worker_.Quit(); + OnWorkStop(); + if (wait) { + // Release the thread's lock so that it can return from ::Run. + cs_.Leave(); + worker_.Stop(); + cs_.Enter(); + refcount_--; + } + } else { + ASSERT(false); + } +} + +void SignalThread::Release() { + EnterExit ee(this); + ASSERT(main_->IsCurrent()); + if (kComplete == state_) { + refcount_--; + } else if (kRunning == state_) { + state_ = kReleasing; + } else { + // if (kInit == state_) use Destroy() + ASSERT(false); + } +} + +bool SignalThread::ContinueWork() { + EnterExit ee(this); + ASSERT(worker_.IsCurrent()); + return worker_.ProcessMessages(0); +} + +void SignalThread::OnMessage(Message *msg) { + EnterExit ee(this); + if (ST_MSG_WORKER_DONE == msg->message_id) { + ASSERT(main_->IsCurrent()); + OnWorkDone(); + bool do_delete = false; + if (kRunning == state_) { + state_ = kComplete; + } else { + do_delete = true; + } + if (kStopping != state_) { + // Before signaling that the work is done, make sure that the worker + // thread actually is done. We got here because DoWork() finished and + // Run() posted the ST_MSG_WORKER_DONE message. This means the worker + // thread is about to go away anyway, but sometimes it doesn't actually + // finish before SignalWorkDone is processed, and for a reusable + // SignalThread this makes an assert in thread.cc fire. + // + // Calling Stop() on the worker ensures that the OS thread that underlies + // the worker will finish, and will be set to NULL, enabling us to call + // Start() again. + worker_.Stop(); + SignalWorkDone(this); + } + if (do_delete) { + refcount_--; + } + } +} + +void SignalThread::Run() { + DoWork(); + { + EnterExit ee(this); + if (main_) { + main_->Post(this, ST_MSG_WORKER_DONE); + } + } +} + +void SignalThread::OnMainThreadDestroyed() { + EnterExit ee(this); + main_ = NULL; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/signalthread.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/signalthread.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/signalthread.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/signalthread.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,157 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SIGNALTHREAD_H_ +#define WEBRTC_BASE_SIGNALTHREAD_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/sigslot.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// SignalThread - Base class for worker threads. The main thread should call +// Start() to begin work, and then follow one of these models: +// Normal: Wait for SignalWorkDone, and then call Release to destroy. +// Cancellation: Call Release(true), to abort the worker thread. +// Fire-and-forget: Call Release(false), which allows the thread to run to +// completion, and then self-destruct without further notification. +// Periodic tasks: Wait for SignalWorkDone, then eventually call Start() +// again to repeat the task. When the instance isn't needed anymore, +// call Release. DoWork, OnWorkStart and OnWorkStop are called again, +// on a new thread. +// The subclass should override DoWork() to perform the background task. By +// periodically calling ContinueWork(), it can check for cancellation. +// OnWorkStart and OnWorkDone can be overridden to do pre- or post-work +// tasks in the context of the main thread. +/////////////////////////////////////////////////////////////////////////////// + +class SignalThread + : public sigslot::has_slots<>, + protected MessageHandler { + public: + SignalThread(); + + // Context: Main Thread. Call before Start to change the worker's name. + bool SetName(const std::string& name, const void* obj); + + // Context: Main Thread. Call before Start to change the worker's priority. + bool SetPriority(ThreadPriority priority); + + // Context: Main Thread. Call to begin the worker thread. + void Start(); + + // Context: Main Thread. If the worker thread is not running, deletes the + // object immediately. Otherwise, asks the worker thread to abort processing, + // and schedules the object to be deleted once the worker exits. + // SignalWorkDone will not be signalled. If wait is true, does not return + // until the thread is deleted. + void Destroy(bool wait); + + // Context: Main Thread. If the worker thread is complete, deletes the + // object immediately. Otherwise, schedules the object to be deleted once + // the worker thread completes. SignalWorkDone will be signalled. + void Release(); + + // Context: Main Thread. Signalled when work is complete. + sigslot::signal1 SignalWorkDone; + + enum { ST_MSG_WORKER_DONE, ST_MSG_FIRST_AVAILABLE }; + + protected: + virtual ~SignalThread(); + + Thread* worker() { return &worker_; } + + // Context: Main Thread. Subclass should override to do pre-work setup. + virtual void OnWorkStart() { } + + // Context: Worker Thread. Subclass should override to do work. + virtual void DoWork() = 0; + + // Context: Worker Thread. Subclass should call periodically to + // dispatch messages and determine if the thread should terminate. + bool ContinueWork(); + + // Context: Worker Thread. Subclass should override when extra work is + // needed to abort the worker thread. + virtual void OnWorkStop() { } + + // Context: Main Thread. Subclass should override to do post-work cleanup. + virtual void OnWorkDone() { } + + // Context: Any Thread. If subclass overrides, be sure to call the base + // implementation. Do not use (message_id < ST_MSG_FIRST_AVAILABLE) + virtual void OnMessage(Message *msg); + + private: + enum State { + kInit, // Initialized, but not started + kRunning, // Started and doing work + kReleasing, // Same as running, but to be deleted when work is done + kComplete, // Work is done + kStopping, // Work is being interrupted + }; + + class Worker : public Thread { + public: + explicit Worker(SignalThread* parent) : parent_(parent) {} + virtual ~Worker() { Stop(); } + virtual void Run() { parent_->Run(); } + + private: + SignalThread* parent_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(Worker); + }; + + class SCOPED_LOCKABLE EnterExit { + public: + explicit EnterExit(SignalThread* t) EXCLUSIVE_LOCK_FUNCTION(t->cs_) + : t_(t) { + t_->cs_.Enter(); + // If refcount_ is zero then the object has already been deleted and we + // will be double-deleting it in ~EnterExit()! (shouldn't happen) + ASSERT(t_->refcount_ != 0); + ++t_->refcount_; + } + ~EnterExit() UNLOCK_FUNCTION() { + bool d = (0 == --t_->refcount_); + t_->cs_.Leave(); + if (d) + delete t_; + } + + private: + SignalThread* t_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(EnterExit); + }; + + void Run(); + void OnMainThreadDestroyed(); + + Thread* main_; + Worker worker_; + CriticalSection cs_; + State state_; + int refcount_; + + DISALLOW_COPY_AND_ASSIGN(SignalThread); +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_SIGNALTHREAD_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/signalthread_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/signalthread_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/signalthread_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/signalthread_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,199 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/signalthread.h" +#include "webrtc/base/thread.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +using namespace rtc; + +class SignalThreadTest : public testing::Test, public sigslot::has_slots<> { + public: + class SlowSignalThread : public SignalThread { + public: + SlowSignalThread(SignalThreadTest* harness) : harness_(harness) { + } + + virtual ~SlowSignalThread() { + EXPECT_EQ(harness_->main_thread_, Thread::Current()); + ++harness_->thread_deleted_; + } + + const SignalThreadTest* harness() { return harness_; } + + protected: + virtual void OnWorkStart() { + ASSERT_TRUE(harness_ != NULL); + ++harness_->thread_started_; + EXPECT_EQ(harness_->main_thread_, Thread::Current()); + EXPECT_FALSE(worker()->RunningForTest()); // not started yet + } + + virtual void OnWorkStop() { + ++harness_->thread_stopped_; + EXPECT_EQ(harness_->main_thread_, Thread::Current()); + EXPECT_TRUE(worker()->RunningForTest()); // not stopped yet + } + + virtual void OnWorkDone() { + ++harness_->thread_done_; + EXPECT_EQ(harness_->main_thread_, Thread::Current()); + EXPECT_TRUE(worker()->RunningForTest()); // not stopped yet + } + + virtual void DoWork() { + EXPECT_NE(harness_->main_thread_, Thread::Current()); + EXPECT_EQ(worker(), Thread::Current()); + Thread::Current()->socketserver()->Wait(250, false); + } + + private: + SignalThreadTest* harness_; + DISALLOW_EVIL_CONSTRUCTORS(SlowSignalThread); + }; + + void OnWorkComplete(rtc::SignalThread* thread) { + SlowSignalThread* t = static_cast(thread); + EXPECT_EQ(t->harness(), this); + EXPECT_EQ(main_thread_, Thread::Current()); + + ++thread_completed_; + if (!called_release_) { + thread->Release(); + } + } + + virtual void SetUp() { + main_thread_ = Thread::Current(); + thread_ = new SlowSignalThread(this); + thread_->SignalWorkDone.connect(this, &SignalThreadTest::OnWorkComplete); + called_release_ = false; + thread_started_ = 0; + thread_done_ = 0; + thread_completed_ = 0; + thread_stopped_ = 0; + thread_deleted_ = 0; + } + + virtual void TearDown() { + } + + Thread* main_thread_; + SlowSignalThread* thread_; + bool called_release_; + + int thread_started_; + int thread_done_; + int thread_completed_; + int thread_stopped_; + int thread_deleted_; +}; + +class OwnerThread : public Thread, public sigslot::has_slots<> { + public: + explicit OwnerThread(SignalThreadTest* harness) + : harness_(harness), + has_run_(false) { + } + + virtual ~OwnerThread() { + Stop(); + } + + virtual void Run() { + SignalThreadTest::SlowSignalThread* signal_thread = + new SignalThreadTest::SlowSignalThread(harness_); + signal_thread->SignalWorkDone.connect(this, &OwnerThread::OnWorkDone); + signal_thread->Start(); + Thread::Current()->socketserver()->Wait(100, false); + signal_thread->Release(); + // Delete |signal_thread|. + signal_thread->Destroy(true); + has_run_ = true; + } + + bool has_run() { return has_run_; } + void OnWorkDone(SignalThread* signal_thread) { + FAIL() << " This shouldn't get called."; + } + + private: + SignalThreadTest* harness_; + bool has_run_; + DISALLOW_EVIL_CONSTRUCTORS(OwnerThread); +}; + +// Test for when the main thread goes away while the +// signal thread is still working. This may happen +// when shutting down the process. +TEST_F(SignalThreadTest, OwnerThreadGoesAway) { + { + scoped_ptr owner(new OwnerThread(this)); + main_thread_ = owner.get(); + owner->Start(); + while (!owner->has_run()) { + Thread::Current()->socketserver()->Wait(10, false); + } + } + // At this point the main thread has gone away. + // Give the SignalThread a little time to do its callback, + // which will crash if the signal thread doesn't handle + // this situation well. + Thread::Current()->socketserver()->Wait(500, false); +} + +#define EXPECT_STATE(started, done, completed, stopped, deleted) \ + EXPECT_EQ(started, thread_started_); \ + EXPECT_EQ(done, thread_done_); \ + EXPECT_EQ(completed, thread_completed_); \ + EXPECT_EQ(stopped, thread_stopped_); \ + EXPECT_EQ(deleted, thread_deleted_); + +TEST_F(SignalThreadTest, ThreadFinishes) { + thread_->Start(); + EXPECT_STATE(1, 0, 0, 0, 0); + Thread::SleepMs(500); + EXPECT_STATE(1, 0, 0, 0, 0); + Thread::Current()->ProcessMessages(0); + EXPECT_STATE(1, 1, 1, 0, 1); +} + +TEST_F(SignalThreadTest, ReleasedThreadFinishes) { + thread_->Start(); + EXPECT_STATE(1, 0, 0, 0, 0); + thread_->Release(); + called_release_ = true; + EXPECT_STATE(1, 0, 0, 0, 0); + Thread::SleepMs(500); + EXPECT_STATE(1, 0, 0, 0, 0); + Thread::Current()->ProcessMessages(0); + EXPECT_STATE(1, 1, 1, 0, 1); +} + +TEST_F(SignalThreadTest, DestroyedThreadCleansUp) { + thread_->Start(); + EXPECT_STATE(1, 0, 0, 0, 0); + thread_->Destroy(true); + EXPECT_STATE(1, 0, 0, 1, 1); + Thread::Current()->ProcessMessages(0); + EXPECT_STATE(1, 0, 0, 1, 1); +} + +TEST_F(SignalThreadTest, DeferredDestroyedThreadCleansUp) { + thread_->Start(); + EXPECT_STATE(1, 0, 0, 0, 0); + thread_->Destroy(false); + EXPECT_STATE(1, 0, 0, 1, 0); + Thread::SleepMs(500); + EXPECT_STATE(1, 0, 0, 1, 0); + Thread::Current()->ProcessMessages(0); + EXPECT_STATE(1, 1, 0, 1, 1); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sigslot.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sigslot.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sigslot.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sigslot.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,2853 @@ +// sigslot.h: Signal/Slot classes +// +// Written by Sarah Thompson (sarah@telergy.com) 2002. +// +// License: Public domain. You are free to use this code however you like, with the proviso that +// the author takes on no responsibility or liability for any use. +// +// QUICK DOCUMENTATION +// +// (see also the full documentation at http://sigslot.sourceforge.net/) +// +// #define switches +// SIGSLOT_PURE_ISO - Define this to force ISO C++ compliance. This also disables +// all of the thread safety support on platforms where it is +// available. +// +// SIGSLOT_USE_POSIX_THREADS - Force use of Posix threads when using a C++ compiler other than +// gcc on a platform that supports Posix threads. (When using gcc, +// this is the default - use SIGSLOT_PURE_ISO to disable this if +// necessary) +// +// SIGSLOT_DEFAULT_MT_POLICY - Where thread support is enabled, this defaults to multi_threaded_global. +// Otherwise, the default is single_threaded. #define this yourself to +// override the default. In pure ISO mode, anything other than +// single_threaded will cause a compiler error. +// +// PLATFORM NOTES +// +// Win32 - On Win32, the WEBRTC_WIN symbol must be #defined. Most mainstream +// compilers do this by default, but you may need to define it +// yourself if your build environment is less standard. This causes +// the Win32 thread support to be compiled in and used automatically. +// +// Unix/Linux/BSD, etc. - If you're using gcc, it is assumed that you have Posix threads +// available, so they are used automatically. You can override this +// (as under Windows) with the SIGSLOT_PURE_ISO switch. If you're using +// something other than gcc but still want to use Posix threads, you +// need to #define SIGSLOT_USE_POSIX_THREADS. +// +// ISO C++ - If none of the supported platforms are detected, or if +// SIGSLOT_PURE_ISO is defined, all multithreading support is turned off, +// along with any code that might cause a pure ISO C++ environment to +// complain. Before you ask, gcc -ansi -pedantic won't compile this +// library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of +// errors that aren't really there. If you feel like investigating this, +// please contact the author. +// +// +// THREADING MODES +// +// single_threaded - Your program is assumed to be single threaded from the point of view +// of signal/slot usage (i.e. all objects using signals and slots are +// created and destroyed from a single thread). Behaviour if objects are +// destroyed concurrently is undefined (i.e. you'll get the occasional +// segmentation fault/memory exception). +// +// multi_threaded_global - Your program is assumed to be multi threaded. Objects using signals and +// slots can be safely created and destroyed from any thread, even when +// connections exist. In multi_threaded_global mode, this is achieved by a +// single global mutex (actually a critical section on Windows because they +// are faster). This option uses less OS resources, but results in more +// opportunities for contention, possibly resulting in more context switches +// than are strictly necessary. +// +// multi_threaded_local - Behaviour in this mode is essentially the same as multi_threaded_global, +// except that each signal, and each object that inherits has_slots, all +// have their own mutex/critical section. In practice, this means that +// mutex collisions (and hence context switches) only happen if they are +// absolutely essential. However, on some platforms, creating a lot of +// mutexes can slow down the whole OS, so use this option with care. +// +// USING THE LIBRARY +// +// See the full documentation at http://sigslot.sourceforge.net/ +// +// +// Libjingle specific: +// This file has been modified such that has_slots and signalx do not have to be +// using the same threading requirements. E.g. it is possible to connect a +// has_slots and signal0 or +// has_slots and signal0. +// If has_slots is single threaded the user must ensure that it is not trying +// to connect or disconnect to signalx concurrently or data race may occur. +// If signalx is single threaded the user must ensure that disconnect, connect +// or signal is not happening concurrently or data race may occur. + +#ifndef WEBRTC_BASE_SIGSLOT_H__ +#define WEBRTC_BASE_SIGSLOT_H__ +#ifndef TALK_BASE_SIGSLOT_H__ +#define TALK_BASE_SIGSLOT_H__ + +#include +#include +#include + +// On our copy of sigslot.h, we set single threading as default. +#define SIGSLOT_DEFAULT_MT_POLICY single_threaded + +#if defined(SIGSLOT_PURE_ISO) || (!defined(WEBRTC_WIN) && !defined(__GNUG__) && !defined(SIGSLOT_USE_POSIX_THREADS)) +# define _SIGSLOT_SINGLE_THREADED +#elif defined(WEBRTC_WIN) +# define _SIGSLOT_HAS_WIN32_THREADS +# if !defined(WIN32_LEAN_AND_MEAN) +# define WIN32_LEAN_AND_MEAN +# endif +# include "webrtc/base/win32.h" +#elif defined(__GNUG__) || defined(SIGSLOT_USE_POSIX_THREADS) +# define _SIGSLOT_HAS_POSIX_THREADS +# include +#else +# define _SIGSLOT_SINGLE_THREADED +#endif + +#ifndef SIGSLOT_DEFAULT_MT_POLICY +# ifdef _SIGSLOT_SINGLE_THREADED +# define SIGSLOT_DEFAULT_MT_POLICY single_threaded +# else +# define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local +# endif +#endif + +// TODO: change this namespace to rtc? +namespace sigslot { + + class single_threaded + { + public: + single_threaded() + { + ; + } + + virtual ~single_threaded() + { + ; + } + + virtual void lock() + { + ; + } + + virtual void unlock() + { + ; + } + }; + +#ifdef _SIGSLOT_HAS_WIN32_THREADS + // The multi threading policies only get compiled in if they are enabled. + class multi_threaded_global + { + public: + multi_threaded_global() + { + static bool isinitialised = false; + + if(!isinitialised) + { + InitializeCriticalSection(get_critsec()); + isinitialised = true; + } + } + + multi_threaded_global(const multi_threaded_global&) + { + ; + } + + virtual ~multi_threaded_global() + { + ; + } + + virtual void lock() + { + EnterCriticalSection(get_critsec()); + } + + virtual void unlock() + { + LeaveCriticalSection(get_critsec()); + } + + private: + CRITICAL_SECTION* get_critsec() + { + static CRITICAL_SECTION g_critsec; + return &g_critsec; + } + }; + + class multi_threaded_local + { + public: + multi_threaded_local() + { + InitializeCriticalSection(&m_critsec); + } + + multi_threaded_local(const multi_threaded_local&) + { + InitializeCriticalSection(&m_critsec); + } + + virtual ~multi_threaded_local() + { + DeleteCriticalSection(&m_critsec); + } + + virtual void lock() + { + EnterCriticalSection(&m_critsec); + } + + virtual void unlock() + { + LeaveCriticalSection(&m_critsec); + } + + private: + CRITICAL_SECTION m_critsec; + }; +#endif // _SIGSLOT_HAS_WIN32_THREADS + +#ifdef _SIGSLOT_HAS_POSIX_THREADS + // The multi threading policies only get compiled in if they are enabled. + class multi_threaded_global + { + public: + multi_threaded_global() + { + pthread_mutex_init(get_mutex(), NULL); + } + + multi_threaded_global(const multi_threaded_global&) + { + ; + } + + virtual ~multi_threaded_global() + { + ; + } + + virtual void lock() + { + pthread_mutex_lock(get_mutex()); + } + + virtual void unlock() + { + pthread_mutex_unlock(get_mutex()); + } + + private: + pthread_mutex_t* get_mutex() + { + static pthread_mutex_t g_mutex; + return &g_mutex; + } + }; + + class multi_threaded_local + { + public: + multi_threaded_local() + { + pthread_mutex_init(&m_mutex, NULL); + } + + multi_threaded_local(const multi_threaded_local&) + { + pthread_mutex_init(&m_mutex, NULL); + } + + virtual ~multi_threaded_local() + { + pthread_mutex_destroy(&m_mutex); + } + + virtual void lock() + { + pthread_mutex_lock(&m_mutex); + } + + virtual void unlock() + { + pthread_mutex_unlock(&m_mutex); + } + + private: + pthread_mutex_t m_mutex; + }; +#endif // _SIGSLOT_HAS_POSIX_THREADS + + template + class lock_block + { + public: + mt_policy *m_mutex; + + lock_block(mt_policy *mtx) + : m_mutex(mtx) + { + m_mutex->lock(); + } + + ~lock_block() + { + m_mutex->unlock(); + } + }; + + class has_slots_interface; + + template + class _connection_base0 + { + public: + virtual ~_connection_base0() {} + virtual has_slots_interface* getdest() const = 0; + virtual void emit() = 0; + virtual _connection_base0* clone() = 0; + virtual _connection_base0* duplicate(has_slots_interface* pnewdest) = 0; + }; + + template + class _connection_base1 + { + public: + virtual ~_connection_base1() {} + virtual has_slots_interface* getdest() const = 0; + virtual void emit(arg1_type) = 0; + virtual _connection_base1* clone() = 0; + virtual _connection_base1* duplicate(has_slots_interface* pnewdest) = 0; + }; + + template + class _connection_base2 + { + public: + virtual ~_connection_base2() {} + virtual has_slots_interface* getdest() const = 0; + virtual void emit(arg1_type, arg2_type) = 0; + virtual _connection_base2* clone() = 0; + virtual _connection_base2* duplicate(has_slots_interface* pnewdest) = 0; + }; + + template + class _connection_base3 + { + public: + virtual ~_connection_base3() {} + virtual has_slots_interface* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type) = 0; + virtual _connection_base3* clone() = 0; + virtual _connection_base3* duplicate(has_slots_interface* pnewdest) = 0; + }; + + template + class _connection_base4 + { + public: + virtual ~_connection_base4() {} + virtual has_slots_interface* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type) = 0; + virtual _connection_base4* clone() = 0; + virtual _connection_base4* duplicate(has_slots_interface* pnewdest) = 0; + }; + + template + class _connection_base5 + { + public: + virtual ~_connection_base5() {} + virtual has_slots_interface* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type) = 0; + virtual _connection_base5* clone() = 0; + virtual _connection_base5* duplicate(has_slots_interface* pnewdest) = 0; + }; + + template + class _connection_base6 + { + public: + virtual ~_connection_base6() {} + virtual has_slots_interface* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type) = 0; + virtual _connection_base6* clone() = 0; + virtual _connection_base6* duplicate(has_slots_interface* pnewdest) = 0; + }; + + template + class _connection_base7 + { + public: + virtual ~_connection_base7() {} + virtual has_slots_interface* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type) = 0; + virtual _connection_base7* clone() = 0; + virtual _connection_base7* duplicate(has_slots_interface* pnewdest) = 0; + }; + + template + class _connection_base8 + { + public: + virtual ~_connection_base8() {} + virtual has_slots_interface* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type, arg8_type) = 0; + virtual _connection_base8* clone() = 0; + virtual _connection_base8* duplicate(has_slots_interface* pnewdest) = 0; + }; + + class _signal_base_interface + { + public: + virtual void slot_disconnect(has_slots_interface* pslot) = 0; + virtual void slot_duplicate(const has_slots_interface* poldslot, has_slots_interface* pnewslot) = 0; + }; + + template + class _signal_base : public _signal_base_interface, public mt_policy + { + }; + + class has_slots_interface + { + public: + has_slots_interface() + { + ; + } + + virtual void signal_connect(_signal_base_interface* sender) = 0; + + virtual void signal_disconnect(_signal_base_interface* sender) = 0; + + virtual ~has_slots_interface() + { + } + + virtual void disconnect_all() = 0; + }; + + template + class has_slots : public has_slots_interface, public mt_policy + { + private: + typedef std::set<_signal_base_interface*> sender_set; + typedef sender_set::const_iterator const_iterator; + + public: + has_slots() + { + ; + } + + has_slots(const has_slots& hs) + { + lock_block lock(this); + const_iterator it = hs.m_senders.begin(); + const_iterator itEnd = hs.m_senders.end(); + + while(it != itEnd) + { + (*it)->slot_duplicate(&hs, this); + m_senders.insert(*it); + ++it; + } + } + + void signal_connect(_signal_base_interface* sender) + { + lock_block lock(this); + m_senders.insert(sender); + } + + void signal_disconnect(_signal_base_interface* sender) + { + lock_block lock(this); + m_senders.erase(sender); + } + + virtual ~has_slots() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_senders.begin(); + const_iterator itEnd = m_senders.end(); + + while(it != itEnd) + { + (*it)->slot_disconnect(this); + ++it; + } + + m_senders.erase(m_senders.begin(), m_senders.end()); + } + + private: + sender_set m_senders; + }; + + template + class _signal_base0 : public _signal_base + { + public: + typedef std::list<_connection_base0 *> connections_list; + + _signal_base0() + { + ; + } + + _signal_base0(const _signal_base0& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + ~_signal_base0() + { + disconnect_all(); + } + + bool is_empty() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + return it == itEnd; + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots_interface* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + } + + it = itNext; + } + } + + void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base1 : public _signal_base + { + public: + typedef std::list<_connection_base1 *> connections_list; + + _signal_base1() + { + ; + } + + _signal_base1(const _signal_base1& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base1() + { + disconnect_all(); + } + + bool is_empty() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + return it == itEnd; + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots_interface* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + } + + it = itNext; + } + } + + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base2 : public _signal_base + { + public: + typedef std::list<_connection_base2 *> + connections_list; + + _signal_base2() + { + ; + } + + _signal_base2(const _signal_base2& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base2() + { + disconnect_all(); + } + + bool is_empty() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + return it == itEnd; + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots_interface* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base3 : public _signal_base + { + public: + typedef std::list<_connection_base3 *> + connections_list; + + _signal_base3() + { + ; + } + + _signal_base3(const _signal_base3& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base3() + { + disconnect_all(); + } + + bool is_empty() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + return it == itEnd; + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots_interface* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base4 : public _signal_base + { + public: + typedef std::list<_connection_base4 *> connections_list; + + _signal_base4() + { + ; + } + + _signal_base4(const _signal_base4& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base4() + { + disconnect_all(); + } + + bool is_empty() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + return it == itEnd; + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots_interface* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base5 : public _signal_base + { + public: + typedef std::list<_connection_base5 *> connections_list; + + _signal_base5() + { + ; + } + + _signal_base5(const _signal_base5& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base5() + { + disconnect_all(); + } + + bool is_empty() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + return it == itEnd; + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots_interface* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base6 : public _signal_base + { + public: + typedef std::list<_connection_base6 *> connections_list; + + _signal_base6() + { + ; + } + + _signal_base6(const _signal_base6& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base6() + { + disconnect_all(); + } + + bool is_empty() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + return it == itEnd; + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots_interface* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base7 : public _signal_base + { + public: + typedef std::list<_connection_base7 *> connections_list; + + _signal_base7() + { + ; + } + + _signal_base7(const _signal_base7& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base7() + { + disconnect_all(); + } + + bool is_empty() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + return it == itEnd; + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots_interface* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base8 : public _signal_base + { + public: + typedef std::list<_connection_base8 *> + connections_list; + + _signal_base8() + { + ; + } + + _signal_base8(const _signal_base8& s) + : _signal_base(s) + { + lock_block lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base8() + { + disconnect_all(); + } + + bool is_empty() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + return it == itEnd; + } + + void disconnect_all() + { + lock_block lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots_interface* pclass) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots_interface* pslot) + { + lock_block lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + + template + class _connection0 : public _connection_base0 + { + public: + _connection0() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection0(dest_type* pobject, void (dest_type::*pmemfun)()) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection0() + { + } + + virtual _connection_base0* clone() + { + return new _connection0(*this); + } + + virtual _connection_base0* duplicate(has_slots_interface* pnewdest) + { + return new _connection0((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit() + { + (m_pobject->*m_pmemfun)(); + } + + virtual has_slots_interface* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(); + }; + + template + class _connection1 : public _connection_base1 + { + public: + _connection1() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection1(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection1() + { + } + + virtual _connection_base1* clone() + { + return new _connection1(*this); + } + + virtual _connection_base1* duplicate(has_slots_interface* pnewdest) + { + return new _connection1((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1) + { + (m_pobject->*m_pmemfun)(a1); + } + + virtual has_slots_interface* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type); + }; + + template + class _connection2 : public _connection_base2 + { + public: + _connection2() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection2(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection2() + { + } + + virtual _connection_base2* clone() + { + return new _connection2(*this); + } + + virtual _connection_base2* duplicate(has_slots_interface* pnewdest) + { + return new _connection2((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2) + { + (m_pobject->*m_pmemfun)(a1, a2); + } + + virtual has_slots_interface* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type); + }; + + template + class _connection3 : public _connection_base3 + { + public: + _connection3() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection3(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection3() + { + } + + virtual _connection_base3* clone() + { + return new _connection3(*this); + } + + virtual _connection_base3* duplicate(has_slots_interface* pnewdest) + { + return new _connection3((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3) + { + (m_pobject->*m_pmemfun)(a1, a2, a3); + } + + virtual has_slots_interface* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type); + }; + + template + class _connection4 : public _connection_base4 + { + public: + _connection4() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection4(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection4() + { + } + + virtual _connection_base4* clone() + { + return new _connection4(*this); + } + + virtual _connection_base4* duplicate(has_slots_interface* pnewdest) + { + return new _connection4((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, + arg4_type a4) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4); + } + + virtual has_slots_interface* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, + arg4_type); + }; + + template + class _connection5 : public _connection_base5 + { + public: + _connection5() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection5(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection5() + { + } + + virtual _connection_base5* clone() + { + return new _connection5(*this); + } + + virtual _connection_base5* duplicate(has_slots_interface* pnewdest) + { + return new _connection5((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5); + } + + virtual has_slots_interface* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type); + }; + + template + class _connection6 : public _connection_base6 + { + public: + _connection6() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection6(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection6() + { + } + + virtual _connection_base6* clone() + { + return new _connection6(*this); + } + + virtual _connection_base6* duplicate(has_slots_interface* pnewdest) + { + return new _connection6((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6); + } + + virtual has_slots_interface* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type); + }; + + template + class _connection7 : public _connection_base7 + { + public: + _connection7() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection7(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, arg7_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection7() + { + } + + virtual _connection_base7* clone() + { + return new _connection7(*this); + } + + virtual _connection_base7* duplicate(has_slots_interface* pnewdest) + { + return new _connection7((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7); + } + + virtual has_slots_interface* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type); + }; + + template + class _connection8 : public _connection_base8 + { + public: + _connection8() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection8(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection8() + { + } + + virtual _connection_base8* clone() + { + return new _connection8(*this); + } + + virtual _connection_base8* duplicate(has_slots_interface* pnewdest) + { + return new _connection8((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7, a8); + } + + virtual has_slots_interface* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type); + }; + + template + class signal0 : public _signal_base0 + { + public: + typedef _signal_base0 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal0() + { + ; + } + + signal0(const signal0& s) + : _signal_base0(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)()) + { + lock_block lock(this); + _connection0* conn = + new _connection0(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit() + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(); + + it = itNext; + } + } + + void operator()() + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(); + + it = itNext; + } + } + }; + + template + class signal1 : public _signal_base1 + { + public: + typedef _signal_base1 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal1() + { + ; + } + + signal1(const signal1& s) + : _signal_base1(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type)) + { + lock_block lock(this); + _connection1* conn = + new _connection1(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1); + + it = itNext; + } + } + + void operator()(arg1_type a1) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1); + + it = itNext; + } + } + }; + + template + class signal2 : public _signal_base2 + { + public: + typedef _signal_base2 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal2() + { + ; + } + + signal2(const signal2& s) + : _signal_base2(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type)) + { + lock_block lock(this); + _connection2* conn = new + _connection2(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2); + + it = itNext; + } + } + }; + + template + class signal3 : public _signal_base3 + { + public: + typedef _signal_base3 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal3() + { + ; + } + + signal3(const signal3& s) + : _signal_base3(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type)) + { + lock_block lock(this); + _connection3* conn = + new _connection3(pclass, + pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3); + + it = itNext; + } + } + }; + + template + class signal4 : public _signal_base4 + { + public: + typedef _signal_base4 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal4() + { + ; + } + + signal4(const signal4& s) + : _signal_base4(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) + { + lock_block lock(this); + _connection4* + conn = new _connection4(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4); + + it = itNext; + } + } + }; + + template + class signal5 : public _signal_base5 + { + public: + typedef _signal_base5 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal5() + { + ; + } + + signal5(const signal5& s) + : _signal_base5(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) + { + lock_block lock(this); + _connection5* conn = new _connection5(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5); + + it = itNext; + } + } + }; + + + template + class signal6 : public _signal_base6 + { + public: + typedef _signal_base6 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal6() + { + ; + } + + signal6(const signal6& s) + : _signal_base6(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) + { + lock_block lock(this); + _connection6* conn = + new _connection6(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6); + + it = itNext; + } + } + }; + + template + class signal7 : public _signal_base7 + { + public: + typedef _signal_base7 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal7() + { + ; + } + + signal7(const signal7& s) + : _signal_base7(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type)) + { + lock_block lock(this); + _connection7* conn = + new _connection7(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7); + + it = itNext; + } + } + }; + + template + class signal8 : public _signal_base8 + { + public: + typedef _signal_base8 base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal8() + { + ; + } + + signal8(const signal8& s) + : _signal_base8(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) + { + lock_block lock(this); + _connection8* conn = + new _connection8(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8); + + it = itNext; + } + } + }; + +}; // namespace sigslot + +#endif // TALK_BASE_SIGSLOT_H__ +#endif // WEBRTC_BASE_SIGSLOT_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sigslotrepeater.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sigslotrepeater.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sigslotrepeater.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sigslotrepeater.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,94 @@ +/* + * Copyright 2006 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SIGSLOTREPEATER_H__ +#define WEBRTC_BASE_SIGSLOTREPEATER_H__ + +// repeaters are both signals and slots, which are designed as intermediate +// pass-throughs for signals and slots which don't know about each other (for +// modularity or encapsulation). This eliminates the need to declare a signal +// handler whose sole purpose is to fire another signal. The repeater connects +// to the originating signal using the 'repeat' method. When the repeated +// signal fires, the repeater will also fire. + +#include "webrtc/base/sigslot.h" + +namespace sigslot { + + template + class repeater0 : public signal0, + public has_slots + { + public: + typedef signal0 base_type; + typedef repeater0 this_type; + + repeater0() { } + repeater0(const this_type& s) : base_type(s) { } + + void reemit() { signal0::emit(); } + void repeat(base_type &s) { s.connect(this, &this_type::reemit); } + void stop(base_type &s) { s.disconnect(this); } + }; + + template + class repeater1 : public signal1, + public has_slots + { + public: + typedef signal1 base_type; + typedef repeater1 this_type; + + repeater1() { } + repeater1(const this_type& s) : base_type(s) { } + + void reemit(arg1_type a1) { signal1::emit(a1); } + void repeat(base_type& s) { s.connect(this, &this_type::reemit); } + void stop(base_type &s) { s.disconnect(this); } + }; + + template + class repeater2 : public signal2, + public has_slots + { + public: + typedef signal2 base_type; + typedef repeater2 this_type; + + repeater2() { } + repeater2(const this_type& s) : base_type(s) { } + + void reemit(arg1_type a1, arg2_type a2) { signal2::emit(a1,a2); } + void repeat(base_type& s) { s.connect(this, &this_type::reemit); } + void stop(base_type &s) { s.disconnect(this); } + }; + + template + class repeater3 : public signal3, + public has_slots + { + public: + typedef signal3 base_type; + typedef repeater3 this_type; + + repeater3() { } + repeater3(const this_type& s) : base_type(s) { } + + void reemit(arg1_type a1, arg2_type a2, arg3_type a3) { + signal3::emit(a1,a2,a3); + } + void repeat(base_type& s) { s.connect(this, &this_type::reemit); } + void stop(base_type &s) { s.disconnect(this); } + }; + +} // namespace sigslot + +#endif // WEBRTC_BASE_SIGSLOTREPEATER_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sigslottester.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sigslottester.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sigslottester.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sigslottester.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,199 @@ +// This file was GENERATED by command: +// pump.py sigslottester.h.pump +// DO NOT EDIT BY HAND!!! + +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SIGSLOTTESTER_H_ +#define WEBRTC_BASE_SIGSLOTTESTER_H_ + +// To generate sigslottester.h from sigslottester.h.pump, execute: +// /home/build/google3/third_party/gtest/scripts/pump.py sigslottester.h.pump + + +// SigslotTester(s) are utility classes to check if signals owned by an +// object are being invoked at the right time and with the right arguments. +// They are meant to be used in tests. Tests must provide "capture" pointers +// (i.e. address of variables) where the arguments from the signal callback +// can be stored. +// +// Example: +// /* Some signal */ +// sigslot::signal1 foo; +// +// /* We want to monitor foo in some test. Note how signal argument is +// const std::string&, but capture-type is std::string. Capture type +// must be type that can be assigned to. */ +// std::string capture; +// SigslotTester1 slot(&foo, &capture); +// foo.emit("hello"); +// EXPECT_EQ(1, slot.callback_count()); +// EXPECT_EQ("hello", capture); +// /* See unit-tests for more examples */ + +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/sigslot.h" + +namespace rtc { + +// For all the templates below: +// - A1-A5 is the type of the argument i in the callback. Signals may and often +// do use const-references here for efficiency. +// - C1-C5 is the type of the variable to capture argument i. These should be +// non-const value types suitable for use as lvalues. + +template +class SigslotTester1 : public sigslot::has_slots<> { + public: + SigslotTester1(sigslot::signal1* signal, + C1* capture1) + : callback_count_(0), + capture1_(capture1) { + signal->connect(this, &SigslotTester1::OnSignalCallback); + } + + int callback_count() const { return callback_count_; } + + private: + void OnSignalCallback(A1 arg1) { + callback_count_++; + *capture1_ = arg1; + } + + int callback_count_; + C1* capture1_; + + DISALLOW_COPY_AND_ASSIGN(SigslotTester1); +}; + +template +class SigslotTester2 : public sigslot::has_slots<> { + public: + SigslotTester2(sigslot::signal2* signal, + C1* capture1, C2* capture2) + : callback_count_(0), + capture1_(capture1), capture2_(capture2) { + signal->connect(this, &SigslotTester2::OnSignalCallback); + } + + int callback_count() const { return callback_count_; } + + private: + void OnSignalCallback(A1 arg1, A2 arg2) { + callback_count_++; + *capture1_ = arg1; + *capture2_ = arg2; + } + + int callback_count_; + C1* capture1_; + C2* capture2_; + + DISALLOW_COPY_AND_ASSIGN(SigslotTester2); +}; + +template +class SigslotTester3 : public sigslot::has_slots<> { + public: + SigslotTester3(sigslot::signal3* signal, + C1* capture1, C2* capture2, C3* capture3) + : callback_count_(0), + capture1_(capture1), capture2_(capture2), capture3_(capture3) { + signal->connect(this, &SigslotTester3::OnSignalCallback); + } + + int callback_count() const { return callback_count_; } + + private: + void OnSignalCallback(A1 arg1, A2 arg2, A3 arg3) { + callback_count_++; + *capture1_ = arg1; + *capture2_ = arg2; + *capture3_ = arg3; + } + + int callback_count_; + C1* capture1_; + C2* capture2_; + C3* capture3_; + + DISALLOW_COPY_AND_ASSIGN(SigslotTester3); +}; + +template +class SigslotTester4 : public sigslot::has_slots<> { + public: + SigslotTester4(sigslot::signal4* signal, + C1* capture1, C2* capture2, C3* capture3, C4* capture4) + : callback_count_(0), + capture1_(capture1), capture2_(capture2), capture3_(capture3), + capture4_(capture4) { + signal->connect(this, &SigslotTester4::OnSignalCallback); + } + + int callback_count() const { return callback_count_; } + + private: + void OnSignalCallback(A1 arg1, A2 arg2, A3 arg3, A4 arg4) { + callback_count_++; + *capture1_ = arg1; + *capture2_ = arg2; + *capture3_ = arg3; + *capture4_ = arg4; + } + + int callback_count_; + C1* capture1_; + C2* capture2_; + C3* capture3_; + C4* capture4_; + + DISALLOW_COPY_AND_ASSIGN(SigslotTester4); +}; + +template +class SigslotTester5 : public sigslot::has_slots<> { + public: + SigslotTester5(sigslot::signal5* signal, + C1* capture1, C2* capture2, C3* capture3, C4* capture4, + C5* capture5) + : callback_count_(0), + capture1_(capture1), capture2_(capture2), capture3_(capture3), + capture4_(capture4), capture5_(capture5) { + signal->connect(this, &SigslotTester5::OnSignalCallback); + } + + int callback_count() const { return callback_count_; } + + private: + void OnSignalCallback(A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5) { + callback_count_++; + *capture1_ = arg1; + *capture2_ = arg2; + *capture3_ = arg3; + *capture4_ = arg4; + *capture5_ = arg5; + } + + int callback_count_; + C1* capture1_; + C2* capture2_; + C3* capture3_; + C4* capture4_; + C5* capture5_; + + DISALLOW_COPY_AND_ASSIGN(SigslotTester5); +}; +} // namespace rtc + +#endif // WEBRTC_BASE_SIGSLOTTESTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sigslottester.h.pump thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sigslottester.h.pump --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sigslottester.h.pump 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sigslottester.h.pump 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,85 @@ +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SIGSLOTTESTER_H_ +#define WEBRTC_BASE_SIGSLOTTESTER_H_ + +// To generate sigslottester.h from sigslottester.h.pump, execute: +// /home/build/google3/third_party/gtest/scripts/pump.py sigslottester.h.pump + + +// SigslotTester(s) are utility classes to check if signals owned by an +// object are being invoked at the right time and with the right arguments. +// They are meant to be used in tests. Tests must provide "capture" pointers +// (i.e. address of variables) where the arguments from the signal callback +// can be stored. +// +// Example: +// /* Some signal */ +// sigslot::signal1 foo; +// +// /* We want to monitor foo in some test. Note how signal argument is +// const std::string&, but capture-type is std::string. Capture type +// must be type that can be assigned to. */ +// std::string capture; +// SigslotTester1 slot(&foo, &capture); +// foo.emit("hello"); +// EXPECT_EQ(1, slot.callback_count()); +// EXPECT_EQ("hello", capture); +// /* See unit-tests for more examples */ + +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/sigslot.h" + +namespace rtc { + +// For all the templates below: +// - A1-A5 is the type of the argument i in the callback. Signals may and often +// do use const-references here for efficiency. +// - C1-C5 is the type of the variable to capture argument i. These should be +// non-const value types suitable for use as lvalues. + +$var n = 5 +$range i 1..n +$for i [[ +$range j 1..i + +template <$for j , [[class A$j]], $for j , [[class C$j]]> +class SigslotTester$i : public sigslot::has_slots<> { + public: + SigslotTester$i(sigslot::signal$i<$for j , [[A$j]]>* signal, + $for j , [[C$j* capture$j]]) + : callback_count_(0), + $for j , [[capture$j[[]]_(capture$j)]] { + signal->connect(this, &SigslotTester$i::OnSignalCallback); + } + + int callback_count() const { return callback_count_; } + + private: + void OnSignalCallback($for j , [[A$j arg$j]]) { + callback_count_++;$for j [[ + + *capture$j[[]]_ = arg$j;]] + + } + + int callback_count_;$for j [[ + + C$j* capture$j[[]]_;]] + + + DISALLOW_COPY_AND_ASSIGN(SigslotTester$i); +}; + +]] +} // namespace rtc + +#endif // WEBRTC_BASE_SIGSLOTTESTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sigslottester_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sigslottester_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sigslottester_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sigslottester_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,86 @@ +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/sigslottester.h" + +#include + +#include "webrtc/base/gunit.h" +#include "webrtc/base/sigslot.h" + +namespace rtc { + +TEST(SigslotTester, TestSignal1Arg) { + sigslot::signal1 source1; + int capture1; + SigslotTester1 slot1(&source1, &capture1); + EXPECT_EQ(0, slot1.callback_count()); + + source1.emit(10); + EXPECT_EQ(1, slot1.callback_count()); + EXPECT_EQ(10, capture1); + + source1.emit(20); + EXPECT_EQ(2, slot1.callback_count()); + EXPECT_EQ(20, capture1); +} + +TEST(SigslotTester, TestSignal2Args) { + sigslot::signal2 source2; + int capture1; + char capture2; + SigslotTester2 slot2(&source2, &capture1, &capture2); + EXPECT_EQ(0, slot2.callback_count()); + + source2.emit(10, 'x'); + EXPECT_EQ(1, slot2.callback_count()); + EXPECT_EQ(10, capture1); + EXPECT_EQ('x', capture2); + + source2.emit(20, 'y'); + EXPECT_EQ(2, slot2.callback_count()); + EXPECT_EQ(20, capture1); + EXPECT_EQ('y', capture2); +} + +// Since it applies for 1 and 2 args, we assume it will work for up to 5 args. + +TEST(SigslotTester, TestSignalWithConstReferenceArgs) { + sigslot::signal1 source1; + std::string capture1; + SigslotTester1 slot1(&source1, &capture1); + EXPECT_EQ(0, slot1.callback_count()); + source1.emit("hello"); + EXPECT_EQ(1, slot1.callback_count()); + EXPECT_EQ("hello", capture1); +} + +TEST(SigslotTester, TestSignalWithPointerToConstArgs) { + sigslot::signal1 source1; + const std::string* capture1; + SigslotTester1 slot1(&source1, + &capture1); + EXPECT_EQ(0, slot1.callback_count()); + source1.emit(NULL); + EXPECT_EQ(1, slot1.callback_count()); + EXPECT_EQ(NULL, capture1); +} + +TEST(SigslotTester, TestSignalWithConstPointerArgs) { + sigslot::signal1 source1; + std::string* capture1; + SigslotTester1 slot1(&source1, &capture1); + EXPECT_EQ(0, slot1.callback_count()); + source1.emit(NULL); + EXPECT_EQ(1, slot1.callback_count()); + EXPECT_EQ(NULL, capture1); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sigslot_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sigslot_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sigslot_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sigslot_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,250 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/sigslot.h" + +#include "webrtc/base/gunit.h" + +// This function, when passed a has_slots or signalx, will break the build if +// its threading requirement is not single threaded +static bool TemplateIsST(const sigslot::single_threaded* p) { + return true; +} +// This function, when passed a has_slots or signalx, will break the build if +// its threading requirement is not multi threaded +static bool TemplateIsMT(const sigslot::multi_threaded_local* p) { + return true; +} + +class SigslotDefault : public testing::Test, public sigslot::has_slots<> { + protected: + sigslot::signal0<> signal_; +}; + +template +class SigslotReceiver : public sigslot::has_slots { + public: + SigslotReceiver() : signal_(NULL), signal_count_(0) { + } + ~SigslotReceiver() { + } + + void Connect(sigslot::signal0* signal) { + if (!signal) return; + Disconnect(); + signal_ = signal; + signal->connect(this, + &SigslotReceiver::OnSignal); + } + void Disconnect() { + if (!signal_) return; + signal_->disconnect(this); + signal_ = NULL; + } + void OnSignal() { + ++signal_count_; + } + int signal_count() { return signal_count_; } + + private: + sigslot::signal0* signal_; + int signal_count_; +}; + +template +class SigslotSlotTest : public testing::Test { + protected: + SigslotSlotTest() { + mt_signal_policy mt_policy; + TemplateIsMT(&mt_policy); + } + + virtual void SetUp() { + Connect(); + } + virtual void TearDown() { + Disconnect(); + } + + void Disconnect() { + st_receiver_.Disconnect(); + mt_receiver_.Disconnect(); + } + + void Connect() { + st_receiver_.Connect(&SignalSTLoopback); + mt_receiver_.Connect(&SignalMTLoopback); + } + + int st_loop_back_count() { return st_receiver_.signal_count(); } + int mt_loop_back_count() { return mt_receiver_.signal_count(); } + + sigslot::signal0<> SignalSTLoopback; + SigslotReceiver st_receiver_; + sigslot::signal0 SignalMTLoopback; + SigslotReceiver mt_receiver_; +}; + +typedef SigslotSlotTest<> SigslotSTSlotTest; +typedef SigslotSlotTest SigslotMTSlotTest; + +class multi_threaded_local_fake : public sigslot::multi_threaded_local { + public: + multi_threaded_local_fake() : lock_count_(0), unlock_count_(0) { + } + + virtual void lock() { + ++lock_count_; + } + virtual void unlock() { + ++unlock_count_; + } + + int lock_count() { return lock_count_; } + + bool InCriticalSection() { return lock_count_ != unlock_count_; } + + protected: + int lock_count_; + int unlock_count_; +}; + +typedef SigslotSlotTest SigslotMTLockBase; + +class SigslotMTLockTest : public SigslotMTLockBase { + protected: + SigslotMTLockTest() {} + + virtual void SetUp() { + EXPECT_EQ(0, SlotLockCount()); + SigslotMTLockBase::SetUp(); + // Connects to two signals (ST and MT). However, + // SlotLockCount() only gets the count for the + // MT signal (there are two separate SigslotReceiver which + // keep track of their own count). + EXPECT_EQ(1, SlotLockCount()); + } + virtual void TearDown() { + const int previous_lock_count = SlotLockCount(); + SigslotMTLockBase::TearDown(); + // Disconnects from two signals. Note analogous to SetUp(). + EXPECT_EQ(previous_lock_count + 1, SlotLockCount()); + } + + int SlotLockCount() { return mt_receiver_.lock_count(); } + void Signal() { SignalMTLoopback(); } + int SignalLockCount() { return SignalMTLoopback.lock_count(); } + int signal_count() { return mt_loop_back_count(); } + bool InCriticalSection() { return SignalMTLoopback.InCriticalSection(); } +}; + +// This test will always succeed. However, if the default template instantiation +// changes from single threaded to multi threaded it will break the build here. +TEST_F(SigslotDefault, DefaultIsST) { + EXPECT_TRUE(TemplateIsST(this)); + EXPECT_TRUE(TemplateIsST(&signal_)); +} + +// ST slot, ST signal +TEST_F(SigslotSTSlotTest, STLoopbackTest) { + SignalSTLoopback(); + EXPECT_EQ(1, st_loop_back_count()); + EXPECT_EQ(0, mt_loop_back_count()); +} + +// ST slot, MT signal +TEST_F(SigslotSTSlotTest, MTLoopbackTest) { + SignalMTLoopback(); + EXPECT_EQ(1, mt_loop_back_count()); + EXPECT_EQ(0, st_loop_back_count()); +} + +// ST slot, both ST and MT (separate) signal +TEST_F(SigslotSTSlotTest, AllLoopbackTest) { + SignalSTLoopback(); + SignalMTLoopback(); + EXPECT_EQ(1, mt_loop_back_count()); + EXPECT_EQ(1, st_loop_back_count()); +} + +TEST_F(SigslotSTSlotTest, Reconnect) { + SignalSTLoopback(); + SignalMTLoopback(); + EXPECT_EQ(1, mt_loop_back_count()); + EXPECT_EQ(1, st_loop_back_count()); + Disconnect(); + SignalSTLoopback(); + SignalMTLoopback(); + EXPECT_EQ(1, mt_loop_back_count()); + EXPECT_EQ(1, st_loop_back_count()); + Connect(); + SignalSTLoopback(); + SignalMTLoopback(); + EXPECT_EQ(2, mt_loop_back_count()); + EXPECT_EQ(2, st_loop_back_count()); +} + +// MT slot, ST signal +TEST_F(SigslotMTSlotTest, STLoopbackTest) { + SignalSTLoopback(); + EXPECT_EQ(1, st_loop_back_count()); + EXPECT_EQ(0, mt_loop_back_count()); +} + +// MT slot, MT signal +TEST_F(SigslotMTSlotTest, MTLoopbackTest) { + SignalMTLoopback(); + EXPECT_EQ(1, mt_loop_back_count()); + EXPECT_EQ(0, st_loop_back_count()); +} + +// MT slot, both ST and MT (separate) signal +TEST_F(SigslotMTSlotTest, AllLoopbackTest) { + SignalMTLoopback(); + SignalSTLoopback(); + EXPECT_EQ(1, st_loop_back_count()); + EXPECT_EQ(1, mt_loop_back_count()); +} + +// Test that locks are acquired and released correctly. +TEST_F(SigslotMTLockTest, LockSanity) { + const int lock_count = SignalLockCount(); + Signal(); + EXPECT_FALSE(InCriticalSection()); + EXPECT_EQ(lock_count + 1, SignalLockCount()); + EXPECT_EQ(1, signal_count()); +} + +// Destroy signal and slot in different orders. +TEST(DestructionOrder, SignalFirst) { + sigslot::signal0<>* signal = new sigslot::signal0<>; + SigslotReceiver<>* receiver = new SigslotReceiver<>(); + receiver->Connect(signal); + (*signal)(); + EXPECT_EQ(1, receiver->signal_count()); + delete signal; + delete receiver; +} + +TEST(DestructionOrder, SlotFirst) { + sigslot::signal0<>* signal = new sigslot::signal0<>; + SigslotReceiver<>* receiver = new SigslotReceiver<>(); + receiver->Connect(signal); + (*signal)(); + EXPECT_EQ(1, receiver->signal_count()); + + delete receiver; + (*signal)(); + delete signal; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketadapters.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketadapters.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketadapters.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketadapters.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,893 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif + +#include +#include + +#if defined(WEBRTC_WIN) +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#define SECURITY_WIN32 +#include +#endif + +#include "webrtc/base/bytebuffer.h" +#include "webrtc/base/common.h" +#include "webrtc/base/httpcommon.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/socketadapters.h" +#include "webrtc/base/stringencode.h" +#include "webrtc/base/stringutils.h" + +#if defined(WEBRTC_WIN) +#include "webrtc/base/sec_buffer.h" +#endif // WEBRTC_WIN + +namespace rtc { + +BufferedReadAdapter::BufferedReadAdapter(AsyncSocket* socket, size_t size) + : AsyncSocketAdapter(socket), buffer_size_(size), + data_len_(0), buffering_(false) { + buffer_ = new char[buffer_size_]; +} + +BufferedReadAdapter::~BufferedReadAdapter() { + delete [] buffer_; +} + +int BufferedReadAdapter::Send(const void *pv, size_t cb) { + if (buffering_) { + // TODO: Spoof error better; Signal Writeable + socket_->SetError(EWOULDBLOCK); + return -1; + } + return AsyncSocketAdapter::Send(pv, cb); +} + +int BufferedReadAdapter::Recv(void *pv, size_t cb) { + if (buffering_) { + socket_->SetError(EWOULDBLOCK); + return -1; + } + + size_t read = 0; + + if (data_len_) { + read = _min(cb, data_len_); + memcpy(pv, buffer_, read); + data_len_ -= read; + if (data_len_ > 0) { + memmove(buffer_, buffer_ + read, data_len_); + } + pv = static_cast(pv) + read; + cb -= read; + } + + // FIX: If cb == 0, we won't generate another read event + + int res = AsyncSocketAdapter::Recv(pv, cb); + if (res < 0) + return res; + + return res + static_cast(read); +} + +void BufferedReadAdapter::BufferInput(bool on) { + buffering_ = on; +} + +void BufferedReadAdapter::OnReadEvent(AsyncSocket * socket) { + ASSERT(socket == socket_); + + if (!buffering_) { + AsyncSocketAdapter::OnReadEvent(socket); + return; + } + + if (data_len_ >= buffer_size_) { + LOG(INFO) << "Input buffer overflow"; + ASSERT(false); + data_len_ = 0; + } + + int len = socket_->Recv(buffer_ + data_len_, buffer_size_ - data_len_); + if (len < 0) { + // TODO: Do something better like forwarding the error to the user. + LOG_ERR(INFO) << "Recv"; + return; + } + + data_len_ += len; + + ProcessInput(buffer_, &data_len_); +} + +/////////////////////////////////////////////////////////////////////////////// + +// This is a SSL v2 CLIENT_HELLO message. +// TODO: Should this have a session id? The response doesn't have a +// certificate, so the hello should have a session id. +static const uint8 kSslClientHello[] = { + 0x80, 0x46, // msg len + 0x01, // CLIENT_HELLO + 0x03, 0x01, // SSL 3.1 + 0x00, 0x2d, // ciphersuite len + 0x00, 0x00, // session id len + 0x00, 0x10, // challenge len + 0x01, 0x00, 0x80, 0x03, 0x00, 0x80, 0x07, 0x00, 0xc0, // ciphersuites + 0x06, 0x00, 0x40, 0x02, 0x00, 0x80, 0x04, 0x00, 0x80, // + 0x00, 0x00, 0x04, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x0a, // + 0x00, 0xfe, 0xfe, 0x00, 0x00, 0x09, 0x00, 0x00, 0x64, // + 0x00, 0x00, 0x62, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, // + 0x1f, 0x17, 0x0c, 0xa6, 0x2f, 0x00, 0x78, 0xfc, // challenge + 0x46, 0x55, 0x2e, 0xb1, 0x83, 0x39, 0xf1, 0xea // +}; + +// This is a TLSv1 SERVER_HELLO message. +static const uint8 kSslServerHello[] = { + 0x16, // handshake message + 0x03, 0x01, // SSL 3.1 + 0x00, 0x4a, // message len + 0x02, // SERVER_HELLO + 0x00, 0x00, 0x46, // handshake len + 0x03, 0x01, // SSL 3.1 + 0x42, 0x85, 0x45, 0xa7, 0x27, 0xa9, 0x5d, 0xa0, // server random + 0xb3, 0xc5, 0xe7, 0x53, 0xda, 0x48, 0x2b, 0x3f, // + 0xc6, 0x5a, 0xca, 0x89, 0xc1, 0x58, 0x52, 0xa1, // + 0x78, 0x3c, 0x5b, 0x17, 0x46, 0x00, 0x85, 0x3f, // + 0x20, // session id len + 0x0e, 0xd3, 0x06, 0x72, 0x5b, 0x5b, 0x1b, 0x5f, // session id + 0x15, 0xac, 0x13, 0xf9, 0x88, 0x53, 0x9d, 0x9b, // + 0xe8, 0x3d, 0x7b, 0x0c, 0x30, 0x32, 0x6e, 0x38, // + 0x4d, 0xa2, 0x75, 0x57, 0x41, 0x6c, 0x34, 0x5c, // + 0x00, 0x04, // RSA/RC4-128/MD5 + 0x00 // null compression +}; + +AsyncSSLSocket::AsyncSSLSocket(AsyncSocket* socket) + : BufferedReadAdapter(socket, 1024) { +} + +int AsyncSSLSocket::Connect(const SocketAddress& addr) { + // Begin buffering before we connect, so that there isn't a race condition + // between potential senders and receiving the OnConnectEvent signal + BufferInput(true); + return BufferedReadAdapter::Connect(addr); +} + +void AsyncSSLSocket::OnConnectEvent(AsyncSocket * socket) { + ASSERT(socket == socket_); + // TODO: we could buffer output too... + VERIFY(sizeof(kSslClientHello) == + DirectSend(kSslClientHello, sizeof(kSslClientHello))); +} + +void AsyncSSLSocket::ProcessInput(char* data, size_t* len) { + if (*len < sizeof(kSslServerHello)) + return; + + if (memcmp(kSslServerHello, data, sizeof(kSslServerHello)) != 0) { + Close(); + SignalCloseEvent(this, 0); // TODO: error code? + return; + } + + *len -= sizeof(kSslServerHello); + if (*len > 0) { + memmove(data, data + sizeof(kSslServerHello), *len); + } + + bool remainder = (*len > 0); + BufferInput(false); + SignalConnectEvent(this); + + // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble + if (remainder) + SignalReadEvent(this); +} + +AsyncSSLServerSocket::AsyncSSLServerSocket(AsyncSocket* socket) + : BufferedReadAdapter(socket, 1024) { + BufferInput(true); +} + +void AsyncSSLServerSocket::ProcessInput(char* data, size_t* len) { + // We only accept client hello messages. + if (*len < sizeof(kSslClientHello)) { + return; + } + + if (memcmp(kSslClientHello, data, sizeof(kSslClientHello)) != 0) { + Close(); + SignalCloseEvent(this, 0); + return; + } + + *len -= sizeof(kSslClientHello); + + // Clients should not send more data until the handshake is completed. + ASSERT(*len == 0); + + // Send a server hello back to the client. + DirectSend(kSslServerHello, sizeof(kSslServerHello)); + + // Handshake completed for us, redirect input to our parent. + BufferInput(false); +} + +/////////////////////////////////////////////////////////////////////////////// + +AsyncHttpsProxySocket::AsyncHttpsProxySocket(AsyncSocket* socket, + const std::string& user_agent, + const SocketAddress& proxy, + const std::string& username, + const CryptString& password) + : BufferedReadAdapter(socket, 1024), proxy_(proxy), agent_(user_agent), + user_(username), pass_(password), force_connect_(false), state_(PS_ERROR), + context_(0) { +} + +AsyncHttpsProxySocket::~AsyncHttpsProxySocket() { + delete context_; +} + +int AsyncHttpsProxySocket::Connect(const SocketAddress& addr) { + int ret; + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::Connect(" + << proxy_.ToSensitiveString() << ")"; + dest_ = addr; + state_ = PS_INIT; + if (ShouldIssueConnect()) { + BufferInput(true); + } + ret = BufferedReadAdapter::Connect(proxy_); + // TODO: Set state_ appropriately if Connect fails. + return ret; +} + +SocketAddress AsyncHttpsProxySocket::GetRemoteAddress() const { + return dest_; +} + +int AsyncHttpsProxySocket::Close() { + headers_.clear(); + state_ = PS_ERROR; + dest_.Clear(); + delete context_; + context_ = NULL; + return BufferedReadAdapter::Close(); +} + +Socket::ConnState AsyncHttpsProxySocket::GetState() const { + if (state_ < PS_TUNNEL) { + return CS_CONNECTING; + } else if (state_ == PS_TUNNEL) { + return CS_CONNECTED; + } else { + return CS_CLOSED; + } +} + +void AsyncHttpsProxySocket::OnConnectEvent(AsyncSocket * socket) { + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnConnectEvent"; + if (!ShouldIssueConnect()) { + state_ = PS_TUNNEL; + BufferedReadAdapter::OnConnectEvent(socket); + return; + } + SendRequest(); +} + +void AsyncHttpsProxySocket::OnCloseEvent(AsyncSocket * socket, int err) { + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnCloseEvent(" << err << ")"; + if ((state_ == PS_WAIT_CLOSE) && (err == 0)) { + state_ = PS_ERROR; + Connect(dest_); + } else { + BufferedReadAdapter::OnCloseEvent(socket, err); + } +} + +void AsyncHttpsProxySocket::ProcessInput(char* data, size_t* len) { + size_t start = 0; + for (size_t pos = start; state_ < PS_TUNNEL && pos < *len;) { + if (state_ == PS_SKIP_BODY) { + size_t consume = _min(*len - pos, content_length_); + pos += consume; + start = pos; + content_length_ -= consume; + if (content_length_ == 0) { + EndResponse(); + } + continue; + } + + if (data[pos++] != '\n') + continue; + + size_t len = pos - start - 1; + if ((len > 0) && (data[start + len - 1] == '\r')) + --len; + + data[start + len] = 0; + ProcessLine(data + start, len); + start = pos; + } + + *len -= start; + if (*len > 0) { + memmove(data, data + start, *len); + } + + if (state_ != PS_TUNNEL) + return; + + bool remainder = (*len > 0); + BufferInput(false); + SignalConnectEvent(this); + + // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble + if (remainder) + SignalReadEvent(this); // TODO: signal this?? +} + +bool AsyncHttpsProxySocket::ShouldIssueConnect() const { + // TODO: Think about whether a more sophisticated test + // than dest port == 80 is needed. + return force_connect_ || (dest_.port() != 80); +} + +void AsyncHttpsProxySocket::SendRequest() { + std::stringstream ss; + ss << "CONNECT " << dest_.ToString() << " HTTP/1.0\r\n"; + ss << "User-Agent: " << agent_ << "\r\n"; + ss << "Host: " << dest_.HostAsURIString() << "\r\n"; + ss << "Content-Length: 0\r\n"; + ss << "Proxy-Connection: Keep-Alive\r\n"; + ss << headers_; + ss << "\r\n"; + std::string str = ss.str(); + DirectSend(str.c_str(), str.size()); + state_ = PS_LEADER; + expect_close_ = true; + content_length_ = 0; + headers_.clear(); + + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket >> " << str; +} + +void AsyncHttpsProxySocket::ProcessLine(char * data, size_t len) { + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket << " << data; + + if (len == 0) { + if (state_ == PS_TUNNEL_HEADERS) { + state_ = PS_TUNNEL; + } else if (state_ == PS_ERROR_HEADERS) { + Error(defer_error_); + return; + } else if (state_ == PS_SKIP_HEADERS) { + if (content_length_) { + state_ = PS_SKIP_BODY; + } else { + EndResponse(); + return; + } + } else { + static bool report = false; + if (!unknown_mechanisms_.empty() && !report) { + report = true; + std::string msg( + "Unable to connect to the Google Talk service due to an incompatibility " + "with your proxy.\r\nPlease help us resolve this issue by submitting the " + "following information to us using our technical issue submission form " + "at:\r\n\r\n" + "http://www.google.com/support/talk/bin/request.py\r\n\r\n" + "We apologize for the inconvenience.\r\n\r\n" + "Information to submit to Google: " + ); + //std::string msg("Please report the following information to foo@bar.com:\r\nUnknown methods: "); + msg.append(unknown_mechanisms_); +#if defined(WEBRTC_WIN) + MessageBoxA(0, msg.c_str(), "Oops!", MB_OK); +#endif +#if defined(WEBRTC_POSIX) + // TODO: Raise a signal so the UI can be separated. + LOG(LS_ERROR) << "Oops!\n\n" << msg; +#endif + } + // Unexpected end of headers + Error(0); + return; + } + } else if (state_ == PS_LEADER) { + unsigned int code; + if (sscanf(data, "HTTP/%*u.%*u %u", &code) != 1) { + Error(0); + return; + } + switch (code) { + case 200: + // connection good! + state_ = PS_TUNNEL_HEADERS; + return; +#if defined(HTTP_STATUS_PROXY_AUTH_REQ) && (HTTP_STATUS_PROXY_AUTH_REQ != 407) +#error Wrong code for HTTP_STATUS_PROXY_AUTH_REQ +#endif + case 407: // HTTP_STATUS_PROXY_AUTH_REQ + state_ = PS_AUTHENTICATE; + return; + default: + defer_error_ = 0; + state_ = PS_ERROR_HEADERS; + return; + } + } else if ((state_ == PS_AUTHENTICATE) + && (_strnicmp(data, "Proxy-Authenticate:", 19) == 0)) { + std::string response, auth_method; + switch (HttpAuthenticate(data + 19, len - 19, + proxy_, "CONNECT", "/", + user_, pass_, context_, response, auth_method)) { + case HAR_IGNORE: + LOG(LS_VERBOSE) << "Ignoring Proxy-Authenticate: " << auth_method; + if (!unknown_mechanisms_.empty()) + unknown_mechanisms_.append(", "); + unknown_mechanisms_.append(auth_method); + break; + case HAR_RESPONSE: + headers_ = "Proxy-Authorization: "; + headers_.append(response); + headers_.append("\r\n"); + state_ = PS_SKIP_HEADERS; + unknown_mechanisms_.clear(); + break; + case HAR_CREDENTIALS: + defer_error_ = SOCKET_EACCES; + state_ = PS_ERROR_HEADERS; + unknown_mechanisms_.clear(); + break; + case HAR_ERROR: + defer_error_ = 0; + state_ = PS_ERROR_HEADERS; + unknown_mechanisms_.clear(); + break; + } + } else if (_strnicmp(data, "Content-Length:", 15) == 0) { + content_length_ = strtoul(data + 15, 0, 0); + } else if (_strnicmp(data, "Proxy-Connection: Keep-Alive", 28) == 0) { + expect_close_ = false; + /* + } else if (_strnicmp(data, "Connection: close", 17) == 0) { + expect_close_ = true; + */ + } +} + +void AsyncHttpsProxySocket::EndResponse() { + if (!expect_close_) { + SendRequest(); + return; + } + + // No point in waiting for the server to close... let's close now + // TODO: Refactor out PS_WAIT_CLOSE + state_ = PS_WAIT_CLOSE; + BufferedReadAdapter::Close(); + OnCloseEvent(this, 0); +} + +void AsyncHttpsProxySocket::Error(int error) { + BufferInput(false); + Close(); + SetError(error); + SignalCloseEvent(this, error); +} + +/////////////////////////////////////////////////////////////////////////////// + +AsyncSocksProxySocket::AsyncSocksProxySocket(AsyncSocket* socket, + const SocketAddress& proxy, + const std::string& username, + const CryptString& password) + : BufferedReadAdapter(socket, 1024), state_(SS_ERROR), proxy_(proxy), + user_(username), pass_(password) { +} + +int AsyncSocksProxySocket::Connect(const SocketAddress& addr) { + int ret; + dest_ = addr; + state_ = SS_INIT; + BufferInput(true); + ret = BufferedReadAdapter::Connect(proxy_); + // TODO: Set state_ appropriately if Connect fails. + return ret; +} + +SocketAddress AsyncSocksProxySocket::GetRemoteAddress() const { + return dest_; +} + +int AsyncSocksProxySocket::Close() { + state_ = SS_ERROR; + dest_.Clear(); + return BufferedReadAdapter::Close(); +} + +Socket::ConnState AsyncSocksProxySocket::GetState() const { + if (state_ < SS_TUNNEL) { + return CS_CONNECTING; + } else if (state_ == SS_TUNNEL) { + return CS_CONNECTED; + } else { + return CS_CLOSED; + } +} + +void AsyncSocksProxySocket::OnConnectEvent(AsyncSocket* socket) { + SendHello(); +} + +void AsyncSocksProxySocket::ProcessInput(char* data, size_t* len) { + ASSERT(state_ < SS_TUNNEL); + + ByteBuffer response(data, *len); + + if (state_ == SS_HELLO) { + uint8 ver, method; + if (!response.ReadUInt8(&ver) || + !response.ReadUInt8(&method)) + return; + + if (ver != 5) { + Error(0); + return; + } + + if (method == 0) { + SendConnect(); + } else if (method == 2) { + SendAuth(); + } else { + Error(0); + return; + } + } else if (state_ == SS_AUTH) { + uint8 ver, status; + if (!response.ReadUInt8(&ver) || + !response.ReadUInt8(&status)) + return; + + if ((ver != 1) || (status != 0)) { + Error(SOCKET_EACCES); + return; + } + + SendConnect(); + } else if (state_ == SS_CONNECT) { + uint8 ver, rep, rsv, atyp; + if (!response.ReadUInt8(&ver) || + !response.ReadUInt8(&rep) || + !response.ReadUInt8(&rsv) || + !response.ReadUInt8(&atyp)) + return; + + if ((ver != 5) || (rep != 0)) { + Error(0); + return; + } + + uint16 port; + if (atyp == 1) { + uint32 addr; + if (!response.ReadUInt32(&addr) || + !response.ReadUInt16(&port)) + return; + LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port; + } else if (atyp == 3) { + uint8 len; + std::string addr; + if (!response.ReadUInt8(&len) || + !response.ReadString(&addr, len) || + !response.ReadUInt16(&port)) + return; + LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port; + } else if (atyp == 4) { + std::string addr; + if (!response.ReadString(&addr, 16) || + !response.ReadUInt16(&port)) + return; + LOG(LS_VERBOSE) << "Bound on :" << port; + } else { + Error(0); + return; + } + + state_ = SS_TUNNEL; + } + + // Consume parsed data + *len = response.Length(); + memcpy(data, response.Data(), *len); + + if (state_ != SS_TUNNEL) + return; + + bool remainder = (*len > 0); + BufferInput(false); + SignalConnectEvent(this); + + // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble + if (remainder) + SignalReadEvent(this); // TODO: signal this?? +} + +void AsyncSocksProxySocket::SendHello() { + ByteBuffer request; + request.WriteUInt8(5); // Socks Version + if (user_.empty()) { + request.WriteUInt8(1); // Authentication Mechanisms + request.WriteUInt8(0); // No authentication + } else { + request.WriteUInt8(2); // Authentication Mechanisms + request.WriteUInt8(0); // No authentication + request.WriteUInt8(2); // Username/Password + } + DirectSend(request.Data(), request.Length()); + state_ = SS_HELLO; +} + +void AsyncSocksProxySocket::SendAuth() { + ByteBuffer request; + request.WriteUInt8(1); // Negotiation Version + request.WriteUInt8(static_cast(user_.size())); + request.WriteString(user_); // Username + request.WriteUInt8(static_cast(pass_.GetLength())); + size_t len = pass_.GetLength() + 1; + char * sensitive = new char[len]; + pass_.CopyTo(sensitive, true); + request.WriteString(sensitive); // Password + memset(sensitive, 0, len); + delete [] sensitive; + DirectSend(request.Data(), request.Length()); + state_ = SS_AUTH; +} + +void AsyncSocksProxySocket::SendConnect() { + ByteBuffer request; + request.WriteUInt8(5); // Socks Version + request.WriteUInt8(1); // CONNECT + request.WriteUInt8(0); // Reserved + if (dest_.IsUnresolved()) { + std::string hostname = dest_.hostname(); + request.WriteUInt8(3); // DOMAINNAME + request.WriteUInt8(static_cast(hostname.size())); + request.WriteString(hostname); // Destination Hostname + } else { + request.WriteUInt8(1); // IPV4 + request.WriteUInt32(dest_.ip()); // Destination IP + } + request.WriteUInt16(dest_.port()); // Destination Port + DirectSend(request.Data(), request.Length()); + state_ = SS_CONNECT; +} + +void AsyncSocksProxySocket::Error(int error) { + state_ = SS_ERROR; + BufferInput(false); + Close(); + SetError(SOCKET_EACCES); + SignalCloseEvent(this, error); +} + +AsyncSocksProxyServerSocket::AsyncSocksProxyServerSocket(AsyncSocket* socket) + : AsyncProxyServerSocket(socket, kBufferSize), state_(SS_HELLO) { + BufferInput(true); +} + +void AsyncSocksProxyServerSocket::ProcessInput(char* data, size_t* len) { + // TODO: See if the whole message has arrived + ASSERT(state_ < SS_CONNECT_PENDING); + + ByteBuffer response(data, *len); + if (state_ == SS_HELLO) { + HandleHello(&response); + } else if (state_ == SS_AUTH) { + HandleAuth(&response); + } else if (state_ == SS_CONNECT) { + HandleConnect(&response); + } + + // Consume parsed data + *len = response.Length(); + memcpy(data, response.Data(), *len); +} + +void AsyncSocksProxyServerSocket::DirectSend(const ByteBuffer& buf) { + BufferedReadAdapter::DirectSend(buf.Data(), buf.Length()); +} + +void AsyncSocksProxyServerSocket::HandleHello(ByteBuffer* request) { + uint8 ver, num_methods; + if (!request->ReadUInt8(&ver) || + !request->ReadUInt8(&num_methods)) { + Error(0); + return; + } + + if (ver != 5) { + Error(0); + return; + } + + // Handle either no-auth (0) or user/pass auth (2) + uint8 method = 0xFF; + if (num_methods > 0 && !request->ReadUInt8(&method)) { + Error(0); + return; + } + + // TODO: Ask the server which method to use. + SendHelloReply(method); + if (method == 0) { + state_ = SS_CONNECT; + } else if (method == 2) { + state_ = SS_AUTH; + } else { + state_ = SS_ERROR; + } +} + +void AsyncSocksProxyServerSocket::SendHelloReply(uint8 method) { + ByteBuffer response; + response.WriteUInt8(5); // Socks Version + response.WriteUInt8(method); // Auth method + DirectSend(response); +} + +void AsyncSocksProxyServerSocket::HandleAuth(ByteBuffer* request) { + uint8 ver, user_len, pass_len; + std::string user, pass; + if (!request->ReadUInt8(&ver) || + !request->ReadUInt8(&user_len) || + !request->ReadString(&user, user_len) || + !request->ReadUInt8(&pass_len) || + !request->ReadString(&pass, pass_len)) { + Error(0); + return; + } + + // TODO: Allow for checking of credentials. + SendAuthReply(0); + state_ = SS_CONNECT; +} + +void AsyncSocksProxyServerSocket::SendAuthReply(uint8 result) { + ByteBuffer response; + response.WriteUInt8(1); // Negotiation Version + response.WriteUInt8(result); + DirectSend(response); +} + +void AsyncSocksProxyServerSocket::HandleConnect(ByteBuffer* request) { + uint8 ver, command, reserved, addr_type; + uint32 ip; + uint16 port; + if (!request->ReadUInt8(&ver) || + !request->ReadUInt8(&command) || + !request->ReadUInt8(&reserved) || + !request->ReadUInt8(&addr_type) || + !request->ReadUInt32(&ip) || + !request->ReadUInt16(&port)) { + Error(0); + return; + } + + if (ver != 5 || command != 1 || + reserved != 0 || addr_type != 1) { + Error(0); + return; + } + + SignalConnectRequest(this, SocketAddress(ip, port)); + state_ = SS_CONNECT_PENDING; +} + +void AsyncSocksProxyServerSocket::SendConnectResult(int result, + const SocketAddress& addr) { + if (state_ != SS_CONNECT_PENDING) + return; + + ByteBuffer response; + response.WriteUInt8(5); // Socks version + response.WriteUInt8((result != 0)); // 0x01 is generic error + response.WriteUInt8(0); // reserved + response.WriteUInt8(1); // IPv4 address + response.WriteUInt32(addr.ip()); + response.WriteUInt16(addr.port()); + DirectSend(response); + BufferInput(false); + state_ = SS_TUNNEL; +} + +void AsyncSocksProxyServerSocket::Error(int error) { + state_ = SS_ERROR; + BufferInput(false); + Close(); + SetError(SOCKET_EACCES); + SignalCloseEvent(this, error); +} + +/////////////////////////////////////////////////////////////////////////////// + +LoggingSocketAdapter::LoggingSocketAdapter(AsyncSocket* socket, + LoggingSeverity level, + const char * label, bool hex_mode) + : AsyncSocketAdapter(socket), level_(level), hex_mode_(hex_mode) { + label_.append("["); + label_.append(label); + label_.append("]"); +} + +int LoggingSocketAdapter::Send(const void *pv, size_t cb) { + int res = AsyncSocketAdapter::Send(pv, cb); + if (res > 0) + LogMultiline(level_, label_.c_str(), false, pv, res, hex_mode_, &lms_); + return res; +} + +int LoggingSocketAdapter::SendTo(const void *pv, size_t cb, + const SocketAddress& addr) { + int res = AsyncSocketAdapter::SendTo(pv, cb, addr); + if (res > 0) + LogMultiline(level_, label_.c_str(), false, pv, res, hex_mode_, &lms_); + return res; +} + +int LoggingSocketAdapter::Recv(void *pv, size_t cb) { + int res = AsyncSocketAdapter::Recv(pv, cb); + if (res > 0) + LogMultiline(level_, label_.c_str(), true, pv, res, hex_mode_, &lms_); + return res; +} + +int LoggingSocketAdapter::RecvFrom(void *pv, size_t cb, SocketAddress *paddr) { + int res = AsyncSocketAdapter::RecvFrom(pv, cb, paddr); + if (res > 0) + LogMultiline(level_, label_.c_str(), true, pv, res, hex_mode_, &lms_); + return res; +} + +int LoggingSocketAdapter::Close() { + LogMultiline(level_, label_.c_str(), false, NULL, 0, hex_mode_, &lms_); + LogMultiline(level_, label_.c_str(), true, NULL, 0, hex_mode_, &lms_); + LOG_V(level_) << label_ << " Closed locally"; + return socket_->Close(); +} + +void LoggingSocketAdapter::OnConnectEvent(AsyncSocket * socket) { + LOG_V(level_) << label_ << " Connected"; + AsyncSocketAdapter::OnConnectEvent(socket); +} + +void LoggingSocketAdapter::OnCloseEvent(AsyncSocket * socket, int err) { + LogMultiline(level_, label_.c_str(), false, NULL, 0, hex_mode_, &lms_); + LogMultiline(level_, label_.c_str(), true, NULL, 0, hex_mode_, &lms_); + LOG_V(level_) << label_ << " Closed with error: " << err; + AsyncSocketAdapter::OnCloseEvent(socket, err); +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketadapters.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketadapters.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketadapters.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketadapters.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,244 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SOCKETADAPTERS_H_ +#define WEBRTC_BASE_SOCKETADAPTERS_H_ + +#include +#include + +#include "webrtc/base/asyncsocket.h" +#include "webrtc/base/cryptstring.h" +#include "webrtc/base/logging.h" + +namespace rtc { + +struct HttpAuthContext; +class ByteBuffer; + +/////////////////////////////////////////////////////////////////////////////// + +// Implements a socket adapter that can buffer and process data internally, +// as in the case of connecting to a proxy, where you must speak the proxy +// protocol before commencing normal socket behavior. +class BufferedReadAdapter : public AsyncSocketAdapter { + public: + BufferedReadAdapter(AsyncSocket* socket, size_t buffer_size); + virtual ~BufferedReadAdapter(); + + virtual int Send(const void* pv, size_t cb); + virtual int Recv(void* pv, size_t cb); + + protected: + int DirectSend(const void* pv, size_t cb) { + return AsyncSocketAdapter::Send(pv, cb); + } + + void BufferInput(bool on = true); + virtual void ProcessInput(char* data, size_t* len) = 0; + + virtual void OnReadEvent(AsyncSocket * socket); + + private: + char * buffer_; + size_t buffer_size_, data_len_; + bool buffering_; + DISALLOW_EVIL_CONSTRUCTORS(BufferedReadAdapter); +}; + +/////////////////////////////////////////////////////////////////////////////// + +// Interface for implementing proxy server sockets. +class AsyncProxyServerSocket : public BufferedReadAdapter { + public: + AsyncProxyServerSocket(AsyncSocket* socket, size_t buffer_size) + : BufferedReadAdapter(socket, buffer_size) {} + sigslot::signal2 SignalConnectRequest; + virtual void SendConnectResult(int err, const SocketAddress& addr) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// + +// Implements a socket adapter that performs the client side of a +// fake SSL handshake. Used for "ssltcp" P2P functionality. +class AsyncSSLSocket : public BufferedReadAdapter { + public: + explicit AsyncSSLSocket(AsyncSocket* socket); + + virtual int Connect(const SocketAddress& addr); + + protected: + virtual void OnConnectEvent(AsyncSocket* socket); + virtual void ProcessInput(char* data, size_t* len); + DISALLOW_EVIL_CONSTRUCTORS(AsyncSSLSocket); +}; + +// Implements a socket adapter that performs the server side of a +// fake SSL handshake. Used when implementing a relay server that does "ssltcp". +class AsyncSSLServerSocket : public BufferedReadAdapter { + public: + explicit AsyncSSLServerSocket(AsyncSocket* socket); + + protected: + virtual void ProcessInput(char* data, size_t* len); + DISALLOW_EVIL_CONSTRUCTORS(AsyncSSLServerSocket); +}; + +/////////////////////////////////////////////////////////////////////////////// + +// Implements a socket adapter that speaks the HTTP/S proxy protocol. +class AsyncHttpsProxySocket : public BufferedReadAdapter { + public: + AsyncHttpsProxySocket(AsyncSocket* socket, const std::string& user_agent, + const SocketAddress& proxy, + const std::string& username, const CryptString& password); + virtual ~AsyncHttpsProxySocket(); + + // If connect is forced, the adapter will always issue an HTTP CONNECT to the + // target address. Otherwise, it will connect only if the destination port + // is not port 80. + void SetForceConnect(bool force) { force_connect_ = force; } + + virtual int Connect(const SocketAddress& addr); + virtual SocketAddress GetRemoteAddress() const; + virtual int Close(); + virtual ConnState GetState() const; + + protected: + virtual void OnConnectEvent(AsyncSocket* socket); + virtual void OnCloseEvent(AsyncSocket* socket, int err); + virtual void ProcessInput(char* data, size_t* len); + + bool ShouldIssueConnect() const; + void SendRequest(); + void ProcessLine(char* data, size_t len); + void EndResponse(); + void Error(int error); + + private: + SocketAddress proxy_, dest_; + std::string agent_, user_, headers_; + CryptString pass_; + bool force_connect_; + size_t content_length_; + int defer_error_; + bool expect_close_; + enum ProxyState { + PS_INIT, PS_LEADER, PS_AUTHENTICATE, PS_SKIP_HEADERS, PS_ERROR_HEADERS, + PS_TUNNEL_HEADERS, PS_SKIP_BODY, PS_TUNNEL, PS_WAIT_CLOSE, PS_ERROR + } state_; + HttpAuthContext * context_; + std::string unknown_mechanisms_; + DISALLOW_EVIL_CONSTRUCTORS(AsyncHttpsProxySocket); +}; + +/* TODO: Implement this. +class AsyncHttpsProxyServerSocket : public AsyncProxyServerSocket { + public: + explicit AsyncHttpsProxyServerSocket(AsyncSocket* socket); + + private: + virtual void ProcessInput(char * data, size_t& len); + void Error(int error); + DISALLOW_EVIL_CONSTRUCTORS(AsyncHttpsProxyServerSocket); +}; +*/ + +/////////////////////////////////////////////////////////////////////////////// + +// Implements a socket adapter that speaks the SOCKS proxy protocol. +class AsyncSocksProxySocket : public BufferedReadAdapter { + public: + AsyncSocksProxySocket(AsyncSocket* socket, const SocketAddress& proxy, + const std::string& username, const CryptString& password); + + virtual int Connect(const SocketAddress& addr); + virtual SocketAddress GetRemoteAddress() const; + virtual int Close(); + virtual ConnState GetState() const; + + protected: + virtual void OnConnectEvent(AsyncSocket* socket); + virtual void ProcessInput(char* data, size_t* len); + + void SendHello(); + void SendConnect(); + void SendAuth(); + void Error(int error); + + private: + enum State { + SS_INIT, SS_HELLO, SS_AUTH, SS_CONNECT, SS_TUNNEL, SS_ERROR + }; + State state_; + SocketAddress proxy_, dest_; + std::string user_; + CryptString pass_; + DISALLOW_EVIL_CONSTRUCTORS(AsyncSocksProxySocket); +}; + +// Implements a proxy server socket for the SOCKS protocol. +class AsyncSocksProxyServerSocket : public AsyncProxyServerSocket { + public: + explicit AsyncSocksProxyServerSocket(AsyncSocket* socket); + + private: + virtual void ProcessInput(char* data, size_t* len); + void DirectSend(const ByteBuffer& buf); + + void HandleHello(ByteBuffer* request); + void SendHelloReply(uint8 method); + void HandleAuth(ByteBuffer* request); + void SendAuthReply(uint8 result); + void HandleConnect(ByteBuffer* request); + virtual void SendConnectResult(int result, const SocketAddress& addr); + + void Error(int error); + + static const int kBufferSize = 1024; + enum State { + SS_HELLO, SS_AUTH, SS_CONNECT, SS_CONNECT_PENDING, SS_TUNNEL, SS_ERROR + }; + State state_; + DISALLOW_EVIL_CONSTRUCTORS(AsyncSocksProxyServerSocket); +}; + +/////////////////////////////////////////////////////////////////////////////// + +// Implements a socket adapter that logs everything that it sends and receives. +class LoggingSocketAdapter : public AsyncSocketAdapter { + public: + LoggingSocketAdapter(AsyncSocket* socket, LoggingSeverity level, + const char * label, bool hex_mode = false); + + virtual int Send(const void *pv, size_t cb); + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr); + virtual int Recv(void *pv, size_t cb); + virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr); + virtual int Close(); + + protected: + virtual void OnConnectEvent(AsyncSocket * socket); + virtual void OnCloseEvent(AsyncSocket * socket, int err); + + private: + LoggingSeverity level_; + std::string label_; + bool hex_mode_; + LogMultilineState lms_; + DISALLOW_EVIL_CONSTRUCTORS(LoggingSocketAdapter); +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_SOCKETADAPTERS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketaddress.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketaddress.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketaddress.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketaddress.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,378 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/socketaddress.h" + +#if defined(WEBRTC_POSIX) +#include +#include +#include +#if defined(OPENBSD) +#include +#endif +#if !defined(__native_client__) +#include +#endif +#include +#include +#include +#endif + +#include + +#include "webrtc/base/byteorder.h" +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/nethelpers.h" + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32.h" +#endif + +namespace rtc { + +SocketAddress::SocketAddress() { + Clear(); +} + +SocketAddress::SocketAddress(const std::string& hostname, int port) { + SetIP(hostname); + SetPort(port); +} + +SocketAddress::SocketAddress(uint32 ip_as_host_order_integer, int port) { + SetIP(IPAddress(ip_as_host_order_integer)); + SetPort(port); +} + +SocketAddress::SocketAddress(const IPAddress& ip, int port) { + SetIP(ip); + SetPort(port); +} + +SocketAddress::SocketAddress(const SocketAddress& addr) { + this->operator=(addr); +} + +void SocketAddress::Clear() { + hostname_.clear(); + literal_ = false; + ip_ = IPAddress(); + port_ = 0; + scope_id_ = 0; +} + +bool SocketAddress::IsNil() const { + return hostname_.empty() && IPIsUnspec(ip_) && 0 == port_; +} + +bool SocketAddress::IsComplete() const { + return (!IPIsAny(ip_)) && (0 != port_); +} + +SocketAddress& SocketAddress::operator=(const SocketAddress& addr) { + hostname_ = addr.hostname_; + ip_ = addr.ip_; + port_ = addr.port_; + literal_ = addr.literal_; + scope_id_ = addr.scope_id_; + return *this; +} + +void SocketAddress::SetIP(uint32 ip_as_host_order_integer) { + hostname_.clear(); + literal_ = false; + ip_ = IPAddress(ip_as_host_order_integer); + scope_id_ = 0; +} + +void SocketAddress::SetIP(const IPAddress& ip) { + hostname_.clear(); + literal_ = false; + ip_ = ip; + scope_id_ = 0; +} + +void SocketAddress::SetIP(const std::string& hostname) { + hostname_ = hostname; + literal_ = IPFromString(hostname, &ip_); + if (!literal_) { + ip_ = IPAddress(); + } + scope_id_ = 0; +} + +void SocketAddress::SetResolvedIP(uint32 ip_as_host_order_integer) { + ip_ = IPAddress(ip_as_host_order_integer); + scope_id_ = 0; +} + +void SocketAddress::SetResolvedIP(const IPAddress& ip) { + ip_ = ip; + scope_id_ = 0; +} + +void SocketAddress::SetPort(int port) { + ASSERT((0 <= port) && (port < 65536)); + port_ = static_cast(port); +} + +uint32 SocketAddress::ip() const { + return ip_.v4AddressAsHostOrderInteger(); +} + +const IPAddress& SocketAddress::ipaddr() const { + return ip_; +} + +uint16 SocketAddress::port() const { + return port_; +} + +std::string SocketAddress::HostAsURIString() const { + // If the hostname was a literal IP string, it may need to have square + // brackets added (for SocketAddress::ToString()). + if (!literal_ && !hostname_.empty()) + return hostname_; + if (ip_.family() == AF_INET6) { + return "[" + ip_.ToString() + "]"; + } else { + return ip_.ToString(); + } +} + +std::string SocketAddress::HostAsSensitiveURIString() const { + // If the hostname was a literal IP string, it may need to have square + // brackets added (for SocketAddress::ToString()). + if (!literal_ && !hostname_.empty()) + return hostname_; + if (ip_.family() == AF_INET6) { + return "[" + ip_.ToSensitiveString() + "]"; + } else { + return ip_.ToSensitiveString(); + } +} + +std::string SocketAddress::PortAsString() const { + std::ostringstream ost; + ost << port_; + return ost.str(); +} + +std::string SocketAddress::ToString() const { + std::ostringstream ost; + ost << *this; + return ost.str(); +} + +std::string SocketAddress::ToSensitiveString() const { + std::ostringstream ost; + ost << HostAsSensitiveURIString() << ":" << port(); + return ost.str(); +} + +bool SocketAddress::FromString(const std::string& str) { + if (str.at(0) == '[') { + std::string::size_type closebracket = str.rfind(']'); + if (closebracket != std::string::npos) { + std::string::size_type colon = str.find(':', closebracket); + if (colon != std::string::npos && colon > closebracket) { + SetPort(strtoul(str.substr(colon + 1).c_str(), NULL, 10)); + SetIP(str.substr(1, closebracket - 1)); + } else { + return false; + } + } + } else { + std::string::size_type pos = str.find(':'); + if (std::string::npos == pos) + return false; + SetPort(strtoul(str.substr(pos + 1).c_str(), NULL, 10)); + SetIP(str.substr(0, pos)); + } + return true; +} + +std::ostream& operator<<(std::ostream& os, const SocketAddress& addr) { + os << addr.HostAsURIString() << ":" << addr.port(); + return os; +} + +bool SocketAddress::IsAnyIP() const { + return IPIsAny(ip_); +} + +bool SocketAddress::IsLoopbackIP() const { + return IPIsLoopback(ip_) || (IPIsAny(ip_) && + 0 == strcmp(hostname_.c_str(), "localhost")); +} + +bool SocketAddress::IsPrivateIP() const { + return IPIsPrivate(ip_); +} + +bool SocketAddress::IsUnresolvedIP() const { + return IPIsUnspec(ip_) && !literal_ && !hostname_.empty(); +} + +bool SocketAddress::operator==(const SocketAddress& addr) const { + return EqualIPs(addr) && EqualPorts(addr); +} + +bool SocketAddress::operator<(const SocketAddress& addr) const { + if (ip_ != addr.ip_) + return ip_ < addr.ip_; + + // We only check hostnames if both IPs are ANY or unspecified. This matches + // EqualIPs(). + if ((IPIsAny(ip_) || IPIsUnspec(ip_)) && hostname_ != addr.hostname_) + return hostname_ < addr.hostname_; + + return port_ < addr.port_; +} + +bool SocketAddress::EqualIPs(const SocketAddress& addr) const { + return (ip_ == addr.ip_) && + ((!IPIsAny(ip_) && !IPIsUnspec(ip_)) || (hostname_ == addr.hostname_)); +} + +bool SocketAddress::EqualPorts(const SocketAddress& addr) const { + return (port_ == addr.port_); +} + +size_t SocketAddress::Hash() const { + size_t h = 0; + h ^= HashIP(ip_); + h ^= port_ | (port_ << 16); + return h; +} + +void SocketAddress::ToSockAddr(sockaddr_in* saddr) const { + memset(saddr, 0, sizeof(*saddr)); + if (ip_.family() != AF_INET) { + saddr->sin_family = AF_UNSPEC; + return; + } + saddr->sin_family = AF_INET; + saddr->sin_port = HostToNetwork16(port_); + if (IPIsAny(ip_)) { + saddr->sin_addr.s_addr = INADDR_ANY; + } else { + saddr->sin_addr = ip_.ipv4_address(); + } +} + +bool SocketAddress::FromSockAddr(const sockaddr_in& saddr) { + if (saddr.sin_family != AF_INET) + return false; + SetIP(NetworkToHost32(saddr.sin_addr.s_addr)); + SetPort(NetworkToHost16(saddr.sin_port)); + literal_ = false; + return true; +} + +static size_t ToSockAddrStorageHelper(sockaddr_storage* addr, + IPAddress ip, uint16 port, int scope_id) { + memset(addr, 0, sizeof(sockaddr_storage)); + addr->ss_family = static_cast(ip.family()); + if (addr->ss_family == AF_INET6) { + sockaddr_in6* saddr = reinterpret_cast(addr); + saddr->sin6_addr = ip.ipv6_address(); + saddr->sin6_port = HostToNetwork16(port); + saddr->sin6_scope_id = scope_id; + return sizeof(sockaddr_in6); + } else if (addr->ss_family == AF_INET) { + sockaddr_in* saddr = reinterpret_cast(addr); + saddr->sin_addr = ip.ipv4_address(); + saddr->sin_port = HostToNetwork16(port); + return sizeof(sockaddr_in); + } + return 0; +} + +size_t SocketAddress::ToDualStackSockAddrStorage(sockaddr_storage *addr) const { + return ToSockAddrStorageHelper(addr, ip_.AsIPv6Address(), port_, scope_id_); +} + +size_t SocketAddress::ToSockAddrStorage(sockaddr_storage* addr) const { + return ToSockAddrStorageHelper(addr, ip_, port_, scope_id_); +} + +std::string SocketAddress::IPToString(uint32 ip_as_host_order_integer) { + return IPAddress(ip_as_host_order_integer).ToString(); +} + +std::string IPToSensitiveString(uint32 ip_as_host_order_integer) { + return IPAddress(ip_as_host_order_integer).ToSensitiveString(); +} + +bool SocketAddress::StringToIP(const std::string& hostname, uint32* ip) { + in_addr addr; + if (rtc::inet_pton(AF_INET, hostname.c_str(), &addr) == 0) + return false; + *ip = NetworkToHost32(addr.s_addr); + return true; +} + +bool SocketAddress::StringToIP(const std::string& hostname, IPAddress* ip) { + in_addr addr4; + if (rtc::inet_pton(AF_INET, hostname.c_str(), &addr4) > 0) { + if (ip) { + *ip = IPAddress(addr4); + } + return true; + } + + in6_addr addr6; + if (rtc::inet_pton(AF_INET6, hostname.c_str(), &addr6) > 0) { + if (ip) { + *ip = IPAddress(addr6); + } + return true; + } + return false; +} + +uint32 SocketAddress::StringToIP(const std::string& hostname) { + uint32 ip = 0; + StringToIP(hostname, &ip); + return ip; +} + +bool SocketAddressFromSockAddrStorage(const sockaddr_storage& addr, + SocketAddress* out) { + if (!out) { + return false; + } + if (addr.ss_family == AF_INET) { + const sockaddr_in* saddr = reinterpret_cast(&addr); + *out = SocketAddress(IPAddress(saddr->sin_addr), + NetworkToHost16(saddr->sin_port)); + return true; + } else if (addr.ss_family == AF_INET6) { + const sockaddr_in6* saddr = reinterpret_cast(&addr); + *out = SocketAddress(IPAddress(saddr->sin6_addr), + NetworkToHost16(saddr->sin6_port)); + out->SetScopeID(saddr->sin6_scope_id); + return true; + } + return false; +} + +SocketAddress EmptySocketAddressWithFamily(int family) { + if (family == AF_INET) { + return SocketAddress(IPAddress(INADDR_ANY), 0); + } else if (family == AF_INET6) { + return SocketAddress(IPAddress(in6addr_any), 0); + } + return SocketAddress(); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketaddress.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketaddress.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketaddress.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketaddress.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,214 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SOCKETADDRESS_H_ +#define WEBRTC_BASE_SOCKETADDRESS_H_ + +#include +#include +#include +#include "webrtc/base/basictypes.h" +#include "webrtc/base/ipaddress.h" + +#undef SetPort + +struct sockaddr_in; +struct sockaddr_storage; + +namespace rtc { + +// Records an IP address and port. +class SocketAddress { + public: + // Creates a nil address. + SocketAddress(); + + // Creates the address with the given host and port. Host may be a + // literal IP string or a hostname to be resolved later. + SocketAddress(const std::string& hostname, int port); + + // Creates the address with the given IP and port. + // IP is given as an integer in host byte order. V4 only, to be deprecated. + SocketAddress(uint32 ip_as_host_order_integer, int port); + + // Creates the address with the given IP and port. + SocketAddress(const IPAddress& ip, int port); + + // Creates a copy of the given address. + SocketAddress(const SocketAddress& addr); + + // Resets to the nil address. + void Clear(); + + // Determines if this is a nil address (empty hostname, any IP, null port) + bool IsNil() const; + + // Returns true if ip and port are set. + bool IsComplete() const; + + // Replaces our address with the given one. + SocketAddress& operator=(const SocketAddress& addr); + + // Changes the IP of this address to the given one, and clears the hostname + // IP is given as an integer in host byte order. V4 only, to be deprecated.. + void SetIP(uint32 ip_as_host_order_integer); + + // Changes the IP of this address to the given one, and clears the hostname. + void SetIP(const IPAddress& ip); + + // Changes the hostname of this address to the given one. + // Does not resolve the address; use Resolve to do so. + void SetIP(const std::string& hostname); + + // Sets the IP address while retaining the hostname. Useful for bypassing + // DNS for a pre-resolved IP. + // IP is given as an integer in host byte order. V4 only, to be deprecated. + void SetResolvedIP(uint32 ip_as_host_order_integer); + + // Sets the IP address while retaining the hostname. Useful for bypassing + // DNS for a pre-resolved IP. + void SetResolvedIP(const IPAddress& ip); + + // Changes the port of this address to the given one. + void SetPort(int port); + + // Returns the hostname. + const std::string& hostname() const { return hostname_; } + + // Returns the IP address as a host byte order integer. + // Returns 0 for non-v4 addresses. + uint32 ip() const; + + const IPAddress& ipaddr() const; + + int family() const {return ip_.family(); } + + // Returns the port part of this address. + uint16 port() const; + + // Returns the scope ID associated with this address. Scope IDs are a + // necessary addition to IPv6 link-local addresses, with different network + // interfaces having different scope-ids for their link-local addresses. + // IPv4 address do not have scope_ids and sockaddr_in structures do not have + // a field for them. + int scope_id() const {return scope_id_; } + void SetScopeID(int id) { scope_id_ = id; } + + // Returns the 'host' portion of the address (hostname or IP) in a form + // suitable for use in a URI. If both IP and hostname are present, hostname + // is preferred. IPv6 addresses are enclosed in square brackets ('[' and ']'). + std::string HostAsURIString() const; + + // Same as HostAsURIString but anonymizes IP addresses by hiding the last + // part. + std::string HostAsSensitiveURIString() const; + + // Returns the port as a string. + std::string PortAsString() const; + + // Returns hostname:port or [hostname]:port. + std::string ToString() const; + + // Same as ToString but anonymizes it by hiding the last part. + std::string ToSensitiveString() const; + + // Parses hostname:port and [hostname]:port. + bool FromString(const std::string& str); + + friend std::ostream& operator<<(std::ostream& os, const SocketAddress& addr); + + // Determines whether this represents a missing / any IP address. + // That is, 0.0.0.0 or ::. + // Hostname and/or port may be set. + bool IsAnyIP() const; + inline bool IsAny() const { return IsAnyIP(); } // deprecated + + // Determines whether the IP address refers to a loopback address. + // For v4 addresses this means the address is in the range 127.0.0.0/8. + // For v6 addresses this means the address is ::1. + bool IsLoopbackIP() const; + + // Determines whether the IP address is in one of the private ranges: + // For v4: 127.0.0.0/8 10.0.0.0/8 192.168.0.0/16 172.16.0.0/12. + // For v6: FE80::/16 and ::1. + bool IsPrivateIP() const; + + // Determines whether the hostname has been resolved to an IP. + bool IsUnresolvedIP() const; + inline bool IsUnresolved() const { return IsUnresolvedIP(); } // deprecated + + // Determines whether this address is identical to the given one. + bool operator ==(const SocketAddress& addr) const; + inline bool operator !=(const SocketAddress& addr) const { + return !this->operator ==(addr); + } + + // Compares based on IP and then port. + bool operator <(const SocketAddress& addr) const; + + // Determines whether this address has the same IP as the one given. + bool EqualIPs(const SocketAddress& addr) const; + + // Determines whether this address has the same port as the one given. + bool EqualPorts(const SocketAddress& addr) const; + + // Hashes this address into a small number. + size_t Hash() const; + + // Write this address to a sockaddr_in. + // If IPv6, will zero out the sockaddr_in and sets family to AF_UNSPEC. + void ToSockAddr(sockaddr_in* saddr) const; + + // Read this address from a sockaddr_in. + bool FromSockAddr(const sockaddr_in& saddr); + + // Read and write the address to/from a sockaddr_storage. + // Dual stack version always sets family to AF_INET6, and maps v4 addresses. + // The other version doesn't map, and outputs an AF_INET address for + // v4 or mapped addresses, and AF_INET6 addresses for others. + // Returns the size of the sockaddr_in or sockaddr_in6 structure that is + // written to the sockaddr_storage, or zero on failure. + size_t ToDualStackSockAddrStorage(sockaddr_storage* saddr) const; + size_t ToSockAddrStorage(sockaddr_storage* saddr) const; + + // Converts the IP address given in 'compact form' into dotted form. + // IP is given as an integer in host byte order. V4 only, to be deprecated. + // TODO: Deprecate this. + static std::string IPToString(uint32 ip_as_host_order_integer); + + // Same as IPToString but anonymizes it by hiding the last part. + // TODO: Deprecate this. + static std::string IPToSensitiveString(uint32 ip_as_host_order_integer); + + // Converts the IP address given in dotted form into compact form. + // Only dotted names (A.B.C.D) are converted. + // Output integer is returned in host byte order. + // TODO: Deprecate, replace wth agnostic versions. + static bool StringToIP(const std::string& str, uint32* ip); + static uint32 StringToIP(const std::string& str); + + // Converts the IP address given in printable form into an IPAddress. + static bool StringToIP(const std::string& str, IPAddress* ip); + + private: + std::string hostname_; + IPAddress ip_; + uint16 port_; + int scope_id_; + bool literal_; // Indicates that 'hostname_' contains a literal IP string. +}; + +bool SocketAddressFromSockAddrStorage(const sockaddr_storage& saddr, + SocketAddress* out); +SocketAddress EmptySocketAddressWithFamily(int family); + +} // namespace rtc + +#endif // WEBRTC_BASE_SOCKETADDRESS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketaddresspair.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketaddresspair.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketaddresspair.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketaddresspair.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,41 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/socketaddresspair.h" + +namespace rtc { + +SocketAddressPair::SocketAddressPair( + const SocketAddress& src, const SocketAddress& dest) + : src_(src), dest_(dest) { +} + + +bool SocketAddressPair::operator ==(const SocketAddressPair& p) const { + return (src_ == p.src_) && (dest_ == p.dest_); +} + +bool SocketAddressPair::operator <(const SocketAddressPair& p) const { + if (src_ < p.src_) + return true; + if (p.src_ < src_) + return false; + if (dest_ < p.dest_) + return true; + if (p.dest_ < dest_) + return false; + return false; +} + +size_t SocketAddressPair::Hash() const { + return src_.Hash() ^ dest_.Hash(); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketaddresspair.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketaddresspair.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketaddresspair.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketaddresspair.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,41 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SOCKETADDRESSPAIR_H__ +#define WEBRTC_BASE_SOCKETADDRESSPAIR_H__ + +#include "webrtc/base/socketaddress.h" + +namespace rtc { + +// Records a pair (source,destination) of socket addresses. The two addresses +// identify a connection between two machines. (For UDP, this "connection" is +// not maintained explicitly in a socket.) +class SocketAddressPair { +public: + SocketAddressPair() {} + SocketAddressPair(const SocketAddress& srs, const SocketAddress& dest); + + const SocketAddress& source() const { return src_; } + const SocketAddress& destination() const { return dest_; } + + bool operator ==(const SocketAddressPair& r) const; + bool operator <(const SocketAddressPair& r) const; + + size_t Hash() const; + +private: + SocketAddress src_; + SocketAddress dest_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_SOCKETADDRESSPAIR_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketaddress_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketaddress_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketaddress_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketaddress_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,347 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if defined(WEBRTC_POSIX) +#include // for sockaddr_in +#endif + +#include "webrtc/base/gunit.h" +#include "webrtc/base/socketaddress.h" +#include "webrtc/base/ipaddress.h" + +namespace rtc { + +const in6_addr kTestV6Addr = { { {0x20, 0x01, 0x0d, 0xb8, + 0x10, 0x20, 0x30, 0x40, + 0x50, 0x60, 0x70, 0x80, + 0x90, 0xA0, 0xB0, 0xC0} } }; +const in6_addr kMappedV4Addr = { { {0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, + 0x01, 0x02, 0x03, 0x04} } }; +const std::string kTestV6AddrString = "2001:db8:1020:3040:5060:7080:90a0:b0c0"; +const std::string kTestV6AddrAnonymizedString = "2001:db8:1020::"; +const std::string kTestV6AddrFullString = + "[2001:db8:1020:3040:5060:7080:90a0:b0c0]:5678"; +const std::string kTestV6AddrFullAnonymizedString = "[2001:db8:1020::]:5678"; + +TEST(SocketAddressTest, TestDefaultCtor) { + SocketAddress addr; + EXPECT_FALSE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(), addr.ipaddr()); + EXPECT_EQ(0, addr.port()); + EXPECT_EQ("", addr.hostname()); +} + +TEST(SocketAddressTest, TestIPPortCtor) { + SocketAddress addr(IPAddress(0x01020304), 5678); + EXPECT_FALSE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr()); + EXPECT_EQ(5678, addr.port()); + EXPECT_EQ("", addr.hostname()); + EXPECT_EQ("1.2.3.4:5678", addr.ToString()); +} + +TEST(SocketAddressTest, TestIPv4StringPortCtor) { + SocketAddress addr("1.2.3.4", 5678); + EXPECT_FALSE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr()); + EXPECT_EQ(5678, addr.port()); + EXPECT_EQ("1.2.3.4", addr.hostname()); + EXPECT_EQ("1.2.3.4:5678", addr.ToString()); +} + +TEST(SocketAddressTest, TestIPv6StringPortCtor) { + SocketAddress addr2(kTestV6AddrString, 1234); + IPAddress tocheck(kTestV6Addr); + + EXPECT_FALSE(addr2.IsUnresolvedIP()); + EXPECT_EQ(tocheck, addr2.ipaddr()); + EXPECT_EQ(1234, addr2.port()); + EXPECT_EQ(kTestV6AddrString, addr2.hostname()); + EXPECT_EQ("[" + kTestV6AddrString + "]:1234", addr2.ToString()); +} + +TEST(SocketAddressTest, TestSpecialStringPortCtor) { + // inet_addr doesn't handle this address properly. + SocketAddress addr("255.255.255.255", 5678); + EXPECT_FALSE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(0xFFFFFFFFU), addr.ipaddr()); + EXPECT_EQ(5678, addr.port()); + EXPECT_EQ("255.255.255.255", addr.hostname()); + EXPECT_EQ("255.255.255.255:5678", addr.ToString()); +} + +TEST(SocketAddressTest, TestHostnamePortCtor) { + SocketAddress addr("a.b.com", 5678); + EXPECT_TRUE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(), addr.ipaddr()); + EXPECT_EQ(5678, addr.port()); + EXPECT_EQ("a.b.com", addr.hostname()); + EXPECT_EQ("a.b.com:5678", addr.ToString()); +} + +TEST(SocketAddressTest, TestCopyCtor) { + SocketAddress from("1.2.3.4", 5678); + SocketAddress addr(from); + EXPECT_FALSE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr()); + EXPECT_EQ(5678, addr.port()); + EXPECT_EQ("1.2.3.4", addr.hostname()); + EXPECT_EQ("1.2.3.4:5678", addr.ToString()); +} + +TEST(SocketAddressTest, TestAssign) { + SocketAddress from("1.2.3.4", 5678); + SocketAddress addr(IPAddress(0x88888888), 9999); + addr = from; + EXPECT_FALSE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr()); + EXPECT_EQ(5678, addr.port()); + EXPECT_EQ("1.2.3.4", addr.hostname()); + EXPECT_EQ("1.2.3.4:5678", addr.ToString()); +} + +TEST(SocketAddressTest, TestSetIPPort) { + SocketAddress addr(IPAddress(0x88888888), 9999); + addr.SetIP(IPAddress(0x01020304)); + addr.SetPort(5678); + EXPECT_FALSE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr()); + EXPECT_EQ(5678, addr.port()); + EXPECT_EQ("", addr.hostname()); + EXPECT_EQ("1.2.3.4:5678", addr.ToString()); +} + +TEST(SocketAddressTest, TestSetIPFromString) { + SocketAddress addr(IPAddress(0x88888888), 9999); + addr.SetIP("1.2.3.4"); + addr.SetPort(5678); + EXPECT_FALSE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr()); + EXPECT_EQ(5678, addr.port()); + EXPECT_EQ("1.2.3.4", addr.hostname()); + EXPECT_EQ("1.2.3.4:5678", addr.ToString()); +} + +TEST(SocketAddressTest, TestSetIPFromHostname) { + SocketAddress addr(IPAddress(0x88888888), 9999); + addr.SetIP("a.b.com"); + addr.SetPort(5678); + EXPECT_TRUE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(), addr.ipaddr()); + EXPECT_EQ(5678, addr.port()); + EXPECT_EQ("a.b.com", addr.hostname()); + EXPECT_EQ("a.b.com:5678", addr.ToString()); + addr.SetResolvedIP(IPAddress(0x01020304)); + EXPECT_FALSE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr()); + EXPECT_EQ("a.b.com", addr.hostname()); + EXPECT_EQ("a.b.com:5678", addr.ToString()); +} + +TEST(SocketAddressTest, TestFromIPv4String) { + SocketAddress addr; + EXPECT_TRUE(addr.FromString("1.2.3.4:5678")); + EXPECT_FALSE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr()); + EXPECT_EQ(5678, addr.port()); + EXPECT_EQ("1.2.3.4", addr.hostname()); + EXPECT_EQ("1.2.3.4:5678", addr.ToString()); +} + +TEST(SocketAddressTest, TestFromIPv6String) { + SocketAddress addr; + EXPECT_TRUE(addr.FromString(kTestV6AddrFullString)); + EXPECT_FALSE(addr.IsUnresolvedIP()); + EXPECT_EQ(5678, addr.port()); + EXPECT_EQ(kTestV6AddrString, addr.hostname()); + EXPECT_EQ(kTestV6AddrFullString, addr.ToString()); +} + +TEST(SocketAddressTest, TestFromHostname) { + SocketAddress addr; + EXPECT_TRUE(addr.FromString("a.b.com:5678")); + EXPECT_TRUE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(), addr.ipaddr()); + EXPECT_EQ(5678, addr.port()); + EXPECT_EQ("a.b.com", addr.hostname()); + EXPECT_EQ("a.b.com:5678", addr.ToString()); +} + +TEST(SocketAddressTest, TestToFromSockAddr) { + SocketAddress from("1.2.3.4", 5678), addr; + sockaddr_in addr_in; + from.ToSockAddr(&addr_in); + EXPECT_TRUE(addr.FromSockAddr(addr_in)); + EXPECT_FALSE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr()); + EXPECT_EQ(5678, addr.port()); + EXPECT_EQ("", addr.hostname()); + EXPECT_EQ("1.2.3.4:5678", addr.ToString()); +} + +TEST(SocketAddressTest, TestToFromSockAddrStorage) { + SocketAddress from("1.2.3.4", 5678), addr; + sockaddr_storage addr_storage; + from.ToSockAddrStorage(&addr_storage); + EXPECT_TRUE(SocketAddressFromSockAddrStorage(addr_storage, &addr)); + EXPECT_FALSE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr()); + EXPECT_EQ(5678, addr.port()); + EXPECT_EQ("", addr.hostname()); + EXPECT_EQ("1.2.3.4:5678", addr.ToString()); + + addr.Clear(); + from.ToDualStackSockAddrStorage(&addr_storage); + EXPECT_TRUE(SocketAddressFromSockAddrStorage(addr_storage, &addr)); + EXPECT_FALSE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(kMappedV4Addr), addr.ipaddr()); + EXPECT_EQ(5678, addr.port()); + EXPECT_EQ("", addr.hostname()); + EXPECT_EQ("[::ffff:1.2.3.4]:5678", addr.ToString()); + + addr.Clear(); + memset(&addr_storage, 0, sizeof(sockaddr_storage)); + from = SocketAddress(kTestV6AddrString, 5678); + from.SetScopeID(6); + from.ToSockAddrStorage(&addr_storage); + EXPECT_TRUE(SocketAddressFromSockAddrStorage(addr_storage, &addr)); + EXPECT_FALSE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(kTestV6Addr), addr.ipaddr()); + EXPECT_EQ(5678, addr.port()); + EXPECT_EQ("", addr.hostname()); + EXPECT_EQ(kTestV6AddrFullString, addr.ToString()); + EXPECT_EQ(6, addr.scope_id()); + + addr.Clear(); + from.ToDualStackSockAddrStorage(&addr_storage); + EXPECT_TRUE(SocketAddressFromSockAddrStorage(addr_storage, &addr)); + EXPECT_FALSE(addr.IsUnresolvedIP()); + EXPECT_EQ(IPAddress(kTestV6Addr), addr.ipaddr()); + EXPECT_EQ(5678, addr.port()); + EXPECT_EQ("", addr.hostname()); + EXPECT_EQ(kTestV6AddrFullString, addr.ToString()); + EXPECT_EQ(6, addr.scope_id()); + + addr = from; + addr_storage.ss_family = AF_UNSPEC; + EXPECT_FALSE(SocketAddressFromSockAddrStorage(addr_storage, &addr)); + EXPECT_EQ(from, addr); + + EXPECT_FALSE(SocketAddressFromSockAddrStorage(addr_storage, NULL)); +} + +bool AreEqual(const SocketAddress& addr1, + const SocketAddress& addr2) { + return addr1 == addr2 && addr2 == addr1 && + !(addr1 != addr2) && !(addr2 != addr1); +} + +bool AreUnequal(const SocketAddress& addr1, + const SocketAddress& addr2) { + return !(addr1 == addr2) && !(addr2 == addr1) && + addr1 != addr2 && addr2 != addr1; +} + +TEST(SocketAddressTest, TestEqualityOperators) { + SocketAddress addr1("1.2.3.4", 5678); + SocketAddress addr2("1.2.3.4", 5678); + EXPECT_PRED2(AreEqual, addr1, addr2); + + addr2 = SocketAddress("0.0.0.1", 5678); + EXPECT_PRED2(AreUnequal, addr1, addr2); + + addr2 = SocketAddress("1.2.3.4", 1234); + EXPECT_PRED2(AreUnequal, addr1, addr2); + + addr2 = SocketAddress(kTestV6AddrString, 5678); + EXPECT_PRED2(AreUnequal, addr1, addr2); + + addr1 = SocketAddress(kTestV6AddrString, 5678); + EXPECT_PRED2(AreEqual, addr1, addr2); + + addr2 = SocketAddress(kTestV6AddrString, 1234); + EXPECT_PRED2(AreUnequal, addr1, addr2); + + addr2 = SocketAddress("fe80::1", 5678); + EXPECT_PRED2(AreUnequal, addr1, addr2); + + SocketAddress addr3("a.b.c.d", 1); + SocketAddress addr4("b.b.c.d", 1); + EXPECT_PRED2(AreUnequal, addr3, addr4); + EXPECT_PRED2(AreEqual, addr3, addr3); + + addr3.SetIP(addr1.ip()); + addr4.SetIP(addr1.ip()); + EXPECT_PRED2(AreEqual,addr3, addr4); +} + +bool IsLessThan(const SocketAddress& addr1, const SocketAddress& addr2) { + return addr1 < addr2 && + !(addr2 < addr1) && + !(addr1 == addr2); +} + +TEST(SocketAddressTest, TestComparisonOperator) { + SocketAddress addr1("1.2.3.4", 5678); + SocketAddress addr2("1.2.3.4", 5678); + + EXPECT_FALSE(addr1 < addr2); + EXPECT_FALSE(addr2 < addr1); + + addr2 = SocketAddress("1.2.3.4", 5679); + EXPECT_PRED2(IsLessThan, addr1, addr2); + + addr2 = SocketAddress("2.2.3.4", 49152); + EXPECT_PRED2(IsLessThan, addr1, addr2); + + addr2 = SocketAddress(kTestV6AddrString, 5678); + EXPECT_PRED2(IsLessThan, addr1, addr2); + + addr1 = SocketAddress("fe80::1", 5678); + EXPECT_PRED2(IsLessThan, addr2, addr1); + + addr2 = SocketAddress("fe80::1", 5679); + EXPECT_PRED2(IsLessThan, addr1, addr2); + + addr2 = SocketAddress("fe80::1", 5678); + EXPECT_FALSE(addr1 < addr2); + EXPECT_FALSE(addr2 < addr1); + + SocketAddress addr3("a.b.c.d", 1); + SocketAddress addr4("b.b.c.d", 1); + EXPECT_PRED2(IsLessThan, addr3, addr4); +} + +TEST(SocketAddressTest, TestToSensitiveString) { + SocketAddress addr_v4("1.2.3.4", 5678); + EXPECT_EQ("1.2.3.4", addr_v4.HostAsURIString()); + EXPECT_EQ("1.2.3.4:5678", addr_v4.ToString()); + EXPECT_EQ("1.2.3.4", addr_v4.HostAsSensitiveURIString()); + EXPECT_EQ("1.2.3.4:5678", addr_v4.ToSensitiveString()); + IPAddress::set_strip_sensitive(true); + EXPECT_EQ("1.2.3.x", addr_v4.HostAsSensitiveURIString()); + EXPECT_EQ("1.2.3.x:5678", addr_v4.ToSensitiveString()); + IPAddress::set_strip_sensitive(false); + + SocketAddress addr_v6(kTestV6AddrString, 5678); + EXPECT_EQ("[" + kTestV6AddrString + "]", addr_v6.HostAsURIString()); + EXPECT_EQ(kTestV6AddrFullString, addr_v6.ToString()); + EXPECT_EQ("[" + kTestV6AddrString + "]", addr_v6.HostAsSensitiveURIString()); + EXPECT_EQ(kTestV6AddrFullString, addr_v6.ToSensitiveString()); + IPAddress::set_strip_sensitive(true); + EXPECT_EQ("[" + kTestV6AddrAnonymizedString + "]", + addr_v6.HostAsSensitiveURIString()); + EXPECT_EQ(kTestV6AddrFullAnonymizedString, addr_v6.ToSensitiveString()); + IPAddress::set_strip_sensitive(false); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketfactory.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketfactory.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketfactory.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketfactory.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,38 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SOCKETFACTORY_H__ +#define WEBRTC_BASE_SOCKETFACTORY_H__ + +#include "webrtc/base/socket.h" +#include "webrtc/base/asyncsocket.h" + +namespace rtc { + +class SocketFactory { +public: + virtual ~SocketFactory() {} + + // Returns a new socket for blocking communication. The type can be + // SOCK_DGRAM and SOCK_STREAM. + // TODO: C++ inheritance rules mean that all users must have both + // CreateSocket(int) and CreateSocket(int,int). Will remove CreateSocket(int) + // (and CreateAsyncSocket(int) when all callers are changed. + virtual Socket* CreateSocket(int type) = 0; + virtual Socket* CreateSocket(int family, int type) = 0; + // Returns a new socket for nonblocking communication. The type can be + // SOCK_DGRAM and SOCK_STREAM. + virtual AsyncSocket* CreateAsyncSocket(int type) = 0; + virtual AsyncSocket* CreateAsyncSocket(int family, int type) = 0; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_SOCKETFACTORY_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socket.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socket.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socket.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socket.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,188 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SOCKET_H__ +#define WEBRTC_BASE_SOCKET_H__ + +#include + +#if defined(WEBRTC_POSIX) +#include +#include +#include +#include +#define SOCKET_EACCES EACCES +#endif + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32.h" +#endif + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/socketaddress.h" + +// Rather than converting errors into a private namespace, +// Reuse the POSIX socket api errors. Note this depends on +// Win32 compatibility. + +#if defined(WEBRTC_WIN) +#undef EWOULDBLOCK // Remove errno.h's definition for each macro below. +#define EWOULDBLOCK WSAEWOULDBLOCK +#undef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#undef EALREADY +#define EALREADY WSAEALREADY +#undef ENOTSOCK +#define ENOTSOCK WSAENOTSOCK +#undef EDESTADDRREQ +#define EDESTADDRREQ WSAEDESTADDRREQ +#undef EMSGSIZE +#define EMSGSIZE WSAEMSGSIZE +#undef EPROTOTYPE +#define EPROTOTYPE WSAEPROTOTYPE +#undef ENOPROTOOPT +#define ENOPROTOOPT WSAENOPROTOOPT +#undef EPROTONOSUPPORT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#undef ESOCKTNOSUPPORT +#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#undef EOPNOTSUPP +#define EOPNOTSUPP WSAEOPNOTSUPP +#undef EPFNOSUPPORT +#define EPFNOSUPPORT WSAEPFNOSUPPORT +#undef EAFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#undef EADDRINUSE +#define EADDRINUSE WSAEADDRINUSE +#undef EADDRNOTAVAIL +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#undef ENETDOWN +#define ENETDOWN WSAENETDOWN +#undef ENETUNREACH +#define ENETUNREACH WSAENETUNREACH +#undef ENETRESET +#define ENETRESET WSAENETRESET +#undef ECONNABORTED +#define ECONNABORTED WSAECONNABORTED +#undef ECONNRESET +#define ECONNRESET WSAECONNRESET +#undef ENOBUFS +#define ENOBUFS WSAENOBUFS +#undef EISCONN +#define EISCONN WSAEISCONN +#undef ENOTCONN +#define ENOTCONN WSAENOTCONN +#undef ESHUTDOWN +#define ESHUTDOWN WSAESHUTDOWN +#undef ETOOMANYREFS +#define ETOOMANYREFS WSAETOOMANYREFS +#undef ETIMEDOUT +#define ETIMEDOUT WSAETIMEDOUT +#undef ECONNREFUSED +#define ECONNREFUSED WSAECONNREFUSED +#undef ELOOP +#define ELOOP WSAELOOP +#undef ENAMETOOLONG +#define ENAMETOOLONG WSAENAMETOOLONG +#undef EHOSTDOWN +#define EHOSTDOWN WSAEHOSTDOWN +#undef EHOSTUNREACH +#define EHOSTUNREACH WSAEHOSTUNREACH +#undef ENOTEMPTY +#define ENOTEMPTY WSAENOTEMPTY +#undef EPROCLIM +#define EPROCLIM WSAEPROCLIM +#undef EUSERS +#define EUSERS WSAEUSERS +#undef EDQUOT +#define EDQUOT WSAEDQUOT +#undef ESTALE +#define ESTALE WSAESTALE +#undef EREMOTE +#define EREMOTE WSAEREMOTE +#undef EACCES +#define SOCKET_EACCES WSAEACCES +#endif // WEBRTC_WIN + +#if defined(WEBRTC_POSIX) +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define closesocket(s) close(s) +#endif // WEBRTC_POSIX + +namespace rtc { + +inline bool IsBlockingError(int e) { + return (e == EWOULDBLOCK) || (e == EAGAIN) || (e == EINPROGRESS); +} + +// General interface for the socket implementations of various networks. The +// methods match those of normal UNIX sockets very closely. +class Socket { + public: + virtual ~Socket() {} + + // Returns the address to which the socket is bound. If the socket is not + // bound, then the any-address is returned. + virtual SocketAddress GetLocalAddress() const = 0; + + // Returns the address to which the socket is connected. If the socket is + // not connected, then the any-address is returned. + virtual SocketAddress GetRemoteAddress() const = 0; + + virtual int Bind(const SocketAddress& addr) = 0; + virtual int Connect(const SocketAddress& addr) = 0; + virtual int Send(const void *pv, size_t cb) = 0; + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr) = 0; + virtual int Recv(void *pv, size_t cb) = 0; + virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) = 0; + virtual int Listen(int backlog) = 0; + virtual Socket *Accept(SocketAddress *paddr) = 0; + virtual int Close() = 0; + virtual int GetError() const = 0; + virtual void SetError(int error) = 0; + inline bool IsBlocking() const { return IsBlockingError(GetError()); } + + enum ConnState { + CS_CLOSED, + CS_CONNECTING, + CS_CONNECTED + }; + virtual ConnState GetState() const = 0; + + // Fills in the given uint16 with the current estimate of the MTU along the + // path to the address to which this socket is connected. NOTE: This method + // can block for up to 10 seconds on Windows. + virtual int EstimateMTU(uint16* mtu) = 0; + + enum Option { + OPT_DONTFRAGMENT, + OPT_RCVBUF, // receive buffer size + OPT_SNDBUF, // send buffer size + OPT_NODELAY, // whether Nagle algorithm is enabled + OPT_IPV6_V6ONLY, // Whether the socket is IPv6 only. + OPT_DSCP, // DSCP code + OPT_RTP_SENDTIME_EXTN_ID, // This is a non-traditional socket option param. + // This is specific to libjingle and will be used + // if SendTime option is needed at socket level. + }; + virtual int GetOption(Option opt, int* value) = 0; + virtual int SetOption(Option opt, int value) = 0; + + protected: + Socket() {} + + private: + DISALLOW_EVIL_CONSTRUCTORS(Socket); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_SOCKET_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketpool.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketpool.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketpool.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketpool.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,280 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/asyncsocket.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/socketfactory.h" +#include "webrtc/base/socketpool.h" +#include "webrtc/base/socketstream.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// StreamCache - Caches a set of open streams, defers creation to a separate +// StreamPool. +/////////////////////////////////////////////////////////////////////////////// + +StreamCache::StreamCache(StreamPool* pool) : pool_(pool) { +} + +StreamCache::~StreamCache() { + for (ConnectedList::iterator it = active_.begin(); it != active_.end(); + ++it) { + delete it->second; + } + for (ConnectedList::iterator it = cached_.begin(); it != cached_.end(); + ++it) { + delete it->second; + } +} + +StreamInterface* StreamCache::RequestConnectedStream( + const SocketAddress& remote, int* err) { + LOG_F(LS_VERBOSE) << "(" << remote << ")"; + for (ConnectedList::iterator it = cached_.begin(); it != cached_.end(); + ++it) { + if (remote == it->first) { + it->second->SignalEvent.disconnect(this); + // Move from cached_ to active_ + active_.push_front(*it); + cached_.erase(it); + if (err) + *err = 0; + LOG_F(LS_VERBOSE) << "Providing cached stream"; + return active_.front().second; + } + } + if (StreamInterface* stream = pool_->RequestConnectedStream(remote, err)) { + // We track active streams so that we can remember their address + active_.push_front(ConnectedStream(remote, stream)); + LOG_F(LS_VERBOSE) << "Providing new stream"; + return active_.front().second; + } + return NULL; +} + +void StreamCache::ReturnConnectedStream(StreamInterface* stream) { + for (ConnectedList::iterator it = active_.begin(); it != active_.end(); + ++it) { + if (stream == it->second) { + LOG_F(LS_VERBOSE) << "(" << it->first << ")"; + if (stream->GetState() == SS_CLOSED) { + // Return closed streams + LOG_F(LS_VERBOSE) << "Returning closed stream"; + pool_->ReturnConnectedStream(it->second); + } else { + // Monitor open streams + stream->SignalEvent.connect(this, &StreamCache::OnStreamEvent); + LOG_F(LS_VERBOSE) << "Caching stream"; + cached_.push_front(*it); + } + active_.erase(it); + return; + } + } + ASSERT(false); +} + +void StreamCache::OnStreamEvent(StreamInterface* stream, int events, int err) { + if ((events & SE_CLOSE) == 0) { + LOG_F(LS_WARNING) << "(" << events << ", " << err + << ") received non-close event"; + return; + } + for (ConnectedList::iterator it = cached_.begin(); it != cached_.end(); + ++it) { + if (stream == it->second) { + LOG_F(LS_VERBOSE) << "(" << it->first << ")"; + // We don't cache closed streams, so return it. + it->second->SignalEvent.disconnect(this); + LOG_F(LS_VERBOSE) << "Returning closed stream"; + pool_->ReturnConnectedStream(it->second); + cached_.erase(it); + return; + } + } + ASSERT(false); +} + +////////////////////////////////////////////////////////////////////// +// NewSocketPool +////////////////////////////////////////////////////////////////////// + +NewSocketPool::NewSocketPool(SocketFactory* factory) : factory_(factory) { +} + +NewSocketPool::~NewSocketPool() { +} + +StreamInterface* +NewSocketPool::RequestConnectedStream(const SocketAddress& remote, int* err) { + AsyncSocket* socket = + factory_->CreateAsyncSocket(remote.family(), SOCK_STREAM); + if (!socket) { + if (err) + *err = -1; + return NULL; + } + if ((socket->Connect(remote) != 0) && !socket->IsBlocking()) { + if (err) + *err = socket->GetError(); + delete socket; + return NULL; + } + if (err) + *err = 0; + return new SocketStream(socket); +} + +void +NewSocketPool::ReturnConnectedStream(StreamInterface* stream) { + Thread::Current()->Dispose(stream); +} + +////////////////////////////////////////////////////////////////////// +// ReuseSocketPool +////////////////////////////////////////////////////////////////////// + +ReuseSocketPool::ReuseSocketPool(SocketFactory* factory) +: factory_(factory), stream_(NULL), checked_out_(false) { +} + +ReuseSocketPool::~ReuseSocketPool() { + ASSERT(!checked_out_); + delete stream_; +} + +StreamInterface* +ReuseSocketPool::RequestConnectedStream(const SocketAddress& remote, int* err) { + // Only one socket can be used from this "pool" at a time + ASSERT(!checked_out_); + if (!stream_) { + LOG_F(LS_VERBOSE) << "Creating new socket"; + int family = remote.family(); + // TODO: Deal with this when we/I clean up DNS resolution. + if (remote.IsUnresolvedIP()) { + family = AF_INET; + } + AsyncSocket* socket = + factory_->CreateAsyncSocket(family, SOCK_STREAM); + if (!socket) { + if (err) + *err = -1; + return NULL; + } + stream_ = new SocketStream(socket); + } + if ((stream_->GetState() == SS_OPEN) && (remote == remote_)) { + LOG_F(LS_VERBOSE) << "Reusing connection to: " << remote_; + } else { + remote_ = remote; + stream_->Close(); + if ((stream_->GetSocket()->Connect(remote_) != 0) + && !stream_->GetSocket()->IsBlocking()) { + if (err) + *err = stream_->GetSocket()->GetError(); + return NULL; + } else { + LOG_F(LS_VERBOSE) << "Opening connection to: " << remote_; + } + } + stream_->SignalEvent.disconnect(this); + checked_out_ = true; + if (err) + *err = 0; + return stream_; +} + +void +ReuseSocketPool::ReturnConnectedStream(StreamInterface* stream) { + ASSERT(stream == stream_); + ASSERT(checked_out_); + checked_out_ = false; + // Until the socket is reused, monitor it to determine if it closes. + stream_->SignalEvent.connect(this, &ReuseSocketPool::OnStreamEvent); +} + +void +ReuseSocketPool::OnStreamEvent(StreamInterface* stream, int events, int err) { + ASSERT(stream == stream_); + ASSERT(!checked_out_); + + // If the stream was written to and then immediately returned to us then + // we may get a writable notification for it, which we should ignore. + if (events == SE_WRITE) { + LOG_F(LS_VERBOSE) << "Pooled Socket unexpectedly writable: ignoring"; + return; + } + + // If the peer sent data, we can't process it, so drop the connection. + // If the socket has closed, clean it up. + // In either case, we'll reconnect it the next time it is used. + ASSERT(0 != (events & (SE_READ|SE_CLOSE))); + if (0 != (events & SE_CLOSE)) { + LOG_F(LS_VERBOSE) << "Connection closed with error: " << err; + } else { + LOG_F(LS_VERBOSE) << "Pooled Socket unexpectedly readable: closing"; + } + stream_->Close(); +} + +/////////////////////////////////////////////////////////////////////////////// +// LoggingPoolAdapter - Adapts a StreamPool to supply streams with attached +// LoggingAdapters. +/////////////////////////////////////////////////////////////////////////////// + +LoggingPoolAdapter::LoggingPoolAdapter( + StreamPool* pool, LoggingSeverity level, const std::string& label, + bool binary_mode) + : pool_(pool), level_(level), label_(label), binary_mode_(binary_mode) { +} + +LoggingPoolAdapter::~LoggingPoolAdapter() { + for (StreamList::iterator it = recycle_bin_.begin(); + it != recycle_bin_.end(); ++it) { + delete *it; + } +} + +StreamInterface* LoggingPoolAdapter::RequestConnectedStream( + const SocketAddress& remote, int* err) { + if (StreamInterface* stream = pool_->RequestConnectedStream(remote, err)) { + ASSERT(SS_CLOSED != stream->GetState()); + std::stringstream ss; + ss << label_ << "(0x" << std::setfill('0') << std::hex << std::setw(8) + << stream << ")"; + LOG_V(level_) << ss.str() + << ((SS_OPEN == stream->GetState()) ? " Connected" + : " Connecting") + << " to " << remote; + if (recycle_bin_.empty()) { + return new LoggingAdapter(stream, level_, ss.str(), binary_mode_); + } + LoggingAdapter* logging = recycle_bin_.front(); + recycle_bin_.pop_front(); + logging->set_label(ss.str()); + logging->Attach(stream); + return logging; + } + return NULL; +} + +void LoggingPoolAdapter::ReturnConnectedStream(StreamInterface* stream) { + LoggingAdapter* logging = static_cast(stream); + pool_->ReturnConnectedStream(logging->Detach()); + recycle_bin_.push_back(logging); +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketpool.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketpool.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketpool.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketpool.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,143 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SOCKETPOOL_H_ +#define WEBRTC_BASE_SOCKETPOOL_H_ + +#include +#include +#include "webrtc/base/logging.h" +#include "webrtc/base/sigslot.h" +#include "webrtc/base/socketaddress.h" + +namespace rtc { + +class AsyncSocket; +class LoggingAdapter; +class SocketFactory; +class SocketStream; +class StreamInterface; + +////////////////////////////////////////////////////////////////////// +// StreamPool +////////////////////////////////////////////////////////////////////// + +class StreamPool { +public: + virtual ~StreamPool() { } + + virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote, + int* err) = 0; + virtual void ReturnConnectedStream(StreamInterface* stream) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamCache - Caches a set of open streams, defers creation/destruction to +// the supplied StreamPool. +/////////////////////////////////////////////////////////////////////////////// + +class StreamCache : public StreamPool, public sigslot::has_slots<> { +public: + StreamCache(StreamPool* pool); + virtual ~StreamCache(); + + // StreamPool Interface + virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote, + int* err); + virtual void ReturnConnectedStream(StreamInterface* stream); + +private: + typedef std::pair ConnectedStream; + typedef std::list ConnectedList; + + void OnStreamEvent(StreamInterface* stream, int events, int err); + + // We delegate stream creation and deletion to this pool. + StreamPool* pool_; + // Streams that are in use (returned from RequestConnectedStream). + ConnectedList active_; + // Streams which were returned to us, but are still open. + ConnectedList cached_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// NewSocketPool +// Creates a new stream on every request +/////////////////////////////////////////////////////////////////////////////// + +class NewSocketPool : public StreamPool { +public: + NewSocketPool(SocketFactory* factory); + virtual ~NewSocketPool(); + + // StreamPool Interface + virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote, + int* err); + virtual void ReturnConnectedStream(StreamInterface* stream); + +private: + SocketFactory* factory_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// ReuseSocketPool +// Maintains a single socket at a time, and will reuse it without closing if +// the destination address is the same. +/////////////////////////////////////////////////////////////////////////////// + +class ReuseSocketPool : public StreamPool, public sigslot::has_slots<> { +public: + ReuseSocketPool(SocketFactory* factory); + virtual ~ReuseSocketPool(); + + // StreamPool Interface + virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote, + int* err); + virtual void ReturnConnectedStream(StreamInterface* stream); + +private: + void OnStreamEvent(StreamInterface* stream, int events, int err); + + SocketFactory* factory_; + SocketStream* stream_; + SocketAddress remote_; + bool checked_out_; // Whether the stream is currently checked out +}; + +/////////////////////////////////////////////////////////////////////////////// +// LoggingPoolAdapter - Adapts a StreamPool to supply streams with attached +// LoggingAdapters. +/////////////////////////////////////////////////////////////////////////////// + +class LoggingPoolAdapter : public StreamPool { +public: + LoggingPoolAdapter(StreamPool* pool, LoggingSeverity level, + const std::string& label, bool binary_mode); + virtual ~LoggingPoolAdapter(); + + // StreamPool Interface + virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote, + int* err); + virtual void ReturnConnectedStream(StreamInterface* stream); + +private: + StreamPool* pool_; + LoggingSeverity level_; + std::string label_; + bool binary_mode_; + typedef std::deque StreamList; + StreamList recycle_bin_; +}; + +////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_SOCKETPOOL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketserver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketserver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketserver.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketserver.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,44 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SOCKETSERVER_H_ +#define WEBRTC_BASE_SOCKETSERVER_H_ + +#include "webrtc/base/socketfactory.h" + +namespace rtc { + +class MessageQueue; + +// Provides the ability to wait for activity on a set of sockets. The Thread +// class provides a nice wrapper on a socket server. +// +// The server is also a socket factory. The sockets it creates will be +// notified of asynchronous I/O from this server's Wait method. +class SocketServer : public SocketFactory { + public: + // When the socket server is installed into a Thread, this function is + // called to allow the socket server to use the thread's message queue for + // any messaging that it might need to perform. + virtual void SetMessageQueue(MessageQueue* queue) {} + + // Sleeps until: + // 1) cms milliseconds have elapsed (unless cms == kForever) + // 2) WakeUp() is called + // While sleeping, I/O is performed if process_io is true. + virtual bool Wait(int cms, bool process_io) = 0; + + // Causes the current wait (if one is in progress) to wake up. + virtual void WakeUp() = 0; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_SOCKETSERVER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketstream.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketstream.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketstream.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketstream.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,121 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/socketstream.h" + +namespace rtc { + +SocketStream::SocketStream(AsyncSocket* socket) : socket_(NULL) { + Attach(socket); +} + +SocketStream::~SocketStream() { + delete socket_; +} + +void SocketStream::Attach(AsyncSocket* socket) { + if (socket_) + delete socket_; + socket_ = socket; + if (socket_) { + socket_->SignalConnectEvent.connect(this, &SocketStream::OnConnectEvent); + socket_->SignalReadEvent.connect(this, &SocketStream::OnReadEvent); + socket_->SignalWriteEvent.connect(this, &SocketStream::OnWriteEvent); + socket_->SignalCloseEvent.connect(this, &SocketStream::OnCloseEvent); + } +} + +AsyncSocket* SocketStream::Detach() { + AsyncSocket* socket = socket_; + if (socket_) { + socket_->SignalConnectEvent.disconnect(this); + socket_->SignalReadEvent.disconnect(this); + socket_->SignalWriteEvent.disconnect(this); + socket_->SignalCloseEvent.disconnect(this); + socket_ = NULL; + } + return socket; +} + +StreamState SocketStream::GetState() const { + ASSERT(socket_ != NULL); + switch (socket_->GetState()) { + case Socket::CS_CONNECTED: + return SS_OPEN; + case Socket::CS_CONNECTING: + return SS_OPENING; + case Socket::CS_CLOSED: + default: + return SS_CLOSED; + } +} + +StreamResult SocketStream::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + ASSERT(socket_ != NULL); + int result = socket_->Recv(buffer, buffer_len); + if (result < 0) { + if (socket_->IsBlocking()) + return SR_BLOCK; + if (error) + *error = socket_->GetError(); + return SR_ERROR; + } + if ((result > 0) || (buffer_len == 0)) { + if (read) + *read = result; + return SR_SUCCESS; + } + return SR_EOS; +} + +StreamResult SocketStream::Write(const void* data, size_t data_len, + size_t* written, int* error) { + ASSERT(socket_ != NULL); + int result = socket_->Send(data, data_len); + if (result < 0) { + if (socket_->IsBlocking()) + return SR_BLOCK; + if (error) + *error = socket_->GetError(); + return SR_ERROR; + } + if (written) + *written = result; + return SR_SUCCESS; +} + +void SocketStream::Close() { + ASSERT(socket_ != NULL); + socket_->Close(); +} + +void SocketStream::OnConnectEvent(AsyncSocket* socket) { + ASSERT(socket == socket_); + SignalEvent(this, SE_OPEN | SE_READ | SE_WRITE, 0); +} + +void SocketStream::OnReadEvent(AsyncSocket* socket) { + ASSERT(socket == socket_); + SignalEvent(this, SE_READ, 0); +} + +void SocketStream::OnWriteEvent(AsyncSocket* socket) { + ASSERT(socket == socket_); + SignalEvent(this, SE_WRITE, 0); +} + +void SocketStream::OnCloseEvent(AsyncSocket* socket, int err) { + ASSERT(socket == socket_); + SignalEvent(this, SE_CLOSE, err); +} + + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketstream.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketstream.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socketstream.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socketstream.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,57 @@ +/* + * Copyright 2005 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SOCKETSTREAM_H_ +#define WEBRTC_BASE_SOCKETSTREAM_H_ + +#include "webrtc/base/asyncsocket.h" +#include "webrtc/base/common.h" +#include "webrtc/base/stream.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// + +class SocketStream : public StreamInterface, public sigslot::has_slots<> { + public: + explicit SocketStream(AsyncSocket* socket); + virtual ~SocketStream(); + + void Attach(AsyncSocket* socket); + AsyncSocket* Detach(); + + AsyncSocket* GetSocket() { return socket_; } + + virtual StreamState GetState() const; + + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + + virtual void Close(); + + private: + void OnConnectEvent(AsyncSocket* socket); + void OnReadEvent(AsyncSocket* socket); + void OnWriteEvent(AsyncSocket* socket); + void OnCloseEvent(AsyncSocket* socket, int err); + + AsyncSocket* socket_; + + DISALLOW_EVIL_CONSTRUCTORS(SocketStream); +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_SOCKETSTREAM_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socket_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socket_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socket_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socket_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,1012 @@ +/* + * Copyright 2007 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/socket_unittest.h" + +#include "webrtc/base/asyncudpsocket.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/nethelpers.h" +#include "webrtc/base/socketserver.h" +#include "webrtc/base/testclient.h" +#include "webrtc/base/testutils.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +#define MAYBE_SKIP_IPV6 \ + if (!HasIPv6Enabled()) { \ + LOG(LS_INFO) << "No IPv6... skipping"; \ + return; \ + } + + +void SocketTest::TestConnectIPv4() { + ConnectInternal(kIPv4Loopback); +} + +void SocketTest::TestConnectIPv6() { + MAYBE_SKIP_IPV6; + ConnectInternal(kIPv6Loopback); +} + +void SocketTest::TestConnectWithDnsLookupIPv4() { + ConnectWithDnsLookupInternal(kIPv4Loopback, "localhost"); +} + +void SocketTest::TestConnectWithDnsLookupIPv6() { + // TODO: Enable this when DNS resolution supports IPv6. + LOG(LS_INFO) << "Skipping IPv6 DNS test"; + // ConnectWithDnsLookupInternal(kIPv6Loopback, "localhost6"); +} + +void SocketTest::TestConnectFailIPv4() { + ConnectFailInternal(kIPv4Loopback); +} + +void SocketTest::TestConnectFailIPv6() { + MAYBE_SKIP_IPV6; + ConnectFailInternal(kIPv6Loopback); +} + +void SocketTest::TestConnectWithDnsLookupFailIPv4() { + ConnectWithDnsLookupFailInternal(kIPv4Loopback); +} + +void SocketTest::TestConnectWithDnsLookupFailIPv6() { + MAYBE_SKIP_IPV6; + ConnectWithDnsLookupFailInternal(kIPv6Loopback); +} + +void SocketTest::TestConnectWithClosedSocketIPv4() { + ConnectWithClosedSocketInternal(kIPv4Loopback); +} + +void SocketTest::TestConnectWithClosedSocketIPv6() { + MAYBE_SKIP_IPV6; + ConnectWithClosedSocketInternal(kIPv6Loopback); +} + +void SocketTest::TestConnectWhileNotClosedIPv4() { + ConnectWhileNotClosedInternal(kIPv4Loopback); +} + +void SocketTest::TestConnectWhileNotClosedIPv6() { + MAYBE_SKIP_IPV6; + ConnectWhileNotClosedInternal(kIPv6Loopback); +} + +void SocketTest::TestServerCloseDuringConnectIPv4() { + ServerCloseDuringConnectInternal(kIPv4Loopback); +} + +void SocketTest::TestServerCloseDuringConnectIPv6() { + MAYBE_SKIP_IPV6; + ServerCloseDuringConnectInternal(kIPv6Loopback); +} + +void SocketTest::TestClientCloseDuringConnectIPv4() { + ClientCloseDuringConnectInternal(kIPv4Loopback); +} + +void SocketTest::TestClientCloseDuringConnectIPv6() { + MAYBE_SKIP_IPV6; + ClientCloseDuringConnectInternal(kIPv6Loopback); +} + +void SocketTest::TestServerCloseIPv4() { + ServerCloseInternal(kIPv4Loopback); +} + +void SocketTest::TestServerCloseIPv6() { + MAYBE_SKIP_IPV6; + ServerCloseInternal(kIPv6Loopback); +} + +void SocketTest::TestCloseInClosedCallbackIPv4() { + CloseInClosedCallbackInternal(kIPv4Loopback); +} + +void SocketTest::TestCloseInClosedCallbackIPv6() { + MAYBE_SKIP_IPV6; + CloseInClosedCallbackInternal(kIPv6Loopback); +} + +void SocketTest::TestSocketServerWaitIPv4() { + SocketServerWaitInternal(kIPv4Loopback); +} + +void SocketTest::TestSocketServerWaitIPv6() { + MAYBE_SKIP_IPV6; + SocketServerWaitInternal(kIPv6Loopback); +} + +void SocketTest::TestTcpIPv4() { + TcpInternal(kIPv4Loopback); +} + +void SocketTest::TestTcpIPv6() { + MAYBE_SKIP_IPV6; + TcpInternal(kIPv6Loopback); +} + +void SocketTest::TestSingleFlowControlCallbackIPv4() { + SingleFlowControlCallbackInternal(kIPv4Loopback); +} + +void SocketTest::TestSingleFlowControlCallbackIPv6() { + MAYBE_SKIP_IPV6; + SingleFlowControlCallbackInternal(kIPv6Loopback); +} + +void SocketTest::TestUdpIPv4() { + UdpInternal(kIPv4Loopback); +} + +void SocketTest::TestUdpIPv6() { + MAYBE_SKIP_IPV6; + UdpInternal(kIPv6Loopback); +} + +void SocketTest::TestUdpReadyToSendIPv4() { +#if !defined(WEBRTC_MAC) + // TODO(ronghuawu): Enable this test on mac/ios. + UdpReadyToSend(kIPv4Loopback); +#endif +} + +void SocketTest::TestUdpReadyToSendIPv6() { +#if defined(WEBRTC_WIN) + // TODO(ronghuawu): Enable this test (currently flakey) on mac and linux. + MAYBE_SKIP_IPV6; + UdpReadyToSend(kIPv6Loopback); +#endif +} + +void SocketTest::TestGetSetOptionsIPv4() { + GetSetOptionsInternal(kIPv4Loopback); +} + +void SocketTest::TestGetSetOptionsIPv6() { + MAYBE_SKIP_IPV6; + GetSetOptionsInternal(kIPv6Loopback); +} + +// For unbound sockets, GetLocalAddress / GetRemoteAddress return AF_UNSPEC +// values on Windows, but an empty address of the same family on Linux/MacOS X. +bool IsUnspecOrEmptyIP(const IPAddress& address) { +#if !defined(WEBRTC_WIN) + return IPIsAny(address); +#else + return address.family() == AF_UNSPEC; +#endif +} + +void SocketTest::ConnectInternal(const IPAddress& loopback) { + testing::StreamSink sink; + SocketAddress accept_addr; + + // Create client. + scoped_ptr client(ss_->CreateAsyncSocket(loopback.family(), + SOCK_STREAM)); + sink.Monitor(client.get()); + EXPECT_EQ(AsyncSocket::CS_CLOSED, client->GetState()); + EXPECT_PRED1(IsUnspecOrEmptyIP, client->GetLocalAddress().ipaddr()); + + // Create server and listen. + scoped_ptr server( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(server.get()); + EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0))); + EXPECT_EQ(0, server->Listen(5)); + EXPECT_EQ(AsyncSocket::CS_CONNECTING, server->GetState()); + + // Ensure no pending server connections, since we haven't done anything yet. + EXPECT_FALSE(sink.Check(server.get(), testing::SSE_READ)); + EXPECT_TRUE(NULL == server->Accept(&accept_addr)); + EXPECT_TRUE(accept_addr.IsNil()); + + // Attempt connect to listening socket. + EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); + EXPECT_FALSE(client->GetLocalAddress().IsNil()); + EXPECT_NE(server->GetLocalAddress(), client->GetLocalAddress()); + + // Client is connecting, outcome not yet determined. + EXPECT_EQ(AsyncSocket::CS_CONNECTING, client->GetState()); + EXPECT_FALSE(sink.Check(client.get(), testing::SSE_OPEN)); + EXPECT_FALSE(sink.Check(client.get(), testing::SSE_CLOSE)); + + // Server has pending connection, accept it. + EXPECT_TRUE_WAIT((sink.Check(server.get(), testing::SSE_READ)), kTimeout); + scoped_ptr accepted(server->Accept(&accept_addr)); + ASSERT_TRUE(accepted); + EXPECT_FALSE(accept_addr.IsNil()); + EXPECT_EQ(accepted->GetRemoteAddress(), accept_addr); + + // Connected from server perspective, check the addresses are correct. + EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState()); + EXPECT_EQ(server->GetLocalAddress(), accepted->GetLocalAddress()); + EXPECT_EQ(client->GetLocalAddress(), accepted->GetRemoteAddress()); + + // Connected from client perspective, check the addresses are correct. + EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout); + EXPECT_TRUE(sink.Check(client.get(), testing::SSE_OPEN)); + EXPECT_FALSE(sink.Check(client.get(), testing::SSE_CLOSE)); + EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress()); + EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress()); +} + +void SocketTest::ConnectWithDnsLookupInternal(const IPAddress& loopback, + const std::string& host) { + testing::StreamSink sink; + SocketAddress accept_addr; + + // Create client. + scoped_ptr client( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(client.get()); + + // Create server and listen. + scoped_ptr server( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(server.get()); + EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0))); + EXPECT_EQ(0, server->Listen(5)); + + // Attempt connect to listening socket. + SocketAddress dns_addr(server->GetLocalAddress()); + dns_addr.SetIP(host); + EXPECT_EQ(0, client->Connect(dns_addr)); + // TODO: Bind when doing DNS lookup. + //EXPECT_NE(kEmptyAddr, client->GetLocalAddress()); // Implicit Bind + + // Client is connecting, outcome not yet determined. + EXPECT_EQ(AsyncSocket::CS_CONNECTING, client->GetState()); + EXPECT_FALSE(sink.Check(client.get(), testing::SSE_OPEN)); + EXPECT_FALSE(sink.Check(client.get(), testing::SSE_CLOSE)); + + // Server has pending connection, accept it. + EXPECT_TRUE_WAIT((sink.Check(server.get(), testing::SSE_READ)), kTimeout); + scoped_ptr accepted(server->Accept(&accept_addr)); + ASSERT_TRUE(accepted); + EXPECT_FALSE(accept_addr.IsNil()); + EXPECT_EQ(accepted->GetRemoteAddress(), accept_addr); + + // Connected from server perspective, check the addresses are correct. + EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState()); + EXPECT_EQ(server->GetLocalAddress(), accepted->GetLocalAddress()); + EXPECT_EQ(client->GetLocalAddress(), accepted->GetRemoteAddress()); + + // Connected from client perspective, check the addresses are correct. + EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout); + EXPECT_TRUE(sink.Check(client.get(), testing::SSE_OPEN)); + EXPECT_FALSE(sink.Check(client.get(), testing::SSE_CLOSE)); + EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress()); + EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress()); +} + +void SocketTest::ConnectFailInternal(const IPAddress& loopback) { + testing::StreamSink sink; + SocketAddress accept_addr; + + // Create client. + scoped_ptr client( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(client.get()); + + // Create server, but don't listen yet. + scoped_ptr server( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(server.get()); + EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0))); + + // Attempt connect to a non-existent socket. + // We don't connect to the server socket created above, since on + // MacOS it takes about 75 seconds to get back an error! + SocketAddress bogus_addr(loopback, 65535); + EXPECT_EQ(0, client->Connect(bogus_addr)); + + // Wait for connection to fail (ECONNREFUSED). + EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout); + EXPECT_FALSE(sink.Check(client.get(), testing::SSE_OPEN)); + EXPECT_TRUE(sink.Check(client.get(), testing::SSE_ERROR)); + EXPECT_TRUE(client->GetRemoteAddress().IsNil()); + + // Should be no pending server connections. + EXPECT_FALSE(sink.Check(server.get(), testing::SSE_READ)); + EXPECT_TRUE(NULL == server->Accept(&accept_addr)); + EXPECT_EQ(IPAddress(), accept_addr.ipaddr()); +} + +void SocketTest::ConnectWithDnsLookupFailInternal(const IPAddress& loopback) { + testing::StreamSink sink; + SocketAddress accept_addr; + + // Create client. + scoped_ptr client( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(client.get()); + + // Create server, but don't listen yet. + scoped_ptr server( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(server.get()); + EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0))); + + // Attempt connect to a non-existent host. + // We don't connect to the server socket created above, since on + // MacOS it takes about 75 seconds to get back an error! + SocketAddress bogus_dns_addr("not-a-real-hostname", 65535); + EXPECT_EQ(0, client->Connect(bogus_dns_addr)); + + // Wait for connection to fail (EHOSTNOTFOUND). + bool dns_lookup_finished = false; + WAIT_(client->GetState() == AsyncSocket::CS_CLOSED, kTimeout, + dns_lookup_finished); + if (!dns_lookup_finished) { + LOG(LS_WARNING) << "Skipping test; DNS resolution took longer than 5 " + << "seconds."; + return; + } + + EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout); + EXPECT_FALSE(sink.Check(client.get(), testing::SSE_OPEN)); + EXPECT_TRUE(sink.Check(client.get(), testing::SSE_ERROR)); + EXPECT_TRUE(client->GetRemoteAddress().IsNil()); + // Should be no pending server connections. + EXPECT_FALSE(sink.Check(server.get(), testing::SSE_READ)); + EXPECT_TRUE(NULL == server->Accept(&accept_addr)); + EXPECT_TRUE(accept_addr.IsNil()); +} + +void SocketTest::ConnectWithClosedSocketInternal(const IPAddress& loopback) { + // Create server and listen. + scoped_ptr server( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0))); + EXPECT_EQ(0, server->Listen(5)); + + // Create a client and put in to CS_CLOSED state. + scoped_ptr client( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + EXPECT_EQ(0, client->Close()); + EXPECT_EQ(AsyncSocket::CS_CLOSED, client->GetState()); + + // Connect() should reinitialize the socket, and put it in to CS_CONNECTING. + EXPECT_EQ(0, client->Connect(SocketAddress(server->GetLocalAddress()))); + EXPECT_EQ(AsyncSocket::CS_CONNECTING, client->GetState()); +} + +void SocketTest::ConnectWhileNotClosedInternal(const IPAddress& loopback) { + // Create server and listen. + testing::StreamSink sink; + scoped_ptr server( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(server.get()); + EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0))); + EXPECT_EQ(0, server->Listen(5)); + // Create client, connect. + scoped_ptr client( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + EXPECT_EQ(0, client->Connect(SocketAddress(server->GetLocalAddress()))); + EXPECT_EQ(AsyncSocket::CS_CONNECTING, client->GetState()); + // Try to connect again. Should fail, but not interfere with original attempt. + EXPECT_EQ(SOCKET_ERROR, + client->Connect(SocketAddress(server->GetLocalAddress()))); + + // Accept the original connection. + SocketAddress accept_addr; + EXPECT_TRUE_WAIT((sink.Check(server.get(), testing::SSE_READ)), kTimeout); + scoped_ptr accepted(server->Accept(&accept_addr)); + ASSERT_TRUE(accepted); + EXPECT_FALSE(accept_addr.IsNil()); + + // Check the states and addresses. + EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState()); + EXPECT_EQ(server->GetLocalAddress(), accepted->GetLocalAddress()); + EXPECT_EQ(client->GetLocalAddress(), accepted->GetRemoteAddress()); + EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout); + EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress()); + EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress()); + + // Try to connect again, to an unresolved hostname. + // Shouldn't break anything. + EXPECT_EQ(SOCKET_ERROR, + client->Connect(SocketAddress("localhost", + server->GetLocalAddress().port()))); + EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState()); + EXPECT_EQ(AsyncSocket::CS_CONNECTED, client->GetState()); + EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress()); + EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress()); +} + +void SocketTest::ServerCloseDuringConnectInternal(const IPAddress& loopback) { + testing::StreamSink sink; + + // Create client. + scoped_ptr client( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(client.get()); + + // Create server and listen. + scoped_ptr server( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(server.get()); + EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0))); + EXPECT_EQ(0, server->Listen(5)); + + // Attempt connect to listening socket. + EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); + + // Close down the server while the socket is in the accept queue. + EXPECT_TRUE_WAIT(sink.Check(server.get(), testing::SSE_READ), kTimeout); + server->Close(); + + // This should fail the connection for the client. Clean up. + EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout); + EXPECT_TRUE(sink.Check(client.get(), testing::SSE_ERROR)); + client->Close(); +} + +void SocketTest::ClientCloseDuringConnectInternal(const IPAddress& loopback) { + testing::StreamSink sink; + SocketAddress accept_addr; + + // Create client. + scoped_ptr client( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(client.get()); + + // Create server and listen. + scoped_ptr server( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(server.get()); + EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0))); + EXPECT_EQ(0, server->Listen(5)); + + // Attempt connect to listening socket. + EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); + + // Close down the client while the socket is in the accept queue. + EXPECT_TRUE_WAIT(sink.Check(server.get(), testing::SSE_READ), kTimeout); + client->Close(); + + // The connection should still be able to be accepted. + scoped_ptr accepted(server->Accept(&accept_addr)); + ASSERT_TRUE(accepted); + sink.Monitor(accepted.get()); + EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState()); + + // The accepted socket should then close (possibly with err, timing-related) + EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, accepted->GetState(), kTimeout); + EXPECT_TRUE(sink.Check(accepted.get(), testing::SSE_CLOSE) || + sink.Check(accepted.get(), testing::SSE_ERROR)); + + // The client should not get a close event. + EXPECT_FALSE(sink.Check(client.get(), testing::SSE_CLOSE)); +} + +void SocketTest::ServerCloseInternal(const IPAddress& loopback) { + testing::StreamSink sink; + SocketAddress accept_addr; + + // Create client. + scoped_ptr client( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(client.get()); + + // Create server and listen. + scoped_ptr server( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(server.get()); + EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0))); + EXPECT_EQ(0, server->Listen(5)); + + // Attempt connection. + EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); + + // Accept connection. + EXPECT_TRUE_WAIT((sink.Check(server.get(), testing::SSE_READ)), kTimeout); + scoped_ptr accepted(server->Accept(&accept_addr)); + ASSERT_TRUE(accepted); + sink.Monitor(accepted.get()); + + // Both sides are now connected. + EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout); + EXPECT_TRUE(sink.Check(client.get(), testing::SSE_OPEN)); + EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress()); + EXPECT_EQ(accepted->GetRemoteAddress(), client->GetLocalAddress()); + + // Send data to the client, and then close the connection. + EXPECT_EQ(1, accepted->Send("a", 1)); + accepted->Close(); + EXPECT_EQ(AsyncSocket::CS_CLOSED, accepted->GetState()); + + // Expect that the client is notified, and has not yet closed. + EXPECT_TRUE_WAIT(sink.Check(client.get(), testing::SSE_READ), kTimeout); + EXPECT_FALSE(sink.Check(client.get(), testing::SSE_CLOSE)); + EXPECT_EQ(AsyncSocket::CS_CONNECTED, client->GetState()); + + // Ensure the data can be read. + char buffer[10]; + EXPECT_EQ(1, client->Recv(buffer, sizeof(buffer))); + EXPECT_EQ('a', buffer[0]); + + // Now we should close, but the remote address will remain. + EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout); + EXPECT_TRUE(sink.Check(client.get(), testing::SSE_CLOSE)); + EXPECT_FALSE(client->GetRemoteAddress().IsAnyIP()); + + // The closer should not get a close signal. + EXPECT_FALSE(sink.Check(accepted.get(), testing::SSE_CLOSE)); + EXPECT_TRUE(accepted->GetRemoteAddress().IsNil()); + + // And the closee should only get a single signal. + Thread::Current()->ProcessMessages(0); + EXPECT_FALSE(sink.Check(client.get(), testing::SSE_CLOSE)); + + // Close down the client and ensure all is good. + client->Close(); + EXPECT_FALSE(sink.Check(client.get(), testing::SSE_CLOSE)); + EXPECT_TRUE(client->GetRemoteAddress().IsNil()); +} + +class SocketCloser : public sigslot::has_slots<> { + public: + void OnClose(AsyncSocket* socket, int error) { + socket->Close(); // Deleting here would blow up the vector of handlers + // for the socket's signal. + } +}; + +void SocketTest::CloseInClosedCallbackInternal(const IPAddress& loopback) { + testing::StreamSink sink; + SocketCloser closer; + SocketAddress accept_addr; + + // Create client. + scoped_ptr client( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(client.get()); + client->SignalCloseEvent.connect(&closer, &SocketCloser::OnClose); + + // Create server and listen. + scoped_ptr server( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(server.get()); + EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0))); + EXPECT_EQ(0, server->Listen(5)); + + // Attempt connection. + EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); + + // Accept connection. + EXPECT_TRUE_WAIT((sink.Check(server.get(), testing::SSE_READ)), kTimeout); + scoped_ptr accepted(server->Accept(&accept_addr)); + ASSERT_TRUE(accepted); + sink.Monitor(accepted.get()); + + // Both sides are now connected. + EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout); + EXPECT_TRUE(sink.Check(client.get(), testing::SSE_OPEN)); + EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress()); + EXPECT_EQ(accepted->GetRemoteAddress(), client->GetLocalAddress()); + + // Send data to the client, and then close the connection. + accepted->Close(); + EXPECT_EQ(AsyncSocket::CS_CLOSED, accepted->GetState()); + + // Expect that the client is notified, and has not yet closed. + EXPECT_FALSE(sink.Check(client.get(), testing::SSE_CLOSE)); + EXPECT_EQ(AsyncSocket::CS_CONNECTED, client->GetState()); + + // Now we should be closed and invalidated + EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout); + EXPECT_TRUE(sink.Check(client.get(), testing::SSE_CLOSE)); + EXPECT_TRUE(Socket::CS_CLOSED == client->GetState()); +} + +class Sleeper : public MessageHandler { + public: + Sleeper() {} + void OnMessage(Message* msg) { + Thread::Current()->SleepMs(500); + } +}; + +void SocketTest::SocketServerWaitInternal(const IPAddress& loopback) { + testing::StreamSink sink; + SocketAddress accept_addr; + + // Create & connect server and client sockets. + scoped_ptr client( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + scoped_ptr server( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(client.get()); + sink.Monitor(server.get()); + EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0))); + EXPECT_EQ(0, server->Listen(5)); + + EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); + EXPECT_TRUE_WAIT((sink.Check(server.get(), testing::SSE_READ)), kTimeout); + + scoped_ptr accepted(server->Accept(&accept_addr)); + ASSERT_TRUE(accepted); + sink.Monitor(accepted.get()); + EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState()); + EXPECT_EQ(server->GetLocalAddress(), accepted->GetLocalAddress()); + EXPECT_EQ(client->GetLocalAddress(), accepted->GetRemoteAddress()); + + EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout); + EXPECT_TRUE(sink.Check(client.get(), testing::SSE_OPEN)); + EXPECT_FALSE(sink.Check(client.get(), testing::SSE_CLOSE)); + EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress()); + EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress()); + + // Do an i/o operation, triggering an eventual callback. + EXPECT_FALSE(sink.Check(accepted.get(), testing::SSE_READ)); + char buf[1024] = {0}; + + EXPECT_EQ(1024, client->Send(buf, 1024)); + EXPECT_FALSE(sink.Check(accepted.get(), testing::SSE_READ)); + + // Shouldn't signal when blocked in a thread Send, where process_io is false. + scoped_ptr thread(new Thread()); + thread->Start(); + Sleeper sleeper; + TypedMessageData data(client.get()); + thread->Send(&sleeper, 0, &data); + EXPECT_FALSE(sink.Check(accepted.get(), testing::SSE_READ)); + + // But should signal when process_io is true. + EXPECT_TRUE_WAIT((sink.Check(accepted.get(), testing::SSE_READ)), kTimeout); + EXPECT_LT(0, accepted->Recv(buf, 1024)); +} + +void SocketTest::TcpInternal(const IPAddress& loopback) { + testing::StreamSink sink; + SocketAddress accept_addr; + + // Create test data. + const size_t kDataSize = 1024 * 1024; + scoped_ptr send_buffer(new char[kDataSize]); + scoped_ptr recv_buffer(new char[kDataSize]); + size_t send_pos = 0, recv_pos = 0; + for (size_t i = 0; i < kDataSize; ++i) { + send_buffer[i] = static_cast(i % 256); + recv_buffer[i] = 0; + } + + // Create client. + scoped_ptr client( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(client.get()); + + // Create server and listen. + scoped_ptr server( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(server.get()); + EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0))); + EXPECT_EQ(0, server->Listen(5)); + + // Attempt connection. + EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); + + // Accept connection. + EXPECT_TRUE_WAIT((sink.Check(server.get(), testing::SSE_READ)), kTimeout); + scoped_ptr accepted(server->Accept(&accept_addr)); + ASSERT_TRUE(accepted); + sink.Monitor(accepted.get()); + + // Both sides are now connected. + EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout); + EXPECT_TRUE(sink.Check(client.get(), testing::SSE_OPEN)); + EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress()); + EXPECT_EQ(accepted->GetRemoteAddress(), client->GetLocalAddress()); + + // Send and receive a bunch of data. + bool send_waiting_for_writability = false; + bool send_expect_success = true; + bool recv_waiting_for_readability = true; + bool recv_expect_success = false; + int data_in_flight = 0; + while (recv_pos < kDataSize) { + // Send as much as we can if we've been cleared to send. + while (!send_waiting_for_writability && send_pos < kDataSize) { + int tosend = static_cast(kDataSize - send_pos); + int sent = accepted->Send(send_buffer.get() + send_pos, tosend); + if (send_expect_success) { + // The first Send() after connecting or getting writability should + // succeed and send some data. + EXPECT_GT(sent, 0); + send_expect_success = false; + } + if (sent >= 0) { + EXPECT_LE(sent, tosend); + send_pos += sent; + data_in_flight += sent; + } else { + ASSERT_TRUE(accepted->IsBlocking()); + send_waiting_for_writability = true; + } + } + + // Read all the sent data. + while (data_in_flight > 0) { + if (recv_waiting_for_readability) { + // Wait until data is available. + EXPECT_TRUE_WAIT(sink.Check(client.get(), testing::SSE_READ), kTimeout); + recv_waiting_for_readability = false; + recv_expect_success = true; + } + + // Receive as much as we can get in a single recv call. + int rcvd = client->Recv(recv_buffer.get() + recv_pos, + kDataSize - recv_pos); + + if (recv_expect_success) { + // The first Recv() after getting readability should succeed and receive + // some data. + // TODO: The following line is disabled due to flakey pulse + // builds. Re-enable if/when possible. + // EXPECT_GT(rcvd, 0); + recv_expect_success = false; + } + if (rcvd >= 0) { + EXPECT_LE(rcvd, data_in_flight); + recv_pos += rcvd; + data_in_flight -= rcvd; + } else { + ASSERT_TRUE(client->IsBlocking()); + recv_waiting_for_readability = true; + } + } + + // Once all that we've sent has been rcvd, expect to be able to send again. + if (send_waiting_for_writability) { + EXPECT_TRUE_WAIT(sink.Check(accepted.get(), testing::SSE_WRITE), + kTimeout); + send_waiting_for_writability = false; + send_expect_success = true; + } + } + + // The received data matches the sent data. + EXPECT_EQ(kDataSize, send_pos); + EXPECT_EQ(kDataSize, recv_pos); + EXPECT_EQ(0, memcmp(recv_buffer.get(), send_buffer.get(), kDataSize)); + + // Close down. + accepted->Close(); + EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout); + EXPECT_TRUE(sink.Check(client.get(), testing::SSE_CLOSE)); + client->Close(); +} + +void SocketTest::SingleFlowControlCallbackInternal(const IPAddress& loopback) { + testing::StreamSink sink; + SocketAddress accept_addr; + + // Create client. + scoped_ptr client( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(client.get()); + + // Create server and listen. + scoped_ptr server( + ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM)); + sink.Monitor(server.get()); + EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0))); + EXPECT_EQ(0, server->Listen(5)); + + // Attempt connection. + EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); + + // Accept connection. + EXPECT_TRUE_WAIT((sink.Check(server.get(), testing::SSE_READ)), kTimeout); + scoped_ptr accepted(server->Accept(&accept_addr)); + ASSERT_TRUE(accepted); + sink.Monitor(accepted.get()); + + // Both sides are now connected. + EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout); + EXPECT_TRUE(sink.Check(client.get(), testing::SSE_OPEN)); + EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress()); + EXPECT_EQ(accepted->GetRemoteAddress(), client->GetLocalAddress()); + + // Expect a writable callback from the connect. + EXPECT_TRUE_WAIT(sink.Check(accepted.get(), testing::SSE_WRITE), kTimeout); + + // Fill the socket buffer. + char buf[1024 * 16] = {0}; + int sends = 0; + while (++sends && accepted->Send(&buf, ARRAY_SIZE(buf)) != -1) {} + EXPECT_TRUE(accepted->IsBlocking()); + + // Wait until data is available. + EXPECT_TRUE_WAIT(sink.Check(client.get(), testing::SSE_READ), kTimeout); + + // Pull data. + for (int i = 0; i < sends; ++i) { + client->Recv(buf, ARRAY_SIZE(buf)); + } + + // Expect at least one additional writable callback. + EXPECT_TRUE_WAIT(sink.Check(accepted.get(), testing::SSE_WRITE), kTimeout); + + // Adding data in response to the writeable callback shouldn't cause infinite + // callbacks. + int extras = 0; + for (int i = 0; i < 100; ++i) { + accepted->Send(&buf, ARRAY_SIZE(buf)); + rtc::Thread::Current()->ProcessMessages(1); + if (sink.Check(accepted.get(), testing::SSE_WRITE)) { + extras++; + } + } + EXPECT_LT(extras, 2); + + // Close down. + accepted->Close(); + client->Close(); +} + +void SocketTest::UdpInternal(const IPAddress& loopback) { + SocketAddress empty = EmptySocketAddressWithFamily(loopback.family()); + // Test basic bind and connect behavior. + AsyncSocket* socket = + ss_->CreateAsyncSocket(loopback.family(), SOCK_DGRAM); + EXPECT_EQ(AsyncSocket::CS_CLOSED, socket->GetState()); + EXPECT_EQ(0, socket->Bind(SocketAddress(loopback, 0))); + SocketAddress addr1 = socket->GetLocalAddress(); + EXPECT_EQ(0, socket->Connect(addr1)); + EXPECT_EQ(AsyncSocket::CS_CONNECTED, socket->GetState()); + socket->Close(); + EXPECT_EQ(AsyncSocket::CS_CLOSED, socket->GetState()); + delete socket; + + // Test send/receive behavior. + scoped_ptr client1( + new TestClient(AsyncUDPSocket::Create(ss_, addr1))); + scoped_ptr client2( + new TestClient(AsyncUDPSocket::Create(ss_, empty))); + + SocketAddress addr2; + EXPECT_EQ(3, client2->SendTo("foo", 3, addr1)); + EXPECT_TRUE(client1->CheckNextPacket("foo", 3, &addr2)); + + SocketAddress addr3; + EXPECT_EQ(6, client1->SendTo("bizbaz", 6, addr2)); + EXPECT_TRUE(client2->CheckNextPacket("bizbaz", 6, &addr3)); + EXPECT_EQ(addr3, addr1); + // TODO: figure out what the intent is here + for (int i = 0; i < 10; ++i) { + client2.reset(new TestClient(AsyncUDPSocket::Create(ss_, empty))); + + SocketAddress addr4; + EXPECT_EQ(3, client2->SendTo("foo", 3, addr1)); + EXPECT_TRUE(client1->CheckNextPacket("foo", 3, &addr4)); + EXPECT_EQ(addr4.ipaddr(), addr2.ipaddr()); + + SocketAddress addr5; + EXPECT_EQ(6, client1->SendTo("bizbaz", 6, addr4)); + EXPECT_TRUE(client2->CheckNextPacket("bizbaz", 6, &addr5)); + EXPECT_EQ(addr5, addr1); + + addr2 = addr4; + } +} + +void SocketTest::UdpReadyToSend(const IPAddress& loopback) { + SocketAddress empty = EmptySocketAddressWithFamily(loopback.family()); + // RFC 5737 - The blocks 192.0.2.0/24 (TEST-NET-1) ... are provided for use in + // documentation. + // RFC 3849 - 2001:DB8::/32 as a documentation-only prefix. + std::string dest = (loopback.family() == AF_INET6) ? + "2001:db8::1" : "192.0.2.0"; + SocketAddress test_addr(dest, 2345); + + // Test send + scoped_ptr client( + new TestClient(AsyncUDPSocket::Create(ss_, empty))); + int test_packet_size = 1200; + rtc::scoped_ptr test_packet(new char[test_packet_size]); + // Init the test packet just to avoid memcheck warning. + memset(test_packet.get(), 0, test_packet_size); + // Set the send buffer size to the same size as the test packet to have a + // better chance to get EWOULDBLOCK. + int send_buffer_size = test_packet_size; +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) + send_buffer_size /= 2; +#endif + client->SetOption(rtc::Socket::OPT_SNDBUF, send_buffer_size); + + int error = 0; + uint32 start_ms = Time(); + int sent_packet_num = 0; + int expected_error = EWOULDBLOCK; + while (start_ms + kTimeout > Time()) { + int ret = client->SendTo(test_packet.get(), test_packet_size, test_addr); + ++sent_packet_num; + if (ret != test_packet_size) { + error = client->GetError(); + if (error == expected_error) { + LOG(LS_INFO) << "Got expected error code after sending " + << sent_packet_num << " packets."; + break; + } + } + } + EXPECT_EQ(expected_error, error); + EXPECT_FALSE(client->ready_to_send()); + EXPECT_TRUE_WAIT(client->ready_to_send(), kTimeout); + LOG(LS_INFO) << "Got SignalReadyToSend"; +} + +void SocketTest::GetSetOptionsInternal(const IPAddress& loopback) { + rtc::scoped_ptr socket( + ss_->CreateAsyncSocket(loopback.family(), SOCK_DGRAM)); + socket->Bind(SocketAddress(loopback, 0)); + + // Check SNDBUF/RCVBUF. + const int desired_size = 12345; +#if defined(WEBRTC_LINUX) + // Yes, really. It's in the kernel source. + const int expected_size = desired_size * 2; +#else // !WEBRTC_LINUX + const int expected_size = desired_size; +#endif // !WEBRTC_LINUX + int recv_size = 0; + int send_size = 0; + // get the initial sizes + ASSERT_NE(-1, socket->GetOption(Socket::OPT_RCVBUF, &recv_size)); + ASSERT_NE(-1, socket->GetOption(Socket::OPT_SNDBUF, &send_size)); + // set our desired sizes + ASSERT_NE(-1, socket->SetOption(Socket::OPT_RCVBUF, desired_size)); + ASSERT_NE(-1, socket->SetOption(Socket::OPT_SNDBUF, desired_size)); + // get the sizes again + ASSERT_NE(-1, socket->GetOption(Socket::OPT_RCVBUF, &recv_size)); + ASSERT_NE(-1, socket->GetOption(Socket::OPT_SNDBUF, &send_size)); + // make sure they are right + ASSERT_EQ(expected_size, recv_size); + ASSERT_EQ(expected_size, send_size); + + // Check that we can't set NODELAY on a UDP socket. + int current_nd, desired_nd = 1; + ASSERT_EQ(-1, socket->GetOption(Socket::OPT_NODELAY, ¤t_nd)); + ASSERT_EQ(-1, socket->SetOption(Socket::OPT_NODELAY, desired_nd)); + + // Skip the esimate MTU test for IPv6 for now. + if (loopback.family() != AF_INET6) { + // Try estimating MTU. + rtc::scoped_ptr + mtu_socket( + ss_->CreateAsyncSocket(loopback.family(), SOCK_DGRAM)); + mtu_socket->Bind(SocketAddress(loopback, 0)); + uint16 mtu; + // should fail until we connect + ASSERT_EQ(-1, mtu_socket->EstimateMTU(&mtu)); + mtu_socket->Connect(SocketAddress(loopback, 0)); +#if defined(WEBRTC_WIN) + // now it should succeed + ASSERT_NE(-1, mtu_socket->EstimateMTU(&mtu)); + ASSERT_GE(mtu, 1492); // should be at least the 1492 "plateau" on localhost +#elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + // except on WEBRTC_MAC && !WEBRTC_IOS, where it's not yet implemented + ASSERT_EQ(-1, mtu_socket->EstimateMTU(&mtu)); +#else + // and the behavior seems unpredictable on Linux, + // failing on the build machine + // but succeeding on my Ubiquity instance. +#endif + } +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socket_unittest.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socket_unittest.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/socket_unittest.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/socket_unittest.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,88 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SOCKET_UNITTEST_H_ +#define WEBRTC_BASE_SOCKET_UNITTEST_H_ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +// Generic socket tests, to be used when testing individual socketservers. +// Derive your specific test class from SocketTest, install your +// socketserver, and call the SocketTest test methods. +class SocketTest : public testing::Test { + protected: + SocketTest() : ss_(NULL), kIPv4Loopback(INADDR_LOOPBACK), + kIPv6Loopback(in6addr_loopback) {} + virtual void SetUp() { ss_ = Thread::Current()->socketserver(); } + void TestConnectIPv4(); + void TestConnectIPv6(); + void TestConnectWithDnsLookupIPv4(); + void TestConnectWithDnsLookupIPv6(); + void TestConnectFailIPv4(); + void TestConnectFailIPv6(); + void TestConnectWithDnsLookupFailIPv4(); + void TestConnectWithDnsLookupFailIPv6(); + void TestConnectWithClosedSocketIPv4(); + void TestConnectWithClosedSocketIPv6(); + void TestConnectWhileNotClosedIPv4(); + void TestConnectWhileNotClosedIPv6(); + void TestServerCloseDuringConnectIPv4(); + void TestServerCloseDuringConnectIPv6(); + void TestClientCloseDuringConnectIPv4(); + void TestClientCloseDuringConnectIPv6(); + void TestServerCloseIPv4(); + void TestServerCloseIPv6(); + void TestCloseInClosedCallbackIPv4(); + void TestCloseInClosedCallbackIPv6(); + void TestSocketServerWaitIPv4(); + void TestSocketServerWaitIPv6(); + void TestTcpIPv4(); + void TestTcpIPv6(); + void TestSingleFlowControlCallbackIPv4(); + void TestSingleFlowControlCallbackIPv6(); + void TestUdpIPv4(); + void TestUdpIPv6(); + void TestUdpReadyToSendIPv4(); + void TestUdpReadyToSendIPv6(); + void TestGetSetOptionsIPv4(); + void TestGetSetOptionsIPv6(); + + private: + void ConnectInternal(const IPAddress& loopback); + void ConnectWithDnsLookupInternal(const IPAddress& loopback, + const std::string& host); + void ConnectFailInternal(const IPAddress& loopback); + + void ConnectWithDnsLookupFailInternal(const IPAddress& loopback); + void ConnectWithClosedSocketInternal(const IPAddress& loopback); + void ConnectWhileNotClosedInternal(const IPAddress& loopback); + void ServerCloseDuringConnectInternal(const IPAddress& loopback); + void ClientCloseDuringConnectInternal(const IPAddress& loopback); + void ServerCloseInternal(const IPAddress& loopback); + void CloseInClosedCallbackInternal(const IPAddress& loopback); + void SocketServerWaitInternal(const IPAddress& loopback); + void TcpInternal(const IPAddress& loopback); + void SingleFlowControlCallbackInternal(const IPAddress& loopback); + void UdpInternal(const IPAddress& loopback); + void UdpReadyToSend(const IPAddress& loopback); + void GetSetOptionsInternal(const IPAddress& loopback); + + static const int kTimeout = 5000; // ms + SocketServer* ss_; + const IPAddress kIPv4Loopback; + const IPAddress kIPv6Loopback; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_SOCKET_UNITTEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ssladapter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ssladapter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ssladapter.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ssladapter.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,97 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#include "webrtc/base/ssladapter.h" + +#include "webrtc/base/sslconfig.h" + +#if SSL_USE_SCHANNEL + +#include "schanneladapter.h" + +#elif SSL_USE_OPENSSL // && !SSL_USE_SCHANNEL + +#include "openssladapter.h" + +#elif SSL_USE_NSS // && !SSL_USE_CHANNEL && !SSL_USE_OPENSSL + +#include "nssstreamadapter.h" + +#endif // SSL_USE_OPENSSL && !SSL_USE_SCHANNEL && !SSL_USE_NSS + +/////////////////////////////////////////////////////////////////////////////// + +namespace rtc { + +SSLAdapter* +SSLAdapter::Create(AsyncSocket* socket) { +#if SSL_USE_SCHANNEL + return new SChannelAdapter(socket); +#elif SSL_USE_OPENSSL // && !SSL_USE_SCHANNEL + return new OpenSSLAdapter(socket); +#else // !SSL_USE_OPENSSL && !SSL_USE_SCHANNEL + delete socket; + return NULL; +#endif // !SSL_USE_OPENSSL && !SSL_USE_SCHANNEL +} + +/////////////////////////////////////////////////////////////////////////////// + +#if SSL_USE_OPENSSL + +bool InitializeSSL(VerificationCallback callback) { + return OpenSSLAdapter::InitializeSSL(callback); +} + +bool InitializeSSLThread() { + return OpenSSLAdapter::InitializeSSLThread(); +} + +bool CleanupSSL() { + return OpenSSLAdapter::CleanupSSL(); +} + +#elif SSL_USE_NSS // !SSL_USE_OPENSSL + +bool InitializeSSL(VerificationCallback callback) { + return NSSContext::InitializeSSL(callback); +} + +bool InitializeSSLThread() { + return NSSContext::InitializeSSLThread(); +} + +bool CleanupSSL() { + return NSSContext::CleanupSSL(); +} + +#else // !SSL_USE_OPENSSL && !SSL_USE_NSS + +bool InitializeSSL(VerificationCallback callback) { + return true; +} + +bool InitializeSSLThread() { + return true; +} + +bool CleanupSSL() { + return true; +} + +#endif // !SSL_USE_OPENSSL && !SSL_USE_NSS + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ssladapter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ssladapter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ssladapter.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ssladapter.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,61 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SSLADAPTER_H_ +#define WEBRTC_BASE_SSLADAPTER_H_ + +#include "webrtc/base/asyncsocket.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// + +class SSLAdapter : public AsyncSocketAdapter { + public: + explicit SSLAdapter(AsyncSocket* socket) + : AsyncSocketAdapter(socket), ignore_bad_cert_(false) { } + + bool ignore_bad_cert() const { return ignore_bad_cert_; } + void set_ignore_bad_cert(bool ignore) { ignore_bad_cert_ = ignore; } + + // StartSSL returns 0 if successful. + // If StartSSL is called while the socket is closed or connecting, the SSL + // negotiation will begin as soon as the socket connects. + virtual int StartSSL(const char* hostname, bool restartable) = 0; + + // Create the default SSL adapter for this platform. On failure, returns NULL + // and deletes |socket|. Otherwise, the returned SSLAdapter takes ownership + // of |socket|. + static SSLAdapter* Create(AsyncSocket* socket); + + private: + // If true, the server certificate need not match the configured hostname. + bool ignore_bad_cert_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +typedef bool (*VerificationCallback)(void* cert); + +// Call this on the main thread, before using SSL. +// Call CleanupSSLThread when finished with SSL. +bool InitializeSSL(VerificationCallback callback = NULL); + +// Call to initialize additional threads. +bool InitializeSSLThread(); + +// Call to cleanup additional threads, and also the main thread. +bool CleanupSSL(); + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_SSLADAPTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ssladapter_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ssladapter_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/ssladapter_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/ssladapter_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,334 @@ +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/gunit.h" +#include "webrtc/base/ipaddress.h" +#include "webrtc/base/socketstream.h" +#include "webrtc/base/ssladapter.h" +#include "webrtc/base/sslstreamadapter.h" +#include "webrtc/base/stream.h" +#include "webrtc/base/virtualsocketserver.h" + +static const int kTimeout = 5000; + +static rtc::AsyncSocket* CreateSocket(const rtc::SSLMode& ssl_mode) { + rtc::SocketAddress address(rtc::IPAddress(INADDR_ANY), 0); + + rtc::AsyncSocket* socket = rtc::Thread::Current()-> + socketserver()->CreateAsyncSocket( + address.family(), (ssl_mode == rtc::SSL_MODE_DTLS) ? + SOCK_DGRAM : SOCK_STREAM); + socket->Bind(address); + + return socket; +} + +static std::string GetSSLProtocolName(const rtc::SSLMode& ssl_mode) { + return (ssl_mode == rtc::SSL_MODE_DTLS) ? "DTLS" : "TLS"; +} + +class SSLAdapterTestDummyClient : public sigslot::has_slots<> { + public: + explicit SSLAdapterTestDummyClient(const rtc::SSLMode& ssl_mode) + : ssl_mode_(ssl_mode) { + rtc::AsyncSocket* socket = CreateSocket(ssl_mode_); + + ssl_adapter_.reset(rtc::SSLAdapter::Create(socket)); + + // Ignore any certificate errors for the purpose of testing. + // Note: We do this only because we don't have a real certificate. + // NEVER USE THIS IN PRODUCTION CODE! + ssl_adapter_->set_ignore_bad_cert(true); + + ssl_adapter_->SignalReadEvent.connect(this, + &SSLAdapterTestDummyClient::OnSSLAdapterReadEvent); + ssl_adapter_->SignalCloseEvent.connect(this, + &SSLAdapterTestDummyClient::OnSSLAdapterCloseEvent); + } + + rtc::AsyncSocket::ConnState GetState() const { + return ssl_adapter_->GetState(); + } + + const std::string& GetReceivedData() const { + return data_; + } + + int Connect(const std::string& hostname, const rtc::SocketAddress& address) { + LOG(LS_INFO) << "Starting " << GetSSLProtocolName(ssl_mode_) + << " handshake with " << hostname; + + if (ssl_adapter_->StartSSL(hostname.c_str(), false) != 0) { + return -1; + } + + LOG(LS_INFO) << "Initiating connection with " << address; + + return ssl_adapter_->Connect(address); + } + + int Close() { + return ssl_adapter_->Close(); + } + + int Send(const std::string& message) { + LOG(LS_INFO) << "Client sending '" << message << "'"; + + return ssl_adapter_->Send(message.data(), message.length()); + } + + void OnSSLAdapterReadEvent(rtc::AsyncSocket* socket) { + char buffer[4096] = ""; + + // Read data received from the server and store it in our internal buffer. + int read = socket->Recv(buffer, sizeof(buffer) - 1); + if (read != -1) { + buffer[read] = '\0'; + + LOG(LS_INFO) << "Client received '" << buffer << "'"; + + data_ += buffer; + } + } + + void OnSSLAdapterCloseEvent(rtc::AsyncSocket* socket, int error) { + // OpenSSLAdapter signals handshake failure with a close event, but without + // closing the socket! Let's close the socket here. This way GetState() can + // return CS_CLOSED after failure. + if (socket->GetState() != rtc::AsyncSocket::CS_CLOSED) { + socket->Close(); + } + } + + private: + const rtc::SSLMode ssl_mode_; + + rtc::scoped_ptr ssl_adapter_; + + std::string data_; +}; + +class SSLAdapterTestDummyServer : public sigslot::has_slots<> { + public: + explicit SSLAdapterTestDummyServer(const rtc::SSLMode& ssl_mode) + : ssl_mode_(ssl_mode) { + // Generate a key pair and a certificate for this host. + ssl_identity_.reset(rtc::SSLIdentity::Generate(GetHostname())); + + server_socket_.reset(CreateSocket(ssl_mode_)); + + server_socket_->SignalReadEvent.connect(this, + &SSLAdapterTestDummyServer::OnServerSocketReadEvent); + + server_socket_->Listen(1); + + LOG(LS_INFO) << ((ssl_mode_ == rtc::SSL_MODE_DTLS) ? "UDP" : "TCP") + << " server listening on " << server_socket_->GetLocalAddress(); + } + + rtc::SocketAddress GetAddress() const { + return server_socket_->GetLocalAddress(); + } + + std::string GetHostname() const { + // Since we don't have a real certificate anyway, the value here doesn't + // really matter. + return "example.com"; + } + + const std::string& GetReceivedData() const { + return data_; + } + + int Send(const std::string& message) { + if (ssl_stream_adapter_ == NULL + || ssl_stream_adapter_->GetState() != rtc::SS_OPEN) { + // No connection yet. + return -1; + } + + LOG(LS_INFO) << "Server sending '" << message << "'"; + + size_t written; + int error; + + rtc::StreamResult r = ssl_stream_adapter_->Write(message.data(), + message.length(), &written, &error); + if (r == rtc::SR_SUCCESS) { + return written; + } else { + return -1; + } + } + + void OnServerSocketReadEvent(rtc::AsyncSocket* socket) { + if (ssl_stream_adapter_ != NULL) { + // Only a single connection is supported. + return; + } + + rtc::SocketAddress address; + rtc::AsyncSocket* new_socket = socket->Accept(&address); + rtc::SocketStream* stream = new rtc::SocketStream(new_socket); + + ssl_stream_adapter_.reset(rtc::SSLStreamAdapter::Create(stream)); + ssl_stream_adapter_->SetServerRole(); + + // SSLStreamAdapter is normally used for peer-to-peer communication, but + // here we're testing communication between a client and a server + // (e.g. a WebRTC-based application and an RFC 5766 TURN server), where + // clients are not required to provide a certificate during handshake. + // Accordingly, we must disable client authentication here. + ssl_stream_adapter_->set_client_auth_enabled(false); + + ssl_stream_adapter_->SetIdentity(ssl_identity_->GetReference()); + + // Set a bogus peer certificate digest. + unsigned char digest[20]; + size_t digest_len = sizeof(digest); + ssl_stream_adapter_->SetPeerCertificateDigest(rtc::DIGEST_SHA_1, digest, + digest_len); + + ssl_stream_adapter_->StartSSLWithPeer(); + + ssl_stream_adapter_->SignalEvent.connect(this, + &SSLAdapterTestDummyServer::OnSSLStreamAdapterEvent); + } + + void OnSSLStreamAdapterEvent(rtc::StreamInterface* stream, int sig, int err) { + if (sig & rtc::SE_READ) { + char buffer[4096] = ""; + + size_t read; + int error; + + // Read data received from the client and store it in our internal + // buffer. + rtc::StreamResult r = stream->Read(buffer, + sizeof(buffer) - 1, &read, &error); + if (r == rtc::SR_SUCCESS) { + buffer[read] = '\0'; + + LOG(LS_INFO) << "Server received '" << buffer << "'"; + + data_ += buffer; + } + } + } + + private: + const rtc::SSLMode ssl_mode_; + + rtc::scoped_ptr server_socket_; + rtc::scoped_ptr ssl_stream_adapter_; + + rtc::scoped_ptr ssl_identity_; + + std::string data_; +}; + +class SSLAdapterTestBase : public testing::Test, + public sigslot::has_slots<> { + public: + explicit SSLAdapterTestBase(const rtc::SSLMode& ssl_mode) + : ssl_mode_(ssl_mode), + ss_scope_(new rtc::VirtualSocketServer(NULL)), + server_(new SSLAdapterTestDummyServer(ssl_mode_)), + client_(new SSLAdapterTestDummyClient(ssl_mode_)), + handshake_wait_(kTimeout) { + } + + void SetHandshakeWait(int wait) { + handshake_wait_ = wait; + } + + void TestHandshake(bool expect_success) { + int rv; + + // The initial state is CS_CLOSED + ASSERT_EQ(rtc::AsyncSocket::CS_CLOSED, client_->GetState()); + + rv = client_->Connect(server_->GetHostname(), server_->GetAddress()); + ASSERT_EQ(0, rv); + + // Now the state should be CS_CONNECTING + ASSERT_EQ(rtc::AsyncSocket::CS_CONNECTING, client_->GetState()); + + if (expect_success) { + // If expecting success, the client should end up in the CS_CONNECTED + // state after handshake. + EXPECT_EQ_WAIT(rtc::AsyncSocket::CS_CONNECTED, client_->GetState(), + handshake_wait_); + + LOG(LS_INFO) << GetSSLProtocolName(ssl_mode_) << " handshake complete."; + + } else { + // On handshake failure the client should end up in the CS_CLOSED state. + EXPECT_EQ_WAIT(rtc::AsyncSocket::CS_CLOSED, client_->GetState(), + handshake_wait_); + + LOG(LS_INFO) << GetSSLProtocolName(ssl_mode_) << " handshake failed."; + } + } + + void TestTransfer(const std::string& message) { + int rv; + + rv = client_->Send(message); + ASSERT_EQ(static_cast(message.length()), rv); + + // The server should have received the client's message. + EXPECT_EQ_WAIT(message, server_->GetReceivedData(), kTimeout); + + rv = server_->Send(message); + ASSERT_EQ(static_cast(message.length()), rv); + + // The client should have received the server's message. + EXPECT_EQ_WAIT(message, client_->GetReceivedData(), kTimeout); + + LOG(LS_INFO) << "Transfer complete."; + } + + private: + const rtc::SSLMode ssl_mode_; + + const rtc::SocketServerScope ss_scope_; + + rtc::scoped_ptr server_; + rtc::scoped_ptr client_; + + int handshake_wait_; +}; + +class SSLAdapterTestTLS : public SSLAdapterTestBase { + public: + SSLAdapterTestTLS() : SSLAdapterTestBase(rtc::SSL_MODE_TLS) {} +}; + + +#if SSL_USE_OPENSSL + +// Basic tests: TLS + +// Test that handshake works +TEST_F(SSLAdapterTestTLS, TestTLSConnect) { + TestHandshake(true); +} + +// Test transfer between client and server +TEST_F(SSLAdapterTestTLS, TestTLSTransfer) { + TestHandshake(true); + TestTransfer("Hello, world!"); +} + +#endif // SSL_USE_OPENSSL + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslconfig.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslconfig.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslconfig.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslconfig.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,33 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SSLCONFIG_H_ +#define WEBRTC_BASE_SSLCONFIG_H_ + +// If no preference has been indicated, default to SChannel on Windows and +// OpenSSL everywhere else, if it is available. +#if !defined(SSL_USE_SCHANNEL) && !defined(SSL_USE_OPENSSL) && \ + !defined(SSL_USE_NSS) +#if defined(WEBRTC_WIN) + +#define SSL_USE_SCHANNEL 1 + +#else // defined(WEBRTC_WIN) + +#if defined(HAVE_OPENSSL_SSL_H) +#define SSL_USE_OPENSSL 1 +#elif defined(HAVE_NSS_SSL_H) +#define SSL_USE_NSS 1 +#endif + +#endif // !defined(WEBRTC_WIN) +#endif + +#endif // WEBRTC_BASE_SSLCONFIG_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslfingerprint.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslfingerprint.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslfingerprint.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslfingerprint.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,96 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/sslfingerprint.h" + +#include +#include + +#include "webrtc/base/helpers.h" +#include "webrtc/base/messagedigest.h" +#include "webrtc/base/stringencode.h" + +namespace rtc { + +SSLFingerprint* SSLFingerprint::Create( + const std::string& algorithm, const rtc::SSLIdentity* identity) { + if (!identity) { + return NULL; + } + + return Create(algorithm, &(identity->certificate())); +} + +SSLFingerprint* SSLFingerprint::Create( + const std::string& algorithm, const rtc::SSLCertificate* cert) { + uint8 digest_val[64]; + size_t digest_len; + bool ret = cert->ComputeDigest( + algorithm, digest_val, sizeof(digest_val), &digest_len); + if (!ret) { + return NULL; + } + + return new SSLFingerprint(algorithm, digest_val, digest_len); +} + +SSLFingerprint* SSLFingerprint::CreateFromRfc4572( + const std::string& algorithm, const std::string& fingerprint) { + if (algorithm.empty() || !rtc::IsFips180DigestAlgorithm(algorithm)) + return NULL; + + if (fingerprint.empty()) + return NULL; + + size_t value_len; + char value[rtc::MessageDigest::kMaxSize]; + value_len = rtc::hex_decode_with_delimiter(value, sizeof(value), + fingerprint.c_str(), + fingerprint.length(), + ':'); + if (!value_len) + return NULL; + + return new SSLFingerprint(algorithm, + reinterpret_cast(value), + value_len); +} + +SSLFingerprint::SSLFingerprint( + const std::string& algorithm, const uint8* digest_in, size_t digest_len) + : algorithm(algorithm) { + digest.SetData(digest_in, digest_len); +} + +SSLFingerprint::SSLFingerprint(const SSLFingerprint& from) + : algorithm(from.algorithm), digest(from.digest) {} + +bool SSLFingerprint::operator==(const SSLFingerprint& other) const { + return algorithm == other.algorithm && + digest == other.digest; +} + +std::string SSLFingerprint::GetRfc4572Fingerprint() const { + std::string fingerprint = + rtc::hex_encode_with_delimiter( + digest.data(), digest.length(), ':'); + std::transform(fingerprint.begin(), fingerprint.end(), + fingerprint.begin(), ::toupper); + return fingerprint; +} + +std::string SSLFingerprint::ToString() { + std::string fp_str = algorithm; + fp_str.append(" "); + fp_str.append(GetRfc4572Fingerprint()); + return fp_str; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslfingerprint.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslfingerprint.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslfingerprint.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslfingerprint.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SSLFINGERPRINT_H_ +#define WEBRTC_BASE_SSLFINGERPRINT_H_ + +#include + +#include "webrtc/base/buffer.h" +#include "webrtc/base/sslidentity.h" + +namespace rtc { + +class SSLCertificate; + +struct SSLFingerprint { + static SSLFingerprint* Create(const std::string& algorithm, + const rtc::SSLIdentity* identity); + + static SSLFingerprint* Create(const std::string& algorithm, + const rtc::SSLCertificate* cert); + + static SSLFingerprint* CreateFromRfc4572(const std::string& algorithm, + const std::string& fingerprint); + + SSLFingerprint(const std::string& algorithm, const uint8* digest_in, + size_t digest_len); + + SSLFingerprint(const SSLFingerprint& from); + + bool operator==(const SSLFingerprint& other) const; + + std::string GetRfc4572Fingerprint() const; + + std::string ToString(); + + std::string algorithm; + rtc::Buffer digest; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_SSLFINGERPRINT_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslidentity.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslidentity.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslidentity.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslidentity.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,154 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Handling of certificates and keypairs for SSLStreamAdapter's peer mode. +#if HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#include "webrtc/base/sslidentity.h" + +#include + +#include "webrtc/base/base64.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/sslconfig.h" + +#if SSL_USE_SCHANNEL + +#elif SSL_USE_OPENSSL // !SSL_USE_SCHANNEL + +#include "webrtc/base/opensslidentity.h" + +#elif SSL_USE_NSS // !SSL_USE_SCHANNEL && !SSL_USE_OPENSSL + +#include "webrtc/base/nssidentity.h" + +#endif // SSL_USE_SCHANNEL + +namespace rtc { + +const char kPemTypeCertificate[] = "CERTIFICATE"; +const char kPemTypeRsaPrivateKey[] = "RSA PRIVATE KEY"; + +bool SSLIdentity::PemToDer(const std::string& pem_type, + const std::string& pem_string, + std::string* der) { + // Find the inner body. We need this to fulfill the contract of + // returning pem_length. + size_t header = pem_string.find("-----BEGIN " + pem_type + "-----"); + if (header == std::string::npos) + return false; + + size_t body = pem_string.find("\n", header); + if (body == std::string::npos) + return false; + + size_t trailer = pem_string.find("-----END " + pem_type + "-----"); + if (trailer == std::string::npos) + return false; + + std::string inner = pem_string.substr(body + 1, trailer - (body + 1)); + + *der = Base64::Decode(inner, Base64::DO_PARSE_WHITE | + Base64::DO_PAD_ANY | + Base64::DO_TERM_BUFFER); + return true; +} + +std::string SSLIdentity::DerToPem(const std::string& pem_type, + const unsigned char* data, + size_t length) { + std::stringstream result; + + result << "-----BEGIN " << pem_type << "-----\n"; + + std::string b64_encoded; + Base64::EncodeFromArray(data, length, &b64_encoded); + + // Divide the Base-64 encoded data into 64-character chunks, as per + // 4.3.2.4 of RFC 1421. + static const size_t kChunkSize = 64; + size_t chunks = (b64_encoded.size() + (kChunkSize - 1)) / kChunkSize; + for (size_t i = 0, chunk_offset = 0; i < chunks; + ++i, chunk_offset += kChunkSize) { + result << b64_encoded.substr(chunk_offset, kChunkSize); + result << "\n"; + } + + result << "-----END " << pem_type << "-----\n"; + + return result.str(); +} + +#if SSL_USE_SCHANNEL + +SSLCertificate* SSLCertificate::FromPEMString(const std::string& pem_string) { + return NULL; +} + +SSLIdentity* SSLIdentity::Generate(const std::string& common_name) { + return NULL; +} + +SSLIdentity* GenerateForTest(const SSLIdentityParams& params) { + return NULL; +} + +SSLIdentity* SSLIdentity::FromPEMStrings(const std::string& private_key, + const std::string& certificate) { + return NULL; +} + +#elif SSL_USE_OPENSSL // !SSL_USE_SCHANNEL + +SSLCertificate* SSLCertificate::FromPEMString(const std::string& pem_string) { + return OpenSSLCertificate::FromPEMString(pem_string); +} + +SSLIdentity* SSLIdentity::Generate(const std::string& common_name) { + return OpenSSLIdentity::Generate(common_name); +} + +SSLIdentity* SSLIdentity::GenerateForTest(const SSLIdentityParams& params) { + return OpenSSLIdentity::GenerateForTest(params); +} + +SSLIdentity* SSLIdentity::FromPEMStrings(const std::string& private_key, + const std::string& certificate) { + return OpenSSLIdentity::FromPEMStrings(private_key, certificate); +} + +#elif SSL_USE_NSS // !SSL_USE_OPENSSL && !SSL_USE_SCHANNEL + +SSLCertificate* SSLCertificate::FromPEMString(const std::string& pem_string) { + return NSSCertificate::FromPEMString(pem_string); +} + +SSLIdentity* SSLIdentity::Generate(const std::string& common_name) { + return NSSIdentity::Generate(common_name); +} + +SSLIdentity* SSLIdentity::GenerateForTest(const SSLIdentityParams& params) { + return NSSIdentity::GenerateForTest(params); +} + +SSLIdentity* SSLIdentity::FromPEMStrings(const std::string& private_key, + const std::string& certificate) { + return NSSIdentity::FromPEMStrings(private_key, certificate); +} + +#else // !SSL_USE_OPENSSL && !SSL_USE_SCHANNEL && !SSL_USE_NSS + +#error "No SSL implementation" + +#endif // SSL_USE_SCHANNEL + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslidentity.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslidentity.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslidentity.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslidentity.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,172 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Handling of certificates and keypairs for SSLStreamAdapter's peer mode. + +#ifndef WEBRTC_BASE_SSLIDENTITY_H_ +#define WEBRTC_BASE_SSLIDENTITY_H_ + +#include +#include +#include + +#include "webrtc/base/buffer.h" +#include "webrtc/base/messagedigest.h" + +namespace rtc { + +// Forward declaration due to circular dependency with SSLCertificate. +class SSLCertChain; + +// Abstract interface overridden by SSL library specific +// implementations. + +// A somewhat opaque type used to encapsulate a certificate. +// Wraps the SSL library's notion of a certificate, with reference counting. +// The SSLCertificate object is pretty much immutable once created. +// (The OpenSSL implementation only does reference counting and +// possibly caching of intermediate results.) +class SSLCertificate { + public: + // Parses and build a certificate from a PEM encoded string. + // Returns NULL on failure. + // The length of the string representation of the certificate is + // stored in *pem_length if it is non-NULL, and only if + // parsing was successful. + // Caller is responsible for freeing the returned object. + static SSLCertificate* FromPEMString(const std::string& pem_string); + virtual ~SSLCertificate() {} + + // Returns a new SSLCertificate object instance wrapping the same + // underlying certificate, including its chain if present. + // Caller is responsible for freeing the returned object. + virtual SSLCertificate* GetReference() const = 0; + + // Provides the cert chain, or returns false. The caller owns the chain. + // The chain includes a copy of each certificate, excluding the leaf. + virtual bool GetChain(SSLCertChain** chain) const = 0; + + // Returns a PEM encoded string representation of the certificate. + virtual std::string ToPEMString() const = 0; + + // Provides a DER encoded binary representation of the certificate. + virtual void ToDER(Buffer* der_buffer) const = 0; + + // Gets the name of the digest algorithm that was used to compute this + // certificate's signature. + virtual bool GetSignatureDigestAlgorithm(std::string* algorithm) const = 0; + + // Compute the digest of the certificate given algorithm + virtual bool ComputeDigest(const std::string& algorithm, + unsigned char* digest, + size_t size, + size_t* length) const = 0; +}; + +// SSLCertChain is a simple wrapper for a vector of SSLCertificates. It serves +// primarily to ensure proper memory management (especially deletion) of the +// SSLCertificate pointers. +class SSLCertChain { + public: + // These constructors copy the provided SSLCertificate(s), so the caller + // retains ownership. + explicit SSLCertChain(const std::vector& certs) { + ASSERT(!certs.empty()); + certs_.resize(certs.size()); + std::transform(certs.begin(), certs.end(), certs_.begin(), DupCert); + } + explicit SSLCertChain(const SSLCertificate* cert) { + certs_.push_back(cert->GetReference()); + } + + ~SSLCertChain() { + std::for_each(certs_.begin(), certs_.end(), DeleteCert); + } + + // Vector access methods. + size_t GetSize() const { return certs_.size(); } + + // Returns a temporary reference, only valid until the chain is destroyed. + const SSLCertificate& Get(size_t pos) const { return *(certs_[pos]); } + + // Returns a new SSLCertChain object instance wrapping the same underlying + // certificate chain. Caller is responsible for freeing the returned object. + SSLCertChain* Copy() const { + return new SSLCertChain(certs_); + } + + private: + // Helper function for duplicating a vector of certificates. + static SSLCertificate* DupCert(const SSLCertificate* cert) { + return cert->GetReference(); + } + + // Helper function for deleting a vector of certificates. + static void DeleteCert(SSLCertificate* cert) { delete cert; } + + std::vector certs_; + + DISALLOW_COPY_AND_ASSIGN(SSLCertChain); +}; + +// Parameters for generating an identity for testing. If common_name is +// non-empty, it will be used for the certificate's subject and issuer name, +// otherwise a random string will be used. |not_before| and |not_after| are +// offsets to the current time in number of seconds. +struct SSLIdentityParams { + std::string common_name; + int not_before; // in seconds. + int not_after; // in seconds. +}; + +// Our identity in an SSL negotiation: a keypair and certificate (both +// with the same public key). +// This too is pretty much immutable once created. +class SSLIdentity { + public: + // Generates an identity (keypair and self-signed certificate). If + // common_name is non-empty, it will be used for the certificate's + // subject and issuer name, otherwise a random string will be used. + // Returns NULL on failure. + // Caller is responsible for freeing the returned object. + static SSLIdentity* Generate(const std::string& common_name); + + // Generates an identity with the specified validity period. + static SSLIdentity* GenerateForTest(const SSLIdentityParams& params); + + // Construct an identity from a private key and a certificate. + static SSLIdentity* FromPEMStrings(const std::string& private_key, + const std::string& certificate); + + virtual ~SSLIdentity() {} + + // Returns a new SSLIdentity object instance wrapping the same + // identity information. + // Caller is responsible for freeing the returned object. + virtual SSLIdentity* GetReference() const = 0; + + // Returns a temporary reference to the certificate. + virtual const SSLCertificate& certificate() const = 0; + + // Helpers for parsing converting between PEM and DER format. + static bool PemToDer(const std::string& pem_type, + const std::string& pem_string, + std::string* der); + static std::string DerToPem(const std::string& pem_type, + const unsigned char* data, + size_t length); +}; + +extern const char kPemTypeCertificate[]; +extern const char kPemTypeRsaPrivateKey[]; + +} // namespace rtc + +#endif // WEBRTC_BASE_SSLIDENTITY_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslidentity_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslidentity_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslidentity_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslidentity_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,200 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/gunit.h" +#include "webrtc/base/ssladapter.h" +#include "webrtc/base/sslidentity.h" + +using rtc::SSLIdentity; + +const char kTestCertificate[] = "-----BEGIN CERTIFICATE-----\n" + "MIIB6TCCAVICAQYwDQYJKoZIhvcNAQEEBQAwWzELMAkGA1UEBhMCQVUxEzARBgNV\n" + "BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMRswGQYD\n" + "VQQDExJUZXN0IENBICgxMDI0IGJpdCkwHhcNMDAxMDE2MjIzMTAzWhcNMDMwMTE0\n" + "MjIzMTAzWjBjMQswCQYDVQQGEwJBVTETMBEGA1UECBMKUXVlZW5zbGFuZDEaMBgG\n" + "A1UEChMRQ3J5cHRTb2Z0IFB0eSBMdGQxIzAhBgNVBAMTGlNlcnZlciB0ZXN0IGNl\n" + "cnQgKDUxMiBiaXQpMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ+zw4Qnlf8SMVIP\n" + "Fe9GEcStgOY2Ww/dgNdhjeD8ckUJNP5VZkVDTGiXav6ooKXfX3j/7tdkuD8Ey2//\n" + "Kv7+ue0CAwEAATANBgkqhkiG9w0BAQQFAAOBgQCT0grFQeZaqYb5EYfk20XixZV4\n" + "GmyAbXMftG1Eo7qGiMhYzRwGNWxEYojf5PZkYZXvSqZ/ZXHXa4g59jK/rJNnaVGM\n" + "k+xIX8mxQvlV0n5O9PIha5BX5teZnkHKgL8aKKLKW1BK7YTngsfSzzaeame5iKfz\n" + "itAE+OjGF+PFKbwX8Q==\n" + "-----END CERTIFICATE-----\n"; + +const unsigned char kTestCertSha1[] = {0xA6, 0xC8, 0x59, 0xEA, + 0xC3, 0x7E, 0x6D, 0x33, + 0xCF, 0xE2, 0x69, 0x9D, + 0x74, 0xE6, 0xF6, 0x8A, + 0x9E, 0x47, 0xA7, 0xCA}; + +class SSLIdentityTest : public testing::Test { + public: + SSLIdentityTest() : + identity1_(), identity2_() { + } + + ~SSLIdentityTest() { + } + + virtual void SetUp() { + identity1_.reset(SSLIdentity::Generate("test1")); + identity2_.reset(SSLIdentity::Generate("test2")); + + ASSERT_TRUE(identity1_); + ASSERT_TRUE(identity2_); + + test_cert_.reset( + rtc::SSLCertificate::FromPEMString(kTestCertificate)); + ASSERT_TRUE(test_cert_); + } + + void TestGetSignatureDigestAlgorithm() { + std::string digest_algorithm; + // Both NSSIdentity::Generate and OpenSSLIdentity::Generate are + // hard-coded to generate RSA-SHA1 certificates. + ASSERT_TRUE(identity1_->certificate().GetSignatureDigestAlgorithm( + &digest_algorithm)); + ASSERT_EQ(rtc::DIGEST_SHA_1, digest_algorithm); + ASSERT_TRUE(identity2_->certificate().GetSignatureDigestAlgorithm( + &digest_algorithm)); + ASSERT_EQ(rtc::DIGEST_SHA_1, digest_algorithm); + + // The test certificate has an MD5-based signature. + ASSERT_TRUE(test_cert_->GetSignatureDigestAlgorithm(&digest_algorithm)); + ASSERT_EQ(rtc::DIGEST_MD5, digest_algorithm); + } + + void TestDigest(const std::string &algorithm, size_t expected_len, + const unsigned char *expected_digest = NULL) { + unsigned char digest1[64]; + unsigned char digest1b[64]; + unsigned char digest2[64]; + size_t digest1_len; + size_t digest1b_len; + size_t digest2_len; + bool rv; + + rv = identity1_->certificate().ComputeDigest(algorithm, + digest1, sizeof(digest1), + &digest1_len); + EXPECT_TRUE(rv); + EXPECT_EQ(expected_len, digest1_len); + + rv = identity1_->certificate().ComputeDigest(algorithm, + digest1b, sizeof(digest1b), + &digest1b_len); + EXPECT_TRUE(rv); + EXPECT_EQ(expected_len, digest1b_len); + EXPECT_EQ(0, memcmp(digest1, digest1b, expected_len)); + + + rv = identity2_->certificate().ComputeDigest(algorithm, + digest2, sizeof(digest2), + &digest2_len); + EXPECT_TRUE(rv); + EXPECT_EQ(expected_len, digest2_len); + EXPECT_NE(0, memcmp(digest1, digest2, expected_len)); + + // If we have an expected hash for the test cert, check it. + if (expected_digest) { + unsigned char digest3[64]; + size_t digest3_len; + + rv = test_cert_->ComputeDigest(algorithm, digest3, sizeof(digest3), + &digest3_len); + EXPECT_TRUE(rv); + EXPECT_EQ(expected_len, digest3_len); + EXPECT_EQ(0, memcmp(digest3, expected_digest, expected_len)); + } + } + + private: + rtc::scoped_ptr identity1_; + rtc::scoped_ptr identity2_; + rtc::scoped_ptr test_cert_; +}; + +TEST_F(SSLIdentityTest, DigestSHA1) { + TestDigest(rtc::DIGEST_SHA_1, 20, kTestCertSha1); +} + +// HASH_AlgSHA224 is not supported in the chromium linux build. +#if SSL_USE_NSS +TEST_F(SSLIdentityTest, DISABLED_DigestSHA224) { +#else +TEST_F(SSLIdentityTest, DigestSHA224) { +#endif + TestDigest(rtc::DIGEST_SHA_224, 28); +} + +TEST_F(SSLIdentityTest, DigestSHA256) { + TestDigest(rtc::DIGEST_SHA_256, 32); +} + +TEST_F(SSLIdentityTest, DigestSHA384) { + TestDigest(rtc::DIGEST_SHA_384, 48); +} + +TEST_F(SSLIdentityTest, DigestSHA512) { + TestDigest(rtc::DIGEST_SHA_512, 64); +} + +TEST_F(SSLIdentityTest, FromPEMStrings) { + static const char kRSA_PRIVATE_KEY_PEM[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMYRkbhmI7kVA/rM\n" + "czsZ+6JDhDvnkF+vn6yCAGuRPV03zuRqZtDy4N4to7PZu9PjqrRl7nDMXrG3YG9y\n" + "rlIAZ72KjcKKFAJxQyAKLCIdawKRyp8RdK3LEySWEZb0AV58IadqPZDTNHHRX8dz\n" + "5aTSMsbbkZ+C/OzTnbiMqLL/vg6jAgMBAAECgYAvgOs4FJcgvp+TuREx7YtiYVsH\n" + "mwQPTum2z/8VzWGwR8BBHBvIpVe1MbD/Y4seyI2aco/7UaisatSgJhsU46/9Y4fq\n" + "2TwXH9QANf4at4d9n/R6rzwpAJOpgwZgKvdQjkfrKTtgLV+/dawvpxUYkRH4JZM1\n" + "CVGukMfKNrSVH4Ap4QJBAOJmGV1ASPnB4r4nc99at7JuIJmd7fmuVUwUgYi4XgaR\n" + "WhScBsgYwZ/JoywdyZJgnbcrTDuVcWG56B3vXbhdpMsCQQDf9zeJrjnPZ3Cqm79y\n" + "kdqANep0uwZciiNiWxsQrCHztywOvbFhdp8iYVFG9EK8DMY41Y5TxUwsHD+67zao\n" + "ZNqJAkEA1suLUP/GvL8IwuRneQd2tWDqqRQ/Td3qq03hP7e77XtF/buya3Ghclo5\n" + "54czUR89QyVfJEC6278nzA7n2h1uVQJAcG6mztNL6ja/dKZjYZye2CY44QjSlLo0\n" + "MTgTSjdfg/28fFn2Jjtqf9Pi/X+50LWI/RcYMC2no606wRk9kyOuIQJBAK6VSAim\n" + "1pOEjsYQn0X5KEIrz1G3bfCbB848Ime3U2/FWlCHMr6ch8kCZ5d1WUeJD3LbwMNG\n" + "UCXiYxSsu20QNVw=\n" + "-----END RSA PRIVATE KEY-----\n"; + + static const char kCERT_PEM[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIBmTCCAQKgAwIBAgIEbzBSAjANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDEwZX\n" + "ZWJSVEMwHhcNMTQwMTAyMTgyNDQ3WhcNMTQwMjAxMTgyNDQ3WjARMQ8wDQYDVQQD\n" + "EwZXZWJSVEMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMYRkbhmI7kVA/rM\n" + "czsZ+6JDhDvnkF+vn6yCAGuRPV03zuRqZtDy4N4to7PZu9PjqrRl7nDMXrG3YG9y\n" + "rlIAZ72KjcKKFAJxQyAKLCIdawKRyp8RdK3LEySWEZb0AV58IadqPZDTNHHRX8dz\n" + "5aTSMsbbkZ+C/OzTnbiMqLL/vg6jAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAUflI\n" + "VUe5Krqf5RVa5C3u/UTAOAUJBiDS3VANTCLBxjuMsvqOG0WvaYWP3HYPgrz0jXK2\n" + "LJE/mGw3MyFHEqi81jh95J+ypl6xKW6Rm8jKLR87gUvCaVYn/Z4/P3AqcQTB7wOv\n" + "UD0A8qfhfDM+LK6rPAnCsVN0NRDY3jvd6rzix9M=\n" + "-----END CERTIFICATE-----\n"; + + rtc::scoped_ptr identity( + SSLIdentity::FromPEMStrings(kRSA_PRIVATE_KEY_PEM, kCERT_PEM)); + EXPECT_TRUE(identity); + EXPECT_EQ(kCERT_PEM, identity->certificate().ToPEMString()); +} + +TEST_F(SSLIdentityTest, PemDerConversion) { + std::string der; + EXPECT_TRUE(SSLIdentity::PemToDer("CERTIFICATE", kTestCertificate, &der)); + + EXPECT_EQ(kTestCertificate, SSLIdentity::DerToPem( + "CERTIFICATE", + reinterpret_cast(der.data()), der.length())); +} + +TEST_F(SSLIdentityTest, GetSignatureDigestAlgorithm) { + TestGetSignatureDigestAlgorithm(); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslroots.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslroots.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslroots.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslroots.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,4932 @@ +// This file is the root certificates in C form that are needed to connect to +// Google. + +// It was generated with the following command line: +// > python //depot/googleclient/talk/tools/generate_sslroots.py +// //depot/google3/security/cacerts/for_connecting_to_google/roots.pem + +/* subject:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root */ +/* issuer :/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root */ + +namespace rtc { + +const unsigned char AddTrust_External_Root_certificate[1082]={ +0x30,0x82,0x04,0x36,0x30,0x82,0x03,0x1E,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30, +0x6F,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x53,0x45,0x31,0x14, +0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x13,0x0B,0x41,0x64,0x64,0x54,0x72,0x75,0x73, +0x74,0x20,0x41,0x42,0x31,0x26,0x30,0x24,0x06,0x03,0x55,0x04,0x0B,0x13,0x1D,0x41, +0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x45,0x78,0x74,0x65,0x72,0x6E,0x61,0x6C, +0x20,0x54,0x54,0x50,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x31,0x22,0x30,0x20, +0x06,0x03,0x55,0x04,0x03,0x13,0x19,0x41,0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20, +0x45,0x78,0x74,0x65,0x72,0x6E,0x61,0x6C,0x20,0x43,0x41,0x20,0x52,0x6F,0x6F,0x74, +0x30,0x1E,0x17,0x0D,0x30,0x30,0x30,0x35,0x33,0x30,0x31,0x30,0x34,0x38,0x33,0x38, +0x5A,0x17,0x0D,0x32,0x30,0x30,0x35,0x33,0x30,0x31,0x30,0x34,0x38,0x33,0x38,0x5A, +0x30,0x6F,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x53,0x45,0x31, +0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x13,0x0B,0x41,0x64,0x64,0x54,0x72,0x75, +0x73,0x74,0x20,0x41,0x42,0x31,0x26,0x30,0x24,0x06,0x03,0x55,0x04,0x0B,0x13,0x1D, +0x41,0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x45,0x78,0x74,0x65,0x72,0x6E,0x61, +0x6C,0x20,0x54,0x54,0x50,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x31,0x22,0x30, +0x20,0x06,0x03,0x55,0x04,0x03,0x13,0x19,0x41,0x64,0x64,0x54,0x72,0x75,0x73,0x74, +0x20,0x45,0x78,0x74,0x65,0x72,0x6E,0x61,0x6C,0x20,0x43,0x41,0x20,0x52,0x6F,0x6F, +0x74,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01, +0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01, +0x01,0x00,0xB7,0xF7,0x1A,0x33,0xE6,0xF2,0x00,0x04,0x2D,0x39,0xE0,0x4E,0x5B,0xED, +0x1F,0xBC,0x6C,0x0F,0xCD,0xB5,0xFA,0x23,0xB6,0xCE,0xDE,0x9B,0x11,0x33,0x97,0xA4, +0x29,0x4C,0x7D,0x93,0x9F,0xBD,0x4A,0xBC,0x93,0xED,0x03,0x1A,0xE3,0x8F,0xCF,0xE5, +0x6D,0x50,0x5A,0xD6,0x97,0x29,0x94,0x5A,0x80,0xB0,0x49,0x7A,0xDB,0x2E,0x95,0xFD, +0xB8,0xCA,0xBF,0x37,0x38,0x2D,0x1E,0x3E,0x91,0x41,0xAD,0x70,0x56,0xC7,0xF0,0x4F, +0x3F,0xE8,0x32,0x9E,0x74,0xCA,0xC8,0x90,0x54,0xE9,0xC6,0x5F,0x0F,0x78,0x9D,0x9A, +0x40,0x3C,0x0E,0xAC,0x61,0xAA,0x5E,0x14,0x8F,0x9E,0x87,0xA1,0x6A,0x50,0xDC,0xD7, +0x9A,0x4E,0xAF,0x05,0xB3,0xA6,0x71,0x94,0x9C,0x71,0xB3,0x50,0x60,0x0A,0xC7,0x13, +0x9D,0x38,0x07,0x86,0x02,0xA8,0xE9,0xA8,0x69,0x26,0x18,0x90,0xAB,0x4C,0xB0,0x4F, +0x23,0xAB,0x3A,0x4F,0x84,0xD8,0xDF,0xCE,0x9F,0xE1,0x69,0x6F,0xBB,0xD7,0x42,0xD7, +0x6B,0x44,0xE4,0xC7,0xAD,0xEE,0x6D,0x41,0x5F,0x72,0x5A,0x71,0x08,0x37,0xB3,0x79, +0x65,0xA4,0x59,0xA0,0x94,0x37,0xF7,0x00,0x2F,0x0D,0xC2,0x92,0x72,0xDA,0xD0,0x38, +0x72,0xDB,0x14,0xA8,0x45,0xC4,0x5D,0x2A,0x7D,0xB7,0xB4,0xD6,0xC4,0xEE,0xAC,0xCD, +0x13,0x44,0xB7,0xC9,0x2B,0xDD,0x43,0x00,0x25,0xFA,0x61,0xB9,0x69,0x6A,0x58,0x23, +0x11,0xB7,0xA7,0x33,0x8F,0x56,0x75,0x59,0xF5,0xCD,0x29,0xD7,0x46,0xB7,0x0A,0x2B, +0x65,0xB6,0xD3,0x42,0x6F,0x15,0xB2,0xB8,0x7B,0xFB,0xEF,0xE9,0x5D,0x53,0xD5,0x34, +0x5A,0x27,0x02,0x03,0x01,0x00,0x01,0xA3,0x81,0xDC,0x30,0x81,0xD9,0x30,0x1D,0x06, +0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xAD,0xBD,0x98,0x7A,0x34,0xB4,0x26,0xF7, +0xFA,0xC4,0x26,0x54,0xEF,0x03,0xBD,0xE0,0x24,0xCB,0x54,0x1A,0x30,0x0B,0x06,0x03, +0x55,0x1D,0x0F,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13, +0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x81,0x99,0x06,0x03,0x55, +0x1D,0x23,0x04,0x81,0x91,0x30,0x81,0x8E,0x80,0x14,0xAD,0xBD,0x98,0x7A,0x34,0xB4, +0x26,0xF7,0xFA,0xC4,0x26,0x54,0xEF,0x03,0xBD,0xE0,0x24,0xCB,0x54,0x1A,0xA1,0x73, +0xA4,0x71,0x30,0x6F,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x53, +0x45,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x13,0x0B,0x41,0x64,0x64,0x54, +0x72,0x75,0x73,0x74,0x20,0x41,0x42,0x31,0x26,0x30,0x24,0x06,0x03,0x55,0x04,0x0B, +0x13,0x1D,0x41,0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x45,0x78,0x74,0x65,0x72, +0x6E,0x61,0x6C,0x20,0x54,0x54,0x50,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x31, +0x22,0x30,0x20,0x06,0x03,0x55,0x04,0x03,0x13,0x19,0x41,0x64,0x64,0x54,0x72,0x75, +0x73,0x74,0x20,0x45,0x78,0x74,0x65,0x72,0x6E,0x61,0x6C,0x20,0x43,0x41,0x20,0x52, +0x6F,0x6F,0x74,0x82,0x01,0x01,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D, +0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0xB0,0x9B,0xE0,0x85,0x25,0xC2, +0xD6,0x23,0xE2,0x0F,0x96,0x06,0x92,0x9D,0x41,0x98,0x9C,0xD9,0x84,0x79,0x81,0xD9, +0x1E,0x5B,0x14,0x07,0x23,0x36,0x65,0x8F,0xB0,0xD8,0x77,0xBB,0xAC,0x41,0x6C,0x47, +0x60,0x83,0x51,0xB0,0xF9,0x32,0x3D,0xE7,0xFC,0xF6,0x26,0x13,0xC7,0x80,0x16,0xA5, +0xBF,0x5A,0xFC,0x87,0xCF,0x78,0x79,0x89,0x21,0x9A,0xE2,0x4C,0x07,0x0A,0x86,0x35, +0xBC,0xF2,0xDE,0x51,0xC4,0xD2,0x96,0xB7,0xDC,0x7E,0x4E,0xEE,0x70,0xFD,0x1C,0x39, +0xEB,0x0C,0x02,0x51,0x14,0x2D,0x8E,0xBD,0x16,0xE0,0xC1,0xDF,0x46,0x75,0xE7,0x24, +0xAD,0xEC,0xF4,0x42,0xB4,0x85,0x93,0x70,0x10,0x67,0xBA,0x9D,0x06,0x35,0x4A,0x18, +0xD3,0x2B,0x7A,0xCC,0x51,0x42,0xA1,0x7A,0x63,0xD1,0xE6,0xBB,0xA1,0xC5,0x2B,0xC2, +0x36,0xBE,0x13,0x0D,0xE6,0xBD,0x63,0x7E,0x79,0x7B,0xA7,0x09,0x0D,0x40,0xAB,0x6A, +0xDD,0x8F,0x8A,0xC3,0xF6,0xF6,0x8C,0x1A,0x42,0x05,0x51,0xD4,0x45,0xF5,0x9F,0xA7, +0x62,0x21,0x68,0x15,0x20,0x43,0x3C,0x99,0xE7,0x7C,0xBD,0x24,0xD8,0xA9,0x91,0x17, +0x73,0x88,0x3F,0x56,0x1B,0x31,0x38,0x18,0xB4,0x71,0x0F,0x9A,0xCD,0xC8,0x0E,0x9E, +0x8E,0x2E,0x1B,0xE1,0x8C,0x98,0x83,0xCB,0x1F,0x31,0xF1,0x44,0x4C,0xC6,0x04,0x73, +0x49,0x76,0x60,0x0F,0xC7,0xF8,0xBD,0x17,0x80,0x6B,0x2E,0xE9,0xCC,0x4C,0x0E,0x5A, +0x9A,0x79,0x0F,0x20,0x0A,0x2E,0xD5,0x9E,0x63,0x26,0x1E,0x55,0x92,0x94,0xD8,0x82, +0x17,0x5A,0x7B,0xD0,0xBC,0xC7,0x8F,0x4E,0x86,0x04, +}; + + +/* subject:/C=SE/O=AddTrust AB/OU=AddTrust TTP Network/CN=AddTrust Class 1 CA Root */ +/* issuer :/C=SE/O=AddTrust AB/OU=AddTrust TTP Network/CN=AddTrust Class 1 CA Root */ + + +const unsigned char AddTrust_Low_Value_Services_Root_certificate[1052]={ +0x30,0x82,0x04,0x18,0x30,0x82,0x03,0x00,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30, +0x65,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x53,0x45,0x31,0x14, +0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x13,0x0B,0x41,0x64,0x64,0x54,0x72,0x75,0x73, +0x74,0x20,0x41,0x42,0x31,0x1D,0x30,0x1B,0x06,0x03,0x55,0x04,0x0B,0x13,0x14,0x41, +0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x54,0x54,0x50,0x20,0x4E,0x65,0x74,0x77, +0x6F,0x72,0x6B,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x03,0x13,0x18,0x41,0x64, +0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x31,0x20,0x43, +0x41,0x20,0x52,0x6F,0x6F,0x74,0x30,0x1E,0x17,0x0D,0x30,0x30,0x30,0x35,0x33,0x30, +0x31,0x30,0x33,0x38,0x33,0x31,0x5A,0x17,0x0D,0x32,0x30,0x30,0x35,0x33,0x30,0x31, +0x30,0x33,0x38,0x33,0x31,0x5A,0x30,0x65,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04, +0x06,0x13,0x02,0x53,0x45,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x13,0x0B, +0x41,0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x41,0x42,0x31,0x1D,0x30,0x1B,0x06, +0x03,0x55,0x04,0x0B,0x13,0x14,0x41,0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x54, +0x54,0x50,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x31,0x21,0x30,0x1F,0x06,0x03, +0x55,0x04,0x03,0x13,0x18,0x41,0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x43,0x6C, +0x61,0x73,0x73,0x20,0x31,0x20,0x43,0x41,0x20,0x52,0x6F,0x6F,0x74,0x30,0x82,0x01, +0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00, +0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0x96,0x96, +0xD4,0x21,0x49,0x60,0xE2,0x6B,0xE8,0x41,0x07,0x0C,0xDE,0xC4,0xE0,0xDC,0x13,0x23, +0xCD,0xC1,0x35,0xC7,0xFB,0xD6,0x4E,0x11,0x0A,0x67,0x5E,0xF5,0x06,0x5B,0x6B,0xA5, +0x08,0x3B,0x5B,0x29,0x16,0x3A,0xE7,0x87,0xB2,0x34,0x06,0xC5,0xBC,0x05,0xA5,0x03, +0x7C,0x82,0xCB,0x29,0x10,0xAE,0xE1,0x88,0x81,0xBD,0xD6,0x9E,0xD3,0xFE,0x2D,0x56, +0xC1,0x15,0xCE,0xE3,0x26,0x9D,0x15,0x2E,0x10,0xFB,0x06,0x8F,0x30,0x04,0xDE,0xA7, +0xB4,0x63,0xB4,0xFF,0xB1,0x9C,0xAE,0x3C,0xAF,0x77,0xB6,0x56,0xC5,0xB5,0xAB,0xA2, +0xE9,0x69,0x3A,0x3D,0x0E,0x33,0x79,0x32,0x3F,0x70,0x82,0x92,0x99,0x61,0x6D,0x8D, +0x30,0x08,0x8F,0x71,0x3F,0xA6,0x48,0x57,0x19,0xF8,0x25,0xDC,0x4B,0x66,0x5C,0xA5, +0x74,0x8F,0x98,0xAE,0xC8,0xF9,0xC0,0x06,0x22,0xE7,0xAC,0x73,0xDF,0xA5,0x2E,0xFB, +0x52,0xDC,0xB1,0x15,0x65,0x20,0xFA,0x35,0x66,0x69,0xDE,0xDF,0x2C,0xF1,0x6E,0xBC, +0x30,0xDB,0x2C,0x24,0x12,0xDB,0xEB,0x35,0x35,0x68,0x90,0xCB,0x00,0xB0,0x97,0x21, +0x3D,0x74,0x21,0x23,0x65,0x34,0x2B,0xBB,0x78,0x59,0xA3,0xD6,0xE1,0x76,0x39,0x9A, +0xA4,0x49,0x8E,0x8C,0x74,0xAF,0x6E,0xA4,0x9A,0xA3,0xD9,0x9B,0xD2,0x38,0x5C,0x9B, +0xA2,0x18,0xCC,0x75,0x23,0x84,0xBE,0xEB,0xE2,0x4D,0x33,0x71,0x8E,0x1A,0xF0,0xC2, +0xF8,0xC7,0x1D,0xA2,0xAD,0x03,0x97,0x2C,0xF8,0xCF,0x25,0xC6,0xF6,0xB8,0x24,0x31, +0xB1,0x63,0x5D,0x92,0x7F,0x63,0xF0,0x25,0xC9,0x53,0x2E,0x1F,0xBF,0x4D,0x02,0x03, +0x01,0x00,0x01,0xA3,0x81,0xD2,0x30,0x81,0xCF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E, +0x04,0x16,0x04,0x14,0x95,0xB1,0xB4,0xF0,0x94,0xB6,0xBD,0xC7,0xDA,0xD1,0x11,0x09, +0x21,0xBE,0xC1,0xAF,0x49,0xFD,0x10,0x7B,0x30,0x0B,0x06,0x03,0x55,0x1D,0x0F,0x04, +0x04,0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04, +0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x81,0x8F,0x06,0x03,0x55,0x1D,0x23,0x04,0x81, +0x87,0x30,0x81,0x84,0x80,0x14,0x95,0xB1,0xB4,0xF0,0x94,0xB6,0xBD,0xC7,0xDA,0xD1, +0x11,0x09,0x21,0xBE,0xC1,0xAF,0x49,0xFD,0x10,0x7B,0xA1,0x69,0xA4,0x67,0x30,0x65, +0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x53,0x45,0x31,0x14,0x30, +0x12,0x06,0x03,0x55,0x04,0x0A,0x13,0x0B,0x41,0x64,0x64,0x54,0x72,0x75,0x73,0x74, +0x20,0x41,0x42,0x31,0x1D,0x30,0x1B,0x06,0x03,0x55,0x04,0x0B,0x13,0x14,0x41,0x64, +0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x54,0x54,0x50,0x20,0x4E,0x65,0x74,0x77,0x6F, +0x72,0x6B,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x03,0x13,0x18,0x41,0x64,0x64, +0x54,0x72,0x75,0x73,0x74,0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x31,0x20,0x43,0x41, +0x20,0x52,0x6F,0x6F,0x74,0x82,0x01,0x01,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x2C,0x6D,0x64,0x1B, +0x1F,0xCD,0x0D,0xDD,0xB9,0x01,0xFA,0x96,0x63,0x34,0x32,0x48,0x47,0x99,0xAE,0x97, +0xED,0xFD,0x72,0x16,0xA6,0x73,0x47,0x5A,0xF4,0xEB,0xDD,0xE9,0xF5,0xD6,0xFB,0x45, +0xCC,0x29,0x89,0x44,0x5D,0xBF,0x46,0x39,0x3D,0xE8,0xEE,0xBC,0x4D,0x54,0x86,0x1E, +0x1D,0x6C,0xE3,0x17,0x27,0x43,0xE1,0x89,0x56,0x2B,0xA9,0x6F,0x72,0x4E,0x49,0x33, +0xE3,0x72,0x7C,0x2A,0x23,0x9A,0xBC,0x3E,0xFF,0x28,0x2A,0xED,0xA3,0xFF,0x1C,0x23, +0xBA,0x43,0x57,0x09,0x67,0x4D,0x4B,0x62,0x06,0x2D,0xF8,0xFF,0x6C,0x9D,0x60,0x1E, +0xD8,0x1C,0x4B,0x7D,0xB5,0x31,0x2F,0xD9,0xD0,0x7C,0x5D,0xF8,0xDE,0x6B,0x83,0x18, +0x78,0x37,0x57,0x2F,0xE8,0x33,0x07,0x67,0xDF,0x1E,0xC7,0x6B,0x2A,0x95,0x76,0xAE, +0x8F,0x57,0xA3,0xF0,0xF4,0x52,0xB4,0xA9,0x53,0x08,0xCF,0xE0,0x4F,0xD3,0x7A,0x53, +0x8B,0xFD,0xBB,0x1C,0x56,0x36,0xF2,0xFE,0xB2,0xB6,0xE5,0x76,0xBB,0xD5,0x22,0x65, +0xA7,0x3F,0xFE,0xD1,0x66,0xAD,0x0B,0xBC,0x6B,0x99,0x86,0xEF,0x3F,0x7D,0xF3,0x18, +0x32,0xCA,0x7B,0xC6,0xE3,0xAB,0x64,0x46,0x95,0xF8,0x26,0x69,0xD9,0x55,0x83,0x7B, +0x2C,0x96,0x07,0xFF,0x59,0x2C,0x44,0xA3,0xC6,0xE5,0xE9,0xA9,0xDC,0xA1,0x63,0x80, +0x5A,0x21,0x5E,0x21,0xCF,0x53,0x54,0xF0,0xBA,0x6F,0x89,0xDB,0xA8,0xAA,0x95,0xCF, +0x8B,0xE3,0x71,0xCC,0x1E,0x1B,0x20,0x44,0x08,0xC0,0x7A,0xB6,0x40,0xFD,0xC4,0xE4, +0x35,0xE1,0x1D,0x16,0x1C,0xD0,0xBC,0x2B,0x8E,0xD6,0x71,0xD9, +}; + + +/* subject:/C=SE/O=AddTrust AB/OU=AddTrust TTP Network/CN=AddTrust Public CA Root */ +/* issuer :/C=SE/O=AddTrust AB/OU=AddTrust TTP Network/CN=AddTrust Public CA Root */ + + +const unsigned char AddTrust_Public_Services_Root_certificate[1049]={ +0x30,0x82,0x04,0x15,0x30,0x82,0x02,0xFD,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30, +0x64,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x53,0x45,0x31,0x14, +0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x13,0x0B,0x41,0x64,0x64,0x54,0x72,0x75,0x73, +0x74,0x20,0x41,0x42,0x31,0x1D,0x30,0x1B,0x06,0x03,0x55,0x04,0x0B,0x13,0x14,0x41, +0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x54,0x54,0x50,0x20,0x4E,0x65,0x74,0x77, +0x6F,0x72,0x6B,0x31,0x20,0x30,0x1E,0x06,0x03,0x55,0x04,0x03,0x13,0x17,0x41,0x64, +0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x50,0x75,0x62,0x6C,0x69,0x63,0x20,0x43,0x41, +0x20,0x52,0x6F,0x6F,0x74,0x30,0x1E,0x17,0x0D,0x30,0x30,0x30,0x35,0x33,0x30,0x31, +0x30,0x34,0x31,0x35,0x30,0x5A,0x17,0x0D,0x32,0x30,0x30,0x35,0x33,0x30,0x31,0x30, +0x34,0x31,0x35,0x30,0x5A,0x30,0x64,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06, +0x13,0x02,0x53,0x45,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x13,0x0B,0x41, +0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x41,0x42,0x31,0x1D,0x30,0x1B,0x06,0x03, +0x55,0x04,0x0B,0x13,0x14,0x41,0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x54,0x54, +0x50,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x31,0x20,0x30,0x1E,0x06,0x03,0x55, +0x04,0x03,0x13,0x17,0x41,0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x50,0x75,0x62, +0x6C,0x69,0x63,0x20,0x43,0x41,0x20,0x52,0x6F,0x6F,0x74,0x30,0x82,0x01,0x22,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82, +0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xE9,0x1A,0x30,0x8F, +0x83,0x88,0x14,0xC1,0x20,0xD8,0x3C,0x9B,0x8F,0x1B,0x7E,0x03,0x74,0xBB,0xDA,0x69, +0xD3,0x46,0xA5,0xF8,0x8E,0xC2,0x0C,0x11,0x90,0x51,0xA5,0x2F,0x66,0x54,0x40,0x55, +0xEA,0xDB,0x1F,0x4A,0x56,0xEE,0x9F,0x23,0x6E,0xF4,0x39,0xCB,0xA1,0xB9,0x6F,0xF2, +0x7E,0xF9,0x5D,0x87,0x26,0x61,0x9E,0x1C,0xF8,0xE2,0xEC,0xA6,0x81,0xF8,0x21,0xC5, +0x24,0xCC,0x11,0x0C,0x3F,0xDB,0x26,0x72,0x7A,0xC7,0x01,0x97,0x07,0x17,0xF9,0xD7, +0x18,0x2C,0x30,0x7D,0x0E,0x7A,0x1E,0x62,0x1E,0xC6,0x4B,0xC0,0xFD,0x7D,0x62,0x77, +0xD3,0x44,0x1E,0x27,0xF6,0x3F,0x4B,0x44,0xB3,0xB7,0x38,0xD9,0x39,0x1F,0x60,0xD5, +0x51,0x92,0x73,0x03,0xB4,0x00,0x69,0xE3,0xF3,0x14,0x4E,0xEE,0xD1,0xDC,0x09,0xCF, +0x77,0x34,0x46,0x50,0xB0,0xF8,0x11,0xF2,0xFE,0x38,0x79,0xF7,0x07,0x39,0xFE,0x51, +0x92,0x97,0x0B,0x5B,0x08,0x5F,0x34,0x86,0x01,0xAD,0x88,0x97,0xEB,0x66,0xCD,0x5E, +0xD1,0xFF,0xDC,0x7D,0xF2,0x84,0xDA,0xBA,0x77,0xAD,0xDC,0x80,0x08,0xC7,0xA7,0x87, +0xD6,0x55,0x9F,0x97,0x6A,0xE8,0xC8,0x11,0x64,0xBA,0xE7,0x19,0x29,0x3F,0x11,0xB3, +0x78,0x90,0x84,0x20,0x52,0x5B,0x11,0xEF,0x78,0xD0,0x83,0xF6,0xD5,0x48,0x90,0xD0, +0x30,0x1C,0xCF,0x80,0xF9,0x60,0xFE,0x79,0xE4,0x88,0xF2,0xDD,0x00,0xEB,0x94,0x45, +0xEB,0x65,0x94,0x69,0x40,0xBA,0xC0,0xD5,0xB4,0xB8,0xBA,0x7D,0x04,0x11,0xA8,0xEB, +0x31,0x05,0x96,0x94,0x4E,0x58,0x21,0x8E,0x9F,0xD0,0x60,0xFD,0x02,0x03,0x01,0x00, +0x01,0xA3,0x81,0xD1,0x30,0x81,0xCE,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16, +0x04,0x14,0x81,0x3E,0x37,0xD8,0x92,0xB0,0x1F,0x77,0x9F,0x5C,0xB4,0xAB,0x73,0xAA, +0xE7,0xF6,0x34,0x60,0x2F,0xFA,0x30,0x0B,0x06,0x03,0x55,0x1D,0x0F,0x04,0x04,0x03, +0x02,0x01,0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30, +0x03,0x01,0x01,0xFF,0x30,0x81,0x8E,0x06,0x03,0x55,0x1D,0x23,0x04,0x81,0x86,0x30, +0x81,0x83,0x80,0x14,0x81,0x3E,0x37,0xD8,0x92,0xB0,0x1F,0x77,0x9F,0x5C,0xB4,0xAB, +0x73,0xAA,0xE7,0xF6,0x34,0x60,0x2F,0xFA,0xA1,0x68,0xA4,0x66,0x30,0x64,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x53,0x45,0x31,0x14,0x30,0x12,0x06, +0x03,0x55,0x04,0x0A,0x13,0x0B,0x41,0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x41, +0x42,0x31,0x1D,0x30,0x1B,0x06,0x03,0x55,0x04,0x0B,0x13,0x14,0x41,0x64,0x64,0x54, +0x72,0x75,0x73,0x74,0x20,0x54,0x54,0x50,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B, +0x31,0x20,0x30,0x1E,0x06,0x03,0x55,0x04,0x03,0x13,0x17,0x41,0x64,0x64,0x54,0x72, +0x75,0x73,0x74,0x20,0x50,0x75,0x62,0x6C,0x69,0x63,0x20,0x43,0x41,0x20,0x52,0x6F, +0x6F,0x74,0x82,0x01,0x01,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01, +0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x03,0xF7,0x15,0x4A,0xF8,0x24,0xDA, +0x23,0x56,0x16,0x93,0x76,0xDD,0x36,0x28,0xB9,0xAE,0x1B,0xB8,0xC3,0xF1,0x64,0xBA, +0x20,0x18,0x78,0x95,0x29,0x27,0x57,0x05,0xBC,0x7C,0x2A,0xF4,0xB9,0x51,0x55,0xDA, +0x87,0x02,0xDE,0x0F,0x16,0x17,0x31,0xF8,0xAA,0x79,0x2E,0x09,0x13,0xBB,0xAF,0xB2, +0x20,0x19,0x12,0xE5,0x93,0xF9,0x4B,0xF9,0x83,0xE8,0x44,0xD5,0xB2,0x41,0x25,0xBF, +0x88,0x75,0x6F,0xFF,0x10,0xFC,0x4A,0x54,0xD0,0x5F,0xF0,0xFA,0xEF,0x36,0x73,0x7D, +0x1B,0x36,0x45,0xC6,0x21,0x6D,0xB4,0x15,0xB8,0x4E,0xCF,0x9C,0x5C,0xA5,0x3D,0x5A, +0x00,0x8E,0x06,0xE3,0x3C,0x6B,0x32,0x7B,0xF2,0x9F,0xF0,0xB6,0xFD,0xDF,0xF0,0x28, +0x18,0x48,0xF0,0xC6,0xBC,0xD0,0xBF,0x34,0x80,0x96,0xC2,0x4A,0xB1,0x6D,0x8E,0xC7, +0x90,0x45,0xDE,0x2F,0x67,0xAC,0x45,0x04,0xA3,0x7A,0xDC,0x55,0x92,0xC9,0x47,0x66, +0xD8,0x1A,0x8C,0xC7,0xED,0x9C,0x4E,0x9A,0xE0,0x12,0xBB,0xB5,0x6A,0x4C,0x84,0xE1, +0xE1,0x22,0x0D,0x87,0x00,0x64,0xFE,0x8C,0x7D,0x62,0x39,0x65,0xA6,0xEF,0x42,0xB6, +0x80,0x25,0x12,0x61,0x01,0xA8,0x24,0x13,0x70,0x00,0x11,0x26,0x5F,0xFA,0x35,0x50, +0xC5,0x48,0xCC,0x06,0x47,0xE8,0x27,0xD8,0x70,0x8D,0x5F,0x64,0xE6,0xA1,0x44,0x26, +0x5E,0x22,0xEC,0x92,0xCD,0xFF,0x42,0x9A,0x44,0x21,0x6D,0x5C,0xC5,0xE3,0x22,0x1D, +0x5F,0x47,0x12,0xE7,0xCE,0x5F,0x5D,0xFA,0xD8,0xAA,0xB1,0x33,0x2D,0xD9,0x76,0xF2, +0x4E,0x3A,0x33,0x0C,0x2B,0xB3,0x2D,0x90,0x06, +}; + + +/* subject:/C=SE/O=AddTrust AB/OU=AddTrust TTP Network/CN=AddTrust Qualified CA Root */ +/* issuer :/C=SE/O=AddTrust AB/OU=AddTrust TTP Network/CN=AddTrust Qualified CA Root */ + + +const unsigned char AddTrust_Qualified_Certificates_Root_certificate[1058]={ +0x30,0x82,0x04,0x1E,0x30,0x82,0x03,0x06,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30, +0x67,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x53,0x45,0x31,0x14, +0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x13,0x0B,0x41,0x64,0x64,0x54,0x72,0x75,0x73, +0x74,0x20,0x41,0x42,0x31,0x1D,0x30,0x1B,0x06,0x03,0x55,0x04,0x0B,0x13,0x14,0x41, +0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x54,0x54,0x50,0x20,0x4E,0x65,0x74,0x77, +0x6F,0x72,0x6B,0x31,0x23,0x30,0x21,0x06,0x03,0x55,0x04,0x03,0x13,0x1A,0x41,0x64, +0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x51,0x75,0x61,0x6C,0x69,0x66,0x69,0x65,0x64, +0x20,0x43,0x41,0x20,0x52,0x6F,0x6F,0x74,0x30,0x1E,0x17,0x0D,0x30,0x30,0x30,0x35, +0x33,0x30,0x31,0x30,0x34,0x34,0x35,0x30,0x5A,0x17,0x0D,0x32,0x30,0x30,0x35,0x33, +0x30,0x31,0x30,0x34,0x34,0x35,0x30,0x5A,0x30,0x67,0x31,0x0B,0x30,0x09,0x06,0x03, +0x55,0x04,0x06,0x13,0x02,0x53,0x45,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A, +0x13,0x0B,0x41,0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x41,0x42,0x31,0x1D,0x30, +0x1B,0x06,0x03,0x55,0x04,0x0B,0x13,0x14,0x41,0x64,0x64,0x54,0x72,0x75,0x73,0x74, +0x20,0x54,0x54,0x50,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x31,0x23,0x30,0x21, +0x06,0x03,0x55,0x04,0x03,0x13,0x1A,0x41,0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20, +0x51,0x75,0x61,0x6C,0x69,0x66,0x69,0x65,0x64,0x20,0x43,0x41,0x20,0x52,0x6F,0x6F, +0x74,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01, +0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01, +0x01,0x00,0xE4,0x1E,0x9A,0xFE,0xDC,0x09,0x5A,0x87,0xA4,0x9F,0x47,0xBE,0x11,0x5F, +0xAF,0x84,0x34,0xDB,0x62,0x3C,0x79,0x78,0xB7,0xE9,0x30,0xB5,0xEC,0x0C,0x1C,0x2A, +0xC4,0x16,0xFF,0xE0,0xEC,0x71,0xEB,0x8A,0xF5,0x11,0x6E,0xED,0x4F,0x0D,0x91,0xD2, +0x12,0x18,0x2D,0x49,0x15,0x01,0xC2,0xA4,0x22,0x13,0xC7,0x11,0x64,0xFF,0x22,0x12, +0x9A,0xB9,0x8E,0x5C,0x2F,0x08,0xCF,0x71,0x6A,0xB3,0x67,0x01,0x59,0xF1,0x5D,0x46, +0xF3,0xB0,0x78,0xA5,0xF6,0x0E,0x42,0x7A,0xE3,0x7F,0x1B,0xCC,0xD0,0xF0,0xB7,0x28, +0xFD,0x2A,0xEA,0x9E,0xB3,0xB0,0xB9,0x04,0xAA,0xFD,0xF6,0xC7,0xB4,0xB1,0xB8,0x2A, +0xA0,0xFB,0x58,0xF1,0x19,0xA0,0x6F,0x70,0x25,0x7E,0x3E,0x69,0x4A,0x7F,0x0F,0x22, +0xD8,0xEF,0xAD,0x08,0x11,0x9A,0x29,0x99,0xE1,0xAA,0x44,0x45,0x9A,0x12,0x5E,0x3E, +0x9D,0x6D,0x52,0xFC,0xE7,0xA0,0x3D,0x68,0x2F,0xF0,0x4B,0x70,0x7C,0x13,0x38,0xAD, +0xBC,0x15,0x25,0xF1,0xD6,0xCE,0xAB,0xA2,0xC0,0x31,0xD6,0x2F,0x9F,0xE0,0xFF,0x14, +0x59,0xFC,0x84,0x93,0xD9,0x87,0x7C,0x4C,0x54,0x13,0xEB,0x9F,0xD1,0x2D,0x11,0xF8, +0x18,0x3A,0x3A,0xDE,0x25,0xD9,0xF7,0xD3,0x40,0xED,0xA4,0x06,0x12,0xC4,0x3B,0xE1, +0x91,0xC1,0x56,0x35,0xF0,0x14,0xDC,0x65,0x36,0x09,0x6E,0xAB,0xA4,0x07,0xC7,0x35, +0xD1,0xC2,0x03,0x33,0x36,0x5B,0x75,0x26,0x6D,0x42,0xF1,0x12,0x6B,0x43,0x6F,0x4B, +0x71,0x94,0xFA,0x34,0x1D,0xED,0x13,0x6E,0xCA,0x80,0x7F,0x98,0x2F,0x6C,0xB9,0x65, +0xD8,0xE9,0x02,0x03,0x01,0x00,0x01,0xA3,0x81,0xD4,0x30,0x81,0xD1,0x30,0x1D,0x06, +0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x39,0x95,0x8B,0x62,0x8B,0x5C,0xC9,0xD4, +0x80,0xBA,0x58,0x0F,0x97,0x3F,0x15,0x08,0x43,0xCC,0x98,0xA7,0x30,0x0B,0x06,0x03, +0x55,0x1D,0x0F,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13, +0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x81,0x91,0x06,0x03,0x55, +0x1D,0x23,0x04,0x81,0x89,0x30,0x81,0x86,0x80,0x14,0x39,0x95,0x8B,0x62,0x8B,0x5C, +0xC9,0xD4,0x80,0xBA,0x58,0x0F,0x97,0x3F,0x15,0x08,0x43,0xCC,0x98,0xA7,0xA1,0x6B, +0xA4,0x69,0x30,0x67,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x53, +0x45,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x13,0x0B,0x41,0x64,0x64,0x54, +0x72,0x75,0x73,0x74,0x20,0x41,0x42,0x31,0x1D,0x30,0x1B,0x06,0x03,0x55,0x04,0x0B, +0x13,0x14,0x41,0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x54,0x54,0x50,0x20,0x4E, +0x65,0x74,0x77,0x6F,0x72,0x6B,0x31,0x23,0x30,0x21,0x06,0x03,0x55,0x04,0x03,0x13, +0x1A,0x41,0x64,0x64,0x54,0x72,0x75,0x73,0x74,0x20,0x51,0x75,0x61,0x6C,0x69,0x66, +0x69,0x65,0x64,0x20,0x43,0x41,0x20,0x52,0x6F,0x6F,0x74,0x82,0x01,0x01,0x30,0x0D, +0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01, +0x01,0x00,0x19,0xAB,0x75,0xEA,0xF8,0x8B,0x65,0x61,0x95,0x13,0xBA,0x69,0x04,0xEF, +0x86,0xCA,0x13,0xA0,0xC7,0xAA,0x4F,0x64,0x1B,0x3F,0x18,0xF6,0xA8,0x2D,0x2C,0x55, +0x8F,0x05,0xB7,0x30,0xEA,0x42,0x6A,0x1D,0xC0,0x25,0x51,0x2D,0xA7,0xBF,0x0C,0xB3, +0xED,0xEF,0x08,0x7F,0x6C,0x3C,0x46,0x1A,0xEA,0x18,0x43,0xDF,0x76,0xCC,0xF9,0x66, +0x86,0x9C,0x2C,0x68,0xF5,0xE9,0x17,0xF8,0x31,0xB3,0x18,0xC4,0xD6,0x48,0x7D,0x23, +0x4C,0x68,0xC1,0x7E,0xBB,0x01,0x14,0x6F,0xC5,0xD9,0x6E,0xDE,0xBB,0x04,0x42,0x6A, +0xF8,0xF6,0x5C,0x7D,0xE5,0xDA,0xFA,0x87,0xEB,0x0D,0x35,0x52,0x67,0xD0,0x9E,0x97, +0x76,0x05,0x93,0x3F,0x95,0xC7,0x01,0xE6,0x69,0x55,0x38,0x7F,0x10,0x61,0x99,0xC9, +0xE3,0x5F,0xA6,0xCA,0x3E,0x82,0x63,0x48,0xAA,0xE2,0x08,0x48,0x3E,0xAA,0xF2,0xB2, +0x85,0x62,0xA6,0xB4,0xA7,0xD9,0xBD,0x37,0x9C,0x68,0xB5,0x2D,0x56,0x7D,0xB0,0xB7, +0x3F,0xA0,0xB1,0x07,0xD6,0xE9,0x4F,0xDC,0xDE,0x45,0x71,0x30,0x32,0x7F,0x1B,0x2E, +0x09,0xF9,0xBF,0x52,0xA1,0xEE,0xC2,0x80,0x3E,0x06,0x5C,0x2E,0x55,0x40,0xC1,0x1B, +0xF5,0x70,0x45,0xB0,0xDC,0x5D,0xFA,0xF6,0x72,0x5A,0x77,0xD2,0x63,0xCD,0xCF,0x58, +0x89,0x00,0x42,0x63,0x3F,0x79,0x39,0xD0,0x44,0xB0,0x82,0x6E,0x41,0x19,0xE8,0xDD, +0xE0,0xC1,0x88,0x5A,0xD1,0x1E,0x71,0x93,0x1F,0x24,0x30,0x74,0xE5,0x1E,0xA8,0xDE, +0x3C,0x27,0x37,0x7F,0x83,0xAE,0x9E,0x77,0xCF,0xF0,0x30,0xB1,0xFF,0x4B,0x99,0xE8, +0xC6,0xA1, +}; + + +/* subject:/C=US/O=AffirmTrust/CN=AffirmTrust Commercial */ +/* issuer :/C=US/O=AffirmTrust/CN=AffirmTrust Commercial */ + + +const unsigned char AffirmTrust_Commercial_certificate[848]={ +0x30,0x82,0x03,0x4C,0x30,0x82,0x02,0x34,0xA0,0x03,0x02,0x01,0x02,0x02,0x08,0x77, +0x77,0x06,0x27,0x26,0xA9,0xB1,0x7C,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,0x44,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04, +0x06,0x13,0x02,0x55,0x53,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0B, +0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x31,0x1F,0x30,0x1D,0x06, +0x03,0x55,0x04,0x03,0x0C,0x16,0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73, +0x74,0x20,0x43,0x6F,0x6D,0x6D,0x65,0x72,0x63,0x69,0x61,0x6C,0x30,0x1E,0x17,0x0D, +0x31,0x30,0x30,0x31,0x32,0x39,0x31,0x34,0x30,0x36,0x30,0x36,0x5A,0x17,0x0D,0x33, +0x30,0x31,0x32,0x33,0x31,0x31,0x34,0x30,0x36,0x30,0x36,0x5A,0x30,0x44,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x14,0x30,0x12,0x06, +0x03,0x55,0x04,0x0A,0x0C,0x0B,0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73, +0x74,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x03,0x0C,0x16,0x41,0x66,0x66,0x69, +0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x20,0x43,0x6F,0x6D,0x6D,0x65,0x72,0x63,0x69, +0x61,0x6C,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D, +0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82, +0x01,0x01,0x00,0xF6,0x1B,0x4F,0x67,0x07,0x2B,0xA1,0x15,0xF5,0x06,0x22,0xCB,0x1F, +0x01,0xB2,0xE3,0x73,0x45,0x06,0x44,0x49,0x2C,0xBB,0x49,0x25,0x14,0xD6,0xCE,0xC3, +0xB7,0xAB,0x2C,0x4F,0xC6,0x41,0x32,0x94,0x57,0xFA,0x12,0xA7,0x5B,0x0E,0xE2,0x8F, +0x1F,0x1E,0x86,0x19,0xA7,0xAA,0xB5,0x2D,0xB9,0x5F,0x0D,0x8A,0xC2,0xAF,0x85,0x35, +0x79,0x32,0x2D,0xBB,0x1C,0x62,0x37,0xF2,0xB1,0x5B,0x4A,0x3D,0xCA,0xCD,0x71,0x5F, +0xE9,0x42,0xBE,0x94,0xE8,0xC8,0xDE,0xF9,0x22,0x48,0x64,0xC6,0xE5,0xAB,0xC6,0x2B, +0x6D,0xAD,0x05,0xF0,0xFA,0xD5,0x0B,0xCF,0x9A,0xE5,0xF0,0x50,0xA4,0x8B,0x3B,0x47, +0xA5,0x23,0x5B,0x7A,0x7A,0xF8,0x33,0x3F,0xB8,0xEF,0x99,0x97,0xE3,0x20,0xC1,0xD6, +0x28,0x89,0xCF,0x94,0xFB,0xB9,0x45,0xED,0xE3,0x40,0x17,0x11,0xD4,0x74,0xF0,0x0B, +0x31,0xE2,0x2B,0x26,0x6A,0x9B,0x4C,0x57,0xAE,0xAC,0x20,0x3E,0xBA,0x45,0x7A,0x05, +0xF3,0xBD,0x9B,0x69,0x15,0xAE,0x7D,0x4E,0x20,0x63,0xC4,0x35,0x76,0x3A,0x07,0x02, +0xC9,0x37,0xFD,0xC7,0x47,0xEE,0xE8,0xF1,0x76,0x1D,0x73,0x15,0xF2,0x97,0xA4,0xB5, +0xC8,0x7A,0x79,0xD9,0x42,0xAA,0x2B,0x7F,0x5C,0xFE,0xCE,0x26,0x4F,0xA3,0x66,0x81, +0x35,0xAF,0x44,0xBA,0x54,0x1E,0x1C,0x30,0x32,0x65,0x9D,0xE6,0x3C,0x93,0x5E,0x50, +0x4E,0x7A,0xE3,0x3A,0xD4,0x6E,0xCC,0x1A,0xFB,0xF9,0xD2,0x37,0xAE,0x24,0x2A,0xAB, +0x57,0x03,0x22,0x28,0x0D,0x49,0x75,0x7F,0xB7,0x28,0xDA,0x75,0xBF,0x8E,0xE3,0xDC, +0x0E,0x79,0x31,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x1D,0x06,0x03, +0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x9D,0x93,0xC6,0x53,0x8B,0x5E,0xCA,0xAF,0x3F, +0x9F,0x1E,0x0F,0xE5,0x99,0x95,0xBC,0x24,0xF6,0x94,0x8F,0x30,0x0F,0x06,0x03,0x55, +0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03, +0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0D,0x06,0x09, +0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01,0x00, +0x58,0xAC,0xF4,0x04,0x0E,0xCD,0xC0,0x0D,0xFF,0x0A,0xFD,0xD4,0xBA,0x16,0x5F,0x29, +0xBD,0x7B,0x68,0x99,0x58,0x49,0xD2,0xB4,0x1D,0x37,0x4D,0x7F,0x27,0x7D,0x46,0x06, +0x5D,0x43,0xC6,0x86,0x2E,0x3E,0x73,0xB2,0x26,0x7D,0x4F,0x93,0xA9,0xB6,0xC4,0x2A, +0x9A,0xAB,0x21,0x97,0x14,0xB1,0xDE,0x8C,0xD3,0xAB,0x89,0x15,0xD8,0x6B,0x24,0xD4, +0xF1,0x16,0xAE,0xD8,0xA4,0x5C,0xD4,0x7F,0x51,0x8E,0xED,0x18,0x01,0xB1,0x93,0x63, +0xBD,0xBC,0xF8,0x61,0x80,0x9A,0x9E,0xB1,0xCE,0x42,0x70,0xE2,0xA9,0x7D,0x06,0x25, +0x7D,0x27,0xA1,0xFE,0x6F,0xEC,0xB3,0x1E,0x24,0xDA,0xE3,0x4B,0x55,0x1A,0x00,0x3B, +0x35,0xB4,0x3B,0xD9,0xD7,0x5D,0x30,0xFD,0x81,0x13,0x89,0xF2,0xC2,0x06,0x2B,0xED, +0x67,0xC4,0x8E,0xC9,0x43,0xB2,0x5C,0x6B,0x15,0x89,0x02,0xBC,0x62,0xFC,0x4E,0xF2, +0xB5,0x33,0xAA,0xB2,0x6F,0xD3,0x0A,0xA2,0x50,0xE3,0xF6,0x3B,0xE8,0x2E,0x44,0xC2, +0xDB,0x66,0x38,0xA9,0x33,0x56,0x48,0xF1,0x6D,0x1B,0x33,0x8D,0x0D,0x8C,0x3F,0x60, +0x37,0x9D,0xD3,0xCA,0x6D,0x7E,0x34,0x7E,0x0D,0x9F,0x72,0x76,0x8B,0x1B,0x9F,0x72, +0xFD,0x52,0x35,0x41,0x45,0x02,0x96,0x2F,0x1C,0xB2,0x9A,0x73,0x49,0x21,0xB1,0x49, +0x47,0x45,0x47,0xB4,0xEF,0x6A,0x34,0x11,0xC9,0x4D,0x9A,0xCC,0x59,0xB7,0xD6,0x02, +0x9E,0x5A,0x4E,0x65,0xB5,0x94,0xAE,0x1B,0xDF,0x29,0xB0,0x16,0xF1,0xBF,0x00,0x9E, +0x07,0x3A,0x17,0x64,0xB5,0x04,0xB5,0x23,0x21,0x99,0x0A,0x95,0x3B,0x97,0x7C,0xEF, +}; + + +/* subject:/C=US/O=AffirmTrust/CN=AffirmTrust Networking */ +/* issuer :/C=US/O=AffirmTrust/CN=AffirmTrust Networking */ + + +const unsigned char AffirmTrust_Networking_certificate[848]={ +0x30,0x82,0x03,0x4C,0x30,0x82,0x02,0x34,0xA0,0x03,0x02,0x01,0x02,0x02,0x08,0x7C, +0x4F,0x04,0x39,0x1C,0xD4,0x99,0x2D,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x44,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04, +0x06,0x13,0x02,0x55,0x53,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0B, +0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x31,0x1F,0x30,0x1D,0x06, +0x03,0x55,0x04,0x03,0x0C,0x16,0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73, +0x74,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x69,0x6E,0x67,0x30,0x1E,0x17,0x0D, +0x31,0x30,0x30,0x31,0x32,0x39,0x31,0x34,0x30,0x38,0x32,0x34,0x5A,0x17,0x0D,0x33, +0x30,0x31,0x32,0x33,0x31,0x31,0x34,0x30,0x38,0x32,0x34,0x5A,0x30,0x44,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x14,0x30,0x12,0x06, +0x03,0x55,0x04,0x0A,0x0C,0x0B,0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73, +0x74,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x03,0x0C,0x16,0x41,0x66,0x66,0x69, +0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x69, +0x6E,0x67,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D, +0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82, +0x01,0x01,0x00,0xB4,0x84,0xCC,0x33,0x17,0x2E,0x6B,0x94,0x6C,0x6B,0x61,0x52,0xA0, +0xEB,0xA3,0xCF,0x79,0x94,0x4C,0xE5,0x94,0x80,0x99,0xCB,0x55,0x64,0x44,0x65,0x8F, +0x67,0x64,0xE2,0x06,0xE3,0x5C,0x37,0x49,0xF6,0x2F,0x9B,0x84,0x84,0x1E,0x2D,0xF2, +0x60,0x9D,0x30,0x4E,0xCC,0x84,0x85,0xE2,0x2C,0xCF,0x1E,0x9E,0xFE,0x36,0xAB,0x33, +0x77,0x35,0x44,0xD8,0x35,0x96,0x1A,0x3D,0x36,0xE8,0x7A,0x0E,0xD8,0xD5,0x47,0xA1, +0x6A,0x69,0x8B,0xD9,0xFC,0xBB,0x3A,0xAE,0x79,0x5A,0xD5,0xF4,0xD6,0x71,0xBB,0x9A, +0x90,0x23,0x6B,0x9A,0xB7,0x88,0x74,0x87,0x0C,0x1E,0x5F,0xB9,0x9E,0x2D,0xFA,0xAB, +0x53,0x2B,0xDC,0xBB,0x76,0x3E,0x93,0x4C,0x08,0x08,0x8C,0x1E,0xA2,0x23,0x1C,0xD4, +0x6A,0xAD,0x22,0xBA,0x99,0x01,0x2E,0x6D,0x65,0xCB,0xBE,0x24,0x66,0x55,0x24,0x4B, +0x40,0x44,0xB1,0x1B,0xD7,0xE1,0xC2,0x85,0xC0,0xDE,0x10,0x3F,0x3D,0xED,0xB8,0xFC, +0xF1,0xF1,0x23,0x53,0xDC,0xBF,0x65,0x97,0x6F,0xD9,0xF9,0x40,0x71,0x8D,0x7D,0xBD, +0x95,0xD4,0xCE,0xBE,0xA0,0x5E,0x27,0x23,0xDE,0xFD,0xA6,0xD0,0x26,0x0E,0x00,0x29, +0xEB,0x3C,0x46,0xF0,0x3D,0x60,0xBF,0x3F,0x50,0xD2,0xDC,0x26,0x41,0x51,0x9E,0x14, +0x37,0x42,0x04,0xA3,0x70,0x57,0xA8,0x1B,0x87,0xED,0x2D,0xFA,0x7B,0xEE,0x8C,0x0A, +0xE3,0xA9,0x66,0x89,0x19,0xCB,0x41,0xF9,0xDD,0x44,0x36,0x61,0xCF,0xE2,0x77,0x46, +0xC8,0x7D,0xF6,0xF4,0x92,0x81,0x36,0xFD,0xDB,0x34,0xF1,0x72,0x7E,0xF3,0x0C,0x16, +0xBD,0xB4,0x15,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x1D,0x06,0x03, +0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x07,0x1F,0xD2,0xE7,0x9C,0xDA,0xC2,0x6E,0xA2, +0x40,0xB4,0xB0,0x7A,0x50,0x10,0x50,0x74,0xC4,0xC8,0xBD,0x30,0x0F,0x06,0x03,0x55, +0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03, +0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0D,0x06,0x09, +0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00, +0x89,0x57,0xB2,0x16,0x7A,0xA8,0xC2,0xFD,0xD6,0xD9,0x9B,0x9B,0x34,0xC2,0x9C,0xB4, +0x32,0x14,0x4D,0xA7,0xA4,0xDF,0xEC,0xBE,0xA7,0xBE,0xF8,0x43,0xDB,0x91,0x37,0xCE, +0xB4,0x32,0x2E,0x50,0x55,0x1A,0x35,0x4E,0x76,0x43,0x71,0x20,0xEF,0x93,0x77,0x4E, +0x15,0x70,0x2E,0x87,0xC3,0xC1,0x1D,0x6D,0xDC,0xCB,0xB5,0x27,0xD4,0x2C,0x56,0xD1, +0x52,0x53,0x3A,0x44,0xD2,0x73,0xC8,0xC4,0x1B,0x05,0x65,0x5A,0x62,0x92,0x9C,0xEE, +0x41,0x8D,0x31,0xDB,0xE7,0x34,0xEA,0x59,0x21,0xD5,0x01,0x7A,0xD7,0x64,0xB8,0x64, +0x39,0xCD,0xC9,0xED,0xAF,0xED,0x4B,0x03,0x48,0xA7,0xA0,0x99,0x01,0x80,0xDC,0x65, +0xA3,0x36,0xAE,0x65,0x59,0x48,0x4F,0x82,0x4B,0xC8,0x65,0xF1,0x57,0x1D,0xE5,0x59, +0x2E,0x0A,0x3F,0x6C,0xD8,0xD1,0xF5,0xE5,0x09,0xB4,0x6C,0x54,0x00,0x0A,0xE0,0x15, +0x4D,0x87,0x75,0x6D,0xB7,0x58,0x96,0x5A,0xDD,0x6D,0xD2,0x00,0xA0,0xF4,0x9B,0x48, +0xBE,0xC3,0x37,0xA4,0xBA,0x36,0xE0,0x7C,0x87,0x85,0x97,0x1A,0x15,0xA2,0xDE,0x2E, +0xA2,0x5B,0xBD,0xAF,0x18,0xF9,0x90,0x50,0xCD,0x70,0x59,0xF8,0x27,0x67,0x47,0xCB, +0xC7,0xA0,0x07,0x3A,0x7D,0xD1,0x2C,0x5D,0x6C,0x19,0x3A,0x66,0xB5,0x7D,0xFD,0x91, +0x6F,0x82,0xB1,0xBE,0x08,0x93,0xDB,0x14,0x47,0xF1,0xA2,0x37,0xC7,0x45,0x9E,0x3C, +0xC7,0x77,0xAF,0x64,0xA8,0x93,0xDF,0xF6,0x69,0x83,0x82,0x60,0xF2,0x49,0x42,0x34, +0xED,0x5A,0x00,0x54,0x85,0x1C,0x16,0x36,0x92,0x0C,0x5C,0xFA,0xA6,0xAD,0xBF,0xDB, +}; + + +/* subject:/C=US/O=AffirmTrust/CN=AffirmTrust Premium */ +/* issuer :/C=US/O=AffirmTrust/CN=AffirmTrust Premium */ + + +const unsigned char AffirmTrust_Premium_certificate[1354]={ +0x30,0x82,0x05,0x46,0x30,0x82,0x03,0x2E,0xA0,0x03,0x02,0x01,0x02,0x02,0x08,0x6D, +0x8C,0x14,0x46,0xB1,0xA6,0x0A,0xEE,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x0C,0x05,0x00,0x30,0x41,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04, +0x06,0x13,0x02,0x55,0x53,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0B, +0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x31,0x1C,0x30,0x1A,0x06, +0x03,0x55,0x04,0x03,0x0C,0x13,0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73, +0x74,0x20,0x50,0x72,0x65,0x6D,0x69,0x75,0x6D,0x30,0x1E,0x17,0x0D,0x31,0x30,0x30, +0x31,0x32,0x39,0x31,0x34,0x31,0x30,0x33,0x36,0x5A,0x17,0x0D,0x34,0x30,0x31,0x32, +0x33,0x31,0x31,0x34,0x31,0x30,0x33,0x36,0x5A,0x30,0x41,0x31,0x0B,0x30,0x09,0x06, +0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04, +0x0A,0x0C,0x0B,0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x31,0x1C, +0x30,0x1A,0x06,0x03,0x55,0x04,0x03,0x0C,0x13,0x41,0x66,0x66,0x69,0x72,0x6D,0x54, +0x72,0x75,0x73,0x74,0x20,0x50,0x72,0x65,0x6D,0x69,0x75,0x6D,0x30,0x82,0x02,0x22, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03, +0x82,0x02,0x0F,0x00,0x30,0x82,0x02,0x0A,0x02,0x82,0x02,0x01,0x00,0xC4,0x12,0xDF, +0xA9,0x5F,0xFE,0x41,0xDD,0xDD,0xF5,0x9F,0x8A,0xE3,0xF6,0xAC,0xE1,0x3C,0x78,0x9A, +0xBC,0xD8,0xF0,0x7F,0x7A,0xA0,0x33,0x2A,0xDC,0x8D,0x20,0x5B,0xAE,0x2D,0x6F,0xE7, +0x93,0xD9,0x36,0x70,0x6A,0x68,0xCF,0x8E,0x51,0xA3,0x85,0x5B,0x67,0x04,0xA0,0x10, +0x24,0x6F,0x5D,0x28,0x82,0xC1,0x97,0x57,0xD8,0x48,0x29,0x13,0xB6,0xE1,0xBE,0x91, +0x4D,0xDF,0x85,0x0C,0x53,0x18,0x9A,0x1E,0x24,0xA2,0x4F,0x8F,0xF0,0xA2,0x85,0x0B, +0xCB,0xF4,0x29,0x7F,0xD2,0xA4,0x58,0xEE,0x26,0x4D,0xC9,0xAA,0xA8,0x7B,0x9A,0xD9, +0xFA,0x38,0xDE,0x44,0x57,0x15,0xE5,0xF8,0x8C,0xC8,0xD9,0x48,0xE2,0x0D,0x16,0x27, +0x1D,0x1E,0xC8,0x83,0x85,0x25,0xB7,0xBA,0xAA,0x55,0x41,0xCC,0x03,0x22,0x4B,0x2D, +0x91,0x8D,0x8B,0xE6,0x89,0xAF,0x66,0xC7,0xE9,0xFF,0x2B,0xE9,0x3C,0xAC,0xDA,0xD2, +0xB3,0xC3,0xE1,0x68,0x9C,0x89,0xF8,0x7A,0x00,0x56,0xDE,0xF4,0x55,0x95,0x6C,0xFB, +0xBA,0x64,0xDD,0x62,0x8B,0xDF,0x0B,0x77,0x32,0xEB,0x62,0xCC,0x26,0x9A,0x9B,0xBB, +0xAA,0x62,0x83,0x4C,0xB4,0x06,0x7A,0x30,0xC8,0x29,0xBF,0xED,0x06,0x4D,0x97,0xB9, +0x1C,0xC4,0x31,0x2B,0xD5,0x5F,0xBC,0x53,0x12,0x17,0x9C,0x99,0x57,0x29,0x66,0x77, +0x61,0x21,0x31,0x07,0x2E,0x25,0x49,0x9D,0x18,0xF2,0xEE,0xF3,0x2B,0x71,0x8C,0xB5, +0xBA,0x39,0x07,0x49,0x77,0xFC,0xEF,0x2E,0x92,0x90,0x05,0x8D,0x2D,0x2F,0x77,0x7B, +0xEF,0x43,0xBF,0x35,0xBB,0x9A,0xD8,0xF9,0x73,0xA7,0x2C,0xF2,0xD0,0x57,0xEE,0x28, +0x4E,0x26,0x5F,0x8F,0x90,0x68,0x09,0x2F,0xB8,0xF8,0xDC,0x06,0xE9,0x2E,0x9A,0x3E, +0x51,0xA7,0xD1,0x22,0xC4,0x0A,0xA7,0x38,0x48,0x6C,0xB3,0xF9,0xFF,0x7D,0xAB,0x86, +0x57,0xE3,0xBA,0xD6,0x85,0x78,0x77,0xBA,0x43,0xEA,0x48,0x7F,0xF6,0xD8,0xBE,0x23, +0x6D,0x1E,0xBF,0xD1,0x36,0x6C,0x58,0x5C,0xF1,0xEE,0xA4,0x19,0x54,0x1A,0xF5,0x03, +0xD2,0x76,0xE6,0xE1,0x8C,0xBD,0x3C,0xB3,0xD3,0x48,0x4B,0xE2,0xC8,0xF8,0x7F,0x92, +0xA8,0x76,0x46,0x9C,0x42,0x65,0x3E,0xA4,0x1E,0xC1,0x07,0x03,0x5A,0x46,0x2D,0xB8, +0x97,0xF3,0xB7,0xD5,0xB2,0x55,0x21,0xEF,0xBA,0xDC,0x4C,0x00,0x97,0xFB,0x14,0x95, +0x27,0x33,0xBF,0xE8,0x43,0x47,0x46,0xD2,0x08,0x99,0x16,0x60,0x3B,0x9A,0x7E,0xD2, +0xE6,0xED,0x38,0xEA,0xEC,0x01,0x1E,0x3C,0x48,0x56,0x49,0x09,0xC7,0x4C,0x37,0x00, +0x9E,0x88,0x0E,0xC0,0x73,0xE1,0x6F,0x66,0xE9,0x72,0x47,0x30,0x3E,0x10,0xE5,0x0B, +0x03,0xC9,0x9A,0x42,0x00,0x6C,0xC5,0x94,0x7E,0x61,0xC4,0x8A,0xDF,0x7F,0x82,0x1A, +0x0B,0x59,0xC4,0x59,0x32,0x77,0xB3,0xBC,0x60,0x69,0x56,0x39,0xFD,0xB4,0x06,0x7B, +0x2C,0xD6,0x64,0x36,0xD9,0xBD,0x48,0xED,0x84,0x1F,0x7E,0xA5,0x22,0x8F,0x2A,0xB8, +0x42,0xF4,0x82,0xB7,0xD4,0x53,0x90,0x78,0x4E,0x2D,0x1A,0xFD,0x81,0x6F,0x44,0xD7, +0x3B,0x01,0x74,0x96,0x42,0xE0,0x00,0xE2,0x2E,0x6B,0xEA,0xC5,0xEE,0x72,0xAC,0xBB, +0xBF,0xFE,0xEA,0xAA,0xA8,0xF8,0xDC,0xF6,0xB2,0x79,0x8A,0xB6,0x67,0x02,0x03,0x01, +0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04, +0x14,0x9D,0xC0,0x67,0xA6,0x0C,0x22,0xD9,0x26,0xF5,0x45,0xAB,0xA6,0x65,0x52,0x11, +0x27,0xD8,0x45,0xAC,0x63,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04, +0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF, +0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D, +0x01,0x01,0x0C,0x05,0x00,0x03,0x82,0x02,0x01,0x00,0xB3,0x57,0x4D,0x10,0x62,0x4E, +0x3A,0xE4,0xAC,0xEA,0xB8,0x1C,0xAF,0x32,0x23,0xC8,0xB3,0x49,0x5A,0x51,0x9C,0x76, +0x28,0x8D,0x79,0xAA,0x57,0x46,0x17,0xD5,0xF5,0x52,0xF6,0xB7,0x44,0xE8,0x08,0x44, +0xBF,0x18,0x84,0xD2,0x0B,0x80,0xCD,0xC5,0x12,0xFD,0x00,0x55,0x05,0x61,0x87,0x41, +0xDC,0xB5,0x24,0x9E,0x3C,0xC4,0xD8,0xC8,0xFB,0x70,0x9E,0x2F,0x78,0x96,0x83,0x20, +0x36,0xDE,0x7C,0x0F,0x69,0x13,0x88,0xA5,0x75,0x36,0x98,0x08,0xA6,0xC6,0xDF,0xAC, +0xCE,0xE3,0x58,0xD6,0xB7,0x3E,0xDE,0xBA,0xF3,0xEB,0x34,0x40,0xD8,0xA2,0x81,0xF5, +0x78,0x3F,0x2F,0xD5,0xA5,0xFC,0xD9,0xA2,0xD4,0x5E,0x04,0x0E,0x17,0xAD,0xFE,0x41, +0xF0,0xE5,0xB2,0x72,0xFA,0x44,0x82,0x33,0x42,0xE8,0x2D,0x58,0xF7,0x56,0x8C,0x62, +0x3F,0xBA,0x42,0xB0,0x9C,0x0C,0x5C,0x7E,0x2E,0x65,0x26,0x5C,0x53,0x4F,0x00,0xB2, +0x78,0x7E,0xA1,0x0D,0x99,0x2D,0x8D,0xB8,0x1D,0x8E,0xA2,0xC4,0xB0,0xFD,0x60,0xD0, +0x30,0xA4,0x8E,0xC8,0x04,0x62,0xA9,0xC4,0xED,0x35,0xDE,0x7A,0x97,0xED,0x0E,0x38, +0x5E,0x92,0x2F,0x93,0x70,0xA5,0xA9,0x9C,0x6F,0xA7,0x7D,0x13,0x1D,0x7E,0xC6,0x08, +0x48,0xB1,0x5E,0x67,0xEB,0x51,0x08,0x25,0xE9,0xE6,0x25,0x6B,0x52,0x29,0x91,0x9C, +0xD2,0x39,0x73,0x08,0x57,0xDE,0x99,0x06,0xB4,0x5B,0x9D,0x10,0x06,0xE1,0xC2,0x00, +0xA8,0xB8,0x1C,0x4A,0x02,0x0A,0x14,0xD0,0xC1,0x41,0xCA,0xFB,0x8C,0x35,0x21,0x7D, +0x82,0x38,0xF2,0xA9,0x54,0x91,0x19,0x35,0x93,0x94,0x6D,0x6A,0x3A,0xC5,0xB2,0xD0, +0xBB,0x89,0x86,0x93,0xE8,0x9B,0xC9,0x0F,0x3A,0xA7,0x7A,0xB8,0xA1,0xF0,0x78,0x46, +0xFA,0xFC,0x37,0x2F,0xE5,0x8A,0x84,0xF3,0xDF,0xFE,0x04,0xD9,0xA1,0x68,0xA0,0x2F, +0x24,0xE2,0x09,0x95,0x06,0xD5,0x95,0xCA,0xE1,0x24,0x96,0xEB,0x7C,0xF6,0x93,0x05, +0xBB,0xED,0x73,0xE9,0x2D,0xD1,0x75,0x39,0xD7,0xE7,0x24,0xDB,0xD8,0x4E,0x5F,0x43, +0x8F,0x9E,0xD0,0x14,0x39,0xBF,0x55,0x70,0x48,0x99,0x57,0x31,0xB4,0x9C,0xEE,0x4A, +0x98,0x03,0x96,0x30,0x1F,0x60,0x06,0xEE,0x1B,0x23,0xFE,0x81,0x60,0x23,0x1A,0x47, +0x62,0x85,0xA5,0xCC,0x19,0x34,0x80,0x6F,0xB3,0xAC,0x1A,0xE3,0x9F,0xF0,0x7B,0x48, +0xAD,0xD5,0x01,0xD9,0x67,0xB6,0xA9,0x72,0x93,0xEA,0x2D,0x66,0xB5,0xB2,0xB8,0xE4, +0x3D,0x3C,0xB2,0xEF,0x4C,0x8C,0xEA,0xEB,0x07,0xBF,0xAB,0x35,0x9A,0x55,0x86,0xBC, +0x18,0xA6,0xB5,0xA8,0x5E,0xB4,0x83,0x6C,0x6B,0x69,0x40,0xD3,0x9F,0xDC,0xF1,0xC3, +0x69,0x6B,0xB9,0xE1,0x6D,0x09,0xF4,0xF1,0xAA,0x50,0x76,0x0A,0x7A,0x7D,0x7A,0x17, +0xA1,0x55,0x96,0x42,0x99,0x31,0x09,0xDD,0x60,0x11,0x8D,0x05,0x30,0x7E,0xE6,0x8E, +0x46,0xD1,0x9D,0x14,0xDA,0xC7,0x17,0xE4,0x05,0x96,0x8C,0xC4,0x24,0xB5,0x1B,0xCF, +0x14,0x07,0xB2,0x40,0xF8,0xA3,0x9E,0x41,0x86,0xBC,0x04,0xD0,0x6B,0x96,0xC8,0x2A, +0x80,0x34,0xFD,0xBF,0xEF,0x06,0xA3,0xDD,0x58,0xC5,0x85,0x3D,0x3E,0x8F,0xFE,0x9E, +0x29,0xE0,0xB6,0xB8,0x09,0x68,0x19,0x1C,0x18,0x43, +}; + + +/* subject:/C=US/O=AffirmTrust/CN=AffirmTrust Premium ECC */ +/* issuer :/C=US/O=AffirmTrust/CN=AffirmTrust Premium ECC */ + + +const unsigned char AffirmTrust_Premium_ECC_certificate[514]={ +0x30,0x82,0x01,0xFE,0x30,0x82,0x01,0x85,0xA0,0x03,0x02,0x01,0x02,0x02,0x08,0x74, +0x97,0x25,0x8A,0xC7,0x3F,0x7A,0x54,0x30,0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D, +0x04,0x03,0x03,0x30,0x45,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02, +0x55,0x53,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0B,0x41,0x66,0x66, +0x69,0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x31,0x20,0x30,0x1E,0x06,0x03,0x55,0x04, +0x03,0x0C,0x17,0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x20,0x50, +0x72,0x65,0x6D,0x69,0x75,0x6D,0x20,0x45,0x43,0x43,0x30,0x1E,0x17,0x0D,0x31,0x30, +0x30,0x31,0x32,0x39,0x31,0x34,0x32,0x30,0x32,0x34,0x5A,0x17,0x0D,0x34,0x30,0x31, +0x32,0x33,0x31,0x31,0x34,0x32,0x30,0x32,0x34,0x5A,0x30,0x45,0x31,0x0B,0x30,0x09, +0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x14,0x30,0x12,0x06,0x03,0x55, +0x04,0x0A,0x0C,0x0B,0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x31, +0x20,0x30,0x1E,0x06,0x03,0x55,0x04,0x03,0x0C,0x17,0x41,0x66,0x66,0x69,0x72,0x6D, +0x54,0x72,0x75,0x73,0x74,0x20,0x50,0x72,0x65,0x6D,0x69,0x75,0x6D,0x20,0x45,0x43, +0x43,0x30,0x76,0x30,0x10,0x06,0x07,0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,0x06,0x05, +0x2B,0x81,0x04,0x00,0x22,0x03,0x62,0x00,0x04,0x0D,0x30,0x5E,0x1B,0x15,0x9D,0x03, +0xD0,0xA1,0x79,0x35,0xB7,0x3A,0x3C,0x92,0x7A,0xCA,0x15,0x1C,0xCD,0x62,0xF3,0x9C, +0x26,0x5C,0x07,0x3D,0xE5,0x54,0xFA,0xA3,0xD6,0xCC,0x12,0xEA,0xF4,0x14,0x5F,0xE8, +0x8E,0x19,0xAB,0x2F,0x2E,0x48,0xE6,0xAC,0x18,0x43,0x78,0xAC,0xD0,0x37,0xC3,0xBD, +0xB2,0xCD,0x2C,0xE6,0x47,0xE2,0x1A,0xE6,0x63,0xB8,0x3D,0x2E,0x2F,0x78,0xC4,0x4F, +0xDB,0xF4,0x0F,0xA4,0x68,0x4C,0x55,0x72,0x6B,0x95,0x1D,0x4E,0x18,0x42,0x95,0x78, +0xCC,0x37,0x3C,0x91,0xE2,0x9B,0x65,0x2B,0x29,0xA3,0x42,0x30,0x40,0x30,0x1D,0x06, +0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x9A,0xAF,0x29,0x7A,0xC0,0x11,0x35,0x35, +0x26,0x51,0x30,0x00,0xC3,0x6A,0xFE,0x40,0xD5,0xAE,0xD6,0x3C,0x30,0x0F,0x06,0x03, +0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06, +0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0A,0x06, +0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x03,0x67,0x00,0x30,0x64,0x02,0x30, +0x17,0x09,0xF3,0x87,0x88,0x50,0x5A,0xAF,0xC8,0xC0,0x42,0xBF,0x47,0x5F,0xF5,0x6C, +0x6A,0x86,0xE0,0xC4,0x27,0x74,0xE4,0x38,0x53,0xD7,0x05,0x7F,0x1B,0x34,0xE3,0xC6, +0x2F,0xB3,0xCA,0x09,0x3C,0x37,0x9D,0xD7,0xE7,0xB8,0x46,0xF1,0xFD,0xA1,0xE2,0x71, +0x02,0x30,0x42,0x59,0x87,0x43,0xD4,0x51,0xDF,0xBA,0xD3,0x09,0x32,0x5A,0xCE,0x88, +0x7E,0x57,0x3D,0x9C,0x5F,0x42,0x6B,0xF5,0x07,0x2D,0xB5,0xF0,0x82,0x93,0xF9,0x59, +0x6F,0xAE,0x64,0xFA,0x58,0xE5,0x8B,0x1E,0xE3,0x63,0xBE,0xB5,0x81,0xCD,0x6F,0x02, +0x8C,0x79, +}; + + +/* subject:/C=US/O=America Online Inc./CN=America Online Root Certification Authority 1 */ +/* issuer :/C=US/O=America Online Inc./CN=America Online Root Certification Authority 1 */ + + +const unsigned char America_Online_Root_Certification_Authority_1_certificate[936]={ +0x30,0x82,0x03,0xA4,0x30,0x82,0x02,0x8C,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30, +0x63,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x1C, +0x30,0x1A,0x06,0x03,0x55,0x04,0x0A,0x13,0x13,0x41,0x6D,0x65,0x72,0x69,0x63,0x61, +0x20,0x4F,0x6E,0x6C,0x69,0x6E,0x65,0x20,0x49,0x6E,0x63,0x2E,0x31,0x36,0x30,0x34, +0x06,0x03,0x55,0x04,0x03,0x13,0x2D,0x41,0x6D,0x65,0x72,0x69,0x63,0x61,0x20,0x4F, +0x6E,0x6C,0x69,0x6E,0x65,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69, +0x74,0x79,0x20,0x31,0x30,0x1E,0x17,0x0D,0x30,0x32,0x30,0x35,0x32,0x38,0x30,0x36, +0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x37,0x31,0x31,0x31,0x39,0x32,0x30,0x34, +0x33,0x30,0x30,0x5A,0x30,0x63,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13, +0x02,0x55,0x53,0x31,0x1C,0x30,0x1A,0x06,0x03,0x55,0x04,0x0A,0x13,0x13,0x41,0x6D, +0x65,0x72,0x69,0x63,0x61,0x20,0x4F,0x6E,0x6C,0x69,0x6E,0x65,0x20,0x49,0x6E,0x63, +0x2E,0x31,0x36,0x30,0x34,0x06,0x03,0x55,0x04,0x03,0x13,0x2D,0x41,0x6D,0x65,0x72, +0x69,0x63,0x61,0x20,0x4F,0x6E,0x6C,0x69,0x6E,0x65,0x20,0x52,0x6F,0x6F,0x74,0x20, +0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75, +0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x20,0x31,0x30,0x82,0x01,0x22,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F, +0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xA8,0x2F,0xE8,0xA4,0x69,0x06, +0x03,0x47,0xC3,0xE9,0x2A,0x98,0xFF,0x19,0xA2,0x70,0x9A,0xC6,0x50,0xB2,0x7E,0xA5, +0xDF,0x68,0x4D,0x1B,0x7C,0x0F,0xB6,0x97,0x68,0x7D,0x2D,0xA6,0x8B,0x97,0xE9,0x64, +0x86,0xC9,0xA3,0xEF,0xA0,0x86,0xBF,0x60,0x65,0x9C,0x4B,0x54,0x88,0xC2,0x48,0xC5, +0x4A,0x39,0xBF,0x14,0xE3,0x59,0x55,0xE5,0x19,0xB4,0x74,0xC8,0xB4,0x05,0x39,0x5C, +0x16,0xA5,0xE2,0x95,0x05,0xE0,0x12,0xAE,0x59,0x8B,0xA2,0x33,0x68,0x58,0x1C,0xA6, +0xD4,0x15,0xB7,0xD8,0x9F,0xD7,0xDC,0x71,0xAB,0x7E,0x9A,0xBF,0x9B,0x8E,0x33,0x0F, +0x22,0xFD,0x1F,0x2E,0xE7,0x07,0x36,0xEF,0x62,0x39,0xC5,0xDD,0xCB,0xBA,0x25,0x14, +0x23,0xDE,0x0C,0xC6,0x3D,0x3C,0xCE,0x82,0x08,0xE6,0x66,0x3E,0xDA,0x51,0x3B,0x16, +0x3A,0xA3,0x05,0x7F,0xA0,0xDC,0x87,0xD5,0x9C,0xFC,0x72,0xA9,0xA0,0x7D,0x78,0xE4, +0xB7,0x31,0x55,0x1E,0x65,0xBB,0xD4,0x61,0xB0,0x21,0x60,0xED,0x10,0x32,0x72,0xC5, +0x92,0x25,0x1E,0xF8,0x90,0x4A,0x18,0x78,0x47,0xDF,0x7E,0x30,0x37,0x3E,0x50,0x1B, +0xDB,0x1C,0xD3,0x6B,0x9A,0x86,0x53,0x07,0xB0,0xEF,0xAC,0x06,0x78,0xF8,0x84,0x99, +0xFE,0x21,0x8D,0x4C,0x80,0xB6,0x0C,0x82,0xF6,0x66,0x70,0x79,0x1A,0xD3,0x4F,0xA3, +0xCF,0xF1,0xCF,0x46,0xB0,0x4B,0x0F,0x3E,0xDD,0x88,0x62,0xB8,0x8C,0xA9,0x09,0x28, +0x3B,0x7A,0xC7,0x97,0xE1,0x1E,0xE5,0xF4,0x9F,0xC0,0xC0,0xAE,0x24,0xA0,0xC8,0xA1, +0xD9,0x0F,0xD6,0x7B,0x26,0x82,0x69,0x32,0x3D,0xA7,0x02,0x03,0x01,0x00,0x01,0xA3, +0x63,0x30,0x61,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30, +0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x00, +0xAD,0xD9,0xA3,0xF6,0x79,0xF6,0x6E,0x74,0xA9,0x7F,0x33,0x3D,0x81,0x17,0xD7,0x4C, +0xCF,0x33,0xDE,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14, +0x00,0xAD,0xD9,0xA3,0xF6,0x79,0xF6,0x6E,0x74,0xA9,0x7F,0x33,0x3D,0x81,0x17,0xD7, +0x4C,0xCF,0x33,0xDE,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04, +0x03,0x02,0x01,0x86,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01, +0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x7C,0x8A,0xD1,0x1F,0x18,0x37,0x82,0xE0, +0xB8,0xB0,0xA3,0xED,0x56,0x95,0xC8,0x62,0x61,0x9C,0x05,0xA2,0xCD,0xC2,0x62,0x26, +0x61,0xCD,0x10,0x16,0xD7,0xCC,0xB4,0x65,0x34,0xD0,0x11,0x8A,0xAD,0xA8,0xA9,0x05, +0x66,0xEF,0x74,0xF3,0x6D,0x5F,0x9D,0x99,0xAF,0xF6,0x8B,0xFB,0xEB,0x52,0xB2,0x05, +0x98,0xA2,0x6F,0x2A,0xC5,0x54,0xBD,0x25,0xBD,0x5F,0xAE,0xC8,0x86,0xEA,0x46,0x2C, +0xC1,0xB3,0xBD,0xC1,0xE9,0x49,0x70,0x18,0x16,0x97,0x08,0x13,0x8C,0x20,0xE0,0x1B, +0x2E,0x3A,0x47,0xCB,0x1E,0xE4,0x00,0x30,0x95,0x5B,0xF4,0x45,0xA3,0xC0,0x1A,0xB0, +0x01,0x4E,0xAB,0xBD,0xC0,0x23,0x6E,0x63,0x3F,0x80,0x4A,0xC5,0x07,0xED,0xDC,0xE2, +0x6F,0xC7,0xC1,0x62,0xF1,0xE3,0x72,0xD6,0x04,0xC8,0x74,0x67,0x0B,0xFA,0x88,0xAB, +0xA1,0x01,0xC8,0x6F,0xF0,0x14,0xAF,0xD2,0x99,0xCD,0x51,0x93,0x7E,0xED,0x2E,0x38, +0xC7,0xBD,0xCE,0x46,0x50,0x3D,0x72,0xE3,0x79,0x25,0x9D,0x9B,0x88,0x2B,0x10,0x20, +0xDD,0xA5,0xB8,0x32,0x9F,0x8D,0xE0,0x29,0xDF,0x21,0x74,0x86,0x82,0xDB,0x2F,0x82, +0x30,0xC6,0xC7,0x35,0x86,0xB3,0xF9,0x96,0x5F,0x46,0xDB,0x0C,0x45,0xFD,0xF3,0x50, +0xC3,0x6F,0xC6,0xC3,0x48,0xAD,0x46,0xA6,0xE1,0x27,0x47,0x0A,0x1D,0x0E,0x9B,0xB6, +0xC2,0x77,0x7F,0x63,0xF2,0xE0,0x7D,0x1A,0xBE,0xFC,0xE0,0xDF,0xD7,0xC7,0xA7,0x6C, +0xB0,0xF9,0xAE,0xBA,0x3C,0xFD,0x74,0xB4,0x11,0xE8,0x58,0x0D,0x80,0xBC,0xD3,0xA8, +0x80,0x3A,0x99,0xED,0x75,0xCC,0x46,0x7B, +}; + + +/* subject:/C=US/O=America Online Inc./CN=America Online Root Certification Authority 2 */ +/* issuer :/C=US/O=America Online Inc./CN=America Online Root Certification Authority 2 */ + + +const unsigned char America_Online_Root_Certification_Authority_2_certificate[1448]={ +0x30,0x82,0x05,0xA4,0x30,0x82,0x03,0x8C,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30, +0x63,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x1C, +0x30,0x1A,0x06,0x03,0x55,0x04,0x0A,0x13,0x13,0x41,0x6D,0x65,0x72,0x69,0x63,0x61, +0x20,0x4F,0x6E,0x6C,0x69,0x6E,0x65,0x20,0x49,0x6E,0x63,0x2E,0x31,0x36,0x30,0x34, +0x06,0x03,0x55,0x04,0x03,0x13,0x2D,0x41,0x6D,0x65,0x72,0x69,0x63,0x61,0x20,0x4F, +0x6E,0x6C,0x69,0x6E,0x65,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69, +0x74,0x79,0x20,0x32,0x30,0x1E,0x17,0x0D,0x30,0x32,0x30,0x35,0x32,0x38,0x30,0x36, +0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x37,0x30,0x39,0x32,0x39,0x31,0x34,0x30, +0x38,0x30,0x30,0x5A,0x30,0x63,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13, +0x02,0x55,0x53,0x31,0x1C,0x30,0x1A,0x06,0x03,0x55,0x04,0x0A,0x13,0x13,0x41,0x6D, +0x65,0x72,0x69,0x63,0x61,0x20,0x4F,0x6E,0x6C,0x69,0x6E,0x65,0x20,0x49,0x6E,0x63, +0x2E,0x31,0x36,0x30,0x34,0x06,0x03,0x55,0x04,0x03,0x13,0x2D,0x41,0x6D,0x65,0x72, +0x69,0x63,0x61,0x20,0x4F,0x6E,0x6C,0x69,0x6E,0x65,0x20,0x52,0x6F,0x6F,0x74,0x20, +0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75, +0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x20,0x32,0x30,0x82,0x02,0x22,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x02,0x0F, +0x00,0x30,0x82,0x02,0x0A,0x02,0x82,0x02,0x01,0x00,0xCC,0x41,0x45,0x1D,0xE9,0x3D, +0x4D,0x10,0xF6,0x8C,0xB1,0x41,0xC9,0xE0,0x5E,0xCB,0x0D,0xB7,0xBF,0x47,0x73,0xD3, +0xF0,0x55,0x4D,0xDD,0xC6,0x0C,0xFA,0xB1,0x66,0x05,0x6A,0xCD,0x78,0xB4,0xDC,0x02, +0xDB,0x4E,0x81,0xF3,0xD7,0xA7,0x7C,0x71,0xBC,0x75,0x63,0xA0,0x5D,0xE3,0x07,0x0C, +0x48,0xEC,0x25,0xC4,0x03,0x20,0xF4,0xFF,0x0E,0x3B,0x12,0xFF,0x9B,0x8D,0xE1,0xC6, +0xD5,0x1B,0xB4,0x6D,0x22,0xE3,0xB1,0xDB,0x7F,0x21,0x64,0xAF,0x86,0xBC,0x57,0x22, +0x2A,0xD6,0x47,0x81,0x57,0x44,0x82,0x56,0x53,0xBD,0x86,0x14,0x01,0x0B,0xFC,0x7F, +0x74,0xA4,0x5A,0xAE,0xF1,0xBA,0x11,0xB5,0x9B,0x58,0x5A,0x80,0xB4,0x37,0x78,0x09, +0x33,0x7C,0x32,0x47,0x03,0x5C,0xC4,0xA5,0x83,0x48,0xF4,0x57,0x56,0x6E,0x81,0x36, +0x27,0x18,0x4F,0xEC,0x9B,0x28,0xC2,0xD4,0xB4,0xD7,0x7C,0x0C,0x3E,0x0C,0x2B,0xDF, +0xCA,0x04,0xD7,0xC6,0x8E,0xEA,0x58,0x4E,0xA8,0xA4,0xA5,0x18,0x1C,0x6C,0x45,0x98, +0xA3,0x41,0xD1,0x2D,0xD2,0xC7,0x6D,0x8D,0x19,0xF1,0xAD,0x79,0xB7,0x81,0x3F,0xBD, +0x06,0x82,0x27,0x2D,0x10,0x58,0x05,0xB5,0x78,0x05,0xB9,0x2F,0xDB,0x0C,0x6B,0x90, +0x90,0x7E,0x14,0x59,0x38,0xBB,0x94,0x24,0x13,0xE5,0xD1,0x9D,0x14,0xDF,0xD3,0x82, +0x4D,0x46,0xF0,0x80,0x39,0x52,0x32,0x0F,0xE3,0x84,0xB2,0x7A,0x43,0xF2,0x5E,0xDE, +0x5F,0x3F,0x1D,0xDD,0xE3,0xB2,0x1B,0xA0,0xA1,0x2A,0x23,0x03,0x6E,0x2E,0x01,0x15, +0x87,0x5C,0xA6,0x75,0x75,0xC7,0x97,0x61,0xBE,0xDE,0x86,0xDC,0xD4,0x48,0xDB,0xBD, +0x2A,0xBF,0x4A,0x55,0xDA,0xE8,0x7D,0x50,0xFB,0xB4,0x80,0x17,0xB8,0x94,0xBF,0x01, +0x3D,0xEA,0xDA,0xBA,0x7C,0xE0,0x58,0x67,0x17,0xB9,0x58,0xE0,0x88,0x86,0x46,0x67, +0x6C,0x9D,0x10,0x47,0x58,0x32,0xD0,0x35,0x7C,0x79,0x2A,0x90,0xA2,0x5A,0x10,0x11, +0x23,0x35,0xAD,0x2F,0xCC,0xE4,0x4A,0x5B,0xA7,0xC8,0x27,0xF2,0x83,0xDE,0x5E,0xBB, +0x5E,0x77,0xE7,0xE8,0xA5,0x6E,0x63,0xC2,0x0D,0x5D,0x61,0xD0,0x8C,0xD2,0x6C,0x5A, +0x21,0x0E,0xCA,0x28,0xA3,0xCE,0x2A,0xE9,0x95,0xC7,0x48,0xCF,0x96,0x6F,0x1D,0x92, +0x25,0xC8,0xC6,0xC6,0xC1,0xC1,0x0C,0x05,0xAC,0x26,0xC4,0xD2,0x75,0xD2,0xE1,0x2A, +0x67,0xC0,0x3D,0x5B,0xA5,0x9A,0xEB,0xCF,0x7B,0x1A,0xA8,0x9D,0x14,0x45,0xE5,0x0F, +0xA0,0x9A,0x65,0xDE,0x2F,0x28,0xBD,0xCE,0x6F,0x94,0x66,0x83,0x48,0x29,0xD8,0xEA, +0x65,0x8C,0xAF,0x93,0xD9,0x64,0x9F,0x55,0x57,0x26,0xBF,0x6F,0xCB,0x37,0x31,0x99, +0xA3,0x60,0xBB,0x1C,0xAD,0x89,0x34,0x32,0x62,0xB8,0x43,0x21,0x06,0x72,0x0C,0xA1, +0x5C,0x6D,0x46,0xC5,0xFA,0x29,0xCF,0x30,0xDE,0x89,0xDC,0x71,0x5B,0xDD,0xB6,0x37, +0x3E,0xDF,0x50,0xF5,0xB8,0x07,0x25,0x26,0xE5,0xBC,0xB5,0xFE,0x3C,0x02,0xB3,0xB7, +0xF8,0xBE,0x43,0xC1,0x87,0x11,0x94,0x9E,0x23,0x6C,0x17,0x8A,0xB8,0x8A,0x27,0x0C, +0x54,0x47,0xF0,0xA9,0xB3,0xC0,0x80,0x8C,0xA0,0x27,0xEB,0x1D,0x19,0xE3,0x07,0x8E, +0x77,0x70,0xCA,0x2B,0xF4,0x7D,0x76,0xE0,0x78,0x67,0x02,0x03,0x01,0x00,0x01,0xA3, +0x63,0x30,0x61,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30, +0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x4D, +0x45,0xC1,0x68,0x38,0xBB,0x73,0xA9,0x69,0xA1,0x20,0xE7,0xED,0xF5,0x22,0xA1,0x23, +0x14,0xD7,0x9E,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14, +0x4D,0x45,0xC1,0x68,0x38,0xBB,0x73,0xA9,0x69,0xA1,0x20,0xE7,0xED,0xF5,0x22,0xA1, +0x23,0x14,0xD7,0x9E,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04, +0x03,0x02,0x01,0x86,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01, +0x05,0x05,0x00,0x03,0x82,0x02,0x01,0x00,0x67,0x6B,0x06,0xB9,0x5F,0x45,0x3B,0x2A, +0x4B,0x33,0xB3,0xE6,0x1B,0x6B,0x59,0x4E,0x22,0xCC,0xB9,0xB7,0xA4,0x25,0xC9,0xA7, +0xC4,0xF0,0x54,0x96,0x0B,0x64,0xF3,0xB1,0x58,0x4F,0x5E,0x51,0xFC,0xB2,0x97,0x7B, +0x27,0x65,0xC2,0xE5,0xCA,0xE7,0x0D,0x0C,0x25,0x7B,0x62,0xE3,0xFA,0x9F,0xB4,0x87, +0xB7,0x45,0x46,0xAF,0x83,0xA5,0x97,0x48,0x8C,0xA5,0xBD,0xF1,0x16,0x2B,0x9B,0x76, +0x2C,0x7A,0x35,0x60,0x6C,0x11,0x80,0x97,0xCC,0xA9,0x92,0x52,0xE6,0x2B,0xE6,0x69, +0xED,0xA9,0xF8,0x36,0x2D,0x2C,0x77,0xBF,0x61,0x48,0xD1,0x63,0x0B,0xB9,0x5B,0x52, +0xED,0x18,0xB0,0x43,0x42,0x22,0xA6,0xB1,0x77,0xAE,0xDE,0x69,0xC5,0xCD,0xC7,0x1C, +0xA1,0xB1,0xA5,0x1C,0x10,0xFB,0x18,0xBE,0x1A,0x70,0xDD,0xC1,0x92,0x4B,0xBE,0x29, +0x5A,0x9D,0x3F,0x35,0xBE,0xE5,0x7D,0x51,0xF8,0x55,0xE0,0x25,0x75,0x23,0x87,0x1E, +0x5C,0xDC,0xBA,0x9D,0xB0,0xAC,0xB3,0x69,0xDB,0x17,0x83,0xC9,0xF7,0xDE,0x0C,0xBC, +0x08,0xDC,0x91,0x9E,0xA8,0xD0,0xD7,0x15,0x37,0x73,0xA5,0x35,0xB8,0xFC,0x7E,0xC5, +0x44,0x40,0x06,0xC3,0xEB,0xF8,0x22,0x80,0x5C,0x47,0xCE,0x02,0xE3,0x11,0x9F,0x44, +0xFF,0xFD,0x9A,0x32,0xCC,0x7D,0x64,0x51,0x0E,0xEB,0x57,0x26,0x76,0x3A,0xE3,0x1E, +0x22,0x3C,0xC2,0xA6,0x36,0xDD,0x19,0xEF,0xA7,0xFC,0x12,0xF3,0x26,0xC0,0x59,0x31, +0x85,0x4C,0x9C,0xD8,0xCF,0xDF,0xA4,0xCC,0xCC,0x29,0x93,0xFF,0x94,0x6D,0x76,0x5C, +0x13,0x08,0x97,0xF2,0xED,0xA5,0x0B,0x4D,0xDD,0xE8,0xC9,0x68,0x0E,0x66,0xD3,0x00, +0x0E,0x33,0x12,0x5B,0xBC,0x95,0xE5,0x32,0x90,0xA8,0xB3,0xC6,0x6C,0x83,0xAD,0x77, +0xEE,0x8B,0x7E,0x7E,0xB1,0xA9,0xAB,0xD3,0xE1,0xF1,0xB6,0xC0,0xB1,0xEA,0x88,0xC0, +0xE7,0xD3,0x90,0xE9,0x28,0x92,0x94,0x7B,0x68,0x7B,0x97,0x2A,0x0A,0x67,0x2D,0x85, +0x02,0x38,0x10,0xE4,0x03,0x61,0xD4,0xDA,0x25,0x36,0xC7,0x08,0x58,0x2D,0xA1,0xA7, +0x51,0xAF,0x30,0x0A,0x49,0xF5,0xA6,0x69,0x87,0x07,0x2D,0x44,0x46,0x76,0x8E,0x2A, +0xE5,0x9A,0x3B,0xD7,0x18,0xA2,0xFC,0x9C,0x38,0x10,0xCC,0xC6,0x3B,0xD2,0xB5,0x17, +0x3A,0x6F,0xFD,0xAE,0x25,0xBD,0xF5,0x72,0x59,0x64,0xB1,0x74,0x2A,0x38,0x5F,0x18, +0x4C,0xDF,0xCF,0x71,0x04,0x5A,0x36,0xD4,0xBF,0x2F,0x99,0x9C,0xE8,0xD9,0xBA,0xB1, +0x95,0xE6,0x02,0x4B,0x21,0xA1,0x5B,0xD5,0xC1,0x4F,0x8F,0xAE,0x69,0x6D,0x53,0xDB, +0x01,0x93,0xB5,0x5C,0x1E,0x18,0xDD,0x64,0x5A,0xCA,0x18,0x28,0x3E,0x63,0x04,0x11, +0xFD,0x1C,0x8D,0x00,0x0F,0xB8,0x37,0xDF,0x67,0x8A,0x9D,0x66,0xA9,0x02,0x6A,0x91, +0xFF,0x13,0xCA,0x2F,0x5D,0x83,0xBC,0x87,0x93,0x6C,0xDC,0x24,0x51,0x16,0x04,0x25, +0x66,0xFA,0xB3,0xD9,0xC2,0xBA,0x29,0xBE,0x9A,0x48,0x38,0x82,0x99,0xF4,0xBF,0x3B, +0x4A,0x31,0x19,0xF9,0xBF,0x8E,0x21,0x33,0x14,0xCA,0x4F,0x54,0x5F,0xFB,0xCE,0xFB, +0x8F,0x71,0x7F,0xFD,0x5E,0x19,0xA0,0x0F,0x4B,0x91,0xB8,0xC4,0x54,0xBC,0x06,0xB0, +0x45,0x8F,0x26,0x91,0xA2,0x8E,0xFE,0xA9, +}; + + +/* subject:/C=IE/O=Baltimore/OU=CyberTrust/CN=Baltimore CyberTrust Root */ +/* issuer :/C=IE/O=Baltimore/OU=CyberTrust/CN=Baltimore CyberTrust Root */ + + +const unsigned char Baltimore_CyberTrust_Root_certificate[891]={ +0x30,0x82,0x03,0x77,0x30,0x82,0x02,0x5F,0xA0,0x03,0x02,0x01,0x02,0x02,0x04,0x02, +0x00,0x00,0xB9,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05, +0x05,0x00,0x30,0x5A,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x49, +0x45,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x0A,0x13,0x09,0x42,0x61,0x6C,0x74, +0x69,0x6D,0x6F,0x72,0x65,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0B,0x13,0x0A, +0x43,0x79,0x62,0x65,0x72,0x54,0x72,0x75,0x73,0x74,0x31,0x22,0x30,0x20,0x06,0x03, +0x55,0x04,0x03,0x13,0x19,0x42,0x61,0x6C,0x74,0x69,0x6D,0x6F,0x72,0x65,0x20,0x43, +0x79,0x62,0x65,0x72,0x54,0x72,0x75,0x73,0x74,0x20,0x52,0x6F,0x6F,0x74,0x30,0x1E, +0x17,0x0D,0x30,0x30,0x30,0x35,0x31,0x32,0x31,0x38,0x34,0x36,0x30,0x30,0x5A,0x17, +0x0D,0x32,0x35,0x30,0x35,0x31,0x32,0x32,0x33,0x35,0x39,0x30,0x30,0x5A,0x30,0x5A, +0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x49,0x45,0x31,0x12,0x30, +0x10,0x06,0x03,0x55,0x04,0x0A,0x13,0x09,0x42,0x61,0x6C,0x74,0x69,0x6D,0x6F,0x72, +0x65,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0B,0x13,0x0A,0x43,0x79,0x62,0x65, +0x72,0x54,0x72,0x75,0x73,0x74,0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,0x03,0x13, +0x19,0x42,0x61,0x6C,0x74,0x69,0x6D,0x6F,0x72,0x65,0x20,0x43,0x79,0x62,0x65,0x72, +0x54,0x72,0x75,0x73,0x74,0x20,0x52,0x6F,0x6F,0x74,0x30,0x82,0x01,0x22,0x30,0x0D, +0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01, +0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xA3,0x04,0xBB,0x22,0xAB, +0x98,0x3D,0x57,0xE8,0x26,0x72,0x9A,0xB5,0x79,0xD4,0x29,0xE2,0xE1,0xE8,0x95,0x80, +0xB1,0xB0,0xE3,0x5B,0x8E,0x2B,0x29,0x9A,0x64,0xDF,0xA1,0x5D,0xED,0xB0,0x09,0x05, +0x6D,0xDB,0x28,0x2E,0xCE,0x62,0xA2,0x62,0xFE,0xB4,0x88,0xDA,0x12,0xEB,0x38,0xEB, +0x21,0x9D,0xC0,0x41,0x2B,0x01,0x52,0x7B,0x88,0x77,0xD3,0x1C,0x8F,0xC7,0xBA,0xB9, +0x88,0xB5,0x6A,0x09,0xE7,0x73,0xE8,0x11,0x40,0xA7,0xD1,0xCC,0xCA,0x62,0x8D,0x2D, +0xE5,0x8F,0x0B,0xA6,0x50,0xD2,0xA8,0x50,0xC3,0x28,0xEA,0xF5,0xAB,0x25,0x87,0x8A, +0x9A,0x96,0x1C,0xA9,0x67,0xB8,0x3F,0x0C,0xD5,0xF7,0xF9,0x52,0x13,0x2F,0xC2,0x1B, +0xD5,0x70,0x70,0xF0,0x8F,0xC0,0x12,0xCA,0x06,0xCB,0x9A,0xE1,0xD9,0xCA,0x33,0x7A, +0x77,0xD6,0xF8,0xEC,0xB9,0xF1,0x68,0x44,0x42,0x48,0x13,0xD2,0xC0,0xC2,0xA4,0xAE, +0x5E,0x60,0xFE,0xB6,0xA6,0x05,0xFC,0xB4,0xDD,0x07,0x59,0x02,0xD4,0x59,0x18,0x98, +0x63,0xF5,0xA5,0x63,0xE0,0x90,0x0C,0x7D,0x5D,0xB2,0x06,0x7A,0xF3,0x85,0xEA,0xEB, +0xD4,0x03,0xAE,0x5E,0x84,0x3E,0x5F,0xFF,0x15,0xED,0x69,0xBC,0xF9,0x39,0x36,0x72, +0x75,0xCF,0x77,0x52,0x4D,0xF3,0xC9,0x90,0x2C,0xB9,0x3D,0xE5,0xC9,0x23,0x53,0x3F, +0x1F,0x24,0x98,0x21,0x5C,0x07,0x99,0x29,0xBD,0xC6,0x3A,0xEC,0xE7,0x6E,0x86,0x3A, +0x6B,0x97,0x74,0x63,0x33,0xBD,0x68,0x18,0x31,0xF0,0x78,0x8D,0x76,0xBF,0xFC,0x9E, +0x8E,0x5D,0x2A,0x86,0xA7,0x4D,0x90,0xDC,0x27,0x1A,0x39,0x02,0x03,0x01,0x00,0x01, +0xA3,0x45,0x30,0x43,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xE5, +0x9D,0x59,0x30,0x82,0x47,0x58,0xCC,0xAC,0xFA,0x08,0x54,0x36,0x86,0x7B,0x3A,0xB5, +0x04,0x4D,0xF0,0x30,0x12,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x08,0x30, +0x06,0x01,0x01,0xFF,0x02,0x01,0x03,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01, +0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x85,0x0C,0x5D,0x8E,0xE4, +0x6F,0x51,0x68,0x42,0x05,0xA0,0xDD,0xBB,0x4F,0x27,0x25,0x84,0x03,0xBD,0xF7,0x64, +0xFD,0x2D,0xD7,0x30,0xE3,0xA4,0x10,0x17,0xEB,0xDA,0x29,0x29,0xB6,0x79,0x3F,0x76, +0xF6,0x19,0x13,0x23,0xB8,0x10,0x0A,0xF9,0x58,0xA4,0xD4,0x61,0x70,0xBD,0x04,0x61, +0x6A,0x12,0x8A,0x17,0xD5,0x0A,0xBD,0xC5,0xBC,0x30,0x7C,0xD6,0xE9,0x0C,0x25,0x8D, +0x86,0x40,0x4F,0xEC,0xCC,0xA3,0x7E,0x38,0xC6,0x37,0x11,0x4F,0xED,0xDD,0x68,0x31, +0x8E,0x4C,0xD2,0xB3,0x01,0x74,0xEE,0xBE,0x75,0x5E,0x07,0x48,0x1A,0x7F,0x70,0xFF, +0x16,0x5C,0x84,0xC0,0x79,0x85,0xB8,0x05,0xFD,0x7F,0xBE,0x65,0x11,0xA3,0x0F,0xC0, +0x02,0xB4,0xF8,0x52,0x37,0x39,0x04,0xD5,0xA9,0x31,0x7A,0x18,0xBF,0xA0,0x2A,0xF4, +0x12,0x99,0xF7,0xA3,0x45,0x82,0xE3,0x3C,0x5E,0xF5,0x9D,0x9E,0xB5,0xC8,0x9E,0x7C, +0x2E,0xC8,0xA4,0x9E,0x4E,0x08,0x14,0x4B,0x6D,0xFD,0x70,0x6D,0x6B,0x1A,0x63,0xBD, +0x64,0xE6,0x1F,0xB7,0xCE,0xF0,0xF2,0x9F,0x2E,0xBB,0x1B,0xB7,0xF2,0x50,0x88,0x73, +0x92,0xC2,0xE2,0xE3,0x16,0x8D,0x9A,0x32,0x02,0xAB,0x8E,0x18,0xDD,0xE9,0x10,0x11, +0xEE,0x7E,0x35,0xAB,0x90,0xAF,0x3E,0x30,0x94,0x7A,0xD0,0x33,0x3D,0xA7,0x65,0x0F, +0xF5,0xFC,0x8E,0x9E,0x62,0xCF,0x47,0x44,0x2C,0x01,0x5D,0xBB,0x1D,0xB5,0x32,0xD2, +0x47,0xD2,0x38,0x2E,0xD0,0xFE,0x81,0xDC,0x32,0x6A,0x1E,0xB5,0xEE,0x3C,0xD5,0xFC, +0xE7,0x81,0x1D,0x19,0xC3,0x24,0x42,0xEA,0x63,0x39,0xA9, +}; + + +/* subject:/C=GB/ST=Greater Manchester/L=Salford/O=Comodo CA Limited/CN=AAA Certificate Services */ +/* issuer :/C=GB/ST=Greater Manchester/L=Salford/O=Comodo CA Limited/CN=AAA Certificate Services */ + + +const unsigned char Comodo_AAA_Services_root_certificate[1078]={ +0x30,0x82,0x04,0x32,0x30,0x82,0x03,0x1A,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30, +0x7B,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x47,0x42,0x31,0x1B, +0x30,0x19,0x06,0x03,0x55,0x04,0x08,0x0C,0x12,0x47,0x72,0x65,0x61,0x74,0x65,0x72, +0x20,0x4D,0x61,0x6E,0x63,0x68,0x65,0x73,0x74,0x65,0x72,0x31,0x10,0x30,0x0E,0x06, +0x03,0x55,0x04,0x07,0x0C,0x07,0x53,0x61,0x6C,0x66,0x6F,0x72,0x64,0x31,0x1A,0x30, +0x18,0x06,0x03,0x55,0x04,0x0A,0x0C,0x11,0x43,0x6F,0x6D,0x6F,0x64,0x6F,0x20,0x43, +0x41,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x21,0x30,0x1F,0x06,0x03,0x55, +0x04,0x03,0x0C,0x18,0x41,0x41,0x41,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63, +0x61,0x74,0x65,0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x30,0x1E,0x17,0x0D, +0x30,0x34,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32, +0x38,0x31,0x32,0x33,0x31,0x32,0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x7B,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x47,0x42,0x31,0x1B,0x30,0x19,0x06, +0x03,0x55,0x04,0x08,0x0C,0x12,0x47,0x72,0x65,0x61,0x74,0x65,0x72,0x20,0x4D,0x61, +0x6E,0x63,0x68,0x65,0x73,0x74,0x65,0x72,0x31,0x10,0x30,0x0E,0x06,0x03,0x55,0x04, +0x07,0x0C,0x07,0x53,0x61,0x6C,0x66,0x6F,0x72,0x64,0x31,0x1A,0x30,0x18,0x06,0x03, +0x55,0x04,0x0A,0x0C,0x11,0x43,0x6F,0x6D,0x6F,0x64,0x6F,0x20,0x43,0x41,0x20,0x4C, +0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x03,0x0C, +0x18,0x41,0x41,0x41,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x65, +0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x30,0x82,0x01,0x22,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F, +0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xBE,0x40,0x9D,0xF4,0x6E,0xE1, +0xEA,0x76,0x87,0x1C,0x4D,0x45,0x44,0x8E,0xBE,0x46,0xC8,0x83,0x06,0x9D,0xC1,0x2A, +0xFE,0x18,0x1F,0x8E,0xE4,0x02,0xFA,0xF3,0xAB,0x5D,0x50,0x8A,0x16,0x31,0x0B,0x9A, +0x06,0xD0,0xC5,0x70,0x22,0xCD,0x49,0x2D,0x54,0x63,0xCC,0xB6,0x6E,0x68,0x46,0x0B, +0x53,0xEA,0xCB,0x4C,0x24,0xC0,0xBC,0x72,0x4E,0xEA,0xF1,0x15,0xAE,0xF4,0x54,0x9A, +0x12,0x0A,0xC3,0x7A,0xB2,0x33,0x60,0xE2,0xDA,0x89,0x55,0xF3,0x22,0x58,0xF3,0xDE, +0xDC,0xCF,0xEF,0x83,0x86,0xA2,0x8C,0x94,0x4F,0x9F,0x68,0xF2,0x98,0x90,0x46,0x84, +0x27,0xC7,0x76,0xBF,0xE3,0xCC,0x35,0x2C,0x8B,0x5E,0x07,0x64,0x65,0x82,0xC0,0x48, +0xB0,0xA8,0x91,0xF9,0x61,0x9F,0x76,0x20,0x50,0xA8,0x91,0xC7,0x66,0xB5,0xEB,0x78, +0x62,0x03,0x56,0xF0,0x8A,0x1A,0x13,0xEA,0x31,0xA3,0x1E,0xA0,0x99,0xFD,0x38,0xF6, +0xF6,0x27,0x32,0x58,0x6F,0x07,0xF5,0x6B,0xB8,0xFB,0x14,0x2B,0xAF,0xB7,0xAA,0xCC, +0xD6,0x63,0x5F,0x73,0x8C,0xDA,0x05,0x99,0xA8,0x38,0xA8,0xCB,0x17,0x78,0x36,0x51, +0xAC,0xE9,0x9E,0xF4,0x78,0x3A,0x8D,0xCF,0x0F,0xD9,0x42,0xE2,0x98,0x0C,0xAB,0x2F, +0x9F,0x0E,0x01,0xDE,0xEF,0x9F,0x99,0x49,0xF1,0x2D,0xDF,0xAC,0x74,0x4D,0x1B,0x98, +0xB5,0x47,0xC5,0xE5,0x29,0xD1,0xF9,0x90,0x18,0xC7,0x62,0x9C,0xBE,0x83,0xC7,0x26, +0x7B,0x3E,0x8A,0x25,0xC7,0xC0,0xDD,0x9D,0xE6,0x35,0x68,0x10,0x20,0x9D,0x8F,0xD8, +0xDE,0xD2,0xC3,0x84,0x9C,0x0D,0x5E,0xE8,0x2F,0xC9,0x02,0x03,0x01,0x00,0x01,0xA3, +0x81,0xC0,0x30,0x81,0xBD,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14, +0xA0,0x11,0x0A,0x23,0x3E,0x96,0xF1,0x07,0xEC,0xE2,0xAF,0x29,0xEF,0x82,0xA5,0x7F, +0xD0,0x30,0xA4,0xB4,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04, +0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05, +0x30,0x03,0x01,0x01,0xFF,0x30,0x7B,0x06,0x03,0x55,0x1D,0x1F,0x04,0x74,0x30,0x72, +0x30,0x38,0xA0,0x36,0xA0,0x34,0x86,0x32,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63, +0x72,0x6C,0x2E,0x63,0x6F,0x6D,0x6F,0x64,0x6F,0x63,0x61,0x2E,0x63,0x6F,0x6D,0x2F, +0x41,0x41,0x41,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x65,0x53,0x65, +0x72,0x76,0x69,0x63,0x65,0x73,0x2E,0x63,0x72,0x6C,0x30,0x36,0xA0,0x34,0xA0,0x32, +0x86,0x30,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x2E,0x63,0x6F,0x6D, +0x6F,0x64,0x6F,0x2E,0x6E,0x65,0x74,0x2F,0x41,0x41,0x41,0x43,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x65,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x2E,0x63, +0x72,0x6C,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05, +0x00,0x03,0x82,0x01,0x01,0x00,0x08,0x56,0xFC,0x02,0xF0,0x9B,0xE8,0xFF,0xA4,0xFA, +0xD6,0x7B,0xC6,0x44,0x80,0xCE,0x4F,0xC4,0xC5,0xF6,0x00,0x58,0xCC,0xA6,0xB6,0xBC, +0x14,0x49,0x68,0x04,0x76,0xE8,0xE6,0xEE,0x5D,0xEC,0x02,0x0F,0x60,0xD6,0x8D,0x50, +0x18,0x4F,0x26,0x4E,0x01,0xE3,0xE6,0xB0,0xA5,0xEE,0xBF,0xBC,0x74,0x54,0x41,0xBF, +0xFD,0xFC,0x12,0xB8,0xC7,0x4F,0x5A,0xF4,0x89,0x60,0x05,0x7F,0x60,0xB7,0x05,0x4A, +0xF3,0xF6,0xF1,0xC2,0xBF,0xC4,0xB9,0x74,0x86,0xB6,0x2D,0x7D,0x6B,0xCC,0xD2,0xF3, +0x46,0xDD,0x2F,0xC6,0xE0,0x6A,0xC3,0xC3,0x34,0x03,0x2C,0x7D,0x96,0xDD,0x5A,0xC2, +0x0E,0xA7,0x0A,0x99,0xC1,0x05,0x8B,0xAB,0x0C,0x2F,0xF3,0x5C,0x3A,0xCF,0x6C,0x37, +0x55,0x09,0x87,0xDE,0x53,0x40,0x6C,0x58,0xEF,0xFC,0xB6,0xAB,0x65,0x6E,0x04,0xF6, +0x1B,0xDC,0x3C,0xE0,0x5A,0x15,0xC6,0x9E,0xD9,0xF1,0x59,0x48,0x30,0x21,0x65,0x03, +0x6C,0xEC,0xE9,0x21,0x73,0xEC,0x9B,0x03,0xA1,0xE0,0x37,0xAD,0xA0,0x15,0x18,0x8F, +0xFA,0xBA,0x02,0xCE,0xA7,0x2C,0xA9,0x10,0x13,0x2C,0xD4,0xE5,0x08,0x26,0xAB,0x22, +0x97,0x60,0xF8,0x90,0x5E,0x74,0xD4,0xA2,0x9A,0x53,0xBD,0xF2,0xA9,0x68,0xE0,0xA2, +0x6E,0xC2,0xD7,0x6C,0xB1,0xA3,0x0F,0x9E,0xBF,0xEB,0x68,0xE7,0x56,0xF2,0xAE,0xF2, +0xE3,0x2B,0x38,0x3A,0x09,0x81,0xB5,0x6B,0x85,0xD7,0xBE,0x2D,0xED,0x3F,0x1A,0xB7, +0xB2,0x63,0xE2,0xF5,0x62,0x2C,0x82,0xD4,0x6A,0x00,0x41,0x50,0xF1,0x39,0x83,0x9F, +0x95,0xE9,0x36,0x96,0x98,0x6E, +}; + + +/* subject:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO Certification Authority */ +/* issuer :/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO Certification Authority */ + + +const unsigned char COMODO_Certification_Authority_certificate[1057]={ +0x30,0x82,0x04,0x1D,0x30,0x82,0x03,0x05,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x4E, +0x81,0x2D,0x8A,0x82,0x65,0xE0,0x0B,0x02,0xEE,0x3E,0x35,0x02,0x46,0xE5,0x3D,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x81, +0x81,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x47,0x42,0x31,0x1B, +0x30,0x19,0x06,0x03,0x55,0x04,0x08,0x13,0x12,0x47,0x72,0x65,0x61,0x74,0x65,0x72, +0x20,0x4D,0x61,0x6E,0x63,0x68,0x65,0x73,0x74,0x65,0x72,0x31,0x10,0x30,0x0E,0x06, +0x03,0x55,0x04,0x07,0x13,0x07,0x53,0x61,0x6C,0x66,0x6F,0x72,0x64,0x31,0x1A,0x30, +0x18,0x06,0x03,0x55,0x04,0x0A,0x13,0x11,0x43,0x4F,0x4D,0x4F,0x44,0x4F,0x20,0x43, +0x41,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x27,0x30,0x25,0x06,0x03,0x55, +0x04,0x03,0x13,0x1E,0x43,0x4F,0x4D,0x4F,0x44,0x4F,0x20,0x43,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69, +0x74,0x79,0x30,0x1E,0x17,0x0D,0x30,0x36,0x31,0x32,0x30,0x31,0x30,0x30,0x30,0x30, +0x30,0x30,0x5A,0x17,0x0D,0x32,0x39,0x31,0x32,0x33,0x31,0x32,0x33,0x35,0x39,0x35, +0x39,0x5A,0x30,0x81,0x81,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02, +0x47,0x42,0x31,0x1B,0x30,0x19,0x06,0x03,0x55,0x04,0x08,0x13,0x12,0x47,0x72,0x65, +0x61,0x74,0x65,0x72,0x20,0x4D,0x61,0x6E,0x63,0x68,0x65,0x73,0x74,0x65,0x72,0x31, +0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x07,0x13,0x07,0x53,0x61,0x6C,0x66,0x6F,0x72, +0x64,0x31,0x1A,0x30,0x18,0x06,0x03,0x55,0x04,0x0A,0x13,0x11,0x43,0x4F,0x4D,0x4F, +0x44,0x4F,0x20,0x43,0x41,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x27,0x30, +0x25,0x06,0x03,0x55,0x04,0x03,0x13,0x1E,0x43,0x4F,0x4D,0x4F,0x44,0x4F,0x20,0x43, +0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74, +0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82, +0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xD0,0x40,0x8B,0x8B,0x72,0xE3,0x91,0x1B,0xF7, +0x51,0xC1,0x1B,0x54,0x04,0x98,0xD3,0xA9,0xBF,0xC1,0xE6,0x8A,0x5D,0x3B,0x87,0xFB, +0xBB,0x88,0xCE,0x0D,0xE3,0x2F,0x3F,0x06,0x96,0xF0,0xA2,0x29,0x50,0x99,0xAE,0xDB, +0x3B,0xA1,0x57,0xB0,0x74,0x51,0x71,0xCD,0xED,0x42,0x91,0x4D,0x41,0xFE,0xA9,0xC8, +0xD8,0x6A,0x86,0x77,0x44,0xBB,0x59,0x66,0x97,0x50,0x5E,0xB4,0xD4,0x2C,0x70,0x44, +0xCF,0xDA,0x37,0x95,0x42,0x69,0x3C,0x30,0xC4,0x71,0xB3,0x52,0xF0,0x21,0x4D,0xA1, +0xD8,0xBA,0x39,0x7C,0x1C,0x9E,0xA3,0x24,0x9D,0xF2,0x83,0x16,0x98,0xAA,0x16,0x7C, +0x43,0x9B,0x15,0x5B,0xB7,0xAE,0x34,0x91,0xFE,0xD4,0x62,0x26,0x18,0x46,0x9A,0x3F, +0xEB,0xC1,0xF9,0xF1,0x90,0x57,0xEB,0xAC,0x7A,0x0D,0x8B,0xDB,0x72,0x30,0x6A,0x66, +0xD5,0xE0,0x46,0xA3,0x70,0xDC,0x68,0xD9,0xFF,0x04,0x48,0x89,0x77,0xDE,0xB5,0xE9, +0xFB,0x67,0x6D,0x41,0xE9,0xBC,0x39,0xBD,0x32,0xD9,0x62,0x02,0xF1,0xB1,0xA8,0x3D, +0x6E,0x37,0x9C,0xE2,0x2F,0xE2,0xD3,0xA2,0x26,0x8B,0xC6,0xB8,0x55,0x43,0x88,0xE1, +0x23,0x3E,0xA5,0xD2,0x24,0x39,0x6A,0x47,0xAB,0x00,0xD4,0xA1,0xB3,0xA9,0x25,0xFE, +0x0D,0x3F,0xA7,0x1D,0xBA,0xD3,0x51,0xC1,0x0B,0xA4,0xDA,0xAC,0x38,0xEF,0x55,0x50, +0x24,0x05,0x65,0x46,0x93,0x34,0x4F,0x2D,0x8D,0xAD,0xC6,0xD4,0x21,0x19,0xD2,0x8E, +0xCA,0x05,0x61,0x71,0x07,0x73,0x47,0xE5,0x8A,0x19,0x12,0xBD,0x04,0x4D,0xCE,0x4E, +0x9C,0xA5,0x48,0xAC,0xBB,0x26,0xF7,0x02,0x03,0x01,0x00,0x01,0xA3,0x81,0x8E,0x30, +0x81,0x8B,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x0B,0x58,0xE5, +0x8B,0xC6,0x4C,0x15,0x37,0xA4,0x40,0xA9,0x30,0xA9,0x21,0xBE,0x47,0x36,0x5A,0x56, +0xFF,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01, +0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01, +0x01,0xFF,0x30,0x49,0x06,0x03,0x55,0x1D,0x1F,0x04,0x42,0x30,0x40,0x30,0x3E,0xA0, +0x3C,0xA0,0x3A,0x86,0x38,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x2E, +0x63,0x6F,0x6D,0x6F,0x64,0x6F,0x63,0x61,0x2E,0x63,0x6F,0x6D,0x2F,0x43,0x4F,0x4D, +0x4F,0x44,0x4F,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E, +0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x2E,0x63,0x72,0x6C,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01, +0x00,0x3E,0x98,0x9E,0x9B,0xF6,0x1B,0xE9,0xD7,0x39,0xB7,0x78,0xAE,0x1D,0x72,0x18, +0x49,0xD3,0x87,0xE4,0x43,0x82,0xEB,0x3F,0xC9,0xAA,0xF5,0xA8,0xB5,0xEF,0x55,0x7C, +0x21,0x52,0x65,0xF9,0xD5,0x0D,0xE1,0x6C,0xF4,0x3E,0x8C,0x93,0x73,0x91,0x2E,0x02, +0xC4,0x4E,0x07,0x71,0x6F,0xC0,0x8F,0x38,0x61,0x08,0xA8,0x1E,0x81,0x0A,0xC0,0x2F, +0x20,0x2F,0x41,0x8B,0x91,0xDC,0x48,0x45,0xBC,0xF1,0xC6,0xDE,0xBA,0x76,0x6B,0x33, +0xC8,0x00,0x2D,0x31,0x46,0x4C,0xED,0xE7,0x9D,0xCF,0x88,0x94,0xFF,0x33,0xC0,0x56, +0xE8,0x24,0x86,0x26,0xB8,0xD8,0x38,0x38,0xDF,0x2A,0x6B,0xDD,0x12,0xCC,0xC7,0x3F, +0x47,0x17,0x4C,0xA2,0xC2,0x06,0x96,0x09,0xD6,0xDB,0xFE,0x3F,0x3C,0x46,0x41,0xDF, +0x58,0xE2,0x56,0x0F,0x3C,0x3B,0xC1,0x1C,0x93,0x35,0xD9,0x38,0x52,0xAC,0xEE,0xC8, +0xEC,0x2E,0x30,0x4E,0x94,0x35,0xB4,0x24,0x1F,0x4B,0x78,0x69,0xDA,0xF2,0x02,0x38, +0xCC,0x95,0x52,0x93,0xF0,0x70,0x25,0x59,0x9C,0x20,0x67,0xC4,0xEE,0xF9,0x8B,0x57, +0x61,0xF4,0x92,0x76,0x7D,0x3F,0x84,0x8D,0x55,0xB7,0xE8,0xE5,0xAC,0xD5,0xF1,0xF5, +0x19,0x56,0xA6,0x5A,0xFB,0x90,0x1C,0xAF,0x93,0xEB,0xE5,0x1C,0xD4,0x67,0x97,0x5D, +0x04,0x0E,0xBE,0x0B,0x83,0xA6,0x17,0x83,0xB9,0x30,0x12,0xA0,0xC5,0x33,0x15,0x05, +0xB9,0x0D,0xFB,0xC7,0x05,0x76,0xE3,0xD8,0x4A,0x8D,0xFC,0x34,0x17,0xA3,0xC6,0x21, +0x28,0xBE,0x30,0x45,0x31,0x1E,0xC7,0x78,0xBE,0x58,0x61,0x38,0xAC,0x3B,0xE2,0x01, +0x65, +}; + + +/* subject:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO ECC Certification Authority */ +/* issuer :/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO ECC Certification Authority */ + + +const unsigned char COMODO_ECC_Certification_Authority_certificate[653]={ +0x30,0x82,0x02,0x89,0x30,0x82,0x02,0x0F,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x1F, +0x47,0xAF,0xAA,0x62,0x00,0x70,0x50,0x54,0x4C,0x01,0x9E,0x9B,0x63,0x99,0x2A,0x30, +0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x30,0x81,0x85,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x47,0x42,0x31,0x1B,0x30,0x19,0x06, +0x03,0x55,0x04,0x08,0x13,0x12,0x47,0x72,0x65,0x61,0x74,0x65,0x72,0x20,0x4D,0x61, +0x6E,0x63,0x68,0x65,0x73,0x74,0x65,0x72,0x31,0x10,0x30,0x0E,0x06,0x03,0x55,0x04, +0x07,0x13,0x07,0x53,0x61,0x6C,0x66,0x6F,0x72,0x64,0x31,0x1A,0x30,0x18,0x06,0x03, +0x55,0x04,0x0A,0x13,0x11,0x43,0x4F,0x4D,0x4F,0x44,0x4F,0x20,0x43,0x41,0x20,0x4C, +0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x2B,0x30,0x29,0x06,0x03,0x55,0x04,0x03,0x13, +0x22,0x43,0x4F,0x4D,0x4F,0x44,0x4F,0x20,0x45,0x43,0x43,0x20,0x43,0x65,0x72,0x74, +0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72, +0x69,0x74,0x79,0x30,0x1E,0x17,0x0D,0x30,0x38,0x30,0x33,0x30,0x36,0x30,0x30,0x30, +0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x38,0x30,0x31,0x31,0x38,0x32,0x33,0x35,0x39, +0x35,0x39,0x5A,0x30,0x81,0x85,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13, +0x02,0x47,0x42,0x31,0x1B,0x30,0x19,0x06,0x03,0x55,0x04,0x08,0x13,0x12,0x47,0x72, +0x65,0x61,0x74,0x65,0x72,0x20,0x4D,0x61,0x6E,0x63,0x68,0x65,0x73,0x74,0x65,0x72, +0x31,0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x07,0x13,0x07,0x53,0x61,0x6C,0x66,0x6F, +0x72,0x64,0x31,0x1A,0x30,0x18,0x06,0x03,0x55,0x04,0x0A,0x13,0x11,0x43,0x4F,0x4D, +0x4F,0x44,0x4F,0x20,0x43,0x41,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x2B, +0x30,0x29,0x06,0x03,0x55,0x04,0x03,0x13,0x22,0x43,0x4F,0x4D,0x4F,0x44,0x4F,0x20, +0x45,0x43,0x43,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F, +0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x76,0x30,0x10,0x06, +0x07,0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,0x06,0x05,0x2B,0x81,0x04,0x00,0x22,0x03, +0x62,0x00,0x04,0x03,0x47,0x7B,0x2F,0x75,0xC9,0x82,0x15,0x85,0xFB,0x75,0xE4,0x91, +0x16,0xD4,0xAB,0x62,0x99,0xF5,0x3E,0x52,0x0B,0x06,0xCE,0x41,0x00,0x7F,0x97,0xE1, +0x0A,0x24,0x3C,0x1D,0x01,0x04,0xEE,0x3D,0xD2,0x8D,0x09,0x97,0x0C,0xE0,0x75,0xE4, +0xFA,0xFB,0x77,0x8A,0x2A,0xF5,0x03,0x60,0x4B,0x36,0x8B,0x16,0x23,0x16,0xAD,0x09, +0x71,0xF4,0x4A,0xF4,0x28,0x50,0xB4,0xFE,0x88,0x1C,0x6E,0x3F,0x6C,0x2F,0x2F,0x09, +0x59,0x5B,0xA5,0x5B,0x0B,0x33,0x99,0xE2,0xC3,0x3D,0x89,0xF9,0x6A,0x2C,0xEF,0xB2, +0xD3,0x06,0xE9,0xA3,0x42,0x30,0x40,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16, +0x04,0x14,0x75,0x71,0xA7,0x19,0x48,0x19,0xBC,0x9D,0x9D,0xEA,0x41,0x47,0xDF,0x94, +0xC4,0x48,0x77,0x99,0xD3,0x79,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF, +0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF, +0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D, +0x04,0x03,0x03,0x03,0x68,0x00,0x30,0x65,0x02,0x31,0x00,0xEF,0x03,0x5B,0x7A,0xAC, +0xB7,0x78,0x0A,0x72,0xB7,0x88,0xDF,0xFF,0xB5,0x46,0x14,0x09,0x0A,0xFA,0xA0,0xE6, +0x7D,0x08,0xC6,0x1A,0x87,0xBD,0x18,0xA8,0x73,0xBD,0x26,0xCA,0x60,0x0C,0x9D,0xCE, +0x99,0x9F,0xCF,0x5C,0x0F,0x30,0xE1,0xBE,0x14,0x31,0xEA,0x02,0x30,0x14,0xF4,0x93, +0x3C,0x49,0xA7,0x33,0x7A,0x90,0x46,0x47,0xB3,0x63,0x7D,0x13,0x9B,0x4E,0xB7,0x6F, +0x18,0x37,0x80,0x53,0xFE,0xDD,0x20,0xE0,0x35,0x9A,0x36,0xD1,0xC7,0x01,0xB9,0xE6, +0xDC,0xDD,0xF3,0xFF,0x1D,0x2C,0x3A,0x16,0x57,0xD9,0x92,0x39,0xD6, +}; + + +/* subject:/C=GB/ST=Greater Manchester/L=Salford/O=Comodo CA Limited/CN=Secure Certificate Services */ +/* issuer :/C=GB/ST=Greater Manchester/L=Salford/O=Comodo CA Limited/CN=Secure Certificate Services */ + + +const unsigned char Comodo_Secure_Services_root_certificate[1091]={ +0x30,0x82,0x04,0x3F,0x30,0x82,0x03,0x27,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30, +0x7E,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x47,0x42,0x31,0x1B, +0x30,0x19,0x06,0x03,0x55,0x04,0x08,0x0C,0x12,0x47,0x72,0x65,0x61,0x74,0x65,0x72, +0x20,0x4D,0x61,0x6E,0x63,0x68,0x65,0x73,0x74,0x65,0x72,0x31,0x10,0x30,0x0E,0x06, +0x03,0x55,0x04,0x07,0x0C,0x07,0x53,0x61,0x6C,0x66,0x6F,0x72,0x64,0x31,0x1A,0x30, +0x18,0x06,0x03,0x55,0x04,0x0A,0x0C,0x11,0x43,0x6F,0x6D,0x6F,0x64,0x6F,0x20,0x43, +0x41,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x24,0x30,0x22,0x06,0x03,0x55, +0x04,0x03,0x0C,0x1B,0x53,0x65,0x63,0x75,0x72,0x65,0x20,0x43,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x65,0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x30, +0x1E,0x17,0x0D,0x30,0x34,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5A, +0x17,0x0D,0x32,0x38,0x31,0x32,0x33,0x31,0x32,0x33,0x35,0x39,0x35,0x39,0x5A,0x30, +0x7E,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x47,0x42,0x31,0x1B, +0x30,0x19,0x06,0x03,0x55,0x04,0x08,0x0C,0x12,0x47,0x72,0x65,0x61,0x74,0x65,0x72, +0x20,0x4D,0x61,0x6E,0x63,0x68,0x65,0x73,0x74,0x65,0x72,0x31,0x10,0x30,0x0E,0x06, +0x03,0x55,0x04,0x07,0x0C,0x07,0x53,0x61,0x6C,0x66,0x6F,0x72,0x64,0x31,0x1A,0x30, +0x18,0x06,0x03,0x55,0x04,0x0A,0x0C,0x11,0x43,0x6F,0x6D,0x6F,0x64,0x6F,0x20,0x43, +0x41,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x24,0x30,0x22,0x06,0x03,0x55, +0x04,0x03,0x0C,0x1B,0x53,0x65,0x63,0x75,0x72,0x65,0x20,0x43,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x65,0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x30, +0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01, +0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00, +0xC0,0x71,0x33,0x82,0x8A,0xD0,0x70,0xEB,0x73,0x87,0x82,0x40,0xD5,0x1D,0xE4,0xCB, +0xC9,0x0E,0x42,0x90,0xF9,0xDE,0x34,0xB9,0xA1,0xBA,0x11,0xF4,0x25,0x85,0xF3,0xCC, +0x72,0x6D,0xF2,0x7B,0x97,0x6B,0xB3,0x07,0xF1,0x77,0x24,0x91,0x5F,0x25,0x8F,0xF6, +0x74,0x3D,0xE4,0x80,0xC2,0xF8,0x3C,0x0D,0xF3,0xBF,0x40,0xEA,0xF7,0xC8,0x52,0xD1, +0x72,0x6F,0xEF,0xC8,0xAB,0x41,0xB8,0x6E,0x2E,0x17,0x2A,0x95,0x69,0x0C,0xCD,0xD2, +0x1E,0x94,0x7B,0x2D,0x94,0x1D,0xAA,0x75,0xD7,0xB3,0x98,0xCB,0xAC,0xBC,0x64,0x53, +0x40,0xBC,0x8F,0xAC,0xAC,0x36,0xCB,0x5C,0xAD,0xBB,0xDD,0xE0,0x94,0x17,0xEC,0xD1, +0x5C,0xD0,0xBF,0xEF,0xA5,0x95,0xC9,0x90,0xC5,0xB0,0xAC,0xFB,0x1B,0x43,0xDF,0x7A, +0x08,0x5D,0xB7,0xB8,0xF2,0x40,0x1B,0x2B,0x27,0x9E,0x50,0xCE,0x5E,0x65,0x82,0x88, +0x8C,0x5E,0xD3,0x4E,0x0C,0x7A,0xEA,0x08,0x91,0xB6,0x36,0xAA,0x2B,0x42,0xFB,0xEA, +0xC2,0xA3,0x39,0xE5,0xDB,0x26,0x38,0xAD,0x8B,0x0A,0xEE,0x19,0x63,0xC7,0x1C,0x24, +0xDF,0x03,0x78,0xDA,0xE6,0xEA,0xC1,0x47,0x1A,0x0B,0x0B,0x46,0x09,0xDD,0x02,0xFC, +0xDE,0xCB,0x87,0x5F,0xD7,0x30,0x63,0x68,0xA1,0xAE,0xDC,0x32,0xA1,0xBA,0xBE,0xFE, +0x44,0xAB,0x68,0xB6,0xA5,0x17,0x15,0xFD,0xBD,0xD5,0xA7,0xA7,0x9A,0xE4,0x44,0x33, +0xE9,0x88,0x8E,0xFC,0xED,0x51,0xEB,0x93,0x71,0x4E,0xAD,0x01,0xE7,0x44,0x8E,0xAB, +0x2D,0xCB,0xA8,0xFE,0x01,0x49,0x48,0xF0,0xC0,0xDD,0xC7,0x68,0xD8,0x92,0xFE,0x3D, +0x02,0x03,0x01,0x00,0x01,0xA3,0x81,0xC7,0x30,0x81,0xC4,0x30,0x1D,0x06,0x03,0x55, +0x1D,0x0E,0x04,0x16,0x04,0x14,0x3C,0xD8,0x93,0x88,0xC2,0xC0,0x82,0x09,0xCC,0x01, +0x99,0x06,0x93,0x20,0xE9,0x9E,0x70,0x09,0x63,0x4F,0x30,0x0E,0x06,0x03,0x55,0x1D, +0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03,0x55,0x1D, +0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x81,0x81,0x06,0x03, +0x55,0x1D,0x1F,0x04,0x7A,0x30,0x78,0x30,0x3B,0xA0,0x39,0xA0,0x37,0x86,0x35,0x68, +0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x2E,0x63,0x6F,0x6D,0x6F,0x64,0x6F, +0x63,0x61,0x2E,0x63,0x6F,0x6D,0x2F,0x53,0x65,0x63,0x75,0x72,0x65,0x43,0x65,0x72, +0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x65,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73, +0x2E,0x63,0x72,0x6C,0x30,0x39,0xA0,0x37,0xA0,0x35,0x86,0x33,0x68,0x74,0x74,0x70, +0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x2E,0x63,0x6F,0x6D,0x6F,0x64,0x6F,0x2E,0x6E,0x65, +0x74,0x2F,0x53,0x65,0x63,0x75,0x72,0x65,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63, +0x61,0x74,0x65,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x2E,0x63,0x72,0x6C,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82, +0x01,0x01,0x00,0x87,0x01,0x6D,0x23,0x1D,0x7E,0x5B,0x17,0x7D,0xC1,0x61,0x32,0xCF, +0x8F,0xE7,0xF3,0x8A,0x94,0x59,0x66,0xE0,0x9E,0x28,0xA8,0x5E,0xD3,0xB7,0xF4,0x34, +0xE6,0xAA,0x39,0xB2,0x97,0x16,0xC5,0x82,0x6F,0x32,0xA4,0xE9,0x8C,0xE7,0xAF,0xFD, +0xEF,0xC2,0xE8,0xB9,0x4B,0xAA,0xA3,0xF4,0xE6,0xDA,0x8D,0x65,0x21,0xFB,0xBA,0x80, +0xEB,0x26,0x28,0x85,0x1A,0xFE,0x39,0x8C,0xDE,0x5B,0x04,0x04,0xB4,0x54,0xF9,0xA3, +0x67,0x9E,0x41,0xFA,0x09,0x52,0xCC,0x05,0x48,0xA8,0xC9,0x3F,0x21,0x04,0x1E,0xCE, +0x48,0x6B,0xFC,0x85,0xE8,0xC2,0x7B,0xAF,0x7F,0xB7,0xCC,0xF8,0x5F,0x3A,0xFD,0x35, +0xC6,0x0D,0xEF,0x97,0xDC,0x4C,0xAB,0x11,0xE1,0x6B,0xCB,0x31,0xD1,0x6C,0xFB,0x48, +0x80,0xAB,0xDC,0x9C,0x37,0xB8,0x21,0x14,0x4B,0x0D,0x71,0x3D,0xEC,0x83,0x33,0x6E, +0xD1,0x6E,0x32,0x16,0xEC,0x98,0xC7,0x16,0x8B,0x59,0xA6,0x34,0xAB,0x05,0x57,0x2D, +0x93,0xF7,0xAA,0x13,0xCB,0xD2,0x13,0xE2,0xB7,0x2E,0x3B,0xCD,0x6B,0x50,0x17,0x09, +0x68,0x3E,0xB5,0x26,0x57,0xEE,0xB6,0xE0,0xB6,0xDD,0xB9,0x29,0x80,0x79,0x7D,0x8F, +0xA3,0xF0,0xA4,0x28,0xA4,0x15,0xC4,0x85,0xF4,0x27,0xD4,0x6B,0xBF,0xE5,0x5C,0xE4, +0x65,0x02,0x76,0x54,0xB4,0xE3,0x37,0x66,0x24,0xD3,0x19,0x61,0xC8,0x52,0x10,0xE5, +0x8B,0x37,0x9A,0xB9,0xA9,0xF9,0x1D,0xBF,0xEA,0x99,0x92,0x61,0x96,0xFF,0x01,0xCD, +0xA1,0x5F,0x0D,0xBC,0x71,0xBC,0x0E,0xAC,0x0B,0x1D,0x47,0x45,0x1D,0xC1,0xEC,0x7C, +0xEC,0xFD,0x29, +}; + + +/* subject:/C=GB/ST=Greater Manchester/L=Salford/O=Comodo CA Limited/CN=Trusted Certificate Services */ +/* issuer :/C=GB/ST=Greater Manchester/L=Salford/O=Comodo CA Limited/CN=Trusted Certificate Services */ + + +const unsigned char Comodo_Trusted_Services_root_certificate[1095]={ +0x30,0x82,0x04,0x43,0x30,0x82,0x03,0x2B,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30, +0x7F,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x47,0x42,0x31,0x1B, +0x30,0x19,0x06,0x03,0x55,0x04,0x08,0x0C,0x12,0x47,0x72,0x65,0x61,0x74,0x65,0x72, +0x20,0x4D,0x61,0x6E,0x63,0x68,0x65,0x73,0x74,0x65,0x72,0x31,0x10,0x30,0x0E,0x06, +0x03,0x55,0x04,0x07,0x0C,0x07,0x53,0x61,0x6C,0x66,0x6F,0x72,0x64,0x31,0x1A,0x30, +0x18,0x06,0x03,0x55,0x04,0x0A,0x0C,0x11,0x43,0x6F,0x6D,0x6F,0x64,0x6F,0x20,0x43, +0x41,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x25,0x30,0x23,0x06,0x03,0x55, +0x04,0x03,0x0C,0x1C,0x54,0x72,0x75,0x73,0x74,0x65,0x64,0x20,0x43,0x65,0x72,0x74, +0x69,0x66,0x69,0x63,0x61,0x74,0x65,0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73, +0x30,0x1E,0x17,0x0D,0x30,0x34,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30, +0x5A,0x17,0x0D,0x32,0x38,0x31,0x32,0x33,0x31,0x32,0x33,0x35,0x39,0x35,0x39,0x5A, +0x30,0x7F,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x47,0x42,0x31, +0x1B,0x30,0x19,0x06,0x03,0x55,0x04,0x08,0x0C,0x12,0x47,0x72,0x65,0x61,0x74,0x65, +0x72,0x20,0x4D,0x61,0x6E,0x63,0x68,0x65,0x73,0x74,0x65,0x72,0x31,0x10,0x30,0x0E, +0x06,0x03,0x55,0x04,0x07,0x0C,0x07,0x53,0x61,0x6C,0x66,0x6F,0x72,0x64,0x31,0x1A, +0x30,0x18,0x06,0x03,0x55,0x04,0x0A,0x0C,0x11,0x43,0x6F,0x6D,0x6F,0x64,0x6F,0x20, +0x43,0x41,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x25,0x30,0x23,0x06,0x03, +0x55,0x04,0x03,0x0C,0x1C,0x54,0x72,0x75,0x73,0x74,0x65,0x64,0x20,0x43,0x65,0x72, +0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x65,0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65, +0x73,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01, +0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01, +0x01,0x00,0xDF,0x71,0x6F,0x36,0x58,0x53,0x5A,0xF2,0x36,0x54,0x57,0x80,0xC4,0x74, +0x08,0x20,0xED,0x18,0x7F,0x2A,0x1D,0xE6,0x35,0x9A,0x1E,0x25,0xAC,0x9C,0xE5,0x96, +0x7E,0x72,0x52,0xA0,0x15,0x42,0xDB,0x59,0xDD,0x64,0x7A,0x1A,0xD0,0xB8,0x7B,0xDD, +0x39,0x15,0xBC,0x55,0x48,0xC4,0xED,0x3A,0x00,0xEA,0x31,0x11,0xBA,0xF2,0x71,0x74, +0x1A,0x67,0xB8,0xCF,0x33,0xCC,0xA8,0x31,0xAF,0xA3,0xE3,0xD7,0x7F,0xBF,0x33,0x2D, +0x4C,0x6A,0x3C,0xEC,0x8B,0xC3,0x92,0xD2,0x53,0x77,0x24,0x74,0x9C,0x07,0x6E,0x70, +0xFC,0xBD,0x0B,0x5B,0x76,0xBA,0x5F,0xF2,0xFF,0xD7,0x37,0x4B,0x4A,0x60,0x78,0xF7, +0xF0,0xFA,0xCA,0x70,0xB4,0xEA,0x59,0xAA,0xA3,0xCE,0x48,0x2F,0xA9,0xC3,0xB2,0x0B, +0x7E,0x17,0x72,0x16,0x0C,0xA6,0x07,0x0C,0x1B,0x38,0xCF,0xC9,0x62,0xB7,0x3F,0xA0, +0x93,0xA5,0x87,0x41,0xF2,0xB7,0x70,0x40,0x77,0xD8,0xBE,0x14,0x7C,0xE3,0xA8,0xC0, +0x7A,0x8E,0xE9,0x63,0x6A,0xD1,0x0F,0x9A,0xC6,0xD2,0xF4,0x8B,0x3A,0x14,0x04,0x56, +0xD4,0xED,0xB8,0xCC,0x6E,0xF5,0xFB,0xE2,0x2C,0x58,0xBD,0x7F,0x4F,0x6B,0x2B,0xF7, +0x60,0x24,0x58,0x24,0xCE,0x26,0xEF,0x34,0x91,0x3A,0xD5,0xE3,0x81,0xD0,0xB2,0xF0, +0x04,0x02,0xD7,0x5B,0xB7,0x3E,0x92,0xAC,0x6B,0x12,0x8A,0xF9,0xE4,0x05,0xB0,0x3B, +0x91,0x49,0x5C,0xB2,0xEB,0x53,0xEA,0xF8,0x9F,0x47,0x86,0xEE,0xBF,0x95,0xC0,0xC0, +0x06,0x9F,0xD2,0x5B,0x5E,0x11,0x1B,0xF4,0xC7,0x04,0x35,0x29,0xD2,0x55,0x5C,0xE4, +0xED,0xEB,0x02,0x03,0x01,0x00,0x01,0xA3,0x81,0xC9,0x30,0x81,0xC6,0x30,0x1D,0x06, +0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xC5,0x7B,0x58,0xBD,0xED,0xDA,0x25,0x69, +0xD2,0xF7,0x59,0x16,0xA8,0xB3,0x32,0xC0,0x7B,0x27,0x5B,0xF4,0x30,0x0E,0x06,0x03, +0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03, +0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x81,0x83, +0x06,0x03,0x55,0x1D,0x1F,0x04,0x7C,0x30,0x7A,0x30,0x3C,0xA0,0x3A,0xA0,0x38,0x86, +0x36,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x2E,0x63,0x6F,0x6D,0x6F, +0x64,0x6F,0x63,0x61,0x2E,0x63,0x6F,0x6D,0x2F,0x54,0x72,0x75,0x73,0x74,0x65,0x64, +0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x65,0x53,0x65,0x72,0x76,0x69, +0x63,0x65,0x73,0x2E,0x63,0x72,0x6C,0x30,0x3A,0xA0,0x38,0xA0,0x36,0x86,0x34,0x68, +0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x2E,0x63,0x6F,0x6D,0x6F,0x64,0x6F, +0x2E,0x6E,0x65,0x74,0x2F,0x54,0x72,0x75,0x73,0x74,0x65,0x64,0x43,0x65,0x72,0x74, +0x69,0x66,0x69,0x63,0x61,0x74,0x65,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x2E, +0x63,0x72,0x6C,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05, +0x05,0x00,0x03,0x82,0x01,0x01,0x00,0xC8,0x93,0x81,0x3B,0x89,0xB4,0xAF,0xB8,0x84, +0x12,0x4C,0x8D,0xD2,0xF0,0xDB,0x70,0xBA,0x57,0x86,0x15,0x34,0x10,0xB9,0x2F,0x7F, +0x1E,0xB0,0xA8,0x89,0x60,0xA1,0x8A,0xC2,0x77,0x0C,0x50,0x4A,0x9B,0x00,0x8B,0xD8, +0x8B,0xF4,0x41,0xE2,0xD0,0x83,0x8A,0x4A,0x1C,0x14,0x06,0xB0,0xA3,0x68,0x05,0x70, +0x31,0x30,0xA7,0x53,0x9B,0x0E,0xE9,0x4A,0xA0,0x58,0x69,0x67,0x0E,0xAE,0x9D,0xF6, +0xA5,0x2C,0x41,0xBF,0x3C,0x06,0x6B,0xE4,0x59,0xCC,0x6D,0x10,0xF1,0x96,0x6F,0x1F, +0xDF,0xF4,0x04,0x02,0xA4,0x9F,0x45,0x3E,0xC8,0xD8,0xFA,0x36,0x46,0x44,0x50,0x3F, +0x82,0x97,0x91,0x1F,0x28,0xDB,0x18,0x11,0x8C,0x2A,0xE4,0x65,0x83,0x57,0x12,0x12, +0x8C,0x17,0x3F,0x94,0x36,0xFE,0x5D,0xB0,0xC0,0x04,0x77,0x13,0xB8,0xF4,0x15,0xD5, +0x3F,0x38,0xCC,0x94,0x3A,0x55,0xD0,0xAC,0x98,0xF5,0xBA,0x00,0x5F,0xE0,0x86,0x19, +0x81,0x78,0x2F,0x28,0xC0,0x7E,0xD3,0xCC,0x42,0x0A,0xF5,0xAE,0x50,0xA0,0xD1,0x3E, +0xC6,0xA1,0x71,0xEC,0x3F,0xA0,0x20,0x8C,0x66,0x3A,0x89,0xB4,0x8E,0xD4,0xD8,0xB1, +0x4D,0x25,0x47,0xEE,0x2F,0x88,0xC8,0xB5,0xE1,0x05,0x45,0xC0,0xBE,0x14,0x71,0xDE, +0x7A,0xFD,0x8E,0x7B,0x7D,0x4D,0x08,0x96,0xA5,0x12,0x73,0xF0,0x2D,0xCA,0x37,0x27, +0x74,0x12,0x27,0x4C,0xCB,0xB6,0x97,0xE9,0xD9,0xAE,0x08,0x6D,0x5A,0x39,0x40,0xDD, +0x05,0x47,0x75,0x6A,0x5A,0x21,0xB3,0xA3,0x18,0xCF,0x4E,0xF7,0x2E,0x57,0xB7,0x98, +0x70,0x5E,0xC8,0xC4,0x78,0xB0,0x62, +}; + + +/* subject:/O=Cybertrust, Inc/CN=Cybertrust Global Root */ +/* issuer :/O=Cybertrust, Inc/CN=Cybertrust Global Root */ + + +const unsigned char Cybertrust_Global_Root_certificate[933]={ +0x30,0x82,0x03,0xA1,0x30,0x82,0x02,0x89,0xA0,0x03,0x02,0x01,0x02,0x02,0x0B,0x04, +0x00,0x00,0x00,0x00,0x01,0x0F,0x85,0xAA,0x2D,0x48,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x3B,0x31,0x18,0x30,0x16,0x06, +0x03,0x55,0x04,0x0A,0x13,0x0F,0x43,0x79,0x62,0x65,0x72,0x74,0x72,0x75,0x73,0x74, +0x2C,0x20,0x49,0x6E,0x63,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x03,0x13,0x16, +0x43,0x79,0x62,0x65,0x72,0x74,0x72,0x75,0x73,0x74,0x20,0x47,0x6C,0x6F,0x62,0x61, +0x6C,0x20,0x52,0x6F,0x6F,0x74,0x30,0x1E,0x17,0x0D,0x30,0x36,0x31,0x32,0x31,0x35, +0x30,0x38,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,0x31,0x31,0x32,0x31,0x35,0x30, +0x38,0x30,0x30,0x30,0x30,0x5A,0x30,0x3B,0x31,0x18,0x30,0x16,0x06,0x03,0x55,0x04, +0x0A,0x13,0x0F,0x43,0x79,0x62,0x65,0x72,0x74,0x72,0x75,0x73,0x74,0x2C,0x20,0x49, +0x6E,0x63,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x03,0x13,0x16,0x43,0x79,0x62, +0x65,0x72,0x74,0x72,0x75,0x73,0x74,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x52, +0x6F,0x6F,0x74,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02, +0x82,0x01,0x01,0x00,0xF8,0xC8,0xBC,0xBD,0x14,0x50,0x66,0x13,0xFF,0xF0,0xD3,0x79, +0xEC,0x23,0xF2,0xB7,0x1A,0xC7,0x8E,0x85,0xF1,0x12,0x73,0xA6,0x19,0xAA,0x10,0xDB, +0x9C,0xA2,0x65,0x74,0x5A,0x77,0x3E,0x51,0x7D,0x56,0xF6,0xDC,0x23,0xB6,0xD4,0xED, +0x5F,0x58,0xB1,0x37,0x4D,0xD5,0x49,0x0E,0x6E,0xF5,0x6A,0x87,0xD6,0xD2,0x8C,0xD2, +0x27,0xC6,0xE2,0xFF,0x36,0x9F,0x98,0x65,0xA0,0x13,0x4E,0xC6,0x2A,0x64,0x9B,0xD5, +0x90,0x12,0xCF,0x14,0x06,0xF4,0x3B,0xE3,0xD4,0x28,0xBE,0xE8,0x0E,0xF8,0xAB,0x4E, +0x48,0x94,0x6D,0x8E,0x95,0x31,0x10,0x5C,0xED,0xA2,0x2D,0xBD,0xD5,0x3A,0x6D,0xB2, +0x1C,0xBB,0x60,0xC0,0x46,0x4B,0x01,0xF5,0x49,0xAE,0x7E,0x46,0x8A,0xD0,0x74,0x8D, +0xA1,0x0C,0x02,0xCE,0xEE,0xFC,0xE7,0x8F,0xB8,0x6B,0x66,0xF3,0x7F,0x44,0x00,0xBF, +0x66,0x25,0x14,0x2B,0xDD,0x10,0x30,0x1D,0x07,0x96,0x3F,0x4D,0xF6,0x6B,0xB8,0x8F, +0xB7,0x7B,0x0C,0xA5,0x38,0xEB,0xDE,0x47,0xDB,0xD5,0x5D,0x39,0xFC,0x88,0xA7,0xF3, +0xD7,0x2A,0x74,0xF1,0xE8,0x5A,0xA2,0x3B,0x9F,0x50,0xBA,0xA6,0x8C,0x45,0x35,0xC2, +0x50,0x65,0x95,0xDC,0x63,0x82,0xEF,0xDD,0xBF,0x77,0x4D,0x9C,0x62,0xC9,0x63,0x73, +0x16,0xD0,0x29,0x0F,0x49,0xA9,0x48,0xF0,0xB3,0xAA,0xB7,0x6C,0xC5,0xA7,0x30,0x39, +0x40,0x5D,0xAE,0xC4,0xE2,0x5D,0x26,0x53,0xF0,0xCE,0x1C,0x23,0x08,0x61,0xA8,0x94, +0x19,0xBA,0x04,0x62,0x40,0xEC,0x1F,0x38,0x70,0x77,0x12,0x06,0x71,0xA7,0x30,0x18, +0x5D,0x25,0x27,0xA5,0x02,0x03,0x01,0x00,0x01,0xA3,0x81,0xA5,0x30,0x81,0xA2,0x30, +0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30, +0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF, +0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xB6,0x08,0x7B,0x0D,0x7A, +0xCC,0xAC,0x20,0x4C,0x86,0x56,0x32,0x5E,0xCF,0xAB,0x6E,0x85,0x2D,0x70,0x57,0x30, +0x3F,0x06,0x03,0x55,0x1D,0x1F,0x04,0x38,0x30,0x36,0x30,0x34,0xA0,0x32,0xA0,0x30, +0x86,0x2E,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x77,0x77,0x77,0x32,0x2E,0x70,0x75, +0x62,0x6C,0x69,0x63,0x2D,0x74,0x72,0x75,0x73,0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x63, +0x72,0x6C,0x2F,0x63,0x74,0x2F,0x63,0x74,0x72,0x6F,0x6F,0x74,0x2E,0x63,0x72,0x6C, +0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0xB6,0x08,0x7B, +0x0D,0x7A,0xCC,0xAC,0x20,0x4C,0x86,0x56,0x32,0x5E,0xCF,0xAB,0x6E,0x85,0x2D,0x70, +0x57,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00, +0x03,0x82,0x01,0x01,0x00,0x56,0xEF,0x0A,0x23,0xA0,0x54,0x4E,0x95,0x97,0xC9,0xF8, +0x89,0xDA,0x45,0xC1,0xD4,0xA3,0x00,0x25,0xF4,0x1F,0x13,0xAB,0xB7,0xA3,0x85,0x58, +0x69,0xC2,0x30,0xAD,0xD8,0x15,0x8A,0x2D,0xE3,0xC9,0xCD,0x81,0x5A,0xF8,0x73,0x23, +0x5A,0xA7,0x7C,0x05,0xF3,0xFD,0x22,0x3B,0x0E,0xD1,0x06,0xC4,0xDB,0x36,0x4C,0x73, +0x04,0x8E,0xE5,0xB0,0x22,0xE4,0xC5,0xF3,0x2E,0xA5,0xD9,0x23,0xE3,0xB8,0x4E,0x4A, +0x20,0xA7,0x6E,0x02,0x24,0x9F,0x22,0x60,0x67,0x7B,0x8B,0x1D,0x72,0x09,0xC5,0x31, +0x5C,0xE9,0x79,0x9F,0x80,0x47,0x3D,0xAD,0xA1,0x0B,0x07,0x14,0x3D,0x47,0xFF,0x03, +0x69,0x1A,0x0C,0x0B,0x44,0xE7,0x63,0x25,0xA7,0x7F,0xB2,0xC9,0xB8,0x76,0x84,0xED, +0x23,0xF6,0x7D,0x07,0xAB,0x45,0x7E,0xD3,0xDF,0xB3,0xBF,0xE9,0x8A,0xB6,0xCD,0xA8, +0xA2,0x67,0x2B,0x52,0xD5,0xB7,0x65,0xF0,0x39,0x4C,0x63,0xA0,0x91,0x79,0x93,0x52, +0x0F,0x54,0xDD,0x83,0xBB,0x9F,0xD1,0x8F,0xA7,0x53,0x73,0xC3,0xCB,0xFF,0x30,0xEC, +0x7C,0x04,0xB8,0xD8,0x44,0x1F,0x93,0x5F,0x71,0x09,0x22,0xB7,0x6E,0x3E,0xEA,0x1C, +0x03,0x4E,0x9D,0x1A,0x20,0x61,0xFB,0x81,0x37,0xEC,0x5E,0xFC,0x0A,0x45,0xAB,0xD7, +0xE7,0x17,0x55,0xD0,0xA0,0xEA,0x60,0x9B,0xA6,0xF6,0xE3,0x8C,0x5B,0x29,0xC2,0x06, +0x60,0x14,0x9D,0x2D,0x97,0x4C,0xA9,0x93,0x15,0x9D,0x61,0xC4,0x01,0x5F,0x48,0xD6, +0x58,0xBD,0x56,0x31,0x12,0x4E,0x11,0xC8,0x21,0xE0,0xB3,0x11,0x91,0x65,0xDB,0xB4, +0xA6,0x88,0x38,0xCE,0x55, +}; + + +/* subject:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Assured ID Root CA */ +/* issuer :/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Assured ID Root CA */ + + +const unsigned char DigiCert_Assured_ID_Root_CA_certificate[955]={ +0x30,0x82,0x03,0xB7,0x30,0x82,0x02,0x9F,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x0C, +0xE7,0xE0,0xE5,0x17,0xD8,0x46,0xFE,0x8F,0xE5,0x60,0xFC,0x1B,0xF0,0x30,0x39,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x65, +0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30, +0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74, +0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77, +0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31, +0x24,0x30,0x22,0x06,0x03,0x55,0x04,0x03,0x13,0x1B,0x44,0x69,0x67,0x69,0x43,0x65, +0x72,0x74,0x20,0x41,0x73,0x73,0x75,0x72,0x65,0x64,0x20,0x49,0x44,0x20,0x52,0x6F, +0x6F,0x74,0x20,0x43,0x41,0x30,0x1E,0x17,0x0D,0x30,0x36,0x31,0x31,0x31,0x30,0x30, +0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x31,0x31,0x31,0x31,0x30,0x30,0x30, +0x30,0x30,0x30,0x30,0x5A,0x30,0x65,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06, +0x13,0x02,0x55,0x53,0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44, +0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06, +0x03,0x55,0x04,0x0B,0x13,0x10,0x77,0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65, +0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,0x24,0x30,0x22,0x06,0x03,0x55,0x04,0x03,0x13, +0x1B,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x41,0x73,0x73,0x75,0x72,0x65, +0x64,0x20,0x49,0x44,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x30,0x82,0x01,0x22, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03, +0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xAD,0x0E,0x15, +0xCE,0xE4,0x43,0x80,0x5C,0xB1,0x87,0xF3,0xB7,0x60,0xF9,0x71,0x12,0xA5,0xAE,0xDC, +0x26,0x94,0x88,0xAA,0xF4,0xCE,0xF5,0x20,0x39,0x28,0x58,0x60,0x0C,0xF8,0x80,0xDA, +0xA9,0x15,0x95,0x32,0x61,0x3C,0xB5,0xB1,0x28,0x84,0x8A,0x8A,0xDC,0x9F,0x0A,0x0C, +0x83,0x17,0x7A,0x8F,0x90,0xAC,0x8A,0xE7,0x79,0x53,0x5C,0x31,0x84,0x2A,0xF6,0x0F, +0x98,0x32,0x36,0x76,0xCC,0xDE,0xDD,0x3C,0xA8,0xA2,0xEF,0x6A,0xFB,0x21,0xF2,0x52, +0x61,0xDF,0x9F,0x20,0xD7,0x1F,0xE2,0xB1,0xD9,0xFE,0x18,0x64,0xD2,0x12,0x5B,0x5F, +0xF9,0x58,0x18,0x35,0xBC,0x47,0xCD,0xA1,0x36,0xF9,0x6B,0x7F,0xD4,0xB0,0x38,0x3E, +0xC1,0x1B,0xC3,0x8C,0x33,0xD9,0xD8,0x2F,0x18,0xFE,0x28,0x0F,0xB3,0xA7,0x83,0xD6, +0xC3,0x6E,0x44,0xC0,0x61,0x35,0x96,0x16,0xFE,0x59,0x9C,0x8B,0x76,0x6D,0xD7,0xF1, +0xA2,0x4B,0x0D,0x2B,0xFF,0x0B,0x72,0xDA,0x9E,0x60,0xD0,0x8E,0x90,0x35,0xC6,0x78, +0x55,0x87,0x20,0xA1,0xCF,0xE5,0x6D,0x0A,0xC8,0x49,0x7C,0x31,0x98,0x33,0x6C,0x22, +0xE9,0x87,0xD0,0x32,0x5A,0xA2,0xBA,0x13,0x82,0x11,0xED,0x39,0x17,0x9D,0x99,0x3A, +0x72,0xA1,0xE6,0xFA,0xA4,0xD9,0xD5,0x17,0x31,0x75,0xAE,0x85,0x7D,0x22,0xAE,0x3F, +0x01,0x46,0x86,0xF6,0x28,0x79,0xC8,0xB1,0xDA,0xE4,0x57,0x17,0xC4,0x7E,0x1C,0x0E, +0xB0,0xB4,0x92,0xA6,0x56,0xB3,0xBD,0xB2,0x97,0xED,0xAA,0xA7,0xF0,0xB7,0xC5,0xA8, +0x3F,0x95,0x16,0xD0,0xFF,0xA1,0x96,0xEB,0x08,0x5F,0x18,0x77,0x4F,0x02,0x03,0x01, +0x00,0x01,0xA3,0x63,0x30,0x61,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF, +0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF, +0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16, +0x04,0x14,0x45,0xEB,0xA2,0xAF,0xF4,0x92,0xCB,0x82,0x31,0x2D,0x51,0x8B,0xA7,0xA7, +0x21,0x9D,0xF3,0x6D,0xC8,0x0F,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30, +0x16,0x80,0x14,0x45,0xEB,0xA2,0xAF,0xF4,0x92,0xCB,0x82,0x31,0x2D,0x51,0x8B,0xA7, +0xA7,0x21,0x9D,0xF3,0x6D,0xC8,0x0F,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0xA2,0x0E,0xBC,0xDF,0xE2, +0xED,0xF0,0xE3,0x72,0x73,0x7A,0x64,0x94,0xBF,0xF7,0x72,0x66,0xD8,0x32,0xE4,0x42, +0x75,0x62,0xAE,0x87,0xEB,0xF2,0xD5,0xD9,0xDE,0x56,0xB3,0x9F,0xCC,0xCE,0x14,0x28, +0xB9,0x0D,0x97,0x60,0x5C,0x12,0x4C,0x58,0xE4,0xD3,0x3D,0x83,0x49,0x45,0x58,0x97, +0x35,0x69,0x1A,0xA8,0x47,0xEA,0x56,0xC6,0x79,0xAB,0x12,0xD8,0x67,0x81,0x84,0xDF, +0x7F,0x09,0x3C,0x94,0xE6,0xB8,0x26,0x2C,0x20,0xBD,0x3D,0xB3,0x28,0x89,0xF7,0x5F, +0xFF,0x22,0xE2,0x97,0x84,0x1F,0xE9,0x65,0xEF,0x87,0xE0,0xDF,0xC1,0x67,0x49,0xB3, +0x5D,0xEB,0xB2,0x09,0x2A,0xEB,0x26,0xED,0x78,0xBE,0x7D,0x3F,0x2B,0xF3,0xB7,0x26, +0x35,0x6D,0x5F,0x89,0x01,0xB6,0x49,0x5B,0x9F,0x01,0x05,0x9B,0xAB,0x3D,0x25,0xC1, +0xCC,0xB6,0x7F,0xC2,0xF1,0x6F,0x86,0xC6,0xFA,0x64,0x68,0xEB,0x81,0x2D,0x94,0xEB, +0x42,0xB7,0xFA,0x8C,0x1E,0xDD,0x62,0xF1,0xBE,0x50,0x67,0xB7,0x6C,0xBD,0xF3,0xF1, +0x1F,0x6B,0x0C,0x36,0x07,0x16,0x7F,0x37,0x7C,0xA9,0x5B,0x6D,0x7A,0xF1,0x12,0x46, +0x60,0x83,0xD7,0x27,0x04,0xBE,0x4B,0xCE,0x97,0xBE,0xC3,0x67,0x2A,0x68,0x11,0xDF, +0x80,0xE7,0x0C,0x33,0x66,0xBF,0x13,0x0D,0x14,0x6E,0xF3,0x7F,0x1F,0x63,0x10,0x1E, +0xFA,0x8D,0x1B,0x25,0x6D,0x6C,0x8F,0xA5,0xB7,0x61,0x01,0xB1,0xD2,0xA3,0x26,0xA1, +0x10,0x71,0x9D,0xAD,0xE2,0xC3,0xF9,0xC3,0x99,0x51,0xB7,0x2B,0x07,0x08,0xCE,0x2E, +0xE6,0x50,0xB2,0xA7,0xFA,0x0A,0x45,0x2F,0xA2,0xF0,0xF2, +}; + + +/* subject:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA */ +/* issuer :/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA */ + + +const unsigned char DigiCert_Global_Root_CA_certificate[947]={ +0x30,0x82,0x03,0xAF,0x30,0x82,0x02,0x97,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x08, +0x3B,0xE0,0x56,0x90,0x42,0x46,0xB1,0xA1,0x75,0x6A,0xC9,0x59,0x91,0xC7,0x4A,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x61, +0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30, +0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74, +0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77, +0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31, +0x20,0x30,0x1E,0x06,0x03,0x55,0x04,0x03,0x13,0x17,0x44,0x69,0x67,0x69,0x43,0x65, +0x72,0x74,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43, +0x41,0x30,0x1E,0x17,0x0D,0x30,0x36,0x31,0x31,0x31,0x30,0x30,0x30,0x30,0x30,0x30, +0x30,0x5A,0x17,0x0D,0x33,0x31,0x31,0x31,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x30, +0x5A,0x30,0x61,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53, +0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43, +0x65,0x72,0x74,0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B, +0x13,0x10,0x77,0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63, +0x6F,0x6D,0x31,0x20,0x30,0x1E,0x06,0x03,0x55,0x04,0x03,0x13,0x17,0x44,0x69,0x67, +0x69,0x43,0x65,0x72,0x74,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x52,0x6F,0x6F, +0x74,0x20,0x43,0x41,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A, +0x02,0x82,0x01,0x01,0x00,0xE2,0x3B,0xE1,0x11,0x72,0xDE,0xA8,0xA4,0xD3,0xA3,0x57, +0xAA,0x50,0xA2,0x8F,0x0B,0x77,0x90,0xC9,0xA2,0xA5,0xEE,0x12,0xCE,0x96,0x5B,0x01, +0x09,0x20,0xCC,0x01,0x93,0xA7,0x4E,0x30,0xB7,0x53,0xF7,0x43,0xC4,0x69,0x00,0x57, +0x9D,0xE2,0x8D,0x22,0xDD,0x87,0x06,0x40,0x00,0x81,0x09,0xCE,0xCE,0x1B,0x83,0xBF, +0xDF,0xCD,0x3B,0x71,0x46,0xE2,0xD6,0x66,0xC7,0x05,0xB3,0x76,0x27,0x16,0x8F,0x7B, +0x9E,0x1E,0x95,0x7D,0xEE,0xB7,0x48,0xA3,0x08,0xDA,0xD6,0xAF,0x7A,0x0C,0x39,0x06, +0x65,0x7F,0x4A,0x5D,0x1F,0xBC,0x17,0xF8,0xAB,0xBE,0xEE,0x28,0xD7,0x74,0x7F,0x7A, +0x78,0x99,0x59,0x85,0x68,0x6E,0x5C,0x23,0x32,0x4B,0xBF,0x4E,0xC0,0xE8,0x5A,0x6D, +0xE3,0x70,0xBF,0x77,0x10,0xBF,0xFC,0x01,0xF6,0x85,0xD9,0xA8,0x44,0x10,0x58,0x32, +0xA9,0x75,0x18,0xD5,0xD1,0xA2,0xBE,0x47,0xE2,0x27,0x6A,0xF4,0x9A,0x33,0xF8,0x49, +0x08,0x60,0x8B,0xD4,0x5F,0xB4,0x3A,0x84,0xBF,0xA1,0xAA,0x4A,0x4C,0x7D,0x3E,0xCF, +0x4F,0x5F,0x6C,0x76,0x5E,0xA0,0x4B,0x37,0x91,0x9E,0xDC,0x22,0xE6,0x6D,0xCE,0x14, +0x1A,0x8E,0x6A,0xCB,0xFE,0xCD,0xB3,0x14,0x64,0x17,0xC7,0x5B,0x29,0x9E,0x32,0xBF, +0xF2,0xEE,0xFA,0xD3,0x0B,0x42,0xD4,0xAB,0xB7,0x41,0x32,0xDA,0x0C,0xD4,0xEF,0xF8, +0x81,0xD5,0xBB,0x8D,0x58,0x3F,0xB5,0x1B,0xE8,0x49,0x28,0xA2,0x70,0xDA,0x31,0x04, +0xDD,0xF7,0xB2,0x16,0xF2,0x4C,0x0A,0x4E,0x07,0xA8,0xED,0x4A,0x3D,0x5E,0xB5,0x7F, +0xA3,0x90,0xC3,0xAF,0x27,0x02,0x03,0x01,0x00,0x01,0xA3,0x63,0x30,0x61,0x30,0x0E, +0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x0F, +0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30, +0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x03,0xDE,0x50,0x35,0x56,0xD1, +0x4C,0xBB,0x66,0xF0,0xA3,0xE2,0x1B,0x1B,0xC3,0x97,0xB2,0x3D,0xD1,0x55,0x30,0x1F, +0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0x03,0xDE,0x50,0x35,0x56, +0xD1,0x4C,0xBB,0x66,0xF0,0xA3,0xE2,0x1B,0x1B,0xC3,0x97,0xB2,0x3D,0xD1,0x55,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82, +0x01,0x01,0x00,0xCB,0x9C,0x37,0xAA,0x48,0x13,0x12,0x0A,0xFA,0xDD,0x44,0x9C,0x4F, +0x52,0xB0,0xF4,0xDF,0xAE,0x04,0xF5,0x79,0x79,0x08,0xA3,0x24,0x18,0xFC,0x4B,0x2B, +0x84,0xC0,0x2D,0xB9,0xD5,0xC7,0xFE,0xF4,0xC1,0x1F,0x58,0xCB,0xB8,0x6D,0x9C,0x7A, +0x74,0xE7,0x98,0x29,0xAB,0x11,0xB5,0xE3,0x70,0xA0,0xA1,0xCD,0x4C,0x88,0x99,0x93, +0x8C,0x91,0x70,0xE2,0xAB,0x0F,0x1C,0xBE,0x93,0xA9,0xFF,0x63,0xD5,0xE4,0x07,0x60, +0xD3,0xA3,0xBF,0x9D,0x5B,0x09,0xF1,0xD5,0x8E,0xE3,0x53,0xF4,0x8E,0x63,0xFA,0x3F, +0xA7,0xDB,0xB4,0x66,0xDF,0x62,0x66,0xD6,0xD1,0x6E,0x41,0x8D,0xF2,0x2D,0xB5,0xEA, +0x77,0x4A,0x9F,0x9D,0x58,0xE2,0x2B,0x59,0xC0,0x40,0x23,0xED,0x2D,0x28,0x82,0x45, +0x3E,0x79,0x54,0x92,0x26,0x98,0xE0,0x80,0x48,0xA8,0x37,0xEF,0xF0,0xD6,0x79,0x60, +0x16,0xDE,0xAC,0xE8,0x0E,0xCD,0x6E,0xAC,0x44,0x17,0x38,0x2F,0x49,0xDA,0xE1,0x45, +0x3E,0x2A,0xB9,0x36,0x53,0xCF,0x3A,0x50,0x06,0xF7,0x2E,0xE8,0xC4,0x57,0x49,0x6C, +0x61,0x21,0x18,0xD5,0x04,0xAD,0x78,0x3C,0x2C,0x3A,0x80,0x6B,0xA7,0xEB,0xAF,0x15, +0x14,0xE9,0xD8,0x89,0xC1,0xB9,0x38,0x6C,0xE2,0x91,0x6C,0x8A,0xFF,0x64,0xB9,0x77, +0x25,0x57,0x30,0xC0,0x1B,0x24,0xA3,0xE1,0xDC,0xE9,0xDF,0x47,0x7C,0xB5,0xB4,0x24, +0x08,0x05,0x30,0xEC,0x2D,0xBD,0x0B,0xBF,0x45,0xBF,0x50,0xB9,0xA9,0xF3,0xEB,0x98, +0x01,0x12,0xAD,0xC8,0x88,0xC6,0x98,0x34,0x5F,0x8D,0x0A,0x3C,0xC6,0xE9,0xD5,0x95, +0x95,0x6D,0xDE, +}; + + +/* subject:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA */ +/* issuer :/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA */ + + +const unsigned char DigiCert_High_Assurance_EV_Root_CA_certificate[969]={ +0x30,0x82,0x03,0xC5,0x30,0x82,0x02,0xAD,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x02, +0xAC,0x5C,0x26,0x6A,0x0B,0x40,0x9B,0x8F,0x0B,0x79,0xF2,0xAE,0x46,0x25,0x77,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x6C, +0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30, +0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74, +0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77, +0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31, +0x2B,0x30,0x29,0x06,0x03,0x55,0x04,0x03,0x13,0x22,0x44,0x69,0x67,0x69,0x43,0x65, +0x72,0x74,0x20,0x48,0x69,0x67,0x68,0x20,0x41,0x73,0x73,0x75,0x72,0x61,0x6E,0x63, +0x65,0x20,0x45,0x56,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x30,0x1E,0x17,0x0D, +0x30,0x36,0x31,0x31,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33, +0x31,0x31,0x31,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x30,0x6C,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,0x13,0x06, +0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x49, +0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77,0x77,0x77, +0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,0x2B,0x30, +0x29,0x06,0x03,0x55,0x04,0x03,0x13,0x22,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74, +0x20,0x48,0x69,0x67,0x68,0x20,0x41,0x73,0x73,0x75,0x72,0x61,0x6E,0x63,0x65,0x20, +0x45,0x56,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x30,0x82,0x01,0x22,0x30,0x0D, +0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01, +0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xC6,0xCC,0xE5,0x73,0xE6, +0xFB,0xD4,0xBB,0xE5,0x2D,0x2D,0x32,0xA6,0xDF,0xE5,0x81,0x3F,0xC9,0xCD,0x25,0x49, +0xB6,0x71,0x2A,0xC3,0xD5,0x94,0x34,0x67,0xA2,0x0A,0x1C,0xB0,0x5F,0x69,0xA6,0x40, +0xB1,0xC4,0xB7,0xB2,0x8F,0xD0,0x98,0xA4,0xA9,0x41,0x59,0x3A,0xD3,0xDC,0x94,0xD6, +0x3C,0xDB,0x74,0x38,0xA4,0x4A,0xCC,0x4D,0x25,0x82,0xF7,0x4A,0xA5,0x53,0x12,0x38, +0xEE,0xF3,0x49,0x6D,0x71,0x91,0x7E,0x63,0xB6,0xAB,0xA6,0x5F,0xC3,0xA4,0x84,0xF8, +0x4F,0x62,0x51,0xBE,0xF8,0xC5,0xEC,0xDB,0x38,0x92,0xE3,0x06,0xE5,0x08,0x91,0x0C, +0xC4,0x28,0x41,0x55,0xFB,0xCB,0x5A,0x89,0x15,0x7E,0x71,0xE8,0x35,0xBF,0x4D,0x72, +0x09,0x3D,0xBE,0x3A,0x38,0x50,0x5B,0x77,0x31,0x1B,0x8D,0xB3,0xC7,0x24,0x45,0x9A, +0xA7,0xAC,0x6D,0x00,0x14,0x5A,0x04,0xB7,0xBA,0x13,0xEB,0x51,0x0A,0x98,0x41,0x41, +0x22,0x4E,0x65,0x61,0x87,0x81,0x41,0x50,0xA6,0x79,0x5C,0x89,0xDE,0x19,0x4A,0x57, +0xD5,0x2E,0xE6,0x5D,0x1C,0x53,0x2C,0x7E,0x98,0xCD,0x1A,0x06,0x16,0xA4,0x68,0x73, +0xD0,0x34,0x04,0x13,0x5C,0xA1,0x71,0xD3,0x5A,0x7C,0x55,0xDB,0x5E,0x64,0xE1,0x37, +0x87,0x30,0x56,0x04,0xE5,0x11,0xB4,0x29,0x80,0x12,0xF1,0x79,0x39,0x88,0xA2,0x02, +0x11,0x7C,0x27,0x66,0xB7,0x88,0xB7,0x78,0xF2,0xCA,0x0A,0xA8,0x38,0xAB,0x0A,0x64, +0xC2,0xBF,0x66,0x5D,0x95,0x84,0xC1,0xA1,0x25,0x1E,0x87,0x5D,0x1A,0x50,0x0B,0x20, +0x12,0xCC,0x41,0xBB,0x6E,0x0B,0x51,0x38,0xB8,0x4B,0xCB,0x02,0x03,0x01,0x00,0x01, +0xA3,0x63,0x30,0x61,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04, +0x03,0x02,0x01,0x86,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05, +0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14, +0xB1,0x3E,0xC3,0x69,0x03,0xF8,0xBF,0x47,0x01,0xD4,0x98,0x26,0x1A,0x08,0x02,0xEF, +0x63,0x64,0x2B,0xC3,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80, +0x14,0xB1,0x3E,0xC3,0x69,0x03,0xF8,0xBF,0x47,0x01,0xD4,0x98,0x26,0x1A,0x08,0x02, +0xEF,0x63,0x64,0x2B,0xC3,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01, +0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x1C,0x1A,0x06,0x97,0xDC,0xD7,0x9C, +0x9F,0x3C,0x88,0x66,0x06,0x08,0x57,0x21,0xDB,0x21,0x47,0xF8,0x2A,0x67,0xAA,0xBF, +0x18,0x32,0x76,0x40,0x10,0x57,0xC1,0x8A,0xF3,0x7A,0xD9,0x11,0x65,0x8E,0x35,0xFA, +0x9E,0xFC,0x45,0xB5,0x9E,0xD9,0x4C,0x31,0x4B,0xB8,0x91,0xE8,0x43,0x2C,0x8E,0xB3, +0x78,0xCE,0xDB,0xE3,0x53,0x79,0x71,0xD6,0xE5,0x21,0x94,0x01,0xDA,0x55,0x87,0x9A, +0x24,0x64,0xF6,0x8A,0x66,0xCC,0xDE,0x9C,0x37,0xCD,0xA8,0x34,0xB1,0x69,0x9B,0x23, +0xC8,0x9E,0x78,0x22,0x2B,0x70,0x43,0xE3,0x55,0x47,0x31,0x61,0x19,0xEF,0x58,0xC5, +0x85,0x2F,0x4E,0x30,0xF6,0xA0,0x31,0x16,0x23,0xC8,0xE7,0xE2,0x65,0x16,0x33,0xCB, +0xBF,0x1A,0x1B,0xA0,0x3D,0xF8,0xCA,0x5E,0x8B,0x31,0x8B,0x60,0x08,0x89,0x2D,0x0C, +0x06,0x5C,0x52,0xB7,0xC4,0xF9,0x0A,0x98,0xD1,0x15,0x5F,0x9F,0x12,0xBE,0x7C,0x36, +0x63,0x38,0xBD,0x44,0xA4,0x7F,0xE4,0x26,0x2B,0x0A,0xC4,0x97,0x69,0x0D,0xE9,0x8C, +0xE2,0xC0,0x10,0x57,0xB8,0xC8,0x76,0x12,0x91,0x55,0xF2,0x48,0x69,0xD8,0xBC,0x2A, +0x02,0x5B,0x0F,0x44,0xD4,0x20,0x31,0xDB,0xF4,0xBA,0x70,0x26,0x5D,0x90,0x60,0x9E, +0xBC,0x4B,0x17,0x09,0x2F,0xB4,0xCB,0x1E,0x43,0x68,0xC9,0x07,0x27,0xC1,0xD2,0x5C, +0xF7,0xEA,0x21,0xB9,0x68,0x12,0x9C,0x3C,0x9C,0xBF,0x9E,0xFC,0x80,0x5C,0x9B,0x63, +0xCD,0xEC,0x47,0xAA,0x25,0x27,0x67,0xA0,0x37,0xF3,0x00,0x82,0x7D,0x54,0xD7,0xA9, +0xF8,0xE9,0x2E,0x13,0xA3,0x77,0xE8,0x1F,0x4A, +}; + + +/* subject:/O=Entrust.net/OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/OU=(c) 1999 Entrust.net Limited/CN=Entrust.net Certification Authority (2048) */ +/* issuer :/O=Entrust.net/OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/OU=(c) 1999 Entrust.net Limited/CN=Entrust.net Certification Authority (2048) */ + + +const unsigned char Entrust_net_Premium_2048_Secure_Server_CA_certificate[1120]={ +0x30,0x82,0x04,0x5C,0x30,0x82,0x03,0x44,0xA0,0x03,0x02,0x01,0x02,0x02,0x04,0x38, +0x63,0xB9,0x66,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05, +0x05,0x00,0x30,0x81,0xB4,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x13,0x0B, +0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x31,0x40,0x30,0x3E,0x06, +0x03,0x55,0x04,0x0B,0x14,0x37,0x77,0x77,0x77,0x2E,0x65,0x6E,0x74,0x72,0x75,0x73, +0x74,0x2E,0x6E,0x65,0x74,0x2F,0x43,0x50,0x53,0x5F,0x32,0x30,0x34,0x38,0x20,0x69, +0x6E,0x63,0x6F,0x72,0x70,0x2E,0x20,0x62,0x79,0x20,0x72,0x65,0x66,0x2E,0x20,0x28, +0x6C,0x69,0x6D,0x69,0x74,0x73,0x20,0x6C,0x69,0x61,0x62,0x2E,0x29,0x31,0x25,0x30, +0x23,0x06,0x03,0x55,0x04,0x0B,0x13,0x1C,0x28,0x63,0x29,0x20,0x31,0x39,0x39,0x39, +0x20,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x20,0x4C,0x69,0x6D, +0x69,0x74,0x65,0x64,0x31,0x33,0x30,0x31,0x06,0x03,0x55,0x04,0x03,0x13,0x2A,0x45, +0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x20,0x43,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69, +0x74,0x79,0x20,0x28,0x32,0x30,0x34,0x38,0x29,0x30,0x1E,0x17,0x0D,0x39,0x39,0x31, +0x32,0x32,0x34,0x31,0x37,0x35,0x30,0x35,0x31,0x5A,0x17,0x0D,0x31,0x39,0x31,0x32, +0x32,0x34,0x31,0x38,0x32,0x30,0x35,0x31,0x5A,0x30,0x81,0xB4,0x31,0x14,0x30,0x12, +0x06,0x03,0x55,0x04,0x0A,0x13,0x0B,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E, +0x65,0x74,0x31,0x40,0x30,0x3E,0x06,0x03,0x55,0x04,0x0B,0x14,0x37,0x77,0x77,0x77, +0x2E,0x65,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x2F,0x43,0x50,0x53, +0x5F,0x32,0x30,0x34,0x38,0x20,0x69,0x6E,0x63,0x6F,0x72,0x70,0x2E,0x20,0x62,0x79, +0x20,0x72,0x65,0x66,0x2E,0x20,0x28,0x6C,0x69,0x6D,0x69,0x74,0x73,0x20,0x6C,0x69, +0x61,0x62,0x2E,0x29,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x0B,0x13,0x1C,0x28, +0x63,0x29,0x20,0x31,0x39,0x39,0x39,0x20,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E, +0x6E,0x65,0x74,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x33,0x30,0x31,0x06, +0x03,0x55,0x04,0x03,0x13,0x2A,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E,0x65, +0x74,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20, +0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x20,0x28,0x32,0x30,0x34,0x38,0x29, +0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01, +0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01, +0x00,0xAD,0x4D,0x4B,0xA9,0x12,0x86,0xB2,0xEA,0xA3,0x20,0x07,0x15,0x16,0x64,0x2A, +0x2B,0x4B,0xD1,0xBF,0x0B,0x4A,0x4D,0x8E,0xED,0x80,0x76,0xA5,0x67,0xB7,0x78,0x40, +0xC0,0x73,0x42,0xC8,0x68,0xC0,0xDB,0x53,0x2B,0xDD,0x5E,0xB8,0x76,0x98,0x35,0x93, +0x8B,0x1A,0x9D,0x7C,0x13,0x3A,0x0E,0x1F,0x5B,0xB7,0x1E,0xCF,0xE5,0x24,0x14,0x1E, +0xB1,0x81,0xA9,0x8D,0x7D,0xB8,0xCC,0x6B,0x4B,0x03,0xF1,0x02,0x0C,0xDC,0xAB,0xA5, +0x40,0x24,0x00,0x7F,0x74,0x94,0xA1,0x9D,0x08,0x29,0xB3,0x88,0x0B,0xF5,0x87,0x77, +0x9D,0x55,0xCD,0xE4,0xC3,0x7E,0xD7,0x6A,0x64,0xAB,0x85,0x14,0x86,0x95,0x5B,0x97, +0x32,0x50,0x6F,0x3D,0xC8,0xBA,0x66,0x0C,0xE3,0xFC,0xBD,0xB8,0x49,0xC1,0x76,0x89, +0x49,0x19,0xFD,0xC0,0xA8,0xBD,0x89,0xA3,0x67,0x2F,0xC6,0x9F,0xBC,0x71,0x19,0x60, +0xB8,0x2D,0xE9,0x2C,0xC9,0x90,0x76,0x66,0x7B,0x94,0xE2,0xAF,0x78,0xD6,0x65,0x53, +0x5D,0x3C,0xD6,0x9C,0xB2,0xCF,0x29,0x03,0xF9,0x2F,0xA4,0x50,0xB2,0xD4,0x48,0xCE, +0x05,0x32,0x55,0x8A,0xFD,0xB2,0x64,0x4C,0x0E,0xE4,0x98,0x07,0x75,0xDB,0x7F,0xDF, +0xB9,0x08,0x55,0x60,0x85,0x30,0x29,0xF9,0x7B,0x48,0xA4,0x69,0x86,0xE3,0x35,0x3F, +0x1E,0x86,0x5D,0x7A,0x7A,0x15,0xBD,0xEF,0x00,0x8E,0x15,0x22,0x54,0x17,0x00,0x90, +0x26,0x93,0xBC,0x0E,0x49,0x68,0x91,0xBF,0xF8,0x47,0xD3,0x9D,0x95,0x42,0xC1,0x0E, +0x4D,0xDF,0x6F,0x26,0xCF,0xC3,0x18,0x21,0x62,0x66,0x43,0x70,0xD6,0xD5,0xC0,0x07, +0xE1,0x02,0x03,0x01,0x00,0x01,0xA3,0x74,0x30,0x72,0x30,0x11,0x06,0x09,0x60,0x86, +0x48,0x01,0x86,0xF8,0x42,0x01,0x01,0x04,0x04,0x03,0x02,0x00,0x07,0x30,0x1F,0x06, +0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0x55,0xE4,0x81,0xD1,0x11,0x80, +0xBE,0xD8,0x89,0xB9,0x08,0xA3,0x31,0xF9,0xA1,0x24,0x09,0x16,0xB9,0x70,0x30,0x1D, +0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x55,0xE4,0x81,0xD1,0x11,0x80,0xBE, +0xD8,0x89,0xB9,0x08,0xA3,0x31,0xF9,0xA1,0x24,0x09,0x16,0xB9,0x70,0x30,0x1D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF6,0x7D,0x07,0x41,0x00,0x04,0x10,0x30,0x0E,0x1B,0x08, +0x56,0x35,0x2E,0x30,0x3A,0x34,0x2E,0x30,0x03,0x02,0x04,0x90,0x30,0x0D,0x06,0x09, +0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00, +0x59,0x47,0xAC,0x21,0x84,0x8A,0x17,0xC9,0x9C,0x89,0x53,0x1E,0xBA,0x80,0x85,0x1A, +0xC6,0x3C,0x4E,0x3E,0xB1,0x9C,0xB6,0x7C,0xC6,0x92,0x5D,0x18,0x64,0x02,0xE3,0xD3, +0x06,0x08,0x11,0x61,0x7C,0x63,0xE3,0x2B,0x9D,0x31,0x03,0x70,0x76,0xD2,0xA3,0x28, +0xA0,0xF4,0xBB,0x9A,0x63,0x73,0xED,0x6D,0xE5,0x2A,0xDB,0xED,0x14,0xA9,0x2B,0xC6, +0x36,0x11,0xD0,0x2B,0xEB,0x07,0x8B,0xA5,0xDA,0x9E,0x5C,0x19,0x9D,0x56,0x12,0xF5, +0x54,0x29,0xC8,0x05,0xED,0xB2,0x12,0x2A,0x8D,0xF4,0x03,0x1B,0xFF,0xE7,0x92,0x10, +0x87,0xB0,0x3A,0xB5,0xC3,0x9D,0x05,0x37,0x12,0xA3,0xC7,0xF4,0x15,0xB9,0xD5,0xA4, +0x39,0x16,0x9B,0x53,0x3A,0x23,0x91,0xF1,0xA8,0x82,0xA2,0x6A,0x88,0x68,0xC1,0x79, +0x02,0x22,0xBC,0xAA,0xA6,0xD6,0xAE,0xDF,0xB0,0x14,0x5F,0xB8,0x87,0xD0,0xDD,0x7C, +0x7F,0x7B,0xFF,0xAF,0x1C,0xCF,0xE6,0xDB,0x07,0xAD,0x5E,0xDB,0x85,0x9D,0xD0,0x2B, +0x0D,0x33,0xDB,0x04,0xD1,0xE6,0x49,0x40,0x13,0x2B,0x76,0xFB,0x3E,0xE9,0x9C,0x89, +0x0F,0x15,0xCE,0x18,0xB0,0x85,0x78,0x21,0x4F,0x6B,0x4F,0x0E,0xFA,0x36,0x67,0xCD, +0x07,0xF2,0xFF,0x08,0xD0,0xE2,0xDE,0xD9,0xBF,0x2A,0xAF,0xB8,0x87,0x86,0x21,0x3C, +0x04,0xCA,0xB7,0x94,0x68,0x7F,0xCF,0x3C,0xE9,0x98,0xD7,0x38,0xFF,0xEC,0xC0,0xD9, +0x50,0xF0,0x2E,0x4B,0x58,0xAE,0x46,0x6F,0xD0,0x2E,0xC3,0x60,0xDA,0x72,0x55,0x72, +0xBD,0x4C,0x45,0x9E,0x61,0xBA,0xBF,0x84,0x81,0x92,0x03,0xD1,0xD2,0x69,0x7C,0xC5, +}; + + +/* subject:/C=US/O=Entrust.net/OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/OU=(c) 1999 Entrust.net Limited/CN=Entrust.net Secure Server Certification Authority */ +/* issuer :/C=US/O=Entrust.net/OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/OU=(c) 1999 Entrust.net Limited/CN=Entrust.net Secure Server Certification Authority */ + + +const unsigned char Entrust_net_Secure_Server_CA_certificate[1244]={ +0x30,0x82,0x04,0xD8,0x30,0x82,0x04,0x41,0xA0,0x03,0x02,0x01,0x02,0x02,0x04,0x37, +0x4A,0xD2,0x43,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05, +0x05,0x00,0x30,0x81,0xC3,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02, +0x55,0x53,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x13,0x0B,0x45,0x6E,0x74, +0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x31,0x3B,0x30,0x39,0x06,0x03,0x55,0x04, +0x0B,0x13,0x32,0x77,0x77,0x77,0x2E,0x65,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E, +0x65,0x74,0x2F,0x43,0x50,0x53,0x20,0x69,0x6E,0x63,0x6F,0x72,0x70,0x2E,0x20,0x62, +0x79,0x20,0x72,0x65,0x66,0x2E,0x20,0x28,0x6C,0x69,0x6D,0x69,0x74,0x73,0x20,0x6C, +0x69,0x61,0x62,0x2E,0x29,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x0B,0x13,0x1C, +0x28,0x63,0x29,0x20,0x31,0x39,0x39,0x39,0x20,0x45,0x6E,0x74,0x72,0x75,0x73,0x74, +0x2E,0x6E,0x65,0x74,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x3A,0x30,0x38, +0x06,0x03,0x55,0x04,0x03,0x13,0x31,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E, +0x65,0x74,0x20,0x53,0x65,0x63,0x75,0x72,0x65,0x20,0x53,0x65,0x72,0x76,0x65,0x72, +0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41, +0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x1E,0x17,0x0D,0x39,0x39,0x30,0x35, +0x32,0x35,0x31,0x36,0x30,0x39,0x34,0x30,0x5A,0x17,0x0D,0x31,0x39,0x30,0x35,0x32, +0x35,0x31,0x36,0x33,0x39,0x34,0x30,0x5A,0x30,0x81,0xC3,0x31,0x0B,0x30,0x09,0x06, +0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04, +0x0A,0x13,0x0B,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x31,0x3B, +0x30,0x39,0x06,0x03,0x55,0x04,0x0B,0x13,0x32,0x77,0x77,0x77,0x2E,0x65,0x6E,0x74, +0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x2F,0x43,0x50,0x53,0x20,0x69,0x6E,0x63, +0x6F,0x72,0x70,0x2E,0x20,0x62,0x79,0x20,0x72,0x65,0x66,0x2E,0x20,0x28,0x6C,0x69, +0x6D,0x69,0x74,0x73,0x20,0x6C,0x69,0x61,0x62,0x2E,0x29,0x31,0x25,0x30,0x23,0x06, +0x03,0x55,0x04,0x0B,0x13,0x1C,0x28,0x63,0x29,0x20,0x31,0x39,0x39,0x39,0x20,0x45, +0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x20,0x4C,0x69,0x6D,0x69,0x74, +0x65,0x64,0x31,0x3A,0x30,0x38,0x06,0x03,0x55,0x04,0x03,0x13,0x31,0x45,0x6E,0x74, +0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x20,0x53,0x65,0x63,0x75,0x72,0x65,0x20, +0x53,0x65,0x72,0x76,0x65,0x72,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61, +0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x81, +0x9D,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00, +0x03,0x81,0x8B,0x00,0x30,0x81,0x87,0x02,0x81,0x81,0x00,0xCD,0x28,0x83,0x34,0x54, +0x1B,0x89,0xF3,0x0F,0xAF,0x37,0x91,0x31,0xFF,0xAF,0x31,0x60,0xC9,0xA8,0xE8,0xB2, +0x10,0x68,0xED,0x9F,0xE7,0x93,0x36,0xF1,0x0A,0x64,0xBB,0x47,0xF5,0x04,0x17,0x3F, +0x23,0x47,0x4D,0xC5,0x27,0x19,0x81,0x26,0x0C,0x54,0x72,0x0D,0x88,0x2D,0xD9,0x1F, +0x9A,0x12,0x9F,0xBC,0xB3,0x71,0xD3,0x80,0x19,0x3F,0x47,0x66,0x7B,0x8C,0x35,0x28, +0xD2,0xB9,0x0A,0xDF,0x24,0xDA,0x9C,0xD6,0x50,0x79,0x81,0x7A,0x5A,0xD3,0x37,0xF7, +0xC2,0x4A,0xD8,0x29,0x92,0x26,0x64,0xD1,0xE4,0x98,0x6C,0x3A,0x00,0x8A,0xF5,0x34, +0x9B,0x65,0xF8,0xED,0xE3,0x10,0xFF,0xFD,0xB8,0x49,0x58,0xDC,0xA0,0xDE,0x82,0x39, +0x6B,0x81,0xB1,0x16,0x19,0x61,0xB9,0x54,0xB6,0xE6,0x43,0x02,0x01,0x03,0xA3,0x82, +0x01,0xD7,0x30,0x82,0x01,0xD3,0x30,0x11,0x06,0x09,0x60,0x86,0x48,0x01,0x86,0xF8, +0x42,0x01,0x01,0x04,0x04,0x03,0x02,0x00,0x07,0x30,0x82,0x01,0x19,0x06,0x03,0x55, +0x1D,0x1F,0x04,0x82,0x01,0x10,0x30,0x82,0x01,0x0C,0x30,0x81,0xDE,0xA0,0x81,0xDB, +0xA0,0x81,0xD8,0xA4,0x81,0xD5,0x30,0x81,0xD2,0x31,0x0B,0x30,0x09,0x06,0x03,0x55, +0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x13, +0x0B,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x31,0x3B,0x30,0x39, +0x06,0x03,0x55,0x04,0x0B,0x13,0x32,0x77,0x77,0x77,0x2E,0x65,0x6E,0x74,0x72,0x75, +0x73,0x74,0x2E,0x6E,0x65,0x74,0x2F,0x43,0x50,0x53,0x20,0x69,0x6E,0x63,0x6F,0x72, +0x70,0x2E,0x20,0x62,0x79,0x20,0x72,0x65,0x66,0x2E,0x20,0x28,0x6C,0x69,0x6D,0x69, +0x74,0x73,0x20,0x6C,0x69,0x61,0x62,0x2E,0x29,0x31,0x25,0x30,0x23,0x06,0x03,0x55, +0x04,0x0B,0x13,0x1C,0x28,0x63,0x29,0x20,0x31,0x39,0x39,0x39,0x20,0x45,0x6E,0x74, +0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64, +0x31,0x3A,0x30,0x38,0x06,0x03,0x55,0x04,0x03,0x13,0x31,0x45,0x6E,0x74,0x72,0x75, +0x73,0x74,0x2E,0x6E,0x65,0x74,0x20,0x53,0x65,0x63,0x75,0x72,0x65,0x20,0x53,0x65, +0x72,0x76,0x65,0x72,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69, +0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x31,0x0D,0x30,0x0B, +0x06,0x03,0x55,0x04,0x03,0x13,0x04,0x43,0x52,0x4C,0x31,0x30,0x29,0xA0,0x27,0xA0, +0x25,0x86,0x23,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x77,0x77,0x77,0x2E,0x65,0x6E, +0x74,0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x2F,0x43,0x52,0x4C,0x2F,0x6E,0x65, +0x74,0x31,0x2E,0x63,0x72,0x6C,0x30,0x2B,0x06,0x03,0x55,0x1D,0x10,0x04,0x24,0x30, +0x22,0x80,0x0F,0x31,0x39,0x39,0x39,0x30,0x35,0x32,0x35,0x31,0x36,0x30,0x39,0x34, +0x30,0x5A,0x81,0x0F,0x32,0x30,0x31,0x39,0x30,0x35,0x32,0x35,0x31,0x36,0x30,0x39, +0x34,0x30,0x5A,0x30,0x0B,0x06,0x03,0x55,0x1D,0x0F,0x04,0x04,0x03,0x02,0x01,0x06, +0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0xF0,0x17,0x62, +0x13,0x55,0x3D,0xB3,0xFF,0x0A,0x00,0x6B,0xFB,0x50,0x84,0x97,0xF3,0xED,0x62,0xD0, +0x1A,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xF0,0x17,0x62,0x13, +0x55,0x3D,0xB3,0xFF,0x0A,0x00,0x6B,0xFB,0x50,0x84,0x97,0xF3,0xED,0x62,0xD0,0x1A, +0x30,0x0C,0x06,0x03,0x55,0x1D,0x13,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x19, +0x06,0x09,0x2A,0x86,0x48,0x86,0xF6,0x7D,0x07,0x41,0x00,0x04,0x0C,0x30,0x0A,0x1B, +0x04,0x56,0x34,0x2E,0x30,0x03,0x02,0x04,0x90,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48, +0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x81,0x81,0x00,0x90,0xDC,0x30,0x02, +0xFA,0x64,0x74,0xC2,0xA7,0x0A,0xA5,0x7C,0x21,0x8D,0x34,0x17,0xA8,0xFB,0x47,0x0E, +0xFF,0x25,0x7C,0x8D,0x13,0x0A,0xFB,0xE4,0x98,0xB5,0xEF,0x8C,0xF8,0xC5,0x10,0x0D, +0xF7,0x92,0xBE,0xF1,0xC3,0xD5,0xD5,0x95,0x6A,0x04,0xBB,0x2C,0xCE,0x26,0x36,0x65, +0xC8,0x31,0xC6,0xE7,0xEE,0x3F,0xE3,0x57,0x75,0x84,0x7A,0x11,0xEF,0x46,0x4F,0x18, +0xF4,0xD3,0x98,0xBB,0xA8,0x87,0x32,0xBA,0x72,0xF6,0x3C,0xE2,0x3D,0x9F,0xD7,0x1D, +0xD9,0xC3,0x60,0x43,0x8C,0x58,0x0E,0x22,0x96,0x2F,0x62,0xA3,0x2C,0x1F,0xBA,0xAD, +0x05,0xEF,0xAB,0x32,0x78,0x87,0xA0,0x54,0x73,0x19,0xB5,0x5C,0x05,0xF9,0x52,0x3E, +0x6D,0x2D,0x45,0x0B,0xF7,0x0A,0x93,0xEA,0xED,0x06,0xF9,0xB2, +}; + + +/* subject:/C=US/O=Entrust, Inc./OU=www.entrust.net/CPS is incorporated by reference/OU=(c) 2006 Entrust, Inc./CN=Entrust Root Certification Authority */ +/* issuer :/C=US/O=Entrust, Inc./OU=www.entrust.net/CPS is incorporated by reference/OU=(c) 2006 Entrust, Inc./CN=Entrust Root Certification Authority */ + + +const unsigned char Entrust_Root_Certification_Authority_certificate[1173]={ +0x30,0x82,0x04,0x91,0x30,0x82,0x03,0x79,0xA0,0x03,0x02,0x01,0x02,0x02,0x04,0x45, +0x6B,0x50,0x54,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05, +0x05,0x00,0x30,0x81,0xB0,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02, +0x55,0x53,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D,0x45,0x6E,0x74, +0x72,0x75,0x73,0x74,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x39,0x30,0x37,0x06,0x03, +0x55,0x04,0x0B,0x13,0x30,0x77,0x77,0x77,0x2E,0x65,0x6E,0x74,0x72,0x75,0x73,0x74, +0x2E,0x6E,0x65,0x74,0x2F,0x43,0x50,0x53,0x20,0x69,0x73,0x20,0x69,0x6E,0x63,0x6F, +0x72,0x70,0x6F,0x72,0x61,0x74,0x65,0x64,0x20,0x62,0x79,0x20,0x72,0x65,0x66,0x65, +0x72,0x65,0x6E,0x63,0x65,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x0B,0x13,0x16, +0x28,0x63,0x29,0x20,0x32,0x30,0x30,0x36,0x20,0x45,0x6E,0x74,0x72,0x75,0x73,0x74, +0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x2D,0x30,0x2B,0x06,0x03,0x55,0x04,0x03,0x13, +0x24,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x65, +0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68, +0x6F,0x72,0x69,0x74,0x79,0x30,0x1E,0x17,0x0D,0x30,0x36,0x31,0x31,0x32,0x37,0x32, +0x30,0x32,0x33,0x34,0x32,0x5A,0x17,0x0D,0x32,0x36,0x31,0x31,0x32,0x37,0x32,0x30, +0x35,0x33,0x34,0x32,0x5A,0x30,0x81,0xB0,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04, +0x06,0x13,0x02,0x55,0x53,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D, +0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x39,0x30, +0x37,0x06,0x03,0x55,0x04,0x0B,0x13,0x30,0x77,0x77,0x77,0x2E,0x65,0x6E,0x74,0x72, +0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x2F,0x43,0x50,0x53,0x20,0x69,0x73,0x20,0x69, +0x6E,0x63,0x6F,0x72,0x70,0x6F,0x72,0x61,0x74,0x65,0x64,0x20,0x62,0x79,0x20,0x72, +0x65,0x66,0x65,0x72,0x65,0x6E,0x63,0x65,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04, +0x0B,0x13,0x16,0x28,0x63,0x29,0x20,0x32,0x30,0x30,0x36,0x20,0x45,0x6E,0x74,0x72, +0x75,0x73,0x74,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x2D,0x30,0x2B,0x06,0x03,0x55, +0x04,0x03,0x13,0x24,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x20,0x52,0x6F,0x6F,0x74, +0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41, +0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09, +0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00, +0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xB6,0x95,0xB6,0x43,0x42,0xFA,0xC6, +0x6D,0x2A,0x6F,0x48,0xDF,0x94,0x4C,0x39,0x57,0x05,0xEE,0xC3,0x79,0x11,0x41,0x68, +0x36,0xED,0xEC,0xFE,0x9A,0x01,0x8F,0xA1,0x38,0x28,0xFC,0xF7,0x10,0x46,0x66,0x2E, +0x4D,0x1E,0x1A,0xB1,0x1A,0x4E,0xC6,0xD1,0xC0,0x95,0x88,0xB0,0xC9,0xFF,0x31,0x8B, +0x33,0x03,0xDB,0xB7,0x83,0x7B,0x3E,0x20,0x84,0x5E,0xED,0xB2,0x56,0x28,0xA7,0xF8, +0xE0,0xB9,0x40,0x71,0x37,0xC5,0xCB,0x47,0x0E,0x97,0x2A,0x68,0xC0,0x22,0x95,0x62, +0x15,0xDB,0x47,0xD9,0xF5,0xD0,0x2B,0xFF,0x82,0x4B,0xC9,0xAD,0x3E,0xDE,0x4C,0xDB, +0x90,0x80,0x50,0x3F,0x09,0x8A,0x84,0x00,0xEC,0x30,0x0A,0x3D,0x18,0xCD,0xFB,0xFD, +0x2A,0x59,0x9A,0x23,0x95,0x17,0x2C,0x45,0x9E,0x1F,0x6E,0x43,0x79,0x6D,0x0C,0x5C, +0x98,0xFE,0x48,0xA7,0xC5,0x23,0x47,0x5C,0x5E,0xFD,0x6E,0xE7,0x1E,0xB4,0xF6,0x68, +0x45,0xD1,0x86,0x83,0x5B,0xA2,0x8A,0x8D,0xB1,0xE3,0x29,0x80,0xFE,0x25,0x71,0x88, +0xAD,0xBE,0xBC,0x8F,0xAC,0x52,0x96,0x4B,0xAA,0x51,0x8D,0xE4,0x13,0x31,0x19,0xE8, +0x4E,0x4D,0x9F,0xDB,0xAC,0xB3,0x6A,0xD5,0xBC,0x39,0x54,0x71,0xCA,0x7A,0x7A,0x7F, +0x90,0xDD,0x7D,0x1D,0x80,0xD9,0x81,0xBB,0x59,0x26,0xC2,0x11,0xFE,0xE6,0x93,0xE2, +0xF7,0x80,0xE4,0x65,0xFB,0x34,0x37,0x0E,0x29,0x80,0x70,0x4D,0xAF,0x38,0x86,0x2E, +0x9E,0x7F,0x57,0xAF,0x9E,0x17,0xAE,0xEB,0x1C,0xCB,0x28,0x21,0x5F,0xB6,0x1C,0xD8, +0xE7,0xA2,0x04,0x22,0xF9,0xD3,0xDA,0xD8,0xCB,0x02,0x03,0x01,0x00,0x01,0xA3,0x81, +0xB0,0x30,0x81,0xAD,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04, +0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05, +0x30,0x03,0x01,0x01,0xFF,0x30,0x2B,0x06,0x03,0x55,0x1D,0x10,0x04,0x24,0x30,0x22, +0x80,0x0F,0x32,0x30,0x30,0x36,0x31,0x31,0x32,0x37,0x32,0x30,0x32,0x33,0x34,0x32, +0x5A,0x81,0x0F,0x32,0x30,0x32,0x36,0x31,0x31,0x32,0x37,0x32,0x30,0x35,0x33,0x34, +0x32,0x5A,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0x68, +0x90,0xE4,0x67,0xA4,0xA6,0x53,0x80,0xC7,0x86,0x66,0xA4,0xF1,0xF7,0x4B,0x43,0xFB, +0x84,0xBD,0x6D,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x68,0x90, +0xE4,0x67,0xA4,0xA6,0x53,0x80,0xC7,0x86,0x66,0xA4,0xF1,0xF7,0x4B,0x43,0xFB,0x84, +0xBD,0x6D,0x30,0x1D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF6,0x7D,0x07,0x41,0x00,0x04, +0x10,0x30,0x0E,0x1B,0x08,0x56,0x37,0x2E,0x31,0x3A,0x34,0x2E,0x30,0x03,0x02,0x04, +0x90,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00, +0x03,0x82,0x01,0x01,0x00,0x93,0xD4,0x30,0xB0,0xD7,0x03,0x20,0x2A,0xD0,0xF9,0x63, +0xE8,0x91,0x0C,0x05,0x20,0xA9,0x5F,0x19,0xCA,0x7B,0x72,0x4E,0xD4,0xB1,0xDB,0xD0, +0x96,0xFB,0x54,0x5A,0x19,0x2C,0x0C,0x08,0xF7,0xB2,0xBC,0x85,0xA8,0x9D,0x7F,0x6D, +0x3B,0x52,0xB3,0x2A,0xDB,0xE7,0xD4,0x84,0x8C,0x63,0xF6,0x0F,0xCB,0x26,0x01,0x91, +0x50,0x6C,0xF4,0x5F,0x14,0xE2,0x93,0x74,0xC0,0x13,0x9E,0x30,0x3A,0x50,0xE3,0xB4, +0x60,0xC5,0x1C,0xF0,0x22,0x44,0x8D,0x71,0x47,0xAC,0xC8,0x1A,0xC9,0xE9,0x9B,0x9A, +0x00,0x60,0x13,0xFF,0x70,0x7E,0x5F,0x11,0x4D,0x49,0x1B,0xB3,0x15,0x52,0x7B,0xC9, +0x54,0xDA,0xBF,0x9D,0x95,0xAF,0x6B,0x9A,0xD8,0x9E,0xE9,0xF1,0xE4,0x43,0x8D,0xE2, +0x11,0x44,0x3A,0xBF,0xAF,0xBD,0x83,0x42,0x73,0x52,0x8B,0xAA,0xBB,0xA7,0x29,0xCF, +0xF5,0x64,0x1C,0x0A,0x4D,0xD1,0xBC,0xAA,0xAC,0x9F,0x2A,0xD0,0xFF,0x7F,0x7F,0xDA, +0x7D,0xEA,0xB1,0xED,0x30,0x25,0xC1,0x84,0xDA,0x34,0xD2,0x5B,0x78,0x83,0x56,0xEC, +0x9C,0x36,0xC3,0x26,0xE2,0x11,0xF6,0x67,0x49,0x1D,0x92,0xAB,0x8C,0xFB,0xEB,0xFF, +0x7A,0xEE,0x85,0x4A,0xA7,0x50,0x80,0xF0,0xA7,0x5C,0x4A,0x94,0x2E,0x5F,0x05,0x99, +0x3C,0x52,0x41,0xE0,0xCD,0xB4,0x63,0xCF,0x01,0x43,0xBA,0x9C,0x83,0xDC,0x8F,0x60, +0x3B,0xF3,0x5A,0xB4,0xB4,0x7B,0xAE,0xDA,0x0B,0x90,0x38,0x75,0xEF,0x81,0x1D,0x66, +0xD2,0xF7,0x57,0x70,0x36,0xB3,0xBF,0xFC,0x28,0xAF,0x71,0x25,0x85,0x5B,0x13,0xFE, +0x1E,0x7F,0x5A,0xB4,0x3C, +}; + + +/* subject:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority */ +/* issuer :/C=US/O=Equifax/OU=Equifax Secure Certificate Authority */ + + +const unsigned char Equifax_Secure_CA_certificate[804]={ +0x30,0x82,0x03,0x20,0x30,0x82,0x02,0x89,0xA0,0x03,0x02,0x01,0x02,0x02,0x04,0x35, +0xDE,0xF4,0xCF,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05, +0x05,0x00,0x30,0x4E,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55, +0x53,0x31,0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x0A,0x13,0x07,0x45,0x71,0x75,0x69, +0x66,0x61,0x78,0x31,0x2D,0x30,0x2B,0x06,0x03,0x55,0x04,0x0B,0x13,0x24,0x45,0x71, +0x75,0x69,0x66,0x61,0x78,0x20,0x53,0x65,0x63,0x75,0x72,0x65,0x20,0x43,0x65,0x72, +0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x65,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69, +0x74,0x79,0x30,0x1E,0x17,0x0D,0x39,0x38,0x30,0x38,0x32,0x32,0x31,0x36,0x34,0x31, +0x35,0x31,0x5A,0x17,0x0D,0x31,0x38,0x30,0x38,0x32,0x32,0x31,0x36,0x34,0x31,0x35, +0x31,0x5A,0x30,0x4E,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55, +0x53,0x31,0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x0A,0x13,0x07,0x45,0x71,0x75,0x69, +0x66,0x61,0x78,0x31,0x2D,0x30,0x2B,0x06,0x03,0x55,0x04,0x0B,0x13,0x24,0x45,0x71, +0x75,0x69,0x66,0x61,0x78,0x20,0x53,0x65,0x63,0x75,0x72,0x65,0x20,0x43,0x65,0x72, +0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x65,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69, +0x74,0x79,0x30,0x81,0x9F,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01, +0x01,0x01,0x05,0x00,0x03,0x81,0x8D,0x00,0x30,0x81,0x89,0x02,0x81,0x81,0x00,0xC1, +0x5D,0xB1,0x58,0x67,0x08,0x62,0xEE,0xA0,0x9A,0x2D,0x1F,0x08,0x6D,0x91,0x14,0x68, +0x98,0x0A,0x1E,0xFE,0xDA,0x04,0x6F,0x13,0x84,0x62,0x21,0xC3,0xD1,0x7C,0xCE,0x9F, +0x05,0xE0,0xB8,0x01,0xF0,0x4E,0x34,0xEC,0xE2,0x8A,0x95,0x04,0x64,0xAC,0xF1,0x6B, +0x53,0x5F,0x05,0xB3,0xCB,0x67,0x80,0xBF,0x42,0x02,0x8E,0xFE,0xDD,0x01,0x09,0xEC, +0xE1,0x00,0x14,0x4F,0xFC,0xFB,0xF0,0x0C,0xDD,0x43,0xBA,0x5B,0x2B,0xE1,0x1F,0x80, +0x70,0x99,0x15,0x57,0x93,0x16,0xF1,0x0F,0x97,0x6A,0xB7,0xC2,0x68,0x23,0x1C,0xCC, +0x4D,0x59,0x30,0xAC,0x51,0x1E,0x3B,0xAF,0x2B,0xD6,0xEE,0x63,0x45,0x7B,0xC5,0xD9, +0x5F,0x50,0xD2,0xE3,0x50,0x0F,0x3A,0x88,0xE7,0xBF,0x14,0xFD,0xE0,0xC7,0xB9,0x02, +0x03,0x01,0x00,0x01,0xA3,0x82,0x01,0x09,0x30,0x82,0x01,0x05,0x30,0x70,0x06,0x03, +0x55,0x1D,0x1F,0x04,0x69,0x30,0x67,0x30,0x65,0xA0,0x63,0xA0,0x61,0xA4,0x5F,0x30, +0x5D,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x10, +0x30,0x0E,0x06,0x03,0x55,0x04,0x0A,0x13,0x07,0x45,0x71,0x75,0x69,0x66,0x61,0x78, +0x31,0x2D,0x30,0x2B,0x06,0x03,0x55,0x04,0x0B,0x13,0x24,0x45,0x71,0x75,0x69,0x66, +0x61,0x78,0x20,0x53,0x65,0x63,0x75,0x72,0x65,0x20,0x43,0x65,0x72,0x74,0x69,0x66, +0x69,0x63,0x61,0x74,0x65,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x31, +0x0D,0x30,0x0B,0x06,0x03,0x55,0x04,0x03,0x13,0x04,0x43,0x52,0x4C,0x31,0x30,0x1A, +0x06,0x03,0x55,0x1D,0x10,0x04,0x13,0x30,0x11,0x81,0x0F,0x32,0x30,0x31,0x38,0x30, +0x38,0x32,0x32,0x31,0x36,0x34,0x31,0x35,0x31,0x5A,0x30,0x0B,0x06,0x03,0x55,0x1D, +0x0F,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18, +0x30,0x16,0x80,0x14,0x48,0xE6,0x68,0xF9,0x2B,0xD2,0xB2,0x95,0xD7,0x47,0xD8,0x23, +0x20,0x10,0x4F,0x33,0x98,0x90,0x9F,0xD4,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04, +0x16,0x04,0x14,0x48,0xE6,0x68,0xF9,0x2B,0xD2,0xB2,0x95,0xD7,0x47,0xD8,0x23,0x20, +0x10,0x4F,0x33,0x98,0x90,0x9F,0xD4,0x30,0x0C,0x06,0x03,0x55,0x1D,0x13,0x04,0x05, +0x30,0x03,0x01,0x01,0xFF,0x30,0x1A,0x06,0x09,0x2A,0x86,0x48,0x86,0xF6,0x7D,0x07, +0x41,0x00,0x04,0x0D,0x30,0x0B,0x1B,0x05,0x56,0x33,0x2E,0x30,0x63,0x03,0x02,0x06, +0xC0,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00, +0x03,0x81,0x81,0x00,0x58,0xCE,0x29,0xEA,0xFC,0xF7,0xDE,0xB5,0xCE,0x02,0xB9,0x17, +0xB5,0x85,0xD1,0xB9,0xE3,0xE0,0x95,0xCC,0x25,0x31,0x0D,0x00,0xA6,0x92,0x6E,0x7F, +0xB6,0x92,0x63,0x9E,0x50,0x95,0xD1,0x9A,0x6F,0xE4,0x11,0xDE,0x63,0x85,0x6E,0x98, +0xEE,0xA8,0xFF,0x5A,0xC8,0xD3,0x55,0xB2,0x66,0x71,0x57,0xDE,0xC0,0x21,0xEB,0x3D, +0x2A,0xA7,0x23,0x49,0x01,0x04,0x86,0x42,0x7B,0xFC,0xEE,0x7F,0xA2,0x16,0x52,0xB5, +0x67,0x67,0xD3,0x40,0xDB,0x3B,0x26,0x58,0xB2,0x28,0x77,0x3D,0xAE,0x14,0x77,0x61, +0xD6,0xFA,0x2A,0x66,0x27,0xA0,0x0D,0xFA,0xA7,0x73,0x5C,0xEA,0x70,0xF1,0x94,0x21, +0x65,0x44,0x5F,0xFA,0xFC,0xEF,0x29,0x68,0xA9,0xA2,0x87,0x79,0xEF,0x79,0xEF,0x4F, +0xAC,0x07,0x77,0x38, +}; + + +/* subject:/C=US/O=Equifax Secure Inc./CN=Equifax Secure eBusiness CA-1 */ +/* issuer :/C=US/O=Equifax Secure Inc./CN=Equifax Secure eBusiness CA-1 */ + + +const unsigned char Equifax_Secure_eBusiness_CA_1_certificate[646]={ +0x30,0x82,0x02,0x82,0x30,0x82,0x01,0xEB,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x04, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x04,0x05,0x00,0x30, +0x53,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x1C, +0x30,0x1A,0x06,0x03,0x55,0x04,0x0A,0x13,0x13,0x45,0x71,0x75,0x69,0x66,0x61,0x78, +0x20,0x53,0x65,0x63,0x75,0x72,0x65,0x20,0x49,0x6E,0x63,0x2E,0x31,0x26,0x30,0x24, +0x06,0x03,0x55,0x04,0x03,0x13,0x1D,0x45,0x71,0x75,0x69,0x66,0x61,0x78,0x20,0x53, +0x65,0x63,0x75,0x72,0x65,0x20,0x65,0x42,0x75,0x73,0x69,0x6E,0x65,0x73,0x73,0x20, +0x43,0x41,0x2D,0x31,0x30,0x1E,0x17,0x0D,0x39,0x39,0x30,0x36,0x32,0x31,0x30,0x34, +0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,0x30,0x30,0x36,0x32,0x31,0x30,0x34,0x30, +0x30,0x30,0x30,0x5A,0x30,0x53,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13, +0x02,0x55,0x53,0x31,0x1C,0x30,0x1A,0x06,0x03,0x55,0x04,0x0A,0x13,0x13,0x45,0x71, +0x75,0x69,0x66,0x61,0x78,0x20,0x53,0x65,0x63,0x75,0x72,0x65,0x20,0x49,0x6E,0x63, +0x2E,0x31,0x26,0x30,0x24,0x06,0x03,0x55,0x04,0x03,0x13,0x1D,0x45,0x71,0x75,0x69, +0x66,0x61,0x78,0x20,0x53,0x65,0x63,0x75,0x72,0x65,0x20,0x65,0x42,0x75,0x73,0x69, +0x6E,0x65,0x73,0x73,0x20,0x43,0x41,0x2D,0x31,0x30,0x81,0x9F,0x30,0x0D,0x06,0x09, +0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x81,0x8D,0x00,0x30, +0x81,0x89,0x02,0x81,0x81,0x00,0xCE,0x2F,0x19,0xBC,0x17,0xB7,0x77,0xDE,0x93,0xA9, +0x5F,0x5A,0x0D,0x17,0x4F,0x34,0x1A,0x0C,0x98,0xF4,0x22,0xD9,0x59,0xD4,0xC4,0x68, +0x46,0xF0,0xB4,0x35,0xC5,0x85,0x03,0x20,0xC6,0xAF,0x45,0xA5,0x21,0x51,0x45,0x41, +0xEB,0x16,0x58,0x36,0x32,0x6F,0xE2,0x50,0x62,0x64,0xF9,0xFD,0x51,0x9C,0xAA,0x24, +0xD9,0xF4,0x9D,0x83,0x2A,0x87,0x0A,0x21,0xD3,0x12,0x38,0x34,0x6C,0x8D,0x00,0x6E, +0x5A,0xA0,0xD9,0x42,0xEE,0x1A,0x21,0x95,0xF9,0x52,0x4C,0x55,0x5A,0xC5,0x0F,0x38, +0x4F,0x46,0xFA,0x6D,0xF8,0x2E,0x35,0xD6,0x1D,0x7C,0xEB,0xE2,0xF0,0xB0,0x75,0x80, +0xC8,0xA9,0x13,0xAC,0xBE,0x88,0xEF,0x3A,0x6E,0xAB,0x5F,0x2A,0x38,0x62,0x02,0xB0, +0x12,0x7B,0xFE,0x8F,0xA6,0x03,0x02,0x03,0x01,0x00,0x01,0xA3,0x66,0x30,0x64,0x30, +0x11,0x06,0x09,0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x01,0x04,0x04,0x03,0x02, +0x00,0x07,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03, +0x01,0x01,0xFF,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14, +0x4A,0x78,0x32,0x52,0x11,0xDB,0x59,0x16,0x36,0x5E,0xDF,0xC1,0x14,0x36,0x40,0x6A, +0x47,0x7C,0x4C,0xA1,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x4A, +0x78,0x32,0x52,0x11,0xDB,0x59,0x16,0x36,0x5E,0xDF,0xC1,0x14,0x36,0x40,0x6A,0x47, +0x7C,0x4C,0xA1,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x04, +0x05,0x00,0x03,0x81,0x81,0x00,0x75,0x5B,0xA8,0x9B,0x03,0x11,0xE6,0xE9,0x56,0x4C, +0xCD,0xF9,0xA9,0x4C,0xC0,0x0D,0x9A,0xF3,0xCC,0x65,0x69,0xE6,0x25,0x76,0xCC,0x59, +0xB7,0xD6,0x54,0xC3,0x1D,0xCD,0x99,0xAC,0x19,0xDD,0xB4,0x85,0xD5,0xE0,0x3D,0xFC, +0x62,0x20,0xA7,0x84,0x4B,0x58,0x65,0xF1,0xE2,0xF9,0x95,0x21,0x3F,0xF5,0xD4,0x7E, +0x58,0x1E,0x47,0x87,0x54,0x3E,0x58,0xA1,0xB5,0xB5,0xF8,0x2A,0xEF,0x71,0xE7,0xBC, +0xC3,0xF6,0xB1,0x49,0x46,0xE2,0xD7,0xA0,0x6B,0xE5,0x56,0x7A,0x9A,0x27,0x98,0x7C, +0x46,0x62,0x14,0xE7,0xC9,0xFC,0x6E,0x03,0x12,0x79,0x80,0x38,0x1D,0x48,0x82,0x8D, +0xFC,0x17,0xFE,0x2A,0x96,0x2B,0xB5,0x62,0xA6,0xA6,0x3D,0xBD,0x7F,0x92,0x59,0xCD, +0x5A,0x2A,0x82,0xB2,0x37,0x79, +}; + + +/* subject:/C=US/O=Equifax Secure/OU=Equifax Secure eBusiness CA-2 */ +/* issuer :/C=US/O=Equifax Secure/OU=Equifax Secure eBusiness CA-2 */ + + +const unsigned char Equifax_Secure_eBusiness_CA_2_certificate[804]={ +0x30,0x82,0x03,0x20,0x30,0x82,0x02,0x89,0xA0,0x03,0x02,0x01,0x02,0x02,0x04,0x37, +0x70,0xCF,0xB5,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05, +0x05,0x00,0x30,0x4E,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55, +0x53,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x0A,0x13,0x0E,0x45,0x71,0x75,0x69, +0x66,0x61,0x78,0x20,0x53,0x65,0x63,0x75,0x72,0x65,0x31,0x26,0x30,0x24,0x06,0x03, +0x55,0x04,0x0B,0x13,0x1D,0x45,0x71,0x75,0x69,0x66,0x61,0x78,0x20,0x53,0x65,0x63, +0x75,0x72,0x65,0x20,0x65,0x42,0x75,0x73,0x69,0x6E,0x65,0x73,0x73,0x20,0x43,0x41, +0x2D,0x32,0x30,0x1E,0x17,0x0D,0x39,0x39,0x30,0x36,0x32,0x33,0x31,0x32,0x31,0x34, +0x34,0x35,0x5A,0x17,0x0D,0x31,0x39,0x30,0x36,0x32,0x33,0x31,0x32,0x31,0x34,0x34, +0x35,0x5A,0x30,0x4E,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55, +0x53,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x0A,0x13,0x0E,0x45,0x71,0x75,0x69, +0x66,0x61,0x78,0x20,0x53,0x65,0x63,0x75,0x72,0x65,0x31,0x26,0x30,0x24,0x06,0x03, +0x55,0x04,0x0B,0x13,0x1D,0x45,0x71,0x75,0x69,0x66,0x61,0x78,0x20,0x53,0x65,0x63, +0x75,0x72,0x65,0x20,0x65,0x42,0x75,0x73,0x69,0x6E,0x65,0x73,0x73,0x20,0x43,0x41, +0x2D,0x32,0x30,0x81,0x9F,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01, +0x01,0x01,0x05,0x00,0x03,0x81,0x8D,0x00,0x30,0x81,0x89,0x02,0x81,0x81,0x00,0xE4, +0x39,0x39,0x93,0x1E,0x52,0x06,0x1B,0x28,0x36,0xF8,0xB2,0xA3,0x29,0xC5,0xED,0x8E, +0xB2,0x11,0xBD,0xFE,0xEB,0xE7,0xB4,0x74,0xC2,0x8F,0xFF,0x05,0xE7,0xD9,0x9D,0x06, +0xBF,0x12,0xC8,0x3F,0x0E,0xF2,0xD6,0xD1,0x24,0xB2,0x11,0xDE,0xD1,0x73,0x09,0x8A, +0xD4,0xB1,0x2C,0x98,0x09,0x0D,0x1E,0x50,0x46,0xB2,0x83,0xA6,0x45,0x8D,0x62,0x68, +0xBB,0x85,0x1B,0x20,0x70,0x32,0xAA,0x40,0xCD,0xA6,0x96,0x5F,0xC4,0x71,0x37,0x3F, +0x04,0xF3,0xB7,0x41,0x24,0x39,0x07,0x1A,0x1E,0x2E,0x61,0x58,0xA0,0x12,0x0B,0xE5, +0xA5,0xDF,0xC5,0xAB,0xEA,0x37,0x71,0xCC,0x1C,0xC8,0x37,0x3A,0xB9,0x97,0x52,0xA7, +0xAC,0xC5,0x6A,0x24,0x94,0x4E,0x9C,0x7B,0xCF,0xC0,0x6A,0xD6,0xDF,0x21,0xBD,0x02, +0x03,0x01,0x00,0x01,0xA3,0x82,0x01,0x09,0x30,0x82,0x01,0x05,0x30,0x70,0x06,0x03, +0x55,0x1D,0x1F,0x04,0x69,0x30,0x67,0x30,0x65,0xA0,0x63,0xA0,0x61,0xA4,0x5F,0x30, +0x5D,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x17, +0x30,0x15,0x06,0x03,0x55,0x04,0x0A,0x13,0x0E,0x45,0x71,0x75,0x69,0x66,0x61,0x78, +0x20,0x53,0x65,0x63,0x75,0x72,0x65,0x31,0x26,0x30,0x24,0x06,0x03,0x55,0x04,0x0B, +0x13,0x1D,0x45,0x71,0x75,0x69,0x66,0x61,0x78,0x20,0x53,0x65,0x63,0x75,0x72,0x65, +0x20,0x65,0x42,0x75,0x73,0x69,0x6E,0x65,0x73,0x73,0x20,0x43,0x41,0x2D,0x32,0x31, +0x0D,0x30,0x0B,0x06,0x03,0x55,0x04,0x03,0x13,0x04,0x43,0x52,0x4C,0x31,0x30,0x1A, +0x06,0x03,0x55,0x1D,0x10,0x04,0x13,0x30,0x11,0x81,0x0F,0x32,0x30,0x31,0x39,0x30, +0x36,0x32,0x33,0x31,0x32,0x31,0x34,0x34,0x35,0x5A,0x30,0x0B,0x06,0x03,0x55,0x1D, +0x0F,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18, +0x30,0x16,0x80,0x14,0x50,0x9E,0x0B,0xEA,0xAF,0x5E,0xB9,0x20,0x48,0xA6,0x50,0x6A, +0xCB,0xFD,0xD8,0x20,0x7A,0xA7,0x82,0x76,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04, +0x16,0x04,0x14,0x50,0x9E,0x0B,0xEA,0xAF,0x5E,0xB9,0x20,0x48,0xA6,0x50,0x6A,0xCB, +0xFD,0xD8,0x20,0x7A,0xA7,0x82,0x76,0x30,0x0C,0x06,0x03,0x55,0x1D,0x13,0x04,0x05, +0x30,0x03,0x01,0x01,0xFF,0x30,0x1A,0x06,0x09,0x2A,0x86,0x48,0x86,0xF6,0x7D,0x07, +0x41,0x00,0x04,0x0D,0x30,0x0B,0x1B,0x05,0x56,0x33,0x2E,0x30,0x63,0x03,0x02,0x06, +0xC0,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00, +0x03,0x81,0x81,0x00,0x0C,0x86,0x82,0xAD,0xE8,0x4E,0x1A,0xF5,0x8E,0x89,0x27,0xE2, +0x35,0x58,0x3D,0x29,0xB4,0x07,0x8F,0x36,0x50,0x95,0xBF,0x6E,0xC1,0x9E,0xEB,0xC4, +0x90,0xB2,0x85,0xA8,0xBB,0xB7,0x42,0xE0,0x0F,0x07,0x39,0xDF,0xFB,0x9E,0x90,0xB2, +0xD1,0xC1,0x3E,0x53,0x9F,0x03,0x44,0xB0,0x7E,0x4B,0xF4,0x6F,0xE4,0x7C,0x1F,0xE7, +0xE2,0xB1,0xE4,0xB8,0x9A,0xEF,0xC3,0xBD,0xCE,0xDE,0x0B,0x32,0x34,0xD9,0xDE,0x28, +0xED,0x33,0x6B,0xC4,0xD4,0xD7,0x3D,0x12,0x58,0xAB,0x7D,0x09,0x2D,0xCB,0x70,0xF5, +0x13,0x8A,0x94,0xA1,0x27,0xA4,0xD6,0x70,0xC5,0x6D,0x94,0xB5,0xC9,0x7D,0x9D,0xA0, +0xD2,0xC6,0x08,0x49,0xD9,0x66,0x9B,0xA6,0xD3,0xF4,0x0B,0xDC,0xC5,0x26,0x57,0xE1, +0x91,0x30,0xEA,0xCD, +}; + + +/* subject:/C=US/O=Equifax Secure Inc./CN=Equifax Secure Global eBusiness CA-1 */ +/* issuer :/C=US/O=Equifax Secure Inc./CN=Equifax Secure Global eBusiness CA-1 */ + + +const unsigned char Equifax_Secure_Global_eBusiness_CA_certificate[660]={ +0x30,0x82,0x02,0x90,0x30,0x82,0x01,0xF9,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x04,0x05,0x00,0x30, +0x5A,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x1C, +0x30,0x1A,0x06,0x03,0x55,0x04,0x0A,0x13,0x13,0x45,0x71,0x75,0x69,0x66,0x61,0x78, +0x20,0x53,0x65,0x63,0x75,0x72,0x65,0x20,0x49,0x6E,0x63,0x2E,0x31,0x2D,0x30,0x2B, +0x06,0x03,0x55,0x04,0x03,0x13,0x24,0x45,0x71,0x75,0x69,0x66,0x61,0x78,0x20,0x53, +0x65,0x63,0x75,0x72,0x65,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x65,0x42,0x75, +0x73,0x69,0x6E,0x65,0x73,0x73,0x20,0x43,0x41,0x2D,0x31,0x30,0x1E,0x17,0x0D,0x39, +0x39,0x30,0x36,0x32,0x31,0x30,0x34,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,0x30, +0x30,0x36,0x32,0x31,0x30,0x34,0x30,0x30,0x30,0x30,0x5A,0x30,0x5A,0x31,0x0B,0x30, +0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x1C,0x30,0x1A,0x06,0x03, +0x55,0x04,0x0A,0x13,0x13,0x45,0x71,0x75,0x69,0x66,0x61,0x78,0x20,0x53,0x65,0x63, +0x75,0x72,0x65,0x20,0x49,0x6E,0x63,0x2E,0x31,0x2D,0x30,0x2B,0x06,0x03,0x55,0x04, +0x03,0x13,0x24,0x45,0x71,0x75,0x69,0x66,0x61,0x78,0x20,0x53,0x65,0x63,0x75,0x72, +0x65,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x65,0x42,0x75,0x73,0x69,0x6E,0x65, +0x73,0x73,0x20,0x43,0x41,0x2D,0x31,0x30,0x81,0x9F,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x81,0x8D,0x00,0x30,0x81,0x89, +0x02,0x81,0x81,0x00,0xBA,0xE7,0x17,0x90,0x02,0x65,0xB1,0x34,0x55,0x3C,0x49,0xC2, +0x51,0xD5,0xDF,0xA7,0xD1,0x37,0x8F,0xD1,0xE7,0x81,0x73,0x41,0x52,0x60,0x9B,0x9D, +0xA1,0x17,0x26,0x78,0xAD,0xC7,0xB1,0xE8,0x26,0x94,0x32,0xB5,0xDE,0x33,0x8D,0x3A, +0x2F,0xDB,0xF2,0x9A,0x7A,0x5A,0x73,0x98,0xA3,0x5C,0xE9,0xFB,0x8A,0x73,0x1B,0x5C, +0xE7,0xC3,0xBF,0x80,0x6C,0xCD,0xA9,0xF4,0xD6,0x2B,0xC0,0xF7,0xF9,0x99,0xAA,0x63, +0xA2,0xB1,0x47,0x02,0x0F,0xD4,0xE4,0x51,0x3A,0x12,0x3C,0x6C,0x8A,0x5A,0x54,0x84, +0x70,0xDB,0xC1,0xC5,0x90,0xCF,0x72,0x45,0xCB,0xA8,0x59,0xC0,0xCD,0x33,0x9D,0x3F, +0xA3,0x96,0xEB,0x85,0x33,0x21,0x1C,0x3E,0x1E,0x3E,0x60,0x6E,0x76,0x9C,0x67,0x85, +0xC5,0xC8,0xC3,0x61,0x02,0x03,0x01,0x00,0x01,0xA3,0x66,0x30,0x64,0x30,0x11,0x06, +0x09,0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x01,0x04,0x04,0x03,0x02,0x00,0x07, +0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01, +0xFF,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0xBE,0xA8, +0xA0,0x74,0x72,0x50,0x6B,0x44,0xB7,0xC9,0x23,0xD8,0xFB,0xA8,0xFF,0xB3,0x57,0x6B, +0x68,0x6C,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xBE,0xA8,0xA0, +0x74,0x72,0x50,0x6B,0x44,0xB7,0xC9,0x23,0xD8,0xFB,0xA8,0xFF,0xB3,0x57,0x6B,0x68, +0x6C,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x04,0x05,0x00, +0x03,0x81,0x81,0x00,0x30,0xE2,0x01,0x51,0xAA,0xC7,0xEA,0x5F,0xDA,0xB9,0xD0,0x65, +0x0F,0x30,0xD6,0x3E,0xDA,0x0D,0x14,0x49,0x6E,0x91,0x93,0x27,0x14,0x31,0xEF,0xC4, +0xF7,0x2D,0x45,0xF8,0xEC,0xC7,0xBF,0xA2,0x41,0x0D,0x23,0xB4,0x92,0xF9,0x19,0x00, +0x67,0xBD,0x01,0xAF,0xCD,0xE0,0x71,0xFC,0x5A,0xCF,0x64,0xC4,0xE0,0x96,0x98,0xD0, +0xA3,0x40,0xE2,0x01,0x8A,0xEF,0x27,0x07,0xF1,0x65,0x01,0x8A,0x44,0x2D,0x06,0x65, +0x75,0x52,0xC0,0x86,0x10,0x20,0x21,0x5F,0x6C,0x6B,0x0F,0x6C,0xAE,0x09,0x1C,0xAF, +0xF2,0xA2,0x18,0x34,0xC4,0x75,0xA4,0x73,0x1C,0xF1,0x8D,0xDC,0xEF,0xAD,0xF9,0xB3, +0x76,0xB4,0x92,0xBF,0xDC,0x95,0x10,0x1E,0xBE,0xCB,0xC8,0x3B,0x5A,0x84,0x60,0x19, +0x56,0x94,0xA9,0x55, +}; + + +/* subject:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA */ +/* issuer :/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA */ + + +const unsigned char GeoTrust_Global_CA_certificate[856]={ +0x30,0x82,0x03,0x54,0x30,0x82,0x02,0x3C,0xA0,0x03,0x02,0x01,0x02,0x02,0x03,0x02, +0x34,0x56,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05, +0x00,0x30,0x42,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53, +0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D,0x47,0x65,0x6F,0x54,0x72, +0x75,0x73,0x74,0x20,0x49,0x6E,0x63,0x2E,0x31,0x1B,0x30,0x19,0x06,0x03,0x55,0x04, +0x03,0x13,0x12,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20,0x47,0x6C,0x6F,0x62, +0x61,0x6C,0x20,0x43,0x41,0x30,0x1E,0x17,0x0D,0x30,0x32,0x30,0x35,0x32,0x31,0x30, +0x34,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,0x32,0x30,0x35,0x32,0x31,0x30,0x34, +0x30,0x30,0x30,0x30,0x5A,0x30,0x42,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06, +0x13,0x02,0x55,0x53,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D,0x47, +0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20,0x49,0x6E,0x63,0x2E,0x31,0x1B,0x30,0x19, +0x06,0x03,0x55,0x04,0x03,0x13,0x12,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20, +0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x43,0x41,0x30,0x82,0x01,0x22,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F, +0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xDA,0xCC,0x18,0x63,0x30,0xFD, +0xF4,0x17,0x23,0x1A,0x56,0x7E,0x5B,0xDF,0x3C,0x6C,0x38,0xE4,0x71,0xB7,0x78,0x91, +0xD4,0xBC,0xA1,0xD8,0x4C,0xF8,0xA8,0x43,0xB6,0x03,0xE9,0x4D,0x21,0x07,0x08,0x88, +0xDA,0x58,0x2F,0x66,0x39,0x29,0xBD,0x05,0x78,0x8B,0x9D,0x38,0xE8,0x05,0xB7,0x6A, +0x7E,0x71,0xA4,0xE6,0xC4,0x60,0xA6,0xB0,0xEF,0x80,0xE4,0x89,0x28,0x0F,0x9E,0x25, +0xD6,0xED,0x83,0xF3,0xAD,0xA6,0x91,0xC7,0x98,0xC9,0x42,0x18,0x35,0x14,0x9D,0xAD, +0x98,0x46,0x92,0x2E,0x4F,0xCA,0xF1,0x87,0x43,0xC1,0x16,0x95,0x57,0x2D,0x50,0xEF, +0x89,0x2D,0x80,0x7A,0x57,0xAD,0xF2,0xEE,0x5F,0x6B,0xD2,0x00,0x8D,0xB9,0x14,0xF8, +0x14,0x15,0x35,0xD9,0xC0,0x46,0xA3,0x7B,0x72,0xC8,0x91,0xBF,0xC9,0x55,0x2B,0xCD, +0xD0,0x97,0x3E,0x9C,0x26,0x64,0xCC,0xDF,0xCE,0x83,0x19,0x71,0xCA,0x4E,0xE6,0xD4, +0xD5,0x7B,0xA9,0x19,0xCD,0x55,0xDE,0xC8,0xEC,0xD2,0x5E,0x38,0x53,0xE5,0x5C,0x4F, +0x8C,0x2D,0xFE,0x50,0x23,0x36,0xFC,0x66,0xE6,0xCB,0x8E,0xA4,0x39,0x19,0x00,0xB7, +0x95,0x02,0x39,0x91,0x0B,0x0E,0xFE,0x38,0x2E,0xD1,0x1D,0x05,0x9A,0xF6,0x4D,0x3E, +0x6F,0x0F,0x07,0x1D,0xAF,0x2C,0x1E,0x8F,0x60,0x39,0xE2,0xFA,0x36,0x53,0x13,0x39, +0xD4,0x5E,0x26,0x2B,0xDB,0x3D,0xA8,0x14,0xBD,0x32,0xEB,0x18,0x03,0x28,0x52,0x04, +0x71,0xE5,0xAB,0x33,0x3D,0xE1,0x38,0xBB,0x07,0x36,0x84,0x62,0x9C,0x79,0xEA,0x16, +0x30,0xF4,0x5F,0xC0,0x2B,0xE8,0x71,0x6B,0xE4,0xF9,0x02,0x03,0x01,0x00,0x01,0xA3, +0x53,0x30,0x51,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30, +0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xC0, +0x7A,0x98,0x68,0x8D,0x89,0xFB,0xAB,0x05,0x64,0x0C,0x11,0x7D,0xAA,0x7D,0x65,0xB8, +0xCA,0xCC,0x4E,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14, +0xC0,0x7A,0x98,0x68,0x8D,0x89,0xFB,0xAB,0x05,0x64,0x0C,0x11,0x7D,0xAA,0x7D,0x65, +0xB8,0xCA,0xCC,0x4E,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01, +0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x35,0xE3,0x29,0x6A,0xE5,0x2F,0x5D,0x54, +0x8E,0x29,0x50,0x94,0x9F,0x99,0x1A,0x14,0xE4,0x8F,0x78,0x2A,0x62,0x94,0xA2,0x27, +0x67,0x9E,0xD0,0xCF,0x1A,0x5E,0x47,0xE9,0xC1,0xB2,0xA4,0xCF,0xDD,0x41,0x1A,0x05, +0x4E,0x9B,0x4B,0xEE,0x4A,0x6F,0x55,0x52,0xB3,0x24,0xA1,0x37,0x0A,0xEB,0x64,0x76, +0x2A,0x2E,0x2C,0xF3,0xFD,0x3B,0x75,0x90,0xBF,0xFA,0x71,0xD8,0xC7,0x3D,0x37,0xD2, +0xB5,0x05,0x95,0x62,0xB9,0xA6,0xDE,0x89,0x3D,0x36,0x7B,0x38,0x77,0x48,0x97,0xAC, +0xA6,0x20,0x8F,0x2E,0xA6,0xC9,0x0C,0xC2,0xB2,0x99,0x45,0x00,0xC7,0xCE,0x11,0x51, +0x22,0x22,0xE0,0xA5,0xEA,0xB6,0x15,0x48,0x09,0x64,0xEA,0x5E,0x4F,0x74,0xF7,0x05, +0x3E,0xC7,0x8A,0x52,0x0C,0xDB,0x15,0xB4,0xBD,0x6D,0x9B,0xE5,0xC6,0xB1,0x54,0x68, +0xA9,0xE3,0x69,0x90,0xB6,0x9A,0xA5,0x0F,0xB8,0xB9,0x3F,0x20,0x7D,0xAE,0x4A,0xB5, +0xB8,0x9C,0xE4,0x1D,0xB6,0xAB,0xE6,0x94,0xA5,0xC1,0xC7,0x83,0xAD,0xDB,0xF5,0x27, +0x87,0x0E,0x04,0x6C,0xD5,0xFF,0xDD,0xA0,0x5D,0xED,0x87,0x52,0xB7,0x2B,0x15,0x02, +0xAE,0x39,0xA6,0x6A,0x74,0xE9,0xDA,0xC4,0xE7,0xBC,0x4D,0x34,0x1E,0xA9,0x5C,0x4D, +0x33,0x5F,0x92,0x09,0x2F,0x88,0x66,0x5D,0x77,0x97,0xC7,0x1D,0x76,0x13,0xA9,0xD5, +0xE5,0xF1,0x16,0x09,0x11,0x35,0xD5,0xAC,0xDB,0x24,0x71,0x70,0x2C,0x98,0x56,0x0B, +0xD9,0x17,0xB4,0xD1,0xE3,0x51,0x2B,0x5E,0x75,0xE8,0xD5,0xD0,0xDC,0x4F,0x34,0xED, +0xC2,0x05,0x66,0x80,0xA1,0xCB,0xE6,0x33, +}; + + +/* subject:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA 2 */ +/* issuer :/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA 2 */ + + +const unsigned char GeoTrust_Global_CA_2_certificate[874]={ +0x30,0x82,0x03,0x66,0x30,0x82,0x02,0x4E,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30, +0x44,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x16, +0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D,0x47,0x65,0x6F,0x54,0x72,0x75,0x73, +0x74,0x20,0x49,0x6E,0x63,0x2E,0x31,0x1D,0x30,0x1B,0x06,0x03,0x55,0x04,0x03,0x13, +0x14,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C, +0x20,0x43,0x41,0x20,0x32,0x30,0x1E,0x17,0x0D,0x30,0x34,0x30,0x33,0x30,0x34,0x30, +0x35,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x31,0x39,0x30,0x33,0x30,0x34,0x30,0x35, +0x30,0x30,0x30,0x30,0x5A,0x30,0x44,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06, +0x13,0x02,0x55,0x53,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D,0x47, +0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20,0x49,0x6E,0x63,0x2E,0x31,0x1D,0x30,0x1B, +0x06,0x03,0x55,0x04,0x03,0x13,0x14,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20, +0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x43,0x41,0x20,0x32,0x30,0x82,0x01,0x22,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82, +0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xEF,0x3C,0x4D,0x40, +0x3D,0x10,0xDF,0x3B,0x53,0x00,0xE1,0x67,0xFE,0x94,0x60,0x15,0x3E,0x85,0x88,0xF1, +0x89,0x0D,0x90,0xC8,0x28,0x23,0x99,0x05,0xE8,0x2B,0x20,0x9D,0xC6,0xF3,0x60,0x46, +0xD8,0xC1,0xB2,0xD5,0x8C,0x31,0xD9,0xDC,0x20,0x79,0x24,0x81,0xBF,0x35,0x32,0xFC, +0x63,0x69,0xDB,0xB1,0x2A,0x6B,0xEE,0x21,0x58,0xF2,0x08,0xE9,0x78,0xCB,0x6F,0xCB, +0xFC,0x16,0x52,0xC8,0x91,0xC4,0xFF,0x3D,0x73,0xDE,0xB1,0x3E,0xA7,0xC2,0x7D,0x66, +0xC1,0xF5,0x7E,0x52,0x24,0x1A,0xE2,0xD5,0x67,0x91,0xD0,0x82,0x10,0xD7,0x78,0x4B, +0x4F,0x2B,0x42,0x39,0xBD,0x64,0x2D,0x40,0xA0,0xB0,0x10,0xD3,0x38,0x48,0x46,0x88, +0xA1,0x0C,0xBB,0x3A,0x33,0x2A,0x62,0x98,0xFB,0x00,0x9D,0x13,0x59,0x7F,0x6F,0x3B, +0x72,0xAA,0xEE,0xA6,0x0F,0x86,0xF9,0x05,0x61,0xEA,0x67,0x7F,0x0C,0x37,0x96,0x8B, +0xE6,0x69,0x16,0x47,0x11,0xC2,0x27,0x59,0x03,0xB3,0xA6,0x60,0xC2,0x21,0x40,0x56, +0xFA,0xA0,0xC7,0x7D,0x3A,0x13,0xE3,0xEC,0x57,0xC7,0xB3,0xD6,0xAE,0x9D,0x89,0x80, +0xF7,0x01,0xE7,0x2C,0xF6,0x96,0x2B,0x13,0x0D,0x79,0x2C,0xD9,0xC0,0xE4,0x86,0x7B, +0x4B,0x8C,0x0C,0x72,0x82,0x8A,0xFB,0x17,0xCD,0x00,0x6C,0x3A,0x13,0x3C,0xB0,0x84, +0x87,0x4B,0x16,0x7A,0x29,0xB2,0x4F,0xDB,0x1D,0xD4,0x0B,0xF3,0x66,0x37,0xBD,0xD8, +0xF6,0x57,0xBB,0x5E,0x24,0x7A,0xB8,0x3C,0x8B,0xB9,0xFA,0x92,0x1A,0x1A,0x84,0x9E, +0xD8,0x74,0x8F,0xAA,0x1B,0x7F,0x5E,0xF4,0xFE,0x45,0x22,0x21,0x02,0x03,0x01,0x00, +0x01,0xA3,0x63,0x30,0x61,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04, +0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04, +0x14,0x71,0x38,0x36,0xF2,0x02,0x31,0x53,0x47,0x2B,0x6E,0xBA,0x65,0x46,0xA9,0x10, +0x15,0x58,0x20,0x05,0x09,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16, +0x80,0x14,0x71,0x38,0x36,0xF2,0x02,0x31,0x53,0x47,0x2B,0x6E,0xBA,0x65,0x46,0xA9, +0x10,0x15,0x58,0x20,0x05,0x09,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF, +0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D, +0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x03,0xF7,0xB5,0x2B,0xAB,0x5D, +0x10,0xFC,0x7B,0xB2,0xB2,0x5E,0xAC,0x9B,0x0E,0x7E,0x53,0x78,0x59,0x3E,0x42,0x04, +0xFE,0x75,0xA3,0xAD,0xAC,0x81,0x4E,0xD7,0x02,0x8B,0x5E,0xC4,0x2D,0xC8,0x52,0x76, +0xC7,0x2C,0x1F,0xFC,0x81,0x32,0x98,0xD1,0x4B,0xC6,0x92,0x93,0x33,0x35,0x31,0x2F, +0xFC,0xD8,0x1D,0x44,0xDD,0xE0,0x81,0x7F,0x9D,0xE9,0x8B,0xE1,0x64,0x91,0x62,0x0B, +0x39,0x08,0x8C,0xAC,0x74,0x9D,0x59,0xD9,0x7A,0x59,0x52,0x97,0x11,0xB9,0x16,0x7B, +0x6F,0x45,0xD3,0x96,0xD9,0x31,0x7D,0x02,0x36,0x0F,0x9C,0x3B,0x6E,0xCF,0x2C,0x0D, +0x03,0x46,0x45,0xEB,0xA0,0xF4,0x7F,0x48,0x44,0xC6,0x08,0x40,0xCC,0xDE,0x1B,0x70, +0xB5,0x29,0xAD,0xBA,0x8B,0x3B,0x34,0x65,0x75,0x1B,0x71,0x21,0x1D,0x2C,0x14,0x0A, +0xB0,0x96,0x95,0xB8,0xD6,0xEA,0xF2,0x65,0xFB,0x29,0xBA,0x4F,0xEA,0x91,0x93,0x74, +0x69,0xB6,0xF2,0xFF,0xE1,0x1A,0xD0,0x0C,0xD1,0x76,0x85,0xCB,0x8A,0x25,0xBD,0x97, +0x5E,0x2C,0x6F,0x15,0x99,0x26,0xE7,0xB6,0x29,0xFF,0x22,0xEC,0xC9,0x02,0xC7,0x56, +0x00,0xCD,0x49,0xB9,0xB3,0x6C,0x7B,0x53,0x04,0x1A,0xE2,0xA8,0xC9,0xAA,0x12,0x05, +0x23,0xC2,0xCE,0xE7,0xBB,0x04,0x02,0xCC,0xC0,0x47,0xA2,0xE4,0xC4,0x29,0x2F,0x5B, +0x45,0x57,0x89,0x51,0xEE,0x3C,0xEB,0x52,0x08,0xFF,0x07,0x35,0x1E,0x9F,0x35,0x6A, +0x47,0x4A,0x56,0x98,0xD1,0x5A,0x85,0x1F,0x8C,0xF5,0x22,0xBF,0xAB,0xCE,0x83,0xF3, +0xE2,0x22,0x29,0xAE,0x7D,0x83,0x40,0xA8,0xBA,0x6C, +}; + + +/* subject:/C=US/O=GeoTrust Inc./CN=GeoTrust Primary Certification Authority */ +/* issuer :/C=US/O=GeoTrust Inc./CN=GeoTrust Primary Certification Authority */ + + +const unsigned char GeoTrust_Primary_Certification_Authority_certificate[896]={ +0x30,0x82,0x03,0x7C,0x30,0x82,0x02,0x64,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x18, +0xAC,0xB5,0x6A,0xFD,0x69,0xB6,0x15,0x3A,0x63,0x6C,0xAF,0xDA,0xFA,0xC4,0xA1,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x58, +0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x16,0x30, +0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74, +0x20,0x49,0x6E,0x63,0x2E,0x31,0x31,0x30,0x2F,0x06,0x03,0x55,0x04,0x03,0x13,0x28, +0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20,0x50,0x72,0x69,0x6D,0x61,0x72,0x79, +0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41, +0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x1E,0x17,0x0D,0x30,0x36,0x31,0x31, +0x32,0x37,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x36,0x30,0x37,0x31, +0x36,0x32,0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x58,0x31,0x0B,0x30,0x09,0x06,0x03, +0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x0A, +0x13,0x0D,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20,0x49,0x6E,0x63,0x2E,0x31, +0x31,0x30,0x2F,0x06,0x03,0x55,0x04,0x03,0x13,0x28,0x47,0x65,0x6F,0x54,0x72,0x75, +0x73,0x74,0x20,0x50,0x72,0x69,0x6D,0x61,0x72,0x79,0x20,0x43,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69, +0x74,0x79,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D, +0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82, +0x01,0x01,0x00,0xBE,0xB8,0x15,0x7B,0xFF,0xD4,0x7C,0x7D,0x67,0xAD,0x83,0x64,0x7B, +0xC8,0x42,0x53,0x2D,0xDF,0xF6,0x84,0x08,0x20,0x61,0xD6,0x01,0x59,0x6A,0x9C,0x44, +0x11,0xAF,0xEF,0x76,0xFD,0x95,0x7E,0xCE,0x61,0x30,0xBB,0x7A,0x83,0x5F,0x02,0xBD, +0x01,0x66,0xCA,0xEE,0x15,0x8D,0x6F,0xA1,0x30,0x9C,0xBD,0xA1,0x85,0x9E,0x94,0x3A, +0xF3,0x56,0x88,0x00,0x31,0xCF,0xD8,0xEE,0x6A,0x96,0x02,0xD9,0xED,0x03,0x8C,0xFB, +0x75,0x6D,0xE7,0xEA,0xB8,0x55,0x16,0x05,0x16,0x9A,0xF4,0xE0,0x5E,0xB1,0x88,0xC0, +0x64,0x85,0x5C,0x15,0x4D,0x88,0xC7,0xB7,0xBA,0xE0,0x75,0xE9,0xAD,0x05,0x3D,0x9D, +0xC7,0x89,0x48,0xE0,0xBB,0x28,0xC8,0x03,0xE1,0x30,0x93,0x64,0x5E,0x52,0xC0,0x59, +0x70,0x22,0x35,0x57,0x88,0x8A,0xF1,0x95,0x0A,0x83,0xD7,0xBC,0x31,0x73,0x01,0x34, +0xED,0xEF,0x46,0x71,0xE0,0x6B,0x02,0xA8,0x35,0x72,0x6B,0x97,0x9B,0x66,0xE0,0xCB, +0x1C,0x79,0x5F,0xD8,0x1A,0x04,0x68,0x1E,0x47,0x02,0xE6,0x9D,0x60,0xE2,0x36,0x97, +0x01,0xDF,0xCE,0x35,0x92,0xDF,0xBE,0x67,0xC7,0x6D,0x77,0x59,0x3B,0x8F,0x9D,0xD6, +0x90,0x15,0x94,0xBC,0x42,0x34,0x10,0xC1,0x39,0xF9,0xB1,0x27,0x3E,0x7E,0xD6,0x8A, +0x75,0xC5,0xB2,0xAF,0x96,0xD3,0xA2,0xDE,0x9B,0xE4,0x98,0xBE,0x7D,0xE1,0xE9,0x81, +0xAD,0xB6,0x6F,0xFC,0xD7,0x0E,0xDA,0xE0,0x34,0xB0,0x0D,0x1A,0x77,0xE7,0xE3,0x08, +0x98,0xEF,0x58,0xFA,0x9C,0x84,0xB7,0x36,0xAF,0xC2,0xDF,0xAC,0xD2,0xF4,0x10,0x06, +0x70,0x71,0x35,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x0F,0x06,0x03, +0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06, +0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x1D,0x06, +0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x2C,0xD5,0x50,0x41,0x97,0x15,0x8B,0xF0, +0x8F,0x36,0x61,0x5B,0x4A,0xFB,0x6B,0xD9,0x99,0xC9,0x33,0x92,0x30,0x0D,0x06,0x09, +0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00, +0x5A,0x70,0x7F,0x2C,0xDD,0xB7,0x34,0x4F,0xF5,0x86,0x51,0xA9,0x26,0xBE,0x4B,0xB8, +0xAA,0xF1,0x71,0x0D,0xDC,0x61,0xC7,0xA0,0xEA,0x34,0x1E,0x7A,0x77,0x0F,0x04,0x35, +0xE8,0x27,0x8F,0x6C,0x90,0xBF,0x91,0x16,0x24,0x46,0x3E,0x4A,0x4E,0xCE,0x2B,0x16, +0xD5,0x0B,0x52,0x1D,0xFC,0x1F,0x67,0xA2,0x02,0x45,0x31,0x4F,0xCE,0xF3,0xFA,0x03, +0xA7,0x79,0x9D,0x53,0x6A,0xD9,0xDA,0x63,0x3A,0xF8,0x80,0xD7,0xD3,0x99,0xE1,0xA5, +0xE1,0xBE,0xD4,0x55,0x71,0x98,0x35,0x3A,0xBE,0x93,0xEA,0xAE,0xAD,0x42,0xB2,0x90, +0x6F,0xE0,0xFC,0x21,0x4D,0x35,0x63,0x33,0x89,0x49,0xD6,0x9B,0x4E,0xCA,0xC7,0xE7, +0x4E,0x09,0x00,0xF7,0xDA,0xC7,0xEF,0x99,0x62,0x99,0x77,0xB6,0x95,0x22,0x5E,0x8A, +0xA0,0xAB,0xF4,0xB8,0x78,0x98,0xCA,0x38,0x19,0x99,0xC9,0x72,0x9E,0x78,0xCD,0x4B, +0xAC,0xAF,0x19,0xA0,0x73,0x12,0x2D,0xFC,0xC2,0x41,0xBA,0x81,0x91,0xDA,0x16,0x5A, +0x31,0xB7,0xF9,0xB4,0x71,0x80,0x12,0x48,0x99,0x72,0x73,0x5A,0x59,0x53,0xC1,0x63, +0x52,0x33,0xED,0xA7,0xC9,0xD2,0x39,0x02,0x70,0xFA,0xE0,0xB1,0x42,0x66,0x29,0xAA, +0x9B,0x51,0xED,0x30,0x54,0x22,0x14,0x5F,0xD9,0xAB,0x1D,0xC1,0xE4,0x94,0xF0,0xF8, +0xF5,0x2B,0xF7,0xEA,0xCA,0x78,0x46,0xD6,0xB8,0x91,0xFD,0xA6,0x0D,0x2B,0x1A,0x14, +0x01,0x3E,0x80,0xF0,0x42,0xA0,0x95,0x07,0x5E,0x6D,0xCD,0xCC,0x4B,0xA4,0x45,0x8D, +0xAB,0x12,0xE8,0xB3,0xDE,0x5A,0xE5,0xA0,0x7C,0xE8,0x0F,0x22,0x1D,0x5A,0xE9,0x59, +}; + + +/* subject:/C=US/O=GeoTrust Inc./OU=(c) 2007 GeoTrust Inc. - For authorized use only/CN=GeoTrust Primary Certification Authority - G2 */ +/* issuer :/C=US/O=GeoTrust Inc./OU=(c) 2007 GeoTrust Inc. - For authorized use only/CN=GeoTrust Primary Certification Authority - G2 */ + + +const unsigned char GeoTrust_Primary_Certification_Authority___G2_certificate[690]={ +0x30,0x82,0x02,0xAE,0x30,0x82,0x02,0x35,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x3C, +0xB2,0xF4,0x48,0x0A,0x00,0xE2,0xFE,0xEB,0x24,0x3B,0x5E,0x60,0x3E,0xC3,0x6B,0x30, +0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x30,0x81,0x98,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x16,0x30,0x14,0x06, +0x03,0x55,0x04,0x0A,0x13,0x0D,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20,0x49, +0x6E,0x63,0x2E,0x31,0x39,0x30,0x37,0x06,0x03,0x55,0x04,0x0B,0x13,0x30,0x28,0x63, +0x29,0x20,0x32,0x30,0x30,0x37,0x20,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20, +0x49,0x6E,0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F,0x72,0x20,0x61,0x75,0x74,0x68,0x6F, +0x72,0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C,0x79,0x31,0x36, +0x30,0x34,0x06,0x03,0x55,0x04,0x03,0x13,0x2D,0x47,0x65,0x6F,0x54,0x72,0x75,0x73, +0x74,0x20,0x50,0x72,0x69,0x6D,0x61,0x72,0x79,0x20,0x43,0x65,0x72,0x74,0x69,0x66, +0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74, +0x79,0x20,0x2D,0x20,0x47,0x32,0x30,0x1E,0x17,0x0D,0x30,0x37,0x31,0x31,0x30,0x35, +0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x38,0x30,0x31,0x31,0x38,0x32, +0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x81,0x98,0x31,0x0B,0x30,0x09,0x06,0x03,0x55, +0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13, +0x0D,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20,0x49,0x6E,0x63,0x2E,0x31,0x39, +0x30,0x37,0x06,0x03,0x55,0x04,0x0B,0x13,0x30,0x28,0x63,0x29,0x20,0x32,0x30,0x30, +0x37,0x20,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20,0x49,0x6E,0x63,0x2E,0x20, +0x2D,0x20,0x46,0x6F,0x72,0x20,0x61,0x75,0x74,0x68,0x6F,0x72,0x69,0x7A,0x65,0x64, +0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C,0x79,0x31,0x36,0x30,0x34,0x06,0x03,0x55, +0x04,0x03,0x13,0x2D,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20,0x50,0x72,0x69, +0x6D,0x61,0x72,0x79,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69, +0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x20,0x2D,0x20,0x47, +0x32,0x30,0x76,0x30,0x10,0x06,0x07,0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,0x06,0x05, +0x2B,0x81,0x04,0x00,0x22,0x03,0x62,0x00,0x04,0x15,0xB1,0xE8,0xFD,0x03,0x15,0x43, +0xE5,0xAC,0xEB,0x87,0x37,0x11,0x62,0xEF,0xD2,0x83,0x36,0x52,0x7D,0x45,0x57,0x0B, +0x4A,0x8D,0x7B,0x54,0x3B,0x3A,0x6E,0x5F,0x15,0x02,0xC0,0x50,0xA6,0xCF,0x25,0x2F, +0x7D,0xCA,0x48,0xB8,0xC7,0x50,0x63,0x1C,0x2A,0x21,0x08,0x7C,0x9A,0x36,0xD8,0x0B, +0xFE,0xD1,0x26,0xC5,0x58,0x31,0x30,0x28,0x25,0xF3,0x5D,0x5D,0xA3,0xB8,0xB6,0xA5, +0xB4,0x92,0xED,0x6C,0x2C,0x9F,0xEB,0xDD,0x43,0x89,0xA2,0x3C,0x4B,0x48,0x91,0x1D, +0x50,0xEC,0x26,0xDF,0xD6,0x60,0x2E,0xBD,0x21,0xA3,0x42,0x30,0x40,0x30,0x0F,0x06, +0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E, +0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x1D, +0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x15,0x5F,0x35,0x57,0x51,0x55,0xFB, +0x25,0xB2,0xAD,0x03,0x69,0xFC,0x01,0xA3,0xFA,0xBE,0x11,0x55,0xD5,0x30,0x0A,0x06, +0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x03,0x67,0x00,0x30,0x64,0x02,0x30, +0x64,0x96,0x59,0xA6,0xE8,0x09,0xDE,0x8B,0xBA,0xFA,0x5A,0x88,0x88,0xF0,0x1F,0x91, +0xD3,0x46,0xA8,0xF2,0x4A,0x4C,0x02,0x63,0xFB,0x6C,0x5F,0x38,0xDB,0x2E,0x41,0x93, +0xA9,0x0E,0xE6,0x9D,0xDC,0x31,0x1C,0xB2,0xA0,0xA7,0x18,0x1C,0x79,0xE1,0xC7,0x36, +0x02,0x30,0x3A,0x56,0xAF,0x9A,0x74,0x6C,0xF6,0xFB,0x83,0xE0,0x33,0xD3,0x08,0x5F, +0xA1,0x9C,0xC2,0x5B,0x9F,0x46,0xD6,0xB6,0xCB,0x91,0x06,0x63,0xA2,0x06,0xE7,0x33, +0xAC,0x3E,0xA8,0x81,0x12,0xD0,0xCB,0xBA,0xD0,0x92,0x0B,0xB6,0x9E,0x96,0xAA,0x04, +0x0F,0x8A, +}; + + +/* subject:/C=US/O=GeoTrust Inc./OU=(c) 2008 GeoTrust Inc. - For authorized use only/CN=GeoTrust Primary Certification Authority - G3 */ +/* issuer :/C=US/O=GeoTrust Inc./OU=(c) 2008 GeoTrust Inc. - For authorized use only/CN=GeoTrust Primary Certification Authority - G3 */ + + +const unsigned char GeoTrust_Primary_Certification_Authority___G3_certificate[1026]={ +0x30,0x82,0x03,0xFE,0x30,0x82,0x02,0xE6,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x15, +0xAC,0x6E,0x94,0x19,0xB2,0x79,0x4B,0x41,0xF6,0x27,0xA9,0xC3,0x18,0x0F,0x1F,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,0x81, +0x98,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x16, +0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D,0x47,0x65,0x6F,0x54,0x72,0x75,0x73, +0x74,0x20,0x49,0x6E,0x63,0x2E,0x31,0x39,0x30,0x37,0x06,0x03,0x55,0x04,0x0B,0x13, +0x30,0x28,0x63,0x29,0x20,0x32,0x30,0x30,0x38,0x20,0x47,0x65,0x6F,0x54,0x72,0x75, +0x73,0x74,0x20,0x49,0x6E,0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F,0x72,0x20,0x61,0x75, +0x74,0x68,0x6F,0x72,0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C, +0x79,0x31,0x36,0x30,0x34,0x06,0x03,0x55,0x04,0x03,0x13,0x2D,0x47,0x65,0x6F,0x54, +0x72,0x75,0x73,0x74,0x20,0x50,0x72,0x69,0x6D,0x61,0x72,0x79,0x20,0x43,0x65,0x72, +0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F, +0x72,0x69,0x74,0x79,0x20,0x2D,0x20,0x47,0x33,0x30,0x1E,0x17,0x0D,0x30,0x38,0x30, +0x34,0x30,0x32,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x37,0x31,0x32, +0x30,0x31,0x32,0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x81,0x98,0x31,0x0B,0x30,0x09, +0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x16,0x30,0x14,0x06,0x03,0x55, +0x04,0x0A,0x13,0x0D,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20,0x49,0x6E,0x63, +0x2E,0x31,0x39,0x30,0x37,0x06,0x03,0x55,0x04,0x0B,0x13,0x30,0x28,0x63,0x29,0x20, +0x32,0x30,0x30,0x38,0x20,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20,0x49,0x6E, +0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F,0x72,0x20,0x61,0x75,0x74,0x68,0x6F,0x72,0x69, +0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C,0x79,0x31,0x36,0x30,0x34, +0x06,0x03,0x55,0x04,0x03,0x13,0x2D,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20, +0x50,0x72,0x69,0x6D,0x61,0x72,0x79,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63, +0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x20, +0x2D,0x20,0x47,0x33,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A, +0x02,0x82,0x01,0x01,0x00,0xDC,0xE2,0x5E,0x62,0x58,0x1D,0x33,0x57,0x39,0x32,0x33, +0xFA,0xEB,0xCB,0x87,0x8C,0xA7,0xD4,0x4A,0xDD,0x06,0x88,0xEA,0x64,0x8E,0x31,0x98, +0xA5,0x38,0x90,0x1E,0x98,0xCF,0x2E,0x63,0x2B,0xF0,0x46,0xBC,0x44,0xB2,0x89,0xA1, +0xC0,0x28,0x0C,0x49,0x70,0x21,0x95,0x9F,0x64,0xC0,0xA6,0x93,0x12,0x02,0x65,0x26, +0x86,0xC6,0xA5,0x89,0xF0,0xFA,0xD7,0x84,0xA0,0x70,0xAF,0x4F,0x1A,0x97,0x3F,0x06, +0x44,0xD5,0xC9,0xEB,0x72,0x10,0x7D,0xE4,0x31,0x28,0xFB,0x1C,0x61,0xE6,0x28,0x07, +0x44,0x73,0x92,0x22,0x69,0xA7,0x03,0x88,0x6C,0x9D,0x63,0xC8,0x52,0xDA,0x98,0x27, +0xE7,0x08,0x4C,0x70,0x3E,0xB4,0xC9,0x12,0xC1,0xC5,0x67,0x83,0x5D,0x33,0xF3,0x03, +0x11,0xEC,0x6A,0xD0,0x53,0xE2,0xD1,0xBA,0x36,0x60,0x94,0x80,0xBB,0x61,0x63,0x6C, +0x5B,0x17,0x7E,0xDF,0x40,0x94,0x1E,0xAB,0x0D,0xC2,0x21,0x28,0x70,0x88,0xFF,0xD6, +0x26,0x6C,0x6C,0x60,0x04,0x25,0x4E,0x55,0x7E,0x7D,0xEF,0xBF,0x94,0x48,0xDE,0xB7, +0x1D,0xDD,0x70,0x8D,0x05,0x5F,0x88,0xA5,0x9B,0xF2,0xC2,0xEE,0xEA,0xD1,0x40,0x41, +0x6D,0x62,0x38,0x1D,0x56,0x06,0xC5,0x03,0x47,0x51,0x20,0x19,0xFC,0x7B,0x10,0x0B, +0x0E,0x62,0xAE,0x76,0x55,0xBF,0x5F,0x77,0xBE,0x3E,0x49,0x01,0x53,0x3D,0x98,0x25, +0x03,0x76,0x24,0x5A,0x1D,0xB4,0xDB,0x89,0xEA,0x79,0xE5,0xB6,0xB3,0x3B,0x3F,0xBA, +0x4C,0x28,0x41,0x7F,0x06,0xAC,0x6A,0x8E,0xC1,0xD0,0xF6,0x05,0x1D,0x7D,0xE6,0x42, +0x86,0xE3,0xA5,0xD5,0x47,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x0F, +0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30, +0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30, +0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xC4,0x79,0xCA,0x8E,0xA1,0x4E, +0x03,0x1D,0x1C,0xDC,0x6B,0xDB,0x31,0x5B,0x94,0x3E,0x3F,0x30,0x7F,0x2D,0x30,0x0D, +0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01, +0x01,0x00,0x2D,0xC5,0x13,0xCF,0x56,0x80,0x7B,0x7A,0x78,0xBD,0x9F,0xAE,0x2C,0x99, +0xE7,0xEF,0xDA,0xDF,0x94,0x5E,0x09,0x69,0xA7,0xE7,0x6E,0x68,0x8C,0xBD,0x72,0xBE, +0x47,0xA9,0x0E,0x97,0x12,0xB8,0x4A,0xF1,0x64,0xD3,0x39,0xDF,0x25,0x34,0xD4,0xC1, +0xCD,0x4E,0x81,0xF0,0x0F,0x04,0xC4,0x24,0xB3,0x34,0x96,0xC6,0xA6,0xAA,0x30,0xDF, +0x68,0x61,0x73,0xD7,0xF9,0x8E,0x85,0x89,0xEF,0x0E,0x5E,0x95,0x28,0x4A,0x2A,0x27, +0x8F,0x10,0x8E,0x2E,0x7C,0x86,0xC4,0x02,0x9E,0xDA,0x0C,0x77,0x65,0x0E,0x44,0x0D, +0x92,0xFD,0xFD,0xB3,0x16,0x36,0xFA,0x11,0x0D,0x1D,0x8C,0x0E,0x07,0x89,0x6A,0x29, +0x56,0xF7,0x72,0xF4,0xDD,0x15,0x9C,0x77,0x35,0x66,0x57,0xAB,0x13,0x53,0xD8,0x8E, +0xC1,0x40,0xC5,0xD7,0x13,0x16,0x5A,0x72,0xC7,0xB7,0x69,0x01,0xC4,0x7A,0xB1,0x83, +0x01,0x68,0x7D,0x8D,0x41,0xA1,0x94,0x18,0xC1,0x25,0x5C,0xFC,0xF0,0xFE,0x83,0x02, +0x87,0x7C,0x0D,0x0D,0xCF,0x2E,0x08,0x5C,0x4A,0x40,0x0D,0x3E,0xEC,0x81,0x61,0xE6, +0x24,0xDB,0xCA,0xE0,0x0E,0x2D,0x07,0xB2,0x3E,0x56,0xDC,0x8D,0xF5,0x41,0x85,0x07, +0x48,0x9B,0x0C,0x0B,0xCB,0x49,0x3F,0x7D,0xEC,0xB7,0xFD,0xCB,0x8D,0x67,0x89,0x1A, +0xAB,0xED,0xBB,0x1E,0xA3,0x00,0x08,0x08,0x17,0x2A,0x82,0x5C,0x31,0x5D,0x46,0x8A, +0x2D,0x0F,0x86,0x9B,0x74,0xD9,0x45,0xFB,0xD4,0x40,0xB1,0x7A,0xAA,0x68,0x2D,0x86, +0xB2,0x99,0x22,0xE1,0xC1,0x2B,0xC7,0x9C,0xF8,0xF3,0x5F,0xA8,0x82,0x12,0xEB,0x19, +0x11,0x2D, +}; + + +/* subject:/C=US/O=GeoTrust Inc./CN=GeoTrust Universal CA */ +/* issuer :/C=US/O=GeoTrust Inc./CN=GeoTrust Universal CA */ + + +const unsigned char GeoTrust_Universal_CA_certificate[1388]={ +0x30,0x82,0x05,0x68,0x30,0x82,0x03,0x50,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30, +0x45,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x16, +0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D,0x47,0x65,0x6F,0x54,0x72,0x75,0x73, +0x74,0x20,0x49,0x6E,0x63,0x2E,0x31,0x1E,0x30,0x1C,0x06,0x03,0x55,0x04,0x03,0x13, +0x15,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20,0x55,0x6E,0x69,0x76,0x65,0x72, +0x73,0x61,0x6C,0x20,0x43,0x41,0x30,0x1E,0x17,0x0D,0x30,0x34,0x30,0x33,0x30,0x34, +0x30,0x35,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,0x39,0x30,0x33,0x30,0x34,0x30, +0x35,0x30,0x30,0x30,0x30,0x5A,0x30,0x45,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04, +0x06,0x13,0x02,0x55,0x53,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D, +0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20,0x49,0x6E,0x63,0x2E,0x31,0x1E,0x30, +0x1C,0x06,0x03,0x55,0x04,0x03,0x13,0x15,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74, +0x20,0x55,0x6E,0x69,0x76,0x65,0x72,0x73,0x61,0x6C,0x20,0x43,0x41,0x30,0x82,0x02, +0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00, +0x03,0x82,0x02,0x0F,0x00,0x30,0x82,0x02,0x0A,0x02,0x82,0x02,0x01,0x00,0xA6,0x15, +0x55,0xA0,0xA3,0xC6,0xE0,0x1F,0x8C,0x9D,0x21,0x50,0xD7,0xC1,0xBE,0x2B,0x5B,0xB5, +0xA4,0x9E,0xA1,0xD9,0x72,0x58,0xBD,0x00,0x1B,0x4C,0xBF,0x61,0xC9,0x14,0x1D,0x45, +0x82,0xAB,0xC6,0x1D,0x80,0xD6,0x3D,0xEB,0x10,0x9C,0x3A,0xAF,0x6D,0x24,0xF8,0xBC, +0x71,0x01,0x9E,0x06,0xF5,0x7C,0x5F,0x1E,0xC1,0x0E,0x55,0xCA,0x83,0x9A,0x59,0x30, +0xAE,0x19,0xCB,0x30,0x48,0x95,0xED,0x22,0x37,0x8D,0xF4,0x4A,0x9A,0x72,0x66,0x3E, +0xAD,0x95,0xC0,0xE0,0x16,0x00,0xE0,0x10,0x1F,0x2B,0x31,0x0E,0xD7,0x94,0x54,0xD3, +0x42,0x33,0xA0,0x34,0x1D,0x1E,0x45,0x76,0xDD,0x4F,0xCA,0x18,0x37,0xEC,0x85,0x15, +0x7A,0x19,0x08,0xFC,0xD5,0xC7,0x9C,0xF0,0xF2,0xA9,0x2E,0x10,0xA9,0x92,0xE6,0x3D, +0x58,0x3D,0xA9,0x16,0x68,0x3C,0x2F,0x75,0x21,0x18,0x7F,0x28,0x77,0xA5,0xE1,0x61, +0x17,0xB7,0xA6,0xE9,0xF8,0x1E,0x99,0xDB,0x73,0x6E,0xF4,0x0A,0xA2,0x21,0x6C,0xEE, +0xDA,0xAA,0x85,0x92,0x66,0xAF,0xF6,0x7A,0x6B,0x82,0xDA,0xBA,0x22,0x08,0x35,0x0F, +0xCF,0x42,0xF1,0x35,0xFA,0x6A,0xEE,0x7E,0x2B,0x25,0xCC,0x3A,0x11,0xE4,0x6D,0xAF, +0x73,0xB2,0x76,0x1D,0xAD,0xD0,0xB2,0x78,0x67,0x1A,0xA4,0x39,0x1C,0x51,0x0B,0x67, +0x56,0x83,0xFD,0x38,0x5D,0x0D,0xCE,0xDD,0xF0,0xBB,0x2B,0x96,0x1F,0xDE,0x7B,0x32, +0x52,0xFD,0x1D,0xBB,0xB5,0x06,0xA1,0xB2,0x21,0x5E,0xA5,0xD6,0x95,0x68,0x7F,0xF0, +0x99,0x9E,0xDC,0x45,0x08,0x3E,0xE7,0xD2,0x09,0x0D,0x35,0x94,0xDD,0x80,0x4E,0x53, +0x97,0xD7,0xB5,0x09,0x44,0x20,0x64,0x16,0x17,0x03,0x02,0x4C,0x53,0x0D,0x68,0xDE, +0xD5,0xAA,0x72,0x4D,0x93,0x6D,0x82,0x0E,0xDB,0x9C,0xBD,0xCF,0xB4,0xF3,0x5C,0x5D, +0x54,0x7A,0x69,0x09,0x96,0xD6,0xDB,0x11,0xC1,0x8D,0x75,0xA8,0xB4,0xCF,0x39,0xC8, +0xCE,0x3C,0xBC,0x24,0x7C,0xE6,0x62,0xCA,0xE1,0xBD,0x7D,0xA7,0xBD,0x57,0x65,0x0B, +0xE4,0xFE,0x25,0xED,0xB6,0x69,0x10,0xDC,0x28,0x1A,0x46,0xBD,0x01,0x1D,0xD0,0x97, +0xB5,0xE1,0x98,0x3B,0xC0,0x37,0x64,0xD6,0x3D,0x94,0xEE,0x0B,0xE1,0xF5,0x28,0xAE, +0x0B,0x56,0xBF,0x71,0x8B,0x23,0x29,0x41,0x8E,0x86,0xC5,0x4B,0x52,0x7B,0xD8,0x71, +0xAB,0x1F,0x8A,0x15,0xA6,0x3B,0x83,0x5A,0xD7,0x58,0x01,0x51,0xC6,0x4C,0x41,0xD9, +0x7F,0xD8,0x41,0x67,0x72,0xA2,0x28,0xDF,0x60,0x83,0xA9,0x9E,0xC8,0x7B,0xFC,0x53, +0x73,0x72,0x59,0xF5,0x93,0x7A,0x17,0x76,0x0E,0xCE,0xF7,0xE5,0x5C,0xD9,0x0B,0x55, +0x34,0xA2,0xAA,0x5B,0xB5,0x6A,0x54,0xE7,0x13,0xCA,0x57,0xEC,0x97,0x6D,0xF4,0x5E, +0x06,0x2F,0x45,0x8B,0x58,0xD4,0x23,0x16,0x92,0xE4,0x16,0x6E,0x28,0x63,0x59,0x30, +0xDF,0x50,0x01,0x9C,0x63,0x89,0x1A,0x9F,0xDB,0x17,0x94,0x82,0x70,0x37,0xC3,0x24, +0x9E,0x9A,0x47,0xD6,0x5A,0xCA,0x4E,0xA8,0x69,0x89,0x72,0x1F,0x91,0x6C,0xDB,0x7E, +0x9E,0x1B,0xAD,0xC7,0x1F,0x73,0xDD,0x2C,0x4F,0x19,0x65,0xFD,0x7F,0x93,0x40,0x10, +0x2E,0xD2,0xF0,0xED,0x3C,0x9E,0x2E,0x28,0x3E,0x69,0x26,0x33,0xC5,0x7B,0x02,0x03, +0x01,0x00,0x01,0xA3,0x63,0x30,0x61,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01, +0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04, +0x16,0x04,0x14,0xDA,0xBB,0x2E,0xAA,0xB0,0x0C,0xB8,0x88,0x26,0x51,0x74,0x5C,0x6D, +0x03,0xD3,0xC0,0xD8,0x8F,0x7A,0xD6,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18, +0x30,0x16,0x80,0x14,0xDA,0xBB,0x2E,0xAA,0xB0,0x0C,0xB8,0x88,0x26,0x51,0x74,0x5C, +0x6D,0x03,0xD3,0xC0,0xD8,0x8F,0x7A,0xD6,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01, +0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x02,0x01,0x00,0x31,0x78,0xE6,0xC7, +0xB5,0xDF,0xB8,0x94,0x40,0xC9,0x71,0xC4,0xA8,0x35,0xEC,0x46,0x1D,0xC2,0x85,0xF3, +0x28,0x58,0x86,0xB0,0x0B,0xFC,0x8E,0xB2,0x39,0x8F,0x44,0x55,0xAB,0x64,0x84,0x5C, +0x69,0xA9,0xD0,0x9A,0x38,0x3C,0xFA,0xE5,0x1F,0x35,0xE5,0x44,0xE3,0x80,0x79,0x94, +0x68,0xA4,0xBB,0xC4,0x9F,0x3D,0xE1,0x34,0xCD,0x30,0x46,0x8B,0x54,0x2B,0x95,0xA5, +0xEF,0xF7,0x3F,0x99,0x84,0xFD,0x35,0xE6,0xCF,0x31,0xC6,0xDC,0x6A,0xBF,0xA7,0xD7, +0x23,0x08,0xE1,0x98,0x5E,0xC3,0x5A,0x08,0x76,0xA9,0xA6,0xAF,0x77,0x2F,0xB7,0x60, +0xBD,0x44,0x46,0x6A,0xEF,0x97,0xFF,0x73,0x95,0xC1,0x8E,0xE8,0x93,0xFB,0xFD,0x31, +0xB7,0xEC,0x57,0x11,0x11,0x45,0x9B,0x30,0xF1,0x1A,0x88,0x39,0xC1,0x4F,0x3C,0xA7, +0x00,0xD5,0xC7,0xFC,0xAB,0x6D,0x80,0x22,0x70,0xA5,0x0C,0xE0,0x5D,0x04,0x29,0x02, +0xFB,0xCB,0xA0,0x91,0xD1,0x7C,0xD6,0xC3,0x7E,0x50,0xD5,0x9D,0x58,0xBE,0x41,0x38, +0xEB,0xB9,0x75,0x3C,0x15,0xD9,0x9B,0xC9,0x4A,0x83,0x59,0xC0,0xDA,0x53,0xFD,0x33, +0xBB,0x36,0x18,0x9B,0x85,0x0F,0x15,0xDD,0xEE,0x2D,0xAC,0x76,0x93,0xB9,0xD9,0x01, +0x8D,0x48,0x10,0xA8,0xFB,0xF5,0x38,0x86,0xF1,0xDB,0x0A,0xC6,0xBD,0x84,0xA3,0x23, +0x41,0xDE,0xD6,0x77,0x6F,0x85,0xD4,0x85,0x1C,0x50,0xE0,0xAE,0x51,0x8A,0xBA,0x8D, +0x3E,0x76,0xE2,0xB9,0xCA,0x27,0xF2,0x5F,0x9F,0xEF,0x6E,0x59,0x0D,0x06,0xD8,0x2B, +0x17,0xA4,0xD2,0x7C,0x6B,0xBB,0x5F,0x14,0x1A,0x48,0x8F,0x1A,0x4C,0xE7,0xB3,0x47, +0x1C,0x8E,0x4C,0x45,0x2B,0x20,0xEE,0x48,0xDF,0xE7,0xDD,0x09,0x8E,0x18,0xA8,0xDA, +0x40,0x8D,0x92,0x26,0x11,0x53,0x61,0x73,0x5D,0xEB,0xBD,0xE7,0xC4,0x4D,0x29,0x37, +0x61,0xEB,0xAC,0x39,0x2D,0x67,0x2E,0x16,0xD6,0xF5,0x00,0x83,0x85,0xA1,0xCC,0x7F, +0x76,0xC4,0x7D,0xE4,0xB7,0x4B,0x66,0xEF,0x03,0x45,0x60,0x69,0xB6,0x0C,0x52,0x96, +0x92,0x84,0x5E,0xA6,0xA3,0xB5,0xA4,0x3E,0x2B,0xD9,0xCC,0xD8,0x1B,0x47,0xAA,0xF2, +0x44,0xDA,0x4F,0xF9,0x03,0xE8,0xF0,0x14,0xCB,0x3F,0xF3,0x83,0xDE,0xD0,0xC1,0x54, +0xE3,0xB7,0xE8,0x0A,0x37,0x4D,0x8B,0x20,0x59,0x03,0x30,0x19,0xA1,0x2C,0xC8,0xBD, +0x11,0x1F,0xDF,0xAE,0xC9,0x4A,0xC5,0xF3,0x27,0x66,0x66,0x86,0xAC,0x68,0x91,0xFF, +0xD9,0xE6,0x53,0x1C,0x0F,0x8B,0x5C,0x69,0x65,0x0A,0x26,0xC8,0x1E,0x34,0xC3,0x5D, +0x51,0x7B,0xD7,0xA9,0x9C,0x06,0xA1,0x36,0xDD,0xD5,0x89,0x94,0xBC,0xD9,0xE4,0x2D, +0x0C,0x5E,0x09,0x6C,0x08,0x97,0x7C,0xA3,0x3D,0x7C,0x93,0xFF,0x3F,0xA1,0x14,0xA7, +0xCF,0xB5,0x5D,0xEB,0xDB,0xDB,0x1C,0xC4,0x76,0xDF,0x88,0xB9,0xBD,0x45,0x05,0x95, +0x1B,0xAE,0xFC,0x46,0x6A,0x4C,0xAF,0x48,0xE3,0xCE,0xAE,0x0F,0xD2,0x7E,0xEB,0xE6, +0x6C,0x9C,0x4F,0x81,0x6A,0x7A,0x64,0xAC,0xBB,0x3E,0xD5,0xE7,0xCB,0x76,0x2E,0xC5, +0xA7,0x48,0xC1,0x5C,0x90,0x0F,0xCB,0xC8,0x3F,0xFA,0xE6,0x32,0xE1,0x8D,0x1B,0x6F, +0xA4,0xE6,0x8E,0xD8,0xF9,0x29,0x48,0x8A,0xCE,0x73,0xFE,0x2C, +}; + + +/* subject:/C=US/O=GeoTrust Inc./CN=GeoTrust Universal CA 2 */ +/* issuer :/C=US/O=GeoTrust Inc./CN=GeoTrust Universal CA 2 */ + + +const unsigned char GeoTrust_Universal_CA_2_certificate[1392]={ +0x30,0x82,0x05,0x6C,0x30,0x82,0x03,0x54,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30, +0x47,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x16, +0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D,0x47,0x65,0x6F,0x54,0x72,0x75,0x73, +0x74,0x20,0x49,0x6E,0x63,0x2E,0x31,0x20,0x30,0x1E,0x06,0x03,0x55,0x04,0x03,0x13, +0x17,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20,0x55,0x6E,0x69,0x76,0x65,0x72, +0x73,0x61,0x6C,0x20,0x43,0x41,0x20,0x32,0x30,0x1E,0x17,0x0D,0x30,0x34,0x30,0x33, +0x30,0x34,0x30,0x35,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,0x39,0x30,0x33,0x30, +0x34,0x30,0x35,0x30,0x30,0x30,0x30,0x5A,0x30,0x47,0x31,0x0B,0x30,0x09,0x06,0x03, +0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x0A, +0x13,0x0D,0x47,0x65,0x6F,0x54,0x72,0x75,0x73,0x74,0x20,0x49,0x6E,0x63,0x2E,0x31, +0x20,0x30,0x1E,0x06,0x03,0x55,0x04,0x03,0x13,0x17,0x47,0x65,0x6F,0x54,0x72,0x75, +0x73,0x74,0x20,0x55,0x6E,0x69,0x76,0x65,0x72,0x73,0x61,0x6C,0x20,0x43,0x41,0x20, +0x32,0x30,0x82,0x02,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01, +0x01,0x01,0x05,0x00,0x03,0x82,0x02,0x0F,0x00,0x30,0x82,0x02,0x0A,0x02,0x82,0x02, +0x01,0x00,0xB3,0x54,0x52,0xC1,0xC9,0x3E,0xF2,0xD9,0xDC,0xB1,0x53,0x1A,0x59,0x29, +0xE7,0xB1,0xC3,0x45,0x28,0xE5,0xD7,0xD1,0xED,0xC5,0xC5,0x4B,0xA1,0xAA,0x74,0x7B, +0x57,0xAF,0x4A,0x26,0xFC,0xD8,0xF5,0x5E,0xA7,0x6E,0x19,0xDB,0x74,0x0C,0x4F,0x35, +0x5B,0x32,0x0B,0x01,0xE3,0xDB,0xEB,0x7A,0x77,0x35,0xEA,0xAA,0x5A,0xE0,0xD6,0xE8, +0xA1,0x57,0x94,0xF0,0x90,0xA3,0x74,0x56,0x94,0x44,0x30,0x03,0x1E,0x5C,0x4E,0x2B, +0x85,0x26,0x74,0x82,0x7A,0x0C,0x76,0xA0,0x6F,0x4D,0xCE,0x41,0x2D,0xA0,0x15,0x06, +0x14,0x5F,0xB7,0x42,0xCD,0x7B,0x8F,0x58,0x61,0x34,0xDC,0x2A,0x08,0xF9,0x2E,0xC3, +0x01,0xA6,0x22,0x44,0x1C,0x4C,0x07,0x82,0xE6,0x5B,0xCE,0xD0,0x4A,0x7C,0x04,0xD3, +0x19,0x73,0x27,0xF0,0xAA,0x98,0x7F,0x2E,0xAF,0x4E,0xEB,0x87,0x1E,0x24,0x77,0x6A, +0x5D,0xB6,0xE8,0x5B,0x45,0xBA,0xDC,0xC3,0xA1,0x05,0x6F,0x56,0x8E,0x8F,0x10,0x26, +0xA5,0x49,0xC3,0x2E,0xD7,0x41,0x87,0x22,0xE0,0x4F,0x86,0xCA,0x60,0xB5,0xEA,0xA1, +0x63,0xC0,0x01,0x97,0x10,0x79,0xBD,0x00,0x3C,0x12,0x6D,0x2B,0x15,0xB1,0xAC,0x4B, +0xB1,0xEE,0x18,0xB9,0x4E,0x96,0xDC,0xDC,0x76,0xFF,0x3B,0xBE,0xCF,0x5F,0x03,0xC0, +0xFC,0x3B,0xE8,0xBE,0x46,0x1B,0xFF,0xDA,0x40,0xC2,0x52,0xF7,0xFE,0xE3,0x3A,0xF7, +0x6A,0x77,0x35,0xD0,0xDA,0x8D,0xEB,0x5E,0x18,0x6A,0x31,0xC7,0x1E,0xBA,0x3C,0x1B, +0x28,0xD6,0x6B,0x54,0xC6,0xAA,0x5B,0xD7,0xA2,0x2C,0x1B,0x19,0xCC,0xA2,0x02,0xF6, +0x9B,0x59,0xBD,0x37,0x6B,0x86,0xB5,0x6D,0x82,0xBA,0xD8,0xEA,0xC9,0x56,0xBC,0xA9, +0x36,0x58,0xFD,0x3E,0x19,0xF3,0xED,0x0C,0x26,0xA9,0x93,0x38,0xF8,0x4F,0xC1,0x5D, +0x22,0x06,0xD0,0x97,0xEA,0xE1,0xAD,0xC6,0x55,0xE0,0x81,0x2B,0x28,0x83,0x3A,0xFA, +0xF4,0x7B,0x21,0x51,0x00,0xBE,0x52,0x38,0xCE,0xCD,0x66,0x79,0xA8,0xF4,0x81,0x56, +0xE2,0xD0,0x83,0x09,0x47,0x51,0x5B,0x50,0x6A,0xCF,0xDB,0x48,0x1A,0x5D,0x3E,0xF7, +0xCB,0xF6,0x65,0xF7,0x6C,0xF1,0x95,0xF8,0x02,0x3B,0x32,0x56,0x82,0x39,0x7A,0x5B, +0xBD,0x2F,0x89,0x1B,0xBF,0xA1,0xB4,0xE8,0xFF,0x7F,0x8D,0x8C,0xDF,0x03,0xF1,0x60, +0x4E,0x58,0x11,0x4C,0xEB,0xA3,0x3F,0x10,0x2B,0x83,0x9A,0x01,0x73,0xD9,0x94,0x6D, +0x84,0x00,0x27,0x66,0xAC,0xF0,0x70,0x40,0x09,0x42,0x92,0xAD,0x4F,0x93,0x0D,0x61, +0x09,0x51,0x24,0xD8,0x92,0xD5,0x0B,0x94,0x61,0xB2,0x87,0xB2,0xED,0xFF,0x9A,0x35, +0xFF,0x85,0x54,0xCA,0xED,0x44,0x43,0xAC,0x1B,0x3C,0x16,0x6B,0x48,0x4A,0x0A,0x1C, +0x40,0x88,0x1F,0x92,0xC2,0x0B,0x00,0x05,0xFF,0xF2,0xC8,0x02,0x4A,0xA4,0xAA,0xA9, +0xCC,0x99,0x96,0x9C,0x2F,0x58,0xE0,0x7D,0xE1,0xBE,0xBB,0x07,0xDC,0x5F,0x04,0x72, +0x5C,0x31,0x34,0xC3,0xEC,0x5F,0x2D,0xE0,0x3D,0x64,0x90,0x22,0xE6,0xD1,0xEC,0xB8, +0x2E,0xDD,0x59,0xAE,0xD9,0xA1,0x37,0xBF,0x54,0x35,0xDC,0x73,0x32,0x4F,0x8C,0x04, +0x1E,0x33,0xB2,0xC9,0x46,0xF1,0xD8,0x5C,0xC8,0x55,0x50,0xC9,0x68,0xBD,0xA8,0xBA, +0x36,0x09,0x02,0x03,0x01,0x00,0x01,0xA3,0x63,0x30,0x61,0x30,0x0F,0x06,0x03,0x55, +0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03, +0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x76,0xF3,0x55,0xE1,0xFA,0xA4,0x36,0xFB,0xF0, +0x9F,0x5C,0x62,0x71,0xED,0x3C,0xF4,0x47,0x38,0x10,0x2B,0x30,0x1F,0x06,0x03,0x55, +0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0x76,0xF3,0x55,0xE1,0xFA,0xA4,0x36,0xFB, +0xF0,0x9F,0x5C,0x62,0x71,0xED,0x3C,0xF4,0x47,0x38,0x10,0x2B,0x30,0x0E,0x06,0x03, +0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x0D,0x06,0x09, +0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x02,0x01,0x00, +0x66,0xC1,0xC6,0x23,0xF3,0xD9,0xE0,0x2E,0x6E,0x5F,0xE8,0xCF,0xAE,0xB0,0xB0,0x25, +0x4D,0x2B,0xF8,0x3B,0x58,0x9B,0x40,0x24,0x37,0x5A,0xCB,0xAB,0x16,0x49,0xFF,0xB3, +0x75,0x79,0x33,0xA1,0x2F,0x6D,0x70,0x17,0x34,0x91,0xFE,0x67,0x7E,0x8F,0xEC,0x9B, +0xE5,0x5E,0x82,0xA9,0x55,0x1F,0x2F,0xDC,0xD4,0x51,0x07,0x12,0xFE,0xAC,0x16,0x3E, +0x2C,0x35,0xC6,0x63,0xFC,0xDC,0x10,0xEB,0x0D,0xA3,0xAA,0xD0,0x7C,0xCC,0xD1,0xD0, +0x2F,0x51,0x2E,0xC4,0x14,0x5A,0xDE,0xE8,0x19,0xE1,0x3E,0xC6,0xCC,0xA4,0x29,0xE7, +0x2E,0x84,0xAA,0x06,0x30,0x78,0x76,0x54,0x73,0x28,0x98,0x59,0x38,0xE0,0x00,0x0D, +0x62,0xD3,0x42,0x7D,0x21,0x9F,0xAE,0x3D,0x3A,0x8C,0xD5,0xFA,0x77,0x0D,0x18,0x2B, +0x16,0x0E,0x5F,0x36,0xE1,0xFC,0x2A,0xB5,0x30,0x24,0xCF,0xE0,0x63,0x0C,0x7B,0x58, +0x1A,0xFE,0x99,0xBA,0x42,0x12,0xB1,0x91,0xF4,0x7C,0x68,0xE2,0xC8,0xE8,0xAF,0x2C, +0xEA,0xC9,0x7E,0xAE,0xBB,0x2A,0x3D,0x0D,0x15,0xDC,0x34,0x95,0xB6,0x18,0x74,0xA8, +0x6A,0x0F,0xC7,0xB4,0xF4,0x13,0xC4,0xE4,0x5B,0xED,0x0A,0xD2,0xA4,0x97,0x4C,0x2A, +0xED,0x2F,0x6C,0x12,0x89,0x3D,0xF1,0x27,0x70,0xAA,0x6A,0x03,0x52,0x21,0x9F,0x40, +0xA8,0x67,0x50,0xF2,0xF3,0x5A,0x1F,0xDF,0xDF,0x23,0xF6,0xDC,0x78,0x4E,0xE6,0x98, +0x4F,0x55,0x3A,0x53,0xE3,0xEF,0xF2,0xF4,0x9F,0xC7,0x7C,0xD8,0x58,0xAF,0x29,0x22, +0x97,0xB8,0xE0,0xBD,0x91,0x2E,0xB0,0x76,0xEC,0x57,0x11,0xCF,0xEF,0x29,0x44,0xF3, +0xE9,0x85,0x7A,0x60,0x63,0xE4,0x5D,0x33,0x89,0x17,0xD9,0x31,0xAA,0xDA,0xD6,0xF3, +0x18,0x35,0x72,0xCF,0x87,0x2B,0x2F,0x63,0x23,0x84,0x5D,0x84,0x8C,0x3F,0x57,0xA0, +0x88,0xFC,0x99,0x91,0x28,0x26,0x69,0x99,0xD4,0x8F,0x97,0x44,0xBE,0x8E,0xD5,0x48, +0xB1,0xA4,0x28,0x29,0xF1,0x15,0xB4,0xE1,0xE5,0x9E,0xDD,0xF8,0x8F,0xA6,0x6F,0x26, +0xD7,0x09,0x3C,0x3A,0x1C,0x11,0x0E,0xA6,0x6C,0x37,0xF7,0xAD,0x44,0x87,0x2C,0x28, +0xC7,0xD8,0x74,0x82,0xB3,0xD0,0x6F,0x4A,0x57,0xBB,0x35,0x29,0x27,0xA0,0x8B,0xE8, +0x21,0xA7,0x87,0x64,0x36,0x5D,0xCC,0xD8,0x16,0xAC,0xC7,0xB2,0x27,0x40,0x92,0x55, +0x38,0x28,0x8D,0x51,0x6E,0xDD,0x14,0x67,0x53,0x6C,0x71,0x5C,0x26,0x84,0x4D,0x75, +0x5A,0xB6,0x7E,0x60,0x56,0xA9,0x4D,0xAD,0xFB,0x9B,0x1E,0x97,0xF3,0x0D,0xD9,0xD2, +0x97,0x54,0x77,0xDA,0x3D,0x12,0xB7,0xE0,0x1E,0xEF,0x08,0x06,0xAC,0xF9,0x85,0x87, +0xE9,0xA2,0xDC,0xAF,0x7E,0x18,0x12,0x83,0xFD,0x56,0x17,0x41,0x2E,0xD5,0x29,0x82, +0x7D,0x99,0xF4,0x31,0xF6,0x71,0xA9,0xCF,0x2C,0x01,0x27,0xA5,0x05,0xB9,0xAA,0xB2, +0x48,0x4E,0x2A,0xEF,0x9F,0x93,0x52,0x51,0x95,0x3C,0x52,0x73,0x8E,0x56,0x4C,0x17, +0x40,0xC0,0x09,0x28,0xE4,0x8B,0x6A,0x48,0x53,0xDB,0xEC,0xCD,0x55,0x55,0xF1,0xC6, +0xF8,0xE9,0xA2,0x2C,0x4C,0xA6,0xD1,0x26,0x5F,0x7E,0xAF,0x5A,0x4C,0xDA,0x1F,0xA6, +0xF2,0x1C,0x2C,0x7E,0xAE,0x02,0x16,0xD2,0x56,0xD0,0x2F,0x57,0x53,0x47,0xE8,0x92, +}; + + +/* subject:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA */ +/* issuer :/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA */ + + +const unsigned char GlobalSign_Root_CA_certificate[889]={ +0x30,0x82,0x03,0x75,0x30,0x82,0x02,0x5D,0xA0,0x03,0x02,0x01,0x02,0x02,0x0B,0x04, +0x00,0x00,0x00,0x00,0x01,0x15,0x4B,0x5A,0xC3,0x94,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x57,0x31,0x0B,0x30,0x09,0x06, +0x03,0x55,0x04,0x06,0x13,0x02,0x42,0x45,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04, +0x0A,0x13,0x10,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x20,0x6E,0x76, +0x2D,0x73,0x61,0x31,0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x0B,0x13,0x07,0x52,0x6F, +0x6F,0x74,0x20,0x43,0x41,0x31,0x1B,0x30,0x19,0x06,0x03,0x55,0x04,0x03,0x13,0x12, +0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x20,0x52,0x6F,0x6F,0x74,0x20, +0x43,0x41,0x30,0x1E,0x17,0x0D,0x39,0x38,0x30,0x39,0x30,0x31,0x31,0x32,0x30,0x30, +0x30,0x30,0x5A,0x17,0x0D,0x32,0x38,0x30,0x31,0x32,0x38,0x31,0x32,0x30,0x30,0x30, +0x30,0x5A,0x30,0x57,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x42, +0x45,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0A,0x13,0x10,0x47,0x6C,0x6F,0x62, +0x61,0x6C,0x53,0x69,0x67,0x6E,0x20,0x6E,0x76,0x2D,0x73,0x61,0x31,0x10,0x30,0x0E, +0x06,0x03,0x55,0x04,0x0B,0x13,0x07,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x31,0x1B, +0x30,0x19,0x06,0x03,0x55,0x04,0x03,0x13,0x12,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53, +0x69,0x67,0x6E,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x30,0x82,0x01,0x22,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82, +0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xDA,0x0E,0xE6,0x99, +0x8D,0xCE,0xA3,0xE3,0x4F,0x8A,0x7E,0xFB,0xF1,0x8B,0x83,0x25,0x6B,0xEA,0x48,0x1F, +0xF1,0x2A,0xB0,0xB9,0x95,0x11,0x04,0xBD,0xF0,0x63,0xD1,0xE2,0x67,0x66,0xCF,0x1C, +0xDD,0xCF,0x1B,0x48,0x2B,0xEE,0x8D,0x89,0x8E,0x9A,0xAF,0x29,0x80,0x65,0xAB,0xE9, +0xC7,0x2D,0x12,0xCB,0xAB,0x1C,0x4C,0x70,0x07,0xA1,0x3D,0x0A,0x30,0xCD,0x15,0x8D, +0x4F,0xF8,0xDD,0xD4,0x8C,0x50,0x15,0x1C,0xEF,0x50,0xEE,0xC4,0x2E,0xF7,0xFC,0xE9, +0x52,0xF2,0x91,0x7D,0xE0,0x6D,0xD5,0x35,0x30,0x8E,0x5E,0x43,0x73,0xF2,0x41,0xE9, +0xD5,0x6A,0xE3,0xB2,0x89,0x3A,0x56,0x39,0x38,0x6F,0x06,0x3C,0x88,0x69,0x5B,0x2A, +0x4D,0xC5,0xA7,0x54,0xB8,0x6C,0x89,0xCC,0x9B,0xF9,0x3C,0xCA,0xE5,0xFD,0x89,0xF5, +0x12,0x3C,0x92,0x78,0x96,0xD6,0xDC,0x74,0x6E,0x93,0x44,0x61,0xD1,0x8D,0xC7,0x46, +0xB2,0x75,0x0E,0x86,0xE8,0x19,0x8A,0xD5,0x6D,0x6C,0xD5,0x78,0x16,0x95,0xA2,0xE9, +0xC8,0x0A,0x38,0xEB,0xF2,0x24,0x13,0x4F,0x73,0x54,0x93,0x13,0x85,0x3A,0x1B,0xBC, +0x1E,0x34,0xB5,0x8B,0x05,0x8C,0xB9,0x77,0x8B,0xB1,0xDB,0x1F,0x20,0x91,0xAB,0x09, +0x53,0x6E,0x90,0xCE,0x7B,0x37,0x74,0xB9,0x70,0x47,0x91,0x22,0x51,0x63,0x16,0x79, +0xAE,0xB1,0xAE,0x41,0x26,0x08,0xC8,0x19,0x2B,0xD1,0x46,0xAA,0x48,0xD6,0x64,0x2A, +0xD7,0x83,0x34,0xFF,0x2C,0x2A,0xC1,0x6C,0x19,0x43,0x4A,0x07,0x85,0xE7,0xD3,0x7C, +0xF6,0x21,0x68,0xEF,0xEA,0xF2,0x52,0x9F,0x7F,0x93,0x90,0xCF,0x02,0x03,0x01,0x00, +0x01,0xA3,0x42,0x30,0x40,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04, +0x04,0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04, +0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04, +0x14,0x60,0x7B,0x66,0x1A,0x45,0x0D,0x97,0xCA,0x89,0x50,0x2F,0x7D,0x04,0xCD,0x34, +0xA8,0xFF,0xFC,0xFD,0x4B,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01, +0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0xD6,0x73,0xE7,0x7C,0x4F,0x76,0xD0, +0x8D,0xBF,0xEC,0xBA,0xA2,0xBE,0x34,0xC5,0x28,0x32,0xB5,0x7C,0xFC,0x6C,0x9C,0x2C, +0x2B,0xBD,0x09,0x9E,0x53,0xBF,0x6B,0x5E,0xAA,0x11,0x48,0xB6,0xE5,0x08,0xA3,0xB3, +0xCA,0x3D,0x61,0x4D,0xD3,0x46,0x09,0xB3,0x3E,0xC3,0xA0,0xE3,0x63,0x55,0x1B,0xF2, +0xBA,0xEF,0xAD,0x39,0xE1,0x43,0xB9,0x38,0xA3,0xE6,0x2F,0x8A,0x26,0x3B,0xEF,0xA0, +0x50,0x56,0xF9,0xC6,0x0A,0xFD,0x38,0xCD,0xC4,0x0B,0x70,0x51,0x94,0x97,0x98,0x04, +0xDF,0xC3,0x5F,0x94,0xD5,0x15,0xC9,0x14,0x41,0x9C,0xC4,0x5D,0x75,0x64,0x15,0x0D, +0xFF,0x55,0x30,0xEC,0x86,0x8F,0xFF,0x0D,0xEF,0x2C,0xB9,0x63,0x46,0xF6,0xAA,0xFC, +0xDF,0xBC,0x69,0xFD,0x2E,0x12,0x48,0x64,0x9A,0xE0,0x95,0xF0,0xA6,0xEF,0x29,0x8F, +0x01,0xB1,0x15,0xB5,0x0C,0x1D,0xA5,0xFE,0x69,0x2C,0x69,0x24,0x78,0x1E,0xB3,0xA7, +0x1C,0x71,0x62,0xEE,0xCA,0xC8,0x97,0xAC,0x17,0x5D,0x8A,0xC2,0xF8,0x47,0x86,0x6E, +0x2A,0xC4,0x56,0x31,0x95,0xD0,0x67,0x89,0x85,0x2B,0xF9,0x6C,0xA6,0x5D,0x46,0x9D, +0x0C,0xAA,0x82,0xE4,0x99,0x51,0xDD,0x70,0xB7,0xDB,0x56,0x3D,0x61,0xE4,0x6A,0xE1, +0x5C,0xD6,0xF6,0xFE,0x3D,0xDE,0x41,0xCC,0x07,0xAE,0x63,0x52,0xBF,0x53,0x53,0xF4, +0x2B,0xE9,0xC7,0xFD,0xB6,0xF7,0x82,0x5F,0x85,0xD2,0x41,0x18,0xDB,0x81,0xB3,0x04, +0x1C,0xC5,0x1F,0xA4,0x80,0x6F,0x15,0x20,0xC9,0xDE,0x0C,0x88,0x0A,0x1D,0xD6,0x66, +0x55,0xE2,0xFC,0x48,0xC9,0x29,0x26,0x69,0xE0, +}; + + +/* subject:/OU=GlobalSign Root CA - R2/O=GlobalSign/CN=GlobalSign */ +/* issuer :/OU=GlobalSign Root CA - R2/O=GlobalSign/CN=GlobalSign */ + + +const unsigned char GlobalSign_Root_CA___R2_certificate[958]={ +0x30,0x82,0x03,0xBA,0x30,0x82,0x02,0xA2,0xA0,0x03,0x02,0x01,0x02,0x02,0x0B,0x04, +0x00,0x00,0x00,0x00,0x01,0x0F,0x86,0x26,0xE6,0x0D,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x4C,0x31,0x20,0x30,0x1E,0x06, +0x03,0x55,0x04,0x0B,0x13,0x17,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E, +0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x20,0x2D,0x20,0x52,0x32,0x31,0x13,0x30, +0x11,0x06,0x03,0x55,0x04,0x0A,0x13,0x0A,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69, +0x67,0x6E,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0A,0x47,0x6C,0x6F, +0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x30,0x1E,0x17,0x0D,0x30,0x36,0x31,0x32,0x31, +0x35,0x30,0x38,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,0x31,0x31,0x32,0x31,0x35, +0x30,0x38,0x30,0x30,0x30,0x30,0x5A,0x30,0x4C,0x31,0x20,0x30,0x1E,0x06,0x03,0x55, +0x04,0x0B,0x13,0x17,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x20,0x52, +0x6F,0x6F,0x74,0x20,0x43,0x41,0x20,0x2D,0x20,0x52,0x32,0x31,0x13,0x30,0x11,0x06, +0x03,0x55,0x04,0x0A,0x13,0x0A,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E, +0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0A,0x47,0x6C,0x6F,0x62,0x61, +0x6C,0x53,0x69,0x67,0x6E,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48, +0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01, +0x0A,0x02,0x82,0x01,0x01,0x00,0xA6,0xCF,0x24,0x0E,0xBE,0x2E,0x6F,0x28,0x99,0x45, +0x42,0xC4,0xAB,0x3E,0x21,0x54,0x9B,0x0B,0xD3,0x7F,0x84,0x70,0xFA,0x12,0xB3,0xCB, +0xBF,0x87,0x5F,0xC6,0x7F,0x86,0xD3,0xB2,0x30,0x5C,0xD6,0xFD,0xAD,0xF1,0x7B,0xDC, +0xE5,0xF8,0x60,0x96,0x09,0x92,0x10,0xF5,0xD0,0x53,0xDE,0xFB,0x7B,0x7E,0x73,0x88, +0xAC,0x52,0x88,0x7B,0x4A,0xA6,0xCA,0x49,0xA6,0x5E,0xA8,0xA7,0x8C,0x5A,0x11,0xBC, +0x7A,0x82,0xEB,0xBE,0x8C,0xE9,0xB3,0xAC,0x96,0x25,0x07,0x97,0x4A,0x99,0x2A,0x07, +0x2F,0xB4,0x1E,0x77,0xBF,0x8A,0x0F,0xB5,0x02,0x7C,0x1B,0x96,0xB8,0xC5,0xB9,0x3A, +0x2C,0xBC,0xD6,0x12,0xB9,0xEB,0x59,0x7D,0xE2,0xD0,0x06,0x86,0x5F,0x5E,0x49,0x6A, +0xB5,0x39,0x5E,0x88,0x34,0xEC,0xBC,0x78,0x0C,0x08,0x98,0x84,0x6C,0xA8,0xCD,0x4B, +0xB4,0xA0,0x7D,0x0C,0x79,0x4D,0xF0,0xB8,0x2D,0xCB,0x21,0xCA,0xD5,0x6C,0x5B,0x7D, +0xE1,0xA0,0x29,0x84,0xA1,0xF9,0xD3,0x94,0x49,0xCB,0x24,0x62,0x91,0x20,0xBC,0xDD, +0x0B,0xD5,0xD9,0xCC,0xF9,0xEA,0x27,0x0A,0x2B,0x73,0x91,0xC6,0x9D,0x1B,0xAC,0xC8, +0xCB,0xE8,0xE0,0xA0,0xF4,0x2F,0x90,0x8B,0x4D,0xFB,0xB0,0x36,0x1B,0xF6,0x19,0x7A, +0x85,0xE0,0x6D,0xF2,0x61,0x13,0x88,0x5C,0x9F,0xE0,0x93,0x0A,0x51,0x97,0x8A,0x5A, +0xCE,0xAF,0xAB,0xD5,0xF7,0xAA,0x09,0xAA,0x60,0xBD,0xDC,0xD9,0x5F,0xDF,0x72,0xA9, +0x60,0x13,0x5E,0x00,0x01,0xC9,0x4A,0xFA,0x3F,0xA4,0xEA,0x07,0x03,0x21,0x02,0x8E, +0x82,0xCA,0x03,0xC2,0x9B,0x8F,0x02,0x03,0x01,0x00,0x01,0xA3,0x81,0x9C,0x30,0x81, +0x99,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01, +0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01, +0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x9B,0xE2,0x07, +0x57,0x67,0x1C,0x1E,0xC0,0x6A,0x06,0xDE,0x59,0xB4,0x9A,0x2D,0xDF,0xDC,0x19,0x86, +0x2E,0x30,0x36,0x06,0x03,0x55,0x1D,0x1F,0x04,0x2F,0x30,0x2D,0x30,0x2B,0xA0,0x29, +0xA0,0x27,0x86,0x25,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x2E,0x67, +0x6C,0x6F,0x62,0x61,0x6C,0x73,0x69,0x67,0x6E,0x2E,0x6E,0x65,0x74,0x2F,0x72,0x6F, +0x6F,0x74,0x2D,0x72,0x32,0x2E,0x63,0x72,0x6C,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23, +0x04,0x18,0x30,0x16,0x80,0x14,0x9B,0xE2,0x07,0x57,0x67,0x1C,0x1E,0xC0,0x6A,0x06, +0xDE,0x59,0xB4,0x9A,0x2D,0xDF,0xDC,0x19,0x86,0x2E,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x99,0x81, +0x53,0x87,0x1C,0x68,0x97,0x86,0x91,0xEC,0xE0,0x4A,0xB8,0x44,0x0B,0xAB,0x81,0xAC, +0x27,0x4F,0xD6,0xC1,0xB8,0x1C,0x43,0x78,0xB3,0x0C,0x9A,0xFC,0xEA,0x2C,0x3C,0x6E, +0x61,0x1B,0x4D,0x4B,0x29,0xF5,0x9F,0x05,0x1D,0x26,0xC1,0xB8,0xE9,0x83,0x00,0x62, +0x45,0xB6,0xA9,0x08,0x93,0xB9,0xA9,0x33,0x4B,0x18,0x9A,0xC2,0xF8,0x87,0x88,0x4E, +0xDB,0xDD,0x71,0x34,0x1A,0xC1,0x54,0xDA,0x46,0x3F,0xE0,0xD3,0x2A,0xAB,0x6D,0x54, +0x22,0xF5,0x3A,0x62,0xCD,0x20,0x6F,0xBA,0x29,0x89,0xD7,0xDD,0x91,0xEE,0xD3,0x5C, +0xA2,0x3E,0xA1,0x5B,0x41,0xF5,0xDF,0xE5,0x64,0x43,0x2D,0xE9,0xD5,0x39,0xAB,0xD2, +0xA2,0xDF,0xB7,0x8B,0xD0,0xC0,0x80,0x19,0x1C,0x45,0xC0,0x2D,0x8C,0xE8,0xF8,0x2D, +0xA4,0x74,0x56,0x49,0xC5,0x05,0xB5,0x4F,0x15,0xDE,0x6E,0x44,0x78,0x39,0x87,0xA8, +0x7E,0xBB,0xF3,0x79,0x18,0x91,0xBB,0xF4,0x6F,0x9D,0xC1,0xF0,0x8C,0x35,0x8C,0x5D, +0x01,0xFB,0xC3,0x6D,0xB9,0xEF,0x44,0x6D,0x79,0x46,0x31,0x7E,0x0A,0xFE,0xA9,0x82, +0xC1,0xFF,0xEF,0xAB,0x6E,0x20,0xC4,0x50,0xC9,0x5F,0x9D,0x4D,0x9B,0x17,0x8C,0x0C, +0xE5,0x01,0xC9,0xA0,0x41,0x6A,0x73,0x53,0xFA,0xA5,0x50,0xB4,0x6E,0x25,0x0F,0xFB, +0x4C,0x18,0xF4,0xFD,0x52,0xD9,0x8E,0x69,0xB1,0xE8,0x11,0x0F,0xDE,0x88,0xD8,0xFB, +0x1D,0x49,0xF7,0xAA,0xDE,0x95,0xCF,0x20,0x78,0xC2,0x60,0x12,0xDB,0x25,0x40,0x8C, +0x6A,0xFC,0x7E,0x42,0x38,0x40,0x64,0x12,0xF7,0x9E,0x81,0xE1,0x93,0x2E, +}; + + +/* subject:/OU=GlobalSign Root CA - R3/O=GlobalSign/CN=GlobalSign */ +/* issuer :/OU=GlobalSign Root CA - R3/O=GlobalSign/CN=GlobalSign */ + + +const unsigned char GlobalSign_Root_CA___R3_certificate[867]={ +0x30,0x82,0x03,0x5F,0x30,0x82,0x02,0x47,0xA0,0x03,0x02,0x01,0x02,0x02,0x0B,0x04, +0x00,0x00,0x00,0x00,0x01,0x21,0x58,0x53,0x08,0xA2,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,0x4C,0x31,0x20,0x30,0x1E,0x06, +0x03,0x55,0x04,0x0B,0x13,0x17,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E, +0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x20,0x2D,0x20,0x52,0x33,0x31,0x13,0x30, +0x11,0x06,0x03,0x55,0x04,0x0A,0x13,0x0A,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69, +0x67,0x6E,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0A,0x47,0x6C,0x6F, +0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x30,0x1E,0x17,0x0D,0x30,0x39,0x30,0x33,0x31, +0x38,0x31,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,0x39,0x30,0x33,0x31,0x38, +0x31,0x30,0x30,0x30,0x30,0x30,0x5A,0x30,0x4C,0x31,0x20,0x30,0x1E,0x06,0x03,0x55, +0x04,0x0B,0x13,0x17,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x20,0x52, +0x6F,0x6F,0x74,0x20,0x43,0x41,0x20,0x2D,0x20,0x52,0x33,0x31,0x13,0x30,0x11,0x06, +0x03,0x55,0x04,0x0A,0x13,0x0A,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E, +0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0A,0x47,0x6C,0x6F,0x62,0x61, +0x6C,0x53,0x69,0x67,0x6E,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48, +0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01, +0x0A,0x02,0x82,0x01,0x01,0x00,0xCC,0x25,0x76,0x90,0x79,0x06,0x78,0x22,0x16,0xF5, +0xC0,0x83,0xB6,0x84,0xCA,0x28,0x9E,0xFD,0x05,0x76,0x11,0xC5,0xAD,0x88,0x72,0xFC, +0x46,0x02,0x43,0xC7,0xB2,0x8A,0x9D,0x04,0x5F,0x24,0xCB,0x2E,0x4B,0xE1,0x60,0x82, +0x46,0xE1,0x52,0xAB,0x0C,0x81,0x47,0x70,0x6C,0xDD,0x64,0xD1,0xEB,0xF5,0x2C,0xA3, +0x0F,0x82,0x3D,0x0C,0x2B,0xAE,0x97,0xD7,0xB6,0x14,0x86,0x10,0x79,0xBB,0x3B,0x13, +0x80,0x77,0x8C,0x08,0xE1,0x49,0xD2,0x6A,0x62,0x2F,0x1F,0x5E,0xFA,0x96,0x68,0xDF, +0x89,0x27,0x95,0x38,0x9F,0x06,0xD7,0x3E,0xC9,0xCB,0x26,0x59,0x0D,0x73,0xDE,0xB0, +0xC8,0xE9,0x26,0x0E,0x83,0x15,0xC6,0xEF,0x5B,0x8B,0xD2,0x04,0x60,0xCA,0x49,0xA6, +0x28,0xF6,0x69,0x3B,0xF6,0xCB,0xC8,0x28,0x91,0xE5,0x9D,0x8A,0x61,0x57,0x37,0xAC, +0x74,0x14,0xDC,0x74,0xE0,0x3A,0xEE,0x72,0x2F,0x2E,0x9C,0xFB,0xD0,0xBB,0xBF,0xF5, +0x3D,0x00,0xE1,0x06,0x33,0xE8,0x82,0x2B,0xAE,0x53,0xA6,0x3A,0x16,0x73,0x8C,0xDD, +0x41,0x0E,0x20,0x3A,0xC0,0xB4,0xA7,0xA1,0xE9,0xB2,0x4F,0x90,0x2E,0x32,0x60,0xE9, +0x57,0xCB,0xB9,0x04,0x92,0x68,0x68,0xE5,0x38,0x26,0x60,0x75,0xB2,0x9F,0x77,0xFF, +0x91,0x14,0xEF,0xAE,0x20,0x49,0xFC,0xAD,0x40,0x15,0x48,0xD1,0x02,0x31,0x61,0x19, +0x5E,0xB8,0x97,0xEF,0xAD,0x77,0xB7,0x64,0x9A,0x7A,0xBF,0x5F,0xC1,0x13,0xEF,0x9B, +0x62,0xFB,0x0D,0x6C,0xE0,0x54,0x69,0x16,0xA9,0x03,0xDA,0x6E,0xE9,0x83,0x93,0x71, +0x76,0xC6,0x69,0x85,0x82,0x17,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30, +0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30, +0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF, +0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x8F,0xF0,0x4B,0x7F,0xA8, +0x2E,0x45,0x24,0xAE,0x4D,0x50,0xFA,0x63,0x9A,0x8B,0xDE,0xE2,0xDD,0x1B,0xBC,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82, +0x01,0x01,0x00,0x4B,0x40,0xDB,0xC0,0x50,0xAA,0xFE,0xC8,0x0C,0xEF,0xF7,0x96,0x54, +0x45,0x49,0xBB,0x96,0x00,0x09,0x41,0xAC,0xB3,0x13,0x86,0x86,0x28,0x07,0x33,0xCA, +0x6B,0xE6,0x74,0xB9,0xBA,0x00,0x2D,0xAE,0xA4,0x0A,0xD3,0xF5,0xF1,0xF1,0x0F,0x8A, +0xBF,0x73,0x67,0x4A,0x83,0xC7,0x44,0x7B,0x78,0xE0,0xAF,0x6E,0x6C,0x6F,0x03,0x29, +0x8E,0x33,0x39,0x45,0xC3,0x8E,0xE4,0xB9,0x57,0x6C,0xAA,0xFC,0x12,0x96,0xEC,0x53, +0xC6,0x2D,0xE4,0x24,0x6C,0xB9,0x94,0x63,0xFB,0xDC,0x53,0x68,0x67,0x56,0x3E,0x83, +0xB8,0xCF,0x35,0x21,0xC3,0xC9,0x68,0xFE,0xCE,0xDA,0xC2,0x53,0xAA,0xCC,0x90,0x8A, +0xE9,0xF0,0x5D,0x46,0x8C,0x95,0xDD,0x7A,0x58,0x28,0x1A,0x2F,0x1D,0xDE,0xCD,0x00, +0x37,0x41,0x8F,0xED,0x44,0x6D,0xD7,0x53,0x28,0x97,0x7E,0xF3,0x67,0x04,0x1E,0x15, +0xD7,0x8A,0x96,0xB4,0xD3,0xDE,0x4C,0x27,0xA4,0x4C,0x1B,0x73,0x73,0x76,0xF4,0x17, +0x99,0xC2,0x1F,0x7A,0x0E,0xE3,0x2D,0x08,0xAD,0x0A,0x1C,0x2C,0xFF,0x3C,0xAB,0x55, +0x0E,0x0F,0x91,0x7E,0x36,0xEB,0xC3,0x57,0x49,0xBE,0xE1,0x2E,0x2D,0x7C,0x60,0x8B, +0xC3,0x41,0x51,0x13,0x23,0x9D,0xCE,0xF7,0x32,0x6B,0x94,0x01,0xA8,0x99,0xE7,0x2C, +0x33,0x1F,0x3A,0x3B,0x25,0xD2,0x86,0x40,0xCE,0x3B,0x2C,0x86,0x78,0xC9,0x61,0x2F, +0x14,0xBA,0xEE,0xDB,0x55,0x6F,0xDF,0x84,0xEE,0x05,0x09,0x4D,0xBD,0x28,0xD8,0x72, +0xCE,0xD3,0x62,0x50,0x65,0x1E,0xEB,0x92,0x97,0x83,0x31,0xD9,0xB3,0xB5,0xCA,0x47, +0x58,0x3F,0x5F, +}; + + +/* subject:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority */ +/* issuer :/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority */ + + +const unsigned char Go_Daddy_Class_2_CA_certificate[1028]={ +0x30,0x82,0x04,0x00,0x30,0x82,0x02,0xE8,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x00, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30, +0x63,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x21, +0x30,0x1F,0x06,0x03,0x55,0x04,0x0A,0x13,0x18,0x54,0x68,0x65,0x20,0x47,0x6F,0x20, +0x44,0x61,0x64,0x64,0x79,0x20,0x47,0x72,0x6F,0x75,0x70,0x2C,0x20,0x49,0x6E,0x63, +0x2E,0x31,0x31,0x30,0x2F,0x06,0x03,0x55,0x04,0x0B,0x13,0x28,0x47,0x6F,0x20,0x44, +0x61,0x64,0x64,0x79,0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x32,0x20,0x43,0x65,0x72, +0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F, +0x72,0x69,0x74,0x79,0x30,0x1E,0x17,0x0D,0x30,0x34,0x30,0x36,0x32,0x39,0x31,0x37, +0x30,0x36,0x32,0x30,0x5A,0x17,0x0D,0x33,0x34,0x30,0x36,0x32,0x39,0x31,0x37,0x30, +0x36,0x32,0x30,0x5A,0x30,0x63,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13, +0x02,0x55,0x53,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x0A,0x13,0x18,0x54,0x68, +0x65,0x20,0x47,0x6F,0x20,0x44,0x61,0x64,0x64,0x79,0x20,0x47,0x72,0x6F,0x75,0x70, +0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x31,0x30,0x2F,0x06,0x03,0x55,0x04,0x0B,0x13, +0x28,0x47,0x6F,0x20,0x44,0x61,0x64,0x64,0x79,0x20,0x43,0x6C,0x61,0x73,0x73,0x20, +0x32,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20, +0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x82,0x01,0x20,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0D, +0x00,0x30,0x82,0x01,0x08,0x02,0x82,0x01,0x01,0x00,0xDE,0x9D,0xD7,0xEA,0x57,0x18, +0x49,0xA1,0x5B,0xEB,0xD7,0x5F,0x48,0x86,0xEA,0xBE,0xDD,0xFF,0xE4,0xEF,0x67,0x1C, +0xF4,0x65,0x68,0xB3,0x57,0x71,0xA0,0x5E,0x77,0xBB,0xED,0x9B,0x49,0xE9,0x70,0x80, +0x3D,0x56,0x18,0x63,0x08,0x6F,0xDA,0xF2,0xCC,0xD0,0x3F,0x7F,0x02,0x54,0x22,0x54, +0x10,0xD8,0xB2,0x81,0xD4,0xC0,0x75,0x3D,0x4B,0x7F,0xC7,0x77,0xC3,0x3E,0x78,0xAB, +0x1A,0x03,0xB5,0x20,0x6B,0x2F,0x6A,0x2B,0xB1,0xC5,0x88,0x7E,0xC4,0xBB,0x1E,0xB0, +0xC1,0xD8,0x45,0x27,0x6F,0xAA,0x37,0x58,0xF7,0x87,0x26,0xD7,0xD8,0x2D,0xF6,0xA9, +0x17,0xB7,0x1F,0x72,0x36,0x4E,0xA6,0x17,0x3F,0x65,0x98,0x92,0xDB,0x2A,0x6E,0x5D, +0xA2,0xFE,0x88,0xE0,0x0B,0xDE,0x7F,0xE5,0x8D,0x15,0xE1,0xEB,0xCB,0x3A,0xD5,0xE2, +0x12,0xA2,0x13,0x2D,0xD8,0x8E,0xAF,0x5F,0x12,0x3D,0xA0,0x08,0x05,0x08,0xB6,0x5C, +0xA5,0x65,0x38,0x04,0x45,0x99,0x1E,0xA3,0x60,0x60,0x74,0xC5,0x41,0xA5,0x72,0x62, +0x1B,0x62,0xC5,0x1F,0x6F,0x5F,0x1A,0x42,0xBE,0x02,0x51,0x65,0xA8,0xAE,0x23,0x18, +0x6A,0xFC,0x78,0x03,0xA9,0x4D,0x7F,0x80,0xC3,0xFA,0xAB,0x5A,0xFC,0xA1,0x40,0xA4, +0xCA,0x19,0x16,0xFE,0xB2,0xC8,0xEF,0x5E,0x73,0x0D,0xEE,0x77,0xBD,0x9A,0xF6,0x79, +0x98,0xBC,0xB1,0x07,0x67,0xA2,0x15,0x0D,0xDD,0xA0,0x58,0xC6,0x44,0x7B,0x0A,0x3E, +0x62,0x28,0x5F,0xBA,0x41,0x07,0x53,0x58,0xCF,0x11,0x7E,0x38,0x74,0xC5,0xF8,0xFF, +0xB5,0x69,0x90,0x8F,0x84,0x74,0xEA,0x97,0x1B,0xAF,0x02,0x01,0x03,0xA3,0x81,0xC0, +0x30,0x81,0xBD,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xD2,0xC4, +0xB0,0xD2,0x91,0xD4,0x4C,0x11,0x71,0xB3,0x61,0xCB,0x3D,0xA1,0xFE,0xDD,0xA8,0x6A, +0xD4,0xE3,0x30,0x81,0x8D,0x06,0x03,0x55,0x1D,0x23,0x04,0x81,0x85,0x30,0x81,0x82, +0x80,0x14,0xD2,0xC4,0xB0,0xD2,0x91,0xD4,0x4C,0x11,0x71,0xB3,0x61,0xCB,0x3D,0xA1, +0xFE,0xDD,0xA8,0x6A,0xD4,0xE3,0xA1,0x67,0xA4,0x65,0x30,0x63,0x31,0x0B,0x30,0x09, +0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x21,0x30,0x1F,0x06,0x03,0x55, +0x04,0x0A,0x13,0x18,0x54,0x68,0x65,0x20,0x47,0x6F,0x20,0x44,0x61,0x64,0x64,0x79, +0x20,0x47,0x72,0x6F,0x75,0x70,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x31,0x30,0x2F, +0x06,0x03,0x55,0x04,0x0B,0x13,0x28,0x47,0x6F,0x20,0x44,0x61,0x64,0x64,0x79,0x20, +0x43,0x6C,0x61,0x73,0x73,0x20,0x32,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63, +0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x82, +0x01,0x00,0x30,0x0C,0x06,0x03,0x55,0x1D,0x13,0x04,0x05,0x30,0x03,0x01,0x01,0xFF, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03, +0x82,0x01,0x01,0x00,0x32,0x4B,0xF3,0xB2,0xCA,0x3E,0x91,0xFC,0x12,0xC6,0xA1,0x07, +0x8C,0x8E,0x77,0xA0,0x33,0x06,0x14,0x5C,0x90,0x1E,0x18,0xF7,0x08,0xA6,0x3D,0x0A, +0x19,0xF9,0x87,0x80,0x11,0x6E,0x69,0xE4,0x96,0x17,0x30,0xFF,0x34,0x91,0x63,0x72, +0x38,0xEE,0xCC,0x1C,0x01,0xA3,0x1D,0x94,0x28,0xA4,0x31,0xF6,0x7A,0xC4,0x54,0xD7, +0xF6,0xE5,0x31,0x58,0x03,0xA2,0xCC,0xCE,0x62,0xDB,0x94,0x45,0x73,0xB5,0xBF,0x45, +0xC9,0x24,0xB5,0xD5,0x82,0x02,0xAD,0x23,0x79,0x69,0x8D,0xB8,0xB6,0x4D,0xCE,0xCF, +0x4C,0xCA,0x33,0x23,0xE8,0x1C,0x88,0xAA,0x9D,0x8B,0x41,0x6E,0x16,0xC9,0x20,0xE5, +0x89,0x9E,0xCD,0x3B,0xDA,0x70,0xF7,0x7E,0x99,0x26,0x20,0x14,0x54,0x25,0xAB,0x6E, +0x73,0x85,0xE6,0x9B,0x21,0x9D,0x0A,0x6C,0x82,0x0E,0xA8,0xF8,0xC2,0x0C,0xFA,0x10, +0x1E,0x6C,0x96,0xEF,0x87,0x0D,0xC4,0x0F,0x61,0x8B,0xAD,0xEE,0x83,0x2B,0x95,0xF8, +0x8E,0x92,0x84,0x72,0x39,0xEB,0x20,0xEA,0x83,0xED,0x83,0xCD,0x97,0x6E,0x08,0xBC, +0xEB,0x4E,0x26,0xB6,0x73,0x2B,0xE4,0xD3,0xF6,0x4C,0xFE,0x26,0x71,0xE2,0x61,0x11, +0x74,0x4A,0xFF,0x57,0x1A,0x87,0x0F,0x75,0x48,0x2E,0xCF,0x51,0x69,0x17,0xA0,0x02, +0x12,0x61,0x95,0xD5,0xD1,0x40,0xB2,0x10,0x4C,0xEE,0xC4,0xAC,0x10,0x43,0xA6,0xA5, +0x9E,0x0A,0xD5,0x95,0x62,0x9A,0x0D,0xCF,0x88,0x82,0xC5,0x32,0x0C,0xE4,0x2B,0x9F, +0x45,0xE6,0x0D,0x9F,0x28,0x9C,0xB1,0xB9,0x2A,0x5A,0x57,0xAD,0x37,0x0F,0xAF,0x1D, +0x7F,0xDB,0xBD,0x9F, +}; + + +/* subject:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./CN=Go Daddy Root Certificate Authority - G2 */ +/* issuer :/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./CN=Go Daddy Root Certificate Authority - G2 */ + + +const unsigned char Go_Daddy_Root_Certificate_Authority___G2_certificate[969]={ +0x30,0x82,0x03,0xC5,0x30,0x82,0x02,0xAD,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x00, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30, +0x81,0x83,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31, +0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x08,0x13,0x07,0x41,0x72,0x69,0x7A,0x6F,0x6E, +0x61,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x07,0x13,0x0A,0x53,0x63,0x6F,0x74, +0x74,0x73,0x64,0x61,0x6C,0x65,0x31,0x1A,0x30,0x18,0x06,0x03,0x55,0x04,0x0A,0x13, +0x11,0x47,0x6F,0x44,0x61,0x64,0x64,0x79,0x2E,0x63,0x6F,0x6D,0x2C,0x20,0x49,0x6E, +0x63,0x2E,0x31,0x31,0x30,0x2F,0x06,0x03,0x55,0x04,0x03,0x13,0x28,0x47,0x6F,0x20, +0x44,0x61,0x64,0x64,0x79,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x65,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79, +0x20,0x2D,0x20,0x47,0x32,0x30,0x1E,0x17,0x0D,0x30,0x39,0x30,0x39,0x30,0x31,0x30, +0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x37,0x31,0x32,0x33,0x31,0x32,0x33, +0x35,0x39,0x35,0x39,0x5A,0x30,0x81,0x83,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04, +0x06,0x13,0x02,0x55,0x53,0x31,0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x08,0x13,0x07, +0x41,0x72,0x69,0x7A,0x6F,0x6E,0x61,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x07, +0x13,0x0A,0x53,0x63,0x6F,0x74,0x74,0x73,0x64,0x61,0x6C,0x65,0x31,0x1A,0x30,0x18, +0x06,0x03,0x55,0x04,0x0A,0x13,0x11,0x47,0x6F,0x44,0x61,0x64,0x64,0x79,0x2E,0x63, +0x6F,0x6D,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x31,0x30,0x2F,0x06,0x03,0x55,0x04, +0x03,0x13,0x28,0x47,0x6F,0x20,0x44,0x61,0x64,0x64,0x79,0x20,0x52,0x6F,0x6F,0x74, +0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x65,0x20,0x41,0x75,0x74, +0x68,0x6F,0x72,0x69,0x74,0x79,0x20,0x2D,0x20,0x47,0x32,0x30,0x82,0x01,0x22,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82, +0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xBF,0x71,0x62,0x08, +0xF1,0xFA,0x59,0x34,0xF7,0x1B,0xC9,0x18,0xA3,0xF7,0x80,0x49,0x58,0xE9,0x22,0x83, +0x13,0xA6,0xC5,0x20,0x43,0x01,0x3B,0x84,0xF1,0xE6,0x85,0x49,0x9F,0x27,0xEA,0xF6, +0x84,0x1B,0x4E,0xA0,0xB4,0xDB,0x70,0x98,0xC7,0x32,0x01,0xB1,0x05,0x3E,0x07,0x4E, +0xEE,0xF4,0xFA,0x4F,0x2F,0x59,0x30,0x22,0xE7,0xAB,0x19,0x56,0x6B,0xE2,0x80,0x07, +0xFC,0xF3,0x16,0x75,0x80,0x39,0x51,0x7B,0xE5,0xF9,0x35,0xB6,0x74,0x4E,0xA9,0x8D, +0x82,0x13,0xE4,0xB6,0x3F,0xA9,0x03,0x83,0xFA,0xA2,0xBE,0x8A,0x15,0x6A,0x7F,0xDE, +0x0B,0xC3,0xB6,0x19,0x14,0x05,0xCA,0xEA,0xC3,0xA8,0x04,0x94,0x3B,0x46,0x7C,0x32, +0x0D,0xF3,0x00,0x66,0x22,0xC8,0x8D,0x69,0x6D,0x36,0x8C,0x11,0x18,0xB7,0xD3,0xB2, +0x1C,0x60,0xB4,0x38,0xFA,0x02,0x8C,0xCE,0xD3,0xDD,0x46,0x07,0xDE,0x0A,0x3E,0xEB, +0x5D,0x7C,0xC8,0x7C,0xFB,0xB0,0x2B,0x53,0xA4,0x92,0x62,0x69,0x51,0x25,0x05,0x61, +0x1A,0x44,0x81,0x8C,0x2C,0xA9,0x43,0x96,0x23,0xDF,0xAC,0x3A,0x81,0x9A,0x0E,0x29, +0xC5,0x1C,0xA9,0xE9,0x5D,0x1E,0xB6,0x9E,0x9E,0x30,0x0A,0x39,0xCE,0xF1,0x88,0x80, +0xFB,0x4B,0x5D,0xCC,0x32,0xEC,0x85,0x62,0x43,0x25,0x34,0x02,0x56,0x27,0x01,0x91, +0xB4,0x3B,0x70,0x2A,0x3F,0x6E,0xB1,0xE8,0x9C,0x88,0x01,0x7D,0x9F,0xD4,0xF9,0xDB, +0x53,0x6D,0x60,0x9D,0xBF,0x2C,0xE7,0x58,0xAB,0xB8,0x5F,0x46,0xFC,0xCE,0xC4,0x1B, +0x03,0x3C,0x09,0xEB,0x49,0x31,0x5C,0x69,0x46,0xB3,0xE0,0x47,0x02,0x03,0x01,0x00, +0x01,0xA3,0x42,0x30,0x40,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04, +0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF, +0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04, +0x14,0x3A,0x9A,0x85,0x07,0x10,0x67,0x28,0xB6,0xEF,0xF6,0xBD,0x05,0x41,0x6E,0x20, +0xC1,0x94,0xDA,0x0F,0xDE,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01, +0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x99,0xDB,0x5D,0x79,0xD5,0xF9,0x97, +0x59,0x67,0x03,0x61,0xF1,0x7E,0x3B,0x06,0x31,0x75,0x2D,0xA1,0x20,0x8E,0x4F,0x65, +0x87,0xB4,0xF7,0xA6,0x9C,0xBC,0xD8,0xE9,0x2F,0xD0,0xDB,0x5A,0xEE,0xCF,0x74,0x8C, +0x73,0xB4,0x38,0x42,0xDA,0x05,0x7B,0xF8,0x02,0x75,0xB8,0xFD,0xA5,0xB1,0xD7,0xAE, +0xF6,0xD7,0xDE,0x13,0xCB,0x53,0x10,0x7E,0x8A,0x46,0xD1,0x97,0xFA,0xB7,0x2E,0x2B, +0x11,0xAB,0x90,0xB0,0x27,0x80,0xF9,0xE8,0x9F,0x5A,0xE9,0x37,0x9F,0xAB,0xE4,0xDF, +0x6C,0xB3,0x85,0x17,0x9D,0x3D,0xD9,0x24,0x4F,0x79,0x91,0x35,0xD6,0x5F,0x04,0xEB, +0x80,0x83,0xAB,0x9A,0x02,0x2D,0xB5,0x10,0xF4,0xD8,0x90,0xC7,0x04,0x73,0x40,0xED, +0x72,0x25,0xA0,0xA9,0x9F,0xEC,0x9E,0xAB,0x68,0x12,0x99,0x57,0xC6,0x8F,0x12,0x3A, +0x09,0xA4,0xBD,0x44,0xFD,0x06,0x15,0x37,0xC1,0x9B,0xE4,0x32,0xA3,0xED,0x38,0xE8, +0xD8,0x64,0xF3,0x2C,0x7E,0x14,0xFC,0x02,0xEA,0x9F,0xCD,0xFF,0x07,0x68,0x17,0xDB, +0x22,0x90,0x38,0x2D,0x7A,0x8D,0xD1,0x54,0xF1,0x69,0xE3,0x5F,0x33,0xCA,0x7A,0x3D, +0x7B,0x0A,0xE3,0xCA,0x7F,0x5F,0x39,0xE5,0xE2,0x75,0xBA,0xC5,0x76,0x18,0x33,0xCE, +0x2C,0xF0,0x2F,0x4C,0xAD,0xF7,0xB1,0xE7,0xCE,0x4F,0xA8,0xC4,0x9B,0x4A,0x54,0x06, +0xC5,0x7F,0x7D,0xD5,0x08,0x0F,0xE2,0x1C,0xFE,0x7E,0x17,0xB8,0xAC,0x5E,0xF6,0xD4, +0x16,0xB2,0x43,0x09,0x0C,0x4D,0xF6,0xA7,0x6B,0xB4,0x99,0x84,0x65,0xCA,0x7A,0x88, +0xE2,0xE2,0x44,0xBE,0x5C,0xF7,0xEA,0x1C,0xF5, +}; + + +/* subject:/C=US/O=GTE Corporation/OU=GTE CyberTrust Solutions, Inc./CN=GTE CyberTrust Global Root */ +/* issuer :/C=US/O=GTE Corporation/OU=GTE CyberTrust Solutions, Inc./CN=GTE CyberTrust Global Root */ + + +const unsigned char GTE_CyberTrust_Global_Root_certificate[606]={ +0x30,0x82,0x02,0x5A,0x30,0x82,0x01,0xC3,0x02,0x02,0x01,0xA5,0x30,0x0D,0x06,0x09, +0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x04,0x05,0x00,0x30,0x75,0x31,0x0B,0x30, +0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x18,0x30,0x16,0x06,0x03, +0x55,0x04,0x0A,0x13,0x0F,0x47,0x54,0x45,0x20,0x43,0x6F,0x72,0x70,0x6F,0x72,0x61, +0x74,0x69,0x6F,0x6E,0x31,0x27,0x30,0x25,0x06,0x03,0x55,0x04,0x0B,0x13,0x1E,0x47, +0x54,0x45,0x20,0x43,0x79,0x62,0x65,0x72,0x54,0x72,0x75,0x73,0x74,0x20,0x53,0x6F, +0x6C,0x75,0x74,0x69,0x6F,0x6E,0x73,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x23,0x30, +0x21,0x06,0x03,0x55,0x04,0x03,0x13,0x1A,0x47,0x54,0x45,0x20,0x43,0x79,0x62,0x65, +0x72,0x54,0x72,0x75,0x73,0x74,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x52,0x6F, +0x6F,0x74,0x30,0x1E,0x17,0x0D,0x39,0x38,0x30,0x38,0x31,0x33,0x30,0x30,0x32,0x39, +0x30,0x30,0x5A,0x17,0x0D,0x31,0x38,0x30,0x38,0x31,0x33,0x32,0x33,0x35,0x39,0x30, +0x30,0x5A,0x30,0x75,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55, +0x53,0x31,0x18,0x30,0x16,0x06,0x03,0x55,0x04,0x0A,0x13,0x0F,0x47,0x54,0x45,0x20, +0x43,0x6F,0x72,0x70,0x6F,0x72,0x61,0x74,0x69,0x6F,0x6E,0x31,0x27,0x30,0x25,0x06, +0x03,0x55,0x04,0x0B,0x13,0x1E,0x47,0x54,0x45,0x20,0x43,0x79,0x62,0x65,0x72,0x54, +0x72,0x75,0x73,0x74,0x20,0x53,0x6F,0x6C,0x75,0x74,0x69,0x6F,0x6E,0x73,0x2C,0x20, +0x49,0x6E,0x63,0x2E,0x31,0x23,0x30,0x21,0x06,0x03,0x55,0x04,0x03,0x13,0x1A,0x47, +0x54,0x45,0x20,0x43,0x79,0x62,0x65,0x72,0x54,0x72,0x75,0x73,0x74,0x20,0x47,0x6C, +0x6F,0x62,0x61,0x6C,0x20,0x52,0x6F,0x6F,0x74,0x30,0x81,0x9F,0x30,0x0D,0x06,0x09, +0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x81,0x8D,0x00,0x30, +0x81,0x89,0x02,0x81,0x81,0x00,0x95,0x0F,0xA0,0xB6,0xF0,0x50,0x9C,0xE8,0x7A,0xC7, +0x88,0xCD,0xDD,0x17,0x0E,0x2E,0xB0,0x94,0xD0,0x1B,0x3D,0x0E,0xF6,0x94,0xC0,0x8A, +0x94,0xC7,0x06,0xC8,0x90,0x97,0xC8,0xB8,0x64,0x1A,0x7A,0x7E,0x6C,0x3C,0x53,0xE1, +0x37,0x28,0x73,0x60,0x7F,0xB2,0x97,0x53,0x07,0x9F,0x53,0xF9,0x6D,0x58,0x94,0xD2, +0xAF,0x8D,0x6D,0x88,0x67,0x80,0xE6,0xED,0xB2,0x95,0xCF,0x72,0x31,0xCA,0xA5,0x1C, +0x72,0xBA,0x5C,0x02,0xE7,0x64,0x42,0xE7,0xF9,0xA9,0x2C,0xD6,0x3A,0x0D,0xAC,0x8D, +0x42,0xAA,0x24,0x01,0x39,0xE6,0x9C,0x3F,0x01,0x85,0x57,0x0D,0x58,0x87,0x45,0xF8, +0xD3,0x85,0xAA,0x93,0x69,0x26,0x85,0x70,0x48,0x80,0x3F,0x12,0x15,0xC7,0x79,0xB4, +0x1F,0x05,0x2F,0x3B,0x62,0x99,0x02,0x03,0x01,0x00,0x01,0x30,0x0D,0x06,0x09,0x2A, +0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x04,0x05,0x00,0x03,0x81,0x81,0x00,0x6D,0xEB, +0x1B,0x09,0xE9,0x5E,0xD9,0x51,0xDB,0x67,0x22,0x61,0xA4,0x2A,0x3C,0x48,0x77,0xE3, +0xA0,0x7C,0xA6,0xDE,0x73,0xA2,0x14,0x03,0x85,0x3D,0xFB,0xAB,0x0E,0x30,0xC5,0x83, +0x16,0x33,0x81,0x13,0x08,0x9E,0x7B,0x34,0x4E,0xDF,0x40,0xC8,0x74,0xD7,0xB9,0x7D, +0xDC,0xF4,0x76,0x55,0x7D,0x9B,0x63,0x54,0x18,0xE9,0xF0,0xEA,0xF3,0x5C,0xB1,0xD9, +0x8B,0x42,0x1E,0xB9,0xC0,0x95,0x4E,0xBA,0xFA,0xD5,0xE2,0x7C,0xF5,0x68,0x61,0xBF, +0x8E,0xEC,0x05,0x97,0x5F,0x5B,0xB0,0xD7,0xA3,0x85,0x34,0xC4,0x24,0xA7,0x0D,0x0F, +0x95,0x93,0xEF,0xCB,0x94,0xD8,0x9E,0x1F,0x9D,0x5C,0x85,0x6D,0xC7,0xAA,0xAE,0x4F, +0x1F,0x22,0xB5,0xCD,0x95,0xAD,0xBA,0xA7,0xCC,0xF9,0xAB,0x0B,0x7A,0x7F, +}; + + +/* subject:/C=US/O=Network Solutions L.L.C./CN=Network Solutions Certificate Authority */ +/* issuer :/C=US/O=Network Solutions L.L.C./CN=Network Solutions Certificate Authority */ + + +const unsigned char Network_Solutions_Certificate_Authority_certificate[1002]={ +0x30,0x82,0x03,0xE6,0x30,0x82,0x02,0xCE,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x57, +0xCB,0x33,0x6F,0xC2,0x5C,0x16,0xE6,0x47,0x16,0x17,0xE3,0x90,0x31,0x68,0xE0,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x62, +0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x21,0x30, +0x1F,0x06,0x03,0x55,0x04,0x0A,0x13,0x18,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x20, +0x53,0x6F,0x6C,0x75,0x74,0x69,0x6F,0x6E,0x73,0x20,0x4C,0x2E,0x4C,0x2E,0x43,0x2E, +0x31,0x30,0x30,0x2E,0x06,0x03,0x55,0x04,0x03,0x13,0x27,0x4E,0x65,0x74,0x77,0x6F, +0x72,0x6B,0x20,0x53,0x6F,0x6C,0x75,0x74,0x69,0x6F,0x6E,0x73,0x20,0x43,0x65,0x72, +0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x65,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69, +0x74,0x79,0x30,0x1E,0x17,0x0D,0x30,0x36,0x31,0x32,0x30,0x31,0x30,0x30,0x30,0x30, +0x30,0x30,0x5A,0x17,0x0D,0x32,0x39,0x31,0x32,0x33,0x31,0x32,0x33,0x35,0x39,0x35, +0x39,0x5A,0x30,0x62,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55, +0x53,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x0A,0x13,0x18,0x4E,0x65,0x74,0x77, +0x6F,0x72,0x6B,0x20,0x53,0x6F,0x6C,0x75,0x74,0x69,0x6F,0x6E,0x73,0x20,0x4C,0x2E, +0x4C,0x2E,0x43,0x2E,0x31,0x30,0x30,0x2E,0x06,0x03,0x55,0x04,0x03,0x13,0x27,0x4E, +0x65,0x74,0x77,0x6F,0x72,0x6B,0x20,0x53,0x6F,0x6C,0x75,0x74,0x69,0x6F,0x6E,0x73, +0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x65,0x20,0x41,0x75,0x74, +0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82, +0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xE4,0xBC,0x7E,0x92,0x30,0x6D,0xC6,0xD8,0x8E, +0x2B,0x0B,0xBC,0x46,0xCE,0xE0,0x27,0x96,0xDE,0xDE,0xF9,0xFA,0x12,0xD3,0x3C,0x33, +0x73,0xB3,0x04,0x2F,0xBC,0x71,0x8C,0xE5,0x9F,0xB6,0x22,0x60,0x3E,0x5F,0x5D,0xCE, +0x09,0xFF,0x82,0x0C,0x1B,0x9A,0x51,0x50,0x1A,0x26,0x89,0xDD,0xD5,0x61,0x5D,0x19, +0xDC,0x12,0x0F,0x2D,0x0A,0xA2,0x43,0x5D,0x17,0xD0,0x34,0x92,0x20,0xEA,0x73,0xCF, +0x38,0x2C,0x06,0x26,0x09,0x7A,0x72,0xF7,0xFA,0x50,0x32,0xF8,0xC2,0x93,0xD3,0x69, +0xA2,0x23,0xCE,0x41,0xB1,0xCC,0xE4,0xD5,0x1F,0x36,0xD1,0x8A,0x3A,0xF8,0x8C,0x63, +0xE2,0x14,0x59,0x69,0xED,0x0D,0xD3,0x7F,0x6B,0xE8,0xB8,0x03,0xE5,0x4F,0x6A,0xE5, +0x98,0x63,0x69,0x48,0x05,0xBE,0x2E,0xFF,0x33,0xB6,0xE9,0x97,0x59,0x69,0xF8,0x67, +0x19,0xAE,0x93,0x61,0x96,0x44,0x15,0xD3,0x72,0xB0,0x3F,0xBC,0x6A,0x7D,0xEC,0x48, +0x7F,0x8D,0xC3,0xAB,0xAA,0x71,0x2B,0x53,0x69,0x41,0x53,0x34,0xB5,0xB0,0xB9,0xC5, +0x06,0x0A,0xC4,0xB0,0x45,0xF5,0x41,0x5D,0x6E,0x89,0x45,0x7B,0x3D,0x3B,0x26,0x8C, +0x74,0xC2,0xE5,0xD2,0xD1,0x7D,0xB2,0x11,0xD4,0xFB,0x58,0x32,0x22,0x9A,0x80,0xC9, +0xDC,0xFD,0x0C,0xE9,0x7F,0x5E,0x03,0x97,0xCE,0x3B,0x00,0x14,0x87,0x27,0x70,0x38, +0xA9,0x8E,0x6E,0xB3,0x27,0x76,0x98,0x51,0xE0,0x05,0xE3,0x21,0xAB,0x1A,0xD5,0x85, +0x22,0x3C,0x29,0xB5,0x9A,0x16,0xC5,0x80,0xA8,0xF4,0xBB,0x6B,0x30,0x8F,0x2F,0x46, +0x02,0xA2,0xB1,0x0C,0x22,0xE0,0xD3,0x02,0x03,0x01,0x00,0x01,0xA3,0x81,0x97,0x30, +0x81,0x94,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x21,0x30,0xC9, +0xFB,0x00,0xD7,0x4E,0x98,0xDA,0x87,0xAA,0x2A,0xD0,0xA7,0x2E,0xB1,0x40,0x31,0xA7, +0x4C,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01, +0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01, +0x01,0xFF,0x30,0x52,0x06,0x03,0x55,0x1D,0x1F,0x04,0x4B,0x30,0x49,0x30,0x47,0xA0, +0x45,0xA0,0x43,0x86,0x41,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x2E, +0x6E,0x65,0x74,0x73,0x6F,0x6C,0x73,0x73,0x6C,0x2E,0x63,0x6F,0x6D,0x2F,0x4E,0x65, +0x74,0x77,0x6F,0x72,0x6B,0x53,0x6F,0x6C,0x75,0x74,0x69,0x6F,0x6E,0x73,0x43,0x65, +0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x65,0x41,0x75,0x74,0x68,0x6F,0x72,0x69, +0x74,0x79,0x2E,0x63,0x72,0x6C,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D, +0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0xBB,0xAE,0x4B,0xE7,0xB7,0x57, +0xEB,0x7F,0xAA,0x2D,0xB7,0x73,0x47,0x85,0x6A,0xC1,0xE4,0xA5,0x1D,0xE4,0xE7,0x3C, +0xE9,0xF4,0x59,0x65,0x77,0xB5,0x7A,0x5B,0x5A,0x8D,0x25,0x36,0xE0,0x7A,0x97,0x2E, +0x38,0xC0,0x57,0x60,0x83,0x98,0x06,0x83,0x9F,0xB9,0x76,0x7A,0x6E,0x50,0xE0,0xBA, +0x88,0x2C,0xFC,0x45,0xCC,0x18,0xB0,0x99,0x95,0x51,0x0E,0xEC,0x1D,0xB8,0x88,0xFF, +0x87,0x50,0x1C,0x82,0xC2,0xE3,0xE0,0x32,0x80,0xBF,0xA0,0x0B,0x47,0xC8,0xC3,0x31, +0xEF,0x99,0x67,0x32,0x80,0x4F,0x17,0x21,0x79,0x0C,0x69,0x5C,0xDE,0x5E,0x34,0xAE, +0x02,0xB5,0x26,0xEA,0x50,0xDF,0x7F,0x18,0x65,0x2C,0xC9,0xF2,0x63,0xE1,0xA9,0x07, +0xFE,0x7C,0x71,0x1F,0x6B,0x33,0x24,0x6A,0x1E,0x05,0xF7,0x05,0x68,0xC0,0x6A,0x12, +0xCB,0x2E,0x5E,0x61,0xCB,0xAE,0x28,0xD3,0x7E,0xC2,0xB4,0x66,0x91,0x26,0x5F,0x3C, +0x2E,0x24,0x5F,0xCB,0x58,0x0F,0xEB,0x28,0xEC,0xAF,0x11,0x96,0xF3,0xDC,0x7B,0x6F, +0xC0,0xA7,0x88,0xF2,0x53,0x77,0xB3,0x60,0x5E,0xAE,0xAE,0x28,0xDA,0x35,0x2C,0x6F, +0x34,0x45,0xD3,0x26,0xE1,0xDE,0xEC,0x5B,0x4F,0x27,0x6B,0x16,0x7C,0xBD,0x44,0x04, +0x18,0x82,0xB3,0x89,0x79,0x17,0x10,0x71,0x3D,0x7A,0xA2,0x16,0x4E,0xF5,0x01,0xCD, +0xA4,0x6C,0x65,0x68,0xA1,0x49,0x76,0x5C,0x43,0xC9,0xD8,0xBC,0x36,0x67,0x6C,0xA5, +0x94,0xB5,0xD4,0xCC,0xB9,0xBD,0x6A,0x35,0x56,0x21,0xDE,0xD8,0xC3,0xEB,0xFB,0xCB, +0xA4,0x60,0x4C,0xB0,0x55,0xA0,0xA0,0x7B,0x57,0xB2, +}; + + +/* subject:/L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 3 Policy Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com */ +/* issuer :/L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 3 Policy Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com */ + + +const unsigned char RSA_Root_Certificate_1_certificate[747]={ +0x30,0x82,0x02,0xE7,0x30,0x82,0x02,0x50,0x02,0x01,0x01,0x30,0x0D,0x06,0x09,0x2A, +0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x81,0xBB,0x31,0x24,0x30, +0x22,0x06,0x03,0x55,0x04,0x07,0x13,0x1B,0x56,0x61,0x6C,0x69,0x43,0x65,0x72,0x74, +0x20,0x56,0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x4E,0x65,0x74,0x77, +0x6F,0x72,0x6B,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x0A,0x13,0x0E,0x56,0x61, +0x6C,0x69,0x43,0x65,0x72,0x74,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x35,0x30,0x33, +0x06,0x03,0x55,0x04,0x0B,0x13,0x2C,0x56,0x61,0x6C,0x69,0x43,0x65,0x72,0x74,0x20, +0x43,0x6C,0x61,0x73,0x73,0x20,0x33,0x20,0x50,0x6F,0x6C,0x69,0x63,0x79,0x20,0x56, +0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72, +0x69,0x74,0x79,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x03,0x13,0x18,0x68,0x74, +0x74,0x70,0x3A,0x2F,0x2F,0x77,0x77,0x77,0x2E,0x76,0x61,0x6C,0x69,0x63,0x65,0x72, +0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x31,0x20,0x30,0x1E,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x09,0x01,0x16,0x11,0x69,0x6E,0x66,0x6F,0x40,0x76,0x61,0x6C,0x69, +0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x30,0x1E,0x17,0x0D,0x39,0x39,0x30,0x36, +0x32,0x36,0x30,0x30,0x32,0x32,0x33,0x33,0x5A,0x17,0x0D,0x31,0x39,0x30,0x36,0x32, +0x36,0x30,0x30,0x32,0x32,0x33,0x33,0x5A,0x30,0x81,0xBB,0x31,0x24,0x30,0x22,0x06, +0x03,0x55,0x04,0x07,0x13,0x1B,0x56,0x61,0x6C,0x69,0x43,0x65,0x72,0x74,0x20,0x56, +0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72, +0x6B,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x0A,0x13,0x0E,0x56,0x61,0x6C,0x69, +0x43,0x65,0x72,0x74,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x35,0x30,0x33,0x06,0x03, +0x55,0x04,0x0B,0x13,0x2C,0x56,0x61,0x6C,0x69,0x43,0x65,0x72,0x74,0x20,0x43,0x6C, +0x61,0x73,0x73,0x20,0x33,0x20,0x50,0x6F,0x6C,0x69,0x63,0x79,0x20,0x56,0x61,0x6C, +0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74, +0x79,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x03,0x13,0x18,0x68,0x74,0x74,0x70, +0x3A,0x2F,0x2F,0x77,0x77,0x77,0x2E,0x76,0x61,0x6C,0x69,0x63,0x65,0x72,0x74,0x2E, +0x63,0x6F,0x6D,0x2F,0x31,0x20,0x30,0x1E,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D, +0x01,0x09,0x01,0x16,0x11,0x69,0x6E,0x66,0x6F,0x40,0x76,0x61,0x6C,0x69,0x63,0x65, +0x72,0x74,0x2E,0x63,0x6F,0x6D,0x30,0x81,0x9F,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48, +0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x81,0x8D,0x00,0x30,0x81,0x89,0x02, +0x81,0x81,0x00,0xE3,0x98,0x51,0x96,0x1C,0xE8,0xD5,0xB1,0x06,0x81,0x6A,0x57,0xC3, +0x72,0x75,0x93,0xAB,0xCF,0x9E,0xA6,0xFC,0xF3,0x16,0x52,0xD6,0x2D,0x4D,0x9F,0x35, +0x44,0xA8,0x2E,0x04,0x4D,0x07,0x49,0x8A,0x38,0x29,0xF5,0x77,0x37,0xE7,0xB7,0xAB, +0x5D,0xDF,0x36,0x71,0x14,0x99,0x8F,0xDC,0xC2,0x92,0xF1,0xE7,0x60,0x92,0x97,0xEC, +0xD8,0x48,0xDC,0xBF,0xC1,0x02,0x20,0xC6,0x24,0xA4,0x28,0x4C,0x30,0x5A,0x76,0x6D, +0xB1,0x5C,0xF3,0xDD,0xDE,0x9E,0x10,0x71,0xA1,0x88,0xC7,0x5B,0x9B,0x41,0x6D,0xCA, +0xB0,0xB8,0x8E,0x15,0xEE,0xAD,0x33,0x2B,0xCF,0x47,0x04,0x5C,0x75,0x71,0x0A,0x98, +0x24,0x98,0x29,0xA7,0x49,0x59,0xA5,0xDD,0xF8,0xB7,0x43,0x62,0x61,0xF3,0xD3,0xE2, +0xD0,0x55,0x3F,0x02,0x03,0x01,0x00,0x01,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x81,0x81,0x00,0x56,0xBB,0x02,0x58,0x84, +0x67,0x08,0x2C,0xDF,0x1F,0xDB,0x7B,0x49,0x33,0xF5,0xD3,0x67,0x9D,0xF4,0xB4,0x0A, +0x10,0xB3,0xC9,0xC5,0x2C,0xE2,0x92,0x6A,0x71,0x78,0x27,0xF2,0x70,0x83,0x42,0xD3, +0x3E,0xCF,0xA9,0x54,0xF4,0xF1,0xD8,0x92,0x16,0x8C,0xD1,0x04,0xCB,0x4B,0xAB,0xC9, +0x9F,0x45,0xAE,0x3C,0x8A,0xA9,0xB0,0x71,0x33,0x5D,0xC8,0xC5,0x57,0xDF,0xAF,0xA8, +0x35,0xB3,0x7F,0x89,0x87,0xE9,0xE8,0x25,0x92,0xB8,0x7F,0x85,0x7A,0xAE,0xD6,0xBC, +0x1E,0x37,0x58,0x2A,0x67,0xC9,0x91,0xCF,0x2A,0x81,0x3E,0xED,0xC6,0x39,0xDF,0xC0, +0x3E,0x19,0x9C,0x19,0xCC,0x13,0x4D,0x82,0x41,0xB5,0x8C,0xDE,0xE0,0x3D,0x60,0x08, +0x20,0x0F,0x45,0x7E,0x6B,0xA2,0x7F,0xA3,0x8C,0x15,0xEE, +}; + + +/* subject:/C=US/O=Starfield Technologies, Inc./OU=Starfield Class 2 Certification Authority */ +/* issuer :/C=US/O=Starfield Technologies, Inc./OU=Starfield Class 2 Certification Authority */ + + +const unsigned char Starfield_Class_2_CA_certificate[1043]={ +0x30,0x82,0x04,0x0F,0x30,0x82,0x02,0xF7,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x00, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30, +0x68,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x25, +0x30,0x23,0x06,0x03,0x55,0x04,0x0A,0x13,0x1C,0x53,0x74,0x61,0x72,0x66,0x69,0x65, +0x6C,0x64,0x20,0x54,0x65,0x63,0x68,0x6E,0x6F,0x6C,0x6F,0x67,0x69,0x65,0x73,0x2C, +0x20,0x49,0x6E,0x63,0x2E,0x31,0x32,0x30,0x30,0x06,0x03,0x55,0x04,0x0B,0x13,0x29, +0x53,0x74,0x61,0x72,0x66,0x69,0x65,0x6C,0x64,0x20,0x43,0x6C,0x61,0x73,0x73,0x20, +0x32,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20, +0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x1E,0x17,0x0D,0x30,0x34,0x30, +0x36,0x32,0x39,0x31,0x37,0x33,0x39,0x31,0x36,0x5A,0x17,0x0D,0x33,0x34,0x30,0x36, +0x32,0x39,0x31,0x37,0x33,0x39,0x31,0x36,0x5A,0x30,0x68,0x31,0x0B,0x30,0x09,0x06, +0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04, +0x0A,0x13,0x1C,0x53,0x74,0x61,0x72,0x66,0x69,0x65,0x6C,0x64,0x20,0x54,0x65,0x63, +0x68,0x6E,0x6F,0x6C,0x6F,0x67,0x69,0x65,0x73,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31, +0x32,0x30,0x30,0x06,0x03,0x55,0x04,0x0B,0x13,0x29,0x53,0x74,0x61,0x72,0x66,0x69, +0x65,0x6C,0x64,0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x32,0x20,0x43,0x65,0x72,0x74, +0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72, +0x69,0x74,0x79,0x30,0x82,0x01,0x20,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0D,0x00,0x30,0x82,0x01,0x08,0x02, +0x82,0x01,0x01,0x00,0xB7,0x32,0xC8,0xFE,0xE9,0x71,0xA6,0x04,0x85,0xAD,0x0C,0x11, +0x64,0xDF,0xCE,0x4D,0xEF,0xC8,0x03,0x18,0x87,0x3F,0xA1,0xAB,0xFB,0x3C,0xA6,0x9F, +0xF0,0xC3,0xA1,0xDA,0xD4,0xD8,0x6E,0x2B,0x53,0x90,0xFB,0x24,0xA4,0x3E,0x84,0xF0, +0x9E,0xE8,0x5F,0xEC,0xE5,0x27,0x44,0xF5,0x28,0xA6,0x3F,0x7B,0xDE,0xE0,0x2A,0xF0, +0xC8,0xAF,0x53,0x2F,0x9E,0xCA,0x05,0x01,0x93,0x1E,0x8F,0x66,0x1C,0x39,0xA7,0x4D, +0xFA,0x5A,0xB6,0x73,0x04,0x25,0x66,0xEB,0x77,0x7F,0xE7,0x59,0xC6,0x4A,0x99,0x25, +0x14,0x54,0xEB,0x26,0xC7,0xF3,0x7F,0x19,0xD5,0x30,0x70,0x8F,0xAF,0xB0,0x46,0x2A, +0xFF,0xAD,0xEB,0x29,0xED,0xD7,0x9F,0xAA,0x04,0x87,0xA3,0xD4,0xF9,0x89,0xA5,0x34, +0x5F,0xDB,0x43,0x91,0x82,0x36,0xD9,0x66,0x3C,0xB1,0xB8,0xB9,0x82,0xFD,0x9C,0x3A, +0x3E,0x10,0xC8,0x3B,0xEF,0x06,0x65,0x66,0x7A,0x9B,0x19,0x18,0x3D,0xFF,0x71,0x51, +0x3C,0x30,0x2E,0x5F,0xBE,0x3D,0x77,0x73,0xB2,0x5D,0x06,0x6C,0xC3,0x23,0x56,0x9A, +0x2B,0x85,0x26,0x92,0x1C,0xA7,0x02,0xB3,0xE4,0x3F,0x0D,0xAF,0x08,0x79,0x82,0xB8, +0x36,0x3D,0xEA,0x9C,0xD3,0x35,0xB3,0xBC,0x69,0xCA,0xF5,0xCC,0x9D,0xE8,0xFD,0x64, +0x8D,0x17,0x80,0x33,0x6E,0x5E,0x4A,0x5D,0x99,0xC9,0x1E,0x87,0xB4,0x9D,0x1A,0xC0, +0xD5,0x6E,0x13,0x35,0x23,0x5E,0xDF,0x9B,0x5F,0x3D,0xEF,0xD6,0xF7,0x76,0xC2,0xEA, +0x3E,0xBB,0x78,0x0D,0x1C,0x42,0x67,0x6B,0x04,0xD8,0xF8,0xD6,0xDA,0x6F,0x8B,0xF2, +0x44,0xA0,0x01,0xAB,0x02,0x01,0x03,0xA3,0x81,0xC5,0x30,0x81,0xC2,0x30,0x1D,0x06, +0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xBF,0x5F,0xB7,0xD1,0xCE,0xDD,0x1F,0x86, +0xF4,0x5B,0x55,0xAC,0xDC,0xD7,0x10,0xC2,0x0E,0xA9,0x88,0xE7,0x30,0x81,0x92,0x06, +0x03,0x55,0x1D,0x23,0x04,0x81,0x8A,0x30,0x81,0x87,0x80,0x14,0xBF,0x5F,0xB7,0xD1, +0xCE,0xDD,0x1F,0x86,0xF4,0x5B,0x55,0xAC,0xDC,0xD7,0x10,0xC2,0x0E,0xA9,0x88,0xE7, +0xA1,0x6C,0xA4,0x6A,0x30,0x68,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13, +0x02,0x55,0x53,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x0A,0x13,0x1C,0x53,0x74, +0x61,0x72,0x66,0x69,0x65,0x6C,0x64,0x20,0x54,0x65,0x63,0x68,0x6E,0x6F,0x6C,0x6F, +0x67,0x69,0x65,0x73,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x32,0x30,0x30,0x06,0x03, +0x55,0x04,0x0B,0x13,0x29,0x53,0x74,0x61,0x72,0x66,0x69,0x65,0x6C,0x64,0x20,0x43, +0x6C,0x61,0x73,0x73,0x20,0x32,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61, +0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x82,0x01, +0x00,0x30,0x0C,0x06,0x03,0x55,0x1D,0x13,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82, +0x01,0x01,0x00,0x05,0x9D,0x3F,0x88,0x9D,0xD1,0xC9,0x1A,0x55,0xA1,0xAC,0x69,0xF3, +0xF3,0x59,0xDA,0x9B,0x01,0x87,0x1A,0x4F,0x57,0xA9,0xA1,0x79,0x09,0x2A,0xDB,0xF7, +0x2F,0xB2,0x1E,0xCC,0xC7,0x5E,0x6A,0xD8,0x83,0x87,0xA1,0x97,0xEF,0x49,0x35,0x3E, +0x77,0x06,0x41,0x58,0x62,0xBF,0x8E,0x58,0xB8,0x0A,0x67,0x3F,0xEC,0xB3,0xDD,0x21, +0x66,0x1F,0xC9,0x54,0xFA,0x72,0xCC,0x3D,0x4C,0x40,0xD8,0x81,0xAF,0x77,0x9E,0x83, +0x7A,0xBB,0xA2,0xC7,0xF5,0x34,0x17,0x8E,0xD9,0x11,0x40,0xF4,0xFC,0x2C,0x2A,0x4D, +0x15,0x7F,0xA7,0x62,0x5D,0x2E,0x25,0xD3,0x00,0x0B,0x20,0x1A,0x1D,0x68,0xF9,0x17, +0xB8,0xF4,0xBD,0x8B,0xED,0x28,0x59,0xDD,0x4D,0x16,0x8B,0x17,0x83,0xC8,0xB2,0x65, +0xC7,0x2D,0x7A,0xA5,0xAA,0xBC,0x53,0x86,0x6D,0xDD,0x57,0xA4,0xCA,0xF8,0x20,0x41, +0x0B,0x68,0xF0,0xF4,0xFB,0x74,0xBE,0x56,0x5D,0x7A,0x79,0xF5,0xF9,0x1D,0x85,0xE3, +0x2D,0x95,0xBE,0xF5,0x71,0x90,0x43,0xCC,0x8D,0x1F,0x9A,0x00,0x0A,0x87,0x29,0xE9, +0x55,0x22,0x58,0x00,0x23,0xEA,0xE3,0x12,0x43,0x29,0x5B,0x47,0x08,0xDD,0x8C,0x41, +0x6A,0x65,0x06,0xA8,0xE5,0x21,0xAA,0x41,0xB4,0x95,0x21,0x95,0xB9,0x7D,0xD1,0x34, +0xAB,0x13,0xD6,0xAD,0xBC,0xDC,0xE2,0x3D,0x39,0xCD,0xBD,0x3E,0x75,0x70,0xA1,0x18, +0x59,0x03,0xC9,0x22,0xB4,0x8F,0x9C,0xD5,0x5E,0x2A,0xD7,0xA5,0xB6,0xD4,0x0A,0x6D, +0xF8,0xB7,0x40,0x11,0x46,0x9A,0x1F,0x79,0x0E,0x62,0xBF,0x0F,0x97,0xEC,0xE0,0x2F, +0x1F,0x17,0x94, +}; + + +/* subject:/C=US/ST=Arizona/L=Scottsdale/O=Starfield Technologies, Inc./CN=Starfield Root Certificate Authority - G2 */ +/* issuer :/C=US/ST=Arizona/L=Scottsdale/O=Starfield Technologies, Inc./CN=Starfield Root Certificate Authority - G2 */ + + +const unsigned char Starfield_Root_Certificate_Authority___G2_certificate[993]={ +0x30,0x82,0x03,0xDD,0x30,0x82,0x02,0xC5,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x00, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30, +0x81,0x8F,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31, +0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x08,0x13,0x07,0x41,0x72,0x69,0x7A,0x6F,0x6E, +0x61,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x07,0x13,0x0A,0x53,0x63,0x6F,0x74, +0x74,0x73,0x64,0x61,0x6C,0x65,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x0A,0x13, +0x1C,0x53,0x74,0x61,0x72,0x66,0x69,0x65,0x6C,0x64,0x20,0x54,0x65,0x63,0x68,0x6E, +0x6F,0x6C,0x6F,0x67,0x69,0x65,0x73,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x32,0x30, +0x30,0x06,0x03,0x55,0x04,0x03,0x13,0x29,0x53,0x74,0x61,0x72,0x66,0x69,0x65,0x6C, +0x64,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61, +0x74,0x65,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x20,0x2D,0x20,0x47, +0x32,0x30,0x1E,0x17,0x0D,0x30,0x39,0x30,0x39,0x30,0x31,0x30,0x30,0x30,0x30,0x30, +0x30,0x5A,0x17,0x0D,0x33,0x37,0x31,0x32,0x33,0x31,0x32,0x33,0x35,0x39,0x35,0x39, +0x5A,0x30,0x81,0x8F,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55, +0x53,0x31,0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x08,0x13,0x07,0x41,0x72,0x69,0x7A, +0x6F,0x6E,0x61,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x07,0x13,0x0A,0x53,0x63, +0x6F,0x74,0x74,0x73,0x64,0x61,0x6C,0x65,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04, +0x0A,0x13,0x1C,0x53,0x74,0x61,0x72,0x66,0x69,0x65,0x6C,0x64,0x20,0x54,0x65,0x63, +0x68,0x6E,0x6F,0x6C,0x6F,0x67,0x69,0x65,0x73,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31, +0x32,0x30,0x30,0x06,0x03,0x55,0x04,0x03,0x13,0x29,0x53,0x74,0x61,0x72,0x66,0x69, +0x65,0x6C,0x64,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69, +0x63,0x61,0x74,0x65,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x20,0x2D, +0x20,0x47,0x32,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02, +0x82,0x01,0x01,0x00,0xBD,0xED,0xC1,0x03,0xFC,0xF6,0x8F,0xFC,0x02,0xB1,0x6F,0x5B, +0x9F,0x48,0xD9,0x9D,0x79,0xE2,0xA2,0xB7,0x03,0x61,0x56,0x18,0xC3,0x47,0xB6,0xD7, +0xCA,0x3D,0x35,0x2E,0x89,0x43,0xF7,0xA1,0x69,0x9B,0xDE,0x8A,0x1A,0xFD,0x13,0x20, +0x9C,0xB4,0x49,0x77,0x32,0x29,0x56,0xFD,0xB9,0xEC,0x8C,0xDD,0x22,0xFA,0x72,0xDC, +0x27,0x61,0x97,0xEE,0xF6,0x5A,0x84,0xEC,0x6E,0x19,0xB9,0x89,0x2C,0xDC,0x84,0x5B, +0xD5,0x74,0xFB,0x6B,0x5F,0xC5,0x89,0xA5,0x10,0x52,0x89,0x46,0x55,0xF4,0xB8,0x75, +0x1C,0xE6,0x7F,0xE4,0x54,0xAE,0x4B,0xF8,0x55,0x72,0x57,0x02,0x19,0xF8,0x17,0x71, +0x59,0xEB,0x1E,0x28,0x07,0x74,0xC5,0x9D,0x48,0xBE,0x6C,0xB4,0xF4,0xA4,0xB0,0xF3, +0x64,0x37,0x79,0x92,0xC0,0xEC,0x46,0x5E,0x7F,0xE1,0x6D,0x53,0x4C,0x62,0xAF,0xCD, +0x1F,0x0B,0x63,0xBB,0x3A,0x9D,0xFB,0xFC,0x79,0x00,0x98,0x61,0x74,0xCF,0x26,0x82, +0x40,0x63,0xF3,0xB2,0x72,0x6A,0x19,0x0D,0x99,0xCA,0xD4,0x0E,0x75,0xCC,0x37,0xFB, +0x8B,0x89,0xC1,0x59,0xF1,0x62,0x7F,0x5F,0xB3,0x5F,0x65,0x30,0xF8,0xA7,0xB7,0x4D, +0x76,0x5A,0x1E,0x76,0x5E,0x34,0xC0,0xE8,0x96,0x56,0x99,0x8A,0xB3,0xF0,0x7F,0xA4, +0xCD,0xBD,0xDC,0x32,0x31,0x7C,0x91,0xCF,0xE0,0x5F,0x11,0xF8,0x6B,0xAA,0x49,0x5C, +0xD1,0x99,0x94,0xD1,0xA2,0xE3,0x63,0x5B,0x09,0x76,0xB5,0x56,0x62,0xE1,0x4B,0x74, +0x1D,0x96,0xD4,0x26,0xD4,0x08,0x04,0x59,0xD0,0x98,0x0E,0x0E,0xE6,0xDE,0xFC,0xC3, +0xEC,0x1F,0x90,0xF1,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x0F,0x06, +0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E, +0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x1D, +0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x7C,0x0C,0x32,0x1F,0xA7,0xD9,0x30, +0x7F,0xC4,0x7D,0x68,0xA3,0x62,0xA8,0xA1,0xCE,0xAB,0x07,0x5B,0x27,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01, +0x00,0x11,0x59,0xFA,0x25,0x4F,0x03,0x6F,0x94,0x99,0x3B,0x9A,0x1F,0x82,0x85,0x39, +0xD4,0x76,0x05,0x94,0x5E,0xE1,0x28,0x93,0x6D,0x62,0x5D,0x09,0xC2,0xA0,0xA8,0xD4, +0xB0,0x75,0x38,0xF1,0x34,0x6A,0x9D,0xE4,0x9F,0x8A,0x86,0x26,0x51,0xE6,0x2C,0xD1, +0xC6,0x2D,0x6E,0x95,0x20,0x4A,0x92,0x01,0xEC,0xB8,0x8A,0x67,0x7B,0x31,0xE2,0x67, +0x2E,0x8C,0x95,0x03,0x26,0x2E,0x43,0x9D,0x4A,0x31,0xF6,0x0E,0xB5,0x0C,0xBB,0xB7, +0xE2,0x37,0x7F,0x22,0xBA,0x00,0xA3,0x0E,0x7B,0x52,0xFB,0x6B,0xBB,0x3B,0xC4,0xD3, +0x79,0x51,0x4E,0xCD,0x90,0xF4,0x67,0x07,0x19,0xC8,0x3C,0x46,0x7A,0x0D,0x01,0x7D, +0xC5,0x58,0xE7,0x6D,0xE6,0x85,0x30,0x17,0x9A,0x24,0xC4,0x10,0xE0,0x04,0xF7,0xE0, +0xF2,0x7F,0xD4,0xAA,0x0A,0xFF,0x42,0x1D,0x37,0xED,0x94,0xE5,0x64,0x59,0x12,0x20, +0x77,0x38,0xD3,0x32,0x3E,0x38,0x81,0x75,0x96,0x73,0xFA,0x68,0x8F,0xB1,0xCB,0xCE, +0x1F,0xC5,0xEC,0xFA,0x9C,0x7E,0xCF,0x7E,0xB1,0xF1,0x07,0x2D,0xB6,0xFC,0xBF,0xCA, +0xA4,0xBF,0xD0,0x97,0x05,0x4A,0xBC,0xEA,0x18,0x28,0x02,0x90,0xBD,0x54,0x78,0x09, +0x21,0x71,0xD3,0xD1,0x7D,0x1D,0xD9,0x16,0xB0,0xA9,0x61,0x3D,0xD0,0x0A,0x00,0x22, +0xFC,0xC7,0x7B,0xCB,0x09,0x64,0x45,0x0B,0x3B,0x40,0x81,0xF7,0x7D,0x7C,0x32,0xF5, +0x98,0xCA,0x58,0x8E,0x7D,0x2A,0xEE,0x90,0x59,0x73,0x64,0xF9,0x36,0x74,0x5E,0x25, +0xA1,0xF5,0x66,0x05,0x2E,0x7F,0x39,0x15,0xA9,0x2A,0xFB,0x50,0x8B,0x8E,0x85,0x69, +0xF4, +}; + + +/* subject:/C=US/ST=Arizona/L=Scottsdale/O=Starfield Technologies, Inc./CN=Starfield Services Root Certificate Authority - G2 */ +/* issuer :/C=US/ST=Arizona/L=Scottsdale/O=Starfield Technologies, Inc./CN=Starfield Services Root Certificate Authority - G2 */ + + +const unsigned char Starfield_Services_Root_Certificate_Authority___G2_certificate[1011]={ +0x30,0x82,0x03,0xEF,0x30,0x82,0x02,0xD7,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x00, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30, +0x81,0x98,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31, +0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x08,0x13,0x07,0x41,0x72,0x69,0x7A,0x6F,0x6E, +0x61,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x07,0x13,0x0A,0x53,0x63,0x6F,0x74, +0x74,0x73,0x64,0x61,0x6C,0x65,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x0A,0x13, +0x1C,0x53,0x74,0x61,0x72,0x66,0x69,0x65,0x6C,0x64,0x20,0x54,0x65,0x63,0x68,0x6E, +0x6F,0x6C,0x6F,0x67,0x69,0x65,0x73,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x3B,0x30, +0x39,0x06,0x03,0x55,0x04,0x03,0x13,0x32,0x53,0x74,0x61,0x72,0x66,0x69,0x65,0x6C, +0x64,0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x20,0x52,0x6F,0x6F,0x74,0x20, +0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x65,0x20,0x41,0x75,0x74,0x68, +0x6F,0x72,0x69,0x74,0x79,0x20,0x2D,0x20,0x47,0x32,0x30,0x1E,0x17,0x0D,0x30,0x39, +0x30,0x39,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x37,0x31, +0x32,0x33,0x31,0x32,0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x81,0x98,0x31,0x0B,0x30, +0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x10,0x30,0x0E,0x06,0x03, +0x55,0x04,0x08,0x13,0x07,0x41,0x72,0x69,0x7A,0x6F,0x6E,0x61,0x31,0x13,0x30,0x11, +0x06,0x03,0x55,0x04,0x07,0x13,0x0A,0x53,0x63,0x6F,0x74,0x74,0x73,0x64,0x61,0x6C, +0x65,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x0A,0x13,0x1C,0x53,0x74,0x61,0x72, +0x66,0x69,0x65,0x6C,0x64,0x20,0x54,0x65,0x63,0x68,0x6E,0x6F,0x6C,0x6F,0x67,0x69, +0x65,0x73,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x3B,0x30,0x39,0x06,0x03,0x55,0x04, +0x03,0x13,0x32,0x53,0x74,0x61,0x72,0x66,0x69,0x65,0x6C,0x64,0x20,0x53,0x65,0x72, +0x76,0x69,0x63,0x65,0x73,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x65,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79, +0x20,0x2D,0x20,0x47,0x32,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48, +0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01, +0x0A,0x02,0x82,0x01,0x01,0x00,0xD5,0x0C,0x3A,0xC4,0x2A,0xF9,0x4E,0xE2,0xF5,0xBE, +0x19,0x97,0x5F,0x8E,0x88,0x53,0xB1,0x1F,0x3F,0xCB,0xCF,0x9F,0x20,0x13,0x6D,0x29, +0x3A,0xC8,0x0F,0x7D,0x3C,0xF7,0x6B,0x76,0x38,0x63,0xD9,0x36,0x60,0xA8,0x9B,0x5E, +0x5C,0x00,0x80,0xB2,0x2F,0x59,0x7F,0xF6,0x87,0xF9,0x25,0x43,0x86,0xE7,0x69,0x1B, +0x52,0x9A,0x90,0xE1,0x71,0xE3,0xD8,0x2D,0x0D,0x4E,0x6F,0xF6,0xC8,0x49,0xD9,0xB6, +0xF3,0x1A,0x56,0xAE,0x2B,0xB6,0x74,0x14,0xEB,0xCF,0xFB,0x26,0xE3,0x1A,0xBA,0x1D, +0x96,0x2E,0x6A,0x3B,0x58,0x94,0x89,0x47,0x56,0xFF,0x25,0xA0,0x93,0x70,0x53,0x83, +0xDA,0x84,0x74,0x14,0xC3,0x67,0x9E,0x04,0x68,0x3A,0xDF,0x8E,0x40,0x5A,0x1D,0x4A, +0x4E,0xCF,0x43,0x91,0x3B,0xE7,0x56,0xD6,0x00,0x70,0xCB,0x52,0xEE,0x7B,0x7D,0xAE, +0x3A,0xE7,0xBC,0x31,0xF9,0x45,0xF6,0xC2,0x60,0xCF,0x13,0x59,0x02,0x2B,0x80,0xCC, +0x34,0x47,0xDF,0xB9,0xDE,0x90,0x65,0x6D,0x02,0xCF,0x2C,0x91,0xA6,0xA6,0xE7,0xDE, +0x85,0x18,0x49,0x7C,0x66,0x4E,0xA3,0x3A,0x6D,0xA9,0xB5,0xEE,0x34,0x2E,0xBA,0x0D, +0x03,0xB8,0x33,0xDF,0x47,0xEB,0xB1,0x6B,0x8D,0x25,0xD9,0x9B,0xCE,0x81,0xD1,0x45, +0x46,0x32,0x96,0x70,0x87,0xDE,0x02,0x0E,0x49,0x43,0x85,0xB6,0x6C,0x73,0xBB,0x64, +0xEA,0x61,0x41,0xAC,0xC9,0xD4,0x54,0xDF,0x87,0x2F,0xC7,0x22,0xB2,0x26,0xCC,0x9F, +0x59,0x54,0x68,0x9F,0xFC,0xBE,0x2A,0x2F,0xC4,0x55,0x1C,0x75,0x40,0x60,0x17,0x85, +0x02,0x55,0x39,0x8B,0x7F,0x05,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30, +0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF, +0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06, +0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x9C,0x5F,0x00,0xDF,0xAA, +0x01,0xD7,0x30,0x2B,0x38,0x88,0xA2,0xB8,0x6D,0x4A,0x9C,0xF2,0x11,0x91,0x83,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82, +0x01,0x01,0x00,0x4B,0x36,0xA6,0x84,0x77,0x69,0xDD,0x3B,0x19,0x9F,0x67,0x23,0x08, +0x6F,0x0E,0x61,0xC9,0xFD,0x84,0xDC,0x5F,0xD8,0x36,0x81,0xCD,0xD8,0x1B,0x41,0x2D, +0x9F,0x60,0xDD,0xC7,0x1A,0x68,0xD9,0xD1,0x6E,0x86,0xE1,0x88,0x23,0xCF,0x13,0xDE, +0x43,0xCF,0xE2,0x34,0xB3,0x04,0x9D,0x1F,0x29,0xD5,0xBF,0xF8,0x5E,0xC8,0xD5,0xC1, +0xBD,0xEE,0x92,0x6F,0x32,0x74,0xF2,0x91,0x82,0x2F,0xBD,0x82,0x42,0x7A,0xAD,0x2A, +0xB7,0x20,0x7D,0x4D,0xBC,0x7A,0x55,0x12,0xC2,0x15,0xEA,0xBD,0xF7,0x6A,0x95,0x2E, +0x6C,0x74,0x9F,0xCF,0x1C,0xB4,0xF2,0xC5,0x01,0xA3,0x85,0xD0,0x72,0x3E,0xAD,0x73, +0xAB,0x0B,0x9B,0x75,0x0C,0x6D,0x45,0xB7,0x8E,0x94,0xAC,0x96,0x37,0xB5,0xA0,0xD0, +0x8F,0x15,0x47,0x0E,0xE3,0xE8,0x83,0xDD,0x8F,0xFD,0xEF,0x41,0x01,0x77,0xCC,0x27, +0xA9,0x62,0x85,0x33,0xF2,0x37,0x08,0xEF,0x71,0xCF,0x77,0x06,0xDE,0xC8,0x19,0x1D, +0x88,0x40,0xCF,0x7D,0x46,0x1D,0xFF,0x1E,0xC7,0xE1,0xCE,0xFF,0x23,0xDB,0xC6,0xFA, +0x8D,0x55,0x4E,0xA9,0x02,0xE7,0x47,0x11,0x46,0x3E,0xF4,0xFD,0xBD,0x7B,0x29,0x26, +0xBB,0xA9,0x61,0x62,0x37,0x28,0xB6,0x2D,0x2A,0xF6,0x10,0x86,0x64,0xC9,0x70,0xA7, +0xD2,0xAD,0xB7,0x29,0x70,0x79,0xEA,0x3C,0xDA,0x63,0x25,0x9F,0xFD,0x68,0xB7,0x30, +0xEC,0x70,0xFB,0x75,0x8A,0xB7,0x6D,0x60,0x67,0xB2,0x1E,0xC8,0xB9,0xE9,0xD8,0xA8, +0x6F,0x02,0x8B,0x67,0x0D,0x4D,0x26,0x57,0x71,0xDA,0x20,0xFC,0xC1,0x4A,0x50,0x8D, +0xB1,0x28,0xBA, +}; + + +/* subject:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority */ +/* issuer :/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority */ + + +const unsigned char StartCom_Certification_Authority_certificate[1931]={ +0x30,0x82,0x07,0x87,0x30,0x82,0x05,0x6F,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x2D, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30, +0x7D,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x49,0x4C,0x31,0x16, +0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D,0x53,0x74,0x61,0x72,0x74,0x43,0x6F, +0x6D,0x20,0x4C,0x74,0x64,0x2E,0x31,0x2B,0x30,0x29,0x06,0x03,0x55,0x04,0x0B,0x13, +0x22,0x53,0x65,0x63,0x75,0x72,0x65,0x20,0x44,0x69,0x67,0x69,0x74,0x61,0x6C,0x20, +0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x65,0x20,0x53,0x69,0x67,0x6E, +0x69,0x6E,0x67,0x31,0x29,0x30,0x27,0x06,0x03,0x55,0x04,0x03,0x13,0x20,0x53,0x74, +0x61,0x72,0x74,0x43,0x6F,0x6D,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61, +0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x1E, +0x17,0x0D,0x30,0x36,0x30,0x39,0x31,0x37,0x31,0x39,0x34,0x36,0x33,0x37,0x5A,0x17, +0x0D,0x33,0x36,0x30,0x39,0x31,0x37,0x31,0x39,0x34,0x36,0x33,0x36,0x5A,0x30,0x7D, +0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x49,0x4C,0x31,0x16,0x30, +0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D,0x53,0x74,0x61,0x72,0x74,0x43,0x6F,0x6D, +0x20,0x4C,0x74,0x64,0x2E,0x31,0x2B,0x30,0x29,0x06,0x03,0x55,0x04,0x0B,0x13,0x22, +0x53,0x65,0x63,0x75,0x72,0x65,0x20,0x44,0x69,0x67,0x69,0x74,0x61,0x6C,0x20,0x43, +0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x65,0x20,0x53,0x69,0x67,0x6E,0x69, +0x6E,0x67,0x31,0x29,0x30,0x27,0x06,0x03,0x55,0x04,0x03,0x13,0x20,0x53,0x74,0x61, +0x72,0x74,0x43,0x6F,0x6D,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74, +0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x82,0x02, +0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00, +0x03,0x82,0x02,0x0F,0x00,0x30,0x82,0x02,0x0A,0x02,0x82,0x02,0x01,0x00,0xC1,0x88, +0xDB,0x09,0xBC,0x6C,0x46,0x7C,0x78,0x9F,0x95,0x7B,0xB5,0x33,0x90,0xF2,0x72,0x62, +0xD6,0xC1,0x36,0x20,0x22,0x24,0x5E,0xCE,0xE9,0x77,0xF2,0x43,0x0A,0xA2,0x06,0x64, +0xA4,0xCC,0x8E,0x36,0xF8,0x38,0xE6,0x23,0xF0,0x6E,0x6D,0xB1,0x3C,0xDD,0x72,0xA3, +0x85,0x1C,0xA1,0xD3,0x3D,0xB4,0x33,0x2B,0xD3,0x2F,0xAF,0xFE,0xEA,0xB0,0x41,0x59, +0x67,0xB6,0xC4,0x06,0x7D,0x0A,0x9E,0x74,0x85,0xD6,0x79,0x4C,0x80,0x37,0x7A,0xDF, +0x39,0x05,0x52,0x59,0xF7,0xF4,0x1B,0x46,0x43,0xA4,0xD2,0x85,0x85,0xD2,0xC3,0x71, +0xF3,0x75,0x62,0x34,0xBA,0x2C,0x8A,0x7F,0x1E,0x8F,0xEE,0xED,0x34,0xD0,0x11,0xC7, +0x96,0xCD,0x52,0x3D,0xBA,0x33,0xD6,0xDD,0x4D,0xDE,0x0B,0x3B,0x4A,0x4B,0x9F,0xC2, +0x26,0x2F,0xFA,0xB5,0x16,0x1C,0x72,0x35,0x77,0xCA,0x3C,0x5D,0xE6,0xCA,0xE1,0x26, +0x8B,0x1A,0x36,0x76,0x5C,0x01,0xDB,0x74,0x14,0x25,0xFE,0xED,0xB5,0xA0,0x88,0x0F, +0xDD,0x78,0xCA,0x2D,0x1F,0x07,0x97,0x30,0x01,0x2D,0x72,0x79,0xFA,0x46,0xD6,0x13, +0x2A,0xA8,0xB9,0xA6,0xAB,0x83,0x49,0x1D,0xE5,0xF2,0xEF,0xDD,0xE4,0x01,0x8E,0x18, +0x0A,0x8F,0x63,0x53,0x16,0x85,0x62,0xA9,0x0E,0x19,0x3A,0xCC,0xB5,0x66,0xA6,0xC2, +0x6B,0x74,0x07,0xE4,0x2B,0xE1,0x76,0x3E,0xB4,0x6D,0xD8,0xF6,0x44,0xE1,0x73,0x62, +0x1F,0x3B,0xC4,0xBE,0xA0,0x53,0x56,0x25,0x6C,0x51,0x09,0xF7,0xAA,0xAB,0xCA,0xBF, +0x76,0xFD,0x6D,0x9B,0xF3,0x9D,0xDB,0xBF,0x3D,0x66,0xBC,0x0C,0x56,0xAA,0xAF,0x98, +0x48,0x95,0x3A,0x4B,0xDF,0xA7,0x58,0x50,0xD9,0x38,0x75,0xA9,0x5B,0xEA,0x43,0x0C, +0x02,0xFF,0x99,0xEB,0xE8,0x6C,0x4D,0x70,0x5B,0x29,0x65,0x9C,0xDD,0xAA,0x5D,0xCC, +0xAF,0x01,0x31,0xEC,0x0C,0xEB,0xD2,0x8D,0xE8,0xEA,0x9C,0x7B,0xE6,0x6E,0xF7,0x27, +0x66,0x0C,0x1A,0x48,0xD7,0x6E,0x42,0xE3,0x3F,0xDE,0x21,0x3E,0x7B,0xE1,0x0D,0x70, +0xFB,0x63,0xAA,0xA8,0x6C,0x1A,0x54,0xB4,0x5C,0x25,0x7A,0xC9,0xA2,0xC9,0x8B,0x16, +0xA6,0xBB,0x2C,0x7E,0x17,0x5E,0x05,0x4D,0x58,0x6E,0x12,0x1D,0x01,0xEE,0x12,0x10, +0x0D,0xC6,0x32,0x7F,0x18,0xFF,0xFC,0xF4,0xFA,0xCD,0x6E,0x91,0xE8,0x36,0x49,0xBE, +0x1A,0x48,0x69,0x8B,0xC2,0x96,0x4D,0x1A,0x12,0xB2,0x69,0x17,0xC1,0x0A,0x90,0xD6, +0xFA,0x79,0x22,0x48,0xBF,0xBA,0x7B,0x69,0xF8,0x70,0xC7,0xFA,0x7A,0x37,0xD8,0xD8, +0x0D,0xD2,0x76,0x4F,0x57,0xFF,0x90,0xB7,0xE3,0x91,0xD2,0xDD,0xEF,0xC2,0x60,0xB7, +0x67,0x3A,0xDD,0xFE,0xAA,0x9C,0xF0,0xD4,0x8B,0x7F,0x72,0x22,0xCE,0xC6,0x9F,0x97, +0xB6,0xF8,0xAF,0x8A,0xA0,0x10,0xA8,0xD9,0xFB,0x18,0xC6,0xB6,0xB5,0x5C,0x52,0x3C, +0x89,0xB6,0x19,0x2A,0x73,0x01,0x0A,0x0F,0x03,0xB3,0x12,0x60,0xF2,0x7A,0x2F,0x81, +0xDB,0xA3,0x6E,0xFF,0x26,0x30,0x97,0xF5,0x8B,0xDD,0x89,0x57,0xB6,0xAD,0x3D,0xB3, +0xAF,0x2B,0xC5,0xB7,0x76,0x02,0xF0,0xA5,0xD6,0x2B,0x9A,0x86,0x14,0x2A,0x72,0xF6, +0xE3,0x33,0x8C,0x5D,0x09,0x4B,0x13,0xDF,0xBB,0x8C,0x74,0x13,0x52,0x4B,0x02,0x03, +0x01,0x00,0x01,0xA3,0x82,0x02,0x10,0x30,0x82,0x02,0x0C,0x30,0x0F,0x06,0x03,0x55, +0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03, +0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x1D,0x06,0x03, +0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x4E,0x0B,0xEF,0x1A,0xA4,0x40,0x5B,0xA5,0x17, +0x69,0x87,0x30,0xCA,0x34,0x68,0x43,0xD0,0x41,0xAE,0xF2,0x30,0x1F,0x06,0x03,0x55, +0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0x4E,0x0B,0xEF,0x1A,0xA4,0x40,0x5B,0xA5, +0x17,0x69,0x87,0x30,0xCA,0x34,0x68,0x43,0xD0,0x41,0xAE,0xF2,0x30,0x82,0x01,0x5A, +0x06,0x03,0x55,0x1D,0x20,0x04,0x82,0x01,0x51,0x30,0x82,0x01,0x4D,0x30,0x82,0x01, +0x49,0x06,0x0B,0x2B,0x06,0x01,0x04,0x01,0x81,0xB5,0x37,0x01,0x01,0x01,0x30,0x82, +0x01,0x38,0x30,0x2E,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x02,0x01,0x16,0x22, +0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x77,0x77,0x77,0x2E,0x73,0x74,0x61,0x72,0x74, +0x73,0x73,0x6C,0x2E,0x63,0x6F,0x6D,0x2F,0x70,0x6F,0x6C,0x69,0x63,0x79,0x2E,0x70, +0x64,0x66,0x30,0x34,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x02,0x01,0x16,0x28, +0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x77,0x77,0x77,0x2E,0x73,0x74,0x61,0x72,0x74, +0x73,0x73,0x6C,0x2E,0x63,0x6F,0x6D,0x2F,0x69,0x6E,0x74,0x65,0x72,0x6D,0x65,0x64, +0x69,0x61,0x74,0x65,0x2E,0x70,0x64,0x66,0x30,0x81,0xCF,0x06,0x08,0x2B,0x06,0x01, +0x05,0x05,0x07,0x02,0x02,0x30,0x81,0xC2,0x30,0x27,0x16,0x20,0x53,0x74,0x61,0x72, +0x74,0x20,0x43,0x6F,0x6D,0x6D,0x65,0x72,0x63,0x69,0x61,0x6C,0x20,0x28,0x53,0x74, +0x61,0x72,0x74,0x43,0x6F,0x6D,0x29,0x20,0x4C,0x74,0x64,0x2E,0x30,0x03,0x02,0x01, +0x01,0x1A,0x81,0x96,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64,0x20,0x4C,0x69,0x61,0x62, +0x69,0x6C,0x69,0x74,0x79,0x2C,0x20,0x72,0x65,0x61,0x64,0x20,0x74,0x68,0x65,0x20, +0x73,0x65,0x63,0x74,0x69,0x6F,0x6E,0x20,0x2A,0x4C,0x65,0x67,0x61,0x6C,0x20,0x4C, +0x69,0x6D,0x69,0x74,0x61,0x74,0x69,0x6F,0x6E,0x73,0x2A,0x20,0x6F,0x66,0x20,0x74, +0x68,0x65,0x20,0x53,0x74,0x61,0x72,0x74,0x43,0x6F,0x6D,0x20,0x43,0x65,0x72,0x74, +0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72, +0x69,0x74,0x79,0x20,0x50,0x6F,0x6C,0x69,0x63,0x79,0x20,0x61,0x76,0x61,0x69,0x6C, +0x61,0x62,0x6C,0x65,0x20,0x61,0x74,0x20,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x77, +0x77,0x77,0x2E,0x73,0x74,0x61,0x72,0x74,0x73,0x73,0x6C,0x2E,0x63,0x6F,0x6D,0x2F, +0x70,0x6F,0x6C,0x69,0x63,0x79,0x2E,0x70,0x64,0x66,0x30,0x11,0x06,0x09,0x60,0x86, +0x48,0x01,0x86,0xF8,0x42,0x01,0x01,0x04,0x04,0x03,0x02,0x00,0x07,0x30,0x38,0x06, +0x09,0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x0D,0x04,0x2B,0x16,0x29,0x53,0x74, +0x61,0x72,0x74,0x43,0x6F,0x6D,0x20,0x46,0x72,0x65,0x65,0x20,0x53,0x53,0x4C,0x20, +0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75, +0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x02,0x01,0x00,0x8E,0x8F,0xE7,0xDC,0x94, +0x79,0x7C,0xF1,0x85,0x7F,0x9F,0x49,0x6F,0x6B,0xCA,0x5D,0xFB,0x8C,0xFE,0x04,0xC5, +0xC1,0x62,0xD1,0x7D,0x42,0x8A,0xBC,0x53,0xB7,0x94,0x03,0x66,0x30,0x3F,0xB1,0xE7, +0x0A,0xA7,0x50,0x20,0x55,0x25,0x7F,0x76,0x7A,0x14,0x0D,0xEB,0x04,0x0E,0x40,0xE6, +0x3E,0xD8,0x88,0xAB,0x07,0x27,0x83,0xA9,0x75,0xA6,0x37,0x73,0xC7,0xFD,0x4B,0xD2, +0x4D,0xAD,0x17,0x40,0xC8,0x46,0xBE,0x3B,0x7F,0x51,0xFC,0xC3,0xB6,0x05,0x31,0xDC, +0xCD,0x85,0x22,0x4E,0x71,0xB7,0xF2,0x71,0x5E,0xB0,0x1A,0xC6,0xBA,0x93,0x8B,0x78, +0x92,0x4A,0x85,0xF8,0x78,0x0F,0x83,0xFE,0x2F,0xAD,0x2C,0xF7,0xE4,0xA4,0xBB,0x2D, +0xD0,0xE7,0x0D,0x3A,0xB8,0x3E,0xCE,0xF6,0x78,0xF6,0xAE,0x47,0x24,0xCA,0xA3,0x35, +0x36,0xCE,0xC7,0xC6,0x87,0x98,0xDA,0xEC,0xFB,0xE9,0xB2,0xCE,0x27,0x9B,0x88,0xC3, +0x04,0xA1,0xF6,0x0B,0x59,0x68,0xAF,0xC9,0xDB,0x10,0x0F,0x4D,0xF6,0x64,0x63,0x5C, +0xA5,0x12,0x6F,0x92,0xB2,0x93,0x94,0xC7,0x88,0x17,0x0E,0x93,0xB6,0x7E,0x62,0x8B, +0x90,0x7F,0xAB,0x4E,0x9F,0xFC,0xE3,0x75,0x14,0x4F,0x2A,0x32,0xDF,0x5B,0x0D,0xE0, +0xF5,0x7B,0x93,0x0D,0xAB,0xA1,0xCF,0x87,0xE1,0xA5,0x04,0x45,0xE8,0x3C,0x12,0xA5, +0x09,0xC5,0xB0,0xD1,0xB7,0x53,0xF3,0x60,0x14,0xBA,0x85,0x69,0x6A,0x21,0x7C,0x1F, +0x75,0x61,0x17,0x20,0x17,0x7B,0x6C,0x3B,0x41,0x29,0x5C,0xE1,0xAC,0x5A,0xD1,0xCD, +0x8C,0x9B,0xEB,0x60,0x1D,0x19,0xEC,0xF7,0xE5,0xB0,0xDA,0xF9,0x79,0x18,0xA5,0x45, +0x3F,0x49,0x43,0x57,0xD2,0xDD,0x24,0xD5,0x2C,0xA3,0xFD,0x91,0x8D,0x27,0xB5,0xE5, +0xEB,0x14,0x06,0x9A,0x4C,0x7B,0x21,0xBB,0x3A,0xAD,0x30,0x06,0x18,0xC0,0xD8,0xC1, +0x6B,0x2C,0x7F,0x59,0x5C,0x5D,0x91,0xB1,0x70,0x22,0x57,0xEB,0x8A,0x6B,0x48,0x4A, +0xD5,0x0F,0x29,0xEC,0xC6,0x40,0xC0,0x2F,0x88,0x4C,0x68,0x01,0x17,0x77,0xF4,0x24, +0x19,0x4F,0xBD,0xFA,0xE1,0xB2,0x20,0x21,0x4B,0xDD,0x1A,0xD8,0x29,0x7D,0xAA,0xB8, +0xDE,0x54,0xEC,0x21,0x55,0x80,0x6C,0x1E,0xF5,0x30,0xC8,0xA3,0x10,0xE5,0xB2,0xE6, +0x2A,0x14,0x31,0xC3,0x85,0x2D,0x8C,0x98,0xB1,0x86,0x5A,0x4F,0x89,0x59,0x2D,0xB9, +0xC7,0xF7,0x1C,0xC8,0x8A,0x7F,0xC0,0x9D,0x05,0x4A,0xE6,0x42,0x4F,0x62,0xA3,0x6D, +0x29,0xA4,0x1F,0x85,0xAB,0xDB,0xE5,0x81,0xC8,0xAD,0x2A,0x3D,0x4C,0x5D,0x5B,0x84, +0x26,0x71,0xC4,0x85,0x5E,0x71,0x24,0xCA,0xA5,0x1B,0x6C,0xD8,0x61,0xD3,0x1A,0xE0, +0x54,0xDB,0xCE,0xBA,0xA9,0x32,0xB5,0x22,0xF6,0x73,0x41,0x09,0x5D,0xB8,0x17,0x5D, +0x0E,0x0F,0x99,0x90,0xD6,0x47,0xDA,0x6F,0x0A,0x3A,0x62,0x28,0x14,0x67,0x82,0xD9, +0xF1,0xD0,0x80,0x59,0x9B,0xCB,0x31,0xD8,0x9B,0x0F,0x8C,0x77,0x4E,0xB5,0x68,0x8A, +0xF2,0x6C,0xF6,0x24,0x0E,0x2D,0x6C,0x70,0xC5,0x73,0xD1,0xDE,0x14,0xD0,0x71,0x8F, +0xB6,0xD3,0x7B,0x02,0xF6,0xE3,0xB8,0xD4,0x09,0x6E,0x6B,0x9E,0x75,0x84,0x39,0xE6, +0x7F,0x25,0xA5,0xF2,0x48,0x00,0xC0,0xA4,0x01,0xDA,0x3F, +}; + + +/* subject:/C=IL/O=StartCom Ltd./CN=StartCom Certification Authority G2 */ +/* issuer :/C=IL/O=StartCom Ltd./CN=StartCom Certification Authority G2 */ + + +const unsigned char StartCom_Certification_Authority_G2_certificate[1383]={ +0x30,0x82,0x05,0x63,0x30,0x82,0x03,0x4B,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x3B, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30, +0x53,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x49,0x4C,0x31,0x16, +0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D,0x53,0x74,0x61,0x72,0x74,0x43,0x6F, +0x6D,0x20,0x4C,0x74,0x64,0x2E,0x31,0x2C,0x30,0x2A,0x06,0x03,0x55,0x04,0x03,0x13, +0x23,0x53,0x74,0x61,0x72,0x74,0x43,0x6F,0x6D,0x20,0x43,0x65,0x72,0x74,0x69,0x66, +0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74, +0x79,0x20,0x47,0x32,0x30,0x1E,0x17,0x0D,0x31,0x30,0x30,0x31,0x30,0x31,0x30,0x31, +0x30,0x30,0x30,0x31,0x5A,0x17,0x0D,0x33,0x39,0x31,0x32,0x33,0x31,0x32,0x33,0x35, +0x39,0x30,0x31,0x5A,0x30,0x53,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13, +0x02,0x49,0x4C,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D,0x53,0x74, +0x61,0x72,0x74,0x43,0x6F,0x6D,0x20,0x4C,0x74,0x64,0x2E,0x31,0x2C,0x30,0x2A,0x06, +0x03,0x55,0x04,0x03,0x13,0x23,0x53,0x74,0x61,0x72,0x74,0x43,0x6F,0x6D,0x20,0x43, +0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74, +0x68,0x6F,0x72,0x69,0x74,0x79,0x20,0x47,0x32,0x30,0x82,0x02,0x22,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x02,0x0F, +0x00,0x30,0x82,0x02,0x0A,0x02,0x82,0x02,0x01,0x00,0xB6,0x89,0x36,0x5B,0x07,0xB7, +0x20,0x36,0xBD,0x82,0xBB,0xE1,0x16,0x20,0x03,0x95,0x7A,0xAF,0x0E,0xA3,0x55,0xC9, +0x25,0x99,0x4A,0xC5,0xD0,0x56,0x41,0x87,0x90,0x4D,0x21,0x60,0xA4,0x14,0x87,0x3B, +0xCD,0xFD,0xB2,0x3E,0xB4,0x67,0x03,0x6A,0xED,0xE1,0x0F,0x4B,0xC0,0x91,0x85,0x70, +0x45,0xE0,0x42,0x9E,0xDE,0x29,0x23,0xD4,0x01,0x0D,0xA0,0x10,0x79,0xB8,0xDB,0x03, +0xBD,0xF3,0xA9,0x2F,0xD1,0xC6,0xE0,0x0F,0xCB,0x9E,0x8A,0x14,0x0A,0xB8,0xBD,0xF6, +0x56,0x62,0xF1,0xC5,0x72,0xB6,0x32,0x25,0xD9,0xB2,0xF3,0xBD,0x65,0xC5,0x0D,0x2C, +0x6E,0xD5,0x92,0x6F,0x18,0x8B,0x00,0x41,0x14,0x82,0x6F,0x40,0x20,0x26,0x7A,0x28, +0x0F,0xF5,0x1E,0x7F,0x27,0xF7,0x94,0xB1,0x37,0x3D,0xB7,0xC7,0x91,0xF7,0xE2,0x01, +0xEC,0xFD,0x94,0x89,0xE1,0xCC,0x6E,0xD3,0x36,0xD6,0x0A,0x19,0x79,0xAE,0xD7,0x34, +0x82,0x65,0xFF,0x7C,0x42,0xBB,0xB6,0xDD,0x0B,0xA6,0x34,0xAF,0x4B,0x60,0xFE,0x7F, +0x43,0x49,0x06,0x8B,0x8C,0x43,0xB8,0x56,0xF2,0xD9,0x7F,0x21,0x43,0x17,0xEA,0xA7, +0x48,0x95,0x01,0x75,0x75,0xEA,0x2B,0xA5,0x43,0x95,0xEA,0x15,0x84,0x9D,0x08,0x8D, +0x26,0x6E,0x55,0x9B,0xAB,0xDC,0xD2,0x39,0xD2,0x31,0x1D,0x60,0xE2,0xAC,0xCC,0x56, +0x45,0x24,0xF5,0x1C,0x54,0xAB,0xEE,0x86,0xDD,0x96,0x32,0x85,0xF8,0x4C,0x4F,0xE8, +0x95,0x76,0xB6,0x05,0xDD,0x36,0x23,0x67,0xBC,0xFF,0x15,0xE2,0xCA,0x3B,0xE6,0xA6, +0xEC,0x3B,0xEC,0x26,0x11,0x34,0x48,0x8D,0xF6,0x80,0x2B,0x1A,0x23,0x02,0xEB,0x8A, +0x1C,0x3A,0x76,0x2A,0x7B,0x56,0x16,0x1C,0x72,0x2A,0xB3,0xAA,0xE3,0x60,0xA5,0x00, +0x9F,0x04,0x9B,0xE2,0x6F,0x1E,0x14,0x58,0x5B,0xA5,0x6C,0x8B,0x58,0x3C,0xC3,0xBA, +0x4E,0x3A,0x5C,0xF7,0xE1,0x96,0x2B,0x3E,0xEF,0x07,0xBC,0xA4,0xE5,0x5D,0xCC,0x4D, +0x9F,0x0D,0xE1,0xDC,0xAA,0xBB,0xE1,0x6E,0x1A,0xEC,0x8F,0xE1,0xB6,0x4C,0x4D,0x79, +0x72,0x5D,0x17,0x35,0x0B,0x1D,0xD7,0xC1,0x47,0xDA,0x96,0x24,0xE0,0xD0,0x72,0xA8, +0x5A,0x5F,0x66,0x2D,0x10,0xDC,0x2F,0x2A,0x13,0xAE,0x26,0xFE,0x0A,0x1C,0x19,0xCC, +0xD0,0x3E,0x0B,0x9C,0xC8,0x09,0x2E,0xF9,0x5B,0x96,0x7A,0x47,0x9C,0xE9,0x7A,0xF3, +0x05,0x50,0x74,0x95,0x73,0x9E,0x30,0x09,0xF3,0x97,0x82,0x5E,0xE6,0x8F,0x39,0x08, +0x1E,0x59,0xE5,0x35,0x14,0x42,0x13,0xFF,0x00,0x9C,0xF7,0xBE,0xAA,0x50,0xCF,0xE2, +0x51,0x48,0xD7,0xB8,0x6F,0xAF,0xF8,0x4E,0x7E,0x33,0x98,0x92,0x14,0x62,0x3A,0x75, +0x63,0xCF,0x7B,0xFA,0xDE,0x82,0x3B,0xA9,0xBB,0x39,0xE2,0xC4,0xBD,0x2C,0x00,0x0E, +0xC8,0x17,0xAC,0x13,0xEF,0x4D,0x25,0x8E,0xD8,0xB3,0x90,0x2F,0xA9,0xDA,0x29,0x7D, +0x1D,0xAF,0x74,0x3A,0xB2,0x27,0xC0,0xC1,0x1E,0x3E,0x75,0xA3,0x16,0xA9,0xAF,0x7A, +0x22,0x5D,0x9F,0x13,0x1A,0xCF,0xA7,0xA0,0xEB,0xE3,0x86,0x0A,0xD3,0xFD,0xE6,0x96, +0x95,0xD7,0x23,0xC8,0x37,0xDD,0xC4,0x7C,0xAA,0x36,0xAC,0x98,0x1A,0x12,0xB1,0xE0, +0x4E,0xE8,0xB1,0x3B,0xF5,0xD6,0x6F,0xF1,0x30,0xD7,0x02,0x03,0x01,0x00,0x01,0xA3, +0x42,0x30,0x40,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30, +0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04, +0x03,0x02,0x01,0x06,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x4B, +0xC5,0xB4,0x40,0x6B,0xAD,0x1C,0xB3,0xA5,0x1C,0x65,0x6E,0x46,0x36,0x89,0x87,0x05, +0x0C,0x0E,0xB6,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B, +0x05,0x00,0x03,0x82,0x02,0x01,0x00,0x73,0x57,0x3F,0x2C,0xD5,0x95,0x32,0x7E,0x37, +0xDB,0x96,0x92,0xEB,0x19,0x5E,0x7E,0x53,0xE7,0x41,0xEC,0x11,0xB6,0x47,0xEF,0xB5, +0xDE,0xED,0x74,0x5C,0xC5,0xF1,0x8E,0x49,0xE0,0xFC,0x6E,0x99,0x13,0xCD,0x9F,0x8A, +0xDA,0xCD,0x3A,0x0A,0xD8,0x3A,0x5A,0x09,0x3F,0x5F,0x34,0xD0,0x2F,0x03,0xD2,0x66, +0x1D,0x1A,0xBD,0x9C,0x90,0x37,0xC8,0x0C,0x8E,0x07,0x5A,0x94,0x45,0x46,0x2A,0xE6, +0xBE,0x7A,0xDA,0xA1,0xA9,0xA4,0x69,0x12,0x92,0xB0,0x7D,0x36,0xD4,0x44,0x87,0xD7, +0x51,0xF1,0x29,0x63,0xD6,0x75,0xCD,0x16,0xE4,0x27,0x89,0x1D,0xF8,0xC2,0x32,0x48, +0xFD,0xDB,0x99,0xD0,0x8F,0x5F,0x54,0x74,0xCC,0xAC,0x67,0x34,0x11,0x62,0xD9,0x0C, +0x0A,0x37,0x87,0xD1,0xA3,0x17,0x48,0x8E,0xD2,0x17,0x1D,0xF6,0xD7,0xFD,0xDB,0x65, +0xEB,0xFD,0xA8,0xD4,0xF5,0xD6,0x4F,0xA4,0x5B,0x75,0xE8,0xC5,0xD2,0x60,0xB2,0xDB, +0x09,0x7E,0x25,0x8B,0x7B,0xBA,0x52,0x92,0x9E,0x3E,0xE8,0xC5,0x77,0xA1,0x3C,0xE0, +0x4A,0x73,0x6B,0x61,0xCF,0x86,0xDC,0x43,0xFF,0xFF,0x21,0xFE,0x23,0x5D,0x24,0x4A, +0xF5,0xD3,0x6D,0x0F,0x62,0x04,0x05,0x57,0x82,0xDA,0x6E,0xA4,0x33,0x25,0x79,0x4B, +0x2E,0x54,0x19,0x8B,0xCC,0x2C,0x3D,0x30,0xE9,0xD1,0x06,0xFF,0xE8,0x32,0x46,0xBE, +0xB5,0x33,0x76,0x77,0xA8,0x01,0x5D,0x96,0xC1,0xC1,0xD5,0xBE,0xAE,0x25,0xC0,0xC9, +0x1E,0x0A,0x09,0x20,0x88,0xA1,0x0E,0xC9,0xF3,0x6F,0x4D,0x82,0x54,0x00,0x20,0xA7, +0xD2,0x8F,0xE4,0x39,0x54,0x17,0x2E,0x8D,0x1E,0xB8,0x1B,0xBB,0x1B,0xBD,0x9A,0x4E, +0x3B,0x10,0x34,0xDC,0x9C,0x88,0x53,0xEF,0xA2,0x31,0x5B,0x58,0x4F,0x91,0x62,0xC8, +0xC2,0x9A,0x9A,0xCD,0x15,0x5D,0x38,0xA9,0xD6,0xBE,0xF8,0x13,0xB5,0x9F,0x12,0x69, +0xF2,0x50,0x62,0xAC,0xFB,0x17,0x37,0xF4,0xEE,0xB8,0x75,0x67,0x60,0x10,0xFB,0x83, +0x50,0xF9,0x44,0xB5,0x75,0x9C,0x40,0x17,0xB2,0xFE,0xFD,0x79,0x5D,0x6E,0x58,0x58, +0x5F,0x30,0xFC,0x00,0xAE,0xAF,0x33,0xC1,0x0E,0x4E,0x6C,0xBA,0xA7,0xA6,0xA1,0x7F, +0x32,0xDB,0x38,0xE0,0xB1,0x72,0x17,0x0A,0x2B,0x91,0xEC,0x6A,0x63,0x26,0xED,0x89, +0xD4,0x78,0xCC,0x74,0x1E,0x05,0xF8,0x6B,0xFE,0x8C,0x6A,0x76,0x39,0x29,0xAE,0x65, +0x23,0x12,0x95,0x08,0x22,0x1C,0x97,0xCE,0x5B,0x06,0xEE,0x0C,0xE2,0xBB,0xBC,0x1F, +0x44,0x93,0xF6,0xD8,0x38,0x45,0x05,0x21,0xED,0xE4,0xAD,0xAB,0x12,0xB6,0x03,0xA4, +0x42,0x2E,0x2D,0xC4,0x09,0x3A,0x03,0x67,0x69,0x84,0x9A,0xE1,0x59,0x90,0x8A,0x28, +0x85,0xD5,0x5D,0x74,0xB1,0xD1,0x0E,0x20,0x58,0x9B,0x13,0xA5,0xB0,0x63,0xA6,0xED, +0x7B,0x47,0xFD,0x45,0x55,0x30,0xA4,0xEE,0x9A,0xD4,0xE6,0xE2,0x87,0xEF,0x98,0xC9, +0x32,0x82,0x11,0x29,0x22,0xBC,0x00,0x0A,0x31,0x5E,0x2D,0x0F,0xC0,0x8E,0xE9,0x6B, +0xB2,0x8F,0x2E,0x06,0xD8,0xD1,0x91,0xC7,0xC6,0x12,0xF4,0x4C,0xFD,0x30,0x17,0xC3, +0xC1,0xDA,0x38,0x5B,0xE3,0xA9,0xEA,0xE6,0xA1,0xBA,0x79,0xEF,0x73,0xD8,0xB6,0x53, +0x57,0x2D,0xF6,0xD0,0xE1,0xD7,0x48, +}; + + +/* subject:/C=DE/O=TC TrustCenter GmbH/OU=TC TrustCenter Class 2 CA/CN=TC TrustCenter Class 2 CA II */ +/* issuer :/C=DE/O=TC TrustCenter GmbH/OU=TC TrustCenter Class 2 CA/CN=TC TrustCenter Class 2 CA II */ + + +const unsigned char TC_TrustCenter_Class_2_CA_II_certificate[1198]={ +0x30,0x82,0x04,0xAA,0x30,0x82,0x03,0x92,0xA0,0x03,0x02,0x01,0x02,0x02,0x0E,0x2E, +0x6A,0x00,0x01,0x00,0x02,0x1F,0xD7,0x52,0x21,0x2C,0x11,0x5C,0x3B,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x76,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x44,0x45,0x31,0x1C,0x30,0x1A,0x06, +0x03,0x55,0x04,0x0A,0x13,0x13,0x54,0x43,0x20,0x54,0x72,0x75,0x73,0x74,0x43,0x65, +0x6E,0x74,0x65,0x72,0x20,0x47,0x6D,0x62,0x48,0x31,0x22,0x30,0x20,0x06,0x03,0x55, +0x04,0x0B,0x13,0x19,0x54,0x43,0x20,0x54,0x72,0x75,0x73,0x74,0x43,0x65,0x6E,0x74, +0x65,0x72,0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x32,0x20,0x43,0x41,0x31,0x25,0x30, +0x23,0x06,0x03,0x55,0x04,0x03,0x13,0x1C,0x54,0x43,0x20,0x54,0x72,0x75,0x73,0x74, +0x43,0x65,0x6E,0x74,0x65,0x72,0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x32,0x20,0x43, +0x41,0x20,0x49,0x49,0x30,0x1E,0x17,0x0D,0x30,0x36,0x30,0x31,0x31,0x32,0x31,0x34, +0x33,0x38,0x34,0x33,0x5A,0x17,0x0D,0x32,0x35,0x31,0x32,0x33,0x31,0x32,0x32,0x35, +0x39,0x35,0x39,0x5A,0x30,0x76,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13, +0x02,0x44,0x45,0x31,0x1C,0x30,0x1A,0x06,0x03,0x55,0x04,0x0A,0x13,0x13,0x54,0x43, +0x20,0x54,0x72,0x75,0x73,0x74,0x43,0x65,0x6E,0x74,0x65,0x72,0x20,0x47,0x6D,0x62, +0x48,0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,0x0B,0x13,0x19,0x54,0x43,0x20,0x54, +0x72,0x75,0x73,0x74,0x43,0x65,0x6E,0x74,0x65,0x72,0x20,0x43,0x6C,0x61,0x73,0x73, +0x20,0x32,0x20,0x43,0x41,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x03,0x13,0x1C, +0x54,0x43,0x20,0x54,0x72,0x75,0x73,0x74,0x43,0x65,0x6E,0x74,0x65,0x72,0x20,0x43, +0x6C,0x61,0x73,0x73,0x20,0x32,0x20,0x43,0x41,0x20,0x49,0x49,0x30,0x82,0x01,0x22, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03, +0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xAB,0x80,0x87, +0x9B,0x8E,0xF0,0xC3,0x7C,0x87,0xD7,0xE8,0x24,0x82,0x11,0xB3,0x3C,0xDD,0x43,0x62, +0xEE,0xF8,0xC3,0x45,0xDA,0xE8,0xE1,0xA0,0x5F,0xD1,0x2A,0xB2,0xEA,0x93,0x68,0xDF, +0xB4,0xC8,0xD6,0x43,0xE9,0xC4,0x75,0x59,0x7F,0xFC,0xE1,0x1D,0xF8,0x31,0x70,0x23, +0x1B,0x88,0x9E,0x27,0xB9,0x7B,0xFD,0x3A,0xD2,0xC9,0xA9,0xE9,0x14,0x2F,0x90,0xBE, +0x03,0x52,0xC1,0x49,0xCD,0xF6,0xFD,0xE4,0x08,0x66,0x0B,0x57,0x8A,0xA2,0x42,0xA0, +0xB8,0xD5,0x7F,0x69,0x5C,0x90,0x32,0xB2,0x97,0x0D,0xCA,0x4A,0xDC,0x46,0x3E,0x02, +0x55,0x89,0x53,0xE3,0x1A,0x5A,0xCB,0x36,0xC6,0x07,0x56,0xF7,0x8C,0xCF,0x11,0xF4, +0x4C,0xBB,0x30,0x70,0x04,0x95,0xA5,0xF6,0x39,0x8C,0xFD,0x73,0x81,0x08,0x7D,0x89, +0x5E,0x32,0x1E,0x22,0xA9,0x22,0x45,0x4B,0xB0,0x66,0x2E,0x30,0xCC,0x9F,0x65,0xFD, +0xFC,0xCB,0x81,0xA9,0xF1,0xE0,0x3B,0xAF,0xA3,0x86,0xD1,0x89,0xEA,0xC4,0x45,0x79, +0x50,0x5D,0xAE,0xE9,0x21,0x74,0x92,0x4D,0x8B,0x59,0x82,0x8F,0x94,0xE3,0xE9,0x4A, +0xF1,0xE7,0x49,0xB0,0x14,0xE3,0xF5,0x62,0xCB,0xD5,0x72,0xBD,0x1F,0xB9,0xD2,0x9F, +0xA0,0xCD,0xA8,0xFA,0x01,0xC8,0xD9,0x0D,0xDF,0xDA,0xFC,0x47,0x9D,0xB3,0xC8,0x54, +0xDF,0x49,0x4A,0xF1,0x21,0xA9,0xFE,0x18,0x4E,0xEE,0x48,0xD4,0x19,0xBB,0xEF,0x7D, +0xE4,0xE2,0x9D,0xCB,0x5B,0xB6,0x6E,0xFF,0xE3,0xCD,0x5A,0xE7,0x74,0x82,0x05,0xBA, +0x80,0x25,0x38,0xCB,0xE4,0x69,0x9E,0xAF,0x41,0xAA,0x1A,0x84,0xF5,0x02,0x03,0x01, +0x00,0x01,0xA3,0x82,0x01,0x34,0x30,0x82,0x01,0x30,0x30,0x0F,0x06,0x03,0x55,0x1D, +0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03,0x55, +0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x1D,0x06,0x03,0x55, +0x1D,0x0E,0x04,0x16,0x04,0x14,0xE3,0xAB,0x54,0x4C,0x80,0xA1,0xDB,0x56,0x43,0xB7, +0x91,0x4A,0xCB,0xF3,0x82,0x7A,0x13,0x5C,0x08,0xAB,0x30,0x81,0xED,0x06,0x03,0x55, +0x1D,0x1F,0x04,0x81,0xE5,0x30,0x81,0xE2,0x30,0x81,0xDF,0xA0,0x81,0xDC,0xA0,0x81, +0xD9,0x86,0x35,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x77,0x77,0x77,0x2E,0x74,0x72, +0x75,0x73,0x74,0x63,0x65,0x6E,0x74,0x65,0x72,0x2E,0x64,0x65,0x2F,0x63,0x72,0x6C, +0x2F,0x76,0x32,0x2F,0x74,0x63,0x5F,0x63,0x6C,0x61,0x73,0x73,0x5F,0x32,0x5F,0x63, +0x61,0x5F,0x49,0x49,0x2E,0x63,0x72,0x6C,0x86,0x81,0x9F,0x6C,0x64,0x61,0x70,0x3A, +0x2F,0x2F,0x77,0x77,0x77,0x2E,0x74,0x72,0x75,0x73,0x74,0x63,0x65,0x6E,0x74,0x65, +0x72,0x2E,0x64,0x65,0x2F,0x43,0x4E,0x3D,0x54,0x43,0x25,0x32,0x30,0x54,0x72,0x75, +0x73,0x74,0x43,0x65,0x6E,0x74,0x65,0x72,0x25,0x32,0x30,0x43,0x6C,0x61,0x73,0x73, +0x25,0x32,0x30,0x32,0x25,0x32,0x30,0x43,0x41,0x25,0x32,0x30,0x49,0x49,0x2C,0x4F, +0x3D,0x54,0x43,0x25,0x32,0x30,0x54,0x72,0x75,0x73,0x74,0x43,0x65,0x6E,0x74,0x65, +0x72,0x25,0x32,0x30,0x47,0x6D,0x62,0x48,0x2C,0x4F,0x55,0x3D,0x72,0x6F,0x6F,0x74, +0x63,0x65,0x72,0x74,0x73,0x2C,0x44,0x43,0x3D,0x74,0x72,0x75,0x73,0x74,0x63,0x65, +0x6E,0x74,0x65,0x72,0x2C,0x44,0x43,0x3D,0x64,0x65,0x3F,0x63,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x65,0x52,0x65,0x76,0x6F,0x63,0x61,0x74,0x69,0x6F,0x6E, +0x4C,0x69,0x73,0x74,0x3F,0x62,0x61,0x73,0x65,0x3F,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x8C,0xD7, +0xDF,0x7E,0xEE,0x1B,0x80,0x10,0xB3,0x83,0xF5,0xDB,0x11,0xEA,0x6B,0x4B,0xA8,0x92, +0x18,0xD9,0xF7,0x07,0x39,0xF5,0x2C,0xBE,0x06,0x75,0x7A,0x68,0x53,0x15,0x1C,0xEA, +0x4A,0xED,0x5E,0xFC,0x23,0xB2,0x13,0xA0,0xD3,0x09,0xFF,0xF6,0xF6,0x2E,0x6B,0x41, +0x71,0x79,0xCD,0xE2,0x6D,0xFD,0xAE,0x59,0x6B,0x85,0x1D,0xB8,0x4E,0x22,0x9A,0xED, +0x66,0x39,0x6E,0x4B,0x94,0xE6,0x55,0xFC,0x0B,0x1B,0x8B,0x77,0xC1,0x53,0x13,0x66, +0x89,0xD9,0x28,0xD6,0x8B,0xF3,0x45,0x4A,0x63,0xB7,0xFD,0x7B,0x0B,0x61,0x5D,0xB8, +0x6D,0xBE,0xC3,0xDC,0x5B,0x79,0xD2,0xED,0x86,0xE5,0xA2,0x4D,0xBE,0x5E,0x74,0x7C, +0x6A,0xED,0x16,0x38,0x1F,0x7F,0x58,0x81,0x5A,0x1A,0xEB,0x32,0x88,0x2D,0xB2,0xF3, +0x39,0x77,0x80,0xAF,0x5E,0xB6,0x61,0x75,0x29,0xDB,0x23,0x4D,0x88,0xCA,0x50,0x28, +0xCB,0x85,0xD2,0xD3,0x10,0xA2,0x59,0x6E,0xD3,0x93,0x54,0x00,0x7A,0xA2,0x46,0x95, +0x86,0x05,0x9C,0xA9,0x19,0x98,0xE5,0x31,0x72,0x0C,0x00,0xE2,0x67,0xD9,0x40,0xE0, +0x24,0x33,0x7B,0x6F,0x2C,0xB9,0x5C,0xAB,0x65,0x9D,0x2C,0xAC,0x76,0xEA,0x35,0x99, +0xF5,0x97,0xB9,0x0F,0x24,0xEC,0xC7,0x76,0x21,0x28,0x65,0xAE,0x57,0xE8,0x07,0x88, +0x75,0x4A,0x56,0xA0,0xD2,0x05,0x3A,0xA4,0xE6,0x8D,0x92,0x88,0x2C,0xF3,0xF2,0xE1, +0xC1,0xC6,0x61,0xDB,0x41,0xC5,0xC7,0x9B,0xF7,0x0E,0x1A,0x51,0x45,0xC2,0x61,0x6B, +0xDC,0x64,0x27,0x17,0x8C,0x5A,0xB7,0xDA,0x74,0x28,0xCD,0x97,0xE4,0xBD, +}; + + +/* subject:/C=DE/O=TC TrustCenter GmbH/OU=TC TrustCenter Class 3 CA/CN=TC TrustCenter Class 3 CA II */ +/* issuer :/C=DE/O=TC TrustCenter GmbH/OU=TC TrustCenter Class 3 CA/CN=TC TrustCenter Class 3 CA II */ + + +const unsigned char TC_TrustCenter_Class_3_CA_II_certificate[1198]={ +0x30,0x82,0x04,0xAA,0x30,0x82,0x03,0x92,0xA0,0x03,0x02,0x01,0x02,0x02,0x0E,0x4A, +0x47,0x00,0x01,0x00,0x02,0xE5,0xA0,0x5D,0xD6,0x3F,0x00,0x51,0xBF,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x76,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x44,0x45,0x31,0x1C,0x30,0x1A,0x06, +0x03,0x55,0x04,0x0A,0x13,0x13,0x54,0x43,0x20,0x54,0x72,0x75,0x73,0x74,0x43,0x65, +0x6E,0x74,0x65,0x72,0x20,0x47,0x6D,0x62,0x48,0x31,0x22,0x30,0x20,0x06,0x03,0x55, +0x04,0x0B,0x13,0x19,0x54,0x43,0x20,0x54,0x72,0x75,0x73,0x74,0x43,0x65,0x6E,0x74, +0x65,0x72,0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x33,0x20,0x43,0x41,0x31,0x25,0x30, +0x23,0x06,0x03,0x55,0x04,0x03,0x13,0x1C,0x54,0x43,0x20,0x54,0x72,0x75,0x73,0x74, +0x43,0x65,0x6E,0x74,0x65,0x72,0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x33,0x20,0x43, +0x41,0x20,0x49,0x49,0x30,0x1E,0x17,0x0D,0x30,0x36,0x30,0x31,0x31,0x32,0x31,0x34, +0x34,0x31,0x35,0x37,0x5A,0x17,0x0D,0x32,0x35,0x31,0x32,0x33,0x31,0x32,0x32,0x35, +0x39,0x35,0x39,0x5A,0x30,0x76,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13, +0x02,0x44,0x45,0x31,0x1C,0x30,0x1A,0x06,0x03,0x55,0x04,0x0A,0x13,0x13,0x54,0x43, +0x20,0x54,0x72,0x75,0x73,0x74,0x43,0x65,0x6E,0x74,0x65,0x72,0x20,0x47,0x6D,0x62, +0x48,0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,0x0B,0x13,0x19,0x54,0x43,0x20,0x54, +0x72,0x75,0x73,0x74,0x43,0x65,0x6E,0x74,0x65,0x72,0x20,0x43,0x6C,0x61,0x73,0x73, +0x20,0x33,0x20,0x43,0x41,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x03,0x13,0x1C, +0x54,0x43,0x20,0x54,0x72,0x75,0x73,0x74,0x43,0x65,0x6E,0x74,0x65,0x72,0x20,0x43, +0x6C,0x61,0x73,0x73,0x20,0x33,0x20,0x43,0x41,0x20,0x49,0x49,0x30,0x82,0x01,0x22, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03, +0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xB4,0xE0,0xBB, +0x51,0xBB,0x39,0x5C,0x8B,0x04,0xC5,0x4C,0x79,0x1C,0x23,0x86,0x31,0x10,0x63,0x43, +0x55,0x27,0x3F,0xC6,0x45,0xC7,0xA4,0x3D,0xEC,0x09,0x0D,0x1A,0x1E,0x20,0xC2,0x56, +0x1E,0xDE,0x1B,0x37,0x07,0x30,0x22,0x2F,0x6F,0xF1,0x06,0xF1,0xAB,0xAD,0xD6,0xC8, +0xAB,0x61,0xA3,0x2F,0x43,0xC4,0xB0,0xB2,0x2D,0xFC,0xC3,0x96,0x69,0x7B,0x7E,0x8A, +0xE4,0xCC,0xC0,0x39,0x12,0x90,0x42,0x60,0xC9,0xCC,0x35,0x68,0xEE,0xDA,0x5F,0x90, +0x56,0x5F,0xCD,0x1C,0x4D,0x5B,0x58,0x49,0xEB,0x0E,0x01,0x4F,0x64,0xFA,0x2C,0x3C, +0x89,0x58,0xD8,0x2F,0x2E,0xE2,0xB0,0x68,0xE9,0x22,0x3B,0x75,0x89,0xD6,0x44,0x1A, +0x65,0xF2,0x1B,0x97,0x26,0x1D,0x28,0x6D,0xAC,0xE8,0xBD,0x59,0x1D,0x2B,0x24,0xF6, +0xD6,0x84,0x03,0x66,0x88,0x24,0x00,0x78,0x60,0xF1,0xF8,0xAB,0xFE,0x02,0xB2,0x6B, +0xFB,0x22,0xFB,0x35,0xE6,0x16,0xD1,0xAD,0xF6,0x2E,0x12,0xE4,0xFA,0x35,0x6A,0xE5, +0x19,0xB9,0x5D,0xDB,0x3B,0x1E,0x1A,0xFB,0xD3,0xFF,0x15,0x14,0x08,0xD8,0x09,0x6A, +0xBA,0x45,0x9D,0x14,0x79,0x60,0x7D,0xAF,0x40,0x8A,0x07,0x73,0xB3,0x93,0x96,0xD3, +0x74,0x34,0x8D,0x3A,0x37,0x29,0xDE,0x5C,0xEC,0xF5,0xEE,0x2E,0x31,0xC2,0x20,0xDC, +0xBE,0xF1,0x4F,0x7F,0x23,0x52,0xD9,0x5B,0xE2,0x64,0xD9,0x9C,0xAA,0x07,0x08,0xB5, +0x45,0xBD,0xD1,0xD0,0x31,0xC1,0xAB,0x54,0x9F,0xA9,0xD2,0xC3,0x62,0x60,0x03,0xF1, +0xBB,0x39,0x4A,0x92,0x4A,0x3D,0x0A,0xB9,0x9D,0xC5,0xA0,0xFE,0x37,0x02,0x03,0x01, +0x00,0x01,0xA3,0x82,0x01,0x34,0x30,0x82,0x01,0x30,0x30,0x0F,0x06,0x03,0x55,0x1D, +0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03,0x55, +0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x1D,0x06,0x03,0x55, +0x1D,0x0E,0x04,0x16,0x04,0x14,0xD4,0xA2,0xFC,0x9F,0xB3,0xC3,0xD8,0x03,0xD3,0x57, +0x5C,0x07,0xA4,0xD0,0x24,0xA7,0xC0,0xF2,0x00,0xD4,0x30,0x81,0xED,0x06,0x03,0x55, +0x1D,0x1F,0x04,0x81,0xE5,0x30,0x81,0xE2,0x30,0x81,0xDF,0xA0,0x81,0xDC,0xA0,0x81, +0xD9,0x86,0x35,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x77,0x77,0x77,0x2E,0x74,0x72, +0x75,0x73,0x74,0x63,0x65,0x6E,0x74,0x65,0x72,0x2E,0x64,0x65,0x2F,0x63,0x72,0x6C, +0x2F,0x76,0x32,0x2F,0x74,0x63,0x5F,0x63,0x6C,0x61,0x73,0x73,0x5F,0x33,0x5F,0x63, +0x61,0x5F,0x49,0x49,0x2E,0x63,0x72,0x6C,0x86,0x81,0x9F,0x6C,0x64,0x61,0x70,0x3A, +0x2F,0x2F,0x77,0x77,0x77,0x2E,0x74,0x72,0x75,0x73,0x74,0x63,0x65,0x6E,0x74,0x65, +0x72,0x2E,0x64,0x65,0x2F,0x43,0x4E,0x3D,0x54,0x43,0x25,0x32,0x30,0x54,0x72,0x75, +0x73,0x74,0x43,0x65,0x6E,0x74,0x65,0x72,0x25,0x32,0x30,0x43,0x6C,0x61,0x73,0x73, +0x25,0x32,0x30,0x33,0x25,0x32,0x30,0x43,0x41,0x25,0x32,0x30,0x49,0x49,0x2C,0x4F, +0x3D,0x54,0x43,0x25,0x32,0x30,0x54,0x72,0x75,0x73,0x74,0x43,0x65,0x6E,0x74,0x65, +0x72,0x25,0x32,0x30,0x47,0x6D,0x62,0x48,0x2C,0x4F,0x55,0x3D,0x72,0x6F,0x6F,0x74, +0x63,0x65,0x72,0x74,0x73,0x2C,0x44,0x43,0x3D,0x74,0x72,0x75,0x73,0x74,0x63,0x65, +0x6E,0x74,0x65,0x72,0x2C,0x44,0x43,0x3D,0x64,0x65,0x3F,0x63,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x65,0x52,0x65,0x76,0x6F,0x63,0x61,0x74,0x69,0x6F,0x6E, +0x4C,0x69,0x73,0x74,0x3F,0x62,0x61,0x73,0x65,0x3F,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x36,0x60, +0xE4,0x70,0xF7,0x06,0x20,0x43,0xD9,0x23,0x1A,0x42,0xF2,0xF8,0xA3,0xB2,0xB9,0x4D, +0x8A,0xB4,0xF3,0xC2,0x9A,0x55,0x31,0x7C,0xC4,0x3B,0x67,0x9A,0xB4,0xDF,0x4D,0x0E, +0x8A,0x93,0x4A,0x17,0x8B,0x1B,0x8D,0xCA,0x89,0xE1,0xCF,0x3A,0x1E,0xAC,0x1D,0xF1, +0x9C,0x32,0xB4,0x8E,0x59,0x76,0xA2,0x41,0x85,0x25,0x37,0xA0,0x13,0xD0,0xF5,0x7C, +0x4E,0xD5,0xEA,0x96,0xE2,0x6E,0x72,0xC1,0xBB,0x2A,0xFE,0x6C,0x6E,0xF8,0x91,0x98, +0x46,0xFC,0xC9,0x1B,0x57,0x5B,0xEA,0xC8,0x1A,0x3B,0x3F,0xB0,0x51,0x98,0x3C,0x07, +0xDA,0x2C,0x59,0x01,0xDA,0x8B,0x44,0xE8,0xE1,0x74,0xFD,0xA7,0x68,0xDD,0x54,0xBA, +0x83,0x46,0xEC,0xC8,0x46,0xB5,0xF8,0xAF,0x97,0xC0,0x3B,0x09,0x1C,0x8F,0xCE,0x72, +0x96,0x3D,0x33,0x56,0x70,0xBC,0x96,0xCB,0xD8,0xD5,0x7D,0x20,0x9A,0x83,0x9F,0x1A, +0xDC,0x39,0xF1,0xC5,0x72,0xA3,0x11,0x03,0xFD,0x3B,0x42,0x52,0x29,0xDB,0xE8,0x01, +0xF7,0x9B,0x5E,0x8C,0xD6,0x8D,0x86,0x4E,0x19,0xFA,0xBC,0x1C,0xBE,0xC5,0x21,0xA5, +0x87,0x9E,0x78,0x2E,0x36,0xDB,0x09,0x71,0xA3,0x72,0x34,0xF8,0x6C,0xE3,0x06,0x09, +0xF2,0x5E,0x56,0xA5,0xD3,0xDD,0x98,0xFA,0xD4,0xE6,0x06,0xF4,0xF0,0xB6,0x20,0x63, +0x4B,0xEA,0x29,0xBD,0xAA,0x82,0x66,0x1E,0xFB,0x81,0xAA,0xA7,0x37,0xAD,0x13,0x18, +0xE6,0x92,0xC3,0x81,0xC1,0x33,0xBB,0x88,0x1E,0xA1,0xE7,0xE2,0xB4,0xBD,0x31,0x6C, +0x0E,0x51,0x3D,0x6F,0xFB,0x96,0x56,0x80,0xE2,0x36,0x17,0xD1,0xDC,0xE4, +}; + + +/* subject:/C=DE/O=TC TrustCenter GmbH/OU=TC TrustCenter Universal CA/CN=TC TrustCenter Universal CA I */ +/* issuer :/C=DE/O=TC TrustCenter GmbH/OU=TC TrustCenter Universal CA/CN=TC TrustCenter Universal CA I */ + + +const unsigned char TC_TrustCenter_Universal_CA_I_certificate[993]={ +0x30,0x82,0x03,0xDD,0x30,0x82,0x02,0xC5,0xA0,0x03,0x02,0x01,0x02,0x02,0x0E,0x1D, +0xA2,0x00,0x01,0x00,0x02,0xEC,0xB7,0x60,0x80,0x78,0x8D,0xB6,0x06,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x79,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x44,0x45,0x31,0x1C,0x30,0x1A,0x06, +0x03,0x55,0x04,0x0A,0x13,0x13,0x54,0x43,0x20,0x54,0x72,0x75,0x73,0x74,0x43,0x65, +0x6E,0x74,0x65,0x72,0x20,0x47,0x6D,0x62,0x48,0x31,0x24,0x30,0x22,0x06,0x03,0x55, +0x04,0x0B,0x13,0x1B,0x54,0x43,0x20,0x54,0x72,0x75,0x73,0x74,0x43,0x65,0x6E,0x74, +0x65,0x72,0x20,0x55,0x6E,0x69,0x76,0x65,0x72,0x73,0x61,0x6C,0x20,0x43,0x41,0x31, +0x26,0x30,0x24,0x06,0x03,0x55,0x04,0x03,0x13,0x1D,0x54,0x43,0x20,0x54,0x72,0x75, +0x73,0x74,0x43,0x65,0x6E,0x74,0x65,0x72,0x20,0x55,0x6E,0x69,0x76,0x65,0x72,0x73, +0x61,0x6C,0x20,0x43,0x41,0x20,0x49,0x30,0x1E,0x17,0x0D,0x30,0x36,0x30,0x33,0x32, +0x32,0x31,0x35,0x35,0x34,0x32,0x38,0x5A,0x17,0x0D,0x32,0x35,0x31,0x32,0x33,0x31, +0x32,0x32,0x35,0x39,0x35,0x39,0x5A,0x30,0x79,0x31,0x0B,0x30,0x09,0x06,0x03,0x55, +0x04,0x06,0x13,0x02,0x44,0x45,0x31,0x1C,0x30,0x1A,0x06,0x03,0x55,0x04,0x0A,0x13, +0x13,0x54,0x43,0x20,0x54,0x72,0x75,0x73,0x74,0x43,0x65,0x6E,0x74,0x65,0x72,0x20, +0x47,0x6D,0x62,0x48,0x31,0x24,0x30,0x22,0x06,0x03,0x55,0x04,0x0B,0x13,0x1B,0x54, +0x43,0x20,0x54,0x72,0x75,0x73,0x74,0x43,0x65,0x6E,0x74,0x65,0x72,0x20,0x55,0x6E, +0x69,0x76,0x65,0x72,0x73,0x61,0x6C,0x20,0x43,0x41,0x31,0x26,0x30,0x24,0x06,0x03, +0x55,0x04,0x03,0x13,0x1D,0x54,0x43,0x20,0x54,0x72,0x75,0x73,0x74,0x43,0x65,0x6E, +0x74,0x65,0x72,0x20,0x55,0x6E,0x69,0x76,0x65,0x72,0x73,0x61,0x6C,0x20,0x43,0x41, +0x20,0x49,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D, +0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82, +0x01,0x01,0x00,0xA4,0x77,0x23,0x96,0x44,0xAF,0x90,0xF4,0x31,0xA7,0x10,0xF4,0x26, +0x87,0x9C,0xF3,0x38,0xD9,0x0F,0x5E,0xDE,0xCF,0x41,0xE8,0x31,0xAD,0xC6,0x74,0x91, +0x24,0x96,0x78,0x1E,0x09,0xA0,0x9B,0x9A,0x95,0x4A,0x4A,0xF5,0x62,0x7C,0x02,0xA8, +0xCA,0xAC,0xFB,0x5A,0x04,0x76,0x39,0xDE,0x5F,0xF1,0xF9,0xB3,0xBF,0xF3,0x03,0x58, +0x55,0xD2,0xAA,0xB7,0xE3,0x04,0x22,0xD1,0xF8,0x94,0xDA,0x22,0x08,0x00,0x8D,0xD3, +0x7C,0x26,0x5D,0xCC,0x77,0x79,0xE7,0x2C,0x78,0x39,0xA8,0x26,0x73,0x0E,0xA2,0x5D, +0x25,0x69,0x85,0x4F,0x55,0x0E,0x9A,0xEF,0xC6,0xB9,0x44,0xE1,0x57,0x3D,0xDF,0x1F, +0x54,0x22,0xE5,0x6F,0x65,0xAA,0x33,0x84,0x3A,0xF3,0xCE,0x7A,0xBE,0x55,0x97,0xAE, +0x8D,0x12,0x0F,0x14,0x33,0xE2,0x50,0x70,0xC3,0x49,0x87,0x13,0xBC,0x51,0xDE,0xD7, +0x98,0x12,0x5A,0xEF,0x3A,0x83,0x33,0x92,0x06,0x75,0x8B,0x92,0x7C,0x12,0x68,0x7B, +0x70,0x6A,0x0F,0xB5,0x9B,0xB6,0x77,0x5B,0x48,0x59,0x9D,0xE4,0xEF,0x5A,0xAD,0xF3, +0xC1,0x9E,0xD4,0xD7,0x45,0x4E,0xCA,0x56,0x34,0x21,0xBC,0x3E,0x17,0x5B,0x6F,0x77, +0x0C,0x48,0x01,0x43,0x29,0xB0,0xDD,0x3F,0x96,0x6E,0xE6,0x95,0xAA,0x0C,0xC0,0x20, +0xB6,0xFD,0x3E,0x36,0x27,0x9C,0xE3,0x5C,0xCF,0x4E,0x81,0xDC,0x19,0xBB,0x91,0x90, +0x7D,0xEC,0xE6,0x97,0x04,0x1E,0x93,0xCC,0x22,0x49,0xD7,0x97,0x86,0xB6,0x13,0x0A, +0x3C,0x43,0x23,0x77,0x7E,0xF0,0xDC,0xE6,0xCD,0x24,0x1F,0x3B,0x83,0x9B,0x34,0x3A, +0x83,0x34,0xE3,0x02,0x03,0x01,0x00,0x01,0xA3,0x63,0x30,0x61,0x30,0x1F,0x06,0x03, +0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0x92,0xA4,0x75,0x2C,0xA4,0x9E,0xBE, +0x81,0x44,0xEB,0x79,0xFC,0x8A,0xC5,0x95,0xA5,0xEB,0x10,0x75,0x73,0x30,0x0F,0x06, +0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E, +0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x1D, +0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x92,0xA4,0x75,0x2C,0xA4,0x9E,0xBE, +0x81,0x44,0xEB,0x79,0xFC,0x8A,0xC5,0x95,0xA5,0xEB,0x10,0x75,0x73,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01, +0x00,0x28,0xD2,0xE0,0x86,0xD5,0xE6,0xF8,0x7B,0xF0,0x97,0xDC,0x22,0x6B,0x3B,0x95, +0x14,0x56,0x0F,0x11,0x30,0xA5,0x9A,0x4F,0x3A,0xB0,0x3A,0xE0,0x06,0xCB,0x65,0xF5, +0xED,0xC6,0x97,0x27,0xFE,0x25,0xF2,0x57,0xE6,0x5E,0x95,0x8C,0x3E,0x64,0x60,0x15, +0x5A,0x7F,0x2F,0x0D,0x01,0xC5,0xB1,0x60,0xFD,0x45,0x35,0xCF,0xF0,0xB2,0xBF,0x06, +0xD9,0xEF,0x5A,0xBE,0xB3,0x62,0x21,0xB4,0xD7,0xAB,0x35,0x7C,0x53,0x3E,0xA6,0x27, +0xF1,0xA1,0x2D,0xDA,0x1A,0x23,0x9D,0xCC,0xDD,0xEC,0x3C,0x2D,0x9E,0x27,0x34,0x5D, +0x0F,0xC2,0x36,0x79,0xBC,0xC9,0x4A,0x62,0x2D,0xED,0x6B,0xD9,0x7D,0x41,0x43,0x7C, +0xB6,0xAA,0xCA,0xED,0x61,0xB1,0x37,0x82,0x15,0x09,0x1A,0x8A,0x16,0x30,0xD8,0xEC, +0xC9,0xD6,0x47,0x72,0x78,0x4B,0x10,0x46,0x14,0x8E,0x5F,0x0E,0xAF,0xEC,0xC7,0x2F, +0xAB,0x10,0xD7,0xB6,0xF1,0x6E,0xEC,0x86,0xB2,0xC2,0xE8,0x0D,0x92,0x73,0xDC,0xA2, +0xF4,0x0F,0x3A,0xBF,0x61,0x23,0x10,0x89,0x9C,0x48,0x40,0x6E,0x70,0x00,0xB3,0xD3, +0xBA,0x37,0x44,0x58,0x11,0x7A,0x02,0x6A,0x88,0xF0,0x37,0x34,0xF0,0x19,0xE9,0xAC, +0xD4,0x65,0x73,0xF6,0x69,0x8C,0x64,0x94,0x3A,0x79,0x85,0x29,0xB0,0x16,0x2B,0x0C, +0x82,0x3F,0x06,0x9C,0xC7,0xFD,0x10,0x2B,0x9E,0x0F,0x2C,0xB6,0x9E,0xE3,0x15,0xBF, +0xD9,0x36,0x1C,0xBA,0x25,0x1A,0x52,0x3D,0x1A,0xEC,0x22,0x0C,0x1C,0xE0,0xA4,0xA2, +0x3D,0xF0,0xE8,0x39,0xCF,0x81,0xC0,0x7B,0xED,0x5D,0x1F,0x6F,0xC5,0xD0,0x0B,0xD7, +0x98, +}; + + +/* subject:/C=DE/O=TC TrustCenter GmbH/OU=TC TrustCenter Universal CA/CN=TC TrustCenter Universal CA III */ +/* issuer :/C=DE/O=TC TrustCenter GmbH/OU=TC TrustCenter Universal CA/CN=TC TrustCenter Universal CA III */ + + +const unsigned char TC_TrustCenter_Universal_CA_III_certificate[997]={ +0x30,0x82,0x03,0xE1,0x30,0x82,0x02,0xC9,0xA0,0x03,0x02,0x01,0x02,0x02,0x0E,0x63, +0x25,0x00,0x01,0x00,0x02,0x14,0x8D,0x33,0x15,0x02,0xE4,0x6C,0xF4,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x7B,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x44,0x45,0x31,0x1C,0x30,0x1A,0x06, +0x03,0x55,0x04,0x0A,0x13,0x13,0x54,0x43,0x20,0x54,0x72,0x75,0x73,0x74,0x43,0x65, +0x6E,0x74,0x65,0x72,0x20,0x47,0x6D,0x62,0x48,0x31,0x24,0x30,0x22,0x06,0x03,0x55, +0x04,0x0B,0x13,0x1B,0x54,0x43,0x20,0x54,0x72,0x75,0x73,0x74,0x43,0x65,0x6E,0x74, +0x65,0x72,0x20,0x55,0x6E,0x69,0x76,0x65,0x72,0x73,0x61,0x6C,0x20,0x43,0x41,0x31, +0x28,0x30,0x26,0x06,0x03,0x55,0x04,0x03,0x13,0x1F,0x54,0x43,0x20,0x54,0x72,0x75, +0x73,0x74,0x43,0x65,0x6E,0x74,0x65,0x72,0x20,0x55,0x6E,0x69,0x76,0x65,0x72,0x73, +0x61,0x6C,0x20,0x43,0x41,0x20,0x49,0x49,0x49,0x30,0x1E,0x17,0x0D,0x30,0x39,0x30, +0x39,0x30,0x39,0x30,0x38,0x31,0x35,0x32,0x37,0x5A,0x17,0x0D,0x32,0x39,0x31,0x32, +0x33,0x31,0x32,0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x7B,0x31,0x0B,0x30,0x09,0x06, +0x03,0x55,0x04,0x06,0x13,0x02,0x44,0x45,0x31,0x1C,0x30,0x1A,0x06,0x03,0x55,0x04, +0x0A,0x13,0x13,0x54,0x43,0x20,0x54,0x72,0x75,0x73,0x74,0x43,0x65,0x6E,0x74,0x65, +0x72,0x20,0x47,0x6D,0x62,0x48,0x31,0x24,0x30,0x22,0x06,0x03,0x55,0x04,0x0B,0x13, +0x1B,0x54,0x43,0x20,0x54,0x72,0x75,0x73,0x74,0x43,0x65,0x6E,0x74,0x65,0x72,0x20, +0x55,0x6E,0x69,0x76,0x65,0x72,0x73,0x61,0x6C,0x20,0x43,0x41,0x31,0x28,0x30,0x26, +0x06,0x03,0x55,0x04,0x03,0x13,0x1F,0x54,0x43,0x20,0x54,0x72,0x75,0x73,0x74,0x43, +0x65,0x6E,0x74,0x65,0x72,0x20,0x55,0x6E,0x69,0x76,0x65,0x72,0x73,0x61,0x6C,0x20, +0x43,0x41,0x20,0x49,0x49,0x49,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82, +0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xC2,0xDA,0x9C,0x62,0xB0,0xB9,0x71,0x12,0xB0, +0x0B,0xC8,0x1A,0x57,0xB2,0xAE,0x83,0x14,0x99,0xB3,0x34,0x4B,0x9B,0x90,0xA2,0xC5, +0xE7,0xE7,0x2F,0x02,0xA0,0x4D,0x2D,0xA4,0xFA,0x85,0xDA,0x9B,0x25,0x85,0x2D,0x40, +0x28,0x20,0x6D,0xEA,0xE0,0xBD,0xB1,0x48,0x83,0x22,0x29,0x44,0x9F,0x4E,0x83,0xEE, +0x35,0x51,0x13,0x73,0x74,0xD5,0xBC,0xF2,0x30,0x66,0x94,0x53,0xC0,0x40,0x36,0x2F, +0x0C,0x84,0x65,0xCE,0x0F,0x6E,0xC2,0x58,0x93,0xE8,0x2C,0x0B,0x3A,0xE9,0xC1,0x8E, +0xFB,0xF2,0x6B,0xCA,0x3C,0xE2,0x9C,0x4E,0x8E,0xE4,0xF9,0x7D,0xD3,0x27,0x9F,0x1B, +0xD5,0x67,0x78,0x87,0x2D,0x7F,0x0B,0x47,0xB3,0xC7,0xE8,0xC9,0x48,0x7C,0xAF,0x2F, +0xCC,0x0A,0xD9,0x41,0xEF,0x9F,0xFE,0x9A,0xE1,0xB2,0xAE,0xF9,0x53,0xB5,0xE5,0xE9, +0x46,0x9F,0x60,0xE3,0xDF,0x8D,0xD3,0x7F,0xFB,0x96,0x7E,0xB3,0xB5,0x72,0xF8,0x4B, +0xAD,0x08,0x79,0xCD,0x69,0x89,0x40,0x27,0xF5,0x2A,0xC1,0xAD,0x43,0xEC,0xA4,0x53, +0xC8,0x61,0xB6,0xF7,0xD2,0x79,0x2A,0x67,0x18,0x76,0x48,0x6D,0x5B,0x25,0x01,0xD1, +0x26,0xC5,0xB7,0x57,0x69,0x23,0x15,0x5B,0x61,0x8A,0xAD,0xF0,0x1B,0x2D,0xD9,0xAF, +0x5C,0xF1,0x26,0x90,0x69,0xA9,0xD5,0x0C,0x40,0xF5,0x33,0x80,0x43,0x8F,0x9C,0xA3, +0x76,0x2A,0x45,0xB4,0xAF,0xBF,0x7F,0x3E,0x87,0x3F,0x76,0xC5,0xCD,0x2A,0xDE,0x20, +0xC5,0x16,0x58,0xCB,0xF9,0x1B,0xF5,0x0F,0xCB,0x0D,0x11,0x52,0x64,0xB8,0xD2,0x76, +0x62,0x77,0x83,0xF1,0x58,0x9F,0xFF,0x02,0x03,0x01,0x00,0x01,0xA3,0x63,0x30,0x61, +0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0x56,0xE7,0xE1, +0x5B,0x25,0x43,0x80,0xE0,0xF6,0x8C,0xE1,0x71,0xBC,0x8E,0xE5,0x80,0x2F,0xC4,0x48, +0xE2,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01, +0x01,0xFF,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02, +0x01,0x06,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x56,0xE7,0xE1, +0x5B,0x25,0x43,0x80,0xE0,0xF6,0x8C,0xE1,0x71,0xBC,0x8E,0xE5,0x80,0x2F,0xC4,0x48, +0xE2,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00, +0x03,0x82,0x01,0x01,0x00,0x83,0xC7,0xAF,0xEA,0x7F,0x4D,0x0A,0x3C,0x39,0xB1,0x68, +0xBE,0x7B,0x6D,0x89,0x2E,0xE9,0xB3,0x09,0xE7,0x18,0x57,0x8D,0x85,0x9A,0x17,0xF3, +0x76,0x42,0x50,0x13,0x0F,0xC7,0x90,0x6F,0x33,0xAD,0xC5,0x49,0x60,0x2B,0x6C,0x49, +0x58,0x19,0xD4,0xE2,0xBE,0xB7,0xBF,0xAB,0x49,0xBC,0x94,0xC8,0xAB,0xBE,0x28,0x6C, +0x16,0x68,0xE0,0xC8,0x97,0x46,0x20,0xA0,0x68,0x67,0x60,0x88,0x39,0x20,0x51,0xD8, +0x68,0x01,0x11,0xCE,0xA7,0xF6,0x11,0x07,0xF6,0xEC,0xEC,0xAC,0x1A,0x1F,0xB2,0x66, +0x6E,0x56,0x67,0x60,0x7A,0x74,0x5E,0xC0,0x6D,0x97,0x36,0xAE,0xB5,0x0D,0x5D,0x66, +0x73,0xC0,0x25,0x32,0x45,0xD8,0x4A,0x06,0x07,0x8F,0xC4,0xB7,0x07,0xB1,0x4D,0x06, +0x0D,0xE1,0xA5,0xEB,0xF4,0x75,0xCA,0xBA,0x9C,0xD0,0xBD,0xB3,0xD3,0x32,0x24,0x4C, +0xEE,0x7E,0xE2,0x76,0x04,0x4B,0x49,0x53,0xD8,0xF2,0xE9,0x54,0x33,0xFC,0xE5,0x71, +0x1F,0x3D,0x14,0x5C,0x96,0x4B,0xF1,0x3A,0xF2,0x00,0xBB,0x6C,0xB4,0xFA,0x96,0x55, +0x08,0x88,0x09,0xC1,0xCC,0x91,0x19,0x29,0xB0,0x20,0x2D,0xFF,0xCB,0x38,0xA4,0x40, +0xE1,0x17,0xBE,0x79,0x61,0x80,0xFF,0x07,0x03,0x86,0x4C,0x4E,0x7B,0x06,0x9F,0x11, +0x86,0x8D,0x89,0xEE,0x27,0xC4,0xDB,0xE2,0xBC,0x19,0x8E,0x0B,0xC3,0xC3,0x13,0xC7, +0x2D,0x03,0x63,0x3B,0xD3,0xE8,0xE4,0xA2,0x2A,0xC2,0x82,0x08,0x94,0x16,0x54,0xF0, +0xEF,0x1F,0x27,0x90,0x25,0xB8,0x0D,0x0E,0x28,0x1B,0x47,0x77,0x47,0xBD,0x1C,0xA8, +0x25,0xF1,0x94,0xB4,0x66, +}; + + +/* subject:/C=ZA/ST=Western Cape/L=Cape Town/O=Thawte Consulting cc/OU=Certification Services Division/CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com */ +/* issuer :/C=ZA/ST=Western Cape/L=Cape Town/O=Thawte Consulting cc/OU=Certification Services Division/CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com */ + + +const unsigned char Thawte_Premium_Server_CA_certificate[811]={ +0x30,0x82,0x03,0x27,0x30,0x82,0x02,0x90,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x04,0x05,0x00,0x30, +0x81,0xCE,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x5A,0x41,0x31, +0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x08,0x13,0x0C,0x57,0x65,0x73,0x74,0x65,0x72, +0x6E,0x20,0x43,0x61,0x70,0x65,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x13, +0x09,0x43,0x61,0x70,0x65,0x20,0x54,0x6F,0x77,0x6E,0x31,0x1D,0x30,0x1B,0x06,0x03, +0x55,0x04,0x0A,0x13,0x14,0x54,0x68,0x61,0x77,0x74,0x65,0x20,0x43,0x6F,0x6E,0x73, +0x75,0x6C,0x74,0x69,0x6E,0x67,0x20,0x63,0x63,0x31,0x28,0x30,0x26,0x06,0x03,0x55, +0x04,0x0B,0x13,0x1F,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F, +0x6E,0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x20,0x44,0x69,0x76,0x69,0x73, +0x69,0x6F,0x6E,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x03,0x13,0x18,0x54,0x68, +0x61,0x77,0x74,0x65,0x20,0x50,0x72,0x65,0x6D,0x69,0x75,0x6D,0x20,0x53,0x65,0x72, +0x76,0x65,0x72,0x20,0x43,0x41,0x31,0x28,0x30,0x26,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x09,0x01,0x16,0x19,0x70,0x72,0x65,0x6D,0x69,0x75,0x6D,0x2D,0x73, +0x65,0x72,0x76,0x65,0x72,0x40,0x74,0x68,0x61,0x77,0x74,0x65,0x2E,0x63,0x6F,0x6D, +0x30,0x1E,0x17,0x0D,0x39,0x36,0x30,0x38,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30, +0x5A,0x17,0x0D,0x32,0x30,0x31,0x32,0x33,0x31,0x32,0x33,0x35,0x39,0x35,0x39,0x5A, +0x30,0x81,0xCE,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x5A,0x41, +0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x08,0x13,0x0C,0x57,0x65,0x73,0x74,0x65, +0x72,0x6E,0x20,0x43,0x61,0x70,0x65,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07, +0x13,0x09,0x43,0x61,0x70,0x65,0x20,0x54,0x6F,0x77,0x6E,0x31,0x1D,0x30,0x1B,0x06, +0x03,0x55,0x04,0x0A,0x13,0x14,0x54,0x68,0x61,0x77,0x74,0x65,0x20,0x43,0x6F,0x6E, +0x73,0x75,0x6C,0x74,0x69,0x6E,0x67,0x20,0x63,0x63,0x31,0x28,0x30,0x26,0x06,0x03, +0x55,0x04,0x0B,0x13,0x1F,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69, +0x6F,0x6E,0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x20,0x44,0x69,0x76,0x69, +0x73,0x69,0x6F,0x6E,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x03,0x13,0x18,0x54, +0x68,0x61,0x77,0x74,0x65,0x20,0x50,0x72,0x65,0x6D,0x69,0x75,0x6D,0x20,0x53,0x65, +0x72,0x76,0x65,0x72,0x20,0x43,0x41,0x31,0x28,0x30,0x26,0x06,0x09,0x2A,0x86,0x48, +0x86,0xF7,0x0D,0x01,0x09,0x01,0x16,0x19,0x70,0x72,0x65,0x6D,0x69,0x75,0x6D,0x2D, +0x73,0x65,0x72,0x76,0x65,0x72,0x40,0x74,0x68,0x61,0x77,0x74,0x65,0x2E,0x63,0x6F, +0x6D,0x30,0x81,0x9F,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01, +0x01,0x05,0x00,0x03,0x81,0x8D,0x00,0x30,0x81,0x89,0x02,0x81,0x81,0x00,0xD2,0x36, +0x36,0x6A,0x8B,0xD7,0xC2,0x5B,0x9E,0xDA,0x81,0x41,0x62,0x8F,0x38,0xEE,0x49,0x04, +0x55,0xD6,0xD0,0xEF,0x1C,0x1B,0x95,0x16,0x47,0xEF,0x18,0x48,0x35,0x3A,0x52,0xF4, +0x2B,0x6A,0x06,0x8F,0x3B,0x2F,0xEA,0x56,0xE3,0xAF,0x86,0x8D,0x9E,0x17,0xF7,0x9E, +0xB4,0x65,0x75,0x02,0x4D,0xEF,0xCB,0x09,0xA2,0x21,0x51,0xD8,0x9B,0xD0,0x67,0xD0, +0xBA,0x0D,0x92,0x06,0x14,0x73,0xD4,0x93,0xCB,0x97,0x2A,0x00,0x9C,0x5C,0x4E,0x0C, +0xBC,0xFA,0x15,0x52,0xFC,0xF2,0x44,0x6E,0xDA,0x11,0x4A,0x6E,0x08,0x9F,0x2F,0x2D, +0xE3,0xF9,0xAA,0x3A,0x86,0x73,0xB6,0x46,0x53,0x58,0xC8,0x89,0x05,0xBD,0x83,0x11, +0xB8,0x73,0x3F,0xAA,0x07,0x8D,0xF4,0x42,0x4D,0xE7,0x40,0x9D,0x1C,0x37,0x02,0x03, +0x01,0x00,0x01,0xA3,0x13,0x30,0x11,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01, +0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x01,0x04,0x05,0x00,0x03,0x81,0x81,0x00,0x26,0x48,0x2C,0x16,0xC2, +0x58,0xFA,0xE8,0x16,0x74,0x0C,0xAA,0xAA,0x5F,0x54,0x3F,0xF2,0xD7,0xC9,0x78,0x60, +0x5E,0x5E,0x6E,0x37,0x63,0x22,0x77,0x36,0x7E,0xB2,0x17,0xC4,0x34,0xB9,0xF5,0x08, +0x85,0xFC,0xC9,0x01,0x38,0xFF,0x4D,0xBE,0xF2,0x16,0x42,0x43,0xE7,0xBB,0x5A,0x46, +0xFB,0xC1,0xC6,0x11,0x1F,0xF1,0x4A,0xB0,0x28,0x46,0xC9,0xC3,0xC4,0x42,0x7D,0xBC, +0xFA,0xAB,0x59,0x6E,0xD5,0xB7,0x51,0x88,0x11,0xE3,0xA4,0x85,0x19,0x6B,0x82,0x4C, +0xA4,0x0C,0x12,0xAD,0xE9,0xA4,0xAE,0x3F,0xF1,0xC3,0x49,0x65,0x9A,0x8C,0xC5,0xC8, +0x3E,0x25,0xB7,0x94,0x99,0xBB,0x92,0x32,0x71,0x07,0xF0,0x86,0x5E,0xED,0x50,0x27, +0xA6,0x0D,0xA6,0x23,0xF9,0xBB,0xCB,0xA6,0x07,0x14,0x42, +}; + + +/* subject:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA */ +/* issuer :/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA */ + + +const unsigned char thawte_Primary_Root_CA_certificate[1060]={ +0x30,0x82,0x04,0x20,0x30,0x82,0x03,0x08,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x34, +0x4E,0xD5,0x57,0x20,0xD5,0xED,0xEC,0x49,0xF4,0x2F,0xCE,0x37,0xDB,0x2B,0x6D,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x81, +0xA9,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15, +0x30,0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x74,0x68,0x61,0x77,0x74,0x65,0x2C, +0x20,0x49,0x6E,0x63,0x2E,0x31,0x28,0x30,0x26,0x06,0x03,0x55,0x04,0x0B,0x13,0x1F, +0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x53,0x65, +0x72,0x76,0x69,0x63,0x65,0x73,0x20,0x44,0x69,0x76,0x69,0x73,0x69,0x6F,0x6E,0x31, +0x38,0x30,0x36,0x06,0x03,0x55,0x04,0x0B,0x13,0x2F,0x28,0x63,0x29,0x20,0x32,0x30, +0x30,0x36,0x20,0x74,0x68,0x61,0x77,0x74,0x65,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x20, +0x2D,0x20,0x46,0x6F,0x72,0x20,0x61,0x75,0x74,0x68,0x6F,0x72,0x69,0x7A,0x65,0x64, +0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C,0x79,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55, +0x04,0x03,0x13,0x16,0x74,0x68,0x61,0x77,0x74,0x65,0x20,0x50,0x72,0x69,0x6D,0x61, +0x72,0x79,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x30,0x1E,0x17,0x0D,0x30,0x36, +0x31,0x31,0x31,0x37,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x36,0x30, +0x37,0x31,0x36,0x32,0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x81,0xA9,0x31,0x0B,0x30, +0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,0x13,0x06,0x03, +0x55,0x04,0x0A,0x13,0x0C,0x74,0x68,0x61,0x77,0x74,0x65,0x2C,0x20,0x49,0x6E,0x63, +0x2E,0x31,0x28,0x30,0x26,0x06,0x03,0x55,0x04,0x0B,0x13,0x1F,0x43,0x65,0x72,0x74, +0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x53,0x65,0x72,0x76,0x69,0x63, +0x65,0x73,0x20,0x44,0x69,0x76,0x69,0x73,0x69,0x6F,0x6E,0x31,0x38,0x30,0x36,0x06, +0x03,0x55,0x04,0x0B,0x13,0x2F,0x28,0x63,0x29,0x20,0x32,0x30,0x30,0x36,0x20,0x74, +0x68,0x61,0x77,0x74,0x65,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F, +0x72,0x20,0x61,0x75,0x74,0x68,0x6F,0x72,0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65, +0x20,0x6F,0x6E,0x6C,0x79,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x03,0x13,0x16, +0x74,0x68,0x61,0x77,0x74,0x65,0x20,0x50,0x72,0x69,0x6D,0x61,0x72,0x79,0x20,0x52, +0x6F,0x6F,0x74,0x20,0x43,0x41,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82, +0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xAC,0xA0,0xF0,0xFB,0x80,0x59,0xD4,0x9C,0xC7, +0xA4,0xCF,0x9D,0xA1,0x59,0x73,0x09,0x10,0x45,0x0C,0x0D,0x2C,0x6E,0x68,0xF1,0x6C, +0x5B,0x48,0x68,0x49,0x59,0x37,0xFC,0x0B,0x33,0x19,0xC2,0x77,0x7F,0xCC,0x10,0x2D, +0x95,0x34,0x1C,0xE6,0xEB,0x4D,0x09,0xA7,0x1C,0xD2,0xB8,0xC9,0x97,0x36,0x02,0xB7, +0x89,0xD4,0x24,0x5F,0x06,0xC0,0xCC,0x44,0x94,0x94,0x8D,0x02,0x62,0x6F,0xEB,0x5A, +0xDD,0x11,0x8D,0x28,0x9A,0x5C,0x84,0x90,0x10,0x7A,0x0D,0xBD,0x74,0x66,0x2F,0x6A, +0x38,0xA0,0xE2,0xD5,0x54,0x44,0xEB,0x1D,0x07,0x9F,0x07,0xBA,0x6F,0xEE,0xE9,0xFD, +0x4E,0x0B,0x29,0xF5,0x3E,0x84,0xA0,0x01,0xF1,0x9C,0xAB,0xF8,0x1C,0x7E,0x89,0xA4, +0xE8,0xA1,0xD8,0x71,0x65,0x0D,0xA3,0x51,0x7B,0xEE,0xBC,0xD2,0x22,0x60,0x0D,0xB9, +0x5B,0x9D,0xDF,0xBA,0xFC,0x51,0x5B,0x0B,0xAF,0x98,0xB2,0xE9,0x2E,0xE9,0x04,0xE8, +0x62,0x87,0xDE,0x2B,0xC8,0xD7,0x4E,0xC1,0x4C,0x64,0x1E,0xDD,0xCF,0x87,0x58,0xBA, +0x4A,0x4F,0xCA,0x68,0x07,0x1D,0x1C,0x9D,0x4A,0xC6,0xD5,0x2F,0x91,0xCC,0x7C,0x71, +0x72,0x1C,0xC5,0xC0,0x67,0xEB,0x32,0xFD,0xC9,0x92,0x5C,0x94,0xDA,0x85,0xC0,0x9B, +0xBF,0x53,0x7D,0x2B,0x09,0xF4,0x8C,0x9D,0x91,0x1F,0x97,0x6A,0x52,0xCB,0xDE,0x09, +0x36,0xA4,0x77,0xD8,0x7B,0x87,0x50,0x44,0xD5,0x3E,0x6E,0x29,0x69,0xFB,0x39,0x49, +0x26,0x1E,0x09,0xA5,0x80,0x7B,0x40,0x2D,0xEB,0xE8,0x27,0x85,0xC9,0xFE,0x61,0xFD, +0x7E,0xE6,0x7C,0x97,0x1D,0xD5,0x9D,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40, +0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01, +0xFF,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01, +0x06,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x7B,0x5B,0x45,0xCF, +0xAF,0xCE,0xCB,0x7A,0xFD,0x31,0x92,0x1A,0x6A,0xB6,0xF3,0x46,0xEB,0x57,0x48,0x50, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03, +0x82,0x01,0x01,0x00,0x79,0x11,0xC0,0x4B,0xB3,0x91,0xB6,0xFC,0xF0,0xE9,0x67,0xD4, +0x0D,0x6E,0x45,0xBE,0x55,0xE8,0x93,0xD2,0xCE,0x03,0x3F,0xED,0xDA,0x25,0xB0,0x1D, +0x57,0xCB,0x1E,0x3A,0x76,0xA0,0x4C,0xEC,0x50,0x76,0xE8,0x64,0x72,0x0C,0xA4,0xA9, +0xF1,0xB8,0x8B,0xD6,0xD6,0x87,0x84,0xBB,0x32,0xE5,0x41,0x11,0xC0,0x77,0xD9,0xB3, +0x60,0x9D,0xEB,0x1B,0xD5,0xD1,0x6E,0x44,0x44,0xA9,0xA6,0x01,0xEC,0x55,0x62,0x1D, +0x77,0xB8,0x5C,0x8E,0x48,0x49,0x7C,0x9C,0x3B,0x57,0x11,0xAC,0xAD,0x73,0x37,0x8E, +0x2F,0x78,0x5C,0x90,0x68,0x47,0xD9,0x60,0x60,0xE6,0xFC,0x07,0x3D,0x22,0x20,0x17, +0xC4,0xF7,0x16,0xE9,0xC4,0xD8,0x72,0xF9,0xC8,0x73,0x7C,0xDF,0x16,0x2F,0x15,0xA9, +0x3E,0xFD,0x6A,0x27,0xB6,0xA1,0xEB,0x5A,0xBA,0x98,0x1F,0xD5,0xE3,0x4D,0x64,0x0A, +0x9D,0x13,0xC8,0x61,0xBA,0xF5,0x39,0x1C,0x87,0xBA,0xB8,0xBD,0x7B,0x22,0x7F,0xF6, +0xFE,0xAC,0x40,0x79,0xE5,0xAC,0x10,0x6F,0x3D,0x8F,0x1B,0x79,0x76,0x8B,0xC4,0x37, +0xB3,0x21,0x18,0x84,0xE5,0x36,0x00,0xEB,0x63,0x20,0x99,0xB9,0xE9,0xFE,0x33,0x04, +0xBB,0x41,0xC8,0xC1,0x02,0xF9,0x44,0x63,0x20,0x9E,0x81,0xCE,0x42,0xD3,0xD6,0x3F, +0x2C,0x76,0xD3,0x63,0x9C,0x59,0xDD,0x8F,0xA6,0xE1,0x0E,0xA0,0x2E,0x41,0xF7,0x2E, +0x95,0x47,0xCF,0xBC,0xFD,0x33,0xF3,0xF6,0x0B,0x61,0x7E,0x7E,0x91,0x2B,0x81,0x47, +0xC2,0x27,0x30,0xEE,0xA7,0x10,0x5D,0x37,0x8F,0x5C,0x39,0x2B,0xE4,0x04,0xF0,0x7B, +0x8D,0x56,0x8C,0x68, +}; + + +/* subject:/C=US/O=thawte, Inc./OU=(c) 2007 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA - G2 */ +/* issuer :/C=US/O=thawte, Inc./OU=(c) 2007 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA - G2 */ + + +const unsigned char thawte_Primary_Root_CA___G2_certificate[652]={ +0x30,0x82,0x02,0x88,0x30,0x82,0x02,0x0D,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x35, +0xFC,0x26,0x5C,0xD9,0x84,0x4F,0xC9,0x3D,0x26,0x3D,0x57,0x9B,0xAE,0xD7,0x56,0x30, +0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x30,0x81,0x84,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,0x13,0x06, +0x03,0x55,0x04,0x0A,0x13,0x0C,0x74,0x68,0x61,0x77,0x74,0x65,0x2C,0x20,0x49,0x6E, +0x63,0x2E,0x31,0x38,0x30,0x36,0x06,0x03,0x55,0x04,0x0B,0x13,0x2F,0x28,0x63,0x29, +0x20,0x32,0x30,0x30,0x37,0x20,0x74,0x68,0x61,0x77,0x74,0x65,0x2C,0x20,0x49,0x6E, +0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F,0x72,0x20,0x61,0x75,0x74,0x68,0x6F,0x72,0x69, +0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C,0x79,0x31,0x24,0x30,0x22, +0x06,0x03,0x55,0x04,0x03,0x13,0x1B,0x74,0x68,0x61,0x77,0x74,0x65,0x20,0x50,0x72, +0x69,0x6D,0x61,0x72,0x79,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x20,0x2D,0x20, +0x47,0x32,0x30,0x1E,0x17,0x0D,0x30,0x37,0x31,0x31,0x30,0x35,0x30,0x30,0x30,0x30, +0x30,0x30,0x5A,0x17,0x0D,0x33,0x38,0x30,0x31,0x31,0x38,0x32,0x33,0x35,0x39,0x35, +0x39,0x5A,0x30,0x81,0x84,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02, +0x55,0x53,0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x74,0x68,0x61, +0x77,0x74,0x65,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x38,0x30,0x36,0x06,0x03,0x55, +0x04,0x0B,0x13,0x2F,0x28,0x63,0x29,0x20,0x32,0x30,0x30,0x37,0x20,0x74,0x68,0x61, +0x77,0x74,0x65,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F,0x72,0x20, +0x61,0x75,0x74,0x68,0x6F,0x72,0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20,0x6F, +0x6E,0x6C,0x79,0x31,0x24,0x30,0x22,0x06,0x03,0x55,0x04,0x03,0x13,0x1B,0x74,0x68, +0x61,0x77,0x74,0x65,0x20,0x50,0x72,0x69,0x6D,0x61,0x72,0x79,0x20,0x52,0x6F,0x6F, +0x74,0x20,0x43,0x41,0x20,0x2D,0x20,0x47,0x32,0x30,0x76,0x30,0x10,0x06,0x07,0x2A, +0x86,0x48,0xCE,0x3D,0x02,0x01,0x06,0x05,0x2B,0x81,0x04,0x00,0x22,0x03,0x62,0x00, +0x04,0xA2,0xD5,0x9C,0x82,0x7B,0x95,0x9D,0xF1,0x52,0x78,0x87,0xFE,0x8A,0x16,0xBF, +0x05,0xE6,0xDF,0xA3,0x02,0x4F,0x0D,0x07,0xC6,0x00,0x51,0xBA,0x0C,0x02,0x52,0x2D, +0x22,0xA4,0x42,0x39,0xC4,0xFE,0x8F,0xEA,0xC9,0xC1,0xBE,0xD4,0x4D,0xFF,0x9F,0x7A, +0x9E,0xE2,0xB1,0x7C,0x9A,0xAD,0xA7,0x86,0x09,0x73,0x87,0xD1,0xE7,0x9A,0xE3,0x7A, +0xA5,0xAA,0x6E,0xFB,0xBA,0xB3,0x70,0xC0,0x67,0x88,0xA2,0x35,0xD4,0xA3,0x9A,0xB1, +0xFD,0xAD,0xC2,0xEF,0x31,0xFA,0xA8,0xB9,0xF3,0xFB,0x08,0xC6,0x91,0xD1,0xFB,0x29, +0x95,0xA3,0x42,0x30,0x40,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04, +0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF, +0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04, +0x14,0x9A,0xD8,0x00,0x30,0x00,0xE7,0x6B,0x7F,0x85,0x18,0xEE,0x8B,0xB6,0xCE,0x8A, +0x0C,0xF8,0x11,0xE1,0xBB,0x30,0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03, +0x03,0x03,0x69,0x00,0x30,0x66,0x02,0x31,0x00,0xDD,0xF8,0xE0,0x57,0x47,0x5B,0xA7, +0xE6,0x0A,0xC3,0xBD,0xF5,0x80,0x8A,0x97,0x35,0x0D,0x1B,0x89,0x3C,0x54,0x86,0x77, +0x28,0xCA,0xA1,0xF4,0x79,0xDE,0xB5,0xE6,0x38,0xB0,0xF0,0x65,0x70,0x8C,0x7F,0x02, +0x54,0xC2,0xBF,0xFF,0xD8,0xA1,0x3E,0xD9,0xCF,0x02,0x31,0x00,0xC4,0x8D,0x94,0xFC, +0xDC,0x53,0xD2,0xDC,0x9D,0x78,0x16,0x1F,0x15,0x33,0x23,0x53,0x52,0xE3,0x5A,0x31, +0x5D,0x9D,0xCA,0xAE,0xBD,0x13,0x29,0x44,0x0D,0x27,0x5B,0xA8,0xE7,0x68,0x9C,0x12, +0xF7,0x58,0x3F,0x2E,0x72,0x02,0x57,0xA3,0x8F,0xA1,0x14,0x2E, +}; + + +/* subject:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2008 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA - G3 */ +/* issuer :/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2008 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA - G3 */ + + +const unsigned char thawte_Primary_Root_CA___G3_certificate[1070]={ +0x30,0x82,0x04,0x2A,0x30,0x82,0x03,0x12,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x60, +0x01,0x97,0xB7,0x46,0xA7,0xEA,0xB4,0xB4,0x9A,0xD6,0x4B,0x2F,0xF7,0x90,0xFB,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,0x81, +0xAE,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15, +0x30,0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x74,0x68,0x61,0x77,0x74,0x65,0x2C, +0x20,0x49,0x6E,0x63,0x2E,0x31,0x28,0x30,0x26,0x06,0x03,0x55,0x04,0x0B,0x13,0x1F, +0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x53,0x65, +0x72,0x76,0x69,0x63,0x65,0x73,0x20,0x44,0x69,0x76,0x69,0x73,0x69,0x6F,0x6E,0x31, +0x38,0x30,0x36,0x06,0x03,0x55,0x04,0x0B,0x13,0x2F,0x28,0x63,0x29,0x20,0x32,0x30, +0x30,0x38,0x20,0x74,0x68,0x61,0x77,0x74,0x65,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x20, +0x2D,0x20,0x46,0x6F,0x72,0x20,0x61,0x75,0x74,0x68,0x6F,0x72,0x69,0x7A,0x65,0x64, +0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C,0x79,0x31,0x24,0x30,0x22,0x06,0x03,0x55, +0x04,0x03,0x13,0x1B,0x74,0x68,0x61,0x77,0x74,0x65,0x20,0x50,0x72,0x69,0x6D,0x61, +0x72,0x79,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x20,0x2D,0x20,0x47,0x33,0x30, +0x1E,0x17,0x0D,0x30,0x38,0x30,0x34,0x30,0x32,0x30,0x30,0x30,0x30,0x30,0x30,0x5A, +0x17,0x0D,0x33,0x37,0x31,0x32,0x30,0x31,0x32,0x33,0x35,0x39,0x35,0x39,0x5A,0x30, +0x81,0xAE,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31, +0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x74,0x68,0x61,0x77,0x74,0x65, +0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x28,0x30,0x26,0x06,0x03,0x55,0x04,0x0B,0x13, +0x1F,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x53, +0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x20,0x44,0x69,0x76,0x69,0x73,0x69,0x6F,0x6E, +0x31,0x38,0x30,0x36,0x06,0x03,0x55,0x04,0x0B,0x13,0x2F,0x28,0x63,0x29,0x20,0x32, +0x30,0x30,0x38,0x20,0x74,0x68,0x61,0x77,0x74,0x65,0x2C,0x20,0x49,0x6E,0x63,0x2E, +0x20,0x2D,0x20,0x46,0x6F,0x72,0x20,0x61,0x75,0x74,0x68,0x6F,0x72,0x69,0x7A,0x65, +0x64,0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C,0x79,0x31,0x24,0x30,0x22,0x06,0x03, +0x55,0x04,0x03,0x13,0x1B,0x74,0x68,0x61,0x77,0x74,0x65,0x20,0x50,0x72,0x69,0x6D, +0x61,0x72,0x79,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x20,0x2D,0x20,0x47,0x33, +0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01, +0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01, +0x00,0xB2,0xBF,0x27,0x2C,0xFB,0xDB,0xD8,0x5B,0xDD,0x78,0x7B,0x1B,0x9E,0x77,0x66, +0x81,0xCB,0x3E,0xBC,0x7C,0xAE,0xF3,0xA6,0x27,0x9A,0x34,0xA3,0x68,0x31,0x71,0x38, +0x33,0x62,0xE4,0xF3,0x71,0x66,0x79,0xB1,0xA9,0x65,0xA3,0xA5,0x8B,0xD5,0x8F,0x60, +0x2D,0x3F,0x42,0xCC,0xAA,0x6B,0x32,0xC0,0x23,0xCB,0x2C,0x41,0xDD,0xE4,0xDF,0xFC, +0x61,0x9C,0xE2,0x73,0xB2,0x22,0x95,0x11,0x43,0x18,0x5F,0xC4,0xB6,0x1F,0x57,0x6C, +0x0A,0x05,0x58,0x22,0xC8,0x36,0x4C,0x3A,0x7C,0xA5,0xD1,0xCF,0x86,0xAF,0x88,0xA7, +0x44,0x02,0x13,0x74,0x71,0x73,0x0A,0x42,0x59,0x02,0xF8,0x1B,0x14,0x6B,0x42,0xDF, +0x6F,0x5F,0xBA,0x6B,0x82,0xA2,0x9D,0x5B,0xE7,0x4A,0xBD,0x1E,0x01,0x72,0xDB,0x4B, +0x74,0xE8,0x3B,0x7F,0x7F,0x7D,0x1F,0x04,0xB4,0x26,0x9B,0xE0,0xB4,0x5A,0xAC,0x47, +0x3D,0x55,0xB8,0xD7,0xB0,0x26,0x52,0x28,0x01,0x31,0x40,0x66,0xD8,0xD9,0x24,0xBD, +0xF6,0x2A,0xD8,0xEC,0x21,0x49,0x5C,0x9B,0xF6,0x7A,0xE9,0x7F,0x55,0x35,0x7E,0x96, +0x6B,0x8D,0x93,0x93,0x27,0xCB,0x92,0xBB,0xEA,0xAC,0x40,0xC0,0x9F,0xC2,0xF8,0x80, +0xCF,0x5D,0xF4,0x5A,0xDC,0xCE,0x74,0x86,0xA6,0x3E,0x6C,0x0B,0x53,0xCA,0xBD,0x92, +0xCE,0x19,0x06,0x72,0xE6,0x0C,0x5C,0x38,0x69,0xC7,0x04,0xD6,0xBC,0x6C,0xCE,0x5B, +0xF6,0xF7,0x68,0x9C,0xDC,0x25,0x15,0x48,0x88,0xA1,0xE9,0xA9,0xF8,0x98,0x9C,0xE0, +0xF3,0xD5,0x31,0x28,0x61,0x11,0x6C,0x67,0x96,0x8D,0x39,0x99,0xCB,0xC2,0x45,0x24, +0x39,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x0F,0x06,0x03,0x55,0x1D, +0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03,0x55, +0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x1D,0x06,0x03,0x55, +0x1D,0x0E,0x04,0x16,0x04,0x14,0xAD,0x6C,0xAA,0x94,0x60,0x9C,0xED,0xE4,0xFF,0xFA, +0x3E,0x0A,0x74,0x2B,0x63,0x03,0xF7,0xB6,0x59,0xBF,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x1A,0x40, +0xD8,0x95,0x65,0xAC,0x09,0x92,0x89,0xC6,0x39,0xF4,0x10,0xE5,0xA9,0x0E,0x66,0x53, +0x5D,0x78,0xDE,0xFA,0x24,0x91,0xBB,0xE7,0x44,0x51,0xDF,0xC6,0x16,0x34,0x0A,0xEF, +0x6A,0x44,0x51,0xEA,0x2B,0x07,0x8A,0x03,0x7A,0xC3,0xEB,0x3F,0x0A,0x2C,0x52,0x16, +0xA0,0x2B,0x43,0xB9,0x25,0x90,0x3F,0x70,0xA9,0x33,0x25,0x6D,0x45,0x1A,0x28,0x3B, +0x27,0xCF,0xAA,0xC3,0x29,0x42,0x1B,0xDF,0x3B,0x4C,0xC0,0x33,0x34,0x5B,0x41,0x88, +0xBF,0x6B,0x2B,0x65,0xAF,0x28,0xEF,0xB2,0xF5,0xC3,0xAA,0x66,0xCE,0x7B,0x56,0xEE, +0xB7,0xC8,0xCB,0x67,0xC1,0xC9,0x9C,0x1A,0x18,0xB8,0xC4,0xC3,0x49,0x03,0xF1,0x60, +0x0E,0x50,0xCD,0x46,0xC5,0xF3,0x77,0x79,0xF7,0xB6,0x15,0xE0,0x38,0xDB,0xC7,0x2F, +0x28,0xA0,0x0C,0x3F,0x77,0x26,0x74,0xD9,0x25,0x12,0xDA,0x31,0xDA,0x1A,0x1E,0xDC, +0x29,0x41,0x91,0x22,0x3C,0x69,0xA7,0xBB,0x02,0xF2,0xB6,0x5C,0x27,0x03,0x89,0xF4, +0x06,0xEA,0x9B,0xE4,0x72,0x82,0xE3,0xA1,0x09,0xC1,0xE9,0x00,0x19,0xD3,0x3E,0xD4, +0x70,0x6B,0xBA,0x71,0xA6,0xAA,0x58,0xAE,0xF4,0xBB,0xE9,0x6C,0xB6,0xEF,0x87,0xCC, +0x9B,0xBB,0xFF,0x39,0xE6,0x56,0x61,0xD3,0x0A,0xA7,0xC4,0x5C,0x4C,0x60,0x7B,0x05, +0x77,0x26,0x7A,0xBF,0xD8,0x07,0x52,0x2C,0x62,0xF7,0x70,0x63,0xD9,0x39,0xBC,0x6F, +0x1C,0xC2,0x79,0xDC,0x76,0x29,0xAF,0xCE,0xC5,0x2C,0x64,0x04,0x5E,0x88,0x36,0x6E, +0x31,0xD4,0x40,0x1A,0x62,0x34,0x36,0x3F,0x35,0x01,0xAE,0xAC,0x63,0xA0, +}; + + +/* subject:/C=ZA/ST=Western Cape/L=Cape Town/O=Thawte Consulting cc/OU=Certification Services Division/CN=Thawte Server CA/emailAddress=server-certs@thawte.com */ +/* issuer :/C=ZA/ST=Western Cape/L=Cape Town/O=Thawte Consulting cc/OU=Certification Services Division/CN=Thawte Server CA/emailAddress=server-certs@thawte.com */ + + +const unsigned char Thawte_Server_CA_certificate[791]={ +0x30,0x82,0x03,0x13,0x30,0x82,0x02,0x7C,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x04,0x05,0x00,0x30, +0x81,0xC4,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x5A,0x41,0x31, +0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x08,0x13,0x0C,0x57,0x65,0x73,0x74,0x65,0x72, +0x6E,0x20,0x43,0x61,0x70,0x65,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x13, +0x09,0x43,0x61,0x70,0x65,0x20,0x54,0x6F,0x77,0x6E,0x31,0x1D,0x30,0x1B,0x06,0x03, +0x55,0x04,0x0A,0x13,0x14,0x54,0x68,0x61,0x77,0x74,0x65,0x20,0x43,0x6F,0x6E,0x73, +0x75,0x6C,0x74,0x69,0x6E,0x67,0x20,0x63,0x63,0x31,0x28,0x30,0x26,0x06,0x03,0x55, +0x04,0x0B,0x13,0x1F,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F, +0x6E,0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x20,0x44,0x69,0x76,0x69,0x73, +0x69,0x6F,0x6E,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x03,0x13,0x10,0x54,0x68, +0x61,0x77,0x74,0x65,0x20,0x53,0x65,0x72,0x76,0x65,0x72,0x20,0x43,0x41,0x31,0x26, +0x30,0x24,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x01,0x16,0x17,0x73, +0x65,0x72,0x76,0x65,0x72,0x2D,0x63,0x65,0x72,0x74,0x73,0x40,0x74,0x68,0x61,0x77, +0x74,0x65,0x2E,0x63,0x6F,0x6D,0x30,0x1E,0x17,0x0D,0x39,0x36,0x30,0x38,0x30,0x31, +0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,0x30,0x31,0x32,0x33,0x31,0x32, +0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x81,0xC4,0x31,0x0B,0x30,0x09,0x06,0x03,0x55, +0x04,0x06,0x13,0x02,0x5A,0x41,0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x08,0x13, +0x0C,0x57,0x65,0x73,0x74,0x65,0x72,0x6E,0x20,0x43,0x61,0x70,0x65,0x31,0x12,0x30, +0x10,0x06,0x03,0x55,0x04,0x07,0x13,0x09,0x43,0x61,0x70,0x65,0x20,0x54,0x6F,0x77, +0x6E,0x31,0x1D,0x30,0x1B,0x06,0x03,0x55,0x04,0x0A,0x13,0x14,0x54,0x68,0x61,0x77, +0x74,0x65,0x20,0x43,0x6F,0x6E,0x73,0x75,0x6C,0x74,0x69,0x6E,0x67,0x20,0x63,0x63, +0x31,0x28,0x30,0x26,0x06,0x03,0x55,0x04,0x0B,0x13,0x1F,0x43,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65, +0x73,0x20,0x44,0x69,0x76,0x69,0x73,0x69,0x6F,0x6E,0x31,0x19,0x30,0x17,0x06,0x03, +0x55,0x04,0x03,0x13,0x10,0x54,0x68,0x61,0x77,0x74,0x65,0x20,0x53,0x65,0x72,0x76, +0x65,0x72,0x20,0x43,0x41,0x31,0x26,0x30,0x24,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x09,0x01,0x16,0x17,0x73,0x65,0x72,0x76,0x65,0x72,0x2D,0x63,0x65,0x72, +0x74,0x73,0x40,0x74,0x68,0x61,0x77,0x74,0x65,0x2E,0x63,0x6F,0x6D,0x30,0x81,0x9F, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03, +0x81,0x8D,0x00,0x30,0x81,0x89,0x02,0x81,0x81,0x00,0xD3,0xA4,0x50,0x6E,0xC8,0xFF, +0x56,0x6B,0xE6,0xCF,0x5D,0xB6,0xEA,0x0C,0x68,0x75,0x47,0xA2,0xAA,0xC2,0xDA,0x84, +0x25,0xFC,0xA8,0xF4,0x47,0x51,0xDA,0x85,0xB5,0x20,0x74,0x94,0x86,0x1E,0x0F,0x75, +0xC9,0xE9,0x08,0x61,0xF5,0x06,0x6D,0x30,0x6E,0x15,0x19,0x02,0xE9,0x52,0xC0,0x62, +0xDB,0x4D,0x99,0x9E,0xE2,0x6A,0x0C,0x44,0x38,0xCD,0xFE,0xBE,0xE3,0x64,0x09,0x70, +0xC5,0xFE,0xB1,0x6B,0x29,0xB6,0x2F,0x49,0xC8,0x3B,0xD4,0x27,0x04,0x25,0x10,0x97, +0x2F,0xE7,0x90,0x6D,0xC0,0x28,0x42,0x99,0xD7,0x4C,0x43,0xDE,0xC3,0xF5,0x21,0x6D, +0x54,0x9F,0x5D,0xC3,0x58,0xE1,0xC0,0xE4,0xD9,0x5B,0xB0,0xB8,0xDC,0xB4,0x7B,0xDF, +0x36,0x3A,0xC2,0xB5,0x66,0x22,0x12,0xD6,0x87,0x0D,0x02,0x03,0x01,0x00,0x01,0xA3, +0x13,0x30,0x11,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30, +0x03,0x01,0x01,0xFF,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01, +0x04,0x05,0x00,0x03,0x81,0x81,0x00,0x07,0xFA,0x4C,0x69,0x5C,0xFB,0x95,0xCC,0x46, +0xEE,0x85,0x83,0x4D,0x21,0x30,0x8E,0xCA,0xD9,0xA8,0x6F,0x49,0x1A,0xE6,0xDA,0x51, +0xE3,0x60,0x70,0x6C,0x84,0x61,0x11,0xA1,0x1A,0xC8,0x48,0x3E,0x59,0x43,0x7D,0x4F, +0x95,0x3D,0xA1,0x8B,0xB7,0x0B,0x62,0x98,0x7A,0x75,0x8A,0xDD,0x88,0x4E,0x4E,0x9E, +0x40,0xDB,0xA8,0xCC,0x32,0x74,0xB9,0x6F,0x0D,0xC6,0xE3,0xB3,0x44,0x0B,0xD9,0x8A, +0x6F,0x9A,0x29,0x9B,0x99,0x18,0x28,0x3B,0xD1,0xE3,0x40,0x28,0x9A,0x5A,0x3C,0xD5, +0xB5,0xE7,0x20,0x1B,0x8B,0xCA,0xA4,0xAB,0x8D,0xE9,0x51,0xD9,0xE2,0x4C,0x2C,0x59, +0xA9,0xDA,0xB9,0xB2,0x75,0x1B,0xF6,0x42,0xF2,0xEF,0xC7,0xF2,0x18,0xF9,0x89,0xBC, +0xA3,0xFF,0x8A,0x23,0x2E,0x70,0x47, +}; + + +/* subject:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN - DATACorp SGC */ +/* issuer :/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN - DATACorp SGC */ + + +const unsigned char UTN_DATACorp_SGC_Root_CA_certificate[1122]={ +0x30,0x82,0x04,0x5E,0x30,0x82,0x03,0x46,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x44, +0xBE,0x0C,0x8B,0x50,0x00,0x21,0xB4,0x11,0xD3,0x2A,0x68,0x06,0xA9,0xAD,0x69,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x81, +0x93,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x08,0x13,0x02,0x55,0x54,0x31,0x17,0x30,0x15,0x06, +0x03,0x55,0x04,0x07,0x13,0x0E,0x53,0x61,0x6C,0x74,0x20,0x4C,0x61,0x6B,0x65,0x20, +0x43,0x69,0x74,0x79,0x31,0x1E,0x30,0x1C,0x06,0x03,0x55,0x04,0x0A,0x13,0x15,0x54, +0x68,0x65,0x20,0x55,0x53,0x45,0x52,0x54,0x52,0x55,0x53,0x54,0x20,0x4E,0x65,0x74, +0x77,0x6F,0x72,0x6B,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x0B,0x13,0x18,0x68, +0x74,0x74,0x70,0x3A,0x2F,0x2F,0x77,0x77,0x77,0x2E,0x75,0x73,0x65,0x72,0x74,0x72, +0x75,0x73,0x74,0x2E,0x63,0x6F,0x6D,0x31,0x1B,0x30,0x19,0x06,0x03,0x55,0x04,0x03, +0x13,0x12,0x55,0x54,0x4E,0x20,0x2D,0x20,0x44,0x41,0x54,0x41,0x43,0x6F,0x72,0x70, +0x20,0x53,0x47,0x43,0x30,0x1E,0x17,0x0D,0x39,0x39,0x30,0x36,0x32,0x34,0x31,0x38, +0x35,0x37,0x32,0x31,0x5A,0x17,0x0D,0x31,0x39,0x30,0x36,0x32,0x34,0x31,0x39,0x30, +0x36,0x33,0x30,0x5A,0x30,0x81,0x93,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06, +0x13,0x02,0x55,0x53,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x08,0x13,0x02,0x55, +0x54,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x07,0x13,0x0E,0x53,0x61,0x6C,0x74, +0x20,0x4C,0x61,0x6B,0x65,0x20,0x43,0x69,0x74,0x79,0x31,0x1E,0x30,0x1C,0x06,0x03, +0x55,0x04,0x0A,0x13,0x15,0x54,0x68,0x65,0x20,0x55,0x53,0x45,0x52,0x54,0x52,0x55, +0x53,0x54,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x31,0x21,0x30,0x1F,0x06,0x03, +0x55,0x04,0x0B,0x13,0x18,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x77,0x77,0x77,0x2E, +0x75,0x73,0x65,0x72,0x74,0x72,0x75,0x73,0x74,0x2E,0x63,0x6F,0x6D,0x31,0x1B,0x30, +0x19,0x06,0x03,0x55,0x04,0x03,0x13,0x12,0x55,0x54,0x4E,0x20,0x2D,0x20,0x44,0x41, +0x54,0x41,0x43,0x6F,0x72,0x70,0x20,0x53,0x47,0x43,0x30,0x82,0x01,0x22,0x30,0x0D, +0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01, +0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xDF,0xEE,0x58,0x10,0xA2, +0x2B,0x6E,0x55,0xC4,0x8E,0xBF,0x2E,0x46,0x09,0xE7,0xE0,0x08,0x0F,0x2E,0x2B,0x7A, +0x13,0x94,0x1B,0xBD,0xF6,0xB6,0x80,0x8E,0x65,0x05,0x93,0x00,0x1E,0xBC,0xAF,0xE2, +0x0F,0x8E,0x19,0x0D,0x12,0x47,0xEC,0xAC,0xAD,0xA3,0xFA,0x2E,0x70,0xF8,0xDE,0x6E, +0xFB,0x56,0x42,0x15,0x9E,0x2E,0x5C,0xEF,0x23,0xDE,0x21,0xB9,0x05,0x76,0x27,0x19, +0x0F,0x4F,0xD6,0xC3,0x9C,0xB4,0xBE,0x94,0x19,0x63,0xF2,0xA6,0x11,0x0A,0xEB,0x53, +0x48,0x9C,0xBE,0xF2,0x29,0x3B,0x16,0xE8,0x1A,0xA0,0x4C,0xA6,0xC9,0xF4,0x18,0x59, +0x68,0xC0,0x70,0xF2,0x53,0x00,0xC0,0x5E,0x50,0x82,0xA5,0x56,0x6F,0x36,0xF9,0x4A, +0xE0,0x44,0x86,0xA0,0x4D,0x4E,0xD6,0x47,0x6E,0x49,0x4A,0xCB,0x67,0xD7,0xA6,0xC4, +0x05,0xB9,0x8E,0x1E,0xF4,0xFC,0xFF,0xCD,0xE7,0x36,0xE0,0x9C,0x05,0x6C,0xB2,0x33, +0x22,0x15,0xD0,0xB4,0xE0,0xCC,0x17,0xC0,0xB2,0xC0,0xF4,0xFE,0x32,0x3F,0x29,0x2A, +0x95,0x7B,0xD8,0xF2,0xA7,0x4E,0x0F,0x54,0x7C,0xA1,0x0D,0x80,0xB3,0x09,0x03,0xC1, +0xFF,0x5C,0xDD,0x5E,0x9A,0x3E,0xBC,0xAE,0xBC,0x47,0x8A,0x6A,0xAE,0x71,0xCA,0x1F, +0xB1,0x2A,0xB8,0x5F,0x42,0x05,0x0B,0xEC,0x46,0x30,0xD1,0x72,0x0B,0xCA,0xE9,0x56, +0x6D,0xF5,0xEF,0xDF,0x78,0xBE,0x61,0xBA,0xB2,0xA5,0xAE,0x04,0x4C,0xBC,0xA8,0xAC, +0x69,0x15,0x97,0xBD,0xEF,0xEB,0xB4,0x8C,0xBF,0x35,0xF8,0xD4,0xC3,0xD1,0x28,0x0E, +0x5C,0x3A,0x9F,0x70,0x18,0x33,0x20,0x77,0xC4,0xA2,0xAF,0x02,0x03,0x01,0x00,0x01, +0xA3,0x81,0xAB,0x30,0x81,0xA8,0x30,0x0B,0x06,0x03,0x55,0x1D,0x0F,0x04,0x04,0x03, +0x02,0x01,0xC6,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30, +0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x53, +0x32,0xD1,0xB3,0xCF,0x7F,0xFA,0xE0,0xF1,0xA0,0x5D,0x85,0x4E,0x92,0xD2,0x9E,0x45, +0x1D,0xB4,0x4F,0x30,0x3D,0x06,0x03,0x55,0x1D,0x1F,0x04,0x36,0x30,0x34,0x30,0x32, +0xA0,0x30,0xA0,0x2E,0x86,0x2C,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C, +0x2E,0x75,0x73,0x65,0x72,0x74,0x72,0x75,0x73,0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x55, +0x54,0x4E,0x2D,0x44,0x41,0x54,0x41,0x43,0x6F,0x72,0x70,0x53,0x47,0x43,0x2E,0x63, +0x72,0x6C,0x30,0x2A,0x06,0x03,0x55,0x1D,0x25,0x04,0x23,0x30,0x21,0x06,0x08,0x2B, +0x06,0x01,0x05,0x05,0x07,0x03,0x01,0x06,0x0A,0x2B,0x06,0x01,0x04,0x01,0x82,0x37, +0x0A,0x03,0x03,0x06,0x09,0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x04,0x01,0x30,0x0D, +0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01, +0x01,0x00,0x27,0x35,0x97,0x00,0x8A,0x8B,0x28,0xBD,0xC6,0x33,0x30,0x1E,0x29,0xFC, +0xE2,0xF7,0xD5,0x98,0xD4,0x40,0xBB,0x60,0xCA,0xBF,0xAB,0x17,0x2C,0x09,0x36,0x7F, +0x50,0xFA,0x41,0xDC,0xAE,0x96,0x3A,0x0A,0x23,0x3E,0x89,0x59,0xC9,0xA3,0x07,0xED, +0x1B,0x37,0xAD,0xFC,0x7C,0xBE,0x51,0x49,0x5A,0xDE,0x3A,0x0A,0x54,0x08,0x16,0x45, +0xC2,0x99,0xB1,0x87,0xCD,0x8C,0x68,0xE0,0x69,0x03,0xE9,0xC4,0x4E,0x98,0xB2,0x3B, +0x8C,0x16,0xB3,0x0E,0xA0,0x0C,0x98,0x50,0x9B,0x93,0xA9,0x70,0x09,0xC8,0x2C,0xA3, +0x8F,0xDF,0x02,0xE4,0xE0,0x71,0x3A,0xF1,0xB4,0x23,0x72,0xA0,0xAA,0x01,0xDF,0xDF, +0x98,0x3E,0x14,0x50,0xA0,0x31,0x26,0xBD,0x28,0xE9,0x5A,0x30,0x26,0x75,0xF9,0x7B, +0x60,0x1C,0x8D,0xF3,0xCD,0x50,0x26,0x6D,0x04,0x27,0x9A,0xDF,0xD5,0x0D,0x45,0x47, +0x29,0x6B,0x2C,0xE6,0x76,0xD9,0xA9,0x29,0x7D,0x32,0xDD,0xC9,0x36,0x3C,0xBD,0xAE, +0x35,0xF1,0x11,0x9E,0x1D,0xBB,0x90,0x3F,0x12,0x47,0x4E,0x8E,0xD7,0x7E,0x0F,0x62, +0x73,0x1D,0x52,0x26,0x38,0x1C,0x18,0x49,0xFD,0x30,0x74,0x9A,0xC4,0xE5,0x22,0x2F, +0xD8,0xC0,0x8D,0xED,0x91,0x7A,0x4C,0x00,0x8F,0x72,0x7F,0x5D,0xDA,0xDD,0x1B,0x8B, +0x45,0x6B,0xE7,0xDD,0x69,0x97,0xA8,0xC5,0x56,0x4C,0x0F,0x0C,0xF6,0x9F,0x7A,0x91, +0x37,0xF6,0x97,0x82,0xE0,0xDD,0x71,0x69,0xFF,0x76,0x3F,0x60,0x4D,0x3C,0xCF,0xF7, +0x99,0xF9,0xC6,0x57,0xF4,0xC9,0x55,0x39,0x78,0xBA,0x2C,0x79,0xC9,0xA6,0x88,0x2B, +0xF4,0x08, +}; + + +/* subject:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN-USERFirst-Hardware */ +/* issuer :/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN-USERFirst-Hardware */ + + +const unsigned char UTN_USERFirst_Hardware_Root_CA_certificate[1144]={ +0x30,0x82,0x04,0x74,0x30,0x82,0x03,0x5C,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x44, +0xBE,0x0C,0x8B,0x50,0x00,0x24,0xB4,0x11,0xD3,0x36,0x2A,0xFE,0x65,0x0A,0xFD,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x81, +0x97,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x08,0x13,0x02,0x55,0x54,0x31,0x17,0x30,0x15,0x06, +0x03,0x55,0x04,0x07,0x13,0x0E,0x53,0x61,0x6C,0x74,0x20,0x4C,0x61,0x6B,0x65,0x20, +0x43,0x69,0x74,0x79,0x31,0x1E,0x30,0x1C,0x06,0x03,0x55,0x04,0x0A,0x13,0x15,0x54, +0x68,0x65,0x20,0x55,0x53,0x45,0x52,0x54,0x52,0x55,0x53,0x54,0x20,0x4E,0x65,0x74, +0x77,0x6F,0x72,0x6B,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x0B,0x13,0x18,0x68, +0x74,0x74,0x70,0x3A,0x2F,0x2F,0x77,0x77,0x77,0x2E,0x75,0x73,0x65,0x72,0x74,0x72, +0x75,0x73,0x74,0x2E,0x63,0x6F,0x6D,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x03, +0x13,0x16,0x55,0x54,0x4E,0x2D,0x55,0x53,0x45,0x52,0x46,0x69,0x72,0x73,0x74,0x2D, +0x48,0x61,0x72,0x64,0x77,0x61,0x72,0x65,0x30,0x1E,0x17,0x0D,0x39,0x39,0x30,0x37, +0x30,0x39,0x31,0x38,0x31,0x30,0x34,0x32,0x5A,0x17,0x0D,0x31,0x39,0x30,0x37,0x30, +0x39,0x31,0x38,0x31,0x39,0x32,0x32,0x5A,0x30,0x81,0x97,0x31,0x0B,0x30,0x09,0x06, +0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04, +0x08,0x13,0x02,0x55,0x54,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x07,0x13,0x0E, +0x53,0x61,0x6C,0x74,0x20,0x4C,0x61,0x6B,0x65,0x20,0x43,0x69,0x74,0x79,0x31,0x1E, +0x30,0x1C,0x06,0x03,0x55,0x04,0x0A,0x13,0x15,0x54,0x68,0x65,0x20,0x55,0x53,0x45, +0x52,0x54,0x52,0x55,0x53,0x54,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x31,0x21, +0x30,0x1F,0x06,0x03,0x55,0x04,0x0B,0x13,0x18,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F, +0x77,0x77,0x77,0x2E,0x75,0x73,0x65,0x72,0x74,0x72,0x75,0x73,0x74,0x2E,0x63,0x6F, +0x6D,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x03,0x13,0x16,0x55,0x54,0x4E,0x2D, +0x55,0x53,0x45,0x52,0x46,0x69,0x72,0x73,0x74,0x2D,0x48,0x61,0x72,0x64,0x77,0x61, +0x72,0x65,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D, +0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82, +0x01,0x01,0x00,0xB1,0xF7,0xC3,0x38,0x3F,0xB4,0xA8,0x7F,0xCF,0x39,0x82,0x51,0x67, +0xD0,0x6D,0x9F,0xD2,0xFF,0x58,0xF3,0xE7,0x9F,0x2B,0xEC,0x0D,0x89,0x54,0x99,0xB9, +0x38,0x99,0x16,0xF7,0xE0,0x21,0x79,0x48,0xC2,0xBB,0x61,0x74,0x12,0x96,0x1D,0x3C, +0x6A,0x72,0xD5,0x3C,0x10,0x67,0x3A,0x39,0xED,0x2B,0x13,0xCD,0x66,0xEB,0x95,0x09, +0x33,0xA4,0x6C,0x97,0xB1,0xE8,0xC6,0xEC,0xC1,0x75,0x79,0x9C,0x46,0x5E,0x8D,0xAB, +0xD0,0x6A,0xFD,0xB9,0x2A,0x55,0x17,0x10,0x54,0xB3,0x19,0xF0,0x9A,0xF6,0xF1,0xB1, +0x5D,0xB6,0xA7,0x6D,0xFB,0xE0,0x71,0x17,0x6B,0xA2,0x88,0xFB,0x00,0xDF,0xFE,0x1A, +0x31,0x77,0x0C,0x9A,0x01,0x7A,0xB1,0x32,0xE3,0x2B,0x01,0x07,0x38,0x6E,0xC3,0xA5, +0x5E,0x23,0xBC,0x45,0x9B,0x7B,0x50,0xC1,0xC9,0x30,0x8F,0xDB,0xE5,0x2B,0x7A,0xD3, +0x5B,0xFB,0x33,0x40,0x1E,0xA0,0xD5,0x98,0x17,0xBC,0x8B,0x87,0xC3,0x89,0xD3,0x5D, +0xA0,0x8E,0xB2,0xAA,0xAA,0xF6,0x8E,0x69,0x88,0x06,0xC5,0xFA,0x89,0x21,0xF3,0x08, +0x9D,0x69,0x2E,0x09,0x33,0x9B,0x29,0x0D,0x46,0x0F,0x8C,0xCC,0x49,0x34,0xB0,0x69, +0x51,0xBD,0xF9,0x06,0xCD,0x68,0xAD,0x66,0x4C,0xBC,0x3E,0xAC,0x61,0xBD,0x0A,0x88, +0x0E,0xC8,0xDF,0x3D,0xEE,0x7C,0x04,0x4C,0x9D,0x0A,0x5E,0x6B,0x91,0xD6,0xEE,0xC7, +0xED,0x28,0x8D,0xAB,0x4D,0x87,0x89,0x73,0xD0,0x6E,0xA4,0xD0,0x1E,0x16,0x8B,0x14, +0xE1,0x76,0x44,0x03,0x7F,0x63,0xAC,0xE4,0xCD,0x49,0x9C,0xC5,0x92,0xF4,0xAB,0x32, +0xA1,0x48,0x5B,0x02,0x03,0x01,0x00,0x01,0xA3,0x81,0xB9,0x30,0x81,0xB6,0x30,0x0B, +0x06,0x03,0x55,0x1D,0x0F,0x04,0x04,0x03,0x02,0x01,0xC6,0x30,0x0F,0x06,0x03,0x55, +0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03, +0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xA1,0x72,0x5F,0x26,0x1B,0x28,0x98,0x43,0x95, +0x5D,0x07,0x37,0xD5,0x85,0x96,0x9D,0x4B,0xD2,0xC3,0x45,0x30,0x44,0x06,0x03,0x55, +0x1D,0x1F,0x04,0x3D,0x30,0x3B,0x30,0x39,0xA0,0x37,0xA0,0x35,0x86,0x33,0x68,0x74, +0x74,0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x2E,0x75,0x73,0x65,0x72,0x74,0x72,0x75, +0x73,0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x55,0x54,0x4E,0x2D,0x55,0x53,0x45,0x52,0x46, +0x69,0x72,0x73,0x74,0x2D,0x48,0x61,0x72,0x64,0x77,0x61,0x72,0x65,0x2E,0x63,0x72, +0x6C,0x30,0x31,0x06,0x03,0x55,0x1D,0x25,0x04,0x2A,0x30,0x28,0x06,0x08,0x2B,0x06, +0x01,0x05,0x05,0x07,0x03,0x01,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x05, +0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x06,0x06,0x08,0x2B,0x06,0x01,0x05, +0x05,0x07,0x03,0x07,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01, +0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x47,0x19,0x0F,0xDE,0x74,0xC6,0x99,0x97, +0xAF,0xFC,0xAD,0x28,0x5E,0x75,0x8E,0xEB,0x2D,0x67,0xEE,0x4E,0x7B,0x2B,0xD7,0x0C, +0xFF,0xF6,0xDE,0xCB,0x55,0xA2,0x0A,0xE1,0x4C,0x54,0x65,0x93,0x60,0x6B,0x9F,0x12, +0x9C,0xAD,0x5E,0x83,0x2C,0xEB,0x5A,0xAE,0xC0,0xE4,0x2D,0xF4,0x00,0x63,0x1D,0xB8, +0xC0,0x6C,0xF2,0xCF,0x49,0xBB,0x4D,0x93,0x6F,0x06,0xA6,0x0A,0x22,0xB2,0x49,0x62, +0x08,0x4E,0xFF,0xC8,0xC8,0x14,0xB2,0x88,0x16,0x5D,0xE7,0x01,0xE4,0x12,0x95,0xE5, +0x45,0x34,0xB3,0x8B,0x69,0xBD,0xCF,0xB4,0x85,0x8F,0x75,0x51,0x9E,0x7D,0x3A,0x38, +0x3A,0x14,0x48,0x12,0xC6,0xFB,0xA7,0x3B,0x1A,0x8D,0x0D,0x82,0x40,0x07,0xE8,0x04, +0x08,0x90,0xA1,0x89,0xCB,0x19,0x50,0xDF,0xCA,0x1C,0x01,0xBC,0x1D,0x04,0x19,0x7B, +0x10,0x76,0x97,0x3B,0xEE,0x90,0x90,0xCA,0xC4,0x0E,0x1F,0x16,0x6E,0x75,0xEF,0x33, +0xF8,0xD3,0x6F,0x5B,0x1E,0x96,0xE3,0xE0,0x74,0x77,0x74,0x7B,0x8A,0xA2,0x6E,0x2D, +0xDD,0x76,0xD6,0x39,0x30,0x82,0xF0,0xAB,0x9C,0x52,0xF2,0x2A,0xC7,0xAF,0x49,0x5E, +0x7E,0xC7,0x68,0xE5,0x82,0x81,0xC8,0x6A,0x27,0xF9,0x27,0x88,0x2A,0xD5,0x58,0x50, +0x95,0x1F,0xF0,0x3B,0x1C,0x57,0xBB,0x7D,0x14,0x39,0x62,0x2B,0x9A,0xC9,0x94,0x92, +0x2A,0xA3,0x22,0x0C,0xFF,0x89,0x26,0x7D,0x5F,0x23,0x2B,0x47,0xD7,0x15,0x1D,0xA9, +0x6A,0x9E,0x51,0x0D,0x2A,0x51,0x9E,0x81,0xF9,0xD4,0x3B,0x5E,0x70,0x12,0x7F,0x10, +0x32,0x9C,0x1E,0xBB,0x9D,0xF8,0x66,0xA8, +}; + + +/* subject:/L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 1 Policy Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com */ +/* issuer :/L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 1 Policy Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com */ + + +const unsigned char ValiCert_Class_1_VA_certificate[747]={ +0x30,0x82,0x02,0xE7,0x30,0x82,0x02,0x50,0x02,0x01,0x01,0x30,0x0D,0x06,0x09,0x2A, +0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x81,0xBB,0x31,0x24,0x30, +0x22,0x06,0x03,0x55,0x04,0x07,0x13,0x1B,0x56,0x61,0x6C,0x69,0x43,0x65,0x72,0x74, +0x20,0x56,0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x4E,0x65,0x74,0x77, +0x6F,0x72,0x6B,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x0A,0x13,0x0E,0x56,0x61, +0x6C,0x69,0x43,0x65,0x72,0x74,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x35,0x30,0x33, +0x06,0x03,0x55,0x04,0x0B,0x13,0x2C,0x56,0x61,0x6C,0x69,0x43,0x65,0x72,0x74,0x20, +0x43,0x6C,0x61,0x73,0x73,0x20,0x31,0x20,0x50,0x6F,0x6C,0x69,0x63,0x79,0x20,0x56, +0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72, +0x69,0x74,0x79,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x03,0x13,0x18,0x68,0x74, +0x74,0x70,0x3A,0x2F,0x2F,0x77,0x77,0x77,0x2E,0x76,0x61,0x6C,0x69,0x63,0x65,0x72, +0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x31,0x20,0x30,0x1E,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x09,0x01,0x16,0x11,0x69,0x6E,0x66,0x6F,0x40,0x76,0x61,0x6C,0x69, +0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x30,0x1E,0x17,0x0D,0x39,0x39,0x30,0x36, +0x32,0x35,0x32,0x32,0x32,0x33,0x34,0x38,0x5A,0x17,0x0D,0x31,0x39,0x30,0x36,0x32, +0x35,0x32,0x32,0x32,0x33,0x34,0x38,0x5A,0x30,0x81,0xBB,0x31,0x24,0x30,0x22,0x06, +0x03,0x55,0x04,0x07,0x13,0x1B,0x56,0x61,0x6C,0x69,0x43,0x65,0x72,0x74,0x20,0x56, +0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72, +0x6B,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x0A,0x13,0x0E,0x56,0x61,0x6C,0x69, +0x43,0x65,0x72,0x74,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x35,0x30,0x33,0x06,0x03, +0x55,0x04,0x0B,0x13,0x2C,0x56,0x61,0x6C,0x69,0x43,0x65,0x72,0x74,0x20,0x43,0x6C, +0x61,0x73,0x73,0x20,0x31,0x20,0x50,0x6F,0x6C,0x69,0x63,0x79,0x20,0x56,0x61,0x6C, +0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74, +0x79,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x03,0x13,0x18,0x68,0x74,0x74,0x70, +0x3A,0x2F,0x2F,0x77,0x77,0x77,0x2E,0x76,0x61,0x6C,0x69,0x63,0x65,0x72,0x74,0x2E, +0x63,0x6F,0x6D,0x2F,0x31,0x20,0x30,0x1E,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D, +0x01,0x09,0x01,0x16,0x11,0x69,0x6E,0x66,0x6F,0x40,0x76,0x61,0x6C,0x69,0x63,0x65, +0x72,0x74,0x2E,0x63,0x6F,0x6D,0x30,0x81,0x9F,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48, +0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x81,0x8D,0x00,0x30,0x81,0x89,0x02, +0x81,0x81,0x00,0xD8,0x59,0x82,0x7A,0x89,0xB8,0x96,0xBA,0xA6,0x2F,0x68,0x6F,0x58, +0x2E,0xA7,0x54,0x1C,0x06,0x6E,0xF4,0xEA,0x8D,0x48,0xBC,0x31,0x94,0x17,0xF0,0xF3, +0x4E,0xBC,0xB2,0xB8,0x35,0x92,0x76,0xB0,0xD0,0xA5,0xA5,0x01,0xD7,0x00,0x03,0x12, +0x22,0x19,0x08,0xF8,0xFF,0x11,0x23,0x9B,0xCE,0x07,0xF5,0xBF,0x69,0x1A,0x26,0xFE, +0x4E,0xE9,0xD1,0x7F,0x9D,0x2C,0x40,0x1D,0x59,0x68,0x6E,0xA6,0xF8,0x58,0xB0,0x9D, +0x1A,0x8F,0xD3,0x3F,0xF1,0xDC,0x19,0x06,0x81,0xA8,0x0E,0xE0,0x3A,0xDD,0xC8,0x53, +0x45,0x09,0x06,0xE6,0x0F,0x70,0xC3,0xFA,0x40,0xA6,0x0E,0xE2,0x56,0x05,0x0F,0x18, +0x4D,0xFC,0x20,0x82,0xD1,0x73,0x55,0x74,0x8D,0x76,0x72,0xA0,0x1D,0x9D,0x1D,0xC0, +0xDD,0x3F,0x71,0x02,0x03,0x01,0x00,0x01,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x81,0x81,0x00,0x50,0x68,0x3D,0x49,0xF4, +0x2C,0x1C,0x06,0x94,0xDF,0x95,0x60,0x7F,0x96,0x7B,0x17,0xFE,0x4F,0x71,0xAD,0x64, +0xC8,0xDD,0x77,0xD2,0xEF,0x59,0x55,0xE8,0x3F,0xE8,0x8E,0x05,0x2A,0x21,0xF2,0x07, +0xD2,0xB5,0xA7,0x52,0xFE,0x9C,0xB1,0xB6,0xE2,0x5B,0x77,0x17,0x40,0xEA,0x72,0xD6, +0x23,0xCB,0x28,0x81,0x32,0xC3,0x00,0x79,0x18,0xEC,0x59,0x17,0x89,0xC9,0xC6,0x6A, +0x1E,0x71,0xC9,0xFD,0xB7,0x74,0xA5,0x25,0x45,0x69,0xC5,0x48,0xAB,0x19,0xE1,0x45, +0x8A,0x25,0x6B,0x19,0xEE,0xE5,0xBB,0x12,0xF5,0x7F,0xF7,0xA6,0x8D,0x51,0xC3,0xF0, +0x9D,0x74,0xB7,0xA9,0x3E,0xA0,0xA5,0xFF,0xB6,0x49,0x03,0x13,0xDA,0x22,0xCC,0xED, +0x71,0x82,0x2B,0x99,0xCF,0x3A,0xB7,0xF5,0x2D,0x72,0xC8, +}; + + +/* subject:/L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 2 Policy Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com */ +/* issuer :/L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 2 Policy Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com */ + + +const unsigned char ValiCert_Class_2_VA_certificate[747]={ +0x30,0x82,0x02,0xE7,0x30,0x82,0x02,0x50,0x02,0x01,0x01,0x30,0x0D,0x06,0x09,0x2A, +0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x81,0xBB,0x31,0x24,0x30, +0x22,0x06,0x03,0x55,0x04,0x07,0x13,0x1B,0x56,0x61,0x6C,0x69,0x43,0x65,0x72,0x74, +0x20,0x56,0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x4E,0x65,0x74,0x77, +0x6F,0x72,0x6B,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x0A,0x13,0x0E,0x56,0x61, +0x6C,0x69,0x43,0x65,0x72,0x74,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x35,0x30,0x33, +0x06,0x03,0x55,0x04,0x0B,0x13,0x2C,0x56,0x61,0x6C,0x69,0x43,0x65,0x72,0x74,0x20, +0x43,0x6C,0x61,0x73,0x73,0x20,0x32,0x20,0x50,0x6F,0x6C,0x69,0x63,0x79,0x20,0x56, +0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72, +0x69,0x74,0x79,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x03,0x13,0x18,0x68,0x74, +0x74,0x70,0x3A,0x2F,0x2F,0x77,0x77,0x77,0x2E,0x76,0x61,0x6C,0x69,0x63,0x65,0x72, +0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x31,0x20,0x30,0x1E,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x09,0x01,0x16,0x11,0x69,0x6E,0x66,0x6F,0x40,0x76,0x61,0x6C,0x69, +0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x30,0x1E,0x17,0x0D,0x39,0x39,0x30,0x36, +0x32,0x36,0x30,0x30,0x31,0x39,0x35,0x34,0x5A,0x17,0x0D,0x31,0x39,0x30,0x36,0x32, +0x36,0x30,0x30,0x31,0x39,0x35,0x34,0x5A,0x30,0x81,0xBB,0x31,0x24,0x30,0x22,0x06, +0x03,0x55,0x04,0x07,0x13,0x1B,0x56,0x61,0x6C,0x69,0x43,0x65,0x72,0x74,0x20,0x56, +0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72, +0x6B,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x0A,0x13,0x0E,0x56,0x61,0x6C,0x69, +0x43,0x65,0x72,0x74,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x35,0x30,0x33,0x06,0x03, +0x55,0x04,0x0B,0x13,0x2C,0x56,0x61,0x6C,0x69,0x43,0x65,0x72,0x74,0x20,0x43,0x6C, +0x61,0x73,0x73,0x20,0x32,0x20,0x50,0x6F,0x6C,0x69,0x63,0x79,0x20,0x56,0x61,0x6C, +0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74, +0x79,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x03,0x13,0x18,0x68,0x74,0x74,0x70, +0x3A,0x2F,0x2F,0x77,0x77,0x77,0x2E,0x76,0x61,0x6C,0x69,0x63,0x65,0x72,0x74,0x2E, +0x63,0x6F,0x6D,0x2F,0x31,0x20,0x30,0x1E,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D, +0x01,0x09,0x01,0x16,0x11,0x69,0x6E,0x66,0x6F,0x40,0x76,0x61,0x6C,0x69,0x63,0x65, +0x72,0x74,0x2E,0x63,0x6F,0x6D,0x30,0x81,0x9F,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48, +0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x81,0x8D,0x00,0x30,0x81,0x89,0x02, +0x81,0x81,0x00,0xCE,0x3A,0x71,0xCA,0xE5,0xAB,0xC8,0x59,0x92,0x55,0xD7,0xAB,0xD8, +0x74,0x0E,0xF9,0xEE,0xD9,0xF6,0x55,0x47,0x59,0x65,0x47,0x0E,0x05,0x55,0xDC,0xEB, +0x98,0x36,0x3C,0x5C,0x53,0x5D,0xD3,0x30,0xCF,0x38,0xEC,0xBD,0x41,0x89,0xED,0x25, +0x42,0x09,0x24,0x6B,0x0A,0x5E,0xB3,0x7C,0xDD,0x52,0x2D,0x4C,0xE6,0xD4,0xD6,0x7D, +0x5A,0x59,0xA9,0x65,0xD4,0x49,0x13,0x2D,0x24,0x4D,0x1C,0x50,0x6F,0xB5,0xC1,0x85, +0x54,0x3B,0xFE,0x71,0xE4,0xD3,0x5C,0x42,0xF9,0x80,0xE0,0x91,0x1A,0x0A,0x5B,0x39, +0x36,0x67,0xF3,0x3F,0x55,0x7C,0x1B,0x3F,0xB4,0x5F,0x64,0x73,0x34,0xE3,0xB4,0x12, +0xBF,0x87,0x64,0xF8,0xDA,0x12,0xFF,0x37,0x27,0xC1,0xB3,0x43,0xBB,0xEF,0x7B,0x6E, +0x2E,0x69,0xF7,0x02,0x03,0x01,0x00,0x01,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x81,0x81,0x00,0x3B,0x7F,0x50,0x6F,0x6F, +0x50,0x94,0x99,0x49,0x62,0x38,0x38,0x1F,0x4B,0xF8,0xA5,0xC8,0x3E,0xA7,0x82,0x81, +0xF6,0x2B,0xC7,0xE8,0xC5,0xCE,0xE8,0x3A,0x10,0x82,0xCB,0x18,0x00,0x8E,0x4D,0xBD, +0xA8,0x58,0x7F,0xA1,0x79,0x00,0xB5,0xBB,0xE9,0x8D,0xAF,0x41,0xD9,0x0F,0x34,0xEE, +0x21,0x81,0x19,0xA0,0x32,0x49,0x28,0xF4,0xC4,0x8E,0x56,0xD5,0x52,0x33,0xFD,0x50, +0xD5,0x7E,0x99,0x6C,0x03,0xE4,0xC9,0x4C,0xFC,0xCB,0x6C,0xAB,0x66,0xB3,0x4A,0x21, +0x8C,0xE5,0xB5,0x0C,0x32,0x3E,0x10,0xB2,0xCC,0x6C,0xA1,0xDC,0x9A,0x98,0x4C,0x02, +0x5B,0xF3,0xCE,0xB9,0x9E,0xA5,0x72,0x0E,0x4A,0xB7,0x3F,0x3C,0xE6,0x16,0x68,0xF8, +0xBE,0xED,0x74,0x4C,0xBC,0x5B,0xD5,0x62,0x1F,0x43,0xDD, +}; + + +/* subject:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority */ +/* issuer :/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority */ + + +const unsigned char Verisign_Class_3_Public_Primary_Certification_Authority_certificate[576]={ +0x30,0x82,0x02,0x3C,0x30,0x82,0x01,0xA5,0x02,0x10,0x3C,0x91,0x31,0xCB,0x1F,0xF6, +0xD0,0x1B,0x0E,0x9A,0xB8,0xD0,0x44,0xBF,0x12,0xBE,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x5F,0x31,0x0B,0x30,0x09,0x06, +0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04, +0x0A,0x13,0x0E,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x2C,0x20,0x49,0x6E,0x63, +0x2E,0x31,0x37,0x30,0x35,0x06,0x03,0x55,0x04,0x0B,0x13,0x2E,0x43,0x6C,0x61,0x73, +0x73,0x20,0x33,0x20,0x50,0x75,0x62,0x6C,0x69,0x63,0x20,0x50,0x72,0x69,0x6D,0x61, +0x72,0x79,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E, +0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x1E,0x17,0x0D,0x39,0x36, +0x30,0x31,0x32,0x39,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,0x38,0x30, +0x38,0x30,0x32,0x32,0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x5F,0x31,0x0B,0x30,0x09, +0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x17,0x30,0x15,0x06,0x03,0x55, +0x04,0x0A,0x13,0x0E,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x2C,0x20,0x49,0x6E, +0x63,0x2E,0x31,0x37,0x30,0x35,0x06,0x03,0x55,0x04,0x0B,0x13,0x2E,0x43,0x6C,0x61, +0x73,0x73,0x20,0x33,0x20,0x50,0x75,0x62,0x6C,0x69,0x63,0x20,0x50,0x72,0x69,0x6D, +0x61,0x72,0x79,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F, +0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x81,0x9F,0x30,0x0D, +0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x81,0x8D, +0x00,0x30,0x81,0x89,0x02,0x81,0x81,0x00,0xC9,0x5C,0x59,0x9E,0xF2,0x1B,0x8A,0x01, +0x14,0xB4,0x10,0xDF,0x04,0x40,0xDB,0xE3,0x57,0xAF,0x6A,0x45,0x40,0x8F,0x84,0x0C, +0x0B,0xD1,0x33,0xD9,0xD9,0x11,0xCF,0xEE,0x02,0x58,0x1F,0x25,0xF7,0x2A,0xA8,0x44, +0x05,0xAA,0xEC,0x03,0x1F,0x78,0x7F,0x9E,0x93,0xB9,0x9A,0x00,0xAA,0x23,0x7D,0xD6, +0xAC,0x85,0xA2,0x63,0x45,0xC7,0x72,0x27,0xCC,0xF4,0x4C,0xC6,0x75,0x71,0xD2,0x39, +0xEF,0x4F,0x42,0xF0,0x75,0xDF,0x0A,0x90,0xC6,0x8E,0x20,0x6F,0x98,0x0F,0xF8,0xAC, +0x23,0x5F,0x70,0x29,0x36,0xA4,0xC9,0x86,0xE7,0xB1,0x9A,0x20,0xCB,0x53,0xA5,0x85, +0xE7,0x3D,0xBE,0x7D,0x9A,0xFE,0x24,0x45,0x33,0xDC,0x76,0x15,0xED,0x0F,0xA2,0x71, +0x64,0x4C,0x65,0x2E,0x81,0x68,0x45,0xA7,0x02,0x03,0x01,0x00,0x01,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x81,0x81,0x00, +0x10,0x72,0x52,0xA9,0x05,0x14,0x19,0x32,0x08,0x41,0xF0,0xC5,0x6B,0x0A,0xCC,0x7E, +0x0F,0x21,0x19,0xCD,0xE4,0x67,0xDC,0x5F,0xA9,0x1B,0xE6,0xCA,0xE8,0x73,0x9D,0x22, +0xD8,0x98,0x6E,0x73,0x03,0x61,0x91,0xC5,0x7C,0xB0,0x45,0x40,0x6E,0x44,0x9D,0x8D, +0xB0,0xB1,0x96,0x74,0x61,0x2D,0x0D,0xA9,0x45,0xD2,0xA4,0x92,0x2A,0xD6,0x9A,0x75, +0x97,0x6E,0x3F,0x53,0xFD,0x45,0x99,0x60,0x1D,0xA8,0x2B,0x4C,0xF9,0x5E,0xA7,0x09, +0xD8,0x75,0x30,0xD7,0xD2,0x65,0x60,0x3D,0x67,0xD6,0x48,0x55,0x75,0x69,0x3F,0x91, +0xF5,0x48,0x0B,0x47,0x69,0x22,0x69,0x82,0x96,0xBE,0xC9,0xC8,0x38,0x86,0x4A,0x7A, +0x2C,0x73,0x19,0x48,0x69,0x4E,0x6B,0x7C,0x65,0xBF,0x0F,0xFC,0x70,0xCE,0x88,0x90, +}; + + +/* subject:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority - G2/OU=(c) 1998 VeriSign, Inc. - For authorized use only/OU=VeriSign Trust Network */ +/* issuer :/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority - G2/OU=(c) 1998 VeriSign, Inc. - For authorized use only/OU=VeriSign Trust Network */ + + +const unsigned char Verisign_Class_3_Public_Primary_Certification_Authority___G2_certificate[774]={ +0x30,0x82,0x03,0x02,0x30,0x82,0x02,0x6B,0x02,0x10,0x7D,0xD9,0xFE,0x07,0xCF,0xA8, +0x1E,0xB7,0x10,0x79,0x67,0xFB,0xA7,0x89,0x34,0xC6,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x81,0xC1,0x31,0x0B,0x30,0x09, +0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x17,0x30,0x15,0x06,0x03,0x55, +0x04,0x0A,0x13,0x0E,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x2C,0x20,0x49,0x6E, +0x63,0x2E,0x31,0x3C,0x30,0x3A,0x06,0x03,0x55,0x04,0x0B,0x13,0x33,0x43,0x6C,0x61, +0x73,0x73,0x20,0x33,0x20,0x50,0x75,0x62,0x6C,0x69,0x63,0x20,0x50,0x72,0x69,0x6D, +0x61,0x72,0x79,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F, +0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x20,0x2D,0x20,0x47,0x32, +0x31,0x3A,0x30,0x38,0x06,0x03,0x55,0x04,0x0B,0x13,0x31,0x28,0x63,0x29,0x20,0x31, +0x39,0x39,0x38,0x20,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x2C,0x20,0x49,0x6E, +0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F,0x72,0x20,0x61,0x75,0x74,0x68,0x6F,0x72,0x69, +0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C,0x79,0x31,0x1F,0x30,0x1D, +0x06,0x03,0x55,0x04,0x0B,0x13,0x16,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x20, +0x54,0x72,0x75,0x73,0x74,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x30,0x1E,0x17, +0x0D,0x39,0x38,0x30,0x35,0x31,0x38,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D, +0x32,0x38,0x30,0x38,0x30,0x31,0x32,0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x81,0xC1, +0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x17,0x30, +0x15,0x06,0x03,0x55,0x04,0x0A,0x13,0x0E,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E, +0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x3C,0x30,0x3A,0x06,0x03,0x55,0x04,0x0B,0x13, +0x33,0x43,0x6C,0x61,0x73,0x73,0x20,0x33,0x20,0x50,0x75,0x62,0x6C,0x69,0x63,0x20, +0x50,0x72,0x69,0x6D,0x61,0x72,0x79,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63, +0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x20, +0x2D,0x20,0x47,0x32,0x31,0x3A,0x30,0x38,0x06,0x03,0x55,0x04,0x0B,0x13,0x31,0x28, +0x63,0x29,0x20,0x31,0x39,0x39,0x38,0x20,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E, +0x2C,0x20,0x49,0x6E,0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F,0x72,0x20,0x61,0x75,0x74, +0x68,0x6F,0x72,0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C,0x79, +0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x0B,0x13,0x16,0x56,0x65,0x72,0x69,0x53, +0x69,0x67,0x6E,0x20,0x54,0x72,0x75,0x73,0x74,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72, +0x6B,0x30,0x81,0x9F,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01, +0x01,0x05,0x00,0x03,0x81,0x8D,0x00,0x30,0x81,0x89,0x02,0x81,0x81,0x00,0xCC,0x5E, +0xD1,0x11,0x5D,0x5C,0x69,0xD0,0xAB,0xD3,0xB9,0x6A,0x4C,0x99,0x1F,0x59,0x98,0x30, +0x8E,0x16,0x85,0x20,0x46,0x6D,0x47,0x3F,0xD4,0x85,0x20,0x84,0xE1,0x6D,0xB3,0xF8, +0xA4,0xED,0x0C,0xF1,0x17,0x0F,0x3B,0xF9,0xA7,0xF9,0x25,0xD7,0xC1,0xCF,0x84,0x63, +0xF2,0x7C,0x63,0xCF,0xA2,0x47,0xF2,0xC6,0x5B,0x33,0x8E,0x64,0x40,0x04,0x68,0xC1, +0x80,0xB9,0x64,0x1C,0x45,0x77,0xC7,0xD8,0x6E,0xF5,0x95,0x29,0x3C,0x50,0xE8,0x34, +0xD7,0x78,0x1F,0xA8,0xBA,0x6D,0x43,0x91,0x95,0x8F,0x45,0x57,0x5E,0x7E,0xC5,0xFB, +0xCA,0xA4,0x04,0xEB,0xEA,0x97,0x37,0x54,0x30,0x6F,0xBB,0x01,0x47,0x32,0x33,0xCD, +0xDC,0x57,0x9B,0x64,0x69,0x61,0xF8,0x9B,0x1D,0x1C,0x89,0x4F,0x5C,0x67,0x02,0x03, +0x01,0x00,0x01,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05, +0x05,0x00,0x03,0x81,0x81,0x00,0x51,0x4D,0xCD,0xBE,0x5C,0xCB,0x98,0x19,0x9C,0x15, +0xB2,0x01,0x39,0x78,0x2E,0x4D,0x0F,0x67,0x70,0x70,0x99,0xC6,0x10,0x5A,0x94,0xA4, +0x53,0x4D,0x54,0x6D,0x2B,0xAF,0x0D,0x5D,0x40,0x8B,0x64,0xD3,0xD7,0xEE,0xDE,0x56, +0x61,0x92,0x5F,0xA6,0xC4,0x1D,0x10,0x61,0x36,0xD3,0x2C,0x27,0x3C,0xE8,0x29,0x09, +0xB9,0x11,0x64,0x74,0xCC,0xB5,0x73,0x9F,0x1C,0x48,0xA9,0xBC,0x61,0x01,0xEE,0xE2, +0x17,0xA6,0x0C,0xE3,0x40,0x08,0x3B,0x0E,0xE7,0xEB,0x44,0x73,0x2A,0x9A,0xF1,0x69, +0x92,0xEF,0x71,0x14,0xC3,0x39,0xAC,0x71,0xA7,0x91,0x09,0x6F,0xE4,0x71,0x06,0xB3, +0xBA,0x59,0x57,0x26,0x79,0x00,0xF6,0xF8,0x0D,0xA2,0x33,0x30,0x28,0xD4,0xAA,0x58, +0xA0,0x9D,0x9D,0x69,0x91,0xFD, +}; + + +/* subject:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 1999 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G3 */ +/* issuer :/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 1999 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G3 */ + + +const unsigned char Verisign_Class_3_Public_Primary_Certification_Authority___G3_certificate[1054]={ +0x30,0x82,0x04,0x1A,0x30,0x82,0x03,0x02,0x02,0x11,0x00,0x9B,0x7E,0x06,0x49,0xA3, +0x3E,0x62,0xB9,0xD5,0xEE,0x90,0x48,0x71,0x29,0xEF,0x57,0x30,0x0D,0x06,0x09,0x2A, +0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x81,0xCA,0x31,0x0B,0x30, +0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x17,0x30,0x15,0x06,0x03, +0x55,0x04,0x0A,0x13,0x0E,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x2C,0x20,0x49, +0x6E,0x63,0x2E,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x0B,0x13,0x16,0x56,0x65, +0x72,0x69,0x53,0x69,0x67,0x6E,0x20,0x54,0x72,0x75,0x73,0x74,0x20,0x4E,0x65,0x74, +0x77,0x6F,0x72,0x6B,0x31,0x3A,0x30,0x38,0x06,0x03,0x55,0x04,0x0B,0x13,0x31,0x28, +0x63,0x29,0x20,0x31,0x39,0x39,0x39,0x20,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E, +0x2C,0x20,0x49,0x6E,0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F,0x72,0x20,0x61,0x75,0x74, +0x68,0x6F,0x72,0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C,0x79, +0x31,0x45,0x30,0x43,0x06,0x03,0x55,0x04,0x03,0x13,0x3C,0x56,0x65,0x72,0x69,0x53, +0x69,0x67,0x6E,0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x33,0x20,0x50,0x75,0x62,0x6C, +0x69,0x63,0x20,0x50,0x72,0x69,0x6D,0x61,0x72,0x79,0x20,0x43,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69, +0x74,0x79,0x20,0x2D,0x20,0x47,0x33,0x30,0x1E,0x17,0x0D,0x39,0x39,0x31,0x30,0x30, +0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x36,0x30,0x37,0x31,0x36, +0x32,0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x81,0xCA,0x31,0x0B,0x30,0x09,0x06,0x03, +0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x0A, +0x13,0x0E,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x2C,0x20,0x49,0x6E,0x63,0x2E, +0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x0B,0x13,0x16,0x56,0x65,0x72,0x69,0x53, +0x69,0x67,0x6E,0x20,0x54,0x72,0x75,0x73,0x74,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72, +0x6B,0x31,0x3A,0x30,0x38,0x06,0x03,0x55,0x04,0x0B,0x13,0x31,0x28,0x63,0x29,0x20, +0x31,0x39,0x39,0x39,0x20,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x2C,0x20,0x49, +0x6E,0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F,0x72,0x20,0x61,0x75,0x74,0x68,0x6F,0x72, +0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C,0x79,0x31,0x45,0x30, +0x43,0x06,0x03,0x55,0x04,0x03,0x13,0x3C,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E, +0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x33,0x20,0x50,0x75,0x62,0x6C,0x69,0x63,0x20, +0x50,0x72,0x69,0x6D,0x61,0x72,0x79,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63, +0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x20, +0x2D,0x20,0x47,0x33,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A, +0x02,0x82,0x01,0x01,0x00,0xCB,0xBA,0x9C,0x52,0xFC,0x78,0x1F,0x1A,0x1E,0x6F,0x1B, +0x37,0x73,0xBD,0xF8,0xC9,0x6B,0x94,0x12,0x30,0x4F,0xF0,0x36,0x47,0xF5,0xD0,0x91, +0x0A,0xF5,0x17,0xC8,0xA5,0x61,0xC1,0x16,0x40,0x4D,0xFB,0x8A,0x61,0x90,0xE5,0x76, +0x20,0xC1,0x11,0x06,0x7D,0xAB,0x2C,0x6E,0xA6,0xF5,0x11,0x41,0x8E,0xFA,0x2D,0xAD, +0x2A,0x61,0x59,0xA4,0x67,0x26,0x4C,0xD0,0xE8,0xBC,0x52,0x5B,0x70,0x20,0x04,0x58, +0xD1,0x7A,0xC9,0xA4,0x69,0xBC,0x83,0x17,0x64,0xAD,0x05,0x8B,0xBC,0xD0,0x58,0xCE, +0x8D,0x8C,0xF5,0xEB,0xF0,0x42,0x49,0x0B,0x9D,0x97,0x27,0x67,0x32,0x6E,0xE1,0xAE, +0x93,0x15,0x1C,0x70,0xBC,0x20,0x4D,0x2F,0x18,0xDE,0x92,0x88,0xE8,0x6C,0x85,0x57, +0x11,0x1A,0xE9,0x7E,0xE3,0x26,0x11,0x54,0xA2,0x45,0x96,0x55,0x83,0xCA,0x30,0x89, +0xE8,0xDC,0xD8,0xA3,0xED,0x2A,0x80,0x3F,0x7F,0x79,0x65,0x57,0x3E,0x15,0x20,0x66, +0x08,0x2F,0x95,0x93,0xBF,0xAA,0x47,0x2F,0xA8,0x46,0x97,0xF0,0x12,0xE2,0xFE,0xC2, +0x0A,0x2B,0x51,0xE6,0x76,0xE6,0xB7,0x46,0xB7,0xE2,0x0D,0xA6,0xCC,0xA8,0xC3,0x4C, +0x59,0x55,0x89,0xE6,0xE8,0x53,0x5C,0x1C,0xEA,0x9D,0xF0,0x62,0x16,0x0B,0xA7,0xC9, +0x5F,0x0C,0xF0,0xDE,0xC2,0x76,0xCE,0xAF,0xF7,0x6A,0xF2,0xFA,0x41,0xA6,0xA2,0x33, +0x14,0xC9,0xE5,0x7A,0x63,0xD3,0x9E,0x62,0x37,0xD5,0x85,0x65,0x9E,0x0E,0xE6,0x53, +0x24,0x74,0x1B,0x5E,0x1D,0x12,0x53,0x5B,0xC7,0x2C,0xE7,0x83,0x49,0x3B,0x15,0xAE, +0x8A,0x68,0xB9,0x57,0x97,0x02,0x03,0x01,0x00,0x01,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x11,0x14, +0x96,0xC1,0xAB,0x92,0x08,0xF7,0x3F,0x2F,0xC9,0xB2,0xFE,0xE4,0x5A,0x9F,0x64,0xDE, +0xDB,0x21,0x4F,0x86,0x99,0x34,0x76,0x36,0x57,0xDD,0xD0,0x15,0x2F,0xC5,0xAD,0x7F, +0x15,0x1F,0x37,0x62,0x73,0x3E,0xD4,0xE7,0x5F,0xCE,0x17,0x03,0xDB,0x35,0xFA,0x2B, +0xDB,0xAE,0x60,0x09,0x5F,0x1E,0x5F,0x8F,0x6E,0xBB,0x0B,0x3D,0xEA,0x5A,0x13,0x1E, +0x0C,0x60,0x6F,0xB5,0xC0,0xB5,0x23,0x22,0x2E,0x07,0x0B,0xCB,0xA9,0x74,0xCB,0x47, +0xBB,0x1D,0xC1,0xD7,0xA5,0x6B,0xCC,0x2F,0xD2,0x42,0xFD,0x49,0xDD,0xA7,0x89,0xCF, +0x53,0xBA,0xDA,0x00,0x5A,0x28,0xBF,0x82,0xDF,0xF8,0xBA,0x13,0x1D,0x50,0x86,0x82, +0xFD,0x8E,0x30,0x8F,0x29,0x46,0xB0,0x1E,0x3D,0x35,0xDA,0x38,0x62,0x16,0x18,0x4A, +0xAD,0xE6,0xB6,0x51,0x6C,0xDE,0xAF,0x62,0xEB,0x01,0xD0,0x1E,0x24,0xFE,0x7A,0x8F, +0x12,0x1A,0x12,0x68,0xB8,0xFB,0x66,0x99,0x14,0x14,0x45,0x5C,0xAE,0xE7,0xAE,0x69, +0x17,0x81,0x2B,0x5A,0x37,0xC9,0x5E,0x2A,0xF4,0xC6,0xE2,0xA1,0x5C,0x54,0x9B,0xA6, +0x54,0x00,0xCF,0xF0,0xF1,0xC1,0xC7,0x98,0x30,0x1A,0x3B,0x36,0x16,0xDB,0xA3,0x6E, +0xEA,0xFD,0xAD,0xB2,0xC2,0xDA,0xEF,0x02,0x47,0x13,0x8A,0xC0,0xF1,0xB3,0x31,0xAD, +0x4F,0x1C,0xE1,0x4F,0x9C,0xAF,0x0F,0x0C,0x9D,0xF7,0x78,0x0D,0xD8,0xF4,0x35,0x56, +0x80,0xDA,0xB7,0x6D,0x17,0x8F,0x9D,0x1E,0x81,0x64,0xE1,0xFE,0xC5,0x45,0xBA,0xAD, +0x6B,0xB9,0x0A,0x7A,0x4E,0x4F,0x4B,0x84,0xEE,0x4B,0xF1,0x7D,0xDD,0x11, +}; + + +/* subject:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2007 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G4 */ +/* issuer :/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2007 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G4 */ + + +const unsigned char VeriSign_Class_3_Public_Primary_Certification_Authority___G4_certificate[904]={ +0x30,0x82,0x03,0x84,0x30,0x82,0x03,0x0A,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x2F, +0x80,0xFE,0x23,0x8C,0x0E,0x22,0x0F,0x48,0x67,0x12,0x28,0x91,0x87,0xAC,0xB3,0x30, +0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x30,0x81,0xCA,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x17,0x30,0x15,0x06, +0x03,0x55,0x04,0x0A,0x13,0x0E,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x2C,0x20, +0x49,0x6E,0x63,0x2E,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x0B,0x13,0x16,0x56, +0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x20,0x54,0x72,0x75,0x73,0x74,0x20,0x4E,0x65, +0x74,0x77,0x6F,0x72,0x6B,0x31,0x3A,0x30,0x38,0x06,0x03,0x55,0x04,0x0B,0x13,0x31, +0x28,0x63,0x29,0x20,0x32,0x30,0x30,0x37,0x20,0x56,0x65,0x72,0x69,0x53,0x69,0x67, +0x6E,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F,0x72,0x20,0x61,0x75, +0x74,0x68,0x6F,0x72,0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C, +0x79,0x31,0x45,0x30,0x43,0x06,0x03,0x55,0x04,0x03,0x13,0x3C,0x56,0x65,0x72,0x69, +0x53,0x69,0x67,0x6E,0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x33,0x20,0x50,0x75,0x62, +0x6C,0x69,0x63,0x20,0x50,0x72,0x69,0x6D,0x61,0x72,0x79,0x20,0x43,0x65,0x72,0x74, +0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72, +0x69,0x74,0x79,0x20,0x2D,0x20,0x47,0x34,0x30,0x1E,0x17,0x0D,0x30,0x37,0x31,0x31, +0x30,0x35,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x38,0x30,0x31,0x31, +0x38,0x32,0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x81,0xCA,0x31,0x0B,0x30,0x09,0x06, +0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04, +0x0A,0x13,0x0E,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x2C,0x20,0x49,0x6E,0x63, +0x2E,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x0B,0x13,0x16,0x56,0x65,0x72,0x69, +0x53,0x69,0x67,0x6E,0x20,0x54,0x72,0x75,0x73,0x74,0x20,0x4E,0x65,0x74,0x77,0x6F, +0x72,0x6B,0x31,0x3A,0x30,0x38,0x06,0x03,0x55,0x04,0x0B,0x13,0x31,0x28,0x63,0x29, +0x20,0x32,0x30,0x30,0x37,0x20,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x2C,0x20, +0x49,0x6E,0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F,0x72,0x20,0x61,0x75,0x74,0x68,0x6F, +0x72,0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C,0x79,0x31,0x45, +0x30,0x43,0x06,0x03,0x55,0x04,0x03,0x13,0x3C,0x56,0x65,0x72,0x69,0x53,0x69,0x67, +0x6E,0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x33,0x20,0x50,0x75,0x62,0x6C,0x69,0x63, +0x20,0x50,0x72,0x69,0x6D,0x61,0x72,0x79,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69, +0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79, +0x20,0x2D,0x20,0x47,0x34,0x30,0x76,0x30,0x10,0x06,0x07,0x2A,0x86,0x48,0xCE,0x3D, +0x02,0x01,0x06,0x05,0x2B,0x81,0x04,0x00,0x22,0x03,0x62,0x00,0x04,0xA7,0x56,0x7A, +0x7C,0x52,0xDA,0x64,0x9B,0x0E,0x2D,0x5C,0xD8,0x5E,0xAC,0x92,0x3D,0xFE,0x01,0xE6, +0x19,0x4A,0x3D,0x14,0x03,0x4B,0xFA,0x60,0x27,0x20,0xD9,0x83,0x89,0x69,0xFA,0x54, +0xC6,0x9A,0x18,0x5E,0x55,0x2A,0x64,0xDE,0x06,0xF6,0x8D,0x4A,0x3B,0xAD,0x10,0x3C, +0x65,0x3D,0x90,0x88,0x04,0x89,0xE0,0x30,0x61,0xB3,0xAE,0x5D,0x01,0xA7,0x7B,0xDE, +0x7C,0xB2,0xBE,0xCA,0x65,0x61,0x00,0x86,0xAE,0xDA,0x8F,0x7B,0xD0,0x89,0xAD,0x4D, +0x1D,0x59,0x9A,0x41,0xB1,0xBC,0x47,0x80,0xDC,0x9E,0x62,0xC3,0xF9,0xA3,0x81,0xB2, +0x30,0x81,0xAF,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30, +0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04, +0x03,0x02,0x01,0x06,0x30,0x6D,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x0C, +0x04,0x61,0x30,0x5F,0xA1,0x5D,0xA0,0x5B,0x30,0x59,0x30,0x57,0x30,0x55,0x16,0x09, +0x69,0x6D,0x61,0x67,0x65,0x2F,0x67,0x69,0x66,0x30,0x21,0x30,0x1F,0x30,0x07,0x06, +0x05,0x2B,0x0E,0x03,0x02,0x1A,0x04,0x14,0x8F,0xE5,0xD3,0x1A,0x86,0xAC,0x8D,0x8E, +0x6B,0xC3,0xCF,0x80,0x6A,0xD4,0x48,0x18,0x2C,0x7B,0x19,0x2E,0x30,0x25,0x16,0x23, +0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x6C,0x6F,0x67,0x6F,0x2E,0x76,0x65,0x72,0x69, +0x73,0x69,0x67,0x6E,0x2E,0x63,0x6F,0x6D,0x2F,0x76,0x73,0x6C,0x6F,0x67,0x6F,0x2E, +0x67,0x69,0x66,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xB3,0x16, +0x91,0xFD,0xEE,0xA6,0x6E,0xE4,0xB5,0x2E,0x49,0x8F,0x87,0x78,0x81,0x80,0xEC,0xE5, +0xB1,0xB5,0x30,0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x03,0x68, +0x00,0x30,0x65,0x02,0x30,0x66,0x21,0x0C,0x18,0x26,0x60,0x5A,0x38,0x7B,0x56,0x42, +0xE0,0xA7,0xFC,0x36,0x84,0x51,0x91,0x20,0x2C,0x76,0x4D,0x43,0x3D,0xC4,0x1D,0x84, +0x23,0xD0,0xAC,0xD6,0x7C,0x35,0x06,0xCE,0xCD,0x69,0xBD,0x90,0x0D,0xDB,0x6C,0x48, +0x42,0x1D,0x0E,0xAA,0x42,0x02,0x31,0x00,0x9C,0x3D,0x48,0x39,0x23,0x39,0x58,0x1A, +0x15,0x12,0x59,0x6A,0x9E,0xEF,0xD5,0x59,0xB2,0x1D,0x52,0x2C,0x99,0x71,0xCD,0xC7, +0x29,0xDF,0x1B,0x2A,0x61,0x7B,0x71,0xD1,0xDE,0xF3,0xC0,0xE5,0x0D,0x3A,0x4A,0xAA, +0x2D,0xA7,0xD8,0x86,0x2A,0xDD,0x2E,0x10, +}; + + +/* subject:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5 */ +/* issuer :/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5 */ + + +const unsigned char VeriSign_Class_3_Public_Primary_Certification_Authority___G5_certificate[1239]={ +0x30,0x82,0x04,0xD3,0x30,0x82,0x03,0xBB,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x18, +0xDA,0xD1,0x9E,0x26,0x7D,0xE8,0xBB,0x4A,0x21,0x58,0xCD,0xCC,0x6B,0x3B,0x4A,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x81, +0xCA,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x17, +0x30,0x15,0x06,0x03,0x55,0x04,0x0A,0x13,0x0E,0x56,0x65,0x72,0x69,0x53,0x69,0x67, +0x6E,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x0B, +0x13,0x16,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x20,0x54,0x72,0x75,0x73,0x74, +0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x31,0x3A,0x30,0x38,0x06,0x03,0x55,0x04, +0x0B,0x13,0x31,0x28,0x63,0x29,0x20,0x32,0x30,0x30,0x36,0x20,0x56,0x65,0x72,0x69, +0x53,0x69,0x67,0x6E,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F,0x72, +0x20,0x61,0x75,0x74,0x68,0x6F,0x72,0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20, +0x6F,0x6E,0x6C,0x79,0x31,0x45,0x30,0x43,0x06,0x03,0x55,0x04,0x03,0x13,0x3C,0x56, +0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x33,0x20, +0x50,0x75,0x62,0x6C,0x69,0x63,0x20,0x50,0x72,0x69,0x6D,0x61,0x72,0x79,0x20,0x43, +0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74, +0x68,0x6F,0x72,0x69,0x74,0x79,0x20,0x2D,0x20,0x47,0x35,0x30,0x1E,0x17,0x0D,0x30, +0x36,0x31,0x31,0x30,0x38,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x36, +0x30,0x37,0x31,0x36,0x32,0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x81,0xCA,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x17,0x30,0x15,0x06, +0x03,0x55,0x04,0x0A,0x13,0x0E,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x2C,0x20, +0x49,0x6E,0x63,0x2E,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x0B,0x13,0x16,0x56, +0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x20,0x54,0x72,0x75,0x73,0x74,0x20,0x4E,0x65, +0x74,0x77,0x6F,0x72,0x6B,0x31,0x3A,0x30,0x38,0x06,0x03,0x55,0x04,0x0B,0x13,0x31, +0x28,0x63,0x29,0x20,0x32,0x30,0x30,0x36,0x20,0x56,0x65,0x72,0x69,0x53,0x69,0x67, +0x6E,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F,0x72,0x20,0x61,0x75, +0x74,0x68,0x6F,0x72,0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C, +0x79,0x31,0x45,0x30,0x43,0x06,0x03,0x55,0x04,0x03,0x13,0x3C,0x56,0x65,0x72,0x69, +0x53,0x69,0x67,0x6E,0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x33,0x20,0x50,0x75,0x62, +0x6C,0x69,0x63,0x20,0x50,0x72,0x69,0x6D,0x61,0x72,0x79,0x20,0x43,0x65,0x72,0x74, +0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72, +0x69,0x74,0x79,0x20,0x2D,0x20,0x47,0x35,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09, +0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00, +0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xAF,0x24,0x08,0x08,0x29,0x7A,0x35, +0x9E,0x60,0x0C,0xAA,0xE7,0x4B,0x3B,0x4E,0xDC,0x7C,0xBC,0x3C,0x45,0x1C,0xBB,0x2B, +0xE0,0xFE,0x29,0x02,0xF9,0x57,0x08,0xA3,0x64,0x85,0x15,0x27,0xF5,0xF1,0xAD,0xC8, +0x31,0x89,0x5D,0x22,0xE8,0x2A,0xAA,0xA6,0x42,0xB3,0x8F,0xF8,0xB9,0x55,0xB7,0xB1, +0xB7,0x4B,0xB3,0xFE,0x8F,0x7E,0x07,0x57,0xEC,0xEF,0x43,0xDB,0x66,0x62,0x15,0x61, +0xCF,0x60,0x0D,0xA4,0xD8,0xDE,0xF8,0xE0,0xC3,0x62,0x08,0x3D,0x54,0x13,0xEB,0x49, +0xCA,0x59,0x54,0x85,0x26,0xE5,0x2B,0x8F,0x1B,0x9F,0xEB,0xF5,0xA1,0x91,0xC2,0x33, +0x49,0xD8,0x43,0x63,0x6A,0x52,0x4B,0xD2,0x8F,0xE8,0x70,0x51,0x4D,0xD1,0x89,0x69, +0x7B,0xC7,0x70,0xF6,0xB3,0xDC,0x12,0x74,0xDB,0x7B,0x5D,0x4B,0x56,0xD3,0x96,0xBF, +0x15,0x77,0xA1,0xB0,0xF4,0xA2,0x25,0xF2,0xAF,0x1C,0x92,0x67,0x18,0xE5,0xF4,0x06, +0x04,0xEF,0x90,0xB9,0xE4,0x00,0xE4,0xDD,0x3A,0xB5,0x19,0xFF,0x02,0xBA,0xF4,0x3C, +0xEE,0xE0,0x8B,0xEB,0x37,0x8B,0xEC,0xF4,0xD7,0xAC,0xF2,0xF6,0xF0,0x3D,0xAF,0xDD, +0x75,0x91,0x33,0x19,0x1D,0x1C,0x40,0xCB,0x74,0x24,0x19,0x21,0x93,0xD9,0x14,0xFE, +0xAC,0x2A,0x52,0xC7,0x8F,0xD5,0x04,0x49,0xE4,0x8D,0x63,0x47,0x88,0x3C,0x69,0x83, +0xCB,0xFE,0x47,0xBD,0x2B,0x7E,0x4F,0xC5,0x95,0xAE,0x0E,0x9D,0xD4,0xD1,0x43,0xC0, +0x67,0x73,0xE3,0x14,0x08,0x7E,0xE5,0x3F,0x9F,0x73,0xB8,0x33,0x0A,0xCF,0x5D,0x3F, +0x34,0x87,0x96,0x8A,0xEE,0x53,0xE8,0x25,0x15,0x02,0x03,0x01,0x00,0x01,0xA3,0x81, +0xB2,0x30,0x81,0xAF,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05, +0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04, +0x04,0x03,0x02,0x01,0x06,0x30,0x6D,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x01, +0x0C,0x04,0x61,0x30,0x5F,0xA1,0x5D,0xA0,0x5B,0x30,0x59,0x30,0x57,0x30,0x55,0x16, +0x09,0x69,0x6D,0x61,0x67,0x65,0x2F,0x67,0x69,0x66,0x30,0x21,0x30,0x1F,0x30,0x07, +0x06,0x05,0x2B,0x0E,0x03,0x02,0x1A,0x04,0x14,0x8F,0xE5,0xD3,0x1A,0x86,0xAC,0x8D, +0x8E,0x6B,0xC3,0xCF,0x80,0x6A,0xD4,0x48,0x18,0x2C,0x7B,0x19,0x2E,0x30,0x25,0x16, +0x23,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x6C,0x6F,0x67,0x6F,0x2E,0x76,0x65,0x72, +0x69,0x73,0x69,0x67,0x6E,0x2E,0x63,0x6F,0x6D,0x2F,0x76,0x73,0x6C,0x6F,0x67,0x6F, +0x2E,0x67,0x69,0x66,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x7F, +0xD3,0x65,0xA7,0xC2,0xDD,0xEC,0xBB,0xF0,0x30,0x09,0xF3,0x43,0x39,0xFA,0x02,0xAF, +0x33,0x31,0x33,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05, +0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x93,0x24,0x4A,0x30,0x5F,0x62,0xCF,0xD8,0x1A, +0x98,0x2F,0x3D,0xEA,0xDC,0x99,0x2D,0xBD,0x77,0xF6,0xA5,0x79,0x22,0x38,0xEC,0xC4, +0xA7,0xA0,0x78,0x12,0xAD,0x62,0x0E,0x45,0x70,0x64,0xC5,0xE7,0x97,0x66,0x2D,0x98, +0x09,0x7E,0x5F,0xAF,0xD6,0xCC,0x28,0x65,0xF2,0x01,0xAA,0x08,0x1A,0x47,0xDE,0xF9, +0xF9,0x7C,0x92,0x5A,0x08,0x69,0x20,0x0D,0xD9,0x3E,0x6D,0x6E,0x3C,0x0D,0x6E,0xD8, +0xE6,0x06,0x91,0x40,0x18,0xB9,0xF8,0xC1,0xED,0xDF,0xDB,0x41,0xAA,0xE0,0x96,0x20, +0xC9,0xCD,0x64,0x15,0x38,0x81,0xC9,0x94,0xEE,0xA2,0x84,0x29,0x0B,0x13,0x6F,0x8E, +0xDB,0x0C,0xDD,0x25,0x02,0xDB,0xA4,0x8B,0x19,0x44,0xD2,0x41,0x7A,0x05,0x69,0x4A, +0x58,0x4F,0x60,0xCA,0x7E,0x82,0x6A,0x0B,0x02,0xAA,0x25,0x17,0x39,0xB5,0xDB,0x7F, +0xE7,0x84,0x65,0x2A,0x95,0x8A,0xBD,0x86,0xDE,0x5E,0x81,0x16,0x83,0x2D,0x10,0xCC, +0xDE,0xFD,0xA8,0x82,0x2A,0x6D,0x28,0x1F,0x0D,0x0B,0xC4,0xE5,0xE7,0x1A,0x26,0x19, +0xE1,0xF4,0x11,0x6F,0x10,0xB5,0x95,0xFC,0xE7,0x42,0x05,0x32,0xDB,0xCE,0x9D,0x51, +0x5E,0x28,0xB6,0x9E,0x85,0xD3,0x5B,0xEF,0xA5,0x7D,0x45,0x40,0x72,0x8E,0xB7,0x0E, +0x6B,0x0E,0x06,0xFB,0x33,0x35,0x48,0x71,0xB8,0x9D,0x27,0x8B,0xC4,0x65,0x5F,0x0D, +0x86,0x76,0x9C,0x44,0x7A,0xF6,0x95,0x5C,0xF6,0x5D,0x32,0x08,0x33,0xA4,0x54,0xB6, +0x18,0x3F,0x68,0x5C,0xF2,0x42,0x4A,0x85,0x38,0x54,0x83,0x5F,0xD1,0xE8,0x2C,0xF2, +0xAC,0x11,0xD6,0xA8,0xED,0x63,0x6A, +}; + + +/* subject:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 1999 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 4 Public Primary Certification Authority - G3 */ +/* issuer :/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 1999 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 4 Public Primary Certification Authority - G3 */ + + +const unsigned char Verisign_Class_4_Public_Primary_Certification_Authority___G3_certificate[1054]={ +0x30,0x82,0x04,0x1A,0x30,0x82,0x03,0x02,0x02,0x11,0x00,0xEC,0xA0,0xA7,0x8B,0x6E, +0x75,0x6A,0x01,0xCF,0xC4,0x7C,0xCC,0x2F,0x94,0x5E,0xD7,0x30,0x0D,0x06,0x09,0x2A, +0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x81,0xCA,0x31,0x0B,0x30, +0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x17,0x30,0x15,0x06,0x03, +0x55,0x04,0x0A,0x13,0x0E,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x2C,0x20,0x49, +0x6E,0x63,0x2E,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x0B,0x13,0x16,0x56,0x65, +0x72,0x69,0x53,0x69,0x67,0x6E,0x20,0x54,0x72,0x75,0x73,0x74,0x20,0x4E,0x65,0x74, +0x77,0x6F,0x72,0x6B,0x31,0x3A,0x30,0x38,0x06,0x03,0x55,0x04,0x0B,0x13,0x31,0x28, +0x63,0x29,0x20,0x31,0x39,0x39,0x39,0x20,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E, +0x2C,0x20,0x49,0x6E,0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F,0x72,0x20,0x61,0x75,0x74, +0x68,0x6F,0x72,0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C,0x79, +0x31,0x45,0x30,0x43,0x06,0x03,0x55,0x04,0x03,0x13,0x3C,0x56,0x65,0x72,0x69,0x53, +0x69,0x67,0x6E,0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x34,0x20,0x50,0x75,0x62,0x6C, +0x69,0x63,0x20,0x50,0x72,0x69,0x6D,0x61,0x72,0x79,0x20,0x43,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69, +0x74,0x79,0x20,0x2D,0x20,0x47,0x33,0x30,0x1E,0x17,0x0D,0x39,0x39,0x31,0x30,0x30, +0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x36,0x30,0x37,0x31,0x36, +0x32,0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x81,0xCA,0x31,0x0B,0x30,0x09,0x06,0x03, +0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x0A, +0x13,0x0E,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x2C,0x20,0x49,0x6E,0x63,0x2E, +0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x0B,0x13,0x16,0x56,0x65,0x72,0x69,0x53, +0x69,0x67,0x6E,0x20,0x54,0x72,0x75,0x73,0x74,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72, +0x6B,0x31,0x3A,0x30,0x38,0x06,0x03,0x55,0x04,0x0B,0x13,0x31,0x28,0x63,0x29,0x20, +0x31,0x39,0x39,0x39,0x20,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x2C,0x20,0x49, +0x6E,0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F,0x72,0x20,0x61,0x75,0x74,0x68,0x6F,0x72, +0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C,0x79,0x31,0x45,0x30, +0x43,0x06,0x03,0x55,0x04,0x03,0x13,0x3C,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E, +0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x34,0x20,0x50,0x75,0x62,0x6C,0x69,0x63,0x20, +0x50,0x72,0x69,0x6D,0x61,0x72,0x79,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63, +0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x20, +0x2D,0x20,0x47,0x33,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A, +0x02,0x82,0x01,0x01,0x00,0xAD,0xCB,0xA5,0x11,0x69,0xC6,0x59,0xAB,0xF1,0x8F,0xB5, +0x19,0x0F,0x56,0xCE,0xCC,0xB5,0x1F,0x20,0xE4,0x9E,0x26,0x25,0x4B,0xE0,0x73,0x65, +0x89,0x59,0xDE,0xD0,0x83,0xE4,0xF5,0x0F,0xB5,0xBB,0xAD,0xF1,0x7C,0xE8,0x21,0xFC, +0xE4,0xE8,0x0C,0xEE,0x7C,0x45,0x22,0x19,0x76,0x92,0xB4,0x13,0xB7,0x20,0x5B,0x09, +0xFA,0x61,0xAE,0xA8,0xF2,0xA5,0x8D,0x85,0xC2,0x2A,0xD6,0xDE,0x66,0x36,0xD2,0x9B, +0x02,0xF4,0xA8,0x92,0x60,0x7C,0x9C,0x69,0xB4,0x8F,0x24,0x1E,0xD0,0x86,0x52,0xF6, +0x32,0x9C,0x41,0x58,0x1E,0x22,0xBD,0xCD,0x45,0x62,0x95,0x08,0x6E,0xD0,0x66,0xDD, +0x53,0xA2,0xCC,0xF0,0x10,0xDC,0x54,0x73,0x8B,0x04,0xA1,0x46,0x33,0x33,0x5C,0x17, +0x40,0xB9,0x9E,0x4D,0xD3,0xF3,0xBE,0x55,0x83,0xE8,0xB1,0x89,0x8E,0x5A,0x7C,0x9A, +0x96,0x22,0x90,0x3B,0x88,0x25,0xF2,0xD2,0x53,0x88,0x02,0x0C,0x0B,0x78,0xF2,0xE6, +0x37,0x17,0x4B,0x30,0x46,0x07,0xE4,0x80,0x6D,0xA6,0xD8,0x96,0x2E,0xE8,0x2C,0xF8, +0x11,0xB3,0x38,0x0D,0x66,0xA6,0x9B,0xEA,0xC9,0x23,0x5B,0xDB,0x8E,0xE2,0xF3,0x13, +0x8E,0x1A,0x59,0x2D,0xAA,0x02,0xF0,0xEC,0xA4,0x87,0x66,0xDC,0xC1,0x3F,0xF5,0xD8, +0xB9,0xF4,0xEC,0x82,0xC6,0xD2,0x3D,0x95,0x1D,0xE5,0xC0,0x4F,0x84,0xC9,0xD9,0xA3, +0x44,0x28,0x06,0x6A,0xD7,0x45,0xAC,0xF0,0x6B,0x6A,0xEF,0x4E,0x5F,0xF8,0x11,0x82, +0x1E,0x38,0x63,0x34,0x66,0x50,0xD4,0x3E,0x93,0x73,0xFA,0x30,0xC3,0x66,0xAD,0xFF, +0x93,0x2D,0x97,0xEF,0x03,0x02,0x03,0x01,0x00,0x01,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x8F,0xFA, +0x25,0x6B,0x4F,0x5B,0xE4,0xA4,0x4E,0x27,0x55,0xAB,0x22,0x15,0x59,0x3C,0xCA,0xB5, +0x0A,0xD4,0x4A,0xDB,0xAB,0xDD,0xA1,0x5F,0x53,0xC5,0xA0,0x57,0x39,0xC2,0xCE,0x47, +0x2B,0xBE,0x3A,0xC8,0x56,0xBF,0xC2,0xD9,0x27,0x10,0x3A,0xB1,0x05,0x3C,0xC0,0x77, +0x31,0xBB,0x3A,0xD3,0x05,0x7B,0x6D,0x9A,0x1C,0x30,0x8C,0x80,0xCB,0x93,0x93,0x2A, +0x83,0xAB,0x05,0x51,0x82,0x02,0x00,0x11,0x67,0x6B,0xF3,0x88,0x61,0x47,0x5F,0x03, +0x93,0xD5,0x5B,0x0D,0xE0,0xF1,0xD4,0xA1,0x32,0x35,0x85,0xB2,0x3A,0xDB,0xB0,0x82, +0xAB,0xD1,0xCB,0x0A,0xBC,0x4F,0x8C,0x5B,0xC5,0x4B,0x00,0x3B,0x1F,0x2A,0x82,0xA6, +0x7E,0x36,0x85,0xDC,0x7E,0x3C,0x67,0x00,0xB5,0xE4,0x3B,0x52,0xE0,0xA8,0xEB,0x5D, +0x15,0xF9,0xC6,0x6D,0xF0,0xAD,0x1D,0x0E,0x85,0xB7,0xA9,0x9A,0x73,0x14,0x5A,0x5B, +0x8F,0x41,0x28,0xC0,0xD5,0xE8,0x2D,0x4D,0xA4,0x5E,0xCD,0xAA,0xD9,0xED,0xCE,0xDC, +0xD8,0xD5,0x3C,0x42,0x1D,0x17,0xC1,0x12,0x5D,0x45,0x38,0xC3,0x38,0xF3,0xFC,0x85, +0x2E,0x83,0x46,0x48,0xB2,0xD7,0x20,0x5F,0x92,0x36,0x8F,0xE7,0x79,0x0F,0x98,0x5E, +0x99,0xE8,0xF0,0xD0,0xA4,0xBB,0xF5,0x53,0xBD,0x2A,0xCE,0x59,0xB0,0xAF,0x6E,0x7F, +0x6C,0xBB,0xD2,0x1E,0x00,0xB0,0x21,0xED,0xF8,0x41,0x62,0x82,0xB9,0xD8,0xB2,0xC4, +0xBB,0x46,0x50,0xF3,0x31,0xC5,0x8F,0x01,0xA8,0x74,0xEB,0xF5,0x78,0x27,0xDA,0xE7, +0xF7,0x66,0x43,0xF3,0x9E,0x83,0x3E,0x20,0xAA,0xC3,0x35,0x60,0x91,0xCE, +}; + + +/* subject:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2008 VeriSign, Inc. - For authorized use only/CN=VeriSign Universal Root Certification Authority */ +/* issuer :/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2008 VeriSign, Inc. - For authorized use only/CN=VeriSign Universal Root Certification Authority */ + + +const unsigned char VeriSign_Universal_Root_Certification_Authority_certificate[1213]={ +0x30,0x82,0x04,0xB9,0x30,0x82,0x03,0xA1,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x40, +0x1A,0xC4,0x64,0x21,0xB3,0x13,0x21,0x03,0x0E,0xBB,0xE4,0x12,0x1A,0xC5,0x1D,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,0x81, +0xBD,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x17, +0x30,0x15,0x06,0x03,0x55,0x04,0x0A,0x13,0x0E,0x56,0x65,0x72,0x69,0x53,0x69,0x67, +0x6E,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x0B, +0x13,0x16,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x20,0x54,0x72,0x75,0x73,0x74, +0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x31,0x3A,0x30,0x38,0x06,0x03,0x55,0x04, +0x0B,0x13,0x31,0x28,0x63,0x29,0x20,0x32,0x30,0x30,0x38,0x20,0x56,0x65,0x72,0x69, +0x53,0x69,0x67,0x6E,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F,0x72, +0x20,0x61,0x75,0x74,0x68,0x6F,0x72,0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20, +0x6F,0x6E,0x6C,0x79,0x31,0x38,0x30,0x36,0x06,0x03,0x55,0x04,0x03,0x13,0x2F,0x56, +0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x20,0x55,0x6E,0x69,0x76,0x65,0x72,0x73,0x61, +0x6C,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61, +0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x1E, +0x17,0x0D,0x30,0x38,0x30,0x34,0x30,0x32,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17, +0x0D,0x33,0x37,0x31,0x32,0x30,0x31,0x32,0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x81, +0xBD,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x17, +0x30,0x15,0x06,0x03,0x55,0x04,0x0A,0x13,0x0E,0x56,0x65,0x72,0x69,0x53,0x69,0x67, +0x6E,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x0B, +0x13,0x16,0x56,0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x20,0x54,0x72,0x75,0x73,0x74, +0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x31,0x3A,0x30,0x38,0x06,0x03,0x55,0x04, +0x0B,0x13,0x31,0x28,0x63,0x29,0x20,0x32,0x30,0x30,0x38,0x20,0x56,0x65,0x72,0x69, +0x53,0x69,0x67,0x6E,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x20,0x2D,0x20,0x46,0x6F,0x72, +0x20,0x61,0x75,0x74,0x68,0x6F,0x72,0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20, +0x6F,0x6E,0x6C,0x79,0x31,0x38,0x30,0x36,0x06,0x03,0x55,0x04,0x03,0x13,0x2F,0x56, +0x65,0x72,0x69,0x53,0x69,0x67,0x6E,0x20,0x55,0x6E,0x69,0x76,0x65,0x72,0x73,0x61, +0x6C,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61, +0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x82, +0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05, +0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xC7, +0x61,0x37,0x5E,0xB1,0x01,0x34,0xDB,0x62,0xD7,0x15,0x9B,0xFF,0x58,0x5A,0x8C,0x23, +0x23,0xD6,0x60,0x8E,0x91,0xD7,0x90,0x98,0x83,0x7A,0xE6,0x58,0x19,0x38,0x8C,0xC5, +0xF6,0xE5,0x64,0x85,0xB4,0xA2,0x71,0xFB,0xED,0xBD,0xB9,0xDA,0xCD,0x4D,0x00,0xB4, +0xC8,0x2D,0x73,0xA5,0xC7,0x69,0x71,0x95,0x1F,0x39,0x3C,0xB2,0x44,0x07,0x9C,0xE8, +0x0E,0xFA,0x4D,0x4A,0xC4,0x21,0xDF,0x29,0x61,0x8F,0x32,0x22,0x61,0x82,0xC5,0x87, +0x1F,0x6E,0x8C,0x7C,0x5F,0x16,0x20,0x51,0x44,0xD1,0x70,0x4F,0x57,0xEA,0xE3,0x1C, +0xE3,0xCC,0x79,0xEE,0x58,0xD8,0x0E,0xC2,0xB3,0x45,0x93,0xC0,0x2C,0xE7,0x9A,0x17, +0x2B,0x7B,0x00,0x37,0x7A,0x41,0x33,0x78,0xE1,0x33,0xE2,0xF3,0x10,0x1A,0x7F,0x87, +0x2C,0xBE,0xF6,0xF5,0xF7,0x42,0xE2,0xE5,0xBF,0x87,0x62,0x89,0x5F,0x00,0x4B,0xDF, +0xC5,0xDD,0xE4,0x75,0x44,0x32,0x41,0x3A,0x1E,0x71,0x6E,0x69,0xCB,0x0B,0x75,0x46, +0x08,0xD1,0xCA,0xD2,0x2B,0x95,0xD0,0xCF,0xFB,0xB9,0x40,0x6B,0x64,0x8C,0x57,0x4D, +0xFC,0x13,0x11,0x79,0x84,0xED,0x5E,0x54,0xF6,0x34,0x9F,0x08,0x01,0xF3,0x10,0x25, +0x06,0x17,0x4A,0xDA,0xF1,0x1D,0x7A,0x66,0x6B,0x98,0x60,0x66,0xA4,0xD9,0xEF,0xD2, +0x2E,0x82,0xF1,0xF0,0xEF,0x09,0xEA,0x44,0xC9,0x15,0x6A,0xE2,0x03,0x6E,0x33,0xD3, +0xAC,0x9F,0x55,0x00,0xC7,0xF6,0x08,0x6A,0x94,0xB9,0x5F,0xDC,0xE0,0x33,0xF1,0x84, +0x60,0xF9,0x5B,0x27,0x11,0xB4,0xFC,0x16,0xF2,0xBB,0x56,0x6A,0x80,0x25,0x8D,0x02, +0x03,0x01,0x00,0x01,0xA3,0x81,0xB2,0x30,0x81,0xAF,0x30,0x0F,0x06,0x03,0x55,0x1D, +0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03,0x55, +0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x6D,0x06,0x08,0x2B, +0x06,0x01,0x05,0x05,0x07,0x01,0x0C,0x04,0x61,0x30,0x5F,0xA1,0x5D,0xA0,0x5B,0x30, +0x59,0x30,0x57,0x30,0x55,0x16,0x09,0x69,0x6D,0x61,0x67,0x65,0x2F,0x67,0x69,0x66, +0x30,0x21,0x30,0x1F,0x30,0x07,0x06,0x05,0x2B,0x0E,0x03,0x02,0x1A,0x04,0x14,0x8F, +0xE5,0xD3,0x1A,0x86,0xAC,0x8D,0x8E,0x6B,0xC3,0xCF,0x80,0x6A,0xD4,0x48,0x18,0x2C, +0x7B,0x19,0x2E,0x30,0x25,0x16,0x23,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x6C,0x6F, +0x67,0x6F,0x2E,0x76,0x65,0x72,0x69,0x73,0x69,0x67,0x6E,0x2E,0x63,0x6F,0x6D,0x2F, +0x76,0x73,0x6C,0x6F,0x67,0x6F,0x2E,0x67,0x69,0x66,0x30,0x1D,0x06,0x03,0x55,0x1D, +0x0E,0x04,0x16,0x04,0x14,0xB6,0x77,0xFA,0x69,0x48,0x47,0x9F,0x53,0x12,0xD5,0xC2, +0xEA,0x07,0x32,0x76,0x07,0xD1,0x97,0x07,0x19,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48, +0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x4A,0xF8,0xF8, +0xB0,0x03,0xE6,0x2C,0x67,0x7B,0xE4,0x94,0x77,0x63,0xCC,0x6E,0x4C,0xF9,0x7D,0x0E, +0x0D,0xDC,0xC8,0xB9,0x35,0xB9,0x70,0x4F,0x63,0xFA,0x24,0xFA,0x6C,0x83,0x8C,0x47, +0x9D,0x3B,0x63,0xF3,0x9A,0xF9,0x76,0x32,0x95,0x91,0xB1,0x77,0xBC,0xAC,0x9A,0xBE, +0xB1,0xE4,0x31,0x21,0xC6,0x81,0x95,0x56,0x5A,0x0E,0xB1,0xC2,0xD4,0xB1,0xA6,0x59, +0xAC,0xF1,0x63,0xCB,0xB8,0x4C,0x1D,0x59,0x90,0x4A,0xEF,0x90,0x16,0x28,0x1F,0x5A, +0xAE,0x10,0xFB,0x81,0x50,0x38,0x0C,0x6C,0xCC,0xF1,0x3D,0xC3,0xF5,0x63,0xE3,0xB3, +0xE3,0x21,0xC9,0x24,0x39,0xE9,0xFD,0x15,0x66,0x46,0xF4,0x1B,0x11,0xD0,0x4D,0x73, +0xA3,0x7D,0x46,0xF9,0x3D,0xED,0xA8,0x5F,0x62,0xD4,0xF1,0x3F,0xF8,0xE0,0x74,0x57, +0x2B,0x18,0x9D,0x81,0xB4,0xC4,0x28,0xDA,0x94,0x97,0xA5,0x70,0xEB,0xAC,0x1D,0xBE, +0x07,0x11,0xF0,0xD5,0xDB,0xDD,0xE5,0x8C,0xF0,0xD5,0x32,0xB0,0x83,0xE6,0x57,0xE2, +0x8F,0xBF,0xBE,0xA1,0xAA,0xBF,0x3D,0x1D,0xB5,0xD4,0x38,0xEA,0xD7,0xB0,0x5C,0x3A, +0x4F,0x6A,0x3F,0x8F,0xC0,0x66,0x6C,0x63,0xAA,0xE9,0xD9,0xA4,0x16,0xF4,0x81,0xD1, +0x95,0x14,0x0E,0x7D,0xCD,0x95,0x34,0xD9,0xD2,0x8F,0x70,0x73,0x81,0x7B,0x9C,0x7E, +0xBD,0x98,0x61,0xD8,0x45,0x87,0x98,0x90,0xC5,0xEB,0x86,0x30,0xC6,0x35,0xBF,0xF0, +0xFF,0xC3,0x55,0x88,0x83,0x4B,0xEF,0x05,0x92,0x06,0x71,0xF2,0xB8,0x98,0x93,0xB7, +0xEC,0xCD,0x82,0x61,0xF1,0x38,0xE6,0x4F,0x97,0x98,0x2A,0x5A,0x8D, +}; + + +/* subject:/C=US/OU=www.xrampsecurity.com/O=XRamp Security Services Inc/CN=XRamp Global Certification Authority */ +/* issuer :/C=US/OU=www.xrampsecurity.com/O=XRamp Security Services Inc/CN=XRamp Global Certification Authority */ + + +const unsigned char XRamp_Global_CA_Root_certificate[1076]={ +0x30,0x82,0x04,0x30,0x30,0x82,0x03,0x18,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x50, +0x94,0x6C,0xEC,0x18,0xEA,0xD5,0x9C,0x4D,0xD5,0x97,0xEF,0x75,0x8F,0xA0,0xAD,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x81, +0x82,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x1E, +0x30,0x1C,0x06,0x03,0x55,0x04,0x0B,0x13,0x15,0x77,0x77,0x77,0x2E,0x78,0x72,0x61, +0x6D,0x70,0x73,0x65,0x63,0x75,0x72,0x69,0x74,0x79,0x2E,0x63,0x6F,0x6D,0x31,0x24, +0x30,0x22,0x06,0x03,0x55,0x04,0x0A,0x13,0x1B,0x58,0x52,0x61,0x6D,0x70,0x20,0x53, +0x65,0x63,0x75,0x72,0x69,0x74,0x79,0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73, +0x20,0x49,0x6E,0x63,0x31,0x2D,0x30,0x2B,0x06,0x03,0x55,0x04,0x03,0x13,0x24,0x58, +0x52,0x61,0x6D,0x70,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x43,0x65,0x72,0x74, +0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72, +0x69,0x74,0x79,0x30,0x1E,0x17,0x0D,0x30,0x34,0x31,0x31,0x30,0x31,0x31,0x37,0x31, +0x34,0x30,0x34,0x5A,0x17,0x0D,0x33,0x35,0x30,0x31,0x30,0x31,0x30,0x35,0x33,0x37, +0x31,0x39,0x5A,0x30,0x81,0x82,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13, +0x02,0x55,0x53,0x31,0x1E,0x30,0x1C,0x06,0x03,0x55,0x04,0x0B,0x13,0x15,0x77,0x77, +0x77,0x2E,0x78,0x72,0x61,0x6D,0x70,0x73,0x65,0x63,0x75,0x72,0x69,0x74,0x79,0x2E, +0x63,0x6F,0x6D,0x31,0x24,0x30,0x22,0x06,0x03,0x55,0x04,0x0A,0x13,0x1B,0x58,0x52, +0x61,0x6D,0x70,0x20,0x53,0x65,0x63,0x75,0x72,0x69,0x74,0x79,0x20,0x53,0x65,0x72, +0x76,0x69,0x63,0x65,0x73,0x20,0x49,0x6E,0x63,0x31,0x2D,0x30,0x2B,0x06,0x03,0x55, +0x04,0x03,0x13,0x24,0x58,0x52,0x61,0x6D,0x70,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C, +0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41, +0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09, +0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00, +0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0x98,0x24,0x1E,0xBD,0x15,0xB4,0xBA, +0xDF,0xC7,0x8C,0xA5,0x27,0xB6,0x38,0x0B,0x69,0xF3,0xB6,0x4E,0xA8,0x2C,0x2E,0x21, +0x1D,0x5C,0x44,0xDF,0x21,0x5D,0x7E,0x23,0x74,0xFE,0x5E,0x7E,0xB4,0x4A,0xB7,0xA6, +0xAD,0x1F,0xAE,0xE0,0x06,0x16,0xE2,0x9B,0x5B,0xD9,0x67,0x74,0x6B,0x5D,0x80,0x8F, +0x29,0x9D,0x86,0x1B,0xD9,0x9C,0x0D,0x98,0x6D,0x76,0x10,0x28,0x58,0xE4,0x65,0xB0, +0x7F,0x4A,0x98,0x79,0x9F,0xE0,0xC3,0x31,0x7E,0x80,0x2B,0xB5,0x8C,0xC0,0x40,0x3B, +0x11,0x86,0xD0,0xCB,0xA2,0x86,0x36,0x60,0xA4,0xD5,0x30,0x82,0x6D,0xD9,0x6E,0xD0, +0x0F,0x12,0x04,0x33,0x97,0x5F,0x4F,0x61,0x5A,0xF0,0xE4,0xF9,0x91,0xAB,0xE7,0x1D, +0x3B,0xBC,0xE8,0xCF,0xF4,0x6B,0x2D,0x34,0x7C,0xE2,0x48,0x61,0x1C,0x8E,0xF3,0x61, +0x44,0xCC,0x6F,0xA0,0x4A,0xA9,0x94,0xB0,0x4D,0xDA,0xE7,0xA9,0x34,0x7A,0x72,0x38, +0xA8,0x41,0xCC,0x3C,0x94,0x11,0x7D,0xEB,0xC8,0xA6,0x8C,0xB7,0x86,0xCB,0xCA,0x33, +0x3B,0xD9,0x3D,0x37,0x8B,0xFB,0x7A,0x3E,0x86,0x2C,0xE7,0x73,0xD7,0x0A,0x57,0xAC, +0x64,0x9B,0x19,0xEB,0xF4,0x0F,0x04,0x08,0x8A,0xAC,0x03,0x17,0x19,0x64,0xF4,0x5A, +0x25,0x22,0x8D,0x34,0x2C,0xB2,0xF6,0x68,0x1D,0x12,0x6D,0xD3,0x8A,0x1E,0x14,0xDA, +0xC4,0x8F,0xA6,0xE2,0x23,0x85,0xD5,0x7A,0x0D,0xBD,0x6A,0xE0,0xE9,0xEC,0xEC,0x17, +0xBB,0x42,0x1B,0x67,0xAA,0x25,0xED,0x45,0x83,0x21,0xFC,0xC1,0xC9,0x7C,0xD5,0x62, +0x3E,0xFA,0xF2,0xC5,0x2D,0xD3,0xFD,0xD4,0x65,0x02,0x03,0x01,0x00,0x01,0xA3,0x81, +0x9F,0x30,0x81,0x9C,0x30,0x13,0x06,0x09,0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x14, +0x02,0x04,0x06,0x1E,0x04,0x00,0x43,0x00,0x41,0x30,0x0B,0x06,0x03,0x55,0x1D,0x0F, +0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF, +0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16, +0x04,0x14,0xC6,0x4F,0xA2,0x3D,0x06,0x63,0x84,0x09,0x9C,0xCE,0x62,0xE4,0x04,0xAC, +0x8D,0x5C,0xB5,0xE9,0xB6,0x1B,0x30,0x36,0x06,0x03,0x55,0x1D,0x1F,0x04,0x2F,0x30, +0x2D,0x30,0x2B,0xA0,0x29,0xA0,0x27,0x86,0x25,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F, +0x63,0x72,0x6C,0x2E,0x78,0x72,0x61,0x6D,0x70,0x73,0x65,0x63,0x75,0x72,0x69,0x74, +0x79,0x2E,0x63,0x6F,0x6D,0x2F,0x58,0x47,0x43,0x41,0x2E,0x63,0x72,0x6C,0x30,0x10, +0x06,0x09,0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x15,0x01,0x04,0x03,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03, +0x82,0x01,0x01,0x00,0x91,0x15,0x39,0x03,0x01,0x1B,0x67,0xFB,0x4A,0x1C,0xF9,0x0A, +0x60,0x5B,0xA1,0xDA,0x4D,0x97,0x62,0xF9,0x24,0x53,0x27,0xD7,0x82,0x64,0x4E,0x90, +0x2E,0xC3,0x49,0x1B,0x2B,0x9A,0xDC,0xFC,0xA8,0x78,0x67,0x35,0xF1,0x1D,0xF0,0x11, +0xBD,0xB7,0x48,0xE3,0x10,0xF6,0x0D,0xDF,0x3F,0xD2,0xC9,0xB6,0xAA,0x55,0xA4,0x48, +0xBA,0x02,0xDB,0xDE,0x59,0x2E,0x15,0x5B,0x3B,0x9D,0x16,0x7D,0x47,0xD7,0x37,0xEA, +0x5F,0x4D,0x76,0x12,0x36,0xBB,0x1F,0xD7,0xA1,0x81,0x04,0x46,0x20,0xA3,0x2C,0x6D, +0xA9,0x9E,0x01,0x7E,0x3F,0x29,0xCE,0x00,0x93,0xDF,0xFD,0xC9,0x92,0x73,0x89,0x89, +0x64,0x9E,0xE7,0x2B,0xE4,0x1C,0x91,0x2C,0xD2,0xB9,0xCE,0x7D,0xCE,0x6F,0x31,0x99, +0xD3,0xE6,0xBE,0xD2,0x1E,0x90,0xF0,0x09,0x14,0x79,0x5C,0x23,0xAB,0x4D,0xD2,0xDA, +0x21,0x1F,0x4D,0x99,0x79,0x9D,0xE1,0xCF,0x27,0x9F,0x10,0x9B,0x1C,0x88,0x0D,0xB0, +0x8A,0x64,0x41,0x31,0xB8,0x0E,0x6C,0x90,0x24,0xA4,0x9B,0x5C,0x71,0x8F,0xBA,0xBB, +0x7E,0x1C,0x1B,0xDB,0x6A,0x80,0x0F,0x21,0xBC,0xE9,0xDB,0xA6,0xB7,0x40,0xF4,0xB2, +0x8B,0xA9,0xB1,0xE4,0xEF,0x9A,0x1A,0xD0,0x3D,0x69,0x99,0xEE,0xA8,0x28,0xA3,0xE1, +0x3C,0xB3,0xF0,0xB2,0x11,0x9C,0xCF,0x7C,0x40,0xE6,0xDD,0xE7,0x43,0x7D,0xA2,0xD8, +0x3A,0xB5,0xA9,0x8D,0xF2,0x34,0x99,0xC4,0xD4,0x10,0xE1,0x06,0xFD,0x09,0x84,0x10, +0x3B,0xEE,0xC4,0x4C,0xF4,0xEC,0x27,0x7C,0x42,0xC2,0x74,0x7C,0x82,0x8A,0x09,0xC9, +0xB4,0x03,0x25,0xBC, +}; + + +const unsigned char* kSSLCertCertificateList[] = { + AddTrust_External_Root_certificate, + AddTrust_Low_Value_Services_Root_certificate, + AddTrust_Public_Services_Root_certificate, + AddTrust_Qualified_Certificates_Root_certificate, + AffirmTrust_Commercial_certificate, + AffirmTrust_Networking_certificate, + AffirmTrust_Premium_certificate, + AffirmTrust_Premium_ECC_certificate, + America_Online_Root_Certification_Authority_1_certificate, + America_Online_Root_Certification_Authority_2_certificate, + Baltimore_CyberTrust_Root_certificate, + Comodo_AAA_Services_root_certificate, + COMODO_Certification_Authority_certificate, + COMODO_ECC_Certification_Authority_certificate, + Comodo_Secure_Services_root_certificate, + Comodo_Trusted_Services_root_certificate, + Cybertrust_Global_Root_certificate, + DigiCert_Assured_ID_Root_CA_certificate, + DigiCert_Global_Root_CA_certificate, + DigiCert_High_Assurance_EV_Root_CA_certificate, + Entrust_net_Premium_2048_Secure_Server_CA_certificate, + Entrust_net_Secure_Server_CA_certificate, + Entrust_Root_Certification_Authority_certificate, + Equifax_Secure_CA_certificate, + Equifax_Secure_eBusiness_CA_1_certificate, + Equifax_Secure_eBusiness_CA_2_certificate, + Equifax_Secure_Global_eBusiness_CA_certificate, + GeoTrust_Global_CA_certificate, + GeoTrust_Global_CA_2_certificate, + GeoTrust_Primary_Certification_Authority_certificate, + GeoTrust_Primary_Certification_Authority___G2_certificate, + GeoTrust_Primary_Certification_Authority___G3_certificate, + GeoTrust_Universal_CA_certificate, + GeoTrust_Universal_CA_2_certificate, + GlobalSign_Root_CA_certificate, + GlobalSign_Root_CA___R2_certificate, + GlobalSign_Root_CA___R3_certificate, + Go_Daddy_Class_2_CA_certificate, + Go_Daddy_Root_Certificate_Authority___G2_certificate, + GTE_CyberTrust_Global_Root_certificate, + Network_Solutions_Certificate_Authority_certificate, + RSA_Root_Certificate_1_certificate, + Starfield_Class_2_CA_certificate, + Starfield_Root_Certificate_Authority___G2_certificate, + Starfield_Services_Root_Certificate_Authority___G2_certificate, + StartCom_Certification_Authority_certificate, + StartCom_Certification_Authority_G2_certificate, + TC_TrustCenter_Class_2_CA_II_certificate, + TC_TrustCenter_Class_3_CA_II_certificate, + TC_TrustCenter_Universal_CA_I_certificate, + TC_TrustCenter_Universal_CA_III_certificate, + Thawte_Premium_Server_CA_certificate, + thawte_Primary_Root_CA_certificate, + thawte_Primary_Root_CA___G2_certificate, + thawte_Primary_Root_CA___G3_certificate, + Thawte_Server_CA_certificate, + UTN_DATACorp_SGC_Root_CA_certificate, + UTN_USERFirst_Hardware_Root_CA_certificate, + ValiCert_Class_1_VA_certificate, + ValiCert_Class_2_VA_certificate, + Verisign_Class_3_Public_Primary_Certification_Authority_certificate, + Verisign_Class_3_Public_Primary_Certification_Authority___G2_certificate, + Verisign_Class_3_Public_Primary_Certification_Authority___G3_certificate, + VeriSign_Class_3_Public_Primary_Certification_Authority___G4_certificate, + VeriSign_Class_3_Public_Primary_Certification_Authority___G5_certificate, + Verisign_Class_4_Public_Primary_Certification_Authority___G3_certificate, + VeriSign_Universal_Root_Certification_Authority_certificate, + XRamp_Global_CA_Root_certificate, +}; + +const size_t kSSLCertCertificateSizeList[] = { + 1082, + 1052, + 1049, + 1058, + 848, + 848, + 1354, + 514, + 936, + 1448, + 891, + 1078, + 1057, + 653, + 1091, + 1095, + 933, + 955, + 947, + 969, + 1120, + 1244, + 1173, + 804, + 646, + 804, + 660, + 856, + 874, + 896, + 690, + 1026, + 1388, + 1392, + 889, + 958, + 867, + 1028, + 969, + 606, + 1002, + 747, + 1043, + 993, + 1011, + 1931, + 1383, + 1198, + 1198, + 993, + 997, + 811, + 1060, + 652, + 1070, + 791, + 1122, + 1144, + 747, + 747, + 576, + 774, + 1054, + 904, + 1239, + 1054, + 1213, + 1076, +}; + +} // namspace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslsocketfactory.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslsocketfactory.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslsocketfactory.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslsocketfactory.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,175 @@ +/* + * Copyright 2007 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/autodetectproxy.h" +#include "webrtc/base/httpcommon.h" +#include "webrtc/base/httpcommon-inl.h" +#include "webrtc/base/socketadapters.h" +#include "webrtc/base/ssladapter.h" +#include "webrtc/base/sslsocketfactory.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// ProxySocketAdapter +// TODO: Consider combining AutoDetectProxy and ProxySocketAdapter. I think +// the socket adapter is the more appropriate idiom for automatic proxy +// detection. We may or may not want to combine proxydetect.* as well. +/////////////////////////////////////////////////////////////////////////////// + +class ProxySocketAdapter : public AsyncSocketAdapter { + public: + ProxySocketAdapter(SslSocketFactory* factory, int family, int type) + : AsyncSocketAdapter(NULL), factory_(factory), family_(family), + type_(type), detect_(NULL) { + } + virtual ~ProxySocketAdapter() { + Close(); + } + + virtual int Connect(const SocketAddress& addr) { + ASSERT(NULL == detect_); + ASSERT(NULL == socket_); + remote_ = addr; + if (remote_.IsAnyIP() && remote_.hostname().empty()) { + LOG_F(LS_ERROR) << "Empty address"; + return SOCKET_ERROR; + } + Url url("/", remote_.HostAsURIString(), remote_.port()); + detect_ = new AutoDetectProxy(factory_->agent_); + detect_->set_server_url(url.url()); + detect_->SignalWorkDone.connect(this, + &ProxySocketAdapter::OnProxyDetectionComplete); + detect_->Start(); + return SOCKET_ERROR; + } + virtual int GetError() const { + if (socket_) { + return socket_->GetError(); + } + return detect_ ? EWOULDBLOCK : EADDRNOTAVAIL; + } + virtual int Close() { + if (socket_) { + return socket_->Close(); + } + if (detect_) { + detect_->Destroy(false); + detect_ = NULL; + } + return 0; + } + virtual ConnState GetState() const { + if (socket_) { + return socket_->GetState(); + } + return detect_ ? CS_CONNECTING : CS_CLOSED; + } + +private: + // AutoDetectProxy Slots + void OnProxyDetectionComplete(SignalThread* thread) { + ASSERT(detect_ == thread); + Attach(factory_->CreateProxySocket(detect_->proxy(), family_, type_)); + detect_->Release(); + detect_ = NULL; + if (0 == AsyncSocketAdapter::Connect(remote_)) { + SignalConnectEvent(this); + } else if (!IsBlockingError(socket_->GetError())) { + SignalCloseEvent(this, socket_->GetError()); + } + } + + SslSocketFactory* factory_; + int family_; + int type_; + SocketAddress remote_; + AutoDetectProxy* detect_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// SslSocketFactory +/////////////////////////////////////////////////////////////////////////////// + +Socket* SslSocketFactory::CreateSocket(int type) { + return CreateSocket(AF_INET, type); +} + +Socket* SslSocketFactory::CreateSocket(int family, int type) { + return factory_->CreateSocket(family, type); +} + +AsyncSocket* SslSocketFactory::CreateAsyncSocket(int type) { + return CreateAsyncSocket(AF_INET, type); +} + +AsyncSocket* SslSocketFactory::CreateAsyncSocket(int family, int type) { + if (autodetect_proxy_) { + return new ProxySocketAdapter(this, family, type); + } else { + return CreateProxySocket(proxy_, family, type); + } +} + + +AsyncSocket* SslSocketFactory::CreateProxySocket(const ProxyInfo& proxy, + int family, + int type) { + AsyncSocket* socket = factory_->CreateAsyncSocket(family, type); + if (!socket) + return NULL; + + // Binary logging happens at the lowest level + if (!logging_label_.empty() && binary_mode_) { + socket = new LoggingSocketAdapter(socket, logging_level_, + logging_label_.c_str(), binary_mode_); + } + + if (proxy.type) { + AsyncSocket* proxy_socket = 0; + if (proxy_.type == PROXY_SOCKS5) { + proxy_socket = new AsyncSocksProxySocket(socket, proxy.address, + proxy.username, proxy.password); + } else { + // Note: we are trying unknown proxies as HTTPS currently + AsyncHttpsProxySocket* http_proxy = + new AsyncHttpsProxySocket(socket, agent_, proxy.address, + proxy.username, proxy.password); + http_proxy->SetForceConnect(force_connect_ || !hostname_.empty()); + proxy_socket = http_proxy; + } + if (!proxy_socket) { + delete socket; + return NULL; + } + socket = proxy_socket; // for our purposes the proxy is now the socket + } + + if (!hostname_.empty()) { + if (SSLAdapter* ssl_adapter = SSLAdapter::Create(socket)) { + ssl_adapter->set_ignore_bad_cert(ignore_bad_cert_); + ssl_adapter->StartSSL(hostname_.c_str(), true); + socket = ssl_adapter; + } else { + LOG_F(LS_ERROR) << "SSL unavailable"; + } + } + + // Regular logging occurs at the highest level + if (!logging_label_.empty() && !binary_mode_) { + socket = new LoggingSocketAdapter(socket, logging_level_, + logging_label_.c_str(), binary_mode_); + } + return socket; +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslsocketfactory.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslsocketfactory.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslsocketfactory.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslsocketfactory.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,81 @@ +/* + * Copyright 2007 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SSLSOCKETFACTORY_H__ +#define WEBRTC_BASE_SSLSOCKETFACTORY_H__ + +#include "webrtc/base/proxyinfo.h" +#include "webrtc/base/socketserver.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// SslSocketFactory +/////////////////////////////////////////////////////////////////////////////// + +class SslSocketFactory : public SocketFactory { + public: + SslSocketFactory(SocketFactory* factory, const std::string& user_agent) + : factory_(factory), agent_(user_agent), autodetect_proxy_(true), + force_connect_(false), logging_level_(LS_VERBOSE), binary_mode_(false), + ignore_bad_cert_(false) { + } + + void SetAutoDetectProxy() { + autodetect_proxy_ = true; + } + void SetForceConnect(bool force) { + force_connect_ = force; + } + void SetProxy(const ProxyInfo& proxy) { + autodetect_proxy_ = false; + proxy_ = proxy; + } + bool autodetect_proxy() const { return autodetect_proxy_; } + const ProxyInfo& proxy() const { return proxy_; } + + void UseSSL(const char* hostname) { hostname_ = hostname; } + void DisableSSL() { hostname_.clear(); } + void SetIgnoreBadCert(bool ignore) { ignore_bad_cert_ = ignore; } + bool ignore_bad_cert() const { return ignore_bad_cert_; } + + void SetLogging(LoggingSeverity level, const std::string& label, + bool binary_mode = false) { + logging_level_ = level; + logging_label_ = label; + binary_mode_ = binary_mode; + } + + // SocketFactory Interface + virtual Socket* CreateSocket(int type); + virtual Socket* CreateSocket(int family, int type); + + virtual AsyncSocket* CreateAsyncSocket(int type); + virtual AsyncSocket* CreateAsyncSocket(int family, int type); + + private: + friend class ProxySocketAdapter; + AsyncSocket* CreateProxySocket(const ProxyInfo& proxy, int family, int type); + + SocketFactory* factory_; + std::string agent_; + bool autodetect_proxy_, force_connect_; + ProxyInfo proxy_; + std::string hostname_, logging_label_; + LoggingSeverity logging_level_; + bool binary_mode_; + bool ignore_bad_cert_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_SSLSOCKETFACTORY_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapter.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapter.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,77 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#include "webrtc/base/sslstreamadapter.h" +#include "webrtc/base/sslconfig.h" + +#if SSL_USE_SCHANNEL + +// SChannel support for DTLS and peer-to-peer mode are not +// done. +#elif SSL_USE_OPENSSL // && !SSL_USE_SCHANNEL + +#include "webrtc/base/opensslstreamadapter.h" + +#elif SSL_USE_NSS // && !SSL_USE_SCHANNEL && !SSL_USE_OPENSSL + +#include "webrtc/base/nssstreamadapter.h" + +#endif // !SSL_USE_OPENSSL && !SSL_USE_SCHANNEL && !SSL_USE_NSS + +/////////////////////////////////////////////////////////////////////////////// + +namespace rtc { + +SSLStreamAdapter* SSLStreamAdapter::Create(StreamInterface* stream) { +#if SSL_USE_SCHANNEL + return NULL; +#elif SSL_USE_OPENSSL // !SSL_USE_SCHANNEL + return new OpenSSLStreamAdapter(stream); +#elif SSL_USE_NSS // !SSL_USE_SCHANNEL && !SSL_USE_OPENSSL + return new NSSStreamAdapter(stream); +#else // !SSL_USE_SCHANNEL && !SSL_USE_OPENSSL && !SSL_USE_NSS + return NULL; +#endif +} + +// Note: this matches the logic above with SCHANNEL dominating +#if SSL_USE_SCHANNEL +bool SSLStreamAdapter::HaveDtls() { return false; } +bool SSLStreamAdapter::HaveDtlsSrtp() { return false; } +bool SSLStreamAdapter::HaveExporter() { return false; } +#elif SSL_USE_OPENSSL +bool SSLStreamAdapter::HaveDtls() { + return OpenSSLStreamAdapter::HaveDtls(); +} +bool SSLStreamAdapter::HaveDtlsSrtp() { + return OpenSSLStreamAdapter::HaveDtlsSrtp(); +} +bool SSLStreamAdapter::HaveExporter() { + return OpenSSLStreamAdapter::HaveExporter(); +} +#elif SSL_USE_NSS +bool SSLStreamAdapter::HaveDtls() { + return NSSStreamAdapter::HaveDtls(); +} +bool SSLStreamAdapter::HaveDtlsSrtp() { + return NSSStreamAdapter::HaveDtlsSrtp(); +} +bool SSLStreamAdapter::HaveExporter() { + return NSSStreamAdapter::HaveExporter(); +} +#endif // !SSL_USE_SCHANNEL && !SSL_USE_OPENSSL && !SSL_USE_NSS + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapter.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapter.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,172 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SSLSTREAMADAPTER_H_ +#define WEBRTC_BASE_SSLSTREAMADAPTER_H_ + +#include +#include + +#include "webrtc/base/stream.h" +#include "webrtc/base/sslidentity.h" + +namespace rtc { + +// SSLStreamAdapter : A StreamInterfaceAdapter that does SSL/TLS. +// After SSL has been started, the stream will only open on successful +// SSL verification of certificates, and the communication is +// encrypted of course. +// +// This class was written with SSLAdapter as a starting point. It +// offers a similar interface, with two differences: there is no +// support for a restartable SSL connection, and this class has a +// peer-to-peer mode. +// +// The SSL library requires initialization and cleanup. Static method +// for doing this are in SSLAdapter. They should possibly be moved out +// to a neutral class. + + +enum SSLRole { SSL_CLIENT, SSL_SERVER }; +enum SSLMode { SSL_MODE_TLS, SSL_MODE_DTLS }; + +// Errors for Read -- in the high range so no conflict with OpenSSL. +enum { SSE_MSG_TRUNC = 0xff0001 }; + +class SSLStreamAdapter : public StreamAdapterInterface { + public: + // Instantiate an SSLStreamAdapter wrapping the given stream, + // (using the selected implementation for the platform). + // Caller is responsible for freeing the returned object. + static SSLStreamAdapter* Create(StreamInterface* stream); + + explicit SSLStreamAdapter(StreamInterface* stream) + : StreamAdapterInterface(stream), ignore_bad_cert_(false), + client_auth_enabled_(true) { } + + void set_ignore_bad_cert(bool ignore) { ignore_bad_cert_ = ignore; } + bool ignore_bad_cert() const { return ignore_bad_cert_; } + + void set_client_auth_enabled(bool enabled) { client_auth_enabled_ = enabled; } + bool client_auth_enabled() const { return client_auth_enabled_; } + + // Specify our SSL identity: key and certificate. Mostly this is + // only used in the peer-to-peer mode (unless we actually want to + // provide a client certificate to a server). + // SSLStream takes ownership of the SSLIdentity object and will + // free it when appropriate. Should be called no more than once on a + // given SSLStream instance. + virtual void SetIdentity(SSLIdentity* identity) = 0; + + // Call this to indicate that we are to play the server's role in + // the peer-to-peer mode. + // The default argument is for backward compatibility + // TODO(ekr@rtfm.com): rename this SetRole to reflect its new function + virtual void SetServerRole(SSLRole role = SSL_SERVER) = 0; + + // Do DTLS or TLS + virtual void SetMode(SSLMode mode) = 0; + + // The mode of operation is selected by calling either + // StartSSLWithServer or StartSSLWithPeer. + // Use of the stream prior to calling either of these functions will + // pass data in clear text. + // Calling one of these functions causes SSL negotiation to begin as + // soon as possible: right away if the underlying wrapped stream is + // already opened, or else as soon as it opens. + // + // These functions return a negative error code on failure. + // Returning 0 means success so far, but negotiation is probably not + // complete and will continue asynchronously. In that case, the + // exposed stream will open after successful negotiation and + // verification, or an SE_CLOSE event will be raised if negotiation + // fails. + + // StartSSLWithServer starts SSL negotiation with a server in + // traditional mode. server_name specifies the expected server name + // which the server's certificate needs to specify. + virtual int StartSSLWithServer(const char* server_name) = 0; + + // StartSSLWithPeer starts negotiation in the special peer-to-peer + // mode. + // Generally, SetIdentity() and possibly SetServerRole() should have + // been called before this. + // SetPeerCertificate() or SetPeerCertificateDigest() must also be called. + // It may be called after StartSSLWithPeer() but must be called before the + // underlying stream opens. + virtual int StartSSLWithPeer() = 0; + + // Specify the digest of the certificate that our peer is expected to use in + // peer-to-peer mode. Only this certificate will be accepted during + // SSL verification. The certificate is assumed to have been + // obtained through some other secure channel (such as the XMPP + // channel). Unlike SetPeerCertificate(), this must specify the + // terminal certificate, not just a CA. + // SSLStream makes a copy of the digest value. + virtual bool SetPeerCertificateDigest(const std::string& digest_alg, + const unsigned char* digest_val, + size_t digest_len) = 0; + + // Retrieves the peer's X.509 certificate, if a connection has been + // established. It returns the transmitted over SSL, including the entire + // chain. The returned certificate is owned by the caller. + virtual bool GetPeerCertificate(SSLCertificate** cert) const = 0; + + // Key Exporter interface from RFC 5705 + // Arguments are: + // label -- the exporter label. + // part of the RFC defining each exporter + // usage (IN) + // context/context_len -- a context to bind to for this connection; + // optional, can be NULL, 0 (IN) + // use_context -- whether to use the context value + // (needed to distinguish no context from + // zero-length ones). + // result -- where to put the computed value + // result_len -- the length of the computed value + virtual bool ExportKeyingMaterial(const std::string& label, + const uint8* context, + size_t context_len, + bool use_context, + uint8* result, + size_t result_len) { + return false; // Default is unsupported + } + + + // DTLS-SRTP interface + virtual bool SetDtlsSrtpCiphers(const std::vector& ciphers) { + return false; + } + + virtual bool GetDtlsSrtpCipher(std::string* cipher) { + return false; + } + + // Capabilities testing + static bool HaveDtls(); + static bool HaveDtlsSrtp(); + static bool HaveExporter(); + + private: + // If true, the server certificate need not match the configured + // server_name, and in fact missing certificate authority and other + // verification errors are ignored. + bool ignore_bad_cert_; + + // If true (default), the client is required to provide a certificate during + // handshake. If no certificate is given, handshake fails. This applies to + // server mode only. + bool client_auth_enabled_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_SSLSTREAMADAPTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapterhelper.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapterhelper.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapterhelper.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapterhelper.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,130 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + + +#include + +#if HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#include "webrtc/base/sslstreamadapterhelper.h" + +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/stream.h" + +namespace rtc { + +void SSLStreamAdapterHelper::SetIdentity(SSLIdentity* identity) { + ASSERT(identity_.get() == NULL); + identity_.reset(identity); +} + +void SSLStreamAdapterHelper::SetServerRole(SSLRole role) { + role_ = role; +} + +int SSLStreamAdapterHelper::StartSSLWithServer(const char* server_name) { + ASSERT(server_name != NULL && server_name[0] != '\0'); + ssl_server_name_ = server_name; + return StartSSL(); +} + +int SSLStreamAdapterHelper::StartSSLWithPeer() { + ASSERT(ssl_server_name_.empty()); + // It is permitted to specify peer_certificate_ only later. + return StartSSL(); +} + +void SSLStreamAdapterHelper::SetMode(SSLMode mode) { + ASSERT(state_ == SSL_NONE); + ssl_mode_ = mode; +} + +StreamState SSLStreamAdapterHelper::GetState() const { + switch (state_) { + case SSL_WAIT: + case SSL_CONNECTING: + return SS_OPENING; + case SSL_CONNECTED: + return SS_OPEN; + default: + return SS_CLOSED; + }; + // not reached +} + +bool SSLStreamAdapterHelper::GetPeerCertificate(SSLCertificate** cert) const { + if (!peer_certificate_) + return false; + + *cert = peer_certificate_->GetReference(); + return true; +} + +bool SSLStreamAdapterHelper::SetPeerCertificateDigest( + const std::string &digest_alg, + const unsigned char* digest_val, + size_t digest_len) { + ASSERT(peer_certificate_.get() == NULL); + ASSERT(peer_certificate_digest_algorithm_.empty()); + ASSERT(ssl_server_name_.empty()); + size_t expected_len; + + if (!GetDigestLength(digest_alg, &expected_len)) { + LOG(LS_WARNING) << "Unknown digest algorithm: " << digest_alg; + return false; + } + if (expected_len != digest_len) + return false; + + peer_certificate_digest_value_.SetData(digest_val, digest_len); + peer_certificate_digest_algorithm_ = digest_alg; + + return true; +} + +void SSLStreamAdapterHelper::Error(const char* context, int err, bool signal) { + LOG(LS_WARNING) << "SSLStreamAdapterHelper::Error(" + << context << ", " << err << "," << signal << ")"; + state_ = SSL_ERROR; + ssl_error_code_ = err; + Cleanup(); + if (signal) + StreamAdapterInterface::OnEvent(stream(), SE_CLOSE, err); +} + +void SSLStreamAdapterHelper::Close() { + Cleanup(); + ASSERT(state_ == SSL_CLOSED || state_ == SSL_ERROR); + StreamAdapterInterface::Close(); +} + +int SSLStreamAdapterHelper::StartSSL() { + ASSERT(state_ == SSL_NONE); + + if (StreamAdapterInterface::GetState() != SS_OPEN) { + state_ = SSL_WAIT; + return 0; + } + + state_ = SSL_CONNECTING; + int err = BeginSSL(); + if (err) { + Error("BeginSSL", err, false); + return err; + } + + return 0; +} + +} // namespace rtc + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapterhelper.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapterhelper.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapterhelper.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapterhelper.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,118 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SSLSTREAMADAPTERHELPER_H_ +#define WEBRTC_BASE_SSLSTREAMADAPTERHELPER_H_ + +#include +#include + +#include "webrtc/base/buffer.h" +#include "webrtc/base/stream.h" +#include "webrtc/base/sslidentity.h" +#include "webrtc/base/sslstreamadapter.h" + +namespace rtc { + +// SSLStreamAdapterHelper : A stream adapter which implements much +// of the logic that is common between the known implementations +// (NSS and OpenSSL) +class SSLStreamAdapterHelper : public SSLStreamAdapter { + public: + explicit SSLStreamAdapterHelper(StreamInterface* stream) + : SSLStreamAdapter(stream), + state_(SSL_NONE), + role_(SSL_CLIENT), + ssl_error_code_(0), // Not meaningful yet + ssl_mode_(SSL_MODE_TLS) {} + + + // Overrides of SSLStreamAdapter + virtual void SetIdentity(SSLIdentity* identity); + virtual void SetServerRole(SSLRole role = SSL_SERVER); + virtual void SetMode(SSLMode mode); + + virtual int StartSSLWithServer(const char* server_name); + virtual int StartSSLWithPeer(); + + virtual bool SetPeerCertificateDigest(const std::string& digest_alg, + const unsigned char* digest_val, + size_t digest_len); + virtual bool GetPeerCertificate(SSLCertificate** cert) const; + virtual StreamState GetState() const; + virtual void Close(); + + protected: + // Internal helper methods + // The following method returns 0 on success and a negative + // error code on failure. The error code may be either -1 or + // from the impl on some other error cases, so it can't really be + // interpreted unfortunately. + + // Perform SSL negotiation steps. + int ContinueSSL(); + + // Error handler helper. signal is given as true for errors in + // asynchronous contexts (when an error code was not returned + // through some other method), and in that case an SE_CLOSE event is + // raised on the stream with the specified error. + // A 0 error means a graceful close, otherwise there is not really enough + // context to interpret the error code. + virtual void Error(const char* context, int err, bool signal); + + // Must be implemented by descendents + virtual int BeginSSL() = 0; + virtual void Cleanup() = 0; + virtual bool GetDigestLength(const std::string& algorithm, + size_t* length) = 0; + + enum SSLState { + // Before calling one of the StartSSL methods, data flows + // in clear text. + SSL_NONE, + SSL_WAIT, // waiting for the stream to open to start SSL negotiation + SSL_CONNECTING, // SSL negotiation in progress + SSL_CONNECTED, // SSL stream successfully established + SSL_ERROR, // some SSL error occurred, stream is closed + SSL_CLOSED // Clean close + }; + + // MSG_MAX is the maximum generic stream message number. + enum { MSG_DTLS_TIMEOUT = MSG_MAX + 1 }; + + SSLState state_; + SSLRole role_; + int ssl_error_code_; // valid when state_ == SSL_ERROR + + // Our key and certificate, mostly useful in peer-to-peer mode. + scoped_ptr identity_; + // in traditional mode, the server name that the server's certificate + // must specify. Empty in peer-to-peer mode. + std::string ssl_server_name_; + // The peer's certificate. Only used for GetPeerCertificate. + scoped_ptr peer_certificate_; + + // The digest of the certificate that the peer must present. + Buffer peer_certificate_digest_value_; + std::string peer_certificate_digest_algorithm_; + + // Do DTLS or not + SSLMode ssl_mode_; + + private: + // Go from state SSL_NONE to either SSL_CONNECTING or SSL_WAIT, + // depending on whether the underlying stream is already open or + // not. Returns 0 on success and a negative value on error. + int StartSSL(); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_SSLSTREAMADAPTERHELPER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapter_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapter_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapter_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/sslstreamadapter_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,941 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + + +#include +#include +#include + +#include "webrtc/base/gunit.h" +#include "webrtc/base/helpers.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/ssladapter.h" +#include "webrtc/base/sslconfig.h" +#include "webrtc/base/sslidentity.h" +#include "webrtc/base/sslstreamadapter.h" +#include "webrtc/base/stream.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +static const int kBlockSize = 4096; +static const char kAES_CM_HMAC_SHA1_80[] = "AES_CM_128_HMAC_SHA1_80"; +static const char kAES_CM_HMAC_SHA1_32[] = "AES_CM_128_HMAC_SHA1_32"; +static const char kExporterLabel[] = "label"; +static const unsigned char kExporterContext[] = "context"; +static int kExporterContextLen = sizeof(kExporterContext); + +static const char kRSA_PRIVATE_KEY_PEM[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMYRkbhmI7kVA/rM\n" + "czsZ+6JDhDvnkF+vn6yCAGuRPV03zuRqZtDy4N4to7PZu9PjqrRl7nDMXrG3YG9y\n" + "rlIAZ72KjcKKFAJxQyAKLCIdawKRyp8RdK3LEySWEZb0AV58IadqPZDTNHHRX8dz\n" + "5aTSMsbbkZ+C/OzTnbiMqLL/vg6jAgMBAAECgYAvgOs4FJcgvp+TuREx7YtiYVsH\n" + "mwQPTum2z/8VzWGwR8BBHBvIpVe1MbD/Y4seyI2aco/7UaisatSgJhsU46/9Y4fq\n" + "2TwXH9QANf4at4d9n/R6rzwpAJOpgwZgKvdQjkfrKTtgLV+/dawvpxUYkRH4JZM1\n" + "CVGukMfKNrSVH4Ap4QJBAOJmGV1ASPnB4r4nc99at7JuIJmd7fmuVUwUgYi4XgaR\n" + "WhScBsgYwZ/JoywdyZJgnbcrTDuVcWG56B3vXbhdpMsCQQDf9zeJrjnPZ3Cqm79y\n" + "kdqANep0uwZciiNiWxsQrCHztywOvbFhdp8iYVFG9EK8DMY41Y5TxUwsHD+67zao\n" + "ZNqJAkEA1suLUP/GvL8IwuRneQd2tWDqqRQ/Td3qq03hP7e77XtF/buya3Ghclo5\n" + "54czUR89QyVfJEC6278nzA7n2h1uVQJAcG6mztNL6ja/dKZjYZye2CY44QjSlLo0\n" + "MTgTSjdfg/28fFn2Jjtqf9Pi/X+50LWI/RcYMC2no606wRk9kyOuIQJBAK6VSAim\n" + "1pOEjsYQn0X5KEIrz1G3bfCbB848Ime3U2/FWlCHMr6ch8kCZ5d1WUeJD3LbwMNG\n" + "UCXiYxSsu20QNVw=\n" + "-----END RSA PRIVATE KEY-----\n"; + +static const char kCERT_PEM[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIBmTCCAQKgAwIBAgIEbzBSAjANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDEwZX\n" + "ZWJSVEMwHhcNMTQwMTAyMTgyNDQ3WhcNMTQwMjAxMTgyNDQ3WjARMQ8wDQYDVQQD\n" + "EwZXZWJSVEMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMYRkbhmI7kVA/rM\n" + "czsZ+6JDhDvnkF+vn6yCAGuRPV03zuRqZtDy4N4to7PZu9PjqrRl7nDMXrG3YG9y\n" + "rlIAZ72KjcKKFAJxQyAKLCIdawKRyp8RdK3LEySWEZb0AV58IadqPZDTNHHRX8dz\n" + "5aTSMsbbkZ+C/OzTnbiMqLL/vg6jAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAUflI\n" + "VUe5Krqf5RVa5C3u/UTAOAUJBiDS3VANTCLBxjuMsvqOG0WvaYWP3HYPgrz0jXK2\n" + "LJE/mGw3MyFHEqi81jh95J+ypl6xKW6Rm8jKLR87gUvCaVYn/Z4/P3AqcQTB7wOv\n" + "UD0A8qfhfDM+LK6rPAnCsVN0NRDY3jvd6rzix9M=\n" + "-----END CERTIFICATE-----\n"; + +#define MAYBE_SKIP_TEST(feature) \ + if (!(rtc::SSLStreamAdapter::feature())) { \ + LOG(LS_INFO) << "Feature disabled... skipping"; \ + return; \ + } + +class SSLStreamAdapterTestBase; + +class SSLDummyStream : public rtc::StreamInterface, + public sigslot::has_slots<> { + public: + explicit SSLDummyStream(SSLStreamAdapterTestBase *test, + const std::string &side, + rtc::FifoBuffer *in, + rtc::FifoBuffer *out) : + test_(test), + side_(side), + in_(in), + out_(out), + first_packet_(true) { + in_->SignalEvent.connect(this, &SSLDummyStream::OnEventIn); + out_->SignalEvent.connect(this, &SSLDummyStream::OnEventOut); + } + + virtual rtc::StreamState GetState() const { return rtc::SS_OPEN; } + + virtual rtc::StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + rtc::StreamResult r; + + r = in_->Read(buffer, buffer_len, read, error); + if (r == rtc::SR_BLOCK) + return rtc::SR_BLOCK; + if (r == rtc::SR_EOS) + return rtc::SR_EOS; + + if (r != rtc::SR_SUCCESS) { + ADD_FAILURE(); + return rtc::SR_ERROR; + } + + return rtc::SR_SUCCESS; + } + + // Catch readability events on in and pass them up. + virtual void OnEventIn(rtc::StreamInterface *stream, int sig, + int err) { + int mask = (rtc::SE_READ | rtc::SE_CLOSE); + + if (sig & mask) { + LOG(LS_INFO) << "SSLDummyStream::OnEvent side=" << side_ << " sig=" + << sig << " forwarding upward"; + PostEvent(sig & mask, 0); + } + } + + // Catch writeability events on out and pass them up. + virtual void OnEventOut(rtc::StreamInterface *stream, int sig, + int err) { + if (sig & rtc::SE_WRITE) { + LOG(LS_INFO) << "SSLDummyStream::OnEvent side=" << side_ << " sig=" + << sig << " forwarding upward"; + + PostEvent(sig & rtc::SE_WRITE, 0); + } + } + + // Write to the outgoing FifoBuffer + rtc::StreamResult WriteData(const void* data, size_t data_len, + size_t* written, int* error) { + return out_->Write(data, data_len, written, error); + } + + // Defined later + virtual rtc::StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + + virtual void Close() { + LOG(LS_INFO) << "Closing outbound stream"; + out_->Close(); + } + + private: + SSLStreamAdapterTestBase *test_; + const std::string side_; + rtc::FifoBuffer *in_; + rtc::FifoBuffer *out_; + bool first_packet_; +}; + +static const int kFifoBufferSize = 4096; + +class SSLStreamAdapterTestBase : public testing::Test, + public sigslot::has_slots<> { + public: + SSLStreamAdapterTestBase(const std::string& client_cert_pem, + const std::string& client_private_key_pem, + bool dtls) : + client_buffer_(kFifoBufferSize), server_buffer_(kFifoBufferSize), + client_stream_( + new SSLDummyStream(this, "c2s", &client_buffer_, &server_buffer_)), + server_stream_( + new SSLDummyStream(this, "s2c", &server_buffer_, &client_buffer_)), + client_ssl_(rtc::SSLStreamAdapter::Create(client_stream_)), + server_ssl_(rtc::SSLStreamAdapter::Create(server_stream_)), + client_identity_(NULL), server_identity_(NULL), + delay_(0), mtu_(1460), loss_(0), lose_first_packet_(false), + damage_(false), dtls_(dtls), + handshake_wait_(5000), identities_set_(false) { + // Set use of the test RNG to get predictable loss patterns. + rtc::SetRandomTestMode(true); + + // Set up the slots + client_ssl_->SignalEvent.connect(this, &SSLStreamAdapterTestBase::OnEvent); + server_ssl_->SignalEvent.connect(this, &SSLStreamAdapterTestBase::OnEvent); + + if (!client_cert_pem.empty() && !client_private_key_pem.empty()) { + client_identity_ = rtc::SSLIdentity::FromPEMStrings( + client_private_key_pem, client_cert_pem); + } else { + client_identity_ = rtc::SSLIdentity::Generate("client"); + } + server_identity_ = rtc::SSLIdentity::Generate("server"); + + client_ssl_->SetIdentity(client_identity_); + server_ssl_->SetIdentity(server_identity_); + } + + ~SSLStreamAdapterTestBase() { + // Put it back for the next test. + rtc::SetRandomTestMode(false); + } + + // Recreate the client/server identities with the specified validity period. + // |not_before| and |not_after| are offsets from the current time in number + // of seconds. + void ResetIdentitiesWithValidity(int not_before, int not_after) { + client_stream_ = + new SSLDummyStream(this, "c2s", &client_buffer_, &server_buffer_); + server_stream_ = + new SSLDummyStream(this, "s2c", &server_buffer_, &client_buffer_); + + client_ssl_.reset(rtc::SSLStreamAdapter::Create(client_stream_)); + server_ssl_.reset(rtc::SSLStreamAdapter::Create(server_stream_)); + + client_ssl_->SignalEvent.connect(this, &SSLStreamAdapterTestBase::OnEvent); + server_ssl_->SignalEvent.connect(this, &SSLStreamAdapterTestBase::OnEvent); + + rtc::SSLIdentityParams client_params; + client_params.common_name = "client"; + client_params.not_before = not_before; + client_params.not_after = not_after; + client_identity_ = rtc::SSLIdentity::GenerateForTest(client_params); + + rtc::SSLIdentityParams server_params; + server_params.common_name = "server"; + server_params.not_before = not_before; + server_params.not_after = not_after; + server_identity_ = rtc::SSLIdentity::GenerateForTest(server_params); + + client_ssl_->SetIdentity(client_identity_); + server_ssl_->SetIdentity(server_identity_); + } + + virtual void OnEvent(rtc::StreamInterface *stream, int sig, int err) { + LOG(LS_INFO) << "SSLStreamAdapterTestBase::OnEvent sig=" << sig; + + if (sig & rtc::SE_READ) { + ReadData(stream); + } + + if ((stream == client_ssl_.get()) && (sig & rtc::SE_WRITE)) { + WriteData(); + } + } + + void SetPeerIdentitiesByDigest(bool correct) { + unsigned char digest[20]; + size_t digest_len; + bool rv; + + LOG(LS_INFO) << "Setting peer identities by digest"; + + rv = server_identity_->certificate().ComputeDigest(rtc::DIGEST_SHA_1, + digest, 20, + &digest_len); + ASSERT_TRUE(rv); + if (!correct) { + LOG(LS_INFO) << "Setting bogus digest for server cert"; + digest[0]++; + } + rv = client_ssl_->SetPeerCertificateDigest(rtc::DIGEST_SHA_1, digest, + digest_len); + ASSERT_TRUE(rv); + + + rv = client_identity_->certificate().ComputeDigest(rtc::DIGEST_SHA_1, + digest, 20, &digest_len); + ASSERT_TRUE(rv); + if (!correct) { + LOG(LS_INFO) << "Setting bogus digest for client cert"; + digest[0]++; + } + rv = server_ssl_->SetPeerCertificateDigest(rtc::DIGEST_SHA_1, digest, + digest_len); + ASSERT_TRUE(rv); + + identities_set_ = true; + } + + void TestHandshake(bool expect_success = true) { + server_ssl_->SetMode(dtls_ ? rtc::SSL_MODE_DTLS : + rtc::SSL_MODE_TLS); + client_ssl_->SetMode(dtls_ ? rtc::SSL_MODE_DTLS : + rtc::SSL_MODE_TLS); + + if (!dtls_) { + // Make sure we simulate a reliable network for TLS. + // This is just a check to make sure that people don't write wrong + // tests. + ASSERT((mtu_ == 1460) && (loss_ == 0) && (lose_first_packet_ == 0)); + } + + if (!identities_set_) + SetPeerIdentitiesByDigest(true); + + // Start the handshake + int rv; + + server_ssl_->SetServerRole(); + rv = server_ssl_->StartSSLWithPeer(); + ASSERT_EQ(0, rv); + + rv = client_ssl_->StartSSLWithPeer(); + ASSERT_EQ(0, rv); + + // Now run the handshake + if (expect_success) { + EXPECT_TRUE_WAIT((client_ssl_->GetState() == rtc::SS_OPEN) + && (server_ssl_->GetState() == rtc::SS_OPEN), + handshake_wait_); + } else { + EXPECT_TRUE_WAIT(client_ssl_->GetState() == rtc::SS_CLOSED, + handshake_wait_); + } + } + + rtc::StreamResult DataWritten(SSLDummyStream *from, const void *data, + size_t data_len, size_t *written, + int *error) { + // Randomly drop loss_ percent of packets + if (rtc::CreateRandomId() % 100 < static_cast(loss_)) { + LOG(LS_INFO) << "Randomly dropping packet, size=" << data_len; + *written = data_len; + return rtc::SR_SUCCESS; + } + if (dtls_ && (data_len > mtu_)) { + LOG(LS_INFO) << "Dropping packet > mtu, size=" << data_len; + *written = data_len; + return rtc::SR_SUCCESS; + } + + // Optionally damage application data (type 23). Note that we don't damage + // handshake packets and we damage the last byte to keep the header + // intact but break the MAC. + if (damage_ && (*static_cast(data) == 23)) { + std::vector buf(data_len); + + LOG(LS_INFO) << "Damaging packet"; + + memcpy(&buf[0], data, data_len); + buf[data_len - 1]++; + + return from->WriteData(&buf[0], data_len, written, error); + } + + return from->WriteData(data, data_len, written, error); + } + + void SetDelay(int delay) { + delay_ = delay; + } + int GetDelay() { return delay_; } + + void SetLoseFirstPacket(bool lose) { + lose_first_packet_ = lose; + } + bool GetLoseFirstPacket() { return lose_first_packet_; } + + void SetLoss(int percent) { + loss_ = percent; + } + + void SetDamage() { + damage_ = true; + } + + void SetMtu(size_t mtu) { + mtu_ = mtu; + } + + void SetHandshakeWait(int wait) { + handshake_wait_ = wait; + } + + void SetDtlsSrtpCiphers(const std::vector &ciphers, + bool client) { + if (client) + client_ssl_->SetDtlsSrtpCiphers(ciphers); + else + server_ssl_->SetDtlsSrtpCiphers(ciphers); + } + + bool GetDtlsSrtpCipher(bool client, std::string *retval) { + if (client) + return client_ssl_->GetDtlsSrtpCipher(retval); + else + return server_ssl_->GetDtlsSrtpCipher(retval); + } + + bool GetPeerCertificate(bool client, rtc::SSLCertificate** cert) { + if (client) + return client_ssl_->GetPeerCertificate(cert); + else + return server_ssl_->GetPeerCertificate(cert); + } + + bool ExportKeyingMaterial(const char *label, + const unsigned char *context, + size_t context_len, + bool use_context, + bool client, + unsigned char *result, + size_t result_len) { + if (client) + return client_ssl_->ExportKeyingMaterial(label, + context, context_len, + use_context, + result, result_len); + else + return server_ssl_->ExportKeyingMaterial(label, + context, context_len, + use_context, + result, result_len); + } + + // To be implemented by subclasses. + virtual void WriteData() = 0; + virtual void ReadData(rtc::StreamInterface *stream) = 0; + virtual void TestTransfer(int size) = 0; + + protected: + rtc::FifoBuffer client_buffer_; + rtc::FifoBuffer server_buffer_; + SSLDummyStream *client_stream_; // freed by client_ssl_ destructor + SSLDummyStream *server_stream_; // freed by server_ssl_ destructor + rtc::scoped_ptr client_ssl_; + rtc::scoped_ptr server_ssl_; + rtc::SSLIdentity *client_identity_; // freed by client_ssl_ destructor + rtc::SSLIdentity *server_identity_; // freed by server_ssl_ destructor + int delay_; + size_t mtu_; + int loss_; + bool lose_first_packet_; + bool damage_; + bool dtls_; + int handshake_wait_; + bool identities_set_; +}; + +class SSLStreamAdapterTestTLS : public SSLStreamAdapterTestBase { + public: + SSLStreamAdapterTestTLS() : + SSLStreamAdapterTestBase("", "", false) { + }; + + // Test data transfer for TLS + virtual void TestTransfer(int size) { + LOG(LS_INFO) << "Starting transfer test with " << size << " bytes"; + // Create some dummy data to send. + size_t received; + + send_stream_.ReserveSize(size); + for (int i = 0; i < size; ++i) { + char ch = static_cast(i); + send_stream_.Write(&ch, 1, NULL, NULL); + } + send_stream_.Rewind(); + + // Prepare the receive stream. + recv_stream_.ReserveSize(size); + + // Start sending + WriteData(); + + // Wait for the client to close + EXPECT_TRUE_WAIT(server_ssl_->GetState() == rtc::SS_CLOSED, 10000); + + // Now check the data + recv_stream_.GetSize(&received); + + EXPECT_EQ(static_cast(size), received); + EXPECT_EQ(0, memcmp(send_stream_.GetBuffer(), + recv_stream_.GetBuffer(), size)); + } + + void WriteData() { + size_t position, tosend, size; + rtc::StreamResult rv; + size_t sent; + char block[kBlockSize]; + + send_stream_.GetSize(&size); + if (!size) + return; + + for (;;) { + send_stream_.GetPosition(&position); + if (send_stream_.Read(block, sizeof(block), &tosend, NULL) != + rtc::SR_EOS) { + rv = client_ssl_->Write(block, tosend, &sent, 0); + + if (rv == rtc::SR_SUCCESS) { + send_stream_.SetPosition(position + sent); + LOG(LS_VERBOSE) << "Sent: " << position + sent; + } else if (rv == rtc::SR_BLOCK) { + LOG(LS_VERBOSE) << "Blocked..."; + send_stream_.SetPosition(position); + break; + } else { + ADD_FAILURE(); + break; + } + } else { + // Now close + LOG(LS_INFO) << "Wrote " << position << " bytes. Closing"; + client_ssl_->Close(); + break; + } + } + }; + + virtual void ReadData(rtc::StreamInterface *stream) { + char buffer[1600]; + size_t bread; + int err2; + rtc::StreamResult r; + + for (;;) { + r = stream->Read(buffer, sizeof(buffer), &bread, &err2); + + if (r == rtc::SR_ERROR || r == rtc::SR_EOS) { + // Unfortunately, errors are the way that the stream adapter + // signals close in OpenSSL + stream->Close(); + return; + } + + if (r == rtc::SR_BLOCK) + break; + + ASSERT_EQ(rtc::SR_SUCCESS, r); + LOG(LS_INFO) << "Read " << bread; + + recv_stream_.Write(buffer, bread, NULL, NULL); + } + } + + private: + rtc::MemoryStream send_stream_; + rtc::MemoryStream recv_stream_; +}; + +class SSLStreamAdapterTestDTLS : public SSLStreamAdapterTestBase { + public: + SSLStreamAdapterTestDTLS() : + SSLStreamAdapterTestBase("", "", true), + packet_size_(1000), count_(0), sent_(0) { + } + + SSLStreamAdapterTestDTLS(const std::string& cert_pem, + const std::string& private_key_pem) : + SSLStreamAdapterTestBase(cert_pem, private_key_pem, true), + packet_size_(1000), count_(0), sent_(0) { + } + + virtual void WriteData() { + unsigned char *packet = new unsigned char[1600]; + + do { + memset(packet, sent_ & 0xff, packet_size_); + *(reinterpret_cast(packet)) = sent_; + + size_t sent; + int rv = client_ssl_->Write(packet, packet_size_, &sent, 0); + if (rv == rtc::SR_SUCCESS) { + LOG(LS_VERBOSE) << "Sent: " << sent_; + sent_++; + } else if (rv == rtc::SR_BLOCK) { + LOG(LS_VERBOSE) << "Blocked..."; + break; + } else { + ADD_FAILURE(); + break; + } + } while (sent_ < count_); + + delete [] packet; + } + + virtual void ReadData(rtc::StreamInterface *stream) { + unsigned char buffer[2000]; + size_t bread; + int err2; + rtc::StreamResult r; + + for (;;) { + r = stream->Read(buffer, 2000, &bread, &err2); + + if (r == rtc::SR_ERROR) { + // Unfortunately, errors are the way that the stream adapter + // signals close right now + stream->Close(); + return; + } + + if (r == rtc::SR_BLOCK) + break; + + ASSERT_EQ(rtc::SR_SUCCESS, r); + LOG(LS_INFO) << "Read " << bread; + + // Now parse the datagram + ASSERT_EQ(packet_size_, bread); + unsigned char* ptr_to_buffer = buffer; + uint32_t packet_num = *(reinterpret_cast(ptr_to_buffer)); + + for (size_t i = 4; i < packet_size_; i++) { + ASSERT_EQ((packet_num & 0xff), buffer[i]); + } + received_.insert(packet_num); + } + } + + virtual void TestTransfer(int count) { + count_ = count; + + WriteData(); + + EXPECT_TRUE_WAIT(sent_ == count_, 10000); + LOG(LS_INFO) << "sent_ == " << sent_; + + if (damage_) { + WAIT(false, 2000); + EXPECT_EQ(0U, received_.size()); + } else if (loss_ == 0) { + EXPECT_EQ_WAIT(static_cast(sent_), received_.size(), 1000); + } else { + LOG(LS_INFO) << "Sent " << sent_ << " packets; received " << + received_.size(); + } + }; + + private: + size_t packet_size_; + int count_; + int sent_; + std::set received_; +}; + + +rtc::StreamResult SSLDummyStream::Write(const void* data, size_t data_len, + size_t* written, int* error) { + *written = data_len; + + LOG(LS_INFO) << "Writing to loopback " << data_len; + + if (first_packet_) { + first_packet_ = false; + if (test_->GetLoseFirstPacket()) { + LOG(LS_INFO) << "Losing initial packet of length " << data_len; + return rtc::SR_SUCCESS; + } + } + + return test_->DataWritten(this, data, data_len, written, error); + + return rtc::SR_SUCCESS; +}; + +class SSLStreamAdapterTestDTLSFromPEMStrings : public SSLStreamAdapterTestDTLS { + public: + SSLStreamAdapterTestDTLSFromPEMStrings() : + SSLStreamAdapterTestDTLS(kCERT_PEM, kRSA_PRIVATE_KEY_PEM) { + } +}; + +// Basic tests: TLS + +// Test that we cannot read/write if we have not yet handshaked. +// This test only applies to NSS because OpenSSL has passthrough +// semantics for I/O before the handshake is started. +#if SSL_USE_NSS +TEST_F(SSLStreamAdapterTestTLS, TestNoReadWriteBeforeConnect) { + rtc::StreamResult rv; + char block[kBlockSize]; + size_t dummy; + + rv = client_ssl_->Write(block, sizeof(block), &dummy, NULL); + ASSERT_EQ(rtc::SR_BLOCK, rv); + + rv = client_ssl_->Read(block, sizeof(block), &dummy, NULL); + ASSERT_EQ(rtc::SR_BLOCK, rv); +} +#endif + + +// Test that we can make a handshake work +TEST_F(SSLStreamAdapterTestTLS, TestTLSConnect) { + TestHandshake(); +}; + +// Test that closing the connection on one side updates the other side. +TEST_F(SSLStreamAdapterTestTLS, TestTLSClose) { + TestHandshake(); + client_ssl_->Close(); + EXPECT_EQ_WAIT(rtc::SS_CLOSED, server_ssl_->GetState(), handshake_wait_); +}; + +// Test transfer -- trivial +TEST_F(SSLStreamAdapterTestTLS, TestTLSTransfer) { + TestHandshake(); + TestTransfer(100000); +}; + +// Test read-write after close. +TEST_F(SSLStreamAdapterTestTLS, ReadWriteAfterClose) { + TestHandshake(); + TestTransfer(100000); + client_ssl_->Close(); + + rtc::StreamResult rv; + char block[kBlockSize]; + size_t dummy; + + // It's an error to write after closed. + rv = client_ssl_->Write(block, sizeof(block), &dummy, NULL); + ASSERT_EQ(rtc::SR_ERROR, rv); + + // But after closed read gives you EOS. + rv = client_ssl_->Read(block, sizeof(block), &dummy, NULL); + ASSERT_EQ(rtc::SR_EOS, rv); +}; + +// Test a handshake with a bogus peer digest +TEST_F(SSLStreamAdapterTestTLS, TestTLSBogusDigest) { + SetPeerIdentitiesByDigest(false); + TestHandshake(false); +}; + +// Test moving a bunch of data + +// Basic tests: DTLS +// Test that we can make a handshake work +TEST_F(SSLStreamAdapterTestDTLS, TestDTLSConnect) { + MAYBE_SKIP_TEST(HaveDtls); + TestHandshake(); +}; + +// Test that we can make a handshake work if the first packet in +// each direction is lost. This gives us predictable loss +// rather than having to tune random +TEST_F(SSLStreamAdapterTestDTLS, TestDTLSConnectWithLostFirstPacket) { + MAYBE_SKIP_TEST(HaveDtls); + SetLoseFirstPacket(true); + TestHandshake(); +}; + +// Test a handshake with loss and delay +TEST_F(SSLStreamAdapterTestDTLS, + TestDTLSConnectWithLostFirstPacketDelay2s) { + MAYBE_SKIP_TEST(HaveDtls); + SetLoseFirstPacket(true); + SetDelay(2000); + SetHandshakeWait(20000); + TestHandshake(); +}; + +// Test a handshake with small MTU +// Disabled due to https://code.google.com/p/webrtc/issues/detail?id=3910 +TEST_F(SSLStreamAdapterTestDTLS, DISABLED_TestDTLSConnectWithSmallMtu) { + MAYBE_SKIP_TEST(HaveDtls); + SetMtu(700); + SetHandshakeWait(20000); + TestHandshake(); +}; + +// Test transfer -- trivial +TEST_F(SSLStreamAdapterTestDTLS, TestDTLSTransfer) { + MAYBE_SKIP_TEST(HaveDtls); + TestHandshake(); + TestTransfer(100); +}; + +TEST_F(SSLStreamAdapterTestDTLS, TestDTLSTransferWithLoss) { + MAYBE_SKIP_TEST(HaveDtls); + TestHandshake(); + SetLoss(10); + TestTransfer(100); +}; + +TEST_F(SSLStreamAdapterTestDTLS, TestDTLSTransferWithDamage) { + MAYBE_SKIP_TEST(HaveDtls); + SetDamage(); // Must be called first because first packet + // write happens at end of handshake. + TestHandshake(); + TestTransfer(100); +}; + +// Test DTLS-SRTP with all high ciphers +TEST_F(SSLStreamAdapterTestDTLS, TestDTLSSrtpHigh) { + MAYBE_SKIP_TEST(HaveDtlsSrtp); + std::vector high; + high.push_back(kAES_CM_HMAC_SHA1_80); + SetDtlsSrtpCiphers(high, true); + SetDtlsSrtpCiphers(high, false); + TestHandshake(); + + std::string client_cipher; + ASSERT_TRUE(GetDtlsSrtpCipher(true, &client_cipher)); + std::string server_cipher; + ASSERT_TRUE(GetDtlsSrtpCipher(false, &server_cipher)); + + ASSERT_EQ(client_cipher, server_cipher); + ASSERT_EQ(client_cipher, kAES_CM_HMAC_SHA1_80); +}; + +// Test DTLS-SRTP with all low ciphers +TEST_F(SSLStreamAdapterTestDTLS, TestDTLSSrtpLow) { + MAYBE_SKIP_TEST(HaveDtlsSrtp); + std::vector low; + low.push_back(kAES_CM_HMAC_SHA1_32); + SetDtlsSrtpCiphers(low, true); + SetDtlsSrtpCiphers(low, false); + TestHandshake(); + + std::string client_cipher; + ASSERT_TRUE(GetDtlsSrtpCipher(true, &client_cipher)); + std::string server_cipher; + ASSERT_TRUE(GetDtlsSrtpCipher(false, &server_cipher)); + + ASSERT_EQ(client_cipher, server_cipher); + ASSERT_EQ(client_cipher, kAES_CM_HMAC_SHA1_32); +}; + + +// Test DTLS-SRTP with a mismatch -- should not converge +TEST_F(SSLStreamAdapterTestDTLS, TestDTLSSrtpHighLow) { + MAYBE_SKIP_TEST(HaveDtlsSrtp); + std::vector high; + high.push_back(kAES_CM_HMAC_SHA1_80); + std::vector low; + low.push_back(kAES_CM_HMAC_SHA1_32); + SetDtlsSrtpCiphers(high, true); + SetDtlsSrtpCiphers(low, false); + TestHandshake(); + + std::string client_cipher; + ASSERT_FALSE(GetDtlsSrtpCipher(true, &client_cipher)); + std::string server_cipher; + ASSERT_FALSE(GetDtlsSrtpCipher(false, &server_cipher)); +}; + +// Test DTLS-SRTP with each side being mixed -- should select high +TEST_F(SSLStreamAdapterTestDTLS, TestDTLSSrtpMixed) { + MAYBE_SKIP_TEST(HaveDtlsSrtp); + std::vector mixed; + mixed.push_back(kAES_CM_HMAC_SHA1_80); + mixed.push_back(kAES_CM_HMAC_SHA1_32); + SetDtlsSrtpCiphers(mixed, true); + SetDtlsSrtpCiphers(mixed, false); + TestHandshake(); + + std::string client_cipher; + ASSERT_TRUE(GetDtlsSrtpCipher(true, &client_cipher)); + std::string server_cipher; + ASSERT_TRUE(GetDtlsSrtpCipher(false, &server_cipher)); + + ASSERT_EQ(client_cipher, server_cipher); + ASSERT_EQ(client_cipher, kAES_CM_HMAC_SHA1_80); +}; + +// Test an exporter +TEST_F(SSLStreamAdapterTestDTLS, TestDTLSExporter) { + MAYBE_SKIP_TEST(HaveExporter); + TestHandshake(); + unsigned char client_out[20]; + unsigned char server_out[20]; + + bool result; + result = ExportKeyingMaterial(kExporterLabel, + kExporterContext, kExporterContextLen, + true, true, + client_out, sizeof(client_out)); + ASSERT_TRUE(result); + + result = ExportKeyingMaterial(kExporterLabel, + kExporterContext, kExporterContextLen, + true, false, + server_out, sizeof(server_out)); + ASSERT_TRUE(result); + + ASSERT_TRUE(!memcmp(client_out, server_out, sizeof(client_out))); +} + +// Test not yet valid certificates are not rejected. +TEST_F(SSLStreamAdapterTestDTLS, TestCertNotYetValid) { + MAYBE_SKIP_TEST(HaveDtls); + long one_day = 60 * 60 * 24; + // Make the certificates not valid until one day later. + ResetIdentitiesWithValidity(one_day, one_day); + TestHandshake(); +} + +// Test expired certificates are not rejected. +TEST_F(SSLStreamAdapterTestDTLS, TestCertExpired) { + MAYBE_SKIP_TEST(HaveDtls); + long one_day = 60 * 60 * 24; + // Make the certificates already expired. + ResetIdentitiesWithValidity(-one_day, -one_day); + TestHandshake(); +} + +// Test data transfer using certs created from strings. +TEST_F(SSLStreamAdapterTestDTLSFromPEMStrings, TestTransfer) { + MAYBE_SKIP_TEST(HaveDtls); + TestHandshake(); + TestTransfer(100); +} + +// Test getting the remote certificate. +TEST_F(SSLStreamAdapterTestDTLSFromPEMStrings, TestDTLSGetPeerCertificate) { + MAYBE_SKIP_TEST(HaveDtls); + + // Peer certificates haven't been received yet. + rtc::scoped_ptr client_peer_cert; + ASSERT_FALSE(GetPeerCertificate(true, client_peer_cert.accept())); + ASSERT_FALSE(client_peer_cert != NULL); + + rtc::scoped_ptr server_peer_cert; + ASSERT_FALSE(GetPeerCertificate(false, server_peer_cert.accept())); + ASSERT_FALSE(server_peer_cert != NULL); + + TestHandshake(); + + // The client should have a peer certificate after the handshake. + ASSERT_TRUE(GetPeerCertificate(true, client_peer_cert.accept())); + ASSERT_TRUE(client_peer_cert != NULL); + + // It's not kCERT_PEM. + std::string client_peer_string = client_peer_cert->ToPEMString(); + ASSERT_NE(kCERT_PEM, client_peer_string); + + // It must not have a chain, because the test certs are self-signed. + rtc::SSLCertChain* client_peer_chain; + ASSERT_FALSE(client_peer_cert->GetChain(&client_peer_chain)); + + // The server should have a peer certificate after the handshake. + ASSERT_TRUE(GetPeerCertificate(false, server_peer_cert.accept())); + ASSERT_TRUE(server_peer_cert != NULL); + + // It's kCERT_PEM + ASSERT_EQ(kCERT_PEM, server_peer_cert->ToPEMString()); + + // It must not have a chain, because the test certs are self-signed. + rtc::SSLCertChain* server_peer_chain; + ASSERT_FALSE(server_peer_cert->GetChain(&server_peer_chain)); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stream.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stream.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stream.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stream.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,1335 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if defined(WEBRTC_POSIX) +#include +#endif // WEBRTC_POSIX +#include +#include +#include +#include +#include "webrtc/base/basictypes.h" +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/messagequeue.h" +#include "webrtc/base/stream.h" +#include "webrtc/base/stringencode.h" +#include "webrtc/base/stringutils.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/timeutils.h" + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32.h" +#define fileno _fileno +#endif + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// StreamInterface +/////////////////////////////////////////////////////////////////////////////// +StreamInterface::~StreamInterface() { +} + +StreamResult StreamInterface::WriteAll(const void* data, size_t data_len, + size_t* written, int* error) { + StreamResult result = SR_SUCCESS; + size_t total_written = 0, current_written; + while (total_written < data_len) { + result = Write(static_cast(data) + total_written, + data_len - total_written, ¤t_written, error); + if (result != SR_SUCCESS) + break; + total_written += current_written; + } + if (written) + *written = total_written; + return result; +} + +StreamResult StreamInterface::ReadAll(void* buffer, size_t buffer_len, + size_t* read, int* error) { + StreamResult result = SR_SUCCESS; + size_t total_read = 0, current_read; + while (total_read < buffer_len) { + result = Read(static_cast(buffer) + total_read, + buffer_len - total_read, ¤t_read, error); + if (result != SR_SUCCESS) + break; + total_read += current_read; + } + if (read) + *read = total_read; + return result; +} + +StreamResult StreamInterface::ReadLine(std::string* line) { + line->clear(); + StreamResult result = SR_SUCCESS; + while (true) { + char ch; + result = Read(&ch, sizeof(ch), NULL, NULL); + if (result != SR_SUCCESS) { + break; + } + if (ch == '\n') { + break; + } + line->push_back(ch); + } + if (!line->empty()) { // give back the line we've collected so far with + result = SR_SUCCESS; // a success code. Otherwise return the last code + } + return result; +} + +void StreamInterface::PostEvent(Thread* t, int events, int err) { + t->Post(this, MSG_POST_EVENT, new StreamEventData(events, err)); +} + +void StreamInterface::PostEvent(int events, int err) { + PostEvent(Thread::Current(), events, err); +} + +StreamInterface::StreamInterface() { +} + +void StreamInterface::OnMessage(Message* msg) { + if (MSG_POST_EVENT == msg->message_id) { + StreamEventData* pe = static_cast(msg->pdata); + SignalEvent(this, pe->events, pe->error); + delete msg->pdata; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// StreamAdapterInterface +/////////////////////////////////////////////////////////////////////////////// + +StreamAdapterInterface::StreamAdapterInterface(StreamInterface* stream, + bool owned) + : stream_(stream), owned_(owned) { + if (NULL != stream_) + stream_->SignalEvent.connect(this, &StreamAdapterInterface::OnEvent); +} + +void StreamAdapterInterface::Attach(StreamInterface* stream, bool owned) { + if (NULL != stream_) + stream_->SignalEvent.disconnect(this); + if (owned_) + delete stream_; + stream_ = stream; + owned_ = owned; + if (NULL != stream_) + stream_->SignalEvent.connect(this, &StreamAdapterInterface::OnEvent); +} + +StreamInterface* StreamAdapterInterface::Detach() { + if (NULL != stream_) + stream_->SignalEvent.disconnect(this); + StreamInterface* stream = stream_; + stream_ = NULL; + return stream; +} + +StreamAdapterInterface::~StreamAdapterInterface() { + if (owned_) + delete stream_; +} + +/////////////////////////////////////////////////////////////////////////////// +// StreamTap +/////////////////////////////////////////////////////////////////////////////// + +StreamTap::StreamTap(StreamInterface* stream, StreamInterface* tap) + : StreamAdapterInterface(stream), tap_(), tap_result_(SR_SUCCESS), + tap_error_(0) { + AttachTap(tap); +} + +void StreamTap::AttachTap(StreamInterface* tap) { + tap_.reset(tap); +} + +StreamInterface* StreamTap::DetachTap() { + return tap_.release(); +} + +StreamResult StreamTap::GetTapResult(int* error) { + if (error) { + *error = tap_error_; + } + return tap_result_; +} + +StreamResult StreamTap::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + size_t backup_read; + if (!read) { + read = &backup_read; + } + StreamResult res = StreamAdapterInterface::Read(buffer, buffer_len, + read, error); + if ((res == SR_SUCCESS) && (tap_result_ == SR_SUCCESS)) { + tap_result_ = tap_->WriteAll(buffer, *read, NULL, &tap_error_); + } + return res; +} + +StreamResult StreamTap::Write(const void* data, size_t data_len, + size_t* written, int* error) { + size_t backup_written; + if (!written) { + written = &backup_written; + } + StreamResult res = StreamAdapterInterface::Write(data, data_len, + written, error); + if ((res == SR_SUCCESS) && (tap_result_ == SR_SUCCESS)) { + tap_result_ = tap_->WriteAll(data, *written, NULL, &tap_error_); + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// +// StreamSegment +/////////////////////////////////////////////////////////////////////////////// + +StreamSegment::StreamSegment(StreamInterface* stream) + : StreamAdapterInterface(stream), start_(SIZE_UNKNOWN), pos_(0), + length_(SIZE_UNKNOWN) { + // It's ok for this to fail, in which case start_ is left as SIZE_UNKNOWN. + stream->GetPosition(&start_); +} + +StreamSegment::StreamSegment(StreamInterface* stream, size_t length) + : StreamAdapterInterface(stream), start_(SIZE_UNKNOWN), pos_(0), + length_(length) { + // It's ok for this to fail, in which case start_ is left as SIZE_UNKNOWN. + stream->GetPosition(&start_); +} + +StreamResult StreamSegment::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + if (SIZE_UNKNOWN != length_) { + if (pos_ >= length_) + return SR_EOS; + buffer_len = _min(buffer_len, length_ - pos_); + } + size_t backup_read; + if (!read) { + read = &backup_read; + } + StreamResult result = StreamAdapterInterface::Read(buffer, buffer_len, + read, error); + if (SR_SUCCESS == result) { + pos_ += *read; + } + return result; +} + +bool StreamSegment::SetPosition(size_t position) { + if (SIZE_UNKNOWN == start_) + return false; // Not seekable + if ((SIZE_UNKNOWN != length_) && (position > length_)) + return false; // Seek past end of segment + if (!StreamAdapterInterface::SetPosition(start_ + position)) + return false; + pos_ = position; + return true; +} + +bool StreamSegment::GetPosition(size_t* position) const { + if (SIZE_UNKNOWN == start_) + return false; // Not seekable + if (!StreamAdapterInterface::GetPosition(position)) + return false; + if (position) { + ASSERT(*position >= start_); + *position -= start_; + } + return true; +} + +bool StreamSegment::GetSize(size_t* size) const { + if (!StreamAdapterInterface::GetSize(size)) + return false; + if (size) { + if (SIZE_UNKNOWN != start_) { + ASSERT(*size >= start_); + *size -= start_; + } + if (SIZE_UNKNOWN != length_) { + *size = _min(*size, length_); + } + } + return true; +} + +bool StreamSegment::GetAvailable(size_t* size) const { + if (!StreamAdapterInterface::GetAvailable(size)) + return false; + if (size && (SIZE_UNKNOWN != length_)) + *size = _min(*size, length_ - pos_); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// NullStream +/////////////////////////////////////////////////////////////////////////////// + +NullStream::NullStream() { +} + +NullStream::~NullStream() { +} + +StreamState NullStream::GetState() const { + return SS_OPEN; +} + +StreamResult NullStream::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + if (error) *error = -1; + return SR_ERROR; +} + +StreamResult NullStream::Write(const void* data, size_t data_len, + size_t* written, int* error) { + if (written) *written = data_len; + return SR_SUCCESS; +} + +void NullStream::Close() { +} + +/////////////////////////////////////////////////////////////////////////////// +// FileStream +/////////////////////////////////////////////////////////////////////////////// + +FileStream::FileStream() : file_(NULL) { +} + +FileStream::~FileStream() { + FileStream::Close(); +} + +bool FileStream::Open(const std::string& filename, const char* mode, + int* error) { + Close(); +#if defined(WEBRTC_WIN) + std::wstring wfilename; + if (Utf8ToWindowsFilename(filename, &wfilename)) { + file_ = _wfopen(wfilename.c_str(), ToUtf16(mode).c_str()); + } else { + if (error) { + *error = -1; + return false; + } + } +#else + file_ = fopen(filename.c_str(), mode); +#endif + if (!file_ && error) { + *error = errno; + } + return (file_ != NULL); +} + +bool FileStream::OpenShare(const std::string& filename, const char* mode, + int shflag, int* error) { + Close(); +#if defined(WEBRTC_WIN) + std::wstring wfilename; + if (Utf8ToWindowsFilename(filename, &wfilename)) { + file_ = _wfsopen(wfilename.c_str(), ToUtf16(mode).c_str(), shflag); + if (!file_ && error) { + *error = errno; + return false; + } + return file_ != NULL; + } else { + if (error) { + *error = -1; + } + return false; + } +#else + return Open(filename, mode, error); +#endif +} + +bool FileStream::DisableBuffering() { + if (!file_) + return false; + return (setvbuf(file_, NULL, _IONBF, 0) == 0); +} + +StreamState FileStream::GetState() const { + return (file_ == NULL) ? SS_CLOSED : SS_OPEN; +} + +StreamResult FileStream::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + if (!file_) + return SR_EOS; + size_t result = fread(buffer, 1, buffer_len, file_); + if ((result == 0) && (buffer_len > 0)) { + if (feof(file_)) + return SR_EOS; + if (error) + *error = errno; + return SR_ERROR; + } + if (read) + *read = result; + return SR_SUCCESS; +} + +StreamResult FileStream::Write(const void* data, size_t data_len, + size_t* written, int* error) { + if (!file_) + return SR_EOS; + size_t result = fwrite(data, 1, data_len, file_); + if ((result == 0) && (data_len > 0)) { + if (error) + *error = errno; + return SR_ERROR; + } + if (written) + *written = result; + return SR_SUCCESS; +} + +void FileStream::Close() { + if (file_) { + DoClose(); + file_ = NULL; + } +} + +bool FileStream::SetPosition(size_t position) { + if (!file_) + return false; + return (fseek(file_, static_cast(position), SEEK_SET) == 0); +} + +bool FileStream::GetPosition(size_t* position) const { + ASSERT(NULL != position); + if (!file_) + return false; + long result = ftell(file_); + if (result < 0) + return false; + if (position) + *position = result; + return true; +} + +bool FileStream::GetSize(size_t* size) const { + ASSERT(NULL != size); + if (!file_) + return false; + struct stat file_stats; + if (fstat(fileno(file_), &file_stats) != 0) + return false; + if (size) + *size = file_stats.st_size; + return true; +} + +bool FileStream::GetAvailable(size_t* size) const { + ASSERT(NULL != size); + if (!GetSize(size)) + return false; + long result = ftell(file_); + if (result < 0) + return false; + if (size) + *size -= result; + return true; +} + +bool FileStream::ReserveSize(size_t size) { + // TODO: extend the file to the proper length + return true; +} + +bool FileStream::GetSize(const std::string& filename, size_t* size) { + struct stat file_stats; + if (stat(filename.c_str(), &file_stats) != 0) + return false; + *size = file_stats.st_size; + return true; +} + +bool FileStream::Flush() { + if (file_) { + return (0 == fflush(file_)); + } + // try to flush empty file? + ASSERT(false); + return false; +} + +#if defined(WEBRTC_POSIX) && !defined(__native_client__) + +bool FileStream::TryLock() { + if (file_ == NULL) { + // Stream not open. + ASSERT(false); + return false; + } + + return flock(fileno(file_), LOCK_EX|LOCK_NB) == 0; +} + +bool FileStream::Unlock() { + if (file_ == NULL) { + // Stream not open. + ASSERT(false); + return false; + } + + return flock(fileno(file_), LOCK_UN) == 0; +} + +#endif + +void FileStream::DoClose() { + fclose(file_); +} + +CircularFileStream::CircularFileStream(size_t max_size) + : max_write_size_(max_size), + position_(0), + marked_position_(max_size / 2), + last_write_position_(0), + read_segment_(READ_LATEST), + read_segment_available_(0) { +} + +bool CircularFileStream::Open( + const std::string& filename, const char* mode, int* error) { + if (!FileStream::Open(filename.c_str(), mode, error)) + return false; + + if (strchr(mode, "r") != NULL) { // Opened in read mode. + // Check if the buffer has been overwritten and determine how to read the + // log in time sequence. + size_t file_size; + GetSize(&file_size); + if (file_size == position_) { + // The buffer has not been overwritten yet. Read 0 .. file_size + read_segment_ = READ_LATEST; + read_segment_available_ = file_size; + } else { + // The buffer has been over written. There are three segments: The first + // one is 0 .. marked_position_, which is the marked earliest log. The + // second one is position_ .. file_size, which is the middle log. The + // last one is marked_position_ .. position_, which is the latest log. + read_segment_ = READ_MARKED; + read_segment_available_ = marked_position_; + last_write_position_ = position_; + } + + // Read from the beginning. + position_ = 0; + SetPosition(position_); + } + + return true; +} + +StreamResult CircularFileStream::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + if (read_segment_available_ == 0) { + size_t file_size; + switch (read_segment_) { + case READ_MARKED: // Finished READ_MARKED and start READ_MIDDLE. + read_segment_ = READ_MIDDLE; + position_ = last_write_position_; + SetPosition(position_); + GetSize(&file_size); + read_segment_available_ = file_size - position_; + break; + + case READ_MIDDLE: // Finished READ_MIDDLE and start READ_LATEST. + read_segment_ = READ_LATEST; + position_ = marked_position_; + SetPosition(position_); + read_segment_available_ = last_write_position_ - position_; + break; + + default: // Finished READ_LATEST and return EOS. + return rtc::SR_EOS; + } + } + + size_t local_read; + if (!read) read = &local_read; + + size_t to_read = rtc::_min(buffer_len, read_segment_available_); + rtc::StreamResult result + = rtc::FileStream::Read(buffer, to_read, read, error); + if (result == rtc::SR_SUCCESS) { + read_segment_available_ -= *read; + position_ += *read; + } + return result; +} + +StreamResult CircularFileStream::Write(const void* data, size_t data_len, + size_t* written, int* error) { + if (position_ >= max_write_size_) { + ASSERT(position_ == max_write_size_); + position_ = marked_position_; + SetPosition(position_); + } + + size_t local_written; + if (!written) written = &local_written; + + size_t to_eof = max_write_size_ - position_; + size_t to_write = rtc::_min(data_len, to_eof); + rtc::StreamResult result + = rtc::FileStream::Write(data, to_write, written, error); + if (result == rtc::SR_SUCCESS) { + position_ += *written; + } + return result; +} + +AsyncWriteStream::~AsyncWriteStream() { + write_thread_->Clear(this, 0, NULL); + ClearBufferAndWrite(); + + CritScope cs(&crit_stream_); + stream_.reset(); +} + +// This is needed by some stream writers, such as RtpDumpWriter. +bool AsyncWriteStream::GetPosition(size_t* position) const { + CritScope cs(&crit_stream_); + return stream_->GetPosition(position); +} + +// This is needed by some stream writers, such as the plugin log writers. +StreamResult AsyncWriteStream::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + CritScope cs(&crit_stream_); + return stream_->Read(buffer, buffer_len, read, error); +} + +void AsyncWriteStream::Close() { + if (state_ == SS_CLOSED) { + return; + } + + write_thread_->Clear(this, 0, NULL); + ClearBufferAndWrite(); + + CritScope cs(&crit_stream_); + stream_->Close(); + state_ = SS_CLOSED; +} + +StreamResult AsyncWriteStream::Write(const void* data, size_t data_len, + size_t* written, int* error) { + if (state_ == SS_CLOSED) { + return SR_ERROR; + } + + size_t previous_buffer_length = 0; + { + CritScope cs(&crit_buffer_); + previous_buffer_length = buffer_.length(); + buffer_.AppendData(data, data_len); + } + + if (previous_buffer_length == 0) { + // If there's stuff already in the buffer, then we already called + // Post and the write_thread_ hasn't pulled it out yet, so we + // don't need to re-Post. + write_thread_->Post(this, 0, NULL); + } + // Return immediately, assuming that it works. + if (written) { + *written = data_len; + } + return SR_SUCCESS; +} + +void AsyncWriteStream::OnMessage(rtc::Message* pmsg) { + ClearBufferAndWrite(); +} + +bool AsyncWriteStream::Flush() { + if (state_ == SS_CLOSED) { + return false; + } + + ClearBufferAndWrite(); + + CritScope cs(&crit_stream_); + return stream_->Flush(); +} + +void AsyncWriteStream::ClearBufferAndWrite() { + Buffer to_write; + { + CritScope cs_buffer(&crit_buffer_); + buffer_.TransferTo(&to_write); + } + + if (to_write.length() > 0) { + CritScope cs(&crit_stream_); + stream_->WriteAll(to_write.data(), to_write.length(), NULL, NULL); + } +} + +#if defined(WEBRTC_POSIX) && !defined(__native_client__) + +// Have to identically rewrite the FileStream destructor or else it would call +// the base class's Close() instead of the sub-class's. +POpenStream::~POpenStream() { + POpenStream::Close(); +} + +bool POpenStream::Open(const std::string& subcommand, + const char* mode, + int* error) { + Close(); + file_ = popen(subcommand.c_str(), mode); + if (file_ == NULL) { + if (error) + *error = errno; + return false; + } + return true; +} + +bool POpenStream::OpenShare(const std::string& subcommand, const char* mode, + int shflag, int* error) { + return Open(subcommand, mode, error); +} + +void POpenStream::DoClose() { + wait_status_ = pclose(file_); +} + +#endif + +/////////////////////////////////////////////////////////////////////////////// +// MemoryStream +/////////////////////////////////////////////////////////////////////////////// + +MemoryStreamBase::MemoryStreamBase() + : buffer_(NULL), buffer_length_(0), data_length_(0), + seek_position_(0) { +} + +StreamState MemoryStreamBase::GetState() const { + return SS_OPEN; +} + +StreamResult MemoryStreamBase::Read(void* buffer, size_t bytes, + size_t* bytes_read, int* error) { + if (seek_position_ >= data_length_) { + return SR_EOS; + } + size_t available = data_length_ - seek_position_; + if (bytes > available) { + // Read partial buffer + bytes = available; + } + memcpy(buffer, &buffer_[seek_position_], bytes); + seek_position_ += bytes; + if (bytes_read) { + *bytes_read = bytes; + } + return SR_SUCCESS; +} + +StreamResult MemoryStreamBase::Write(const void* buffer, size_t bytes, + size_t* bytes_written, int* error) { + size_t available = buffer_length_ - seek_position_; + if (0 == available) { + // Increase buffer size to the larger of: + // a) new position rounded up to next 256 bytes + // b) double the previous length + size_t new_buffer_length = _max(((seek_position_ + bytes) | 0xFF) + 1, + buffer_length_ * 2); + StreamResult result = DoReserve(new_buffer_length, error); + if (SR_SUCCESS != result) { + return result; + } + ASSERT(buffer_length_ >= new_buffer_length); + available = buffer_length_ - seek_position_; + } + + if (bytes > available) { + bytes = available; + } + memcpy(&buffer_[seek_position_], buffer, bytes); + seek_position_ += bytes; + if (data_length_ < seek_position_) { + data_length_ = seek_position_; + } + if (bytes_written) { + *bytes_written = bytes; + } + return SR_SUCCESS; +} + +void MemoryStreamBase::Close() { + // nothing to do +} + +bool MemoryStreamBase::SetPosition(size_t position) { + if (position > data_length_) + return false; + seek_position_ = position; + return true; +} + +bool MemoryStreamBase::GetPosition(size_t* position) const { + if (position) + *position = seek_position_; + return true; +} + +bool MemoryStreamBase::GetSize(size_t* size) const { + if (size) + *size = data_length_; + return true; +} + +bool MemoryStreamBase::GetAvailable(size_t* size) const { + if (size) + *size = data_length_ - seek_position_; + return true; +} + +bool MemoryStreamBase::ReserveSize(size_t size) { + return (SR_SUCCESS == DoReserve(size, NULL)); +} + +StreamResult MemoryStreamBase::DoReserve(size_t size, int* error) { + return (buffer_length_ >= size) ? SR_SUCCESS : SR_EOS; +} + +/////////////////////////////////////////////////////////////////////////////// + +MemoryStream::MemoryStream() + : buffer_alloc_(NULL) { +} + +MemoryStream::MemoryStream(const char* data) + : buffer_alloc_(NULL) { + SetData(data, strlen(data)); +} + +MemoryStream::MemoryStream(const void* data, size_t length) + : buffer_alloc_(NULL) { + SetData(data, length); +} + +MemoryStream::~MemoryStream() { + delete [] buffer_alloc_; +} + +void MemoryStream::SetData(const void* data, size_t length) { + data_length_ = buffer_length_ = length; + delete [] buffer_alloc_; + buffer_alloc_ = new char[buffer_length_ + kAlignment]; + buffer_ = reinterpret_cast(ALIGNP(buffer_alloc_, kAlignment)); + memcpy(buffer_, data, data_length_); + seek_position_ = 0; +} + +StreamResult MemoryStream::DoReserve(size_t size, int* error) { + if (buffer_length_ >= size) + return SR_SUCCESS; + + if (char* new_buffer_alloc = new char[size + kAlignment]) { + char* new_buffer = reinterpret_cast( + ALIGNP(new_buffer_alloc, kAlignment)); + memcpy(new_buffer, buffer_, data_length_); + delete [] buffer_alloc_; + buffer_alloc_ = new_buffer_alloc; + buffer_ = new_buffer; + buffer_length_ = size; + return SR_SUCCESS; + } + + if (error) { + *error = ENOMEM; + } + return SR_ERROR; +} + +/////////////////////////////////////////////////////////////////////////////// + +ExternalMemoryStream::ExternalMemoryStream() { +} + +ExternalMemoryStream::ExternalMemoryStream(void* data, size_t length) { + SetData(data, length); +} + +ExternalMemoryStream::~ExternalMemoryStream() { +} + +void ExternalMemoryStream::SetData(void* data, size_t length) { + data_length_ = buffer_length_ = length; + buffer_ = static_cast(data); + seek_position_ = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// FifoBuffer +/////////////////////////////////////////////////////////////////////////////// + +FifoBuffer::FifoBuffer(size_t size) + : state_(SS_OPEN), buffer_(new char[size]), buffer_length_(size), + data_length_(0), read_position_(0), owner_(Thread::Current()) { + // all events are done on the owner_ thread +} + +FifoBuffer::FifoBuffer(size_t size, Thread* owner) + : state_(SS_OPEN), buffer_(new char[size]), buffer_length_(size), + data_length_(0), read_position_(0), owner_(owner) { + // all events are done on the owner_ thread +} + +FifoBuffer::~FifoBuffer() { +} + +bool FifoBuffer::GetBuffered(size_t* size) const { + CritScope cs(&crit_); + *size = data_length_; + return true; +} + +bool FifoBuffer::SetCapacity(size_t size) { + CritScope cs(&crit_); + if (data_length_ > size) { + return false; + } + + if (size != buffer_length_) { + char* buffer = new char[size]; + const size_t copy = data_length_; + const size_t tail_copy = _min(copy, buffer_length_ - read_position_); + memcpy(buffer, &buffer_[read_position_], tail_copy); + memcpy(buffer + tail_copy, &buffer_[0], copy - tail_copy); + buffer_.reset(buffer); + read_position_ = 0; + buffer_length_ = size; + } + return true; +} + +StreamResult FifoBuffer::ReadOffset(void* buffer, size_t bytes, + size_t offset, size_t* bytes_read) { + CritScope cs(&crit_); + return ReadOffsetLocked(buffer, bytes, offset, bytes_read); +} + +StreamResult FifoBuffer::WriteOffset(const void* buffer, size_t bytes, + size_t offset, size_t* bytes_written) { + CritScope cs(&crit_); + return WriteOffsetLocked(buffer, bytes, offset, bytes_written); +} + +StreamState FifoBuffer::GetState() const { + return state_; +} + +StreamResult FifoBuffer::Read(void* buffer, size_t bytes, + size_t* bytes_read, int* error) { + CritScope cs(&crit_); + const bool was_writable = data_length_ < buffer_length_; + size_t copy = 0; + StreamResult result = ReadOffsetLocked(buffer, bytes, 0, ©); + + if (result == SR_SUCCESS) { + // If read was successful then adjust the read position and number of + // bytes buffered. + read_position_ = (read_position_ + copy) % buffer_length_; + data_length_ -= copy; + if (bytes_read) { + *bytes_read = copy; + } + + // if we were full before, and now we're not, post an event + if (!was_writable && copy > 0) { + PostEvent(owner_, SE_WRITE, 0); + } + } + return result; +} + +StreamResult FifoBuffer::Write(const void* buffer, size_t bytes, + size_t* bytes_written, int* error) { + CritScope cs(&crit_); + + const bool was_readable = (data_length_ > 0); + size_t copy = 0; + StreamResult result = WriteOffsetLocked(buffer, bytes, 0, ©); + + if (result == SR_SUCCESS) { + // If write was successful then adjust the number of readable bytes. + data_length_ += copy; + if (bytes_written) { + *bytes_written = copy; + } + + // if we didn't have any data to read before, and now we do, post an event + if (!was_readable && copy > 0) { + PostEvent(owner_, SE_READ, 0); + } + } + return result; +} + +void FifoBuffer::Close() { + CritScope cs(&crit_); + state_ = SS_CLOSED; +} + +const void* FifoBuffer::GetReadData(size_t* size) { + CritScope cs(&crit_); + *size = (read_position_ + data_length_ <= buffer_length_) ? + data_length_ : buffer_length_ - read_position_; + return &buffer_[read_position_]; +} + +void FifoBuffer::ConsumeReadData(size_t size) { + CritScope cs(&crit_); + ASSERT(size <= data_length_); + const bool was_writable = data_length_ < buffer_length_; + read_position_ = (read_position_ + size) % buffer_length_; + data_length_ -= size; + if (!was_writable && size > 0) { + PostEvent(owner_, SE_WRITE, 0); + } +} + +void* FifoBuffer::GetWriteBuffer(size_t* size) { + CritScope cs(&crit_); + if (state_ == SS_CLOSED) { + return NULL; + } + + // if empty, reset the write position to the beginning, so we can get + // the biggest possible block + if (data_length_ == 0) { + read_position_ = 0; + } + + const size_t write_position = (read_position_ + data_length_) + % buffer_length_; + *size = (write_position > read_position_ || data_length_ == 0) ? + buffer_length_ - write_position : read_position_ - write_position; + return &buffer_[write_position]; +} + +void FifoBuffer::ConsumeWriteBuffer(size_t size) { + CritScope cs(&crit_); + ASSERT(size <= buffer_length_ - data_length_); + const bool was_readable = (data_length_ > 0); + data_length_ += size; + if (!was_readable && size > 0) { + PostEvent(owner_, SE_READ, 0); + } +} + +bool FifoBuffer::GetWriteRemaining(size_t* size) const { + CritScope cs(&crit_); + *size = buffer_length_ - data_length_; + return true; +} + +StreamResult FifoBuffer::ReadOffsetLocked(void* buffer, + size_t bytes, + size_t offset, + size_t* bytes_read) { + if (offset >= data_length_) { + return (state_ != SS_CLOSED) ? SR_BLOCK : SR_EOS; + } + + const size_t available = data_length_ - offset; + const size_t read_position = (read_position_ + offset) % buffer_length_; + const size_t copy = _min(bytes, available); + const size_t tail_copy = _min(copy, buffer_length_ - read_position); + char* const p = static_cast(buffer); + memcpy(p, &buffer_[read_position], tail_copy); + memcpy(p + tail_copy, &buffer_[0], copy - tail_copy); + + if (bytes_read) { + *bytes_read = copy; + } + return SR_SUCCESS; +} + +StreamResult FifoBuffer::WriteOffsetLocked(const void* buffer, + size_t bytes, + size_t offset, + size_t* bytes_written) { + if (state_ == SS_CLOSED) { + return SR_EOS; + } + + if (data_length_ + offset >= buffer_length_) { + return SR_BLOCK; + } + + const size_t available = buffer_length_ - data_length_ - offset; + const size_t write_position = (read_position_ + data_length_ + offset) + % buffer_length_; + const size_t copy = _min(bytes, available); + const size_t tail_copy = _min(copy, buffer_length_ - write_position); + const char* const p = static_cast(buffer); + memcpy(&buffer_[write_position], p, tail_copy); + memcpy(&buffer_[0], p + tail_copy, copy - tail_copy); + + if (bytes_written) { + *bytes_written = copy; + } + return SR_SUCCESS; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// LoggingAdapter +/////////////////////////////////////////////////////////////////////////////// + +LoggingAdapter::LoggingAdapter(StreamInterface* stream, LoggingSeverity level, + const std::string& label, bool hex_mode) + : StreamAdapterInterface(stream), level_(level), hex_mode_(hex_mode) { + set_label(label); +} + +void LoggingAdapter::set_label(const std::string& label) { + label_.assign("["); + label_.append(label); + label_.append("]"); +} + +StreamResult LoggingAdapter::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + size_t local_read; if (!read) read = &local_read; + StreamResult result = StreamAdapterInterface::Read(buffer, buffer_len, read, + error); + if (result == SR_SUCCESS) { + LogMultiline(level_, label_.c_str(), true, buffer, *read, hex_mode_, &lms_); + } + return result; +} + +StreamResult LoggingAdapter::Write(const void* data, size_t data_len, + size_t* written, int* error) { + size_t local_written; + if (!written) written = &local_written; + StreamResult result = StreamAdapterInterface::Write(data, data_len, written, + error); + if (result == SR_SUCCESS) { + LogMultiline(level_, label_.c_str(), false, data, *written, hex_mode_, + &lms_); + } + return result; +} + +void LoggingAdapter::Close() { + LogMultiline(level_, label_.c_str(), false, NULL, 0, hex_mode_, &lms_); + LogMultiline(level_, label_.c_str(), true, NULL, 0, hex_mode_, &lms_); + LOG_V(level_) << label_ << " Closed locally"; + StreamAdapterInterface::Close(); +} + +void LoggingAdapter::OnEvent(StreamInterface* stream, int events, int err) { + if (events & SE_OPEN) { + LOG_V(level_) << label_ << " Open"; + } else if (events & SE_CLOSE) { + LogMultiline(level_, label_.c_str(), false, NULL, 0, hex_mode_, &lms_); + LogMultiline(level_, label_.c_str(), true, NULL, 0, hex_mode_, &lms_); + LOG_V(level_) << label_ << " Closed with error: " << err; + } + StreamAdapterInterface::OnEvent(stream, events, err); +} + +/////////////////////////////////////////////////////////////////////////////// +// StringStream - Reads/Writes to an external std::string +/////////////////////////////////////////////////////////////////////////////// + +StringStream::StringStream(std::string& str) + : str_(str), read_pos_(0), read_only_(false) { +} + +StringStream::StringStream(const std::string& str) + : str_(const_cast(str)), read_pos_(0), read_only_(true) { +} + +StreamState StringStream::GetState() const { + return SS_OPEN; +} + +StreamResult StringStream::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + size_t available = _min(buffer_len, str_.size() - read_pos_); + if (!available) + return SR_EOS; + memcpy(buffer, str_.data() + read_pos_, available); + read_pos_ += available; + if (read) + *read = available; + return SR_SUCCESS; +} + +StreamResult StringStream::Write(const void* data, size_t data_len, + size_t* written, int* error) { + if (read_only_) { + if (error) { + *error = -1; + } + return SR_ERROR; + } + str_.append(static_cast(data), + static_cast(data) + data_len); + if (written) + *written = data_len; + return SR_SUCCESS; +} + +void StringStream::Close() { +} + +bool StringStream::SetPosition(size_t position) { + if (position > str_.size()) + return false; + read_pos_ = position; + return true; +} + +bool StringStream::GetPosition(size_t* position) const { + if (position) + *position = read_pos_; + return true; +} + +bool StringStream::GetSize(size_t* size) const { + if (size) + *size = str_.size(); + return true; +} + +bool StringStream::GetAvailable(size_t* size) const { + if (size) + *size = str_.size() - read_pos_; + return true; +} + +bool StringStream::ReserveSize(size_t size) { + if (read_only_) + return false; + str_.reserve(size); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// StreamReference +/////////////////////////////////////////////////////////////////////////////// + +StreamReference::StreamReference(StreamInterface* stream) + : StreamAdapterInterface(stream, false) { + // owner set to false so the destructor does not free the stream. + stream_ref_count_ = new StreamRefCount(stream); +} + +StreamInterface* StreamReference::NewReference() { + stream_ref_count_->AddReference(); + return new StreamReference(stream_ref_count_, stream()); +} + +StreamReference::~StreamReference() { + stream_ref_count_->Release(); +} + +StreamReference::StreamReference(StreamRefCount* stream_ref_count, + StreamInterface* stream) + : StreamAdapterInterface(stream, false), + stream_ref_count_(stream_ref_count) { +} + +/////////////////////////////////////////////////////////////////////////////// + +StreamResult Flow(StreamInterface* source, + char* buffer, size_t buffer_len, + StreamInterface* sink, + size_t* data_len /* = NULL */) { + ASSERT(buffer_len > 0); + + StreamResult result; + size_t count, read_pos, write_pos; + if (data_len) { + read_pos = *data_len; + } else { + read_pos = 0; + } + + bool end_of_stream = false; + do { + // Read until buffer is full, end of stream, or error + while (!end_of_stream && (read_pos < buffer_len)) { + result = source->Read(buffer + read_pos, buffer_len - read_pos, + &count, NULL); + if (result == SR_EOS) { + end_of_stream = true; + } else if (result != SR_SUCCESS) { + if (data_len) { + *data_len = read_pos; + } + return result; + } else { + read_pos += count; + } + } + + // Write until buffer is empty, or error (including end of stream) + write_pos = 0; + while (write_pos < read_pos) { + result = sink->Write(buffer + write_pos, read_pos - write_pos, + &count, NULL); + if (result != SR_SUCCESS) { + if (data_len) { + *data_len = read_pos - write_pos; + if (write_pos > 0) { + memmove(buffer, buffer + write_pos, *data_len); + } + } + return result; + } + write_pos += count; + } + + read_pos = 0; + } while (!end_of_stream); + + if (data_len) { + *data_len = 0; + } + return SR_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stream.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stream.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stream.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stream.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,820 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_STREAM_H_ +#define WEBRTC_BASE_STREAM_H_ + +#include + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/buffer.h" +#include "webrtc/base/criticalsection.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/messagehandler.h" +#include "webrtc/base/messagequeue.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/sigslot.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// StreamInterface is a generic asynchronous stream interface, supporting read, +// write, and close operations, and asynchronous signalling of state changes. +// The interface is designed with file, memory, and socket implementations in +// mind. Some implementations offer extended operations, such as seeking. +/////////////////////////////////////////////////////////////////////////////// + +// The following enumerations are declared outside of the StreamInterface +// class for brevity in use. + +// The SS_OPENING state indicates that the stream will signal open or closed +// in the future. +enum StreamState { SS_CLOSED, SS_OPENING, SS_OPEN }; + +// Stream read/write methods return this value to indicate various success +// and failure conditions described below. +enum StreamResult { SR_ERROR, SR_SUCCESS, SR_BLOCK, SR_EOS }; + +// StreamEvents are used to asynchronously signal state transitionss. The flags +// may be combined. +// SE_OPEN: The stream has transitioned to the SS_OPEN state +// SE_CLOSE: The stream has transitioned to the SS_CLOSED state +// SE_READ: Data is available, so Read is likely to not return SR_BLOCK +// SE_WRITE: Data can be written, so Write is likely to not return SR_BLOCK +enum StreamEvent { SE_OPEN = 1, SE_READ = 2, SE_WRITE = 4, SE_CLOSE = 8 }; + +class Thread; + +struct StreamEventData : public MessageData { + int events, error; + StreamEventData(int ev, int er) : events(ev), error(er) { } +}; + +class StreamInterface : public MessageHandler { + public: + enum { + MSG_POST_EVENT = 0xF1F1, MSG_MAX = MSG_POST_EVENT + }; + + virtual ~StreamInterface(); + + virtual StreamState GetState() const = 0; + + // Read attempts to fill buffer of size buffer_len. Write attempts to send + // data_len bytes stored in data. The variables read and write are set only + // on SR_SUCCESS (see below). Likewise, error is only set on SR_ERROR. + // Read and Write return a value indicating: + // SR_ERROR: an error occurred, which is returned in a non-null error + // argument. Interpretation of the error requires knowledge of the + // stream's concrete type, which limits its usefulness. + // SR_SUCCESS: some number of bytes were successfully written, which is + // returned in a non-null read/write argument. + // SR_BLOCK: the stream is in non-blocking mode, and the operation would + // block, or the stream is in SS_OPENING state. + // SR_EOS: the end-of-stream has been reached, or the stream is in the + // SS_CLOSED state. + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error) = 0; + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error) = 0; + // Attempt to transition to the SS_CLOSED state. SE_CLOSE will not be + // signalled as a result of this call. + virtual void Close() = 0; + + // Streams may signal one or more StreamEvents to indicate state changes. + // The first argument identifies the stream on which the state change occured. + // The second argument is a bit-wise combination of StreamEvents. + // If SE_CLOSE is signalled, then the third argument is the associated error + // code. Otherwise, the value is undefined. + // Note: Not all streams will support asynchronous event signalling. However, + // SS_OPENING and SR_BLOCK returned from stream member functions imply that + // certain events will be raised in the future. + sigslot::signal3 SignalEvent; + + // Like calling SignalEvent, but posts a message to the specified thread, + // which will call SignalEvent. This helps unroll the stack and prevent + // re-entrancy. + void PostEvent(Thread* t, int events, int err); + // Like the aforementioned method, but posts to the current thread. + void PostEvent(int events, int err); + + // + // OPTIONAL OPERATIONS + // + // Not all implementations will support the following operations. In general, + // a stream will only support an operation if it reasonably efficient to do + // so. For example, while a socket could buffer incoming data to support + // seeking, it will not do so. Instead, a buffering stream adapter should + // be used. + // + // Even though several of these operations are related, you should + // always use whichever operation is most relevant. For example, you may + // be tempted to use GetSize() and GetPosition() to deduce the result of + // GetAvailable(). However, a stream which is read-once may support the + // latter operation but not the former. + // + + // The following four methods are used to avoid copying data multiple times. + + // GetReadData returns a pointer to a buffer which is owned by the stream. + // The buffer contains data_len bytes. NULL is returned if no data is + // available, or if the method fails. If the caller processes the data, it + // must call ConsumeReadData with the number of processed bytes. GetReadData + // does not require a matching call to ConsumeReadData if the data is not + // processed. Read and ConsumeReadData invalidate the buffer returned by + // GetReadData. + virtual const void* GetReadData(size_t* data_len) { return NULL; } + virtual void ConsumeReadData(size_t used) {} + + // GetWriteBuffer returns a pointer to a buffer which is owned by the stream. + // The buffer has a capacity of buf_len bytes. NULL is returned if there is + // no buffer available, or if the method fails. The call may write data to + // the buffer, and then call ConsumeWriteBuffer with the number of bytes + // written. GetWriteBuffer does not require a matching call to + // ConsumeWriteData if no data is written. Write, ForceWrite, and + // ConsumeWriteData invalidate the buffer returned by GetWriteBuffer. + // TODO: Allow the caller to specify a minimum buffer size. If the specified + // amount of buffer is not yet available, return NULL and Signal SE_WRITE + // when it is available. If the requested amount is too large, return an + // error. + virtual void* GetWriteBuffer(size_t* buf_len) { return NULL; } + virtual void ConsumeWriteBuffer(size_t used) {} + + // Write data_len bytes found in data, circumventing any throttling which + // would could cause SR_BLOCK to be returned. Returns true if all the data + // was written. Otherwise, the method is unsupported, or an unrecoverable + // error occurred, and the error value is set. This method should be used + // sparingly to write critical data which should not be throttled. A stream + // which cannot circumvent its blocking constraints should not implement this + // method. + // NOTE: This interface is being considered experimentally at the moment. It + // would be used by JUDP and BandwidthStream as a way to circumvent certain + // soft limits in writing. + //virtual bool ForceWrite(const void* data, size_t data_len, int* error) { + // if (error) *error = -1; + // return false; + //} + + // Seek to a byte offset from the beginning of the stream. Returns false if + // the stream does not support seeking, or cannot seek to the specified + // position. + virtual bool SetPosition(size_t position) { return false; } + + // Get the byte offset of the current position from the start of the stream. + // Returns false if the position is not known. + virtual bool GetPosition(size_t* position) const { return false; } + + // Get the byte length of the entire stream. Returns false if the length + // is not known. + virtual bool GetSize(size_t* size) const { return false; } + + // Return the number of Read()-able bytes remaining before end-of-stream. + // Returns false if not known. + virtual bool GetAvailable(size_t* size) const { return false; } + + // Return the number of Write()-able bytes remaining before end-of-stream. + // Returns false if not known. + virtual bool GetWriteRemaining(size_t* size) const { return false; } + + // Return true if flush is successful. + virtual bool Flush() { return false; } + + // Communicates the amount of data which will be written to the stream. The + // stream may choose to preallocate memory to accomodate this data. The + // stream may return false to indicate that there is not enough room (ie, + // Write will return SR_EOS/SR_ERROR at some point). Note that calling this + // function should not affect the existing state of data in the stream. + virtual bool ReserveSize(size_t size) { return true; } + + // + // CONVENIENCE METHODS + // + // These methods are implemented in terms of other methods, for convenience. + // + + // Seek to the start of the stream. + inline bool Rewind() { return SetPosition(0); } + + // WriteAll is a helper function which repeatedly calls Write until all the + // data is written, or something other than SR_SUCCESS is returned. Note that + // unlike Write, the argument 'written' is always set, and may be non-zero + // on results other than SR_SUCCESS. The remaining arguments have the + // same semantics as Write. + StreamResult WriteAll(const void* data, size_t data_len, + size_t* written, int* error); + + // Similar to ReadAll. Calls Read until buffer_len bytes have been read, or + // until a non-SR_SUCCESS result is returned. 'read' is always set. + StreamResult ReadAll(void* buffer, size_t buffer_len, + size_t* read, int* error); + + // ReadLine is a helper function which repeatedly calls Read until it hits + // the end-of-line character, or something other than SR_SUCCESS. + // TODO: this is too inefficient to keep here. Break this out into a buffered + // readline object or adapter + StreamResult ReadLine(std::string* line); + + protected: + StreamInterface(); + + // MessageHandler Interface + virtual void OnMessage(Message* msg); + + private: + DISALLOW_EVIL_CONSTRUCTORS(StreamInterface); +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamAdapterInterface is a convenient base-class for adapting a stream. +// By default, all operations are pass-through. Override the methods that you +// require adaptation. Streams should really be upgraded to reference-counted. +// In the meantime, use the owned flag to indicate whether the adapter should +// own the adapted stream. +/////////////////////////////////////////////////////////////////////////////// + +class StreamAdapterInterface : public StreamInterface, + public sigslot::has_slots<> { + public: + explicit StreamAdapterInterface(StreamInterface* stream, bool owned = true); + + // Core Stream Interface + virtual StreamState GetState() const { + return stream_->GetState(); + } + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + return stream_->Read(buffer, buffer_len, read, error); + } + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error) { + return stream_->Write(data, data_len, written, error); + } + virtual void Close() { + stream_->Close(); + } + + // Optional Stream Interface + /* Note: Many stream adapters were implemented prior to this Read/Write + interface. Therefore, a simple pass through of data in those cases may + be broken. At a later time, we should do a once-over pass of all + adapters, and make them compliant with these interfaces, after which this + code can be uncommented. + virtual const void* GetReadData(size_t* data_len) { + return stream_->GetReadData(data_len); + } + virtual void ConsumeReadData(size_t used) { + stream_->ConsumeReadData(used); + } + + virtual void* GetWriteBuffer(size_t* buf_len) { + return stream_->GetWriteBuffer(buf_len); + } + virtual void ConsumeWriteBuffer(size_t used) { + stream_->ConsumeWriteBuffer(used); + } + */ + + /* Note: This interface is currently undergoing evaluation. + virtual bool ForceWrite(const void* data, size_t data_len, int* error) { + return stream_->ForceWrite(data, data_len, error); + } + */ + + virtual bool SetPosition(size_t position) { + return stream_->SetPosition(position); + } + virtual bool GetPosition(size_t* position) const { + return stream_->GetPosition(position); + } + virtual bool GetSize(size_t* size) const { + return stream_->GetSize(size); + } + virtual bool GetAvailable(size_t* size) const { + return stream_->GetAvailable(size); + } + virtual bool GetWriteRemaining(size_t* size) const { + return stream_->GetWriteRemaining(size); + } + virtual bool ReserveSize(size_t size) { + return stream_->ReserveSize(size); + } + virtual bool Flush() { + return stream_->Flush(); + } + + void Attach(StreamInterface* stream, bool owned = true); + StreamInterface* Detach(); + + protected: + virtual ~StreamAdapterInterface(); + + // Note that the adapter presents itself as the origin of the stream events, + // since users of the adapter may not recognize the adapted object. + virtual void OnEvent(StreamInterface* stream, int events, int err) { + SignalEvent(this, events, err); + } + StreamInterface* stream() { return stream_; } + + private: + StreamInterface* stream_; + bool owned_; + DISALLOW_EVIL_CONSTRUCTORS(StreamAdapterInterface); +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamTap is a non-modifying, pass-through adapter, which copies all data +// in either direction to the tap. Note that errors or blocking on writing to +// the tap will prevent further tap writes from occurring. +/////////////////////////////////////////////////////////////////////////////// + +class StreamTap : public StreamAdapterInterface { + public: + explicit StreamTap(StreamInterface* stream, StreamInterface* tap); + + void AttachTap(StreamInterface* tap); + StreamInterface* DetachTap(); + StreamResult GetTapResult(int* error); + + // StreamAdapterInterface Interface + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + + private: + scoped_ptr tap_; + StreamResult tap_result_; + int tap_error_; + DISALLOW_EVIL_CONSTRUCTORS(StreamTap); +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamSegment adapts a read stream, to expose a subset of the adapted +// stream's data. This is useful for cases where a stream contains multiple +// documents concatenated together. StreamSegment can expose a subset of +// the data as an independent stream, including support for rewinding and +// seeking. +/////////////////////////////////////////////////////////////////////////////// + +class StreamSegment : public StreamAdapterInterface { + public: + // The current position of the adapted stream becomes the beginning of the + // segment. If a length is specified, it bounds the length of the segment. + explicit StreamSegment(StreamInterface* stream); + explicit StreamSegment(StreamInterface* stream, size_t length); + + // StreamAdapterInterface Interface + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual bool SetPosition(size_t position); + virtual bool GetPosition(size_t* position) const; + virtual bool GetSize(size_t* size) const; + virtual bool GetAvailable(size_t* size) const; + + private: + size_t start_, pos_, length_; + DISALLOW_EVIL_CONSTRUCTORS(StreamSegment); +}; + +/////////////////////////////////////////////////////////////////////////////// +// NullStream gives errors on read, and silently discards all written data. +/////////////////////////////////////////////////////////////////////////////// + +class NullStream : public StreamInterface { + public: + NullStream(); + virtual ~NullStream(); + + // StreamInterface Interface + virtual StreamState GetState() const; + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + virtual void Close(); +}; + +/////////////////////////////////////////////////////////////////////////////// +// FileStream is a simple implementation of a StreamInterface, which does not +// support asynchronous notification. +/////////////////////////////////////////////////////////////////////////////// + +class FileStream : public StreamInterface { + public: + FileStream(); + virtual ~FileStream(); + + // The semantics of filename and mode are the same as stdio's fopen + virtual bool Open(const std::string& filename, const char* mode, int* error); + virtual bool OpenShare(const std::string& filename, const char* mode, + int shflag, int* error); + + // By default, reads and writes are buffered for efficiency. Disabling + // buffering causes writes to block until the bytes on disk are updated. + virtual bool DisableBuffering(); + + virtual StreamState GetState() const; + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + virtual void Close(); + virtual bool SetPosition(size_t position); + virtual bool GetPosition(size_t* position) const; + virtual bool GetSize(size_t* size) const; + virtual bool GetAvailable(size_t* size) const; + virtual bool ReserveSize(size_t size); + + virtual bool Flush(); + +#if defined(WEBRTC_POSIX) && !defined(__native_client__) + // Tries to aquire an exclusive lock on the file. + // Use OpenShare(...) on win32 to get similar functionality. + bool TryLock(); + bool Unlock(); +#endif + + // Note: Deprecated in favor of Filesystem::GetFileSize(). + static bool GetSize(const std::string& filename, size_t* size); + + protected: + virtual void DoClose(); + + FILE* file_; + + private: + DISALLOW_EVIL_CONSTRUCTORS(FileStream); +}; + +// A stream that caps the output at a certain size, dropping content from the +// middle of the logical stream and maintaining equal parts of the start/end of +// the logical stream. +class CircularFileStream : public FileStream { + public: + explicit CircularFileStream(size_t max_size); + + virtual bool Open(const std::string& filename, const char* mode, int* error); + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + + private: + enum ReadSegment { + READ_MARKED, // Read 0 .. marked_position_ + READ_MIDDLE, // Read position_ .. file_size + READ_LATEST, // Read marked_position_ .. position_ if the buffer was + // overwritten or 0 .. position_ otherwise. + }; + + size_t max_write_size_; + size_t position_; + size_t marked_position_; + size_t last_write_position_; + ReadSegment read_segment_; + size_t read_segment_available_; +}; + +// A stream which pushes writes onto a separate thread and +// returns from the write call immediately. +class AsyncWriteStream : public StreamInterface { + public: + // Takes ownership of the stream, but not the thread. + AsyncWriteStream(StreamInterface* stream, rtc::Thread* write_thread) + : stream_(stream), + write_thread_(write_thread), + state_(stream ? stream->GetState() : SS_CLOSED) { + } + + virtual ~AsyncWriteStream(); + + // StreamInterface Interface + virtual StreamState GetState() const { return state_; } + // This is needed by some stream writers, such as RtpDumpWriter. + virtual bool GetPosition(size_t* position) const; + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + virtual void Close(); + virtual bool Flush(); + + protected: + // From MessageHandler + virtual void OnMessage(rtc::Message* pmsg); + virtual void ClearBufferAndWrite(); + + private: + rtc::scoped_ptr stream_; + Thread* write_thread_; + StreamState state_; + Buffer buffer_; + mutable CriticalSection crit_stream_; + CriticalSection crit_buffer_; + + DISALLOW_EVIL_CONSTRUCTORS(AsyncWriteStream); +}; + + +#if defined(WEBRTC_POSIX) && !defined(__native_client__) +// A FileStream that is actually not a file, but the output or input of a +// sub-command. See "man 3 popen" for documentation of the underlying OS popen() +// function. +class POpenStream : public FileStream { + public: + POpenStream() : wait_status_(-1) {} + virtual ~POpenStream(); + + virtual bool Open(const std::string& subcommand, const char* mode, + int* error); + // Same as Open(). shflag is ignored. + virtual bool OpenShare(const std::string& subcommand, const char* mode, + int shflag, int* error); + + // Returns the wait status from the last Close() of an Open()'ed stream, or + // -1 if no Open()+Close() has been done on this object. Meaning of the number + // is documented in "man 2 wait". + int GetWaitStatus() const { return wait_status_; } + + protected: + virtual void DoClose(); + + private: + int wait_status_; +}; +#endif // WEBRTC_POSIX + +/////////////////////////////////////////////////////////////////////////////// +// MemoryStream is a simple implementation of a StreamInterface over in-memory +// data. Data is read and written at the current seek position. Reads return +// end-of-stream when they reach the end of data. Writes actually extend the +// end of data mark. +/////////////////////////////////////////////////////////////////////////////// + +class MemoryStreamBase : public StreamInterface { + public: + virtual StreamState GetState() const; + virtual StreamResult Read(void* buffer, size_t bytes, size_t* bytes_read, + int* error); + virtual StreamResult Write(const void* buffer, size_t bytes, + size_t* bytes_written, int* error); + virtual void Close(); + virtual bool SetPosition(size_t position); + virtual bool GetPosition(size_t* position) const; + virtual bool GetSize(size_t* size) const; + virtual bool GetAvailable(size_t* size) const; + virtual bool ReserveSize(size_t size); + + char* GetBuffer() { return buffer_; } + const char* GetBuffer() const { return buffer_; } + + protected: + MemoryStreamBase(); + + virtual StreamResult DoReserve(size_t size, int* error); + + // Invariant: 0 <= seek_position <= data_length_ <= buffer_length_ + char* buffer_; + size_t buffer_length_; + size_t data_length_; + size_t seek_position_; + + private: + DISALLOW_EVIL_CONSTRUCTORS(MemoryStreamBase); +}; + +// MemoryStream dynamically resizes to accomodate written data. + +class MemoryStream : public MemoryStreamBase { + public: + MemoryStream(); + explicit MemoryStream(const char* data); // Calls SetData(data, strlen(data)) + MemoryStream(const void* data, size_t length); // Calls SetData(data, length) + virtual ~MemoryStream(); + + void SetData(const void* data, size_t length); + + protected: + virtual StreamResult DoReserve(size_t size, int* error); + // Memory Streams are aligned for efficiency. + static const int kAlignment = 16; + char* buffer_alloc_; +}; + +// ExternalMemoryStream adapts an external memory buffer, so writes which would +// extend past the end of the buffer will return end-of-stream. + +class ExternalMemoryStream : public MemoryStreamBase { + public: + ExternalMemoryStream(); + ExternalMemoryStream(void* data, size_t length); + virtual ~ExternalMemoryStream(); + + void SetData(void* data, size_t length); +}; + +// FifoBuffer allows for efficient, thread-safe buffering of data between +// writer and reader. As the data can wrap around the end of the buffer, +// MemoryStreamBase can't help us here. + +class FifoBuffer : public StreamInterface { + public: + // Creates a FIFO buffer with the specified capacity. + explicit FifoBuffer(size_t length); + // Creates a FIFO buffer with the specified capacity and owner + FifoBuffer(size_t length, Thread* owner); + virtual ~FifoBuffer(); + // Gets the amount of data currently readable from the buffer. + bool GetBuffered(size_t* data_len) const; + // Resizes the buffer to the specified capacity. Fails if data_length_ > size + bool SetCapacity(size_t length); + + // Read into |buffer| with an offset from the current read position, offset + // is specified in number of bytes. + // This method doesn't adjust read position nor the number of available + // bytes, user has to call ConsumeReadData() to do this. + StreamResult ReadOffset(void* buffer, size_t bytes, size_t offset, + size_t* bytes_read); + + // Write |buffer| with an offset from the current write position, offset is + // specified in number of bytes. + // This method doesn't adjust the number of buffered bytes, user has to call + // ConsumeWriteBuffer() to do this. + StreamResult WriteOffset(const void* buffer, size_t bytes, size_t offset, + size_t* bytes_written); + + // StreamInterface methods + virtual StreamState GetState() const; + virtual StreamResult Read(void* buffer, size_t bytes, + size_t* bytes_read, int* error); + virtual StreamResult Write(const void* buffer, size_t bytes, + size_t* bytes_written, int* error); + virtual void Close(); + virtual const void* GetReadData(size_t* data_len); + virtual void ConsumeReadData(size_t used); + virtual void* GetWriteBuffer(size_t* buf_len); + virtual void ConsumeWriteBuffer(size_t used); + virtual bool GetWriteRemaining(size_t* size) const; + + private: + // Helper method that implements ReadOffset. Caller must acquire a lock + // when calling this method. + StreamResult ReadOffsetLocked(void* buffer, size_t bytes, size_t offset, + size_t* bytes_read); + + // Helper method that implements WriteOffset. Caller must acquire a lock + // when calling this method. + StreamResult WriteOffsetLocked(const void* buffer, size_t bytes, + size_t offset, size_t* bytes_written); + + StreamState state_; // keeps the opened/closed state of the stream + scoped_ptr buffer_; // the allocated buffer + size_t buffer_length_; // size of the allocated buffer + size_t data_length_; // amount of readable data in the buffer + size_t read_position_; // offset to the readable data + Thread* owner_; // stream callbacks are dispatched on this thread + mutable CriticalSection crit_; // object lock + DISALLOW_EVIL_CONSTRUCTORS(FifoBuffer); +}; + +/////////////////////////////////////////////////////////////////////////////// + +class LoggingAdapter : public StreamAdapterInterface { + public: + LoggingAdapter(StreamInterface* stream, LoggingSeverity level, + const std::string& label, bool hex_mode = false); + + void set_label(const std::string& label); + + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + virtual void Close(); + + protected: + virtual void OnEvent(StreamInterface* stream, int events, int err); + + private: + LoggingSeverity level_; + std::string label_; + bool hex_mode_; + LogMultilineState lms_; + + DISALLOW_EVIL_CONSTRUCTORS(LoggingAdapter); +}; + +/////////////////////////////////////////////////////////////////////////////// +// StringStream - Reads/Writes to an external std::string +/////////////////////////////////////////////////////////////////////////////// + +class StringStream : public StreamInterface { + public: + explicit StringStream(std::string& str); + explicit StringStream(const std::string& str); + + virtual StreamState GetState() const; + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + virtual void Close(); + virtual bool SetPosition(size_t position); + virtual bool GetPosition(size_t* position) const; + virtual bool GetSize(size_t* size) const; + virtual bool GetAvailable(size_t* size) const; + virtual bool ReserveSize(size_t size); + + private: + std::string& str_; + size_t read_pos_; + bool read_only_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamReference - A reference counting stream adapter +/////////////////////////////////////////////////////////////////////////////// + +// Keep in mind that the streams and adapters defined in this file are +// not thread-safe, so this has limited uses. + +// A StreamRefCount holds the reference count and a pointer to the +// wrapped stream. It deletes the wrapped stream when there are no +// more references. We can then have multiple StreamReference +// instances pointing to one StreamRefCount, all wrapping the same +// stream. + +class StreamReference : public StreamAdapterInterface { + class StreamRefCount; + public: + // Constructor for the first reference to a stream + // Note: get more references through NewReference(). Use this + // constructor only once on a given stream. + explicit StreamReference(StreamInterface* stream); + StreamInterface* GetStream() { return stream(); } + StreamInterface* NewReference(); + virtual ~StreamReference(); + + private: + class StreamRefCount { + public: + explicit StreamRefCount(StreamInterface* stream) + : stream_(stream), ref_count_(1) { + } + void AddReference() { + CritScope lock(&cs_); + ++ref_count_; + } + void Release() { + int ref_count; + { // Atomic ops would have been a better fit here. + CritScope lock(&cs_); + ref_count = --ref_count_; + } + if (ref_count == 0) { + delete stream_; + delete this; + } + } + private: + StreamInterface* stream_; + int ref_count_; + CriticalSection cs_; + DISALLOW_EVIL_CONSTRUCTORS(StreamRefCount); + }; + + // Constructor for adding references + explicit StreamReference(StreamRefCount* stream_ref_count, + StreamInterface* stream); + + StreamRefCount* stream_ref_count_; + DISALLOW_EVIL_CONSTRUCTORS(StreamReference); +}; + +/////////////////////////////////////////////////////////////////////////////// + +// Flow attempts to move bytes from source to sink via buffer of size +// buffer_len. The function returns SR_SUCCESS when source reaches +// end-of-stream (returns SR_EOS), and all the data has been written successful +// to sink. Alternately, if source returns SR_BLOCK or SR_ERROR, or if sink +// returns SR_BLOCK, SR_ERROR, or SR_EOS, then the function immediately returns +// with the unexpected StreamResult value. +// data_len is the length of the valid data in buffer. in case of error +// this is the data that read from source but can't move to destination. +// as a pass in parameter, it indicates data in buffer that should move to sink +StreamResult Flow(StreamInterface* source, + char* buffer, size_t buffer_len, + StreamInterface* sink, size_t* data_len = NULL); + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_STREAM_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stream_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stream_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stream_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stream_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,497 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/stream.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace rtc { + +namespace { +static const int kTimeoutMs = 10000; +} // namespace +/////////////////////////////////////////////////////////////////////////////// +// TestStream +/////////////////////////////////////////////////////////////////////////////// + +class TestStream : public StreamInterface { + public: + TestStream() : pos_(0) { } + + virtual StreamState GetState() const { return SS_OPEN; } + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + unsigned char* uc_buffer = static_cast(buffer); + for (size_t i = 0; i < buffer_len; ++i) { + uc_buffer[i] = static_cast(pos_++); + } + if (read) + *read = buffer_len; + return SR_SUCCESS; + } + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error) { + if (error) + *error = -1; + return SR_ERROR; + } + virtual void Close() { } + virtual bool SetPosition(size_t position) { + pos_ = position; + return true; + } + virtual bool GetPosition(size_t* position) const { + if (position) *position = pos_; + return true; + } + virtual bool GetSize(size_t* size) const { + return false; + } + virtual bool GetAvailable(size_t* size) const { + return false; + } + + private: + size_t pos_; +}; + +bool VerifyTestBuffer(unsigned char* buffer, size_t len, + unsigned char value) { + bool passed = true; + for (size_t i = 0; i < len; ++i) { + if (buffer[i] != value++) { + passed = false; + break; + } + } + // Ensure that we don't pass again without re-writing + memset(buffer, 0, len); + return passed; +} + +void SeekTest(StreamInterface* stream, const unsigned char value) { + size_t bytes; + unsigned char buffer[13] = { 0 }; + const size_t kBufSize = sizeof(buffer); + + EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_SUCCESS); + EXPECT_EQ(bytes, kBufSize); + EXPECT_TRUE(VerifyTestBuffer(buffer, kBufSize, value)); + EXPECT_TRUE(stream->GetPosition(&bytes)); + EXPECT_EQ(13U, bytes); + + EXPECT_TRUE(stream->SetPosition(7)); + + EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_SUCCESS); + EXPECT_EQ(bytes, kBufSize); + EXPECT_TRUE(VerifyTestBuffer(buffer, kBufSize, value + 7)); + EXPECT_TRUE(stream->GetPosition(&bytes)); + EXPECT_EQ(20U, bytes); +} + +TEST(StreamSegment, TranslatesPosition) { + TestStream* test = new TestStream; + // Verify behavior of original stream + SeekTest(test, 0); + StreamSegment* segment = new StreamSegment(test); + // Verify behavior of adapted stream (all values offset by 20) + SeekTest(segment, 20); + delete segment; +} + +TEST(StreamSegment, SupportsArtificialTermination) { + TestStream* test = new TestStream; + + size_t bytes; + unsigned char buffer[5000] = { 0 }; + const size_t kBufSize = sizeof(buffer); + + { + StreamInterface* stream = test; + + // Read a lot of bytes + EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_SUCCESS); + EXPECT_EQ(bytes, kBufSize); + EXPECT_TRUE(VerifyTestBuffer(buffer, kBufSize, 0)); + + // Test seeking far ahead + EXPECT_TRUE(stream->SetPosition(12345)); + + // Read a bunch more bytes + EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_SUCCESS); + EXPECT_EQ(bytes, kBufSize); + EXPECT_TRUE(VerifyTestBuffer(buffer, kBufSize, 12345 % 256)); + } + + // Create a segment of test stream in range [100,600) + EXPECT_TRUE(test->SetPosition(100)); + StreamSegment* segment = new StreamSegment(test, 500); + + { + StreamInterface* stream = segment; + + EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_SUCCESS); + EXPECT_EQ(500U, bytes); + EXPECT_TRUE(VerifyTestBuffer(buffer, 500, 100)); + EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_EOS); + + // Test seeking past "end" of stream + EXPECT_FALSE(stream->SetPosition(12345)); + EXPECT_FALSE(stream->SetPosition(501)); + + // Test seeking to end (edge case) + EXPECT_TRUE(stream->SetPosition(500)); + EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_EOS); + + // Test seeking to start + EXPECT_TRUE(stream->SetPosition(0)); + EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_SUCCESS); + EXPECT_EQ(500U, bytes); + EXPECT_TRUE(VerifyTestBuffer(buffer, 500, 100)); + EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_EOS); + } + + delete segment; +} + +TEST(FifoBufferTest, TestAll) { + const size_t kSize = 16; + const char in[kSize * 2 + 1] = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; + char out[kSize * 2]; + void* p; + const void* q; + size_t bytes; + FifoBuffer buf(kSize); + StreamInterface* stream = &buf; + + // Test assumptions about base state + EXPECT_EQ(SS_OPEN, stream->GetState()); + EXPECT_EQ(SR_BLOCK, stream->Read(out, kSize, &bytes, NULL)); + EXPECT_TRUE(NULL != stream->GetReadData(&bytes)); + EXPECT_EQ((size_t)0, bytes); + stream->ConsumeReadData(0); + EXPECT_TRUE(NULL != stream->GetWriteBuffer(&bytes)); + EXPECT_EQ(kSize, bytes); + stream->ConsumeWriteBuffer(0); + + // Try a full write + EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, NULL)); + EXPECT_EQ(kSize, bytes); + + // Try a write that should block + EXPECT_EQ(SR_BLOCK, stream->Write(in, kSize, &bytes, NULL)); + + // Try a full read + EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize, &bytes, NULL)); + EXPECT_EQ(kSize, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize)); + + // Try a read that should block + EXPECT_EQ(SR_BLOCK, stream->Read(out, kSize, &bytes, NULL)); + + // Try a too-big write + EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize * 2, &bytes, NULL)); + EXPECT_EQ(bytes, kSize); + + // Try a too-big read + EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize * 2, &bytes, NULL)); + EXPECT_EQ(kSize, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize)); + + // Try some small writes and reads + EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, NULL)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, NULL)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize / 2)); + EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, NULL)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, NULL)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, NULL)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize / 2)); + EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, NULL)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize / 2)); + + // Try wraparound reads and writes in the following pattern + // WWWWWWWWWWWW.... 0123456789AB.... + // RRRRRRRRXXXX.... ........89AB.... + // WWWW....XXXXWWWW 4567....89AB0123 + // XXXX....RRRRXXXX 4567........0123 + // XXXXWWWWWWWWXXXX 4567012345670123 + // RRRRXXXXXXXXRRRR ....01234567.... + // ....RRRRRRRR.... ................ + EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize * 3 / 4, &bytes, NULL)); + EXPECT_EQ(kSize * 3 / 4, bytes); + EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, NULL)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize / 2)); + EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, NULL)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 4, &bytes, NULL)); + EXPECT_EQ(kSize / 4 , bytes); + EXPECT_EQ(0, memcmp(in + kSize / 2, out, kSize / 4)); + EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, NULL)); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, NULL)); + EXPECT_EQ(kSize / 2 , bytes); + EXPECT_EQ(0, memcmp(in, out, kSize / 2)); + EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, NULL)); + EXPECT_EQ(kSize / 2 , bytes); + EXPECT_EQ(0, memcmp(in, out, kSize / 2)); + + // Use GetWriteBuffer to reset the read_position for the next tests + stream->GetWriteBuffer(&bytes); + stream->ConsumeWriteBuffer(0); + + // Try using GetReadData to do a full read + EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, NULL)); + q = stream->GetReadData(&bytes); + EXPECT_TRUE(NULL != q); + EXPECT_EQ(kSize, bytes); + EXPECT_EQ(0, memcmp(q, in, kSize)); + stream->ConsumeReadData(kSize); + EXPECT_EQ(SR_BLOCK, stream->Read(out, kSize, &bytes, NULL)); + + // Try using GetReadData to do some small reads + EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, NULL)); + q = stream->GetReadData(&bytes); + EXPECT_TRUE(NULL != q); + EXPECT_EQ(kSize, bytes); + EXPECT_EQ(0, memcmp(q, in, kSize / 2)); + stream->ConsumeReadData(kSize / 2); + q = stream->GetReadData(&bytes); + EXPECT_TRUE(NULL != q); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(0, memcmp(q, in + kSize / 2, kSize / 2)); + stream->ConsumeReadData(kSize / 2); + EXPECT_EQ(SR_BLOCK, stream->Read(out, kSize, &bytes, NULL)); + + // Try using GetReadData in a wraparound case + // WWWWWWWWWWWWWWWW 0123456789ABCDEF + // RRRRRRRRRRRRXXXX ............CDEF + // WWWWWWWW....XXXX 01234567....CDEF + // ............RRRR 01234567........ + // RRRRRRRR........ ................ + EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, NULL)); + EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize * 3 / 4, &bytes, NULL)); + EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, NULL)); + q = stream->GetReadData(&bytes); + EXPECT_TRUE(NULL != q); + EXPECT_EQ(kSize / 4, bytes); + EXPECT_EQ(0, memcmp(q, in + kSize * 3 / 4, kSize / 4)); + stream->ConsumeReadData(kSize / 4); + q = stream->GetReadData(&bytes); + EXPECT_TRUE(NULL != q); + EXPECT_EQ(kSize / 2, bytes); + EXPECT_EQ(0, memcmp(q, in, kSize / 2)); + stream->ConsumeReadData(kSize / 2); + + // Use GetWriteBuffer to reset the read_position for the next tests + stream->GetWriteBuffer(&bytes); + stream->ConsumeWriteBuffer(0); + + // Try using GetWriteBuffer to do a full write + p = stream->GetWriteBuffer(&bytes); + EXPECT_TRUE(NULL != p); + EXPECT_EQ(kSize, bytes); + memcpy(p, in, kSize); + stream->ConsumeWriteBuffer(kSize); + EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize, &bytes, NULL)); + EXPECT_EQ(kSize, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize)); + + // Try using GetWriteBuffer to do some small writes + p = stream->GetWriteBuffer(&bytes); + EXPECT_TRUE(NULL != p); + EXPECT_EQ(kSize, bytes); + memcpy(p, in, kSize / 2); + stream->ConsumeWriteBuffer(kSize / 2); + p = stream->GetWriteBuffer(&bytes); + EXPECT_TRUE(NULL != p); + EXPECT_EQ(kSize / 2, bytes); + memcpy(p, in + kSize / 2, kSize / 2); + stream->ConsumeWriteBuffer(kSize / 2); + EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize, &bytes, NULL)); + EXPECT_EQ(kSize, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize)); + + // Try using GetWriteBuffer in a wraparound case + // WWWWWWWWWWWW.... 0123456789AB.... + // RRRRRRRRXXXX.... ........89AB.... + // ........XXXXWWWW ........89AB0123 + // WWWW....XXXXXXXX 4567....89AB0123 + // RRRR....RRRRRRRR ................ + EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize * 3 / 4, &bytes, NULL)); + EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, NULL)); + p = stream->GetWriteBuffer(&bytes); + EXPECT_TRUE(NULL != p); + EXPECT_EQ(kSize / 4, bytes); + memcpy(p, in, kSize / 4); + stream->ConsumeWriteBuffer(kSize / 4); + p = stream->GetWriteBuffer(&bytes); + EXPECT_TRUE(NULL != p); + EXPECT_EQ(kSize / 2, bytes); + memcpy(p, in + kSize / 4, kSize / 4); + stream->ConsumeWriteBuffer(kSize / 4); + EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize * 3 / 4, &bytes, NULL)); + EXPECT_EQ(kSize * 3 / 4, bytes); + EXPECT_EQ(0, memcmp(in + kSize / 2, out, kSize / 4)); + EXPECT_EQ(0, memcmp(in, out + kSize / 4, kSize / 4)); + + // Check that the stream is now empty + EXPECT_EQ(SR_BLOCK, stream->Read(out, kSize, &bytes, NULL)); + + // Try growing the buffer + EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, NULL)); + EXPECT_EQ(kSize, bytes); + EXPECT_TRUE(buf.SetCapacity(kSize * 2)); + EXPECT_EQ(SR_SUCCESS, stream->Write(in + kSize, kSize, &bytes, NULL)); + EXPECT_EQ(kSize, bytes); + EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize * 2, &bytes, NULL)); + EXPECT_EQ(kSize * 2, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize * 2)); + + // Try shrinking the buffer + EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, NULL)); + EXPECT_EQ(kSize, bytes); + EXPECT_TRUE(buf.SetCapacity(kSize)); + EXPECT_EQ(SR_BLOCK, stream->Write(in, kSize, &bytes, NULL)); + EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize, &bytes, NULL)); + EXPECT_EQ(kSize, bytes); + EXPECT_EQ(0, memcmp(in, out, kSize)); + + // Write to the stream, close it, read the remaining bytes + EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, NULL)); + stream->Close(); + EXPECT_EQ(SS_CLOSED, stream->GetState()); + EXPECT_EQ(SR_EOS, stream->Write(in, kSize / 2, &bytes, NULL)); + EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, NULL)); + EXPECT_EQ(0, memcmp(in, out, kSize / 2)); + EXPECT_EQ(SR_EOS, stream->Read(out, kSize / 2, &bytes, NULL)); +} + +TEST(FifoBufferTest, FullBufferCheck) { + FifoBuffer buff(10); + buff.ConsumeWriteBuffer(10); + + size_t free; + EXPECT_TRUE(buff.GetWriteBuffer(&free) != NULL); + EXPECT_EQ(0U, free); +} + +TEST(FifoBufferTest, WriteOffsetAndReadOffset) { + const size_t kSize = 16; + const char in[kSize * 2 + 1] = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; + char out[kSize * 2]; + FifoBuffer buf(kSize); + + // Write 14 bytes. + EXPECT_EQ(SR_SUCCESS, buf.Write(in, 14, NULL, NULL)); + + // Make sure data is in |buf|. + size_t buffered; + EXPECT_TRUE(buf.GetBuffered(&buffered)); + EXPECT_EQ(14u, buffered); + + // Read 10 bytes. + buf.ConsumeReadData(10); + + // There should be now 12 bytes of available space. + size_t remaining; + EXPECT_TRUE(buf.GetWriteRemaining(&remaining)); + EXPECT_EQ(12u, remaining); + + // Write at offset 12, this should fail. + EXPECT_EQ(SR_BLOCK, buf.WriteOffset(in, 10, 12, NULL)); + + // Write 8 bytes at offset 4, this wraps around the buffer. + EXPECT_EQ(SR_SUCCESS, buf.WriteOffset(in, 8, 4, NULL)); + + // Number of available space remains the same until we call + // ConsumeWriteBuffer(). + EXPECT_TRUE(buf.GetWriteRemaining(&remaining)); + EXPECT_EQ(12u, remaining); + buf.ConsumeWriteBuffer(12); + + // There's 4 bytes bypassed and 4 bytes no read so skip them and verify the + // 8 bytes written. + size_t read; + EXPECT_EQ(SR_SUCCESS, buf.ReadOffset(out, 8, 8, &read)); + EXPECT_EQ(8u, read); + EXPECT_EQ(0, memcmp(out, in, 8)); + + // There should still be 16 bytes available for reading. + EXPECT_TRUE(buf.GetBuffered(&buffered)); + EXPECT_EQ(16u, buffered); + + // Read at offset 16, this should fail since we don't have that much data. + EXPECT_EQ(SR_BLOCK, buf.ReadOffset(out, 10, 16, NULL)); +} + +TEST(AsyncWriteTest, TestWrite) { + FifoBuffer* buf = new FifoBuffer(100); + AsyncWriteStream stream(buf, Thread::Current()); + EXPECT_EQ(SS_OPEN, stream.GetState()); + + // Write "abc". Will go to the logging thread, which is the current + // thread. + stream.Write("abc", 3, NULL, NULL); + char bytes[100]; + size_t count; + // Messages on the thread's queue haven't been processed, so "abc" + // hasn't been written yet. + EXPECT_NE(SR_SUCCESS, buf->ReadOffset(&bytes, 3, 0, &count)); + // Now we process the messages on the thread's queue, so "abc" has + // been written. + EXPECT_TRUE_WAIT(SR_SUCCESS == buf->ReadOffset(&bytes, 3, 0, &count), + kTimeoutMs); + EXPECT_EQ(3u, count); + EXPECT_EQ(0, memcmp(bytes, "abc", 3)); + + // Write "def". Will go to the logging thread, which is the current + // thread. + stream.Write("d", 1, &count, NULL); + stream.Write("e", 1, &count, NULL); + stream.Write("f", 1, &count, NULL); + EXPECT_EQ(1u, count); + // Messages on the thread's queue haven't been processed, so "def" + // hasn't been written yet. + EXPECT_NE(SR_SUCCESS, buf->ReadOffset(&bytes, 3, 3, &count)); + // Flush() causes the message to be processed, so "def" has now been + // written. + stream.Flush(); + EXPECT_EQ(SR_SUCCESS, buf->ReadOffset(&bytes, 3, 3, &count)); + EXPECT_EQ(3u, count); + EXPECT_EQ(0, memcmp(bytes, "def", 3)); + + // Write "xyz". Will go to the logging thread, which is the current + // thread. + stream.Write("xyz", 3, &count, NULL); + EXPECT_EQ(3u, count); + // Messages on the thread's queue haven't been processed, so "xyz" + // hasn't been written yet. + EXPECT_NE(SR_SUCCESS, buf->ReadOffset(&bytes, 3, 6, &count)); + // Close() causes the message to be processed, so "xyz" has now been + // written. + stream.Close(); + EXPECT_EQ(SR_SUCCESS, buf->ReadOffset(&bytes, 3, 6, &count)); + EXPECT_EQ(3u, count); + EXPECT_EQ(0, memcmp(bytes, "xyz", 3)); + EXPECT_EQ(SS_CLOSED, stream.GetState()); + + // Is't closed, so the writes should fail. + EXPECT_EQ(SR_ERROR, stream.Write("000", 3, NULL, NULL)); + +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stringdigest.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stringdigest.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stringdigest.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stringdigest.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,17 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_STRINGDIGEST_H_ +#define WEBRTC_BASE_STRINGDIGEST_H_ + +// TODO: Update remaining callers to use messagedigest.h instead +#include "webrtc/base/messagedigest.h" + +#endif // WEBRTC_BASE_STRINGDIGEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stringencode.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stringencode.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stringencode.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stringencode.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,658 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/stringencode.h" + +#include +#include + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/checks.h" +#include "webrtc/base/stringutils.h" + +namespace rtc { + +///////////////////////////////////////////////////////////////////////////// +// String Encoding Utilities +///////////////////////////////////////////////////////////////////////////// + +size_t escape(char * buffer, size_t buflen, + const char * source, size_t srclen, + const char * illegal, char escape) { + DCHECK(buffer); // TODO: estimate output size + if (buflen <= 0) + return 0; + + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + char ch = source[srcpos++]; + if ((ch == escape) || ::strchr(illegal, ch)) { + if (bufpos + 2 >= buflen) + break; + buffer[bufpos++] = escape; + } + buffer[bufpos++] = ch; + } + + buffer[bufpos] = '\0'; + return bufpos; +} + +size_t unescape(char * buffer, size_t buflen, + const char * source, size_t srclen, + char escape) { + DCHECK(buffer); // TODO: estimate output size + if (buflen <= 0) + return 0; + + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + char ch = source[srcpos++]; + if ((ch == escape) && (srcpos < srclen)) { + ch = source[srcpos++]; + } + buffer[bufpos++] = ch; + } + buffer[bufpos] = '\0'; + return bufpos; +} + +size_t encode(char * buffer, size_t buflen, + const char * source, size_t srclen, + const char * illegal, char escape) { + DCHECK(buffer); // TODO: estimate output size + if (buflen <= 0) + return 0; + + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + char ch = source[srcpos++]; + if ((ch != escape) && !::strchr(illegal, ch)) { + buffer[bufpos++] = ch; + } else if (bufpos + 3 >= buflen) { + break; + } else { + buffer[bufpos+0] = escape; + buffer[bufpos+1] = hex_encode((static_cast(ch) >> 4) & 0xF); + buffer[bufpos+2] = hex_encode((static_cast(ch) ) & 0xF); + bufpos += 3; + } + } + buffer[bufpos] = '\0'; + return bufpos; +} + +size_t decode(char * buffer, size_t buflen, + const char * source, size_t srclen, + char escape) { + if (buflen <= 0) + return 0; + + unsigned char h1, h2; + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + char ch = source[srcpos++]; + if ((ch == escape) + && (srcpos + 1 < srclen) + && hex_decode(source[srcpos], &h1) + && hex_decode(source[srcpos+1], &h2)) { + buffer[bufpos++] = (h1 << 4) | h2; + srcpos += 2; + } else { + buffer[bufpos++] = ch; + } + } + buffer[bufpos] = '\0'; + return bufpos; +} + +const char* unsafe_filename_characters() { + // It might be better to have a single specification which is the union of + // all operating systems, unless one system is overly restrictive. +#if defined(WEBRTC_WIN) + return "\\/:*?\"<>|"; +#else // !WEBRTC_WIN + // TODO + DCHECK(false); + return ""; +#endif // !WEBRTC_WIN +} + +const unsigned char URL_UNSAFE = 0x1; // 0-33 "#$%&+,/:;<=>?@[\]^`{|} 127 +const unsigned char XML_UNSAFE = 0x2; // "&'<> +const unsigned char HTML_UNSAFE = 0x2; // "&'<> + +// ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 6 5 7 8 9 : ; < = > ? +//@ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ +//` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ + +const unsigned char ASCII_CLASS[128] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,0,3,1,1,1,3,2,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,3,1,3,1, + 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0, + 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1, +}; + +size_t url_encode(char * buffer, size_t buflen, + const char * source, size_t srclen) { + if (NULL == buffer) + return srclen * 3 + 1; + if (buflen <= 0) + return 0; + + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + unsigned char ch = source[srcpos++]; + if ((ch < 128) && (ASCII_CLASS[ch] & URL_UNSAFE)) { + if (bufpos + 3 >= buflen) { + break; + } + buffer[bufpos+0] = '%'; + buffer[bufpos+1] = hex_encode((ch >> 4) & 0xF); + buffer[bufpos+2] = hex_encode((ch ) & 0xF); + bufpos += 3; + } else { + buffer[bufpos++] = ch; + } + } + buffer[bufpos] = '\0'; + return bufpos; +} + +size_t url_decode(char * buffer, size_t buflen, + const char * source, size_t srclen) { + if (NULL == buffer) + return srclen + 1; + if (buflen <= 0) + return 0; + + unsigned char h1, h2; + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + unsigned char ch = source[srcpos++]; + if (ch == '+') { + buffer[bufpos++] = ' '; + } else if ((ch == '%') + && (srcpos + 1 < srclen) + && hex_decode(source[srcpos], &h1) + && hex_decode(source[srcpos+1], &h2)) + { + buffer[bufpos++] = (h1 << 4) | h2; + srcpos += 2; + } else { + buffer[bufpos++] = ch; + } + } + buffer[bufpos] = '\0'; + return bufpos; +} + +size_t utf8_decode(const char* source, size_t srclen, unsigned long* value) { + const unsigned char* s = reinterpret_cast(source); + if ((s[0] & 0x80) == 0x00) { // Check s[0] == 0xxxxxxx + *value = s[0]; + return 1; + } + if ((srclen < 2) || ((s[1] & 0xC0) != 0x80)) { // Check s[1] != 10xxxxxx + return 0; + } + // Accumulate the trailer byte values in value16, and combine it with the + // relevant bits from s[0], once we've determined the sequence length. + unsigned long value16 = (s[1] & 0x3F); + if ((s[0] & 0xE0) == 0xC0) { // Check s[0] == 110xxxxx + *value = ((s[0] & 0x1F) << 6) | value16; + return 2; + } + if ((srclen < 3) || ((s[2] & 0xC0) != 0x80)) { // Check s[2] != 10xxxxxx + return 0; + } + value16 = (value16 << 6) | (s[2] & 0x3F); + if ((s[0] & 0xF0) == 0xE0) { // Check s[0] == 1110xxxx + *value = ((s[0] & 0x0F) << 12) | value16; + return 3; + } + if ((srclen < 4) || ((s[3] & 0xC0) != 0x80)) { // Check s[3] != 10xxxxxx + return 0; + } + value16 = (value16 << 6) | (s[3] & 0x3F); + if ((s[0] & 0xF8) == 0xF0) { // Check s[0] == 11110xxx + *value = ((s[0] & 0x07) << 18) | value16; + return 4; + } + return 0; +} + +size_t utf8_encode(char* buffer, size_t buflen, unsigned long value) { + if ((value <= 0x7F) && (buflen >= 1)) { + buffer[0] = static_cast(value); + return 1; + } + if ((value <= 0x7FF) && (buflen >= 2)) { + buffer[0] = 0xC0 | static_cast(value >> 6); + buffer[1] = 0x80 | static_cast(value & 0x3F); + return 2; + } + if ((value <= 0xFFFF) && (buflen >= 3)) { + buffer[0] = 0xE0 | static_cast(value >> 12); + buffer[1] = 0x80 | static_cast((value >> 6) & 0x3F); + buffer[2] = 0x80 | static_cast(value & 0x3F); + return 3; + } + if ((value <= 0x1FFFFF) && (buflen >= 4)) { + buffer[0] = 0xF0 | static_cast(value >> 18); + buffer[1] = 0x80 | static_cast((value >> 12) & 0x3F); + buffer[2] = 0x80 | static_cast((value >> 6) & 0x3F); + buffer[3] = 0x80 | static_cast(value & 0x3F); + return 4; + } + return 0; +} + +size_t html_encode(char * buffer, size_t buflen, + const char * source, size_t srclen) { + DCHECK(buffer); // TODO: estimate output size + if (buflen <= 0) + return 0; + + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + unsigned char ch = source[srcpos]; + if (ch < 128) { + srcpos += 1; + if (ASCII_CLASS[ch] & HTML_UNSAFE) { + const char * escseq = 0; + size_t esclen = 0; + switch (ch) { + case '<': escseq = "<"; esclen = 4; break; + case '>': escseq = ">"; esclen = 4; break; + case '\'': escseq = "'"; esclen = 5; break; + case '\"': escseq = """; esclen = 6; break; + case '&': escseq = "&"; esclen = 5; break; + default: DCHECK(false); + } + if (bufpos + esclen >= buflen) { + break; + } + memcpy(buffer + bufpos, escseq, esclen); + bufpos += esclen; + } else { + buffer[bufpos++] = ch; + } + } else { + // Largest value is 0x1FFFFF => � (10 characters) + const size_t kEscseqSize = 11; + char escseq[kEscseqSize]; + unsigned long val; + if (size_t vallen = utf8_decode(&source[srcpos], srclen - srcpos, &val)) { + srcpos += vallen; + } else { + // Not a valid utf8 sequence, just use the raw character. + val = static_cast(source[srcpos++]); + } + size_t esclen = sprintfn(escseq, kEscseqSize, "&#%lu;", val); + if (bufpos + esclen >= buflen) { + break; + } + memcpy(buffer + bufpos, escseq, esclen); + bufpos += esclen; + } + } + buffer[bufpos] = '\0'; + return bufpos; +} + +size_t html_decode(char * buffer, size_t buflen, + const char * source, size_t srclen) { + DCHECK(buffer); // TODO: estimate output size + return xml_decode(buffer, buflen, source, srclen); +} + +size_t xml_encode(char * buffer, size_t buflen, + const char * source, size_t srclen) { + DCHECK(buffer); // TODO: estimate output size + if (buflen <= 0) + return 0; + + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + unsigned char ch = source[srcpos++]; + if ((ch < 128) && (ASCII_CLASS[ch] & XML_UNSAFE)) { + const char * escseq = 0; + size_t esclen = 0; + switch (ch) { + case '<': escseq = "<"; esclen = 4; break; + case '>': escseq = ">"; esclen = 4; break; + case '\'': escseq = "'"; esclen = 6; break; + case '\"': escseq = """; esclen = 6; break; + case '&': escseq = "&"; esclen = 5; break; + default: DCHECK(false); + } + if (bufpos + esclen >= buflen) { + break; + } + memcpy(buffer + bufpos, escseq, esclen); + bufpos += esclen; + } else { + buffer[bufpos++] = ch; + } + } + buffer[bufpos] = '\0'; + return bufpos; +} + +size_t xml_decode(char * buffer, size_t buflen, + const char * source, size_t srclen) { + DCHECK(buffer); // TODO: estimate output size + if (buflen <= 0) + return 0; + + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + unsigned char ch = source[srcpos++]; + if (ch != '&') { + buffer[bufpos++] = ch; + } else if ((srcpos + 2 < srclen) + && (memcmp(source + srcpos, "lt;", 3) == 0)) { + buffer[bufpos++] = '<'; + srcpos += 3; + } else if ((srcpos + 2 < srclen) + && (memcmp(source + srcpos, "gt;", 3) == 0)) { + buffer[bufpos++] = '>'; + srcpos += 3; + } else if ((srcpos + 4 < srclen) + && (memcmp(source + srcpos, "apos;", 5) == 0)) { + buffer[bufpos++] = '\''; + srcpos += 5; + } else if ((srcpos + 4 < srclen) + && (memcmp(source + srcpos, "quot;", 5) == 0)) { + buffer[bufpos++] = '\"'; + srcpos += 5; + } else if ((srcpos + 3 < srclen) + && (memcmp(source + srcpos, "amp;", 4) == 0)) { + buffer[bufpos++] = '&'; + srcpos += 4; + } else if ((srcpos < srclen) && (source[srcpos] == '#')) { + int int_base = 10; + if ((srcpos + 1 < srclen) && (source[srcpos+1] == 'x')) { + int_base = 16; + srcpos += 1; + } + char * ptr; + // TODO: Fix hack (ptr may go past end of data) + unsigned long val = strtoul(source + srcpos + 1, &ptr, int_base); + if ((static_cast(ptr - source) < srclen) && (*ptr == ';')) { + srcpos = ptr - source + 1; + } else { + // Not a valid escape sequence. + break; + } + if (size_t esclen = utf8_encode(buffer + bufpos, buflen - bufpos, val)) { + bufpos += esclen; + } else { + // Not enough room to encode the character, or illegal character + break; + } + } else { + // Unrecognized escape sequence. + break; + } + } + buffer[bufpos] = '\0'; + return bufpos; +} + +static const char HEX[] = "0123456789abcdef"; + +char hex_encode(unsigned char val) { + DCHECK_LT(val, 16); + return (val < 16) ? HEX[val] : '!'; +} + +bool hex_decode(char ch, unsigned char* val) { + if ((ch >= '0') && (ch <= '9')) { + *val = ch - '0'; + } else if ((ch >= 'A') && (ch <= 'Z')) { + *val = (ch - 'A') + 10; + } else if ((ch >= 'a') && (ch <= 'z')) { + *val = (ch - 'a') + 10; + } else { + return false; + } + return true; +} + +size_t hex_encode(char* buffer, size_t buflen, + const char* csource, size_t srclen) { + return hex_encode_with_delimiter(buffer, buflen, csource, srclen, 0); +} + +size_t hex_encode_with_delimiter(char* buffer, size_t buflen, + const char* csource, size_t srclen, + char delimiter) { + DCHECK(buffer); // TODO: estimate output size + if (buflen == 0) + return 0; + + // Init and check bounds. + const unsigned char* bsource = + reinterpret_cast(csource); + size_t srcpos = 0, bufpos = 0; + size_t needed = delimiter ? (srclen * 3) : (srclen * 2 + 1); + if (buflen < needed) + return 0; + + while (srcpos < srclen) { + unsigned char ch = bsource[srcpos++]; + buffer[bufpos ] = hex_encode((ch >> 4) & 0xF); + buffer[bufpos+1] = hex_encode((ch ) & 0xF); + bufpos += 2; + + // Don't write a delimiter after the last byte. + if (delimiter && (srcpos < srclen)) { + buffer[bufpos] = delimiter; + ++bufpos; + } + } + + // Null terminate. + buffer[bufpos] = '\0'; + return bufpos; +} + +std::string hex_encode(const char* source, size_t srclen) { + return hex_encode_with_delimiter(source, srclen, 0); +} + +std::string hex_encode_with_delimiter(const char* source, size_t srclen, + char delimiter) { + const size_t kBufferSize = srclen * 3; + char* buffer = STACK_ARRAY(char, kBufferSize); + size_t length = hex_encode_with_delimiter(buffer, kBufferSize, + source, srclen, delimiter); + DCHECK(srclen == 0 || length > 0); + return std::string(buffer, length); +} + +size_t hex_decode(char * cbuffer, size_t buflen, + const char * source, size_t srclen) { + return hex_decode_with_delimiter(cbuffer, buflen, source, srclen, 0); +} + +size_t hex_decode_with_delimiter(char* cbuffer, size_t buflen, + const char* source, size_t srclen, + char delimiter) { + DCHECK(cbuffer); // TODO: estimate output size + if (buflen == 0) + return 0; + + // Init and bounds check. + unsigned char* bbuffer = reinterpret_cast(cbuffer); + size_t srcpos = 0, bufpos = 0; + size_t needed = (delimiter) ? (srclen + 1) / 3 : srclen / 2; + if (buflen < needed) + return 0; + + while (srcpos < srclen) { + if ((srclen - srcpos) < 2) { + // This means we have an odd number of bytes. + return 0; + } + + unsigned char h1, h2; + if (!hex_decode(source[srcpos], &h1) || + !hex_decode(source[srcpos + 1], &h2)) + return 0; + + bbuffer[bufpos++] = (h1 << 4) | h2; + srcpos += 2; + + // Remove the delimiter if needed. + if (delimiter && (srclen - srcpos) > 1) { + if (source[srcpos] != delimiter) + return 0; + ++srcpos; + } + } + + return bufpos; +} + +size_t hex_decode(char* buffer, size_t buflen, const std::string& source) { + return hex_decode_with_delimiter(buffer, buflen, source, 0); +} +size_t hex_decode_with_delimiter(char* buffer, size_t buflen, + const std::string& source, char delimiter) { + return hex_decode_with_delimiter(buffer, buflen, + source.c_str(), source.length(), delimiter); +} + +size_t transform(std::string& value, size_t maxlen, const std::string& source, + Transform t) { + char* buffer = STACK_ARRAY(char, maxlen + 1); + size_t length = t(buffer, maxlen + 1, source.data(), source.length()); + value.assign(buffer, length); + return length; +} + +std::string s_transform(const std::string& source, Transform t) { + // Ask transformation function to approximate the destination size (returns upper bound) + size_t maxlen = t(NULL, 0, source.data(), source.length()); + char * buffer = STACK_ARRAY(char, maxlen); + size_t len = t(buffer, maxlen, source.data(), source.length()); + std::string result(buffer, len); + return result; +} + +size_t tokenize(const std::string& source, char delimiter, + std::vector* fields) { + DCHECK(fields); + fields->clear(); + size_t last = 0; + for (size_t i = 0; i < source.length(); ++i) { + if (source[i] == delimiter) { + if (i != last) { + fields->push_back(source.substr(last, i - last)); + } + last = i + 1; + } + } + if (last != source.length()) { + fields->push_back(source.substr(last, source.length() - last)); + } + return fields->size(); +} + +size_t tokenize_append(const std::string& source, char delimiter, + std::vector* fields) { + if (!fields) return 0; + + std::vector new_fields; + tokenize(source, delimiter, &new_fields); + fields->insert(fields->end(), new_fields.begin(), new_fields.end()); + return fields->size(); +} + +size_t tokenize(const std::string& source, char delimiter, char start_mark, + char end_mark, std::vector* fields) { + if (!fields) return 0; + fields->clear(); + + std::string remain_source = source; + while (!remain_source.empty()) { + size_t start_pos = remain_source.find(start_mark); + if (std::string::npos == start_pos) break; + std::string pre_mark; + if (start_pos > 0) { + pre_mark = remain_source.substr(0, start_pos - 1); + } + + ++start_pos; + size_t end_pos = remain_source.find(end_mark, start_pos); + if (std::string::npos == end_pos) break; + + // We have found the matching marks. First tokenize the pre-mask. Then add + // the marked part as a single field. Finally, loop back for the post-mark. + tokenize_append(pre_mark, delimiter, fields); + fields->push_back(remain_source.substr(start_pos, end_pos - start_pos)); + remain_source = remain_source.substr(end_pos + 1); + } + + return tokenize_append(remain_source, delimiter, fields); +} + +size_t split(const std::string& source, char delimiter, + std::vector* fields) { + DCHECK(fields); + fields->clear(); + size_t last = 0; + for (size_t i = 0; i < source.length(); ++i) { + if (source[i] == delimiter) { + fields->push_back(source.substr(last, i - last)); + last = i + 1; + } + } + fields->push_back(source.substr(last, source.length() - last)); + return fields->size(); +} + +char make_char_safe_for_filename(char c) { + if (c < 32) + return '_'; + + switch (c) { + case '<': + case '>': + case ':': + case '"': + case '/': + case '\\': + case '|': + case '*': + case '?': + return '_'; + + default: + return c; + } +} + +/* +void sprintf(std::string& value, size_t maxlen, const char * format, ...) { + char * buffer = STACK_ARRAY(char, maxlen + 1); + va_list args; + va_start(args, format); + value.assign(buffer, vsprintfn(buffer, maxlen + 1, format, args)); + va_end(args); +} +*/ + +///////////////////////////////////////////////////////////////////////////// + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stringencode.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stringencode.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stringencode.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stringencode.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,210 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_STRINGENCODE_H_ +#define WEBRTC_BASE_STRINGENCODE_H_ + +#include +#include +#include + +#include "webrtc/base/checks.h" + +namespace rtc { + +////////////////////////////////////////////////////////////////////// +// String Encoding Utilities +////////////////////////////////////////////////////////////////////// + +// Convert an unsigned value to it's utf8 representation. Returns the length +// of the encoded string, or 0 if the encoding is longer than buflen - 1. +size_t utf8_encode(char* buffer, size_t buflen, unsigned long value); +// Decode the utf8 encoded value pointed to by source. Returns the number of +// bytes used by the encoding, or 0 if the encoding is invalid. +size_t utf8_decode(const char* source, size_t srclen, unsigned long* value); + +// Escaping prefixes illegal characters with the escape character. Compact, but +// illegal characters still appear in the string. +size_t escape(char * buffer, size_t buflen, + const char * source, size_t srclen, + const char * illegal, char escape); +// Note: in-place unescaping (buffer == source) is allowed. +size_t unescape(char * buffer, size_t buflen, + const char * source, size_t srclen, + char escape); + +// Encoding replaces illegal characters with the escape character and 2 hex +// chars, so it's a little less compact than escape, but completely removes +// illegal characters. note that hex digits should not be used as illegal +// characters. +size_t encode(char * buffer, size_t buflen, + const char * source, size_t srclen, + const char * illegal, char escape); +// Note: in-place decoding (buffer == source) is allowed. +size_t decode(char * buffer, size_t buflen, + const char * source, size_t srclen, + char escape); + +// Returns a list of characters that may be unsafe for use in the name of a +// file, suitable for passing to the 'illegal' member of escape or encode. +const char* unsafe_filename_characters(); + +// url_encode is an encode operation with a predefined set of illegal characters +// and escape character (for use in URLs, obviously). +size_t url_encode(char * buffer, size_t buflen, + const char * source, size_t srclen); +// Note: in-place decoding (buffer == source) is allowed. +size_t url_decode(char * buffer, size_t buflen, + const char * source, size_t srclen); + +// html_encode prevents data embedded in html from containing markup. +size_t html_encode(char * buffer, size_t buflen, + const char * source, size_t srclen); +// Note: in-place decoding (buffer == source) is allowed. +size_t html_decode(char * buffer, size_t buflen, + const char * source, size_t srclen); + +// xml_encode makes data suitable for inside xml attributes and values. +size_t xml_encode(char * buffer, size_t buflen, + const char * source, size_t srclen); +// Note: in-place decoding (buffer == source) is allowed. +size_t xml_decode(char * buffer, size_t buflen, + const char * source, size_t srclen); + +// Convert an unsigned value from 0 to 15 to the hex character equivalent... +char hex_encode(unsigned char val); +// ...and vice-versa. +bool hex_decode(char ch, unsigned char* val); + +// hex_encode shows the hex representation of binary data in ascii. +size_t hex_encode(char* buffer, size_t buflen, + const char* source, size_t srclen); + +// hex_encode, but separate each byte representation with a delimiter. +// |delimiter| == 0 means no delimiter +// If the buffer is too short, we return 0 +size_t hex_encode_with_delimiter(char* buffer, size_t buflen, + const char* source, size_t srclen, + char delimiter); + +// Helper functions for hex_encode. +std::string hex_encode(const char* source, size_t srclen); +std::string hex_encode_with_delimiter(const char* source, size_t srclen, + char delimiter); + +// hex_decode converts ascii hex to binary. +size_t hex_decode(char* buffer, size_t buflen, + const char* source, size_t srclen); + +// hex_decode, assuming that there is a delimiter between every byte +// pair. +// |delimiter| == 0 means no delimiter +// If the buffer is too short or the data is invalid, we return 0. +size_t hex_decode_with_delimiter(char* buffer, size_t buflen, + const char* source, size_t srclen, + char delimiter); + +// Helper functions for hex_decode. +size_t hex_decode(char* buffer, size_t buflen, const std::string& source); +size_t hex_decode_with_delimiter(char* buffer, size_t buflen, + const std::string& source, char delimiter); + +// Apply any suitable string transform (including the ones above) to an STL +// string. Stack-allocated temporary space is used for the transformation, +// so value and source may refer to the same string. +typedef size_t (*Transform)(char * buffer, size_t buflen, + const char * source, size_t srclen); +size_t transform(std::string& value, size_t maxlen, const std::string& source, + Transform t); + +// Return the result of applying transform t to source. +std::string s_transform(const std::string& source, Transform t); + +// Convenience wrappers. +inline std::string s_url_encode(const std::string& source) { + return s_transform(source, url_encode); +} +inline std::string s_url_decode(const std::string& source) { + return s_transform(source, url_decode); +} + +// Splits the source string into multiple fields separated by delimiter, +// with duplicates of delimiter creating empty fields. +size_t split(const std::string& source, char delimiter, + std::vector* fields); + +// Splits the source string into multiple fields separated by delimiter, +// with duplicates of delimiter ignored. Trailing delimiter ignored. +size_t tokenize(const std::string& source, char delimiter, + std::vector* fields); + +// Tokenize and append the tokens to fields. Return the new size of fields. +size_t tokenize_append(const std::string& source, char delimiter, + std::vector* fields); + +// Splits the source string into multiple fields separated by delimiter, with +// duplicates of delimiter ignored. Trailing delimiter ignored. A substring in +// between the start_mark and the end_mark is treated as a single field. Return +// the size of fields. For example, if source is "filename +// \"/Library/Application Support/media content.txt\"", delimiter is ' ', and +// the start_mark and end_mark are '"', this method returns two fields: +// "filename" and "/Library/Application Support/media content.txt". +size_t tokenize(const std::string& source, char delimiter, char start_mark, + char end_mark, std::vector* fields); + +// Safe sprintf to std::string +//void sprintf(std::string& value, size_t maxlen, const char * format, ...) +// PRINTF_FORMAT(3); + +// Convert arbitrary values to/from a string. + +template +static bool ToString(const T &t, std::string* s) { + DCHECK(s); + std::ostringstream oss; + oss << std::boolalpha << t; + *s = oss.str(); + return !oss.fail(); +} + +template +static bool FromString(const std::string& s, T* t) { + DCHECK(t); + std::istringstream iss(s); + iss >> std::boolalpha >> *t; + return !iss.fail(); +} + +// Inline versions of the string conversion routines. + +template +static inline std::string ToString(const T& val) { + std::string str; ToString(val, &str); return str; +} + +template +static inline T FromString(const std::string& str) { + T val; FromString(str, &val); return val; +} + +template +static inline T FromString(const T& defaultValue, const std::string& str) { + T val(defaultValue); FromString(str, &val); return val; +} + +// simple function to strip out characters which shouldn't be +// used in filenames +char make_char_safe_for_filename(char c); + +////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_STRINGENCODE_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stringencode_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stringencode_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stringencode_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stringencode_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,385 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/common.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/stringencode.h" +#include "webrtc/base/stringutils.h" + +namespace rtc { + +TEST(Utf8EncodeTest, EncodeDecode) { + const struct Utf8Test { + const char* encoded; + size_t encsize, enclen; + unsigned long decoded; + } kTests[] = { + { "a ", 5, 1, 'a' }, + { "\x7F ", 5, 1, 0x7F }, + { "\xC2\x80 ", 5, 2, 0x80 }, + { "\xDF\xBF ", 5, 2, 0x7FF }, + { "\xE0\xA0\x80 ", 5, 3, 0x800 }, + { "\xEF\xBF\xBF ", 5, 3, 0xFFFF }, + { "\xF0\x90\x80\x80 ", 5, 4, 0x10000 }, + { "\xF0\x90\x80\x80 ", 3, 0, 0x10000 }, + { "\xF0\xF0\x80\x80 ", 5, 0, 0 }, + { "\xF0\x90\x80 ", 5, 0, 0 }, + { "\x90\x80\x80 ", 5, 0, 0 }, + { NULL, 0, 0 }, + }; + for (size_t i = 0; kTests[i].encoded; ++i) { + unsigned long val = 0; + ASSERT_EQ(kTests[i].enclen, utf8_decode(kTests[i].encoded, + kTests[i].encsize, + &val)); + unsigned long result = (kTests[i].enclen == 0) ? 0 : kTests[i].decoded; + ASSERT_EQ(result, val); + + if (kTests[i].decoded == 0) { + // Not an interesting encoding test case + continue; + } + + char buffer[5]; + memset(buffer, 0x01, ARRAY_SIZE(buffer)); + ASSERT_EQ(kTests[i].enclen, utf8_encode(buffer, + kTests[i].encsize, + kTests[i].decoded)); + ASSERT_TRUE(memcmp(buffer, kTests[i].encoded, kTests[i].enclen) == 0); + // Make sure remainder of buffer is unchanged + ASSERT_TRUE(memory_check(buffer + kTests[i].enclen, + 0x1, + ARRAY_SIZE(buffer) - kTests[i].enclen)); + } +} + +class HexEncodeTest : public testing::Test { + public: + HexEncodeTest() : enc_res_(0), dec_res_(0) { + for (size_t i = 0; i < sizeof(data_); ++i) { + data_[i] = (i + 128) & 0xff; + } + memset(decoded_, 0x7f, sizeof(decoded_)); + } + + char data_[10]; + char encoded_[31]; + char decoded_[11]; + size_t enc_res_; + size_t dec_res_; +}; + +// Test that we can convert to/from hex with no delimiter. +TEST_F(HexEncodeTest, TestWithNoDelimiter) { + enc_res_ = hex_encode(encoded_, sizeof(encoded_), data_, sizeof(data_)); + ASSERT_EQ(sizeof(data_) * 2, enc_res_); + ASSERT_STREQ("80818283848586878889", encoded_); + dec_res_ = hex_decode(decoded_, sizeof(decoded_), encoded_, enc_res_); + ASSERT_EQ(sizeof(data_), dec_res_); + ASSERT_EQ(0, memcmp(data_, decoded_, dec_res_)); +} + +// Test that we can convert to/from hex with a colon delimiter. +TEST_F(HexEncodeTest, TestWithDelimiter) { + enc_res_ = hex_encode_with_delimiter(encoded_, sizeof(encoded_), + data_, sizeof(data_), ':'); + ASSERT_EQ(sizeof(data_) * 3 - 1, enc_res_); + ASSERT_STREQ("80:81:82:83:84:85:86:87:88:89", encoded_); + dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_), + encoded_, enc_res_, ':'); + ASSERT_EQ(sizeof(data_), dec_res_); + ASSERT_EQ(0, memcmp(data_, decoded_, dec_res_)); +} + +// Test that encoding with one delimiter and decoding with another fails. +TEST_F(HexEncodeTest, TestWithWrongDelimiter) { + enc_res_ = hex_encode_with_delimiter(encoded_, sizeof(encoded_), + data_, sizeof(data_), ':'); + ASSERT_EQ(sizeof(data_) * 3 - 1, enc_res_); + dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_), + encoded_, enc_res_, '/'); + ASSERT_EQ(0U, dec_res_); +} + +// Test that encoding without a delimiter and decoding with one fails. +TEST_F(HexEncodeTest, TestExpectedDelimiter) { + enc_res_ = hex_encode(encoded_, sizeof(encoded_), data_, sizeof(data_)); + ASSERT_EQ(sizeof(data_) * 2, enc_res_); + dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_), + encoded_, enc_res_, ':'); + ASSERT_EQ(0U, dec_res_); +} + +// Test that encoding with a delimiter and decoding without one fails. +TEST_F(HexEncodeTest, TestExpectedNoDelimiter) { + enc_res_ = hex_encode_with_delimiter(encoded_, sizeof(encoded_), + data_, sizeof(data_), ':'); + ASSERT_EQ(sizeof(data_) * 3 - 1, enc_res_); + dec_res_ = hex_decode(decoded_, sizeof(decoded_), encoded_, enc_res_); + ASSERT_EQ(0U, dec_res_); +} + +// Test that we handle a zero-length buffer with no delimiter. +TEST_F(HexEncodeTest, TestZeroLengthNoDelimiter) { + enc_res_ = hex_encode(encoded_, sizeof(encoded_), "", 0); + ASSERT_EQ(0U, enc_res_); + dec_res_ = hex_decode(decoded_, sizeof(decoded_), encoded_, enc_res_); + ASSERT_EQ(0U, dec_res_); +} + +// Test that we handle a zero-length buffer with a delimiter. +TEST_F(HexEncodeTest, TestZeroLengthWithDelimiter) { + enc_res_ = hex_encode_with_delimiter(encoded_, sizeof(encoded_), "", 0, ':'); + ASSERT_EQ(0U, enc_res_); + dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_), + encoded_, enc_res_, ':'); + ASSERT_EQ(0U, dec_res_); +} + +// Test the std::string variants that take no delimiter. +TEST_F(HexEncodeTest, TestHelpersNoDelimiter) { + std::string result = hex_encode(data_, sizeof(data_)); + ASSERT_EQ("80818283848586878889", result); + dec_res_ = hex_decode(decoded_, sizeof(decoded_), result); + ASSERT_EQ(sizeof(data_), dec_res_); + ASSERT_EQ(0, memcmp(data_, decoded_, dec_res_)); +} + +// Test the std::string variants that use a delimiter. +TEST_F(HexEncodeTest, TestHelpersWithDelimiter) { + std::string result = hex_encode_with_delimiter(data_, sizeof(data_), ':'); + ASSERT_EQ("80:81:82:83:84:85:86:87:88:89", result); + dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_), result, ':'); + ASSERT_EQ(sizeof(data_), dec_res_); + ASSERT_EQ(0, memcmp(data_, decoded_, dec_res_)); +} + +// Test that encoding into a too-small output buffer (without delimiter) fails. +TEST_F(HexEncodeTest, TestEncodeTooShort) { + enc_res_ = hex_encode_with_delimiter(encoded_, sizeof(data_) * 2, + data_, sizeof(data_), 0); + ASSERT_EQ(0U, enc_res_); +} + +// Test that encoding into a too-small output buffer (with delimiter) fails. +TEST_F(HexEncodeTest, TestEncodeWithDelimiterTooShort) { + enc_res_ = hex_encode_with_delimiter(encoded_, sizeof(data_) * 3 - 1, + data_, sizeof(data_), ':'); + ASSERT_EQ(0U, enc_res_); +} + +// Test that decoding into a too-small output buffer fails. +TEST_F(HexEncodeTest, TestDecodeTooShort) { + dec_res_ = hex_decode_with_delimiter(decoded_, 4, "0123456789", 10, 0); + ASSERT_EQ(0U, dec_res_); + ASSERT_EQ(0x7f, decoded_[4]); +} + +// Test that decoding non-hex data fails. +TEST_F(HexEncodeTest, TestDecodeBogusData) { + dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_), "xyz", 3, 0); + ASSERT_EQ(0U, dec_res_); +} + +// Test that decoding an odd number of hex characters fails. +TEST_F(HexEncodeTest, TestDecodeOddHexDigits) { + dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_), "012", 3, 0); + ASSERT_EQ(0U, dec_res_); +} + +// Test that decoding a string with too many delimiters fails. +TEST_F(HexEncodeTest, TestDecodeWithDelimiterTooManyDelimiters) { + dec_res_ = hex_decode_with_delimiter(decoded_, 4, "01::23::45::67", 14, ':'); + ASSERT_EQ(0U, dec_res_); +} + +// Test that decoding a string with a leading delimiter fails. +TEST_F(HexEncodeTest, TestDecodeWithDelimiterLeadingDelimiter) { + dec_res_ = hex_decode_with_delimiter(decoded_, 4, ":01:23:45:67", 12, ':'); + ASSERT_EQ(0U, dec_res_); +} + +// Test that decoding a string with a trailing delimiter fails. +TEST_F(HexEncodeTest, TestDecodeWithDelimiterTrailingDelimiter) { + dec_res_ = hex_decode_with_delimiter(decoded_, 4, "01:23:45:67:", 12, ':'); + ASSERT_EQ(0U, dec_res_); +} + +// Tests counting substrings. +TEST(TokenizeTest, CountSubstrings) { + std::vector fields; + + EXPECT_EQ(5ul, tokenize("one two three four five", ' ', &fields)); + fields.clear(); + EXPECT_EQ(1ul, tokenize("one", ' ', &fields)); + + // Extra spaces should be ignored. + fields.clear(); + EXPECT_EQ(5ul, tokenize(" one two three four five ", ' ', &fields)); + fields.clear(); + EXPECT_EQ(1ul, tokenize(" one ", ' ', &fields)); + fields.clear(); + EXPECT_EQ(0ul, tokenize(" ", ' ', &fields)); +} + +// Tests comparing substrings. +TEST(TokenizeTest, CompareSubstrings) { + std::vector fields; + + tokenize("find middle one", ' ', &fields); + ASSERT_EQ(3ul, fields.size()); + ASSERT_STREQ("middle", fields.at(1).c_str()); + fields.clear(); + + // Extra spaces should be ignored. + tokenize(" find middle one ", ' ', &fields); + ASSERT_EQ(3ul, fields.size()); + ASSERT_STREQ("middle", fields.at(1).c_str()); + fields.clear(); + tokenize(" ", ' ', &fields); + ASSERT_EQ(0ul, fields.size()); +} + +TEST(TokenizeTest, TokenizeAppend) { + ASSERT_EQ(0ul, tokenize_append("A B C", ' ', NULL)); + + std::vector fields; + + tokenize_append("A B C", ' ', &fields); + ASSERT_EQ(3ul, fields.size()); + ASSERT_STREQ("B", fields.at(1).c_str()); + + tokenize_append("D E", ' ', &fields); + ASSERT_EQ(5ul, fields.size()); + ASSERT_STREQ("B", fields.at(1).c_str()); + ASSERT_STREQ("E", fields.at(4).c_str()); +} + +TEST(TokenizeTest, TokenizeWithMarks) { + ASSERT_EQ(0ul, tokenize("D \"A B", ' ', '(', ')', NULL)); + + std::vector fields; + tokenize("A B C", ' ', '"', '"', &fields); + ASSERT_EQ(3ul, fields.size()); + ASSERT_STREQ("C", fields.at(2).c_str()); + + tokenize("\"A B\" C", ' ', '"', '"', &fields); + ASSERT_EQ(2ul, fields.size()); + ASSERT_STREQ("A B", fields.at(0).c_str()); + + tokenize("D \"A B\" C", ' ', '"', '"', &fields); + ASSERT_EQ(3ul, fields.size()); + ASSERT_STREQ("D", fields.at(0).c_str()); + ASSERT_STREQ("A B", fields.at(1).c_str()); + + tokenize("D \"A B\" C \"E F\"", ' ', '"', '"', &fields); + ASSERT_EQ(4ul, fields.size()); + ASSERT_STREQ("D", fields.at(0).c_str()); + ASSERT_STREQ("A B", fields.at(1).c_str()); + ASSERT_STREQ("E F", fields.at(3).c_str()); + + // No matching marks. + tokenize("D \"A B", ' ', '"', '"', &fields); + ASSERT_EQ(3ul, fields.size()); + ASSERT_STREQ("D", fields.at(0).c_str()); + ASSERT_STREQ("\"A", fields.at(1).c_str()); + + tokenize("D (A B) C (E F) G", ' ', '(', ')', &fields); + ASSERT_EQ(5ul, fields.size()); + ASSERT_STREQ("D", fields.at(0).c_str()); + ASSERT_STREQ("A B", fields.at(1).c_str()); + ASSERT_STREQ("E F", fields.at(3).c_str()); +} + +// Tests counting substrings. +TEST(SplitTest, CountSubstrings) { + std::vector fields; + + EXPECT_EQ(5ul, split("one,two,three,four,five", ',', &fields)); + fields.clear(); + EXPECT_EQ(1ul, split("one", ',', &fields)); + + // Empty fields between commas count. + fields.clear(); + EXPECT_EQ(5ul, split("one,,three,four,five", ',', &fields)); + fields.clear(); + EXPECT_EQ(3ul, split(",three,", ',', &fields)); + fields.clear(); + EXPECT_EQ(1ul, split("", ',', &fields)); +} + +// Tests comparing substrings. +TEST(SplitTest, CompareSubstrings) { + std::vector fields; + + split("find,middle,one", ',', &fields); + ASSERT_EQ(3ul, fields.size()); + ASSERT_STREQ("middle", fields.at(1).c_str()); + fields.clear(); + + // Empty fields between commas count. + split("find,,middle,one", ',', &fields); + ASSERT_EQ(4ul, fields.size()); + ASSERT_STREQ("middle", fields.at(2).c_str()); + fields.clear(); + split("", ',', &fields); + ASSERT_EQ(1ul, fields.size()); + ASSERT_STREQ("", fields.at(0).c_str()); +} + +TEST(BoolTest, DecodeValid) { + bool value; + EXPECT_TRUE(FromString("true", &value)); + EXPECT_TRUE(value); + EXPECT_TRUE(FromString("true,", &value)); + EXPECT_TRUE(value); + EXPECT_TRUE(FromString("true , true", &value)); + EXPECT_TRUE(value); + EXPECT_TRUE(FromString("true ,\n false", &value)); + EXPECT_TRUE(value); + EXPECT_TRUE(FromString(" true \n", &value)); + EXPECT_TRUE(value); + + EXPECT_TRUE(FromString("false", &value)); + EXPECT_FALSE(value); + EXPECT_TRUE(FromString(" false ", &value)); + EXPECT_FALSE(value); + EXPECT_TRUE(FromString(" false, ", &value)); + EXPECT_FALSE(value); + + EXPECT_TRUE(FromString("true\n")); + EXPECT_FALSE(FromString("false\n")); +} + +TEST(BoolTest, DecodeInvalid) { + bool value; + EXPECT_FALSE(FromString("True", &value)); + EXPECT_FALSE(FromString("TRUE", &value)); + EXPECT_FALSE(FromString("False", &value)); + EXPECT_FALSE(FromString("FALSE", &value)); + EXPECT_FALSE(FromString("0", &value)); + EXPECT_FALSE(FromString("1", &value)); + EXPECT_FALSE(FromString("0,", &value)); + EXPECT_FALSE(FromString("1,", &value)); + EXPECT_FALSE(FromString("1,0", &value)); + EXPECT_FALSE(FromString("1.", &value)); + EXPECT_FALSE(FromString("1.0", &value)); + EXPECT_FALSE(FromString("", &value)); + EXPECT_FALSE(FromString("false\nfalse")); +} + +TEST(BoolTest, RoundTrip) { + bool value; + EXPECT_TRUE(FromString(ToString(true), &value)); + EXPECT_TRUE(value); + EXPECT_TRUE(FromString(ToString(false), &value)); + EXPECT_FALSE(value); +} +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stringutils.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stringutils.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stringutils.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stringutils.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,133 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/checks.h" +#include "webrtc/base/stringutils.h" + +namespace rtc { + +bool memory_check(const void* memory, int c, size_t count) { + const char* char_memory = static_cast(memory); + char char_c = static_cast(c); + for (size_t i = 0; i < count; ++i) { + if (char_memory[i] != char_c) { + return false; + } + } + return true; +} + +bool string_match(const char* target, const char* pattern) { + while (*pattern) { + if (*pattern == '*') { + if (!*++pattern) { + return true; + } + while (*target) { + if ((toupper(*pattern) == toupper(*target)) + && string_match(target + 1, pattern + 1)) { + return true; + } + ++target; + } + return false; + } else { + if (toupper(*pattern) != toupper(*target)) { + return false; + } + ++target; + ++pattern; + } + } + return !*target; +} + +#if defined(WEBRTC_WIN) +int ascii_string_compare(const wchar_t* s1, const char* s2, size_t n, + CharacterTransformation transformation) { + wchar_t c1, c2; + while (true) { + if (n-- == 0) return 0; + c1 = transformation(*s1); + // Double check that characters are not UTF-8 + DCHECK_LT(static_cast(*s2), 128); + // Note: *s2 gets implicitly promoted to wchar_t + c2 = transformation(*s2); + if (c1 != c2) return (c1 < c2) ? -1 : 1; + if (!c1) return 0; + ++s1; + ++s2; + } +} + +size_t asccpyn(wchar_t* buffer, size_t buflen, + const char* source, size_t srclen) { + if (buflen <= 0) + return 0; + + if (srclen == SIZE_UNKNOWN) { + srclen = strlenn(source, buflen - 1); + } else if (srclen >= buflen) { + srclen = buflen - 1; + } +#if _DEBUG + // Double check that characters are not UTF-8 + for (size_t pos = 0; pos < srclen; ++pos) + DCHECK_LT(static_cast(source[pos]), 128); +#endif // _DEBUG + std::copy(source, source + srclen, buffer); + buffer[srclen] = 0; + return srclen; +} + +#endif // WEBRTC_WIN + +void replace_substrs(const char *search, + size_t search_len, + const char *replace, + size_t replace_len, + std::string *s) { + size_t pos = 0; + while ((pos = s->find(search, pos, search_len)) != std::string::npos) { + s->replace(pos, search_len, replace, replace_len); + pos += replace_len; + } +} + +bool starts_with(const char *s1, const char *s2) { + return strncmp(s1, s2, strlen(s2)) == 0; +} + +bool ends_with(const char *s1, const char *s2) { + size_t s1_length = strlen(s1); + size_t s2_length = strlen(s2); + + if (s2_length > s1_length) { + return false; + } + + const char* start = s1 + (s1_length - s2_length); + return strncmp(start, s2, s2_length) == 0; +} + +static const char kWhitespace[] = " \n\r\t"; + +std::string string_trim(const std::string& s) { + std::string::size_type first = s.find_first_not_of(kWhitespace); + std::string::size_type last = s.find_last_not_of(kWhitespace); + + if (first == std::string::npos || last == std::string::npos) { + return std::string(""); + } + + return s.substr(first, last - first + 1); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stringutils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stringutils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stringutils.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stringutils.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,318 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_STRINGUTILS_H__ +#define WEBRTC_BASE_STRINGUTILS_H__ + +#include +#include +#include +#include + +#if defined(WEBRTC_WIN) +#include +#include +#define alloca _alloca +#endif // WEBRTC_WIN + +#if defined(WEBRTC_POSIX) +#ifdef WEBRTC_BSD +#include +#else // BSD +#include +#endif // !BSD +#endif // WEBRTC_POSIX + +#include + +#include "webrtc/base/basictypes.h" + +/////////////////////////////////////////////////////////////////////////////// +// Generic string/memory utilities +/////////////////////////////////////////////////////////////////////////////// + +#define STACK_ARRAY(TYPE, LEN) static_cast(::alloca((LEN)*sizeof(TYPE))) + +namespace rtc { + +// Complement to memset. Verifies memory consists of count bytes of value c. +bool memory_check(const void* memory, int c, size_t count); + +// Determines whether the simple wildcard pattern matches target. +// Alpha characters in pattern match case-insensitively. +// Asterisks in pattern match 0 or more characters. +// Ex: string_match("www.TEST.GOOGLE.COM", "www.*.com") -> true +bool string_match(const char* target, const char* pattern); + +} // namespace rtc + +/////////////////////////////////////////////////////////////////////////////// +// Rename a bunch of common string functions so they are consistent across +// platforms and between char and wchar_t variants. +// Here is the full list of functions that are unified: +// strlen, strcmp, stricmp, strncmp, strnicmp +// strchr, vsnprintf, strtoul, tolowercase +// tolowercase is like tolower, but not compatible with end-of-file value +// +// It's not clear if we will ever use wchar_t strings on unix. In theory, +// all strings should be Utf8 all the time, except when interfacing with Win32 +// APIs that require Utf16. +/////////////////////////////////////////////////////////////////////////////// + +inline char tolowercase(char c) { + return static_cast(tolower(c)); +} + +#if defined(WEBRTC_WIN) + +inline size_t strlen(const wchar_t* s) { + return wcslen(s); +} +inline int strcmp(const wchar_t* s1, const wchar_t* s2) { + return wcscmp(s1, s2); +} +inline int stricmp(const wchar_t* s1, const wchar_t* s2) { + return _wcsicmp(s1, s2); +} +inline int strncmp(const wchar_t* s1, const wchar_t* s2, size_t n) { + return wcsncmp(s1, s2, n); +} +inline int strnicmp(const wchar_t* s1, const wchar_t* s2, size_t n) { + return _wcsnicmp(s1, s2, n); +} +inline const wchar_t* strchr(const wchar_t* s, wchar_t c) { + return wcschr(s, c); +} +inline const wchar_t* strstr(const wchar_t* haystack, const wchar_t* needle) { + return wcsstr(haystack, needle); +} +#ifndef vsnprintf +inline int vsnprintf(wchar_t* buf, size_t n, const wchar_t* fmt, va_list args) { + return _vsnwprintf(buf, n, fmt, args); +} +#endif // !vsnprintf +inline unsigned long strtoul(const wchar_t* snum, wchar_t** end, int base) { + return wcstoul(snum, end, base); +} +inline wchar_t tolowercase(wchar_t c) { + return static_cast(towlower(c)); +} + +#endif // WEBRTC_WIN + +#if defined(WEBRTC_POSIX) + +inline int _stricmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} +inline int _strnicmp(const char* s1, const char* s2, size_t n) { + return strncasecmp(s1, s2, n); +} + +#endif // WEBRTC_POSIX + +/////////////////////////////////////////////////////////////////////////////// +// Traits simplifies porting string functions to be CTYPE-agnostic +/////////////////////////////////////////////////////////////////////////////// + +namespace rtc { + +const size_t SIZE_UNKNOWN = static_cast(-1); + +template +struct Traits { + // STL string type + //typedef XXX string; + // Null-terminated string + //inline static const CTYPE* empty_str(); +}; + +/////////////////////////////////////////////////////////////////////////////// +// String utilities which work with char or wchar_t +/////////////////////////////////////////////////////////////////////////////// + +template +inline const CTYPE* nonnull(const CTYPE* str, const CTYPE* def_str = NULL) { + return str ? str : (def_str ? def_str : Traits::empty_str()); +} + +template +const CTYPE* strchr(const CTYPE* str, const CTYPE* chs) { + for (size_t i=0; str[i]; ++i) { + for (size_t j=0; chs[j]; ++j) { + if (str[i] == chs[j]) { + return str + i; + } + } + } + return 0; +} + +template +const CTYPE* strchrn(const CTYPE* str, size_t slen, CTYPE ch) { + for (size_t i=0; i +size_t strlenn(const CTYPE* buffer, size_t buflen) { + size_t bufpos = 0; + while (buffer[bufpos] && (bufpos < buflen)) { + ++bufpos; + } + return bufpos; +} + +// Safe versions of strncpy, strncat, snprintf and vsnprintf that always +// null-terminate. + +template +size_t strcpyn(CTYPE* buffer, size_t buflen, + const CTYPE* source, size_t srclen = SIZE_UNKNOWN) { + if (buflen <= 0) + return 0; + + if (srclen == SIZE_UNKNOWN) { + srclen = strlenn(source, buflen - 1); + } else if (srclen >= buflen) { + srclen = buflen - 1; + } + memcpy(buffer, source, srclen * sizeof(CTYPE)); + buffer[srclen] = 0; + return srclen; +} + +template +size_t strcatn(CTYPE* buffer, size_t buflen, + const CTYPE* source, size_t srclen = SIZE_UNKNOWN) { + if (buflen <= 0) + return 0; + + size_t bufpos = strlenn(buffer, buflen - 1); + return bufpos + strcpyn(buffer + bufpos, buflen - bufpos, source, srclen); +} + +// Some compilers (clang specifically) require vsprintfn be defined before +// sprintfn. +template +size_t vsprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format, + va_list args) { + int len = vsnprintf(buffer, buflen, format, args); + if ((len < 0) || (static_cast(len) >= buflen)) { + len = static_cast(buflen - 1); + buffer[len] = 0; + } + return len; +} + +template +size_t sprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format, ...); +template +size_t sprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format, ...) { + va_list args; + va_start(args, format); + size_t len = vsprintfn(buffer, buflen, format, args); + va_end(args); + return len; +} + +/////////////////////////////////////////////////////////////////////////////// +// Allow safe comparing and copying ascii (not UTF-8) with both wide and +// non-wide character strings. +/////////////////////////////////////////////////////////////////////////////// + +inline int asccmp(const char* s1, const char* s2) { + return strcmp(s1, s2); +} +inline int ascicmp(const char* s1, const char* s2) { + return _stricmp(s1, s2); +} +inline int ascncmp(const char* s1, const char* s2, size_t n) { + return strncmp(s1, s2, n); +} +inline int ascnicmp(const char* s1, const char* s2, size_t n) { + return _strnicmp(s1, s2, n); +} +inline size_t asccpyn(char* buffer, size_t buflen, + const char* source, size_t srclen = SIZE_UNKNOWN) { + return strcpyn(buffer, buflen, source, srclen); +} + +#if defined(WEBRTC_WIN) + +typedef wchar_t(*CharacterTransformation)(wchar_t); +inline wchar_t identity(wchar_t c) { return c; } +int ascii_string_compare(const wchar_t* s1, const char* s2, size_t n, + CharacterTransformation transformation); + +inline int asccmp(const wchar_t* s1, const char* s2) { + return ascii_string_compare(s1, s2, static_cast(-1), identity); +} +inline int ascicmp(const wchar_t* s1, const char* s2) { + return ascii_string_compare(s1, s2, static_cast(-1), tolowercase); +} +inline int ascncmp(const wchar_t* s1, const char* s2, size_t n) { + return ascii_string_compare(s1, s2, n, identity); +} +inline int ascnicmp(const wchar_t* s1, const char* s2, size_t n) { + return ascii_string_compare(s1, s2, n, tolowercase); +} +size_t asccpyn(wchar_t* buffer, size_t buflen, + const char* source, size_t srclen = SIZE_UNKNOWN); + +#endif // WEBRTC_WIN + +/////////////////////////////////////////////////////////////////////////////// +// Traits specializations +/////////////////////////////////////////////////////////////////////////////// + +template<> +struct Traits { + typedef std::string string; + inline static const char* empty_str() { return ""; } +}; + +/////////////////////////////////////////////////////////////////////////////// +// Traits specializations (Windows only, currently) +/////////////////////////////////////////////////////////////////////////////// + +#if defined(WEBRTC_WIN) + +template<> +struct Traits { + typedef std::wstring string; + inline static const wchar_t* Traits::empty_str() { return L""; } +}; + +#endif // WEBRTC_WIN + +// Replaces all occurrences of "search" with "replace". +void replace_substrs(const char *search, + size_t search_len, + const char *replace, + size_t replace_len, + std::string *s); + +// True iff s1 starts with s2. +bool starts_with(const char *s1, const char *s2); + +// True iff s1 ends with s2. +bool ends_with(const char *s1, const char *s2); + +// Remove leading and trailing whitespaces. +std::string string_trim(const std::string& s); + +} // namespace rtc + +#endif // WEBRTC_BASE_STRINGUTILS_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stringutils_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stringutils_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/stringutils_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/stringutils_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,109 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/stringutils.h" +#include "webrtc/base/common.h" + +namespace rtc { + +// Tests for string_match(). + +TEST(string_matchTest, Matches) { + EXPECT_TRUE( string_match("A.B.C.D", "a.b.c.d")); + EXPECT_TRUE( string_match("www.TEST.GOOGLE.COM", "www.*.com")); + EXPECT_TRUE( string_match("127.0.0.1", "12*.0.*1")); + EXPECT_TRUE( string_match("127.1.0.21", "12*.0.*1")); + EXPECT_FALSE(string_match("127.0.0.0", "12*.0.*1")); + EXPECT_FALSE(string_match("127.0.0.0", "12*.0.*1")); + EXPECT_FALSE(string_match("127.1.1.21", "12*.0.*1")); +} + +// It's not clear if we will ever use wchar_t strings on unix. In theory, +// all strings should be Utf8 all the time, except when interfacing with Win32 +// APIs that require Utf16. + +#if defined(WEBRTC_WIN) + +// Tests for ascii_string_compare(). + +// Tests NULL input. +TEST(ascii_string_compareTest, NullInput) { + // The following results in an access violation in + // ascii_string_compare. Is this a bug or by design? stringutils.h + // should document the expected behavior in this case. + + // EXPECT_EQ(0, ascii_string_compare(NULL, NULL, 1, identity)); +} + +// Tests comparing two strings of different lengths. +TEST(ascii_string_compareTest, DifferentLengths) { + EXPECT_EQ(-1, ascii_string_compare(L"Test", "Test1", 5, identity)); +} + +// Tests the case where the buffer size is smaller than the string +// lengths. +TEST(ascii_string_compareTest, SmallBuffer) { + EXPECT_EQ(0, ascii_string_compare(L"Test", "Test1", 3, identity)); +} + +// Tests the case where the buffer is not full. +TEST(ascii_string_compareTest, LargeBuffer) { + EXPECT_EQ(0, ascii_string_compare(L"Test", "Test", 10, identity)); +} + +// Tests comparing two eqaul strings. +TEST(ascii_string_compareTest, Equal) { + EXPECT_EQ(0, ascii_string_compare(L"Test", "Test", 5, identity)); + EXPECT_EQ(0, ascii_string_compare(L"TeSt", "tEsT", 5, tolowercase)); +} + +// Tests comparing a smller string to a larger one. +TEST(ascii_string_compareTest, LessThan) { + EXPECT_EQ(-1, ascii_string_compare(L"abc", "abd", 4, identity)); + EXPECT_EQ(-1, ascii_string_compare(L"ABC", "abD", 5, tolowercase)); +} + +// Tests comparing a larger string to a smaller one. +TEST(ascii_string_compareTest, GreaterThan) { + EXPECT_EQ(1, ascii_string_compare(L"xyz", "xy", 5, identity)); + EXPECT_EQ(1, ascii_string_compare(L"abc", "ABB", 5, tolowercase)); +} +#endif // WEBRTC_WIN + +TEST(string_trim_Test, Trimming) { + EXPECT_EQ("temp", string_trim("\n\r\t temp \n\r\t")); + EXPECT_EQ("temp\n\r\t temp", string_trim(" temp\n\r\t temp ")); + EXPECT_EQ("temp temp", string_trim("temp temp")); + EXPECT_EQ("", string_trim(" \r\n\t")); + EXPECT_EQ("", string_trim("")); +} + +TEST(string_startsTest, StartsWith) { + EXPECT_TRUE(starts_with("foobar", "foo")); + EXPECT_TRUE(starts_with("foobar", "foobar")); + EXPECT_TRUE(starts_with("foobar", "")); + EXPECT_TRUE(starts_with("", "")); + EXPECT_FALSE(starts_with("foobar", "bar")); + EXPECT_FALSE(starts_with("foobar", "foobarbaz")); + EXPECT_FALSE(starts_with("", "f")); +} + +TEST(string_endsTest, EndsWith) { + EXPECT_TRUE(ends_with("foobar", "bar")); + EXPECT_TRUE(ends_with("foobar", "foobar")); + EXPECT_TRUE(ends_with("foobar", "")); + EXPECT_TRUE(ends_with("", "")); + EXPECT_FALSE(ends_with("foobar", "foo")); + EXPECT_FALSE(ends_with("foobar", "foobarbaz")); + EXPECT_FALSE(ends_with("", "f")); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/systeminfo.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/systeminfo.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/systeminfo.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/systeminfo.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,518 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/systeminfo.h" + +#if defined(WEBRTC_WIN) +#include +#ifndef EXCLUDE_D3D9 +#include +#endif +#include // for __cpuid() +#elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +#include +#include +#elif defined(WEBRTC_LINUX) +#include +#endif +#if defined(WEBRTC_MAC) +#include +#endif + +#if defined(WEBRTC_WIN) +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/win32.h" +#elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +#include "webrtc/base/macconversion.h" +#elif defined(WEBRTC_LINUX) +#include "webrtc/base/linux.h" +#endif +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/stringutils.h" + +namespace rtc { + +// See Also: http://msdn.microsoft.com/en-us/library/ms683194(v=vs.85).aspx +#if defined(WEBRTC_WIN) +typedef BOOL (WINAPI *LPFN_GLPI)( + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, + PDWORD); + +static void GetProcessorInformation(int* physical_cpus, int* cache_size) { + // GetLogicalProcessorInformation() is available on Windows XP SP3 and beyond. + LPFN_GLPI glpi = reinterpret_cast(GetProcAddress( + GetModuleHandle(L"kernel32"), + "GetLogicalProcessorInformation")); + if (NULL == glpi) { + return; + } + // Determine buffer size, allocate and get processor information. + // Size can change between calls (unlikely), so a loop is done. + DWORD return_length = 0; + scoped_ptr infos; + while (!glpi(infos.get(), &return_length)) { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + infos.reset(new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[ + return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)]); + } else { + return; + } + } + *physical_cpus = 0; + *cache_size = 0; + for (size_t i = 0; + i < return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ++i) { + if (infos[i].Relationship == RelationProcessorCore) { + ++*physical_cpus; + } else if (infos[i].Relationship == RelationCache) { + int next_cache_size = static_cast(infos[i].Cache.Size); + if (next_cache_size >= *cache_size) { + *cache_size = next_cache_size; + } + } + } + return; +} +#else +// TODO(fbarchard): Use gcc 4.4 provided cpuid intrinsic +// 32 bit fpic requires ebx be preserved +#if (defined(__pic__) || defined(__APPLE__)) && defined(__i386__) +static inline void __cpuid(int cpu_info[4], int info_type) { + __asm__ volatile ( // NOLINT + "mov %%ebx, %%edi\n" + "cpuid\n" + "xchg %%edi, %%ebx\n" + : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) + : "a"(info_type) + ); // NOLINT +} +#elif defined(__i386__) || defined(__x86_64__) +static inline void __cpuid(int cpu_info[4], int info_type) { + __asm__ volatile ( // NOLINT + "cpuid\n" + : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) + : "a"(info_type) + ); // NOLINT +} +#endif +#endif // WEBRTC_WIN + +// Note(fbarchard): +// Family and model are extended family and extended model. 8 bits each. +SystemInfo::SystemInfo() + : physical_cpus_(1), logical_cpus_(1), cache_size_(0), + cpu_family_(0), cpu_model_(0), cpu_stepping_(0), + cpu_speed_(0), memory_(0) { + // Initialize the basic information. +#if defined(__arm__) || defined(_M_ARM) + cpu_arch_ = SI_ARCH_ARM; +#elif defined(__x86_64__) || defined(_M_X64) + cpu_arch_ = SI_ARCH_X64; +#elif defined(__i386__) || defined(_M_IX86) + cpu_arch_ = SI_ARCH_X86; +#else + cpu_arch_ = SI_ARCH_UNKNOWN; +#endif + +#if defined(WEBRTC_WIN) + SYSTEM_INFO si; + GetSystemInfo(&si); + logical_cpus_ = si.dwNumberOfProcessors; + GetProcessorInformation(&physical_cpus_, &cache_size_); + if (physical_cpus_ <= 0) { + physical_cpus_ = logical_cpus_; + } + cpu_family_ = si.wProcessorLevel; + cpu_model_ = si.wProcessorRevision >> 8; + cpu_stepping_ = si.wProcessorRevision & 0xFF; +#elif defined(WEBRTC_MAC) + uint32_t sysctl_value; + size_t length = sizeof(sysctl_value); + if (!sysctlbyname("hw.physicalcpu_max", &sysctl_value, &length, NULL, 0)) { + physical_cpus_ = static_cast(sysctl_value); + } + length = sizeof(sysctl_value); + if (!sysctlbyname("hw.logicalcpu_max", &sysctl_value, &length, NULL, 0)) { + logical_cpus_ = static_cast(sysctl_value); + } + uint64_t sysctl_value64; + length = sizeof(sysctl_value64); + if (!sysctlbyname("hw.l3cachesize", &sysctl_value64, &length, NULL, 0)) { + cache_size_ = static_cast(sysctl_value64); + } + if (!cache_size_) { + length = sizeof(sysctl_value64); + if (!sysctlbyname("hw.l2cachesize", &sysctl_value64, &length, NULL, 0)) { + cache_size_ = static_cast(sysctl_value64); + } + } + length = sizeof(sysctl_value); + if (!sysctlbyname("machdep.cpu.family", &sysctl_value, &length, NULL, 0)) { + cpu_family_ = static_cast(sysctl_value); + } + length = sizeof(sysctl_value); + if (!sysctlbyname("machdep.cpu.model", &sysctl_value, &length, NULL, 0)) { + cpu_model_ = static_cast(sysctl_value); + } + length = sizeof(sysctl_value); + if (!sysctlbyname("machdep.cpu.stepping", &sysctl_value, &length, NULL, 0)) { + cpu_stepping_ = static_cast(sysctl_value); + } +#elif defined(__native_client__) + // TODO(ryanpetrie): Implement this via PPAPI when it's available. +#else // WEBRTC_LINUX + ProcCpuInfo proc_info; + if (proc_info.LoadFromSystem()) { + proc_info.GetNumCpus(&logical_cpus_); + proc_info.GetNumPhysicalCpus(&physical_cpus_); + proc_info.GetCpuFamily(&cpu_family_); +#if defined(CPU_X86) + // These values only apply to x86 systems. + proc_info.GetSectionIntValue(0, "model", &cpu_model_); + proc_info.GetSectionIntValue(0, "stepping", &cpu_stepping_); + proc_info.GetSectionIntValue(0, "cpu MHz", &cpu_speed_); + proc_info.GetSectionIntValue(0, "cache size", &cache_size_); + cache_size_ *= 1024; +#endif + } + // ProcCpuInfo reads cpu speed from "cpu MHz" under /proc/cpuinfo. + // But that number is a moving target which can change on-the-fly according to + // many factors including system workload. + // See /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors. + // The one in /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq is more + // accurate. We use it as our cpu speed when it is available. + // cpuinfo_max_freq is measured in KHz and requires conversion to MHz. + int max_freq = rtc::ReadCpuMaxFreq(); + if (max_freq > 0) { + cpu_speed_ = max_freq / 1000; + } +#endif +// For L2 CacheSize see also +// http://www.flounder.com/cpuid_explorer2.htm#CPUID(0x800000006) +#ifdef CPU_X86 + if (cache_size_ == 0) { + int cpu_info[4]; + __cpuid(cpu_info, 0x80000000); // query maximum extended cpuid function. + if (static_cast(cpu_info[0]) >= 0x80000006) { + __cpuid(cpu_info, 0x80000006); + cache_size_ = (cpu_info[2] >> 16) * 1024; + } + } +#endif +} + +// Return the number of cpu threads available to the system. +int SystemInfo::GetMaxCpus() { + return logical_cpus_; +} + +// Return the number of cpu cores available to the system. +int SystemInfo::GetMaxPhysicalCpus() { + return physical_cpus_; +} + +// Return the number of cpus available to the process. Since affinity can be +// changed on the fly, do not cache this value. +// Can be affected by heat. +int SystemInfo::GetCurCpus() { + int cur_cpus; +#if defined(WEBRTC_WIN) + DWORD_PTR process_mask, system_mask; + ::GetProcessAffinityMask(::GetCurrentProcess(), &process_mask, &system_mask); + for (cur_cpus = 0; process_mask; ++cur_cpus) { + // Sparse-ones algorithm. There are slightly faster methods out there but + // they are unintuitive and won't make a difference on a single dword. + process_mask &= (process_mask - 1); + } +#elif defined(WEBRTC_MAC) + uint32_t sysctl_value; + size_t length = sizeof(sysctl_value); + int error = sysctlbyname("hw.ncpu", &sysctl_value, &length, NULL, 0); + cur_cpus = !error ? static_cast(sysctl_value) : 1; +#else + // Linux, Solaris, WEBRTC_ANDROID + cur_cpus = static_cast(sysconf(_SC_NPROCESSORS_ONLN)); +#endif + return cur_cpus; +} + +// Return the type of this CPU. +SystemInfo::Architecture SystemInfo::GetCpuArchitecture() { + return cpu_arch_; +} + +// Returns the vendor string from the cpu, e.g. "GenuineIntel", "AuthenticAMD". +// See "Intel Processor Identification and the CPUID Instruction" +// (Intel document number: 241618) +std::string SystemInfo::GetCpuVendor() { + if (cpu_vendor_.empty()) { +#if defined(CPU_X86) + int cpu_info[4]; + __cpuid(cpu_info, 0); + cpu_info[0] = cpu_info[1]; // Reorder output + cpu_info[1] = cpu_info[3]; + // cpu_info[2] = cpu_info[2]; // Avoid -Werror=self-assign + cpu_info[3] = 0; + cpu_vendor_ = std::string(reinterpret_cast(&cpu_info[0])); +#elif defined(CPU_ARM) + cpu_vendor_ = std::string("ARM"); +#else + cpu_vendor_ = std::string("Undefined"); +#endif + } + return cpu_vendor_; +} + +int SystemInfo::GetCpuCacheSize() { + return cache_size_; +} + +// Return the "family" of this CPU. +int SystemInfo::GetCpuFamily() { + return cpu_family_; +} + +// Return the "model" of this CPU. +int SystemInfo::GetCpuModel() { + return cpu_model_; +} + +// Return the "stepping" of this CPU. +int SystemInfo::GetCpuStepping() { + return cpu_stepping_; +} + +// Return the clockrate of the primary processor in Mhz. This value can be +// cached. Returns -1 on error. +int SystemInfo::GetMaxCpuSpeed() { + if (cpu_speed_) { + return cpu_speed_; + } +#if defined(WEBRTC_WIN) + HKEY key; + static const WCHAR keyName[] = + L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"; + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName , 0, KEY_QUERY_VALUE, &key) + == ERROR_SUCCESS) { + DWORD data, len; + len = sizeof(data); + + if (RegQueryValueEx(key, L"~Mhz", 0, 0, reinterpret_cast(&data), + &len) == ERROR_SUCCESS) { + cpu_speed_ = data; + } else { + LOG(LS_WARNING) << "Failed to query registry value HKLM\\" << keyName + << "\\~Mhz"; + cpu_speed_ = -1; + } + + RegCloseKey(key); + } else { + LOG(LS_WARNING) << "Failed to open registry key HKLM\\" << keyName; + cpu_speed_ = -1; + } +#elif defined(WEBRTC_MAC) + uint64_t sysctl_value; + size_t length = sizeof(sysctl_value); + int error = sysctlbyname("hw.cpufrequency_max", &sysctl_value, &length, + NULL, 0); + cpu_speed_ = !error ? static_cast(sysctl_value/1000000) : -1; +#else + // TODO(fbarchard): Implement using proc/cpuinfo + cpu_speed_ = 0; +#endif + return cpu_speed_; +} + +// Dynamically check the current clockrate, which could be reduced because of +// powersaving profiles. Eventually for windows we want to query WMI for +// root\WMI::ProcessorPerformance.InstanceName="Processor_Number_0".frequency +int SystemInfo::GetCurCpuSpeed() { +#if defined(WEBRTC_WIN) + // TODO(fbarchard): Add WMI check, requires COM initialization + // NOTE(fbarchard): Testable on Sandy Bridge. + return GetMaxCpuSpeed(); +#elif defined(WEBRTC_MAC) + uint64_t sysctl_value; + size_t length = sizeof(sysctl_value); + int error = sysctlbyname("hw.cpufrequency", &sysctl_value, &length, NULL, 0); + return !error ? static_cast(sysctl_value/1000000) : GetMaxCpuSpeed(); +#else // WEBRTC_LINUX + // TODO(fbarchard): Use proc/cpuinfo for Cur speed on Linux. + return GetMaxCpuSpeed(); +#endif +} + +// Returns the amount of installed physical memory in Bytes. Cacheable. +// Returns -1 on error. +int64 SystemInfo::GetMemorySize() { + if (memory_) { + return memory_; + } + +#if defined(WEBRTC_WIN) + MEMORYSTATUSEX status = {0}; + status.dwLength = sizeof(status); + + if (GlobalMemoryStatusEx(&status)) { + memory_ = status.ullTotalPhys; + } else { + LOG_GLE(LS_WARNING) << "GlobalMemoryStatusEx failed."; + memory_ = -1; + } + +#elif defined(WEBRTC_MAC) + size_t len = sizeof(memory_); + int error = sysctlbyname("hw.memsize", &memory_, &len, NULL, 0); + if (error || memory_ == 0) { + memory_ = -1; + } +#else // WEBRTC_LINUX + memory_ = static_cast(sysconf(_SC_PHYS_PAGES)) * + static_cast(sysconf(_SC_PAGESIZE)); + if (memory_ < 0) { + LOG(LS_WARNING) << "sysconf(_SC_PHYS_PAGES) failed." + << "sysconf(_SC_PHYS_PAGES) " << sysconf(_SC_PHYS_PAGES) + << "sysconf(_SC_PAGESIZE) " << sysconf(_SC_PAGESIZE); + memory_ = -1; + } +#endif + + return memory_; +} + + +// Return the name of the machine model we are currently running on. +// This is a human readable string that consists of the name and version +// number of the hardware, i.e 'MacBookAir1,1'. Returns an empty string if +// model can not be determined. The string is cached for subsequent calls. +std::string SystemInfo::GetMachineModel() { + if (!machine_model_.empty()) { + return machine_model_; + } + +#if defined(WEBRTC_MAC) + char buffer[128]; + size_t length = sizeof(buffer); + int error = sysctlbyname("hw.model", buffer, &length, NULL, 0); + if (!error) { + machine_model_.assign(buffer, length - 1); + } else { + machine_model_.clear(); + } +#else + machine_model_ = "Not available"; +#endif + + return machine_model_; +} + +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +// Helper functions to query IOKit for video hardware properties. +static CFTypeRef SearchForProperty(io_service_t port, CFStringRef name) { + return IORegistryEntrySearchCFProperty(port, kIOServicePlane, + name, kCFAllocatorDefault, + kIORegistryIterateRecursively | kIORegistryIterateParents); +} + +static void GetProperty(io_service_t port, CFStringRef name, int* value) { + if (!value) return; + CFTypeRef ref = SearchForProperty(port, name); + if (ref) { + CFTypeID refType = CFGetTypeID(ref); + if (CFNumberGetTypeID() == refType) { + CFNumberRef number = reinterpret_cast(ref); + p_convertCFNumberToInt(number, value); + } else if (CFDataGetTypeID() == refType) { + CFDataRef data = reinterpret_cast(ref); + if (CFDataGetLength(data) == sizeof(UInt32)) { + *value = *reinterpret_cast(CFDataGetBytePtr(data)); + } + } + CFRelease(ref); + } +} + +static void GetProperty(io_service_t port, CFStringRef name, + std::string* value) { + if (!value) return; + CFTypeRef ref = SearchForProperty(port, name); + if (ref) { + CFTypeID refType = CFGetTypeID(ref); + if (CFStringGetTypeID() == refType) { + CFStringRef stringRef = reinterpret_cast(ref); + p_convertHostCFStringRefToCPPString(stringRef, *value); + } else if (CFDataGetTypeID() == refType) { + CFDataRef dataRef = reinterpret_cast(ref); + *value = std::string(reinterpret_cast( + CFDataGetBytePtr(dataRef)), CFDataGetLength(dataRef)); + } + CFRelease(ref); + } +} +#endif + +// Fills a struct with information on the graphics adapater and returns true +// iff successful. +bool SystemInfo::GetGpuInfo(GpuInfo *info) { + if (!info) return false; +#if defined(WEBRTC_WIN) && !defined(EXCLUDE_D3D9) + D3DADAPTER_IDENTIFIER9 identifier; + HRESULT hr = E_FAIL; + HINSTANCE d3d_lib = LoadLibrary(L"d3d9.dll"); + + if (d3d_lib) { + typedef IDirect3D9* (WINAPI *D3DCreate9Proc)(UINT); + D3DCreate9Proc d3d_create_proc = reinterpret_cast( + GetProcAddress(d3d_lib, "Direct3DCreate9")); + if (d3d_create_proc) { + IDirect3D9* d3d = d3d_create_proc(D3D_SDK_VERSION); + if (d3d) { + hr = d3d->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &identifier); + d3d->Release(); + } + } + FreeLibrary(d3d_lib); + } + + if (hr != D3D_OK) { + LOG(LS_ERROR) << "Failed to access Direct3D9 information."; + return false; + } + + info->device_name = identifier.DeviceName; + info->description = identifier.Description; + info->vendor_id = identifier.VendorId; + info->device_id = identifier.DeviceId; + info->driver = identifier.Driver; + // driver_version format: product.version.subversion.build + std::stringstream ss; + ss << HIWORD(identifier.DriverVersion.HighPart) << "." + << LOWORD(identifier.DriverVersion.HighPart) << "." + << HIWORD(identifier.DriverVersion.LowPart) << "." + << LOWORD(identifier.DriverVersion.LowPart); + info->driver_version = ss.str(); + return true; +#elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + // We'll query the IOKit for the gpu of the main display. + io_service_t display_service_port = CGDisplayIOServicePort( + kCGDirectMainDisplay); + GetProperty(display_service_port, CFSTR("vendor-id"), &info->vendor_id); + GetProperty(display_service_port, CFSTR("device-id"), &info->device_id); + GetProperty(display_service_port, CFSTR("model"), &info->description); + return true; +#else // WEBRTC_LINUX + // TODO(fbarchard): Implement this on Linux + return false; +#endif +} +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/systeminfo.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/systeminfo.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/systeminfo.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/systeminfo.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,81 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_SYSTEMINFO_H__ +#define WEBRTC_BASE_SYSTEMINFO_H__ + +#include + +#include "webrtc/base/basictypes.h" + +namespace rtc { + +class SystemInfo { + public: + enum Architecture { + SI_ARCH_UNKNOWN = -1, + SI_ARCH_X86 = 0, + SI_ARCH_X64 = 1, + SI_ARCH_ARM = 2 + }; + + SystemInfo(); + + // The number of CPU Cores in the system. + int GetMaxPhysicalCpus(); + // The number of CPU Threads in the system. + int GetMaxCpus(); + // The number of CPU Threads currently available to this process. + int GetCurCpus(); + // Identity of the CPUs. + Architecture GetCpuArchitecture(); + std::string GetCpuVendor(); + int GetCpuFamily(); + int GetCpuModel(); + int GetCpuStepping(); + // Return size of CPU cache in bytes. Uses largest available cache (L3). + int GetCpuCacheSize(); + // Estimated speed of the CPUs, in MHz. e.g. 2400 for 2.4 GHz + int GetMaxCpuSpeed(); + int GetCurCpuSpeed(); + // Total amount of physical memory, in bytes. + int64 GetMemorySize(); + // The model name of the machine, e.g. "MacBookAir1,1" + std::string GetMachineModel(); + + // The gpu identifier + struct GpuInfo { + GpuInfo() : vendor_id(0), device_id(0) {} + std::string device_name; + std::string description; + int vendor_id; + int device_id; + std::string driver; + std::string driver_version; + }; + bool GetGpuInfo(GpuInfo *info); + + private: + int physical_cpus_; + int logical_cpus_; + int cache_size_; + Architecture cpu_arch_; + std::string cpu_vendor_; + int cpu_family_; + int cpu_model_; + int cpu_stepping_; + int cpu_speed_; + int64 memory_; + std::string machine_model_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_SYSTEMINFO_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/systeminfo_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/systeminfo_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/systeminfo_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/systeminfo_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,194 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/stringutils.h" +#include "webrtc/base/systeminfo.h" + +#if defined(CPU_X86) || defined(CPU_ARM) +TEST(SystemInfoTest, CpuVendorNonEmpty) { + rtc::SystemInfo info; + LOG(LS_INFO) << "CpuVendor: " << info.GetCpuVendor(); + EXPECT_FALSE(info.GetCpuVendor().empty()); +} + +// Tests Vendor identification is Intel or AMD. +// See Also http://en.wikipedia.org/wiki/CPUID +TEST(SystemInfoTest, CpuVendorIntelAMDARM) { + rtc::SystemInfo info; +#if defined(CPU_X86) + EXPECT_TRUE(rtc::string_match(info.GetCpuVendor().c_str(), + "GenuineIntel") || + rtc::string_match(info.GetCpuVendor().c_str(), + "AuthenticAMD")); +#elif defined(CPU_ARM) + EXPECT_TRUE(rtc::string_match(info.GetCpuVendor().c_str(), "ARM")); +#endif +} +#endif // defined(CPU_X86) || defined(CPU_ARM) + +// Tests CpuArchitecture matches expectations. +TEST(SystemInfoTest, GetCpuArchitecture) { + rtc::SystemInfo info; + LOG(LS_INFO) << "CpuArchitecture: " << info.GetCpuArchitecture(); + rtc::SystemInfo::Architecture architecture = info.GetCpuArchitecture(); +#if defined(CPU_X86) || defined(CPU_ARM) + if (sizeof(intptr_t) == 8) { + EXPECT_EQ(rtc::SystemInfo::SI_ARCH_X64, architecture); + } else if (sizeof(intptr_t) == 4) { +#if defined(CPU_ARM) + EXPECT_EQ(rtc::SystemInfo::SI_ARCH_ARM, architecture); +#else + EXPECT_EQ(rtc::SystemInfo::SI_ARCH_X86, architecture); +#endif + } +#endif +} + +// Tests Cpu Cache Size +TEST(SystemInfoTest, CpuCacheSize) { + rtc::SystemInfo info; + LOG(LS_INFO) << "CpuCacheSize: " << info.GetCpuCacheSize(); + EXPECT_GE(info.GetCpuCacheSize(), 8192); // 8 KB min cache + EXPECT_LE(info.GetCpuCacheSize(), 1024 * 1024 * 1024); // 1 GB max cache +} + +// Tests MachineModel is set. On Mac test machine model is known. +TEST(SystemInfoTest, MachineModelKnown) { + rtc::SystemInfo info; + EXPECT_FALSE(info.GetMachineModel().empty()); + const char *machine_model = info.GetMachineModel().c_str(); + LOG(LS_INFO) << "MachineModel: " << machine_model; + bool known = true; +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + // Full list as of May 2012. Update when new OSX based models are added. + known = rtc::string_match(machine_model, "MacBookPro*") || + rtc::string_match(machine_model, "MacBookAir*") || + rtc::string_match(machine_model, "MacBook*") || + rtc::string_match(machine_model, "MacPro*") || + rtc::string_match(machine_model, "Macmini*") || + rtc::string_match(machine_model, "iMac*") || + rtc::string_match(machine_model, "Xserve*"); +#elif !defined(WEBRTC_IOS) + // All other machines return Not available. + known = rtc::string_match(info.GetMachineModel().c_str(), + "Not available"); +#endif + if (!known) { + LOG(LS_WARNING) << "Machine Model Unknown: " << machine_model; + } +} + +// Tests maximum cpu clockrate. +TEST(SystemInfoTest, CpuMaxCpuSpeed) { + rtc::SystemInfo info; + LOG(LS_INFO) << "MaxCpuSpeed: " << info.GetMaxCpuSpeed(); + EXPECT_GT(info.GetMaxCpuSpeed(), 0); + EXPECT_LT(info.GetMaxCpuSpeed(), 100000); // 100 Ghz +} + +// Tests current cpu clockrate. +TEST(SystemInfoTest, CpuCurCpuSpeed) { + rtc::SystemInfo info; + LOG(LS_INFO) << "MaxCurSpeed: " << info.GetCurCpuSpeed(); + EXPECT_GT(info.GetCurCpuSpeed(), 0); + EXPECT_LT(info.GetMaxCpuSpeed(), 100000); +} + +// Tests physical memory size. +TEST(SystemInfoTest, MemorySize) { + rtc::SystemInfo info; + LOG(LS_INFO) << "MemorySize: " << info.GetMemorySize(); + EXPECT_GT(info.GetMemorySize(), -1); +} + +// Tests number of logical cpus available to the system. +TEST(SystemInfoTest, MaxCpus) { + rtc::SystemInfo info; + LOG(LS_INFO) << "MaxCpus: " << info.GetMaxCpus(); + EXPECT_GT(info.GetMaxCpus(), 0); +} + +// Tests number of physical cpus available to the system. +TEST(SystemInfoTest, MaxPhysicalCpus) { + rtc::SystemInfo info; + LOG(LS_INFO) << "MaxPhysicalCpus: " << info.GetMaxPhysicalCpus(); + EXPECT_GT(info.GetMaxPhysicalCpus(), 0); + EXPECT_LE(info.GetMaxPhysicalCpus(), info.GetMaxCpus()); +} + +// Tests number of logical cpus available to the process. +TEST(SystemInfoTest, CurCpus) { + rtc::SystemInfo info; + LOG(LS_INFO) << "CurCpus: " << info.GetCurCpus(); + EXPECT_GT(info.GetCurCpus(), 0); + EXPECT_LE(info.GetCurCpus(), info.GetMaxCpus()); +} + +#ifdef CPU_X86 +// CPU family/model/stepping is only available on X86. The following tests +// that they are set when running on x86 CPUs. Valid Family/Model/Stepping +// values are non-zero on known CPUs. + +// Tests Intel CPU Family identification. +TEST(SystemInfoTest, CpuFamily) { + rtc::SystemInfo info; + LOG(LS_INFO) << "CpuFamily: " << info.GetCpuFamily(); + EXPECT_GT(info.GetCpuFamily(), 0); +} + +// Tests Intel CPU Model identification. +TEST(SystemInfoTest, CpuModel) { + rtc::SystemInfo info; + LOG(LS_INFO) << "CpuModel: " << info.GetCpuModel(); + EXPECT_GT(info.GetCpuModel(), 0); +} + +// Tests Intel CPU Stepping identification. +TEST(SystemInfoTest, CpuStepping) { + rtc::SystemInfo info; + LOG(LS_INFO) << "CpuStepping: " << info.GetCpuStepping(); + EXPECT_GT(info.GetCpuStepping(), 0); +} +#else // CPU_X86 +// If not running on x86 CPU the following tests expect the functions to +// return 0. +TEST(SystemInfoTest, CpuFamily) { + rtc::SystemInfo info; + LOG(LS_INFO) << "CpuFamily: " << info.GetCpuFamily(); + EXPECT_EQ(0, info.GetCpuFamily()); +} + +// Tests Intel CPU Model identification. +TEST(SystemInfoTest, CpuModel) { + rtc::SystemInfo info; + LOG(LS_INFO) << "CpuModel: " << info.GetCpuModel(); + EXPECT_EQ(0, info.GetCpuModel()); +} + +// Tests Intel CPU Stepping identification. +TEST(SystemInfoTest, CpuStepping) { + rtc::SystemInfo info; + LOG(LS_INFO) << "CpuStepping: " << info.GetCpuStepping(); + EXPECT_EQ(0, info.GetCpuStepping()); +} +#endif // CPU_X86 + +#if WEBRTC_WIN && !defined(EXCLUDE_D3D9) +TEST(SystemInfoTest, GpuInfo) { + rtc::SystemInfo info; + rtc::SystemInfo::GpuInfo gi; + EXPECT_TRUE(info.GetGpuInfo(&gi)); + LOG(LS_INFO) << "GpuDriver: " << gi.driver; + EXPECT_FALSE(gi.driver.empty()); + LOG(LS_INFO) << "GpuDriverVersion: " << gi.driver_version; + EXPECT_FALSE(gi.driver_version.empty()); +} +#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/task.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/task.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/task.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/task.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,272 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/task.h" +#include "webrtc/base/common.h" +#include "webrtc/base/taskrunner.h" + +namespace rtc { + +int32 Task::unique_id_seed_ = 0; + +Task::Task(TaskParent *parent) + : TaskParent(this, parent), + state_(STATE_INIT), + blocked_(false), + done_(false), + aborted_(false), + busy_(false), + error_(false), + start_time_(0), + timeout_time_(0), + timeout_seconds_(0), + timeout_suspended_(false) { + unique_id_ = unique_id_seed_++; + + // sanity check that we didn't roll-over our id seed + ASSERT(unique_id_ < unique_id_seed_); +} + +Task::~Task() { + // Is this task being deleted in the correct manner? + ASSERT(!done_ || GetRunner()->is_ok_to_delete(this)); + ASSERT(state_ == STATE_INIT || done_); + ASSERT(state_ == STATE_INIT || blocked_); + + // If the task is being deleted without being done, it + // means that it hasn't been removed from its parent. + // This happens if a task is deleted outside of TaskRunner. + if (!done_) { + Stop(); + } +} + +int64 Task::CurrentTime() { + return GetRunner()->CurrentTime(); +} + +int64 Task::ElapsedTime() { + return CurrentTime() - start_time_; +} + +void Task::Start() { + if (state_ != STATE_INIT) + return; + // Set the start time before starting the task. Otherwise if the task + // finishes quickly and deletes the Task object, setting start_time_ + // will crash. + start_time_ = CurrentTime(); + GetRunner()->StartTask(this); +} + +void Task::Step() { + if (done_) { +#ifdef _DEBUG + // we do not know how !blocked_ happens when done_ - should be impossible. + // But it causes problems, so in retail build, we force blocked_, and + // under debug we assert. + ASSERT(blocked_); +#else + blocked_ = true; +#endif + return; + } + + // Async Error() was called + if (error_) { + done_ = true; + state_ = STATE_ERROR; + blocked_ = true; +// obsolete - an errored task is not considered done now +// SignalDone(); + + Stop(); +#ifdef _DEBUG + // verify that stop removed this from its parent + ASSERT(!parent()->IsChildTask(this)); +#endif + return; + } + + busy_ = true; + int new_state = Process(state_); + busy_ = false; + + if (aborted_) { + Abort(true); // no need to wake because we're awake + return; + } + + if (new_state == STATE_BLOCKED) { + blocked_ = true; + // Let the timeout continue + } else { + state_ = new_state; + blocked_ = false; + ResetTimeout(); + } + + if (new_state == STATE_DONE) { + done_ = true; + } else if (new_state == STATE_ERROR) { + done_ = true; + error_ = true; + } + + if (done_) { +// obsolete - call this yourself +// SignalDone(); + + Stop(); +#if _DEBUG + // verify that stop removed this from its parent + ASSERT(!parent()->IsChildTask(this)); +#endif + blocked_ = true; + } +} + +void Task::Abort(bool nowake) { + // Why only check for done_ (instead of "aborted_ || done_")? + // + // If aborted_ && !done_, it means the logic for aborting still + // needs to be executed (because busy_ must have been true when + // Abort() was previously called). + if (done_) + return; + aborted_ = true; + if (!busy_) { + done_ = true; + blocked_ = true; + error_ = true; + + // "done_" is set before calling "Stop()" to ensure that this code + // doesn't execute more than once (recursively) for the same task. + Stop(); +#ifdef _DEBUG + // verify that stop removed this from its parent + ASSERT(!parent()->IsChildTask(this)); +#endif + if (!nowake) { + // WakeTasks to self-delete. + // Don't call Wake() because it is a no-op after "done_" is set. + // Even if Wake() did run, it clears "blocked_" which isn't desireable. + GetRunner()->WakeTasks(); + } + } +} + +void Task::Wake() { + if (done_) + return; + if (blocked_) { + blocked_ = false; + GetRunner()->WakeTasks(); + } +} + +void Task::Error() { + if (error_ || done_) + return; + error_ = true; + Wake(); +} + +std::string Task::GetStateName(int state) const { + switch (state) { + case STATE_BLOCKED: return "BLOCKED"; + case STATE_INIT: return "INIT"; + case STATE_START: return "START"; + case STATE_DONE: return "DONE"; + case STATE_ERROR: return "ERROR"; + case STATE_RESPONSE: return "RESPONSE"; + } + return "??"; +} + +int Task::Process(int state) { + int newstate = STATE_ERROR; + + if (TimedOut()) { + ClearTimeout(); + newstate = OnTimeout(); + SignalTimeout(); + } else { + switch (state) { + case STATE_INIT: + newstate = STATE_START; + break; + case STATE_START: + newstate = ProcessStart(); + break; + case STATE_RESPONSE: + newstate = ProcessResponse(); + break; + case STATE_DONE: + case STATE_ERROR: + newstate = STATE_BLOCKED; + break; + } + } + + return newstate; +} + +void Task::Stop() { + // No need to wake because we're either awake or in abort + TaskParent::OnStopped(this); +} + +void Task::set_timeout_seconds(const int timeout_seconds) { + timeout_seconds_ = timeout_seconds; + ResetTimeout(); +} + +bool Task::TimedOut() { + return timeout_seconds_ && + timeout_time_ && + CurrentTime() >= timeout_time_; +} + +void Task::ResetTimeout() { + int64 previous_timeout_time = timeout_time_; + bool timeout_allowed = (state_ != STATE_INIT) + && (state_ != STATE_DONE) + && (state_ != STATE_ERROR); + if (timeout_seconds_ && timeout_allowed && !timeout_suspended_) + timeout_time_ = CurrentTime() + + (timeout_seconds_ * kSecToMsec * kMsecTo100ns); + else + timeout_time_ = 0; + + GetRunner()->UpdateTaskTimeout(this, previous_timeout_time); +} + +void Task::ClearTimeout() { + int64 previous_timeout_time = timeout_time_; + timeout_time_ = 0; + GetRunner()->UpdateTaskTimeout(this, previous_timeout_time); +} + +void Task::SuspendTimeout() { + if (!timeout_suspended_) { + timeout_suspended_ = true; + ResetTimeout(); + } +} + +void Task::ResumeTimeout() { + if (timeout_suspended_) { + timeout_suspended_ = false; + ResetTimeout(); + } +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/task.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/task.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/task.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/task.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,177 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_TASK_H__ +#define WEBRTC_BASE_TASK_H__ + +#include +#include "webrtc/base/basictypes.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/sigslot.h" +#include "webrtc/base/taskparent.h" + +///////////////////////////////////////////////////////////////////// +// +// TASK +// +///////////////////////////////////////////////////////////////////// +// +// Task is a state machine infrastructure. States are pushed forward by +// pushing forwards a TaskRunner that holds on to all Tasks. The purpose +// of Task is threefold: +// +// (1) It manages ongoing work on the UI thread. Multitasking without +// threads, keeping it easy, keeping it real. :-) It does this by +// organizing a set of states for each task. When you return from your +// Process*() function, you return an integer for the next state. You do +// not go onto the next state yourself. Every time you enter a state, +// you check to see if you can do anything yet. If not, you return +// STATE_BLOCKED. If you _could_ do anything, do not return +// STATE_BLOCKED - even if you end up in the same state, return +// STATE_mysamestate. When you are done, return STATE_DONE and then the +// task will self-delete sometime afterwards. +// +// (2) It helps you avoid all those reentrancy problems when you chain +// too many triggers on one thread. Basically if you want to tell a task +// to process something for you, you feed your task some information and +// then you Wake() it. Don't tell it to process it right away. If it +// might be working on something as you send it information, you may want +// to have a queue in the task. +// +// (3) Finally it helps manage parent tasks and children. If a parent +// task gets aborted, all the children tasks are too. The nice thing +// about this, for example, is if you have one parent task that +// represents, say, and Xmpp connection, then you can spawn a whole bunch +// of infinite lifetime child tasks and now worry about cleaning them up. +// When the parent task goes to STATE_DONE, the task engine will make +// sure all those children are aborted and get deleted. +// +// Notice that Task has a few built-in states, e.g., +// +// STATE_INIT - the task isn't running yet +// STATE_START - the task is in its first state +// STATE_RESPONSE - the task is in its second state +// STATE_DONE - the task is done +// +// STATE_ERROR - indicates an error - we should audit the error code in +// light of any usage of it to see if it should be improved. When I +// first put down the task stuff I didn't have a good sense of what was +// needed for Abort and Error, and now the subclasses of Task will ground +// the design in a stronger way. +// +// STATE_NEXT - the first undefined state number. (like WM_USER) - you +// can start defining more task states there. +// +// When you define more task states, just override Process(int state) and +// add your own switch statement. If you want to delegate to +// Task::Process, you can effectively delegate to its switch statement. +// No fancy method pointers or such - this is all just pretty low tech, +// easy to debug, and fast. +// +// Also notice that Task has some primitive built-in timeout functionality. +// +// A timeout is defined as "the task stays in STATE_BLOCKED longer than +// timeout_seconds_." +// +// Descendant classes can override this behavior by calling the +// various protected methods to change the timeout behavior. For +// instance, a descendand might call SuspendTimeout() when it knows +// that it isn't waiting for anything that might timeout, but isn't +// yet in the STATE_DONE state. +// + +namespace rtc { + +// Executes a sequence of steps +class Task : public TaskParent { + public: + Task(TaskParent *parent); + virtual ~Task(); + + int32 unique_id() { return unique_id_; } + + void Start(); + void Step(); + int GetState() const { return state_; } + bool HasError() const { return (GetState() == STATE_ERROR); } + bool Blocked() const { return blocked_; } + bool IsDone() const { return done_; } + int64 ElapsedTime(); + + // Called from outside to stop task without any more callbacks + void Abort(bool nowake = false); + + bool TimedOut(); + + int64 timeout_time() const { return timeout_time_; } + int timeout_seconds() const { return timeout_seconds_; } + void set_timeout_seconds(int timeout_seconds); + + sigslot::signal0<> SignalTimeout; + + // Called inside the task to signal that the task may be unblocked + void Wake(); + + protected: + + enum { + STATE_BLOCKED = -1, + STATE_INIT = 0, + STATE_START = 1, + STATE_DONE = 2, + STATE_ERROR = 3, + STATE_RESPONSE = 4, + STATE_NEXT = 5, // Subclasses which need more states start here and higher + }; + + // Called inside to advise that the task should wake and signal an error + void Error(); + + int64 CurrentTime(); + + virtual std::string GetStateName(int state) const; + virtual int Process(int state); + virtual void Stop(); + virtual int ProcessStart() = 0; + virtual int ProcessResponse() { return STATE_DONE; } + + void ResetTimeout(); + void ClearTimeout(); + + void SuspendTimeout(); + void ResumeTimeout(); + + protected: + virtual int OnTimeout() { + // by default, we are finished after timing out + return STATE_DONE; + } + + private: + void Done(); + + int state_; + bool blocked_; + bool done_; + bool aborted_; + bool busy_; + bool error_; + int64 start_time_; + int64 timeout_time_; + int timeout_seconds_; + bool timeout_suspended_; + int32 unique_id_; + + static int32 unique_id_seed_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_TASK_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/taskparent.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/taskparent.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/taskparent.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/taskparent.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,95 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/taskparent.h" + +#include "webrtc/base/task.h" +#include "webrtc/base/taskrunner.h" + +namespace rtc { + +TaskParent::TaskParent(Task* derived_instance, TaskParent *parent) + : parent_(parent) { + ASSERT(derived_instance != NULL); + ASSERT(parent != NULL); + runner_ = parent->GetRunner(); + parent_->AddChild(derived_instance); + Initialize(); +} + +TaskParent::TaskParent(TaskRunner *derived_instance) + : parent_(NULL), + runner_(derived_instance) { + ASSERT(derived_instance != NULL); + Initialize(); +} + +// Does common initialization of member variables +void TaskParent::Initialize() { + children_.reset(new ChildSet()); + child_error_ = false; +} + +void TaskParent::AddChild(Task *child) { + children_->insert(child); +} + +#ifdef _DEBUG +bool TaskParent::IsChildTask(Task *task) { + ASSERT(task != NULL); + return task->parent_ == this && children_->find(task) != children_->end(); +} +#endif + +bool TaskParent::AllChildrenDone() { + for (ChildSet::iterator it = children_->begin(); + it != children_->end(); + ++it) { + if (!(*it)->IsDone()) + return false; + } + return true; +} + +bool TaskParent::AnyChildError() { + return child_error_; +} + +void TaskParent::AbortAllChildren() { + if (children_->size() > 0) { +#ifdef _DEBUG + runner_->IncrementAbortCount(); +#endif + + ChildSet copy = *children_; + for (ChildSet::iterator it = copy.begin(); it != copy.end(); ++it) { + (*it)->Abort(true); // Note we do not wake + } + +#ifdef _DEBUG + runner_->DecrementAbortCount(); +#endif + } +} + +void TaskParent::OnStopped(Task *task) { + AbortAllChildren(); + parent_->OnChildStopped(task); +} + +void TaskParent::OnChildStopped(Task *child) { + if (child->HasError()) + child_error_ = true; + children_->erase(child); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/taskparent.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/taskparent.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/taskparent.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/taskparent.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_TASKPARENT_H__ +#define WEBRTC_BASE_TASKPARENT_H__ + +#include + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/scoped_ptr.h" + +namespace rtc { + +class Task; +class TaskRunner; + +class TaskParent { + public: + TaskParent(Task *derived_instance, TaskParent *parent); + explicit TaskParent(TaskRunner *derived_instance); + virtual ~TaskParent() { } + + TaskParent *GetParent() { return parent_; } + TaskRunner *GetRunner() { return runner_; } + + bool AllChildrenDone(); + bool AnyChildError(); +#ifdef _DEBUG + bool IsChildTask(Task *task); +#endif + + protected: + void OnStopped(Task *task); + void AbortAllChildren(); + TaskParent *parent() { + return parent_; + } + + private: + void Initialize(); + void OnChildStopped(Task *child); + void AddChild(Task *child); + + TaskParent *parent_; + TaskRunner *runner_; + bool child_error_; + typedef std::set ChildSet; + scoped_ptr children_; + DISALLOW_EVIL_CONSTRUCTORS(TaskParent); +}; + + +} // namespace rtc + +#endif // WEBRTC_BASE_TASKPARENT_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/taskrunner.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/taskrunner.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/taskrunner.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/taskrunner.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,224 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/taskrunner.h" + +#include "webrtc/base/common.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/task.h" +#include "webrtc/base/logging.h" + +namespace rtc { + +TaskRunner::TaskRunner() + : TaskParent(this), + next_timeout_task_(NULL), + tasks_running_(false) +#ifdef _DEBUG + , abort_count_(0), + deleting_task_(NULL) +#endif +{ +} + +TaskRunner::~TaskRunner() { + // this kills and deletes children silently! + AbortAllChildren(); + InternalRunTasks(true); +} + +void TaskRunner::StartTask(Task * task) { + tasks_.push_back(task); + + // the task we just started could be about to timeout -- + // make sure our "next timeout task" is correct + UpdateTaskTimeout(task, 0); + + WakeTasks(); +} + +void TaskRunner::RunTasks() { + InternalRunTasks(false); +} + +void TaskRunner::InternalRunTasks(bool in_destructor) { + // This shouldn't run while an abort is happening. + // If that occurs, then tasks may be deleted in this method, + // but pointers to them will still be in the + // "ChildSet copy" in TaskParent::AbortAllChildren. + // Subsequent use of those task may cause data corruption or crashes. + ASSERT(!abort_count_); + // Running continues until all tasks are Blocked (ok for a small # of tasks) + if (tasks_running_) { + return; // don't reenter + } + + tasks_running_ = true; + + int64 previous_timeout_time = next_task_timeout(); + + int did_run = true; + while (did_run) { + did_run = false; + // use indexing instead of iterators because tasks_ may grow + for (size_t i = 0; i < tasks_.size(); ++i) { + while (!tasks_[i]->Blocked()) { + tasks_[i]->Step(); + did_run = true; + } + } + } + // Tasks are deleted when running has paused + bool need_timeout_recalc = false; + for (size_t i = 0; i < tasks_.size(); ++i) { + if (tasks_[i]->IsDone()) { + Task* task = tasks_[i]; + if (next_timeout_task_ && + task->unique_id() == next_timeout_task_->unique_id()) { + next_timeout_task_ = NULL; + need_timeout_recalc = true; + } + +#ifdef _DEBUG + deleting_task_ = task; +#endif + delete task; +#ifdef _DEBUG + deleting_task_ = NULL; +#endif + tasks_[i] = NULL; + } + } + // Finally, remove nulls + std::vector::iterator it; + it = std::remove(tasks_.begin(), + tasks_.end(), + reinterpret_cast(NULL)); + + tasks_.erase(it, tasks_.end()); + + if (need_timeout_recalc) + RecalcNextTimeout(NULL); + + // Make sure that adjustments are done to account + // for any timeout changes (but don't call this + // while being destroyed since it calls a pure virtual function). + if (!in_destructor) + CheckForTimeoutChange(previous_timeout_time); + + tasks_running_ = false; +} + +void TaskRunner::PollTasks() { + // see if our "next potentially timed-out task" has indeed timed out. + // If it has, wake it up, then queue up the next task in line + // Repeat while we have new timed-out tasks. + // TODO: We need to guard against WakeTasks not updating + // next_timeout_task_. Maybe also add documentation in the header file once + // we understand this code better. + Task* old_timeout_task = NULL; + while (next_timeout_task_ && + old_timeout_task != next_timeout_task_ && + next_timeout_task_->TimedOut()) { + old_timeout_task = next_timeout_task_; + next_timeout_task_->Wake(); + WakeTasks(); + } +} + +int64 TaskRunner::next_task_timeout() const { + if (next_timeout_task_) { + return next_timeout_task_->timeout_time(); + } + return 0; +} + +// this function gets called frequently -- when each task changes +// state to something other than DONE, ERROR or BLOCKED, it calls +// ResetTimeout(), which will call this function to make sure that +// the next timeout-able task hasn't changed. The logic in this function +// prevents RecalcNextTimeout() from getting called in most cases, +// effectively making the task scheduler O-1 instead of O-N + +void TaskRunner::UpdateTaskTimeout(Task* task, + int64 previous_task_timeout_time) { + ASSERT(task != NULL); + int64 previous_timeout_time = next_task_timeout(); + bool task_is_timeout_task = next_timeout_task_ != NULL && + task->unique_id() == next_timeout_task_->unique_id(); + if (task_is_timeout_task) { + previous_timeout_time = previous_task_timeout_time; + } + + // if the relevant task has a timeout, then + // check to see if it's closer than the current + // "about to timeout" task + if (task->timeout_time()) { + if (next_timeout_task_ == NULL || + (task->timeout_time() <= next_timeout_task_->timeout_time())) { + next_timeout_task_ = task; + } + } else if (task_is_timeout_task) { + // otherwise, if the task doesn't have a timeout, + // and it used to be our "about to timeout" task, + // walk through all the tasks looking for the real + // "about to timeout" task + RecalcNextTimeout(task); + } + + // Note when task_running_, then the running routine + // (TaskRunner::InternalRunTasks) is responsible for calling + // CheckForTimeoutChange. + if (!tasks_running_) { + CheckForTimeoutChange(previous_timeout_time); + } +} + +void TaskRunner::RecalcNextTimeout(Task *exclude_task) { + // walk through all the tasks looking for the one + // which satisfies the following: + // it's not finished already + // we're not excluding it + // it has the closest timeout time + + int64 next_timeout_time = 0; + next_timeout_task_ = NULL; + + for (size_t i = 0; i < tasks_.size(); ++i) { + Task *task = tasks_[i]; + // if the task isn't complete, and it actually has a timeout time + if (!task->IsDone() && (task->timeout_time() > 0)) + // if it doesn't match our "exclude" task + if (exclude_task == NULL || + exclude_task->unique_id() != task->unique_id()) + // if its timeout time is sooner than our current timeout time + if (next_timeout_time == 0 || + task->timeout_time() <= next_timeout_time) { + // set this task as our next-to-timeout + next_timeout_time = task->timeout_time(); + next_timeout_task_ = task; + } + } +} + +void TaskRunner::CheckForTimeoutChange(int64 previous_timeout_time) { + int64 next_timeout = next_task_timeout(); + bool timeout_change = (previous_timeout_time == 0 && next_timeout != 0) || + next_timeout < previous_timeout_time || + (previous_timeout_time <= CurrentTime() && + previous_timeout_time != next_timeout); + if (timeout_change) { + OnTimeoutChange(); + } +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/taskrunner.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/taskrunner.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/taskrunner.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/taskrunner.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,100 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_TASKRUNNER_H__ +#define WEBRTC_BASE_TASKRUNNER_H__ + +#include + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/sigslot.h" +#include "webrtc/base/taskparent.h" + +namespace rtc { +class Task; + +const int64 kSecToMsec = 1000; +const int64 kMsecTo100ns = 10000; +const int64 kSecTo100ns = kSecToMsec * kMsecTo100ns; + +class TaskRunner : public TaskParent, public sigslot::has_slots<> { + public: + TaskRunner(); + virtual ~TaskRunner(); + + virtual void WakeTasks() = 0; + + // Returns the current time in 100ns units. It is used for + // determining timeouts. The origin is not important, only + // the units and that rollover while the computer is running. + // + // On Windows, GetSystemTimeAsFileTime is the typical implementation. + virtual int64 CurrentTime() = 0 ; + + void StartTask(Task *task); + void RunTasks(); + void PollTasks(); + + void UpdateTaskTimeout(Task *task, int64 previous_task_timeout_time); + +#ifdef _DEBUG + bool is_ok_to_delete(Task* task) { + return task == deleting_task_; + } + + void IncrementAbortCount() { + ++abort_count_; + } + + void DecrementAbortCount() { + --abort_count_; + } +#endif + + // Returns the next absolute time when a task times out + // OR "0" if there is no next timeout. + int64 next_task_timeout() const; + + protected: + // The primary usage of this method is to know if + // a callback timer needs to be set-up or adjusted. + // This method will be called + // * when the next_task_timeout() becomes a smaller value OR + // * when next_task_timeout() has changed values and the previous + // value is in the past. + // + // If the next_task_timeout moves to the future, this method will *not* + // get called (because it subclass should check next_task_timeout() + // when its timer goes off up to see if it needs to set-up a new timer). + // + // Note that this maybe called conservatively. In that it may be + // called when no time change has happened. + virtual void OnTimeoutChange() { + // by default, do nothing. + } + + private: + void InternalRunTasks(bool in_destructor); + void CheckForTimeoutChange(int64 previous_timeout_time); + + std::vector tasks_; + Task *next_timeout_task_; + bool tasks_running_; +#ifdef _DEBUG + int abort_count_; + Task* deleting_task_; +#endif + + void RecalcNextTimeout(Task *exclude_task); +}; + +} // namespace rtc + +#endif // TASK_BASE_TASKRUNNER_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/task_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/task_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/task_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/task_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,546 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if defined(WEBRTC_POSIX) +#include +#endif // WEBRTC_POSIX + +// TODO: Remove this once the cause of sporadic failures in these +// tests is tracked down. +#include + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32.h" +#endif // WEBRTC_WIN + +#include "webrtc/base/common.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/task.h" +#include "webrtc/base/taskrunner.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/timeutils.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace rtc { + +static int64 GetCurrentTime() { + return static_cast(Time()) * 10000; +} + +// feel free to change these numbers. Note that '0' won't work, though +#define STUCK_TASK_COUNT 5 +#define HAPPY_TASK_COUNT 20 + +// this is a generic timeout task which, when it signals timeout, will +// include the unique ID of the task in the signal (we don't use this +// in production code because we haven't yet had occasion to generate +// an array of the same types of task) + +class IdTimeoutTask : public Task, public sigslot::has_slots<> { + public: + explicit IdTimeoutTask(TaskParent *parent) : Task(parent) { + SignalTimeout.connect(this, &IdTimeoutTask::OnLocalTimeout); + } + + sigslot::signal1 SignalTimeoutId; + sigslot::signal1 SignalDoneId; + + virtual int ProcessStart() { + return STATE_RESPONSE; + } + + void OnLocalTimeout() { + SignalTimeoutId(unique_id()); + } + + protected: + virtual void Stop() { + SignalDoneId(unique_id()); + Task::Stop(); + } +}; + +class StuckTask : public IdTimeoutTask { + public: + explicit StuckTask(TaskParent *parent) : IdTimeoutTask(parent) {} + virtual int ProcessStart() { + return STATE_BLOCKED; + } +}; + +class HappyTask : public IdTimeoutTask { + public: + explicit HappyTask(TaskParent *parent) : IdTimeoutTask(parent) { + time_to_perform_ = rand() % (STUCK_TASK_COUNT / 2); + } + virtual int ProcessStart() { + if (ElapsedTime() > (time_to_perform_ * 1000 * 10000)) + return STATE_RESPONSE; + else + return STATE_BLOCKED; + } + + private: + int time_to_perform_; +}; + +// simple implementation of a task runner which uses Windows' +// GetSystemTimeAsFileTime() to get the current clock ticks + +class MyTaskRunner : public TaskRunner { + public: + virtual void WakeTasks() { RunTasks(); } + virtual int64 CurrentTime() { + return GetCurrentTime(); + } + + bool timeout_change() const { + return timeout_change_; + } + + void clear_timeout_change() { + timeout_change_ = false; + } + protected: + virtual void OnTimeoutChange() { + timeout_change_ = true; + } + bool timeout_change_; +}; + +// +// this unit test is primarily concerned (for now) with the timeout +// functionality in tasks. It works as follows: +// +// * Create a bunch of tasks, some "stuck" (ie., guaranteed to timeout) +// and some "happy" (will immediately finish). +// * Set the timeout on the "stuck" tasks to some number of seconds between +// 1 and the number of stuck tasks +// * Start all the stuck & happy tasks in random order +// * Wait "number of stuck tasks" seconds and make sure everything timed out + +class TaskTest : public sigslot::has_slots<> { + public: + TaskTest() {} + + // no need to delete any tasks; the task runner owns them + ~TaskTest() {} + + void Start() { + // create and configure tasks + for (int i = 0; i < STUCK_TASK_COUNT; ++i) { + stuck_[i].task_ = new StuckTask(&task_runner_); + stuck_[i].task_->SignalTimeoutId.connect(this, + &TaskTest::OnTimeoutStuck); + stuck_[i].timed_out_ = false; + stuck_[i].xlat_ = stuck_[i].task_->unique_id(); + stuck_[i].task_->set_timeout_seconds(i + 1); + LOG(LS_INFO) << "Task " << stuck_[i].xlat_ << " created with timeout " + << stuck_[i].task_->timeout_seconds(); + } + + for (int i = 0; i < HAPPY_TASK_COUNT; ++i) { + happy_[i].task_ = new HappyTask(&task_runner_); + happy_[i].task_->SignalTimeoutId.connect(this, + &TaskTest::OnTimeoutHappy); + happy_[i].task_->SignalDoneId.connect(this, + &TaskTest::OnDoneHappy); + happy_[i].timed_out_ = false; + happy_[i].xlat_ = happy_[i].task_->unique_id(); + } + + // start all the tasks in random order + int stuck_index = 0; + int happy_index = 0; + for (int i = 0; i < STUCK_TASK_COUNT + HAPPY_TASK_COUNT; ++i) { + if ((stuck_index < STUCK_TASK_COUNT) && + (happy_index < HAPPY_TASK_COUNT)) { + if (rand() % 2 == 1) { + stuck_[stuck_index++].task_->Start(); + } else { + happy_[happy_index++].task_->Start(); + } + } else if (stuck_index < STUCK_TASK_COUNT) { + stuck_[stuck_index++].task_->Start(); + } else { + happy_[happy_index++].task_->Start(); + } + } + + for (int i = 0; i < STUCK_TASK_COUNT; ++i) { + std::cout << "Stuck task #" << i << " timeout is " << + stuck_[i].task_->timeout_seconds() << " at " << + stuck_[i].task_->timeout_time() << std::endl; + } + + // just a little self-check to make sure we started all the tasks + ASSERT_EQ(STUCK_TASK_COUNT, stuck_index); + ASSERT_EQ(HAPPY_TASK_COUNT, happy_index); + + // run the unblocked tasks + LOG(LS_INFO) << "Running tasks"; + task_runner_.RunTasks(); + + std::cout << "Start time is " << GetCurrentTime() << std::endl; + + // give all the stuck tasks time to timeout + for (int i = 0; !task_runner_.AllChildrenDone() && i < STUCK_TASK_COUNT; + ++i) { + Thread::Current()->ProcessMessages(1000); + for (int j = 0; j < HAPPY_TASK_COUNT; ++j) { + if (happy_[j].task_) { + happy_[j].task_->Wake(); + } + } + LOG(LS_INFO) << "Polling tasks"; + task_runner_.PollTasks(); + } + + // We see occasional test failures here due to the stuck tasks not having + // timed-out yet, which seems like it should be impossible. To help track + // this down we have added logging of the timing information, which we send + // directly to stdout so that we get it in opt builds too. + std::cout << "End time is " << GetCurrentTime() << std::endl; + } + + void OnTimeoutStuck(const int id) { + LOG(LS_INFO) << "Timed out task " << id; + + int i; + for (i = 0; i < STUCK_TASK_COUNT; ++i) { + if (stuck_[i].xlat_ == id) { + stuck_[i].timed_out_ = true; + stuck_[i].task_ = NULL; + break; + } + } + + // getting a bad ID here is a failure, but let's continue + // running to see what else might go wrong + EXPECT_LT(i, STUCK_TASK_COUNT); + } + + void OnTimeoutHappy(const int id) { + int i; + for (i = 0; i < HAPPY_TASK_COUNT; ++i) { + if (happy_[i].xlat_ == id) { + happy_[i].timed_out_ = true; + happy_[i].task_ = NULL; + break; + } + } + + // getting a bad ID here is a failure, but let's continue + // running to see what else might go wrong + EXPECT_LT(i, HAPPY_TASK_COUNT); + } + + void OnDoneHappy(const int id) { + int i; + for (i = 0; i < HAPPY_TASK_COUNT; ++i) { + if (happy_[i].xlat_ == id) { + happy_[i].task_ = NULL; + break; + } + } + + // getting a bad ID here is a failure, but let's continue + // running to see what else might go wrong + EXPECT_LT(i, HAPPY_TASK_COUNT); + } + + void check_passed() { + EXPECT_TRUE(task_runner_.AllChildrenDone()); + + // make sure none of our happy tasks timed out + for (int i = 0; i < HAPPY_TASK_COUNT; ++i) { + EXPECT_FALSE(happy_[i].timed_out_); + } + + // make sure all of our stuck tasks timed out + for (int i = 0; i < STUCK_TASK_COUNT; ++i) { + EXPECT_TRUE(stuck_[i].timed_out_); + if (!stuck_[i].timed_out_) { + std::cout << "Stuck task #" << i << " timeout is at " + << stuck_[i].task_->timeout_time() << std::endl; + } + } + + std::cout.flush(); + } + + private: + struct TaskInfo { + IdTimeoutTask *task_; + bool timed_out_; + int xlat_; + }; + + MyTaskRunner task_runner_; + TaskInfo stuck_[STUCK_TASK_COUNT]; + TaskInfo happy_[HAPPY_TASK_COUNT]; +}; + +TEST(start_task_test, Timeout) { + TaskTest task_test; + task_test.Start(); + task_test.check_passed(); +} + +// Test for aborting the task while it is running + +class AbortTask : public Task { + public: + explicit AbortTask(TaskParent *parent) : Task(parent) { + set_timeout_seconds(1); + } + + virtual int ProcessStart() { + Abort(); + return STATE_NEXT; + } + private: + DISALLOW_EVIL_CONSTRUCTORS(AbortTask); +}; + +class TaskAbortTest : public sigslot::has_slots<> { + public: + TaskAbortTest() {} + + // no need to delete any tasks; the task runner owns them + ~TaskAbortTest() {} + + void Start() { + Task *abort_task = new AbortTask(&task_runner_); + abort_task->SignalTimeout.connect(this, &TaskAbortTest::OnTimeout); + abort_task->Start(); + + // run the task + task_runner_.RunTasks(); + } + + private: + void OnTimeout() { + FAIL() << "Task timed out instead of aborting."; + } + + MyTaskRunner task_runner_; + DISALLOW_EVIL_CONSTRUCTORS(TaskAbortTest); +}; + +TEST(start_task_test, Abort) { + TaskAbortTest abort_test; + abort_test.Start(); +} + +// Test for aborting a task to verify that it does the Wake operation +// which gets it deleted. + +class SetBoolOnDeleteTask : public Task { + public: + SetBoolOnDeleteTask(TaskParent *parent, bool *set_when_deleted) + : Task(parent), + set_when_deleted_(set_when_deleted) { + EXPECT_TRUE(NULL != set_when_deleted); + EXPECT_FALSE(*set_when_deleted); + } + + virtual ~SetBoolOnDeleteTask() { + *set_when_deleted_ = true; + } + + virtual int ProcessStart() { + return STATE_BLOCKED; + } + + private: + bool* set_when_deleted_; + DISALLOW_EVIL_CONSTRUCTORS(SetBoolOnDeleteTask); +}; + +class AbortShouldWakeTest : public sigslot::has_slots<> { + public: + AbortShouldWakeTest() {} + + // no need to delete any tasks; the task runner owns them + ~AbortShouldWakeTest() {} + + void Start() { + bool task_deleted = false; + Task *task_to_abort = new SetBoolOnDeleteTask(&task_runner_, &task_deleted); + task_to_abort->Start(); + + // Task::Abort() should call TaskRunner::WakeTasks(). WakeTasks calls + // TaskRunner::RunTasks() immediately which should delete the task. + task_to_abort->Abort(); + EXPECT_TRUE(task_deleted); + + if (!task_deleted) { + // avoid a crash (due to referencing a local variable) + // if the test fails. + task_runner_.RunTasks(); + } + } + + private: + void OnTimeout() { + FAIL() << "Task timed out instead of aborting."; + } + + MyTaskRunner task_runner_; + DISALLOW_EVIL_CONSTRUCTORS(AbortShouldWakeTest); +}; + +TEST(start_task_test, AbortShouldWake) { + AbortShouldWakeTest abort_should_wake_test; + abort_should_wake_test.Start(); +} + +// Validate that TaskRunner's OnTimeoutChange gets called appropriately +// * When a task calls UpdateTaskTimeout +// * When the next timeout task time, times out +class TimeoutChangeTest : public sigslot::has_slots<> { + public: + TimeoutChangeTest() + : task_count_(ARRAY_SIZE(stuck_tasks_)) {} + + // no need to delete any tasks; the task runner owns them + ~TimeoutChangeTest() {} + + void Start() { + for (int i = 0; i < task_count_; ++i) { + stuck_tasks_[i] = new StuckTask(&task_runner_); + stuck_tasks_[i]->set_timeout_seconds(i + 2); + stuck_tasks_[i]->SignalTimeoutId.connect(this, + &TimeoutChangeTest::OnTimeoutId); + } + + for (int i = task_count_ - 1; i >= 0; --i) { + stuck_tasks_[i]->Start(); + } + task_runner_.clear_timeout_change(); + + // At this point, our timeouts are set as follows + // task[0] is 2 seconds, task[1] at 3 seconds, etc. + + stuck_tasks_[0]->set_timeout_seconds(2); + // Now, task[0] is 2 seconds, task[1] at 3 seconds... + // so timeout change shouldn't be called. + EXPECT_FALSE(task_runner_.timeout_change()); + task_runner_.clear_timeout_change(); + + stuck_tasks_[0]->set_timeout_seconds(1); + // task[0] is 1 seconds, task[1] at 3 seconds... + // The smallest timeout got smaller so timeout change be called. + EXPECT_TRUE(task_runner_.timeout_change()); + task_runner_.clear_timeout_change(); + + stuck_tasks_[1]->set_timeout_seconds(2); + // task[0] is 1 seconds, task[1] at 2 seconds... + // The smallest timeout is still 1 second so no timeout change. + EXPECT_FALSE(task_runner_.timeout_change()); + task_runner_.clear_timeout_change(); + + while (task_count_ > 0) { + int previous_count = task_count_; + task_runner_.PollTasks(); + if (previous_count != task_count_) { + // We only get here when a task times out. When that + // happens, the timeout change should get called because + // the smallest timeout is now in the past. + EXPECT_TRUE(task_runner_.timeout_change()); + task_runner_.clear_timeout_change(); + } + Thread::Current()->socketserver()->Wait(500, false); + } + } + + private: + void OnTimeoutId(const int id) { + for (int i = 0; i < ARRAY_SIZE(stuck_tasks_); ++i) { + if (stuck_tasks_[i] && stuck_tasks_[i]->unique_id() == id) { + task_count_--; + stuck_tasks_[i] = NULL; + break; + } + } + } + + MyTaskRunner task_runner_; + StuckTask* (stuck_tasks_[3]); + int task_count_; + DISALLOW_EVIL_CONSTRUCTORS(TimeoutChangeTest); +}; + +TEST(start_task_test, TimeoutChange) { + TimeoutChangeTest timeout_change_test; + timeout_change_test.Start(); +} + +class DeleteTestTaskRunner : public TaskRunner { + public: + DeleteTestTaskRunner() { + } + virtual void WakeTasks() { } + virtual int64 CurrentTime() { + return GetCurrentTime(); + } + private: + DISALLOW_EVIL_CONSTRUCTORS(DeleteTestTaskRunner); +}; + +TEST(unstarted_task_test, DeleteTask) { + // This test ensures that we don't + // crash if a task is deleted without running it. + DeleteTestTaskRunner task_runner; + HappyTask* happy_task = new HappyTask(&task_runner); + happy_task->Start(); + + // try deleting the task directly + HappyTask* child_happy_task = new HappyTask(happy_task); + delete child_happy_task; + + // run the unblocked tasks + task_runner.RunTasks(); +} + +TEST(unstarted_task_test, DoNotDeleteTask1) { + // This test ensures that we don't + // crash if a task runner is deleted without + // running a certain task. + DeleteTestTaskRunner task_runner; + HappyTask* happy_task = new HappyTask(&task_runner); + happy_task->Start(); + + HappyTask* child_happy_task = new HappyTask(happy_task); + child_happy_task->Start(); + + // Never run the tasks +} + +TEST(unstarted_task_test, DoNotDeleteTask2) { + // This test ensures that we don't + // crash if a taskrunner is delete with a + // task that has never been started. + DeleteTestTaskRunner task_runner; + HappyTask* happy_task = new HappyTask(&task_runner); + happy_task->Start(); + + // Do not start the task. + // Note: this leaks memory, so don't do this. + // Instead, always run your tasks or delete them. + new HappyTask(happy_task); + + // run the unblocked tasks + task_runner.RunTasks(); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/template_util.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/template_util.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/template_util.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/template_util.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,112 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_TEMPLATE_UTIL_H_ +#define WEBRTC_BASE_TEMPLATE_UTIL_H_ + +#include // For size_t. + +namespace rtc { + +// template definitions from tr1 + +template +struct integral_constant { + static const T value = v; + typedef T value_type; + typedef integral_constant type; +}; + +template const T integral_constant::value; + +typedef integral_constant true_type; +typedef integral_constant false_type; + +template struct is_pointer : false_type {}; +template struct is_pointer : true_type {}; + +template struct is_same : public false_type {}; +template struct is_same : true_type {}; + +template struct is_array : public false_type {}; +template struct is_array : public true_type {}; +template struct is_array : public true_type {}; + +template struct is_non_const_reference : false_type {}; +template struct is_non_const_reference : true_type {}; +template struct is_non_const_reference : false_type {}; + +template struct is_void : false_type {}; +template <> struct is_void : true_type {}; + +namespace internal { + +// Types YesType and NoType are guaranteed such that sizeof(YesType) < +// sizeof(NoType). +typedef char YesType; + +struct NoType { + YesType dummy[2]; +}; + +// This class is an implementation detail for is_convertible, and you +// don't need to know how it works to use is_convertible. For those +// who care: we declare two different functions, one whose argument is +// of type To and one with a variadic argument list. We give them +// return types of different size, so we can use sizeof to trick the +// compiler into telling us which function it would have chosen if we +// had called it with an argument of type From. See Alexandrescu's +// _Modern C++ Design_ for more details on this sort of trick. + +struct ConvertHelper { + template + static YesType Test(To); + + template + static NoType Test(...); + + template + static From& Create(); +}; + +// Used to determine if a type is a struct/union/class. Inspired by Boost's +// is_class type_trait implementation. +struct IsClassHelper { + template + static YesType Test(void(C::*)(void)); + + template + static NoType Test(...); +}; + +} // namespace internal + +// Inherits from true_type if From is convertible to To, false_type otherwise. +// +// Note that if the type is convertible, this will be a true_type REGARDLESS +// of whether or not the conversion would emit a warning. +template +struct is_convertible + : integral_constant( + internal::ConvertHelper::Create())) == + sizeof(internal::YesType)> { +}; + +template +struct is_class + : integral_constant(0)) == + sizeof(internal::YesType)> { +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_TEMPLATE_UTIL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/testbase64.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/testbase64.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/testbase64.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/testbase64.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,5 @@ +/* This file was generated by googleclient/talk/binary2header.sh */ + +static unsigned char testbase64[] = { +0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, 0x02, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xff, 0xe1, 0x0d, 0x07, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x01, 0x0e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x9e, 0x01, 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xbe, 0x01, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0xc3, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xcc, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd4, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x31, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xdc, 0x01, 0x32, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x3c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x04, 0x02, 0x13, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x02, 0xc4, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x53, 0x4f, 0x4e, 0x59, 0x00, 0x44, 0x53, 0x43, 0x2d, 0x50, 0x32, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20, 0x37, 0x2e, 0x30, 0x00, 0x32, 0x30, 0x30, 0x37, 0x3a, 0x30, 0x31, 0x3a, 0x33, 0x30, 0x20, 0x32, 0x33, 0x3a, 0x31, 0x30, 0x3a, 0x30, 0x34, 0x00, 0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x58, 0x20, 0x31, 0x30, 0x2e, 0x34, 0x2e, 0x38, 0x00, 0x00, 0x1c, 0x82, 0x9a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x6a, 0x82, 0x9d, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x72, 0x88, 0x22, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x88, 0x27, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x64, 0x00, 0x00, 0x90, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30, 0x32, 0x32, 0x30, 0x90, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x02, 0x7a, 0x90, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x02, 0x8e, 0x91, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x01, 0x02, 0x03, 0x00, 0x91, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0xa2, 0x92, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0xaa, 0x92, 0x05, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0xb2, 0x92, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x92, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92, 0x09, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x00, 0x92, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0xba, 0xa0, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30, 0x31, 0x30, 0x30, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0xa3, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0xa3, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0xa4, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x09, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x0a, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x01, 0x90, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x0a, 0x32, 0x30, 0x30, 0x37, 0x3a, 0x30, 0x31, 0x3a, 0x32, 0x30, 0x20, 0x32, 0x33, 0x3a, 0x30, 0x35, 0x3a, 0x35, 0x32, 0x00, 0x32, 0x30, 0x30, 0x37, 0x3a, 0x30, 0x31, 0x3a, 0x32, 0x30, 0x20, 0x32, 0x33, 0x3a, 0x30, 0x35, 0x3a, 0x35, 0x32, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x12, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x1a, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x22, 0x02, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x09, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, 0x02, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xff, 0xed, 0x00, 0x0c, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x5f, 0x43, 0x4d, 0x00, 0x02, 0xff, 0xee, 0x00, 0x0e, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x00, 0x64, 0x80, 0x00, 0x00, 0x00, 0x01, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0c, 0x08, 0x08, 0x08, 0x09, 0x08, 0x0c, 0x09, 0x09, 0x0c, 0x11, 0x0b, 0x0a, 0x0b, 0x11, 0x15, 0x0f, 0x0c, 0x0c, 0x0f, 0x15, 0x18, 0x13, 0x13, 0x15, 0x13, 0x13, 0x18, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x01, 0x0d, 0x0b, 0x0b, 0x0d, 0x0e, 0x0d, 0x10, 0x0e, 0x0e, 0x10, 0x14, 0x0e, 0x0e, 0x0e, 0x14, 0x14, 0x0e, 0x0e, 0x0e, 0x0e, 0x14, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x11, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0x64, 0x00, 0x64, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xdd, 0x00, 0x04, 0x00, 0x07, 0xff, 0xc4, 0x01, 0x3f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x02, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x01, 0x04, 0x01, 0x03, 0x02, 0x04, 0x02, 0x05, 0x07, 0x06, 0x08, 0x05, 0x03, 0x0c, 0x33, 0x01, 0x00, 0x02, 0x11, 0x03, 0x04, 0x21, 0x12, 0x31, 0x05, 0x41, 0x51, 0x61, 0x13, 0x22, 0x71, 0x81, 0x32, 0x06, 0x14, 0x91, 0xa1, 0xb1, 0x42, 0x23, 0x24, 0x15, 0x52, 0xc1, 0x62, 0x33, 0x34, 0x72, 0x82, 0xd1, 0x43, 0x07, 0x25, 0x92, 0x53, 0xf0, 0xe1, 0xf1, 0x63, 0x73, 0x35, 0x16, 0xa2, 0xb2, 0x83, 0x26, 0x44, 0x93, 0x54, 0x64, 0x45, 0xc2, 0xa3, 0x74, 0x36, 0x17, 0xd2, 0x55, 0xe2, 0x65, 0xf2, 0xb3, 0x84, 0xc3, 0xd3, 0x75, 0xe3, 0xf3, 0x46, 0x27, 0x94, 0xa4, 0x85, 0xb4, 0x95, 0xc4, 0xd4, 0xe4, 0xf4, 0xa5, 0xb5, 0xc5, 0xd5, 0xe5, 0xf5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6, 0xc6, 0xd6, 0xe6, 0xf6, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7, 0xc7, 0xd7, 0xe7, 0xf7, 0x11, 0x00, 0x02, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x06, 0x05, 0x35, 0x01, 0x00, 0x02, 0x11, 0x03, 0x21, 0x31, 0x12, 0x04, 0x41, 0x51, 0x61, 0x71, 0x22, 0x13, 0x05, 0x32, 0x81, 0x91, 0x14, 0xa1, 0xb1, 0x42, 0x23, 0xc1, 0x52, 0xd1, 0xf0, 0x33, 0x24, 0x62, 0xe1, 0x72, 0x82, 0x92, 0x43, 0x53, 0x15, 0x63, 0x73, 0x34, 0xf1, 0x25, 0x06, 0x16, 0xa2, 0xb2, 0x83, 0x07, 0x26, 0x35, 0xc2, 0xd2, 0x44, 0x93, 0x54, 0xa3, 0x17, 0x64, 0x45, 0x55, 0x36, 0x74, 0x65, 0xe2, 0xf2, 0xb3, 0x84, 0xc3, 0xd3, 0x75, 0xe3, 0xf3, 0x46, 0x94, 0xa4, 0x85, 0xb4, 0x95, 0xc4, 0xd4, 0xe4, 0xf4, 0xa5, 0xb5, 0xc5, 0xd5, 0xe5, 0xf5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6, 0xc6, 0xd6, 0xe6, 0xf6, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7, 0xc7, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xf2, 0xed, 0xb2, 0x8d, 0x4d, 0x45, 0xcd, 0x2f, 0x3f, 0x44, 0x68, 0x93, 0xc3, 0x58, 0xc8, 0xf1, 0x1f, 0x8a, 0x33, 0x86, 0xda, 0x58, 0xc1, 0xa0, 0x02, 0x4f, 0xc4, 0xa1, 0x69, 0xa5, 0x9b, 0x5b, 0x4b, 0x84, 0x73, 0xdf, 0xc9, 0x15, 0xf8, 0xe3, 0xd1, 0x0e, 0x07, 0x93, 0xf3, 0xd1, 0x0f, 0x1c, 0x17, 0xef, 0x2e, 0x3b, 0x5b, 0xdc, 0xff, 0x00, 0xdf, 0x42, 0xbf, 0x8f, 0x8e, 0xdc, 0x82, 0xca, 0xd8, 0x37, 0x11, 0xa9, 0x3d, 0x82, 0x69, 0x2b, 0xc4, 0x6d, 0xc9, 0x75, 0x25, 0xbc, 0xf7, 0xec, 0xa1, 0xb5, 0x74, 0x19, 0x5d, 0x2e, 0x8a, 0x9a, 0x4b, 0x89, 0x7d, 0xc4, 0x68, 0xc6, 0xf6, 0xfe, 0xb2, 0xa0, 0x30, 0x1d, 0x60, 0x86, 0x88, 0x8d, 0x49, 0x3e, 0x01, 0x11, 0x20, 0xa3, 0x8c, 0xb9, 0xb1, 0xaa, 0x62, 0xad, 0xbf, 0x18, 0x97, 0x43, 0x47, 0x1d, 0xd2, 0xaf, 0x04, 0xd9, 0xb8, 0xc8, 0x0d, 0x68, 0xe4, 0xf7, 0x3e, 0x48, 0xf1, 0x05, 0xbc, 0x25, 0xaa, 0x07, 0x71, 0xd9, 0x14, 0x78, 0xf6, 0x49, 0xb5, 0x90, 0xfd, 0xa7, 0xc6, 0x14, 0xfd, 0x1b, 0x1c, 0xff, 0x00, 0x4d, 0x8d, 0x2e, 0x73, 0x8c, 0x35, 0xa3, 0x52, 0x4f, 0x92, 0x48, 0xa6, 0x1a, 0x24, 0xb6, 0x2a, 0xfa, 0xa5, 0x9e, 0x60, 0x64, 0x39, 0x94, 0x13, 0xcb, 0x27, 0x73, 0x80, 0xf3, 0x0c, 0xf6, 0xff, 0x00, 0xd2, 0x5a, 0x78, 0xbf, 0x53, 0x71, 0xf6, 0x01, 0x75, 0xb6, 0x97, 0x6a, 0x25, 0xa1, 0xad, 0x1f, 0xf4, 0xb7, 0x23, 0x48, 0xb7, 0x94, 0x84, 0x97, 0x5b, 0xff, 0x00, 0x32, 0xa9, 0xdd, 0xfc, 0xed, 0x9b, 0x7e, 0x0d, 0x9e, 0x52, 0x4a, 0x95, 0x61, 0xff, 0xd0, 0xf3, 0x3b, 0xa7, 0x70, 0xee, 0x01, 0x8f, 0xb9, 0x59, 0xfa, 0x7e, 0xdf, 0xe4, 0xc8, 0xf9, 0x2a, 0xc2, 0x5c, 0x63, 0xc3, 0x54, 0x67, 0x87, 0x6e, 0x10, 0x35, 0x68, 0xd4, 0x79, 0x1e, 0x53, 0x4a, 0xe0, 0xdc, 0xe9, 0xb8, 0x1f, 0x6a, 0xda, 0x6c, 0x25, 0x94, 0x37, 0xb0, 0xd0, 0xb8, 0xad, 0x67, 0xe4, 0x55, 0x8a, 0x5b, 0x8b, 0x82, 0xc0, 0x6f, 0x76, 0x80, 0x34, 0x49, 0x05, 0x2e, 0x9e, 0xc6, 0x1c, 0x66, 0x31, 0xba, 0x10, 0x23, 0xe0, 0xaf, 0xe1, 0x61, 0x53, 0x43, 0x8d, 0x81, 0xb3, 0x67, 0xef, 0x9e, 0x49, 0x2a, 0x12, 0x6c, 0xb6, 0x63, 0x1a, 0x0c, 0x31, 0xba, 0x55, 0xcd, 0xac, 0xfa, 0x8e, 0xdf, 0x91, 0x6e, 0x91, 0xd9, 0xb3, 0xc9, 0x73, 0x90, 0x7a, 0xab, 0x6a, 0xc2, 0xa4, 0x60, 0xe2, 0x8f, 0xd2, 0x38, 0x03, 0x7d, 0x9e, 0x0d, 0xff, 0x00, 0xcc, 0xd6, 0xd3, 0x6b, 0x71, 0x67, 0xd2, 0x3e, 0x64, 0x72, 0xab, 0xdb, 0x8d, 0x54, 0x39, 0xc5, 0x83, 0x6b, 0x3d, 0xee, 0x2e, 0xd4, 0x92, 0x3c, 0x4a, 0x56, 0xba, 0xb4, 0x79, 0x5c, 0xf7, 0xb2, 0x96, 0x6c, 0x8d, 0xaf, 0x80, 0x48, 0x3c, 0xf0, 0xb2, 0x1f, 0x63, 0x9c, 0xe9, 0x3f, 0x24, 0x5c, 0xdb, 0xdd, 0x76, 0x43, 0xde, 0xfd, 0x5c, 0xe3, 0x24, 0xfc, 0x50, 0x00, 0x93, 0x0a, 0x78, 0x8a, 0x0d, 0x49, 0xca, 0xcf, 0x93, 0x63, 0x1b, 0x7d, 0xd7, 0x57, 0x50, 0xd5, 0xef, 0x70, 0x6b, 0x4f, 0xc7, 0x45, 0xdb, 0x74, 0x9e, 0x8d, 0x5e, 0x33, 0x83, 0xd8, 0x37, 0xdd, 0xc3, 0xac, 0x3d, 0xbf, 0x92, 0xc5, 0x5b, 0xea, 0xbf, 0xd5, 0x62, 0xc0, 0xdc, 0xbc, 0xbd, 0x2d, 0x22, 0x5a, 0xcf, 0xdd, 0x69, 0xff, 0x00, 0xd1, 0x8e, 0x5d, 0xa5, 0x38, 0xb5, 0xb0, 0x00, 0xc6, 0xc4, 0x24, 0x4a, 0xd6, 0x8d, 0x18, 0x04, 0x49, 0x88, 0x9e, 0x55, 0xd6, 0x61, 0xb0, 0xc1, 0x70, 0x32, 0xdd, 0x3c, 0x95, 0xda, 0xf1, 0xfe, 0xf5, 0x62, 0xbc, 0x76, 0x8e, 0x75, 0x28, 0x02, 0xa2, 0xe7, 0x7d, 0x92, 0xb9, 0x84, 0x96, 0x96, 0xda, 0xf7, 0x70, 0x12, 0x4e, 0x5a, 0xff, 0x00, 0xff, 0xd1, 0xf3, 0x7a, 0x21, 0xaf, 0xde, 0xef, 0xa2, 0x22, 0x55, 0xfc, 0x5a, 0xbd, 0x42, 0xfb, 0x08, 0xfa, 0x67, 0x4f, 0x82, 0xcd, 0x6d, 0x85, 0xc0, 0x56, 0x3b, 0x90, 0xb7, 0xf0, 0x2a, 0x0e, 0x63, 0x58, 0x3b, 0xf2, 0xa3, 0x9e, 0x8c, 0xb8, 0x86, 0xbe, 0x49, 0xf1, 0x2c, 0x0c, 0x86, 0xb4, 0x4c, 0x69, 0xe4, 0xaf, 0x6e, 0xcc, 0x6b, 0x7d, 0x46, 0xb3, 0x70, 0xec, 0x38, 0x51, 0x7d, 0x02, 0x8a, 0xc7, 0xa6, 0xd9, 0x20, 0x68, 0x0f, 0x8f, 0x8a, 0xcf, 0xc9, 0xc2, 0xea, 0x59, 0x5b, 0x48, 0xb0, 0x91, 0xae, 0xe6, 0xc9, 0x03, 0xc9, 0x30, 0x51, 0x66, 0xd4, 0x0d, 0xad, 0xbd, 0x5f, 0x53, 0xcc, 0x6b, 0xb6, 0x90, 0x5a, 0x3b, 0x83, 0x0b, 0x43, 0x17, 0x31, 0xd6, 0xc3, 0x6e, 0x12, 0x3b, 0x79, 0xac, 0xc1, 0x89, 0x47, 0xd9, 0xe8, 0x63, 0x98, 0x45, 0xed, 0x6c, 0x5a, 0xf1, 0xa0, 0x27, 0xc5, 0x5b, 0xc3, 0x6f, 0xa6, 0xe0, 0x1c, 0x7d, 0xb3, 0xa2, 0x69, 0x34, 0x7b, 0xae, 0x1a, 0x8d, 0x45, 0x17, 0x9d, 0xeb, 0xfd, 0x21, 0xd8, 0xb9, 0xae, 0xb5, 0x80, 0xbb, 0x1e, 0xd2, 0x5c, 0xd7, 0x78, 0x13, 0xf9, 0xae, 0x4b, 0xea, 0xc7, 0x4a, 0x39, 0xbd, 0x55, 0xb3, 0xed, 0x66, 0x38, 0xf5, 0x09, 0x22, 0x41, 0x23, 0xe8, 0x37, 0xfb, 0x4b, 0xa1, 0xeb, 0xd6, 0xfe, 0x88, 0x31, 0xbf, 0x41, 0xc0, 0xee, 0xd2, 0x74, 0x02, 0x78, 0x53, 0xfa, 0x97, 0x43, 0x19, 0x85, 0x65, 0xff, 0x00, 0x9d, 0x71, 0x33, 0xe4, 0x1a, 0x7d, 0x8d, 0x53, 0x42, 0x56, 0x35, 0x6b, 0xe5, 0x80, 0x06, 0xc7, 0x57, 0xa7, 0xc4, 0xa9, 0xdb, 0xb6, 0x81, 0x1f, 0xeb, 0xd9, 0x69, 0x56, 0xc2, 0xd0, 0x00, 0xe5, 0x55, 0xc0, 0x12, 0xc2, 0xd7, 0x4e, 0xa2, 0x5a, 0x7c, 0x0a, 0xd0, 0x63, 0x9a, 0xd1, 0xaf, 0xd2, 0xe2, 0x3c, 0x12, 0x62, 0x66, 0xc6, 0x42, 0x23, 0x5a, 0x49, 0x8f, 0x10, 0xa2, 0xd2, 0x3e, 0x28, 0x9d, 0xc4, 0x88, 0x09, 0x29, 0x16, 0xc3, 0x3c, 0x24, 0x8d, 0xe6, 0x92, 0x72, 0x1f, 0xff, 0xd2, 0xf3, 0xbb, 0xb0, 0xfe, 0xcb, 0x99, 0xe9, 0xce, 0xf6, 0x88, 0x2d, 0x77, 0x91, 0x5b, 0x3d, 0x3d, 0xd0, 0xe6, 0x90, 0xa9, 0x65, 0x57, 0x38, 0x95, 0xdd, 0xcb, 0x9a, 0x7d, 0xce, 0xf2, 0x3f, 0x44, 0x23, 0x60, 0x58, 0x76, 0xe9, 0xca, 0x8c, 0xea, 0x1b, 0x31, 0x02, 0x32, 0x23, 0xea, 0xee, 0xb1, 0xcd, 0xb0, 0xc7, 0x87, 0x74, 0x7a, 0xeb, 0x70, 0x1a, 0x71, 0xe1, 0xfe, 0xe4, 0x1c, 0x1d, 0xae, 0xe5, 0x69, 0xd8, 0xfa, 0x99, 0x50, 0x0d, 0x1a, 0xf7, 0x2a, 0x3a, 0x0c, 0xf4, 0x1a, 0x8e, 0xc7, 0x27, 0x5d, 0xbf, 0x18, 0x41, 0xdc, 0xc2, 0xf0, 0x7f, 0x74, 0xf6, 0x3a, 0x22, 0x66, 0xdb, 0x68, 0xc6, 0x80, 0x48, 0x6b, 0x88, 0x06, 0x39, 0x0d, 0xee, 0xaa, 0x1f, 0xb3, 0xd5, 0x1b, 0x83, 0xd8, 0x3b, 0x38, 0x8f, 0x69, 0xfe, 0xdf, 0xd1, 0x4d, 0x29, 0xa1, 0x4c, 0x7a, 0xf4, 0xbf, 0xa7, 0x92, 0xcf, 0xa5, 0x20, 0x08, 0xf3, 0xf6, 0xff, 0x00, 0x15, 0xbb, 0xd1, 0x31, 0xd9, 0x5e, 0x3d, 0x75, 0x56, 0x36, 0x88, 0x00, 0x81, 0xe0, 0x16, 0x5e, 0x55, 0x74, 0x3f, 0x00, 0x9d, 0xe0, 0xcc, 0x69, 0xe7, 0x3a, 0x2d, 0xbe, 0x90, 0x00, 0xa9, 0xae, 0xef, 0x1f, 0x95, 0x4b, 0x0d, 0x9a, 0xdc, 0xc7, 0x45, 0xfe, 0xb1, 0x7d, 0x60, 0xa7, 0xa1, 0xe0, 0x1f, 0x4e, 0x1d, 0x99, 0x69, 0x02, 0x9a, 0xcf, 0x1f, 0xca, 0x7b, 0xbf, 0x90, 0xc5, 0xc2, 0xb3, 0xeb, 0x57, 0xd6, 0x03, 0x6b, 0xae, 0x39, 0xb6, 0x82, 0xe3, 0x31, 0xa1, 0x68, 0xf2, 0x6b, 0x5c, 0x12, 0xfa, 0xe1, 0x91, 0x66, 0x47, 0x5d, 0xb8, 0x3b, 0x4f, 0x44, 0x36, 0xb6, 0x8f, 0x28, 0xdd, 0xff, 0x00, 0x7e, 0x46, 0xab, 0x12, 0x2b, 0x65, 0x55, 0x32, 0xa7, 0x62, 0xb6, 0xbd, 0xf7, 0x64, 0x10, 0xdb, 0x03, 0x9f, 0x1b, 0x9e, 0xc7, 0xd9, 0xb8, 0x3b, 0x1f, 0x67, 0xf3, 0x6c, 0x52, 0x80, 0xd7, 0x7d, 0x0f, 0xea, 0x7f, 0x5d, 0x1d, 0x67, 0xa6, 0x0b, 0x1e, 0x47, 0xda, 0x69, 0x3b, 0x2e, 0x03, 0xc7, 0xf3, 0x5f, 0x1f, 0xf0, 0x8b, 0xa1, 0x02, 0x46, 0xba, 0x79, 0xaf, 0x32, 0xff, 0x00, 0x16, 0xad, 0xca, 0x1d, 0x57, 0x2a, 0xdc, 0x79, 0x18, 0x41, 0xb0, 0xf6, 0x9e, 0xe4, 0x9f, 0xd0, 0x8f, 0xeb, 0x31, 0xab, 0xd2, 0x83, 0xa4, 0xcb, 0x8c, 0xb8, 0xa0, 0x42, 0x12, 0x7b, 0x67, 0x9f, 0x2f, 0xf5, 0x09, 0x26, 0x96, 0xc4, 0xce, 0xa9, 0x20, 0xa7, 0xff, 0xd3, 0xf3, 0x2f, 0xb4, 0x5d, 0xe9, 0x0a, 0xb7, 0x9f, 0x4c, 0x19, 0xdb, 0x3a, 0x2d, 0x5e, 0x94, 0xfd, 0xc4, 0xb7, 0xc5, 0x62, 0xf9, 0x2b, 0xfd, 0x2e, 0xe3, 0x5d, 0xe0, 0x7c, 0x13, 0x48, 0xd1, 0x92, 0x12, 0xa9, 0x0b, 0x7a, 0xbc, 0x2d, 0xc2, 0x7f, 0x92, 0x60, 0xab, 0x4e, 0x79, 0x2e, 0x00, 0xf0, 0xaa, 0xe1, 0xda, 0x3d, 0x43, 0xfc, 0xad, 0x55, 0xbb, 0x80, 0x79, 0x81, 0xa0, 0xe6, 0x54, 0x32, 0x6d, 0x02, 0xbe, 0xf3, 0x61, 0x81, 0xa8, 0x44, 0x14, 0x03, 0x59, 0x0e, 0x1c, 0xf6, 0x1f, 0xdc, 0xb2, 0xec, 0xa3, 0x23, 0x77, 0xe8, 0x6e, 0x70, 0xf2, 0x25, 0x1f, 0x1f, 0x17, 0xa9, 0x6d, 0x71, 0x36, 0x97, 0x47, 0x00, 0xa4, 0x02, 0xe0, 0x2c, 0x7c, 0xc1, 0xab, 0xd5, 0x31, 0x85, 0x35, 0xd4, 0xe6, 0x13, 0x02, 0xd6, 0x4b, 0x67, 0x48, 0x2b, 0xa9, 0xe9, 0x2e, 0x02, 0xb6, 0x4f, 0x82, 0xe5, 0x7a, 0x95, 0x19, 0xc6, 0x87, 0x3d, 0xfb, 0xa2, 0xb8, 0x79, 0x1e, 0x4d, 0x3b, 0x96, 0xcf, 0x4f, 0xbd, 0xcd, 0xa2, 0xa2, 0x1f, 0xa0, 0x82, 0xd3, 0xfc, 0x97, 0x05, 0x24, 0x36, 0x6b, 0xf3, 0x31, 0xa2, 0x35, 0x79, 0xef, 0xad, 0xf8, 0xae, 0xaf, 0xaf, 0xd8, 0xf2, 0xd8, 0x6d, 0xed, 0x6b, 0xda, 0x7b, 0x18, 0x1b, 0x5d, 0xff, 0x00, 0x52, 0xb1, 0x6d, 0xf0, 0x81, 0x31, 0xca, 0xf4, 0x6e, 0xb1, 0x80, 0xce, 0xb1, 0x84, 0xc0, 0x21, 0xb7, 0xd6, 0x77, 0x31, 0xd1, 0x27, 0xc1, 0xcd, 0xfe, 0xd2, 0xe3, 0xec, 0xe8, 0x1d, 0x45, 0x96, 0xb0, 0x9a, 0xb7, 0x87, 0x3f, 0x68, 0x2d, 0xf7, 0x01, 0x1f, 0xbe, 0xd1, 0xf4, 0x7f, 0xb4, 0xa4, 0x0d, 0x77, 0xbb, 0xfa, 0x8f, 0x80, 0x3a, 0x7f, 0x43, 0xaa, 0xe2, 0xdf, 0xd2, 0x65, 0x7e, 0x95, 0xe4, 0x0f, 0x1f, 0xa1, 0xfe, 0x6b, 0x16, 0x9f, 0x52, 0xfa, 0xc1, 0xd3, 0xba, 0x6d, 0x26, 0xdc, 0xac, 0x86, 0xd4, 0xd9, 0x0d, 0x31, 0x2e, 0x74, 0x9e, 0xdb, 0x59, 0x2e, 0x55, 0xe8, 0xc9, 0xb2, 0x96, 0xd5, 0x4b, 0x9f, 0xb8, 0x6d, 0xda, 0x1c, 0x04, 0x09, 0x03, 0xfe, 0x8a, 0xc6, 0xfa, 0xd3, 0xf5, 0x6a, 0xbe, 0xbb, 0x5b, 0x2e, 0xc6, 0xb5, 0x94, 0xe6, 0xd5, 0x20, 0x97, 0x7d, 0x1b, 0x1b, 0xf9, 0xad, 0x7c, 0x7d, 0x17, 0xb7, 0xf3, 0x1e, 0x92, 0x1b, 0x7f, 0xf8, 0xe0, 0x7d, 0x59, 0xdd, 0xfd, 0x32, 0xd8, 0x8f, 0xa5, 0xe8, 0x3a, 0x12, 0x5c, 0x3f, 0xfc, 0xc4, 0xfa, 0xc3, 0xb3, 0x77, 0xa7, 0x56, 0xed, 0xdb, 0x76, 0x7a, 0x8d, 0xdd, 0x1f, 0xbf, 0xfd, 0x44, 0x92, 0x56, 0x8f, 0xff, 0xd4, 0xf2, 0xe8, 0x86, 0x17, 0x1e, 0xfa, 0x04, 0x56, 0x4b, 0x43, 0x6c, 0x6f, 0x2d, 0xe5, 0x46, 0x01, 0x64, 0x2b, 0x14, 0x32, 0x5b, 0xb4, 0xa0, 0x52, 0x1d, 0xde, 0x9b, 0x94, 0xdb, 0xab, 0x6b, 0x81, 0xf7, 0x05, 0xb0, 0xd7, 0x07, 0xb2, 0x27, 0x55, 0xc6, 0x57, 0x65, 0xd8, 0x76, 0x6e, 0x64, 0xed, 0xee, 0x16, 0xce, 0x27, 0x57, 0x63, 0xda, 0x0c, 0xc2, 0x8e, 0x51, 0x67, 0x84, 0xfa, 0x1d, 0xdd, 0x62, 0xc7, 0x07, 0xe9, 0xf7, 0xa3, 0xd6, 0x6c, 0x02, 0x41, 0x55, 0x31, 0xf3, 0x2b, 0xb3, 0xba, 0x2b, 0x2e, 0x68, 0x24, 0x1d, 0x47, 0x64, 0xca, 0xa6, 0x50, 0x41, 0x65, 0x90, 0x6c, 0xb1, 0xa5, 0xae, 0x33, 0x23, 0x51, 0xe4, 0xab, 0x7d, 0x5d, 0xcb, 0xb6, 0xcc, 0x37, 0xd0, 0x40, 0x73, 0x71, 0xde, 0x58, 0x09, 0xe7, 0x6f, 0x2c, 0x44, 0xc9, 0xc9, 0xae, 0xba, 0x9d, 0x63, 0x88, 0x01, 0xa0, 0x95, 0x9d, 0xf5, 0x3f, 0x2a, 0xe6, 0x67, 0xdb, 0x50, 0x83, 0x55, 0xad, 0x36, 0x3e, 0x78, 0x10, 0x74, 0x77, 0xfd, 0x2d, 0xaa, 0x4c, 0x7d, 0x58, 0x73, 0x91, 0xa0, 0x0f, 0x51, 0x45, 0xb7, 0x33, 0xdd, 0x58, 0x69, 0x1d, 0xd8, 0x0c, 0x9f, 0x96, 0x88, 0x19, 0x99, 0x19, 0xac, 0xcf, 0xa3, 0xd2, 0xad, 0xb5, 0xdb, 0x76, 0x8f, 0xad, 0xc4, 0xea, 0xcf, 0xdf, 0x7e, 0xdf, 0xdd, 0xfc, 0xd5, 0xa3, 0x5e, 0x43, 0x2b, 0x6b, 0xb2, 0xad, 0x3b, 0x6a, 0xa4, 0x13, 0xa7, 0x04, 0xac, 0x7a, 0x6f, 0xb3, 0x23, 0x26, 0xcc, 0xfb, 0xb4, 0x75, 0x8e, 0x01, 0x83, 0xf7, 0x58, 0x3e, 0x8b, 0x53, 0xa7, 0x2a, 0x1a, 0x31, 0x42, 0x36, 0x5d, 0x4c, 0x9a, 0xf2, 0xdc, 0xc6, 0xfe, 0x98, 0xb4, 0x34, 0xcb, 0x48, 0x0a, 0x8f, 0xdb, 0xb2, 0xeb, 0x76, 0xd6, 0x07, 0x5c, 0x59, 0xc9, 0x64, 0x8f, 0x93, 0xa7, 0x73, 0x16, 0x83, 0xaf, 0x0e, 0xa4, 0x33, 0xef, 0x50, 0xc5, 0x0c, 0xda, 0x59, 0x10, 0x06, 0x8a, 0x2e, 0x29, 0x0e, 0xac, 0xc2, 0x31, 0x3d, 0x36, 0x69, 0x7e, 0xd6, 0xcc, 0xf5, 0x3d, 0x6f, 0xb3, 0xeb, 0x1b, 0x76, 0xef, 0x3b, 0xa3, 0xfa, 0xc9, 0x2b, 0x5f, 0x66, 0x6f, 0xa9, 0x1e, 0x73, 0xf2, 0x49, 0x2e, 0x39, 0xf7, 0x4f, 0xb7, 0x8d, 0xff, 0xd5, 0xf3, 0x26, 0xfe, 0x0a, 0xc5, 0x1b, 0xa7, 0xcb, 0xb2, 0xcf, 0x49, 0x03, 0xb2, 0x46, 0xee, 0xd9, 0xd9, 0xb3, 0xf4, 0x9f, 0x25, 0x4a, 0xdf, 0x4b, 0x77, 0xe8, 0x27, 0xd4, 0xef, 0x1c, 0x2a, 0x29, 0x26, 0xc5, 0x7c, 0x9d, 0x6c, 0x7f, 0xb7, 0x6e, 0x1b, 0x26, 0x7f, 0x05, 0xa3, 0xfe, 0x53, 0x8d, 0x62, 0x57, 0x30, 0x92, 0x12, 0xfa, 0x2f, 0x86, 0xdf, 0xa4, 0xec, 0x67, 0xfe, 0xd0, 0xf4, 0xff, 0x00, 0x4d, 0xfc, 0xdf, 0x78, 0xe1, 0x68, 0x7d, 0x54, 0x99, 0xbf, 0x6f, 0xf3, 0xbe, 0xdf, 0x8e, 0xdd, 0x7f, 0xef, 0xeb, 0x97, 0x49, 0x3e, 0x3b, 0x7f, 0x06, 0x2c, 0x9f, 0x37, 0x5f, 0xf0, 0x9f, 0x4c, 0xeb, 0x7b, 0xbf, 0x67, 0x55, 0xe8, 0xff, 0x00, 0x31, 0xbc, 0x7a, 0x9e, 0x31, 0xdb, 0xfe, 0x92, 0xae, 0x37, 0x7a, 0x4d, 0xdb, 0xe2, 0x17, 0x9d, 0xa4, 0xa3, 0xc9, 0xba, 0xfc, 0x7b, 0x7d, 0x5f, 0x52, 0xa7, 0x7e, 0xd1, 0x28, 0xf8, 0xf3, 0xb0, 0xc7, 0x32, 0xbc, 0x99, 0x24, 0xc5, 0xe3, 0xab, 0xeb, 0x1f, 0xa4, 0xf5, 0xfc, 0xe1, 0x25, 0xe4, 0xe9, 0x24, 0x97, 0xff, 0xd9, 0xff, 0xed, 0x2e, 0x1c, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20, 0x33, 0x2e, 0x30, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x1c, 0x02, 0x00, 0x00, 0x02, 0x00, 0x02, 0x1c, 0x02, 0x78, 0x00, 0x1f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfb, 0x09, 0xa6, 0xbd, 0x07, 0x4c, 0x2a, 0x36, 0x9d, 0x8f, 0xe2, 0xcc, 0x57, 0xa9, 0xac, 0x85, 0x38, 0x42, 0x49, 0x4d, 0x03, 0xea, 0x00, 0x00, 0x00, 0x00, 0x1d, 0xb0, 0x3c, 0x3f, 0x78, 0x6d, 0x6c, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x2e, 0x30, 0x22, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x3d, 0x22, 0x55, 0x54, 0x46, 0x2d, 0x38, 0x22, 0x3f, 0x3e, 0x0a, 0x3c, 0x21, 0x44, 0x4f, 0x43, 0x54, 0x59, 0x50, 0x45, 0x20, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x20, 0x22, 0x2d, 0x2f, 0x2f, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x2f, 0x44, 0x54, 0x44, 0x20, 0x50, 0x4c, 0x49, 0x53, 0x54, 0x20, 0x31, 0x2e, 0x30, 0x2f, 0x2f, 0x45, 0x4e, 0x22, 0x20, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x54, 0x44, 0x73, 0x2f, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x2d, 0x31, 0x2e, 0x30, 0x2e, 0x64, 0x74, 0x64, 0x22, 0x3e, 0x0a, 0x3c, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x48, 0x6f, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x48, 0x6f, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x31, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x53, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x53, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x31, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x56, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x56, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x56, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x56, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x31, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x73, 0x75, 0x62, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x70, 0x61, 0x70, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x41, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x63, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x41, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x63, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x30, 0x2e, 0x30, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x30, 0x2e, 0x30, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x37, 0x33, 0x34, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x35, 0x37, 0x36, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x41, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x70, 0x65, 0x72, 0x52, 0x65, 0x63, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x41, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x70, 0x65, 0x72, 0x52, 0x65, 0x63, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x2d, 0x31, 0x38, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x2d, 0x31, 0x38, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x37, 0x37, 0x34, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x35, 0x39, 0x34, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x4d, 0x50, 0x61, 0x70, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x70, 0x6d, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x4d, 0x50, 0x61, 0x70, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x6e, 0x61, 0x2d, 0x6c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x70, 0x6d, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x33, 0x2d, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x54, 0x31, 0x37, 0x3a, 0x34, 0x39, 0x3a, 0x33, 0x36, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x31, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x4d, 0x55, 0x6e, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x63, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x70, 0x6d, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x4d, 0x55, 0x6e, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x63, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x30, 0x2e, 0x30, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x30, 0x2e, 0x30, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x37, 0x33, 0x34, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x35, 0x37, 0x36, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x4d, 0x55, 0x6e, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x70, 0x65, 0x72, 0x52, 0x65, 0x63, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x70, 0x6d, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x4d, 0x55, 0x6e, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x70, 0x65, 0x72, 0x52, 0x65, 0x63, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x2d, 0x31, 0x38, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x2d, 0x31, 0x38, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x37, 0x37, 0x34, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x35, 0x39, 0x34, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x70, 0x70, 0x64, 0x2e, 0x50, 0x4d, 0x50, 0x61, 0x70, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x70, 0x6d, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x70, 0x70, 0x64, 0x2e, 0x50, 0x4d, 0x50, 0x61, 0x70, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x55, 0x53, 0x20, 0x4c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x70, 0x6d, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x33, 0x2d, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x54, 0x31, 0x37, 0x3a, 0x34, 0x39, 0x3a, 0x33, 0x36, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x31, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x41, 0x50, 0x49, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x30, 0x30, 0x2e, 0x32, 0x30, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2f, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x41, 0x50, 0x49, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x30, 0x30, 0x2e, 0x32, 0x30, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2f, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x3c, 0x2f, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x3e, 0x0a, 0x38, 0x42, 0x49, 0x4d, 0x03, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x00, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02, 0xde, 0x02, 0x40, 0xff, 0xee, 0xff, 0xee, 0x03, 0x06, 0x02, 0x52, 0x03, 0x67, 0x05, 0x28, 0x03, 0xfc, 0x00, 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02, 0xd8, 0x02, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x01, 0x7f, 0xff, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x08, 0x00, 0x19, 0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x03, 0xed, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x80, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1e, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1e, 0x38, 0x42, 0x49, 0x4d, 0x03, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x27, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x42, 0x49, 0x4d, 0x03, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x2f, 0x66, 0x66, 0x00, 0x01, 0x00, 0x6c, 0x66, 0x66, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x2f, 0x66, 0x66, 0x00, 0x01, 0x00, 0xa1, 0x99, 0x9a, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x35, 0x00, 0x00, 0x00, 0x01, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x42, 0x49, 0x4d, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe8, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x03, 0x45, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x08, 0x00, 0x44, 0x00, 0x53, 0x00, 0x43, 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x32, 0x00, 0x35, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x75, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x4f, 0x62, 0x6a, 0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x63, 0x74, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x54, 0x6f, 0x70, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x66, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x74, 0x6f, 0x6d, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x52, 0x67, 0x68, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x06, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x56, 0x6c, 0x4c, 0x73, 0x00, 0x00, 0x00, 0x01, 0x4f, 0x62, 0x6a, 0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x07, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x49, 0x44, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x65, 0x6e, 0x75, 0x6d, 0x00, 0x00, 0x00, 0x0c, 0x45, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x6f, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x00, 0x00, 0x00, 0x00, 0x54, 0x79, 0x70, 0x65, 0x65, 0x6e, 0x75, 0x6d, 0x00, 0x00, 0x00, 0x0a, 0x45, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x00, 0x00, 0x00, 0x00, 0x49, 0x6d, 0x67, 0x20, 0x00, 0x00, 0x00, 0x06, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x4f, 0x62, 0x6a, 0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x63, 0x74, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x54, 0x6f, 0x70, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x66, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x74, 0x6f, 0x6d, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x52, 0x67, 0x68, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, 0x6c, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x75, 0x6c, 0x6c, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x73, 0x67, 0x65, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x61, 0x6c, 0x74, 0x54, 0x61, 0x67, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x63, 0x65, 0x6c, 0x6c, 0x54, 0x65, 0x78, 0x74, 0x49, 0x73, 0x48, 0x54, 0x4d, 0x4c, 0x62, 0x6f, 0x6f, 0x6c, 0x01, 0x00, 0x00, 0x00, 0x08, 0x63, 0x65, 0x6c, 0x6c, 0x54, 0x65, 0x78, 0x74, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x68, 0x6f, 0x72, 0x7a, 0x41, 0x6c, 0x69, 0x67, 0x6e, 0x65, 0x6e, 0x75, 0x6d, 0x00, 0x00, 0x00, 0x0f, 0x45, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x48, 0x6f, 0x72, 0x7a, 0x41, 0x6c, 0x69, 0x67, 0x6e, 0x00, 0x00, 0x00, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x00, 0x09, 0x76, 0x65, 0x72, 0x74, 0x41, 0x6c, 0x69, 0x67, 0x6e, 0x65, 0x6e, 0x75, 0x6d, 0x00, 0x00, 0x00, 0x0f, 0x45, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x56, 0x65, 0x72, 0x74, 0x41, 0x6c, 0x69, 0x67, 0x6e, 0x00, 0x00, 0x00, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x00, 0x0b, 0x62, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x65, 0x6e, 0x75, 0x6d, 0x00, 0x00, 0x00, 0x11, 0x45, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x42, 0x47, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x09, 0x74, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x6c, 0x65, 0x66, 0x74, 0x4f, 0x75, 0x74, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x4f, 0x75, 0x74, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x72, 0x69, 0x67, 0x68, 0x74, 0x4f, 0x75, 0x74, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x09, 0xf9, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x75, 0x30, 0x00, 0x00, 0x09, 0xdd, 0x00, 0x18, 0x00, 0x01, 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, 0x02, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xff, 0xed, 0x00, 0x0c, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x5f, 0x43, 0x4d, 0x00, 0x02, 0xff, 0xee, 0x00, 0x0e, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x00, 0x64, 0x80, 0x00, 0x00, 0x00, 0x01, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0c, 0x08, 0x08, 0x08, 0x09, 0x08, 0x0c, 0x09, 0x09, 0x0c, 0x11, 0x0b, 0x0a, 0x0b, 0x11, 0x15, 0x0f, 0x0c, 0x0c, 0x0f, 0x15, 0x18, 0x13, 0x13, 0x15, 0x13, 0x13, 0x18, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x01, 0x0d, 0x0b, 0x0b, 0x0d, 0x0e, 0x0d, 0x10, 0x0e, 0x0e, 0x10, 0x14, 0x0e, 0x0e, 0x0e, 0x14, 0x14, 0x0e, 0x0e, 0x0e, 0x0e, 0x14, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x11, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0x64, 0x00, 0x64, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xdd, 0x00, 0x04, 0x00, 0x07, 0xff, 0xc4, 0x01, 0x3f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x02, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x01, 0x04, 0x01, 0x03, 0x02, 0x04, 0x02, 0x05, 0x07, 0x06, 0x08, 0x05, 0x03, 0x0c, 0x33, 0x01, 0x00, 0x02, 0x11, 0x03, 0x04, 0x21, 0x12, 0x31, 0x05, 0x41, 0x51, 0x61, 0x13, 0x22, 0x71, 0x81, 0x32, 0x06, 0x14, 0x91, 0xa1, 0xb1, 0x42, 0x23, 0x24, 0x15, 0x52, 0xc1, 0x62, 0x33, 0x34, 0x72, 0x82, 0xd1, 0x43, 0x07, 0x25, 0x92, 0x53, 0xf0, 0xe1, 0xf1, 0x63, 0x73, 0x35, 0x16, 0xa2, 0xb2, 0x83, 0x26, 0x44, 0x93, 0x54, 0x64, 0x45, 0xc2, 0xa3, 0x74, 0x36, 0x17, 0xd2, 0x55, 0xe2, 0x65, 0xf2, 0xb3, 0x84, 0xc3, 0xd3, 0x75, 0xe3, 0xf3, 0x46, 0x27, 0x94, 0xa4, 0x85, 0xb4, 0x95, 0xc4, 0xd4, 0xe4, 0xf4, 0xa5, 0xb5, 0xc5, 0xd5, 0xe5, 0xf5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6, 0xc6, 0xd6, 0xe6, 0xf6, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7, 0xc7, 0xd7, 0xe7, 0xf7, 0x11, 0x00, 0x02, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x06, 0x05, 0x35, 0x01, 0x00, 0x02, 0x11, 0x03, 0x21, 0x31, 0x12, 0x04, 0x41, 0x51, 0x61, 0x71, 0x22, 0x13, 0x05, 0x32, 0x81, 0x91, 0x14, 0xa1, 0xb1, 0x42, 0x23, 0xc1, 0x52, 0xd1, 0xf0, 0x33, 0x24, 0x62, 0xe1, 0x72, 0x82, 0x92, 0x43, 0x53, 0x15, 0x63, 0x73, 0x34, 0xf1, 0x25, 0x06, 0x16, 0xa2, 0xb2, 0x83, 0x07, 0x26, 0x35, 0xc2, 0xd2, 0x44, 0x93, 0x54, 0xa3, 0x17, 0x64, 0x45, 0x55, 0x36, 0x74, 0x65, 0xe2, 0xf2, 0xb3, 0x84, 0xc3, 0xd3, 0x75, 0xe3, 0xf3, 0x46, 0x94, 0xa4, 0x85, 0xb4, 0x95, 0xc4, 0xd4, 0xe4, 0xf4, 0xa5, 0xb5, 0xc5, 0xd5, 0xe5, 0xf5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6, 0xc6, 0xd6, 0xe6, 0xf6, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7, 0xc7, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xf2, 0xed, 0xb2, 0x8d, 0x4d, 0x45, 0xcd, 0x2f, 0x3f, 0x44, 0x68, 0x93, 0xc3, 0x58, 0xc8, 0xf1, 0x1f, 0x8a, 0x33, 0x86, 0xda, 0x58, 0xc1, 0xa0, 0x02, 0x4f, 0xc4, 0xa1, 0x69, 0xa5, 0x9b, 0x5b, 0x4b, 0x84, 0x73, 0xdf, 0xc9, 0x15, 0xf8, 0xe3, 0xd1, 0x0e, 0x07, 0x93, 0xf3, 0xd1, 0x0f, 0x1c, 0x17, 0xef, 0x2e, 0x3b, 0x5b, 0xdc, 0xff, 0x00, 0xdf, 0x42, 0xbf, 0x8f, 0x8e, 0xdc, 0x82, 0xca, 0xd8, 0x37, 0x11, 0xa9, 0x3d, 0x82, 0x69, 0x2b, 0xc4, 0x6d, 0xc9, 0x75, 0x25, 0xbc, 0xf7, 0xec, 0xa1, 0xb5, 0x74, 0x19, 0x5d, 0x2e, 0x8a, 0x9a, 0x4b, 0x89, 0x7d, 0xc4, 0x68, 0xc6, 0xf6, 0xfe, 0xb2, 0xa0, 0x30, 0x1d, 0x60, 0x86, 0x88, 0x8d, 0x49, 0x3e, 0x01, 0x11, 0x20, 0xa3, 0x8c, 0xb9, 0xb1, 0xaa, 0x62, 0xad, 0xbf, 0x18, 0x97, 0x43, 0x47, 0x1d, 0xd2, 0xaf, 0x04, 0xd9, 0xb8, 0xc8, 0x0d, 0x68, 0xe4, 0xf7, 0x3e, 0x48, 0xf1, 0x05, 0xbc, 0x25, 0xaa, 0x07, 0x71, 0xd9, 0x14, 0x78, 0xf6, 0x49, 0xb5, 0x90, 0xfd, 0xa7, 0xc6, 0x14, 0xfd, 0x1b, 0x1c, 0xff, 0x00, 0x4d, 0x8d, 0x2e, 0x73, 0x8c, 0x35, 0xa3, 0x52, 0x4f, 0x92, 0x48, 0xa6, 0x1a, 0x24, 0xb6, 0x2a, 0xfa, 0xa5, 0x9e, 0x60, 0x64, 0x39, 0x94, 0x13, 0xcb, 0x27, 0x73, 0x80, 0xf3, 0x0c, 0xf6, 0xff, 0x00, 0xd2, 0x5a, 0x78, 0xbf, 0x53, 0x71, 0xf6, 0x01, 0x75, 0xb6, 0x97, 0x6a, 0x25, 0xa1, 0xad, 0x1f, 0xf4, 0xb7, 0x23, 0x48, 0xb7, 0x94, 0x84, 0x97, 0x5b, 0xff, 0x00, 0x32, 0xa9, 0xdd, 0xfc, 0xed, 0x9b, 0x7e, 0x0d, 0x9e, 0x52, 0x4a, 0x95, 0x61, 0xff, 0xd0, 0xf3, 0x3b, 0xa7, 0x70, 0xee, 0x01, 0x8f, 0xb9, 0x59, 0xfa, 0x7e, 0xdf, 0xe4, 0xc8, 0xf9, 0x2a, 0xc2, 0x5c, 0x63, 0xc3, 0x54, 0x67, 0x87, 0x6e, 0x10, 0x35, 0x68, 0xd4, 0x79, 0x1e, 0x53, 0x4a, 0xe0, 0xdc, 0xe9, 0xb8, 0x1f, 0x6a, 0xda, 0x6c, 0x25, 0x94, 0x37, 0xb0, 0xd0, 0xb8, 0xad, 0x67, 0xe4, 0x55, 0x8a, 0x5b, 0x8b, 0x82, 0xc0, 0x6f, 0x76, 0x80, 0x34, 0x49, 0x05, 0x2e, 0x9e, 0xc6, 0x1c, 0x66, 0x31, 0xba, 0x10, 0x23, 0xe0, 0xaf, 0xe1, 0x61, 0x53, 0x43, 0x8d, 0x81, 0xb3, 0x67, 0xef, 0x9e, 0x49, 0x2a, 0x12, 0x6c, 0xb6, 0x63, 0x1a, 0x0c, 0x31, 0xba, 0x55, 0xcd, 0xac, 0xfa, 0x8e, 0xdf, 0x91, 0x6e, 0x91, 0xd9, 0xb3, 0xc9, 0x73, 0x90, 0x7a, 0xab, 0x6a, 0xc2, 0xa4, 0x60, 0xe2, 0x8f, 0xd2, 0x38, 0x03, 0x7d, 0x9e, 0x0d, 0xff, 0x00, 0xcc, 0xd6, 0xd3, 0x6b, 0x71, 0x67, 0xd2, 0x3e, 0x64, 0x72, 0xab, 0xdb, 0x8d, 0x54, 0x39, 0xc5, 0x83, 0x6b, 0x3d, 0xee, 0x2e, 0xd4, 0x92, 0x3c, 0x4a, 0x56, 0xba, 0xb4, 0x79, 0x5c, 0xf7, 0xb2, 0x96, 0x6c, 0x8d, 0xaf, 0x80, 0x48, 0x3c, 0xf0, 0xb2, 0x1f, 0x63, 0x9c, 0xe9, 0x3f, 0x24, 0x5c, 0xdb, 0xdd, 0x76, 0x43, 0xde, 0xfd, 0x5c, 0xe3, 0x24, 0xfc, 0x50, 0x00, 0x93, 0x0a, 0x78, 0x8a, 0x0d, 0x49, 0xca, 0xcf, 0x93, 0x63, 0x1b, 0x7d, 0xd7, 0x57, 0x50, 0xd5, 0xef, 0x70, 0x6b, 0x4f, 0xc7, 0x45, 0xdb, 0x74, 0x9e, 0x8d, 0x5e, 0x33, 0x83, 0xd8, 0x37, 0xdd, 0xc3, 0xac, 0x3d, 0xbf, 0x92, 0xc5, 0x5b, 0xea, 0xbf, 0xd5, 0x62, 0xc0, 0xdc, 0xbc, 0xbd, 0x2d, 0x22, 0x5a, 0xcf, 0xdd, 0x69, 0xff, 0x00, 0xd1, 0x8e, 0x5d, 0xa5, 0x38, 0xb5, 0xb0, 0x00, 0xc6, 0xc4, 0x24, 0x4a, 0xd6, 0x8d, 0x18, 0x04, 0x49, 0x88, 0x9e, 0x55, 0xd6, 0x61, 0xb0, 0xc1, 0x70, 0x32, 0xdd, 0x3c, 0x95, 0xda, 0xf1, 0xfe, 0xf5, 0x62, 0xbc, 0x76, 0x8e, 0x75, 0x28, 0x02, 0xa2, 0xe7, 0x7d, 0x92, 0xb9, 0x84, 0x96, 0x96, 0xda, 0xf7, 0x70, 0x12, 0x4e, 0x5a, 0xff, 0x00, 0xff, 0xd1, 0xf3, 0x7a, 0x21, 0xaf, 0xde, 0xef, 0xa2, 0x22, 0x55, 0xfc, 0x5a, 0xbd, 0x42, 0xfb, 0x08, 0xfa, 0x67, 0x4f, 0x82, 0xcd, 0x6d, 0x85, 0xc0, 0x56, 0x3b, 0x90, 0xb7, 0xf0, 0x2a, 0x0e, 0x63, 0x58, 0x3b, 0xf2, 0xa3, 0x9e, 0x8c, 0xb8, 0x86, 0xbe, 0x49, 0xf1, 0x2c, 0x0c, 0x86, 0xb4, 0x4c, 0x69, 0xe4, 0xaf, 0x6e, 0xcc, 0x6b, 0x7d, 0x46, 0xb3, 0x70, 0xec, 0x38, 0x51, 0x7d, 0x02, 0x8a, 0xc7, 0xa6, 0xd9, 0x20, 0x68, 0x0f, 0x8f, 0x8a, 0xcf, 0xc9, 0xc2, 0xea, 0x59, 0x5b, 0x48, 0xb0, 0x91, 0xae, 0xe6, 0xc9, 0x03, 0xc9, 0x30, 0x51, 0x66, 0xd4, 0x0d, 0xad, 0xbd, 0x5f, 0x53, 0xcc, 0x6b, 0xb6, 0x90, 0x5a, 0x3b, 0x83, 0x0b, 0x43, 0x17, 0x31, 0xd6, 0xc3, 0x6e, 0x12, 0x3b, 0x79, 0xac, 0xc1, 0x89, 0x47, 0xd9, 0xe8, 0x63, 0x98, 0x45, 0xed, 0x6c, 0x5a, 0xf1, 0xa0, 0x27, 0xc5, 0x5b, 0xc3, 0x6f, 0xa6, 0xe0, 0x1c, 0x7d, 0xb3, 0xa2, 0x69, 0x34, 0x7b, 0xae, 0x1a, 0x8d, 0x45, 0x17, 0x9d, 0xeb, 0xfd, 0x21, 0xd8, 0xb9, 0xae, 0xb5, 0x80, 0xbb, 0x1e, 0xd2, 0x5c, 0xd7, 0x78, 0x13, 0xf9, 0xae, 0x4b, 0xea, 0xc7, 0x4a, 0x39, 0xbd, 0x55, 0xb3, 0xed, 0x66, 0x38, 0xf5, 0x09, 0x22, 0x41, 0x23, 0xe8, 0x37, 0xfb, 0x4b, 0xa1, 0xeb, 0xd6, 0xfe, 0x88, 0x31, 0xbf, 0x41, 0xc0, 0xee, 0xd2, 0x74, 0x02, 0x78, 0x53, 0xfa, 0x97, 0x43, 0x19, 0x85, 0x65, 0xff, 0x00, 0x9d, 0x71, 0x33, 0xe4, 0x1a, 0x7d, 0x8d, 0x53, 0x42, 0x56, 0x35, 0x6b, 0xe5, 0x80, 0x06, 0xc7, 0x57, 0xa7, 0xc4, 0xa9, 0xdb, 0xb6, 0x81, 0x1f, 0xeb, 0xd9, 0x69, 0x56, 0xc2, 0xd0, 0x00, 0xe5, 0x55, 0xc0, 0x12, 0xc2, 0xd7, 0x4e, 0xa2, 0x5a, 0x7c, 0x0a, 0xd0, 0x63, 0x9a, 0xd1, 0xaf, 0xd2, 0xe2, 0x3c, 0x12, 0x62, 0x66, 0xc6, 0x42, 0x23, 0x5a, 0x49, 0x8f, 0x10, 0xa2, 0xd2, 0x3e, 0x28, 0x9d, 0xc4, 0x88, 0x09, 0x29, 0x16, 0xc3, 0x3c, 0x24, 0x8d, 0xe6, 0x92, 0x72, 0x1f, 0xff, 0xd2, 0xf3, 0xbb, 0xb0, 0xfe, 0xcb, 0x99, 0xe9, 0xce, 0xf6, 0x88, 0x2d, 0x77, 0x91, 0x5b, 0x3d, 0x3d, 0xd0, 0xe6, 0x90, 0xa9, 0x65, 0x57, 0x38, 0x95, 0xdd, 0xcb, 0x9a, 0x7d, 0xce, 0xf2, 0x3f, 0x44, 0x23, 0x60, 0x58, 0x76, 0xe9, 0xca, 0x8c, 0xea, 0x1b, 0x31, 0x02, 0x32, 0x23, 0xea, 0xee, 0xb1, 0xcd, 0xb0, 0xc7, 0x87, 0x74, 0x7a, 0xeb, 0x70, 0x1a, 0x71, 0xe1, 0xfe, 0xe4, 0x1c, 0x1d, 0xae, 0xe5, 0x69, 0xd8, 0xfa, 0x99, 0x50, 0x0d, 0x1a, 0xf7, 0x2a, 0x3a, 0x0c, 0xf4, 0x1a, 0x8e, 0xc7, 0x27, 0x5d, 0xbf, 0x18, 0x41, 0xdc, 0xc2, 0xf0, 0x7f, 0x74, 0xf6, 0x3a, 0x22, 0x66, 0xdb, 0x68, 0xc6, 0x80, 0x48, 0x6b, 0x88, 0x06, 0x39, 0x0d, 0xee, 0xaa, 0x1f, 0xb3, 0xd5, 0x1b, 0x83, 0xd8, 0x3b, 0x38, 0x8f, 0x69, 0xfe, 0xdf, 0xd1, 0x4d, 0x29, 0xa1, 0x4c, 0x7a, 0xf4, 0xbf, 0xa7, 0x92, 0xcf, 0xa5, 0x20, 0x08, 0xf3, 0xf6, 0xff, 0x00, 0x15, 0xbb, 0xd1, 0x31, 0xd9, 0x5e, 0x3d, 0x75, 0x56, 0x36, 0x88, 0x00, 0x81, 0xe0, 0x16, 0x5e, 0x55, 0x74, 0x3f, 0x00, 0x9d, 0xe0, 0xcc, 0x69, 0xe7, 0x3a, 0x2d, 0xbe, 0x90, 0x00, 0xa9, 0xae, 0xef, 0x1f, 0x95, 0x4b, 0x0d, 0x9a, 0xdc, 0xc7, 0x45, 0xfe, 0xb1, 0x7d, 0x60, 0xa7, 0xa1, 0xe0, 0x1f, 0x4e, 0x1d, 0x99, 0x69, 0x02, 0x9a, 0xcf, 0x1f, 0xca, 0x7b, 0xbf, 0x90, 0xc5, 0xc2, 0xb3, 0xeb, 0x57, 0xd6, 0x03, 0x6b, 0xae, 0x39, 0xb6, 0x82, 0xe3, 0x31, 0xa1, 0x68, 0xf2, 0x6b, 0x5c, 0x12, 0xfa, 0xe1, 0x91, 0x66, 0x47, 0x5d, 0xb8, 0x3b, 0x4f, 0x44, 0x36, 0xb6, 0x8f, 0x28, 0xdd, 0xff, 0x00, 0x7e, 0x46, 0xab, 0x12, 0x2b, 0x65, 0x55, 0x32, 0xa7, 0x62, 0xb6, 0xbd, 0xf7, 0x64, 0x10, 0xdb, 0x03, 0x9f, 0x1b, 0x9e, 0xc7, 0xd9, 0xb8, 0x3b, 0x1f, 0x67, 0xf3, 0x6c, 0x52, 0x80, 0xd7, 0x7d, 0x0f, 0xea, 0x7f, 0x5d, 0x1d, 0x67, 0xa6, 0x0b, 0x1e, 0x47, 0xda, 0x69, 0x3b, 0x2e, 0x03, 0xc7, 0xf3, 0x5f, 0x1f, 0xf0, 0x8b, 0xa1, 0x02, 0x46, 0xba, 0x79, 0xaf, 0x32, 0xff, 0x00, 0x16, 0xad, 0xca, 0x1d, 0x57, 0x2a, 0xdc, 0x79, 0x18, 0x41, 0xb0, 0xf6, 0x9e, 0xe4, 0x9f, 0xd0, 0x8f, 0xeb, 0x31, 0xab, 0xd2, 0x83, 0xa4, 0xcb, 0x8c, 0xb8, 0xa0, 0x42, 0x12, 0x7b, 0x67, 0x9f, 0x2f, 0xf5, 0x09, 0x26, 0x96, 0xc4, 0xce, 0xa9, 0x20, 0xa7, 0xff, 0xd3, 0xf3, 0x2f, 0xb4, 0x5d, 0xe9, 0x0a, 0xb7, 0x9f, 0x4c, 0x19, 0xdb, 0x3a, 0x2d, 0x5e, 0x94, 0xfd, 0xc4, 0xb7, 0xc5, 0x62, 0xf9, 0x2b, 0xfd, 0x2e, 0xe3, 0x5d, 0xe0, 0x7c, 0x13, 0x48, 0xd1, 0x92, 0x12, 0xa9, 0x0b, 0x7a, 0xbc, 0x2d, 0xc2, 0x7f, 0x92, 0x60, 0xab, 0x4e, 0x79, 0x2e, 0x00, 0xf0, 0xaa, 0xe1, 0xda, 0x3d, 0x43, 0xfc, 0xad, 0x55, 0xbb, 0x80, 0x79, 0x81, 0xa0, 0xe6, 0x54, 0x32, 0x6d, 0x02, 0xbe, 0xf3, 0x61, 0x81, 0xa8, 0x44, 0x14, 0x03, 0x59, 0x0e, 0x1c, 0xf6, 0x1f, 0xdc, 0xb2, 0xec, 0xa3, 0x23, 0x77, 0xe8, 0x6e, 0x70, 0xf2, 0x25, 0x1f, 0x1f, 0x17, 0xa9, 0x6d, 0x71, 0x36, 0x97, 0x47, 0x00, 0xa4, 0x02, 0xe0, 0x2c, 0x7c, 0xc1, 0xab, 0xd5, 0x31, 0x85, 0x35, 0xd4, 0xe6, 0x13, 0x02, 0xd6, 0x4b, 0x67, 0x48, 0x2b, 0xa9, 0xe9, 0x2e, 0x02, 0xb6, 0x4f, 0x82, 0xe5, 0x7a, 0x95, 0x19, 0xc6, 0x87, 0x3d, 0xfb, 0xa2, 0xb8, 0x79, 0x1e, 0x4d, 0x3b, 0x96, 0xcf, 0x4f, 0xbd, 0xcd, 0xa2, 0xa2, 0x1f, 0xa0, 0x82, 0xd3, 0xfc, 0x97, 0x05, 0x24, 0x36, 0x6b, 0xf3, 0x31, 0xa2, 0x35, 0x79, 0xef, 0xad, 0xf8, 0xae, 0xaf, 0xaf, 0xd8, 0xf2, 0xd8, 0x6d, 0xed, 0x6b, 0xda, 0x7b, 0x18, 0x1b, 0x5d, 0xff, 0x00, 0x52, 0xb1, 0x6d, 0xf0, 0x81, 0x31, 0xca, 0xf4, 0x6e, 0xb1, 0x80, 0xce, 0xb1, 0x84, 0xc0, 0x21, 0xb7, 0xd6, 0x77, 0x31, 0xd1, 0x27, 0xc1, 0xcd, 0xfe, 0xd2, 0xe3, 0xec, 0xe8, 0x1d, 0x45, 0x96, 0xb0, 0x9a, 0xb7, 0x87, 0x3f, 0x68, 0x2d, 0xf7, 0x01, 0x1f, 0xbe, 0xd1, 0xf4, 0x7f, 0xb4, 0xa4, 0x0d, 0x77, 0xbb, 0xfa, 0x8f, 0x80, 0x3a, 0x7f, 0x43, 0xaa, 0xe2, 0xdf, 0xd2, 0x65, 0x7e, 0x95, 0xe4, 0x0f, 0x1f, 0xa1, 0xfe, 0x6b, 0x16, 0x9f, 0x52, 0xfa, 0xc1, 0xd3, 0xba, 0x6d, 0x26, 0xdc, 0xac, 0x86, 0xd4, 0xd9, 0x0d, 0x31, 0x2e, 0x74, 0x9e, 0xdb, 0x59, 0x2e, 0x55, 0xe8, 0xc9, 0xb2, 0x96, 0xd5, 0x4b, 0x9f, 0xb8, 0x6d, 0xda, 0x1c, 0x04, 0x09, 0x03, 0xfe, 0x8a, 0xc6, 0xfa, 0xd3, 0xf5, 0x6a, 0xbe, 0xbb, 0x5b, 0x2e, 0xc6, 0xb5, 0x94, 0xe6, 0xd5, 0x20, 0x97, 0x7d, 0x1b, 0x1b, 0xf9, 0xad, 0x7c, 0x7d, 0x17, 0xb7, 0xf3, 0x1e, 0x92, 0x1b, 0x7f, 0xf8, 0xe0, 0x7d, 0x59, 0xdd, 0xfd, 0x32, 0xd8, 0x8f, 0xa5, 0xe8, 0x3a, 0x12, 0x5c, 0x3f, 0xfc, 0xc4, 0xfa, 0xc3, 0xb3, 0x77, 0xa7, 0x56, 0xed, 0xdb, 0x76, 0x7a, 0x8d, 0xdd, 0x1f, 0xbf, 0xfd, 0x44, 0x92, 0x56, 0x8f, 0xff, 0xd4, 0xf2, 0xe8, 0x86, 0x17, 0x1e, 0xfa, 0x04, 0x56, 0x4b, 0x43, 0x6c, 0x6f, 0x2d, 0xe5, 0x46, 0x01, 0x64, 0x2b, 0x14, 0x32, 0x5b, 0xb4, 0xa0, 0x52, 0x1d, 0xde, 0x9b, 0x94, 0xdb, 0xab, 0x6b, 0x81, 0xf7, 0x05, 0xb0, 0xd7, 0x07, 0xb2, 0x27, 0x55, 0xc6, 0x57, 0x65, 0xd8, 0x76, 0x6e, 0x64, 0xed, 0xee, 0x16, 0xce, 0x27, 0x57, 0x63, 0xda, 0x0c, 0xc2, 0x8e, 0x51, 0x67, 0x84, 0xfa, 0x1d, 0xdd, 0x62, 0xc7, 0x07, 0xe9, 0xf7, 0xa3, 0xd6, 0x6c, 0x02, 0x41, 0x55, 0x31, 0xf3, 0x2b, 0xb3, 0xba, 0x2b, 0x2e, 0x68, 0x24, 0x1d, 0x47, 0x64, 0xca, 0xa6, 0x50, 0x41, 0x65, 0x90, 0x6c, 0xb1, 0xa5, 0xae, 0x33, 0x23, 0x51, 0xe4, 0xab, 0x7d, 0x5d, 0xcb, 0xb6, 0xcc, 0x37, 0xd0, 0x40, 0x73, 0x71, 0xde, 0x58, 0x09, 0xe7, 0x6f, 0x2c, 0x44, 0xc9, 0xc9, 0xae, 0xba, 0x9d, 0x63, 0x88, 0x01, 0xa0, 0x95, 0x9d, 0xf5, 0x3f, 0x2a, 0xe6, 0x67, 0xdb, 0x50, 0x83, 0x55, 0xad, 0x36, 0x3e, 0x78, 0x10, 0x74, 0x77, 0xfd, 0x2d, 0xaa, 0x4c, 0x7d, 0x58, 0x73, 0x91, 0xa0, 0x0f, 0x51, 0x45, 0xb7, 0x33, 0xdd, 0x58, 0x69, 0x1d, 0xd8, 0x0c, 0x9f, 0x96, 0x88, 0x19, 0x99, 0x19, 0xac, 0xcf, 0xa3, 0xd2, 0xad, 0xb5, 0xdb, 0x76, 0x8f, 0xad, 0xc4, 0xea, 0xcf, 0xdf, 0x7e, 0xdf, 0xdd, 0xfc, 0xd5, 0xa3, 0x5e, 0x43, 0x2b, 0x6b, 0xb2, 0xad, 0x3b, 0x6a, 0xa4, 0x13, 0xa7, 0x04, 0xac, 0x7a, 0x6f, 0xb3, 0x23, 0x26, 0xcc, 0xfb, 0xb4, 0x75, 0x8e, 0x01, 0x83, 0xf7, 0x58, 0x3e, 0x8b, 0x53, 0xa7, 0x2a, 0x1a, 0x31, 0x42, 0x36, 0x5d, 0x4c, 0x9a, 0xf2, 0xdc, 0xc6, 0xfe, 0x98, 0xb4, 0x34, 0xcb, 0x48, 0x0a, 0x8f, 0xdb, 0xb2, 0xeb, 0x76, 0xd6, 0x07, 0x5c, 0x59, 0xc9, 0x64, 0x8f, 0x93, 0xa7, 0x73, 0x16, 0x83, 0xaf, 0x0e, 0xa4, 0x33, 0xef, 0x50, 0xc5, 0x0c, 0xda, 0x59, 0x10, 0x06, 0x8a, 0x2e, 0x29, 0x0e, 0xac, 0xc2, 0x31, 0x3d, 0x36, 0x69, 0x7e, 0xd6, 0xcc, 0xf5, 0x3d, 0x6f, 0xb3, 0xeb, 0x1b, 0x76, 0xef, 0x3b, 0xa3, 0xfa, 0xc9, 0x2b, 0x5f, 0x66, 0x6f, 0xa9, 0x1e, 0x73, 0xf2, 0x49, 0x2e, 0x39, 0xf7, 0x4f, 0xb7, 0x8d, 0xff, 0xd5, 0xf3, 0x26, 0xfe, 0x0a, 0xc5, 0x1b, 0xa7, 0xcb, 0xb2, 0xcf, 0x49, 0x03, 0xb2, 0x46, 0xee, 0xd9, 0xd9, 0xb3, 0xf4, 0x9f, 0x25, 0x4a, 0xdf, 0x4b, 0x77, 0xe8, 0x27, 0xd4, 0xef, 0x1c, 0x2a, 0x29, 0x26, 0xc5, 0x7c, 0x9d, 0x6c, 0x7f, 0xb7, 0x6e, 0x1b, 0x26, 0x7f, 0x05, 0xa3, 0xfe, 0x53, 0x8d, 0x62, 0x57, 0x30, 0x92, 0x12, 0xfa, 0x2f, 0x86, 0xdf, 0xa4, 0xec, 0x67, 0xfe, 0xd0, 0xf4, 0xff, 0x00, 0x4d, 0xfc, 0xdf, 0x78, 0xe1, 0x68, 0x7d, 0x54, 0x99, 0xbf, 0x6f, 0xf3, 0xbe, 0xdf, 0x8e, 0xdd, 0x7f, 0xef, 0xeb, 0x97, 0x49, 0x3e, 0x3b, 0x7f, 0x06, 0x2c, 0x9f, 0x37, 0x5f, 0xf0, 0x9f, 0x4c, 0xeb, 0x7b, 0xbf, 0x67, 0x55, 0xe8, 0xff, 0x00, 0x31, 0xbc, 0x7a, 0x9e, 0x31, 0xdb, 0xfe, 0x92, 0xae, 0x37, 0x7a, 0x4d, 0xdb, 0xe2, 0x17, 0x9d, 0xa4, 0xa3, 0xc9, 0xba, 0xfc, 0x7b, 0x7d, 0x5f, 0x52, 0xa7, 0x7e, 0xd1, 0x28, 0xf8, 0xf3, 0xb0, 0xc7, 0x32, 0xbc, 0x99, 0x24, 0xc5, 0xe3, 0xab, 0xeb, 0x1f, 0xa4, 0xf5, 0xfc, 0xe1, 0x25, 0xe4, 0xe9, 0x24, 0x97, 0xff, 0xd9, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x65, 0x00, 0x20, 0x00, 0x50, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x70, 0x00, 0x00, 0x00, 0x13, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x65, 0x00, 0x20, 0x00, 0x50, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x70, 0x00, 0x20, 0x00, 0x37, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0xff, 0xe1, 0x15, 0x67, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x00, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x3d, 0x27, 0xef, 0xbb, 0xbf, 0x27, 0x20, 0x69, 0x64, 0x3d, 0x27, 0x57, 0x35, 0x4d, 0x30, 0x4d, 0x70, 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, 0x53, 0x7a, 0x4e, 0x54, 0x63, 0x7a, 0x6b, 0x63, 0x39, 0x64, 0x27, 0x3f, 0x3e, 0x0a, 0x3c, 0x3f, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2d, 0x78, 0x61, 0x70, 0x2d, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x20, 0x65, 0x73, 0x63, 0x3d, 0x22, 0x43, 0x52, 0x22, 0x3f, 0x3e, 0x0a, 0x3c, 0x78, 0x3a, 0x78, 0x61, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x27, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x27, 0x20, 0x78, 0x3a, 0x78, 0x61, 0x70, 0x74, 0x6b, 0x3d, 0x27, 0x58, 0x4d, 0x50, 0x20, 0x74, 0x6f, 0x6f, 0x6c, 0x6b, 0x69, 0x74, 0x20, 0x32, 0x2e, 0x38, 0x2e, 0x32, 0x2d, 0x33, 0x33, 0x2c, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x31, 0x2e, 0x35, 0x27, 0x3e, 0x0a, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x27, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x69, 0x58, 0x3d, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x58, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x27, 0x3e, 0x0a, 0x0a, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x27, 0x75, 0x75, 0x69, 0x64, 0x3a, 0x32, 0x32, 0x64, 0x30, 0x32, 0x62, 0x30, 0x61, 0x2d, 0x62, 0x32, 0x34, 0x39, 0x2d, 0x31, 0x31, 0x64, 0x62, 0x2d, 0x38, 0x61, 0x66, 0x38, 0x2d, 0x39, 0x31, 0x64, 0x35, 0x34, 0x30, 0x33, 0x66, 0x39, 0x32, 0x66, 0x39, 0x27, 0x0a, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x70, 0x64, 0x66, 0x3d, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x64, 0x66, 0x2f, 0x31, 0x2e, 0x33, 0x2f, 0x27, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x21, 0x2d, 0x2d, 0x20, 0x70, 0x64, 0x66, 0x3a, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x2d, 0x2d, 0x3e, 0x0a, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x0a, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x27, 0x75, 0x75, 0x69, 0x64, 0x3a, 0x32, 0x32, 0x64, 0x30, 0x32, 0x62, 0x30, 0x61, 0x2d, 0x62, 0x32, 0x34, 0x39, 0x2d, 0x31, 0x31, 0x64, 0x62, 0x2d, 0x38, 0x61, 0x66, 0x38, 0x2d, 0x39, 0x31, 0x64, 0x35, 0x34, 0x30, 0x33, 0x66, 0x39, 0x32, 0x66, 0x39, 0x27, 0x0a, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3d, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x27, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x21, 0x2d, 0x2d, 0x20, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3a, 0x43, 0x61, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x2d, 0x2d, 0x3e, 0x0a, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x0a, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x27, 0x75, 0x75, 0x69, 0x64, 0x3a, 0x32, 0x32, 0x64, 0x30, 0x32, 0x62, 0x30, 0x61, 0x2d, 0x62, 0x32, 0x34, 0x39, 0x2d, 0x31, 0x31, 0x64, 0x62, 0x2d, 0x38, 0x61, 0x66, 0x38, 0x2d, 0x39, 0x31, 0x64, 0x35, 0x34, 0x30, 0x33, 0x66, 0x39, 0x32, 0x66, 0x39, 0x27, 0x0a, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x61, 0x70, 0x3d, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x27, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x21, 0x2d, 0x2d, 0x20, 0x78, 0x61, 0x70, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x2d, 0x2d, 0x3e, 0x0a, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x0a, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x27, 0x75, 0x75, 0x69, 0x64, 0x3a, 0x32, 0x32, 0x64, 0x30, 0x32, 0x62, 0x30, 0x61, 0x2d, 0x62, 0x32, 0x34, 0x39, 0x2d, 0x31, 0x31, 0x64, 0x62, 0x2d, 0x38, 0x61, 0x66, 0x38, 0x2d, 0x39, 0x31, 0x64, 0x35, 0x34, 0x30, 0x33, 0x66, 0x39, 0x32, 0x66, 0x39, 0x27, 0x0a, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x61, 0x70, 0x4d, 0x4d, 0x3d, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x6d, 0x6d, 0x2f, 0x27, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x78, 0x61, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x64, 0x6f, 0x63, 0x69, 0x64, 0x3a, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3a, 0x32, 0x32, 0x64, 0x30, 0x32, 0x62, 0x30, 0x36, 0x2d, 0x62, 0x32, 0x34, 0x39, 0x2d, 0x31, 0x31, 0x64, 0x62, 0x2d, 0x38, 0x61, 0x66, 0x38, 0x2d, 0x39, 0x31, 0x64, 0x35, 0x34, 0x30, 0x33, 0x66, 0x39, 0x32, 0x66, 0x39, 0x3c, 0x2f, 0x78, 0x61, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3e, 0x0a, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x0a, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x27, 0x75, 0x75, 0x69, 0x64, 0x3a, 0x32, 0x32, 0x64, 0x30, 0x32, 0x62, 0x30, 0x61, 0x2d, 0x62, 0x32, 0x34, 0x39, 0x2d, 0x31, 0x31, 0x64, 0x62, 0x2d, 0x38, 0x61, 0x66, 0x38, 0x2d, 0x39, 0x31, 0x64, 0x35, 0x34, 0x30, 0x33, 0x66, 0x39, 0x32, 0x66, 0x39, 0x27, 0x0a, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x64, 0x63, 0x3d, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x72, 0x6c, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x64, 0x63, 0x2f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x31, 0x2e, 0x31, 0x2f, 0x27, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x64, 0x63, 0x3a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x41, 0x6c, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0x20, 0x78, 0x6d, 0x6c, 0x3a, 0x6c, 0x61, 0x6e, 0x67, 0x3d, 0x27, 0x78, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x27, 0x3e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x41, 0x6c, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x63, 0x3a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x0a, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x61, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x27, 0x77, 0x27, 0x3f, 0x3e, 0xff, 0xee, 0x00, 0x0e, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x00, 0x64, 0x40, 0x00, 0x00, 0x00, 0x01, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x06, 0x04, 0x03, 0x04, 0x06, 0x07, 0x05, 0x04, 0x04, 0x05, 0x07, 0x08, 0x06, 0x06, 0x07, 0x06, 0x06, 0x08, 0x0a, 0x08, 0x09, 0x09, 0x09, 0x09, 0x08, 0x0a, 0x0a, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0a, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x01, 0x04, 0x05, 0x05, 0x08, 0x07, 0x08, 0x0f, 0x0a, 0x0a, 0x0f, 0x14, 0x0e, 0x0e, 0x0e, 0x14, 0x14, 0x0e, 0x0e, 0x0e, 0x0e, 0x14, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x11, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0x64, 0x00, 0x64, 0x03, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xdd, 0x00, 0x04, 0x00, 0x0d, 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x00, 0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x05, 0x03, 0x02, 0x06, 0x01, 0x00, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, 0x00, 0x02, 0x02, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x02, 0x06, 0x07, 0x03, 0x04, 0x02, 0x06, 0x02, 0x73, 0x01, 0x02, 0x03, 0x11, 0x04, 0x00, 0x05, 0x21, 0x12, 0x31, 0x41, 0x51, 0x06, 0x13, 0x61, 0x22, 0x71, 0x81, 0x14, 0x32, 0x91, 0xa1, 0x07, 0x15, 0xb1, 0x42, 0x23, 0xc1, 0x52, 0xd1, 0xe1, 0x33, 0x16, 0x62, 0xf0, 0x24, 0x72, 0x82, 0xf1, 0x25, 0x43, 0x34, 0x53, 0x92, 0xa2, 0xb2, 0x63, 0x73, 0xc2, 0x35, 0x44, 0x27, 0x93, 0xa3, 0xb3, 0x36, 0x17, 0x54, 0x64, 0x74, 0xc3, 0xd2, 0xe2, 0x08, 0x26, 0x83, 0x09, 0x0a, 0x18, 0x19, 0x84, 0x94, 0x45, 0x46, 0xa4, 0xb4, 0x56, 0xd3, 0x55, 0x28, 0x1a, 0xf2, 0xe3, 0xf3, 0xc4, 0xd4, 0xe4, 0xf4, 0x65, 0x75, 0x85, 0x95, 0xa5, 0xb5, 0xc5, 0xd5, 0xe5, 0xf5, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6, 0xc6, 0xd6, 0xe6, 0xf6, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7, 0xc7, 0xd7, 0xe7, 0xf7, 0x38, 0x48, 0x58, 0x68, 0x78, 0x88, 0x98, 0xa8, 0xb8, 0xc8, 0xd8, 0xe8, 0xf8, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, 0x89, 0x99, 0xa9, 0xb9, 0xc9, 0xd9, 0xe9, 0xf9, 0x2a, 0x3a, 0x4a, 0x5a, 0x6a, 0x7a, 0x8a, 0x9a, 0xaa, 0xba, 0xca, 0xda, 0xea, 0xfa, 0x11, 0x00, 0x02, 0x02, 0x01, 0x02, 0x03, 0x05, 0x05, 0x04, 0x05, 0x06, 0x04, 0x08, 0x03, 0x03, 0x6d, 0x01, 0x00, 0x02, 0x11, 0x03, 0x04, 0x21, 0x12, 0x31, 0x41, 0x05, 0x51, 0x13, 0x61, 0x22, 0x06, 0x71, 0x81, 0x91, 0x32, 0xa1, 0xb1, 0xf0, 0x14, 0xc1, 0xd1, 0xe1, 0x23, 0x42, 0x15, 0x52, 0x62, 0x72, 0xf1, 0x33, 0x24, 0x34, 0x43, 0x82, 0x16, 0x92, 0x53, 0x25, 0xa2, 0x63, 0xb2, 0xc2, 0x07, 0x73, 0xd2, 0x35, 0xe2, 0x44, 0x83, 0x17, 0x54, 0x93, 0x08, 0x09, 0x0a, 0x18, 0x19, 0x26, 0x36, 0x45, 0x1a, 0x27, 0x64, 0x74, 0x55, 0x37, 0xf2, 0xa3, 0xb3, 0xc3, 0x28, 0x29, 0xd3, 0xe3, 0xf3, 0x84, 0x94, 0xa4, 0xb4, 0xc4, 0xd4, 0xe4, 0xf4, 0x65, 0x75, 0x85, 0x95, 0xa5, 0xb5, 0xc5, 0xd5, 0xe5, 0xf5, 0x46, 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6, 0xc6, 0xd6, 0xe6, 0xf6, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7, 0xc7, 0xd7, 0xe7, 0xf7, 0x38, 0x48, 0x58, 0x68, 0x78, 0x88, 0x98, 0xa8, 0xb8, 0xc8, 0xd8, 0xe8, 0xf8, 0x39, 0x49, 0x59, 0x69, 0x79, 0x89, 0x99, 0xa9, 0xb9, 0xc9, 0xd9, 0xe9, 0xf9, 0x2a, 0x3a, 0x4a, 0x5a, 0x6a, 0x7a, 0x8a, 0x9a, 0xaa, 0xba, 0xca, 0xda, 0xea, 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xf0, 0x67, 0xa6, 0x5c, 0x0f, 0x01, 0xd4, 0x7e, 0x18, 0x12, 0x98, 0xe9, 0xd6, 0x2d, 0x34, 0x6d, 0x70, 0xdf, 0xdc, 0xa1, 0xe3, 0xec, 0x5b, 0xfb, 0x32, 0x24, 0xb2, 0x01, 0x1f, 0x15, 0xa4, 0x52, 0x4a, 0x82, 0x31, 0xf1, 0xfe, 0xd1, 0x3d, 0x14, 0x64, 0x49, 0x64, 0x22, 0x98, 0xcf, 0xa5, 0x46, 0x6c, 0x16, 0x55, 0x71, 0x56, 0x62, 0x28, 0x07, 0xc5, 0x45, 0x15, 0xa0, 0xc8, 0x89, 0x33, 0xe1, 0x63, 0xd2, 0xd8, 0x34, 0x44, 0x17, 0xa0, 0x2c, 0x4d, 0x16, 0xbb, 0xed, 0xdc, 0xf8, 0x64, 0xc1, 0x6b, 0x31, 0x42, 0x18, 0x8e, 0xc7, 0xb5, 0x2a, 0x7d, 0xb2, 0x56, 0xc5, 0x61, 0x8c, 0xf2, 0xa0, 0x1b, 0x1e, 0x83, 0x0d, 0xa1, 0x63, 0x50, 0x1f, 0x97, 0x7c, 0x2a, 0xa9, 0x1a, 0x9a, 0x86, 0x4f, 0xb4, 0xb4, 0x38, 0x0a, 0xa6, 0x0b, 0xb8, 0x0c, 0x05, 0x14, 0xf8, 0x76, 0x3e, 0x19, 0x14, 0xb6, 0x78, 0xf8, 0x8c, 0x2a, 0xd5, 0x01, 0xdc, 0x6f, 0x8a, 0x1a, 0xe3, 0x8d, 0xab, 0xff, 0xd0, 0xf0, 0xec, 0xe9, 0x15, 0xb5, 0xb9, 0x5a, 0x7c, 0x4c, 0xa2, 0x9e, 0x24, 0xf5, 0xca, 0xc6, 0xe5, 0x99, 0xd9, 0x34, 0x99, 0x04, 0x3a, 0x7d, 0xb5, 0xba, 0xd5, 0x51, 0x63, 0x0e, 0xc7, 0xc5, 0x9b, 0x73, 0xf8, 0xe4, 0x6f, 0x76, 0xca, 0xd9, 0xda, 0x54, 0x6d, 0x72, 0x2e, 0x1a, 0x57, 0x11, 0x44, 0x40, 0x0d, 0x27, 0x7a, 0x0f, 0xd9, 0x5f, 0x12, 0x69, 0x4c, 0x84, 0xcd, 0x36, 0xe3, 0x85, 0xb2, 0xcd, 0x2f, 0x4a, 0x8b, 0x58, 0x36, 0xf6, 0x76, 0xa8, 0x64, 0x64, 0x3c, 0xa4, 0x93, 0xaa, 0x25, 0x3c, 0x49, 0xda, 0xa4, 0xe5, 0x26, 0x54, 0xe4, 0x8c, 0x7c, 0x5c, 0x93, 0x4d, 0x67, 0xc9, 0x3a, 0x6e, 0x9f, 0x13, 0xb4, 0xce, 0xf7, 0x3a, 0x9b, 0xad, 0x52, 0xd6, 0x2a, 0xd1, 0x49, 0xee, 0xc7, 0xf8, 0x64, 0x46, 0x42, 0x4e, 0xcd, 0x92, 0xc2, 0x00, 0xdd, 0x8a, 0x47, 0xe5, 0x69, 0x6e, 0xd4, 0xa4, 0x08, 0x16, 0x83, 0x9c, 0x8c, 0xdd, 0x95, 0x6b, 0xb9, 0xf6, 0xef, 0x97, 0x78, 0x94, 0xe3, 0x78, 0x04, 0xa4, 0xf3, 0xe8, 0xee, 0x64, 0xe1, 0x12, 0x10, 0x05, 0x6a, 0xc7, 0xc0, 0x6f, 0x53, 0xf3, 0xc9, 0x89, 0xb4, 0x9c, 0x4e, 0xb4, 0xf2, 0xd3, 0xde, 0x7a, 0xd2, 0x19, 0x16, 0x38, 0x61, 0x5d, 0xd9, 0x88, 0x05, 0x9c, 0xf4, 0x0a, 0x0f, 0x5f, 0x73, 0x84, 0xe4, 0xa4, 0xc7, 0x0d, 0xa5, 0xf1, 0x59, 0xba, 0x5c, 0x08, 0x98, 0x6f, 0xc8, 0x20, 0xfa, 0x4e, 0x4e, 0xf6, 0x69, 0xe1, 0xa2, 0x89, 0xfd, 0x1f, 0x77, 0x2c, 0xe6, 0xce, 0xd6, 0x17, 0x9a, 0x69, 0xdb, 0xd3, 0x86, 0x18, 0xc1, 0x67, 0x77, 0x26, 0x80, 0x28, 0x1b, 0x93, 0x88, 0x41, 0x0f, 0x40, 0xb0, 0xfc, 0x87, 0xf3, 0x43, 0x98, 0xd7, 0x58, 0x96, 0xdb, 0x4d, 0x91, 0x88, 0xe5, 0x6c, 0x58, 0xdc, 0x5c, 0x2a, 0xf7, 0x2c, 0xb1, 0xfc, 0x20, 0x8f, 0x02, 0xd9, 0x65, 0x06, 0xbe, 0x26, 0x6f, 0xa2, 0x7f, 0xce, 0x3d, 0x69, 0x26, 0xdd, 0x13, 0x52, 0xbf, 0xbd, 0x92, 0x62, 0x59, 0x4c, 0x90, 0xac, 0x50, 0x45, 0x5e, 0xbb, 0x09, 0x03, 0x12, 0x29, 0x84, 0x00, 0xc4, 0xc9, 0x11, 0xff, 0x00, 0x42, 0xe7, 0xa7, 0x7a, 0xd4, 0xfd, 0x21, 0x79, 0xe9, 0x78, 0x71, 0x8b, 0x95, 0x39, 0x75, 0xaf, 0x4e, 0x98, 0x78, 0x42, 0x38, 0xdf, 0xff, 0xd1, 0xf0, 0xe6, 0xa0, 0x58, 0xc8, 0x84, 0x9a, 0xaa, 0x30, 0x55, 0xf9, 0x0a, 0x6f, 0x90, 0x0c, 0xca, 0x72, 0x48, 0xb8, 0x1e, 0x89, 0xa7, 0x23, 0x17, 0x24, 0xff, 0x00, 0x61, 0xb6, 0x54, 0x76, 0x6e, 0x1b, 0xa7, 0xbe, 0x50, 0xf2, 0xc1, 0xd7, 0x4c, 0x52, 0x5e, 0x33, 0x5b, 0xe9, 0x10, 0xf4, 0x54, 0x3c, 0x5e, 0x77, 0xee, 0x49, 0xec, 0x2b, 0xb6, 0x63, 0xe4, 0xc9, 0xc3, 0xef, 0x73, 0xf0, 0xe1, 0x32, 0x1b, 0xf2, 0x7a, 0x05, 0xce, 0xad, 0x65, 0xa1, 0x98, 0xb4, 0x0f, 0x2a, 0x5b, 0x23, 0xeb, 0x12, 0x00, 0x88, 0xb0, 0xa8, 0x66, 0x46, 0x3d, 0xea, 0x7b, 0xfb, 0x9e, 0x99, 0x89, 0xbc, 0x8d, 0x97, 0x3a, 0x34, 0x05, 0x32, 0x5d, 0x1f, 0xc9, 0x1a, 0x8c, 0x36, 0x8c, 0x6f, 0x66, 0xfa, 0xc6, 0xb7, 0x7d, 0xf0, 0x94, 0x04, 0xf0, 0x88, 0xc9, 0xd5, 0x9d, 0x8d, 0x4b, 0x11, 0xd4, 0x9f, 0xbb, 0x25, 0xc5, 0xdc, 0xa2, 0x03, 0x99, 0x4b, 0xbc, 0xf3, 0x0d, 0x97, 0x96, 0x74, 0xe5, 0xf2, 0xb6, 0x80, 0x95, 0xbd, 0x99, 0x15, 0xf5, 0x4b, 0xd2, 0x37, 0x58, 0x46, 0xd4, 0x27, 0xc5, 0xce, 0xc1, 0x7c, 0x30, 0x8e, 0x68, 0x94, 0x7b, 0x9e, 0x6d, 0xe6, 0x7b, 0x9b, 0x5d, 0x3a, 0xd8, 0xdb, 0x32, 0xfa, 0x77, 0x65, 0x15, 0xe4, 0x57, 0xa7, 0x21, 0x55, 0x04, 0x57, 0xef, 0xd8, 0x66, 0x56, 0x38, 0x19, 0x1b, 0xe8, 0xe0, 0x67, 0x98, 0xc7, 0x1a, 0x1c, 0xde, 0x71, 0x71, 0x79, 0x2c, 0xf2, 0xfa, 0x8c, 0x48, 0xec, 0xb5, 0x24, 0x9a, 0x0c, 0xce, 0x75, 0x29, 0xae, 0x8c, 0x67, 0xd4, 0xb5, 0x0b, 0x4b, 0x04, 0x05, 0xef, 0x2e, 0x66, 0x8e, 0x18, 0x08, 0x15, 0xdd, 0x8f, 0x11, 0xb0, 0xeb, 0x4c, 0x04, 0x5b, 0x21, 0x2a, 0x7d, 0x41, 0xe4, 0x4f, 0xcb, 0xcb, 0x5d, 0x12, 0x45, 0xb8, 0xb7, 0x53, 0x71, 0xaa, 0x9f, 0x86, 0x5b, 0xd6, 0x50, 0x4a, 0xed, 0xba, 0x46, 0x77, 0x00, 0x13, 0xd4, 0x8c, 0x85, 0xd3, 0x12, 0x6d, 0xeb, 0x1a, 0x67, 0x95, 0xd9, 0x39, 0x39, 0x50, 0xac, 0xff, 0x00, 0x6f, 0xc4, 0xff, 0x00, 0x1c, 0x81, 0x92, 0xb2, 0x6b, 0x6d, 0x02, 0xdd, 0xbd, 0x36, 0x92, 0x36, 0x2d, 0x1f, 0xc0, 0x2a, 0x0b, 0x28, 0x1b, 0x91, 0x41, 0xf4, 0x9c, 0xb6, 0x25, 0x81, 0x46, 0xfe, 0x81, 0xb5, 0xad, 0x3d, 0xba, 0x57, 0xb7, 0xf9, 0xf6, 0xc9, 0xb0, 0x7f, 0xff, 0xd2, 0xf0, 0xe2, 0x86, 0x95, 0xc4, 0x67, 0x7e, 0x3f, 0x11, 0xf7, 0xa8, 0x19, 0x06, 0x69, 0x8d, 0xca, 0xca, 0x24, 0x8f, 0xd3, 0x52, 0x24, 0x89, 0x47, 0x25, 0x1f, 0xcb, 0x20, 0xf8, 0xb2, 0xb2, 0x76, 0x6e, 0x88, 0x36, 0xf6, 0x6f, 0x2a, 0xc1, 0x6e, 0xfa, 0x45, 0xad, 0xbc, 0x3f, 0x0b, 0x46, 0x81, 0x4d, 0x46, 0xea, 0x7a, 0x9a, 0x83, 0x9a, 0xa9, 0xdd, 0xbb, 0xec, 0x7b, 0x06, 0x5b, 0xe5, 0xcf, 0x2e, 0x69, 0xfa, 0x5c, 0xcd, 0x7b, 0x14, 0x5e, 0xa5, 0xee, 0xf5, 0xb8, 0x7d, 0xdd, 0x99, 0xba, 0xef, 0x91, 0x16, 0x5b, 0x36, 0xb6, 0x65, 0x0d, 0xac, 0xb2, 0x5b, 0xed, 0x34, 0x81, 0x7a, 0xbb, 0x46, 0x40, 0x6a, 0x9e, 0xb4, 0x39, 0x31, 0x13, 0x49, 0xda, 0xd2, 0x9b, 0xed, 0x1e, 0xc4, 0x24, 0xb3, 0x35, 0xb2, 0x88, 0x60, 0x06, 0xe6, 0x56, 0x98, 0x96, 0x79, 0x1e, 0x31, 0x51, 0xc9, 0x8f, 0xcb, 0x00, 0xe6, 0xb3, 0xe4, 0xf9, 0x2b, 0xcc, 0x7a, 0x94, 0xda, 0x96, 0xa9, 0x71, 0x77, 0x70, 0x79, 0xcd, 0x33, 0x97, 0x76, 0x3f, 0xcc, 0xc6, 0xa6, 0x9f, 0x2e, 0x99, 0xb9, 0xc6, 0x2a, 0x21, 0xe6, 0x73, 0xca, 0xe6, 0x4a, 0x51, 0x1a, 0x99, 0x1c, 0x28, 0x04, 0x93, 0xd0, 0x0e, 0xa4, 0xe4, 0xda, 0x5f, 0x50, 0xfe, 0x4a, 0xfe, 0x48, 0xb5, 0xb2, 0xc1, 0xe6, 0x1f, 0x31, 0x7e, 0xef, 0x52, 0x91, 0x43, 0xc3, 0x6e, 0x77, 0xf4, 0x22, 0x6d, 0xbf, 0xe4, 0x63, 0x0e, 0xbf, 0xca, 0x36, 0xeb, 0x5c, 0x84, 0xa5, 0x48, 0x7d, 0x3b, 0x61, 0xa1, 0xdb, 0x5b, 0x2c, 0x71, 0xda, 0x45, 0xc4, 0x28, 0x00, 0x81, 0xdb, 0x31, 0xc9, 0xb4, 0xb2, 0x3b, 0x5d, 0x27, 0xa5, 0x05, 0x1b, 0xc7, 0xdb, 0x10, 0xa9, 0xbd, 0xa6, 0x93, 0x0c, 0x75, 0xe4, 0x39, 0x35, 0x41, 0x3d, 0xc5, 0x06, 0xdb, 0x8e, 0xfd, 0x46, 0x5b, 0x1d, 0x98, 0x95, 0x4f, 0x46, 0xdb, 0xd5, 0xfb, 0x29, 0x5e, 0x9d, 0x0d, 0x32, 0xeb, 0x61, 0x4f, 0xff, 0xd3, 0xf1, 0x46, 0x9a, 0x16, 0x1b, 0x91, 0x71, 0x28, 0xac, 0x4a, 0x14, 0x30, 0x3e, 0x19, 0x54, 0xb9, 0x36, 0xc7, 0x9b, 0x2d, 0xd1, 0x6c, 0x45, 0xe3, 0xdc, 0xde, 0xc8, 0x95, 0x5b, 0x87, 0xf8, 0x41, 0x1d, 0x10, 0x54, 0x01, 0x98, 0x79, 0x25, 0xd1, 0xda, 0xe9, 0xe1, 0xb5, 0x9e, 0xac, 0xeb, 0x42, 0xba, 0x8e, 0xdf, 0x8c, 0x31, 0x21, 0x70, 0xb4, 0x5d, 0xbe, 0xc5, 0x7c, 0x2b, 0xed, 0xe1, 0x94, 0x18, 0xb9, 0x51, 0x3d, 0x03, 0x2c, 0x13, 0x6b, 0xf1, 0x42, 0x6e, 0xe2, 0xb7, 0x12, 0xa0, 0xdd, 0x50, 0x9f, 0x4f, 0x6f, 0xa7, 0x6f, 0xc7, 0x03, 0x61, 0xa0, 0x83, 0xb5, 0xf3, 0x97, 0x98, 0x20, 0x9c, 0x44, 0xea, 0xd0, 0xad, 0x48, 0x64, 0x90, 0x21, 0xd8, 0x9f, 0xa7, 0xa6, 0x44, 0xca, 0x99, 0xc6, 0x36, 0xcb, 0x74, 0x5d, 0x7e, 0x5b, 0xfe, 0x31, 0x6a, 0x31, 0xf3, 0x8c, 0xd0, 0xad, 0x40, 0xa3, 0x1f, 0x7c, 0x44, 0xd6, 0x51, 0xd9, 0xe0, 0x5f, 0x9a, 0x7e, 0x41, 0x9f, 0x40, 0xf3, 0x14, 0xba, 0x85, 0xba, 0x34, 0xba, 0x2d, 0xfb, 0x34, 0xd0, 0xcf, 0x4f, 0xb0, 0xce, 0x6a, 0x51, 0xe9, 0xb0, 0x20, 0xf4, 0xf1, 0x19, 0xb2, 0xc3, 0x90, 0x11, 0x4e, 0x97, 0x55, 0x80, 0x83, 0xc4, 0x17, 0x7e, 0x4c, 0x79, 0x19, 0xfc, 0xd1, 0xe7, 0x78, 0x4b, 0x91, 0x1d, 0xae, 0x92, 0xa6, 0xf6, 0x46, 0x75, 0xe4, 0xad, 0x22, 0x1f, 0xdd, 0xa1, 0x07, 0xb3, 0x1e, 0xfe, 0xd9, 0x92, 0xeb, 0x4b, 0xed, 0xfd, 0x0a, 0xc2, 0x63, 0x27, 0xa4, 0x88, 0x17, 0x60, 0x49, 0x35, 0xdc, 0x8e, 0xa5, 0x7d, 0xab, 0xd3, 0x28, 0x90, 0x50, 0xcd, 0xed, 0x2d, 0xda, 0x15, 0x55, 0x51, 0xf1, 0x1a, 0x0a, 0xf7, 0x39, 0x5d, 0xaa, 0x77, 0x6f, 0x01, 0x8e, 0xa7, 0x7d, 0xfa, 0xff, 0x00, 0x66, 0x10, 0xa8, 0xb8, 0x63, 0x76, 0x90, 0xa8, 0x20, 0x06, 0x56, 0xdb, 0x61, 0xda, 0xbd, 0x4f, 0xcb, 0x24, 0x15, 0x0f, 0xf5, 0x66, 0xe5, 0x5f, 0x4c, 0x53, 0xc3, 0xb7, 0xce, 0x99, 0x6b, 0x17, 0xff, 0xd4, 0xf0, 0xec, 0x57, 0x6f, 0x32, 0xa5, 0xa4, 0x43, 0x76, 0x75, 0xa9, 0xf1, 0x03, 0xfa, 0x64, 0x08, 0x6c, 0x8e, 0xfb, 0x3d, 0x7f, 0xcb, 0x16, 0x2b, 0x3d, 0xbc, 0x16, 0xa3, 0x66, 0x6d, 0x98, 0xfb, 0x1e, 0xb9, 0xac, 0xc8, 0x77, 0xb7, 0x7d, 0x01, 0xb3, 0x37, 0xb8, 0xd3, 0x46, 0x95, 0x68, 0x86, 0xd2, 0x2e, 0x4e, 0xab, 0xf0, 0x23, 0x11, 0x4e, 0x5f, 0xcd, 0x98, 0xe7, 0x25, 0x96, 0x71, 0x83, 0x0f, 0xd6, 0x3c, 0xb9, 0xe7, 0x0d, 0x7c, 0x41, 0x22, 0x5e, 0xb3, 0x20, 0x0c, 0x65, 0x80, 0xc8, 0x63, 0x8e, 0xbb, 0x95, 0xa5, 0x07, 0xeb, 0xcc, 0xac, 0x73, 0x83, 0x4e, 0x5c, 0x59, 0x09, 0xd8, 0xec, 0xc8, 0x57, 0x41, 0xd3, 0x4e, 0x95, 0xa5, 0x5b, 0x4b, 0x6a, 0xcb, 0xab, 0x43, 0x10, 0x4b, 0xeb, 0x85, 0xa2, 0x2c, 0x8e, 0x3f, 0x68, 0x54, 0xf5, 0x00, 0xd3, 0x97, 0x7a, 0x65, 0x79, 0xa6, 0x24, 0x76, 0x6f, 0xd3, 0x62, 0x96, 0x30, 0x78, 0xcb, 0x21, 0xf2, 0xf4, 0x22, 0xce, 0x54, 0x8e, 0x46, 0x26, 0x10, 0x7e, 0x0a, 0xf5, 0xd8, 0xf5, 0x1f, 0x31, 0x98, 0x83, 0x73, 0xb3, 0x91, 0xcd, 0x67, 0xe6, 0x7d, 0xe8, 0x16, 0x69, 0x6f, 0x10, 0x1f, 0x54, 0x9a, 0x37, 0xf5, 0x41, 0x5e, 0x7f, 0x0a, 0x29, 0x62, 0x02, 0xf8, 0x9c, 0xc8, 0x8c, 0x77, 0x6a, 0x99, 0xa0, 0x89, 0xff, 0x00, 0x9c, 0x74, 0xd2, 0xed, 0xed, 0xfc, 0xbb, 0x7b, 0xaa, 0x9a, 0x7d, 0x62, 0xfe, 0x46, 0x2d, 0xfe, 0x4c, 0x51, 0x31, 0x11, 0xa9, 0xf6, 0xef, 0x9b, 0x30, 0x5e, 0x7b, 0x38, 0xdd, 0xf4, 0x7f, 0x95, 0x94, 0xbc, 0x12, 0x43, 0x30, 0x6a, 0xb2, 0xf3, 0x86, 0x40, 0x3e, 0xcb, 0xd7, 0x6a, 0xd7, 0xb1, 0xe9, 0x8f, 0x37, 0x19, 0x97, 0x41, 0x2c, 0x71, 0x20, 0xf5, 0x36, 0x9c, 0x55, 0x78, 0x1d, 0x8a, 0x91, 0xd7, 0x11, 0x14, 0x5a, 0x3e, 0x19, 0x03, 0x10, 0x6b, 0xca, 0xbd, 0x86, 0xf8, 0x9d, 0x95, 0x18, 0x36, 0x65, 0x2e, 0xbc, 0x54, 0x1f, 0xa2, 0x99, 0x00, 0x59, 0x2a, 0x6f, 0x5e, 0x55, 0x15, 0xe9, 0x5f, 0xc3, 0x2f, 0xb6, 0x14, 0xff, 0x00, 0xff, 0xd5, 0xf1, 0x95, 0xfe, 0x80, 0x74, 0x0d, 0x7c, 0xd9, 0x89, 0x3d, 0x78, 0x57, 0x8b, 0xc5, 0x28, 0xe8, 0x55, 0xf7, 0x1f, 0x48, 0xca, 0x38, 0xb8, 0x83, 0x9f, 0x93, 0x07, 0x85, 0x3a, 0x7a, 0x6f, 0x95, 0x66, 0x2b, 0x2c, 0x4c, 0x0d, 0x14, 0x00, 0x3e, 0x9c, 0xc3, 0x98, 0x76, 0xb8, 0x45, 0xbd, 0x02, 0xde, 0x48, 0xee, 0xdc, 0xa0, 0x15, 0xe2, 0x2b, 0xc8, 0x8a, 0x8a, 0xfd, 0x3b, 0x66, 0x3f, 0x00, 0x73, 0x84, 0x2d, 0x36, 0xb5, 0xb5, 0x9e, 0x35, 0x1c, 0x29, 0xc4, 0xfe, 0xc8, 0x04, 0x7f, 0xc4, 0x69, 0x91, 0xe1, 0x67, 0x2c, 0x4a, 0xd2, 0xe9, 0x4e, 0xe3, 0xd4, 0xf4, 0x81, 0x5a, 0x12, 0xc5, 0x41, 0x3f, 0x79, 0x38, 0x9b, 0x60, 0x20, 0x07, 0x34, 0xb0, 0xc9, 0x03, 0x5c, 0x23, 0x03, 0x53, 0x13, 0x56, 0x88, 0xdf, 0x09, 0xda, 0x9b, 0xd3, 0xb6, 0x52, 0x0e, 0xec, 0xe4, 0x29, 0x24, 0xfc, 0xd0, 0xe7, 0x75, 0xe5, 0x57, 0x6b, 0x61, 0xfb, 0xf0, 0xca, 0xaa, 0x57, 0xa8, 0xe6, 0x78, 0x1a, 0x7d, 0xf9, 0x95, 0x8a, 0x5e, 0xa0, 0xe3, 0x67, 0x8f, 0xa0, 0xbd, 0x5b, 0xf2, 0xdf, 0x4a, 0x82, 0xcb, 0x4a, 0xb3, 0xb0, 0xb4, 0x41, 0x0a, 0x70, 0x48, 0xd9, 0x57, 0x60, 0x51, 0x3a, 0x8f, 0xbc, 0xe6, 0x7b, 0xcb, 0xe4, 0x3b, 0xa7, 0x3f, 0x9b, 0x9f, 0x9a, 0xba, 0x77, 0xe5, 0x5f, 0x95, 0x9c, 0x59, 0x94, 0x9f, 0xcd, 0x37, 0x8c, 0xa9, 0xa6, 0xd9, 0x39, 0xaa, 0xd0, 0x7d, 0xa9, 0x1c, 0x03, 0x5e, 0x09, 0xff, 0x00, 0x0c, 0x76, 0xcb, 0x62, 0x2d, 0xa5, 0xf2, 0x85, 0xbf, 0xe7, 0x87, 0xe6, 0xa3, 0x5e, 0x4d, 0xa8, 0xc9, 0xe6, 0x8b, 0xd5, 0x69, 0x5c, 0xb0, 0x4a, 0xab, 0xc4, 0xb5, 0x35, 0x0a, 0xaa, 0xea, 0x40, 0x03, 0xa0, 0xf6, 0xcb, 0x40, 0x4d, 0x3e, 0xdb, 0xff, 0x00, 0x9c, 0x7f, 0xfc, 0xce, 0x4f, 0xcc, 0xbf, 0x26, 0x25, 0xe5, 0xd3, 0x2f, 0xe9, 0xdd, 0x3d, 0xfe, 0xab, 0xa9, 0xaa, 0xd2, 0xa6, 0x40, 0x2a, 0xb2, 0x71, 0x00, 0x01, 0xea, 0x0d, 0xe8, 0x3a, 0x64, 0x25, 0x16, 0x1c, 0x8b, 0xd9, 0x51, 0x39, 0x28, 0x12, 0x51, 0x41, 0xfd, 0xa3, 0xd2, 0xb9, 0x4f, 0x0d, 0x33, 0xb5, 0xf4, 0x87, 0x9d, 0x79, 0x0e, 0xb4, 0xaf, 0x6a, 0xf8, 0xf1, 0xf0, 0xc9, 0xda, 0xbf, 0xff, 0xd6, 0xf2, 0xc6, 0xb5, 0x68, 0x64, 0xd0, 0x6d, 0x35, 0x20, 0x39, 0xcd, 0x13, 0x0f, 0x5e, 0x61, 0xfc, 0x8f, 0x40, 0x8b, 0x5e, 0xe0, 0x66, 0x1c, 0x4f, 0xaa, 0x9d, 0xe6, 0xa6, 0x1e, 0x91, 0x2e, 0xa9, 0x87, 0x95, 0xee, 0x9c, 0xc5, 0x55, 0x34, 0x60, 0x40, 0xae, 0x57, 0x30, 0xd9, 0xa7, 0x95, 0xbd, 0x6f, 0xcb, 0x26, 0x39, 0x40, 0x0d, 0x4e, 0xc0, 0x9f, 0x9e, 0x50, 0x5d, 0xac, 0x79, 0x33, 0x8b, 0xbb, 0x9b, 0x3b, 0x6b, 0x35, 0x48, 0x54, 0x09, 0x29, 0x56, 0x7f, 0xe1, 0x86, 0x72, 0x00, 0x2c, 0x6e, 0xf7, 0x63, 0x3e, 0x63, 0xbd, 0xbd, 0x5d, 0x20, 0x2a, 0xb3, 0xa4, 0x33, 0x48, 0xab, 0x21, 0x43, 0xf1, 0x2c, 0x47, 0xed, 0x1d, 0xbc, 0x73, 0x18, 0x9b, 0x64, 0x28, 0x96, 0x3a, 0xc7, 0x49, 0xb0, 0xf4, 0xcc, 0xe9, 0x73, 0x6c, 0xb4, 0xf8, 0x67, 0x92, 0x32, 0x21, 0x70, 0x7b, 0x89, 0x05, 0x57, 0xef, 0x38, 0x28, 0x94, 0x4a, 0x7d, 0x13, 0x7d, 0x6a, 0xd3, 0x4c, 0xb8, 0xf2, 0xc3, 0xc8, 0x2e, 0x03, 0xf3, 0xe2, 0x7d, 0x33, 0xb7, 0xc5, 0xcc, 0x71, 0x03, 0xc6, 0xb9, 0x64, 0x06, 0xe2, 0x9a, 0xf2, 0x4f, 0xd2, 0x6d, 0xe9, 0xfe, 0x41, 0x45, 0x5b, 0x18, 0x66, 0xa5, 0x64, 0x09, 0xf4, 0xd5, 0xb7, 0xcd, 0x93, 0xc7, 0xcf, 0x9b, 0xe5, 0x6f, 0xf9, 0xc8, 0x0d, 0x56, 0xeb, 0x59, 0xfc, 0xce, 0xd5, 0x12, 0x61, 0xc4, 0x69, 0xe9, 0x0d, 0xa4, 0x4b, 0xfe, 0x48, 0x40, 0xd5, 0x3e, 0xe4, 0xb6, 0x64, 0x8e, 0x4c, 0x02, 0x61, 0x65, 0xa0, 0x14, 0xb4, 0xb6, 0xb0, 0xb1, 0xb6, 0xb2, 0x97, 0xcb, 0xf1, 0x5a, 0x2d, 0xc6, 0xa5, 0xac, 0xb4, 0x70, 0x5d, 0xc7, 0x3d, 0xc1, 0x51, 0x24, 0x91, 0xc9, 0x31, 0x75, 0x6b, 0x70, 0x9f, 0x14, 0x68, 0x01, 0x46, 0xe4, 0xb5, 0xa3, 0x17, 0xcb, 0x40, 0x61, 0x6f, 0x47, 0xff, 0x00, 0x9c, 0x3a, 0x8f, 0x5b, 0x4f, 0x3c, 0x6b, 0xb7, 0xfa, 0x30, 0x91, 0x3c, 0xa4, 0xb1, 0x95, 0xb9, 0x82, 0x42, 0x0a, 0xbc, 0x8e, 0xe4, 0xdb, 0xa9, 0xef, 0xc9, 0x17, 0x91, 0x24, 0x7c, 0xb2, 0x05, 0x64, 0xfb, 0x75, 0x64, 0x32, 0x39, 0x69, 0x5b, 0x9c, 0xad, 0xb9, 0xdb, 0xa7, 0xb5, 0x3b, 0x53, 0x2a, 0x21, 0x41, 0x44, 0xf3, 0x8b, 0x8f, 0x2e, 0x43, 0x9d, 0x2b, 0xd4, 0x57, 0x23, 0x41, 0x36, 0xff, 0x00, 0xff, 0xd7, 0xf0, 0xc0, 0xd5, 0xb5, 0x11, 0x64, 0xb6, 0x3f, 0x59, 0x90, 0xd9, 0xab, 0x06, 0xf4, 0x79, 0x7c, 0x3b, 0x74, 0xc8, 0x08, 0x8b, 0xb6, 0xe3, 0x96, 0x55, 0x57, 0xb3, 0x3e, 0xf2, 0x35, 0xc7, 0xd6, 0x0b, 0x45, 0x5d, 0xdc, 0x8a, 0x7d, 0xd9, 0x8d, 0x94, 0x3b, 0x3d, 0x1c, 0x9e, 0xc3, 0xe5, 0xc3, 0x2c, 0x7c, 0xc5, 0x0f, 0xee, 0xdb, 0x8b, 0x0c, 0xc4, 0x26, 0x9d, 0xa0, 0x9a, 0x7d, 0x2c, 0xe5, 0xe4, 0x55, 0x7f, 0xee, 0xc1, 0x15, 0x04, 0xd0, 0x12, 0x3c, 0x72, 0x89, 0x1b, 0x2c, 0xcc, 0xa8, 0x2a, 0x8b, 0x87, 0xbb, 0x63, 0x1a, 0x28, 0x65, 0xf0, 0xed, 0xf2, 0xc3, 0xc2, 0x0a, 0x06, 0x4a, 0x46, 0xc7, 0xa5, 0xa3, 0x59, 0xc8, 0xb2, 0xc7, 0x45, 0x22, 0x9c, 0x14, 0x54, 0x10, 0x46, 0xf5, 0x1d, 0x32, 0x5c, 0x14, 0x14, 0xe4, 0x32, 0x2f, 0x3a, 0xf3, 0xb6, 0x90, 0x9a, 0x6d, 0xae, 0x9f, 0x3d, 0xab, 0xb8, 0x8a, 0x3b, 0xf8, 0x39, 0x44, 0x58, 0xf0, 0x08, 0xd5, 0x14, 0xa5, 0x7b, 0x65, 0x98, 0x8e, 0xfb, 0xb5, 0x67, 0x87, 0xa5, 0xef, 0x5e, 0x44, 0x96, 0x35, 0xb5, 0xb6, 0x59, 0x36, 0xfd, 0xd8, 0xa0, 0xf1, 0x20, 0x53, 0x33, 0xc0, 0x79, 0x59, 0x73, 0x7c, 0xd7, 0xf9, 0xfb, 0xa2, 0xcd, 0x67, 0xf9, 0xa7, 0x7b, 0x72, 0xf1, 0x71, 0x83, 0x53, 0x86, 0x0b, 0x98, 0x24, 0x22, 0x8a, 0xcc, 0x88, 0x23, 0x7f, 0xb8, 0xae, 0xf9, 0x7c, 0x50, 0x1e, 0x5f, 0x7c, 0x48, 0x21, 0x44, 0x6b, 0xce, 0x9b, 0xb0, 0x1b, 0x9e, 0xf5, 0xaf, 0x8e, 0x4d, 0x5f, 0x7a, 0x7f, 0xce, 0x34, 0xf9, 0x5d, 0x3c, 0xa3, 0xf9, 0x69, 0x63, 0xa9, 0x3c, 0x27, 0xeb, 0xda, 0xe1, 0x37, 0xd7, 0x2e, 0xaa, 0xdb, 0x06, 0xda, 0x30, 0x49, 0xfe, 0x54, 0x03, 0x03, 0x49, 0xdc, 0xb3, 0xaf, 0x38, 0xfe, 0x6a, 0xf9, 0x47, 0xc9, 0x3a, 0x74, 0x97, 0xfa, 0xf6, 0xaf, 0x15, 0x85, 0xb8, 0x75, 0x89, 0xb8, 0x87, 0x9a, 0x72, 0xee, 0x2a, 0x14, 0x24, 0x60, 0xb1, 0xa8, 0xdf, 0x07, 0x0b, 0x2d, 0xcb, 0xcf, 0x7f, 0xe8, 0x6a, 0xff, 0x00, 0x26, 0xbd, 0x6a, 0x7f, 0x89, 0x2f, 0xf8, 0x52, 0x9e, 0xb7, 0xe8, 0xb9, 0xb8, 0x57, 0xc2, 0x95, 0xe9, 0x8f, 0x08, 0x5a, 0x2f, 0xff, 0xd0, 0xf0, 0x4d, 0x40, 0xaa, 0xd7, 0x00, 0x64, 0xcb, 0x3c, 0x97, 0xa8, 0xb5, 0x9e, 0xa3, 0x1a, 0xd6, 0x84, 0x95, 0x3f, 0x45, 0x72, 0x9c, 0xa2, 0xc3, 0x99, 0xa5, 0x9d, 0x49, 0xf4, 0x17, 0x97, 0xaf, 0x63, 0x17, 0x52, 0x6f, 0xf0, 0xc8, 0x43, 0x6f, 0x9a, 0xe9, 0x07, 0x70, 0x0e, 0xec, 0x83, 0x51, 0x44, 0xb8, 0x61, 0x1a, 0x9e, 0x11, 0xd3, 0x91, 0x60, 0x68, 0x6b, 0xd3, 0x31, 0x4f, 0x36, 0xd3, 0x4c, 0x52, 0xef, 0x4c, 0xd5, 0x0c, 0xc4, 0x69, 0xda, 0x94, 0xc8, 0x3a, 0xf0, 0x66, 0x07, 0x73, 0xe0, 0x40, 0xfd, 0x79, 0x93, 0x12, 0x1c, 0x9c, 0x32, 0xc7, 0xfc, 0x41, 0x33, 0xd2, 0xb4, 0x6f, 0x38, 0x98, 0x65, 0x76, 0xbf, 0x69, 0x42, 0xd0, 0xaa, 0xc9, 0xde, 0x95, 0xad, 0x28, 0x46, 0x4e, 0xac, 0x39, 0x77, 0x80, 0x11, 0xbf, 0xd8, 0xc7, 0x7c, 0xe1, 0xa5, 0xf9, 0x92, 0x4d, 0x32, 0x5b, 0x8b, 0x93, 0x27, 0xa7, 0x68, 0x56, 0xe2, 0x45, 0xda, 0x85, 0x61, 0x6e, 0x67, 0xad, 0x6b, 0xb0, 0x38, 0xc2, 0x81, 0xe4, 0xc7, 0x52, 0x31, 0x1c, 0x67, 0x86, 0x5b, 0xbd, 0x37, 0xca, 0x7a, 0x94, 0xb1, 0x69, 0xb6, 0x2e, 0xb7, 0x15, 0x48, 0xc2, 0xb4, 0x52, 0x53, 0xac, 0x32, 0xaf, 0xb1, 0xed, 0x9b, 0x10, 0x36, 0x78, 0x5c, 0x9f, 0x51, 0x64, 0x1f, 0x98, 0x3e, 0x58, 0xb6, 0xfc, 0xc8, 0xf2, 0xe5, 0xbc, 0x68, 0x52, 0x2d, 0x5a, 0xd1, 0x84, 0xb6, 0xf3, 0x95, 0x0e, 0xc0, 0x85, 0xe2, 0xcb, 0xd8, 0xd1, 0xbb, 0xe4, 0xc1, 0xa6, 0x97, 0xce, 0x17, 0x5f, 0x95, 0xde, 0x6d, 0xb6, 0xbe, 0xb7, 0x69, 0x34, 0xf3, 0x3c, 0x72, 0xcf, 0xe8, 0xa3, 0x45, 0x49, 0x95, 0x4a, 0x90, 0x3e, 0x35, 0x5a, 0x95, 0x1d, 0xfe, 0x21, 0x93, 0x4d, 0xbe, 0xd2, 0xd2, 0xf5, 0x8b, 0xbd, 0x32, 0x2d, 0x3f, 0x4c, 0x9a, 0xe4, 0xca, 0x9e, 0x90, 0x85, 0x65, 0x55, 0x08, 0x85, 0x91, 0x01, 0x3b, 0x0a, 0x05, 0xe9, 0xb0, 0xc0, 0x5a, 0xc3, 0xcd, 0x3f, 0x3b, 0x7f, 0x26, 0xec, 0xff, 0x00, 0x35, 0x6d, 0x6d, 0xb5, 0x3d, 0x16, 0xfe, 0x0d, 0x3b, 0xcd, 0x96, 0x01, 0x92, 0x46, 0x9e, 0xa2, 0x0b, 0xc8, 0xb7, 0x28, 0x92, 0x71, 0xfb, 0x2e, 0xa7, 0xec, 0x3d, 0x0f, 0xc2, 0x68, 0x71, 0x05, 0x95, 0xd3, 0xe7, 0x9f, 0xfa, 0x16, 0x2f, 0xcd, 0x7f, 0x43, 0xd6, 0xfa, 0xa5, 0x97, 0xab, 0xeb, 0x7a, 0x5f, 0x55, 0xfa, 0xec, 0x5e, 0xaf, 0x0f, 0xf7, 0xed, 0x2b, 0x4e, 0x15, 0xff, 0x00, 0x65, 0xdf, 0x8e, 0x14, 0xf1, 0xbf, 0xff, 0xd1, 0xf0, 0x5a, 0xa7, 0x18, 0x5e, 0x56, 0x1f, 0x68, 0x71, 0x5f, 0xa7, 0xbe, 0x2a, 0x98, 0xdb, 0xfa, 0x90, 0x24, 0x37, 0xb0, 0xfd, 0xb8, 0xa8, 0x58, 0x78, 0xae, 0x43, 0xc9, 0xb4, 0x6d, 0xbb, 0xda, 0x3c, 0xa1, 0xad, 0x43, 0xa8, 0xda, 0xc5, 0x2a, 0x3d, 0x26, 0x5a, 0x02, 0x2b, 0xbe, 0x60, 0x64, 0x8d, 0x17, 0x6f, 0x8b, 0x20, 0x90, 0x7a, 0x3c, 0x32, 0x8b, 0xa8, 0x02, 0xf3, 0xfd, 0xe0, 0x1b, 0x11, 0x98, 0x66, 0x3b, 0xb9, 0x62, 0x54, 0x83, 0x36, 0xf2, 0xa4, 0xe4, 0x29, 0x34, 0xeb, 0xc8, 0x74, 0xae, 0x0d, 0xc3, 0x65, 0x82, 0x13, 0x6b, 0x57, 0xba, 0x54, 0xe4, 0x8c, 0x41, 0x1b, 0x75, 0xa7, 0xe0, 0x72, 0x5c, 0x4c, 0x84, 0x50, 0x5a, 0xb3, 0xdd, 0xdd, 0xc3, 0x24, 0x33, 0xb1, 0x60, 0xe0, 0x86, 0x52, 0x45, 0x38, 0xd2, 0x87, 0x24, 0x26, 0x6d, 0x8c, 0xe1, 0x41, 0x25, 0xfc, 0xa3, 0xd7, 0x2f, 0x6f, 0x3c, 0xbf, 0x73, 0xa5, 0xb2, 0x2c, 0xd1, 0x69, 0x17, 0x2f, 0x6b, 0x14, 0x8c, 0x0f, 0x21, 0x0d, 0x79, 0x46, 0x09, 0x15, 0xed, 0xb7, 0x4e, 0xd9, 0xb9, 0x8b, 0xcb, 0xe4, 0xa2, 0x5e, 0xa3, 0xa6, 0xdf, 0x6a, 0x36, 0xe4, 0xcd, 0x69, 0x1c, 0x4e, 0x84, 0x7c, 0x76, 0xab, 0x21, 0x67, 0xa8, 0xa7, 0xd9, 0xf8, 0x4d, 0x2b, 0xf3, 0xc3, 0x4d, 0x49, 0x57, 0x98, 0x75, 0x6f, 0x31, 0xda, 0xf9, 0xa3, 0x4b, 0xfd, 0x1f, 0x69, 0x1d, 0xae, 0xa1, 0xa9, 0x7e, 0xee, 0xe6, 0xd2, 0x79, 0x18, 0xf3, 0xb5, 0x1f, 0xee, 0xd9, 0x0a, 0x01, 0x4e, 0x3f, 0xb3, 0x4d, 0xf2, 0x9c, 0xb9, 0x04, 0x05, 0xb7, 0xe2, 0x87, 0x1e, 0xdd, 0x19, 0x3e, 0xaf, 0x6b, 0xae, 0xcb, 0x6d, 0x13, 0x0d, 0x45, 0xa2, 0x8e, 0x06, 0xe5, 0x13, 0x2a, 0x02, 0x01, 0x5e, 0x82, 0xb5, 0x04, 0xe6, 0x11, 0xd4, 0xcd, 0xda, 0x43, 0x49, 0x8e, 0xb7, 0xdc, 0xb1, 0x51, 0xe6, 0x4d, 0x76, 0xd2, 0x61, 0x15, 0xaa, 0x4b, 0xa8, 0xc9, 0x6e, 0x49, 0x79, 0x20, 0xe6, 0x8c, 0x49, 0xad, 0x43, 0x16, 0xe4, 0xa7, 0xaf, 0x43, 0xd3, 0x26, 0x35, 0x75, 0xcd, 0xa8, 0xe8, 0x87, 0x46, 0xbf, 0xc7, 0x9a, 0xff, 0x00, 0xd6, 0xbf, 0x48, 0xfe, 0x88, 0xfd, 0xe7, 0x0f, 0xab, 0xfa, 0x3f, 0x58, 0x7f, 0x5f, 0x8d, 0x3f, 0x9f, 0xa7, 0x5e, 0xd4, 0xc3, 0xf9, 0xd1, 0x7c, 0xb6, 0x47, 0xe4, 0x3a, 0x5b, 0xff, 0xd2, 0xf0, 0xb7, 0xa6, 0x1e, 0xdf, 0xd3, 0xf6, 0xa5, 0x71, 0x54, 0xdb, 0x4b, 0x80, 0x3c, 0x42, 0x26, 0xee, 0x29, 0xbe, 0x51, 0x23, 0x4e, 0x44, 0x05, 0x84, 0x45, 0xa5, 0xd5, 0xf7, 0x97, 0x2e, 0xfd, 0x6b, 0x6a, 0x98, 0x09, 0xab, 0xc7, 0xfc, 0x46, 0x3b, 0x4c, 0x26, 0x32, 0x30, 0x3e, 0x4f, 0x49, 0xd0, 0xfc, 0xfb, 0x05, 0xd4, 0x4a, 0x7d, 0x40, 0xac, 0x3a, 0x8e, 0x84, 0x1c, 0xc5, 0x96, 0x2a, 0x73, 0xe1, 0x9c, 0x16, 0x6d, 0xa5, 0x79, 0x86, 0xd6, 0xec, 0x80, 0x5a, 0xa0, 0xf5, 0xca, 0xcc, 0x5c, 0xa1, 0x2b, 0x1b, 0x26, 0x30, 0x6a, 0x31, 0x46, 0xcf, 0x1c, 0x87, 0x94, 0x64, 0x9e, 0x3d, 0xb6, 0xf0, 0xca, 0xa8, 0x39, 0x51, 0x99, 0x42, 0x6b, 0x1a, 0xc5, 0xa5, 0xa5, 0x94, 0xf7, 0x92, 0xc8, 0xaa, 0xb1, 0x23, 0x30, 0x04, 0xf8, 0x0e, 0x9f, 0x4e, 0x4a, 0x11, 0xb2, 0xd5, 0x9b, 0x25, 0x06, 0x1b, 0xff, 0x00, 0x38, 0xfd, 0xad, 0xdf, 0xda, 0xf9, 0xa2, 0xfe, 0xc5, 0x42, 0xbe, 0x9b, 0x7f, 0x0b, 0xdd, 0xdd, 0x07, 0xaf, 0x14, 0x68, 0xd8, 0x71, 0x6d, 0xbb, 0x90, 0xfc, 0x73, 0x6e, 0xf2, 0xf2, 0xdd, 0xf4, 0xad, 0xa6, 0xab, 0x6d, 0x69, 0x14, 0xfa, 0xee, 0xa0, 0xe2, 0x0b, 0x0d, 0x39, 0x19, 0xfe, 0x11, 0xc5, 0x1a, 0x4a, 0x1d, 0x8f, 0x73, 0x4f, 0xf8, 0x96, 0x0b, 0x40, 0x8d, 0xec, 0xf3, 0x6d, 0x3f, 0x52, 0xba, 0xd6, 0x35, 0x8b, 0xbf, 0x36, 0x6a, 0x5f, 0x0d, 0xc5, 0xdc, 0xa8, 0xb6, 0xa8, 0x7a, 0xc5, 0x6c, 0x9b, 0x22, 0x0f, 0xa3, 0x73, 0x9a, 0xbc, 0xb3, 0xe2, 0x36, 0xed, 0xb1, 0x43, 0x80, 0x53, 0xd0, 0xa7, 0xd4, 0x44, 0xfa, 0x7a, 0xda, 0x83, 0xbd, 0x3e, 0x2f, 0xa7, 0x2b, 0xad, 0x9b, 0xb8, 0x8d, 0xa8, 0xe8, 0x91, 0xdb, 0xfa, 0x2d, 0x6f, 0xc3, 0x8a, 0x2d, 0x56, 0xa3, 0xad, 0x4f, 0x5c, 0xa4, 0x0d, 0xdc, 0xa3, 0xca, 0xd0, 0xbf, 0xa1, 0xe3, 0xfa, 0xe7, 0x0f, 0xf2, 0xb9, 0x57, 0xbf, 0x1a, 0xe4, 0xb8, 0x57, 0xc5, 0xdd, 0xff, 0xd3, 0xf0, 0xcc, 0x5d, 0x7b, 0x70, 0xc5, 0x53, 0x6d, 0x2f, 0xd5, 0xe4, 0x69, 0xfd, 0xdf, 0xec, 0xd7, 0xad, 0x7d, 0xb2, 0x8c, 0x8d, 0xd8, 0xed, 0x91, 0x9f, 0x43, 0xea, 0xe7, 0xeb, 0x94, 0xad, 0x3e, 0x1e, 0x95, 0xfc, 0x72, 0x81, 0x7d, 0x1c, 0x9d, 0xba, 0xb1, 0x7b, 0xdf, 0xa9, 0x7a, 0xdf, 0xee, 0x2f, 0xd4, 0xfa, 0xe7, 0xed, 0x7a, 0x7f, 0xdd, 0xff, 0x00, 0xb2, 0xae, 0x64, 0x0b, 0xea, 0xe3, 0x9a, 0xbf, 0x4a, 0x6f, 0xa4, 0xff, 0x00, 0x89, 0xbd, 0x45, 0xfa, 0xb5, 0x79, 0xf7, 0xeb, 0xc7, 0xe9, 0xae, 0x57, 0x2e, 0x17, 0x23, 0x1f, 0x89, 0xd1, 0x99, 0x8f, 0xf1, 0xa7, 0x11, 0xcf, 0xd3, 0xf5, 0x29, 0xb5, 0x6b, 0xd3, 0xe8, 0xcc, 0x7f, 0x45, 0xb9, 0xa3, 0xc5, 0x62, 0xbe, 0x68, 0xff, 0x00, 0x15, 0xfd, 0x4c, 0xfe, 0x90, 0xaf, 0xd4, 0xab, 0xf1, 0x7a, 0x7f, 0x62, 0x9d, 0xab, 0xdf, 0x32, 0xb1, 0x70, 0x5e, 0xdc, 0xdc, 0x2d, 0x47, 0x8b, 0x5e, 0xae, 0x4c, 0xbf, 0xf2, 0x37, 0x9f, 0x3d, 0x5b, 0xd2, 0xff, 0x00, 0x8e, 0x87, 0xee, 0x29, 0x5a, 0xf2, 0xf4, 0xaa, 0xd4, 0xa5, 0x36, 0xa7, 0x3a, 0x57, 0xfd, 0x8e, 0x64, 0x3a, 0xf2, 0xf6, 0xbf, 0xcc, 0x7f, 0x5b, 0xfc, 0x23, 0xa7, 0xfe, 0x8e, 0xff, 0x00, 0x8e, 0x37, 0xd6, 0x63, 0xfa, 0xe5, 0x2b, 0xcb, 0x87, 0xec, 0xd6, 0xbd, 0xb9, 0x7d, 0xac, 0xc7, 0xcd, 0x7c, 0x2d, 0xf8, 0x2b, 0x89, 0x26, 0x8f, 0xd4, 0xfa, 0x94, 0x3e, 0x85, 0x29, 0xc9, 0x69, 0xfc, 0x33, 0x58, 0x5d, 0x9c, 0x79, 0xb2, 0xbb, 0x0f, 0xac, 0x7a, 0x2b, 0xea, 0x75, 0xef, 0x92, 0x0c, 0x53, 0x3d, 0x2f, 0xd4, 0xfa, 0xbb, 0xfa, 0x74, 0xf5, 0x39, 0x9a, 0xd7, 0xe7, 0x80, 0x53, 0x79, 0xba, 0x5b, 0xfe, 0x97, 0xfa, 0x4b, 0xfc, 0xba, 0x7f, 0xb1, 0xc7, 0xab, 0x1e, 0x8f, 0xff, 0xd9 +}; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/testclient.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/testclient.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/testclient.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/testclient.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,148 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/testclient.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/timeutils.h" + +namespace rtc { + +// DESIGN: Each packet received is put it into a list of packets. +// Callers can retrieve received packets from any thread by calling +// NextPacket. + +TestClient::TestClient(AsyncPacketSocket* socket) + : socket_(socket), ready_to_send_(false) { + packets_ = new std::vector(); + socket_->SignalReadPacket.connect(this, &TestClient::OnPacket); + socket_->SignalReadyToSend.connect(this, &TestClient::OnReadyToSend); +} + +TestClient::~TestClient() { + delete socket_; + for (unsigned i = 0; i < packets_->size(); i++) + delete (*packets_)[i]; + delete packets_; +} + +bool TestClient::CheckConnState(AsyncPacketSocket::State state) { + // Wait for our timeout value until the socket reaches the desired state. + uint32 end = TimeAfter(kTimeout); + while (socket_->GetState() != state && TimeUntil(end) > 0) + Thread::Current()->ProcessMessages(1); + return (socket_->GetState() == state); +} + +int TestClient::Send(const char* buf, size_t size) { + rtc::PacketOptions options; + return socket_->Send(buf, size, options); +} + +int TestClient::SendTo(const char* buf, size_t size, + const SocketAddress& dest) { + rtc::PacketOptions options; + return socket_->SendTo(buf, size, dest, options); +} + +TestClient::Packet* TestClient::NextPacket() { + // If no packets are currently available, we go into a get/dispatch loop for + // at most 1 second. If, during the loop, a packet arrives, then we can stop + // early and return it. + + // Note that the case where no packet arrives is important. We often want to + // test that a packet does not arrive. + + // Note also that we only try to pump our current thread's message queue. + // Pumping another thread's queue could lead to messages being dispatched from + // the wrong thread to non-thread-safe objects. + + uint32 end = TimeAfter(kTimeout); + while (TimeUntil(end) > 0) { + { + CritScope cs(&crit_); + if (packets_->size() != 0) { + break; + } + } + Thread::Current()->ProcessMessages(1); + } + + // Return the first packet placed in the queue. + Packet* packet = NULL; + CritScope cs(&crit_); + if (packets_->size() > 0) { + packet = packets_->front(); + packets_->erase(packets_->begin()); + } + + return packet; +} + +bool TestClient::CheckNextPacket(const char* buf, size_t size, + SocketAddress* addr) { + bool res = false; + Packet* packet = NextPacket(); + if (packet) { + res = (packet->size == size && memcmp(packet->buf, buf, size) == 0); + if (addr) + *addr = packet->addr; + delete packet; + } + return res; +} + +bool TestClient::CheckNoPacket() { + bool res; + Packet* packet = NextPacket(); + res = (packet == NULL); + delete packet; + return res; +} + +int TestClient::GetError() { + return socket_->GetError(); +} + +int TestClient::SetOption(Socket::Option opt, int value) { + return socket_->SetOption(opt, value); +} + +bool TestClient::ready_to_send() const { + return ready_to_send_; +} + +void TestClient::OnPacket(AsyncPacketSocket* socket, const char* buf, + size_t size, const SocketAddress& remote_addr, + const PacketTime& packet_time) { + CritScope cs(&crit_); + packets_->push_back(new Packet(remote_addr, buf, size)); +} + +void TestClient::OnReadyToSend(AsyncPacketSocket* socket) { + ready_to_send_ = true; +} + +TestClient::Packet::Packet(const SocketAddress& a, const char* b, size_t s) + : addr(a), buf(0), size(s) { + buf = new char[size]; + memcpy(buf, b, size); +} + +TestClient::Packet::Packet(const Packet& p) + : addr(p.addr), buf(0), size(p.size) { + buf = new char[size]; + memcpy(buf, p.buf, size); +} + +TestClient::Packet::~Packet() { + delete[] buf; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/testclient.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/testclient.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/testclient.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/testclient.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,93 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_TESTCLIENT_H_ +#define WEBRTC_BASE_TESTCLIENT_H_ + +#include +#include "webrtc/base/asyncudpsocket.h" +#include "webrtc/base/criticalsection.h" + +namespace rtc { + +// A simple client that can send TCP or UDP data and check that it receives +// what it expects to receive. Useful for testing server functionality. +class TestClient : public sigslot::has_slots<> { + public: + // Records the contents of a packet that was received. + struct Packet { + Packet(const SocketAddress& a, const char* b, size_t s); + Packet(const Packet& p); + virtual ~Packet(); + + SocketAddress addr; + char* buf; + size_t size; + }; + + // Creates a client that will send and receive with the given socket and + // will post itself messages with the given thread. + explicit TestClient(AsyncPacketSocket* socket); + ~TestClient(); + + SocketAddress address() const { return socket_->GetLocalAddress(); } + SocketAddress remote_address() const { return socket_->GetRemoteAddress(); } + + // Checks that the socket moves to the specified connect state. + bool CheckConnState(AsyncPacketSocket::State state); + + // Checks that the socket is connected to the remote side. + bool CheckConnected() { + return CheckConnState(AsyncPacketSocket::STATE_CONNECTED); + } + + // Sends using the clients socket. + int Send(const char* buf, size_t size); + + // Sends using the clients socket to the given destination. + int SendTo(const char* buf, size_t size, const SocketAddress& dest); + + // Returns the next packet received by the client or 0 if none is received + // within a reasonable amount of time. The caller must delete the packet + // when done with it. + Packet* NextPacket(); + + // Checks that the next packet has the given contents. Returns the remote + // address that the packet was sent from. + bool CheckNextPacket(const char* buf, size_t len, SocketAddress* addr); + + // Checks that no packets have arrived or will arrive in the next second. + bool CheckNoPacket(); + + int GetError(); + int SetOption(Socket::Option opt, int value); + + bool ready_to_send() const; + + private: + static const int kTimeout = 1000; + // Workaround for the fact that AsyncPacketSocket::GetConnState doesn't exist. + Socket::ConnState GetState(); + // Slot for packets read on the socket. + void OnPacket(AsyncPacketSocket* socket, const char* buf, size_t len, + const SocketAddress& remote_addr, + const PacketTime& packet_time); + void OnReadyToSend(AsyncPacketSocket* socket); + + CriticalSection crit_; + AsyncPacketSocket* socket_; + std::vector* packets_; + bool ready_to_send_; + DISALLOW_EVIL_CONSTRUCTORS(TestClient); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_TESTCLIENT_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/testclient_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/testclient_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/testclient_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/testclient_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,78 @@ +/* + * Copyright 2006 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/nethelpers.h" +#include "webrtc/base/physicalsocketserver.h" +#include "webrtc/base/testclient.h" +#include "webrtc/base/testechoserver.h" +#include "webrtc/base/thread.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +using namespace rtc; + +void TestUdpInternal(const SocketAddress& loopback) { + Thread *main = Thread::Current(); + AsyncSocket* socket = main->socketserver() + ->CreateAsyncSocket(loopback.family(), SOCK_DGRAM); + socket->Bind(loopback); + + TestClient client(new AsyncUDPSocket(socket)); + SocketAddress addr = client.address(), from; + EXPECT_EQ(3, client.SendTo("foo", 3, addr)); + EXPECT_TRUE(client.CheckNextPacket("foo", 3, &from)); + EXPECT_EQ(from, addr); + EXPECT_TRUE(client.CheckNoPacket()); +} + +void TestTcpInternal(const SocketAddress& loopback) { + Thread *main = Thread::Current(); + TestEchoServer server(main, loopback); + + AsyncSocket* socket = main->socketserver() + ->CreateAsyncSocket(loopback.family(), SOCK_STREAM); + AsyncTCPSocket* tcp_socket = AsyncTCPSocket::Create( + socket, loopback, server.address()); + ASSERT_TRUE(tcp_socket != NULL); + + TestClient client(tcp_socket); + SocketAddress addr = client.address(), from; + EXPECT_TRUE(client.CheckConnected()); + EXPECT_EQ(3, client.Send("foo", 3)); + EXPECT_TRUE(client.CheckNextPacket("foo", 3, &from)); + EXPECT_EQ(from, server.address()); + EXPECT_TRUE(client.CheckNoPacket()); +} + +// Tests whether the TestClient can send UDP to itself. +TEST(TestClientTest, TestUdpIPv4) { + TestUdpInternal(SocketAddress("127.0.0.1", 0)); +} + +TEST(TestClientTest, TestUdpIPv6) { + if (HasIPv6Enabled()) { + TestUdpInternal(SocketAddress("::1", 0)); + } else { + LOG(LS_INFO) << "Skipping IPv6 test."; + } +} + +// Tests whether the TestClient can connect to a server and exchange data. +TEST(TestClientTest, TestTcpIPv4) { + TestTcpInternal(SocketAddress("127.0.0.1", 0)); +} + +TEST(TestClientTest, TestTcpIPv6) { + if (HasIPv6Enabled()) { + TestTcpInternal(SocketAddress("::1", 0)); + } else { + LOG(LS_INFO) << "Skipping IPv6 test."; + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/testechoserver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/testechoserver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/testechoserver.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/testechoserver.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,73 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_TESTECHOSERVER_H_ +#define WEBRTC_BASE_TESTECHOSERVER_H_ + +#include +#include "webrtc/base/asynctcpsocket.h" +#include "webrtc/base/socketaddress.h" +#include "webrtc/base/sigslot.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +// A test echo server, echoes back any packets sent to it. +// Useful for unit tests. +class TestEchoServer : public sigslot::has_slots<> { + public: + TestEchoServer(Thread* thread, const SocketAddress& addr) + : server_socket_(thread->socketserver()->CreateAsyncSocket(addr.family(), + SOCK_STREAM)) { + server_socket_->Bind(addr); + server_socket_->Listen(5); + server_socket_->SignalReadEvent.connect(this, &TestEchoServer::OnAccept); + } + ~TestEchoServer() { + for (ClientList::iterator it = client_sockets_.begin(); + it != client_sockets_.end(); ++it) { + delete *it; + } + } + + SocketAddress address() const { return server_socket_->GetLocalAddress(); } + + private: + void OnAccept(AsyncSocket* socket) { + AsyncSocket* raw_socket = socket->Accept(NULL); + if (raw_socket) { + AsyncTCPSocket* packet_socket = new AsyncTCPSocket(raw_socket, false); + packet_socket->SignalReadPacket.connect(this, &TestEchoServer::OnPacket); + packet_socket->SignalClose.connect(this, &TestEchoServer::OnClose); + client_sockets_.push_back(packet_socket); + } + } + void OnPacket(AsyncPacketSocket* socket, const char* buf, size_t size, + const SocketAddress& remote_addr, + const PacketTime& packet_time) { + rtc::PacketOptions options; + socket->Send(buf, size, options); + } + void OnClose(AsyncPacketSocket* socket, int err) { + ClientList::iterator it = + std::find(client_sockets_.begin(), client_sockets_.end(), socket); + client_sockets_.erase(it); + Thread::Current()->Dispose(socket); + } + + typedef std::list ClientList; + scoped_ptr server_socket_; + ClientList client_sockets_; + DISALLOW_EVIL_CONSTRUCTORS(TestEchoServer); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_TESTECHOSERVER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/testutils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/testutils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/testutils.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/testutils.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,629 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_TESTUTILS_H__ +#define WEBRTC_BASE_TESTUTILS_H__ + +// Utilities for testing rtc infrastructure in unittests + +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) +#include +#include + +// X defines a few macros that stomp on types that gunit.h uses. +#undef None +#undef Bool +#endif + +#include +#include +#include "webrtc/base/asyncsocket.h" +#include "webrtc/base/common.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/nethelpers.h" +#include "webrtc/base/pathutils.h" +#include "webrtc/base/stream.h" +#include "webrtc/base/stringencode.h" +#include "webrtc/base/stringutils.h" +#include "webrtc/base/thread.h" + +namespace testing { + +using namespace rtc; + +/////////////////////////////////////////////////////////////////////////////// +// StreamSink - Monitor asynchronously signalled events from StreamInterface +// or AsyncSocket (which should probably be a StreamInterface. +/////////////////////////////////////////////////////////////////////////////// + +// Note: Any event that is an error is treaded as SSE_ERROR instead of that +// event. + +enum StreamSinkEvent { + SSE_OPEN = SE_OPEN, + SSE_READ = SE_READ, + SSE_WRITE = SE_WRITE, + SSE_CLOSE = SE_CLOSE, + SSE_ERROR = 16 +}; + +class StreamSink : public sigslot::has_slots<> { + public: + void Monitor(StreamInterface* stream) { + stream->SignalEvent.connect(this, &StreamSink::OnEvent); + events_.erase(stream); + } + void Unmonitor(StreamInterface* stream) { + stream->SignalEvent.disconnect(this); + // In case you forgot to unmonitor a previous object with this address + events_.erase(stream); + } + bool Check(StreamInterface* stream, StreamSinkEvent event, bool reset = true) { + return DoCheck(stream, event, reset); + } + int Events(StreamInterface* stream, bool reset = true) { + return DoEvents(stream, reset); + } + + void Monitor(AsyncSocket* socket) { + socket->SignalConnectEvent.connect(this, &StreamSink::OnConnectEvent); + socket->SignalReadEvent.connect(this, &StreamSink::OnReadEvent); + socket->SignalWriteEvent.connect(this, &StreamSink::OnWriteEvent); + socket->SignalCloseEvent.connect(this, &StreamSink::OnCloseEvent); + // In case you forgot to unmonitor a previous object with this address + events_.erase(socket); + } + void Unmonitor(AsyncSocket* socket) { + socket->SignalConnectEvent.disconnect(this); + socket->SignalReadEvent.disconnect(this); + socket->SignalWriteEvent.disconnect(this); + socket->SignalCloseEvent.disconnect(this); + events_.erase(socket); + } + bool Check(AsyncSocket* socket, StreamSinkEvent event, bool reset = true) { + return DoCheck(socket, event, reset); + } + int Events(AsyncSocket* socket, bool reset = true) { + return DoEvents(socket, reset); + } + + private: + typedef std::map EventMap; + + void OnEvent(StreamInterface* stream, int events, int error) { + if (error) { + events = SSE_ERROR; + } + AddEvents(stream, events); + } + void OnConnectEvent(AsyncSocket* socket) { + AddEvents(socket, SSE_OPEN); + } + void OnReadEvent(AsyncSocket* socket) { + AddEvents(socket, SSE_READ); + } + void OnWriteEvent(AsyncSocket* socket) { + AddEvents(socket, SSE_WRITE); + } + void OnCloseEvent(AsyncSocket* socket, int error) { + AddEvents(socket, (0 == error) ? SSE_CLOSE : SSE_ERROR); + } + + void AddEvents(void* obj, int events) { + EventMap::iterator it = events_.find(obj); + if (events_.end() == it) { + events_.insert(EventMap::value_type(obj, events)); + } else { + it->second |= events; + } + } + bool DoCheck(void* obj, StreamSinkEvent event, bool reset) { + EventMap::iterator it = events_.find(obj); + if ((events_.end() == it) || (0 == (it->second & event))) { + return false; + } + if (reset) { + it->second &= ~event; + } + return true; + } + int DoEvents(void* obj, bool reset) { + EventMap::iterator it = events_.find(obj); + if (events_.end() == it) + return 0; + int events = it->second; + if (reset) { + it->second = 0; + } + return events; + } + + EventMap events_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamSource - Implements stream interface and simulates asynchronous +// events on the stream, without a network. Also buffers written data. +/////////////////////////////////////////////////////////////////////////////// + +class StreamSource : public StreamInterface { +public: + StreamSource() { + Clear(); + } + + void Clear() { + readable_data_.clear(); + written_data_.clear(); + state_ = SS_CLOSED; + read_block_ = 0; + write_block_ = SIZE_UNKNOWN; + } + void QueueString(const char* data) { + QueueData(data, strlen(data)); + } + void QueueStringF(const char* format, ...) { + va_list args; + va_start(args, format); + char buffer[1024]; + size_t len = vsprintfn(buffer, sizeof(buffer), format, args); + ASSERT(len < sizeof(buffer) - 1); + va_end(args); + QueueData(buffer, len); + } + void QueueData(const char* data, size_t len) { + readable_data_.insert(readable_data_.end(), data, data + len); + if ((SS_OPEN == state_) && (readable_data_.size() == len)) { + SignalEvent(this, SE_READ, 0); + } + } + std::string ReadData() { + std::string data; + // avoid accessing written_data_[0] if it is undefined + if (written_data_.size() > 0) { + data.insert(0, &written_data_[0], written_data_.size()); + } + written_data_.clear(); + return data; + } + void SetState(StreamState state) { + int events = 0; + if ((SS_OPENING == state_) && (SS_OPEN == state)) { + events |= SE_OPEN; + if (!readable_data_.empty()) { + events |= SE_READ; + } + } else if ((SS_CLOSED != state_) && (SS_CLOSED == state)) { + events |= SE_CLOSE; + } + state_ = state; + if (events) { + SignalEvent(this, events, 0); + } + } + // Will cause Read to block when there are pos bytes in the read queue. + void SetReadBlock(size_t pos) { read_block_ = pos; } + // Will cause Write to block when there are pos bytes in the write queue. + void SetWriteBlock(size_t pos) { write_block_ = pos; } + + virtual StreamState GetState() const { return state_; } + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + if (SS_CLOSED == state_) { + if (error) *error = -1; + return SR_ERROR; + } + if ((SS_OPENING == state_) || (readable_data_.size() <= read_block_)) { + return SR_BLOCK; + } + size_t count = _min(buffer_len, readable_data_.size() - read_block_); + memcpy(buffer, &readable_data_[0], count); + size_t new_size = readable_data_.size() - count; + // Avoid undefined access beyond the last element of the vector. + // This only happens when new_size is 0. + if (count < readable_data_.size()) { + memmove(&readable_data_[0], &readable_data_[count], new_size); + } + readable_data_.resize(new_size); + if (read) *read = count; + return SR_SUCCESS; + } + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error) { + if (SS_CLOSED == state_) { + if (error) *error = -1; + return SR_ERROR; + } + if (SS_OPENING == state_) { + return SR_BLOCK; + } + if (SIZE_UNKNOWN != write_block_) { + if (written_data_.size() >= write_block_) { + return SR_BLOCK; + } + if (data_len > (write_block_ - written_data_.size())) { + data_len = write_block_ - written_data_.size(); + } + } + if (written) *written = data_len; + const char* cdata = static_cast(data); + written_data_.insert(written_data_.end(), cdata, cdata + data_len); + return SR_SUCCESS; + } + virtual void Close() { state_ = SS_CLOSED; } + +private: + typedef std::vector Buffer; + Buffer readable_data_, written_data_; + StreamState state_; + size_t read_block_, write_block_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// SocketTestClient +// Creates a simulated client for testing. Works on real and virtual networks. +/////////////////////////////////////////////////////////////////////////////// + +class SocketTestClient : public sigslot::has_slots<> { +public: + SocketTestClient() { + Init(NULL, AF_INET); + } + SocketTestClient(AsyncSocket* socket) { + Init(socket, socket->GetLocalAddress().family()); + } + SocketTestClient(const SocketAddress& address) { + Init(NULL, address.family()); + socket_->Connect(address); + } + + AsyncSocket* socket() { return socket_.get(); } + + void QueueString(const char* data) { + QueueData(data, strlen(data)); + } + void QueueStringF(const char* format, ...) { + va_list args; + va_start(args, format); + char buffer[1024]; + size_t len = vsprintfn(buffer, sizeof(buffer), format, args); + ASSERT(len < sizeof(buffer) - 1); + va_end(args); + QueueData(buffer, len); + } + void QueueData(const char* data, size_t len) { + send_buffer_.insert(send_buffer_.end(), data, data + len); + if (Socket::CS_CONNECTED == socket_->GetState()) { + Flush(); + } + } + std::string ReadData() { + std::string data(&recv_buffer_[0], recv_buffer_.size()); + recv_buffer_.clear(); + return data; + } + + bool IsConnected() const { + return (Socket::CS_CONNECTED == socket_->GetState()); + } + bool IsClosed() const { + return (Socket::CS_CLOSED == socket_->GetState()); + } + +private: + typedef std::vector Buffer; + + void Init(AsyncSocket* socket, int family) { + if (!socket) { + socket = Thread::Current()->socketserver() + ->CreateAsyncSocket(family, SOCK_STREAM); + } + socket_.reset(socket); + socket_->SignalConnectEvent.connect(this, + &SocketTestClient::OnConnectEvent); + socket_->SignalReadEvent.connect(this, &SocketTestClient::OnReadEvent); + socket_->SignalWriteEvent.connect(this, &SocketTestClient::OnWriteEvent); + socket_->SignalCloseEvent.connect(this, &SocketTestClient::OnCloseEvent); + } + + void Flush() { + size_t sent = 0; + while (sent < send_buffer_.size()) { + int result = socket_->Send(&send_buffer_[sent], + send_buffer_.size() - sent); + if (result > 0) { + sent += result; + } else { + break; + } + } + size_t new_size = send_buffer_.size() - sent; + memmove(&send_buffer_[0], &send_buffer_[sent], new_size); + send_buffer_.resize(new_size); + } + + void OnConnectEvent(AsyncSocket* socket) { + if (!send_buffer_.empty()) { + Flush(); + } + } + void OnReadEvent(AsyncSocket* socket) { + char data[64 * 1024]; + int result = socket_->Recv(data, ARRAY_SIZE(data)); + if (result > 0) { + recv_buffer_.insert(recv_buffer_.end(), data, data + result); + } + } + void OnWriteEvent(AsyncSocket* socket) { + if (!send_buffer_.empty()) { + Flush(); + } + } + void OnCloseEvent(AsyncSocket* socket, int error) { + } + + scoped_ptr socket_; + Buffer send_buffer_, recv_buffer_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// SocketTestServer +// Creates a simulated server for testing. Works on real and virtual networks. +/////////////////////////////////////////////////////////////////////////////// + +class SocketTestServer : public sigslot::has_slots<> { + public: + SocketTestServer(const SocketAddress& address) + : socket_(Thread::Current()->socketserver() + ->CreateAsyncSocket(address.family(), SOCK_STREAM)) + { + socket_->SignalReadEvent.connect(this, &SocketTestServer::OnReadEvent); + socket_->Bind(address); + socket_->Listen(5); + } + virtual ~SocketTestServer() { + clear(); + } + + size_t size() const { return clients_.size(); } + SocketTestClient* client(size_t index) const { return clients_[index]; } + SocketTestClient* operator[](size_t index) const { return client(index); } + + void clear() { + for (size_t i=0; i(socket_->Accept(NULL)); + if (!accepted) + return; + clients_.push_back(new SocketTestClient(accepted)); + } + + scoped_ptr socket_; + std::vector clients_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Generic Utilities +/////////////////////////////////////////////////////////////////////////////// + +inline bool ReadFile(const char* filename, std::string* contents) { + FILE* fp = fopen(filename, "rb"); + if (!fp) + return false; + char buffer[1024*64]; + size_t read; + contents->clear(); + while ((read = fread(buffer, 1, sizeof(buffer), fp))) { + contents->append(buffer, read); + } + bool success = (0 != feof(fp)); + fclose(fp); + return success; +} + +// Look in parent dir for parallel directory. +inline rtc::Pathname GetSiblingDirectory( + const std::string& parallel_dir) { + rtc::Pathname path = rtc::Filesystem::GetCurrentDirectory(); + while (!path.empty()) { + rtc::Pathname potential_parallel_dir = path; + potential_parallel_dir.AppendFolder(parallel_dir); + if (rtc::Filesystem::IsFolder(potential_parallel_dir)) { + return potential_parallel_dir; + } + + path.SetFolder(path.parent_folder()); + } + return path; +} + +inline rtc::Pathname GetGoogle3Directory() { + return GetSiblingDirectory("google3"); +} + +inline rtc::Pathname GetTalkDirectory() { + return GetSiblingDirectory("talk"); +} + +/////////////////////////////////////////////////////////////////////////////// +// Unittest predicates which are similar to STREQ, but for raw memory +/////////////////////////////////////////////////////////////////////////////// + +inline AssertionResult CmpHelperMemEq(const char* expected_expression, + const char* expected_length_expression, + const char* actual_expression, + const char* actual_length_expression, + const void* expected, + size_t expected_length, + const void* actual, + size_t actual_length) +{ + if ((expected_length == actual_length) + && (0 == memcmp(expected, actual, expected_length))) { + return AssertionSuccess(); + } + + Message msg; + msg << "Value of: " << actual_expression + << " [" << actual_length_expression << "]"; + if (true) { //!actual_value.Equals(actual_expression)) { + size_t buffer_size = actual_length * 2 + 1; + char* buffer = STACK_ARRAY(char, buffer_size); + hex_encode(buffer, buffer_size, + reinterpret_cast(actual), actual_length); + msg << "\n Actual: " << buffer << " [" << actual_length << "]"; + } + + msg << "\nExpected: " << expected_expression + << " [" << expected_length_expression << "]"; + if (true) { //!expected_value.Equals(expected_expression)) { + size_t buffer_size = expected_length * 2 + 1; + char* buffer = STACK_ARRAY(char, buffer_size); + hex_encode(buffer, buffer_size, + reinterpret_cast(expected), expected_length); + msg << "\nWhich is: " << buffer << " [" << expected_length << "]"; + } + + return AssertionFailure(msg); +} + +inline AssertionResult CmpHelperFileEq(const char* expected_expression, + const char* expected_length_expression, + const char* actual_filename, + const void* expected, + size_t expected_length, + const char* filename) +{ + std::string contents; + if (!ReadFile(filename, &contents)) { + Message msg; + msg << "File '" << filename << "' could not be read."; + return AssertionFailure(msg); + } + return CmpHelperMemEq(expected_expression, expected_length_expression, + actual_filename, "", + expected, expected_length, + contents.c_str(), contents.size()); +} + +#define EXPECT_MEMEQ(expected, expected_length, actual, actual_length) \ + EXPECT_PRED_FORMAT4(::testing::CmpHelperMemEq, expected, expected_length, \ + actual, actual_length) + +#define ASSERT_MEMEQ(expected, expected_length, actual, actual_length) \ + ASSERT_PRED_FORMAT4(::testing::CmpHelperMemEq, expected, expected_length, \ + actual, actual_length) + +#define EXPECT_FILEEQ(expected, expected_length, filename) \ + EXPECT_PRED_FORMAT3(::testing::CmpHelperFileEq, expected, expected_length, \ + filename) + +#define ASSERT_FILEEQ(expected, expected_length, filename) \ + ASSERT_PRED_FORMAT3(::testing::CmpHelperFileEq, expected, expected_length, \ + filename) + +/////////////////////////////////////////////////////////////////////////////// +// Helpers for initializing constant memory with integers in a particular byte +// order +/////////////////////////////////////////////////////////////////////////////// + +#define BYTE_CAST(x) static_cast((x) & 0xFF) + +// Declare a N-bit integer as a little-endian sequence of bytes +#define LE16(x) BYTE_CAST(((uint16)x) >> 0), BYTE_CAST(((uint16)x) >> 8) + +#define LE32(x) BYTE_CAST(((uint32)x) >> 0), BYTE_CAST(((uint32)x) >> 8), \ + BYTE_CAST(((uint32)x) >> 16), BYTE_CAST(((uint32)x) >> 24) + +#define LE64(x) BYTE_CAST(((uint64)x) >> 0), BYTE_CAST(((uint64)x) >> 8), \ + BYTE_CAST(((uint64)x) >> 16), BYTE_CAST(((uint64)x) >> 24), \ + BYTE_CAST(((uint64)x) >> 32), BYTE_CAST(((uint64)x) >> 40), \ + BYTE_CAST(((uint64)x) >> 48), BYTE_CAST(((uint64)x) >> 56) + +// Declare a N-bit integer as a big-endian (Internet) sequence of bytes +#define BE16(x) BYTE_CAST(((uint16)x) >> 8), BYTE_CAST(((uint16)x) >> 0) + +#define BE32(x) BYTE_CAST(((uint32)x) >> 24), BYTE_CAST(((uint32)x) >> 16), \ + BYTE_CAST(((uint32)x) >> 8), BYTE_CAST(((uint32)x) >> 0) + +#define BE64(x) BYTE_CAST(((uint64)x) >> 56), BYTE_CAST(((uint64)x) >> 48), \ + BYTE_CAST(((uint64)x) >> 40), BYTE_CAST(((uint64)x) >> 32), \ + BYTE_CAST(((uint64)x) >> 24), BYTE_CAST(((uint64)x) >> 16), \ + BYTE_CAST(((uint64)x) >> 8), BYTE_CAST(((uint64)x) >> 0) + +// Declare a N-bit integer as a this-endian (local machine) sequence of bytes +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 1 +#endif // BIG_ENDIAN + +#if BIG_ENDIAN +#define TE16 BE16 +#define TE32 BE32 +#define TE64 BE64 +#else // !BIG_ENDIAN +#define TE16 LE16 +#define TE32 LE32 +#define TE64 LE64 +#endif // !BIG_ENDIAN + +/////////////////////////////////////////////////////////////////////////////// + +// Helpers for determining if X/screencasting is available (on linux). + +#define MAYBE_SKIP_SCREENCAST_TEST() \ + if (!testing::IsScreencastingAvailable()) { \ + LOG(LS_WARNING) << "Skipping test, since it doesn't have the requisite " \ + << "X environment for screen capture."; \ + return; \ + } \ + +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) +struct XDisplay { + XDisplay() : display_(XOpenDisplay(NULL)) { } + ~XDisplay() { if (display_) XCloseDisplay(display_); } + bool IsValid() const { return display_ != NULL; } + operator Display*() { return display_; } + private: + Display* display_; +}; +#endif + +// Returns true if screencasting is available. When false, anything that uses +// screencasting features may fail. +inline bool IsScreencastingAvailable() { +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) + XDisplay display; + if (!display.IsValid()) { + LOG(LS_WARNING) << "No X Display available."; + return false; + } + int ignored_int, major_version, minor_version; + if (!XRRQueryExtension(display, &ignored_int, &ignored_int) || + !XRRQueryVersion(display, &major_version, &minor_version) || + major_version < 1 || + (major_version < 2 && minor_version < 3)) { + LOG(LS_WARNING) << "XRandr version: " << major_version << "." + << minor_version; + LOG(LS_WARNING) << "XRandr is not supported or is too old (pre 1.3)."; + return false; + } +#endif + return true; +} +} // namespace testing + +#endif // WEBRTC_BASE_TESTUTILS_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/thread_annotations.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/thread_annotations.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/thread_annotations.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/thread_annotations.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,99 @@ +// +// Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// +// Borrowed from +// https://code.google.com/p/gperftools/source/browse/src/base/thread_annotations.h +// but adapted for clang attributes instead of the gcc. +// +// This header file contains the macro definitions for thread safety +// annotations that allow the developers to document the locking policies +// of their multi-threaded code. The annotations can also help program +// analysis tools to identify potential thread safety issues. + +#ifndef BASE_THREAD_ANNOTATIONS_H_ +#define BASE_THREAD_ANNOTATIONS_H_ + +#if defined(__clang__) && (!defined(SWIG)) +#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +// Document if a shared variable/field needs to be protected by a lock. +// GUARDED_BY allows the user to specify a particular lock that should be +// held when accessing the annotated variable, while GUARDED_VAR only +// indicates a shared variable should be guarded (by any lock). GUARDED_VAR +// is primarily used when the client cannot express the name of the lock. +#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) +#define GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(guarded) + +// Document if the memory location pointed to by a pointer should be guarded +// by a lock when dereferencing the pointer. Similar to GUARDED_VAR, +// PT_GUARDED_VAR is primarily used when the client cannot express the name +// of the lock. Note that a pointer variable to a shared memory location +// could itself be a shared variable. For example, if a shared global pointer +// q, which is guarded by mu1, points to a shared memory location that is +// guarded by mu2, q should be annotated as follows: +// int *q GUARDED_BY(mu1) PT_GUARDED_BY(mu2); +#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(point_to_guarded_by(x)) +#define PT_GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(point_to_guarded) + +// Document the acquisition order between locks that can be held +// simultaneously by a thread. For any two locks that need to be annotated +// to establish an acquisition order, only one of them needs the annotation. +// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER +// and ACQUIRED_BEFORE.) +#define ACQUIRED_AFTER(x) THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(x)) +#define ACQUIRED_BEFORE(x) THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(x)) + +// The following three annotations document the lock requirements for +// functions/methods. + +// Document if a function expects certain locks to be held before it is called +#define EXCLUSIVE_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) + +#define SHARED_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) + +// Document the locks acquired in the body of the function. These locks +// cannot be held when calling this function (as google3's Mutex locks are +// non-reentrant). +#define LOCKS_EXCLUDED(x) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(x)) + +// Document the lock the annotated function returns without acquiring it. +#define LOCK_RETURNED(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) + +// Document if a class/type is a lockable type (such as the Mutex class). +#define LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(lockable) + +// Document if a class is a scoped lockable type (such as the MutexLock class). +#define SCOPED_LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) + +// The following annotations specify lock and unlock primitives. +#define EXCLUSIVE_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) + +#define SHARED_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) + +#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) + +#define SHARED_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) + +#define UNLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) + +// An escape hatch for thread safety analysis to ignore the annotated function. +#define NO_THREAD_SAFETY_ANALYSIS \ + THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) + +#endif // BASE_THREAD_ANNOTATIONS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/thread.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/thread.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/thread.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/thread.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,627 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/thread.h" + +#ifndef __has_feature +#define __has_feature(x) 0 // Compatibility with non-clang or LLVM compilers. +#endif // __has_feature + +#if defined(WEBRTC_WIN) +#include +#elif defined(WEBRTC_POSIX) +#include +#endif + +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/stringutils.h" +#include "webrtc/base/timeutils.h" + +#if !__has_feature(objc_arc) && (defined(WEBRTC_MAC)) +#include "webrtc/base/maccocoathreadhelper.h" +#include "webrtc/base/scoped_autorelease_pool.h" +#endif + +namespace rtc { + +ThreadManager* ThreadManager::Instance() { + LIBJINGLE_DEFINE_STATIC_LOCAL(ThreadManager, thread_manager, ()); + return &thread_manager; +} + +// static +Thread* Thread::Current() { + return ThreadManager::Instance()->CurrentThread(); +} + +#if defined(WEBRTC_POSIX) +ThreadManager::ThreadManager() { + pthread_key_create(&key_, NULL); +#ifndef NO_MAIN_THREAD_WRAPPING + WrapCurrentThread(); +#endif +#if !__has_feature(objc_arc) && (defined(WEBRTC_MAC)) + // Under Automatic Reference Counting (ARC), you cannot use autorelease pools + // directly. Instead, you use @autoreleasepool blocks instead. Also, we are + // maintaining thread safety using immutability within context of GCD dispatch + // queues in this case. + InitCocoaMultiThreading(); +#endif +} + +ThreadManager::~ThreadManager() { +#if __has_feature(objc_arc) + @autoreleasepool +#elif defined(WEBRTC_MAC) + // This is called during exit, at which point apparently no NSAutoreleasePools + // are available; but we might still need them to do cleanup (or we get the + // "no autoreleasepool in place, just leaking" warning when exiting). + ScopedAutoreleasePool pool; +#endif + { + UnwrapCurrentThread(); + pthread_key_delete(key_); + } +} + +Thread *ThreadManager::CurrentThread() { + return static_cast(pthread_getspecific(key_)); +} + +void ThreadManager::SetCurrentThread(Thread *thread) { + pthread_setspecific(key_, thread); +} +#endif + +#if defined(WEBRTC_WIN) +ThreadManager::ThreadManager() { + key_ = TlsAlloc(); +#ifndef NO_MAIN_THREAD_WRAPPING + WrapCurrentThread(); +#endif +} + +ThreadManager::~ThreadManager() { + UnwrapCurrentThread(); + TlsFree(key_); +} + +Thread *ThreadManager::CurrentThread() { + return static_cast(TlsGetValue(key_)); +} + +void ThreadManager::SetCurrentThread(Thread *thread) { + TlsSetValue(key_, thread); +} +#endif + +Thread *ThreadManager::WrapCurrentThread() { + Thread* result = CurrentThread(); + if (NULL == result) { + result = new Thread(); + result->WrapCurrentWithThreadManager(this, true); + } + return result; +} + +void ThreadManager::UnwrapCurrentThread() { + Thread* t = CurrentThread(); + if (t && !(t->IsOwned())) { + t->UnwrapCurrent(); + delete t; + } +} + +struct ThreadInit { + Thread* thread; + Runnable* runnable; +}; + +Thread::ScopedDisallowBlockingCalls::ScopedDisallowBlockingCalls() + : thread_(Thread::Current()), + previous_state_(thread_->SetAllowBlockingCalls(false)) { +} + +Thread::ScopedDisallowBlockingCalls::~ScopedDisallowBlockingCalls() { + ASSERT(thread_->IsCurrent()); + thread_->SetAllowBlockingCalls(previous_state_); +} + +Thread::Thread(SocketServer* ss) + : MessageQueue(ss), + priority_(PRIORITY_NORMAL), + running_(true, false), +#if defined(WEBRTC_WIN) + thread_(NULL), + thread_id_(0), +#endif + owned_(true), + blocking_calls_allowed_(true) { + SetName("Thread", this); // default name +} + +Thread::~Thread() { + Stop(); + Clear(NULL); +} + +bool Thread::SleepMs(int milliseconds) { + AssertBlockingIsAllowedOnCurrentThread(); + +#if defined(WEBRTC_WIN) + ::Sleep(milliseconds); + return true; +#else + // POSIX has both a usleep() and a nanosleep(), but the former is deprecated, + // so we use nanosleep() even though it has greater precision than necessary. + struct timespec ts; + ts.tv_sec = milliseconds / 1000; + ts.tv_nsec = (milliseconds % 1000) * 1000000; + int ret = nanosleep(&ts, NULL); + if (ret != 0) { + LOG_ERR(LS_WARNING) << "nanosleep() returning early"; + return false; + } + return true; +#endif +} + +bool Thread::SetName(const std::string& name, const void* obj) { + if (running()) return false; + name_ = name; + if (obj) { + char buf[16]; + sprintfn(buf, sizeof(buf), " 0x%p", obj); + name_ += buf; + } + return true; +} + +bool Thread::SetPriority(ThreadPriority priority) { +#if defined(WEBRTC_WIN) + if (running()) { + ASSERT(thread_ != NULL); + BOOL ret = FALSE; + if (priority == PRIORITY_NORMAL) { + ret = ::SetThreadPriority(thread_, THREAD_PRIORITY_NORMAL); + } else if (priority == PRIORITY_HIGH) { + ret = ::SetThreadPriority(thread_, THREAD_PRIORITY_HIGHEST); + } else if (priority == PRIORITY_ABOVE_NORMAL) { + ret = ::SetThreadPriority(thread_, THREAD_PRIORITY_ABOVE_NORMAL); + } else if (priority == PRIORITY_IDLE) { + ret = ::SetThreadPriority(thread_, THREAD_PRIORITY_IDLE); + } + if (!ret) { + return false; + } + } + priority_ = priority; + return true; +#else + // TODO: Implement for Linux/Mac if possible. + if (running()) return false; + priority_ = priority; + return true; +#endif +} + +bool Thread::Start(Runnable* runnable) { + ASSERT(owned_); + if (!owned_) return false; + ASSERT(!running()); + if (running()) return false; + + Restart(); // reset fStop_ if the thread is being restarted + + // Make sure that ThreadManager is created on the main thread before + // we start a new thread. + ThreadManager::Instance(); + + ThreadInit* init = new ThreadInit; + init->thread = this; + init->runnable = runnable; +#if defined(WEBRTC_WIN) + DWORD flags = 0; + if (priority_ != PRIORITY_NORMAL) { + flags = CREATE_SUSPENDED; + } + thread_ = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PreRun, init, flags, + &thread_id_); + if (thread_) { + running_.Set(); + if (priority_ != PRIORITY_NORMAL) { + SetPriority(priority_); + ::ResumeThread(thread_); + } + } else { + return false; + } +#elif defined(WEBRTC_POSIX) + pthread_attr_t attr; + pthread_attr_init(&attr); + + // Thread priorities are not supported in NaCl. +#if !defined(__native_client__) + if (priority_ != PRIORITY_NORMAL) { + if (priority_ == PRIORITY_IDLE) { + // There is no POSIX-standard way to set a below-normal priority for an + // individual thread (only whole process), so let's not support it. + LOG(LS_WARNING) << "PRIORITY_IDLE not supported"; + } else { + // Set real-time round-robin policy. + if (pthread_attr_setschedpolicy(&attr, SCHED_RR) != 0) { + LOG(LS_ERROR) << "pthread_attr_setschedpolicy"; + } + struct sched_param param; + if (pthread_attr_getschedparam(&attr, ¶m) != 0) { + LOG(LS_ERROR) << "pthread_attr_getschedparam"; + } else { + // The numbers here are arbitrary. + if (priority_ == PRIORITY_HIGH) { + param.sched_priority = 6; // 6 = HIGH + } else { + ASSERT(priority_ == PRIORITY_ABOVE_NORMAL); + param.sched_priority = 4; // 4 = ABOVE_NORMAL + } + if (pthread_attr_setschedparam(&attr, ¶m) != 0) { + LOG(LS_ERROR) << "pthread_attr_setschedparam"; + } + } + } + } +#endif // !defined(__native_client__) + + int error_code = pthread_create(&thread_, &attr, PreRun, init); + if (0 != error_code) { + LOG(LS_ERROR) << "Unable to create pthread, error " << error_code; + return false; + } + running_.Set(); +#endif + return true; +} + +bool Thread::WrapCurrent() { + return WrapCurrentWithThreadManager(ThreadManager::Instance(), true); +} + +void Thread::UnwrapCurrent() { + // Clears the platform-specific thread-specific storage. + ThreadManager::Instance()->SetCurrentThread(NULL); +#if defined(WEBRTC_WIN) + if (thread_ != NULL) { + if (!CloseHandle(thread_)) { + LOG_GLE(LS_ERROR) << "When unwrapping thread, failed to close handle."; + } + thread_ = NULL; + } +#endif + running_.Reset(); +} + +void Thread::SafeWrapCurrent() { + WrapCurrentWithThreadManager(ThreadManager::Instance(), false); +} + +void Thread::Join() { + if (running()) { + ASSERT(!IsCurrent()); + if (Current() && !Current()->blocking_calls_allowed_) { + LOG(LS_WARNING) << "Waiting for the thread to join, " + << "but blocking calls have been disallowed"; + } + +#if defined(WEBRTC_WIN) + ASSERT(thread_ != NULL); + WaitForSingleObject(thread_, INFINITE); + CloseHandle(thread_); + thread_ = NULL; + thread_id_ = 0; +#elif defined(WEBRTC_POSIX) + void *pv; + pthread_join(thread_, &pv); +#endif + running_.Reset(); + } +} + +bool Thread::SetAllowBlockingCalls(bool allow) { + ASSERT(IsCurrent()); + bool previous = blocking_calls_allowed_; + blocking_calls_allowed_ = allow; + return previous; +} + +// static +void Thread::AssertBlockingIsAllowedOnCurrentThread() { +#ifdef _DEBUG + Thread* current = Thread::Current(); + ASSERT(!current || current->blocking_calls_allowed_); +#endif +} + +#if defined(WEBRTC_WIN) +// As seen on MSDN. +// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.71).aspx +#define MSDEV_SET_THREAD_NAME 0x406D1388 +typedef struct tagTHREADNAME_INFO { + DWORD dwType; + LPCSTR szName; + DWORD dwThreadID; + DWORD dwFlags; +} THREADNAME_INFO; + +void SetThreadName(DWORD dwThreadID, LPCSTR szThreadName) { + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + __try { + RaiseException(MSDEV_SET_THREAD_NAME, 0, sizeof(info) / sizeof(DWORD), + reinterpret_cast(&info)); + } + __except(EXCEPTION_CONTINUE_EXECUTION) { + } +} +#endif // WEBRTC_WIN + +void* Thread::PreRun(void* pv) { + ThreadInit* init = static_cast(pv); + ThreadManager::Instance()->SetCurrentThread(init->thread); +#if defined(WEBRTC_WIN) + SetThreadName(GetCurrentThreadId(), init->thread->name_.c_str()); +#elif defined(WEBRTC_POSIX) + // TODO: See if naming exists for pthreads. +#endif +#if __has_feature(objc_arc) + @autoreleasepool +#elif defined(WEBRTC_MAC) + // Make sure the new thread has an autoreleasepool + ScopedAutoreleasePool pool; +#endif + { + if (init->runnable) { + init->runnable->Run(init->thread); + } else { + init->thread->Run(); + } + delete init; + return NULL; + } +} + +void Thread::Run() { + ProcessMessages(kForever); +} + +bool Thread::IsOwned() { + return owned_; +} + +void Thread::Stop() { + MessageQueue::Quit(); + Join(); +} + +void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) { + if (fStop_) + return; + + // Sent messages are sent to the MessageHandler directly, in the context + // of "thread", like Win32 SendMessage. If in the right context, + // call the handler directly. + Message msg; + msg.phandler = phandler; + msg.message_id = id; + msg.pdata = pdata; + if (IsCurrent()) { + phandler->OnMessage(&msg); + return; + } + + AssertBlockingIsAllowedOnCurrentThread(); + + AutoThread thread; + Thread *current_thread = Thread::Current(); + ASSERT(current_thread != NULL); // AutoThread ensures this + + bool ready = false; + { + CritScope cs(&crit_); + _SendMessage smsg; + smsg.thread = current_thread; + smsg.msg = msg; + smsg.ready = &ready; + sendlist_.push_back(smsg); + } + + // Wait for a reply + + ss_->WakeUp(); + + bool waited = false; + crit_.Enter(); + while (!ready) { + crit_.Leave(); + // We need to limit "ReceiveSends" to |this| thread to avoid an arbitrary + // thread invoking calls on the current thread. + current_thread->ReceiveSendsFromThread(this); + current_thread->socketserver()->Wait(kForever, false); + waited = true; + crit_.Enter(); + } + crit_.Leave(); + + // Our Wait loop above may have consumed some WakeUp events for this + // MessageQueue, that weren't relevant to this Send. Losing these WakeUps can + // cause problems for some SocketServers. + // + // Concrete example: + // Win32SocketServer on thread A calls Send on thread B. While processing the + // message, thread B Posts a message to A. We consume the wakeup for that + // Post while waiting for the Send to complete, which means that when we exit + // this loop, we need to issue another WakeUp, or else the Posted message + // won't be processed in a timely manner. + + if (waited) { + current_thread->socketserver()->WakeUp(); + } +} + +void Thread::ReceiveSends() { + ReceiveSendsFromThread(NULL); +} + +void Thread::ReceiveSendsFromThread(const Thread* source) { + // Receive a sent message. Cleanup scenarios: + // - thread sending exits: We don't allow this, since thread can exit + // only via Join, so Send must complete. + // - thread receiving exits: Wakeup/set ready in Thread::Clear() + // - object target cleared: Wakeup/set ready in Thread::Clear() + _SendMessage smsg; + + crit_.Enter(); + while (PopSendMessageFromThread(source, &smsg)) { + crit_.Leave(); + + smsg.msg.phandler->OnMessage(&smsg.msg); + + crit_.Enter(); + *smsg.ready = true; + smsg.thread->socketserver()->WakeUp(); + } + crit_.Leave(); +} + +bool Thread::PopSendMessageFromThread(const Thread* source, _SendMessage* msg) { + for (std::list<_SendMessage>::iterator it = sendlist_.begin(); + it != sendlist_.end(); ++it) { + if (it->thread == source || source == NULL) { + *msg = *it; + sendlist_.erase(it); + return true; + } + } + return false; +} + +void Thread::Clear(MessageHandler *phandler, uint32 id, + MessageList* removed) { + CritScope cs(&crit_); + + // Remove messages on sendlist_ with phandler + // Object target cleared: remove from send list, wakeup/set ready + // if sender not NULL. + + std::list<_SendMessage>::iterator iter = sendlist_.begin(); + while (iter != sendlist_.end()) { + _SendMessage smsg = *iter; + if (smsg.msg.Match(phandler, id)) { + if (removed) { + removed->push_back(smsg.msg); + } else { + delete smsg.msg.pdata; + } + iter = sendlist_.erase(iter); + *smsg.ready = true; + smsg.thread->socketserver()->WakeUp(); + continue; + } + ++iter; + } + + MessageQueue::Clear(phandler, id, removed); +} + +bool Thread::ProcessMessages(int cmsLoop) { + uint32 msEnd = (kForever == cmsLoop) ? 0 : TimeAfter(cmsLoop); + int cmsNext = cmsLoop; + + while (true) { +#if __has_feature(objc_arc) + @autoreleasepool +#elif defined(WEBRTC_MAC) + // see: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSAutoreleasePool_Class/Reference/Reference.html + // Each thread is supposed to have an autorelease pool. Also for event loops + // like this, autorelease pool needs to be created and drained/released + // for each cycle. + ScopedAutoreleasePool pool; +#endif + { + Message msg; + if (!Get(&msg, cmsNext)) + return !IsQuitting(); + Dispatch(&msg); + + if (cmsLoop != kForever) { + cmsNext = TimeUntil(msEnd); + if (cmsNext < 0) + return true; + } + } + } +} + +bool Thread::WrapCurrentWithThreadManager(ThreadManager* thread_manager, + bool need_synchronize_access) { + if (running()) + return false; + +#if defined(WEBRTC_WIN) + if (need_synchronize_access) { + // We explicitly ask for no rights other than synchronization. + // This gives us the best chance of succeeding. + thread_ = OpenThread(SYNCHRONIZE, FALSE, GetCurrentThreadId()); + if (!thread_) { + LOG_GLE(LS_ERROR) << "Unable to get handle to thread."; + return false; + } + thread_id_ = GetCurrentThreadId(); + } +#elif defined(WEBRTC_POSIX) + thread_ = pthread_self(); +#endif + + owned_ = false; + running_.Set(); + thread_manager->SetCurrentThread(this); + return true; +} + +AutoThread::AutoThread(SocketServer* ss) : Thread(ss) { + if (!ThreadManager::Instance()->CurrentThread()) { + ThreadManager::Instance()->SetCurrentThread(this); + } +} + +AutoThread::~AutoThread() { + Stop(); + if (ThreadManager::Instance()->CurrentThread() == this) { + ThreadManager::Instance()->SetCurrentThread(NULL); + } +} + +#if defined(WEBRTC_WIN) +void ComThread::Run() { + HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + ASSERT(SUCCEEDED(hr)); + if (SUCCEEDED(hr)) { + Thread::Run(); + CoUninitialize(); + } else { + LOG(LS_ERROR) << "CoInitialize failed, hr=" << hr; + } +} +#endif + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/thread_checker.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/thread_checker.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/thread_checker.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/thread_checker.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Borrowed from Chromium's src/base/threading/thread_checker.h. + +#ifndef WEBRTC_BASE_THREAD_CHECKER_H_ +#define WEBRTC_BASE_THREAD_CHECKER_H_ + +// Apart from debug builds, we also enable the thread checker in +// builds with DCHECK_ALWAYS_ON so that trybots and waterfall bots +// with this define will get the same level of thread checking as +// debug bots. +// +// Note that this does not perfectly match situations where DCHECK is +// enabled. For example a non-official release build may have +// DCHECK_ALWAYS_ON undefined (and therefore ThreadChecker would be +// disabled) but have DCHECKs enabled at runtime. +#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) +#define ENABLE_THREAD_CHECKER 1 +#else +#define ENABLE_THREAD_CHECKER 0 +#endif + +#include "webrtc/base/thread_checker_impl.h" + +namespace rtc { + +// Do nothing implementation, for use in release mode. +// +// Note: You should almost always use the ThreadChecker class to get the +// right version for your build configuration. +class ThreadCheckerDoNothing { + public: + bool CalledOnValidThread() const { + return true; + } + + void DetachFromThread() {} +}; + +// ThreadChecker is a helper class used to help verify that some methods of a +// class are called from the same thread. It provides identical functionality to +// base::NonThreadSafe, but it is meant to be held as a member variable, rather +// than inherited from base::NonThreadSafe. +// +// While inheriting from base::NonThreadSafe may give a clear indication about +// the thread-safety of a class, it may also lead to violations of the style +// guide with regard to multiple inheritance. The choice between having a +// ThreadChecker member and inheriting from base::NonThreadSafe should be based +// on whether: +// - Derived classes need to know the thread they belong to, as opposed to +// having that functionality fully encapsulated in the base class. +// - Derived classes should be able to reassign the base class to another +// thread, via DetachFromThread. +// +// If neither of these are true, then having a ThreadChecker member and calling +// CalledOnValidThread is the preferable solution. +// +// Example: +// class MyClass { +// public: +// void Foo() { +// DCHECK(thread_checker_.CalledOnValidThread()); +// ... (do stuff) ... +// } +// +// private: +// ThreadChecker thread_checker_; +// } +// +// In Release mode, CalledOnValidThread will always return true. +#if ENABLE_THREAD_CHECKER +class ThreadChecker : public ThreadCheckerImpl { +}; +#else +class ThreadChecker : public ThreadCheckerDoNothing { +}; +#endif // ENABLE_THREAD_CHECKER + +#undef ENABLE_THREAD_CHECKER + +} // namespace rtc + +#endif // WEBRTC_BASE_THREAD_CHECKER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/thread_checker_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/thread_checker_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/thread_checker_impl.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/thread_checker_impl.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Borrowed from Chromium's src/base/threading/thread_checker_impl.cc. + +#include "webrtc/base/thread_checker_impl.h" + +#include "webrtc/base/thread.h" + +namespace rtc { + +ThreadCheckerImpl::ThreadCheckerImpl() + : valid_thread_() { + EnsureThreadIdAssigned(); +} + +ThreadCheckerImpl::~ThreadCheckerImpl() { +} + +bool ThreadCheckerImpl::CalledOnValidThread() const { + CritScope scoped_lock(&lock_); + EnsureThreadIdAssigned(); + return valid_thread_->IsCurrent(); +} + +void ThreadCheckerImpl::DetachFromThread() { + CritScope scoped_lock(&lock_); + valid_thread_ = NULL; +} + +void ThreadCheckerImpl::EnsureThreadIdAssigned() const { + if (!valid_thread_) { + valid_thread_ = Thread::Current(); + } +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/thread_checker_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/thread_checker_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/thread_checker_impl.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/thread_checker_impl.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Borrowed from Chromium's src/base/threading/thread_checker_impl.h. + +#ifndef WEBRTC_BASE_THREAD_CHECKER_IMPL_H_ +#define WEBRTC_BASE_THREAD_CHECKER_IMPL_H_ + +#include "webrtc/base/criticalsection.h" + +namespace rtc { + +class Thread; + +// Real implementation of ThreadChecker, for use in debug mode, or +// for temporary use in release mode (e.g. to CHECK on a threading issue +// seen only in the wild). +// +// Note: You should almost always use the ThreadChecker class to get the +// right version for your build configuration. +class ThreadCheckerImpl { + public: + ThreadCheckerImpl(); + ~ThreadCheckerImpl(); + + bool CalledOnValidThread() const; + + // Changes the thread that is checked for in CalledOnValidThread. This may + // be useful when an object may be created on one thread and then used + // exclusively on another thread. + void DetachFromThread(); + + private: + void EnsureThreadIdAssigned() const; + + mutable CriticalSection lock_; + // This is mutable so that CalledOnValidThread can set it. + // It's guarded by |lock_|. + mutable const Thread* valid_thread_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_THREAD_CHECKER_IMPL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/thread_checker_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/thread_checker_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/thread_checker_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/thread_checker_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Borrowed from Chromium's src/base/threading/thread_checker_unittest.cc. + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/checks.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/thread_checker.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +// Duplicated from base/threading/thread_checker.h so that we can be +// good citizens there and undef the macro. +#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) +#define ENABLE_THREAD_CHECKER 1 +#else +#define ENABLE_THREAD_CHECKER 0 +#endif + +namespace rtc { + +namespace { + +// Simple class to exercise the basics of ThreadChecker. +// Both the destructor and DoStuff should verify that they were +// called on the same thread as the constructor. +class ThreadCheckerClass : public ThreadChecker { + public: + ThreadCheckerClass() {} + + // Verifies that it was called on the same thread as the constructor. + void DoStuff() { + DCHECK(CalledOnValidThread()); + } + + void DetachFromThread() { + ThreadChecker::DetachFromThread(); + } + + static void MethodOnDifferentThreadImpl(); + static void DetachThenCallFromDifferentThreadImpl(); + + private: + DISALLOW_COPY_AND_ASSIGN(ThreadCheckerClass); +}; + +// Calls ThreadCheckerClass::DoStuff on another thread. +class CallDoStuffOnThread : public Thread { + public: + explicit CallDoStuffOnThread(ThreadCheckerClass* thread_checker_class) + : Thread(), + thread_checker_class_(thread_checker_class) { + SetName("call_do_stuff_on_thread", NULL); + } + + virtual void Run() OVERRIDE { + thread_checker_class_->DoStuff(); + } + + // New method. Needed since Thread::Join is protected, and it is called by + // the TEST. + void Join() { + Thread::Join(); + } + + private: + ThreadCheckerClass* thread_checker_class_; + + DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread); +}; + +// Deletes ThreadCheckerClass on a different thread. +class DeleteThreadCheckerClassOnThread : public Thread { + public: + explicit DeleteThreadCheckerClassOnThread( + ThreadCheckerClass* thread_checker_class) + : Thread(), + thread_checker_class_(thread_checker_class) { + SetName("delete_thread_checker_class_on_thread", NULL); + } + + virtual void Run() OVERRIDE { + thread_checker_class_.reset(); + } + + // New method. Needed since Thread::Join is protected, and it is called by + // the TEST. + void Join() { + Thread::Join(); + } + + private: + scoped_ptr thread_checker_class_; + + DISALLOW_COPY_AND_ASSIGN(DeleteThreadCheckerClassOnThread); +}; + +} // namespace + +TEST(ThreadCheckerTest, CallsAllowedOnSameThread) { + scoped_ptr thread_checker_class( + new ThreadCheckerClass); + + // Verify that DoStuff doesn't assert. + thread_checker_class->DoStuff(); + + // Verify that the destructor doesn't assert. + thread_checker_class.reset(); +} + +TEST(ThreadCheckerTest, DestructorAllowedOnDifferentThread) { + scoped_ptr thread_checker_class( + new ThreadCheckerClass); + + // Verify that the destructor doesn't assert + // when called on a different thread. + DeleteThreadCheckerClassOnThread delete_on_thread( + thread_checker_class.release()); + + delete_on_thread.Start(); + delete_on_thread.Join(); +} + +TEST(ThreadCheckerTest, DetachFromThread) { + scoped_ptr thread_checker_class( + new ThreadCheckerClass); + + // Verify that DoStuff doesn't assert when called on a different thread after + // a call to DetachFromThread. + thread_checker_class->DetachFromThread(); + CallDoStuffOnThread call_on_thread(thread_checker_class.get()); + + call_on_thread.Start(); + call_on_thread.Join(); +} + +#if GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER + +void ThreadCheckerClass::MethodOnDifferentThreadImpl() { + scoped_ptr thread_checker_class( + new ThreadCheckerClass); + + // DoStuff should assert in debug builds only when called on a + // different thread. + CallDoStuffOnThread call_on_thread(thread_checker_class.get()); + + call_on_thread.Start(); + call_on_thread.Join(); +} + +#if ENABLE_THREAD_CHECKER +TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThreadInDebug) { + ASSERT_DEATH({ + ThreadCheckerClass::MethodOnDifferentThreadImpl(); + }, ""); +} +#else +TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) { + ThreadCheckerClass::MethodOnDifferentThreadImpl(); +} +#endif // ENABLE_THREAD_CHECKER + +void ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl() { + scoped_ptr thread_checker_class( + new ThreadCheckerClass); + + // DoStuff doesn't assert when called on a different thread + // after a call to DetachFromThread. + thread_checker_class->DetachFromThread(); + CallDoStuffOnThread call_on_thread(thread_checker_class.get()); + + call_on_thread.Start(); + call_on_thread.Join(); + + // DoStuff should assert in debug builds only after moving to + // another thread. + thread_checker_class->DoStuff(); +} + +#if ENABLE_THREAD_CHECKER +TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) { + ASSERT_DEATH({ + ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl(); + }, ""); +} +#else +TEST(ThreadCheckerTest, DetachFromThreadInRelease) { + ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl(); +} +#endif // ENABLE_THREAD_CHECKER + +#endif // GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER + +// Just in case we ever get lumped together with other compilation units. +#undef ENABLE_THREAD_CHECKER + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/thread.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/thread.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/thread.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/thread.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,331 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_THREAD_H_ +#define WEBRTC_BASE_THREAD_H_ + +#include +#include +#include +#include + +#if defined(WEBRTC_POSIX) +#include +#endif +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/event.h" +#include "webrtc/base/messagequeue.h" + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32.h" +#endif + +namespace rtc { + +class Thread; + +class ThreadManager { + public: + ThreadManager(); + ~ThreadManager(); + + static ThreadManager* Instance(); + + Thread* CurrentThread(); + void SetCurrentThread(Thread* thread); + + // Returns a thread object with its thread_ ivar set + // to whatever the OS uses to represent the thread. + // If there already *is* a Thread object corresponding to this thread, + // this method will return that. Otherwise it creates a new Thread + // object whose wrapped() method will return true, and whose + // handle will, on Win32, be opened with only synchronization privileges - + // if you need more privilegs, rather than changing this method, please + // write additional code to adjust the privileges, or call a different + // factory method of your own devising, because this one gets used in + // unexpected contexts (like inside browser plugins) and it would be a + // shame to break it. It is also conceivable on Win32 that we won't even + // be able to get synchronization privileges, in which case the result + // will have a NULL handle. + Thread *WrapCurrentThread(); + void UnwrapCurrentThread(); + + private: +#if defined(WEBRTC_POSIX) + pthread_key_t key_; +#endif + +#if defined(WEBRTC_WIN) + DWORD key_; +#endif + + DISALLOW_COPY_AND_ASSIGN(ThreadManager); +}; + +struct _SendMessage { + _SendMessage() {} + Thread *thread; + Message msg; + bool *ready; +}; + +enum ThreadPriority { + PRIORITY_IDLE = -1, + PRIORITY_NORMAL = 0, + PRIORITY_ABOVE_NORMAL = 1, + PRIORITY_HIGH = 2, +}; + +class Runnable { + public: + virtual ~Runnable() {} + virtual void Run(Thread* thread) = 0; + + protected: + Runnable() {} + + private: + DISALLOW_COPY_AND_ASSIGN(Runnable); +}; + +// WARNING! SUBCLASSES MUST CALL Stop() IN THEIR DESTRUCTORS! See ~Thread(). + +class Thread : public MessageQueue { + public: + explicit Thread(SocketServer* ss = NULL); + // NOTE: ALL SUBCLASSES OF Thread MUST CALL Stop() IN THEIR DESTRUCTORS (or + // guarantee Stop() is explicitly called before the subclass is destroyed). + // This is required to avoid a data race between the destructor modifying the + // vtable, and the Thread::PreRun calling the virtual method Run(). + virtual ~Thread(); + + static Thread* Current(); + + // Used to catch performance regressions. Use this to disallow blocking calls + // (Invoke) for a given scope. If a synchronous call is made while this is in + // effect, an assert will be triggered. + // Note that this is a single threaded class. + class ScopedDisallowBlockingCalls { + public: + ScopedDisallowBlockingCalls(); + ~ScopedDisallowBlockingCalls(); + private: + Thread* const thread_; + const bool previous_state_; + }; + + bool IsCurrent() const { + return Current() == this; + } + + // Sleeps the calling thread for the specified number of milliseconds, during + // which time no processing is performed. Returns false if sleeping was + // interrupted by a signal (POSIX only). + static bool SleepMs(int millis); + + // Sets the thread's name, for debugging. Must be called before Start(). + // If |obj| is non-NULL, its value is appended to |name|. + const std::string& name() const { return name_; } + bool SetName(const std::string& name, const void* obj); + + // Sets the thread's priority. Must be called before Start(). + ThreadPriority priority() const { return priority_; } + bool SetPriority(ThreadPriority priority); + + // Starts the execution of the thread. + bool Start(Runnable* runnable = NULL); + + // Tells the thread to stop and waits until it is joined. + // Never call Stop on the current thread. Instead use the inherited Quit + // function which will exit the base MessageQueue without terminating the + // underlying OS thread. + virtual void Stop(); + + // By default, Thread::Run() calls ProcessMessages(kForever). To do other + // work, override Run(). To receive and dispatch messages, call + // ProcessMessages occasionally. + virtual void Run(); + + virtual void Send(MessageHandler *phandler, uint32 id = 0, + MessageData *pdata = NULL); + + // Convenience method to invoke a functor on another thread. Caller must + // provide the |ReturnT| template argument, which cannot (easily) be deduced. + // Uses Send() internally, which blocks the current thread until execution + // is complete. + // Ex: bool result = thread.Invoke(&MyFunctionReturningBool); + // NOTE: This function can only be called when synchronous calls are allowed. + // See ScopedDisallowBlockingCalls for details. + template + ReturnT Invoke(const FunctorT& functor) { + FunctorMessageHandler handler(functor); + Send(&handler); + return handler.result(); + } + + // From MessageQueue + virtual void Clear(MessageHandler *phandler, uint32 id = MQID_ANY, + MessageList* removed = NULL); + virtual void ReceiveSends(); + + // ProcessMessages will process I/O and dispatch messages until: + // 1) cms milliseconds have elapsed (returns true) + // 2) Stop() is called (returns false) + bool ProcessMessages(int cms); + + // Returns true if this is a thread that we created using the standard + // constructor, false if it was created by a call to + // ThreadManager::WrapCurrentThread(). The main thread of an application + // is generally not owned, since the OS representation of the thread + // obviously exists before we can get to it. + // You cannot call Start on non-owned threads. + bool IsOwned(); + +#if defined(WEBRTC_WIN) + HANDLE GetHandle() const { + return thread_; + } + DWORD GetId() const { + return thread_id_; + } +#elif defined(WEBRTC_POSIX) + pthread_t GetPThread() { + return thread_; + } +#endif + + // Expose private method running() for tests. + // + // DANGER: this is a terrible public API. Most callers that might want to + // call this likely do not have enough control/knowledge of the Thread in + // question to guarantee that the returned value remains true for the duration + // of whatever code is conditionally executing because of the return value! + bool RunningForTest() { return running(); } + + // Sets the per-thread allow-blocking-calls flag and returns the previous + // value. Must be called on this thread. + bool SetAllowBlockingCalls(bool allow); + + // These functions are public to avoid injecting test hooks. Don't call them + // outside of tests. + // This method should be called when thread is created using non standard + // method, like derived implementation of rtc::Thread and it can not be + // started by calling Start(). This will set started flag to true and + // owned to false. This must be called from the current thread. + bool WrapCurrent(); + void UnwrapCurrent(); + + protected: + // Same as WrapCurrent except that it never fails as it does not try to + // acquire the synchronization access of the thread. The caller should never + // call Stop() or Join() on this thread. + void SafeWrapCurrent(); + + // Blocks the calling thread until this thread has terminated. + void Join(); + + static void AssertBlockingIsAllowedOnCurrentThread(); + + friend class ScopedDisallowBlockingCalls; + + private: + static void *PreRun(void *pv); + + // ThreadManager calls this instead WrapCurrent() because + // ThreadManager::Instance() cannot be used while ThreadManager is + // being created. + // The method tries to get synchronization rights of the thread on Windows if + // |need_synchronize_access| is true. + bool WrapCurrentWithThreadManager(ThreadManager* thread_manager, + bool need_synchronize_access); + + // Return true if the thread was started and hasn't yet stopped. + bool running() { return running_.Wait(0); } + + // Processes received "Send" requests. If |source| is not NULL, only requests + // from |source| are processed, otherwise, all requests are processed. + void ReceiveSendsFromThread(const Thread* source); + + // If |source| is not NULL, pops the first "Send" message from |source| in + // |sendlist_|, otherwise, pops the first "Send" message of |sendlist_|. + // The caller must lock |crit_| before calling. + // Returns true if there is such a message. + bool PopSendMessageFromThread(const Thread* source, _SendMessage* msg); + + std::list<_SendMessage> sendlist_; + std::string name_; + ThreadPriority priority_; + Event running_; // Signalled means running. + +#if defined(WEBRTC_POSIX) + pthread_t thread_; +#endif + +#if defined(WEBRTC_WIN) + HANDLE thread_; + DWORD thread_id_; +#endif + + bool owned_; + bool blocking_calls_allowed_; // By default set to |true|. + + friend class ThreadManager; + + DISALLOW_COPY_AND_ASSIGN(Thread); +}; + +// AutoThread automatically installs itself at construction +// uninstalls at destruction, if a Thread object is +// _not already_ associated with the current OS thread. + +class AutoThread : public Thread { + public: + explicit AutoThread(SocketServer* ss = 0); + virtual ~AutoThread(); + + private: + DISALLOW_COPY_AND_ASSIGN(AutoThread); +}; + +// Win32 extension for threads that need to use COM +#if defined(WEBRTC_WIN) +class ComThread : public Thread { + public: + ComThread() {} + virtual ~ComThread() { Stop(); } + + protected: + virtual void Run(); + + private: + DISALLOW_COPY_AND_ASSIGN(ComThread); +}; +#endif + +// Provides an easy way to install/uninstall a socketserver on a thread. +class SocketServerScope { + public: + explicit SocketServerScope(SocketServer* ss) { + old_ss_ = Thread::Current()->socketserver(); + Thread::Current()->set_socketserver(ss); + } + ~SocketServerScope() { + Thread::Current()->set_socketserver(old_ss_); + } + + private: + SocketServer* old_ss_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(SocketServerScope); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_THREAD_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/thread_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/thread_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/thread_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/thread_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,546 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/asyncinvoker.h" +#include "webrtc/base/asyncudpsocket.h" +#include "webrtc/base/event.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/physicalsocketserver.h" +#include "webrtc/base/socketaddress.h" +#include "webrtc/base/thread.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +#if defined(WEBRTC_WIN) +#include // NOLINT +#endif + +using namespace rtc; + +// Generates a sequence of numbers (collaboratively). +class TestGenerator { + public: + TestGenerator() : last(0), count(0) {} + + int Next(int prev) { + int result = prev + last; + last = result; + count += 1; + return result; + } + + int last; + int count; +}; + +struct TestMessage : public MessageData { + explicit TestMessage(int v) : value(v) {} + virtual ~TestMessage() {} + + int value; +}; + +// Receives on a socket and sends by posting messages. +class SocketClient : public TestGenerator, public sigslot::has_slots<> { + public: + SocketClient(AsyncSocket* socket, const SocketAddress& addr, + Thread* post_thread, MessageHandler* phandler) + : socket_(AsyncUDPSocket::Create(socket, addr)), + post_thread_(post_thread), + post_handler_(phandler) { + socket_->SignalReadPacket.connect(this, &SocketClient::OnPacket); + } + + ~SocketClient() { + delete socket_; + } + + SocketAddress address() const { return socket_->GetLocalAddress(); } + + void OnPacket(AsyncPacketSocket* socket, const char* buf, size_t size, + const SocketAddress& remote_addr, + const PacketTime& packet_time) { + EXPECT_EQ(size, sizeof(uint32)); + uint32 prev = reinterpret_cast(buf)[0]; + uint32 result = Next(prev); + + post_thread_->PostDelayed(200, post_handler_, 0, new TestMessage(result)); + } + + private: + AsyncUDPSocket* socket_; + Thread* post_thread_; + MessageHandler* post_handler_; +}; + +// Receives messages and sends on a socket. +class MessageClient : public MessageHandler, public TestGenerator { + public: + MessageClient(Thread* pth, Socket* socket) + : socket_(socket) { + } + + virtual ~MessageClient() { + delete socket_; + } + + virtual void OnMessage(Message *pmsg) { + TestMessage* msg = static_cast(pmsg->pdata); + int result = Next(msg->value); + EXPECT_GE(socket_->Send(&result, sizeof(result)), 0); + delete msg; + } + + private: + Socket* socket_; +}; + +class CustomThread : public rtc::Thread { + public: + CustomThread() {} + virtual ~CustomThread() { Stop(); } + bool Start() { return false; } + + bool WrapCurrent() { + return Thread::WrapCurrent(); + } + void UnwrapCurrent() { + Thread::UnwrapCurrent(); + } +}; + + +// A thread that does nothing when it runs and signals an event +// when it is destroyed. +class SignalWhenDestroyedThread : public Thread { + public: + SignalWhenDestroyedThread(Event* event) + : event_(event) { + } + + virtual ~SignalWhenDestroyedThread() { + Stop(); + event_->Set(); + } + + virtual void Run() { + // Do nothing. + } + + private: + Event* event_; +}; + +// Function objects to test Thread::Invoke. +struct FunctorA { + int operator()() { return 42; } +}; +class FunctorB { + public: + explicit FunctorB(bool* flag) : flag_(flag) {} + void operator()() { if (flag_) *flag_ = true; } + private: + bool* flag_; +}; +struct FunctorC { + int operator()() { + Thread::Current()->ProcessMessages(50); + return 24; + } +}; + +// See: https://code.google.com/p/webrtc/issues/detail?id=2409 +TEST(ThreadTest, DISABLED_Main) { + const SocketAddress addr("127.0.0.1", 0); + + // Create the messaging client on its own thread. + Thread th1; + Socket* socket = th1.socketserver()->CreateAsyncSocket(addr.family(), + SOCK_DGRAM); + MessageClient msg_client(&th1, socket); + + // Create the socket client on its own thread. + Thread th2; + AsyncSocket* asocket = + th2.socketserver()->CreateAsyncSocket(addr.family(), SOCK_DGRAM); + SocketClient sock_client(asocket, addr, &th1, &msg_client); + + socket->Connect(sock_client.address()); + + th1.Start(); + th2.Start(); + + // Get the messages started. + th1.PostDelayed(100, &msg_client, 0, new TestMessage(1)); + + // Give the clients a little while to run. + // Messages will be processed at 100, 300, 500, 700, 900. + Thread* th_main = Thread::Current(); + th_main->ProcessMessages(1000); + + // Stop the sending client. Give the receiver a bit longer to run, in case + // it is running on a machine that is under load (e.g. the build machine). + th1.Stop(); + th_main->ProcessMessages(200); + th2.Stop(); + + // Make sure the results were correct + EXPECT_EQ(5, msg_client.count); + EXPECT_EQ(34, msg_client.last); + EXPECT_EQ(5, sock_client.count); + EXPECT_EQ(55, sock_client.last); +} + +// Test that setting thread names doesn't cause a malfunction. +// There's no easy way to verify the name was set properly at this time. +TEST(ThreadTest, Names) { + // Default name + Thread *thread; + thread = new Thread(); + EXPECT_TRUE(thread->Start()); + thread->Stop(); + delete thread; + thread = new Thread(); + // Name with no object parameter + EXPECT_TRUE(thread->SetName("No object", NULL)); + EXPECT_TRUE(thread->Start()); + thread->Stop(); + delete thread; + // Really long name + thread = new Thread(); + EXPECT_TRUE(thread->SetName("Abcdefghijklmnopqrstuvwxyz1234567890", this)); + EXPECT_TRUE(thread->Start()); + thread->Stop(); + delete thread; +} + +// Test that setting thread priorities doesn't cause a malfunction. +// There's no easy way to verify the priority was set properly at this time. +TEST(ThreadTest, Priorities) { + Thread *thread; + thread = new Thread(); + EXPECT_TRUE(thread->SetPriority(PRIORITY_HIGH)); + EXPECT_TRUE(thread->Start()); + thread->Stop(); + delete thread; + thread = new Thread(); + EXPECT_TRUE(thread->SetPriority(PRIORITY_ABOVE_NORMAL)); + EXPECT_TRUE(thread->Start()); + thread->Stop(); + delete thread; + + thread = new Thread(); + EXPECT_TRUE(thread->Start()); +#if defined(WEBRTC_WIN) + EXPECT_TRUE(thread->SetPriority(PRIORITY_ABOVE_NORMAL)); +#else + EXPECT_FALSE(thread->SetPriority(PRIORITY_ABOVE_NORMAL)); +#endif + thread->Stop(); + delete thread; + +} + +TEST(ThreadTest, Wrap) { + Thread* current_thread = Thread::Current(); + current_thread->UnwrapCurrent(); + CustomThread* cthread = new CustomThread(); + EXPECT_TRUE(cthread->WrapCurrent()); + EXPECT_TRUE(cthread->RunningForTest()); + EXPECT_FALSE(cthread->IsOwned()); + cthread->UnwrapCurrent(); + EXPECT_FALSE(cthread->RunningForTest()); + delete cthread; + current_thread->WrapCurrent(); +} + +TEST(ThreadTest, Invoke) { + // Create and start the thread. + Thread thread; + thread.Start(); + // Try calling functors. + EXPECT_EQ(42, thread.Invoke(FunctorA())); + bool called = false; + FunctorB f2(&called); + thread.Invoke(f2); + EXPECT_TRUE(called); + // Try calling bare functions. + struct LocalFuncs { + static int Func1() { return 999; } + static void Func2() {} + }; + EXPECT_EQ(999, thread.Invoke(&LocalFuncs::Func1)); + thread.Invoke(&LocalFuncs::Func2); +} + +// Verifies that two threads calling Invoke on each other at the same time does +// not deadlock. +TEST(ThreadTest, TwoThreadsInvokeNoDeadlock) { + AutoThread thread; + Thread* current_thread = Thread::Current(); + ASSERT_TRUE(current_thread != NULL); + + Thread other_thread; + other_thread.Start(); + + struct LocalFuncs { + static void Set(bool* out) { *out = true; } + static void InvokeSet(Thread* thread, bool* out) { + thread->Invoke(Bind(&Set, out)); + } + }; + + bool called = false; + other_thread.Invoke( + Bind(&LocalFuncs::InvokeSet, current_thread, &called)); + + EXPECT_TRUE(called); +} + +// Verifies that if thread A invokes a call on thread B and thread C is trying +// to invoke A at the same time, thread A does not handle C's invoke while +// invoking B. +TEST(ThreadTest, ThreeThreadsInvoke) { + AutoThread thread; + Thread* thread_a = Thread::Current(); + Thread thread_b, thread_c; + thread_b.Start(); + thread_c.Start(); + + class LockedBool { + public: + explicit LockedBool(bool value) : value_(value) {} + + void Set(bool value) { + CritScope lock(&crit_); + value_ = value; + } + + bool Get() { + CritScope lock(&crit_); + return value_; + } + + private: + CriticalSection crit_; + bool value_ GUARDED_BY(crit_); + }; + + struct LocalFuncs { + static void Set(LockedBool* out) { out->Set(true); } + static void InvokeSet(Thread* thread, LockedBool* out) { + thread->Invoke(Bind(&Set, out)); + } + + // Set |out| true and call InvokeSet on |thread|. + static void SetAndInvokeSet(LockedBool* out, + Thread* thread, + LockedBool* out_inner) { + out->Set(true); + InvokeSet(thread, out_inner); + } + + // Asynchronously invoke SetAndInvokeSet on |thread1| and wait until + // |thread1| starts the call. + static void AsyncInvokeSetAndWait( + Thread* thread1, Thread* thread2, LockedBool* out) { + CriticalSection crit; + LockedBool async_invoked(false); + + AsyncInvoker invoker; + invoker.AsyncInvoke( + thread1, Bind(&SetAndInvokeSet, &async_invoked, thread2, out)); + + EXPECT_TRUE_WAIT(async_invoked.Get(), 2000); + } + }; + + LockedBool thread_a_called(false); + + // Start the sequence A --(invoke)--> B --(async invoke)--> C --(invoke)--> A. + // Thread B returns when C receives the call and C should be blocked until A + // starts to process messages. + thread_b.Invoke(Bind(&LocalFuncs::AsyncInvokeSetAndWait, + &thread_c, thread_a, &thread_a_called)); + EXPECT_FALSE(thread_a_called.Get()); + + EXPECT_TRUE_WAIT(thread_a_called.Get(), 2000); +} + +class AsyncInvokeTest : public testing::Test { + public: + void IntCallback(int value) { + EXPECT_EQ(expected_thread_, Thread::Current()); + int_value_ = value; + } + void AsyncInvokeIntCallback(AsyncInvoker* invoker, Thread* thread) { + expected_thread_ = thread; + invoker->AsyncInvoke(thread, FunctorC(), + &AsyncInvokeTest::IntCallback, + static_cast(this)); + invoke_started_.Set(); + } + void SetExpectedThreadForIntCallback(Thread* thread) { + expected_thread_ = thread; + } + + protected: + enum { kWaitTimeout = 1000 }; + AsyncInvokeTest() + : int_value_(0), + invoke_started_(true, false), + expected_thread_(NULL) {} + + int int_value_; + Event invoke_started_; + Thread* expected_thread_; +}; + +TEST_F(AsyncInvokeTest, FireAndForget) { + AsyncInvoker invoker; + // Create and start the thread. + Thread thread; + thread.Start(); + // Try calling functor. + bool called = false; + invoker.AsyncInvoke(&thread, FunctorB(&called)); + EXPECT_TRUE_WAIT(called, kWaitTimeout); +} + +TEST_F(AsyncInvokeTest, WithCallback) { + AsyncInvoker invoker; + // Create and start the thread. + Thread thread; + thread.Start(); + // Try calling functor. + SetExpectedThreadForIntCallback(Thread::Current()); + invoker.AsyncInvoke(&thread, FunctorA(), + &AsyncInvokeTest::IntCallback, + static_cast(this)); + EXPECT_EQ_WAIT(42, int_value_, kWaitTimeout); +} + +TEST_F(AsyncInvokeTest, CancelInvoker) { + // Create and start the thread. + Thread thread; + thread.Start(); + // Try destroying invoker during call. + { + AsyncInvoker invoker; + invoker.AsyncInvoke(&thread, FunctorC(), + &AsyncInvokeTest::IntCallback, + static_cast(this)); + } + // With invoker gone, callback should be cancelled. + Thread::Current()->ProcessMessages(kWaitTimeout); + EXPECT_EQ(0, int_value_); +} + +TEST_F(AsyncInvokeTest, CancelCallingThread) { + AsyncInvoker invoker; + { // Create and start the thread. + Thread thread; + thread.Start(); + // Try calling functor. + thread.Invoke(Bind(&AsyncInvokeTest::AsyncInvokeIntCallback, + static_cast(this), + &invoker, Thread::Current())); + // Wait for the call to begin. + ASSERT_TRUE(invoke_started_.Wait(kWaitTimeout)); + } + // Calling thread is gone. Return message shouldn't happen. + Thread::Current()->ProcessMessages(kWaitTimeout); + EXPECT_EQ(0, int_value_); +} + +TEST_F(AsyncInvokeTest, KillInvokerBeforeExecute) { + Thread thread; + thread.Start(); + { + AsyncInvoker invoker; + // Try calling functor. + thread.Invoke(Bind(&AsyncInvokeTest::AsyncInvokeIntCallback, + static_cast(this), + &invoker, Thread::Current())); + // Wait for the call to begin. + ASSERT_TRUE(invoke_started_.Wait(kWaitTimeout)); + } + // Invoker is destroyed. Function should not execute. + Thread::Current()->ProcessMessages(kWaitTimeout); + EXPECT_EQ(0, int_value_); +} + +TEST_F(AsyncInvokeTest, Flush) { + AsyncInvoker invoker; + bool flag1 = false; + bool flag2 = false; + // Queue two async calls to the current thread. + invoker.AsyncInvoke(Thread::Current(), + FunctorB(&flag1)); + invoker.AsyncInvoke(Thread::Current(), + FunctorB(&flag2)); + // Because we haven't pumped messages, these should not have run yet. + EXPECT_FALSE(flag1); + EXPECT_FALSE(flag2); + // Force them to run now. + invoker.Flush(Thread::Current()); + EXPECT_TRUE(flag1); + EXPECT_TRUE(flag2); +} + +TEST_F(AsyncInvokeTest, FlushWithIds) { + AsyncInvoker invoker; + bool flag1 = false; + bool flag2 = false; + // Queue two async calls to the current thread, one with a message id. + invoker.AsyncInvoke(Thread::Current(), + FunctorB(&flag1), + 5); + invoker.AsyncInvoke(Thread::Current(), + FunctorB(&flag2)); + // Because we haven't pumped messages, these should not have run yet. + EXPECT_FALSE(flag1); + EXPECT_FALSE(flag2); + // Execute pending calls with id == 5. + invoker.Flush(Thread::Current(), 5); + EXPECT_TRUE(flag1); + EXPECT_FALSE(flag2); + flag1 = false; + // Execute all pending calls. The id == 5 call should not execute again. + invoker.Flush(Thread::Current()); + EXPECT_FALSE(flag1); + EXPECT_TRUE(flag2); +} + + +#if defined(WEBRTC_WIN) +class ComThreadTest : public testing::Test, public MessageHandler { + public: + ComThreadTest() : done_(false) {} + protected: + virtual void OnMessage(Message* message) { + HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + // S_FALSE means the thread was already inited for a multithread apartment. + EXPECT_EQ(S_FALSE, hr); + if (SUCCEEDED(hr)) { + CoUninitialize(); + } + done_ = true; + } + bool done_; +}; + +TEST_F(ComThreadTest, ComInited) { + Thread* thread = new ComThread(); + EXPECT_TRUE(thread->Start()); + thread->Post(this, 0); + EXPECT_TRUE_WAIT(done_, 1000); + delete thread; +} +#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/timeutils.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/timeutils.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/timeutils.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/timeutils.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,207 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#if defined(WEBRTC_POSIX) +#include +#if defined(WEBRTC_MAC) +#include +#endif +#endif + +#if defined(WEBRTC_WIN) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#endif + +#include "webrtc/base/checks.h" +#include "webrtc/base/timeutils.h" + +#define EFFICIENT_IMPLEMENTATION 1 + +namespace rtc { + +const uint32 HALF = 0x80000000; + +uint64 TimeNanos() { + int64 ticks = 0; +#if defined(WEBRTC_MAC) + static mach_timebase_info_data_t timebase; + if (timebase.denom == 0) { + // Get the timebase if this is the first time we run. + // Recommended by Apple's QA1398. + if (mach_timebase_info(&timebase) != KERN_SUCCESS) { + DCHECK(false); + } + } + // Use timebase to convert absolute time tick units into nanoseconds. + ticks = mach_absolute_time() * timebase.numer / timebase.denom; +#elif defined(WEBRTC_POSIX) + struct timespec ts; + // TODO: Do we need to handle the case when CLOCK_MONOTONIC + // is not supported? + clock_gettime(CLOCK_MONOTONIC, &ts); + ticks = kNumNanosecsPerSec * static_cast(ts.tv_sec) + + static_cast(ts.tv_nsec); +#elif defined(WEBRTC_WIN) + static volatile LONG last_timegettime = 0; + static volatile int64 num_wrap_timegettime = 0; + volatile LONG* last_timegettime_ptr = &last_timegettime; + DWORD now = timeGetTime(); + // Atomically update the last gotten time + DWORD old = InterlockedExchange(last_timegettime_ptr, now); + if (now < old) { + // If now is earlier than old, there may have been a race between + // threads. + // 0x0fffffff ~3.1 days, the code will not take that long to execute + // so it must have been a wrap around. + if (old > 0xf0000000 && now < 0x0fffffff) { + num_wrap_timegettime++; + } + } + ticks = now + (num_wrap_timegettime << 32); + // TODO: Calculate with nanosecond precision. Otherwise, we're just + // wasting a multiply and divide when doing Time() on Windows. + ticks = ticks * kNumNanosecsPerMillisec; +#endif + return ticks; +} + +uint32 Time() { + return static_cast(TimeNanos() / kNumNanosecsPerMillisec); +} + +uint64 TimeMicros() { + return static_cast(TimeNanos() / kNumNanosecsPerMicrosec); +} + +#if defined(WEBRTC_WIN) +static const uint64 kFileTimeToUnixTimeEpochOffset = 116444736000000000ULL; + +struct timeval { + long tv_sec, tv_usec; // NOLINT +}; + +// Emulate POSIX gettimeofday(). +// Based on breakpad/src/third_party/glog/src/utilities.cc +static int gettimeofday(struct timeval *tv, void *tz) { + // FILETIME is measured in tens of microseconds since 1601-01-01 UTC. + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + + LARGE_INTEGER li; + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + + // Convert to seconds and microseconds since Unix time Epoch. + int64 micros = (li.QuadPart - kFileTimeToUnixTimeEpochOffset) / 10; + tv->tv_sec = static_cast(micros / kNumMicrosecsPerSec); // NOLINT + tv->tv_usec = static_cast(micros % kNumMicrosecsPerSec); // NOLINT + + return 0; +} + +// Emulate POSIX gmtime_r(). +static struct tm *gmtime_r(const time_t *timep, struct tm *result) { + // On Windows, gmtime is thread safe. + struct tm *tm = gmtime(timep); // NOLINT + if (tm == NULL) { + return NULL; + } + *result = *tm; + return result; +} +#endif // WEBRTC_WIN + +void CurrentTmTime(struct tm *tm, int *microseconds) { + struct timeval timeval; + if (gettimeofday(&timeval, NULL) < 0) { + // Incredibly unlikely code path. + timeval.tv_sec = timeval.tv_usec = 0; + } + time_t secs = timeval.tv_sec; + gmtime_r(&secs, tm); + *microseconds = timeval.tv_usec; +} + +uint32 TimeAfter(int32 elapsed) { + DCHECK_GE(elapsed, 0); + DCHECK_LT(static_cast(elapsed), HALF); + return Time() + elapsed; +} + +bool TimeIsBetween(uint32 earlier, uint32 middle, uint32 later) { + if (earlier <= later) { + return ((earlier <= middle) && (middle <= later)); + } else { + return !((later < middle) && (middle < earlier)); + } +} + +bool TimeIsLaterOrEqual(uint32 earlier, uint32 later) { +#if EFFICIENT_IMPLEMENTATION + int32 diff = later - earlier; + return (diff >= 0 && static_cast(diff) < HALF); +#else + const bool later_or_equal = TimeIsBetween(earlier, later, earlier + HALF); + return later_or_equal; +#endif +} + +bool TimeIsLater(uint32 earlier, uint32 later) { +#if EFFICIENT_IMPLEMENTATION + int32 diff = later - earlier; + return (diff > 0 && static_cast(diff) < HALF); +#else + const bool earlier_or_equal = TimeIsBetween(later, earlier, later + HALF); + return !earlier_or_equal; +#endif +} + +int32 TimeDiff(uint32 later, uint32 earlier) { +#if EFFICIENT_IMPLEMENTATION + return later - earlier; +#else + const bool later_or_equal = TimeIsBetween(earlier, later, earlier + HALF); + if (later_or_equal) { + if (earlier <= later) { + return static_cast(later - earlier); + } else { + return static_cast(later + (UINT32_MAX - earlier) + 1); + } + } else { + if (later <= earlier) { + return -static_cast(earlier - later); + } else { + return -static_cast(earlier + (UINT32_MAX - later) + 1); + } + } +#endif +} + +TimestampWrapAroundHandler::TimestampWrapAroundHandler() + : last_ts_(0), num_wrap_(0) {} + +int64 TimestampWrapAroundHandler::Unwrap(uint32 ts) { + if (ts < last_ts_) { + if (last_ts_ > 0xf0000000 && ts < 0x0fffffff) { + ++num_wrap_; + } + } + last_ts_ = ts; + int64_t unwrapped_ts = ts + (num_wrap_ << 32); + return unwrapped_ts; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/timeutils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/timeutils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/timeutils.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/timeutils.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,96 @@ +/* + * Copyright 2005 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_TIMEUTILS_H_ +#define WEBRTC_BASE_TIMEUTILS_H_ + +#include + +#include "webrtc/base/basictypes.h" + +namespace rtc { + +static const int64 kNumMillisecsPerSec = INT64_C(1000); +static const int64 kNumMicrosecsPerSec = INT64_C(1000000); +static const int64 kNumNanosecsPerSec = INT64_C(1000000000); + +static const int64 kNumMicrosecsPerMillisec = kNumMicrosecsPerSec / + kNumMillisecsPerSec; +static const int64 kNumNanosecsPerMillisec = kNumNanosecsPerSec / + kNumMillisecsPerSec; +static const int64 kNumNanosecsPerMicrosec = kNumNanosecsPerSec / + kNumMicrosecsPerSec; + +// January 1970, in NTP milliseconds. +static const int64 kJan1970AsNtpMillisecs = INT64_C(2208988800000); + +typedef uint32 TimeStamp; + +// Returns the current time in milliseconds. +uint32 Time(); +// Returns the current time in microseconds. +uint64 TimeMicros(); +// Returns the current time in nanoseconds. +uint64 TimeNanos(); + +// Stores current time in *tm and microseconds in *microseconds. +void CurrentTmTime(struct tm *tm, int *microseconds); + +// Returns a future timestamp, 'elapsed' milliseconds from now. +uint32 TimeAfter(int32 elapsed); + +// Comparisons between time values, which can wrap around. +bool TimeIsBetween(uint32 earlier, uint32 middle, uint32 later); // Inclusive +bool TimeIsLaterOrEqual(uint32 earlier, uint32 later); // Inclusive +bool TimeIsLater(uint32 earlier, uint32 later); // Exclusive + +// Returns the later of two timestamps. +inline uint32 TimeMax(uint32 ts1, uint32 ts2) { + return TimeIsLaterOrEqual(ts1, ts2) ? ts2 : ts1; +} + +// Returns the earlier of two timestamps. +inline uint32 TimeMin(uint32 ts1, uint32 ts2) { + return TimeIsLaterOrEqual(ts1, ts2) ? ts1 : ts2; +} + +// Number of milliseconds that would elapse between 'earlier' and 'later' +// timestamps. The value is negative if 'later' occurs before 'earlier'. +int32 TimeDiff(uint32 later, uint32 earlier); + +// The number of milliseconds that have elapsed since 'earlier'. +inline int32 TimeSince(uint32 earlier) { + return TimeDiff(Time(), earlier); +} + +// The number of milliseconds that will elapse between now and 'later'. +inline int32 TimeUntil(uint32 later) { + return TimeDiff(later, Time()); +} + +// Converts a unix timestamp in nanoseconds to an NTP timestamp in ms. +inline int64 UnixTimestampNanosecsToNtpMillisecs(int64 unix_ts_ns) { + return unix_ts_ns / kNumNanosecsPerMillisec + kJan1970AsNtpMillisecs; +} + +class TimestampWrapAroundHandler { + public: + TimestampWrapAroundHandler(); + + int64 Unwrap(uint32 ts); + + private: + uint32 last_ts_; + int64 num_wrap_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_TIMEUTILS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/timeutils_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/timeutils_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/timeutils_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/timeutils_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,169 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/common.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/timeutils.h" + +namespace rtc { + +TEST(TimeTest, TimeInMs) { + uint32 ts_earlier = Time(); + Thread::SleepMs(100); + uint32 ts_now = Time(); + // Allow for the thread to wakeup ~20ms early. + EXPECT_GE(ts_now, ts_earlier + 80); + // Make sure the Time is not returning in smaller unit like microseconds. + EXPECT_LT(ts_now, ts_earlier + 1000); +} + +TEST(TimeTest, Comparison) { + // Obtain two different times, in known order + TimeStamp ts_earlier = Time(); + Thread::SleepMs(100); + TimeStamp ts_now = Time(); + EXPECT_NE(ts_earlier, ts_now); + + // Common comparisons + EXPECT_TRUE( TimeIsLaterOrEqual(ts_earlier, ts_now)); + EXPECT_TRUE( TimeIsLater( ts_earlier, ts_now)); + EXPECT_FALSE(TimeIsLaterOrEqual(ts_now, ts_earlier)); + EXPECT_FALSE(TimeIsLater( ts_now, ts_earlier)); + + // Edge cases + EXPECT_TRUE( TimeIsLaterOrEqual(ts_earlier, ts_earlier)); + EXPECT_FALSE(TimeIsLater( ts_earlier, ts_earlier)); + + // Obtain a third time + TimeStamp ts_later = TimeAfter(100); + EXPECT_NE(ts_now, ts_later); + EXPECT_TRUE( TimeIsLater(ts_now, ts_later)); + EXPECT_TRUE( TimeIsLater(ts_earlier, ts_later)); + + // Common comparisons + EXPECT_TRUE( TimeIsBetween(ts_earlier, ts_now, ts_later)); + EXPECT_FALSE(TimeIsBetween(ts_earlier, ts_later, ts_now)); + EXPECT_FALSE(TimeIsBetween(ts_now, ts_earlier, ts_later)); + EXPECT_TRUE( TimeIsBetween(ts_now, ts_later, ts_earlier)); + EXPECT_TRUE( TimeIsBetween(ts_later, ts_earlier, ts_now)); + EXPECT_FALSE(TimeIsBetween(ts_later, ts_now, ts_earlier)); + + // Edge cases + EXPECT_TRUE( TimeIsBetween(ts_earlier, ts_earlier, ts_earlier)); + EXPECT_TRUE( TimeIsBetween(ts_earlier, ts_earlier, ts_later)); + EXPECT_TRUE( TimeIsBetween(ts_earlier, ts_later, ts_later)); + + // Earlier of two times + EXPECT_EQ(ts_earlier, TimeMin(ts_earlier, ts_earlier)); + EXPECT_EQ(ts_earlier, TimeMin(ts_earlier, ts_now)); + EXPECT_EQ(ts_earlier, TimeMin(ts_earlier, ts_later)); + EXPECT_EQ(ts_earlier, TimeMin(ts_now, ts_earlier)); + EXPECT_EQ(ts_earlier, TimeMin(ts_later, ts_earlier)); + + // Later of two times + EXPECT_EQ(ts_earlier, TimeMax(ts_earlier, ts_earlier)); + EXPECT_EQ(ts_now, TimeMax(ts_earlier, ts_now)); + EXPECT_EQ(ts_later, TimeMax(ts_earlier, ts_later)); + EXPECT_EQ(ts_now, TimeMax(ts_now, ts_earlier)); + EXPECT_EQ(ts_later, TimeMax(ts_later, ts_earlier)); +} + +TEST(TimeTest, Intervals) { + TimeStamp ts_earlier = Time(); + TimeStamp ts_later = TimeAfter(500); + + // We can't depend on ts_later and ts_earlier to be exactly 500 apart + // since time elapses between the calls to Time() and TimeAfter(500) + EXPECT_LE(500, TimeDiff(ts_later, ts_earlier)); + EXPECT_GE(-500, TimeDiff(ts_earlier, ts_later)); + + // Time has elapsed since ts_earlier + EXPECT_GE(TimeSince(ts_earlier), 0); + + // ts_earlier is earlier than now, so TimeUntil ts_earlier is -ve + EXPECT_LE(TimeUntil(ts_earlier), 0); + + // ts_later likely hasn't happened yet, so TimeSince could be -ve + // but within 500 + EXPECT_GE(TimeSince(ts_later), -500); + + // TimeUntil ts_later is at most 500 + EXPECT_LE(TimeUntil(ts_later), 500); +} + +TEST(TimeTest, BoundaryComparison) { + // Obtain two different times, in known order + TimeStamp ts_earlier = static_cast(-50); + TimeStamp ts_later = ts_earlier + 100; + EXPECT_NE(ts_earlier, ts_later); + + // Common comparisons + EXPECT_TRUE( TimeIsLaterOrEqual(ts_earlier, ts_later)); + EXPECT_TRUE( TimeIsLater( ts_earlier, ts_later)); + EXPECT_FALSE(TimeIsLaterOrEqual(ts_later, ts_earlier)); + EXPECT_FALSE(TimeIsLater( ts_later, ts_earlier)); + + // Earlier of two times + EXPECT_EQ(ts_earlier, TimeMin(ts_earlier, ts_earlier)); + EXPECT_EQ(ts_earlier, TimeMin(ts_earlier, ts_later)); + EXPECT_EQ(ts_earlier, TimeMin(ts_later, ts_earlier)); + + // Later of two times + EXPECT_EQ(ts_earlier, TimeMax(ts_earlier, ts_earlier)); + EXPECT_EQ(ts_later, TimeMax(ts_earlier, ts_later)); + EXPECT_EQ(ts_later, TimeMax(ts_later, ts_earlier)); + + // Interval + EXPECT_EQ(100, TimeDiff(ts_later, ts_earlier)); + EXPECT_EQ(-100, TimeDiff(ts_earlier, ts_later)); +} + +TEST(TimeTest, DISABLED_CurrentTmTime) { + struct tm tm; + int microseconds; + + time_t before = ::time(NULL); + CurrentTmTime(&tm, µseconds); + time_t after = ::time(NULL); + + // Assert that 'tm' represents a time between 'before' and 'after'. + // mktime() uses local time, so we have to compensate for that. + time_t local_delta = before - ::mktime(::gmtime(&before)); // NOLINT + time_t t = ::mktime(&tm) + local_delta; + + EXPECT_TRUE(before <= t && t <= after); + EXPECT_TRUE(0 <= microseconds && microseconds < 1000000); +} + +class TimestampWrapAroundHandlerTest : public testing::Test { + public: + TimestampWrapAroundHandlerTest() {} + + protected: + TimestampWrapAroundHandler wraparound_handler_; +}; + +TEST_F(TimestampWrapAroundHandlerTest, Unwrap) { + uint32 ts = 0xfffffff2; + int64 unwrapped_ts = ts; + EXPECT_EQ(ts, wraparound_handler_.Unwrap(ts)); + ts = 2; + unwrapped_ts += 0x10; + EXPECT_EQ(unwrapped_ts, wraparound_handler_.Unwrap(ts)); + ts = 0xfffffff2; + unwrapped_ts += 0xfffffff0; + EXPECT_EQ(unwrapped_ts, wraparound_handler_.Unwrap(ts)); + ts = 0; + unwrapped_ts += 0xe; + EXPECT_EQ(unwrapped_ts, wraparound_handler_.Unwrap(ts)); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/timing.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/timing.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/timing.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/timing.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,113 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/timing.h" +#include "webrtc/base/timeutils.h" + +#if defined(WEBRTC_POSIX) +#include +#include +#include +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +#include +#include +#endif +#elif defined(WEBRTC_WIN) +#include +#include "webrtc/base/win32.h" +#endif + +namespace rtc { + +Timing::Timing() { +#if defined(WEBRTC_WIN) + // This may fail, but we handle failure gracefully in the methods + // that use it (use alternative sleep method). + // + // TODO: Make it possible for user to tell if IdleWait will + // be done at lesser resolution because of this. + timer_handle_ = CreateWaitableTimer(NULL, // Security attributes. + FALSE, // Manual reset? + NULL); // Timer name. +#endif +} + +Timing::~Timing() { +#if defined(WEBRTC_WIN) + if (timer_handle_ != NULL) + CloseHandle(timer_handle_); +#endif +} + +// static +double Timing::WallTimeNow() { +#if defined(WEBRTC_POSIX) + struct timeval time; + gettimeofday(&time, NULL); + // Convert from second (1.0) and microsecond (1e-6). + return (static_cast(time.tv_sec) + + static_cast(time.tv_usec) * 1.0e-6); + +#elif defined(WEBRTC_WIN) + struct _timeb time; + _ftime(&time); + // Convert from second (1.0) and milliseconds (1e-3). + return (static_cast(time.time) + + static_cast(time.millitm) * 1.0e-3); +#endif +} + +double Timing::TimerNow() { + return (static_cast(TimeNanos()) / kNumNanosecsPerSec); +} + +double Timing::BusyWait(double period) { + double start_time = TimerNow(); + while (TimerNow() - start_time < period) { + } + return TimerNow() - start_time; +} + +double Timing::IdleWait(double period) { + double start_time = TimerNow(); + +#if defined(WEBRTC_POSIX) + double sec_int, sec_frac = modf(period, &sec_int); + struct timespec ts; + ts.tv_sec = static_cast(sec_int); + ts.tv_nsec = static_cast(sec_frac * 1.0e9); // NOLINT + + // NOTE(liulk): for the NOLINT above, long is the appropriate POSIX + // type. + + // POSIX nanosleep may be interrupted by signals. + while (nanosleep(&ts, &ts) == -1 && errno == EINTR) { + } + +#elif defined(WEBRTC_WIN) + if (timer_handle_ != NULL) { + LARGE_INTEGER due_time; + + // Negative indicates relative time. The unit is 100 nanoseconds. + due_time.QuadPart = -LONGLONG(period * 1.0e7); + + SetWaitableTimer(timer_handle_, &due_time, 0, NULL, NULL, TRUE); + WaitForSingleObject(timer_handle_, INFINITE); + } else { + // Still attempts to sleep with lesser resolution. + // The unit is in milliseconds. + Sleep(DWORD(period * 1.0e3)); + } +#endif + + return TimerNow() - start_time; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/timing.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/timing.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/timing.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/timing.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,60 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_TIMING_H_ +#define WEBRTC_BASE_TIMING_H_ + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32.h" +#endif + +namespace rtc { + +class Timing { + public: + Timing(); + virtual ~Timing(); + + // WallTimeNow() returns the current wall-clock time in seconds, + // within 10 milliseconds resolution. + // WallTimeNow is static and does not require a timer_handle_ on Windows. + static double WallTimeNow(); + + // TimerNow() is like WallTimeNow(), but is monotonically + // increasing. It returns seconds in resolution of 10 microseconds + // or better. Although timer and wall-clock time have the same + // timing unit, they do not necessarily correlate because wall-clock + // time may be adjusted backwards, hence not monotonic. + // Made virtual so we can make a fake one. + virtual double TimerNow(); + + // BusyWait() exhausts CPU as long as the time elapsed is less than + // the specified interval in seconds. Returns the actual waiting + // time based on TimerNow() measurement. + double BusyWait(double period); + + // IdleWait() relinquishes control of CPU for specified period in + // seconds. It uses highest resolution sleep mechanism as possible, + // but does not otherwise guarantee the accuracy. Returns the + // actual waiting time based on TimerNow() measurement. + // + // This function is not re-entrant for an object. Create a fresh + // Timing object for each thread. + double IdleWait(double period); + + private: +#if defined(WEBRTC_WIN) + HANDLE timer_handle_; +#endif +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_TIMING_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/transformadapter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/transformadapter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/transformadapter.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/transformadapter.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,185 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/transformadapter.h" + +#include + +#include "webrtc/base/common.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// + +TransformAdapter::TransformAdapter(StreamInterface * stream, + TransformInterface * transform, + bool direction_read) + : StreamAdapterInterface(stream), transform_(transform), + direction_read_(direction_read), state_(ST_PROCESSING), len_(0) { +} + +TransformAdapter::~TransformAdapter() { + TransformAdapter::Close(); + delete transform_; +} + +StreamResult +TransformAdapter::Read(void * buffer, size_t buffer_len, + size_t * read, int * error) { + if (!direction_read_) + return SR_EOS; + + while (state_ != ST_ERROR) { + if (state_ == ST_COMPLETE) + return SR_EOS; + + // Buffer more data + if ((state_ == ST_PROCESSING) && (len_ < sizeof(buffer_))) { + size_t subread; + StreamResult result = StreamAdapterInterface::Read( + buffer_ + len_, + sizeof(buffer_) - len_, + &subread, + &error_); + if (result == SR_BLOCK) { + return SR_BLOCK; + } else if (result == SR_ERROR) { + state_ = ST_ERROR; + break; + } else if (result == SR_EOS) { + state_ = ST_FLUSHING; + } else { + len_ += subread; + } + } + + // Process buffered data + size_t in_len = len_; + size_t out_len = buffer_len; + StreamResult result = transform_->Transform(buffer_, &in_len, + buffer, &out_len, + (state_ == ST_FLUSHING)); + ASSERT(result != SR_BLOCK); + if (result == SR_EOS) { + // Note: Don't signal SR_EOS this iteration, unless out_len is zero + state_ = ST_COMPLETE; + } else if (result == SR_ERROR) { + state_ = ST_ERROR; + error_ = -1; // TODO: propagate error + break; + } else if ((out_len == 0) && (state_ == ST_FLUSHING)) { + // If there is no output AND no more input, then something is wrong + state_ = ST_ERROR; + error_ = -1; // TODO: better error code? + break; + } + + len_ -= in_len; + if (len_ > 0) + memmove(buffer_, buffer_ + in_len, len_); + + if (out_len == 0) + continue; + + if (read) + *read = out_len; + return SR_SUCCESS; + } + + if (error) + *error = error_; + return SR_ERROR; +} + +StreamResult +TransformAdapter::Write(const void * data, size_t data_len, + size_t * written, int * error) { + if (direction_read_) + return SR_EOS; + + size_t bytes_written = 0; + while (state_ != ST_ERROR) { + if (state_ == ST_COMPLETE) + return SR_EOS; + + if (len_ < sizeof(buffer_)) { + // Process buffered data + size_t in_len = data_len; + size_t out_len = sizeof(buffer_) - len_; + StreamResult result = transform_->Transform(data, &in_len, + buffer_ + len_, &out_len, + (state_ == ST_FLUSHING)); + + ASSERT(result != SR_BLOCK); + if (result == SR_EOS) { + // Note: Don't signal SR_EOS this iteration, unless no data written + state_ = ST_COMPLETE; + } else if (result == SR_ERROR) { + ASSERT(false); // When this happens, think about what should be done + state_ = ST_ERROR; + error_ = -1; // TODO: propagate error + break; + } + + len_ = out_len; + bytes_written = in_len; + } + + size_t pos = 0; + while (pos < len_) { + size_t subwritten; + StreamResult result = StreamAdapterInterface::Write(buffer_ + pos, + len_ - pos, + &subwritten, + &error_); + if (result == SR_BLOCK) { + ASSERT(false); // TODO: we should handle this + return SR_BLOCK; + } else if (result == SR_ERROR) { + state_ = ST_ERROR; + break; + } else if (result == SR_EOS) { + state_ = ST_COMPLETE; + break; + } + + pos += subwritten; + } + + len_ -= pos; + if (len_ > 0) + memmove(buffer_, buffer_ + pos, len_); + + if (bytes_written == 0) + continue; + + if (written) + *written = bytes_written; + return SR_SUCCESS; + } + + if (error) + *error = error_; + return SR_ERROR; +} + +void +TransformAdapter::Close() { + if (!direction_read_ && (state_ == ST_PROCESSING)) { + state_ = ST_FLUSHING; + do { + Write(0, 0, NULL, NULL); + } while (state_ == ST_FLUSHING); + } + state_ = ST_COMPLETE; + StreamAdapterInterface::Close(); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/transformadapter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/transformadapter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/transformadapter.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/transformadapter.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,80 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_TRANSFORMADAPTER_H__ +#define WEBRTC_BASE_TRANSFORMADAPTER_H__ + +#include "webrtc/base/stream.h" + +namespace rtc { +/////////////////////////////////////////////////////////////////////////////// + +class TransformInterface { +public: + virtual ~TransformInterface() { } + + // Transform should convert the in_len bytes of input into the out_len-sized + // output buffer. If flush is true, there will be no more data following + // input. + // After the transformation, in_len contains the number of bytes consumed, and + // out_len contains the number of bytes ready in output. + // Note: Transform should not return SR_BLOCK, as there is no asynchronous + // notification available. + virtual StreamResult Transform(const void * input, size_t * in_len, + void * output, size_t * out_len, + bool flush) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// + +// TransformAdapter causes all data passed through to be transformed by the +// supplied TransformInterface object, which may apply compression, encryption, +// etc. + +class TransformAdapter : public StreamAdapterInterface { +public: + // Note that the transformation is unidirectional, in the direction specified + // by the constructor. Operations in the opposite direction result in SR_EOS. + TransformAdapter(StreamInterface * stream, + TransformInterface * transform, + bool direction_read); + virtual ~TransformAdapter(); + + virtual StreamResult Read(void * buffer, size_t buffer_len, + size_t * read, int * error); + virtual StreamResult Write(const void * data, size_t data_len, + size_t * written, int * error); + virtual void Close(); + + // Apriori, we can't tell what the transformation does to the stream length. + virtual bool GetAvailable(size_t* size) const { return false; } + virtual bool ReserveSize(size_t size) { return true; } + + // Transformations might not be restartable + virtual bool Rewind() { return false; } + +private: + enum State { ST_PROCESSING, ST_FLUSHING, ST_COMPLETE, ST_ERROR }; + enum { BUFFER_SIZE = 1024 }; + + TransformInterface * transform_; + bool direction_read_; + State state_; + int error_; + + char buffer_[BUFFER_SIZE]; + size_t len_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_TRANSFORMADAPTER_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/unittest_main.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/unittest_main.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/unittest_main.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/unittest_main.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,107 @@ +/* + * Copyright 2007 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +// +// A reuseable entry point for gunit tests. + +#if defined(WEBRTC_WIN) +#include +#endif + +#include "webrtc/base/flags.h" +#include "webrtc/base/fileutils.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/ssladapter.h" + +DEFINE_bool(help, false, "prints this message"); +DEFINE_string(log, "", "logging options to use"); +#if defined(WEBRTC_WIN) +DEFINE_int(crt_break_alloc, -1, "memory allocation to break on"); +DEFINE_bool(default_error_handlers, false, + "leave the default exception/dbg handler functions in place"); + +void TestInvalidParameterHandler(const wchar_t* expression, + const wchar_t* function, + const wchar_t* file, + unsigned int line, + uintptr_t pReserved) { + LOG(LS_ERROR) << "InvalidParameter Handler called. Exiting."; + LOG(LS_ERROR) << expression << std::endl << function << std::endl << file + << std::endl << line; + exit(1); +} +void TestPureCallHandler() { + LOG(LS_ERROR) << "Purecall Handler called. Exiting."; + exit(1); +} +int TestCrtReportHandler(int report_type, char* msg, int* retval) { + LOG(LS_ERROR) << "CrtReport Handler called..."; + LOG(LS_ERROR) << msg; + if (report_type == _CRT_ASSERT) { + exit(1); + } else { + *retval = 0; + return TRUE; + } +} +#endif // WEBRTC_WIN + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, false); + if (FLAG_help) { + rtc::FlagList::Print(NULL, false); + return 0; + } + +#if defined(WEBRTC_WIN) + if (!FLAG_default_error_handlers) { + // Make sure any errors don't throw dialogs hanging the test run. + _set_invalid_parameter_handler(TestInvalidParameterHandler); + _set_purecall_handler(TestPureCallHandler); + _CrtSetReportHook2(_CRT_RPTHOOK_INSTALL, TestCrtReportHandler); + } + +#ifdef _DEBUG // Turn on memory leak checking on Windows. + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF |_CRTDBG_LEAK_CHECK_DF); + if (FLAG_crt_break_alloc >= 0) { + _crtBreakAlloc = FLAG_crt_break_alloc; + } +#endif // _DEBUG +#endif // WEBRTC_WIN + + rtc::Filesystem::SetOrganizationName("google"); + rtc::Filesystem::SetApplicationName("unittest"); + + // By default, log timestamps. Allow overrides by used of a --log flag. + rtc::LogMessage::LogTimestamps(); + if (*FLAG_log != '\0') { + rtc::LogMessage::ConfigureLogging(FLAG_log, "unittest.log"); + } + + // Initialize SSL which are used by several tests. + rtc::InitializeSSL(); + + int res = RUN_ALL_TESTS(); + + rtc::CleanupSSL(); + + // clean up logging so we don't appear to leak memory. + rtc::LogMessage::ConfigureLogging("", ""); + +#if defined(WEBRTC_WIN) + // Unhook crt function so that we don't ever log after statics have been + // uninitialized. + if (!FLAG_default_error_handlers) + _CrtSetReportHook2(_CRT_RPTHOOK_REMOVE, TestCrtReportHandler); +#endif + + return res; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/unixfilesystem.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/unixfilesystem.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/unixfilesystem.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/unixfilesystem.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,572 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/unixfilesystem.h" + +#include +#include +#include +#include +#include + +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +#include +#include +#include +#include "webrtc/base/macutils.h" +#endif // WEBRTC_MAC && !defined(WEBRTC_IOS) + +#if defined(WEBRTC_POSIX) && !defined(WEBRTC_MAC) || defined(WEBRTC_IOS) +#include +#if defined(WEBRTC_ANDROID) +#include +#elif !defined(__native_client__) +#include +#endif // !defined(__native_client__) +#include +#include +#include +#endif // WEBRTC_POSIX && !WEBRTC_MAC || WEBRTC_IOS + +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) +#include +#include +#endif + +#if defined(__native_client__) && !defined(__GLIBC__) +#include +#endif + +#include "webrtc/base/fileutils.h" +#include "webrtc/base/pathutils.h" +#include "webrtc/base/stream.h" +#include "webrtc/base/stringutils.h" + +#if defined(WEBRTC_IOS) +// Defined in iosfilesystem.mm. No header file to discourage use +// elsewhere; other places should use GetApp{Data,Temp}Folder() in +// this file. Don't copy/paste. I mean it. +char* IOSDataDirectory(); +char* IOSTempDirectory(); +void IOSAppName(rtc::Pathname* path); +#endif + +namespace rtc { + +#if !defined(WEBRTC_ANDROID) && !defined(WEBRTC_IOS) +char* UnixFilesystem::app_temp_path_ = NULL; +#else +char* UnixFilesystem::provided_app_data_folder_ = NULL; +char* UnixFilesystem::provided_app_temp_folder_ = NULL; + +void UnixFilesystem::SetAppDataFolder(const std::string& folder) { + delete [] provided_app_data_folder_; + provided_app_data_folder_ = CopyString(folder); +} + +void UnixFilesystem::SetAppTempFolder(const std::string& folder) { + delete [] provided_app_temp_folder_; + provided_app_temp_folder_ = CopyString(folder); +} +#endif + +UnixFilesystem::UnixFilesystem() { +#if defined(WEBRTC_IOS) + if (!provided_app_data_folder_) + provided_app_data_folder_ = IOSDataDirectory(); + if (!provided_app_temp_folder_) + provided_app_temp_folder_ = IOSTempDirectory(); +#endif +} + +UnixFilesystem::~UnixFilesystem() {} + +bool UnixFilesystem::CreateFolder(const Pathname &path, mode_t mode) { + std::string pathname(path.pathname()); + int len = pathname.length(); + if ((len == 0) || (pathname[len - 1] != '/')) + return false; + + struct stat st; + int res = ::stat(pathname.c_str(), &st); + if (res == 0) { + // Something exists at this location, check if it is a directory + return S_ISDIR(st.st_mode) != 0; + } else if (errno != ENOENT) { + // Unexpected error + return false; + } + + // Directory doesn't exist, look up one directory level + do { + --len; + } while ((len > 0) && (pathname[len - 1] != '/')); + + if (!CreateFolder(Pathname(pathname.substr(0, len)), mode)) { + return false; + } + + LOG(LS_INFO) << "Creating folder: " << pathname; + return (0 == ::mkdir(pathname.c_str(), mode)); +} + +bool UnixFilesystem::CreateFolder(const Pathname &path) { + return CreateFolder(path, 0755); +} + +FileStream *UnixFilesystem::OpenFile(const Pathname &filename, + const std::string &mode) { + FileStream *fs = new FileStream(); + if (fs && !fs->Open(filename.pathname().c_str(), mode.c_str(), NULL)) { + delete fs; + fs = NULL; + } + return fs; +} + +bool UnixFilesystem::CreatePrivateFile(const Pathname &filename) { + int fd = open(filename.pathname().c_str(), + O_RDWR | O_CREAT | O_EXCL, + S_IRUSR | S_IWUSR); + if (fd < 0) { + LOG_ERR(LS_ERROR) << "open() failed."; + return false; + } + // Don't need to keep the file descriptor. + if (close(fd) < 0) { + LOG_ERR(LS_ERROR) << "close() failed."; + // Continue. + } + return true; +} + +bool UnixFilesystem::DeleteFile(const Pathname &filename) { + LOG(LS_INFO) << "Deleting file:" << filename.pathname(); + + if (!IsFile(filename)) { + ASSERT(IsFile(filename)); + return false; + } + return ::unlink(filename.pathname().c_str()) == 0; +} + +bool UnixFilesystem::DeleteEmptyFolder(const Pathname &folder) { + LOG(LS_INFO) << "Deleting folder" << folder.pathname(); + + if (!IsFolder(folder)) { + ASSERT(IsFolder(folder)); + return false; + } + std::string no_slash(folder.pathname(), 0, folder.pathname().length()-1); + return ::rmdir(no_slash.c_str()) == 0; +} + +bool UnixFilesystem::GetTemporaryFolder(Pathname &pathname, bool create, + const std::string *append) { +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + FSRef fr; + if (0 != FSFindFolder(kOnAppropriateDisk, kTemporaryFolderType, + kCreateFolder, &fr)) + return false; + unsigned char buffer[NAME_MAX+1]; + if (0 != FSRefMakePath(&fr, buffer, ARRAY_SIZE(buffer))) + return false; + pathname.SetPathname(reinterpret_cast(buffer), ""); +#elif defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) + ASSERT(provided_app_temp_folder_ != NULL); + pathname.SetPathname(provided_app_temp_folder_, ""); +#else // !WEBRTC_MAC || WEBRTC_IOS && !WEBRTC_ANDROID + if (const char* tmpdir = getenv("TMPDIR")) { + pathname.SetPathname(tmpdir, ""); + } else if (const char* tmp = getenv("TMP")) { + pathname.SetPathname(tmp, ""); + } else { +#ifdef P_tmpdir + pathname.SetPathname(P_tmpdir, ""); +#else // !P_tmpdir + pathname.SetPathname("/tmp/", ""); +#endif // !P_tmpdir + } +#endif // !WEBRTC_MAC || WEBRTC_IOS && !WEBRTC_ANDROID + if (append) { + ASSERT(!append->empty()); + pathname.AppendFolder(*append); + } + return !create || CreateFolder(pathname); +} + +std::string UnixFilesystem::TempFilename(const Pathname &dir, + const std::string &prefix) { + int len = dir.pathname().size() + prefix.size() + 2 + 6; + char *tempname = new char[len]; + + snprintf(tempname, len, "%s/%sXXXXXX", dir.pathname().c_str(), + prefix.c_str()); + int fd = ::mkstemp(tempname); + if (fd != -1) + ::close(fd); + std::string ret(tempname); + delete[] tempname; + + return ret; +} + +bool UnixFilesystem::MoveFile(const Pathname &old_path, + const Pathname &new_path) { + if (!IsFile(old_path)) { + ASSERT(IsFile(old_path)); + return false; + } + LOG(LS_VERBOSE) << "Moving " << old_path.pathname() + << " to " << new_path.pathname(); + if (rename(old_path.pathname().c_str(), new_path.pathname().c_str()) != 0) { + if (errno != EXDEV) + return false; + if (!CopyFile(old_path, new_path)) + return false; + if (!DeleteFile(old_path)) + return false; + } + return true; +} + +bool UnixFilesystem::MoveFolder(const Pathname &old_path, + const Pathname &new_path) { + if (!IsFolder(old_path)) { + ASSERT(IsFolder(old_path)); + return false; + } + LOG(LS_VERBOSE) << "Moving " << old_path.pathname() + << " to " << new_path.pathname(); + if (rename(old_path.pathname().c_str(), new_path.pathname().c_str()) != 0) { + if (errno != EXDEV) + return false; + if (!CopyFolder(old_path, new_path)) + return false; + if (!DeleteFolderAndContents(old_path)) + return false; + } + return true; +} + +bool UnixFilesystem::IsFolder(const Pathname &path) { + struct stat st; + if (stat(path.pathname().c_str(), &st) < 0) + return false; + return S_ISDIR(st.st_mode); +} + +bool UnixFilesystem::CopyFile(const Pathname &old_path, + const Pathname &new_path) { + LOG(LS_VERBOSE) << "Copying " << old_path.pathname() + << " to " << new_path.pathname(); + char buf[256]; + size_t len; + + StreamInterface *source = OpenFile(old_path, "rb"); + if (!source) + return false; + + StreamInterface *dest = OpenFile(new_path, "wb"); + if (!dest) { + delete source; + return false; + } + + while (source->Read(buf, sizeof(buf), &len, NULL) == SR_SUCCESS) + dest->Write(buf, len, NULL, NULL); + + delete source; + delete dest; + return true; +} + +bool UnixFilesystem::IsTemporaryPath(const Pathname& pathname) { +#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) + ASSERT(provided_app_temp_folder_ != NULL); +#endif + + const char* const kTempPrefixes[] = { +#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) + provided_app_temp_folder_, +#else + "/tmp/", "/var/tmp/", +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + "/private/tmp/", "/private/var/tmp/", "/private/var/folders/", +#endif // WEBRTC_MAC && !defined(WEBRTC_IOS) +#endif // WEBRTC_ANDROID || WEBRTC_IOS + }; + for (size_t i = 0; i < ARRAY_SIZE(kTempPrefixes); ++i) { + if (0 == strncmp(pathname.pathname().c_str(), kTempPrefixes[i], + strlen(kTempPrefixes[i]))) + return true; + } + return false; +} + +bool UnixFilesystem::IsFile(const Pathname& pathname) { + struct stat st; + int res = ::stat(pathname.pathname().c_str(), &st); + // Treat symlinks, named pipes, etc. all as files. + return res == 0 && !S_ISDIR(st.st_mode); +} + +bool UnixFilesystem::IsAbsent(const Pathname& pathname) { + struct stat st; + int res = ::stat(pathname.pathname().c_str(), &st); + // Note: we specifically maintain ENOTDIR as an error, because that implies + // that you could not call CreateFolder(pathname). + return res != 0 && ENOENT == errno; +} + +bool UnixFilesystem::GetFileSize(const Pathname& pathname, size_t *size) { + struct stat st; + if (::stat(pathname.pathname().c_str(), &st) != 0) + return false; + *size = st.st_size; + return true; +} + +bool UnixFilesystem::GetFileTime(const Pathname& path, FileTimeType which, + time_t* time) { + struct stat st; + if (::stat(path.pathname().c_str(), &st) != 0) + return false; + switch (which) { + case FTT_CREATED: + *time = st.st_ctime; + break; + case FTT_MODIFIED: + *time = st.st_mtime; + break; + case FTT_ACCESSED: + *time = st.st_atime; + break; + default: + return false; + } + return true; +} + +bool UnixFilesystem::GetAppPathname(Pathname* path) { +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + ProcessSerialNumber psn = { 0, kCurrentProcess }; + CFDictionaryRef procinfo = ProcessInformationCopyDictionary(&psn, + kProcessDictionaryIncludeAllInformationMask); + if (NULL == procinfo) + return false; + CFStringRef cfpath = (CFStringRef) CFDictionaryGetValue(procinfo, + kIOBundleExecutableKey); + std::string path8; + bool success = ToUtf8(cfpath, &path8); + CFRelease(procinfo); + if (success) + path->SetPathname(path8); + return success; +#elif defined(__native_client__) + return false; +#elif IOS + IOSAppName(path); + return true; +#else // WEBRTC_MAC && !defined(WEBRTC_IOS) + char buffer[PATH_MAX + 2]; + ssize_t len = readlink("/proc/self/exe", buffer, ARRAY_SIZE(buffer) - 1); + if ((len <= 0) || (len == PATH_MAX + 1)) + return false; + buffer[len] = '\0'; + path->SetPathname(buffer); + return true; +#endif // WEBRTC_MAC && !defined(WEBRTC_IOS) +} + +bool UnixFilesystem::GetAppDataFolder(Pathname* path, bool per_user) { + ASSERT(!organization_name_.empty()); + ASSERT(!application_name_.empty()); + + // First get the base directory for app data. +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + if (per_user) { + // Use ~/Library/Application Support/// + FSRef fr; + if (0 != FSFindFolder(kUserDomain, kApplicationSupportFolderType, + kCreateFolder, &fr)) + return false; + unsigned char buffer[NAME_MAX+1]; + if (0 != FSRefMakePath(&fr, buffer, ARRAY_SIZE(buffer))) + return false; + path->SetPathname(reinterpret_cast(buffer), ""); + } else { + // TODO + return false; + } +#elif defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) // && !WEBRTC_MAC || WEBRTC_IOS + ASSERT(provided_app_data_folder_ != NULL); + path->SetPathname(provided_app_data_folder_, ""); +#elif defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) // && !WEBRTC_MAC && !WEBRTC_IOS && !WEBRTC_ANDROID + if (per_user) { + // We follow the recommendations in + // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + // It specifies separate directories for data and config files, but + // GetAppDataFolder() does not distinguish. We just return the config dir + // path. + const char* xdg_config_home = getenv("XDG_CONFIG_HOME"); + if (xdg_config_home) { + path->SetPathname(xdg_config_home, ""); + } else { + // XDG says to default to $HOME/.config. We also support falling back to + // other synonyms for HOME if for some reason it is not defined. + const char* homedir; + if (const char* home = getenv("HOME")) { + homedir = home; + } else if (const char* dotdir = getenv("DOTDIR")) { + homedir = dotdir; + } else if (passwd* pw = getpwuid(geteuid())) { + homedir = pw->pw_dir; + } else { + return false; + } + path->SetPathname(homedir, ""); + path->AppendFolder(".config"); + } + } else { + // XDG does not define a standard directory for writable global data. Let's + // just use this. + path->SetPathname("/var/cache/", ""); + } +#endif // !WEBRTC_MAC && !WEBRTC_LINUX + + // Now add on a sub-path for our app. +#if defined(WEBRTC_MAC) || defined(WEBRTC_ANDROID) + path->AppendFolder(organization_name_); + path->AppendFolder(application_name_); +#elif defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) + // XDG says to use a single directory level, so we concatenate the org and app + // name with a hyphen. We also do the Linuxy thing and convert to all + // lowercase with no spaces. + std::string subdir(organization_name_); + subdir.append("-"); + subdir.append(application_name_); + replace_substrs(" ", 1, "", 0, &subdir); + std::transform(subdir.begin(), subdir.end(), subdir.begin(), ::tolower); + path->AppendFolder(subdir); +#endif + if (!CreateFolder(*path, 0700)) { + return false; + } +#if !defined(__native_client__) + // If the folder already exists, it may have the wrong mode or be owned by + // someone else, both of which are security problems. Setting the mode + // avoids both issues since it will fail if the path is not owned by us. + if (0 != ::chmod(path->pathname().c_str(), 0700)) { + LOG_ERR(LS_ERROR) << "Can't set mode on " << path; + return false; + } +#endif + return true; +} + +bool UnixFilesystem::GetAppTempFolder(Pathname* path) { +#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) + ASSERT(provided_app_temp_folder_ != NULL); + path->SetPathname(provided_app_temp_folder_); + return true; +#else + ASSERT(!application_name_.empty()); + // TODO: Consider whether we are worried about thread safety. + if (app_temp_path_ != NULL && strlen(app_temp_path_) > 0) { + path->SetPathname(app_temp_path_); + return true; + } + + // Create a random directory as /tmp/-- + char buffer[128]; + sprintfn(buffer, ARRAY_SIZE(buffer), "-%d-%d", + static_cast(getpid()), + static_cast(time(0))); + std::string folder(application_name_); + folder.append(buffer); + if (!GetTemporaryFolder(*path, true, &folder)) + return false; + + delete [] app_temp_path_; + app_temp_path_ = CopyString(path->pathname()); + // TODO: atexit(DeleteFolderAndContents(app_temp_path_)); + return true; +#endif +} + +bool UnixFilesystem::GetDiskFreeSpace(const Pathname& path, int64 *freebytes) { +#ifdef __native_client__ + return false; +#else // __native_client__ + ASSERT(NULL != freebytes); + // TODO: Consider making relative paths absolute using cwd. + // TODO: When popping off a symlink, push back on the components of the + // symlink, so we don't jump out of the target disk inadvertently. + Pathname existing_path(path.folder(), ""); + while (!existing_path.folder().empty() && IsAbsent(existing_path)) { + existing_path.SetFolder(existing_path.parent_folder()); + } +#if defined(WEBRTC_ANDROID) + struct statfs vfs; + memset(&vfs, 0, sizeof(vfs)); + if (0 != statfs(existing_path.pathname().c_str(), &vfs)) + return false; +#else + struct statvfs vfs; + memset(&vfs, 0, sizeof(vfs)); + if (0 != statvfs(existing_path.pathname().c_str(), &vfs)) + return false; +#endif // WEBRTC_ANDROID +#if defined(WEBRTC_LINUX) + *freebytes = static_cast(vfs.f_bsize) * vfs.f_bavail; +#elif defined(WEBRTC_MAC) + *freebytes = static_cast(vfs.f_frsize) * vfs.f_bavail; +#endif + + return true; +#endif // !__native_client__ +} + +Pathname UnixFilesystem::GetCurrentDirectory() { + Pathname cwd; + char buffer[PATH_MAX]; + char *path = getcwd(buffer, PATH_MAX); + + if (!path) { + LOG_ERR(LS_ERROR) << "getcwd() failed"; + return cwd; // returns empty pathname + } + cwd.SetFolder(std::string(path)); + + return cwd; +} + +char* UnixFilesystem::CopyString(const std::string& str) { + size_t size = str.length() + 1; + + char* buf = new char[size]; + if (!buf) { + return NULL; + } + + strcpyn(buf, size, str.c_str()); + return buf; +} + +} // namespace rtc + +#if defined(__native_client__) +extern "C" int __attribute__((weak)) +link(const char* oldpath, const char* newpath) { + errno = EACCES; + return -1; +} +#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/unixfilesystem.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/unixfilesystem.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/unixfilesystem.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/unixfilesystem.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,126 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_UNIXFILESYSTEM_H_ +#define WEBRTC_BASE_UNIXFILESYSTEM_H_ + +#include + +#include "webrtc/base/fileutils.h" + +namespace rtc { + +class UnixFilesystem : public FilesystemInterface { + public: + UnixFilesystem(); + virtual ~UnixFilesystem(); + +#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) + // Android does not have a native code API to fetch the app data or temp + // folders. That needs to be passed into this class from Java. Similarly, iOS + // only supports an Objective-C API for fetching the folder locations, so that + // needs to be passed in here from Objective-C. Or at least that used to be + // the case; now the ctor will do the work if necessary and possible. + // TODO(fischman): add an Android version that uses JNI and drop the + // SetApp*Folder() APIs once external users stop using them. + static void SetAppDataFolder(const std::string& folder); + static void SetAppTempFolder(const std::string& folder); +#endif + + // Opens a file. Returns an open StreamInterface if function succeeds. + // Otherwise, returns NULL. + virtual FileStream *OpenFile(const Pathname &filename, + const std::string &mode); + + // Atomically creates an empty file accessible only to the current user if one + // does not already exist at the given path, otherwise fails. + virtual bool CreatePrivateFile(const Pathname &filename); + + // This will attempt to delete the file located at filename. + // It will fail with VERIY if you pass it a non-existant file, or a directory. + virtual bool DeleteFile(const Pathname &filename); + + // This will attempt to delete the folder located at 'folder' + // It ASSERTs and returns false if you pass it a non-existant folder or a + // plain file. + virtual bool DeleteEmptyFolder(const Pathname &folder); + + // Creates a directory. This will call itself recursively to create /foo/bar + // even if /foo does not exist. All created directories are created with the + // given mode. + // Returns TRUE if function succeeds + virtual bool CreateFolder(const Pathname &pathname, mode_t mode); + + // As above, with mode = 0755. + virtual bool CreateFolder(const Pathname &pathname); + + // This moves a file from old_path to new_path, where "file" can be a plain + // file or directory, which will be moved recursively. + // Returns true if function succeeds. + virtual bool MoveFile(const Pathname &old_path, const Pathname &new_path); + virtual bool MoveFolder(const Pathname &old_path, const Pathname &new_path); + + // This copies a file from old_path to _new_path where "file" can be a plain + // file or directory, which will be copied recursively. + // Returns true if function succeeds + virtual bool CopyFile(const Pathname &old_path, const Pathname &new_path); + + // Returns true if a pathname is a directory + virtual bool IsFolder(const Pathname& pathname); + + // Returns true if pathname represents a temporary location on the system. + virtual bool IsTemporaryPath(const Pathname& pathname); + + // Returns true of pathname represents an existing file + virtual bool IsFile(const Pathname& pathname); + + // Returns true if pathname refers to no filesystem object, every parent + // directory either exists, or is also absent. + virtual bool IsAbsent(const Pathname& pathname); + + virtual std::string TempFilename(const Pathname &dir, + const std::string &prefix); + + // A folder appropriate for storing temporary files (Contents are + // automatically deleted when the program exists) + virtual bool GetTemporaryFolder(Pathname &path, bool create, + const std::string *append); + + virtual bool GetFileSize(const Pathname& path, size_t* size); + virtual bool GetFileTime(const Pathname& path, FileTimeType which, + time_t* time); + + // Returns the path to the running application. + virtual bool GetAppPathname(Pathname* path); + + virtual bool GetAppDataFolder(Pathname* path, bool per_user); + + // Get a temporary folder that is unique to the current user and application. + virtual bool GetAppTempFolder(Pathname* path); + + virtual bool GetDiskFreeSpace(const Pathname& path, int64 *freebytes); + + // Returns the absolute path of the current directory. + virtual Pathname GetCurrentDirectory(); + + private: +#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) + static char* provided_app_data_folder_; + static char* provided_app_temp_folder_; +#else + static char* app_temp_path_; +#endif + + static char* CopyString(const std::string& str); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_UNIXFILESYSTEM_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/urlencode.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/urlencode.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/urlencode.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/urlencode.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,181 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/urlencode.h" + +#include "webrtc/base/common.h" +#include "webrtc/base/stringutils.h" + +static int HexPairValue(const char * code) { + int value = 0; + for (const char * pch = code; pch < code + 2; ++pch) { + value <<= 4; + int digit = *pch; + if (digit >= '0' && digit <= '9') { + value += digit - '0'; + } + else if (digit >= 'A' && digit <= 'F') { + value += digit - 'A' + 10; + } + else if (digit >= 'a' && digit <= 'f') { + value += digit - 'a' + 10; + } + else { + return -1; + } + } + return value; +} + +static int InternalUrlDecode(const char *source, char *dest, + bool encode_space_as_plus) { + char * start = dest; + + while (*source) { + switch (*source) { + case '+': + if (encode_space_as_plus) { + *(dest++) = ' '; + } else { + *dest++ = *source; + } + break; + case '%': + if (source[1] && source[2]) { + int value = HexPairValue(source + 1); + if (value >= 0) { + *(dest++) = static_cast(value); + source += 2; + } + else { + *dest++ = '?'; + } + } + else { + *dest++ = '?'; + } + break; + default: + *dest++ = *source; + } + source++; + } + + *dest = 0; + return static_cast(dest - start); +} + +static bool IsValidUrlChar(char ch, bool unsafe_only) { + if (unsafe_only) { + return !(ch <= ' ' || strchr("\\\"^&`<>[]{}", ch)); + } else { + return isalnum(ch) || strchr("-_.!~*'()", ch); + } +} + +namespace rtc { + +int UrlDecode(const char *source, char *dest) { + return InternalUrlDecode(source, dest, true); +} + +int UrlDecodeWithoutEncodingSpaceAsPlus(const char *source, char *dest) { + return InternalUrlDecode(source, dest, false); +} + +int InternalUrlEncode(const char *source, char *dest, unsigned int max, + bool encode_space_as_plus, bool unsafe_only) { + static const char *digits = "0123456789ABCDEF"; + if (max == 0) { + return 0; + } + + char *start = dest; + while (static_cast(dest - start) < max && *source) { + unsigned char ch = static_cast(*source); + if (*source == ' ' && encode_space_as_plus && !unsafe_only) { + *dest++ = '+'; + } else if (IsValidUrlChar(ch, unsafe_only)) { + *dest++ = *source; + } else { + if (static_cast(dest - start) + 4 > max) { + break; + } + *dest++ = '%'; + *dest++ = digits[(ch >> 4) & 0x0F]; + *dest++ = digits[ ch & 0x0F]; + } + source++; + } + ASSERT(static_cast(dest - start) < max); + *dest = 0; + + return static_cast(dest - start); +} + +int UrlEncode(const char *source, char *dest, unsigned max) { + return InternalUrlEncode(source, dest, max, true, false); +} + +int UrlEncodeWithoutEncodingSpaceAsPlus(const char *source, char *dest, + unsigned max) { + return InternalUrlEncode(source, dest, max, false, false); +} + +int UrlEncodeOnlyUnsafeChars(const char *source, char *dest, unsigned max) { + return InternalUrlEncode(source, dest, max, false, true); +} + +std::string +InternalUrlDecodeString(const std::string & encoded, + bool encode_space_as_plus) { + size_t needed_length = encoded.length() + 1; + char* buf = STACK_ARRAY(char, needed_length); + InternalUrlDecode(encoded.c_str(), buf, encode_space_as_plus); + return buf; +} + +std::string +UrlDecodeString(const std::string & encoded) { + return InternalUrlDecodeString(encoded, true); +} + +std::string +UrlDecodeStringWithoutEncodingSpaceAsPlus(const std::string & encoded) { + return InternalUrlDecodeString(encoded, false); +} + +std::string +InternalUrlEncodeString(const std::string & decoded, + bool encode_space_as_plus, + bool unsafe_only) { + int needed_length = static_cast(decoded.length()) * 3 + 1; + char* buf = STACK_ARRAY(char, needed_length); + InternalUrlEncode(decoded.c_str(), buf, needed_length, + encode_space_as_plus, unsafe_only); + return buf; +} + +std::string +UrlEncodeString(const std::string & decoded) { + return InternalUrlEncodeString(decoded, true, false); +} + +std::string +UrlEncodeStringWithoutEncodingSpaceAsPlus(const std::string & decoded) { + return InternalUrlEncodeString(decoded, false, false); +} + +std::string +UrlEncodeStringForOnlyUnsafeChars(const std::string & decoded) { + return InternalUrlEncodeString(decoded, false, true); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/urlencode.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/urlencode.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/urlencode.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/urlencode.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef _URLENCODE_H_ +#define _URLENCODE_H_ + +#include + +namespace rtc { + +// Decode all encoded characters. Also decode + as space. +int UrlDecode(const char *source, char *dest); + +// Decode all encoded characters. +int UrlDecodeWithoutEncodingSpaceAsPlus(const char *source, char *dest); + +// Encode all characters except alphas, numbers, and -_.!~*'() +// Also encode space as +. +int UrlEncode(const char *source, char *dest, unsigned max); + +// Encode all characters except alphas, numbers, and -_.!~*'() +int UrlEncodeWithoutEncodingSpaceAsPlus(const char *source, char *dest, + unsigned max); + +// Encode only unsafe chars, including \ "^&`<>[]{} +// Also encode space as %20, instead of + +int UrlEncodeOnlyUnsafeChars(const char *source, char *dest, unsigned max); + +std::string UrlDecodeString(const std::string & encoded); +std::string UrlDecodeStringWithoutEncodingSpaceAsPlus( + const std::string & encoded); +std::string UrlEncodeString(const std::string & decoded); +std::string UrlEncodeStringWithoutEncodingSpaceAsPlus( + const std::string & decoded); +std::string UrlEncodeStringForOnlyUnsafeChars(const std::string & decoded); + +#endif + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/urlencode_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/urlencode_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/urlencode_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/urlencode_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,83 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/common.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/urlencode.h" + +using rtc::UrlEncode; + +TEST(Urlencode, SourceTooLong) { + char source[] = "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" + "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"; + char dest[1]; + ASSERT_EQ(0, UrlEncode(source, dest, ARRAY_SIZE(dest))); + ASSERT_EQ('\0', dest[0]); + + dest[0] = 'a'; + ASSERT_EQ(0, UrlEncode(source, dest, 0)); + ASSERT_EQ('a', dest[0]); +} + +TEST(Urlencode, OneCharacterConversion) { + char source[] = "^"; + char dest[4]; + ASSERT_EQ(3, UrlEncode(source, dest, ARRAY_SIZE(dest))); + ASSERT_STREQ("%5E", dest); +} + +TEST(Urlencode, ShortDestinationNoEncoding) { + // In this case we have a destination that would not be + // big enough to hold an encoding but is big enough to + // hold the text given. + char source[] = "aa"; + char dest[3]; + ASSERT_EQ(2, UrlEncode(source, dest, ARRAY_SIZE(dest))); + ASSERT_STREQ("aa", dest); +} + +TEST(Urlencode, ShortDestinationEncoding) { + // In this case we have a destination that is not + // big enough to hold the encoding. + char source[] = "&"; + char dest[3]; + ASSERT_EQ(0, UrlEncode(source, dest, ARRAY_SIZE(dest))); + ASSERT_EQ('\0', dest[0]); +} + +TEST(Urlencode, Encoding1) { + char source[] = "A^ "; + char dest[8]; + ASSERT_EQ(5, UrlEncode(source, dest, ARRAY_SIZE(dest))); + ASSERT_STREQ("A%5E+", dest); +} + +TEST(Urlencode, Encoding2) { + char source[] = "A^ "; + char dest[8]; + ASSERT_EQ(7, rtc::UrlEncodeWithoutEncodingSpaceAsPlus(source, dest, + ARRAY_SIZE(dest))); + ASSERT_STREQ("A%5E%20", dest); +} + +TEST(Urldecode, Decoding1) { + char source[] = "A%5E+"; + char dest[8]; + ASSERT_EQ(3, rtc::UrlDecode(source, dest)); + ASSERT_STREQ("A^ ", dest); +} + +TEST(Urldecode, Decoding2) { + char source[] = "A%5E+"; + char dest[8]; + ASSERT_EQ(3, rtc::UrlDecodeWithoutEncodingSpaceAsPlus(source, dest)); + ASSERT_STREQ("A^+", dest); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/versionparsing.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/versionparsing.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/versionparsing.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/versionparsing.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,57 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/versionparsing.h" + +#include + +namespace rtc { + +bool ParseVersionString(const std::string& version_str, + int num_expected_segments, + int version[]) { + size_t pos = 0; + for (int i = 0;;) { + size_t dot_pos = version_str.find('.', pos); + size_t n; + if (dot_pos == std::string::npos) { + // npos here is a special value meaning "to the end of the string" + n = std::string::npos; + } else { + n = dot_pos - pos; + } + + version[i] = atoi(version_str.substr(pos, n).c_str()); + + if (++i >= num_expected_segments) break; + + if (dot_pos == std::string::npos) { + // Previous segment was not terminated by a dot, but there's supposed to + // be more segments, so that's an error. + return false; + } + pos = dot_pos + 1; + } + return true; +} + +int CompareVersions(const int version1[], + const int version2[], + int num_segments) { + for (int i = 0; i < num_segments; ++i) { + int diff = version1[i] - version2[i]; + if (diff != 0) { + return diff; + } + } + return 0; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/versionparsing.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/versionparsing.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/versionparsing.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/versionparsing.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,35 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_VERSIONPARSING_H_ +#define WEBRTC_BASE_VERSIONPARSING_H_ + +#include + +namespace rtc { + +// Parses a version string into an array. "num_expected_segments" must be the +// number of numerical segments that the version is expected to have (e.g., +// "1.1.2.0" has 4). "version" must be an array of that length to hold the +// parsed numbers. +// Returns "true" iff successful. +bool ParseVersionString(const std::string& version_str, + int num_expected_segments, + int version[]); + +// Computes the lexicographical order of two versions. The return value +// indicates the order in the standard way (e.g., see strcmp()). +int CompareVersions(const int version1[], + const int version2[], + int num_segments); + +} // namespace rtc + +#endif // WEBRTC_BASE_VERSIONPARSING_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/versionparsing_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/versionparsing_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/versionparsing_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/versionparsing_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,74 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/versionparsing.h" + +#include "webrtc/base/gunit.h" + +namespace rtc { + +static const int kExampleSegments = 4; + +typedef int ExampleVersion[kExampleSegments]; + +TEST(VersionParsing, TestGoodParse) { + ExampleVersion ver; + std::string str1("1.1.2.0"); + static const ExampleVersion expect1 = {1, 1, 2, 0}; + EXPECT_TRUE(ParseVersionString(str1, kExampleSegments, ver)); + EXPECT_EQ(0, CompareVersions(ver, expect1, kExampleSegments)); + std::string str2("2.0.0.1"); + static const ExampleVersion expect2 = {2, 0, 0, 1}; + EXPECT_TRUE(ParseVersionString(str2, kExampleSegments, ver)); + EXPECT_EQ(0, CompareVersions(ver, expect2, kExampleSegments)); +} + +TEST(VersionParsing, TestBadParse) { + ExampleVersion ver; + std::string str1("1.1.2"); + EXPECT_FALSE(ParseVersionString(str1, kExampleSegments, ver)); + std::string str2(""); + EXPECT_FALSE(ParseVersionString(str2, kExampleSegments, ver)); + std::string str3("garbarge"); + EXPECT_FALSE(ParseVersionString(str3, kExampleSegments, ver)); +} + +TEST(VersionParsing, TestCompare) { + static const ExampleVersion ver1 = {1, 0, 21, 0}; + static const ExampleVersion ver2 = {1, 1, 2, 0}; + static const ExampleVersion ver3 = {1, 1, 3, 0}; + static const ExampleVersion ver4 = {1, 1, 3, 9861}; + + // Test that every combination of comparisons has the expected outcome. + EXPECT_EQ(0, CompareVersions(ver1, ver1, kExampleSegments)); + EXPECT_EQ(0, CompareVersions(ver2, ver2, kExampleSegments)); + EXPECT_EQ(0, CompareVersions(ver3, ver3, kExampleSegments)); + EXPECT_EQ(0, CompareVersions(ver4, ver4, kExampleSegments)); + + EXPECT_GT(0, CompareVersions(ver1, ver2, kExampleSegments)); + EXPECT_LT(0, CompareVersions(ver2, ver1, kExampleSegments)); + + EXPECT_GT(0, CompareVersions(ver1, ver3, kExampleSegments)); + EXPECT_LT(0, CompareVersions(ver3, ver1, kExampleSegments)); + + EXPECT_GT(0, CompareVersions(ver1, ver4, kExampleSegments)); + EXPECT_LT(0, CompareVersions(ver4, ver1, kExampleSegments)); + + EXPECT_GT(0, CompareVersions(ver2, ver3, kExampleSegments)); + EXPECT_LT(0, CompareVersions(ver3, ver2, kExampleSegments)); + + EXPECT_GT(0, CompareVersions(ver2, ver4, kExampleSegments)); + EXPECT_LT(0, CompareVersions(ver4, ver2, kExampleSegments)); + + EXPECT_GT(0, CompareVersions(ver3, ver4, kExampleSegments)); + EXPECT_LT(0, CompareVersions(ver4, ver3, kExampleSegments)); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/virtualsocketserver.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/virtualsocketserver.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/virtualsocketserver.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/virtualsocketserver.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,1105 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/virtualsocketserver.h" + +#include +#include + +#include +#include +#include + +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/physicalsocketserver.h" +#include "webrtc/base/socketaddresspair.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/timeutils.h" + +namespace rtc { +#if defined(WEBRTC_WIN) +const in_addr kInitialNextIPv4 = { {0x01, 0, 0, 0} }; +#else +// This value is entirely arbitrary, hence the lack of concern about endianness. +const in_addr kInitialNextIPv4 = { 0x01000000 }; +#endif +// Starts at ::2 so as to not cause confusion with ::1. +const in6_addr kInitialNextIPv6 = { { { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 + } } }; + +const uint16 kFirstEphemeralPort = 49152; +const uint16 kLastEphemeralPort = 65535; +const uint16 kEphemeralPortCount = kLastEphemeralPort - kFirstEphemeralPort + 1; +const uint32 kDefaultNetworkCapacity = 64 * 1024; +const uint32 kDefaultTcpBufferSize = 32 * 1024; + +const uint32 UDP_HEADER_SIZE = 28; // IP + UDP headers +const uint32 TCP_HEADER_SIZE = 40; // IP + TCP headers +const uint32 TCP_MSS = 1400; // Maximum segment size + +// Note: The current algorithm doesn't work for sample sizes smaller than this. +const int NUM_SAMPLES = 1000; + +enum { + MSG_ID_PACKET, + MSG_ID_CONNECT, + MSG_ID_DISCONNECT, +}; + +// Packets are passed between sockets as messages. We copy the data just like +// the kernel does. +class Packet : public MessageData { + public: + Packet(const char* data, size_t size, const SocketAddress& from) + : size_(size), consumed_(0), from_(from) { + ASSERT(NULL != data); + data_ = new char[size_]; + memcpy(data_, data, size_); + } + + virtual ~Packet() { + delete[] data_; + } + + const char* data() const { return data_ + consumed_; } + size_t size() const { return size_ - consumed_; } + const SocketAddress& from() const { return from_; } + + // Remove the first size bytes from the data. + void Consume(size_t size) { + ASSERT(size + consumed_ < size_); + consumed_ += size; + } + + private: + char* data_; + size_t size_, consumed_; + SocketAddress from_; +}; + +struct MessageAddress : public MessageData { + explicit MessageAddress(const SocketAddress& a) : addr(a) { } + SocketAddress addr; +}; + +// Implements the socket interface using the virtual network. Packets are +// passed as messages using the message queue of the socket server. +class VirtualSocket : public AsyncSocket, public MessageHandler { + public: + VirtualSocket(VirtualSocketServer* server, int family, int type, bool async) + : server_(server), family_(family), type_(type), async_(async), + state_(CS_CLOSED), error_(0), listen_queue_(NULL), + write_enabled_(false), + network_size_(0), recv_buffer_size_(0), bound_(false), was_any_(false) { + ASSERT((type_ == SOCK_DGRAM) || (type_ == SOCK_STREAM)); + ASSERT(async_ || (type_ != SOCK_STREAM)); // We only support async streams + } + + virtual ~VirtualSocket() { + Close(); + + for (RecvBuffer::iterator it = recv_buffer_.begin(); + it != recv_buffer_.end(); ++it) { + delete *it; + } + } + + virtual SocketAddress GetLocalAddress() const { + return local_addr_; + } + + virtual SocketAddress GetRemoteAddress() const { + return remote_addr_; + } + + // Used by server sockets to set the local address without binding. + void SetLocalAddress(const SocketAddress& addr) { + local_addr_ = addr; + } + + virtual int Bind(const SocketAddress& addr) { + if (!local_addr_.IsNil()) { + error_ = EINVAL; + return -1; + } + local_addr_ = addr; + int result = server_->Bind(this, &local_addr_); + if (result != 0) { + local_addr_.Clear(); + error_ = EADDRINUSE; + } else { + bound_ = true; + was_any_ = addr.IsAnyIP(); + } + return result; + } + + virtual int Connect(const SocketAddress& addr) { + return InitiateConnect(addr, true); + } + + virtual int Close() { + if (!local_addr_.IsNil() && bound_) { + // Remove from the binding table. + server_->Unbind(local_addr_, this); + bound_ = false; + } + + if (SOCK_STREAM == type_) { + // Cancel pending sockets + if (listen_queue_) { + while (!listen_queue_->empty()) { + SocketAddress addr = listen_queue_->front(); + + // Disconnect listening socket. + server_->Disconnect(server_->LookupBinding(addr)); + listen_queue_->pop_front(); + } + delete listen_queue_; + listen_queue_ = NULL; + } + // Disconnect stream sockets + if (CS_CONNECTED == state_) { + // Disconnect remote socket, check if it is a child of a server socket. + VirtualSocket* socket = + server_->LookupConnection(local_addr_, remote_addr_); + if (!socket) { + // Not a server socket child, then see if it is bound. + // TODO: If this is indeed a server socket that has no + // children this will cause the server socket to be + // closed. This might lead to unexpected results, how to fix this? + socket = server_->LookupBinding(remote_addr_); + } + server_->Disconnect(socket); + + // Remove mapping for both directions. + server_->RemoveConnection(remote_addr_, local_addr_); + server_->RemoveConnection(local_addr_, remote_addr_); + } + // Cancel potential connects + MessageList msgs; + if (server_->msg_queue_) { + server_->msg_queue_->Clear(this, MSG_ID_CONNECT, &msgs); + } + for (MessageList::iterator it = msgs.begin(); it != msgs.end(); ++it) { + ASSERT(NULL != it->pdata); + MessageAddress* data = static_cast(it->pdata); + + // Lookup remote side. + VirtualSocket* socket = server_->LookupConnection(local_addr_, + data->addr); + if (socket) { + // Server socket, remote side is a socket retreived by + // accept. Accepted sockets are not bound so we will not + // find it by looking in the bindings table. + server_->Disconnect(socket); + server_->RemoveConnection(local_addr_, data->addr); + } else { + server_->Disconnect(server_->LookupBinding(data->addr)); + } + delete data; + } + // Clear incoming packets and disconnect messages + if (server_->msg_queue_) { + server_->msg_queue_->Clear(this); + } + } + + state_ = CS_CLOSED; + local_addr_.Clear(); + remote_addr_.Clear(); + return 0; + } + + virtual int Send(const void *pv, size_t cb) { + if (CS_CONNECTED != state_) { + error_ = ENOTCONN; + return -1; + } + if (SOCK_DGRAM == type_) { + return SendUdp(pv, cb, remote_addr_); + } else { + return SendTcp(pv, cb); + } + } + + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr) { + if (SOCK_DGRAM == type_) { + return SendUdp(pv, cb, addr); + } else { + if (CS_CONNECTED != state_) { + error_ = ENOTCONN; + return -1; + } + return SendTcp(pv, cb); + } + } + + virtual int Recv(void *pv, size_t cb) { + SocketAddress addr; + return RecvFrom(pv, cb, &addr); + } + + virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) { + // If we don't have a packet, then either error or wait for one to arrive. + if (recv_buffer_.empty()) { + if (async_) { + error_ = EAGAIN; + return -1; + } + while (recv_buffer_.empty()) { + Message msg; + server_->msg_queue_->Get(&msg); + server_->msg_queue_->Dispatch(&msg); + } + } + + // Return the packet at the front of the queue. + Packet* packet = recv_buffer_.front(); + size_t data_read = _min(cb, packet->size()); + memcpy(pv, packet->data(), data_read); + *paddr = packet->from(); + + if (data_read < packet->size()) { + packet->Consume(data_read); + } else { + recv_buffer_.pop_front(); + delete packet; + } + + if (SOCK_STREAM == type_) { + bool was_full = (recv_buffer_size_ == server_->recv_buffer_capacity_); + recv_buffer_size_ -= data_read; + if (was_full) { + VirtualSocket* sender = server_->LookupBinding(remote_addr_); + ASSERT(NULL != sender); + server_->SendTcp(sender); + } + } + + return static_cast(data_read); + } + + virtual int Listen(int backlog) { + ASSERT(SOCK_STREAM == type_); + ASSERT(CS_CLOSED == state_); + if (local_addr_.IsNil()) { + error_ = EINVAL; + return -1; + } + ASSERT(NULL == listen_queue_); + listen_queue_ = new ListenQueue; + state_ = CS_CONNECTING; + return 0; + } + + virtual VirtualSocket* Accept(SocketAddress *paddr) { + if (NULL == listen_queue_) { + error_ = EINVAL; + return NULL; + } + while (!listen_queue_->empty()) { + VirtualSocket* socket = new VirtualSocket(server_, AF_INET, type_, + async_); + + // Set the new local address to the same as this server socket. + socket->SetLocalAddress(local_addr_); + // Sockets made from a socket that 'was Any' need to inherit that. + socket->set_was_any(was_any_); + SocketAddress remote_addr(listen_queue_->front()); + int result = socket->InitiateConnect(remote_addr, false); + listen_queue_->pop_front(); + if (result != 0) { + delete socket; + continue; + } + socket->CompleteConnect(remote_addr, false); + if (paddr) { + *paddr = remote_addr; + } + return socket; + } + error_ = EWOULDBLOCK; + return NULL; + } + + virtual int GetError() const { + return error_; + } + + virtual void SetError(int error) { + error_ = error; + } + + virtual ConnState GetState() const { + return state_; + } + + virtual int GetOption(Option opt, int* value) { + OptionsMap::const_iterator it = options_map_.find(opt); + if (it == options_map_.end()) { + return -1; + } + *value = it->second; + return 0; // 0 is success to emulate getsockopt() + } + + virtual int SetOption(Option opt, int value) { + options_map_[opt] = value; + return 0; // 0 is success to emulate setsockopt() + } + + virtual int EstimateMTU(uint16* mtu) { + if (CS_CONNECTED != state_) + return ENOTCONN; + else + return 65536; + } + + void OnMessage(Message *pmsg) { + if (pmsg->message_id == MSG_ID_PACKET) { + //ASSERT(!local_addr_.IsAny()); + ASSERT(NULL != pmsg->pdata); + Packet* packet = static_cast(pmsg->pdata); + + recv_buffer_.push_back(packet); + + if (async_) { + SignalReadEvent(this); + } + } else if (pmsg->message_id == MSG_ID_CONNECT) { + ASSERT(NULL != pmsg->pdata); + MessageAddress* data = static_cast(pmsg->pdata); + if (listen_queue_ != NULL) { + listen_queue_->push_back(data->addr); + if (async_) { + SignalReadEvent(this); + } + } else if ((SOCK_STREAM == type_) && (CS_CONNECTING == state_)) { + CompleteConnect(data->addr, true); + } else { + LOG(LS_VERBOSE) << "Socket at " << local_addr_ << " is not listening"; + server_->Disconnect(server_->LookupBinding(data->addr)); + } + delete data; + } else if (pmsg->message_id == MSG_ID_DISCONNECT) { + ASSERT(SOCK_STREAM == type_); + if (CS_CLOSED != state_) { + int error = (CS_CONNECTING == state_) ? ECONNREFUSED : 0; + state_ = CS_CLOSED; + remote_addr_.Clear(); + if (async_) { + SignalCloseEvent(this, error); + } + } + } else { + ASSERT(false); + } + } + + bool was_any() { return was_any_; } + void set_was_any(bool was_any) { was_any_ = was_any; } + + private: + struct NetworkEntry { + size_t size; + uint32 done_time; + }; + + typedef std::deque ListenQueue; + typedef std::deque NetworkQueue; + typedef std::vector SendBuffer; + typedef std::list RecvBuffer; + typedef std::map OptionsMap; + + int InitiateConnect(const SocketAddress& addr, bool use_delay) { + if (!remote_addr_.IsNil()) { + error_ = (CS_CONNECTED == state_) ? EISCONN : EINPROGRESS; + return -1; + } + if (local_addr_.IsNil()) { + // If there's no local address set, grab a random one in the correct AF. + int result = 0; + if (addr.ipaddr().family() == AF_INET) { + result = Bind(SocketAddress("0.0.0.0", 0)); + } else if (addr.ipaddr().family() == AF_INET6) { + result = Bind(SocketAddress("::", 0)); + } + if (result != 0) { + return result; + } + } + if (type_ == SOCK_DGRAM) { + remote_addr_ = addr; + state_ = CS_CONNECTED; + } else { + int result = server_->Connect(this, addr, use_delay); + if (result != 0) { + error_ = EHOSTUNREACH; + return -1; + } + state_ = CS_CONNECTING; + } + return 0; + } + + void CompleteConnect(const SocketAddress& addr, bool notify) { + ASSERT(CS_CONNECTING == state_); + remote_addr_ = addr; + state_ = CS_CONNECTED; + server_->AddConnection(remote_addr_, local_addr_, this); + if (async_ && notify) { + SignalConnectEvent(this); + } + } + + int SendUdp(const void* pv, size_t cb, const SocketAddress& addr) { + // If we have not been assigned a local port, then get one. + if (local_addr_.IsNil()) { + local_addr_ = EmptySocketAddressWithFamily(addr.ipaddr().family()); + int result = server_->Bind(this, &local_addr_); + if (result != 0) { + local_addr_.Clear(); + error_ = EADDRINUSE; + return result; + } + } + + // Send the data in a message to the appropriate socket. + return server_->SendUdp(this, static_cast(pv), cb, addr); + } + + int SendTcp(const void* pv, size_t cb) { + size_t capacity = server_->send_buffer_capacity_ - send_buffer_.size(); + if (0 == capacity) { + write_enabled_ = true; + error_ = EWOULDBLOCK; + return -1; + } + size_t consumed = _min(cb, capacity); + const char* cpv = static_cast(pv); + send_buffer_.insert(send_buffer_.end(), cpv, cpv + consumed); + server_->SendTcp(this); + return static_cast(consumed); + } + + VirtualSocketServer* server_; + int family_; + int type_; + bool async_; + ConnState state_; + int error_; + SocketAddress local_addr_; + SocketAddress remote_addr_; + + // Pending sockets which can be Accepted + ListenQueue* listen_queue_; + + // Data which tcp has buffered for sending + SendBuffer send_buffer_; + bool write_enabled_; + + // Critical section to protect the recv_buffer and queue_ + CriticalSection crit_; + + // Network model that enforces bandwidth and capacity constraints + NetworkQueue network_; + size_t network_size_; + + // Data which has been received from the network + RecvBuffer recv_buffer_; + // The amount of data which is in flight or in recv_buffer_ + size_t recv_buffer_size_; + + // Is this socket bound? + bool bound_; + + // When we bind a socket to Any, VSS's Bind gives it another address. For + // dual-stack sockets, we want to distinguish between sockets that were + // explicitly given a particular address and sockets that had one picked + // for them by VSS. + bool was_any_; + + // Store the options that are set + OptionsMap options_map_; + + friend class VirtualSocketServer; +}; + +VirtualSocketServer::VirtualSocketServer(SocketServer* ss) + : server_(ss), server_owned_(false), msg_queue_(NULL), stop_on_idle_(false), + network_delay_(Time()), next_ipv4_(kInitialNextIPv4), + next_ipv6_(kInitialNextIPv6), next_port_(kFirstEphemeralPort), + bindings_(new AddressMap()), connections_(new ConnectionMap()), + bandwidth_(0), network_capacity_(kDefaultNetworkCapacity), + send_buffer_capacity_(kDefaultTcpBufferSize), + recv_buffer_capacity_(kDefaultTcpBufferSize), + delay_mean_(0), delay_stddev_(0), delay_samples_(NUM_SAMPLES), + delay_dist_(NULL), drop_prob_(0.0) { + if (!server_) { + server_ = new PhysicalSocketServer(); + server_owned_ = true; + } + UpdateDelayDistribution(); +} + +VirtualSocketServer::~VirtualSocketServer() { + delete bindings_; + delete connections_; + delete delay_dist_; + if (server_owned_) { + delete server_; + } +} + +IPAddress VirtualSocketServer::GetNextIP(int family) { + if (family == AF_INET) { + IPAddress next_ip(next_ipv4_); + next_ipv4_.s_addr = + HostToNetwork32(NetworkToHost32(next_ipv4_.s_addr) + 1); + return next_ip; + } else if (family == AF_INET6) { + IPAddress next_ip(next_ipv6_); + uint32* as_ints = reinterpret_cast(&next_ipv6_.s6_addr); + as_ints[3] += 1; + return next_ip; + } + return IPAddress(); +} + +uint16 VirtualSocketServer::GetNextPort() { + uint16 port = next_port_; + if (next_port_ < kLastEphemeralPort) { + ++next_port_; + } else { + next_port_ = kFirstEphemeralPort; + } + return port; +} + +Socket* VirtualSocketServer::CreateSocket(int type) { + return CreateSocket(AF_INET, type); +} + +Socket* VirtualSocketServer::CreateSocket(int family, int type) { + return CreateSocketInternal(family, type); +} + +AsyncSocket* VirtualSocketServer::CreateAsyncSocket(int type) { + return CreateAsyncSocket(AF_INET, type); +} + +AsyncSocket* VirtualSocketServer::CreateAsyncSocket(int family, int type) { + return CreateSocketInternal(family, type); +} + +VirtualSocket* VirtualSocketServer::CreateSocketInternal(int family, int type) { + return new VirtualSocket(this, family, type, true); +} + +void VirtualSocketServer::SetMessageQueue(MessageQueue* msg_queue) { + msg_queue_ = msg_queue; + if (msg_queue_) { + msg_queue_->SignalQueueDestroyed.connect(this, + &VirtualSocketServer::OnMessageQueueDestroyed); + } +} + +bool VirtualSocketServer::Wait(int cmsWait, bool process_io) { + ASSERT(msg_queue_ == Thread::Current()); + if (stop_on_idle_ && Thread::Current()->empty()) { + return false; + } + return socketserver()->Wait(cmsWait, process_io); +} + +void VirtualSocketServer::WakeUp() { + socketserver()->WakeUp(); +} + +bool VirtualSocketServer::ProcessMessagesUntilIdle() { + ASSERT(msg_queue_ == Thread::Current()); + stop_on_idle_ = true; + while (!msg_queue_->empty()) { + Message msg; + if (msg_queue_->Get(&msg, kForever)) { + msg_queue_->Dispatch(&msg); + } + } + stop_on_idle_ = false; + return !msg_queue_->IsQuitting(); +} + +void VirtualSocketServer::SetNextPortForTesting(uint16 port) { + next_port_ = port; +} + +int VirtualSocketServer::Bind(VirtualSocket* socket, + const SocketAddress& addr) { + ASSERT(NULL != socket); + // Address must be completely specified at this point + ASSERT(!IPIsUnspec(addr.ipaddr())); + ASSERT(addr.port() != 0); + + // Normalize the address (turns v6-mapped addresses into v4-addresses). + SocketAddress normalized(addr.ipaddr().Normalized(), addr.port()); + + AddressMap::value_type entry(normalized, socket); + return bindings_->insert(entry).second ? 0 : -1; +} + +int VirtualSocketServer::Bind(VirtualSocket* socket, SocketAddress* addr) { + ASSERT(NULL != socket); + + if (IPIsAny(addr->ipaddr())) { + addr->SetIP(GetNextIP(addr->ipaddr().family())); + } else if (!IPIsUnspec(addr->ipaddr())) { + addr->SetIP(addr->ipaddr().Normalized()); + } else { + ASSERT(false); + } + + if (addr->port() == 0) { + for (int i = 0; i < kEphemeralPortCount; ++i) { + addr->SetPort(GetNextPort()); + if (bindings_->find(*addr) == bindings_->end()) { + break; + } + } + } + + return Bind(socket, *addr); +} + +VirtualSocket* VirtualSocketServer::LookupBinding(const SocketAddress& addr) { + SocketAddress normalized(addr.ipaddr().Normalized(), + addr.port()); + AddressMap::iterator it = bindings_->find(normalized); + return (bindings_->end() != it) ? it->second : NULL; +} + +int VirtualSocketServer::Unbind(const SocketAddress& addr, + VirtualSocket* socket) { + SocketAddress normalized(addr.ipaddr().Normalized(), + addr.port()); + ASSERT((*bindings_)[normalized] == socket); + bindings_->erase(bindings_->find(normalized)); + return 0; +} + +void VirtualSocketServer::AddConnection(const SocketAddress& local, + const SocketAddress& remote, + VirtualSocket* remote_socket) { + // Add this socket pair to our routing table. This will allow + // multiple clients to connect to the same server address. + SocketAddress local_normalized(local.ipaddr().Normalized(), + local.port()); + SocketAddress remote_normalized(remote.ipaddr().Normalized(), + remote.port()); + SocketAddressPair address_pair(local_normalized, remote_normalized); + connections_->insert(std::pair(address_pair, remote_socket)); +} + +VirtualSocket* VirtualSocketServer::LookupConnection( + const SocketAddress& local, + const SocketAddress& remote) { + SocketAddress local_normalized(local.ipaddr().Normalized(), + local.port()); + SocketAddress remote_normalized(remote.ipaddr().Normalized(), + remote.port()); + SocketAddressPair address_pair(local_normalized, remote_normalized); + ConnectionMap::iterator it = connections_->find(address_pair); + return (connections_->end() != it) ? it->second : NULL; +} + +void VirtualSocketServer::RemoveConnection(const SocketAddress& local, + const SocketAddress& remote) { + SocketAddress local_normalized(local.ipaddr().Normalized(), + local.port()); + SocketAddress remote_normalized(remote.ipaddr().Normalized(), + remote.port()); + SocketAddressPair address_pair(local_normalized, remote_normalized); + connections_->erase(address_pair); +} + +static double Random() { + return static_cast(rand()) / RAND_MAX; +} + +int VirtualSocketServer::Connect(VirtualSocket* socket, + const SocketAddress& remote_addr, + bool use_delay) { + uint32 delay = use_delay ? GetRandomTransitDelay() : 0; + VirtualSocket* remote = LookupBinding(remote_addr); + if (!CanInteractWith(socket, remote)) { + LOG(LS_INFO) << "Address family mismatch between " + << socket->GetLocalAddress() << " and " << remote_addr; + return -1; + } + if (remote != NULL) { + SocketAddress addr = socket->GetLocalAddress(); + msg_queue_->PostDelayed(delay, remote, MSG_ID_CONNECT, + new MessageAddress(addr)); + } else { + LOG(LS_INFO) << "No one listening at " << remote_addr; + msg_queue_->PostDelayed(delay, socket, MSG_ID_DISCONNECT); + } + return 0; +} + +bool VirtualSocketServer::Disconnect(VirtualSocket* socket) { + if (socket) { + // Remove the mapping. + msg_queue_->Post(socket, MSG_ID_DISCONNECT); + return true; + } + return false; +} + +int VirtualSocketServer::SendUdp(VirtualSocket* socket, + const char* data, size_t data_size, + const SocketAddress& remote_addr) { + // See if we want to drop this packet. + if (Random() < drop_prob_) { + LOG(LS_VERBOSE) << "Dropping packet: bad luck"; + return static_cast(data_size); + } + + VirtualSocket* recipient = LookupBinding(remote_addr); + if (!recipient) { + // Make a fake recipient for address family checking. + scoped_ptr dummy_socket( + CreateSocketInternal(AF_INET, SOCK_DGRAM)); + dummy_socket->SetLocalAddress(remote_addr); + if (!CanInteractWith(socket, dummy_socket.get())) { + LOG(LS_VERBOSE) << "Incompatible address families: " + << socket->GetLocalAddress() << " and " << remote_addr; + return -1; + } + LOG(LS_VERBOSE) << "No one listening at " << remote_addr; + return static_cast(data_size); + } + + if (!CanInteractWith(socket, recipient)) { + LOG(LS_VERBOSE) << "Incompatible address families: " + << socket->GetLocalAddress() << " and " << remote_addr; + return -1; + } + + CritScope cs(&socket->crit_); + + uint32 cur_time = Time(); + PurgeNetworkPackets(socket, cur_time); + + // Determine whether we have enough bandwidth to accept this packet. To do + // this, we need to update the send queue. Once we know it's current size, + // we know whether we can fit this packet. + // + // NOTE: There are better algorithms for maintaining such a queue (such as + // "Derivative Random Drop"); however, this algorithm is a more accurate + // simulation of what a normal network would do. + + size_t packet_size = data_size + UDP_HEADER_SIZE; + if (socket->network_size_ + packet_size > network_capacity_) { + LOG(LS_VERBOSE) << "Dropping packet: network capacity exceeded"; + return static_cast(data_size); + } + + AddPacketToNetwork(socket, recipient, cur_time, data, data_size, + UDP_HEADER_SIZE, false); + + return static_cast(data_size); +} + +void VirtualSocketServer::SendTcp(VirtualSocket* socket) { + // TCP can't send more data than will fill up the receiver's buffer. + // We track the data that is in the buffer plus data in flight using the + // recipient's recv_buffer_size_. Anything beyond that must be stored in the + // sender's buffer. We will trigger the buffered data to be sent when data + // is read from the recv_buffer. + + // Lookup the local/remote pair in the connections table. + VirtualSocket* recipient = LookupConnection(socket->local_addr_, + socket->remote_addr_); + if (!recipient) { + LOG(LS_VERBOSE) << "Sending data to no one."; + return; + } + + CritScope cs(&socket->crit_); + + uint32 cur_time = Time(); + PurgeNetworkPackets(socket, cur_time); + + while (true) { + size_t available = recv_buffer_capacity_ - recipient->recv_buffer_size_; + size_t max_data_size = _min(available, TCP_MSS - TCP_HEADER_SIZE); + size_t data_size = _min(socket->send_buffer_.size(), max_data_size); + if (0 == data_size) + break; + + AddPacketToNetwork(socket, recipient, cur_time, &socket->send_buffer_[0], + data_size, TCP_HEADER_SIZE, true); + recipient->recv_buffer_size_ += data_size; + + size_t new_buffer_size = socket->send_buffer_.size() - data_size; + // Avoid undefined access beyond the last element of the vector. + // This only happens when new_buffer_size is 0. + if (data_size < socket->send_buffer_.size()) { + // memmove is required for potentially overlapping source/destination. + memmove(&socket->send_buffer_[0], &socket->send_buffer_[data_size], + new_buffer_size); + } + socket->send_buffer_.resize(new_buffer_size); + } + + if (socket->write_enabled_ + && (socket->send_buffer_.size() < send_buffer_capacity_)) { + socket->write_enabled_ = false; + socket->SignalWriteEvent(socket); + } +} + +void VirtualSocketServer::AddPacketToNetwork(VirtualSocket* sender, + VirtualSocket* recipient, + uint32 cur_time, + const char* data, + size_t data_size, + size_t header_size, + bool ordered) { + VirtualSocket::NetworkEntry entry; + entry.size = data_size + header_size; + + sender->network_size_ += entry.size; + uint32 send_delay = SendDelay(static_cast(sender->network_size_)); + entry.done_time = cur_time + send_delay; + sender->network_.push_back(entry); + + // Find the delay for crossing the many virtual hops of the network. + uint32 transit_delay = GetRandomTransitDelay(); + + // Post the packet as a message to be delivered (on our own thread) + Packet* p = new Packet(data, data_size, sender->local_addr_); + uint32 ts = TimeAfter(send_delay + transit_delay); + if (ordered) { + // Ensure that new packets arrive after previous ones + // TODO: consider ordering on a per-socket basis, since this + // introduces artifical delay. + ts = TimeMax(ts, network_delay_); + } + msg_queue_->PostAt(ts, recipient, MSG_ID_PACKET, p); + network_delay_ = TimeMax(ts, network_delay_); +} + +void VirtualSocketServer::PurgeNetworkPackets(VirtualSocket* socket, + uint32 cur_time) { + while (!socket->network_.empty() && + (socket->network_.front().done_time <= cur_time)) { + ASSERT(socket->network_size_ >= socket->network_.front().size); + socket->network_size_ -= socket->network_.front().size; + socket->network_.pop_front(); + } +} + +uint32 VirtualSocketServer::SendDelay(uint32 size) { + if (bandwidth_ == 0) + return 0; + else + return 1000 * size / bandwidth_; +} + +#if 0 +void PrintFunction(std::vector >* f) { + return; + double sum = 0; + for (uint32 i = 0; i < f->size(); ++i) { + std::cout << (*f)[i].first << '\t' << (*f)[i].second << std::endl; + sum += (*f)[i].second; + } + if (!f->empty()) { + const double mean = sum / f->size(); + double sum_sq_dev = 0; + for (uint32 i = 0; i < f->size(); ++i) { + double dev = (*f)[i].second - mean; + sum_sq_dev += dev * dev; + } + std::cout << "Mean = " << mean << " StdDev = " + << sqrt(sum_sq_dev / f->size()) << std::endl; + } +} +#endif // + +void VirtualSocketServer::UpdateDelayDistribution() { + Function* dist = CreateDistribution(delay_mean_, delay_stddev_, + delay_samples_); + // We take a lock just to make sure we don't leak memory. + { + CritScope cs(&delay_crit_); + delete delay_dist_; + delay_dist_ = dist; + } +} + +static double PI = 4 * atan(1.0); + +static double Normal(double x, double mean, double stddev) { + double a = (x - mean) * (x - mean) / (2 * stddev * stddev); + return exp(-a) / (stddev * sqrt(2 * PI)); +} + +#if 0 // static unused gives a warning +static double Pareto(double x, double min, double k) { + if (x < min) + return 0; + else + return k * std::pow(min, k) / std::pow(x, k+1); +} +#endif + +VirtualSocketServer::Function* VirtualSocketServer::CreateDistribution( + uint32 mean, uint32 stddev, uint32 samples) { + Function* f = new Function(); + + if (0 == stddev) { + f->push_back(Point(mean, 1.0)); + } else { + double start = 0; + if (mean >= 4 * static_cast(stddev)) + start = mean - 4 * static_cast(stddev); + double end = mean + 4 * static_cast(stddev); + + for (uint32 i = 0; i < samples; i++) { + double x = start + (end - start) * i / (samples - 1); + double y = Normal(x, mean, stddev); + f->push_back(Point(x, y)); + } + } + return Resample(Invert(Accumulate(f)), 0, 1, samples); +} + +uint32 VirtualSocketServer::GetRandomTransitDelay() { + size_t index = rand() % delay_dist_->size(); + double delay = (*delay_dist_)[index].second; + //LOG_F(LS_INFO) << "random[" << index << "] = " << delay; + return static_cast(delay); +} + +struct FunctionDomainCmp { + bool operator()(const VirtualSocketServer::Point& p1, + const VirtualSocketServer::Point& p2) { + return p1.first < p2.first; + } + bool operator()(double v1, const VirtualSocketServer::Point& p2) { + return v1 < p2.first; + } + bool operator()(const VirtualSocketServer::Point& p1, double v2) { + return p1.first < v2; + } +}; + +VirtualSocketServer::Function* VirtualSocketServer::Accumulate(Function* f) { + ASSERT(f->size() >= 1); + double v = 0; + for (Function::size_type i = 0; i < f->size() - 1; ++i) { + double dx = (*f)[i + 1].first - (*f)[i].first; + double avgy = ((*f)[i + 1].second + (*f)[i].second) / 2; + (*f)[i].second = v; + v = v + dx * avgy; + } + (*f)[f->size()-1].second = v; + return f; +} + +VirtualSocketServer::Function* VirtualSocketServer::Invert(Function* f) { + for (Function::size_type i = 0; i < f->size(); ++i) + std::swap((*f)[i].first, (*f)[i].second); + + std::sort(f->begin(), f->end(), FunctionDomainCmp()); + return f; +} + +VirtualSocketServer::Function* VirtualSocketServer::Resample( + Function* f, double x1, double x2, uint32 samples) { + Function* g = new Function(); + + for (size_t i = 0; i < samples; i++) { + double x = x1 + (x2 - x1) * i / (samples - 1); + double y = Evaluate(f, x); + g->push_back(Point(x, y)); + } + + delete f; + return g; +} + +double VirtualSocketServer::Evaluate(Function* f, double x) { + Function::iterator iter = + std::lower_bound(f->begin(), f->end(), x, FunctionDomainCmp()); + if (iter == f->begin()) { + return (*f)[0].second; + } else if (iter == f->end()) { + ASSERT(f->size() >= 1); + return (*f)[f->size() - 1].second; + } else if (iter->first == x) { + return iter->second; + } else { + double x1 = (iter - 1)->first; + double y1 = (iter - 1)->second; + double x2 = iter->first; + double y2 = iter->second; + return y1 + (y2 - y1) * (x - x1) / (x2 - x1); + } +} + +bool VirtualSocketServer::CanInteractWith(VirtualSocket* local, + VirtualSocket* remote) { + if (!local || !remote) { + return false; + } + IPAddress local_ip = local->GetLocalAddress().ipaddr(); + IPAddress remote_ip = remote->GetLocalAddress().ipaddr(); + IPAddress local_normalized = local_ip.Normalized(); + IPAddress remote_normalized = remote_ip.Normalized(); + // Check if the addresses are the same family after Normalization (turns + // mapped IPv6 address into IPv4 addresses). + // This will stop unmapped V6 addresses from talking to mapped V6 addresses. + if (local_normalized.family() == remote_normalized.family()) { + return true; + } + + // If ip1 is IPv4 and ip2 is :: and ip2 is not IPV6_V6ONLY. + int remote_v6_only = 0; + remote->GetOption(Socket::OPT_IPV6_V6ONLY, &remote_v6_only); + if (local_ip.family() == AF_INET && !remote_v6_only && IPIsAny(remote_ip)) { + return true; + } + // Same check, backwards. + int local_v6_only = 0; + local->GetOption(Socket::OPT_IPV6_V6ONLY, &local_v6_only); + if (remote_ip.family() == AF_INET && !local_v6_only && IPIsAny(local_ip)) { + return true; + } + + // Check to see if either socket was explicitly bound to IPv6-any. + // These sockets can talk with anyone. + if (local_ip.family() == AF_INET6 && local->was_any()) { + return true; + } + if (remote_ip.family() == AF_INET6 && remote->was_any()) { + return true; + } + + return false; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/virtualsocketserver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/virtualsocketserver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/virtualsocketserver.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/virtualsocketserver.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,237 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_VIRTUALSOCKETSERVER_H_ +#define WEBRTC_BASE_VIRTUALSOCKETSERVER_H_ + +#include + +#include +#include + +#include "webrtc/base/messagequeue.h" +#include "webrtc/base/socketserver.h" + +namespace rtc { + +class VirtualSocket; +class SocketAddressPair; + +// Simulates a network in the same manner as a loopback interface. The +// interface can create as many addresses as you want. All of the sockets +// created by this network will be able to communicate with one another, unless +// they are bound to addresses from incompatible families. +class VirtualSocketServer : public SocketServer, public sigslot::has_slots<> { + public: + // TODO: Add "owned" parameter. + // If "owned" is set, the supplied socketserver will be deleted later. + explicit VirtualSocketServer(SocketServer* ss); + virtual ~VirtualSocketServer(); + + SocketServer* socketserver() { return server_; } + + // Limits the network bandwidth (maximum bytes per second). Zero means that + // all sends occur instantly. Defaults to 0. + uint32 bandwidth() const { return bandwidth_; } + void set_bandwidth(uint32 bandwidth) { bandwidth_ = bandwidth; } + + // Limits the amount of data which can be in flight on the network without + // packet loss (on a per sender basis). Defaults to 64 KB. + uint32 network_capacity() const { return network_capacity_; } + void set_network_capacity(uint32 capacity) { + network_capacity_ = capacity; + } + + // The amount of data which can be buffered by tcp on the sender's side + uint32 send_buffer_capacity() const { return send_buffer_capacity_; } + void set_send_buffer_capacity(uint32 capacity) { + send_buffer_capacity_ = capacity; + } + + // The amount of data which can be buffered by tcp on the receiver's side + uint32 recv_buffer_capacity() const { return recv_buffer_capacity_; } + void set_recv_buffer_capacity(uint32 capacity) { + recv_buffer_capacity_ = capacity; + } + + // Controls the (transit) delay for packets sent in the network. This does + // not inclue the time required to sit in the send queue. Both of these + // values are measured in milliseconds. Defaults to no delay. + uint32 delay_mean() const { return delay_mean_; } + uint32 delay_stddev() const { return delay_stddev_; } + uint32 delay_samples() const { return delay_samples_; } + void set_delay_mean(uint32 delay_mean) { delay_mean_ = delay_mean; } + void set_delay_stddev(uint32 delay_stddev) { + delay_stddev_ = delay_stddev; + } + void set_delay_samples(uint32 delay_samples) { + delay_samples_ = delay_samples; + } + + // If the (transit) delay parameters are modified, this method should be + // called to recompute the new distribution. + void UpdateDelayDistribution(); + + // Controls the (uniform) probability that any sent packet is dropped. This + // is separate from calculations to drop based on queue size. + double drop_probability() { return drop_prob_; } + void set_drop_probability(double drop_prob) { + assert((0 <= drop_prob) && (drop_prob <= 1)); + drop_prob_ = drop_prob; + } + + // SocketFactory: + virtual Socket* CreateSocket(int type); + virtual Socket* CreateSocket(int family, int type); + + virtual AsyncSocket* CreateAsyncSocket(int type); + virtual AsyncSocket* CreateAsyncSocket(int family, int type); + + // SocketServer: + virtual void SetMessageQueue(MessageQueue* queue); + virtual bool Wait(int cms, bool process_io); + virtual void WakeUp(); + + typedef std::pair Point; + typedef std::vector Function; + + static Function* CreateDistribution(uint32 mean, uint32 stddev, + uint32 samples); + + // Similar to Thread::ProcessMessages, but it only processes messages until + // there are no immediate messages or pending network traffic. Returns false + // if Thread::Stop() was called. + bool ProcessMessagesUntilIdle(); + + // Sets the next port number to use for testing. + void SetNextPortForTesting(uint16 port); + + protected: + // Returns a new IP not used before in this network. + IPAddress GetNextIP(int family); + uint16 GetNextPort(); + + VirtualSocket* CreateSocketInternal(int family, int type); + + // Binds the given socket to addr, assigning and IP and Port if necessary + int Bind(VirtualSocket* socket, SocketAddress* addr); + + // Binds the given socket to the given (fully-defined) address. + int Bind(VirtualSocket* socket, const SocketAddress& addr); + + // Find the socket bound to the given address + VirtualSocket* LookupBinding(const SocketAddress& addr); + + int Unbind(const SocketAddress& addr, VirtualSocket* socket); + + // Adds a mapping between this socket pair and the socket. + void AddConnection(const SocketAddress& client, + const SocketAddress& server, + VirtualSocket* socket); + + // Find the socket pair corresponding to this server address. + VirtualSocket* LookupConnection(const SocketAddress& client, + const SocketAddress& server); + + void RemoveConnection(const SocketAddress& client, + const SocketAddress& server); + + // Connects the given socket to the socket at the given address + int Connect(VirtualSocket* socket, const SocketAddress& remote_addr, + bool use_delay); + + // Sends a disconnect message to the socket at the given address + bool Disconnect(VirtualSocket* socket); + + // Sends the given packet to the socket at the given address (if one exists). + int SendUdp(VirtualSocket* socket, const char* data, size_t data_size, + const SocketAddress& remote_addr); + + // Moves as much data as possible from the sender's buffer to the network + void SendTcp(VirtualSocket* socket); + + // Places a packet on the network. + void AddPacketToNetwork(VirtualSocket* socket, VirtualSocket* recipient, + uint32 cur_time, const char* data, size_t data_size, + size_t header_size, bool ordered); + + // Removes stale packets from the network + void PurgeNetworkPackets(VirtualSocket* socket, uint32 cur_time); + + // Computes the number of milliseconds required to send a packet of this size. + uint32 SendDelay(uint32 size); + + // Returns a random transit delay chosen from the appropriate distribution. + uint32 GetRandomTransitDelay(); + + // Basic operations on functions. Those that return a function also take + // ownership of the function given (and hence, may modify or delete it). + static Function* Accumulate(Function* f); + static Function* Invert(Function* f); + static Function* Resample(Function* f, double x1, double x2, uint32 samples); + static double Evaluate(Function* f, double x); + + // NULL out our message queue if it goes away. Necessary in the case where + // our lifetime is greater than that of the thread we are using, since we + // try to send Close messages for all connected sockets when we shutdown. + void OnMessageQueueDestroyed() { msg_queue_ = NULL; } + + // Determine if two sockets should be able to communicate. + // We don't (currently) specify an address family for sockets; instead, + // the currently bound address is used to infer the address family. + // Any socket that is not explicitly bound to an IPv4 address is assumed to be + // dual-stack capable. + // This function tests if two addresses can communicate, as well as the + // sockets to which they may be bound (the addresses may or may not yet be + // bound to the sockets). + // First the addresses are tested (after normalization): + // If both have the same family, then communication is OK. + // If only one is IPv4 then false, unless the other is bound to ::. + // This applies even if the IPv4 address is 0.0.0.0. + // The socket arguments are optional; the sockets are checked to see if they + // were explicitly bound to IPv6-any ('::'), and if so communication is + // permitted. + // NB: This scheme doesn't permit non-dualstack IPv6 sockets. + static bool CanInteractWith(VirtualSocket* local, VirtualSocket* remote); + + private: + friend class VirtualSocket; + + typedef std::map AddressMap; + typedef std::map ConnectionMap; + + SocketServer* server_; + bool server_owned_; + MessageQueue* msg_queue_; + bool stop_on_idle_; + uint32 network_delay_; + in_addr next_ipv4_; + in6_addr next_ipv6_; + uint16 next_port_; + AddressMap* bindings_; + ConnectionMap* connections_; + + uint32 bandwidth_; + uint32 network_capacity_; + uint32 send_buffer_capacity_; + uint32 recv_buffer_capacity_; + uint32 delay_mean_; + uint32 delay_stddev_; + uint32 delay_samples_; + Function* delay_dist_; + CriticalSection delay_crit_; + + double drop_prob_; + DISALLOW_EVIL_CONSTRUCTORS(VirtualSocketServer); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_VIRTUALSOCKETSERVER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/virtualsocket_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/virtualsocket_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/virtualsocket_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/virtualsocket_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,1002 @@ +/* + * Copyright 2006 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include +#if defined(WEBRTC_POSIX) +#include +#endif + +#include "webrtc/base/logging.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/testclient.h" +#include "webrtc/base/testutils.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/timeutils.h" +#include "webrtc/base/virtualsocketserver.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +using namespace rtc; + +// Sends at a constant rate but with random packet sizes. +struct Sender : public MessageHandler { + Sender(Thread* th, AsyncSocket* s, uint32 rt) + : thread(th), socket(new AsyncUDPSocket(s)), + done(false), rate(rt), count(0) { + last_send = rtc::Time(); + thread->PostDelayed(NextDelay(), this, 1); + } + + uint32 NextDelay() { + uint32 size = (rand() % 4096) + 1; + return 1000 * size / rate; + } + + void OnMessage(Message* pmsg) { + ASSERT_EQ(1u, pmsg->message_id); + + if (done) + return; + + uint32 cur_time = rtc::Time(); + uint32 delay = cur_time - last_send; + uint32 size = rate * delay / 1000; + size = std::min(size, 4096); + size = std::max(size, sizeof(uint32)); + + count += size; + memcpy(dummy, &cur_time, sizeof(cur_time)); + socket->Send(dummy, size, options); + + last_send = cur_time; + thread->PostDelayed(NextDelay(), this, 1); + } + + Thread* thread; + scoped_ptr socket; + rtc::PacketOptions options; + bool done; + uint32 rate; // bytes per second + uint32 count; + uint32 last_send; + char dummy[4096]; +}; + +struct Receiver : public MessageHandler, public sigslot::has_slots<> { + Receiver(Thread* th, AsyncSocket* s, uint32 bw) + : thread(th), socket(new AsyncUDPSocket(s)), bandwidth(bw), done(false), + count(0), sec_count(0), sum(0), sum_sq(0), samples(0) { + socket->SignalReadPacket.connect(this, &Receiver::OnReadPacket); + thread->PostDelayed(1000, this, 1); + } + + ~Receiver() { + thread->Clear(this); + } + + void OnReadPacket(AsyncPacketSocket* s, const char* data, size_t size, + const SocketAddress& remote_addr, + const PacketTime& packet_time) { + ASSERT_EQ(socket.get(), s); + ASSERT_GE(size, 4U); + + count += size; + sec_count += size; + + uint32 send_time = *reinterpret_cast(data); + uint32 recv_time = rtc::Time(); + uint32 delay = recv_time - send_time; + sum += delay; + sum_sq += delay * delay; + samples += 1; + } + + void OnMessage(Message* pmsg) { + ASSERT_EQ(1u, pmsg->message_id); + + if (done) + return; + + // It is always possible for us to receive more than expected because + // packets can be further delayed in delivery. + if (bandwidth > 0) + ASSERT_TRUE(sec_count <= 5 * bandwidth / 4); + sec_count = 0; + thread->PostDelayed(1000, this, 1); + } + + Thread* thread; + scoped_ptr socket; + uint32 bandwidth; + bool done; + size_t count; + size_t sec_count; + double sum; + double sum_sq; + uint32 samples; +}; + +class VirtualSocketServerTest : public testing::Test { + public: + VirtualSocketServerTest() : ss_(new VirtualSocketServer(NULL)), + kIPv4AnyAddress(IPAddress(INADDR_ANY), 0), + kIPv6AnyAddress(IPAddress(in6addr_any), 0) { + } + + void CheckAddressIncrementalization(const SocketAddress& post, + const SocketAddress& pre) { + EXPECT_EQ(post.port(), pre.port() + 1); + IPAddress post_ip = post.ipaddr(); + IPAddress pre_ip = pre.ipaddr(); + EXPECT_EQ(pre_ip.family(), post_ip.family()); + if (post_ip.family() == AF_INET) { + in_addr pre_ipv4 = pre_ip.ipv4_address(); + in_addr post_ipv4 = post_ip.ipv4_address(); + int difference = ntohl(post_ipv4.s_addr) - ntohl(pre_ipv4.s_addr); + EXPECT_EQ(1, difference); + } else if (post_ip.family() == AF_INET6) { + in6_addr post_ip6 = post_ip.ipv6_address(); + in6_addr pre_ip6 = pre_ip.ipv6_address(); + uint32* post_as_ints = reinterpret_cast(&post_ip6.s6_addr); + uint32* pre_as_ints = reinterpret_cast(&pre_ip6.s6_addr); + EXPECT_EQ(post_as_ints[3], pre_as_ints[3] + 1); + } + } + + void BasicTest(const SocketAddress& initial_addr) { + AsyncSocket* socket = ss_->CreateAsyncSocket(initial_addr.family(), + SOCK_DGRAM); + socket->Bind(initial_addr); + SocketAddress server_addr = socket->GetLocalAddress(); + // Make sure VSS didn't switch families on us. + EXPECT_EQ(server_addr.family(), initial_addr.family()); + + TestClient* client1 = new TestClient(new AsyncUDPSocket(socket)); + AsyncSocket* socket2 = + ss_->CreateAsyncSocket(initial_addr.family(), SOCK_DGRAM); + TestClient* client2 = new TestClient(new AsyncUDPSocket(socket2)); + + SocketAddress client2_addr; + EXPECT_EQ(3, client2->SendTo("foo", 3, server_addr)); + EXPECT_TRUE(client1->CheckNextPacket("foo", 3, &client2_addr)); + + SocketAddress client1_addr; + EXPECT_EQ(6, client1->SendTo("bizbaz", 6, client2_addr)); + EXPECT_TRUE(client2->CheckNextPacket("bizbaz", 6, &client1_addr)); + EXPECT_EQ(client1_addr, server_addr); + + SocketAddress empty = EmptySocketAddressWithFamily(initial_addr.family()); + for (int i = 0; i < 10; i++) { + client2 = new TestClient(AsyncUDPSocket::Create(ss_, empty)); + + SocketAddress next_client2_addr; + EXPECT_EQ(3, client2->SendTo("foo", 3, server_addr)); + EXPECT_TRUE(client1->CheckNextPacket("foo", 3, &next_client2_addr)); + CheckAddressIncrementalization(next_client2_addr, client2_addr); + // EXPECT_EQ(next_client2_addr.port(), client2_addr.port() + 1); + + SocketAddress server_addr2; + EXPECT_EQ(6, client1->SendTo("bizbaz", 6, next_client2_addr)); + EXPECT_TRUE(client2->CheckNextPacket("bizbaz", 6, &server_addr2)); + EXPECT_EQ(server_addr2, server_addr); + + client2_addr = next_client2_addr; + } + } + + // initial_addr should be made from either INADDR_ANY or in6addr_any. + void ConnectTest(const SocketAddress& initial_addr) { + testing::StreamSink sink; + SocketAddress accept_addr; + const SocketAddress kEmptyAddr = + EmptySocketAddressWithFamily(initial_addr.family()); + + // Create client + AsyncSocket* client = ss_->CreateAsyncSocket(initial_addr.family(), + SOCK_STREAM); + sink.Monitor(client); + EXPECT_EQ(client->GetState(), AsyncSocket::CS_CLOSED); + EXPECT_TRUE(client->GetLocalAddress().IsNil()); + + // Create server + AsyncSocket* server = ss_->CreateAsyncSocket(initial_addr.family(), + SOCK_STREAM); + sink.Monitor(server); + EXPECT_NE(0, server->Listen(5)); // Bind required + EXPECT_EQ(0, server->Bind(initial_addr)); + EXPECT_EQ(server->GetLocalAddress().family(), initial_addr.family()); + EXPECT_EQ(0, server->Listen(5)); + EXPECT_EQ(server->GetState(), AsyncSocket::CS_CONNECTING); + + // No pending server connections + EXPECT_FALSE(sink.Check(server, testing::SSE_READ)); + EXPECT_TRUE(NULL == server->Accept(&accept_addr)); + EXPECT_EQ(AF_UNSPEC, accept_addr.family()); + + // Attempt connect to listening socket + EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); + EXPECT_NE(client->GetLocalAddress(), kEmptyAddr); // Implicit Bind + EXPECT_NE(AF_UNSPEC, client->GetLocalAddress().family()); // Implicit Bind + EXPECT_NE(client->GetLocalAddress(), server->GetLocalAddress()); + + // Client is connecting + EXPECT_EQ(client->GetState(), AsyncSocket::CS_CONNECTING); + EXPECT_FALSE(sink.Check(client, testing::SSE_OPEN)); + EXPECT_FALSE(sink.Check(client, testing::SSE_CLOSE)); + + ss_->ProcessMessagesUntilIdle(); + + // Client still connecting + EXPECT_EQ(client->GetState(), AsyncSocket::CS_CONNECTING); + EXPECT_FALSE(sink.Check(client, testing::SSE_OPEN)); + EXPECT_FALSE(sink.Check(client, testing::SSE_CLOSE)); + + // Server has pending connection + EXPECT_TRUE(sink.Check(server, testing::SSE_READ)); + Socket* accepted = server->Accept(&accept_addr); + EXPECT_TRUE(NULL != accepted); + EXPECT_NE(accept_addr, kEmptyAddr); + EXPECT_EQ(accepted->GetRemoteAddress(), accept_addr); + + EXPECT_EQ(accepted->GetState(), AsyncSocket::CS_CONNECTED); + EXPECT_EQ(accepted->GetLocalAddress(), server->GetLocalAddress()); + EXPECT_EQ(accepted->GetRemoteAddress(), client->GetLocalAddress()); + + ss_->ProcessMessagesUntilIdle(); + + // Client has connected + EXPECT_EQ(client->GetState(), AsyncSocket::CS_CONNECTED); + EXPECT_TRUE(sink.Check(client, testing::SSE_OPEN)); + EXPECT_FALSE(sink.Check(client, testing::SSE_CLOSE)); + EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress()); + EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress()); + } + + void ConnectToNonListenerTest(const SocketAddress& initial_addr) { + testing::StreamSink sink; + SocketAddress accept_addr; + const SocketAddress nil_addr; + const SocketAddress empty_addr = + EmptySocketAddressWithFamily(initial_addr.family()); + + // Create client + AsyncSocket* client = ss_->CreateAsyncSocket(initial_addr.family(), + SOCK_STREAM); + sink.Monitor(client); + + // Create server + AsyncSocket* server = ss_->CreateAsyncSocket(initial_addr.family(), + SOCK_STREAM); + sink.Monitor(server); + EXPECT_EQ(0, server->Bind(initial_addr)); + EXPECT_EQ(server->GetLocalAddress().family(), initial_addr.family()); + // Attempt connect to non-listening socket + EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); + + ss_->ProcessMessagesUntilIdle(); + + // No pending server connections + EXPECT_FALSE(sink.Check(server, testing::SSE_READ)); + EXPECT_TRUE(NULL == server->Accept(&accept_addr)); + EXPECT_EQ(accept_addr, nil_addr); + + // Connection failed + EXPECT_EQ(client->GetState(), AsyncSocket::CS_CLOSED); + EXPECT_FALSE(sink.Check(client, testing::SSE_OPEN)); + EXPECT_TRUE(sink.Check(client, testing::SSE_ERROR)); + EXPECT_EQ(client->GetRemoteAddress(), nil_addr); + } + + void CloseDuringConnectTest(const SocketAddress& initial_addr) { + testing::StreamSink sink; + SocketAddress accept_addr; + const SocketAddress empty_addr = + EmptySocketAddressWithFamily(initial_addr.family()); + + // Create client and server + scoped_ptr client(ss_->CreateAsyncSocket(initial_addr.family(), + SOCK_STREAM)); + sink.Monitor(client.get()); + scoped_ptr server(ss_->CreateAsyncSocket(initial_addr.family(), + SOCK_STREAM)); + sink.Monitor(server.get()); + + // Initiate connect + EXPECT_EQ(0, server->Bind(initial_addr)); + EXPECT_EQ(server->GetLocalAddress().family(), initial_addr.family()); + + EXPECT_EQ(0, server->Listen(5)); + EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); + + // Server close before socket enters accept queue + EXPECT_FALSE(sink.Check(server.get(), testing::SSE_READ)); + server->Close(); + + ss_->ProcessMessagesUntilIdle(); + + // Result: connection failed + EXPECT_EQ(client->GetState(), AsyncSocket::CS_CLOSED); + EXPECT_TRUE(sink.Check(client.get(), testing::SSE_ERROR)); + + server.reset(ss_->CreateAsyncSocket(initial_addr.family(), SOCK_STREAM)); + sink.Monitor(server.get()); + + // Initiate connect + EXPECT_EQ(0, server->Bind(initial_addr)); + EXPECT_EQ(server->GetLocalAddress().family(), initial_addr.family()); + + EXPECT_EQ(0, server->Listen(5)); + EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); + + ss_->ProcessMessagesUntilIdle(); + + // Server close while socket is in accept queue + EXPECT_TRUE(sink.Check(server.get(), testing::SSE_READ)); + server->Close(); + + ss_->ProcessMessagesUntilIdle(); + + // Result: connection failed + EXPECT_EQ(client->GetState(), AsyncSocket::CS_CLOSED); + EXPECT_TRUE(sink.Check(client.get(), testing::SSE_ERROR)); + + // New server + server.reset(ss_->CreateAsyncSocket(initial_addr.family(), SOCK_STREAM)); + sink.Monitor(server.get()); + + // Initiate connect + EXPECT_EQ(0, server->Bind(initial_addr)); + EXPECT_EQ(server->GetLocalAddress().family(), initial_addr.family()); + + EXPECT_EQ(0, server->Listen(5)); + EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); + + ss_->ProcessMessagesUntilIdle(); + + // Server accepts connection + EXPECT_TRUE(sink.Check(server.get(), testing::SSE_READ)); + scoped_ptr accepted(server->Accept(&accept_addr)); + ASSERT_TRUE(NULL != accepted.get()); + sink.Monitor(accepted.get()); + + // Client closes before connection complets + EXPECT_EQ(accepted->GetState(), AsyncSocket::CS_CONNECTED); + + // Connected message has not been processed yet. + EXPECT_EQ(client->GetState(), AsyncSocket::CS_CONNECTING); + client->Close(); + + ss_->ProcessMessagesUntilIdle(); + + // Result: accepted socket closes + EXPECT_EQ(accepted->GetState(), AsyncSocket::CS_CLOSED); + EXPECT_TRUE(sink.Check(accepted.get(), testing::SSE_CLOSE)); + EXPECT_FALSE(sink.Check(client.get(), testing::SSE_CLOSE)); + } + + void CloseTest(const SocketAddress& initial_addr) { + testing::StreamSink sink; + const SocketAddress kEmptyAddr; + + // Create clients + AsyncSocket* a = ss_->CreateAsyncSocket(initial_addr.family(), SOCK_STREAM); + sink.Monitor(a); + a->Bind(initial_addr); + EXPECT_EQ(a->GetLocalAddress().family(), initial_addr.family()); + + + scoped_ptr b(ss_->CreateAsyncSocket(initial_addr.family(), + SOCK_STREAM)); + sink.Monitor(b.get()); + b->Bind(initial_addr); + EXPECT_EQ(b->GetLocalAddress().family(), initial_addr.family()); + + EXPECT_EQ(0, a->Connect(b->GetLocalAddress())); + EXPECT_EQ(0, b->Connect(a->GetLocalAddress())); + + ss_->ProcessMessagesUntilIdle(); + + EXPECT_TRUE(sink.Check(a, testing::SSE_OPEN)); + EXPECT_EQ(a->GetState(), AsyncSocket::CS_CONNECTED); + EXPECT_EQ(a->GetRemoteAddress(), b->GetLocalAddress()); + + EXPECT_TRUE(sink.Check(b.get(), testing::SSE_OPEN)); + EXPECT_EQ(b->GetState(), AsyncSocket::CS_CONNECTED); + EXPECT_EQ(b->GetRemoteAddress(), a->GetLocalAddress()); + + EXPECT_EQ(1, a->Send("a", 1)); + b->Close(); + EXPECT_EQ(1, a->Send("b", 1)); + + ss_->ProcessMessagesUntilIdle(); + + char buffer[10]; + EXPECT_FALSE(sink.Check(b.get(), testing::SSE_READ)); + EXPECT_EQ(-1, b->Recv(buffer, 10)); + + EXPECT_TRUE(sink.Check(a, testing::SSE_CLOSE)); + EXPECT_EQ(a->GetState(), AsyncSocket::CS_CLOSED); + EXPECT_EQ(a->GetRemoteAddress(), kEmptyAddr); + + // No signal for Closer + EXPECT_FALSE(sink.Check(b.get(), testing::SSE_CLOSE)); + EXPECT_EQ(b->GetState(), AsyncSocket::CS_CLOSED); + EXPECT_EQ(b->GetRemoteAddress(), kEmptyAddr); + } + + void TcpSendTest(const SocketAddress& initial_addr) { + testing::StreamSink sink; + const SocketAddress kEmptyAddr; + + // Connect two sockets + AsyncSocket* a = ss_->CreateAsyncSocket(initial_addr.family(), SOCK_STREAM); + sink.Monitor(a); + a->Bind(initial_addr); + EXPECT_EQ(a->GetLocalAddress().family(), initial_addr.family()); + + AsyncSocket* b = ss_->CreateAsyncSocket(initial_addr.family(), SOCK_STREAM); + sink.Monitor(b); + b->Bind(initial_addr); + EXPECT_EQ(b->GetLocalAddress().family(), initial_addr.family()); + + EXPECT_EQ(0, a->Connect(b->GetLocalAddress())); + EXPECT_EQ(0, b->Connect(a->GetLocalAddress())); + + ss_->ProcessMessagesUntilIdle(); + + const size_t kBufferSize = 2000; + ss_->set_send_buffer_capacity(kBufferSize); + ss_->set_recv_buffer_capacity(kBufferSize); + + const size_t kDataSize = 5000; + char send_buffer[kDataSize], recv_buffer[kDataSize]; + for (size_t i = 0; i < kDataSize; ++i) + send_buffer[i] = static_cast(i % 256); + memset(recv_buffer, 0, sizeof(recv_buffer)); + size_t send_pos = 0, recv_pos = 0; + + // Can't send more than send buffer in one write + int result = a->Send(send_buffer + send_pos, kDataSize - send_pos); + EXPECT_EQ(static_cast(kBufferSize), result); + send_pos += result; + + ss_->ProcessMessagesUntilIdle(); + EXPECT_FALSE(sink.Check(a, testing::SSE_WRITE)); + EXPECT_TRUE(sink.Check(b, testing::SSE_READ)); + + // Receive buffer is already filled, fill send buffer again + result = a->Send(send_buffer + send_pos, kDataSize - send_pos); + EXPECT_EQ(static_cast(kBufferSize), result); + send_pos += result; + + ss_->ProcessMessagesUntilIdle(); + EXPECT_FALSE(sink.Check(a, testing::SSE_WRITE)); + EXPECT_FALSE(sink.Check(b, testing::SSE_READ)); + + // No more room in send or receive buffer + result = a->Send(send_buffer + send_pos, kDataSize - send_pos); + EXPECT_EQ(-1, result); + EXPECT_TRUE(a->IsBlocking()); + + // Read a subset of the data + result = b->Recv(recv_buffer + recv_pos, 500); + EXPECT_EQ(500, result); + recv_pos += result; + + ss_->ProcessMessagesUntilIdle(); + EXPECT_TRUE(sink.Check(a, testing::SSE_WRITE)); + EXPECT_TRUE(sink.Check(b, testing::SSE_READ)); + + // Room for more on the sending side + result = a->Send(send_buffer + send_pos, kDataSize - send_pos); + EXPECT_EQ(500, result); + send_pos += result; + + // Empty the recv buffer + while (true) { + result = b->Recv(recv_buffer + recv_pos, kDataSize - recv_pos); + if (result < 0) { + EXPECT_EQ(-1, result); + EXPECT_TRUE(b->IsBlocking()); + break; + } + recv_pos += result; + } + + ss_->ProcessMessagesUntilIdle(); + EXPECT_TRUE(sink.Check(b, testing::SSE_READ)); + + // Continue to empty the recv buffer + while (true) { + result = b->Recv(recv_buffer + recv_pos, kDataSize - recv_pos); + if (result < 0) { + EXPECT_EQ(-1, result); + EXPECT_TRUE(b->IsBlocking()); + break; + } + recv_pos += result; + } + + // Send last of the data + result = a->Send(send_buffer + send_pos, kDataSize - send_pos); + EXPECT_EQ(500, result); + send_pos += result; + + ss_->ProcessMessagesUntilIdle(); + EXPECT_TRUE(sink.Check(b, testing::SSE_READ)); + + // Receive the last of the data + while (true) { + result = b->Recv(recv_buffer + recv_pos, kDataSize - recv_pos); + if (result < 0) { + EXPECT_EQ(-1, result); + EXPECT_TRUE(b->IsBlocking()); + break; + } + recv_pos += result; + } + + ss_->ProcessMessagesUntilIdle(); + EXPECT_FALSE(sink.Check(b, testing::SSE_READ)); + + // The received data matches the sent data + EXPECT_EQ(kDataSize, send_pos); + EXPECT_EQ(kDataSize, recv_pos); + EXPECT_EQ(0, memcmp(recv_buffer, send_buffer, kDataSize)); + } + + void TcpSendsPacketsInOrderTest(const SocketAddress& initial_addr) { + const SocketAddress kEmptyAddr; + + // Connect two sockets + AsyncSocket* a = ss_->CreateAsyncSocket(initial_addr.family(), + SOCK_STREAM); + AsyncSocket* b = ss_->CreateAsyncSocket(initial_addr.family(), + SOCK_STREAM); + a->Bind(initial_addr); + EXPECT_EQ(a->GetLocalAddress().family(), initial_addr.family()); + + b->Bind(initial_addr); + EXPECT_EQ(b->GetLocalAddress().family(), initial_addr.family()); + + EXPECT_EQ(0, a->Connect(b->GetLocalAddress())); + EXPECT_EQ(0, b->Connect(a->GetLocalAddress())); + ss_->ProcessMessagesUntilIdle(); + + // First, deliver all packets in 0 ms. + char buffer[2] = { 0, 0 }; + const char cNumPackets = 10; + for (char i = 0; i < cNumPackets; ++i) { + buffer[0] = '0' + i; + EXPECT_EQ(1, a->Send(buffer, 1)); + } + + ss_->ProcessMessagesUntilIdle(); + + for (char i = 0; i < cNumPackets; ++i) { + EXPECT_EQ(1, b->Recv(buffer, sizeof(buffer))); + EXPECT_EQ(static_cast('0' + i), buffer[0]); + } + + // Next, deliver packets at random intervals + const uint32 mean = 50; + const uint32 stddev = 50; + + ss_->set_delay_mean(mean); + ss_->set_delay_stddev(stddev); + ss_->UpdateDelayDistribution(); + + for (char i = 0; i < cNumPackets; ++i) { + buffer[0] = 'A' + i; + EXPECT_EQ(1, a->Send(buffer, 1)); + } + + ss_->ProcessMessagesUntilIdle(); + + for (char i = 0; i < cNumPackets; ++i) { + EXPECT_EQ(1, b->Recv(buffer, sizeof(buffer))); + EXPECT_EQ(static_cast('A' + i), buffer[0]); + } + } + + void BandwidthTest(const SocketAddress& initial_addr) { + AsyncSocket* send_socket = + ss_->CreateAsyncSocket(initial_addr.family(), SOCK_DGRAM); + AsyncSocket* recv_socket = + ss_->CreateAsyncSocket(initial_addr.family(), SOCK_DGRAM); + ASSERT_EQ(0, send_socket->Bind(initial_addr)); + ASSERT_EQ(0, recv_socket->Bind(initial_addr)); + EXPECT_EQ(send_socket->GetLocalAddress().family(), initial_addr.family()); + EXPECT_EQ(recv_socket->GetLocalAddress().family(), initial_addr.family()); + ASSERT_EQ(0, send_socket->Connect(recv_socket->GetLocalAddress())); + + uint32 bandwidth = 64 * 1024; + ss_->set_bandwidth(bandwidth); + + Thread* pthMain = Thread::Current(); + Sender sender(pthMain, send_socket, 80 * 1024); + Receiver receiver(pthMain, recv_socket, bandwidth); + + pthMain->ProcessMessages(5000); + sender.done = true; + pthMain->ProcessMessages(5000); + + ASSERT_TRUE(receiver.count >= 5 * 3 * bandwidth / 4); + ASSERT_TRUE(receiver.count <= 6 * bandwidth); // queue could drain for 1s + + ss_->set_bandwidth(0); + } + + void DelayTest(const SocketAddress& initial_addr) { + time_t seed = ::time(NULL); + LOG(LS_VERBOSE) << "seed = " << seed; + srand(static_cast(seed)); + + const uint32 mean = 2000; + const uint32 stddev = 500; + + ss_->set_delay_mean(mean); + ss_->set_delay_stddev(stddev); + ss_->UpdateDelayDistribution(); + + AsyncSocket* send_socket = + ss_->CreateAsyncSocket(initial_addr.family(), SOCK_DGRAM); + AsyncSocket* recv_socket = + ss_->CreateAsyncSocket(initial_addr.family(), SOCK_DGRAM); + ASSERT_EQ(0, send_socket->Bind(initial_addr)); + ASSERT_EQ(0, recv_socket->Bind(initial_addr)); + EXPECT_EQ(send_socket->GetLocalAddress().family(), initial_addr.family()); + EXPECT_EQ(recv_socket->GetLocalAddress().family(), initial_addr.family()); + ASSERT_EQ(0, send_socket->Connect(recv_socket->GetLocalAddress())); + + Thread* pthMain = Thread::Current(); + // Avg packet size is 2K, so at 200KB/s for 10s, we should see about + // 1000 packets, which is necessary to get a good distribution. + Sender sender(pthMain, send_socket, 100 * 2 * 1024); + Receiver receiver(pthMain, recv_socket, 0); + + pthMain->ProcessMessages(10000); + sender.done = receiver.done = true; + ss_->ProcessMessagesUntilIdle(); + + const double sample_mean = receiver.sum / receiver.samples; + double num = + receiver.samples * receiver.sum_sq - receiver.sum * receiver.sum; + double den = receiver.samples * (receiver.samples - 1); + const double sample_stddev = sqrt(num / den); + LOG(LS_VERBOSE) << "mean=" << sample_mean << " stddev=" << sample_stddev; + + EXPECT_LE(500u, receiver.samples); + // We initially used a 0.1 fudge factor, but on the build machine, we + // have seen the value differ by as much as 0.13. + EXPECT_NEAR(mean, sample_mean, 0.15 * mean); + EXPECT_NEAR(stddev, sample_stddev, 0.15 * stddev); + + ss_->set_delay_mean(0); + ss_->set_delay_stddev(0); + ss_->UpdateDelayDistribution(); + } + + // Test cross-family communication between a client bound to client_addr and a + // server bound to server_addr. shouldSucceed indicates if communication is + // expected to work or not. + void CrossFamilyConnectionTest(const SocketAddress& client_addr, + const SocketAddress& server_addr, + bool shouldSucceed) { + testing::StreamSink sink; + SocketAddress accept_address; + const SocketAddress kEmptyAddr; + + // Client gets a IPv4 address + AsyncSocket* client = ss_->CreateAsyncSocket(client_addr.family(), + SOCK_STREAM); + sink.Monitor(client); + EXPECT_EQ(client->GetState(), AsyncSocket::CS_CLOSED); + EXPECT_EQ(client->GetLocalAddress(), kEmptyAddr); + client->Bind(client_addr); + + // Server gets a non-mapped non-any IPv6 address. + // IPv4 sockets should not be able to connect to this. + AsyncSocket* server = ss_->CreateAsyncSocket(server_addr.family(), + SOCK_STREAM); + sink.Monitor(server); + server->Bind(server_addr); + server->Listen(5); + + if (shouldSucceed) { + EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); + ss_->ProcessMessagesUntilIdle(); + EXPECT_TRUE(sink.Check(server, testing::SSE_READ)); + Socket* accepted = server->Accept(&accept_address); + EXPECT_TRUE(NULL != accepted); + EXPECT_NE(kEmptyAddr, accept_address); + ss_->ProcessMessagesUntilIdle(); + EXPECT_TRUE(sink.Check(client, testing::SSE_OPEN)); + EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress()); + } else { + // Check that the connection failed. + EXPECT_EQ(-1, client->Connect(server->GetLocalAddress())); + ss_->ProcessMessagesUntilIdle(); + + EXPECT_FALSE(sink.Check(server, testing::SSE_READ)); + EXPECT_TRUE(NULL == server->Accept(&accept_address)); + EXPECT_EQ(accept_address, kEmptyAddr); + EXPECT_EQ(client->GetState(), AsyncSocket::CS_CLOSED); + EXPECT_FALSE(sink.Check(client, testing::SSE_OPEN)); + EXPECT_EQ(client->GetRemoteAddress(), kEmptyAddr); + } + } + + // Test cross-family datagram sending between a client bound to client_addr + // and a server bound to server_addr. shouldSucceed indicates if sending is + // expected to succed or not. + void CrossFamilyDatagramTest(const SocketAddress& client_addr, + const SocketAddress& server_addr, + bool shouldSucceed) { + AsyncSocket* socket = ss_->CreateAsyncSocket(SOCK_DGRAM); + socket->Bind(server_addr); + SocketAddress bound_server_addr = socket->GetLocalAddress(); + TestClient* client1 = new TestClient(new AsyncUDPSocket(socket)); + + AsyncSocket* socket2 = ss_->CreateAsyncSocket(SOCK_DGRAM); + socket2->Bind(client_addr); + TestClient* client2 = new TestClient(new AsyncUDPSocket(socket2)); + SocketAddress client2_addr; + + if (shouldSucceed) { + EXPECT_EQ(3, client2->SendTo("foo", 3, bound_server_addr)); + EXPECT_TRUE(client1->CheckNextPacket("foo", 3, &client2_addr)); + SocketAddress client1_addr; + EXPECT_EQ(6, client1->SendTo("bizbaz", 6, client2_addr)); + EXPECT_TRUE(client2->CheckNextPacket("bizbaz", 6, &client1_addr)); + EXPECT_EQ(client1_addr, bound_server_addr); + } else { + EXPECT_EQ(-1, client2->SendTo("foo", 3, bound_server_addr)); + EXPECT_FALSE(client1->CheckNextPacket("foo", 3, 0)); + } + } + + protected: + virtual void SetUp() { + Thread::Current()->set_socketserver(ss_); + } + virtual void TearDown() { + Thread::Current()->set_socketserver(NULL); + } + + VirtualSocketServer* ss_; + const SocketAddress kIPv4AnyAddress; + const SocketAddress kIPv6AnyAddress; +}; + +TEST_F(VirtualSocketServerTest, basic_v4) { + SocketAddress ipv4_test_addr(IPAddress(INADDR_ANY), 5000); + BasicTest(ipv4_test_addr); +} + +TEST_F(VirtualSocketServerTest, basic_v6) { + SocketAddress ipv6_test_addr(IPAddress(in6addr_any), 5000); + BasicTest(ipv6_test_addr); +} + +TEST_F(VirtualSocketServerTest, connect_v4) { + ConnectTest(kIPv4AnyAddress); +} + +TEST_F(VirtualSocketServerTest, connect_v6) { + ConnectTest(kIPv6AnyAddress); +} + +TEST_F(VirtualSocketServerTest, connect_to_non_listener_v4) { + ConnectToNonListenerTest(kIPv4AnyAddress); +} + +TEST_F(VirtualSocketServerTest, connect_to_non_listener_v6) { + ConnectToNonListenerTest(kIPv6AnyAddress); +} + +TEST_F(VirtualSocketServerTest, close_during_connect_v4) { + CloseDuringConnectTest(kIPv4AnyAddress); +} + +TEST_F(VirtualSocketServerTest, close_during_connect_v6) { + CloseDuringConnectTest(kIPv6AnyAddress); +} + +TEST_F(VirtualSocketServerTest, close_v4) { + CloseTest(kIPv4AnyAddress); +} + +TEST_F(VirtualSocketServerTest, close_v6) { + CloseTest(kIPv6AnyAddress); +} + +TEST_F(VirtualSocketServerTest, tcp_send_v4) { + TcpSendTest(kIPv4AnyAddress); +} + +TEST_F(VirtualSocketServerTest, tcp_send_v6) { + TcpSendTest(kIPv6AnyAddress); +} + +TEST_F(VirtualSocketServerTest, TcpSendsPacketsInOrder_v4) { + TcpSendsPacketsInOrderTest(kIPv4AnyAddress); +} + +TEST_F(VirtualSocketServerTest, TcpSendsPacketsInOrder_v6) { + TcpSendsPacketsInOrderTest(kIPv6AnyAddress); +} + +TEST_F(VirtualSocketServerTest, bandwidth_v4) { + SocketAddress ipv4_test_addr(IPAddress(INADDR_ANY), 1000); + BandwidthTest(ipv4_test_addr); +} + +TEST_F(VirtualSocketServerTest, bandwidth_v6) { + SocketAddress ipv6_test_addr(IPAddress(in6addr_any), 1000); + BandwidthTest(ipv6_test_addr); +} + +TEST_F(VirtualSocketServerTest, delay_v4) { + SocketAddress ipv4_test_addr(IPAddress(INADDR_ANY), 1000); + DelayTest(ipv4_test_addr); +} + +// See: https://code.google.com/p/webrtc/issues/detail?id=2409 +TEST_F(VirtualSocketServerTest, DISABLED_delay_v6) { + SocketAddress ipv6_test_addr(IPAddress(in6addr_any), 1000); + DelayTest(ipv6_test_addr); +} + +// Works, receiving socket sees 127.0.0.2. +TEST_F(VirtualSocketServerTest, CanConnectFromMappedIPv6ToIPv4Any) { + CrossFamilyConnectionTest(SocketAddress("::ffff:127.0.0.2", 0), + SocketAddress("0.0.0.0", 5000), + true); +} + +// Fails. +TEST_F(VirtualSocketServerTest, CantConnectFromUnMappedIPv6ToIPv4Any) { + CrossFamilyConnectionTest(SocketAddress("::2", 0), + SocketAddress("0.0.0.0", 5000), + false); +} + +// Fails. +TEST_F(VirtualSocketServerTest, CantConnectFromUnMappedIPv6ToMappedIPv6) { + CrossFamilyConnectionTest(SocketAddress("::2", 0), + SocketAddress("::ffff:127.0.0.1", 5000), + false); +} + +// Works. receiving socket sees ::ffff:127.0.0.2. +TEST_F(VirtualSocketServerTest, CanConnectFromIPv4ToIPv6Any) { + CrossFamilyConnectionTest(SocketAddress("127.0.0.2", 0), + SocketAddress("::", 5000), + true); +} + +// Fails. +TEST_F(VirtualSocketServerTest, CantConnectFromIPv4ToUnMappedIPv6) { + CrossFamilyConnectionTest(SocketAddress("127.0.0.2", 0), + SocketAddress("::1", 5000), + false); +} + +// Works. Receiving socket sees ::ffff:127.0.0.1. +TEST_F(VirtualSocketServerTest, CanConnectFromIPv4ToMappedIPv6) { + CrossFamilyConnectionTest(SocketAddress("127.0.0.1", 0), + SocketAddress("::ffff:127.0.0.2", 5000), + true); +} + +// Works, receiving socket sees a result from GetNextIP. +TEST_F(VirtualSocketServerTest, CanConnectFromUnboundIPv6ToIPv4Any) { + CrossFamilyConnectionTest(SocketAddress("::", 0), + SocketAddress("0.0.0.0", 5000), + true); +} + +// Works, receiving socket sees whatever GetNextIP gave the client. +TEST_F(VirtualSocketServerTest, CanConnectFromUnboundIPv4ToIPv6Any) { + CrossFamilyConnectionTest(SocketAddress("0.0.0.0", 0), + SocketAddress("::", 5000), + true); +} + +TEST_F(VirtualSocketServerTest, CanSendDatagramFromUnboundIPv4ToIPv6Any) { + CrossFamilyDatagramTest(SocketAddress("0.0.0.0", 0), + SocketAddress("::", 5000), + true); +} + +TEST_F(VirtualSocketServerTest, CanSendDatagramFromMappedIPv6ToIPv4Any) { + CrossFamilyDatagramTest(SocketAddress("::ffff:127.0.0.1", 0), + SocketAddress("0.0.0.0", 5000), + true); +} + +TEST_F(VirtualSocketServerTest, CantSendDatagramFromUnMappedIPv6ToIPv4Any) { + CrossFamilyDatagramTest(SocketAddress("::2", 0), + SocketAddress("0.0.0.0", 5000), + false); +} + +TEST_F(VirtualSocketServerTest, CantSendDatagramFromUnMappedIPv6ToMappedIPv6) { + CrossFamilyDatagramTest(SocketAddress("::2", 0), + SocketAddress("::ffff:127.0.0.1", 5000), + false); +} + +TEST_F(VirtualSocketServerTest, CanSendDatagramFromIPv4ToIPv6Any) { + CrossFamilyDatagramTest(SocketAddress("127.0.0.2", 0), + SocketAddress("::", 5000), + true); +} + +TEST_F(VirtualSocketServerTest, CantSendDatagramFromIPv4ToUnMappedIPv6) { + CrossFamilyDatagramTest(SocketAddress("127.0.0.2", 0), + SocketAddress("::1", 5000), + false); +} + +TEST_F(VirtualSocketServerTest, CanSendDatagramFromIPv4ToMappedIPv6) { + CrossFamilyDatagramTest(SocketAddress("127.0.0.1", 0), + SocketAddress("::ffff:127.0.0.2", 5000), + true); +} + +TEST_F(VirtualSocketServerTest, CanSendDatagramFromUnboundIPv6ToIPv4Any) { + CrossFamilyDatagramTest(SocketAddress("::", 0), + SocketAddress("0.0.0.0", 5000), + true); +} + +TEST_F(VirtualSocketServerTest, CreatesStandardDistribution) { + const uint32 kTestMean[] = { 10, 100, 333, 1000 }; + const double kTestDev[] = { 0.25, 0.1, 0.01 }; + // TODO: The current code only works for 1000 data points or more. + const uint32 kTestSamples[] = { /*10, 100,*/ 1000 }; + for (size_t midx = 0; midx < ARRAY_SIZE(kTestMean); ++midx) { + for (size_t didx = 0; didx < ARRAY_SIZE(kTestDev); ++didx) { + for (size_t sidx = 0; sidx < ARRAY_SIZE(kTestSamples); ++sidx) { + ASSERT_LT(0u, kTestSamples[sidx]); + const uint32 kStdDev = + static_cast(kTestDev[didx] * kTestMean[midx]); + VirtualSocketServer::Function* f = + VirtualSocketServer::CreateDistribution(kTestMean[midx], + kStdDev, + kTestSamples[sidx]); + ASSERT_TRUE(NULL != f); + ASSERT_EQ(kTestSamples[sidx], f->size()); + double sum = 0; + for (uint32 i = 0; i < f->size(); ++i) { + sum += (*f)[i].second; + } + const double mean = sum / f->size(); + double sum_sq_dev = 0; + for (uint32 i = 0; i < f->size(); ++i) { + double dev = (*f)[i].second - mean; + sum_sq_dev += dev * dev; + } + const double stddev = sqrt(sum_sq_dev / f->size()); + EXPECT_NEAR(kTestMean[midx], mean, 0.1 * kTestMean[midx]) + << "M=" << kTestMean[midx] + << " SD=" << kStdDev + << " N=" << kTestSamples[sidx]; + EXPECT_NEAR(kStdDev, stddev, 0.1 * kStdDev) + << "M=" << kTestMean[midx] + << " SD=" << kStdDev + << " N=" << kTestSamples[sidx]; + delete f; + } + } + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,456 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/win32.h" + +#include +#include +#include + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/byteorder.h" +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" + +namespace rtc { + +// Helper function declarations for inet_ntop/inet_pton. +static const char* inet_ntop_v4(const void* src, char* dst, socklen_t size); +static const char* inet_ntop_v6(const void* src, char* dst, socklen_t size); +static int inet_pton_v4(const char* src, void* dst); +static int inet_pton_v6(const char* src, void* dst); + +// Implementation of inet_ntop (create a printable representation of an +// ip address). XP doesn't have its own inet_ntop, and +// WSAAddressToString requires both IPv6 to be installed and for Winsock +// to be initialized. +const char* win32_inet_ntop(int af, const void *src, + char* dst, socklen_t size) { + if (!src || !dst) { + return NULL; + } + switch (af) { + case AF_INET: { + return inet_ntop_v4(src, dst, size); + } + case AF_INET6: { + return inet_ntop_v6(src, dst, size); + } + } + return NULL; +} + +// As above, but for inet_pton. Implements inet_pton for v4 and v6. +// Note that our inet_ntop will output normal 'dotted' v4 addresses only. +int win32_inet_pton(int af, const char* src, void* dst) { + if (!src || !dst) { + return 0; + } + if (af == AF_INET) { + return inet_pton_v4(src, dst); + } else if (af == AF_INET6) { + return inet_pton_v6(src, dst); + } + return -1; +} + +// Helper function for inet_ntop for IPv4 addresses. +// Outputs "dotted-quad" decimal notation. +const char* inet_ntop_v4(const void* src, char* dst, socklen_t size) { + if (size < INET_ADDRSTRLEN) { + return NULL; + } + const struct in_addr* as_in_addr = + reinterpret_cast(src); + rtc::sprintfn(dst, size, "%d.%d.%d.%d", + as_in_addr->S_un.S_un_b.s_b1, + as_in_addr->S_un.S_un_b.s_b2, + as_in_addr->S_un.S_un_b.s_b3, + as_in_addr->S_un.S_un_b.s_b4); + return dst; +} + +// Helper function for inet_ntop for IPv6 addresses. +const char* inet_ntop_v6(const void* src, char* dst, socklen_t size) { + if (size < INET6_ADDRSTRLEN) { + return NULL; + } + const uint16* as_shorts = + reinterpret_cast(src); + int runpos[8]; + int current = 1; + int max = 1; + int maxpos = -1; + int run_array_size = ARRAY_SIZE(runpos); + // Run over the address marking runs of 0s. + for (int i = 0; i < run_array_size; ++i) { + if (as_shorts[i] == 0) { + runpos[i] = current; + if (current > max) { + maxpos = i; + max = current; + } + ++current; + } else { + runpos[i] = -1; + current =1; + } + } + + if (max > 1) { + int tmpmax = maxpos; + // Run back through, setting -1 for all but the longest run. + for (int i = run_array_size - 1; i >= 0; i--) { + if (i > tmpmax) { + runpos[i] = -1; + } else if (runpos[i] == -1) { + // We're less than maxpos, we hit a -1, so the 'good' run is done. + // Setting tmpmax -1 means all remaining positions get set to -1. + tmpmax = -1; + } + } + } + + char* cursor = dst; + // Print IPv4 compatible and IPv4 mapped addresses using the IPv4 helper. + // These addresses have an initial run of either eight zero-bytes followed + // by 0xFFFF, or an initial run of ten zero-bytes. + if (runpos[0] == 1 && (maxpos == 5 || + (maxpos == 4 && as_shorts[5] == 0xFFFF))) { + *cursor++ = ':'; + *cursor++ = ':'; + if (maxpos == 4) { + cursor += rtc::sprintfn(cursor, INET6_ADDRSTRLEN - 2, "ffff:"); + } + const struct in_addr* as_v4 = + reinterpret_cast(&(as_shorts[6])); + inet_ntop_v4(as_v4, cursor, + static_cast(INET6_ADDRSTRLEN - (cursor - dst))); + } else { + for (int i = 0; i < run_array_size; ++i) { + if (runpos[i] == -1) { + cursor += rtc::sprintfn(cursor, + INET6_ADDRSTRLEN - (cursor - dst), + "%x", NetworkToHost16(as_shorts[i])); + if (i != 7 && runpos[i + 1] != 1) { + *cursor++ = ':'; + } + } else if (runpos[i] == 1) { + // Entered the run; print the colons and skip the run. + *cursor++ = ':'; + *cursor++ = ':'; + i += (max - 1); + } + } + } + return dst; +} + +// Helper function for inet_pton for IPv4 addresses. +// |src| points to a character string containing an IPv4 network address in +// dotted-decimal format, "ddd.ddd.ddd.ddd", where ddd is a decimal number +// of up to three digits in the range 0 to 255. +// The address is converted and copied to dst, +// which must be sizeof(struct in_addr) (4) bytes (32 bits) long. +int inet_pton_v4(const char* src, void* dst) { + const int kIpv4AddressSize = 4; + int found = 0; + const char* src_pos = src; + unsigned char result[kIpv4AddressSize] = {0}; + + while (*src_pos != '\0') { + // strtol won't treat whitespace characters in the begining as an error, + // so check to ensure this is started with digit before passing to strtol. + if (!isdigit(*src_pos)) { + return 0; + } + char* end_pos; + long value = strtol(src_pos, &end_pos, 10); + if (value < 0 || value > 255 || src_pos == end_pos) { + return 0; + } + ++found; + if (found > kIpv4AddressSize) { + return 0; + } + result[found - 1] = static_cast(value); + src_pos = end_pos; + if (*src_pos == '.') { + // There's more. + ++src_pos; + } else if (*src_pos != '\0') { + // If it's neither '.' nor '\0' then return fail. + return 0; + } + } + if (found != kIpv4AddressSize) { + return 0; + } + memcpy(dst, result, sizeof(result)); + return 1; +} + +// Helper function for inet_pton for IPv6 addresses. +int inet_pton_v6(const char* src, void* dst) { + // sscanf will pick any other invalid chars up, but it parses 0xnnnn as hex. + // Check for literal x in the input string. + const char* readcursor = src; + char c = *readcursor++; + while (c) { + if (c == 'x') { + return 0; + } + c = *readcursor++; + } + readcursor = src; + + struct in6_addr an_addr; + memset(&an_addr, 0, sizeof(an_addr)); + + uint16* addr_cursor = reinterpret_cast(&an_addr.s6_addr[0]); + uint16* addr_end = reinterpret_cast(&an_addr.s6_addr[16]); + bool seencompressed = false; + + // Addresses that start with "::" (i.e., a run of initial zeros) or + // "::ffff:" can potentially be IPv4 mapped or compatibility addresses. + // These have dotted-style IPv4 addresses on the end (e.g. "::192.168.7.1"). + if (*readcursor == ':' && *(readcursor+1) == ':' && + *(readcursor + 2) != 0) { + // Check for periods, which we'll take as a sign of v4 addresses. + const char* addrstart = readcursor + 2; + if (rtc::strchr(addrstart, ".")) { + const char* colon = rtc::strchr(addrstart, "::"); + if (colon) { + uint16 a_short; + int bytesread = 0; + if (sscanf(addrstart, "%hx%n", &a_short, &bytesread) != 1 || + a_short != 0xFFFF || bytesread != 4) { + // Colons + periods means has to be ::ffff:a.b.c.d. But it wasn't. + return 0; + } else { + an_addr.s6_addr[10] = 0xFF; + an_addr.s6_addr[11] = 0xFF; + addrstart = colon + 1; + } + } + struct in_addr v4; + if (inet_pton_v4(addrstart, &v4.s_addr)) { + memcpy(&an_addr.s6_addr[12], &v4, sizeof(v4)); + memcpy(dst, &an_addr, sizeof(an_addr)); + return 1; + } else { + // Invalid v4 address. + return 0; + } + } + } + + // For addresses without a trailing IPv4 component ('normal' IPv6 addresses). + while (*readcursor != 0 && addr_cursor < addr_end) { + if (*readcursor == ':') { + if (*(readcursor + 1) == ':') { + if (seencompressed) { + // Can only have one compressed run of zeroes ("::") per address. + return 0; + } + // Hit a compressed run. Count colons to figure out how much of the + // address is skipped. + readcursor += 2; + const char* coloncounter = readcursor; + int coloncount = 0; + if (*coloncounter == 0) { + // Special case - trailing ::. + addr_cursor = addr_end; + } else { + while (*coloncounter) { + if (*coloncounter == ':') { + ++coloncount; + } + ++coloncounter; + } + // (coloncount + 1) is the number of shorts left in the address. + addr_cursor = addr_end - (coloncount + 1); + seencompressed = true; + } + } else { + ++readcursor; + } + } else { + uint16 word; + int bytesread = 0; + if (sscanf(readcursor, "%hx%n", &word, &bytesread) != 1) { + return 0; + } else { + *addr_cursor = HostToNetwork16(word); + ++addr_cursor; + readcursor += bytesread; + if (*readcursor != ':' && *readcursor != '\0') { + return 0; + } + } + } + } + + if (*readcursor != '\0' || addr_cursor < addr_end) { + // Catches addresses too short or too long. + return 0; + } + memcpy(dst, &an_addr, sizeof(an_addr)); + return 1; +} + +// +// Unix time is in seconds relative to 1/1/1970. So we compute the windows +// FILETIME of that time/date, then we add/subtract in appropriate units to +// convert to/from unix time. +// The units of FILETIME are 100ns intervals, so by multiplying by or dividing +// by 10000000, we can convert to/from seconds. +// +// FileTime = UnixTime*10000000 + FileTime(1970) +// UnixTime = (FileTime-FileTime(1970))/10000000 +// + +void FileTimeToUnixTime(const FILETIME& ft, time_t* ut) { + ASSERT(NULL != ut); + + // FILETIME has an earlier date base than time_t (1/1/1970), so subtract off + // the difference. + SYSTEMTIME base_st; + memset(&base_st, 0, sizeof(base_st)); + base_st.wDay = 1; + base_st.wMonth = 1; + base_st.wYear = 1970; + + FILETIME base_ft; + SystemTimeToFileTime(&base_st, &base_ft); + + ULARGE_INTEGER base_ul, current_ul; + memcpy(&base_ul, &base_ft, sizeof(FILETIME)); + memcpy(¤t_ul, &ft, sizeof(FILETIME)); + + // Divide by big number to convert to seconds, then subtract out the 1970 + // base date value. + const ULONGLONG RATIO = 10000000; + *ut = static_cast((current_ul.QuadPart - base_ul.QuadPart) / RATIO); +} + +void UnixTimeToFileTime(const time_t& ut, FILETIME* ft) { + ASSERT(NULL != ft); + + // FILETIME has an earlier date base than time_t (1/1/1970), so add in + // the difference. + SYSTEMTIME base_st; + memset(&base_st, 0, sizeof(base_st)); + base_st.wDay = 1; + base_st.wMonth = 1; + base_st.wYear = 1970; + + FILETIME base_ft; + SystemTimeToFileTime(&base_st, &base_ft); + + ULARGE_INTEGER base_ul; + memcpy(&base_ul, &base_ft, sizeof(FILETIME)); + + // Multiply by big number to convert to 100ns units, then add in the 1970 + // base date value. + const ULONGLONG RATIO = 10000000; + ULARGE_INTEGER current_ul; + current_ul.QuadPart = base_ul.QuadPart + static_cast(ut) * RATIO; + memcpy(ft, ¤t_ul, sizeof(FILETIME)); +} + +bool Utf8ToWindowsFilename(const std::string& utf8, std::wstring* filename) { + // TODO: Integrate into fileutils.h + // TODO: Handle wide and non-wide cases via TCHAR? + // TODO: Skip \\?\ processing if the length is not > MAX_PATH? + // TODO: Write unittests + + // Convert to Utf16 + int wlen = ::MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), + static_cast(utf8.length() + 1), NULL, + 0); + if (0 == wlen) { + return false; + } + wchar_t* wfilename = STACK_ARRAY(wchar_t, wlen); + if (0 == ::MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), + static_cast(utf8.length() + 1), + wfilename, wlen)) { + return false; + } + // Replace forward slashes with backslashes + std::replace(wfilename, wfilename + wlen, L'/', L'\\'); + // Convert to complete filename + DWORD full_len = ::GetFullPathName(wfilename, 0, NULL, NULL); + if (0 == full_len) { + return false; + } + wchar_t* filepart = NULL; + wchar_t* full_filename = STACK_ARRAY(wchar_t, full_len + 6); + wchar_t* start = full_filename + 6; + if (0 == ::GetFullPathName(wfilename, full_len, start, &filepart)) { + return false; + } + // Add long-path prefix + const wchar_t kLongPathPrefix[] = L"\\\\?\\UNC"; + if ((start[0] != L'\\') || (start[1] != L'\\')) { + // Non-unc path: + // Becomes: \\?\ + start -= 4; + ASSERT(start >= full_filename); + memcpy(start, kLongPathPrefix, 4 * sizeof(wchar_t)); + } else if (start[2] != L'?') { + // Unc path: \\\ + // Becomes: \\?\UNC\\ + start -= 6; + ASSERT(start >= full_filename); + memcpy(start, kLongPathPrefix, 7 * sizeof(wchar_t)); + } else { + // Already in long-path form. + } + filename->assign(start); + return true; +} + +bool GetOsVersion(int* major, int* minor, int* build) { + OSVERSIONINFO info = {0}; + info.dwOSVersionInfoSize = sizeof(info); + if (GetVersionEx(&info)) { + if (major) *major = info.dwMajorVersion; + if (minor) *minor = info.dwMinorVersion; + if (build) *build = info.dwBuildNumber; + return true; + } + return false; +} + +bool GetCurrentProcessIntegrityLevel(int* level) { + bool ret = false; + HANDLE process = ::GetCurrentProcess(), token; + if (OpenProcessToken(process, TOKEN_QUERY | TOKEN_QUERY_SOURCE, &token)) { + DWORD size; + if (!GetTokenInformation(token, TokenIntegrityLevel, NULL, 0, &size) && + GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + + char* buf = STACK_ARRAY(char, size); + TOKEN_MANDATORY_LABEL* til = + reinterpret_cast(buf); + if (GetTokenInformation(token, TokenIntegrityLevel, til, size, &size)) { + + DWORD count = *GetSidSubAuthorityCount(til->Label.Sid); + *level = *GetSidSubAuthority(til->Label.Sid, count - 1); + ret = true; + } + } + CloseHandle(token); + } + return ret; +} +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32filesystem.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32filesystem.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32filesystem.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32filesystem.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,460 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/win32filesystem.h" + +#include "webrtc/base/win32.h" +#include +#include +#include + +#include "webrtc/base/fileutils.h" +#include "webrtc/base/pathutils.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/stream.h" +#include "webrtc/base/stringutils.h" + +// In several places in this file, we test the integrity level of the process +// before calling GetLongPathName. We do this because calling GetLongPathName +// when running under protected mode IE (a low integrity process) can result in +// a virtualized path being returned, which is wrong if you only plan to read. +// TODO: Waiting to hear back from IE team on whether this is the +// best approach; IEIsProtectedModeProcess is another possible solution. + +namespace rtc { + +bool Win32Filesystem::CreateFolder(const Pathname &pathname) { + if (pathname.pathname().empty() || !pathname.filename().empty()) + return false; + + std::wstring path16; + if (!Utf8ToWindowsFilename(pathname.pathname(), &path16)) + return false; + + DWORD res = ::GetFileAttributes(path16.c_str()); + if (res != INVALID_FILE_ATTRIBUTES) { + // Something exists at this location, check if it is a directory + return ((res & FILE_ATTRIBUTE_DIRECTORY) != 0); + } else if ((GetLastError() != ERROR_FILE_NOT_FOUND) + && (GetLastError() != ERROR_PATH_NOT_FOUND)) { + // Unexpected error + return false; + } + + // Directory doesn't exist, look up one directory level + if (!pathname.parent_folder().empty()) { + Pathname parent(pathname); + parent.SetFolder(pathname.parent_folder()); + if (!CreateFolder(parent)) { + return false; + } + } + + return (::CreateDirectory(path16.c_str(), NULL) != 0); +} + +FileStream *Win32Filesystem::OpenFile(const Pathname &filename, + const std::string &mode) { + FileStream *fs = new FileStream(); + if (fs && !fs->Open(filename.pathname().c_str(), mode.c_str(), NULL)) { + delete fs; + fs = NULL; + } + return fs; +} + +bool Win32Filesystem::CreatePrivateFile(const Pathname &filename) { + // To make the file private to the current user, we first must construct a + // SECURITY_DESCRIPTOR specifying an ACL. This code is mostly based upon + // http://msdn.microsoft.com/en-us/library/ms707085%28VS.85%29.aspx + + // Get the current process token. + HANDLE process_token = INVALID_HANDLE_VALUE; + if (!::OpenProcessToken(::GetCurrentProcess(), + TOKEN_QUERY, + &process_token)) { + LOG_ERR(LS_ERROR) << "OpenProcessToken() failed"; + return false; + } + + // Get the size of its TOKEN_USER structure. Return value is not checked + // because we expect it to fail. + DWORD token_user_size = 0; + (void)::GetTokenInformation(process_token, + TokenUser, + NULL, + 0, + &token_user_size); + + // Get the TOKEN_USER structure. + scoped_ptr token_user_bytes(new char[token_user_size]); + PTOKEN_USER token_user = reinterpret_cast( + token_user_bytes.get()); + memset(token_user, 0, token_user_size); + BOOL success = ::GetTokenInformation(process_token, + TokenUser, + token_user, + token_user_size, + &token_user_size); + // We're now done with this. + ::CloseHandle(process_token); + if (!success) { + LOG_ERR(LS_ERROR) << "GetTokenInformation() failed"; + return false; + } + + if (!IsValidSid(token_user->User.Sid)) { + LOG_ERR(LS_ERROR) << "Current process has invalid user SID"; + return false; + } + + // Compute size needed for an ACL that allows access to just this user. + int acl_size = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + + GetLengthSid(token_user->User.Sid); + + // Allocate it. + scoped_ptr acl_bytes(new char[acl_size]); + PACL acl = reinterpret_cast(acl_bytes.get()); + memset(acl, 0, acl_size); + if (!::InitializeAcl(acl, acl_size, ACL_REVISION)) { + LOG_ERR(LS_ERROR) << "InitializeAcl() failed"; + return false; + } + + // Allow access to only the current user. + if (!::AddAccessAllowedAce(acl, + ACL_REVISION, + GENERIC_READ | GENERIC_WRITE | STANDARD_RIGHTS_ALL, + token_user->User.Sid)) { + LOG_ERR(LS_ERROR) << "AddAccessAllowedAce() failed"; + return false; + } + + // Now make the security descriptor. + SECURITY_DESCRIPTOR security_descriptor; + if (!::InitializeSecurityDescriptor(&security_descriptor, + SECURITY_DESCRIPTOR_REVISION)) { + LOG_ERR(LS_ERROR) << "InitializeSecurityDescriptor() failed"; + return false; + } + + // Put the ACL in it. + if (!::SetSecurityDescriptorDacl(&security_descriptor, + TRUE, + acl, + FALSE)) { + LOG_ERR(LS_ERROR) << "SetSecurityDescriptorDacl() failed"; + return false; + } + + // Finally create the file. + SECURITY_ATTRIBUTES security_attributes; + security_attributes.nLength = sizeof(security_attributes); + security_attributes.lpSecurityDescriptor = &security_descriptor; + security_attributes.bInheritHandle = FALSE; + HANDLE handle = ::CreateFile( + ToUtf16(filename.pathname()).c_str(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + &security_attributes, + CREATE_NEW, + 0, + NULL); + if (INVALID_HANDLE_VALUE == handle) { + LOG_ERR(LS_ERROR) << "CreateFile() failed"; + return false; + } + if (!::CloseHandle(handle)) { + LOG_ERR(LS_ERROR) << "CloseFile() failed"; + // Continue. + } + return true; +} + +bool Win32Filesystem::DeleteFile(const Pathname &filename) { + LOG(LS_INFO) << "Deleting file " << filename.pathname(); + if (!IsFile(filename)) { + ASSERT(IsFile(filename)); + return false; + } + return ::DeleteFile(ToUtf16(filename.pathname()).c_str()) != 0; +} + +bool Win32Filesystem::DeleteEmptyFolder(const Pathname &folder) { + LOG(LS_INFO) << "Deleting folder " << folder.pathname(); + + std::string no_slash(folder.pathname(), 0, folder.pathname().length()-1); + return ::RemoveDirectory(ToUtf16(no_slash).c_str()) != 0; +} + +bool Win32Filesystem::GetTemporaryFolder(Pathname &pathname, bool create, + const std::string *append) { + wchar_t buffer[MAX_PATH + 1]; + if (!::GetTempPath(ARRAY_SIZE(buffer), buffer)) + return false; + if (!IsCurrentProcessLowIntegrity() && + !::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer))) + return false; + size_t len = strlen(buffer); + if ((len > 0) && (buffer[len-1] != '\\')) { + len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, L"\\"); + } + if (len >= ARRAY_SIZE(buffer) - 1) + return false; + pathname.clear(); + pathname.SetFolder(ToUtf8(buffer)); + if (append != NULL) { + ASSERT(!append->empty()); + pathname.AppendFolder(*append); + } + return !create || CreateFolder(pathname); +} + +std::string Win32Filesystem::TempFilename(const Pathname &dir, + const std::string &prefix) { + wchar_t filename[MAX_PATH]; + if (::GetTempFileName(ToUtf16(dir.pathname()).c_str(), + ToUtf16(prefix).c_str(), 0, filename) != 0) + return ToUtf8(filename); + ASSERT(false); + return ""; +} + +bool Win32Filesystem::MoveFile(const Pathname &old_path, + const Pathname &new_path) { + if (!IsFile(old_path)) { + ASSERT(IsFile(old_path)); + return false; + } + LOG(LS_INFO) << "Moving " << old_path.pathname() + << " to " << new_path.pathname(); + return ::MoveFile(ToUtf16(old_path.pathname()).c_str(), + ToUtf16(new_path.pathname()).c_str()) != 0; +} + +bool Win32Filesystem::MoveFolder(const Pathname &old_path, + const Pathname &new_path) { + if (!IsFolder(old_path)) { + ASSERT(IsFolder(old_path)); + return false; + } + LOG(LS_INFO) << "Moving " << old_path.pathname() + << " to " << new_path.pathname(); + if (::MoveFile(ToUtf16(old_path.pathname()).c_str(), + ToUtf16(new_path.pathname()).c_str()) == 0) { + if (::GetLastError() != ERROR_NOT_SAME_DEVICE) { + LOG_GLE(LS_ERROR) << "Failed to move file"; + return false; + } + if (!CopyFolder(old_path, new_path)) + return false; + if (!DeleteFolderAndContents(old_path)) + return false; + } + return true; +} + +bool Win32Filesystem::IsFolder(const Pathname &path) { + WIN32_FILE_ATTRIBUTE_DATA data = {0}; + if (0 == ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(), + GetFileExInfoStandard, &data)) + return false; + return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == + FILE_ATTRIBUTE_DIRECTORY; +} + +bool Win32Filesystem::IsFile(const Pathname &path) { + WIN32_FILE_ATTRIBUTE_DATA data = {0}; + if (0 == ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(), + GetFileExInfoStandard, &data)) + return false; + return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0; +} + +bool Win32Filesystem::IsAbsent(const Pathname& path) { + WIN32_FILE_ATTRIBUTE_DATA data = {0}; + if (0 != ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(), + GetFileExInfoStandard, &data)) + return false; + DWORD err = ::GetLastError(); + return (ERROR_FILE_NOT_FOUND == err || ERROR_PATH_NOT_FOUND == err); +} + +bool Win32Filesystem::CopyFile(const Pathname &old_path, + const Pathname &new_path) { + return ::CopyFile(ToUtf16(old_path.pathname()).c_str(), + ToUtf16(new_path.pathname()).c_str(), TRUE) != 0; +} + +bool Win32Filesystem::IsTemporaryPath(const Pathname& pathname) { + TCHAR buffer[MAX_PATH + 1]; + if (!::GetTempPath(ARRAY_SIZE(buffer), buffer)) + return false; + if (!IsCurrentProcessLowIntegrity() && + !::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer))) + return false; + return (::strnicmp(ToUtf16(pathname.pathname()).c_str(), + buffer, strlen(buffer)) == 0); +} + +bool Win32Filesystem::GetFileSize(const Pathname &pathname, size_t *size) { + WIN32_FILE_ATTRIBUTE_DATA data = {0}; + if (::GetFileAttributesEx(ToUtf16(pathname.pathname()).c_str(), + GetFileExInfoStandard, &data) == 0) + return false; + *size = data.nFileSizeLow; + return true; +} + +bool Win32Filesystem::GetFileTime(const Pathname& path, FileTimeType which, + time_t* time) { + WIN32_FILE_ATTRIBUTE_DATA data = {0}; + if (::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(), + GetFileExInfoStandard, &data) == 0) + return false; + switch (which) { + case FTT_CREATED: + FileTimeToUnixTime(data.ftCreationTime, time); + break; + case FTT_MODIFIED: + FileTimeToUnixTime(data.ftLastWriteTime, time); + break; + case FTT_ACCESSED: + FileTimeToUnixTime(data.ftLastAccessTime, time); + break; + default: + return false; + } + return true; +} + +bool Win32Filesystem::GetAppPathname(Pathname* path) { + TCHAR buffer[MAX_PATH + 1]; + if (0 == ::GetModuleFileName(NULL, buffer, ARRAY_SIZE(buffer))) + return false; + path->SetPathname(ToUtf8(buffer)); + return true; +} + +bool Win32Filesystem::GetAppDataFolder(Pathname* path, bool per_user) { + ASSERT(!organization_name_.empty()); + ASSERT(!application_name_.empty()); + TCHAR buffer[MAX_PATH + 1]; + int csidl = per_user ? CSIDL_LOCAL_APPDATA : CSIDL_COMMON_APPDATA; + if (!::SHGetSpecialFolderPath(NULL, buffer, csidl, TRUE)) + return false; + if (!IsCurrentProcessLowIntegrity() && + !::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer))) + return false; + size_t len = strcatn(buffer, ARRAY_SIZE(buffer), __T("\\")); + len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, + ToUtf16(organization_name_).c_str()); + if ((len > 0) && (buffer[len-1] != __T('\\'))) { + len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, __T("\\")); + } + len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, + ToUtf16(application_name_).c_str()); + if ((len > 0) && (buffer[len-1] != __T('\\'))) { + len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, __T("\\")); + } + if (len >= ARRAY_SIZE(buffer) - 1) + return false; + path->clear(); + path->SetFolder(ToUtf8(buffer)); + return CreateFolder(*path); +} + +bool Win32Filesystem::GetAppTempFolder(Pathname* path) { + if (!GetAppPathname(path)) + return false; + std::string filename(path->filename()); + return GetTemporaryFolder(*path, true, &filename); +} + +bool Win32Filesystem::GetDiskFreeSpace(const Pathname& path, int64 *freebytes) { + if (!freebytes) { + return false; + } + char drive[4]; + std::wstring drive16; + const wchar_t* target_drive = NULL; + if (path.GetDrive(drive, sizeof(drive))) { + drive16 = ToUtf16(drive); + target_drive = drive16.c_str(); + } else if (path.folder().substr(0, 2) == "\\\\") { + // UNC path, fail. + // TODO: Handle UNC paths. + return false; + } else { + // The path is probably relative. GetDriveType and GetDiskFreeSpaceEx + // use the current drive if NULL is passed as the drive name. + // TODO: Add method to Pathname to determine if the path is relative. + // TODO: Add method to Pathname to convert a path to absolute. + } + UINT driveType = ::GetDriveType(target_drive); + if ( (driveType & DRIVE_REMOTE) || (driveType & DRIVE_UNKNOWN) ) { + LOG(LS_VERBOSE) << " remove or unknown drive " << drive; + return false; + } + + int64 totalNumberOfBytes; // receives the number of bytes on disk + int64 totalNumberOfFreeBytes; // receives the free bytes on disk + // make sure things won't change in 64 bit machine + // TODO replace with compile time assert + ASSERT(sizeof(ULARGE_INTEGER) == sizeof(uint64)); //NOLINT + if (::GetDiskFreeSpaceEx(target_drive, + (PULARGE_INTEGER)freebytes, + (PULARGE_INTEGER)&totalNumberOfBytes, + (PULARGE_INTEGER)&totalNumberOfFreeBytes)) { + return true; + } else { + LOG(LS_VERBOSE) << " GetDiskFreeSpaceEx returns error "; + return false; + } +} + +Pathname Win32Filesystem::GetCurrentDirectory() { + Pathname cwd; + int path_len = 0; + scoped_ptr path; + do { + int needed = ::GetCurrentDirectory(path_len, path.get()); + if (needed == 0) { + // Error. + LOG_GLE(LS_ERROR) << "::GetCurrentDirectory() failed"; + return cwd; // returns empty pathname + } + if (needed <= path_len) { + // It wrote successfully. + break; + } + // Else need to re-alloc for "needed". + path.reset(new wchar_t[needed]); + path_len = needed; + } while (true); + cwd.SetFolder(ToUtf8(path.get())); + return cwd; +} + +// TODO: Consider overriding DeleteFolderAndContents for speed and potentially +// better OS integration (recycle bin?) +/* + std::wstring temp_path16 = ToUtf16(temp_path.pathname()); + temp_path16.append(1, '*'); + temp_path16.append(1, '\0'); + + SHFILEOPSTRUCT file_op = { 0 }; + file_op.wFunc = FO_DELETE; + file_op.pFrom = temp_path16.c_str(); + file_op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT; + return (0 == SHFileOperation(&file_op)); +*/ + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32filesystem.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32filesystem.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32filesystem.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32filesystem.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,101 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef _WEBRTC_BASE_WIN32FILESYSTEM_H__ +#define _WEBRTC_BASE_WIN32FILESYSTEM_H__ + +#include "fileutils.h" + +namespace rtc { + +class Win32Filesystem : public FilesystemInterface { + public: + // Opens a file. Returns an open StreamInterface if function succeeds. Otherwise, + // returns NULL. + virtual FileStream *OpenFile(const Pathname &filename, + const std::string &mode); + + // Atomically creates an empty file accessible only to the current user if one + // does not already exist at the given path, otherwise fails. + virtual bool CreatePrivateFile(const Pathname &filename); + + // This will attempt to delete the path located at filename. + // If the path points to a folder, it will fail with VERIFY + virtual bool DeleteFile(const Pathname &filename); + + // This will attempt to delete an empty folder. If the path does not point to + // a folder, it fails with VERIFY. If the folder is not empty, it fails normally + virtual bool DeleteEmptyFolder(const Pathname &folder); + + // Creates a directory. This will call itself recursively to create /foo/bar even if + // /foo does not exist. + // Returns TRUE if function succeeds + virtual bool CreateFolder(const Pathname &pathname); + + // This moves a file from old_path to new_path. If the new path is on a + // different volume than the old, it will attempt to copy and then delete + // the folder + // Returns true if the file is successfully moved + virtual bool MoveFile(const Pathname &old_path, const Pathname &new_path); + + // Moves a folder from old_path to new_path. If the new path is on a different + // volume from the old, it will attempt to Copy and then Delete the folder + // Returns true if the folder is successfully moved + virtual bool MoveFolder(const Pathname &old_path, const Pathname &new_path); + + // This copies a file from old_path to _new_path + // Returns true if function succeeds + virtual bool CopyFile(const Pathname &old_path, const Pathname &new_path); + + // Returns true if a pathname is a directory + virtual bool IsFolder(const Pathname& pathname); + + // Returns true if a file exists at path + virtual bool IsFile(const Pathname &path); + + // Returns true if pathname refers to no filesystem object, every parent + // directory either exists, or is also absent. + virtual bool IsAbsent(const Pathname& pathname); + + // Returns true if pathname represents a temporary location on the system. + virtual bool IsTemporaryPath(const Pathname& pathname); + + // All of the following functions set pathname and return true if successful. + // Returned paths always include a trailing backslash. + // If create is true, the path will be recursively created. + // If append is non-NULL, it will be appended (and possibly created). + + virtual std::string TempFilename(const Pathname &dir, const std::string &prefix); + + virtual bool GetFileSize(const Pathname& path, size_t* size); + virtual bool GetFileTime(const Pathname& path, FileTimeType which, + time_t* time); + + // A folder appropriate for storing temporary files (Contents are + // automatically deleted when the program exists) + virtual bool GetTemporaryFolder(Pathname &path, bool create, + const std::string *append); + + // Returns the path to the running application. + virtual bool GetAppPathname(Pathname* path); + + virtual bool GetAppDataFolder(Pathname* path, bool per_user); + + // Get a temporary folder that is unique to the current user and application. + virtual bool GetAppTempFolder(Pathname* path); + + virtual bool GetDiskFreeSpace(const Pathname& path, int64 *freebytes); + + virtual Pathname GetCurrentDirectory(); +}; + +} // namespace rtc + +#endif // WEBRTC_WINFILESYSTEM_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32.h 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,129 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_WIN32_H_ +#define WEBRTC_BASE_WIN32_H_ + +#if defined(WEBRTC_WIN) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +// Make sure we don't get min/max macros +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include +#include + +#ifndef SECURITY_MANDATORY_LABEL_AUTHORITY +// Add defines that we use if we are compiling against older sdks +#define SECURITY_MANDATORY_MEDIUM_RID (0x00002000L) +#define TokenIntegrityLevel static_cast(0x19) +typedef struct _TOKEN_MANDATORY_LABEL { + SID_AND_ATTRIBUTES Label; +} TOKEN_MANDATORY_LABEL, *PTOKEN_MANDATORY_LABEL; +#endif // SECURITY_MANDATORY_LABEL_AUTHORITY + +#undef SetPort + +#include + +#include "webrtc/base/stringutils.h" +#include "webrtc/base/basictypes.h" + +namespace rtc { + +const char* win32_inet_ntop(int af, const void *src, char* dst, socklen_t size); +int win32_inet_pton(int af, const char* src, void *dst); + +/////////////////////////////////////////////////////////////////////////////// + +inline std::wstring ToUtf16(const char* utf8, size_t len) { + int len16 = ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast(len), + NULL, 0); + wchar_t* ws = STACK_ARRAY(wchar_t, len16); + ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast(len), ws, len16); + return std::wstring(ws, len16); +} + +inline std::wstring ToUtf16(const std::string& str) { + return ToUtf16(str.data(), str.length()); +} + +inline std::string ToUtf8(const wchar_t* wide, size_t len) { + int len8 = ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast(len), + NULL, 0, NULL, NULL); + char* ns = STACK_ARRAY(char, len8); + ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast(len), ns, len8, + NULL, NULL); + return std::string(ns, len8); +} + +inline std::string ToUtf8(const wchar_t* wide) { + return ToUtf8(wide, wcslen(wide)); +} + +inline std::string ToUtf8(const std::wstring& wstr) { + return ToUtf8(wstr.data(), wstr.length()); +} + +// Convert FILETIME to time_t +void FileTimeToUnixTime(const FILETIME& ft, time_t* ut); + +// Convert time_t to FILETIME +void UnixTimeToFileTime(const time_t& ut, FILETIME * ft); + +// Convert a Utf8 path representation to a non-length-limited Unicode pathname. +bool Utf8ToWindowsFilename(const std::string& utf8, std::wstring* filename); + +// Convert a FILETIME to a UInt64 +inline uint64 ToUInt64(const FILETIME& ft) { + ULARGE_INTEGER r = {ft.dwLowDateTime, ft.dwHighDateTime}; + return r.QuadPart; +} + +enum WindowsMajorVersions { + kWindows2000 = 5, + kWindowsVista = 6, +}; +bool GetOsVersion(int* major, int* minor, int* build); + +inline bool IsWindowsVistaOrLater() { + int major; + return (GetOsVersion(&major, NULL, NULL) && major >= kWindowsVista); +} + +inline bool IsWindowsXpOrLater() { + int major, minor; + return (GetOsVersion(&major, &minor, NULL) && + (major >= kWindowsVista || + (major == kWindows2000 && minor >= 1))); +} + +// Determine the current integrity level of the process. +bool GetCurrentProcessIntegrityLevel(int* level); + +inline bool IsCurrentProcessLowIntegrity() { + int level; + return (GetCurrentProcessIntegrityLevel(&level) && + level < SECURITY_MANDATORY_MEDIUM_RID); +} + +bool AdjustCurrentProcessPrivilege(const TCHAR* privilege, bool to_enable); + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_WIN +#endif // WEBRTC_BASE_WIN32_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32regkey.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32regkey.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32regkey.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32regkey.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,1102 @@ +/* + * Copyright 2003 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Registry configuration wrapers class implementation +// +// Change made by S. Ganesh - ganesh@google.com: +// Use SHQueryValueEx instead of RegQueryValueEx throughout. +// A call to the SHLWAPI function is essentially a call to the standard +// function but with post-processing: +// * to fix REG_SZ or REG_EXPAND_SZ data that is not properly null-terminated; +// * to expand REG_EXPAND_SZ data. + +#include "webrtc/base/win32regkey.h" + +#include + +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/scoped_ptr.h" + +namespace rtc { + +RegKey::RegKey() { + h_key_ = NULL; +} + +RegKey::~RegKey() { + Close(); +} + +HRESULT RegKey::Create(HKEY parent_key, const wchar_t* key_name) { + return Create(parent_key, + key_name, + REG_NONE, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + NULL); +} + +HRESULT RegKey::Open(HKEY parent_key, const wchar_t* key_name) { + return Open(parent_key, key_name, KEY_ALL_ACCESS); +} + +bool RegKey::HasValue(const TCHAR* value_name) const { + return (ERROR_SUCCESS == ::RegQueryValueEx(h_key_, value_name, NULL, + NULL, NULL, NULL)); +} + +HRESULT RegKey::SetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + DWORD value) { + ASSERT(full_key_name != NULL); + + return SetValueStaticHelper(full_key_name, value_name, REG_DWORD, &value); +} + +HRESULT RegKey::SetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + DWORD64 value) { + ASSERT(full_key_name != NULL); + + return SetValueStaticHelper(full_key_name, value_name, REG_QWORD, &value); +} + +HRESULT RegKey::SetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + float value) { + ASSERT(full_key_name != NULL); + + return SetValueStaticHelper(full_key_name, value_name, + REG_BINARY, &value, sizeof(value)); +} + +HRESULT RegKey::SetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + double value) { + ASSERT(full_key_name != NULL); + + return SetValueStaticHelper(full_key_name, value_name, + REG_BINARY, &value, sizeof(value)); +} + +HRESULT RegKey::SetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + const TCHAR* value) { + ASSERT(full_key_name != NULL); + ASSERT(value != NULL); + + return SetValueStaticHelper(full_key_name, value_name, + REG_SZ, const_cast(value)); +} + +HRESULT RegKey::SetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + const uint8* value, + DWORD byte_count) { + ASSERT(full_key_name != NULL); + + return SetValueStaticHelper(full_key_name, value_name, REG_BINARY, + const_cast(value), byte_count); +} + +HRESULT RegKey::SetValueMultiSZ(const wchar_t* full_key_name, + const wchar_t* value_name, + const uint8* value, + DWORD byte_count) { + ASSERT(full_key_name != NULL); + + return SetValueStaticHelper(full_key_name, value_name, REG_MULTI_SZ, + const_cast(value), byte_count); +} + +HRESULT RegKey::GetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + DWORD* value) { + ASSERT(full_key_name != NULL); + ASSERT(value != NULL); + + return GetValueStaticHelper(full_key_name, value_name, REG_DWORD, value); +} + +HRESULT RegKey::GetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + DWORD64* value) { + ASSERT(full_key_name != NULL); + ASSERT(value != NULL); + + return GetValueStaticHelper(full_key_name, value_name, REG_QWORD, value); +} + +HRESULT RegKey::GetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + float* value) { + ASSERT(value != NULL); + ASSERT(full_key_name != NULL); + + DWORD byte_count = 0; + scoped_ptr buffer; + HRESULT hr = GetValueStaticHelper(full_key_name, value_name, + REG_BINARY, buffer.accept(), &byte_count); + if (SUCCEEDED(hr)) { + ASSERT(byte_count == sizeof(*value)); + if (byte_count == sizeof(*value)) { + *value = *reinterpret_cast(buffer.get()); + } + } + return hr; +} + +HRESULT RegKey::GetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + double* value) { + ASSERT(value != NULL); + ASSERT(full_key_name != NULL); + + DWORD byte_count = 0; + scoped_ptr buffer; + HRESULT hr = GetValueStaticHelper(full_key_name, value_name, + REG_BINARY, buffer.accept(), &byte_count); + if (SUCCEEDED(hr)) { + ASSERT(byte_count == sizeof(*value)); + if (byte_count == sizeof(*value)) { + *value = *reinterpret_cast(buffer.get()); + } + } + return hr; +} + +HRESULT RegKey::GetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + wchar_t** value) { + ASSERT(full_key_name != NULL); + ASSERT(value != NULL); + + return GetValueStaticHelper(full_key_name, value_name, REG_SZ, value); +} + +HRESULT RegKey::GetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + std::wstring* value) { + ASSERT(full_key_name != NULL); + ASSERT(value != NULL); + + scoped_ptr buffer; + HRESULT hr = RegKey::GetValue(full_key_name, value_name, buffer.accept()); + if (SUCCEEDED(hr)) { + value->assign(buffer.get()); + } + return hr; +} + +HRESULT RegKey::GetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + std::vector* value) { + ASSERT(full_key_name != NULL); + ASSERT(value != NULL); + + return GetValueStaticHelper(full_key_name, value_name, REG_MULTI_SZ, value); +} + +HRESULT RegKey::GetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + uint8** value, + DWORD* byte_count) { + ASSERT(full_key_name != NULL); + ASSERT(value != NULL); + ASSERT(byte_count != NULL); + + return GetValueStaticHelper(full_key_name, value_name, + REG_BINARY, value, byte_count); +} + +HRESULT RegKey::DeleteSubKey(const wchar_t* key_name) { + ASSERT(key_name != NULL); + ASSERT(h_key_ != NULL); + + LONG res = ::RegDeleteKey(h_key_, key_name); + HRESULT hr = HRESULT_FROM_WIN32(res); + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || + hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) { + hr = S_FALSE; + } + return hr; +} + +HRESULT RegKey::DeleteValue(const wchar_t* value_name) { + ASSERT(h_key_ != NULL); + + LONG res = ::RegDeleteValue(h_key_, value_name); + HRESULT hr = HRESULT_FROM_WIN32(res); + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || + hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) { + hr = S_FALSE; + } + return hr; +} + +HRESULT RegKey::Close() { + HRESULT hr = S_OK; + if (h_key_ != NULL) { + LONG res = ::RegCloseKey(h_key_); + hr = HRESULT_FROM_WIN32(res); + h_key_ = NULL; + } + return hr; +} + +HRESULT RegKey::Create(HKEY parent_key, + const wchar_t* key_name, + wchar_t* lpszClass, + DWORD options, + REGSAM sam_desired, + LPSECURITY_ATTRIBUTES lpSecAttr, + LPDWORD lpdwDisposition) { + ASSERT(key_name != NULL); + ASSERT(parent_key != NULL); + + DWORD dw = 0; + HKEY h_key = NULL; + LONG res = ::RegCreateKeyEx(parent_key, key_name, 0, lpszClass, options, + sam_desired, lpSecAttr, &h_key, &dw); + HRESULT hr = HRESULT_FROM_WIN32(res); + + if (lpdwDisposition) { + *lpdwDisposition = dw; + } + + // we have to close the currently opened key + // before replacing it with the new one + if (hr == S_OK) { + hr = Close(); + ASSERT(hr == S_OK); + h_key_ = h_key; + } + return hr; +} + +HRESULT RegKey::Open(HKEY parent_key, + const wchar_t* key_name, + REGSAM sam_desired) { + ASSERT(key_name != NULL); + ASSERT(parent_key != NULL); + + HKEY h_key = NULL; + LONG res = ::RegOpenKeyEx(parent_key, key_name, 0, sam_desired, &h_key); + HRESULT hr = HRESULT_FROM_WIN32(res); + + // we have to close the currently opened key + // before replacing it with the new one + if (hr == S_OK) { + // close the currently opened key if any + hr = Close(); + ASSERT(hr == S_OK); + h_key_ = h_key; + } + return hr; +} + +// save the key and all of its subkeys and values to a file +HRESULT RegKey::Save(const wchar_t* full_key_name, const wchar_t* file_name) { + ASSERT(full_key_name != NULL); + ASSERT(file_name != NULL); + + std::wstring key_name(full_key_name); + HKEY h_key = GetRootKeyInfo(&key_name); + if (!h_key) { + return E_FAIL; + } + + RegKey key; + HRESULT hr = key.Open(h_key, key_name.c_str(), KEY_READ); + if (FAILED(hr)) { + return hr; + } + + AdjustCurrentProcessPrivilege(SE_BACKUP_NAME, true); + LONG res = ::RegSaveKey(key.h_key_, file_name, NULL); + AdjustCurrentProcessPrivilege(SE_BACKUP_NAME, false); + + return HRESULT_FROM_WIN32(res); +} + +// restore the key and all of its subkeys and values which are saved into a file +HRESULT RegKey::Restore(const wchar_t* full_key_name, + const wchar_t* file_name) { + ASSERT(full_key_name != NULL); + ASSERT(file_name != NULL); + + std::wstring key_name(full_key_name); + HKEY h_key = GetRootKeyInfo(&key_name); + if (!h_key) { + return E_FAIL; + } + + RegKey key; + HRESULT hr = key.Open(h_key, key_name.c_str(), KEY_WRITE); + if (FAILED(hr)) { + return hr; + } + + AdjustCurrentProcessPrivilege(SE_RESTORE_NAME, true); + LONG res = ::RegRestoreKey(key.h_key_, file_name, REG_FORCE_RESTORE); + AdjustCurrentProcessPrivilege(SE_RESTORE_NAME, false); + + return HRESULT_FROM_WIN32(res); +} + +// check if the current key has the specified subkey +bool RegKey::HasSubkey(const wchar_t* key_name) const { + ASSERT(key_name != NULL); + + RegKey key; + HRESULT hr = key.Open(h_key_, key_name, KEY_READ); + key.Close(); + return hr == S_OK; +} + +// static flush key +HRESULT RegKey::FlushKey(const wchar_t* full_key_name) { + ASSERT(full_key_name != NULL); + + HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); + // get the root HKEY + std::wstring key_name(full_key_name); + HKEY h_key = GetRootKeyInfo(&key_name); + + if (h_key != NULL) { + LONG res = ::RegFlushKey(h_key); + hr = HRESULT_FROM_WIN32(res); + } + return hr; +} + +// static SET helper +HRESULT RegKey::SetValueStaticHelper(const wchar_t* full_key_name, + const wchar_t* value_name, + DWORD type, + LPVOID value, + DWORD byte_count) { + ASSERT(full_key_name != NULL); + + HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); + // get the root HKEY + std::wstring key_name(full_key_name); + HKEY h_key = GetRootKeyInfo(&key_name); + + if (h_key != NULL) { + RegKey key; + hr = key.Create(h_key, key_name.c_str()); + if (hr == S_OK) { + switch (type) { + case REG_DWORD: + hr = key.SetValue(value_name, *(static_cast(value))); + break; + case REG_QWORD: + hr = key.SetValue(value_name, *(static_cast(value))); + break; + case REG_SZ: + hr = key.SetValue(value_name, static_cast(value)); + break; + case REG_BINARY: + hr = key.SetValue(value_name, static_cast(value), + byte_count); + break; + case REG_MULTI_SZ: + hr = key.SetValue(value_name, static_cast(value), + byte_count, type); + break; + default: + ASSERT(false); + hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH); + break; + } + // close the key after writing + HRESULT temp_hr = key.Close(); + if (hr == S_OK) { + hr = temp_hr; + } + } + } + return hr; +} + +// static GET helper +HRESULT RegKey::GetValueStaticHelper(const wchar_t* full_key_name, + const wchar_t* value_name, + DWORD type, + LPVOID value, + DWORD* byte_count) { + ASSERT(full_key_name != NULL); + + HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); + // get the root HKEY + std::wstring key_name(full_key_name); + HKEY h_key = GetRootKeyInfo(&key_name); + + if (h_key != NULL) { + RegKey key; + hr = key.Open(h_key, key_name.c_str(), KEY_READ); + if (hr == S_OK) { + switch (type) { + case REG_DWORD: + hr = key.GetValue(value_name, reinterpret_cast(value)); + break; + case REG_QWORD: + hr = key.GetValue(value_name, reinterpret_cast(value)); + break; + case REG_SZ: + hr = key.GetValue(value_name, reinterpret_cast(value)); + break; + case REG_MULTI_SZ: + hr = key.GetValue(value_name, reinterpret_cast< + std::vector*>(value)); + break; + case REG_BINARY: + hr = key.GetValue(value_name, reinterpret_cast(value), + byte_count); + break; + default: + ASSERT(false); + hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH); + break; + } + // close the key after writing + HRESULT temp_hr = key.Close(); + if (hr == S_OK) { + hr = temp_hr; + } + } + } + return hr; +} + +// GET helper +HRESULT RegKey::GetValueHelper(const wchar_t* value_name, + DWORD* type, + uint8** value, + DWORD* byte_count) const { + ASSERT(byte_count != NULL); + ASSERT(value != NULL); + ASSERT(type != NULL); + + // init return buffer + *value = NULL; + + // get the size of the return data buffer + LONG res = ::SHQueryValueEx(h_key_, value_name, NULL, type, NULL, byte_count); + HRESULT hr = HRESULT_FROM_WIN32(res); + + if (hr == S_OK) { + // if the value length is 0, nothing to do + if (*byte_count != 0) { + // allocate the buffer + *value = new byte[*byte_count]; + ASSERT(*value != NULL); + + // make the call again to get the data + res = ::SHQueryValueEx(h_key_, value_name, NULL, + type, *value, byte_count); + hr = HRESULT_FROM_WIN32(res); + ASSERT(hr == S_OK); + } + } + return hr; +} + +// Int32 Get +HRESULT RegKey::GetValue(const wchar_t* value_name, DWORD* value) const { + ASSERT(value != NULL); + + DWORD type = 0; + DWORD byte_count = sizeof(DWORD); + LONG res = ::SHQueryValueEx(h_key_, value_name, NULL, &type, + value, &byte_count); + HRESULT hr = HRESULT_FROM_WIN32(res); + ASSERT((hr != S_OK) || (type == REG_DWORD)); + ASSERT((hr != S_OK) || (byte_count == sizeof(DWORD))); + return hr; +} + +// Int64 Get +HRESULT RegKey::GetValue(const wchar_t* value_name, DWORD64* value) const { + ASSERT(value != NULL); + + DWORD type = 0; + DWORD byte_count = sizeof(DWORD64); + LONG res = ::SHQueryValueEx(h_key_, value_name, NULL, &type, + value, &byte_count); + HRESULT hr = HRESULT_FROM_WIN32(res); + ASSERT((hr != S_OK) || (type == REG_QWORD)); + ASSERT((hr != S_OK) || (byte_count == sizeof(DWORD64))); + return hr; +} + +// String Get +HRESULT RegKey::GetValue(const wchar_t* value_name, wchar_t** value) const { + ASSERT(value != NULL); + + DWORD byte_count = 0; + DWORD type = 0; + + // first get the size of the string buffer + LONG res = ::SHQueryValueEx(h_key_, value_name, NULL, + &type, NULL, &byte_count); + HRESULT hr = HRESULT_FROM_WIN32(res); + + if (hr == S_OK) { + // allocate room for the string and a terminating \0 + *value = new wchar_t[(byte_count / sizeof(wchar_t)) + 1]; + + if ((*value) != NULL) { + if (byte_count != 0) { + // make the call again + res = ::SHQueryValueEx(h_key_, value_name, NULL, &type, + *value, &byte_count); + hr = HRESULT_FROM_WIN32(res); + } else { + (*value)[0] = L'\0'; + } + + ASSERT((hr != S_OK) || (type == REG_SZ) || + (type == REG_MULTI_SZ) || (type == REG_EXPAND_SZ)); + } else { + hr = E_OUTOFMEMORY; + } + } + + return hr; +} + +// get a string value +HRESULT RegKey::GetValue(const wchar_t* value_name, std::wstring* value) const { + ASSERT(value != NULL); + + DWORD byte_count = 0; + DWORD type = 0; + + // first get the size of the string buffer + LONG res = ::SHQueryValueEx(h_key_, value_name, NULL, + &type, NULL, &byte_count); + HRESULT hr = HRESULT_FROM_WIN32(res); + + if (hr == S_OK) { + if (byte_count != 0) { + // Allocate some memory and make the call again + value->resize(byte_count / sizeof(wchar_t) + 1); + res = ::SHQueryValueEx(h_key_, value_name, NULL, &type, + &value->at(0), &byte_count); + hr = HRESULT_FROM_WIN32(res); + value->resize(wcslen(value->data())); + } else { + value->clear(); + } + + ASSERT((hr != S_OK) || (type == REG_SZ) || + (type == REG_MULTI_SZ) || (type == REG_EXPAND_SZ)); + } + + return hr; +} + +// convert REG_MULTI_SZ bytes to string array +HRESULT RegKey::MultiSZBytesToStringArray(const uint8* buffer, + DWORD byte_count, + std::vector* value) { + ASSERT(buffer != NULL); + ASSERT(value != NULL); + + const wchar_t* data = reinterpret_cast(buffer); + DWORD data_len = byte_count / sizeof(wchar_t); + value->clear(); + if (data_len > 1) { + // must be terminated by two null characters + if (data[data_len - 1] != 0 || data[data_len - 2] != 0) { + return E_INVALIDARG; + } + + // put null-terminated strings into arrays + while (*data) { + std::wstring str(data); + value->push_back(str); + data += str.length() + 1; + } + } + return S_OK; +} + +// get a std::vector value from REG_MULTI_SZ type +HRESULT RegKey::GetValue(const wchar_t* value_name, + std::vector* value) const { + ASSERT(value != NULL); + + DWORD byte_count = 0; + DWORD type = 0; + uint8* buffer = 0; + + // first get the size of the buffer + HRESULT hr = GetValueHelper(value_name, &type, &buffer, &byte_count); + ASSERT((hr != S_OK) || (type == REG_MULTI_SZ)); + + if (SUCCEEDED(hr)) { + hr = MultiSZBytesToStringArray(buffer, byte_count, value); + } + + return hr; +} + +// Binary data Get +HRESULT RegKey::GetValue(const wchar_t* value_name, + uint8** value, + DWORD* byte_count) const { + ASSERT(byte_count != NULL); + ASSERT(value != NULL); + + DWORD type = 0; + HRESULT hr = GetValueHelper(value_name, &type, value, byte_count); + ASSERT((hr != S_OK) || (type == REG_MULTI_SZ) || (type == REG_BINARY)); + return hr; +} + +// Raw data get +HRESULT RegKey::GetValue(const wchar_t* value_name, + uint8** value, + DWORD* byte_count, + DWORD*type) const { + ASSERT(type != NULL); + ASSERT(byte_count != NULL); + ASSERT(value != NULL); + + return GetValueHelper(value_name, type, value, byte_count); +} + +// Int32 set +HRESULT RegKey::SetValue(const wchar_t* value_name, DWORD value) const { + ASSERT(h_key_ != NULL); + + LONG res = ::RegSetValueEx(h_key_, value_name, NULL, REG_DWORD, + reinterpret_cast(&value), + sizeof(DWORD)); + return HRESULT_FROM_WIN32(res); +} + +// Int64 set +HRESULT RegKey::SetValue(const wchar_t* value_name, DWORD64 value) const { + ASSERT(h_key_ != NULL); + + LONG res = ::RegSetValueEx(h_key_, value_name, NULL, REG_QWORD, + reinterpret_cast(&value), + sizeof(DWORD64)); + return HRESULT_FROM_WIN32(res); +} + +// String set +HRESULT RegKey::SetValue(const wchar_t* value_name, + const wchar_t* value) const { + ASSERT(value != NULL); + ASSERT(h_key_ != NULL); + + LONG res = ::RegSetValueEx(h_key_, value_name, NULL, REG_SZ, + reinterpret_cast(value), + (lstrlen(value) + 1) * sizeof(wchar_t)); + return HRESULT_FROM_WIN32(res); +} + +// Binary data set +HRESULT RegKey::SetValue(const wchar_t* value_name, + const uint8* value, + DWORD byte_count) const { + ASSERT(h_key_ != NULL); + + // special case - if 'value' is NULL make sure byte_count is zero + if (value == NULL) { + byte_count = 0; + } + + LONG res = ::RegSetValueEx(h_key_, value_name, NULL, + REG_BINARY, value, byte_count); + return HRESULT_FROM_WIN32(res); +} + +// Raw data set +HRESULT RegKey::SetValue(const wchar_t* value_name, + const uint8* value, + DWORD byte_count, + DWORD type) const { + ASSERT(value != NULL); + ASSERT(h_key_ != NULL); + + LONG res = ::RegSetValueEx(h_key_, value_name, NULL, type, value, byte_count); + return HRESULT_FROM_WIN32(res); +} + +bool RegKey::HasKey(const wchar_t* full_key_name) { + ASSERT(full_key_name != NULL); + + // get the root HKEY + std::wstring key_name(full_key_name); + HKEY h_key = GetRootKeyInfo(&key_name); + + if (h_key != NULL) { + RegKey key; + HRESULT hr = key.Open(h_key, key_name.c_str(), KEY_READ); + key.Close(); + return S_OK == hr; + } + return false; +} + +// static version of HasValue +bool RegKey::HasValue(const wchar_t* full_key_name, const wchar_t* value_name) { + ASSERT(full_key_name != NULL); + + bool has_value = false; + // get the root HKEY + std::wstring key_name(full_key_name); + HKEY h_key = GetRootKeyInfo(&key_name); + + if (h_key != NULL) { + RegKey key; + if (key.Open(h_key, key_name.c_str(), KEY_READ) == S_OK) { + has_value = key.HasValue(value_name); + key.Close(); + } + } + return has_value; +} + +HRESULT RegKey::GetValueType(const wchar_t* full_key_name, + const wchar_t* value_name, + DWORD* value_type) { + ASSERT(full_key_name != NULL); + ASSERT(value_type != NULL); + + *value_type = REG_NONE; + + std::wstring key_name(full_key_name); + HKEY h_key = GetRootKeyInfo(&key_name); + + RegKey key; + HRESULT hr = key.Open(h_key, key_name.c_str(), KEY_READ); + if (SUCCEEDED(hr)) { + LONG res = ::SHQueryValueEx(key.h_key_, value_name, NULL, value_type, + NULL, NULL); + if (res != ERROR_SUCCESS) { + hr = HRESULT_FROM_WIN32(res); + } + } + + return hr; +} + +HRESULT RegKey::DeleteKey(const wchar_t* full_key_name) { + ASSERT(full_key_name != NULL); + + return DeleteKey(full_key_name, true); +} + +HRESULT RegKey::DeleteKey(const wchar_t* full_key_name, bool recursively) { + ASSERT(full_key_name != NULL); + + // need to open the parent key first + // get the root HKEY + std::wstring key_name(full_key_name); + HKEY h_key = GetRootKeyInfo(&key_name); + + // get the parent key + std::wstring parent_key(GetParentKeyInfo(&key_name)); + + RegKey key; + HRESULT hr = key.Open(h_key, parent_key.c_str()); + + if (hr == S_OK) { + hr = recursively ? key.RecurseDeleteSubKey(key_name.c_str()) + : key.DeleteSubKey(key_name.c_str()); + } else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || + hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) { + hr = S_FALSE; + } + + key.Close(); + return hr; +} + +HRESULT RegKey::DeleteValue(const wchar_t* full_key_name, + const wchar_t* value_name) { + ASSERT(full_key_name != NULL); + + HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); + // get the root HKEY + std::wstring key_name(full_key_name); + HKEY h_key = GetRootKeyInfo(&key_name); + + if (h_key != NULL) { + RegKey key; + hr = key.Open(h_key, key_name.c_str()); + if (hr == S_OK) { + hr = key.DeleteValue(value_name); + key.Close(); + } + } + return hr; +} + +HRESULT RegKey::RecurseDeleteSubKey(const wchar_t* key_name) { + ASSERT(key_name != NULL); + + RegKey key; + HRESULT hr = key.Open(h_key_, key_name); + + if (hr == S_OK) { + // enumerate all subkeys of this key and recursivelly delete them + FILETIME time = {0}; + wchar_t key_name_buf[kMaxKeyNameChars] = {0}; + DWORD key_name_buf_size = kMaxKeyNameChars; + while (hr == S_OK && + ::RegEnumKeyEx(key.h_key_, 0, key_name_buf, &key_name_buf_size, + NULL, NULL, NULL, &time) == ERROR_SUCCESS) { + hr = key.RecurseDeleteSubKey(key_name_buf); + + // restore the buffer size + key_name_buf_size = kMaxKeyNameChars; + } + // close the top key + key.Close(); + } + + if (hr == S_OK) { + // the key has no more children keys + // delete the key and all of its values + hr = DeleteSubKey(key_name); + } + + return hr; +} + +HKEY RegKey::GetRootKeyInfo(std::wstring* full_key_name) { + ASSERT(full_key_name != NULL); + + HKEY h_key = NULL; + // get the root HKEY + size_t index = full_key_name->find(L'\\'); + std::wstring root_key; + + if (index == -1) { + root_key = *full_key_name; + *full_key_name = L""; + } else { + root_key = full_key_name->substr(0, index); + *full_key_name = full_key_name->substr(index + 1, + full_key_name->length() - index - 1); + } + + for (std::wstring::iterator iter = root_key.begin(); + iter != root_key.end(); ++iter) { + *iter = toupper(*iter); + } + + if (!root_key.compare(L"HKLM") || + !root_key.compare(L"HKEY_LOCAL_MACHINE")) { + h_key = HKEY_LOCAL_MACHINE; + } else if (!root_key.compare(L"HKCU") || + !root_key.compare(L"HKEY_CURRENT_USER")) { + h_key = HKEY_CURRENT_USER; + } else if (!root_key.compare(L"HKU") || + !root_key.compare(L"HKEY_USERS")) { + h_key = HKEY_USERS; + } else if (!root_key.compare(L"HKCR") || + !root_key.compare(L"HKEY_CLASSES_ROOT")) { + h_key = HKEY_CLASSES_ROOT; + } + + return h_key; +} + + +// Returns true if this key name is 'safe' for deletion +// (doesn't specify a key root) +bool RegKey::SafeKeyNameForDeletion(const wchar_t* key_name) { + ASSERT(key_name != NULL); + std::wstring key(key_name); + + HKEY root_key = GetRootKeyInfo(&key); + + if (!root_key) { + key = key_name; + } + if (key.empty()) { + return false; + } + bool found_subkey = false, backslash_found = false; + for (size_t i = 0 ; i < key.length() ; ++i) { + if (key[i] == L'\\') { + backslash_found = true; + } else if (backslash_found) { + found_subkey = true; + break; + } + } + return (root_key == HKEY_USERS) ? found_subkey : true; +} + +std::wstring RegKey::GetParentKeyInfo(std::wstring* key_name) { + ASSERT(key_name != NULL); + + // get the parent key + size_t index = key_name->rfind(L'\\'); + std::wstring parent_key; + if (index == -1) { + parent_key = L""; + } else { + parent_key = key_name->substr(0, index); + *key_name = key_name->substr(index + 1, key_name->length() - index - 1); + } + + return parent_key; +} + +// get the number of values for this key +uint32 RegKey::GetValueCount() { + DWORD num_values = 0; + + if (ERROR_SUCCESS != ::RegQueryInfoKey( + h_key_, // key handle + NULL, // buffer for class name + NULL, // size of class string + NULL, // reserved + NULL, // number of subkeys + NULL, // longest subkey size + NULL, // longest class string + &num_values, // number of values for this key + NULL, // longest value name + NULL, // longest value data + NULL, // security descriptor + NULL)) { // last write time + ASSERT(false); + } + return num_values; +} + +// Enumerators for the value_names for this key + +// Called to get the value name for the given value name index +// Use GetValueCount() to get the total value_name count for this key +// Returns failure if no key at the specified index +HRESULT RegKey::GetValueNameAt(int index, std::wstring* value_name, + DWORD* type) { + ASSERT(value_name != NULL); + + LONG res = ERROR_SUCCESS; + wchar_t value_name_buf[kMaxValueNameChars] = {0}; + DWORD value_name_buf_size = kMaxValueNameChars; + res = ::RegEnumValue(h_key_, index, value_name_buf, &value_name_buf_size, + NULL, type, NULL, NULL); + + if (res == ERROR_SUCCESS) { + value_name->assign(value_name_buf); + } + + return HRESULT_FROM_WIN32(res); +} + +uint32 RegKey::GetSubkeyCount() { + // number of values for key + DWORD num_subkeys = 0; + + if (ERROR_SUCCESS != ::RegQueryInfoKey( + h_key_, // key handle + NULL, // buffer for class name + NULL, // size of class string + NULL, // reserved + &num_subkeys, // number of subkeys + NULL, // longest subkey size + NULL, // longest class string + NULL, // number of values for this key + NULL, // longest value name + NULL, // longest value data + NULL, // security descriptor + NULL)) { // last write time + ASSERT(false); + } + return num_subkeys; +} + +HRESULT RegKey::GetSubkeyNameAt(int index, std::wstring* key_name) { + ASSERT(key_name != NULL); + + LONG res = ERROR_SUCCESS; + wchar_t key_name_buf[kMaxKeyNameChars] = {0}; + DWORD key_name_buf_size = kMaxKeyNameChars; + + res = ::RegEnumKeyEx(h_key_, index, key_name_buf, &key_name_buf_size, + NULL, NULL, NULL, NULL); + + if (res == ERROR_SUCCESS) { + key_name->assign(key_name_buf); + } + + return HRESULT_FROM_WIN32(res); +} + +// Is the key empty: having no sub-keys and values +bool RegKey::IsKeyEmpty(const wchar_t* full_key_name) { + ASSERT(full_key_name != NULL); + + bool is_empty = true; + + // Get the root HKEY + std::wstring key_name(full_key_name); + HKEY h_key = GetRootKeyInfo(&key_name); + + // Open the key to check + if (h_key != NULL) { + RegKey key; + HRESULT hr = key.Open(h_key, key_name.c_str(), KEY_READ); + if (SUCCEEDED(hr)) { + is_empty = key.GetSubkeyCount() == 0 && key.GetValueCount() == 0; + key.Close(); + } + } + + return is_empty; +} + +bool AdjustCurrentProcessPrivilege(const TCHAR* privilege, bool to_enable) { + ASSERT(privilege != NULL); + + bool ret = false; + HANDLE token; + if (::OpenProcessToken(::GetCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) { + LUID luid; + memset(&luid, 0, sizeof(luid)); + if (::LookupPrivilegeValue(NULL, privilege, &luid)) { + TOKEN_PRIVILEGES privs; + privs.PrivilegeCount = 1; + privs.Privileges[0].Luid = luid; + privs.Privileges[0].Attributes = to_enable ? SE_PRIVILEGE_ENABLED : 0; + if (::AdjustTokenPrivileges(token, FALSE, &privs, 0, NULL, 0)) { + ret = true; + } else { + LOG_GLE(LS_ERROR) << "AdjustTokenPrivileges failed"; + } + } else { + LOG_GLE(LS_ERROR) << "LookupPrivilegeValue failed"; + } + CloseHandle(token); + } else { + LOG_GLE(LS_ERROR) << "OpenProcessToken(GetCurrentProcess) failed"; + } + + return ret; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32regkey.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32regkey.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32regkey.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32regkey.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,337 @@ +/* + * Copyright 2003 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Registry configuration wrappers class +// +// Offers static functions for convenient +// fast access for individual values +// +// Also provides a wrapper class for efficient +// batch operations on values of a given registry key. +// + +#ifndef WEBRTC_BASE_WIN32REGKEY_H_ +#define WEBRTC_BASE_WIN32REGKEY_H_ + +#include +#include + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/win32.h" + +namespace rtc { + +// maximum sizes registry key and value names +const int kMaxKeyNameChars = 255 + 1; +const int kMaxValueNameChars = 16383 + 1; + +class RegKey { + public: + // constructor + RegKey(); + + // destructor + ~RegKey(); + + // create a reg key + HRESULT Create(HKEY parent_key, const wchar_t* key_name); + + HRESULT Create(HKEY parent_key, + const wchar_t* key_name, + wchar_t* reg_class, + DWORD options, + REGSAM sam_desired, + LPSECURITY_ATTRIBUTES lp_sec_attr, + LPDWORD lp_disposition); + + // open an existing reg key + HRESULT Open(HKEY parent_key, const wchar_t* key_name); + + HRESULT Open(HKEY parent_key, const wchar_t* key_name, REGSAM sam_desired); + + // close this reg key + HRESULT Close(); + + // check if the key has a specified value + bool HasValue(const wchar_t* value_name) const; + + // get the number of values for this key + uint32 GetValueCount(); + + // Called to get the value name for the given value name index + // Use GetValueCount() to get the total value_name count for this key + // Returns failure if no key at the specified index + // If you modify the key while enumerating, the indexes will be out of order. + // Since the index order is not guaranteed, you need to reset your counting + // loop. + // 'type' refers to REG_DWORD, REG_QWORD, etc.. + // 'type' can be NULL if not interested in the value type + HRESULT GetValueNameAt(int index, std::wstring* value_name, DWORD* type); + + // check if the current key has the specified subkey + bool HasSubkey(const wchar_t* key_name) const; + + // get the number of subkeys for this key + uint32 GetSubkeyCount(); + + // Called to get the key name for the given key index + // Use GetSubkeyCount() to get the total count for this key + // Returns failure if no key at the specified index + // If you modify the key while enumerating, the indexes will be out of order. + // Since the index order is not guaranteed, you need to reset your counting + // loop. + HRESULT GetSubkeyNameAt(int index, std::wstring* key_name); + + // SETTERS + + // set an int32 value - use when reading multiple values from a key + HRESULT SetValue(const wchar_t* value_name, DWORD value) const; + + // set an int64 value + HRESULT SetValue(const wchar_t* value_name, DWORD64 value) const; + + // set a string value + HRESULT SetValue(const wchar_t* value_name, const wchar_t* value) const; + + // set binary data + HRESULT SetValue(const wchar_t* value_name, + const uint8* value, + DWORD byte_count) const; + + // set raw data, including type + HRESULT SetValue(const wchar_t* value_name, + const uint8* value, + DWORD byte_count, + DWORD type) const; + + // GETTERS + + // get an int32 value + HRESULT GetValue(const wchar_t* value_name, DWORD* value) const; + + // get an int64 value + HRESULT GetValue(const wchar_t* value_name, DWORD64* value) const; + + // get a string value - the caller must free the return buffer + HRESULT GetValue(const wchar_t* value_name, wchar_t** value) const; + + // get a string value + HRESULT GetValue(const wchar_t* value_name, std::wstring* value) const; + + // get a std::vector value from REG_MULTI_SZ type + HRESULT GetValue(const wchar_t* value_name, + std::vector* value) const; + + // get binary data - the caller must free the return buffer + HRESULT GetValue(const wchar_t* value_name, + uint8** value, + DWORD* byte_count) const; + + // get raw data, including type - the caller must free the return buffer + HRESULT GetValue(const wchar_t* value_name, + uint8** value, + DWORD* byte_count, + DWORD* type) const; + + // STATIC VERSIONS + + // flush + static HRESULT FlushKey(const wchar_t* full_key_name); + + // check if a key exists + static bool HasKey(const wchar_t* full_key_name); + + // check if the key has a specified value + static bool HasValue(const wchar_t* full_key_name, const wchar_t* value_name); + + // SETTERS + + // STATIC int32 set + static HRESULT SetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + DWORD value); + + // STATIC int64 set + static HRESULT SetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + DWORD64 value); + + // STATIC float set + static HRESULT SetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + float value); + + // STATIC double set + static HRESULT SetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + double value); + + // STATIC string set + static HRESULT SetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + const wchar_t* value); + + // STATIC binary data set + static HRESULT SetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + const uint8* value, + DWORD byte_count); + + // STATIC multi-string set + static HRESULT SetValueMultiSZ(const wchar_t* full_key_name, + const TCHAR* value_name, + const uint8* value, + DWORD byte_count); + + // GETTERS + + // STATIC int32 get + static HRESULT GetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + DWORD* value); + + // STATIC int64 get + // + // Note: if you are using time64 you should + // likely use GetLimitedTimeValue (util.h) instead of this method. + static HRESULT GetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + DWORD64* value); + + // STATIC float get + static HRESULT GetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + float* value); + + // STATIC double get + static HRESULT GetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + double* value); + + // STATIC string get + // Note: the caller must free the return buffer for wchar_t* version + static HRESULT GetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + wchar_t** value); + static HRESULT GetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + std::wstring* value); + + // STATIC REG_MULTI_SZ get + static HRESULT GetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + std::vector* value); + + // STATIC get binary data - the caller must free the return buffer + static HRESULT GetValue(const wchar_t* full_key_name, + const wchar_t* value_name, + uint8** value, + DWORD* byte_count); + + // Get type of a registry value + static HRESULT GetValueType(const wchar_t* full_key_name, + const wchar_t* value_name, + DWORD* value_type); + + // delete a subkey of the current key (with no subkeys) + HRESULT DeleteSubKey(const wchar_t* key_name); + + // recursively delete a sub key of the current key (and all its subkeys) + HRESULT RecurseDeleteSubKey(const wchar_t* key_name); + + // STATIC version of delete key - handles nested keys also + // delete a key and all its sub-keys recursively + // Returns S_FALSE if key didn't exist, S_OK if deletion was successful, + // and failure otherwise. + static HRESULT DeleteKey(const wchar_t* full_key_name); + + // STATIC version of delete key + // delete a key recursively or non-recursively + // Returns S_FALSE if key didn't exist, S_OK if deletion was successful, + // and failure otherwise. + static HRESULT DeleteKey(const wchar_t* full_key_name, bool recursive); + + // delete the specified value + HRESULT DeleteValue(const wchar_t* value_name); + + // STATIC version of delete value + // Returns S_FALSE if key didn't exist, S_OK if deletion was successful, + // and failure otherwise. + static HRESULT DeleteValue(const wchar_t* full_key_name, + const wchar_t* value_name); + + // Peek inside (use a RegKey as a smart wrapper around a registry handle) + HKEY key() { return h_key_; } + + // helper function to get the HKEY and the root key from a string + // modifies the argument in place and returns the key name + // e.g. HKLM\\Software\\Google\... returns HKLM, "Software\\Google\..." + // Necessary for the static versions that use the full name of the reg key + static HKEY GetRootKeyInfo(std::wstring* full_key_name); + + // Returns true if this key name is 'safe' for deletion (doesn't specify a key + // root) + static bool SafeKeyNameForDeletion(const wchar_t* key_name); + + // save the key and all of its subkeys and values to a file + static HRESULT Save(const wchar_t* full_key_name, const wchar_t* file_name); + + // restore the key and all of its subkeys and values which are saved into a + // file + static HRESULT Restore(const wchar_t* full_key_name, + const wchar_t* file_name); + + // Is the key empty: having no sub-keys and values + static bool IsKeyEmpty(const wchar_t* full_key_name); + + private: + + // helper function to get any value from the registry + // used when the size of the data is unknown + HRESULT GetValueHelper(const wchar_t* value_name, + DWORD* type, uint8** value, + DWORD* byte_count) const; + + // helper function to get the parent key name and the subkey from a string + // modifies the argument in place and returns the key name + // Necessary for the static versions that use the full name of the reg key + static std::wstring GetParentKeyInfo(std::wstring* key_name); + + // common SET Helper for the static case + static HRESULT SetValueStaticHelper(const wchar_t* full_key_name, + const wchar_t* value_name, + DWORD type, + LPVOID value, + DWORD byte_count = 0); + + // common GET Helper for the static case + static HRESULT GetValueStaticHelper(const wchar_t* full_key_name, + const wchar_t* value_name, + DWORD type, + LPVOID value, + DWORD* byte_count = NULL); + + // convert REG_MULTI_SZ bytes to string array + static HRESULT MultiSZBytesToStringArray(const uint8* buffer, + DWORD byte_count, + std::vector* value); + + // the HKEY for the current key + HKEY h_key_; + + // for unittest + friend void RegKeyHelperFunctionsTest(); + + DISALLOW_EVIL_CONSTRUCTORS(RegKey); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_WIN32REGKEY_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32regkey_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32regkey_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32regkey_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32regkey_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,590 @@ +/* + * Copyright 2003 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unittest for registry access API + +#include "webrtc/base/gunit.h" +#include "webrtc/base/common.h" +#include "webrtc/base/win32regkey.h" + +namespace rtc { + +#ifndef EXPECT_SUCCEEDED +#define EXPECT_SUCCEEDED(x) EXPECT_TRUE(SUCCEEDED(x)) +#endif + +#ifndef EXPECT_FAILED +#define EXPECT_FAILED(x) EXPECT_TRUE(FAILED(x)) +#endif + +#define kBaseKey L"Software\\Google\\__TEST" +#define kSubkeyName L"subkey_test" + +const wchar_t kRkey1[] = kBaseKey; +const wchar_t kRkey1SubkeyName[] = kSubkeyName; +const wchar_t kRkey1Subkey[] = kBaseKey L"\\" kSubkeyName; +const wchar_t kFullRkey1[] = L"HKCU\\" kBaseKey; +const wchar_t kFullRkey1Subkey[] = L"HKCU\\" kBaseKey L"\\" kSubkeyName; + +const wchar_t kValNameInt[] = L"Int32 Value"; +const DWORD kIntVal = 20; +const DWORD kIntVal2 = 30; + +const wchar_t kValNameInt64[] = L"Int64 Value"; +const DWORD64 kIntVal64 = 119600064000000000uI64; + +const wchar_t kValNameFloat[] = L"Float Value"; +const float kFloatVal = 12.3456789f; + +const wchar_t kValNameDouble[] = L"Double Value"; +const double kDoubleVal = 98.7654321; + +const wchar_t kValNameStr[] = L"Str Value"; +const wchar_t kStrVal[] = L"Some string data 1"; +const wchar_t kStrVal2[] = L"Some string data 2"; + +const wchar_t kValNameBinary[] = L"Binary Value"; +const char kBinaryVal[] = "Some binary data abcdefghi 1"; +const char kBinaryVal2[] = "Some binary data abcdefghi 2"; + +const wchar_t kValNameMultiStr[] = L"MultiStr Value"; +const wchar_t kMultiSZ[] = L"abc\0def\0P12345\0"; +const wchar_t kEmptyMultiSZ[] = L""; +const wchar_t kInvalidMultiSZ[] = {L'6', L'7', L'8'}; + +// friend function of RegKey +void RegKeyHelperFunctionsTest() { + // Try out some dud values + std::wstring temp_key = L""; + EXPECT_TRUE(RegKey::GetRootKeyInfo(&temp_key) == NULL); + EXPECT_STREQ(temp_key.c_str(), L""); + + temp_key = L"a"; + EXPECT_TRUE(RegKey::GetRootKeyInfo(&temp_key) == NULL); + EXPECT_STREQ(temp_key.c_str(), L""); + + // The basics + temp_key = L"HKLM\\a"; + EXPECT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_LOCAL_MACHINE); + EXPECT_STREQ(temp_key.c_str(), L"a"); + + temp_key = L"HKEY_LOCAL_MACHINE\\a"; + EXPECT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_LOCAL_MACHINE); + EXPECT_STREQ(temp_key.c_str(), L"a"); + + temp_key = L"HKCU\\a"; + EXPECT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CURRENT_USER); + EXPECT_STREQ(temp_key.c_str(), L"a"); + + temp_key = L"HKEY_CURRENT_USER\\a"; + EXPECT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CURRENT_USER); + EXPECT_STREQ(temp_key.c_str(), L"a"); + + temp_key = L"HKU\\a"; + EXPECT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_USERS); + EXPECT_STREQ(temp_key.c_str(), L"a"); + + temp_key = L"HKEY_USERS\\a"; + EXPECT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_USERS); + EXPECT_STREQ(temp_key.c_str(), L"a"); + + temp_key = L"HKCR\\a"; + EXPECT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CLASSES_ROOT); + EXPECT_STREQ(temp_key.c_str(), L"a"); + + temp_key = L"HKEY_CLASSES_ROOT\\a"; + EXPECT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CLASSES_ROOT); + EXPECT_STREQ(temp_key.c_str(), L"a"); + + // Make sure it is case insensitive + temp_key = L"hkcr\\a"; + EXPECT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CLASSES_ROOT); + EXPECT_STREQ(temp_key.c_str(), L"a"); + + temp_key = L"hkey_CLASSES_ROOT\\a"; + EXPECT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CLASSES_ROOT); + EXPECT_STREQ(temp_key.c_str(), L"a"); + + // + // Test RegKey::GetParentKeyInfo + // + + // dud cases + temp_key = L""; + EXPECT_STREQ(RegKey::GetParentKeyInfo(&temp_key).c_str(), L""); + EXPECT_STREQ(temp_key.c_str(), L""); + + temp_key = L"a"; + EXPECT_STREQ(RegKey::GetParentKeyInfo(&temp_key).c_str(), L""); + EXPECT_STREQ(temp_key.c_str(), L"a"); + + temp_key = L"a\\b"; + EXPECT_STREQ(RegKey::GetParentKeyInfo(&temp_key).c_str(), L"a"); + EXPECT_STREQ(temp_key.c_str(), L"b"); + + temp_key = L"\\b"; + EXPECT_STREQ(RegKey::GetParentKeyInfo(&temp_key).c_str(), L""); + EXPECT_STREQ(temp_key.c_str(), L"b"); + + // Some regular cases + temp_key = L"HKEY_CLASSES_ROOT\\moon"; + EXPECT_STREQ(RegKey::GetParentKeyInfo(&temp_key).c_str(), + L"HKEY_CLASSES_ROOT"); + EXPECT_STREQ(temp_key.c_str(), L"moon"); + + temp_key = L"HKEY_CLASSES_ROOT\\moon\\doggy"; + EXPECT_STREQ(RegKey::GetParentKeyInfo(&temp_key).c_str(), + L"HKEY_CLASSES_ROOT\\moon"); + EXPECT_STREQ(temp_key.c_str(), L"doggy"); + + // + // Test MultiSZBytesToStringArray + // + + std::vector result; + EXPECT_SUCCEEDED(RegKey::MultiSZBytesToStringArray( + reinterpret_cast(kMultiSZ), sizeof(kMultiSZ), &result)); + EXPECT_EQ(result.size(), 3); + EXPECT_STREQ(result[0].c_str(), L"abc"); + EXPECT_STREQ(result[1].c_str(), L"def"); + EXPECT_STREQ(result[2].c_str(), L"P12345"); + + EXPECT_SUCCEEDED(RegKey::MultiSZBytesToStringArray( + reinterpret_cast(kEmptyMultiSZ), + sizeof(kEmptyMultiSZ), &result)); + EXPECT_EQ(result.size(), 0); + EXPECT_FALSE(SUCCEEDED(RegKey::MultiSZBytesToStringArray( + reinterpret_cast(kInvalidMultiSZ), + sizeof(kInvalidMultiSZ), &result))); +} + +TEST(RegKeyTest, RegKeyHelperFunctionsTest) { + RegKeyHelperFunctionsTest(); +} + +TEST(RegKeyTest, RegKeyNonStaticFunctionsTest) { + DWORD int_val = 0; + DWORD64 int64_val = 0; + wchar_t* str_val = NULL; + uint8* binary_val = NULL; + DWORD uint8_count = 0; + + // Just in case... + // make sure the no test key residue is left from previous aborted runs + RegKey::DeleteKey(kFullRkey1); + + // initial state + RegKey r_key; + EXPECT_TRUE(r_key.key() == NULL); + + // create a reg key + EXPECT_SUCCEEDED(r_key.Create(HKEY_CURRENT_USER, kRkey1)); + + // do the create twice - it should return the already created one + EXPECT_SUCCEEDED(r_key.Create(HKEY_CURRENT_USER, kRkey1)); + + // now do an open - should work just fine + EXPECT_SUCCEEDED(r_key.Open(HKEY_CURRENT_USER, kRkey1)); + + // get an in-existent value + EXPECT_EQ(r_key.GetValue(kValNameInt, &int_val), + HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + + // set and get some values + + // set an INT 32 + EXPECT_SUCCEEDED(r_key.SetValue(kValNameInt, kIntVal)); + + // check that the value exists + EXPECT_TRUE(r_key.HasValue(kValNameInt)); + + // read it back + EXPECT_SUCCEEDED(r_key.GetValue(kValNameInt, &int_val)); + EXPECT_EQ(int_val, kIntVal); + + // set it again! + EXPECT_SUCCEEDED(r_key.SetValue(kValNameInt, kIntVal2)); + + // read it again + EXPECT_SUCCEEDED(r_key.GetValue(kValNameInt, &int_val)); + EXPECT_EQ(int_val, kIntVal2); + + // delete the value + EXPECT_SUCCEEDED(r_key.DeleteValue(kValNameInt)); + + // check that the value is gone + EXPECT_FALSE(r_key.HasValue(kValNameInt)); + + // set an INT 64 + EXPECT_SUCCEEDED(r_key.SetValue(kValNameInt64, kIntVal64)); + + // check that the value exists + EXPECT_TRUE(r_key.HasValue(kValNameInt64)); + + // read it back + EXPECT_SUCCEEDED(r_key.GetValue(kValNameInt64, &int64_val)); + EXPECT_EQ(int64_val, kIntVal64); + + // delete the value + EXPECT_SUCCEEDED(r_key.DeleteValue(kValNameInt64)); + + // check that the value is gone + EXPECT_FALSE(r_key.HasValue(kValNameInt64)); + + // set a string + EXPECT_SUCCEEDED(r_key.SetValue(kValNameStr, kStrVal)); + + // check that the value exists + EXPECT_TRUE(r_key.HasValue(kValNameStr)); + + // read it back + EXPECT_SUCCEEDED(r_key.GetValue(kValNameStr, &str_val)); + EXPECT_TRUE(lstrcmp(str_val, kStrVal) == 0); + delete[] str_val; + + // set it again + EXPECT_SUCCEEDED(r_key.SetValue(kValNameStr, kStrVal2)); + + // read it again + EXPECT_SUCCEEDED(r_key.GetValue(kValNameStr, &str_val)); + EXPECT_TRUE(lstrcmp(str_val, kStrVal2) == 0); + delete[] str_val; + + // delete the value + EXPECT_SUCCEEDED(r_key.DeleteValue(kValNameStr)); + + // check that the value is gone + EXPECT_FALSE(r_key.HasValue(kValNameInt)); + + // set a binary value + EXPECT_SUCCEEDED(r_key.SetValue(kValNameBinary, + reinterpret_cast(kBinaryVal), sizeof(kBinaryVal) - 1)); + + // check that the value exists + EXPECT_TRUE(r_key.HasValue(kValNameBinary)); + + // read it back + EXPECT_SUCCEEDED(r_key.GetValue(kValNameBinary, &binary_val, &uint8_count)); + EXPECT_TRUE(memcmp(binary_val, kBinaryVal, sizeof(kBinaryVal) - 1) == 0); + delete[] binary_val; + + // set it again + EXPECT_SUCCEEDED(r_key.SetValue(kValNameBinary, + reinterpret_cast(kBinaryVal2), sizeof(kBinaryVal) - 1)); + + // read it again + EXPECT_SUCCEEDED(r_key.GetValue(kValNameBinary, &binary_val, &uint8_count)); + EXPECT_TRUE(memcmp(binary_val, kBinaryVal2, sizeof(kBinaryVal2) - 1) == 0); + delete[] binary_val; + + // delete the value + EXPECT_SUCCEEDED(r_key.DeleteValue(kValNameBinary)); + + // check that the value is gone + EXPECT_FALSE(r_key.HasValue(kValNameBinary)); + + // set some values and check the total count + + // set an INT 32 + EXPECT_SUCCEEDED(r_key.SetValue(kValNameInt, kIntVal)); + + // set an INT 64 + EXPECT_SUCCEEDED(r_key.SetValue(kValNameInt64, kIntVal64)); + + // set a string + EXPECT_SUCCEEDED(r_key.SetValue(kValNameStr, kStrVal)); + + // set a binary value + EXPECT_SUCCEEDED(r_key.SetValue(kValNameBinary, + reinterpret_cast(kBinaryVal), sizeof(kBinaryVal) - 1)); + + // get the value count + uint32 value_count = r_key.GetValueCount(); + EXPECT_EQ(value_count, 4); + + // check the value names + std::wstring value_name; + DWORD type = 0; + + EXPECT_SUCCEEDED(r_key.GetValueNameAt(0, &value_name, &type)); + EXPECT_STREQ(value_name.c_str(), kValNameInt); + EXPECT_EQ(type, REG_DWORD); + + EXPECT_SUCCEEDED(r_key.GetValueNameAt(1, &value_name, &type)); + EXPECT_STREQ(value_name.c_str(), kValNameInt64); + EXPECT_EQ(type, REG_QWORD); + + EXPECT_SUCCEEDED(r_key.GetValueNameAt(2, &value_name, &type)); + EXPECT_STREQ(value_name.c_str(), kValNameStr); + EXPECT_EQ(type, REG_SZ); + + EXPECT_SUCCEEDED(r_key.GetValueNameAt(3, &value_name, &type)); + EXPECT_STREQ(value_name.c_str(), kValNameBinary); + EXPECT_EQ(type, REG_BINARY); + + // check that there are no more values + EXPECT_FAILED(r_key.GetValueNameAt(4, &value_name, &type)); + + uint32 subkey_count = r_key.GetSubkeyCount(); + EXPECT_EQ(subkey_count, 0); + + // now create a subkey and make sure we can get the name + RegKey temp_key; + EXPECT_SUCCEEDED(temp_key.Create(HKEY_CURRENT_USER, kRkey1Subkey)); + + // check the subkey exists + EXPECT_TRUE(r_key.HasSubkey(kRkey1SubkeyName)); + + // check the name + EXPECT_EQ(r_key.GetSubkeyCount(), 1); + + std::wstring subkey_name; + EXPECT_SUCCEEDED(r_key.GetSubkeyNameAt(0, &subkey_name)); + EXPECT_STREQ(subkey_name.c_str(), kRkey1SubkeyName); + + // delete the key + EXPECT_SUCCEEDED(r_key.DeleteSubKey(kRkey1)); + + // close this key + EXPECT_SUCCEEDED(r_key.Close()); + + // whack the whole key + EXPECT_SUCCEEDED(RegKey::DeleteKey(kFullRkey1)); +} + +TEST(RegKeyTest, RegKeyStaticFunctionsTest) { + DWORD int_val = 0; + DWORD64 int64_val = 0; + float float_val = 0; + double double_val = 0; + wchar_t* str_val = NULL; + std::wstring wstr_val; + uint8* binary_val = NULL; + DWORD uint8_count = 0; + + // Just in case... + // make sure the no test key residue is left from previous aborted runs + RegKey::DeleteKey(kFullRkey1); + + // get an in-existent value from an un-existent key + EXPECT_EQ(RegKey::GetValue(kFullRkey1, kValNameInt, &int_val), + HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + + // set int32 + EXPECT_SUCCEEDED(RegKey::SetValue(kFullRkey1, kValNameInt, kIntVal)); + + // check that the value exists + EXPECT_TRUE(RegKey::HasValue(kFullRkey1, kValNameInt)); + + // get an in-existent value from an existent key + EXPECT_EQ(RegKey::GetValue(kFullRkey1, L"bogus", &int_val), + HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + + // read it back + EXPECT_SUCCEEDED(RegKey::GetValue(kFullRkey1, kValNameInt, &int_val)); + EXPECT_EQ(int_val, kIntVal); + + // delete the value + EXPECT_SUCCEEDED(RegKey::DeleteValue(kFullRkey1, kValNameInt)); + + // check that the value is gone + EXPECT_FALSE(RegKey::HasValue(kFullRkey1, kValNameInt)); + + // set int64 + EXPECT_SUCCEEDED(RegKey::SetValue(kFullRkey1, kValNameInt64, kIntVal64)); + + // check that the value exists + EXPECT_TRUE(RegKey::HasValue(kFullRkey1, kValNameInt64)); + + // read it back + EXPECT_SUCCEEDED(RegKey::GetValue(kFullRkey1, kValNameInt64, &int64_val)); + EXPECT_EQ(int64_val, kIntVal64); + + // delete the value + EXPECT_SUCCEEDED(RegKey::DeleteValue(kFullRkey1, kValNameInt64)); + + // check that the value is gone + EXPECT_FALSE(RegKey::HasValue(kFullRkey1, kValNameInt64)); + + // set float + EXPECT_SUCCEEDED(RegKey::SetValue(kFullRkey1, kValNameFloat, kFloatVal)); + + // check that the value exists + EXPECT_TRUE(RegKey::HasValue(kFullRkey1, kValNameFloat)); + + // read it back + EXPECT_SUCCEEDED(RegKey::GetValue(kFullRkey1, kValNameFloat, &float_val)); + EXPECT_EQ(float_val, kFloatVal); + + // delete the value + EXPECT_SUCCEEDED(RegKey::DeleteValue(kFullRkey1, kValNameFloat)); + + // check that the value is gone + EXPECT_FALSE(RegKey::HasValue(kFullRkey1, kValNameFloat)); + EXPECT_FAILED(RegKey::GetValue(kFullRkey1, kValNameFloat, &float_val)); + + // set double + EXPECT_SUCCEEDED(RegKey::SetValue(kFullRkey1, kValNameDouble, kDoubleVal)); + + // check that the value exists + EXPECT_TRUE(RegKey::HasValue(kFullRkey1, kValNameDouble)); + + // read it back + EXPECT_SUCCEEDED(RegKey::GetValue(kFullRkey1, kValNameDouble, &double_val)); + EXPECT_EQ(double_val, kDoubleVal); + + // delete the value + EXPECT_SUCCEEDED(RegKey::DeleteValue(kFullRkey1, kValNameDouble)); + + // check that the value is gone + EXPECT_FALSE(RegKey::HasValue(kFullRkey1, kValNameDouble)); + EXPECT_FAILED(RegKey::GetValue(kFullRkey1, kValNameDouble, &double_val)); + + // set string + EXPECT_SUCCEEDED(RegKey::SetValue(kFullRkey1, kValNameStr, kStrVal)); + + // check that the value exists + EXPECT_TRUE(RegKey::HasValue(kFullRkey1, kValNameStr)); + + // read it back + EXPECT_SUCCEEDED(RegKey::GetValue(kFullRkey1, kValNameStr, &str_val)); + EXPECT_TRUE(lstrcmp(str_val, kStrVal) == 0); + delete[] str_val; + + // read it back in std::wstring + EXPECT_SUCCEEDED(RegKey::GetValue(kFullRkey1, kValNameStr, &wstr_val)); + EXPECT_STREQ(wstr_val.c_str(), kStrVal); + + // get an in-existent value from an existent key + EXPECT_EQ(RegKey::GetValue(kFullRkey1, L"bogus", &str_val), + HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + + // delete the value + EXPECT_SUCCEEDED(RegKey::DeleteValue(kFullRkey1, kValNameStr)); + + // check that the value is gone + EXPECT_FALSE(RegKey::HasValue(kFullRkey1, kValNameStr)); + + // set binary + EXPECT_SUCCEEDED(RegKey::SetValue(kFullRkey1, kValNameBinary, + reinterpret_cast(kBinaryVal), sizeof(kBinaryVal)-1)); + + // check that the value exists + EXPECT_TRUE(RegKey::HasValue(kFullRkey1, kValNameBinary)); + + // read it back + EXPECT_SUCCEEDED(RegKey::GetValue(kFullRkey1, kValNameBinary, + &binary_val, &uint8_count)); + EXPECT_TRUE(memcmp(binary_val, kBinaryVal, sizeof(kBinaryVal)-1) == 0); + delete[] binary_val; + + // delete the value + EXPECT_SUCCEEDED(RegKey::DeleteValue(kFullRkey1, kValNameBinary)); + + // check that the value is gone + EXPECT_FALSE(RegKey::HasValue(kFullRkey1, kValNameBinary)); + + // special case - set a binary value with length 0 + EXPECT_SUCCEEDED(RegKey::SetValue(kFullRkey1, kValNameBinary, + reinterpret_cast(kBinaryVal), 0)); + + // check that the value exists + EXPECT_TRUE(RegKey::HasValue(kFullRkey1, kValNameBinary)); + + // read it back + EXPECT_SUCCEEDED(RegKey::GetValue(kFullRkey1, kValNameBinary, + &binary_val, &uint8_count)); + EXPECT_EQ(uint8_count, 0); + EXPECT_TRUE(binary_val == NULL); + delete[] binary_val; + + // delete the value + EXPECT_SUCCEEDED(RegKey::DeleteValue(kFullRkey1, kValNameBinary)); + + // check that the value is gone + EXPECT_FALSE(RegKey::HasValue(kFullRkey1, kValNameBinary)); + + // special case - set a NULL binary value + EXPECT_SUCCEEDED(RegKey::SetValue(kFullRkey1, kValNameBinary, NULL, 100)); + + // check that the value exists + EXPECT_TRUE(RegKey::HasValue(kFullRkey1, kValNameBinary)); + + // read it back + EXPECT_SUCCEEDED(RegKey::GetValue(kFullRkey1, kValNameBinary, + &binary_val, &uint8_count)); + EXPECT_EQ(uint8_count, 0); + EXPECT_TRUE(binary_val == NULL); + delete[] binary_val; + + // delete the value + EXPECT_SUCCEEDED(RegKey::DeleteValue(kFullRkey1, kValNameBinary)); + + // check that the value is gone + EXPECT_FALSE(RegKey::HasValue(kFullRkey1, kValNameBinary)); + + // test read/write REG_MULTI_SZ value + std::vector result; + EXPECT_SUCCEEDED(RegKey::SetValueMultiSZ(kFullRkey1, kValNameMultiStr, + reinterpret_cast(kMultiSZ), sizeof(kMultiSZ))); + EXPECT_SUCCEEDED(RegKey::GetValue(kFullRkey1, kValNameMultiStr, &result)); + EXPECT_EQ(result.size(), 3); + EXPECT_STREQ(result[0].c_str(), L"abc"); + EXPECT_STREQ(result[1].c_str(), L"def"); + EXPECT_STREQ(result[2].c_str(), L"P12345"); + EXPECT_SUCCEEDED(RegKey::SetValueMultiSZ(kFullRkey1, kValNameMultiStr, + reinterpret_cast(kEmptyMultiSZ), sizeof(kEmptyMultiSZ))); + EXPECT_SUCCEEDED(RegKey::GetValue(kFullRkey1, kValNameMultiStr, &result)); + EXPECT_EQ(result.size(), 0); + // writing REG_MULTI_SZ value will automatically add ending null characters + EXPECT_SUCCEEDED(RegKey::SetValueMultiSZ(kFullRkey1, kValNameMultiStr, + reinterpret_cast(kInvalidMultiSZ), sizeof(kInvalidMultiSZ))); + EXPECT_SUCCEEDED(RegKey::GetValue(kFullRkey1, kValNameMultiStr, &result)); + EXPECT_EQ(result.size(), 1); + EXPECT_STREQ(result[0].c_str(), L"678"); + + // Run the following test only in dev machine + // This is because the build machine might not have admin privilege +#ifdef IS_PRIVATE_BUILD + // get a temp file name + wchar_t temp_path[MAX_PATH] = {0}; + EXPECT_LT(::GetTempPath(ARRAY_SIZE(temp_path), temp_path), + static_cast(ARRAY_SIZE(temp_path))); + wchar_t temp_file[MAX_PATH] = {0}; + EXPECT_NE(::GetTempFileName(temp_path, L"rkut_", + ::GetTickCount(), temp_file), 0); + + // test save + EXPECT_SUCCEEDED(RegKey::SetValue(kFullRkey1Subkey, kValNameInt, kIntVal)); + EXPECT_SUCCEEDED(RegKey::SetValue(kFullRkey1Subkey, kValNameInt64, kIntVal64)); + EXPECT_SUCCEEDED(RegKey::Save(kFullRkey1Subkey, temp_file)); + EXPECT_SUCCEEDED(RegKey::DeleteValue(kFullRkey1Subkey, kValNameInt)); + EXPECT_SUCCEEDED(RegKey::DeleteValue(kFullRkey1Subkey, kValNameInt64)); + + // test restore + EXPECT_SUCCEEDED(RegKey::Restore(kFullRkey1Subkey, temp_file)); + int_val = 0; + EXPECT_SUCCEEDED(RegKey::GetValue(kFullRkey1Subkey, kValNameInt, &int_val)); + EXPECT_EQ(int_val, kIntVal); + int64_val = 0; + EXPECT_SUCCEEDED(RegKey::GetValue(kFullRkey1Subkey, + kValNameInt64, + &int64_val)); + EXPECT_EQ(int64_val, kIntVal64); + + // delete the temp file + EXPECT_EQ(TRUE, ::DeleteFile(temp_file)); +#endif + + // whack the whole key + EXPECT_SUCCEEDED(RegKey::DeleteKey(kFullRkey1)); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32securityerrors.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32securityerrors.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32securityerrors.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32securityerrors.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,49 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/win32.h" +#include "webrtc/base/logging.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// + +extern const ConstantLabel SECURITY_ERRORS[]; + +const ConstantLabel SECURITY_ERRORS[] = { + KLABEL(SEC_I_COMPLETE_AND_CONTINUE), + KLABEL(SEC_I_COMPLETE_NEEDED), + KLABEL(SEC_I_CONTEXT_EXPIRED), + KLABEL(SEC_I_CONTINUE_NEEDED), + KLABEL(SEC_I_INCOMPLETE_CREDENTIALS), + KLABEL(SEC_I_RENEGOTIATE), + KLABEL(SEC_E_CERT_EXPIRED), + KLABEL(SEC_E_INCOMPLETE_MESSAGE), + KLABEL(SEC_E_INSUFFICIENT_MEMORY), + KLABEL(SEC_E_INTERNAL_ERROR), + KLABEL(SEC_E_INVALID_HANDLE), + KLABEL(SEC_E_INVALID_TOKEN), + KLABEL(SEC_E_LOGON_DENIED), + KLABEL(SEC_E_NO_AUTHENTICATING_AUTHORITY), + KLABEL(SEC_E_NO_CREDENTIALS), + KLABEL(SEC_E_NOT_OWNER), + KLABEL(SEC_E_OK), + KLABEL(SEC_E_SECPKG_NOT_FOUND), + KLABEL(SEC_E_TARGET_UNKNOWN), + KLABEL(SEC_E_UNKNOWN_CREDENTIALS), + KLABEL(SEC_E_UNSUPPORTED_FUNCTION), + KLABEL(SEC_E_UNTRUSTED_ROOT), + KLABEL(SEC_E_WRONG_PRINCIPAL), + LASTLABEL +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32socketinit.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32socketinit.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32socketinit.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32socketinit.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/win32socketinit.h" + +#include "webrtc/base/win32.h" + +namespace rtc { + +// Please don't remove this function. +void EnsureWinsockInit() { + // The default implementation uses a global initializer, so WSAStartup + // happens at module load time. Thus we don't need to do anything here. + // The hook is provided so that a client that statically links with + // libjingle can override it, to provide its own initialization. +} + +#if defined(WEBRTC_WIN) +class WinsockInitializer { + public: + WinsockInitializer() { + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(1, 0); + err_ = WSAStartup(wVersionRequested, &wsaData); + } + ~WinsockInitializer() { + if (!err_) + WSACleanup(); + } + int error() { + return err_; + } + private: + int err_; +}; +WinsockInitializer g_winsockinit; +#endif + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32socketinit.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32socketinit.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32socketinit.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32socketinit.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_WIN32SOCKETINIT_H_ +#define WEBRTC_BASE_WIN32SOCKETINIT_H_ + +namespace rtc { + +void EnsureWinsockInit(); + +} // namespace rtc + +#endif // WEBRTC_BASE_WIN32SOCKETINIT_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32socketserver.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32socketserver.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32socketserver.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32socketserver.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,850 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/win32socketserver.h" +#include "webrtc/base/byteorder.h" +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/winping.h" +#include "webrtc/base/win32window.h" +#include // NOLINT + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// Win32Socket +/////////////////////////////////////////////////////////////////////////////// + +// TODO: Move this to a common place where PhysicalSocketServer can +// share it. +// Standard MTUs +static const uint16 PACKET_MAXIMUMS[] = { + 65535, // Theoretical maximum, Hyperchannel + 32000, // Nothing + 17914, // 16Mb IBM Token Ring + 8166, // IEEE 802.4 + // 4464 // IEEE 802.5 (4Mb max) + 4352, // FDDI + // 2048, // Wideband Network + 2002, // IEEE 802.5 (4Mb recommended) + // 1536, // Expermental Ethernet Networks + // 1500, // Ethernet, Point-to-Point (default) + 1492, // IEEE 802.3 + 1006, // SLIP, ARPANET + // 576, // X.25 Networks + // 544, // DEC IP Portal + // 512, // NETBIOS + 508, // IEEE 802/Source-Rt Bridge, ARCNET + 296, // Point-to-Point (low delay) + 68, // Official minimum + 0, // End of list marker +}; + +static const int IP_HEADER_SIZE = 20u; +static const int ICMP_HEADER_SIZE = 8u; +static const int ICMP_PING_TIMEOUT_MILLIS = 10000u; + +// TODO: Enable for production builds also? Use FormatMessage? +#ifdef _DEBUG +LPCSTR WSAErrorToString(int error, LPCSTR *description_result) { + LPCSTR string = "Unspecified"; + LPCSTR description = "Unspecified description"; + switch (error) { + case ERROR_SUCCESS: + string = "SUCCESS"; + description = "Operation succeeded"; + break; + case WSAEWOULDBLOCK: + string = "WSAEWOULDBLOCK"; + description = "Using a non-blocking socket, will notify later"; + break; + case WSAEACCES: + string = "WSAEACCES"; + description = "Access denied, or sharing violation"; + break; + case WSAEADDRNOTAVAIL: + string = "WSAEADDRNOTAVAIL"; + description = "Address is not valid in this context"; + break; + case WSAENETDOWN: + string = "WSAENETDOWN"; + description = "Network is down"; + break; + case WSAENETUNREACH: + string = "WSAENETUNREACH"; + description = "Network is up, but unreachable"; + break; + case WSAENETRESET: + string = "WSANETRESET"; + description = "Connection has been reset due to keep-alive activity"; + break; + case WSAECONNABORTED: + string = "WSAECONNABORTED"; + description = "Aborted by host"; + break; + case WSAECONNRESET: + string = "WSAECONNRESET"; + description = "Connection reset by host"; + break; + case WSAETIMEDOUT: + string = "WSAETIMEDOUT"; + description = "Timed out, host failed to respond"; + break; + case WSAECONNREFUSED: + string = "WSAECONNREFUSED"; + description = "Host actively refused connection"; + break; + case WSAEHOSTDOWN: + string = "WSAEHOSTDOWN"; + description = "Host is down"; + break; + case WSAEHOSTUNREACH: + string = "WSAEHOSTUNREACH"; + description = "Host is unreachable"; + break; + case WSAHOST_NOT_FOUND: + string = "WSAHOST_NOT_FOUND"; + description = "No such host is known"; + break; + } + if (description_result) { + *description_result = description; + } + return string; +} + +void ReportWSAError(LPCSTR context, int error, const SocketAddress& address) { + LPCSTR description_string; + LPCSTR error_string = WSAErrorToString(error, &description_string); + LOG(LS_INFO) << context << " = " << error + << " (" << error_string << ":" << description_string << ") [" + << address.ToString() << "]"; +} +#else +void ReportWSAError(LPCSTR context, int error, const SocketAddress& address) {} +#endif + +///////////////////////////////////////////////////////////////////////////// +// Win32Socket::EventSink +///////////////////////////////////////////////////////////////////////////// + +#define WM_SOCKETNOTIFY (WM_USER + 50) +#define WM_DNSNOTIFY (WM_USER + 51) + +struct Win32Socket::DnsLookup { + HANDLE handle; + uint16 port; + char buffer[MAXGETHOSTSTRUCT]; +}; + +class Win32Socket::EventSink : public Win32Window { + public: + explicit EventSink(Win32Socket * parent) : parent_(parent) { } + + void Dispose(); + + virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, + LRESULT& result); + virtual void OnNcDestroy(); + + private: + bool OnSocketNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result); + bool OnDnsNotify(WPARAM wParam, LPARAM lParam, LRESULT& result); + + Win32Socket * parent_; +}; + +void Win32Socket::EventSink::Dispose() { + parent_ = NULL; + if (::IsWindow(handle())) { + ::DestroyWindow(handle()); + } else { + delete this; + } +} + +bool Win32Socket::EventSink::OnMessage(UINT uMsg, WPARAM wParam, + LPARAM lParam, LRESULT& result) { + switch (uMsg) { + case WM_SOCKETNOTIFY: + case WM_TIMER: + return OnSocketNotify(uMsg, wParam, lParam, result); + case WM_DNSNOTIFY: + return OnDnsNotify(wParam, lParam, result); + } + return false; +} + +bool Win32Socket::EventSink::OnSocketNotify(UINT uMsg, WPARAM wParam, + LPARAM lParam, LRESULT& result) { + result = 0; + + int wsa_event = WSAGETSELECTEVENT(lParam); + int wsa_error = WSAGETSELECTERROR(lParam); + + // Treat connect timeouts as close notifications + if (uMsg == WM_TIMER) { + wsa_event = FD_CLOSE; + wsa_error = WSAETIMEDOUT; + } + + if (parent_) + parent_->OnSocketNotify(static_cast(wParam), wsa_event, wsa_error); + return true; +} + +bool Win32Socket::EventSink::OnDnsNotify(WPARAM wParam, LPARAM lParam, + LRESULT& result) { + result = 0; + + int error = WSAGETASYNCERROR(lParam); + if (parent_) + parent_->OnDnsNotify(reinterpret_cast(wParam), error); + return true; +} + +void Win32Socket::EventSink::OnNcDestroy() { + if (parent_) { + LOG(LS_ERROR) << "EventSink hwnd is being destroyed, but the event sink" + " hasn't yet been disposed."; + } else { + delete this; + } +} + +///////////////////////////////////////////////////////////////////////////// +// Win32Socket +///////////////////////////////////////////////////////////////////////////// + +Win32Socket::Win32Socket() + : socket_(INVALID_SOCKET), error_(0), state_(CS_CLOSED), connect_time_(0), + closing_(false), close_error_(0), sink_(NULL), dns_(NULL) { +} + +Win32Socket::~Win32Socket() { + Close(); +} + +bool Win32Socket::CreateT(int family, int type) { + Close(); + int proto = (SOCK_DGRAM == type) ? IPPROTO_UDP : IPPROTO_TCP; + socket_ = ::WSASocket(family, type, proto, NULL, NULL, 0); + if (socket_ == INVALID_SOCKET) { + UpdateLastError(); + return false; + } + if ((SOCK_DGRAM == type) && !SetAsync(FD_READ | FD_WRITE)) { + return false; + } + return true; +} + +int Win32Socket::Attach(SOCKET s) { + ASSERT(socket_ == INVALID_SOCKET); + if (socket_ != INVALID_SOCKET) + return SOCKET_ERROR; + + ASSERT(s != INVALID_SOCKET); + if (s == INVALID_SOCKET) + return SOCKET_ERROR; + + socket_ = s; + state_ = CS_CONNECTED; + + if (!SetAsync(FD_READ | FD_WRITE | FD_CLOSE)) + return SOCKET_ERROR; + + return 0; +} + +void Win32Socket::SetTimeout(int ms) { + if (sink_) + ::SetTimer(sink_->handle(), 1, ms, 0); +} + +SocketAddress Win32Socket::GetLocalAddress() const { + sockaddr_storage addr = {0}; + socklen_t addrlen = sizeof(addr); + int result = ::getsockname(socket_, reinterpret_cast(&addr), + &addrlen); + SocketAddress address; + if (result >= 0) { + SocketAddressFromSockAddrStorage(addr, &address); + } else { + LOG(LS_WARNING) << "GetLocalAddress: unable to get local addr, socket=" + << socket_; + } + return address; +} + +SocketAddress Win32Socket::GetRemoteAddress() const { + sockaddr_storage addr = {0}; + socklen_t addrlen = sizeof(addr); + int result = ::getpeername(socket_, reinterpret_cast(&addr), + &addrlen); + SocketAddress address; + if (result >= 0) { + SocketAddressFromSockAddrStorage(addr, &address); + } else { + LOG(LS_WARNING) << "GetRemoteAddress: unable to get remote addr, socket=" + << socket_; + } + return address; +} + +int Win32Socket::Bind(const SocketAddress& addr) { + ASSERT(socket_ != INVALID_SOCKET); + if (socket_ == INVALID_SOCKET) + return SOCKET_ERROR; + + sockaddr_storage saddr; + size_t len = addr.ToSockAddrStorage(&saddr); + int err = ::bind(socket_, + reinterpret_cast(&saddr), + static_cast(len)); + UpdateLastError(); + return err; +} + +int Win32Socket::Connect(const SocketAddress& addr) { + if (state_ != CS_CLOSED) { + SetError(EALREADY); + return SOCKET_ERROR; + } + + if (!addr.IsUnresolvedIP()) { + return DoConnect(addr); + } + + LOG_F(LS_INFO) << "async dns lookup (" << addr.hostname() << ")"; + DnsLookup * dns = new DnsLookup; + if (!sink_) { + // Explicitly create the sink ourselves here; we can't rely on SetAsync + // because we don't have a socket_ yet. + CreateSink(); + } + // TODO: Replace with IPv6 compatible lookup. + dns->handle = WSAAsyncGetHostByName(sink_->handle(), WM_DNSNOTIFY, + addr.hostname().c_str(), dns->buffer, + sizeof(dns->buffer)); + + if (!dns->handle) { + LOG_F(LS_ERROR) << "WSAAsyncGetHostByName error: " << WSAGetLastError(); + delete dns; + UpdateLastError(); + Close(); + return SOCKET_ERROR; + } + + dns->port = addr.port(); + dns_ = dns; + state_ = CS_CONNECTING; + return 0; +} + +int Win32Socket::DoConnect(const SocketAddress& addr) { + if ((socket_ == INVALID_SOCKET) && !CreateT(addr.family(), SOCK_STREAM)) { + return SOCKET_ERROR; + } + if (!SetAsync(FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE)) { + return SOCKET_ERROR; + } + + sockaddr_storage saddr = {0}; + size_t len = addr.ToSockAddrStorage(&saddr); + connect_time_ = Time(); + int result = connect(socket_, + reinterpret_cast(&saddr), + static_cast(len)); + if (result != SOCKET_ERROR) { + state_ = CS_CONNECTED; + } else { + int code = WSAGetLastError(); + if (code == WSAEWOULDBLOCK) { + state_ = CS_CONNECTING; + } else { + ReportWSAError("WSAAsync:connect", code, addr); + error_ = code; + Close(); + return SOCKET_ERROR; + } + } + addr_ = addr; + + return 0; +} + +int Win32Socket::GetError() const { + return error_; +} + +void Win32Socket::SetError(int error) { + error_ = error; +} + +Socket::ConnState Win32Socket::GetState() const { + return state_; +} + +int Win32Socket::GetOption(Option opt, int* value) { + int slevel; + int sopt; + if (TranslateOption(opt, &slevel, &sopt) == -1) + return -1; + + char* p = reinterpret_cast(value); + int optlen = sizeof(value); + return ::getsockopt(socket_, slevel, sopt, p, &optlen); +} + +int Win32Socket::SetOption(Option opt, int value) { + int slevel; + int sopt; + if (TranslateOption(opt, &slevel, &sopt) == -1) + return -1; + + const char* p = reinterpret_cast(&value); + return ::setsockopt(socket_, slevel, sopt, p, sizeof(value)); +} + +int Win32Socket::Send(const void* buffer, size_t length) { + int sent = ::send(socket_, + reinterpret_cast(buffer), + static_cast(length), + 0); + UpdateLastError(); + return sent; +} + +int Win32Socket::SendTo(const void* buffer, size_t length, + const SocketAddress& addr) { + sockaddr_storage saddr; + size_t addr_len = addr.ToSockAddrStorage(&saddr); + int sent = ::sendto(socket_, reinterpret_cast(buffer), + static_cast(length), 0, + reinterpret_cast(&saddr), + static_cast(addr_len)); + UpdateLastError(); + return sent; +} + +int Win32Socket::Recv(void* buffer, size_t length) { + int received = ::recv(socket_, static_cast(buffer), + static_cast(length), 0); + UpdateLastError(); + if (closing_ && received <= static_cast(length)) + PostClosed(); + return received; +} + +int Win32Socket::RecvFrom(void* buffer, size_t length, + SocketAddress* out_addr) { + sockaddr_storage saddr; + socklen_t addr_len = sizeof(saddr); + int received = ::recvfrom(socket_, static_cast(buffer), + static_cast(length), 0, + reinterpret_cast(&saddr), &addr_len); + UpdateLastError(); + if (received != SOCKET_ERROR) + SocketAddressFromSockAddrStorage(saddr, out_addr); + if (closing_ && received <= static_cast(length)) + PostClosed(); + return received; +} + +int Win32Socket::Listen(int backlog) { + int err = ::listen(socket_, backlog); + if (!SetAsync(FD_ACCEPT)) + return SOCKET_ERROR; + + UpdateLastError(); + if (err == 0) + state_ = CS_CONNECTING; + return err; +} + +Win32Socket* Win32Socket::Accept(SocketAddress* out_addr) { + sockaddr_storage saddr; + socklen_t addr_len = sizeof(saddr); + SOCKET s = ::accept(socket_, reinterpret_cast(&saddr), &addr_len); + UpdateLastError(); + if (s == INVALID_SOCKET) + return NULL; + if (out_addr) + SocketAddressFromSockAddrStorage(saddr, out_addr); + Win32Socket* socket = new Win32Socket; + if (0 == socket->Attach(s)) + return socket; + delete socket; + return NULL; +} + +int Win32Socket::Close() { + int err = 0; + if (socket_ != INVALID_SOCKET) { + err = ::closesocket(socket_); + socket_ = INVALID_SOCKET; + closing_ = false; + close_error_ = 0; + UpdateLastError(); + } + if (dns_) { + WSACancelAsyncRequest(dns_->handle); + delete dns_; + dns_ = NULL; + } + if (sink_) { + sink_->Dispose(); + sink_ = NULL; + } + addr_.Clear(); + state_ = CS_CLOSED; + return err; +} + +int Win32Socket::EstimateMTU(uint16* mtu) { + SocketAddress addr = GetRemoteAddress(); + if (addr.IsAny()) { + error_ = ENOTCONN; + return -1; + } + + WinPing ping; + if (!ping.IsValid()) { + error_ = EINVAL; // can't think of a better error ID + return -1; + } + + for (int level = 0; PACKET_MAXIMUMS[level + 1] > 0; ++level) { + int32 size = PACKET_MAXIMUMS[level] - IP_HEADER_SIZE - ICMP_HEADER_SIZE; + WinPing::PingResult result = ping.Ping(addr.ipaddr(), size, + ICMP_PING_TIMEOUT_MILLIS, 1, false); + if (result == WinPing::PING_FAIL) { + error_ = EINVAL; // can't think of a better error ID + return -1; + } + if (result != WinPing::PING_TOO_LARGE) { + *mtu = PACKET_MAXIMUMS[level]; + return 0; + } + } + + ASSERT(false); + return 0; +} + +void Win32Socket::CreateSink() { + ASSERT(NULL == sink_); + + // Create window + sink_ = new EventSink(this); + sink_->Create(NULL, L"EventSink", 0, 0, 0, 0, 10, 10); +} + +bool Win32Socket::SetAsync(int events) { + if (NULL == sink_) { + CreateSink(); + ASSERT(NULL != sink_); + } + + // start the async select + if (WSAAsyncSelect(socket_, sink_->handle(), WM_SOCKETNOTIFY, events) + == SOCKET_ERROR) { + UpdateLastError(); + Close(); + return false; + } + + return true; +} + +bool Win32Socket::HandleClosed(int close_error) { + // WM_CLOSE will be received before all data has been read, so we need to + // hold on to it until the read buffer has been drained. + char ch; + closing_ = true; + close_error_ = close_error; + return (::recv(socket_, &ch, 1, MSG_PEEK) <= 0); +} + +void Win32Socket::PostClosed() { + // If we see that the buffer is indeed drained, then send the close. + closing_ = false; + ::PostMessage(sink_->handle(), WM_SOCKETNOTIFY, + socket_, WSAMAKESELECTREPLY(FD_CLOSE, close_error_)); +} + +void Win32Socket::UpdateLastError() { + error_ = WSAGetLastError(); +} + +int Win32Socket::TranslateOption(Option opt, int* slevel, int* sopt) { + switch (opt) { + case OPT_DONTFRAGMENT: + *slevel = IPPROTO_IP; + *sopt = IP_DONTFRAGMENT; + break; + case OPT_RCVBUF: + *slevel = SOL_SOCKET; + *sopt = SO_RCVBUF; + break; + case OPT_SNDBUF: + *slevel = SOL_SOCKET; + *sopt = SO_SNDBUF; + break; + case OPT_NODELAY: + *slevel = IPPROTO_TCP; + *sopt = TCP_NODELAY; + break; + case OPT_DSCP: + LOG(LS_WARNING) << "Socket::OPT_DSCP not supported."; + return -1; + default: + ASSERT(false); + return -1; + } + return 0; +} + +void Win32Socket::OnSocketNotify(SOCKET socket, int event, int error) { + // Ignore events if we're already closed. + if (socket != socket_) + return; + + error_ = error; + switch (event) { + case FD_CONNECT: + if (error != ERROR_SUCCESS) { + ReportWSAError("WSAAsync:connect notify", error, addr_); +#ifdef _DEBUG + int32 duration = TimeSince(connect_time_); + LOG(LS_INFO) << "WSAAsync:connect error (" << duration + << " ms), faking close"; +#endif + state_ = CS_CLOSED; + // If you get an error connecting, close doesn't really do anything + // and it certainly doesn't send back any close notification, but + // we really only maintain a few states, so it is easiest to get + // back into a known state by pretending that a close happened, even + // though the connect event never did occur. + SignalCloseEvent(this, error); + } else { +#ifdef _DEBUG + int32 duration = TimeSince(connect_time_); + LOG(LS_INFO) << "WSAAsync:connect (" << duration << " ms)"; +#endif + state_ = CS_CONNECTED; + SignalConnectEvent(this); + } + break; + + case FD_ACCEPT: + case FD_READ: + if (error != ERROR_SUCCESS) { + ReportWSAError("WSAAsync:read notify", error, addr_); + } else { + SignalReadEvent(this); + } + break; + + case FD_WRITE: + if (error != ERROR_SUCCESS) { + ReportWSAError("WSAAsync:write notify", error, addr_); + } else { + SignalWriteEvent(this); + } + break; + + case FD_CLOSE: + if (HandleClosed(error)) { + ReportWSAError("WSAAsync:close notify", error, addr_); + state_ = CS_CLOSED; + SignalCloseEvent(this, error); + } + break; + } +} + +void Win32Socket::OnDnsNotify(HANDLE task, int error) { + if (!dns_ || dns_->handle != task) + return; + + uint32 ip = 0; + if (error == 0) { + hostent* pHost = reinterpret_cast(dns_->buffer); + uint32 net_ip = *reinterpret_cast(pHost->h_addr_list[0]); + ip = NetworkToHost32(net_ip); + } + + LOG_F(LS_INFO) << "(" << IPAddress(ip).ToSensitiveString() + << ", " << error << ")"; + + if (error == 0) { + SocketAddress address(ip, dns_->port); + error = DoConnect(address); + } else { + Close(); + } + + if (error) { + error_ = error; + SignalCloseEvent(this, error_); + } else { + delete dns_; + dns_ = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Win32SocketServer +// Provides cricket base services on top of a win32 gui thread +/////////////////////////////////////////////////////////////////////////////// + +static UINT s_wm_wakeup_id = 0; +const TCHAR Win32SocketServer::kWindowName[] = L"libjingle Message Window"; + +Win32SocketServer::Win32SocketServer(MessageQueue* message_queue) + : message_queue_(message_queue), + wnd_(this), + posted_(false), + hdlg_(NULL) { + if (s_wm_wakeup_id == 0) + s_wm_wakeup_id = RegisterWindowMessage(L"WM_WAKEUP"); + if (!wnd_.Create(NULL, kWindowName, 0, 0, 0, 0, 0, 0)) { + LOG_GLE(LS_ERROR) << "Failed to create message window."; + } +} + +Win32SocketServer::~Win32SocketServer() { + if (wnd_.handle() != NULL) { + KillTimer(wnd_.handle(), 1); + wnd_.Destroy(); + } +} + +Socket* Win32SocketServer::CreateSocket(int type) { + return CreateSocket(AF_INET, type); +} + +Socket* Win32SocketServer::CreateSocket(int family, int type) { + return CreateAsyncSocket(family, type); +} + +AsyncSocket* Win32SocketServer::CreateAsyncSocket(int type) { + return CreateAsyncSocket(AF_INET, type); +} + +AsyncSocket* Win32SocketServer::CreateAsyncSocket(int family, int type) { + Win32Socket* socket = new Win32Socket; + if (socket->CreateT(family, type)) { + return socket; + } + delete socket; + return NULL; +} + +void Win32SocketServer::SetMessageQueue(MessageQueue* queue) { + message_queue_ = queue; +} + +bool Win32SocketServer::Wait(int cms, bool process_io) { + BOOL b; + if (process_io) { + // Spin the Win32 message pump at least once, and as long as requested. + // This is the Thread::ProcessMessages case. + uint32 start = Time(); + do { + MSG msg; + SetTimer(wnd_.handle(), 0, cms, NULL); + // Get the next available message. If we have a modeless dialog, give + // give the message to IsDialogMessage, which will return true if it + // was a message for the dialog that it handled internally. + // Otherwise, dispatch as usual via Translate/DispatchMessage. + b = GetMessage(&msg, NULL, 0, 0); + if (b == -1) { + LOG_GLE(LS_ERROR) << "GetMessage failed."; + return false; + } else if(b) { + if (!hdlg_ || !IsDialogMessage(hdlg_, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + KillTimer(wnd_.handle(), 0); + } while (b && TimeSince(start) < cms); + } else if (cms != 0) { + // Sit and wait forever for a WakeUp. This is the Thread::Send case. + ASSERT(cms == -1); + MSG msg; + b = GetMessage(&msg, NULL, s_wm_wakeup_id, s_wm_wakeup_id); + { + CritScope scope(&cs_); + posted_ = false; + } + } else { + // No-op (cms == 0 && !process_io). This is the Pump case. + b = TRUE; + } + return (b != FALSE); +} + +void Win32SocketServer::WakeUp() { + if (wnd_.handle()) { + // Set the "message pending" flag, if not already set. + { + CritScope scope(&cs_); + if (posted_) + return; + posted_ = true; + } + + PostMessage(wnd_.handle(), s_wm_wakeup_id, 0, 0); + } +} + +void Win32SocketServer::Pump() { + // Clear the "message pending" flag. + { + CritScope scope(&cs_); + posted_ = false; + } + + // Dispatch all the messages that are currently in our queue. If new messages + // are posted during the dispatch, they will be handled in the next Pump. + // We use max(1, ...) to make sure we try to dispatch at least once, since + // this allow us to process "sent" messages, not included in the size() count. + Message msg; + for (size_t max_messages_to_process = _max(1, message_queue_->size()); + max_messages_to_process > 0 && message_queue_->Get(&msg, 0, false); + --max_messages_to_process) { + message_queue_->Dispatch(&msg); + } + + // Anything remaining? + int delay = message_queue_->GetDelay(); + if (delay == -1) { + KillTimer(wnd_.handle(), 1); + } else { + SetTimer(wnd_.handle(), 1, delay, NULL); + } +} + +bool Win32SocketServer::MessageWindow::OnMessage(UINT wm, WPARAM wp, + LPARAM lp, LRESULT& lr) { + bool handled = false; + if (wm == s_wm_wakeup_id || (wm == WM_TIMER && wp == 1)) { + ss_->Pump(); + lr = 0; + handled = true; + } + return handled; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32socketserver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32socketserver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32socketserver.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32socketserver.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,164 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_WIN32SOCKETSERVER_H_ +#define WEBRTC_BASE_WIN32SOCKETSERVER_H_ + +#if defined(WEBRTC_WIN) +#include "webrtc/base/asyncsocket.h" +#include "webrtc/base/criticalsection.h" +#include "webrtc/base/messagequeue.h" +#include "webrtc/base/socketserver.h" +#include "webrtc/base/socketfactory.h" +#include "webrtc/base/socket.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/win32window.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// Win32Socket +/////////////////////////////////////////////////////////////////////////////// + +class Win32Socket : public AsyncSocket { + public: + Win32Socket(); + virtual ~Win32Socket(); + + bool CreateT(int family, int type); + + int Attach(SOCKET s); + void SetTimeout(int ms); + + // AsyncSocket Interface + virtual SocketAddress GetLocalAddress() const; + virtual SocketAddress GetRemoteAddress() const; + virtual int Bind(const SocketAddress& addr); + virtual int Connect(const SocketAddress& addr); + virtual int Send(const void *buffer, size_t length); + virtual int SendTo(const void *buffer, size_t length, const SocketAddress& addr); + virtual int Recv(void *buffer, size_t length); + virtual int RecvFrom(void *buffer, size_t length, SocketAddress *out_addr); + virtual int Listen(int backlog); + virtual Win32Socket *Accept(SocketAddress *out_addr); + virtual int Close(); + virtual int GetError() const; + virtual void SetError(int error); + virtual ConnState GetState() const; + virtual int EstimateMTU(uint16* mtu); + virtual int GetOption(Option opt, int* value); + virtual int SetOption(Option opt, int value); + + private: + void CreateSink(); + bool SetAsync(int events); + int DoConnect(const SocketAddress& addr); + bool HandleClosed(int close_error); + void PostClosed(); + void UpdateLastError(); + static int TranslateOption(Option opt, int* slevel, int* sopt); + + void OnSocketNotify(SOCKET socket, int event, int error); + void OnDnsNotify(HANDLE task, int error); + + SOCKET socket_; + int error_; + ConnState state_; + SocketAddress addr_; // address that we connected to (see DoConnect) + uint32 connect_time_; + bool closing_; + int close_error_; + + class EventSink; + friend class EventSink; + EventSink * sink_; + + struct DnsLookup; + DnsLookup * dns_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Win32SocketServer +/////////////////////////////////////////////////////////////////////////////// + +class Win32SocketServer : public SocketServer { + public: + explicit Win32SocketServer(MessageQueue* message_queue); + virtual ~Win32SocketServer(); + + void set_modeless_dialog(HWND hdlg) { + hdlg_ = hdlg; + } + + // SocketServer Interface + virtual Socket* CreateSocket(int type); + virtual Socket* CreateSocket(int family, int type); + + virtual AsyncSocket* CreateAsyncSocket(int type); + virtual AsyncSocket* CreateAsyncSocket(int family, int type); + + virtual void SetMessageQueue(MessageQueue* queue); + virtual bool Wait(int cms, bool process_io); + virtual void WakeUp(); + + void Pump(); + + HWND handle() { return wnd_.handle(); } + + private: + class MessageWindow : public Win32Window { + public: + explicit MessageWindow(Win32SocketServer* ss) : ss_(ss) {} + private: + virtual bool OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT& result); + Win32SocketServer* ss_; + }; + + static const TCHAR kWindowName[]; + MessageQueue *message_queue_; + MessageWindow wnd_; + CriticalSection cs_; + bool posted_; + HWND hdlg_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Win32Thread. Automatically pumps Windows messages. +/////////////////////////////////////////////////////////////////////////////// + +class Win32Thread : public Thread { + public: + Win32Thread() : ss_(this), id_(0) { + set_socketserver(&ss_); + } + virtual ~Win32Thread() { + Stop(); + set_socketserver(NULL); + } + virtual void Run() { + id_ = GetCurrentThreadId(); + Thread::Run(); + id_ = 0; + } + virtual void Quit() { + PostThreadMessage(id_, WM_QUIT, 0, 0); + } + private: + Win32SocketServer ss_; + DWORD id_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_WIN + +#endif // WEBRTC_BASE_WIN32SOCKETSERVER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32socketserver_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32socketserver_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32socketserver_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32socketserver_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,157 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "webrtc/base/gunit.h" +#include "webrtc/base/socket_unittest.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/win32socketserver.h" + +namespace rtc { + +// Test that Win32SocketServer::Wait works as expected. +TEST(Win32SocketServerTest, TestWait) { + Win32SocketServer server(NULL); + uint32 start = Time(); + server.Wait(1000, true); + EXPECT_GE(TimeSince(start), 1000); +} + +// Test that Win32Socket::Pump does not touch general Windows messages. +TEST(Win32SocketServerTest, TestPump) { + Win32SocketServer server(NULL); + SocketServerScope scope(&server); + EXPECT_EQ(TRUE, PostMessage(NULL, WM_USER, 999, 0)); + server.Pump(); + MSG msg; + EXPECT_EQ(TRUE, PeekMessage(&msg, NULL, WM_USER, 0, PM_REMOVE)); + EXPECT_EQ(WM_USER, msg.message); + EXPECT_EQ(999, msg.wParam); +} + +// Test that Win32Socket passes all the generic Socket tests. +class Win32SocketTest : public SocketTest { + protected: + Win32SocketTest() : server_(NULL), scope_(&server_) {} + Win32SocketServer server_; + SocketServerScope scope_; +}; + +TEST_F(Win32SocketTest, TestConnectIPv4) { + SocketTest::TestConnectIPv4(); +} + +TEST_F(Win32SocketTest, TestConnectIPv6) { + SocketTest::TestConnectIPv6(); +} + +TEST_F(Win32SocketTest, TestConnectWithDnsLookupIPv4) { + SocketTest::TestConnectWithDnsLookupIPv4(); +} + +TEST_F(Win32SocketTest, TestConnectWithDnsLookupIPv6) { + SocketTest::TestConnectWithDnsLookupIPv6(); +} + +TEST_F(Win32SocketTest, TestConnectFailIPv4) { + SocketTest::TestConnectFailIPv4(); +} + +TEST_F(Win32SocketTest, TestConnectFailIPv6) { + SocketTest::TestConnectFailIPv6(); +} + +TEST_F(Win32SocketTest, TestConnectWithDnsLookupFailIPv4) { + SocketTest::TestConnectWithDnsLookupFailIPv4(); +} + +TEST_F(Win32SocketTest, TestConnectWithDnsLookupFailIPv6) { + SocketTest::TestConnectWithDnsLookupFailIPv6(); +} + +TEST_F(Win32SocketTest, TestConnectWithClosedSocketIPv4) { + SocketTest::TestConnectWithClosedSocketIPv4(); +} + +TEST_F(Win32SocketTest, TestConnectWithClosedSocketIPv6) { + SocketTest::TestConnectWithClosedSocketIPv6(); +} + +TEST_F(Win32SocketTest, TestConnectWhileNotClosedIPv4) { + SocketTest::TestConnectWhileNotClosedIPv4(); +} + +TEST_F(Win32SocketTest, TestConnectWhileNotClosedIPv6) { + SocketTest::TestConnectWhileNotClosedIPv6(); +} + +TEST_F(Win32SocketTest, TestServerCloseDuringConnectIPv4) { + SocketTest::TestServerCloseDuringConnectIPv4(); +} + +TEST_F(Win32SocketTest, TestServerCloseDuringConnectIPv6) { + SocketTest::TestServerCloseDuringConnectIPv6(); +} + +TEST_F(Win32SocketTest, TestClientCloseDuringConnectIPv4) { + SocketTest::TestClientCloseDuringConnectIPv4(); +} + +TEST_F(Win32SocketTest, TestClientCloseDuringConnectIPv6) { + SocketTest::TestClientCloseDuringConnectIPv6(); +} + +TEST_F(Win32SocketTest, TestServerCloseIPv4) { + SocketTest::TestServerCloseIPv4(); +} + +TEST_F(Win32SocketTest, TestServerCloseIPv6) { + SocketTest::TestServerCloseIPv6(); +} + +TEST_F(Win32SocketTest, TestCloseInClosedCallbackIPv4) { + SocketTest::TestCloseInClosedCallbackIPv4(); +} + +TEST_F(Win32SocketTest, TestCloseInClosedCallbackIPv6) { + SocketTest::TestCloseInClosedCallbackIPv6(); +} + +TEST_F(Win32SocketTest, TestSocketServerWaitIPv4) { + SocketTest::TestSocketServerWaitIPv4(); +} + +TEST_F(Win32SocketTest, TestSocketServerWaitIPv6) { + SocketTest::TestSocketServerWaitIPv6(); +} + +TEST_F(Win32SocketTest, TestTcpIPv4) { + SocketTest::TestTcpIPv4(); +} + +TEST_F(Win32SocketTest, TestTcpIPv6) { + SocketTest::TestTcpIPv6(); +} + +TEST_F(Win32SocketTest, TestUdpIPv4) { + SocketTest::TestUdpIPv4(); +} + +TEST_F(Win32SocketTest, TestUdpIPv6) { + SocketTest::TestUdpIPv6(); +} + +TEST_F(Win32SocketTest, TestGetSetOptionsIPv4) { + SocketTest::TestGetSetOptionsIPv4(); +} + +TEST_F(Win32SocketTest, TestGetSetOptionsIPv6) { + SocketTest::TestGetSetOptionsIPv6(); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32toolhelp.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32toolhelp.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32toolhelp.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32toolhelp.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,172 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef WEBRTC_BASE_WIN32TOOLHELP_H_ +#define WEBRTC_BASE_WIN32TOOLHELP_H_ + +#if !defined(WEBRTC_WIN) +#error WEBRTC_WIN Only +#endif + +#include "webrtc/base/win32.h" + +// Should be included first, but that causes redefinitions. +#include + +#include "webrtc/base/constructormagic.h" + +namespace rtc { + +// The toolhelp api used to enumerate processes and their modules +// on Windows is very repetetive and clunky to use. This little +// template wraps it to make it a little more programmer friendly. +// +// Traits: Traits type that adapts the enumerator to the corresponding +// win32 toolhelp api. Each traits class need to: +// - define the type of the enumerated data as a public symbol Type +// +// - implement bool First(HANDLE, T*) normally calls a +// Xxxx32First method in the toolhelp API. Ex Process32First(...) +// +// - implement bool Next(HANDLE, T*) normally calls a +// Xxxx32Next method in the toolhelp API. Ex Process32Next(...) +// +// - implement bool CloseHandle(HANDLE) +// +template +class ToolhelpEnumeratorBase { + public: + ToolhelpEnumeratorBase(HANDLE snapshot) + : snapshot_(snapshot), broken_(false), first_(true) { + + // Clear out the Traits::Type structure instance. + Zero(¤t_); + } + + virtual ~ToolhelpEnumeratorBase() { + Close(); + } + + // Moves forward to the next object using the First and Next + // pointers. If either First or Next ever indicates an failure + // all subsequent calls to this method will fail; the enumerator + // object is considered broken. + bool Next() { + if (!Valid()) { + return false; + } + + // Move the iteration forward. + current_.dwSize = sizeof(typename Traits::Type); + bool incr_ok = false; + if (first_) { + incr_ok = Traits::First(snapshot_, ¤t_); + first_ = false; + } else { + incr_ok = Traits::Next(snapshot_, ¤t_); + } + + if (!incr_ok) { + Zero(¤t_); + broken_ = true; + } + + return incr_ok; + } + + const typename Traits::Type& current() const { + return current_; + } + + void Close() { + if (snapshot_ != INVALID_HANDLE_VALUE) { + Traits::CloseHandle(snapshot_); + snapshot_ = INVALID_HANDLE_VALUE; + } + } + + private: + // Checks the state of the snapshot handle. + bool Valid() { + return snapshot_ != INVALID_HANDLE_VALUE && !broken_; + } + + static void Zero(typename Traits::Type* buff) { + ZeroMemory(buff, sizeof(typename Traits::Type)); + } + + HANDLE snapshot_; + typename Traits::Type current_; + bool broken_; + bool first_; +}; + +class ToolhelpTraits { + public: + static HANDLE CreateSnapshot(uint32 flags, uint32 process_id) { + return CreateToolhelp32Snapshot(flags, process_id); + } + + static bool CloseHandle(HANDLE handle) { + return ::CloseHandle(handle) == TRUE; + } +}; + +class ToolhelpProcessTraits : public ToolhelpTraits { + public: + typedef PROCESSENTRY32 Type; + + static bool First(HANDLE handle, Type* t) { + return ::Process32First(handle, t) == TRUE; + } + + static bool Next(HANDLE handle, Type* t) { + return ::Process32Next(handle, t) == TRUE; + } +}; + +class ProcessEnumerator : public ToolhelpEnumeratorBase { + public: + ProcessEnumerator() + : ToolhelpEnumeratorBase( + ToolhelpProcessTraits::CreateSnapshot(TH32CS_SNAPPROCESS, 0)) { + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(ProcessEnumerator); +}; + +class ToolhelpModuleTraits : public ToolhelpTraits { + public: + typedef MODULEENTRY32 Type; + + static bool First(HANDLE handle, Type* t) { + return ::Module32First(handle, t) == TRUE; + } + + static bool Next(HANDLE handle, Type* t) { + return ::Module32Next(handle, t) == TRUE; + } +}; + +class ModuleEnumerator : public ToolhelpEnumeratorBase { + public: + explicit ModuleEnumerator(uint32 process_id) + : ToolhelpEnumeratorBase( + ToolhelpModuleTraits::CreateSnapshot(TH32CS_SNAPMODULE, + process_id)) { + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(ModuleEnumerator); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_WIN32TOOLHELP_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32toolhelp_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32toolhelp_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32toolhelp_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32toolhelp_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,278 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/pathutils.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/win32toolhelp.h" + +namespace rtc { + +typedef struct { + // Required to match the toolhelp api struct 'design'. + DWORD dwSize; + int a; + uint32 b; +} TestData; + +class Win32ToolhelpTest : public testing::Test { + public: + Win32ToolhelpTest() { + } + + HANDLE AsHandle() { + return reinterpret_cast(this); + } + + static Win32ToolhelpTest* AsFixture(HANDLE handle) { + return reinterpret_cast(handle); + } + + static bool First(HANDLE handle, TestData* d) { + Win32ToolhelpTest* tst = Win32ToolhelpTest::AsFixture(handle); + // This method should be called only once for every test. + // If it is called more than once it return false which + // should break the test. + EXPECT_EQ(0, tst->first_called_); // Just to be safe. + if (tst->first_called_ > 0) { + return false; + } + + *d = kTestData[0]; + tst->index_ = 1; + ++(tst->first_called_); + return true; + } + + static bool Next(HANDLE handle, TestData* d) { + Win32ToolhelpTest* tst = Win32ToolhelpTest::AsFixture(handle); + ++(tst->next_called_); + + if (tst->index_ >= kTestDataSize) { + return FALSE; + } + + *d = kTestData[tst->index_]; + ++(tst->index_); + return true; + } + + static bool Fail(HANDLE handle, TestData* d) { + Win32ToolhelpTest* tst = Win32ToolhelpTest::AsFixture(handle); + ++(tst->fail_called_); + return false; + } + + static bool CloseHandle(HANDLE handle) { + Win32ToolhelpTest* tst = Win32ToolhelpTest::AsFixture(handle); + ++(tst->close_handle_called_); + return true; + } + + protected: + virtual void SetUp() { + fail_called_ = 0; + first_called_ = 0; + next_called_ = 0; + close_handle_called_ = 0; + index_ = 0; + } + + static bool AllZero(const TestData& data) { + return data.dwSize == 0 && data.a == 0 && data.b == 0; + } + + static bool Equals(const TestData& expected, const TestData& actual) { + return expected.dwSize == actual.dwSize + && expected.a == actual.a + && expected.b == actual.b; + } + + bool CheckCallCounters(int first, int next, int fail, int close) { + bool match = first_called_ == first && next_called_ == next + && fail_called_ == fail && close_handle_called_ == close; + + if (!match) { + LOG(LS_ERROR) << "Expected: (" + << first << ", " + << next << ", " + << fail << ", " + << close << ")"; + + LOG(LS_ERROR) << "Actual: (" + << first_called_ << ", " + << next_called_ << ", " + << fail_called_ << ", " + << close_handle_called_ << ")"; + } + return match; + } + + static const int kTestDataSize = 3; + static const TestData kTestData[]; + int index_; + int first_called_; + int fail_called_; + int next_called_; + int close_handle_called_; +}; + +const TestData Win32ToolhelpTest::kTestData[] = { + {1, 1, 1}, {2, 2, 2}, {3, 3, 3} +}; + + +class TestTraits { + public: + typedef TestData Type; + + static bool First(HANDLE handle, Type* t) { + return Win32ToolhelpTest::First(handle, t); + } + + static bool Next(HANDLE handle, Type* t) { + return Win32ToolhelpTest::Next(handle, t); + } + + static bool CloseHandle(HANDLE handle) { + return Win32ToolhelpTest::CloseHandle(handle); + } +}; + +class BadFirstTraits { + public: + typedef TestData Type; + + static bool First(HANDLE handle, Type* t) { + return Win32ToolhelpTest::Fail(handle, t); + } + + static bool Next(HANDLE handle, Type* t) { + // This should never be called. + ADD_FAILURE(); + return false; + } + + static bool CloseHandle(HANDLE handle) { + return Win32ToolhelpTest::CloseHandle(handle); + } +}; + +class BadNextTraits { + public: + typedef TestData Type; + + static bool First(HANDLE handle, Type* t) { + return Win32ToolhelpTest::First(handle, t); + } + + static bool Next(HANDLE handle, Type* t) { + return Win32ToolhelpTest::Fail(handle, t); + } + + static bool CloseHandle(HANDLE handle) { + return Win32ToolhelpTest::CloseHandle(handle); + } +}; + +// The toolhelp in normally inherited but most of +// these tests only excercise the methods from the +// traits therefore I use a typedef to make the +// test code easier to read. +typedef rtc::ToolhelpEnumeratorBase EnumeratorForTest; + +TEST_F(Win32ToolhelpTest, TestNextWithInvalidCtorHandle) { + EnumeratorForTest t(INVALID_HANDLE_VALUE); + + EXPECT_FALSE(t.Next()); + EXPECT_TRUE(CheckCallCounters(0, 0, 0, 0)); +} + +// Tests that Next() returns false if the first-pointer +// function fails. +TEST_F(Win32ToolhelpTest, TestNextFirstFails) { + typedef rtc::ToolhelpEnumeratorBase BadEnumerator; + rtc::scoped_ptr t(new BadEnumerator(AsHandle())); + + // If next ever fails it shall always fail. + EXPECT_FALSE(t->Next()); + EXPECT_FALSE(t->Next()); + EXPECT_FALSE(t->Next()); + t.reset(); + EXPECT_TRUE(CheckCallCounters(0, 0, 1, 1)); +} + +// Tests that Next() returns false if the next-pointer +// function fails. +TEST_F(Win32ToolhelpTest, TestNextNextFails) { + typedef rtc::ToolhelpEnumeratorBase BadEnumerator; + rtc::scoped_ptr t(new BadEnumerator(AsHandle())); + + // If next ever fails it shall always fail. No more calls + // shall be dispatched to Next(...). + EXPECT_TRUE(t->Next()); + EXPECT_FALSE(t->Next()); + EXPECT_FALSE(t->Next()); + t.reset(); + EXPECT_TRUE(CheckCallCounters(1, 0, 1, 1)); +} + + +// Tests that current returns an object is all zero's +// if Next() hasn't been called. +TEST_F(Win32ToolhelpTest, TestCurrentNextNotCalled) { + rtc::scoped_ptr t(new EnumeratorForTest(AsHandle())); + EXPECT_TRUE(AllZero(t->current())); + t.reset(); + EXPECT_TRUE(CheckCallCounters(0, 0, 0, 1)); +} + +// Tests the simple everything works path through the code. +TEST_F(Win32ToolhelpTest, TestCurrentNextCalled) { + rtc::scoped_ptr t(new EnumeratorForTest(AsHandle())); + + EXPECT_TRUE(t->Next()); + EXPECT_TRUE(Equals(t->current(), kTestData[0])); + EXPECT_TRUE(t->Next()); + EXPECT_TRUE(Equals(t->current(), kTestData[1])); + EXPECT_TRUE(t->Next()); + EXPECT_TRUE(Equals(t->current(), kTestData[2])); + EXPECT_FALSE(t->Next()); + t.reset(); + EXPECT_TRUE(CheckCallCounters(1, 3, 0, 1)); +} + +TEST_F(Win32ToolhelpTest, TestCurrentProcess) { + WCHAR buf[MAX_PATH]; + GetModuleFileName(NULL, buf, ARRAY_SIZE(buf)); + std::wstring name = ToUtf16(Pathname(ToUtf8(buf)).filename()); + + rtc::ProcessEnumerator processes; + bool found = false; + while (processes.Next()) { + if (!name.compare(processes.current().szExeFile)) { + found = true; + break; + } + } + EXPECT_TRUE(found); + + rtc::ModuleEnumerator modules(processes.current().th32ProcessID); + found = false; + while (modules.Next()) { + if (!name.compare(modules.current().szModule)) { + found = true; + break; + } + } + EXPECT_TRUE(found); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32_unittest.cc 2015-02-03 14:33:33.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/gunit.h" +#include "webrtc/base/nethelpers.h" +#include "webrtc/base/win32.h" +#include "webrtc/base/winping.h" + +#if !defined(WEBRTC_WIN) +#error Only for Windows +#endif + +namespace rtc { + +class Win32Test : public testing::Test { + public: + Win32Test() { + } +}; + +TEST_F(Win32Test, FileTimeToUInt64Test) { + FILETIME ft; + ft.dwHighDateTime = 0xBAADF00D; + ft.dwLowDateTime = 0xFEED3456; + + uint64 expected = 0xBAADF00DFEED3456; + EXPECT_EQ(expected, ToUInt64(ft)); +} + +TEST_F(Win32Test, WinPingTest) { + WinPing ping; + ASSERT_TRUE(ping.IsValid()); + + // Test valid ping cases. + WinPing::PingResult result = ping.Ping(IPAddress(INADDR_LOOPBACK), 20, 50, 1, + false); + ASSERT_EQ(WinPing::PING_SUCCESS, result); + if (HasIPv6Enabled()) { + WinPing::PingResult v6result = ping.Ping(IPAddress(in6addr_loopback), 20, + 50, 1, false); + ASSERT_EQ(WinPing::PING_SUCCESS, v6result); + } + + // Test invalid parameter cases. + ASSERT_EQ(WinPing::PING_INVALID_PARAMS, ping.Ping( + IPAddress(INADDR_LOOPBACK), 0, 50, 1, false)); + ASSERT_EQ(WinPing::PING_INVALID_PARAMS, ping.Ping( + IPAddress(INADDR_LOOPBACK), 20, 0, 1, false)); + ASSERT_EQ(WinPing::PING_INVALID_PARAMS, ping.Ping( + IPAddress(INADDR_LOOPBACK), 20, 50, 0, false)); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32window.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32window.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32window.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32window.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,121 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/win32window.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// Win32Window +/////////////////////////////////////////////////////////////////////////////// + +static const wchar_t kWindowBaseClassName[] = L"WindowBaseClass"; +HINSTANCE Win32Window::instance_ = NULL; +ATOM Win32Window::window_class_ = 0; + +Win32Window::Win32Window() : wnd_(NULL) { +} + +Win32Window::~Win32Window() { + ASSERT(NULL == wnd_); +} + +bool Win32Window::Create(HWND parent, const wchar_t* title, DWORD style, + DWORD exstyle, int x, int y, int cx, int cy) { + if (wnd_) { + // Window already exists. + return false; + } + + if (!window_class_) { + if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast(&Win32Window::WndProc), + &instance_)) { + LOG_GLE(LS_ERROR) << "GetModuleHandleEx failed"; + return false; + } + + // Class not registered, register it. + WNDCLASSEX wcex; + memset(&wcex, 0, sizeof(wcex)); + wcex.cbSize = sizeof(wcex); + wcex.hInstance = instance_; + wcex.lpfnWndProc = &Win32Window::WndProc; + wcex.lpszClassName = kWindowBaseClassName; + window_class_ = ::RegisterClassEx(&wcex); + if (!window_class_) { + LOG_GLE(LS_ERROR) << "RegisterClassEx failed"; + return false; + } + } + wnd_ = ::CreateWindowEx(exstyle, kWindowBaseClassName, title, style, + x, y, cx, cy, parent, NULL, instance_, this); + return (NULL != wnd_); +} + +void Win32Window::Destroy() { + VERIFY(::DestroyWindow(wnd_) != FALSE); +} + +void Win32Window::Shutdown() { + if (window_class_) { + ::UnregisterClass(MAKEINTATOM(window_class_), instance_); + window_class_ = 0; + } +} + +bool Win32Window::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, + LRESULT& result) { + switch (uMsg) { + case WM_CLOSE: + if (!OnClose()) { + result = 0; + return true; + } + break; + } + return false; +} + +LRESULT Win32Window::WndProc(HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam) { + Win32Window* that = reinterpret_cast( + ::GetWindowLongPtr(hwnd, GWLP_USERDATA)); + if (!that && (WM_CREATE == uMsg)) { + CREATESTRUCT* cs = reinterpret_cast(lParam); + that = static_cast(cs->lpCreateParams); + that->wnd_ = hwnd; + ::SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(that)); + } + if (that) { + LRESULT result; + bool handled = that->OnMessage(uMsg, wParam, lParam, result); + if (WM_DESTROY == uMsg) { + for (HWND child = ::GetWindow(hwnd, GW_CHILD); child; + child = ::GetWindow(child, GW_HWNDNEXT)) { + LOG(LS_INFO) << "Child window: " << static_cast(child); + } + } + if (WM_NCDESTROY == uMsg) { + ::SetWindowLongPtr(hwnd, GWLP_USERDATA, NULL); + that->wnd_ = NULL; + that->OnNcDestroy(); + } + if (handled) { + return result; + } + } + return ::DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32window.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32window.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32window.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32window.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,60 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_WIN32WINDOW_H_ +#define WEBRTC_BASE_WIN32WINDOW_H_ + +#if defined(WEBRTC_WIN) + +#include "webrtc/base/win32.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// Win32Window +/////////////////////////////////////////////////////////////////////////////// + +class Win32Window { + public: + Win32Window(); + virtual ~Win32Window(); + + HWND handle() const { return wnd_; } + + bool Create(HWND parent, const wchar_t* title, DWORD style, DWORD exstyle, + int x, int y, int cx, int cy); + void Destroy(); + + // Call this when your DLL unloads. + static void Shutdown(); + + protected: + virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, + LRESULT& result); + + virtual bool OnClose() { return true; } + virtual void OnNcDestroy() { } + + private: + static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, + LPARAM lParam); + + HWND wnd_; + static HINSTANCE instance_; + static ATOM window_class_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_WIN + +#endif // WEBRTC_BASE_WIN32WINDOW_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32windowpicker.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32windowpicker.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32windowpicker.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32windowpicker.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,143 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "webrtc/base/win32windowpicker.h" + +#include +#include + +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" + +namespace rtc { + +namespace { + +// Window class names that we want to filter out. +const char kProgramManagerClass[] = "Progman"; +const char kButtonClass[] = "Button"; + +} // namespace + +BOOL CALLBACK Win32WindowPicker::EnumProc(HWND hwnd, LPARAM l_param) { + WindowDescriptionList* descriptions = + reinterpret_cast(l_param); + + // Skip windows that are invisible, minimized, have no title, or are owned, + // unless they have the app window style set. Except for minimized windows, + // this is what Alt-Tab does. + // TODO: Figure out how to grab a thumbnail of a minimized window and + // include them in the list. + int len = GetWindowTextLength(hwnd); + HWND owner = GetWindow(hwnd, GW_OWNER); + LONG exstyle = GetWindowLong(hwnd, GWL_EXSTYLE); + if (len == 0 || IsIconic(hwnd) || !IsWindowVisible(hwnd) || + (owner && !(exstyle & WS_EX_APPWINDOW))) { + // TODO: Investigate if windows without title still could be + // interesting to share. We could use the name of the process as title: + // + // GetWindowThreadProcessId() + // OpenProcess() + // QueryFullProcessImageName() + return TRUE; + } + + // Skip the Program Manager window and the Start button. + TCHAR class_name_w[500]; + ::GetClassName(hwnd, class_name_w, 500); + std::string class_name = ToUtf8(class_name_w); + if (class_name == kProgramManagerClass || class_name == kButtonClass) { + // We don't want the Program Manager window nor the Start button. + return TRUE; + } + + TCHAR window_title[500]; + GetWindowText(hwnd, window_title, ARRAY_SIZE(window_title)); + std::string title = ToUtf8(window_title); + + WindowId id(hwnd); + WindowDescription desc(id, title); + descriptions->push_back(desc); + return TRUE; +} + +BOOL CALLBACK Win32WindowPicker::MonitorEnumProc(HMONITOR h_monitor, + HDC hdc_monitor, + LPRECT lprc_monitor, + LPARAM l_param) { + DesktopDescriptionList* desktop_desc = + reinterpret_cast(l_param); + + DesktopId id(h_monitor, static_cast(desktop_desc->size())); + // TODO: Figure out an appropriate desktop title. + DesktopDescription desc(id, ""); + + // Determine whether it's the primary monitor. + MONITORINFO monitor_info = {0}; + monitor_info.cbSize = sizeof(monitor_info); + bool primary = (GetMonitorInfo(h_monitor, &monitor_info) && + (monitor_info.dwFlags & MONITORINFOF_PRIMARY) != 0); + desc.set_primary(primary); + + desktop_desc->push_back(desc); + return TRUE; +} + +Win32WindowPicker::Win32WindowPicker() { +} + +bool Win32WindowPicker::Init() { + return true; +} +// TODO: Consider changing enumeration to clear() descriptions +// before append(). +bool Win32WindowPicker::GetWindowList(WindowDescriptionList* descriptions) { + LPARAM desc = reinterpret_cast(descriptions); + return EnumWindows(Win32WindowPicker::EnumProc, desc) != FALSE; +} + +bool Win32WindowPicker::GetDesktopList(DesktopDescriptionList* descriptions) { + // Create a fresh WindowDescriptionList so that we can use desktop_desc.size() + // in MonitorEnumProc to compute the desktop index. + DesktopDescriptionList desktop_desc; + HDC hdc = GetDC(NULL); + bool success = false; + if (EnumDisplayMonitors(hdc, NULL, Win32WindowPicker::MonitorEnumProc, + reinterpret_cast(&desktop_desc)) != FALSE) { + // Append the desktop descriptions to the end of the returned descriptions. + descriptions->insert(descriptions->end(), desktop_desc.begin(), + desktop_desc.end()); + success = true; + } + ReleaseDC(NULL, hdc); + return success; +} + +bool Win32WindowPicker::GetDesktopDimensions(const DesktopId& id, + int* width, + int* height) { + MONITORINFOEX monitor_info; + monitor_info.cbSize = sizeof(MONITORINFOEX); + if (!GetMonitorInfo(id.id(), &monitor_info)) { + return false; + } + *width = monitor_info.rcMonitor.right - monitor_info.rcMonitor.left; + *height = monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top; + return true; +} + +bool Win32WindowPicker::IsVisible(const WindowId& id) { + return (::IsWindow(id.id()) != FALSE && ::IsWindowVisible(id.id()) != FALSE); +} + +bool Win32WindowPicker::MoveToFront(const WindowId& id) { + return SetForegroundWindow(id.id()) != FALSE; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32windowpicker.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32windowpicker.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32windowpicker.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32windowpicker.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,39 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef WEBRTC_BASE_WIN32WINDOWPICKER_H_ +#define WEBRTC_BASE_WIN32WINDOWPICKER_H_ + +#include "webrtc/base/win32.h" +#include "webrtc/base/windowpicker.h" + +namespace rtc { + +class Win32WindowPicker : public WindowPicker { + public: + Win32WindowPicker(); + virtual bool Init(); + virtual bool IsVisible(const WindowId& id); + virtual bool MoveToFront(const WindowId& id); + virtual bool GetWindowList(WindowDescriptionList* descriptions); + virtual bool GetDesktopList(DesktopDescriptionList* descriptions); + virtual bool GetDesktopDimensions(const DesktopId& id, int* width, + int* height); + + protected: + static BOOL CALLBACK EnumProc(HWND hwnd, LPARAM l_param); + static BOOL CALLBACK MonitorEnumProc(HMONITOR h_monitor, + HDC hdc_monitor, + LPRECT lprc_monitor, + LPARAM l_param); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_WIN32WINDOWPICKER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32windowpicker_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32windowpicker_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32windowpicker_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32windowpicker_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,99 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "webrtc/base/gunit.h" +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/win32window.h" +#include "webrtc/base/win32windowpicker.h" +#include "webrtc/base/windowpicker.h" + +#if !defined(WEBRTC_WIN) +#error Only for Windows +#endif + +namespace rtc { + +static const TCHAR* kVisibleWindowTitle = L"Visible Window"; +static const TCHAR* kInvisibleWindowTitle = L"Invisible Window"; + +class Win32WindowPickerForTest : public Win32WindowPicker { + public: + Win32WindowPickerForTest() { + EXPECT_TRUE(visible_window_.Create(NULL, kVisibleWindowTitle, WS_VISIBLE, + 0, 0, 0, 0, 0)); + EXPECT_TRUE(invisible_window_.Create(NULL, kInvisibleWindowTitle, 0, + 0, 0, 0, 0, 0)); + } + + ~Win32WindowPickerForTest() { + visible_window_.Destroy(); + invisible_window_.Destroy(); + } + + virtual bool GetWindowList(WindowDescriptionList* descriptions) { + if (!Win32WindowPicker::EnumProc(visible_window_.handle(), + reinterpret_cast(descriptions))) { + return false; + } + if (!Win32WindowPicker::EnumProc(invisible_window_.handle(), + reinterpret_cast(descriptions))) { + return false; + } + return true; + } + + Win32Window* visible_window() { + return &visible_window_; + } + + Win32Window* invisible_window() { + return &invisible_window_; + } + + private: + Win32Window visible_window_; + Win32Window invisible_window_; +}; + +TEST(Win32WindowPickerTest, TestGetWindowList) { + Win32WindowPickerForTest window_picker; + WindowDescriptionList descriptions; + EXPECT_TRUE(window_picker.GetWindowList(&descriptions)); + EXPECT_EQ(1, descriptions.size()); + WindowDescription desc = descriptions.front(); + EXPECT_EQ(window_picker.visible_window()->handle(), desc.id().id()); + TCHAR window_title[500]; + GetWindowText(window_picker.visible_window()->handle(), window_title, + ARRAY_SIZE(window_title)); + EXPECT_EQ(0, wcscmp(window_title, kVisibleWindowTitle)); +} + +TEST(Win32WindowPickerTest, TestIsVisible) { + Win32WindowPickerForTest window_picker; + HWND visible_id = window_picker.visible_window()->handle(); + HWND invisible_id = window_picker.invisible_window()->handle(); + EXPECT_TRUE(window_picker.IsVisible(WindowId(visible_id))); + EXPECT_FALSE(window_picker.IsVisible(WindowId(invisible_id))); +} + +TEST(Win32WindowPickerTest, TestMoveToFront) { + Win32WindowPickerForTest window_picker; + HWND visible_id = window_picker.visible_window()->handle(); + HWND invisible_id = window_picker.invisible_window()->handle(); + + // There are a number of condition where SetForegroundWindow might + // fail depending on the state of the calling process. To be on the + // safe side we doesn't expect MoveToFront to return true, just test + // that we don't crash. + window_picker.MoveToFront(WindowId(visible_id)); + window_picker.MoveToFront(WindowId(invisible_id)); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32window_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32window_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/win32window_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/win32window_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,66 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/common.h" +#include "webrtc/base/win32window.h" +#include "webrtc/base/logging.h" + +static LRESULT kDummyResult = 0x1234ABCD; + +class TestWindow : public rtc::Win32Window { + public: + TestWindow() : destroyed_(false) { memset(&msg_, 0, sizeof(msg_)); } + const MSG& msg() const { return msg_; } + bool destroyed() const { return destroyed_; } + + virtual bool OnMessage(UINT uMsg, WPARAM wParam, + LPARAM lParam, LRESULT& result) { + msg_.message = uMsg; + msg_.wParam = wParam; + msg_.lParam = lParam; + result = kDummyResult; + return true; + } + virtual void OnNcDestroy() { + destroyed_ = true; + } + + private: + MSG msg_; + bool destroyed_; +}; + +TEST(Win32WindowTest, Basics) { + TestWindow wnd; + EXPECT_TRUE(wnd.handle() == NULL); + EXPECT_FALSE(wnd.destroyed()); + EXPECT_TRUE(wnd.Create(0, L"Test", 0, 0, 0, 0, 100, 100)); + EXPECT_TRUE(wnd.handle() != NULL); + EXPECT_EQ(kDummyResult, ::SendMessage(wnd.handle(), WM_USER, 1, 2)); + EXPECT_EQ(WM_USER, wnd.msg().message); + EXPECT_EQ(1, wnd.msg().wParam); + EXPECT_EQ(2, wnd.msg().lParam); + wnd.Destroy(); + EXPECT_TRUE(wnd.handle() == NULL); + EXPECT_TRUE(wnd.destroyed()); +} + +TEST(Win32WindowTest, MultipleWindows) { + TestWindow wnd1, wnd2; + EXPECT_TRUE(wnd1.Create(0, L"Test", 0, 0, 0, 0, 100, 100)); + EXPECT_TRUE(wnd2.Create(0, L"Test", 0, 0, 0, 0, 100, 100)); + EXPECT_TRUE(wnd1.handle() != NULL); + EXPECT_TRUE(wnd2.handle() != NULL); + wnd1.Destroy(); + wnd2.Destroy(); + EXPECT_TRUE(wnd2.handle() == NULL); + EXPECT_TRUE(wnd1.handle() == NULL); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/window.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/window.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/window.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/window.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,125 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_WINDOW_H_ +#define WEBRTC_BASE_WINDOW_H_ + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/stringencode.h" + +// Define platform specific window types. +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) +typedef unsigned long Window; // Avoid include . +#elif defined(WEBRTC_WIN) +// We commonly include win32.h in webrtc/base so just include it here. +#include "webrtc/base/win32.h" // Include HWND, HMONITOR. +#elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +typedef unsigned int CGWindowID; +typedef unsigned int CGDirectDisplayID; +#endif + +namespace rtc { + +class WindowId { + public: + // Define WindowT for each platform. +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) + typedef Window WindowT; +#elif defined(WEBRTC_WIN) + typedef HWND WindowT; +#elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + typedef CGWindowID WindowT; +#else + typedef unsigned int WindowT; +#endif + + static WindowId Cast(uint64 id) { +#if defined(WEBRTC_WIN) + return WindowId(reinterpret_cast(id)); +#else + return WindowId(static_cast(id)); +#endif + } + + static uint64 Format(const WindowT& id) { +#if defined(WEBRTC_WIN) + return static_cast(reinterpret_cast(id)); +#else + return static_cast(id); +#endif + } + + WindowId() : id_(0) {} + WindowId(const WindowT& id) : id_(id) {} // NOLINT + const WindowT& id() const { return id_; } + bool IsValid() const { return id_ != 0; } + bool Equals(const WindowId& other) const { + return id_ == other.id(); + } + + private: + WindowT id_; +}; + +class DesktopId { + public: + // Define DesktopT for each platform. +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) + typedef Window DesktopT; +#elif defined(WEBRTC_WIN) + typedef HMONITOR DesktopT; +#elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + typedef CGDirectDisplayID DesktopT; +#else + typedef unsigned int DesktopT; +#endif + + static DesktopId Cast(int id, int index) { +#if defined(WEBRTC_WIN) + return DesktopId(reinterpret_cast(id), index); +#else + return DesktopId(static_cast(id), index); +#endif + } + + DesktopId() : id_(0), index_(-1) {} + DesktopId(const DesktopT& id, int index) // NOLINT + : id_(id), index_(index) { + } + const DesktopT& id() const { return id_; } + int index() const { return index_; } + bool IsValid() const { return index_ != -1; } + bool Equals(const DesktopId& other) const { + return id_ == other.id() && index_ == other.index(); + } + + private: + // Id is the platform specific desktop identifier. + DesktopT id_; + // Index is the desktop index as enumerated by each platform. + // Desktop capturer typically takes the index instead of id. + int index_; +}; + +// Window event types. +enum WindowEvent { + WE_RESIZE = 0, + WE_CLOSE = 1, + WE_MINIMIZE = 2, + WE_RESTORE = 3, +}; + +inline std::string ToString(const WindowId& window) { + return ToString(window.id()); +} + +} // namespace rtc + +#endif // WEBRTC_BASE_WINDOW_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/windowpickerfactory.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/windowpickerfactory.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/windowpickerfactory.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/windowpickerfactory.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,59 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_WINDOWPICKERFACTORY_H_ +#define WEBRTC_BASE_WINDOWPICKERFACTORY_H_ + +#if defined(WEBRTC_WIN) +#include "webrtc/base/win32windowpicker.h" +#elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +#include "webrtc/base/macutils.h" +#include "webrtc/base/macwindowpicker.h" +#elif defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) && defined(HAVE_X11) +#include "webrtc/base/x11windowpicker.h" +#endif + +#include "webrtc/base/windowpicker.h" + +namespace rtc { + +class WindowPickerFactory { + public: + virtual ~WindowPickerFactory() {} + + // Instance method for dependency injection. + virtual WindowPicker* Create() { + return CreateWindowPicker(); + } + + static WindowPicker* CreateWindowPicker() { +#if defined(WEBRTC_WIN) + return new Win32WindowPicker(); +#elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + return new MacWindowPicker(); +#elif defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) && defined(HAVE_X11) + return new X11WindowPicker(); +#else + return NULL; +#endif + } + + static bool IsSupported() { +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + return GetOSVersionName() >= kMacOSLeopard; +#else + return true; +#endif + } +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_WINDOWPICKERFACTORY_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/windowpicker.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/windowpicker.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/windowpicker.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/windowpicker.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,84 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_WINDOWPICKER_H_ +#define WEBRTC_BASE_WINDOWPICKER_H_ + +#include +#include + +#include "webrtc/base/window.h" + +namespace rtc { + +class WindowDescription { + public: + WindowDescription() : id_() {} + WindowDescription(const WindowId& id, const std::string& title) + : id_(id), title_(title) { + } + const WindowId& id() const { return id_; } + void set_id(const WindowId& id) { id_ = id; } + const std::string& title() const { return title_; } + void set_title(const std::string& title) { title_ = title; } + + private: + WindowId id_; + std::string title_; +}; + +class DesktopDescription { + public: + DesktopDescription() : id_() {} + DesktopDescription(const DesktopId& id, const std::string& title) + : id_(id), title_(title), primary_(false) { + } + const DesktopId& id() const { return id_; } + void set_id(const DesktopId& id) { id_ = id; } + const std::string& title() const { return title_; } + void set_title(const std::string& title) { title_ = title; } + // Indicates whether it is the primary desktop in the system. + bool primary() const { return primary_; } + void set_primary(bool primary) { primary_ = primary; } + + private: + DesktopId id_; + std::string title_; + bool primary_; +}; + +typedef std::vector WindowDescriptionList; +typedef std::vector DesktopDescriptionList; + +class WindowPicker { + public: + virtual ~WindowPicker() {} + virtual bool Init() = 0; + + // TODO: Move this two methods to window.h when we no longer need to load + // CoreGraphics dynamically. + virtual bool IsVisible(const WindowId& id) = 0; + virtual bool MoveToFront(const WindowId& id) = 0; + + // Gets a list of window description and appends to descriptions. + // Returns true if successful. + virtual bool GetWindowList(WindowDescriptionList* descriptions) = 0; + // Gets a list of desktop descriptions and appends to descriptions. + // Returns true if successful. + virtual bool GetDesktopList(DesktopDescriptionList* descriptions) = 0; + // Gets the width and height of a desktop. + // Returns true if successful. + virtual bool GetDesktopDimensions(const DesktopId& id, int* width, + int* height) = 0; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_WINDOWPICKER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/windowpicker_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/windowpicker_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/windowpicker_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/windowpicker_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,67 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "webrtc/base/gunit.h" +#include "webrtc/base/testutils.h" +#include "webrtc/base/window.h" +#include "webrtc/base/windowpicker.h" +#include "webrtc/base/windowpickerfactory.h" + +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +# define DISABLE_ON_MAC(name) DISABLED_ ## name +#else +# define DISABLE_ON_MAC(name) name +#endif + +TEST(WindowPickerTest, GetWindowList) { + MAYBE_SKIP_SCREENCAST_TEST(); + if (!rtc::WindowPickerFactory::IsSupported()) { + LOG(LS_INFO) << "skipping test: window capturing is not supported with " + << "current configuration."; + } + rtc::scoped_ptr picker( + rtc::WindowPickerFactory::CreateWindowPicker()); + EXPECT_TRUE(picker->Init()); + rtc::WindowDescriptionList descriptions; + EXPECT_TRUE(picker->GetWindowList(&descriptions)); +} + +// TODO(hughv) Investigate why this fails on pulse but not locally after +// upgrading to XCode 4.5. The failure is GetDesktopList returning FALSE. +TEST(WindowPickerTest, DISABLE_ON_MAC(GetDesktopList)) { + MAYBE_SKIP_SCREENCAST_TEST(); + if (!rtc::WindowPickerFactory::IsSupported()) { + LOG(LS_INFO) << "skipping test: window capturing is not supported with " + << "current configuration."; + } + rtc::scoped_ptr picker( + rtc::WindowPickerFactory::CreateWindowPicker()); + EXPECT_TRUE(picker->Init()); + rtc::DesktopDescriptionList descriptions; + EXPECT_TRUE(picker->GetDesktopList(&descriptions)); + if (descriptions.size() > 0) { + int width = 0; + int height = 0; + EXPECT_TRUE(picker->GetDesktopDimensions(descriptions[0].id(), &width, + &height)); + EXPECT_GT(width, 0); + EXPECT_GT(height, 0); + + // Test |IsPrimaryDesktop|. Only one desktop should be a primary. + bool found_primary = false; + for (rtc::DesktopDescriptionList::iterator it = descriptions.begin(); + it != descriptions.end(); ++it) { + if (it->primary()) { + EXPECT_FALSE(found_primary); + found_primary = true; + } + } + EXPECT_TRUE(found_primary); + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/winfirewall.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/winfirewall.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/winfirewall.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/winfirewall.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,155 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/winfirewall.h" + +#include "webrtc/base/win32.h" + +#include +#include + +#define RELEASE(lpUnk) do { \ + if ((lpUnk) != NULL) { \ + (lpUnk)->Release(); \ + (lpUnk) = NULL; \ + } \ +} while (0) + +namespace rtc { + +////////////////////////////////////////////////////////////////////// +// WinFirewall +////////////////////////////////////////////////////////////////////// + +WinFirewall::WinFirewall() : mgr_(NULL), policy_(NULL), profile_(NULL) { +} + +WinFirewall::~WinFirewall() { + Shutdown(); +} + +bool WinFirewall::Initialize(HRESULT* result) { + if (mgr_) { + if (result) { + *result = S_OK; + } + return true; + } + + HRESULT hr = CoCreateInstance(__uuidof(NetFwMgr), + 0, CLSCTX_INPROC_SERVER, + __uuidof(INetFwMgr), + reinterpret_cast(&mgr_)); + if (SUCCEEDED(hr) && (mgr_ != NULL)) + hr = mgr_->get_LocalPolicy(&policy_); + if (SUCCEEDED(hr) && (policy_ != NULL)) + hr = policy_->get_CurrentProfile(&profile_); + + if (result) + *result = hr; + return SUCCEEDED(hr) && (profile_ != NULL); +} + +void WinFirewall::Shutdown() { + RELEASE(profile_); + RELEASE(policy_); + RELEASE(mgr_); +} + +bool WinFirewall::Enabled() const { + if (!profile_) + return false; + + VARIANT_BOOL fwEnabled = VARIANT_FALSE; + profile_->get_FirewallEnabled(&fwEnabled); + return (fwEnabled != VARIANT_FALSE); +} + +bool WinFirewall::QueryAuthorized(const char* filename, bool* authorized) + const { + return QueryAuthorizedW(ToUtf16(filename).c_str(), authorized); +} + +bool WinFirewall::QueryAuthorizedW(const wchar_t* filename, bool* authorized) + const { + *authorized = false; + bool success = false; + + if (!profile_) + return false; + + _bstr_t bfilename = filename; + + INetFwAuthorizedApplications* apps = NULL; + HRESULT hr = profile_->get_AuthorizedApplications(&apps); + if (SUCCEEDED(hr) && (apps != NULL)) { + INetFwAuthorizedApplication* app = NULL; + hr = apps->Item(bfilename, &app); + if (SUCCEEDED(hr) && (app != NULL)) { + VARIANT_BOOL fwEnabled = VARIANT_FALSE; + hr = app->get_Enabled(&fwEnabled); + app->Release(); + + if (SUCCEEDED(hr)) { + success = true; + *authorized = (fwEnabled != VARIANT_FALSE); + } + } else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { + // No entry in list of authorized apps + success = true; + } else { + // Unexpected error + } + apps->Release(); + } + + return success; +} + +bool WinFirewall::AddApplication(const char* filename, + const char* friendly_name, + bool authorized, + HRESULT* result) { + return AddApplicationW(ToUtf16(filename).c_str(), + ToUtf16(friendly_name).c_str(), authorized, result); +} + +bool WinFirewall::AddApplicationW(const wchar_t* filename, + const wchar_t* friendly_name, + bool authorized, + HRESULT* result) { + INetFwAuthorizedApplications* apps = NULL; + HRESULT hr = profile_->get_AuthorizedApplications(&apps); + if (SUCCEEDED(hr) && (apps != NULL)) { + INetFwAuthorizedApplication* app = NULL; + hr = CoCreateInstance(__uuidof(NetFwAuthorizedApplication), + 0, CLSCTX_INPROC_SERVER, + __uuidof(INetFwAuthorizedApplication), + reinterpret_cast(&app)); + if (SUCCEEDED(hr) && (app != NULL)) { + _bstr_t bstr = filename; + hr = app->put_ProcessImageFileName(bstr); + bstr = friendly_name; + if (SUCCEEDED(hr)) + hr = app->put_Name(bstr); + if (SUCCEEDED(hr)) + hr = app->put_Enabled(authorized ? VARIANT_TRUE : VARIANT_FALSE); + if (SUCCEEDED(hr)) + hr = apps->Add(app); + app->Release(); + } + apps->Release(); + } + if (result) + *result = hr; + return SUCCEEDED(hr); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/winfirewall.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/winfirewall.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/winfirewall.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/winfirewall.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_WINFIREWALL_H_ +#define WEBRTC_BASE_WINFIREWALL_H_ + +#ifndef _HRESULT_DEFINED +#define _HRESULT_DEFINED +typedef long HRESULT; // Can't forward declare typedef, but don't need all win +#endif // !_HRESULT_DEFINED + +struct INetFwMgr; +struct INetFwPolicy; +struct INetFwProfile; + +namespace rtc { + +////////////////////////////////////////////////////////////////////// +// WinFirewall +////////////////////////////////////////////////////////////////////// + +class WinFirewall { + public: + WinFirewall(); + ~WinFirewall(); + + bool Initialize(HRESULT* result); + void Shutdown(); + + bool Enabled() const; + bool QueryAuthorized(const char* filename, bool* authorized) const; + bool QueryAuthorizedW(const wchar_t* filename, bool* authorized) const; + + bool AddApplication(const char* filename, const char* friendly_name, + bool authorized, HRESULT* result); + bool AddApplicationW(const wchar_t* filename, const wchar_t* friendly_name, + bool authorized, HRESULT* result); + + private: + INetFwMgr* mgr_; + INetFwPolicy* policy_; + INetFwProfile* profile_; +}; + +////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_WINFIREWALL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/winfirewall_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/winfirewall_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/winfirewall_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/winfirewall_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/winfirewall.h" + +#include + +namespace rtc { + +TEST(WinFirewallTest, ReadStatus) { + ::CoInitialize(NULL); + WinFirewall fw; + HRESULT hr; + bool authorized; + + EXPECT_FALSE(fw.QueryAuthorized("bogus.exe", &authorized)); + EXPECT_TRUE(fw.Initialize(&hr)); + EXPECT_EQ(S_OK, hr); + + EXPECT_TRUE(fw.QueryAuthorized("bogus.exe", &authorized)); + + // Unless we mock out INetFwMgr we can't really have an expectation either way + // about whether we're authorized. It will depend on the settings of the + // machine running the test. Same goes for AddApplication. + + fw.Shutdown(); + EXPECT_FALSE(fw.QueryAuthorized("bogus.exe", &authorized)); + + ::CoUninitialize(); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/winping.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/winping.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/winping.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/winping.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,359 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/winping.h" + +#include +#include + +#include "webrtc/base/byteorder.h" +#include "webrtc/base/common.h" +#include "webrtc/base/ipaddress.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/nethelpers.h" +#include "webrtc/base/socketaddress.h" + +namespace rtc { + +////////////////////////////////////////////////////////////////////// +// Found in IPExport.h +////////////////////////////////////////////////////////////////////// + +typedef struct icmp_echo_reply { + ULONG Address; // Replying address + ULONG Status; // Reply IP_STATUS + ULONG RoundTripTime; // RTT in milliseconds + USHORT DataSize; // Reply data size in bytes + USHORT Reserved; // Reserved for system use + PVOID Data; // Pointer to the reply data + struct ip_option_information Options; // Reply options +} ICMP_ECHO_REPLY, * PICMP_ECHO_REPLY; + +typedef struct icmpv6_echo_reply_lh { + sockaddr_in6 Address; + ULONG Status; + unsigned int RoundTripTime; +} ICMPV6_ECHO_REPLY, *PICMPV6_ECHO_REPLY; + +// +// IP_STATUS codes returned from IP APIs +// + +#define IP_STATUS_BASE 11000 + +#define IP_SUCCESS 0 +#define IP_BUF_TOO_SMALL (IP_STATUS_BASE + 1) +#define IP_DEST_NET_UNREACHABLE (IP_STATUS_BASE + 2) +#define IP_DEST_HOST_UNREACHABLE (IP_STATUS_BASE + 3) +#define IP_DEST_PROT_UNREACHABLE (IP_STATUS_BASE + 4) +#define IP_DEST_PORT_UNREACHABLE (IP_STATUS_BASE + 5) +#define IP_NO_RESOURCES (IP_STATUS_BASE + 6) +#define IP_BAD_OPTION (IP_STATUS_BASE + 7) +#define IP_HW_ERROR (IP_STATUS_BASE + 8) +#define IP_PACKET_TOO_BIG (IP_STATUS_BASE + 9) +#define IP_REQ_TIMED_OUT (IP_STATUS_BASE + 10) +#define IP_BAD_REQ (IP_STATUS_BASE + 11) +#define IP_BAD_ROUTE (IP_STATUS_BASE + 12) +#define IP_TTL_EXPIRED_TRANSIT (IP_STATUS_BASE + 13) +#define IP_TTL_EXPIRED_REASSEM (IP_STATUS_BASE + 14) +#define IP_PARAM_PROBLEM (IP_STATUS_BASE + 15) +#define IP_SOURCE_QUENCH (IP_STATUS_BASE + 16) +#define IP_OPTION_TOO_BIG (IP_STATUS_BASE + 17) +#define IP_BAD_DESTINATION (IP_STATUS_BASE + 18) + +#define IP_ADDR_DELETED (IP_STATUS_BASE + 19) +#define IP_SPEC_MTU_CHANGE (IP_STATUS_BASE + 20) +#define IP_MTU_CHANGE (IP_STATUS_BASE + 21) +#define IP_UNLOAD (IP_STATUS_BASE + 22) +#define IP_ADDR_ADDED (IP_STATUS_BASE + 23) +#define IP_MEDIA_CONNECT (IP_STATUS_BASE + 24) +#define IP_MEDIA_DISCONNECT (IP_STATUS_BASE + 25) +#define IP_BIND_ADAPTER (IP_STATUS_BASE + 26) +#define IP_UNBIND_ADAPTER (IP_STATUS_BASE + 27) +#define IP_DEVICE_DOES_NOT_EXIST (IP_STATUS_BASE + 28) +#define IP_DUPLICATE_ADDRESS (IP_STATUS_BASE + 29) +#define IP_INTERFACE_METRIC_CHANGE (IP_STATUS_BASE + 30) +#define IP_RECONFIG_SECFLTR (IP_STATUS_BASE + 31) +#define IP_NEGOTIATING_IPSEC (IP_STATUS_BASE + 32) +#define IP_INTERFACE_WOL_CAPABILITY_CHANGE (IP_STATUS_BASE + 33) +#define IP_DUPLICATE_IPADD (IP_STATUS_BASE + 34) + +#define IP_GENERAL_FAILURE (IP_STATUS_BASE + 50) +#define MAX_IP_STATUS IP_GENERAL_FAILURE +#define IP_PENDING (IP_STATUS_BASE + 255) + +// +// Values used in the IP header Flags field. +// +#define IP_FLAG_DF 0x2 // Don't fragment this packet. + +// +// Supported IP Option Types. +// +// These types define the options which may be used in the OptionsData field +// of the ip_option_information structure. See RFC 791 for a complete +// description of each. +// +#define IP_OPT_EOL 0 // End of list option +#define IP_OPT_NOP 1 // No operation +#define IP_OPT_SECURITY 0x82 // Security option +#define IP_OPT_LSRR 0x83 // Loose source route +#define IP_OPT_SSRR 0x89 // Strict source route +#define IP_OPT_RR 0x7 // Record route +#define IP_OPT_TS 0x44 // Timestamp +#define IP_OPT_SID 0x88 // Stream ID (obsolete) +#define IP_OPT_ROUTER_ALERT 0x94 // Router Alert Option + +#define MAX_OPT_SIZE 40 // Maximum length of IP options in bytes + +////////////////////////////////////////////////////////////////////// +// Global Constants and Types +////////////////////////////////////////////////////////////////////// + +const char * const ICMP_DLL_NAME = "Iphlpapi.dll"; +const char * const ICMP_CREATE_FUNC = "IcmpCreateFile"; +const char * const ICMP_CLOSE_FUNC = "IcmpCloseHandle"; +const char * const ICMP_SEND_FUNC = "IcmpSendEcho"; +const char * const ICMP6_CREATE_FUNC = "Icmp6CreateFile"; +const char * const ICMP6_CLOSE_FUNC = "Icmp6CloseHandle"; +const char * const ICMP6_SEND_FUNC = "Icmp6SendEcho2"; + +inline uint32 ReplySize(uint32 data_size, int family) { + if (family == AF_INET) { + // A ping error message is 8 bytes long, so make sure we allow for at least + // 8 bytes of reply data. + return sizeof(ICMP_ECHO_REPLY) + rtc::_max(8, data_size); + } else if (family == AF_INET6) { + // Per MSDN, Send6IcmpEcho2 needs at least one ICMPV6_ECHO_REPLY, + // 8 bytes for ICMP header, _and_ an IO_BLOCK_STATUS (2 pointers), + // in addition to the data size. + return sizeof(ICMPV6_ECHO_REPLY) + data_size + 8 + (2 * sizeof(DWORD*)); + } else { + return 0; + } +} + +////////////////////////////////////////////////////////////////////// +// WinPing +////////////////////////////////////////////////////////////////////// + +WinPing::WinPing() + : dll_(0), hping_(INVALID_HANDLE_VALUE), create_(0), close_(0), send_(0), + create6_(0), send6_(0), data_(0), dlen_(0), reply_(0), + rlen_(0), valid_(false) { + + dll_ = LoadLibraryA(ICMP_DLL_NAME); + if (!dll_) { + LOG(LERROR) << "LoadLibrary: " << GetLastError(); + return; + } + + create_ = (PIcmpCreateFile) GetProcAddress(dll_, ICMP_CREATE_FUNC); + close_ = (PIcmpCloseHandle) GetProcAddress(dll_, ICMP_CLOSE_FUNC); + send_ = (PIcmpSendEcho) GetProcAddress(dll_, ICMP_SEND_FUNC); + if (!create_ || !close_ || !send_) { + LOG(LERROR) << "GetProcAddress(ICMP_*): " << GetLastError(); + return; + } + hping_ = create_(); + if (hping_ == INVALID_HANDLE_VALUE) { + LOG(LERROR) << "IcmpCreateFile: " << GetLastError(); + return; + } + + if (HasIPv6Enabled()) { + create6_ = (PIcmp6CreateFile) GetProcAddress(dll_, ICMP6_CREATE_FUNC); + send6_ = (PIcmp6SendEcho2) GetProcAddress(dll_, ICMP6_SEND_FUNC); + if (!create6_ || !send6_) { + LOG(LERROR) << "GetProcAddress(ICMP6_*): " << GetLastError(); + return; + } + hping6_ = create6_(); + if (hping6_ == INVALID_HANDLE_VALUE) { + LOG(LERROR) << "Icmp6CreateFile: " << GetLastError(); + } + } + + dlen_ = 0; + rlen_ = ReplySize(dlen_, AF_INET); + data_ = new char[dlen_]; + reply_ = new char[rlen_]; + + valid_ = true; +} + +WinPing::~WinPing() { + if ((hping_ != INVALID_HANDLE_VALUE) && close_) { + if (!close_(hping_)) + LOG(WARNING) << "IcmpCloseHandle: " << GetLastError(); + } + if ((hping6_ != INVALID_HANDLE_VALUE) && close_) { + if (!close_(hping6_)) { + LOG(WARNING) << "Icmp6CloseHandle: " << GetLastError(); + } + } + + if (dll_) + FreeLibrary(dll_); + + delete[] data_; + delete[] reply_; +} + +WinPing::PingResult WinPing::Ping( + IPAddress ip, uint32 data_size, uint32 timeout, uint8 ttl, + bool allow_fragments) { + + if (data_size == 0 || timeout == 0 || ttl == 0) { + LOG(LERROR) << "IcmpSendEcho: data_size/timeout/ttl is 0."; + return PING_INVALID_PARAMS; + } + + assert(IsValid()); + + IP_OPTION_INFORMATION ipopt; + memset(&ipopt, 0, sizeof(ipopt)); + if (!allow_fragments) + ipopt.Flags |= IP_FLAG_DF; + ipopt.Ttl = ttl; + + uint32 reply_size = ReplySize(data_size, ip.family()); + + if (data_size > dlen_) { + delete [] data_; + dlen_ = data_size; + data_ = new char[dlen_]; + memset(data_, 'z', dlen_); + } + + if (reply_size > rlen_) { + delete [] reply_; + rlen_ = reply_size; + reply_ = new char[rlen_]; + } + DWORD result = 0; + if (ip.family() == AF_INET) { + result = send_(hping_, ip.ipv4_address().S_un.S_addr, + data_, uint16(data_size), &ipopt, + reply_, reply_size, timeout); + } else if (ip.family() == AF_INET6) { + sockaddr_in6 src = {0}; + sockaddr_in6 dst = {0}; + src.sin6_family = AF_INET6; + dst.sin6_family = AF_INET6; + dst.sin6_addr = ip.ipv6_address(); + result = send6_(hping6_, NULL, NULL, NULL, + &src, &dst, + data_, int16(data_size), &ipopt, + reply_, reply_size, timeout); + } + if (result == 0) { + DWORD error = GetLastError(); + if (error == IP_PACKET_TOO_BIG) + return PING_TOO_LARGE; + if (error == IP_REQ_TIMED_OUT) + return PING_TIMEOUT; + LOG(LERROR) << "IcmpSendEcho(" << ip.ToSensitiveString() + << ", " << data_size << "): " << error; + return PING_FAIL; + } + + return PING_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// +// Microsoft Documenation +////////////////////////////////////////////////////////////////////// +// +// Routine Name: +// +// IcmpCreateFile +// +// Routine Description: +// +// Opens a handle on which ICMP Echo Requests can be issued. +// +// Arguments: +// +// None. +// +// Return Value: +// +// An open file handle or INVALID_HANDLE_VALUE. Extended error information +// is available by calling GetLastError(). +// +////////////////////////////////////////////////////////////////////// +// +// Routine Name: +// +// IcmpCloseHandle +// +// Routine Description: +// +// Closes a handle opened by ICMPOpenFile. +// +// Arguments: +// +// IcmpHandle - The handle to close. +// +// Return Value: +// +// TRUE if the handle was closed successfully, otherwise FALSE. Extended +// error information is available by calling GetLastError(). +// +////////////////////////////////////////////////////////////////////// +// +// Routine Name: +// +// IcmpSendEcho +// +// Routine Description: +// +// Sends an ICMP Echo request and returns any replies. The +// call returns when the timeout has expired or the reply buffer +// is filled. +// +// Arguments: +// +// IcmpHandle - An open handle returned by ICMPCreateFile. +// +// DestinationAddress - The destination of the echo request. +// +// RequestData - A buffer containing the data to send in the +// request. +// +// RequestSize - The number of bytes in the request data buffer. +// +// RequestOptions - Pointer to the IP header options for the request. +// May be NULL. +// +// ReplyBuffer - A buffer to hold any replies to the request. +// On return, the buffer will contain an array of +// ICMP_ECHO_REPLY structures followed by the +// options and data for the replies. The buffer +// should be large enough to hold at least one +// ICMP_ECHO_REPLY structure plus +// MAX(RequestSize, 8) bytes of data since an ICMP +// error message contains 8 bytes of data. +// +// ReplySize - The size in bytes of the reply buffer. +// +// Timeout - The time in milliseconds to wait for replies. +// +// Return Value: +// +// Returns the number of ICMP_ECHO_REPLY structures stored in ReplyBuffer. +// The status of each reply is contained in the structure. If the return +// value is zero, extended error information is available via +// GetLastError(). +// +////////////////////////////////////////////////////////////////////// + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/winping.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/winping.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/winping.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/winping.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,103 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_WINPING_H__ +#define WEBRTC_BASE_WINPING_H__ + +#if defined(WEBRTC_WIN) + +#include "webrtc/base/win32.h" +#include "webrtc/base/basictypes.h" +#include "webrtc/base/IPAddress.h" + +namespace rtc { + +// This class wraps a Win32 API for doing ICMP pinging. This API, unlike the +// the normal socket APIs (as implemented on Win9x), will return an error if +// an ICMP packet with the dont-fragment bit set is too large. This means this +// class can be used to detect the MTU to a given address. + +typedef struct ip_option_information { + UCHAR Ttl; // Time To Live + UCHAR Tos; // Type Of Service + UCHAR Flags; // IP header flags + UCHAR OptionsSize; // Size in bytes of options data + PUCHAR OptionsData; // Pointer to options data +} IP_OPTION_INFORMATION, * PIP_OPTION_INFORMATION; + +typedef HANDLE (WINAPI *PIcmpCreateFile)(); + +typedef BOOL (WINAPI *PIcmpCloseHandle)(HANDLE icmp_handle); + +typedef HANDLE (WINAPI *PIcmp6CreateFile)(); + +typedef BOOL (WINAPI *PIcmp6CloseHandle)(HANDLE icmp_handle); + +typedef DWORD (WINAPI *PIcmpSendEcho)( + HANDLE IcmpHandle, + ULONG DestinationAddress, + LPVOID RequestData, + WORD RequestSize, + PIP_OPTION_INFORMATION RequestOptions, + LPVOID ReplyBuffer, + DWORD ReplySize, + DWORD Timeout); + +typedef DWORD (WINAPI *PIcmp6SendEcho2)( + HANDLE IcmpHandle, + HANDLE Event, + FARPROC ApcRoutine, + PVOID ApcContext, + struct sockaddr_in6 *SourceAddress, + struct sockaddr_in6 *DestinationAddress, + LPVOID RequestData, + WORD RequestSize, + PIP_OPTION_INFORMATION RequestOptions, + LPVOID ReplyBuffer, + DWORD ReplySize, + DWORD Timeout +); + +class WinPing { +public: + WinPing(); + ~WinPing(); + + // Determines whether the class was initialized correctly. + bool IsValid() { return valid_; } + + // Attempts to send a ping with the given parameters. + enum PingResult { PING_FAIL, PING_INVALID_PARAMS, + PING_TOO_LARGE, PING_TIMEOUT, PING_SUCCESS }; + PingResult Ping( + IPAddress ip, uint32 data_size, uint32 timeout_millis, uint8 ttl, + bool allow_fragments); + +private: + HMODULE dll_; + HANDLE hping_; + HANDLE hping6_; + PIcmpCreateFile create_; + PIcmpCloseHandle close_; + PIcmpSendEcho send_; + PIcmp6CreateFile create6_; + PIcmp6SendEcho2 send6_; + char* data_; + uint32 dlen_; + char* reply_; + uint32 rlen_; + bool valid_; +}; + +} // namespace rtc + +#endif // WEBRTC_WIN + +#endif // WEBRTC_BASE_WINPING_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/worker.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/worker.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/worker.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/worker.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,75 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/worker.h" + +#include "webrtc/base/common.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/thread.h" + +namespace rtc { + +enum { + MSG_HAVEWORK = 0, +}; + +Worker::Worker() : worker_thread_(NULL) {} + +Worker::~Worker() { + // We need to already be stopped before being destroyed. We cannot call + // StopWork() from here because the subclass's data has already been + // destructed, so OnStop() cannot be called. + ASSERT(!worker_thread_); +} + +bool Worker::StartWork() { + rtc::Thread *me = rtc::Thread::Current(); + if (worker_thread_) { + if (worker_thread_ == me) { + // Already working on this thread, so nothing to do. + return true; + } else { + LOG(LS_ERROR) << "Automatically switching threads is not supported"; + ASSERT(false); + return false; + } + } + worker_thread_ = me; + OnStart(); + return true; +} + +bool Worker::StopWork() { + if (!worker_thread_) { + // Already not working, so nothing to do. + return true; + } else if (worker_thread_ != rtc::Thread::Current()) { + LOG(LS_ERROR) << "Stopping from a different thread is not supported"; + ASSERT(false); + return false; + } + OnStop(); + worker_thread_->Clear(this, MSG_HAVEWORK); + worker_thread_ = NULL; + return true; +} + +void Worker::HaveWork() { + ASSERT(worker_thread_ != NULL); + worker_thread_->Post(this, MSG_HAVEWORK); +} + +void Worker::OnMessage(rtc::Message *msg) { + ASSERT(msg->message_id == MSG_HAVEWORK); + ASSERT(worker_thread_ == rtc::Thread::Current()); + OnHaveWork(); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/worker.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/worker.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/worker.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/worker.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,72 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_WORKER_H_ +#define WEBRTC_BASE_WORKER_H_ + +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/messagehandler.h" + +namespace rtc { + +class Thread; + +// A worker is an object that performs some specific long-lived task in an +// event-driven manner. +// The only method that should be considered thread-safe is HaveWork(), which +// allows you to signal the availability of work from any thread. All other +// methods are thread-hostile. Specifically: +// StartWork()/StopWork() should not be called concurrently with themselves or +// each other, and it is an error to call them while the worker is running on +// a different thread. +// The destructor may not be called if the worker is currently running +// (regardless of the thread), but you can call StopWork() in a subclass's +// destructor. +class Worker : private MessageHandler { + public: + Worker(); + + // Destroys this Worker, but it must have already been stopped via StopWork(). + virtual ~Worker(); + + // Attaches the worker to the current thread and begins processing work if not + // already doing so. + bool StartWork(); + // Stops processing work if currently doing so and detaches from the current + // thread. + bool StopWork(); + + protected: + // Signal that work is available to be done. May only be called within the + // lifetime of a OnStart()/OnStop() pair. + void HaveWork(); + + // These must be implemented by a subclass. + // Called on the worker thread to start working. + virtual void OnStart() = 0; + // Called on the worker thread when work has been signalled via HaveWork(). + virtual void OnHaveWork() = 0; + // Called on the worker thread to stop working. Upon return, any pending + // OnHaveWork() calls are cancelled. + virtual void OnStop() = 0; + + private: + // Inherited from MessageHandler. + virtual void OnMessage(Message *msg); + + // The thread that is currently doing the work. + Thread *worker_thread_; + + DISALLOW_COPY_AND_ASSIGN(Worker); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_WORKER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/x11windowpicker.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/x11windowpicker.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/x11windowpicker.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/x11windowpicker.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,818 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/x11windowpicker.h" + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "webrtc/base/logging.h" + +namespace rtc { + +// Convenience wrapper for XGetWindowProperty results. +template +class XWindowProperty { + public: + XWindowProperty(Display* display, Window window, Atom property) + : data_(NULL) { + const int kBitsPerByte = 8; + Atom actual_type; + int actual_format; + unsigned long bytes_after; // NOLINT: type required by XGetWindowProperty + int status = XGetWindowProperty(display, window, property, 0L, ~0L, False, + AnyPropertyType, &actual_type, + &actual_format, &size_, + &bytes_after, &data_); + succeeded_ = (status == Success); + if (!succeeded_) { + data_ = NULL; // Ensure nothing is freed. + } else if (sizeof(PropertyType) * kBitsPerByte != actual_format) { + LOG(LS_WARNING) << "Returned type size differs from " + "requested type size."; + succeeded_ = false; + // We still need to call XFree in this case, so leave data_ alone. + } + if (!succeeded_) { + size_ = 0; + } + } + + ~XWindowProperty() { + if (data_) { + XFree(data_); + } + } + + bool succeeded() const { return succeeded_; } + size_t size() const { return size_; } + const PropertyType* data() const { + return reinterpret_cast(data_); + } + PropertyType* data() { + return reinterpret_cast(data_); + } + + private: + bool succeeded_; + unsigned long size_; // NOLINT: type required by XGetWindowProperty + unsigned char* data_; + + DISALLOW_COPY_AND_ASSIGN(XWindowProperty); +}; + +// Stupid X11. It seems none of the synchronous returns codes from X11 calls +// are meaningful unless an asynchronous error handler is configured. This +// RAII class registers and unregisters an X11 error handler. +class XErrorSuppressor { + public: + explicit XErrorSuppressor(Display* display) + : display_(display), original_error_handler_(NULL) { + SuppressX11Errors(); + } + ~XErrorSuppressor() { + UnsuppressX11Errors(); + } + + private: + static int ErrorHandler(Display* display, XErrorEvent* e) { + char buf[256]; + XGetErrorText(display, e->error_code, buf, sizeof buf); + LOG(LS_WARNING) << "Received X11 error \"" << buf << "\" for request code " + << static_cast(e->request_code); + return 0; + } + + void SuppressX11Errors() { + XFlush(display_); + XSync(display_, False); + original_error_handler_ = XSetErrorHandler(&ErrorHandler); + } + + void UnsuppressX11Errors() { + XFlush(display_); + XSync(display_, False); + XErrorHandler handler = XSetErrorHandler(original_error_handler_); + if (handler != &ErrorHandler) { + LOG(LS_WARNING) << "Unbalanced XSetErrorHandler() calls detected. " + << "Final error handler may not be what you expect!"; + } + original_error_handler_ = NULL; + } + + Display* display_; + XErrorHandler original_error_handler_; + + DISALLOW_COPY_AND_ASSIGN(XErrorSuppressor); +}; + +// Hiding all X11 specifics inside its own class. This to avoid +// conflicts between talk and X11 header declarations. +class XWindowEnumerator { + public: + XWindowEnumerator() + : display_(NULL), + has_composite_extension_(false), + has_render_extension_(false) { + } + + ~XWindowEnumerator() { + if (display_ != NULL) { + XCloseDisplay(display_); + } + } + + bool Init() { + if (display_ != NULL) { + // Already initialized. + return true; + } + display_ = XOpenDisplay(NULL); + if (display_ == NULL) { + LOG(LS_ERROR) << "Failed to open display."; + return false; + } + + XErrorSuppressor error_suppressor(display_); + + wm_state_ = XInternAtom(display_, "WM_STATE", True); + net_wm_icon_ = XInternAtom(display_, "_NET_WM_ICON", False); + + int event_base, error_base, major_version, minor_version; + if (XCompositeQueryExtension(display_, &event_base, &error_base) && + XCompositeQueryVersion(display_, &major_version, &minor_version) && + // XCompositeNameWindowPixmap() requires version 0.2 + (major_version > 0 || minor_version >= 2)) { + has_composite_extension_ = true; + } else { + LOG(LS_INFO) << "Xcomposite extension not available or too old."; + } + + if (XRenderQueryExtension(display_, &event_base, &error_base) && + XRenderQueryVersion(display_, &major_version, &minor_version) && + // XRenderSetPictureTransform() requires version 0.6 + (major_version > 0 || minor_version >= 6)) { + has_render_extension_ = true; + } else { + LOG(LS_INFO) << "Xrender extension not available or too old."; + } + return true; + } + + bool EnumerateWindows(WindowDescriptionList* descriptions) { + if (!Init()) { + return false; + } + XErrorSuppressor error_suppressor(display_); + int num_screens = XScreenCount(display_); + bool result = false; + for (int i = 0; i < num_screens; ++i) { + if (EnumerateScreenWindows(descriptions, i)) { + // We know we succeded on at least one screen. + result = true; + } + } + return result; + } + + bool EnumerateDesktops(DesktopDescriptionList* descriptions) { + if (!Init()) { + return false; + } + XErrorSuppressor error_suppressor(display_); + Window default_root_window = XDefaultRootWindow(display_); + int num_screens = XScreenCount(display_); + for (int i = 0; i < num_screens; ++i) { + Window root_window = XRootWindow(display_, i); + DesktopId id(DesktopId(root_window, i)); + // TODO: Figure out an appropriate desktop title. + DesktopDescription desc(id, ""); + desc.set_primary(root_window == default_root_window); + descriptions->push_back(desc); + } + return num_screens > 0; + } + + bool IsVisible(const WindowId& id) { + if (!Init()) { + return false; + } + XErrorSuppressor error_suppressor(display_); + XWindowAttributes attr; + if (!XGetWindowAttributes(display_, id.id(), &attr)) { + LOG(LS_ERROR) << "XGetWindowAttributes() failed"; + return false; + } + return attr.map_state == IsViewable; + } + + bool MoveToFront(const WindowId& id) { + if (!Init()) { + return false; + } + XErrorSuppressor error_suppressor(display_); + unsigned int num_children; + Window* children; + Window parent; + Window root; + + // Find root window to pass event to. + int status = XQueryTree(display_, id.id(), &root, &parent, &children, + &num_children); + if (status == 0) { + LOG(LS_WARNING) << "Failed to query for child windows."; + return false; + } + if (children != NULL) { + XFree(children); + } + + // Move the window to front. + XRaiseWindow(display_, id.id()); + + // Some window managers (e.g., metacity in GNOME) consider it illegal to + // raise a window without also giving it input focus with + // _NET_ACTIVE_WINDOW, so XRaiseWindow() on its own isn't enough. + Atom atom = XInternAtom(display_, "_NET_ACTIVE_WINDOW", True); + if (atom != None) { + XEvent xev; + long event_mask; + + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = id.id(); + xev.xclient.message_type = atom; + + // The format member is set to 8, 16, or 32 and specifies whether the + // data should be viewed as a list of bytes, shorts, or longs. + xev.xclient.format = 32; + + xev.xclient.data.l[0] = 0; + xev.xclient.data.l[1] = 0; + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + + event_mask = SubstructureRedirectMask | SubstructureNotifyMask; + + XSendEvent(display_, root, False, event_mask, &xev); + } + XFlush(display_); + return true; + } + + uint8* GetWindowIcon(const WindowId& id, int* width, int* height) { + if (!Init()) { + return NULL; + } + XErrorSuppressor error_suppressor(display_); + Atom ret_type; + int format; + unsigned long length, bytes_after, size; + unsigned char* data = NULL; + + // Find out the size of the icon data. + if (XGetWindowProperty( + display_, id.id(), net_wm_icon_, 0, 0, False, XA_CARDINAL, + &ret_type, &format, &length, &size, &data) == Success && + data) { + XFree(data); + } else { + LOG(LS_ERROR) << "Failed to get size of the icon."; + return NULL; + } + // Get the icon data, the format is one uint32 each for width and height, + // followed by the actual pixel data. + if (size >= 2 && + XGetWindowProperty( + display_, id.id(), net_wm_icon_, 0, size, False, XA_CARDINAL, + &ret_type, &format, &length, &bytes_after, &data) == Success && + data) { + uint32* data_ptr = reinterpret_cast(data); + int w, h; + w = data_ptr[0]; + h = data_ptr[1]; + if (size < static_cast(w * h + 2)) { + XFree(data); + LOG(LS_ERROR) << "Not a vaild icon."; + return NULL; + } + uint8* rgba = + ArgbToRgba(&data_ptr[2], 0, 0, w, h, w, h, true); + XFree(data); + *width = w; + *height = h; + return rgba; + } else { + LOG(LS_ERROR) << "Failed to get window icon data."; + return NULL; + } + } + + uint8* GetWindowThumbnail(const WindowId& id, int width, int height) { + if (!Init()) { + return NULL; + } + + if (!has_composite_extension_) { + // Without the Xcomposite extension we would only get a good thumbnail if + // the whole window is visible on screen and not covered by any + // other window. This is not something we want so instead, just + // bail out. + LOG(LS_INFO) << "No Xcomposite extension detected."; + return NULL; + } + XErrorSuppressor error_suppressor(display_); + + Window root; + int x; + int y; + unsigned int src_width; + unsigned int src_height; + unsigned int border_width; + unsigned int depth; + + // In addition to needing X11 server-side support for Xcomposite, it + // actually needs to be turned on for this window in order to get a good + // thumbnail. If the user has modern hardware/drivers but isn't using a + // compositing window manager, that won't be the case. Here we + // automatically turn it on for shareable windows so that we can get + // thumbnails. We used to avoid it because the transition is visually ugly, + // but recent window managers don't always redirect windows which led to + // no thumbnails at all, which is a worse experience. + + // Redirect drawing to an offscreen buffer (ie, turn on compositing). + // X11 remembers what has requested this and will turn it off for us when + // we exit. + XCompositeRedirectWindow(display_, id.id(), CompositeRedirectAutomatic); + Pixmap src_pixmap = XCompositeNameWindowPixmap(display_, id.id()); + if (!src_pixmap) { + // Even if the backing pixmap doesn't exist, this still should have + // succeeded and returned a valid handle (it just wouldn't be a handle to + // anything). So this is a real error path. + LOG(LS_ERROR) << "XCompositeNameWindowPixmap() failed"; + return NULL; + } + if (!XGetGeometry(display_, src_pixmap, &root, &x, &y, + &src_width, &src_height, &border_width, + &depth)) { + // If the window does not actually have a backing pixmap, this is the path + // that will "fail", so it's a warning rather than an error. + LOG(LS_WARNING) << "XGetGeometry() failed (probably composite is not in " + << "use)"; + XFreePixmap(display_, src_pixmap); + return NULL; + } + + // If we get to here, then composite is in use for this window and it has a + // valid backing pixmap. + + XWindowAttributes attr; + if (!XGetWindowAttributes(display_, id.id(), &attr)) { + LOG(LS_ERROR) << "XGetWindowAttributes() failed"; + XFreePixmap(display_, src_pixmap); + return NULL; + } + + uint8* data = GetDrawableThumbnail(src_pixmap, + attr.visual, + src_width, + src_height, + width, + height); + XFreePixmap(display_, src_pixmap); + return data; + } + + int GetNumDesktops() { + if (!Init()) { + return -1; + } + + return XScreenCount(display_); + } + + uint8* GetDesktopThumbnail(const DesktopId& id, int width, int height) { + if (!Init()) { + return NULL; + } + XErrorSuppressor error_suppressor(display_); + + Window root_window = id.id(); + XWindowAttributes attr; + if (!XGetWindowAttributes(display_, root_window, &attr)) { + LOG(LS_ERROR) << "XGetWindowAttributes() failed"; + return NULL; + } + + return GetDrawableThumbnail(root_window, + attr.visual, + attr.width, + attr.height, + width, + height); + } + + bool GetDesktopDimensions(const DesktopId& id, int* width, int* height) { + if (!Init()) { + return false; + } + XErrorSuppressor error_suppressor(display_); + XWindowAttributes attr; + if (!XGetWindowAttributes(display_, id.id(), &attr)) { + LOG(LS_ERROR) << "XGetWindowAttributes() failed"; + return false; + } + *width = attr.width; + *height = attr.height; + return true; + } + + private: + uint8* GetDrawableThumbnail(Drawable src_drawable, + Visual* visual, + int src_width, + int src_height, + int dst_width, + int dst_height) { + if (!has_render_extension_) { + // Without the Xrender extension we would have to read the full window and + // scale it down in our process. Xrender is over a decade old so we aren't + // going to expend effort to support that situation. We still need to + // check though because probably some virtual VNC displays are in this + // category. + LOG(LS_INFO) << "No Xrender extension detected."; + return NULL; + } + + XRenderPictFormat* format = XRenderFindVisualFormat(display_, + visual); + if (!format) { + LOG(LS_ERROR) << "XRenderFindVisualFormat() failed"; + return NULL; + } + + // Create a picture to reference the window pixmap. + XRenderPictureAttributes pa; + pa.subwindow_mode = IncludeInferiors; // Don't clip child widgets + Picture src = XRenderCreatePicture(display_, + src_drawable, + format, + CPSubwindowMode, + &pa); + if (!src) { + LOG(LS_ERROR) << "XRenderCreatePicture() failed"; + return NULL; + } + + // Create a picture to reference the destination pixmap. + Pixmap dst_pixmap = XCreatePixmap(display_, + src_drawable, + dst_width, + dst_height, + format->depth); + if (!dst_pixmap) { + LOG(LS_ERROR) << "XCreatePixmap() failed"; + XRenderFreePicture(display_, src); + return NULL; + } + + Picture dst = XRenderCreatePicture(display_, dst_pixmap, format, 0, NULL); + if (!dst) { + LOG(LS_ERROR) << "XRenderCreatePicture() failed"; + XFreePixmap(display_, dst_pixmap); + XRenderFreePicture(display_, src); + return NULL; + } + + // Clear the background. + XRenderColor transparent = {0}; + XRenderFillRectangle(display_, + PictOpSrc, + dst, + &transparent, + 0, + 0, + dst_width, + dst_height); + + // Calculate how much we need to scale the image. + double scale_x = static_cast(dst_width) / + static_cast(src_width); + double scale_y = static_cast(dst_height) / + static_cast(src_height); + double scale = rtc::_min(scale_y, scale_x); + + int scaled_width = round(src_width * scale); + int scaled_height = round(src_height * scale); + + // Render the thumbnail centered on both axis. + int centered_x = (dst_width - scaled_width) / 2; + int centered_y = (dst_height - scaled_height) / 2; + + // Scaling matrix + XTransform xform = { { + { XDoubleToFixed(1), XDoubleToFixed(0), XDoubleToFixed(0) }, + { XDoubleToFixed(0), XDoubleToFixed(1), XDoubleToFixed(0) }, + { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(scale) } + } }; + XRenderSetPictureTransform(display_, src, &xform); + + // Apply filter to smooth out the image. + XRenderSetPictureFilter(display_, src, FilterBest, NULL, 0); + + // Render the image to the destination picture. + XRenderComposite(display_, + PictOpSrc, + src, + None, + dst, + 0, + 0, + 0, + 0, + centered_x, + centered_y, + scaled_width, + scaled_height); + + // Get the pixel data from the X server. TODO: XGetImage + // might be slow here, compare with ShmGetImage. + XImage* image = XGetImage(display_, + dst_pixmap, + 0, + 0, + dst_width, + dst_height, + AllPlanes, ZPixmap); + uint8* data = ArgbToRgba(reinterpret_cast(image->data), + centered_x, + centered_y, + scaled_width, + scaled_height, + dst_width, + dst_height, + false); + XDestroyImage(image); + XRenderFreePicture(display_, dst); + XFreePixmap(display_, dst_pixmap); + XRenderFreePicture(display_, src); + return data; + } + + uint8* ArgbToRgba(uint32* argb_data, int x, int y, int w, int h, + int stride_x, int stride_y, bool has_alpha) { + uint8* p; + int len = stride_x * stride_y * 4; + uint8* data = new uint8[len]; + memset(data, 0, len); + p = data + 4 * (y * stride_x + x); + for (int i = 0; i < h; ++i) { + for (int j = 0; j < w; ++j) { + uint32 argb; + uint32 rgba; + argb = argb_data[stride_x * (y + i) + x + j]; + rgba = (argb << 8) | (argb >> 24); + *p = rgba >> 24; + ++p; + *p = (rgba >> 16) & 0xff; + ++p; + *p = (rgba >> 8) & 0xff; + ++p; + *p = has_alpha ? rgba & 0xFF : 0xFF; + ++p; + } + p += (stride_x - w) * 4; + } + return data; + } + + bool EnumerateScreenWindows(WindowDescriptionList* descriptions, int screen) { + Window parent; + Window *children; + int status; + unsigned int num_children; + Window root_window = XRootWindow(display_, screen); + status = XQueryTree(display_, root_window, &root_window, &parent, &children, + &num_children); + if (status == 0) { + LOG(LS_ERROR) << "Failed to query for child windows."; + return false; + } + for (unsigned int i = 0; i < num_children; ++i) { + // Iterate in reverse order to display windows from front to back. +#ifdef CHROMEOS + // TODO(jhorwich): Short-term fix for crbug.com/120229: Don't need to + // filter, just return all windows and let the picker scan through them. + Window app_window = children[num_children - 1 - i]; +#else + Window app_window = GetApplicationWindow(children[num_children - 1 - i]); +#endif + if (app_window && + !X11WindowPicker::IsDesktopElement(display_, app_window)) { + std::string title; + if (GetWindowTitle(app_window, &title)) { + WindowId id(app_window); + WindowDescription desc(id, title); + descriptions->push_back(desc); + } + } + } + if (children != NULL) { + XFree(children); + } + return true; + } + + bool GetWindowTitle(Window window, std::string* title) { + int status; + bool result = false; + XTextProperty window_name; + window_name.value = NULL; + if (window) { + status = XGetWMName(display_, window, &window_name); + if (status && window_name.value && window_name.nitems) { + int cnt; + char **list = NULL; + status = Xutf8TextPropertyToTextList(display_, &window_name, &list, + &cnt); + if (status >= Success && cnt && *list) { + if (cnt > 1) { + LOG(LS_INFO) << "Window has " << cnt + << " text properties, only using the first one."; + } + *title = *list; + result = true; + } + if (list != NULL) { + XFreeStringList(list); + } + } + if (window_name.value != NULL) { + XFree(window_name.value); + } + } + return result; + } + + Window GetApplicationWindow(Window window) { + Window root, parent; + Window app_window = 0; + Window *children; + unsigned int num_children; + Atom type = None; + int format; + unsigned long nitems, after; + unsigned char *data; + + int ret = XGetWindowProperty(display_, window, + wm_state_, 0L, 2, + False, wm_state_, &type, &format, + &nitems, &after, &data); + if (ret != Success) { + LOG(LS_ERROR) << "XGetWindowProperty failed with return code " << ret + << " for window " << window << "."; + return 0; + } + if (type != None) { + int64 state = static_cast(*data); + XFree(data); + return state == NormalState ? window : 0; + } + XFree(data); + if (!XQueryTree(display_, window, &root, &parent, &children, + &num_children)) { + LOG(LS_ERROR) << "Failed to query for child windows although window" + << "does not have a valid WM_STATE."; + return 0; + } + for (unsigned int i = 0; i < num_children; ++i) { + app_window = GetApplicationWindow(children[i]); + if (app_window) { + break; + } + } + if (children != NULL) { + XFree(children); + } + return app_window; + } + + Atom wm_state_; + Atom net_wm_icon_; + Display* display_; + bool has_composite_extension_; + bool has_render_extension_; +}; + +X11WindowPicker::X11WindowPicker() : enumerator_(new XWindowEnumerator()) { +} + +X11WindowPicker::~X11WindowPicker() { +} + +bool X11WindowPicker::IsDesktopElement(_XDisplay* display, Window window) { + if (window == 0) { + LOG(LS_WARNING) << "Zero is never a valid window."; + return false; + } + + // First look for _NET_WM_WINDOW_TYPE. The standard + // (http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2760306) + // says this hint *should* be present on all windows, and we use the existence + // of _NET_WM_WINDOW_TYPE_NORMAL in the property to indicate a window is not + // a desktop element (that is, only "normal" windows should be shareable). + Atom window_type_atom = XInternAtom(display, "_NET_WM_WINDOW_TYPE", True); + XWindowProperty window_type(display, window, window_type_atom); + if (window_type.succeeded() && window_type.size() > 0) { + Atom normal_window_type_atom = XInternAtom( + display, "_NET_WM_WINDOW_TYPE_NORMAL", True); + uint32_t* end = window_type.data() + window_type.size(); + bool is_normal = (end != std::find( + window_type.data(), end, normal_window_type_atom)); + return !is_normal; + } + + // Fall back on using the hint. + XClassHint class_hint; + Status s = XGetClassHint(display, window, &class_hint); + bool result = false; + if (s == 0) { + // No hints, assume this is a normal application window. + return result; + } + static const std::string gnome_panel("gnome-panel"); + static const std::string desktop_window("desktop_window"); + + if (gnome_panel.compare(class_hint.res_name) == 0 || + desktop_window.compare(class_hint.res_name) == 0) { + result = true; + } + XFree(class_hint.res_name); + XFree(class_hint.res_class); + return result; +} + +bool X11WindowPicker::Init() { + return enumerator_->Init(); +} + +bool X11WindowPicker::GetWindowList(WindowDescriptionList* descriptions) { + return enumerator_->EnumerateWindows(descriptions); +} + +bool X11WindowPicker::GetDesktopList(DesktopDescriptionList* descriptions) { + return enumerator_->EnumerateDesktops(descriptions); +} + +bool X11WindowPicker::IsVisible(const WindowId& id) { + return enumerator_->IsVisible(id); +} + +bool X11WindowPicker::MoveToFront(const WindowId& id) { + return enumerator_->MoveToFront(id); +} + + +uint8* X11WindowPicker::GetWindowIcon(const WindowId& id, int* width, + int* height) { + return enumerator_->GetWindowIcon(id, width, height); +} + +uint8* X11WindowPicker::GetWindowThumbnail(const WindowId& id, int width, + int height) { + return enumerator_->GetWindowThumbnail(id, width, height); +} + +int X11WindowPicker::GetNumDesktops() { + return enumerator_->GetNumDesktops(); +} + +uint8* X11WindowPicker::GetDesktopThumbnail(const DesktopId& id, + int width, + int height) { + return enumerator_->GetDesktopThumbnail(id, width, height); +} + +bool X11WindowPicker::GetDesktopDimensions(const DesktopId& id, int* width, + int* height) { + return enumerator_->GetDesktopDimensions(id, width, height); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/x11windowpicker.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/x11windowpicker.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/x11windowpicker.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/x11windowpicker.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_LINUXWINDOWPICKER_H_ +#define WEBRTC_BASE_LINUXWINDOWPICKER_H_ + +#include "webrtc/base/basictypes.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/windowpicker.h" + +// Avoid include . +struct _XDisplay; +typedef unsigned long Window; + +namespace rtc { + +class XWindowEnumerator; + +class X11WindowPicker : public WindowPicker { + public: + X11WindowPicker(); + ~X11WindowPicker(); + + static bool IsDesktopElement(_XDisplay* display, Window window); + + virtual bool Init(); + virtual bool IsVisible(const WindowId& id); + virtual bool MoveToFront(const WindowId& id); + virtual bool GetWindowList(WindowDescriptionList* descriptions); + virtual bool GetDesktopList(DesktopDescriptionList* descriptions); + virtual bool GetDesktopDimensions(const DesktopId& id, int* width, + int* height); + uint8* GetWindowIcon(const WindowId& id, int* width, int* height); + uint8* GetWindowThumbnail(const WindowId& id, int width, int height); + int GetNumDesktops(); + uint8* GetDesktopThumbnail(const DesktopId& id, int width, int height); + + private: + scoped_ptr enumerator_; +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_LINUXWINDOWPICKER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/x11windowpicker_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/x11windowpicker_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/base/x11windowpicker_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/base/x11windowpicker_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright 2010 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/gunit.h" +#include "webrtc/base/x11windowpicker.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/testutils.h" +#include "webrtc/base/windowpicker.h" + +#if !defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID) +#error Only for Linux +#endif + +namespace rtc { + +TEST(X11WindowPickerTest, TestGetWindowList) { + MAYBE_SKIP_SCREENCAST_TEST(); + X11WindowPicker window_picker; + WindowDescriptionList descriptions; + window_picker.Init(); + window_picker.GetWindowList(&descriptions); +} + +TEST(X11WindowPickerTest, TestGetDesktopList) { + MAYBE_SKIP_SCREENCAST_TEST(); + X11WindowPicker window_picker; + DesktopDescriptionList descriptions; + EXPECT_TRUE(window_picker.Init()); + EXPECT_TRUE(window_picker.GetDesktopList(&descriptions)); + EXPECT_TRUE(descriptions.size() > 0); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/build/apk_tests.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/build/apk_tests.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/build/apk_tests.gyp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/build/apk_tests.gyp 2015-02-03 14:33:34.000000000 +0000 @@ -29,7 +29,7 @@ '<(webrtc_root)/modules/modules.gyp:audio_decoder_unittests', ], 'includes': [ - '../../../build/apk_test.gypi', + '../../build/apk_test.gypi', ], }, { @@ -43,7 +43,7 @@ '<(webrtc_root)/common_audio/common_audio.gyp:common_audio_unittests', ], 'includes': [ - '../../../build/apk_test.gypi', + '../../build/apk_test.gypi', ], }, { @@ -57,7 +57,7 @@ '<(webrtc_root)/common_video/common_video_unittests.gyp:common_video_unittests', ], 'includes': [ - '../../../build/apk_test.gypi', + '../../build/apk_test.gypi', ], }, { @@ -71,7 +71,7 @@ '<(webrtc_root)/modules/modules.gyp:modules_tests', ], 'includes': [ - '../../../build/apk_test.gypi', + '../../build/apk_test.gypi', ], }, { @@ -85,21 +85,7 @@ '<(webrtc_root)/modules/modules.gyp:modules_unittests', ], 'includes': [ - '../../../build/apk_test.gypi', - ], - }, - { - 'target_name': 'neteq_unittests_apk', - 'type': 'none', - 'variables': { - 'test_suite_name': 'neteq_unittests', - 'input_shlib_path': '<(SHARED_LIB_DIR)/<(SHARED_LIB_PREFIX)neteq_unittests<(SHARED_LIB_SUFFIX)', - }, - 'dependencies': [ - '<(webrtc_root)/modules/modules.gyp:neteq_unittests', - ], - 'includes': [ - '../../../build/apk_test.gypi', + '../../build/apk_test.gypi', ], }, { @@ -113,7 +99,7 @@ '<(webrtc_root)/system_wrappers/source/system_wrappers_tests.gyp:system_wrappers_unittests', ], 'includes': [ - '../../../build/apk_test.gypi', + '../../build/apk_test.gypi', ], }, { @@ -127,7 +113,7 @@ '<(webrtc_root)/test/test.gyp:test_support_unittests', ], 'includes': [ - '../../../build/apk_test.gypi', + '../../build/apk_test.gypi', ], }, { @@ -141,7 +127,7 @@ '<(webrtc_root)/tools/tools.gyp:tools_unittests', ], 'includes': [ - '../../../build/apk_test.gypi', + '../../build/apk_test.gypi', ], }, { @@ -155,7 +141,7 @@ '<(webrtc_root)/video_engine/video_engine.gyp:video_engine_core_unittests', ], 'includes': [ - '../../../build/apk_test.gypi', + '../../build/apk_test.gypi', ], }, { @@ -169,7 +155,7 @@ '<(webrtc_root)/webrtc.gyp:video_engine_tests', ], 'includes': [ - '../../../build/apk_test.gypi', + '../../build/apk_test.gypi', ], }, { @@ -183,7 +169,7 @@ '<(webrtc_root)/voice_engine/voice_engine.gyp:voice_engine_unittests', ], 'includes': [ - '../../../build/apk_test.gypi', + '../../build/apk_test.gypi', ], }, { @@ -197,9 +183,52 @@ '<(webrtc_root)/webrtc.gyp:webrtc_perf_tests', ], 'includes': [ - '../../../build/apk_test.gypi', + '../../build/apk_test.gypi', ], - }, + }, + { + 'target_name': 'audio_codec_speed_tests_apk', + 'type': 'none', + 'variables': { + 'test_suite_name': 'audio_codec_speed_tests', + 'input_shlib_path': '<(SHARED_LIB_DIR)/<(SHARED_LIB_PREFIX)audio_codec_speed_tests<(SHARED_LIB_SUFFIX)', + }, + 'dependencies': [ + '<(webrtc_root)/modules/modules.gyp:audio_codec_speed_tests', + ], + 'includes': [ + '../../build/apk_test.gypi', + ], + }, + { + 'target_name': 'video_capture_tests_apk', + 'type': 'none', + 'variables': { + 'test_suite_name': 'video_capture_tests', + 'input_shlib_path': '<(SHARED_LIB_DIR)/<(SHARED_LIB_PREFIX)video_capture_tests<(SHARED_LIB_SUFFIX)', + }, + 'dependencies': [ + '<(webrtc_root)/modules/modules.gyp:video_capture_tests', + 'video_capture_java', + ], + 'includes': [ + '../../build/apk_test.gypi', + ], + }, + { + # Used only by video_capture_tests_apk above, and impossible to use in the + # standalone build, which is why it's declared here instead of under + # modules/video_capture/ (to avoid the need for a forked _noop.gyp file + # like this file has; see comment at the top of this file). + 'target_name': 'video_capture_java', + 'type': 'none', + 'variables': { + 'java_in_dir': '<(webrtc_root)/modules/video_capture/android/java', + }, + 'includes': [ + '../../build/java.gypi', + ], + }, ], } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/build/apk_tests_noop.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/build/apk_tests_noop.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/build/apk_tests_noop.gyp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/build/apk_tests_noop.gyp 2015-02-03 14:33:34.000000000 +0000 @@ -30,10 +30,6 @@ 'type': 'none', }, { - 'target_name': 'neteq_unittests_apk', - 'type': 'none', - }, - { 'target_name': 'system_wrappers_unittests_apk', 'type': 'none', }, @@ -61,5 +57,13 @@ 'target_name': 'webrtc_perf_tests_apk', 'type': 'none', }, + { + 'target_name': 'audio_codec_speed_tests_apk', + 'type': 'none', + }, + { + 'target_name': 'video_capture_tests_apk', + 'type': 'none', + }, ], } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/build/common.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/build/common.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/build/common.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/build/common.gypi 2015-02-03 14:33:34.000000000 +0000 @@ -23,13 +23,13 @@ ['build_with_chromium==1', { 'build_with_libjingle': 1, 'webrtc_root%': '<(DEPTH)/third_party/webrtc', - 'apk_tests_path%': '<(DEPTH)/third_party/webrtc/build/apk_tests.gyp', + 'apk_tests_path%': '<(DEPTH)/third_party/webrtc/build/apk_tests_noop.gyp', 'modules_java_gyp_path%': '<(DEPTH)/third_party/webrtc/modules/modules_java_chromium.gyp', 'gen_core_neon_offsets_gyp%': '<(DEPTH)/third_party/webrtc/modules/audio_processing/gen_core_neon_offsets_chromium.gyp', }, { 'build_with_libjingle%': 0, 'webrtc_root%': '<(DEPTH)/webrtc', - 'apk_tests_path%': '<(DEPTH)/webrtc/build/apk_test_noop.gyp', + 'apk_tests_path%': '<(DEPTH)/webrtc/build/apk_tests.gyp', 'modules_java_gyp_path%': '<(DEPTH)/webrtc/modules/modules_java.gyp', 'gen_core_neon_offsets_gyp%':'<(DEPTH)/webrtc/modules/audio_processing/gen_core_neon_offsets.gyp', }], @@ -42,6 +42,7 @@ 'modules_java_gyp_path%': '<(modules_java_gyp_path)', 'gen_core_neon_offsets_gyp%': '<(gen_core_neon_offsets_gyp)', 'webrtc_vp8_dir%': '<(webrtc_root)/modules/video_coding/codecs/vp8', + 'webrtc_vp9_dir%': '<(webrtc_root)/modules/video_coding/codecs/vp9', 'webrtc_h264_dir%': '<(webrtc_root)/modules/video_coding/codecs/h264', 'rbe_components_path%': '<(webrtc_root)/modules/remote_bitrate_estimator', 'include_g711%': 1, @@ -58,6 +59,7 @@ 'modules_java_gyp_path%': '<(modules_java_gyp_path)', 'gen_core_neon_offsets_gyp%': '<(gen_core_neon_offsets_gyp)', 'webrtc_vp8_dir%': '<(webrtc_vp8_dir)', + 'webrtc_vp9_dir%': '<(webrtc_vp9_dir)', 'webrtc_h264_dir%': '<(webrtc_h264_dir)', 'include_g711%': '<(include_g711)', @@ -67,7 +69,14 @@ 'include_isac%': '<(include_isac)', 'include_pcm16b%': '<(include_pcm16b)', + 'rtc_relative_path%': 1, 'rbe_components_path%': '<(rbe_components_path)', + 'external_libraries%': '0', + 'json_root%': '<(DEPTH)/third_party/jsoncpp/source/include/', + # openssl needs to be defined or gyp will complain. Is is only used when + # when providing external libraries so just use current directory as a + # placeholder. + 'ssl_root%': '.', # The Chromium common.gypi we use treats all gyp files without # chromium_code==1 as third party code. This disables many of the @@ -77,6 +86,9 @@ # third party code will still have the reduced warning settings. 'chromium_code': 1, + # Set to 1 to enable code coverage on Linux using the gcov library. + 'coverage%': 0, + # Remote bitrate estimator logging/plotting. 'enable_bwe_test_logging%': 0, @@ -96,9 +108,14 @@ 'enable_protobuf%': 1, # Disable these to not build components which can be externally provided. + 'build_json%': 1, 'build_libjpeg%': 1, 'build_libyuv%': 1, 'build_libvpx%': 1, + 'build_ssl%': 1, + + # Disable by default + 'have_dbus_glib%': 0, # Enable to use the Mozilla internal settings. 'build_with_mozilla%': 0, @@ -107,11 +124,20 @@ # Define MIPS architecture variant, MIPS DSP variant and MIPS FPU # This may be subject to change in accordance to Chromium's MIPS flags - 'mips_arch_variant%': 'mips32r1', 'mips_dsp_rev%': 0, 'mips_fpu%' : 1, 'enable_android_opensl%': 1, + # Link-Time Optimizations + # Executes code generation at link-time instead of compile-time + # https://gcc.gnu.org/wiki/LinkTimeOptimization + 'use_lto%': 0, + + # Defer ssl perference to that specified through sslconfig.h instead of + # choosing openssl or nss directly. In practice, this can be used to + # enable schannel on windows. + 'use_legacy_ssl_defaults%': 0, + 'conditions': [ ['build_with_chromium==1', { # Exclude pulse audio on Chromium since its prerequisites don't require @@ -121,12 +147,6 @@ # Exclude internal ADM since Chromium uses its own IO handling. 'include_internal_audio_device%': 0, - # Exclude internal VCM in Chromium build. - 'include_internal_video_capture%': 0, - - # Exclude internal video render module in Chromium build. - 'include_internal_video_render%': 0, - # lazily allocate the ~4MB of trace message buffers if set 'enable_lazy_trace_alloc%': 0, @@ -139,8 +159,6 @@ 'include_pulse_audio%': 1, 'include_internal_audio_device%': 1, - 'include_internal_video_capture%': 1, - 'include_internal_video_render%': 1, 'include_ndk_cpu_features%': 0, }], ['build_with_libjingle==1', { @@ -168,18 +186,19 @@ ['OS=="ios"', { 'build_libjpeg%': 0, 'enable_protobuf%': 0, - 'include_tests%': 0, }], ['target_arch=="arm" or target_arch=="armv7"', { 'prefer_fixed_point%': 1, }], + ['OS!="ios" and (target_arch!="arm" or arm_version>=7) and build_with_mozilla==0', { + 'rtc_use_openmax_dl%': 1, + }, { + 'rtc_use_openmax_dl%': 0, + }], ], # conditions }, 'target_defaults': { 'include_dirs': [ - # Allow includes to be prefixed with webrtc/ in case it is not an - # immediate subdirectory of <(DEPTH). - '../..', # To include the top-level directory when building in Chrome, so we can # use full paths (e.g. headers inside testing/ or third_party/). '<(DEPTH)', @@ -199,6 +218,17 @@ 'WEBRTC_MOZILLA_BUILD', ], }], + ['have_dbus_glib==1', { + 'defines': [ + 'HAVE_DBUS_GLIB', + ], + 'cflags': [ + '\n" + +// Audio processing +// https://code.google.com/p/webrtc/issues/detail?id=2521 for details. +"race:webrtc/modules/audio_processing/aec/aec_core.c\n" +"race:webrtc/modules/audio_processing/aec/aec_rdft.c\n" + +// rtc_unittest +// https://code.google.com/p/webrtc/issues/detail?id=3911 for details. +"race:rtc::FireAndForgetAsyncClosure::Execute\n" +"race:rtc::MessageQueueManager::Clear\n" +"race:rtc::Thread::Clear\n" + +// libjingle_p2p_unittest +// https://code.google.com/p/webrtc/issues/detail?id=2079 +"race:webrtc/base/testclient.cc\n" +"race:webrtc/base/virtualsocketserver.cc\n" +"race:talk/p2p/base/stunserver_unittest.cc\n" + +// libjingle_unittest +// https://code.google.com/p/webrtc/issues/detail?id=2080 +"race:webrtc/base/logging.cc\n" +"race:webrtc/base/sharedexclusivelock_unittest.cc\n" +"race:webrtc/base/signalthread_unittest.cc\n" + +// third_party/usrsctp +// TODO(jiayl): https://code.google.com/p/webrtc/issues/detail?id=3492 +"race:user_sctp_timer_iterate\n" + +// Potential deadlocks detected after roll in r6516. +// https://code.google.com/p/webrtc/issues/detail?id=3509 +"deadlock:webrtc::ProcessThreadImpl::RegisterModule\n" +"deadlock:webrtc::RTCPReceiver::SetSsrcs\n" +"deadlock:webrtc::RTPSenderAudio::RegisterAudioPayload\n" +"deadlock:webrtc::test::UdpSocketManagerPosixImpl::RemoveSocket\n" +"deadlock:webrtc::vcm::VideoReceiver::RegisterPacketRequestCallback\n" +"deadlock:webrtc::ViECaptureImpl::ConnectCaptureDevice\n" +"deadlock:webrtc::ViEChannel::StartSend\n" +"deadlock:webrtc::ViECodecImpl::GetSendSideDelay\n" +"deadlock:webrtc::ViEEncoder::OnLocalSsrcChanged\n" +"deadlock:webrtc::ViESender::RegisterSendTransport\n" + +// End of suppressions. +; // Please keep this semicolon. + +#endif // THREAD_SANITIZER diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/build/webrtc.gni thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/build/webrtc.gni --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/build/webrtc.gni 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/build/webrtc.gni 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,116 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("//build/config/arm.gni") + +declare_args() { + # Assume Chromium build for now, since that's the priority case for getting GN + # up and running with WebRTC. + build_with_chromium = true + build_with_libjingle = true + + # Disable this to avoid building the Opus audio codec. + rtc_include_opus = true + + # Used to specify an external Jsoncpp include path when not compiling the + # library that comes with WebRTC (i.e. rtc_build_json == 0). + rtc_jsoncpp_root = "//third_party/jsoncpp/source/include" + + # Used to specify an external OpenSSL include path when not compiling the + # library that comes with WebRTC (i.e. rtc_build_ssl == 0). + rtc_ssl_root = "" + + # Adds video support to dependencies shared by voice and video engine. + # This should normally be enabled; the intended use is to disable only + # when building voice engine exclusively. + rtc_enable_video = true + + # Selects fixed-point code where possible. + rtc_prefer_fixed_point = false + + # Enable data logging. Produces text files with data logged within engines + # which can be easily parsed for offline processing. + rtc_enable_data_logging = false + + # Enables the use of protocol buffers for debug recordings. + rtc_enable_protobuf = true + + # Disable these to not build components which can be externally provided. + rtc_build_json = true + rtc_build_libjpeg = true + rtc_build_libyuv = true + rtc_build_libvpx = true + rtc_build_ssl = true + + # Disable by default. + rtc_have_dbus_glib = false + + # Enable to use the Mozilla internal settings. + build_with_mozilla = false + + # Define MIPS architecture variant, MIPS DSP variant and MIPS FPU + # This may be subject to change in accordance to Chromium's MIPS flags + mips_arch_variant = "mips32r1" + mips_dsp_rev = 0 + mips_fpu = true + + rtc_enable_android_opensl = true + + # Link-Time Optimizations. + # Executes code generation at link-time instead of compile-time. + # https://gcc.gnu.org/wiki/LinkTimeOptimization + rtc_use_lto = false + + if (build_with_chromium) { + # Exclude pulse audio on Chromium since its prerequisites don't require + # pulse audio. + rtc_include_pulse_audio = false + + # Exclude internal ADM since Chromium uses its own IO handling. + rtc_include_internal_audio_device = false + + } else { + # Settings for the standalone (not-in-Chromium) build. + + # TODO(andrew): For now, disable the Chrome plugins, which causes a + # flood of chromium-style warnings. Investigate enabling them: + # http://code.google.com/p/webrtc/issues/detail?id=163 + clang_use_chrome_plugins = false + + rtc_include_pulse_audio = true + rtc_include_internal_audio_device = true + } + + if (build_with_libjingle) { + rtc_include_tests = false + rtc_restrict_logging = true + } else { + rtc_include_tests = true + rtc_restrict_logging = false + } + + if (is_ios) { + rtc_build_libjpeg = false + rtc_enable_protobuf = false + } + + if (cpu_arch == "arm") { + rtc_prefer_fixed_point = true + } + + if (!is_ios && (cpu_arch != "arm" || arm_version >= 7)) { + rtc_use_openmax_dl = true + } else { + rtc_use_openmax_dl = false + } + + # WebRTC builds ARM v7 Neon instruction set optimized code for both iOS and + # Android, which is why we currently cannot use the variables in + # //build/config/arm.gni (since it disables Neon for Android). + rtc_build_armv7_neon = (cpu_arch == "arm" && arm_version == 7) +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/BUILD.gn 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,245 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +# TODO(kjellander): Rebase this to webrtc/build/common.gypi changes after r6330. + +import("//build/config/arm.gni") +import("//build/config/crypto.gni") +import("//build/config/linux/pkg_config.gni") +import("build/webrtc.gni") + +# Contains the defines and includes in common.gypi that are duplicated both as +# target_defaults and direct_dependent_settings. +config("common_inherited_config") { + defines = [] + if (build_with_mozilla) { + defines += [ "WEBRTC_MOZILLA_BUILD" ] + } + if (build_with_chromium) { + defines = [ + "WEBRTC_CHROMIUM_BUILD", + ] + include_dirs = [ + # overrides must be included first as that is the mechanism for + # selecting the override headers in Chromium. + "overrides", + # Allow includes to be prefixed with webrtc/ in case it is not an + # immediate subdirectory of the top-level. + "..", + ] + } + if (is_posix) { + defines += [ "WEBRTC_POSIX" ] + } + if (is_ios) { + defines += [ + "WEBRTC_MAC", + "WEBRTC_IOS", + ] + } + if (is_linux) { + defines += [ "WEBRTC_LINUX" ] + } + if (is_mac) { + defines += [ "WEBRTC_MAC" ] + } + if (is_win) { + defines += [ "WEBRTC_WIN" ] + } + if (is_android) { + defines += [ + "WEBRTC_LINUX", + "WEBRTC_ANDROID", + ] + if (rtc_enable_android_opensl) { + defines += [ "WEBRTC_ANDROID_OPENSLES" ] + } + } +} + +if (rtc_have_dbus_glib) { + pkg_config("dbus-glib") { + packages = [ "dbus-glib-1" ] + } +} + +config("common_config") { + cflags = [] + cflags_cc = [] + if (rtc_restrict_logging) { + defines = [ "WEBRTC_RESTRICT_LOGGING" ] + } + + if (rtc_have_dbus_glib) { + defines += [ "HAVE_DBUS_GLIB" ] + # TODO(kjellander): Investigate this, it seems like include + # is still not found even if the execution of + # build/config/linux/pkg-config.py dbus-glib-1 returns correct include + # dirs on Linux. + all_dependent_configs = [ "dbus-glib" ] + } + + if (rtc_enable_video) { + defines += [ "WEBRTC_MODULE_UTILITY_VIDEO" ] + } + + if (build_with_chromium) { + defines += [ "LOGGING_INSIDE_WEBRTC" ] + } else { + if (is_posix) { + # -Wextra is currently disabled in Chromium"s common.gypi. Enable + # for targets that can handle it. For Android/arm64 right now + # there will be an "enumeral and non-enumeral type in conditional + # expression" warning in android_tools/ndk_experimental"s version + # of stlport. + # See: https://code.google.com/p/chromium/issues/detail?id=379699 + if (cpu_arch != "arm64" || !is_android) { + cflags = [ + "-Wextra", + # We need to repeat some flags from Chromium"s common.gypi + # here that get overridden by -Wextra. + "-Wno-unused-parameter", + "-Wno-missing-field-initializers", + "-Wno-strict-overflow", + ] + cflags_cc = [ + "-Wnon-virtual-dtor", + # This is enabled for clang; enable for gcc as well. + "-Woverloaded-virtual", + ] + } + } + + if (is_clang) { + cflags += [ "-Wthread-safety" ] + } + } + + if (cpu_arch == "arm64") { + defines += [ "WEBRTC_ARCH_ARM" ] + } + + if (cpu_arch == "arm") { + defines += [ "WEBRTC_ARCH_ARM" ] + if (arm_version == 7) { + defines += [ "WEBRTC_ARCH_ARM_V7" ] + if (arm_use_neon) { + defines += [ "WEBRTC_ARCH_ARM_NEON" ] + } else { + defines += [ "WEBRTC_DETECT_ARM_NEON" ] + } + } + } + + if (cpu_arch == "mipsel") { + defines += [ "MIPS32_LE" ] + if (mips_fpu) { + defines += [ "MIPS_FPU_LE" ] + cflags += [ "-mhard-float" ] + } else { + cflags += [ "-msoft-float" ] + } + if (mips_arch_variant == "mips32r2") { + defines += [ "MIPS32_R2_LE" ] + cflags += [ "-mips32r2" ] + cflags_cc += [ "-mips32r2" ] + } + if (mips_dsp_rev == 1) { + defines += [ "MIPS_DSP_R1_LE" ] + cflags += [ "-mdsp" ] + cflags_cc += [ "-mdsp" ] + } else if (mips_dsp_rev == 2) { + defines += [ + "MIPS_DSP_R1_LE", + "MIPS_DSP_R2_LE", + ] + cflags += [ "-mdspr2" ] + cflags_cc += [ "-mdspr2" ] + } + } + + # TODO(kjellander): Handle warnings on Windows where WebRTC differ from the + # default warnings set in build/config/compiler/BUILD.gn. + + if (is_android && is_clang) { + # The Android NDK doesn"t provide optimized versions of these + # functions. Ensure they are disabled for all compilers. + cflags += [ + "-fno-builtin-cos", + "-fno-builtin-sin", + "-fno-builtin-cosf", + "-fno-builtin-sinf", + ] + } +} + +static_library("webrtc") { + sources = [ + "call.h", + "config.h", + "experiments.h", + "frame_callback.h", + "transport.h", + ] + + configs += [ ":common_config" ] + public_configs = [ ":common_inherited_config"] + + deps = [ + ":webrtc_common", + "base:webrtc_base", + "common_audio", + "common_video", + "modules/audio_coding", + "modules/audio_conference_mixer", + "modules/audio_device", + "modules/audio_processing", + "modules/bitrate_controller", + "modules/desktop_capture", + "modules/media_file", + "modules/rtp_rtcp", + "modules/utility", + "modules/video_capture", + "modules/video_coding", + "modules/video_processing", + "modules/video_render", + "system_wrappers", + "tools", + "video", + "video_engine", + "voice_engine", + ] +} + +if (!build_with_chromium) { + executable("webrtc_tests") { + testonly = true + deps = [ + ":webrtc", + "modules/video_render:video_render_internal_impl", + "modules/video_capture:video_capture_internal_impl", + "test", + ] + } +} + +source_set("webrtc_common") { + sources = [ + "config.h", + "config.cc", + ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + configs += [ ":common_config" ] + public_configs = [ ":common_inherited_config" ] +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/call.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/call.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/call.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/call.h 2015-02-03 14:33:34.000000000 +0000 @@ -25,23 +25,30 @@ class PacketReceiver { public: - virtual bool DeliverPacket(const uint8_t* packet, size_t length) = 0; + enum DeliveryStatus { + DELIVERY_OK, + DELIVERY_UNKNOWN_SSRC, + DELIVERY_PACKET_ERROR, + }; + + virtual DeliveryStatus DeliverPacket(const uint8_t* packet, + size_t length) = 0; protected: virtual ~PacketReceiver() {} }; // Callback interface for reporting when a system overuse is detected. -// The detection is based on the jitter of incoming captured frames. -class OveruseCallback { +class LoadObserver { public: - // Called as soon as an overuse is detected. - virtual void OnOveruse() = 0; - // Called periodically when the system is not overused any longer. - virtual void OnNormalUse() = 0; + enum Load { kOveruse, kUnderuse }; + + // Triggered when overuse is detected or when we believe the system can take + // more load. + virtual void OnLoadUpdate(Load load) = 0; protected: - virtual ~OveruseCallback() {} + virtual ~LoadObserver() {} }; // A Call instance can contain several send and/or receive streams. All streams @@ -49,14 +56,19 @@ // etc. class Call { public: + enum NetworkState { + kNetworkUp, + kNetworkDown, + }; struct Config { explicit Config(newapi::Transport* send_transport) : webrtc_config(NULL), send_transport(send_transport), voice_engine(NULL), - trace_callback(NULL), - trace_filter(kTraceDefault), - overuse_callback(NULL) {} + overuse_callback(NULL), + stream_start_bitrate_bps(kDefaultStartBitrateBps) {} + + static const int kDefaultStartBitrateBps; webrtc::Config* webrtc_config; @@ -65,12 +77,23 @@ // VoiceEngine used for audio/video synchronization for this Call. VoiceEngine* voice_engine; - TraceCallback* trace_callback; - uint32_t trace_filter; - // Callback for overuse and normal usage based on the jitter of incoming // captured frames. 'NULL' disables the callback. - OveruseCallback* overuse_callback; + LoadObserver* overuse_callback; + + // Start bitrate used before a valid bitrate estimate is calculated. + // Note: This is currently set only for video and is per-stream rather of + // for the entire link. + // TODO(pbos): Set start bitrate for entire Call. + int stream_start_bitrate_bps; + }; + + struct Stats { + Stats() : send_bandwidth_bps(0), recv_bandwidth_bps(0), pacer_delay_ms(0) {} + + int send_bandwidth_bps; + int recv_bandwidth_bps; + int pacer_delay_ms; }; static Call* Create(const Call::Config& config); @@ -78,17 +101,12 @@ static Call* Create(const Call::Config& config, const webrtc::Config& webrtc_config); - virtual std::vector GetVideoCodecs() = 0; - - virtual VideoSendStream::Config GetDefaultSendConfig() = 0; - virtual VideoSendStream* CreateVideoSendStream( - const VideoSendStream::Config& config) = 0; + const VideoSendStream::Config& config, + const VideoEncoderConfig& encoder_config) = 0; virtual void DestroyVideoSendStream(VideoSendStream* send_stream) = 0; - virtual VideoReceiveStream::Config GetDefaultReceiveConfig() = 0; - virtual VideoReceiveStream* CreateVideoReceiveStream( const VideoReceiveStream::Config& config) = 0; virtual void DestroyVideoReceiveStream( @@ -99,13 +117,11 @@ // Call instance exists. virtual PacketReceiver* Receiver() = 0; - // Returns the estimated total send bandwidth. Note: this can differ from the - // actual encoded bitrate. - virtual uint32_t SendBitrateEstimate() = 0; - - // Returns the total estimated receive bandwidth for the call. Note: this can - // differ from the actual receive bitrate. - virtual uint32_t ReceiveBitrateEstimate() = 0; + // Returns the call statistics, such as estimated send and receive bandwidth, + // pacing delay, etc. + virtual Stats GetStats() const = 0; + + virtual void SignalNetworkState(NetworkState state) = 0; virtual ~Call() {} }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_converter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_converter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_converter.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_converter.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/checks.h" +#include "webrtc/common_audio/audio_converter.h" +#include "webrtc/common_audio/resampler/push_sinc_resampler.h" + +namespace webrtc { +namespace { + +void DownmixToMono(const float* const* src, + int src_channels, + int frames, + float* dst) { + DCHECK_GT(src_channels, 0); + for (int i = 0; i < frames; ++i) { + float sum = 0; + for (int j = 0; j < src_channels; ++j) + sum += src[j][i]; + dst[i] = sum / src_channels; + } +} + +void UpmixFromMono(const float* src, + int dst_channels, + int frames, + float* const* dst) { + DCHECK_GT(dst_channels, 0); + for (int i = 0; i < frames; ++i) { + float value = src[i]; + for (int j = 0; j < dst_channels; ++j) + dst[j][i] = value; + } +} + +} // namespace + +AudioConverter::AudioConverter(int src_channels, int src_frames, + int dst_channels, int dst_frames) + : src_channels_(src_channels), + src_frames_(src_frames), + dst_channels_(dst_channels), + dst_frames_(dst_frames) { + CHECK(dst_channels == src_channels || dst_channels == 1 || src_channels == 1); + const int resample_channels = std::min(src_channels, dst_channels); + + // Prepare buffers as needed for intermediate stages. + if (dst_channels < src_channels) + downmix_buffer_.reset(new ChannelBuffer(src_frames, + resample_channels)); + + if (src_frames != dst_frames) { + resamplers_.reserve(resample_channels); + for (int i = 0; i < resample_channels; ++i) + resamplers_.push_back(new PushSincResampler(src_frames, dst_frames)); + } +} + +void AudioConverter::Convert(const float* const* src, + int src_channels, + int src_frames, + int dst_channels, + int dst_frames, + float* const* dst) { + DCHECK_EQ(src_channels_, src_channels); + DCHECK_EQ(src_frames_, src_frames); + DCHECK_EQ(dst_channels_, dst_channels); + DCHECK_EQ(dst_frames_, dst_frames);; + + if (src_channels == dst_channels && src_frames == dst_frames) { + // Shortcut copy. + if (src != dst) { + for (int i = 0; i < src_channels; ++i) + memcpy(dst[i], src[i], dst_frames * sizeof(*dst[i])); + } + return; + } + + const float* const* src_ptr = src; + if (dst_channels < src_channels) { + float* const* dst_ptr = dst; + if (src_frames != dst_frames) { + // Downmix to a buffer for subsequent resampling. + DCHECK_EQ(downmix_buffer_->num_channels(), dst_channels); + DCHECK_EQ(downmix_buffer_->samples_per_channel(), src_frames); + dst_ptr = downmix_buffer_->channels(); + } + + DownmixToMono(src, src_channels, src_frames, dst_ptr[0]); + src_ptr = dst_ptr; + } + + if (src_frames != dst_frames) { + for (size_t i = 0; i < resamplers_.size(); ++i) + resamplers_[i]->Resample(src_ptr[i], src_frames, dst[i], dst_frames); + src_ptr = dst; + } + + if (dst_channels > src_channels) + UpmixFromMono(src_ptr[0], dst_channels, dst_frames, dst); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_converter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_converter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_converter.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_converter.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_COMMON_AUDIO_AUDIO_CONVERTER_H_ +#define WEBRTC_COMMON_AUDIO_AUDIO_CONVERTER_H_ + +// TODO(ajm): Move channel buffer to common_audio. +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_processing/common.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/system_wrappers/interface/scoped_vector.h" + +namespace webrtc { + +class PushSincResampler; + +// Format conversion (remixing and resampling) for audio. Only simple remixing +// conversions are supported: downmix to mono (i.e. |dst_channels| == 1) or +// upmix from mono (i.e. |src_channels == 1|). +// +// The source and destination chunks have the same duration in time; specifying +// the number of frames is equivalent to specifying the sample rates. +class AudioConverter { + public: + AudioConverter(int src_channels, int src_frames, + int dst_channels, int dst_frames); + + void Convert(const float* const* src, + int src_channels, + int src_frames, + int dst_channels, + int dst_frames, + float* const* dest); + + private: + const int src_channels_; + const int src_frames_; + const int dst_channels_; + const int dst_frames_; + scoped_ptr> downmix_buffer_; + ScopedVector resamplers_; + + DISALLOW_COPY_AND_ASSIGN(AudioConverter); +}; + +} // namespace webrtc + +#endif // WEBRTC_COMMON_AUDIO_AUDIO_CONVERTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_converter_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_converter_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_converter_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_converter_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/common_audio/audio_converter.h" +#include "webrtc/common_audio/resampler/push_sinc_resampler.h" +#include "webrtc/modules/audio_processing/common.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +typedef scoped_ptr> ScopedBuffer; + +// Sets the signal value to increase by |data| with every sample. +ScopedBuffer CreateBuffer(const std::vector& data, int frames) { + const int num_channels = static_cast(data.size()); + ScopedBuffer sb(new ChannelBuffer(frames, num_channels)); + for (int i = 0; i < num_channels; ++i) + for (int j = 0; j < frames; ++j) + sb->channel(i)[j] = data[i] * j; + return sb; +} + +void VerifyParams(const ChannelBuffer& ref, + const ChannelBuffer& test) { + EXPECT_EQ(ref.num_channels(), test.num_channels()); + EXPECT_EQ(ref.samples_per_channel(), test.samples_per_channel()); +} + +// Computes the best SNR based on the error between |ref_frame| and +// |test_frame|. It searches around |expected_delay| in samples between the +// signals to compensate for the resampling delay. +float ComputeSNR(const ChannelBuffer& ref, + const ChannelBuffer& test, + int expected_delay) { + VerifyParams(ref, test); + float best_snr = 0; + int best_delay = 0; + + // Search within one sample of the expected delay. + for (int delay = std::max(expected_delay - 1, 0); + delay <= std::min(expected_delay + 1, ref.samples_per_channel()); + ++delay) { + float mse = 0; + float variance = 0; + float mean = 0; + for (int i = 0; i < ref.num_channels(); ++i) { + for (int j = 0; j < ref.samples_per_channel() - delay; ++j) { + float error = ref.channel(i)[j] - test.channel(i)[j + delay]; + mse += error * error; + variance += ref.channel(i)[j] * ref.channel(i)[j]; + mean += ref.channel(i)[j]; + } + } + const int length = ref.num_channels() * (ref.samples_per_channel() - delay); + mse /= length; + variance /= length; + mean /= length; + variance -= mean * mean; + float snr = 100; // We assign 100 dB to the zero-error case. + if (mse > 0) + snr = 10 * log10(variance / mse); + if (snr > best_snr) { + best_snr = snr; + best_delay = delay; + } + } + printf("SNR=%.1f dB at delay=%d\n", best_snr, best_delay); + return best_snr; +} + +// Sets the source to a linearly increasing signal for which we can easily +// generate a reference. Runs the AudioConverter and ensures the output has +// sufficiently high SNR relative to the reference. +void RunAudioConverterTest(int src_channels, + int src_sample_rate_hz, + int dst_channels, + int dst_sample_rate_hz) { + const float kSrcLeft = 0.0002f; + const float kSrcRight = 0.0001f; + const float resampling_factor = (1.f * src_sample_rate_hz) / + dst_sample_rate_hz; + const float dst_left = resampling_factor * kSrcLeft; + const float dst_right = resampling_factor * kSrcRight; + const float dst_mono = (dst_left + dst_right) / 2; + const int src_frames = src_sample_rate_hz / 100; + const int dst_frames = dst_sample_rate_hz / 100; + + std::vector src_data(1, kSrcLeft); + if (src_channels == 2) + src_data.push_back(kSrcRight); + ScopedBuffer src_buffer = CreateBuffer(src_data, src_frames); + + std::vector dst_data(1, 0); + std::vector ref_data; + if (dst_channels == 1) { + if (src_channels == 1) + ref_data.push_back(dst_left); + else + ref_data.push_back(dst_mono); + } else { + dst_data.push_back(0); + ref_data.push_back(dst_left); + if (src_channels == 1) + ref_data.push_back(dst_left); + else + ref_data.push_back(dst_right); + } + ScopedBuffer dst_buffer = CreateBuffer(dst_data, dst_frames); + ScopedBuffer ref_buffer = CreateBuffer(ref_data, dst_frames); + + // The sinc resampler has a known delay, which we compute here. + const int delay_frames = src_sample_rate_hz == dst_sample_rate_hz ? 0 : + PushSincResampler::AlgorithmicDelaySeconds(src_sample_rate_hz) * + dst_sample_rate_hz; + printf("(%d, %d Hz) -> (%d, %d Hz) ", // SNR reported on the same line later. + src_channels, src_sample_rate_hz, dst_channels, dst_sample_rate_hz); + + AudioConverter converter(src_channels, src_frames, dst_channels, dst_frames); + converter.Convert(src_buffer->channels(), src_channels, src_frames, + dst_channels, dst_frames, dst_buffer->channels()); + + EXPECT_LT(43.f, + ComputeSNR(*ref_buffer.get(), *dst_buffer.get(), delay_frames)); +} + +TEST(AudioConverterTest, ConversionsPassSNRThreshold) { + const int kSampleRates[] = {8000, 16000, 32000, 44100, 48000}; + const int kSampleRatesSize = sizeof(kSampleRates) / sizeof(*kSampleRates); + const int kChannels[] = {1, 2}; + const int kChannelsSize = sizeof(kChannels) / sizeof(*kChannels); + for (int src_rate = 0; src_rate < kSampleRatesSize; ++src_rate) { + for (int dst_rate = 0; dst_rate < kSampleRatesSize; ++dst_rate) { + for (int src_channel = 0; src_channel < kChannelsSize; ++src_channel) { + for (int dst_channel = 0; dst_channel < kChannelsSize; ++dst_channel) { + RunAudioConverterTest(kChannels[src_channel], kSampleRates[src_rate], + kChannels[dst_channel], kSampleRates[dst_rate]); + } + } + } + } +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_util.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_util.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_util.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_util.cc 2015-02-03 14:33:34.000000000 +0000 @@ -14,28 +14,29 @@ namespace webrtc { -void Deinterleave(const int16_t* interleaved, int samples_per_channel, - int num_channels, int16_t** deinterleaved) { - for (int i = 0; i < num_channels; i++) { - int16_t* channel = deinterleaved[i]; - int interleaved_idx = i; - for (int j = 0; j < samples_per_channel; j++) { - channel[j] = interleaved[interleaved_idx]; - interleaved_idx += num_channels; - } - } +void FloatToS16(const float* src, size_t size, int16_t* dest) { + for (size_t i = 0; i < size; ++i) + dest[i] = FloatToS16(src[i]); } -void Interleave(const int16_t* const* deinterleaved, int samples_per_channel, - int num_channels, int16_t* interleaved) { - for (int i = 0; i < num_channels; ++i) { - const int16_t* channel = deinterleaved[i]; - int interleaved_idx = i; - for (int j = 0; j < samples_per_channel; j++) { - interleaved[interleaved_idx] = channel[j]; - interleaved_idx += num_channels; - } - } +void S16ToFloat(const int16_t* src, size_t size, float* dest) { + for (size_t i = 0; i < size; ++i) + dest[i] = S16ToFloat(src[i]); +} + +void FloatS16ToS16(const float* src, size_t size, int16_t* dest) { + for (size_t i = 0; i < size; ++i) + dest[i] = FloatS16ToS16(src[i]); +} + +void FloatToFloatS16(const float* src, size_t size, float* dest) { + for (size_t i = 0; i < size; ++i) + dest[i] = FloatToFloatS16(src[i]); +} + +void FloatS16ToFloat(const float* src, size_t size, float* dest) { + for (size_t i = 0; i < size; ++i) + dest[i] = FloatS16ToFloat(src[i]); } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_util_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_util_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_util_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/audio_util_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -16,22 +16,70 @@ void ExpectArraysEq(const int16_t* ref, const int16_t* test, int length) { for (int i = 0; i < length; ++i) { - EXPECT_EQ(test[i], ref[i]); + EXPECT_EQ(ref[i], test[i]); } } -TEST(AudioUtilTest, Clamp) { - EXPECT_EQ(1000.f, ClampInt16(1000.f)); - EXPECT_EQ(32767.f, ClampInt16(32767.5f)); - EXPECT_EQ(-32768.f, ClampInt16(-32768.5f)); +void ExpectArraysEq(const float* ref, const float* test, int length) { + for (int i = 0; i < length; ++i) { + EXPECT_FLOAT_EQ(ref[i], test[i]); + } +} + +TEST(AudioUtilTest, FloatToS16) { + const int kSize = 9; + const float kInput[kSize] = { + 0.f, 0.4f / 32767.f, 0.6f / 32767.f, -0.4f / 32768.f, -0.6f / 32768.f, + 1.f, -1.f, 1.1f, -1.1f}; + const int16_t kReference[kSize] = { + 0, 0, 1, 0, -1, 32767, -32768, 32767, -32768}; + int16_t output[kSize]; + FloatToS16(kInput, kSize, output); + ExpectArraysEq(kReference, output, kSize); +} + +TEST(AudioUtilTest, S16ToFloat) { + const int kSize = 7; + const int16_t kInput[kSize] = {0, 1, -1, 16384, -16384, 32767, -32768}; + const float kReference[kSize] = { + 0.f, 1.f / 32767.f, -1.f / 32768.f, 16384.f / 32767.f, -0.5f, 1.f, -1.f}; + float output[kSize]; + S16ToFloat(kInput, kSize, output); + ExpectArraysEq(kReference, output, kSize); +} + +TEST(AudioUtilTest, FloatS16ToS16) { + const int kSize = 7; + const float kInput[kSize] = { + 0.f, 0.4f, 0.5f, -0.4f, -0.5f, 32768.f, -32769.f}; + const int16_t kReference[kSize] = {0, 0, 1, 0, -1, 32767, -32768}; + int16_t output[kSize]; + FloatS16ToS16(kInput, kSize, output); + ExpectArraysEq(kReference, output, kSize); +} + +TEST(AudioUtilTest, FloatToFloatS16) { + const int kSize = 9; + const float kInput[kSize] = { + 0.f, 0.4f / 32767.f, 0.6f / 32767.f, -0.4f / 32768.f, -0.6f / 32768.f, + 1.f, -1.f, 1.1f, -1.1f}; + const float kReference[kSize] = { + 0.f, 0.4f, 0.6f, -0.4f, -0.6f, 32767.f, -32768.f, 36043.7f, -36044.8f}; + float output[kSize]; + FloatToFloatS16(kInput, kSize, output); + ExpectArraysEq(kReference, output, kSize); } -TEST(AudioUtilTest, Round) { - EXPECT_EQ(0, RoundToInt16(0.f)); - EXPECT_EQ(0, RoundToInt16(0.4f)); - EXPECT_EQ(1, RoundToInt16(0.5f)); - EXPECT_EQ(0, RoundToInt16(-0.4f)); - EXPECT_EQ(-1, RoundToInt16(-0.5f)); +TEST(AudioUtilTest, FloatS16ToFloat) { + const int kSize = 9; + const float kInput[kSize] = { + 0.f, 0.4f, 0.6f, -0.4f, -0.6f, 32767.f, -32768.f, 36043.7f, -36044.8f}; + const float kReference[kSize] = { + 0.f, 0.4f / 32767.f, 0.6f / 32767.f, -0.4f / 32768.f, -0.6f / 32768.f, + 1.f, -1.f, 1.1f, -1.1f}; + float output[kSize]; + FloatS16ToFloat(kInput, kSize, output); + ExpectArraysEq(kReference, output, kSize); } TEST(AudioUtilTest, InterleavingStereo) { @@ -44,12 +92,12 @@ Deinterleave(kInterleaved, kSamplesPerChannel, kNumChannels, deinterleaved); const int16_t kRefLeft[] = {2, 4, 8, 16}; const int16_t kRefRight[] = {3, 9, 27, 81}; - ExpectArraysEq(left, kRefLeft, kSamplesPerChannel); - ExpectArraysEq(right, kRefRight, kSamplesPerChannel); + ExpectArraysEq(kRefLeft, left, kSamplesPerChannel); + ExpectArraysEq(kRefRight, right, kSamplesPerChannel); int16_t interleaved[kLength]; Interleave(deinterleaved, kSamplesPerChannel, kNumChannels, interleaved); - ExpectArraysEq(interleaved, kInterleaved, kLength); + ExpectArraysEq(kInterleaved, interleaved, kLength); } TEST(AudioUtilTest, InterleavingMonoIsIdentical) { @@ -59,11 +107,11 @@ int16_t mono[kSamplesPerChannel]; int16_t* deinterleaved[] = {mono}; Deinterleave(kInterleaved, kSamplesPerChannel, kNumChannels, deinterleaved); - ExpectArraysEq(mono, kInterleaved, kSamplesPerChannel); + ExpectArraysEq(kInterleaved, mono, kSamplesPerChannel); int16_t interleaved[kSamplesPerChannel]; Interleave(deinterleaved, kSamplesPerChannel, kNumChannels, interleaved); - ExpectArraysEq(interleaved, mono, kSamplesPerChannel); + ExpectArraysEq(mono, interleaved, kSamplesPerChannel); } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/blocker.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/blocker.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/blocker.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/blocker.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/common_audio/blocker.h" + +#include + +#include "webrtc/base/checks.h" + +namespace { + +// Adds |a| and |b| frame by frame into |result| (basically matrix addition). +void AddFrames(const float* const* a, + int a_start_index, + const float* const* b, + int b_start_index, + int num_frames, + int num_channels, + float* const* result, + int result_start_index) { + for (int i = 0; i < num_channels; ++i) { + for (int j = 0; j < num_frames; ++j) { + result[i][j + result_start_index] = + a[i][j + a_start_index] + b[i][j + b_start_index]; + } + } +} + +// Copies |src| into |dst| channel by channel. +void CopyFrames(const float* const* src, + int src_start_index, + int num_frames, + int num_channels, + float* const* dst, + int dst_start_index) { + for (int i = 0; i < num_channels; ++i) { + memcpy(&dst[i][dst_start_index], + &src[i][src_start_index], + num_frames * sizeof(float)); + } +} + +void ZeroOut(float* const* buffer, + int starting_idx, + int num_frames, + int num_channels) { + for (int i = 0; i < num_channels; ++i) { + memset(&buffer[i][starting_idx], 0, num_frames * sizeof(float)); + } +} + +// Pointwise multiplies each channel of |frames| with |window|. Results are +// stored in |frames|. +void ApplyWindow(const float* window, + int num_frames, + int num_channels, + float* const* frames) { + for (int i = 0; i < num_channels; ++i) { + for (int j = 0; j < num_frames; ++j) { + frames[i][j] = frames[i][j] * window[j]; + } + } +} + +} // namespace + +namespace webrtc { + +Blocker::Blocker(int chunk_size, + int block_size, + int num_input_channels, + int num_output_channels, + const float* window, + int shift_amount, + BlockerCallback* callback) + : chunk_size_(chunk_size), + block_size_(block_size), + num_input_channels_(num_input_channels), + num_output_channels_(num_output_channels), + initial_delay_(block_size_), + frame_offset_(0), + input_buffer_(chunk_size_ + initial_delay_, num_input_channels_), + output_buffer_(chunk_size_ + initial_delay_, num_output_channels_), + input_block_(block_size_, num_input_channels_), + output_block_(block_size_, num_output_channels_), + window_(new float[block_size_]), + shift_amount_(shift_amount), + callback_(callback) { + CHECK_LE(num_output_channels_, num_input_channels_); + CHECK_GE(chunk_size_, block_size_); + + memcpy(window_.get(), window, block_size_ * sizeof(float)); + size_t buffer_size = chunk_size_ + initial_delay_; + memset(input_buffer_.channels()[0], + 0, + buffer_size * num_input_channels_ * sizeof(float)); + memset(output_buffer_.channels()[0], + 0, + buffer_size * num_output_channels_ * sizeof(float)); +} + +// Both the input and output buffers look like this: +// +// delay* chunk_size chunk_size + delay* +// buffer: <-------------|---------------------|---------------|> +// _a_ _b_ _c_ +// +// On each call to ProcessChunk(): +// 1. New input gets read into sections _b_ and _c_ of the input buffer. +// 2. We block starting from frame_offset. +// 3. We block until we reach a block |bl| that doesn't contain any frames +// from sections _a_ or _b_ of the input buffer. +// 4. We window the current block, fire the callback for processing, window +// again, and overlap/add to the output buffer. +// 5. We copy sections _a_ and _b_ of the output buffer into output. +// 6. For both the input and the output buffers, we copy section c into +// section a. +// 7. We set the new frame_offset to be the difference between the first frame +// of |bl| and the border between sections _b_ and _c_. +// +// * delay here refers to inintial_delay_ +// +// TODO(claguna): Look at using ring buffers to eliminate some copies. +void Blocker::ProcessChunk(const float* const* input, + int chunk_size, + int num_input_channels, + int num_output_channels, + float* const* output) { + CHECK_EQ(chunk_size, chunk_size_); + CHECK_EQ(num_input_channels, num_input_channels_); + CHECK_EQ(num_output_channels, num_output_channels_); + + // Copy new data into input buffer at + // [|initial_delay_|, |chunk_size_| + |initial_delay_|]. + CopyFrames(input, + 0, + chunk_size_, + num_input_channels_, + input_buffer_.channels(), + initial_delay_); + + int first_frame_in_block = frame_offset_; + + // Loop through blocks. + while (first_frame_in_block < chunk_size_) { + CopyFrames(input_buffer_.channels(), + first_frame_in_block, + block_size_, + num_input_channels_, + input_block_.channels(), + 0); + + ApplyWindow(window_.get(), + block_size_, + num_input_channels_, + input_block_.channels()); + callback_->ProcessBlock(input_block_.channels(), + block_size_, + num_input_channels_, + num_output_channels_, + output_block_.channels()); + ApplyWindow(window_.get(), + block_size_, + num_output_channels_, + output_block_.channels()); + + AddFrames(output_buffer_.channels(), + first_frame_in_block, + output_block_.channels(), + 0, + block_size_, + num_output_channels_, + output_buffer_.channels(), + first_frame_in_block); + + first_frame_in_block += shift_amount_; + } + + // Copy output buffer to output + CopyFrames(output_buffer_.channels(), + 0, + chunk_size_, + num_output_channels_, + output, + 0); + + // Copy input buffer [chunk_size_, chunk_size_ + initial_delay] + // to input buffer [0, initial_delay] + CopyFrames(input_buffer_.channels(), + chunk_size, + initial_delay_, + num_input_channels_, + input_buffer_.channels(), + 0); + + // Copy output buffer [chunk_size_, chunk_size_ + initial_delay] + // to output buffer [0, initial_delay], zero the rest. + CopyFrames(output_buffer_.channels(), + chunk_size, + initial_delay_, + num_output_channels_, + output_buffer_.channels(), + 0); + ZeroOut(output_buffer_.channels(), + initial_delay_, + chunk_size_, + num_output_channels_); + + // Calculate new starting frames. + frame_offset_ = first_frame_in_block - chunk_size_; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/blocker.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/blocker.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/blocker.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/blocker.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_INTERNAL_BEAMFORMER_BLOCKER_H_ +#define WEBRTC_INTERNAL_BEAMFORMER_BLOCKER_H_ + +#include "webrtc/modules/audio_processing/common.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +// The callback function to process audio in the time domain. Input has already +// been windowed, and output will be windowed. The number of input channels +// must be >= the number of output channels. +class BlockerCallback { + public: + virtual ~BlockerCallback() {} + + virtual void ProcessBlock(const float* const* input, + int num_frames, + int num_input_channels, + int num_output_channels, + float* const* output) = 0; +}; + +// The main purpose of Blocker is to abstract away the fact that often we +// receive a different number of audio frames than our transform takes. For +// example, most FFTs work best when the fft-size is a power of 2, but suppose +// we receive 20ms of audio at a sample rate of 48000. That comes to 960 frames +// of audio, which is not a power of 2. Blocker allows us to specify the +// transform and all other necessary processing via the Process() callback +// function without any constraints on the transform-size +// (read: |block_size_|) or received-audio-size (read: |chunk_size_|). +// We handle this for the multichannel audio case, allowing for different +// numbers of input and output channels (for example, beamforming takes 2 or +// more input channels and returns 1 output channel). Audio signals are +// represented as deinterleaved floats in the range [-1, 1]. +// +// Blocker is responsible for: +// - blocking audio while handling potential discontinuities on the edges +// of chunks +// - windowing blocks before sending them to Process() +// - windowing processed blocks, and overlap-adding them together before +// sending back a processed chunk +// +// To use blocker: +// 1. Impelment a BlockerCallback object |bc|. +// 2. Instantiate a Blocker object |b|, passing in |bc|. +// 3. As you receive audio, call b.ProcessChunk() to get processed audio. +// +// A small amount of delay is added to the first received chunk to deal with +// the difference in chunk/block sizes. This delay is <= chunk_size. +class Blocker { + public: + Blocker(int chunk_size, + int block_size, + int num_input_channels, + int num_output_channels, + const float* window, + int shift_amount, + BlockerCallback* callback); + + void ProcessChunk(const float* const* input, + int num_frames, + int num_input_channels, + int num_output_channels, + float* const* output); + + private: + const int chunk_size_; + const int block_size_; + const int num_input_channels_; + const int num_output_channels_; + + // The number of frames of delay to add at the beginning of the first chunk. + // + // TODO(claguna): find a lower cap for this than |block_size_|. + const int initial_delay_; + + // The frame index into the input buffer where the first block should be read + // from. This is necessary because shift_amount_ is not necessarily a + // multiple of chunk_size_, so blocks won't line up at the start of the + // buffer. + int frame_offset_; + + // Since blocks nearly always overlap, there are certain blocks that require + // frames from the end of one chunk and the beginning of the next chunk. The + // input and output buffers are responsible for saving those frames between + // calls to ProcessChunk(). + // + // Both contain |initial delay| + |chunk_size| frames. + ChannelBuffer input_buffer_; + ChannelBuffer output_buffer_; + + // Space for the input block (can't wrap because of windowing). + ChannelBuffer input_block_; + + // Space for the output block (can't wrap because of overlap/add). + ChannelBuffer output_block_; + + scoped_ptr window_; + + // The amount of frames between the start of contiguous blocks. For example, + // |shift_amount_| = |block_size_| / 2 for a Hann window. + int shift_amount_; + + BlockerCallback* callback_; +}; + +} // namespace webrtc + +#endif // WEBRTC_INTERNAL_BEAMFORMER_BLOCKER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/blocker_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/blocker_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/blocker_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/blocker_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/common_audio/blocker.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// Callback Function to add 3 to every sample in the signal. +class SimpleBlockerCallback : public webrtc::BlockerCallback { + public: + virtual void ProcessBlock(const float* const* input, + int num_frames, + int num_input_channels, + int num_output_channels, + float* const* output) OVERRIDE { + for (int i = 0; i < num_output_channels; ++i) { + for (int j = 0; j < num_frames; ++j) { + output[i][j] = input[i][j] + 3; + } + } + } +}; + +} // namespace + +namespace webrtc { + +// Tests blocking with a window that multiplies the signal by 2, a callback +// that adds 3 to each sample in the signal, and different combinations of chunk +// size, block size, and shift amount. +class BlockerTest : public ::testing::Test { + protected: + void RunTest(Blocker* blocker, + int chunk_size, + int num_frames, + const float* const* input, + float* const* input_chunk, + float* const* output, + float* const* output_chunk, + int num_input_channels, + int num_output_channels) { + int start = 0; + int end = chunk_size - 1; + while (end < num_frames) { + CopyTo(input_chunk, 0, start, num_input_channels, chunk_size, input); + blocker->ProcessChunk(input_chunk, + chunk_size, + num_input_channels, + num_output_channels, + output_chunk); + CopyTo(output, start, 0, num_output_channels, chunk_size, output_chunk); + + start = start + chunk_size; + end = end + chunk_size; + } + } + + void ValidateSignalEquality(const float* const* expected, + const float* const* actual, + int num_channels, + int num_frames) { + for (int i = 0; i < num_channels; ++i) { + for (int j = 0; j < num_frames; ++j) { + EXPECT_FLOAT_EQ(expected[i][j], actual[i][j]); + } + } + } + + static void CopyTo(float* const* dst, + int start_index_dst, + int start_index_src, + int num_channels, + int num_frames, + const float* const* src) { + for (int i = 0; i < num_channels; ++i) { + memcpy(&dst[i][start_index_dst], + &src[i][start_index_src], + num_frames * sizeof(float)); + } + } +}; + +TEST_F(BlockerTest, TestBlockerMutuallyPrimeChunkandBlockSize) { + const int kNumInputChannels = 3; + const int kNumOutputChannels = 2; + const int kNumFrames = 10; + const int kBlockSize = 4; + const int kChunkSize = 5; + const int kShiftAmount = 2; + + const float kInput[kNumInputChannels][kNumFrames] = { + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, + {3, 3, 3, 3, 3, 3, 3, 3, 3, 3}}; + const ChannelBuffer input_cb(kInput[0], kNumFrames, kNumInputChannels); + + const float kExpectedOutput[kNumInputChannels][kNumFrames] = { + {6, 6, 12, 12, 20, 20, 20, 20, 20, 20}, + {6, 6, 12, 12, 28, 28, 28, 28, 28, 28}}; + const ChannelBuffer expected_output_cb( + kExpectedOutput[0], kNumFrames, kNumInputChannels); + + const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f}; + + ChannelBuffer actual_output_cb(kNumFrames, kNumOutputChannels); + ChannelBuffer input_chunk_cb(kChunkSize, kNumInputChannels); + ChannelBuffer output_chunk_cb(kChunkSize, kNumOutputChannels); + + SimpleBlockerCallback callback; + Blocker blocker(kChunkSize, + kBlockSize, + kNumInputChannels, + kNumOutputChannels, + kWindow, + kShiftAmount, + &callback); + + RunTest(&blocker, + kChunkSize, + kNumFrames, + input_cb.channels(), + input_chunk_cb.channels(), + actual_output_cb.channels(), + output_chunk_cb.channels(), + kNumInputChannels, + kNumOutputChannels); + + ValidateSignalEquality(expected_output_cb.channels(), + actual_output_cb.channels(), + kNumOutputChannels, + kNumFrames); +} + +TEST_F(BlockerTest, TestBlockerMutuallyPrimeShiftAndBlockSize) { + const int kNumInputChannels = 3; + const int kNumOutputChannels = 2; + const int kNumFrames = 12; + const int kBlockSize = 4; + const int kChunkSize = 6; + const int kShiftAmount = 3; + + const float kInput[kNumInputChannels][kNumFrames] = { + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, + {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}}; + const ChannelBuffer input_cb(kInput[0], kNumFrames, kNumInputChannels); + + const float kExpectedOutput[kNumInputChannels][kNumFrames] = { + {6, 6, 6, 12, 10, 10, 20, 10, 10, 20, 10, 10}, + {6, 6, 6, 12, 14, 14, 28, 14, 14, 28, 14, 14}}; + const ChannelBuffer expected_output_cb( + kExpectedOutput[0], kNumFrames, kNumInputChannels); + + const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f}; + + ChannelBuffer actual_output_cb(kNumFrames, kNumOutputChannels); + ChannelBuffer input_chunk_cb(kChunkSize, kNumInputChannels); + ChannelBuffer output_chunk_cb(kChunkSize, kNumOutputChannels); + + SimpleBlockerCallback callback; + Blocker blocker(kChunkSize, + kBlockSize, + kNumInputChannels, + kNumOutputChannels, + kWindow, + kShiftAmount, + &callback); + + RunTest(&blocker, + kChunkSize, + kNumFrames, + input_cb.channels(), + input_chunk_cb.channels(), + actual_output_cb.channels(), + output_chunk_cb.channels(), + kNumInputChannels, + kNumOutputChannels); + + ValidateSignalEquality(expected_output_cb.channels(), + actual_output_cb.channels(), + kNumOutputChannels, + kNumFrames); +} + +TEST_F(BlockerTest, TestBlockerNoOverlap) { + const int kNumInputChannels = 3; + const int kNumOutputChannels = 2; + const int kNumFrames = 12; + const int kBlockSize = 4; + const int kChunkSize = 4; + const int kShiftAmount = 4; + + const float kInput[kNumInputChannels][kNumFrames] = { + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, + {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}}; + const ChannelBuffer input_cb(kInput[0], kNumFrames, kNumInputChannels); + + const float kExpectedOutput[kNumInputChannels][kNumFrames] = { + {6, 6, 6, 6, 10, 10, 10, 10, 10, 10, 10, 10}, + {6, 6, 6, 6, 14, 14, 14, 14, 14, 14, 14, 14}}; + const ChannelBuffer expected_output_cb( + kExpectedOutput[0], kNumFrames, kNumInputChannels); + + const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f}; + + ChannelBuffer actual_output_cb(kNumFrames, kNumOutputChannels); + ChannelBuffer input_chunk_cb(kChunkSize, kNumInputChannels); + ChannelBuffer output_chunk_cb(kChunkSize, kNumOutputChannels); + + SimpleBlockerCallback callback; + Blocker blocker(kChunkSize, + kBlockSize, + kNumInputChannels, + kNumOutputChannels, + kWindow, + kShiftAmount, + &callback); + + RunTest(&blocker, + kChunkSize, + kNumFrames, + input_cb.channels(), + input_chunk_cb.channels(), + actual_output_cb.channels(), + output_chunk_cb.channels(), + kNumInputChannels, + kNumOutputChannels); + + ValidateSignalEquality(expected_output_cb.channels(), + actual_output_cb.channels(), + kNumOutputChannels, + kNumFrames); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/BUILD.gn 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,232 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("//build/config/arm.gni") +import("../build/webrtc.gni") + +config("common_audio_config") { + include_dirs = [ + "resampler/include", + "signal_processing/include", + "vad/include", + ] +} + +source_set("common_audio") { + sources = [ + "audio_converter.cc", + "audio_converter.h", + "audio_util.cc", + "blocker.cc", + "blocker.h", + "fir_filter.cc", + "fir_filter.h", + "fir_filter_neon.h", + "fir_filter_sse.h", + "include/audio_util.h", + "resampler/include/push_resampler.h", + "resampler/include/resampler.h", + "resampler/push_resampler.cc", + "resampler/push_sinc_resampler.cc", + "resampler/push_sinc_resampler.h", + "resampler/resampler.cc", + "resampler/sinc_resampler.cc", + "resampler/sinc_resampler.h", + "signal_processing/include/real_fft.h", + "signal_processing/include/signal_processing_library.h", + "signal_processing/include/spl_inl.h", + "signal_processing/auto_corr_to_refl_coef.c", + "signal_processing/auto_correlation.c", + "signal_processing/complex_fft_tables.h", + "signal_processing/copy_set_operations.c", + "signal_processing/cross_correlation.c", + "signal_processing/division_operations.c", + "signal_processing/dot_product_with_scale.c", + "signal_processing/downsample_fast.c", + "signal_processing/energy.c", + "signal_processing/filter_ar.c", + "signal_processing/filter_ma_fast_q12.c", + "signal_processing/get_hanning_window.c", + "signal_processing/get_scaling_square.c", + "signal_processing/ilbc_specific_functions.c", + "signal_processing/levinson_durbin.c", + "signal_processing/lpc_to_refl_coef.c", + "signal_processing/min_max_operations.c", + "signal_processing/randomization_functions.c", + "signal_processing/refl_coef_to_lpc.c", + "signal_processing/real_fft.c", + "signal_processing/resample.c", + "signal_processing/resample_48khz.c", + "signal_processing/resample_by_2.c", + "signal_processing/resample_by_2_internal.c", + "signal_processing/resample_by_2_internal.h", + "signal_processing/resample_fractional.c", + "signal_processing/spl_init.c", + "signal_processing/spl_sqrt.c", + "signal_processing/splitting_filter.c", + "signal_processing/sqrt_of_one_minus_x_squared.c", + "signal_processing/vector_scaling_operations.c", + "vad/include/vad.h", + "vad/include/webrtc_vad.h", + "vad/vad.cc", + "vad/webrtc_vad.c", + "vad/vad_core.c", + "vad/vad_core.h", + "vad/vad_filterbank.c", + "vad/vad_filterbank.h", + "vad/vad_gmm.c", + "vad/vad_gmm.h", + "vad/vad_sp.c", + "vad/vad_sp.h", + "wav_header.cc", + "wav_header.h", + "wav_file.cc", + "wav_file.h", + "window_generator.cc", + "window_generator.h", + ] + + deps = [ "../system_wrappers" ] + + if (rtc_use_openmax_dl) { + sources += [ + "lapped_transform.cc", + "lapped_transform.h", + "real_fourier.cc", + "real_fourier.h", + ] + + deps += [ "//third_party/openmax_dl/dl" ] + } + + if (cpu_arch == "arm") { + sources += [ + "signal_processing/complex_bit_reverse_arm.S", + "signal_processing/spl_sqrt_floor_arm.S", + ] + + if (arm_version == 7) { + deps += [ ":common_audio_neon" ] + sources += [ "signal_processing/filter_ar_fast_q12_armv7.S" ] + } else { + sources += [ "signal_processing/filter_ar_fast_q12.c" ] + } + } + + if (cpu_arch == "mipsel") { + sources += [ + "signal_processing/include/spl_inl_mips.h", + "signal_processing/complex_bit_reverse_mips.c", + "signal_processing/complex_fft_mips.c", + "signal_processing/cross_correlation_mips.c", + "signal_processing/downsample_fast_mips.c", + "signal_processing/filter_ar_fast_q12_mips.c", + "signal_processing/min_max_operations_mips.c", + "signal_processing/resample_by_2_mips.c", + "signal_processing/spl_sqrt_floor_mips.c", + ] + if (mips_dsp_rev > 0) { + sources += [ "signal_processing/vector_scaling_operations_mips.c" ] + } + } else { + sources += [ "signal_processing/complex_fft.c" ] + } + + if (cpu_arch != "arm" && cpu_arch != "mipsel") { + sources += [ + "signal_processing/complex_bit_reverse.c", + "signal_processing/filter_ar_fast_q12.c", + "signal_processing/spl_sqrt_floor.c", + ] + } + + if (is_win) { + cflags = [ + "/wd4334", # Ignore warning on shift operator promotion. + ] + } + + configs += [ "..:common_config" ] + + public_configs = [ + "..:common_inherited_config", + ":common_audio_config", + ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + if (cpu_arch == "x86" || cpu_arch == "x64") { + deps += [ ":common_audio_sse2" ] + } +} + +if (cpu_arch == "x86" || cpu_arch == "x64") { + source_set("common_audio_sse2") { + sources = [ + "fir_filter_sse.cc", + "resampler/sinc_resampler_sse.cc", + ] + + cflags = [ "-msse2" ] + + configs += [ "..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + } +} + +if (rtc_build_armv7_neon) { + source_set("common_audio_neon") { + sources = [ + "fir_filter_neon.cc", + "resampler/sinc_resampler_neon.cc", + "signal_processing/cross_correlation_neon.S", + "signal_processing/downsample_fast_neon.S", + "signal_processing/min_max_operations_neon.S", + "signal_processing/vector_scaling_operations_neon.S", + ] + + configs += [ "..:common_config" ] + public_configs = [ "..:common_inherited_config" ] + + + # Enable compilation for the ARM v7 Neon instruction set. This is needed + # since //build/config/arm.gni only enables Neon for iOS, not Android. + # This provides the same functionality as webrtc/build/arm_neon.gypi. + # TODO(kjellander): Investigate if this can be moved into webrtc.gni or + # //build/config/arm.gni instead, to reduce code duplication. + # Remove the -mfpu=vfpv3-d16 cflag. + configs -= [ "//build/config/compiler:compiler_arm_fpu" ] + cflags = [ + "-flax-vector-conversions", + "-mfpu=neon", + ] + + # Disable LTO in audio_processing_neon target due to compiler bug. + if (rtc_use_lto) { + cflags -= [ + "-flto", + "-ffat-lto-objects", + ] + } + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/common_audio.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/common_audio.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/common_audio.gyp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/common_audio.gyp 2015-02-03 14:33:34.000000000 +0000 @@ -29,7 +29,15 @@ ], }, 'sources': [ + 'audio_converter.cc', + 'audio_converter.h', 'audio_util.cc', + 'blocker.cc', + 'blocker.h', + 'fir_filter.cc', + 'fir_filter.h', + 'fir_filter_neon.h', + 'fir_filter_sse.h', 'include/audio_util.h', 'resampler/include/push_resampler.h', 'resampler/include/resampler.h', @@ -74,11 +82,12 @@ 'signal_processing/spl_init.c', 'signal_processing/spl_sqrt.c', 'signal_processing/spl_sqrt_floor.c', - 'signal_processing/spl_version.c', 'signal_processing/splitting_filter.c', 'signal_processing/sqrt_of_one_minus_x_squared.c', 'signal_processing/vector_scaling_operations.c', + 'vad/include/vad.h', 'vad/include/webrtc_vad.h', + 'vad/vad.cc', 'vad/webrtc_vad.c', 'vad/vad_core.c', 'vad/vad_core.h', @@ -88,8 +97,25 @@ 'vad/vad_gmm.h', 'vad/vad_sp.c', 'vad/vad_sp.h', + 'wav_header.cc', + 'wav_header.h', + 'wav_file.cc', + 'wav_file.h', + 'window_generator.cc', + 'window_generator.h', ], 'conditions': [ + ['rtc_use_openmax_dl==1', { + 'sources': [ + 'lapped_transform.cc', + 'lapped_transform.h', + 'real_fourier.cc', + 'real_fourier.h', + ], + 'dependencies': [ + '<(DEPTH)/third_party/openmax_dl/dl/dl.gyp:openmax_dl', + ], + }], ['target_arch=="ia32" or target_arch=="x64"', { 'dependencies': ['common_audio_sse2',], }], @@ -114,7 +140,7 @@ }], ], # conditions }], - ['target_arch=="mipsel"', { + ['target_arch=="mipsel" and mips_arch_variant!="r6" and android_webview_build==0', { 'sources': [ 'signal_processing/include/spl_inl_mips.h', 'signal_processing/complex_bit_reverse_mips.c', @@ -152,6 +178,7 @@ 'target_name': 'common_audio_sse2', 'type': 'static_library', 'sources': [ + 'fir_filter_sse.cc', 'resampler/sinc_resampler_sse.cc', ], 'cflags': ['-msse2',], @@ -173,12 +200,22 @@ 'type': 'static_library', 'includes': ['../build/arm_neon.gypi',], 'sources': [ + 'fir_filter_neon.cc', 'resampler/sinc_resampler_neon.cc', 'signal_processing/cross_correlation_neon.S', 'signal_processing/downsample_fast_neon.S', 'signal_processing/min_max_operations_neon.S', 'signal_processing/vector_scaling_operations_neon.S', ], + 'conditions': [ + # Disable LTO in common_audio_neon target due to compiler bug + ['use_lto==1', { + 'cflags!': [ + '-flto', + '-ffat-lto-objects', + ], + }], + ], }, ], # targets }], @@ -194,7 +231,10 @@ '<(DEPTH)/testing/gtest.gyp:gtest', ], 'sources': [ + 'audio_converter_unittest.cc', 'audio_util_unittest.cc', + 'blocker_unittest.cc', + 'fir_filter_unittest.cc', 'resampler/resampler_unittest.cc', 'resampler/push_resampler_unittest.cc', 'resampler/push_sinc_resampler_unittest.cc', @@ -209,11 +249,18 @@ 'vad/vad_sp_unittest.cc', 'vad/vad_unittest.cc', 'vad/vad_unittest.h', + 'wav_header_unittest.cc', + 'wav_file_unittest.cc', + 'window_generator_unittest.cc', ], 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are - # using Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['rtc_use_openmax_dl==1', { + 'sources': [ + 'lapped_transform_unittest.cc', + 'real_fourier_unittest.cc', + ], + }], + ['OS=="android"', { 'dependencies': [ '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code', ], @@ -222,9 +269,7 @@ }, ], # targets 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are using - # Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['OS=="android"', { 'targets': [ { 'target_name': 'common_audio_unittests_apk_target', @@ -245,7 +290,6 @@ ], 'includes': [ '../build/isolate.gypi', - 'common_audio_unittests.isolate', ], 'sources': [ 'common_audio_unittests.isolate', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/common_audio_unittests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/common_audio_unittests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/common_audio_unittests.isolate 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/common_audio_unittests.isolate 2015-02-03 14:33:34.000000000 +0000 @@ -8,28 +8,23 @@ { 'conditions': [ ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. 'variables': { - 'isolate_dependency_untracked': [ - '../../../data/', - '../../../resources/', + 'files': [ + '<(DEPTH)/data/', + '<(DEPTH)/resources/', ], }, }], ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../../testing/test_env.py', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/common_audio_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_tracked': [ - '../../testing/test_env.py', + 'files': [ + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/common_audio_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '../../tools/swarming_client/', - ], }, }], ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/common_audio/fir_filter.h" + +#include +#include + +#include "webrtc/common_audio/fir_filter_neon.h" +#include "webrtc/common_audio/fir_filter_sse.h" +#include "webrtc/system_wrappers/interface/cpu_features_wrapper.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class FIRFilterC : public FIRFilter { + public: + FIRFilterC(const float* coefficients, + size_t coefficients_length); + + virtual void Filter(const float* in, size_t length, float* out) OVERRIDE; + + private: + size_t coefficients_length_; + size_t state_length_; + scoped_ptr coefficients_; + scoped_ptr state_; +}; + +FIRFilter* FIRFilter::Create(const float* coefficients, + size_t coefficients_length, + size_t max_input_length) { + if (!coefficients || coefficients_length <= 0 || max_input_length <= 0) { + assert(false); + return NULL; + } + + FIRFilter* filter = NULL; +// If we know the minimum architecture at compile time, avoid CPU detection. +#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(__SSE2__) + filter = + new FIRFilterSSE2(coefficients, coefficients_length, max_input_length); +#else + // x86 CPU detection required. + if (WebRtc_GetCPUInfo(kSSE2)) { + filter = + new FIRFilterSSE2(coefficients, coefficients_length, max_input_length); + } else { + filter = new FIRFilterC(coefficients, coefficients_length); + } +#endif +#elif defined(WEBRTC_ARCH_ARM_V7) +#if defined(WEBRTC_ARCH_ARM_NEON) + filter = + new FIRFilterNEON(coefficients, coefficients_length, max_input_length); +#else + // ARM CPU detection required. + if (WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) { + filter = + new FIRFilterNEON(coefficients, coefficients_length, max_input_length); + } else { + filter = new FIRFilterC(coefficients, coefficients_length); + } +#endif +#else + filter = new FIRFilterC(coefficients, coefficients_length); +#endif + + return filter; +} + +FIRFilterC::FIRFilterC(const float* coefficients, size_t coefficients_length) + : coefficients_length_(coefficients_length), + state_length_(coefficients_length - 1), + coefficients_(new float[coefficients_length_]), + state_(new float[state_length_]) { + for (size_t i = 0; i < coefficients_length_; ++i) { + coefficients_[i] = coefficients[coefficients_length_ - i - 1]; + } + memset(state_.get(), 0, state_length_ * sizeof(state_[0])); +} + +void FIRFilterC::Filter(const float* in, size_t length, float* out) { + assert(length > 0); + + // Convolves the input signal |in| with the filter kernel |coefficients_| + // taking into account the previous state. + for (size_t i = 0; i < length; ++i) { + out[i] = 0.f; + size_t j; + for (j = 0; state_length_ > i && j < state_length_ - i; ++j) { + out[i] += state_[i + j] * coefficients_[j]; + } + for (; j < coefficients_length_; ++j) { + out[i] += in[j + i - state_length_] * coefficients_[j]; + } + } + + // Update current state. + if (length >= state_length_) { + memcpy( + state_.get(), &in[length - state_length_], state_length_ * sizeof(*in)); + } else { + memmove(state_.get(), + &state_[length], + (state_length_ - length) * sizeof(state_[0])); + memcpy(&state_[state_length_ - length], in, length * sizeof(*in)); + } +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_COMMON_AUDIO_FIR_FILTER_H_ +#define WEBRTC_COMMON_AUDIO_FIR_FILTER_H_ + +#include + +namespace webrtc { + +// Finite Impulse Response filter using floating-point arithmetic. +class FIRFilter { + public: + // Creates a filter with the given coefficients. All initial state values will + // be zeros. + // The length of the chunks fed to the filter should never be greater than + // |max_input_length|. This is needed because, when vectorizing it is + // necessary to concatenate the input after the state, and resizing this array + // dynamically is expensive. + static FIRFilter* Create(const float* coefficients, + size_t coefficients_length, + size_t max_input_length); + + virtual ~FIRFilter() {} + + // Filters the |in| data supplied. + // |out| must be previously allocated and it must be at least of |length|. + virtual void Filter(const float* in, size_t length, float* out) = 0; +}; + +} // namespace webrtc + +#endif // WEBRTC_COMMON_AUDIO_FIR_FILTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_neon.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_neon.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_neon.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_neon.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/common_audio/fir_filter_neon.h" + +#include +#include +#include + +#include "webrtc/system_wrappers/interface/aligned_malloc.h" + +namespace webrtc { + +FIRFilterNEON::FIRFilterNEON(const float* coefficients, + size_t coefficients_length, + size_t max_input_length) + : // Closest higher multiple of four. + coefficients_length_((coefficients_length + 3) & ~0x03), + state_length_(coefficients_length_ - 1), + coefficients_(static_cast( + AlignedMalloc(sizeof(float) * coefficients_length_, 16))), + state_(static_cast( + AlignedMalloc(sizeof(float) * (max_input_length + state_length_), + 16))) { + // Add zeros at the end of the coefficients. + size_t padding = coefficients_length_ - coefficients_length; + memset(coefficients_.get(), 0.f, padding * sizeof(coefficients_[0])); + // The coefficients are reversed to compensate for the order in which the + // input samples are acquired (most recent last). + for (size_t i = 0; i < coefficients_length; ++i) { + coefficients_[i + padding] = coefficients[coefficients_length - i - 1]; + } + memset(state_.get(), + 0.f, + (max_input_length + state_length_) * sizeof(state_[0])); +} + +void FIRFilterNEON::Filter(const float* in, size_t length, float* out) { + assert(length > 0); + + memcpy(&state_[state_length_], in, length * sizeof(*in)); + + // Convolves the input signal |in| with the filter kernel |coefficients_| + // taking into account the previous state. + for (size_t i = 0; i < length; ++i) { + float* in_ptr = &state_[i]; + float* coef_ptr = coefficients_.get(); + + float32x4_t m_sum = vmovq_n_f32(0); + float32x4_t m_in; + + for (size_t j = 0; j < coefficients_length_; j += 4) { + m_in = vld1q_f32(in_ptr + j); + m_sum = vmlaq_f32(m_sum, m_in, vld1q_f32(coef_ptr + j)); + } + + float32x2_t m_half = vadd_f32(vget_high_f32(m_sum), vget_low_f32(m_sum)); + out[i] = vget_lane_f32(vpadd_f32(m_half, m_half), 0); + } + + // Update current state. + memmove(state_.get(), &state_[length], state_length_ * sizeof(state_[0])); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_neon.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_neon.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_neon.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_neon.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_COMMON_AUDIO_FIR_FILTER_NEON_H_ +#define WEBRTC_COMMON_AUDIO_FIR_FILTER_NEON_H_ + +#include "webrtc/common_audio/fir_filter.h" +#include "webrtc/system_wrappers/interface/aligned_malloc.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class FIRFilterNEON : public FIRFilter { + public: + FIRFilterNEON(const float* coefficients, + size_t coefficients_length, + size_t max_input_length); + + virtual void Filter(const float* in, size_t length, float* out) OVERRIDE; + + private: + size_t coefficients_length_; + size_t state_length_; + scoped_ptr coefficients_; + scoped_ptr state_; +}; + +} // namespace webrtc + +#endif // WEBRTC_COMMON_AUDIO_FIR_FILTER_NEON_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_sse.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_sse.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_sse.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_sse.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/common_audio/fir_filter_sse.h" + +#include +#include +#include + +#include "webrtc/system_wrappers/interface/aligned_malloc.h" + +namespace webrtc { + +FIRFilterSSE2::FIRFilterSSE2(const float* coefficients, + size_t coefficients_length, + size_t max_input_length) + : // Closest higher multiple of four. + coefficients_length_((coefficients_length + 3) & ~0x03), + state_length_(coefficients_length_ - 1), + coefficients_(static_cast( + AlignedMalloc(sizeof(float) * coefficients_length_, 16))), + state_(static_cast( + AlignedMalloc(sizeof(float) * (max_input_length + state_length_), + 16))) { + // Add zeros at the end of the coefficients. + size_t padding = coefficients_length_ - coefficients_length; + memset(coefficients_.get(), 0, padding * sizeof(coefficients_[0])); + // The coefficients are reversed to compensate for the order in which the + // input samples are acquired (most recent last). + for (size_t i = 0; i < coefficients_length; ++i) { + coefficients_[i + padding] = coefficients[coefficients_length - i - 1]; + } + memset(state_.get(), + 0, + (max_input_length + state_length_) * sizeof(state_[0])); +} + +void FIRFilterSSE2::Filter(const float* in, size_t length, float* out) { + assert(length > 0); + + memcpy(&state_[state_length_], in, length * sizeof(*in)); + + // Convolves the input signal |in| with the filter kernel |coefficients_| + // taking into account the previous state. + for (size_t i = 0; i < length; ++i) { + float* in_ptr = &state_[i]; + float* coef_ptr = coefficients_.get(); + + __m128 m_sum = _mm_setzero_ps(); + __m128 m_in; + + // Depending on if the pointer is aligned with 16 bytes or not it is loaded + // differently. + if (reinterpret_cast(in_ptr) & 0x0F) { + for (size_t j = 0; j < coefficients_length_; j += 4) { + m_in = _mm_loadu_ps(in_ptr + j); + m_sum = _mm_add_ps(m_sum, _mm_mul_ps(m_in, _mm_load_ps(coef_ptr + j))); + } + } else { + for (size_t j = 0; j < coefficients_length_; j += 4) { + m_in = _mm_load_ps(in_ptr + j); + m_sum = _mm_add_ps(m_sum, _mm_mul_ps(m_in, _mm_load_ps(coef_ptr + j))); + } + } + m_sum = _mm_add_ps(_mm_movehl_ps(m_sum, m_sum), m_sum); + _mm_store_ss(out + i, _mm_add_ss(m_sum, _mm_shuffle_ps(m_sum, m_sum, 1))); + } + + // Update current state. + memmove(state_.get(), &state_[length], state_length_ * sizeof(state_[0])); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_sse.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_sse.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_sse.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_sse.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_COMMON_AUDIO_FIR_FILTER_SSE_H_ +#define WEBRTC_COMMON_AUDIO_FIR_FILTER_SSE_H_ + +#include "webrtc/common_audio/fir_filter.h" +#include "webrtc/system_wrappers/interface/aligned_malloc.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class FIRFilterSSE2 : public FIRFilter { + public: + FIRFilterSSE2(const float* coefficients, + size_t coefficients_length, + size_t max_input_length); + + virtual void Filter(const float* in, size_t length, float* out) OVERRIDE; + + private: + size_t coefficients_length_; + size_t state_length_; + scoped_ptr coefficients_; + scoped_ptr state_; +}; + +} // namespace webrtc + +#endif // WEBRTC_COMMON_AUDIO_FIR_FILTER_SSE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/fir_filter_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/common_audio/fir_filter.h" + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +static const float kCoefficients[] = {0.2f, 0.3f, 0.5f, 0.7f, 0.11f}; +static const size_t kCoefficientsLength = sizeof(kCoefficients) / + sizeof(kCoefficients[0]); + +static const float kInput[] = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, + 8.f, 9.f, 10.f}; +static const size_t kInputLength = sizeof(kInput) / + sizeof(kInput[0]); + +void VerifyOutput(const float* expected_output, + const float* output, + size_t length) { + EXPECT_EQ(0, memcmp(expected_output, + output, + length * sizeof(expected_output[0]))); +} + +TEST(FIRFilterTest, FilterAsIdentity) { + const float kCoefficients[] = {1.f, 0.f, 0.f, 0.f, 0.f}; + float output[kInputLength]; + scoped_ptr filter(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kInputLength)); + filter->Filter(kInput, kInputLength, output); + + VerifyOutput(kInput, output, kInputLength); +} + +TEST(FIRFilterTest, FilterUsedAsScalarMultiplication) { + const float kCoefficients[] = {5.f, 0.f, 0.f, 0.f, 0.f}; + float output[kInputLength]; + scoped_ptr filter(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kInputLength)); + filter->Filter(kInput, kInputLength, output); + + EXPECT_FLOAT_EQ(5.f, output[0]); + EXPECT_FLOAT_EQ(20.f, output[3]); + EXPECT_FLOAT_EQ(25.f, output[4]); + EXPECT_FLOAT_EQ(50.f, output[kInputLength - 1]); +} + +TEST(FIRFilterTest, FilterUsedAsInputShifting) { + const float kCoefficients[] = {0.f, 0.f, 0.f, 0.f, 1.f}; + float output[kInputLength]; + scoped_ptr filter(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kInputLength)); + filter->Filter(kInput, kInputLength, output); + + EXPECT_FLOAT_EQ(0.f, output[0]); + EXPECT_FLOAT_EQ(0.f, output[3]); + EXPECT_FLOAT_EQ(1.f, output[4]); + EXPECT_FLOAT_EQ(2.f, output[5]); + EXPECT_FLOAT_EQ(6.f, output[kInputLength - 1]); +} + +TEST(FIRFilterTest, FilterUsedAsArbitraryWeighting) { + float output[kInputLength]; + scoped_ptr filter(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kInputLength)); + filter->Filter(kInput, kInputLength, output); + + EXPECT_FLOAT_EQ(0.2f, output[0]); + EXPECT_FLOAT_EQ(3.4f, output[3]); + EXPECT_FLOAT_EQ(5.21f, output[4]); + EXPECT_FLOAT_EQ(7.02f, output[5]); + EXPECT_FLOAT_EQ(14.26f, output[kInputLength - 1]); +} + +TEST(FIRFilterTest, FilterInLengthLesserOrEqualToCoefficientsLength) { + float output[kInputLength]; + scoped_ptr filter( + FIRFilter::Create(kCoefficients, kCoefficientsLength, 2)); + filter->Filter(kInput, 2, output); + + EXPECT_FLOAT_EQ(0.2f, output[0]); + EXPECT_FLOAT_EQ(0.7f, output[1]); + filter.reset(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kCoefficientsLength)); + filter->Filter(kInput, kCoefficientsLength, output); + + EXPECT_FLOAT_EQ(0.2f, output[0]); + EXPECT_FLOAT_EQ(3.4f, output[3]); + EXPECT_FLOAT_EQ(5.21f, output[4]); +} + +TEST(FIRFilterTest, MultipleFilterCalls) { + float output[kInputLength]; + scoped_ptr filter( + FIRFilter::Create(kCoefficients, kCoefficientsLength, 3)); + filter->Filter(kInput, 2, output); + EXPECT_FLOAT_EQ(0.2f, output[0]); + EXPECT_FLOAT_EQ(0.7f, output[1]); + + filter->Filter(kInput, 2, output); + EXPECT_FLOAT_EQ(1.3f, output[0]); + EXPECT_FLOAT_EQ(2.4f, output[1]); + + filter->Filter(kInput, 2, output); + EXPECT_FLOAT_EQ(2.81f, output[0]); + EXPECT_FLOAT_EQ(2.62f, output[1]); + + filter->Filter(kInput, 2, output); + EXPECT_FLOAT_EQ(2.81f, output[0]); + EXPECT_FLOAT_EQ(2.62f, output[1]); + + filter->Filter(&kInput[3], 3, output); + EXPECT_FLOAT_EQ(3.41f, output[0]); + EXPECT_FLOAT_EQ(4.12f, output[1]); + EXPECT_FLOAT_EQ(6.21f, output[2]); + + filter->Filter(&kInput[3], 3, output); + EXPECT_FLOAT_EQ(8.12f, output[0]); + EXPECT_FLOAT_EQ(9.14f, output[1]); + EXPECT_FLOAT_EQ(9.45f, output[2]); +} + +TEST(FIRFilterTest, VerifySampleBasedVsBlockBasedFiltering) { + float output_block_based[kInputLength]; + scoped_ptr filter(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kInputLength)); + filter->Filter(kInput, kInputLength, output_block_based); + + float output_sample_based[kInputLength]; + filter.reset(FIRFilter::Create(kCoefficients, kCoefficientsLength, 1)); + for (size_t i = 0; i < kInputLength; ++i) { + filter->Filter(&kInput[i], 1, &output_sample_based[i]); + } + + EXPECT_EQ(0, memcmp(output_sample_based, + output_block_based, + kInputLength)); +} + +TEST(FIRFilterTest, SimplestHighPassFilter) { + const float kCoefficients[] = {1.f, -1.f}; + const size_t kCoefficientsLength = sizeof(kCoefficients) / + sizeof(kCoefficients[0]); + + float kConstantInput[] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}; + const size_t kConstantInputLength = sizeof(kConstantInput) / + sizeof(kConstantInput[0]); + + float output[kConstantInputLength]; + scoped_ptr filter(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kConstantInputLength)); + filter->Filter(kConstantInput, kConstantInputLength, output); + EXPECT_FLOAT_EQ(1.f, output[0]); + for (size_t i = kCoefficientsLength - 1; i < kConstantInputLength; ++i) { + EXPECT_FLOAT_EQ(0.f, output[i]); + } +} + +TEST(FIRFilterTest, SimplestLowPassFilter) { + const float kCoefficients[] = {1.f, 1.f}; + const size_t kCoefficientsLength = sizeof(kCoefficients) / + sizeof(kCoefficients[0]); + + float kHighFrequencyInput[] = {-1.f, 1.f, -1.f, 1.f, -1.f, 1.f, -1.f, 1.f}; + const size_t kHighFrequencyInputLength = sizeof(kHighFrequencyInput) / + sizeof(kHighFrequencyInput[0]); + + float output[kHighFrequencyInputLength]; + scoped_ptr filter(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kHighFrequencyInputLength)); + filter->Filter(kHighFrequencyInput, kHighFrequencyInputLength, output); + EXPECT_FLOAT_EQ(-1.f, output[0]); + for (size_t i = kCoefficientsLength - 1; i < kHighFrequencyInputLength; ++i) { + EXPECT_FLOAT_EQ(0.f, output[i]); + } +} + +TEST(FIRFilterTest, SameOutputWhenSwapedCoefficientsAndInput) { + float output[kCoefficientsLength]; + float output_swaped[kCoefficientsLength]; + scoped_ptr filter(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kCoefficientsLength)); + // Use kCoefficientsLength for in_length to get same-length outputs. + filter->Filter(kInput, kCoefficientsLength, output); + + filter.reset(FIRFilter::Create( + kInput, kCoefficientsLength, kCoefficientsLength)); + filter->Filter(kCoefficients, kCoefficientsLength, output_swaped); + + for (size_t i = 0 ; i < kCoefficientsLength; ++i) { + EXPECT_FLOAT_EQ(output[i], output_swaped[i]); + } +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/include/audio_util.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/include/audio_util.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/include/audio_util.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/include/audio_util.h 2015-02-03 14:33:34.000000000 +0000 @@ -11,36 +11,91 @@ #ifndef WEBRTC_COMMON_AUDIO_INCLUDE_AUDIO_UTIL_H_ #define WEBRTC_COMMON_AUDIO_INCLUDE_AUDIO_UTIL_H_ +#include + +#include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/typedefs.h" namespace webrtc { -// Clamp the floating |value| to the range representable by an int16_t. -static inline float ClampInt16(float value) { - const float kMaxInt16 = 32767.f; - const float kMinInt16 = -32768.f; - return value < kMinInt16 ? kMinInt16 : - (value > kMaxInt16 ? kMaxInt16 : value); +typedef std::numeric_limits limits_int16; + +// The conversion functions use the following naming convention: +// S16: int16_t [-32768, 32767] +// Float: float [-1.0, 1.0] +// FloatS16: float [-32768.0, 32767.0] +static inline int16_t FloatToS16(float v) { + if (v > 0) + return v >= 1 ? limits_int16::max() : + static_cast(v * limits_int16::max() + 0.5f); + return v <= -1 ? limits_int16::min() : + static_cast(-v * limits_int16::min() - 0.5f); +} + +static inline float S16ToFloat(int16_t v) { + static const float kMaxInt16Inverse = 1.f / limits_int16::max(); + static const float kMinInt16Inverse = 1.f / limits_int16::min(); + return v * (v > 0 ? kMaxInt16Inverse : -kMinInt16Inverse); +} + +static inline int16_t FloatS16ToS16(float v) { + static const float kMaxRound = limits_int16::max() - 0.5f; + static const float kMinRound = limits_int16::min() + 0.5f; + if (v > 0) + return v >= kMaxRound ? limits_int16::max() : + static_cast(v + 0.5f); + return v <= kMinRound ? limits_int16::min() : + static_cast(v - 0.5f); } -// Return a rounded int16_t of the floating |value|. Doesn't handle overflow; -// use ClampInt16 if necessary. -static inline int16_t RoundToInt16(float value) { - return static_cast(value < 0.f ? value - 0.5f : value + 0.5f); +static inline float FloatToFloatS16(float v) { + return v * (v > 0 ? limits_int16::max() : -limits_int16::min()); } +static inline float FloatS16ToFloat(float v) { + static const float kMaxInt16Inverse = 1.f / limits_int16::max(); + static const float kMinInt16Inverse = 1.f / limits_int16::min(); + return v * (v > 0 ? kMaxInt16Inverse : -kMinInt16Inverse); +} + +void FloatToS16(const float* src, size_t size, int16_t* dest); +void S16ToFloat(const int16_t* src, size_t size, float* dest); +void FloatS16ToS16(const float* src, size_t size, int16_t* dest); +void FloatToFloatS16(const float* src, size_t size, float* dest); +void FloatS16ToFloat(const float* src, size_t size, float* dest); + // Deinterleave audio from |interleaved| to the channel buffers pointed to // by |deinterleaved|. There must be sufficient space allocated in the // |deinterleaved| buffers (|num_channel| buffers with |samples_per_channel| // per buffer). -void Deinterleave(const int16_t* interleaved, int samples_per_channel, - int num_channels, int16_t** deinterleaved); +template +void Deinterleave(const T* interleaved, int samples_per_channel, + int num_channels, T* const* deinterleaved) { + for (int i = 0; i < num_channels; ++i) { + T* channel = deinterleaved[i]; + int interleaved_idx = i; + for (int j = 0; j < samples_per_channel; ++j) { + channel[j] = interleaved[interleaved_idx]; + interleaved_idx += num_channels; + } + } +} // Interleave audio from the channel buffers pointed to by |deinterleaved| to // |interleaved|. There must be sufficient space allocated in |interleaved| // (|samples_per_channel| * |num_channels|). -void Interleave(const int16_t* const* deinterleaved, int samples_per_channel, - int num_channels, int16_t* interleaved); +template +void Interleave(const T* const* deinterleaved, int samples_per_channel, + int num_channels, T* interleaved) { + for (int i = 0; i < num_channels; ++i) { + const T* channel = deinterleaved[i]; + int interleaved_idx = i; + for (int j = 0; j < samples_per_channel; ++j) { + interleaved[interleaved_idx] = channel[j]; + interleaved_idx += num_channels; + } + } +} } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/lapped_transform.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/lapped_transform.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/lapped_transform.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/lapped_transform.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/common_audio/lapped_transform.h" + +#include +#include + +#include "webrtc/base/checks.h" +#include "webrtc/common_audio/real_fourier.h" + +namespace webrtc { + +void LappedTransform::BlockThunk::ProcessBlock(const float* const* input, + int num_frames, + int num_input_channels, + int num_output_channels, + float* const* output) { + CHECK_EQ(num_input_channels, parent_->in_channels_); + CHECK_EQ(num_output_channels, parent_->out_channels_); + CHECK_EQ(parent_->block_length_, num_frames); + + for (int i = 0; i < num_input_channels; ++i) { + memcpy(parent_->real_buf_.Row(i), input[i], + num_frames * sizeof(*input[0])); + parent_->fft_.Forward(parent_->real_buf_.Row(i), parent_->cplx_pre_.Row(i)); + } + + int block_length = RealFourier::ComplexLength( + RealFourier::FftOrder(num_frames)); + CHECK_EQ(parent_->cplx_length_, block_length); + parent_->block_processor_->ProcessAudioBlock(parent_->cplx_pre_.Array(), + num_input_channels, + parent_->cplx_length_, + num_output_channels, + parent_->cplx_post_.Array()); + + for (int i = 0; i < num_output_channels; ++i) { + parent_->fft_.Inverse(parent_->cplx_post_.Row(i), + parent_->real_buf_.Row(i)); + memcpy(output[i], parent_->real_buf_.Row(i), + num_frames * sizeof(*input[0])); + } +} + +LappedTransform::LappedTransform(int in_channels, int out_channels, + int chunk_length, const float* window, + int block_length, int shift_amount, + Callback* callback) + : blocker_callback_(this), + in_channels_(in_channels), + out_channels_(out_channels), + window_(window), + own_window_(false), + window_shift_amount_(shift_amount), + block_length_(block_length), + chunk_length_(chunk_length), + block_processor_(callback), + blocker_(nullptr), + fft_(RealFourier::FftOrder(block_length_)), + cplx_length_(RealFourier::ComplexLength(fft_.order())), + real_buf_(in_channels, block_length, RealFourier::kFftBufferAlignment), + cplx_pre_(in_channels, cplx_length_, RealFourier::kFftBufferAlignment), + cplx_post_(out_channels, cplx_length_, RealFourier::kFftBufferAlignment) { + CHECK(in_channels_ > 0 && out_channels_ > 0); + CHECK_GT(block_length_, 0); + CHECK_GT(chunk_length_, 0); + CHECK(block_processor_); + CHECK_EQ(0, block_length & (block_length - 1)); // block_length_ power of 2? + + if (!window_) { + own_window_ = true; + window_ = new float[block_length_]; + CHECK(window_ != nullptr); + window_shift_amount_ = block_length_; + float* temp = const_cast(window_); + for (int i = 0; i < block_length_; ++i) { + temp[i] = 1.0f; + } + } + + blocker_.reset(new Blocker(chunk_length_, block_length_, in_channels_, + out_channels_, window_, window_shift_amount_, + &blocker_callback_)); +} + +LappedTransform::~LappedTransform() { + if (own_window_) { + delete [] window_; + } +} + +void LappedTransform::ProcessChunk(const float* const* in_chunk, + float* const* out_chunk) { + blocker_->ProcessChunk(in_chunk, chunk_length_, in_channels_, out_channels_, + out_chunk); +} + +} // namespace webrtc + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/lapped_transform.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/lapped_transform.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/lapped_transform.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/lapped_transform.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_COMMON_AUDIO_LAPPED_TRANSFORM_H_ +#define WEBRTC_COMMON_AUDIO_LAPPED_TRANSFORM_H_ + +#include + +#include "webrtc/base/checks.h" +#include "webrtc/common_audio/blocker.h" +#include "webrtc/common_audio/real_fourier.h" +#include "webrtc/system_wrappers/interface/aligned_array.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +// Helper class for audio processing modules which operate on frequency domain +// input derived from the windowed time domain audio stream. +// +// The input audio chunk is sliced into possibly overlapping blocks, multiplied +// by a window and transformed with an FFT implementation. The transformed data +// is supplied to the given callback for processing. The processed output is +// then inverse transformed into the time domain and spliced back into a chunk +// which constitutes the final output of this processing module. +class LappedTransform { + public: + class Callback { + public: + virtual ~Callback() {} + + virtual void ProcessAudioBlock(const std::complex* const* in_block, + int in_channels, int frames, + int out_channels, + std::complex* const* out_block) = 0; + }; + + // Construct a transform instance. |chunk_length| is the number of samples in + // each channel. |window| defines the window, owned by the caller (a copy is + // made internally); can be NULL to disable windowing entirely. + // |block_length| defines the length of a block, in samples, even when + // windowing is disabled. |shift_length| is in samples. |callback| is the + // caller-owned audio processing function called for each block of the input + // chunk. + LappedTransform(int in_channels, int out_channels, int chunk_length, + const float* window, int block_length, int shift_amount, + Callback* callback); + ~LappedTransform(); + + // Main audio processing helper method. Internally slices |in_chunk| into + // blocks, transforms them to frequency domain, calls the callback for each + // block and returns a de-blocked time domain chunk of audio through + // |out_chunk|. Both buffers are caller-owned. + void ProcessChunk(const float* const* in_chunk, float* const* out_chunk); + + private: + // Internal middleware callback, given to the blocker. Transforms each block + // and hands it over to the processing method given at construction time. + friend class BlockThunk; + class BlockThunk : public BlockerCallback { + public: + explicit BlockThunk(LappedTransform* parent) : parent_(parent) {} + virtual ~BlockThunk() {} + + virtual void ProcessBlock(const float* const* input, int num_frames, + int num_input_channels, int num_output_channels, + float* const* output); + + private: + LappedTransform* parent_; + } blocker_callback_; + + int in_channels_; + int out_channels_; + + const float* window_; + bool own_window_; + int window_shift_amount_; + + int block_length_; + int chunk_length_; + Callback* block_processor_; + scoped_ptr blocker_; + + RealFourier fft_; + int cplx_length_; + AlignedArray real_buf_; + AlignedArray > cplx_pre_; + AlignedArray > cplx_post_; +}; + +} // namespace webrtc + +#endif // WEBRTC_COMMON_AUDIO_LAPPED_TRANSFORM_H_ + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/lapped_transform_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/lapped_transform_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/lapped_transform_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/lapped_transform_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/common_audio/lapped_transform.h" + +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" + +using std::complex; + +namespace { + +class NoopCallback : public webrtc::LappedTransform::Callback { + public: + NoopCallback() : block_num_(0) {} + + virtual void ProcessAudioBlock(const complex* const* in_block, + int in_channels, int frames, int out_channels, + complex* const* out_block) { + CHECK_EQ(in_channels, out_channels); + for (int i = 0; i < out_channels; ++i) { + memcpy(out_block[i], in_block[i], sizeof(**in_block) * frames); + } + ++block_num_; + } + + int block_num() { + return block_num_; + } + + private: + int block_num_; +}; + +class FftCheckerCallback : public webrtc::LappedTransform::Callback { + public: + FftCheckerCallback() : block_num_(0) {} + + virtual void ProcessAudioBlock(const complex* const* in_block, + int in_channels, int frames, int out_channels, + complex* const* out_block) { + CHECK_EQ(in_channels, out_channels); + + float full_length = (frames - 1) * 2; + ++block_num_; + + if (block_num_ == 1) { + for (int i = 0; i < frames; ++i) { + ASSERT_NEAR(in_block[0][i].real(), 0.0f, 1e-5f); + ASSERT_NEAR(in_block[0][i].imag(), 0.0f, 1e-5f); + } + } else { + ASSERT_NEAR(in_block[0][0].real(), full_length, 1e-5f); + ASSERT_NEAR(in_block[0][0].imag(), 0.0f, 1e-5f); + for (int i = 1; i < frames; ++i) { + ASSERT_NEAR(in_block[0][i].real(), 0.0f, 1e-5f); + ASSERT_NEAR(in_block[0][i].imag(), 0.0f, 1e-5f); + } + } + } + + int block_num() { + return block_num_; + } + + private: + int block_num_; +}; + +void SetFloatArray(float value, int rows, int cols, float* const* array) { + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + array[i][j] = value; + } + } +} + +} // namespace + +namespace webrtc { + +TEST(LappedTransformTest, Windowless) { + const int kChannels = 3; + const int kChunkLength = 512; + const int kBlockLength = 64; + const int kShiftAmount = 32; + NoopCallback noop; + LappedTransform trans(kChannels, kChannels, kChunkLength, nullptr, + kBlockLength, kShiftAmount, &noop); + float in_buffer[kChannels][kChunkLength]; + float* in_chunk[kChannels]; + float out_buffer[kChannels][kChunkLength]; + float* out_chunk[kChannels]; + + in_chunk[0] = in_buffer[0]; + in_chunk[1] = in_buffer[1]; + in_chunk[2] = in_buffer[2]; + out_chunk[0] = out_buffer[0]; + out_chunk[1] = out_buffer[1]; + out_chunk[2] = out_buffer[2]; + SetFloatArray(2.0f, kChannels, kChunkLength, in_chunk); + SetFloatArray(-1.0f, kChannels, kChunkLength, out_chunk); + + trans.ProcessChunk(in_chunk, out_chunk); + + for (int i = 0; i < kChannels; ++i) { + for (int j = 0; j < kChunkLength; ++j) { + ASSERT_NEAR(out_chunk[i][j], (j < kBlockLength) ? 0.0f : 2.0f, 1e-5f); + } + } + + ASSERT_EQ(kChunkLength / kBlockLength, noop.block_num()); +} + +TEST(LappedTransformTest, IdentityProcessor) { + const int kChunkLength = 512; + const int kBlockLength = 64; + const int kShiftAmount = 32; + NoopCallback noop; + float window[kBlockLength]; + float* window_ptr = window; + + // Identity window for |overlap = block_size / 2|. + SetFloatArray(sqrtf(0.5f), 1, kBlockLength, &window_ptr); + + LappedTransform trans(1, 1, kChunkLength, window, kBlockLength, kShiftAmount, + &noop); + float in_buffer[kChunkLength]; + float* in_chunk = in_buffer; + float out_buffer[kChunkLength]; + float* out_chunk = out_buffer; + + SetFloatArray(2.0f, 1, kChunkLength, &in_chunk); + SetFloatArray(-1.0f, 1, kChunkLength, &out_chunk); + + trans.ProcessChunk(&in_chunk, &out_chunk); + + for (int i = 0; i < kChunkLength; ++i) { + ASSERT_NEAR(out_chunk[i], (i < kBlockLength) ? 0.0f : 2.0f, 1e-5f); + } + + ASSERT_EQ(kChunkLength / kShiftAmount, noop.block_num()); +} + +TEST(LappedTransformTest, Callbacks) { + const int kChunkLength = 512; + const int kBlockLength = 64; + FftCheckerCallback call; + LappedTransform trans(1, 1, kChunkLength, nullptr, kBlockLength, + kBlockLength, &call); + float in_buffer[kChunkLength]; + float* in_chunk = in_buffer; + float out_buffer[kChunkLength]; + float* out_chunk = out_buffer; + + SetFloatArray(1.0f, 1, kChunkLength, &in_chunk); + SetFloatArray(-1.0f, 1, kChunkLength, &out_chunk); + + trans.ProcessChunk(&in_chunk, &out_chunk); + + ASSERT_EQ(kChunkLength / kBlockLength, call.block_num()); +} + +} // namespace webrtc + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -2,3 +2,12 @@ tina.legrand@webrtc.org jan.skoglund@webrtc.org andrew@webrtc.org + +per-file *.isolate=kjellander@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/real_fourier.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/real_fourier.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/real_fourier.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/real_fourier.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/common_audio/real_fourier.h" + +#include + +#include "third_party/openmax_dl/dl/sp/api/omxSP.h" +#include "webrtc/base/checks.h" + +namespace webrtc { + +using std::complex; + +// The omx implementation uses this macro to check order validity. +const int RealFourier::kMaxFftOrder = TWIDDLE_TABLE_ORDER; +const int RealFourier::kFftBufferAlignment = 32; + +RealFourier::RealFourier(int fft_order) + : order_(fft_order), + omx_spec_(nullptr) { + CHECK_GE(order_, 1); + CHECK_LE(order_, kMaxFftOrder); + + OMX_INT buffer_size; + OMXResult r; + + r = omxSP_FFTGetBufSize_R_F32(order_, &buffer_size); + CHECK_EQ(r, OMX_Sts_NoErr); + + omx_spec_ = malloc(buffer_size); + DCHECK(omx_spec_); + + r = omxSP_FFTInit_R_F32(omx_spec_, order_); + CHECK_EQ(r, OMX_Sts_NoErr); +} + +RealFourier::~RealFourier() { + free(omx_spec_); +} + +int RealFourier::FftOrder(int length) { + for (int order = 0; order <= kMaxFftOrder; order++) { + if ((1 << order) >= length) { + return order; + } + } + return -1; +} + +int RealFourier::ComplexLength(int order) { + CHECK_LE(order, kMaxFftOrder); + CHECK_GT(order, 0); + return (1 << order) / 2 + 1; +} + +RealFourier::fft_real_scoper RealFourier::AllocRealBuffer(int count) { + return fft_real_scoper(static_cast( + AlignedMalloc(sizeof(float) * count, kFftBufferAlignment))); +} + +RealFourier::fft_cplx_scoper RealFourier::AllocCplxBuffer(int count) { + return fft_cplx_scoper(static_cast*>( + AlignedMalloc(sizeof(complex) * count, kFftBufferAlignment))); +} + +void RealFourier::Forward(const float* src, complex* dest) const { + OMXResult r; + r = omxSP_FFTFwd_RToCCS_F32(src, reinterpret_cast(dest), omx_spec_); + CHECK_EQ(r, OMX_Sts_NoErr); +} + +void RealFourier::Inverse(const complex* src, float* dest) const { + OMXResult r; + r = omxSP_FFTInv_CCSToR_F32(reinterpret_cast(src), dest, + omx_spec_); + CHECK_EQ(r, OMX_Sts_NoErr); +} + +} // namespace webrtc + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/real_fourier.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/real_fourier.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/real_fourier.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/real_fourier.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_COMMON_AUDIO_REAL_FOURIER_H_ +#define WEBRTC_COMMON_AUDIO_REAL_FOURIER_H_ + +#include + +#include "webrtc/system_wrappers/interface/aligned_malloc.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +// Uniform interface class for the real DFT and its inverse, for power-of-2 +// input lengths. Also contains helper functions for buffer allocation, taking +// care of any memory alignment requirements the underlying library might have. + +namespace webrtc { + +class RealFourier { + public: + // Shorthand typenames for the scopers used by the buffer allocation helpers. + typedef scoped_ptr fft_real_scoper; + typedef scoped_ptr[], AlignedFreeDeleter> fft_cplx_scoper; + + // The maximum input order supported by this implementation. + static const int kMaxFftOrder; + + // The alignment required for all input and output buffers, in bytes. + static const int kFftBufferAlignment; + + // Construct a wrapper instance for the given input order, which must be + // between 1 and kMaxFftOrder, inclusively. + explicit RealFourier(int fft_order); + ~RealFourier(); + + // Short helper to compute the smallest FFT order (a power of 2) which will + // contain the given input length. Returns -1 if the order would have been + // too big for the implementation. + static int FftOrder(int length); + + // Short helper to compute the exact length, in complex floats, of the + // transform output (i.e. |2^order / 2 + 1|). + static int ComplexLength(int order); + + // Buffer allocation helpers. The buffers are large enough to hold |count| + // floats/complexes and suitably aligned for use by the implementation. + // The returned scopers are set up with proper deleters; the caller owns + // the allocated memory. + static fft_real_scoper AllocRealBuffer(int count); + static fft_cplx_scoper AllocCplxBuffer(int count); + + // Main forward transform interface. The output array need only be big + // enough for |2^order / 2 + 1| elements - the conjugate pairs are not + // returned. Input and output must be properly aligned (e.g. through + // AllocRealBuffer and AllocCplxBuffer) and input length must be + // |2^order| (same as given at construction time). + void Forward(const float* src, std::complex* dest) const; + + // Inverse transform. Same input format as output above, conjugate pairs + // not needed. + void Inverse(const std::complex* src, float* dest) const; + + int order() const { + return order_; + } + + private: + // Basically a forward declare of OMXFFTSpec_R_F32. To get rid of the + // dependency on openmax. + typedef void OMXFFTSpec_R_F32_; + const int order_; + + OMXFFTSpec_R_F32_* omx_spec_; +}; + +} // namespace webrtc + +#endif // WEBRTC_COMMON_AUDIO_REAL_FOURIER_H_ + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/real_fourier_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/real_fourier_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/real_fourier_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/real_fourier_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/common_audio/real_fourier.h" + +#include + +#include "testing/gtest/include/gtest/gtest.h" + +namespace webrtc { + +using std::complex; + +TEST(RealFourierStaticsTest, AllocatorAlignment) { + { + RealFourier::fft_real_scoper real; + real = RealFourier::AllocRealBuffer(3); + ASSERT_TRUE(real.get() != nullptr); + int64_t ptr_value = reinterpret_cast(real.get()); + ASSERT_EQ(ptr_value % RealFourier::kFftBufferAlignment, 0); + } + { + RealFourier::fft_cplx_scoper cplx; + cplx = RealFourier::AllocCplxBuffer(3); + ASSERT_TRUE(cplx.get() != nullptr); + int64_t ptr_value = reinterpret_cast(cplx.get()); + ASSERT_EQ(ptr_value % RealFourier::kFftBufferAlignment, 0); + } +} + +TEST(RealFourierStaticsTest, OrderComputation) { + ASSERT_EQ(RealFourier::FftOrder(2000000), -1); + ASSERT_EQ(RealFourier::FftOrder((1 << RealFourier::kMaxFftOrder) + 1), -1); + ASSERT_EQ(RealFourier::FftOrder(1 << RealFourier::kMaxFftOrder), + RealFourier::kMaxFftOrder); + ASSERT_EQ(RealFourier::FftOrder(13), 4); + ASSERT_EQ(RealFourier::FftOrder(32), 5); + ASSERT_EQ(RealFourier::FftOrder(2), 1); + ASSERT_EQ(RealFourier::FftOrder(1), 0); + ASSERT_EQ(RealFourier::FftOrder(0), 0); +} + +TEST(RealFourierStaticsTest, ComplexLengthComputation) { + ASSERT_EQ(RealFourier::ComplexLength(1), 2); + ASSERT_EQ(RealFourier::ComplexLength(2), 3); + ASSERT_EQ(RealFourier::ComplexLength(3), 5); + ASSERT_EQ(RealFourier::ComplexLength(4), 9); + ASSERT_EQ(RealFourier::ComplexLength(5), 17); + ASSERT_EQ(RealFourier::ComplexLength(7), 65); +} + +class RealFourierTest : public ::testing::Test { + protected: + RealFourierTest() + : rf_(new RealFourier(2)), + real_buffer_(RealFourier::AllocRealBuffer(4)), + cplx_buffer_(RealFourier::AllocCplxBuffer(3)) {} + + ~RealFourierTest() { + delete rf_; + } + + const RealFourier* rf_; + const RealFourier::fft_real_scoper real_buffer_; + const RealFourier::fft_cplx_scoper cplx_buffer_; +}; + +TEST_F(RealFourierTest, SimpleForwardTransform) { + real_buffer_[0] = 1.0f; + real_buffer_[1] = 2.0f; + real_buffer_[2] = 3.0f; + real_buffer_[3] = 4.0f; + + rf_->Forward(real_buffer_.get(), cplx_buffer_.get()); + + ASSERT_NEAR(cplx_buffer_[0].real(), 10.0f, 1e-8f); + ASSERT_NEAR(cplx_buffer_[0].imag(), 0.0f, 1e-8f); + ASSERT_NEAR(cplx_buffer_[1].real(), -2.0f, 1e-8f); + ASSERT_NEAR(cplx_buffer_[1].imag(), 2.0f, 1e-8f); + ASSERT_NEAR(cplx_buffer_[2].real(), -2.0f, 1e-8f); + ASSERT_NEAR(cplx_buffer_[2].imag(), 0.0f, 1e-8f); +} + +TEST_F(RealFourierTest, SimpleBackwardTransform) { + cplx_buffer_[0] = complex(10.0f, 0.0f); + cplx_buffer_[1] = complex(-2.0f, 2.0f); + cplx_buffer_[2] = complex(-2.0f, 0.0f); + + rf_->Inverse(cplx_buffer_.get(), real_buffer_.get()); + + ASSERT_NEAR(real_buffer_[0], 1.0f, 1e-8f); + ASSERT_NEAR(real_buffer_[1], 2.0f, 1e-8f); + ASSERT_NEAR(real_buffer_[2], 3.0f, 1e-8f); + ASSERT_NEAR(real_buffer_[3], 4.0f, 1e-8f); +} + +} // namespace webrtc + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_resampler -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := resampler.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../.. \ - $(LOCAL_PATH)/../signal_processing/include - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) -LOCAL_LDLIBS += -ldl -lpthread -endif - -ifneq ($(TARGET_SIMULATOR),true) -LOCAL_SHARED_LIBRARIES += libdl -endif - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/include/push_resampler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/include/push_resampler.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/include/push_resampler.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/include/push_resampler.h 2015-02-03 14:33:34.000000000 +0000 @@ -20,6 +20,7 @@ // Wraps PushSincResampler to provide stereo support. // TODO(ajm): add support for an arbitrary number of channels. +template class PushResampler { public: PushResampler(); @@ -32,22 +33,18 @@ // Returns the total number of samples provided in destination (e.g. 32 kHz, // 2 channel audio gives 640 samples). - int Resample(const int16_t* src, int src_length, int16_t* dst, - int dst_capacity); + int Resample(const T* src, int src_length, T* dst, int dst_capacity); private: - int ResampleSinc(const int16_t* src, int src_length, int16_t* dst, - int dst_capacity); - scoped_ptr sinc_resampler_; scoped_ptr sinc_resampler_right_; int src_sample_rate_hz_; int dst_sample_rate_hz_; int num_channels_; - scoped_array src_left_; - scoped_array src_right_; - scoped_array dst_left_; - scoped_array dst_right_; + scoped_ptr src_left_; + scoped_ptr src_right_; + scoped_ptr dst_left_; + scoped_ptr dst_right_; }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_resampler.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_resampler.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_resampler.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_resampler.cc 2015-02-03 14:33:34.000000000 +0000 @@ -17,22 +17,21 @@ namespace webrtc { -PushResampler::PushResampler() +template +PushResampler::PushResampler() : src_sample_rate_hz_(0), dst_sample_rate_hz_(0), - num_channels_(0), - src_left_(NULL), - src_right_(NULL), - dst_left_(NULL), - dst_right_(NULL) { + num_channels_(0) { } -PushResampler::~PushResampler() { +template +PushResampler::~PushResampler() { } -int PushResampler::InitializeIfNeeded(int src_sample_rate_hz, - int dst_sample_rate_hz, - int num_channels) { +template +int PushResampler::InitializeIfNeeded(int src_sample_rate_hz, + int dst_sample_rate_hz, + int num_channels) { if (src_sample_rate_hz == src_sample_rate_hz_ && dst_sample_rate_hz == dst_sample_rate_hz_ && num_channels == num_channels_) @@ -52,10 +51,10 @@ sinc_resampler_.reset(new PushSincResampler(src_size_10ms_mono, dst_size_10ms_mono)); if (num_channels_ == 2) { - src_left_.reset(new int16_t[src_size_10ms_mono]); - src_right_.reset(new int16_t[src_size_10ms_mono]); - dst_left_.reset(new int16_t[dst_size_10ms_mono]); - dst_right_.reset(new int16_t[dst_size_10ms_mono]); + src_left_.reset(new T[src_size_10ms_mono]); + src_right_.reset(new T[src_size_10ms_mono]); + dst_left_.reset(new T[dst_size_10ms_mono]); + dst_right_.reset(new T[dst_size_10ms_mono]); sinc_resampler_right_.reset(new PushSincResampler(src_size_10ms_mono, dst_size_10ms_mono)); } @@ -63,8 +62,9 @@ return 0; } -int PushResampler::Resample(const int16_t* src, int src_length, - int16_t* dst, int dst_capacity) { +template +int PushResampler::Resample(const T* src, int src_length, T* dst, + int dst_capacity) { const int src_size_10ms = src_sample_rate_hz_ * num_channels_ / 100; const int dst_size_10ms = dst_sample_rate_hz_ * num_channels_ / 100; if (src_length != src_size_10ms || dst_capacity < dst_size_10ms) @@ -73,13 +73,13 @@ if (src_sample_rate_hz_ == dst_sample_rate_hz_) { // The old resampler provides this memcpy facility in the case of matching // sample rates, so reproduce it here for the sinc resampler. - memcpy(dst, src, src_length * sizeof(int16_t)); + memcpy(dst, src, src_length * sizeof(T)); return src_length; } if (num_channels_ == 2) { const int src_length_mono = src_length / num_channels_; const int dst_capacity_mono = dst_capacity / num_channels_; - int16_t* deinterleaved[] = {src_left_.get(), src_right_.get()}; + T* deinterleaved[] = {src_left_.get(), src_right_.get()}; Deinterleave(src, src_length_mono, num_channels_, deinterleaved); int dst_length_mono = @@ -97,4 +97,8 @@ } } +// Explictly generate required instantiations. +template class PushResampler; +template class PushResampler; + } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_resampler_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_resampler_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_resampler_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_resampler_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -16,7 +16,7 @@ namespace webrtc { TEST(PushResamplerTest, VerifiesInputParameters) { - PushResampler resampler; + PushResampler resampler; EXPECT_EQ(-1, resampler.InitializeIfNeeded(-1, 16000, 1)); EXPECT_EQ(-1, resampler.InitializeIfNeeded(16000, -1, 1)); EXPECT_EQ(-1, resampler.InitializeIfNeeded(16000, 16000, 0)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_sinc_resampler.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_sinc_resampler.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_sinc_resampler.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_sinc_resampler.cc 2015-02-03 14:33:34.000000000 +0000 @@ -9,22 +9,23 @@ */ #include "webrtc/common_audio/include/audio_util.h" -#include "webrtc/common_audio/resampler/push_sinc_resampler.h" +#include #include +#include "webrtc/common_audio/resampler/push_sinc_resampler.h" + namespace webrtc { -PushSincResampler::PushSincResampler(int source_frames, - int destination_frames) +PushSincResampler::PushSincResampler(int source_frames, int destination_frames) : resampler_(new SincResampler(source_frames * 1.0 / destination_frames, - source_frames, this)), - float_buffer_(new float[destination_frames]), + source_frames, + this)), source_ptr_(NULL), + source_ptr_int_(NULL), destination_frames_(destination_frames), first_pass_(true), - source_available_(0) { -} + source_available_(0) {} PushSincResampler::~PushSincResampler() { } @@ -33,6 +34,21 @@ int source_length, int16_t* destination, int destination_capacity) { + if (!float_buffer_.get()) + float_buffer_.reset(new float[destination_frames_]); + + source_ptr_int_ = source; + // Pass NULL as the float source to have Run() read from the int16 source. + Resample(NULL, source_length, float_buffer_.get(), destination_frames_); + FloatS16ToS16(float_buffer_.get(), destination_frames_, destination); + source_ptr_int_ = NULL; + return destination_frames_; +} + +int PushSincResampler::Resample(const float* source, + int source_length, + float* destination, + int destination_capacity) { assert(source_length == resampler_->request_frames()); assert(destination_capacity >= destination_frames_); // Cache the source pointer. Calling Resample() will immediately trigger @@ -54,17 +70,14 @@ // request in order to prime the buffer with a single Run() request for // |source_frames|. if (first_pass_) - resampler_->Resample(resampler_->ChunkSize(), float_buffer_.get()); + resampler_->Resample(resampler_->ChunkSize(), destination); - resampler_->Resample(destination_frames_, float_buffer_.get()); - for (int i = 0; i < destination_frames_; ++i) - destination[i] = RoundToInt16(ClampInt16(float_buffer_[i])); + resampler_->Resample(destination_frames_, destination); source_ptr_ = NULL; return destination_frames_; } void PushSincResampler::Run(int frames, float* destination) { - assert(source_ptr_ != NULL); // Ensure we are only asked for the available samples. This would fail if // Run() was triggered more than once per Resample() call. assert(source_available_ == frames); @@ -74,11 +87,16 @@ // discarded, as described in Resample(). memset(destination, 0, frames * sizeof(float)); first_pass_ = false; + return; + } + + if (source_ptr_) { + memcpy(destination, source_ptr_, frames * sizeof(float)); } else { for (int i = 0; i < frames; ++i) - destination[i] = static_cast(source_ptr_[i]); - source_available_ -= frames; + destination[i] = static_cast(source_ptr_int_[i]); } + source_available_ -= frames; } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_sinc_resampler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_sinc_resampler.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_sinc_resampler.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_sinc_resampler.h 2015-02-03 14:33:34.000000000 +0000 @@ -11,8 +11,8 @@ #ifndef WEBRTC_COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_ #define WEBRTC_COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_ +#include "webrtc/base/constructormagic.h" #include "webrtc/common_audio/resampler/sinc_resampler.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/typedefs.h" @@ -35,16 +35,24 @@ // to |destination_frames|). int Resample(const int16_t* source, int source_frames, int16_t* destination, int destination_capacity); + int Resample(const float* source, + int source_frames, + float* destination, + int destination_capacity); // Implements SincResamplerCallback. virtual void Run(int frames, float* destination) OVERRIDE; SincResampler* get_resampler_for_testing() { return resampler_.get(); } + static float AlgorithmicDelaySeconds(int source_rate_hz) { + return 1.f / source_rate_hz * SincResampler::kKernelSize / 2; + } private: scoped_ptr resampler_; - scoped_array float_buffer_; - const int16_t* source_ptr_; + scoped_ptr float_buffer_; + const float* source_ptr_; + const int16_t* source_ptr_int_; const int destination_frames_; // True on the first call to Resample(), to prime the SincResampler buffer. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_sinc_resampler_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_sinc_resampler_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_sinc_resampler_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/push_sinc_resampler_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -12,6 +12,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/common_audio/include/audio_util.h" #include "webrtc/common_audio/resampler/push_sinc_resampler.h" #include "webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" @@ -34,6 +35,9 @@ virtual ~PushSincResamplerTest() {} protected: + void ResampleBenchmarkTest(bool int_format); + void ResampleTest(bool int_format); + int input_rate_; int output_rate_; double rms_error_; @@ -47,20 +51,18 @@ } }; -// Disabled because it takes too long to run routinely. Use for performance -// benchmarking when needed. -TEST_P(PushSincResamplerTest, DISABLED_ResampleBenchmark) { +void PushSincResamplerTest::ResampleBenchmarkTest(bool int_format) { const int input_samples = input_rate_ / 100; const int output_samples = output_rate_ / 100; - const int kResampleIterations = 200000; + const int kResampleIterations = 500000; // Source for data to be resampled. ZeroSource resampler_source; - scoped_array resampled_destination(new float[output_samples]); - scoped_array source(new float[input_samples]); - scoped_array source_int(new int16_t[input_samples]); - scoped_array destination_int(new int16_t[output_samples]); + scoped_ptr resampled_destination(new float[output_samples]); + scoped_ptr source(new float[input_samples]); + scoped_ptr source_int(new int16_t[input_samples]); + scoped_ptr destination_int(new int16_t[output_samples]); resampler_source.Run(input_samples, source.get()); for (int i = 0; i < input_samples; ++i) { @@ -82,10 +84,22 @@ PushSincResampler resampler(input_samples, output_samples); start = TickTime::Now(); - for (int i = 0; i < kResampleIterations; ++i) { - EXPECT_EQ(output_samples, - resampler.Resample(source_int.get(), input_samples, - destination_int.get(), output_samples)); + if (int_format) { + for (int i = 0; i < kResampleIterations; ++i) { + EXPECT_EQ(output_samples, + resampler.Resample(source_int.get(), + input_samples, + destination_int.get(), + output_samples)); + } + } else { + for (int i = 0; i < kResampleIterations; ++i) { + EXPECT_EQ(output_samples, + resampler.Resample(source.get(), + input_samples, + resampled_destination.get(), + output_samples)); + } } double total_time_us = (TickTime::Now() - start).Microseconds(); printf("PushSincResampler took %.2f us per frame; which is a %.1f%% overhead " @@ -93,8 +107,18 @@ (total_time_us - total_time_sinc_us) / total_time_sinc_us * 100); } +// Disabled because it takes too long to run routinely. Use for performance +// benchmarking when needed. +TEST_P(PushSincResamplerTest, DISABLED_BenchmarkInt) { + ResampleBenchmarkTest(true); +} + +TEST_P(PushSincResamplerTest, DISABLED_BenchmarkFloat) { + ResampleBenchmarkTest(false); +} + // Tests resampling using a given input and output sample rate. -TEST_P(PushSincResamplerTest, Resample) { +void PushSincResamplerTest::ResampleTest(bool int_format) { // Make comparisons using one second of data. static const double kTestDurationSecs = 1; // 10 ms blocks. @@ -115,11 +139,11 @@ // TODO(dalecurtis): If we switch to AVX/SSE optimization, we'll need to // allocate these on 32-byte boundaries and ensure they're sized % 32 bytes. - scoped_array resampled_destination(new float[output_samples]); - scoped_array pure_destination(new float[output_samples]); - scoped_array source(new float[input_samples]); - scoped_array source_int(new int16_t[input_block_size]); - scoped_array destination_int(new int16_t[output_block_size]); + scoped_ptr resampled_destination(new float[output_samples]); + scoped_ptr pure_destination(new float[output_samples]); + scoped_ptr source(new float[input_samples]); + scoped_ptr source_int(new int16_t[input_block_size]); + scoped_ptr destination_int(new int16_t[output_block_size]); // The sinc resampler has an implicit delay of approximately half the kernel // size at the input sample rate. By moving to a push model, this delay @@ -134,17 +158,26 @@ // With the PushSincResampler, we produce the signal block-by-10ms-block // rather than in a single pass, to exercise how it will be used in WebRTC. resampler_source.Run(input_samples, source.get()); - for (int i = 0; i < kNumBlocks; ++i) { - for (int j = 0; j < input_block_size; ++j) { - source_int[j] = static_cast(floor(32767 * - source[i * input_block_size + j] + 0.5)); + if (int_format) { + for (int i = 0; i < kNumBlocks; ++i) { + FloatToS16(&source[i * input_block_size], input_block_size, + source_int.get()); + EXPECT_EQ(output_block_size, + resampler.Resample(source_int.get(), + input_block_size, + destination_int.get(), + output_block_size)); + S16ToFloat(destination_int.get(), output_block_size, + &resampled_destination[i * output_block_size]); } - EXPECT_EQ(output_block_size, - resampler.Resample(source_int.get(), input_block_size, - destination_int.get(), output_block_size)); - for (int j = 0; j < output_block_size; ++j) { - resampled_destination[i * output_block_size + j] = - static_cast(destination_int[j]) / 32767; + } else { + for (int i = 0; i < kNumBlocks; ++i) { + EXPECT_EQ( + output_block_size, + resampler.Resample(&source[i * input_block_size], + input_block_size, + &resampled_destination[i * output_block_size], + output_block_size)); } } @@ -204,13 +237,19 @@ EXPECT_LE(high_freq_max_error, kHighFrequencyMaxError); } +TEST_P(PushSincResamplerTest, ResampleInt) { ResampleTest(true); } + +TEST_P(PushSincResamplerTest, ResampleFloat) { ResampleTest(false); } + // Almost all conversions have an RMS error of around -14 dbFS. static const double kResamplingRMSError = -14.42; // Thresholds chosen arbitrarily based on what each resampling reported during // testing. All thresholds are in dbFS, http://en.wikipedia.org/wiki/DBFS. INSTANTIATE_TEST_CASE_P( - PushSincResamplerTest, PushSincResamplerTest, testing::Values( + PushSincResamplerTest, + PushSincResamplerTest, + testing::Values( // First run through the rates tested in SincResamplerTest. The // thresholds are identical. // @@ -261,7 +300,7 @@ // practice anyway. // To 8 kHz - std::tr1::make_tuple(8000, 8000, kResamplingRMSError, -75.51), + std::tr1::make_tuple(8000, 8000, kResamplingRMSError, -75.50), std::tr1::make_tuple(16000, 8000, -18.56, -28.79), std::tr1::make_tuple(32000, 8000, -20.36, -14.13), std::tr1::make_tuple(44100, 8000, -21.00, -11.39), @@ -278,7 +317,7 @@ // To 32 kHz std::tr1::make_tuple(8000, 32000, kResamplingRMSError, -70.30), std::tr1::make_tuple(16000, 32000, kResamplingRMSError, -75.51), - std::tr1::make_tuple(32000, 32000, kResamplingRMSError, -75.56), + std::tr1::make_tuple(32000, 32000, kResamplingRMSError, -75.51), std::tr1::make_tuple(44100, 32000, -16.44, -51.10), std::tr1::make_tuple(48000, 32000, -16.90, -44.03), std::tr1::make_tuple(96000, 32000, -19.61, -18.04), diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinc_resampler.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinc_resampler.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinc_resampler.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinc_resampler.cc 2015-02-03 14:33:34.000000000 +0000 @@ -90,6 +90,7 @@ #include "webrtc/system_wrappers/interface/cpu_features_wrapper.h" #include "webrtc/typedefs.h" +#include #include #include @@ -114,13 +115,12 @@ } // If we know the minimum architecture at compile time, avoid CPU detection. -// iOS lies about its architecture, so we also need to exclude it here. -#if defined(WEBRTC_ARCH_X86_FAMILY) && !defined(WEBRTC_IOS) -#if defined(__SSE__) +#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(__SSE2__) #define CONVOLVE_FUNC Convolve_SSE void SincResampler::InitializeCPUSpecificFeatures() {} #else -// X86 CPU detection required. Function will be set by +// x86 CPU detection required. Function will be set by // InitializeCPUSpecificFeatures(). // TODO(dalecurtis): Once Chrome moves to an SSE baseline this can be removed. #define CONVOLVE_FUNC convolve_proc_ @@ -134,7 +134,7 @@ #define CONVOLVE_FUNC Convolve_NEON void SincResampler::InitializeCPUSpecificFeatures() {} #else -// NEON CPU detection required. Function will be set by +// ARM CPU detection required. Function will be set by // InitializeCPUSpecificFeatures(). #define CONVOLVE_FUNC convolve_proc_ @@ -165,12 +165,12 @@ AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))), input_buffer_(static_cast( AlignedMalloc(sizeof(float) * input_buffer_size_, 16))), -#if defined(WEBRTC_RESAMPLER_CPU_DETECTION) +#if defined(WEBRTC_CPU_DETECTION) convolve_proc_(NULL), #endif r1_(input_buffer_.get()), r2_(input_buffer_.get() + kKernelSize / 2) { -#if defined(WEBRTC_RESAMPLER_CPU_DETECTION) +#if defined(WEBRTC_CPU_DETECTION) InitializeCPUSpecificFeatures(); assert(convolve_proc_); #endif @@ -222,23 +222,22 @@ for (int i = 0; i < kKernelSize; ++i) { const int idx = i + offset_idx * kKernelSize; - const float pre_sinc = M_PI * (i - kKernelSize / 2 - subsample_offset); - kernel_pre_sinc_storage_.get()[idx] = pre_sinc; + const float pre_sinc = + static_cast(M_PI * (i - kKernelSize / 2 - subsample_offset)); + kernel_pre_sinc_storage_[idx] = pre_sinc; // Compute Blackman window, matching the offset of the sinc(). const float x = (i - subsample_offset) / kKernelSize; - const float window = kA0 - kA1 * cos(2.0 * M_PI * x) + kA2 - * cos(4.0 * M_PI * x); - kernel_window_storage_.get()[idx] = window; + const float window = static_cast(kA0 - kA1 * cos(2.0 * M_PI * x) + + kA2 * cos(4.0 * M_PI * x)); + kernel_window_storage_[idx] = window; // Compute the sinc with offset, then window the sinc() function and store // at the correct offset. - if (pre_sinc == 0) { - kernel_storage_.get()[idx] = sinc_scale_factor * window; - } else { - kernel_storage_.get()[idx] = - window * sin(sinc_scale_factor * pre_sinc) / pre_sinc; - } + kernel_storage_[idx] = static_cast(window * + ((pre_sinc == 0) ? + sinc_scale_factor : + (sin(sinc_scale_factor * pre_sinc) / pre_sinc))); } } } @@ -257,15 +256,13 @@ for (int offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) { for (int i = 0; i < kKernelSize; ++i) { const int idx = i + offset_idx * kKernelSize; - const float window = kernel_window_storage_.get()[idx]; - const float pre_sinc = kernel_pre_sinc_storage_.get()[idx]; + const float window = kernel_window_storage_[idx]; + const float pre_sinc = kernel_pre_sinc_storage_[idx]; - if (pre_sinc == 0) { - kernel_storage_.get()[idx] = sinc_scale_factor * window; - } else { - kernel_storage_.get()[idx] = - window * sin(sinc_scale_factor * pre_sinc) / pre_sinc; - } + kernel_storage_[idx] = static_cast(window * + ((pre_sinc == 0) ? + sinc_scale_factor : + (sin(sinc_scale_factor * pre_sinc) / pre_sinc))); } } } @@ -289,18 +286,19 @@ // // Note: The loop construct here can severely impact performance on ARM // or when built with clang. See https://codereview.chromium.org/18566009/ - for (int i = ceil((block_size_ - virtual_source_idx_) / current_io_ratio); + for (int i = static_cast( + ceil((block_size_ - virtual_source_idx_) / current_io_ratio)); i > 0; --i) { assert(virtual_source_idx_ < block_size_); // |virtual_source_idx_| lies in between two kernel offsets so figure out // what they are. - const int source_idx = virtual_source_idx_; + const int source_idx = static_cast(virtual_source_idx_); const double subsample_remainder = virtual_source_idx_ - source_idx; const double virtual_offset_idx = subsample_remainder * kKernelOffsetCount; - const int offset_idx = virtual_offset_idx; + const int offset_idx = static_cast(virtual_offset_idx); // We'll compute "convolutions" for the two kernels which straddle // |virtual_source_idx_|. @@ -347,7 +345,7 @@ #undef CONVOLVE_FUNC int SincResampler::ChunkSize() const { - return block_size_ / io_sample_rate_ratio_; + return static_cast(block_size_ / io_sample_rate_ratio_); } void SincResampler::Flush() { @@ -373,8 +371,8 @@ } // Linearly interpolate the two "convolutions". - return (1.0 - kernel_interpolation_factor) * sum1 - + kernel_interpolation_factor * sum2; + return static_cast((1.0 - kernel_interpolation_factor) * sum1 + + kernel_interpolation_factor * sum2); } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinc_resampler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinc_resampler.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinc_resampler.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinc_resampler.h 2015-02-03 14:33:34.000000000 +0000 @@ -14,19 +14,12 @@ #ifndef WEBRTC_COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_ #define WEBRTC_COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_ +#include "webrtc/base/constructormagic.h" #include "webrtc/system_wrappers/interface/aligned_malloc.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/test/testsupport/gtest_prod_util.h" #include "webrtc/typedefs.h" -#if (defined(WEBRTC_ARCH_X86_FAMILY) && !defined(WEBRTC_IOS) && \ - !defined(__SSE__)) || \ - (defined(WEBRTC_ARCH_ARM_V7) && !defined(WEBRTC_ARCH_ARM_NEON)) -// Convenience define. -#define WEBRTC_RESAMPLER_CPU_DETECTION -#endif - namespace webrtc { // Callback class for providing more data into the resampler. Expects |frames| @@ -106,9 +99,8 @@ void InitializeCPUSpecificFeatures(); // Compute convolution of |k1| and |k2| over |input_ptr|, resultant sums are - // linearly interpolated using |kernel_interpolation_factor|. On x86, the - // underlying implementation is chosen at run time based on SSE support. On - // ARM, NEON support is chosen at compile time based on compilation flags. + // linearly interpolated using |kernel_interpolation_factor|. On x86 and ARM + // the underlying implementation is chosen at run time. static float Convolve_C(const float* input_ptr, const float* k1, const float* k2, double kernel_interpolation_factor); #if defined(WEBRTC_ARCH_X86_FAMILY) @@ -146,18 +138,18 @@ // Contains kKernelOffsetCount kernels back-to-back, each of size kKernelSize. // The kernel offsets are sub-sample shifts of a windowed sinc shifted from // 0.0 to 1.0 sample. - scoped_ptr_malloc kernel_storage_; - scoped_ptr_malloc kernel_pre_sinc_storage_; - scoped_ptr_malloc kernel_window_storage_; + scoped_ptr kernel_storage_; + scoped_ptr kernel_pre_sinc_storage_; + scoped_ptr kernel_window_storage_; // Data from the source is copied into this buffer for each processing pass. - scoped_ptr_malloc input_buffer_; + scoped_ptr input_buffer_; // Stores the runtime selection of which Convolve function to use. // TODO(ajm): Move to using a global static which must only be initialized // once by the user. We're not doing this initially, because we don't have // e.g. a LazyInstance helper in webrtc. -#if defined(WEBRTC_RESAMPLER_CPU_DETECTION) +#if defined(WEBRTC_CPU_DETECTION) typedef float (*ConvolveProc)(const float*, const float*, const float*, double); ConvolveProc convolve_proc_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinc_resampler_sse.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinc_resampler_sse.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinc_resampler_sse.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinc_resampler_sse.cc 2015-02-03 14:33:34.000000000 +0000 @@ -41,8 +41,10 @@ } // Linearly interpolate the two "convolutions". - m_sums1 = _mm_mul_ps(m_sums1, _mm_set_ps1(1.0 - kernel_interpolation_factor)); - m_sums2 = _mm_mul_ps(m_sums2, _mm_set_ps1(kernel_interpolation_factor)); + m_sums1 = _mm_mul_ps(m_sums1, _mm_set_ps1( + static_cast(1.0 - kernel_interpolation_factor))); + m_sums2 = _mm_mul_ps(m_sums2, _mm_set_ps1( + static_cast(kernel_interpolation_factor))); m_sums1 = _mm_add_ps(m_sums1, m_sums2); // Sum components together. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinc_resampler_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinc_resampler_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinc_resampler_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinc_resampler_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -62,7 +62,7 @@ static const int kChunks = 2; int max_chunk_size = resampler.ChunkSize() * kChunks; - scoped_array resampled_destination(new float[max_chunk_size]); + scoped_ptr resampled_destination(new float[max_chunk_size]); // Verify requesting ChunkSize() frames causes a single callback. EXPECT_CALL(mock_source, Run(_, _)) @@ -81,7 +81,7 @@ MockSource mock_source; SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize, &mock_source); - scoped_array resampled_destination(new float[resampler.ChunkSize()]); + scoped_ptr resampled_destination(new float[resampler.ChunkSize()]); // Fill the resampler with junk data. EXPECT_CALL(mock_source, Run(_, _)) @@ -266,7 +266,7 @@ // Force an update to the sample rate ratio to ensure dyanmic sample rate // changes are working correctly. - scoped_array kernel(new float[SincResampler::kKernelStorageSize]); + scoped_ptr kernel(new float[SincResampler::kKernelStorageSize]); memcpy(kernel.get(), resampler.get_kernel_for_testing(), SincResampler::kKernelStorageSize); resampler.SetRatio(M_PI); @@ -278,8 +278,8 @@ // TODO(dalecurtis): If we switch to AVX/SSE optimization, we'll need to // allocate these on 32-byte boundaries and ensure they're sized % 32 bytes. - scoped_array resampled_destination(new float[output_samples]); - scoped_array pure_destination(new float[output_samples]); + scoped_ptr resampled_destination(new float[output_samples]); + scoped_ptr pure_destination(new float[output_samples]); // Generate resampled signal. resampler.Resample(output_samples, resampled_destination.get()); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h 2015-02-03 14:33:34.000000000 +0000 @@ -14,8 +14,8 @@ #ifndef WEBRTC_COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_ #define WEBRTC_COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_ +#include "webrtc/base/constructormagic.h" #include "webrtc/common_audio/resampler/sinc_resampler.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,126 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_spl -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := \ - auto_corr_to_refl_coef.c \ - auto_correlation.c \ - complex_fft.c \ - copy_set_operations.c \ - cross_correlation.c \ - division_operations.c \ - dot_product_with_scale.c \ - downsample_fast.c \ - energy.c \ - filter_ar.c \ - filter_ma_fast_q12.c \ - get_hanning_window.c \ - get_scaling_square.c \ - ilbc_specific_functions.c \ - levinson_durbin.c \ - lpc_to_refl_coef.c \ - min_max_operations.c \ - randomization_functions.c \ - real_fft.c \ - refl_coef_to_lpc.c \ - resample.c \ - resample_48khz.c \ - resample_by_2.c \ - resample_by_2_internal.c \ - resample_fractional.c \ - spl_init.c \ - spl_sqrt.c \ - spl_version.c \ - splitting_filter.c \ - sqrt_of_one_minus_x_squared.c \ - vector_scaling_operations.c - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../.. \ - external/webrtc - -ifeq ($(ARCH_ARM_HAVE_ARMV7A),true) -LOCAL_SRC_FILES += \ - filter_ar_fast_q12_armv7.S -else -LOCAL_SRC_FILES += \ - filter_ar_fast_q12.c -endif - -ifeq ($(TARGET_ARCH),arm) -LOCAL_SRC_FILES += \ - complex_bit_reverse_arm.S \ - spl_sqrt_floor_arm.S -else -LOCAL_SRC_FILES += \ - complex_bit_reverse.c \ - spl_sqrt_floor.c -endif - -LOCAL_SHARED_LIBRARIES := libstlport - -ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) -LOCAL_LDLIBS += -ldl -lpthread -endif - -ifneq ($(TARGET_SIMULATOR),true) -LOCAL_SHARED_LIBRARIES += libdl -endif - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) - -######################### -# Build the neon library. -ifeq ($(WEBRTC_BUILD_NEON_LIBS),true) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_spl_neon -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := \ - cross_correlation_neon.S \ - downsample_fast_neon.S \ - min_max_operations_neon.S \ - vector_scaling_operations_neon.S - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) \ - $(MY_ARM_CFLAGS_NEON) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../.. \ - external/webrtc - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) - -endif # ifeq ($(WEBRTC_BUILD_NEON_LIBS),true) - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/auto_corr_to_refl_coef.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/auto_corr_to_refl_coef.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/auto_corr_to_refl_coef.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/auto_corr_to_refl_coef.c 2015-02-03 14:33:34.000000000 +0000 @@ -88,15 +88,15 @@ pptr = P; wptr = w1ptr; tmp = (int16_t)(((int32_t)*p1ptr * (int32_t)*K + 16384) >> 15); - *pptr = WEBRTC_SPL_ADD_SAT_W16( *pptr, tmp ); + *pptr = WebRtcSpl_AddSatW16(*pptr, tmp); pptr++; for (i = 1; i <= use_order - n; i++) { tmp = (int16_t)(((int32_t)*wptr * (int32_t)*K + 16384) >> 15); - *pptr = WEBRTC_SPL_ADD_SAT_W16( *(pptr+1), tmp ); + *pptr = WebRtcSpl_AddSatW16(*(pptr + 1), tmp); pptr++; tmp = (int16_t)(((int32_t)*pptr * (int32_t)*K + 16384) >> 15); - *wptr = WEBRTC_SPL_ADD_SAT_W16( *wptr, tmp ); + *wptr = WebRtcSpl_AddSatW16(*wptr, tmp); wptr++; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/complex_fft.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/complex_fft.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/complex_fft.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/complex_fft.c 2015-02-03 14:33:34.000000000 +0000 @@ -65,18 +65,16 @@ { j = i + l; - tr32 = WEBRTC_SPL_RSHIFT_W32((WEBRTC_SPL_MUL_16_16(wr, frfi[2 * j]) - - WEBRTC_SPL_MUL_16_16(wi, frfi[2 * j + 1])), 15); + tr32 = (wr * frfi[2 * j] - wi * frfi[2 * j + 1]) >> 15; - ti32 = WEBRTC_SPL_RSHIFT_W32((WEBRTC_SPL_MUL_16_16(wr, frfi[2 * j + 1]) - + WEBRTC_SPL_MUL_16_16(wi, frfi[2 * j])), 15); + ti32 = (wr * frfi[2 * j + 1] + wi * frfi[2 * j]) >> 15; qr32 = (int32_t)frfi[2 * i]; qi32 = (int32_t)frfi[2 * i + 1]; - frfi[2 * j] = (int16_t)WEBRTC_SPL_RSHIFT_W32(qr32 - tr32, 1); - frfi[2 * j + 1] = (int16_t)WEBRTC_SPL_RSHIFT_W32(qi32 - ti32, 1); - frfi[2 * i] = (int16_t)WEBRTC_SPL_RSHIFT_W32(qr32 + tr32, 1); - frfi[2 * i + 1] = (int16_t)WEBRTC_SPL_RSHIFT_W32(qi32 + ti32, 1); + frfi[2 * j] = (int16_t)((qr32 - tr32) >> 1); + frfi[2 * j + 1] = (int16_t)((qi32 - ti32) >> 1); + frfi[2 * i] = (int16_t)((qr32 + tr32) >> 1); + frfi[2 * i + 1] = (int16_t)((qi32 + ti32) >> 1); } } @@ -105,7 +103,6 @@ #ifdef WEBRTC_ARCH_ARM_V7 int32_t wri = 0; - int32_t frfi_r = 0; __asm __volatile("pkhbt %0, %1, %2, lsl #16" : "=r"(wri) : "r"((int32_t)wr), "r"((int32_t)wi)); #endif @@ -115,19 +112,19 @@ j = i + l; #ifdef WEBRTC_ARCH_ARM_V7 + register int32_t frfi_r; __asm __volatile( - "pkhbt %[frfi_r], %[frfi_even], %[frfi_odd], lsl #16\n\t" - "smlsd %[tr32], %[wri], %[frfi_r], %[cfftrnd]\n\t" - "smladx %[ti32], %[wri], %[frfi_r], %[cfftrnd]\n\t" - :[frfi_r]"+r"(frfi_r), - [tr32]"=r"(tr32), - [ti32]"=r"(ti32) - :[frfi_even]"r"((int32_t)frfi[2*j]), - [frfi_odd]"r"((int32_t)frfi[2*j +1]), - [wri]"r"(wri), - [cfftrnd]"r"(CFFTRND) - ); - + "pkhbt %[frfi_r], %[frfi_even], %[frfi_odd]," + " lsl #16\n\t" + "smlsd %[tr32], %[wri], %[frfi_r], %[cfftrnd]\n\t" + "smladx %[ti32], %[wri], %[frfi_r], %[cfftrnd]\n\t" + :[frfi_r]"=&r"(frfi_r), + [tr32]"=&r"(tr32), + [ti32]"=r"(ti32) + :[frfi_even]"r"((int32_t)frfi[2*j]), + [frfi_odd]"r"((int32_t)frfi[2*j +1]), + [wri]"r"(wri), + [cfftrnd]"r"(CFFTRND)); #else tr32 = WEBRTC_SPL_MUL_16_16(wr, frfi[2 * j]) - WEBRTC_SPL_MUL_16_16(wi, frfi[2 * j + 1]) + CFFTRND; @@ -136,20 +133,20 @@ + WEBRTC_SPL_MUL_16_16(wi, frfi[2 * j]) + CFFTRND; #endif - tr32 = WEBRTC_SPL_RSHIFT_W32(tr32, 15 - CFFTSFT); - ti32 = WEBRTC_SPL_RSHIFT_W32(ti32, 15 - CFFTSFT); + tr32 >>= 15 - CFFTSFT; + ti32 >>= 15 - CFFTSFT; qr32 = ((int32_t)frfi[2 * i]) << CFFTSFT; qi32 = ((int32_t)frfi[2 * i + 1]) << CFFTSFT; - frfi[2 * j] = (int16_t)WEBRTC_SPL_RSHIFT_W32( - (qr32 - tr32 + CFFTRND2), 1 + CFFTSFT); - frfi[2 * j + 1] = (int16_t)WEBRTC_SPL_RSHIFT_W32( - (qi32 - ti32 + CFFTRND2), 1 + CFFTSFT); - frfi[2 * i] = (int16_t)WEBRTC_SPL_RSHIFT_W32( - (qr32 + tr32 + CFFTRND2), 1 + CFFTSFT); - frfi[2 * i + 1] = (int16_t)WEBRTC_SPL_RSHIFT_W32( - (qi32 + ti32 + CFFTRND2), 1 + CFFTSFT); + frfi[2 * j] = (int16_t)( + (qr32 - tr32 + CFFTRND2) >> (1 + CFFTSFT)); + frfi[2 * j + 1] = (int16_t)( + (qi32 - ti32 + CFFTRND2) >> (1 + CFFTSFT)); + frfi[2 * i] = (int16_t)( + (qr32 + tr32 + CFFTRND2) >> (1 + CFFTSFT)); + frfi[2 * i + 1] = (int16_t)( + (qi32 + ti32 + CFFTRND2) >> (1 + CFFTSFT)); } } @@ -220,19 +217,16 @@ { j = i + l; - tr32 = WEBRTC_SPL_RSHIFT_W32((WEBRTC_SPL_MUL_16_16_RSFT(wr, frfi[2 * j], 0) - - WEBRTC_SPL_MUL_16_16_RSFT(wi, frfi[2 * j + 1], 0)), 15); + tr32 = (wr * frfi[2 * j] - wi * frfi[2 * j + 1]) >> 15; - ti32 = WEBRTC_SPL_RSHIFT_W32( - (WEBRTC_SPL_MUL_16_16_RSFT(wr, frfi[2 * j + 1], 0) - + WEBRTC_SPL_MUL_16_16_RSFT(wi,frfi[2*j],0)), 15); + ti32 = (wr * frfi[2 * j + 1] + wi * frfi[2 * j]) >> 15; qr32 = (int32_t)frfi[2 * i]; qi32 = (int32_t)frfi[2 * i + 1]; - frfi[2 * j] = (int16_t)WEBRTC_SPL_RSHIFT_W32(qr32 - tr32, shift); - frfi[2 * j + 1] = (int16_t)WEBRTC_SPL_RSHIFT_W32(qi32 - ti32, shift); - frfi[2 * i] = (int16_t)WEBRTC_SPL_RSHIFT_W32(qr32 + tr32, shift); - frfi[2 * i + 1] = (int16_t)WEBRTC_SPL_RSHIFT_W32(qi32 + ti32, shift); + frfi[2 * j] = (int16_t)((qr32 - tr32) >> shift); + frfi[2 * j + 1] = (int16_t)((qi32 - ti32) >> shift); + frfi[2 * i] = (int16_t)((qr32 + tr32) >> shift); + frfi[2 * i + 1] = (int16_t)((qi32 + ti32) >> shift); } } } else @@ -252,7 +246,6 @@ #ifdef WEBRTC_ARCH_ARM_V7 int32_t wri = 0; - int32_t frfi_r = 0; __asm __volatile("pkhbt %0, %1, %2, lsl #16" : "=r"(wri) : "r"((int32_t)wr), "r"((int32_t)wi)); #endif @@ -262,12 +255,13 @@ j = i + l; #ifdef WEBRTC_ARCH_ARM_V7 + register int32_t frfi_r; __asm __volatile( "pkhbt %[frfi_r], %[frfi_even], %[frfi_odd], lsl #16\n\t" "smlsd %[tr32], %[wri], %[frfi_r], %[cifftrnd]\n\t" "smladx %[ti32], %[wri], %[frfi_r], %[cifftrnd]\n\t" - :[frfi_r]"+r"(frfi_r), - [tr32]"=r"(tr32), + :[frfi_r]"=&r"(frfi_r), + [tr32]"=&r"(tr32), [ti32]"=r"(ti32) :[frfi_even]"r"((int32_t)frfi[2*j]), [frfi_odd]"r"((int32_t)frfi[2*j +1]), @@ -282,20 +276,20 @@ ti32 = WEBRTC_SPL_MUL_16_16(wr, frfi[2 * j + 1]) + WEBRTC_SPL_MUL_16_16(wi, frfi[2 * j]) + CIFFTRND; #endif - tr32 = WEBRTC_SPL_RSHIFT_W32(tr32, 15 - CIFFTSFT); - ti32 = WEBRTC_SPL_RSHIFT_W32(ti32, 15 - CIFFTSFT); + tr32 >>= 15 - CIFFTSFT; + ti32 >>= 15 - CIFFTSFT; qr32 = ((int32_t)frfi[2 * i]) << CIFFTSFT; qi32 = ((int32_t)frfi[2 * i + 1]) << CIFFTSFT; - frfi[2 * j] = (int16_t)WEBRTC_SPL_RSHIFT_W32((qr32 - tr32+round2), - shift+CIFFTSFT); - frfi[2 * j + 1] = (int16_t)WEBRTC_SPL_RSHIFT_W32( - (qi32 - ti32 + round2), shift + CIFFTSFT); - frfi[2 * i] = (int16_t)WEBRTC_SPL_RSHIFT_W32((qr32 + tr32 + round2), - shift + CIFFTSFT); - frfi[2 * i + 1] = (int16_t)WEBRTC_SPL_RSHIFT_W32( - (qi32 + ti32 + round2), shift + CIFFTSFT); + frfi[2 * j] = (int16_t)( + (qr32 - tr32 + round2) >> (shift + CIFFTSFT)); + frfi[2 * j + 1] = (int16_t)( + (qi32 - ti32 + round2) >> (shift + CIFFTSFT)); + frfi[2 * i] = (int16_t)( + (qr32 + tr32 + round2) >> (shift + CIFFTSFT)); + frfi[2 * i + 1] = (int16_t)( + (qi32 + ti32 + round2) >> (shift + CIFFTSFT)); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/copy_set_operations.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/copy_set_operations.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/copy_set_operations.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/copy_set_operations.c 2015-02-03 14:33:34.000000000 +0000 @@ -17,8 +17,6 @@ * WebRtcSpl_CopyFromEndW16() * WebRtcSpl_ZerosArrayW16() * WebRtcSpl_ZerosArrayW32() - * WebRtcSpl_OnesArrayW16() - * WebRtcSpl_OnesArrayW32() * * The description header can be found in signal_processing_library.h * @@ -62,47 +60,21 @@ } } -int16_t WebRtcSpl_CopyFromEndW16(const int16_t *vector_in, - int16_t length, - int16_t samples, - int16_t *vector_out) +void WebRtcSpl_CopyFromEndW16(const int16_t *vector_in, + int length, + int samples, + int16_t *vector_out) { // Copy the last of the input vector to vector_out WEBRTC_SPL_MEMCPY_W16(vector_out, &vector_in[length - samples], samples); - - return samples; } -int16_t WebRtcSpl_ZerosArrayW16(int16_t *vector, int16_t length) +void WebRtcSpl_ZerosArrayW16(int16_t *vector, int length) { WebRtcSpl_MemSetW16(vector, 0, length); - return length; } -int16_t WebRtcSpl_ZerosArrayW32(int32_t *vector, int16_t length) +void WebRtcSpl_ZerosArrayW32(int32_t *vector, int length) { WebRtcSpl_MemSetW32(vector, 0, length); - return length; -} - -int16_t WebRtcSpl_OnesArrayW16(int16_t *vector, int16_t length) -{ - int16_t i; - int16_t *tmpvec = vector; - for (i = 0; i < length; i++) - { - *tmpvec++ = 1; - } - return length; -} - -int16_t WebRtcSpl_OnesArrayW32(int32_t *vector, int16_t length) -{ - int16_t i; - int32_t *tmpvec = vector; - for (i = 0; i < length; i++) - { - *tmpvec++ = 1; - } - return length; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/division_operations.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/division_operations.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/division_operations.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/division_operations.c 2015-02-03 14:33:34.000000000 +0000 @@ -113,23 +113,20 @@ tmpW32 = (int32_t)0x7fffffffL - tmpW32; // result in Q30 (tmpW32 = 2.0-(den*approx)) // Store tmpW32 in hi and low format - tmp_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmpW32, 16); - tmp_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((tmpW32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)tmp_hi, 16)), 1); + tmp_hi = (int16_t)(tmpW32 >> 16); + tmp_low = (int16_t)((tmpW32 - ((int32_t)tmp_hi << 16)) >> 1); // tmpW32 = 1/den in Q29 tmpW32 = ((WEBRTC_SPL_MUL_16_16(tmp_hi, approx) + (WEBRTC_SPL_MUL_16_16(tmp_low, approx) >> 15)) << 1); // 1/den in hi and low format - tmp_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmpW32, 16); - tmp_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((tmpW32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)tmp_hi, 16)), 1); + tmp_hi = (int16_t)(tmpW32 >> 16); + tmp_low = (int16_t)((tmpW32 - ((int32_t)tmp_hi << 16)) >> 1); // Store num in hi and low format - num_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(num, 16); - num_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((num - - WEBRTC_SPL_LSHIFT_W32((int32_t)num_hi, 16)), 1); + num_hi = (int16_t)(num >> 16); + num_low = (int16_t)((num - ((int32_t)num_hi << 16)) >> 1); // num * (1/den) by 32 bit multiplication (result in Q28) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/get_scaling_square.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/get_scaling_square.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/get_scaling_square.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/get_scaling_square.c 2015-02-03 14:33:34.000000000 +0000 @@ -17,14 +17,16 @@ #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -int WebRtcSpl_GetScalingSquare(int16_t *in_vector, int in_vector_length, int times) +int16_t WebRtcSpl_GetScalingSquare(int16_t* in_vector, + int in_vector_length, + int times) { - int nbits = WebRtcSpl_GetSizeInBits(times); + int16_t nbits = WebRtcSpl_GetSizeInBits(times); int i; int16_t smax = -1; int16_t sabs; int16_t *sptr = in_vector; - int t; + int16_t t; int looptimes = in_vector_length; for (i = looptimes; i > 0; i--) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/include/signal_processing_library.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/include/signal_processing_library.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/include/signal_processing_library.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/include/signal_processing_library.h 2015-02-03 14:33:34.000000000 +0000 @@ -27,7 +27,6 @@ #define WEBRTC_SPL_WORD32_MAX (int32_t)0x7fffffff #define WEBRTC_SPL_WORD32_MIN (int32_t)0x80000000 #define WEBRTC_SPL_MAX_LPC_ORDER 14 -#define WEBRTC_SPL_MAX_SEED_USED 0x80000000L #define WEBRTC_SPL_MIN(A, B) (A < B ? A : B) // Get min value #define WEBRTC_SPL_MAX(A, B) (A > B ? A : B) // Get max value // TODO(kma/bjorn): For the next two macros, investigate how to correct the code @@ -37,39 +36,14 @@ #define WEBRTC_SPL_ABS_W32(a) \ (((int32_t)a >= 0) ? ((int32_t)a) : -((int32_t)a)) -#ifdef WEBRTC_ARCH_LITTLE_ENDIAN -#define WEBRTC_SPL_GET_BYTE(a, nr) (((int8_t *)a)[nr]) -#define WEBRTC_SPL_SET_BYTE(d_ptr, val, index) \ - (((int8_t *)d_ptr)[index] = (val)) -#else -#define WEBRTC_SPL_GET_BYTE(a, nr) \ - ((((int16_t *)a)[nr >> 1]) >> (((nr + 1) & 0x1) * 8) & 0x00ff) -#define WEBRTC_SPL_SET_BYTE(d_ptr, val, index) \ - ((int16_t *)d_ptr)[index >> 1] = \ - ((((int16_t *)d_ptr)[index >> 1]) \ - & (0x00ff << (8 * ((index) & 0x1)))) | (val << (8 * ((index + 1) & 0x1))) -#endif - #define WEBRTC_SPL_MUL(a, b) \ ((int32_t) ((int32_t)(a) * (int32_t)(b))) #define WEBRTC_SPL_UMUL(a, b) \ ((uint32_t) ((uint32_t)(a) * (uint32_t)(b))) -#define WEBRTC_SPL_UMUL_RSFT16(a, b) \ - ((uint32_t) ((uint32_t)(a) * (uint32_t)(b)) >> 16) -#define WEBRTC_SPL_UMUL_16_16(a, b) \ - ((uint32_t) (uint16_t)(a) * (uint16_t)(b)) -#define WEBRTC_SPL_UMUL_16_16_RSFT16(a, b) \ - (((uint32_t) (uint16_t)(a) * (uint16_t)(b)) >> 16) #define WEBRTC_SPL_UMUL_32_16(a, b) \ ((uint32_t) ((uint32_t)(a) * (uint16_t)(b))) -#define WEBRTC_SPL_UMUL_32_16_RSFT16(a, b) \ - ((uint32_t) ((uint32_t)(a) * (uint16_t)(b)) >> 16) #define WEBRTC_SPL_MUL_16_U16(a, b) \ ((int32_t)(int16_t)(a) * (uint16_t)(b)) -#define WEBRTC_SPL_DIV(a, b) \ - ((int32_t) ((int32_t)(a) / (int32_t)(b))) -#define WEBRTC_SPL_UDIV(a, b) \ - ((uint32_t) ((uint32_t)(a) / (uint32_t)(b))) #ifndef WEBRTC_ARCH_ARM_V7 // For ARMv7 platforms, these are inline functions in spl_inl_armv7.h @@ -80,14 +54,6 @@ #define WEBRTC_SPL_MUL_16_32_RSFT16(a, b) \ (WEBRTC_SPL_MUL_16_16(a, b >> 16) \ + ((WEBRTC_SPL_MUL_16_16(a, (b & 0xffff) >> 1) + 0x4000) >> 15)) -#define WEBRTC_SPL_MUL_32_32_RSFT32(a32a, a32b, b32) \ - ((int32_t)(WEBRTC_SPL_MUL_16_32_RSFT16(a32a, b32) \ - + (WEBRTC_SPL_MUL_16_32_RSFT16(a32b, b32) >> 16))) -#define WEBRTC_SPL_MUL_32_32_RSFT32BI(a32, b32) \ - ((int32_t)(WEBRTC_SPL_MUL_16_32_RSFT16(( \ - (int16_t)(a32 >> 16)), b32) + \ - (WEBRTC_SPL_MUL_16_32_RSFT16(( \ - (int16_t)((a32 & 0x0000FFFF) >> 1)), b32) >> 15))) #endif #endif @@ -107,41 +73,23 @@ #define WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(a, b, c) \ ((WEBRTC_SPL_MUL_16_16(a, b) + ((int32_t) \ (((int32_t)1) << ((c) - 1)))) >> (c)) -#define WEBRTC_SPL_MUL_16_16_RSFT_WITH_FIXROUND(a, b) \ - ((WEBRTC_SPL_MUL_16_16(a, b) + ((int32_t) (1 << 14))) >> 15) // C + the 32 most significant bits of A * B #define WEBRTC_SPL_SCALEDIFF32(A, B, C) \ (C + (B >> 16) * A + (((uint32_t)(0x0000FFFF & B) * A) >> 16)) -#define WEBRTC_SPL_ADD_SAT_W32(a, b) WebRtcSpl_AddSatW32(a, b) #define WEBRTC_SPL_SAT(a, b, c) (b > a ? a : b < c ? c : b) -#define WEBRTC_SPL_MUL_32_16(a, b) ((a) * (b)) -#define WEBRTC_SPL_SUB_SAT_W32(a, b) WebRtcSpl_SubSatW32(a, b) -#define WEBRTC_SPL_ADD_SAT_W16(a, b) WebRtcSpl_AddSatW16(a, b) -#define WEBRTC_SPL_SUB_SAT_W16(a, b) WebRtcSpl_SubSatW16(a, b) - -// We cannot do casting here due to signed/unsigned problem -#define WEBRTC_SPL_IS_NEG(a) ((a) & 0x80000000) // Shifting with negative numbers allowed // Positive means left shift -#define WEBRTC_SPL_SHIFT_W16(x, c) \ - (((c) >= 0) ? ((x) << (c)) : ((x) >> (-(c)))) #define WEBRTC_SPL_SHIFT_W32(x, c) \ (((c) >= 0) ? ((x) << (c)) : ((x) >> (-(c)))) // Shifting with negative numbers not allowed // We cannot do casting here due to signed/unsigned problem -#define WEBRTC_SPL_RSHIFT_W16(x, c) ((x) >> (c)) -#define WEBRTC_SPL_LSHIFT_W16(x, c) ((x) << (c)) -#define WEBRTC_SPL_RSHIFT_W32(x, c) ((x) >> (c)) #define WEBRTC_SPL_LSHIFT_W32(x, c) ((x) << (c)) -#define WEBRTC_SPL_RSHIFT_U16(x, c) ((uint16_t)(x) >> (c)) -#define WEBRTC_SPL_LSHIFT_U16(x, c) ((uint16_t)(x) << (c)) #define WEBRTC_SPL_RSHIFT_U32(x, c) ((uint32_t)(x) >> (c)) -#define WEBRTC_SPL_LSHIFT_U32(x, c) ((uint32_t)(x) << (c)) #define WEBRTC_SPL_RAND(a) \ ((int16_t)(WEBRTC_SPL_MUL_16_16_RSFT((a), 18816, 7) & 0x00007fff)) @@ -150,14 +98,9 @@ extern "C" { #endif -#define WEBRTC_SPL_MEMCPY_W8(v1, v2, length) \ - memcpy(v1, v2, (length) * sizeof(char)) #define WEBRTC_SPL_MEMCPY_W16(v1, v2, length) \ memcpy(v1, v2, (length) * sizeof(int16_t)) -#define WEBRTC_SPL_MEMMOVE_W16(v1, v2, length) \ - memmove(v1, v2, (length) * sizeof(int16_t)) - // inline functions: #include "webrtc/common_audio/signal_processing/include/spl_inl.h" @@ -171,12 +114,9 @@ // functions. void WebRtcSpl_Init(); -// Get SPL Version -int16_t WebRtcSpl_get_version(char* version, int16_t length_in_bytes); - -int WebRtcSpl_GetScalingSquare(int16_t* in_vector, - int in_vector_length, - int times); +int16_t WebRtcSpl_GetScalingSquare(int16_t* in_vector, + int in_vector_length, + int times); // Copy and set operations. Implementation in copy_set_operations.c. // Descriptions at bottom of file. @@ -189,18 +129,14 @@ void WebRtcSpl_MemCpyReversedOrder(int16_t* out_vector, int16_t* in_vector, int vector_length); -int16_t WebRtcSpl_CopyFromEndW16(const int16_t* in_vector, - int16_t in_vector_length, - int16_t samples, - int16_t* out_vector); -int16_t WebRtcSpl_ZerosArrayW16(int16_t* vector, - int16_t vector_length); -int16_t WebRtcSpl_ZerosArrayW32(int32_t* vector, - int16_t vector_length); -int16_t WebRtcSpl_OnesArrayW16(int16_t* vector, - int16_t vector_length); -int16_t WebRtcSpl_OnesArrayW32(int32_t* vector, - int16_t vector_length); +void WebRtcSpl_CopyFromEndW16(const int16_t* in_vector, + int in_vector_length, + int samples, + int16_t* out_vector); +void WebRtcSpl_ZerosArrayW16(int16_t* vector, + int vector_length); +void WebRtcSpl_ZerosArrayW32(int32_t* vector, + int vector_length); // End: Copy and set operations. @@ -672,7 +608,6 @@ // Randomization functions. Implementations collected in // randomization_functions.c and descriptions at bottom of this file. -uint32_t WebRtcSpl_IncreaseSeed(uint32_t* seed); int16_t WebRtcSpl_RandU(uint32_t* seed); int16_t WebRtcSpl_RandN(uint32_t* seed); int16_t WebRtcSpl_RandUArray(int16_t* vector, @@ -983,10 +918,10 @@ * ******************************************************************/ -void WebRtcSpl_DownsampleBy2(const int16_t* in, int16_t len, +void WebRtcSpl_DownsampleBy2(const int16_t* in, int len, int16_t* out, int32_t* filtState); -void WebRtcSpl_UpsampleBy2(const int16_t* in, int16_t len, +void WebRtcSpl_UpsampleBy2(const int16_t* in, int len, int16_t* out, int32_t* filtState); /************************************************************ @@ -1162,8 +1097,6 @@ // Output: // - out_vector : Vector with the requested samples // -// Return value : Number of copied samples in |out_vector| -// // // WebRtcSpl_ZerosArrayW16(...) @@ -1178,24 +1111,6 @@ // Output: // - vector : Vector containing all zeros // -// Return value : Number of samples in vector -// - -// -// WebRtcSpl_OnesArrayW16(...) -// WebRtcSpl_OnesArrayW32(...) -// -// Inserts the value "one" in all positions of a w16 and a w32 vector -// respectively. -// -// Input: -// - vector_length : Number of samples in vector -// -// Output: -// - vector : Vector containing all ones -// -// Return value : Number of samples in vector -// // // WebRtcSpl_VectorBitShiftW16(...) @@ -1763,16 +1678,3 @@ // // Return Value: The value of a * b + c. // - -// int16_t WebRtcSpl_get_version(...) -// -// This function gives the version string of the Signal Processing Library. -// -// Input: -// - length_in_bytes : The size of Allocated space (in Bytes) where -// the version number is written to (in string format). -// -// Output: -// - version : Pointer to a buffer where the version number is -// written to. -// diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/include/spl_inl_armv7.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/include/spl_inl_armv7.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/include/spl_inl_armv7.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/include/spl_inl_armv7.h 2015-02-03 14:33:34.000000000 +0000 @@ -30,31 +30,6 @@ return tmp; } -/* This function produces result that is not bit exact with that by the generic - * C version in some cases, although the former is at least as accurate as the - * later. - */ -static __inline int32_t WEBRTC_SPL_MUL_32_32_RSFT32(int16_t a, - int16_t b, - int32_t c) { - int32_t tmp = 0; - __asm __volatile ( - "pkhbt %[tmp], %[b], %[a], lsl #16\n\t" - "smmulr %[tmp], %[tmp], %[c]\n\t" - :[tmp]"+r"(tmp) - :[a]"r"(a), - [b]"r"(b), - [c]"r"(c) - ); - return tmp; -} - -static __inline int32_t WEBRTC_SPL_MUL_32_32_RSFT32BI(int32_t a, int32_t b) { - int32_t tmp = 0; - __asm volatile ("smmulr %0, %1, %2":"=r"(tmp):"r"(a), "r"(b)); - return tmp; -} - static __inline int32_t WEBRTC_SPL_MUL_16_16(int16_t a, int16_t b) { int32_t tmp = 0; __asm __volatile ("smulbb %0, %1, %2":"=r"(tmp):"r"(a), "r"(b)); @@ -76,10 +51,6 @@ return (int16_t) s_sum; } -/* TODO(kma): find the cause of unittest errors by the next two functions: - * http://code.google.com/p/webrtc/issues/detail?id=740. - */ -#if 0 static __inline int32_t WebRtcSpl_AddSatW32(int32_t l_var1, int32_t l_var2) { int32_t l_sum = 0; @@ -95,7 +66,6 @@ return l_sub; } -#endif static __inline int16_t WebRtcSpl_SubSatW16(int16_t var1, int16_t var2) { int32_t s_sub = 0; @@ -113,7 +83,7 @@ return (int16_t)(32 - tmp); } -static __inline int WebRtcSpl_NormW32(int32_t a) { +static __inline int16_t WebRtcSpl_NormW32(int32_t a) { int32_t tmp = 0; if (a == 0) { @@ -125,20 +95,20 @@ __asm __volatile ("clz %0, %1":"=r"(tmp):"r"(a)); - return tmp - 1; + return (int16_t)(tmp - 1); } -static __inline int WebRtcSpl_NormU32(uint32_t a) { +static __inline int16_t WebRtcSpl_NormU32(uint32_t a) { int tmp = 0; if (a == 0) return 0; __asm __volatile ("clz %0, %1":"=r"(tmp):"r"(a)); - return tmp; + return (int16_t)tmp; } -static __inline int WebRtcSpl_NormW16(int16_t a) { +static __inline int16_t WebRtcSpl_NormW16(int16_t a) { int32_t tmp = 0; if (a == 0) { @@ -150,7 +120,7 @@ __asm __volatile ("clz %0, %1":"=r"(tmp):"r"(a)); - return tmp - 17; + return (int16_t)(tmp - 17); } // TODO(kma): add unit test. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/include/spl_inl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/include/spl_inl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/include/spl_inl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/include/spl_inl.h 2015-02-03 14:33:34.000000000 +0000 @@ -35,6 +35,44 @@ return out16; } +static __inline int32_t WebRtcSpl_AddSatW32(int32_t l_var1, int32_t l_var2) { + int32_t l_sum; + + // Perform long addition + l_sum = l_var1 + l_var2; + + if (l_var1 < 0) { // Check for underflow. + if ((l_var2 < 0) && (l_sum >= 0)) { + l_sum = (int32_t)0x80000000; + } + } else { // Check for overflow. + if ((l_var2 > 0) && (l_sum < 0)) { + l_sum = (int32_t)0x7FFFFFFF; + } + } + + return l_sum; +} + +static __inline int32_t WebRtcSpl_SubSatW32(int32_t l_var1, int32_t l_var2) { + int32_t l_diff; + + // Perform subtraction. + l_diff = l_var1 - l_var2; + + if (l_var1 < 0) { // Check for underflow. + if ((l_var2 > 0) && (l_diff > 0)) { + l_diff = (int32_t)0x80000000; + } + } else { // Check for overflow. + if ((l_var2 < 0) && (l_diff < 0)) { + l_diff = (int32_t)0x7FFFFFFF; + } + } + + return l_diff; +} + static __inline int16_t WebRtcSpl_AddSatW16(int16_t a, int16_t b) { return WebRtcSpl_SatW32ToW16((int32_t) a + (int32_t) b); } @@ -46,7 +84,7 @@ #if !defined(MIPS32_LE) static __inline int16_t WebRtcSpl_GetSizeInBits(uint32_t n) { - int bits; + int16_t bits; if (0xFFFF0000 & n) { bits = 16; @@ -62,8 +100,8 @@ return bits; } -static __inline int WebRtcSpl_NormW32(int32_t a) { - int zeros; +static __inline int16_t WebRtcSpl_NormW32(int32_t a) { + int16_t zeros; if (a == 0) { return 0; @@ -85,8 +123,8 @@ return zeros; } -static __inline int WebRtcSpl_NormU32(uint32_t a) { - int zeros; +static __inline int16_t WebRtcSpl_NormU32(uint32_t a) { + int16_t zeros; if (a == 0) return 0; @@ -103,8 +141,8 @@ return zeros; } -static __inline int WebRtcSpl_NormW16(int16_t a) { - int zeros; +static __inline int16_t WebRtcSpl_NormW16(int16_t a) { + int16_t zeros; if (a == 0) { return 0; @@ -132,46 +170,4 @@ #endif // WEBRTC_ARCH_ARM_V7 -// The following functions have no optimized versions. -// TODO(kma): Consider saturating add/sub instructions in X86 platform. -#if !defined(MIPS_DSP_R1_LE) -static __inline int32_t WebRtcSpl_AddSatW32(int32_t l_var1, int32_t l_var2) { - int32_t l_sum; - - // Perform long addition - l_sum = l_var1 + l_var2; - - if (l_var1 < 0) { // Check for underflow. - if ((l_var2 < 0) && (l_sum >= 0)) { - l_sum = (int32_t)0x80000000; - } - } else { // Check for overflow. - if ((l_var2 > 0) && (l_sum < 0)) { - l_sum = (int32_t)0x7FFFFFFF; - } - } - - return l_sum; -} - -static __inline int32_t WebRtcSpl_SubSatW32(int32_t l_var1, int32_t l_var2) { - int32_t l_diff; - - // Perform subtraction. - l_diff = l_var1 - l_var2; - - if (l_var1 < 0) { // Check for underflow. - if ((l_var2 > 0) && (l_diff > 0)) { - l_diff = (int32_t)0x80000000; - } - } else { // Check for overflow. - if ((l_var2 < 0) && (l_diff < 0)) { - l_diff = (int32_t)0x7FFFFFFF; - } - } - - return l_diff; -} -#endif // #if !defined(MIPS_DSP_R1_LE) - #endif // WEBRTC_SPL_SPL_INL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/include/spl_inl_mips.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/include/spl_inl_mips.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/include/spl_inl_mips.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/include/spl_inl_mips.h 2015-02-03 14:33:34.000000000 +0000 @@ -66,62 +66,6 @@ return value32; } -static __inline int32_t WEBRTC_SPL_MUL_32_32_RSFT32BI(int32_t a, - int32_t b) { - int32_t tmp = 0; - - if ((32767 < a) || (a < 0)) - tmp = WEBRTC_SPL_MUL_16_32_RSFT16(((int16_t)(a >> 16)), b); - tmp += WEBRTC_SPL_MUL_16_32_RSFT16(((int16_t)((a & 0x0000FFFF) >> 1)), - b) >> 15; - - return tmp; -} - -static __inline int32_t WEBRTC_SPL_MUL_32_32_RSFT32(int16_t a, - int16_t b, - int32_t c) { - int32_t tmp1 = 0, tmp2 = 0, tmp3 = 0, tmp4 = 0; - - __asm __volatile( - "sra %[tmp1], %[c], 16 \n\t" - "andi %[tmp2], %[c], 0xFFFF \n\t" -#if defined(MIPS32_R2_LE) - "seh %[a], %[a] \n\t" - "seh %[b], %[b] \n\t" -#else - "sll %[a], %[a], 16 \n\t" - "sra %[a], %[a], 16 \n\t" - "sll %[b], %[b], 16 \n\t" - "sra %[b], %[b], 16 \n\t" -#endif - "sra %[tmp2], %[tmp2], 1 \n\t" - "mul %[tmp3], %[a], %[tmp2] \n\t" - "mul %[tmp4], %[b], %[tmp2] \n\t" - "mul %[tmp2], %[a], %[tmp1] \n\t" - "mul %[tmp1], %[b], %[tmp1] \n\t" -#if defined(MIPS_DSP_R1_LE) - "shra_r.w %[tmp3], %[tmp3], 15 \n\t" - "shra_r.w %[tmp4], %[tmp4], 15 \n\t" -#else - "addiu %[tmp3], %[tmp3], 0x4000 \n\t" - "sra %[tmp3], %[tmp3], 15 \n\t" - "addiu %[tmp4], %[tmp4], 0x4000 \n\t" - "sra %[tmp4], %[tmp4], 15 \n\t" -#endif - "addu %[tmp3], %[tmp3], %[tmp2] \n\t" - "addu %[tmp4], %[tmp4], %[tmp1] \n\t" - "sra %[tmp4], %[tmp4], 16 \n\t" - "addu %[tmp1], %[tmp3], %[tmp4] \n\t" - : [tmp1] "=&r" (tmp1), [tmp2] "=&r" (tmp2), - [tmp3] "=&r" (tmp3), [tmp4] "=&r" (tmp4), - [a] "+r" (a), [b] "+r" (b) - : [c] "r" (c) - : "hi", "lo" - ); - return tmp1; -} - #if defined(MIPS_DSP_R1_LE) static __inline int16_t WebRtcSpl_SatW32ToW16(int32_t value32) { __asm __volatile( @@ -193,10 +137,10 @@ : [n] "r" (n), [i32] "r" (i32) ); - return bits; + return (int16_t)bits; } -static __inline int WebRtcSpl_NormW32(int32_t a) { +static __inline int16_t WebRtcSpl_NormW32(int32_t a) { int zeros = 0; __asm __volatile( @@ -216,10 +160,10 @@ : [a] "r" (a) ); - return zeros; + return (int16_t)zeros; } -static __inline int WebRtcSpl_NormU32(uint32_t a) { +static __inline int16_t WebRtcSpl_NormU32(uint32_t a) { int zeros = 0; __asm __volatile( @@ -228,10 +172,10 @@ : [a] "r" (a) ); - return (zeros & 0x1f); + return (int16_t)(zeros & 0x1f); } -static __inline int WebRtcSpl_NormW16(int16_t a) { +static __inline int16_t WebRtcSpl_NormW16(int16_t a) { int zeros = 0; int a0 = a << 16; @@ -252,7 +196,7 @@ : [a0] "r" (a0) ); - return zeros; + return (int16_t)zeros; } static __inline int32_t WebRtc_MulAccumW16(int16_t a, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/levinson_durbin.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/levinson_durbin.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/levinson_durbin.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/levinson_durbin.c 2015-02-03 14:33:34.000000000 +0000 @@ -45,9 +45,8 @@ { temp1W32 = WEBRTC_SPL_LSHIFT_W32(R[i], norm); // Put R in hi and low format - R_hi[i] = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - R_low[i] = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)R_hi[i], 16)), 1); + R_hi[i] = (int16_t)(temp1W32 >> 16); + R_low[i] = (int16_t)((temp1W32 - ((int32_t)R_hi[i] << 16)) >> 1); } // K = A[1] = -R[1] / R[0] @@ -63,19 +62,17 @@ } // Put K in hi and low format - K_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - K_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)K_hi, 16)), 1); + K_hi = (int16_t)(temp1W32 >> 16); + K_low = (int16_t)((temp1W32 - ((int32_t)K_hi << 16)) >> 1); // Store first reflection coefficient K[0] = K_hi; - temp1W32 = WEBRTC_SPL_RSHIFT_W32(temp1W32, 4); // A[1] in Q27 + temp1W32 >>= 4; // A[1] in Q27. // Put A[1] in hi and low format - A_hi[1] = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - A_low[1] = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)A_hi[1], 16)), 1); + A_hi[1] = (int16_t)(temp1W32 >> 16); + A_low[1] = (int16_t)((temp1W32 - ((int32_t)A_hi[1] << 16)) >> 1); // Alpha = R[0] * (1-K^2) @@ -86,9 +83,8 @@ temp1W32 = (int32_t)0x7fffffffL - temp1W32; // temp1W32 = (1 - K[0]*K[0]) in Q31 // Store temp1W32 = 1 - K[0]*K[0] on hi and low format - tmp_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - tmp_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)tmp_hi, 16)), 1); + tmp_hi = (int16_t)(temp1W32 >> 16); + tmp_low = (int16_t)((temp1W32 - ((int32_t)tmp_hi << 16)) >> 1); // Calculate Alpha in Q31 temp1W32 = ((WEBRTC_SPL_MUL_16_16(R_hi[0], tmp_hi) @@ -99,9 +95,8 @@ Alpha_exp = WebRtcSpl_NormW32(temp1W32); temp1W32 = WEBRTC_SPL_LSHIFT_W32(temp1W32, Alpha_exp); - Alpha_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - Alpha_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)Alpha_hi, 16)), 1); + Alpha_hi = (int16_t)(temp1W32 >> 16); + Alpha_low = (int16_t)((temp1W32 - ((int32_t)Alpha_hi << 16)) >> 1); // Perform the iterative calculations in the Levinson-Durbin algorithm @@ -155,9 +150,8 @@ } // Put K on hi and low format - K_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp3W32, 16); - K_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp3W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)K_hi, 16)), 1); + K_hi = (int16_t)(temp3W32 >> 16); + K_low = (int16_t)((temp3W32 - ((int32_t)K_hi << 16)) >> 1); // Store Reflection coefficient in Q15 K[i - 1] = K_hi; @@ -188,18 +182,18 @@ + (WEBRTC_SPL_MUL_16_16(K_low, A_hi[i-j]) >> 15)) << 1); // Put Anew in hi and low format - A_upd_hi[j] = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - A_upd_low[j] = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)A_upd_hi[j], 16)), 1); + A_upd_hi[j] = (int16_t)(temp1W32 >> 16); + A_upd_low[j] = (int16_t)( + (temp1W32 - ((int32_t)A_upd_hi[j] << 16)) >> 1); } // temp3W32 = K in Q27 (Convert from Q31 to Q27) - temp3W32 = WEBRTC_SPL_RSHIFT_W32(temp3W32, 4); + temp3W32 >>= 4; // Store Anew in hi and low format - A_upd_hi[i] = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp3W32, 16); - A_upd_low[i] = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp3W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)A_upd_hi[i], 16)), 1); + A_upd_hi[i] = (int16_t)(temp3W32 >> 16); + A_upd_low[i] = (int16_t)( + (temp3W32 - ((int32_t)A_upd_hi[i] << 16)) >> 1); // Alpha = Alpha * (1-K^2) @@ -210,9 +204,8 @@ temp1W32 = (int32_t)0x7fffffffL - temp1W32; // 1 - K*K in Q31 // Convert 1- K^2 in hi and low format - tmp_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - tmp_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)tmp_hi, 16)), 1); + tmp_hi = (int16_t)(temp1W32 >> 16); + tmp_low = (int16_t)((temp1W32 - ((int32_t)tmp_hi << 16)) >> 1); // Calculate Alpha = Alpha * (1-K^2) in Q31 temp1W32 = ((WEBRTC_SPL_MUL_16_16(Alpha_hi, tmp_hi) @@ -224,9 +217,8 @@ norm = WebRtcSpl_NormW32(temp1W32); temp1W32 = WEBRTC_SPL_LSHIFT_W32(temp1W32, norm); - Alpha_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - Alpha_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)Alpha_hi, 16)), 1); + Alpha_hi = (int16_t)(temp1W32 >> 16); + Alpha_low = (int16_t)((temp1W32 - ((int32_t)Alpha_hi << 16)) >> 1); // Update the total normalization of Alpha Alpha_exp = Alpha_exp + norm; @@ -253,7 +245,7 @@ temp1W32 = WEBRTC_SPL_LSHIFT_W32((int32_t)A_hi[i], 16) + WEBRTC_SPL_LSHIFT_W32((int32_t)A_low[i], 1); // Round and store upper word - A[i] = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32<<1)+(int32_t)32768, 16); + A[i] = (int16_t)(((temp1W32 << 1) + 32768) >> 16); } return 1; // Stable filters } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/lpc_to_refl_coef.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/lpc_to_refl_coef.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/lpc_to_refl_coef.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/lpc_to_refl_coef.c 2015-02-03 14:33:34.000000000 +0000 @@ -26,13 +26,13 @@ int32_t tmp_inv_denom32; int16_t tmp_inv_denom16; - k16[use_order - 1] = WEBRTC_SPL_LSHIFT_W16(a16[use_order], 3); //Q12<<3 => Q15 + k16[use_order - 1] = a16[use_order] << 3; // Q12<<3 => Q15 for (m = use_order - 1; m > 0; m--) { // (1 - k^2) in Q30 tmp_inv_denom32 = ((int32_t)1073741823) - WEBRTC_SPL_MUL_16_16(k16[m], k16[m]); // (1 - k^2) in Q15 - tmp_inv_denom16 = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp_inv_denom32, 15); + tmp_inv_denom16 = (int16_t)(tmp_inv_denom32 >> 15); for (k = 1; k <= m; k++) { @@ -47,7 +47,7 @@ for (k = 1; k < m; k++) { - a16[k] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32[k], 1); //Q13>>1 => Q12 + a16[k] = (int16_t)(tmp32[k] >> 1); // Q13>>1 => Q12 } tmp32[m] = WEBRTC_SPL_SAT(8191, tmp32[m], -8191); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/min_max_operations_neon.S thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/min_max_operations_neon.S --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/min_max_operations_neon.S 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/min_max_operations_neon.S 2015-02-03 14:33:34.000000000 +0000 @@ -209,7 +209,7 @@ cmp r1, #0 ble END_MIN_VALUE_W16 - vmov.i16 q12, #0x7FFF + vdup.16 q12, r2 cmp r1, #8 blt LOOP_MIN_VALUE_W16 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/randomization_functions.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/randomization_functions.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/randomization_functions.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/randomization_functions.c 2015-02-03 14:33:34.000000000 +0000 @@ -11,7 +11,6 @@ /* * This file contains implementations of the randomization functions - * WebRtcSpl_IncreaseSeed() * WebRtcSpl_RandU() * WebRtcSpl_RandN() * WebRtcSpl_RandUArray() @@ -22,6 +21,8 @@ #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +static const uint32_t kMaxSeedUsed = 0x80000000; + static const int16_t kRandNTable[] = { 9178, -7260, 40, 10189, 4894, -3531, -13779, 14764, -4008, -8884, -8990, 1008, 7368, 5184, 3251, -5817, @@ -89,31 +90,26 @@ 2374, -5797, 11839, 8940, -11874, 18213, 2855, 10492 }; -uint32_t WebRtcSpl_IncreaseSeed(uint32_t *seed) -{ - seed[0] = (seed[0] * ((int32_t)69069) + 1) & (WEBRTC_SPL_MAX_SEED_USED - 1); - return seed[0]; +static uint32_t IncreaseSeed(uint32_t* seed) { + seed[0] = (seed[0] * ((int32_t)69069) + 1) & (kMaxSeedUsed - 1); + return seed[0]; } -int16_t WebRtcSpl_RandU(uint32_t *seed) -{ - return (int16_t)(WebRtcSpl_IncreaseSeed(seed) >> 16); +int16_t WebRtcSpl_RandU(uint32_t* seed) { + return (int16_t)(IncreaseSeed(seed) >> 16); } -int16_t WebRtcSpl_RandN(uint32_t *seed) -{ - return kRandNTable[WebRtcSpl_IncreaseSeed(seed) >> 23]; +int16_t WebRtcSpl_RandN(uint32_t* seed) { + return kRandNTable[IncreaseSeed(seed) >> 23]; } -// Creates an array of uniformly distributed variables +// Creates an array of uniformly distributed variables. int16_t WebRtcSpl_RandUArray(int16_t* vector, int16_t vector_length, - uint32_t* seed) -{ - int i; - for (i = 0; i < vector_length; i++) - { - vector[i] = WebRtcSpl_RandU(seed); - } - return vector_length; + uint32_t* seed) { + int i; + for (i = 0; i < vector_length; i++) { + vector[i] = WebRtcSpl_RandU(seed); + } + return vector_length; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/real_fft_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/real_fft_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/real_fft_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/real_fft_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -10,6 +10,7 @@ #include "webrtc/common_audio/signal_processing/include/real_fft.h" #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/test/testsupport/gtest_disable.h" #include "webrtc/typedefs.h" #include "testing/gtest/include/gtest/gtest.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/refl_coef_to_lpc.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/refl_coef_to_lpc.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/refl_coef_to_lpc.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/refl_coef_to_lpc.c 2015-02-03 14:33:34.000000000 +0000 @@ -27,7 +27,7 @@ kptr = k; *a = 4096; // i.e., (Word16_MAX >> 3)+1. *any = *a; - a[1] = WEBRTC_SPL_RSHIFT_W16((*k), 3); + a[1] = *k >> 3; for (m = 1; m < use_order; m++) { @@ -38,7 +38,7 @@ anyptr = any; anyptr++; - any[m + 1] = WEBRTC_SPL_RSHIFT_W16((*kptr), 3); + any[m + 1] = *kptr >> 3; for (i = 0; i < m; i++) { *anyptr = (*aptr) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/resample_by_2.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/resample_by_2.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/resample_by_2.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/resample_by_2.c 2015-02-03 14:33:34.000000000 +0000 @@ -67,10 +67,10 @@ // decimator #if !defined(MIPS32_LE) -void WebRtcSpl_DownsampleBy2(const int16_t* in, int16_t len, +void WebRtcSpl_DownsampleBy2(const int16_t* in, int len, int16_t* out, int32_t* filtState) { int32_t tmp1, tmp2, diff, in32, out32; - int16_t i; + int i; register int32_t state0 = filtState[0]; register int32_t state1 = filtState[1]; @@ -125,10 +125,10 @@ #endif // #if defined(MIPS32_LE) -void WebRtcSpl_UpsampleBy2(const int16_t* in, int16_t len, +void WebRtcSpl_UpsampleBy2(const int16_t* in, int len, int16_t* out, int32_t* filtState) { int32_t tmp1, tmp2, diff, in32, out32; - int16_t i; + int i; register int32_t state0 = filtState[0]; register int32_t state1 = filtState[1]; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/resample_by_2_mips.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/resample_by_2_mips.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/resample_by_2_mips.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/resample_by_2_mips.c 2015-02-03 14:33:34.000000000 +0000 @@ -29,11 +29,11 @@ // decimator void WebRtcSpl_DownsampleBy2(const int16_t* in, - int16_t len, + int len, int16_t* out, int32_t* filtState) { int32_t out32; - int16_t i, len1; + int i, len1; register int32_t state0 = filtState[0]; register int32_t state1 = filtState[1]; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/signal_processing_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/signal_processing_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/signal_processing_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/signal_processing_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -30,77 +30,44 @@ int B = 21; int a = -3; int b = WEBRTC_SPL_WORD32_MAX; - int nr = 2; - int d_ptr2 = 0; EXPECT_EQ(10, WEBRTC_SPL_MIN(A, B)); EXPECT_EQ(21, WEBRTC_SPL_MAX(A, B)); EXPECT_EQ(3, WEBRTC_SPL_ABS_W16(a)); EXPECT_EQ(3, WEBRTC_SPL_ABS_W32(a)); - EXPECT_EQ(0, WEBRTC_SPL_GET_BYTE(&B, nr)); - WEBRTC_SPL_SET_BYTE(&d_ptr2, 1, nr); - EXPECT_EQ(65536, d_ptr2); EXPECT_EQ(-63, WEBRTC_SPL_MUL(a, B)); EXPECT_EQ(-2147483645, WEBRTC_SPL_MUL(a, b)); EXPECT_EQ(2147483651u, WEBRTC_SPL_UMUL(a, b)); b = WEBRTC_SPL_WORD16_MAX >> 1; - EXPECT_EQ(65535u, WEBRTC_SPL_UMUL_RSFT16(a, b)); - EXPECT_EQ(1073627139u, WEBRTC_SPL_UMUL_16_16(a, b)); - EXPECT_EQ(16382u, WEBRTC_SPL_UMUL_16_16_RSFT16(a, b)); EXPECT_EQ(4294918147u, WEBRTC_SPL_UMUL_32_16(a, b)); - EXPECT_EQ(65535u, WEBRTC_SPL_UMUL_32_16_RSFT16(a, b)); EXPECT_EQ(-49149, WEBRTC_SPL_MUL_16_U16(a, b)); a = b; b = -3; - EXPECT_EQ(-5461, WEBRTC_SPL_DIV(a, b)); - EXPECT_EQ(0u, WEBRTC_SPL_UDIV(a, b)); EXPECT_EQ(-1, WEBRTC_SPL_MUL_16_32_RSFT16(a, b)); EXPECT_EQ(-1, WEBRTC_SPL_MUL_16_32_RSFT15(a, b)); EXPECT_EQ(-3, WEBRTC_SPL_MUL_16_32_RSFT14(a, b)); EXPECT_EQ(-24, WEBRTC_SPL_MUL_16_32_RSFT11(a, b)); - int a32 = WEBRTC_SPL_WORD32_MAX; - int a32a = (WEBRTC_SPL_WORD32_MAX >> 16); - int a32b = (WEBRTC_SPL_WORD32_MAX & 0x0000ffff); - EXPECT_EQ(5, WEBRTC_SPL_MUL_32_32_RSFT32(a32a, a32b, A)); - EXPECT_EQ(5, WEBRTC_SPL_MUL_32_32_RSFT32BI(a32, A)); - EXPECT_EQ(-12288, WEBRTC_SPL_MUL_16_16_RSFT(a, b, 2)); EXPECT_EQ(-12287, WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(a, b, 2)); - EXPECT_EQ(-1, WEBRTC_SPL_MUL_16_16_RSFT_WITH_FIXROUND(a, b)); - EXPECT_EQ(16380, WEBRTC_SPL_ADD_SAT_W32(a, b)); EXPECT_EQ(21, WEBRTC_SPL_SAT(a, A, B)); EXPECT_EQ(21, WEBRTC_SPL_SAT(a, B, A)); - EXPECT_EQ(-49149, WEBRTC_SPL_MUL_32_16(a, b)); - - EXPECT_EQ(16386, WEBRTC_SPL_SUB_SAT_W32(a, b)); - EXPECT_EQ(16380, WEBRTC_SPL_ADD_SAT_W16(a, b)); - EXPECT_EQ(16386, WEBRTC_SPL_SUB_SAT_W16(a, b)); - - EXPECT_TRUE(WEBRTC_SPL_IS_NEG(b)); // Shifting with negative numbers allowed int shift_amount = 1; // Workaround compiler warning using variable here. // Positive means left shift - EXPECT_EQ(32766, WEBRTC_SPL_SHIFT_W16(a, shift_amount)); EXPECT_EQ(32766, WEBRTC_SPL_SHIFT_W32(a, shift_amount)); // Shifting with negative numbers not allowed // We cannot do casting here due to signed/unsigned problem - EXPECT_EQ(8191, WEBRTC_SPL_RSHIFT_W16(a, 1)); - EXPECT_EQ(32766, WEBRTC_SPL_LSHIFT_W16(a, 1)); - EXPECT_EQ(8191, WEBRTC_SPL_RSHIFT_W32(a, 1)); EXPECT_EQ(32766, WEBRTC_SPL_LSHIFT_W32(a, 1)); - EXPECT_EQ(8191, WEBRTC_SPL_RSHIFT_U16(a, 1)); - EXPECT_EQ(32766, WEBRTC_SPL_LSHIFT_U16(a, 1)); EXPECT_EQ(8191u, WEBRTC_SPL_RSHIFT_U32(a, 1)); - EXPECT_EQ(32766u, WEBRTC_SPL_LSHIFT_U32(a, 1)); EXPECT_EQ(1470, WEBRTC_SPL_RAND(A)); @@ -115,18 +82,10 @@ EXPECT_EQ(-1073741824, WEBRTC_SPL_MUL_16_32_RSFT16(WEBRTC_SPL_WORD16_MIN, WEBRTC_SPL_WORD32_MAX)); - EXPECT_EQ(0x3fffffff, WEBRTC_SPL_MUL_32_32_RSFT32(WEBRTC_SPL_WORD16_MAX, - 0xffff, WEBRTC_SPL_WORD32_MAX)); - EXPECT_EQ(0x3fffffff, WEBRTC_SPL_MUL_32_32_RSFT32BI(WEBRTC_SPL_WORD32_MAX, - WEBRTC_SPL_WORD32_MAX)); #else EXPECT_EQ(-1073741823, WEBRTC_SPL_MUL_16_32_RSFT16(WEBRTC_SPL_WORD16_MIN, WEBRTC_SPL_WORD32_MAX)); - EXPECT_EQ(0x3fff7ffe, WEBRTC_SPL_MUL_32_32_RSFT32(WEBRTC_SPL_WORD16_MAX, - 0xffff, WEBRTC_SPL_WORD32_MAX)); - EXPECT_EQ(0x3ffffffd, WEBRTC_SPL_MUL_32_32_RSFT32BI(WEBRTC_SPL_WORD32_MAX, - WEBRTC_SPL_WORD32_MAX)); #endif } @@ -135,7 +94,6 @@ int16_t b16 = -17; int32_t a32 = 111121; int32_t b32 = -1711; - char bVersion[8]; EXPECT_EQ(17, WebRtcSpl_GetSizeInBits(a32)); @@ -149,10 +107,9 @@ EXPECT_EQ(0, WebRtcSpl_NormW16(WEBRTC_SPL_WORD16_MIN)); EXPECT_EQ(4, WebRtcSpl_NormW16(b32)); - EXPECT_EQ(0, WebRtcSpl_NormU32(0)); - EXPECT_EQ(0, WebRtcSpl_NormU32(-1)); - EXPECT_EQ(0, WebRtcSpl_NormU32(WEBRTC_SPL_WORD32_MIN)); - EXPECT_EQ(15, WebRtcSpl_NormU32(a32)); + EXPECT_EQ(0, WebRtcSpl_NormU32(0u)); + EXPECT_EQ(0, WebRtcSpl_NormU32(0xffffffff)); + EXPECT_EQ(15, WebRtcSpl_NormU32(static_cast(a32))); EXPECT_EQ(104, WebRtcSpl_AddSatW16(a16, b16)); EXPECT_EQ(138, WebRtcSpl_SubSatW16(a16, b16)); @@ -176,8 +133,6 @@ a32 = 0x80000000; b32 = 0x7fffffff; EXPECT_EQ(static_cast(0x80000000), WebRtcSpl_SubSatW32(a32, b32)); - - EXPECT_EQ(0, WebRtcSpl_get_version(bVersion, 8)); } TEST_F(SplTest, MathOperationsTest) { @@ -199,11 +154,9 @@ TEST_F(SplTest, BasicArrayOperationsTest) { const int kVectorSize = 4; int B[] = {4, 12, 133, 1100}; - uint8_t b8[kVectorSize]; int16_t b16[kVectorSize]; int32_t b32[kVectorSize]; - uint8_t bTmp8[kVectorSize]; int16_t bTmp16[kVectorSize]; int32_t bTmp32[kVectorSize]; @@ -211,35 +164,22 @@ for (int kk = 0; kk < kVectorSize; ++kk) { EXPECT_EQ(3, b16[kk]); } - EXPECT_EQ(kVectorSize, WebRtcSpl_ZerosArrayW16(b16, kVectorSize)); + WebRtcSpl_ZerosArrayW16(b16, kVectorSize); for (int kk = 0; kk < kVectorSize; ++kk) { EXPECT_EQ(0, b16[kk]); } - EXPECT_EQ(kVectorSize, WebRtcSpl_OnesArrayW16(b16, kVectorSize)); - for (int kk = 0; kk < kVectorSize; ++kk) { - EXPECT_EQ(1, b16[kk]); - } WebRtcSpl_MemSetW32(b32, 3, kVectorSize); for (int kk = 0; kk < kVectorSize; ++kk) { EXPECT_EQ(3, b32[kk]); } - EXPECT_EQ(kVectorSize, WebRtcSpl_ZerosArrayW32(b32, kVectorSize)); + WebRtcSpl_ZerosArrayW32(b32, kVectorSize); for (int kk = 0; kk < kVectorSize; ++kk) { EXPECT_EQ(0, b32[kk]); } - EXPECT_EQ(kVectorSize, WebRtcSpl_OnesArrayW32(b32, kVectorSize)); - for (int kk = 0; kk < kVectorSize; ++kk) { - EXPECT_EQ(1, b32[kk]); - } for (int kk = 0; kk < kVectorSize; ++kk) { - bTmp8[kk] = (int8_t)kk; bTmp16[kk] = (int16_t)kk; bTmp32[kk] = (int32_t)kk; } - WEBRTC_SPL_MEMCPY_W8(b8, bTmp8, kVectorSize); - for (int kk = 0; kk < kVectorSize; ++kk) { - EXPECT_EQ(b8[kk], bTmp8[kk]); - } WEBRTC_SPL_MEMCPY_W16(b16, bTmp16, kVectorSize); for (int kk = 0; kk < kVectorSize; ++kk) { EXPECT_EQ(b16[kk], bTmp16[kk]); @@ -248,7 +188,7 @@ // for (int kk = 0; kk < kVectorSize; ++kk) { // EXPECT_EQ(b32[kk], bTmp32[kk]); // } - EXPECT_EQ(2, WebRtcSpl_CopyFromEndW16(b16, kVectorSize, 2, bTmp16)); + WebRtcSpl_CopyFromEndW16(b16, kVectorSize, 2, bTmp16); for (int kk = 0; kk < 2; ++kk) { EXPECT_EQ(kk+2, bTmp16[kk]); } @@ -497,7 +437,7 @@ int16_t b16[kVectorSize]; uint32_t bSeed = 100000; - EXPECT_EQ(464449057u, WebRtcSpl_IncreaseSeed(&bSeed)); + EXPECT_EQ(7086, WebRtcSpl_RandU(&bSeed)); EXPECT_EQ(31565, WebRtcSpl_RandU(&bSeed)); EXPECT_EQ(-9786, WebRtcSpl_RandN(&bSeed)); EXPECT_EQ(kVectorSize, WebRtcSpl_RandUArray(b16, kVectorSize, &bSeed)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/spl_init.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/spl_init.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/spl_init.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/spl_init.c 2015-02-03 14:33:34.000000000 +0000 @@ -65,8 +65,10 @@ WebRtcSpl_MinValueW32 = WebRtcSpl_MinValueW32Neon; WebRtcSpl_CrossCorrelation = WebRtcSpl_CrossCorrelationNeon; WebRtcSpl_DownsampleFast = WebRtcSpl_DownsampleFastNeon; + /* TODO(henrik.lundin): re-enable NEON when the crash from bug 3243 is + understood. */ WebRtcSpl_ScaleAndAddVectorsWithRound = - WebRtcSpl_ScaleAndAddVectorsWithRoundNeon; + WebRtcSpl_ScaleAndAddVectorsWithRoundC; WebRtcSpl_CreateRealFFT = WebRtcSpl_CreateRealFFTNeon; WebRtcSpl_FreeRealFFT = WebRtcSpl_FreeRealFFTNeon; WebRtcSpl_RealForwardFFT = WebRtcSpl_RealForwardFFTNeon; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/splitting_filter.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/splitting_filter.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/splitting_filter.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/splitting_filter.c 2015-02-03 14:33:34.000000000 +0000 @@ -45,7 +45,7 @@ // |data_length| // -void WebRtcSpl_AllPassQMF(int32_t* in_data, int16_t data_length, +void WebRtcSpl_AllPassQMF(int32_t* in_data, int data_length, int32_t* out_data, const uint16_t* filter_coefficients, int32_t* filter_state) { @@ -65,7 +65,7 @@ // filter operation takes the |in_data| (which is the output from the previous cascade // filter) and store the output in |out_data|. // Note that the input vector values are changed during the process. - int16_t k; + int k; int32_t diff; // First all-pass cascade; filter from in_data to out_data. @@ -74,14 +74,16 @@ // First loop, use the states stored in memory. // "diff" should be safe from wrap around since max values are 2^25 - diff = WEBRTC_SPL_SUB_SAT_W32(in_data[0], filter_state[1]); // = (x[0] - y_1[-1]) + // diff = (x[0] - y_1[-1]) + diff = WebRtcSpl_SubSatW32(in_data[0], filter_state[1]); // y_1[0] = x[-1] + a_1 * (x[0] - y_1[-1]) out_data[0] = WEBRTC_SPL_SCALEDIFF32(filter_coefficients[0], diff, filter_state[0]); // For the remaining loops, use previous values. for (k = 1; k < data_length; k++) { - diff = WEBRTC_SPL_SUB_SAT_W32(in_data[k], out_data[k - 1]); // = (x[n] - y_1[n-1]) + // diff = (x[n] - y_1[n-1]) + diff = WebRtcSpl_SubSatW32(in_data[k], out_data[k - 1]); // y_1[n] = x[n-1] + a_1 * (x[n] - y_1[n-1]) out_data[k] = WEBRTC_SPL_SCALEDIFF32(filter_coefficients[0], diff, in_data[k - 1]); } @@ -91,12 +93,14 @@ filter_state[1] = out_data[data_length - 1]; // y_1[N-1], becomes y_1[-1] next time // Second all-pass cascade; filter from out_data to in_data. - diff = WEBRTC_SPL_SUB_SAT_W32(out_data[0], filter_state[3]); // = (y_1[0] - y_2[-1]) + // diff = (y_1[0] - y_2[-1]) + diff = WebRtcSpl_SubSatW32(out_data[0], filter_state[3]); // y_2[0] = y_1[-1] + a_2 * (y_1[0] - y_2[-1]) in_data[0] = WEBRTC_SPL_SCALEDIFF32(filter_coefficients[1], diff, filter_state[2]); for (k = 1; k < data_length; k++) { - diff = WEBRTC_SPL_SUB_SAT_W32(out_data[k], in_data[k - 1]); // =(y_1[n] - y_2[n-1]) + // diff = (y_1[n] - y_2[n-1]) + diff = WebRtcSpl_SubSatW32(out_data[k], in_data[k - 1]); // y_2[0] = y_1[-1] + a_2 * (y_1[0] - y_2[-1]) in_data[k] = WEBRTC_SPL_SCALEDIFF32(filter_coefficients[1], diff, out_data[k-1]); } @@ -105,12 +109,14 @@ filter_state[3] = in_data[data_length - 1]; // y_2[N-1], becomes y_2[-1] next time // Third all-pass cascade; filter from in_data to out_data. - diff = WEBRTC_SPL_SUB_SAT_W32(in_data[0], filter_state[5]); // = (y_2[0] - y[-1]) + // diff = (y_2[0] - y[-1]) + diff = WebRtcSpl_SubSatW32(in_data[0], filter_state[5]); // y[0] = y_2[-1] + a_3 * (y_2[0] - y[-1]) out_data[0] = WEBRTC_SPL_SCALEDIFF32(filter_coefficients[2], diff, filter_state[4]); for (k = 1; k < data_length; k++) { - diff = WEBRTC_SPL_SUB_SAT_W32(in_data[k], out_data[k - 1]); // = (y_2[n] - y[n-1]) + // diff = (y_2[n] - y[n-1]) + diff = WebRtcSpl_SubSatW32(in_data[k], out_data[k - 1]); // y[n] = y_2[n-1] + a_3 * (y_2[n] - y[n-1]) out_data[k] = WEBRTC_SPL_SCALEDIFF32(filter_coefficients[2], diff, in_data[k-1]); } @@ -150,12 +156,10 @@ // branches to get upper & lower band. for (i = 0; i < band_length; i++) { - tmp = filter1[i] + filter2[i] + 1024; - tmp = WEBRTC_SPL_RSHIFT_W32(tmp, 11); + tmp = (filter1[i] + filter2[i] + 1024) >> 11; low_band[i] = WebRtcSpl_SatW32ToW16(tmp); - tmp = filter1[i] - filter2[i] + 1024; - tmp = WEBRTC_SPL_RSHIFT_W32(tmp, 11); + tmp = (filter1[i] - filter2[i] + 1024) >> 11; high_band[i] = WebRtcSpl_SatW32ToW16(tmp); } } @@ -194,10 +198,10 @@ // saturation. for (i = 0, k = 0; i < band_length; i++) { - tmp = WEBRTC_SPL_RSHIFT_W32(filter2[i] + 512, 10); + tmp = (filter2[i] + 512) >> 10; out_data[k++] = WebRtcSpl_SatW32ToW16(tmp); - tmp = WEBRTC_SPL_RSHIFT_W32(filter1[i] + 512, 10); + tmp = (filter1[i] + 512) >> 10; out_data[k++] = WebRtcSpl_SatW32ToW16(tmp); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/spl_sqrt.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/spl_sqrt.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/spl_sqrt.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/spl_sqrt.c 2015-02-03 14:33:34.000000000 +0000 @@ -17,6 +17,8 @@ #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include + int32_t WebRtcSpl_SqrtLocal(int32_t in); int32_t WebRtcSpl_SqrtLocal(int32_t in) @@ -33,11 +35,10 @@ + 0.875*((x_half)^5) */ - B = in; + B = in / 2; - B = WEBRTC_SPL_RSHIFT_W32(B, 1); // B = in/2 B = B - ((int32_t)0x40000000); // B = in/2 - 1/2 - x_half = (int16_t)WEBRTC_SPL_RSHIFT_W32(B, 16);// x_half = x/2 = (in-1)/2 + x_half = (int16_t)(B >> 16); // x_half = x/2 = (in-1)/2 B = B + ((int32_t)0x40000000); // B = 1 + x/2 B = B + ((int32_t)0x40000000); // Add 0.5 twice (since 1.0 does not exist in Q31) @@ -45,19 +46,18 @@ A = -x2; // A = -(x/2)^2 B = B + (A >> 1); // B = 1 + x/2 - 0.5*(x/2)^2 - A = WEBRTC_SPL_RSHIFT_W32(A, 16); + A >>= 16; A = A * A * 2; // A = (x/2)^4 - t16 = (int16_t)WEBRTC_SPL_RSHIFT_W32(A, 16); + t16 = (int16_t)(A >> 16); B = B + WEBRTC_SPL_MUL_16_16(-20480, t16) * 2; // B = B - 0.625*A // After this, B = 1 + x/2 - 0.5*(x/2)^2 - 0.625*(x/2)^4 - t16 = (int16_t)WEBRTC_SPL_RSHIFT_W32(A, 16); A = WEBRTC_SPL_MUL_16_16(x_half, t16) * 2; // A = (x/2)^5 - t16 = (int16_t)WEBRTC_SPL_RSHIFT_W32(A, 16); + t16 = (int16_t)(A >> 16); B = B + WEBRTC_SPL_MUL_16_16(28672, t16) * 2; // B = B + 0.875*A // After this, B = 1 + x/2 - 0.5*(x/2)^2 - 0.625*(x/2)^4 + 0.875*(x/2)^5 - t16 = (int16_t)WEBRTC_SPL_RSHIFT_W32(x2, 16); + t16 = (int16_t)(x2 >> 16); A = WEBRTC_SPL_MUL_16_16(x_half, t16) * 2; // A = x/2^3 B = B + (A >> 1); // B = B + 0.5*A @@ -152,33 +152,33 @@ A = WEBRTC_SPL_WORD32_MAX; } - x_norm = (int16_t)WEBRTC_SPL_RSHIFT_W32(A, 16); // x_norm = AH + x_norm = (int16_t)(A >> 16); // x_norm = AH - nshift = WEBRTC_SPL_RSHIFT_W16(sh, 1); // nshift = sh>>1 - nshift = -nshift; // Negate the power for later de-normalization + nshift = (sh / 2); + assert(nshift >= 0); A = (int32_t)WEBRTC_SPL_LSHIFT_W32((int32_t)x_norm, 16); A = WEBRTC_SPL_ABS_W32(A); // A = abs(x_norm<<16) A = WebRtcSpl_SqrtLocal(A); // A = sqrt(A) - if ((-2 * nshift) == sh) - { // Even shift value case + if (2 * nshift == sh) { + // Even shift value case - t16 = (int16_t)WEBRTC_SPL_RSHIFT_W32(A, 16); // t16 = AH + t16 = (int16_t)(A >> 16); // t16 = AH A = WEBRTC_SPL_MUL_16_16(k_sqrt_2, t16) * 2; // A = 1/sqrt(2)*t16 A = A + ((int32_t)32768); // Round off A = A & ((int32_t)0x7fff0000); // Round off - A = WEBRTC_SPL_RSHIFT_W32(A, 15); // A = A>>16 + A >>= 15; // A = A>>16 } else { - A = WEBRTC_SPL_RSHIFT_W32(A, 16); // A = A>>16 + A >>= 16; // A = A>>16 } A = A & ((int32_t)0x0000ffff); - A = (int32_t)WEBRTC_SPL_SHIFT_W32(A, nshift); // De-normalize the result + A >>= nshift; // De-normalize the result. return A; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/spl_version.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/spl_version.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/spl_version.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/spl_version.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - - -/* - * This file contains the function WebRtcSpl_get_version(). - * The description header can be found in signal_processing_library.h - * - */ - -#include -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" - -int16_t WebRtcSpl_get_version(char* version, int16_t length_in_bytes) -{ - strncpy(version, "1.2.0", length_in_bytes); - return 0; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/vector_scaling_operations.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/vector_scaling_operations.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/vector_scaling_operations.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/signal_processing/vector_scaling_operations.c 2015-02-03 14:33:34.000000000 +0000 @@ -75,7 +75,7 @@ (*out++) = WebRtcSpl_SatW32ToW16(tmp_w32); } } else { - int16_t left_shifts = -right_shifts; + int left_shifts = -right_shifts; for (i = length; i > 0; i--) { tmp_w32 = (*in++) << left_shifts; (*out++) = WebRtcSpl_SatW32ToW16(tmp_w32); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_vad -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := \ - webrtc_vad.c \ - vad_core.c \ - vad_filterbank.c \ - vad_gmm.c \ - vad_sp.c - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../.. \ - $(LOCAL_PATH)/../signal_processing/include \ - external/webrtc - -LOCAL_SHARED_LIBRARIES := \ - libdl \ - libstlport - -ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) -LOCAL_LDLIBS += -ldl -lpthread -endif - -ifneq ($(TARGET_SIMULATOR),true) -LOCAL_SHARED_LIBRARIES += libdl -endif - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/include/vad.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/include/vad.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/include/vad.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/include/vad.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_COMMON_AUDIO_VAD_INCLUDE_VAD_H_ +#define WEBRTC_COMMON_AUDIO_VAD_INCLUDE_VAD_H_ + +#include "webrtc/base/checks.h" +#include "webrtc/common_audio/vad/include/webrtc_vad.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// This is a C++ wrapper class for WebRtcVad. +class Vad { + public: + enum Aggressiveness { + kVadNormal = 0, + kVadLowBitrate = 1, + kVadAggressive = 2, + kVadVeryAggressive = 3 + }; + + enum Activity { kPassive = 0, kActive = 1, kError = -1 }; + + explicit Vad(enum Aggressiveness mode); + + virtual ~Vad(); + + enum Activity VoiceActivity(const int16_t* audio, + size_t num_samples, + int sample_rate_hz); + + private: + VadInst* handle_; +}; + +} // namespace webrtc +#endif // WEBRTC_COMMON_AUDIO_VAD_INCLUDE_VAD_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/include/webrtc_vad.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/include/webrtc_vad.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/include/webrtc_vad.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/include/webrtc_vad.h 2015-02-03 14:33:34.000000000 +0000 @@ -34,9 +34,7 @@ // Frees the dynamic memory of a specified VAD instance. // // - handle [i] : Pointer to VAD instance that should be freed. -// -// returns : 0 - (OK), -1 - (NULL pointer in) -int WebRtcVad_Free(VadInst* handle); +void WebRtcVad_Free(VadInst* handle); // Initializes a VAD instance. // @@ -71,7 +69,7 @@ // returns : 1 - (Active Voice), // 0 - (Non-active Voice), // -1 - (Error) -int WebRtcVad_Process(VadInst* handle, int fs, int16_t* audio_frame, +int WebRtcVad_Process(VadInst* handle, int fs, const int16_t* audio_frame, int frame_length); // Checks for valid combinations of |rate| and |frame_length|. We support 10, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/mock/mock_vad.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/mock/mock_vad.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/mock/mock_vad.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/mock/mock_vad.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_COMMON_AUDIO_VAD_MOCK_MOCK_VAD_H_ +#define WEBRTC_COMMON_AUDIO_VAD_MOCK_MOCK_VAD_H_ + +#include "webrtc/common_audio/vad/include/vad.h" + +#include "testing/gmock/include/gmock/gmock.h" + +namespace webrtc { + +class MockVad : public Vad { + public: + explicit MockVad(enum Aggressiveness mode) {} + virtual ~MockVad() { Die(); } + MOCK_METHOD0(Die, void()); + + MOCK_METHOD3(VoiceActivity, + enum Activity(const int16_t* audio, + size_t num_samples, + int sample_rate_hz)); +}; + +} // namespace webrtc + +#endif // WEBRTC_COMMON_AUDIO_VAD_MOCK_MOCK_VAD_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/common_audio/vad/include/vad.h" + +#include "webrtc/base/checks.h" + +namespace webrtc { + +Vad::Vad(enum Aggressiveness mode) { + CHECK_EQ(WebRtcVad_Create(&handle_), 0); + CHECK_EQ(WebRtcVad_Init(handle_), 0); + CHECK_EQ(WebRtcVad_set_mode(handle_, mode), 0); +} + +Vad::~Vad() { + WebRtcVad_Free(handle_); +} + +enum Vad::Activity Vad::VoiceActivity(const int16_t* audio, + size_t num_samples, + int sample_rate_hz) { + int ret = WebRtcVad_Process( + handle_, sample_rate_hz, audio, static_cast(num_samples)); + switch (ret) { + case 0: + return kPassive; + case 1: + return kActive; + default: + DCHECK(false) << "WebRtcVad_Process returned an error."; + return kError; + } +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_core.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_core.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_core.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_core.c 2015-02-03 14:33:34.000000000 +0000 @@ -603,7 +603,7 @@ // Calculate VAD decision by first extracting feature values and then calculate // probability for both speech and background noise. -int WebRtcVad_CalcVad48khz(VadInstT* inst, int16_t* speech_frame, +int WebRtcVad_CalcVad48khz(VadInstT* inst, const int16_t* speech_frame, int frame_length) { int vad; int i; @@ -628,7 +628,7 @@ return vad; } -int WebRtcVad_CalcVad32khz(VadInstT* inst, int16_t* speech_frame, +int WebRtcVad_CalcVad32khz(VadInstT* inst, const int16_t* speech_frame, int frame_length) { int len, vad; @@ -639,10 +639,10 @@ // Downsample signal 32->16->8 before doing VAD WebRtcVad_Downsampling(speech_frame, speechWB, &(inst->downsampling_filter_states[2]), frame_length); - len = WEBRTC_SPL_RSHIFT_W16(frame_length, 1); + len = frame_length / 2; WebRtcVad_Downsampling(speechWB, speechNB, inst->downsampling_filter_states, len); - len = WEBRTC_SPL_RSHIFT_W16(len, 1); + len /= 2; // Do VAD on an 8 kHz signal vad = WebRtcVad_CalcVad8khz(inst, speechNB, len); @@ -650,7 +650,7 @@ return vad; } -int WebRtcVad_CalcVad16khz(VadInstT* inst, int16_t* speech_frame, +int WebRtcVad_CalcVad16khz(VadInstT* inst, const int16_t* speech_frame, int frame_length) { int len, vad; @@ -660,13 +660,13 @@ WebRtcVad_Downsampling(speech_frame, speechNB, inst->downsampling_filter_states, frame_length); - len = WEBRTC_SPL_RSHIFT_W16(frame_length, 1); + len = frame_length / 2; vad = WebRtcVad_CalcVad8khz(inst, speechNB, len); return vad; } -int WebRtcVad_CalcVad8khz(VadInstT* inst, int16_t* speech_frame, +int WebRtcVad_CalcVad8khz(VadInstT* inst, const int16_t* speech_frame, int frame_length) { int16_t feature_vector[kNumChannels], total_power; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_core.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_core.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_core.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_core.h 2015-02-03 14:33:34.000000000 +0000 @@ -85,9 +85,9 @@ /**************************************************************************** * WebRtcVad_CalcVad48khz(...) - * WebRtcVad_CalcVad32khz(...) - * WebRtcVad_CalcVad16khz(...) - * WebRtcVad_CalcVad8khz(...) + * WebRtcVad_CalcVad32khz(...) + * WebRtcVad_CalcVad16khz(...) + * WebRtcVad_CalcVad8khz(...) * * Calculate probability for active speech and make VAD decision. * @@ -103,13 +103,13 @@ * 0 - No active speech * 1-6 - Active speech */ -int WebRtcVad_CalcVad48khz(VadInstT* inst, int16_t* speech_frame, +int WebRtcVad_CalcVad48khz(VadInstT* inst, const int16_t* speech_frame, int frame_length); -int WebRtcVad_CalcVad32khz(VadInstT* inst, int16_t* speech_frame, +int WebRtcVad_CalcVad32khz(VadInstT* inst, const int16_t* speech_frame, int frame_length); -int WebRtcVad_CalcVad16khz(VadInstT* inst, int16_t* speech_frame, +int WebRtcVad_CalcVad16khz(VadInstT* inst, const int16_t* speech_frame, int frame_length); -int WebRtcVad_CalcVad8khz(VadInstT* inst, int16_t* speech_frame, +int WebRtcVad_CalcVad8khz(VadInstT* inst, const int16_t* speech_frame, int frame_length); #endif // WEBRTC_COMMON_AUDIO_VAD_VAD_CORE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_sp.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_sp.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_sp.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_sp.c 2015-02-03 14:33:34.000000000 +0000 @@ -24,7 +24,7 @@ // TODO(bjornv): Move this function to vad_filterbank.c. // Downsampling filter based on splitting filter and allpass functions. -void WebRtcVad_Downsampling(int16_t* signal_in, +void WebRtcVad_Downsampling(const int16_t* signal_in, int16_t* signal_out, int32_t* filter_state, int in_length) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_sp.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_sp.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_sp.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_sp.h 2015-02-03 14:33:34.000000000 +0000 @@ -30,7 +30,7 @@ // // Output: // - signal_out : Downsampled signal (of length |in_length| / 2). -void WebRtcVad_Downsampling(int16_t* signal_in, +void WebRtcVad_Downsampling(const int16_t* signal_in, int16_t* signal_out, int32_t* filter_state, int in_length); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/vad_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -70,7 +70,6 @@ // NULL instance tests EXPECT_EQ(-1, WebRtcVad_Create(NULL)); EXPECT_EQ(-1, WebRtcVad_Init(NULL)); - EXPECT_EQ(-1, WebRtcVad_Free(NULL)); EXPECT_EQ(-1, WebRtcVad_set_mode(NULL, kModes[0])); EXPECT_EQ(-1, WebRtcVad_Process(NULL, kRates[0], speech, kFrameLengths[0])); @@ -121,7 +120,7 @@ } } - EXPECT_EQ(0, WebRtcVad_Free(handle)); + WebRtcVad_Free(handle); } TEST_F(VadTest, ValidRatesFrameLengths) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/webrtc_vad.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/webrtc_vad.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/webrtc_vad.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/vad/webrtc_vad.c 2015-02-03 14:33:34.000000000 +0000 @@ -44,14 +44,8 @@ return 0; } -int WebRtcVad_Free(VadInst* handle) { - if (handle == NULL) { - return -1; - } - +void WebRtcVad_Free(VadInst* handle) { free(handle); - - return 0; } // TODO(bjornv): Move WebRtcVad_InitCore() code here. @@ -74,7 +68,7 @@ return WebRtcVad_set_mode_core(self, mode); } -int WebRtcVad_Process(VadInst* handle, int fs, int16_t* audio_frame, +int WebRtcVad_Process(VadInst* handle, int fs, const int16_t* audio_frame, int frame_length) { int vad = -1; VadInstT* self = (VadInstT*) handle; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_file.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_file.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_file.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_file.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/common_audio/wav_file.h" + +#include +#include +#include + +#include "webrtc/base/checks.h" +#include "webrtc/common_audio/include/audio_util.h" +#include "webrtc/common_audio/wav_header.h" + +namespace webrtc { + +// We write 16-bit PCM WAV files. +static const WavFormat kWavFormat = kWavFormatPcm; +static const int kBytesPerSample = 2; + +WavReader::WavReader(const std::string& filename) + : file_handle_(fopen(filename.c_str(), "rb")) { + CHECK(file_handle_); + uint8_t header[kWavHeaderSize]; + const size_t read = + fread(header, sizeof(*header), kWavHeaderSize, file_handle_); + CHECK_EQ(kWavHeaderSize, read); + + WavFormat format; + int bytes_per_sample; + CHECK(ReadWavHeader(header, &num_channels_, &sample_rate_, &format, + &bytes_per_sample, &num_samples_)); + CHECK_EQ(kWavFormat, format); + CHECK_EQ(kBytesPerSample, bytes_per_sample); +} + +WavReader::~WavReader() { + Close(); +} + +size_t WavReader::ReadSamples(size_t num_samples, int16_t* samples) { +#ifndef WEBRTC_ARCH_LITTLE_ENDIAN +#error "Need to convert samples to big-endian when reading from WAV file" +#endif + const size_t read = + fread(samples, sizeof(*samples), num_samples, file_handle_); + // If we didn't read what was requested, ensure we've reached the EOF. + CHECK(read == num_samples || feof(file_handle_)); + return read; +} + +size_t WavReader::ReadSamples(size_t num_samples, float* samples) { + static const size_t kChunksize = 4096 / sizeof(uint16_t); + size_t read = 0; + for (size_t i = 0; i < num_samples; i += kChunksize) { + int16_t isamples[kChunksize]; + size_t chunk = std::min(kChunksize, num_samples - i); + chunk = ReadSamples(chunk, isamples); + for (size_t j = 0; j < chunk; ++j) + samples[i + j] = isamples[j]; + read += chunk; + } + return read; +} + +void WavReader::Close() { + CHECK_EQ(0, fclose(file_handle_)); + file_handle_ = NULL; +} + +WavWriter::WavWriter(const std::string& filename, int sample_rate, + int num_channels) + : sample_rate_(sample_rate), + num_channels_(num_channels), + num_samples_(0), + file_handle_(fopen(filename.c_str(), "wb")) { + CHECK(file_handle_); + CHECK(CheckWavParameters(num_channels_, + sample_rate_, + kWavFormat, + kBytesPerSample, + num_samples_)); + + // Write a blank placeholder header, since we need to know the total number + // of samples before we can fill in the real data. + static const uint8_t blank_header[kWavHeaderSize] = {0}; + CHECK_EQ(1u, fwrite(blank_header, kWavHeaderSize, 1, file_handle_)); +} + +WavWriter::~WavWriter() { + Close(); +} + +void WavWriter::WriteSamples(const int16_t* samples, size_t num_samples) { +#ifndef WEBRTC_ARCH_LITTLE_ENDIAN +#error "Need to convert samples to little-endian when writing to WAV file" +#endif + const size_t written = + fwrite(samples, sizeof(*samples), num_samples, file_handle_); + CHECK_EQ(num_samples, written); + num_samples_ += static_cast(written); + CHECK(written <= std::numeric_limits::max() || + num_samples_ >= written); // detect uint32_t overflow + CHECK(CheckWavParameters(num_channels_, + sample_rate_, + kWavFormat, + kBytesPerSample, + num_samples_)); +} + +void WavWriter::WriteSamples(const float* samples, size_t num_samples) { + static const size_t kChunksize = 4096 / sizeof(uint16_t); + for (size_t i = 0; i < num_samples; i += kChunksize) { + int16_t isamples[kChunksize]; + const size_t chunk = std::min(kChunksize, num_samples - i); + FloatS16ToS16(samples + i, chunk, isamples); + WriteSamples(isamples, chunk); + } +} + +void WavWriter::Close() { + CHECK_EQ(0, fseek(file_handle_, 0, SEEK_SET)); + uint8_t header[kWavHeaderSize]; + WriteWavHeader(header, num_channels_, sample_rate_, kWavFormat, + kBytesPerSample, num_samples_); + CHECK_EQ(1u, fwrite(header, kWavHeaderSize, 1, file_handle_)); + CHECK_EQ(0, fclose(file_handle_)); + file_handle_ = NULL; +} + +} // namespace webrtc + +rtc_WavWriter* rtc_WavOpen(const char* filename, + int sample_rate, + int num_channels) { + return reinterpret_cast( + new webrtc::WavWriter(filename, sample_rate, num_channels)); +} + +void rtc_WavClose(rtc_WavWriter* wf) { + delete reinterpret_cast(wf); +} + +void rtc_WavWriteSamples(rtc_WavWriter* wf, + const float* samples, + size_t num_samples) { + reinterpret_cast(wf)->WriteSamples(samples, num_samples); +} + +int rtc_WavSampleRate(const rtc_WavWriter* wf) { + return reinterpret_cast(wf)->sample_rate(); +} + +int rtc_WavNumChannels(const rtc_WavWriter* wf) { + return reinterpret_cast(wf)->num_channels(); +} + +uint32_t rtc_WavNumSamples(const rtc_WavWriter* wf) { + return reinterpret_cast(wf)->num_samples(); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_file.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_file.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_file.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_file.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_COMMON_AUDIO_WAV_FILE_H_ +#define WEBRTC_COMMON_AUDIO_WAV_FILE_H_ + +#ifdef __cplusplus + +#include +#include +#include + +namespace webrtc { + +// Simple C++ class for writing 16-bit PCM WAV files. All error handling is +// by calls to CHECK(), making it unsuitable for anything but debug code. +class WavWriter { + public: + // Open a new WAV file for writing. + WavWriter(const std::string& filename, int sample_rate, int num_channels); + + // Close the WAV file, after writing its header. + ~WavWriter(); + + // Write additional samples to the file. Each sample is in the range + // [-32768,32767], and there must be the previously specified number of + // interleaved channels. + void WriteSamples(const float* samples, size_t num_samples); + void WriteSamples(const int16_t* samples, size_t num_samples); + + int sample_rate() const { return sample_rate_; } + int num_channels() const { return num_channels_; } + uint32_t num_samples() const { return num_samples_; } + + private: + void Close(); + const int sample_rate_; + const int num_channels_; + uint32_t num_samples_; // Total number of samples written to file. + FILE* file_handle_; // Output file, owned by this class +}; + +// Follows the conventions of WavWriter. +class WavReader { + public: + // Opens an existing WAV file for reading. + explicit WavReader(const std::string& filename); + + // Close the WAV file. + ~WavReader(); + + // Returns the number of samples read. If this is less than requested, + // verifies that the end of the file was reached. + size_t ReadSamples(size_t num_samples, float* samples); + size_t ReadSamples(size_t num_samples, int16_t* samples); + + int sample_rate() const { return sample_rate_; } + int num_channels() const { return num_channels_; } + uint32_t num_samples() const { return num_samples_; } + + private: + void Close(); + int sample_rate_; + int num_channels_; + uint32_t num_samples_; // Total number of samples in the file. + FILE* file_handle_; // Input file, owned by this class. +}; + +} // namespace webrtc + +extern "C" { +#endif // __cplusplus + +// C wrappers for the WavWriter class. +typedef struct rtc_WavWriter rtc_WavWriter; +rtc_WavWriter* rtc_WavOpen(const char* filename, + int sample_rate, + int num_channels); +void rtc_WavClose(rtc_WavWriter* wf); +void rtc_WavWriteSamples(rtc_WavWriter* wf, + const float* samples, + size_t num_samples); +int rtc_WavSampleRate(const rtc_WavWriter* wf); +int rtc_WavNumChannels(const rtc_WavWriter* wf); +uint32_t rtc_WavNumSamples(const rtc_WavWriter* wf); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBRTC_COMMON_AUDIO_WAV_FILE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_file_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_file_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_file_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_file_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// MSVC++ requires this to be set before any other includes to get M_PI. +#define _USE_MATH_DEFINES + +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/compile_assert.h" +#include "webrtc/common_audio/wav_header.h" +#include "webrtc/common_audio/wav_file.h" +#include "webrtc/test/testsupport/fileutils.h" + +static const float kSamples[] = {0.0, 10.0, 4e4, -1e9}; + +// Write a tiny WAV file with the C++ interface and verify the result. +TEST(WavWriterTest, CPP) { + const std::string outfile = webrtc::test::OutputPath() + "wavtest1.wav"; + static const uint32_t kNumSamples = 3; + { + webrtc::WavWriter w(outfile, 14099, 1); + EXPECT_EQ(14099, w.sample_rate()); + EXPECT_EQ(1, w.num_channels()); + EXPECT_EQ(0u, w.num_samples()); + w.WriteSamples(kSamples, kNumSamples); + EXPECT_EQ(kNumSamples, w.num_samples()); + } + static const uint8_t kExpectedContents[] = { + 'R', 'I', 'F', 'F', + 42, 0, 0, 0, // size of whole file - 8: 6 + 44 - 8 + 'W', 'A', 'V', 'E', + 'f', 'm', 't', ' ', + 16, 0, 0, 0, // size of fmt block - 8: 24 - 8 + 1, 0, // format: PCM (1) + 1, 0, // channels: 1 + 0x13, 0x37, 0, 0, // sample rate: 14099 + 0x26, 0x6e, 0, 0, // byte rate: 2 * 14099 + 2, 0, // block align: NumChannels * BytesPerSample + 16, 0, // bits per sample: 2 * 8 + 'd', 'a', 't', 'a', + 6, 0, 0, 0, // size of payload: 6 + 0, 0, // first sample: 0.0 + 10, 0, // second sample: 10.0 + 0xff, 0x7f, // third sample: 4e4 (saturated) + }; + static const int kContentSize = + webrtc::kWavHeaderSize + kNumSamples * sizeof(int16_t); + COMPILE_ASSERT(sizeof(kExpectedContents) == kContentSize, content_size); + EXPECT_EQ(size_t(kContentSize), webrtc::test::GetFileSize(outfile)); + FILE* f = fopen(outfile.c_str(), "rb"); + ASSERT_TRUE(f); + uint8_t contents[kContentSize]; + ASSERT_EQ(1u, fread(contents, kContentSize, 1, f)); + EXPECT_EQ(0, fclose(f)); + EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize)); + + { + webrtc::WavReader r(outfile); + EXPECT_EQ(14099, r.sample_rate()); + EXPECT_EQ(1, r.num_channels()); + EXPECT_EQ(kNumSamples, r.num_samples()); + static const float kTruncatedSamples[] = {0.0, 10.0, 32767.0}; + float samples[kNumSamples]; + EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples)); + EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples))); + EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples)); + } +} + +// Write a tiny WAV file with the C interface and verify the result. +TEST(WavWriterTest, C) { + const std::string outfile = webrtc::test::OutputPath() + "wavtest2.wav"; + rtc_WavWriter *w = rtc_WavOpen(outfile.c_str(), 11904, 2); + EXPECT_EQ(11904, rtc_WavSampleRate(w)); + EXPECT_EQ(2, rtc_WavNumChannels(w)); + EXPECT_EQ(0u, rtc_WavNumSamples(w)); + static const uint32_t kNumSamples = 4; + rtc_WavWriteSamples(w, &kSamples[0], 2); + EXPECT_EQ(2u, rtc_WavNumSamples(w)); + rtc_WavWriteSamples(w, &kSamples[2], kNumSamples - 2); + EXPECT_EQ(kNumSamples, rtc_WavNumSamples(w)); + rtc_WavClose(w); + static const uint8_t kExpectedContents[] = { + 'R', 'I', 'F', 'F', + 44, 0, 0, 0, // size of whole file - 8: 8 + 44 - 8 + 'W', 'A', 'V', 'E', + 'f', 'm', 't', ' ', + 16, 0, 0, 0, // size of fmt block - 8: 24 - 8 + 1, 0, // format: PCM (1) + 2, 0, // channels: 2 + 0x80, 0x2e, 0, 0, // sample rate: 11904 + 0, 0xba, 0, 0, // byte rate: 2 * 2 * 11904 + 4, 0, // block align: NumChannels * BytesPerSample + 16, 0, // bits per sample: 2 * 8 + 'd', 'a', 't', 'a', + 8, 0, 0, 0, // size of payload: 8 + 0, 0, // first sample: 0.0 + 10, 0, // second sample: 10.0 + 0xff, 0x7f, // third sample: 4e4 (saturated) + 0, 0x80, // fourth sample: -1e9 (saturated) + }; + static const int kContentSize = + webrtc::kWavHeaderSize + kNumSamples * sizeof(int16_t); + COMPILE_ASSERT(sizeof(kExpectedContents) == kContentSize, content_size); + EXPECT_EQ(size_t(kContentSize), webrtc::test::GetFileSize(outfile)); + FILE* f = fopen(outfile.c_str(), "rb"); + ASSERT_TRUE(f); + uint8_t contents[kContentSize]; + ASSERT_EQ(1u, fread(contents, kContentSize, 1, f)); + EXPECT_EQ(0, fclose(f)); + EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize)); +} + +// Write a larger WAV file. You can listen to this file to sanity-check it. +TEST(WavWriterTest, LargeFile) { + std::string outfile = webrtc::test::OutputPath() + "wavtest3.wav"; + static const int kSampleRate = 8000; + static const int kNumChannels = 2; + static const uint32_t kNumSamples = 3 * kSampleRate * kNumChannels; + float samples[kNumSamples]; + for (uint32_t i = 0; i < kNumSamples; i += kNumChannels) { + // A nice periodic beeping sound. + static const double kToneHz = 440; + const double t = static_cast(i) / (kNumChannels * kSampleRate); + const double x = + std::numeric_limits::max() * std::sin(t * kToneHz * 2 * M_PI); + samples[i] = std::pow(std::sin(t * 2 * 2 * M_PI), 10) * x; + samples[i + 1] = std::pow(std::cos(t * 2 * 2 * M_PI), 10) * x; + } + { + webrtc::WavWriter w(outfile, kSampleRate, kNumChannels); + EXPECT_EQ(kSampleRate, w.sample_rate()); + EXPECT_EQ(kNumChannels, w.num_channels()); + EXPECT_EQ(0u, w.num_samples()); + w.WriteSamples(samples, kNumSamples); + EXPECT_EQ(kNumSamples, w.num_samples()); + } + EXPECT_EQ(sizeof(int16_t) * kNumSamples + webrtc::kWavHeaderSize, + webrtc::test::GetFileSize(outfile)); + + { + webrtc::WavReader r(outfile); + EXPECT_EQ(kSampleRate, r.sample_rate()); + EXPECT_EQ(kNumChannels, r.num_channels()); + EXPECT_EQ(kNumSamples, r.num_samples()); + + float read_samples[kNumSamples]; + EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, read_samples)); + for (size_t i = 0; i < kNumSamples; ++i) + EXPECT_NEAR(samples[i], read_samples[i], 1); + + EXPECT_EQ(0u, r.ReadSamples(kNumSamples, read_samples)); + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_header.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_header.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_header.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_header.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Based on the WAV file format documentation at +// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ and +// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html + +#include "webrtc/common_audio/wav_header.h" + +#include +#include +#include + +#include "webrtc/base/checks.h" +#include "webrtc/common_audio/include/audio_util.h" + +namespace webrtc { +namespace { + +struct ChunkHeader { + uint32_t ID; + uint32_t Size; +}; +COMPILE_ASSERT(sizeof(ChunkHeader) == 8, chunk_header_size); + +// We can't nest this definition in WavHeader, because VS2013 gives an error +// on sizeof(WavHeader::fmt): "error C2070: 'unknown': illegal sizeof operand". +struct FmtSubchunk { + ChunkHeader header; + uint16_t AudioFormat; + uint16_t NumChannels; + uint32_t SampleRate; + uint32_t ByteRate; + uint16_t BlockAlign; + uint16_t BitsPerSample; +}; +COMPILE_ASSERT(sizeof(FmtSubchunk) == 24, fmt_subchunk_size); +const uint32_t kFmtSubchunkSize = sizeof(FmtSubchunk) - sizeof(ChunkHeader); + +struct WavHeader { + struct { + ChunkHeader header; + uint32_t Format; + } riff; + FmtSubchunk fmt; + struct { + ChunkHeader header; + } data; +}; +COMPILE_ASSERT(sizeof(WavHeader) == kWavHeaderSize, no_padding_in_header); + +} // namespace + +bool CheckWavParameters(int num_channels, + int sample_rate, + WavFormat format, + int bytes_per_sample, + uint32_t num_samples) { + // num_channels, sample_rate, and bytes_per_sample must be positive, must fit + // in their respective fields, and their product must fit in the 32-bit + // ByteRate field. + if (num_channels <= 0 || sample_rate <= 0 || bytes_per_sample <= 0) + return false; + if (static_cast(sample_rate) > std::numeric_limits::max()) + return false; + if (static_cast(num_channels) > + std::numeric_limits::max()) + return false; + if (static_cast(bytes_per_sample) * 8 > + std::numeric_limits::max()) + return false; + if (static_cast(sample_rate) * num_channels * bytes_per_sample > + std::numeric_limits::max()) + return false; + + // format and bytes_per_sample must agree. + switch (format) { + case kWavFormatPcm: + // Other values may be OK, but for now we're conservative: + if (bytes_per_sample != 1 && bytes_per_sample != 2) + return false; + break; + case kWavFormatALaw: + case kWavFormatMuLaw: + if (bytes_per_sample != 1) + return false; + break; + default: + return false; + } + + // The number of bytes in the file, not counting the first ChunkHeader, must + // be less than 2^32; otherwise, the ChunkSize field overflows. + const uint32_t max_samples = + (std::numeric_limits::max() + - (kWavHeaderSize - sizeof(ChunkHeader))) / + bytes_per_sample; + if (num_samples > max_samples) + return false; + + // Each channel must have the same number of samples. + if (num_samples % num_channels != 0) + return false; + + return true; +} + +#ifdef WEBRTC_ARCH_LITTLE_ENDIAN +static inline void WriteLE16(uint16_t* f, uint16_t x) { *f = x; } +static inline void WriteLE32(uint32_t* f, uint32_t x) { *f = x; } +static inline void WriteFourCC(uint32_t* f, char a, char b, char c, char d) { + *f = static_cast(a) + | static_cast(b) << 8 + | static_cast(c) << 16 + | static_cast(d) << 24; +} + +static inline uint16_t ReadLE16(uint16_t x) { return x; } +static inline uint32_t ReadLE32(uint32_t x) { return x; } +static inline std::string ReadFourCC(uint32_t x) { + return std::string(reinterpret_cast(&x), 4); +} +#else +#error "Write be-to-le conversion functions" +#endif + +static inline uint32_t RiffChunkSize(uint32_t bytes_in_payload) { + return bytes_in_payload + kWavHeaderSize - sizeof(ChunkHeader); +} + +static inline uint32_t ByteRate(int num_channels, int sample_rate, + int bytes_per_sample) { + return static_cast(num_channels) * sample_rate * bytes_per_sample; +} + +static inline uint16_t BlockAlign(int num_channels, int bytes_per_sample) { + return num_channels * bytes_per_sample; +} + +void WriteWavHeader(uint8_t* buf, + int num_channels, + int sample_rate, + WavFormat format, + int bytes_per_sample, + uint32_t num_samples) { + CHECK(CheckWavParameters(num_channels, sample_rate, format, + bytes_per_sample, num_samples)); + + WavHeader header; + const uint32_t bytes_in_payload = bytes_per_sample * num_samples; + + WriteFourCC(&header.riff.header.ID, 'R', 'I', 'F', 'F'); + WriteLE32(&header.riff.header.Size, RiffChunkSize(bytes_in_payload)); + WriteFourCC(&header.riff.Format, 'W', 'A', 'V', 'E'); + + WriteFourCC(&header.fmt.header.ID, 'f', 'm', 't', ' '); + WriteLE32(&header.fmt.header.Size, kFmtSubchunkSize); + WriteLE16(&header.fmt.AudioFormat, format); + WriteLE16(&header.fmt.NumChannels, num_channels); + WriteLE32(&header.fmt.SampleRate, sample_rate); + WriteLE32(&header.fmt.ByteRate, ByteRate(num_channels, sample_rate, + bytes_per_sample)); + WriteLE16(&header.fmt.BlockAlign, BlockAlign(num_channels, bytes_per_sample)); + WriteLE16(&header.fmt.BitsPerSample, 8 * bytes_per_sample); + + WriteFourCC(&header.data.header.ID, 'd', 'a', 't', 'a'); + WriteLE32(&header.data.header.Size, bytes_in_payload); + + // Do an extra copy rather than writing everything to buf directly, since buf + // might not be correctly aligned. + memcpy(buf, &header, kWavHeaderSize); +} + +bool ReadWavHeader(const uint8_t* buf, + int* num_channels, + int* sample_rate, + WavFormat* format, + int* bytes_per_sample, + uint32_t* num_samples) { + WavHeader header; + memcpy(&header, buf, kWavHeaderSize); + + // Parse needed fields. + *format = static_cast(ReadLE16(header.fmt.AudioFormat)); + *num_channels = ReadLE16(header.fmt.NumChannels); + *sample_rate = ReadLE32(header.fmt.SampleRate); + *bytes_per_sample = ReadLE16(header.fmt.BitsPerSample) / 8; + const uint32_t bytes_in_payload = ReadLE32(header.data.header.Size); + if (*bytes_per_sample <= 0) + return false; + *num_samples = bytes_in_payload / *bytes_per_sample; + + // Sanity check remaining fields. + if (ReadFourCC(header.riff.header.ID) != "RIFF") + return false; + if (ReadFourCC(header.riff.Format) != "WAVE") + return false; + if (ReadFourCC(header.fmt.header.ID) != "fmt ") + return false; + if (ReadFourCC(header.data.header.ID) != "data") + return false; + + if (ReadLE32(header.riff.header.Size) != RiffChunkSize(bytes_in_payload)) + return false; + if (ReadLE32(header.fmt.header.Size) != kFmtSubchunkSize) + return false; + if (ReadLE32(header.fmt.ByteRate) != + ByteRate(*num_channels, *sample_rate, *bytes_per_sample)) + return false; + if (ReadLE16(header.fmt.BlockAlign) != + BlockAlign(*num_channels, *bytes_per_sample)) + return false; + + return CheckWavParameters(*num_channels, *sample_rate, *format, + *bytes_per_sample, *num_samples); +} + + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_header.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_header.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_header.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_header.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_COMMON_AUDIO_WAV_HEADER_H_ +#define WEBRTC_COMMON_AUDIO_WAV_HEADER_H_ + +#include +#include + +namespace webrtc { + +static const size_t kWavHeaderSize = 44; + +enum WavFormat { + kWavFormatPcm = 1, // PCM, each sample of size bytes_per_sample + kWavFormatALaw = 6, // 8-bit ITU-T G.711 A-law + kWavFormatMuLaw = 7, // 8-bit ITU-T G.711 mu-law +}; + +// Return true if the given parameters will make a well-formed WAV header. +bool CheckWavParameters(int num_channels, + int sample_rate, + WavFormat format, + int bytes_per_sample, + uint32_t num_samples); + +// Write a kWavHeaderSize bytes long WAV header to buf. The payload that +// follows the header is supposed to have the specified number of interleaved +// channels and contain the specified total number of samples of the specified +// type. CHECKs the input parameters for validity. +void WriteWavHeader(uint8_t* buf, + int num_channels, + int sample_rate, + WavFormat format, + int bytes_per_sample, + uint32_t num_samples); + +// Read a kWavHeaderSize bytes long WAV header from buf and parse the values +// into the provided output parameters. Returns false if the header is invalid. +bool ReadWavHeader(const uint8_t* buf, + int* num_channels, + int* sample_rate, + WavFormat* format, + int* bytes_per_sample, + uint32_t* num_samples); + +} // namespace webrtc + +#endif // WEBRTC_COMMON_AUDIO_WAV_HEADER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_header_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_header_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_header_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/wav_header_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/common_audio/wav_header.h" +#include "webrtc/system_wrappers/interface/compile_assert.h" + +// Try various choices of WAV header parameters, and make sure that the good +// ones are accepted and the bad ones rejected. +TEST(WavHeaderTest, CheckWavParameters) { + // Try some really stupid values for one parameter at a time. + EXPECT_TRUE(webrtc::CheckWavParameters(1, 8000, webrtc::kWavFormatPcm, 1, 0)); + EXPECT_FALSE( + webrtc::CheckWavParameters(0, 8000, webrtc::kWavFormatPcm, 1, 0)); + EXPECT_FALSE( + webrtc::CheckWavParameters(-1, 8000, webrtc::kWavFormatPcm, 1, 0)); + EXPECT_FALSE(webrtc::CheckWavParameters(1, 0, webrtc::kWavFormatPcm, 1, 0)); + EXPECT_FALSE(webrtc::CheckWavParameters(1, 8000, webrtc::WavFormat(0), 1, 0)); + EXPECT_FALSE( + webrtc::CheckWavParameters(1, 8000, webrtc::kWavFormatPcm, 0, 0)); + + // Try invalid format/bytes-per-sample combinations. + EXPECT_TRUE(webrtc::CheckWavParameters(1, 8000, webrtc::kWavFormatPcm, 2, 0)); + EXPECT_FALSE( + webrtc::CheckWavParameters(1, 8000, webrtc::kWavFormatPcm, 4, 0)); + EXPECT_FALSE( + webrtc::CheckWavParameters(1, 8000, webrtc::kWavFormatALaw, 2, 0)); + EXPECT_FALSE( + webrtc::CheckWavParameters(1, 8000, webrtc::kWavFormatMuLaw, 2, 0)); + + // Too large values. + EXPECT_FALSE(webrtc::CheckWavParameters( + 1 << 20, 1 << 20, webrtc::kWavFormatPcm, 1, 0)); + EXPECT_FALSE(webrtc::CheckWavParameters( + 1, 8000, webrtc::kWavFormatPcm, 1, std::numeric_limits::max())); + + // Not the same number of samples for each channel. + EXPECT_FALSE( + webrtc::CheckWavParameters(3, 8000, webrtc::kWavFormatPcm, 1, 5)); +} + +TEST(WavHeaderTest, ReadWavHeaderWithErrors) { + int num_channels = 0; + int sample_rate = 0; + webrtc::WavFormat format = webrtc::kWavFormatPcm; + int bytes_per_sample = 0; + uint32_t num_samples = 0; + + // Test a few ways the header can be invalid. We start with the valid header + // used in WriteAndReadWavHeader, and invalidate one field per test. The + // invalid field is indicated in the array name, and in the comments with + // *BAD*. + static const uint8_t kBadRiffID[] = { + 'R', 'i', 'f', 'f', // *BAD* + 0xbd, 0xd0, 0x5b, 0x07, // size of whole file - 8: 123457689 + 44 - 8 + 'W', 'A', 'V', 'E', + 'f', 'm', 't', ' ', + 16, 0, 0, 0, // size of fmt block - 8: 24 - 8 + 6, 0, // format: A-law (6) + 17, 0, // channels: 17 + 0x39, 0x30, 0, 0, // sample rate: 12345 + 0xc9, 0x33, 0x03, 0, // byte rate: 1 * 17 * 12345 + 17, 0, // block align: NumChannels * BytesPerSample + 8, 0, // bits per sample: 1 * 8 + 'd', 'a', 't', 'a', + 0x99, 0xd0, 0x5b, 0x07, // size of payload: 123457689 + 0xa4, 0xa4, 0xa4, 0xa4, // untouched bytes after header + }; + EXPECT_FALSE( + webrtc::ReadWavHeader(kBadRiffID, &num_channels, &sample_rate, + &format, &bytes_per_sample, &num_samples)); + + static const uint8_t kBadBitsPerSample[] = { + 'R', 'I', 'F', 'F', + 0xbd, 0xd0, 0x5b, 0x07, // size of whole file - 8: 123457689 + 44 - 8 + 'W', 'A', 'V', 'E', + 'f', 'm', 't', ' ', + 16, 0, 0, 0, // size of fmt block - 8: 24 - 8 + 6, 0, // format: A-law (6) + 17, 0, // channels: 17 + 0x39, 0x30, 0, 0, // sample rate: 12345 + 0xc9, 0x33, 0x03, 0, // byte rate: 1 * 17 * 12345 + 17, 0, // block align: NumChannels * BytesPerSample + 1, 0, // bits per sample: *BAD* + 'd', 'a', 't', 'a', + 0x99, 0xd0, 0x5b, 0x07, // size of payload: 123457689 + 0xa4, 0xa4, 0xa4, 0xa4, // untouched bytes after header + }; + EXPECT_FALSE( + webrtc::ReadWavHeader(kBadBitsPerSample, &num_channels, &sample_rate, + &format, &bytes_per_sample, &num_samples)); + + static const uint8_t kBadByteRate[] = { + 'R', 'I', 'F', 'F', + 0xbd, 0xd0, 0x5b, 0x07, // size of whole file - 8: 123457689 + 44 - 8 + 'W', 'A', 'V', 'E', + 'f', 'm', 't', ' ', + 16, 0, 0, 0, // size of fmt block - 8: 24 - 8 + 6, 0, // format: A-law (6) + 17, 0, // channels: 17 + 0x39, 0x30, 0, 0, // sample rate: 12345 + 0x00, 0x33, 0x03, 0, // byte rate: *BAD* + 17, 0, // block align: NumChannels * BytesPerSample + 8, 0, // bits per sample: 1 * 8 + 'd', 'a', 't', 'a', + 0x99, 0xd0, 0x5b, 0x07, // size of payload: 123457689 + 0xa4, 0xa4, 0xa4, 0xa4, // untouched bytes after header + }; + EXPECT_FALSE( + webrtc::ReadWavHeader(kBadByteRate, &num_channels, &sample_rate, + &format, &bytes_per_sample, &num_samples)); +} + +// Try writing and reading a valid WAV header and make sure it looks OK. +TEST(WavHeaderTest, WriteAndReadWavHeader) { + static const int kSize = 4 + webrtc::kWavHeaderSize + 4; + uint8_t buf[kSize]; + memset(buf, 0xa4, sizeof(buf)); + webrtc::WriteWavHeader( + buf + 4, 17, 12345, webrtc::kWavFormatALaw, 1, 123457689); + static const uint8_t kExpectedBuf[] = { + 0xa4, 0xa4, 0xa4, 0xa4, // untouched bytes before header + 'R', 'I', 'F', 'F', + 0xbd, 0xd0, 0x5b, 0x07, // size of whole file - 8: 123457689 + 44 - 8 + 'W', 'A', 'V', 'E', + 'f', 'm', 't', ' ', + 16, 0, 0, 0, // size of fmt block - 8: 24 - 8 + 6, 0, // format: A-law (6) + 17, 0, // channels: 17 + 0x39, 0x30, 0, 0, // sample rate: 12345 + 0xc9, 0x33, 0x03, 0, // byte rate: 1 * 17 * 12345 + 17, 0, // block align: NumChannels * BytesPerSample + 8, 0, // bits per sample: 1 * 8 + 'd', 'a', 't', 'a', + 0x99, 0xd0, 0x5b, 0x07, // size of payload: 123457689 + 0xa4, 0xa4, 0xa4, 0xa4, // untouched bytes after header + }; + COMPILE_ASSERT(sizeof(kExpectedBuf) == kSize, buf_size); + EXPECT_EQ(0, memcmp(kExpectedBuf, buf, kSize)); + + int num_channels = 0; + int sample_rate = 0; + webrtc::WavFormat format = webrtc::kWavFormatPcm; + int bytes_per_sample = 0; + uint32_t num_samples = 0; + EXPECT_TRUE( + webrtc::ReadWavHeader(buf + 4, &num_channels, &sample_rate, &format, + &bytes_per_sample, &num_samples)); + EXPECT_EQ(17, num_channels); + EXPECT_EQ(12345, sample_rate); + EXPECT_EQ(webrtc::kWavFormatALaw, format); + EXPECT_EQ(1, bytes_per_sample); + EXPECT_EQ(123457689u, num_samples); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/window_generator.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/window_generator.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/window_generator.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/window_generator.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#define _USE_MATH_DEFINES + +#include "webrtc/common_audio/window_generator.h" + +#include +#include + +#include "webrtc/base/checks.h" + +using std::complex; + +namespace { + +// Modified Bessel function of order 0 for complex inputs. +complex I0(complex x) { + complex y = x / 3.75f; + y *= y; + return 1.0f + y * ( + 3.5156229f + y * ( + 3.0899424f + y * ( + 1.2067492f + y * ( + 0.2659732f + y * ( + 0.360768e-1f + y * 0.45813e-2f))))); +} + +} // namespace + +namespace webrtc { + +void WindowGenerator::Hanning(int length, float* window) { + CHECK_GT(length, 1); + CHECK(window != nullptr); + for (int i = 0; i < length; ++i) { + window[i] = 0.5f * (1 - cosf(2 * static_cast(M_PI) * i / + (length - 1))); + } +} + +void WindowGenerator::KaiserBesselDerived(float alpha, int length, + float* window) { + CHECK_GT(length, 1); + CHECK(window != nullptr); + + const int half = (length + 1) / 2; + float sum = 0.0f; + + for (int i = 0; i <= half; ++i) { + complex r = (4.0f * i) / length - 1.0f; + sum += I0(static_cast(M_PI) * alpha * sqrt(1.0f - r * r)).real(); + window[i] = sum; + } + for (int i = length - 1; i >= half; --i) { + window[length - i - 1] = sqrtf(window[length - i - 1] / sum); + window[i] = window[length - i - 1]; + } + if (length % 2 == 1) { + window[half - 1] = sqrtf(window[half - 1] / sum); + } +} + +} // namespace webrtc + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/window_generator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/window_generator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/window_generator.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/window_generator.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_COMMON_AUDIO_WINDOW_GENERATOR_H_ +#define WEBRTC_COMMON_AUDIO_WINDOW_GENERATOR_H_ + +#include "webrtc/base/constructormagic.h" + +namespace webrtc { + +// Helper class with generators for various signal transform windows. +class WindowGenerator { + public: + static void Hanning(int length, float* window); + static void KaiserBesselDerived(float alpha, int length, float* window); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(WindowGenerator); +}; + +} // namespace webrtc + +#endif // WEBRTC_COMMON_AUDIO_WINDOW_GENERATOR_H_ + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/window_generator_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/window_generator_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_audio/window_generator_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_audio/window_generator_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/common_audio/window_generator.h" + +#include + +#include "testing/gtest/include/gtest/gtest.h" + +namespace webrtc { + +TEST(WindowGeneratorTest, KaiserBesselDerived) { + float window[7]; + + memset(window, 0, sizeof(window)); + + WindowGenerator::KaiserBesselDerived(0.397856f, 2, window); + ASSERT_NEAR(window[0], 0.707106f, 1e-6f); + ASSERT_NEAR(window[1], 0.707106f, 1e-6f); + ASSERT_NEAR(window[2], 0.0f, 1e-6f); + ASSERT_NEAR(window[3], 0.0f, 1e-6f); + ASSERT_NEAR(window[4], 0.0f, 1e-6f); + ASSERT_NEAR(window[5], 0.0f, 1e-6f); + ASSERT_NEAR(window[6], 0.0f, 1e-6f); + + WindowGenerator::KaiserBesselDerived(0.397856f, 3, window); + ASSERT_NEAR(window[0], 0.598066f, 1e-6f); + ASSERT_NEAR(window[1], 0.922358f, 1e-6f); + ASSERT_NEAR(window[2], 0.598066f, 1e-6f); + ASSERT_NEAR(window[3], 0.0f, 1e-6f); + ASSERT_NEAR(window[4], 0.0f, 1e-6f); + ASSERT_NEAR(window[5], 0.0f, 1e-6f); + ASSERT_NEAR(window[6], 0.0f, 1e-6f); + + WindowGenerator::KaiserBesselDerived(0.397856f, 6, window); + ASSERT_NEAR(window[0], 0.458495038865344f, 1e-6f); + ASSERT_NEAR(window[1], 0.707106781186548f, 1e-6f); + ASSERT_NEAR(window[2], 0.888696967101760f, 1e-6f); + ASSERT_NEAR(window[3], 0.888696967101760f, 1e-6f); + ASSERT_NEAR(window[4], 0.707106781186548f, 1e-6f); + ASSERT_NEAR(window[5], 0.458495038865344f, 1e-6f); + ASSERT_NEAR(window[6], 0.0f, 1e-6f); +} + +TEST(WindowGeneratorTest, Hanning) { + float window[7]; + + memset(window, 0, sizeof(window)); + + window[0] = -1.0f; + window[1] = -1.0f; + WindowGenerator::Hanning(2, window); + ASSERT_NEAR(window[0], 0.0f, 1e-6f); + ASSERT_NEAR(window[1], 0.0f, 1e-6f); + ASSERT_NEAR(window[2], 0.0f, 1e-6f); + ASSERT_NEAR(window[3], 0.0f, 1e-6f); + ASSERT_NEAR(window[4], 0.0f, 1e-6f); + ASSERT_NEAR(window[5], 0.0f, 1e-6f); + ASSERT_NEAR(window[6], 0.0f, 1e-6f); + + window[0] = -1.0f; + window[2] = -1.0f; + WindowGenerator::Hanning(3, window); + ASSERT_NEAR(window[0], 0.0f, 1e-6f); + ASSERT_NEAR(window[1], 1.0f, 1e-6f); + ASSERT_NEAR(window[2], 0.0f, 1e-6f); + ASSERT_NEAR(window[3], 0.0f, 1e-6f); + ASSERT_NEAR(window[4], 0.0f, 1e-6f); + ASSERT_NEAR(window[5], 0.0f, 1e-6f); + ASSERT_NEAR(window[6], 0.0f, 1e-6f); + + window[0] = -1.0f; + window[5] = -1.0f; + WindowGenerator::Hanning(6, window); + ASSERT_NEAR(window[0], 0.0f, 1e-6f); + ASSERT_NEAR(window[1], 0.345491f, 1e-6f); + ASSERT_NEAR(window[2], 0.904508f, 1e-6f); + ASSERT_NEAR(window[3], 0.904508f, 1e-6f); + ASSERT_NEAR(window[4], 0.345491f, 1e-6f); + ASSERT_NEAR(window[5], 0.0f, 1e-6f); + ASSERT_NEAR(window[6], 0.0f, 1e-6f); +} + +} // namespace webrtc + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common.gyp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common.gyp 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,20 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. +{ + 'includes': ['build/common.gypi'], + 'targets': [ + { + 'target_name': 'webrtc_common', + 'type': 'static_library', + 'sources': [ + 'config.h', + 'config.cc', + ], + }, + ], +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_types.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_types.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_types.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_types.h 2015-02-03 14:33:34.000000000 +0000 @@ -11,7 +11,12 @@ #ifndef WEBRTC_COMMON_TYPES_H_ #define WEBRTC_COMMON_TYPES_H_ -#include // size_t +#include +#include + +#include +#include + #include "webrtc/typedefs.h" #if defined(_MSC_VER) @@ -34,7 +39,7 @@ #define RTP_PAYLOAD_NAME_SIZE 32 -#if defined(WEBRTC_WIN) +#if defined(WEBRTC_WIN) || defined(WIN32) // Compares two strings without regard to case. #define STR_CASE_CMP(s1, s2) ::_stricmp(s1, s2) // Compares characters of two strings without regard to case. @@ -90,7 +95,6 @@ kTraceAudioDevice = 0x0012, kTraceVideoRenderer = 0x0014, kTraceVideoCapture = 0x0015, - kTraceVideoPreocessing = 0x0016, kTraceRemoteBitrateEstimator = 0x0017, }; @@ -158,71 +162,6 @@ kVideoFrameDelta = 4, // depends on the previus frame }; -// Interface for encrypting and decrypting regular data and rtp/rtcp packets. -// Implement this interface if you wish to provide an encryption scheme to -// the voice or video engines. -class Encryption -{ -public: - // Encrypt the given data. - // - // Args: - // channel: The channel to encrypt data for. - // in_data: The data to encrypt. This data is bytes_in bytes long. - // out_data: The buffer to write the encrypted data to. You may write more - // bytes of encrypted data than what you got as input, up to a maximum - // of webrtc::kViEMaxMtu if you are encrypting in the video engine, or - // webrtc::kVoiceEngineMaxIpPacketSizeBytes for the voice engine. - // bytes_in: The number of bytes in the input buffer. - // bytes_out: The number of bytes written in out_data. - virtual void encrypt( - int channel, - unsigned char* in_data, - unsigned char* out_data, - int bytes_in, - int* bytes_out) = 0; - - // Decrypts the given data. This should reverse the effects of encrypt(). - // - // Args: - // channel_no: The channel to decrypt data for. - // in_data: The data to decrypt. This data is bytes_in bytes long. - // out_data: The buffer to write the decrypted data to. You may write more - // bytes of decrypted data than what you got as input, up to a maximum - // of webrtc::kViEMaxMtu if you are encrypting in the video engine, or - // webrtc::kVoiceEngineMaxIpPacketSizeBytes for the voice engine. - // bytes_in: The number of bytes in the input buffer. - // bytes_out: The number of bytes written in out_data. - virtual void decrypt( - int channel, - unsigned char* in_data, - unsigned char* out_data, - int bytes_in, - int* bytes_out) = 0; - - // Encrypts a RTCP packet. Otherwise, this method has the same contract as - // encrypt(). - virtual void encrypt_rtcp( - int channel, - unsigned char* in_data, - unsigned char* out_data, - int bytes_in, - int* bytes_out) = 0; - - // Decrypts a RTCP packet. Otherwise, this method has the same contract as - // decrypt(). - virtual void decrypt_rtcp( - int channel, - unsigned char* in_data, - unsigned char* out_data, - int bytes_in, - int* bytes_out) = 0; - -protected: - virtual ~Encryption() {} - Encryption() {} -}; - // External transport callback interface class Transport { @@ -258,6 +197,38 @@ uint32_t ssrc) = 0; }; +// Statistics for RTCP packet types. +struct RtcpPacketTypeCounter { + RtcpPacketTypeCounter() + : nack_packets(0), + fir_packets(0), + pli_packets(0), + nack_requests(0), + unique_nack_requests(0) {} + + void Add(const RtcpPacketTypeCounter& other) { + nack_packets += other.nack_packets; + fir_packets += other.fir_packets; + pli_packets += other.pli_packets; + nack_requests += other.nack_requests; + unique_nack_requests += other.unique_nack_requests; + } + + int UniqueNackRequestsInPercent() const { + if (nack_requests == 0) { + return 0; + } + return static_cast( + (unique_nack_requests * 100.0f / nack_requests) + 0.5f); + } + + uint32_t nack_packets; // Number of RTCP NACK packets. + uint32_t fir_packets; // Number of RTCP FIR packets. + uint32_t pli_packets; // Number of RTCP PLI packets. + uint32_t nack_requests; // Number of NACKed RTP packets. + uint32_t unique_nack_requests; // Number of unique NACKed RTP packets. +}; + // Data usage statistics for a (rtp) stream struct StreamDataCounters { StreamDataCounters() @@ -268,6 +239,7 @@ retransmitted_packets(0), fec_packets(0) {} + // TODO(pbos): Rename bytes -> media_bytes. uint32_t bytes; // Payload bytes, excluding RTP headers and padding. uint32_t header_bytes; // Number of bytes used by RTP headers. uint32_t padding_bytes; // Number of padding bytes. @@ -299,7 +271,9 @@ public: virtual ~BitrateStatisticsObserver() {} - virtual void Notify(const BitrateStatistics& stats, uint32_t ssrc) = 0; + virtual void Notify(const BitrateStatistics& total_stats, + const BitrateStatistics& retransmit_stats, + uint32_t ssrc) = 0; }; // Callback, used to notify an observer whenever frame counts have been updated @@ -311,19 +285,40 @@ const unsigned int ssrc) = 0; }; +// Callback, used to notify an observer whenever the send-side delay is updated. +class SendSideDelayObserver { + public: + virtual ~SendSideDelayObserver() {} + virtual void SendSideDelayUpdated(int avg_delay_ms, + int max_delay_ms, + uint32_t ssrc) = 0; +}; + // ================================================================== // Voice specific types // ================================================================== // Each codec supported can be described by this structure. -struct CodecInst -{ - int pltype; - char plname[RTP_PAYLOAD_NAME_SIZE]; - int plfreq; - int pacsize; - int channels; - int rate; // bits/sec unlike {start,min,max}Bitrate elsewhere in this file! +struct CodecInst { + int pltype; + char plname[RTP_PAYLOAD_NAME_SIZE]; + int plfreq; + int pacsize; + int channels; + int rate; // bits/sec unlike {start,min,max}Bitrate elsewhere in this file! + + bool operator==(const CodecInst& other) const { + return pltype == other.pltype && + (STR_CASE_CMP(plname, other.plname) == 0) && + plfreq == other.plfreq && + pacsize == other.pacsize && + channels == other.channels && + rate == other.rate; + } + + bool operator!=(const CodecInst& other) const { + return !(*this == other); + } }; // RTP @@ -358,9 +353,9 @@ uint16_t preferredBufferSize; // adding extra delay due to "peaky jitter" bool jitterPeaksFound; - // loss rate (network + late) in percent (in Q14) + // Loss rate (network + late); fraction between 0 and 1, scaled to Q14. uint16_t currentPacketLossRate; - // late loss rate in percent (in Q14) + // Late loss rate; fraction between 0 and 1, scaled to Q14. uint16_t currentDiscardRate; // fraction (of original stream) of synthesized speech inserted through // expansion (in Q14) @@ -499,6 +494,7 @@ kAudioLinuxPulse = 4 }; +// TODO(henrika): to be removed. enum NetEqModes // NetEQ playout configurations { // Optimized trade-off between low delay and jitter robustness for two-way @@ -515,6 +511,7 @@ kNetEqOff = 3 }; +// TODO(henrika): to be removed. enum OnHoldModes // On Hold direction { kHoldSendAndPlay = 0, // Put both sending and playing in on-hold state. @@ -522,6 +519,7 @@ kHoldPlayOnly // Put only playing in on-hold state. }; +// TODO(henrika): to be removed. enum AmrMode { kRfc3267BwEfficient = 0, @@ -595,66 +593,106 @@ }; // VP8 specific -struct VideoCodecVP8 -{ - bool pictureLossIndicationOn; - bool feedbackModeOn; - VideoCodecComplexity complexity; - VP8ResilienceMode resilience; - unsigned char numberOfTemporalLayers; - bool denoisingOn; - bool errorConcealmentOn; - bool automaticResizeOn; - bool frameDroppingOn; - int keyFrameInterval; -}; - -// H264 specific -struct VideoCodecH264 -{ - uint8_t profile; - uint8_t constraints; - uint8_t level; - uint8_t packetizationMode; // 0 or 1 - bool frameDroppingOn; - int keyFrameInterval; - // These are null/0 if not externally negotiated - const uint8_t* spsData; - size_t spsLen; - const uint8_t* ppsData; - size_t ppsLen; +struct VideoCodecVP8 { + bool pictureLossIndicationOn; + bool feedbackModeOn; + VideoCodecComplexity complexity; + VP8ResilienceMode resilience; + unsigned char numberOfTemporalLayers; + bool denoisingOn; + bool errorConcealmentOn; + bool automaticResizeOn; + bool frameDroppingOn; + int keyFrameInterval; + + bool operator==(const VideoCodecVP8& other) const { + return pictureLossIndicationOn == other.pictureLossIndicationOn && + feedbackModeOn == other.feedbackModeOn && + complexity == other.complexity && + resilience == other.resilience && + numberOfTemporalLayers == other.numberOfTemporalLayers && + denoisingOn == other.denoisingOn && + errorConcealmentOn == other.errorConcealmentOn && + automaticResizeOn == other.automaticResizeOn && + frameDroppingOn == other.frameDroppingOn && + keyFrameInterval == other.keyFrameInterval; + } + + bool operator!=(const VideoCodecVP8& other) const { + return !(*this == other); + } +}; + +// VP9 specific +struct VideoCodecVP9 { + VideoCodecComplexity complexity; + int resilience; + unsigned char numberOfTemporalLayers; + bool denoisingOn; + bool frameDroppingOn; + int keyFrameInterval; + bool adaptiveQpMode; +}; + +// H264 specific. +struct VideoCodecH264 { + VideoCodecProfile profile; + uint8_t profile_byte; + uint8_t constraints; + uint8_t level; + uint8_t packetizationMode; // 0 or 1 + bool frameDroppingOn; + int keyFrameInterval; + // These are NULL/0 if not externally negotiated. + const uint8_t* spsData; + size_t spsLen; + const uint8_t* ppsData; + size_t ppsLen; }; // Video codec types -enum VideoCodecType -{ - kVideoCodecVP8, - kVideoCodecH264, - kVideoCodecI420, - kVideoCodecRED, - kVideoCodecULPFEC, - kVideoCodecGeneric, - kVideoCodecUnknown +enum VideoCodecType { + kVideoCodecVP8, + kVideoCodecVP9, + kVideoCodecH264, + kVideoCodecI420, + kVideoCodecRED, + kVideoCodecULPFEC, + kVideoCodecGeneric, + kVideoCodecUnknown }; -union VideoCodecUnion -{ - VideoCodecVP8 VP8; - VideoCodecH264 H264; +union VideoCodecUnion { + VideoCodecVP8 VP8; + VideoCodecVP9 VP9; + VideoCodecH264 H264; }; // Simulcast is when the same stream is encoded multiple times with different // settings such as resolution. -struct SimulcastStream -{ - unsigned short width; - unsigned short height; - unsigned char numberOfTemporalLayers; - unsigned int maxBitrate; // kilobits/sec. - unsigned int targetBitrate; // kilobits/sec. - unsigned int minBitrate; // kilobits/sec. - unsigned int qpMax; // minimum quality +struct SimulcastStream { + unsigned short width; + unsigned short height; + unsigned char numberOfTemporalLayers; + unsigned int maxBitrate; // kilobits/sec. + unsigned int targetBitrate; // kilobits/sec. + unsigned int minBitrate; // kilobits/sec. + unsigned int qpMax; // minimum quality + + bool operator==(const SimulcastStream& other) const { + return width == other.width && + height == other.height && + numberOfTemporalLayers == other.numberOfTemporalLayers && + maxBitrate == other.maxBitrate && + targetBitrate == other.targetBitrate && + minBitrate == other.minBitrate && + qpMax == other.qpMax; + } + + bool operator!=(const SimulcastStream& other) const { + return !(*this == other); + } }; enum VideoCodecMode { @@ -663,33 +701,62 @@ }; // Common video codec properties -struct VideoCodec -{ - VideoCodecType codecType; - char plName[kPayloadNameSize]; - unsigned char plType; - - unsigned short width; - unsigned short height; - // width & height modulo resolution_divisor must be 0 - unsigned char resolution_divisor; - - unsigned int startBitrate; // kilobits/sec. - unsigned int maxBitrate; // kilobits/sec. - unsigned int minBitrate; // kilobits/sec. - unsigned char maxFramerate; - - VideoCodecUnion codecSpecific; - - unsigned int qpMax; - unsigned char numberOfSimulcastStreams; - SimulcastStream simulcastStream[kMaxSimulcastStreams]; - - VideoCodecMode mode; +struct VideoCodec { + VideoCodecType codecType; + char plName[kPayloadNameSize]; + unsigned char plType; + + unsigned short width; + unsigned short height; + // width & height modulo resolution_divisor must be 0 + unsigned char resolution_divisor; + + unsigned int startBitrate; // kilobits/sec. + unsigned int maxBitrate; // kilobits/sec. + unsigned int minBitrate; // kilobits/sec. + unsigned int targetBitrate; // kilobits/sec. + + unsigned char maxFramerate; + + VideoCodecUnion codecSpecific; + + unsigned int qpMax; + unsigned char numberOfSimulcastStreams; + SimulcastStream simulcastStream[kMaxSimulcastStreams]; + + VideoCodecMode mode; + + // When using an external encoder/decoder this allows to pass + // extra options without requiring webrtc to be aware of them. + Config* extra_options; + + bool operator==(const VideoCodec& other) const { + bool ret = codecType == other.codecType && + (STR_CASE_CMP(plName, other.plName) == 0) && + plType == other.plType && + width == other.width && + height == other.height && + startBitrate == other.startBitrate && + maxBitrate == other.maxBitrate && + minBitrate == other.minBitrate && + targetBitrate == other.targetBitrate && + maxFramerate == other.maxFramerate && + qpMax == other.qpMax && + numberOfSimulcastStreams == other.numberOfSimulcastStreams && + mode == other.mode; + if (ret && codecType == kVideoCodecVP8) { + ret &= (codecSpecific.VP8 == other.codecSpecific.VP8); + } + + for (unsigned char i = 0; i < other.numberOfSimulcastStreams && ret; ++i) { + ret &= (simulcastStream[i] == other.simulcastStream[i]); + } + return ret; + } - // When using an external encoder/decoder this allows to pass - // extra options without requiring webrtc to be aware of them. - Config* extra_options; + bool operator!=(const VideoCodec& other) const { + return !(*this == other); + } }; // Bandwidth over-use detector options. These are used to drive @@ -741,20 +808,67 @@ // This structure will have the information about when packet is actually // received by socket. struct PacketTime { - PacketTime() : timestamp(-1), max_error_us(-1) {} - PacketTime(int64_t timestamp, int64_t max_error_us) - : timestamp(timestamp), max_error_us(max_error_us) { + PacketTime() : timestamp(-1), not_before(-1) {} + PacketTime(int64_t timestamp, int64_t not_before) + : timestamp(timestamp), not_before(not_before) { } - int64_t timestamp; // Receive time after socket delivers the data. - int64_t max_error_us; // Earliest possible time the data could have arrived, - // indicating the potential error in the |timestamp| - // value,in case the system is busy. - // For example, the time of the last select() call. - // If unknown, this value will be set to zero. + int64_t timestamp; // Receive time after socket delivers the data. + int64_t not_before; // Earliest possible time the data could have arrived, + // indicating the potential error in the |timestamp| + // value,in case the system is busy. + // For example, the time of the last select() call. + // If unknown, this value will be set to zero. +}; + +struct RTPHeaderExtension { + RTPHeaderExtension() + : hasTransmissionTimeOffset(false), + transmissionTimeOffset(0), + hasAbsoluteSendTime(false), + absoluteSendTime(0), + hasAudioLevel(false), + audioLevel(0) {} + + bool hasTransmissionTimeOffset; + int32_t transmissionTimeOffset; + bool hasAbsoluteSendTime; + uint32_t absoluteSendTime; + + // Audio Level includes both level in dBov and voiced/unvoiced bit. See: + // https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/ + bool hasAudioLevel; + uint8_t audioLevel; +}; + +struct RTPHeader { + RTPHeader() + : markerBit(false), + payloadType(0), + sequenceNumber(0), + timestamp(0), + ssrc(0), + numCSRCs(0), + paddingLength(0), + headerLength(0), + payload_type_frequency(0), + extension() { + memset(&arrOfCSRCs, 0, sizeof(arrOfCSRCs)); + } + + bool markerBit; + uint8_t payloadType; + uint16_t sequenceNumber; + uint32_t timestamp; + uint32_t ssrc; + uint8_t numCSRCs; + uint32_t arrOfCSRCs[kRtpCsrcSize]; + uint8_t paddingLength; + uint16_t headerLength; + int payload_type_frequency; + RTPHeaderExtension extension; }; } // namespace webrtc #endif // WEBRTC_COMMON_TYPES_H_ - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/BUILD.gn 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,55 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../build/webrtc.gni") + +config("common_video_config") { + include_dirs = [ + "interface", + "libyuv/include", + ] +} + +source_set("common_video") { + sources = [ + "i420_video_frame.cc", + "interface/i420_video_frame.h", + "interface/native_handle.h", + "interface/texture_video_frame.h", + "libyuv/include/scaler.h", + "libyuv/include/webrtc_libyuv.h", + "libyuv/scaler.cc", + "libyuv/webrtc_libyuv.cc", + "plane.cc", + "plane.h", + "texture_video_frame.cc" + ] + + include_dirs = [ "../modules/interface" ] + + configs += [ "..:common_config" ] + public_configs = [ + "..:common_inherited_config", + ":common_video_config", + ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ "../system_wrappers" ] + + if (rtc_build_libyuv) { + deps += [ "//third_party/libyuv" ] + } else { + # Need to add a directory normally exported by libyuv. + include_dirs += [ "//third_party/libyuv/include" ] + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/common_video_unittests.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/common_video_unittests.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/common_video_unittests.gyp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/common_video_unittests.gyp 2015-02-03 14:33:34.000000000 +0000 @@ -30,9 +30,7 @@ 4267, # size_t to int truncation. ], 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are - # using Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['OS=="android"', { 'dependencies': [ '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code', ], @@ -41,9 +39,7 @@ }, ], # targets 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are using - # Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['OS=="android"', { 'targets': [ { 'target_name': 'common_video_unittests_apk_target', @@ -64,7 +60,6 @@ ], 'includes': [ '../build/isolate.gypi', - 'common_video_unittests.isolate', ], 'sources': [ 'common_video_unittests.isolate', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/common_video_unittests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/common_video_unittests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/common_video_unittests.isolate 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/common_video_unittests.isolate 2015-02-03 14:33:34.000000000 +0000 @@ -8,30 +8,25 @@ { 'conditions': [ ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. 'variables': { - 'isolate_dependency_untracked': [ - '../../../data/', - '../../../resources/', + 'files': [ + '<(DEPTH)/data/', + '<(DEPTH)/resources/', ], }, }], ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../../testing/test_env.py', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/common_video_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_tracked': [ - '../../DEPS', - '../../resources/foreman_cif.yuv', - '../../testing/test_env.py', + 'files': [ + '<(DEPTH)/DEPS', + '<(DEPTH)/resources/foreman_cif.yuv', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/common_video_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '../../tools/swarming_client/', - ], }, }], ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/i420_video_frame.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/i420_video_frame.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/i420_video_frame.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/i420_video_frame.cc 2015-02-03 14:33:34.000000000 +0000 @@ -10,6 +10,8 @@ #include "webrtc/common_video/interface/i420_video_frame.h" +#include + #include // swap namespace webrtc { @@ -18,6 +20,7 @@ : width_(0), height_(0), timestamp_(0), + ntp_time_ms_(0), render_time_ms_(0) {} I420VideoFrame::~I420VideoFrame() {} @@ -37,6 +40,7 @@ v_plane_.CreateEmptyPlane(size_v, stride_v, size_v); // Creating empty frame - reset all values. timestamp_ = 0; + ntp_time_ms_ = 0; render_time_ms_ = 0; return 0; } @@ -71,10 +75,20 @@ if (ret < 0) return ret; timestamp_ = videoFrame.timestamp_; + ntp_time_ms_ = videoFrame.ntp_time_ms_; render_time_ms_ = videoFrame.render_time_ms_; return 0; } +I420VideoFrame* I420VideoFrame::CloneFrame() const { + scoped_ptr new_frame(new I420VideoFrame()); + if (new_frame->CopyFrame(*this) == -1) { + // CopyFrame failed. + return NULL; + } + return new_frame.release(); +} + void I420VideoFrame::SwapFrame(I420VideoFrame* videoFrame) { y_plane_.Swap(videoFrame->y_plane_); u_plane_.Swap(videoFrame->u_plane_); @@ -82,6 +96,7 @@ std::swap(width_, videoFrame->width_); std::swap(height_, videoFrame->height_); std::swap(timestamp_, videoFrame->timestamp_); + std::swap(ntp_time_ms_, videoFrame->ntp_time_ms_); std::swap(render_time_ms_, videoFrame->render_time_ms_); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/i420_video_frame_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/i420_video_frame_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/i420_video_frame_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/i420_video_frame_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -19,8 +19,8 @@ namespace webrtc { -bool EqualFrames(const I420VideoFrame& videoFrame1, - const I420VideoFrame& videoFrame2); +bool EqualFrames(const I420VideoFrame& frame1, + const I420VideoFrame& frame2); bool EqualFramesExceptSize(const I420VideoFrame& frame1, const I420VideoFrame& frame2); int ExpectedSize(int plane_stride, int image_height, PlaneType type); @@ -49,10 +49,12 @@ EXPECT_EQ(valid_value, frame.height()); EXPECT_EQ(invalid_value, frame.set_height(0)); EXPECT_EQ(valid_value, frame.height()); - frame.set_timestamp(100u); - EXPECT_EQ(100u, frame.timestamp()); - frame.set_render_time_ms(100); - EXPECT_EQ(100, frame.render_time_ms()); + frame.set_timestamp(123u); + EXPECT_EQ(123u, frame.timestamp()); + frame.set_ntp_time_ms(456); + EXPECT_EQ(456, frame.ntp_time_ms()); + frame.set_render_time_ms(789); + EXPECT_EQ(789, frame.render_time_ms()); } TEST(TestI420VideoFrame, SizeAllocation) { @@ -82,7 +84,8 @@ TEST(TestI420VideoFrame, CopyFrame) { I420VideoFrame frame1, frame2; uint32_t timestamp = 1; - int64_t render_time_ms = 1; + int64_t ntp_time_ms = 2; + int64_t render_time_ms = 3; int stride_y = 15; int stride_u = 10; int stride_v = 10; @@ -92,6 +95,7 @@ EXPECT_EQ(0, frame1.CreateEmptyFrame(width, height, stride_y, stride_u, stride_v)); frame1.set_timestamp(timestamp); + frame1.set_ntp_time_ms(ntp_time_ms); frame1.set_render_time_ms(render_time_ms); const int kSizeY = 225; const int kSizeU = 80; @@ -118,6 +122,29 @@ EXPECT_TRUE(EqualFrames(frame1, frame2)); } +TEST(TestI420VideoFrame, CloneFrame) { + I420VideoFrame frame1; + scoped_ptr frame2; + const int kSizeY = 225; + const int kSizeU = 80; + const int kSizeV = 80; + uint8_t buffer_y[kSizeY]; + uint8_t buffer_u[kSizeU]; + uint8_t buffer_v[kSizeV]; + memset(buffer_y, 16, kSizeY); + memset(buffer_u, 8, kSizeU); + memset(buffer_v, 4, kSizeV); + frame1.CreateFrame( + kSizeY, buffer_y, kSizeU, buffer_u, kSizeV, buffer_v, 20, 20, 20, 10, 10); + frame1.set_timestamp(1); + frame1.set_ntp_time_ms(2); + frame1.set_render_time_ms(3); + + frame2.reset(frame1.CloneFrame()); + EXPECT_TRUE(frame2.get() != NULL); + EXPECT_TRUE(EqualFrames(frame1, *frame2)); +} + TEST(TestI420VideoFrame, CopyBuffer) { I420VideoFrame frame1, frame2; int width = 15; @@ -151,7 +178,8 @@ TEST(TestI420VideoFrame, FrameSwap) { I420VideoFrame frame1, frame2; uint32_t timestamp1 = 1; - int64_t render_time_ms1 = 1; + int64_t ntp_time_ms1 = 2; + int64_t render_time_ms1 = 3; int stride_y1 = 15; int stride_u1 = 10; int stride_v1 = 10; @@ -160,8 +188,9 @@ const int kSizeY1 = 225; const int kSizeU1 = 80; const int kSizeV1 = 80; - uint32_t timestamp2 = 2; - int64_t render_time_ms2 = 4; + uint32_t timestamp2 = 4; + int64_t ntp_time_ms2 = 5; + int64_t render_time_ms2 = 6; int stride_y2 = 30; int stride_u2 = 20; int stride_v2 = 20; @@ -174,6 +203,7 @@ EXPECT_EQ(0, frame1.CreateEmptyFrame(width1, height1, stride_y1, stride_u1, stride_v1)); frame1.set_timestamp(timestamp1); + frame1.set_ntp_time_ms(ntp_time_ms1); frame1.set_render_time_ms(render_time_ms1); // Set memory for frame1. uint8_t buffer_y1[kSizeY1]; @@ -190,6 +220,7 @@ EXPECT_EQ(0, frame2.CreateEmptyFrame(width2, height2, stride_y2, stride_u2, stride_v2)); frame2.set_timestamp(timestamp2); + frame1.set_ntp_time_ms(ntp_time_ms2); frame2.set_render_time_ms(render_time_ms2); // Set memory for frame2. uint8_t buffer_y2[kSizeY2]; @@ -226,28 +257,24 @@ bool EqualFrames(const I420VideoFrame& frame1, const I420VideoFrame& frame2) { - if (!EqualFramesExceptSize(frame1, frame2)) - return false; - // Compare allocated memory size. - bool ret = true; - ret |= (frame1.allocated_size(kYPlane) == frame2.allocated_size(kYPlane)); - ret |= (frame1.allocated_size(kUPlane) == frame2.allocated_size(kUPlane)); - ret |= (frame1.allocated_size(kVPlane) == frame2.allocated_size(kVPlane)); - return ret; + return (EqualFramesExceptSize(frame1, frame2) && + (frame1.allocated_size(kYPlane) == frame2.allocated_size(kYPlane)) && + (frame1.allocated_size(kUPlane) == frame2.allocated_size(kUPlane)) && + (frame1.allocated_size(kVPlane) == frame2.allocated_size(kVPlane))); } bool EqualFramesExceptSize(const I420VideoFrame& frame1, const I420VideoFrame& frame2) { - bool ret = true; - ret |= (frame1.width() == frame2.width()); - ret |= (frame1.height() == frame2.height()); - ret |= (frame1.stride(kYPlane) == frame2.stride(kYPlane)); - ret |= (frame1.stride(kUPlane) == frame2.stride(kUPlane)); - ret |= (frame1.stride(kVPlane) == frame2.stride(kVPlane)); - ret |= (frame1.timestamp() == frame2.timestamp()); - ret |= (frame1.render_time_ms() == frame2.render_time_ms()); - if (!ret) + if ((frame1.width() != frame2.width()) || + (frame1.height() != frame2.height()) || + (frame1.stride(kYPlane) != frame2.stride(kYPlane)) || + (frame1.stride(kUPlane) != frame2.stride(kUPlane)) || + (frame1.stride(kVPlane) != frame2.stride(kVPlane)) || + (frame1.timestamp() != frame2.timestamp()) || + (frame1.ntp_time_ms() != frame2.ntp_time_ms()) || + (frame1.render_time_ms() != frame2.render_time_ms())) { return false; + } // Memory should be the equal for the minimum of the two sizes. int size_y = std::min(frame1.allocated_size(kYPlane), frame2.allocated_size(kYPlane)); @@ -255,13 +282,9 @@ frame2.allocated_size(kUPlane)); int size_v = std::min(frame1.allocated_size(kVPlane), frame2.allocated_size(kVPlane)); - int ret_val = 0; - ret_val += memcmp(frame1.buffer(kYPlane), frame2.buffer(kYPlane), size_y); - ret_val += memcmp(frame1.buffer(kUPlane), frame2.buffer(kUPlane), size_u); - ret_val += memcmp(frame1.buffer(kVPlane), frame2.buffer(kVPlane), size_v); - if (ret_val == 0) - return true; - return false; + return (memcmp(frame1.buffer(kYPlane), frame2.buffer(kYPlane), size_y) == 0 && + memcmp(frame1.buffer(kUPlane), frame2.buffer(kUPlane), size_u) == 0 && + memcmp(frame1.buffer(kVPlane), frame2.buffer(kVPlane), size_v) == 0); } int ExpectedSize(int plane_stride, int image_height, PlaneType type) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/interface/i420_video_frame.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/interface/i420_video_frame.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/interface/i420_video_frame.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/interface/i420_video_frame.h 2015-02-03 14:33:34.000000000 +0000 @@ -11,134 +11,7 @@ #ifndef COMMON_VIDEO_INTERFACE_I420_VIDEO_FRAME_H #define COMMON_VIDEO_INTERFACE_I420_VIDEO_FRAME_H -// I420VideoFrame class -// -// Storing and handling of YUV (I420) video frames. - -#include "webrtc/common_video/plane.h" -#include "webrtc/system_wrappers/interface/scoped_refptr.h" -#include "webrtc/typedefs.h" - -/* - * I420VideoFrame includes support for a reference counted impl. - */ - -namespace webrtc { - -enum PlaneType { - kYPlane = 0, - kUPlane = 1, - kVPlane = 2, - kNumOfPlanes = 3 -}; - -class I420VideoFrame { - public: - I420VideoFrame(); - virtual ~I420VideoFrame(); - // Infrastructure for refCount implementation. - // Implements dummy functions for reference counting so that non reference - // counted instantiation can be done. These functions should not be called - // when creating the frame with new I420VideoFrame(). - // Note: do not pass a I420VideoFrame created with new I420VideoFrame() or - // equivalent to a scoped_refptr or memory leak will occur. - virtual int32_t AddRef() {assert(false); return -1;} - virtual int32_t Release() {assert(false); return -1;} - - // 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. - // Return value: 0 on success ,-1 on error. - virtual int CreateEmptyFrame(int width, int height, - int stride_y, int stride_u, int stride_v); - - // CreateFrame: Sets the frame's members and buffers. If required size is - // bigger than allocated one, new buffers of adequate size will be allocated. - // Return value: 0 on success ,-1 on error. - virtual int CreateFrame(int size_y, const uint8_t* buffer_y, - int size_u, const uint8_t* buffer_u, - int size_v, const uint8_t* buffer_v, - int width, int height, - int stride_y, int stride_u, int stride_v); - - // Copy frame: If required size is bigger than allocated one, new buffers of - // adequate size will be allocated. - // Return value: 0 on success ,-1 on error. - virtual int CopyFrame(const I420VideoFrame& videoFrame); - - // Swap Frame. - virtual void SwapFrame(I420VideoFrame* videoFrame); - - // Get pointer to buffer per plane. - virtual uint8_t* buffer(PlaneType type); - // Overloading with const. - virtual const uint8_t* buffer(PlaneType type) const; - - // Get allocated size per plane. - virtual int allocated_size(PlaneType type) const; - - // Get allocated stride per plane. - virtual int stride(PlaneType type) const; - - // Set frame width. - virtual int set_width(int width); - - // Set frame height. - virtual int set_height(int height); - - // Get frame width. - virtual int width() const {return width_;} - - // Get frame height. - virtual int height() const {return height_;} - - // Set frame timestamp (90kHz). - virtual void set_timestamp(uint32_t timestamp) {timestamp_ = timestamp;} - - // Get frame timestamp (90kHz). - virtual uint32_t timestamp() const {return timestamp_;} - - // Set render time in miliseconds. - virtual void set_render_time_ms(int64_t render_time_ms) {render_time_ms_ = - render_time_ms;} - - // Get render time in miliseconds. - virtual int64_t render_time_ms() const {return render_time_ms_;} - - // Return true if underlying plane buffers are of zero size, false if not. - virtual bool IsZeroSize() const; - - // Reset underlying plane buffers sizes to 0. This function doesn't - // clear memory. - virtual void ResetSize(); - - // Return the handle of the underlying video frame. This is used when the - // frame is backed by a texture. The object should be destroyed when it is no - // longer in use, so the underlying resource can be freed. - virtual void* native_handle() const; - - protected: - // Verifies legality of parameters. - // Return value: 0 on success, -1 on error. - virtual int CheckDimensions(int width, int height, - int stride_y, int stride_u, int stride_v); - - private: - // Get the pointer to a specific plane. - const Plane* GetPlane(PlaneType type) const; - // Overloading with non-const. - Plane* GetPlane(PlaneType type); - - Plane y_plane_; - Plane u_plane_; - Plane v_plane_; - int width_; - int height_; - uint32_t timestamp_; - int64_t render_time_ms_; -}; // I420VideoFrame - -} // namespace webrtc +// TODO(pbos): Remove this file and include webrtc/video_frame.h instead. +#include "webrtc/video_frame.h" #endif // COMMON_VIDEO_INTERFACE_I420_VIDEO_FRAME_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/interface/texture_video_frame.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/interface/texture_video_frame.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/interface/texture_video_frame.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/interface/texture_video_frame.h 2015-02-03 14:33:34.000000000 +0000 @@ -49,6 +49,7 @@ int stride_u, int stride_v) OVERRIDE; virtual int CopyFrame(const I420VideoFrame& videoFrame) OVERRIDE; + virtual I420VideoFrame* CloneFrame() const OVERRIDE; virtual void SwapFrame(I420VideoFrame* videoFrame) OVERRIDE; virtual uint8_t* buffer(PlaneType type) OVERRIDE; virtual const uint8_t* buffer(PlaneType type) const OVERRIDE; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/interface/video_image.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/interface/video_image.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/interface/video_image.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/interface/video_image.h 2015-02-03 14:33:34.000000000 +0000 @@ -11,59 +11,7 @@ #ifndef COMMON_VIDEO_INTERFACE_VIDEO_IMAGE_H #define COMMON_VIDEO_INTERFACE_VIDEO_IMAGE_H -#include -#include "webrtc/typedefs.h" - -namespace webrtc -{ - -enum VideoFrameType -{ - kKeyFrame = 0, - kDeltaFrame = 1, - kGoldenFrame = 2, - kAltRefFrame = 3, - kSkipFrame = 4 -}; - -class EncodedImage -{ -public: - EncodedImage() - : _encodedWidth(0), - _encodedHeight(0), - _timeStamp(0), - capture_time_ms_(0), - _frameType(kDeltaFrame), - _buffer(NULL), - _length(0), - _size(0), - _completeFrame(false) {} - - EncodedImage(uint8_t* buffer, - uint32_t length, - uint32_t size) - : _encodedWidth(0), - _encodedHeight(0), - _timeStamp(0), - capture_time_ms_(0), - _frameType(kDeltaFrame), - _buffer(buffer), - _length(length), - _size(size), - _completeFrame(false) {} - - uint32_t _encodedWidth; - uint32_t _encodedHeight; - uint32_t _timeStamp; - int64_t capture_time_ms_; - VideoFrameType _frameType; - uint8_t* _buffer; - uint32_t _length; - uint32_t _size; - bool _completeFrame; -}; - -} // namespace webrtc +// TODO(pbos): Remove this file and include webrtc/video_frame.h instead. +#include "webrtc/video_frame.h" #endif // COMMON_VIDEO_INTERFACE_VIDEO_IMAGE_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/libyuv/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/libyuv/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/libyuv/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/libyuv/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../android-webrtc.mk - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_yuv -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := \ - libyuv.cc \ - scaler.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../.. \ - $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/../../../../libyuv/files/include - - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/libyuv/libyuv_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/libyuv/libyuv_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/libyuv/libyuv_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/libyuv/libyuv_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -84,7 +84,7 @@ FILE* source_file_; I420VideoFrame orig_frame_; - scoped_array orig_buffer_; + scoped_ptr orig_buffer_; const int width_; const int height_; const int size_y_; @@ -147,7 +147,7 @@ (width_ + 1) / 2, (width_ + 1) / 2)); printf("\nConvert #%d I420 <-> I420 \n", j); - scoped_array out_i420_buffer(new uint8_t[frame_length_]); + scoped_ptr out_i420_buffer(new uint8_t[frame_length_]); EXPECT_EQ(0, ConvertFromI420(orig_frame_, kI420, 0, out_i420_buffer.get())); EXPECT_EQ(0, ConvertToI420(kI420, out_i420_buffer.get(), 0, 0, @@ -162,7 +162,7 @@ j++; printf("\nConvert #%d I420 <-> RGB24\n", j); - scoped_array res_rgb_buffer2(new uint8_t[width_ * height_ * 3]); + scoped_ptr res_rgb_buffer2(new uint8_t[width_ * height_ * 3]); // Align the stride values for the output frame. int stride_y = 0; int stride_uv = 0; @@ -184,7 +184,7 @@ j++; printf("\nConvert #%d I420 <-> UYVY\n", j); - scoped_array out_uyvy_buffer(new uint8_t[width_ * height_ * 2]); + scoped_ptr out_uyvy_buffer(new uint8_t[width_ * height_ * 2]); EXPECT_EQ(0, ConvertFromI420(orig_frame_, kUYVY, 0, out_uyvy_buffer.get())); EXPECT_EQ(0, ConvertToI420(kUYVY, out_uyvy_buffer.get(), 0, 0, width_, height_, 0, kRotateNone, &res_i420_frame)); @@ -196,8 +196,8 @@ j++; printf("\nConvert #%d I420 <-> YV12\n", j); - scoped_array outYV120Buffer(new uint8_t[frame_length_]); - scoped_array res_i420_buffer(new uint8_t[frame_length_]); + scoped_ptr outYV120Buffer(new uint8_t[frame_length_]); + scoped_ptr res_i420_buffer(new uint8_t[frame_length_]); I420VideoFrame yv12_frame; EXPECT_EQ(0, ConvertFromI420(orig_frame_, kYV12, 0, outYV120Buffer.get())); yv12_frame.CreateFrame(size_y_, outYV120Buffer.get(), @@ -218,7 +218,7 @@ j++; printf("\nConvert #%d I420 <-> YUY2\n", j); - scoped_array out_yuy2_buffer(new uint8_t[width_ * height_ * 2]); + scoped_ptr out_yuy2_buffer(new uint8_t[width_ * height_ * 2]); EXPECT_EQ(0, ConvertFromI420(orig_frame_, kYUY2, 0, out_yuy2_buffer.get())); EXPECT_EQ(0, ConvertToI420(kYUY2, out_yuy2_buffer.get(), 0, 0, width_, @@ -231,7 +231,7 @@ psnr = I420PSNR(&orig_frame_, &res_i420_frame); EXPECT_EQ(48.0, psnr); printf("\nConvert #%d I420 <-> RGB565\n", j); - scoped_array out_rgb565_buffer(new uint8_t[width_ * height_ * 2]); + scoped_ptr out_rgb565_buffer(new uint8_t[width_ * height_ * 2]); EXPECT_EQ(0, ConvertFromI420(orig_frame_, kRGB565, 0, out_rgb565_buffer.get())); @@ -250,7 +250,7 @@ EXPECT_GT(ceil(psnr), 40); printf("\nConvert #%d I420 <-> ARGB8888\n", j); - scoped_array out_argb8888_buffer(new uint8_t[width_ * height_ * 4]); + scoped_ptr out_argb8888_buffer(new uint8_t[width_ * height_ * 4]); EXPECT_EQ(0, ConvertFromI420(orig_frame_, kARGB, 0, out_argb8888_buffer.get())); @@ -283,7 +283,7 @@ Calc16ByteAlignedStride(width_, &stride_y, &stride_uv); EXPECT_EQ(0,res_i420_frame.CreateEmptyFrame(width_, height_, stride_y, stride_uv, stride_uv)); - scoped_array out_i420_buffer(new uint8_t[frame_length_]); + scoped_ptr out_i420_buffer(new uint8_t[frame_length_]); EXPECT_EQ(0, ConvertFromI420(orig_frame_, kI420, 0, out_i420_buffer.get())); EXPECT_EQ(0, ConvertToI420(kI420, out_i420_buffer.get(), 0, 0, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/libyuv/scaler_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/libyuv/scaler_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/libyuv/scaler_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/libyuv/scaler_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -99,7 +99,7 @@ kI420, kI420, kScalePoint)); I420VideoFrame test_frame2; - scoped_array orig_buffer(new uint8_t[frame_length_]); + scoped_ptr orig_buffer(new uint8_t[frame_length_]); EXPECT_GT(fread(orig_buffer.get(), 1, frame_length_, source_file_), 0U); test_frame_.CreateFrame(size_y_, orig_buffer.get(), size_uv_, orig_buffer.get() + size_y_, @@ -442,7 +442,7 @@ total_clock = 0; int frame_count = 0; int src_required_size = CalcBufferSize(kI420, src_width, src_height); - scoped_array frame_buffer(new uint8_t[src_required_size]); + scoped_ptr frame_buffer(new uint8_t[src_required_size]); int size_y = src_width * src_height; int size_uv = ((src_width + 1) / 2) * ((src_height + 1) / 2); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -1,4 +1,12 @@ stefan@webrtc.org -mikhal@webrtc.org marpan@webrtc.org henrik.lundin@webrtc.org + +per-file *.isolate=kjellander@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/plane.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/plane.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/plane.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/plane.cc 2015-02-03 14:33:34.000000000 +0000 @@ -20,8 +20,7 @@ static const int kBufferAlignment = 64; Plane::Plane() - : buffer_(NULL), - allocated_size_(0), + : allocated_size_(0), plane_size_(0), stride_(0) {} @@ -42,8 +41,8 @@ return -1; if (new_size <= allocated_size_) return 0; - Allocator::scoped_ptr_aligned new_buffer( - AlignedMalloc(new_size, kBufferAlignment)); + scoped_ptr new_buffer(static_cast( + AlignedMalloc(new_size, kBufferAlignment))); if (buffer_.get()) { memcpy(new_buffer.get(), buffer_.get(), plane_size_); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/plane.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/plane.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/plane.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/plane.h 2015-02-03 14:33:34.000000000 +0000 @@ -12,6 +12,7 @@ #define COMMON_VIDEO_PLANE_H #include "webrtc/system_wrappers/interface/aligned_malloc.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/typedefs.h" namespace webrtc { @@ -63,7 +64,7 @@ // Return value: 0 on success ,-1 on error. int MaybeResize(int new_size); - Allocator::scoped_ptr_aligned buffer_; + scoped_ptr buffer_; int allocated_size_; int plane_size_; int stride_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/texture_video_frame.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/texture_video_frame.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/texture_video_frame.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/texture_video_frame.cc 2015-02-03 14:33:34.000000000 +0000 @@ -12,14 +12,6 @@ #include -#include "webrtc/system_wrappers/interface/trace.h" - -#define NOTREACHED() \ - do { \ - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1, "Not reached"); \ - assert(false); \ - } while (0) - namespace webrtc { TextureVideoFrame::TextureVideoFrame(NativeHandle* handle, @@ -41,7 +33,7 @@ int stride_y, int stride_u, int stride_v) { - NOTREACHED(); + assert(false); // Should not be called. return -1; } @@ -56,46 +48,51 @@ int stride_y, int stride_u, int stride_v) { - NOTREACHED(); + assert(false); // Should not be called. return -1; } int TextureVideoFrame::CopyFrame(const I420VideoFrame& videoFrame) { - NOTREACHED(); + assert(false); // Should not be called. return -1; } +I420VideoFrame* TextureVideoFrame::CloneFrame() const { + return new TextureVideoFrame( + handle_, width(), height(), timestamp(), render_time_ms()); +} + void TextureVideoFrame::SwapFrame(I420VideoFrame* videoFrame) { - NOTREACHED(); + assert(false); // Should not be called. } uint8_t* TextureVideoFrame::buffer(PlaneType type) { - NOTREACHED(); + assert(false); // Should not be called. return NULL; } const uint8_t* TextureVideoFrame::buffer(PlaneType type) const { - NOTREACHED(); + assert(false); // Should not be called. return NULL; } int TextureVideoFrame::allocated_size(PlaneType type) const { - NOTREACHED(); + assert(false); // Should not be called. return -1; } int TextureVideoFrame::stride(PlaneType type) const { - NOTREACHED(); + assert(false); // Should not be called. return -1; } bool TextureVideoFrame::IsZeroSize() const { - NOTREACHED(); + assert(false); // Should not be called. return true; } void TextureVideoFrame::ResetSize() { - NOTREACHED(); + assert(false); // Should not be called. } void* TextureVideoFrame::native_handle() const { return handle_.get(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/texture_video_frame_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/texture_video_frame_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/common_video/texture_video_frame_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/common_video/texture_video_frame_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -8,9 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include "webrtc/common_video/interface/texture_video_frame.h" + #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/common_video/interface/native_handle.h" -#include "webrtc/common_video/interface/texture_video_frame.h" namespace webrtc { @@ -27,6 +28,9 @@ int32_t ref_count_; }; +bool EqualTextureFrames(const I420VideoFrame& frame1, + const I420VideoFrame& frame2); + TEST(TestTextureVideoFrame, InitialValues) { NativeHandleImpl handle; TextureVideoFrame frame(&handle, 640, 480, 100, 10); @@ -55,4 +59,21 @@ EXPECT_EQ(0, handle.ref_count()); } +TEST(TestTextureVideoFrame, CloneFrame) { + NativeHandleImpl handle; + TextureVideoFrame frame1(&handle, 640, 480, 100, 200); + scoped_ptr frame2(frame1.CloneFrame()); + EXPECT_TRUE(frame2.get() != NULL); + EXPECT_TRUE(EqualTextureFrames(frame1, *frame2)); +} + +bool EqualTextureFrames(const I420VideoFrame& frame1, + const I420VideoFrame& frame2) { + return ((frame1.native_handle() == frame2.native_handle()) && + (frame1.width() == frame2.width()) && + (frame1.height() == frame2.height()) && + (frame1.timestamp() == frame2.timestamp()) && + (frame1.render_time_ms() == frame2.render_time_ms())); +} + } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/config.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/config.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/config.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/config.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "webrtc/config.h" + +#include +#include + +namespace webrtc { +std::string FecConfig::ToString() const { + std::stringstream ss; + ss << "{ulpfec_payload_type: " << ulpfec_payload_type; + ss << ", red_payload_type: " << red_payload_type; + ss << '}'; + return ss.str(); +} + +std::string RtpExtension::ToString() const { + std::stringstream ss; + ss << "{name: " << name; + ss << ", id: " << id; + ss << '}'; + return ss.str(); +} + +std::string VideoStream::ToString() const { + std::stringstream ss; + ss << "{width: " << width; + ss << ", height: " << height; + ss << ", max_framerate: " << max_framerate; + ss << ", min_bitrate_bps:" << min_bitrate_bps; + ss << ", target_bitrate_bps:" << target_bitrate_bps; + ss << ", max_bitrate_bps:" << max_bitrate_bps; + ss << ", max_qp: " << max_qp; + + ss << ", temporal_layer_thresholds_bps: ["; + for (size_t i = 0; i < temporal_layer_thresholds_bps.size(); ++i) { + ss << temporal_layer_thresholds_bps[i]; + if (i != temporal_layer_thresholds_bps.size() - 1) + ss << ", "; + } + ss << ']'; + + ss << '}'; + return ss.str(); +} + +std::string VideoEncoderConfig::ToString() const { + std::stringstream ss; + + ss << "{streams: ["; + for (size_t i = 0; i < streams.size(); ++i) { + ss << streams[i].ToString(); + if (i != streams.size() - 1) + ss << ", "; + } + ss << ']'; + ss << ", content_type: "; + switch (content_type) { + case kRealtimeVideo: + ss << "kRealtimeVideo"; + break; + case kScreenshare: + ss << "kScreenshare"; + break; + } + ss << ", encoder_specific_settings: "; + ss << (encoder_specific_settings != NULL ? "(ptr)" : "NULL"); + + ss << ", min_transmit_bitrate_bps: " << min_transmit_bitrate_bps; + ss << '}'; + return ss.str(); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/config.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/config.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/config.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/config.h 2015-02-03 14:33:34.000000000 +0000 @@ -10,8 +10,8 @@ // TODO(pbos): Move Config from common.h to here. -#ifndef WEBRTC_VIDEO_ENGINE_NEW_INCLUDE_CONFIG_H_ -#define WEBRTC_VIDEO_ENGINE_NEW_INCLUDE_CONFIG_H_ +#ifndef WEBRTC_CONFIG_H_ +#define WEBRTC_CONFIG_H_ #include #include @@ -31,14 +31,23 @@ int fraction_loss; int cumulative_loss; int extended_max_sequence_number; - std::string c_name; }; -struct StreamStats { - StreamStats() : key_frames(0), delta_frames(0), bitrate_bps(0) {} +struct SsrcStats { + SsrcStats() + : key_frames(0), + delta_frames(0), + total_bitrate_bps(0), + retransmit_bitrate_bps(0), + avg_delay_ms(0), + max_delay_ms(0) {} uint32_t key_frames; uint32_t delta_frames; - int32_t bitrate_bps; + // TODO(holmer): Move bitrate_bps out to the webrtc::Call layer. + int total_bitrate_bps; + int retransmit_bitrate_bps; + int avg_delay_ms; + int max_delay_ms; StreamDataCounters rtp_stats; RtcpStatistics rtcp_stats; }; @@ -57,6 +66,7 @@ // payload types to '-1' to disable. struct FecConfig { FecConfig() : ulpfec_payload_type(-1), red_payload_type(-1) {} + std::string ToString() const; // Payload type used for ULPFEC packets. int ulpfec_payload_type; @@ -66,13 +76,74 @@ // RTP header extension to use for the video stream, see RFC 5285. struct RtpExtension { + RtpExtension(const std::string& name, int id) : name(name), id(id) {} + std::string ToString() const; + static bool IsSupported(const std::string& name); + static const char* kTOffset; static const char* kAbsSendTime; - RtpExtension(const char* name, int id) : name(name), id(id) {} - // TODO(mflodman) Add API to query supported extensions. std::string name; int id; }; + +struct VideoStream { + VideoStream() + : width(0), + height(0), + max_framerate(-1), + min_bitrate_bps(-1), + target_bitrate_bps(-1), + max_bitrate_bps(-1), + max_qp(-1) {} + std::string ToString() const; + + size_t width; + size_t height; + int max_framerate; + + int min_bitrate_bps; + int target_bitrate_bps; + int max_bitrate_bps; + + int max_qp; + + // Bitrate thresholds for enabling additional temporal layers. Since these are + // thresholds in between layers, we have one additional layer. One threshold + // gives two temporal layers, one below the threshold and one above, two give + // three, and so on. + // The VideoEncoder may redistribute bitrates over the temporal layers so a + // bitrate threshold of 100k and an estimate of 105k does not imply that we + // get 100k in one temporal layer and 5k in the other, just that the bitrate + // in the first temporal layer should not exceed 100k. + // TODO(pbos): Apart from a special case for two-layer screencast these + // thresholds are not propagated to the VideoEncoder. To be implemented. + std::vector temporal_layer_thresholds_bps; +}; + +struct VideoEncoderConfig { + enum ContentType { + kRealtimeVideo, + kScreenshare, + }; + + VideoEncoderConfig() + : content_type(kRealtimeVideo), + encoder_specific_settings(NULL), + min_transmit_bitrate_bps(0) {} + + std::string ToString() const; + + std::vector streams; + ContentType content_type; + void* encoder_specific_settings; + + // Padding will be used up to this bitrate regardless of the bitrate produced + // by the encoder. Padding above what's actually produced by the encoder helps + // maintaining a higher bitrate estimate. Padding will however not be sent + // unless the estimated bandwidth indicates that the link can handle it. + int min_transmit_bitrate_bps; +}; + } // namespace webrtc -#endif // WEBRTC_VIDEO_ENGINE_NEW_INCLUDE_CONFIG_H_ +#endif // WEBRTC_CONFIG_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/engine_configurations.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/engine_configurations.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/engine_configurations.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/engine_configurations.h 2015-02-03 14:33:34.000000000 +0000 @@ -21,13 +21,14 @@ // [Voice] Codec settings // ---------------------------------------------------------------------------- -// iSAC is not included in the Mozilla build, but in all other builds. +// iSAC and G722 are not included in the Mozilla build, but in all other builds. #ifndef WEBRTC_MOZILLA_BUILD #ifdef WEBRTC_ARCH_ARM #define WEBRTC_CODEC_ISACFX // Fix-point iSAC implementation. #else #define WEBRTC_CODEC_ISAC // Floating-point iSAC implementation (default). #endif // WEBRTC_ARCH_ARM +#define WEBRTC_CODEC_G722 #endif // !WEBRTC_MOZILLA_BUILD // AVT is included in all builds, along with G.711, NetEQ and CNG @@ -39,11 +40,10 @@ #define WEBRTC_CODEC_PCM16 #endif -// iLBC, G.722, and Redundancy coding are excluded from Chromium and Mozilla +// iLBC and Redundancy coding are excluded from Chromium and Mozilla // builds to reduce binary size. #if !defined(WEBRTC_CHROMIUM_BUILD) && !defined(WEBRTC_MOZILLA_BUILD) #define WEBRTC_CODEC_ILBC -#define WEBRTC_CODEC_G722 #define WEBRTC_CODEC_RED #endif // !WEBRTC_CHROMIUM_BUILD && !WEBRTC_MOZILLA_BUILD @@ -53,6 +53,8 @@ #define VIDEOCODEC_I420 #define VIDEOCODEC_VP8 +#define VIDEOCODEC_VP9 +#define VIDEOCODEC_H264 // ============================================================================ // VoiceEngine @@ -86,11 +88,6 @@ #define WEBRTC_VOICE_ENGINE_VIDEO_SYNC_API #define WEBRTC_VOICE_ENGINE_VOLUME_CONTROL_API -#ifndef WEBRTC_CHROMIUM_BUILD -#define WEBRTC_VOICE_ENGINE_CALL_REPORT_API -#define WEBRTC_VOICE_ENGINE_ENCRYPTION_API -#endif - // ============================================================================ // VideoEngine // ============================================================================ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/experiments.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/experiments.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/experiments.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/experiments.h 2015-02-03 14:33:34.000000000 +0000 @@ -11,17 +11,9 @@ #ifndef WEBRTC_EXPERIMENTS_H_ #define WEBRTC_EXPERIMENTS_H_ -namespace webrtc { -struct PaddingStrategy { - PaddingStrategy() - : redundant_payloads(false) {} - explicit PaddingStrategy(bool redundant_payloads) - : redundant_payloads(redundant_payloads) {} - virtual ~PaddingStrategy() {} - - const bool redundant_payloads; -}; +#include "webrtc/typedefs.h" +namespace webrtc { struct RemoteBitrateEstimatorMinRate { RemoteBitrateEstimatorMinRate() : min_rate(30000) {} RemoteBitrateEstimatorMinRate(uint32_t min_rate) : min_rate(min_rate) {} @@ -29,11 +21,11 @@ uint32_t min_rate; }; -struct SkipEncodingUnusedStreams { - SkipEncodingUnusedStreams() : enabled(false) {} - explicit SkipEncodingUnusedStreams(bool set_enabled) +struct AimdRemoteRateControl { + AimdRemoteRateControl() : enabled(false) {} + explicit AimdRemoteRateControl(bool set_enabled) : enabled(set_enabled) {} - virtual ~SkipEncodingUnusedStreams() {} + virtual ~AimdRemoteRateControl() {} const bool enabled; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/BUILD.gn 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,733 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("//build/config/arm.gni") +import("../../build/webrtc.gni") + +config("audio_coding_config") { + include_dirs = [ + "main/interface", + "../interface", + ] +} + +source_set("audio_coding") { + sources = [ + "main/acm2/acm_amr.cc", + "main/acm2/acm_amr.h", + "main/acm2/acm_amrwb.cc", + "main/acm2/acm_amrwb.h", + "main/acm2/acm_celt.cc", + "main/acm2/acm_celt.h", + "main/acm2/acm_cng.cc", + "main/acm2/acm_cng.h", + "main/acm2/acm_codec_database.cc", + "main/acm2/acm_codec_database.h", + "main/acm2/acm_common_defs.h", + "main/acm2/acm_dtmf_playout.cc", + "main/acm2/acm_dtmf_playout.h", + "main/acm2/acm_g722.cc", + "main/acm2/acm_g722.h", + "main/acm2/acm_g7221.cc", + "main/acm2/acm_g7221.h", + "main/acm2/acm_g7221c.cc", + "main/acm2/acm_g7221c.h", + "main/acm2/acm_g729.cc", + "main/acm2/acm_g729.h", + "main/acm2/acm_g7291.cc", + "main/acm2/acm_g7291.h", + "main/acm2/acm_generic_codec.cc", + "main/acm2/acm_generic_codec.h", + "main/acm2/acm_gsmfr.cc", + "main/acm2/acm_gsmfr.h", + "main/acm2/acm_ilbc.cc", + "main/acm2/acm_ilbc.h", + "main/acm2/acm_isac.cc", + "main/acm2/acm_isac.h", + "main/acm2/acm_isac_macros.h", + "main/acm2/acm_opus.cc", + "main/acm2/acm_opus.h", + "main/acm2/acm_speex.cc", + "main/acm2/acm_speex.h", + "main/acm2/acm_pcm16b.cc", + "main/acm2/acm_pcm16b.h", + "main/acm2/acm_pcma.cc", + "main/acm2/acm_pcma.h", + "main/acm2/acm_pcmu.cc", + "main/acm2/acm_pcmu.h", + "main/acm2/acm_red.cc", + "main/acm2/acm_red.h", + "main/acm2/acm_receiver.cc", + "main/acm2/acm_receiver.h", + "main/acm2/acm_resampler.cc", + "main/acm2/acm_resampler.h", + "main/acm2/audio_coding_module.cc", + "main/acm2/audio_coding_module_impl.cc", + "main/acm2/audio_coding_module_impl.h", + "main/acm2/call_statistics.cc", + "main/acm2/call_statistics.h", + "main/acm2/initial_delay_manager.cc", + "main/acm2/initial_delay_manager.h", + "main/acm2/nack.cc", + "main/acm2/nack.h", + "main/interface/audio_coding_module.h", + "main/interface/audio_coding_module_typedefs.h", + ] + + defines = [] + + configs += [ "../..:common_config" ] + + public_configs = [ + "../..:common_inherited_config", + ":audio_coding_config", + ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ + ":cng", + ":g711", + ":g722", + ":ilbc", + ":isac", + ":isacfix", + ":neteq", + ":pcm16b", + "../../common_audio", + "../../system_wrappers", + ] + + if (rtc_include_opus) { + defines += [ "WEBRTC_CODEC_OPUS" ] + deps += [ ":webrtc_opus" ] + } +} + +config("cng_config") { + include_dirs = [ + "../../..", + "codecs/cng/include", + ] +} + +source_set("cng") { + sources = [ + "codecs/cng/cng_helpfuns.c", + "codecs/cng/cng_helpfuns.h", + "codecs/cng/include/webrtc_cng.h", + "codecs/cng/webrtc_cng.c", + ] + + configs += [ "../..:common_config" ] + + public_configs = [ + "../..:common_inherited_config", + ":cng_config", + ] + + deps = [ "../../common_audio" ] +} + +config("g711_config") { + include_dirs = [ + "../../..", + "codecs/g711/include", + ] +} + +source_set("g711") { + sources = [ + "codecs/g711/include/audio_encoder_pcm.h", + "codecs/g711/include/g711_interface.h", + "codecs/g711/audio_encoder_pcm.cc", + "codecs/g711/g711_interface.c", + "codecs/g711/g711.c", + "codecs/g711/g711.h", + ] + + configs += [ "../..:common_config" ] + + public_configs = [ + "../..:common_inherited_config", + ":g711_config", + ] +} + +config("g722_config") { + include_dirs = [ + "../../..", + "codecs/g722/include", + ] +} + +source_set("g722") { + sources = [ + "codecs/g722/include/g722_interface.h", + "codecs/g722/g722_interface.c", + "codecs/g722/g722_encode.c", + "codecs/g722/g722_decode.c", + "codecs/g722/g722_enc_dec.h", + ] + + configs += [ "../..:common_config" ] + + public_configs = [ + "../..:common_inherited_config", + ":g722_config", + ] +} + +config("ilbc_config") { + include_dirs = [ + "../../..", + "codecs/ilbc/interface", + ] +} + +source_set("ilbc") { + sources = [ + "codecs/ilbc/abs_quant.c", + "codecs/ilbc/abs_quant.h", + "codecs/ilbc/abs_quant_loop.c", + "codecs/ilbc/abs_quant_loop.h", + "codecs/ilbc/augmented_cb_corr.c", + "codecs/ilbc/augmented_cb_corr.h", + "codecs/ilbc/bw_expand.c", + "codecs/ilbc/bw_expand.h", + "codecs/ilbc/cb_construct.c", + "codecs/ilbc/cb_construct.h", + "codecs/ilbc/cb_mem_energy_augmentation.c", + "codecs/ilbc/cb_mem_energy_augmentation.h", + "codecs/ilbc/cb_mem_energy.c", + "codecs/ilbc/cb_mem_energy_calc.c", + "codecs/ilbc/cb_mem_energy_calc.h", + "codecs/ilbc/cb_mem_energy.h", + "codecs/ilbc/cb_search.c", + "codecs/ilbc/cb_search_core.c", + "codecs/ilbc/cb_search_core.h", + "codecs/ilbc/cb_search.h", + "codecs/ilbc/cb_update_best_index.c", + "codecs/ilbc/cb_update_best_index.h", + "codecs/ilbc/chebyshev.c", + "codecs/ilbc/chebyshev.h", + "codecs/ilbc/comp_corr.c", + "codecs/ilbc/comp_corr.h", + "codecs/ilbc/constants.c", + "codecs/ilbc/constants.h", + "codecs/ilbc/create_augmented_vec.c", + "codecs/ilbc/create_augmented_vec.h", + "codecs/ilbc/decode.c", + "codecs/ilbc/decode.h", + "codecs/ilbc/decode_residual.c", + "codecs/ilbc/decode_residual.h", + "codecs/ilbc/decoder_interpolate_lsf.c", + "codecs/ilbc/decoder_interpolate_lsf.h", + "codecs/ilbc/defines.h", + "codecs/ilbc/do_plc.c", + "codecs/ilbc/do_plc.h", + "codecs/ilbc/encode.c", + "codecs/ilbc/encode.h", + "codecs/ilbc/energy_inverse.c", + "codecs/ilbc/energy_inverse.h", + "codecs/ilbc/enhancer.c", + "codecs/ilbc/enhancer.h", + "codecs/ilbc/enhancer_interface.c", + "codecs/ilbc/enhancer_interface.h", + "codecs/ilbc/enh_upsample.c", + "codecs/ilbc/enh_upsample.h", + "codecs/ilbc/filtered_cb_vecs.c", + "codecs/ilbc/filtered_cb_vecs.h", + "codecs/ilbc/frame_classify.c", + "codecs/ilbc/frame_classify.h", + "codecs/ilbc/gain_dequant.c", + "codecs/ilbc/gain_dequant.h", + "codecs/ilbc/gain_quant.c", + "codecs/ilbc/gain_quant.h", + "codecs/ilbc/get_cd_vec.c", + "codecs/ilbc/get_cd_vec.h", + "codecs/ilbc/get_lsp_poly.c", + "codecs/ilbc/get_lsp_poly.h", + "codecs/ilbc/get_sync_seq.c", + "codecs/ilbc/get_sync_seq.h", + "codecs/ilbc/hp_input.c", + "codecs/ilbc/hp_input.h", + "codecs/ilbc/hp_output.c", + "codecs/ilbc/hp_output.h", + "codecs/ilbc/ilbc.c", + "codecs/ilbc/index_conv_dec.c", + "codecs/ilbc/index_conv_dec.h", + "codecs/ilbc/index_conv_enc.c", + "codecs/ilbc/index_conv_enc.h", + "codecs/ilbc/init_decode.c", + "codecs/ilbc/init_decode.h", + "codecs/ilbc/init_encode.c", + "codecs/ilbc/init_encode.h", + "codecs/ilbc/interface/ilbc.h", + "codecs/ilbc/interpolate.c", + "codecs/ilbc/interpolate.h", + "codecs/ilbc/interpolate_samples.c", + "codecs/ilbc/interpolate_samples.h", + "codecs/ilbc/lpc_encode.c", + "codecs/ilbc/lpc_encode.h", + "codecs/ilbc/lsf_check.c", + "codecs/ilbc/lsf_check.h", + "codecs/ilbc/lsf_interpolate_to_poly_dec.c", + "codecs/ilbc/lsf_interpolate_to_poly_dec.h", + "codecs/ilbc/lsf_interpolate_to_poly_enc.c", + "codecs/ilbc/lsf_interpolate_to_poly_enc.h", + "codecs/ilbc/lsf_to_lsp.c", + "codecs/ilbc/lsf_to_lsp.h", + "codecs/ilbc/lsf_to_poly.c", + "codecs/ilbc/lsf_to_poly.h", + "codecs/ilbc/lsp_to_lsf.c", + "codecs/ilbc/lsp_to_lsf.h", + "codecs/ilbc/my_corr.c", + "codecs/ilbc/my_corr.h", + "codecs/ilbc/nearest_neighbor.c", + "codecs/ilbc/nearest_neighbor.h", + "codecs/ilbc/pack_bits.c", + "codecs/ilbc/pack_bits.h", + "codecs/ilbc/poly_to_lsf.c", + "codecs/ilbc/poly_to_lsf.h", + "codecs/ilbc/poly_to_lsp.c", + "codecs/ilbc/poly_to_lsp.h", + "codecs/ilbc/refiner.c", + "codecs/ilbc/refiner.h", + "codecs/ilbc/simple_interpolate_lsf.c", + "codecs/ilbc/simple_interpolate_lsf.h", + "codecs/ilbc/simple_lpc_analysis.c", + "codecs/ilbc/simple_lpc_analysis.h", + "codecs/ilbc/simple_lsf_dequant.c", + "codecs/ilbc/simple_lsf_dequant.h", + "codecs/ilbc/simple_lsf_quant.c", + "codecs/ilbc/simple_lsf_quant.h", + "codecs/ilbc/smooth.c", + "codecs/ilbc/smooth.h", + "codecs/ilbc/smooth_out_data.c", + "codecs/ilbc/smooth_out_data.h", + "codecs/ilbc/sort_sq.c", + "codecs/ilbc/sort_sq.h", + "codecs/ilbc/split_vq.c", + "codecs/ilbc/split_vq.h", + "codecs/ilbc/state_construct.c", + "codecs/ilbc/state_construct.h", + "codecs/ilbc/state_search.c", + "codecs/ilbc/state_search.h", + "codecs/ilbc/swap_bytes.c", + "codecs/ilbc/swap_bytes.h", + "codecs/ilbc/unpack_bits.c", + "codecs/ilbc/unpack_bits.h", + "codecs/ilbc/vq3.c", + "codecs/ilbc/vq3.h", + "codecs/ilbc/vq4.c", + "codecs/ilbc/vq4.h", + "codecs/ilbc/window32_w32.c", + "codecs/ilbc/window32_w32.h", + "codecs/ilbc/xcorr_coef.c", + "codecs/ilbc/xcorr_coef.h", + ] + + configs += [ "../..:common_config" ] + + public_configs = [ + "../..:common_inherited_config", + ":ilbc_config", + ] + + deps = [ "../../common_audio" ] +} + +config("isac_config") { + include_dirs = [ + "../../..", + "codecs/isac/main/interface", + ] +} + +source_set("isac") { + sources = [ + "codecs/isac/main/interface/isac.h", + "codecs/isac/main/source/arith_routines.c", + "codecs/isac/main/source/arith_routines.h", + "codecs/isac/main/source/arith_routines_hist.c", + "codecs/isac/main/source/arith_routines_logist.c", + "codecs/isac/main/source/bandwidth_estimator.c", + "codecs/isac/main/source/bandwidth_estimator.h", + "codecs/isac/main/source/codec.h", + "codecs/isac/main/source/crc.c", + "codecs/isac/main/source/crc.h", + "codecs/isac/main/source/decode_bwe.c", + "codecs/isac/main/source/decode.c", + "codecs/isac/main/source/encode.c", + "codecs/isac/main/source/encode_lpc_swb.c", + "codecs/isac/main/source/encode_lpc_swb.h", + "codecs/isac/main/source/entropy_coding.c", + "codecs/isac/main/source/entropy_coding.h", + "codecs/isac/main/source/fft.c", + "codecs/isac/main/source/fft.h", + "codecs/isac/main/source/filterbanks.c", + "codecs/isac/main/source/filterbank_tables.c", + "codecs/isac/main/source/filterbank_tables.h", + "codecs/isac/main/source/filter_functions.c", + "codecs/isac/main/source/intialize.c", + "codecs/isac/main/source/isac.c", + "codecs/isac/main/source/lattice.c", + "codecs/isac/main/source/lpc_analysis.c", + "codecs/isac/main/source/lpc_analysis.h", + "codecs/isac/main/source/lpc_gain_swb_tables.c", + "codecs/isac/main/source/lpc_gain_swb_tables.h", + "codecs/isac/main/source/lpc_shape_swb12_tables.c", + "codecs/isac/main/source/lpc_shape_swb12_tables.h", + "codecs/isac/main/source/lpc_shape_swb16_tables.c", + "codecs/isac/main/source/lpc_shape_swb16_tables.h", + "codecs/isac/main/source/lpc_tables.c", + "codecs/isac/main/source/lpc_tables.h", + "codecs/isac/main/source/os_specific_inline.h", + "codecs/isac/main/source/pitch_estimator.c", + "codecs/isac/main/source/pitch_estimator.h", + "codecs/isac/main/source/pitch_filter.c", + "codecs/isac/main/source/pitch_gain_tables.c", + "codecs/isac/main/source/pitch_gain_tables.h", + "codecs/isac/main/source/pitch_lag_tables.c", + "codecs/isac/main/source/pitch_lag_tables.h", + "codecs/isac/main/source/settings.h", + "codecs/isac/main/source/spectrum_ar_model_tables.c", + "codecs/isac/main/source/spectrum_ar_model_tables.h", + "codecs/isac/main/source/structs.h", + "codecs/isac/main/source/transform.c", + ] + + if (is_linux) { + libs = [ "m" ] + } + + configs += [ "../..:common_config" ] + + public_configs = [ + "../..:common_inherited_config", + ":isac_config", + ] + + deps = [ "../../common_audio" ] +} + +config("isac_fix_config") { + include_dirs = [ + "../../..", + "codecs/isac/fix/interface", + ] +} + +source_set("isacfix") { + sources = [ + "codecs/isac/fix/interface/isacfix.h", + "codecs/isac/fix/source/arith_routines.c", + "codecs/isac/fix/source/arith_routines_hist.c", + "codecs/isac/fix/source/arith_routines_logist.c", + "codecs/isac/fix/source/arith_routins.h", + "codecs/isac/fix/source/bandwidth_estimator.c", + "codecs/isac/fix/source/bandwidth_estimator.h", + "codecs/isac/fix/source/codec.h", + "codecs/isac/fix/source/decode_bwe.c", + "codecs/isac/fix/source/decode.c", + "codecs/isac/fix/source/decode_plc.c", + "codecs/isac/fix/source/encode.c", + "codecs/isac/fix/source/entropy_coding.c", + "codecs/isac/fix/source/entropy_coding.h", + "codecs/isac/fix/source/fft.c", + "codecs/isac/fix/source/fft.h", + "codecs/isac/fix/source/filterbanks.c", + "codecs/isac/fix/source/filterbank_tables.c", + "codecs/isac/fix/source/filterbank_tables.h", + "codecs/isac/fix/source/filters.c", + "codecs/isac/fix/source/initialize.c", + "codecs/isac/fix/source/isacfix.c", + "codecs/isac/fix/source/lattice.c", + "codecs/isac/fix/source/lpc_masking_model.c", + "codecs/isac/fix/source/lpc_masking_model.h", + "codecs/isac/fix/source/lpc_tables.c", + "codecs/isac/fix/source/lpc_tables.h", + "codecs/isac/fix/source/pitch_estimator.c", + "codecs/isac/fix/source/pitch_estimator.h", + "codecs/isac/fix/source/pitch_filter.c", + "codecs/isac/fix/source/pitch_gain_tables.c", + "codecs/isac/fix/source/pitch_gain_tables.h", + "codecs/isac/fix/source/pitch_lag_tables.c", + "codecs/isac/fix/source/pitch_lag_tables.h", + "codecs/isac/fix/source/settings.h", + "codecs/isac/fix/source/spectrum_ar_model_tables.c", + "codecs/isac/fix/source/spectrum_ar_model_tables.h", + "codecs/isac/fix/source/structs.h", + "codecs/isac/fix/source/transform.c", + "codecs/isac/fix/source/transform_tables.c", + ] + + if (!is_win) { + defines = [ "WEBRTC_LINUX" ] + } + + configs += [ "../..:common_config" ] + + public_configs = [ + "../..:common_inherited_config", + ":isac_fix_config", + ] + + deps = [ + "../../common_audio", + "../../system_wrappers", + ] + + if (rtc_build_armv7_neon) { + deps += [ ":isac_neon" ] + + # Enable compilation for the ARM v7 Neon instruction set. This is needed + # since //build/config/arm.gni only enables Neon for iOS, not Android. + # This provides the same functionality as webrtc/build/arm_neon.gypi. + # TODO(kjellander): Investigate if this can be moved into webrtc.gni or + # //build/config/arm.gni instead, to reduce code duplication. + # Remove the -mfpu=vfpv3-d16 cflag. + configs -= [ "//build/config/compiler:compiler_arm_fpu" ] + cflags = [ + "-flax-vector-conversions", + "-mfpu=neon", + ] + + sources += [ + "codecs/isac/fix/source/lattice_armv7.S", + "codecs/isac/fix/source/pitch_filter_armv6.S", + ] + } else { + sources += [ "codecs/isac/fix/source/pitch_filter_c.c" ] + } + + if (cpu_arch == "mipsel") { + sources += [ + "codecs/isac/fix/source/entropy_coding_mips.c", + "codecs/isac/fix/source/filters_mips.c", + "codecs/isac/fix/source/lattice_mips.c", + "codecs/isac/fix/source/pitch_estimator_mips.c", + "codecs/isac/fix/source/transform_mips.c", + ] + if (mips_dsp_rev > 0) { + sources += [ "codecs/isac/fix/source/filterbanks_mips.c" ] + } + if (mips_dsp_rev > 1) { + sources += [ + "codecs/isac/fix/source/lpc_masking_model_mips.c", + "codecs/isac/fix/source/pitch_filter_mips.c", + ] + } else { + sources += [ "codecs/isac/fix/source/pitch_filter_c.c" ] + } + } else { + sources += [ "codecs/isac/fix/source/pitch_estimator_c.c" ] + } + + if (!rtc_build_armv7_neon && cpu_arch != "mipsel") { + sources += [ "codecs/isac/fix/source/lattice_c.c" ] + } +} + +if (rtc_build_armv7_neon) { + source_set("isac_neon") { + sources = [ + "codecs/isac/fix/source/entropy_coding_neon.c", + "codecs/isac/fix/source/filterbanks_neon.S", + "codecs/isac/fix/source/filters_neon.S", + "codecs/isac/fix/source/lattice_neon.S", + "codecs/isac/fix/source/lpc_masking_model_neon.S", + "codecs/isac/fix/source/transform_neon.S", + ] + + include_dirs = [ + "../../..", + ] + + # Disable LTO in audio_processing_neon target due to compiler bug. + if (rtc_use_lto) { + cflags -= [ + "-flto", + "-ffat-lto-objects", + ] + } + + # Enable compilation for the ARM v7 Neon instruction set. This is needed + # since //build/config/arm.gni only enables Neon for iOS, not Android. + # This provides the same functionality as webrtc/build/arm_neon.gypi. + # TODO(kjellander): Investigate if this can be moved into webrtc.gni or + # //build/config/arm.gni instead, to reduce code duplication. + # Remove the -mfpu=vfpv3-d16 cflag. + configs -= [ "//build/config/compiler:compiler_arm_fpu" ] + cflags = [ + "-flax-vector-conversions", + "-mfpu=neon", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + deps = [ "../../common_audio" ] + } +} + +config("pcm16b_config") { + include_dirs = [ + "../../..", + "codecs/pcm16b/include", + ] +} + +source_set("pcm16b") { + sources = [ + "codecs/pcm16b/include/pcm16b.h", + "codecs/pcm16b/pcm16b.c", + ] + + configs += [ "../..:common_config" ] + + public_configs = [ + "../..:common_inherited_config", + ":pcm16b_config", + ] +} + +config("opus_config") { + include_dirs = [ "../../.." ] +} + +source_set("webrtc_opus") { + sources = [ + "codecs/opus/audio_encoder_opus.cc", + "codecs/opus/interface/audio_encoder_opus.h", + "codecs/opus/interface/opus_interface.h", + "codecs/opus/opus_inst.h", + "codecs/opus/opus_interface.c", + ] + if (build_with_mozilla) { + include_dirs = [ getenv("DIST") + "/include/opus" ] + } else { + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + deps = [ "//third_party/opus" ] + } +} + +config("neteq_config") { + include_dirs = [ + # Need Opus header files for the audio classifier. + "//third_party/opus/src/celt", + "//third_party/opus/src/src", + ] +} + +source_set("neteq") { + sources = [ + "neteq/interface/audio_decoder.h", + "neteq/interface/neteq.h", + "neteq/accelerate.cc", + "neteq/accelerate.h", + "neteq/audio_classifier.cc", + "neteq/audio_classifier.h", + "neteq/audio_decoder_impl.cc", + "neteq/audio_decoder_impl.h", + "neteq/audio_decoder.cc", + "neteq/audio_multi_vector.cc", + "neteq/audio_multi_vector.h", + "neteq/audio_vector.cc", + "neteq/audio_vector.h", + "neteq/background_noise.cc", + "neteq/background_noise.h", + "neteq/buffer_level_filter.cc", + "neteq/buffer_level_filter.h", + "neteq/comfort_noise.cc", + "neteq/comfort_noise.h", + "neteq/decision_logic.cc", + "neteq/decision_logic.h", + "neteq/decision_logic_fax.cc", + "neteq/decision_logic_fax.h", + "neteq/decision_logic_normal.cc", + "neteq/decision_logic_normal.h", + "neteq/decoder_database.cc", + "neteq/decoder_database.h", + "neteq/defines.h", + "neteq/delay_manager.cc", + "neteq/delay_manager.h", + "neteq/delay_peak_detector.cc", + "neteq/delay_peak_detector.h", + "neteq/dsp_helper.cc", + "neteq/dsp_helper.h", + "neteq/dtmf_buffer.cc", + "neteq/dtmf_buffer.h", + "neteq/dtmf_tone_generator.cc", + "neteq/dtmf_tone_generator.h", + "neteq/expand.cc", + "neteq/expand.h", + "neteq/merge.cc", + "neteq/merge.h", + "neteq/neteq_impl.cc", + "neteq/neteq_impl.h", + "neteq/neteq.cc", + "neteq/statistics_calculator.cc", + "neteq/statistics_calculator.h", + "neteq/normal.cc", + "neteq/normal.h", + "neteq/packet_buffer.cc", + "neteq/packet_buffer.h", + "neteq/payload_splitter.cc", + "neteq/payload_splitter.h", + "neteq/post_decode_vad.cc", + "neteq/post_decode_vad.h", + "neteq/preemptive_expand.cc", + "neteq/preemptive_expand.h", + "neteq/random_vector.cc", + "neteq/random_vector.h", + "neteq/rtcp.cc", + "neteq/rtcp.h", + "neteq/sync_buffer.cc", + "neteq/sync_buffer.h", + "neteq/timestamp_scaler.cc", + "neteq/timestamp_scaler.h", + "neteq/time_stretch.cc", + "neteq/time_stretch.h", + ] + + configs += [ "../..:common_config" ] + + public_configs = [ + "../..:common_inherited_config", + ":neteq_config", + ] + + forward_dependent_configs_from = [ "//third_party/opus" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ + ":cng", + ":g711", + ":g722", + ":ilbc", + ":isac", + ":isacfix", + ":pcm16b", + "../../common_audio", + "../../system_wrappers", + "//third_party/opus", + ] +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/audio_encoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/audio_encoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/audio_encoder.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/audio_encoder.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_AUDIO_ENCODER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_CODECS_AUDIO_ENCODER_H_ + +#include + +#include "webrtc/base/checks.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// This is the interface class for encoders in AudioCoding module. Each codec +// codec type must have an implementation of this class. +class AudioEncoder { + public: + virtual ~AudioEncoder() {} + + // Accepts one 10 ms block of input audio (i.e., sample_rate_hz() / 100 * + // num_channels() samples). Multi-channel audio must be sample-interleaved. + // If successful, the encoder produces zero or more bytes of output in + // |encoded|, and provides the number of encoded bytes in |encoded_bytes|. + // In case of error, false is returned, otherwise true. It is an error for the + // encoder to attempt to produce more than |max_encoded_bytes| bytes of + // output. + bool Encode(uint32_t timestamp, + const int16_t* audio, + size_t num_samples_per_channel, + size_t max_encoded_bytes, + uint8_t* encoded, + size_t* encoded_bytes, + uint32_t* encoded_timestamp) { + CHECK_EQ(num_samples_per_channel, + static_cast(sample_rate_hz() / 100)); + bool ret = Encode(timestamp, + audio, + max_encoded_bytes, + encoded, + encoded_bytes, + encoded_timestamp); + CHECK_LE(*encoded_bytes, max_encoded_bytes); + return ret; + } + + // Return the input sample rate in Hz and the number of input channels. + // These are constants set at instantiation time. + virtual int sample_rate_hz() const = 0; + virtual int num_channels() const = 0; + + // Returns the number of 10 ms frames the encoder will put in the next + // packet. This value may only change when Encode() outputs a packet; i.e., + // the encoder may vary the number of 10 ms frames from packet to packet, but + // it must decide the length of the next packet no later than when outputting + // the preceding packet. + virtual int Num10MsFramesInNextPacket() const = 0; + + protected: + virtual bool Encode(uint32_t timestamp, + const int16_t* audio, + size_t max_encoded_bytes, + uint8_t* encoded, + size_t* encoded_bytes, + uint32_t* encoded_timestamp) = 0; +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_AUDIO_ENCODER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_cng -LOCAL_MODULE_TAGS := optional -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := \ - webrtc_cng.c \ - cng_helpfuns.c - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/../../../../common_audio/signal_processing/include - -LOCAL_SHARED_LIBRARIES := \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/cng_helpfuns.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/cng_helpfuns.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/cng_helpfuns.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/cng_helpfuns.c 2015-02-03 14:33:34.000000000 +0000 @@ -11,7 +11,7 @@ #include "cng_helpfuns.h" #include "signal_processing_library.h" -#include "typedefs.h" +#include "webrtc/typedefs.h" #include "webrtc_cng.h" /* Values in |k| are Q15, and |a| Q12. */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/cng_helpfuns.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/cng_helpfuns.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/cng_helpfuns.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/cng_helpfuns.h 2015-02-03 14:33:34.000000000 +0000 @@ -10,7 +10,7 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_CNG_CNG_HELPFUNS_H_ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_CNG_CNG_HELPFUNS_H_ -#include "typedefs.h" +#include "webrtc/typedefs.h" #ifdef __cplusplus extern "C" { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/cng_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/cng_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/cng_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/cng_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -9,7 +9,7 @@ */ #include -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/test/testsupport/fileutils.h" #include "webrtc_cng.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h 2015-02-03 14:33:34.000000000 +0000 @@ -12,7 +12,7 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_CNG_MAIN_INTERFACE_WEBRTC_CNG_H_ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_CNG_MAIN_INTERFACE_WEBRTC_CNG_H_ -#include "typedefs.h" +#include "webrtc/typedefs.h" #ifdef __cplusplus extern "C" { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_g711 -LOCAL_MODULE_TAGS := optional -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := \ - g711_interface.c \ - g711.c - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../../../.. - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/audio_encoder_pcm.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/audio_encoder_pcm.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/audio_encoder_pcm.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/audio_encoder_pcm.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/codecs/g711/include/audio_encoder_pcm.h" + +#include + +#include "webrtc/modules/audio_coding/codecs/g711/include/g711_interface.h" + +namespace webrtc { + +namespace { +int16_t NumSamplesPerFrame(int num_channels, + int frame_size_ms, + int sample_rate_hz) { + int samples_per_frame = num_channels * frame_size_ms * sample_rate_hz / 1000; + CHECK_LE(samples_per_frame, std::numeric_limits::max()) + << "Frame size too large."; + return static_cast(samples_per_frame); +} +} // namespace + +AudioEncoderPcm::AudioEncoderPcm(const Config& config) + : num_channels_(config.num_channels), + num_10ms_frames_per_packet_(config.frame_size_ms / 10), + full_frame_samples_(NumSamplesPerFrame(num_channels_, + config.frame_size_ms, + kSampleRateHz)), + first_timestamp_in_buffer_(0) { + CHECK_EQ(config.frame_size_ms % 10, 0) + << "Frame size must be an integer multiple of 10 ms."; + speech_buffer_.reserve(full_frame_samples_); +} + +AudioEncoderPcm::~AudioEncoderPcm() { +} + +int AudioEncoderPcm::sample_rate_hz() const { + return kSampleRateHz; +} +int AudioEncoderPcm::num_channels() const { + return num_channels_; +} +int AudioEncoderPcm::Num10MsFramesInNextPacket() const { + return num_10ms_frames_per_packet_; +} + +bool AudioEncoderPcm::Encode(uint32_t timestamp, + const int16_t* audio, + size_t max_encoded_bytes, + uint8_t* encoded, + size_t* encoded_bytes, + uint32_t* encoded_timestamp) { + const int num_samples = sample_rate_hz() / 100 * num_channels(); + if (speech_buffer_.empty()) { + first_timestamp_in_buffer_ = timestamp; + } + for (int i = 0; i < num_samples; ++i) { + speech_buffer_.push_back(audio[i]); + } + if (speech_buffer_.size() < static_cast(full_frame_samples_)) { + *encoded_bytes = 0; + return true; + } + CHECK_EQ(speech_buffer_.size(), static_cast(full_frame_samples_)); + int16_t ret = EncodeCall(&speech_buffer_[0], full_frame_samples_, encoded); + speech_buffer_.clear(); + *encoded_timestamp = first_timestamp_in_buffer_; + if (ret < 0) + return false; + *encoded_bytes = static_cast(ret); + return true; +} + +int16_t AudioEncoderPcmA::EncodeCall(const int16_t* audio, + size_t input_len, + uint8_t* encoded) { + return WebRtcG711_EncodeA(const_cast(audio), + static_cast(input_len), + reinterpret_cast(encoded)); +} + +int16_t AudioEncoderPcmU::EncodeCall(const int16_t* audio, + size_t input_len, + uint8_t* encoded) { + return WebRtcG711_EncodeU(const_cast(audio), + static_cast(input_len), + reinterpret_cast(encoded)); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/g711.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/g711.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/g711.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/g711.c 2015-02-03 14:33:34.000000000 +0000 @@ -21,7 +21,7 @@ */ #include "g711.h" -#include "typedefs.h" +#include "webrtc/typedefs.h" /* Copied from the CCITT G.711 specification */ static const uint8_t ulaw_to_alaw_table[256] = { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/g711.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/g711.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/g711.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/g711.gypi 2015-02-03 14:33:34.000000000 +0000 @@ -23,9 +23,11 @@ }, 'sources': [ 'include/g711_interface.h', + 'include/audio_encoder_pcm.h', 'g711_interface.c', 'g711.c', 'g711.h', + 'audio_encoder_pcm.cc', ], }, ], # targets diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/g711.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/g711.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/g711.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/g711.h 2015-02-03 14:33:34.000000000 +0000 @@ -49,7 +49,7 @@ extern "C" { #endif -#include "typedefs.h" +#include "webrtc/typedefs.h" #if defined(__i386__) /*! \brief Find the bit position of the highest set bit in a word diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/g711_interface.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/g711_interface.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/g711_interface.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/g711_interface.c 2015-02-03 14:33:34.000000000 +0000 @@ -10,18 +10,14 @@ #include #include "g711.h" #include "g711_interface.h" -#include "typedefs.h" +#include "webrtc/typedefs.h" -int16_t WebRtcG711_EncodeA(void* state, - int16_t* speechIn, +int16_t WebRtcG711_EncodeA(int16_t* speechIn, int16_t len, int16_t* encoded) { int n; uint16_t tempVal, tempVal2; - // Set and discard to avoid getting warnings - (void)(state = NULL); - // Sanity check of input length if (len < 0) { return (-1); @@ -50,16 +46,12 @@ return (len); } -int16_t WebRtcG711_EncodeU(void* state, - int16_t* speechIn, +int16_t WebRtcG711_EncodeU(int16_t* speechIn, int16_t len, int16_t* encoded) { int n; uint16_t tempVal; - // Set and discard to avoid getting warnings - (void)(state = NULL); - // Sanity check of input length if (len < 0) { return (-1); @@ -86,17 +78,13 @@ return (len); } -int16_t WebRtcG711_DecodeA(void* state, - int16_t* encoded, +int16_t WebRtcG711_DecodeA(int16_t* encoded, int16_t len, int16_t* decoded, int16_t* speechType) { int n; uint16_t tempVal; - // Set and discard to avoid getting warnings - (void)(state = NULL); - // Sanity check of input length if (len < 0) { return (-1); @@ -123,17 +111,13 @@ return (len); } -int16_t WebRtcG711_DecodeU(void* state, - int16_t* encoded, +int16_t WebRtcG711_DecodeU(int16_t* encoded, int16_t len, int16_t* decoded, int16_t* speechType) { int n; uint16_t tempVal; - // Set and discard to avoid getting warnings - (void)(state = NULL); - // Sanity check of input length if (len < 0) { return (-1); @@ -160,10 +144,8 @@ return (len); } -int WebRtcG711_DurationEst(void* state, - const uint8_t* payload, +int WebRtcG711_DurationEst(const uint8_t* payload, int payload_length_bytes) { - (void) state; (void) payload; /* G.711 is one byte per sample, so we can just return the number of bytes. */ return payload_length_bytes; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/include/audio_encoder_pcm.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/include/audio_encoder_pcm.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/include/audio_encoder_pcm.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/include/audio_encoder_pcm.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_G711_INCLUDE_AUDIO_ENCODER_PCM_H_ +#define WEBRTC_MODULES_AUDIO_CODING_CODECS_G711_INCLUDE_AUDIO_ENCODER_PCM_H_ + +#include + +#include "webrtc/modules/audio_coding/codecs/audio_encoder.h" + +namespace webrtc { + +class AudioEncoderPcm : public AudioEncoder { + public: + struct Config { + Config() : frame_size_ms(20), num_channels(1) {} + + int frame_size_ms; + int num_channels; + }; + + explicit AudioEncoderPcm(const Config& config); + + virtual ~AudioEncoderPcm(); + + virtual int sample_rate_hz() const OVERRIDE; + virtual int num_channels() const OVERRIDE; + virtual int Num10MsFramesInNextPacket() const OVERRIDE; + + protected: + virtual bool Encode(uint32_t timestamp, + const int16_t* audio, + size_t max_encoded_bytes, + uint8_t* encoded, + size_t* encoded_bytes, + uint32_t* encoded_timestamp) OVERRIDE; + + virtual int16_t EncodeCall(const int16_t* audio, + size_t input_len, + uint8_t* encoded) = 0; + + private: + static const int kSampleRateHz = 8000; + const int num_channels_; + const int num_10ms_frames_per_packet_; + const int16_t full_frame_samples_; + std::vector speech_buffer_; + uint32_t first_timestamp_in_buffer_; +}; + +class AudioEncoderPcmA : public AudioEncoderPcm { + public: + explicit AudioEncoderPcmA(const Config& config) : AudioEncoderPcm(config) {} + + protected: + virtual int16_t EncodeCall(const int16_t* audio, + size_t input_len, + uint8_t* encoded) OVERRIDE; +}; + +class AudioEncoderPcmU : public AudioEncoderPcm { + public: + explicit AudioEncoderPcmU(const Config& config) : AudioEncoderPcm(config) {} + + protected: + virtual int16_t EncodeCall(const int16_t* audio, + size_t input_len, + uint8_t* encoded) OVERRIDE; +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_G711_INCLUDE_AUDIO_ENCODER_PCM_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/include/g711_interface.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/include/g711_interface.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/include/g711_interface.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/include/g711_interface.h 2015-02-03 14:33:34.000000000 +0000 @@ -11,7 +11,7 @@ #ifndef MODULES_AUDIO_CODING_CODECS_G711_MAIN_INTERFACE_G711_INTERFACE_H_ #define MODULES_AUDIO_CODING_CODECS_G711_MAIN_INTERFACE_G711_INTERFACE_H_ -#include "typedefs.h" +#include "webrtc/typedefs.h" // Comfort noise constants #define G711_WEBRTC_SPEECH 1 @@ -28,8 +28,6 @@ * Input speech length has be of any length. * * Input: - * - state : Dummy state to make this codec look more like - * other codecs * - speechIn : Input speech vector * - len : Samples in speechIn * @@ -40,8 +38,7 @@ * -1 - Error */ -int16_t WebRtcG711_EncodeA(void* state, - int16_t* speechIn, +int16_t WebRtcG711_EncodeA(int16_t* speechIn, int16_t len, int16_t* encoded); @@ -52,8 +49,6 @@ * Input speech length has be of any length. * * Input: - * - state : Dummy state to make this codec look more like - * other codecs * - speechIn : Input speech vector * - len : Samples in speechIn * @@ -64,8 +59,7 @@ * -1 - Error */ -int16_t WebRtcG711_EncodeU(void* state, - int16_t* speechIn, +int16_t WebRtcG711_EncodeU(int16_t* speechIn, int16_t len, int16_t* encoded); @@ -75,8 +69,6 @@ * This function decodes a packet G711 A-law frame. * * Input: - * - state : Dummy state to make this codec look more like - * other codecs * - encoded : Encoded data * - len : Bytes in encoded vector * @@ -90,8 +82,7 @@ * -1 - Error */ -int16_t WebRtcG711_DecodeA(void* state, - int16_t* encoded, +int16_t WebRtcG711_DecodeA(int16_t* encoded, int16_t len, int16_t* decoded, int16_t* speechType); @@ -102,8 +93,6 @@ * This function decodes a packet G711 U-law frame. * * Input: - * - state : Dummy state to make this codec look more like - * other codecs * - encoded : Encoded data * - len : Bytes in encoded vector * @@ -117,8 +106,7 @@ * -1 - Error */ -int16_t WebRtcG711_DecodeU(void* state, - int16_t* encoded, +int16_t WebRtcG711_DecodeU(int16_t* encoded, int16_t len, int16_t* decoded, int16_t* speechType); @@ -129,8 +117,6 @@ * This function estimates the duration of a G711 packet in samples. * * Input: - * - state : Dummy state to make this codec look more like - * other codecs * - payload : Encoded data * - payloadLengthBytes : Bytes in encoded vector * @@ -139,8 +125,7 @@ * byte per sample. */ -int WebRtcG711_DurationEst(void* state, - const uint8_t* payload, +int WebRtcG711_DurationEst(const uint8_t* payload, int payload_length_bytes); /********************************************************************** diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/test/testG711.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/test/testG711.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/test/testG711.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/test/testG711.cc 2015-02-03 14:33:34.000000000 +0000 @@ -127,7 +127,7 @@ /* G.711 encoding */ if (!strcmp(law, "A")) { /* A-law encoding */ - stream_len = WebRtcG711_EncodeA(NULL, shortdata, framelength, streamdata); + stream_len = WebRtcG711_EncodeA(shortdata, framelength, streamdata); if (argc == 6) { /* Write bits to file */ if (fwrite(streamdata, sizeof(unsigned char), stream_len, bitp) != @@ -135,11 +135,11 @@ return -1; } } - err = WebRtcG711_DecodeA(NULL, streamdata, stream_len, decoded, + err = WebRtcG711_DecodeA(streamdata, stream_len, decoded, speechType); } else if (!strcmp(law, "u")) { /* u-law encoding */ - stream_len = WebRtcG711_EncodeU(NULL, shortdata, framelength, streamdata); + stream_len = WebRtcG711_EncodeU(shortdata, framelength, streamdata); if (argc == 6) { /* Write bits to file */ if (fwrite(streamdata, sizeof(unsigned char), stream_len, bitp) != @@ -147,8 +147,7 @@ return -1; } } - err = WebRtcG711_DecodeU(NULL, streamdata, stream_len, decoded, - speechType); + err = WebRtcG711_DecodeU(streamdata, stream_len, decoded, speechType); } else { printf("Wrong law mode\n"); exit(1); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_g722 -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := \ - g722_interface.c \ - g722_encode.c \ - g722_decode.c - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../../../.. - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/g722_decode.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/g722_decode.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/g722_decode.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/g722_decode.c 2015-02-03 14:33:34.000000000 +0000 @@ -34,13 +34,12 @@ #include #endif -#include #include +#include #include -#include "typedefs.h" #include "g722_enc_dec.h" - +#include "webrtc/typedefs.h" #if !defined(FALSE) #define FALSE 0 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/g722_enc_dec.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/g722_enc_dec.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/g722_enc_dec.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/g722_enc_dec.h 2015-02-03 14:33:34.000000000 +0000 @@ -31,6 +31,8 @@ #if !defined(_G722_ENC_DEC_H_) #define _G722_ENC_DEC_H_ +#include "webrtc/typedefs.h" + /*! \page g722_page G.722 encoding and decoding \section g722_page_sec_1 What does it do? The G.722 module is a bit exact implementation of the ITU G.722 specification for all three diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/g722_encode.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/g722_encode.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/g722_encode.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/g722_encode.c 2015-02-03 14:33:34.000000000 +0000 @@ -34,12 +34,12 @@ #include #endif -#include #include +#include #include -#include "typedefs.h" #include "g722_enc_dec.h" +#include "webrtc/typedefs.h" #if !defined(FALSE) #define FALSE 0 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/g722_interface.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/g722_interface.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/g722_interface.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/g722_interface.c 2015-02-03 14:33:34.000000000 +0000 @@ -12,10 +12,9 @@ #include #include -#include "g722_interface.h" #include "g722_enc_dec.h" -#include "typedefs.h" - +#include "g722_interface.h" +#include "webrtc/typedefs.h" int16_t WebRtcG722_CreateEncoder(G722EncInst **G722enc_inst) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/include/g722_interface.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/include/g722_interface.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/include/g722_interface.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/include/g722_interface.h 2015-02-03 14:33:34.000000000 +0000 @@ -11,7 +11,7 @@ #ifndef MODULES_AUDIO_CODING_CODECS_G722_MAIN_INTERFACE_G722_INTERFACE_H_ #define MODULES_AUDIO_CODING_CODECS_G722_MAIN_INTERFACE_G722_INTERFACE_H_ -#include "typedefs.h" +#include "webrtc/typedefs.h" /* * Solution to support multiple instances diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/test/testG722.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/test/testG722.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/test/testG722.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/test/testG722.cc 2015-02-03 14:33:34.000000000 +0000 @@ -15,7 +15,7 @@ #include #include #include -#include "typedefs.h" +#include "webrtc/typedefs.h" /* include API */ #include "g722_interface.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,165 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../android-webrtc.mk - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_ilbc -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := \ - abs_quant.c \ - abs_quant_loop.c \ - augmented_cb_corr.c \ - bw_expand.c \ - cb_construct.c \ - cb_mem_energy.c \ - cb_mem_energy_augmentation.c \ - cb_mem_energy_calc.c \ - cb_search.c \ - cb_search_core.c \ - cb_update_best_index.c \ - chebyshev.c \ - comp_corr.c \ - constants.c \ - create_augmented_vec.c \ - decode.c \ - decode_residual.c \ - decoder_interpolate_lsf.c \ - do_plc.c \ - encode.c \ - energy_inverse.c \ - enh_upsample.c \ - enhancer.c \ - enhancer_interface.c \ - filtered_cb_vecs.c \ - frame_classify.c \ - gain_dequant.c \ - gain_quant.c \ - get_cd_vec.c \ - get_lsp_poly.c \ - get_sync_seq.c \ - hp_input.c \ - hp_output.c \ - ilbc.c \ - index_conv_dec.c \ - index_conv_enc.c \ - init_decode.c \ - init_encode.c \ - interpolate.c \ - interpolate_samples.c \ - lpc_encode.c \ - lsf_check.c \ - lsf_interpolate_to_poly_dec.c \ - lsf_interpolate_to_poly_enc.c \ - lsf_to_lsp.c \ - lsf_to_poly.c \ - lsp_to_lsf.c \ - my_corr.c \ - nearest_neighbor.c \ - pack_bits.c \ - poly_to_lsf.c \ - poly_to_lsp.c \ - refiner.c \ - simple_interpolate_lsf.c \ - simple_lpc_analysis.c \ - simple_lsf_dequant.c \ - simple_lsf_quant.c \ - smooth.c \ - smooth_out_data.c \ - sort_sq.c \ - split_vq.c \ - state_construct.c \ - state_search.c \ - swap_bytes.c \ - unpack_bits.c \ - vq3.c \ - vq4.c \ - window32_w32.c \ - xcorr_coef.c - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/interface \ - $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/../../../../common_audio/signal_processing/include - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) - - -# iLBC test app -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES:= test/iLBC_test.c - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/interface \ - $(LOCAL_PATH)/../../../.. - -LOCAL_STATIC_LIBRARIES := \ - libwebrtc_ilbc \ - libwebrtc_spl - -LOCAL_SHARED_LIBRARIES := \ - libutils - -LOCAL_MODULE:= webrtc_ilbc_test - -ifdef NDK_ROOT -include $(BUILD_EXECUTABLE) -else -include $(BUILD_NATIVE_TEST) -endif - -# iLBC_testLib test app -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES:= test/iLBC_testLib.c - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/interface \ - $(LOCAL_PATH)/../../../.. - -LOCAL_STATIC_LIBRARIES := \ - libwebrtc_ilbc \ - libwebrtc_spl - -LOCAL_SHARED_LIBRARIES := \ - libutils - -LOCAL_MODULE:= webrtc_ilbc_testLib - -ifdef NDK_ROOT -include $(BUILD_EXECUTABLE) -else -include $(BUILD_NATIVE_TEST) -endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_construct.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_construct.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_construct.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_construct.c 2015-02-03 14:33:34.000000000 +0000 @@ -60,7 +60,7 @@ a32 += WEBRTC_SPL_MUL_16_16(*gainPtr++, cbvec1[j]); a32 += WEBRTC_SPL_MUL_16_16(*gainPtr, cbvec2[j]); gainPtr -= 2; - decvector[j] = (int16_t) WEBRTC_SPL_RSHIFT_W32(a32 + 8192, 14); + decvector[j] = (int16_t)((a32 + 8192) >> 14); } return; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_mem_energy_augmentation.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_mem_energy_augmentation.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_mem_energy_augmentation.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_mem_energy_augmentation.c 2015-02-03 14:33:34.000000000 +0000 @@ -60,7 +60,7 @@ /* Normalize the energy and store the number of shifts */ (*enShPtr) = (int16_t)WebRtcSpl_NormW32(energy); tmp32 = WEBRTC_SPL_LSHIFT_W32(energy, (*enShPtr)); - (*enPtr) = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 16); + *enPtr = (int16_t)(tmp32 >> 16); enShPtr++; enPtr++; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_mem_energy.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_mem_energy.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_mem_energy.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_mem_energy.c 2015-02-03 14:33:34.000000000 +0000 @@ -54,7 +54,7 @@ /* Normalize the energy and store the number of shifts */ energyShifts[0] = (int16_t)WebRtcSpl_NormW32(energy); tmp32 = WEBRTC_SPL_LSHIFT_W32(energy, energyShifts[0]); - energyW16[0] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 16); + energyW16[0] = (int16_t)(tmp32 >> 16); /* Compute the energy of the rest of the cb memory * by step wise adding and subtracting the next @@ -70,7 +70,7 @@ /* Normalize the energy and store the number of shifts */ energyShifts[base_size] = (int16_t)WebRtcSpl_NormW32(energy); tmp32 = WEBRTC_SPL_LSHIFT_W32(energy, energyShifts[base_size]); - energyW16[base_size] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 16); + energyW16[base_size] = (int16_t)(tmp32 >> 16); ppi = filteredCB + lMem - 1 - lTarget; ppo = filteredCB + lMem - 1; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_mem_energy_calc.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_mem_energy_calc.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_mem_energy_calc.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_mem_energy_calc.c 2015-02-03 14:33:34.000000000 +0000 @@ -47,7 +47,7 @@ operation on the edge samples */ tmp = WEBRTC_SPL_MUL_16_16(*ppi, *ppi); tmp -= WEBRTC_SPL_MUL_16_16(*ppo, *ppo); - energy += WEBRTC_SPL_RSHIFT_W32(tmp, scale); + energy += tmp >> scale; energy = WEBRTC_SPL_MAX(energy, 0); ppi--; @@ -60,6 +60,6 @@ *eSh_ptr++ = shft; tmp = WEBRTC_SPL_LSHIFT_W32(energy, shft); - *eW16_ptr++ = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp, 16); + *eW16_ptr++ = (int16_t)(tmp >> 16); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_search_core.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_search_core.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_search_core.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_search_core.c 2015-02-03 14:33:34.000000000 +0000 @@ -66,7 +66,7 @@ for (i=0;i> 16); cDotSqW16 = (int16_t)(((int32_t)(tmp16)*(tmp16))>>16); /* Calculate the criteria (cDot*cDot/energy) */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_update_best_index.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_update_best_index.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_update_best_index.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/cb_update_best_index.c 2015-02-03 14:33:34.000000000 +0000 @@ -51,8 +51,7 @@ calculate the gain and store this index as the new best one */ - if (WEBRTC_SPL_RSHIFT_W32(CritNew, shNew)> - WEBRTC_SPL_RSHIFT_W32((*CritMax),shOld)) { + if ((CritNew >> shNew) > (*CritMax >> shOld)) { tmp16 = (int16_t)WebRtcSpl_NormW32(cDotNew); tmp16 = 16 - tmp16; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/chebyshev.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/chebyshev.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/chebyshev.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/chebyshev.c 2015-02-03 14:33:34.000000000 +0000 @@ -46,8 +46,8 @@ tmp2W32 = tmp1W32; /* Split b1 (in tmp1W32) into a high and low part */ - b1_high = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp1W32, 16); - b1_low = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp1W32-WEBRTC_SPL_LSHIFT_W32(((int32_t)b1_high),16), 1); + b1_high = (int16_t)(tmp1W32 >> 16); + b1_low = (int16_t)((tmp1W32 - ((int32_t)b1_high << 16)) >> 1); /* Calculate 2*x*b1-b2+f[i] */ tmp1W32 = WEBRTC_SPL_LSHIFT_W32( (WEBRTC_SPL_MUL_16_16(b1_high, x) + @@ -61,8 +61,8 @@ } /* Split b1 (in tmp1W32) into a high and low part */ - b1_high = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp1W32, 16); - b1_low = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp1W32-WEBRTC_SPL_LSHIFT_W32(((int32_t)b1_high),16), 1); + b1_high = (int16_t)(tmp1W32 >> 16); + b1_low = (int16_t)((tmp1W32 - ((int32_t)b1_high << 16)) >> 1); /* tmp1W32 = x*b1 - b2 + f[i]/2 */ tmp1W32 = WEBRTC_SPL_LSHIFT_W32(WEBRTC_SPL_MUL_16_16(b1_high, x), 1) + @@ -77,6 +77,6 @@ } else if (tmp1W32<((int32_t)-33554432)) { return(WEBRTC_SPL_WORD16_MIN); } else { - return((int16_t)WEBRTC_SPL_RSHIFT_W32(tmp1W32, 10)); + return (int16_t)(tmp1W32 >> 10); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/constants.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/constants.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/constants.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/constants.h 2015-02-03 14:33:34.000000000 +0000 @@ -20,7 +20,7 @@ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_CONSTANTS_H_ #include "defines.h" -#include "typedefs.h" +#include "webrtc/typedefs.h" /* high pass filters */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/decode_residual.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/decode_residual.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/decode_residual.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/decode_residual.c 2015-02-03 14:33:34.000000000 +0000 @@ -16,6 +16,8 @@ ******************************************************************/ +#include + #include "defines.h" #include "state_construct.h" #include "cb_construct.h" @@ -130,7 +132,7 @@ ); /* update memory */ - WEBRTC_SPL_MEMMOVE_W16(mem, mem+SUBL, CB_MEML-SUBL); + memmove(mem, mem + SUBL, (CB_MEML - SUBL) * sizeof(*mem)); WEBRTC_SPL_MEMCPY_W16(mem+CB_MEML-SUBL, &decresidual[(iLBC_encbits->startIdx+1+subframe)*SUBL], SUBL); @@ -169,7 +171,7 @@ ); /* update memory */ - WEBRTC_SPL_MEMMOVE_W16(mem, mem+SUBL, CB_MEML-SUBL); + memmove(mem, mem + SUBL, (CB_MEML - SUBL) * sizeof(*mem)); WEBRTC_SPL_MEMCPY_W16(mem+CB_MEML-SUBL, &reverseDecresidual[subframe*SUBL], SUBL); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/defines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/defines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/defines.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/defines.h 2015-02-03 14:33:34.000000000 +0000 @@ -18,9 +18,9 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_DEFINES_H_ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_DEFINES_H_ -#include "typedefs.h" -#include "signal_processing_library.h" #include +#include "signal_processing_library.h" +#include "webrtc/typedefs.h" /* general codec settings */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/do_plc.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/do_plc.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/do_plc.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/do_plc.c 2015-02-03 14:33:34.000000000 +0000 @@ -262,11 +262,11 @@ /* mix noise and pitch repeatition */ - PLCresidual[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(tot_gain, - (int16_t)WEBRTC_SPL_RSHIFT_W32( (WEBRTC_SPL_MUL_16_16(pitchfact, PLCresidual[i]) + - WEBRTC_SPL_MUL_16_16((32767-pitchfact), randvec[i]) + 16384), - 15), - 15); + PLCresidual[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT( + tot_gain, + (pitchfact * PLCresidual[i] + (32767 - pitchfact) * randvec[i] + + 16384) >> 15, + 15); /* Shifting down the result one step extra to ensure that no overflow will occur */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/encode.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/encode.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/encode.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/encode.c 2015-02-03 14:33:34.000000000 +0000 @@ -16,6 +16,8 @@ ******************************************************************/ +#include + #include "defines.h" #include "lpc_encode.h" #include "frame_classify.h" @@ -352,7 +354,7 @@ /* update memory */ - WEBRTC_SPL_MEMMOVE_W16(mem, mem+SUBL, (CB_MEML-SUBL)); + memmove(mem, mem + SUBL, (CB_MEML - SUBL) * sizeof(*mem)); WEBRTC_SPL_MEMCPY_W16(mem+CB_MEML-SUBL, &decresidual[(iLBCbits_inst->startIdx+1+subframe)*SUBL], SUBL); @@ -457,8 +459,7 @@ ); /* update memory */ - - WEBRTC_SPL_MEMMOVE_W16(mem, mem+SUBL, (CB_MEML-SUBL)); + memmove(mem, mem + SUBL, (CB_MEML - SUBL) * sizeof(*mem)); WEBRTC_SPL_MEMCPY_W16(mem+CB_MEML-SUBL, &reverseDecresidual[subframe*SUBL], SUBL); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/enhancer_interface.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/enhancer_interface.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/enhancer_interface.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/enhancer_interface.c 2015-02-03 14:33:34.000000000 +0000 @@ -16,6 +16,8 @@ ******************************************************************/ +#include + #include "defines.h" #include "constants.h" #include "xcorr_coef.h" @@ -71,9 +73,8 @@ enh_period=iLBCdec_inst->enh_period; /* Copy in the new data into the enhancer buffer */ - - WEBRTC_SPL_MEMMOVE_W16(enh_buf, &enh_buf[iLBCdec_inst->blockl], - ENH_BUFL-iLBCdec_inst->blockl); + memmove(enh_buf, &enh_buf[iLBCdec_inst->blockl], + (ENH_BUFL - iLBCdec_inst->blockl) * sizeof(*enh_buf)); WEBRTC_SPL_MEMCPY_W16(&enh_buf[ENH_BUFL-iLBCdec_inst->blockl], in, iLBCdec_inst->blockl); @@ -92,14 +93,14 @@ } /* Update the pitch prediction for each enhancer block, move the old ones */ - WEBRTC_SPL_MEMMOVE_W16(enh_period, &enh_period[new_blocks], - (ENH_NBLOCKS_TOT-new_blocks)); + memmove(enh_period, &enh_period[new_blocks], + (ENH_NBLOCKS_TOT - new_blocks) * sizeof(*enh_period)); k=WebRtcSpl_DownsampleFast( enh_buf+ENH_BUFL-inLen, /* Input samples */ (int16_t)(inLen+ENH_BUFL_FILTEROVERHEAD), downsampled, - (int16_t)WEBRTC_SPL_RSHIFT_W16(inLen, 1), + (int16_t)(inLen / 2), (int16_t*)WebRtcIlbcfix_kLpFiltCoefs, /* Coefficients in Q12 */ FILTERORDER_DS_PLUS1, /* Length of filter (order-1) */ FACTOR_DS, @@ -291,7 +292,7 @@ /* Calculate increase parameter for window part (16 last samples) */ /* (1-2*SqrtEnChange)/16 in Q15 */ - inc=(2048-WEBRTC_SPL_RSHIFT_W16(SqrtEnChange, 3)); + inc = 2048 - (SqrtEnChange >> 3); win=0; tmpW16ptr=&plc_pred[plc_blockl-16]; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/get_cd_vec.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/get_cd_vec.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/get_cd_vec.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/get_cd_vec.c 2015-02-03 14:33:34.000000000 +0000 @@ -41,7 +41,7 @@ base_size=lMem-cbveclen+1; if (cbveclen==SUBL) { - base_size+=WEBRTC_SPL_RSHIFT_W16(cbveclen,1); + base_size += cbveclen / 2; } /* No filter -> First codebook section */ @@ -60,7 +60,7 @@ k=(int16_t)WEBRTC_SPL_MUL_16_16(2, (index-(lMem-cbveclen+1)))+cbveclen; - lag=WEBRTC_SPL_RSHIFT_W16(k, 1); + lag = k / 2; WebRtcIlbcfix_CreateAugmentedVec(lag, mem+lMem, cbvec); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/get_lsp_poly.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/get_lsp_poly.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/get_lsp_poly.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/get_lsp_poly.c 2015-02-03 14:33:34.000000000 +0000 @@ -64,8 +64,8 @@ for(j=i; j>1; j--) { /* Compute f[j] = f[j] + tmp*f[j-1] + f[j-2]; */ - high = (int16_t)WEBRTC_SPL_RSHIFT_W32(fPtr[-1], 16); - low = (int16_t)WEBRTC_SPL_RSHIFT_W32(fPtr[-1]-WEBRTC_SPL_LSHIFT_W32(((int32_t)high),16), 1); + high = (int16_t)(fPtr[-1] >> 16); + low = (int16_t)((fPtr[-1] - ((int32_t)high << 16)) >> 1); tmpW32 = WEBRTC_SPL_LSHIFT_W32(WEBRTC_SPL_MUL_16_16(high, (*lspPtr)), 2) + WEBRTC_SPL_LSHIFT_W32(WEBRTC_SPL_MUL_16_16_RSFT(low, (*lspPtr), 15), 2); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/hp_input.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/hp_input.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/hp_input.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/hp_input.c 2015-02-03 14:33:34.000000000 +0000 @@ -65,7 +65,7 @@ tmpW32b = WEBRTC_SPL_SAT((int32_t)268435455, tmpW32b, (int32_t)-268435456); /* Convert back to Q0 and multiply with 0.5 */ - signal[i] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmpW32b, 13); + signal[i] = (int16_t)(tmpW32b >> 13); /* Update state (filtered part) */ y[2] = y[0]; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/hp_output.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/hp_output.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/hp_output.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/hp_output.c 2015-02-03 14:33:34.000000000 +0000 @@ -65,7 +65,7 @@ tmpW32b = WEBRTC_SPL_SAT((int32_t)67108863, tmpW32b, (int32_t)-67108864); /* Convert back to Q0 and multiply with 2 */ - signal[i] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmpW32b, 11); + signal[i] = (int16_t)(tmpW32b >> 11); /* Update state (filtered part) */ y[2] = y[0]; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/interface/ilbc.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/interface/ilbc.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/interface/ilbc.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/interface/ilbc.h 2015-02-03 14:33:34.000000000 +0000 @@ -22,7 +22,7 @@ * Define the fixpoint numeric formats */ -#include "typedefs.h" +#include "webrtc/typedefs.h" /* * Solution to support multiple instances diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/interpolate.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/interpolate.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/interpolate.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/interpolate.c 2015-02-03 14:33:34.000000000 +0000 @@ -39,9 +39,7 @@ invcoef = 16384 - coef; /* 16384 = 1.0 (Q14)*/ for (i = 0; i < length; i++) { - out[i] = (int16_t) WEBRTC_SPL_RSHIFT_W32( - (WEBRTC_SPL_MUL_16_16(coef, in1[i]) + WEBRTC_SPL_MUL_16_16(invcoef, in2[i]))+8192, - 14); + out[i] = (int16_t)((coef * in1[i] + invcoef * in2[i] + 8192) >> 14); } return; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/lsf_to_lsp.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/lsf_to_lsp.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/lsf_to_lsp.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/lsf_to_lsp.c 2015-02-03 14:33:34.000000000 +0000 @@ -43,7 +43,7 @@ Lower 8 bits give the difference, which needs to be approximated linearly */ - k = WEBRTC_SPL_RSHIFT_W16(freq, 8); + k = freq >> 8; diff = (freq&0x00ff); /* Guard against getting outside table */ @@ -54,7 +54,7 @@ /* Calculate linear approximation */ tmpW32 = WEBRTC_SPL_MUL_16_16(WebRtcIlbcfix_kCosDerivative[k], diff); - lsp[i] = WebRtcIlbcfix_kCos[k]+(int16_t)(WEBRTC_SPL_RSHIFT_W32(tmpW32, 12)); + lsp[i] = WebRtcIlbcfix_kCos[k] + (int16_t)(tmpW32 >> 12); } return; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/lsf_to_poly.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/lsf_to_poly.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/lsf_to_poly.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/lsf_to_poly.c 2015-02-03 14:33:34.000000000 +0000 @@ -71,10 +71,10 @@ for (i=5; i>0; i--) { tmpW32 = (*f1ptr) + (*f2ptr); - (*a1ptr) = (int16_t)WEBRTC_SPL_RSHIFT_W32((tmpW32+4096),13); + *a1ptr = (int16_t)((tmpW32 + 4096) >> 13); tmpW32 = (*f1ptr) - (*f2ptr); - (*a2ptr) = (int16_t)WEBRTC_SPL_RSHIFT_W32((tmpW32+4096),13); + *a2ptr = (int16_t)((tmpW32 + 4096) >> 13); a1ptr++; a2ptr--; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/lsp_to_lsf.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/lsp_to_lsf.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/lsp_to_lsf.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/lsp_to_lsf.c 2015-02-03 14:33:34.000000000 +0000 @@ -71,7 +71,7 @@ tmp = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(WebRtcIlbcfix_kAcosDerivative[k],diff, 11); /* freq in Q16 */ - freq = (int16_t)WEBRTC_SPL_LSHIFT_W16(k,9)+tmp; + freq = (k << 9) + tmp; /* lsf = freq*2*pi */ (*lsfPtr) = (int16_t)(((int32_t)freq*25736)>>15); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/poly_to_lsp.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/poly_to_lsp.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/poly_to_lsp.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/poly_to_lsp.c 2015-02-03 14:33:34.000000000 +0000 @@ -56,8 +56,10 @@ (*f1ptr) = 1024; /* 1.0 in Q10 */ (*f2ptr) = 1024; /* 1.0 in Q10 */ for (i = 0; i < 5; i++) { - (*(f1ptr+1)) = (int16_t)(WEBRTC_SPL_RSHIFT_W32(((int32_t)(*a_i_ptr)+(*a_10mi_ptr)), 2) - (*f1ptr)); - (*(f2ptr+1)) = (int16_t)(WEBRTC_SPL_RSHIFT_W32(((int32_t)(*a_i_ptr)-(*a_10mi_ptr)), 2) + (*f2ptr)); + *(f1ptr + 1) = + (int16_t)((((int32_t)(*a_i_ptr) + *a_10mi_ptr) >> 2) - *f1ptr); + *(f2ptr + 1) = + (int16_t)((((int32_t)(*a_i_ptr) - *a_10mi_ptr) >> 2) + *f2ptr); a_i_ptr++; a_10mi_ptr--; f1ptr++; @@ -91,7 +93,7 @@ /* Run 4 times to reduce the interval */ for (i = 0; i < 4; i++) { /* xmid =(xlow + xhigh)/2 */ - xmid = WEBRTC_SPL_RSHIFT_W16(xlow, 1) + WEBRTC_SPL_RSHIFT_W16(xhigh, 1); + xmid = (xlow >> 1) + (xhigh >> 1); ymid = WebRtcIlbcfix_Chebyshev(xmid, f[fi_select]); if (WEBRTC_SPL_MUL_16_16(ylow, ymid) <= 0) { @@ -117,7 +119,7 @@ sign = y; y = WEBRTC_SPL_ABS_W16(y); shifts = (int16_t)WebRtcSpl_NormW32(y)-16; - y = WEBRTC_SPL_LSHIFT_W16(y, shifts); + y <<= shifts; y = (int16_t)WebRtcSpl_DivW32W16(536838144, y); /* 1/(yhigh-ylow) */ tmpW32 = WEBRTC_SPL_MUL_16_16_RSFT(x, y, (19-shifts)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/refiner.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/refiner.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/refiner.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/refiner.c 2015-02-03 14:33:34.000000000 +0000 @@ -53,7 +53,7 @@ /* defining array bounds */ - estSegPosRounded=WEBRTC_SPL_RSHIFT_W16((estSegPos - 2),2); + estSegPosRounded = (estSegPos - 2) >> 2; searchSegStartPos=estSegPosRounded-ENH_SLOP; @@ -81,7 +81,7 @@ if (scalefact>0) { for (i=0;i> scalefact); } } else { for (i=0;i> 2; st=searchSegStartPos+tloc2-ENH_FL0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/smooth.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/smooth.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/smooth.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/smooth.c 2015-02-03 14:33:34.000000000 +0000 @@ -101,7 +101,7 @@ } else { /* crit = 0.05 * w00 (Result in Q-6) */ crit = WEBRTC_SPL_SHIFT_W32( - WEBRTC_SPL_MUL(ENH_A0, WEBRTC_SPL_RSHIFT_W32(w00prim, 14)), + WEBRTC_SPL_MUL(ENH_A0, w00prim >> 14), -(6-scale+scale1)); } @@ -139,7 +139,7 @@ endiff = (w11w00-w10w10); endiff = WEBRTC_SPL_MAX(0, endiff); /* denom is in Q16 */ - denom = WebRtcSpl_DivW32W16(endiff, (int16_t)WEBRTC_SPL_RSHIFT_W32(w00w00, 16)); + denom = WebRtcSpl_DivW32W16(endiff, (int16_t)(w00w00 >> 16)); } else { denom = 65536; } @@ -151,10 +151,10 @@ if (scale>0) { /* denomW16 is in Q(16+scale) */ - denomW16=(int16_t)WEBRTC_SPL_RSHIFT_W32(denom, scale); + denomW16 = (int16_t)(denom >> scale); /* num in Q(34-scale) */ - num=WEBRTC_SPL_RSHIFT_W32(ENH_A0_MINUS_A0A0DIV4, scale); + num = ENH_A0_MINUS_A0A0DIV4 >> scale; } else { /* denomW16 is in Q16 */ denomW16=(int16_t)denom; @@ -174,8 +174,8 @@ scale = bitsw00-scale2-15; if (scale>0) { - w10prim=WEBRTC_SPL_RSHIFT_W32(w10prim, scale); - w00prim=WEBRTC_SPL_RSHIFT_W32(w00prim, scale); + w10prim >>= scale; + w00prim >>= scale; } if ((w00prim>0)&&(w10prim>0)) { @@ -187,7 +187,7 @@ B_W32 = (int32_t)1073741824 - (int32_t)ENH_A0DIV2 - WEBRTC_SPL_MUL(A, w11_div_w00); } - B = (int16_t)WEBRTC_SPL_RSHIFT_W32(B_W32, 16); /* B in Q14 */ + B = (int16_t)(B_W32 >> 16); /* B in Q14. */ } else { /* No smoothing */ A = 0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/smooth_out_data.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/smooth_out_data.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/smooth_out_data.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/smooth_out_data.c 2015-02-03 14:33:34.000000000 +0000 @@ -31,13 +31,12 @@ int32_t errs; for(i=0;i<80;i++) { - odata[i]= (int16_t)WEBRTC_SPL_RSHIFT_W32( - (WEBRTC_SPL_MUL_16_16(C, surround[i])+1024), 11); + odata[i]= (int16_t)((C * surround[i] + 1024) >> 11); } errs=0; for(i=0;i<80;i++) { - err=(int16_t)WEBRTC_SPL_RSHIFT_W16((psseq[i]-odata[i]), 3); + err = (psseq[i] - odata[i]) >> 3; errs+=WEBRTC_SPL_MUL_16_16(err, err); /* errs in Q-6 */ } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/sort_sq.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/sort_sq.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/sort_sq.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/sort_sq.c 2015-02-03 14:33:34.000000000 +0000 @@ -40,7 +40,7 @@ i++; } - if (x > WEBRTC_SPL_RSHIFT_W32(( (int32_t)cb[i] + cb[i - 1] + 1),1)) { + if (x > (((int32_t)cb[i] + cb[i - 1] + 1) >> 1)) { *index = i; *xq = cb[i]; } else { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/vq3.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/vq3.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/vq3.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/vq3.h 2015-02-03 14:33:34.000000000 +0000 @@ -19,7 +19,7 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_VQ3_H_ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_VQ3_H_ -#include "typedefs.h" +#include "webrtc/typedefs.h" /*----------------------------------------------------------------* * Vector quantization of order 3 (based on MSE) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/vq4.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/vq4.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/vq4.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/vq4.h 2015-02-03 14:33:34.000000000 +0000 @@ -19,7 +19,7 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_VQ4_H_ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_VQ4_H_ -#include "typedefs.h" +#include "webrtc/typedefs.h" /*----------------------------------------------------------------* * Vector quantization of order 4 (based on MSE) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/window32_w32.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/window32_w32.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/window32_w32.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/window32_w32.c 2015-02-03 14:33:34.000000000 +0000 @@ -42,15 +42,15 @@ */ for (i = 0; i < N; i++) { /* Extract higher bytes */ - x_hi = (int16_t) WEBRTC_SPL_RSHIFT_W32(x[i], 16); - y_hi = (int16_t) WEBRTC_SPL_RSHIFT_W32(y[i], 16); + x_hi = (int16_t)(x[i] >> 16); + y_hi = (int16_t)(y[i] >> 16); /* Extract lower bytes, defined as (w32 - hi<<16)>>1 */ temp = WEBRTC_SPL_LSHIFT_W32((int32_t)x_hi, 16); - x_low = (int16_t) WEBRTC_SPL_RSHIFT_W32((x[i] - temp), 1); + x_low = (int16_t)((x[i] - temp) >> 1); temp = WEBRTC_SPL_LSHIFT_W32((int32_t)y_hi, 16); - y_low = (int16_t) WEBRTC_SPL_RSHIFT_W32((y[i] - temp), 1); + y_low = (int16_t)((y[i] - temp) >> 1); /* Calculate z by a 32 bit multiplication using both low and high from x and y */ temp = WEBRTC_SPL_LSHIFT_W32(WEBRTC_SPL_MUL_16_16(x_hi, y_hi), 1); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/xcorr_coef.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/xcorr_coef.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/xcorr_coef.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/xcorr_coef.c 2015-02-03 14:33:34.000000000 +0000 @@ -131,9 +131,7 @@ pos+=step; /* Do a +/- to get the next energy */ - Energy += step*(WEBRTC_SPL_RSHIFT_W32( - ((int32_t)(*rp_end)*(*rp_end)) - ((int32_t)(*rp_beg)*(*rp_beg)), - shifts)); + Energy += step * ((*rp_end * *rp_end - *rp_beg * *rp_beg) >> shifts); rp_beg+=step; rp_end+=step; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h 2015-02-03 14:33:34.000000000 +0000 @@ -14,8 +14,7 @@ /* * Define the fixpoint numeric formats */ -#include "typedefs.h" - +#include "webrtc/typedefs.h" typedef struct { void *dummy; @@ -131,7 +130,7 @@ int16_t WebRtcIsacfix_Encode(ISACFIX_MainStruct *ISAC_main_inst, const int16_t *speechIn, - int16_t *encoded); + uint8_t* encoded); @@ -202,7 +201,7 @@ */ int16_t WebRtcIsacfix_UpdateBwEstimate1(ISACFIX_MainStruct *ISAC_main_inst, - const uint16_t *encoded, + const uint8_t* encoded, int32_t packet_size, uint16_t rtp_seq_number, uint32_t arr_ts); @@ -227,7 +226,7 @@ */ int16_t WebRtcIsacfix_UpdateBwEstimate(ISACFIX_MainStruct *ISAC_main_inst, - const uint16_t *encoded, + const uint8_t* encoded, int32_t packet_size, uint16_t rtp_seq_number, uint32_t send_ts, @@ -253,7 +252,7 @@ */ int16_t WebRtcIsacfix_Decode(ISACFIX_MainStruct *ISAC_main_inst, - const uint16_t *encoded, + const uint8_t* encoded, int16_t len, int16_t *decoded, int16_t *speechType); @@ -351,13 +350,15 @@ * * Input: * - encoded : Encoded bitstream + * - encoded_len_bytes : Length of the bitstream in bytes. * * Output: * - frameLength : Length of frame in packet (in samples) * */ - int16_t WebRtcIsacfix_ReadFrameLen(const int16_t* encoded, + int16_t WebRtcIsacfix_ReadFrameLen(const uint8_t* encoded, + int encoded_len_bytes, int16_t* frameLength); /**************************************************************************** @@ -556,7 +557,7 @@ int16_t WebRtcIsacfix_GetNewBitStream(ISACFIX_MainStruct *ISAC_main_inst, int16_t bweIndex, float scale, - int16_t *encoded); + uint8_t* encoded); /**************************************************************************** @@ -600,13 +601,15 @@ * * Input: * - encoded : Encoded bitstream + * - encoded_len_bytes : Length of the bitstream in bytes. * * Output: * - rateIndex : Bandwidth estimate in bitstream * */ - int16_t WebRtcIsacfix_ReadBwIndex(const int16_t* encoded, + int16_t WebRtcIsacfix_ReadBwIndex(const uint8_t* encoded, + int encoded_len_bytes, int16_t* rateIndex); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,152 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -############################# -# Build the non-neon library. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_isacfix -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := \ - arith_routines.c \ - arith_routines_hist.c \ - arith_routines_logist.c \ - bandwidth_estimator.c \ - decode.c \ - decode_bwe.c \ - decode_plc.c \ - encode.c \ - entropy_coding.c \ - fft.c \ - filterbank_tables.c \ - filterbanks.c \ - filters.c \ - initialize.c \ - isacfix.c \ - lattice.c \ - lpc_masking_model.c \ - lpc_tables.c \ - pitch_estimator.c \ - pitch_filter.c \ - pitch_gain_tables.c \ - pitch_lag_tables.c \ - spectrum_ar_model_tables.c \ - transform.c - -ifeq ($(ARCH_ARM_HAVE_ARMV7A),true) -# Using .S (instead of .s) extention is to include a C header file in assembly. -LOCAL_SRC_FILES += \ - lattice_armv7.S \ - pitch_filter_armv6.S -else -LOCAL_SRC_FILES += \ - lattice_c.c -endif - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../../../.. \ - $(LOCAL_PATH)/../../../../../../common_audio/signal_processing/include - -LOCAL_STATIC_LIBRARIES += libwebrtc_system_wrappers - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) - -######################### -# Build the neon library. -ifeq ($(WEBRTC_BUILD_NEON_LIBS),true) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_isacfix_neon -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := \ - filterbanks_neon.S \ - filters_neon.S \ - lattice_neon.S \ - lpc_masking_model_neon.S \ - transform_neon.S - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) \ - -mfpu=neon \ - -mfloat-abi=softfp \ - -flax-vector-conversions - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../../../.. \ - $(LOCAL_PATH)/../../../../../../common_audio/signal_processing/include - - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) - -endif # ifeq ($(WEBRTC_BUILD_NEON_LIBS),true) - -########################### -# isac test app - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES:= ../test/kenny.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../../../.. - -LOCAL_STATIC_LIBRARIES := \ - libwebrtc_isacfix \ - libwebrtc_spl \ - libwebrtc_system_wrappers \ - libwebrtc_test_support - -ifeq ($(WEBRTC_BUILD_NEON_LIBS),true) -LOCAL_STATIC_LIBRARIES += \ - libwebrtc_isacfix_neon -endif - -LOCAL_SHARED_LIBRARIES := \ - libutils - -LOCAL_MODULE:= webrtc_isac_test - -ifdef NDK_ROOT -include $(BUILD_EXECUTABLE) -else -include $(BUILD_NATIVE_TEST) -endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/arith_routines.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/arith_routines.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/arith_routines.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/arith_routines.c 2015-02-03 14:33:34.000000000 +0000 @@ -72,11 +72,10 @@ } /* write remaining data to bitstream, if "full == 0" first byte has data */ if (streamData->full == 0) { - *streamPtr++ += (uint16_t) WEBRTC_SPL_RSHIFT_W32(streamData->streamval, 24); + *streamPtr++ += (uint16_t)(streamData->streamval >> 24); streamData->full = 1; } else { - *streamPtr = (uint16_t) WEBRTC_SPL_LSHIFT_W32( - WEBRTC_SPL_RSHIFT_W32(streamData->streamval, 24), 8); + *streamPtr = (uint16_t)((streamData->streamval >> 24) << 8); streamData->full = 0; } } @@ -111,11 +110,10 @@ } /* write remaining data (2 bytes) to bitstream */ if (streamData->full) { - *streamPtr++ = (uint16_t) WEBRTC_SPL_RSHIFT_W32(streamData->streamval, 16); + *streamPtr++ = (uint16_t)(streamData->streamval >> 16); } else { - *streamPtr++ |= (uint16_t) WEBRTC_SPL_RSHIFT_W32(streamData->streamval, 24); - *streamPtr = (uint16_t) WEBRTC_SPL_RSHIFT_W32(streamData->streamval, 8) - & 0xFF00; + *streamPtr++ |= (uint16_t)(streamData->streamval >> 24); + *streamPtr = (uint16_t)(streamData->streamval >> 8) & 0xFF00; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/arith_routines_hist.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/arith_routines_hist.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/arith_routines_hist.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/arith_routines_hist.c 2015-02-03 14:33:34.000000000 +0000 @@ -65,11 +65,11 @@ /* update interval */ W_upper_LSB = W_upper & 0x0000FFFF; - W_upper_MSB = WEBRTC_SPL_RSHIFT_W32(W_upper, 16); + W_upper_MSB = W_upper >> 16; W_lower = WEBRTC_SPL_UMUL(W_upper_MSB, cdfLo); - W_lower += WEBRTC_SPL_UMUL_RSFT16(W_upper_LSB, cdfLo); + W_lower += ((W_upper_LSB * cdfLo) >> 16); W_upper = WEBRTC_SPL_UMUL(W_upper_MSB, cdfHi); - W_upper += WEBRTC_SPL_UMUL_RSFT16(W_upper_LSB, cdfHi); + W_upper += ((W_upper_LSB * cdfHi) >> 16); /* shift interval such that it begins at zero */ W_upper -= ++W_lower; @@ -103,11 +103,10 @@ { W_upper = WEBRTC_SPL_LSHIFT_W32(W_upper, 8); if (streamData->full == 0) { - *streamPtr++ += (uint16_t) WEBRTC_SPL_RSHIFT_W32(streamData->streamval, 24); + *streamPtr++ += (uint16_t)(streamData->streamval >> 24); streamData->full = 1; } else { - *streamPtr = (uint16_t) WEBRTC_SPL_LSHIFT_W32( - WEBRTC_SPL_RSHIFT_W32(streamData->streamval, 24), 8); + *streamPtr = (uint16_t)((streamData->streamval >> 24) << 8); streamData->full = 0; } @@ -185,18 +184,18 @@ { /* find the integer *data for which streamval lies in [W_lower+1, W_upper] */ W_upper_LSB = W_upper & 0x0000FFFF; - W_upper_MSB = WEBRTC_SPL_RSHIFT_W32(W_upper, 16); + W_upper_MSB = W_upper >> 16; /* start halfway the cdf range */ - sizeTmp = WEBRTC_SPL_RSHIFT_W16(*cdfSize++, 1); + sizeTmp = *cdfSize++ / 2; cdfPtr = *cdf + (sizeTmp - 1); /* method of bisection */ for ( ;; ) { W_tmp = WEBRTC_SPL_UMUL_32_16(W_upper_MSB, *cdfPtr); - W_tmp += WEBRTC_SPL_UMUL_32_16_RSFT16(W_upper_LSB, *cdfPtr); - sizeTmp = WEBRTC_SPL_RSHIFT_W16(sizeTmp, 1); + W_tmp += (W_upper_LSB * (*cdfPtr)) >> 16; + sizeTmp /= 2; if (sizeTmp == 0) { break; } @@ -235,8 +234,7 @@ (*streamPtr++ & 0x00FF); streamData->full = 1; } else { - streamval = WEBRTC_SPL_LSHIFT_W32(streamval, 8) | - WEBRTC_SPL_RSHIFT_W16(*streamPtr, 8); + streamval = (streamval << 8) | (*streamPtr >> 8); streamData->full = 0; } W_upper = WEBRTC_SPL_LSHIFT_W32(W_upper, 8); @@ -310,7 +308,7 @@ if (streamData->stream_index == 0) { /* read first word from bytestream */ - streamval = WEBRTC_SPL_LSHIFT_U32(*streamPtr++, 16); + streamval = (uint32_t)(*streamPtr++) << 16; streamval |= *streamPtr++; } else { streamval = streamData->streamval; @@ -325,7 +323,7 @@ /* start at the specified table entry */ cdfPtr = *cdf + (*initIndex++); W_tmp = WEBRTC_SPL_UMUL_32_16(W_upper_MSB, *cdfPtr); - W_tmp += WEBRTC_SPL_UMUL_32_16_RSFT16(W_upper_LSB, *cdfPtr); + W_tmp += (W_upper_LSB * (*cdfPtr)) >> 16; if (streamval > W_tmp) { @@ -339,7 +337,7 @@ } W_tmp = WEBRTC_SPL_UMUL_32_16(W_upper_MSB, *++cdfPtr); - W_tmp += WEBRTC_SPL_UMUL_32_16_RSFT16(W_upper_LSB, *cdfPtr); + W_tmp += (W_upper_LSB * (*cdfPtr)) >> 16; if (streamval <= W_tmp) { break; @@ -359,7 +357,7 @@ } W_tmp = WEBRTC_SPL_UMUL_32_16(W_upper_MSB, *cdfPtr); - W_tmp += WEBRTC_SPL_UMUL_32_16_RSFT16(W_upper_LSB, *cdfPtr); + W_tmp += (W_upper_LSB * (*cdfPtr)) >> 16; if (streamval > W_tmp) { break; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/arith_routines_logist.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/arith_routines_logist.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/arith_routines_logist.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/arith_routines_logist.c 2015-02-03 14:33:34.000000000 +0000 @@ -17,7 +17,6 @@ #include "arith_routins.h" - /* Tables for piecewise linear cdf functions: y = k*x */ /* x Points for function piecewise() in Q15 */ @@ -72,7 +71,7 @@ /* Find index for x-value */ qtmp1 = WEBRTC_SPL_SAT(kHistEdges[50],xinQ15,kHistEdges[0]); ind = WEBRTC_SPL_MUL(5, qtmp1 - kHistEdges[0]); - ind = WEBRTC_SPL_RSHIFT_W32(ind, 16); + ind >>= 16; /* Calculate corresponding y-value ans return*/ qtmp1 = qtmp1 - kHistEdges[ind]; @@ -151,9 +150,9 @@ W_upper_LSB = (uint16_t)W_upper; W_upper_MSB = (uint16_t)WEBRTC_SPL_RSHIFT_U32(W_upper, 16); W_lower = WEBRTC_SPL_UMUL_32_16(cdfLo, W_upper_MSB); - W_lower += WEBRTC_SPL_UMUL_32_16_RSFT16(cdfLo, W_upper_LSB); + W_lower += (cdfLo * W_upper_LSB) >> 16; W_upper = WEBRTC_SPL_UMUL_32_16(cdfHi, W_upper_MSB); - W_upper += WEBRTC_SPL_UMUL_32_16_RSFT16(cdfHi, W_upper_LSB); + W_upper += (cdfHi * W_upper_LSB) >> 16; /* shift interval such that it begins at zero */ W_upper -= ++W_lower; @@ -185,21 +184,20 @@ * W_upper < 2^24 */ while ( !(W_upper & 0xFF000000) ) { - W_upper = WEBRTC_SPL_LSHIFT_U32(W_upper, 8); + W_upper <<= 8; if (streamData->full == 0) { *streamPtr++ += (uint16_t) WEBRTC_SPL_RSHIFT_U32( streamData->streamval, 24); streamData->full = 1; } else { - *streamPtr = (uint16_t) WEBRTC_SPL_LSHIFT_U32( - WEBRTC_SPL_RSHIFT_U32(streamData->streamval, 24), 8); + *streamPtr = (uint16_t)((streamData->streamval >> 24) << 8); streamData->full = 0; } if( streamPtr > maxStreamPtr ) return -ISAC_DISALLOWED_BITSTREAM_LENGTH; - streamData->streamval = WEBRTC_SPL_LSHIFT_U32(streamData->streamval, 8); + streamData->streamval <<= 8; } } @@ -248,7 +246,7 @@ int16_t envCount; uint16_t tmpARSpecQ8 = 0; int k, i; - + int offset = 0; /* point to beginning of stream buffer */ streamPtr = streamData->stream + streamData->stream_index; @@ -258,7 +256,7 @@ if (streamData->stream_index == 0) { /* read first word from bytestream */ - streamVal = WEBRTC_SPL_LSHIFT_U32(*streamPtr++, 16); + streamVal = (uint32_t)(*streamPtr++) << 16; streamVal |= *streamPtr++; } else { @@ -266,8 +264,7 @@ } - res = WEBRTC_SPL_LSHIFT_W32((int32_t)1, - WEBRTC_SPL_RSHIFT_W16(WebRtcSpl_GetSizeInBits(envQ8[0]), 1)); + res = 1 << (WebRtcSpl_GetSizeInBits(envQ8[0]) >> 1); envCount = 0; /* code assumes lenData%4 == 0 */ @@ -283,11 +280,11 @@ if (inSqrt < 0) inSqrt=-inSqrt; - newRes = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_DIV(inSqrt, res) + res, 1); + newRes = (inSqrt / res + res) >> 1; do { res = newRes; - newRes = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_DIV(inSqrt, res) + res, 1); + newRes = (inSqrt / res + res) >> 1; } while (newRes != res && i-- > 0); tmpARSpecQ8 = (uint16_t)newRes; @@ -303,8 +300,8 @@ candQ7 = - *dataQ7 + 64; cdfTmp = WebRtcIsacfix_Piecewise(WEBRTC_SPL_MUL_16_U16(candQ7, tmpARSpecQ8)); - W_tmp = WEBRTC_SPL_UMUL_16_16(cdfTmp, W_upper_MSB); - W_tmp += WEBRTC_SPL_UMUL_16_16_RSFT16(cdfTmp, W_upper_LSB); + W_tmp = (uint32_t)cdfTmp * W_upper_MSB; + W_tmp += ((uint32_t)cdfTmp * (uint32_t)W_upper_LSB) >> 16; if (streamVal > W_tmp) { @@ -312,8 +309,8 @@ candQ7 += 128; cdfTmp = WebRtcIsacfix_Piecewise(WEBRTC_SPL_MUL_16_U16(candQ7, tmpARSpecQ8)); - W_tmp = WEBRTC_SPL_UMUL_16_16(cdfTmp, W_upper_MSB); - W_tmp += WEBRTC_SPL_UMUL_16_16_RSFT16(cdfTmp, W_upper_LSB); + W_tmp = (uint32_t)cdfTmp * W_upper_MSB; + W_tmp += ((uint32_t)cdfTmp * (uint32_t)W_upper_LSB) >> 16; while (streamVal > W_tmp) { @@ -322,8 +319,8 @@ cdfTmp = WebRtcIsacfix_Piecewise( WEBRTC_SPL_MUL_16_U16(candQ7, tmpARSpecQ8)); - W_tmp = WEBRTC_SPL_UMUL_16_16(cdfTmp, W_upper_MSB); - W_tmp += WEBRTC_SPL_UMUL_16_16_RSFT16(cdfTmp, W_upper_LSB); + W_tmp = (uint32_t)cdfTmp * W_upper_MSB; + W_tmp += ((uint32_t)cdfTmp * (uint32_t)W_upper_LSB) >> 16; /* error check */ if (W_lower == W_tmp) { @@ -341,8 +338,8 @@ candQ7 -= 128; cdfTmp = WebRtcIsacfix_Piecewise(WEBRTC_SPL_MUL_16_U16(candQ7, tmpARSpecQ8)); - W_tmp = WEBRTC_SPL_UMUL_16_16(cdfTmp, W_upper_MSB); - W_tmp += WEBRTC_SPL_UMUL_16_16_RSFT16(cdfTmp, W_upper_LSB); + W_tmp = (uint32_t)cdfTmp * W_upper_MSB; + W_tmp += ((uint32_t)cdfTmp * (uint32_t)W_upper_LSB) >> 16; while ( !(streamVal > W_tmp) ) { @@ -351,8 +348,8 @@ cdfTmp = WebRtcIsacfix_Piecewise( WEBRTC_SPL_MUL_16_U16(candQ7, tmpARSpecQ8)); - W_tmp = WEBRTC_SPL_UMUL_16_16(cdfTmp, W_upper_MSB); - W_tmp += WEBRTC_SPL_UMUL_16_16_RSFT16(cdfTmp, W_upper_LSB); + W_tmp = (uint32_t)cdfTmp * W_upper_MSB; + W_tmp += ((uint32_t)cdfTmp * (uint32_t)W_upper_LSB) >> 16; /* error check */ if (W_upper == W_tmp){ @@ -377,14 +374,27 @@ * W_upper < 2^24 */ while ( !(W_upper & 0xFF000000) ) { - /* read next byte from stream */ - if (streamData->full == 0) { - streamVal = WEBRTC_SPL_LSHIFT_W32(streamVal, 8) | (*streamPtr++ & 0x00FF); - streamData->full = 1; + if (streamPtr < streamData->stream + streamData->stream_size) { + /* read next byte from stream */ + if (streamData->full == 0) { + streamVal = WEBRTC_SPL_LSHIFT_W32(streamVal, 8) | (*streamPtr++ & 0x00FF); + streamData->full = 1; + } else { + streamVal = WEBRTC_SPL_LSHIFT_W32(streamVal, 8) | + ((*streamPtr) >> 8); + streamData->full = 0; + } } else { - streamVal = WEBRTC_SPL_LSHIFT_W32(streamVal, 8) | - WEBRTC_SPL_RSHIFT_U16(*streamPtr, 8); - streamData->full = 0; + /* Intending to read outside the stream. This can happen for the last + * two or three bytes. It is how the algorithm is implemented. Do + * not read from the bit stream and insert zeros instead. */ + streamVal = WEBRTC_SPL_LSHIFT_W32(streamVal, 8); + if (streamData->full == 0) { + offset++; // We would have incremented the pointer in this case. + streamData->full = 1; + } else { + streamData->full = 0; + } } W_upper = WEBRTC_SPL_LSHIFT_W32(W_upper, 8); } @@ -392,7 +402,7 @@ envCount++; } - streamData->stream_index = streamPtr - streamData->stream; + streamData->stream_index = streamPtr + offset - streamData->stream; streamData->W_upper = W_upper; streamData->streamval = streamVal; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/bandwidth_estimator.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/bandwidth_estimator.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/bandwidth_estimator.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/bandwidth_estimator.c 2015-02-03 14:33:34.000000000 +0000 @@ -196,12 +196,12 @@ bweStr->maxBwInv = kInvBandwidth[3]; bweStr->minBwInv = kInvBandwidth[2]; - bweStr->recBwInv = WEBRTC_SPL_UDIV(1073741824, (bweStr->recBw + bweStr->recHeaderRate)); + bweStr->recBwInv = 1073741824 / (bweStr->recBw + bweStr->recHeaderRate); } /* kBitsByteSec is in Q15 */ - recRtpRate = (int16_t)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(kBitsByteSec, - (int32_t)pksize), 15) + bweStr->recHeaderRate; + recRtpRate = (int16_t)((kBitsByteSec * pksize) >> 15) + + bweStr->recHeaderRate; } else { /* If frameSize changed since last call, from 60 to 30, recalculate some values */ @@ -211,12 +211,12 @@ bweStr->maxBwInv = kInvBandwidth[1]; bweStr->minBwInv = kInvBandwidth[0]; - bweStr->recBwInv = WEBRTC_SPL_UDIV(1073741824, (bweStr->recBw + bweStr->recHeaderRate)); + bweStr->recBwInv = 1073741824 / (bweStr->recBw + bweStr->recHeaderRate); } /* kBitsByteSec is in Q14 */ - recRtpRate = (uint16_t)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(kBitsByteSec, - (int32_t)pksize), 14) + bweStr->recHeaderRate; + recRtpRate = (uint16_t)((kBitsByteSec * pksize) >> 14) + + bweStr->recHeaderRate; } @@ -265,7 +265,7 @@ if ((arrivalTime - bweStr->lastUpdate) > FS3) { /* Calculate expected number of received packets since last update */ - numPktsExpected = WEBRTC_SPL_UDIV(arrivalTime - bweStr->lastUpdate, frameSizeSampl); + numPktsExpected = (arrivalTime - bweStr->lastUpdate) / frameSizeSampl; /* If received number of packets is more than 90% of expected (922 = 0.9 in Q10): */ /* do the update, else not */ @@ -293,12 +293,12 @@ if ( reductionFactor != 0 ) { bweStr->recBwInv = WEBRTC_SPL_MUL((int32_t)bweStr->recBwInv, (int32_t)reductionFactor); - bweStr->recBwInv = WEBRTC_SPL_RSHIFT_W32((int32_t)bweStr->recBwInv, 13); + bweStr->recBwInv = (int32_t)bweStr->recBwInv >> 13; } else { - /* recBwInv = 1 / (INIT_BN_EST + INIT_HDR_RATE) in Q26 (Q30??)*/ - bweStr->recBwInv = WEBRTC_SPL_DIV((1073741824 + - WEBRTC_SPL_LSHIFT_W32(((int32_t)INIT_BN_EST + INIT_HDR_RATE), 1)), INIT_BN_EST + INIT_HDR_RATE); + static const uint32_t kInitRate = INIT_BN_EST + INIT_HDR_RATE; + /* recBwInv = 1 / kInitRate in Q26 (Q30??)*/ + bweStr->recBwInv = (1073741824 + kInitRate / 2) / kInitRate; } /* reset time-since-update counter */ @@ -345,8 +345,8 @@ } } - if ((bweStr->prevRtpRate > WEBRTC_SPL_RSHIFT_W32((int32_t) bweStr->recBwAvg, 5)) && - (recRtpRate > WEBRTC_SPL_RSHIFT_W32((int32_t)bweStr->recBwAvg, 5)) && + if ((bweStr->prevRtpRate > (int32_t)bweStr->recBwAvg >> 5) && + (recRtpRate > (int32_t)bweStr->recBwAvg >> 5) && !bweStr->inWaitPeriod) { /* test if still in initiation period and increment counter */ @@ -356,7 +356,7 @@ } else { /* weight decreases with number of updates, 1/countUpdates in Q13 */ weight = (uint16_t) WebRtcSpl_DivW32W16( - (int32_t)(8192 + WEBRTC_SPL_RSHIFT_W32((int32_t) bweStr->countUpdates, 1)), + 8192 + (bweStr->countUpdates >> 1), (int16_t)bweStr->countUpdates); } @@ -374,8 +374,8 @@ /* compute inverse receiving rate for last packet, in Q19 */ numBytesInv = (uint16_t) WebRtcSpl_DivW32W16( - (int32_t)(524288 + WEBRTC_SPL_RSHIFT_W32(((int32_t)pksize + HEADER_SIZE), 1)), - (int16_t)(pksize + HEADER_SIZE)); + 524288 + ((pksize + HEADER_SIZE) >> 1), + pksize + HEADER_SIZE); /* 8389 is ~ 1/128000 in Q30 */ byteSecondsPerBit = WEBRTC_SPL_MUL_16_16(arrTimeDiff, 8389); @@ -417,8 +417,7 @@ and NOT right shifting recBwAvg 5 bits to an integer At max 13 bits are used shift to Q5 */ - recBwAvgInv = WEBRTC_SPL_UDIV((uint32_t)(0x80000000 + WEBRTC_SPL_RSHIFT_U32(bweStr->recBwAvg, 1)), - bweStr->recBwAvg); + recBwAvgInv = (0x80000000 + bweStr->recBwAvg / 2) / bweStr->recBwAvg; /* Calculate Projected arrival time difference */ @@ -446,12 +445,12 @@ arrTimeNoiseAbs = arrTimeNoise; /* long term averaged absolute jitter, Q15 */ - weight = WEBRTC_SPL_RSHIFT_W32(weight, 3); + weight >>= 3; bweStr->recJitter = WEBRTC_SPL_MUL(weight, WEBRTC_SPL_LSHIFT_W32(arrTimeNoiseAbs, 5)) + WEBRTC_SPL_MUL(1024 - weight, bweStr->recJitter); /* remove the fractional portion */ - bweStr->recJitter = WEBRTC_SPL_RSHIFT_W32(bweStr->recJitter, 10); + bweStr->recJitter >>= 10; /* Maximum jitter is 10 msec in Q15 */ if (bweStr->recJitter > (int32_t)327680) { @@ -462,7 +461,7 @@ /* Calculation in Q13 products in Q23 */ bweStr->recJitterShortTermAbs = WEBRTC_SPL_MUL(51, WEBRTC_SPL_LSHIFT_W32(arrTimeNoiseAbs, 3)) + WEBRTC_SPL_MUL(973, bweStr->recJitterShortTermAbs); - bweStr->recJitterShortTermAbs = WEBRTC_SPL_RSHIFT_W32(bweStr->recJitterShortTermAbs , 10); + bweStr->recJitterShortTermAbs >>= 10; /* short term averaged jitter */ /* Calculation in Q13 products in Q23 */ @@ -471,10 +470,10 @@ if (bweStr->recJitterShortTerm < 0) { temp = -bweStr->recJitterShortTerm; - temp = WEBRTC_SPL_RSHIFT_W32(temp, 12); + temp >>= 12; bweStr->recJitterShortTerm = -temp; } else { - bweStr->recJitterShortTerm = WEBRTC_SPL_RSHIFT_W32(bweStr->recJitterShortTerm, 12); + bweStr->recJitterShortTerm >>= 12; } } } @@ -513,7 +512,7 @@ bweStr->prevSendTime = sendTime; /* Replace bweStr->recBw by the new value */ - bweStr->recBw = WEBRTC_SPL_UDIV(1073741824, bweStr->recBwInv) - bweStr->recHeaderRate; + bweStr->recBw = 1073741824 / bweStr->recBwInv - bweStr->recHeaderRate; if (immediateSet) { /* delay correction factor is in Q10 */ @@ -524,13 +523,13 @@ bweStr->recBw = (int32_t) MIN_ISAC_BW; } - bweStr->recBwAvg = WEBRTC_SPL_LSHIFT_U32(bweStr->recBw + bweStr->recHeaderRate, 5); + bweStr->recBwAvg = (bweStr->recBw + bweStr->recHeaderRate) << 5; - bweStr->recBwAvgQ = WEBRTC_SPL_LSHIFT_U32(bweStr->recBw, 7); + bweStr->recBwAvgQ = bweStr->recBw << 7; bweStr->recJitterShortTerm = 0; - bweStr->recBwInv = WEBRTC_SPL_UDIV(1073741824, bweStr->recBw + bweStr->recHeaderRate); + bweStr->recBwInv = 1073741824 / (bweStr->recBw + bweStr->recHeaderRate); immediateSet = 0; } @@ -559,7 +558,7 @@ /* sendMaxDelayAvg = 0.9 * sendMaxDelayAvg + 0.1 * MAX_ISAC_MD */ bweStr->sendMaxDelayAvg = WEBRTC_SPL_MUL(461, bweStr->sendMaxDelayAvg) + WEBRTC_SPL_MUL(51, WEBRTC_SPL_LSHIFT_W32((int32_t)MAX_ISAC_MD, 9)); - bweStr->sendMaxDelayAvg = WEBRTC_SPL_RSHIFT_W32(bweStr->sendMaxDelayAvg, 9); + bweStr->sendMaxDelayAvg >>= 9; } else { RateInd = Index; @@ -567,15 +566,15 @@ /* sendMaxDelayAvg = 0.9 * sendMaxDelayAvg + 0.1 * MIN_ISAC_MD */ bweStr->sendMaxDelayAvg = WEBRTC_SPL_MUL(461, bweStr->sendMaxDelayAvg) + WEBRTC_SPL_MUL(51, WEBRTC_SPL_LSHIFT_W32((int32_t)MIN_ISAC_MD,9)); - bweStr->sendMaxDelayAvg = WEBRTC_SPL_RSHIFT_W32(bweStr->sendMaxDelayAvg, 9); + bweStr->sendMaxDelayAvg >>= 9; } /* compute the BN estimate as decoded on the other side */ /* sendBwAvg = 0.9 * sendBwAvg + 0.1 * kQRateTable[RateInd]; */ - bweStr->sendBwAvg = WEBRTC_SPL_UMUL(461, bweStr->sendBwAvg) + - WEBRTC_SPL_UMUL(51, WEBRTC_SPL_LSHIFT_U32(kQRateTable[RateInd], 7)); + bweStr->sendBwAvg = 461 * bweStr->sendBwAvg + + 51 * ((uint32_t)kQRateTable[RateInd] << 7); bweStr->sendBwAvg = WEBRTC_SPL_RSHIFT_U32(bweStr->sendBwAvg, 9); @@ -626,8 +625,8 @@ /* Compute the averaged BN estimate on this side */ /* recBwAvg = 0.9 * recBwAvg + 0.1 * (rate + bweStr->recHeaderRate), 0.9 and 0.1 in Q9 */ - bweStr->recBwAvg = WEBRTC_SPL_UMUL(922, bweStr->recBwAvg) + - WEBRTC_SPL_UMUL(102, WEBRTC_SPL_LSHIFT_U32((uint32_t)rate + bweStr->recHeaderRate, 5)); + bweStr->recBwAvg = 922 * bweStr->recBwAvg + + 102 * (((uint32_t)rate + bweStr->recHeaderRate) << 5); bweStr->recBwAvg = WEBRTC_SPL_RSHIFT_U32(bweStr->recBwAvg, 10); /* Find quantization index that gives the closest rate after averaging. @@ -645,7 +644,7 @@ /* 0.9 times recBwAvgQ in Q16 */ /* 461/512 - 25/65536 =0.900009 */ tempTerm1 = WEBRTC_SPL_MUL(bweStr->recBwAvgQ, 25); - tempTerm1 = WEBRTC_SPL_RSHIFT_W32(tempTerm1, 7); + tempTerm1 >>= 7; tempTermX = WEBRTC_SPL_UMUL(461, bweStr->recBwAvgQ) - tempTerm1; /* rate in Q16 */ @@ -668,7 +667,7 @@ tempTermX += KQRate01[rateInd]; /* Shift back to Q7 */ - bweStr->recBwAvgQ = WEBRTC_SPL_RSHIFT_W32(tempTermX, 9); + bweStr->recBwAvgQ = tempTermX >> 9; /* Count consecutive received bandwidth above 28000 kbps (28000 in Q7 = 3584000) */ /* If 66 high estimates in a row, set highSpeedRec to one */ @@ -701,13 +700,13 @@ tempTerm1 = tempTermX + tempMin; /* update quantized average, shift back to Q9 */ - bweStr->recMaxDelayAvgQ = WEBRTC_SPL_RSHIFT_W32(tempTerm1, 9); + bweStr->recMaxDelayAvgQ = tempTerm1 >> 9; } else { maxDelayBit = 12; tempTerm1 = tempTermX + tempMax; /* update quantized average, shift back to Q9 */ - bweStr->recMaxDelayAvgQ = WEBRTC_SPL_RSHIFT_W32(tempTerm1, 9); + bweStr->recMaxDelayAvgQ = tempTerm1 >> 9; } /* Return bandwitdh and jitter index (0..23) */ @@ -725,42 +724,44 @@ /* Q18 rec jitter short term abs is in Q13, multiply it by 2^13 to save precision 2^18 then needs to be shifted 13 bits to 2^31 */ - rec_jitter_short_term_abs_inv = WEBRTC_SPL_UDIV(0x80000000, bweStr->recJitterShortTermAbs); + rec_jitter_short_term_abs_inv = 0x80000000u / bweStr->recJitterShortTermAbs; /* Q27 = 9 + 18 */ - jitter_sign = WEBRTC_SPL_MUL(WEBRTC_SPL_RSHIFT_W32(bweStr->recJitterShortTerm, 4), (int32_t)rec_jitter_short_term_abs_inv); + jitter_sign = (bweStr->recJitterShortTerm >> 4) * + rec_jitter_short_term_abs_inv; if (jitter_sign < 0) { temp = -jitter_sign; - temp = WEBRTC_SPL_RSHIFT_W32(temp, 19); + temp >>= 19; jitter_sign = -temp; } else { - jitter_sign = WEBRTC_SPL_RSHIFT_W32(jitter_sign, 19); + jitter_sign >>= 19; } /* adjust bw proportionally to negative average jitter sign */ //bw_adjust = 1.0f - jitter_sign * (0.15f + 0.15f * jitter_sign * jitter_sign); //Q8 -> Q16 .15 +.15 * jitter^2 first term is .15 in Q16 latter term is Q8*Q8*Q8 //38 in Q8 ~.15 9830 in Q16 ~.15 - temp = 9830 + WEBRTC_SPL_RSHIFT_W32((WEBRTC_SPL_MUL(38, WEBRTC_SPL_MUL(jitter_sign, jitter_sign))), 8); + temp = 9830 + ((38 * jitter_sign * jitter_sign) >> 8); if (jitter_sign < 0) { temp = WEBRTC_SPL_MUL(jitter_sign, temp); temp = -temp; - temp = WEBRTC_SPL_RSHIFT_W32(temp, 8); + temp >>= 8; bw_adjust = (uint32_t)65536 + temp; /* (1 << 16) + temp; */ } else { - bw_adjust = (uint32_t)65536 - WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(jitter_sign, temp), 8);/* (1 << 16) - ((jitter_sign * temp) >> 8); */ + /* (1 << 16) - ((jitter_sign * temp) >> 8); */ + bw_adjust = 65536 - ((jitter_sign * temp) >> 8); } //make sure following multiplication won't overflow //bw adjust now Q14 - bw_adjust = WEBRTC_SPL_RSHIFT_W32(bw_adjust, 2);//see if good resolution is maintained + bw_adjust >>= 2; // See if good resolution is maintained. /* adjust Rate if jitter sign is mostly constant */ recBw = WEBRTC_SPL_UMUL(bweStr->recBw, bw_adjust); - recBw = WEBRTC_SPL_RSHIFT_W32(recBw, 14); + recBw >>= 14; /* limit range of bottle neck rate */ if (recBw < MIN_ISAC_BW) { @@ -775,9 +776,7 @@ /* Returns the mmax delay (in ms) */ int16_t WebRtcIsacfix_GetDownlinkMaxDelay(const BwEstimatorstr *bweStr) { - int16_t recMaxDelay; - - recMaxDelay = (int16_t) WEBRTC_SPL_RSHIFT_W32(bweStr->recMaxDelay, 15); + int16_t recMaxDelay = (int16_t)(bweStr->recMaxDelay >> 15); /* limit range of jitter estimate */ if (recMaxDelay < MIN_ISAC_MD) { @@ -811,9 +810,7 @@ /* Returns the max delay value from the other side in ms */ int16_t WebRtcIsacfix_GetUplinkMaxDelay(const BwEstimatorstr *bweStr) { - int16_t send_max_delay; - - send_max_delay = (int16_t) WEBRTC_SPL_RSHIFT_W32(bweStr->sendMaxDelayAvg, 9); + int16_t send_max_delay = (int16_t)(bweStr->sendMaxDelayAvg >> 9); /* limit range of jitter estimate */ if (send_max_delay < MIN_ISAC_MD) { @@ -855,20 +852,23 @@ } else { /* handle burst */ if (State->BurstCounter) { - if (State->StillBuffered < WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL((512 - WEBRTC_SPL_DIV(512, BURST_LEN)), DelayBuildUp), 9)) { + if (State->StillBuffered < + (((512 - 512 / BURST_LEN) * DelayBuildUp) >> 9)) { /* max bps derived from BottleNeck and DelayBuildUp values */ - inv_Q12 = WEBRTC_SPL_DIV(4096, WEBRTC_SPL_MUL(BURST_LEN, FrameSamples)); - MinRate = WEBRTC_SPL_MUL(512 + WEBRTC_SPL_MUL(SAMPLES_PER_MSEC, WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(DelayBuildUp, inv_Q12), 3)), BottleNeck); + inv_Q12 = 4096 / (BURST_LEN * FrameSamples); + MinRate = (512 + SAMPLES_PER_MSEC * ((DelayBuildUp * inv_Q12) >> 3)) * + BottleNeck; } else { /* max bps derived from StillBuffered and DelayBuildUp values */ - inv_Q12 = WEBRTC_SPL_DIV(4096, FrameSamples); + inv_Q12 = 4096 / FrameSamples; if (DelayBuildUp > State->StillBuffered) { - MinRate = WEBRTC_SPL_MUL(512 + WEBRTC_SPL_MUL(SAMPLES_PER_MSEC, WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(DelayBuildUp - State->StillBuffered, inv_Q12), 3)), BottleNeck); + MinRate = (512 + SAMPLES_PER_MSEC * (((DelayBuildUp - + State->StillBuffered) * inv_Q12) >> 3)) * BottleNeck; } else if ((den = WEBRTC_SPL_MUL(SAMPLES_PER_MSEC, (State->StillBuffered - DelayBuildUp))) >= FrameSamples) { /* MinRate will be negative here */ MinRate = 0; } else { - MinRate = WEBRTC_SPL_MUL((512 - WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(den, inv_Q12), 3)), BottleNeck); + MinRate = (512 - ((den * inv_Q12) >> 3)) * BottleNeck; } //if (MinRate < 1.04 * BottleNeck) // MinRate = 1.04 * BottleNeck; @@ -886,8 +886,8 @@ /* convert rate from bits/second to bytes/packet */ //round and shift before conversion MinRate += 256; - MinRate = WEBRTC_SPL_RSHIFT_W32(MinRate, 9); - MinBytes = (uint16_t)WEBRTC_SPL_UDIV(WEBRTC_SPL_MUL(MinRate, FrameSamples), FS8); + MinRate >>= 9; + MinBytes = MinRate * FrameSamples / FS8; /* StreamSize will be adjusted if less than MinBytes */ if (StreamSize < MinBytes) { @@ -896,20 +896,20 @@ /* keep track of when bottle neck was last exceeded by at least 1% */ //517/512 ~ 1.01 - if (WEBRTC_SPL_DIV(WEBRTC_SPL_MUL(StreamSize, FS8), FrameSamples) > (WEBRTC_SPL_MUL(517, BottleNeck) >> 9)) { + if ((StreamSize * (int32_t)FS8) / FrameSamples > (517 * BottleNeck) >> 9) { if (State->PrevExceed) { /* bottle_neck exceded twice in a row, decrease ExceedAgo */ - State->ExceedAgo -= WEBRTC_SPL_DIV(BURST_INTERVAL, BURST_LEN - 1); + State->ExceedAgo -= BURST_INTERVAL / (BURST_LEN - 1); if (State->ExceedAgo < 0) { State->ExceedAgo = 0; } } else { - State->ExceedAgo += (int16_t)WEBRTC_SPL_RSHIFT_W16(FrameSamples, 4); /* ms */ + State->ExceedAgo += FrameSamples / SAMPLES_PER_MSEC; /* ms */ State->PrevExceed = 1; } } else { State->PrevExceed = 0; - State->ExceedAgo += (int16_t)WEBRTC_SPL_RSHIFT_W16(FrameSamples, 4); /* ms */ + State->ExceedAgo += FrameSamples / SAMPLES_PER_MSEC; /* ms */ } /* set burst flag if bottle neck not exceeded for long time */ @@ -923,9 +923,9 @@ /* Update buffer delay */ - TransmissionTime = (int16_t)WEBRTC_SPL_DIV(WEBRTC_SPL_MUL(StreamSize, 8000), BottleNeck); /* ms */ + TransmissionTime = (StreamSize * 8000) / BottleNeck; /* ms */ State->StillBuffered += TransmissionTime; - State->StillBuffered -= (int16_t)WEBRTC_SPL_RSHIFT_W16(FrameSamples, 4); //>>4 = SAMPLES_PER_MSEC /* ms */ + State->StillBuffered -= FrameSamples / SAMPLES_PER_MSEC; /* ms */ if (State->StillBuffered < 0) { State->StillBuffered = 0; } @@ -946,15 +946,14 @@ const int16_t FrameSamples, /* samples per frame */ const int16_t BottleNeck) /* bottle neck rate; excl headers (bps) */ { - int16_t TransmissionTime; + const int16_t TransmissionTime = (StreamSize * 8000) / BottleNeck; /* ms */ /* avoid the initial "high-rate" burst */ State->InitCounter = 0; /* Update buffer delay */ - TransmissionTime = (int16_t)WEBRTC_SPL_DIV(WEBRTC_SPL_MUL(WEBRTC_SPL_MUL(StreamSize, 8), 1000), BottleNeck); /* ms */ State->StillBuffered += TransmissionTime; - State->StillBuffered -= (int16_t)WEBRTC_SPL_RSHIFT_W16(FrameSamples, 4); /* ms */ + State->StillBuffered -= FrameSamples >> 4; /* ms */ if (State->StillBuffered < 0) { State->StillBuffered = 0; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/codec.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/codec.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/codec.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/codec.h 2015-02-03 14:33:34.000000000 +0000 @@ -101,6 +101,16 @@ int32_t* outre2Q16); #endif +#if defined(MIPS32_LE) +void WebRtcIsacfix_Time2SpecMIPS(int16_t* inre1Q9, + int16_t* inre2Q9, + int16_t* outre, + int16_t* outim); +void WebRtcIsacfix_Spec2TimeMIPS(int16_t* inreQ7, + int16_t* inimQ7, + int32_t* outre1Q16, + int32_t* outre2Q16); +#endif /* filterbank functions */ @@ -175,6 +185,21 @@ int16_t input1, int32_t input2, int32_t* ptr0, + int32_t* ptr1, + int32_t* ptr2); +#endif + +#if defined(MIPS32_LE) +int WebRtcIsacfix_AutocorrMIPS(int32_t* __restrict r, + const int16_t* __restrict x, + int16_t N, + int16_t order, + int16_t* __restrict scale); + +void WebRtcIsacfix_FilterMaLoopMIPS(int16_t input0, + int16_t input1, + int32_t input2, + int32_t* ptr0, int32_t* ptr1, int32_t* ptr2); #endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/decode_bwe.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/decode_bwe.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/decode_bwe.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/decode_bwe.c 2015-02-03 14:33:34.000000000 +0000 @@ -53,7 +53,7 @@ err = WebRtcIsacfix_UpdateUplinkBwImpl( bwest_str, rtp_seq_number, - (uint16_t)WEBRTC_SPL_UDIV(WEBRTC_SPL_UMUL(frame_samples,1000), FS), + frame_samples * 1000 / FS, send_ts, arr_ts, (int16_t) packet_size, /* in bytes */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/decode.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/decode.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/decode.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/decode.c 2015-02-03 14:33:34.000000000 +0000 @@ -54,13 +54,13 @@ int16_t AvgPitchGain_Q12; int16_t tmp_1, tmp_2; - int32_t tmp32a, tmp32b; + int32_t tmp32a; int16_t gainQ13; int16_t frame_nb; /* counter */ - int16_t frame_mode; /* 0 for 20ms and 30ms, 1 for 60ms */ - int16_t processed_samples; + int16_t frame_mode; /* 0 for 30ms, 1 for 60ms */ + static const int16_t kProcessedSamples = 480; /* 480 (for both 30, 60 ms) */ /* PLC */ int16_t overlapWin[ 240 ]; @@ -76,14 +76,14 @@ if (err<0) // error check return err; - frame_mode = (int16_t)WEBRTC_SPL_DIV(*current_framesamples, MAX_FRAMESAMPLES); /* 0, or 1 */ - processed_samples = (int16_t)WEBRTC_SPL_DIV(*current_framesamples, frame_mode+1); /* either 320 (20ms) or 480 (30, 60 ms) */ + frame_mode = *current_framesamples / MAX_FRAMESAMPLES; /* 0, or 1 */ err = WebRtcIsacfix_DecodeSendBandwidth(&ISACdec_obj->bitstr_obj, &BWno); if (err<0) // error check return err; - /* one loop if it's one frame (20 or 30ms), 2 loops if 2 frames bundled together (60ms) */ + /* one loop if it's one frame (30ms), two loops if two frames bundled together + * (60ms) */ for (frame_nb = 0; frame_nb <= frame_mode; frame_nb++) { /* decode & dequantize pitch parameters */ @@ -113,7 +113,8 @@ WebRtcIsacfix_Spec2Time(Vector_Word16_1, Vector_Word16_2, Vector_Word32_1, Vector_Word32_2); for (k=0; k Q9 + // Q16 -> Q9. + Vector_Word16_1[k] = (int16_t)((Vector_Word32_1[k] + 64) >> 7); } /* ---- If this is recovery frame ---- */ @@ -134,7 +135,7 @@ /* ---- Add-overlap ---- */ WebRtcSpl_GetHanningWindow( overlapWin, RECOVERY_OVERLAP ); for( k = 0; k < RECOVERY_OVERLAP; k++ ) - Vector_Word16_1[k] = WEBRTC_SPL_ADD_SAT_W16( + Vector_Word16_1[k] = WebRtcSpl_AddSatW16( (int16_t)WEBRTC_SPL_MUL_16_16_RSFT( (ISACdec_obj->plcstr_obj).overlapLP[k], overlapWin[RECOVERY_OVERLAP - k - 1], 14), (int16_t)WEBRTC_SPL_MUL_16_16_RSFT( Vector_Word16_1[k], overlapWin[k], 14) ); @@ -176,8 +177,7 @@ /* reduce gain to compensate for pitch enhancer */ /* gain = 1.0f - 0.45f * AvgPitchGain; */ tmp32a = WEBRTC_SPL_MUL_16_16_RSFT(AvgPitchGain_Q12, 29, 0); // Q18 - tmp32b = 262144 - tmp32a; // Q18 - gainQ13 = (int16_t) WEBRTC_SPL_RSHIFT_W32(tmp32b, 5); // Q13 + gainQ13 = (int16_t)((262144 - tmp32a) >> 5); // Q18 -> Q13. for (k = 0; k < FRAMESAMPLES/2; k++) { @@ -210,7 +210,10 @@ Vector_Word16_2[k] = tmp_2; } - WebRtcIsacfix_FilterAndCombine1(Vector_Word16_1, Vector_Word16_2, signal_out16 + frame_nb * processed_samples, &ISACdec_obj->postfiltbankstr_obj); + WebRtcIsacfix_FilterAndCombine1(Vector_Word16_1, + Vector_Word16_2, + signal_out16 + frame_nb * kProcessedSamples, + &ISACdec_obj->postfiltbankstr_obj); } return len; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/decode_plc.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/decode_plc.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/decode_plc.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/decode_plc.c 2015-02-03 14:33:34.000000000 +0000 @@ -59,19 +59,19 @@ for (j = 0;j < Blen; j++) { - o = WEBRTC_SPL_ADD_SAT_W32( o, WEBRTC_SPL_MUL_16_16( *b_ptr, *x_ptr) ); + o = WebRtcSpl_AddSatW32(o, WEBRTC_SPL_MUL_16_16(*b_ptr, *x_ptr)); b_ptr++; x_ptr--; } /* to round off correctly */ - o = WEBRTC_SPL_ADD_SAT_W32( o, WEBRTC_SPL_LSHIFT_W32( 1, (rshift-1) ) ); + o = WebRtcSpl_AddSatW32(o, 1 << (rshift - 1)); /* saturate according to the domain of the filter coefficients */ o = WEBRTC_SPL_SAT((int32_t)lim, o, (int32_t)-lim); /* o should be in the range of int16_t */ - o = WEBRTC_SPL_RSHIFT_W32( o, rshift ); + o >>= rshift; /* decay the output signal; this is specific to plc */ *Out++ = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT( (int16_t)o, decay, 15); // ((o + (int32_t)2048) >> 12); @@ -93,16 +93,14 @@ static __inline int32_t log2_Q8_T( uint32_t x ) { - int32_t zeros, lg2; + int32_t zeros; int16_t frac; zeros=WebRtcSpl_NormU32(x); - frac=(int16_t)WEBRTC_SPL_RSHIFT_W32(((uint32_t)WEBRTC_SPL_LSHIFT_W32(x, zeros)&0x7FFFFFFF), 23); - /* log2(magn(i)) */ - - lg2= (WEBRTC_SPL_LSHIFT_W16((31-zeros), 8)+frac); - return lg2; + frac = (int16_t)(((x << zeros) & 0x7FFFFFFF) >> 23); + /* log2(magn(i)) */ + return ((31 - zeros) << 8) + frac; } static __inline int16_t exp2_Q10_T(int16_t x) { // Both in and out in Q10 @@ -110,11 +108,11 @@ int16_t tmp16_1, tmp16_2; tmp16_2=(int16_t)(0x0400|(x&0x03FF)); - tmp16_1=-(int16_t)WEBRTC_SPL_RSHIFT_W16(x,10); + tmp16_1 = -(x >> 10); if(tmp16_1>0) - return (int16_t) WEBRTC_SPL_RSHIFT_W16(tmp16_2, tmp16_1); + return tmp16_2 >> tmp16_1; else - return (int16_t) WEBRTC_SPL_LSHIFT_W16(tmp16_2, -tmp16_1); + return tmp16_2 << -tmp16_1; } @@ -147,7 +145,7 @@ WEBRTC_SPL_MUL_16_16_RSFT( in, 983, 12) ); /* b = x^2 / 2 {in Q15} so a shift of 16 is required to be in correct domain and one more for the division by 2 */ - *B = (int16_t)WEBRTC_SPL_RSHIFT_W32( WEBRTC_SPL_MUL_16_16( x, x ) + 0x00010000, 17 ); + *B = (int16_t)((x * x + 0x00010000) >> 17); *A = WEBRTC_SPL_WORD16_MAX - *B; } else @@ -166,7 +164,7 @@ WEBRTC_SPL_MUL_16_16_RSFT( in, 983, 12) ); /* b = x^2 / 2 {in Q15} so a shift of 16 is required to be in correct domain and one more for the division by 2 */ - *A = (int16_t)WEBRTC_SPL_RSHIFT_W32( WEBRTC_SPL_MUL_16_16( x, x ) + 0x00010000, 17 ); + *A = (int16_t)((x * x + 0x00010000) >> 17); *B = WEBRTC_SPL_WORD16_MAX - *A; } @@ -305,8 +303,7 @@ - lag0 = WEBRTC_SPL_RSHIFT_W16( - (ISACdec_obj->plcstr_obj).lastPitchLag_Q7 + 64, 7 ) + 1; + lag0 = ((ISACdec_obj->plcstr_obj.lastPitchLag_Q7 + 64) >> 7) + 1; if( (ISACdec_obj->plcstr_obj).used != PLC_WAS_USED ) @@ -325,8 +322,8 @@ corr = 0; for( k = 0; k < lag0; k++ ) { - corr = WEBRTC_SPL_ADD_SAT_W32( corr, WEBRTC_SPL_ABS_W32( - WEBRTC_SPL_SUB_SAT_W16( + corr = WebRtcSpl_AddSatW32(corr, WEBRTC_SPL_ABS_W32( + WebRtcSpl_SubSatW16( (ISACdec_obj->plcstr_obj).lastPitchLP[k], (ISACdec_obj->plcstr_obj).prevPitchInvIn[ FRAMESAMPLES_HALF - 2*lag0 - 10 + i + k ] ) ) ); @@ -473,27 +470,23 @@ /* --- Low Pass */ (ISACdec_obj->plcstr_obj).seed = WEBRTC_SPL_RAND( (ISACdec_obj->plcstr_obj).seed ); - Vector_Word16_1[i] = WEBRTC_SPL_RSHIFT_W16( - (ISACdec_obj->plcstr_obj).seed, 10 ) - 16; + Vector_Word16_1[i] = (ISACdec_obj->plcstr_obj.seed >> 10) - 16; /* --- Highpass */ (ISACdec_obj->plcstr_obj).seed = WEBRTC_SPL_RAND( (ISACdec_obj->plcstr_obj).seed ); - Vector_Word16_2[i] = WEBRTC_SPL_RSHIFT_W16( - (ISACdec_obj->plcstr_obj).seed, 10 ) - 16; + Vector_Word16_2[i] = (ISACdec_obj->plcstr_obj.seed >> 10) - 16; } for( i = 1; i < NOISE_FILTER_LEN; i++ ) { (ISACdec_obj->plcstr_obj).seed = WEBRTC_SPL_RAND( (ISACdec_obj->plcstr_obj).seed ); - Vector_Word16_Extended_1[ i ] = WEBRTC_SPL_RSHIFT_W16( - (ISACdec_obj->plcstr_obj).seed, 10 ) - 16; + Vector_Word16_Extended_1[i] = (ISACdec_obj->plcstr_obj.seed >> 10) - 16; (ISACdec_obj->plcstr_obj).seed = WEBRTC_SPL_RAND( (ISACdec_obj->plcstr_obj).seed ); - Vector_Word16_Extended_2[ i ] = WEBRTC_SPL_RSHIFT_W16( - (ISACdec_obj->plcstr_obj).seed, 10 ) - 16; + Vector_Word16_Extended_2[i] = (ISACdec_obj->plcstr_obj.seed >> 10) - 16; } plc_filterma_Fast(Vector_Word16_1, Vector_Word16_Extended_1, &(ISACdec_obj->plcstr_obj).prevPitchInvIn[FRAMESAMPLES_HALF - @@ -508,14 +501,13 @@ rshift = 0; while( maxCoeff > WEBRTC_SPL_WORD16_MAX ) { - maxCoeff = WEBRTC_SPL_RSHIFT_W32(maxCoeff, 1); + maxCoeff >>= 1; rshift++; } for( i = 0; i < NOISE_FILTER_LEN; i++ ) { - Vector_Word16_1[ FRAMESAMPLES_HALF - NOISE_FILTER_LEN + i] = - (int16_t)WEBRTC_SPL_RSHIFT_W32( - (ISACdec_obj->plcstr_obj).prevHP[ - PITCH_MAX_LAG + 10 - NOISE_FILTER_LEN + i], rshift); + Vector_Word16_1[FRAMESAMPLES_HALF - NOISE_FILTER_LEN + i] =(int16_t)( + ISACdec_obj->plcstr_obj.prevHP[PITCH_MAX_LAG + 10 - NOISE_FILTER_LEN + + i] >> rshift); } (ISACdec_obj->plcstr_obj).decayCoeffNoise = plc_filterma_Fast( Vector_Word16_2, @@ -638,8 +630,7 @@ (ISACdec_obj->plcstr_obj).seed = WEBRTC_SPL_RAND( (ISACdec_obj->plcstr_obj).seed ); - noise1 = WEBRTC_SPL_RSHIFT_W16( - (ISACdec_obj->plcstr_obj).seed, 10 ) - 16; + noise1 = (ISACdec_obj->plcstr_obj.seed >> 10) - 16; nLP = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT( (int16_t)((noise1)*(ISACdec_obj->plcstr_obj).std), @@ -648,8 +639,7 @@ /* --- Highpass */ (ISACdec_obj->plcstr_obj).seed = WEBRTC_SPL_RAND( (ISACdec_obj->plcstr_obj).seed ); - noise1 = WEBRTC_SPL_RSHIFT_W16( - (ISACdec_obj->plcstr_obj).seed, 11 ) - 8; + noise1 = (ISACdec_obj->plcstr_obj.seed >> 11) - 8; nHP = (int32_t)WEBRTC_SPL_MUL_16_32_RSFT15( (ISACdec_obj->plcstr_obj).decayCoeffNoise, @@ -756,10 +746,8 @@ } /* ------ Sum the noisy and periodic signals ------ */ - Vector_Word16_1[i] = (int16_t)WEBRTC_SPL_ADD_SAT_W16( - wNoisyLP, wPriodicLP ); - Vector_Word32_2[i] = (int32_t)WEBRTC_SPL_ADD_SAT_W32( - wNoisyHP, wPriodicHP ); + Vector_Word16_1[i] = WebRtcSpl_AddSatW16(wNoisyLP, wPriodicLP); + Vector_Word32_2[i] = WebRtcSpl_AddSatW32(wNoisyHP, wPriodicHP); } } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/encode.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/encode.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/encode.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/encode.c 2015-02-03 14:33:34.000000000 +0000 @@ -15,18 +15,21 @@ * */ -#include "arith_routins.h" -#include "bandwidth_estimator.h" -#include "codec.h" -#include "pitch_gain_tables.h" -#include "pitch_lag_tables.h" -#include "entropy_coding.h" -#include "lpc_tables.h" -#include "lpc_masking_model.h" -#include "pitch_estimator.h" -#include "structs.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/codec.h" + +#include #include +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/arith_routins.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/bandwidth_estimator.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/entropy_coding.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_tables.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_gain_tables.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_lag_tables.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/structs.h" + int WebRtcIsacfix_EncodeImpl(int16_t *in, ISACFIX_EncInst_t *ISACenc_obj, @@ -191,7 +194,8 @@ } return status; } - AvgPitchGain_Q12 = WEBRTC_SPL_RSHIFT_W32(PitchGains_Q12[0] + PitchGains_Q12[1] + PitchGains_Q12[2] + PitchGains_Q12[3], 2); + AvgPitchGain_Q12 = (PitchGains_Q12[0] + PitchGains_Q12[1] + + PitchGains_Q12[2] + PitchGains_Q12[3]) >> 2; /* find coefficients for perceptual pre-filters */ WebRtcIsacfix_GetLpcCoef(LPandHP, HP16a+QLOOKAHEAD, &ISACenc_obj->maskfiltstr_obj, @@ -450,12 +454,15 @@ while (stream_length < MinBytes) { + assert(stream_length >= 0); if (stream_length & 0x0001){ ISACenc_obj->bitstr_seed = WEBRTC_SPL_RAND( ISACenc_obj->bitstr_seed ); - ISACenc_obj->bitstr_obj.stream[ WEBRTC_SPL_RSHIFT_W16(stream_length, 1) ] |= (uint16_t)(ISACenc_obj->bitstr_seed & 0xFF); + ISACenc_obj->bitstr_obj.stream[stream_length / 2] |= + (uint16_t)(ISACenc_obj->bitstr_seed & 0xFF); } else { ISACenc_obj->bitstr_seed = WEBRTC_SPL_RAND( ISACenc_obj->bitstr_seed ); - ISACenc_obj->bitstr_obj.stream[ WEBRTC_SPL_RSHIFT_W16(stream_length, 1) ] = WEBRTC_SPL_LSHIFT_U16(ISACenc_obj->bitstr_seed, 8); + ISACenc_obj->bitstr_obj.stream[stream_length / 2] = + ((uint16_t)ISACenc_obj->bitstr_seed << 8); } stream_length++; } @@ -467,7 +474,8 @@ } else { ISACenc_obj->bitstr_obj.stream[usefulstr_len>>1] &= 0x00FF; - ISACenc_obj->bitstr_obj.stream[usefulstr_len>>1] += WEBRTC_SPL_LSHIFT_U16((MinBytes - usefulstr_len) & 0x00FF, 8); + ISACenc_obj->bitstr_obj.stream[usefulstr_len >> 1] += + ((uint16_t)((MinBytes - usefulstr_len) & 0x00FF) << 8); } } else diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/entropy_coding.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/entropy_coding.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/entropy_coding.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/entropy_coding.c 2015-02-03 14:33:34.000000000 +0000 @@ -72,13 +72,7 @@ */ static __inline int32_t CalcLrIntQ(int32_t fixVal, int16_t qDomain) { - int32_t intgr; - int32_t roundVal; - - roundVal = WEBRTC_SPL_LSHIFT_W32((int32_t)1, qDomain-1); - intgr = WEBRTC_SPL_RSHIFT_W32(fixVal+roundVal, qDomain); - - return intgr; + return (fixVal + (1 << (qDomain - 1))) >> qDomain; } /* @@ -142,7 +136,7 @@ if (x>=0) { // ax=(int16_t)WEBRTC_SPL_MUL_16_16_RSFT(x, 23637-700, 14); //Q8 ax=(int16_t)WEBRTC_SPL_MUL_16_16_RSFT(x, 23637, 14); //Q8 - axINT = WEBRTC_SPL_RSHIFT_W16(ax, 8); //Q0 + axINT = ax >> 8; //Q0 axFRAC = ax&0x00FF; exp16 = WEBRTC_SPL_LSHIFT_W32(1, axINT); //Q0 axFRAC = axFRAC+256; //Q8 @@ -152,12 +146,12 @@ // ax=(int16_t)WEBRTC_SPL_MUL_16_16_RSFT(x, 23637+700, 14); //Q8 ax=(int16_t)WEBRTC_SPL_MUL_16_16_RSFT(x, 23637, 14); //Q8 ax = -ax; - axINT = 1 + WEBRTC_SPL_RSHIFT_W16(ax, 8); //Q0 + axINT = 1 + (ax >> 8); //Q0 axFRAC = 0x00FF - (ax&0x00FF); - exp16 = (int16_t) WEBRTC_SPL_RSHIFT_W32(32768, axINT); //Q15 + exp16 = (int16_t)(32768 >> axINT); // Q15 axFRAC = axFRAC+256; //Q8 exp = WEBRTC_SPL_MUL_16_16(exp16, axFRAC); // Q15*Q8 = Q23 - exp = WEBRTC_SPL_RSHIFT_W32(exp, 6); //Q17 + exp >>= 6; // Q17 } return exp; @@ -173,8 +167,8 @@ int k, n; for (k = 0; k < FRAMESAMPLES/8; k++) { - summ[k] = WEBRTC_SPL_RSHIFT_W32(PSpecQ12[k] + PSpecQ12[FRAMESAMPLES/4-1 - k] + 16, 5); - diff[k] = WEBRTC_SPL_RSHIFT_W32(PSpecQ12[k] - PSpecQ12[FRAMESAMPLES/4-1 - k] + 16, 5); + summ[k] = (PSpecQ12[k] + PSpecQ12[FRAMESAMPLES / 4 - 1 - k] + 16) >> 5; + diff[k] = (PSpecQ12[k] - PSpecQ12[FRAMESAMPLES / 4 - 1 - k] + 16) >> 5; } sum = 2; @@ -185,14 +179,14 @@ for (k = 0; k < AR_ORDER; k += 2) { sum = 0; for (n = 0; n < FRAMESAMPLES/8; n++) - sum += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(WebRtcIsacfix_kCos[k][n], diff[n]) + 256, 9); + sum += (WebRtcIsacfix_kCos[k][n] * diff[n] + 256) >> 9; CorrQ7[k+1] = sum; } for (k=1; k> 9; CorrQ7[k+1] = sum; } } @@ -213,12 +207,12 @@ sum = 0; for (n = 0; n < AR_ORDER+1; n++) sum += WEBRTC_SPL_MUL(ARCoefQ12[n], ARCoefQ12[n]); /* Q24 */ - sum = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(WEBRTC_SPL_RSHIFT_W32(sum, 6), 65) + 32768, 16); /* result in Q8 */ - CorrQ11[0] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(sum, gainQ10) + 256, 9); + sum = ((sum >> 6) * 65 + 32768) >> 16; /* Result in Q8. */ + CorrQ11[0] = (sum * gainQ10 + 256) >> 9; /* To avoid overflow, we shift down gainQ10 if it is large. We will not lose any precision */ if(gainQ10>400000){ - tmpGain = WEBRTC_SPL_RSHIFT_W32(gainQ10, 3); + tmpGain = gainQ10 >> 3; round = 32; shftVal = 6; } else { @@ -231,8 +225,8 @@ sum = 16384; for (n = k; n < AR_ORDER+1; n++) sum += WEBRTC_SPL_MUL(ARCoefQ12[n-k], ARCoefQ12[n]); /* Q24 */ - sum = WEBRTC_SPL_RSHIFT_W32(sum, 15); - CorrQ11[k] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(sum, tmpGain) + round, shftVal); + sum >>= 15; + CorrQ11[k] = (sum * tmpGain + round) >> shftVal; } sum = WEBRTC_SPL_LSHIFT_W32(CorrQ11[0], 7); for (n = 0; n < FRAMESAMPLES/8; n++) @@ -240,7 +234,7 @@ for (k = 1; k < AR_ORDER; k += 2) { for (n = 0; n < FRAMESAMPLES/8; n++) - CurveQ16[n] += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(WebRtcIsacfix_kCos[k][n], CorrQ11[k+1]) + 2, 2); + CurveQ16[n] += (WebRtcIsacfix_kCos[k][n] * CorrQ11[k + 1] + 2) >> 2; } CS_ptrQ9 = WebRtcIsacfix_kCos[0]; @@ -256,11 +250,11 @@ shftVal = 0; for (n = 0; n < FRAMESAMPLES/8; n++) - diffQ16[n] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(CS_ptrQ9[n], WEBRTC_SPL_RSHIFT_W32(CorrQ11[1], shftVal)) + 2, 2); + diffQ16[n] = (CS_ptrQ9[n] * (CorrQ11[1] >> shftVal) + 2) >> 2; for (k = 2; k < AR_ORDER; k += 2) { CS_ptrQ9 = WebRtcIsacfix_kCos[k]; for (n = 0; n < FRAMESAMPLES/8; n++) - diffQ16[n] += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(CS_ptrQ9[n], WEBRTC_SPL_RSHIFT_W32(CorrQ11[k+1], shftVal)) + 2, 2); + diffQ16[n] += (CS_ptrQ9[n] * (CorrQ11[k + 1] >> shftVal) + 2) >> 2; } for (k=0; k> 6) * 65 + 32768) >> 16; /* Result in Q8. */ + CorrQ11[0] = (sum * gainQ10 + 256) >> 9; /* To avoid overflow, we shift down gainQ10 if it is large. We will not lose any precision */ if(gainQ10>400000){ - tmpGain = WEBRTC_SPL_RSHIFT_W32(gainQ10, 3); + tmpGain = gainQ10 >> 3; round = 32; shftVal = 6; } else { @@ -304,8 +298,8 @@ sum = 16384; for (n = k; n < AR_ORDER+1; n++) sum += WEBRTC_SPL_MUL(ARCoefQ12[n-k], ARCoefQ12[n]); /* Q24 */ - sum = WEBRTC_SPL_RSHIFT_W32(sum, 15); - CorrQ11[k] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(sum, tmpGain) + round, shftVal); + sum >>= 15; + CorrQ11[k] = (sum * tmpGain + round) >> shftVal; } sum = WEBRTC_SPL_LSHIFT_W32(CorrQ11[0], 7); for (n = 0; n < FRAMESAMPLES/8; n++) @@ -313,7 +307,7 @@ for (k = 1; k < (AR_ORDER); k += 2) { for (n = 0; n < FRAMESAMPLES/8; n++) - summQ16[n] += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_32_16(CorrQ11[k+1],WebRtcIsacfix_kCos[k][n]) + 2, 2); + summQ16[n] += ((CorrQ11[k + 1] * WebRtcIsacfix_kCos[k][n]) + 2) >> 2; } CS_ptrQ9 = WebRtcIsacfix_kCos[0]; @@ -329,17 +323,17 @@ shftVal = 0; for (n = 0; n < FRAMESAMPLES/8; n++) - diffQ16[n] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(CS_ptrQ9[n], WEBRTC_SPL_RSHIFT_W32(CorrQ11[1], shftVal)) + 2, 2); + diffQ16[n] = (CS_ptrQ9[n] * (CorrQ11[1] >> shftVal) + 2) >> 2; for (k = 2; k < AR_ORDER; k += 2) { CS_ptrQ9 = WebRtcIsacfix_kCos[k]; for (n = 0; n < FRAMESAMPLES/8; n++) - diffQ16[n] += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(CS_ptrQ9[n], WEBRTC_SPL_RSHIFT_W32(CorrQ11[k+1], shftVal)) + 2, 2); + diffQ16[n] += (CS_ptrQ9[n] * (CorrQ11[k + 1] >> shftVal) + 2) >> 2; } in_sqrt = summQ16[0] + WEBRTC_SPL_LSHIFT_W32(diffQ16[0], shftVal); /* convert to magnitude spectrum, by doing square-roots (modified from SPLIB) */ - res = WEBRTC_SPL_LSHIFT_W32(1, WEBRTC_SPL_RSHIFT_W16(WebRtcSpl_GetSizeInBits(in_sqrt), 1)); + res = 1 << (WebRtcSpl_GetSizeInBits(in_sqrt) >> 1); for (k = 0; k < FRAMESAMPLES/8; k++) { @@ -350,11 +344,11 @@ if(in_sqrt<0) in_sqrt=-in_sqrt; - newRes = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_DIV(in_sqrt, res) + res, 1); + newRes = (in_sqrt / res + res) >> 1; do { res = newRes; - newRes = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_DIV(in_sqrt, res) + res, 1); + newRes = (in_sqrt / res + res) >> 1; } while (newRes != res && i-- > 0); CurveQ8[k] = (int16_t)newRes; @@ -368,11 +362,11 @@ if(in_sqrt<0) in_sqrt=-in_sqrt; - newRes = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_DIV(in_sqrt, res) + res, 1); + newRes = (in_sqrt / res + res) >> 1; do { res = newRes; - newRes = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_DIV(in_sqrt, res) + res, 1); + newRes = (in_sqrt / res + res) >> 1; } while (newRes != res && i-- > 0); CurveQ8[k] = (int16_t)newRes; @@ -399,13 +393,13 @@ seed = WEBRTC_SPL_UMUL(seed, 196314165) + 907633515; /* fixed-point dither sample between -64 and 64 (Q7) */ - dither1_Q7 = (int16_t)WEBRTC_SPL_RSHIFT_W32((int32_t)seed + 16777216, 25); // * 128/4294967295 + dither1_Q7 = (int16_t)(((int32_t)seed + 16777216) >> 25); /* new random unsigned int32_t */ seed = WEBRTC_SPL_UMUL(seed, 196314165) + 907633515; /* fixed-point dither sample between -64 and 64 */ - dither2_Q7 = (int16_t)WEBRTC_SPL_RSHIFT_W32(seed + 16777216, 25); + dither2_Q7 = (int16_t)((seed + 16777216) >> 25); shft = (int16_t)(WEBRTC_SPL_RSHIFT_U32(seed, 25) & 15); if (shft < 5) @@ -439,12 +433,12 @@ seed = WEBRTC_SPL_UMUL(seed, 196314165) + 907633515; /* fixed-point dither sample between -64 and 64 */ - dither1_Q7 = (int16_t)WEBRTC_SPL_RSHIFT_W32((int32_t)seed + 16777216, 25); + dither1_Q7 = (int16_t)(((int32_t)seed + 16777216) >> 25); /* dither sample is placed in either even or odd index */ shft = (int16_t)(WEBRTC_SPL_RSHIFT_U32(seed, 25) & 1); /* either 0 or 1 */ - bufQ7[k + shft] = (int16_t)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(dither_gain_Q14, dither1_Q7) + 8192, 14); + bufQ7[k + shft] = (int16_t)((dither_gain_Q14 * dither1_Q7 + 8192) >> 14); bufQ7[k + 1 - shft] = 0; } } @@ -501,10 +495,10 @@ { gainQ10 = WebRtcSpl_DivW32W16ResW16(WEBRTC_SPL_LSHIFT_W32((int32_t)30, 10), (int16_t)WEBRTC_SPL_RSHIFT_U32(invARSpec2_Q16[k>>2] + (uint32_t)2195456, 16)); - *frQ7++ = (int16_t)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(data[ k ], gainQ10) + 512, 10); - *fiQ7++ = (int16_t)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(data[k+1], gainQ10) + 512, 10); - *frQ7++ = (int16_t)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(data[k+2], gainQ10) + 512, 10); - *fiQ7++ = (int16_t)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(data[k+3], gainQ10) + 512, 10); + *frQ7++ = (int16_t)((data[k] * gainQ10 + 512) >> 10); + *fiQ7++ = (int16_t)((data[k + 1] * gainQ10 + 512) >> 10); + *frQ7++ = (int16_t)((data[k + 2] * gainQ10 + 512) >> 10); + *fiQ7++ = (int16_t)((data[k + 3] * gainQ10 + 512) >> 10); } } else @@ -513,10 +507,10 @@ { gainQ10 = WebRtcSpl_DivW32W16ResW16(WEBRTC_SPL_LSHIFT_W32((int32_t)36, 10), (int16_t)WEBRTC_SPL_RSHIFT_U32(invARSpec2_Q16[k>>2] + (uint32_t)2654208, 16)); - *frQ7++ = (int16_t)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(data[ k ], gainQ10) + 512, 10); - *fiQ7++ = (int16_t)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(data[k+1], gainQ10) + 512, 10); - *frQ7++ = (int16_t)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(data[k+2], gainQ10) + 512, 10); - *fiQ7++ = (int16_t)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(data[k+3], gainQ10) + 512, 10); + *frQ7++ = (int16_t)((data[k] * gainQ10 + 512) >> 10); + *fiQ7++ = (int16_t)((data[k + 1] * gainQ10 + 512) >> 10); + *frQ7++ = (int16_t)((data[k + 2] * gainQ10 + 512) >> 10); + *fiQ7++ = (int16_t)((data[k + 3] * gainQ10 + 512) >> 10); } } @@ -584,7 +578,7 @@ CorrQ7_norm[k] = WEBRTC_SPL_LSHIFT_W32(CorrQ7[k], lft_shft); } else { for (k=0; k> -lft_shft; } /* find RC coefficients */ @@ -603,20 +597,22 @@ nrg = 0; for (j = 0; j <= AR_ORDER; j++) { for (n = 0; n <= j; n++) - nrg += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(ARCoefQ12[j], WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(CorrQ7_norm[j-n], ARCoefQ12[n]) + 256, 9)) + 4, 3); + nrg += (ARCoefQ12[j] * ((CorrQ7_norm[j - n] * ARCoefQ12[n] + 256) >> 9) + + 4) >> 3; for (n = j+1; n <= AR_ORDER; n++) - nrg += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(ARCoefQ12[j], WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(CorrQ7_norm[n-j], ARCoefQ12[n]) + 256, 9)) + 4, 3); + nrg += (ARCoefQ12[j] * ((CorrQ7_norm[n - j] * ARCoefQ12[n] + 256) >> 9) + + 4) >> 3; } if (lft_shft > 0) - nrg = WEBRTC_SPL_RSHIFT_W32(nrg, lft_shft); + nrg >>= lft_shft; else nrg = WEBRTC_SPL_LSHIFT_W32(nrg, -lft_shft); if(nrg>131072) gain2_Q10 = WebRtcSpl_DivResultInQ31(FRAMESAMPLES >> 2, nrg); /* also shifts 31 bits to the left! */ else - gain2_Q10 = WEBRTC_SPL_RSHIFT_W32(FRAMESAMPLES, 2); + gain2_Q10 = FRAMESAMPLES >> 2; /* quantize & code gain2_Q10 */ if (WebRtcIsacfix_EncodeGain2(&gain2_Q10, streamdata)) @@ -715,20 +711,20 @@ for (k = 0; k < order; k++) { - larAbsQ11 = (int16_t) WEBRTC_SPL_ABS_W32(WEBRTC_SPL_RSHIFT_W32(larQ17[k]+32,6)); //Q11 + larAbsQ11 = (int16_t)WEBRTC_SPL_ABS_W32((larQ17[k] + 32) >> 6); // Q11 if (larAbsQ11<4097) { //2.000012018559 in Q11 // Q11*Q16>>12 = Q15 rc = WEBRTC_SPL_MUL_16_16_RSFT(larAbsQ11, 24957, 12); } else if (larAbsQ11<6393) { //3.121320351712 in Q11 // (Q11*Q17 + Q13)>>13 = Q15 - rc = WEBRTC_SPL_RSHIFT_W32((WEBRTC_SPL_MUL_16_16(larAbsQ11, 17993) + 130738688), 13); + rc = (larAbsQ11 * 17993 + 130738688) >> 13; } else if (larAbsQ11<11255) { //5.495270168700 in Q11 // (Q11*Q19 + Q30)>>15 = Q15 - rc = WEBRTC_SPL_RSHIFT_W32((WEBRTC_SPL_MUL_16_16(larAbsQ11, 16850) + 875329820), 15); + rc = (larAbsQ11 * 16850 + 875329820) >> 15; } else { // (Q11*Q24>>16 + Q19)>>4 = Q15 - rc = WEBRTC_SPL_RSHIFT_W32(((WEBRTC_SPL_MUL_16_16_RSFT(larAbsQ11, 24433, 16)) + 515804), 4); + rc = (((larAbsQ11 * 24433) >> 16) + 515804) >> 4; } if (larQ17[k]<=0) { @@ -1020,14 +1016,16 @@ for (k=0; k> 11); sumQQ16 += WebRtcIsacfix_kMeansGainQ8[model][posg]; sumQQ = CalcExpN(sumQQ16); // Q8 in and Q17 out gain_lo_hiQ17[gainpos] = sumQQ; //Q17 gainpos++; posg++; - sumQQ16 = (int16_t) WEBRTC_SPL_RSHIFT_W32(tmpcoeffs_gQ17[posg], 2+9); //Divide by 4 and get Q17 to Q8, i.e. shift 2+9 + // Divide by 4 and get Q17 to Q8, i.e. shift 2+9. + sumQQ16 = (int16_t)(tmpcoeffs_gQ17[posg] >> 11); sumQQ16 += WebRtcIsacfix_kMeansGainQ8[model][posg]; sumQQ = CalcExpN(sumQQ16); // Q8 in and Q17 out gain_lo_hiQ17[gainpos] = sumQQ; //Q17 @@ -1321,7 +1319,8 @@ gainpos = 0; for (k=0; k<2*SUBFRAMES; k++) { - sumQQ16 = (int16_t) WEBRTC_SPL_RSHIFT_W32(tmpcoeffs_gQ17[posg], 2+9); //Divide by 4 and get Q17 to Q8, i.e. shift 2+9 + // Divide by 4 and get Q17 to Q8, i.e. shift 2+9. + sumQQ16 = (int16_t)(tmpcoeffs_gQ17[posg] >> 11); sumQQ16 += WebRtcIsacfix_kMeansGainQ8[0][posg]; sumQQ = CalcExpN(sumQQ16); // Q8 in and Q17 out gain_lo_hiQ17[gainpos] = sumQQ; //Q17 @@ -1687,7 +1686,7 @@ for (k = 0; k < 4; k++) meangainQ12 += PitchGain_Q12[k]; - meangainQ12 = WEBRTC_SPL_RSHIFT_W32(meangainQ12, 2); // Get average + meangainQ12 >>= 2; // Get average. /* voicing classificiation */ if (meangainQ12 <= 819) { // mean_gain < 0.2 @@ -1731,21 +1730,21 @@ CQ11 = WEBRTC_SPL_SHIFT_W32(CQ11,11-shft); // Scale with StepSize, Q11 for (k=0; k> 5); PitchLags_Q7[k] = tmp16a; } CQ10 = mean_val2Q10[index[1]]; for (k=0; k> 5); PitchLags_Q7[k] += tmp16c; } CQ10 = mean_val4Q10[index[3]]; for (k=0; k> 5); PitchLags_Q7[k] += tmp16c; } @@ -1775,7 +1774,7 @@ for (k = 0; k < 4; k++) meangainQ12 += PitchGain_Q12[k]; - meangainQ12 = WEBRTC_SPL_RSHIFT_W32(meangainQ12, 2); + meangainQ12 >>= 2; /* Save data for creation of multiple bitstreams */ if (encData != NULL) { @@ -1817,7 +1816,7 @@ CQ17 = WEBRTC_SPL_SHIFT_W32(CQ17,shft); // Scale with StepSize /* quantize */ - tmp16b = (int16_t) WEBRTC_SPL_RSHIFT_W32(CQ17 + 65536, 17 ); + tmp16b = (int16_t)((CQ17 + 65536) >> 17); index[k] = tmp16b; /* check that the index is not outside the boundaries of the table */ @@ -1837,21 +1836,21 @@ for (k=0; k> 5); // Q7. PitchLagsQ7[k] = tmp16a; } CQ10 = mean_val2Q10[index[1]]; for (k=0; k> 5); // Q7. PitchLagsQ7[k] += tmp16c; } CQ10 = mean_val4Q10[index[3]]; for (k=0; k> 5); // Q7. PitchLagsQ7[k] += tmp16c; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/entropy_coding.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/entropy_coding.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/entropy_coding.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/entropy_coding.h 2015-02-03 14:33:34.000000000 +0000 @@ -166,4 +166,24 @@ const int matrix0_index_step); #endif +#if defined(MIPS32_LE) +void WebRtcIsacfix_MatrixProduct1MIPS(const int16_t matrix0[], + const int32_t matrix1[], + int32_t matrix_product[], + const int matrix1_index_factor1, + const int matrix0_index_factor1, + const int matrix1_index_init_case, + const int matrix1_index_step, + const int matrix0_index_step, + const int inner_loop_count, + const int mid_loop_count, + const int shift); + +void WebRtcIsacfix_MatrixProduct2MIPS(const int16_t matrix0[], + const int32_t matrix1[], + int32_t matrix_product[], + const int matrix0_index_factor, + const int matrix0_index_step); +#endif + #endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_ENTROPY_CODING_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/entropy_coding_mips.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/entropy_coding_mips.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/entropy_coding_mips.c 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/entropy_coding_mips.c 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/entropy_coding.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/settings.h" + +// MIPS optimization of the function WebRtcIsacfix_MatrixProduct1. +// Bit-exact with the function WebRtcIsacfix_MatrixProduct1C from +// entropy_coding.c file. +void WebRtcIsacfix_MatrixProduct1MIPS(const int16_t matrix0[], + const int32_t matrix1[], + int32_t matrix_product[], + const int matrix1_index_factor1, + const int matrix0_index_factor1, + const int matrix1_index_init_case, + const int matrix1_index_step, + const int matrix0_index_step, + const int inner_loop_count, + const int mid_loop_count, + const int shift) { + if (matrix1_index_init_case != 0) { + int j = SUBFRAMES, k = 0, n = 0; + int32_t r0, r1, r2, sum32; + int32_t* product_start = matrix_product; + int32_t* product_ptr; + const uint32_t product_step = 4 * mid_loop_count; + const uint32_t matrix0_step = 2 * matrix0_index_step; + const uint32_t matrix1_step = 4 * matrix1_index_step; + const uint32_t matrix0_step2 = 2 * matrix0_index_factor1; + const uint32_t matrix1_step2 = 4 * matrix1_index_factor1; + const int16_t* matrix0_start = matrix0; + const int32_t* matrix1_start = matrix1; + int16_t* matrix0_ptr; + int32_t* matrix1_ptr; + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "1: \n\t" + "addu %[product_ptr], %[product_start], $0 \n\t" + "addu %[k], %[product_step], $0 \n\t" + "addiu %[j], %[j], -1 \n\t" + "addu %[matrix1_start], %[matrix1], $0 \n\t" + "2: \n\t" + "addu %[matrix1_ptr], %[matrix1_start], $0 \n\t" + "addu %[matrix0_ptr], %[matrix0_start], $0 \n\t" + "addu %[n], %[inner_loop_count], $0 \n\t" + "mul %[sum32], $0, $0 \n\t" + "3: \n\t" + "lw %[r0], 0(%[matrix1_ptr]) \n\t" + "lh %[r1], 0(%[matrix0_ptr]) \n\t" + "addu %[matrix1_ptr], %[matrix1_ptr], %[matrix1_step] \n\t" + "sllv %[r0], %[r0], %[shift] \n\t" + "andi %[r2], %[r0], 0xffff \n\t" + "sra %[r2], %[r2], 1 \n\t" + "mul %[r2], %[r2], %[r1] \n\t" + "sra %[r0], %[r0], 16 \n\t" + "mul %[r0], %[r0], %[r1] \n\t" + "addu %[matrix0_ptr], %[matrix0_ptr], %[matrix0_step] \n\t" + "addiu %[n], %[n], -1 \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r2], %[r2], 15 \n\t" +#else + "addiu %[r2], %[r2], 0x4000 \n\t" + "sra %[r2], %[r2], 15 \n\t" +#endif + "addu %[sum32], %[sum32], %[r2] \n\t" + "bgtz %[n], 3b \n\t" + " addu %[sum32], %[sum32], %[r0] \n\t" + "addiu %[k], %[k], -4 \n\t" + "addu %[matrix1_start], %[matrix1_start], %[matrix1_step2] \n\t" + "sw %[sum32], 0(%[product_ptr]) \n\t" + "bgtz %[k], 2b \n\t" + " addiu %[product_ptr], %[product_ptr], 4 \n\t" + "addu %[matrix0_start], %[matrix0_start], %[matrix0_step2] \n\t" + "bgtz %[j], 1b \n\t" + " addu %[product_start], %[product_start], %[product_step] \n\t" + ".set pop \n\t" + : [product_ptr] "=&r" (product_ptr), [product_start] "+r" (product_start), + [k] "=&r" (k), [j] "+r" (j), [matrix1_start] "=&r"(matrix1_start), + [matrix1_ptr] "=&r" (matrix1_ptr), [matrix0_ptr] "=&r" (matrix0_ptr), + [matrix0_start] "+r" (matrix0_start), [n] "=&r" (n), [r0] "=&r" (r0), + [sum32] "=&r" (sum32), [r1] "=&r" (r1),[r2] "=&r" (r2) + : [product_step] "r" (product_step), [matrix1] "r" (matrix1), + [inner_loop_count] "r" (inner_loop_count), + [matrix1_step] "r" (matrix1_step), [shift] "r" (shift), + [matrix0_step] "r" (matrix0_step), [matrix1_step2] "r" (matrix1_step2), + [matrix0_step2] "r" (matrix0_step2) + : "hi", "lo", "memory" + ); + } else { + int j = SUBFRAMES, k = 0, n = 0; + int32_t r0, r1, r2, sum32; + int32_t* product_start = matrix_product; + int32_t* product_ptr; + const uint32_t product_step = 4 * mid_loop_count; + const uint32_t matrix0_step = 2 * matrix0_index_step; + const uint32_t matrix1_step = 4 * matrix1_index_step; + const uint32_t matrix0_step2 = 2 * matrix0_index_factor1; + const uint32_t matrix1_step2 = 4 * matrix1_index_factor1; + const int16_t* matrix0_start = matrix0; + const int32_t* matrix1_start = matrix1; + int16_t* matrix0_ptr; + int32_t* matrix1_ptr; + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "1: \n\t" + "addu %[product_ptr], %[product_start], $0 \n\t" + "addu %[k], %[product_step], $0 \n\t" + "addiu %[j], %[j], -1 \n\t" + "addu %[matrix0_start], %[matrix0], $0 \n\t" + "2: \n\t" + "addu %[matrix1_ptr], %[matrix1_start], $0 \n\t" + "addu %[matrix0_ptr], %[matrix0_start], $0 \n\t" + "addu %[n], %[inner_loop_count], $0 \n\t" + "mul %[sum32], $0, $0 \n\t" + "3: \n\t" + "lw %[r0], 0(%[matrix1_ptr]) \n\t" + "lh %[r1], 0(%[matrix0_ptr]) \n\t" + "addu %[matrix1_ptr], %[matrix1_ptr], %[matrix1_step] \n\t" + "sllv %[r0], %[r0], %[shift] \n\t" + "andi %[r2], %[r0], 0xffff \n\t" + "sra %[r2], %[r2], 1 \n\t" + "mul %[r2], %[r2], %[r1] \n\t" + "sra %[r0], %[r0], 16 \n\t" + "mul %[r0], %[r0], %[r1] \n\t" + "addu %[matrix0_ptr], %[matrix0_ptr], %[matrix0_step] \n\t" + "addiu %[n], %[n], -1 \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r2], %[r2], 15 \n\t" +#else + "addiu %[r2], %[r2], 0x4000 \n\t" + "sra %[r2], %[r2], 15 \n\t" +#endif + "addu %[sum32], %[sum32], %[r2] \n\t" + "bgtz %[n], 3b \n\t" + " addu %[sum32], %[sum32], %[r0] \n\t" + "addiu %[k], %[k], -4 \n\t" + "addu %[matrix0_start], %[matrix0_start], %[matrix0_step2] \n\t" + "sw %[sum32], 0(%[product_ptr]) \n\t" + "bgtz %[k], 2b \n\t" + " addiu %[product_ptr], %[product_ptr], 4 \n\t" + "addu %[matrix1_start], %[matrix1_start], %[matrix1_step2] \n\t" + "bgtz %[j], 1b \n\t" + " addu %[product_start], %[product_start], %[product_step] \n\t" + ".set pop \n\t" + : [product_ptr] "=&r" (product_ptr), [product_start] "+r" (product_start), + [k] "=&r" (k), [j] "+r" (j), [matrix1_start] "+r"(matrix1_start), + [matrix1_ptr] "=&r" (matrix1_ptr), [matrix0_ptr] "=&r" (matrix0_ptr), + [matrix0_start] "=&r" (matrix0_start), [n] "=&r" (n), [r0] "=&r" (r0), + [sum32] "=&r" (sum32), [r1] "=&r" (r1),[r2] "=&r" (r2) + : [product_step] "r" (product_step), [matrix0] "r" (matrix0), + [inner_loop_count] "r" (inner_loop_count), + [matrix1_step] "r" (matrix1_step), [shift] "r" (shift), + [matrix0_step] "r" (matrix0_step), [matrix1_step2] "r" (matrix1_step2), + [matrix0_step2] "r" (matrix0_step2) + : "hi", "lo", "memory" + ); + } +} + +// MIPS optimization of the function WebRtcIsacfix_MatrixProduct2. +// Bit-exact with the function WebRtcIsacfix_MatrixProduct2C from +// entropy_coding.c file. +void WebRtcIsacfix_MatrixProduct2MIPS(const int16_t matrix0[], + const int32_t matrix1[], + int32_t matrix_product[], + const int matrix0_index_factor, + const int matrix0_index_step) { + int j = 0, n = 0; + int loop_count = SUBFRAMES; + const int16_t* matrix0_ptr; + const int32_t* matrix1_ptr; + const int16_t* matrix0_start = matrix0; + const int matrix0_step = 2 * matrix0_index_step; + const int matrix0_step2 = 2 * matrix0_index_factor; + int32_t r0, r1, r2, r3, r4, sum32, sum32_2; + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addu %[j], %[loop_count], $0 \n\t" + "addu %[matrix0_start], %[matrix0], $0 \n\t" + "1: \n\t" + "addu %[matrix1_ptr], %[matrix1], $0 \n\t" + "addu %[matrix0_ptr], %[matrix0_start], $0 \n\t" + "addu %[n], %[loop_count], $0 \n\t" + "mul %[sum32], $0, $0 \n\t" + "mul %[sum32_2], $0, $0 \n\t" + "2: \n\t" + "lw %[r0], 0(%[matrix1_ptr]) \n\t" + "lw %[r1], 4(%[matrix1_ptr]) \n\t" + "lh %[r2], 0(%[matrix0_ptr]) \n\t" + "andi %[r3], %[r0], 0xffff \n\t" + "sra %[r3], %[r3], 1 \n\t" + "mul %[r3], %[r3], %[r2] \n\t" + "andi %[r4], %[r1], 0xffff \n\t" + "sra %[r4], %[r4], 1 \n\t" + "mul %[r4], %[r4], %[r2] \n\t" + "sra %[r0], %[r0], 16 \n\t" + "mul %[r0], %[r0], %[r2] \n\t" + "sra %[r1], %[r1], 16 \n\t" + "mul %[r1], %[r1], %[r2] \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r3], %[r3], 15 \n\t" + "shra_r.w %[r4], %[r4], 15 \n\t" +#else + "addiu %[r3], %[r3], 0x4000 \n\t" + "sra %[r3], %[r3], 15 \n\t" + "addiu %[r4], %[r4], 0x4000 \n\t" + "sra %[r4], %[r4], 15 \n\t" +#endif + "addiu %[matrix1_ptr], %[matrix1_ptr], 8 \n\t" + "addu %[matrix0_ptr], %[matrix0_ptr], %[matrix0_step] \n\t" + "addiu %[n], %[n], -1 \n\t" + "addu %[sum32], %[sum32], %[r3] \n\t" + "addu %[sum32_2], %[sum32_2], %[r4] \n\t" + "addu %[sum32], %[sum32], %[r0] \n\t" + "bgtz %[n], 2b \n\t" + " addu %[sum32_2], %[sum32_2], %[r1] \n\t" + "sra %[sum32], %[sum32], 3 \n\t" + "sra %[sum32_2], %[sum32_2], 3 \n\t" + "addiu %[j], %[j], -1 \n\t" + "addu %[matrix0_start], %[matrix0_start], %[matrix0_step2] \n\t" + "sw %[sum32], 0(%[matrix_product]) \n\t" + "sw %[sum32_2], 4(%[matrix_product]) \n\t" + "bgtz %[j], 1b \n\t" + " addiu %[matrix_product], %[matrix_product], 8 \n\t" + ".set pop \n\t" + : [j] "=&r" (j), [matrix0_start] "=&r" (matrix0_start), + [matrix1_ptr] "=&r" (matrix1_ptr), [matrix0_ptr] "=&r" (matrix0_ptr), + [n] "=&r" (n), [sum32] "=&r" (sum32), [sum32_2] "=&r" (sum32_2), + [r0] "=&r" (r0), [r1] "=&r" (r1), [r2] "=&r" (r2), [r3] "=&r" (r3), + [r4] "=&r" (r4), [matrix_product] "+r" (matrix_product) + : [loop_count] "r" (loop_count), [matrix0] "r" (matrix0), + [matrix1] "r" (matrix1), [matrix0_step] "r" (matrix0_step), + [matrix0_step2] "r" (matrix0_step2) + : "hi", "lo", "memory" + ); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/fft.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/fft.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/fft.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/fft.c 2015-02-03 14:33:34.000000000 +0000 @@ -176,10 +176,8 @@ bjQx = ImxQx[k1] + ImxQx[k2]; RexQx[kk] = akQx + ajQx; ImxQx[kk] = bkQx + bjQx; - tmp116 = WEBRTC_SPL_RSHIFT_W16(ajQx, 1); - tmp216 = WEBRTC_SPL_RSHIFT_W16(bjQx, 1); - akQx = akQx - tmp116; - bkQx = bkQx - tmp216; + akQx -= ajQx >> 1; + bkQx -= bjQx >> 1; tmp116 = RexQx[k1] - RexQx[k2]; tmp216 = ImxQx[k1] - ImxQx[k2]; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbank_internal.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbank_internal.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbank_internal.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbank_internal.h 2015-02-03 14:33:34.000000000 +0000 @@ -11,7 +11,7 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_FILTERBANK_INTERNAL_H_ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_FILTERBANK_INTERNAL_H_ -#include "typedefs.h" +#include "webrtc/typedefs.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { @@ -23,10 +23,23 @@ * coefficient: Input. * state: Input/output, filter state, in Q4. */ -void WebRtcIsacfix_HighpassFilterFixDec32(int16_t *io, - int16_t len, - const int16_t *coefficient, - int32_t *state); +typedef void (*HighpassFilterFixDec32)(int16_t* io, + int16_t len, + const int16_t* coefficient, + int32_t* state); +extern HighpassFilterFixDec32 WebRtcIsacfix_HighpassFilterFixDec32; + +void WebRtcIsacfix_HighpassFilterFixDec32C(int16_t* io, + int16_t len, + const int16_t* coefficient, + int32_t* state); + +#if defined(MIPS_DSP_R1_LE) +void WebRtcIsacfix_HighpassFilterFixDec32MIPS(int16_t* io, + int16_t len, + const int16_t* coefficient, + int32_t* state); +#endif typedef void (*AllpassFilter2FixDec16)( int16_t *data_ch1, // Input and output in channel 1, in Q0 @@ -52,6 +65,17 @@ int16_t *data_ch1, int16_t *data_ch2, const int16_t *factor_ch1, + const int16_t *factor_ch2, + const int length, + int32_t *filter_state_ch1, + int32_t *filter_state_ch2); +#endif + +#if defined(MIPS_DSP_R1_LE) +void WebRtcIsacfix_AllpassFilter2FixDec16MIPS( + int16_t *data_ch1, + int16_t *data_ch2, + const int16_t *factor_ch1, const int16_t *factor_ch2, const int length, int32_t *filter_state_ch1, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbanks.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbanks.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbanks.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbanks.c 2015-02-03 14:33:34.000000000 +0000 @@ -51,32 +51,32 @@ in_out = data_ch1[n]; a = WEBRTC_SPL_MUL_16_16(factor_ch1[0], in_out); // Q15 * Q0 = Q15 a <<= 1; // Q15 -> Q16 - b = WEBRTC_SPL_ADD_SAT_W32(a, state0_ch1); + b = WebRtcSpl_AddSatW32(a, state0_ch1); a = WEBRTC_SPL_MUL_16_16(-factor_ch1[0], (int16_t) (b >> 16)); // Q15 - state0_ch1 = WEBRTC_SPL_ADD_SAT_W32(a << 1, (uint32_t)in_out << 16); // Q16 + state0_ch1 = WebRtcSpl_AddSatW32(a << 1, (uint32_t)in_out << 16); // Q16 in_out = (int16_t) (b >> 16); // Save as Q0 a = WEBRTC_SPL_MUL_16_16(factor_ch1[1], in_out); // Q15 * Q0 = Q15 a <<= 1; // Q15 -> Q16 - b = WEBRTC_SPL_ADD_SAT_W32(a, state1_ch1); // Q16 + b = WebRtcSpl_AddSatW32(a, state1_ch1); // Q16 a = WEBRTC_SPL_MUL_16_16(-factor_ch1[1], (int16_t) (b >> 16)); // Q15 - state1_ch1 = WEBRTC_SPL_ADD_SAT_W32(a << 1, (uint32_t)in_out << 16); // Q16 + state1_ch1 = WebRtcSpl_AddSatW32(a << 1, (uint32_t)in_out << 16); // Q16 data_ch1[n] = (int16_t) (b >> 16); // Save as Q0 // Process channel 2: in_out = data_ch2[n]; a = WEBRTC_SPL_MUL_16_16(factor_ch2[0], in_out); // Q15 * Q0 = Q15 a <<= 1; // Q15 -> Q16 - b = WEBRTC_SPL_ADD_SAT_W32(a, state0_ch2); // Q16 + b = WebRtcSpl_AddSatW32(a, state0_ch2); // Q16 a = WEBRTC_SPL_MUL_16_16(-factor_ch2[0], (int16_t) (b >> 16)); // Q15 - state0_ch2 = WEBRTC_SPL_ADD_SAT_W32(a << 1, (uint32_t)in_out << 16); // Q16 + state0_ch2 = WebRtcSpl_AddSatW32(a << 1, (uint32_t)in_out << 16); // Q16 in_out = (int16_t) (b >> 16); // Save as Q0 a = WEBRTC_SPL_MUL_16_16(factor_ch2[1], in_out); // Q15 * Q0 = Q15 a <<= 1; // Q15 -> Q16 - b = WEBRTC_SPL_ADD_SAT_W32(a, state1_ch2); // Q16 + b = WebRtcSpl_AddSatW32(a, state1_ch2); // Q16 a = WEBRTC_SPL_MUL_16_16(-factor_ch2[1], (int16_t) (b >> 16)); // Q15 - state1_ch2 = WEBRTC_SPL_ADD_SAT_W32(a << 1, (uint32_t)in_out << 16); // Q16 + state1_ch2 = WebRtcSpl_AddSatW32(a << 1, (uint32_t)in_out << 16); // Q16 data_ch2[n] = (int16_t) (b >> 16); // Save as Q0 } @@ -86,10 +86,13 @@ filter_state_ch2[1] = state1_ch2; } -void WebRtcIsacfix_HighpassFilterFixDec32(int16_t *io, - int16_t len, - const int16_t *coefficient, - int32_t *state) +// Declare a function pointer. +HighpassFilterFixDec32 WebRtcIsacfix_HighpassFilterFixDec32; + +void WebRtcIsacfix_HighpassFilterFixDec32C(int16_t *io, + int16_t len, + const int16_t *coefficient, + int32_t *state) { int k; int32_t a1 = 0, b1 = 0, c = 0, in = 0; @@ -102,8 +105,8 @@ #ifdef WEBRTC_ARCH_ARM_V7 { - int tmp_coeff0 = 0; - int tmp_coeff1 = 0; + register int tmp_coeff0; + register int tmp_coeff1; __asm __volatile( "ldr %[tmp_coeff0], [%[coeff]]\n\t" "ldr %[tmp_coeff1], [%[coeff], #4]\n\t" @@ -113,12 +116,12 @@ "ldr %[tmp_coeff1], [%[coeff], #12]\n\t" "smmulr %[a1], %[tmp_coeff0], %[state0]\n\t" "smmulr %[b1], %[tmp_coeff1], %[state1]\n\t" - :[a2]"+r"(a2), - [b2]"+r"(b2), - [a1]"+r"(a1), - [b1]"+r"(b1), - [tmp_coeff0]"+r"(tmp_coeff0), - [tmp_coeff1]"+r"(tmp_coeff1) + :[a2]"=&r"(a2), + [b2]"=&r"(b2), + [a1]"=&r"(a1), + [b1]"=r"(b1), + [tmp_coeff0]"=&r"(tmp_coeff0), + [tmp_coeff1]"=&r"(tmp_coeff1) :[coeff]"r"(coefficient), [state0]"r"(state0), [state1]"r"(state1) @@ -126,15 +129,19 @@ } #else /* Q35 * Q4 = Q39 ; shift 32 bit => Q7 */ - a1 = WEBRTC_SPL_MUL_32_32_RSFT32(coefficient[5], coefficient[4], state0); - b1 = WEBRTC_SPL_MUL_32_32_RSFT32(coefficient[7], coefficient[6], state1); + a1 = WEBRTC_SPL_MUL_16_32_RSFT16(coefficient[5], state0) + + (WEBRTC_SPL_MUL_16_32_RSFT16(coefficient[4], state0) >> 16); + b1 = WEBRTC_SPL_MUL_16_32_RSFT16(coefficient[7], state1) + + (WEBRTC_SPL_MUL_16_32_RSFT16(coefficient[6], state1) >> 16); /* Q30 * Q4 = Q34 ; shift 32 bit => Q2 */ - a2 = WEBRTC_SPL_MUL_32_32_RSFT32(coefficient[1], coefficient[0], state0); - b2 = WEBRTC_SPL_MUL_32_32_RSFT32(coefficient[3], coefficient[2], state1); + a2 = WEBRTC_SPL_MUL_16_32_RSFT16(coefficient[1], state0) + + (WEBRTC_SPL_MUL_16_32_RSFT16(coefficient[0], state0) >> 16); + b2 = WEBRTC_SPL_MUL_16_32_RSFT16(coefficient[3], state1) + + (WEBRTC_SPL_MUL_16_32_RSFT16(coefficient[2], state1) >> 16); #endif - c = ((int32_t)in) + WEBRTC_SPL_RSHIFT_W32(a1+b1, 7); // Q0 + c = in + ((a1 + b1) >> 7); // Q0. io[k] = (int16_t)WebRtcSpl_SatW32ToW16(c); // Write output as Q0. c = WEBRTC_SPL_LSHIFT_W32((int32_t)in, 2) - a2 - b2; // In Q2. @@ -216,9 +223,9 @@ int32_t tmp1, tmp2, tmp3; tmp1 = (int32_t)tempin_ch1[k]; // Q0 -> Q0 tmp2 = (int32_t)tempin_ch2[k]; // Q0 -> Q0 - tmp3 = (int32_t)WEBRTC_SPL_RSHIFT_W32((tmp1 + tmp2), 1);/* low pass signal*/ + tmp3 = (tmp1 + tmp2) >> 1; /* Low pass signal. */ LP16[k] = (int16_t)WebRtcSpl_SatW32ToW16(tmp3); /*low pass */ - tmp3 = (int32_t)WEBRTC_SPL_RSHIFT_W32((tmp1 - tmp2), 1);/* high pass signal*/ + tmp3 = (tmp1 - tmp2) >> 1; /* High pass signal. */ HP16[k] = (int16_t)WebRtcSpl_SatW32ToW16(tmp3); /*high pass */ } @@ -275,9 +282,9 @@ int32_t tmp1, tmp2, tmp3; tmp1 = (int32_t)tempin_ch1[k]; // Q0 -> Q0 tmp2 = (int32_t)tempin_ch2[k]; // Q0 -> Q0 - tmp3 = (int32_t)WEBRTC_SPL_RSHIFT_W32((tmp1 + tmp2), 1);/* low pass signal*/ + tmp3 = (tmp1 + tmp2) >> 1; /* Low pass signal. */ LP16[k] = (int16_t)WebRtcSpl_SatW32ToW16(tmp3); /*low pass */ - tmp3 = (int32_t)WEBRTC_SPL_RSHIFT_W32((tmp1 - tmp2), 1);/* high pass signal*/ + tmp3 = (tmp1 - tmp2) >> 1; /* High pass signal. */ HP16[k] = (int16_t)WebRtcSpl_SatW32ToW16(tmp3); /*high pass */ } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbanks_mips.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbanks_mips.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbanks_mips.c 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbanks_mips.c 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/filterbank_internal.h" + +// WebRtcIsacfix_AllpassFilter2FixDec16 function optimized for MIPSDSP platform. +// Bit-exact with WebRtcIsacfix_AllpassFilter2FixDec16C from filterbanks.c. +void WebRtcIsacfix_AllpassFilter2FixDec16MIPS( + int16_t* data_ch1, // Input and output in channel 1, in Q0. + int16_t* data_ch2, // Input and output in channel 2, in Q0. + const int16_t* factor_ch1, // Scaling factor for channel 1, in Q15. + const int16_t* factor_ch2, // Scaling factor for channel 2, in Q15. + const int length, // Length of the data buffers. + int32_t* filter_state_ch1, // Filter state for channel 1, in Q16. + int32_t* filter_state_ch2) { // Filter state for channel 2, in Q16. + + int32_t st0_ch1, st1_ch1; // channel1 state variables. + int32_t st0_ch2, st1_ch2; // channel2 state variables. + int32_t f_ch10, f_ch11, f_ch20, f_ch21; // factor variables. + int32_t r0, r1, r2, r3, r4, r5; // temporary register variables. + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + // Load all the state and factor variables. + "lh %[f_ch10], 0(%[factor_ch1]) \n\t" + "lh %[f_ch20], 0(%[factor_ch2]) \n\t" + "lh %[f_ch11], 2(%[factor_ch1]) \n\t" + "lh %[f_ch21], 2(%[factor_ch2]) \n\t" + "lw %[st0_ch1], 0(%[filter_state_ch1]) \n\t" + "lw %[st1_ch1], 4(%[filter_state_ch1]) \n\t" + "lw %[st0_ch2], 0(%[filter_state_ch2]) \n\t" + "lw %[st1_ch2], 4(%[filter_state_ch2]) \n\t" + // Allpass filtering loop. + "1: \n\t" + "lh %[r0], 0(%[data_ch1]) \n\t" + "lh %[r1], 0(%[data_ch2]) \n\t" + "addiu %[length], %[length], -1 \n\t" + "mul %[r2], %[r0], %[f_ch10] \n\t" + "mul %[r3], %[r1], %[f_ch20] \n\t" + "sll %[r0], %[r0], 16 \n\t" + "sll %[r1], %[r1], 16 \n\t" + "sll %[r2], %[r2], 1 \n\t" + "addq_s.w %[r2], %[r2], %[st0_ch1] \n\t" + "sll %[r3], %[r3], 1 \n\t" + "addq_s.w %[r3], %[r3], %[st0_ch2] \n\t" + "sra %[r2], %[r2], 16 \n\t" + "mul %[st0_ch1], %[f_ch10], %[r2] \n\t" + "sra %[r3], %[r3], 16 \n\t" + "mul %[st0_ch2], %[f_ch20], %[r3] \n\t" + "mul %[r4], %[r2], %[f_ch11] \n\t" + "mul %[r5], %[r3], %[f_ch21] \n\t" + "sll %[st0_ch1], %[st0_ch1], 1 \n\t" + "subq_s.w %[st0_ch1], %[r0], %[st0_ch1] \n\t" + "sll %[st0_ch2], %[st0_ch2], 1 \n\t" + "subq_s.w %[st0_ch2], %[r1], %[st0_ch2] \n\t" + "sll %[r4], %[r4], 1 \n\t" + "addq_s.w %[r4], %[r4], %[st1_ch1] \n\t" + "sll %[r5], %[r5], 1 \n\t" + "addq_s.w %[r5], %[r5], %[st1_ch2] \n\t" + "sra %[r4], %[r4], 16 \n\t" + "mul %[r0], %[r4], %[f_ch11] \n\t" + "sra %[r5], %[r5], 16 \n\t" + "mul %[r1], %[r5], %[f_ch21] \n\t" + "sh %[r4], 0(%[data_ch1]) \n\t" + "sh %[r5], 0(%[data_ch2]) \n\t" + "addiu %[data_ch1], %[data_ch1], 2 \n\t" + "sll %[r2], %[r2], 16 \n\t" + "sll %[r0], %[r0], 1 \n\t" + "subq_s.w %[st1_ch1], %[r2], %[r0] \n\t" + "sll %[r3], %[r3], 16 \n\t" + "sll %[r1], %[r1], 1 \n\t" + "subq_s.w %[st1_ch2], %[r3], %[r1] \n\t" + "bgtz %[length], 1b \n\t" + " addiu %[data_ch2], %[data_ch2], 2 \n\t" + // Store channel states. + "sw %[st0_ch1], 0(%[filter_state_ch1]) \n\t" + "sw %[st1_ch1], 4(%[filter_state_ch1]) \n\t" + "sw %[st0_ch2], 0(%[filter_state_ch2]) \n\t" + "sw %[st1_ch2], 4(%[filter_state_ch2]) \n\t" + ".set pop \n\t" + : [f_ch10] "=&r" (f_ch10), [f_ch20] "=&r" (f_ch20), + [f_ch11] "=&r" (f_ch11), [f_ch21] "=&r" (f_ch21), + [st0_ch1] "=&r" (st0_ch1), [st1_ch1] "=&r" (st1_ch1), + [st0_ch2] "=&r" (st0_ch2), [st1_ch2] "=&r" (st1_ch2), + [r0] "=&r" (r0), [r1] "=&r" (r1), [r2] "=&r" (r2), + [r3] "=&r" (r3), [r4] "=&r" (r4), [r5] "=&r" (r5) + : [factor_ch1] "r" (factor_ch1), [factor_ch2] "r" (factor_ch2), + [filter_state_ch1] "r" (filter_state_ch1), + [filter_state_ch2] "r" (filter_state_ch2), + [data_ch1] "r" (data_ch1), [data_ch2] "r" (data_ch2), + [length] "r" (length) + : "memory", "hi", "lo" + ); +} + +// WebRtcIsacfix_HighpassFilterFixDec32 function optimized for MIPSDSP platform. +// Bit-exact with WebRtcIsacfix_HighpassFilterFixDec32C from filterbanks.c. +void WebRtcIsacfix_HighpassFilterFixDec32MIPS(int16_t* io, + int16_t len, + const int16_t* coefficient, + int32_t* state) { + int k; + int32_t a1, a2, b1, b2, in; + int32_t state0 = state[0]; + int32_t state1 = state[1]; + + int32_t c0, c1, c2, c3; + int32_t c4, c5, c6, c7; + int32_t state0_lo, state0_hi; + int32_t state1_lo, state1_hi; + int32_t t0, t1, t2, t3, t4, t5; + + __asm __volatile ( + "lh %[c0], 0(%[coeff_ptr]) \n\t" + "lh %[c1], 2(%[coeff_ptr]) \n\t" + "lh %[c2], 4(%[coeff_ptr]) \n\t" + "lh %[c3], 6(%[coeff_ptr]) \n\t" + "sra %[state0_hi], %[state0], 16 \n\t" + "sra %[state1_hi], %[state1], 16 \n\t" + "andi %[state0_lo], %[state0], 0xFFFF \n\t" + "andi %[state1_lo], %[state1], 0xFFFF \n\t" + "lh %[c4], 8(%[coeff_ptr]) \n\t" + "lh %[c5], 10(%[coeff_ptr]) \n\t" + "lh %[c6], 12(%[coeff_ptr]) \n\t" + "lh %[c7], 14(%[coeff_ptr]) \n\t" + "sra %[state0_lo], %[state0_lo], 1 \n\t" + "sra %[state1_lo], %[state1_lo], 1 \n\t" + : [c0] "=&r" (c0), [c1] "=&r" (c1), [c2] "=&r" (c2), [c3] "=&r" (c3), + [c4] "=&r" (c4), [c5] "=&r" (c5), [c6] "=&r" (c6), [c7] "=&r" (c7), + [state0_hi] "=&r" (state0_hi), [state0_lo] "=&r" (state0_lo), + [state1_hi] "=&r" (state1_hi), [state1_lo] "=&r" (state1_lo) + : [coeff_ptr] "r" (coefficient), [state0] "r" (state0), + [state1] "r" (state1) + : "memory" + ); + + for (k = 0; k < len; k++) { + in = (int32_t)io[k]; + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "mul %[t2], %[c4], %[state0_lo] \n\t" + "mul %[t0], %[c5], %[state0_lo] \n\t" + "mul %[t1], %[c4], %[state0_hi] \n\t" + "mul %[a1], %[c5], %[state0_hi] \n\t" + "mul %[t5], %[c6], %[state1_lo] \n\t" + "mul %[t3], %[c7], %[state1_lo] \n\t" + "mul %[t4], %[c6], %[state1_hi] \n\t" + "mul %[b1], %[c7], %[state1_hi] \n\t" + "shra_r.w %[t2], %[t2], 15 \n\t" + "shra_r.w %[t0], %[t0], 15 \n\t" + "addu %[t1], %[t1], %[t2] \n\t" + "addu %[a1], %[a1], %[t0] \n\t" + "sra %[t1], %[t1], 16 \n\t" + "addu %[a1], %[a1], %[t1] \n\t" + "shra_r.w %[t5], %[t5], 15 \n\t" + "shra_r.w %[t3], %[t3], 15 \n\t" + "addu %[t4], %[t4], %[t5] \n\t" + "addu %[b1], %[b1], %[t3] \n\t" + "sra %[t4], %[t4], 16 \n\t" + "addu %[b1], %[b1], %[t4] \n\t" + "mul %[t2], %[c0], %[state0_lo] \n\t" + "mul %[t0], %[c1], %[state0_lo] \n\t" + "mul %[t1], %[c0], %[state0_hi] \n\t" + "mul %[a2], %[c1], %[state0_hi] \n\t" + "mul %[t5], %[c2], %[state1_lo] \n\t" + "mul %[t3], %[c3], %[state1_lo] \n\t" + "mul %[t4], %[c2], %[state1_hi] \n\t" + "mul %[b2], %[c3], %[state1_hi] \n\t" + "shra_r.w %[t2], %[t2], 15 \n\t" + "shra_r.w %[t0], %[t0], 15 \n\t" + "addu %[t1], %[t1], %[t2] \n\t" + "addu %[a2], %[a2], %[t0] \n\t" + "sra %[t1], %[t1], 16 \n\t" + "addu %[a2], %[a2], %[t1] \n\t" + "shra_r.w %[t5], %[t5], 15 \n\t" + "shra_r.w %[t3], %[t3], 15 \n\t" + "addu %[t4], %[t4], %[t5] \n\t" + "addu %[b2], %[b2], %[t3] \n\t" + "sra %[t4], %[t4], 16 \n\t" + "addu %[b2], %[b2], %[t4] \n\t" + "addu %[a1], %[a1], %[b1] \n\t" + "sra %[a1], %[a1], 7 \n\t" + "addu %[a1], %[a1], %[in] \n\t" + "sll %[t0], %[in], 2 \n\t" + "addu %[a2], %[a2], %[b2] \n\t" + "subu %[t0], %[t0], %[a2] \n\t" + "shll_s.w %[a1], %[a1], 16 \n\t" + "shll_s.w %[t0], %[t0], 2 \n\t" + "sra %[a1], %[a1], 16 \n\t" + "addu %[state1_hi], %[state0_hi], $0 \n\t" + "addu %[state1_lo], %[state0_lo], $0 \n\t" + "sra %[state0_hi], %[t0], 16 \n\t" + "andi %[state0_lo], %[t0], 0xFFFF \n\t" + "sra %[state0_lo], %[state0_lo], 1 \n\t" + ".set pop \n\t" + : [a1] "=&r" (a1), [b1] "=&r" (b1), [a2] "=&r" (a2), [b2] "=&r" (b2), + [state0_hi] "+r" (state0_hi), [state0_lo] "+r" (state0_lo), + [state1_hi] "+r" (state1_hi), [state1_lo] "+r" (state1_lo), + [t0] "=&r" (t0), [t1] "=&r" (t1), [t2] "=&r" (t2), + [t3] "=&r" (t3), [t4] "=&r" (t4), [t5] "=&r" (t5) + : [c0] "r" (c0), [c1] "r" (c1), [c2] "r" (c2), [c3] "r" (c3), + [c4] "r" (c4), [c5] "r" (c5), [c6] "r" (c6), [c7] "r" (c7), + [in] "r" (in) + : "hi", "lo" + ); + io[k] = (int16_t)a1; + } + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" +#if !defined(MIPS_DSP_R2_LE) + "sll %[state0_hi], %[state0_hi], 16 \n\t" + "sll %[state0_lo], %[state0_lo], 1 \n\t" + "sll %[state1_hi], %[state1_hi], 16 \n\t" + "sll %[state1_lo], %[state1_lo], 1 \n\t" + "or %[state0_hi], %[state0_hi], %[state0_lo] \n\t" + "or %[state1_hi], %[state1_hi], %[state1_lo] \n\t" +#else + "sll %[state0_lo], %[state0_lo], 1 \n\t" + "sll %[state1_lo], %[state1_lo], 1 \n\t" + "precr_sra.ph.w %[state0_hi], %[state0_lo], 0 \n\t" + "precr_sra.ph.w %[state1_hi], %[state1_lo], 0 \n\t" +#endif + "sw %[state0_hi], 0(%[state]) \n\t" + "sw %[state1_hi], 4(%[state]) \n\t" + ".set pop \n\t" + : [state0_hi] "+r" (state0_hi), [state0_lo] "+r" (state0_lo), + [state1_hi] "+r" (state1_hi), [state1_lo] "+r" (state1_lo) + : [state] "r" (state) + : "memory" + ); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbanks_neon.S thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbanks_neon.S --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbanks_neon.S 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbanks_neon.S 2015-02-03 14:33:34.000000000 +0000 @@ -147,14 +147,14 @@ @ a0_ch1 = WEBRTC_SPL_MUL_16_16(factor_ch1[0], sample0_ch1) << 1; @ a0_ch2 = WEBRTC_SPL_MUL_16_16(factor_ch2[0], sample0_ch2) << 1; @ -@ b0_ch1 = WEBRTC_SPL_ADD_SAT_W32(a0_ch1, state0_ch1); -@ b0_ch2 = WEBRTC_SPL_ADD_SAT_W32(a0_ch2, state0_ch2); //Q16+Q16=Q16 +@ b0_ch1 = WebRtcSpl_AddSatW32(a0_ch1, state0_ch1); +@ b0_ch2 = WebRtcSpl_AddSatW32(a0_ch2, state0_ch2); //Q16+Q16=Q16 @ @ a0_ch1 = WEBRTC_SPL_MUL_16_16(-factor_ch1[0], (int16_t) (b0_ch1 >> 16)); @ a0_ch2 = WEBRTC_SPL_MUL_16_16(-factor_ch2[0], (int16_t) (b0_ch2 >> 16)); @ -@ state0_ch1 = WEBRTC_SPL_ADD_SAT_W32(a0_ch1 <<1, (uint32_t)sample0_ch1 << 16); -@ state0_ch2 = WEBRTC_SPL_ADD_SAT_W32(a0_ch2 <<1, (uint32_t)sample0_ch2 << 16); +@ state0_ch1 = WebRtcSpl_AddSatW32(a0_ch1 <<1, (uint32_t)sample0_ch1 << 16); +@ state0_ch2 = WebRtcSpl_AddSatW32(a0_ch2 <<1, (uint32_t)sample0_ch2 << 16); @ @ sample1_ch1 = data_ch1[n + 1]; @ sample0_ch1 = (int16_t) (b0_ch1 >> 16); //Save as Q0 @@ -168,20 +168,20 @@ @ a1_ch2 = WEBRTC_SPL_MUL_16_16(factor_ch2[0], sample1_ch2 ) << 1; @ a0_ch2 = WEBRTC_SPL_MUL_16_16(factor_ch2[1], sample0_ch2) << 1; @ -@ b1_ch1 = WEBRTC_SPL_ADD_SAT_W32(a1_ch1, state0_ch1); -@ b0_ch1 = WEBRTC_SPL_ADD_SAT_W32(a0_ch1, state1_ch1); //Q16+Q16=Q16 -@ b1_ch2 = WEBRTC_SPL_ADD_SAT_W32(a1_ch2, state0_ch2); //Q16+Q16=Q16 -@ b0_ch2 = WEBRTC_SPL_ADD_SAT_W32(a0_ch2, state1_ch2); //Q16+Q16=Q16 +@ b1_ch1 = WebRtcSpl_AddSatW32(a1_ch1, state0_ch1); +@ b0_ch1 = WebRtcSpl_AddSatW32(a0_ch1, state1_ch1); //Q16+Q16=Q16 +@ b1_ch2 = WebRtcSpl_AddSatW32(a1_ch2, state0_ch2); //Q16+Q16=Q16 +@ b0_ch2 = WebRtcSpl_AddSatW32(a0_ch2, state1_ch2); //Q16+Q16=Q16 @ @ a1_ch1 = WEBRTC_SPL_MUL_16_16(-factor_ch1[0], (int16_t) (b1_ch1 >> 16)); @ a0_ch1 = WEBRTC_SPL_MUL_16_16(-factor_ch1[1], (int16_t) (b0_ch1 >> 16)); @ a1_ch2 = WEBRTC_SPL_MUL_16_16(-factor_ch2[0], (int16_t) (b1_ch2 >> 16)); @ a0_ch2 = WEBRTC_SPL_MUL_16_16(-factor_ch2[1], (int16_t) (b0_ch2 >> 16)); @ -@ state0_ch1 = WEBRTC_SPL_ADD_SAT_W32(a1_ch1<<1, (uint32_t)sample1_ch1 <<16); -@ state1_ch1 = WEBRTC_SPL_ADD_SAT_W32(a0_ch1<<1, (uint32_t)sample0_ch1 <<16); -@ state0_ch2 = WEBRTC_SPL_ADD_SAT_W32(a1_ch2<<1, (uint32_t)sample1_ch2 <<16); -@ state1_ch2 = WEBRTC_SPL_ADD_SAT_W32(a0_ch2<<1, (uint32_t)sample0_ch2 <<16); +@ state0_ch1 = WebRtcSpl_AddSatW32(a1_ch1<<1, (uint32_t)sample1_ch1 <<16); +@ state1_ch1 = WebRtcSpl_AddSatW32(a0_ch1<<1, (uint32_t)sample0_ch1 <<16); +@ state0_ch2 = WebRtcSpl_AddSatW32(a1_ch2<<1, (uint32_t)sample1_ch2 <<16); +@ state1_ch2 = WebRtcSpl_AddSatW32(a0_ch2<<1, (uint32_t)sample0_ch2 <<16); @ @ sample0_ch1 = data_ch1[n + 2]; @ sample1_ch1 = (int16_t) (b1_ch1 >> 16); //Save as Q0 @@ -193,20 +193,20 @@ @ a0_ch2 = WEBRTC_SPL_MUL_16_16(factor_ch2[0], sample0_ch2) << 1; @ a1_ch2 = WEBRTC_SPL_MUL_16_16(factor_ch2[1], sample1_ch2 ) << 1; @ -@ b2_ch1 = WEBRTC_SPL_ADD_SAT_W32(a0_ch1, state0_ch1); -@ b1_ch1 = WEBRTC_SPL_ADD_SAT_W32(a1_ch1, state1_ch1); //Q16+Q16=Q16 -@ b2_ch2 = WEBRTC_SPL_ADD_SAT_W32(a0_ch2, state0_ch2); //Q16+Q16=Q16 -@ b1_ch2 = WEBRTC_SPL_ADD_SAT_W32(a1_ch2, state1_ch2); //Q16+Q16=Q16 +@ b2_ch1 = WebRtcSpl_AddSatW32(a0_ch1, state0_ch1); +@ b1_ch1 = WebRtcSpl_AddSatW32(a1_ch1, state1_ch1); //Q16+Q16=Q16 +@ b2_ch2 = WebRtcSpl_AddSatW32(a0_ch2, state0_ch2); //Q16+Q16=Q16 +@ b1_ch2 = WebRtcSpl_AddSatW32(a1_ch2, state1_ch2); //Q16+Q16=Q16 @ @ a0_ch1 = WEBRTC_SPL_MUL_16_16(-factor_ch1[0], (int16_t) (b2_ch1 >> 16)); @ a1_ch1 = WEBRTC_SPL_MUL_16_16(-factor_ch1[1], (int16_t) (b1_ch1 >> 16)); @ a0_ch2 = WEBRTC_SPL_MUL_16_16(-factor_ch2[0], (int16_t) (b2_ch2 >> 16)); @ a1_ch2 = WEBRTC_SPL_MUL_16_16(-factor_ch2[1], (int16_t) (b1_ch2 >> 16)); @ -@ state0_ch1 = WEBRTC_SPL_ADD_SAT_W32(a0_ch1<<1, (uint32_t)sample0_ch1<<16); -@ state1_ch1 = WEBRTC_SPL_ADD_SAT_W32(a1_ch1<<1, (uint32_t)sample1_ch1<<16); -@ state0_ch2 = WEBRTC_SPL_ADD_SAT_W32(a0_ch2<<1, (uint32_t)sample0_ch2<<16); -@ state1_ch2 = WEBRTC_SPL_ADD_SAT_W32(a1_ch2<<1, (uint32_t)sample1_ch2<<16); +@ state0_ch1 = WebRtcSpl_AddSatW32(a0_ch1<<1, (uint32_t)sample0_ch1<<16); +@ state1_ch1 = WebRtcSpl_AddSatW32(a1_ch1<<1, (uint32_t)sample1_ch1<<16); +@ state0_ch2 = WebRtcSpl_AddSatW32(a0_ch2<<1, (uint32_t)sample0_ch2<<16); +@ state1_ch2 = WebRtcSpl_AddSatW32(a1_ch2<<1, (uint32_t)sample1_ch2<<16); @ @ @ sample1_ch1 = data_ch1[n + 3]; @@ -227,20 +227,20 @@ @ a1_ch2 = WEBRTC_SPL_MUL_16_16(factor_ch2[0], sample1_ch2 ) << 1; @ a0_ch2 = WEBRTC_SPL_MUL_16_16(factor_ch2[1], sample0_ch2) << 1; @ -@ b1_ch1 = WEBRTC_SPL_ADD_SAT_W32(a1_ch1, state0_ch1); -@ b0_ch1 = WEBRTC_SPL_ADD_SAT_W32(a0_ch1, state1_ch1); -@ b1_ch2 = WEBRTC_SPL_ADD_SAT_W32(a1_ch2, state0_ch2); -@ b0_ch2 = WEBRTC_SPL_ADD_SAT_W32(a0_ch2, state1_ch2); +@ b1_ch1 = WebRtcSpl_AddSatW32(a1_ch1, state0_ch1); +@ b0_ch1 = WebRtcSpl_AddSatW32(a0_ch1, state1_ch1); +@ b1_ch2 = WebRtcSpl_AddSatW32(a1_ch2, state0_ch2); +@ b0_ch2 = WebRtcSpl_AddSatW32(a0_ch2, state1_ch2); @ @ a1_ch1 = WEBRTC_SPL_MUL_16_16(-factor_ch1[0], (int16_t) (b1_ch1 >> 16)); @ a0_ch1 = WEBRTC_SPL_MUL_16_16(-factor_ch1[1], (int16_t) (b0_ch1 >> 16)); @ a1_ch2 = WEBRTC_SPL_MUL_16_16(-factor_ch2[0], (int16_t) (b1_ch2 >> 16)); @ a0_ch2 = WEBRTC_SPL_MUL_16_16(-factor_ch2[1], (int16_t) (b0_ch2 >> 16)); @ -@ state0_ch1 = WEBRTC_SPL_ADD_SAT_W32(a1_ch1<<1, (uint32_t)sample1_ch1 << 16); -@ state1_ch1 = WEBRTC_SPL_ADD_SAT_W32(a0_ch1<<1, (uint32_t)sample0_ch1 << 16); -@ state0_ch2 = WEBRTC_SPL_ADD_SAT_W32(a1_ch2<<1, (uint32_t)sample1_ch2 << 16); -@ state1_ch2 = WEBRTC_SPL_ADD_SAT_W32(a0_ch2<<1, (uint32_t)sample0_ch2 << 16); +@ state0_ch1 = WebRtcSpl_AddSatW32(a1_ch1<<1, (uint32_t)sample1_ch1 << 16); +@ state1_ch1 = WebRtcSpl_AddSatW32(a0_ch1<<1, (uint32_t)sample0_ch1 << 16); +@ state0_ch2 = WebRtcSpl_AddSatW32(a1_ch2<<1, (uint32_t)sample1_ch2 << 16); +@ state1_ch2 = WebRtcSpl_AddSatW32(a0_ch2<<1, (uint32_t)sample0_ch2 << 16); @ @ data_ch1[n] = (int16_t) (b0_ch1 >> 16); //Save as Q0 @ data_ch2[n] = (int16_t) (b0_ch2 >> 16); @@ -251,14 +251,14 @@ @ a1_ch1 = WEBRTC_SPL_MUL_16_16(factor_ch1[1], sample1_ch1) << 1; @ a1_ch2 = WEBRTC_SPL_MUL_16_16(factor_ch2[1], sample1_ch2 ) << 1; @ -@ b1_ch1 = WEBRTC_SPL_ADD_SAT_W32(a1_ch1, state1_ch1); //Q16+Q16=Q16 -@ b1_ch2 = WEBRTC_SPL_ADD_SAT_W32(a1_ch2, state1_ch2); //Q16+Q16=Q16 +@ b1_ch1 = WebRtcSpl_AddSatW32(a1_ch1, state1_ch1); //Q16+Q16=Q16 +@ b1_ch2 = WebRtcSpl_AddSatW32(a1_ch2, state1_ch2); //Q16+Q16=Q16 @ @ a1_ch1 = WEBRTC_SPL_MUL_16_16(-factor_ch1[1], (int16_t) (b1_ch1 >> 16)); @ a1_ch2 = WEBRTC_SPL_MUL_16_16(-factor_ch2[1], (int16_t) (b1_ch2 >> 16)); @ -@ state1_ch1 = WEBRTC_SPL_ADD_SAT_W32(a1_ch1<<1, (uint32_t)sample1_ch1<<16); -@ state1_ch2 = WEBRTC_SPL_ADD_SAT_W32(a1_ch2<<1, (uint32_t)sample1_ch2<<16); +@ state1_ch1 = WebRtcSpl_AddSatW32(a1_ch1<<1, (uint32_t)sample1_ch1<<16); +@ state1_ch2 = WebRtcSpl_AddSatW32(a1_ch2<<1, (uint32_t)sample1_ch2<<16); @ @ data_ch1[n + 1] = (int16_t) (b1_ch1 >> 16); //Save as Q0 @ data_ch2[n + 1] = (int16_t) (b1_ch2 >> 16); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbanks_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbanks_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbanks_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbanks_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" #include "webrtc/modules/audio_coding/codecs/isac/fix/source/filterbank_internal.h" #include "webrtc/modules/audio_coding/codecs/isac/fix/source/filterbank_tables.h" @@ -86,6 +86,13 @@ -1280, -8554, -14496, -7561, -23541, -27263, -30560, -32768, -3441, -32768, 25203, -27550, 22419}; #endif + HighpassFilterFixDec32 WebRtcIsacfix_HighpassFilterFixDec32; +#if defined(MIPS_DSP_R1_LE) + WebRtcIsacfix_HighpassFilterFixDec32 = + WebRtcIsacfix_HighpassFilterFixDec32MIPS; +#else + WebRtcIsacfix_HighpassFilterFixDec32 = WebRtcIsacfix_HighpassFilterFixDec32C; +#endif for (int i = 0; i < kSamples; i++) { in[i] = WEBRTC_SPL_WORD32_MAX / (i + 1); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbank_tables.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbank_tables.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbank_tables.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filterbank_tables.h 2015-02-03 14:33:34.000000000 +0000 @@ -19,7 +19,7 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_FILTERBANK_TABLES_H_ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_FILTERBANK_TABLES_H_ -#include "typedefs.h" +#include "webrtc/typedefs.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filters.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filters.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filters.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filters.c 2015-02-03 14:33:34.000000000 +0000 @@ -75,14 +75,13 @@ for (n=0;n Q16 - b = WEBRTC_SPL_ADD_SAT_W32(a, FilterState[j]); //Q16+Q16=Q16 - a = WEBRTC_SPL_MUL_16_32_RSFT16( - (int16_t) WEBRTC_SPL_RSHIFT_W32(b, 16), - -APSectionFactors[j]); //Q0*Q31=Q31 shifted 16 gives Q15 - FilterState[j] = WEBRTC_SPL_ADD_SAT_W32( + b = WebRtcSpl_AddSatW32(a, FilterState[j]); //Q16+Q16=Q16 + // |a| in Q15 (Q0*Q31=Q31 shifted 16 gives Q15). + a = WEBRTC_SPL_MUL_16_32_RSFT16(b >> 16, -APSectionFactors[j]); + FilterState[j] = WebRtcSpl_AddSatW32( WEBRTC_SPL_LSHIFT_W32(a,1), WEBRTC_SPL_LSHIFT_W32((uint32_t)InOut16[n], 16)); // Q15<<1 + Q0<<16 = Q16 + Q16 = Q16 - InOut16[n] = (int16_t) WEBRTC_SPL_RSHIFT_W32(b, 16); //Save as Q0 + InOut16[n] = (int16_t)(b >> 16); // Save as Q0. } } } @@ -102,7 +101,7 @@ memcpy(data_vec+1, in, WEBRTC_SPL_MUL_16_16(sizeof(int16_t), (N-1))); - data_vec[0] = (int16_t) WEBRTC_SPL_RSHIFT_W32(state_in[WEBRTC_SPL_MUL_16_16(2, ALLPASSSECTIONS)],16); //the z^(-1) state + data_vec[0] = (int16_t)(state_in[2 * ALLPASSSECTIONS] >> 16); // z^-1 state. state_in[WEBRTC_SPL_MUL_16_16(2, ALLPASSSECTIONS)] = WEBRTC_SPL_LSHIFT_W32((uint32_t)in[N-1],16); @@ -111,6 +110,7 @@ AllpassFilterForDec32(data_vec, kApLowerQ15, N, state_in+ALLPASSSECTIONS); for (n=0;n> 3); + int count = (int)(N & 7); + // Declare temporary variables used as registry values. + int32_t r0, r1, r2, r3; +#if !defined(MIPS_DSP_R2_LE) + // For non-DSPR2 optimizations 4 more registers are used. + int32_t r4, r5, r6, r7; +#endif + + // Calculate r[0] and scaling needed. + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "mult $0, $0 \n\t" + // Loop is unrolled 8 times, set accumulator to zero in branch delay slot. + "beqz %[loop_size], 2f \n\t" + " mult $0, $0 \n\t" + "1: \n\t" + // Load 8 samples per loop iteration. +#if defined(MIPS_DSP_R2_LE) + "ulw %[r0], 0(%[in]) \n\t" + "ulw %[r1], 4(%[in]) \n\t" + "ulw %[r2], 8(%[in]) \n\t" + "ulw %[r3], 12(%[in]) \n\t" +#else + "lh %[r0], 0(%[in]) \n\t" + "lh %[r1], 2(%[in]) \n\t" + "lh %[r2], 4(%[in]) \n\t" + "lh %[r3], 6(%[in]) \n\t" + "lh %[r4], 8(%[in]) \n\t" + "lh %[r5], 10(%[in]) \n\t" + "lh %[r6], 12(%[in]) \n\t" + "lh %[r7], 14(%[in]) \n\t" +#endif + "addiu %[loop_size], %[loop_size], -1 \n\t" + // Multiply and accumulate. +#if defined(MIPS_DSP_R2_LE) + "dpa.w.ph $ac0, %[r0], %[r0] \n\t" + "dpa.w.ph $ac0, %[r1], %[r1] \n\t" + "dpa.w.ph $ac0, %[r2], %[r2] \n\t" + "dpa.w.ph $ac0, %[r3], %[r3] \n\t" +#else + "madd %[r0], %[r0] \n\t" + "madd %[r1], %[r1] \n\t" + "madd %[r2], %[r2] \n\t" + "madd %[r3], %[r3] \n\t" + "madd %[r4], %[r4] \n\t" + "madd %[r5], %[r5] \n\t" + "madd %[r6], %[r6] \n\t" + "madd %[r7], %[r7] \n\t" +#endif + "bnez %[loop_size], 1b \n\t" + " addiu %[in], %[in], 16 \n\t" + "2: \n\t" + "beqz %[count], 4f \n\t" +#if defined(MIPS_DSP_R1_LE) + " extr.w %[r0], $ac0, 31 \n\t" +#else + " mfhi %[r2] \n\t" +#endif + // Process remaining samples (if any). + "3: \n\t" + "lh %[r0], 0(%[in]) \n\t" + "addiu %[count], %[count], -1 \n\t" + "madd %[r0], %[r0] \n\t" + "bnez %[count], 3b \n\t" + " addiu %[in], %[in], 2 \n\t" +#if defined(MIPS_DSP_R1_LE) + "extr.w %[r0], $ac0, 31 \n\t" +#else + "mfhi %[r2] \n\t" +#endif + "4: \n\t" +#if !defined(MIPS_DSP_R1_LE) + "mflo %[r3] \n\t" + "sll %[r0], %[r2], 1 \n\t" + "srl %[r1], %[r3], 31 \n\t" + "addu %[r0], %[r0], %[r1] \n\t" +#endif + // Calculate scaling (the value of shifting). + "clz %[r1], %[r0] \n\t" + "addiu %[r1], %[r1], -32 \n\t" + "subu %[scaling], $0, %[r1] \n\t" + "slti %[r1], %[r0], 0x1 \n\t" + "movn %[scaling], $0, %[r1] \n\t" +#if defined(MIPS_DSP_R1_LE) + "extrv.w %[r0], $ac0, %[scaling] \n\t" + "mfhi %[r2], $ac0 \n\t" +#else + "addiu %[r1], %[scaling], -32 \n\t" + "subu %[r1], $0, %[r1] \n\t" + "sllv %[r1], %[r2], %[r1] \n\t" + "srlv %[r0], %[r3], %[scaling] \n\t" + "addu %[r0], %[r0], %[r1] \n\t" +#endif + "slti %[r1], %[scaling], 32 \n\t" + "movz %[r0], %[r2], %[r1] \n\t" + ".set pop \n\t" + : [loop_size] "+r" (loop_size), [in] "+r" (in), [r0] "=&r" (r0), + [r1] "=&r" (r1), [r2] "=&r" (r2), [r3] "=&r" (r3), +#if !defined(MIPS_DSP_R2_LE) + [r4] "=&r" (r4), [r5] "=&r" (r5), [r6] "=&r" (r6), [r7] "=&r" (r7), +#endif + [count] "+r" (count), [scaling] "=r" (scaling) + : [N] "r" (N) + : "memory", "hi", "lo" + ); + r[0] = r0; + + // Correlation calculation is divided in 3 cases depending on the scaling + // value (different accumulator manipulation needed). Three slightly different + // loops are written in order to avoid branches inside the loop. + if (scaling == 0) { + // In this case, the result will be in low part of the accumulator. + for (i = 1; i < order + 1; i++) { + in = (int16_t*)x; + int16_t* in1 = (int16_t*)x + i; + count = N - i; + loop_size = (count) >> 2; + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "mult $0, $0 \n\t" + "beqz %[loop_size], 2f \n\t" + " andi %[count], %[count], 0x3 \n\t" + // Loop processing 4 pairs of samples per iteration. + "1: \n\t" +#if defined(MIPS_DSP_R2_LE) + "ulw %[r0], 0(%[in]) \n\t" + "ulw %[r1], 0(%[in1]) \n\t" + "ulw %[r2], 4(%[in]) \n\t" + "ulw %[r3], 4(%[in1]) \n\t" +#else + "lh %[r0], 0(%[in]) \n\t" + "lh %[r1], 0(%[in1]) \n\t" + "lh %[r2], 2(%[in]) \n\t" + "lh %[r3], 2(%[in1]) \n\t" + "lh %[r4], 4(%[in]) \n\t" + "lh %[r5], 4(%[in1]) \n\t" + "lh %[r6], 6(%[in]) \n\t" + "lh %[r7], 6(%[in1]) \n\t" +#endif + "addiu %[loop_size], %[loop_size], -1 \n\t" +#if defined(MIPS_DSP_R2_LE) + "dpa.w.ph $ac0, %[r0], %[r1] \n\t" + "dpa.w.ph $ac0, %[r2], %[r3] \n\t" +#else + "madd %[r0], %[r1] \n\t" + "madd %[r2], %[r3] \n\t" + "madd %[r4], %[r5] \n\t" + "madd %[r6], %[r7] \n\t" +#endif + "addiu %[in], %[in], 8 \n\t" + "bnez %[loop_size], 1b \n\t" + " addiu %[in1], %[in1], 8 \n\t" + "2: \n\t" + "beqz %[count], 4f \n\t" + " mflo %[r0] \n\t" + // Process remaining samples (if any). + "3: \n\t" + "lh %[r0], 0(%[in]) \n\t" + "lh %[r1], 0(%[in1]) \n\t" + "addiu %[count], %[count], -1 \n\t" + "addiu %[in], %[in], 2 \n\t" + "madd %[r0], %[r1] \n\t" + "bnez %[count], 3b \n\t" + " addiu %[in1], %[in1], 2 \n\t" + "mflo %[r0] \n\t" + "4: \n\t" + ".set pop \n\t" + : [loop_size] "+r" (loop_size), [in] "+r" (in), [in1] "+r" (in1), +#if !defined(MIPS_DSP_R2_LE) + [r4] "=&r" (r4), [r5] "=&r" (r5), [r6] "=&r" (r6), [r7] "=&r" (r7), +#endif + [r0] "=&r" (r0), [r1] "=&r" (r1), [r2] "=&r" (r2), [r3] "=&r" (r3), + [count] "+r" (count) + : + : "memory", "hi", "lo" + ); + r[i] = r0; + } + } else if (scaling == 32) { + // In this case, the result will be high part of the accumulator. + for (i = 1; i < order + 1; i++) { + in = (int16_t*)x; + int16_t* in1 = (int16_t*)x + i; + count = N - i; + loop_size = (count) >> 2; + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "mult $0, $0 \n\t" + "beqz %[loop_size], 2f \n\t" + " andi %[count], %[count], 0x3 \n\t" + // Loop processing 4 pairs of samples per iteration. + "1: \n\t" +#if defined(MIPS_DSP_R2_LE) + "ulw %[r0], 0(%[in]) \n\t" + "ulw %[r1], 0(%[in1]) \n\t" + "ulw %[r2], 4(%[in]) \n\t" + "ulw %[r3], 4(%[in1]) \n\t" +#else + "lh %[r0], 0(%[in]) \n\t" + "lh %[r1], 0(%[in1]) \n\t" + "lh %[r2], 2(%[in]) \n\t" + "lh %[r3], 2(%[in1]) \n\t" + "lh %[r4], 4(%[in]) \n\t" + "lh %[r5], 4(%[in1]) \n\t" + "lh %[r6], 6(%[in]) \n\t" + "lh %[r7], 6(%[in1]) \n\t" +#endif + "addiu %[loop_size], %[loop_size], -1 \n\t" +#if defined(MIPS_DSP_R2_LE) + "dpa.w.ph $ac0, %[r0], %[r1] \n\t" + "dpa.w.ph $ac0, %[r2], %[r3] \n\t" +#else + "madd %[r0], %[r1] \n\t" + "madd %[r2], %[r3] \n\t" + "madd %[r4], %[r5] \n\t" + "madd %[r6], %[r7] \n\t" +#endif + "addiu %[in], %[in], 8 \n\t" + "bnez %[loop_size], 1b \n\t" + " addiu %[in1], %[in1], 8 \n\t" + "2: \n\t" + "beqz %[count], 4f \n\t" + " mfhi %[r0] \n\t" + // Process remaining samples (if any). + "3: \n\t" + "lh %[r0], 0(%[in]) \n\t" + "lh %[r1], 0(%[in1]) \n\t" + "addiu %[count], %[count], -1 \n\t" + "addiu %[in], %[in], 2 \n\t" + "madd %[r0], %[r1] \n\t" + "bnez %[count], 3b \n\t" + " addiu %[in1], %[in1], 2 \n\t" + "mfhi %[r0] \n\t" + "4: \n\t" + ".set pop \n\t" + : [loop_size] "+r" (loop_size), [in] "+r" (in), [in1] "+r" (in1), +#if !defined(MIPS_DSP_R2_LE) + [r4] "=&r" (r4), [r5] "=&r" (r5), [r6] "=&r" (r6), [r7] "=&r" (r7), +#endif + [r0] "=&r" (r0), [r1] "=&r" (r1), [r2] "=&r" (r2), [r3] "=&r" (r3), + [count] "+r" (count) + : + : "memory", "hi", "lo" + ); + r[i] = r0; + } + } else { + // In this case, the result is obtained by combining low and high parts + // of the accumulator. +#if !defined(MIPS_DSP_R1_LE) + int32_t tmp_shift = 32 - scaling; +#endif + for (i = 1; i < order + 1; i++) { + in = (int16_t*)x; + int16_t* in1 = (int16_t*)x + i; + count = N - i; + loop_size = (count) >> 2; + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "mult $0, $0 \n\t" + "beqz %[loop_size], 2f \n\t" + " andi %[count], %[count], 0x3 \n\t" + "1: \n\t" +#if defined(MIPS_DSP_R2_LE) + "ulw %[r0], 0(%[in]) \n\t" + "ulw %[r1], 0(%[in1]) \n\t" + "ulw %[r2], 4(%[in]) \n\t" + "ulw %[r3], 4(%[in1]) \n\t" +#else + "lh %[r0], 0(%[in]) \n\t" + "lh %[r1], 0(%[in1]) \n\t" + "lh %[r2], 2(%[in]) \n\t" + "lh %[r3], 2(%[in1]) \n\t" + "lh %[r4], 4(%[in]) \n\t" + "lh %[r5], 4(%[in1]) \n\t" + "lh %[r6], 6(%[in]) \n\t" + "lh %[r7], 6(%[in1]) \n\t" +#endif + "addiu %[loop_size], %[loop_size], -1 \n\t" +#if defined(MIPS_DSP_R2_LE) + "dpa.w.ph $ac0, %[r0], %[r1] \n\t" + "dpa.w.ph $ac0, %[r2], %[r3] \n\t" +#else + "madd %[r0], %[r1] \n\t" + "madd %[r2], %[r3] \n\t" + "madd %[r4], %[r5] \n\t" + "madd %[r6], %[r7] \n\t" +#endif + "addiu %[in], %[in], 8 \n\t" + "bnez %[loop_size], 1b \n\t" + " addiu %[in1], %[in1], 8 \n\t" + "2: \n\t" + "beqz %[count], 4f \n\t" +#if defined(MIPS_DSP_R1_LE) + " extrv.w %[r0], $ac0, %[scaling] \n\t" +#else + " mfhi %[r0] \n\t" +#endif + "3: \n\t" + "lh %[r0], 0(%[in]) \n\t" + "lh %[r1], 0(%[in1]) \n\t" + "addiu %[count], %[count], -1 \n\t" + "addiu %[in], %[in], 2 \n\t" + "madd %[r0], %[r1] \n\t" + "bnez %[count], 3b \n\t" + " addiu %[in1], %[in1], 2 \n\t" +#if defined(MIPS_DSP_R1_LE) + "extrv.w %[r0], $ac0, %[scaling] \n\t" +#else + "mfhi %[r0] \n\t" +#endif + "4: \n\t" +#if !defined(MIPS_DSP_R1_LE) + "mflo %[r1] \n\t" + "sllv %[r0], %[r0], %[tmp_shift] \n\t" + "srlv %[r1], %[r1], %[scaling] \n\t" + "addu %[r0], %[r0], %[r1] \n\t" +#endif + ".set pop \n\t" + : [loop_size] "+r" (loop_size), [in] "+r" (in), [in1] "+r" (in1), +#if !defined(MIPS_DSP_R2_LE) + [r4] "=&r" (r4), [r5] "=&r" (r5), [r6] "=&r" (r6), [r7] "=&r" (r7), +#endif + [r0] "=&r" (r0), [r1] "=&r" (r1), [r2] "=&r" (r2), [r3] "=&r" (r3), + [count] "+r" (count) + : [scaling] "r" (scaling) +#if !defined(MIPS_DSP_R1_LE) + , [tmp_shift] "r" (tmp_shift) +#endif + : "memory", "hi", "lo" + ); + r[i] = r0; + } + } + *scale = scaling; + + return (order + 1); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filters_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filters_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filters_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/filters_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -7,7 +7,7 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/audio_coding/codecs/isac/fix/source/codec.h" #include "webrtc/system_wrappers/interface/cpu_features_wrapper.h" #include "webrtc/typedefs.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/isacfix.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/isacfix.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/isacfix.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/isacfix.c 2015-02-03 14:33:34.000000000 +0000 @@ -15,17 +15,18 @@ * */ -#include "modules/audio_coding/codecs/isac/fix/interface/isacfix.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h" +#include #include -#include "modules/audio_coding/codecs/isac/fix/source/bandwidth_estimator.h" -#include "modules/audio_coding/codecs/isac/fix/source/codec.h" -#include "modules/audio_coding/codecs/isac/fix/source/entropy_coding.h" -#include "modules/audio_coding/codecs/isac/fix/source/filterbank_internal.h" -#include "modules/audio_coding/codecs/isac/fix/source/lpc_masking_model.h" -#include "modules/audio_coding/codecs/isac/fix/source/structs.h" -#include "system_wrappers/interface/cpu_features_wrapper.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/bandwidth_estimator.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/codec.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/entropy_coding.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/filterbank_internal.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/structs.h" +#include "webrtc/system_wrappers/interface/cpu_features_wrapper.h" // Declare function pointers. FilterMaLoopFix WebRtcIsacfix_FilterMaLoopFix; @@ -34,6 +35,19 @@ MatrixProduct1 WebRtcIsacfix_MatrixProduct1; MatrixProduct2 WebRtcIsacfix_MatrixProduct2; +/* This method assumes that |stream_size_bytes| is in valid range, + * i.e. >= 0 && <= STREAM_MAXW16_60MS + */ +static void InitializeDecoderBitstream(int stream_size_bytes, + Bitstr_dec* bitstream) { + bitstream->W_upper = 0xFFFFFFFF; + bitstream->streamval = 0; + bitstream->stream_index = 0; + bitstream->full = 1; + bitstream->stream_size = (stream_size_bytes + 1) >> 1; + memset(bitstream->stream, 0, sizeof(bitstream->stream)); +} + /************************************************************************** * WebRtcIsacfix_AssignSize(...) * @@ -179,7 +193,7 @@ } /**************************************************************************** - * WebRtcAecm_InitNeon(...) + * WebRtcIsacfix_InitNeon(...) * * This function initializes function pointers for ARM Neon platform. */ @@ -200,6 +214,33 @@ #endif /**************************************************************************** + * WebRtcIsacfix_InitMIPS(...) + * + * This function initializes function pointers for MIPS platform. + */ + +#if defined(MIPS32_LE) +static void WebRtcIsacfix_InitMIPS(void) { + WebRtcIsacfix_AutocorrFix = WebRtcIsacfix_AutocorrMIPS; + WebRtcIsacfix_FilterMaLoopFix = WebRtcIsacfix_FilterMaLoopMIPS; + WebRtcIsacfix_Spec2Time = WebRtcIsacfix_Spec2TimeMIPS; + WebRtcIsacfix_Time2Spec = WebRtcIsacfix_Time2SpecMIPS; + WebRtcIsacfix_MatrixProduct1 = WebRtcIsacfix_MatrixProduct1MIPS; + WebRtcIsacfix_MatrixProduct2 = WebRtcIsacfix_MatrixProduct2MIPS; +#if defined(MIPS_DSP_R1_LE) + WebRtcIsacfix_AllpassFilter2FixDec16 = + WebRtcIsacfix_AllpassFilter2FixDec16MIPS; + WebRtcIsacfix_HighpassFilterFixDec32 = + WebRtcIsacfix_HighpassFilterFixDec32MIPS; +#endif +#if defined(MIPS_DSP_R2_LE) + WebRtcIsacfix_CalculateResidualEnergy = + WebRtcIsacfix_CalculateResidualEnergyMIPS; +#endif +} +#endif + +/**************************************************************************** * WebRtcIsacfix_EncoderInit(...) * * This function initializes a ISAC instance prior to the encoder calls. @@ -283,10 +324,11 @@ WebRtcIsacfix_CalculateResidualEnergy = WebRtcIsacfix_CalculateResidualEnergyC; WebRtcIsacfix_AllpassFilter2FixDec16 = WebRtcIsacfix_AllpassFilter2FixDec16C; + WebRtcIsacfix_HighpassFilterFixDec32 = WebRtcIsacfix_HighpassFilterFixDec32C; WebRtcIsacfix_Time2Spec = WebRtcIsacfix_Time2SpecC; WebRtcIsacfix_Spec2Time = WebRtcIsacfix_Spec2TimeC; WebRtcIsacfix_MatrixProduct1 = WebRtcIsacfix_MatrixProduct1C; - WebRtcIsacfix_MatrixProduct2 = WebRtcIsacfix_MatrixProduct2C ; + WebRtcIsacfix_MatrixProduct2 = WebRtcIsacfix_MatrixProduct2C; #ifdef WEBRTC_DETECT_ARM_NEON if ((WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) != 0) { @@ -296,9 +338,42 @@ WebRtcIsacfix_InitNeon(); #endif +#if defined(MIPS32_LE) + WebRtcIsacfix_InitMIPS(); +#endif + return statusInit; } +/* Read the given number of bytes of big-endian 16-bit integers from |src| and + write them to |dest| in host endian. If |nbytes| is odd, the number of + output elements is rounded up, and the least significant byte of the last + element is set to 0. */ +static void read_be16(const uint8_t* src, size_t nbytes, uint16_t* dest) { + size_t i; + for (i = 0; i < nbytes / 2; ++i) + dest[i] = src[2 * i] << 8 | src[2 * i + 1]; + if (nbytes % 2 == 1) + dest[nbytes / 2] = src[nbytes - 1] << 8; +} + +/* Read the given number of bytes of host-endian 16-bit integers from |src| and + write them to |dest| in big endian. If |nbytes| is odd, the number of source + elements is rounded up (but only the most significant byte of the last + element is used), and the number of output bytes written will be + nbytes + 1. */ +static void write_be16(const uint16_t* src, size_t nbytes, uint8_t* dest) { + size_t i; + for (i = 0; i < nbytes / 2; ++i) { + dest[2 * i] = src[i] >> 8; + dest[2 * i + 1] = src[i]; + } + if (nbytes % 2 == 1) { + dest[nbytes - 1] = src[nbytes / 2] >> 8; + dest[nbytes] = 0; + } +} + /**************************************************************************** * WebRtcIsacfix_Encode(...) * @@ -323,13 +398,10 @@ int16_t WebRtcIsacfix_Encode(ISACFIX_MainStruct *ISAC_main_inst, const int16_t *speechIn, - int16_t *encoded) + uint8_t* encoded) { ISACFIX_SubStruct *ISAC_inst; int16_t stream_len; -#ifndef WEBRTC_ARCH_BIG_ENDIAN - int k; -#endif /* typecast pointer to rela structure */ ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; @@ -350,20 +422,7 @@ return -1; } - - /* convert from bytes to int16_t */ -#ifndef WEBRTC_ARCH_BIG_ENDIAN - for (k=0;k<(stream_len+1)>>1;k++) { - encoded[k] = (int16_t)( ( (uint16_t)(ISAC_inst->ISACenc_obj.bitstr_obj).stream[k] >> 8 ) - | (((ISAC_inst->ISACenc_obj.bitstr_obj).stream[k] & 0x00FF) << 8)); - } - -#else - WEBRTC_SPL_MEMCPY_W16(encoded, (ISAC_inst->ISACenc_obj.bitstr_obj).stream, (stream_len + 1)>>1); -#endif - - - + write_be16(ISAC_inst->ISACenc_obj.bitstr_obj.stream, stream_len, encoded); return stream_len; } @@ -440,20 +499,9 @@ return -1; } - - /* convert from bytes to int16_t */ -#ifndef WEBRTC_ARCH_BIG_ENDIAN - for (k=0;k<(stream_len+1)>>1;k++) { - encoded[k] = (int16_t)(((uint16_t)(ISAC_inst->ISACenc_obj.bitstr_obj).stream[k] >> 8) - | (((ISAC_inst->ISACenc_obj.bitstr_obj).stream[k] & 0x00FF) << 8)); - } - -#else - WEBRTC_SPL_MEMCPY_W16(encoded, (ISAC_inst->ISACenc_obj.bitstr_obj).stream, (stream_len + 1)>>1); -#endif - - - + write_be16(ISAC_inst->ISACenc_obj.bitstr_obj.stream, + stream_len, + (uint8_t*)encoded); return stream_len; } #endif /* WEBRTC_ISAC_FIX_NB_CALLS_ENABLED */ @@ -481,13 +529,10 @@ int16_t WebRtcIsacfix_GetNewBitStream(ISACFIX_MainStruct *ISAC_main_inst, int16_t bweIndex, float scale, - int16_t *encoded) + uint8_t* encoded) { ISACFIX_SubStruct *ISAC_inst; int16_t stream_len; -#ifndef WEBRTC_ARCH_BIG_ENDIAN - int k; -#endif /* typecast pointer to rela structure */ ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; @@ -507,18 +552,8 @@ return -1; } -#ifndef WEBRTC_ARCH_BIG_ENDIAN - for (k=0;k<(stream_len+1)>>1;k++) { - encoded[k] = (int16_t)( ( (uint16_t)(ISAC_inst->ISACenc_obj.bitstr_obj).stream[k] >> 8 ) - | (((ISAC_inst->ISACenc_obj.bitstr_obj).stream[k] & 0x00FF) << 8)); - } - -#else - WEBRTC_SPL_MEMCPY_W16(encoded, (ISAC_inst->ISACenc_obj.bitstr_obj).stream, (stream_len + 1)>>1); -#endif - + write_be16(ISAC_inst->ISACenc_obj.bitstr_obj.stream, stream_len, encoded); return stream_len; - } @@ -580,21 +615,15 @@ */ int16_t WebRtcIsacfix_UpdateBwEstimate1(ISACFIX_MainStruct *ISAC_main_inst, - const uint16_t *encoded, + const uint8_t* encoded, int32_t packet_size, uint16_t rtp_seq_number, uint32_t arr_ts) { ISACFIX_SubStruct *ISAC_inst; Bitstr_dec streamdata; - uint16_t partOfStream[5]; -#ifndef WEBRTC_ARCH_BIG_ENDIAN - int k; -#endif int16_t err; - - /* Set stream pointer to point at partOfStream */ - streamdata.stream = (uint16_t *)partOfStream; + const int kRequiredEncodedLenBytes = 10; /* typecast pointer to real structure */ ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; @@ -616,18 +645,9 @@ return (-1); } - streamdata.W_upper = 0xFFFFFFFF; - streamdata.streamval = 0; - streamdata.stream_index = 0; - streamdata.full = 1; - -#ifndef WEBRTC_ARCH_BIG_ENDIAN - for (k=0; k<5; k++) { - streamdata.stream[k] = (uint16_t) (((uint16_t)encoded[k] >> 8)|((encoded[k] & 0xFF)<<8)); - } -#else - memcpy(streamdata.stream, encoded, 5); -#endif + InitializeDecoderBitstream(packet_size, &streamdata); + + read_be16(encoded, kRequiredEncodedLenBytes, streamdata.stream); err = WebRtcIsacfix_EstimateBandwidth(&ISAC_inst->bwestimator_obj, &streamdata, @@ -667,7 +687,7 @@ */ int16_t WebRtcIsacfix_UpdateBwEstimate(ISACFIX_MainStruct *ISAC_main_inst, - const uint16_t *encoded, + const uint8_t* encoded, int32_t packet_size, uint16_t rtp_seq_number, uint32_t send_ts, @@ -675,14 +695,8 @@ { ISACFIX_SubStruct *ISAC_inst; Bitstr_dec streamdata; - uint16_t partOfStream[5]; -#ifndef WEBRTC_ARCH_BIG_ENDIAN - int k; -#endif int16_t err; - - /* Set stream pointer to point at partOfStream */ - streamdata.stream = (uint16_t *)partOfStream; + const int kRequiredEncodedLenBytes = 10; /* typecast pointer to real structure */ ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; @@ -692,6 +706,9 @@ /* return error code if the packet length is null or less */ ISAC_inst->errorcode = ISAC_EMPTY_PACKET; return -1; + } else if (packet_size < kRequiredEncodedLenBytes) { + ISAC_inst->errorcode = ISAC_PACKET_TOO_SHORT; + return -1; } else if (packet_size > (STREAM_MAXW16<<1)) { /* return error code if length of stream is too long */ ISAC_inst->errorcode = ISAC_LENGTH_MISMATCH; @@ -704,18 +721,9 @@ return (-1); } - streamdata.W_upper = 0xFFFFFFFF; - streamdata.streamval = 0; - streamdata.stream_index = 0; - streamdata.full = 1; - -#ifndef WEBRTC_ARCH_BIG_ENDIAN - for (k=0; k<5; k++) { - streamdata.stream[k] = (uint16_t) ((encoded[k] >> 8)|((encoded[k] & 0xFF)<<8)); - } -#else - memcpy(streamdata.stream, encoded, 5); -#endif + InitializeDecoderBitstream(packet_size, &streamdata); + + read_be16(encoded, kRequiredEncodedLenBytes, streamdata.stream); err = WebRtcIsacfix_EstimateBandwidth(&ISAC_inst->bwestimator_obj, &streamdata, @@ -756,7 +764,7 @@ int16_t WebRtcIsacfix_Decode(ISACFIX_MainStruct *ISAC_main_inst, - const uint16_t *encoded, + const uint8_t* encoded, int16_t len, int16_t *decoded, int16_t *speechType) @@ -765,9 +773,6 @@ /* number of samples (480 or 960), output from decoder */ /* that were actually used in the encoder/decoder (determined on the fly) */ int16_t number_of_samples; -#ifndef WEBRTC_ARCH_BIG_ENDIAN - int k; -#endif int16_t declen = 0; /* typecast pointer to real structure */ @@ -790,16 +795,9 @@ return -1; } - (ISAC_inst->ISACdec_obj.bitstr_obj).stream = (uint16_t *)encoded; + InitializeDecoderBitstream(len, &ISAC_inst->ISACdec_obj.bitstr_obj); - /* convert bitstream from int16_t to bytes */ -#ifndef WEBRTC_ARCH_BIG_ENDIAN - for (k=0; k<(len>>1); k++) { - (ISAC_inst->ISACdec_obj.bitstr_obj).stream[k] = (uint16_t) ((encoded[k] >> 8)|((encoded[k] & 0xFF)<<8)); - } - if (len & 0x0001) - (ISAC_inst->ISACdec_obj.bitstr_obj).stream[k] = (uint16_t) ((encoded[k] & 0xFF)<<8); -#endif + read_be16(encoded, len, ISAC_inst->ISACdec_obj.bitstr_obj.stream); /* added for NetEq purposes (VAD/DTX related) */ *speechType=1; @@ -868,9 +866,6 @@ /* twice the number of samples (480 or 960), output from decoder */ /* that were actually used in the encoder/decoder (determined on the fly) */ int16_t number_of_samples; -#ifndef WEBRTC_ARCH_BIG_ENDIAN - int k; -#endif int16_t declen = 0; int16_t dummy[FRAMESAMPLES/2]; @@ -884,23 +879,19 @@ return (-1); } - if (len == 0) - { /* return error code if the packet length is null */ - + if (len <= 0) { + /* return error code if the packet length is null or less */ ISAC_inst->errorcode = ISAC_EMPTY_PACKET; return -1; + } else if (len > (STREAM_MAXW16<<1)) { + /* return error code if length of stream is too long */ + ISAC_inst->errorcode = ISAC_LENGTH_MISMATCH; + return -1; } - (ISAC_inst->ISACdec_obj.bitstr_obj).stream = (uint16_t *)encoded; + InitializeDecoderBitstream(len, &ISAC_inst->ISACdec_obj.bitstr_obj); - /* convert bitstream from int16_t to bytes */ -#ifndef WEBRTC_ARCH_BIG_ENDIAN - for (k=0; k<(len>>1); k++) { - (ISAC_inst->ISACdec_obj.bitstr_obj).stream[k] = (uint16_t) ((encoded[k] >> 8)|((encoded[k] & 0xFF)<<8)); - } - if (len & 0x0001) - (ISAC_inst->ISACdec_obj.bitstr_obj).stream[k] = (uint16_t) ((encoded[k] & 0xFF)<<8); -#endif + read_be16(encoded, len, ISAC_inst->ISACdec_obj.bitstr_obj.stream); /* added for NetEq purposes (VAD/DTX related) */ *speechType=1; @@ -1262,31 +1253,21 @@ * */ -int16_t WebRtcIsacfix_ReadFrameLen(const int16_t* encoded, +int16_t WebRtcIsacfix_ReadFrameLen(const uint8_t* encoded, + int encoded_len_bytes, int16_t* frameLength) { Bitstr_dec streamdata; - uint16_t partOfStream[5]; -#ifndef WEBRTC_ARCH_BIG_ENDIAN - int k; -#endif int16_t err; + const int kRequiredEncodedLenBytes = 10; - /* Set stream pointer to point at partOfStream */ - streamdata.stream = (uint16_t *)partOfStream; - - streamdata.W_upper = 0xFFFFFFFF; - streamdata.streamval = 0; - streamdata.stream_index = 0; - streamdata.full = 1; - -#ifndef WEBRTC_ARCH_BIG_ENDIAN - for (k=0; k<5; k++) { - streamdata.stream[k] = (uint16_t) (((uint16_t)encoded[k] >> 8)|((encoded[k] & 0xFF)<<8)); + if (encoded_len_bytes < kRequiredEncodedLenBytes) { + return -1; } -#else - memcpy(streamdata.stream, encoded, 5); -#endif + + InitializeDecoderBitstream(encoded_len_bytes, &streamdata); + + read_be16(encoded, kRequiredEncodedLenBytes, streamdata.stream); /* decode frame length */ err = WebRtcIsacfix_DecodeFrameLen(&streamdata, frameLength); @@ -1311,31 +1292,21 @@ * */ -int16_t WebRtcIsacfix_ReadBwIndex(const int16_t* encoded, +int16_t WebRtcIsacfix_ReadBwIndex(const uint8_t* encoded, + int encoded_len_bytes, int16_t* rateIndex) { Bitstr_dec streamdata; - uint16_t partOfStream[5]; -#ifndef WEBRTC_ARCH_BIG_ENDIAN - int k; -#endif int16_t err; + const int kRequiredEncodedLenBytes = 10; - /* Set stream pointer to point at partOfStream */ - streamdata.stream = (uint16_t *)partOfStream; - - streamdata.W_upper = 0xFFFFFFFF; - streamdata.streamval = 0; - streamdata.stream_index = 0; - streamdata.full = 1; - -#ifndef WEBRTC_ARCH_BIG_ENDIAN - for (k=0; k<5; k++) { - streamdata.stream[k] = (uint16_t) (((uint16_t)encoded[k] >> 8)|((encoded[k] & 0xFF)<<8)); + if (encoded_len_bytes < kRequiredEncodedLenBytes) { + return -1; } -#else - memcpy(streamdata.stream, encoded, 5); -#endif + + InitializeDecoderBitstream(encoded_len_bytes, &streamdata); + + read_be16(encoded, kRequiredEncodedLenBytes, streamdata.stream); /* decode frame length, needed to get to the rateIndex in the bitstream */ err = WebRtcIsacfix_DecodeFrameLen(&streamdata, rateIndex); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/isacfix.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/isacfix.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/isacfix.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/isacfix.gypi 2015-02-03 14:33:34.000000000 +0000 @@ -47,12 +47,14 @@ 'lpc_masking_model.c', 'lpc_tables.c', 'pitch_estimator.c', + 'pitch_estimator_c.c', 'pitch_filter.c', 'pitch_filter_c.c', 'pitch_gain_tables.c', 'pitch_lag_tables.c', 'spectrum_ar_model_tables.c', 'transform.c', + 'transform_tables.c', 'arith_routins.h', 'bandwidth_estimator.h', 'codec.h', @@ -85,6 +87,35 @@ 'pitch_filter_c.c', ], }], + ['target_arch=="mipsel" and mips_arch_variant!="r6" and android_webview_build==0', { + 'sources': [ + 'entropy_coding_mips.c', + 'filters_mips.c', + 'lattice_mips.c', + 'pitch_estimator_mips.c', + 'transform_mips.c', + ], + 'sources!': [ + 'lattice_c.c', + 'pitch_estimator_c.c', + ], + 'conditions': [ + ['mips_dsp_rev>0', { + 'sources': [ + 'filterbanks_mips.c', + ], + }], + ['mips_dsp_rev>1', { + 'sources': [ + 'lpc_masking_model_mips.c', + 'pitch_filter_mips.c', + ], + 'sources!': [ + 'pitch_filter_c.c', + ], + }], + ], + }], ], }, ], @@ -109,6 +140,15 @@ 'lpc_masking_model_neon.S', 'transform_neon.S', ], + 'conditions': [ + # Disable LTO in isac_neon target due to compiler bug + ['use_lto==1', { + 'cflags!': [ + '-flto', + '-ffat-lto-objects', + ], + }], + ], }, ], }], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lattice.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lattice.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lattice.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lattice.c 2015-02-03 14:33:34.000000000 +0000 @@ -136,7 +136,7 @@ gain32 = WEBRTC_SPL_MUL_16_32_RSFT15(cthQ15[k], gain32); //Q15*Q(17+gain_sh)>>15 = Q(17+gain_sh) inv_cthQ16[k] = WebRtcSpl_DivW32W16((int32_t)2147483647, cthQ15[k]); // 1/cth[k] in Q31/Q15 = Q16 } - gain16 = (int16_t) WEBRTC_SPL_RSHIFT_W32(gain32, 16); //Q(1+gain_sh) + gain16 = (int16_t)(gain32 >> 16); // Q(1+gain_sh). /* normalized lattice filter */ /*****************************/ @@ -158,7 +158,7 @@ tmp32 = WEBRTC_SPL_MUL_16_32_RSFT15(sthQ15[i-1], stateGQ15[i-1]);//Q15*Q15>>15 = Q15 tmp32b= fQtmp + tmp32; //Q15+Q15=Q15 tmp32 = inv_cthQ16[i-1]; //Q16 - t16a = (int16_t) WEBRTC_SPL_RSHIFT_W32(tmp32, 16); + t16a = (int16_t)(tmp32 >> 16); t16b = (int16_t) (tmp32-WEBRTC_SPL_LSHIFT_W32(((int32_t)t16a), 16)); if (t16b<0) t16a++; tmp32 = LATTICE_MUL_32_32_RSFT16(t16a, t16b, tmp32b); @@ -186,7 +186,7 @@ for(n=0;n Q17 + //gain32 >>= gain_sh; // Q(17+gain_sh) -> Q17 tmp32 = WEBRTC_SPL_MUL_16_32_RSFT16(gain16, fQ15vec[n]); //Q(1+gain_sh)*Q15>>16 = Q(gain_sh) sh = 9-gain_sh; //number of needed shifts to reach Q9 t16a = (int16_t) WEBRTC_SPL_SHIFT_W32(tmp32, sh); @@ -267,7 +267,7 @@ inv_gain32 = WebRtcSpl_DivW32W16((int32_t)2147483647, den16); // 1/gain in Q31/Q(sh+11) = Q(20-sh) //initial conditions - inv_gain16 = (int16_t) WEBRTC_SPL_RSHIFT_W32(inv_gain32, 2); // 1/gain in Q(20-sh-2) = Q(18-sh) + inv_gain16 = (int16_t)(inv_gain32 >> 2); // 1/gain in Q(20-sh-2) = Q(18-sh) for (i=0;i=0;i--) //get the state of f&g for the first input, for all orders { - tmp32 = WEBRTC_SPL_RSHIFT_W32(((WEBRTC_SPL_MUL_16_16(cthQ15[i],ARfQ0vec[0])) - (WEBRTC_SPL_MUL_16_16(sthQ15[i],stateGQ0[i])) + 16384), 15); + tmp32 = (cthQ15[i] * ARfQ0vec[0] - sthQ15[i] * stateGQ0[i] + 16384) >> 15; tmpAR = (int16_t)WebRtcSpl_SatW32ToW16(tmp32); // Q0 - tmp32 = WEBRTC_SPL_RSHIFT_W32(((WEBRTC_SPL_MUL_16_16(sthQ15[i],ARfQ0vec[0])) + (WEBRTC_SPL_MUL_16_16(cthQ15[i], stateGQ0[i])) + 16384), 15); + tmp32 = (sthQ15[i] * ARfQ0vec[0] + cthQ15[i] * stateGQ0[i] + 16384) >> 15; ARgQ0vec[i+1] = (int16_t)WebRtcSpl_SatW32ToW16(tmp32); // Q0 ARfQ0vec[0] = tmpAR; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lattice_c.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lattice_c.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lattice_c.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lattice_c.c 2015-02-03 14:33:34.000000000 +0000 @@ -16,7 +16,7 @@ #include "settings.h" #include "signal_processing_library.h" -#include "typedefs.h" +#include "webrtc/typedefs.h" /* Filter ar_g_Q0[] and ar_f_Q0[] through an AR filter with coefficients * cth_Q15[] and sth_Q15[]. @@ -36,10 +36,8 @@ tmpAR = ar_f_Q0[n + 1]; for (k = order_coef - 1; k >= 0; k--) { - tmp32 = WEBRTC_SPL_RSHIFT_W32(((WEBRTC_SPL_MUL_16_16(cth_Q15[k], tmpAR)) - - (WEBRTC_SPL_MUL_16_16(sth_Q15[k], ar_g_Q0[k])) + 16384), 15); - tmp32_2 = WEBRTC_SPL_RSHIFT_W32(((WEBRTC_SPL_MUL_16_16(sth_Q15[k], tmpAR)) - + (WEBRTC_SPL_MUL_16_16(cth_Q15[k], ar_g_Q0[k])) + 16384), 15); + tmp32 = (cth_Q15[k] * tmpAR - sth_Q15[k] * ar_g_Q0[k] + 16384) >> 15; + tmp32_2 = (sth_Q15[k] * tmpAR + cth_Q15[k] * ar_g_Q0[k] + 16384) >> 15; tmpAR = (int16_t)WebRtcSpl_SatW32ToW16(tmp32); ar_g_Q0[k + 1] = (int16_t)WebRtcSpl_SatW32ToW16(tmp32_2); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lattice_mips.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lattice_mips.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lattice_mips.c 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lattice_mips.c 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/settings.h" +#include "webrtc/typedefs.h" + +// Filter ar_g_Q0[] and ar_f_Q0[] through an AR filter with coefficients +// cth_Q15[] and sth_Q15[]. +void WebRtcIsacfix_FilterArLoop(int16_t* ar_g_Q0, // Input samples + int16_t* ar_f_Q0, // Input samples + int16_t* cth_Q15, // Filter coefficients + int16_t* sth_Q15, // Filter coefficients + int16_t order_coef) { // order of the filter + int n = 0; + + for (n = 0; n < HALF_SUBFRAMELEN - 1; n++) { + int count = order_coef - 1; + int offset; +#if !defined(MIPS_DSP_R1_LE) + int16_t* tmp_cth; + int16_t* tmp_sth; + int16_t* tmp_arg; + int32_t max_q16 = 0x7fff; + int32_t min_q16 = 0xffff8000; +#endif + // Declare variables used as temporary registers. + int32_t r0, r1, r2, t0, t1, t2, t_ar; + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "bltz %[count], 2f \n\t" + " lh %[t_ar], 0(%[tmp]) \n\t" + // Inner loop + "1: \n\t" + "sll %[offset], %[count], 1 \n\t" +#if defined(MIPS_DSP_R1_LE) + "lhx %[r0], %[offset](%[cth_Q15]) \n\t" + "lhx %[r1], %[offset](%[sth_Q15]) \n\t" + "lhx %[r2], %[offset](%[ar_g_Q0]) \n\t" +#else + "addu %[tmp_cth], %[cth_Q15], %[offset] \n\t" + "addu %[tmp_sth], %[sth_Q15], %[offset] \n\t" + "addu %[tmp_arg], %[ar_g_Q0], %[offset] \n\t" + "lh %[r0], 0(%[tmp_cth]) \n\t" + "lh %[r1], 0(%[tmp_sth]) \n\t" + "lh %[r2], 0(%[tmp_arg]) \n\t" +#endif + "mul %[t0], %[r0], %[t_ar] \n\t" + "mul %[t1], %[r1], %[t_ar] \n\t" + "mul %[t2], %[r1], %[r2] \n\t" + "mul %[r0], %[r0], %[r2] \n\t" + "subu %[t0], %[t0], %[t2] \n\t" + "addu %[t1], %[t1], %[r0] \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[t1], %[t1], 15 \n\t" + "shra_r.w %[t0], %[t0], 15 \n\t" +#else + "addiu %[t1], %[t1], 0x4000 \n\t" + "sra %[t1], %[t1], 15 \n\t" + "addiu %[t0], %[t0], 0x4000 \n\t" + "sra %[t0], %[t0], 15 \n\t" +#endif + "addiu %[offset], %[offset], 2 \n\t" +#if defined(MIPS_DSP_R1_LE) + "shll_s.w %[t1], %[t1], 16 \n\t" + "shll_s.w %[t_ar], %[t0], 16 \n\t" +#else + "slt %[r0], %[t1], %[max_q16] \n\t" + "slt %[r1], %[t0], %[max_q16] \n\t" + "movz %[t1], %[max_q16], %[r0] \n\t" + "movz %[t0], %[max_q16], %[r1] \n\t" +#endif + "addu %[offset], %[offset], %[ar_g_Q0] \n\t" +#if defined(MIPS_DSP_R1_LE) + "sra %[t1], %[t1], 16 \n\t" + "sra %[t_ar], %[t_ar], 16 \n\t" +#else + "slt %[r0], %[t1], %[min_q16] \n\t" + "slt %[r1], %[t0], %[min_q16] \n\t" + "movn %[t1], %[min_q16], %[r0] \n\t" + "movn %[t0], %[min_q16], %[r1] \n\t" + "addu %[t_ar], $zero, %[t0] \n\t" +#endif + "sh %[t1], 0(%[offset]) \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[count], %[count], -1 \n\t" + "2: \n\t" + "sh %[t_ar], 0(%[tmp]) \n\t" + "sh %[t_ar], 0(%[ar_g_Q0]) \n\t" + ".set pop \n\t" + : [t_ar] "=&r" (t_ar), [count] "+r" (count), [offset] "=&r" (offset), + [r0] "=&r" (r0), [r1] "=&r" (r1), [r2] "=&r" (r2), [t0] "=&r" (t0), +#if !defined(MIPS_DSP_R1_LE) + [tmp_cth] "=&r" (tmp_cth), [tmp_sth] "=&r" (tmp_sth), + [tmp_arg] "=&r" (tmp_arg), +#endif + [t1] "=&r" (t1), [t2] "=&r" (t2) + : [tmp] "r" (&ar_f_Q0[n+1]), [cth_Q15] "r" (cth_Q15), +#if !defined(MIPS_DSP_R1_LE) + [max_q16] "r" (max_q16), [min_q16] "r" (min_q16), +#endif + [sth_Q15] "r" (sth_Q15), [ar_g_Q0] "r" (ar_g_Q0) + : "memory", "hi", "lo" + ); + } +} + +// MIPS optimization of the inner loop used for function +// WebRtcIsacfix_NormLatticeFilterMa(). It does: +// +// for 0 <= n < HALF_SUBFRAMELEN - 1: +// *ptr2 = input2 * (*ptr2) + input0 * (*ptr0)); +// *ptr1 = input1 * (*ptr0) + input0 * (*ptr2); +// +// Note, function WebRtcIsacfix_FilterMaLoopMIPS and WebRtcIsacfix_FilterMaLoopC +// are not bit-exact. The accuracy of the MIPS function is same or better. +void WebRtcIsacfix_FilterMaLoopMIPS(int16_t input0, // Filter coefficient + int16_t input1, // Filter coefficient + int32_t input2, // Inverse coeff (1/input1) + int32_t* ptr0, // Sample buffer + int32_t* ptr1, // Sample buffer + int32_t* ptr2) { // Sample buffer +#if defined(MIPS_DSP_R2_LE) + // MIPS DSPR2 version. 4 available accumulators allows loop unrolling 4 times. + // This variant is not bit-exact with WebRtcIsacfix_FilterMaLoopC, since we + // are exploiting 64-bit accumulators. The accuracy of the MIPS DSPR2 function + // is same or better. + int n = (HALF_SUBFRAMELEN - 1) >> 2; + int m = (HALF_SUBFRAMELEN - 1) & 3; + + int r0, r1, r2, r3; + int t0, t1, t2, t3; + int s0, s1, s2, s3; + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "1: \n\t" + "lw %[r0], 0(%[ptr0]) \n\t" + "lw %[r1], 4(%[ptr0]) \n\t" + "lw %[r2], 8(%[ptr0]) \n\t" + "lw %[r3], 12(%[ptr0]) \n\t" + "mult $ac0, %[r0], %[input0] \n\t" + "mult $ac1, %[r1], %[input0] \n\t" + "mult $ac2, %[r2], %[input0] \n\t" + "mult $ac3, %[r3], %[input0] \n\t" + "lw %[t0], 0(%[ptr2]) \n\t" + "extr_rs.w %[s0], $ac0, 15 \n\t" + "extr_rs.w %[s1], $ac1, 15 \n\t" + "extr_rs.w %[s2], $ac2, 15 \n\t" + "extr_rs.w %[s3], $ac3, 15 \n\t" + "lw %[t1], 4(%[ptr2]) \n\t" + "lw %[t2], 8(%[ptr2]) \n\t" + "lw %[t3], 12(%[ptr2]) \n\t" + "addu %[t0], %[t0], %[s0] \n\t" + "addu %[t1], %[t1], %[s1] \n\t" + "addu %[t2], %[t2], %[s2] \n\t" + "addu %[t3], %[t3], %[s3] \n\t" + "mult $ac0, %[t0], %[input2] \n\t" + "mult $ac1, %[t1], %[input2] \n\t" + "mult $ac2, %[t2], %[input2] \n\t" + "mult $ac3, %[t3], %[input2] \n\t" + "addiu %[ptr0], %[ptr0], 16 \n\t" + "extr_rs.w %[t0], $ac0, 16 \n\t" + "extr_rs.w %[t1], $ac1, 16 \n\t" + "extr_rs.w %[t2], $ac2, 16 \n\t" + "extr_rs.w %[t3], $ac3, 16 \n\t" + "addiu %[n], %[n], -1 \n\t" + "mult $ac0, %[r0], %[input1] \n\t" + "mult $ac1, %[r1], %[input1] \n\t" + "mult $ac2, %[r2], %[input1] \n\t" + "mult $ac3, %[r3], %[input1] \n\t" + "sw %[t0], 0(%[ptr2]) \n\t" + "extr_rs.w %[s0], $ac0, 15 \n\t" + "extr_rs.w %[s1], $ac1, 15 \n\t" + "extr_rs.w %[s2], $ac2, 15 \n\t" + "extr_rs.w %[s3], $ac3, 15 \n\t" + "sw %[t1], 4(%[ptr2]) \n\t" + "sw %[t2], 8(%[ptr2]) \n\t" + "sw %[t3], 12(%[ptr2]) \n\t" + "mult $ac0, %[t0], %[input0] \n\t" + "mult $ac1, %[t1], %[input0] \n\t" + "mult $ac2, %[t2], %[input0] \n\t" + "mult $ac3, %[t3], %[input0] \n\t" + "addiu %[ptr2], %[ptr2], 16 \n\t" + "extr_rs.w %[t0], $ac0, 15 \n\t" + "extr_rs.w %[t1], $ac1, 15 \n\t" + "extr_rs.w %[t2], $ac2, 15 \n\t" + "extr_rs.w %[t3], $ac3, 15 \n\t" + "addu %[t0], %[t0], %[s0] \n\t" + "addu %[t1], %[t1], %[s1] \n\t" + "addu %[t2], %[t2], %[s2] \n\t" + "addu %[t3], %[t3], %[s3] \n\t" + "sw %[t0], 0(%[ptr1]) \n\t" + "sw %[t1], 4(%[ptr1]) \n\t" + "sw %[t2], 8(%[ptr1]) \n\t" + "sw %[t3], 12(%[ptr1]) \n\t" + "bgtz %[n], 1b \n\t" + " addiu %[ptr1], %[ptr1], 16 \n\t" + "beq %[m], %0, 3f \n\t" + " nop \n\t" + "2: \n\t" + "lw %[r0], 0(%[ptr0]) \n\t" + "lw %[t0], 0(%[ptr2]) \n\t" + "addiu %[ptr0], %[ptr0], 4 \n\t" + "mult $ac0, %[r0], %[input0] \n\t" + "mult $ac1, %[r0], %[input1] \n\t" + "extr_rs.w %[r1], $ac0, 15 \n\t" + "extr_rs.w %[t1], $ac1, 15 \n\t" + "addu %[t0], %[t0], %[r1] \n\t" + "mult $ac0, %[t0], %[input2] \n\t" + "extr_rs.w %[t0], $ac0, 16 \n\t" + "sw %[t0], 0(%[ptr2]) \n\t" + "mult $ac0, %[t0], %[input0] \n\t" + "addiu %[ptr2], %[ptr2], 4 \n\t" + "addiu %[m], %[m], -1 \n\t" + "extr_rs.w %[t0], $ac0, 15 \n\t" + "addu %[t0], %[t0], %[t1] \n\t" + "sw %[t0], 0(%[ptr1]) \n\t" + "bgtz %[m], 2b \n\t" + " addiu %[ptr1], %[ptr1], 4 \n\t" + "3: \n\t" + ".set pop \n\t" + : [r0] "=&r" (r0), [r1] "=&r" (r1), [r2] "=&r" (r2), + [r3] "=&r" (r3), [t0] "=&r" (t0), [t1] "=&r" (t1), + [t2] "=&r" (t2), [t3] "=&r" (t3), [s0] "=&r" (s0), + [s1] "=&r" (s1), [s2] "=&r" (s2), [s3] "=&r" (s3), + [ptr0] "+r" (ptr0), [ptr1] "+r" (ptr1), [m] "+r" (m), + [ptr2] "+r" (ptr2), [n] "+r" (n) + : [input0] "r" (input0), [input1] "r" (input1), + [input2] "r" (input2) + : "memory", "hi", "lo", "$ac1hi", "$ac1lo", "$ac2hi", + "$ac2lo", "$ac3hi", "$ac3lo" + ); +#else + // Non-DSPR2 version of the function. Avoiding the accumulator usage due to + // large latencies. This variant is bit-exact with C code. + int n = HALF_SUBFRAMELEN - 1; + int32_t t16a, t16b; + int32_t r0, r1, r2, r3, r4; + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "sra %[t16a], %[input2], 16 \n\t" + "andi %[t16b], %[input2], 0xFFFF \n\t" +#if defined(MIPS32R2_LE) + "seh %[t16b], %[t16b] \n\t" + "seh %[input0], %[input0] \n\t" + "seh %[input1], %[input1] \n\t" +#else + "sll %[t16b], %[t16b], 16 \n\t" + "sra %[t16b], %[t16b], 16 \n\t" + "sll %[input0], %[input0], 16 \n\t" + "sra %[input0], %[input0], 16 \n\t" + "sll %[input1], %[input1], 16 \n\t" + "sra %[input1], %[input1], 16 \n\t" +#endif + "addiu %[r0], %[t16a], 1 \n\t" + "slt %[r1], %[t16b], $zero \n\t" + "movn %[t16a], %[r0], %[r1] \n\t" + "1: \n\t" + "lw %[r0], 0(%[ptr0]) \n\t" + "lw %[r1], 0(%[ptr2]) \n\t" + "addiu %[ptr0], %[ptr0], 4 \n\t" + "sra %[r2], %[r0], 16 \n\t" + "andi %[r0], %[r0], 0xFFFF \n\t" + "mul %[r3], %[r2], %[input0] \n\t" + "mul %[r4], %[r0], %[input0] \n\t" + "mul %[r2], %[r2], %[input1] \n\t" + "mul %[r0], %[r0], %[input1] \n\t" + "addiu %[ptr2], %[ptr2], 4 \n\t" + "sll %[r3], %[r3], 1 \n\t" + "sra %[r4], %[r4], 1 \n\t" + "addiu %[r4], %[r4], 0x2000 \n\t" + "sra %[r4], %[r4], 14 \n\t" + "addu %[r3], %[r3], %[r4] \n\t" + "addu %[r1], %[r1], %[r3] \n\t" + "sra %[r3], %[r1], 16 \n\t" + "andi %[r4], %[r1], 0xFFFF \n\t" + "sra %[r4], %[r4], 1 \n\t" + "mul %[r1], %[r1], %[t16a] \n\t" + "mul %[r3], %[r3], %[t16b] \n\t" + "mul %[r4], %[r4], %[t16b] \n\t" + "sll %[r2], %[r2], 1 \n\t" + "sra %[r0], %[r0], 1 \n\t" + "addiu %[r0], %[r0], 0x2000 \n\t" + "sra %[r0], %[r0], 14 \n\t" + "addu %[r0], %[r0], %[r2] \n\t" + "addiu %[n], %[n], -1 \n\t" + "addu %[r1], %[r1], %[r3] \n\t" + "addiu %[r4], %[r4], 0x4000 \n\t" + "sra %[r4], %[r4], 15 \n\t" + "addu %[r1], %[r1], %[r4] \n\t" + "sra %[r2], %[r1], 16 \n\t" + "andi %[r3], %[r1], 0xFFFF \n\t" + "mul %[r3], %[r3], %[input0] \n\t" + "mul %[r2], %[r2], %[input0] \n\t" + "sw %[r1], -4(%[ptr2]) \n\t" + "sra %[r3], %[r3], 1 \n\t" + "addiu %[r3], %[r3], 0x2000 \n\t" + "sra %[r3], %[r3], 14 \n\t" + "addu %[r0], %[r0], %[r3] \n\t" + "sll %[r2], %[r2], 1 \n\t" + "addu %[r0], %[r0], %[r2] \n\t" + "sw %[r0], 0(%[ptr1]) \n\t" + "bgtz %[n], 1b \n\t" + " addiu %[ptr1], %[ptr1], 4 \n\t" + ".set pop \n\t" + : [t16a] "=&r" (t16a), [t16b] "=&r" (t16b), [r0] "=&r" (r0), + [r1] "=&r" (r1), [r2] "=&r" (r2), [r3] "=&r" (r3), + [r4] "=&r" (r4), [ptr0] "+r" (ptr0), [ptr1] "+r" (ptr1), + [ptr2] "+r" (ptr2), [n] "+r" (n) + : [input0] "r" (input0), [input1] "r" (input1), + [input2] "r" (input2) + : "hi", "lo", "memory" + ); +#endif +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model.c 2015-02-03 14:33:34.000000000 +0000 @@ -35,11 +35,11 @@ int32_t tmp_inv_denum32; int16_t tmp_inv_denum16; - k16[useOrder-1]= WEBRTC_SPL_LSHIFT_W16(a16[useOrder], 4); //Q11<<4 => Q15 + k16[useOrder-1] = a16[useOrder] << 4; // Q11<<4 => Q15 for (m=useOrder-1; m>0; m--) { tmp_inv_denum32 = ((int32_t) 1073741823) - WEBRTC_SPL_MUL_16_16(k16[m], k16[m]); // (1 - k^2) in Q30 - tmp_inv_denum16 = (int16_t) WEBRTC_SPL_RSHIFT_W32(tmp_inv_denum32, 15); // (1 - k^2) in Q15 + tmp_inv_denum16 = (int16_t)(tmp_inv_denum32 >> 15); // (1 - k^2) in Q15. for (k=1; k<=m; k++) { tmp32b = WEBRTC_SPL_LSHIFT_W32((int32_t)a16[k], 16) - @@ -49,7 +49,7 @@ } for (k=1; k>1 => Q11 + a16[k] = (int16_t)(tmp32[k] >> 1); // Q12>>1 => Q11 } tmp32[m] = WEBRTC_SPL_SAT(4092, tmp32[m], -4092); @@ -90,8 +90,8 @@ for (i=order;i>=0;i--) { temp1W32 = WEBRTC_SPL_LSHIFT_W32(R[i], norm); /* Put R in hi and low format */ - R_hi[i] = (int16_t) WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - R_low[i] = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - WEBRTC_SPL_LSHIFT_W32((int32_t)R_hi[i], 16)), 1); + R_hi[i] = (int16_t)(temp1W32 >> 16); + R_low[i] = (int16_t)((temp1W32 - ((int32_t)R_hi[i] << 16)) >> 1); } /* K = A[1] = -R[1] / R[0] */ @@ -106,41 +106,39 @@ } /* Put K in hi and low format */ - K_hi = (int16_t) WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - K_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - WEBRTC_SPL_LSHIFT_W32((int32_t)K_hi, 16)), 1); + K_hi = (int16_t)(temp1W32 >> 16); + K_low = (int16_t)((temp1W32 - ((int32_t)K_hi << 16)) >> 1); /* Store first reflection coefficient */ K[0] = K_hi; - temp1W32 = WEBRTC_SPL_RSHIFT_W32(temp1W32, 4); /* A[1] in Q27 */ + temp1W32 >>= 4; /* A[1] in Q27. */ /* Put A[1] in hi and low format */ - A_hi[1] = (int16_t) WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - A_low[1] = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - WEBRTC_SPL_LSHIFT_W32((int32_t)A_hi[1], 16)), 1); + A_hi[1] = (int16_t)(temp1W32 >> 16); + A_low[1] = (int16_t)((temp1W32 - ((int32_t)A_hi[1] << 16)) >> 1); /* Alpha = R[0] * (1-K^2) */ - temp1W32 = WEBRTC_SPL_LSHIFT_W32((WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(K_hi, K_low), 14) + - WEBRTC_SPL_MUL_16_16(K_hi, K_hi)), 1); /* temp1W32 = k^2 in Q31 */ + temp1W32 = (((K_hi * K_low) >> 14) + K_hi * K_hi) << 1; /* = k^2 in Q31 */ temp1W32 = WEBRTC_SPL_ABS_W32(temp1W32); /* Guard against <0 */ temp1W32 = (int32_t)0x7fffffffL - temp1W32; /* temp1W32 = (1 - K[0]*K[0]) in Q31 */ /* Store temp1W32 = 1 - K[0]*K[0] on hi and low format */ - tmp_hi = (int16_t) WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - tmp_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - WEBRTC_SPL_LSHIFT_W32((int32_t)tmp_hi, 16)), 1); + tmp_hi = (int16_t)(temp1W32 >> 16); + tmp_low = (int16_t)((temp1W32 - ((int32_t)tmp_hi << 16)) >> 1); /* Calculate Alpha in Q31 */ - temp1W32 = WEBRTC_SPL_LSHIFT_W32((WEBRTC_SPL_MUL_16_16(R_hi[0], tmp_hi) + - WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(R_hi[0], tmp_low), 15) + - WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(R_low[0], tmp_hi), 15) ), 1); + temp1W32 = (R_hi[0] * tmp_hi + ((R_hi[0] * tmp_low) >> 15) + + ((R_low[0] * tmp_hi) >> 15)) << 1; /* Normalize Alpha and put it in hi and low format */ Alpha_exp = WebRtcSpl_NormW32(temp1W32); temp1W32 = WEBRTC_SPL_LSHIFT_W32(temp1W32, Alpha_exp); - Alpha_hi = (int16_t) WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - Alpha_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - WEBRTC_SPL_LSHIFT_W32((int32_t)Alpha_hi, 16)), 1); + Alpha_hi = (int16_t)(temp1W32 >> 16); + Alpha_low = (int16_t)((temp1W32 - ((int32_t)Alpha_hi<< 16)) >> 1); /* Perform the iterative calculations in the Levinson Durbin algorithm */ @@ -150,7 +148,7 @@ /* ---- \ - temp1W32 = R[i] + > R[j]*A[i-j] + temp1W32 = R[i] + > R[j]*A[i-j] / ---- j=1..i-1 @@ -160,9 +158,9 @@ for(j=1; j> 15) + + ((R_low[j] * A_hi[i - j]) >> 15)) << 1); } temp1W32 = WEBRTC_SPL_LSHIFT_W32(temp1W32, 4); @@ -193,8 +191,8 @@ } /* Put K on hi and low format */ - K_hi = (int16_t) WEBRTC_SPL_RSHIFT_W32(temp3W32, 16); - K_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp3W32 - WEBRTC_SPL_LSHIFT_W32((int32_t)K_hi, 16)), 1); + K_hi = (int16_t)(temp3W32 >> 16); + K_low = (int16_t)((temp3W32 - ((int32_t)K_hi << 16)) >> 1); /* Store Reflection coefficient in Q15 */ K[i-1] = K_hi; @@ -218,45 +216,42 @@ temp1W32 = WEBRTC_SPL_LSHIFT_W32((int32_t)A_hi[j],16) + WEBRTC_SPL_LSHIFT_W32((int32_t)A_low[j],1); /* temp1W32 = A[j] in Q27 */ - temp1W32 += WEBRTC_SPL_LSHIFT_W32(( WEBRTC_SPL_MUL_16_16(K_hi, A_hi[i-j]) + - WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(K_hi, A_low[i-j]), 15) + - WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(K_low, A_hi[i-j]), 15) ), 1); /* temp1W32 += K*A[i-j] in Q27 */ + temp1W32 += (K_hi * A_hi[i - j] + ((K_hi * A_low[i - j]) >> 15) + + ((K_low * A_hi[i - j]) >> 15)) << 1; // temp1W32 += K*A[i-j] in Q27. /* Put Anew in hi and low format */ - A_upd_hi[j] = (int16_t) WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - A_upd_low[j] = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - WEBRTC_SPL_LSHIFT_W32((int32_t)A_upd_hi[j], 16)), 1); + A_upd_hi[j] = (int16_t)(temp1W32 >> 16); + A_upd_low[j] = (int16_t)((temp1W32 - ((int32_t)A_upd_hi[j] << 16)) >> 1); } - temp3W32 = WEBRTC_SPL_RSHIFT_W32(temp3W32, 4); /* temp3W32 = K in Q27 (Convert from Q31 to Q27) */ + temp3W32 >>= 4; /* temp3W32 = K in Q27 (Convert from Q31 to Q27) */ /* Store Anew in hi and low format */ - A_upd_hi[i] = (int16_t) WEBRTC_SPL_RSHIFT_W32(temp3W32, 16); - A_upd_low[i] = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp3W32 - WEBRTC_SPL_LSHIFT_W32((int32_t)A_upd_hi[i], 16)), 1); + A_upd_hi[i] = (int16_t)(temp3W32 >> 16); + A_upd_low[i] = (int16_t)((temp3W32 - ((int32_t)A_upd_hi[i] << 16)) >> 1); /* Alpha = Alpha * (1-K^2) */ - temp1W32 = WEBRTC_SPL_LSHIFT_W32((WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(K_hi, K_low), 14) + - WEBRTC_SPL_MUL_16_16(K_hi, K_hi)), 1); /* K*K in Q31 */ + temp1W32 = (((K_hi * K_low) >> 14) + K_hi * K_hi) << 1; /* K*K in Q31 */ temp1W32 = WEBRTC_SPL_ABS_W32(temp1W32); /* Guard against <0 */ temp1W32 = (int32_t)0x7fffffffL - temp1W32; /* 1 - K*K in Q31 */ /* Convert 1- K^2 in hi and low format */ - tmp_hi = (int16_t) WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - tmp_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - WEBRTC_SPL_LSHIFT_W32((int32_t)tmp_hi, 16)), 1); + tmp_hi = (int16_t)(temp1W32 >> 16); + tmp_low = (int16_t)((temp1W32 - ((int32_t)tmp_hi << 16)) >> 1); /* Calculate Alpha = Alpha * (1-K^2) in Q31 */ - temp1W32 = WEBRTC_SPL_LSHIFT_W32(( WEBRTC_SPL_MUL_16_16(Alpha_hi, tmp_hi) + - WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(Alpha_hi, tmp_low), 15) + - WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(Alpha_low, tmp_hi), 15)), 1); + temp1W32 = (Alpha_hi * tmp_hi + ((Alpha_hi * tmp_low) >> 15) + + ((Alpha_low * tmp_hi) >> 15)) << 1; /* Normalize Alpha and store it on hi and low format */ norm = WebRtcSpl_NormW32(temp1W32); temp1W32 = WEBRTC_SPL_LSHIFT_W32(temp1W32, norm); - Alpha_hi = (int16_t) WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - Alpha_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - WEBRTC_SPL_LSHIFT_W32((int32_t)Alpha_hi, 16)), 1); + Alpha_hi = (int16_t)(temp1W32 >> 16); + Alpha_low = (int16_t)((temp1W32 - ((int32_t)Alpha_hi << 16)) >> 1); /* Update the total nomalization of Alpha */ Alpha_exp = Alpha_exp + norm; @@ -282,7 +277,7 @@ temp1W32 = WEBRTC_SPL_LSHIFT_W32((int32_t)A_hi[i], 16) + WEBRTC_SPL_LSHIFT_W32((int32_t)A_low[i], 1); /* Round and store upper word */ - A[i] = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp1W32+(int32_t)32768, 16); + A[i] = (int16_t)((temp1W32 + 32768) >> 16); } return(1); /* Stable filters */ } @@ -346,17 +341,14 @@ static __inline int32_t log2_Q8_LPC( uint32_t x ) { - int32_t zeros, lg2; + int32_t zeros; int16_t frac; zeros=WebRtcSpl_NormU32(x); - frac=(int16_t)WEBRTC_SPL_RSHIFT_W32(((uint32_t)WEBRTC_SPL_LSHIFT_W32(x, zeros)&0x7FFFFFFF), 23); + frac = (int16_t)(((x << zeros) & 0x7FFFFFFF) >> 23); /* log2(x) */ - - lg2= (WEBRTC_SPL_LSHIFT_W16((31-zeros), 8)+frac); - return lg2; - + return ((31 - zeros) << 8) + frac; } static const int16_t kMulPitchGain = -25; /* 200/256 in Q5 */ @@ -422,25 +414,25 @@ tmp16=(int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(kExp2,pg3,13);/* Q13*Q10>>13 => Q10*/ if (tmp16<0) { tmp16_2 = (0x0400 | (tmp16 & 0x03FF)); - tmp16_1 = (WEBRTC_SPL_RSHIFT_W16((uint16_t)(tmp16 ^ 0xFFFF), 10)-3); /* Gives result in Q14 */ + tmp16_1 = ((uint16_t)(tmp16 ^ 0xFFFF) >> 10) - 3; /* Gives result in Q14 */ if (tmp16_1<0) - expPg=(int16_t) -WEBRTC_SPL_LSHIFT_W16(tmp16_2, -tmp16_1); + expPg = -(tmp16_2 << -tmp16_1); else - expPg=(int16_t) -WEBRTC_SPL_RSHIFT_W16(tmp16_2, tmp16_1); + expPg = -(tmp16_2 >> tmp16_1); } else expPg = (int16_t) -16384; /* 1 in Q14, since 2^0=1 */ - expPg32 = (int32_t)WEBRTC_SPL_LSHIFT_W16((int32_t)expPg, 8); /* Q22 */ + expPg32 = (int32_t)expPg << 8; /* Q22 */ divVal = WebRtcSpl_DivW32W16ResW16(expPg32, chngQ); /* Q22/Q12=Q10 */ tmp16=(int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(kExp2,divVal,13);/* Q13*Q10>>13 => Q10*/ if (tmp16<0) { tmp16_2 = (0x0400 | (tmp16 & 0x03FF)); - tmp16_1 = (WEBRTC_SPL_RSHIFT_W16((uint16_t)(tmp16 ^ 0xFFFF), 10)-3); /* Gives result in Q14 */ + tmp16_1 = ((uint16_t)(tmp16 ^ 0xFFFF) >> 10) - 3; /* Gives result in Q14 */ if (tmp16_1<0) - expPg=(int16_t) WEBRTC_SPL_LSHIFT_W16(tmp16_2, -tmp16_1); + expPg = tmp16_2 << -tmp16_1; else - expPg=(int16_t) WEBRTC_SPL_RSHIFT_W16(tmp16_2, tmp16_1); + expPg = tmp16_2 >> tmp16_1; } else expPg = (int16_t) 16384; /* 1 in Q14, since 2^0=1 */ @@ -455,11 +447,11 @@ int16_t tmp16_1, tmp16_2; tmp16_2=(int16_t)(0x0400|(x&0x03FF)); - tmp16_1=-(int16_t)WEBRTC_SPL_RSHIFT_W16(x,10); + tmp16_1 = -(x >> 10); if(tmp16_1>0) - return (int16_t) WEBRTC_SPL_RSHIFT_W16(tmp16_2, tmp16_1); + return tmp16_2 >> tmp16_1; else - return (int16_t) WEBRTC_SPL_LSHIFT_W16(tmp16_2, -tmp16_1); + return tmp16_2 << -tmp16_1; } @@ -604,16 +596,14 @@ With 0.35 in Q16 (0.35 ~= 22938/65536.0 = 0.3500061) and varscaleQ14 in Q14, we get Q16*Q14>>16 = Q14 */ - aaQ14 = (int16_t) WEBRTC_SPL_RSHIFT_W32( - (WEBRTC_SPL_MUL_16_16(22938, (8192 + WEBRTC_SPL_RSHIFT_W32(varscaleQ14, 1))) - + ((int32_t)32768)), 16); + aaQ14 = (int16_t)((22938 * (8192 + (varscaleQ14 >> 1)) + 32768) >> 16); /* Calculate tmp = (1.0 + aa*aa); in Q12 */ tmp16 = (int16_t) WEBRTC_SPL_MUL_16_16_RSFT(aaQ14, aaQ14, 15); //Q14*Q14>>15 = Q13 - tmpQQlo = 4096 + WEBRTC_SPL_RSHIFT_W16(tmp16, 1); // Q12 + Q13>>1 = Q12 + tmpQQlo = 4096 + (tmp16 >> 1); // Q12 + Q13>>1 = Q12. /* Calculate tmp = (1.0+aa) * (1.0+aa); */ - tmp16 = 8192 + WEBRTC_SPL_RSHIFT_W16(aaQ14, 1); // 1+a in Q13 + tmp16 = 8192 + (aaQ14 >> 1); // 1+a in Q13. tmpQQhi = (int16_t) WEBRTC_SPL_MUL_16_16_RSFT(tmp16, tmp16, 14); //Q13*Q13>>14 = Q12 /* replace data in buffer by new look-ahead data */ @@ -676,16 +666,16 @@ /* less noise for lower frequencies, by filtering/scaling autocorrelation sequences */ /* Calculate corrlo2[0] = tmpQQlo * corrlo[0] - 2.0*tmpQQlo * corrlo[1];*/ - corrlo2QQ[0] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_32_RSFT16(tmpQQlo, corrloQQ[0]), 1)- // Q(12+QdomLO-16)>>1 = Q(QdomLO-5) - WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_32_RSFT16(aaQ14, corrloQQ[1]), 2); // 2*Q(14+QdomLO-16)>>3 = Q(QdomLO-2)>>2 = Q(QdomLO-5) + // |corrlo2QQ| in Q(QdomLO-5). + corrlo2QQ[0] = (WEBRTC_SPL_MUL_16_32_RSFT16(tmpQQlo, corrloQQ[0]) >> 1) - + (WEBRTC_SPL_MUL_16_32_RSFT16(aaQ14, corrloQQ[1]) >> 2); /* Calculate corrlo2[n] = tmpQQlo * corrlo[n] - tmpQQlo * (corrlo[n-1] + corrlo[n+1]);*/ for (n = 1; n <= ORDERLO; n++) { - tmp32 = WEBRTC_SPL_RSHIFT_W32(corrloQQ[n-1], 1) + WEBRTC_SPL_RSHIFT_W32(corrloQQ[n+1], 1); // Q(QdomLO-1) - corrlo2QQ[n] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_32_RSFT16(tmpQQlo, corrloQQ[n]), 1)- // Q(12+QdomLO-16)>>1 = Q(QdomLO-5) - WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_32_RSFT16(aaQ14, tmp32), 2); // Q(14+QdomLO-1-16)>>2 = Q(QdomLO-3)>>2 = Q(QdomLO-5) - + tmp32 = (corrloQQ[n - 1] >> 1) + (corrloQQ[n + 1] >> 1); // Q(QdomLO-1). + corrlo2QQ[n] = (WEBRTC_SPL_MUL_16_32_RSFT16(tmpQQlo, corrloQQ[n]) >> 1) - + (WEBRTC_SPL_MUL_16_32_RSFT16(aaQ14, tmp32) >> 2); } QdomLO -= 5; @@ -708,12 +698,12 @@ /* corrlo2QQ is in Q(QdomLO) and corrhiQQ is in Q(QdomHI) before the following code segment, where we want to make sure we get a 1-bit margin */ for (n = 0; n <= ORDERLO; n++) { - corrlo2QQ[n] = WEBRTC_SPL_RSHIFT_W32(corrlo2QQ[n], 1); // Make sure we have a 1-bit margin + corrlo2QQ[n] >>= 1; // Make sure we have a 1-bit margin. } QdomLO -= 1; // Now, corrlo2QQ is in Q(QdomLO), with a 1-bit margin for (n = 0; n <= ORDERHI; n++) { - corrhiQQ[n] = WEBRTC_SPL_RSHIFT_W32(corrhiQQ[n], 1); // Make sure we have a 1-bit margin + corrhiQQ[n] >>= 1; // Make sure we have a 1-bit margin. } QdomHI -= 1; // Now, corrhiQQ is in Q(QdomHI), with a 1-bit margin @@ -734,11 +724,14 @@ tmp = WEBRTC_SPL_MUL_16_32_RSFT15(alpha, tmp); } else if ((sh-shMem)<7){ tmp = WEBRTC_SPL_SHIFT_W32(maskdata->CorrBufLoQQ[n], shMem); // Shift up CorrBufLoQQ as much as possible - tmp = WEBRTC_SPL_MUL_16_32_RSFT15(WEBRTC_SPL_LSHIFT_W16(alpha, (sh-shMem)), tmp); // Shift alpha the number of times required to get tmp in QdomLO + // Shift |alpha| the number of times required to get |tmp| in QdomLO. + tmp = WEBRTC_SPL_MUL_16_32_RSFT15(alpha << (sh - shMem), tmp); } else { tmp = WEBRTC_SPL_SHIFT_W32(maskdata->CorrBufLoQQ[n], shMem); // Shift up CorrBufHiQQ as much as possible - tmp = WEBRTC_SPL_MUL_16_32_RSFT15(WEBRTC_SPL_LSHIFT_W16(alpha, 6), tmp); // Shift alpha as much as possible without overflow the number of times required to get tmp in QdomHI - tmpCorr = WEBRTC_SPL_RSHIFT_W32(corrloQQ[n], sh-shMem-6); + // Shift |alpha| as much as possible without overflow the number of + // times required to get |tmp| in QdomLO. + tmp = WEBRTC_SPL_MUL_16_32_RSFT15(alpha << 6, tmp); + tmpCorr = corrloQQ[n] >> (sh - shMem - 6); tmp = tmp + tmpCorr; maskdata->CorrBufLoQQ[n] = tmp; newQdomLO = QdomLO-(sh-shMem-6); @@ -759,7 +752,7 @@ if( newQdomLO!=QdomLO) { for (n = 0; n <= ORDERLO; n++) { if (maskdata->CorrBufLoQdom[n] != newQdomLO) - corrloQQ[n] = WEBRTC_SPL_RSHIFT_W32(corrloQQ[n], maskdata->CorrBufLoQdom[n]-newQdomLO); + corrloQQ[n] >>= maskdata->CorrBufLoQdom[n] - newQdomLO; } QdomLO = newQdomLO; } @@ -784,15 +777,18 @@ maskdata->CorrBufHiQdom[n] = QdomHI; } else if ((sh-shMem)<7) { tmp = WEBRTC_SPL_SHIFT_W32(maskdata->CorrBufHiQQ[n], shMem); // Shift up CorrBufHiQQ as much as possible - tmp = WEBRTC_SPL_MUL_16_32_RSFT15(WEBRTC_SPL_LSHIFT_W16(alpha, (sh-shMem)), tmp); // Shift alpha the number of times required to get tmp in QdomHI + // Shift |alpha| the number of times required to get |tmp| in QdomHI. + tmp = WEBRTC_SPL_MUL_16_32_RSFT15(alpha << (sh - shMem), tmp); tmpCorr = corrhiQQ[n]; tmp = tmp + tmpCorr; maskdata->CorrBufHiQQ[n] = tmp; maskdata->CorrBufHiQdom[n] = QdomHI; } else { tmp = WEBRTC_SPL_SHIFT_W32(maskdata->CorrBufHiQQ[n], shMem); // Shift up CorrBufHiQQ as much as possible - tmp = WEBRTC_SPL_MUL_16_32_RSFT15(WEBRTC_SPL_LSHIFT_W16(alpha, 6), tmp); // Shift alpha as much as possible without overflow the number of times required to get tmp in QdomHI - tmpCorr = WEBRTC_SPL_RSHIFT_W32(corrhiQQ[n], sh-shMem-6); + // Shift |alpha| as much as possible without overflow the number of + // times required to get |tmp| in QdomHI. + tmp = WEBRTC_SPL_MUL_16_32_RSFT15(alpha << 6, tmp); + tmpCorr = corrhiQQ[n] >> (sh - shMem - 6); tmp = tmp + tmpCorr; maskdata->CorrBufHiQQ[n] = tmp; newQdomHI = QdomHI-(sh-shMem-6); @@ -813,7 +809,7 @@ if( newQdomHI!=QdomHI) { for (n = 0; n <= ORDERHI; n++) { if (maskdata->CorrBufHiQdom[n] != newQdomHI) - corrhiQQ[n] = WEBRTC_SPL_RSHIFT_W32(corrhiQQ[n], maskdata->CorrBufHiQdom[n]-newQdomHI); + corrhiQQ[n] >>= maskdata->CorrBufHiQdom[n] - newQdomHI; } QdomHI = newQdomHI; } @@ -834,13 +830,15 @@ /* bandwidth expansion */ for (n = 1; n <= ORDERLO; n++) { - a_LOQ11[n] = (int16_t) WEBRTC_SPL_MUL_16_16_RSFT_WITH_FIXROUND(kPolyVecLo[n-1], a_LOQ11[n]); + a_LOQ11[n] = (int16_t) ((WEBRTC_SPL_MUL_16_16( + kPolyVecLo[n-1], a_LOQ11[n]) + ((int32_t) (1 << 14))) >> 15); } polyHI[0] = a_HIQ12[0]; for (n = 1; n <= ORDERHI; n++) { - a_HIQ12[n] = (int16_t) WEBRTC_SPL_MUL_16_16_RSFT_WITH_FIXROUND(kPolyVecHi[n-1], a_HIQ12[n]); + a_HIQ12[n] = (int16_t) ((WEBRTC_SPL_MUL_16_16( + kPolyVecHi[n-1], a_HIQ12[n]) + ((int32_t) (1 << 14))) >> 15); polyHI[n] = a_HIQ12[n]; } @@ -862,7 +860,7 @@ WebRtcSpl_AToK_JSK(a_LOQ11, ORDERLO, rcQ15_lo); if (sh_lo & 0x0001) { - res_nrgQQ=WEBRTC_SPL_RSHIFT_W32(res_nrgQQ, 1); + res_nrgQQ >>= 1; sh_lo-=1; } @@ -876,8 +874,8 @@ //tmp32a=WEBRTC_SPL_MUL_16_16_RSFT(varscaleQ14, H_T_HQ19, 17); // Q14 - tmp32a=WEBRTC_SPL_RSHIFT_W32((int32_t) varscaleQ14,1); // H_T_HQ19=65536 (16-17=-1) ssh= WEBRTC_SPL_RSHIFT_W16(sh_lo, 1); // sqrt_nrg is in Qssh - ssh= WEBRTC_SPL_RSHIFT_W16(sh_lo, 1); // sqrt_nrg is in Qssh + tmp32a = varscaleQ14 >> 1; // H_T_HQ19=65536 (16-17=-1) + ssh = sh_lo >> 1; // sqrt_nrg is in Qssh. sh = ssh - 14; tmp32b = WEBRTC_SPL_SHIFT_W32(tmp32a, sh); // Q14->Qssh tmp32c = sqrt_nrg + tmp32b; // Qssh (denominator) @@ -911,7 +909,7 @@ WebRtcSpl_LpcToReflCoef(polyHI, ORDERHI, rcQ15_hi); if (sh_hi & 0x0001) { - res_nrgQQ=WEBRTC_SPL_RSHIFT_W32(res_nrgQQ, 1); + res_nrgQQ >>= 1; sh_hi-=1; } @@ -925,9 +923,9 @@ /* hi_coeff = varscale * S_N_R / (sqrt_nrg + varscale * H_T_H); */ //tmp32a=WEBRTC_SPL_MUL_16_16_RSFT(varscaleQ14, H_T_HQ19, 17); // Q14 - tmp32a=WEBRTC_SPL_RSHIFT_W32((int32_t) varscaleQ14,1); // H_T_HQ19=65536 (16-17=-1) + tmp32a = varscaleQ14 >> 1; // H_T_HQ19=65536 (16-17=-1) - ssh= WEBRTC_SPL_RSHIFT_W32(sh_hi, 1); // sqrt_nrg is in Qssh + ssh = sh_hi >> 1; // |sqrt_nrg| is in Qssh. sh = ssh - 14; tmp32b = WEBRTC_SPL_SHIFT_W32(tmp32a, sh); // Q14->Qssh tmp32c = sqrt_nrg + tmp32b; // Qssh (denominator) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model.h 2015-02-03 14:33:34.000000000 +0000 @@ -62,6 +62,15 @@ int* q_val_residual_energy); #endif +#if defined(MIPS_DSP_R2_LE) +int32_t WebRtcIsacfix_CalculateResidualEnergyMIPS(int lpc_order, + int32_t q_val_corr, + int q_val_polynomial, + int16_t* a_polynomial, + int32_t* corr_coeffs, + int* q_val_residual_energy); +#endif + #ifdef __cplusplus } /* extern "C" */ #endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model_mips.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model_mips.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model_mips.c 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model_mips.c 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model.h" + +// MIPS DSPR2 optimization for function WebRtcIsacfix_CalculateResidualEnergy +// Bit-exact with WebRtcIsacfix_CalculateResidualEnergyC from file +// lpc_masking_model.c +int32_t WebRtcIsacfix_CalculateResidualEnergyMIPS(int lpc_order, + int32_t q_val_corr, + int q_val_polynomial, + int16_t* a_polynomial, + int32_t* corr_coeffs, + int* q_val_residual_energy) { + + int i = 0, j = 0; + int shift_internal = 0, shift_norm = 0; + int32_t tmp32 = 0, word32_high = 0, word32_low = 0, residual_energy = 0; + int32_t tmp_corr_c = corr_coeffs[0]; + int16_t* tmp_a_poly = &a_polynomial[0]; + int32_t sum64_hi = 0; + int32_t sum64_lo = 0; + + for (j = 0; j <= lpc_order; j++) { + // For the case of i == 0: + // residual_energy += + // a_polynomial[j] * corr_coeffs[i] * a_polynomial[j - i]; + + int32_t tmp2, tmp3; + int16_t sign_1; + int16_t sign_2; + int16_t sign_3; + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "lh %[tmp2], 0(%[tmp_a_poly]) \n\t" + "mul %[tmp32], %[tmp2], %[tmp2] \n\t" + "addiu %[tmp_a_poly], %[tmp_a_poly], 2 \n\t" + "sra %[sign_2], %[sum64_hi], 31 \n\t" + "mult $ac0, %[tmp32], %[tmp_corr_c] \n\t" + "shilov $ac0, %[shift_internal] \n\t" + "mfhi %[tmp2], $ac0 \n\t" + "mflo %[tmp3], $ac0 \n\t" + "sra %[sign_1], %[tmp2], 31 \n\t" + "xor %[sign_3], %[sign_1], %[sign_2] \n\t" + ".set pop \n\t" + : [tmp2] "=&r" (tmp2), [tmp3] "=&r" (tmp3), [tmp32] "=&r" (tmp32), + [tmp_a_poly] "+r" (tmp_a_poly), [sign_1] "=&r" (sign_1), + [sign_3] "=&r" (sign_3), [sign_2] "=&r" (sign_2), + [sum64_hi] "+r" (sum64_hi), [sum64_lo] "+r" (sum64_lo) + : [tmp_corr_c] "r" (tmp_corr_c), [shift_internal] "r" (shift_internal) + : "hi", "lo", "memory" + ); + + if (sign_3 != 0) { + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addsc %[sum64_lo], %[sum64_lo], %[tmp3] \n\t" + "addwc %[sum64_hi], %[sum64_hi], %[tmp2] \n\t" + ".set pop \n\t" + : [sum64_hi] "+r" (sum64_hi), [sum64_lo] "+r" (sum64_lo) + : [tmp2] "r" (tmp2), [tmp3] "r" (tmp3) + : "hi", "lo", "memory" + ); + } else { + if (((!(sign_1 || sign_2)) && (0x7FFFFFFF - sum64_hi < tmp2)) || + ((sign_1 && sign_2) && (sum64_hi + tmp2 > 0))) { + // Shift right for overflow. + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[shift_internal], %[shift_internal], 1 \n\t" + "prepend %[sum64_lo], %[sum64_hi], 1 \n\t" + "sra %[sum64_hi], %[sum64_hi], 1 \n\t" + "prepend %[tmp3], %[tmp2], 1 \n\t" + "sra %[tmp2], %[tmp2], 1 \n\t" + "addsc %[sum64_lo], %[sum64_lo], %[tmp3] \n\t" + "addwc %[sum64_hi], %[sum64_hi], %[tmp2] \n\t" + ".set pop \n\t" + : [tmp2] "+r" (tmp2), [tmp3] "+r" (tmp3), + [shift_internal] "+r" (shift_internal), + [sum64_hi] "+r" (sum64_hi), [sum64_lo] "+r" (sum64_lo) + : + : "hi", "lo", "memory" + ); + } else { + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addsc %[sum64_lo], %[sum64_lo], %[tmp3] \n\t" + "addwc %[sum64_hi], %[sum64_hi], %[tmp2] \n\t" + ".set pop \n\t" + : [sum64_hi] "+r" (sum64_hi), [sum64_lo] "+r" (sum64_lo) + : [tmp2] "r" (tmp2), [tmp3] "r" (tmp3) + : "hi", "lo", "memory" + ); + } + } + } + + for (i = 1; i <= lpc_order; i++) { + tmp_corr_c = corr_coeffs[i]; + int16_t* tmp_a_poly_j = &a_polynomial[i]; + int16_t* tmp_a_poly_j_i = &a_polynomial[0]; + for (j = i; j <= lpc_order; j++) { + // For the case of i = 1 .. lpc_order: + // residual_energy += + // a_polynomial[j] * corr_coeffs[i] * a_polynomial[j - i] * 2; + + int32_t tmp2, tmp3; + int16_t sign_1; + int16_t sign_2; + int16_t sign_3; + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "lh %[tmp3], 0(%[tmp_a_poly_j]) \n\t" + "lh %[tmp2], 0(%[tmp_a_poly_j_i]) \n\t" + "addiu %[tmp_a_poly_j], %[tmp_a_poly_j], 2 \n\t" + "addiu %[tmp_a_poly_j_i], %[tmp_a_poly_j_i], 2 \n\t" + "mul %[tmp32], %[tmp3], %[tmp2] \n\t" + "sll %[tmp32], %[tmp32], 1 \n\t" + "mult $ac0, %[tmp32], %[tmp_corr_c] \n\t" + "shilov $ac0, %[shift_internal] \n\t" + "mfhi %[tmp2], $ac0 \n\t" + "mflo %[tmp3], $ac0 \n\t" + "sra %[sign_1], %[tmp2], 31 \n\t" + "sra %[sign_2], %[sum64_hi], 31 \n\t" + "xor %[sign_3], %[sign_1], %[sign_2] \n\t" + ".set pop \n\t" + : [tmp2] "=&r" (tmp2), [tmp3] "=&r" (tmp3), [tmp32] "=&r" (tmp32), + [tmp_a_poly_j] "+r" (tmp_a_poly_j), [sign_1] "=&r" (sign_1), + [tmp_a_poly_j_i] "+r" (tmp_a_poly_j_i), [sign_2] "=&r" (sign_2), + [sign_3] "=&r" (sign_3), [sum64_hi] "+r" (sum64_hi), + [sum64_lo] "+r" (sum64_lo) + : [tmp_corr_c] "r" (tmp_corr_c), [shift_internal] "r" (shift_internal) + : "hi", "lo", "memory" + ); + if (sign_3 != 0) { + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addsc %[sum64_lo], %[sum64_lo], %[tmp3] \n\t" + "addwc %[sum64_hi], %[sum64_hi], %[tmp2] \n\t" + ".set pop \n\t" + : [tmp2] "+r" (tmp2), [tmp3] "+r" (tmp3), [sum64_hi] "+r" (sum64_hi), + [sum64_lo] "+r" (sum64_lo) + : + :"memory" + ); + } else { + // Test overflow and sum the result. + if (((!(sign_1 || sign_2)) && (0x7FFFFFFF - sum64_hi < tmp2)) || + ((sign_1 && sign_2) && (sum64_hi + tmp2 > 0))) { + // Shift right for overflow. + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[shift_internal], %[shift_internal], 1 \n\t" + "prepend %[sum64_lo], %[sum64_hi], 1 \n\t" + "sra %[sum64_hi], %[sum64_hi], 1 \n\t" + "prepend %[tmp3], %[tmp2], 1 \n\t" + "sra %[tmp2], %[tmp2], 1 \n\t" + "addsc %[sum64_lo], %[sum64_lo], %[tmp3] \n\t" + "addwc %[sum64_hi], %[sum64_hi], %[tmp2] \n\t" + ".set pop \n\t" + : [tmp2] "+r" (tmp2), [tmp3] "+r" (tmp3), + [shift_internal] "+r" (shift_internal), + [sum64_hi] "+r" (sum64_hi), [sum64_lo] "+r" (sum64_lo) + : + : "hi", "lo", "memory" + ); + } else { + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addsc %[sum64_lo], %[sum64_lo], %[tmp3] \n\t" + "addwc %[sum64_hi], %[sum64_hi], %[tmp2] \n\t" + ".set pop \n\t" + : [tmp2] "+r" (tmp2), [tmp3] "+r" (tmp3), + [sum64_hi] "+r" (sum64_hi), [sum64_lo] "+r" (sum64_lo) + : + : "hi", "lo", "memory" + ); + } + } + } + } + word32_high = sum64_hi; + word32_low = sum64_lo; + + // Calculate the value of shifting (shift_norm) for the 64-bit sum. + if (word32_high != 0) { + shift_norm = 32 - WebRtcSpl_NormW32(word32_high); + int tmp1; + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "srl %[residual_energy], %[sum64_lo], %[shift_norm] \n\t" + "li %[tmp1], 32 \n\t" + "subu %[tmp1], %[tmp1], %[shift_norm] \n\t" + "sll %[tmp1], %[sum64_hi], %[tmp1] \n\t" + "or %[residual_energy], %[residual_energy], %[tmp1] \n\t" + ".set pop \n\t" + : [residual_energy] "=&r" (residual_energy), [tmp1]"=&r"(tmp1), + [sum64_hi] "+r" (sum64_hi), [sum64_lo] "+r" (sum64_lo) + : [shift_norm] "r" (shift_norm) + : "memory" + ); + } else { + if ((word32_low & 0x80000000) != 0) { + shift_norm = 1; + residual_energy = (uint32_t)word32_low >> 1; + } else { + shift_norm = WebRtcSpl_NormW32(word32_low); + residual_energy = word32_low << shift_norm; + shift_norm = -shift_norm; + } + } + + // Q(q_val_polynomial * 2) * Q(q_val_corr) >> shift_internal >> shift_norm + // = Q(q_val_corr - shift_internal - shift_norm + q_val_polynomial * 2) + *q_val_residual_energy = + q_val_corr - shift_internal - shift_norm + q_val_polynomial * 2; + + return residual_energy; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_masking_model.h" #include "webrtc/system_wrappers/interface/cpu_features_wrapper.h" #include "webrtc/typedefs.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_tables.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_tables.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_tables.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/lpc_tables.h 2015-02-03 14:33:34.000000000 +0000 @@ -18,8 +18,7 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_LPC_TABLES_H_ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_LPC_TABLES_H_ -#include "typedefs.h" - +#include "webrtc/typedefs.h" /* indices of KLT coefficients used */ extern const uint16_t WebRtcIsacfix_kSelIndGain[12]; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator.c 2015-02-03 14:33:34.000000000 +0000 @@ -27,15 +27,12 @@ 4096, -3072, 1024 }; - - -static __inline int32_t Log2Q8( uint32_t x ) { - +int32_t WebRtcIsacfix_Log2Q8(uint32_t x) { int32_t zeros, lg2; int16_t frac; zeros=WebRtcSpl_NormU32(x); - frac=(int16_t)WEBRTC_SPL_RSHIFT_W32(((uint32_t)(WEBRTC_SPL_LSHIFT_W32(x, zeros))&0x7FFFFFFF), 23); + frac = (int16_t)(((x << zeros) & 0x7FFFFFFF) >> 23); /* log2(magn(i)) */ lg2= (WEBRTC_SPL_LSHIFT_W32((31-zeros), 8)+frac); @@ -48,11 +45,11 @@ int16_t tmp16_1, tmp16_2; tmp16_2=(int16_t)(0x0400|(x&0x03FF)); - tmp16_1=-(int16_t)WEBRTC_SPL_RSHIFT_W16(x,10); + tmp16_1 = -(x >> 10); if(tmp16_1>0) - return (int16_t) WEBRTC_SPL_RSHIFT_W16(tmp16_2, tmp16_1); + return tmp16_2 >> tmp16_1; else - return (int16_t) WEBRTC_SPL_LSHIFT_W16(tmp16_2, -tmp16_1); + return tmp16_2 << -tmp16_1; } @@ -69,7 +66,7 @@ r32=fx[1]-fx[2]; q32=fx[0]-fx[1]; nom32=q32+r32; - den32=WEBRTC_SPL_MUL_32_16((q32-r32), 2); + den32 = (q32 - r32) * 2; if (nom32<0) sign1=-1; if (den32<0) @@ -77,9 +74,10 @@ /* t = (q32+r32)/(2*(q32-r32)) = (fx[0]-fx[1] + fx[1]-fx[2])/(2 * fx[0]-fx[1] - (fx[1]-fx[2]))*/ /* (Signs are removed because WebRtcSpl_DivResultInQ31 can't handle negative numbers) */ - t32=WebRtcSpl_DivResultInQ31(WEBRTC_SPL_MUL_32_16(nom32, sign1),WEBRTC_SPL_MUL_32_16(den32, sign2)); /* t in Q31, without signs */ + /* t in Q31, without signs */ + t32 = WebRtcSpl_DivResultInQ31(nom32 * sign1, den32 * sign2); - t16=(int16_t)WEBRTC_SPL_RSHIFT_W32(t32, 23); /* Q8 */ + t16 = (int16_t)(t32 >> 23); /* Q8 */ t16=t16*sign1*sign2; /* t in Q8 with signs */ *y = x[0]+t16; /* Q8 */ @@ -90,7 +88,7 @@ /* Part I: 0.5 * t * (t-1) * fx[0] */ tmp16_1=(int16_t)WEBRTC_SPL_MUL_16_16(t16,t16); /* Q8*Q8=Q16 */ - tmp16_1 = WEBRTC_SPL_RSHIFT_W16(tmp16_1,2); /* Q16>>2 = Q14 */ + tmp16_1 >>= 2; /* Q16>>2 = Q14 */ t16 = (int16_t)WEBRTC_SPL_MUL_16_16(t16, 64); /* Q8<<6 = Q14 */ tmp16 = tmp16_1-t16; *fy = WEBRTC_SPL_MUL_16_32_RSFT15(tmp16, fx[0]); /* (Q14 * Q8 >>15)/2 = Q8 */ @@ -153,109 +151,7 @@ -static void PCorr2Q32(const int16_t *in, int32_t *logcorQ8) -{ - int16_t scaling,n,k; - int32_t ysum32,csum32, lys, lcs; - int32_t oneQ8; - - - const int16_t *x, *inptr; - - oneQ8 = WEBRTC_SPL_LSHIFT_W32((int32_t)1, 8); // 1.00 in Q8 - - x = in + PITCH_MAX_LAG/2 + 2; - scaling = WebRtcSpl_GetScalingSquare ((int16_t *) in, PITCH_CORR_LEN2, PITCH_CORR_LEN2); - ysum32 = 1; - csum32 = 0; - x = in + PITCH_MAX_LAG/2 + 2; - for (n = 0; n < PITCH_CORR_LEN2; n++) { - ysum32 += WEBRTC_SPL_MUL_16_16_RSFT( (int16_t) in[n],(int16_t) in[n], scaling); // Q0 - csum32 += WEBRTC_SPL_MUL_16_16_RSFT((int16_t) x[n],(int16_t) in[n], scaling); // Q0 - } - - logcorQ8 += PITCH_LAG_SPAN2 - 1; - - lys=Log2Q8((uint32_t) ysum32); // Q8 - lys=WEBRTC_SPL_RSHIFT_W32(lys, 1); //sqrt(ysum); - - if (csum32>0) { - - lcs=Log2Q8((uint32_t) csum32); // 2log(csum) in Q8 - - if (lcs>(lys + oneQ8) ){ // csum/sqrt(ysum) > 2 in Q8 - *logcorQ8 = lcs - lys; // log2(csum/sqrt(ysum)) - } else { - *logcorQ8 = oneQ8; // 1.00 - } - - } else { - *logcorQ8 = 0; - } - - - for (k = 1; k < PITCH_LAG_SPAN2; k++) { - inptr = &in[k]; - ysum32 -= WEBRTC_SPL_MUL_16_16_RSFT( (int16_t) in[k-1],(int16_t) in[k-1], scaling); - ysum32 += WEBRTC_SPL_MUL_16_16_RSFT( (int16_t) in[PITCH_CORR_LEN2 + k - 1],(int16_t) in[PITCH_CORR_LEN2 + k - 1], scaling); - -#ifdef WEBRTC_ARCH_ARM_NEON - { - int32_t vbuff[4]; - int32x4_t int_32x4_sum = vmovq_n_s32(0); - // Can't shift a Neon register to right with a non-constant shift value. - int32x4_t int_32x4_scale = vdupq_n_s32(-scaling); - // Assert a codition used in loop unrolling at compile-time. - COMPILE_ASSERT(PITCH_CORR_LEN2 %4 == 0); - - for (n = 0; n < PITCH_CORR_LEN2; n += 4) { - int16x4_t int_16x4_x = vld1_s16(&x[n]); - int16x4_t int_16x4_in = vld1_s16(&inptr[n]); - int32x4_t int_32x4 = vmull_s16(int_16x4_x, int_16x4_in); - int_32x4 = vshlq_s32(int_32x4, int_32x4_scale); - int_32x4_sum = vaddq_s32(int_32x4_sum, int_32x4); - } - - // Use vector store to avoid long stall from data trasferring - // from vector to general register. - vst1q_s32(vbuff, int_32x4_sum); - csum32 = vbuff[0] + vbuff[1]; - csum32 += vbuff[2]; - csum32 += vbuff[3]; - } -#else - csum32 = 0; - if(scaling == 0) { - for (n = 0; n < PITCH_CORR_LEN2; n++) { - csum32 += x[n] * inptr[n]; - } - } else { - for (n = 0; n < PITCH_CORR_LEN2; n++) { - csum32 += (x[n] * inptr[n]) >> scaling; - } - } -#endif - - logcorQ8--; - - lys=Log2Q8((uint32_t)ysum32); // Q8 - lys=WEBRTC_SPL_RSHIFT_W32(lys, 1); //sqrt(ysum); - - if (csum32>0) { - - lcs=Log2Q8((uint32_t) csum32); // 2log(csum) in Q8 - - if (lcs>(lys + oneQ8) ){ // csum/sqrt(ysum) > 2 - *logcorQ8 = lcs - lys; // log2(csum/sqrt(ysum)) - } else { - *logcorQ8 = oneQ8; // 1.00 - } - - } else { - *logcorQ8 = 0; - } - } -} +extern void WebRtcIsacfix_PCorr2Q32(const int16_t *in, int32_t *logcorQ8); @@ -311,12 +207,13 @@ /* compute correlation for first and second half of the frame */ - PCorr2Q32(buf_dec16, crrvecQ8_1); - PCorr2Q32(buf_dec16 + PITCH_CORR_STEP2, crrvecQ8_2); + WebRtcIsacfix_PCorr2Q32(buf_dec16, crrvecQ8_1); + WebRtcIsacfix_PCorr2Q32(buf_dec16 + PITCH_CORR_STEP2, crrvecQ8_2); /* bias towards pitch lag of previous frame */ - tmp32a = Log2Q8((uint32_t) old_lagQ8) - 2304; // log2(0.5*oldlag) in Q8 + tmp32a = WebRtcIsacfix_Log2Q8((uint32_t) old_lagQ8) - 2304; + // log2(0.5*oldlag) in Q8 tmp32b = WEBRTC_SPL_MUL_16_16_RSFT(oldgQ12,oldgQ12, 10); //Q12 & * 4.0; gain_bias16 = (int16_t) tmp32b; //Q12 if (gain_bias16 > 3276) gain_bias16 = 3276; // 0.8 in Q12 @@ -325,7 +222,7 @@ for (k = 0; k < PITCH_LAG_SPAN2; k++) { if (crrvecQ8_1[k]>0) { - tmp32b = Log2Q8((uint32_t) (k + (PITCH_MIN_LAG/2-2))); + tmp32b = WebRtcIsacfix_Log2Q8((uint32_t) (k + (PITCH_MIN_LAG/2-2))); tmp16a = (int16_t) (tmp32b - tmp32a); // Q8 & fabs(ratio)<4 tmp32c = WEBRTC_SPL_MUL_16_16_RSFT(tmp16a,tmp16a, 6); //Q10 tmp16b = (int16_t) tmp32c; // Q10 & <8 @@ -334,7 +231,8 @@ tmp16d = Exp2Q10((int16_t) -tmp16c); //Q10 tmp32c = WEBRTC_SPL_MUL_16_16_RSFT(gain_bias16,tmp16d,13); // Q10 & * 0.5 bias16 = (int16_t) (1024 + tmp32c); // Q10 - tmp32b = Log2Q8((uint32_t) bias16) - 2560; // Q10 in -> Q8 out with 10*2^8 offset + tmp32b = WebRtcIsacfix_Log2Q8((uint32_t)bias16) - 2560; + // Q10 in -> Q8 out with 10*2^8 offset crrvecQ8_1[k] += tmp32b ; // -10*2^8 offset } } @@ -407,7 +305,7 @@ xq[0] = WEBRTC_SPL_LSHIFT_W32(xq[0], 8); Intrp1DQ8(xq, fxq, yq, fyq); - tmp32a= Log2Q8((uint32_t) *yq) - 2048; // offset 8*2^8 + tmp32a= WebRtcIsacfix_Log2Q8((uint32_t) *yq) - 2048; // offset 8*2^8 /* Bias towards short lags */ /* log(pow(0.8, log(2.0 * *y )))/log(2.0) */ tmp32b= WEBRTC_SPL_MUL_16_16_RSFT((int16_t) tmp32a, -42, 8); @@ -429,7 +327,7 @@ /* Bias towards constant pitch */ tmp32a = lagsQ8[0] - PITCH_MIN_LAG_Q8; - ratq = WEBRTC_SPL_RSHIFT_W32(tmp32a, 1) + OFFSET_Q8; + ratq = (tmp32a >> 1) + OFFSET_Q8; for (k = 1; k <= PITCH_LAG_SPAN2; k++) { @@ -437,12 +335,15 @@ tmp32b = (int32_t) (WEBRTC_SPL_LSHIFT_W32(tmp32a, 1)) - ratq; // Q8 tmp32c = WEBRTC_SPL_MUL_16_16_RSFT((int16_t) tmp32b, (int16_t) tmp32b, 8); // Q8 - tmp32b = (int32_t) tmp32c + (int32_t) WEBRTC_SPL_RSHIFT_W32(ratq, 1); // (k-r)^2 + 0.5 * r Q8 - tmp32c = Log2Q8((uint32_t) tmp32a) - 2048; // offset 8*2^8 , log2(0.5*k) Q8 - tmp32d = Log2Q8((uint32_t) tmp32b) - 2048; // offset 8*2^8 , log2(0.5*k) Q8 - tmp32e = tmp32c -tmp32d; + tmp32b = tmp32c + (ratq >> 1); + // (k-r)^2 + 0.5 * r Q8 + tmp32c = WebRtcIsacfix_Log2Q8((uint32_t)tmp32a) - 2048; + // offset 8*2^8 , log2(0.5*k) Q8 + tmp32d = WebRtcIsacfix_Log2Q8((uint32_t)tmp32b) - 2048; + // offset 8*2^8 , log2(0.5*k) Q8 + tmp32e = tmp32c - tmp32d; - cv2q[k] += WEBRTC_SPL_RSHIFT_W32(tmp32e, 1); + cv2q[k] += tmp32e >> 1; } @@ -481,7 +382,7 @@ /* Bias towards short lags */ /* log(pow(0.8, log(2.0f * *y )))/log(2.0f) */ - tmp32a= Log2Q8((uint32_t) *yq) - 2048; // offset 8*2^8 + tmp32a= WebRtcIsacfix_Log2Q8((uint32_t) *yq) - 2048; // offset 8*2^8 tmp32b= WEBRTC_SPL_MUL_16_16_RSFT((int16_t) tmp32a, -82, 8); tmp32c= tmp32b + 256; *fyq += tmp32c; @@ -500,12 +401,10 @@ lagsQ8[3] = lagsQ8[0]; } - lagsQ7[0]=(int16_t) WEBRTC_SPL_RSHIFT_W32(lagsQ8[0], 1); - lagsQ7[1]=(int16_t) WEBRTC_SPL_RSHIFT_W32(lagsQ8[1], 1); - lagsQ7[2]=(int16_t) WEBRTC_SPL_RSHIFT_W32(lagsQ8[2], 1); - lagsQ7[3]=(int16_t) WEBRTC_SPL_RSHIFT_W32(lagsQ8[3], 1); - - + lagsQ7[0] = (int16_t)(lagsQ8[0] >> 1); + lagsQ7[1] = (int16_t)(lagsQ8[1] >> 1); + lagsQ7[2] = (int16_t)(lagsQ8[2] >> 1); + lagsQ7[3] = (int16_t)(lagsQ8[3] >> 1); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator_c.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator_c.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator_c.c 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator_c.c 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator.h" + +#ifdef WEBRTC_ARCH_ARM_NEON +#include +#endif + +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/system_wrappers/interface/compile_assert_c.h" + +extern int32_t WebRtcIsacfix_Log2Q8(uint32_t x); + +void WebRtcIsacfix_PCorr2Q32(const int16_t* in, int32_t* logcorQ8) { + int16_t scaling,n,k; + int32_t ysum32,csum32, lys, lcs; + int32_t oneQ8; + const int16_t* x; + const int16_t* inptr; + + oneQ8 = WEBRTC_SPL_LSHIFT_W32((int32_t)1, 8); // 1.00 in Q8 + + x = in + PITCH_MAX_LAG / 2 + 2; + scaling = WebRtcSpl_GetScalingSquare((int16_t*)in, + PITCH_CORR_LEN2, + PITCH_CORR_LEN2); + ysum32 = 1; + csum32 = 0; + x = in + PITCH_MAX_LAG / 2 + 2; + for (n = 0; n < PITCH_CORR_LEN2; n++) { + ysum32 += WEBRTC_SPL_MUL_16_16_RSFT((int16_t)in[n], + (int16_t)in[n], + scaling); // Q0 + csum32 += WEBRTC_SPL_MUL_16_16_RSFT((int16_t)x[n], + (int16_t)in[n], + scaling); // Q0 + } + logcorQ8 += PITCH_LAG_SPAN2 - 1; + lys = WebRtcIsacfix_Log2Q8((uint32_t)ysum32) >> 1; // Q8, sqrt(ysum) + if (csum32 > 0) { + lcs = WebRtcIsacfix_Log2Q8((uint32_t)csum32); // 2log(csum) in Q8 + if (lcs > (lys + oneQ8)) { // csum/sqrt(ysum) > 2 in Q8 + *logcorQ8 = lcs - lys; // log2(csum/sqrt(ysum)) + } else { + *logcorQ8 = oneQ8; // 1.00 + } + } else { + *logcorQ8 = 0; + } + + + for (k = 1; k < PITCH_LAG_SPAN2; k++) { + inptr = &in[k]; + ysum32 -= WEBRTC_SPL_MUL_16_16_RSFT((int16_t)in[k - 1], + (int16_t)in[k - 1], + scaling); + ysum32 += WEBRTC_SPL_MUL_16_16_RSFT((int16_t)in[PITCH_CORR_LEN2 + k - 1], + (int16_t)in[PITCH_CORR_LEN2 + k - 1], + scaling); +#ifdef WEBRTC_ARCH_ARM_NEON + { + int32_t vbuff[4]; + int32x4_t int_32x4_sum = vmovq_n_s32(0); + // Can't shift a Neon register to right with a non-constant shift value. + int32x4_t int_32x4_scale = vdupq_n_s32(-scaling); + // Assert a codition used in loop unrolling at compile-time. + COMPILE_ASSERT(PITCH_CORR_LEN2 %4 == 0); + + for (n = 0; n < PITCH_CORR_LEN2; n += 4) { + int16x4_t int_16x4_x = vld1_s16(&x[n]); + int16x4_t int_16x4_in = vld1_s16(&inptr[n]); + int32x4_t int_32x4 = vmull_s16(int_16x4_x, int_16x4_in); + int_32x4 = vshlq_s32(int_32x4, int_32x4_scale); + int_32x4_sum = vaddq_s32(int_32x4_sum, int_32x4); + } + + // Use vector store to avoid long stall from data trasferring + // from vector to general register. + vst1q_s32(vbuff, int_32x4_sum); + csum32 = vbuff[0] + vbuff[1]; + csum32 += vbuff[2]; + csum32 += vbuff[3]; + } +#else + csum32 = 0; + if(scaling == 0) { + for (n = 0; n < PITCH_CORR_LEN2; n++) { + csum32 += x[n] * inptr[n]; + } + } else { + for (n = 0; n < PITCH_CORR_LEN2; n++) { + csum32 += (x[n] * inptr[n]) >> scaling; + } + } +#endif + + logcorQ8--; + + lys = WebRtcIsacfix_Log2Q8((uint32_t)ysum32) >> 1; // Q8, sqrt(ysum) + + if (csum32 > 0) { + lcs = WebRtcIsacfix_Log2Q8((uint32_t)csum32); // 2log(csum) in Q8 + if (lcs > (lys + oneQ8)) { // csum/sqrt(ysum) > 2 + *logcorQ8 = lcs - lys; // log2(csum/sqrt(ysum)) + } else { + *logcorQ8 = oneQ8; // 1.00 + } + } else { + *logcorQ8 = 0; + } + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator.h 2015-02-03 14:33:34.000000000 +0000 @@ -58,4 +58,8 @@ int16_t N, /* number of input samples */ int16_t *out); /* array of size N/2 */ +int32_t WebRtcIsacfix_Log2Q8( uint32_t x ); + +void WebRtcIsacfix_PCorr2Q32(const int16_t* in, int32_t* logcorQ8); + #endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_PITCH_ESTIMATOR_H_ */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator_mips.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator_mips.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator_mips.c 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator_mips.c 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator.h" +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/system_wrappers/interface/compile_assert_c.h" + +extern int32_t WebRtcIsacfix_Log2Q8(uint32_t x); + +void WebRtcIsacfix_PCorr2Q32(const int16_t* in, int32_t* logcorQ8) { + int16_t scaling,n,k; + int32_t ysum32,csum32, lys, lcs; + int32_t oneQ8; + const int16_t* x; + const int16_t* inptr; + + oneQ8 = WEBRTC_SPL_LSHIFT_W32((int32_t)1, 8); // 1.00 in Q8 + x = in + PITCH_MAX_LAG / 2 + 2; + scaling = WebRtcSpl_GetScalingSquare((int16_t*)in, + PITCH_CORR_LEN2, + PITCH_CORR_LEN2); + ysum32 = 1; + csum32 = 0; + x = in + PITCH_MAX_LAG / 2 + 2; + { + const int16_t* tmp_x = x; + const int16_t* tmp_in = in; + int32_t tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8; + n = PITCH_CORR_LEN2; + COMPILE_ASSERT(PITCH_CORR_LEN2 % 4 == 0); + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "1: \n\t" + "lh %[tmp1], 0(%[tmp_in]) \n\t" + "lh %[tmp2], 2(%[tmp_in]) \n\t" + "lh %[tmp3], 4(%[tmp_in]) \n\t" + "lh %[tmp4], 6(%[tmp_in]) \n\t" + "lh %[tmp5], 0(%[tmp_x]) \n\t" + "lh %[tmp6], 2(%[tmp_x]) \n\t" + "lh %[tmp7], 4(%[tmp_x]) \n\t" + "lh %[tmp8], 6(%[tmp_x]) \n\t" + "mul %[tmp5], %[tmp1], %[tmp5] \n\t" + "mul %[tmp1], %[tmp1], %[tmp1] \n\t" + "mul %[tmp6], %[tmp2], %[tmp6] \n\t" + "mul %[tmp2], %[tmp2], %[tmp2] \n\t" + "mul %[tmp7], %[tmp3], %[tmp7] \n\t" + "mul %[tmp3], %[tmp3], %[tmp3] \n\t" + "mul %[tmp8], %[tmp4], %[tmp8] \n\t" + "mul %[tmp4], %[tmp4], %[tmp4] \n\t" + "addiu %[n], %[n], -4 \n\t" + "srav %[tmp5], %[tmp5], %[scaling] \n\t" + "srav %[tmp1], %[tmp1], %[scaling] \n\t" + "srav %[tmp6], %[tmp6], %[scaling] \n\t" + "srav %[tmp2], %[tmp2], %[scaling] \n\t" + "srav %[tmp7], %[tmp7], %[scaling] \n\t" + "srav %[tmp3], %[tmp3], %[scaling] \n\t" + "srav %[tmp8], %[tmp8], %[scaling] \n\t" + "srav %[tmp4], %[tmp4], %[scaling] \n\t" + "addu %[ysum32], %[ysum32], %[tmp1] \n\t" + "addu %[csum32], %[csum32], %[tmp5] \n\t" + "addu %[ysum32], %[ysum32], %[tmp2] \n\t" + "addu %[csum32], %[csum32], %[tmp6] \n\t" + "addu %[ysum32], %[ysum32], %[tmp3] \n\t" + "addu %[csum32], %[csum32], %[tmp7] \n\t" + "addu %[ysum32], %[ysum32], %[tmp4] \n\t" + "addu %[csum32], %[csum32], %[tmp8] \n\t" + "addiu %[tmp_in], %[tmp_in], 8 \n\t" + "bgtz %[n], 1b \n\t" + " addiu %[tmp_x], %[tmp_x], 8 \n\t" + ".set pop \n\t" + : [tmp1] "=&r" (tmp1), [tmp2] "=&r" (tmp2), [tmp3] "=&r" (tmp3), + [tmp4] "=&r" (tmp4), [tmp5] "=&r" (tmp5), [tmp6] "=&r" (tmp6), + [tmp7] "=&r" (tmp7), [tmp8] "=&r" (tmp8), [tmp_in] "+r" (tmp_in), + [ysum32] "+r" (ysum32), [tmp_x] "+r" (tmp_x), [csum32] "+r" (csum32), + [n] "+r" (n) + : [scaling] "r" (scaling) + : "memory", "hi", "lo" + ); + } + logcorQ8 += PITCH_LAG_SPAN2 - 1; + lys = WebRtcIsacfix_Log2Q8((uint32_t)ysum32) >> 1; // Q8, sqrt(ysum) + if (csum32 > 0) { + lcs = WebRtcIsacfix_Log2Q8((uint32_t)csum32); // 2log(csum) in Q8 + if (lcs > (lys + oneQ8)) { // csum/sqrt(ysum) > 2 in Q8 + *logcorQ8 = lcs - lys; // log2(csum/sqrt(ysum)) + } else { + *logcorQ8 = oneQ8; // 1.00 + } + } else { + *logcorQ8 = 0; + } + + for (k = 1; k < PITCH_LAG_SPAN2; k++) { + inptr = &in[k]; + const int16_t* tmp_in1 = &in[k - 1]; + const int16_t* tmp_in2 = &in[PITCH_CORR_LEN2 + k - 1]; + const int16_t* tmp_x = x; + int32_t tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8; + n = PITCH_CORR_LEN2; + csum32 = 0; + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "lh %[tmp1], 0(%[tmp_in1]) \n\t" + "lh %[tmp2], 0(%[tmp_in2]) \n\t" + "mul %[tmp1], %[tmp1], %[tmp1] \n\t" + "mul %[tmp2], %[tmp2], %[tmp2] \n\t" + "srav %[tmp1], %[tmp1], %[scaling] \n\t" + "srav %[tmp2], %[tmp2], %[scaling] \n\t" + "subu %[ysum32], %[ysum32], %[tmp1] \n\t" + "bnez %[scaling], 2f \n\t" + " addu %[ysum32], %[ysum32], %[tmp2] \n\t" + "1: \n\t" + "lh %[tmp1], 0(%[inptr]) \n\t" + "lh %[tmp2], 0(%[tmp_x]) \n\t" + "lh %[tmp3], 2(%[inptr]) \n\t" + "lh %[tmp4], 2(%[tmp_x]) \n\t" + "lh %[tmp5], 4(%[inptr]) \n\t" + "lh %[tmp6], 4(%[tmp_x]) \n\t" + "lh %[tmp7], 6(%[inptr]) \n\t" + "lh %[tmp8], 6(%[tmp_x]) \n\t" + "mul %[tmp1], %[tmp1], %[tmp2] \n\t" + "mul %[tmp2], %[tmp3], %[tmp4] \n\t" + "mul %[tmp3], %[tmp5], %[tmp6] \n\t" + "mul %[tmp4], %[tmp7], %[tmp8] \n\t" + "addiu %[n], %[n], -4 \n\t" + "addiu %[inptr], %[inptr], 8 \n\t" + "addiu %[tmp_x], %[tmp_x], 8 \n\t" + "addu %[csum32], %[csum32], %[tmp1] \n\t" + "addu %[csum32], %[csum32], %[tmp2] \n\t" + "addu %[csum32], %[csum32], %[tmp3] \n\t" + "bgtz %[n], 1b \n\t" + " addu %[csum32], %[csum32], %[tmp4] \n\t" + "b 3f \n\t" + " nop \n\t" + "2: \n\t" + "lh %[tmp1], 0(%[inptr]) \n\t" + "lh %[tmp2], 0(%[tmp_x]) \n\t" + "lh %[tmp3], 2(%[inptr]) \n\t" + "lh %[tmp4], 2(%[tmp_x]) \n\t" + "lh %[tmp5], 4(%[inptr]) \n\t" + "lh %[tmp6], 4(%[tmp_x]) \n\t" + "lh %[tmp7], 6(%[inptr]) \n\t" + "lh %[tmp8], 6(%[tmp_x]) \n\t" + "mul %[tmp1], %[tmp1], %[tmp2] \n\t" + "mul %[tmp2], %[tmp3], %[tmp4] \n\t" + "mul %[tmp3], %[tmp5], %[tmp6] \n\t" + "mul %[tmp4], %[tmp7], %[tmp8] \n\t" + "addiu %[n], %[n], -4 \n\t" + "addiu %[inptr], %[inptr], 8 \n\t" + "addiu %[tmp_x], %[tmp_x], 8 \n\t" + "srav %[tmp1], %[tmp1], %[scaling] \n\t" + "srav %[tmp2], %[tmp2], %[scaling] \n\t" + "srav %[tmp3], %[tmp3], %[scaling] \n\t" + "srav %[tmp4], %[tmp4], %[scaling] \n\t" + "addu %[csum32], %[csum32], %[tmp1] \n\t" + "addu %[csum32], %[csum32], %[tmp2] \n\t" + "addu %[csum32], %[csum32], %[tmp3] \n\t" + "bgtz %[n], 2b \n\t" + " addu %[csum32], %[csum32], %[tmp4] \n\t" + "3: \n\t" + ".set pop \n\t" + : [tmp1] "=&r" (tmp1), [tmp2] "=&r" (tmp2), [tmp3] "=&r" (tmp3), + [tmp4] "=&r" (tmp4), [tmp5] "=&r" (tmp5), [tmp6] "=&r" (tmp6), + [tmp7] "=&r" (tmp7), [tmp8] "=&r" (tmp8), [inptr] "+r" (inptr), + [csum32] "+r" (csum32), [tmp_x] "+r" (tmp_x), [ysum32] "+r" (ysum32), + [n] "+r" (n) + : [tmp_in1] "r" (tmp_in1), [tmp_in2] "r" (tmp_in2), + [scaling] "r" (scaling) + : "memory", "hi", "lo" + ); + + logcorQ8--; + lys = WebRtcIsacfix_Log2Q8((uint32_t)ysum32) >> 1; // Q8, sqrt(ysum) + if (csum32 > 0) { + lcs = WebRtcIsacfix_Log2Q8((uint32_t)csum32); // 2log(csum) in Q8 + if (lcs > (lys + oneQ8)) { // csum/sqrt(ysum) > 2 + *logcorQ8 = lcs - lys; // log2(csum/sqrt(ysum)) + } else { + *logcorQ8 = oneQ8; // 1.00 + } + } else { + *logcorQ8 = 0; + } + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_filter.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_filter.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_filter.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_filter.c 2015-02-03 14:33:34.000000000 +0000 @@ -53,7 +53,7 @@ int32_t roundVal; roundVal = WEBRTC_SPL_LSHIFT_W32((int32_t)1, qDomain - 1); - intgr = WEBRTC_SPL_RSHIFT_W32(fixVal + roundVal, qDomain); + intgr = (fixVal + roundVal) >> qDomain; return intgr; } @@ -132,8 +132,7 @@ indW32 = CalcLrIntQ(curLagQ7, 7); tmpW32 = WEBRTC_SPL_LSHIFT_W32(indW32, 7); tmpW32 -= curLagQ7; - frcQQ = WEBRTC_SPL_RSHIFT_W32(tmpW32, 4); - frcQQ += 4; + frcQQ = (tmpW32 >> 4) + 4; if (frcQQ == PITCH_FRACS) { frcQQ = 0; @@ -205,10 +204,8 @@ // Update parameters for each segment. curLagQ7 += lagdeltaQ7; indW16 = (int16_t)CalcLrIntQ(curLagQ7, 7); - tmpW16 = WEBRTC_SPL_LSHIFT_W16(indW16, 7); - tmpW16 -= curLagQ7; - frcQQ = WEBRTC_SPL_RSHIFT_W16(tmpW16, 4); - frcQQ += 4; + tmpW16 = (indW16 << 7) - curLagQ7; + frcQQ = (tmpW16 >> 4) + 4; if (frcQQ == PITCH_FRACS) { frcQQ = 0; @@ -230,19 +227,17 @@ tmp2W32 = WEBRTC_SPL_MUL_16_32_RSFT14(indatQ0[ind], tmpW32); tmpW32 += 8192; - tmpW16 = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmpW32, 14); + tmpW16 = (int16_t)(tmpW32 >> 14); tmpW32 = WEBRTC_SPL_MUL_16_16(tmpW16, tmpW16); if ((tmp2W32 > 1073700000) || (csum1QQ > 1073700000) || (tmpW32 > 1073700000) || (esumxQQ > 1073700000)) { // 2^30 scale++; - csum1QQ = WEBRTC_SPL_RSHIFT_W32(csum1QQ, 1); - esumxQQ = WEBRTC_SPL_RSHIFT_W32(esumxQQ, 1); + csum1QQ >>= 1; + esumxQQ >>= 1; } - tmp2W32 = WEBRTC_SPL_RSHIFT_W32(tmp2W32, scale); - csum1QQ += tmp2W32; - tmpW32 = WEBRTC_SPL_RSHIFT_W32(tmpW32, scale); - esumxQQ += tmpW32; + csum1QQ += tmp2W32 >> scale; + esumxQQ += tmpW32 >> scale; ind++; pos++; @@ -254,7 +249,7 @@ tmp2W32 = WebRtcSpl_DivResultInQ31(csum1QQ, esumxQQ); // Gain should be half the correlation. - tmpW32 = WEBRTC_SPL_RSHIFT_W32(tmp2W32, 20); + tmpW32 = tmp2W32 >> 20; } else { tmpW32 = 4096; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_filter_c.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_filter_c.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_filter_c.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_filter_c.c 2015-02-03 14:33:34.000000000 +0000 @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "common_audio/signal_processing/include/signal_processing_library.h" -#include "modules/audio_coding/codecs/isac/fix/source/pitch_estimator.h" +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator.h" /* Filter coefficicients in Q15. */ static const int16_t kDampFilter[PITCH_DAMPORDER] = { @@ -41,7 +41,7 @@ /* Saturate to avoid overflow in tmpW16. */ tmpW32 = WEBRTC_SPL_SAT(536862719, tmpW32, -536879104); tmpW32 += 8192; - tmpW16 = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmpW32, 14); + tmpW16 = (int16_t)(tmpW32 >> 14); /* Shift low pass filter state. */ memmove(&inputState[1], &inputState[0], @@ -60,7 +60,7 @@ /* Saturate to avoid overflow in tmpW16. */ tmpW32 = WEBRTC_SPL_SAT(1073725439, tmpW32, -1073758208); tmpW32 += 16384; - tmpW16 = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmpW32, 15); + tmpW16 = (int16_t)(tmpW32 >> 15); /* Subtract from input and update buffer. */ tmpW32 = inputBuf[*index2] - WEBRTC_SPL_MUL_16_16(sign, tmpW16); @@ -71,4 +71,3 @@ (*index2)++; } } - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_filter_mips.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_filter_mips.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_filter_mips.c 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_filter_mips.c 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_estimator.h" + +void WebRtcIsacfix_PitchFilterCore(int loopNumber, + int16_t gain, + int index, + int16_t sign, + int16_t* inputState, + int16_t* outputBuf2, + const int16_t* coefficient, + int16_t* inputBuf, + int16_t* outputBuf, + int* index2) { + int ind2t = *index2; + int i = 0; + int16_t* out2_pos2 = &outputBuf2[PITCH_BUFFSIZE - (index + 2)] + ind2t; + int32_t w1, w2, w3, w4, w5, gain32, sign32; + int32_t coef1, coef2, coef3, coef4, coef5 = 0; + // Define damp factors as int32_t (pair of int16_t) + int32_t kDampF0 = 0x0000F70A; + int32_t kDampF1 = 0x51EC2000; + int32_t kDampF2 = 0xF70A2000; + int16_t* input1 = inputBuf + ind2t; + int16_t* output1 = outputBuf + ind2t; + int16_t* output2 = outputBuf2 + ind2t + PITCH_BUFFSIZE; + + // Load coefficients outside the loop and sign-extend gain and sign + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "lwl %[coef1], 3(%[coefficient]) \n\t" + "lwl %[coef2], 7(%[coefficient]) \n\t" + "lwl %[coef3], 11(%[coefficient]) \n\t" + "lwl %[coef4], 15(%[coefficient]) \n\t" + "lwr %[coef1], 0(%[coefficient]) \n\t" + "lwr %[coef2], 4(%[coefficient]) \n\t" + "lwr %[coef3], 8(%[coefficient]) \n\t" + "lwr %[coef4], 12(%[coefficient]) \n\t" + "lhu %[coef5], 16(%[coefficient]) \n\t" + "seh %[gain32], %[gain] \n\t" + "seh %[sign32], %[sign] \n\t" + ".set pop \n\t" + : [coef1] "=&r" (coef1), [coef2] "=&r" (coef2), [coef3] "=&r" (coef3), + [coef4] "=&r" (coef4), [coef5] "=&r" (coef5), [gain32] "=&r" (gain32), + [sign32] "=&r" (sign32) + : [coefficient] "r" (coefficient), [gain] "r" (gain), + [sign] "r" (sign) + : "memory" + ); + + for (i = 0; i < loopNumber; i++) { + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + // Filter to get fractional pitch + "li %[w1], 8192 \n\t" + "mtlo %[w1] \n\t" + "mthi $0 \n\t" + "lwl %[w1], 3(%[out2_pos2]) \n\t" + "lwl %[w2], 7(%[out2_pos2]) \n\t" + "lwl %[w3], 11(%[out2_pos2]) \n\t" + "lwl %[w4], 15(%[out2_pos2]) \n\t" + "lwr %[w1], 0(%[out2_pos2]) \n\t" + "lwr %[w2], 4(%[out2_pos2]) \n\t" + "lwr %[w3], 8(%[out2_pos2]) \n\t" + "lwr %[w4], 12(%[out2_pos2]) \n\t" + "lhu %[w5], 16(%[out2_pos2]) \n\t" + "dpa.w.ph $ac0, %[w1], %[coef1] \n\t" + "dpa.w.ph $ac0, %[w2], %[coef2] \n\t" + "dpa.w.ph $ac0, %[w3], %[coef3] \n\t" + "dpa.w.ph $ac0, %[w4], %[coef4] \n\t" + "dpa.w.ph $ac0, %[w5], %[coef5] \n\t" + "addiu %[out2_pos2], %[out2_pos2], 2 \n\t" + "mthi $0, $ac1 \n\t" + "lwl %[w2], 3(%[inputState]) \n\t" + "lwl %[w3], 7(%[inputState]) \n\t" + // Fractional pitch shift & saturation + "extr_s.h %[w1], $ac0, 14 \n\t" + "li %[w4], 16384 \n\t" + "lwr %[w2], 0(%[inputState]) \n\t" + "lwr %[w3], 4(%[inputState]) \n\t" + "mtlo %[w4], $ac1 \n\t" + // Shift low pass filter state + "swl %[w2], 5(%[inputState]) \n\t" + "swl %[w3], 9(%[inputState]) \n\t" + "mul %[w1], %[gain32], %[w1] \n\t" + "swr %[w2], 2(%[inputState]) \n\t" + "swr %[w3], 6(%[inputState]) \n\t" + // Low pass filter accumulation + "dpa.w.ph $ac1, %[kDampF1], %[w2] \n\t" + "dpa.w.ph $ac1, %[kDampF2], %[w3] \n\t" + "lh %[w4], 0(%[input1]) \n\t" + "addiu %[input1], %[input1], 2 \n\t" + "shra_r.w %[w1], %[w1], 12 \n\t" + "sh %[w1], 0(%[inputState]) \n\t" + "dpa.w.ph $ac1, %[kDampF0], %[w1] \n\t" + // Low pass filter shift & saturation + "extr_s.h %[w2], $ac1, 15 \n\t" + "mul %[w2], %[w2], %[sign32] \n\t" + // Buffer update + "subu %[w2], %[w4], %[w2] \n\t" + "shll_s.w %[w2], %[w2], 16 \n\t" + "sra %[w2], %[w2], 16 \n\t" + "sh %[w2], 0(%[output1]) \n\t" + "addu %[w2], %[w2], %[w4] \n\t" + "shll_s.w %[w2], %[w2], 16 \n\t" + "addiu %[output1], %[output1], 2 \n\t" + "sra %[w2], %[w2], 16 \n\t" + "sh %[w2], 0(%[output2]) \n\t" + "addiu %[output2], %[output2], 2 \n\t" + ".set pop \n\t" + : [w1] "=&r" (w1), [w2] "=&r" (w2), [w3] "=&r" (w3), [w4] "=&r" (w4), + [w5] "=&r" (w5), [input1] "+r" (input1), [out2_pos2] "+r" (out2_pos2), + [output1] "+r" (output1), [output2] "+r" (output2) + : [coefficient] "r" (coefficient), [inputState] "r" (inputState), + [gain32] "r" (gain32), [sign32] "r" (sign32), [kDampF0] "r" (kDampF0), + [kDampF1] "r" (kDampF1), [kDampF2] "r" (kDampF2), + [coef1] "r" (coef1), [coef2] "r" (coef2), [coef3] "r" (coef3), + [coef4] "r" (coef4), [coef5] "r" (coef5) + : "hi", "lo", "$ac1hi", "$ac1lo", "memory" + ); + } + (*index2) += loopNumber; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_gain_tables.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_gain_tables.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_gain_tables.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_gain_tables.h 2015-02-03 14:33:34.000000000 +0000 @@ -18,8 +18,7 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_PITCH_GAIN_TABLES_H_ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_PITCH_GAIN_TABLES_H_ -#include "typedefs.h" - +#include "webrtc/typedefs.h" /********************* Pitch Filter Gain Coefficient Tables ************************/ /* cdf for quantized pitch filter gains */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_lag_tables.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_lag_tables.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_lag_tables.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/pitch_lag_tables.h 2015-02-03 14:33:34.000000000 +0000 @@ -18,9 +18,7 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_PITCH_LAG_TABLES_H_ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_PITCH_LAG_TABLES_H_ - -#include "typedefs.h" - +#include "webrtc/typedefs.h" /********************* Pitch Filter Lag Coefficient Tables ************************/ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/settings.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/settings.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/settings.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/settings.h 2015-02-03 14:33:34.000000000 +0000 @@ -96,7 +96,16 @@ #define STREAM_MAXW16 300 /* The old maximum size still needed for the decoding */ #define STREAM_MAXW16_30MS 100 /* 100 Word16 = 200 bytes = 53.4 kbit/s @ 30 ms.framelength */ #define STREAM_MAXW16_60MS 200 /* 200 Word16 = 400 bytes = 53.4 kbit/s @ 60 ms.framelength */ - +/* This is used only at the decoder bit-stream struct. + * - The encoder and decoder bitstream containers are of different size because + * old iSAC limited the encoded bitstream to 600 bytes. But newer versions + * restrict to shorter bitstream. + * - We add 10 bytes of guards to the internal bitstream container. The reason + * is that entropy decoder might read few bytes (3 according to our + * observations) more than the actual size of the bitstream. To avoid reading + * outside memory, in rare occasion of full-size bitstream we add 10 bytes + * of guard. */ +#define INTERNAL_STREAM_SIZE_W16 (STREAM_MAXW16 + 5) /* storage size for bit counts */ //#define BIT_COUNTER_SIZE 30 @@ -190,6 +199,7 @@ /* 6600 Decoder */ #define ISAC_DECODER_NOT_INITIATED 6610 #define ISAC_EMPTY_PACKET 6620 +#define ISAC_PACKET_TOO_SHORT 6625 #define ISAC_DISALLOWED_FRAME_MODE_DECODER 6630 #define ISAC_RANGE_ERROR_DECODE_FRAME_LENGTH 6640 #define ISAC_RANGE_ERROR_DECODE_BANDWIDTH 6650 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/spectrum_ar_model_tables.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/spectrum_ar_model_tables.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/spectrum_ar_model_tables.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/spectrum_ar_model_tables.h 2015-02-03 14:33:34.000000000 +0000 @@ -19,9 +19,8 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_SPECTRUM_AR_MODEL_TABLES_H_ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_SPECTRUM_AR_MODEL_TABLES_H_ -#include "typedefs.h" #include "settings.h" - +#include "webrtc/typedefs.h" /********************* AR Coefficient Tables ************************/ /* cdf for quantized reflection coefficient 1 */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/structs.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/structs.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/structs.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/structs.h 2015-02-03 14:33:34.000000000 +0000 @@ -19,20 +19,21 @@ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_STRUCTS_H_ -#include "common_audio/signal_processing/include/signal_processing_library.h" -#include "modules/audio_coding/codecs/isac/fix/source/settings.h" -#include "typedefs.h" +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/settings.h" +#include "webrtc/typedefs.h" /* Bitstream struct for decoder */ typedef struct Bitstreamstruct_dec { - uint16_t *stream; /* Pointer to bytestream to decode */ + uint16_t stream[INTERNAL_STREAM_SIZE_W16]; /* Array bytestream to decode */ uint32_t W_upper; /* Upper boundary of interval W */ uint32_t streamval; uint16_t stream_index; /* Index to the current position in bytestream */ int16_t full; /* 0 - first byte in memory filled, second empty*/ /* 1 - both bytes are empty (we just filled the previous memory */ + int stream_size; /* The size of stream. */ } Bitstr_dec; /* Bitstream struct for encoder */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform.c 2015-02-03 14:33:34.000000000 +0000 @@ -19,89 +19,13 @@ #include "webrtc/modules/audio_coding/codecs/isac/fix/source/fft.h" #include "webrtc/modules/audio_coding/codecs/isac/fix/source/settings.h" -#if (defined WEBRTC_DETECT_ARM_NEON || defined WEBRTC_ARCH_ARM_NEON) -/* Tables are defined in ARM assembly files. */ +/* Tables are defined in transform_tables.c file or ARM assembly files. */ /* Cosine table 1 in Q14 */ extern const int16_t WebRtcIsacfix_kCosTab1[FRAMESAMPLES/2]; /* Sine table 1 in Q14 */ extern const int16_t WebRtcIsacfix_kSinTab1[FRAMESAMPLES/2]; /* Sine table 2 in Q14 */ extern const int16_t WebRtcIsacfix_kSinTab2[FRAMESAMPLES/4]; -#else -/* Cosine table 1 in Q14 */ -static const int16_t WebRtcIsacfix_kCosTab1[FRAMESAMPLES/2] = { - 16384, 16383, 16378, 16371, 16362, 16349, 16333, 16315, 16294, 16270, - 16244, 16214, 16182, 16147, 16110, 16069, 16026, 15980, 15931, 15880, - 15826, 15769, 15709, 15647, 15582, 15515, 15444, 15371, 15296, 15218, - 15137, 15053, 14968, 14879, 14788, 14694, 14598, 14500, 14399, 14295, - 14189, 14081, 13970, 13856, 13741, 13623, 13502, 13380, 13255, 13128, - 12998, 12867, 12733, 12597, 12458, 12318, 12176, 12031, 11885, 11736, - 11585, 11433, 11278, 11121, 10963, 10803, 10641, 10477, 10311, 10143, - 9974, 9803, 9630, 9456, 9280, 9102, 8923, 8743, 8561, 8377, - 8192, 8006, 7818, 7629, 7438, 7246, 7053, 6859, 6664, 6467, - 6270, 6071, 5872, 5671, 5469, 5266, 5063, 4859, 4653, 4447, - 4240, 4033, 3825, 3616, 3406, 3196, 2986, 2775, 2563, 2351, - 2139, 1926, 1713, 1499, 1285, 1072, 857, 643, 429, 214, - 0, -214, -429, -643, -857, -1072, -1285, -1499, -1713, -1926, - -2139, -2351, -2563, -2775, -2986, -3196, -3406, -3616, -3825, -4033, - -4240, -4447, -4653, -4859, -5063, -5266, -5469, -5671, -5872, -6071, - -6270, -6467, -6664, -6859, -7053, -7246, -7438, -7629, -7818, -8006, - -8192, -8377, -8561, -8743, -8923, -9102, -9280, -9456, -9630, -9803, - -9974, -10143, -10311, -10477, -10641, -10803, -10963, -11121, -11278, -11433, - -11585, -11736, -11885, -12031, -12176, -12318, -12458, -12597, -12733, - -12867, -12998, -13128, -13255, -13380, -13502, -13623, -13741, -13856, - -13970, -14081, -14189, -14295, -14399, -14500, -14598, -14694, -14788, - -14879, -14968, -15053, -15137, -15218, -15296, -15371, -15444, -15515, - -15582, -15647, -15709, -15769, -15826, -15880, -15931, -15980, -16026, - -16069, -16110, -16147, -16182, -16214, -16244, -16270, -16294, -16315, - -16333, -16349, -16362, -16371, -16378, -16383 -}; - -/* Sine table 1 in Q14 */ -static const int16_t WebRtcIsacfix_kSinTab1[FRAMESAMPLES/2] = { - 0, 214, 429, 643, 857, 1072, 1285, 1499, 1713, 1926, - 2139, 2351, 2563, 2775, 2986, 3196, 3406, 3616, 3825, 4033, - 4240, 4447, 4653, 4859, 5063, 5266, 5469, 5671, 5872, 6071, - 6270, 6467, 6664, 6859, 7053, 7246, 7438, 7629, 7818, 8006, - 8192, 8377, 8561, 8743, 8923, 9102, 9280, 9456, 9630, 9803, - 9974, 10143, 10311, 10477, 10641, 10803, 10963, 11121, 11278, 11433, - 11585, 11736, 11885, 12031, 12176, 12318, 12458, 12597, 12733, 12867, - 12998, 13128, 13255, 13380, 13502, 13623, 13741, 13856, 13970, 14081, - 14189, 14295, 14399, 14500, 14598, 14694, 14788, 14879, 14968, 15053, - 15137, 15218, 15296, 15371, 15444, 15515, 15582, 15647, 15709, 15769, - 15826, 15880, 15931, 15980, 16026, 16069, 16110, 16147, 16182, 16214, - 16244, 16270, 16294, 16315, 16333, 16349, 16362, 16371, 16378, 16383, - 16384, 16383, 16378, 16371, 16362, 16349, 16333, 16315, 16294, 16270, - 16244, 16214, 16182, 16147, 16110, 16069, 16026, 15980, 15931, 15880, - 15826, 15769, 15709, 15647, 15582, 15515, 15444, 15371, 15296, 15218, - 15137, 15053, 14968, 14879, 14788, 14694, 14598, 14500, 14399, 14295, - 14189, 14081, 13970, 13856, 13741, 13623, 13502, 13380, 13255, 13128, - 12998, 12867, 12733, 12597, 12458, 12318, 12176, 12031, 11885, 11736, - 11585, 11433, 11278, 11121, 10963, 10803, 10641, 10477, 10311, 10143, - 9974, 9803, 9630, 9456, 9280, 9102, 8923, 8743, 8561, 8377, - 8192, 8006, 7818, 7629, 7438, 7246, 7053, 6859, 6664, 6467, - 6270, 6071, 5872, 5671, 5469, 5266, 5063, 4859, 4653, 4447, - 4240, 4033, 3825, 3616, 3406, 3196, 2986, 2775, 2563, 2351, - 2139, 1926, 1713, 1499, 1285, 1072, 857, 643, 429, 214 -}; - - -/* Sine table 2 in Q14 */ -static const int16_t WebRtcIsacfix_kSinTab2[FRAMESAMPLES/4] = { - 16384, -16381, 16375, -16367, 16356, -16342, 16325, -16305, 16283, -16257, - 16229, -16199, 16165, -16129, 16090, -16048, 16003, -15956, 15906, -15853, - 15798, -15739, 15679, -15615, 15549, -15480, 15408, -15334, 15257, -15178, - 15095, -15011, 14924, -14834, 14741, -14647, 14549, -14449, 14347, -14242, - 14135, -14025, 13913, -13799, 13682, -13563, 13441, -13318, 13192, -13063, - 12933, -12800, 12665, -12528, 12389, -12247, 12104, -11958, 11810, -11661, - 11509, -11356, 11200, -11042, 10883, -10722, 10559, -10394, 10227, -10059, - 9889, -9717, 9543, -9368, 9191, -9013, 8833, -8652, 8469, -8285, - 8099, -7912, 7723, -7534, 7342, -7150, 6957, -6762, 6566, -6369, - 6171, -5971, 5771, -5570, 5368, -5165, 4961, -4756, 4550, -4344, - 4137, -3929, 3720, -3511, 3301, -3091, 2880, -2669, 2457, -2245, - 2032, -1819, 1606, -1392, 1179, -965, 750, -536, 322, -107 -}; -#endif // WEBRTC_DETECT_ARM_NEON || WEBRTC_ARCH_ARM_NEON void WebRtcIsacfix_Time2SpecC(int16_t *inre1Q9, int16_t *inre2Q9, @@ -121,10 +45,11 @@ for (k = 0; k < FRAMESAMPLES/2; k++) { tmp1rQ14 = WebRtcIsacfix_kCosTab1[k]; tmp1iQ14 = WebRtcIsacfix_kSinTab1[k]; - xrQ16 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(tmp1rQ14, inre1Q9[k]) + WEBRTC_SPL_MUL_16_16(tmp1iQ14, inre2Q9[k]), 7); - xiQ16 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(tmp1rQ14, inre2Q9[k]) - WEBRTC_SPL_MUL_16_16(tmp1iQ14, inre1Q9[k]), 7); - tmpreQ16[k] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_32_RSFT16(factQ19, xrQ16)+4, 3); // (Q16*Q19>>16)>>3 = Q16 - tmpimQ16[k] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_32_RSFT16(factQ19, xiQ16)+4, 3); // (Q16*Q19>>16)>>3 = Q16 + xrQ16 = (tmp1rQ14 * inre1Q9[k] + tmp1iQ14 * inre2Q9[k]) >> 7; + xiQ16 = (tmp1rQ14 * inre2Q9[k] - tmp1iQ14 * inre1Q9[k]) >> 7; + // Q-domains below: (Q16*Q19>>16)>>3 = Q16 + tmpreQ16[k] = (WEBRTC_SPL_MUL_16_32_RSFT16(factQ19, xrQ16) + 4) >> 3; + tmpimQ16[k] = (WEBRTC_SPL_MUL_16_32_RSFT16(factQ19, xiQ16) + 4) >> 3; } @@ -147,8 +72,8 @@ } else { int32_t round = WEBRTC_SPL_LSHIFT_W32((int32_t)1, -sh-1); for (k=0; k> -sh); // Q(16+sh) + inre2Q9[k] = (int16_t)((tmpimQ16[k] + round) >> -sh); // Q(16+sh) } } @@ -158,8 +83,8 @@ //"Fastest" vectors if (sh>=0) { for (k=0; k Q16 - tmpimQ16[k] = WEBRTC_SPL_RSHIFT_W32((int32_t)inre2Q9[k], sh); //Q(16+sh) -> Q16 + tmpreQ16[k] = inre1Q9[k] >> sh; // Q(16+sh) -> Q16 + tmpimQ16[k] = inre2Q9[k] >> sh; // Q(16+sh) -> Q16 } } else { for (k=0; k> 9); + outimQ7[k] = (int16_t)(v2Q16 >> 9); v1Q16 = -WEBRTC_SPL_MUL_16_32_RSFT14(tmp1iQ14, yrQ16) - WEBRTC_SPL_MUL_16_32_RSFT14(tmp1rQ14, yiQ16); v2Q16 = -WEBRTC_SPL_MUL_16_32_RSFT14(tmp1rQ14, yrQ16) + WEBRTC_SPL_MUL_16_32_RSFT14(tmp1iQ14, yiQ16); - outreQ7[FRAMESAMPLES/2 - 1 - k] = (int16_t)WEBRTC_SPL_RSHIFT_W32(v1Q16, 9); //CalcLrIntQ(v1Q16, 9); - outimQ7[FRAMESAMPLES/2 - 1 - k] = (int16_t)WEBRTC_SPL_RSHIFT_W32(v2Q16, 9); //CalcLrIntQ(v2Q16, 9); + // CalcLrIntQ(v1Q16, 9); + outreQ7[FRAMESAMPLES / 2 - 1 - k] = (int16_t)(v1Q16 >> 9); + // CalcLrIntQ(v2Q16, 9); + outimQ7[FRAMESAMPLES / 2 - 1 - k] = (int16_t)(v2Q16 >> 9); } } @@ -242,8 +169,8 @@ } else { int32_t round = WEBRTC_SPL_LSHIFT_W32((int32_t)1, -sh-1); for (k=0; k<240; k++) { - inreQ7[k] = (int16_t) WEBRTC_SPL_RSHIFT_W32(outre1Q16[k]+round, -sh); //Q(16+sh) - inimQ7[k] = (int16_t) WEBRTC_SPL_RSHIFT_W32(outre2Q16[k]+round, -sh); //Q(16+sh) + inreQ7[k] = (int16_t)((outre1Q16[k] + round) >> -sh); // Q(16+sh) + inimQ7[k] = (int16_t)((outre2Q16[k] + round) >> -sh); // Q(16+sh) } } @@ -252,8 +179,8 @@ //"Fastest" vectors if (sh>=0) { for (k=0; k<240; k++) { - outre1Q16[k] = WEBRTC_SPL_RSHIFT_W32((int32_t)inreQ7[k], sh); //Q(16+sh) -> Q16 - outre2Q16[k] = WEBRTC_SPL_RSHIFT_W32((int32_t)inimQ7[k], sh); //Q(16+sh) -> Q16 + outre1Q16[k] = inreQ7[k] >> sh; // Q(16+sh) -> Q16 + outre2Q16[k] = inimQ7[k] >> sh; // Q(16+sh) -> Q16 } } else { for (k=0; k<240; k++) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform_mips.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform_mips.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform_mips.c 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform_mips.c 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,1287 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/codec.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/fft.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/settings.h" + +// The tables are defined in transform_tables.c file. +extern const int16_t WebRtcIsacfix_kCosTab1[FRAMESAMPLES/2]; +extern const int16_t WebRtcIsacfix_kSinTab1[FRAMESAMPLES/2]; +extern const int16_t WebRtcIsacfix_kCosTab2[FRAMESAMPLES/4]; +extern const int16_t WebRtcIsacfix_kSinTab2[FRAMESAMPLES/4]; + +// MIPS DSPr2 version of the WebRtcIsacfix_Time2Spec function +// is not bit-exact with the C version. +// The accuracy of the MIPS DSPr2 version is same or better. +void WebRtcIsacfix_Time2SpecMIPS(int16_t* inre1Q9, + int16_t* inre2Q9, + int16_t* outreQ7, + int16_t* outimQ7) { + int k = FRAMESAMPLES / 2; + int32_t tmpreQ16[FRAMESAMPLES / 2], tmpimQ16[FRAMESAMPLES / 2]; + int32_t r0, r1, r2, r3, r4, r5, r6, r7, r8, r9; + int32_t inre1, inre2, tmpre, tmpim, factor, max, max1; + int16_t* cosptr; + int16_t* sinptr; + + cosptr = (int16_t*)WebRtcIsacfix_kCosTab1; + sinptr = (int16_t*)WebRtcIsacfix_kSinTab1; + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[inre1], %[inre1Q9], 0 \n\t" + "addiu %[inre2], %[inre2Q9], 0 \n\t" + "addiu %[tmpre], %[tmpreQ16], 0 \n\t" + "addiu %[tmpim], %[tmpimQ16], 0 \n\t" + "addiu %[factor], $zero, 16921 \n\t" + "mul %[max], $zero, $zero \n\t" + // Multiply with complex exponentials and combine into one complex vector. + // Also, calculate the maximal absolute value in the same loop. + "1: \n\t" +#if defined(MIPS_DSP_R2_LE) + "lwl %[r0], 0(%[inre1]) \n\t" + "lwl %[r2], 0(%[cosptr]) \n\t" + "lwl %[r3], 0(%[sinptr]) \n\t" + "lwl %[r1], 0(%[inre2]) \n\t" + "lwr %[r0], 0(%[inre1]) \n\t" + "lwr %[r2], 0(%[cosptr]) \n\t" + "lwr %[r3], 0(%[sinptr]) \n\t" + "lwr %[r1], 0(%[inre2]) \n\t" + "muleq_s.w.phr %[r4], %[r2], %[r0] \n\t" + "muleq_s.w.phr %[r5], %[r3], %[r0] \n\t" + "muleq_s.w.phr %[r6], %[r3], %[r1] \n\t" + "muleq_s.w.phr %[r7], %[r2], %[r1] \n\t" + "muleq_s.w.phl %[r8], %[r2], %[r0] \n\t" + "muleq_s.w.phl %[r0], %[r3], %[r0] \n\t" + "muleq_s.w.phl %[r3], %[r3], %[r1] \n\t" + "muleq_s.w.phl %[r1], %[r2], %[r1] \n\t" + "addiu %[k], %[k], -2 \n\t" + "addu %[r4], %[r4], %[r6] \n\t" + "subu %[r5], %[r7], %[r5] \n\t" + "sra %[r4], %[r4], 8 \n\t" + "sra %[r5], %[r5], 8 \n\t" + "mult $ac0, %[factor], %[r4] \n\t" + "mult $ac1, %[factor], %[r5] \n\t" + "addu %[r3], %[r8], %[r3] \n\t" + "subu %[r0], %[r1], %[r0] \n\t" + "sra %[r3], %[r3], 8 \n\t" + "sra %[r0], %[r0], 8 \n\t" + "mult $ac2, %[factor], %[r3] \n\t" + "mult $ac3, %[factor], %[r0] \n\t" + "extr_r.w %[r4], $ac0, 16 \n\t" + "extr_r.w %[r5], $ac1, 16 \n\t" + "addiu %[inre1], %[inre1], 4 \n\t" + "addiu %[inre2], %[inre2], 4 \n\t" + "extr_r.w %[r6], $ac2, 16 \n\t" + "extr_r.w %[r7], $ac3, 16 \n\t" + "addiu %[cosptr], %[cosptr], 4 \n\t" + "addiu %[sinptr], %[sinptr], 4 \n\t" + "shra_r.w %[r4], %[r4], 3 \n\t" + "shra_r.w %[r5], %[r5], 3 \n\t" + "sw %[r4], 0(%[tmpre]) \n\t" + "absq_s.w %[r4], %[r4] \n\t" + "sw %[r5], 0(%[tmpim]) \n\t" + "absq_s.w %[r5], %[r5] \n\t" + "shra_r.w %[r6], %[r6], 3 \n\t" + "shra_r.w %[r7], %[r7], 3 \n\t" + "sw %[r6], 4(%[tmpre]) \n\t" + "absq_s.w %[r6], %[r6] \n\t" + "sw %[r7], 4(%[tmpim]) \n\t" + "absq_s.w %[r7], %[r7] \n\t" + "slt %[r0], %[r4], %[r5] \n\t" + "movn %[r4], %[r5], %[r0] \n\t" + "slt %[r1], %[r6], %[r7] \n\t" + "movn %[r6], %[r7], %[r1] \n\t" + "slt %[r0], %[max], %[r4] \n\t" + "movn %[max], %[r4], %[r0] \n\t" + "slt %[r1], %[max], %[r6] \n\t" + "movn %[max], %[r6], %[r1] \n\t" + "addiu %[tmpre], %[tmpre], 8 \n\t" + "bgtz %[k], 1b \n\t" + " addiu %[tmpim], %[tmpim], 8 \n\t" +#else // #if defined(MIPS_DSP_R2_LE) + "lh %[r0], 0(%[inre1]) \n\t" + "lh %[r1], 0(%[inre2]) \n\t" + "lh %[r2], 0(%[cosptr]) \n\t" + "lh %[r3], 0(%[sinptr]) \n\t" + "addiu %[k], %[k], -1 \n\t" + "mul %[r4], %[r0], %[r2] \n\t" + "mul %[r5], %[r1], %[r3] \n\t" + "mul %[r0], %[r0], %[r3] \n\t" + "mul %[r2], %[r1], %[r2] \n\t" + "addiu %[inre1], %[inre1], 2 \n\t" + "addiu %[inre2], %[inre2], 2 \n\t" + "addiu %[cosptr], %[cosptr], 2 \n\t" + "addiu %[sinptr], %[sinptr], 2 \n\t" + "addu %[r1], %[r4], %[r5] \n\t" + "sra %[r1], %[r1], 7 \n\t" + "sra %[r3], %[r1], 16 \n\t" + "andi %[r1], %[r1], 0xFFFF \n\t" + "sra %[r1], %[r1], 1 \n\t" + "mul %[r1], %[factor], %[r1] \n\t" + "mul %[r3], %[factor], %[r3] \n\t" + "subu %[r0], %[r2], %[r0] \n\t" + "sra %[r0], %[r0], 7 \n\t" + "sra %[r2], %[r0], 16 \n\t" + "andi %[r0], %[r0], 0xFFFF \n\t" + "sra %[r0], %[r0], 1 \n\t" + "mul %[r0], %[factor], %[r0] \n\t" + "mul %[r2], %[factor], %[r2] \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r1], %[r1], 15 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r1], %[r1], 0x4000 \n\t" + "sra %[r1], %[r1], 15 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "addu %[r1], %[r3], %[r1] \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r1], %[r1], 3 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r1], %[r1], 4 \n\t" + "sra %[r1], %[r1], 3 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sw %[r1], 0(%[tmpre]) \n\t" + "addiu %[tmpre], %[tmpre], 4 \n\t" +#if defined(MIPS_DSP_R1_LE) + "absq_s.w %[r1], %[r1] \n\t" + "shra_r.w %[r0], %[r0], 15 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "negu %[r4], %[r1] \n\t" + "slt %[r3], %[r1], $zero \n\t" + "movn %[r1], %[r4], %[r3] \n\t" + "addiu %[r0], %[r0], 0x4000 \n\t" + "sra %[r0], %[r0], 15 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "addu %[r0], %[r0], %[r2] \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r0], %[r0], 3 \n\t" + "sw %[r0], 0(%[tmpim]) \n\t" + "absq_s.w %[r0], %[r0] \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r0], %[r0], 4 \n\t" + "sra %[r0], %[r0], 3 \n\t" + "sw %[r0], 0(%[tmpim]) \n\t" + "negu %[r2], %[r0] \n\t" + "slt %[r3], %[r0], $zero \n\t" + "movn %[r0], %[r2], %[r3] \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "slt %[r2], %[max], %[r1] \n\t" + "movn %[max], %[r1], %[r2] \n\t" + "slt %[r2], %[max], %[r0] \n\t" + "movn %[max], %[r0], %[r2] \n\t" + "bgtz %[k], 1b \n\t" + " addiu %[tmpim], %[tmpim], 4 \n\t" +#endif // #if defined(MIPS_DSP_R2_LE) + // Calculate WebRtcSpl_NormW32(max). + // If max gets value >=0, we should shift max steps to the left, and the + // domain will be Q(16+shift). If max gets value <0, we should shift -max + // steps to the right, and the domain will be Q(16+max) + "clz %[max], %[max] \n\t" + "addiu %[max], %[max], -25 \n\t" + ".set pop \n\t" + : [k] "+r" (k), [inre1] "=&r" (inre1), [inre2] "=&r" (inre2), + [r0] "=&r" (r0), [r1] "=&r" (r1), [r2] "=&r" (r2), + [r3] "=&r" (r3), [r4] "=&r" (r4), [tmpre] "=&r" (tmpre), + [tmpim] "=&r" (tmpim), [max] "=&r" (max), [factor] "=&r" (factor), +#if defined(MIPS_DSP_R2_LE) + [r6] "=&r" (r6), [r7] "=&r" (r7), [r8] "=&r" (r8), +#endif // #if defined(MIPS_DSP_R2_LE) + [r5] "=&r" (r5) + : [inre1Q9] "r" (inre1Q9), [inre2Q9] "r" (inre2Q9), + [tmpreQ16] "r" (tmpreQ16), [tmpimQ16] "r" (tmpimQ16), + [cosptr] "r" (cosptr), [sinptr] "r" (sinptr) + : "hi", "lo", "memory" + ); + + // "Fastest" vectors + k = FRAMESAMPLES / 4; + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[tmpre], %[tmpreQ16], 0 \n\t" + "addiu %[tmpim], %[tmpimQ16], 0 \n\t" + "addiu %[inre1], %[inre1Q9], 0 \n\t" + "addiu %[inre2], %[inre2Q9], 0 \n\t" + "blez %[max], 2f \n\t" + " subu %[max1], $zero, %[max] \n\t" + "1: \n\t" + "lw %[r0], 0(%[tmpre]) \n\t" + "lw %[r1], 0(%[tmpim]) \n\t" + "lw %[r2], 4(%[tmpre]) \n\t" + "lw %[r3], 4(%[tmpim]) \n\t" + "addiu %[k], %[k], -1 \n\t" + "sllv %[r0], %[r0], %[max] \n\t" + "sllv %[r1], %[r1], %[max] \n\t" + "sllv %[r2], %[r2], %[max] \n\t" + "sllv %[r3], %[r3], %[max] \n\t" + "addiu %[tmpre], %[tmpre], 8 \n\t" + "addiu %[tmpim], %[tmpim], 8 \n\t" + "sh %[r0], 0(%[inre1]) \n\t" + "sh %[r1], 0(%[inre2]) \n\t" + "sh %[r2], 2(%[inre1]) \n\t" + "sh %[r3], 2(%[inre2]) \n\t" + "addiu %[inre1], %[inre1], 4 \n\t" + "bgtz %[k], 1b \n\t" + " addiu %[inre2], %[inre2], 4 \n\t" + "b 4f \n\t" + " nop \n\t" + "2: \n\t" +#if !defined(MIPS_DSP_R1_LE) + "addiu %[r4], %[max1], -1 \n\t" + "addiu %[r5], $zero, 1 \n\t" + "sllv %[r4], %[r5], %[r4] \n\t" +#endif // #if !defined(MIPS_DSP_R1_LE) + "3: \n\t" + "lw %[r0], 0(%[tmpre]) \n\t" + "lw %[r1], 0(%[tmpim]) \n\t" + "lw %[r2], 4(%[tmpre]) \n\t" + "lw %[r3], 4(%[tmpim]) \n\t" + "addiu %[k], %[k], -1 \n\t" +#if defined(MIPS_DSP_R1_LE) + "shrav_r.w %[r0], %[r0], %[max1] \n\t" + "shrav_r.w %[r1], %[r1], %[max1] \n\t" + "shrav_r.w %[r2], %[r2], %[max1] \n\t" + "shrav_r.w %[r3], %[r3], %[max1] \n\t" +#else // #if !defined(MIPS_DSP_R1_LE) + "addu %[r0], %[r0], %[r4] \n\t" + "addu %[r1], %[r1], %[r4] \n\t" + "addu %[r2], %[r2], %[r4] \n\t" + "addu %[r3], %[r3], %[r4] \n\t" + "srav %[r0], %[r0], %[max1] \n\t" + "srav %[r1], %[r1], %[max1] \n\t" + "srav %[r2], %[r2], %[max1] \n\t" + "srav %[r3], %[r3], %[max1] \n\t" +#endif // #if !defined(MIPS_DSP_R1_LE) + "addiu %[tmpre], %[tmpre], 8 \n\t" + "addiu %[tmpim], %[tmpim], 8 \n\t" + "sh %[r0], 0(%[inre1]) \n\t" + "sh %[r1], 0(%[inre2]) \n\t" + "sh %[r2], 2(%[inre1]) \n\t" + "sh %[r3], 2(%[inre2]) \n\t" + "addiu %[inre1], %[inre1], 4 \n\t" + "bgtz %[k], 3b \n\t" + " addiu %[inre2], %[inre2], 4 \n\t" + "4: \n\t" + ".set pop \n\t" + : [tmpre] "=&r" (tmpre), [tmpim] "=&r" (tmpim), [inre1] "=&r" (inre1), + [inre2] "=&r" (inre2), [k] "+r" (k), [max1] "=&r" (max1), +#if !defined(MIPS_DSP_R1_LE) + [r4] "=&r" (r4), [r5] "=&r" (r5), +#endif // #if !defined(MIPS_DSP_R1_LE) + [r0] "=&r" (r0), [r1] "=&r" (r1), [r2] "=&r" (r2), [r3] "=&r" (r3) + : [tmpreQ16] "r" (tmpreQ16), [tmpimQ16] "r" (tmpimQ16), + [inre1Q9] "r" (inre1Q9), [inre2Q9] "r" (inre2Q9), [max] "r" (max) + : "memory" + ); + + // Get DFT + WebRtcIsacfix_FftRadix16Fastest(inre1Q9, inre2Q9, -1); // real call + + // "Fastest" vectors and + // Use symmetry to separate into two complex vectors + // and center frames in time around zero + // merged into one loop + cosptr = (int16_t*)WebRtcIsacfix_kCosTab2; + sinptr = (int16_t*)WebRtcIsacfix_kSinTab2; + k = FRAMESAMPLES / 4; + factor = FRAMESAMPLES - 2; // offset for FRAMESAMPLES / 2 - 1 array member + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[inre1], %[inre1Q9], 0 \n\t" + "addiu %[inre2], %[inre2Q9], 0 \n\t" + "addiu %[tmpre], %[outreQ7], 0 \n\t" + "addiu %[tmpim], %[outimQ7], 0 \n\t" + "bltz %[max], 2f \n\t" + " subu %[max1], $zero, %[max] \n\t" + "1: \n\t" +#if !defined(MIPS_DSP_R1_LE) + "addu %[r4], %[inre1], %[offset] \n\t" + "addu %[r5], %[inre2], %[offset] \n\t" +#endif // #if !defined(MIPS_DSP_R1_LE) + "lh %[r0], 0(%[inre1]) \n\t" + "lh %[r1], 0(%[inre2]) \n\t" +#if defined(MIPS_DSP_R1_LE) + "lhx %[r2], %[offset](%[inre1]) \n\t" + "lhx %[r3], %[offset](%[inre2]) \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "lh %[r2], 0(%[r4]) \n\t" + "lh %[r3], 0(%[r5]) \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "srav %[r0], %[r0], %[max] \n\t" + "srav %[r1], %[r1], %[max] \n\t" + "srav %[r2], %[r2], %[max] \n\t" + "srav %[r3], %[r3], %[max] \n\t" + "addu %[r4], %[r0], %[r2] \n\t" + "subu %[r0], %[r2], %[r0] \n\t" + "subu %[r2], %[r1], %[r3] \n\t" + "addu %[r1], %[r1], %[r3] \n\t" + "lh %[r3], 0(%[cosptr]) \n\t" + "lh %[r5], 0(%[sinptr]) \n\t" + "andi %[r6], %[r4], 0xFFFF \n\t" + "sra %[r4], %[r4], 16 \n\t" + "mul %[r7], %[r3], %[r6] \n\t" + "mul %[r8], %[r3], %[r4] \n\t" + "mul %[r6], %[r5], %[r6] \n\t" + "mul %[r4], %[r5], %[r4] \n\t" + "addiu %[k], %[k], -1 \n\t" + "addiu %[inre1], %[inre1], 2 \n\t" + "addiu %[inre2], %[inre2], 2 \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r7], %[r7], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r7], %[r7], 0x2000 \n\t" + "sra %[r7], %[r7], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sll %[r8], %[r8], 2 \n\t" + "addu %[r8], %[r8], %[r7] \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r6], %[r6], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r6], %[r6], 0x2000 \n\t" + "sra %[r6], %[r6], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sll %[r4], %[r4], 2 \n\t" + "addu %[r4], %[r4], %[r6] \n\t" + "andi %[r6], %[r2], 0xFFFF \n\t" + "sra %[r2], %[r2], 16 \n\t" + "mul %[r7], %[r5], %[r6] \n\t" + "mul %[r9], %[r5], %[r2] \n\t" + "mul %[r6], %[r3], %[r6] \n\t" + "mul %[r2], %[r3], %[r2] \n\t" + "addiu %[cosptr], %[cosptr], 2 \n\t" + "addiu %[sinptr], %[sinptr], 2 \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r7], %[r7], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r7], %[r7], 0x2000 \n\t" + "sra %[r7], %[r7], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sll %[r9], %[r9], 2 \n\t" + "addu %[r9], %[r7], %[r9] \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r6], %[r6], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r6], %[r6], 0x2000 \n\t" + "sra %[r6], %[r6], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sll %[r2], %[r2], 2 \n\t" + "addu %[r2], %[r6], %[r2] \n\t" + "subu %[r8], %[r8], %[r9] \n\t" + "sra %[r8], %[r8], 9 \n\t" + "addu %[r2], %[r4], %[r2] \n\t" + "sra %[r2], %[r2], 9 \n\t" + "sh %[r8], 0(%[tmpre]) \n\t" + "sh %[r2], 0(%[tmpim]) \n\t" + + "andi %[r4], %[r1], 0xFFFF \n\t" + "sra %[r1], %[r1], 16 \n\t" + "andi %[r6], %[r0], 0xFFFF \n\t" + "sra %[r0], %[r0], 16 \n\t" + "mul %[r7], %[r5], %[r4] \n\t" + "mul %[r9], %[r5], %[r1] \n\t" + "mul %[r4], %[r3], %[r4] \n\t" + "mul %[r1], %[r3], %[r1] \n\t" + "mul %[r8], %[r3], %[r0] \n\t" + "mul %[r3], %[r3], %[r6] \n\t" + "mul %[r6], %[r5], %[r6] \n\t" + "mul %[r0], %[r5], %[r0] \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r7], %[r7], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r7], %[r7], 0x2000 \n\t" + "sra %[r7], %[r7], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sll %[r9], %[r9], 2 \n\t" + "addu %[r9], %[r9], %[r7] \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r4], %[r4], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r4], %[r4], 0x2000 \n\t" + "sra %[r4], %[r4], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sll %[r1], %[r1], 2 \n\t" + "addu %[r1], %[r1], %[r4] \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r3], %[r3], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r3], %[r3], 0x2000 \n\t" + "sra %[r3], %[r3], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sll %[r8], %[r8], 2 \n\t" + "addu %[r8], %[r8], %[r3] \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r6], %[r6], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r6], %[r6], 0x2000 \n\t" + "sra %[r6], %[r6], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sll %[r0], %[r0], 2 \n\t" + "addu %[r0], %[r0], %[r6] \n\t" + "addu %[r3], %[tmpre], %[offset] \n\t" + "addu %[r2], %[tmpim], %[offset] \n\t" + "addu %[r9], %[r9], %[r8] \n\t" + "negu %[r9], %[r9] \n\t" + "sra %[r9], %[r9], 9 \n\t" + "subu %[r0], %[r0], %[r1] \n\t" + "addiu %[offset], %[offset], -4 \n\t" + "sh %[r9], 0(%[r3]) \n\t" + "sh %[r0], 0(%[r2]) \n\t" + "addiu %[tmpre], %[tmpre], 2 \n\t" + "bgtz %[k], 1b \n\t" + " addiu %[tmpim], %[tmpim], 2 \n\t" + "b 3f \n\t" + " nop \n\t" + "2: \n\t" +#if !defined(MIPS_DSP_R1_LE) + "addu %[r4], %[inre1], %[offset] \n\t" + "addu %[r5], %[inre2], %[offset] \n\t" +#endif // #if !defined(MIPS_DSP_R1_LE) + "lh %[r0], 0(%[inre1]) \n\t" + "lh %[r1], 0(%[inre2]) \n\t" +#if defined(MIPS_DSP_R1_LE) + "lhx %[r2], %[offset](%[inre1]) \n\t" + "lhx %[r3], %[offset](%[inre2]) \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "lh %[r2], 0(%[r4]) \n\t" + "lh %[r3], 0(%[r5]) \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sllv %[r0], %[r0], %[max1] \n\t" + "sllv %[r1], %[r1], %[max1] \n\t" + "sllv %[r2], %[r2], %[max1] \n\t" + "sllv %[r3], %[r3], %[max1] \n\t" + "addu %[r4], %[r0], %[r2] \n\t" + "subu %[r0], %[r2], %[r0] \n\t" + "subu %[r2], %[r1], %[r3] \n\t" + "addu %[r1], %[r1], %[r3] \n\t" + "lh %[r3], 0(%[cosptr]) \n\t" + "lh %[r5], 0(%[sinptr]) \n\t" + "andi %[r6], %[r4], 0xFFFF \n\t" + "sra %[r4], %[r4], 16 \n\t" + "mul %[r7], %[r3], %[r6] \n\t" + "mul %[r8], %[r3], %[r4] \n\t" + "mul %[r6], %[r5], %[r6] \n\t" + "mul %[r4], %[r5], %[r4] \n\t" + "addiu %[k], %[k], -1 \n\t" + "addiu %[inre1], %[inre1], 2 \n\t" + "addiu %[inre2], %[inre2], 2 \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r7], %[r7], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r7], %[r7], 0x2000 \n\t" + "sra %[r7], %[r7], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sll %[r8], %[r8], 2 \n\t" + "addu %[r8], %[r8], %[r7] \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r6], %[r6], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r6], %[r6], 0x2000 \n\t" + "sra %[r6], %[r6], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sll %[r4], %[r4], 2 \n\t" + "addu %[r4], %[r4], %[r6] \n\t" + "andi %[r6], %[r2], 0xFFFF \n\t" + "sra %[r2], %[r2], 16 \n\t" + "mul %[r7], %[r5], %[r6] \n\t" + "mul %[r9], %[r5], %[r2] \n\t" + "mul %[r6], %[r3], %[r6] \n\t" + "mul %[r2], %[r3], %[r2] \n\t" + "addiu %[cosptr], %[cosptr], 2 \n\t" + "addiu %[sinptr], %[sinptr], 2 \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r7], %[r7], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r7], %[r7], 0x2000 \n\t" + "sra %[r7], %[r7], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sll %[r9], %[r9], 2 \n\t" + "addu %[r9], %[r7], %[r9] \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r6], %[r6], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r6], %[r6], 0x2000 \n\t" + "sra %[r6], %[r6], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sll %[r2], %[r2], 2 \n\t" + "addu %[r2], %[r6], %[r2] \n\t" + "subu %[r8], %[r8], %[r9] \n\t" + "sra %[r8], %[r8], 9 \n\t" + "addu %[r2], %[r4], %[r2] \n\t" + "sra %[r2], %[r2], 9 \n\t" + "sh %[r8], 0(%[tmpre]) \n\t" + "sh %[r2], 0(%[tmpim]) \n\t" + "andi %[r4], %[r1], 0xFFFF \n\t" + "sra %[r1], %[r1], 16 \n\t" + "andi %[r6], %[r0], 0xFFFF \n\t" + "sra %[r0], %[r0], 16 \n\t" + "mul %[r7], %[r5], %[r4] \n\t" + "mul %[r9], %[r5], %[r1] \n\t" + "mul %[r4], %[r3], %[r4] \n\t" + "mul %[r1], %[r3], %[r1] \n\t" + "mul %[r8], %[r3], %[r0] \n\t" + "mul %[r3], %[r3], %[r6] \n\t" + "mul %[r6], %[r5], %[r6] \n\t" + "mul %[r0], %[r5], %[r0] \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r7], %[r7], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r7], %[r7], 0x2000 \n\t" + "sra %[r7], %[r7], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sll %[r9], %[r9], 2 \n\t" + "addu %[r9], %[r9], %[r7] \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r4], %[r4], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r4], %[r4], 0x2000 \n\t" + "sra %[r4], %[r4], 14 \n\t" +#endif + "sll %[r1], %[r1], 2 \n\t" + "addu %[r1], %[r1], %[r4] \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r3], %[r3], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r3], %[r3], 0x2000 \n\t" + "sra %[r3], %[r3], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sll %[r8], %[r8], 2 \n\t" + "addu %[r8], %[r8], %[r3] \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r6], %[r6], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r6], %[r6], 0x2000 \n\t" + "sra %[r6], %[r6], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sll %[r0], %[r0], 2 \n\t" + "addu %[r0], %[r0], %[r6] \n\t" + "addu %[r3], %[tmpre], %[offset] \n\t" + "addu %[r2], %[tmpim], %[offset] \n\t" + "addu %[r9], %[r9], %[r8] \n\t" + "negu %[r9], %[r9] \n\t" + "sra %[r9], %[r9], 9 \n\t" + "subu %[r0], %[r0], %[r1] \n\t" + "sra %[r0], %[r0], 9 \n\t" + "addiu %[offset], %[offset], -4 \n\t" + "sh %[r9], 0(%[r3]) \n\t" + "sh %[r0], 0(%[r2]) \n\t" + "addiu %[tmpre], %[tmpre], 2 \n\t" + "bgtz %[k], 2b \n\t" + " addiu %[tmpim], %[tmpim], 2 \n\t" + "3: \n\t" + ".set pop \n\t" + : [inre1] "=&r" (inre1), [inre2] "=&r" (inre2), [tmpre] "=&r" (tmpre), + [tmpim] "=&r" (tmpim), [offset] "+r" (factor), [k] "+r" (k), + [r0] "=&r" (r0), [r1] "=&r" (r1), [r2] "=&r" (r2), [r3] "=&r" (r3), + [r4] "=&r" (r4), [r5] "=&r" (r5), [r6] "=&r" (r6), [r7] "=&r" (r7), + [r8] "=&r" (r8), [r9] "=&r" (r9), [max1] "=&r" (max1) + : [inre1Q9] "r" (inre1Q9), [inre2Q9] "r" (inre2Q9), + [outreQ7] "r" (outreQ7), [outimQ7] "r" (outimQ7), + [max] "r" (max), [cosptr] "r" (cosptr), [sinptr] "r" (sinptr) + : "hi", "lo", "memory" + ); +} + +void WebRtcIsacfix_Spec2TimeMIPS(int16_t *inreQ7, + int16_t *inimQ7, + int32_t *outre1Q16, + int32_t *outre2Q16) { + int k = FRAMESAMPLES / 4; + int16_t* inre; + int16_t* inim; + int32_t* outre1; + int32_t* outre2; + int16_t* cosptr = (int16_t*)WebRtcIsacfix_kCosTab2; + int16_t* sinptr = (int16_t*)WebRtcIsacfix_kSinTab2; + int32_t r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, max, max1; +#if defined(MIPS_DSP_R1_LE) + int32_t offset = FRAMESAMPLES - 4; +#else // #if defined(MIPS_DSP_R1_LE) + int32_t offset = FRAMESAMPLES - 2; +#endif // #if defined(MIPS_DSP_R1_LE) + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[inre], %[inreQ7], 0 \n\t" + "addiu %[inim] , %[inimQ7], 0 \n\t" + "addiu %[outre1], %[outre1Q16], 0 \n\t" + "addiu %[outre2], %[outre2Q16], 0 \n\t" + "mul %[max], $zero, $zero \n\t" + "1: \n\t" +#if defined(MIPS_DSP_R1_LE) + // Process two samples in one iteration avoiding left shift before + // multiplication. MaxAbsValueW32 function inlined into the loop. + "addu %[r8], %[inre], %[offset] \n\t" + "addu %[r9], %[inim], %[offset] \n\t" + "lwl %[r4], 0(%[r8]) \n\t" + "lwl %[r5], 0(%[r9]) \n\t" + "lwl %[r0], 0(%[inre]) \n\t" + "lwl %[r1], 0(%[inim]) \n\t" + "lwl %[r2], 0(%[cosptr]) \n\t" + "lwl %[r3], 0(%[sinptr]) \n\t" + "lwr %[r4], 0(%[r8]) \n\t" + "lwr %[r5], 0(%[r9]) \n\t" + "lwr %[r0], 0(%[inre]) \n\t" + "lwr %[r1], 0(%[inim]) \n\t" + "lwr %[r2], 0(%[cosptr]) \n\t" + "lwr %[r3], 0(%[sinptr]) \n\t" + "packrl.ph %[r4], %[r4], %[r4] \n\t" + "packrl.ph %[r5], %[r5], %[r5] \n\t" + "muleq_s.w.phr %[r6], %[r0], %[r2] \n\t" + "muleq_s.w.phr %[r7], %[r1], %[r3] \n\t" + "muleq_s.w.phr %[r8], %[r4], %[r2] \n\t" + "muleq_s.w.phr %[r9], %[r5], %[r3] \n\t" + "addiu %[k], %[k], -2 \n\t" + "addiu %[cosptr], %[cosptr], 4 \n\t" + "addiu %[sinptr], %[sinptr], 4 \n\t" + "addiu %[inre], %[inre], 4 \n\t" + "addiu %[inim], %[inim], 4 \n\t" + "shra_r.w %[r6], %[r6], 6 \n\t" + "shra_r.w %[r7], %[r7], 6 \n\t" + "shra_r.w %[r8], %[r8], 6 \n\t" + "shra_r.w %[r9], %[r9], 6 \n\t" + "addu %[r6], %[r6], %[r7] \n\t" + "subu %[r9], %[r9], %[r8] \n\t" + "subu %[r7], %[r6], %[r9] \n\t" + "addu %[r6], %[r6], %[r9] \n\t" + "sll %[r10], %[offset], 1 \n\t" + "addu %[r10], %[outre1], %[r10] \n\t" + "sw %[r7], 0(%[outre1]) \n\t" + "absq_s.w %[r7], %[r7] \n\t" + "sw %[r6], 4(%[r10]) \n\t" + "absq_s.w %[r6], %[r6] \n\t" + "slt %[r8], %[max], %[r7] \n\t" + "movn %[max], %[r7], %[r8] \n\t" + "slt %[r8], %[max], %[r6] \n\t" + "movn %[max], %[r6], %[r8] \n\t" + "muleq_s.w.phl %[r6], %[r0], %[r2] \n\t" + "muleq_s.w.phl %[r7], %[r1], %[r3] \n\t" + "muleq_s.w.phl %[r8], %[r4], %[r2] \n\t" + "muleq_s.w.phl %[r9], %[r5], %[r3] \n\t" + "shra_r.w %[r6], %[r6], 6 \n\t" + "shra_r.w %[r7], %[r7], 6 \n\t" + "shra_r.w %[r8], %[r8], 6 \n\t" + "shra_r.w %[r9], %[r9], 6 \n\t" + "addu %[r6], %[r6], %[r7] \n\t" + "subu %[r9], %[r9], %[r8] \n\t" + "subu %[r7], %[r6], %[r9] \n\t" + "addu %[r6], %[r6], %[r9] \n\t" + "sw %[r7], 4(%[outre1]) \n\t" + "absq_s.w %[r7], %[r7] \n\t" + "sw %[r6], 0(%[r10]) \n\t" + "absq_s.w %[r6], %[r6] \n\t" + "slt %[r8], %[max], %[r7] \n\t" + "movn %[max], %[r7], %[r8] \n\t" + "slt %[r8], %[max], %[r6] \n\t" + "movn %[max], %[r6], %[r8] \n\t" + "muleq_s.w.phr %[r6], %[r1], %[r2] \n\t" + "muleq_s.w.phr %[r7], %[r0], %[r3] \n\t" + "muleq_s.w.phr %[r8], %[r5], %[r2] \n\t" + "muleq_s.w.phr %[r9], %[r4], %[r3] \n\t" + "addiu %[outre1], %[outre1], 8 \n\t" + "shra_r.w %[r6], %[r6], 6 \n\t" + "shra_r.w %[r7], %[r7], 6 \n\t" + "shra_r.w %[r8], %[r8], 6 \n\t" + "shra_r.w %[r9], %[r9], 6 \n\t" + "subu %[r6], %[r6], %[r7] \n\t" + "addu %[r9], %[r9], %[r8] \n\t" + "subu %[r7], %[r6], %[r9] \n\t" + "addu %[r6], %[r9], %[r6] \n\t" + "negu %[r6], %[r6] \n\t" + "sll %[r10], %[offset], 1 \n\t" + "addu %[r10], %[outre2], %[r10] \n\t" + "sw %[r7], 0(%[outre2]) \n\t" + "absq_s.w %[r7], %[r7] \n\t" + "sw %[r6], 4(%[r10]) \n\t" + "absq_s.w %[r6], %[r6] \n\t" + "slt %[r8], %[max], %[r7] \n\t" + "movn %[max], %[r7], %[r8] \n\t" + "slt %[r8], %[max], %[r6] \n\t" + "movn %[max], %[r6], %[r8] \n\t" + "muleq_s.w.phl %[r6], %[r1], %[r2] \n\t" + "muleq_s.w.phl %[r7], %[r0], %[r3] \n\t" + "muleq_s.w.phl %[r8], %[r5], %[r2] \n\t" + "muleq_s.w.phl %[r9], %[r4], %[r3] \n\t" + "addiu %[offset], %[offset], -8 \n\t" + "shra_r.w %[r6], %[r6], 6 \n\t" + "shra_r.w %[r7], %[r7], 6 \n\t" + "shra_r.w %[r8], %[r8], 6 \n\t" + "shra_r.w %[r9], %[r9], 6 \n\t" + "subu %[r6], %[r6], %[r7] \n\t" + "addu %[r9], %[r9], %[r8] \n\t" + "subu %[r7], %[r6], %[r9] \n\t" + "addu %[r6], %[r9], %[r6] \n\t" + "negu %[r6], %[r6] \n\t" + "sw %[r7], 4(%[outre2]) \n\t" + "absq_s.w %[r7], %[r7] \n\t" + "sw %[r6], 0(%[r10]) \n\t" + "absq_s.w %[r6], %[r6] \n\t" + "slt %[r8], %[max], %[r7] \n\t" + "movn %[max], %[r7], %[r8] \n\t" + "slt %[r8], %[max], %[r6] \n\t" + "movn %[max], %[r6], %[r8] \n\t" + "bgtz %[k], 1b \n\t" + " addiu %[outre2], %[outre2], 8 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "lh %[r0], 0(%[inre]) \n\t" + "lh %[r1], 0(%[inim]) \n\t" + "lh %[r4], 0(%[cosptr]) \n\t" + "lh %[r5], 0(%[sinptr]) \n\t" + "addiu %[k], %[k], -1 \n\t" + "mul %[r2], %[r0], %[r4] \n\t" + "mul %[r0], %[r0], %[r5] \n\t" + "mul %[r3], %[r1], %[r5] \n\t" + "mul %[r1], %[r1], %[r4] \n\t" + "addiu %[cosptr], %[cosptr], 2 \n\t" + "addiu %[sinptr], %[sinptr], 2 \n\t" + "addu %[r8], %[inre], %[offset] \n\t" + "addu %[r9], %[inim], %[offset] \n\t" + "addiu %[r2], %[r2], 16 \n\t" + "sra %[r2], %[r2], 5 \n\t" + "addiu %[r0], %[r0], 16 \n\t" + "sra %[r0], %[r0], 5 \n\t" + "addiu %[r3], %[r3], 16 \n\t" + "sra %[r3], %[r3], 5 \n\t" + "lh %[r6], 0(%[r8]) \n\t" + "lh %[r7], 0(%[r9]) \n\t" + "addiu %[r1], %[r1], 16 \n\t" + "sra %[r1], %[r1], 5 \n\t" + "mul %[r8], %[r7], %[r4] \n\t" + "mul %[r7], %[r7], %[r5] \n\t" + "mul %[r9], %[r6], %[r4] \n\t" + "mul %[r6], %[r6], %[r5] \n\t" + "addu %[r2], %[r2], %[r3] \n\t" + "subu %[r1], %[r1], %[r0] \n\t" + "sll %[r0], %[offset], 1 \n\t" + "addu %[r4], %[outre1], %[r0] \n\t" + "addu %[r5], %[outre2], %[r0] \n\t" + "addiu %[r8], %[r8], 16 \n\t" + "sra %[r8], %[r8], 5 \n\t" + "addiu %[r7], %[r7], 16 \n\t" + "sra %[r7], %[r7], 5 \n\t" + "addiu %[r6], %[r6], 16 \n\t" + "sra %[r6], %[r6], 5 \n\t" + "addiu %[r9], %[r9], 16 \n\t" + "sra %[r9], %[r9], 5 \n\t" + "addu %[r8], %[r8], %[r6] \n\t" + "negu %[r8], %[r8] \n\t" + "subu %[r7], %[r7], %[r9] \n\t" + "subu %[r6], %[r2], %[r7] \n\t" + "addu %[r0], %[r2], %[r7] \n\t" + "addu %[r3], %[r1], %[r8] \n\t" + "subu %[r1], %[r8], %[r1] \n\t" + "sw %[r6], 0(%[outre1]) \n\t" + "sw %[r0], 0(%[r4]) \n\t" + "sw %[r3], 0(%[outre2]) \n\t" + "sw %[r1], 0(%[r5]) \n\t" + "addiu %[outre1], %[outre1], 4 \n\t" + "addiu %[offset], %[offset], -4 \n\t" + "addiu %[inre], %[inre], 2 \n\t" + "addiu %[inim], %[inim], 2 \n\t" + // Inlined WebRtcSpl_MaxAbsValueW32 + "negu %[r5], %[r6] \n\t" + "slt %[r2], %[r6], $zero \n\t" + "movn %[r6], %[r5], %[r2] \n\t" + "negu %[r5], %[r0] \n\t" + "slt %[r2], %[r0], $zero \n\t" + "movn %[r0], %[r5], %[r2] \n\t" + "negu %[r5], %[r3] \n\t" + "slt %[r2], %[r3], $zero \n\t" + "movn %[r3], %[r5], %[r2] \n\t" + "negu %[r5], %[r1] \n\t" + "slt %[r2], %[r1], $zero \n\t" + "movn %[r1], %[r5], %[r2] \n\t" + "slt %[r2], %[r6], %[r0] \n\t" + "slt %[r5], %[r3], %[r1] \n\t" + "movn %[r6], %[r0], %[r2] \n\t" + "movn %[r3], %[r1], %[r5] \n\t" + "slt %[r2], %[r6], %[r3] \n\t" + "movn %[r6], %[r3], %[r2] \n\t" + "slt %[r2], %[max], %[r6] \n\t" + "movn %[max], %[r6], %[r2] \n\t" + "bgtz %[k], 1b \n\t" + " addiu %[outre2], %[outre2], 4 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "clz %[max], %[max] \n\t" + "addiu %[max], %[max], -25 \n\t" + ".set pop \n\t" + : [inre] "=&r" (inre), [inim] "=&r" (inim), + [outre1] "=&r" (outre1), [outre2] "=&r" (outre2), + [offset] "+r" (offset), [k] "+r" (k), [r0] "=&r" (r0), + [r1] "=&r" (r1), [r2] "=&r" (r2), [r3] "=&r" (r3), + [r4] "=&r" (r4), [r5] "=&r" (r5), [r6] "=&r" (r6), + [r7] "=&r" (r7), [r10] "=&r" (r10), + [r8] "=&r" (r8), [r9] "=&r" (r9), [max] "=&r" (max) + : [inreQ7] "r" (inreQ7), [inimQ7] "r" (inimQ7), + [cosptr] "r" (cosptr), [sinptr] "r" (sinptr), + [outre1Q16] "r" (outre1Q16), [outre2Q16] "r" (outre2Q16) + : "hi", "lo", "memory" + ); + + // "Fastest" vectors + k = FRAMESAMPLES / 4; + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[inre], %[inreQ7], 0 \n\t" + "addiu %[inim], %[inimQ7], 0 \n\t" + "addiu %[outre1], %[outre1Q16], 0 \n\t" + "addiu %[outre2], %[outre2Q16], 0 \n\t" + "bltz %[max], 2f \n\t" + " subu %[max1], $zero, %[max] \n\t" + "1: \n\t" + "lw %[r0], 0(%[outre1]) \n\t" + "lw %[r1], 0(%[outre2]) \n\t" + "lw %[r2], 4(%[outre1]) \n\t" + "lw %[r3], 4(%[outre2]) \n\t" + "sllv %[r0], %[r0], %[max] \n\t" + "sllv %[r1], %[r1], %[max] \n\t" + "sllv %[r2], %[r2], %[max] \n\t" + "sllv %[r3], %[r3], %[max] \n\t" + "addiu %[k], %[k], -1 \n\t" + "addiu %[outre1], %[outre1], 8 \n\t" + "addiu %[outre2], %[outre2], 8 \n\t" + "sh %[r0], 0(%[inre]) \n\t" + "sh %[r1], 0(%[inim]) \n\t" + "sh %[r2], 2(%[inre]) \n\t" + "sh %[r3], 2(%[inim]) \n\t" + "addiu %[inre], %[inre], 4 \n\t" + "bgtz %[k], 1b \n\t" + " addiu %[inim], %[inim], 4 \n\t" + "b 4f \n\t" + " nop \n\t" + "2: \n\t" +#if !defined(MIPS_DSP_R1_LE) + "addiu %[r4], $zero, 1 \n\t" + "addiu %[r5], %[max1], -1 \n\t" + "sllv %[r4], %[r4], %[r5] \n\t" +#endif // #if !defined(MIPS_DSP_R1_LE) + "3: \n\t" + "lw %[r0], 0(%[outre1]) \n\t" + "lw %[r1], 0(%[outre2]) \n\t" + "lw %[r2], 4(%[outre1]) \n\t" + "lw %[r3], 4(%[outre2]) \n\t" +#if defined(MIPS_DSP_R1_LE) + "shrav_r.w %[r0], %[r0], %[max1] \n\t" + "shrav_r.w %[r1], %[r1], %[max1] \n\t" + "shrav_r.w %[r2], %[r2], %[max1] \n\t" + "shrav_r.w %[r3], %[r3], %[max1] \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addu %[r0], %[r0], %[r4] \n\t" + "addu %[r1], %[r1], %[r4] \n\t" + "addu %[r2], %[r2], %[r4] \n\t" + "addu %[r3], %[r3], %[r4] \n\t" + "srav %[r0], %[r0], %[max1] \n\t" + "srav %[r1], %[r1], %[max1] \n\t" + "srav %[r2], %[r2], %[max1] \n\t" + "srav %[r3], %[r3], %[max1] \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "addiu %[outre1], %[outre1], 8 \n\t" + "addiu %[outre2], %[outre2], 8 \n\t" + "sh %[r0], 0(%[inre]) \n\t" + "sh %[r1], 0(%[inim]) \n\t" + "sh %[r2], 2(%[inre]) \n\t" + "sh %[r3], 2(%[inim]) \n\t" + "addiu %[k], %[k], -1 \n\t" + "addiu %[inre], %[inre], 4 \n\t" + "bgtz %[k], 3b \n\t" + " addiu %[inim], %[inim], 4 \n\t" + "4: \n\t" + ".set pop \n\t" + : [k] "+r" (k), [max1] "=&r" (max1), [r0] "=&r" (r0), + [inre] "=&r" (inre), [inim] "=&r" (inim), + [outre1] "=&r" (outre1), [outre2] "=&r" (outre2), +#if !defined(MIPS_DSP_R1_LE) + [r4] "=&r" (r4), [r5] "=&r" (r5), +#endif // #if !defined(MIPS_DSP_R1_LE) + [r1] "=&r" (r1), [r2] "=&r" (r2), [r3] "=&r" (r3) + : [max] "r" (max), [inreQ7] "r" (inreQ7), + [inimQ7] "r" (inimQ7), [outre1Q16] "r" (outre1Q16), + [outre2Q16] "r" (outre2Q16) + : "memory" + ); + + WebRtcIsacfix_FftRadix16Fastest(inreQ7, inimQ7, 1); // real call + + // All the remaining processing is done inside a single loop to avoid + // unnecessary memory accesses. MIPS DSPr2 version processes two samples + // at a time. + cosptr = (int16_t*)WebRtcIsacfix_kCosTab1; + sinptr = (int16_t*)WebRtcIsacfix_kSinTab1; + k = FRAMESAMPLES / 2; + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[inre], %[inreQ7], 0 \n\t" + "addiu %[inim], %[inimQ7], 0 \n\t" + "addiu %[outre1], %[outre1Q16], 0 \n\t" + "addiu %[outre2], %[outre2Q16], 0 \n\t" + "addiu %[r4], $zero, 273 \n\t" + "addiu %[r5], $zero, 31727 \n\t" +#if defined(MIPS_DSP_R2_LE) + "addiu %[max], %[max], 16 \n\t" + "replv.ph %[r4], %[r4] \n\t" +#endif // #if defined(MIPS_DSP_R2_LE) + "bltz %[max], 2f \n\t" + " subu %[max1], $zero, %[max] \n\t" +#if defined(MIPS_DSP_R2_LE) + "addiu %[max], %[max], 1 \n\t" +#endif // #if defined(MIPS_DSP_R2_LE) + "1: \n\t" +#if defined(MIPS_DSP_R2_LE) + "lwl %[r0], 0(%[inre]) \n\t" + "lwl %[r1], 0(%[inim]) \n\t" + "lh %[r2], 0(%[cosptr]) \n\t" + "lwr %[r0], 0(%[inre]) \n\t" + "lwr %[r1], 0(%[inim]) \n\t" + "lh %[r3], 0(%[sinptr]) \n\t" + "muleq_s.w.phr %[r6], %[r0], %[r4] \n\t" + "muleq_s.w.phr %[r7], %[r1], %[r4] \n\t" + "muleq_s.w.phl %[r0], %[r0], %[r4] \n\t" + "muleq_s.w.phl %[r1], %[r1], %[r4] \n\t" + "addiu %[k], %[k], -2 \n\t" + "addiu %[inre], %[inre], 4 \n\t" + "addiu %[inim], %[inim], 4 \n\t" + "shrav_r.w %[r6], %[r6], %[max] \n\t" + "shrav_r.w %[r7], %[r7], %[max] \n\t" + "mult $ac0, %[r2], %[r6] \n\t" + "mult $ac1, %[r3], %[r7] \n\t" + "mult $ac2, %[r2], %[r7] \n\t" + "mult $ac3, %[r3], %[r6] \n\t" + "lh %[r2], 2(%[cosptr]) \n\t" + "lh %[r3], 2(%[sinptr]) \n\t" + "extr_r.w %[r6], $ac0, 14 \n\t" + "extr_r.w %[r7], $ac1, 14 \n\t" + "extr_r.w %[r8], $ac2, 14 \n\t" + "extr_r.w %[r9], $ac3, 14 \n\t" + "shrav_r.w %[r0], %[r0], %[max] \n\t" + "shrav_r.w %[r1], %[r1], %[max] \n\t" + "mult $ac0, %[r2], %[r0] \n\t" + "mult $ac1, %[r3], %[r1] \n\t" + "mult $ac2, %[r2], %[r1] \n\t" + "mult $ac3, %[r3], %[r0] \n\t" + "addiu %[cosptr], %[cosptr], 4 \n\t" + "extr_r.w %[r0], $ac0, 14 \n\t" + "extr_r.w %[r1], $ac1, 14 \n\t" + "extr_r.w %[r2], $ac2, 14 \n\t" + "extr_r.w %[r3], $ac3, 14 \n\t" + "subu %[r6], %[r6], %[r7] \n\t" + "addu %[r8], %[r8], %[r9] \n\t" + "mult $ac0, %[r5], %[r6] \n\t" + "mult $ac1, %[r5], %[r8] \n\t" + "addiu %[sinptr], %[sinptr], 4 \n\t" + "subu %[r0], %[r0], %[r1] \n\t" + "addu %[r2], %[r2], %[r3] \n\t" + "extr_r.w %[r1], $ac0, 11 \n\t" + "extr_r.w %[r3], $ac1, 11 \n\t" + "mult $ac2, %[r5], %[r0] \n\t" + "mult $ac3, %[r5], %[r2] \n\t" + "sw %[r1], 0(%[outre1]) \n\t" + "sw %[r3], 0(%[outre2]) \n\t" + "addiu %[outre1], %[outre1], 8 \n\t" + "extr_r.w %[r0], $ac2, 11 \n\t" + "extr_r.w %[r2], $ac3, 11 \n\t" + "sw %[r0], -4(%[outre1]) \n\t" + "sw %[r2], 4(%[outre2]) \n\t" + "bgtz %[k], 1b \n\t" + " addiu %[outre2], %[outre2], 8 \n\t" + "b 3f \n\t" +#else // #if defined(MIPS_DSP_R2_LE) + "lh %[r0], 0(%[inre]) \n\t" + "lh %[r1], 0(%[inim]) \n\t" + "addiu %[k], %[k], -1 \n\t" + "srav %[r0], %[r0], %[max] \n\t" + "srav %[r1], %[r1], %[max] \n\t" + "sra %[r2], %[r0], 16 \n\t" + "andi %[r0], %[r0], 0xFFFF \n\t" + "sra %[r0], %[r0], 1 \n\t" + "sra %[r3], %[r1], 16 \n\t" + "andi %[r1], %[r1], 0xFFFF \n\t" + "sra %[r1], %[r1], 1 \n\t" + "mul %[r2], %[r2], %[r4] \n\t" + "mul %[r0], %[r0], %[r4] \n\t" + "mul %[r3], %[r3], %[r4] \n\t" + "mul %[r1], %[r1], %[r4] \n\t" + "addiu %[inre], %[inre], 2 \n\t" + "addiu %[inim], %[inim], 2 \n\t" + "lh %[r6], 0(%[cosptr]) \n\t" + "lh %[r7], 0(%[sinptr]) \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r0], %[r0], 15 \n\t" + "shra_r.w %[r1], %[r1], 15 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r0], %[r0], 0x4000 \n\t" + "addiu %[r1], %[r1], 0x4000 \n\t" + "sra %[r0], %[r0], 15 \n\t" + "sra %[r1], %[r1], 15 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "addu %[r0], %[r2], %[r0] \n\t" + "addu %[r1], %[r3], %[r1] \n\t" + "sra %[r2], %[r0], 16 \n\t" + "andi %[r0], %[r0], 0xFFFF \n\t" + "mul %[r9], %[r2], %[r6] \n\t" + "mul %[r2], %[r2], %[r7] \n\t" + "mul %[r8], %[r0], %[r6] \n\t" + "mul %[r0], %[r0], %[r7] \n\t" + "sra %[r3], %[r3], 16 \n\t" + "andi %[r1], %[r1], 0xFFFF \n\t" + "sll %[r9], %[r9], 2 \n\t" + "sll %[r2], %[r2], 2 \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r8], %[r8], 14 \n\t" + "shra_r.w %[r0], %[r0], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r8], %[r8], 0x2000 \n\t" + "addiu %[r0], %[r0], 0x2000 \n\t" + "sra %[r8], %[r8], 14 \n\t" + "sra %[r0], %[r0], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "addu %[r9], %[r9], %[r8] \n\t" + "addu %[r2], %[r2], %[r0] \n\t" + "mul %[r0], %[r3], %[r6] \n\t" + "mul %[r3], %[r3], %[r7] \n\t" + "mul %[r8], %[r1], %[r6] \n\t" + "mul %[r1], %[r1], %[r8] \n\t" + "addiu %[cosptr], %[cosptr], 2 \n\t" + "addiu %[sinptr], %[sinptr], 2 \n\t" + "sll %[r0], %[r0], 2 \n\t" + "sll %[r3], %[r3], 2 \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r8], %[r8], 14 \n\t" + "shra_r.w %[r1], %[r1], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r8], %[r8], 0x2000 \n\t" + "addiu %[r1], %[r1], 0x2000 \n\t" + "sra %[r8], %[r8], 14 \n\t" + "sra %[r1], %[r1], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "addu %[r0], %[r0], %[r8] \n\t" + "addu %[r3], %[r3], %[r1] \n\t" + "subu %[r9], %[r9], %[r3] \n\t" + "addu %[r0], %[r0], %[r2] \n\t" + "sra %[r1], %[r9], 16 \n\t" + "andi %[r9], %[r9], 0xFFFF \n\t" + "mul %[r1], %[r1], %[r5] \n\t" + "mul %[r9], %[r9], %[r5] \n\t" + "sra %[r2], %[r0], 16 \n\t" + "andi %[r0], %[r0], 0xFFFF \n\t" + "mul %[r2], %[r2], %[r5] \n\t" + "mul %[r0], %[r0], %[r5] \n\t" + "sll %[r1], %[r1], 5 \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r9], %[r9], 11 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r9], %[r9], 0x400 \n\t" + "sra %[r9], %[r9], 11 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "addu %[r1], %[r1], %[r9] \n\t" + "sll %[r2], %[r2], 5 \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r0], %[r0], 11 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r0], %[r0], 0x400 \n\t" + "sra %[r0], %[r0], 11 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "addu %[r0], %[r0], %[r2] \n\t" + "sw %[r1], 0(%[outre1]) \n\t" + "addiu %[outre1], %[outre1], 4 \n\t" + "sw %[r0], 0(%[outre2]) \n\t" + "bgtz %[k], 1b \n\t" + " addiu %[outre2], %[outre2], 4 \n\t" + "b 3f \n\t" + " nop \n\t" +#endif // #if defined(MIPS_DSP_R2_LE) + "2: \n\t" +#if defined(MIPS_DSP_R2_LE) + "addiu %[max1], %[max1], -1 \n\t" + "21: \n\t" + "lwl %[r0], 0(%[inre]) \n\t" + "lwl %[r1], 0(%[inim]) \n\t" + "lh %[r2], 0(%[cosptr]) \n\t" + "lwr %[r0], 0(%[inre]) \n\t" + "lwr %[r1], 0(%[inim]) \n\t" + "lh %[r3], 0(%[sinptr]) \n\t" + "muleq_s.w.phr %[r6], %[r0], %[r4] \n\t" + "muleq_s.w.phr %[r7], %[r1], %[r4] \n\t" + "muleq_s.w.phl %[r0], %[r0], %[r4] \n\t" + "muleq_s.w.phl %[r1], %[r1], %[r4] \n\t" + "addiu %[k], %[k], -2 \n\t" + "addiu %[inre], %[inre], 4 \n\t" + "addiu %[inim], %[inim], 4 \n\t" + "sllv %[r6], %[r6], %[max1] \n\t" + "sllv %[r7], %[r7], %[max1] \n\t" + "mult $ac0, %[r2], %[r6] \n\t" + "mult $ac1, %[r3], %[r7] \n\t" + "mult $ac2, %[r2], %[r7] \n\t" + "mult $ac3, %[r3], %[r6] \n\t" + "lh %[r2], 2(%[cosptr]) \n\t" + "lh %[r3], 2(%[sinptr]) \n\t" + "extr_r.w %[r6], $ac0, 14 \n\t" + "extr_r.w %[r7], $ac1, 14 \n\t" + "extr_r.w %[r8], $ac2, 14 \n\t" + "extr_r.w %[r9], $ac3, 14 \n\t" + "sllv %[r0], %[r0], %[max1] \n\t" + "sllv %[r1], %[r1], %[max1] \n\t" + "mult $ac0, %[r2], %[r0] \n\t" + "mult $ac1, %[r3], %[r1] \n\t" + "mult $ac2, %[r2], %[r1] \n\t" + "mult $ac3, %[r3], %[r0] \n\t" + "addiu %[cosptr], %[cosptr], 4 \n\t" + "extr_r.w %[r0], $ac0, 14 \n\t" + "extr_r.w %[r1], $ac1, 14 \n\t" + "extr_r.w %[r2], $ac2, 14 \n\t" + "extr_r.w %[r3], $ac3, 14 \n\t" + "subu %[r6], %[r6], %[r7] \n\t" + "addu %[r8], %[r8], %[r9] \n\t" + "mult $ac0, %[r5], %[r6] \n\t" + "mult $ac1, %[r5], %[r8] \n\t" + "addiu %[sinptr], %[sinptr], 4 \n\t" + "subu %[r0], %[r0], %[r1] \n\t" + "addu %[r2], %[r2], %[r3] \n\t" + "extr_r.w %[r1], $ac0, 11 \n\t" + "extr_r.w %[r3], $ac1, 11 \n\t" + "mult $ac2, %[r5], %[r0] \n\t" + "mult $ac3, %[r5], %[r2] \n\t" + "sw %[r1], 0(%[outre1]) \n\t" + "sw %[r3], 0(%[outre2]) \n\t" + "addiu %[outre1], %[outre1], 8 \n\t" + "extr_r.w %[r0], $ac2, 11 \n\t" + "extr_r.w %[r2], $ac3, 11 \n\t" + "sw %[r0], -4(%[outre1]) \n\t" + "sw %[r2], 4(%[outre2]) \n\t" + "bgtz %[k], 21b \n\t" + " addiu %[outre2], %[outre2], 8 \n\t" + "b 3f \n\t" + " nop \n\t" +#else // #if defined(MIPS_DSP_R2_LE) + "lh %[r0], 0(%[inre]) \n\t" + "lh %[r1], 0(%[inim]) \n\t" + "addiu %[k], %[k], -1 \n\t" + "sllv %[r0], %[r0], %[max1] \n\t" + "sllv %[r1], %[r1], %[max1] \n\t" + "sra %[r2], %[r0], 16 \n\t" + "andi %[r0], %[r0], 0xFFFF \n\t" + "sra %[r0], %[r0], 1 \n\t" + "sra %[r3], %[r1], 16 \n\t" + "andi %[r1], %[r1], 0xFFFF \n\t" + "sra %[r1], %[r1], 1 \n\t" + "mul %[r2], %[r2], %[r4] \n\t" + "mul %[r0], %[r0], %[r4] \n\t" + "mul %[r3], %[r3], %[r4] \n\t" + "mul %[r1], %[r1], %[r4] \n\t" + "addiu %[inre], %[inre], 2 \n\t" + "addiu %[inim], %[inim], 2 \n\t" + "lh %[r6], 0(%[cosptr]) \n\t" + "lh %[r7], 0(%[sinptr]) \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r0], %[r0], 15 \n\t" + "shra_r.w %[r1], %[r1], 15 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r0], %[r0], 0x4000 \n\t" + "addiu %[r1], %[r1], 0x4000 \n\t" + "sra %[r0], %[r0], 15 \n\t" + "sra %[r1], %[r1], 15 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "addu %[r0], %[r2], %[r0] \n\t" + "addu %[r1], %[r3], %[r1] \n\t" + "sra %[r2], %[r0], 16 \n\t" + "andi %[r0], %[r0], 0xFFFF \n\t" + "mul %[r9], %[r2], %[r6] \n\t" + "mul %[r2], %[r2], %[r7] \n\t" + "mul %[r8], %[r0], %[r6] \n\t" + "mul %[r0], %[r0], %[r7] \n\t" + "sra %[r3], %[r1], 16 \n\t" + "andi %[r1], %[r1], 0xFFFF \n\t" + "sll %[r9], %[r9], 2 \n\t" + "sll %[r2], %[r2], 2 \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r8], %[r8], 14 \n\t" + "shra_r.w %[r0], %[r0], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r8], %[r8], 0x2000 \n\t" + "addiu %[r0], %[r0], 0x2000 \n\t" + "sra %[r8], %[r8], 14 \n\t" + "sra %[r0], %[r0], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "addu %[r9], %[r9], %[r8] \n\t" + "addu %[r2], %[r2], %[r0] \n\t" + "mul %[r0], %[r3], %[r6] \n\t" + "mul %[r3], %[r3], %[r7] \n\t" + "mul %[r8], %[r1], %[r6] \n\t" + "mul %[r1], %[r1], %[r7] \n\t" + "addiu %[cosptr], %[cosptr], 2 \n\t" + "addiu %[sinptr], %[sinptr], 2 \n\t" + "sll %[r0], %[r0], 2 \n\t" + "sll %[r3], %[r3], 2 \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r8], %[r8], 14 \n\t" + "shra_r.w %[r1], %[r1], 14 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r8], %[r8], 0x2000 \n\t" + "addiu %[r1], %[r1], 0x2000 \n\t" + "sra %[r8], %[r8], 14 \n\t" + "sra %[r1], %[r1], 14 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "addu %[r0], %[r0], %[r8] \n\t" + "addu %[r3], %[r3], %[r1] \n\t" + "subu %[r9], %[r9], %[r3] \n\t" + "addu %[r0], %[r0], %[r2] \n\t" + "sra %[r1], %[r9], 16 \n\t" + "andi %[r9], %[r9], 0xFFFF \n\t" + "mul %[r1], %[r1], %[r5] \n\t" + "mul %[r9], %[r9], %[r5] \n\t" + "sra %[r2], %[r0], 16 \n\t" + "andi %[r0], %[r0], 0xFFFF \n\t" + "mul %[r2], %[r2], %[r5] \n\t" + "mul %[r0], %[r0], %[r5] \n\t" + "sll %[r1], %[r1], 5 \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r9], %[r9], 11 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r9], %[r9], 0x400 \n\t" + "sra %[r9], %[r9], 11 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "addu %[r1], %[r1], %[r9] \n\t" + "sll %[r2], %[r2], 5 \n\t" +#if defined(MIPS_DSP_R1_LE) + "shra_r.w %[r0], %[r0], 11 \n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "addiu %[r0], %[r0], 0x400 \n\t" + "sra %[r0], %[r0], 11 \n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "addu %[r0], %[r0], %[r2] \n\t" + "sw %[r1], 0(%[outre1]) \n\t" + "addiu %[outre1], %[outre1], 4 \n\t" + "sw %[r0], 0(%[outre2]) \n\t" + "bgtz %[k], 2b \n\t" + " addiu %[outre2], %[outre2], 4 \n\t" +#endif // #if defined(MIPS_DSP_R2_LE) + "3: \n\t" + ".set pop \n\t" + : [k] "+r" (k), [r0] "=&r" (r0), [r1] "=&r" (r1), + [r2] "=&r" (r2), [r3] "=&r" (r3), [r4] "=&r" (r4), + [r5] "=&r" (r5), [r6] "=&r" (r6), [r7] "=&r" (r7), + [r8] "=&r" (r8), [r9] "=&r" (r9), [max1] "=&r" (max1), + [inre] "=&r" (inre), [inim] "=&r" (inim), + [outre1] "=&r" (outre1), [outre2] "=&r" (outre2) + : [max] "r" (max), [inreQ7] "r" (inreQ7), + [inimQ7] "r" (inimQ7), [cosptr] "r" (cosptr), + [sinptr] "r" (sinptr), [outre1Q16] "r" (outre1Q16), + [outre2Q16] "r" (outre2Q16) + : "hi", "lo", "memory" +#if defined(MIPS_DSP_R2_LE) + , "$ac1hi", "$ac1lo", "$ac2hi", "$ac2lo", "$ac3hi", "$ac3lo" +#endif // #if defined(MIPS_DSP_R2_LE) + ); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform_neon.S thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform_neon.S --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform_neon.S 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform_neon.S 2015-02-03 14:33:34.000000000 +0000 @@ -42,7 +42,11 @@ add r5, sp, #(16 + FRAMESAMPLES * 2) @ tmpimQ16; adr r9, WebRtcIsacfix_kCosTab1 +#if defined(__APPLE__) + mov r6, #:lower16:(WebRtcIsacfix_kSinTab1 - WebRtcIsacfix_kCosTab1) +#else mov r6, #(WebRtcIsacfix_kSinTab1 - WebRtcIsacfix_kCosTab1) +#endif add r10, r9, r6 @ WebRtcIsacfix_kSinTab1 vmov.u32 q14, #0 @ Initialize the maximum values for tmpInIm. @@ -455,7 +459,12 @@ bgt TransformAndFindMax adr r10, WebRtcIsacfix_kSinTab1 +#if defined(__APPLE__) + mov r2, #:lower16:(WebRtcIsacfix_kSinTab1 - WebRtcIsacfix_kCosTab1) +#else mov r2, #(WebRtcIsacfix_kSinTab1 - WebRtcIsacfix_kCosTab1) +#endif + sub r11, r10, r2 @ WebRtcIsacfix_kCosTab1 @ Find the maximum value in the Neon registers diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform_tables.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform_tables.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform_tables.c 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform_tables.c 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains trigonometric functions look-up tables used in + * transform functions WebRtcIsacfix_Time2Spec and WebRtcIsacfix_Spec2Time. + */ + +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/settings.h" +#include "webrtc/typedefs.h" + +#if !(defined WEBRTC_DETECT_ARM_NEON || defined WEBRTC_ARCH_ARM_NEON) +/* Cosine table 1 in Q14. */ +const int16_t WebRtcIsacfix_kCosTab1[FRAMESAMPLES/2] = { + 16384, 16383, 16378, 16371, 16362, 16349, 16333, 16315, 16294, 16270, + 16244, 16214, 16182, 16147, 16110, 16069, 16026, 15980, 15931, 15880, + 15826, 15769, 15709, 15647, 15582, 15515, 15444, 15371, 15296, 15218, + 15137, 15053, 14968, 14879, 14788, 14694, 14598, 14500, 14399, 14295, + 14189, 14081, 13970, 13856, 13741, 13623, 13502, 13380, 13255, 13128, + 12998, 12867, 12733, 12597, 12458, 12318, 12176, 12031, 11885, 11736, + 11585, 11433, 11278, 11121, 10963, 10803, 10641, 10477, 10311, 10143, + 9974, 9803, 9630, 9456, 9280, 9102, 8923, 8743, 8561, 8377, + 8192, 8006, 7818, 7629, 7438, 7246, 7053, 6859, 6664, 6467, + 6270, 6071, 5872, 5671, 5469, 5266, 5063, 4859, 4653, 4447, + 4240, 4033, 3825, 3616, 3406, 3196, 2986, 2775, 2563, 2351, + 2139, 1926, 1713, 1499, 1285, 1072, 857, 643, 429, 214, + 0, -214, -429, -643, -857, -1072, -1285, -1499, -1713, -1926, + -2139, -2351, -2563, -2775, -2986, -3196, -3406, -3616, -3825, -4033, + -4240, -4447, -4653, -4859, -5063, -5266, -5469, -5671, -5872, -6071, + -6270, -6467, -6664, -6859, -7053, -7246, -7438, -7629, -7818, -8006, + -8192, -8377, -8561, -8743, -8923, -9102, -9280, -9456, -9630, -9803, + -9974, -10143, -10311, -10477, -10641, -10803, -10963, -11121, -11278, -11433, + -11585, -11736, -11885, -12031, -12176, -12318, -12458, -12597, -12733, + -12867, -12998, -13128, -13255, -13380, -13502, -13623, -13741, -13856, + -13970, -14081, -14189, -14295, -14399, -14500, -14598, -14694, -14788, + -14879, -14968, -15053, -15137, -15218, -15296, -15371, -15444, -15515, + -15582, -15647, -15709, -15769, -15826, -15880, -15931, -15980, -16026, + -16069, -16110, -16147, -16182, -16214, -16244, -16270, -16294, -16315, + -16333, -16349, -16362, -16371, -16378, -16383 +}; + +/* Sine table 1 in Q14. */ +const int16_t WebRtcIsacfix_kSinTab1[FRAMESAMPLES/2] = { + 0, 214, 429, 643, 857, 1072, 1285, 1499, 1713, 1926, + 2139, 2351, 2563, 2775, 2986, 3196, 3406, 3616, 3825, 4033, + 4240, 4447, 4653, 4859, 5063, 5266, 5469, 5671, 5872, 6071, + 6270, 6467, 6664, 6859, 7053, 7246, 7438, 7629, 7818, 8006, + 8192, 8377, 8561, 8743, 8923, 9102, 9280, 9456, 9630, 9803, + 9974, 10143, 10311, 10477, 10641, 10803, 10963, 11121, 11278, 11433, + 11585, 11736, 11885, 12031, 12176, 12318, 12458, 12597, 12733, 12867, + 12998, 13128, 13255, 13380, 13502, 13623, 13741, 13856, 13970, 14081, + 14189, 14295, 14399, 14500, 14598, 14694, 14788, 14879, 14968, 15053, + 15137, 15218, 15296, 15371, 15444, 15515, 15582, 15647, 15709, 15769, + 15826, 15880, 15931, 15980, 16026, 16069, 16110, 16147, 16182, 16214, + 16244, 16270, 16294, 16315, 16333, 16349, 16362, 16371, 16378, 16383, + 16384, 16383, 16378, 16371, 16362, 16349, 16333, 16315, 16294, 16270, + 16244, 16214, 16182, 16147, 16110, 16069, 16026, 15980, 15931, 15880, + 15826, 15769, 15709, 15647, 15582, 15515, 15444, 15371, 15296, 15218, + 15137, 15053, 14968, 14879, 14788, 14694, 14598, 14500, 14399, 14295, + 14189, 14081, 13970, 13856, 13741, 13623, 13502, 13380, 13255, 13128, + 12998, 12867, 12733, 12597, 12458, 12318, 12176, 12031, 11885, 11736, + 11585, 11433, 11278, 11121, 10963, 10803, 10641, 10477, 10311, 10143, + 9974, 9803, 9630, 9456, 9280, 9102, 8923, 8743, 8561, 8377, + 8192, 8006, 7818, 7629, 7438, 7246, 7053, 6859, 6664, 6467, + 6270, 6071, 5872, 5671, 5469, 5266, 5063, 4859, 4653, 4447, + 4240, 4033, 3825, 3616, 3406, 3196, 2986, 2775, 2563, 2351, + 2139, 1926, 1713, 1499, 1285, 1072, 857, 643, 429, 214 +}; + + +/* Sine table 2 in Q14. */ +const int16_t WebRtcIsacfix_kSinTab2[FRAMESAMPLES/4] = { + 16384, -16381, 16375, -16367, 16356, -16342, 16325, -16305, 16283, -16257, + 16229, -16199, 16165, -16129, 16090, -16048, 16003, -15956, 15906, -15853, + 15798, -15739, 15679, -15615, 15549, -15480, 15408, -15334, 15257, -15178, + 15095, -15011, 14924, -14834, 14741, -14647, 14549, -14449, 14347, -14242, + 14135, -14025, 13913, -13799, 13682, -13563, 13441, -13318, 13192, -13063, + 12933, -12800, 12665, -12528, 12389, -12247, 12104, -11958, 11810, -11661, + 11509, -11356, 11200, -11042, 10883, -10722, 10559, -10394, 10227, -10059, + 9889, -9717, 9543, -9368, 9191, -9013, 8833, -8652, 8469, -8285, + 8099, -7912, 7723, -7534, 7342, -7150, 6957, -6762, 6566, -6369, + 6171, -5971, 5771, -5570, 5368, -5165, 4961, -4756, 4550, -4344, + 4137, -3929, 3720, -3511, 3301, -3091, 2880, -2669, 2457, -2245, + 2032, -1819, 1606, -1392, 1179, -965, 750, -536, 322, -107 +}; +#endif + +#if defined(MIPS32_LE) +/* Cosine table 2 in Q14. Used only on MIPS platforms. */ +const int16_t WebRtcIsacfix_kCosTab2[FRAMESAMPLES/4] = { + 107, -322, 536, -750, 965, -1179, 1392, -1606, 1819, -2032, + 2245, -2457, 2669, -2880, 3091, -3301, 3511, -3720, 3929, -4137, + 4344, -4550, 4756, -4961, 5165, -5368, 5570, -5771, 5971, -6171, + 6369, -6566, 6762, -6957, 7150, -7342, 7534, -7723, 7912, -8099, + 8285, -8469, 8652, -8833, 9013, -9191, 9368, -9543, 9717, -9889, + 10059, -10227, 10394, -10559, 10722, -10883, 11042, -11200, 11356, -11509, + 11661, -11810, 11958, -12104, 12247, -12389, 12528, -12665, 12800, -12933, + 13063, -13192, 13318, -13441, 13563, -13682, 13799, -13913, 14025, -14135, + 14242, -14347, 14449, -14549, 14647, -14741, 14834, -14924, 15011, -15095, + 15178, -15257, 15334, -15408, 15480, -15549, 15615, -15679, 15739, -15798, + 15853, -15906, 15956, -16003, 16048, -16090, 16129, -16165, 16199, -16229, + 16257, -16283, 16305, -16325, 16342, -16356, 16367, -16375, 16381, -16384 +}; +#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/source/transform_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -7,7 +7,7 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/audio_coding/codecs/isac/fix/source/codec.h" #include "webrtc/system_wrappers/interface/cpu_features_wrapper.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/test/isac_speed_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/test/isac_speed_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/test/isac_speed_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/test/isac_speed_test.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/source/settings.h" +#include "webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_test.h" + +using ::std::string; + +namespace webrtc { + +static const int kIsacBlockDurationMs = 30; +static const int kIsacInputSamplingKhz = 16; +static const int kIsacOutputSamplingKhz = 16; + +class IsacSpeedTest : public AudioCodecSpeedTest { + protected: + IsacSpeedTest(); + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + virtual float EncodeABlock(int16_t* in_data, uint8_t* bit_stream, + int max_bytes, int* encoded_bytes); + virtual float DecodeABlock(const uint8_t* bit_stream, int encoded_bytes, + int16_t* out_data); + ISACFIX_MainStruct *ISACFIX_main_inst_; +}; + +IsacSpeedTest::IsacSpeedTest() + : AudioCodecSpeedTest(kIsacBlockDurationMs, + kIsacInputSamplingKhz, + kIsacOutputSamplingKhz), + ISACFIX_main_inst_(NULL) { +} + +void IsacSpeedTest::SetUp() { + AudioCodecSpeedTest::SetUp(); + + // Check whether the allocated buffer for the bit stream is large enough. + EXPECT_GE(max_bytes_, STREAM_MAXW16_60MS); + + // Create encoder memory. + EXPECT_EQ(0, WebRtcIsacfix_Create(&ISACFIX_main_inst_)); + EXPECT_EQ(0, WebRtcIsacfix_EncoderInit(ISACFIX_main_inst_, 1)); + EXPECT_EQ(0, WebRtcIsacfix_DecoderInit(ISACFIX_main_inst_)); + // Set bitrate and block length. + EXPECT_EQ(0, WebRtcIsacfix_Control(ISACFIX_main_inst_, bit_rate_, + block_duration_ms_)); +} + +void IsacSpeedTest::TearDown() { + AudioCodecSpeedTest::TearDown(); + // Free memory. + EXPECT_EQ(0, WebRtcIsacfix_Free(ISACFIX_main_inst_)); +} + +float IsacSpeedTest::EncodeABlock(int16_t* in_data, uint8_t* bit_stream, + int max_bytes, int* encoded_bytes) { + // ISAC takes 10 ms everycall + const int subblocks = block_duration_ms_ / 10; + const int subblock_length = 10 * input_sampling_khz_; + int value; + + clock_t clocks = clock(); + size_t pointer = 0; + for (int idx = 0; idx < subblocks; idx++, pointer += subblock_length) { + value = WebRtcIsacfix_Encode(ISACFIX_main_inst_, &in_data[pointer], + bit_stream); + } + clocks = clock() - clocks; + EXPECT_GT(value, 0); + assert(value <= max_bytes); + *encoded_bytes = value; + return 1000.0 * clocks / CLOCKS_PER_SEC; +} + +float IsacSpeedTest::DecodeABlock(const uint8_t* bit_stream, int encoded_bytes, + int16_t* out_data) { + int value; + int16_t audio_type; + clock_t clocks = clock(); + value = WebRtcIsacfix_Decode(ISACFIX_main_inst_, + bit_stream, + encoded_bytes, out_data, &audio_type); + clocks = clock() - clocks; + EXPECT_EQ(output_length_sample_, value); + return 1000.0 * clocks / CLOCKS_PER_SEC; +} + +TEST_P(IsacSpeedTest, IsacEncodeDecodeTest) { + size_t kDurationSec = 400; // Test audio length in second. + EncodeDecode(kDurationSec); +} + +const coding_param param_set[] = + {::std::tr1::make_tuple(1, 32000, string("audio_coding/speech_mono_16kHz"), + string("pcm"), true)}; + +INSTANTIATE_TEST_CASE_P(AllTest, IsacSpeedTest, + ::testing::ValuesIn(param_set)); + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/test/kenny.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/test/kenny.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/test/kenny.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/test/kenny.cc 2015-02-03 14:33:34.000000000 +0000 @@ -565,18 +565,21 @@ /* Encode */ stream_len = WebRtcIsacfix_Encode(ISAC_main_inst, shortdata, - (int16_t*)streamdata); + (uint8_t*)streamdata); /* If packet is ready, and CE testing, call the different API functions from the internal API. */ if (stream_len>0) { if (testCE == 1) { - err = WebRtcIsacfix_ReadBwIndex((int16_t*)streamdata, &bwe); + err = WebRtcIsacfix_ReadBwIndex( + reinterpret_cast(streamdata), + stream_len, + &bwe); stream_len = WebRtcIsacfix_GetNewBitStream( ISAC_main_inst, bwe, scale, - (int16_t*)streamdata); + reinterpret_cast(streamdata)); } else if (testCE == 2) { /* transcode function not supported */ } else if (testCE == 3) { @@ -697,12 +700,13 @@ } if (testNum != 9) { - err = WebRtcIsacfix_UpdateBwEstimate(ISAC_main_inst, - streamdata, - stream_len, - BN_data.rtp_number, - BN_data.send_time, - BN_data.arrival_time); + err = WebRtcIsacfix_UpdateBwEstimate( + ISAC_main_inst, + reinterpret_cast(streamdata), + stream_len, + BN_data.rtp_number, + BN_data.send_time, + BN_data.arrival_time); if (err < 0) { /* exit if returned with error */ @@ -740,9 +744,14 @@ if (nbTest !=2 ) { short FL; /* Call getFramelen, only used here for function test */ - err = WebRtcIsacfix_ReadFrameLen((int16_t*)streamdata, &FL); - declen = WebRtcIsacfix_Decode( ISAC_main_inst, streamdata, stream_len, - decoded, speechType ); + err = WebRtcIsacfix_ReadFrameLen( + reinterpret_cast(streamdata), stream_len, &FL); + declen = WebRtcIsacfix_Decode( + ISAC_main_inst, + reinterpret_cast(streamdata), + stream_len, + decoded, + speechType); /* Error check */ if (err<0 || declen<0 || FL!=declen) { errtype=WebRtcIsacfix_GetErrorCode(ISAC_main_inst); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/test/test_iSACfixfloat.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/test/test_iSACfixfloat.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/test/test_iSACfixfloat.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/fix/test/test_iSACfixfloat.c 2015-02-03 14:33:34.000000000 +0000 @@ -439,7 +439,9 @@ /* iSAC encoding */ if (mode==0 || mode ==1) { - stream_len = WebRtcIsac_Encode(ISAC_main_inst, shortdata, streamdata); + stream_len = WebRtcIsac_Encode(ISAC_main_inst, + shortdata, + (uint8_t*)streamdata); if (stream_len < 0) { /* exit if returned with error */ errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); @@ -449,7 +451,10 @@ } else if (mode==2 || mode==3) { /* iSAC encoding */ if (nbTest != 1) - stream_len = WebRtcIsacfix_Encode(ISACFIX_main_inst, shortdata, streamdata); + stream_len = WebRtcIsacfix_Encode( + ISACFIX_main_inst, + shortdata, + (uint8_t*)streamdata); else stream_len = WebRtcIsacfix_EncodeNb(ISACFIX_main_inst, shortdata, streamdata); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/interface/isac.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/interface/isac.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/interface/isac.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/interface/isac.h 2015-02-03 14:33:34.000000000 +0000 @@ -147,7 +147,7 @@ int16_t WebRtcIsac_Encode( ISACStruct* ISAC_main_inst, const int16_t* speechIn, - int16_t* encoded); + uint8_t* encoded); /****************************************************************************** @@ -187,7 +187,7 @@ int16_t WebRtcIsac_UpdateBwEstimate( ISACStruct* ISAC_main_inst, - const uint16_t* encoded, + const uint8_t* encoded, int32_t packet_size, uint16_t rtp_seq_number, uint32_t send_ts, @@ -216,7 +216,7 @@ int16_t WebRtcIsac_Decode( ISACStruct* ISAC_main_inst, - const uint16_t* encoded, + const uint8_t* encoded, int16_t len, int16_t* decoded, int16_t* speechType); @@ -319,7 +319,7 @@ int16_t WebRtcIsac_ReadFrameLen( ISACStruct* ISAC_main_inst, - const int16_t* encoded, + const uint8_t* encoded, int16_t* frameLength); @@ -574,7 +574,7 @@ int16_t bweIndex, int16_t jitterInfo, int32_t rate, - int16_t* encoded, + uint8_t* encoded, int16_t isRCU); @@ -631,7 +631,7 @@ */ int16_t WebRtcIsac_ReadBwIndex( - const int16_t* encoded, + const uint8_t* encoded, int16_t* bweIndex); @@ -679,7 +679,7 @@ */ int16_t WebRtcIsac_GetRedPayload( ISACStruct* ISAC_main_inst, - int16_t* encoded); + uint8_t* encoded); /**************************************************************************** @@ -703,7 +703,7 @@ */ int16_t WebRtcIsac_DecodeRcu( ISACStruct* ISAC_main_inst, - const uint16_t* encoded, + const uint8_t* encoded, int16_t len, int16_t* decoded, int16_t* speechType); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_isac -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := \ - arith_routines.c \ - arith_routines_hist.c \ - arith_routines_logist.c \ - bandwidth_estimator.c \ - crc.c \ - decode.c \ - decode_bwe.c \ - encode.c \ - encode_lpc_swb.c \ - entropy_coding.c \ - fft.c \ - filter_functions.c \ - filterbank_tables.c \ - intialize.c \ - isac.c \ - filterbanks.c \ - pitch_lag_tables.c \ - lattice.c \ - lpc_gain_swb_tables.c \ - lpc_analysis.c \ - lpc_shape_swb12_tables.c \ - lpc_shape_swb16_tables.c \ - lpc_tables.c \ - pitch_estimator.c \ - pitch_filter.c \ - pitch_gain_tables.c \ - spectrum_ar_model_tables.c \ - transform.c - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../../../.. \ - $(LOCAL_PATH)/../../../../../../common_audio/signal_processing/include - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/crc.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/crc.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/crc.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/crc.c 2015-02-03 14:33:34.000000000 +0000 @@ -102,7 +102,7 @@ for (byte_cntr = 0; byte_cntr < len_bitstream_in_bytes; byte_cntr++) { crc_tbl_indx = (WEBRTC_SPL_RSHIFT_U32(crc_state, 24) ^ bitstream_ptr_uw8[byte_cntr]) & 0xFF; - crc_state = WEBRTC_SPL_LSHIFT_U32(crc_state, 8) ^ kCrcTable[crc_tbl_indx]; + crc_state = (crc_state << 8) ^ kCrcTable[crc_tbl_indx]; } *crc = ~crc_state; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/crc.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/crc.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/crc.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/crc.h 2015-02-03 14:33:34.000000000 +0000 @@ -18,7 +18,7 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CRC_H_ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CRC_H_ -#include "typedefs.h" +#include "webrtc/typedefs.h" /**************************************************************************** * WebRtcIsac_GetCrc(...) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.c 2015-02-03 14:33:34.000000000 +0000 @@ -17,16 +17,16 @@ */ #include "encode_lpc_swb.h" -#include "typedefs.h" -#include "settings.h" - -#include "lpc_shape_swb12_tables.h" -#include "lpc_shape_swb16_tables.h" -#include "lpc_gain_swb_tables.h" +#include #include #include -#include + +#include "lpc_gain_swb_tables.h" +#include "lpc_shape_swb12_tables.h" +#include "lpc_shape_swb16_tables.h" +#include "settings.h" +#include "webrtc/typedefs.h" /****************************************************************************** * WebRtcIsac_RemoveLarMean() diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.h 2015-02-03 14:33:34.000000000 +0000 @@ -19,10 +19,9 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENCODE_LPC_SWB_H_ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENCODE_LPC_SWB_H_ -#include "typedefs.h" #include "settings.h" #include "structs.h" - +#include "webrtc/typedefs.h" /****************************************************************************** * WebRtcIsac_RemoveLarMean() diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/entropy_coding.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/entropy_coding.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/entropy_coding.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/entropy_coding.c 2015-02-03 14:33:34.000000000 +0000 @@ -106,14 +106,13 @@ for (n = 0; n < AR_ORDER + 1; n++) { sum += WEBRTC_SPL_MUL(ARCoefQ12[n], ARCoefQ12[n]); /* Q24 */ } - sum = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(WEBRTC_SPL_RSHIFT_W32(sum, 6), - 65) + 32768, 16); /* Q8 */ - CorrQ11[0] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(sum, gainQ10) + 256, 9); + sum = ((sum >> 6) * 65 + 32768) >> 16; /* Q8 */ + CorrQ11[0] = (sum * gainQ10 + 256) >> 9; /* To avoid overflow, we shift down gainQ10 if it is large. * We will not lose any precision */ if (gainQ10 > 400000) { - tmpGain = WEBRTC_SPL_RSHIFT_W32(gainQ10, 3); + tmpGain = gainQ10 >> 3; round = 32; shftVal = 6; } else { @@ -126,9 +125,8 @@ sum = 16384; for (n = k; n < AR_ORDER + 1; n++) sum += WEBRTC_SPL_MUL(ARCoefQ12[n - k], ARCoefQ12[n]); /* Q24 */ - sum = WEBRTC_SPL_RSHIFT_W32(sum, 15); - CorrQ11[k] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(sum, tmpGain) + round, - shftVal); + sum >>= 15; + CorrQ11[k] = (sum * tmpGain + round) >> shftVal; } sum = WEBRTC_SPL_LSHIFT_W32(CorrQ11[0], 7); for (n = 0; n < FRAMESAMPLES / 8; n++) { @@ -136,8 +134,7 @@ } for (k = 1; k < AR_ORDER; k += 2) { for (n = 0; n < FRAMESAMPLES / 8; n++) { - CurveQ16[n] += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL( - WebRtcIsac_kCos[k][n], CorrQ11[k + 1]) + 2, 2); + CurveQ16[n] += (WebRtcIsac_kCos[k][n] * CorrQ11[k + 1] + 2) >> 2; } } @@ -155,14 +152,12 @@ shftVal = 0; } for (n = 0; n < FRAMESAMPLES / 8; n++) { - diffQ16[n] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL( - CS_ptrQ9[n], WEBRTC_SPL_RSHIFT_W32(CorrQ11[1], shftVal)) + 2, 2); + diffQ16[n] = (CS_ptrQ9[n] * (CorrQ11[1] >> shftVal) + 2) >> 2; } for (k = 2; k < AR_ORDER; k += 2) { CS_ptrQ9 = WebRtcIsac_kCos[k]; for (n = 0; n < FRAMESAMPLES / 8; n++) { - diffQ16[n] += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL( - CS_ptrQ9[n], WEBRTC_SPL_RSHIFT_W32(CorrQ11[k + 1], shftVal)) + 2, 2); + diffQ16[n] += (CS_ptrQ9[n] * (CorrQ11[k + 1] >> shftVal) + 2) >> 2; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/filter_functions.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/filter_functions.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/filter_functions.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/filter_functions.c 2015-02-03 14:33:34.000000000 +0000 @@ -9,6 +9,7 @@ */ #include +#include #ifdef WEBRTC_ANDROID #include #endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/isac.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/isac.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/isac.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/isac.c 2015-02-03 14:33:34.000000000 +0000 @@ -496,7 +496,7 @@ */ int16_t WebRtcIsac_Encode(ISACStruct* ISAC_main_inst, const int16_t* speechIn, - int16_t* encoded) { + uint8_t* encoded) { float inFrame[FRAMESAMPLES_10ms]; int16_t speechInLB[FRAMESAMPLES_10ms]; int16_t speechInUB[FRAMESAMPLES_10ms]; @@ -504,7 +504,6 @@ int16_t streamLenUB = 0; int16_t streamLen = 0; int16_t k = 0; - uint8_t* ptrEncodedUW8 = (uint8_t*)encoded; int garbageLen = 0; int32_t bottleneck = 0; int16_t bottleneckIdx = 0; @@ -643,23 +642,22 @@ streamLenUB = 0; } - memcpy(ptrEncodedUW8, instLB->ISACencLB_obj.bitstr_obj.stream, streamLenLB); + memcpy(encoded, instLB->ISACencLB_obj.bitstr_obj.stream, streamLenLB); streamLen = streamLenLB; if (streamLenUB > 0) { - ptrEncodedUW8[streamLenLB] = (uint8_t)(streamLenUB + 1 + - LEN_CHECK_SUM_WORD8); - memcpy(&ptrEncodedUW8[streamLenLB + 1], - instUB->ISACencUB_obj.bitstr_obj.stream, streamLenUB); - streamLen += ptrEncodedUW8[streamLenLB]; + encoded[streamLenLB] = streamLenUB + 1 + LEN_CHECK_SUM_WORD8; + memcpy(&encoded[streamLenLB + 1], + instUB->ISACencUB_obj.bitstr_obj.stream, + streamLenUB); + streamLen += encoded[streamLenLB]; } else { - ptrEncodedUW8[streamLenLB] = 0; + encoded[streamLenLB] = 0; } } else { if (streamLenLB == 0) { return 0; } - memcpy(ptrEncodedUW8, instLB->ISACencLB_obj.bitstr_obj.stream, - streamLenLB); + memcpy(encoded, instLB->ISACencLB_obj.bitstr_obj.stream, streamLenLB); streamLenUB = 0; streamLen = streamLenLB; } @@ -697,11 +695,11 @@ * 255 is the max garbage length we can signal using 8 bits. */ if ((instISAC->bandwidthKHz == isac8kHz) || (streamLenUB == 0)) { - ptrGarbage = &ptrEncodedUW8[streamLenLB]; + ptrGarbage = &encoded[streamLenLB]; limit = streamLen + 255; } else { - ptrGarbage = &ptrEncodedUW8[streamLenLB + 1 + streamLenUB]; - limit = streamLen + (255 - ptrEncodedUW8[streamLenLB]); + ptrGarbage = &encoded[streamLenLB + 1 + streamLenUB]; + limit = streamLen + (255 - encoded[streamLenLB]); } minBytes = (minBytes > limit) ? limit : minBytes; @@ -718,13 +716,12 @@ * That is the only way to preserve backward compatibility. */ if ((instISAC->bandwidthKHz == isac8kHz) || (streamLenUB == 0)) { - ptrEncodedUW8[streamLenLB] = (uint8_t)garbageLen; + encoded[streamLenLB] = garbageLen; } else { - ptrEncodedUW8[streamLenLB] += (uint8_t)garbageLen; + encoded[streamLenLB] += garbageLen; /* Write the length of the garbage at the end of the upper-band * bit-stream, if exists. This helps for sanity check. */ - ptrEncodedUW8[streamLenLB + 1 + streamLenUB] = - (uint8_t)garbageLen; + encoded[streamLenLB + 1 + streamLenUB] = garbageLen; } streamLen += garbageLen; @@ -741,16 +738,14 @@ if ((instISAC->bandwidthKHz != isac8kHz) && (streamLenUB > 0)) { uint32_t crc; - WebRtcIsac_GetCrc((int16_t*)(&(ptrEncodedUW8[streamLenLB + 1])), + WebRtcIsac_GetCrc((int16_t*)(&(encoded[streamLenLB + 1])), streamLenUB + garbageLen, &crc); #ifndef WEBRTC_ARCH_BIG_ENDIAN for (k = 0; k < LEN_CHECK_SUM_WORD8; k++) { - ptrEncodedUW8[streamLen - LEN_CHECK_SUM_WORD8 + k] = - (uint8_t)((crc >> (24 - k * 8)) & 0xFF); + encoded[streamLen - LEN_CHECK_SUM_WORD8 + k] = crc >> (24 - k * 8); } #else - memcpy(&ptrEncodedUW8[streamLenLB + streamLenUB + 1], &crc, - LEN_CHECK_SUM_WORD8); + memcpy(&encoded[streamLenLB + streamLenUB + 1], &crc, LEN_CHECK_SUM_WORD8); #endif } return streamLen; @@ -791,7 +786,7 @@ int16_t bweIndex, int16_t jitterInfo, int32_t rate, - int16_t* encoded, + uint8_t* encoded, int16_t isRCU) { Bitstr iSACBitStreamInst; /* Local struct for bitstream handling */ int16_t streamLenLB; @@ -804,7 +799,6 @@ double rateLB; double rateUB; int32_t currentBN; - uint8_t* encodedPtrUW8 = (uint8_t*)encoded; uint32_t crc; #ifndef WEBRTC_ARCH_BIG_ENDIAN int16_t k; @@ -890,20 +884,20 @@ } totalStreamLen = streamLenLB + streamLenUB + 1 + LEN_CHECK_SUM_WORD8; - encodedPtrUW8[streamLenLB] = streamLenUB + 1 + LEN_CHECK_SUM_WORD8; + encoded[streamLenLB] = streamLenUB + 1 + LEN_CHECK_SUM_WORD8; - memcpy(&encodedPtrUW8[streamLenLB + 1], iSACBitStreamInst.stream, + memcpy(&encoded[streamLenLB + 1], iSACBitStreamInst.stream, streamLenUB); - WebRtcIsac_GetCrc((int16_t*)(&(encodedPtrUW8[streamLenLB + 1])), + WebRtcIsac_GetCrc((int16_t*)(&(encoded[streamLenLB + 1])), streamLenUB, &crc); #ifndef WEBRTC_ARCH_BIG_ENDIAN for (k = 0; k < LEN_CHECK_SUM_WORD8; k++) { - encodedPtrUW8[totalStreamLen - LEN_CHECK_SUM_WORD8 + k] = + encoded[totalStreamLen - LEN_CHECK_SUM_WORD8 + k] = (uint8_t)((crc >> (24 - k * 8)) & 0xFF); } #else - memcpy(&encodedPtrUW8[streamLenLB + streamLenUB + 1], &crc, + memcpy(&encoded[streamLenLB + streamLenUB + 1], &crc, LEN_CHECK_SUM_WORD8); #endif return totalStreamLen; @@ -1002,7 +996,7 @@ * -1 - Error */ int16_t WebRtcIsac_UpdateBwEstimate(ISACStruct* ISAC_main_inst, - const uint16_t* encoded, + const uint8_t* encoded, int32_t packet_size, uint16_t rtp_seq_number, uint32_t send_ts, @@ -1032,8 +1026,8 @@ #ifndef WEBRTC_ARCH_BIG_ENDIAN for (k = 0; k < 10; k++) { - streamdata.stream[k] = (uint8_t)((encoded[k >> 1] >> - ((k & 1) << 3)) & 0xFF); + uint16_t ek = ((const uint16_t*)encoded)[k >> 1]; + streamdata.stream[k] = (uint8_t)((ek >> ((k & 1) << 3)) & 0xff); } #else memcpy(streamdata.stream, encoded, 10); @@ -1052,7 +1046,7 @@ } static int16_t Decode(ISACStruct* ISAC_main_inst, - const uint16_t* encoded, + const uint8_t* encoded, int16_t lenEncodedBytes, int16_t* decoded, int16_t* speechType, @@ -1071,7 +1065,6 @@ int16_t lenEncodedLBBytes; int16_t validChecksum = 1; int16_t k; - uint8_t* ptrEncodedUW8 = (uint8_t*)encoded; uint16_t numLayer; int16_t totSizeBytes; int16_t err; @@ -1100,7 +1093,7 @@ STREAM_SIZE_MAX : lenEncodedBytes; /* Copy to lower-band bit-stream structure. */ - memcpy(instISAC->instLB.ISACdecLB_obj.bitstr_obj.stream, ptrEncodedUW8, + memcpy(instISAC->instLB.ISACdecLB_obj.bitstr_obj.stream, encoded, lenEncodedLBBytes); /* Regardless of that the current codec is setup to work in @@ -1122,12 +1115,12 @@ totSizeBytes = numDecodedBytesLB; while (totSizeBytes != lenEncodedBytes) { if ((totSizeBytes > lenEncodedBytes) || - (ptrEncodedUW8[totSizeBytes] == 0) || + (encoded[totSizeBytes] == 0) || (numLayer > MAX_NUM_LAYERS)) { instISAC->errorCode = ISAC_LENGTH_MISMATCH; return -1; } - totSizeBytes += ptrEncodedUW8[totSizeBytes]; + totSizeBytes += encoded[totSizeBytes]; numLayer++; } @@ -1166,7 +1159,7 @@ instISAC->resetFlag_8kHz = 2; } else { /* This includes the checksum and the bytes that stores the length. */ - int16_t lenNextStream = ptrEncodedUW8[numDecodedBytesLB]; + int16_t lenNextStream = encoded[numDecodedBytesLB]; /* Is this garbage or valid super-wideband bit-stream? * Check if checksum is valid. */ @@ -1176,14 +1169,13 @@ validChecksum = 0; } else { /* Run CRC to see if the checksum match. */ - WebRtcIsac_GetCrc((int16_t*)( - &ptrEncodedUW8[numDecodedBytesLB + 1]), + WebRtcIsac_GetCrc((int16_t*)(&encoded[numDecodedBytesLB + 1]), lenNextStream - LEN_CHECK_SUM_WORD8 - 1, &crc); validChecksum = 1; for (k = 0; k < LEN_CHECK_SUM_WORD8; k++) { validChecksum &= (((crc >> (24 - k * 8)) & 0xFF) == - ptrEncodedUW8[numDecodedBytesLB + lenNextStream - + encoded[numDecodedBytesLB + lenNextStream - LEN_CHECK_SUM_WORD8 + k]); } } @@ -1215,7 +1207,7 @@ lenNextStream -= (LEN_CHECK_SUM_WORD8 + 1); memcpy(decInstUB->bitstr_obj.stream, - &ptrEncodedUW8[numDecodedBytesLB + 1], lenNextStream); + &encoded[numDecodedBytesLB + 1], lenNextStream); /* Reset bit-stream object, this is the first decoding. */ WebRtcIsac_ResetBitstream(&(decInstUB->bitstr_obj)); @@ -1289,7 +1281,7 @@ /* It might be less due to garbage. */ if ((numDecodedBytesUB != lenNextStream) && (numDecodedBytesUB != (lenNextStream - - ptrEncodedUW8[numDecodedBytesLB + 1 + numDecodedBytesUB]))) { + encoded[numDecodedBytesLB + 1 + numDecodedBytesUB]))) { instISAC->errorCode = ISAC_LENGTH_MISMATCH; return -1; } @@ -1352,7 +1344,7 @@ */ int16_t WebRtcIsac_Decode(ISACStruct* ISAC_main_inst, - const uint16_t* encoded, + const uint8_t* encoded, int16_t lenEncodedBytes, int16_t* decoded, int16_t* speechType) { @@ -1384,7 +1376,7 @@ int16_t WebRtcIsac_DecodeRcu(ISACStruct* ISAC_main_inst, - const uint16_t* encoded, + const uint8_t* encoded, int16_t lenEncodedBytes, int16_t* decoded, int16_t* speechType) { @@ -1739,7 +1731,7 @@ * - bweIndex : Bandwidth estimate in bit-stream * */ -int16_t WebRtcIsac_ReadBwIndex(const int16_t* encoded, +int16_t WebRtcIsac_ReadBwIndex(const uint8_t* encoded, int16_t* bweIndex) { Bitstr streamdata; #ifndef WEBRTC_ARCH_BIG_ENDIAN @@ -1751,8 +1743,8 @@ #ifndef WEBRTC_ARCH_BIG_ENDIAN for (k = 0; k < 10; k++) { - streamdata.stream[k] = (uint8_t)((encoded[k >> 1] >> - ((k & 1) << 3)) & 0xFF); + int16_t ek2 = ((const int16_t*)encoded)[k >> 1]; + streamdata.stream[k] = (uint8_t)((ek2 >> ((k & 1) << 3)) & 0xff); } #else memcpy(streamdata.stream, encoded, 10); @@ -1788,7 +1780,7 @@ * */ int16_t WebRtcIsac_ReadFrameLen(ISACStruct* ISAC_main_inst, - const int16_t* encoded, + const uint8_t* encoded, int16_t* frameLength) { Bitstr streamdata; #ifndef WEBRTC_ARCH_BIG_ENDIAN @@ -1801,8 +1793,8 @@ #ifndef WEBRTC_ARCH_BIG_ENDIAN for (k = 0; k < 10; k++) { - streamdata.stream[k] = (uint8_t)((encoded[k >> 1] >> - ((k & 1) << 3)) & 0xFF); + int16_t ek2 = ((const int16_t*)encoded)[k >> 1]; + streamdata.stream[k] = (uint8_t)((ek2 >> ((k & 1) << 3)) & 0xff); } #else memcpy(streamdata.stream, encoded, 10); @@ -2101,13 +2093,12 @@ * : -1 - Error */ int16_t WebRtcIsac_GetRedPayload(ISACStruct* ISAC_main_inst, - int16_t* encoded) { + uint8_t* encoded) { Bitstr iSACBitStreamInst; int16_t streamLenLB; int16_t streamLenUB; int16_t streamLen; int16_t totalLenUB; - uint8_t* ptrEncodedUW8 = (uint8_t*)encoded; ISACMainStruct* instISAC = (ISACMainStruct*)ISAC_main_inst; #ifndef WEBRTC_ARCH_BIG_ENDIAN int k; @@ -2130,7 +2121,7 @@ } /* convert from bytes to int16_t. */ - memcpy(ptrEncodedUW8, iSACBitStreamInst.stream, streamLenLB); + memcpy(encoded, iSACBitStreamInst.stream, streamLenLB); streamLen = streamLenLB; if (instISAC->bandwidthKHz == isac8kHz) { return streamLenLB; @@ -2159,19 +2150,19 @@ (streamLenUB > 0)) { uint32_t crc; streamLen += totalLenUB; - ptrEncodedUW8[streamLenLB] = (uint8_t)totalLenUB; - memcpy(&ptrEncodedUW8[streamLenLB + 1], iSACBitStreamInst.stream, + encoded[streamLenLB] = (uint8_t)totalLenUB; + memcpy(&encoded[streamLenLB + 1], iSACBitStreamInst.stream, streamLenUB); - WebRtcIsac_GetCrc((int16_t*)(&(ptrEncodedUW8[streamLenLB + 1])), + WebRtcIsac_GetCrc((int16_t*)(&(encoded[streamLenLB + 1])), streamLenUB, &crc); #ifndef WEBRTC_ARCH_BIG_ENDIAN for (k = 0; k < LEN_CHECK_SUM_WORD8; k++) { - ptrEncodedUW8[streamLen - LEN_CHECK_SUM_WORD8 + k] = + encoded[streamLen - LEN_CHECK_SUM_WORD8 + k] = (uint8_t)((crc >> (24 - k * 8)) & 0xFF); } #else - memcpy(&ptrEncodedUW8[streamLenLB + streamLenUB + 1], &crc, + memcpy(&encoded[streamLenLB + streamLenUB + 1], &crc, LEN_CHECK_SUM_WORD8); #endif } @@ -2243,8 +2234,6 @@ } else { ISACUBStruct* instUB = &(instISAC->instUB); ISACLBStruct* instLB = &(instISAC->instLB); - double bottleneckLB; - double bottleneckUB; int32_t bottleneck = instISAC->bottleneck; int16_t codingMode = instISAC->codingMode; int16_t frameSizeMs = instLB->ISACencLB_obj.new_framelength / @@ -2263,6 +2252,8 @@ instISAC->maxRateBytesPer30Ms = STREAM_SIZE_MAX_30; } else if ((encoder_operational_rate == kIsacSuperWideband) && (instISAC->encoderSamplingRateKHz == kIsacWideband)) { + double bottleneckLB = 0; + double bottleneckUB = 0; if (codingMode == 1) { WebRtcIsac_RateAllocation(bottleneck, &bottleneckLB, &bottleneckUB, &(instISAC->bandwidthKHz)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/isac_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/isac_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/isac_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/isac_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -31,7 +31,7 @@ int16_t speech_data_[kIsacNumberOfSamples]; int16_t output_data_[kIsacNumberOfSamples]; - int16_t bitstream_[kMaxBytes / 2]; + uint8_t bitstream_[kMaxBytes]; uint8_t bitstream_small_[7]; // Simulate sync packets. }; @@ -79,13 +79,10 @@ WebRtcIsac_EncoderInit(isac_codec_, 0); WebRtcIsac_DecoderInit(isac_codec_); - // Encode & decode. int16_t encoded_bytes; - uint16_t* coded = reinterpret_cast(bitstream_); - uint16_t* coded_small = reinterpret_cast(bitstream_small_); // Test with call with a small packet (sync packet). - EXPECT_EQ(-1, WebRtcIsac_UpdateBwEstimate(isac_codec_, coded_small, 7, 1, + EXPECT_EQ(-1, WebRtcIsac_UpdateBwEstimate(isac_codec_, bitstream_small_, 7, 1, 12345, 56789)); // Encode 60 ms of data (needed to create a first packet). @@ -102,8 +99,8 @@ encoded_bytes = WebRtcIsac_Encode(isac_codec_, speech_data_, bitstream_); // Call to update bandwidth estimator with real data. - EXPECT_EQ(0, WebRtcIsac_UpdateBwEstimate(isac_codec_, coded, encoded_bytes, 1, - 12345, 56789)); + EXPECT_EQ(0, WebRtcIsac_UpdateBwEstimate(isac_codec_, bitstream_, + encoded_bytes, 1, 12345, 56789)); // Free memory. EXPECT_EQ(0, WebRtcIsac_Free(isac_codec_)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lattice.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lattice.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lattice.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lattice.c 2015-02-03 14:33:34.000000000 +0000 @@ -19,6 +19,7 @@ #include #include +#include #ifdef WEBRTC_ANDROID #include #endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.c 2015-02-03 14:33:34.000000000 +0000 @@ -18,7 +18,7 @@ #include "lpc_gain_swb_tables.h" #include "settings.h" -#include "typedefs.h" +#include "webrtc/typedefs.h" const double WebRtcIsac_kQSizeLpcGain = 0.100000; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.h 2015-02-03 14:33:34.000000000 +0000 @@ -20,7 +20,7 @@ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_GAIN_SWB_TABLES_H_ #include "settings.h" -#include "typedefs.h" +#include "webrtc/typedefs.h" extern const double WebRtcIsac_kQSizeLpcGain; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.c 2015-02-03 14:33:34.000000000 +0000 @@ -18,7 +18,7 @@ #include "lpc_shape_swb12_tables.h" #include "settings.h" -#include "typedefs.h" +#include "webrtc/typedefs.h" /* * Mean value of LAR diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.h 2015-02-03 14:33:34.000000000 +0000 @@ -20,7 +20,7 @@ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB12_TABLES_H_ #include "settings.h" -#include "typedefs.h" +#include "webrtc/typedefs.h" extern const double WebRtcIsac_kMeanLarUb12[UB_LPC_ORDER]; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.c 2015-02-03 14:33:34.000000000 +0000 @@ -18,7 +18,7 @@ #include "lpc_shape_swb16_tables.h" #include "settings.h" -#include "typedefs.h" +#include "webrtc/typedefs.h" /* * Mean value of LAR diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h 2015-02-03 14:33:34.000000000 +0000 @@ -20,8 +20,7 @@ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB16_TABLES_H_ #include "settings.h" -#include "typedefs.h" - +#include "webrtc/typedefs.h" extern const double WebRtcIsac_kMeanLarUb16[UB_LPC_ORDER]; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/os_specific_inline.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/os_specific_inline.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/os_specific_inline.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/os_specific_inline.h 2015-02-03 14:33:34.000000000 +0000 @@ -13,7 +13,7 @@ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_OS_SPECIFIC_INLINE_H_ #include -#include "typedefs.h" +#include "webrtc/typedefs.h" #if defined(WEBRTC_POSIX) #define WebRtcIsac_lrint lrint diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c 2015-02-03 14:33:34.000000000 +0000 @@ -12,6 +12,7 @@ #include #include +#include #ifdef WEBRTC_ANDROID #include #endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.h 2015-02-03 14:33:34.000000000 +0000 @@ -18,7 +18,7 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_GAIN_TABLES_H_ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_GAIN_TABLES_H_ -#include "typedefs.h" +#include "webrtc/typedefs.h" /* header file for coding tables for the pitch filter side-info in the entropy coder */ /********************* Pitch Filter Gain Coefficient Tables ************************/ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.h 2015-02-03 14:33:34.000000000 +0000 @@ -18,7 +18,7 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_LAG_TABLES_H_ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_LAG_TABLES_H_ -#include "typedefs.h" +#include "webrtc/typedefs.h" /* header file for coding tables for the pitch filter side-info in the entropy coder */ /********************* Pitch Filter Lag Coefficient Tables ************************/ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/test/ReleaseTest-API/ReleaseTest-API.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/test/ReleaseTest-API/ReleaseTest-API.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/test/ReleaseTest-API/ReleaseTest-API.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/test/ReleaseTest-API/ReleaseTest-API.cc 2015-02-03 14:33:34.000000000 +0000 @@ -49,7 +49,7 @@ int i, errtype, VADusage = 0, packetLossPercent = 0; int16_t CodingMode; - int32_t bottleneck; + int32_t bottleneck = 0; int16_t framesize = 30; /* ms */ int cur_framesmpls, err; @@ -57,7 +57,7 @@ double starttime, runtime, length_file; int16_t stream_len = 0; - int16_t declen, lostFrame = 0, declenTC = 0; + int16_t declen = 0, lostFrame = 0, declenTC = 0; int16_t shortdata[SWBFRAMESAMPLES_10ms]; int16_t vaddata[SWBFRAMESAMPLES_10ms*3]; @@ -93,7 +93,7 @@ //FILE logFile; bool doTransCoding = false; int32_t rateTransCoding = 0; - uint16_t streamDataTransCoding[600]; + uint8_t streamDataTransCoding[1200]; int16_t streamLenTransCoding = 0; FILE* transCodingFile = NULL; FILE* transcodingBitstream = NULL; @@ -448,7 +448,7 @@ { printf(" Error iSAC Cannot write file %s.\n", outname); cout << flush; - getchar(); + getc(stdin); exit(1); } if(VADusage) @@ -609,8 +609,8 @@ cout << "\n" << flush; length_file = 0; - int16_t bnIdxTC; - int16_t jitterInfoTC; + int16_t bnIdxTC = 0; + int16_t jitterInfoTC = 0; while (endfile == 0) { /* Call init functions at random, fault test number 7 */ @@ -662,8 +662,8 @@ if(!(testNum == 3 && framecnt == 0)) { stream_len = WebRtcIsac_Encode(ISAC_main_inst, - shortdata, - (int16_t*)streamdata); + shortdata, + (uint8_t*)streamdata); if((payloadSize != 0) && (stream_len > payloadSize)) { if(testNum == 0) @@ -687,8 +687,12 @@ /************************* Main Transcoding stream *******************************/ WebRtcIsac_GetDownLinkBwIndex(ISAC_main_inst, &bnIdxTC, &jitterInfoTC); streamLenTransCoding = WebRtcIsac_GetNewBitStream( - ISAC_main_inst, bnIdxTC, jitterInfoTC, rateTransCoding, - (int16_t*)streamDataTransCoding, false); + ISAC_main_inst, + bnIdxTC, + jitterInfoTC, + rateTransCoding, + streamDataTransCoding, + false); if(streamLenTransCoding < 0) { fprintf(stderr, "Error in trans-coding\n"); @@ -706,7 +710,7 @@ return -1; } - if (fwrite((uint8_t*)streamDataTransCoding, + if (fwrite(streamDataTransCoding, sizeof(uint8_t), streamLenTransCoding, transcodingBitstream) != @@ -714,9 +718,9 @@ return -1; } - WebRtcIsac_ReadBwIndex((int16_t*)streamDataTransCoding, &indexStream); - if(indexStream != bnIdxTC) - { + WebRtcIsac_ReadBwIndex(streamDataTransCoding, + &indexStream); + if (indexStream != bnIdxTC) { fprintf(stderr, "Error in inserting Bandwidth index into transcoding stream.\n"); exit(0); } @@ -780,14 +784,18 @@ // RED. if(lostFrame) { - stream_len = WebRtcIsac_GetRedPayload(ISAC_main_inst, - (int16_t*)streamdata); + stream_len = WebRtcIsac_GetRedPayload( + ISAC_main_inst, reinterpret_cast(streamdata)); if(doTransCoding) { streamLenTransCoding = WebRtcIsac_GetNewBitStream( - ISAC_main_inst, bnIdxTC, jitterInfoTC, rateTransCoding, - (int16_t*)streamDataTransCoding, true); + ISAC_main_inst, + bnIdxTC, + jitterInfoTC, + rateTransCoding, + streamDataTransCoding, + true); if(streamLenTransCoding < 0) { fprintf(stderr, "Error in RED trans-coding\n"); @@ -848,9 +856,13 @@ if(testNum != 9) { - err = WebRtcIsac_UpdateBwEstimate(ISAC_main_inst, streamdata, - stream_len, BN_data.rtp_number, BN_data.sample_count, - BN_data.arrival_time); + err = WebRtcIsac_UpdateBwEstimate( + ISAC_main_inst, + reinterpret_cast(streamdata), + stream_len, + BN_data.rtp_number, + BN_data.sample_count, + BN_data.arrival_time); if(err < 0) { @@ -872,8 +884,10 @@ } /* Call getFramelen, only used here for function test */ - err = WebRtcIsac_ReadFrameLen(ISAC_main_inst, - (int16_t*)streamdata, &FL); + err = WebRtcIsac_ReadFrameLen( + ISAC_main_inst, + reinterpret_cast(streamdata), + &FL); if(err < 0) { /* exit if returned with error */ @@ -894,26 +908,39 @@ if(lostFrame) { - declen = WebRtcIsac_DecodeRcu(ISAC_main_inst, streamdata, - stream_len, decoded, speechType); + declen = WebRtcIsac_DecodeRcu( + ISAC_main_inst, + reinterpret_cast(streamdata), + stream_len, + decoded, + speechType); if(doTransCoding) { - declenTC = WebRtcIsac_DecodeRcu(decoderTransCoding, - streamDataTransCoding, streamLenTransCoding, - decodedTC, speechType); + declenTC = WebRtcIsac_DecodeRcu( + decoderTransCoding, + streamDataTransCoding, + streamLenTransCoding, + decodedTC, + speechType); } } else { - declen = WebRtcIsac_Decode(ISAC_main_inst, streamdata, - stream_len, decoded, speechType); - + declen = WebRtcIsac_Decode( + ISAC_main_inst, + reinterpret_cast(streamdata), + stream_len, + decoded, + speechType); if(doTransCoding) { - declenTC = WebRtcIsac_Decode(decoderTransCoding, - streamDataTransCoding, streamLenTransCoding, - decodedTC, speechType); + declenTC = WebRtcIsac_Decode( + decoderTransCoding, + streamDataTransCoding, + streamLenTransCoding, + decodedTC, + speechType); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/test/simpleKenny.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/test/simpleKenny.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/test/simpleKenny.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/test/simpleKenny.c 2015-02-03 14:33:34.000000000 +0000 @@ -74,7 +74,7 @@ ISACStruct* ISAC_main_inst; int16_t stream_len = 0; - int16_t declen; + int16_t declen = 0; int16_t err; int16_t cur_framesmpls; int endfile; @@ -102,8 +102,8 @@ unsigned int tmpSumStreamLen = 0; unsigned int packetCntr = 0; unsigned int lostPacketCntr = 0; - uint16_t payload[600]; - uint16_t payloadRCU[600]; + uint8_t payload[1200]; + uint8_t payloadRCU[1200]; uint16_t packetLossPercent = 0; int16_t rcuStreamLen = 0; int onlyEncode; @@ -373,15 +373,17 @@ cur_framesmpls += samplesIn10Ms; //-------- iSAC encoding --------- - stream_len = WebRtcIsac_Encode(ISAC_main_inst, shortdata, - (int16_t*)payload); + stream_len = WebRtcIsac_Encode( + ISAC_main_inst, + shortdata, + payload); if(stream_len < 0) { // exit if returned with error //errType=WebRtcIsac_GetErrorCode(ISAC_main_inst); fprintf(stderr,"\nError in encoder\n"); - getchar(); + getc(stdin); exit(EXIT_FAILURE); } @@ -393,14 +395,18 @@ break; } - rcuStreamLen = WebRtcIsac_GetRedPayload(ISAC_main_inst, (int16_t*)payloadRCU); + rcuStreamLen = WebRtcIsac_GetRedPayload( + ISAC_main_inst, payloadRCU); get_arrival_time(cur_framesmpls, stream_len, bottleneck, &packetData, sampFreqKHz * 1000, sampFreqKHz * 1000); if(WebRtcIsac_UpdateBwEstimate(ISAC_main_inst, - payload, stream_len, packetData.rtp_number, - packetData.sample_count, - packetData.arrival_time) < 0) + payload, + stream_len, + packetData.rtp_number, + packetData.sample_count, + packetData.arrival_time) + < 0) { printf(" BWE Error at client\n"); return -1; @@ -452,20 +458,28 @@ if((rand() % 100) < packetLossPercent) { - declen = WebRtcIsac_DecodeRcu(ISAC_main_inst, payloadRCU, - rcuStreamLen, decoded, speechType); + declen = WebRtcIsac_DecodeRcu( + ISAC_main_inst, + payloadRCU, + rcuStreamLen, + decoded, + speechType); lostPacketCntr++; } else { - declen = WebRtcIsac_Decode(ISAC_main_inst, payload, - stream_len, decoded, speechType); - } - if(declen <= 0) + declen = WebRtcIsac_Decode( + ISAC_main_inst, + payload, + stream_len, + decoded, + speechType); + } + if(declen <= 0) { //errType=WebRtcIsac_GetErrorCode(ISAC_main_inst); fprintf(stderr,"\nError in decoder.\n"); - getchar(); + getc(stdin); exit(1); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/test/SwitchingSampRate/SwitchingSampRate.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/test/SwitchingSampRate/SwitchingSampRate.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/test/SwitchingSampRate/SwitchingSampRate.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/main/test/SwitchingSampRate/SwitchingSampRate.cc 2015-02-03 14:33:34.000000000 +0000 @@ -283,11 +283,14 @@ streamLen = WebRtcIsac_Encode(codecInstance[senderIdx], - audioBuff10ms, (short*)bitStream); + audioBuff10ms, + (uint8_t*)bitStream); int16_t ggg; if (streamLen > 0) { - if(( WebRtcIsac_ReadFrameLen(codecInstance[receiverIdx], - (short *) bitStream, &ggg))<0) + if ((WebRtcIsac_ReadFrameLen( + codecInstance[receiverIdx], + reinterpret_cast(bitStream), + &ggg)) < 0) printf("ERROR\n"); } @@ -408,19 +411,24 @@ } // BWE - if(WebRtcIsac_UpdateBwEstimate(codecInstance[receiverIdx], - bitStream, streamLen, packetData[senderIdx]->rtp_number, - packetData[senderIdx]->sample_count, - packetData[senderIdx]->arrival_time) < 0) - { + if (WebRtcIsac_UpdateBwEstimate( + codecInstance[receiverIdx], + reinterpret_cast(bitStream), + streamLen, + packetData[senderIdx]->rtp_number, + packetData[senderIdx]->sample_count, + packetData[senderIdx]->arrival_time) < 0) { printf(" BWE Error at client %d \n", receiverIdx + 1); return -1; } /**/ // Decode lenDecodedAudio = WebRtcIsac_Decode( - codecInstance[receiverIdx], bitStream, streamLen, - audioBuff60ms, speechType); + codecInstance[receiverIdx], + reinterpret_cast(bitStream), + streamLen, + audioBuff60ms, + speechType); if(lenDecodedAudio < 0) { printf(" Decoder error in client %d \n", receiverIdx + 1); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/codecs/opus/interface/audio_encoder_opus.h" + +#include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" + +namespace webrtc { + +namespace { + +// We always encode at 48 kHz. +const int kSampleRateHz = 48000; + +int DivExact(int a, int b) { + CHECK_EQ(a % b, 0); + return a / b; +} + +int16_t ClampInt16(size_t x) { + return static_cast( + std::min(x, static_cast(std::numeric_limits::max()))); +} + +int16_t CastInt16(size_t x) { + DCHECK_LE(x, static_cast(std::numeric_limits::max())); + return static_cast(x); +} + +} // namespace + +AudioEncoderOpus::Config::Config() : frame_size_ms(20), num_channels(1) {} + +bool AudioEncoderOpus::Config::IsOk() const { + if (frame_size_ms <= 0 || frame_size_ms % 10 != 0) + return false; + if (num_channels <= 0) + return false; + return true; +} + +AudioEncoderOpus::AudioEncoderOpus(const Config& config) + : num_10ms_frames_per_packet_(DivExact(config.frame_size_ms, 10)), + num_channels_(config.num_channels), + samples_per_10ms_frame_(DivExact(kSampleRateHz, 100) * num_channels_) { + CHECK(config.IsOk()); + input_buffer_.reserve(num_10ms_frames_per_packet_ * samples_per_10ms_frame_); + CHECK_EQ(0, WebRtcOpus_EncoderCreate(&inst_, num_channels_)); +} + +AudioEncoderOpus::~AudioEncoderOpus() { + CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_)); +} + +int AudioEncoderOpus::sample_rate_hz() const { + return kSampleRateHz; +} + +int AudioEncoderOpus::num_channels() const { + return num_channels_; +} + +int AudioEncoderOpus::Num10MsFramesInNextPacket() const { + return num_10ms_frames_per_packet_; +} + +bool AudioEncoderOpus::Encode(uint32_t timestamp, + const int16_t* audio, + size_t max_encoded_bytes, + uint8_t* encoded, + size_t* encoded_bytes, + uint32_t* encoded_timestamp) { + if (input_buffer_.empty()) + first_timestamp_in_buffer_ = timestamp; + input_buffer_.insert(input_buffer_.end(), audio, + audio + samples_per_10ms_frame_); + if (input_buffer_.size() < (static_cast(num_10ms_frames_per_packet_) * + samples_per_10ms_frame_)) { + *encoded_bytes = 0; + return true; + } + CHECK_EQ(input_buffer_.size(), + static_cast(num_10ms_frames_per_packet_) * + samples_per_10ms_frame_); + int16_t r = WebRtcOpus_Encode( + inst_, &input_buffer_[0], + DivExact(CastInt16(input_buffer_.size()), num_channels_), + ClampInt16(max_encoded_bytes), encoded); + input_buffer_.clear(); + if (r < 0) + return false; + *encoded_bytes = r; + *encoded_timestamp = first_timestamp_in_buffer_; + return true; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/interface/audio_encoder_opus.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/interface/audio_encoder_opus.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/interface/audio_encoder_opus.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/interface/audio_encoder_opus.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_OPUS_INTERFACE_AUDIO_ENCODER_OPUS_H_ +#define WEBRTC_MODULES_AUDIO_CODING_CODECS_OPUS_INTERFACE_AUDIO_ENCODER_OPUS_H_ + +#include + +#include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" +#include "webrtc/modules/audio_coding/codecs/audio_encoder.h" + +namespace webrtc { + +class AudioEncoderOpus : public AudioEncoder { + public: + struct Config { + Config(); + bool IsOk() const; + int frame_size_ms; + int num_channels; + }; + + explicit AudioEncoderOpus(const Config& config); + virtual ~AudioEncoderOpus() OVERRIDE; + + virtual int sample_rate_hz() const OVERRIDE; + virtual int num_channels() const OVERRIDE; + virtual int Num10MsFramesInNextPacket() const OVERRIDE; + + protected: + virtual bool Encode(uint32_t timestamp, + const int16_t* audio, + size_t max_encoded_bytes, + uint8_t* encoded, + size_t* encoded_bytes, + uint32_t* encoded_timestamp) OVERRIDE; + + private: + const int num_10ms_frames_per_packet_; + const int num_channels_; + const int samples_per_10ms_frame_; + std::vector input_buffer_; + OpusEncInst* inst_; + uint32_t first_timestamp_in_buffer_; +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_OPUS_INTERFACE_AUDIO_ENCODER_OPUS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h 2015-02-03 14:33:34.000000000 +0000 @@ -11,7 +11,7 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_OPUS_INTERFACE_OPUS_INTERFACE_H_ #define WEBRTC_MODULES_AUDIO_CODING_CODECS_OPUS_INTERFACE_OPUS_INTERFACE_H_ -#include "typedefs.h" +#include "webrtc/typedefs.h" #ifdef __cplusplus extern "C" { @@ -42,8 +42,11 @@ * Return value : >0 - Length (in bytes) of coded data * -1 - Error */ -int16_t WebRtcOpus_Encode(OpusEncInst* inst, int16_t* audio_in, int16_t samples, - int16_t length_encoded_buffer, uint8_t* encoded); +int16_t WebRtcOpus_Encode(OpusEncInst* inst, + const int16_t* audio_in, + int16_t samples, + int16_t length_encoded_buffer, + uint8_t* encoded); /**************************************************************************** * WebRtcOpus_SetBitRate(...) @@ -59,6 +62,89 @@ */ int16_t WebRtcOpus_SetBitRate(OpusEncInst* inst, int32_t rate); +/**************************************************************************** + * WebRtcOpus_SetPacketLossRate(...) + * + * This function configures the encoder's expected packet loss percentage. + * + * Input: + * - inst : Encoder context + * - loss_rate : loss percentage in the range 0-100, inclusive. + * Return value : 0 - Success + * -1 - Error + */ +int16_t WebRtcOpus_SetPacketLossRate(OpusEncInst* inst, int32_t loss_rate); + +/**************************************************************************** + * WebRtcOpus_SetMaxPlaybackRate(...) + * + * Configures the maximum playback rate for encoding. Due to hardware + * limitations, the receiver may render audio up to a playback rate. Opus + * encoder can use this information to optimize for network usage and encoding + * complexity. This will affect the audio bandwidth in the coded audio. However, + * the input/output sample rate is not affected. + * + * Input: + * - inst : Encoder context + * - frequency_hz : Maximum playback rate in Hz. + * This parameter can take any value. The relation + * between the value and the Opus internal mode is + * as following: + * frequency_hz <= 8000 narrow band + * 8000 < frequency_hz <= 12000 medium band + * 12000 < frequency_hz <= 16000 wide band + * 16000 < frequency_hz <= 24000 super wide band + * frequency_hz > 24000 full band + * Return value : 0 - Success + * -1 - Error + */ +int16_t WebRtcOpus_SetMaxPlaybackRate(OpusEncInst* inst, int32_t frequency_hz); + +/* TODO(minyue): Check whether an API to check the FEC and the packet loss rate + * is needed. It might not be very useful since there are not many use cases and + * the caller can always maintain the states. */ + +/**************************************************************************** + * WebRtcOpus_EnableFec() + * + * This function enables FEC for encoding. + * + * Input: + * - inst : Encoder context + * + * Return value : 0 - Success + * -1 - Error + */ +int16_t WebRtcOpus_EnableFec(OpusEncInst* inst); + +/**************************************************************************** + * WebRtcOpus_DisableFec() + * + * This function disables FEC for encoding. + * + * Input: + * - inst : Encoder context + * + * Return value : 0 - Success + * -1 - Error + */ +int16_t WebRtcOpus_DisableFec(OpusEncInst* inst); + +/* + * WebRtcOpus_SetComplexity(...) + * + * This function adjusts the computational complexity. The effect is the same as + * calling the complexity setting of Opus as an Opus encoder related CTL. + * + * Input: + * - inst : Encoder context + * - complexity : New target complexity (0-10, inclusive) + * + * Return value : 0 - Success + * -1 - Error + */ +int16_t WebRtcOpus_SetComplexity(OpusEncInst* inst, int32_t complexity); + int16_t WebRtcOpus_DecoderCreate(OpusDecInst** inst, int channels); int16_t WebRtcOpus_DecoderFree(OpusDecInst* inst); @@ -107,12 +193,13 @@ int16_t WebRtcOpus_DecodeNew(OpusDecInst* inst, const uint8_t* encoded, int16_t encoded_bytes, int16_t* decoded, int16_t* audio_type); -int16_t WebRtcOpus_Decode(OpusDecInst* inst, const int16_t* encoded, +int16_t WebRtcOpus_Decode(OpusDecInst* inst, const uint8_t* encoded, int16_t encoded_bytes, int16_t* decoded, int16_t* audio_type); -int16_t WebRtcOpus_DecodeSlave(OpusDecInst* inst, const int16_t* encoded, +int16_t WebRtcOpus_DecodeSlave(OpusDecInst* inst, const uint8_t* encoded, int16_t encoded_bytes, int16_t* decoded, int16_t* audio_type); + /**************************************************************************** * WebRtcOpus_DecodePlc(...) * TODO(tlegrand): Remove master and slave functions when NetEq4 is in place. @@ -138,6 +225,28 @@ int16_t number_of_lost_frames); /**************************************************************************** + * WebRtcOpus_DecodeFec(...) + * + * This function decodes the FEC data from an Opus packet into one or more audio + * frames at the ACM interface's sampling rate (32 kHz). + * + * Input: + * - inst : Decoder context + * - encoded : Encoded data + * - encoded_bytes : Bytes in encoded vector + * + * Output: + * - decoded : The decoded vector (previous frame) + * + * Return value : >0 - Samples per channel in decoded vector + * 0 - No FEC data in the packet + * -1 - Error + */ +int16_t WebRtcOpus_DecodeFec(OpusDecInst* inst, const uint8_t* encoded, + int16_t encoded_bytes, int16_t* decoded, + int16_t* audio_type); + +/**************************************************************************** * WebRtcOpus_DurationEst(...) * * This function calculates the duration of an opus packet. @@ -152,6 +261,40 @@ const uint8_t* payload, int payload_length_bytes); +/* TODO(minyue): Check whether it is needed to add a decoder context to the + * arguments, like WebRtcOpus_DurationEst(...). In fact, the packet itself tells + * the duration. The decoder context in WebRtcOpus_DurationEst(...) is not used. + * So it may be advisable to remove it from WebRtcOpus_DurationEst(...). */ + +/**************************************************************************** + * WebRtcOpus_FecDurationEst(...) + * + * This function calculates the duration of the FEC data within an opus packet. + * Input: + * - payload : Encoded data pointer + * - payload_length_bytes : Bytes of encoded data + * + * Return value : >0 - The duration of the FEC data in the + * packet in samples. + * 0 - No FEC data in the packet. + */ +int WebRtcOpus_FecDurationEst(const uint8_t* payload, + int payload_length_bytes); + +/**************************************************************************** + * WebRtcOpus_PacketHasFec(...) + * + * This function detects if an opus packet has FEC. + * Input: + * - payload : Encoded data pointer + * - payload_length_bytes : Bytes of encoded data + * + * Return value : 0 - the packet does NOT contain FEC. + * 1 - the packet contains FEC. + */ +int WebRtcOpus_PacketHasFec(const uint8_t* payload, + int payload_length_bytes); + #ifdef __cplusplus } // extern "C" #endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_fec_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_fec_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_fec_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_fec_test.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" +#include "webrtc/test/testsupport/fileutils.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +using ::std::string; +using ::std::tr1::tuple; +using ::std::tr1::get; +using ::testing::TestWithParam; + +namespace webrtc { + +// Define coding parameter as . +typedef tuple coding_param; +typedef struct mode mode; + +struct mode { + bool fec; + uint8_t target_packet_loss_rate; +}; + +const int kOpusBlockDurationMs = 20; +const int kOpusSamplingKhz = 48; + +class OpusFecTest : public TestWithParam { + protected: + OpusFecTest(); + + virtual void SetUp(); + virtual void TearDown(); + + virtual void EncodeABlock(); + + virtual void DecodeABlock(bool lost_previous, bool lost_current); + + int block_duration_ms_; + int sampling_khz_; + int block_length_sample_; + + int channels_; + int bit_rate_; + + size_t data_pointer_; + size_t loop_length_samples_; + int max_bytes_; + int encoded_bytes_; + + WebRtcOpusEncInst* opus_encoder_; + WebRtcOpusDecInst* opus_decoder_; + + string in_filename_; + + scoped_ptr in_data_; + scoped_ptr out_data_; + scoped_ptr bit_stream_; +}; + +void OpusFecTest::SetUp() { + channels_ = get<0>(GetParam()); + bit_rate_ = get<1>(GetParam()); + printf("Coding %d channel signal at %d bps.\n", channels_, bit_rate_); + + in_filename_ = test::ResourcePath(get<2>(GetParam()), get<3>(GetParam())); + + FILE* fp = fopen(in_filename_.c_str(), "rb"); + ASSERT_FALSE(fp == NULL); + + // Obtain file size. + fseek(fp, 0, SEEK_END); + loop_length_samples_ = ftell(fp) / sizeof(int16_t); + rewind(fp); + + // Allocate memory to contain the whole file. + in_data_.reset(new int16_t[loop_length_samples_ + + block_length_sample_ * channels_]); + + // Copy the file into the buffer. + ASSERT_EQ(fread(&in_data_[0], sizeof(int16_t), loop_length_samples_, fp), + loop_length_samples_); + fclose(fp); + + // The audio will be used in a looped manner. To ease the acquisition of an + // audio frame that crosses the end of the excerpt, we add an extra block + // length of samples to the end of the array, starting over again from the + // beginning of the array. Audio frames cross the end of the excerpt always + // appear as a continuum of memory. + memcpy(&in_data_[loop_length_samples_], &in_data_[0], + block_length_sample_ * channels_ * sizeof(int16_t)); + + // Maximum number of bytes in output bitstream. + max_bytes_ = block_length_sample_ * channels_ * sizeof(int16_t); + + out_data_.reset(new int16_t[2 * block_length_sample_ * channels_]); + bit_stream_.reset(new uint8_t[max_bytes_]); + + // Create encoder memory. + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, channels_)); + EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_decoder_, channels_)); + // Set bitrate. + EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, bit_rate_)); +} + +void OpusFecTest::TearDown() { + // Free memory. + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); + EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_decoder_)); +} + +OpusFecTest::OpusFecTest() + : block_duration_ms_(kOpusBlockDurationMs), + sampling_khz_(kOpusSamplingKhz), + block_length_sample_(block_duration_ms_ * sampling_khz_), + data_pointer_(0), + max_bytes_(0), + encoded_bytes_(0), + opus_encoder_(NULL), + opus_decoder_(NULL) { +} + +void OpusFecTest::EncodeABlock() { + int16_t value = WebRtcOpus_Encode(opus_encoder_, + &in_data_[data_pointer_], + block_length_sample_, + max_bytes_, &bit_stream_[0]); + EXPECT_GT(value, 0); + + encoded_bytes_ = value; +} + +void OpusFecTest::DecodeABlock(bool lost_previous, bool lost_current) { + int16_t audio_type; + int16_t value_1 = 0, value_2 = 0; + + if (lost_previous) { + // Decode previous frame. + if (!lost_current && + WebRtcOpus_PacketHasFec(&bit_stream_[0], encoded_bytes_) == 1) { + value_1 = WebRtcOpus_DecodeFec(opus_decoder_, &bit_stream_[0], + encoded_bytes_, &out_data_[0], + &audio_type); + } else { + value_1 = WebRtcOpus_DecodePlc(opus_decoder_, &out_data_[0], 1); + } + EXPECT_EQ(block_length_sample_, value_1); + } + + if (!lost_current) { + // Decode current frame. + value_2 = WebRtcOpus_DecodeNew(opus_decoder_, &bit_stream_[0], + encoded_bytes_, + &out_data_[value_1 * channels_], + &audio_type); + EXPECT_EQ(block_length_sample_, value_2); + } +} + +TEST_P(OpusFecTest, RandomPacketLossTest) { + const int kDurationMs = 200000; + int time_now_ms, fec_frames; + int actual_packet_loss_rate; + bool lost_current, lost_previous; + mode mode_set[3] = {{true, 0}, + {false, 0}, + {true, 50}}; + + lost_current = false; + for (int i = 0; i < 3; i++) { + if (mode_set[i].fec) { + EXPECT_EQ(0, WebRtcOpus_EnableFec(opus_encoder_)); + EXPECT_EQ(0, WebRtcOpus_SetPacketLossRate(opus_encoder_, + mode_set[i].target_packet_loss_rate)); + printf("FEC is ON, target at packet loss rate %d percent.\n", + mode_set[i].target_packet_loss_rate); + } else { + EXPECT_EQ(0, WebRtcOpus_DisableFec(opus_encoder_)); + printf("FEC is OFF.\n"); + } + // In this test, we let the target packet loss rate match the actual rate. + actual_packet_loss_rate = mode_set[i].target_packet_loss_rate; + // Run every mode a certain time. + time_now_ms = 0; + fec_frames = 0; + while (time_now_ms < kDurationMs) { + // Encode & decode. + EncodeABlock(); + + // Check if payload has FEC. + int16_t fec = WebRtcOpus_PacketHasFec(&bit_stream_[0], encoded_bytes_); + + // If FEC is disabled or the target packet loss rate is set to 0, there + // should be no FEC in the bit stream. + if (!mode_set[i].fec || mode_set[i].target_packet_loss_rate == 0) { + EXPECT_EQ(fec, 0); + } else if (fec == 1) { + fec_frames++; + } + + lost_previous = lost_current; + lost_current = rand() < actual_packet_loss_rate * (RAND_MAX / 100); + DecodeABlock(lost_previous, lost_current); + + time_now_ms += block_duration_ms_; + + // |data_pointer_| is incremented and wrapped across + // |loop_length_samples_|. + data_pointer_ = (data_pointer_ + block_length_sample_ * channels_) % + loop_length_samples_; + } + if (mode_set[i].fec) { + printf("%.2f percent frames has FEC.\n", + static_cast(fec_frames) * block_duration_ms_ / 2000); + } + } +} + +const coding_param param_set[] = + {::std::tr1::make_tuple(1, 64000, string("audio_coding/testfile32kHz"), + string("pcm")), + ::std::tr1::make_tuple(1, 32000, string("audio_coding/testfile32kHz"), + string("pcm")), + ::std::tr1::make_tuple(2, 64000, string("audio_coding/teststereo32kHz"), + string("pcm"))}; + +// 64 kbps, stereo +INSTANTIATE_TEST_CASE_P(AllTest, OpusFecTest, + ::testing::ValuesIn(param_set)); + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus.gypi 2015-02-03 14:33:34.000000000 +0000 @@ -19,7 +19,16 @@ # Mozilla provides its own build of the opus library. 'include_dirs': [ '/media/libopus/include', - ] + '/media/libopus/src', + '/media/libopus/celt', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '/media/libopus/include', + '/media/libopus/src', + '/media/libopus/celt', + ], + }, }, { 'dependencies': [ '<(DEPTH)/third_party/opus/opus.gyp:opus' @@ -33,9 +42,34 @@ 'OPUS_COMPLEXITY=<(opus_complexity)' ], 'sources': [ + 'audio_encoder_opus.cc', + 'interface/audio_encoder_opus.h', 'interface/opus_interface.h', + 'opus_inst.h', 'opus_interface.c', ], }, ], + 'conditions': [ + ['include_tests==1', { + 'targets': [ + { + 'target_name': 'webrtc_opus_fec_test', + 'type': 'executable', + 'dependencies': [ + 'webrtc_opus', + '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', + '<(webrtc_root)/test/test.gyp:test_support_main', + '<(DEPTH)/testing/gtest.gyp:gtest', + ], + 'include_dirs': [ + '<(webrtc_root)', + ], + 'sources': [ + 'opus_fec_test.cc', + ], + }, + ], + }], + ], } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_inst.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_inst.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_inst.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_inst.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_OPUS_OPUS_INST_H_ +#define WEBRTC_MODULES_AUDIO_CODING_CODECS_OPUS_OPUS_INST_H_ + +#include "opus.h" + +struct WebRtcOpusEncInst { + OpusEncoder* encoder; +}; + +struct WebRtcOpusDecInst { + OpusDecoder* decoder_left; + OpusDecoder* decoder_right; + int prev_decoded_samples; + int channels; +}; + + +#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_OPUS_OPUS_INST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_interface.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_interface.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_interface.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_interface.c 2015-02-03 14:33:34.000000000 +0000 @@ -9,15 +9,11 @@ */ #include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" +#include "webrtc/modules/audio_coding/codecs/opus/opus_inst.h" #include #include -#include "opus.h" - -#include "webrtc/common_audio/signal_processing/resample_by_2_internal.h" -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" - enum { /* Maximum supported frame size in WebRTC is 60 ms. */ kWebRtcOpusMaxEncodeFrameSizeMs = 60, @@ -31,25 +27,10 @@ * milliseconds. */ kWebRtcOpusMaxFrameSizePerChannel = 48 * kWebRtcOpusMaxDecodeFrameSizeMs, - /* Maximum sample count per frame is 48 kHz * maximum frame size in - * milliseconds * maximum number of channels. */ - kWebRtcOpusMaxFrameSize = kWebRtcOpusMaxFrameSizePerChannel * 2, - - /* Maximum sample count per channel for output resampled to 32 kHz, - * 32 kHz * maximum frame size in milliseconds. */ - kWebRtcOpusMaxFrameSizePerChannel32kHz = 32 * kWebRtcOpusMaxDecodeFrameSizeMs, - - /* Number of samples in resampler state. */ - kWebRtcOpusStateSize = 7, - /* Default frame size, 20 ms @ 48 kHz, in samples (for one channel). */ kWebRtcOpusDefaultFrameSize = 960, }; -struct WebRtcOpusEncInst { - OpusEncoder* encoder; -}; - int16_t WebRtcOpus_EncoderCreate(OpusEncInst** inst, int32_t channels) { OpusEncInst* state; if (inst != NULL) { @@ -82,17 +63,21 @@ } } -int16_t WebRtcOpus_Encode(OpusEncInst* inst, int16_t* audio_in, int16_t samples, - int16_t length_encoded_buffer, uint8_t* encoded) { - opus_int16* audio = (opus_int16*) audio_in; - unsigned char* coded = encoded; +int16_t WebRtcOpus_Encode(OpusEncInst* inst, + const int16_t* audio_in, + int16_t samples, + int16_t length_encoded_buffer, + uint8_t* encoded) { int res; if (samples > 48 * kWebRtcOpusMaxEncodeFrameSizeMs) { return -1; } - res = opus_encode(inst->encoder, audio, samples, coded, + res = opus_encode(inst->encoder, + (const opus_int16*)audio_in, + samples, + encoded, length_encoded_buffer); if (res > 0) { @@ -104,22 +89,67 @@ int16_t WebRtcOpus_SetBitRate(OpusEncInst* inst, int32_t rate) { if (inst) { #if defined(OPUS_COMPLEXITY) && (OPUS_COMPLEXITY != 0) - opus_encoder_ctl(inst->encoder, OPUS_SET_COMPLEXITY(OPUS_COMPLEXITY)); + opus_encoder_ctl(inst->encoder, OPUS_SET_COMPLEXITY(OPUS_COMPLEXITY)); #endif - return opus_encoder_ctl(inst->encoder, OPUS_SET_BITRATE(rate)); + return opus_encoder_ctl(inst->encoder, OPUS_SET_BITRATE(rate)); } else { return -1; } } -struct WebRtcOpusDecInst { - int16_t state_48_32_left[8]; - int16_t state_48_32_right[8]; - OpusDecoder* decoder_left; - OpusDecoder* decoder_right; - int prev_decoded_samples; - int channels; -}; +int16_t WebRtcOpus_SetPacketLossRate(OpusEncInst* inst, int32_t loss_rate) { + if (inst) { + return opus_encoder_ctl(inst->encoder, + OPUS_SET_PACKET_LOSS_PERC(loss_rate)); + } else { + return -1; + } +} + +int16_t WebRtcOpus_SetMaxPlaybackRate(OpusEncInst* inst, int32_t frequency_hz) { + opus_int32 set_bandwidth; + + if (!inst) + return -1; + + if (frequency_hz <= 8000) { + set_bandwidth = OPUS_BANDWIDTH_NARROWBAND; + } else if (frequency_hz <= 12000) { + set_bandwidth = OPUS_BANDWIDTH_MEDIUMBAND; + } else if (frequency_hz <= 16000) { + set_bandwidth = OPUS_BANDWIDTH_WIDEBAND; + } else if (frequency_hz <= 24000) { + set_bandwidth = OPUS_BANDWIDTH_SUPERWIDEBAND; + } else { + set_bandwidth = OPUS_BANDWIDTH_FULLBAND; + } + return opus_encoder_ctl(inst->encoder, + OPUS_SET_MAX_BANDWIDTH(set_bandwidth)); +} + +int16_t WebRtcOpus_EnableFec(OpusEncInst* inst) { + if (inst) { + return opus_encoder_ctl(inst->encoder, OPUS_SET_INBAND_FEC(1)); + } else { + return -1; + } +} + +int16_t WebRtcOpus_DisableFec(OpusEncInst* inst) { + if (inst) { + return opus_encoder_ctl(inst->encoder, OPUS_SET_INBAND_FEC(0)); + } else { + return -1; + } +} + +int16_t WebRtcOpus_SetComplexity(OpusEncInst* inst, int32_t complexity) { + if (inst) { + return opus_encoder_ctl(inst->encoder, OPUS_SET_COMPLEXITY(complexity)); + } else { + return -1; + } +} int16_t WebRtcOpus_DecoderCreate(OpusDecInst** inst, int channels) { int error_l; @@ -175,8 +205,6 @@ int16_t WebRtcOpus_DecoderInitNew(OpusDecInst* inst) { int error = opus_decoder_ctl(inst->decoder_left, OPUS_RESET_STATE); if (error == OPUS_OK) { - memset(inst->state_48_32_left, 0, sizeof(inst->state_48_32_left)); - memset(inst->state_48_32_right, 0, sizeof(inst->state_48_32_right)); return 0; } return -1; @@ -185,7 +213,6 @@ int16_t WebRtcOpus_DecoderInit(OpusDecInst* inst) { int error = opus_decoder_ctl(inst->decoder_left, OPUS_RESET_STATE); if (error == OPUS_OK) { - memset(inst->state_48_32_left, 0, sizeof(inst->state_48_32_left)); return 0; } return -1; @@ -194,7 +221,6 @@ int16_t WebRtcOpus_DecoderInitSlave(OpusDecInst* inst) { int error = opus_decoder_ctl(inst->decoder_right, OPUS_RESET_STATE); if (error == OPUS_OK) { - memset(inst->state_48_32_right, 0, sizeof(inst->state_48_32_right)); return 0; } return -1; @@ -203,13 +229,11 @@ /* |frame_size| is set to maximum Opus frame size in the normal case, and * is set to the number of samples needed for PLC in case of losses. * It is up to the caller to make sure the value is correct. */ -static int DecodeNative(OpusDecoder* inst, const int16_t* encoded, +static int DecodeNative(OpusDecoder* inst, const uint8_t* encoded, int16_t encoded_bytes, int frame_size, int16_t* decoded, int16_t* audio_type) { - unsigned char* coded = (unsigned char*) encoded; - opus_int16* audio = (opus_int16*) decoded; - - int res = opus_decode(inst, coded, encoded_bytes, audio, frame_size, 0); + int res = opus_decode( + inst, encoded, encoded_bytes, (opus_int16*)decoded, frame_size, 0); /* TODO(tlegrand): set to DTX for zero-length packets? */ *audio_type = 0; @@ -220,124 +244,44 @@ return -1; } -/* Resample from 48 to 32 kHz. Length of state is assumed to be - * kWebRtcOpusStateSize (7). - */ -static int WebRtcOpus_Resample48to32(const int16_t* samples_in, int length, - int16_t* state, int16_t* samples_out) { - int i; - int blocks; - int16_t output_samples; - int32_t buffer32[kWebRtcOpusMaxFrameSizePerChannel + kWebRtcOpusStateSize]; - - /* Resample from 48 kHz to 32 kHz. */ - for (i = 0; i < kWebRtcOpusStateSize; i++) { - buffer32[i] = state[i]; - state[i] = samples_in[length - kWebRtcOpusStateSize + i]; - } - for (i = 0; i < length; i++) { - buffer32[kWebRtcOpusStateSize + i] = samples_in[i]; - } - /* Resampling 3 samples to 2. Function divides the input in |blocks| number - * of 3-sample groups, and output is |blocks| number of 2-sample groups. - * When this is removed, the compensation in WebRtcOpus_DurationEst should be - * removed too. */ - blocks = length / 3; - WebRtcSpl_Resample48khzTo32khz(buffer32, buffer32, blocks); - output_samples = (int16_t) (blocks * 2); - WebRtcSpl_VectorBitShiftW32ToW16(samples_out, output_samples, buffer32, 15); - - return output_samples; -} +static int DecodeFec(OpusDecoder* inst, const uint8_t* encoded, + int16_t encoded_bytes, int frame_size, + int16_t* decoded, int16_t* audio_type) { + int res = opus_decode( + inst, encoded, encoded_bytes, (opus_int16*)decoded, frame_size, 1); -static int WebRtcOpus_DeInterleaveResample(OpusDecInst* inst, int16_t* input, - int sample_pairs, int16_t* output) { - int i; - int16_t buffer_left[kWebRtcOpusMaxFrameSizePerChannel]; - int16_t buffer_right[kWebRtcOpusMaxFrameSizePerChannel]; - int16_t buffer_out[kWebRtcOpusMaxFrameSizePerChannel32kHz]; - int resampled_samples; - - /* De-interleave the signal in left and right channel. */ - for (i = 0; i < sample_pairs; i++) { - /* Take every second sample, starting at the first sample. */ - buffer_left[i] = input[i * 2]; - buffer_right[i] = input[i * 2 + 1]; - } - - /* Resample from 48 kHz to 32 kHz for left channel. */ - resampled_samples = WebRtcOpus_Resample48to32( - buffer_left, sample_pairs, inst->state_48_32_left, buffer_out); - - /* Add samples interleaved to output vector. */ - for (i = 0; i < resampled_samples; i++) { - output[i * 2] = buffer_out[i]; - } - - /* Resample from 48 kHz to 32 kHz for right channel. */ - resampled_samples = WebRtcOpus_Resample48to32( - buffer_right, sample_pairs, inst->state_48_32_right, buffer_out); + /* TODO(tlegrand): set to DTX for zero-length packets? */ + *audio_type = 0; - /* Add samples interleaved to output vector. */ - for (i = 0; i < resampled_samples; i++) { - output[i * 2 + 1] = buffer_out[i]; + if (res > 0) { + return res; } - - return resampled_samples; + return -1; } int16_t WebRtcOpus_DecodeNew(OpusDecInst* inst, const uint8_t* encoded, int16_t encoded_bytes, int16_t* decoded, int16_t* audio_type) { - /* |buffer| is big enough for 120 ms (the largest Opus packet size) of stereo - * audio at 48 kHz. */ - int16_t buffer[kWebRtcOpusMaxFrameSize]; - int16_t* coded = (int16_t*)encoded; - int decoded_samples; - int resampled_samples; - - /* If mono case, just do a regular call to the decoder. - * If stereo, we need to de-interleave the stereo output into blocks with - * left and right channel. Each block is resampled to 32 kHz, and then - * interleaved again. */ - - /* Decode to a temporary buffer. */ - decoded_samples = DecodeNative(inst->decoder_left, coded, encoded_bytes, - kWebRtcOpusMaxFrameSizePerChannel, - buffer, audio_type); + int decoded_samples = DecodeNative(inst->decoder_left, + encoded, + encoded_bytes, + kWebRtcOpusMaxFrameSizePerChannel, + decoded, + audio_type); if (decoded_samples < 0) { return -1; } - if (inst->channels == 2) { - /* De-interleave and resample. */ - resampled_samples = WebRtcOpus_DeInterleaveResample(inst, - buffer, - decoded_samples, - decoded); - } else { - /* Resample from 48 kHz to 32 kHz. Filter state memory for left channel is - * used for mono signals. */ - resampled_samples = WebRtcOpus_Resample48to32(buffer, - decoded_samples, - inst->state_48_32_left, - decoded); - } - /* Update decoded sample memory, to be used by the PLC in case of losses. */ inst->prev_decoded_samples = decoded_samples; - return resampled_samples; + return decoded_samples; } -int16_t WebRtcOpus_Decode(OpusDecInst* inst, const int16_t* encoded, +int16_t WebRtcOpus_Decode(OpusDecInst* inst, const uint8_t* encoded, int16_t encoded_bytes, int16_t* decoded, int16_t* audio_type) { - /* |buffer16| is big enough for 120 ms (the largestOpus packet size) of - * stereo audio at 48 kHz. */ - int16_t buffer16[kWebRtcOpusMaxFrameSize]; int decoded_samples; - int16_t output_samples; int i; /* If mono case, just do a regular call to the decoder. @@ -346,120 +290,82 @@ * This is to make stereo work with the current setup of NetEQ, which * requires two calls to the decoder to produce stereo. */ - /* Decode to a temporary buffer. */ decoded_samples = DecodeNative(inst->decoder_left, encoded, encoded_bytes, - kWebRtcOpusMaxFrameSizePerChannel, buffer16, + kWebRtcOpusMaxFrameSizePerChannel, decoded, audio_type); if (decoded_samples < 0) { return -1; } if (inst->channels == 2) { /* The parameter |decoded_samples| holds the number of samples pairs, in - * case of stereo. Number of samples in |buffer16| equals |decoded_samples| + * case of stereo. Number of samples in |decoded| equals |decoded_samples| * times 2. */ for (i = 0; i < decoded_samples; i++) { /* Take every second sample, starting at the first sample. This gives * the left channel. */ - buffer16[i] = buffer16[i * 2]; + decoded[i] = decoded[i * 2]; } } - /* Resample from 48 kHz to 32 kHz. */ - output_samples = WebRtcOpus_Resample48to32(buffer16, decoded_samples, - inst->state_48_32_left, decoded); - /* Update decoded sample memory, to be used by the PLC in case of losses. */ inst->prev_decoded_samples = decoded_samples; - return output_samples; + return decoded_samples; } -int16_t WebRtcOpus_DecodeSlave(OpusDecInst* inst, const int16_t* encoded, +int16_t WebRtcOpus_DecodeSlave(OpusDecInst* inst, const uint8_t* encoded, int16_t encoded_bytes, int16_t* decoded, int16_t* audio_type) { - /* |buffer16| is big enough for 120 ms (the largestOpus packet size) of - * stereo audio at 48 kHz. */ - int16_t buffer16[kWebRtcOpusMaxFrameSize]; int decoded_samples; - int16_t output_samples; int i; - /* Decode to a temporary buffer. */ decoded_samples = DecodeNative(inst->decoder_right, encoded, encoded_bytes, - kWebRtcOpusMaxFrameSizePerChannel, buffer16, + kWebRtcOpusMaxFrameSizePerChannel, decoded, audio_type); if (decoded_samples < 0) { return -1; } if (inst->channels == 2) { /* The parameter |decoded_samples| holds the number of samples pairs, in - * case of stereo. Number of samples in |buffer16| equals |decoded_samples| + * case of stereo. Number of samples in |decoded| equals |decoded_samples| * times 2. */ for (i = 0; i < decoded_samples; i++) { /* Take every second sample, starting at the second sample. This gives * the right channel. */ - buffer16[i] = buffer16[i * 2 + 1]; + decoded[i] = decoded[i * 2 + 1]; } } else { /* Decode slave should never be called for mono packets. */ return -1; } - /* Resample from 48 kHz to 32 kHz. */ - output_samples = WebRtcOpus_Resample48to32(buffer16, decoded_samples, - inst->state_48_32_right, decoded); - return output_samples; + return decoded_samples; } int16_t WebRtcOpus_DecodePlc(OpusDecInst* inst, int16_t* decoded, int16_t number_of_lost_frames) { - int16_t buffer[kWebRtcOpusMaxFrameSize]; int16_t audio_type = 0; int decoded_samples; - int resampled_samples; int plc_samples; - /* If mono case, just do a regular call to the plc function, before - * resampling. - * If stereo, we need to de-interleave the stereo output into blocks with - * left and right channel. Each block is resampled to 32 kHz, and then - * interleaved again. */ - - /* Decode to a temporary buffer. The number of samples we ask for is - * |number_of_lost_frames| times |prev_decoded_samples_|. Limit the number - * of samples to maximum |kWebRtcOpusMaxFrameSizePerChannel|. */ + /* The number of samples we ask for is |number_of_lost_frames| times + * |prev_decoded_samples_|. Limit the number of samples to maximum + * |kWebRtcOpusMaxFrameSizePerChannel|. */ plc_samples = number_of_lost_frames * inst->prev_decoded_samples; plc_samples = (plc_samples <= kWebRtcOpusMaxFrameSizePerChannel) ? plc_samples : kWebRtcOpusMaxFrameSizePerChannel; decoded_samples = DecodeNative(inst->decoder_left, NULL, 0, plc_samples, - buffer, &audio_type); + decoded, &audio_type); if (decoded_samples < 0) { return -1; } - if (inst->channels == 2) { - /* De-interleave and resample. */ - resampled_samples = WebRtcOpus_DeInterleaveResample(inst, - buffer, - decoded_samples, - decoded); - } else { - /* Resample from 48 kHz to 32 kHz. Filter state memory for left channel is - * used for mono signals. */ - resampled_samples = WebRtcOpus_Resample48to32(buffer, - decoded_samples, - inst->state_48_32_left, - decoded); - } - - return resampled_samples; + return decoded_samples; } int16_t WebRtcOpus_DecodePlcMaster(OpusDecInst* inst, int16_t* decoded, int16_t number_of_lost_frames) { - int16_t buffer[kWebRtcOpusMaxFrameSize]; int decoded_samples; - int resampled_samples; int16_t audio_type = 0; int plc_samples; int i; @@ -470,42 +376,35 @@ * output. This is to make stereo work with the current setup of NetEQ, which * requires two calls to the decoder to produce stereo. */ - /* Decode to a temporary buffer. The number of samples we ask for is - * |number_of_lost_frames| times |prev_decoded_samples_|. Limit the number - * of samples to maximum |kWebRtcOpusMaxFrameSizePerChannel|. */ + /* The number of samples we ask for is |number_of_lost_frames| times + * |prev_decoded_samples_|. Limit the number of samples to maximum + * |kWebRtcOpusMaxFrameSizePerChannel|. */ plc_samples = number_of_lost_frames * inst->prev_decoded_samples; plc_samples = (plc_samples <= kWebRtcOpusMaxFrameSizePerChannel) ? plc_samples : kWebRtcOpusMaxFrameSizePerChannel; decoded_samples = DecodeNative(inst->decoder_left, NULL, 0, plc_samples, - buffer, &audio_type); + decoded, &audio_type); if (decoded_samples < 0) { return -1; } if (inst->channels == 2) { /* The parameter |decoded_samples| holds the number of sample pairs, in - * case of stereo. The original number of samples in |buffer| equals + * case of stereo. The original number of samples in |decoded| equals * |decoded_samples| times 2. */ for (i = 0; i < decoded_samples; i++) { /* Take every second sample, starting at the first sample. This gives * the left channel. */ - buffer[i] = buffer[i * 2]; + decoded[i] = decoded[i * 2]; } } - /* Resample from 48 kHz to 32 kHz for left channel. */ - resampled_samples = WebRtcOpus_Resample48to32(buffer, - decoded_samples, - inst->state_48_32_left, - decoded); - return resampled_samples; + return decoded_samples; } int16_t WebRtcOpus_DecodePlcSlave(OpusDecInst* inst, int16_t* decoded, int16_t number_of_lost_frames) { - int16_t buffer[kWebRtcOpusMaxFrameSize]; int decoded_samples; - int resampled_samples; int16_t audio_type = 0; int plc_samples; int i; @@ -516,33 +415,49 @@ return -1; } - /* Decode to a temporary buffer. The number of samples we ask for is - * |number_of_lost_frames| times |prev_decoded_samples_|. Limit the number - * of samples to maximum |kWebRtcOpusMaxFrameSizePerChannel|. */ + /* The number of samples we ask for is |number_of_lost_frames| times + * |prev_decoded_samples_|. Limit the number of samples to maximum + * |kWebRtcOpusMaxFrameSizePerChannel|. */ plc_samples = number_of_lost_frames * inst->prev_decoded_samples; plc_samples = (plc_samples <= kWebRtcOpusMaxFrameSizePerChannel) ? plc_samples : kWebRtcOpusMaxFrameSizePerChannel; decoded_samples = DecodeNative(inst->decoder_right, NULL, 0, plc_samples, - buffer, &audio_type); + decoded, &audio_type); if (decoded_samples < 0) { return -1; } /* The parameter |decoded_samples| holds the number of sample pairs, - * The original number of samples in |buffer| equals |decoded_samples| + * The original number of samples in |decoded| equals |decoded_samples| * times 2. */ for (i = 0; i < decoded_samples; i++) { /* Take every second sample, starting at the second sample. This gives * the right channel. */ - buffer[i] = buffer[i * 2 + 1]; + decoded[i] = decoded[i * 2 + 1]; } - /* Resample from 48 kHz to 32 kHz for left channel. */ - resampled_samples = WebRtcOpus_Resample48to32(buffer, - decoded_samples, - inst->state_48_32_right, - decoded); - return resampled_samples; + return decoded_samples; +} + +int16_t WebRtcOpus_DecodeFec(OpusDecInst* inst, const uint8_t* encoded, + int16_t encoded_bytes, int16_t* decoded, + int16_t* audio_type) { + int decoded_samples; + int fec_samples; + + if (WebRtcOpus_PacketHasFec(encoded, encoded_bytes) != 1) { + return 0; + } + + fec_samples = opus_packet_get_samples_per_frame(encoded, 48000); + + decoded_samples = DecodeFec(inst->decoder_left, encoded, encoded_bytes, + fec_samples, decoded, audio_type); + if (decoded_samples < 0) { + return -1; + } + + return decoded_samples; } int WebRtcOpus_DurationEst(OpusDecInst* inst, @@ -559,9 +474,77 @@ /* Invalid payload duration. */ return 0; } - /* Compensate for the down-sampling from 48 kHz to 32 kHz. - * This should be removed when the resampling in WebRtcOpus_Decode is - * removed. */ - samples = samples * 2 / 3; return samples; } + +int WebRtcOpus_FecDurationEst(const uint8_t* payload, + int payload_length_bytes) { + int samples; + if (WebRtcOpus_PacketHasFec(payload, payload_length_bytes) != 1) { + return 0; + } + + samples = opus_packet_get_samples_per_frame(payload, 48000); + if (samples < 480 || samples > 5760) { + /* Invalid payload duration. */ + return 0; + } + return samples; +} + +int WebRtcOpus_PacketHasFec(const uint8_t* payload, + int payload_length_bytes) { + int frames, channels, payload_length_ms; + int n; + opus_int16 frame_sizes[48]; + const unsigned char *frame_data[48]; + + if (payload == NULL || payload_length_bytes <= 0) + return 0; + + /* In CELT_ONLY mode, packets should not have FEC. */ + if (payload[0] & 0x80) + return 0; + + payload_length_ms = opus_packet_get_samples_per_frame(payload, 48000) / 48; + if (10 > payload_length_ms) + payload_length_ms = 10; + + channels = opus_packet_get_nb_channels(payload); + + switch (payload_length_ms) { + case 10: + case 20: { + frames = 1; + break; + } + case 40: { + frames = 2; + break; + } + case 60: { + frames = 3; + break; + } + default: { + return 0; // It is actually even an invalid packet. + } + } + + /* The following is to parse the LBRR flags. */ + if (opus_packet_parse(payload, payload_length_bytes, NULL, frame_data, + frame_sizes, NULL) < 0) { + return 0; + } + + if (frame_sizes[0] <= 1) { + return 0; + } + + for (n = 0; n < channels; n++) { + if (frame_data[0][0] & (0x80 >> ((n + 1) * (frames + 1) - 1))) + return 1; + } + + return 0; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_speed_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_speed_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_speed_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_speed_test.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" +#include "webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_test.h" + +using ::std::string; + +namespace webrtc { + +static const int kOpusBlockDurationMs = 20; +static const int kOpusSamplingKhz = 48; + +class OpusSpeedTest : public AudioCodecSpeedTest { + protected: + OpusSpeedTest(); + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + virtual float EncodeABlock(int16_t* in_data, uint8_t* bit_stream, + int max_bytes, int* encoded_bytes); + virtual float DecodeABlock(const uint8_t* bit_stream, int encoded_bytes, + int16_t* out_data); + WebRtcOpusEncInst* opus_encoder_; + WebRtcOpusDecInst* opus_decoder_; +}; + +OpusSpeedTest::OpusSpeedTest() + : AudioCodecSpeedTest(kOpusBlockDurationMs, + kOpusSamplingKhz, + kOpusSamplingKhz), + opus_encoder_(NULL), + opus_decoder_(NULL) { +} + +void OpusSpeedTest::SetUp() { + AudioCodecSpeedTest::SetUp(); + /* Create encoder memory. */ + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, channels_)); + EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_decoder_, channels_)); + /* Set bitrate. */ + EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, bit_rate_)); +} + +void OpusSpeedTest::TearDown() { + AudioCodecSpeedTest::TearDown(); + /* Free memory. */ + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); + EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_decoder_)); +} + +float OpusSpeedTest::EncodeABlock(int16_t* in_data, uint8_t* bit_stream, + int max_bytes, int* encoded_bytes) { + clock_t clocks = clock(); + int value = WebRtcOpus_Encode(opus_encoder_, in_data, + input_length_sample_, max_bytes, + bit_stream); + clocks = clock() - clocks; + EXPECT_GT(value, 0); + *encoded_bytes = value; + return 1000.0 * clocks / CLOCKS_PER_SEC; +} + +float OpusSpeedTest::DecodeABlock(const uint8_t* bit_stream, + int encoded_bytes, int16_t* out_data) { + int value; + int16_t audio_type; + clock_t clocks = clock(); + value = WebRtcOpus_DecodeNew(opus_decoder_, bit_stream, encoded_bytes, + out_data, &audio_type); + clocks = clock() - clocks; + EXPECT_EQ(output_length_sample_, value); + return 1000.0 * clocks / CLOCKS_PER_SEC; +} + +#define ADD_TEST(complexity) \ +TEST_P(OpusSpeedTest, OpusSetComplexityTest##complexity) { \ + /* Test audio length in second. */ \ + size_t kDurationSec = 400; \ + /* Set complexity. */ \ + printf("Setting complexity to %d ...\n", complexity); \ + EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_encoder_, complexity)); \ + EncodeDecode(kDurationSec); \ +} + +ADD_TEST(10); +ADD_TEST(9); +ADD_TEST(8); +ADD_TEST(7); +ADD_TEST(6); +ADD_TEST(5); +ADD_TEST(4); +ADD_TEST(3); +ADD_TEST(2); +ADD_TEST(1); +ADD_TEST(0); + +// List all test cases: (channel, bit rat, filename, extension). +const coding_param param_set[] = + {::std::tr1::make_tuple(1, 64000, + string("audio_coding/speech_mono_32_48kHz"), + string("pcm"), true), + ::std::tr1::make_tuple(1, 32000, + string("audio_coding/speech_mono_32_48kHz"), + string("pcm"), true), + ::std::tr1::make_tuple(2, 64000, + string("audio_coding/music_stereo_48kHz"), + string("pcm"), true)}; + +INSTANTIATE_TEST_CASE_P(AllTest, OpusSpeedTest, + ::testing::ValuesIn(param_set)); + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -11,23 +11,27 @@ #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" +#include "webrtc/modules/audio_coding/codecs/opus/opus_inst.h" #include "webrtc/test/testsupport/fileutils.h" -struct WebRtcOpusEncInst; -struct WebRtcOpusDecInst; - namespace webrtc { // Number of samples in a 60 ms stereo frame, sampled at 48 kHz. -const int kOpusNumberOfSamples = 480 * 6 * 2; +const int kOpusMaxFrameSamples = 48 * 60 * 2; // Maximum number of bytes in output bitstream. const size_t kMaxBytes = 1000; +// Number of samples-per-channel in a 20 ms frame, sampled at 48 kHz. +const int kOpus20msFrameSamples = 48 * 20; +// Number of samples-per-channel in a 10 ms frame, sampled at 48 kHz. +const int kOpus10msFrameSamples = 48 * 10; class OpusTest : public ::testing::Test { protected: OpusTest(); virtual void SetUp(); + void TestSetMaxPlaybackRate(opus_int32 expect, int32_t set); + WebRtcOpusEncInst* opus_mono_encoder_; WebRtcOpusEncInst* opus_stereo_encoder_; WebRtcOpusDecInst* opus_mono_decoder_; @@ -35,8 +39,8 @@ WebRtcOpusDecInst* opus_stereo_decoder_; WebRtcOpusDecInst* opus_stereo_decoder_new_; - int16_t speech_data_[kOpusNumberOfSamples]; - int16_t output_data_[kOpusNumberOfSamples]; + int16_t speech_data_[kOpusMaxFrameSamples]; + int16_t output_data_[kOpusMaxFrameSamples]; uint8_t bitstream_[kMaxBytes]; }; @@ -50,21 +54,32 @@ } void OpusTest::SetUp() { - // Read some samples from a speech file, to be used in the encode test. - // In this test we do not care that the sampling frequency of the file is - // really 32000 Hz. We pretend that it is 48000 Hz. FILE* input_file; const std::string file_name = - webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); + webrtc::test::ResourcePath("audio_coding/speech_mono_32_48kHz", "pcm"); input_file = fopen(file_name.c_str(), "rb"); ASSERT_TRUE(input_file != NULL); - ASSERT_EQ(kOpusNumberOfSamples, + ASSERT_EQ(kOpusMaxFrameSamples, static_cast(fread(speech_data_, sizeof(int16_t), - kOpusNumberOfSamples, input_file))); + kOpusMaxFrameSamples, input_file))); fclose(input_file); input_file = NULL; } +void OpusTest::TestSetMaxPlaybackRate(opus_int32 expect, int32_t set) { + opus_int32 bandwidth; + // Test mono encoder. + EXPECT_EQ(0, WebRtcOpus_SetMaxPlaybackRate(opus_mono_encoder_, set)); + opus_encoder_ctl(opus_mono_encoder_->encoder, + OPUS_GET_MAX_BANDWIDTH(&bandwidth)); + EXPECT_EQ(expect, bandwidth); + // Test stereo encoder. + EXPECT_EQ(0, WebRtcOpus_SetMaxPlaybackRate(opus_stereo_encoder_, set)); + opus_encoder_ctl(opus_stereo_encoder_->encoder, + OPUS_GET_MAX_BANDWIDTH(&bandwidth)); + EXPECT_EQ(expect, bandwidth); +} + // Test failing Create. TEST_F(OpusTest, OpusCreateFail) { // Test to see that an invalid pointer is caught. @@ -114,21 +129,23 @@ // Encode & decode. int16_t encoded_bytes; int16_t audio_type; - int16_t output_data_decode_new[kOpusNumberOfSamples]; - int16_t output_data_decode[kOpusNumberOfSamples]; - int16_t* coded = reinterpret_cast(bitstream_); - encoded_bytes = WebRtcOpus_Encode(opus_mono_encoder_, speech_data_, 960, - kMaxBytes, bitstream_); - EXPECT_EQ(640, WebRtcOpus_DecodeNew(opus_mono_decoder_new_, bitstream_, - encoded_bytes, output_data_decode_new, - &audio_type)); - EXPECT_EQ(640, WebRtcOpus_Decode(opus_mono_decoder_, coded, - encoded_bytes, output_data_decode, - &audio_type)); + int16_t output_data_decode_new[kOpusMaxFrameSamples]; + int16_t output_data_decode[kOpusMaxFrameSamples]; + encoded_bytes = WebRtcOpus_Encode(opus_mono_encoder_, speech_data_, + kOpus20msFrameSamples, kMaxBytes, + bitstream_); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_DecodeNew(opus_mono_decoder_new_, bitstream_, + encoded_bytes, output_data_decode_new, + &audio_type)); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_Decode(opus_mono_decoder_, bitstream_, + encoded_bytes, output_data_decode, + &audio_type)); // Data in |output_data_decode_new| should be the same as in // |output_data_decode|. - for (int i = 0; i < 640; i++) { + for (int i = 0; i < kOpus20msFrameSamples; i++) { EXPECT_EQ(output_data_decode_new[i], output_data_decode[i]); } @@ -154,26 +171,29 @@ // Encode & decode. int16_t encoded_bytes; int16_t audio_type; - int16_t output_data_decode_new[kOpusNumberOfSamples]; - int16_t output_data_decode[kOpusNumberOfSamples]; - int16_t output_data_decode_slave[kOpusNumberOfSamples]; - int16_t* coded = reinterpret_cast(bitstream_); - encoded_bytes = WebRtcOpus_Encode(opus_stereo_encoder_, speech_data_, 960, - kMaxBytes, bitstream_); - EXPECT_EQ(640, WebRtcOpus_DecodeNew(opus_stereo_decoder_new_, bitstream_, - encoded_bytes, output_data_decode_new, - &audio_type)); - EXPECT_EQ(640, WebRtcOpus_Decode(opus_stereo_decoder_, coded, - encoded_bytes, output_data_decode, + int16_t output_data_decode_new[kOpusMaxFrameSamples]; + int16_t output_data_decode[kOpusMaxFrameSamples]; + int16_t output_data_decode_slave[kOpusMaxFrameSamples]; + encoded_bytes = WebRtcOpus_Encode(opus_stereo_encoder_, speech_data_, + kOpus20msFrameSamples, kMaxBytes, + bitstream_); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_DecodeNew(opus_stereo_decoder_new_, bitstream_, + encoded_bytes, output_data_decode_new, + &audio_type)); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_Decode(opus_stereo_decoder_, bitstream_, + encoded_bytes, output_data_decode, + &audio_type)); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_DecodeSlave(opus_stereo_decoder_, bitstream_, + encoded_bytes, output_data_decode_slave, &audio_type)); - EXPECT_EQ(640, WebRtcOpus_DecodeSlave(opus_stereo_decoder_, coded, - encoded_bytes, output_data_decode_slave, - &audio_type)); // Data in |output_data_decode_new| should be the same as in // |output_data_decode| and |output_data_decode_slave| interleaved to a // stereo signal. - for (int i = 0; i < 640; i++) { + for (int i = 0; i < kOpus20msFrameSamples; i++) { EXPECT_EQ(output_data_decode_new[i * 2], output_data_decode[i]); EXPECT_EQ(output_data_decode_new[i * 2 + 1], output_data_decode_slave[i]); } @@ -202,6 +222,27 @@ EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_stereo_encoder_)); } +TEST_F(OpusTest, OpusSetComplexity) { + // Test without creating encoder memory. + EXPECT_EQ(-1, WebRtcOpus_SetComplexity(opus_mono_encoder_, 9)); + EXPECT_EQ(-1, WebRtcOpus_SetComplexity(opus_stereo_encoder_, 9)); + + // Create encoder memory, try with different complexities. + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 1)); + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2)); + + EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_mono_encoder_, 0)); + EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_stereo_encoder_, 0)); + EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_mono_encoder_, 10)); + EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_stereo_encoder_, 10)); + EXPECT_EQ(-1, WebRtcOpus_SetComplexity(opus_mono_encoder_, 11)); + EXPECT_EQ(-1, WebRtcOpus_SetComplexity(opus_stereo_encoder_, 11)); + + // Free memory. + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_mono_encoder_)); + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_stereo_encoder_)); +} + // Encode and decode one frame (stereo), initialize the decoder and // decode once more. TEST_F(OpusTest, OpusDecodeInit) { @@ -213,26 +254,29 @@ // Encode & decode. int16_t encoded_bytes; int16_t audio_type; - int16_t output_data_decode_new[kOpusNumberOfSamples]; - int16_t output_data_decode[kOpusNumberOfSamples]; - int16_t output_data_decode_slave[kOpusNumberOfSamples]; - int16_t* coded = reinterpret_cast(bitstream_); - encoded_bytes = WebRtcOpus_Encode(opus_stereo_encoder_, speech_data_, 960, - kMaxBytes, bitstream_); - EXPECT_EQ(640, WebRtcOpus_DecodeNew(opus_stereo_decoder_new_, bitstream_, - encoded_bytes, output_data_decode_new, - &audio_type)); - EXPECT_EQ(640, WebRtcOpus_Decode(opus_stereo_decoder_, coded, - encoded_bytes, output_data_decode, + int16_t output_data_decode_new[kOpusMaxFrameSamples]; + int16_t output_data_decode[kOpusMaxFrameSamples]; + int16_t output_data_decode_slave[kOpusMaxFrameSamples]; + encoded_bytes = WebRtcOpus_Encode(opus_stereo_encoder_, speech_data_, + kOpus20msFrameSamples, kMaxBytes, + bitstream_); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_DecodeNew(opus_stereo_decoder_new_, bitstream_, + encoded_bytes, output_data_decode_new, + &audio_type)); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_Decode(opus_stereo_decoder_, bitstream_, + encoded_bytes, output_data_decode, + &audio_type)); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_DecodeSlave(opus_stereo_decoder_, bitstream_, + encoded_bytes, output_data_decode_slave, &audio_type)); - EXPECT_EQ(640, WebRtcOpus_DecodeSlave(opus_stereo_decoder_, coded, - encoded_bytes, output_data_decode_slave, - &audio_type)); // Data in |output_data_decode_new| should be the same as in // |output_data_decode| and |output_data_decode_slave| interleaved to a // stereo signal. - for (int i = 0; i < 640; i++) { + for (int i = 0; i < kOpus20msFrameSamples; i++) { EXPECT_EQ(output_data_decode_new[i * 2], output_data_decode[i]); EXPECT_EQ(output_data_decode_new[i * 2 + 1], output_data_decode_slave[i]); } @@ -241,20 +285,23 @@ EXPECT_EQ(0, WebRtcOpus_DecoderInit(opus_stereo_decoder_)); EXPECT_EQ(0, WebRtcOpus_DecoderInitSlave(opus_stereo_decoder_)); - EXPECT_EQ(640, WebRtcOpus_DecodeNew(opus_stereo_decoder_new_, bitstream_, - encoded_bytes, output_data_decode_new, - &audio_type)); - EXPECT_EQ(640, WebRtcOpus_Decode(opus_stereo_decoder_, coded, - encoded_bytes, output_data_decode, + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_DecodeNew(opus_stereo_decoder_new_, bitstream_, + encoded_bytes, output_data_decode_new, + &audio_type)); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_Decode(opus_stereo_decoder_, bitstream_, + encoded_bytes, output_data_decode, + &audio_type)); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_DecodeSlave(opus_stereo_decoder_, bitstream_, + encoded_bytes, output_data_decode_slave, &audio_type)); - EXPECT_EQ(640, WebRtcOpus_DecodeSlave(opus_stereo_decoder_, coded, - encoded_bytes, output_data_decode_slave, - &audio_type)); // Data in |output_data_decode_new| should be the same as in // |output_data_decode| and |output_data_decode_slave| interleaved to a // stereo signal. - for (int i = 0; i < 640; i++) { + for (int i = 0; i < kOpus20msFrameSamples; i++) { EXPECT_EQ(output_data_decode_new[i * 2], output_data_decode[i]); EXPECT_EQ(output_data_decode_new[i * 2 + 1], output_data_decode_slave[i]); } @@ -265,6 +312,71 @@ EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_stereo_decoder_new_)); } +TEST_F(OpusTest, OpusEnableDisableFec) { + // Test without creating encoder memory. + EXPECT_EQ(-1, WebRtcOpus_EnableFec(opus_mono_encoder_)); + EXPECT_EQ(-1, WebRtcOpus_DisableFec(opus_stereo_encoder_)); + + // Create encoder memory, try with different bitrates. + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 1)); + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2)); + + EXPECT_EQ(0, WebRtcOpus_EnableFec(opus_mono_encoder_)); + EXPECT_EQ(0, WebRtcOpus_EnableFec(opus_stereo_encoder_)); + EXPECT_EQ(0, WebRtcOpus_DisableFec(opus_mono_encoder_)); + EXPECT_EQ(0, WebRtcOpus_DisableFec(opus_stereo_encoder_)); + + // Free memory. + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_mono_encoder_)); + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_stereo_encoder_)); +} + +TEST_F(OpusTest, OpusSetPacketLossRate) { + // Test without creating encoder memory. + EXPECT_EQ(-1, WebRtcOpus_SetPacketLossRate(opus_mono_encoder_, 50)); + EXPECT_EQ(-1, WebRtcOpus_SetPacketLossRate(opus_stereo_encoder_, 50)); + + // Create encoder memory, try with different bitrates. + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 1)); + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2)); + + EXPECT_EQ(0, WebRtcOpus_SetPacketLossRate(opus_mono_encoder_, 50)); + EXPECT_EQ(0, WebRtcOpus_SetPacketLossRate(opus_stereo_encoder_, 50)); + EXPECT_EQ(-1, WebRtcOpus_SetPacketLossRate(opus_mono_encoder_, -1)); + EXPECT_EQ(-1, WebRtcOpus_SetPacketLossRate(opus_stereo_encoder_, -1)); + EXPECT_EQ(-1, WebRtcOpus_SetPacketLossRate(opus_mono_encoder_, 101)); + EXPECT_EQ(-1, WebRtcOpus_SetPacketLossRate(opus_stereo_encoder_, 101)); + + // Free memory. + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_mono_encoder_)); + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_stereo_encoder_)); +} + +TEST_F(OpusTest, OpusSetMaxPlaybackRate) { + // Test without creating encoder memory. + EXPECT_EQ(-1, WebRtcOpus_SetMaxPlaybackRate(opus_mono_encoder_, 20000)); + EXPECT_EQ(-1, WebRtcOpus_SetMaxPlaybackRate(opus_stereo_encoder_, 20000)); + + // Create encoder memory, try with different bitrates. + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 1)); + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2)); + + TestSetMaxPlaybackRate(OPUS_BANDWIDTH_FULLBAND, 48000); + TestSetMaxPlaybackRate(OPUS_BANDWIDTH_FULLBAND, 24001); + TestSetMaxPlaybackRate(OPUS_BANDWIDTH_SUPERWIDEBAND, 24000); + TestSetMaxPlaybackRate(OPUS_BANDWIDTH_SUPERWIDEBAND, 16001); + TestSetMaxPlaybackRate(OPUS_BANDWIDTH_WIDEBAND, 16000); + TestSetMaxPlaybackRate(OPUS_BANDWIDTH_WIDEBAND, 12001); + TestSetMaxPlaybackRate(OPUS_BANDWIDTH_MEDIUMBAND, 12000); + TestSetMaxPlaybackRate(OPUS_BANDWIDTH_MEDIUMBAND, 8001); + TestSetMaxPlaybackRate(OPUS_BANDWIDTH_NARROWBAND, 8000); + TestSetMaxPlaybackRate(OPUS_BANDWIDTH_NARROWBAND, 4000); + + // Free memory. + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_mono_encoder_)); + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_stereo_encoder_)); +} + // PLC in mono mode. TEST_F(OpusTest, OpusDecodePlcMono) { // Create encoder memory. @@ -282,27 +394,30 @@ // Encode & decode. int16_t encoded_bytes; int16_t audio_type; - int16_t output_data_decode_new[kOpusNumberOfSamples]; - int16_t output_data_decode[kOpusNumberOfSamples]; - int16_t* coded = reinterpret_cast(bitstream_); - encoded_bytes = WebRtcOpus_Encode(opus_mono_encoder_, speech_data_, 960, - kMaxBytes, bitstream_); - EXPECT_EQ(640, WebRtcOpus_DecodeNew(opus_mono_decoder_new_, bitstream_, - encoded_bytes, output_data_decode_new, - &audio_type)); - EXPECT_EQ(640, WebRtcOpus_Decode(opus_mono_decoder_, coded, - encoded_bytes, output_data_decode, - &audio_type)); + int16_t output_data_decode_new[kOpusMaxFrameSamples]; + int16_t output_data_decode[kOpusMaxFrameSamples]; + encoded_bytes = WebRtcOpus_Encode(opus_mono_encoder_, speech_data_, + kOpus20msFrameSamples, kMaxBytes, + bitstream_); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_DecodeNew(opus_mono_decoder_new_, bitstream_, + encoded_bytes, output_data_decode_new, + &audio_type)); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_Decode(opus_mono_decoder_, bitstream_, + encoded_bytes, output_data_decode, + &audio_type)); // Call decoder PLC for both versions of the decoder. - int16_t plc_buffer[kOpusNumberOfSamples]; - int16_t plc_buffer_new[kOpusNumberOfSamples]; - EXPECT_EQ(640, WebRtcOpus_DecodePlcMaster(opus_mono_decoder_, plc_buffer, 1)); - EXPECT_EQ(640, WebRtcOpus_DecodePlc(opus_mono_decoder_new_, - plc_buffer_new, 1)); + int16_t plc_buffer[kOpusMaxFrameSamples]; + int16_t plc_buffer_new[kOpusMaxFrameSamples]; + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_DecodePlcMaster(opus_mono_decoder_, plc_buffer, 1)); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_DecodePlc(opus_mono_decoder_new_, plc_buffer_new, 1)); // Data in |plc_buffer| should be the same as in |plc_buffer_new|. - for (int i = 0; i < 640; i++) { + for (int i = 0; i < kOpus20msFrameSamples; i++) { EXPECT_EQ(plc_buffer[i], plc_buffer_new[i]); } @@ -329,36 +444,41 @@ // Encode & decode. int16_t encoded_bytes; int16_t audio_type; - int16_t output_data_decode_new[kOpusNumberOfSamples]; - int16_t output_data_decode[kOpusNumberOfSamples]; - int16_t output_data_decode_slave[kOpusNumberOfSamples]; - int16_t* coded = reinterpret_cast(bitstream_); - encoded_bytes = WebRtcOpus_Encode(opus_stereo_encoder_, speech_data_, 960, - kMaxBytes, bitstream_); - EXPECT_EQ(640, WebRtcOpus_DecodeNew(opus_stereo_decoder_new_, bitstream_, - encoded_bytes, output_data_decode_new, - &audio_type)); - EXPECT_EQ(640, WebRtcOpus_Decode(opus_stereo_decoder_, coded, - encoded_bytes, output_data_decode, + int16_t output_data_decode_new[kOpusMaxFrameSamples]; + int16_t output_data_decode[kOpusMaxFrameSamples]; + int16_t output_data_decode_slave[kOpusMaxFrameSamples]; + encoded_bytes = WebRtcOpus_Encode(opus_stereo_encoder_, speech_data_, + kOpus20msFrameSamples, kMaxBytes, + bitstream_); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_DecodeNew(opus_stereo_decoder_new_, bitstream_, + encoded_bytes, output_data_decode_new, + &audio_type)); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_Decode(opus_stereo_decoder_, bitstream_, + encoded_bytes, output_data_decode, + &audio_type)); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_DecodeSlave(opus_stereo_decoder_, bitstream_, + encoded_bytes, + output_data_decode_slave, &audio_type)); - EXPECT_EQ(640, WebRtcOpus_DecodeSlave(opus_stereo_decoder_, coded, - encoded_bytes, - output_data_decode_slave, - &audio_type)); // Call decoder PLC for both versions of the decoder. - int16_t plc_buffer_left[kOpusNumberOfSamples]; - int16_t plc_buffer_right[kOpusNumberOfSamples]; - int16_t plc_buffer_new[kOpusNumberOfSamples]; - EXPECT_EQ(640, WebRtcOpus_DecodePlcMaster(opus_stereo_decoder_, - plc_buffer_left, 1)); - EXPECT_EQ(640, WebRtcOpus_DecodePlcSlave(opus_stereo_decoder_, - plc_buffer_right, 1)); - EXPECT_EQ(640, WebRtcOpus_DecodePlc(opus_stereo_decoder_new_, plc_buffer_new, - 1)); + int16_t plc_buffer_left[kOpusMaxFrameSamples]; + int16_t plc_buffer_right[kOpusMaxFrameSamples]; + int16_t plc_buffer_new[kOpusMaxFrameSamples]; + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_DecodePlcMaster(opus_stereo_decoder_, + plc_buffer_left, 1)); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_DecodePlcSlave(opus_stereo_decoder_, + plc_buffer_right, 1)); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_DecodePlc(opus_stereo_decoder_new_, plc_buffer_new, 1)); // Data in |plc_buffer_left| and |plc_buffer_right|should be the same as the // interleaved samples in |plc_buffer_new|. - for (int i = 0, j = 0; i < 640; i++) { + for (int i = 0, j = 0; i < kOpus20msFrameSamples; i++) { EXPECT_EQ(plc_buffer_left[i], plc_buffer_new[j++]); EXPECT_EQ(plc_buffer_right[i], plc_buffer_new[j++]); } @@ -375,21 +495,23 @@ EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2)); EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_stereo_decoder_, 2)); - // Encode with different packet sizes (input 48 kHz, output in 32 kHz). int16_t encoded_bytes; // 10 ms. - encoded_bytes = WebRtcOpus_Encode(opus_stereo_encoder_, speech_data_, 480, - kMaxBytes, bitstream_); - EXPECT_EQ(320, WebRtcOpus_DurationEst(opus_stereo_decoder_, bitstream_, - encoded_bytes)); + encoded_bytes = WebRtcOpus_Encode(opus_stereo_encoder_, speech_data_, + kOpus10msFrameSamples, kMaxBytes, + bitstream_); + EXPECT_EQ(kOpus10msFrameSamples, + WebRtcOpus_DurationEst(opus_stereo_decoder_, bitstream_, + encoded_bytes)); // 20 ms - encoded_bytes = WebRtcOpus_Encode(opus_stereo_encoder_, speech_data_, 960, - kMaxBytes, bitstream_); - EXPECT_EQ(640, WebRtcOpus_DurationEst(opus_stereo_decoder_, bitstream_, - encoded_bytes)); - + encoded_bytes = WebRtcOpus_Encode(opus_stereo_encoder_, speech_data_, + kOpus20msFrameSamples, kMaxBytes, + bitstream_); + EXPECT_EQ(kOpus20msFrameSamples, + WebRtcOpus_DurationEst(opus_stereo_decoder_, bitstream_, + encoded_bytes)); // Free memory. EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_stereo_encoder_)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -1,3 +1,4 @@ tina.legrand@webrtc.org turaj@webrtc.org jan.skoglund@webrtc.org +henrik.lundin@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/pcm16b/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/pcm16b/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/pcm16b/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/pcm16b/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_pcm16b -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := pcm16b.c - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../../../.. - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h 2015-02-03 14:33:34.000000000 +0000 @@ -14,7 +14,7 @@ * Define the fixpoint numeric formats */ -#include "typedefs.h" +#include "webrtc/typedefs.h" #ifdef __cplusplus extern "C" { @@ -26,18 +26,18 @@ * "Encode" a sample vector to 16 bit linear (Encoded standard is big endian) * * Input: - * - speechIn16b : Input speech vector - * - len : Number of samples in speech vector + * - speechIn16b : Input speech vector + * - length_samples : Number of samples in speech vector * * Output: - * - speechOut16b : Encoded data vector (big endian 16 bit) + * - speechOut16b : Encoded data vector (big endian 16 bit) * - * Returned value : Size in bytes of speechOut16b + * Returned value : Size in bytes of speechOut16b */ -int16_t WebRtcPcm16b_EncodeW16(int16_t *speechIn16b, - int16_t len, - int16_t *speechOut16b); +int16_t WebRtcPcm16b_EncodeW16(const int16_t* speechIn16b, + int16_t length_samples, + int16_t* speechOut16b); /**************************************************************************** * WebRtcPcm16b_Encode(...) @@ -64,18 +64,17 @@ * "Decode" a vector to 16 bit linear (Encoded standard is big endian) * * Input: - * - speechIn16b : Encoded data vector (big endian 16 bit) - * - len : Number of bytes in speechIn16b + * - speechIn16b : Encoded data vector (big endian 16 bit) + * - length_bytes : Number of bytes in speechIn16b * * Output: - * - speechOut16b : Decoded speech vector + * - speechOut16b : Decoded speech vector * - * Returned value : Samples in speechOut16b + * Returned value : Samples in speechOut16b */ -int16_t WebRtcPcm16b_DecodeW16(void *inst, - int16_t *speechIn16b, - int16_t len, +int16_t WebRtcPcm16b_DecodeW16(int16_t *speechIn16b, + int16_t length_bytes, int16_t *speechOut16b, int16_t* speechType); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/pcm16b/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/pcm16b/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/pcm16b/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/pcm16b/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/pcm16b/pcm16b.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/pcm16b/pcm16b.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/pcm16b/pcm16b.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/pcm16b/pcm16b.c 2015-02-03 14:33:34.000000000 +0000 @@ -12,8 +12,11 @@ #include "pcm16b.h" #include +#ifdef WEBRTC_ARCH_BIG_ENDIAN +#include +#endif -#include "typedefs.h" +#include "webrtc/typedefs.h" #define HIGHEND 0xFF00 #define LOWEND 0xFF @@ -21,19 +24,19 @@ /* Encoder with int16_t Output */ -int16_t WebRtcPcm16b_EncodeW16(int16_t *speechIn16b, - int16_t len, - int16_t *speechOut16b) +int16_t WebRtcPcm16b_EncodeW16(const int16_t* speechIn16b, + int16_t length_samples, + int16_t* speechOut16b) { #ifdef WEBRTC_ARCH_BIG_ENDIAN - memcpy(speechOut16b, speechIn16b, len * sizeof(int16_t)); + memcpy(speechOut16b, speechIn16b, length_samples * sizeof(int16_t)); #else int i; - for (i=0;i>8)|((((uint16_t)speechIn16b[i])<<8)&0xFF00); } #endif - return(len<<1); + return length_samples << 1; } @@ -58,17 +61,16 @@ /* Decoder with int16_t Input instead of char when the int16_t Encoder is used */ -int16_t WebRtcPcm16b_DecodeW16(void *inst, - int16_t *speechIn16b, - int16_t len, +int16_t WebRtcPcm16b_DecodeW16(int16_t *speechIn16b, + int16_t length_bytes, int16_t *speechOut16b, int16_t* speechType) { #ifdef WEBRTC_ARCH_BIG_ENDIAN - memcpy(speechOut16b, speechIn16b, ((len*sizeof(int16_t)+1)>>1)); + memcpy(speechOut16b, speechIn16b, length_bytes); #else int i; - int samples=len>>1; + int samples = length_bytes >> 1; for (i=0;i>8)|(((uint16_t)(speechIn16b[i]&0xFF))<<8); @@ -77,10 +79,7 @@ *speechType=1; - // Avoid warning. - (void)(inst = NULL); - - return(len>>1); + return length_bytes >> 1; } /* "old" version of the decoder that uses char as input (not used in NetEq any more) */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_test.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_test.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/test/testsupport/fileutils.h" + +using ::std::tr1::get; + +namespace webrtc { + +AudioCodecSpeedTest::AudioCodecSpeedTest(int block_duration_ms, + int input_sampling_khz, + int output_sampling_khz) + : block_duration_ms_(block_duration_ms), + input_sampling_khz_(input_sampling_khz), + output_sampling_khz_(output_sampling_khz), + input_length_sample_(block_duration_ms_ * input_sampling_khz_), + output_length_sample_(block_duration_ms_ * output_sampling_khz_), + data_pointer_(0), + loop_length_samples_(0), + max_bytes_(0), + encoded_bytes_(0), + encoding_time_ms_(0.0), + decoding_time_ms_(0.0), + out_file_(NULL) { +} + +void AudioCodecSpeedTest::SetUp() { + channels_ = get<0>(GetParam()); + bit_rate_ = get<1>(GetParam()); + in_filename_ = test::ResourcePath(get<2>(GetParam()), get<3>(GetParam())); + save_out_data_ = get<4>(GetParam()); + + FILE* fp = fopen(in_filename_.c_str(), "rb"); + assert(fp != NULL); + + // Obtain file size. + fseek(fp, 0, SEEK_END); + loop_length_samples_ = ftell(fp) / sizeof(int16_t); + rewind(fp); + + // Allocate memory to contain the whole file. + in_data_.reset(new int16_t[loop_length_samples_ + + input_length_sample_ * channels_]); + + data_pointer_ = 0; + + // Copy the file into the buffer. + ASSERT_EQ(fread(&in_data_[0], sizeof(int16_t), loop_length_samples_, fp), + loop_length_samples_); + fclose(fp); + + // Add an extra block length of samples to the end of the array, starting + // over again from the beginning of the array. This is done to simplify + // the reading process when reading over the end of the loop. + memcpy(&in_data_[loop_length_samples_], &in_data_[0], + input_length_sample_ * channels_ * sizeof(int16_t)); + + max_bytes_ = input_length_sample_ * channels_ * sizeof(int16_t); + out_data_.reset(new int16_t[output_length_sample_ * channels_]); + bit_stream_.reset(new uint8_t[max_bytes_]); + + if (save_out_data_) { + std::string out_filename = + ::testing::UnitTest::GetInstance()->current_test_info()->name(); + + // Erase '/' + size_t found; + while ((found = out_filename.find('/')) != std::string::npos) + out_filename.replace(found, 1, "_"); + + out_filename = test::OutputPath() + out_filename + ".pcm"; + + out_file_ = fopen(out_filename.c_str(), "wb"); + assert(out_file_ != NULL); + + printf("Output to be saved in %s.\n", out_filename.c_str()); + } +} + +void AudioCodecSpeedTest::TearDown() { + if (save_out_data_) { + fclose(out_file_); + } +} + +void AudioCodecSpeedTest::EncodeDecode(size_t audio_duration_sec) { + size_t time_now_ms = 0; + float time_ms; + + printf("Coding %d kHz-sampled %d-channel audio at %d bps ...\n", + input_sampling_khz_, channels_, bit_rate_); + + while (time_now_ms < audio_duration_sec * 1000) { + // Encode & decode. + time_ms = EncodeABlock(&in_data_[data_pointer_], &bit_stream_[0], + max_bytes_, &encoded_bytes_); + encoding_time_ms_ += time_ms; + time_ms = DecodeABlock(&bit_stream_[0], encoded_bytes_, &out_data_[0]); + decoding_time_ms_ += time_ms; + if (save_out_data_) { + fwrite(&out_data_[0], sizeof(int16_t), + output_length_sample_ * channels_, out_file_); + } + data_pointer_ = (data_pointer_ + input_length_sample_ * channels_) % + loop_length_samples_; + time_now_ms += block_duration_ms_; + } + + printf("Encoding: %.2f%% real time,\nDecoding: %.2f%% real time.\n", + (encoding_time_ms_ / audio_duration_sec) / 10.0, + (decoding_time_ms_ / audio_duration_sec) / 10.0); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_test.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_test.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_test.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_test.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_TOOLS_AUDIO_CODEC_SPEED_TEST_H_ +#define WEBRTC_MODULES_AUDIO_CODING_CODECS_TOOLS_AUDIO_CODEC_SPEED_TEST_H_ + +#include +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Define coding parameter as +// . +typedef std::tr1::tuple coding_param; + +class AudioCodecSpeedTest : public testing::TestWithParam { + protected: + AudioCodecSpeedTest(int block_duration_ms, + int input_sampling_khz, + int output_sampling_khz); + virtual void SetUp(); + virtual void TearDown(); + + // EncodeABlock(...) does the following: + // 1. encodes a block of audio, saved in |in_data|, + // 2. save the bit stream to |bit_stream| of |max_bytes| bytes in size, + // 3. assign |encoded_bytes| with the length of the bit stream (in bytes), + // 4. return the cost of time (in millisecond) spent on actual encoding. + virtual float EncodeABlock(int16_t* in_data, uint8_t* bit_stream, + int max_bytes, int* encoded_bytes) = 0; + + // DecodeABlock(...) does the following: + // 1. decodes the bit stream in |bit_stream| with a length of |encoded_bytes| + // (in bytes), + // 2. save the decoded audio in |out_data|, + // 3. return the cost of time (in millisecond) spent on actual decoding. + virtual float DecodeABlock(const uint8_t* bit_stream, int encoded_bytes, + int16_t* out_data) = 0; + + // Encoding and decode an audio of |audio_duration| (in seconds) and + // record the runtime for encoding and decoding separately. + void EncodeDecode(size_t audio_duration); + + int block_duration_ms_; + int input_sampling_khz_; + int output_sampling_khz_; + + // Number of samples-per-channel in a frame. + int input_length_sample_; + + // Expected output number of samples-per-channel in a frame. + int output_length_sample_; + + scoped_ptr in_data_; + scoped_ptr out_data_; + size_t data_pointer_; + size_t loop_length_samples_; + scoped_ptr bit_stream_; + + // Maximum number of bytes in output bitstream for a frame of audio. + int max_bytes_; + + int encoded_bytes_; + float encoding_time_ms_; + float decoding_time_ms_; + FILE* out_file_; + + int channels_; + + // Bit rate is in bit-per-second. + int bit_rate_; + + std::string in_filename_; + + // Determines whether to save the output to file. + bool save_out_data_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_TOOLS_AUDIO_CODEC_SPEED_TEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_tests.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_tests.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_tests.gypi 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_tests.gypi 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,66 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +{ + 'targets': [ + { + 'target_name': 'audio_codec_speed_tests', + 'type': '<(gtest_target_type)', + 'dependencies': [ + 'audio_processing', + 'iSACFix', + 'webrtc_opus', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + '<(webrtc_root)/test/test.gyp:test_support_main', + ], + 'sources': [ + 'audio_codec_speed_test.h', + 'audio_codec_speed_test.cc', + '<(webrtc_root)/modules/audio_coding/codecs/opus/opus_speed_test.cc', + '<(webrtc_root)/modules/audio_coding/codecs/isac/fix/test/isac_speed_test.cc', + ], + 'conditions': [ + ['OS=="android"', { + 'dependencies': [ + '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code', + ], + }], + ], + }], + 'conditions': [ + ['OS=="android"', { + 'targets': [ + { + 'target_name': 'audio_codec_speed_tests_apk_target', + 'type': 'none', + 'dependencies': [ + '<(apk_tests_path):audio_codec_speed_tests_apk', + ], + }, + ], + }], + ['test_isolation_mode != "noop"', { + 'targets': [ + { + 'target_name': 'audio_codec_speed_tests_run', + 'type': 'none', + 'dependencies': [ + 'audio_codec_speed_tests', + ], + 'includes': [ + '../../../../build/isolate.gypi', + ], + 'sources': [ + 'audio_codec_speed_tests.isolate', + ], + }, + ], + }], + ], +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_tests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_tests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_tests.isolate 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_tests.isolate 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,35 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. +{ + 'conditions': [ + ['OS=="android"', { + 'variables': { + 'files': [ + '<(DEPTH)/resources/', + '<(DEPTH)/data/', + ], + }, + }], + ['OS=="linux" or OS=="mac" or OS=="win"', { + 'variables': { + 'command': [ + '<(DEPTH)/testing/test_env.py', + '<(PRODUCT_DIR)/audio_codec_speed_tests<(EXECUTABLE_SUFFIX)', + ], + 'files': [ + '<(DEPTH)/DEPS', + '<(DEPTH)/resources/audio_coding/music_stereo_48kHz.pcm', + '<(DEPTH)/resources/audio_coding/speech_mono_16kHz.pcm', + '<(DEPTH)/resources/audio_coding/speech_mono_32_48kHz.pcm', + '<(DEPTH)/testing/test_env.py', + '<(PRODUCT_DIR)/audio_codec_speed_tests<(EXECUTABLE_SUFFIX)', + ], + }, + }], + ], +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/tools/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,6 @@ +per-file *.isolate=kjellander@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_amr.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_amr.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_amr.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_amr.cc 2015-02-03 14:33:34.000000000 +0000 @@ -77,8 +77,6 @@ int16_t ACMAMR::SetBitRateSafe(const int32_t /* rate */) { return -1; } -void ACMAMR::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } - int16_t ACMAMR::SetAMREncoderPackingFormat( ACMAMRPackingFormat /* packing_format */) { return -1; @@ -268,14 +266,6 @@ return 0; } -void ACMAMR::InternalDestructEncoderInst(void* ptr_inst) { - // Free the memory where ptr_inst is pointing to - if (ptr_inst != NULL) { - WebRtcAmr_FreeEnc(static_cast(ptr_inst)); - } - return; -} - int16_t ACMAMR::SetAMREncoderPackingFormat(ACMAMRPackingFormat packing_format) { if ((packing_format != AMRBandwidthEfficient) && (packing_format != AMROctetAlligned) && diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_amr.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_amr.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_amr.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_amr.h 2015-02-03 14:33:34.000000000 +0000 @@ -48,8 +48,6 @@ int16_t InternalCreateEncoder(); - void InternalDestructEncoderInst(void* ptr_inst); - int16_t SetBitRateSafe(const int32_t rate); int16_t EnableDTX(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_amrwb.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_amrwb.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_amrwb.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_amrwb.cc 2015-02-03 14:33:34.000000000 +0000 @@ -73,8 +73,6 @@ int16_t ACMAMRwb::SetBitRateSafe(const int32_t /* rate */) { return -1; } -void ACMAMRwb::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } - int16_t ACMAMRwb::SetAMRwbEncoderPackingFormat( ACMAMRPackingFormat /* packing_format */) { return -1; @@ -273,13 +271,6 @@ return 0; } -void ACMAMRwb::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcAmrWb_FreeEnc(static_cast(ptr_inst)); - } - return; -} - int16_t ACMAMRwb::SetAMRwbEncoderPackingFormat( ACMAMRPackingFormat packing_format) { if ((packing_format != AMRBandwidthEfficient) && diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_amrwb.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_amrwb.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_amrwb.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_amrwb.h 2015-02-03 14:33:34.000000000 +0000 @@ -48,8 +48,6 @@ int16_t InternalCreateEncoder(); - void InternalDestructEncoderInst(void* ptr_inst); - int16_t SetBitRateSafe(const int32_t rate); int16_t EnableDTX(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_celt.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_celt.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_celt.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_celt.cc 2015-02-03 14:33:34.000000000 +0000 @@ -57,10 +57,6 @@ return; } -void ACMCELT::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - int16_t ACMCELT::SetBitRateSafe(const int32_t /*rate*/) { return -1; } @@ -159,13 +155,6 @@ } } -void ACMCELT::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcCelt_FreeEnc(static_cast(ptr_inst)); - } - return; -} - int16_t ACMCELT::SetBitRateSafe(const int32_t rate) { // Check that rate is in the valid range. if ((rate >= 48000) && (rate <= 128000)) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_celt.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_celt.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_celt.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_celt.h 2015-02-03 14:33:34.000000000 +0000 @@ -37,8 +37,6 @@ int16_t InternalCreateEncoder(); - void InternalDestructEncoderInst(void* ptr_inst); - int16_t SetBitRateSafe(const int32_t rate); CELT_encinst_t_* enc_inst_ptr_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_cng.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_cng.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_cng.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_cng.cc 2015-02-03 14:33:34.000000000 +0000 @@ -71,13 +71,6 @@ encoder_initialized_ = false; } -void ACMCNG::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcCng_FreeEnc(static_cast(ptr_inst)); - } - return; -} - } // namespace acm2 } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_cng.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_cng.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_cng.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_cng.h 2015-02-03 14:33:34.000000000 +0000 @@ -35,12 +35,11 @@ int16_t InternalInitEncoder(WebRtcACMCodecParams *codec_params); protected: - void DestructEncoderSafe(); + void DestructEncoderSafe() OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); int16_t InternalCreateEncoder(); - void InternalDestructEncoderInst(void* ptr_inst); - int16_t EnableDTX() { return -1; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_codec_database.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_codec_database.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_codec_database.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_codec_database.cc 2015-02-03 14:33:34.000000000 +0000 @@ -20,7 +20,7 @@ #include #include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" #include "webrtc/system_wrappers/interface/trace.h" // Includes needed to create the codecs. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_codec_database.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_codec_database.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_codec_database.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_codec_database.h 2015-02-03 14:33:34.000000000 +0000 @@ -18,7 +18,7 @@ #include "webrtc/common_types.h" #include "webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h" -#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.cc 2015-02-03 14:33:34.000000000 +0000 @@ -40,10 +40,6 @@ int16_t ACMDTMFPlayout::InternalCreateEncoder() { return -1; } -void ACMDTMFPlayout::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - void ACMDTMFPlayout::DestructEncoderSafe() { return; } @@ -73,11 +69,6 @@ return 0; } -void ACMDTMFPlayout::InternalDestructEncoderInst(void* /* ptr_inst */) { - // DTMFPlayout has no instance - return; -} - void ACMDTMFPlayout::DestructEncoderSafe() { // DTMFPlayout has no instance return; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_dtmf_playout.h 2015-02-03 14:33:34.000000000 +0000 @@ -33,8 +33,6 @@ void DestructEncoderSafe(); int16_t InternalCreateEncoder(); - - void InternalDestructEncoderInst(void* ptr_inst); }; } // namespace acm2 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7221.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7221.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7221.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7221.cc 2015-02-03 14:33:34.000000000 +0000 @@ -115,8 +115,6 @@ void ACMG722_1::DestructEncoderSafe() { return; } -void ACMG722_1::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } - #else //===================== Actual Implementation ======================= ACMG722_1::ACMG722_1(int16_t codec_id) : encoder_inst_ptr_(NULL), @@ -316,13 +314,6 @@ encoder_inst32_ptr_ = NULL; } -void ACMG722_1::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - delete ptr_inst; - } - return; -} - #endif } // namespace acm2 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7221c.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7221c.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7221c.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7221c.cc 2015-02-03 14:33:34.000000000 +0000 @@ -115,8 +115,6 @@ void ACMG722_1C::DestructEncoderSafe() { return; } -void ACMG722_1C::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } - #else //===================== Actual Implementation ======================= ACMG722_1C::ACMG722_1C(int16_t codec_id) : encoder_inst_ptr_(NULL), @@ -322,13 +320,6 @@ encoder_inst48_ptr_ = NULL; } -void ACMG722_1C::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - delete ptr_inst; - } - return; -} - #endif } // namespace acm2 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7221c.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7221c.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7221c.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7221c.h 2015-02-03 14:33:34.000000000 +0000 @@ -43,8 +43,6 @@ int16_t InternalCreateEncoder(); - void InternalDestructEncoderInst(void* ptr_inst); - int32_t operational_rate_; G722_1_Inst_t_* encoder_inst_ptr_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7221.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7221.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7221.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7221.h 2015-02-03 14:33:34.000000000 +0000 @@ -43,8 +43,6 @@ int16_t InternalCreateEncoder(); - void InternalDestructEncoderInst(void* ptr_inst); - int32_t operational_rate_; G722_1_Inst_t_* encoder_inst_ptr_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g722.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g722.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g722.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g722.cc 2015-02-03 14:33:34.000000000 +0000 @@ -52,8 +52,6 @@ void ACMG722::DestructEncoderSafe() { return; } -void ACMG722::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } - #else //===================== Actual Implementation ======================= // Encoder and decoder memory @@ -187,13 +185,6 @@ encoder_initialized_ = false; } -void ACMG722::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcG722_FreeEncoder(static_cast(ptr_inst)); - } - return; -} - #endif } // namespace acm2 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g722.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g722.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g722.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g722.h 2015-02-03 14:33:34.000000000 +0000 @@ -11,6 +11,7 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_G722_H_ #define WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_G722_H_ +#include "webrtc/base/thread_annotations.h" #include "webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h" typedef struct WebRtcG722EncInst G722EncInst; @@ -32,7 +33,9 @@ // For FEC. ACMGenericCodec* CreateInstance(void); - int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + int16_t InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); @@ -40,14 +43,14 @@ int32_t Add10MsDataSafe(const uint32_t timestamp, const int16_t* data, const uint16_t length_smpl, - const uint8_t audio_channel); + const uint8_t audio_channel) + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); - void DestructEncoderSafe(); + void DestructEncoderSafe() OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); int16_t InternalCreateEncoder(); - void InternalDestructEncoderInst(void* ptr_inst); - ACMG722EncStr* ptr_enc_str_; G722EncInst* encoder_inst_ptr_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7291.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7291.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7291.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7291.cc 2015-02-03 14:33:34.000000000 +0000 @@ -51,8 +51,6 @@ void ACMG729_1::DestructEncoderSafe() { return; } -void ACMG729_1::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } - int16_t ACMG729_1::SetBitRateSafe(const int32_t /*rate*/) { return -1; } #else //===================== Actual Implementation ======================= @@ -159,13 +157,6 @@ } } -void ACMG729_1::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - // WebRtcG7291_Free((G729_1_inst_t*)ptrInst); - } - return; -} - int16_t ACMG729_1::SetBitRateSafe(const int32_t rate) { // allowed rates: { 8000, 12000, 14000, 16000, 18000, 20000, // 22000, 24000, 26000, 28000, 30000, 32000}; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7291.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7291.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7291.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g7291.h 2015-02-03 14:33:34.000000000 +0000 @@ -38,8 +38,6 @@ int16_t InternalCreateEncoder(); - void InternalDestructEncoderInst(void* ptr_inst); - int16_t SetBitRateSafe(const int32_t rate); G729_1_inst_t_* encoder_inst_ptr_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g729.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g729.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g729.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g729.cc 2015-02-03 14:33:34.000000000 +0000 @@ -57,8 +57,6 @@ void ACMG729::DestructEncoderSafe() { return; } -void ACMG729::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } - #else //===================== Actual Implementation ======================= ACMG729::ACMG729(int16_t codec_id) : codec_id_(codec_id), @@ -245,13 +243,6 @@ } } -void ACMG729::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcG729_FreeEnc(static_cast(ptr_inst)); - } - return; -} - #endif } // namespace acm2 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g729.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g729.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g729.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_g729.h 2015-02-03 14:33:34.000000000 +0000 @@ -38,8 +38,6 @@ int16_t InternalCreateEncoder(); - void InternalDestructEncoderInst(void* ptr_inst); - int16_t EnableDTX(); int16_t DisableDTX(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.cc 2015-02-03 14:33:34.000000000 +0000 @@ -26,7 +26,7 @@ // Enum for CNG enum { kMaxPLCParamsCNG = WEBRTC_CNG_MAX_LPC_ORDER, - kNewCNGNumPLCParams = 8 + kNewCNGNumLPCParams = 8 }; // Interval for sending new CNG parameters (SID frames) is 100 msec. @@ -56,10 +56,10 @@ vad_mode_(VADNormal), dtx_enabled_(false), ptr_dtx_inst_(NULL), - num_lpc_params_(kNewCNGNumPLCParams), + num_lpc_params_(kNewCNGNumLPCParams), sent_cn_previous_(false), prev_frame_cng_(0), - neteq_decode_lock_(NULL), + has_internal_fec_(false), codec_wrapper_lock_(*RWLockWrapper::CreateRWLock()), last_timestamp_(0xD87F3F9F), unique_id_(0) { @@ -196,6 +196,12 @@ return true; } +int ACMGenericCodec::SetFEC(bool enable_fec) { + if (!HasInternalFEC() && enable_fec) + return -1; + return 0; +} + int16_t ACMGenericCodec::Encode(uint8_t* bitstream, int16_t* bitstream_len_byte, uint32_t* timestamp, @@ -209,7 +215,6 @@ return 0; } WriteLockScoped lockCodec(codec_wrapper_lock_); - ReadLockScoped lockNetEq(*neteq_decode_lock_); // Not all codecs accept the whole frame to be pushed into encoder at once. // Some codecs needs to be feed with a specific number of samples different @@ -393,7 +398,6 @@ int16_t ACMGenericCodec::ResetEncoder() { WriteLockScoped lockCodec(codec_wrapper_lock_); - ReadLockScoped lockNetEq(*neteq_decode_lock_); return ResetEncoderSafe(); } @@ -442,7 +446,6 @@ int16_t ACMGenericCodec::InitEncoder(WebRtcACMCodecParams* codec_params, bool force_initialization) { WriteLockScoped lockCodec(codec_wrapper_lock_); - ReadLockScoped lockNetEq(*neteq_decode_lock_); return InitEncoderSafe(codec_params, force_initialization); } @@ -546,7 +549,7 @@ WebRtcCng_FreeEnc(ptr_dtx_inst_); ptr_dtx_inst_ = NULL; } - num_lpc_params_ = kNewCNGNumPLCParams; + num_lpc_params_ = kNewCNGNumLPCParams; DestructEncoderSafe(); } @@ -625,14 +628,6 @@ return status; } -void ACMGenericCodec::DestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WriteLockScoped lockCodec(codec_wrapper_lock_); - ReadLockScoped lockNetEq(*neteq_decode_lock_); - InternalDestructEncoderInst(ptr_inst); - } -} - uint32_t ACMGenericCodec::EarliestTimestamp() const { ReadLockScoped cs(codec_wrapper_lock_); return in_timestamp_[0]; @@ -842,7 +837,7 @@ // Calculate number of samples in 10 ms blocks, and number ms in one frame. int16_t samples_in_10ms = static_cast(freq_hz / 100); int32_t frame_len_ms = static_cast(frame_len_smpl_) * 1000 / freq_hz; - int16_t status; + int16_t status = -1; // Vector for storing maximum 30 ms of mono audio at 48 kHz. int16_t audio[1440]; @@ -1004,6 +999,12 @@ return -1; } +int ACMGenericCodec::SetOpusMaxPlaybackRate(int /* frequency_hz */) { + WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, unique_id_, + "The send-codec is not Opus, failed to set maximum playback rate."); + return -1; +} + } // namespace acm2 } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h 2015-02-03 14:33:34.000000000 +0000 @@ -11,10 +11,11 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_GENERIC_CODEC_H_ #define WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_GENERIC_CODEC_H_ +#include "webrtc/base/thread_annotations.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" #include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" #include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" #include "webrtc/system_wrappers/interface/trace.h" @@ -212,18 +213,6 @@ int16_t SetBitRate(const int32_t bitrate_bps); /////////////////////////////////////////////////////////////////////////// - // DestructEncoderInst() - // This API is used in conferencing. It will free the memory that is pointed - // by |ptr_inst|. |ptr_inst| is a pointer to encoder instance, created and - // filled up by calling EncoderInst(...). - // - // Inputs: - // -ptr_inst : pointer to an encoder instance to be deleted. - // - // - void DestructEncoderInst(void* ptr_inst); - - /////////////////////////////////////////////////////////////////////////// // uint32_t EarliestTimestamp() // Returns the timestamp of the first 10 ms in audio buffer. This is used // to identify if a synchronization of two encoders is required. @@ -293,17 +282,6 @@ int32_t IsInternalDTXReplaced(bool* internal_dtx_replaced); /////////////////////////////////////////////////////////////////////////// - // void SetNetEqDecodeLock() - // Passes the NetEq lock to the codec. - // - // Input: - // -neteq_decode_lock : pointer to the lock associated with NetEQ of ACM. - // - void SetNetEqDecodeLock(RWLockWrapper* neteq_decode_lock) { - neteq_decode_lock_ = neteq_decode_lock; - } - - /////////////////////////////////////////////////////////////////////////// // bool HasInternalDTX() // Used to check if the codec has internal DTX. // @@ -311,7 +289,10 @@ // true if the codec has an internal DTX, e.g. G729, // false otherwise. // - bool HasInternalDTX() const { return has_internal_dtx_; } + bool HasInternalDTX() const { + ReadLockScoped rl(codec_wrapper_lock_); + return has_internal_dtx_; + } /////////////////////////////////////////////////////////////////////////// // int32_t GetEstimatedBandwidth() @@ -435,7 +416,8 @@ // -1 if failed, or if this is meaningless for the given codec. // 0 if succeeded. // - virtual int16_t UpdateEncoderSampFreq(uint16_t samp_freq_hz); + virtual int16_t UpdateEncoderSampFreq(uint16_t samp_freq_hz) + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); /////////////////////////////////////////////////////////////////////////// // EncoderSampFreq() @@ -449,7 +431,8 @@ // -1 if failed to output sampling rate. // 0 if the sample rate is returned successfully. // - virtual int16_t EncoderSampFreq(uint16_t* samp_freq_hz); + virtual int16_t EncoderSampFreq(uint16_t* samp_freq_hz) + SHARED_LOCKS_REQUIRED(codec_wrapper_lock_); /////////////////////////////////////////////////////////////////////////// // int32_t ConfigISACBandwidthEstimator() @@ -514,8 +497,6 @@ // virtual int32_t SetISACMaxRate(const uint32_t max_rate_bps); - int32_t FrameSize() { return frame_len_smpl_; } - /////////////////////////////////////////////////////////////////////////// // REDPayloadISAC() // This is an iSAC-specific function. The function is called to get RED @@ -545,6 +526,22 @@ int16_t* payload_len_bytes); /////////////////////////////////////////////////////////////////////////// + // int SetOpusMaxPlaybackRate() + // Sets maximum playback rate the receiver will render, if the codec is Opus. + // This is to tell Opus that it is enough to code the input audio up to a + // bandwidth. Opus can take this information to optimize the bit rate and + // increase the computation efficiency. + // + // Input: + // -frequency_hz : maximum playback rate in Hz. + // + // Return value: + // -1 if failed or on codecs other than Opus + // 0 if succeeded. + // + virtual int SetOpusMaxPlaybackRate(int /* frequency_hz */); + + /////////////////////////////////////////////////////////////////////////// // HasFrameToEncode() // Returns true if there is enough audio buffered for encoding, such that // calling Encode() will return a payload. @@ -560,6 +557,49 @@ // virtual AudioDecoder* Decoder(int /* codec_id */) { return NULL; } + /////////////////////////////////////////////////////////////////////////// + // bool HasInternalFEC() + // Used to check if the codec has internal FEC. + // + // Return value: + // true if the codec has an internal FEC, e.g. Opus. + // false otherwise. + // + bool HasInternalFEC() const { + ReadLockScoped rl(codec_wrapper_lock_); + return has_internal_fec_; + } + + /////////////////////////////////////////////////////////////////////////// + // int SetFEC(); + // Sets the codec internal FEC. No effects on codecs that do not provide + // internal FEC. + // + // Input: + // -enable_fec : if true FEC will be enabled otherwise the FEC is + // disabled. + // + // Return value: + // -1 if failed, + // 0 if succeeded. + // + virtual int SetFEC(bool enable_fec); + + /////////////////////////////////////////////////////////////////////////// + // int SetPacketLossRate() + // Sets expected packet loss rate for encoding. Some encoders provide packet + // loss gnostic encoding to make stream less sensitive to packet losses, + // through e.g., FEC. No effects on codecs that do not provide such encoding. + // + // Input: + // -loss_rate : expected packet loss rate (0 -- 100 inclusive). + // + // Return value: + // -1 if failed, + // 0 if succeeded or packet loss rate is ignored. + // + virtual int SetPacketLossRate(int /* loss_rate */) { return 0; } + protected: /////////////////////////////////////////////////////////////////////////// // All the functions with FunctionNameSafe(...) contain the actual @@ -576,26 +616,29 @@ virtual int32_t Add10MsDataSafe(const uint32_t timestamp, const int16_t* data, const uint16_t length, - const uint8_t audio_channel); + const uint8_t audio_channel) + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); /////////////////////////////////////////////////////////////////////////// // See EncoderParam() for the description of function, input(s)/output(s) // and return value. // - int16_t EncoderParamsSafe(WebRtcACMCodecParams* enc_params); + int16_t EncoderParamsSafe(WebRtcACMCodecParams* enc_params) + SHARED_LOCKS_REQUIRED(codec_wrapper_lock_); /////////////////////////////////////////////////////////////////////////// // See ResetEncoder() for the description of function, input(s)/output(s) // and return value. // - int16_t ResetEncoderSafe(); + int16_t ResetEncoderSafe() EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); /////////////////////////////////////////////////////////////////////////// // See InitEncoder() for the description of function, input(s)/output(s) // and return value. // int16_t InitEncoderSafe(WebRtcACMCodecParams* codec_params, - bool force_initialization); + bool force_initialization) + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); /////////////////////////////////////////////////////////////////////////// // See InitDecoder() for the description of function, input(s)/output(s) @@ -608,7 +651,8 @@ // See DestructEncoder() for the description of function, // input(s)/output(s) and return value. // - virtual void DestructEncoderSafe() = 0; + virtual void DestructEncoderSafe() + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_) = 0; /////////////////////////////////////////////////////////////////////////// // See SetBitRate() for the description of function, input(s)/output(s) @@ -616,7 +660,8 @@ // // Any codec that can change the bit-rate has to implement this. // - virtual int16_t SetBitRateSafe(const int32_t bitrate_bps); + virtual int16_t SetBitRateSafe(const int32_t bitrate_bps) + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); /////////////////////////////////////////////////////////////////////////// // See GetEstimatedBandwidth() for the description of function, @@ -641,7 +686,8 @@ // See SetVAD() for the description of function, input(s)/output(s) and // return value. // - int16_t SetVADSafe(bool* enable_dtx, bool* enable_vad, ACMVADMode* mode); + int16_t SetVADSafe(bool* enable_dtx, bool* enable_vad, ACMVADMode* mode) + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); /////////////////////////////////////////////////////////////////////////// // See ReplaceInternalDTX() for the description of function, input and @@ -663,7 +709,7 @@ // -1 if failed, // 0 if succeeded. // - int16_t CreateEncoder(); + int16_t CreateEncoder() EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); /////////////////////////////////////////////////////////////////////////// // int16_t EnableVAD(); @@ -678,7 +724,8 @@ // -1 if failed, // 0 if succeeded. // - int16_t EnableVAD(ACMVADMode mode); + int16_t EnableVAD(ACMVADMode mode) + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); /////////////////////////////////////////////////////////////////////////// // int16_t DisableVAD() @@ -688,7 +735,7 @@ // -1 if failed, // 0 if succeeded. // - int16_t DisableVAD(); + int16_t DisableVAD() EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); /////////////////////////////////////////////////////////////////////////// // int16_t EnableDTX() @@ -699,7 +746,7 @@ // -1 if failed, // 0 if succeeded. // - virtual int16_t EnableDTX(); + virtual int16_t EnableDTX() EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); /////////////////////////////////////////////////////////////////////////// // int16_t DisableDTX() @@ -710,7 +757,7 @@ // -1 if failed, // 0 if succeeded. // - virtual int16_t DisableDTX(); + virtual int16_t DisableDTX() EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); /////////////////////////////////////////////////////////////////////////// // int16_t InternalEncode() @@ -728,7 +775,8 @@ // otherwise the length of the bit-stream is returned. // virtual int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) = 0; + int16_t* bitstream_len_byte) + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_) = 0; /////////////////////////////////////////////////////////////////////////// // int16_t InternalInitEncoder() @@ -749,7 +797,8 @@ // -1 if failed, // 0 if succeeded. // - virtual int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params) = 0; + virtual int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params) + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_) = 0; /////////////////////////////////////////////////////////////////////////// // void IncreaseNoMissedSamples() @@ -760,7 +809,8 @@ // -num_samples : the number of overwritten samples is incremented // by this value. // - void IncreaseNoMissedSamples(const int16_t num_samples); + void IncreaseNoMissedSamples(const int16_t num_samples) + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); /////////////////////////////////////////////////////////////////////////// // int16_t InternalCreateEncoder() @@ -775,23 +825,6 @@ virtual int16_t InternalCreateEncoder() = 0; /////////////////////////////////////////////////////////////////////////// - // void InternalDestructEncoderInst() - // This is a codec-specific method, used in conferencing, called from - // DestructEncoderInst(). The input argument is pointer to encoder instance - // (codec instance for codecs that encoder and decoder share the same - // instance). This method is called to free the memory that |ptr_inst| is - // pointing to. - // - // Input: - // -ptr_inst : pointer to encoder instance. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - virtual void InternalDestructEncoderInst(void* ptr_inst) = 0; - - /////////////////////////////////////////////////////////////////////////// // int16_t InternalResetEncoder() // This method is called to reset the states of encoder. However, the // current parameters, e.g. frame-length, should remain as they are. For @@ -804,7 +837,8 @@ // -1 if failed, // 0 if succeeded. // - virtual int16_t InternalResetEncoder(); + virtual int16_t InternalResetEncoder() + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); /////////////////////////////////////////////////////////////////////////// // int16_t ProcessFrameVADDTX() @@ -838,7 +872,8 @@ // int16_t ProcessFrameVADDTX(uint8_t* bitstream, int16_t* bitstream_len_byte, - int16_t* samples_processed); + int16_t* samples_processed) + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); /////////////////////////////////////////////////////////////////////////// // CurrentRate() @@ -854,62 +889,63 @@ // &in_audio_[in_audio_ix_write_] always point to where new audio can be // written to - int16_t in_audio_ix_write_; + int16_t in_audio_ix_write_ GUARDED_BY(codec_wrapper_lock_); // &in_audio_[in_audio_ix_read_] points to where audio has to be read from - int16_t in_audio_ix_read_; + int16_t in_audio_ix_read_ GUARDED_BY(codec_wrapper_lock_); - int16_t in_timestamp_ix_write_; + int16_t in_timestamp_ix_write_ GUARDED_BY(codec_wrapper_lock_); // Where the audio is stored before encoding, // To save memory the following buffer can be allocated // dynamically for 80 ms depending on the sampling frequency // of the codec. - int16_t* in_audio_; - uint32_t* in_timestamp_; + int16_t* in_audio_ GUARDED_BY(codec_wrapper_lock_); + uint32_t* in_timestamp_ GUARDED_BY(codec_wrapper_lock_); - int16_t frame_len_smpl_; - uint16_t num_channels_; + int16_t frame_len_smpl_ GUARDED_BY(codec_wrapper_lock_); + uint16_t num_channels_ GUARDED_BY(codec_wrapper_lock_); // This will point to a static database of the supported codecs - int16_t codec_id_; + int16_t codec_id_ GUARDED_BY(codec_wrapper_lock_); // This will account for the number of samples were not encoded // the case is rare, either samples are missed due to overwrite // at input buffer or due to encoding error - uint32_t num_missed_samples_; + uint32_t num_missed_samples_ GUARDED_BY(codec_wrapper_lock_); // True if the encoder instance created - bool encoder_exist_; + bool encoder_exist_ GUARDED_BY(codec_wrapper_lock_); // True if the encoder instance initialized - bool encoder_initialized_; + bool encoder_initialized_ GUARDED_BY(codec_wrapper_lock_); - bool registered_in_neteq_; + const bool registered_in_neteq_ + GUARDED_BY(codec_wrapper_lock_); // TODO(henrik.lundin) Remove? // VAD/DTX - bool has_internal_dtx_; - WebRtcVadInst* ptr_vad_inst_; - bool vad_enabled_; - ACMVADMode vad_mode_; - int16_t vad_label_[MAX_FRAME_SIZE_10MSEC]; - bool dtx_enabled_; - WebRtcCngEncInst* ptr_dtx_inst_; - uint8_t num_lpc_params_; - bool sent_cn_previous_; - int16_t prev_frame_cng_; - - WebRtcACMCodecParams encoder_params_; - - // Used as a global lock for all available decoders - // so that no decoder is used when NetEQ decodes. - RWLockWrapper* neteq_decode_lock_; + bool has_internal_dtx_ GUARDED_BY(codec_wrapper_lock_); + WebRtcVadInst* ptr_vad_inst_ GUARDED_BY(codec_wrapper_lock_); + bool vad_enabled_ GUARDED_BY(codec_wrapper_lock_); + ACMVADMode vad_mode_ GUARDED_BY(codec_wrapper_lock_); + int16_t vad_label_[MAX_FRAME_SIZE_10MSEC] GUARDED_BY(codec_wrapper_lock_); + bool dtx_enabled_ GUARDED_BY(codec_wrapper_lock_); + WebRtcCngEncInst* ptr_dtx_inst_ GUARDED_BY(codec_wrapper_lock_); + uint8_t num_lpc_params_ // TODO(henrik.lundin) Delete and + GUARDED_BY(codec_wrapper_lock_); // replace with kNewCNGNumLPCParams. + bool sent_cn_previous_ GUARDED_BY(codec_wrapper_lock_); + int16_t prev_frame_cng_ GUARDED_BY(codec_wrapper_lock_); + + // FEC. + bool has_internal_fec_ GUARDED_BY(codec_wrapper_lock_); + + WebRtcACMCodecParams encoder_params_ GUARDED_BY(codec_wrapper_lock_); // Used to lock wrapper internal data // such as buffers and state variables. RWLockWrapper& codec_wrapper_lock_; - uint32_t last_timestamp_; + uint32_t last_timestamp_ GUARDED_BY(codec_wrapper_lock_); uint32_t unique_id_; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_gsmfr.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_gsmfr.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_gsmfr.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_gsmfr.cc 2015-02-03 14:33:34.000000000 +0000 @@ -49,10 +49,6 @@ void ACMGSMFR::DestructEncoderSafe() { return; } -void ACMGSMFR::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - #else //===================== Actual Implementation ======================= ACMGSMFR::ACMGSMFR(int16_t codec_id) @@ -147,13 +143,6 @@ encoder_initialized_ = false; } -void ACMGSMFR::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcGSMFR_FreeEnc(static_cast(ptr_inst)); - } - return; -} - #endif } // namespace acm2 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_gsmfr.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_gsmfr.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_gsmfr.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_gsmfr.h 2015-02-03 14:33:34.000000000 +0000 @@ -38,8 +38,6 @@ int16_t InternalCreateEncoder(); - void InternalDestructEncoderInst(void* ptr_inst); - int16_t EnableDTX(); int16_t DisableDTX(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_ilbc.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_ilbc.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_ilbc.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_ilbc.cc 2015-02-03 14:33:34.000000000 +0000 @@ -40,8 +40,6 @@ void ACMILBC::DestructEncoderSafe() { return; } -void ACMILBC::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } - int16_t ACMILBC::SetBitRateSafe(const int32_t /* rate */) { return -1; } #else //===================== Actual Implementation ======================= @@ -117,13 +115,6 @@ } } -void ACMILBC::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcIlbcfix_EncoderFree(static_cast(ptr_inst)); - } - return; -} - int16_t ACMILBC::SetBitRateSafe(const int32_t rate) { // Check that rate is valid. No need to store the value if (rate == 13300) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_ilbc.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_ilbc.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_ilbc.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_ilbc.h 2015-02-03 14:33:34.000000000 +0000 @@ -29,19 +29,21 @@ // for FEC ACMGenericCodec* CreateInstance(void); - int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + int16_t InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); protected: - int16_t SetBitRateSafe(const int32_t rate); + int16_t SetBitRateSafe(const int32_t rate) OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); - void DestructEncoderSafe(); + void DestructEncoderSafe() OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); int16_t InternalCreateEncoder(); - void InternalDestructEncoderInst(void* ptr_inst); - iLBC_encinst_t_* encoder_inst_ptr_; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_isac.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_isac.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_isac.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_isac.cc 2015-02-03 14:33:34.000000000 +0000 @@ -14,7 +14,8 @@ #include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" #include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h" #include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/trace.h" #ifdef WEBRTC_CODEC_ISAC @@ -59,14 +60,15 @@ #if (!defined(WEBRTC_CODEC_ISAC) && !defined(WEBRTC_CODEC_ISACFX)) ACMISAC::ACMISAC(int16_t /* codec_id */) - : codec_inst_ptr_(NULL), + : codec_inst_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + codec_inst_ptr_(NULL), is_enc_initialized_(false), isac_coding_mode_(CHANNEL_INDEPENDENT), enforce_frame_size_(false), isac_currentBN_(32000), samples_in10MsAudio_(160), // Initiates to 16 kHz mode. - audio_decoder_(NULL), - decoder_initialized_(false) {} + decoder_initialized_(false) { +} ACMISAC::~ACMISAC() { return; @@ -91,8 +93,6 @@ void ACMISAC::DestructEncoderSafe() { return; } -void ACMISAC::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } - int16_t ACMISAC::Transcode(uint8_t* /* bitstream */, int16_t* /* bitstream_len_byte */, int16_t /* q_bwe */, @@ -211,7 +211,7 @@ int16_t bwe_index, int16_t /* jitter_index */, int32_t rate, - int16_t* bitstream, + uint8_t* bitstream, bool is_red) { if (is_red) { // RED not supported with iSACFIX @@ -261,81 +261,13 @@ #endif -// Decoder class to be injected into NetEq. -class AcmAudioDecoderIsac : public AudioDecoder { - public: - AcmAudioDecoderIsac(int codec_id, void* state) - : AudioDecoder(ACMCodecDB::neteq_decoders_[codec_id]) { - state_ = state; - } - - // ACMISAC is the owner of the object where |state_| is pointing to. - // Therefore, it should not be deleted in this destructor. - virtual ~AcmAudioDecoderIsac() {} - - virtual int Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type) { - int16_t temp_type; - int ret = ACM_ISAC_DECODE_B(static_cast(state_), - reinterpret_cast(encoded), - static_cast(encoded_len), decoded, - &temp_type); - *speech_type = ConvertSpeechType(temp_type); - return ret; - } - - virtual bool HasDecodePlc() const { return true; } - - virtual int DecodePlc(int num_frames, int16_t* decoded) { - return ACM_ISAC_DECODEPLC(static_cast(state_), - decoded, static_cast(num_frames)); - } - - virtual int Init() { - return 0; // We expect that the initialized instance is injected in the - // constructor. - } - - virtual int IncomingPacket(const uint8_t* payload, - size_t payload_len, - uint16_t rtp_sequence_number, - uint32_t rtp_timestamp, - uint32_t arrival_timestamp) { - return ACM_ISAC_DECODE_BWE(static_cast(state_), - reinterpret_cast(payload), - static_cast(payload_len), - rtp_sequence_number, - rtp_timestamp, - arrival_timestamp); - } - - virtual int DecodeRedundant(const uint8_t* encoded, - size_t encoded_len, int16_t* decoded, - SpeechType* speech_type) { - int16_t temp_type = 1; // Default is speech. - int16_t ret = ACM_ISAC_DECODERCU(static_cast(state_), - reinterpret_cast(encoded), - static_cast(encoded_len), decoded, - &temp_type); - *speech_type = ConvertSpeechType(temp_type); - return ret; - } - - virtual int ErrorCode() { - return ACM_ISAC_GETERRORCODE(static_cast(state_)); - } - - private: - DISALLOW_COPY_AND_ASSIGN(AcmAudioDecoderIsac); -}; - ACMISAC::ACMISAC(int16_t codec_id) - : is_enc_initialized_(false), + : codec_inst_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + is_enc_initialized_(false), isac_coding_mode_(CHANNEL_INDEPENDENT), enforce_frame_size_(false), isac_current_bn_(32000), samples_in_10ms_audio_(160), // Initiates to 16 kHz mode. - audio_decoder_(NULL), decoder_initialized_(false) { codec_id_ = codec_id; @@ -348,11 +280,6 @@ } ACMISAC::~ACMISAC() { - if (audio_decoder_ != NULL) { - delete audio_decoder_; - audio_decoder_ = NULL; - } - if (codec_inst_ptr_ != NULL) { if (codec_inst_ptr_->inst != NULL) { ACM_ISAC_FREE(codec_inst_ptr_->inst); @@ -364,6 +291,34 @@ return; } +int16_t ACMISAC::InternalInitDecoder(WebRtcACMCodecParams* codec_params) { + // set decoder sampling frequency. + if (codec_params->codec_inst.plfreq == 32000 || + codec_params->codec_inst.plfreq == 48000) { + UpdateDecoderSampFreq(ACMCodecDB::kISACSWB); + } else { + UpdateDecoderSampFreq(ACMCodecDB::kISAC); + } + + // in a one-way communication we may never register send-codec. + // However we like that the BWE to work properly so it has to + // be initialized. The BWE is initialized when iSAC encoder is initialized. + // Therefore, we need this. + if (!encoder_initialized_) { + // Since we don't require a valid rate or a valid packet size when + // initializing the decoder, we set valid values before initializing encoder + codec_params->codec_inst.rate = kIsacWbDefaultRate; + codec_params->codec_inst.pacsize = kIsacPacSize960; + if (InternalInitEncoder(codec_params) < 0) { + return -1; + } + encoder_initialized_ = true; + } + + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); + return ACM_ISAC_DECODERINIT(codec_inst_ptr_->inst); +} + ACMGenericCodec* ACMISAC::CreateInstance(void) { return NULL; } int16_t ACMISAC::InternalEncode(uint8_t* bitstream, @@ -375,6 +330,7 @@ // at the first 10ms pushed in to iSAC if the bit-rate is low, this is // sort of a bug in iSAC. to address this we treat iSAC as the // following. + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); if (codec_inst_ptr_ == NULL) { return -1; } @@ -389,8 +345,9 @@ return -1; } *bitstream_len_byte = ACM_ISAC_ENCODE( - codec_inst_ptr_->inst, &in_audio_[in_audio_ix_read_], - reinterpret_cast(bitstream)); + codec_inst_ptr_->inst, + &in_audio_[in_audio_ix_read_], + bitstream); // increment the read index this tell the caller that how far // we have gone forward in reading the audio buffer in_audio_ix_read_ += samples_in_10ms_audio_; @@ -428,6 +385,7 @@ if (UpdateEncoderSampFreq((uint16_t)codec_params->codec_inst.plfreq) < 0) { return -1; } + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); if (ACM_ISAC_ENCODERINIT(codec_inst_ptr_->inst, isac_coding_mode_) < 0) { return -1; } @@ -450,38 +408,8 @@ return 0; } -int16_t ACMISAC::InternalInitDecoder(WebRtcACMCodecParams* codec_params) { - if (codec_inst_ptr_ == NULL) { - return -1; - } - - // set decoder sampling frequency. - if (codec_params->codec_inst.plfreq == 32000 || - codec_params->codec_inst.plfreq == 48000) { - UpdateDecoderSampFreq(ACMCodecDB::kISACSWB); - } else { - UpdateDecoderSampFreq(ACMCodecDB::kISAC); - } - - // in a one-way communication we may never register send-codec. - // However we like that the BWE to work properly so it has to - // be initialized. The BWE is initialized when iSAC encoder is initialized. - // Therefore, we need this. - if (!encoder_initialized_) { - // Since we don't require a valid rate or a valid packet size when - // initializing the decoder, we set valid values before initializing encoder - codec_params->codec_inst.rate = kIsacWbDefaultRate; - codec_params->codec_inst.pacsize = kIsacPacSize960; - if (InternalInitEncoder(codec_params) < 0) { - return -1; - } - encoder_initialized_ = true; - } - - return ACM_ISAC_DECODERINIT(codec_inst_ptr_->inst); -} - int16_t ACMISAC::InternalCreateEncoder() { + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); if (codec_inst_ptr_ == NULL) { return -1; } @@ -493,19 +421,6 @@ return status; } -void ACMISAC::DestructEncoderSafe() { - // codec with shared instance cannot delete. - encoder_initialized_ = false; - return; -} - -void ACMISAC::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - ACM_ISAC_FREE(static_cast(ptr_inst)); - } - return; -} - int16_t ACMISAC::Transcode(uint8_t* bitstream, int16_t* bitstream_len_byte, int16_t q_bwe, @@ -513,13 +428,14 @@ bool is_red) { int16_t jitter_info = 0; // transcode from a higher rate to lower rate sanity check + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); if (codec_inst_ptr_ == NULL) { return -1; } *bitstream_len_byte = ACM_ISAC_GETNEWBITSTREAM( codec_inst_ptr_->inst, q_bwe, jitter_info, rate, - reinterpret_cast(bitstream), (is_red) ? 1 : 0); + bitstream, (is_red) ? 1 : 0); if (*bitstream_len_byte < 0) { // error happened @@ -530,7 +446,20 @@ } } +void ACMISAC::UpdateFrameLen() { + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); + frame_len_smpl_ = ACM_ISAC_GETNEWFRAMELEN(codec_inst_ptr_->inst); + encoder_params_.codec_inst.pacsize = frame_len_smpl_; +} + +void ACMISAC::DestructEncoderSafe() { + // codec with shared instance cannot delete. + encoder_initialized_ = false; + return; +} + int16_t ACMISAC::SetBitRateSafe(int32_t bit_rate) { + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); if (codec_inst_ptr_ == NULL) { return -1; } @@ -594,6 +523,7 @@ int samp_rate; // Get bandwidth information + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); ACM_ISAC_GETSENDBWE(codec_inst_ptr_->inst, &bandwidth_index, &delay_index); // Validy check of index @@ -615,6 +545,7 @@ int16_t bandwidth_index; // Check sample frequency and choose appropriate table + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); samp_rate = ACM_ISAC_GETENCSAMPRATE(codec_inst_ptr_->inst); if (samp_rate == 16000) { @@ -657,9 +588,8 @@ return -1; #else uint8_t* red_payload, int16_t* payload_bytes) { - int16_t bytes = - WebRtcIsac_GetRedPayload( - codec_inst_ptr_->inst, reinterpret_cast(red_payload)); + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); + int16_t bytes = WebRtcIsac_GetRedPayload(codec_inst_ptr_->inst, red_payload); if (bytes < 0) { return -1; } @@ -672,6 +602,7 @@ #ifdef WEBRTC_CODEC_ISAC int16_t codec_id) { // The decoder supports only wideband and super-wideband. + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); if (ACMCodecDB::kISAC == codec_id) { return WebRtcIsac_SetDecSampRate(codec_inst_ptr_->inst, 16000); } else if (ACMCodecDB::kISACSWB == codec_id || @@ -700,6 +631,7 @@ in_audio_ix_read_ = 0; in_audio_ix_write_ = 0; in_timestamp_ix_write_ = 0; + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); if (WebRtcIsac_SetEncSampRate(codec_inst_ptr_->inst, encoder_samp_freq_hz) < 0) { return -1; @@ -718,6 +650,7 @@ } int16_t ACMISAC::EncoderSampFreq(uint16_t* samp_freq_hz) { + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); *samp_freq_hz = ACM_ISAC_GETENCSAMPRATE(codec_inst_ptr_->inst); return 0; } @@ -730,6 +663,7 @@ { uint16_t samp_freq_hz; EncoderSampFreq(&samp_freq_hz); + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); // TODO(turajs): at 32kHz we hardcode calling with 30ms and enforce // the frame-size otherwise we might get error. Revise if // control-bwe is changed. @@ -748,27 +682,29 @@ "Couldn't config iSAC BWE."); return -1; } - UpdateFrameLen(); + { + WriteLockScoped wl(codec_wrapper_lock_); + UpdateFrameLen(); + } + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); ACM_ISAC_GETSENDBITRATE(codec_inst_ptr_->inst, &isac_current_bn_); return 0; } int32_t ACMISAC::SetISACMaxPayloadSize(const uint16_t max_payload_len_bytes) { + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); return ACM_ISAC_SETMAXPAYLOADSIZE(codec_inst_ptr_->inst, max_payload_len_bytes); } int32_t ACMISAC::SetISACMaxRate(const uint32_t max_rate_bit_per_sec) { + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); return ACM_ISAC_SETMAXRATE(codec_inst_ptr_->inst, max_rate_bit_per_sec); } -void ACMISAC::UpdateFrameLen() { - frame_len_smpl_ = ACM_ISAC_GETNEWFRAMELEN(codec_inst_ptr_->inst); - encoder_params_.codec_inst.pacsize = frame_len_smpl_; -} - void ACMISAC::CurrentRate(int32_t* rate_bit_per_sec) { if (isac_coding_mode_ == ADAPTIVE) { + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); ACM_ISAC_GETSENDBITRATE(codec_inst_ptr_->inst, rate_bit_per_sec); } } @@ -784,12 +720,72 @@ return status; } -AudioDecoder* ACMISAC::Decoder(int codec_id) { - if (audio_decoder_) - return audio_decoder_; +int ACMISAC::Decode(const uint8_t* encoded, + size_t encoded_len, + int16_t* decoded, + SpeechType* speech_type) { + int16_t temp_type = 1; // Default is speech. + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); + int ret = + ACM_ISAC_DECODE_B(static_cast(codec_inst_ptr_->inst), + encoded, + static_cast(encoded_len), + decoded, + &temp_type); + *speech_type = ConvertSpeechType(temp_type); + return ret; +} + +int ACMISAC::DecodePlc(int num_frames, int16_t* decoded) { + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); + return ACM_ISAC_DECODEPLC( + static_cast(codec_inst_ptr_->inst), + decoded, + static_cast(num_frames)); +} + +int ACMISAC::IncomingPacket(const uint8_t* payload, + size_t payload_len, + uint16_t rtp_sequence_number, + uint32_t rtp_timestamp, + uint32_t arrival_timestamp) { + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); + return ACM_ISAC_DECODE_BWE( + static_cast(codec_inst_ptr_->inst), + payload, + static_cast(payload_len), + rtp_sequence_number, + rtp_timestamp, + arrival_timestamp); +} + +int ACMISAC::DecodeRedundant(const uint8_t* encoded, + size_t encoded_len, + int16_t* decoded, + SpeechType* speech_type) { + int16_t temp_type = 1; // Default is speech. + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); + int16_t ret = + ACM_ISAC_DECODERCU(static_cast(codec_inst_ptr_->inst), + encoded, + static_cast(encoded_len), + decoded, + &temp_type); + *speech_type = ConvertSpeechType(temp_type); + return ret; +} + +int ACMISAC::ErrorCode() { + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); + return ACM_ISAC_GETERRORCODE( + static_cast(codec_inst_ptr_->inst)); +} +AudioDecoder* ACMISAC::Decoder(int codec_id) { // Create iSAC instance if it does not exist. + WriteLockScoped wl(codec_wrapper_lock_); if (!encoder_exist_) { + CriticalSectionScoped lock(codec_inst_crit_sect_.get()); assert(codec_inst_ptr_->inst == NULL); encoder_initialized_ = false; decoder_initialized_ = false; @@ -822,8 +818,7 @@ decoder_initialized_ = true; } - audio_decoder_ = new AcmAudioDecoderIsac(codec_id, codec_inst_ptr_->inst); - return audio_decoder_; + return this; } #endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_isac.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_isac.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_isac.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_isac.h 2015-02-03 14:33:34.000000000 +0000 @@ -11,87 +11,124 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_ISAC_H_ #define WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_ISAC_H_ +#include "webrtc/base/thread_annotations.h" #include "webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h" +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" namespace webrtc { +class CriticalSectionWrapper; + namespace acm2 { struct ACMISACInst; -class AcmAudioDecoderIsac; enum IsacCodingMode { ADAPTIVE, CHANNEL_INDEPENDENT }; -class ACMISAC : public ACMGenericCodec { +class ACMISAC : public ACMGenericCodec, AudioDecoder { public: explicit ACMISAC(int16_t codec_id); ~ACMISAC(); - // for FEC - ACMGenericCodec* CreateInstance(void); + int16_t InternalInitDecoder(WebRtcACMCodecParams* codec_params) + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); - int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + // Methods below are inherited from ACMGenericCodec. + ACMGenericCodec* CreateInstance(void) OVERRIDE; - int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); + int16_t InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); - int16_t InternalInitDecoder(WebRtcACMCodecParams* codec_params); + int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params) OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); - int16_t UpdateDecoderSampFreq(int16_t codec_id); + int16_t UpdateDecoderSampFreq(int16_t codec_id) OVERRIDE; - int16_t UpdateEncoderSampFreq(uint16_t samp_freq_hz); + int16_t UpdateEncoderSampFreq(uint16_t samp_freq_hz) OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); - int16_t EncoderSampFreq(uint16_t* samp_freq_hz); + int16_t EncoderSampFreq(uint16_t* samp_freq_hz) OVERRIDE; int32_t ConfigISACBandwidthEstimator(const uint8_t init_frame_size_msec, const uint16_t init_rate_bit_per_sec, - const bool enforce_frame_size); + const bool enforce_frame_size) OVERRIDE; - int32_t SetISACMaxPayloadSize(const uint16_t max_payload_len_bytes); + int32_t SetISACMaxPayloadSize(const uint16_t max_payload_len_bytes) OVERRIDE; - int32_t SetISACMaxRate(const uint32_t max_rate_bit_per_sec); + int32_t SetISACMaxRate(const uint32_t max_rate_bit_per_sec) OVERRIDE; int16_t REDPayloadISAC(const int32_t isac_rate, const int16_t isac_bw_estimate, uint8_t* payload, - int16_t* payload_len_bytes); + int16_t* payload_len_bytes) OVERRIDE; - protected: - void DestructEncoderSafe(); + // Methods below are inherited from AudioDecoder. + virtual int Decode(const uint8_t* encoded, + size_t encoded_len, + int16_t* decoded, + SpeechType* speech_type) OVERRIDE; - int16_t SetBitRateSafe(const int32_t bit_rate); + virtual bool HasDecodePlc() const OVERRIDE { return true; } - int32_t GetEstimatedBandwidthSafe(); + virtual int DecodePlc(int num_frames, int16_t* decoded) OVERRIDE; - int32_t SetEstimatedBandwidthSafe(int32_t estimated_bandwidth); + virtual int Init() OVERRIDE { return 0; } - int32_t GetRedPayloadSafe(uint8_t* red_payload, int16_t* payload_bytes); + virtual int IncomingPacket(const uint8_t* payload, + size_t payload_len, + uint16_t rtp_sequence_number, + uint32_t rtp_timestamp, + uint32_t arrival_timestamp) OVERRIDE; - int16_t InternalCreateEncoder(); + virtual int DecodeRedundant(const uint8_t* encoded, + size_t encoded_len, + int16_t* decoded, + SpeechType* speech_type) OVERRIDE; - void InternalDestructEncoderInst(void* ptr_inst); + virtual int ErrorCode() OVERRIDE; + protected: int16_t Transcode(uint8_t* bitstream, int16_t* bitstream_len_byte, int16_t q_bwe, int32_t rate, bool is_red); - void CurrentRate(int32_t* rate_bit_per_sec); + void UpdateFrameLen() EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); + + // Methods below are inherited from ACMGenericCodec. + void DestructEncoderSafe() OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); + + int16_t SetBitRateSafe(const int32_t bit_rate) OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); + + int32_t GetEstimatedBandwidthSafe() OVERRIDE; + + int32_t SetEstimatedBandwidthSafe(int32_t estimated_bandwidth) OVERRIDE; + + int32_t GetRedPayloadSafe(uint8_t* red_payload, + int16_t* payload_bytes) OVERRIDE; + + int16_t InternalCreateEncoder() OVERRIDE; - void UpdateFrameLen(); + void CurrentRate(int32_t* rate_bit_per_sec) OVERRIDE; - virtual AudioDecoder* Decoder(int codec_id); + virtual AudioDecoder* Decoder(int codec_id) OVERRIDE; - ACMISACInst* codec_inst_ptr_; + // |codec_inst_crit_sect_| protects |codec_inst_ptr_|. + const scoped_ptr codec_inst_crit_sect_; + ACMISACInst* codec_inst_ptr_ GUARDED_BY(codec_inst_crit_sect_); bool is_enc_initialized_; IsacCodingMode isac_coding_mode_; bool enforce_frame_size_; int32_t isac_current_bn_; uint16_t samples_in_10ms_audio_; - AcmAudioDecoderIsac* audio_decoder_; bool decoder_initialized_; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_opus.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_opus.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_opus.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_opus.cc 2015-02-03 14:33:34.000000000 +0000 @@ -27,7 +27,8 @@ : encoder_inst_ptr_(NULL), sample_freq_(0), bitrate_(0), - channels_(1) { + channels_(1), + packet_loss_rate_(0) { return; } @@ -56,10 +57,6 @@ return; } -void ACMOpus::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - int16_t ACMOpus::SetBitRateSafe(const int32_t /*rate*/) { return -1; } @@ -70,15 +67,18 @@ : encoder_inst_ptr_(NULL), sample_freq_(32000), // Default sampling frequency. bitrate_(20000), // Default bit-rate. - channels_(1) { // Default mono + channels_(1), // Default mono. + packet_loss_rate_(0) { // Initial packet loss rate. codec_id_ = codec_id; // Opus has internal DTX, but we dont use it for now. has_internal_dtx_ = false; + has_internal_fec_ = true; + if (codec_id_ != ACMCodecDB::kOpus) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, "Wrong codec id for Opus."); - sample_freq_ = -1; + sample_freq_ = 0xFFFF; bitrate_ = -1; } return; @@ -140,6 +140,20 @@ // Store bitrate. bitrate_ = codec_params->codec_inst.rate; + // TODO(tlegrand): Remove this code when we have proper APIs to set the + // complexity at a higher level. +#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) || defined(WEBRTC_ARCH_ARM) + // If we are on Android, iOS and/or ARM, use a lower complexity setting as + // default, to save encoder complexity. + const int kOpusComplexity5 = 5; + WebRtcOpus_SetComplexity(encoder_inst_ptr_, kOpusComplexity5); + if (ret < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, + "Setting complexity failed for Opus"); + return ret; + } +#endif + return 0; } @@ -159,13 +173,6 @@ } } -void ACMOpus::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcOpus_EncoderFree(static_cast(ptr_inst)); - } - return; -} - int16_t ACMOpus::SetBitRateSafe(const int32_t rate) { if (rate < 6000 || rate > 510000) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, @@ -184,6 +191,66 @@ return -1; } +int ACMOpus::SetFEC(bool enable_fec) { + // Ask the encoder to enable FEC. + if (enable_fec) { + if (WebRtcOpus_EnableFec(encoder_inst_ptr_) == 0) + return 0; + } else { + if (WebRtcOpus_DisableFec(encoder_inst_ptr_) == 0) + return 0; + } + return -1; +} + +int ACMOpus::SetPacketLossRate(int loss_rate) { + // Optimize the loss rate to configure Opus. Basically, optimized loss rate is + // the input loss rate rounded down to various levels, because a robustly good + // audio quality is achieved by lowering the packet loss down. + // Additionally, to prevent toggling, margins are used, i.e., when jumping to + // a loss rate from below, a higher threshold is used than jumping to the same + // level from above. + const int kPacketLossRate20 = 20; + const int kPacketLossRate10 = 10; + const int kPacketLossRate5 = 5; + const int kPacketLossRate1 = 1; + const int kLossRate20Margin = 2; + const int kLossRate10Margin = 1; + const int kLossRate5Margin = 1; + int opt_loss_rate; + if (loss_rate >= kPacketLossRate20 + kLossRate20Margin * + (kPacketLossRate20 - packet_loss_rate_ > 0 ? 1 : -1)) { + opt_loss_rate = kPacketLossRate20; + } else if (loss_rate >= kPacketLossRate10 + kLossRate10Margin * + (kPacketLossRate10 - packet_loss_rate_ > 0 ? 1 : -1)) { + opt_loss_rate = kPacketLossRate10; + } else if (loss_rate >= kPacketLossRate5 + kLossRate5Margin * + (kPacketLossRate5 - packet_loss_rate_ > 0 ? 1 : -1)) { + opt_loss_rate = kPacketLossRate5; + } else if (loss_rate >= kPacketLossRate1) { + opt_loss_rate = kPacketLossRate1; + } else { + opt_loss_rate = 0; + } + + if (packet_loss_rate_ == opt_loss_rate) { + return 0; + } + + // Ask the encoder to change the target packet loss rate. + if (WebRtcOpus_SetPacketLossRate(encoder_inst_ptr_, opt_loss_rate) == 0) { + packet_loss_rate_ = opt_loss_rate; + return 0; + } + + return -1; +} + +int ACMOpus::SetOpusMaxPlaybackRate(int frequency_hz) { + // Informs Opus encoder of the maximum playback rate the receiver will render. + return WebRtcOpus_SetMaxPlaybackRate(encoder_inst_ptr_, frequency_hz); +} + #endif // WEBRTC_CODEC_OPUS } // namespace acm2 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_opus.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_opus.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_opus.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_opus.h 2015-02-03 14:33:34.000000000 +0000 @@ -28,23 +28,32 @@ ACMGenericCodec* CreateInstance(void); - int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + int16_t InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); int16_t InternalInitEncoder(WebRtcACMCodecParams *codec_params); + virtual int SetFEC(bool enable_fec) OVERRIDE; + + virtual int SetPacketLossRate(int loss_rate) OVERRIDE; + + virtual int SetOpusMaxPlaybackRate(int frequency_hz) OVERRIDE; + protected: void DestructEncoderSafe(); int16_t InternalCreateEncoder(); - void InternalDestructEncoderInst(void* ptr_inst); - - int16_t SetBitRateSafe(const int32_t rate); + int16_t SetBitRateSafe(const int32_t rate) OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); WebRtcOpusEncInst* encoder_inst_ptr_; uint16_t sample_freq_; - uint16_t bitrate_; + int32_t bitrate_; int channels_; + + int packet_loss_rate_; }; } // namespace acm2 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_opus_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_opus_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_opus_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_opus_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/main/acm2/acm_opus.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h" + +namespace webrtc { + +namespace acm2 { + +namespace { + const CodecInst kOpusCodecInst = {105, "opus", 48000, 960, 1, 32000}; + // These constants correspond to those used in ACMOpus::SetPacketLossRate(). + const int kPacketLossRate20 = 20; + const int kPacketLossRate10 = 10; + const int kPacketLossRate5 = 5; + const int kPacketLossRate1 = 1; + const int kLossRate20Margin = 2; + const int kLossRate10Margin = 1; + const int kLossRate5Margin = 1; +} // namespace + +class AcmOpusTest : public ACMOpus { + public: + explicit AcmOpusTest(int16_t codec_id) + : ACMOpus(codec_id) {} + ~AcmOpusTest() {} + int packet_loss_rate() { return packet_loss_rate_; } + + void TestSetPacketLossRate(int from, int to, int expected_return); +}; + +#ifdef WEBRTC_CODEC_OPUS +void AcmOpusTest::TestSetPacketLossRate(int from, int to, int expected_return) { + for (int loss = from; loss <= to; (to >= from) ? ++loss : --loss) { + EXPECT_EQ(0, SetPacketLossRate(loss)); + EXPECT_EQ(expected_return, packet_loss_rate()); + } +} + +TEST(AcmOpusTest, PacketLossRateOptimized) { + AcmOpusTest opus(ACMCodecDB::kOpus); + WebRtcACMCodecParams params; + memcpy(&(params.codec_inst), &kOpusCodecInst, sizeof(CodecInst)); + EXPECT_EQ(0, opus.InitEncoder(¶ms, true)); + EXPECT_EQ(0, opus.SetFEC(true)); + + // Note that the order of the following calls is critical. + opus.TestSetPacketLossRate(0, 0, 0); + opus.TestSetPacketLossRate(kPacketLossRate1, + kPacketLossRate5 + kLossRate5Margin - 1, + kPacketLossRate1); + opus.TestSetPacketLossRate(kPacketLossRate5 + kLossRate5Margin, + kPacketLossRate10 + kLossRate10Margin - 1, + kPacketLossRate5); + opus.TestSetPacketLossRate(kPacketLossRate10 + kLossRate10Margin, + kPacketLossRate20 + kLossRate20Margin - 1, + kPacketLossRate10); + opus.TestSetPacketLossRate(kPacketLossRate20 + kLossRate20Margin, + 100, + kPacketLossRate20); + opus.TestSetPacketLossRate(kPacketLossRate20 + kLossRate20Margin, + kPacketLossRate20 - kLossRate20Margin, + kPacketLossRate20); + opus.TestSetPacketLossRate(kPacketLossRate20 - kLossRate20Margin - 1, + kPacketLossRate10 - kLossRate10Margin, + kPacketLossRate10); + opus.TestSetPacketLossRate(kPacketLossRate10 - kLossRate10Margin - 1, + kPacketLossRate5 - kLossRate5Margin, + kPacketLossRate5); + opus.TestSetPacketLossRate(kPacketLossRate5 - kLossRate5Margin - 1, + kPacketLossRate1, + kPacketLossRate1); + opus.TestSetPacketLossRate(0, 0, 0); +} +#else +void AcmOpusTest:TestSetPacketLossRate(int /* from */, int /* to */, + int /* expected_return */) { + return; +} +#endif // WEBRTC_CODEC_OPUS + +} // namespace acm2 + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcm16b.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcm16b.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcm16b.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcm16b.cc 2015-02-03 14:33:34.000000000 +0000 @@ -41,8 +41,6 @@ int16_t ACMPCM16B::InternalCreateEncoder() { return -1; } -void ACMPCM16B::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } - void ACMPCM16B::DestructEncoderSafe() { return; } #else //===================== Actual Implementation ======================= @@ -77,11 +75,6 @@ return 0; } -void ACMPCM16B::InternalDestructEncoderInst(void* /* ptr_inst */) { - // PCM has no instance. - return; -} - void ACMPCM16B::DestructEncoderSafe() { // PCM has no instance. encoder_exist_ = false; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcm16b.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcm16b.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcm16b.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcm16b.h 2015-02-03 14:33:34.000000000 +0000 @@ -25,17 +25,18 @@ // For FEC. ACMGenericCodec* CreateInstance(void); - int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + int16_t InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); protected: - void DestructEncoderSafe(); + void DestructEncoderSafe() OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); int16_t InternalCreateEncoder(); - void InternalDestructEncoderInst(void* ptr_inst); - int32_t sampling_freq_hz_; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcma.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcma.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcma.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcma.cc 2015-02-03 14:33:34.000000000 +0000 @@ -27,7 +27,7 @@ int16_t ACMPCMA::InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte) { *bitstream_len_byte = WebRtcG711_EncodeA( - NULL, &in_audio_[in_audio_ix_read_], frame_len_smpl_ * num_channels_, + &in_audio_[in_audio_ix_read_], frame_len_smpl_ * num_channels_, reinterpret_cast(bitstream)); // Increment the read index this tell the caller that how far // we have gone forward in reading the audio buffer. @@ -47,11 +47,6 @@ return 0; } -void ACMPCMA::InternalDestructEncoderInst(void* /* ptr_inst */) { - // PCM has no instance. - return; -} - void ACMPCMA::DestructEncoderSafe() { // PCM has no instance. return; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcma.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcma.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcma.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcma.h 2015-02-03 14:33:34.000000000 +0000 @@ -25,7 +25,9 @@ // For FEC. ACMGenericCodec* CreateInstance(void); - int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + int16_t InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); @@ -33,8 +35,6 @@ void DestructEncoderSafe(); int16_t InternalCreateEncoder(); - - void InternalDestructEncoderInst(void* ptr_inst); }; } // namespace acm2 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcmu.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcmu.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcmu.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcmu.cc 2015-02-03 14:33:34.000000000 +0000 @@ -27,7 +27,7 @@ int16_t ACMPCMU::InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte) { *bitstream_len_byte = WebRtcG711_EncodeU( - NULL, &in_audio_[in_audio_ix_read_], frame_len_smpl_ * num_channels_, + &in_audio_[in_audio_ix_read_], frame_len_smpl_ * num_channels_, reinterpret_cast(bitstream)); // Increment the read index this tell the caller that how far @@ -48,10 +48,6 @@ return 0; } -void ACMPCMU::InternalDestructEncoderInst(void* /* ptr_inst */) { - // PCM has no instance. -} - void ACMPCMU::DestructEncoderSafe() { // PCM has no instance. encoder_exist_ = false; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcmu.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcmu.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcmu.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_pcmu.h 2015-02-03 14:33:34.000000000 +0000 @@ -25,16 +25,17 @@ // For FEC. ACMGenericCodec* CreateInstance(void); - int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); + int16_t InternalEncode(uint8_t* bitstream, + int16_t* bitstream_len_byte) OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params); protected: - void DestructEncoderSafe(); + void DestructEncoderSafe() OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); int16_t InternalCreateEncoder(); - - void InternalDestructEncoderInst(void* ptr_inst); }; } // namespace acm2 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receiver.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receiver.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receiver.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receiver.cc 2015-02-03 14:33:34.000000000 +0000 @@ -21,11 +21,11 @@ #include "webrtc/modules/audio_coding/main/acm2/acm_resampler.h" #include "webrtc/modules/audio_coding/main/acm2/call_statistics.h" #include "webrtc/modules/audio_coding/main/acm2/nack.h" -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" -#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" +#include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/logging.h" -#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" #include "webrtc/system_wrappers/interface/tick_util.h" #include "webrtc/system_wrappers/interface/trace.h" @@ -35,7 +35,6 @@ namespace { -const int kNeteqInitSampleRateHz = 16000; const int kNackThresholdPackets = 2; // |vad_activity_| field of |audio_frame| is set to |previous_audio_activity_| @@ -117,21 +116,25 @@ } // namespace -AcmReceiver::AcmReceiver() - : id_(0), - neteq_(NetEq::Create(kNeteqInitSampleRateHz)), +AcmReceiver::AcmReceiver(const AudioCodingModule::Config& config) + : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + id_(config.id), last_audio_decoder_(-1), // Invalid value. - decode_lock_(RWLockWrapper::CreateRWLock()), - neteq_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), - vad_enabled_(true), - previous_audio_activity_(AudioFrame::kVadUnknown), - current_sample_rate_hz_(kNeteqInitSampleRateHz), + previous_audio_activity_(AudioFrame::kVadPassive), + current_sample_rate_hz_(config.neteq_config.sample_rate_hz), + audio_buffer_(new int16_t[AudioFrame::kMaxDataSizeSamples]), + last_audio_buffer_(new int16_t[AudioFrame::kMaxDataSizeSamples]), nack_(), nack_enabled_(false), + neteq_(NetEq::Create(config.neteq_config)), + vad_enabled_(true), + clock_(config.clock), + resampled_last_output_frame_(true), av_sync_(false), initial_delay_manager_(), missing_packets_sync_stream_(), late_packets_sync_stream_() { + assert(clock_); for (int n = 0; n < ACMCodecDB::kMaxNumCodecs; ++n) { decoders_[n].registered = false; } @@ -143,12 +146,13 @@ neteq_->EnableVad(); else neteq_->DisableVad(); + + memset(audio_buffer_.get(), 0, AudioFrame::kMaxDataSizeSamples); + memset(last_audio_buffer_.get(), 0, AudioFrame::kMaxDataSizeSamples); } AcmReceiver::~AcmReceiver() { delete neteq_; - delete decode_lock_; - delete neteq_crit_sect_; } int AcmReceiver::SetMinimumDelay(int delay_ms) { @@ -162,7 +166,7 @@ if (delay_ms < 0 || delay_ms > 10000) { return -1; } - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); if (delay_ms == 0) { av_sync_ = false; @@ -206,35 +210,31 @@ } int AcmReceiver::current_sample_rate_hz() const { - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); return current_sample_rate_hz_; } // TODO(turajs): use one set of enumerators, e.g. the one defined in // common_types.h +// TODO(henrik.lundin): This method is not used any longer. The call hierarchy +// stops in voe::Channel::SetNetEQPlayoutMode(). Remove it. void AcmReceiver::SetPlayoutMode(AudioPlayoutMode mode) { enum NetEqPlayoutMode playout_mode = kPlayoutOn; - enum NetEqBackgroundNoiseMode bgn_mode = kBgnOn; switch (mode) { case voice: playout_mode = kPlayoutOn; - bgn_mode = kBgnOn; break; case fax: // No change to background noise mode. playout_mode = kPlayoutFax; - bgn_mode = neteq_->BackgroundNoiseMode(); break; case streaming: playout_mode = kPlayoutStreaming; - bgn_mode = kBgnOff; break; case off: playout_mode = kPlayoutOff; - bgn_mode = kBgnOff; break; } neteq_->SetPlayoutMode(playout_mode); - neteq_->SetBackgroundNoiseMode(bgn_mode); } AudioPlayoutMode AcmReceiver::PlayoutMode() const { @@ -269,7 +269,7 @@ const RTPHeader* header = &rtp_header.header; // Just a shorthand. { - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); int codec_id = RtpHeaderToCodecIndex(*header, incoming_payload); if (codec_id < 0) { @@ -328,38 +328,33 @@ rtp_header, receive_timestamp, packet_type, new_codec, sample_rate_hz, missing_packets_sync_stream_.get()); } - } - - { - WriteLockScoped lock_codecs(*decode_lock_); // Lock to prevent an encoding. + } // |crit_sect_| is released. - // If |missing_packets_sync_stream_| is allocated then we are in AV-sync and - // we may need to insert sync-packets. We don't check |av_sync_| as we are - // outside AcmReceiver's critical section. - if (missing_packets_sync_stream_.get()) { - InsertStreamOfSyncPackets(missing_packets_sync_stream_.get()); - } + // If |missing_packets_sync_stream_| is allocated then we are in AV-sync and + // we may need to insert sync-packets. We don't check |av_sync_| as we are + // outside AcmReceiver's critical section. + if (missing_packets_sync_stream_.get()) { + InsertStreamOfSyncPackets(missing_packets_sync_stream_.get()); + } - if (neteq_->InsertPacket(rtp_header, incoming_payload, length_payload, - receive_timestamp) < 0) { - LOG_FERR1(LS_ERROR, "AcmReceiver::InsertPacket", header->payloadType) << - " Failed to insert packet"; - return -1; - } + if (neteq_->InsertPacket(rtp_header, incoming_payload, length_payload, + receive_timestamp) < 0) { + LOG_FERR1(LS_ERROR, "AcmReceiver::InsertPacket", header->payloadType) << + " Failed to insert packet"; + return -1; } return 0; } int AcmReceiver::GetAudio(int desired_freq_hz, AudioFrame* audio_frame) { enum NetEqOutputType type; - int16_t* ptr_audio_buffer = audio_frame->data_; int samples_per_channel; int num_channels; bool return_silence = false; { // Accessing members, take the lock. - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); if (av_sync_) { assert(initial_delay_manager_.get()); @@ -369,42 +364,28 @@ initial_delay_manager_->LatePackets(timestamp_now, late_packets_sync_stream_.get()); } - - if (!return_silence) { - // This is our initial guess regarding whether a resampling will be - // required. It is based on previous sample rate of netEq. Most often, - // this is a correct guess, however, in case that incoming payload changes - // the resampling might might be needed. By doing so, we avoid an - // unnecessary memcpy(). - if (desired_freq_hz != -1 && - current_sample_rate_hz_ != desired_freq_hz) { - ptr_audio_buffer = audio_buffer_; - } - } } - { - WriteLockScoped lock_codecs(*decode_lock_); // Lock to prevent an encoding. - - // If |late_packets_sync_stream_| is allocated then we have been in AV-sync - // mode and we might have to insert sync-packets. - if (late_packets_sync_stream_.get()) { - InsertStreamOfSyncPackets(late_packets_sync_stream_.get()); - if (return_silence) // Silence generated, don't pull from NetEq. - return 0; - } - - if (neteq_->GetAudio(AudioFrame::kMaxDataSizeSamples, - ptr_audio_buffer, - &samples_per_channel, - &num_channels, &type) != NetEq::kOK) { - LOG_FERR0(LS_ERROR, "AcmReceiver::GetAudio") << "NetEq Failed."; - return -1; - } + // If |late_packets_sync_stream_| is allocated then we have been in AV-sync + // mode and we might have to insert sync-packets. + if (late_packets_sync_stream_.get()) { + InsertStreamOfSyncPackets(late_packets_sync_stream_.get()); + if (return_silence) // Silence generated, don't pull from NetEq. + return 0; } // Accessing members, take the lock. - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); + + // Always write the output to |audio_buffer_| first. + if (neteq_->GetAudio(AudioFrame::kMaxDataSizeSamples, + audio_buffer_.get(), + &samples_per_channel, + &num_channels, + &type) != NetEq::kOK) { + LOG_FERR0(LS_ERROR, "AcmReceiver::GetAudio") << "NetEq Failed."; + return -1; + } // Update NACK. int decoded_sequence_num = 0; @@ -423,37 +404,53 @@ bool need_resampling = (desired_freq_hz != -1) && (current_sample_rate_hz_ != desired_freq_hz); - if (ptr_audio_buffer == audio_buffer_) { - // Data is written to local buffer. - if (need_resampling) { - samples_per_channel = resampler_.Resample10Msec( - audio_buffer_, current_sample_rate_hz_, desired_freq_hz, - num_channels, audio_frame->data_); - if (samples_per_channel < 0) { - LOG_FERR0(LS_ERROR, "AcmReceiver::GetAudio") << "Resampler Failed."; - return -1; - } - } else { - // We might end up here ONLY if codec is changed. - memcpy(audio_frame->data_, audio_buffer_, samples_per_channel * - num_channels * sizeof(int16_t)); + if (need_resampling && !resampled_last_output_frame_) { + // Prime the resampler with the last frame. + int16_t temp_output[AudioFrame::kMaxDataSizeSamples]; + samples_per_channel = + resampler_.Resample10Msec(last_audio_buffer_.get(), + current_sample_rate_hz_, + desired_freq_hz, + num_channels, + AudioFrame::kMaxDataSizeSamples, + temp_output); + if (samples_per_channel < 0) { + LOG_FERR0(LS_ERROR, "AcmReceiver::GetAudio") + << "Resampling last_audio_buffer_ failed."; + return -1; } - } else { - // Data is written into |audio_frame|. - if (need_resampling) { - // We might end up here ONLY if codec is changed. - samples_per_channel = resampler_.Resample10Msec( - audio_frame->data_, current_sample_rate_hz_, desired_freq_hz, - num_channels, audio_buffer_); - if (samples_per_channel < 0) { - LOG_FERR0(LS_ERROR, "AcmReceiver::GetAudio") << "Resampler Failed."; - return -1; - } - memcpy(audio_frame->data_, audio_buffer_, samples_per_channel * - num_channels * sizeof(int16_t)); + } + + // The audio in |audio_buffer_| is tansferred to |audio_frame_| below, either + // through resampling, or through straight memcpy. + // TODO(henrik.lundin) Glitches in the output may appear if the output rate + // from NetEq changes. See WebRTC issue 3923. + if (need_resampling) { + samples_per_channel = + resampler_.Resample10Msec(audio_buffer_.get(), + current_sample_rate_hz_, + desired_freq_hz, + num_channels, + AudioFrame::kMaxDataSizeSamples, + audio_frame->data_); + if (samples_per_channel < 0) { + LOG_FERR0(LS_ERROR, "AcmReceiver::GetAudio") + << "Resampling audio_buffer_ failed."; + return -1; } + resampled_last_output_frame_ = true; + } else { + resampled_last_output_frame_ = false; + // We might end up here ONLY if codec is changed. + memcpy(audio_frame->data_, + audio_buffer_.get(), + samples_per_channel * num_channels * sizeof(int16_t)); } + // Swap buffers, so that the current audio is stored in |last_audio_buffer_| + // for next time. + audio_buffer_.swap(last_audio_buffer_); + audio_frame->num_channels_ = num_channels; audio_frame->samples_per_channel_ = samples_per_channel; audio_frame->sample_rate_hz_ = samples_per_channel * 100; @@ -463,6 +460,19 @@ SetAudioFrameActivityAndType(vad_enabled_, type, audio_frame); previous_audio_activity_ = audio_frame->vad_activity_; call_stats_.DecodedByNetEq(audio_frame->speech_type_); + + // Computes the RTP timestamp of the first sample in |audio_frame| from + // |GetPlayoutTimestamp|, which is the timestamp of the last sample of + // |audio_frame|. + uint32_t playout_timestamp = 0; + if (GetPlayoutTimestamp(&playout_timestamp)) { + audio_frame->timestamp_ = + playout_timestamp - audio_frame->samples_per_channel_; + } else { + // Remain 0 until we have a valid |playout_timestamp|. + audio_frame->timestamp_ = 0; + } + return 0; } @@ -473,19 +483,25 @@ assert(acm_codec_id >= 0 && acm_codec_id < ACMCodecDB::kMaxNumCodecs); NetEqDecoder neteq_decoder = ACMCodecDB::neteq_decoders_[acm_codec_id]; - CriticalSectionScoped lock(neteq_crit_sect_); + // Make sure the right decoder is registered for Opus. + if (neteq_decoder == kDecoderOpus && channels == 2) { + neteq_decoder = kDecoderOpus_2ch; + } + + CriticalSectionScoped lock(crit_sect_.get()); // The corresponding NetEq decoder ID. // If this coder has been registered before. if (decoders_[acm_codec_id].registered) { - if (decoders_[acm_codec_id].payload_type == payload_type) { + if (decoders_[acm_codec_id].payload_type == payload_type && + decoders_[acm_codec_id].channels == channels) { // Re-registering the same codec with the same payload-type. Do nothing // and return. return 0; } - // Changing the payload-type of this codec. First unregister. Then register - // with new payload-type. + // Changing the payload-type or number of channels for this codec. + // First unregister. Then register with new payload-type/channels. if (neteq_->RemovePayloadType(decoders_[acm_codec_id].payload_type) != NetEq::kOK) { LOG_F(LS_ERROR) << "Cannot remover payload " @@ -499,8 +515,7 @@ ret_val = neteq_->RegisterPayloadType(neteq_decoder, payload_type); } else { ret_val = neteq_->RegisterExternalDecoder( - audio_decoder, neteq_decoder, - ACMCodecDB::database_[acm_codec_id].plfreq, payload_type); + audio_decoder, neteq_decoder, payload_type); } if (ret_val != NetEq::kOK) { LOG_FERR3(LS_ERROR, "AcmReceiver::AddCodec", acm_codec_id, payload_type, @@ -519,13 +534,13 @@ void AcmReceiver::EnableVad() { neteq_->EnableVad(); - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); vad_enabled_ = true; } void AcmReceiver::DisableVad() { neteq_->DisableVad(); - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); vad_enabled_ = false; } @@ -537,7 +552,7 @@ // many as it can. int AcmReceiver::RemoveAllCodecs() { int ret_val = 0; - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); for (int n = 0; n < ACMCodecDB::kMaxNumCodecs; ++n) { if (decoders_[n].registered) { if (neteq_->RemovePayloadType(decoders_[n].payload_type) == 0) { @@ -557,15 +572,13 @@ int AcmReceiver::RemoveCodec(uint8_t payload_type) { int codec_index = PayloadType2CodecIndex(payload_type); if (codec_index < 0) { // Such a payload-type is not registered. - LOG(LS_WARNING) << "payload_type " << payload_type << " is not registered," - " no action is taken."; return 0; } if (neteq_->RemovePayloadType(payload_type) != NetEq::kOK) { LOG_FERR1(LS_ERROR, "AcmReceiver::RemoveCodec", payload_type); return -1; } - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); decoders_[codec_index].registered = false; if (last_audio_decoder_ == codec_index) last_audio_decoder_ = -1; // Codec is removed, invalidate last decoder. @@ -573,26 +586,27 @@ } void AcmReceiver::set_id(int id) { - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); id_ = id; } -uint32_t AcmReceiver::PlayoutTimestamp() { +bool AcmReceiver::GetPlayoutTimestamp(uint32_t* timestamp) { if (av_sync_) { assert(initial_delay_manager_.get()); - if (initial_delay_manager_->buffering()) - return initial_delay_manager_->playout_timestamp(); + if (initial_delay_manager_->buffering()) { + return initial_delay_manager_->GetPlayoutTimestamp(timestamp); + } } - return neteq_->PlayoutTimestamp(); + return neteq_->GetPlayoutTimestamp(timestamp); } int AcmReceiver::last_audio_codec_id() const { - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); return last_audio_decoder_; } int AcmReceiver::last_audio_payload_type() const { - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); if (last_audio_decoder_ < 0) return -1; assert(decoders_[last_audio_decoder_].registered); @@ -600,7 +614,7 @@ } int AcmReceiver::RedPayloadType() const { - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); if (ACMCodecDB::kRED < 0 || !decoders_[ACMCodecDB::kRED].registered) { LOG_F(LS_WARNING) << "RED is not registered."; @@ -610,9 +624,8 @@ } int AcmReceiver::LastAudioCodec(CodecInst* codec) const { - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); if (last_audio_decoder_ < 0) { - LOG_F(LS_WARNING) << "No audio payload is received, yet."; return -1; } assert(decoders_[last_audio_decoder_].registered); @@ -636,6 +649,7 @@ acm_stat->currentPreemptiveRate = neteq_stat.preemptive_rate; acm_stat->currentAccelerateRate = neteq_stat.accelerate_rate; acm_stat->clockDriftPPM = neteq_stat.clockdrift_ppm; + acm_stat->addedSamples = neteq_stat.added_zero_samples; std::vector waiting_times; neteq_->WaitingTimes(&waiting_times); @@ -665,7 +679,7 @@ int AcmReceiver::DecoderByPayloadType(uint8_t payload_type, CodecInst* codec) const { - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); int codec_index = PayloadType2CodecIndex(payload_type); if (codec_index < 0) { LOG_FERR1(LS_ERROR, "AcmReceiver::DecoderByPayloadType", payload_type); @@ -691,7 +705,7 @@ if (max_nack_list_size == 0 || max_nack_list_size > Nack::kNackListSizeLimit) return -1; - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); if (!nack_enabled_) { nack_.reset(Nack::Create(kNackThresholdPackets)); nack_enabled_ = true; @@ -707,14 +721,14 @@ } void AcmReceiver::DisableNack() { - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); nack_.reset(); // Memory is released. nack_enabled_ = false; } std::vector AcmReceiver::GetNackList( int round_trip_time_ms) const { - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); if (round_trip_time_ms < 0) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, "GetNackList: round trip time cannot be negative." @@ -730,7 +744,7 @@ void AcmReceiver::ResetInitialDelay() { { - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); av_sync_ = false; initial_delay_manager_.reset(NULL); missing_packets_sync_stream_.reset(NULL); @@ -752,13 +766,9 @@ // exceeds a threshold. int num_packets; int max_num_packets; - int buffer_size_byte; - int max_buffer_size_byte; const float kBufferingThresholdScale = 0.9f; - neteq_->PacketBufferStatistics(&num_packets, &max_num_packets, - &buffer_size_byte, &max_buffer_size_byte); - if (num_packets > max_num_packets * kBufferingThresholdScale || - buffer_size_byte > max_buffer_size_byte * kBufferingThresholdScale) { + neteq_->PacketBufferStatistics(&num_packets, &max_num_packets); + if (num_packets > max_num_packets * kBufferingThresholdScale) { initial_delay_manager_->DisableBuffering(); return false; } @@ -771,7 +781,6 @@ current_sample_rate_hz_ = ACMCodecDB::database_[last_audio_decoder_].plfreq; frame->num_channels_ = decoders_[last_audio_decoder_].channels; } else { - current_sample_rate_hz_ = kNeteqInitSampleRateHz; frame->num_channels_ = 1; } @@ -785,16 +794,11 @@ frame->samples_per_channel_ = frame->sample_rate_hz_ / 100; // Always 10 ms. frame->speech_type_ = AudioFrame::kCNG; frame->vad_activity_ = AudioFrame::kVadPassive; - frame->energy_ = 0; int samples = frame->samples_per_channel_ * frame->num_channels_; memset(frame->data_, 0, samples * sizeof(int16_t)); return true; } -NetEqBackgroundNoiseMode AcmReceiver::BackgroundNoiseModeForTest() const { - return neteq_->BackgroundNoiseMode(); -} - int AcmReceiver::RtpHeaderToCodecIndex( const RTPHeader &rtp_header, const uint8_t* payload) const { uint8_t payload_type = rtp_header.payloadType; @@ -815,7 +819,7 @@ // We masked 6 most significant bits of 32-bit so there is no overflow in // the conversion from milliseconds to timestamp. const uint32_t now_in_ms = static_cast( - TickTime::MillisecondTimestamp() & 0x03ffffff); + clock_->TimeInMilliseconds() & 0x03ffffff); return static_cast( (decoder_sampling_rate / 1000) * now_in_ms); } @@ -839,7 +843,7 @@ void AcmReceiver::GetDecodingCallStatistics( AudioDecodingCallStats* stats) const { - CriticalSectionScoped lock(neteq_crit_sect_); + CriticalSectionScoped lock(crit_sect_.get()); *stats = call_stats_.GetDecodingStatistics(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receiver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receiver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receiver.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receiver.h 2015-02-03 14:33:34.000000000 +0000 @@ -13,6 +13,7 @@ #include +#include "webrtc/base/thread_annotations.h" #include "webrtc/common_audio/vad/include/webrtc_vad.h" #include "webrtc/engine_configurations.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" @@ -20,7 +21,7 @@ #include "webrtc/modules/audio_coding/main/acm2/acm_resampler.h" #include "webrtc/modules/audio_coding/main/acm2/call_statistics.h" #include "webrtc/modules/audio_coding/main/acm2/initial_delay_manager.h" -#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" #include "webrtc/modules/interface/module_common_types.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/typedefs.h" @@ -29,7 +30,6 @@ struct CodecInst; class CriticalSectionWrapper; -class RWLockWrapper; class NetEq; namespace acm2 { @@ -47,9 +47,7 @@ }; // Constructor of the class - AcmReceiver(); - - explicit AcmReceiver(NetEq* neteq); + explicit AcmReceiver(const AudioCodingModule::Config& config); // Destructor of the class. ~AcmReceiver(); @@ -211,13 +209,6 @@ bool vad_enabled() const { return vad_enabled_; } // - // Get the decode lock used to protect decoder instances while decoding. - // - // Return value : Pointer to the decode lock. - // - RWLockWrapper* DecodeLock() const { return decode_lock_; } - - // // Flushes the NetEq packet and speech buffers. // void FlushBuffers(); @@ -244,9 +235,10 @@ void set_id(int id); // TODO(turajs): can be inline. // - // Returns the RTP timestamp of the last sample delivered by GetAudio(). + // Gets the RTP timestamp of the last sample delivered by GetAudio(). + // Returns true if the RTP timestamp is valid, otherwise false. // - uint32_t PlayoutTimestamp(); + bool GetPlayoutTimestamp(uint32_t* timestamp); // // Return the index of the codec associated with the last non-CNG/non-DTMF @@ -316,19 +308,14 @@ std::vector GetNackList(int round_trip_time_ms) const; // - // Returns the background noise mode. This is only for testing and ACM is not - // calling this function. Used in acm_receiver_unittest.cc. - // - NetEqBackgroundNoiseMode BackgroundNoiseModeForTest() const; - - // // Get statistics of calls to GetAudio(). void GetDecodingCallStatistics(AudioDecodingCallStats* stats) const; private: int PayloadType2CodecIndex(uint8_t payload_type) const; - bool GetSilence(int desired_sample_rate_hz, AudioFrame* frame); + bool GetSilence(int desired_sample_rate_hz, AudioFrame* frame) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); int GetNumSyncPacketToInsert(uint16_t received_squence_number); @@ -339,20 +326,24 @@ void InsertStreamOfSyncPackets(InitialDelayManager::SyncStream* sync_stream); - int id_; + scoped_ptr crit_sect_; + int id_; // TODO(henrik.lundin) Make const. + int last_audio_decoder_ GUARDED_BY(crit_sect_); + AudioFrame::VADActivity previous_audio_activity_ GUARDED_BY(crit_sect_); + int current_sample_rate_hz_ GUARDED_BY(crit_sect_); + ACMResampler resampler_ GUARDED_BY(crit_sect_); + // Used in GetAudio, declared as member to avoid allocating every 10ms. + // TODO(henrik.lundin) Stack-allocate in GetAudio instead? + scoped_ptr audio_buffer_ GUARDED_BY(crit_sect_); + scoped_ptr last_audio_buffer_ GUARDED_BY(crit_sect_); + scoped_ptr nack_ GUARDED_BY(crit_sect_); + bool nack_enabled_ GUARDED_BY(crit_sect_); + CallStatistics call_stats_ GUARDED_BY(crit_sect_); NetEq* neteq_; Decoder decoders_[ACMCodecDB::kMaxNumCodecs]; - int last_audio_decoder_; - RWLockWrapper* decode_lock_; - CriticalSectionWrapper* neteq_crit_sect_; bool vad_enabled_; - AudioFrame::VADActivity previous_audio_activity_; - int current_sample_rate_hz_; - ACMResampler resampler_; - // Used in GetAudio, declared as member to avoid allocating every 10ms. - int16_t audio_buffer_[AudioFrame::kMaxDataSizeSamples]; - scoped_ptr nack_; - bool nack_enabled_; + Clock* clock_; // TODO(henrik.lundin) Make const if possible. + bool resampled_last_output_frame_ GUARDED_BY(crit_sect_); // Indicates if a non-zero initial delay is set, and the receiver is in // AV-sync mode. @@ -366,8 +357,6 @@ // initial delay is set. scoped_ptr missing_packets_sync_stream_; scoped_ptr late_packets_sync_stream_; - - CallStatistics call_stats_; }; } // namespace acm2 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receiver_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receiver_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receiver_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receiver_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -12,11 +12,12 @@ #include // std::min -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" #include "webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h" #include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h" -#include "webrtc/modules/audio_coding/neteq4/tools/rtp_generator.h" +#include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h" +#include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/test/test_suite.h" #include "webrtc/test/testsupport/fileutils.h" @@ -42,26 +43,25 @@ public ::testing::Test { protected: AcmReceiverTest() - : receiver_(new AcmReceiver), - acm_(new AudioCodingModuleImpl(0)), - timestamp_(0), + : timestamp_(0), packet_sent_(false), last_packet_send_timestamp_(timestamp_), - last_frame_type_(kFrameEmpty) {} + last_frame_type_(kFrameEmpty) { + AudioCoding::Config config; + config.transport = this; + acm_.reset(new AudioCodingImpl(config)); + receiver_.reset(new AcmReceiver(config.ToOldConfig())); + } ~AcmReceiverTest() {} - void SetUp() { + virtual void SetUp() OVERRIDE { ASSERT_TRUE(receiver_.get() != NULL); ASSERT_TRUE(acm_.get() != NULL); for (int n = 0; n < ACMCodecDB::kNumCodecs; n++) { ASSERT_EQ(0, ACMCodecDB::Codec(n, &codecs_[n])); } - acm_->InitializeReceiver(); - acm_->InitializeSender(); - acm_->RegisterTransportCallback(this); - rtp_header_.header.sequenceNumber = 0; rtp_header_.header.timestamp = 0; rtp_header_.header.markerBit = false; @@ -72,19 +72,19 @@ rtp_header_.type.Audio.isCNG = false; } - void TearDown() { + virtual void TearDown() OVERRIDE { } void InsertOnePacketOfSilence(int codec_id) { CodecInst codec; ACMCodecDB::Codec(codec_id, &codec); if (timestamp_ == 0) { // This is the first time inserting audio. - ASSERT_EQ(0, acm_->RegisterSendCodec(codec)); + ASSERT_TRUE(acm_->RegisterSendCodec(codec_id, codec.pltype)); } else { - CodecInst current_codec; - ASSERT_EQ(0, acm_->SendCodec(¤t_codec)); - if (!CodecsEqual(codec, current_codec)) - ASSERT_EQ(0, acm_->RegisterSendCodec(codec)); + const CodecInst* current_codec = acm_->GetSenderCodecInst(); + ASSERT_TRUE(current_codec); + if (!CodecsEqual(codec, *current_codec)) + ASSERT_TRUE(acm_->RegisterSendCodec(codec_id, codec.pltype)); } AudioFrame frame; // Frame setup according to the codec. @@ -99,8 +99,7 @@ while (num_bytes == 0) { frame.timestamp_ = timestamp_; timestamp_ += frame.samples_per_channel_; - ASSERT_EQ(0, acm_->Add10MsData(frame)); - num_bytes = acm_->Process(); + num_bytes = acm_->Add10MsAudio(frame); ASSERT_GE(num_bytes, 0); } ASSERT_TRUE(packet_sent_); // Sanity check. @@ -122,7 +121,7 @@ uint32_t timestamp, const uint8_t* payload_data, uint16_t payload_len_bytes, - const RTPFragmentationHeader* fragmentation) { + const RTPFragmentationHeader* fragmentation) OVERRIDE { if (frame_type == kFrameEmpty) return 0; @@ -148,7 +147,7 @@ scoped_ptr receiver_; CodecInst codecs_[ACMCodecDB::kMaxNumCodecs]; - scoped_ptr acm_; + scoped_ptr acm_; WebRtcRTPHeader rtp_header_; uint32_t timestamp_; bool packet_sent_; // Set when SendData is called reset when inserting audio. @@ -244,34 +243,19 @@ } } -// Changing playout mode to FAX should not change the background noise mode. -TEST_F(AcmReceiverTest, - DISABLED_ON_ANDROID(PlayoutModeAndBackgroundNoiseMode)) { - EXPECT_EQ(kBgnOn, receiver_->BackgroundNoiseModeForTest()); // Default - +// Verify that the playout mode is set correctly. +TEST_F(AcmReceiverTest, DISABLED_ON_ANDROID(PlayoutMode)) { receiver_->SetPlayoutMode(voice); EXPECT_EQ(voice, receiver_->PlayoutMode()); - EXPECT_EQ(kBgnOn, receiver_->BackgroundNoiseModeForTest()); receiver_->SetPlayoutMode(streaming); EXPECT_EQ(streaming, receiver_->PlayoutMode()); - EXPECT_EQ(kBgnOff, receiver_->BackgroundNoiseModeForTest()); receiver_->SetPlayoutMode(fax); EXPECT_EQ(fax, receiver_->PlayoutMode()); - EXPECT_EQ(kBgnOff, receiver_->BackgroundNoiseModeForTest()); receiver_->SetPlayoutMode(off); EXPECT_EQ(off, receiver_->PlayoutMode()); - EXPECT_EQ(kBgnOff, receiver_->BackgroundNoiseModeForTest()); - - // Change to voice then to FAX. - receiver_->SetPlayoutMode(voice); - EXPECT_EQ(voice, receiver_->PlayoutMode()); - EXPECT_EQ(kBgnOn, receiver_->BackgroundNoiseModeForTest()); - receiver_->SetPlayoutMode(fax); - EXPECT_EQ(fax, receiver_->PlayoutMode()); - EXPECT_EQ(kBgnOn, receiver_->BackgroundNoiseModeForTest()); } TEST_F(AcmReceiverTest, DISABLED_ON_ANDROID(PostdecodingVad)) { @@ -302,55 +286,6 @@ EXPECT_EQ(AudioFrame::kVadUnknown, frame.vad_activity_); } -TEST_F(AcmReceiverTest, DISABLED_ON_ANDROID(FlushBuffer)) { - const int id = ACMCodecDB::kISAC; - EXPECT_EQ(0, receiver_->AddCodec(id, codecs_[id].pltype, codecs_[id].channels, - NULL)); - const int kNumPackets = 5; - const int num_10ms_frames = codecs_[id].pacsize / (codecs_[id].plfreq / 100); - for (int n = 0; n < kNumPackets; ++n) - InsertOnePacketOfSilence(id); - ACMNetworkStatistics statistics; - receiver_->NetworkStatistics(&statistics); - ASSERT_EQ(num_10ms_frames * kNumPackets * 10, statistics.currentBufferSize); - - receiver_->FlushBuffers(); - receiver_->NetworkStatistics(&statistics); - ASSERT_EQ(0, statistics.currentBufferSize); -} - -TEST_F(AcmReceiverTest, DISABLED_ON_ANDROID(PlayoutTimestamp)) { - const int id = ACMCodecDB::kPCM16Bwb; - EXPECT_EQ(0, receiver_->AddCodec(id, codecs_[id].pltype, codecs_[id].channels, - NULL)); - receiver_->SetPlayoutMode(fax); - const int kNumPackets = 5; - const int num_10ms_frames = codecs_[id].pacsize / (codecs_[id].plfreq / 100); - uint32_t expected_timestamp; - AudioFrame frame; - int ts_offset = 0; - bool first_audio_frame = true; - for (int n = 0; n < kNumPackets; ++n) { - packet_sent_ = false; - InsertOnePacketOfSilence(id); - ASSERT_TRUE(packet_sent_); - expected_timestamp = last_packet_send_timestamp_; - for (int k = 0; k < num_10ms_frames; ++k) { - ASSERT_EQ(0, receiver_->GetAudio(codecs_[id].plfreq, &frame)); - if (first_audio_frame) { - // There is an offset in playout timestamps. Perhaps, it is related to - // initial delay that NetEq applies - ts_offset = receiver_->PlayoutTimestamp() - expected_timestamp; - first_audio_frame = false; - } else { - EXPECT_EQ(expected_timestamp + ts_offset, - receiver_->PlayoutTimestamp()); - } - expected_timestamp += codecs_[id].plfreq / 100; // Increment by 10 ms. - } - } -} - TEST_F(AcmReceiverTest, DISABLED_ON_ANDROID(LastAudioCodec)) { const int kCodecId[] = { ACMCodecDB::kISAC, ACMCodecDB::kPCMA, ACMCodecDB::kISACSWB, @@ -368,7 +303,7 @@ // Register CNG at sender side. int n = 0; while (kCngId[n] > 0) { - ASSERT_EQ(0, acm_->RegisterSendCodec(codecs_[kCngId[n]])); + ASSERT_TRUE(acm_->RegisterSendCodec(kCngId[n], codecs_[kCngId[n]].pltype)); ++n; } @@ -377,7 +312,7 @@ EXPECT_EQ(-1, receiver_->LastAudioCodec(&codec)); // Start with sending DTX. - ASSERT_EQ(0, acm_->SetVAD(true, true, VADVeryAggr)); + ASSERT_TRUE(acm_->SetVad(true, true, VADVeryAggr)); packet_sent_ = false; InsertOnePacketOfSilence(kCodecId[0]); // Enough to test with one codec. ASSERT_TRUE(packet_sent_); @@ -391,7 +326,7 @@ n = 0; while (kCodecId[n] >= 0) { // Loop over codecs. // Set DTX off to send audio payload. - acm_->SetVAD(false, false, VADAggr); + acm_->SetVad(false, false, VADAggr); packet_sent_ = false; InsertOnePacketOfSilence(kCodecId[n]); @@ -403,7 +338,7 @@ // Set VAD on to send DTX. Then check if the "Last Audio codec" returns // the expected codec. - acm_->SetVAD(true, true, VADAggr); + acm_->SetVad(true, true, VADAggr); // Do as many encoding until a DTX is sent. while (last_frame_type_ != kAudioFrameCN) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receiver_unittest_oldapi.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receiver_unittest_oldapi.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receiver_unittest_oldapi.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receiver_unittest_oldapi.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/main/acm2/acm_receiver.h" + +#include // std::min + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" +#include "webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h" +#include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h" +#include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/test/test_suite.h" +#include "webrtc/test/testsupport/fileutils.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace webrtc { + +namespace acm2 { +namespace { + +bool CodecsEqual(const CodecInst& codec_a, const CodecInst& codec_b) { + if (strcmp(codec_a.plname, codec_b.plname) != 0 || + codec_a.plfreq != codec_b.plfreq || + codec_a.pltype != codec_b.pltype || + codec_b.channels != codec_a.channels) + return false; + return true; +} + +} // namespace + +class AcmReceiverTestOldApi : public AudioPacketizationCallback, + public ::testing::Test { + protected: + AcmReceiverTestOldApi() + : timestamp_(0), + packet_sent_(false), + last_packet_send_timestamp_(timestamp_), + last_frame_type_(kFrameEmpty) { + AudioCodingModule::Config config; + acm_.reset(new AudioCodingModuleImpl(config)); + receiver_.reset(new AcmReceiver(config)); + } + + ~AcmReceiverTestOldApi() {} + + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(receiver_.get() != NULL); + ASSERT_TRUE(acm_.get() != NULL); + for (int n = 0; n < ACMCodecDB::kNumCodecs; n++) { + ASSERT_EQ(0, ACMCodecDB::Codec(n, &codecs_[n])); + } + + acm_->InitializeReceiver(); + acm_->InitializeSender(); + acm_->RegisterTransportCallback(this); + + rtp_header_.header.sequenceNumber = 0; + rtp_header_.header.timestamp = 0; + rtp_header_.header.markerBit = false; + rtp_header_.header.ssrc = 0x12345678; // Arbitrary. + rtp_header_.header.numCSRCs = 0; + rtp_header_.header.payloadType = 0; + rtp_header_.frameType = kAudioFrameSpeech; + rtp_header_.type.Audio.isCNG = false; + } + + virtual void TearDown() OVERRIDE { + } + + void InsertOnePacketOfSilence(int codec_id) { + CodecInst codec; + ACMCodecDB::Codec(codec_id, &codec); + if (timestamp_ == 0) { // This is the first time inserting audio. + ASSERT_EQ(0, acm_->RegisterSendCodec(codec)); + } else { + CodecInst current_codec; + ASSERT_EQ(0, acm_->SendCodec(¤t_codec)); + if (!CodecsEqual(codec, current_codec)) + ASSERT_EQ(0, acm_->RegisterSendCodec(codec)); + } + AudioFrame frame; + // Frame setup according to the codec. + frame.sample_rate_hz_ = codec.plfreq; + frame.samples_per_channel_ = codec.plfreq / 100; // 10 ms. + frame.num_channels_ = codec.channels; + memset(frame.data_, 0, frame.samples_per_channel_ * frame.num_channels_ * + sizeof(int16_t)); + int num_bytes = 0; + packet_sent_ = false; + last_packet_send_timestamp_ = timestamp_; + while (num_bytes == 0) { + frame.timestamp_ = timestamp_; + timestamp_ += frame.samples_per_channel_; + ASSERT_EQ(0, acm_->Add10MsData(frame)); + num_bytes = acm_->Process(); + ASSERT_GE(num_bytes, 0); + } + ASSERT_TRUE(packet_sent_); // Sanity check. + } + + // Last element of id should be negative. + void AddSetOfCodecs(const int* id) { + int n = 0; + while (id[n] >= 0) { + ASSERT_EQ(0, receiver_->AddCodec(id[n], codecs_[id[n]].pltype, + codecs_[id[n]].channels, NULL)); + ++n; + } + } + + virtual int SendData( + FrameType frame_type, + uint8_t payload_type, + uint32_t timestamp, + const uint8_t* payload_data, + uint16_t payload_len_bytes, + const RTPFragmentationHeader* fragmentation) OVERRIDE { + if (frame_type == kFrameEmpty) + return 0; + + rtp_header_.header.payloadType = payload_type; + rtp_header_.frameType = frame_type; + if (frame_type == kAudioFrameSpeech) + rtp_header_.type.Audio.isCNG = false; + else + rtp_header_.type.Audio.isCNG = true; + rtp_header_.header.timestamp = timestamp; + + int ret_val = receiver_->InsertPacket(rtp_header_, payload_data, + payload_len_bytes); + if (ret_val < 0) { + assert(false); + return -1; + } + rtp_header_.header.sequenceNumber++; + packet_sent_ = true; + last_frame_type_ = frame_type; + return 0; + } + + scoped_ptr receiver_; + CodecInst codecs_[ACMCodecDB::kMaxNumCodecs]; + scoped_ptr acm_; + WebRtcRTPHeader rtp_header_; + uint32_t timestamp_; + bool packet_sent_; // Set when SendData is called reset when inserting audio. + uint32_t last_packet_send_timestamp_; + FrameType last_frame_type_; +}; + +TEST_F(AcmReceiverTestOldApi, DISABLED_ON_ANDROID(AddCodecGetCodec)) { + // Add codec. + for (int n = 0; n < ACMCodecDB::kNumCodecs; ++n) { + if (n & 0x1) // Just add codecs with odd index. + EXPECT_EQ(0, receiver_->AddCodec(n, codecs_[n].pltype, + codecs_[n].channels, NULL)); + } + // Get codec and compare. + for (int n = 0; n < ACMCodecDB::kNumCodecs; ++n) { + CodecInst my_codec; + if (n & 0x1) { + // Codecs with odd index should match the reference. + EXPECT_EQ(0, receiver_->DecoderByPayloadType(codecs_[n].pltype, + &my_codec)); + EXPECT_TRUE(CodecsEqual(codecs_[n], my_codec)); + } else { + // Codecs with even index are not registered. + EXPECT_EQ(-1, receiver_->DecoderByPayloadType(codecs_[n].pltype, + &my_codec)); + } + } +} + +TEST_F(AcmReceiverTestOldApi, DISABLED_ON_ANDROID(AddCodecChangePayloadType)) { + CodecInst ref_codec; + const int codec_id = ACMCodecDB::kPCMA; + EXPECT_EQ(0, ACMCodecDB::Codec(codec_id, &ref_codec)); + const int payload_type = ref_codec.pltype; + EXPECT_EQ(0, receiver_->AddCodec(codec_id, ref_codec.pltype, + ref_codec.channels, NULL)); + CodecInst test_codec; + EXPECT_EQ(0, receiver_->DecoderByPayloadType(payload_type, &test_codec)); + EXPECT_EQ(true, CodecsEqual(ref_codec, test_codec)); + + // Re-register the same codec with different payload. + ref_codec.pltype = payload_type + 1; + EXPECT_EQ(0, receiver_->AddCodec(codec_id, ref_codec.pltype, + ref_codec.channels, NULL)); + + // Payload type |payload_type| should not exist. + EXPECT_EQ(-1, receiver_->DecoderByPayloadType(payload_type, &test_codec)); + + // Payload type |payload_type + 1| should exist. + EXPECT_EQ(0, receiver_->DecoderByPayloadType(payload_type + 1, &test_codec)); + EXPECT_TRUE(CodecsEqual(test_codec, ref_codec)); +} + +TEST_F(AcmReceiverTestOldApi, DISABLED_ON_ANDROID(AddCodecRemoveCodec)) { + CodecInst codec; + const int codec_id = ACMCodecDB::kPCMA; + EXPECT_EQ(0, ACMCodecDB::Codec(codec_id, &codec)); + const int payload_type = codec.pltype; + EXPECT_EQ(0, receiver_->AddCodec(codec_id, codec.pltype, + codec.channels, NULL)); + + // Remove non-existing codec should not fail. ACM1 legacy. + EXPECT_EQ(0, receiver_->RemoveCodec(payload_type + 1)); + + // Remove an existing codec. + EXPECT_EQ(0, receiver_->RemoveCodec(payload_type)); + + // Ask for the removed codec, must fail. + EXPECT_EQ(-1, receiver_->DecoderByPayloadType(payload_type, &codec)); +} + +TEST_F(AcmReceiverTestOldApi, DISABLED_ON_ANDROID(SampleRate)) { + const int kCodecId[] = { + ACMCodecDB::kISAC, ACMCodecDB::kISACSWB, ACMCodecDB::kISACFB, + -1 // Terminator. + }; + AddSetOfCodecs(kCodecId); + + AudioFrame frame; + const int kOutSampleRateHz = 8000; // Different than codec sample rate. + int n = 0; + while (kCodecId[n] >= 0) { + const int num_10ms_frames = codecs_[kCodecId[n]].pacsize / + (codecs_[kCodecId[n]].plfreq / 100); + InsertOnePacketOfSilence(kCodecId[n]); + for (int k = 0; k < num_10ms_frames; ++k) { + EXPECT_EQ(0, receiver_->GetAudio(kOutSampleRateHz, &frame)); + } + EXPECT_EQ(std::min(32000, codecs_[kCodecId[n]].plfreq), + receiver_->current_sample_rate_hz()); + ++n; + } +} + +// Verify that the playout mode is set correctly. +TEST_F(AcmReceiverTestOldApi, DISABLED_ON_ANDROID(PlayoutMode)) { + receiver_->SetPlayoutMode(voice); + EXPECT_EQ(voice, receiver_->PlayoutMode()); + + receiver_->SetPlayoutMode(streaming); + EXPECT_EQ(streaming, receiver_->PlayoutMode()); + + receiver_->SetPlayoutMode(fax); + EXPECT_EQ(fax, receiver_->PlayoutMode()); + + receiver_->SetPlayoutMode(off); + EXPECT_EQ(off, receiver_->PlayoutMode()); +} + +TEST_F(AcmReceiverTestOldApi, DISABLED_ON_ANDROID(PostdecodingVad)) { + receiver_->EnableVad(); + EXPECT_TRUE(receiver_->vad_enabled()); + + const int id = ACMCodecDB::kPCM16Bwb; + ASSERT_EQ(0, receiver_->AddCodec(id, codecs_[id].pltype, codecs_[id].channels, + NULL)); + const int kNumPackets = 5; + const int num_10ms_frames = codecs_[id].pacsize / (codecs_[id].plfreq / 100); + AudioFrame frame; + for (int n = 0; n < kNumPackets; ++n) { + InsertOnePacketOfSilence(id); + for (int k = 0; k < num_10ms_frames; ++k) + ASSERT_EQ(0, receiver_->GetAudio(codecs_[id].plfreq, &frame)); + } + EXPECT_EQ(AudioFrame::kVadPassive, frame.vad_activity_); + + receiver_->DisableVad(); + EXPECT_FALSE(receiver_->vad_enabled()); + + for (int n = 0; n < kNumPackets; ++n) { + InsertOnePacketOfSilence(id); + for (int k = 0; k < num_10ms_frames; ++k) + ASSERT_EQ(0, receiver_->GetAudio(codecs_[id].plfreq, &frame)); + } + EXPECT_EQ(AudioFrame::kVadUnknown, frame.vad_activity_); +} + +TEST_F(AcmReceiverTestOldApi, DISABLED_ON_ANDROID(LastAudioCodec)) { + const int kCodecId[] = { + ACMCodecDB::kISAC, ACMCodecDB::kPCMA, ACMCodecDB::kISACSWB, + ACMCodecDB::kPCM16Bswb32kHz, ACMCodecDB::kG722_1C_48, + -1 // Terminator. + }; + AddSetOfCodecs(kCodecId); + + const int kCngId[] = { // Not including full-band. + ACMCodecDB::kCNNB, ACMCodecDB::kCNWB, ACMCodecDB::kCNSWB, + -1 // Terminator. + }; + AddSetOfCodecs(kCngId); + + // Register CNG at sender side. + int n = 0; + while (kCngId[n] > 0) { + ASSERT_EQ(0, acm_->RegisterSendCodec(codecs_[kCngId[n]])); + ++n; + } + + CodecInst codec; + // No audio payload is received. + EXPECT_EQ(-1, receiver_->LastAudioCodec(&codec)); + + // Start with sending DTX. + ASSERT_EQ(0, acm_->SetVAD(true, true, VADVeryAggr)); + packet_sent_ = false; + InsertOnePacketOfSilence(kCodecId[0]); // Enough to test with one codec. + ASSERT_TRUE(packet_sent_); + EXPECT_EQ(kAudioFrameCN, last_frame_type_); + + // Has received, only, DTX. Last Audio codec is undefined. + EXPECT_EQ(-1, receiver_->LastAudioCodec(&codec)); + EXPECT_EQ(-1, receiver_->last_audio_codec_id()); + EXPECT_EQ(-1, receiver_->last_audio_payload_type()); + + n = 0; + while (kCodecId[n] >= 0) { // Loop over codecs. + // Set DTX off to send audio payload. + acm_->SetVAD(false, false, VADAggr); + packet_sent_ = false; + InsertOnePacketOfSilence(kCodecId[n]); + + // Sanity check if Actually an audio payload received, and it should be + // of type "speech." + ASSERT_TRUE(packet_sent_); + ASSERT_EQ(kAudioFrameSpeech, last_frame_type_); + EXPECT_EQ(kCodecId[n], receiver_->last_audio_codec_id()); + + // Set VAD on to send DTX. Then check if the "Last Audio codec" returns + // the expected codec. + acm_->SetVAD(true, true, VADAggr); + + // Do as many encoding until a DTX is sent. + while (last_frame_type_ != kAudioFrameCN) { + packet_sent_ = false; + InsertOnePacketOfSilence(kCodecId[n]); + ASSERT_TRUE(packet_sent_); + } + EXPECT_EQ(kCodecId[n], receiver_->last_audio_codec_id()); + EXPECT_EQ(codecs_[kCodecId[n]].pltype, + receiver_->last_audio_payload_type()); + EXPECT_EQ(0, receiver_->LastAudioCodec(&codec)); + EXPECT_TRUE(CodecsEqual(codecs_[kCodecId[n]], codec)); + ++n; + } +} + +} // namespace acm2 + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receive_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receive_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receive_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receive_test.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/main/acm2/acm_receive_test.h" + +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" +#include "webrtc/modules/audio_coding/neteq/tools/audio_sink.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet_source.h" + +namespace webrtc { +namespace test { + +AcmReceiveTest::AcmReceiveTest(PacketSource* packet_source, + AudioSink* audio_sink, + int output_freq_hz, + NumOutputChannels exptected_output_channels) + : clock_(0), + packet_source_(packet_source), + audio_sink_(audio_sink), + output_freq_hz_(output_freq_hz), + exptected_output_channels_(exptected_output_channels) { + webrtc::AudioCoding::Config config; + config.clock = &clock_; + config.playout_frequency_hz = output_freq_hz_; + acm_.reset(webrtc::AudioCoding::Create(config)); +} + +void AcmReceiveTest::RegisterDefaultCodecs() { + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kOpus, 120)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kISAC, 103)); +#ifndef WEBRTC_ANDROID + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kISACSWB, 104)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kISACFB, 105)); +#endif + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kPCM16B, 107)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kPCM16Bwb, 108)); + ASSERT_TRUE( + acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kPCM16Bswb32kHz, 109)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kPCM16B_2ch, 111)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kPCM16Bwb_2ch, 112)); + ASSERT_TRUE( + acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kPCM16Bswb32kHz_2ch, 113)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kPCMU, 0)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kPCMA, 8)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kPCMU_2ch, 110)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kPCMA_2ch, 118)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kILBC, 102)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kG722, 9)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kG722_2ch, 119)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kCNNB, 13)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kCNWB, 98)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kCNSWB, 99)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kRED, 127)); +} + +void AcmReceiveTest::RegisterNetEqTestCodecs() { + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kISAC, 103)); +#ifndef WEBRTC_ANDROID + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kISACSWB, 104)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kISACFB, 124)); +#endif + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kPCM16B, 93)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kPCM16Bwb, 94)); + ASSERT_TRUE( + acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kPCM16Bswb32kHz, 95)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kPCMU, 0)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kPCMA, 8)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kILBC, 102)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kG722, 9)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kCNNB, 13)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kCNWB, 98)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kCNSWB, 99)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kRED, 117)); +} + +void AcmReceiveTest::Run() { + for (scoped_ptr packet(packet_source_->NextPacket()); packet; + packet.reset(packet_source_->NextPacket())) { + // Pull audio until time to insert packet. + while (clock_.TimeInMilliseconds() < packet->time_ms()) { + AudioFrame output_frame; + EXPECT_TRUE(acm_->Get10MsAudio(&output_frame)); + EXPECT_EQ(output_freq_hz_, output_frame.sample_rate_hz_); + const int samples_per_block = output_freq_hz_ * 10 / 1000; + EXPECT_EQ(samples_per_block, output_frame.samples_per_channel_); + if (exptected_output_channels_ != kArbitraryChannels) { + if (output_frame.speech_type_ == webrtc::AudioFrame::kPLC) { + // Don't check number of channels for PLC output, since each test run + // usually starts with a short period of mono PLC before decoding the + // first packet. + } else { + EXPECT_EQ(exptected_output_channels_, output_frame.num_channels_); + } + } + ASSERT_TRUE(audio_sink_->WriteAudioFrame(output_frame)); + clock_.AdvanceTimeMilliseconds(10); + } + + // Insert packet after converting from RTPHeader to WebRtcRTPHeader. + WebRtcRTPHeader header; + header.header = packet->header(); + header.frameType = kAudioFrameSpeech; + memset(&header.type.Audio, 0, sizeof(RTPAudioHeader)); + EXPECT_TRUE( + acm_->InsertPacket(packet->payload(), + static_cast(packet->payload_length_bytes()), + header)) + << "Failure when inserting packet:" << std::endl + << " PT = " << static_cast(header.header.payloadType) << std::endl + << " TS = " << header.header.timestamp << std::endl + << " SN = " << header.header.sequenceNumber; + } +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receive_test.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receive_test.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receive_test.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receive_test.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_RECEIVE_TEST_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_RECEIVE_TEST_H_ + +#include "webrtc/base/constructormagic.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { +class AudioCoding; +struct CodecInst; + +namespace test { +class AudioSink; +class PacketSource; + +class AcmReceiveTest { + public: + enum NumOutputChannels { + kArbitraryChannels = 0, + kMonoOutput = 1, + kStereoOutput = 2 + }; + + AcmReceiveTest( + PacketSource* packet_source, + AudioSink* audio_sink, + int output_freq_hz, + NumOutputChannels exptected_output_channels); + virtual ~AcmReceiveTest() {} + + // Registers the codecs with default parameters from ACM. + void RegisterDefaultCodecs(); + + // Registers codecs with payload types matching the pre-encoded NetEq test + // files. + void RegisterNetEqTestCodecs(); + + // Runs the test and returns true if successful. + void Run(); + + private: + SimulatedClock clock_; + scoped_ptr acm_; + PacketSource* packet_source_; + AudioSink* audio_sink_; + const int output_freq_hz_; + NumOutputChannels exptected_output_channels_; + + DISALLOW_COPY_AND_ASSIGN(AcmReceiveTest); +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_RECEIVE_TEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h" + +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" +#include "webrtc/modules/audio_coding/neteq/tools/audio_sink.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet_source.h" + +namespace webrtc { +namespace test { + +namespace { +// Returns true if the codec should be registered, otherwise false. Changes +// the number of channels for the Opus codec to always be 1. +bool ModifyAndUseThisCodec(CodecInst* codec_param) { + if (STR_CASE_CMP(codec_param->plname, "CN") == 0 && + codec_param->plfreq == 48000) + return false; // Skip 48 kHz comfort noise. + + if (STR_CASE_CMP(codec_param->plname, "telephone-event") == 0) + return false; // Skip DTFM. + + return true; +} + +// Remaps payload types from ACM's default to those used in the resource file +// neteq_universal_new.rtp. Returns true if the codec should be registered, +// otherwise false. The payload types are set as follows (all are mono codecs): +// PCMu = 0; +// PCMa = 8; +// Comfort noise 8 kHz = 13 +// Comfort noise 16 kHz = 98 +// Comfort noise 32 kHz = 99 +// iLBC = 102 +// iSAC wideband = 103 +// iSAC super-wideband = 104 +// iSAC fullband = 124 +// AVT/DTMF = 106 +// RED = 117 +// PCM16b 8 kHz = 93 +// PCM16b 16 kHz = 94 +// PCM16b 32 kHz = 95 +// G.722 = 94 +bool RemapPltypeAndUseThisCodec(const char* plname, + int plfreq, + int channels, + int* pltype) { + if (channels != 1) + return false; // Don't use non-mono codecs. + + // Re-map pltypes to those used in the NetEq test files. + if (STR_CASE_CMP(plname, "PCMU") == 0 && plfreq == 8000) { + *pltype = 0; + } else if (STR_CASE_CMP(plname, "PCMA") == 0 && plfreq == 8000) { + *pltype = 8; + } else if (STR_CASE_CMP(plname, "CN") == 0 && plfreq == 8000) { + *pltype = 13; + } else if (STR_CASE_CMP(plname, "CN") == 0 && plfreq == 16000) { + *pltype = 98; + } else if (STR_CASE_CMP(plname, "CN") == 0 && plfreq == 32000) { + *pltype = 99; + } else if (STR_CASE_CMP(plname, "ILBC") == 0) { + *pltype = 102; + } else if (STR_CASE_CMP(plname, "ISAC") == 0 && plfreq == 16000) { + *pltype = 103; + } else if (STR_CASE_CMP(plname, "ISAC") == 0 && plfreq == 32000) { + *pltype = 104; + } else if (STR_CASE_CMP(plname, "ISAC") == 0 && plfreq == 48000) { + *pltype = 124; + } else if (STR_CASE_CMP(plname, "telephone-event") == 0) { + *pltype = 106; + } else if (STR_CASE_CMP(plname, "red") == 0) { + *pltype = 117; + } else if (STR_CASE_CMP(plname, "L16") == 0 && plfreq == 8000) { + *pltype = 93; + } else if (STR_CASE_CMP(plname, "L16") == 0 && plfreq == 16000) { + *pltype = 94; + } else if (STR_CASE_CMP(plname, "L16") == 0 && plfreq == 32000) { + *pltype = 95; + } else if (STR_CASE_CMP(plname, "G722") == 0) { + *pltype = 9; + } else { + // Don't use any other codecs. + return false; + } + return true; +} +} // namespace + +AcmReceiveTestOldApi::AcmReceiveTestOldApi( + PacketSource* packet_source, + AudioSink* audio_sink, + int output_freq_hz, + NumOutputChannels exptected_output_channels) + : clock_(0), + acm_(webrtc::AudioCodingModule::Create(0, &clock_)), + packet_source_(packet_source), + audio_sink_(audio_sink), + output_freq_hz_(output_freq_hz), + exptected_output_channels_(exptected_output_channels) { +} + +void AcmReceiveTestOldApi::RegisterDefaultCodecs() { + CodecInst my_codec_param; + for (int n = 0; n < acm_->NumberOfCodecs(); n++) { + ASSERT_EQ(0, acm_->Codec(n, &my_codec_param)) << "Failed to get codec."; + if (ModifyAndUseThisCodec(&my_codec_param)) { + ASSERT_EQ(0, acm_->RegisterReceiveCodec(my_codec_param)) + << "Couldn't register receive codec.\n"; + } + } +} + +void AcmReceiveTestOldApi::RegisterNetEqTestCodecs() { + CodecInst my_codec_param; + for (int n = 0; n < acm_->NumberOfCodecs(); n++) { + ASSERT_EQ(0, acm_->Codec(n, &my_codec_param)) << "Failed to get codec."; + if (!ModifyAndUseThisCodec(&my_codec_param)) { + // Skip this codec. + continue; + } + + if (RemapPltypeAndUseThisCodec(my_codec_param.plname, + my_codec_param.plfreq, + my_codec_param.channels, + &my_codec_param.pltype)) { + ASSERT_EQ(0, acm_->RegisterReceiveCodec(my_codec_param)) + << "Couldn't register receive codec.\n"; + } + } +} + +void AcmReceiveTestOldApi::Run() { + for (scoped_ptr packet(packet_source_->NextPacket()); packet; + packet.reset(packet_source_->NextPacket())) { + // Pull audio until time to insert packet. + while (clock_.TimeInMilliseconds() < packet->time_ms()) { + AudioFrame output_frame; + EXPECT_EQ(0, acm_->PlayoutData10Ms(output_freq_hz_, &output_frame)); + EXPECT_EQ(output_freq_hz_, output_frame.sample_rate_hz_); + const int samples_per_block = output_freq_hz_ * 10 / 1000; + EXPECT_EQ(samples_per_block, output_frame.samples_per_channel_); + if (exptected_output_channels_ != kArbitraryChannels) { + if (output_frame.speech_type_ == webrtc::AudioFrame::kPLC) { + // Don't check number of channels for PLC output, since each test run + // usually starts with a short period of mono PLC before decoding the + // first packet. + } else { + EXPECT_EQ(exptected_output_channels_, output_frame.num_channels_); + } + } + ASSERT_TRUE(audio_sink_->WriteAudioFrame(output_frame)); + clock_.AdvanceTimeMilliseconds(10); + AfterGetAudio(); + } + + // Insert packet after converting from RTPHeader to WebRtcRTPHeader. + WebRtcRTPHeader header; + header.header = packet->header(); + header.frameType = kAudioFrameSpeech; + memset(&header.type.Audio, 0, sizeof(RTPAudioHeader)); + EXPECT_EQ(0, + acm_->IncomingPacket( + packet->payload(), + static_cast(packet->payload_length_bytes()), + header)) + << "Failure when inserting packet:" << std::endl + << " PT = " << static_cast(header.header.payloadType) << std::endl + << " TS = " << header.header.timestamp << std::endl + << " SN = " << header.header.sequenceNumber; + } +} + +AcmReceiveTestToggleOutputFreqOldApi::AcmReceiveTestToggleOutputFreqOldApi( + PacketSource* packet_source, + AudioSink* audio_sink, + int output_freq_hz_1, + int output_freq_hz_2, + int toggle_period_ms, + NumOutputChannels exptected_output_channels) + : AcmReceiveTestOldApi(packet_source, + audio_sink, + output_freq_hz_1, + exptected_output_channels), + output_freq_hz_1_(output_freq_hz_1), + output_freq_hz_2_(output_freq_hz_2), + toggle_period_ms_(toggle_period_ms), + last_toggle_time_ms_(clock_.TimeInMilliseconds()) { +} + +void AcmReceiveTestToggleOutputFreqOldApi::AfterGetAudio() { + if (clock_.TimeInMilliseconds() >= last_toggle_time_ms_ + toggle_period_ms_) { + output_freq_hz_ = (output_freq_hz_ == output_freq_hz_1_) + ? output_freq_hz_2_ + : output_freq_hz_1_; + last_toggle_time_ms_ = clock_.TimeInMilliseconds(); + } +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_RECEIVE_TEST_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_RECEIVE_TEST_H_ + +#include "webrtc/base/constructormagic.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { +class AudioCodingModule; +struct CodecInst; + +namespace test { +class AudioSink; +class PacketSource; + +class AcmReceiveTestOldApi { + public: + enum NumOutputChannels { + kArbitraryChannels = 0, + kMonoOutput = 1, + kStereoOutput = 2 + }; + + AcmReceiveTestOldApi(PacketSource* packet_source, + AudioSink* audio_sink, + int output_freq_hz, + NumOutputChannels exptected_output_channels); + virtual ~AcmReceiveTestOldApi() {} + + // Registers the codecs with default parameters from ACM. + void RegisterDefaultCodecs(); + + // Registers codecs with payload types matching the pre-encoded NetEq test + // files. + void RegisterNetEqTestCodecs(); + + // Runs the test and returns true if successful. + void Run(); + + protected: + // Method is called after each block of output audio is received from ACM. + virtual void AfterGetAudio() {} + + SimulatedClock clock_; + scoped_ptr acm_; + PacketSource* packet_source_; + AudioSink* audio_sink_; + int output_freq_hz_; + NumOutputChannels exptected_output_channels_; + + DISALLOW_COPY_AND_ASSIGN(AcmReceiveTestOldApi); +}; + +// This test toggles the output frequency every |toggle_period_ms|. The test +// starts with |output_freq_hz_1|. Except for the toggling, it does the same +// thing as AcmReceiveTestOldApi. +class AcmReceiveTestToggleOutputFreqOldApi : public AcmReceiveTestOldApi { + public: + AcmReceiveTestToggleOutputFreqOldApi( + PacketSource* packet_source, + AudioSink* audio_sink, + int output_freq_hz_1, + int output_freq_hz_2, + int toggle_period_ms, + NumOutputChannels exptected_output_channels); + + protected: + void AfterGetAudio() OVERRIDE; + + const int output_freq_hz_1_; + const int output_freq_hz_2_; + const int toggle_period_ms_; + int64_t last_toggle_time_ms_; +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_RECEIVE_TEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_red.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_red.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_red.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_red.cc 2015-02-03 14:33:34.000000000 +0000 @@ -41,10 +41,6 @@ return 0; } -void ACMRED::InternalDestructEncoderInst(void* /* ptr_inst */) { - // RED has no instance -} - void ACMRED::DestructEncoderSafe() { // RED has no instance } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_red.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_red.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_red.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_red.h 2015-02-03 14:33:34.000000000 +0000 @@ -33,8 +33,6 @@ void DestructEncoderSafe(); int16_t InternalCreateEncoder(); - - void InternalDestructEncoderInst(void* ptr_inst); }; } // namespace acm2 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_resampler.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_resampler.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_resampler.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_resampler.cc 2015-02-03 14:33:34.000000000 +0000 @@ -10,61 +10,59 @@ #include "webrtc/modules/audio_coding/main/acm2/acm_resampler.h" +#include #include #include "webrtc/common_audio/resampler/include/resampler.h" -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { - namespace acm2 { -ACMResampler::ACMResampler() - : resampler_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()) { +ACMResampler::ACMResampler() { } ACMResampler::~ACMResampler() { - delete resampler_crit_sect_; } int ACMResampler::Resample10Msec(const int16_t* in_audio, int in_freq_hz, int out_freq_hz, int num_audio_channels, + int out_capacity_samples, int16_t* out_audio) { - CriticalSectionScoped cs(resampler_crit_sect_); - + int in_length = in_freq_hz * num_audio_channels / 100; + int out_length = out_freq_hz * num_audio_channels / 100; if (in_freq_hz == out_freq_hz) { - size_t length = static_cast(in_freq_hz * num_audio_channels / 100); - memcpy(out_audio, in_audio, length * sizeof(int16_t)); - return static_cast(in_freq_hz / 100); + if (out_capacity_samples < in_length) { + assert(false); + return -1; + } + memcpy(out_audio, in_audio, in_length * sizeof(int16_t)); + return in_length / num_audio_channels; } - // |maxLen| is maximum number of samples for 10ms at 48kHz. - int max_len = 480 * num_audio_channels; - int length_in = (in_freq_hz / 100) * num_audio_channels; - int out_len; - - ResamplerType type = (num_audio_channels == 1) ? kResamplerSynchronous : - kResamplerSynchronousStereo; - - if (resampler_.ResetIfNeeded(in_freq_hz, out_freq_hz, type) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0, - "Error in reset of resampler"); + if (resampler_.InitializeIfNeeded(in_freq_hz, out_freq_hz, + num_audio_channels) != 0) { + LOG_FERR3(LS_ERROR, InitializeIfNeeded, in_freq_hz, out_freq_hz, + num_audio_channels); return -1; } - if (resampler_.Push(in_audio, length_in, out_audio, max_len, out_len) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0, - "Error in resampler: resampler.Push"); + out_length = + resampler_.Resample(in_audio, in_length, out_audio, out_capacity_samples); + if (out_length == -1) { + LOG_FERR4(LS_ERROR, + Resample, + in_audio, + in_length, + out_audio, + out_capacity_samples); return -1; } - return out_len / num_audio_channels; + return out_length / num_audio_channels; } } // namespace acm2 - } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_resampler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_resampler.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_resampler.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_resampler.h 2015-02-03 14:33:34.000000000 +0000 @@ -11,13 +11,10 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_RESAMPLER_H_ #define WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_RESAMPLER_H_ -#include "webrtc/common_audio/resampler/include/resampler.h" +#include "webrtc/common_audio/resampler/include/push_resampler.h" #include "webrtc/typedefs.h" namespace webrtc { - -class CriticalSectionWrapper; - namespace acm2 { class ACMResampler { @@ -29,16 +26,14 @@ int in_freq_hz, int out_freq_hz, int num_audio_channels, + int out_capacity_samples, int16_t* out_audio); private: - // Use the Resampler class. - Resampler resampler_; - CriticalSectionWrapper* resampler_crit_sect_; + PushResampler resampler_; }; } // namespace acm2 - } // namespace webrtc #endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_RESAMPLER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_send_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_send_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_send_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_send_test.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/main/acm2/acm_send_test.h" + +#include +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/checks.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" +#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet.h" + +namespace webrtc { +namespace test { + +AcmSendTest::AcmSendTest(InputAudioFile* audio_source, + int source_rate_hz, + int test_duration_ms) + : clock_(0), + audio_source_(audio_source), + source_rate_hz_(source_rate_hz), + input_block_size_samples_(source_rate_hz_ * kBlockSizeMs / 1000), + codec_registered_(false), + test_duration_ms_(test_duration_ms), + frame_type_(kAudioFrameSpeech), + payload_type_(0), + timestamp_(0), + sequence_number_(0) { + webrtc::AudioCoding::Config config; + config.clock = &clock_; + config.transport = this; + acm_.reset(webrtc::AudioCoding::Create(config)); + input_frame_.sample_rate_hz_ = source_rate_hz_; + input_frame_.num_channels_ = 1; + input_frame_.samples_per_channel_ = input_block_size_samples_; + assert(input_block_size_samples_ * input_frame_.num_channels_ <= + AudioFrame::kMaxDataSizeSamples); +} + +bool AcmSendTest::RegisterCodec(int codec_type, + int channels, + int payload_type, + int frame_size_samples) { + codec_registered_ = + acm_->RegisterSendCodec(codec_type, payload_type, frame_size_samples); + input_frame_.num_channels_ = channels; + assert(input_block_size_samples_ * input_frame_.num_channels_ <= + AudioFrame::kMaxDataSizeSamples); + return codec_registered_; +} + +Packet* AcmSendTest::NextPacket() { + assert(codec_registered_); + if (filter_.test(payload_type_)) { + // This payload type should be filtered out. Since the payload type is the + // same throughout the whole test run, no packet at all will be delivered. + // We can just as well signal that the test is over by returning NULL. + return NULL; + } + // Insert audio and process until one packet is produced. + while (clock_.TimeInMilliseconds() < test_duration_ms_) { + clock_.AdvanceTimeMilliseconds(kBlockSizeMs); + CHECK(audio_source_->Read(input_block_size_samples_, input_frame_.data_)); + if (input_frame_.num_channels_ > 1) { + InputAudioFile::DuplicateInterleaved(input_frame_.data_, + input_block_size_samples_, + input_frame_.num_channels_, + input_frame_.data_); + } + int32_t encoded_bytes = acm_->Add10MsAudio(input_frame_); + EXPECT_GE(encoded_bytes, 0); + input_frame_.timestamp_ += input_block_size_samples_; + if (encoded_bytes > 0) { + // Encoded packet received. + return CreatePacket(); + } + } + // Test ended. + return NULL; +} + +// This method receives the callback from ACM when a new packet is produced. +int32_t AcmSendTest::SendData(FrameType frame_type, + uint8_t payload_type, + uint32_t timestamp, + const uint8_t* payload_data, + uint16_t payload_len_bytes, + const RTPFragmentationHeader* fragmentation) { + // Store the packet locally. + frame_type_ = frame_type; + payload_type_ = payload_type; + timestamp_ = timestamp; + last_payload_vec_.assign(payload_data, payload_data + payload_len_bytes); + assert(last_payload_vec_.size() == payload_len_bytes); + return 0; +} + +Packet* AcmSendTest::CreatePacket() { + const size_t kRtpHeaderSize = 12; + size_t allocated_bytes = last_payload_vec_.size() + kRtpHeaderSize; + uint8_t* packet_memory = new uint8_t[allocated_bytes]; + // Populate the header bytes. + packet_memory[0] = 0x80; + packet_memory[1] = payload_type_; + packet_memory[2] = (sequence_number_ >> 8) & 0xFF; + packet_memory[3] = (sequence_number_) & 0xFF; + packet_memory[4] = (timestamp_ >> 24) & 0xFF; + packet_memory[5] = (timestamp_ >> 16) & 0xFF; + packet_memory[6] = (timestamp_ >> 8) & 0xFF; + packet_memory[7] = timestamp_ & 0xFF; + // Set SSRC to 0x12345678. + packet_memory[8] = 0x12; + packet_memory[9] = 0x34; + packet_memory[10] = 0x56; + packet_memory[11] = 0x78; + + ++sequence_number_; + + // Copy the payload data. + memcpy(packet_memory + kRtpHeaderSize, + &last_payload_vec_[0], + last_payload_vec_.size()); + Packet* packet = + new Packet(packet_memory, allocated_bytes, clock_.TimeInMilliseconds()); + assert(packet); + assert(packet->valid_header()); + return packet; +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_send_test.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_send_test.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_send_test.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_send_test.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_SEND_TEST_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_SEND_TEST_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet_source.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +namespace test { +class InputAudioFile; +class Packet; + +class AcmSendTest : public AudioPacketizationCallback, public PacketSource { + public: + AcmSendTest(InputAudioFile* audio_source, + int source_rate_hz, + int test_duration_ms); + virtual ~AcmSendTest() {} + + // Registers the send codec. Returns true on success, false otherwise. + bool RegisterCodec(int codec_type, + int channels, + int payload_type, + int frame_size_samples); + + // Returns the next encoded packet. Returns NULL if the test duration was + // exceeded. Ownership of the packet is handed over to the caller. + // Inherited from PacketSource. + virtual Packet* NextPacket() OVERRIDE; + + // Inherited from AudioPacketizationCallback. + virtual int32_t SendData( + FrameType frame_type, + uint8_t payload_type, + uint32_t timestamp, + const uint8_t* payload_data, + uint16_t payload_len_bytes, + const RTPFragmentationHeader* fragmentation) OVERRIDE; + + private: + static const int kBlockSizeMs = 10; + + // Creates a Packet object from the last packet produced by ACM (and received + // through the SendData method as a callback). Ownership of the new Packet + // object is transferred to the caller. + Packet* CreatePacket(); + + SimulatedClock clock_; + scoped_ptr acm_; + InputAudioFile* audio_source_; + int source_rate_hz_; + const int input_block_size_samples_; + AudioFrame input_frame_; + bool codec_registered_; + int test_duration_ms_; + // The following member variables are set whenever SendData() is called. + FrameType frame_type_; + int payload_type_; + uint32_t timestamp_; + uint16_t sequence_number_; + std::vector last_payload_vec_; + + DISALLOW_COPY_AND_ASSIGN(AcmSendTest); +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_SEND_TEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.h" + +#include +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/checks.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" +#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet.h" + +namespace webrtc { +namespace test { + +AcmSendTestOldApi::AcmSendTestOldApi(InputAudioFile* audio_source, + int source_rate_hz, + int test_duration_ms) + : clock_(0), + acm_(webrtc::AudioCodingModule::Create(0, &clock_)), + audio_source_(audio_source), + source_rate_hz_(source_rate_hz), + input_block_size_samples_(source_rate_hz_ * kBlockSizeMs / 1000), + codec_registered_(false), + test_duration_ms_(test_duration_ms), + frame_type_(kAudioFrameSpeech), + payload_type_(0), + timestamp_(0), + sequence_number_(0) { + input_frame_.sample_rate_hz_ = source_rate_hz_; + input_frame_.num_channels_ = 1; + input_frame_.samples_per_channel_ = input_block_size_samples_; + assert(input_block_size_samples_ * input_frame_.num_channels_ <= + AudioFrame::kMaxDataSizeSamples); + acm_->RegisterTransportCallback(this); +} + +bool AcmSendTestOldApi::RegisterCodec(const char* payload_name, + int sampling_freq_hz, + int channels, + int payload_type, + int frame_size_samples) { + CHECK_EQ(0, + AudioCodingModule::Codec( + payload_name, &codec_, sampling_freq_hz, channels)); + codec_.pltype = payload_type; + codec_.pacsize = frame_size_samples; + codec_registered_ = (acm_->RegisterSendCodec(codec_) == 0); + input_frame_.num_channels_ = channels; + assert(input_block_size_samples_ * input_frame_.num_channels_ <= + AudioFrame::kMaxDataSizeSamples); + return codec_registered_; +} + +Packet* AcmSendTestOldApi::NextPacket() { + assert(codec_registered_); + if (filter_.test(payload_type_)) { + // This payload type should be filtered out. Since the payload type is the + // same throughout the whole test run, no packet at all will be delivered. + // We can just as well signal that the test is over by returning NULL. + return NULL; + } + // Insert audio and process until one packet is produced. + while (clock_.TimeInMilliseconds() < test_duration_ms_) { + clock_.AdvanceTimeMilliseconds(kBlockSizeMs); + CHECK(audio_source_->Read(input_block_size_samples_, input_frame_.data_)); + if (input_frame_.num_channels_ > 1) { + InputAudioFile::DuplicateInterleaved(input_frame_.data_, + input_block_size_samples_, + input_frame_.num_channels_, + input_frame_.data_); + } + CHECK_EQ(0, acm_->Add10MsData(input_frame_)); + input_frame_.timestamp_ += input_block_size_samples_; + int32_t encoded_bytes = acm_->Process(); + if (encoded_bytes > 0) { + // Encoded packet received. + return CreatePacket(); + } + } + // Test ended. + return NULL; +} + +// This method receives the callback from ACM when a new packet is produced. +int32_t AcmSendTestOldApi::SendData( + FrameType frame_type, + uint8_t payload_type, + uint32_t timestamp, + const uint8_t* payload_data, + uint16_t payload_len_bytes, + const RTPFragmentationHeader* fragmentation) { + // Store the packet locally. + frame_type_ = frame_type; + payload_type_ = payload_type; + timestamp_ = timestamp; + last_payload_vec_.assign(payload_data, payload_data + payload_len_bytes); + assert(last_payload_vec_.size() == payload_len_bytes); + return 0; +} + +Packet* AcmSendTestOldApi::CreatePacket() { + const size_t kRtpHeaderSize = 12; + size_t allocated_bytes = last_payload_vec_.size() + kRtpHeaderSize; + uint8_t* packet_memory = new uint8_t[allocated_bytes]; + // Populate the header bytes. + packet_memory[0] = 0x80; + packet_memory[1] = payload_type_; + packet_memory[2] = (sequence_number_ >> 8) & 0xFF; + packet_memory[3] = (sequence_number_) & 0xFF; + packet_memory[4] = (timestamp_ >> 24) & 0xFF; + packet_memory[5] = (timestamp_ >> 16) & 0xFF; + packet_memory[6] = (timestamp_ >> 8) & 0xFF; + packet_memory[7] = timestamp_ & 0xFF; + // Set SSRC to 0x12345678. + packet_memory[8] = 0x12; + packet_memory[9] = 0x34; + packet_memory[10] = 0x56; + packet_memory[11] = 0x78; + + ++sequence_number_; + + // Copy the payload data. + memcpy(packet_memory + kRtpHeaderSize, + &last_payload_vec_[0], + last_payload_vec_.size()); + Packet* packet = + new Packet(packet_memory, allocated_bytes, clock_.TimeInMilliseconds()); + assert(packet); + assert(packet->valid_header()); + return packet; +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_SEND_TEST_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_SEND_TEST_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet_source.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +namespace test { +class InputAudioFile; +class Packet; + +class AcmSendTestOldApi : public AudioPacketizationCallback, + public PacketSource { + public: + AcmSendTestOldApi(InputAudioFile* audio_source, + int source_rate_hz, + int test_duration_ms); + virtual ~AcmSendTestOldApi() {} + + // Registers the send codec. Returns true on success, false otherwise. + bool RegisterCodec(const char* payload_name, + int sampling_freq_hz, + int channels, + int payload_type, + int frame_size_samples); + + // Returns the next encoded packet. Returns NULL if the test duration was + // exceeded. Ownership of the packet is handed over to the caller. + // Inherited from PacketSource. + Packet* NextPacket(); + + // Inherited from AudioPacketizationCallback. + virtual int32_t SendData( + FrameType frame_type, + uint8_t payload_type, + uint32_t timestamp, + const uint8_t* payload_data, + uint16_t payload_len_bytes, + const RTPFragmentationHeader* fragmentation) OVERRIDE; + + private: + static const int kBlockSizeMs = 10; + + // Creates a Packet object from the last packet produced by ACM (and received + // through the SendData method as a callback). Ownership of the new Packet + // object is transferred to the caller. + Packet* CreatePacket(); + + SimulatedClock clock_; + scoped_ptr acm_; + InputAudioFile* audio_source_; + int source_rate_hz_; + const int input_block_size_samples_; + AudioFrame input_frame_; + CodecInst codec_; + bool codec_registered_; + int test_duration_ms_; + // The following member variables are set whenever SendData() is called. + FrameType frame_type_; + int payload_type_; + uint32_t timestamp_; + uint16_t sequence_number_; + std::vector last_payload_vec_; + + DISALLOW_COPY_AND_ASSIGN(AcmSendTestOldApi); +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_SEND_TEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_speex.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_speex.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_speex.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_speex.cc 2015-02-03 14:33:34.000000000 +0000 @@ -30,7 +30,7 @@ vbr_enabled_(false), encoding_rate_(-1), sampling_frequency_(-1), - samples_in_20ms_audio_(-1) { + samples_in_20ms_audio_(0xFFFF) { return; } @@ -58,8 +58,6 @@ int16_t ACMSPEEX::SetBitRateSafe(const int32_t /* rate */) { return -1; } -void ACMSPEEX::InternalDestructEncoderInst(void* /* ptr_inst */) { return; } - #ifdef UNUSEDSPEEX int16_t ACMSPEEX::EnableVBR() { return -1; } @@ -250,13 +248,6 @@ return 0; } -void ACMSPEEX::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcSpeex_FreeEnc(static_cast(ptr_inst)); - } - return; -} - #ifdef UNUSEDSPEEX // This API is currently not in use. If requested to be able to enable/disable diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_speex.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_speex.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_speex.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/acm_speex.h 2015-02-03 14:33:34.000000000 +0000 @@ -38,8 +38,6 @@ int16_t InternalCreateEncoder(); - void InternalDestructEncoderInst(void* ptr_inst); - int16_t SetBitRateSafe(const int32_t rate); int16_t EnableDTX(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module.cc 2015-02-03 14:33:34.000000000 +0000 @@ -13,22 +13,21 @@ #include "webrtc/common_types.h" #include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h" #include "webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h" -#include "webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h" #include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { -const char kLegacyAcmVersion[] = "acm1"; -const char kExperimentalAcmVersion[] = "acm2"; - // Create module AudioCodingModule* AudioCodingModule::Create(int id) { - return new acm1::AudioCodingModuleImpl(id, Clock::GetRealTimeClock()); + return Create(id, Clock::GetRealTimeClock()); } AudioCodingModule* AudioCodingModule::Create(int id, Clock* clock) { - return new acm1::AudioCodingModuleImpl(id, clock); + AudioCodingModule::Config config; + config.id = id; + config.clock = clock; + return new acm2::AudioCodingModuleImpl(config); } // Get number of supported codecs @@ -95,13 +94,8 @@ } } -AudioCodingModule* AudioCodingModuleFactory::Create(int id) const { - return new acm1::AudioCodingModuleImpl(static_cast(id), - Clock::GetRealTimeClock()); -} - -AudioCodingModule* NewAudioCodingModuleFactory::Create(int id) const { - return new acm2::AudioCodingModuleImpl(id); +AudioCoding* AudioCoding::Create(const Config& config) { + return new AudioCodingImpl(config); } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi 2015-02-03 14:33:34.000000000 +0000 @@ -7,16 +7,50 @@ # be found in the AUTHORS file in the root of the source tree. { + 'variables': { + 'audio_coding_dependencies': [ + 'CNG', + '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + ], + 'audio_coding_defines': [], + 'conditions': [ + ['include_opus==1', { + 'audio_coding_dependencies': ['webrtc_opus',], + 'audio_coding_defines': ['WEBRTC_CODEC_OPUS',], + }], + ['include_g711==1', { + 'audio_coding_dependencies': ['G711',], + 'audio_coding_defines': ['WEBRTC_CODEC_G711',], + }], + ['include_g722==1', { + 'audio_coding_dependencies': ['G722',], + 'audio_coding_defines': ['WEBRTC_CODEC_G722',], + }], + ['include_ilbc==1', { + 'audio_coding_dependencies': ['iLBC',], + 'audio_coding_defines': ['WEBRTC_CODEC_ILBC',], + }], + ['include_isac==1', { + 'audio_coding_dependencies': ['iSAC', 'iSACFix',], + 'audio_coding_defines': ['WEBRTC_CODEC_ISAC', 'WEBRTC_CODEC_ISACFX',], + }], + ['include_pcm16b==1', { + 'audio_coding_dependencies': ['PCM16B',], + 'audio_coding_defines': ['WEBRTC_CODEC_PCM16',], + }], + ], + }, 'targets': [ { - 'target_name': 'acm2', + 'target_name': 'audio_coding_module', 'type': 'static_library', 'defines': [ '<@(audio_coding_defines)', ], 'dependencies': [ '<@(audio_coding_dependencies)', - 'NetEq4', + 'neteq', ], 'include_dirs': [ '../interface', @@ -33,12 +67,6 @@ 'sources': [ '../interface/audio_coding_module.h', '../interface/audio_coding_module_typedefs.h', - 'acm_amr.cc', - 'acm_amr.h', - 'acm_amrwb.cc', - 'acm_amrwb.h', - 'acm_celt.cc', - 'acm_celt.h', 'acm_cng.cc', 'acm_cng.h', 'acm_codec_database.cc', @@ -46,26 +74,8 @@ 'acm_common_defs.h', 'acm_dtmf_playout.cc', 'acm_dtmf_playout.h', - 'acm_g722.cc', - 'acm_g722.h', - 'acm_g729.cc', - 'acm_g729.h', - 'acm_g7291.cc', - 'acm_g7291.h', 'acm_generic_codec.cc', 'acm_generic_codec.h', - 'acm_gsmfr.cc', - 'acm_gsmfr.h', - 'acm_opus.cc', - 'acm_opus.h', - 'acm_speex.cc', - 'acm_speex.h', - 'acm_pcm16b.cc', - 'acm_pcm16b.h', - 'acm_pcma.cc', - 'acm_pcma.h', - 'acm_pcmu.cc', - 'acm_pcmu.h', 'acm_red.cc', 'acm_red.h', 'acm_receiver.cc', @@ -82,6 +92,123 @@ 'nack.cc', 'nack.h', ], + 'conditions': [ + ['include_opus==1', { + 'sources': [ + 'acm_opus.cc', + 'acm_opus.h', + ], + }], + ['include_g711==1', { + 'sources': [ + 'acm_pcma.cc', + 'acm_pcma.h', + 'acm_pcmu.cc', + 'acm_pcmu.h', + ], + }], + ['include_g722==1', { + 'sources': [ + 'acm_g722.cc', + 'acm_g722.h', + ], + }], + ['include_ilbc==1', { + 'sources': [ +# FIX + ], + }], + ['include_isac==1', { + 'sources': [ +# FIX + ], + }], + ['include_pcm16b==1', { + 'sources': [ + 'acm_pcm16b.cc', + 'acm_pcm16b.h', + ], + }], + ], }, ], + 'conditions': [ + ['include_tests==1', { + 'targets': [ + { + 'target_name': 'acm_receive_test', + 'type': 'static_library', + 'defines': [ + '<@(audio_coding_defines)', + ], + 'dependencies': [ + '<@(audio_coding_dependencies)', + 'audio_coding_module', + 'neteq_unittest_tools', + '<(DEPTH)/testing/gtest.gyp:gtest', + ], + 'sources': [ + 'acm_receive_test.cc', + 'acm_receive_test.h', + 'acm_receive_test_oldapi.cc', + 'acm_receive_test_oldapi.h', + ], + }, # acm_receive_test + { + 'target_name': 'acm_send_test', + 'type': 'static_library', + 'defines': [ + '<@(audio_coding_defines)', + ], + 'dependencies': [ + '<@(audio_coding_dependencies)', + 'audio_coding_module', + 'neteq_unittest_tools', + '<(DEPTH)/testing/gtest.gyp:gtest', + ], + 'sources': [ + 'acm_send_test.cc', + 'acm_send_test.h', + 'acm_send_test_oldapi.cc', + 'acm_send_test_oldapi.h', + ], + }, # acm_send_test + { + 'target_name': 'delay_test', + 'type': 'executable', + 'dependencies': [ + 'audio_coding_module', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(webrtc_root)/test/test.gyp:test_support', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers_default', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + ], + 'sources': [ + '../test/delay_test.cc', + '../test/Channel.cc', + '../test/PCMFile.cc', + '../test/utility.cc', + ], + }, # delay_test + { + 'target_name': 'insert_packet_with_timing', + 'type': 'executable', + 'dependencies': [ + 'audio_coding_module', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(webrtc_root)/test/test.gyp:test_support', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers_default', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + ], + 'sources': [ + '../test/insert_packet_with_timing.cc', + '../test/Channel.cc', + '../test/PCMFile.cc', + ], + }, # delay_test + ], + }], + ], } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc 2015-02-03 14:33:34.000000000 +0000 @@ -14,6 +14,7 @@ #include #include +#include "webrtc/base/checks.h" #include "webrtc/engine_configurations.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" #include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h" @@ -39,11 +40,11 @@ kMaxPacketSize = 2560 }; -// Maximum number of payloads that can be packed in one RED payload. For -// regular FEC, we only pack two payloads. In case of dual-streaming, in worst -// case we might pack 3 payloads in one RED payload. +// Maximum number of payloads that can be packed in one RED packet. For +// regular RED, we only pack two payloads. In case of dual-streaming, in worst +// case we might pack 3 payloads in one RED packet. enum { - kNumFecFragmentationVectors = 2, + kNumRedFragmentationVectors = 2, kMaxNumFragmentationVectors = 3 }; @@ -114,9 +115,10 @@ } // namespace -AudioCodingModuleImpl::AudioCodingModuleImpl(int id) - : packetization_callback_(NULL), - id_(id), +AudioCodingModuleImpl::AudioCodingModuleImpl( + const AudioCodingModule::Config& config) + : acm_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + id_(config.id), expected_codec_ts_(0xD87F3F9F), expected_in_ts_(0xD87F3F9F), send_codec_inst_(), @@ -131,18 +133,20 @@ stereo_send_(false), current_send_codec_idx_(-1), send_codec_registered_(false), - acm_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), - vad_callback_(NULL), + receiver_(config), is_first_red_(true), - fec_enabled_(false), - last_fec_timestamp_(0), + red_enabled_(false), + last_red_timestamp_(0), + codec_fec_enabled_(false), previous_pltype_(255), aux_rtp_header_(NULL), receiver_initialized_(false), - callback_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), secondary_send_codec_inst_(), codec_timestamp_(expected_codec_ts_), - first_10ms_data_(false) { + first_10ms_data_(false), + callback_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + packetization_callback_(NULL), + vad_callback_(NULL) { // Nullify send codec memory, set payload type and set codec name to // invalid values. @@ -159,8 +163,6 @@ mirror_codec_idx_[i] = -1; } - receiver_.set_id(id_); - // Allocate memory for RED. red_buffer_ = new uint8_t[MAX_PAYLOAD_SIZE_BYTE]; @@ -201,7 +203,7 @@ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, "Cannot initialize receiver"); } - WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceAudioCoding, id, "Created"); + WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceAudioCoding, id_, "Created"); } AudioCodingModuleImpl::~AudioCodingModuleImpl() { @@ -349,7 +351,7 @@ int16_t len_bytes = MAX_PAYLOAD_SIZE_BYTE; WebRtcACMEncodingType encoding_type; if (secondary_encoder_->Encode(red_buffer_, &len_bytes, - &last_fec_timestamp_, + &last_red_timestamp_, &encoding_type) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, "ProcessDual(): Encoding of secondary encoder Failed"); @@ -372,7 +374,7 @@ index_primary = secondary_ready_to_encode ? TimestampLessThan(primary_timestamp, secondary_timestamp) : 0; index_primary += has_previous_payload ? - TimestampLessThan(primary_timestamp, last_fec_timestamp_) : 0; + TimestampLessThan(primary_timestamp, last_red_timestamp_) : 0; } if (secondary_ready_to_encode) { @@ -384,7 +386,7 @@ if (has_previous_payload) { index_previous_secondary = primary_ready_to_encode ? - (1 - TimestampLessThan(primary_timestamp, last_fec_timestamp_)) : 0; + (1 - TimestampLessThan(primary_timestamp, last_red_timestamp_)) : 0; // If secondary is ready it always have a timestamp larger than previous // secondary. So the index is either 0 or 1. index_previous_secondary += secondary_ready_to_encode ? 1 : 0; @@ -405,7 +407,7 @@ } else if (index_secondary == 0) { current_timestamp = secondary_timestamp; } else { - current_timestamp = last_fec_timestamp_; + current_timestamp = last_red_timestamp_; } fragmentation_.fragmentationVectorSize = 0; @@ -420,7 +422,7 @@ fragmentation_.fragmentationPlType[index_previous_secondary] = secondary_send_codec_inst_.pltype; fragmentation_.fragmentationTimeDiff[index_previous_secondary] = - static_cast(current_timestamp - last_fec_timestamp_); + static_cast(current_timestamp - last_red_timestamp_); fragmentation_.fragmentationVectorSize++; } @@ -462,7 +464,7 @@ { CriticalSectionScoped lock(callback_crit_sect_); if (packetization_callback_ != NULL) { - // Callback with payload data, including redundant data (FEC/RED). + // Callback with payload data, including redundant data (RED). if (packetization_callback_->SendData(kAudioFrameSpeech, my_red_payload_type, current_timestamp, stream, @@ -495,7 +497,7 @@ FrameType frame_type = kAudioFrameSpeech; uint8_t current_payload_type = 0; bool has_data_to_send = false; - bool fec_active = false; + bool red_active = false; RTPFragmentationHeader my_fragmentation; // Keep the scope of the ACM critical section limited. @@ -562,15 +564,15 @@ // Redundancy encode is done here. The two bitstreams packetized into // one RTP packet and the fragmentation points are set. // Only apply RED on speech data. - if ((fec_enabled_) && + if ((red_enabled_) && ((encoding_type == kActiveNormalEncoded) || (encoding_type == kPassiveNormalEncoded))) { - // FEC is enabled within this scope. + // RED is enabled within this scope. // // Note that, a special solution exists for iSAC since it is the only // codec for which GetRedPayload has a non-empty implementation. // - // Summary of the FEC scheme below (use iSAC as example): + // Summary of the RED scheme below (use iSAC as example): // // 1st (is_first_red_ is true) encoded iSAC frame (primary #1) => // - call GetRedPayload() and store redundancy for packet #1 in @@ -581,7 +583,7 @@ // - store primary #2 in 1st fragment of RED buffer and send the // combined packet // - the transmitted packet contains primary #2 (new) and - // reduncancy for packet #1 (old) + // redundancy for packet #1 (old) // - call GetRed_Payload() and store redundancy for packet #2 in // second fragment of RED buffer // @@ -604,19 +606,19 @@ // // Hence, even if every second packet is dropped, perfect // reconstruction is possible. - fec_active = true; + red_active = true; has_data_to_send = false; // Skip the following part for the first packet in a RED session. if (!is_first_red_) { - // Rearrange stream such that FEC packets are included. + // Rearrange stream such that RED packets are included. // Replace stream now that we have stored current stream. memcpy(stream + fragmentation_.fragmentationOffset[1], red_buffer_, fragmentation_.fragmentationLength[1]); // Update the fragmentation time difference vector, in number of // timestamps. uint16_t time_since_last = static_cast( - rtp_timestamp - last_fec_timestamp_); + rtp_timestamp - last_red_timestamp_); // Update fragmentation vectors. fragmentation_.fragmentationPlType[1] = @@ -630,7 +632,7 @@ // Insert new packet payload type. fragmentation_.fragmentationPlType[0] = current_payload_type; - last_fec_timestamp_ = rtp_timestamp; + last_red_timestamp_ = rtp_timestamp; // Can be modified by the GetRedPayload() call if iSAC is utilized. red_length_bytes = length_bytes; @@ -650,7 +652,7 @@ if (codecs_[current_send_codec_idx_]->GetRedPayload( red_buffer_, &red_length_bytes) == -1) { // The codec was not iSAC => use current encoder output as redundant - // data instead (trivial FEC scheme). + // data instead (trivial RED scheme). memcpy(red_buffer_, stream, red_length_bytes); } @@ -658,7 +660,7 @@ // Update payload type with RED payload type. current_payload_type = red_pltype_; // We have packed 2 payloads. - fragmentation_.fragmentationVectorSize = kNumFecFragmentationVectors; + fragmentation_.fragmentationVectorSize = kNumRedFragmentationVectors; // Copy to local variable, as it will be used outside ACM lock. my_fragmentation.CopyFrom(fragmentation_); @@ -672,8 +674,8 @@ CriticalSectionScoped lock(callback_crit_sect_); if (packetization_callback_ != NULL) { - if (fec_active) { - // Callback with payload data, including redundant data (FEC/RED). + if (red_active) { + // Callback with payload data, including redundant data (RED). packetization_callback_->SendData(frame_type, current_payload_type, rtp_timestamp, stream, length_bytes, &my_fragmentation); @@ -713,14 +715,14 @@ } } - // Initialize FEC/RED. + // Initialize RED. is_first_red_ = true; - if (fec_enabled_ || secondary_encoder_.get() != NULL) { + if (red_enabled_ || secondary_encoder_.get() != NULL) { if (red_buffer_ != NULL) { memset(red_buffer_, 0, MAX_PAYLOAD_SIZE_BYTE); } - if (fec_enabled_) { - ResetFragmentation(kNumFecFragmentationVectors); + if (red_enabled_) { + ResetFragmentation(kNumRedFragmentationVectors); } else { ResetFragmentation(0); } @@ -748,7 +750,6 @@ return my_codec; } my_codec->SetUniqueID(id_); - my_codec->SetNetEqDecodeLock(receiver_.DecodeLock()); return my_codec; } @@ -1031,10 +1032,20 @@ // Everything is fine so we can replace the previous codec with this one. if (send_codec_registered_) { - // If we change codec we start fresh with FEC. + // If we change codec we start fresh with RED. // This is not strictly required by the standard. is_first_red_ = true; codec_ptr->SetVAD(&dtx_enabled_, &vad_enabled_, &vad_mode_); + + if (!codec_ptr->HasInternalFEC()) { + codec_fec_enabled_ = false; + } else { + if (codec_ptr->SetFEC(codec_fec_enabled_) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot set codec FEC"); + return -1; + } + } } current_send_codec_idx_ = codec_id; @@ -1120,8 +1131,18 @@ } send_codec_inst_.rate = send_codec.rate; } - previous_pltype_ = send_codec_inst_.pltype; + if (!codecs_[codec_id]->HasInternalFEC()) { + codec_fec_enabled_ = false; + } else { + if (codecs_[codec_id]->SetFEC(codec_fec_enabled_) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Cannot set codec FEC"); + return -1; + } + } + + previous_pltype_ = send_codec_inst_.pltype; return 0; } } @@ -1182,6 +1203,7 @@ // Set available bandwidth, inform the encoder about the estimated bandwidth // received from the remote party. int AudioCodingModuleImpl::SetReceivedEstimatedBandwidth(int bw) { + CriticalSectionScoped lock(acm_crit_sect_); return codecs_[current_send_codec_idx_]->SetEstimatedBandwidth(bw); } @@ -1205,11 +1227,7 @@ return -1; } - // Allow for 8, 16, 32 and 48kHz input audio. - if ((audio_frame.sample_rate_hz_ != 8000) - && (audio_frame.sample_rate_hz_ != 16000) - && (audio_frame.sample_rate_hz_ != 32000) - && (audio_frame.sample_rate_hz_ != 48000)) { + if (audio_frame.sample_rate_hz_ > 48000) { assert(false); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, "Cannot Add 10 ms audio, input frequency not valid"); @@ -1365,13 +1383,17 @@ // The result of the resampler is written to output frame. dest_ptr_audio = preprocess_frame_.data_; - preprocess_frame_.samples_per_channel_ = resampler_.Resample10Msec( - src_ptr_audio, in_frame.sample_rate_hz_, send_codec_inst_.plfreq, - preprocess_frame_.num_channels_, dest_ptr_audio); + preprocess_frame_.samples_per_channel_ = + resampler_.Resample10Msec(src_ptr_audio, + in_frame.sample_rate_hz_, + send_codec_inst_.plfreq, + preprocess_frame_.num_channels_, + AudioFrame::kMaxDataSizeSamples, + dest_ptr_audio); if (preprocess_frame_.samples_per_channel_ < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot add 10 ms audio, resmapling failed"); + "Cannot add 10 ms audio, resampling failed"); return -1; } preprocess_frame_.sample_rate_hz_ = send_codec_inst_.plfreq; @@ -1384,42 +1406,89 @@ } ///////////////////////////////////////// -// (FEC) Forward Error Correction +// (RED) Redundant Coding // -bool AudioCodingModuleImpl::FECStatus() const { +bool AudioCodingModuleImpl::REDStatus() const { CriticalSectionScoped lock(acm_crit_sect_); - return fec_enabled_; + + return red_enabled_; } -// Configure FEC status i.e on/off. -int AudioCodingModuleImpl::SetFECStatus( +// Configure RED status i.e on/off. +int AudioCodingModuleImpl::SetREDStatus( #ifdef WEBRTC_CODEC_RED - bool enable_fec) { + bool enable_red) { CriticalSectionScoped lock(acm_crit_sect_); - if (fec_enabled_ != enable_fec) { + if (enable_red == true && codec_fec_enabled_ == true) { + WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, + "Codec internal FEC and RED cannot be co-enabled."); + return -1; + } + + if (red_enabled_ != enable_red) { // Reset the RED buffer. memset(red_buffer_, 0, MAX_PAYLOAD_SIZE_BYTE); // Reset fragmentation buffers. - ResetFragmentation(kNumFecFragmentationVectors); - // Set fec_enabled_. - fec_enabled_ = enable_fec; + ResetFragmentation(kNumRedFragmentationVectors); + // Set red_enabled_. + red_enabled_ = enable_red; } - is_first_red_ = true; // Make sure we restart FEC. + is_first_red_ = true; // Make sure we restart RED. return 0; #else - bool /* enable_fec */) { - fec_enabled_ = false; + bool /* enable_red */) { + red_enabled_ = false; WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, - " WEBRTC_CODEC_RED is undefined => fec_enabled_ = %d", - fec_enabled_); + " WEBRTC_CODEC_RED is undefined => red_enabled_ = %d", + red_enabled_); return -1; #endif } ///////////////////////////////////////// +// (FEC) Forward Error Correction (codec internal) +// + +bool AudioCodingModuleImpl::CodecFEC() const { + CriticalSectionScoped lock(acm_crit_sect_); + return codec_fec_enabled_; +} + +int AudioCodingModuleImpl::SetCodecFEC(bool enable_codec_fec) { + CriticalSectionScoped lock(acm_crit_sect_); + + if (enable_codec_fec == true && red_enabled_ == true) { + WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, + "Codec internal FEC and RED cannot be co-enabled."); + return -1; + } + + // Set codec FEC. + if (HaveValidEncoder("SetCodecFEC") && + codecs_[current_send_codec_idx_]->SetFEC(enable_codec_fec) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Set codec internal FEC failed."); + return -1; + } + codec_fec_enabled_ = enable_codec_fec; + return 0; +} + +int AudioCodingModuleImpl::SetPacketLossRate(int loss_rate) { + CriticalSectionScoped lock(acm_crit_sect_); + if (HaveValidEncoder("SetPacketLossRate") && + codecs_[current_send_codec_idx_]->SetPacketLossRate(loss_rate) < 0) { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, + "Set packet loss rate failed."); + return -1; + } + return 0; +} + +///////////////////////////////////////// // (VAD) Voice Activity Detection // int AudioCodingModuleImpl::SetVAD(bool enable_dtx, @@ -1550,14 +1619,8 @@ int codec_id = receiver_.last_audio_codec_id(); - int sample_rate_hz; - if (codec_id < 0) - sample_rate_hz = receiver_.current_sample_rate_hz(); - else - sample_rate_hz = ACMCodecDB::database_[codec_id].plfreq; - - // TODO(tlegrand): Remove this option when we have full 48 kHz support. - return (sample_rate_hz > 32000) ? 32000 : sample_rate_hz; + return codec_id < 0 ? receiver_.current_sample_rate_hz() : + ACMCodecDB::database_[codec_id].plfreq; } // Get current playout frequency. @@ -1710,8 +1773,6 @@ } audio_frame->id_ = id_; - audio_frame->energy_ = 0; - audio_frame->timestamp_ = 0; return 0; } @@ -1726,15 +1787,6 @@ return 0; } -void AudioCodingModuleImpl::DestructEncoderInst(void* inst) { - CriticalSectionScoped lock(acm_crit_sect_); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, id_, - "DestructEncoderInst()"); - if (!HaveValidEncoder("DestructEncoderInst")) - return; - codecs_[current_send_codec_idx_]->DestructEncoderInst(inst); -} - int AudioCodingModuleImpl::RegisterVADCallback(ACMVADCallback* vad_callback) { WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, id_, "RegisterVADCallback()"); @@ -1770,6 +1822,7 @@ aux_rtp_header_->type.Audio.channel = 1; } + aux_rtp_header_->header.timestamp = timestamp; IncomingPacket(incoming_payload, payload_length, *aux_rtp_header_); // Get ready for the next payload. aux_rtp_header_->header.sequenceNumber++; @@ -1850,9 +1903,17 @@ frame_size_ms, rate_bit_per_sec, enforce_frame_size); } +// Informs Opus encoder of the maximum playback rate the receiver will render. +int AudioCodingModuleImpl::SetOpusMaxPlaybackRate(int frequency_hz) { + CriticalSectionScoped lock(acm_crit_sect_); + if (!HaveValidEncoder("SetOpusMaxPlaybackRate")) { + return -1; + } + return codecs_[current_send_codec_idx_]->SetOpusMaxPlaybackRate(frequency_hz); +} + int AudioCodingModuleImpl::PlayoutTimestamp(uint32_t* timestamp) { - *timestamp = receiver_.PlayoutTimestamp(); - return 0; + return receiver_.GetPlayoutTimestamp(timestamp) ? 0 : -1; } bool AudioCodingModuleImpl::HaveValidEncoder(const char* caller_name) const { @@ -1886,6 +1947,7 @@ int isac_bw_estimate, uint8_t* payload, int16_t* length_bytes) { + CriticalSectionScoped lock(acm_crit_sect_); if (!HaveValidEncoder("EncodeData")) { return -1; } @@ -1976,10 +2038,6 @@ return receiver_.LeastRequiredDelayMs(); } -const char* AudioCodingModuleImpl::Version() const { - return kExperimentalAcmVersion; -} - void AudioCodingModuleImpl::GetDecodingCallStatistics( AudioDecodingCallStats* call_stats) const { receiver_.GetDecodingCallStatistics(call_stats); @@ -1987,4 +2045,281 @@ } // namespace acm2 +bool AudioCodingImpl::RegisterSendCodec(AudioEncoder* send_codec) { + FATAL() << "Not implemented yet."; + return false; +} + +bool AudioCodingImpl::RegisterSendCodec(int encoder_type, + uint8_t payload_type, + int frame_size_samples) { + std::string codec_name; + int sample_rate_hz; + int channels; + if (!MapCodecTypeToParameters( + encoder_type, &codec_name, &sample_rate_hz, &channels)) { + return false; + } + webrtc::CodecInst codec; + AudioCodingModule::Codec( + codec_name.c_str(), &codec, sample_rate_hz, channels); + codec.pltype = payload_type; + if (frame_size_samples > 0) { + codec.pacsize = frame_size_samples; + } + return acm_old_->RegisterSendCodec(codec) == 0; +} + +const AudioEncoder* AudioCodingImpl::GetSenderInfo() const { + FATAL() << "Not implemented yet."; + return reinterpret_cast(NULL); +} + +const CodecInst* AudioCodingImpl::GetSenderCodecInst() { + if (acm_old_->SendCodec(¤t_send_codec_) != 0) { + return NULL; + } + return ¤t_send_codec_; +} + +int AudioCodingImpl::Add10MsAudio(const AudioFrame& audio_frame) { + if (acm_old_->Add10MsData(audio_frame) != 0) { + return -1; + } + return acm_old_->Process(); +} + +const ReceiverInfo* AudioCodingImpl::GetReceiverInfo() const { + FATAL() << "Not implemented yet."; + return reinterpret_cast(NULL); +} + +bool AudioCodingImpl::RegisterReceiveCodec(AudioDecoder* receive_codec) { + FATAL() << "Not implemented yet."; + return false; +} + +bool AudioCodingImpl::RegisterReceiveCodec(int decoder_type, + uint8_t payload_type) { + std::string codec_name; + int sample_rate_hz; + int channels; + if (!MapCodecTypeToParameters( + decoder_type, &codec_name, &sample_rate_hz, &channels)) { + return false; + } + webrtc::CodecInst codec; + AudioCodingModule::Codec( + codec_name.c_str(), &codec, sample_rate_hz, channels); + codec.pltype = payload_type; + return acm_old_->RegisterReceiveCodec(codec) == 0; +} + +bool AudioCodingImpl::InsertPacket(const uint8_t* incoming_payload, + int32_t payload_len_bytes, + const WebRtcRTPHeader& rtp_info) { + return acm_old_->IncomingPacket( + incoming_payload, payload_len_bytes, rtp_info) == 0; +} + +bool AudioCodingImpl::InsertPayload(const uint8_t* incoming_payload, + int32_t payload_len_byte, + uint8_t payload_type, + uint32_t timestamp) { + FATAL() << "Not implemented yet."; + return false; +} + +bool AudioCodingImpl::SetMinimumPlayoutDelay(int time_ms) { + FATAL() << "Not implemented yet."; + return false; +} + +bool AudioCodingImpl::SetMaximumPlayoutDelay(int time_ms) { + FATAL() << "Not implemented yet."; + return false; +} + +int AudioCodingImpl::LeastRequiredDelayMs() const { + FATAL() << "Not implemented yet."; + return -1; +} + +bool AudioCodingImpl::PlayoutTimestamp(uint32_t* timestamp) { + FATAL() << "Not implemented yet."; + return false; +} + +bool AudioCodingImpl::Get10MsAudio(AudioFrame* audio_frame) { + return acm_old_->PlayoutData10Ms(playout_frequency_hz_, audio_frame) == 0; +} + +bool AudioCodingImpl::NetworkStatistics( + ACMNetworkStatistics* network_statistics) { + FATAL() << "Not implemented yet."; + return false; +} + +bool AudioCodingImpl::EnableNack(size_t max_nack_list_size) { + FATAL() << "Not implemented yet."; + return false; +} + +void AudioCodingImpl::DisableNack() { + // A bug in the linker of Visual Studio 2013 Update 3 prevent us from using + // FATAL() here, if we do so then the linker hang when the WPO is turned on. + // TODO(sebmarchand): Re-evaluate this when we upgrade the toolchain. +} + +bool AudioCodingImpl::SetVad(bool enable_dtx, + bool enable_vad, + ACMVADMode vad_mode) { + return acm_old_->SetVAD(enable_dtx, enable_vad, vad_mode) == 0; +} + +std::vector AudioCodingImpl::GetNackList( + int round_trip_time_ms) const { + return acm_old_->GetNackList(round_trip_time_ms); +} + +void AudioCodingImpl::GetDecodingCallStatistics( + AudioDecodingCallStats* call_stats) const { + acm_old_->GetDecodingCallStatistics(call_stats); +} + +bool AudioCodingImpl::MapCodecTypeToParameters(int codec_type, + std::string* codec_name, + int* sample_rate_hz, + int* channels) { + switch (codec_type) { +#ifdef WEBRTC_CODEC_PCM16 + case acm2::ACMCodecDB::kPCM16B: + *codec_name = "L16"; + *sample_rate_hz = 8000; + *channels = 1; + break; + case acm2::ACMCodecDB::kPCM16Bwb: + *codec_name = "L16"; + *sample_rate_hz = 16000; + *channels = 1; + break; + case acm2::ACMCodecDB::kPCM16Bswb32kHz: + *codec_name = "L16"; + *sample_rate_hz = 32000; + *channels = 1; + break; + case acm2::ACMCodecDB::kPCM16B_2ch: + *codec_name = "L16"; + *sample_rate_hz = 8000; + *channels = 2; + break; + case acm2::ACMCodecDB::kPCM16Bwb_2ch: + *codec_name = "L16"; + *sample_rate_hz = 16000; + *channels = 2; + break; + case acm2::ACMCodecDB::kPCM16Bswb32kHz_2ch: + *codec_name = "L16"; + *sample_rate_hz = 32000; + *channels = 2; + break; +#endif +#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) + case acm2::ACMCodecDB::kISAC: + *codec_name = "ISAC"; + *sample_rate_hz = 16000; + *channels = 1; + break; +#endif +#ifdef WEBRTC_CODEC_ISAC + case acm2::ACMCodecDB::kISACSWB: + *codec_name = "ISAC"; + *sample_rate_hz = 32000; + *channels = 1; + break; + case acm2::ACMCodecDB::kISACFB: + *codec_name = "ISAC"; + *sample_rate_hz = 48000; + *channels = 1; + break; +#endif +#ifdef WEBRTC_CODEC_ILBC + case acm2::ACMCodecDB::kILBC: + *codec_name = "ILBC"; + *sample_rate_hz = 8000; + *channels = 1; + break; +#endif + case acm2::ACMCodecDB::kPCMA: + *codec_name = "PCMA"; + *sample_rate_hz = 8000; + *channels = 1; + break; + case acm2::ACMCodecDB::kPCMA_2ch: + *codec_name = "PCMA"; + *sample_rate_hz = 8000; + *channels = 2; + break; + case acm2::ACMCodecDB::kPCMU: + *codec_name = "PCMU"; + *sample_rate_hz = 8000; + *channels = 1; + break; + case acm2::ACMCodecDB::kPCMU_2ch: + *codec_name = "PCMU"; + *sample_rate_hz = 8000; + *channels = 2; + break; +#ifdef WEBRTC_CODEC_G722 + case acm2::ACMCodecDB::kG722: + *codec_name = "G722"; + *sample_rate_hz = 16000; + *channels = 1; + break; + case acm2::ACMCodecDB::kG722_2ch: + *codec_name = "G722"; + *sample_rate_hz = 16000; + *channels = 2; + break; +#endif +#ifdef WEBRTC_CODEC_OPUS + case acm2::ACMCodecDB::kOpus: + *codec_name = "opus"; + *sample_rate_hz = 48000; + *channels = 2; + break; +#endif + case acm2::ACMCodecDB::kCNNB: + *codec_name = "CN"; + *sample_rate_hz = 8000; + *channels = 1; + break; + case acm2::ACMCodecDB::kCNWB: + *codec_name = "CN"; + *sample_rate_hz = 16000; + *channels = 1; + break; + case acm2::ACMCodecDB::kCNSWB: + *codec_name = "CN"; + *sample_rate_hz = 32000; + *channels = 1; + break; + case acm2::ACMCodecDB::kRED: + *codec_name = "red"; + *sample_rate_hz = 8000; + *channels = 1; + break; +#ifdef WEBRTC_CODEC_AVT + case acm2::ACMCodecDB::kAVT: + *codec_name = "telephone-event"; + *sample_rate_hz = 8000; + *channels = 1; + break; +#endif + default: + FATAL() << "Codec type " << codec_type << " not supported."; + } + return true; +} + } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h 2015-02-03 14:33:34.000000000 +0000 @@ -13,6 +13,7 @@ #include +#include "webrtc/base/thread_annotations.h" #include "webrtc/common_types.h" #include "webrtc/engine_configurations.h" #include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h" @@ -23,7 +24,6 @@ namespace webrtc { class CriticalSectionWrapper; -class RWLockWrapper; namespace acm2 { @@ -32,76 +32,88 @@ class AudioCodingModuleImpl : public AudioCodingModule { public: - explicit AudioCodingModuleImpl(int id); + explicit AudioCodingModuleImpl(const AudioCodingModule::Config& config); ~AudioCodingModuleImpl(); - virtual const char* Version() const; - // Change the unique identifier of this object. - virtual int32_t ChangeUniqueId(const int32_t id); + virtual int32_t ChangeUniqueId(const int32_t id) OVERRIDE; // Returns the number of milliseconds until the module want a worker thread // to call Process. - int32_t TimeUntilNextProcess(); + virtual int32_t TimeUntilNextProcess() OVERRIDE; // Process any pending tasks such as timeouts. - int32_t Process(); + virtual int32_t Process() OVERRIDE; ///////////////////////////////////////// // Sender // // Initialize send codec. - int InitializeSender(); + virtual int InitializeSender() OVERRIDE; // Reset send codec. - int ResetEncoder(); + virtual int ResetEncoder() OVERRIDE; // Can be called multiple times for Codec, CNG, RED. - int RegisterSendCodec(const CodecInst& send_codec); + virtual int RegisterSendCodec(const CodecInst& send_codec) OVERRIDE; // Register Secondary codec for dual-streaming. Dual-streaming is activated // right after the secondary codec is registered. - int RegisterSecondarySendCodec(const CodecInst& send_codec); + virtual int RegisterSecondarySendCodec(const CodecInst& send_codec) OVERRIDE; // Unregister the secondary codec. Dual-streaming is deactivated right after // deregistering secondary codec. - void UnregisterSecondarySendCodec(); + virtual void UnregisterSecondarySendCodec() OVERRIDE; // Get the secondary codec. - int SecondarySendCodec(CodecInst* secondary_codec) const; + virtual int SecondarySendCodec(CodecInst* secondary_codec) const OVERRIDE; // Get current send codec. - int SendCodec(CodecInst* current_codec) const; + virtual int SendCodec(CodecInst* current_codec) const OVERRIDE; // Get current send frequency. - int SendFrequency() const; + virtual int SendFrequency() const OVERRIDE; // Get encode bit-rate. // Adaptive rate codecs return their current encode target rate, while other // codecs return there long-term average or their fixed rate. - int SendBitrate() const; + virtual int SendBitrate() const OVERRIDE; // Set available bandwidth, inform the encoder about the // estimated bandwidth received from the remote party. - virtual int SetReceivedEstimatedBandwidth(int bw); + virtual int SetReceivedEstimatedBandwidth(int bw) OVERRIDE; // Register a transport callback which will be // called to deliver the encoded buffers. - int RegisterTransportCallback(AudioPacketizationCallback* transport); + virtual int RegisterTransportCallback( + AudioPacketizationCallback* transport) OVERRIDE; // Add 10 ms of raw (PCM) audio data to the encoder. - int Add10MsData(const AudioFrame& audio_frame); + virtual int Add10MsData(const AudioFrame& audio_frame) OVERRIDE; + + ///////////////////////////////////////// + // (RED) Redundant Coding + // + + // Configure RED status i.e. on/off. + virtual int SetREDStatus(bool enable_red) OVERRIDE; + + // Get RED status. + virtual bool REDStatus() const OVERRIDE; ///////////////////////////////////////// - // (FEC) Forward Error Correction + // (FEC) Forward Error Correction (codec internal) // - // Configure FEC status i.e on/off. - int SetFECStatus(bool enable_fec); + // Configure FEC status i.e. on/off. + virtual int SetCodecFEC(bool enabled_codec_fec) OVERRIDE; // Get FEC status. - bool FECStatus() const; + virtual bool CodecFEC() const OVERRIDE; + + // Set target packet loss rate + virtual int SetPacketLossRate(int loss_rate) OVERRIDE; ///////////////////////////////////////// // (VAD) Voice Activity Detection @@ -109,139 +121,150 @@ // (CNG) Comfort Noise Generation // - int SetVAD(bool enable_dtx = true, - bool enable_vad = false, - ACMVADMode mode = VADNormal); - - int VAD(bool* dtx_enabled, bool* vad_enabled, ACMVADMode* mode) const; + virtual int SetVAD(bool enable_dtx = true, + bool enable_vad = false, + ACMVADMode mode = VADNormal) OVERRIDE; + + virtual int VAD(bool* dtx_enabled, + bool* vad_enabled, + ACMVADMode* mode) const OVERRIDE; - int RegisterVADCallback(ACMVADCallback* vad_callback); + virtual int RegisterVADCallback(ACMVADCallback* vad_callback) OVERRIDE; ///////////////////////////////////////// // Receiver // // Initialize receiver, resets codec database etc. - int InitializeReceiver(); + virtual int InitializeReceiver() OVERRIDE; // Reset the decoder state. - int ResetDecoder(); + virtual int ResetDecoder() OVERRIDE; // Get current receive frequency. - int ReceiveFrequency() const; + virtual int ReceiveFrequency() const OVERRIDE; // Get current playout frequency. - int PlayoutFrequency() const; + virtual int PlayoutFrequency() const OVERRIDE; // Register possible receive codecs, can be called multiple times, // for codecs, CNG, DTMF, RED. - int RegisterReceiveCodec(const CodecInst& receive_codec); + virtual int RegisterReceiveCodec(const CodecInst& receive_codec) OVERRIDE; // Get current received codec. - int ReceiveCodec(CodecInst* current_codec) const; + virtual int ReceiveCodec(CodecInst* current_codec) const OVERRIDE; // Incoming packet from network parsed and ready for decode. - int IncomingPacket(const uint8_t* incoming_payload, - int payload_length, - const WebRtcRTPHeader& rtp_info); + virtual int IncomingPacket(const uint8_t* incoming_payload, + int payload_length, + const WebRtcRTPHeader& rtp_info) OVERRIDE; // Incoming payloads, without rtp-info, the rtp-info will be created in ACM. // One usage for this API is when pre-encoded files are pushed in ACM. - int IncomingPayload(const uint8_t* incoming_payload, - int payload_length, - uint8_t payload_type, - uint32_t timestamp); + virtual int IncomingPayload(const uint8_t* incoming_payload, + int payload_length, + uint8_t payload_type, + uint32_t timestamp) OVERRIDE; // Minimum playout delay. - int SetMinimumPlayoutDelay(int time_ms); + virtual int SetMinimumPlayoutDelay(int time_ms) OVERRIDE; // Maximum playout delay. - int SetMaximumPlayoutDelay(int time_ms); + virtual int SetMaximumPlayoutDelay(int time_ms) OVERRIDE; // Smallest latency NetEq will maintain. - int LeastRequiredDelayMs() const; + virtual int LeastRequiredDelayMs() const OVERRIDE; // Impose an initial delay on playout. ACM plays silence until |delay_ms| // audio is accumulated in NetEq buffer, then starts decoding payloads. - int SetInitialPlayoutDelay(int delay_ms); + virtual int SetInitialPlayoutDelay(int delay_ms) OVERRIDE; // TODO(turajs): DTMF playout is always activated in NetEq these APIs should // be removed, as well as all VoE related APIs and methods. // // Configure Dtmf playout status i.e on/off playout the incoming outband Dtmf // tone. - int SetDtmfPlayoutStatus(bool enable) { return 0; } + virtual int SetDtmfPlayoutStatus(bool enable) OVERRIDE { return 0; } // Get Dtmf playout status. - bool DtmfPlayoutStatus() const { return true; } + virtual bool DtmfPlayoutStatus() const OVERRIDE { return true; } // Estimate the Bandwidth based on the incoming stream, needed // for one way audio where the RTCP send the BW estimate. // This is also done in the RTP module . - int DecoderEstimatedBandwidth() const; + virtual int DecoderEstimatedBandwidth() const OVERRIDE; // Set playout mode voice, fax. - int SetPlayoutMode(AudioPlayoutMode mode); + virtual int SetPlayoutMode(AudioPlayoutMode mode) OVERRIDE; // Get playout mode voice, fax. - AudioPlayoutMode PlayoutMode() const; + virtual AudioPlayoutMode PlayoutMode() const OVERRIDE; // Get playout timestamp. - int PlayoutTimestamp(uint32_t* timestamp); + virtual int PlayoutTimestamp(uint32_t* timestamp) OVERRIDE; // Get 10 milliseconds of raw audio data to play out, and // automatic resample to the requested frequency if > 0. - int PlayoutData10Ms(int desired_freq_hz, AudioFrame* audio_frame); + virtual int PlayoutData10Ms(int desired_freq_hz, + AudioFrame* audio_frame) OVERRIDE; ///////////////////////////////////////// // Statistics // - int NetworkStatistics(ACMNetworkStatistics* statistics); - - void DestructEncoderInst(void* inst); + virtual int NetworkStatistics(ACMNetworkStatistics* statistics) OVERRIDE; // GET RED payload for iSAC. The method id called when 'this' ACM is // the default ACM. + // TODO(henrik.lundin) Not used. Remove? int REDPayloadISAC(int isac_rate, int isac_bw_estimate, uint8_t* payload, int16_t* length_bytes); - int ReplaceInternalDTXWithWebRtc(bool use_webrtc_dtx); + virtual int ReplaceInternalDTXWithWebRtc(bool use_webrtc_dtx) OVERRIDE; - int IsInternalDTXReplacedWithWebRtc(bool* uses_webrtc_dtx); + virtual int IsInternalDTXReplacedWithWebRtc(bool* uses_webrtc_dtx) OVERRIDE; - int SetISACMaxRate(int max_bit_per_sec); + virtual int SetISACMaxRate(int max_bit_per_sec) OVERRIDE; - int SetISACMaxPayloadSize(int max_size_bytes); + virtual int SetISACMaxPayloadSize(int max_size_bytes) OVERRIDE; - int ConfigISACBandwidthEstimator(int frame_size_ms, - int rate_bit_per_sec, - bool enforce_frame_size = false); + virtual int ConfigISACBandwidthEstimator( + int frame_size_ms, + int rate_bit_per_sec, + bool enforce_frame_size = false) OVERRIDE; - int UnregisterReceiveCodec(uint8_t payload_type); + // If current send codec is Opus, informs it about the maximum playback rate + // the receiver will render. + virtual int SetOpusMaxPlaybackRate(int frequency_hz) OVERRIDE; - int EnableNack(size_t max_nack_list_size); + virtual int UnregisterReceiveCodec(uint8_t payload_type) OVERRIDE; - void DisableNack(); + virtual int EnableNack(size_t max_nack_list_size) OVERRIDE; - std::vector GetNackList(int round_trip_time_ms) const; + virtual void DisableNack() OVERRIDE; - void GetDecodingCallStatistics(AudioDecodingCallStats* stats) const; + virtual std::vector GetNackList( + int round_trip_time_ms) const OVERRIDE; + + virtual void GetDecodingCallStatistics( + AudioDecodingCallStats* stats) const OVERRIDE; private: int UnregisterReceiveCodecSafe(int payload_type); ACMGenericCodec* CreateCodec(const CodecInst& codec); - int InitializeReceiverSafe(); + int InitializeReceiverSafe() EXCLUSIVE_LOCKS_REQUIRED(acm_crit_sect_); - bool HaveValidEncoder(const char* caller_name) const; + bool HaveValidEncoder(const char* caller_name) const + EXCLUSIVE_LOCKS_REQUIRED(acm_crit_sect_); // Set VAD/DTX status. This function does not acquire a lock, and it is // created to be called only from inside a critical section. - int SetVADSafe(bool enable_dtx, bool enable_vad, ACMVADMode mode); + int SetVADSafe(bool enable_dtx, bool enable_vad, ACMVADMode mode) + EXCLUSIVE_LOCKS_REQUIRED(acm_crit_sect_); // Process buffered audio when dual-streaming is not enabled (When RED is // enabled still this function is used.) @@ -263,18 +286,22 @@ // -1: if encountering an error. // 0: otherwise. int PreprocessToAddData(const AudioFrame& in_frame, - const AudioFrame** ptr_out); + const AudioFrame** ptr_out) + EXCLUSIVE_LOCKS_REQUIRED(acm_crit_sect_); // Change required states after starting to receive the codec corresponding // to |index|. int UpdateUponReceivingCodec(int index); - int EncodeFragmentation(int fragmentation_index, int payload_type, + int EncodeFragmentation(int fragmentation_index, + int payload_type, uint32_t current_timestamp, ACMGenericCodec* encoder, - uint8_t* stream); + uint8_t* stream) + EXCLUSIVE_LOCKS_REQUIRED(acm_crit_sect_); - void ResetFragmentation(int vector_size); + void ResetFragmentation(int vector_size) + EXCLUSIVE_LOCKS_REQUIRED(acm_crit_sect_); // Get a pointer to AudioDecoder of the given codec. For some codecs, e.g. // iSAC, encoding and decoding have to be performed on a shared @@ -287,52 +314,54 @@ // codec owns the decoder-instance. For such codecs |*decoder| should be a // valid pointer, otherwise it will be NULL. int GetAudioDecoder(const CodecInst& codec, int codec_id, - int mirror_id, AudioDecoder** decoder); - - AudioPacketizationCallback* packetization_callback_; + int mirror_id, AudioDecoder** decoder) + EXCLUSIVE_LOCKS_REQUIRED(acm_crit_sect_); - int id_; - uint32_t expected_codec_ts_; - uint32_t expected_in_ts_; - CodecInst send_codec_inst_; - - uint8_t cng_nb_pltype_; - uint8_t cng_wb_pltype_; - uint8_t cng_swb_pltype_; - uint8_t cng_fb_pltype_; - - uint8_t red_pltype_; - bool vad_enabled_; - bool dtx_enabled_; - ACMVADMode vad_mode_; - ACMGenericCodec* codecs_[ACMCodecDB::kMaxNumCodecs]; - int mirror_codec_idx_[ACMCodecDB::kMaxNumCodecs]; - bool stereo_send_; - int current_send_codec_idx_; - bool send_codec_registered_; - ACMResampler resampler_; - AcmReceiver receiver_; CriticalSectionWrapper* acm_crit_sect_; - ACMVADCallback* vad_callback_; - - // RED/FEC. - bool is_first_red_; - bool fec_enabled_; + int id_; // TODO(henrik.lundin) Make const. + uint32_t expected_codec_ts_ GUARDED_BY(acm_crit_sect_); + uint32_t expected_in_ts_ GUARDED_BY(acm_crit_sect_); + CodecInst send_codec_inst_ GUARDED_BY(acm_crit_sect_); + + uint8_t cng_nb_pltype_ GUARDED_BY(acm_crit_sect_); + uint8_t cng_wb_pltype_ GUARDED_BY(acm_crit_sect_); + uint8_t cng_swb_pltype_ GUARDED_BY(acm_crit_sect_); + uint8_t cng_fb_pltype_ GUARDED_BY(acm_crit_sect_); + + uint8_t red_pltype_ GUARDED_BY(acm_crit_sect_); + bool vad_enabled_ GUARDED_BY(acm_crit_sect_); + bool dtx_enabled_ GUARDED_BY(acm_crit_sect_); + ACMVADMode vad_mode_ GUARDED_BY(acm_crit_sect_); + ACMGenericCodec* codecs_[ACMCodecDB::kMaxNumCodecs] + GUARDED_BY(acm_crit_sect_); + int mirror_codec_idx_[ACMCodecDB::kMaxNumCodecs] GUARDED_BY(acm_crit_sect_); + bool stereo_send_ GUARDED_BY(acm_crit_sect_); + int current_send_codec_idx_ GUARDED_BY(acm_crit_sect_); + bool send_codec_registered_ GUARDED_BY(acm_crit_sect_); + ACMResampler resampler_ GUARDED_BY(acm_crit_sect_); + AcmReceiver receiver_; // AcmReceiver has it's own internal lock. + + // RED. + bool is_first_red_ GUARDED_BY(acm_crit_sect_); + bool red_enabled_ GUARDED_BY(acm_crit_sect_); // TODO(turajs): |red_buffer_| is allocated in constructor, why having them // as pointers and not an array. If concerned about the memory, then make a // set-up function to allocate them only when they are going to be used, i.e. - // FEC or Dual-streaming is enabled. - uint8_t* red_buffer_; + // RED or Dual-streaming is enabled. + uint8_t* red_buffer_ GUARDED_BY(acm_crit_sect_); // TODO(turajs): we actually don't need |fragmentation_| as a member variable. // It is sufficient to keep the length & payload type of previous payload in // member variables. - RTPFragmentationHeader fragmentation_; - uint32_t last_fec_timestamp_; + RTPFragmentationHeader fragmentation_ GUARDED_BY(acm_crit_sect_); + uint32_t last_red_timestamp_ GUARDED_BY(acm_crit_sect_); + + // Codec internal FEC + bool codec_fec_enabled_ GUARDED_BY(acm_crit_sect_); // This is to keep track of CN instances where we can send DTMFs. - uint8_t previous_pltype_; + uint8_t previous_pltype_ GUARDED_BY(acm_crit_sect_); // Used when payloads are pushed into ACM without any RTP info // One example is when pre-encoded bit-stream is pushed from @@ -342,19 +371,110 @@ // be used in other methods, locks need to be taken. WebRtcRTPHeader* aux_rtp_header_; - bool receiver_initialized_; + bool receiver_initialized_ GUARDED_BY(acm_crit_sect_); - CriticalSectionWrapper* callback_crit_sect_; + AudioFrame preprocess_frame_ GUARDED_BY(acm_crit_sect_); + CodecInst secondary_send_codec_inst_ GUARDED_BY(acm_crit_sect_); + scoped_ptr secondary_encoder_ GUARDED_BY(acm_crit_sect_); + uint32_t codec_timestamp_ GUARDED_BY(acm_crit_sect_); + bool first_10ms_data_ GUARDED_BY(acm_crit_sect_); - AudioFrame preprocess_frame_; - CodecInst secondary_send_codec_inst_; - scoped_ptr secondary_encoder_; - uint32_t codec_timestamp_; - bool first_10ms_data_; + CriticalSectionWrapper* callback_crit_sect_; + AudioPacketizationCallback* packetization_callback_ + GUARDED_BY(callback_crit_sect_); + ACMVADCallback* vad_callback_ GUARDED_BY(callback_crit_sect_); }; } // namespace acm2 +class AudioCodingImpl : public AudioCoding { + public: + AudioCodingImpl(const Config& config) { + AudioCodingModule::Config config_old = config.ToOldConfig(); + acm_old_.reset(new acm2::AudioCodingModuleImpl(config_old)); + acm_old_->RegisterTransportCallback(config.transport); + acm_old_->RegisterVADCallback(config.vad_callback); + acm_old_->SetDtmfPlayoutStatus(config.play_dtmf); + if (config.initial_playout_delay_ms > 0) { + acm_old_->SetInitialPlayoutDelay(config.initial_playout_delay_ms); + } + playout_frequency_hz_ = config.playout_frequency_hz; + } + + virtual ~AudioCodingImpl() OVERRIDE {}; + + virtual bool RegisterSendCodec(AudioEncoder* send_codec) OVERRIDE; + + virtual bool RegisterSendCodec(int encoder_type, + uint8_t payload_type, + int frame_size_samples = 0) OVERRIDE; + + virtual const AudioEncoder* GetSenderInfo() const OVERRIDE; + + virtual const CodecInst* GetSenderCodecInst() OVERRIDE; + + virtual int Add10MsAudio(const AudioFrame& audio_frame) OVERRIDE; + + virtual const ReceiverInfo* GetReceiverInfo() const OVERRIDE; + + virtual bool RegisterReceiveCodec(AudioDecoder* receive_codec) OVERRIDE; + + virtual bool RegisterReceiveCodec(int decoder_type, + uint8_t payload_type) OVERRIDE; + + virtual bool InsertPacket(const uint8_t* incoming_payload, + int32_t payload_len_bytes, + const WebRtcRTPHeader& rtp_info) OVERRIDE; + + virtual bool InsertPayload(const uint8_t* incoming_payload, + int32_t payload_len_byte, + uint8_t payload_type, + uint32_t timestamp) OVERRIDE; + + virtual bool SetMinimumPlayoutDelay(int time_ms) OVERRIDE; + + virtual bool SetMaximumPlayoutDelay(int time_ms) OVERRIDE; + + virtual int LeastRequiredDelayMs() const OVERRIDE; + + virtual bool PlayoutTimestamp(uint32_t* timestamp) OVERRIDE; + + virtual bool Get10MsAudio(AudioFrame* audio_frame) OVERRIDE; + + virtual bool NetworkStatistics( + ACMNetworkStatistics* network_statistics) OVERRIDE; + + virtual bool EnableNack(size_t max_nack_list_size) OVERRIDE; + + virtual void DisableNack() OVERRIDE; + + virtual bool SetVad(bool enable_dtx, + bool enable_vad, + ACMVADMode vad_mode) OVERRIDE; + + virtual std::vector GetNackList( + int round_trip_time_ms) const OVERRIDE; + + virtual void GetDecodingCallStatistics( + AudioDecodingCallStats* call_stats) const OVERRIDE; + + private: + // Temporary method to be used during redesign phase. + // Maps |codec_type| (a value from the anonymous enum in acm2::ACMCodecDB) to + // |codec_name|, |sample_rate_hz|, and |channels|. + // TODO(henrik.lundin) Remove this when no longer needed. + static bool MapCodecTypeToParameters(int codec_type, + std::string* codec_name, + int* sample_rate_hz, + int* channels); + + int playout_frequency_hz_; + // TODO(henrik.lundin): All members below this line are temporary and should + // be removed after refactoring is completed. + scoped_ptr acm_old_; + CodecInst current_send_codec_; +}; + } // namespace webrtc #endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_AUDIO_CODING_MODULE_IMPL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,932 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/checks.h" +#include "webrtc/base/md5digest.h" +#include "webrtc/base/thread_annotations.h" +#include "webrtc/modules/audio_coding/main/acm2/acm_receive_test.h" +#include "webrtc/modules/audio_coding/main/acm2/acm_send_test.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" +#include "webrtc/modules/audio_coding/neteq/tools/audio_checksum.h" +#include "webrtc/modules/audio_coding/neteq/tools/audio_loop.h" +#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" +#include "webrtc/modules/audio_coding/neteq/tools/output_audio_file.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet.h" +#include "webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h" +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/compile_assert.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/event_wrapper.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/system_wrappers/interface/sleep.h" +#include "webrtc/system_wrappers/interface/thread_wrapper.h" +#include "webrtc/test/testsupport/fileutils.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace webrtc { + +const int kSampleRateHz = 16000; +const int kNumSamples10ms = kSampleRateHz / 100; +const int kFrameSizeMs = 10; // Multiple of 10. +const int kFrameSizeSamples = kFrameSizeMs / 10 * kNumSamples10ms; +const int kPayloadSizeBytes = kFrameSizeSamples * sizeof(int16_t); +const uint8_t kPayloadType = 111; + +class RtpUtility { + public: + RtpUtility(int samples_per_packet, uint8_t payload_type) + : samples_per_packet_(samples_per_packet), payload_type_(payload_type) {} + + virtual ~RtpUtility() {} + + void Populate(WebRtcRTPHeader* rtp_header) { + rtp_header->header.sequenceNumber = 0xABCD; + rtp_header->header.timestamp = 0xABCDEF01; + rtp_header->header.payloadType = payload_type_; + rtp_header->header.markerBit = false; + rtp_header->header.ssrc = 0x1234; + rtp_header->header.numCSRCs = 0; + rtp_header->frameType = kAudioFrameSpeech; + + rtp_header->header.payload_type_frequency = kSampleRateHz; + rtp_header->type.Audio.channel = 1; + rtp_header->type.Audio.isCNG = false; + } + + void Forward(WebRtcRTPHeader* rtp_header) { + ++rtp_header->header.sequenceNumber; + rtp_header->header.timestamp += samples_per_packet_; + } + + private: + int samples_per_packet_; + uint8_t payload_type_; +}; + +class PacketizationCallbackStub : public AudioPacketizationCallback { + public: + PacketizationCallbackStub() + : num_calls_(0), + crit_sect_(CriticalSectionWrapper::CreateCriticalSection()) {} + + virtual int32_t SendData( + FrameType frame_type, + uint8_t payload_type, + uint32_t timestamp, + const uint8_t* payload_data, + uint16_t payload_len_bytes, + const RTPFragmentationHeader* fragmentation) OVERRIDE { + CriticalSectionScoped lock(crit_sect_.get()); + ++num_calls_; + last_payload_vec_.assign(payload_data, payload_data + payload_len_bytes); + return 0; + } + + int num_calls() const { + CriticalSectionScoped lock(crit_sect_.get()); + return num_calls_; + } + + int last_payload_len_bytes() const { + CriticalSectionScoped lock(crit_sect_.get()); + return last_payload_vec_.size(); + } + + void SwapBuffers(std::vector* payload) { + CriticalSectionScoped lock(crit_sect_.get()); + last_payload_vec_.swap(*payload); + } + + private: + int num_calls_ GUARDED_BY(crit_sect_); + std::vector last_payload_vec_ GUARDED_BY(crit_sect_); + const scoped_ptr crit_sect_; +}; + +class AudioCodingModuleTest : public ::testing::Test { + protected: + AudioCodingModuleTest() + : rtp_utility_(new RtpUtility(kFrameSizeSamples, kPayloadType)) { + config_.transport = &packet_cb_; + } + + ~AudioCodingModuleTest() {} + + void TearDown() OVERRIDE {} + + void SetUp() OVERRIDE { + rtp_utility_->Populate(&rtp_header_); + + input_frame_.sample_rate_hz_ = kSampleRateHz; + input_frame_.num_channels_ = 1; + input_frame_.samples_per_channel_ = kSampleRateHz * 10 / 1000; // 10 ms. + COMPILE_ASSERT(kSampleRateHz * 10 / 1000 <= AudioFrame::kMaxDataSizeSamples, + audio_frame_too_small); + memset(input_frame_.data_, + 0, + input_frame_.samples_per_channel_ * sizeof(input_frame_.data_[0])); + } + + void CreateAcm() { + acm_.reset(AudioCoding::Create(config_)); + ASSERT_TRUE(acm_.get() != NULL); + RegisterCodec(); + } + + virtual void RegisterCodec() { + // Register L16 codec in ACM. + int codec_type = acm2::ACMCodecDB::kNone; + switch (kSampleRateHz) { + case 8000: + codec_type = acm2::ACMCodecDB::kPCM16B; + break; + case 16000: + codec_type = acm2::ACMCodecDB::kPCM16Bwb; + break; + case 32000: + codec_type = acm2::ACMCodecDB::kPCM16Bswb32kHz; + break; + default: + FATAL() << "Sample rate not supported in this test."; + } + ASSERT_TRUE(acm_->RegisterSendCodec(codec_type, kPayloadType)); + ASSERT_TRUE(acm_->RegisterReceiveCodec(codec_type, kPayloadType)); + } + + virtual void InsertPacketAndPullAudio() { + InsertPacket(); + PullAudio(); + } + + virtual void InsertPacket() { + const uint8_t kPayload[kPayloadSizeBytes] = {0}; + ASSERT_TRUE(acm_->InsertPacket(kPayload, kPayloadSizeBytes, rtp_header_)); + rtp_utility_->Forward(&rtp_header_); + } + + virtual void PullAudio() { + AudioFrame audio_frame; + ASSERT_TRUE(acm_->Get10MsAudio(&audio_frame)); + } + + virtual void InsertAudio() { + int encoded_bytes = acm_->Add10MsAudio(input_frame_); + ASSERT_GE(encoded_bytes, 0); + input_frame_.timestamp_ += kNumSamples10ms; + } + + AudioCoding::Config config_; + scoped_ptr rtp_utility_; + scoped_ptr acm_; + PacketizationCallbackStub packet_cb_; + WebRtcRTPHeader rtp_header_; + AudioFrame input_frame_; +}; + +// Check if the statistics are initialized correctly. Before any call to ACM +// all fields have to be zero. +TEST_F(AudioCodingModuleTest, DISABLED_ON_ANDROID(InitializedToZero)) { + CreateAcm(); + AudioDecodingCallStats stats; + acm_->GetDecodingCallStatistics(&stats); + EXPECT_EQ(0, stats.calls_to_neteq); + EXPECT_EQ(0, stats.calls_to_silence_generator); + EXPECT_EQ(0, stats.decoded_normal); + EXPECT_EQ(0, stats.decoded_cng); + EXPECT_EQ(0, stats.decoded_plc); + EXPECT_EQ(0, stats.decoded_plc_cng); +} + +// Apply an initial playout delay. Calls to AudioCodingModule::PlayoutData10ms() +// should result in generating silence, check the associated field. +TEST_F(AudioCodingModuleTest, DISABLED_ON_ANDROID(SilenceGeneratorCalled)) { + const int kInitialDelay = 100; + config_.initial_playout_delay_ms = kInitialDelay; + CreateAcm(); + AudioDecodingCallStats stats; + + int num_calls = 0; + for (int time_ms = 0; time_ms < kInitialDelay; + time_ms += kFrameSizeMs, ++num_calls) { + InsertPacketAndPullAudio(); + } + acm_->GetDecodingCallStatistics(&stats); + EXPECT_EQ(0, stats.calls_to_neteq); + EXPECT_EQ(num_calls, stats.calls_to_silence_generator); + EXPECT_EQ(0, stats.decoded_normal); + EXPECT_EQ(0, stats.decoded_cng); + EXPECT_EQ(0, stats.decoded_plc); + EXPECT_EQ(0, stats.decoded_plc_cng); +} + +// Insert some packets and pull audio. Check statistics are valid. Then, +// simulate packet loss and check if PLC and PLC-to-CNG statistics are +// correctly updated. +TEST_F(AudioCodingModuleTest, DISABLED_ON_ANDROID(NetEqCalls)) { + CreateAcm(); + AudioDecodingCallStats stats; + const int kNumNormalCalls = 10; + + for (int num_calls = 0; num_calls < kNumNormalCalls; ++num_calls) { + InsertPacketAndPullAudio(); + } + acm_->GetDecodingCallStatistics(&stats); + EXPECT_EQ(kNumNormalCalls, stats.calls_to_neteq); + EXPECT_EQ(0, stats.calls_to_silence_generator); + EXPECT_EQ(kNumNormalCalls, stats.decoded_normal); + EXPECT_EQ(0, stats.decoded_cng); + EXPECT_EQ(0, stats.decoded_plc); + EXPECT_EQ(0, stats.decoded_plc_cng); + + const int kNumPlc = 3; + const int kNumPlcCng = 5; + + // Simulate packet-loss. NetEq first performs PLC then PLC fades to CNG. + for (int n = 0; n < kNumPlc + kNumPlcCng; ++n) { + PullAudio(); + } + acm_->GetDecodingCallStatistics(&stats); + EXPECT_EQ(kNumNormalCalls + kNumPlc + kNumPlcCng, stats.calls_to_neteq); + EXPECT_EQ(0, stats.calls_to_silence_generator); + EXPECT_EQ(kNumNormalCalls, stats.decoded_normal); + EXPECT_EQ(0, stats.decoded_cng); + EXPECT_EQ(kNumPlc, stats.decoded_plc); + EXPECT_EQ(kNumPlcCng, stats.decoded_plc_cng); +} + +TEST_F(AudioCodingModuleTest, VerifyOutputFrame) { + CreateAcm(); + AudioFrame audio_frame; + const int kSampleRateHz = 32000; + EXPECT_TRUE(acm_->Get10MsAudio(&audio_frame)); + EXPECT_EQ(0u, audio_frame.timestamp_); + EXPECT_GT(audio_frame.num_channels_, 0); + EXPECT_EQ(kSampleRateHz / 100, audio_frame.samples_per_channel_); + EXPECT_EQ(kSampleRateHz, audio_frame.sample_rate_hz_); +} + +// A multi-threaded test for ACM. This base class is using the PCM16b 16 kHz +// codec, while the derive class AcmIsacMtTest is using iSAC. +class AudioCodingModuleMtTest : public AudioCodingModuleTest { + protected: + static const int kNumPackets = 500; + static const int kNumPullCalls = 500; + + AudioCodingModuleMtTest() + : AudioCodingModuleTest(), + send_thread_(ThreadWrapper::CreateThread(CbSendThread, + this, + kRealtimePriority, + "send")), + insert_packet_thread_(ThreadWrapper::CreateThread(CbInsertPacketThread, + this, + kRealtimePriority, + "insert_packet")), + pull_audio_thread_(ThreadWrapper::CreateThread(CbPullAudioThread, + this, + kRealtimePriority, + "pull_audio")), + test_complete_(EventWrapper::Create()), + send_count_(0), + insert_packet_count_(0), + pull_audio_count_(0), + crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + next_insert_packet_time_ms_(0), + fake_clock_(new SimulatedClock(0)) { + config_.clock = fake_clock_.get(); + } + + virtual void SetUp() OVERRIDE { + AudioCodingModuleTest::SetUp(); + CreateAcm(); + StartThreads(); + } + + void StartThreads() { + unsigned int thread_id = 0; + ASSERT_TRUE(send_thread_->Start(thread_id)); + ASSERT_TRUE(insert_packet_thread_->Start(thread_id)); + ASSERT_TRUE(pull_audio_thread_->Start(thread_id)); + } + + virtual void TearDown() OVERRIDE { + AudioCodingModuleTest::TearDown(); + pull_audio_thread_->Stop(); + send_thread_->Stop(); + insert_packet_thread_->Stop(); + } + + EventTypeWrapper RunTest() { + return test_complete_->Wait(10 * 60 * 1000); // 10 minutes' timeout. + } + + virtual bool TestDone() { + if (packet_cb_.num_calls() > kNumPackets) { + CriticalSectionScoped lock(crit_sect_.get()); + if (pull_audio_count_ > kNumPullCalls) { + // Both conditions for completion are met. End the test. + return true; + } + } + return false; + } + + static bool CbSendThread(void* context) { + return reinterpret_cast(context)->CbSendImpl(); + } + + // The send thread doesn't have to care about the current simulated time, + // since only the AcmReceiver is using the clock. + bool CbSendImpl() { + SleepMs(1); + if (HasFatalFailure()) { + // End the test early if a fatal failure (ASSERT_*) has occurred. + test_complete_->Set(); + } + ++send_count_; + InsertAudio(); + if (TestDone()) { + test_complete_->Set(); + } + return true; + } + + static bool CbInsertPacketThread(void* context) { + return reinterpret_cast(context) + ->CbInsertPacketImpl(); + } + + bool CbInsertPacketImpl() { + SleepMs(1); + { + CriticalSectionScoped lock(crit_sect_.get()); + if (fake_clock_->TimeInMilliseconds() < next_insert_packet_time_ms_) { + return true; + } + next_insert_packet_time_ms_ += 10; + } + // Now we're not holding the crit sect when calling ACM. + ++insert_packet_count_; + InsertPacket(); + return true; + } + + static bool CbPullAudioThread(void* context) { + return reinterpret_cast(context) + ->CbPullAudioImpl(); + } + + bool CbPullAudioImpl() { + SleepMs(1); + { + CriticalSectionScoped lock(crit_sect_.get()); + // Don't let the insert thread fall behind. + if (next_insert_packet_time_ms_ < fake_clock_->TimeInMilliseconds()) { + return true; + } + ++pull_audio_count_; + } + // Now we're not holding the crit sect when calling ACM. + PullAudio(); + fake_clock_->AdvanceTimeMilliseconds(10); + return true; + } + + scoped_ptr send_thread_; + scoped_ptr insert_packet_thread_; + scoped_ptr pull_audio_thread_; + const scoped_ptr test_complete_; + int send_count_; + int insert_packet_count_; + int pull_audio_count_ GUARDED_BY(crit_sect_); + const scoped_ptr crit_sect_; + int64_t next_insert_packet_time_ms_ GUARDED_BY(crit_sect_); + scoped_ptr fake_clock_; +}; + +TEST_F(AudioCodingModuleMtTest, DoTest) { + EXPECT_EQ(kEventSignaled, RunTest()); +} + +// This is a multi-threaded ACM test using iSAC. The test encodes audio +// from a PCM file. The most recent encoded frame is used as input to the +// receiving part. Depending on timing, it may happen that the same RTP packet +// is inserted into the receiver multiple times, but this is a valid use-case, +// and simplifies the test code a lot. +class AcmIsacMtTest : public AudioCodingModuleMtTest { + protected: + static const int kNumPackets = 500; + static const int kNumPullCalls = 500; + + AcmIsacMtTest() + : AudioCodingModuleMtTest(), + last_packet_number_(0) {} + + ~AcmIsacMtTest() {} + + virtual void SetUp() OVERRIDE { + AudioCodingModuleTest::SetUp(); + CreateAcm(); + + // Set up input audio source to read from specified file, loop after 5 + // seconds, and deliver blocks of 10 ms. + const std::string input_file_name = + webrtc::test::ResourcePath("audio_coding/speech_mono_16kHz", "pcm"); + audio_loop_.Init(input_file_name, 5 * kSampleRateHz, kNumSamples10ms); + + // Generate one packet to have something to insert. + int loop_counter = 0; + while (packet_cb_.last_payload_len_bytes() == 0) { + InsertAudio(); + ASSERT_LT(loop_counter++, 10); + } + // Set |last_packet_number_| to one less that |num_calls| so that the packet + // will be fetched in the next InsertPacket() call. + last_packet_number_ = packet_cb_.num_calls() - 1; + + StartThreads(); + } + + virtual void RegisterCodec() OVERRIDE { + COMPILE_ASSERT(kSampleRateHz == 16000, test_designed_for_isac_16khz); + + // Register iSAC codec in ACM, effectively unregistering the PCM16B codec + // registered in AudioCodingModuleTest::SetUp(); + ASSERT_TRUE(acm_->RegisterSendCodec(acm2::ACMCodecDB::kISAC, kPayloadType)); + ASSERT_TRUE( + acm_->RegisterReceiveCodec(acm2::ACMCodecDB::kISAC, kPayloadType)); + } + + virtual void InsertPacket() OVERRIDE { + int num_calls = packet_cb_.num_calls(); // Store locally for thread safety. + if (num_calls > last_packet_number_) { + // Get the new payload out from the callback handler. + // Note that since we swap buffers here instead of directly inserting + // a pointer to the data in |packet_cb_|, we avoid locking the callback + // for the duration of the IncomingPacket() call. + packet_cb_.SwapBuffers(&last_payload_vec_); + ASSERT_GT(last_payload_vec_.size(), 0u); + rtp_utility_->Forward(&rtp_header_); + last_packet_number_ = num_calls; + } + ASSERT_GT(last_payload_vec_.size(), 0u); + ASSERT_TRUE(acm_->InsertPacket( + &last_payload_vec_[0], last_payload_vec_.size(), rtp_header_)); + } + + virtual void InsertAudio() OVERRIDE { + memcpy(input_frame_.data_, audio_loop_.GetNextBlock(), kNumSamples10ms); + AudioCodingModuleTest::InsertAudio(); + } + + // This method is the same as AudioCodingModuleMtTest::TestDone(), but here + // it is using the constants defined in this class (i.e., shorter test run). + virtual bool TestDone() OVERRIDE { + if (packet_cb_.num_calls() > kNumPackets) { + CriticalSectionScoped lock(crit_sect_.get()); + if (pull_audio_count_ > kNumPullCalls) { + // Both conditions for completion are met. End the test. + return true; + } + } + return false; + } + + int last_packet_number_; + std::vector last_payload_vec_; + test::AudioLoop audio_loop_; +}; + +TEST_F(AcmIsacMtTest, DoTest) { + EXPECT_EQ(kEventSignaled, RunTest()); +} + +class AcmReceiverBitExactness : public ::testing::Test { + public: + static std::string PlatformChecksum(std::string win64, + std::string android, + std::string others) { +#if defined(_WIN32) && defined(WEBRTC_ARCH_64_BITS) + return win64; +#elif defined(WEBRTC_ANDROID) + return android; +#else + return others; +#endif + } + + protected: + void Run(int output_freq_hz, const std::string& checksum_ref) { + const std::string input_file_name = + webrtc::test::ResourcePath("audio_coding/neteq_universal_new", "rtp"); + scoped_ptr packet_source( + test::RtpFileSource::Create(input_file_name)); +#ifdef WEBRTC_ANDROID + // Filter out iLBC and iSAC-swb since they are not supported on Android. + packet_source->FilterOutPayloadType(102); // iLBC. + packet_source->FilterOutPayloadType(104); // iSAC-swb. +#endif + + test::AudioChecksum checksum; + const std::string output_file_name = + webrtc::test::OutputPath() + + ::testing::UnitTest::GetInstance() + ->current_test_info() + ->test_case_name() + + "_" + ::testing::UnitTest::GetInstance()->current_test_info()->name() + + "_output.pcm"; + test::OutputAudioFile output_file(output_file_name); + test::AudioSinkFork output(&checksum, &output_file); + + test::AcmReceiveTest test(packet_source.get(), &output, output_freq_hz, + test::AcmReceiveTest::kArbitraryChannels); + ASSERT_NO_FATAL_FAILURE(test.RegisterNetEqTestCodecs()); + test.Run(); + + std::string checksum_string = checksum.Finish(); + EXPECT_EQ(checksum_ref, checksum_string); + } +}; + +TEST_F(AcmReceiverBitExactness, 8kHzOutput) { + Run(8000, + PlatformChecksum("bd6f8d9602cd82444ea2539e674df747", + "6ac89c7145072c26bfeba602cd661afb", + "8a8440f5511eb729221b9aac25cda3a0")); +} + +TEST_F(AcmReceiverBitExactness, 16kHzOutput) { + Run(16000, + PlatformChecksum("a39bc6ee0c4eb15f3ad2f43cebcc571d", + "3e888eb04f57db2c6ef952fe64f17fe6", + "7be583092c5adbcb0f6cd66eca20ea63")); +} + +TEST_F(AcmReceiverBitExactness, 32kHzOutput) { + Run(32000, + PlatformChecksum("80964572aaa2dc92f9e34896dd3802b3", + "aeca37e963310f5b6552b7edea23c2f1", + "3a84188abe9fca25fedd6034760f3e22")); +} + +TEST_F(AcmReceiverBitExactness, 48kHzOutput) { + Run(48000, + PlatformChecksum("8aacde91f390e0d5a9c2ed571a25fd37", + "76b9e99e0a3998aa28355e7a2bd836f7", + "89b4b19bdb4de40f1d88302ef8cb9f9b")); +} + +// This test verifies bit exactness for the send-side of ACM. The test setup is +// a chain of three different test classes: +// +// test::AcmSendTest -> AcmSenderBitExactness -> test::AcmReceiveTest +// +// The receiver side is driving the test by requesting new packets from +// AcmSenderBitExactness::NextPacket(). This method, in turn, asks for the +// packet from test::AcmSendTest::NextPacket, which inserts audio from the +// input file until one packet is produced. (The input file loops indefinitely.) +// Before passing the packet to the receiver, this test class verifies the +// packet header and updates a payload checksum with the new payload. The +// decoded output from the receiver is also verified with a (separate) checksum. +class AcmSenderBitExactness : public ::testing::Test, + public test::PacketSource { + protected: + static const int kTestDurationMs = 1000; + + AcmSenderBitExactness() + : frame_size_rtp_timestamps_(0), + packet_count_(0), + payload_type_(0), + last_sequence_number_(0), + last_timestamp_(0) {} + + // Sets up the test::AcmSendTest object. Returns true on success, otherwise + // false. + bool SetUpSender() { + const std::string input_file_name = + webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); + // Note that |audio_source_| will loop forever. The test duration is set + // explicitly by |kTestDurationMs|. + audio_source_.reset(new test::InputAudioFile(input_file_name)); + static const int kSourceRateHz = 32000; + send_test_.reset(new test::AcmSendTest( + audio_source_.get(), kSourceRateHz, kTestDurationMs)); + return send_test_.get() != NULL; + } + + // Registers a send codec in the test::AcmSendTest object. Returns true on + // success, false on failure. + bool RegisterSendCodec(int codec_type, + int channels, + int payload_type, + int frame_size_samples, + int frame_size_rtp_timestamps) { + payload_type_ = payload_type; + frame_size_rtp_timestamps_ = frame_size_rtp_timestamps; + return send_test_->RegisterCodec( + codec_type, channels, payload_type, frame_size_samples); + } + + // Runs the test. SetUpSender() and RegisterSendCodec() must have been called + // before calling this method. + void Run(const std::string& audio_checksum_ref, + const std::string& payload_checksum_ref, + int expected_packets, + test::AcmReceiveTest::NumOutputChannels expected_channels) { + // Set up the receiver used to decode the packets and verify the decoded + // output. + test::AudioChecksum audio_checksum; + const std::string output_file_name = + webrtc::test::OutputPath() + + ::testing::UnitTest::GetInstance() + ->current_test_info() + ->test_case_name() + + "_" + + ::testing::UnitTest::GetInstance()->current_test_info()->name() + + "_output.pcm"; + test::OutputAudioFile output_file(output_file_name); + // Have the output audio sent both to file and to the checksum calculator. + test::AudioSinkFork output(&audio_checksum, &output_file); + const int kOutputFreqHz = 8000; + test::AcmReceiveTest receive_test( + this, &output, kOutputFreqHz, expected_channels); + ASSERT_NO_FATAL_FAILURE(receive_test.RegisterDefaultCodecs()); + + // This is where the actual test is executed. + receive_test.Run(); + + // Extract and verify the audio checksum. + std::string checksum_string = audio_checksum.Finish(); + EXPECT_EQ(audio_checksum_ref, checksum_string); + + // Extract and verify the payload checksum. + char checksum_result[rtc::Md5Digest::kSize]; + payload_checksum_.Finish(checksum_result, rtc::Md5Digest::kSize); + checksum_string = rtc::hex_encode(checksum_result, rtc::Md5Digest::kSize); + EXPECT_EQ(payload_checksum_ref, checksum_string); + + // Verify number of packets produced. + EXPECT_EQ(expected_packets, packet_count_); + } + + // Returns a pointer to the next packet. Returns NULL if the source is + // depleted (i.e., the test duration is exceeded), or if an error occurred. + // Inherited from test::PacketSource. + virtual test::Packet* NextPacket() OVERRIDE { + // Get the next packet from AcmSendTest. Ownership of |packet| is + // transferred to this method. + test::Packet* packet = send_test_->NextPacket(); + if (!packet) + return NULL; + + VerifyPacket(packet); + // TODO(henrik.lundin) Save the packet to file as well. + + // Pass it on to the caller. The caller becomes the owner of |packet|. + return packet; + } + + // Verifies the packet. + void VerifyPacket(const test::Packet* packet) { + EXPECT_TRUE(packet->valid_header()); + // (We can check the header fields even if valid_header() is false.) + EXPECT_EQ(payload_type_, packet->header().payloadType); + if (packet_count_ > 0) { + // This is not the first packet. + uint16_t sequence_number_diff = + packet->header().sequenceNumber - last_sequence_number_; + EXPECT_EQ(1, sequence_number_diff); + uint32_t timestamp_diff = packet->header().timestamp - last_timestamp_; + EXPECT_EQ(frame_size_rtp_timestamps_, timestamp_diff); + } + ++packet_count_; + last_sequence_number_ = packet->header().sequenceNumber; + last_timestamp_ = packet->header().timestamp; + // Update the checksum. + payload_checksum_.Update(packet->payload(), packet->payload_length_bytes()); + } + + void SetUpTest(int codec_type, + int channels, + int payload_type, + int codec_frame_size_samples, + int codec_frame_size_rtp_timestamps) { + ASSERT_TRUE(SetUpSender()); + ASSERT_TRUE(RegisterSendCodec(codec_type, + channels, + payload_type, + codec_frame_size_samples, + codec_frame_size_rtp_timestamps)); + } + + scoped_ptr send_test_; + scoped_ptr audio_source_; + uint32_t frame_size_rtp_timestamps_; + int packet_count_; + uint8_t payload_type_; + uint16_t last_sequence_number_; + uint32_t last_timestamp_; + rtc::Md5Digest payload_checksum_; +}; + +TEST_F(AcmSenderBitExactness, IsacWb30ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest(acm2::ACMCodecDB::kISAC, 1, 103, 480, 480)); + Run(AcmReceiverBitExactness::PlatformChecksum( + "c7e5bdadfa2871df95639fcc297cf23d", + "0499ca260390769b3172136faad925b9", + "0b58f9eeee43d5891f5f6c75e77984a3"), + AcmReceiverBitExactness::PlatformChecksum( + "d42cb5195463da26c8129bbfe73a22e6", + "83de248aea9c3c2bd680b6952401b4ca", + "3c79f16f34218271f3dca4e2b1dfe1bb"), + 33, + test::AcmReceiveTest::kMonoOutput); +} + +TEST_F(AcmSenderBitExactness, IsacWb60ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest(acm2::ACMCodecDB::kISAC, 1, 103, 960, 960)); + Run(AcmReceiverBitExactness::PlatformChecksum( + "14d63c5f08127d280e722e3191b73bdd", + "8da003e16c5371af2dc2be79a50f9076", + "1ad29139a04782a33daad8c2b9b35875"), + AcmReceiverBitExactness::PlatformChecksum( + "ebe04a819d3a9d83a83a17f271e1139a", + "97aeef98553b5a4b5a68f8b716e8eaf0", + "9e0a0ab743ad987b55b8e14802769c56"), + 16, + test::AcmReceiveTest::kMonoOutput); +} + +TEST_F(AcmSenderBitExactness, DISABLED_ON_ANDROID(IsacSwb30ms)) { + ASSERT_NO_FATAL_FAILURE( + SetUpTest(acm2::ACMCodecDB::kISACSWB, 1, 104, 960, 960)); + Run(AcmReceiverBitExactness::PlatformChecksum( + "98d960600eb4ddb3fcbe11f5057ddfd7", + "", + "2f6dfe142f735f1d96f6bd86d2526f42"), + AcmReceiverBitExactness::PlatformChecksum( + "cc9d2d86a71d6f99f97680a5c27e2762", + "", + "7b214fc3a5e33d68bf30e77969371f31"), + 33, + test::AcmReceiveTest::kMonoOutput); +} + +TEST_F(AcmSenderBitExactness, Pcm16_8000khz_10ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest(acm2::ACMCodecDB::kPCM16B, 1, 107, 80, 80)); + Run("de4a98e1406f8b798d99cd0704e862e2", + "c1edd36339ce0326cc4550041ad719a0", + 100, + test::AcmReceiveTest::kMonoOutput); +} + +TEST_F(AcmSenderBitExactness, Pcm16_16000khz_10ms) { + ASSERT_NO_FATAL_FAILURE( + SetUpTest(acm2::ACMCodecDB::kPCM16Bwb, 1, 108, 160, 160)); + Run("ae646d7b68384a1269cc080dd4501916", + "ad786526383178b08d80d6eee06e9bad", + 100, + test::AcmReceiveTest::kMonoOutput); +} + +TEST_F(AcmSenderBitExactness, Pcm16_32000khz_10ms) { + ASSERT_NO_FATAL_FAILURE( + SetUpTest(acm2::ACMCodecDB::kPCM16Bswb32kHz, 1, 109, 320, 320)); + Run("7fe325e8fbaf755e3c5df0b11a4774fb", + "5ef82ea885e922263606c6fdbc49f651", + 100, + test::AcmReceiveTest::kMonoOutput); +} + +TEST_F(AcmSenderBitExactness, Pcm16_stereo_8000khz_10ms) { + ASSERT_NO_FATAL_FAILURE( + SetUpTest(acm2::ACMCodecDB::kPCM16B_2ch, 2, 111, 80, 80)); + Run("fb263b74e7ac3de915474d77e4744ceb", + "62ce5adb0d4965d0a52ec98ae7f98974", + 100, + test::AcmReceiveTest::kStereoOutput); +} + +TEST_F(AcmSenderBitExactness, Pcm16_stereo_16000khz_10ms) { + ASSERT_NO_FATAL_FAILURE( + SetUpTest(acm2::ACMCodecDB::kPCM16Bwb_2ch, 2, 112, 160, 160)); + Run("d09e9239553649d7ac93e19d304281fd", + "41ca8edac4b8c71cd54fd9f25ec14870", + 100, + test::AcmReceiveTest::kStereoOutput); +} + +TEST_F(AcmSenderBitExactness, Pcm16_stereo_32000khz_10ms) { + ASSERT_NO_FATAL_FAILURE( + SetUpTest(acm2::ACMCodecDB::kPCM16Bswb32kHz_2ch, 2, 113, 320, 320)); + Run("5f025d4f390982cc26b3d92fe02e3044", + "50e58502fb04421bf5b857dda4c96879", + 100, + test::AcmReceiveTest::kStereoOutput); +} + +TEST_F(AcmSenderBitExactness, Pcmu_20ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest(acm2::ACMCodecDB::kPCMU, 1, 0, 160, 160)); + Run("81a9d4c0bb72e9becc43aef124c981e9", + "8f9b8750bd80fe26b6cbf6659b89f0f9", + 50, + test::AcmReceiveTest::kMonoOutput); +} + +TEST_F(AcmSenderBitExactness, Pcma_20ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest(acm2::ACMCodecDB::kPCMA, 1, 8, 160, 160)); + Run("39611f798969053925a49dc06d08de29", + "6ad745e55aa48981bfc790d0eeef2dd1", + 50, + test::AcmReceiveTest::kMonoOutput); +} + +TEST_F(AcmSenderBitExactness, Pcmu_stereo_20ms) { + ASSERT_NO_FATAL_FAILURE( + SetUpTest(acm2::ACMCodecDB::kPCMU_2ch, 2, 110, 160, 160)); + Run("437bec032fdc5cbaa0d5175430af7b18", + "60b6f25e8d1e74cb679cfe756dd9bca5", + 50, + test::AcmReceiveTest::kStereoOutput); +} + +TEST_F(AcmSenderBitExactness, Pcma_stereo_20ms) { + ASSERT_NO_FATAL_FAILURE( + SetUpTest(acm2::ACMCodecDB::kPCMA_2ch, 2, 118, 160, 160)); + Run("a5c6d83c5b7cedbeff734238220a4b0c", + "92b282c83efd20e7eeef52ba40842cf7", + 50, + test::AcmReceiveTest::kStereoOutput); +} + +TEST_F(AcmSenderBitExactness, DISABLED_ON_ANDROID(Ilbc_30ms)) { + ASSERT_NO_FATAL_FAILURE(SetUpTest(acm2::ACMCodecDB::kILBC, 1, 102, 240, 240)); + Run(AcmReceiverBitExactness::PlatformChecksum( + "7b6ec10910debd9af08011d3ed5249f7", + "android_audio", + "7b6ec10910debd9af08011d3ed5249f7"), + AcmReceiverBitExactness::PlatformChecksum( + "cfae2e9f6aba96e145f2bcdd5050ce78", + "android_payload", + "cfae2e9f6aba96e145f2bcdd5050ce78"), + 33, + test::AcmReceiveTest::kMonoOutput); +} + +TEST_F(AcmSenderBitExactness, DISABLED_ON_ANDROID(G722_20ms)) { + ASSERT_NO_FATAL_FAILURE(SetUpTest(acm2::ACMCodecDB::kG722, 1, 9, 320, 160)); + Run(AcmReceiverBitExactness::PlatformChecksum( + "7d759436f2533582950d148b5161a36c", + "android_audio", + "7d759436f2533582950d148b5161a36c"), + AcmReceiverBitExactness::PlatformChecksum( + "fc68a87e1380614e658087cb35d5ca10", + "android_payload", + "fc68a87e1380614e658087cb35d5ca10"), + 50, + test::AcmReceiveTest::kMonoOutput); +} + +TEST_F(AcmSenderBitExactness, DISABLED_ON_ANDROID(G722_stereo_20ms)) { + ASSERT_NO_FATAL_FAILURE( + SetUpTest(acm2::ACMCodecDB::kG722_2ch, 2, 119, 320, 160)); + Run(AcmReceiverBitExactness::PlatformChecksum( + "7190ee718ab3d80eca181e5f7140c210", + "android_audio", + "7190ee718ab3d80eca181e5f7140c210"), + AcmReceiverBitExactness::PlatformChecksum( + "66516152eeaa1e650ad94ff85f668dac", + "android_payload", + "66516152eeaa1e650ad94ff85f668dac"), + 50, + test::AcmReceiveTest::kStereoOutput); +} + +TEST_F(AcmSenderBitExactness, Opus_stereo_20ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest(acm2::ACMCodecDB::kOpus, 2, 120, 960, 960)); + Run(AcmReceiverBitExactness::PlatformChecksum( + "855041f2490b887302bce9d544731849", + "1e1a0fce893fef2d66886a7f09e2ebce", + "855041f2490b887302bce9d544731849"), + AcmReceiverBitExactness::PlatformChecksum( + "d781cce1ab986b618d0da87226cdde30", + "1a1fe04dd12e755949987c8d729fb3e0", + "d781cce1ab986b618d0da87226cdde30"), + 50, + test::AcmReceiveTest::kStereoOutput); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,1052 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/md5digest.h" +#include "webrtc/base/thread_annotations.h" +#include "webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h" +#include "webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" +#include "webrtc/modules/audio_coding/neteq/tools/audio_checksum.h" +#include "webrtc/modules/audio_coding/neteq/tools/audio_loop.h" +#include "webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h" +#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" +#include "webrtc/modules/audio_coding/neteq/tools/output_audio_file.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet.h" +#include "webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h" +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/compile_assert.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/event_wrapper.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/system_wrappers/interface/sleep.h" +#include "webrtc/system_wrappers/interface/thread_wrapper.h" +#include "webrtc/test/testsupport/fileutils.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace webrtc { + +const int kSampleRateHz = 16000; +const int kNumSamples10ms = kSampleRateHz / 100; +const int kFrameSizeMs = 10; // Multiple of 10. +const int kFrameSizeSamples = kFrameSizeMs / 10 * kNumSamples10ms; +const int kPayloadSizeBytes = kFrameSizeSamples * sizeof(int16_t); +const uint8_t kPayloadType = 111; + +class RtpUtility { + public: + RtpUtility(int samples_per_packet, uint8_t payload_type) + : samples_per_packet_(samples_per_packet), payload_type_(payload_type) {} + + virtual ~RtpUtility() {} + + void Populate(WebRtcRTPHeader* rtp_header) { + rtp_header->header.sequenceNumber = 0xABCD; + rtp_header->header.timestamp = 0xABCDEF01; + rtp_header->header.payloadType = payload_type_; + rtp_header->header.markerBit = false; + rtp_header->header.ssrc = 0x1234; + rtp_header->header.numCSRCs = 0; + rtp_header->frameType = kAudioFrameSpeech; + + rtp_header->header.payload_type_frequency = kSampleRateHz; + rtp_header->type.Audio.channel = 1; + rtp_header->type.Audio.isCNG = false; + } + + void Forward(WebRtcRTPHeader* rtp_header) { + ++rtp_header->header.sequenceNumber; + rtp_header->header.timestamp += samples_per_packet_; + } + + private: + int samples_per_packet_; + uint8_t payload_type_; +}; + +class PacketizationCallbackStub : public AudioPacketizationCallback { + public: + PacketizationCallbackStub() + : num_calls_(0), + crit_sect_(CriticalSectionWrapper::CreateCriticalSection()) {} + + virtual int32_t SendData( + FrameType frame_type, + uint8_t payload_type, + uint32_t timestamp, + const uint8_t* payload_data, + uint16_t payload_len_bytes, + const RTPFragmentationHeader* fragmentation) OVERRIDE { + CriticalSectionScoped lock(crit_sect_.get()); + ++num_calls_; + last_payload_vec_.assign(payload_data, payload_data + payload_len_bytes); + return 0; + } + + int num_calls() const { + CriticalSectionScoped lock(crit_sect_.get()); + return num_calls_; + } + + int last_payload_len_bytes() const { + CriticalSectionScoped lock(crit_sect_.get()); + return last_payload_vec_.size(); + } + + void SwapBuffers(std::vector* payload) { + CriticalSectionScoped lock(crit_sect_.get()); + last_payload_vec_.swap(*payload); + } + + private: + int num_calls_ GUARDED_BY(crit_sect_); + std::vector last_payload_vec_ GUARDED_BY(crit_sect_); + const scoped_ptr crit_sect_; +}; + +class AudioCodingModuleTestOldApi : public ::testing::Test { + protected: + AudioCodingModuleTestOldApi() + : id_(1), + rtp_utility_(new RtpUtility(kFrameSizeSamples, kPayloadType)), + clock_(Clock::GetRealTimeClock()) {} + + ~AudioCodingModuleTestOldApi() {} + + void TearDown() {} + + void SetUp() { + acm_.reset(AudioCodingModule::Create(id_, clock_)); + + RegisterCodec(); + + rtp_utility_->Populate(&rtp_header_); + + input_frame_.sample_rate_hz_ = kSampleRateHz; + input_frame_.num_channels_ = 1; + input_frame_.samples_per_channel_ = kSampleRateHz * 10 / 1000; // 10 ms. + COMPILE_ASSERT(kSampleRateHz * 10 / 1000 <= AudioFrame::kMaxDataSizeSamples, + audio_frame_too_small); + memset(input_frame_.data_, + 0, + input_frame_.samples_per_channel_ * sizeof(input_frame_.data_[0])); + + ASSERT_EQ(0, acm_->RegisterTransportCallback(&packet_cb_)); + } + + virtual void RegisterCodec() { + AudioCodingModule::Codec("L16", &codec_, kSampleRateHz, 1); + codec_.pltype = kPayloadType; + + // Register L16 codec in ACM. + ASSERT_EQ(0, acm_->RegisterReceiveCodec(codec_)); + ASSERT_EQ(0, acm_->RegisterSendCodec(codec_)); + } + + virtual void InsertPacketAndPullAudio() { + InsertPacket(); + PullAudio(); + } + + virtual void InsertPacket() { + const uint8_t kPayload[kPayloadSizeBytes] = {0}; + ASSERT_EQ(0, + acm_->IncomingPacket(kPayload, kPayloadSizeBytes, rtp_header_)); + rtp_utility_->Forward(&rtp_header_); + } + + virtual void PullAudio() { + AudioFrame audio_frame; + ASSERT_EQ(0, acm_->PlayoutData10Ms(-1, &audio_frame)); + } + + virtual void InsertAudio() { + ASSERT_EQ(0, acm_->Add10MsData(input_frame_)); + input_frame_.timestamp_ += kNumSamples10ms; + } + + virtual void Encode() { + int32_t encoded_bytes = acm_->Process(); + // Expect to get one packet with two bytes per sample, or no packet at all, + // depending on how many 10 ms blocks go into |codec_.pacsize|. + EXPECT_TRUE(encoded_bytes == 2 * codec_.pacsize || encoded_bytes == 0); + } + + const int id_; + scoped_ptr rtp_utility_; + scoped_ptr acm_; + PacketizationCallbackStub packet_cb_; + WebRtcRTPHeader rtp_header_; + AudioFrame input_frame_; + CodecInst codec_; + Clock* clock_; +}; + +// Check if the statistics are initialized correctly. Before any call to ACM +// all fields have to be zero. +TEST_F(AudioCodingModuleTestOldApi, DISABLED_ON_ANDROID(InitializedToZero)) { + AudioDecodingCallStats stats; + acm_->GetDecodingCallStatistics(&stats); + EXPECT_EQ(0, stats.calls_to_neteq); + EXPECT_EQ(0, stats.calls_to_silence_generator); + EXPECT_EQ(0, stats.decoded_normal); + EXPECT_EQ(0, stats.decoded_cng); + EXPECT_EQ(0, stats.decoded_plc); + EXPECT_EQ(0, stats.decoded_plc_cng); +} + +// Apply an initial playout delay. Calls to AudioCodingModule::PlayoutData10ms() +// should result in generating silence, check the associated field. +TEST_F(AudioCodingModuleTestOldApi, + DISABLED_ON_ANDROID(SilenceGeneratorCalled)) { + AudioDecodingCallStats stats; + const int kInitialDelay = 100; + + acm_->SetInitialPlayoutDelay(kInitialDelay); + + int num_calls = 0; + for (int time_ms = 0; time_ms < kInitialDelay; + time_ms += kFrameSizeMs, ++num_calls) { + InsertPacketAndPullAudio(); + } + acm_->GetDecodingCallStatistics(&stats); + EXPECT_EQ(0, stats.calls_to_neteq); + EXPECT_EQ(num_calls, stats.calls_to_silence_generator); + EXPECT_EQ(0, stats.decoded_normal); + EXPECT_EQ(0, stats.decoded_cng); + EXPECT_EQ(0, stats.decoded_plc); + EXPECT_EQ(0, stats.decoded_plc_cng); +} + +// Insert some packets and pull audio. Check statistics are valid. Then, +// simulate packet loss and check if PLC and PLC-to-CNG statistics are +// correctly updated. +TEST_F(AudioCodingModuleTestOldApi, DISABLED_ON_ANDROID(NetEqCalls)) { + AudioDecodingCallStats stats; + const int kNumNormalCalls = 10; + + for (int num_calls = 0; num_calls < kNumNormalCalls; ++num_calls) { + InsertPacketAndPullAudio(); + } + acm_->GetDecodingCallStatistics(&stats); + EXPECT_EQ(kNumNormalCalls, stats.calls_to_neteq); + EXPECT_EQ(0, stats.calls_to_silence_generator); + EXPECT_EQ(kNumNormalCalls, stats.decoded_normal); + EXPECT_EQ(0, stats.decoded_cng); + EXPECT_EQ(0, stats.decoded_plc); + EXPECT_EQ(0, stats.decoded_plc_cng); + + const int kNumPlc = 3; + const int kNumPlcCng = 5; + + // Simulate packet-loss. NetEq first performs PLC then PLC fades to CNG. + for (int n = 0; n < kNumPlc + kNumPlcCng; ++n) { + PullAudio(); + } + acm_->GetDecodingCallStatistics(&stats); + EXPECT_EQ(kNumNormalCalls + kNumPlc + kNumPlcCng, stats.calls_to_neteq); + EXPECT_EQ(0, stats.calls_to_silence_generator); + EXPECT_EQ(kNumNormalCalls, stats.decoded_normal); + EXPECT_EQ(0, stats.decoded_cng); + EXPECT_EQ(kNumPlc, stats.decoded_plc); + EXPECT_EQ(kNumPlcCng, stats.decoded_plc_cng); +} + +TEST_F(AudioCodingModuleTestOldApi, VerifyOutputFrame) { + AudioFrame audio_frame; + const int kSampleRateHz = 32000; + EXPECT_EQ(0, acm_->PlayoutData10Ms(kSampleRateHz, &audio_frame)); + EXPECT_EQ(id_, audio_frame.id_); + EXPECT_EQ(0u, audio_frame.timestamp_); + EXPECT_GT(audio_frame.num_channels_, 0); + EXPECT_EQ(kSampleRateHz / 100, audio_frame.samples_per_channel_); + EXPECT_EQ(kSampleRateHz, audio_frame.sample_rate_hz_); +} + +TEST_F(AudioCodingModuleTestOldApi, FailOnZeroDesiredFrequency) { + AudioFrame audio_frame; + EXPECT_EQ(-1, acm_->PlayoutData10Ms(0, &audio_frame)); +} + +// A multi-threaded test for ACM. This base class is using the PCM16b 16 kHz +// codec, while the derive class AcmIsacMtTest is using iSAC. +class AudioCodingModuleMtTestOldApi : public AudioCodingModuleTestOldApi { + protected: + static const int kNumPackets = 500; + static const int kNumPullCalls = 500; + + AudioCodingModuleMtTestOldApi() + : AudioCodingModuleTestOldApi(), + send_thread_(ThreadWrapper::CreateThread(CbSendThread, + this, + kRealtimePriority, + "send")), + insert_packet_thread_(ThreadWrapper::CreateThread(CbInsertPacketThread, + this, + kRealtimePriority, + "insert_packet")), + pull_audio_thread_(ThreadWrapper::CreateThread(CbPullAudioThread, + this, + kRealtimePriority, + "pull_audio")), + test_complete_(EventWrapper::Create()), + send_count_(0), + insert_packet_count_(0), + pull_audio_count_(0), + crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + next_insert_packet_time_ms_(0), + fake_clock_(new SimulatedClock(0)) { + clock_ = fake_clock_.get(); + } + + void SetUp() { + AudioCodingModuleTestOldApi::SetUp(); + StartThreads(); + } + + void StartThreads() { + unsigned int thread_id = 0; + ASSERT_TRUE(send_thread_->Start(thread_id)); + ASSERT_TRUE(insert_packet_thread_->Start(thread_id)); + ASSERT_TRUE(pull_audio_thread_->Start(thread_id)); + } + + void TearDown() { + AudioCodingModuleTestOldApi::TearDown(); + pull_audio_thread_->Stop(); + send_thread_->Stop(); + insert_packet_thread_->Stop(); + } + + EventTypeWrapper RunTest() { + return test_complete_->Wait(10 * 60 * 1000); // 10 minutes' timeout. + } + + virtual bool TestDone() { + if (packet_cb_.num_calls() > kNumPackets) { + CriticalSectionScoped lock(crit_sect_.get()); + if (pull_audio_count_ > kNumPullCalls) { + // Both conditions for completion are met. End the test. + return true; + } + } + return false; + } + + static bool CbSendThread(void* context) { + return reinterpret_cast(context) + ->CbSendImpl(); + } + + // The send thread doesn't have to care about the current simulated time, + // since only the AcmReceiver is using the clock. + bool CbSendImpl() { + SleepMs(1); + if (HasFatalFailure()) { + // End the test early if a fatal failure (ASSERT_*) has occurred. + test_complete_->Set(); + } + ++send_count_; + InsertAudio(); + Encode(); + if (TestDone()) { + test_complete_->Set(); + } + return true; + } + + static bool CbInsertPacketThread(void* context) { + return reinterpret_cast(context) + ->CbInsertPacketImpl(); + } + + bool CbInsertPacketImpl() { + SleepMs(1); + { + CriticalSectionScoped lock(crit_sect_.get()); + if (clock_->TimeInMilliseconds() < next_insert_packet_time_ms_) { + return true; + } + next_insert_packet_time_ms_ += 10; + } + // Now we're not holding the crit sect when calling ACM. + ++insert_packet_count_; + InsertPacket(); + return true; + } + + static bool CbPullAudioThread(void* context) { + return reinterpret_cast(context) + ->CbPullAudioImpl(); + } + + bool CbPullAudioImpl() { + SleepMs(1); + { + CriticalSectionScoped lock(crit_sect_.get()); + // Don't let the insert thread fall behind. + if (next_insert_packet_time_ms_ < clock_->TimeInMilliseconds()) { + return true; + } + ++pull_audio_count_; + } + // Now we're not holding the crit sect when calling ACM. + PullAudio(); + fake_clock_->AdvanceTimeMilliseconds(10); + return true; + } + + scoped_ptr send_thread_; + scoped_ptr insert_packet_thread_; + scoped_ptr pull_audio_thread_; + const scoped_ptr test_complete_; + int send_count_; + int insert_packet_count_; + int pull_audio_count_ GUARDED_BY(crit_sect_); + const scoped_ptr crit_sect_; + int64_t next_insert_packet_time_ms_ GUARDED_BY(crit_sect_); + scoped_ptr fake_clock_; +}; + +TEST_F(AudioCodingModuleMtTestOldApi, DoTest) { + EXPECT_EQ(kEventSignaled, RunTest()); +} + +// This is a multi-threaded ACM test using iSAC. The test encodes audio +// from a PCM file. The most recent encoded frame is used as input to the +// receiving part. Depending on timing, it may happen that the same RTP packet +// is inserted into the receiver multiple times, but this is a valid use-case, +// and simplifies the test code a lot. +class AcmIsacMtTestOldApi : public AudioCodingModuleMtTestOldApi { + protected: + static const int kNumPackets = 500; + static const int kNumPullCalls = 500; + + AcmIsacMtTestOldApi() + : AudioCodingModuleMtTestOldApi(), last_packet_number_(0) {} + + ~AcmIsacMtTestOldApi() {} + + void SetUp() { + AudioCodingModuleTestOldApi::SetUp(); + + // Set up input audio source to read from specified file, loop after 5 + // seconds, and deliver blocks of 10 ms. + const std::string input_file_name = + webrtc::test::ResourcePath("audio_coding/speech_mono_16kHz", "pcm"); + audio_loop_.Init(input_file_name, 5 * kSampleRateHz, kNumSamples10ms); + + // Generate one packet to have something to insert. + int loop_counter = 0; + while (packet_cb_.last_payload_len_bytes() == 0) { + InsertAudio(); + Encode(); + ASSERT_LT(loop_counter++, 10); + } + // Set |last_packet_number_| to one less that |num_calls| so that the packet + // will be fetched in the next InsertPacket() call. + last_packet_number_ = packet_cb_.num_calls() - 1; + + StartThreads(); + } + + virtual void RegisterCodec() { + COMPILE_ASSERT(kSampleRateHz == 16000, test_designed_for_isac_16khz); + AudioCodingModule::Codec("ISAC", &codec_, kSampleRateHz, 1); + codec_.pltype = kPayloadType; + + // Register iSAC codec in ACM, effectively unregistering the PCM16B codec + // registered in AudioCodingModuleTestOldApi::SetUp(); + ASSERT_EQ(0, acm_->RegisterReceiveCodec(codec_)); + ASSERT_EQ(0, acm_->RegisterSendCodec(codec_)); + } + + void InsertPacket() { + int num_calls = packet_cb_.num_calls(); // Store locally for thread safety. + if (num_calls > last_packet_number_) { + // Get the new payload out from the callback handler. + // Note that since we swap buffers here instead of directly inserting + // a pointer to the data in |packet_cb_|, we avoid locking the callback + // for the duration of the IncomingPacket() call. + packet_cb_.SwapBuffers(&last_payload_vec_); + ASSERT_GT(last_payload_vec_.size(), 0u); + rtp_utility_->Forward(&rtp_header_); + last_packet_number_ = num_calls; + } + ASSERT_GT(last_payload_vec_.size(), 0u); + ASSERT_EQ( + 0, + acm_->IncomingPacket( + &last_payload_vec_[0], last_payload_vec_.size(), rtp_header_)); + } + + void InsertAudio() { + memcpy(input_frame_.data_, audio_loop_.GetNextBlock(), kNumSamples10ms); + AudioCodingModuleTestOldApi::InsertAudio(); + } + + void Encode() { ASSERT_GE(acm_->Process(), 0); } + + // This method is the same as AudioCodingModuleMtTestOldApi::TestDone(), but + // here it is using the constants defined in this class (i.e., shorter test + // run). + virtual bool TestDone() { + if (packet_cb_.num_calls() > kNumPackets) { + CriticalSectionScoped lock(crit_sect_.get()); + if (pull_audio_count_ > kNumPullCalls) { + // Both conditions for completion are met. End the test. + return true; + } + } + return false; + } + + int last_packet_number_; + std::vector last_payload_vec_; + test::AudioLoop audio_loop_; +}; + +TEST_F(AcmIsacMtTestOldApi, DoTest) { + EXPECT_EQ(kEventSignaled, RunTest()); +} + +class AcmReceiverBitExactnessOldApi : public ::testing::Test { + public: + static std::string PlatformChecksum(std::string win64, + std::string android, + std::string others) { +#if defined(_WIN32) && defined(WEBRTC_ARCH_64_BITS) + return win64; +#elif defined(WEBRTC_ANDROID) + return android; +#else + return others; +#endif + } + + protected: + void Run(int output_freq_hz, const std::string& checksum_ref) { + const std::string input_file_name = + webrtc::test::ResourcePath("audio_coding/neteq_universal_new", "rtp"); + scoped_ptr packet_source( + test::RtpFileSource::Create(input_file_name)); +#ifdef WEBRTC_ANDROID + // Filter out iLBC and iSAC-swb since they are not supported on Android. + packet_source->FilterOutPayloadType(102); // iLBC. + packet_source->FilterOutPayloadType(104); // iSAC-swb. +#endif + + test::AudioChecksum checksum; + const std::string output_file_name = + webrtc::test::OutputPath() + + ::testing::UnitTest::GetInstance() + ->current_test_info() + ->test_case_name() + + "_" + ::testing::UnitTest::GetInstance()->current_test_info()->name() + + "_output.pcm"; + test::OutputAudioFile output_file(output_file_name); + test::AudioSinkFork output(&checksum, &output_file); + + test::AcmReceiveTestOldApi test( + packet_source.get(), + &output, + output_freq_hz, + test::AcmReceiveTestOldApi::kArbitraryChannels); + ASSERT_NO_FATAL_FAILURE(test.RegisterNetEqTestCodecs()); + test.Run(); + + std::string checksum_string = checksum.Finish(); + EXPECT_EQ(checksum_ref, checksum_string); + } +}; + +TEST_F(AcmReceiverBitExactnessOldApi, 8kHzOutput) { + Run(8000, + PlatformChecksum("bd6f8d9602cd82444ea2539e674df747", + "6ac89c7145072c26bfeba602cd661afb", + "8a8440f5511eb729221b9aac25cda3a0")); +} + +TEST_F(AcmReceiverBitExactnessOldApi, 16kHzOutput) { + Run(16000, + PlatformChecksum("a39bc6ee0c4eb15f3ad2f43cebcc571d", + "3e888eb04f57db2c6ef952fe64f17fe6", + "7be583092c5adbcb0f6cd66eca20ea63")); +} + +TEST_F(AcmReceiverBitExactnessOldApi, 32kHzOutput) { + Run(32000, + PlatformChecksum("80964572aaa2dc92f9e34896dd3802b3", + "aeca37e963310f5b6552b7edea23c2f1", + "3a84188abe9fca25fedd6034760f3e22")); +} + +TEST_F(AcmReceiverBitExactnessOldApi, 48kHzOutput) { + Run(48000, + PlatformChecksum("8aacde91f390e0d5a9c2ed571a25fd37", + "76b9e99e0a3998aa28355e7a2bd836f7", + "89b4b19bdb4de40f1d88302ef8cb9f9b")); +} + +// This test verifies bit exactness for the send-side of ACM. The test setup is +// a chain of three different test classes: +// +// test::AcmSendTest -> AcmSenderBitExactness -> test::AcmReceiveTest +// +// The receiver side is driving the test by requesting new packets from +// AcmSenderBitExactness::NextPacket(). This method, in turn, asks for the +// packet from test::AcmSendTest::NextPacket, which inserts audio from the +// input file until one packet is produced. (The input file loops indefinitely.) +// Before passing the packet to the receiver, this test class verifies the +// packet header and updates a payload checksum with the new payload. The +// decoded output from the receiver is also verified with a (separate) checksum. +class AcmSenderBitExactnessOldApi : public ::testing::Test, + public test::PacketSource { + protected: + static const int kTestDurationMs = 1000; + + AcmSenderBitExactnessOldApi() + : frame_size_rtp_timestamps_(0), + packet_count_(0), + payload_type_(0), + last_sequence_number_(0), + last_timestamp_(0) {} + + // Sets up the test::AcmSendTest object. Returns true on success, otherwise + // false. + bool SetUpSender() { + const std::string input_file_name = + webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); + // Note that |audio_source_| will loop forever. The test duration is set + // explicitly by |kTestDurationMs|. + audio_source_.reset(new test::InputAudioFile(input_file_name)); + static const int kSourceRateHz = 32000; + send_test_.reset(new test::AcmSendTestOldApi( + audio_source_.get(), kSourceRateHz, kTestDurationMs)); + return send_test_.get() != NULL; + } + + // Registers a send codec in the test::AcmSendTest object. Returns true on + // success, false on failure. + bool RegisterSendCodec(const char* payload_name, + int sampling_freq_hz, + int channels, + int payload_type, + int frame_size_samples, + int frame_size_rtp_timestamps) { + payload_type_ = payload_type; + frame_size_rtp_timestamps_ = frame_size_rtp_timestamps; + return send_test_->RegisterCodec(payload_name, + sampling_freq_hz, + channels, + payload_type, + frame_size_samples); + } + + // Runs the test. SetUpSender() and RegisterSendCodec() must have been called + // before calling this method. + void Run(const std::string& audio_checksum_ref, + const std::string& payload_checksum_ref, + int expected_packets, + test::AcmReceiveTestOldApi::NumOutputChannels expected_channels) { + // Set up the receiver used to decode the packets and verify the decoded + // output. + test::AudioChecksum audio_checksum; + const std::string output_file_name = + webrtc::test::OutputPath() + + ::testing::UnitTest::GetInstance() + ->current_test_info() + ->test_case_name() + + "_" + ::testing::UnitTest::GetInstance()->current_test_info()->name() + + "_output.pcm"; + test::OutputAudioFile output_file(output_file_name); + // Have the output audio sent both to file and to the checksum calculator. + test::AudioSinkFork output(&audio_checksum, &output_file); + const int kOutputFreqHz = 8000; + test::AcmReceiveTestOldApi receive_test( + this, &output, kOutputFreqHz, expected_channels); + ASSERT_NO_FATAL_FAILURE(receive_test.RegisterDefaultCodecs()); + + // This is where the actual test is executed. + receive_test.Run(); + + // Extract and verify the audio checksum. + std::string checksum_string = audio_checksum.Finish(); + EXPECT_EQ(audio_checksum_ref, checksum_string); + + // Extract and verify the payload checksum. + char checksum_result[rtc::Md5Digest::kSize]; + payload_checksum_.Finish(checksum_result, rtc::Md5Digest::kSize); + checksum_string = rtc::hex_encode(checksum_result, rtc::Md5Digest::kSize); + EXPECT_EQ(payload_checksum_ref, checksum_string); + + // Verify number of packets produced. + EXPECT_EQ(expected_packets, packet_count_); + } + + // Returns a pointer to the next packet. Returns NULL if the source is + // depleted (i.e., the test duration is exceeded), or if an error occurred. + // Inherited from test::PacketSource. + test::Packet* NextPacket() OVERRIDE { + // Get the next packet from AcmSendTest. Ownership of |packet| is + // transferred to this method. + test::Packet* packet = send_test_->NextPacket(); + if (!packet) + return NULL; + + VerifyPacket(packet); + // TODO(henrik.lundin) Save the packet to file as well. + + // Pass it on to the caller. The caller becomes the owner of |packet|. + return packet; + } + + // Verifies the packet. + void VerifyPacket(const test::Packet* packet) { + EXPECT_TRUE(packet->valid_header()); + // (We can check the header fields even if valid_header() is false.) + EXPECT_EQ(payload_type_, packet->header().payloadType); + if (packet_count_ > 0) { + // This is not the first packet. + uint16_t sequence_number_diff = + packet->header().sequenceNumber - last_sequence_number_; + EXPECT_EQ(1, sequence_number_diff); + uint32_t timestamp_diff = packet->header().timestamp - last_timestamp_; + EXPECT_EQ(frame_size_rtp_timestamps_, timestamp_diff); + } + ++packet_count_; + last_sequence_number_ = packet->header().sequenceNumber; + last_timestamp_ = packet->header().timestamp; + // Update the checksum. + payload_checksum_.Update(packet->payload(), packet->payload_length_bytes()); + } + + void SetUpTest(const char* codec_name, + int codec_sample_rate_hz, + int channels, + int payload_type, + int codec_frame_size_samples, + int codec_frame_size_rtp_timestamps) { + ASSERT_TRUE(SetUpSender()); + ASSERT_TRUE(RegisterSendCodec(codec_name, + codec_sample_rate_hz, + channels, + payload_type, + codec_frame_size_samples, + codec_frame_size_rtp_timestamps)); + } + + scoped_ptr send_test_; + scoped_ptr audio_source_; + uint32_t frame_size_rtp_timestamps_; + int packet_count_; + uint8_t payload_type_; + uint16_t last_sequence_number_; + uint32_t last_timestamp_; + rtc::Md5Digest payload_checksum_; +}; + +TEST_F(AcmSenderBitExactnessOldApi, IsacWb30ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("ISAC", 16000, 1, 103, 480, 480)); + Run(AcmReceiverBitExactnessOldApi::PlatformChecksum( + "c7e5bdadfa2871df95639fcc297cf23d", + "0499ca260390769b3172136faad925b9", + "0b58f9eeee43d5891f5f6c75e77984a3"), + AcmReceiverBitExactnessOldApi::PlatformChecksum( + "d42cb5195463da26c8129bbfe73a22e6", + "83de248aea9c3c2bd680b6952401b4ca", + "3c79f16f34218271f3dca4e2b1dfe1bb"), + 33, + test::AcmReceiveTestOldApi::kMonoOutput); +} + +TEST_F(AcmSenderBitExactnessOldApi, IsacWb60ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("ISAC", 16000, 1, 103, 960, 960)); + Run(AcmReceiverBitExactnessOldApi::PlatformChecksum( + "14d63c5f08127d280e722e3191b73bdd", + "8da003e16c5371af2dc2be79a50f9076", + "1ad29139a04782a33daad8c2b9b35875"), + AcmReceiverBitExactnessOldApi::PlatformChecksum( + "ebe04a819d3a9d83a83a17f271e1139a", + "97aeef98553b5a4b5a68f8b716e8eaf0", + "9e0a0ab743ad987b55b8e14802769c56"), + 16, + test::AcmReceiveTestOldApi::kMonoOutput); +} + +TEST_F(AcmSenderBitExactnessOldApi, DISABLED_ON_ANDROID(IsacSwb30ms)) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("ISAC", 32000, 1, 104, 960, 960)); + Run(AcmReceiverBitExactnessOldApi::PlatformChecksum( + "98d960600eb4ddb3fcbe11f5057ddfd7", + "", + "2f6dfe142f735f1d96f6bd86d2526f42"), + AcmReceiverBitExactnessOldApi::PlatformChecksum( + "cc9d2d86a71d6f99f97680a5c27e2762", + "", + "7b214fc3a5e33d68bf30e77969371f31"), + 33, + test::AcmReceiveTestOldApi::kMonoOutput); +} + +TEST_F(AcmSenderBitExactnessOldApi, Pcm16_8000khz_10ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 8000, 1, 107, 80, 80)); + Run("de4a98e1406f8b798d99cd0704e862e2", + "c1edd36339ce0326cc4550041ad719a0", + 100, + test::AcmReceiveTestOldApi::kMonoOutput); +} + +TEST_F(AcmSenderBitExactnessOldApi, Pcm16_16000khz_10ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 16000, 1, 108, 160, 160)); + Run("ae646d7b68384a1269cc080dd4501916", + "ad786526383178b08d80d6eee06e9bad", + 100, + test::AcmReceiveTestOldApi::kMonoOutput); +} + +TEST_F(AcmSenderBitExactnessOldApi, Pcm16_32000khz_10ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 32000, 1, 109, 320, 320)); + Run("7fe325e8fbaf755e3c5df0b11a4774fb", + "5ef82ea885e922263606c6fdbc49f651", + 100, + test::AcmReceiveTestOldApi::kMonoOutput); +} + +TEST_F(AcmSenderBitExactnessOldApi, Pcm16_stereo_8000khz_10ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 8000, 2, 111, 80, 80)); + Run("fb263b74e7ac3de915474d77e4744ceb", + "62ce5adb0d4965d0a52ec98ae7f98974", + 100, + test::AcmReceiveTestOldApi::kStereoOutput); +} + +TEST_F(AcmSenderBitExactnessOldApi, Pcm16_stereo_16000khz_10ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 16000, 2, 112, 160, 160)); + Run("d09e9239553649d7ac93e19d304281fd", + "41ca8edac4b8c71cd54fd9f25ec14870", + 100, + test::AcmReceiveTestOldApi::kStereoOutput); +} + +TEST_F(AcmSenderBitExactnessOldApi, Pcm16_stereo_32000khz_10ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 32000, 2, 113, 320, 320)); + Run("5f025d4f390982cc26b3d92fe02e3044", + "50e58502fb04421bf5b857dda4c96879", + 100, + test::AcmReceiveTestOldApi::kStereoOutput); +} + +TEST_F(AcmSenderBitExactnessOldApi, Pcmu_20ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("PCMU", 8000, 1, 0, 160, 160)); + Run("81a9d4c0bb72e9becc43aef124c981e9", + "8f9b8750bd80fe26b6cbf6659b89f0f9", + 50, + test::AcmReceiveTestOldApi::kMonoOutput); +} + +TEST_F(AcmSenderBitExactnessOldApi, Pcma_20ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("PCMA", 8000, 1, 8, 160, 160)); + Run("39611f798969053925a49dc06d08de29", + "6ad745e55aa48981bfc790d0eeef2dd1", + 50, + test::AcmReceiveTestOldApi::kMonoOutput); +} + +TEST_F(AcmSenderBitExactnessOldApi, Pcmu_stereo_20ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("PCMU", 8000, 2, 110, 160, 160)); + Run("437bec032fdc5cbaa0d5175430af7b18", + "60b6f25e8d1e74cb679cfe756dd9bca5", + 50, + test::AcmReceiveTestOldApi::kStereoOutput); +} + +TEST_F(AcmSenderBitExactnessOldApi, Pcma_stereo_20ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("PCMA", 8000, 2, 118, 160, 160)); + Run("a5c6d83c5b7cedbeff734238220a4b0c", + "92b282c83efd20e7eeef52ba40842cf7", + 50, + test::AcmReceiveTestOldApi::kStereoOutput); +} + +TEST_F(AcmSenderBitExactnessOldApi, DISABLED_ON_ANDROID(Ilbc_30ms)) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("ILBC", 8000, 1, 102, 240, 240)); + Run(AcmReceiverBitExactnessOldApi::PlatformChecksum( + "7b6ec10910debd9af08011d3ed5249f7", + "android_audio", + "7b6ec10910debd9af08011d3ed5249f7"), + AcmReceiverBitExactnessOldApi::PlatformChecksum( + "cfae2e9f6aba96e145f2bcdd5050ce78", + "android_payload", + "cfae2e9f6aba96e145f2bcdd5050ce78"), + 33, + test::AcmReceiveTestOldApi::kMonoOutput); +} + +TEST_F(AcmSenderBitExactnessOldApi, DISABLED_ON_ANDROID(G722_20ms)) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("G722", 16000, 1, 9, 320, 160)); + Run(AcmReceiverBitExactnessOldApi::PlatformChecksum( + "7d759436f2533582950d148b5161a36c", + "android_audio", + "7d759436f2533582950d148b5161a36c"), + AcmReceiverBitExactnessOldApi::PlatformChecksum( + "fc68a87e1380614e658087cb35d5ca10", + "android_payload", + "fc68a87e1380614e658087cb35d5ca10"), + 50, + test::AcmReceiveTestOldApi::kMonoOutput); +} + +TEST_F(AcmSenderBitExactnessOldApi, DISABLED_ON_ANDROID(G722_stereo_20ms)) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("G722", 16000, 2, 119, 320, 160)); + Run(AcmReceiverBitExactnessOldApi::PlatformChecksum( + "7190ee718ab3d80eca181e5f7140c210", + "android_audio", + "7190ee718ab3d80eca181e5f7140c210"), + AcmReceiverBitExactnessOldApi::PlatformChecksum( + "66516152eeaa1e650ad94ff85f668dac", + "android_payload", + "66516152eeaa1e650ad94ff85f668dac"), + 50, + test::AcmReceiveTestOldApi::kStereoOutput); +} + +TEST_F(AcmSenderBitExactnessOldApi, Opus_stereo_20ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("opus", 48000, 2, 120, 960, 960)); + Run(AcmReceiverBitExactnessOldApi::PlatformChecksum( + "855041f2490b887302bce9d544731849", + "1e1a0fce893fef2d66886a7f09e2ebce", + "855041f2490b887302bce9d544731849"), + AcmReceiverBitExactnessOldApi::PlatformChecksum( + "d781cce1ab986b618d0da87226cdde30", + "1a1fe04dd12e755949987c8d729fb3e0", + "d781cce1ab986b618d0da87226cdde30"), + 50, + test::AcmReceiveTestOldApi::kStereoOutput); +} + +// This test fixture is implemented to run ACM and change the desired output +// frequency during the call. The input packets are simply PCM16b-wb encoded +// payloads with a constant value of |kSampleValue|. The test fixture itself +// acts as PacketSource in between the receive test class and the constant- +// payload packet source class. The output is both written to file, and analyzed +// in this test fixture. +class AcmSwitchingOutputFrequencyOldApi : public ::testing::Test, + public test::PacketSource, + public test::AudioSink { + protected: + static const size_t kTestNumPackets = 50; + static const int kEncodedSampleRateHz = 16000; + static const size_t kPayloadLenSamples = 30 * kEncodedSampleRateHz / 1000; + static const int kPayloadType = 108; // Default payload type for PCM16b-wb. + + AcmSwitchingOutputFrequencyOldApi() + : first_output_(true), + num_packets_(0), + packet_source_(kPayloadLenSamples, + kSampleValue, + kEncodedSampleRateHz, + kPayloadType), + output_freq_2_(0), + has_toggled_(false) {} + + void Run(int output_freq_1, int output_freq_2, int toggle_period_ms) { + // Set up the receiver used to decode the packets and verify the decoded + // output. + const std::string output_file_name = + webrtc::test::OutputPath() + + ::testing::UnitTest::GetInstance() + ->current_test_info() + ->test_case_name() + + "_" + ::testing::UnitTest::GetInstance()->current_test_info()->name() + + "_output.pcm"; + test::OutputAudioFile output_file(output_file_name); + // Have the output audio sent both to file and to the WriteArray method in + // this class. + test::AudioSinkFork output(this, &output_file); + test::AcmReceiveTestToggleOutputFreqOldApi receive_test( + this, + &output, + output_freq_1, + output_freq_2, + toggle_period_ms, + test::AcmReceiveTestOldApi::kMonoOutput); + ASSERT_NO_FATAL_FAILURE(receive_test.RegisterDefaultCodecs()); + output_freq_2_ = output_freq_2; + + // This is where the actual test is executed. + receive_test.Run(); + } + + // Inherited from test::PacketSource. + test::Packet* NextPacket() OVERRIDE { + // Check if it is time to terminate the test. The packet source is of type + // ConstantPcmPacketSource, which is infinite, so we must end the test + // "manually". + if (num_packets_++ > kTestNumPackets) { + EXPECT_TRUE(has_toggled_); + return NULL; // Test ended. + } + + // Get the next packet from the source. + return packet_source_.NextPacket(); + } + + // Inherited from test::AudioSink. + bool WriteArray(const int16_t* audio, size_t num_samples) { + // Skip checking the first output frame, since it has a number of zeros + // due to how NetEq is initialized. + if (first_output_) { + first_output_ = false; + return true; + } + for (size_t i = 0; i < num_samples; ++i) { + EXPECT_EQ(kSampleValue, audio[i]); + } + if (num_samples == + static_cast(output_freq_2_ / 100)) // Size of 10 ms frame. + has_toggled_ = true; + // The return value does not say if the values match the expectation, just + // that the method could process the samples. + return true; + } + + const int16_t kSampleValue = 1000; + bool first_output_; + size_t num_packets_; + test::ConstantPcmPacketSource packet_source_; + int output_freq_2_; + bool has_toggled_; +}; + +TEST_F(AcmSwitchingOutputFrequencyOldApi, TestWithoutToggling) { + Run(16000, 16000, 1000); +} + +TEST_F(AcmSwitchingOutputFrequencyOldApi, Toggle16KhzTo32Khz) { + Run(16000, 32000, 1000); +} + +TEST_F(AcmSwitchingOutputFrequencyOldApi, Toggle32KhzTo16Khz) { + Run(32000, 16000, 1000); +} + +TEST_F(AcmSwitchingOutputFrequencyOldApi, Toggle16KhzTo8Khz) { + Run(16000, 8000, 1000); +} + +TEST_F(AcmSwitchingOutputFrequencyOldApi, Toggle8KhzTo16Khz) { + Run(8000, 16000, 1000); +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/call_statistics.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/call_statistics.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/call_statistics.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/call_statistics.cc 2015-02-03 14:33:34.000000000 +0000 @@ -10,7 +10,7 @@ #include "webrtc/modules/audio_coding/main/acm2/call_statistics.h" -#include +#include namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/call_statistics_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/call_statistics_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/call_statistics_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/call_statistics_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/audio_coding/main/acm2/call_statistics.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/initial_delay_manager.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/initial_delay_manager.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/initial_delay_manager.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/initial_delay_manager.cc 2015-02-03 14:33:34.000000000 +0000 @@ -219,6 +219,14 @@ return; } +bool InitialDelayManager::GetPlayoutTimestamp(uint32_t* playout_timestamp) { + if (!buffering_) { + return false; + } + *playout_timestamp = playout_timestamp_; + return true; +} + void InitialDelayManager::DisableBuffering() { buffering_ = false; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/initial_delay_manager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/initial_delay_manager.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/initial_delay_manager.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/initial_delay_manager.h 2015-02-03 14:33:34.000000000 +0000 @@ -65,8 +65,9 @@ // sequence of late (or perhaps missing) packets is computed. void LatePackets(uint32_t timestamp_now, SyncStream* sync_stream); - // Playout timestamp, valid when buffering. - uint32_t playout_timestamp() { return playout_timestamp_; } + // Get playout timestamp. + // Returns true if the timestamp is valid (when buffering), otherwise false. + bool GetPlayoutTimestamp(uint32_t* playout_timestamp); // True if buffered audio is less than the given initial delay (specified at // the constructor). Buffering might be disabled by the client of this class. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/initial_delay_manager_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/initial_delay_manager_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/initial_delay_manager_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/initial_delay_manager_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -8,9 +8,9 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include +#include -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/audio_coding/main/acm2/initial_delay_manager.h" namespace webrtc { @@ -359,7 +359,9 @@ EXPECT_TRUE(manager_->buffering()); const uint32_t expected_playout_timestamp = rtp_info_.header.timestamp - kInitDelayMs * kSamplingRateHz / 1000; - EXPECT_EQ(expected_playout_timestamp, manager_->playout_timestamp()); + uint32_t actual_playout_timestamp = 0; + EXPECT_TRUE(manager_->GetPlayoutTimestamp(&actual_playout_timestamp)); + EXPECT_EQ(expected_playout_timestamp, actual_playout_timestamp); NextRtpHeader(&rtp_info_, &rtp_receive_timestamp_); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/nack_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/nack_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/nack_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/nack_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -14,7 +14,7 @@ #include -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/typedefs.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" @@ -398,7 +398,7 @@ // Packet lost more than NACK-list size limit. uint16_t num_lost_packets = kNackThreshold + kNackListSize + 5; - scoped_array seq_num_lost(new uint16_t[num_lost_packets]); + scoped_ptr seq_num_lost(new uint16_t[num_lost_packets]); for (int n = 0; n < num_lost_packets; ++n) { seq_num_lost[n] = ++seq_num; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/acm2/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/interface/audio_coding_module.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/interface/audio_coding_module.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/interface/audio_coding_module.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/interface/audio_coding_module.h 2015-02-03 14:33:34.000000000 +0000 @@ -14,8 +14,11 @@ #include #include "webrtc/common_types.h" +#include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" #include "webrtc/modules/interface/module.h" +#include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/typedefs.h" namespace webrtc { @@ -25,7 +28,6 @@ struct WebRtcRTPHeader; class AudioFrame; class RTPFragmentationHeader; -class Clock; #define WEBRTC_10MS_PCM_AUDIO 960 // 16 bits super wideband 48 kHz @@ -73,15 +75,22 @@ const uint16_t delayMS) = 0; // average delay in ms }; -// Version string for testing, to distinguish instances of ACM1 from ACM2. -extern const char kLegacyAcmVersion[]; -extern const char kExperimentalAcmVersion[]; - class AudioCodingModule: public Module { protected: AudioCodingModule() {} public: + struct Config { + Config() + : id(0), + neteq_config(), + clock(Clock::GetRealTimeClock()) {} + + int id; + NetEq::Config neteq_config; + Clock* clock; + }; + /////////////////////////////////////////////////////////////////////////// // Creation and destruction of a ACM. // @@ -178,11 +187,6 @@ // static bool IsCodecValid(const CodecInst& codec); - // Returns the version of ACM. This facilitates distinguishing instances of - // ACM1 from ACM2 while testing. This API will be removed when ACM1 is - // completely removed. - virtual const char* Version() const = 0; - /////////////////////////////////////////////////////////////////////////// // Sender // @@ -327,6 +331,7 @@ // -1 if error occurred in setting the bandwidth, // 0 bandwidth is set successfully. // + // TODO(henrik.lundin) Unused. Remove? virtual int32_t SetReceivedEstimatedBandwidth( const int32_t bw) = 0; @@ -370,12 +375,12 @@ virtual int32_t Add10MsData(const AudioFrame& audio_frame) = 0; /////////////////////////////////////////////////////////////////////////// - // (FEC) Forward Error Correction + // (RED) Redundant Coding // /////////////////////////////////////////////////////////////////////////// - // int32_t SetFECStatus(const bool enable) - // configure FEC status i.e. on/off. + // int32_t SetREDStatus() + // configure RED status i.e. on/off. // // RFC 2198 describes a solution which has a single payload type which // signifies a packet with redundancy. That packet then becomes a container, @@ -385,27 +390,69 @@ // since each encapsulated payload must be preceded by a header indicating // the type of data enclosed. // - // This means that FEC is actually a RED scheme. + // Input: + // -enable_red : if true RED is enabled, otherwise RED is + // disabled. + // + // Return value: + // -1 if failed to set RED status, + // 0 if succeeded. + // + virtual int32_t SetREDStatus(bool enable_red) = 0; + + /////////////////////////////////////////////////////////////////////////// + // bool REDStatus() + // Get RED status + // + // Return value: + // true if RED is enabled, + // false if RED is disabled. + // + virtual bool REDStatus() const = 0; + + /////////////////////////////////////////////////////////////////////////// + // (FEC) Forward Error Correction (codec internal) + // + + /////////////////////////////////////////////////////////////////////////// + // int32_t SetCodecFEC() + // Configures codec internal FEC status i.e. on/off. No effects on codecs that + // do not provide internal FEC. // // Input: - // -enable_fec : if true FEC is enabled, otherwise FEC is + // -enable_fec : if true FEC will be enabled otherwise the FEC is // disabled. // // Return value: - // -1 if failed to set FEC status, + // -1 if failed, or the codec does not support FEC // 0 if succeeded. // - virtual int32_t SetFECStatus(const bool enable_fec) = 0; + virtual int SetCodecFEC(bool enable_codec_fec) = 0; /////////////////////////////////////////////////////////////////////////// - // bool FECStatus() - // Get FEC status + // bool CodecFEC() + // Gets status of codec internal FEC. // - // Return value + // Return value: // true if FEC is enabled, // false if FEC is disabled. // - virtual bool FECStatus() const = 0; + virtual bool CodecFEC() const = 0; + + /////////////////////////////////////////////////////////////////////////// + // int SetPacketLossRate() + // Sets expected packet loss rate for encoding. Some encoders provide packet + // loss gnostic encoding to make stream less sensitive to packet losses, + // through e.g., FEC. No effects on codecs that do not provide such encoding. + // + // Input: + // -packet_loss_rate : expected packet loss rate (0 -- 100 inclusive). + // + // Return value + // -1 if failed to set packet loss rate, + // 0 if succeeded. + // + virtual int SetPacketLossRate(int packet_loss_rate) = 0; /////////////////////////////////////////////////////////////////////////// // (VAD) Voice Activity Detection @@ -870,6 +917,22 @@ bool enforce_frame_size = false) = 0; /////////////////////////////////////////////////////////////////////////// + // int SetOpusMaxPlaybackRate() + // If current send codec is Opus, informs it about maximum playback rate the + // receiver will render. Opus can use this information to optimize the bit + // rate and increase the computation efficiency. + // + // Input: + // -frequency_hz : maximum playback rate in Hz. + // + // Return value: + // -1 if current send codec is not Opus or + // error occurred in setting the maximum playback rate, + // 0 maximum bandwidth is set successfully. + // + virtual int SetOpusMaxPlaybackRate(int frequency_hz) = 0; + + /////////////////////////////////////////////////////////////////////////// // statistics // @@ -936,18 +999,184 @@ AudioDecodingCallStats* call_stats) const = 0; }; -struct AudioCodingModuleFactory { - AudioCodingModuleFactory() {} - virtual ~AudioCodingModuleFactory() {} +class AudioEncoder; +class ReceiverInfo; - virtual AudioCodingModule* Create(int id) const; -}; +class AudioCoding { + public: + struct Config { + Config() + : neteq_config(), + clock(Clock::GetRealTimeClock()), + transport(NULL), + vad_callback(NULL), + play_dtmf(true), + initial_playout_delay_ms(0), + playout_channels(1), + playout_frequency_hz(32000) {} + + AudioCodingModule::Config ToOldConfig() const { + AudioCodingModule::Config old_config; + old_config.id = 0; + old_config.neteq_config = neteq_config; + old_config.clock = clock; + return old_config; + } + + NetEq::Config neteq_config; + Clock* clock; + AudioPacketizationCallback* transport; + ACMVADCallback* vad_callback; + bool play_dtmf; + int initial_playout_delay_ms; + int playout_channels; + int playout_frequency_hz; + }; -struct NewAudioCodingModuleFactory : AudioCodingModuleFactory { - NewAudioCodingModuleFactory() {} - virtual ~NewAudioCodingModuleFactory() {} + static AudioCoding* Create(const Config& config); + virtual ~AudioCoding() {}; - virtual AudioCodingModule* Create(int id) const; + // Registers a codec, specified by |send_codec|, as sending codec. + // This API can be called multiple times. The last codec registered overwrites + // the previous ones. Returns true if successful, false if not. + // + // Note: If a stereo codec is registered as send codec, VAD/DTX will + // automatically be turned off, since it is not supported for stereo sending. + virtual bool RegisterSendCodec(AudioEncoder* send_codec) = 0; + + // Temporary solution to be used during refactoring: + // |encoder_type| should be from the anonymous enum in acm2::ACMCodecDB. + virtual bool RegisterSendCodec(int encoder_type, + uint8_t payload_type, + int frame_size_samples = 0) = 0; + + // Returns the encoder object currently in use. This is the same as the + // codec that was registered in the latest call to RegisterSendCodec(). + virtual const AudioEncoder* GetSenderInfo() const = 0; + + // Temporary solution to be used during refactoring. + virtual const CodecInst* GetSenderCodecInst() = 0; + + // Adds 10 ms of raw (PCM) audio data to the encoder. If the sampling + // frequency of the audio does not match the sampling frequency of the + // current encoder, ACM will resample the audio. + // + // Return value: + // 0 successfully added the frame. + // -1 some error occurred and data is not added. + // < -1 to add the frame to the buffer n samples had to be + // overwritten, -n is the return value in this case. + // TODO(henrik.lundin): Make a better design for the return values. This one + // is just a copy of the old API. + virtual int Add10MsAudio(const AudioFrame& audio_frame) = 0; + + // Returns a combined info about the currently used decoder(s). + virtual const ReceiverInfo* GetReceiverInfo() const = 0; + + // Registers a codec, specified by |receive_codec|, as receiving codec. + // This API can be called multiple times. If registering with a payload type + // that was already registered in a previous call, the latest call will + // override previous calls. Returns true if successful, false if not. + virtual bool RegisterReceiveCodec(AudioDecoder* receive_codec) = 0; + + // Temporary solution: + // |decoder_type| should be from the anonymous enum in acm2::ACMCodecDB. + virtual bool RegisterReceiveCodec(int decoder_type, uint8_t payload_type) = 0; + + // The following two methods both inserts a new packet to the receiver. + // InsertPacket takes an RTP header input in |rtp_info|, while InsertPayload + // only requires a payload type and a timestamp. The latter assumes that the + // payloads come in the right order, and without any losses. In both cases, + // |incoming_payload| contains the RTP payload after the RTP header. Return + // true if successful, false if not. + virtual bool InsertPacket(const uint8_t* incoming_payload, + int32_t payload_len_bytes, + const WebRtcRTPHeader& rtp_info) = 0; + + // TODO(henrik.lundin): Remove this method? + virtual bool InsertPayload(const uint8_t* incoming_payload, + int32_t payload_len_byte, + uint8_t payload_type, + uint32_t timestamp) = 0; + + // These two methods set a minimum and maximum jitter buffer delay in + // milliseconds. The pupose is mainly to adjust the delay to synchronize + // audio and video. The preferred jitter buffer size, computed by NetEq based + // on the current channel conditions, is clamped from below and above by these + // two methods. The given delay limits must be non-negative, less than + // 10000 ms, and the minimum must be strictly smaller than the maximum. + // Further, the maximum must be at lest one frame duration. If these + // conditions are not met, false is returned. Giving the value 0 effectively + // unsets the minimum or maximum delay limits. + // Note that calling these methods is optional. If not called, NetEq will + // determine the optimal buffer size based on the network conditions. + virtual bool SetMinimumPlayoutDelay(int time_ms) = 0; + + virtual bool SetMaximumPlayoutDelay(int time_ms) = 0; + + // Returns the current value of the jitter buffer's preferred latency. This + // is computed based on inter-arrival times and playout mode of NetEq. The + // actual target delay is this value clamped from below and above by the + // values specified through SetMinimumPlayoutDelay() and + // SetMaximumPlayoutDelay(), respectively, if provided. + // TODO(henrik.lundin) Rename to PreferredDelayMs? + virtual int LeastRequiredDelayMs() const = 0; + + // The send timestamp of an RTP packet is associated with the decoded + // audio of the packet in question. This function returns the timestamp of + // the latest audio delivered by Get10MsAudio(). Returns false if no timestamp + // can be provided, true otherwise. + virtual bool PlayoutTimestamp(uint32_t* timestamp) = 0; + + // Delivers 10 ms of audio in |audio_frame|. Returns true if successful, + // false otherwise. + virtual bool Get10MsAudio(AudioFrame* audio_frame) = 0; + + // Returns the network statistics. Note that the internal statistics of NetEq + // are reset by this call. Returns true if successful, false otherwise. + virtual bool NetworkStatistics(ACMNetworkStatistics* network_statistics) = 0; + + // Enables NACK and sets the maximum size of the NACK list. If NACK is already + // enabled then the maximum NACK list size is modified accordingly. Returns + // true if successful, false otherwise. + // + // If the sequence number of last received packet is N, the sequence numbers + // of NACK list are in the range of [N - |max_nack_list_size|, N). + // + // |max_nack_list_size| should be positive and less than or equal to + // |Nack::kNackListSizeLimit|. + virtual bool EnableNack(size_t max_nack_list_size) = 0; + + // Disables NACK. + virtual void DisableNack() = 0; + + + // Temporary solution to be used during refactoring. + // If DTX is enabled and the codec does not have internal DTX/VAD + // WebRtc VAD will be automatically enabled and |enable_vad| is ignored. + // + // If DTX is disabled but VAD is enabled no DTX packets are sent, + // regardless of whether the codec has internal DTX/VAD or not. In this + // case, WebRtc VAD is running to label frames as active/in-active. + // + // NOTE! VAD/DTX is not supported when sending stereo. + // + // Return true if successful, false otherwise. + virtual bool SetVad(bool enable_dtx, + bool enable_vad, + ACMVADMode vad_mode) = 0; + + // Returns a list of packets to request retransmission of. + // |round_trip_time_ms| is an estimate of the round-trip-time (in + // milliseconds). Missing packets which will be decoded sooner than the + // round-trip-time (with respect to the time this API is called) will not be + // included in the list. + // |round_trip_time_ms| must be non-negative. + virtual std::vector GetNackList(int round_trip_time_ms) const = 0; + + // Returns the timing statistics for calls to Get10MsAudio. + virtual void GetDecodingCallStatistics( + AudioDecodingCallStats* call_stats) const = 0; }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -1,3 +1,4 @@ tina.legrand@webrtc.org turaj@webrtc.org jan.skoglund@webrtc.org +henrik.lundin@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_amr.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_amr.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_amr.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_amr.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,430 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_amr.h" - -#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" - -#ifdef WEBRTC_CODEC_AMR -// NOTE! GSM AMR is not included in the open-source package. The following -// interface file is needed: -// -// /modules/audio_coding/codecs/amr/main/interface/amr_interface.h -// -// The API in the header file should match the one below. -// -// int16_t WebRtcAmr_CreateEnc(AMR_encinst_t_** enc_inst); -// int16_t WebRtcAmr_CreateDec(AMR_decinst_t_** dec_inst); -// int16_t WebRtcAmr_FreeEnc(AMR_encinst_t_* enc_inst); -// int16_t WebRtcAmr_FreeDec(AMR_decinst_t_* dec_inst); -// int16_t WebRtcAmr_Encode(AMR_encinst_t_* enc_inst, -// int16_t* input, -// int16_t len, -// int16_t*output, -// int16_t mode); -// int16_t WebRtcAmr_EncoderInit(AMR_encinst_t_* enc_inst, -// int16_t dtx_mode); -// int16_t WebRtcAmr_EncodeBitmode(AMR_encinst_t_* enc_inst, -// int format); -// int16_t WebRtcAmr_Decode(AMR_decinst_t_* dec_inst); -// int16_t WebRtcAmr_DecodePlc(AMR_decinst_t_* dec_inst); -// int16_t WebRtcAmr_DecoderInit(AMR_decinst_t_* dec_inst); -// int16_t WebRtcAmr_DecodeBitmode(AMR_decinst_t_* dec_inst, -// int format); -#include "amr_interface.h" -#endif - -namespace webrtc { - -namespace acm1 { - -#ifndef WEBRTC_CODEC_AMR -ACMAMR::ACMAMR(int16_t /* codec_id */) - : encoder_inst_ptr_(NULL), - decoder_inst_ptr_(NULL), - encoding_mode_(-1), // Invalid value. - encoding_rate_(0), // Invalid value. - encoder_packing_format_(AMRBandwidthEfficient), - decoder_packing_format_(AMRBandwidthEfficient) { - return; -} - -ACMAMR::~ACMAMR() { - return; -} - -int16_t ACMAMR::InternalEncode(uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - return -1; -} - -int16_t ACMAMR::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return -1; -} - -int16_t ACMAMR::EnableDTX() { - return -1; -} - -int16_t ACMAMR::DisableDTX() { - return -1; -} - -int16_t ACMAMR::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int16_t ACMAMR::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int32_t ACMAMR::CodecDef(WebRtcNetEQ_CodecDef& /* codec_def */, - const CodecInst& /* codec_inst */) { - return -1; -} - -ACMGenericCodec* ACMAMR::CreateInstance(void) { - return NULL; -} - -int16_t ACMAMR::InternalCreateEncoder() { - return -1; -} - -void ACMAMR::DestructEncoderSafe() { - return; -} - -int16_t ACMAMR::InternalCreateDecoder() { - return -1; -} - -void ACMAMR::DestructDecoderSafe() { - return; -} - -int16_t ACMAMR::SetBitRateSafe(const int32_t /* rate */) { - return -1; -} - -void ACMAMR::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - -int16_t ACMAMR::SetAMREncoderPackingFormat( - ACMAMRPackingFormat /* packing_format */) { - return -1; -} - -ACMAMRPackingFormat ACMAMR::AMREncoderPackingFormat() const { - return AMRUndefined; -} - -int16_t ACMAMR::SetAMRDecoderPackingFormat( - ACMAMRPackingFormat /* packing_format */) { - return -1; -} - -ACMAMRPackingFormat ACMAMR::AMRDecoderPackingFormat() const { - return AMRUndefined; -} - -#else //===================== Actual Implementation ======================= - -#define WEBRTC_AMR_MR475 0 -#define WEBRTC_AMR_MR515 1 -#define WEBRTC_AMR_MR59 2 -#define WEBRTC_AMR_MR67 3 -#define WEBRTC_AMR_MR74 4 -#define WEBRTC_AMR_MR795 5 -#define WEBRTC_AMR_MR102 6 -#define WEBRTC_AMR_MR122 7 - -ACMAMR::ACMAMR(int16_t codec_id) - : encoder_inst_ptr_(NULL), - decoder_inst_ptr_(NULL), - encoding_mode_(-1), // invalid value - encoding_rate_(0) { // invalid value - codec_id_ = codec_id; - has_internal_dtx_ = true; - encoder_packing_format_ = AMRBandwidthEfficient; - decoder_packing_format_ = AMRBandwidthEfficient; - return; -} - -ACMAMR::~ACMAMR() { - if (encoder_inst_ptr_ != NULL) { - WebRtcAmr_FreeEnc(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } - if (decoder_inst_ptr_ != NULL) { - WebRtcAmr_FreeDec(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } - return; -} - -int16_t ACMAMR::InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) { - int16_t vad_decision = 1; - // sanity check, if the rate is set correctly. we might skip this - // sanity check. if rate is not set correctly, initialization flag - // should be false and should not be here. - if ((encoding_mode_ < WEBRTC_AMR_MR475) || - (encoding_mode_ > WEBRTC_AMR_MR122)) { - *bitstream_len_byte = 0; - return -1; - } - *bitstream_len_byte = WebRtcAmr_Encode(encoder_inst_ptr_, - &in_audio_[in_audio_ix_read_], - frame_len_smpl_, - (int16_t*)bitstream, - encoding_mode_); - - // Update VAD, if internal DTX is used - if (has_internal_dtx_ && dtx_enabled_) { - if (*bitstream_len_byte <= (7 * frame_len_smpl_ / 160)) { - vad_decision = 0; - } - for (int16_t n = 0; n < MAX_FRAME_SIZE_10MSEC; n++) { - vad_label_[n] = vad_decision; - } - } - // increment the read index - in_audio_ix_read_ += frame_len_smpl_; - return *bitstream_len_byte; -} - -int16_t ACMAMR::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return 0; -} - -int16_t ACMAMR::EnableDTX() { - if (dtx_enabled_) { - return 0; - } else if (encoder_exist_) { // check if encoder exist - // enable DTX - if (WebRtcAmr_EncoderInit(encoder_inst_ptr_, 1) < 0) { - return -1; - } - dtx_enabled_ = true; - return 0; - } else { - return -1; - } -} - -int16_t ACMAMR::DisableDTX() { - if (!dtx_enabled_) { - return 0; - } else if (encoder_exist_) { // check if encoder exist - // disable DTX - if (WebRtcAmr_EncoderInit(encoder_inst_ptr_, 0) < 0) { - return -1; - } - dtx_enabled_ = false; - return 0; - } else { - // encoder doesn't exists, therefore disabling is harmless - return 0; - } -} - -int16_t ACMAMR::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { - int16_t status = SetBitRateSafe((codec_params->codec_inst).rate); - status += (WebRtcAmr_EncoderInit( - encoder_inst_ptr_, ((codec_params->enable_dtx) ? 1 : 0)) < 0) ? -1 : 0; - status += (WebRtcAmr_EncodeBitmode( - encoder_inst_ptr_, encoder_packing_format_) < 0) ? -1 : 0; - return (status < 0) ? -1 : 0; -} - -int16_t ACMAMR::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - int16_t status = - ((WebRtcAmr_DecoderInit(decoder_inst_ptr_) < 0) ? -1 : 0); - status += WebRtcAmr_DecodeBitmode(decoder_inst_ptr_, decoder_packing_format_); - return (status < 0) ? -1 : 0; -} - -int32_t ACMAMR::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - if (!decoder_initialized_) { - // Todo: - // log error - return -1; - } - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_AMR_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codec_def), kDecoderAMR, codec_inst.pltype, decoder_inst_ptr_, - 8000); - SET_AMR_FUNCTIONS((codec_def)); - return 0; -} - -ACMGenericCodec* ACMAMR::CreateInstance(void) { - return NULL; -} - -int16_t ACMAMR::InternalCreateEncoder() { - return WebRtcAmr_CreateEnc(&encoder_inst_ptr_); -} - -void ACMAMR::DestructEncoderSafe() { - if (encoder_inst_ptr_ != NULL) { - WebRtcAmr_FreeEnc(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } - // there is no encoder set the following - encoder_exist_ = false; - encoder_initialized_ = false; - encoding_mode_ = -1; // invalid value - encoding_rate_ = 0; // invalid value -} - -int16_t ACMAMR::InternalCreateDecoder() { - return WebRtcAmr_CreateDec(&decoder_inst_ptr_); -} - -void ACMAMR::DestructDecoderSafe() { - if (decoder_inst_ptr_ != NULL) { - WebRtcAmr_FreeDec(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } - // there is no encoder instance set the followings - decoder_exist_ = false; - decoder_initialized_ = false; -} - -int16_t ACMAMR::SetBitRateSafe(const int32_t rate) { - switch (rate) { - case 4750: { - encoding_mode_ = WEBRTC_AMR_MR475; - encoding_rate_ = 4750; - break; - } - case 5150: { - encoding_mode_ = WEBRTC_AMR_MR515; - encoding_rate_ = 5150; - break; - } - case 5900: { - encoding_mode_ = WEBRTC_AMR_MR59; - encoding_rate_ = 5900; - break; - } - case 6700: { - encoding_mode_ = WEBRTC_AMR_MR67; - encoding_rate_ = 6700; - break; - } - case 7400: { - encoding_mode_ = WEBRTC_AMR_MR74; - encoding_rate_ = 7400; - break; - } - case 7950: { - encoding_mode_ = WEBRTC_AMR_MR795; - encoding_rate_ = 7950; - break; - } - case 10200: { - encoding_mode_ = WEBRTC_AMR_MR102; - encoding_rate_ = 10200; - break; - } - case 12200: { - encoding_mode_ = WEBRTC_AMR_MR122; - encoding_rate_ = 12200; - break; - } - default: { - return -1; - } - } - return 0; -} - -void ACMAMR::InternalDestructEncoderInst(void* ptr_inst) { - // Free the memory where ptr_inst is pointing to - if (ptr_inst != NULL) { - WebRtcAmr_FreeEnc(reinterpret_cast(ptr_inst)); - } - return; -} - -int16_t ACMAMR::SetAMREncoderPackingFormat( - ACMAMRPackingFormat packing_format) { - if ((packing_format != AMRBandwidthEfficient) && - (packing_format != AMROctetAlligned) && - (packing_format != AMRFileStorage)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Invalid AMR Encoder packing-format."); - return -1; - } else { - if (WebRtcAmr_EncodeBitmode(encoder_inst_ptr_, packing_format) < 0) { - return -1; - } else { - encoder_packing_format_ = packing_format; - return 0; - } - } -} - -ACMAMRPackingFormat ACMAMR::AMREncoderPackingFormat() const { - return encoder_packing_format_; -} - -int16_t ACMAMR::SetAMRDecoderPackingFormat( - ACMAMRPackingFormat packing_format) { - if ((packing_format != AMRBandwidthEfficient) && - (packing_format != AMROctetAlligned) && - (packing_format != AMRFileStorage)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Invalid AMR decoder packing-format."); - return -1; - } else { - if (WebRtcAmr_DecodeBitmode(decoder_inst_ptr_, packing_format) < 0) { - return -1; - } else { - decoder_packing_format_ = packing_format; - return 0; - } - } -} - -ACMAMRPackingFormat ACMAMR::AMRDecoderPackingFormat() const { - return decoder_packing_format_; -} - -#endif - -} // namespace acm1 - -} // namespace webrtc - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_amr.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_amr.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_amr.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_amr.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_AMR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_AMR_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -// forward declaration -struct AMR_encinst_t_; -struct AMR_decinst_t_; - -namespace webrtc { - -namespace acm1 { - -class ACMAMR : public ACMGenericCodec { - public: - explicit ACMAMR(int16_t codec_id); - virtual ~ACMAMR(); - - // for FEC - virtual ACMGenericCodec* CreateInstance(void) OVERRIDE; - - virtual int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) OVERRIDE; - - virtual int16_t InternalInitEncoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - virtual int16_t InternalInitDecoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - int16_t SetAMREncoderPackingFormat(const ACMAMRPackingFormat packing_format); - - ACMAMRPackingFormat AMREncoderPackingFormat() const; - - int16_t SetAMRDecoderPackingFormat(const ACMAMRPackingFormat packing_format); - - ACMAMRPackingFormat AMRDecoderPackingFormat() const; - - protected: - virtual int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type) OVERRIDE; - - virtual int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) OVERRIDE; - - virtual void DestructEncoderSafe() OVERRIDE; - - virtual void DestructDecoderSafe() OVERRIDE; - - virtual int16_t InternalCreateEncoder() OVERRIDE; - - virtual int16_t InternalCreateDecoder() OVERRIDE; - - virtual void InternalDestructEncoderInst(void* ptr_inst) OVERRIDE; - - virtual int16_t SetBitRateSafe(const int32_t rate) OVERRIDE; - - virtual int16_t EnableDTX() OVERRIDE; - - virtual int16_t DisableDTX() OVERRIDE; - - AMR_encinst_t_* encoder_inst_ptr_; - AMR_decinst_t_* decoder_inst_ptr_; - int16_t encoding_mode_; - int16_t encoding_rate_; - ACMAMRPackingFormat encoder_packing_format_; - ACMAMRPackingFormat decoder_packing_format_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_AMR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_amrwb.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_amrwb.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_amrwb.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_amrwb.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,436 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_amrwb.h" - -#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" - -#ifdef WEBRTC_CODEC_AMRWB -// NOTE! GSM AMR-wb is not included in the open-source package. The -// following interface file is needed: -// -// /modules/audio_coding/codecs/amrwb/main/interface/amrwb_interface.h -// -// The API in the header file should match the one below. -// -// int16_t WebRtcAmrWb_CreateEnc(AMRWB_encinst_t_** enc_inst); -// int16_t WebRtcAmrWb_CreateDec(AMRWB_decinst_t_** dec_inst); -// int16_t WebRtcAmrWb_FreeEnc(AMRWB_encinst_t_* enc_inst); -// int16_t WebRtcAmrWb_FreeDec(AMRWB_decinst_t_* dec_inst); -// int16_t WebRtcAmrWb_Encode(AMRWB_encinst_t_* enc_inst, int16_t* input, -// int16_t len, int16_t* output, int16_t mode); -// int16_t WebRtcAmrWb_EncoderInit(AMRWB_encinst_t_* enc_inst, -// int16_t dtx_mode); -// int16_t WebRtcAmrWb_EncodeBitmode(AMRWB_encinst_t_* enc_inst, -// int format); -// int16_t WebRtcAmrWb_Decode(AMRWB_decinst_t_* dec_inst); -// int16_t WebRtcAmrWb_DecodePlc(AMRWB_decinst_t_* dec_inst); -// int16_t WebRtcAmrWb_DecoderInit(AMRWB_decinst_t_* dec_inst); -// int16_t WebRtcAmrWb_DecodeBitmode(AMRWB_decinst_t_* dec_inst, -// int format); -#include "amrwb_interface.h" -#endif - -namespace webrtc { - -namespace acm1 { - -#ifndef WEBRTC_CODEC_AMRWB -ACMAMRwb::ACMAMRwb(int16_t /* codec_id */) - : encoder_inst_ptr_(NULL), - decoder_inst_ptr_(NULL), - encoding_mode_(-1), // invalid value - encoding_rate_(0), // invalid value - encoder_packing_format_(AMRBandwidthEfficient), - decoder_packing_format_(AMRBandwidthEfficient) { -} - -ACMAMRwb::~ACMAMRwb() { -} - -int16_t ACMAMRwb::InternalEncode( - uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - return -1; -} - -int16_t ACMAMRwb::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return -1; -} - -int16_t ACMAMRwb::EnableDTX() { - return -1; -} - -int16_t ACMAMRwb::DisableDTX() { - return -1; -} - -int16_t ACMAMRwb::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int16_t ACMAMRwb::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int32_t ACMAMRwb::CodecDef(WebRtcNetEQ_CodecDef& /* codec_def */, - const CodecInst& /* codec_inst */) { - return -1; -} - -ACMGenericCodec* -ACMAMRwb::CreateInstance(void) { - return NULL; -} - -int16_t ACMAMRwb::InternalCreateEncoder() { - return -1; -} - -void ACMAMRwb::DestructEncoderSafe() { - return; -} - -int16_t ACMAMRwb::InternalCreateDecoder() { - return -1; -} - -void ACMAMRwb::DestructDecoderSafe() { - return; -} - -int16_t ACMAMRwb::SetBitRateSafe(const int32_t /* rate */) { - return -1; -} - -void ACMAMRwb::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - -int16_t ACMAMRwb::SetAMRwbEncoderPackingFormat( - ACMAMRPackingFormat /* packing_format */) { - return -1; -} - -ACMAMRPackingFormat ACMAMRwb::AMRwbEncoderPackingFormat() const { - return AMRUndefined; -} - -int16_t ACMAMRwb::SetAMRwbDecoderPackingFormat( - ACMAMRPackingFormat /* packing_format */) { - return -1; -} - -ACMAMRPackingFormat ACMAMRwb::AMRwbDecoderPackingFormat() const { - return AMRUndefined; -} - -#else //===================== Actual Implementation ======================= - -#define AMRWB_MODE_7k 0 -#define AMRWB_MODE_9k 1 -#define AMRWB_MODE_12k 2 -#define AMRWB_MODE_14k 3 -#define AMRWB_MODE_16k 4 -#define AMRWB_MODE_18k 5 -#define AMRWB_MODE_20k 6 -#define AMRWB_MODE_23k 7 -#define AMRWB_MODE_24k 8 - -ACMAMRwb::ACMAMRwb(int16_t codec_id) - : encoder_inst_ptr_(NULL), - decoder_inst_ptr_(NULL), - encoding_mode_(-1), // invalid value - encoding_rate_(0) { // invalid value - codec_id_ = codec_id; - has_internal_dtx_ = true; - encoder_packing_format_ = AMRBandwidthEfficient; - decoder_packing_format_ = AMRBandwidthEfficient; - return; -} - -ACMAMRwb::~ACMAMRwb() { - if (encoder_inst_ptr_ != NULL) { - WebRtcAmrWb_FreeEnc(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } - if (decoder_inst_ptr_ != NULL) { - WebRtcAmrWb_FreeDec(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } - return; -} - -int16_t ACMAMRwb::InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) { - int16_t vad_decision = 1; - // sanity check, if the rate is set correctly. we might skip this - // sanity check. if rate is not set correctly, initialization flag - // should be false and should not be here. - if ((encoding_mode_ < AMRWB_MODE_7k) || (encoding_mode_ > AMRWB_MODE_24k)) { - *bitstream_len_byte = 0; - return -1; - } - *bitstream_len_byte = WebRtcAmrWb_Encode(encoder_inst_ptr_, - &in_audio_[in_audio_ix_read_], - frame_len_smpl_, - (int16_t*)bitstream, - encoding_mode_); - - // Update VAD, if internal DTX is used - if (has_internal_dtx_ && dtx_enabled_) { - if (*bitstream_len_byte <= (7 * frame_len_smpl_ / 160)) { - vad_decision = 0; - } - for (int16_t n = 0; n < MAX_FRAME_SIZE_10MSEC; n++) { - vad_label_[n] = vad_decision; - } - } - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - in_audio_ix_read_ += frame_len_smpl_; - return *bitstream_len_byte; -} - -int16_t ACMAMRwb::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return 0; -} - -int16_t ACMAMRwb::EnableDTX() { - if (dtx_enabled_) { - return 0; - } else if (encoder_exist_) { // check if encoder exist - // enable DTX - if (WebRtcAmrWb_EncoderInit(encoder_inst_ptr_, 1) < 0) { - return -1; - } - dtx_enabled_ = true; - return 0; - } else { - return -1; - } -} - -int16_t ACMAMRwb::DisableDTX() { - if (!dtx_enabled_) { - return 0; - } else if (encoder_exist_) { // check if encoder exist - // disable DTX - if (WebRtcAmrWb_EncoderInit(encoder_inst_ptr_, 0) < 0) { - return -1; - } - dtx_enabled_ = false; - return 0; - } else { - // encoder doesn't exists, therefore disabling is harmless - return 0; - } -} - -int16_t ACMAMRwb::InternalInitEncoder( - WebRtcACMCodecParams* codec_params) { - // sanity check - if (encoder_inst_ptr_ == NULL) { - return -1; - } - - int16_t status = SetBitRateSafe((codec_params->codec_inst).rate); - status += (WebRtcAmrWb_EncoderInit( - encoder_inst_ptr_, ((codec_params->enable_dtx) ? 1 : 0)) < 0) ? -1 : 0; - status += (WebRtcAmrWb_EncodeBitmode( - encoder_inst_ptr_, encoder_packing_format_) < 0) ? -1 : 0; - return (status < 0) ? -1 : 0; -} - -int16_t ACMAMRwb::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - int16_t status = WebRtcAmrWb_DecodeBitmode(decoder_inst_ptr_, - decoder_packing_format_); - status += ((WebRtcAmrWb_DecoderInit(decoder_inst_ptr_) < 0) ? -1 : 0); - return (status < 0) ? -1 : 0; -} - -int32_t ACMAMRwb::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - if (!decoder_initialized_) { - return -1; - } - - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_AMRWB_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codec_def), kDecoderAMRWB, codec_inst.pltype, - decoder_inst_ptr_, 16000); - SET_AMRWB_FUNCTIONS((codec_def)); - return 0; -} - -ACMGenericCodec* ACMAMRwb::CreateInstance(void) { - return NULL; -} - -int16_t ACMAMRwb::InternalCreateEncoder() { - return WebRtcAmrWb_CreateEnc(&encoder_inst_ptr_); -} - -void ACMAMRwb::DestructEncoderSafe() { - if (encoder_inst_ptr_ != NULL) { - WebRtcAmrWb_FreeEnc(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } - // there is no encoder set the following - encoder_exist_ = false; - encoder_initialized_ = false; - encoding_mode_ = -1; // invalid value - encoding_rate_ = 0; -} - -int16_t ACMAMRwb::InternalCreateDecoder() { - return WebRtcAmrWb_CreateDec(&decoder_inst_ptr_); -} - -void ACMAMRwb::DestructDecoderSafe() { - if (decoder_inst_ptr_ != NULL) { - WebRtcAmrWb_FreeDec(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } - // there is no encoder instance set the followings - decoder_exist_ = false; - decoder_initialized_ = false; -} - -int16_t ACMAMRwb::SetBitRateSafe(const int32_t rate) { - switch (rate) { - case 7000: { - encoding_mode_ = AMRWB_MODE_7k; - encoding_rate_ = 7000; - break; - } - case 9000: { - encoding_mode_ = AMRWB_MODE_9k; - encoding_rate_ = 9000; - break; - } - case 12000: { - encoding_mode_ = AMRWB_MODE_12k; - encoding_rate_ = 12000; - break; - } - case 14000: { - encoding_mode_ = AMRWB_MODE_14k; - encoding_rate_ = 14000; - break; - } - case 16000: { - encoding_mode_ = AMRWB_MODE_16k; - encoding_rate_ = 16000; - break; - } - case 18000: { - encoding_mode_ = AMRWB_MODE_18k; - encoding_rate_ = 18000; - break; - } - case 20000: { - encoding_mode_ = AMRWB_MODE_20k; - encoding_rate_ = 20000; - break; - } - case 23000: { - encoding_mode_ = AMRWB_MODE_23k; - encoding_rate_ = 23000; - break; - } - case 24000: { - encoding_mode_ = AMRWB_MODE_24k; - encoding_rate_ = 24000; - break; - } - default: { - return -1; - } - } - return 0; -} - -void ACMAMRwb::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcAmrWb_FreeEnc(static_cast(ptr_inst)); - } - return; -} - -int16_t ACMAMRwb::SetAMRwbEncoderPackingFormat( - ACMAMRPackingFormat packing_format) { - if ((packing_format != AMRBandwidthEfficient) && - (packing_format != AMROctetAlligned) && - (packing_format != AMRFileStorage)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Invalid AMRwb encoder packing-format."); - return -1; - } else { - if (WebRtcAmrWb_EncodeBitmode(encoder_inst_ptr_, packing_format) < 0) { - return -1; - } else { - encoder_packing_format_ = packing_format; - return 0; - } - } -} - -ACMAMRPackingFormat ACMAMRwb::AMRwbEncoderPackingFormat() const { - return encoder_packing_format_; -} - -int16_t ACMAMRwb::SetAMRwbDecoderPackingFormat( - ACMAMRPackingFormat packing_format) { - if ((packing_format != AMRBandwidthEfficient) && - (packing_format != AMROctetAlligned) && - (packing_format != AMRFileStorage)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Invalid AMRwb decoder packing-format."); - return -1; - } else { - if (WebRtcAmrWb_DecodeBitmode(decoder_inst_ptr_, packing_format) < 0) { - return -1; - } else { - decoder_packing_format_ = packing_format; - return 0; - } - } -} - -ACMAMRPackingFormat ACMAMRwb::AMRwbDecoderPackingFormat() const { - return decoder_packing_format_; -} - -#endif - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_amrwb.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_amrwb.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_amrwb.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_amrwb.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_AMRWB_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_AMRWB_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -// forward declaration -struct AMRWB_encinst_t_; -struct AMRWB_decinst_t_; - -namespace webrtc { - -namespace acm1 { - -class ACMAMRwb : public ACMGenericCodec { - public: - explicit ACMAMRwb(int16_t codec_id); - virtual ~ACMAMRwb(); - - // for FEC - virtual ACMGenericCodec* CreateInstance(void) OVERRIDE; - - virtual int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) OVERRIDE; - - virtual int16_t InternalInitEncoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - virtual int16_t InternalInitDecoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - virtual int16_t SetAMRwbEncoderPackingFormat( - const ACMAMRPackingFormat packing_format); - - virtual ACMAMRPackingFormat AMRwbEncoderPackingFormat() const; - - virtual int16_t SetAMRwbDecoderPackingFormat( - const ACMAMRPackingFormat packing_format); - - virtual ACMAMRPackingFormat AMRwbDecoderPackingFormat() const; - - protected: - virtual int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type) OVERRIDE; - - virtual int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) OVERRIDE; - - virtual void DestructEncoderSafe() OVERRIDE; - - virtual void DestructDecoderSafe() OVERRIDE; - - virtual int16_t InternalCreateEncoder() OVERRIDE; - - virtual int16_t InternalCreateDecoder() OVERRIDE; - - virtual void InternalDestructEncoderInst(void* ptr_inst) OVERRIDE; - - virtual int16_t SetBitRateSafe(const int32_t rate) OVERRIDE; - - virtual int16_t EnableDTX() OVERRIDE; - - virtual int16_t DisableDTX() OVERRIDE; - - AMRWB_encinst_t_* encoder_inst_ptr_; - AMRWB_decinst_t_* decoder_inst_ptr_; - - int16_t encoding_mode_; - int16_t encoding_rate_; - ACMAMRPackingFormat encoder_packing_format_; - ACMAMRPackingFormat decoder_packing_format_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_AMRWB_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_celt.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_celt.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_celt.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_celt.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,339 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_celt.h" - -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/trace.h" - -#ifdef WEBRTC_CODEC_CELT -// NOTE! Celt is not included in the open-source package. Modify this file or -// your codec API to match the function call and name of used Celt API file. -#include "celt_interface.h" -#endif - -namespace webrtc { - -namespace acm1 { - -#ifndef WEBRTC_CODEC_CELT - -ACMCELT::ACMCELT(int16_t /* codec_id */) - : enc_inst_ptr_(NULL), - dec_inst_ptr_(NULL), - sampling_freq_(0), - bitrate_(0), - channels_(1), - dec_channels_(1) { - return; -} - -ACMCELT::~ACMCELT() { - return; -} - -int16_t ACMCELT::InternalEncode(uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - return -1; -} - -int16_t ACMCELT::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return -1; -} - -int16_t ACMCELT::InternalInitEncoder(WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int16_t ACMCELT::InternalInitDecoder(WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int32_t ACMCELT::CodecDef(WebRtcNetEQ_CodecDef& /* codec_def */, - const CodecInst& /* codec_inst */) { - return -1; -} - -ACMGenericCodec* ACMCELT::CreateInstance(void) { - return NULL; -} - -int16_t ACMCELT::InternalCreateEncoder() { - return -1; -} - -void ACMCELT::DestructEncoderSafe() { - return; -} - -int16_t ACMCELT::InternalCreateDecoder() { - return -1; -} - -void ACMCELT::DestructDecoderSafe() { - return; -} - -void ACMCELT::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - -bool ACMCELT::IsTrueStereoCodec() { - return true; -} - -int16_t ACMCELT::SetBitRateSafe(const int32_t /*rate*/) { - return -1; -} - -void ACMCELT::SplitStereoPacket(uint8_t* /*payload*/, - int32_t* /*payload_length*/) {} - -#else //===================== Actual Implementation ======================= - -ACMCELT::ACMCELT(int16_t codec_id) - : enc_inst_ptr_(NULL), - dec_inst_ptr_(NULL), - sampling_freq_(32000), // Default sampling frequency. - bitrate_(64000), // Default rate. - channels_(1), // Default send mono. - dec_channels_(1) { // Default receive mono. - // TODO(tlegrand): remove later when ACMGenericCodec has a new constructor. - codec_id_ = codec_id; - - return; -} - -ACMCELT::~ACMCELT() { - if (enc_inst_ptr_ != NULL) { - WebRtcCelt_FreeEnc(enc_inst_ptr_); - enc_inst_ptr_ = NULL; - } - if (dec_inst_ptr_ != NULL) { - WebRtcCelt_FreeDec(dec_inst_ptr_); - dec_inst_ptr_ = NULL; - } - return; -} - -int16_t ACMCELT::InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) { - *bitstream_len_byte = 0; - - // Call Encoder. - *bitstream_len_byte = WebRtcCelt_Encode(enc_inst_ptr_, - &in_audio_[in_audio_ix_read_], - bitstream); - - // Increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer. - in_audio_ix_read_ += frame_len_smpl_ * channels_; - - if (*bitstream_len_byte < 0) { - // Error reported from the encoder. - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalEncode: Encode error for Celt"); - *bitstream_len_byte = 0; - return -1; - } - - return *bitstream_len_byte; -} - -int16_t ACMCELT::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return 0; -} - -int16_t ACMCELT::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { - // Set bitrate and check that it is within the valid range. - int16_t status = SetBitRateSafe((codec_params->codec_inst).rate); - if (status < 0) { - return -1; - } - - // If number of channels changed we need to re-create memory. - if (codec_params->codec_inst.channels != channels_) { - WebRtcCelt_FreeEnc(enc_inst_ptr_); - enc_inst_ptr_ = NULL; - // Store new number of channels. - channels_ = codec_params->codec_inst.channels; - if (WebRtcCelt_CreateEnc(&enc_inst_ptr_, channels_) < 0) { - return -1; - } - } - - // Initiate encoder. - if (WebRtcCelt_EncoderInit(enc_inst_ptr_, channels_, bitrate_) >= 0) { - return 0; - } else { - return -1; - } -} - -int16_t ACMCELT::InternalInitDecoder(WebRtcACMCodecParams* codec_params) { - // If number of channels changed we need to re-create memory. - if (codec_params->codec_inst.channels != dec_channels_) { - WebRtcCelt_FreeDec(dec_inst_ptr_); - dec_inst_ptr_ = NULL; - // Store new number of channels. - dec_channels_ = codec_params->codec_inst.channels; - if (WebRtcCelt_CreateDec(&dec_inst_ptr_, dec_channels_) < 0) { - return -1; - } - } - - // Initiate decoder, both master and slave parts. - if (WebRtcCelt_DecoderInit(dec_inst_ptr_) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalInitDecoder: init decoder failed for Celt."); - return -1; - } - if (WebRtcCelt_DecoderInitSlave(dec_inst_ptr_) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalInitDecoder: init decoder failed for Celt."); - return -1; - } - return 0; -} - -int32_t ACMCELT::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - if (!decoder_initialized_) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "CodecDef: Decoder uninitialized for Celt"); - return -1; - } - - // Fill up the structure by calling - // "SET_CODEC_PAR" and "SET_CELT_FUNCTIONS" or "SET_CELTSLAVE_FUNCTIONS". - // Then call NetEQ to add the codec to it's - // database. - if (codec_inst.channels == 1) { - SET_CODEC_PAR(codec_def, kDecoderCELT_32, codec_inst.pltype, dec_inst_ptr_, - 32000); - } else { - SET_CODEC_PAR(codec_def, kDecoderCELT_32_2ch, codec_inst.pltype, - dec_inst_ptr_, 32000); - } - - // If this is the master of NetEQ, regular decoder will be added, otherwise - // the slave decoder will be used. - if (is_master_) { - SET_CELT_FUNCTIONS(codec_def); - } else { - SET_CELTSLAVE_FUNCTIONS(codec_def); - } - return 0; -} - -ACMGenericCodec* ACMCELT::CreateInstance(void) { - return NULL; -} - -int16_t ACMCELT::InternalCreateEncoder() { - if (WebRtcCelt_CreateEnc(&enc_inst_ptr_, num_channels_) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalCreateEncoder: create encoder failed for Celt"); - return -1; - } - channels_ = num_channels_; - return 0; -} - -void ACMCELT::DestructEncoderSafe() { - encoder_exist_ = false; - encoder_initialized_ = false; - if (enc_inst_ptr_ != NULL) { - WebRtcCelt_FreeEnc(enc_inst_ptr_); - enc_inst_ptr_ = NULL; - } -} - -int16_t ACMCELT::InternalCreateDecoder() { - if (WebRtcCelt_CreateDec(&dec_inst_ptr_, dec_channels_) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalCreateDecoder: create decoder failed for Celt"); - return -1; - } - - return 0; -} - -void ACMCELT::DestructDecoderSafe() { - decoder_exist_ = false; - decoder_initialized_ = false; - if (dec_inst_ptr_ != NULL) { - WebRtcCelt_FreeDec(dec_inst_ptr_); - dec_inst_ptr_ = NULL; - } -} - -void ACMCELT::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcCelt_FreeEnc(static_cast(ptr_inst)); - } - return; -} - -bool ACMCELT::IsTrueStereoCodec() { - return true; -} - -int16_t ACMCELT::SetBitRateSafe(const int32_t rate) { - // Check that rate is in the valid range. - if ((rate >= 48000) && (rate <= 128000)) { - // Store new rate. - bitrate_ = rate; - - // Initiate encoder with new rate. - if (WebRtcCelt_EncoderInit(enc_inst_ptr_, channels_, bitrate_) >= 0) { - return 0; - } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "SetBitRateSafe: Failed to initiate Celt with rate %d", - rate); - return -1; - } - } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "SetBitRateSafe: Invalid rate Celt, %d", rate); - return -1; - } -} - -// Copy the stereo packet so that NetEq will insert into both master and slave. -void ACMCELT::SplitStereoPacket(uint8_t* payload, int32_t* payload_length) { - // Check for valid inputs. - assert(payload != NULL); - assert(*payload_length > 0); - - // Duplicate the payload. - memcpy(&payload[*payload_length], &payload[0], - sizeof(uint8_t) * (*payload_length)); - // Double the size of the packet. - *payload_length *= 2; -} - -#endif - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_celt.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_celt.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_celt.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_celt.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CELT_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CELT_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -// forward declaration -struct CELT_encinst_t_; -struct CELT_decinst_t_; - -namespace webrtc { - -namespace acm1 { - -class ACMCELT : public ACMGenericCodec { - public: - explicit ACMCELT(int16_t codec_id); - virtual ~ACMCELT(); - - virtual ACMGenericCodec* CreateInstance(void) OVERRIDE; - - virtual int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) OVERRIDE; - - virtual int16_t InternalInitEncoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - virtual int16_t InternalInitDecoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - protected: - virtual int16_t DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) OVERRIDE; - - virtual int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) OVERRIDE; - - virtual void DestructEncoderSafe() OVERRIDE; - - virtual void DestructDecoderSafe() OVERRIDE; - - virtual int16_t InternalCreateEncoder() OVERRIDE; - - virtual int16_t InternalCreateDecoder() OVERRIDE; - - virtual void InternalDestructEncoderInst(void* ptr_inst) OVERRIDE; - - virtual bool IsTrueStereoCodec() OVERRIDE; - - virtual int16_t SetBitRateSafe(const int32_t rate) OVERRIDE; - - virtual void SplitStereoPacket(uint8_t* payload, - int32_t* payload_length) OVERRIDE; - - CELT_encinst_t_* enc_inst_ptr_; - CELT_decinst_t_* dec_inst_ptr_; - uint16_t sampling_freq_; - int32_t bitrate_; - uint16_t channels_; - uint16_t dec_channels_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CELT_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_cng.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_cng.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_cng.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_cng.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,150 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_cng.h" - -#include "webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h" -#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/trace.h" - -namespace webrtc { - -namespace acm1 { - -ACMCNG::ACMCNG(int16_t codec_id) { - encoder_inst_ptr_ = NULL; - decoder_inst_ptr_ = NULL; - codec_id_ = codec_id; - samp_freq_hz_ = ACMCodecDB::CodecFreq(codec_id_); - return; -} - -ACMCNG::~ACMCNG() { - if (encoder_inst_ptr_ != NULL) { - WebRtcCng_FreeEnc(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } - if (decoder_inst_ptr_ != NULL) { - WebRtcCng_FreeDec(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } - return; -} - -// CNG is not like a regular encoder, this function -// should not be called normally -// instead the following function is called from inside -// ACMGenericCodec::ProcessFrameVADDTX -int16_t ACMCNG::InternalEncode(uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - return -1; -} - -int16_t ACMCNG::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return 0; -} - -// CNG is not like a regular encoder, -// this function should not be called normally -// instead the following function is called from inside -// ACMGenericCodec::ProcessFrameVADDTX -int16_t ACMCNG::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int16_t ACMCNG::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - return WebRtcCng_InitDec(decoder_inst_ptr_); -} - -int32_t ACMCNG::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - if (!decoder_initialized_) { - // TODO(tlegrand): log error - return -1; - } - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_CNG_FUNCTION." - // Then return the structure back to NetEQ to add the codec to it's - // database. - - if (samp_freq_hz_ == 8000 || samp_freq_hz_ == 16000 || - samp_freq_hz_ == 32000 || samp_freq_hz_ == 48000) { - SET_CODEC_PAR((codec_def), kDecoderCNG, codec_inst.pltype, - decoder_inst_ptr_, samp_freq_hz_); - SET_CNG_FUNCTIONS((codec_def)); - return 0; - } else { - return -1; - } -} - -ACMGenericCodec* ACMCNG::CreateInstance(void) { - return NULL; -} - -int16_t ACMCNG::InternalCreateEncoder() { - if (WebRtcCng_CreateEnc(&encoder_inst_ptr_) < 0) { - encoder_inst_ptr_ = NULL; - return -1; - } else { - return 0; - } -} - -void ACMCNG::DestructEncoderSafe() { - if (encoder_inst_ptr_ != NULL) { - WebRtcCng_FreeEnc(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } - encoder_exist_ = false; - encoder_initialized_ = false; -} - -int16_t ACMCNG::InternalCreateDecoder() { - if (WebRtcCng_CreateDec(&decoder_inst_ptr_) < 0) { - decoder_inst_ptr_ = NULL; - return -1; - } else { - return 0; - } -} - -void ACMCNG::DestructDecoderSafe() { - if (decoder_inst_ptr_ != NULL) { - WebRtcCng_FreeDec(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } - decoder_exist_ = false; - decoder_initialized_ = false; -} - -void ACMCNG::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcCng_FreeEnc(static_cast(ptr_inst)); - } - return; -} - -int16_t ACMCNG::EnableDTX() { return -1; } -int16_t ACMCNG::DisableDTX() { return -1; } - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_cng.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_cng.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_cng.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_cng.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CNG_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CNG_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -// forward declaration -struct WebRtcCngEncInst; -struct WebRtcCngDecInst; - -namespace webrtc { - -namespace acm1 { - -class ACMCNG: public ACMGenericCodec { - public: - explicit ACMCNG(int16_t codec_id); - virtual ~ACMCNG(); - - // for FEC - virtual ACMGenericCodec* CreateInstance(void) OVERRIDE; - - virtual int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) OVERRIDE; - - virtual int16_t InternalInitEncoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - virtual int16_t InternalInitDecoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - protected: - virtual int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type) OVERRIDE; - - virtual int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) OVERRIDE; - - virtual void DestructEncoderSafe() OVERRIDE; - - virtual void DestructDecoderSafe() OVERRIDE; - - virtual int16_t InternalCreateEncoder() OVERRIDE; - - virtual int16_t InternalCreateDecoder() OVERRIDE; - - virtual void InternalDestructEncoderInst(void* ptr_inst) OVERRIDE; - - virtual int16_t EnableDTX() OVERRIDE; - virtual int16_t DisableDTX() OVERRIDE; - - WebRtcCngEncInst* encoder_inst_ptr_; - WebRtcCngDecInst* decoder_inst_ptr_; - uint16_t samp_freq_hz_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CNG_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_codec_database.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_codec_database.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_codec_database.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_codec_database.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,956 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file generates databases with information about all supported audio - * codecs. - */ - -// TODO(tlegrand): Change constant input pointers in all functions to constant -// references, where appropriate. -#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" - -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/system_wrappers/interface/trace.h" - -// Includes needed to create the codecs. -// G.711, PCM mu-law and A-law. -#include "webrtc/modules/audio_coding/codecs/g711/include/g711_interface.h" -#include "webrtc/modules/audio_coding/main/source/acm_pcma.h" -#include "webrtc/modules/audio_coding/main/source/acm_pcmu.h" -// CNG. -#include "webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h" -#include "webrtc/modules/audio_coding/main/source/acm_cng.h" -// NetEQ. -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#ifdef WEBRTC_CODEC_ISAC -#include "webrtc/modules/audio_coding/codecs/isac/main/interface/isac.h" -#endif -#ifdef WEBRTC_CODEC_ISACFX -#include "webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h" -#endif -#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) -#include "webrtc/modules/audio_coding/main/source/acm_isac.h" -#include "webrtc/modules/audio_coding/main/source/acm_isac_macros.h" -#endif -#ifdef WEBRTC_CODEC_PCM16 -#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" -#include "webrtc/modules/audio_coding/main/source/acm_pcm16b.h" -#endif -#ifdef WEBRTC_CODEC_ILBC -#include "webrtc/modules/audio_coding/codecs/ilbc/interface/ilbc.h" -#include "webrtc/modules/audio_coding/main/source/acm_ilbc.h" -#endif -#ifdef WEBRTC_CODEC_AMR -#include "webrtc/modules/audio_coding/codecs/amr/include/amr_interface.h" -#include "webrtc/modules/audio_coding/main/source/acm_amr.h" -#endif -#ifdef WEBRTC_CODEC_AMRWB -#include "webrtc/modules/audio_coding/codecs/amrwb/include/amrwb_interface.h" -#include "webrtc/modules/audio_coding/main/source/acm_amrwb.h" -#endif -#ifdef WEBRTC_CODEC_CELT -#include "webrtc/modules/audio_coding/codecs/celt/include/celt_interface.h" -#include "webrtc/modules/audio_coding/main/source/acm_celt.h" -#endif -#ifdef WEBRTC_CODEC_G722 -#include "webrtc/modules/audio_coding/codecs/g722/include/g722_interface.h" -#include "webrtc/modules/audio_coding/main/source/acm_g722.h" -#endif -#ifdef WEBRTC_CODEC_G722_1 -#include "webrtc/modules/audio_coding/codecs/g7221/include/g7221_interface.h" -#include "webrtc/modules/audio_coding/main/source/acm_g7221.h" -#endif -#ifdef WEBRTC_CODEC_G722_1C -#include "webrtc/modules/audio_coding/codecs/g7221c/include/g7221c_interface.h" -#include "webrtc/modules/audio_coding/main/source/acm_g7221c.h" -#endif -#ifdef WEBRTC_CODEC_G729 -#include "webrtc/modules/audio_coding/codecs/g729/include/g729_interface.h" -#include "webrtc/modules/audio_coding/main/source/acm_g729.h" -#endif -#ifdef WEBRTC_CODEC_G729_1 -#include "webrtc/modules/audio_coding/codecs/g7291/include/g7291_interface.h" -#include "webrtc/modules/audio_coding/main/source/acm_g7291.h" -#endif -#ifdef WEBRTC_CODEC_GSMFR -#include "webrtc/modules/audio_coding/codecs/gsmfr/include/gsmfr_interface.h" -#include "webrtc/modules/audio_coding/main/source/acm_gsmfr.h" -#endif -#ifdef WEBRTC_CODEC_OPUS -#include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" -#include "webrtc/modules/audio_coding/main/source/acm_opus.h" -#endif -#ifdef WEBRTC_CODEC_SPEEX -#include "webrtc/modules/audio_coding/codecs/speex/include/speex_interface.h" -#include "webrtc/modules/audio_coding/main/source/acm_speex.h" -#endif -#ifdef WEBRTC_CODEC_AVT -#include "webrtc/modules/audio_coding/main/source/acm_dtmf_playout.h" -#endif -#ifdef WEBRTC_CODEC_RED -#include "webrtc/modules/audio_coding/main/source/acm_red.h" -#endif - -namespace webrtc { - -namespace acm1 { - -// Not yet used payload-types. -// 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, -// 67, 66, 65 - -const CodecInst ACMCodecDB::database_[] = { -#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) - {103, "ISAC", 16000, kIsacPacSize480, 1, kIsacWbDefaultRate}, -# if (defined(WEBRTC_CODEC_ISAC)) - {104, "ISAC", 32000, kIsacPacSize960, 1, kIsacSwbDefaultRate}, - {105, "ISAC", 48000, kIsacPacSize1440, 1, kIsacSwbDefaultRate}, -# endif -#endif -#ifdef WEBRTC_CODEC_PCM16 - // Mono - {107, "L16", 8000, 80, 1, 128000}, - {108, "L16", 16000, 160, 1, 256000}, - {109, "L16", 32000, 320, 1, 512000}, - // Stereo - {111, "L16", 8000, 80, 2, 128000}, - {112, "L16", 16000, 160, 2, 256000}, - {113, "L16", 32000, 320, 2, 512000}, -#endif - // G.711, PCM mu-law and A-law. - // Mono - {0, "PCMU", 8000, 160, 1, 64000}, - {8, "PCMA", 8000, 160, 1, 64000}, - // Stereo - {110, "PCMU", 8000, 160, 2, 64000}, - {118, "PCMA", 8000, 160, 2, 64000}, -#ifdef WEBRTC_CODEC_ILBC - {102, "ILBC", 8000, 240, 1, 13300}, -#endif -#ifdef WEBRTC_CODEC_AMR - {114, "AMR", 8000, 160, 1, 12200}, -#endif -#ifdef WEBRTC_CODEC_AMRWB - {115, "AMR-WB", 16000, 320, 1, 20000}, -#endif -#ifdef WEBRTC_CODEC_CELT - // Mono - {116, "CELT", 32000, 640, 1, 64000}, - // Stereo - {117, "CELT", 32000, 640, 2, 64000}, -#endif -#ifdef WEBRTC_CODEC_G722 - // Mono - {9, "G722", 16000, 320, 1, 64000}, - // Stereo - {119, "G722", 16000, 320, 2, 64000}, -#endif -#ifdef WEBRTC_CODEC_G722_1 - {92, "G7221", 16000, 320, 1, 32000}, - {91, "G7221", 16000, 320, 1, 24000}, - {90, "G7221", 16000, 320, 1, 16000}, -#endif -#ifdef WEBRTC_CODEC_G722_1C - {89, "G7221", 32000, 640, 1, 48000}, - {88, "G7221", 32000, 640, 1, 32000}, - {87, "G7221", 32000, 640, 1, 24000}, -#endif -#ifdef WEBRTC_CODEC_G729 - {18, "G729", 8000, 240, 1, 8000}, -#endif -#ifdef WEBRTC_CODEC_G729_1 - {86, "G7291", 16000, 320, 1, 32000}, -#endif -#ifdef WEBRTC_CODEC_GSMFR - {3, "GSM", 8000, 160, 1, 13200}, -#endif -#ifdef WEBRTC_CODEC_OPUS - // Opus internally supports 48, 24, 16, 12, 8 kHz. - // Mono and stereo. - {120, "opus", 48000, 960, 2, 64000}, -#endif -#ifdef WEBRTC_CODEC_SPEEX - {85, "speex", 8000, 160, 1, 11000}, - {84, "speex", 16000, 320, 1, 22000}, -#endif - // Comfort noise for four different sampling frequencies. - {13, "CN", 8000, 240, 1, 0}, - {98, "CN", 16000, 480, 1, 0}, - {99, "CN", 32000, 960, 1, 0}, -#ifdef ENABLE_48000_HZ - {100, "CN", 48000, 1440, 1, 0}, -#endif -#ifdef WEBRTC_CODEC_AVT - {106, "telephone-event", 8000, 240, 1, 0}, -#endif -#ifdef WEBRTC_CODEC_RED - {127, "red", 8000, 0, 1, 0}, -#endif - // To prevent compile errors due to trailing commas. - {-1, "Null", -1, -1, -1, -1} -}; - -// Create database with all codec settings at compile time. -// Each entry needs the following parameters in the given order: -// Number of allowed packet sizes, a vector with the allowed packet sizes, -// Basic block samples, max number of channels that are supported. -const ACMCodecDB::CodecSettings ACMCodecDB::codec_settings_[] = { -#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) - {2, {kIsacPacSize480, kIsacPacSize960}, 0, 1}, -# if (defined(WEBRTC_CODEC_ISAC)) - {1, {kIsacPacSize960}, 0, 1}, - {1, {kIsacPacSize1440}, 0, 1}, -# endif -#endif -#ifdef WEBRTC_CODEC_PCM16 - // Mono - {4, {80, 160, 240, 320}, 0, 2}, - {4, {160, 320, 480, 640}, 0, 2}, - {2, {320, 640}, 0, 2}, - // Stereo - {4, {80, 160, 240, 320}, 0, 2}, - {4, {160, 320, 480, 640}, 0, 2}, - {2, {320, 640}, 0, 2}, -#endif - // G.711, PCM mu-law and A-law. - // Mono - {6, {80, 160, 240, 320, 400, 480}, 0, 2}, - {6, {80, 160, 240, 320, 400, 480}, 0, 2}, - // Stereo - {6, {80, 160, 240, 320, 400, 480}, 0, 2}, - {6, {80, 160, 240, 320, 400, 480}, 0, 2}, -#ifdef WEBRTC_CODEC_ILBC - {4, {160, 240, 320, 480}, 0, 1}, -#endif -#ifdef WEBRTC_CODEC_AMR - {3, {160, 320, 480}, 0, 1}, -#endif -#ifdef WEBRTC_CODEC_AMRWB - {3, {320, 640, 960}, 0, 1}, -#endif -#ifdef WEBRTC_CODEC_CELT - // Mono - {1, {640}, 0, 2}, - // Stereo - {1, {640}, 0, 2}, -#endif -#ifdef WEBRTC_CODEC_G722 - // Mono - {6, {160, 320, 480, 640, 800, 960}, 0, 2}, - // Stereo - {6, {160, 320, 480, 640, 800, 960}, 0, 2}, -#endif -#ifdef WEBRTC_CODEC_G722_1 - {1, {320}, 320, 1}, - {1, {320}, 320, 1}, - {1, {320}, 320, 1}, -#endif -#ifdef WEBRTC_CODEC_G722_1C - {1, {640}, 640, 1}, - {1, {640}, 640, 1}, - {1, {640}, 640, 1}, -#endif -#ifdef WEBRTC_CODEC_G729 - {6, {80, 160, 240, 320, 400, 480}, 0, 1}, -#endif -#ifdef WEBRTC_CODEC_G729_1 - {3, {320, 640, 960}, 0, 1}, -#endif -#ifdef WEBRTC_CODEC_GSMFR - {3, {160, 320, 480}, 160, 1}, -#endif -#ifdef WEBRTC_CODEC_OPUS - // Opus supports frames shorter than 10ms, - // but it doesn't help us to use them. - // Mono and stereo. - {4, {480, 960, 1920, 2880}, 0, 2}, -#endif -#ifdef WEBRTC_CODEC_SPEEX - {3, {160, 320, 480}, 0, 1}, - {3, {320, 640, 960}, 0, 1}, -#endif - // Comfort noise for three different sampling frequencies. - {1, {240}, 240, 1}, - {1, {480}, 480, 1}, - {1, {960}, 960, 1}, -#ifdef ENABLE_48000_HZ - {1, {1440}, 1440, 1}, -#endif -#ifdef WEBRTC_CODEC_AVT - {1, {240}, 240, 1}, -#endif -#ifdef WEBRTC_CODEC_RED - {1, {0}, 0, 1}, -#endif - // To prevent compile errors due to trailing commas. - {-1, {-1}, -1, -1} -}; - -// Create a database of all NetEQ decoders at compile time. -const WebRtcNetEQDecoder ACMCodecDB::neteq_decoders_[] = { -#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) - kDecoderISAC, -# if (defined(WEBRTC_CODEC_ISAC)) - kDecoderISACswb, - kDecoderISACfb, -# endif -#endif -#ifdef WEBRTC_CODEC_PCM16 - // Mono - kDecoderPCM16B, - kDecoderPCM16Bwb, - kDecoderPCM16Bswb32kHz, - // Stereo - kDecoderPCM16B_2ch, - kDecoderPCM16Bwb_2ch, - kDecoderPCM16Bswb32kHz_2ch, -#endif - // G.711, PCM mu-las and A-law. - // Mono - kDecoderPCMu, - kDecoderPCMa, - // Stereo - kDecoderPCMu_2ch, - kDecoderPCMa_2ch, -#ifdef WEBRTC_CODEC_ILBC - kDecoderILBC, -#endif -#ifdef WEBRTC_CODEC_AMR - kDecoderAMR, -#endif -#ifdef WEBRTC_CODEC_AMRWB - kDecoderAMRWB, -#endif -#ifdef WEBRTC_CODEC_CELT - // Mono - kDecoderCELT_32, - // Stereo - kDecoderCELT_32_2ch, -#endif -#ifdef WEBRTC_CODEC_G722 - // Mono - kDecoderG722, - // Stereo - kDecoderG722_2ch, -#endif -#ifdef WEBRTC_CODEC_G722_1 - kDecoderG722_1_32, - kDecoderG722_1_24, - kDecoderG722_1_16, -#endif -#ifdef WEBRTC_CODEC_G722_1C - kDecoderG722_1C_48, - kDecoderG722_1C_32, - kDecoderG722_1C_24, -#endif -#ifdef WEBRTC_CODEC_G729 - kDecoderG729, -#endif -#ifdef WEBRTC_CODEC_G729_1 - kDecoderG729_1, -#endif -#ifdef WEBRTC_CODEC_GSMFR - kDecoderGSMFR, -#endif -#ifdef WEBRTC_CODEC_OPUS - // Mono and stereo. - kDecoderOpus, -#endif -#ifdef WEBRTC_CODEC_SPEEX - kDecoderSPEEX_8, - kDecoderSPEEX_16, -#endif - // Comfort noise for three different sampling frequencies. - kDecoderCNG, - kDecoderCNG, - kDecoderCNG, -#ifdef ENABLE_48000_HZ - kDecoderCNG, -#endif -#ifdef WEBRTC_CODEC_AVT - kDecoderAVT, -#endif -#ifdef WEBRTC_CODEC_RED - kDecoderRED, -#endif - kDecoderReservedEnd -}; - -// Get codec information from database. -// TODO(tlegrand): replace memcpy with a pointer to the data base memory. -int ACMCodecDB::Codec(int codec_id, CodecInst* codec_inst) { - // Error check to see that codec_id is not out of bounds. - if ((codec_id < 0) || (codec_id >= kNumCodecs)) { - return -1; - } - - // Copy database information for the codec to the output. - memcpy(codec_inst, &database_[codec_id], sizeof(CodecInst)); - - return 0; -} - -// Enumerator for error codes when asking for codec database id. -enum { - kInvalidCodec = -10, - kInvalidPayloadtype = -30, - kInvalidPacketSize = -40, - kInvalidRate = -50 -}; - -// Gets the codec id number from the database. If there is some mismatch in -// the codec settings, the function will return an error code. -// NOTE! The first mismatch found will generate the return value. -int ACMCodecDB::CodecNumber(const CodecInst* codec_inst, int* mirror_id) { - // Look for a matching codec in the database. - int codec_id = CodecId(codec_inst); - - // Checks if we found a matching codec. - if (codec_id == -1) { - return kInvalidCodec; - } - - // Checks the validity of payload type - if (!ValidPayloadType(codec_inst->pltype)) { - return kInvalidPayloadtype; - } - - // Comfort Noise is special case, packet-size & rate is not checked. - if (STR_CASE_CMP(database_[codec_id].plname, "CN") == 0) { - *mirror_id = codec_id; - return codec_id; - } - - // RED is special case, packet-size & rate is not checked. - if (STR_CASE_CMP(database_[codec_id].plname, "red") == 0) { - *mirror_id = codec_id; - return codec_id; - } - - // Checks the validity of packet size. - if (codec_settings_[codec_id].num_packet_sizes > 0) { - bool packet_size_ok = false; - int i; - int packet_size_samples; - for (i = 0; i < codec_settings_[codec_id].num_packet_sizes; i++) { - packet_size_samples = - codec_settings_[codec_id].packet_sizes_samples[i]; - if (codec_inst->pacsize == packet_size_samples) { - packet_size_ok = true; - break; - } - } - - if (!packet_size_ok) { - return kInvalidPacketSize; - } - } - - if (codec_inst->pacsize < 1) { - return kInvalidPacketSize; - } - - // Check the validity of rate. Codecs with multiple rates have their own - // function for this. - *mirror_id = codec_id; - if (STR_CASE_CMP("isac", codec_inst->plname) == 0) { - if (IsISACRateValid(codec_inst->rate)) { - // Set mirrorID to iSAC WB which is only created once to be used both for - // iSAC WB and SWB, because they need to share struct. - *mirror_id = kISAC; - return codec_id; - } else { - return kInvalidRate; - } - } else if (STR_CASE_CMP("ilbc", codec_inst->plname) == 0) { - return IsILBCRateValid(codec_inst->rate, codec_inst->pacsize) - ? codec_id : kInvalidRate; - } else if (STR_CASE_CMP("amr", codec_inst->plname) == 0) { - return IsAMRRateValid(codec_inst->rate) - ? codec_id : kInvalidRate; - } else if (STR_CASE_CMP("amr-wb", codec_inst->plname) == 0) { - return IsAMRwbRateValid(codec_inst->rate) - ? codec_id : kInvalidRate; - } else if (STR_CASE_CMP("g7291", codec_inst->plname) == 0) { - return IsG7291RateValid(codec_inst->rate) - ? codec_id : kInvalidRate; - } else if (STR_CASE_CMP("opus", codec_inst->plname) == 0) { - return IsOpusRateValid(codec_inst->rate) - ? codec_id : kInvalidRate; - } else if (STR_CASE_CMP("speex", codec_inst->plname) == 0) { - return IsSpeexRateValid(codec_inst->rate) - ? codec_id : kInvalidRate; - } else if (STR_CASE_CMP("celt", codec_inst->plname) == 0) { - return IsCeltRateValid(codec_inst->rate) - ? codec_id : kInvalidRate; - } - - return IsRateValid(codec_id, codec_inst->rate) ? - codec_id : kInvalidRate; -} - -// Looks for a matching payload name, frequency, and channels in the -// codec list. Need to check all three since some codecs have several codec -// entries with different frequencies and/or channels. -// Does not check other codec settings, such as payload type and packet size. -// Returns the id of the codec, or -1 if no match is found. -int ACMCodecDB::CodecId(const CodecInst* codec_inst) { - return (CodecId(codec_inst->plname, codec_inst->plfreq, - codec_inst->channels)); -} - -int ACMCodecDB::CodecId(const char* payload_name, int frequency, int channels) { - for (int id = 0; id < kNumCodecs; id++) { - bool name_match = false; - bool frequency_match = false; - bool channels_match = false; - - // Payload name, sampling frequency and number of channels need to match. - // NOTE! If |frequency| is -1, the frequency is not applicable, and is - // always treated as true, like for RED. - name_match = (STR_CASE_CMP(database_[id].plname, payload_name) == 0); - frequency_match = (frequency == database_[id].plfreq) || (frequency == -1); - // The number of channels must match for all codecs but Opus. - if (STR_CASE_CMP(payload_name, "opus") != 0) { - channels_match = (channels == database_[id].channels); - } else { - // For opus we just check that number of channels is valid. - channels_match = (channels == 1 || channels == 2); - } - - if (name_match && frequency_match && channels_match) { - // We have found a matching codec in the list. - return id; - } - } - - // We didn't find a matching codec. - return -1; -} -// Gets codec id number, and mirror id, from database for the receiver. -int ACMCodecDB::ReceiverCodecNumber(const CodecInst* codec_inst, - int* mirror_id) { - // Look for a matching codec in the database. - int codec_id = CodecId(codec_inst); - - // Set |mirror_id| to |codec_id|, except for iSAC. In case of iSAC we always - // set |mirror_id| to iSAC WB (kISAC) which is only created once to be used - // both for iSAC WB and SWB, because they need to share struct. - if (STR_CASE_CMP(codec_inst->plname, "ISAC") != 0) { - *mirror_id = codec_id; - } else { - *mirror_id = kISAC; - } - - return codec_id; -} - -// Returns the codec sampling frequency for codec with id = "codec_id" in -// database. -int ACMCodecDB::CodecFreq(int codec_id) { - // Error check to see that codec_id is not out of bounds. - if (codec_id < 0 || codec_id >= kNumCodecs) { - return -1; - } - - return database_[codec_id].plfreq; -} - -// Returns the codec's basic coding block size in samples. -int ACMCodecDB::BasicCodingBlock(int codec_id) { - // Error check to see that codec_id is not out of bounds. - if (codec_id < 0 || codec_id >= kNumCodecs) { - return -1; - } - - return codec_settings_[codec_id].basic_block_samples; -} - -// Returns the NetEQ decoder database. -const WebRtcNetEQDecoder* ACMCodecDB::NetEQDecoders() { - return neteq_decoders_; -} - -// Gets mirror id. The Id is used for codecs sharing struct for settings that -// need different payload types. -int ACMCodecDB::MirrorID(int codec_id) { - if (STR_CASE_CMP(database_[codec_id].plname, "isac") == 0) { - return kISAC; - } else { - return codec_id; - } -} - -// Creates memory/instance for storing codec state. -ACMGenericCodec* ACMCodecDB::CreateCodecInstance(const CodecInst* codec_inst) { - // All we have support for right now. - if (!STR_CASE_CMP(codec_inst->plname, "ISAC")) { -#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) - return new ACMISAC(kISAC); -#endif - } else if (!STR_CASE_CMP(codec_inst->plname, "PCMU")) { - if (codec_inst->channels == 1) { - return new ACMPCMU(kPCMU); - } else { - return new ACMPCMU(kPCMU_2ch); - } - } else if (!STR_CASE_CMP(codec_inst->plname, "PCMA")) { - if (codec_inst->channels == 1) { - return new ACMPCMA(kPCMA); - } else { - return new ACMPCMA(kPCMA_2ch); - } - } else if (!STR_CASE_CMP(codec_inst->plname, "ILBC")) { -#ifdef WEBRTC_CODEC_ILBC - return new ACMILBC(kILBC); -#endif - } else if (!STR_CASE_CMP(codec_inst->plname, "AMR")) { -#ifdef WEBRTC_CODEC_AMR - return new ACMAMR(kGSMAMR); -#endif - } else if (!STR_CASE_CMP(codec_inst->plname, "AMR-WB")) { -#ifdef WEBRTC_CODEC_AMRWB - return new ACMAMRwb(kGSMAMRWB); -#endif - } else if (!STR_CASE_CMP(codec_inst->plname, "CELT")) { -#ifdef WEBRTC_CODEC_CELT - if (codec_inst->channels == 1) { - return new ACMCELT(kCELT32); - } else { - return new ACMCELT(kCELT32_2ch); - } -#endif - } else if (!STR_CASE_CMP(codec_inst->plname, "G722")) { -#ifdef WEBRTC_CODEC_G722 - if (codec_inst->channels == 1) { - return new ACMG722(kG722); - } else { - return new ACMG722(kG722_2ch); - } -#endif - } else if (!STR_CASE_CMP(codec_inst->plname, "G7221")) { - switch (codec_inst->plfreq) { - case 16000: { -#ifdef WEBRTC_CODEC_G722_1 - int codec_id; - switch (codec_inst->rate) { - case 16000 : { - codec_id = kG722_1_16; - break; - } - case 24000 : { - codec_id = kG722_1_24; - break; - } - case 32000 : { - codec_id = kG722_1_32; - break; - } - default: { - return NULL; - } - } - return new ACMG722_1(codec_id); -#endif - } - case 32000: { -#ifdef WEBRTC_CODEC_G722_1C - int codec_id; - switch (codec_inst->rate) { - case 24000 : { - codec_id = kG722_1C_24; - break; - } - case 32000 : { - codec_id = kG722_1C_32; - break; - } - case 48000 : { - codec_id = kG722_1C_48; - break; - } - default: { - return NULL; - } - } - return new ACMG722_1C(codec_id); -#endif - } - } - } else if (!STR_CASE_CMP(codec_inst->plname, "CN")) { - // For CN we need to check sampling frequency to know what codec to create. - int codec_id; - switch (codec_inst->plfreq) { - case 8000: { - codec_id = kCNNB; - break; - } - case 16000: { - codec_id = kCNWB; - break; - } - case 32000: { - codec_id = kCNSWB; - break; - } -#ifdef ENABLE_48000_HZ - case 48000: { - codec_id = kCNFB; - break; - } -#endif - default: { - return NULL; - } - } - return new ACMCNG(codec_id); - } else if (!STR_CASE_CMP(codec_inst->plname, "G729")) { -#ifdef WEBRTC_CODEC_G729 - return new ACMG729(kG729); -#endif - } else if (!STR_CASE_CMP(codec_inst->plname, "G7291")) { -#ifdef WEBRTC_CODEC_G729_1 - return new ACMG729_1(kG729_1); -#endif - } else if (!STR_CASE_CMP(codec_inst->plname, "opus")) { -#ifdef WEBRTC_CODEC_OPUS - return new ACMOpus(kOpus); -#endif - } else if (!STR_CASE_CMP(codec_inst->plname, "speex")) { -#ifdef WEBRTC_CODEC_SPEEX - int codec_id; - switch (codec_inst->plfreq) { - case 8000: { - codec_id = kSPEEX8; - break; - } - case 16000: { - codec_id = kSPEEX16; - break; - } - default: { - return NULL; - } - } - return new ACMSPEEX(codec_id); -#endif - } else if (!STR_CASE_CMP(codec_inst->plname, "CN")) { - // For CN we need to check sampling frequency to know what codec to create. - int codec_id; - switch (codec_inst->plfreq) { - case 8000: { - codec_id = kCNNB; - break; - } - case 16000: { - codec_id = kCNWB; - break; - } - case 32000: { - codec_id = kCNSWB; - break; - } -#ifdef ENABLE_48000_HZ - case 48000: { - codec_id = kCNFB; - break; - } -#endif - default: { - return NULL; - } - } - return new ACMCNG(codec_id); - } else if (!STR_CASE_CMP(codec_inst->plname, "L16")) { -#ifdef WEBRTC_CODEC_PCM16 - // For L16 we need to check sampling frequency to know what codec to create. - int codec_id; - if (codec_inst->channels == 1) { - switch (codec_inst->plfreq) { - case 8000: { - codec_id = kPCM16B; - break; - } - case 16000: { - codec_id = kPCM16Bwb; - break; - } - case 32000: { - codec_id = kPCM16Bswb32kHz; - break; - } - default: { - return NULL; - } - } - } else { - switch (codec_inst->plfreq) { - case 8000: { - codec_id = kPCM16B_2ch; - break; - } - case 16000: { - codec_id = kPCM16Bwb_2ch; - break; - } - case 32000: { - codec_id = kPCM16Bswb32kHz_2ch; - break; - } - default: { - return NULL; - } - } - } - return new ACMPCM16B(codec_id); -#endif - } else if (!STR_CASE_CMP(codec_inst->plname, "telephone-event")) { -#ifdef WEBRTC_CODEC_AVT - return new ACMDTMFPlayout(kAVT); -#endif - } else if (!STR_CASE_CMP(codec_inst->plname, "red")) { -#ifdef WEBRTC_CODEC_RED - return new ACMRED(kRED); -#endif - } - return NULL; -} - -// Checks if the bitrate is valid for the codec. -bool ACMCodecDB::IsRateValid(int codec_id, int rate) { - if (database_[codec_id].rate == rate) { - return true; - } else { - return false; - } -} - -// Checks if the bitrate is valid for iSAC. -bool ACMCodecDB::IsISACRateValid(int rate) { - if ((rate == -1) || ((rate <= 56000) && (rate >= 10000))) { - return true; - } else { - return false; - } -} - -// Checks if the bitrate is valid for iLBC. -bool ACMCodecDB::IsILBCRateValid(int rate, int frame_size_samples) { - if (((frame_size_samples == 240) || (frame_size_samples == 480)) && - (rate == 13300)) { - return true; - } else if (((frame_size_samples == 160) || (frame_size_samples == 320)) && - (rate == 15200)) { - return true; - } else { - return false; - } -} - -// Check if the bitrate is valid for the GSM-AMR. -bool ACMCodecDB::IsAMRRateValid(int rate) { - switch (rate) { - case 4750: - case 5150: - case 5900: - case 6700: - case 7400: - case 7950: - case 10200: - case 12200: { - return true; - } - default: { - return false; - } - } -} - -// Check if the bitrate is valid for GSM-AMR-WB. -bool ACMCodecDB::IsAMRwbRateValid(int rate) { - switch (rate) { - case 7000: - case 9000: - case 12000: - case 14000: - case 16000: - case 18000: - case 20000: - case 23000: - case 24000: { - return true; - } - default: { - return false; - } - } -} - -// Check if the bitrate is valid for G.729.1. -bool ACMCodecDB::IsG7291RateValid(int rate) { - switch (rate) { - case 8000: - case 12000: - case 14000: - case 16000: - case 18000: - case 20000: - case 22000: - case 24000: - case 26000: - case 28000: - case 30000: - case 32000: { - return true; - } - default: { - return false; - } - } -} - -// Checks if the bitrate is valid for Speex. -bool ACMCodecDB::IsSpeexRateValid(int rate) { - if (rate > 2000) { - return true; - } else { - return false; - } -} - -// Checks if the bitrate is valid for Opus. -bool ACMCodecDB::IsOpusRateValid(int rate) { - if ((rate < 6000) || (rate > 510000)) { - return false; - } - return true; -} - -// Checks if the bitrate is valid for Celt. -bool ACMCodecDB::IsCeltRateValid(int rate) { - if ((rate >= 48000) && (rate <= 128000)) { - return true; - } else { - return false; - } -} - -// Checks if the payload type is in the valid range. -bool ACMCodecDB::ValidPayloadType(int payload_type) { - if ((payload_type < 0) || (payload_type > 127)) { - return false; - } - return true; -} - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_codec_database.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_codec_database.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_codec_database.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_codec_database.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,336 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file generates databases with information about all supported audio - * codecs. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CODEC_DATABASE_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CODEC_DATABASE_H_ - -#include "webrtc/common_types.h" -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" - -namespace webrtc { - -namespace acm1 { - -// TODO(tlegrand): replace class ACMCodecDB with a namespace. -class ACMCodecDB { - public: - // Enum with array indexes for the supported codecs. NOTE! The order MUST - // be the same as when creating the database in acm_codec_database.cc. - enum { - kNone = -1 -#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) - , kISAC -# if (defined(WEBRTC_CODEC_ISAC)) - , kISACSWB - , kISACFB -# endif -#endif -#ifdef WEBRTC_CODEC_PCM16 - // Mono - , kPCM16B - , kPCM16Bwb - , kPCM16Bswb32kHz - // Stereo - , kPCM16B_2ch - , kPCM16Bwb_2ch - , kPCM16Bswb32kHz_2ch -#endif - // Mono - , kPCMU - , kPCMA - // Stereo - , kPCMU_2ch - , kPCMA_2ch -#ifdef WEBRTC_CODEC_ILBC - , kILBC -#endif -#ifdef WEBRTC_CODEC_AMR - , kGSMAMR -#endif -#ifdef WEBRTC_CODEC_AMRWB - , kGSMAMRWB -#endif -#ifdef WEBRTC_CODEC_CELT - // Mono - , kCELT32 - // Stereo - , kCELT32_2ch -#endif -#ifdef WEBRTC_CODEC_G722 - // Mono - , kG722 - // Stereo - , kG722_2ch -#endif -#ifdef WEBRTC_CODEC_G722_1 - , kG722_1_32 - , kG722_1_24 - , kG722_1_16 -#endif -#ifdef WEBRTC_CODEC_G722_1C - , kG722_1C_48 - , kG722_1C_32 - , kG722_1C_24 -#endif -#ifdef WEBRTC_CODEC_G729 - , kG729 -#endif -#ifdef WEBRTC_CODEC_G729_1 - , kG729_1 -#endif -#ifdef WEBRTC_CODEC_GSMFR - , kGSMFR -#endif -#ifdef WEBRTC_CODEC_OPUS - // Mono and stereo - , kOpus -#endif -#ifdef WEBRTC_CODEC_SPEEX - , kSPEEX8 - , kSPEEX16 -#endif - , kCNNB - , kCNWB - , kCNSWB -#ifdef ENABLE_48000_HZ - , kCNFB -#endif -#ifdef WEBRTC_CODEC_AVT - , kAVT -#endif -#ifdef WEBRTC_CODEC_RED - , kRED -#endif - , kNumCodecs - }; - - // Set unsupported codecs to -1 -#ifndef WEBRTC_CODEC_ISAC - enum {kISACSWB = -1}; - enum {kISACFB = -1}; -# ifndef WEBRTC_CODEC_ISACFX - enum {kISAC = -1}; -# endif -#endif -#ifndef WEBRTC_CODEC_PCM16 - // Mono - enum {kPCM16B = -1}; - enum {kPCM16Bwb = -1}; - enum {kPCM16Bswb32kHz = -1}; - // Stereo - enum {kPCM16B_2ch = -1}; - enum {kPCM16Bwb_2ch = -1}; - enum {kPCM16Bswb32kHz_2ch = -1}; -#endif - // 48 kHz not supported, always set to -1. - enum {kPCM16Bswb48kHz = -1}; -#ifndef WEBRTC_CODEC_ILBC - enum {kILBC = -1}; -#endif -#ifndef WEBRTC_CODEC_AMR - enum {kGSMAMR = -1}; -#endif -#ifndef WEBRTC_CODEC_AMRWB - enum {kGSMAMRWB = -1}; -#endif -#ifndef WEBRTC_CODEC_CELT - // Mono - enum {kCELT32 = -1}; - // Stereo - enum {kCELT32_2ch = -1}; -#endif -#ifndef WEBRTC_CODEC_G722 - // Mono - enum {kG722 = -1}; - // Stereo - enum {kG722_2ch = -1}; -#endif -#ifndef WEBRTC_CODEC_G722_1 - enum {kG722_1_32 = -1}; - enum {kG722_1_24 = -1}; - enum {kG722_1_16 = -1}; -#endif -#ifndef WEBRTC_CODEC_G722_1C - enum {kG722_1C_48 = -1}; - enum {kG722_1C_32 = -1}; - enum {kG722_1C_24 = -1}; -#endif -#ifndef WEBRTC_CODEC_G729 - enum {kG729 = -1}; -#endif -#ifndef WEBRTC_CODEC_G729_1 - enum {kG729_1 = -1}; -#endif -#ifndef WEBRTC_CODEC_GSMFR - enum {kGSMFR = -1}; -#endif -#ifndef WEBRTC_CODEC_SPEEX - enum {kSPEEX8 = -1}; - enum {kSPEEX16 = -1}; -#endif -#ifndef WEBRTC_CODEC_OPUS - // Mono and stereo - enum {kOpus = -1}; -#endif -#ifndef WEBRTC_CODEC_AVT - enum {kAVT = -1}; -#endif -#ifndef WEBRTC_CODEC_RED - enum {kRED = -1}; -#endif - - // kMaxNumCodecs - Maximum number of codecs that can be activated in one - // build. - // kMaxNumPacketSize - Maximum number of allowed packet sizes for one codec. - // These might need to be increased if adding a new codec to the database - static const int kMaxNumCodecs = 50; - static const int kMaxNumPacketSize = 6; - - // Codec specific settings - // - // num_packet_sizes - number of allowed packet sizes. - // packet_sizes_samples - list of the allowed packet sizes. - // basic_block_samples - assigned a value different from 0 if the codec - // requires to be fed with a specific number of samples - // that can be different from packet size. - // channel_support - number of channels supported to encode; - // 1 = mono, 2 = stereo, etc. - struct CodecSettings { - int num_packet_sizes; - int packet_sizes_samples[kMaxNumPacketSize]; - int basic_block_samples; - int channel_support; - }; - - // Gets codec information from database at the position in database given by - // [codec_id]. - // Input: - // [codec_id] - number that specifies at what position in the database to - // get the information. - // Output: - // [codec_inst] - filled with information about the codec. - // Return: - // 0 if successful, otherwise -1. - static int Codec(int codec_id, CodecInst* codec_inst); - - // Returns codec id and mirror id from database, given the information - // received in the input [codec_inst]. Mirror id is a number that tells - // where to find the codec's memory (instance). The number is either the - // same as codec id (most common), or a number pointing at a different - // entry in the database, if the codec has several entries with different - // payload types. This is used for codecs that must share one struct even if - // the payload type differs. - // One example is the codec iSAC which has the same struct for both 16 and - // 32 khz, but they have different entries in the database. Let's say the - // function is called with iSAC 32kHz. The function will return 1 as that is - // the entry in the data base, and [mirror_id] = 0, as that is the entry for - // iSAC 16 kHz, which holds the shared memory. - // Input: - // [codec_inst] - Information about the codec for which we require the - // database id. - // Output: - // [mirror_id] - mirror id, which most often is the same as the return - // value, see above. - // Return: - // codec id if successful, otherwise < 0. - static int CodecNumber(const CodecInst* codec_inst, int* mirror_id); - static int CodecId(const CodecInst* codec_inst); - static int CodecId(const char* payload_name, int frequency, int channels); - static int ReceiverCodecNumber(const CodecInst* codec_inst, int* mirror_id); - - // Returns the codec sampling frequency for codec with id = "codec_id" in - // database. - // TODO(tlegrand): Check if function is needed, or if we can change - // to access database directly. - // Input: - // [codec_id] - number that specifies at what position in the database to - // get the information. - // Return: - // codec sampling frequency if successful, otherwise -1. - static int CodecFreq(int codec_id); - - // Return the codec's basic coding block size in samples. - // TODO(tlegrand): Check if function is needed, or if we can change - // to access database directly. - // Input: - // [codec_id] - number that specifies at what position in the database to - // get the information. - // Return: - // codec basic block size if successful, otherwise -1. - static int BasicCodingBlock(int codec_id); - - // Returns the NetEQ decoder database. - static const WebRtcNetEQDecoder* NetEQDecoders(); - - // Returns mirror id, which is a number that tells where to find the codec's - // memory (instance). It is either the same as codec id (most common), or a - // number pointing at a different entry in the database, if the codec have - // several entries with different payload types. This is used for codecs that - // must share struct even if the payload type differs. - // TODO(tlegrand): Check if function is needed, or if we can change - // to access database directly. - // Input: - // [codec_id] - number that specifies codec's position in the database. - // Return: - // Mirror id on success, otherwise -1. - static int MirrorID(int codec_id); - - // Create memory/instance for storing codec state. - // Input: - // [codec_inst] - information about codec. Only name of codec, "plname", is - // used in this function. - static ACMGenericCodec* CreateCodecInstance(const CodecInst* codec_inst); - - // Checks if the bitrate is valid for the codec. - // Input: - // [codec_id] - number that specifies codec's position in the database. - // [rate] - bitrate to check. - // [frame_size_samples] - (used for iLBC) specifies which frame size to go - // with the rate. - static bool IsRateValid(int codec_id, int rate); - static bool IsISACRateValid(int rate); - static bool IsILBCRateValid(int rate, int frame_size_samples); - static bool IsAMRRateValid(int rate); - static bool IsAMRwbRateValid(int rate); - static bool IsG7291RateValid(int rate); - static bool IsSpeexRateValid(int rate); - static bool IsOpusRateValid(int rate); - static bool IsCeltRateValid(int rate); - - // Check if the payload type is valid, meaning that it is in the valid range - // of 0 to 127. - // Input: - // [payload_type] - payload type. - static bool ValidPayloadType(int payload_type); - - // Databases with information about the supported codecs - // database_ - stored information about all codecs: payload type, name, - // sampling frequency, packet size in samples, default channel - // support, and default rate. - // codec_settings_ - stored codec settings: number of allowed packet sizes, - // a vector with the allowed packet sizes, basic block - // samples, and max number of channels that are supported. - // neteq_decoders_ - list of supported decoders in NetEQ. - static const CodecInst database_[kMaxNumCodecs]; - static const CodecSettings codec_settings_[kMaxNumCodecs]; - static const WebRtcNetEQDecoder neteq_decoders_[kMaxNumCodecs]; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_CODEC_DATABASE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_dtmf_detection.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_dtmf_detection.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_dtmf_detection.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_dtmf_detection.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_dtmf_detection.h" - -#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" - -namespace webrtc { - -namespace acm1 { - -ACMDTMFDetection::ACMDTMFDetection() {} - -ACMDTMFDetection::~ACMDTMFDetection() {} - -int16_t ACMDTMFDetection::Enable(ACMCountries /* cpt */) { - return -1; -} - -int16_t ACMDTMFDetection::Disable() { - return -1; -} - -int16_t ACMDTMFDetection::Detect( - const int16_t* /* in_audio_buff */, - const uint16_t /* in_buff_len_word16 */, - const int32_t /* in_freq_hz */, - bool& /* tone_detected */, - int16_t& /* tone */) { - return -1; -} - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_dtmf_detection.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_dtmf_detection.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_dtmf_detection.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_dtmf_detection.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_DTMF_DETECTION_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_DTMF_DETECTION_H_ - -#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" -#include "webrtc/modules/audio_coding/main/source/acm_resampler.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -namespace acm1 { - -class ACMDTMFDetection { - public: - ACMDTMFDetection(); - ~ACMDTMFDetection(); - int16_t Enable(ACMCountries cpt = ACMDisableCountryDetection); - int16_t Disable(); - int16_t Detect(const int16_t* in_audio_buff, - const uint16_t in_buff_len_word16, - const int32_t in_freq_hz, - bool& tone_detected, - int16_t& tone); - - private: - ACMResampler resampler_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_DTMF_DETECTION_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_dtmf_playout.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_dtmf_playout.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_dtmf_playout.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_dtmf_playout.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_dtmf_playout.h" - -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/trace.h" - -namespace webrtc { - -namespace acm1 { - -#ifndef WEBRTC_CODEC_AVT - -ACMDTMFPlayout::ACMDTMFPlayout( - int16_t /* codec_id */) { - return; -} - -ACMDTMFPlayout::~ACMDTMFPlayout() { - return; -} - -int16_t ACMDTMFPlayout::InternalEncode( - uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - return -1; -} - -int16_t ACMDTMFPlayout::DecodeSafe( - uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return -1; -} - -int16_t ACMDTMFPlayout::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int16_t ACMDTMFPlayout::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int32_t ACMDTMFPlayout::CodecDef(WebRtcNetEQ_CodecDef& /* codec_def */, - const CodecInst& /* codec_inst */) { - return -1; -} - -ACMGenericCodec* ACMDTMFPlayout::CreateInstance(void) { - return NULL; -} - -int16_t ACMDTMFPlayout::InternalCreateEncoder() { - return -1; -} - -int16_t ACMDTMFPlayout::InternalCreateDecoder() { - return -1; -} - -void ACMDTMFPlayout::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - -void ACMDTMFPlayout::DestructEncoderSafe() { - return; -} - -void ACMDTMFPlayout::DestructDecoderSafe() { - return; -} - -#else //===================== Actual Implementation ======================= - -ACMDTMFPlayout::ACMDTMFPlayout(int16_t codec_id) { - codec_id_ = codec_id; -} - -ACMDTMFPlayout::~ACMDTMFPlayout() { - return; -} - -int16_t ACMDTMFPlayout::InternalEncode( - uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - return 0; -} - -int16_t ACMDTMFPlayout::DecodeSafe( - uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return 0; -} - -int16_t ACMDTMFPlayout::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - // This codec does not need initialization, - // DTMFPlayout has no instance - return 0; -} - -int16_t ACMDTMFPlayout::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - // This codec does not need initialization, - // DTMFPlayout has no instance - return 0; -} - -int32_t ACMDTMFPlayout::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_AVT_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codec_def), kDecoderAVT, codec_inst.pltype, NULL, 8000); - SET_AVT_FUNCTIONS((codec_def)); - return 0; -} - -ACMGenericCodec* ACMDTMFPlayout::CreateInstance(void) { - return NULL; -} - -int16_t ACMDTMFPlayout::InternalCreateEncoder() { - // DTMFPlayout has no instance - return 0; -} - -int16_t ACMDTMFPlayout::InternalCreateDecoder() { - // DTMFPlayout has no instance - return 0; -} - -void ACMDTMFPlayout::InternalDestructEncoderInst(void* /* ptr_inst */) { - // DTMFPlayout has no instance - return; -} - -void ACMDTMFPlayout::DestructEncoderSafe() { - // DTMFPlayout has no instance - return; -} - -void ACMDTMFPlayout::DestructDecoderSafe() { - // DTMFPlayout has no instance - return; -} - -#endif - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_dtmf_playout.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_dtmf_playout.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_dtmf_playout.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_dtmf_playout.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_DTMF_PLAYOUT_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_DTMF_PLAYOUT_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -namespace webrtc { - -namespace acm1 { - -class ACMDTMFPlayout: public ACMGenericCodec { - public: - explicit ACMDTMFPlayout(int16_t codec_id); - virtual ~ACMDTMFPlayout(); - - // for FEC - virtual ACMGenericCodec* CreateInstance(void) OVERRIDE; - - virtual int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) OVERRIDE; - - virtual int16_t InternalInitEncoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - virtual int16_t InternalInitDecoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - protected: - virtual int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type) OVERRIDE; - - virtual int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) OVERRIDE; - - virtual void DestructEncoderSafe() OVERRIDE; - - virtual void DestructDecoderSafe() OVERRIDE; - - virtual int16_t InternalCreateEncoder() OVERRIDE; - - virtual int16_t InternalCreateDecoder() OVERRIDE; - - virtual void InternalDestructEncoderInst(void* ptr_inst) OVERRIDE; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_DTMF_PLAYOUT_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7221.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7221.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7221.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7221.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,500 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_g7221.h" - -#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/trace.h" - -#ifdef WEBRTC_CODEC_G722_1 -// NOTE! G.722.1 is not included in the open-source package. The following -// interface file is needed: -// -// /modules/audio_coding/codecs/g7221/main/interface/g7221_interface.h -// -// The API in the header file should match the one below. -// -// int16_t WebRtcG7221_CreateEnc16(G722_1_16_encinst_t_** enc_inst); -// int16_t WebRtcG7221_CreateEnc24(G722_1_24_encinst_t_** enc_inst); -// int16_t WebRtcG7221_CreateEnc32(G722_1_32_encinst_t_** enc_inst); -// int16_t WebRtcG7221_CreateDec16(G722_1_16_decinst_t_** dec_inst); -// int16_t WebRtcG7221_CreateDec24(G722_1_24_decinst_t_** dec_inst); -// int16_t WebRtcG7221_CreateDec32(G722_1_32_decinst_t_** dec_inst); -// -// int16_t WebRtcG7221_FreeEnc16(G722_1_16_encinst_t_** enc_inst); -// int16_t WebRtcG7221_FreeEnc24(G722_1_24_encinst_t_** enc_inst); -// int16_t WebRtcG7221_FreeEnc32(G722_1_32_encinst_t_** enc_inst); -// int16_t WebRtcG7221_FreeDec16(G722_1_16_decinst_t_** dec_inst); -// int16_t WebRtcG7221_FreeDec24(G722_1_24_decinst_t_** dec_inst); -// int16_t WebRtcG7221_FreeDec32(G722_1_32_decinst_t_** dec_inst); -// -// int16_t WebRtcG7221_EncoderInit16(G722_1_16_encinst_t_* enc_inst); -// int16_t WebRtcG7221_EncoderInit24(G722_1_24_encinst_t_* enc_inst); -// int16_t WebRtcG7221_EncoderInit32(G722_1_32_encinst_t_* enc_inst); -// int16_t WebRtcG7221_DecoderInit16(G722_1_16_decinst_t_* dec_inst); -// int16_t WebRtcG7221_DecoderInit24(G722_1_24_decinst_t_* dec_inst); -// int16_t WebRtcG7221_DecoderInit32(G722_1_32_decinst_t_* dec_inst); -// -// int16_t WebRtcG7221_Encode16(G722_1_16_encinst_t_* enc_inst, -// int16_t* input, -// int16_t len, -// int16_t* output); -// int16_t WebRtcG7221_Encode24(G722_1_24_encinst_t_* enc_inst, -// int16_t* input, -// int16_t len, -// int16_t* output); -// int16_t WebRtcG7221_Encode32(G722_1_32_encinst_t_* enc_inst, -// int16_t* input, -// int16_t len, -// int16_t* output); -// -// int16_t WebRtcG7221_Decode16(G722_1_16_decinst_t_* dec_inst, -// int16_t* bitstream, -// int16_t len, -// int16_t* output); -// int16_t WebRtcG7221_Decode24(G722_1_24_decinst_t_* dec_inst, -// int16_t* bitstream, -// int16_t len, -// int16_t* output); -// int16_t WebRtcG7221_Decode32(G722_1_32_decinst_t_* dec_inst, -// int16_t* bitstream, -// int16_t len, -// int16_t* output); -// -// int16_t WebRtcG7221_DecodePlc16(G722_1_16_decinst_t_* dec_inst, -// int16_t* output, -// int16_t nr_lost_frames); -// int16_t WebRtcG7221_DecodePlc24(G722_1_24_decinst_t_* dec_inst, -// int16_t* output, -// int16_t nr_lost_frames); -// int16_t WebRtcG7221_DecodePlc32(G722_1_32_decinst_t_* dec_inst, -// int16_t* output, -// int16_t nr_lost_frames); -#include "g7221_interface.h" -#endif - -namespace webrtc { - -namespace acm1 { - -#ifndef WEBRTC_CODEC_G722_1 - -ACMG722_1::ACMG722_1(int16_t /* codec_id */) - : operational_rate_(-1), - encoder_inst_ptr_(NULL), - encoder_inst_ptr_right_(NULL), - decoder_inst_ptr_(NULL), - encoder_inst16_ptr_(NULL), - encoder_inst16_ptr_right_(NULL), - encoder_inst24_ptr_(NULL), - encoder_inst24_ptr_right_(NULL), - encoder_inst32_ptr_(NULL), - encoder_inst32_ptr_right_(NULL), - decoder_inst16_ptr_(NULL), - decoder_inst24_ptr_(NULL), - decoder_inst32_ptr_(NULL) { - return; -} - -ACMG722_1::~ACMG722_1() { - return; -} - -int16_t ACMG722_1::InternalEncode( - uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - return -1; -} - -int16_t ACMG722_1::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return -1; -} - -int16_t ACMG722_1::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int16_t ACMG722_1::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int32_t ACMG722_1::CodecDef(WebRtcNetEQ_CodecDef& /* codec_def */, - const CodecInst& /* codec_inst */) { - return -1; -} - -ACMGenericCodec* ACMG722_1::CreateInstance(void) { - return NULL; -} - -int16_t ACMG722_1::InternalCreateEncoder() { - return -1; -} - -void ACMG722_1::DestructEncoderSafe() { - return; -} - -int16_t ACMG722_1::InternalCreateDecoder() { - return -1; -} - -void ACMG722_1::DestructDecoderSafe() { - return; -} - -void ACMG722_1::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - -#else //===================== Actual Implementation ======================= -ACMG722_1::ACMG722_1(int16_t codec_id) - : encoder_inst_ptr_(NULL), - encoder_inst_ptr_right_(NULL), - decoder_inst_ptr_(NULL), - encoder_inst16_ptr_(NULL), - encoder_inst16_ptr_right_(NULL), - encoder_inst24_ptr_(NULL), - encoder_inst24_ptr_right_(NULL), - encoder_inst32_ptr_(NULL), - encoder_inst32_ptr_right_(NULL), - decoder_inst16_ptr_(NULL), - decoder_inst24_ptr_(NULL), - decoder_inst32_ptr_(NULL) { - codec_id_ = codec_id; - if (codec_id_ == ACMCodecDB::kG722_1_16) { - operational_rate_ = 16000; - } else if (codec_id_ == ACMCodecDB::kG722_1_24) { - operational_rate_ = 24000; - } else if (codec_id_ == ACMCodecDB::kG722_1_32) { - operational_rate_ = 32000; - } else { - operational_rate_ = -1; - } - return; -} - -ACMG722_1::~ACMG722_1() { - if (encoder_inst_ptr_ != NULL) { - delete encoder_inst_ptr_; - encoder_inst_ptr_ = NULL; - } - if (encoder_inst_ptr_right_ != NULL) { - delete encoder_inst_ptr_right_; - encoder_inst_ptr_right_ = NULL; - } - if (decoder_inst_ptr_ != NULL) { - delete decoder_inst_ptr_; - decoder_inst_ptr_ = NULL; - } - - switch (operational_rate_) { - case 16000: { - encoder_inst16_ptr_ = NULL; - encoder_inst16_ptr_right_ = NULL; - decoder_inst16_ptr_ = NULL; - break; - } - case 24000: { - encoder_inst24_ptr_ = NULL; - encoder_inst24_ptr_right_ = NULL; - decoder_inst24_ptr_ = NULL; - break; - } - case 32000: { - encoder_inst32_ptr_ = NULL; - encoder_inst32_ptr_right_ = NULL; - decoder_inst32_ptr_ = NULL; - break; - } - default: { - break; - } - } - return; -} - -int16_t ACMG722_1::InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) { - int16_t left_channel[320]; - int16_t right_channel[320]; - int16_t len_in_bytes; - int16_t out_bits[160]; - - // If stereo, split input signal in left and right channel before encoding - if (num_channels_ == 2) { - for (int i = 0, j = 0; i < frame_len_smpl_ * 2; i += 2, j++) { - left_channel[j] = in_audio_[in_audio_ix_read_ + i]; - right_channel[j] = in_audio_[in_audio_ix_read_ + i + 1]; - } - } else { - memcpy(left_channel, &in_audio_[in_audio_ix_read_], 320); - } - - switch (operational_rate_) { - case 16000: { - len_in_bytes = WebRtcG7221_Encode16(encoder_inst16_ptr_, left_channel, - 320, &out_bits[0]); - if (num_channels_ == 2) { - len_in_bytes += WebRtcG7221_Encode16(encoder_inst16_ptr_right_, - right_channel, 320, - &out_bits[len_in_bytes / 2]); - } - break; - } - case 24000: { - len_in_bytes = WebRtcG7221_Encode24(encoder_inst24_ptr_, left_channel, - 320, &out_bits[0]); - if (num_channels_ == 2) { - len_in_bytes += WebRtcG7221_Encode24(encoder_inst24_ptr_right_, - right_channel, 320, - &out_bits[len_in_bytes / 2]); - } - break; - } - case 32000: { - len_in_bytes = WebRtcG7221_Encode32(encoder_inst32_ptr_, left_channel, - 320, &out_bits[0]); - if (num_channels_ == 2) { - len_in_bytes += WebRtcG7221_Encode32(encoder_inst32_ptr_right_, - right_channel, 320, - &out_bits[len_in_bytes / 2]); - } - break; - } - default: { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalInitEncode: Wrong rate for G722_1."); - return -1; - } - } - memcpy(bitstream, out_bits, len_in_bytes); - *bitstream_len_byte = len_in_bytes; - - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - in_audio_ix_read_ += 320 * num_channels_; - return *bitstream_len_byte; -} - -int16_t ACMG722_1::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return 0; -} - -int16_t ACMG722_1::InternalInitEncoder( - WebRtcACMCodecParams* codec_params) { - int16_t ret; - - switch (operational_rate_) { - case 16000: { - ret = WebRtcG7221_EncoderInit16(encoder_inst16_ptr_right_); - if (ret < 0) { - return ret; - } - return WebRtcG7221_EncoderInit16(encoder_inst16_ptr_); - } - case 24000: { - ret = WebRtcG7221_EncoderInit24(encoder_inst24_ptr_right_); - if (ret < 0) { - return ret; - } - return WebRtcG7221_EncoderInit24(encoder_inst24_ptr_); - } - case 32000: { - ret = WebRtcG7221_EncoderInit32(encoder_inst32_ptr_right_); - if (ret < 0) { - return ret; - } - return WebRtcG7221_EncoderInit32(encoder_inst32_ptr_); - } - default: { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, - unique_id_, "InternalInitEncoder: Wrong rate for G722_1."); - return -1; - } - } -} - -int16_t ACMG722_1::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - switch (operational_rate_) { - case 16000: { - return WebRtcG7221_DecoderInit16(decoder_inst16_ptr_); - } - case 24000: { - return WebRtcG7221_DecoderInit24(decoder_inst24_ptr_); - } - case 32000: { - return WebRtcG7221_DecoderInit32(decoder_inst32_ptr_); - } - default: { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalInitDecoder: Wrong rate for G722_1."); - return -1; - } - } -} - -int32_t ACMG722_1::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - if (!decoder_initialized_) { - // Todo: - // log error - return -1; - } - // NetEq has an array of pointers to WebRtcNetEQ_CodecDef. - // Get an entry of that array (neteq wrapper will allocate memory) - // by calling "netEq->CodecDef", where "NETEQ_CODEC_G722_1_XX" would - // be the index of the entry. - // Fill up the given structure by calling - // "SET_CODEC_PAR" & "SET_G722_1_XX_FUNCTION." - // Then return the structure back to NetEQ to add the codec to it's - // database. - switch (operational_rate_) { - case 16000: { - SET_CODEC_PAR((codec_def), kDecoderG722_1_16, codec_inst.pltype, - decoder_inst16_ptr_, 16000); - SET_G722_1_16_FUNCTIONS((codec_def)); - break; - } - case 24000: { - SET_CODEC_PAR((codec_def), kDecoderG722_1_24, codec_inst.pltype, - decoder_inst24_ptr_, 16000); - SET_G722_1_24_FUNCTIONS((codec_def)); - break; - } - case 32000: { - SET_CODEC_PAR((codec_def), kDecoderG722_1_32, codec_inst.pltype, - decoder_inst32_ptr_, 16000); - SET_G722_1_32_FUNCTIONS((codec_def)); - break; - } - default: { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "CodecDef: Wrong rate for G722_1."); - return -1; - } - } - return 0; -} - -ACMGenericCodec* ACMG722_1::CreateInstance(void) { - return NULL; -} - -int16_t ACMG722_1::InternalCreateEncoder() { - if ((encoder_inst_ptr_ == NULL) || (encoder_inst_ptr_right_ == NULL)) { - return -1; - } - switch (operational_rate_) { - case 16000: { - WebRtcG7221_CreateEnc16(&encoder_inst16_ptr_); - WebRtcG7221_CreateEnc16(&encoder_inst16_ptr_right_); - break; - } - case 24000: { - WebRtcG7221_CreateEnc24(&encoder_inst24_ptr_); - WebRtcG7221_CreateEnc24(&encoder_inst24_ptr_right_); - break; - } - case 32000: { - WebRtcG7221_CreateEnc32(&encoder_inst32_ptr_); - WebRtcG7221_CreateEnc32(&encoder_inst32_ptr_right_); - break; - } - default: { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalCreateEncoder: Wrong rate for G722_1."); - return -1; - } - } - return 0; -} - -void ACMG722_1::DestructEncoderSafe() { - encoder_exist_ = false; - encoder_initialized_ = false; - if (encoder_inst_ptr_ != NULL) { - delete encoder_inst_ptr_; - encoder_inst_ptr_ = NULL; - } - if (encoder_inst_ptr_right_ != NULL) { - delete encoder_inst_ptr_right_; - encoder_inst_ptr_right_ = NULL; - } - encoder_inst16_ptr_ = NULL; - encoder_inst24_ptr_ = NULL; - encoder_inst32_ptr_ = NULL; -} - -int16_t ACMG722_1::InternalCreateDecoder() { - if (decoder_inst_ptr_ == NULL) { - return -1; - } - switch (operational_rate_) { - case 16000: { - WebRtcG7221_CreateDec16(&decoder_inst16_ptr_); - break; - } - case 24000: { - WebRtcG7221_CreateDec24(&decoder_inst24_ptr_); - break; - } - case 32000: { - WebRtcG7221_CreateDec32(&decoder_inst32_ptr_); - break; - } - default: { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalCreateDecoder: Wrong rate for G722_1."); - return -1; - } - } - return 0; -} - -void ACMG722_1::DestructDecoderSafe() { - decoder_exist_ = false; - decoder_initialized_ = false; - if (decoder_inst_ptr_ != NULL) { - delete decoder_inst_ptr_; - decoder_inst_ptr_ = NULL; - } - decoder_inst16_ptr_ = NULL; - decoder_inst24_ptr_ = NULL; - decoder_inst32_ptr_ = NULL; -} - -void ACMG722_1::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - delete ptr_inst; - } - return; -} - -#endif - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7221c.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7221c.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7221c.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7221c.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,510 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_g7221c.h" - -#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/trace.h" - -#ifdef WEBRTC_CODEC_G722_1C -// NOTE! G.722.1C is not included in the open-source package. The following -// interface file is needed: -// -// /modules/audio_coding/codecs/g7221c/main/interface/g7221c_interface.h -// -// The API in the header file should match the one below. -// - -// int16_t WebRtcG7221C_CreateEnc24(G722_1C_24_encinst_t_** enc_inst); -// int16_t WebRtcG7221C_CreateEnc32(G722_1C_32_encinst_t_** enc_inst); -// int16_t WebRtcG7221C_CreateEnc48(G722_1C_48_encinst_t_** enc_inst); -// int16_t WebRtcG7221C_CreateDec24(G722_1C_24_decinst_t_** dec_inst); -// int16_t WebRtcG7221C_CreateDec32(G722_1C_32_decinst_t_** dec_inst); -// int16_t WebRtcG7221C_CreateDec48(G722_1C_48_decinst_t_** dec_inst); -// -// int16_t WebRtcG7221C_FreeEnc24(G722_1C_24_encinst_t_** enc_inst); -// int16_t WebRtcG7221C_FreeEnc32(G722_1C_32_encinst_t_** enc_inst); -// int16_t WebRtcG7221C_FreeEnc48(G722_1C_48_encinst_t_** enc_inst); -// int16_t WebRtcG7221C_FreeDec24(G722_1C_24_decinst_t_** dec_inst); -// int16_t WebRtcG7221C_FreeDec32(G722_1C_32_decinst_t_** dec_inst); -// int16_t WebRtcG7221C_FreeDec48(G722_1C_48_decinst_t_** dec_inst); -// -// int16_t WebRtcG7221C_EncoderInit24(G722_1C_24_encinst_t_* enc_inst); -// int16_t WebRtcG7221C_EncoderInit32(G722_1C_32_encinst_t_* enc_inst); -// int16_t WebRtcG7221C_EncoderInit48(G722_1C_48_encinst_t_* enc_inst); -// int16_t WebRtcG7221C_DecoderInit24(G722_1C_24_decinst_t_* dec_inst); -// int16_t WebRtcG7221C_DecoderInit32(G722_1C_32_decinst_t_* dec_inst); -// int16_t WebRtcG7221C_DecoderInit48(G722_1C_48_decinst_t_* dec_inst); -// -// int16_t WebRtcG7221C_Encode24(G722_1C_24_encinst_t_* enc_inst, -// int16_t* input, -// int16_t len, -// int16_t* output); -// int16_t WebRtcG7221C_Encode32(G722_1C_32_encinst_t_* enc_inst, -// int16_t* input, -// int16_t len, -// int16_t* output); -// int16_t WebRtcG7221C_Encode48(G722_1C_48_encinst_t_* enc_inst, -// int16_t* input, -// int16_t len, -// int16_t* output); -// -// int16_t WebRtcG7221C_Decode24(G722_1C_24_decinst_t_* dec_inst, -// int16_t* bitstream, -// int16_t len, -// int16_t* output); -// int16_t WebRtcG7221C_Decode32(G722_1C_32_decinst_t_* dec_inst, -// int16_t* bitstream, -// int16_t len, -// int16_t* output); -// int16_t WebRtcG7221C_Decode48(G722_1C_48_decinst_t_* dec_inst, -// int16_t* bitstream, -// int16_t len, -// int16_t* output); -// -// int16_t WebRtcG7221C_DecodePlc24(G722_1C_24_decinst_t_* dec_inst, -// int16_t* output, -// int16_t nr_lost_frames); -// int16_t WebRtcG7221C_DecodePlc32(G722_1C_32_decinst_t_* dec_inst, -// int16_t* output, -// int16_t nr_lost_frames); -// int16_t WebRtcG7221C_DecodePlc48(G722_1C_48_decinst_t_* dec_inst, -// int16_t* output, -// int16_t nr_lost_frames); -#include "g7221c_interface.h" -#endif - -namespace webrtc { - -namespace acm1 { - -#ifndef WEBRTC_CODEC_G722_1C - -ACMG722_1C::ACMG722_1C(int16_t /* codec_id */) - : operational_rate_(-1), - encoder_inst_ptr_(NULL), - encoder_inst_ptr_right_(NULL), - decoder_inst_ptr_(NULL), - encoder_inst24_ptr_(NULL), - encoder_inst24_ptr_right_(NULL), - encoder_inst32_ptr_(NULL), - encoder_inst32_ptr_right_(NULL), - encoder_inst48_ptr_(NULL), - encoder_inst48_ptr_right_(NULL), - decoder_inst24_ptr_(NULL), - decoder_inst32_ptr_(NULL), - decoder_inst48_ptr_(NULL) { - return; -} - -ACMG722_1C::~ACMG722_1C() { - return; -} - -int16_t ACMG722_1C::InternalEncode( - uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - return -1; -} - -int16_t ACMG722_1C::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return -1; -} - -int16_t ACMG722_1C::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int16_t ACMG722_1C::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int32_t ACMG722_1C::CodecDef(WebRtcNetEQ_CodecDef& /* codec_def */, - const CodecInst& /* codec_inst */) { - return -1; -} - -ACMGenericCodec* ACMG722_1C::CreateInstance(void) { - return NULL; -} - -int16_t ACMG722_1C::InternalCreateEncoder() { - return -1; -} - -void ACMG722_1C::DestructEncoderSafe() { - return; -} - -int16_t ACMG722_1C::InternalCreateDecoder() { - return -1; -} - -void ACMG722_1C::DestructDecoderSafe() { - return; -} - -void ACMG722_1C::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - -#else //===================== Actual Implementation ======================= -ACMG722_1C::ACMG722_1C(int16_t codec_id) - : encoder_inst_ptr_(NULL), - encoder_inst_ptr_right_(NULL), - decoder_inst_ptr_(NULL), - encoder_inst24_ptr_(NULL), - encoder_inst24_ptr_right_(NULL), - encoder_inst32_ptr_(NULL), - encoder_inst32_ptr_right_(NULL), - encoder_inst48_ptr_(NULL), - encoder_inst48_ptr_right_(NULL), - decoder_inst24_ptr_(NULL), - decoder_inst32_ptr_(NULL), - decoder_inst48_ptr_(NULL) { - codec_id_ = codec_id; - if (codec_id_ == ACMCodecDB::kG722_1C_24) { - operational_rate_ = 24000; - } else if (codec_id_ == ACMCodecDB::kG722_1C_32) { - operational_rate_ = 32000; - } else if (codec_id_ == ACMCodecDB::kG722_1C_48) { - operational_rate_ = 48000; - } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Wrong codec id for G722_1c."); - operational_rate_ = -1; - } - return; -} - -ACMG722_1C::~ACMG722_1C() { - if (encoder_inst_ptr_ != NULL) { - delete encoder_inst_ptr_; - encoder_inst_ptr_ = NULL; - } - if (encoder_inst_ptr_right_ != NULL) { - delete encoder_inst_ptr_right_; - encoder_inst_ptr_right_ = NULL; - } - if (decoder_inst_ptr_ != NULL) { - delete decoder_inst_ptr_; - decoder_inst_ptr_ = NULL; - } - - switch (operational_rate_) { - case 24000: { - encoder_inst24_ptr_ = NULL; - encoder_inst24_ptr_right_ = NULL; - decoder_inst24_ptr_ = NULL; - break; - } - case 32000: { - encoder_inst32_ptr_ = NULL; - encoder_inst32_ptr_right_ = NULL; - decoder_inst32_ptr_ = NULL; - break; - } - case 48000: { - encoder_inst48_ptr_ = NULL; - encoder_inst48_ptr_right_ = NULL; - decoder_inst48_ptr_ = NULL; - break; - } - default: { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Wrong rate for G722_1c."); - break; - } - } - return; -} - -int16_t ACMG722_1C::InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) { - int16_t left_channel[640]; - int16_t right_channel[640]; - int16_t len_in_bytes; - int16_t out_bits[240]; - - // If stereo, split input signal in left and right channel before encoding - if (num_channels_ == 2) { - for (int i = 0, j = 0; i < frame_len_smpl_ * 2; i += 2, j++) { - left_channel[j] = in_audio_[in_audio_ix_read_ + i]; - right_channel[j] = in_audio_[in_audio_ix_read_ + i + 1]; - } - } else { - memcpy(left_channel, &in_audio_[in_audio_ix_read_], 640); - } - - switch (operational_rate_) { - case 24000: { - len_in_bytes = WebRtcG7221C_Encode24(encoder_inst24_ptr_, left_channel, - 640, &out_bits[0]); - if (num_channels_ == 2) { - len_in_bytes += WebRtcG7221C_Encode24(encoder_inst24_ptr_right_, - right_channel, 640, - &out_bits[len_in_bytes / 2]); - } - break; - } - case 32000: { - len_in_bytes = WebRtcG7221C_Encode32(encoder_inst32_ptr_, left_channel, - 640, &out_bits[0]); - if (num_channels_ == 2) { - len_in_bytes += WebRtcG7221C_Encode32(encoder_inst32_ptr_right_, - right_channel, 640, - &out_bits[len_in_bytes / 2]); - } - break; - } - case 48000: { - len_in_bytes = WebRtcG7221C_Encode48(encoder_inst48_ptr_, left_channel, - 640, &out_bits[0]); - if (num_channels_ == 2) { - len_in_bytes += WebRtcG7221C_Encode48(encoder_inst48_ptr_right_, - right_channel, 640, - &out_bits[len_in_bytes / 2]); - } - break; - } - default: { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalEncode: Wrong rate for G722_1c."); - return -1; - } - } - - memcpy(bitstream, out_bits, len_in_bytes); - *bitstream_len_byte = len_in_bytes; - - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - in_audio_ix_read_ += 640 * num_channels_; - - return *bitstream_len_byte; -} - -int16_t ACMG722_1C::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return 0; -} - -int16_t ACMG722_1C::InternalInitEncoder( - WebRtcACMCodecParams* codec_params) { - int16_t ret; - - switch (operational_rate_) { - case 24000: { - ret = WebRtcG7221C_EncoderInit24(encoder_inst24_ptr_right_); - if (ret < 0) { - return ret; - } - return WebRtcG7221C_EncoderInit24(encoder_inst24_ptr_); - } - case 32000: { - ret = WebRtcG7221C_EncoderInit32(encoder_inst32_ptr_right_); - if (ret < 0) { - return ret; - } - return WebRtcG7221C_EncoderInit32(encoder_inst32_ptr_); - } - case 48000: { - ret = WebRtcG7221C_EncoderInit48(encoder_inst48_ptr_right_); - if (ret < 0) { - return ret; - } - return WebRtcG7221C_EncoderInit48(encoder_inst48_ptr_); - } - default: { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalInitEncode: Wrong rate for G722_1c."); - return -1; - } - } -} - -int16_t ACMG722_1C::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - switch (operational_rate_) { - case 24000: { - return WebRtcG7221C_DecoderInit24(decoder_inst24_ptr_); - } - case 32000: { - return WebRtcG7221C_DecoderInit32(decoder_inst32_ptr_); - } - case 48000: { - return WebRtcG7221C_DecoderInit48(decoder_inst48_ptr_); - } - default: { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalInitDecoder: Wrong rate for G722_1c."); - return -1; - } - } -} - -int32_t ACMG722_1C::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - if (!decoder_initialized_) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "CodeDef: decoder not initialized for G722_1c"); - return -1; - } - // NetEq has an array of pointers to WebRtcNetEQ_CodecDef. - // get an entry of that array (neteq wrapper will allocate memory) - // by calling "netEq->CodecDef", where "NETEQ_CODEC_G722_1_XX" would - // be the index of the entry. - // Fill up the given structure by calling - // "SET_CODEC_PAR" & "SET_G722_1_XX_FUNCTION." - // Then return the structure back to NetEQ to add the codec to it's - // database. - switch (operational_rate_) { - case 24000: { - SET_CODEC_PAR((codec_def), kDecoderG722_1C_24, codec_inst.pltype, - decoder_inst24_ptr_, 32000); - SET_G722_1C_24_FUNCTIONS((codec_def)); - break; - } - case 32000: { - SET_CODEC_PAR((codec_def), kDecoderG722_1C_32, codec_inst.pltype, - decoder_inst32_ptr_, 32000); - SET_G722_1C_32_FUNCTIONS((codec_def)); - break; - } - case 48000: { - SET_CODEC_PAR((codec_def), kDecoderG722_1C_32, codec_inst.pltype, - decoder_inst48_ptr_, 32000); - SET_G722_1C_48_FUNCTIONS((codec_def)); - break; - } - default: { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "CodeDef: Wrong rate for G722_1c."); - return -1; - } - } - return 0; -} - -ACMGenericCodec* -ACMG722_1C::CreateInstance(void) { - return NULL; -} - -int16_t ACMG722_1C::InternalCreateEncoder() { - if ((encoder_inst_ptr_ == NULL) || (encoder_inst_ptr_right_ == NULL)) { - return -1; - } - switch (operational_rate_) { - case 24000: { - WebRtcG7221C_CreateEnc24(&encoder_inst24_ptr_); - WebRtcG7221C_CreateEnc24(&encoder_inst24_ptr_right_); - break; - } - case 32000: { - WebRtcG7221C_CreateEnc32(&encoder_inst32_ptr_); - WebRtcG7221C_CreateEnc32(&encoder_inst32_ptr_right_); - break; - } - case 48000: { - WebRtcG7221C_CreateEnc48(&encoder_inst48_ptr_); - WebRtcG7221C_CreateEnc48(&encoder_inst48_ptr_right_); - break; - } - default: { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalCreateEncoder: Wrong rate for G722_1c."); - return -1; - } - } - return 0; -} - -void ACMG722_1C::DestructEncoderSafe() { - encoder_exist_ = false; - encoder_initialized_ = false; - if (encoder_inst_ptr_ != NULL) { - delete encoder_inst_ptr_; - encoder_inst_ptr_ = NULL; - } - if (encoder_inst_ptr_right_ != NULL) { - delete encoder_inst_ptr_right_; - encoder_inst_ptr_right_ = NULL; - } - encoder_inst24_ptr_ = NULL; - encoder_inst32_ptr_ = NULL; - encoder_inst48_ptr_ = NULL; -} - -int16_t ACMG722_1C::InternalCreateDecoder() { - if (decoder_inst_ptr_ == NULL) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalCreateEncoder: cannot create decoder"); - return -1; - } - switch (operational_rate_) { - case 24000: { - WebRtcG7221C_CreateDec24(&decoder_inst24_ptr_); - break; - } - case 32000: { - WebRtcG7221C_CreateDec32(&decoder_inst32_ptr_); - break; - } - case 48000: { - WebRtcG7221C_CreateDec48(&decoder_inst48_ptr_); - break; - } - default: { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalCreateEncoder: Wrong rate for G722_1c."); - return -1; - } - } - return 0; -} - -void ACMG722_1C::DestructDecoderSafe() { - decoder_exist_ = false; - decoder_initialized_ = false; - if (decoder_inst_ptr_ != NULL) { - delete decoder_inst_ptr_; - decoder_inst_ptr_ = NULL; - } - decoder_inst24_ptr_ = NULL; - decoder_inst32_ptr_ = NULL; - decoder_inst48_ptr_ = NULL; -} - -void ACMG722_1C::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - delete ptr_inst; - } - return; -} - -#endif - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7221c.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7221c.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7221c.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7221c.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7221C_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7221C_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -// forward declaration -struct G722_1C_24_encinst_t_; -struct G722_1C_24_decinst_t_; -struct G722_1C_32_encinst_t_; -struct G722_1C_32_decinst_t_; -struct G722_1C_48_encinst_t_; -struct G722_1C_48_decinst_t_; -struct G722_1_Inst_t_; - -namespace webrtc { - -namespace acm1 { - -class ACMG722_1C : public ACMGenericCodec { - public: - explicit ACMG722_1C(int16_t codec_id); - ~ACMG722_1C(); - - // for FEC - ACMGenericCodec* CreateInstance(void); - - int16_t InternalEncode( - uint8_t* bitstream, - int16_t* bitstream_len_byte); - - int16_t InternalInitEncoder( - WebRtcACMCodecParams *codec_params); - - int16_t InternalInitDecoder( - WebRtcACMCodecParams *codec_params); - - protected: - int16_t DecodeSafe( - uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type); - - int32_t CodecDef( - WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - int16_t InternalCreateEncoder(); - - int16_t InternalCreateDecoder(); - - void InternalDestructEncoderInst( - void* ptr_inst); - - int32_t operational_rate_; - - G722_1_Inst_t_* encoder_inst_ptr_; - G722_1_Inst_t_* encoder_inst_ptr_right_; // Used in stereo mode - G722_1_Inst_t_* decoder_inst_ptr_; - - // Only one set of these pointer is valid at any instance - G722_1C_24_encinst_t_* encoder_inst24_ptr_; - G722_1C_24_encinst_t_* encoder_inst24_ptr_right_; - G722_1C_32_encinst_t_* encoder_inst32_ptr_; - G722_1C_32_encinst_t_* encoder_inst32_ptr_right_; - G722_1C_48_encinst_t_* encoder_inst48_ptr_; - G722_1C_48_encinst_t_* encoder_inst48_ptr_right_; - - // Only one of these pointer is valid at any instance - G722_1C_24_decinst_t_* decoder_inst24_ptr_; - G722_1C_32_decinst_t_* decoder_inst32_ptr_; - G722_1C_48_decinst_t_* decoder_inst48_ptr_; -}; - -} // namespace acm1 - -} // namespace webrtc; - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7221C_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7221.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7221.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7221.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7221.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7221_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7221_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -// forward declaration -struct G722_1_16_encinst_t_; -struct G722_1_16_decinst_t_; -struct G722_1_24_encinst_t_; -struct G722_1_24_decinst_t_; -struct G722_1_32_encinst_t_; -struct G722_1_32_decinst_t_; -struct G722_1_Inst_t_; - -namespace webrtc { - -namespace acm1 { - -class ACMG722_1: public ACMGenericCodec { - public: - explicit ACMG722_1(int16_t codec_id); - ~ACMG722_1(); - - // for FEC - ACMGenericCodec* CreateInstance(void); - - int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); - - int16_t InternalInitEncoder(WebRtcACMCodecParams *codec_params); - - int16_t InternalInitDecoder(WebRtcACMCodecParams *codec_params); - - protected: - int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, int16_t* audio_samples, - int8_t* speech_type); - - int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - int16_t InternalCreateEncoder(); - - int16_t InternalCreateDecoder(); - - void InternalDestructEncoderInst(void* ptr_inst); - - int32_t operational_rate_; - - G722_1_Inst_t_* encoder_inst_ptr_; - G722_1_Inst_t_* encoder_inst_ptr_right_; // Used in stereo mode - G722_1_Inst_t_* decoder_inst_ptr_; - - // Only one set of these pointer is valid at any instance - G722_1_16_encinst_t_* encoder_inst16_ptr_; - G722_1_16_encinst_t_* encoder_inst16_ptr_right_; - G722_1_24_encinst_t_* encoder_inst24_ptr_; - G722_1_24_encinst_t_* encoder_inst24_ptr_right_; - G722_1_32_encinst_t_* encoder_inst32_ptr_; - G722_1_32_encinst_t_* encoder_inst32_ptr_right_; - - // Only one of these pointer is valid at any instance - G722_1_16_decinst_t_* decoder_inst16_ptr_; - G722_1_24_decinst_t_* decoder_inst24_ptr_; - G722_1_32_decinst_t_* decoder_inst32_ptr_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7221_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g722.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g722.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g722.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g722.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,358 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_g722.h" - -#include "webrtc/modules/audio_coding/codecs/g722/include/g722_interface.h" -#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/trace.h" - -namespace webrtc { - -namespace acm1 { - -#ifndef WEBRTC_CODEC_G722 - -ACMG722::ACMG722(int16_t /* codec_id */) - : ptr_enc_str_(NULL), - ptr_dec_str_(NULL), - encoder_inst_ptr_(NULL), - encoder_inst_ptr_right_(NULL), - decoder_inst_ptr_(NULL) {} - -ACMG722::~ACMG722() {} - -int32_t ACMG722::Add10MsDataSafe( - const uint32_t /* timestamp */, - const int16_t* /* data */, - const uint16_t /* length_smpl */, - const uint8_t /* audio_channel */) { - return -1; -} - -int16_t ACMG722::InternalEncode( - uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - return -1; -} - -int16_t ACMG722::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return -1; -} - -int16_t ACMG722::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int16_t ACMG722::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int32_t ACMG722::CodecDef(WebRtcNetEQ_CodecDef& /* codec_def */, - const CodecInst& /* codec_inst */) { - return -1; -} - -ACMGenericCodec* ACMG722::CreateInstance(void) { - return NULL; -} - -int16_t ACMG722::InternalCreateEncoder() { - return -1; -} - -void ACMG722::DestructEncoderSafe() { - return; -} - -int16_t ACMG722::InternalCreateDecoder() { - return -1; -} - -void ACMG722::DestructDecoderSafe() { - return; -} - -void ACMG722::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - -void ACMG722::SplitStereoPacket(uint8_t* /*payload*/, - int32_t* /*payload_length*/) {} - -#else //===================== Actual Implementation ======================= - -// Encoder and decoder memory -struct ACMG722EncStr { - G722EncInst* inst; // instance for left channel in case of stereo - G722EncInst* inst_right; // instance for right channel in case of stereo -}; -struct ACMG722DecStr { - G722DecInst* inst; // instance for left channel in case of stereo - G722DecInst* inst_right; // instance for right channel in case of stereo -}; - -ACMG722::ACMG722(int16_t codec_id) - : encoder_inst_ptr_(NULL), - encoder_inst_ptr_right_(NULL), - decoder_inst_ptr_(NULL) { - // Encoder - ptr_enc_str_ = new ACMG722EncStr; - if (ptr_enc_str_ != NULL) { - ptr_enc_str_->inst = NULL; - ptr_enc_str_->inst_right = NULL; - } - // Decoder - ptr_dec_str_ = new ACMG722DecStr; - if (ptr_dec_str_ != NULL) { - ptr_dec_str_->inst = NULL; - ptr_dec_str_->inst_right = NULL; // Not used - } - codec_id_ = codec_id; - return; -} - -ACMG722::~ACMG722() { - // Encoder - if (ptr_enc_str_ != NULL) { - if (ptr_enc_str_->inst != NULL) { - WebRtcG722_FreeEncoder(ptr_enc_str_->inst); - ptr_enc_str_->inst = NULL; - } - if (ptr_enc_str_->inst_right != NULL) { - WebRtcG722_FreeEncoder(ptr_enc_str_->inst_right); - ptr_enc_str_->inst_right = NULL; - } - delete ptr_enc_str_; - ptr_enc_str_ = NULL; - } - // Decoder - if (ptr_dec_str_ != NULL) { - if (ptr_dec_str_->inst != NULL) { - WebRtcG722_FreeDecoder(ptr_dec_str_->inst); - ptr_dec_str_->inst = NULL; - } - if (ptr_dec_str_->inst_right != NULL) { - WebRtcG722_FreeDecoder(ptr_dec_str_->inst_right); - ptr_dec_str_->inst_right = NULL; - } - delete ptr_dec_str_; - ptr_dec_str_ = NULL; - } - return; -} - -int32_t ACMG722::Add10MsDataSafe(const uint32_t timestamp, - const int16_t* data, - const uint16_t length_smpl, - const uint8_t audio_channel) { - return ACMGenericCodec::Add10MsDataSafe((timestamp >> 1), data, length_smpl, - audio_channel); -} - -int16_t ACMG722::InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) { - // If stereo, split input signal in left and right channel before encoding - if (num_channels_ == 2) { - int16_t left_channel[960]; - int16_t right_channel[960]; - uint8_t out_left[480]; - uint8_t out_right[480]; - int16_t len_in_bytes; - for (int i = 0, j = 0; i < frame_len_smpl_ * 2; i += 2, j++) { - left_channel[j] = in_audio_[in_audio_ix_read_ + i]; - right_channel[j] = in_audio_[in_audio_ix_read_ + i + 1]; - } - len_in_bytes = WebRtcG722_Encode(encoder_inst_ptr_, left_channel, - frame_len_smpl_, - (int16_t*)out_left); - len_in_bytes += WebRtcG722_Encode(encoder_inst_ptr_right_, right_channel, - frame_len_smpl_, - (int16_t*)out_right); - *bitstream_len_byte = len_in_bytes; - - // Interleave the 4 bits per sample from left and right channel - for (int i = 0, j = 0; i < len_in_bytes; i += 2, j++) { - bitstream[i] = (out_left[j] & 0xF0) + (out_right[j] >> 4); - bitstream[i + 1] = ((out_left[j] & 0x0F) << 4) + (out_right[j] & 0x0F); - } - } else { - *bitstream_len_byte = WebRtcG722_Encode(encoder_inst_ptr_, - &in_audio_[in_audio_ix_read_], - frame_len_smpl_, - (int16_t*)bitstream); - } - - // increment the read index this tell the caller how far - // we have gone forward in reading the audio buffer - in_audio_ix_read_ += frame_len_smpl_ * num_channels_; - return *bitstream_len_byte; -} - -int16_t ACMG722::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return 0; -} - -int16_t ACMG722::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { - if (codec_params->codec_inst.channels == 2) { - // Create codec struct for right channel - if (ptr_enc_str_->inst_right == NULL) { - WebRtcG722_CreateEncoder(&ptr_enc_str_->inst_right); - if (ptr_enc_str_->inst_right == NULL) { - return -1; - } - } - encoder_inst_ptr_right_ = ptr_enc_str_->inst_right; - if (WebRtcG722_EncoderInit(encoder_inst_ptr_right_) < 0) { - return -1; - } - } - - return WebRtcG722_EncoderInit(encoder_inst_ptr_); -} - -int16_t ACMG722::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - return WebRtcG722_DecoderInit(decoder_inst_ptr_); -} - -int32_t ACMG722::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - if (!decoder_initialized_) { - // TODO(turajs): log error - return -1; - } - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_G722_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - if (codec_inst.channels == 1) { - SET_CODEC_PAR(codec_def, kDecoderG722, codec_inst.pltype, decoder_inst_ptr_, - 16000); - } else { - SET_CODEC_PAR(codec_def, kDecoderG722_2ch, codec_inst.pltype, - decoder_inst_ptr_, 16000); - } - SET_G722_FUNCTIONS(codec_def); - return 0; -} - -ACMGenericCodec* ACMG722::CreateInstance(void) { - return NULL; -} - -int16_t ACMG722::InternalCreateEncoder() { - if (ptr_enc_str_ == NULL) { - // this structure must be created at the costructor - // if it is still NULL then there is a probelm and - // we dont continue - return -1; - } - WebRtcG722_CreateEncoder(&ptr_enc_str_->inst); - if (ptr_enc_str_->inst == NULL) { - return -1; - } - encoder_inst_ptr_ = ptr_enc_str_->inst; - return 0; -} - -void ACMG722::DestructEncoderSafe() { - if (ptr_enc_str_ != NULL) { - if (ptr_enc_str_->inst != NULL) { - WebRtcG722_FreeEncoder(ptr_enc_str_->inst); - ptr_enc_str_->inst = NULL; - } - } - encoder_exist_ = false; - encoder_initialized_ = false; -} - -int16_t ACMG722::InternalCreateDecoder() { - if (ptr_dec_str_ == NULL) { - // this structure must be created at the costructor - // if it is still NULL then there is a probelm and - // we dont continue - return -1; - } - - WebRtcG722_CreateDecoder(&ptr_dec_str_->inst); - if (ptr_dec_str_->inst == NULL) { - return -1; - } - decoder_inst_ptr_ = ptr_dec_str_->inst; - return 0; -} - -void ACMG722::DestructDecoderSafe() { - decoder_exist_ = false; - decoder_initialized_ = false; - if (ptr_dec_str_ != NULL) { - if (ptr_dec_str_->inst != NULL) { - WebRtcG722_FreeDecoder(ptr_dec_str_->inst); - ptr_dec_str_->inst = NULL; - } - } -} - -void ACMG722::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcG722_FreeEncoder(static_cast(ptr_inst)); - } - return; -} - -// Split the stereo packet and place left and right channel after each other -// in the payload vector. -void ACMG722::SplitStereoPacket(uint8_t* payload, int32_t* payload_length) { - uint8_t right_byte; - - // Check for valid inputs. - assert(payload != NULL); - assert(*payload_length > 0); - - // Regroup the 4 bits/sample so to |l1 l2| |r1 r2| |l3 l4| |r3 r4| ..., - // where "lx" is 4 bits representing left sample number x, and "rx" right - // sample. Two samples fits in one byte, represented with |...|. - for (int i = 0; i < *payload_length; i += 2) { - right_byte = ((payload[i] & 0x0F) << 4) + (payload[i + 1] & 0x0F); - payload[i] = (payload[i] & 0xF0) + (payload[i + 1] >> 4); - payload[i + 1] = right_byte; - } - - // Move one byte representing right channel each loop, and place it at the - // end of the bytestream vector. After looping the data is reordered to: - // |l1 l2| |l3 l4| ... |l(N-1) lN| |r1 r2| |r3 r4| ... |r(N-1) r(N)|, - // where N is the total number of samples. - for (int i = 0; i < *payload_length / 2; i++) { - right_byte = payload[i + 1]; - memmove(&payload[i + 1], &payload[i + 2], *payload_length - i - 2); - payload[*payload_length - 1] = right_byte; - } -} - -#endif - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g722.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g722.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g722.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g722.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G722_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G722_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -typedef struct WebRtcG722EncInst G722EncInst; -typedef struct WebRtcG722DecInst G722DecInst; - -namespace webrtc { - -namespace acm1 { - -// forward declaration -struct ACMG722EncStr; -struct ACMG722DecStr; - -class ACMG722 : public ACMGenericCodec { - public: - explicit ACMG722(int16_t codec_id); - virtual ~ACMG722(); - - // for FEC - virtual ACMGenericCodec* CreateInstance(void) OVERRIDE; - - virtual int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) OVERRIDE; - - virtual int16_t InternalInitEncoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - virtual int16_t InternalInitDecoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - protected: - virtual int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type) OVERRIDE; - - virtual int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) OVERRIDE; - - virtual int32_t Add10MsDataSafe(const uint32_t timestamp, - const int16_t* data, - const uint16_t length_smpl, - const uint8_t audio_channel) OVERRIDE; - - virtual void DestructEncoderSafe() OVERRIDE; - - virtual void DestructDecoderSafe() OVERRIDE; - - virtual int16_t InternalCreateEncoder() OVERRIDE; - - virtual int16_t InternalCreateDecoder() OVERRIDE; - - virtual void InternalDestructEncoderInst(void* ptr_inst) OVERRIDE; - - virtual void SplitStereoPacket(uint8_t* payload, - int32_t* payload_length) OVERRIDE; - - ACMG722EncStr* ptr_enc_str_; - ACMG722DecStr* ptr_dec_str_; - - G722EncInst* encoder_inst_ptr_; - G722EncInst* encoder_inst_ptr_right_; // Prepared for stereo - G722DecInst* decoder_inst_ptr_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G722_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7291.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7291.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7291.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7291.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,349 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_g7291.h" - -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/trace.h" -#ifdef WEBRTC_CODEC_G729_1 -// NOTE! G.729.1 is not included in the open-source package. Modify this file -// or your codec API to match the function calls and names of used G.729.1 API -// file. -#include "g7291_interface.h" -#endif - -namespace webrtc { - -namespace acm1 { - -#ifndef WEBRTC_CODEC_G729_1 - -ACMG729_1::ACMG729_1(int16_t /* codec_id */) - : encoder_inst_ptr_(NULL), - decoder_inst_ptr_(NULL), - my_rate_(32000), - flag_8khz_(0), - flag_g729_mode_(0) { - return; -} - -ACMG729_1::~ACMG729_1() { - return; -} - -int16_t ACMG729_1::InternalEncode( - uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - return -1; -} - -int16_t ACMG729_1::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return -1; -} - -int16_t ACMG729_1::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int16_t ACMG729_1::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int32_t ACMG729_1::CodecDef(WebRtcNetEQ_CodecDef& /* codec_def */, - const CodecInst& /* codec_inst */) { - return -1; -} - -ACMGenericCodec* ACMG729_1::CreateInstance(void) { - return NULL; -} - -int16_t ACMG729_1::InternalCreateEncoder() { - return -1; -} - -void ACMG729_1::DestructEncoderSafe() { - return; -} - -int16_t ACMG729_1::InternalCreateDecoder() { - return -1; -} - -void ACMG729_1::DestructDecoderSafe() { - return; -} - -void ACMG729_1::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - -int16_t ACMG729_1::SetBitRateSafe(const int32_t /*rate*/) { - return -1; -} - -#else //===================== Actual Implementation ======================= - -struct G729_1_inst_t_; - -ACMG729_1::ACMG729_1(int16_t codec_id) - : encoder_inst_ptr_(NULL), - decoder_inst_ptr_(NULL), - my_rate_(32000), // Default rate. - flag_8khz_(0), - flag_g729_mode_(0) { - // TODO(tlegrand): We should add codec_id as a input variable to the - // constructor of ACMGenericCodec. - codec_id_ = codec_id; - return; -} - -ACMG729_1::~ACMG729_1() { - if (encoder_inst_ptr_ != NULL) { - WebRtcG7291_Free(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } - if (decoder_inst_ptr_ != NULL) { - WebRtcG7291_Free(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } - return; -} - -int16_t ACMG729_1::InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) { - - // Initialize before entering the loop - int16_t num_encoded_samples = 0; - *bitstream_len_byte = 0; - - int16_t byte_length_frame = 0; - - // Derive number of 20ms frames per encoded packet. - // [1,2,3] <=> [20,40,60]ms <=> [320,640,960] samples - int16_t num_20ms_frames = (frame_len_smpl_ / 320); - // Byte length for the frame. +1 is for rate information. - byte_length_frame = my_rate_ / (8 * 50) * num_20ms_frames + (1 - - flag_g729_mode_); - - // The following might be revised if we have G729.1 Annex C (support for DTX); - do { - *bitstream_len_byte = WebRtcG7291_Encode(encoder_inst_ptr_, - &in_audio_[in_audio_ix_read_], - (int16_t*) bitstream, - my_rate_, num_20ms_frames); - - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - in_audio_ix_read_ += 160; - - // sanity check - if (*bitstream_len_byte < 0) { - // error has happened - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalEncode: Encode error for G729_1"); - *bitstream_len_byte = 0; - return -1; - } - - num_encoded_samples += 160; - } while (*bitstream_len_byte == 0); - - // This criteria will change if we have Annex C. - if (*bitstream_len_byte != byte_length_frame) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalEncode: Encode error for G729_1"); - *bitstream_len_byte = 0; - return -1; - } - - if (num_encoded_samples != frame_len_smpl_) { - *bitstream_len_byte = 0; - return -1; - } - - return *bitstream_len_byte; -} - -int16_t ACMG729_1::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return 0; -} - -int16_t ACMG729_1::InternalInitEncoder( - WebRtcACMCodecParams* codec_params) { - //set the bit rate and initialize - my_rate_ = codec_params->codec_inst.rate; - return SetBitRateSafe((uint32_t) my_rate_); -} - -int16_t ACMG729_1::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - if (WebRtcG7291_DecoderInit(decoder_inst_ptr_) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalInitDecoder: init decoder failed for G729_1"); - return -1; - } - return 0; -} - -int32_t ACMG729_1::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - if (!decoder_initialized_) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "CodeDef: Decoder uninitialized for G729_1"); - return -1; - } - - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_G729_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codec_def), kDecoderG729_1, codec_inst.pltype, - decoder_inst_ptr_, 16000); - SET_G729_1_FUNCTIONS((codec_def)); - return 0; -} - -ACMGenericCodec* ACMG729_1::CreateInstance(void) { - return NULL; -} - -int16_t ACMG729_1::InternalCreateEncoder() { - if (WebRtcG7291_Create(&encoder_inst_ptr_) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalCreateEncoder: create encoder failed for G729_1"); - return -1; - } - return 0; -} - -void ACMG729_1::DestructEncoderSafe() { - encoder_exist_ = false; - encoder_initialized_ = false; - if (encoder_inst_ptr_ != NULL) { - WebRtcG7291_Free(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } -} - -int16_t ACMG729_1::InternalCreateDecoder() { - if (WebRtcG7291_Create(&decoder_inst_ptr_) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalCreateDecoder: create decoder failed for G729_1"); - return -1; - } - return 0; -} - -void ACMG729_1::DestructDecoderSafe() { - decoder_exist_ = false; - decoder_initialized_ = false; - if (decoder_inst_ptr_ != NULL) { - WebRtcG7291_Free(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } -} - -void ACMG729_1::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - // WebRtcG7291_Free((G729_1_inst_t*)ptrInst); - } - return; -} - -int16_t ACMG729_1::SetBitRateSafe(const int32_t rate) { - // allowed rates: { 8000, 12000, 14000, 16000, 18000, 20000, - // 22000, 24000, 26000, 28000, 30000, 32000}; - // TODO(tlegrand): This check exists in one other place two. Should be - // possible to reuse code. - switch (rate) { - case 8000: { - my_rate_ = 8000; - break; - } - case 12000: { - my_rate_ = 12000; - break; - } - case 14000: { - my_rate_ = 14000; - break; - } - case 16000: { - my_rate_ = 16000; - break; - } - case 18000: { - my_rate_ = 18000; - break; - } - case 20000: { - my_rate_ = 20000; - break; - } - case 22000: { - my_rate_ = 22000; - break; - } - case 24000: { - my_rate_ = 24000; - break; - } - case 26000: { - my_rate_ = 26000; - break; - } - case 28000: { - my_rate_ = 28000; - break; - } - case 30000: { - my_rate_ = 30000; - break; - } - case 32000: { - my_rate_ = 32000; - break; - } - default: { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "SetBitRateSafe: Invalid rate G729_1"); - return -1; - } - } - - // Re-init with new rate - if (WebRtcG7291_EncoderInit(encoder_inst_ptr_, my_rate_, flag_8khz_, - flag_g729_mode_) >= 0) { - encoder_params_.codec_inst.rate = my_rate_; - return 0; - } else { - return -1; - } -} - -#endif - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7291.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7291.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7291.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g7291.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7291_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7291_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -// forward declaration -struct G729_1_inst_t_; -struct G729_1_inst_t_; - -namespace webrtc { - -namespace acm1 { - -class ACMG729_1 : public ACMGenericCodec { - public: - explicit ACMG729_1(int16_t codec_id); - ~ACMG729_1(); - - // for FEC - ACMGenericCodec* CreateInstance(void); - - int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte); - - int16_t InternalInitEncoder(WebRtcACMCodecParams *codec_params); - - int16_t InternalInitDecoder(WebRtcACMCodecParams *codec_params); - - protected: - int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type); - - int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - int16_t InternalCreateEncoder(); - - int16_t InternalCreateDecoder(); - - void InternalDestructEncoderInst(void* ptr_inst); - - int16_t SetBitRateSafe(const int32_t rate); - - G729_1_inst_t_* encoder_inst_ptr_; - G729_1_inst_t_* decoder_inst_ptr_; - - uint16_t my_rate_; - int16_t flag_8khz_; - int16_t flag_g729_mode_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G7291_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g729.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g729.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g729.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g729.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,366 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_g729.h" - -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/trace.h" - -#ifdef WEBRTC_CODEC_G729 -// NOTE! G.729 is not included in the open-source package. Modify this file -// or your codec API to match the function calls and names of used G.729 API -// file. -#include "g729_interface.h" -#endif - -namespace webrtc { - -namespace acm1 { - -#ifndef WEBRTC_CODEC_G729 - -ACMG729::ACMG729(int16_t /* codec_id */) -: encoder_inst_ptr_(NULL), - decoder_inst_ptr_(NULL) { - return; -} - -ACMG729::~ACMG729() { - return; -} - -int16_t ACMG729::InternalEncode( - uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - return -1; -} - -int16_t ACMG729::EnableDTX() { - return -1; -} - -int16_t ACMG729::DisableDTX() { - return -1; -} - -int32_t ACMG729::ReplaceInternalDTXSafe( - const bool /*replace_internal_dtx */) { - return -1; -} - -int32_t ACMG729::IsInternalDTXReplacedSafe( - bool* /* internal_dtx_replaced */) { - return -1; -} - -int16_t ACMG729::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return -1; -} - -int16_t ACMG729::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int16_t ACMG729::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int32_t ACMG729::CodecDef(WebRtcNetEQ_CodecDef& /* codec_def */, - const CodecInst& /* codec_inst */) { - return -1; -} - -ACMGenericCodec* ACMG729::CreateInstance(void) { - return NULL; -} - -int16_t ACMG729::InternalCreateEncoder() { - return -1; -} - -void ACMG729::DestructEncoderSafe() { - return; -} - -int16_t ACMG729::InternalCreateDecoder() { - return -1; -} - -void ACMG729::DestructDecoderSafe() { - return; -} - -void ACMG729::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - -#else //===================== Actual Implementation ======================= -ACMG729::ACMG729(int16_t codec_id) - : encoder_inst_ptr_(NULL), - decoder_inst_ptr_(NULL) { - codec_id_ = codec_id; - has_internal_dtx_ = true; - return; -} - -ACMG729::~ACMG729() { - if (encoder_inst_ptr_ != NULL) { - // Delete encoder memory - WebRtcG729_FreeEnc(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } - if (decoder_inst_ptr_ != NULL) { - // Delete decoder memory - WebRtcG729_FreeDec(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } - return; -} - -int16_t ACMG729::InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) { - // Initialize before entering the loop - int16_t num_encoded_samples = 0; - int16_t tmp_len_byte = 0; - int16_t vad_decision = 0; - *bitstream_len_byte = 0; - while (num_encoded_samples < frame_len_smpl_) { - // Call G.729 encoder with pointer to encoder memory, input - // audio, number of samples and bitsream - tmp_len_byte = WebRtcG729_Encode( - encoder_inst_ptr_, &in_audio_[in_audio_ix_read_], 80, - (int16_t*)(&(bitstream[*bitstream_len_byte]))); - - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - in_audio_ix_read_ += 80; - - // sanity check - if (tmp_len_byte < 0) { - // error has happened - *bitstream_len_byte = 0; - return -1; - } - - // increment number of written bytes - *bitstream_len_byte += tmp_len_byte; - switch (tmp_len_byte) { - case 0: { - if (0 == num_encoded_samples) { - // this is the first 10 ms in this packet and there is - // no data generated, perhaps DTX is enabled and the - // codec is not generating any bit-stream for this 10 ms. - // we do not continue encoding this frame. - return 0; - } - break; - } - case 2: { - // check if G.729 internal DTX is enabled - if (has_internal_dtx_ && dtx_enabled_) { - vad_decision = 0; - for (int16_t n = 0; n < MAX_FRAME_SIZE_10MSEC; n++) { - vad_label_[n] = vad_decision; - } - } - // we got a SID and have to send out this packet no matter - // how much audio we have encoded - return *bitstream_len_byte; - } - case 10: { - vad_decision = 1; - // this is a valid length just continue encoding - break; - } - default: { - return -1; - } - } - - // update number of encoded samples - num_encoded_samples += 80; - } - - // update VAD decision vector - if (has_internal_dtx_ && !vad_decision && dtx_enabled_) { - for (int16_t n = 0; n < MAX_FRAME_SIZE_10MSEC; n++) { - vad_label_[n] = vad_decision; - } - } - - // done encoding, return number of encoded bytes - return *bitstream_len_byte; -} - -int16_t ACMG729::EnableDTX() { - if (dtx_enabled_) { - // DTX already enabled, do nothing - return 0; - } else if (encoder_exist_) { - // Re-init the G.729 encoder to turn on DTX - if (WebRtcG729_EncoderInit(encoder_inst_ptr_, 1) < 0) { - return -1; - } - dtx_enabled_ = true; - return 0; - } else { - return -1; - } -} - -int16_t ACMG729::DisableDTX() { - if (!dtx_enabled_) { - // DTX already dissabled, do nothing - return 0; - } else if (encoder_exist_) { - // Re-init the G.729 decoder to turn off DTX - if (WebRtcG729_EncoderInit(encoder_inst_ptr_, 0) < 0) { - return -1; - } - dtx_enabled_ = false; - return 0; - } else { - // encoder doesn't exists, therefore disabling is harmless - return 0; - } -} - -int32_t ACMG729::ReplaceInternalDTXSafe(const bool replace_internal_dtx) { - // This function is used to disable the G.729 built in DTX and use an - // external instead. - - if (replace_internal_dtx == has_internal_dtx_) { - // Make sure we keep the DTX/VAD setting if possible - bool old_enable_dtx = dtx_enabled_; - bool old_enable_vad = vad_enabled_; - ACMVADMode old_mode = vad_mode_; - if (replace_internal_dtx) { - // Disable internal DTX before enabling external DTX - DisableDTX(); - } else { - // Disable external DTX before enabling internal - ACMGenericCodec::DisableDTX(); - } - has_internal_dtx_ = !replace_internal_dtx; - int16_t status = SetVADSafe(old_enable_dtx, old_enable_vad, old_mode); - // Check if VAD status has changed from inactive to active, or if error was - // reported - if (status == 1) { - vad_enabled_ = true; - return status; - } else if (status < 0) { - has_internal_dtx_ = replace_internal_dtx; - return -1; - } - } - return 0; -} - -int32_t ACMG729::IsInternalDTXReplacedSafe(bool* internal_dtx_replaced) { - // Get status of wether DTX is replaced or not - *internal_dtx_replaced = !has_internal_dtx_; - return 0; -} - -int16_t ACMG729::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - // This function is not used. G.729 decoder is called from inside NetEQ - return 0; -} - -int16_t ACMG729::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { - // Init G.729 encoder - return WebRtcG729_EncoderInit(encoder_inst_ptr_, - ((codec_params->enable_dtx) ? 1 : 0)); -} - -int16_t ACMG729::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - // Init G.729 decoder - return WebRtcG729_DecoderInit(decoder_inst_ptr_); -} - -int32_t ACMG729::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - if (!decoder_initialized_) { - // Todo: - // log error - return -1; - } - - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_G729_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codec_def), kDecoderG729, codec_inst.pltype, decoder_inst_ptr_, - 8000); - SET_G729_FUNCTIONS((codec_def)); - return 0; -} - -ACMGenericCodec* ACMG729::CreateInstance(void) { - // Function not used - return NULL; -} - -int16_t ACMG729::InternalCreateEncoder() { - // Create encoder memory - return WebRtcG729_CreateEnc(&encoder_inst_ptr_); -} - -void ACMG729::DestructEncoderSafe() { - // Free encoder memory - encoder_exist_ = false; - encoder_initialized_ = false; - if (encoder_inst_ptr_ != NULL) { - WebRtcG729_FreeEnc(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } -} - -int16_t ACMG729::InternalCreateDecoder() { - // Create decoder memory - return WebRtcG729_CreateDec(&decoder_inst_ptr_); -} - -void ACMG729::DestructDecoderSafe() { - // Free decoder memory - decoder_exist_ = false; - decoder_initialized_ = false; - if (decoder_inst_ptr_ != NULL) { - WebRtcG729_FreeDec(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } -} - -void ACMG729::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcG729_FreeEnc((G729_encinst_t_*) ptr_inst); - } - return; -} - -#endif - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g729.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g729.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g729.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_g729.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G729_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G729_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -// forward declaration -struct G729_encinst_t_; -struct G729_decinst_t_; - -namespace webrtc { - -namespace acm1 { - -class ACMG729 : public ACMGenericCodec { - public: - explicit ACMG729(int16_t codec_id); - ~ACMG729(); - - // for FEC - ACMGenericCodec* CreateInstance(void); - - int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte); - - int16_t InternalInitEncoder(WebRtcACMCodecParams *codec_params); - - int16_t InternalInitDecoder(WebRtcACMCodecParams *codec_params); - - protected: - int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type); - - int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - int16_t InternalCreateEncoder(); - - int16_t InternalCreateDecoder(); - - void InternalDestructEncoderInst(void* ptr_inst); - - int16_t EnableDTX(); - - int16_t DisableDTX(); - - int32_t ReplaceInternalDTXSafe(const bool replace_internal_dtx); - - int32_t IsInternalDTXReplacedSafe(bool* internal_dtx_replaced); - - G729_encinst_t_* encoder_inst_ptr_; - G729_decinst_t_* decoder_inst_ptr_; - -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_G729_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_generic_codec.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_generic_codec.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_generic_codec.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_generic_codec.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,1263 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -#include -#include - -#include "webrtc/common_audio/vad/include/webrtc_vad.h" -#include "webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h" -#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/system_wrappers/interface/trace.h" - -namespace webrtc { - -namespace acm1 { - -// Enum for CNG -enum { - kMaxPLCParamsCNG = WEBRTC_CNG_MAX_LPC_ORDER, - kNewCNGNumPLCParams = 8 -}; - -// Interval for sending new CNG parameters (SID frames) is 100 msec. -enum { - kCngSidIntervalMsec = 100 -}; - -// We set some of the variables to invalid values as a check point -// if a proper initialization has happened. Another approach is -// to initialize to a default codec that we are sure is always included. -ACMGenericCodec::ACMGenericCodec() - : in_audio_ix_write_(0), - in_audio_ix_read_(0), - in_timestamp_ix_write_(0), - in_audio_(NULL), - in_timestamp_(NULL), - frame_len_smpl_(-1), // invalid value - num_channels_(1), - codec_id_(-1), // invalid value - num_missed_samples_(0), - encoder_exist_(false), - decoder_exist_(false), - encoder_initialized_(false), - decoder_initialized_(false), - registered_in_neteq_(false), - has_internal_dtx_(false), - ptr_vad_inst_(NULL), - vad_enabled_(false), - vad_mode_(VADNormal), - dtx_enabled_(false), - ptr_dtx_inst_(NULL), - num_lpc_params_(kNewCNGNumPLCParams), - sent_cn_previous_(false), - is_master_(true), - prev_frame_cng_(0), - neteq_decode_lock_(NULL), - codec_wrapper_lock_(*RWLockWrapper::CreateRWLock()), - last_encoded_timestamp_(0), - last_timestamp_(0xD87F3F9F), - is_audio_buff_fresh_(true), - unique_id_(0) { - // Initialize VAD vector. - for (int i = 0; i < MAX_FRAME_SIZE_10MSEC; i++) { - vad_label_[i] = 0; - } - // Nullify memory for encoder and decoder, and set payload type to an - // invalid value. - memset(&encoder_params_, 0, sizeof(WebRtcACMCodecParams)); - encoder_params_.codec_inst.pltype = -1; - memset(&decoder_params_, 0, sizeof(WebRtcACMCodecParams)); - decoder_params_.codec_inst.pltype = -1; -} - -ACMGenericCodec::~ACMGenericCodec() { - // Check all the members which are pointers, and if they are not NULL - // delete/free them. - if (ptr_vad_inst_ != NULL) { - WebRtcVad_Free(ptr_vad_inst_); - ptr_vad_inst_ = NULL; - } - if (in_audio_ != NULL) { - delete[] in_audio_; - in_audio_ = NULL; - } - if (in_timestamp_ != NULL) { - delete[] in_timestamp_; - in_timestamp_ = NULL; - } - if (ptr_dtx_inst_ != NULL) { - WebRtcCng_FreeEnc(ptr_dtx_inst_); - ptr_dtx_inst_ = NULL; - } - delete &codec_wrapper_lock_; -} - -int32_t ACMGenericCodec::Add10MsData(const uint32_t timestamp, - const int16_t* data, - const uint16_t length_smpl, - const uint8_t audio_channel) { - WriteLockScoped wl(codec_wrapper_lock_); - return Add10MsDataSafe(timestamp, data, length_smpl, audio_channel); -} - -int32_t ACMGenericCodec::Add10MsDataSafe(const uint32_t timestamp, - const int16_t* data, - const uint16_t length_smpl, - const uint8_t audio_channel) { - // The codec expects to get data in correct sampling rate. Get the sampling - // frequency of the codec. - uint16_t plfreq_hz; - if (EncoderSampFreq(plfreq_hz) < 0) { - return -1; - } - - // Sanity check to make sure the length of the input corresponds to 10 ms. - if ((plfreq_hz / 100) != length_smpl) { - // This is not 10 ms of audio, given the sampling frequency of the codec. - return -1; - } - - if (last_timestamp_ == timestamp) { - // Same timestamp as the last time, overwrite. - if ((in_audio_ix_write_ >= length_smpl * audio_channel) && - (in_timestamp_ix_write_ > 0)) { - in_audio_ix_write_ -= length_smpl * audio_channel; - in_timestamp_ix_write_--; - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, unique_id_, - "Adding 10ms with previous timestamp, overwriting the " - "previous 10ms"); - } else { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, unique_id_, - "Adding 10ms with previous timestamp, this will sound bad"); - } - } - - last_timestamp_ = timestamp; - - // If the data exceeds the buffer size, we throw away the oldest data and - // add the newly received 10 msec at the end. - if ((in_audio_ix_write_ + length_smpl * audio_channel) > - AUDIO_BUFFER_SIZE_W16) { - // Get the number of samples to be overwritten. - int16_t missed_samples = in_audio_ix_write_ + length_smpl * audio_channel - - AUDIO_BUFFER_SIZE_W16; - - // Move the data (overwrite the old data). - memmove(in_audio_, in_audio_ + missed_samples, - (AUDIO_BUFFER_SIZE_W16 - length_smpl * audio_channel) * - sizeof(int16_t)); - - // Copy the new data. - memcpy(in_audio_ + (AUDIO_BUFFER_SIZE_W16 - length_smpl * audio_channel), - data, length_smpl * audio_channel * sizeof(int16_t)); - - // Get the number of 10 ms blocks which are overwritten. - int16_t missed_10ms_blocks =static_cast( - (missed_samples / audio_channel * 100) / plfreq_hz); - - // Move the timestamps. - memmove(in_timestamp_, in_timestamp_ + missed_10ms_blocks, - (in_timestamp_ix_write_ - missed_10ms_blocks) * sizeof(uint32_t)); - in_timestamp_ix_write_ -= missed_10ms_blocks; - assert(in_timestamp_ix_write_ >= 0); - in_timestamp_[in_timestamp_ix_write_] = timestamp; - in_timestamp_ix_write_++; - - // Buffer is full. - in_audio_ix_write_ = AUDIO_BUFFER_SIZE_W16; - IncreaseNoMissedSamples(missed_samples); - is_audio_buff_fresh_ = false; - return -missed_samples; - } - - // Store the input data in our data buffer. - memcpy(in_audio_ + in_audio_ix_write_, data, - length_smpl * audio_channel * sizeof(int16_t)); - in_audio_ix_write_ += length_smpl * audio_channel; - - assert(in_timestamp_ix_write_ < TIMESTAMP_BUFFER_SIZE_W32); - assert(in_timestamp_ix_write_ >= 0); - - in_timestamp_[in_timestamp_ix_write_] = timestamp; - in_timestamp_ix_write_++; - is_audio_buff_fresh_ = false; - return 0; -} - -bool ACMGenericCodec::HasFrameToEncode() const { - ReadLockScoped lockCodec(codec_wrapper_lock_); - if (in_audio_ix_write_ < frame_len_smpl_ * num_channels_) - return false; - return true; -} - -int16_t ACMGenericCodec::Encode(uint8_t* bitstream, - int16_t* bitstream_len_byte, - uint32_t* timestamp, - WebRtcACMEncodingType* encoding_type) { - if (!HasFrameToEncode()) { - // There is not enough audio - *timestamp = 0; - *bitstream_len_byte = 0; - // Doesn't really matter what this parameter set to - *encoding_type = kNoEncoding; - return 0; - } - WriteLockScoped lockCodec(codec_wrapper_lock_); - ReadLockScoped lockNetEq(*neteq_decode_lock_); - - // Not all codecs accept the whole frame to be pushed into encoder at once. - // Some codecs needs to be feed with a specific number of samples different - // from the frame size. If this is the case, |myBasicCodingBlockSmpl| will - // report a number different from 0, and we will loop over calls to encoder - // further down, until we have encode a complete frame. - const int16_t my_basic_coding_block_smpl = - ACMCodecDB::BasicCodingBlock(codec_id_); - if (my_basic_coding_block_smpl < 0 || !encoder_initialized_ || - !encoder_exist_) { - // This should not happen, but in case it does, report no encoding done. - *timestamp = 0; - *bitstream_len_byte = 0; - *encoding_type = kNoEncoding; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "EncodeSafe: error, basic coding sample block is negative"); - return -1; - } - // This makes the internal encoder read from the beginning of the buffer. - in_audio_ix_read_ = 0; - *timestamp = in_timestamp_[0]; - - // Process the audio through VAD. The function will set |_vad_labels|. - // If VAD is disabled all entries in |_vad_labels| are set to ONE (active). - int16_t status = 0; - int16_t dtx_processed_samples = 0; - status = ProcessFrameVADDTX(bitstream, bitstream_len_byte, - &dtx_processed_samples); - if (status < 0) { - *timestamp = 0; - *bitstream_len_byte = 0; - *encoding_type = kNoEncoding; - } else { - if (dtx_processed_samples > 0) { - // Dtx have processed some samples, and even if a bit-stream is generated - // we should not do any encoding (normally there won't be enough data). - - // Setting the following makes sure that the move of audio data and - // timestamps done correctly. - in_audio_ix_read_ = dtx_processed_samples; - // This will let the owner of ACMGenericCodec to know that the - // generated bit-stream is DTX to use correct payload type. - uint16_t samp_freq_hz; - EncoderSampFreq(samp_freq_hz); - if (samp_freq_hz == 8000) { - *encoding_type = kPassiveDTXNB; - } else if (samp_freq_hz == 16000) { - *encoding_type = kPassiveDTXWB; - } else if (samp_freq_hz == 32000) { - *encoding_type = kPassiveDTXSWB; - } else if (samp_freq_hz == 48000) { - *encoding_type = kPassiveDTXFB; - } else { - status = -1; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "EncodeSafe: Wrong sampling frequency for DTX."); - } - - // Transport empty frame if we have an empty bitstream. - if ((*bitstream_len_byte == 0) && - (sent_cn_previous_ || - ((in_audio_ix_write_ - in_audio_ix_read_) <= 0))) { - // Makes sure we transmit an empty frame. - *bitstream_len_byte = 1; - *encoding_type = kNoEncoding; - } - sent_cn_previous_ = true; - } else { - // We should encode the audio frame. Either VAD and/or DTX is off, or the - // audio was considered "active". - - sent_cn_previous_ = false; - if (my_basic_coding_block_smpl == 0) { - // This codec can handle all allowed frame sizes as basic coding block. - status = InternalEncode(bitstream, bitstream_len_byte); - if (status < 0) { - // TODO(tlegrand): Maybe reseting the encoder to be fresh for the next - // frame. - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, - unique_id_, "EncodeSafe: error in internal_encode"); - *bitstream_len_byte = 0; - *encoding_type = kNoEncoding; - } - } else { - // A basic-coding-block for this codec is defined so we loop over the - // audio with the steps of the basic-coding-block. - int16_t tmp_bitstream_len_byte; - - // Reset the variables which will be incremented in the loop. - *bitstream_len_byte = 0; - do { - status = InternalEncode(&bitstream[*bitstream_len_byte], - &tmp_bitstream_len_byte); - *bitstream_len_byte += tmp_bitstream_len_byte; - - // Guard Against errors and too large payloads. - if ((status < 0) || (*bitstream_len_byte > MAX_PAYLOAD_SIZE_BYTE)) { - // Error has happened, and even if we are in the middle of a full - // frame we have to exit. Before exiting, whatever bits are in the - // buffer are probably corrupted, so we ignore them. - *bitstream_len_byte = 0; - *encoding_type = kNoEncoding; - // We might have come here because of the second condition. - status = -1; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, - unique_id_, "EncodeSafe: error in InternalEncode"); - // break from the loop - break; - } - } while (in_audio_ix_read_ < frame_len_smpl_ * num_channels_); - } - if (status >= 0) { - *encoding_type = (vad_label_[0] == 1) ? kActiveNormalEncoded : - kPassiveNormalEncoded; - // Transport empty frame if we have an empty bitstream. - if ((*bitstream_len_byte == 0) && - ((in_audio_ix_write_ - in_audio_ix_read_) <= 0)) { - // Makes sure we transmit an empty frame. - *bitstream_len_byte = 1; - *encoding_type = kNoEncoding; - } - } - } - } - - // Move the timestamp buffer according to the number of 10 ms blocks - // which are read. - uint16_t samp_freq_hz; - EncoderSampFreq(samp_freq_hz); - int16_t num_10ms_blocks = static_cast( - (in_audio_ix_read_ / num_channels_ * 100) / samp_freq_hz); - if (in_timestamp_ix_write_ > num_10ms_blocks) { - memmove(in_timestamp_, in_timestamp_ + num_10ms_blocks, - (in_timestamp_ix_write_ - num_10ms_blocks) * sizeof(int32_t)); - } - in_timestamp_ix_write_ -= num_10ms_blocks; - assert(in_timestamp_ix_write_ >= 0); - // Remove encoded audio and move next audio to be encoded to the beginning - // of the buffer. Accordingly, adjust the read and write indices. - if (in_audio_ix_read_ < in_audio_ix_write_) { - memmove(in_audio_, &in_audio_[in_audio_ix_read_], - (in_audio_ix_write_ - in_audio_ix_read_) * sizeof(int16_t)); - } - in_audio_ix_write_ -= in_audio_ix_read_; - assert(in_timestamp_ix_write_ >= 0); - in_audio_ix_read_ = 0; - last_encoded_timestamp_ = *timestamp; - return (status < 0) ? (-1) : (*bitstream_len_byte); -} - -int16_t ACMGenericCodec::Decode(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type) { - WriteLockScoped wl(codec_wrapper_lock_); - return DecodeSafe(bitstream, bitstream_len_byte, audio, audio_samples, - speech_type); -} - -bool ACMGenericCodec::EncoderInitialized() { - ReadLockScoped rl(codec_wrapper_lock_); - return encoder_initialized_; -} - -bool ACMGenericCodec::DecoderInitialized() { - ReadLockScoped rl(codec_wrapper_lock_); - return decoder_initialized_; -} - -int32_t ACMGenericCodec::RegisterInNetEq(ACMNetEQ* neteq, - const CodecInst& codec_inst) { - WebRtcNetEQ_CodecDef codec_def; - WriteLockScoped wl(codec_wrapper_lock_); - - if (CodecDef(codec_def, codec_inst) < 0) { - // Failed to register the decoder. - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "RegisterInNetEq: error, failed to register"); - registered_in_neteq_ = false; - return -1; - } else { - if (neteq->AddCodec(&codec_def, is_master_) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "RegisterInNetEq: error, failed to add codec"); - registered_in_neteq_ = false; - return -1; - } - // Succeeded registering the decoder. - registered_in_neteq_ = true; - return 0; - } -} - -int16_t ACMGenericCodec::EncoderParams(WebRtcACMCodecParams* enc_params) { - ReadLockScoped rl(codec_wrapper_lock_); - return EncoderParamsSafe(enc_params); -} - -int16_t ACMGenericCodec::EncoderParamsSafe(WebRtcACMCodecParams* enc_params) { - // Codec parameters are valid only if the encoder is initialized. - if (encoder_initialized_) { - int32_t current_rate; - memcpy(enc_params, &encoder_params_, sizeof(WebRtcACMCodecParams)); - current_rate = enc_params->codec_inst.rate; - CurrentRate(current_rate); - enc_params->codec_inst.rate = current_rate; - return 0; - } else { - enc_params->codec_inst.plname[0] = '\0'; - enc_params->codec_inst.pltype = -1; - enc_params->codec_inst.pacsize = 0; - enc_params->codec_inst.rate = 0; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "EncoderParamsSafe: error, encoder not initialized"); - return -1; - } -} - -bool ACMGenericCodec::DecoderParams(WebRtcACMCodecParams* dec_params, - const uint8_t payload_type) { - ReadLockScoped rl(codec_wrapper_lock_); - return DecoderParamsSafe(dec_params, payload_type); -} - -bool ACMGenericCodec::DecoderParamsSafe(WebRtcACMCodecParams* dec_params, - const uint8_t payload_type) { - // Decoder parameters are valid only if decoder is initialized. - if (decoder_initialized_) { - if (payload_type == decoder_params_.codec_inst.pltype) { - memcpy(dec_params, &decoder_params_, sizeof(WebRtcACMCodecParams)); - return true; - } - } - - dec_params->codec_inst.plname[0] = '\0'; - dec_params->codec_inst.pltype = -1; - dec_params->codec_inst.pacsize = 0; - dec_params->codec_inst.rate = 0; - return false; -} - -int16_t ACMGenericCodec::ResetEncoder() { - WriteLockScoped lockCodec(codec_wrapper_lock_); - ReadLockScoped lockNetEq(*neteq_decode_lock_); - return ResetEncoderSafe(); -} - -int16_t ACMGenericCodec::ResetEncoderSafe() { - if (!encoder_exist_ || !encoder_initialized_) { - // We don't reset if encoder doesn't exists or isn't initialized yet. - return 0; - } - - in_audio_ix_write_ = 0; - in_audio_ix_read_ = 0; - in_timestamp_ix_write_ = 0; - num_missed_samples_ = 0; - is_audio_buff_fresh_ = true; - memset(in_audio_, 0, AUDIO_BUFFER_SIZE_W16 * sizeof(int16_t)); - memset(in_timestamp_, 0, TIMESTAMP_BUFFER_SIZE_W32 * sizeof(int32_t)); - - // Store DTX/VAD parameters. - bool enable_vad = vad_enabled_; - bool enable_dtx = dtx_enabled_; - ACMVADMode mode = vad_mode_; - - // Reset the encoder. - if (InternalResetEncoder() < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "ResetEncoderSafe: error in reset encoder"); - return -1; - } - - // Disable DTX & VAD to delete the states and have a fresh start. - DisableDTX(); - DisableVAD(); - - // Set DTX/VAD. - int status = SetVADSafe(&enable_dtx, &enable_vad, &mode); - dtx_enabled_ = enable_dtx; - vad_enabled_ = enable_vad; - vad_mode_ = mode; - return status; -} - -int16_t ACMGenericCodec::InternalResetEncoder() { - // Call the codecs internal encoder initialization/reset function. - return InternalInitEncoder(&encoder_params_); -} - -int16_t ACMGenericCodec::InitEncoder(WebRtcACMCodecParams* codec_params, - bool force_initialization) { - WriteLockScoped lockCodec(codec_wrapper_lock_); - ReadLockScoped lockNetEq(*neteq_decode_lock_); - return InitEncoderSafe(codec_params, force_initialization); -} - -int16_t ACMGenericCodec::InitEncoderSafe(WebRtcACMCodecParams* codec_params, - bool force_initialization) { - // Check if we got a valid set of parameters. - int mirrorID; - int codec_number = ACMCodecDB::CodecNumber(&(codec_params->codec_inst), - &mirrorID); - if (codec_number < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InitEncoderSafe: error, codec number negative"); - return -1; - } - // Check if the parameters are for this codec. - if ((codec_id_ >= 0) && (codec_id_ != codec_number) && - (codec_id_ != mirrorID)) { - // The current codec is not the same as the one given by codec_params. - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InitEncoderSafe: current codec is not the same as the one " - "given by codec_params"); - return -1; - } - - if (!CanChangeEncodingParam(codec_params->codec_inst)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InitEncoderSafe: cannot change encoding parameters"); - return -1; - } - - if (encoder_initialized_ && !force_initialization) { - // The encoder is already initialized, and we don't want to force - // initialization. - return 0; - } - int16_t status; - if (!encoder_exist_) { - // New encoder, start with creating. - encoder_initialized_ = false; - status = CreateEncoder(); - if (status < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InitEncoderSafe: cannot create encoder"); - return -1; - } else { - encoder_exist_ = true; - } - } - frame_len_smpl_ = (codec_params->codec_inst).pacsize; - num_channels_ = codec_params->codec_inst.channels; - status = InternalInitEncoder(codec_params); - if (status < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InitEncoderSafe: error in init encoder"); - encoder_initialized_ = false; - return -1; - } else { - // Store encoder parameters. - memcpy(&encoder_params_, codec_params, sizeof(WebRtcACMCodecParams)); - encoder_initialized_ = true; - if (in_audio_ == NULL) { - in_audio_ = new int16_t[AUDIO_BUFFER_SIZE_W16]; - if (in_audio_ == NULL) { - return -1; - } - } - if (in_timestamp_ == NULL) { - in_timestamp_ = new uint32_t[TIMESTAMP_BUFFER_SIZE_W32]; - if (in_timestamp_ == NULL) { - return -1; - } - } - // Fresh start for audio buffer. - is_audio_buff_fresh_ = true; - memset(in_audio_, 0, AUDIO_BUFFER_SIZE_W16 * sizeof(int16_t)); - memset(in_timestamp_, 0, sizeof(uint32_t) * TIMESTAMP_BUFFER_SIZE_W32); - in_audio_ix_write_ = 0; - in_audio_ix_read_ = 0; - in_timestamp_ix_write_ = 0; - } - status = SetVADSafe(&codec_params->enable_dtx, &codec_params->enable_vad, - &codec_params->vad_mode); - return status; -} - -// TODO(tlegrand): Remove the function CanChangeEncodingParam. Returns true -// for all codecs. -bool ACMGenericCodec::CanChangeEncodingParam(CodecInst& /*codec_inst*/) { - return true; -} - -void ACMGenericCodec::CurrentRate(int32_t& /* rate_bps */) { - return; -} - -int16_t ACMGenericCodec::InitDecoder(WebRtcACMCodecParams* codec_params, - bool force_initialization) { - WriteLockScoped lockCodc(codec_wrapper_lock_); - WriteLockScoped lockNetEq(*neteq_decode_lock_); - return InitDecoderSafe(codec_params, force_initialization); -} - -int16_t ACMGenericCodec::InitDecoderSafe(WebRtcACMCodecParams* codec_params, - bool force_initialization) { - int mirror_id; - // Check if we got a valid set of parameters. - int codec_number = ACMCodecDB::ReceiverCodecNumber(&codec_params->codec_inst, - &mirror_id); - if (codec_number < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InitDecoderSafe: error, invalid codec number"); - return -1; - } - // Check if the parameters are for this codec. - if ((codec_id_ >= 0) && (codec_id_ != codec_number) && - (codec_id_ != mirror_id)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InitDecoderSafe: current codec is not the same as the one " - "given by codec_params"); - // The current codec is not the same as the one given by codec_params. - return -1; - } - - if (decoder_initialized_ && !force_initialization) { - // The decoder is already initialized, and we don't want to force - // initialization. - return 0; - } - - int16_t status; - if (!decoder_exist_) { - // New decoder, start with creating. - decoder_initialized_ = false; - status = CreateDecoder(); - if (status < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InitDecoderSafe: cannot create decoder"); - return -1; - } else { - decoder_exist_ = true; - } - } - - status = InternalInitDecoder(codec_params); - if (status < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InitDecoderSafe: cannot init decoder"); - decoder_initialized_ = false; - return -1; - } else { - // Store decoder parameters. - SaveDecoderParamSafe(codec_params); - decoder_initialized_ = true; - } - return 0; -} - -int16_t ACMGenericCodec::ResetDecoder(int16_t payload_type) { - WriteLockScoped lockCodec(codec_wrapper_lock_); - WriteLockScoped lockNetEq(*neteq_decode_lock_); - return ResetDecoderSafe(payload_type); -} - -int16_t ACMGenericCodec::ResetDecoderSafe(int16_t payload_type) { - WebRtcACMCodecParams decoder_params; - if (!decoder_exist_ || !decoder_initialized_) { - return 0; - } - // Initialization of the decoder should work for all the codec. For codecs - // that needs to keep some states an overloading implementation of - // |DecoderParamsSafe| exists. - DecoderParamsSafe(&decoder_params, static_cast(payload_type)); - return InternalInitDecoder(&decoder_params); -} - -void ACMGenericCodec::ResetNoMissedSamples() { - WriteLockScoped cs(codec_wrapper_lock_); - num_missed_samples_ = 0; -} - -void ACMGenericCodec::IncreaseNoMissedSamples(const int16_t num_samples) { - num_missed_samples_ += num_samples; -} - -// Get the number of missed samples, this can be public. -uint32_t ACMGenericCodec::NoMissedSamples() const { - ReadLockScoped cs(codec_wrapper_lock_); - return num_missed_samples_; -} - -void ACMGenericCodec::DestructEncoder() { - WriteLockScoped wl(codec_wrapper_lock_); - - // Disable VAD and delete the instance. - if (ptr_vad_inst_ != NULL) { - WebRtcVad_Free(ptr_vad_inst_); - ptr_vad_inst_ = NULL; - } - vad_enabled_ = false; - vad_mode_ = VADNormal; - - // Disable DTX and delete the instance. - dtx_enabled_ = false; - if (ptr_dtx_inst_ != NULL) { - WebRtcCng_FreeEnc(ptr_dtx_inst_); - ptr_dtx_inst_ = NULL; - } - num_lpc_params_ = kNewCNGNumPLCParams; - - DestructEncoderSafe(); -} - -void ACMGenericCodec::DestructDecoder() { - WriteLockScoped wl(codec_wrapper_lock_); - decoder_params_.codec_inst.pltype = -1; - DestructDecoderSafe(); -} - -int16_t ACMGenericCodec::SetBitRate(const int32_t bitrate_bps) { - WriteLockScoped wl(codec_wrapper_lock_); - return SetBitRateSafe(bitrate_bps); -} - -int16_t ACMGenericCodec::SetBitRateSafe(const int32_t bitrate_bps) { - // If the codec can change the bit-rate this function is overloaded. - // Otherwise the only acceptable value is the one that is in the database. - CodecInst codec_params; - if (ACMCodecDB::Codec(codec_id_, &codec_params) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "SetBitRateSafe: error in ACMCodecDB::Codec"); - return -1; - } - if (codec_params.rate != bitrate_bps) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "SetBitRateSafe: rate value is not acceptable"); - return -1; - } else { - return 0; - } -} - -// iSAC specific functions: -int32_t ACMGenericCodec::GetEstimatedBandwidth() { - WriteLockScoped wl(codec_wrapper_lock_); - return GetEstimatedBandwidthSafe(); -} - -int32_t ACMGenericCodec::GetEstimatedBandwidthSafe() { - // All codecs but iSAC will return -1. - return -1; -} - -int32_t ACMGenericCodec::SetEstimatedBandwidth(int32_t estimated_bandwidth) { - WriteLockScoped wl(codec_wrapper_lock_); - return SetEstimatedBandwidthSafe(estimated_bandwidth); -} - -int32_t ACMGenericCodec::SetEstimatedBandwidthSafe( - int32_t /*estimated_bandwidth*/) { - // All codecs but iSAC will return -1. - return -1; -} -// End of iSAC specific functions. - -int32_t ACMGenericCodec::GetRedPayload(uint8_t* red_payload, - int16_t* payload_bytes) { - WriteLockScoped wl(codec_wrapper_lock_); - return GetRedPayloadSafe(red_payload, payload_bytes); -} - -int32_t ACMGenericCodec::GetRedPayloadSafe(uint8_t* /* red_payload */, - int16_t* /* payload_bytes */) { - return -1; // Do nothing by default. -} - -int16_t ACMGenericCodec::CreateEncoder() { - int16_t status = 0; - if (!encoder_exist_) { - status = InternalCreateEncoder(); - // We just created the codec and obviously it is not initialized. - encoder_initialized_ = false; - } - if (status < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "CreateEncoder: error in internal create encoder"); - encoder_exist_ = false; - } else { - encoder_exist_ = true; - } - return status; -} - -int16_t ACMGenericCodec::CreateDecoder() { - int16_t status = 0; - if (!decoder_exist_) { - status = InternalCreateDecoder(); - // Decoder just created and obviously it is not initialized. - decoder_initialized_ = false; - } - if (status < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "CreateDecoder: error in internal create decoder"); - decoder_exist_ = false; - } else { - decoder_exist_ = true; - } - return status; -} - -void ACMGenericCodec::DestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WriteLockScoped lockCodec(codec_wrapper_lock_); - ReadLockScoped lockNetEq(*neteq_decode_lock_); - InternalDestructEncoderInst(ptr_inst); - } -} - -// Get the current audio buffer including read and write states, and timestamps. -int16_t ACMGenericCodec::AudioBuffer(WebRtcACMAudioBuff& audio_buff) { - ReadLockScoped cs(codec_wrapper_lock_); - memcpy(audio_buff.in_audio, in_audio_, - AUDIO_BUFFER_SIZE_W16 * sizeof(int16_t)); - audio_buff.in_audio_ix_read = in_audio_ix_read_; - audio_buff.in_audio_ix_write = in_audio_ix_write_; - memcpy(audio_buff.in_timestamp, in_timestamp_, - TIMESTAMP_BUFFER_SIZE_W32 * sizeof(uint32_t)); - audio_buff.in_timestamp_ix_write = in_timestamp_ix_write_; - audio_buff.last_timestamp = last_timestamp_; - return 0; -} - -// Set the audio buffer. -int16_t ACMGenericCodec::SetAudioBuffer(WebRtcACMAudioBuff& audio_buff) { - WriteLockScoped cs(codec_wrapper_lock_); - memcpy(in_audio_, audio_buff.in_audio, - AUDIO_BUFFER_SIZE_W16 * sizeof(int16_t)); - in_audio_ix_read_ = audio_buff.in_audio_ix_read; - in_audio_ix_write_ = audio_buff.in_audio_ix_write; - memcpy(in_timestamp_, audio_buff.in_timestamp, - TIMESTAMP_BUFFER_SIZE_W32 * sizeof(uint32_t)); - in_timestamp_ix_write_ = audio_buff.in_timestamp_ix_write; - last_timestamp_ = audio_buff.last_timestamp; - is_audio_buff_fresh_ = false; - return 0; -} - -uint32_t ACMGenericCodec::LastEncodedTimestamp() const { - ReadLockScoped cs(codec_wrapper_lock_); - return last_encoded_timestamp_; -} - -uint32_t ACMGenericCodec::EarliestTimestamp() const { - ReadLockScoped cs(codec_wrapper_lock_); - return in_timestamp_[0]; -} - -int16_t ACMGenericCodec::SetVAD(bool* enable_dtx, bool* enable_vad, - ACMVADMode* mode) { - WriteLockScoped cs(codec_wrapper_lock_); - return SetVADSafe(enable_dtx, enable_vad, mode); -} - -int16_t ACMGenericCodec::SetVADSafe(bool* enable_dtx, bool* enable_vad, - ACMVADMode* mode) { - if (!STR_CASE_CMP(encoder_params_.codec_inst.plname, "OPUS") || - encoder_params_.codec_inst.channels == 2 ) { - // VAD/DTX is not supported for Opus (even if sending mono), or other - // stereo codecs. - DisableDTX(); - DisableVAD(); - *enable_dtx = false; - *enable_vad = false; - return 0; - } - - if (*enable_dtx) { - // Make G729 AnnexB a special case. - if (!STR_CASE_CMP(encoder_params_.codec_inst.plname, "G729") - && !has_internal_dtx_) { - if (ACMGenericCodec::EnableDTX() < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "SetVADSafe: error in enable DTX"); - *enable_dtx = false; - *enable_vad = vad_enabled_; - return -1; - } - } else { - if (EnableDTX() < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "SetVADSafe: error in enable DTX"); - *enable_dtx = false; - *enable_vad = vad_enabled_; - return -1; - } - } - - // If codec does not have internal DTX (normal case) enabling DTX requires - // an active VAD. '*enable_dtx == true' overwrites VAD status. - // If codec has internal DTX, practically we don't need WebRtc VAD, however, - // we let the user to turn it on if they need call-backs on silence. - if (!has_internal_dtx_) { - // DTX is enabled, and VAD will be activated. - *enable_vad = true; - } - } else { - // Make G729 AnnexB a special case. - if (!STR_CASE_CMP(encoder_params_.codec_inst.plname, "G729") - && !has_internal_dtx_) { - ACMGenericCodec::DisableDTX(); - *enable_dtx = false; - } else { - DisableDTX(); - *enable_dtx = false; - } - } - - int16_t status = (*enable_vad) ? EnableVAD(*mode) : DisableVAD(); - if (status < 0) { - // Failed to set VAD, disable DTX. - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "SetVADSafe: error in enable VAD"); - DisableDTX(); - *enable_dtx = false; - *enable_vad = false; - } - return status; -} - -int16_t ACMGenericCodec::EnableDTX() { - if (has_internal_dtx_) { - // We should not be here if we have internal DTX this function should be - // overloaded by the derived class in this case. - return -1; - } - if (!dtx_enabled_) { - if (WebRtcCng_CreateEnc(&ptr_dtx_inst_) < 0) { - ptr_dtx_inst_ = NULL; - return -1; - } - uint16_t freq_hz; - EncoderSampFreq(freq_hz); - if (WebRtcCng_InitEnc(ptr_dtx_inst_, freq_hz, kCngSidIntervalMsec, - num_lpc_params_) < 0) { - // Couldn't initialize, has to return -1, and free the memory. - WebRtcCng_FreeEnc(ptr_dtx_inst_); - ptr_dtx_inst_ = NULL; - return -1; - } - dtx_enabled_ = true; - } - return 0; -} - -int16_t ACMGenericCodec::DisableDTX() { - if (has_internal_dtx_) { - // We should not be here if we have internal DTX this function should be - // overloaded by the derived class in this case. - return -1; - } - if (ptr_dtx_inst_ != NULL) { - WebRtcCng_FreeEnc(ptr_dtx_inst_); - ptr_dtx_inst_ = NULL; - } - dtx_enabled_ = false; - return 0; -} - -int16_t ACMGenericCodec::EnableVAD(ACMVADMode mode) { - if ((mode < VADNormal) || (mode > VADVeryAggr)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "EnableVAD: error in VAD mode range"); - return -1; - } - - if (!vad_enabled_) { - if (WebRtcVad_Create(&ptr_vad_inst_) < 0) { - ptr_vad_inst_ = NULL; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "EnableVAD: error in create VAD"); - return -1; - } - if (WebRtcVad_Init(ptr_vad_inst_) < 0) { - WebRtcVad_Free(ptr_vad_inst_); - ptr_vad_inst_ = NULL; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "EnableVAD: error in init VAD"); - return -1; - } - } - - // Set the VAD mode to the given value. - if (WebRtcVad_set_mode(ptr_vad_inst_, mode) < 0) { - // We failed to set the mode and we have to return -1. If we already have a - // working VAD (vad_enabled_ == true) then we leave it to work. Otherwise, - // the following will be executed. - if (!vad_enabled_) { - // We just created the instance but cannot set the mode we have to free - // the memory. - WebRtcVad_Free(ptr_vad_inst_); - ptr_vad_inst_ = NULL; - } - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, unique_id_, - "EnableVAD: failed to set the VAD mode"); - return -1; - } - vad_mode_ = mode; - vad_enabled_ = true; - return 0; -} - -int16_t ACMGenericCodec::DisableVAD() { - if (ptr_vad_inst_ != NULL) { - WebRtcVad_Free(ptr_vad_inst_); - ptr_vad_inst_ = NULL; - } - vad_enabled_ = false; - return 0; -} - -int32_t ACMGenericCodec::ReplaceInternalDTX(const bool replace_internal_dtx) { - WriteLockScoped cs(codec_wrapper_lock_); - return ReplaceInternalDTXSafe(replace_internal_dtx); -} - -int32_t ACMGenericCodec::ReplaceInternalDTXSafe( - const bool /* replace_internal_dtx */) { - return -1; -} - -int32_t ACMGenericCodec::IsInternalDTXReplaced(bool* internal_dtx_replaced) { - WriteLockScoped cs(codec_wrapper_lock_); - return IsInternalDTXReplacedSafe(internal_dtx_replaced); -} - -int32_t ACMGenericCodec::IsInternalDTXReplacedSafe( - bool* internal_dtx_replaced) { - *internal_dtx_replaced = false; - return 0; -} - -int16_t ACMGenericCodec::ProcessFrameVADDTX(uint8_t* bitstream, - int16_t* bitstream_len_byte, - int16_t* samples_processed) { - if (!vad_enabled_) { - // VAD not enabled, set all |vad_lable_[]| to 1 (speech detected). - for (int n = 0; n < MAX_FRAME_SIZE_10MSEC; n++) { - vad_label_[n] = 1; - } - *samples_processed = 0; - return 0; - } - - uint16_t freq_hz; - EncoderSampFreq(freq_hz); - - // Calculate number of samples in 10 ms blocks, and number ms in one frame. - int16_t samples_in_10ms = static_cast(freq_hz / 100); - int32_t frame_len_ms = static_cast(frame_len_smpl_) * 1000 / freq_hz; - int16_t status; - - // Vector for storing maximum 30 ms of mono audio at 48 kHz. - int16_t audio[1440]; - - // Calculate number of VAD-blocks to process, and number of samples in each - // block. - int num_samples_to_process[2]; - if (frame_len_ms == 40) { - // 20 ms in each VAD block. - num_samples_to_process[0] = num_samples_to_process[1] = 2 * samples_in_10ms; - } else { - // For 10-30 ms framesizes, second VAD block will be size zero ms, - // for 50 and 60 ms first VAD block will be 30 ms. - num_samples_to_process[0] = - (frame_len_ms > 30) ? 3 * samples_in_10ms : frame_len_smpl_; - num_samples_to_process[1] = frame_len_smpl_ - num_samples_to_process[0]; - } - - int offset = 0; - int loops = (num_samples_to_process[1] > 0) ? 2 : 1; - for (int i = 0; i < loops; i++) { - // TODO(turajs): Do we need to care about VAD together with stereo? - // If stereo, calculate mean of the two channels. - if (num_channels_ == 2) { - for (int j = 0; j < num_samples_to_process[i]; j++) { - audio[j] = (in_audio_[(offset + j) * 2] + - in_audio_[(offset + j) * 2 + 1]) / 2; - } - offset = num_samples_to_process[0]; - } else { - // Mono, copy data from in_audio_ to continue work on. - memcpy(audio, in_audio_, sizeof(int16_t) * num_samples_to_process[i]); - } - - // Call VAD. - status = static_cast(WebRtcVad_Process(ptr_vad_inst_, - static_cast(freq_hz), - audio, - num_samples_to_process[i])); - vad_label_[i] = status; - - if (status < 0) { - // This will force that the data be removed from the buffer. - *samples_processed += num_samples_to_process[i]; - return -1; - } - - // If VAD decision non-active, update DTX. NOTE! We only do this if the - // first part of a frame gets the VAD decision "inactive". Otherwise DTX - // might say it is time to transmit SID frame, but we will encode the whole - // frame, because the first part is active. - *samples_processed = 0; - if ((status == 0) && (i == 0) && dtx_enabled_ && !has_internal_dtx_) { - int16_t bitstream_len; - int num_10ms_frames = num_samples_to_process[i] / samples_in_10ms; - *bitstream_len_byte = 0; - for (int n = 0; n < num_10ms_frames; n++) { - // This block is (passive) && (vad enabled). If first CNG after - // speech, force SID by setting last parameter to "1". - status = WebRtcCng_Encode(ptr_dtx_inst_, &audio[n * samples_in_10ms], - samples_in_10ms, bitstream, &bitstream_len, - !prev_frame_cng_); - if (status < 0) { - return -1; - } - - // Update previous frame was CNG. - prev_frame_cng_ = 1; - - *samples_processed += samples_in_10ms * num_channels_; - - // |bitstream_len_byte| will only be > 0 once per 100 ms. - *bitstream_len_byte += bitstream_len; - } - - // Check if all samples got processed by the DTX. - if (*samples_processed != num_samples_to_process[i] * num_channels_) { - // Set to zero since something went wrong. Shouldn't happen. - *samples_processed = 0; - } - } else { - // Update previous frame was not CNG. - prev_frame_cng_ = 0; - } - - if (*samples_processed > 0) { - // The block contains inactive speech, and is processed by DTX. - // Discontinue running VAD. - break; - } - } - - return status; -} - -int16_t ACMGenericCodec::SamplesLeftToEncode() { - ReadLockScoped rl(codec_wrapper_lock_); - return (frame_len_smpl_ <= in_audio_ix_write_) ? 0 : - (frame_len_smpl_ - in_audio_ix_write_); -} - -void ACMGenericCodec::SetUniqueID(const uint32_t id) { - unique_id_ = id; -} - -bool ACMGenericCodec::IsAudioBufferFresh() const { - ReadLockScoped rl(codec_wrapper_lock_); - return is_audio_buff_fresh_; -} - -int16_t ACMGenericCodec::UpdateDecoderSampFreq(int16_t /* codec_id */) { - return 0; -} - -// This function is replaced by codec specific functions for some codecs. -int16_t ACMGenericCodec::EncoderSampFreq(uint16_t& samp_freq_hz) { - int32_t f; - f = ACMCodecDB::CodecFreq(codec_id_); - if (f < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "EncoderSampFreq: codec frequency is negative"); - return -1; - } else { - samp_freq_hz = static_cast(f); - return 0; - } -} - -int32_t ACMGenericCodec::ConfigISACBandwidthEstimator( - const uint8_t /* init_frame_size_msec */, - const uint16_t /* init_rate_bit_per_sec */, - const bool /* enforce_frame_size */) { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, unique_id_, - "The send-codec is not iSAC, failed to config iSAC bandwidth " - "estimator."); - return -1; -} - -int32_t ACMGenericCodec::SetISACMaxRate( - const uint32_t /* max_rate_bit_per_sec */) { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, unique_id_, - "The send-codec is not iSAC, failed to set iSAC max rate."); - return -1; -} - -int32_t ACMGenericCodec::SetISACMaxPayloadSize( - const uint16_t /* max_payload_len_bytes */) { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, unique_id_, - "The send-codec is not iSAC, failed to set iSAC max " - "payload-size."); - return -1; -} - -void ACMGenericCodec::SaveDecoderParam( - const WebRtcACMCodecParams* codec_params) { - WriteLockScoped wl(codec_wrapper_lock_); - SaveDecoderParamSafe(codec_params); -} - -void ACMGenericCodec::SaveDecoderParamSafe( - const WebRtcACMCodecParams* codec_params) { - memcpy(&decoder_params_, codec_params, sizeof(WebRtcACMCodecParams)); -} - -int16_t ACMGenericCodec::UpdateEncoderSampFreq( - uint16_t /* samp_freq_hz */) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "It is asked for a change in smapling frequency while the " - "current send-codec supports only one sampling rate."); - return -1; -} - -void ACMGenericCodec::SetIsMaster(bool is_master) { - WriteLockScoped wl(codec_wrapper_lock_); - is_master_ = is_master; -} - -int16_t ACMGenericCodec::REDPayloadISAC(const int32_t /* isac_rate */, - const int16_t /* isac_bw_estimate */, - uint8_t* /* payload */, - int16_t* /* payload_len_bytes */) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Error: REDPayloadISAC is an iSAC specific function"); - return -1; -} - -bool ACMGenericCodec::IsTrueStereoCodec() { return false; } - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_generic_codec.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_generic_codec.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_generic_codec.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_generic_codec.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,1224 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_GENERIC_CODEC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_GENERIC_CODEC_H_ - -#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" - -#define MAX_FRAME_SIZE_10MSEC 6 - -// forward declaration -struct WebRtcVadInst; -struct WebRtcCngEncInst; - -namespace webrtc { - -// forward declaration -struct CodecInst; -struct WebRtcACMCodecParams; - -namespace acm1 { - -class ACMNetEQ; - -class ACMGenericCodec { - public: - /////////////////////////////////////////////////////////////////////////// - // Constructor of the class - // - ACMGenericCodec(); - - /////////////////////////////////////////////////////////////////////////// - // Destructor of the class. - // - virtual ~ACMGenericCodec(); - - /////////////////////////////////////////////////////////////////////////// - // ACMGenericCodec* CreateInstance(); - // The function will be used for FEC. It is not implemented yet. - // - virtual ACMGenericCodec* CreateInstance() = 0; - - /////////////////////////////////////////////////////////////////////////// - // int16_t Encode() - // The function is called to perform an encoding of the audio stored in - // audio buffer. An encoding is performed only if enough audio, i.e. equal - // to the frame-size of the codec, exist. The audio frame will be processed - // by VAD and CN/DTX if required. There are few different cases. - // - // A) Neither VAD nor DTX is active; the frame is encoded by the encoder. - // - // B) VAD is enabled but not DTX; in this case the audio is processed by VAD - // and encoded by the encoder. The "*encoding_type" will be either - // "kActiveNormalEncode" or "kPassiveNormalEncode" if frame is active or - // passive, respectively. - // - // C) DTX is enabled; if the codec has internal VAD/DTX we just encode the - // frame by the encoder. Otherwise, the frame is passed through VAD and - // if identified as passive, then it will be processed by CN/DTX. If the - // frame is active it will be encoded by the encoder. - // - // This function acquires the appropriate locks and calls EncodeSafe() for - // the actual processing. - // - // Outputs: - // -bitstream : a buffer where bit-stream will be written to. - // -bitstream_len_byte : contains the length of the bit-stream in - // bytes. - // -timestamp : contains the RTP timestamp, this is the - // sampling time of the first sample encoded - // (measured in number of samples). - // -encoding_type : contains the type of encoding applied on the - // audio samples. The alternatives are - // (c.f. acm_common_types.h) - // -kNoEncoding: - // there was not enough data to encode. or - // some error has happened that we could - // not do encoding. - // -kActiveNormalEncoded: - // the audio frame is active and encoded by - // the given codec. - // -kPassiveNormalEncoded: - // the audio frame is passive but coded with - // the given codec (NO DTX). - // -kPassiveDTXWB: - // The audio frame is passive and used - // wide-band CN to encode. - // -kPassiveDTXNB: - // The audio frame is passive and used - // narrow-band CN to encode. - // - // Return value: - // -1 if error is occurred, otherwise the length of the bit-stream in - // bytes. - // - int16_t Encode(uint8_t* bitstream, - int16_t* bitstream_len_byte, - uint32_t* timestamp, - WebRtcACMEncodingType* encoding_type); - - /////////////////////////////////////////////////////////////////////////// - // int16_t Decode() - // This function is used to decode a given bit-stream, without engaging - // NetEQ. - // - // This function acquires the appropriate locks and calls DecodeSafe() for - // the actual processing. Please note that this is not functional yet. - // - // Inputs: - // -bitstream : a buffer where bit-stream will be read. - // -bitstream_len_byte : the length of the bit-stream in bytes. - // - // Outputs: - // -audio : pointer to a buffer where the audio will written. - // -audio_samples : number of audio samples out of decoding the given - // bit-stream. - // -speech_type : speech type (for future use). - // - // Return value: - // -1 if failed to decode, - // 0 if succeeded. - // - int16_t Decode(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type); - - /////////////////////////////////////////////////////////////////////////// - // void SplitStereoPacket() - // This function is used to split stereo payloads in left and right channel. - // Codecs which has stereo support has there own implementation of the - // function. - // - // Input/Output: - // -payload : a vector with the received payload data. - // The function will reorder the data so that - // first half holds the left channel data, and the - // second half the right channel data. - // -payload_length : length of payload in bytes. Will be changed to - // twice the input in case of true stereo, where - // we simply copy the data and return it both for - // left channel and right channel decoding. - // - virtual void SplitStereoPacket(uint8_t* /* payload */, - int32_t* /* payload_length */) {} - - /////////////////////////////////////////////////////////////////////////// - // bool EncoderInitialized(); - // - // Return value: - // True if the encoder is successfully initialized, - // false otherwise. - // - bool EncoderInitialized(); - - /////////////////////////////////////////////////////////////////////////// - // bool DecoderInitialized(); - // - // Return value: - // True if the decoder is successfully initialized, - // false otherwise. - // - bool DecoderInitialized(); - - /////////////////////////////////////////////////////////////////////////// - // int16_t EncoderParams() - // It is called to get encoder parameters. It will call - // EncoderParamsSafe() in turn. - // - // Output: - // -enc_params : a buffer where the encoder parameters is - // written to. If the encoder is not - // initialized this buffer is filled with - // invalid values - // Return value: - // -1 if the encoder is not initialized, - // 0 otherwise. - // - int16_t EncoderParams(WebRtcACMCodecParams *enc_params); - - /////////////////////////////////////////////////////////////////////////// - // int16_t DecoderParams(...) - // It is called to get decoder parameters. It will call DecoderParamsSafe() - // in turn. - // - // Output: - // -dec_params : a buffer where the decoder parameters is - // written to. If the decoder is not initialized - // this buffer is filled with invalid values - // - // Return value: - // -1 if the decoder is not initialized, - // 0 otherwise. - // - // - bool DecoderParams(WebRtcACMCodecParams *dec_params, - const uint8_t payload_type); - - /////////////////////////////////////////////////////////////////////////// - // int16_t InitEncoder(...) - // This function is called to initialize the encoder with the given - // parameters. - // - // Input: - // -codec_params : parameters of encoder. - // -force_initialization: if false the initialization is invoked only if - // the encoder is not initialized. If true the - // encoder is forced to (re)initialize. - // - // Return value: - // 0 if could initialize successfully, - // -1 if failed to initialize. - // - // - int16_t InitEncoder(WebRtcACMCodecParams* codec_params, - bool force_initialization); - - /////////////////////////////////////////////////////////////////////////// - // int16_t InitDecoder() - // This function is called to initialize the decoder with the given - // parameters. (c.f. acm_common_defs.h & common_types.h for the - // definition of the structure) - // - // Input: - // -codec_params : parameters of decoder. - // -force_initialization: if false the initialization is invoked only - // if the decoder is not initialized. If true - // the encoder is forced to(re)initialize. - // - // Return value: - // 0 if could initialize successfully, - // -1 if failed to initialize. - // - // - int16_t InitDecoder(WebRtcACMCodecParams* codec_params, - bool force_initialization); - - /////////////////////////////////////////////////////////////////////////// - // int32_t RegisterInNetEq(...) - // This function is called to register the decoder in NetEq, with the given - // payload type. - // - // Inputs: - // -neteq : pointer to NetEq Instance - // -codec_inst : instance with of the codec settings of the codec - // - // Return values - // -1 if failed to register, - // 0 if successfully initialized. - // - int32_t RegisterInNetEq(ACMNetEQ* neteq, const CodecInst& codec_inst); - - /////////////////////////////////////////////////////////////////////////// - // int32_t Add10MsData(...) - // This function is called to add 10 ms of audio to the audio buffer of - // the codec. - // - // Inputs: - // -timestamp : the timestamp of the 10 ms audio. the timestamp - // is the sampling time of the - // first sample measured in number of samples. - // -data : a buffer that contains the audio. The codec - // expects to get the audio in correct sampling - // frequency - // -length : the length of the audio buffer - // -audio_channel : 0 for mono, 1 for stereo (not supported yet) - // - // Return values: - // -1 if failed - // 0 otherwise. - // - int32_t Add10MsData(const uint32_t timestamp, - const int16_t* data, - const uint16_t length, - const uint8_t audio_channel); - - /////////////////////////////////////////////////////////////////////////// - // uint32_t NoMissedSamples() - // This function returns the number of samples which are overwritten in - // the audio buffer. The audio samples are overwritten if the input audio - // buffer is full, but Add10MsData() is called. (We might remove this - // function if it is not used) - // - // Return Value: - // Number of samples which are overwritten. - // - uint32_t NoMissedSamples() const; - - /////////////////////////////////////////////////////////////////////////// - // void ResetNoMissedSamples() - // This function resets the number of overwritten samples to zero. - // (We might remove this function if we remove NoMissedSamples()) - // - void ResetNoMissedSamples(); - - /////////////////////////////////////////////////////////////////////////// - // int16_t SetBitRate() - // The function is called to set the encoding rate. - // - // Input: - // -bitrate_bps : encoding rate in bits per second - // - // Return value: - // -1 if failed to set the rate, due to invalid input or given - // codec is not rate-adjustable. - // 0 if the rate is adjusted successfully - // - int16_t SetBitRate(const int32_t bitrate_bps); - - /////////////////////////////////////////////////////////////////////////// - // DestructEncoderInst() - // This API is used in conferencing. It will free the memory that is pointed - // by |ptr_inst|. |ptr_inst| is a pointer to encoder instance, created and - // filled up by calling EncoderInst(...). - // - // Inputs: - // -ptr_inst : pointer to an encoder instance to be deleted. - // - // - void DestructEncoderInst(void* ptr_inst); - - /////////////////////////////////////////////////////////////////////////// - // int16_t AudioBuffer() - // This is used when synchronization of codecs is required. There are cases - // that the audio buffers of two codecs have to be synched. By calling this - // function on can get the audio buffer and other related parameters, such - // as timestamps... - // - // Output: - // -audio_buff : a pointer to WebRtcACMAudioBuff where the audio - // buffer of this codec will be written to. - // - // Return value: - // -1 if fails to copy the audio buffer, - // 0 if succeeded. - // - int16_t AudioBuffer(WebRtcACMAudioBuff& audio_buff); - - /////////////////////////////////////////////////////////////////////////// - // uint32_t EarliestTimestamp() - // Returns the timestamp of the first 10 ms in audio buffer. This is used - // to identify if a synchronization of two encoders is required. - // - // Return value: - // timestamp of the first 10 ms audio in the audio buffer. - // - uint32_t EarliestTimestamp() const; - - /////////////////////////////////////////////////////////////////////////// - // int16_t SetAudioBuffer() - // This function is called to set the audio buffer and the associated - // parameters to a given value. - // - // Return value: - // -1 if fails to copy the audio buffer, - // 0 if succeeded. - // - int16_t SetAudioBuffer(WebRtcACMAudioBuff& audio_buff); - - /////////////////////////////////////////////////////////////////////////// - // int16_t SetVAD() - // This is called to set VAD & DTX. If the codec has internal DTX, it will - // be used. If DTX is enabled and the codec does not have internal DTX, - // WebRtc-VAD will be used to decide if the frame is active. If DTX is - // disabled but VAD is enabled the audio is passed through VAD to label it - // as active or passive, but the frame is encoded normally. However the - // bit-stream is labeled properly so that ACM::Process() can use this - // information. In case of failure, the previous states of the VAD & DTX - // are kept. - // - // Input/Output: - // -enable_dtx : if true DTX will be enabled otherwise the DTX is - // disabled. If codec has internal DTX that will be - // used, otherwise WebRtc-CNG is used. In the latter - // case VAD is automatically activated. - // -enable_vad : if true WebRtc-VAD is enabled, otherwise VAD is - // disabled, except for the case that DTX is enabled - // but codec doesn't have internal DTX. In this case - // VAD is enabled regardless of the value of - // |enable_vad|. - // -mode : this specifies the aggressiveness of VAD. - // - // Return value - // -1 if failed to set DTX & VAD as specified, - // 0 if succeeded. - // - int16_t SetVAD(bool* enable_dtx, - bool* enable_vad, - ACMVADMode* mode); - - /////////////////////////////////////////////////////////////////////////// - // int32_t ReplaceInternalDTX() - // This is called to replace the codec internal DTX with WebRtc DTX. - // This is only valid for G729 where the user has possibility to replace - // AnnexB with WebRtc DTX. For other codecs this function has no effect. - // - // Input: - // -replace_internal_dtx : if true the internal DTX is replaced with WebRtc. - // - // Return value - // -1 if failed to replace internal DTX, - // 0 if succeeded. - // - int32_t ReplaceInternalDTX(const bool replace_internal_dtx); - - /////////////////////////////////////////////////////////////////////////// - // int32_t IsInternalDTXReplaced() - // This is called to check if the codec internal DTX is replaced by WebRtc - // DTX. This is only valid for G729 where the user has possibility to replace - // AnnexB with WebRtc DTX. For other codecs this function has no effect. - // - // Output: - // -internal_dtx_replaced: if true the internal DTX is replaced with WebRtc. - // - // Return value - // -1 if failed to check - // 0 if succeeded. - // - int32_t IsInternalDTXReplaced(bool* internal_dtx_replaced); - - /////////////////////////////////////////////////////////////////////////// - // void SetNetEqDecodeLock() - // Passes the NetEq lock to the codec. - // - // Input: - // -neteq_decode_lock : pointer to the lock associated with NetEQ of ACM. - // - void SetNetEqDecodeLock(RWLockWrapper* neteq_decode_lock) { - neteq_decode_lock_ = neteq_decode_lock; - } - - /////////////////////////////////////////////////////////////////////////// - // bool HasInternalDTX() - // Used to check if the codec has internal DTX. - // - // Return value: - // true if the codec has an internal DTX, e.g. G729, - // false otherwise. - // - bool HasInternalDTX() const { - return has_internal_dtx_; - } - - /////////////////////////////////////////////////////////////////////////// - // int32_t GetEstimatedBandwidth() - // Used to get decoder estimated bandwidth. Only iSAC will provide a value. - // - // - // Return value: - // -1 if fails to get decoder estimated bandwidth, - // >0 estimated bandwidth in bits/sec. - // - int32_t GetEstimatedBandwidth(); - - /////////////////////////////////////////////////////////////////////////// - // int32_t SetEstimatedBandwidth() - // Used to set estiamted bandwidth sent out of band from other side. Only - // iSAC will have use for the value. - // - // Input: - // -estimated_bandwidth: estimated bandwidth in bits/sec - // - // Return value: - // -1 if fails to set estimated bandwidth, - // 0 on success. - // - int32_t SetEstimatedBandwidth(int32_t estimated_bandwidth); - - /////////////////////////////////////////////////////////////////////////// - // int32_t GetRedPayload() - // Used to get codec specific RED payload (if such is implemented). - // Currently only done in iSAC. - // - // Outputs: - // -red_payload : a pointer to the data for RED payload. - // -payload_bytes : number of bytes in RED payload. - // - // Return value: - // -1 if fails to get codec specific RED, - // 0 if succeeded. - // - int32_t GetRedPayload(uint8_t* red_payload, - int16_t* payload_bytes); - - /////////////////////////////////////////////////////////////////////////// - // int16_t ResetEncoder() - // By calling this function you would re-initialize the encoder with the - // current parameters. All the settings, e.g. VAD/DTX, frame-size... should - // remain unchanged. (In case of iSAC we don't want to lose BWE history.) - // - // Return value - // -1 if failed, - // 0 if succeeded. - // - int16_t ResetEncoder(); - - /////////////////////////////////////////////////////////////////////////// - // int16_t ResetEncoder() - // By calling this function you would re-initialize the decoder with the - // current parameters. - // - // Return value - // -1 if failed, - // 0 if succeeded. - // - int16_t ResetDecoder(int16_t payload_type); - - /////////////////////////////////////////////////////////////////////////// - // void DestructEncoder() - // This function is called to delete the encoder instance, if possible, to - // have a fresh start. For codecs where encoder and decoder share the same - // instance we cannot delete the encoder and instead we will initialize the - // encoder. We also delete VAD and DTX if they have been created. - // - void DestructEncoder(); - - /////////////////////////////////////////////////////////////////////////// - // void DestructDecoder() - // This function is called to delete the decoder instance, if possible, to - // have a fresh start. For codecs where encoder and decoder share the same - // instance we cannot delete the encoder and instead we will initialize the - // decoder. Before deleting decoder instance it has to be removed from the - // NetEq list. - // - void DestructDecoder(); - - /////////////////////////////////////////////////////////////////////////// - // int16_t SamplesLeftToEncode() - // Returns the number of samples required to be able to do encoding. - // - // Return value: - // Number of samples. - // - int16_t SamplesLeftToEncode(); - - /////////////////////////////////////////////////////////////////////////// - // uint32_t LastEncodedTimestamp() - // Returns the timestamp of the last frame it encoded. - // - // Return value: - // Timestamp. - // - uint32_t LastEncodedTimestamp() const; - - /////////////////////////////////////////////////////////////////////////// - // SetUniqueID() - // Set a unique ID for the codec to be used for tracing and debugging - // - // Input - // -id : A number to identify the codec. - // - void SetUniqueID(const uint32_t id); - - /////////////////////////////////////////////////////////////////////////// - // IsAudioBufferFresh() - // Specifies if ever audio is injected to this codec. - // - // Return value - // -true; no audio is feed into this codec - // -false; audio has already been fed to the codec. - // - bool IsAudioBufferFresh() const; - - /////////////////////////////////////////////////////////////////////////// - // UpdateDecoderSampFreq() - // For most of the codecs this function does nothing. It must be - // implemented for those codecs that one codec instance serves as the - // decoder for different flavors of the codec. One example is iSAC. there, - // iSAC 16 kHz and iSAC 32 kHz are treated as two different codecs with - // different payload types, however, there is only one iSAC instance to - // decode. The reason for that is we would like to decode and encode with - // the same codec instance for bandwidth estimator to work. - // - // Each time that we receive a new payload type, we call this function to - // prepare the decoder associated with the new payload. Normally, decoders - // doesn't have to do anything. For iSAC the decoder has to change it's - // sampling rate. The input parameter specifies the current flavor of the - // codec in codec database. For instance, if we just got a SWB payload then - // the input parameter is ACMCodecDB::isacswb. - // - // Input: - // -codec_id : the ID of the codec associated with the - // payload type that we just received. - // - // Return value: - // 0 if succeeded in updating the decoder. - // -1 if failed to update. - // - virtual int16_t UpdateDecoderSampFreq(int16_t /* codec_id */); - - /////////////////////////////////////////////////////////////////////////// - // UpdateEncoderSampFreq() - // Call this function to update the encoder sampling frequency. This - // is for codecs where one payload-name supports several encoder sampling - // frequencies. Otherwise, to change the sampling frequency we need to - // register new codec. ACM will consider that as registration of a new - // codec, not a change in parameter. For iSAC, switching from WB to SWB - // is treated as a change in parameter. Therefore, we need this function. - // - // Input: - // -samp_freq_hz : encoder sampling frequency. - // - // Return value: - // -1 if failed, or if this is meaningless for the given codec. - // 0 if succeeded. - // - virtual int16_t UpdateEncoderSampFreq( - uint16_t samp_freq_hz); - - /////////////////////////////////////////////////////////////////////////// - // EncoderSampFreq() - // Get the sampling frequency that the encoder (WebRtc wrapper) expects. - // - // Output: - // -samp_freq_hz : sampling frequency, in Hertz, which the encoder - // should be fed with. - // - // Return value: - // -1 if failed to output sampling rate. - // 0 if the sample rate is returned successfully. - // - virtual int16_t EncoderSampFreq(uint16_t& samp_freq_hz); - - /////////////////////////////////////////////////////////////////////////// - // int32_t ConfigISACBandwidthEstimator() - // Call this function to configure the bandwidth estimator of ISAC. - // During the adaptation of bit-rate, iSAC automatically adjusts the - // frame-size (either 30 or 60 ms) to save on RTP header. The initial - // frame-size can be specified by the first argument. The configuration also - // regards the initial estimate of bandwidths. The estimator starts from - // this point and converges to the actual bottleneck. This is given by the - // second parameter. Furthermore, it is also possible to control the - // adaptation of frame-size. This is specified by the last parameter. - // - // Input: - // -init_frame_fize_ms : initial frame-size in milliseconds. For iSAC-wb - // 30 ms and 60 ms (default) are acceptable values, - // and for iSAC-swb 30 ms is the only acceptable - // value. Zero indicates default value. - // -init_rate_bps : initial estimate of the bandwidth. Values - // between 10000 and 58000 are acceptable. - // -enforce_frame_size : if true, the frame-size will not be adapted. - // - // Return value: - // -1 if failed to configure the bandwidth estimator, - // 0 if the configuration was successfully applied. - // - virtual int32_t ConfigISACBandwidthEstimator( - const uint8_t init_frame_size_msec, - const uint16_t init_rate_bps, - const bool enforce_frame_size); - - /////////////////////////////////////////////////////////////////////////// - // SetISACMaxPayloadSize() - // Set the maximum payload size of iSAC packets. No iSAC payload, - // regardless of its frame-size, may exceed the given limit. For - // an iSAC payload of size B bits and frame-size T sec we have; - // (B < max_payload_len_bytes * 8) and (B/T < max_rate_bit_per_sec), c.f. - // SetISACMaxRate(). - // - // Input: - // -max_payload_len_bytes : maximum payload size in bytes. - // - // Return value: - // -1 if failed to set the maximum payload-size. - // 0 if the given length is set successfully. - // - virtual int32_t SetISACMaxPayloadSize( - const uint16_t max_payload_len_bytes); - - /////////////////////////////////////////////////////////////////////////// - // SetISACMaxRate() - // Set the maximum instantaneous rate of iSAC. For a payload of B bits - // with a frame-size of T sec the instantaneous rate is B/T bits per - // second. Therefore, (B/T < max_rate_bit_per_sec) and - // (B < max_payload_len_bytes * 8) are always satisfied for iSAC payloads, - // c.f SetISACMaxPayloadSize(). - // - // Input: - // -max_rate_bps : maximum instantaneous bit-rate given in bits/sec. - // - // Return value: - // -1 if failed to set the maximum rate. - // 0 if the maximum rate is set successfully. - // - virtual int32_t SetISACMaxRate(const uint32_t max_rate_bps); - - /////////////////////////////////////////////////////////////////////////// - // SaveDecoderParamS() - // Save the parameters of decoder. - // - // Input: - // -codec_params : pointer to a structure where the parameters of - // decoder is stored in. - // - void SaveDecoderParam(const WebRtcACMCodecParams* codec_params); - - int32_t FrameSize() { - return frame_len_smpl_; - } - - void SetIsMaster(bool is_master); - - /////////////////////////////////////////////////////////////////////////// - // REDPayloadISAC() - // This is an iSAC-specific function. The function is called to get RED - // payload from a default-encoder. - // - // Inputs: - // -isac_rate : the target rate of the main payload. A RED - // payload is generated according to the rate of - // main payload. Note that we are not specifying the - // rate of RED payload, but the main payload. - // -isac_bw_estimate : bandwidth information should be inserted in - // RED payload. - // - // Output: - // -payload : pointer to a buffer where the RED payload will - // written to. - // -payload_len_bytes : a place-holder to write the length of the RED - // payload in Bytes. - // - // Return value: - // -1 if an error occurs, otherwise the length of the payload (in Bytes) - // is returned. - // - virtual int16_t REDPayloadISAC(const int32_t isac_rate, - const int16_t isac_bw_estimate, - uint8_t* payload, - int16_t* payload_len_bytes); - - /////////////////////////////////////////////////////////////////////////// - // IsTrueStereoCodec() - // Call to see if current encoder is a true stereo codec. This function - // should be overwritten for codecs which are true stereo codecs - // Return value: - // -true if stereo codec - // -false if not stereo codec. - // - virtual bool IsTrueStereoCodec(); - - /////////////////////////////////////////////////////////////////////////// - // HasFrameToEncode() - // Returns true if there is enough audio buffered for encoding, such that - // calling Encode() will return a payload. - // - bool HasFrameToEncode() const; - - protected: - /////////////////////////////////////////////////////////////////////////// - // All the functions with FunctionNameSafe(...) contain the actual - // implementation of FunctionName(...). FunctionName() acquires an - // appropriate lock and calls FunctionNameSafe() to do the actual work. - // Therefore, for the description of functionality, input/output arguments - // and return value we refer to FunctionName() - // - - /////////////////////////////////////////////////////////////////////////// - // See Decode() for the description of function, input(s)/output(s) and - // return value. - // - virtual int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type) = 0; - - /////////////////////////////////////////////////////////////////////////// - // See Add10MsSafe() for the description of function, input(s)/output(s) - // and return value. - // - virtual int32_t Add10MsDataSafe(const uint32_t timestamp, - const int16_t* data, - const uint16_t length, - const uint8_t audio_channel); - - /////////////////////////////////////////////////////////////////////////// - // See RegisterInNetEq() for the description of function, - // input(s)/output(s) and return value. - // - virtual int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) = 0; - - /////////////////////////////////////////////////////////////////////////// - // See EncoderParam() for the description of function, input(s)/output(s) - // and return value. - // - int16_t EncoderParamsSafe(WebRtcACMCodecParams *enc_params); - - /////////////////////////////////////////////////////////////////////////// - // See DecoderParam for the description of function, input(s)/output(s) - // and return value. - // - // Note: - // Any Class where a single instance handle several flavors of the - // same codec, therefore, several payload types are associated with - // the same instance have to implement this function. - // - // Currently only iSAC is implementing it. A single iSAC instance is - // used for decoding both WB & SWB stream. At one moment both WB & SWB - // can be registered as receive codec. Hence two payloads are associated - // with a single codec instance. - // - virtual bool DecoderParamsSafe(WebRtcACMCodecParams *dec_params, - const uint8_t payload_type); - - /////////////////////////////////////////////////////////////////////////// - // See ResetEncoder() for the description of function, input(s)/output(s) - // and return value. - // - int16_t ResetEncoderSafe(); - - /////////////////////////////////////////////////////////////////////////// - // See InitEncoder() for the description of function, input(s)/output(s) - // and return value. - // - int16_t InitEncoderSafe(WebRtcACMCodecParams *codec_params, - bool force_initialization); - - /////////////////////////////////////////////////////////////////////////// - // See InitDecoder() for the description of function, input(s)/output(s) - // and return value. - // - int16_t InitDecoderSafe(WebRtcACMCodecParams *codec_params, - bool force_initialization); - - /////////////////////////////////////////////////////////////////////////// - // See ResetDecoder() for the description of function, input(s)/output(s) - // and return value. - // - int16_t ResetDecoderSafe(int16_t payload_type); - - /////////////////////////////////////////////////////////////////////////// - // See DestructEncoder() for the description of function, - // input(s)/output(s) and return value. - // - virtual void DestructEncoderSafe() = 0; - - /////////////////////////////////////////////////////////////////////////// - // See DestructDecoder() for the description of function, - // input(s)/output(s) and return value. - // - virtual void DestructDecoderSafe() = 0; - - /////////////////////////////////////////////////////////////////////////// - // See SetBitRate() for the description of function, input(s)/output(s) - // and return value. - // - // Any codec that can change the bit-rate has to implement this. - // - virtual int16_t SetBitRateSafe(const int32_t bitrate_bps); - - /////////////////////////////////////////////////////////////////////////// - // See GetEstimatedBandwidth() for the description of function, - // input(s)/output(s) and return value. - // - virtual int32_t GetEstimatedBandwidthSafe(); - - /////////////////////////////////////////////////////////////////////////// - // See SetEstimatedBandwidth() for the description of function, - // input(s)/output(s) and return value. - // - virtual int32_t SetEstimatedBandwidthSafe( - int32_t estimated_bandwidth); - - /////////////////////////////////////////////////////////////////////////// - // See GetRedPayload() for the description of function, input(s)/output(s) - // and return value. - // - virtual int32_t GetRedPayloadSafe(uint8_t* red_payload, - int16_t* payload_bytes); - - /////////////////////////////////////////////////////////////////////////// - // See SetVAD() for the description of function, input(s)/output(s) and - // return value. - // - int16_t SetVADSafe(bool* enable_dtx, - bool* enable_vad, - ACMVADMode* mode); - - /////////////////////////////////////////////////////////////////////////// - // See ReplaceInternalDTX() for the description of function, input and - // return value. - // - virtual int32_t ReplaceInternalDTXSafe(const bool replace_internal_dtx); - - /////////////////////////////////////////////////////////////////////////// - // See IsInternalDTXReplaced() for the description of function, input and - // return value. - // - virtual int32_t IsInternalDTXReplacedSafe(bool* internal_dtx_replaced); - - /////////////////////////////////////////////////////////////////////////// - // int16_t CreateEncoder() - // Creates the encoder instance. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - int16_t CreateEncoder(); - - /////////////////////////////////////////////////////////////////////////// - // int16_t CreateDecoder() - // Creates the decoder instance. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - int16_t CreateDecoder(); - - /////////////////////////////////////////////////////////////////////////// - // int16_t EnableVAD(); - // Enables VAD with the given mode. The VAD instance will be created if - // it does not exists. - // - // Input: - // -mode : VAD mode c.f. audio_coding_module_typedefs.h for - // the options. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - int16_t EnableVAD(ACMVADMode mode); - - /////////////////////////////////////////////////////////////////////////// - // int16_t DisableVAD() - // Disables VAD. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - int16_t DisableVAD(); - - /////////////////////////////////////////////////////////////////////////// - // int16_t EnableDTX() - // Enables DTX. This method should be overwritten for codecs which have - // internal DTX. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - virtual int16_t EnableDTX(); - - /////////////////////////////////////////////////////////////////////////// - // int16_t DisableDTX() - // Disables usage of DTX. This method should be overwritten for codecs which - // have internal DTX. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - virtual int16_t DisableDTX(); - - /////////////////////////////////////////////////////////////////////////// - // int16_t InternalEncode() - // This is a codec-specific function called in EncodeSafe() to actually - // encode a frame of audio. - // - // Outputs: - // -bitstream : pointer to a buffer where the bit-stream is - // written to. - // -bitstream_len_byte : the length of the bit-stream in bytes, - // a negative value indicates error. - // - // Return value: - // -1 if failed, - // otherwise the length of the bit-stream is returned. - // - virtual int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) = 0; - - /////////////////////////////////////////////////////////////////////////// - // int16_t InternalInitEncoder() - // This is a codec-specific function called in InitEncoderSafe(), it has to - // do all codec-specific operation to initialize the encoder given the - // encoder parameters. - // - // Input: - // -codec_params : pointer to a structure that contains parameters to - // initialize encoder. - // Set codec_params->codec_inst.rate to -1 for - // iSAC to operate in adaptive mode. - // (to do: if frame-length is -1 frame-length will be - // automatically adjusted, otherwise, given - // frame-length is forced) - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - virtual int16_t InternalInitEncoder( - WebRtcACMCodecParams *codec_params) = 0; - - /////////////////////////////////////////////////////////////////////////// - // int16_t InternalInitDecoder() - // This is a codec-specific function called in InitDecoderSafe(), it has to - // do all codec-specific operation to initialize the decoder given the - // decoder parameters. - // - // Input: - // -codec_params : pointer to a structure that contains parameters to - // initialize encoder. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - virtual int16_t InternalInitDecoder( - WebRtcACMCodecParams *codec_params) = 0; - - /////////////////////////////////////////////////////////////////////////// - // void IncreaseNoMissedSamples() - // This method is called to increase the number of samples that are - // overwritten in the audio buffer. - // - // Input: - // -num_samples : the number of overwritten samples is incremented - // by this value. - // - void IncreaseNoMissedSamples(const int16_t num_samples); - - /////////////////////////////////////////////////////////////////////////// - // int16_t InternalCreateEncoder() - // This is a codec-specific method called in CreateEncoderSafe() it is - // supposed to perform all codec-specific operations to create encoder - // instance. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - virtual int16_t InternalCreateEncoder() = 0; - - /////////////////////////////////////////////////////////////////////////// - // int16_t InternalCreateDecoder() - // This is a codec-specific method called in CreateDecoderSafe() it is - // supposed to perform all codec-specific operations to create decoder - // instance. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - virtual int16_t InternalCreateDecoder() = 0; - - /////////////////////////////////////////////////////////////////////////// - // void InternalDestructEncoderInst() - // This is a codec-specific method, used in conferencing, called from - // DestructEncoderInst(). The input argument is pointer to encoder instance - // (codec instance for codecs that encoder and decoder share the same - // instance). This method is called to free the memory that |ptr_inst| is - // pointing to. - // - // Input: - // -ptr_inst : pointer to encoder instance. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - virtual void InternalDestructEncoderInst(void* ptr_inst) = 0; - - /////////////////////////////////////////////////////////////////////////// - // int16_t InternalResetEncoder() - // This method is called to reset the states of encoder. However, the - // current parameters, e.g. frame-length, should remain as they are. For - // most of the codecs a re-initialization of the encoder is what needs to - // be down. But for iSAC we like to keep the BWE history so we cannot - // re-initialize. As soon as such an API is implemented in iSAC this method - // has to be overwritten in ACMISAC class. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - virtual int16_t InternalResetEncoder(); - - /////////////////////////////////////////////////////////////////////////// - // int16_t ProcessFrameVADDTX() - // This function is called when a full frame of audio is available. It will - // break the audio frame into blocks such that each block could be processed - // by VAD & CN/DTX. If a frame is divided into two blocks then there are two - // cases. First, the first block is active, the second block will not be - // processed by CN/DTX but only by VAD and return to caller with - // '*samples_processed' set to zero. There, the audio frame will be encoded - // by the encoder. Second, the first block is inactive and is processed by - // CN/DTX, then we stop processing the next block and return to the caller - // which is EncodeSafe(), with "*samples_processed" equal to the number of - // samples in first block. - // - // Output: - // -bitstream : pointer to a buffer where DTX frame, if - // generated, will be written to. - // -bitstream_len_byte : contains the length of bit-stream in bytes, if - // generated. Zero if no bit-stream is generated. - // -samples_processed : contains no of samples that actually CN has - // processed. Those samples processed by CN will not - // be encoded by the encoder, obviously. If - // contains zero, it means that the frame has been - // identified as active by VAD. Note that - // "*samples_processed" might be non-zero but - // "*bitstream_len_byte" be zero. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - int16_t ProcessFrameVADDTX(uint8_t* bitstream, - int16_t* bitstream_len_byte, - int16_t* samples_processed); - - /////////////////////////////////////////////////////////////////////////// - // CanChangeEncodingParam() - // Check if the codec parameters can be changed. In conferencing normally - // codec parameters cannot be changed. The exception is bit-rate of isac. - // - // return value: - // -true if codec parameters are allowed to change. - // -false otherwise. - // - virtual bool CanChangeEncodingParam(CodecInst& codec_inst); - - /////////////////////////////////////////////////////////////////////////// - // CurrentRate() - // Call to get the current encoding rate of the encoder. This function - // should be overwritten for codecs which automatically change their - // target rate. One example is iSAC. The output of the function is the - // current target rate. - // - // Output: - // -rate_bps : the current target rate of the codec. - // - virtual void CurrentRate(int32_t& /* rate_bps */); - - virtual void SaveDecoderParamSafe(const WebRtcACMCodecParams* codec_params); - - // &in_audio_[in_audio_ix_write_] always point to where new audio can be - // written to - int16_t in_audio_ix_write_; - - // &in_audio_[in_audio_ix_read_] points to where audio has to be read from - int16_t in_audio_ix_read_; - - int16_t in_timestamp_ix_write_; - - // Where the audio is stored before encoding, - // To save memory the following buffer can be allocated - // dynamically for 80 ms depending on the sampling frequency - // of the codec. - int16_t* in_audio_; - uint32_t* in_timestamp_; - - int16_t frame_len_smpl_; - uint16_t num_channels_; - - // This will point to a static database of the supported codecs - int16_t codec_id_; - - // This will account for the number of samples were not encoded - // the case is rare, either samples are missed due to overwrite - // at input buffer or due to encoding error - uint32_t num_missed_samples_; - - // True if the encoder instance created - bool encoder_exist_; - bool decoder_exist_; - // True if the encoder instance initialized - bool encoder_initialized_; - bool decoder_initialized_; - - bool registered_in_neteq_; - - // VAD/DTX - bool has_internal_dtx_; - WebRtcVadInst* ptr_vad_inst_; - bool vad_enabled_; - ACMVADMode vad_mode_; - int16_t vad_label_[MAX_FRAME_SIZE_10MSEC]; - bool dtx_enabled_; - WebRtcCngEncInst* ptr_dtx_inst_; - uint8_t num_lpc_params_; - bool sent_cn_previous_; - bool is_master_; - int16_t prev_frame_cng_; - - WebRtcACMCodecParams encoder_params_; - WebRtcACMCodecParams decoder_params_; - - // Used as a global lock for all available decoders - // so that no decoder is used when NetEQ decodes. - RWLockWrapper* neteq_decode_lock_; - // Used to lock wrapper internal data - // such as buffers and state variables. - RWLockWrapper& codec_wrapper_lock_; - - uint32_t last_encoded_timestamp_; - uint32_t last_timestamp_; - bool is_audio_buff_fresh_; - uint32_t unique_id_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_GENERIC_CODEC_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_gsmfr.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_gsmfr.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_gsmfr.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_gsmfr.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,267 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_gsmfr.h" - -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/trace.h" -#ifdef WEBRTC_CODEC_GSMFR -// NOTE! GSM-FR is not included in the open-source package. Modify this file -// or your codec API to match the function calls and names of used GSM-FR API -// file. -#include "gsmfr_interface.h" -#endif - -namespace webrtc { - -namespace acm1 { - -#ifndef WEBRTC_CODEC_GSMFR - -ACMGSMFR::ACMGSMFR(int16_t /* codec_id */) - : encoder_inst_ptr_(NULL), - decoder_inst_ptr_(NULL) { - return; -} - -ACMGSMFR::~ACMGSMFR() { - return; -} - -int16_t ACMGSMFR::InternalEncode( - uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - return -1; -} - -int16_t ACMGSMFR::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return -1; -} - -int16_t ACMGSMFR::EnableDTX() { - return -1; -} - -int16_t ACMGSMFR::DisableDTX() { - return -1; -} - -int16_t ACMGSMFR::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int16_t ACMGSMFR::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int32_t ACMGSMFR::CodecDef(WebRtcNetEQ_CodecDef& /* codec_def */, - const CodecInst& /* codec_inst */) { - return -1; -} - -ACMGenericCodec* ACMGSMFR::CreateInstance(void) { - return NULL; -} - -int16_t ACMGSMFR::InternalCreateEncoder() { - return -1; -} - -void ACMGSMFR::DestructEncoderSafe() { - return; -} - -int16_t ACMGSMFR::InternalCreateDecoder() { - return -1; -} - -void ACMGSMFR::DestructDecoderSafe() { - return; -} - -void ACMGSMFR::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - -#else //===================== Actual Implementation ======================= - -ACMGSMFR::ACMGSMFR(int16_t codec_id) - : encoder_inst_ptr_(NULL), - decoder_inst_ptr_(NULL) { - codec_id_ = codec_id; - has_internal_dtx_ = true; - return; -} - -ACMGSMFR::~ACMGSMFR() { - if (encoder_inst_ptr_ != NULL) { - WebRtcGSMFR_FreeEnc(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } - if (decoder_inst_ptr_ != NULL) { - WebRtcGSMFR_FreeDec(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } - return; -} - -int16_t ACMGSMFR::InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) { - *bitstream_len_byte = WebRtcGSMFR_Encode(encoder_inst_ptr_, - &in_audio_[in_audio_ix_read_], - frame_len_smpl_, - (int16_t*)bitstream); - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - in_audio_ix_read_ += frame_len_smpl_; - return *bitstream_len_byte; -} - -int16_t ACMGSMFR::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return 0; -} - -int16_t ACMGSMFR::EnableDTX() { - if (dtx_enabled_) { - return 0; - } else if (encoder_exist_) { - if (WebRtcGSMFR_EncoderInit(encoder_inst_ptr_, 1) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "EnableDTX: cannot init encoder for GSMFR"); - return -1; - } - dtx_enabled_ = true; - return 0; - } else { - return -1; - } -} - -int16_t ACMGSMFR::DisableDTX() { - if (!dtx_enabled_) { - return 0; - } else if (encoder_exist_) { - if (WebRtcGSMFR_EncoderInit(encoder_inst_ptr_, 0) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "DisableDTX: cannot init encoder for GSMFR"); - return -1; - } - dtx_enabled_ = false; - return 0; - } else { - // encoder doesn't exists, therefore disabling is harmless - return 0; - } -} - -int16_t ACMGSMFR::InternalInitEncoder( - WebRtcACMCodecParams* codec_params) { - if (WebRtcGSMFR_EncoderInit(encoder_inst_ptr_, - ((codec_params->enable_dtx) ? 1 : 0)) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalInitEncoder: cannot init encoder for GSMFR"); - } - return 0; -} - -int16_t ACMGSMFR::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - if (WebRtcGSMFR_DecoderInit(decoder_inst_ptr_) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalInitDecoder: cannot init decoder for GSMFR"); - return -1; - } - return 0; -} - -int32_t ACMGSMFR::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - if (!decoder_initialized_) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "CodecDef: decoder is not initialized for GSMFR"); - return -1; - } - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_GSMFR_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codec_def), kDecoderGSMFR, codec_inst.pltype, - decoder_inst_ptr_, 8000); - SET_GSMFR_FUNCTIONS((codec_def)); - return 0; -} - -ACMGenericCodec* ACMGSMFR::CreateInstance(void) { - return NULL; -} - -int16_t ACMGSMFR::InternalCreateEncoder() { - if (WebRtcGSMFR_CreateEnc(&encoder_inst_ptr_) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalCreateEncoder: cannot create instance for GSMFR " - "encoder"); - return -1; - } - return 0; -} - -void ACMGSMFR::DestructEncoderSafe() { - if (encoder_inst_ptr_ != NULL) { - WebRtcGSMFR_FreeEnc(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } - encoder_exist_ = false; - encoder_initialized_ = false; -} - -int16_t ACMGSMFR::InternalCreateDecoder() { - if (WebRtcGSMFR_CreateDec(&decoder_inst_ptr_) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalCreateDecoder: cannot create instance for GSMFR " - "decoder"); - return -1; - } - return 0; -} - -void ACMGSMFR::DestructDecoderSafe() { - if (decoder_inst_ptr_ != NULL) { - WebRtcGSMFR_FreeDec(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } - decoder_exist_ = false; - decoder_initialized_ = false; -} - -void ACMGSMFR::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcGSMFR_FreeEnc((GSMFR_encinst_t_*) ptr_inst); - } - return; -} - -#endif - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_gsmfr.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_gsmfr.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_gsmfr.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_gsmfr.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_GSMFR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_GSMFR_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -// forward declaration -struct GSMFR_encinst_t_; -struct GSMFR_decinst_t_; - -namespace webrtc { - -namespace acm1 { - -class ACMGSMFR : public ACMGenericCodec { - public: - explicit ACMGSMFR(int16_t codec_id); - ~ACMGSMFR(); - - // for FEC - ACMGenericCodec* CreateInstance(void); - - int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte); - - int16_t InternalInitEncoder(WebRtcACMCodecParams *codec_params); - - int16_t InternalInitDecoder(WebRtcACMCodecParams *codec_params); - - protected: - int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type); - - int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - int16_t InternalCreateEncoder(); - - int16_t InternalCreateDecoder(); - - void InternalDestructEncoderInst(void* ptr_inst); - - int16_t EnableDTX(); - - int16_t DisableDTX(); - - GSMFR_encinst_t_* encoder_inst_ptr_; - GSMFR_decinst_t_* decoder_inst_ptr_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_GSMFR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_ilbc.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_ilbc.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_ilbc.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_ilbc.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,259 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#include "webrtc/modules/audio_coding/main/source/acm_ilbc.h" - -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/trace.h" - -#ifdef WEBRTC_CODEC_ILBC -#include "webrtc/modules/audio_coding/codecs/ilbc/interface/ilbc.h" -#endif - -namespace webrtc { - -namespace acm1 { - -#ifndef WEBRTC_CODEC_ILBC - -ACMILBC::ACMILBC(int16_t /* codec_id */) - : encoder_inst_ptr_(NULL), - decoder_inst_ptr_(NULL) { - return; -} - -ACMILBC::~ACMILBC() { - return; -} - -int16_t ACMILBC::InternalEncode( - uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - return -1; -} - -int16_t ACMILBC::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return -1; -} - -int16_t ACMILBC::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int16_t ACMILBC::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int32_t ACMILBC::CodecDef(WebRtcNetEQ_CodecDef& /* codec_def */, - const CodecInst& /* codec_inst */) { - return -1; -} - -ACMGenericCodec* ACMILBC::CreateInstance(void) { - return NULL; -} - -int16_t ACMILBC::InternalCreateEncoder() { - return -1; -} - -void ACMILBC::DestructEncoderSafe() { - return; -} - -int16_t ACMILBC::InternalCreateDecoder() { - return -1; -} - -void ACMILBC::DestructDecoderSafe() { - return; -} - -void ACMILBC::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - -int16_t ACMILBC::SetBitRateSafe(const int32_t /* rate */) { - return -1; -} - -#else //===================== Actual Implementation ======================= - -ACMILBC::ACMILBC(int16_t codec_id) - : encoder_inst_ptr_(NULL), - decoder_inst_ptr_(NULL) { - codec_id_ = codec_id; - return; -} - -ACMILBC::~ACMILBC() { - if (encoder_inst_ptr_ != NULL) { - WebRtcIlbcfix_EncoderFree(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } - if (decoder_inst_ptr_ != NULL) { - WebRtcIlbcfix_DecoderFree(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } - return; -} - -int16_t ACMILBC::InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) { - *bitstream_len_byte = WebRtcIlbcfix_Encode(encoder_inst_ptr_, - &in_audio_[in_audio_ix_read_], - frame_len_smpl_, - (int16_t*)bitstream); - if (*bitstream_len_byte < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalEncode: error in encode for ILBC"); - return -1; - } - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - in_audio_ix_read_ += frame_len_smpl_; - return *bitstream_len_byte; -} - -int16_t ACMILBC::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return 0; -} - -int16_t ACMILBC::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { - // initialize with a correct processing block length - if ((160 == (codec_params->codec_inst).pacsize) || - (320 == (codec_params->codec_inst).pacsize)) { - // processing block of 20ms - return WebRtcIlbcfix_EncoderInit(encoder_inst_ptr_, 20); - } else if ((240 == (codec_params->codec_inst).pacsize) || - (480 == (codec_params->codec_inst).pacsize)) { - // processing block of 30ms - return WebRtcIlbcfix_EncoderInit(encoder_inst_ptr_, 30); - } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalInitEncoder: invalid processing block"); - return -1; - } -} - -int16_t ACMILBC::InternalInitDecoder(WebRtcACMCodecParams* codec_params) { - // initialize with a correct processing block length - if ((160 == (codec_params->codec_inst).pacsize) || - (320 == (codec_params->codec_inst).pacsize)) { - // processing block of 20ms - return WebRtcIlbcfix_DecoderInit(decoder_inst_ptr_, 20); - } else if ((240 == (codec_params->codec_inst).pacsize) || - (480 == (codec_params->codec_inst).pacsize)) { - // processing block of 30ms - return WebRtcIlbcfix_DecoderInit(decoder_inst_ptr_, 30); - } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalInitDecoder: invalid processing block"); - return -1; - } -} - -int32_t ACMILBC::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - if (!decoder_initialized_) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "CodeDef: decoder not initialized for ILBC"); - return -1; - } - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_ILBC_FUNCTION." - // Then return the structure back to NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codec_def), kDecoderILBC, codec_inst.pltype, decoder_inst_ptr_, - 8000); - SET_ILBC_FUNCTIONS((codec_def)); - return 0; -} - -ACMGenericCodec* ACMILBC::CreateInstance(void) { - return NULL; -} - -int16_t ACMILBC::InternalCreateEncoder() { - if (WebRtcIlbcfix_EncoderCreate(&encoder_inst_ptr_) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalCreateEncoder: cannot create instance for ILBC " - "encoder"); - return -1; - } - return 0; -} - -void ACMILBC::DestructEncoderSafe() { - encoder_initialized_ = false; - encoder_exist_ = false; - if (encoder_inst_ptr_ != NULL) { - WebRtcIlbcfix_EncoderFree(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } -} - -int16_t ACMILBC::InternalCreateDecoder() { - if (WebRtcIlbcfix_DecoderCreate(&decoder_inst_ptr_) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalCreateDecoder: cannot create instance for ILBC " - "decoder"); - return -1; - } - return 0; -} - -void ACMILBC::DestructDecoderSafe() { - decoder_initialized_ = false; - decoder_exist_ = false; - if (decoder_inst_ptr_ != NULL) { - WebRtcIlbcfix_DecoderFree(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } -} - -void ACMILBC::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcIlbcfix_EncoderFree((iLBC_encinst_t_*) ptr_inst); - } - return; -} - -int16_t ACMILBC::SetBitRateSafe(const int32_t rate) { - // Check that rate is valid. No need to store the value - if (rate == 13300) { - WebRtcIlbcfix_EncoderInit(encoder_inst_ptr_, 30); - } else if (rate == 15200) { - WebRtcIlbcfix_EncoderInit(encoder_inst_ptr_, 20); - } else { - return -1; - } - encoder_params_.codec_inst.rate = rate; - - return 0; -} - -#endif - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_ilbc.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_ilbc.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_ilbc.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_ilbc.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ILBC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ILBC_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -// forward declaration -struct iLBC_encinst_t_; -struct iLBC_decinst_t_; - -namespace webrtc { - -namespace acm1 { - -class ACMILBC : public ACMGenericCodec { - public: - explicit ACMILBC(int16_t codec_id); - virtual ~ACMILBC(); - - // for FEC - virtual ACMGenericCodec* CreateInstance(void) OVERRIDE; - - virtual int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) OVERRIDE; - - virtual int16_t InternalInitEncoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - virtual int16_t InternalInitDecoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - protected: - virtual int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type) OVERRIDE; - - virtual int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) OVERRIDE; - - virtual int16_t SetBitRateSafe(const int32_t rate) OVERRIDE; - - virtual void DestructEncoderSafe() OVERRIDE; - - virtual void DestructDecoderSafe() OVERRIDE; - - virtual int16_t InternalCreateEncoder() OVERRIDE; - - virtual int16_t InternalCreateDecoder() OVERRIDE; - - virtual void InternalDestructEncoderInst(void* ptr_inst) OVERRIDE; - - iLBC_encinst_t_* encoder_inst_ptr_; - iLBC_decinst_t_* decoder_inst_ptr_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ILBC_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_isac.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_isac.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_isac.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_isac.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,903 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#include "webrtc/modules/audio_coding/main/source/acm_isac.h" - -#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/trace.h" - -#ifdef WEBRTC_CODEC_ISAC -#include "webrtc/modules/audio_coding/codecs/isac/main/interface/isac.h" -#include "webrtc/modules/audio_coding/main/source/acm_isac_macros.h" -#endif - -#ifdef WEBRTC_CODEC_ISACFX -#include "webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h" -#include "webrtc/modules/audio_coding/main/source/acm_isac_macros.h" -#endif - -namespace webrtc { - -namespace acm1 { - -// we need this otherwise we cannot use forward declaration -// in the header file -#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) -struct ACMISACInst { - ACM_ISAC_STRUCT *inst; -}; -#endif - -#define ISAC_MIN_RATE 10000 -#define ISAC_MAX_RATE 56000 - -// Tables for bandwidth estimates -#define NR_ISAC_BANDWIDTHS 24 -static const int32_t kIsacRatesWb[NR_ISAC_BANDWIDTHS] = { - 10000, 11100, 12300, 13700, 15200, 16900, - 18800, 20900, 23300, 25900, 28700, 31900, - 10100, 11200, 12400, 13800, 15300, 17000, - 18900, 21000, 23400, 26000, 28800, 32000 -}; - -static const int32_t kIsacRatesSwb[NR_ISAC_BANDWIDTHS] = { - 10000, 11000, 12400, 13800, 15300, 17000, - 18900, 21000, 23200, 25400, 27600, 29800, - 32000, 34100, 36300, 38500, 40700, 42900, - 45100, 47300, 49500, 51700, 53900, 56000, -}; - -#if (!defined(WEBRTC_CODEC_ISAC) && !defined(WEBRTC_CODEC_ISACFX)) - -ACMISAC::ACMISAC(int16_t /* codec_id */) - : codec_inst_ptr_(NULL), - is_enc_initialized_(false), - isac_coding_mode_(CHANNEL_INDEPENDENT), - enforce_frame_size_(false), - isac_currentBN_(32000), - samples_in10MsAudio_(160) { // Initiates to 16 kHz mode. - // Initiate decoder parameters for the 32 kHz mode. - memset(&decoder_params32kHz_, 0, sizeof(WebRtcACMCodecParams)); - decoder_params32kHz_.codec_inst.pltype = -1; - - return; -} - -ACMISAC::~ACMISAC() { - return; -} - -ACMGenericCodec* ACMISAC::CreateInstance(void) { - return NULL; -} - -int16_t ACMISAC::InternalEncode( - uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - return -1; -} - -int16_t ACMISAC::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return 0; -} - -int16_t ACMISAC::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int16_t ACMISAC::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int16_t ACMISAC::InternalCreateDecoder() { - return -1; -} - -void ACMISAC::DestructDecoderSafe() { - return; -} - -int16_t ACMISAC::InternalCreateEncoder() { - return -1; -} - -void ACMISAC::DestructEncoderSafe() { - return; -} - -int32_t ACMISAC::CodecDef(WebRtcNetEQ_CodecDef& /* codec_def */, - const CodecInst& /* codec_inst */) { - return -1; -} - -void ACMISAC::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - -int16_t ACMISAC::DeliverCachedIsacData( - uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */, - uint32_t* /* timestamp */, - WebRtcACMEncodingType* /* encoding_type */, - const uint16_t /* isac_rate */, - const uint8_t /* isac_bw_estimate */) { - return -1; -} - -int16_t ACMISAC::Transcode(uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */, - int16_t /* q_bwe */, - int32_t /* scale */, - bool /* is_red */) { - return -1; -} - -int16_t ACMISAC::SetBitRateSafe(int32_t /* bit_rate */) { - return -1; -} - -int32_t ACMISAC::GetEstimatedBandwidthSafe() { - return -1; -} - -int32_t ACMISAC::SetEstimatedBandwidthSafe( - int32_t /* estimated_bandwidth */) { - return -1; -} - -int32_t ACMISAC::GetRedPayloadSafe(uint8_t* /* red_payload */, - int16_t* /* payload_bytes */) { - return -1; -} - -int16_t ACMISAC::UpdateDecoderSampFreq(int16_t /* codec_id */) { - return -1; -} - -int16_t ACMISAC::UpdateEncoderSampFreq( - uint16_t /* encoder_samp_freq_hz */) { - return -1; -} - -int16_t ACMISAC::EncoderSampFreq(uint16_t& /* samp_freq_hz */) { - return -1; -} - -int32_t ACMISAC::ConfigISACBandwidthEstimator( - const uint8_t /* init_frame_size_msec */, - const uint16_t /* init_rate_bit_per_sec */, - const bool /* enforce_frame_size */) { - return -1; -} - -int32_t ACMISAC::SetISACMaxPayloadSize( - const uint16_t /* max_payload_len_bytes */) { - return -1; -} - -int32_t ACMISAC::SetISACMaxRate( - const uint32_t /* max_rate_bit_per_sec */) { - return -1; -} - -void ACMISAC::UpdateFrameLen() { - return; -} - -void ACMISAC::CurrentRate(int32_t& /*rate_bit_per_sec */) { - return; -} - -bool -ACMISAC::DecoderParamsSafe( - WebRtcACMCodecParams* /* dec_params */, - const uint8_t /* payload_type */) { - return false; -} - -void -ACMISAC::SaveDecoderParamSafe( - const WebRtcACMCodecParams* /* codec_params */) { - return; -} - -int16_t ACMISAC::REDPayloadISAC( - const int32_t /* isac_rate */, - const int16_t /* isac_bw_estimate */, - uint8_t* /* payload */, - int16_t* /* payload_len_bytes */) { - return -1; -} - -#else //===================== Actual Implementation ======================= - -#ifdef WEBRTC_CODEC_ISACFX - -// How the scaling is computed. iSAC computes a gain based on the -// bottleneck. It follows the following expression for that -// -// G(BN_kbps) = pow(10, (a + b * BN_kbps + c * BN_kbps * BN_kbps) / 20.0) -// / 3.4641; -// -// Where for 30 ms framelength we have, -// -// a = -23; b = 0.48; c = 0; -// -// As the default encoder is operating at 32kbps we have the scale as -// -// S(BN_kbps) = G(BN_kbps) / G(32); - -#define ISAC_NUM_SUPPORTED_RATES 9 - -static const uint16_t kIsacSuportedRates[ISAC_NUM_SUPPORTED_RATES] = { - 32000, 30000, 26000, 23000, 21000, - 19000, 17000, 15000, 12000 -}; - -static const float kIsacScale[ISAC_NUM_SUPPORTED_RATES] = { - 1.0f, 0.8954f, 0.7178f, 0.6081f, 0.5445f, - 0.4875f, 0.4365f, 0.3908f, 0.3311f -}; - -enum IsacSamplingRate { - kIsacWideband = 16, - kIsacSuperWideband = 32 -}; - -static float ACMISACFixTranscodingScale(uint16_t rate) { - // find the scale for transcoding, the scale is rounded - // downward - float scale = -1; - for (int16_t n = 0; n < ISAC_NUM_SUPPORTED_RATES; n++) { - if (rate >= kIsacSuportedRates[n]) { - scale = kIsacScale[n]; - break; - } - } - return scale; -} - -static void ACMISACFixGetSendBitrate(ACM_ISAC_STRUCT* inst, - int32_t* bottleneck) { - *bottleneck = WebRtcIsacfix_GetUplinkBw(inst); -} - -static int16_t ACMISACFixGetNewBitstream(ACM_ISAC_STRUCT* inst, - int16_t bwe_index, - int16_t /* jitter_index */, - int32_t rate, - int16_t* bitstream, - bool is_red) { - if (is_red) { - // RED not supported with iSACFIX - return -1; - } - float scale = ACMISACFixTranscodingScale((uint16_t) rate); - return WebRtcIsacfix_GetNewBitStream(inst, bwe_index, scale, bitstream); -} - -static int16_t ACMISACFixGetSendBWE(ACM_ISAC_STRUCT* inst, - int16_t* rate_index, - int16_t* /* dummy */) { - int16_t local_rate_index; - int16_t status = WebRtcIsacfix_GetDownLinkBwIndex(inst, - &local_rate_index); - if (status < 0) { - return -1; - } else { - *rate_index = local_rate_index; - return 0; - } -} - -static int16_t ACMISACFixControlBWE(ACM_ISAC_STRUCT* inst, - int32_t rate_bps, - int16_t frame_size_ms, - int16_t enforce_frame_size) { - return WebRtcIsacfix_ControlBwe(inst, (int16_t) rate_bps, frame_size_ms, - enforce_frame_size); -} - -static int16_t ACMISACFixControl(ACM_ISAC_STRUCT* inst, - int32_t rate_bps, - int16_t frame_size_ms) { - return WebRtcIsacfix_Control(inst, (int16_t) rate_bps, frame_size_ms); -} - -// The following two function should have the same signature as their counter -// part in iSAC floating-point, i.e. WebRtcIsac_EncSampRate & -// WebRtcIsac_DecSampRate. -static uint16_t ACMISACFixGetEncSampRate(ACM_ISAC_STRUCT* /* inst */) { - return 16000; -} - -static uint16_t ACMISACFixGetDecSampRate(ACM_ISAC_STRUCT* /* inst */) { - return 16000; -} - -#endif - -ACMISAC::ACMISAC(int16_t codec_id) - : is_enc_initialized_(false), - isac_coding_mode_(CHANNEL_INDEPENDENT), - enforce_frame_size_(false), - isac_current_bn_(32000), - samples_in_10ms_audio_(160) { // Initiates to 16 kHz mode. - codec_id_ = codec_id; - - // Create codec instance. - codec_inst_ptr_ = new ACMISACInst; - if (codec_inst_ptr_ == NULL) { - return; - } - codec_inst_ptr_->inst = NULL; - - // Initiate decoder parameters for the 32 kHz mode. - memset(&decoder_params_32khz_, 0, sizeof(WebRtcACMCodecParams)); - decoder_params_32khz_.codec_inst.pltype = -1; - - // TODO(tlegrand): Check if the following is really needed, now that - // ACMGenericCodec has been updated to initialize this value. - // Initialize values that can be used uninitialized otherwise - decoder_params_.codec_inst.pltype = -1; -} - -ACMISAC::~ACMISAC() { - if (codec_inst_ptr_ != NULL) { - if (codec_inst_ptr_->inst != NULL) { - ACM_ISAC_FREE(codec_inst_ptr_->inst); - codec_inst_ptr_->inst = NULL; - } - delete codec_inst_ptr_; - codec_inst_ptr_ = NULL; - } - return; -} - -ACMGenericCodec* ACMISAC::CreateInstance(void) { - return NULL; -} - -int16_t ACMISAC::InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) { - // ISAC takes 10ms audio everytime we call encoder, therefor, - // it should be treated like codecs with 'basic coding block' - // non-zero, and the following 'while-loop' should not be necessary. - // However, due to a mistake in the codec the frame-size might change - // at the first 10ms pushed in to iSAC if the bit-rate is low, this is - // sort of a bug in iSAC. to address this we treat iSAC as the - // following. - if (codec_inst_ptr_ == NULL) { - return -1; - } - *bitstream_len_byte = 0; - while ((*bitstream_len_byte == 0) && (in_audio_ix_read_ < frame_len_smpl_)) { - if (in_audio_ix_read_ > in_audio_ix_write_) { - // something is wrong. - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "The actual fram-size of iSAC appears to be larger that " - "expected. All audio pushed in but no bit-stream is " - "generated."); - return -1; - } - *bitstream_len_byte = ACM_ISAC_ENCODE(codec_inst_ptr_->inst, - &in_audio_[in_audio_ix_read_], - (int16_t*)bitstream); - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - in_audio_ix_read_ += samples_in_10ms_audio_; - } - if (*bitstream_len_byte == 0) { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, unique_id_, - "ISAC Has encoded the whole frame but no bit-stream is " - "generated."); - } - - // a packet is generated iSAC, is set in adaptive mode may change - // the frame length and we like to update the bottleneck value as - // well, although updating bottleneck is not crucial - if ((*bitstream_len_byte > 0) && (isac_coding_mode_ == ADAPTIVE)) { - ACM_ISAC_GETSENDBITRATE(codec_inst_ptr_->inst, &isac_current_bn_); - } - UpdateFrameLen(); - return *bitstream_len_byte; -} - -int16_t ACMISAC::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_sample */, - int8_t* /* speech_type */) { - return 0; -} - -int16_t ACMISAC::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { - // if rate is set to -1 then iSAC has to be in adaptive mode - if (codec_params->codec_inst.rate == -1) { - isac_coding_mode_ = ADAPTIVE; - } else if ((codec_params->codec_inst.rate >= ISAC_MIN_RATE) && - (codec_params->codec_inst.rate <= ISAC_MAX_RATE)) { - // sanity check that rate is in acceptable range - isac_coding_mode_ = CHANNEL_INDEPENDENT; - isac_current_bn_ = codec_params->codec_inst.rate; - } else { - return -1; - } - - // we need to set the encoder sampling frequency. - if (UpdateEncoderSampFreq((uint16_t) codec_params->codec_inst.plfreq) - < 0) { - return -1; - } - if (ACM_ISAC_ENCODERINIT(codec_inst_ptr_->inst, isac_coding_mode_) < 0) { - return -1; - } - - // apply the frame-size and rate if operating in - // channel-independent mode - if (isac_coding_mode_ == CHANNEL_INDEPENDENT) { - if (ACM_ISAC_CONTROL(codec_inst_ptr_->inst, - codec_params->codec_inst.rate, - codec_params->codec_inst.pacsize / - (codec_params->codec_inst.plfreq / 1000)) < 0) { - return -1; - } - } else { - // We need this for adaptive case and has to be called - // after initialization - ACM_ISAC_GETSENDBITRATE(codec_inst_ptr_->inst, &isac_current_bn_); - } - frame_len_smpl_ = ACM_ISAC_GETNEWFRAMELEN(codec_inst_ptr_->inst); - return 0; -} - -int16_t ACMISAC::InternalInitDecoder(WebRtcACMCodecParams* codec_params) { - if (codec_inst_ptr_ == NULL) { - return -1; - } - - // set decoder sampling frequency. - if (codec_params->codec_inst.plfreq == 32000 || - codec_params->codec_inst.plfreq == 48000) { - UpdateDecoderSampFreq(ACMCodecDB::kISACSWB); - } else { - UpdateDecoderSampFreq(ACMCodecDB::kISAC); - } - - // in a one-way communication we may never register send-codec. - // However we like that the BWE to work properly so it has to - // be initialized. The BWE is initialized when iSAC encoder is initialized. - // Therefore, we need this. - if (!encoder_initialized_) { - // Since we don't require a valid rate or a valid packet size when - // initializing the decoder, we set valid values before initializing encoder - codec_params->codec_inst.rate = kIsacWbDefaultRate; - codec_params->codec_inst.pacsize = kIsacPacSize960; - if (InternalInitEncoder(codec_params) < 0) { - return -1; - } - encoder_initialized_ = true; - } - - return ACM_ISAC_DECODERINIT(codec_inst_ptr_->inst); -} - -int16_t ACMISAC::InternalCreateDecoder() { - if (codec_inst_ptr_ == NULL) { - return -1; - } - int16_t status = ACM_ISAC_CREATE(&(codec_inst_ptr_->inst)); - - // specific to codecs with one instance for encoding and decoding - encoder_initialized_ = false; - if (status < 0) { - encoder_exist_ = false; - } else { - encoder_exist_ = true; - } - return status; -} - -void ACMISAC::DestructDecoderSafe() { - // codec with shared instance cannot delete. - decoder_initialized_ = false; - return; -} - -int16_t ACMISAC::InternalCreateEncoder() { - if (codec_inst_ptr_ == NULL) { - return -1; - } - int16_t status = ACM_ISAC_CREATE(&(codec_inst_ptr_->inst)); - - // specific to codecs with one instance for encoding and decoding - decoder_initialized_ = false; - if (status < 0) { - decoder_exist_ = false; - } else { - decoder_exist_ = true; - } - return status; -} - -void ACMISAC::DestructEncoderSafe() { - // codec with shared instance cannot delete. - encoder_initialized_ = false; - return; -} - -int32_t ACMISAC::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - // Sanity checks - if (codec_inst_ptr_ == NULL) { - return -1; - } - if (!decoder_initialized_ || !decoder_exist_) { - return -1; - } - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_ISAC_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - if (codec_inst.plfreq == 16000) { - SET_CODEC_PAR((codec_def), kDecoderISAC, codec_inst.pltype, - codec_inst_ptr_->inst, 16000); -#ifdef WEBRTC_CODEC_ISAC - SET_ISAC_FUNCTIONS((codec_def)); -#else - SET_ISACfix_FUNCTIONS((codec_def)); -#endif - } else { -#ifdef WEBRTC_CODEC_ISAC - // Decoder is either @ 16 kHz or 32 kHz. Even if encoder is set @ 48 kHz - // decoding is @ 32 kHz. - if (codec_inst.plfreq == 32000) { - SET_CODEC_PAR((codec_def), kDecoderISACswb, codec_inst.pltype, - codec_inst_ptr_->inst, 32000); - SET_ISACSWB_FUNCTIONS((codec_def)); - } else { - SET_CODEC_PAR((codec_def), kDecoderISACfb, codec_inst.pltype, - codec_inst_ptr_->inst, 32000); - SET_ISACFB_FUNCTIONS((codec_def)); - } -#else - return -1; -#endif - } - return 0; -} - -void ACMISAC::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - ACM_ISAC_FREE((ACM_ISAC_STRUCT *) ptr_inst); - } - return; -} - -int16_t ACMISAC::Transcode(uint8_t* bitstream, - int16_t* bitstream_len_byte, - int16_t q_bwe, - int32_t rate, - bool is_red) { - int16_t jitter_info = 0; - // transcode from a higher rate to lower rate sanity check - if (codec_inst_ptr_ == NULL) { - return -1; - } - - *bitstream_len_byte = ACM_ISAC_GETNEWBITSTREAM(codec_inst_ptr_->inst, q_bwe, - jitter_info, rate, - (int16_t*)bitstream, - (is_red) ? 1 : 0); - - if (*bitstream_len_byte < 0) { - // error happened - *bitstream_len_byte = 0; - return -1; - } else { - return *bitstream_len_byte; - } -} - -int16_t ACMISAC::SetBitRateSafe(int32_t bit_rate) { - if (codec_inst_ptr_ == NULL) { - return -1; - } - uint16_t encoder_samp_freq; - EncoderSampFreq(encoder_samp_freq); - bool reinit = false; - // change the BN of iSAC - if (bit_rate == -1) { - // ADAPTIVE MODE - // Check if it was already in adaptive mode - if (isac_coding_mode_ != ADAPTIVE) { - // was not in adaptive, then set the mode to adaptive - // and flag for re-initialization - isac_coding_mode_ = ADAPTIVE; - reinit = true; - } - } else if ((bit_rate >= ISAC_MIN_RATE) && (bit_rate <= ISAC_MAX_RATE)) { - // Sanity check if the rate valid - // check if it was in channel-independent mode before - if (isac_coding_mode_ != CHANNEL_INDEPENDENT) { - // was not in channel independent, set the mode to - // channel-independent and flag for re-initialization - isac_coding_mode_ = CHANNEL_INDEPENDENT; - reinit = true; - } - // store the bottleneck - isac_current_bn_ = (uint16_t) bit_rate; - } else { - // invlaid rate - return -1; - } - - int16_t status = 0; - if (reinit) { - // initialize and check if it is successful - if (ACM_ISAC_ENCODERINIT(codec_inst_ptr_->inst, isac_coding_mode_) < 0) { - // failed initialization - return -1; - } - } - if (isac_coding_mode_ == CHANNEL_INDEPENDENT) { - status = ACM_ISAC_CONTROL( - codec_inst_ptr_->inst, isac_current_bn_, - (encoder_samp_freq == 32000 || encoder_samp_freq == 48000) ? 30 : - (frame_len_smpl_ / 16)); - if (status < 0) { - status = -1; - } - } - - // Update encoder parameters - encoder_params_.codec_inst.rate = bit_rate; - - UpdateFrameLen(); - return status; -} - -int32_t ACMISAC::GetEstimatedBandwidthSafe() { - int16_t bandwidth_index = 0; - int16_t delay_index = 0; - int samp_rate; - - // Get bandwidth information - ACM_ISAC_GETSENDBWE(codec_inst_ptr_->inst, &bandwidth_index, &delay_index); - - // Validy check of index - if ((bandwidth_index < 0) || (bandwidth_index >= NR_ISAC_BANDWIDTHS)) { - return -1; - } - - // Check sample frequency - samp_rate = ACM_ISAC_GETDECSAMPRATE(codec_inst_ptr_->inst); - if (samp_rate == 16000) { - return kIsacRatesWb[bandwidth_index]; - } else { - return kIsacRatesSwb[bandwidth_index]; - } -} - -int32_t ACMISAC::SetEstimatedBandwidthSafe( - int32_t estimated_bandwidth) { - int samp_rate; - int16_t bandwidth_index; - - // Check sample frequency and choose appropriate table - samp_rate = ACM_ISAC_GETENCSAMPRATE(codec_inst_ptr_->inst); - - if (samp_rate == 16000) { - // Search through the WB rate table to find the index - bandwidth_index = NR_ISAC_BANDWIDTHS / 2 - 1; - for (int i = 0; i < (NR_ISAC_BANDWIDTHS / 2); i++) { - if (estimated_bandwidth == kIsacRatesWb[i]) { - bandwidth_index = i; - break; - } else if (estimated_bandwidth - == kIsacRatesWb[i + NR_ISAC_BANDWIDTHS / 2]) { - bandwidth_index = i + NR_ISAC_BANDWIDTHS / 2; - break; - } else if (estimated_bandwidth < kIsacRatesWb[i]) { - bandwidth_index = i; - break; - } - } - } else { - // Search through the SWB rate table to find the index - bandwidth_index = NR_ISAC_BANDWIDTHS - 1; - for (int i = 0; i < NR_ISAC_BANDWIDTHS; i++) { - if (estimated_bandwidth <= kIsacRatesSwb[i]) { - bandwidth_index = i; - break; - } - } - } - - // Set iSAC Bandwidth Estimate - ACM_ISAC_SETBWE(codec_inst_ptr_->inst, bandwidth_index); - - return 0; -} - -int32_t ACMISAC::GetRedPayloadSafe( -#if (!defined(WEBRTC_CODEC_ISAC)) - uint8_t* /* red_payload */, int16_t* /* payload_bytes */) { - return -1; -#else - uint8_t* red_payload, int16_t* payload_bytes) { - int16_t bytes = WebRtcIsac_GetRedPayload(codec_inst_ptr_->inst, - (int16_t*)red_payload); - if (bytes < 0) { - return -1; - } - *payload_bytes = bytes; - return 0; -#endif -} - -int16_t ACMISAC::UpdateDecoderSampFreq( -#ifdef WEBRTC_CODEC_ISAC - int16_t codec_id) { - // The decoder supports only wideband and super-wideband. - if (ACMCodecDB::kISAC == codec_id) { - return WebRtcIsac_SetDecSampRate(codec_inst_ptr_->inst, 16000); - } else if (ACMCodecDB::kISACSWB == codec_id || - ACMCodecDB::kISACFB == codec_id) { - return WebRtcIsac_SetDecSampRate(codec_inst_ptr_->inst, 32000); - } else { - return -1; - } -#else - int16_t /* codec_id */) { - return 0; -#endif -} - -int16_t ACMISAC::UpdateEncoderSampFreq( -#ifdef WEBRTC_CODEC_ISAC - uint16_t encoder_samp_freq_hz) { - uint16_t current_samp_rate_hz; - EncoderSampFreq(current_samp_rate_hz); - - if (current_samp_rate_hz != encoder_samp_freq_hz) { - if ((encoder_samp_freq_hz != 16000) && - (encoder_samp_freq_hz != 32000) && - (encoder_samp_freq_hz != 48000)) { - return -1; - } else { - in_audio_ix_read_ = 0; - in_audio_ix_write_ = 0; - in_timestamp_ix_write_ = 0; - if (WebRtcIsac_SetEncSampRate(codec_inst_ptr_->inst, - encoder_samp_freq_hz) < 0) { - return -1; - } - samples_in_10ms_audio_ = encoder_samp_freq_hz / 100; - frame_len_smpl_ = ACM_ISAC_GETNEWFRAMELEN(codec_inst_ptr_->inst); - encoder_params_.codec_inst.pacsize = frame_len_smpl_; - encoder_params_.codec_inst.plfreq = encoder_samp_freq_hz; - return 0; - } - } -#else - uint16_t /* codec_id */) { -#endif - return 0; -} - -int16_t ACMISAC::EncoderSampFreq(uint16_t& samp_freq_hz) { - samp_freq_hz = ACM_ISAC_GETENCSAMPRATE(codec_inst_ptr_->inst); - return 0; -} - -int32_t ACMISAC::ConfigISACBandwidthEstimator( - const uint8_t init_frame_size_msec, - const uint16_t init_rate_bit_per_sec, - const bool enforce_frame_size) { - int16_t status; - { - uint16_t samp_freq_hz; - EncoderSampFreq(samp_freq_hz); - // TODO(turajs): at 32kHz we hardcode calling with 30ms and enforce - // the frame-size otherwise we might get error. Revise if - // control-bwe is changed. - if (samp_freq_hz == 32000 || samp_freq_hz == 48000) { - status = ACM_ISAC_CONTROL_BWE(codec_inst_ptr_->inst, - init_rate_bit_per_sec, 30, 1); - } else { - status = ACM_ISAC_CONTROL_BWE(codec_inst_ptr_->inst, - init_rate_bit_per_sec, - init_frame_size_msec, - enforce_frame_size ? 1 : 0); - } - } - if (status < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Couldn't config iSAC BWE."); - return -1; - } - UpdateFrameLen(); - ACM_ISAC_GETSENDBITRATE(codec_inst_ptr_->inst, &isac_current_bn_); - return 0; -} - -int32_t ACMISAC::SetISACMaxPayloadSize( - const uint16_t max_payload_len_bytes) { - return ACM_ISAC_SETMAXPAYLOADSIZE(codec_inst_ptr_->inst, - max_payload_len_bytes); -} - -int32_t ACMISAC::SetISACMaxRate( - const uint32_t max_rate_bit_per_sec) { - return ACM_ISAC_SETMAXRATE(codec_inst_ptr_->inst, max_rate_bit_per_sec); -} - -void ACMISAC::UpdateFrameLen() { - frame_len_smpl_ = ACM_ISAC_GETNEWFRAMELEN(codec_inst_ptr_->inst); - encoder_params_.codec_inst.pacsize = frame_len_smpl_; -} - -void ACMISAC::CurrentRate(int32_t& rate_bit_per_sec) { - if (isac_coding_mode_ == ADAPTIVE) { - ACM_ISAC_GETSENDBITRATE(codec_inst_ptr_->inst, &rate_bit_per_sec); - } -} - -bool ACMISAC::DecoderParamsSafe(WebRtcACMCodecParams* dec_params, - const uint8_t payload_type) { - if (decoder_initialized_) { - if (payload_type == decoder_params_.codec_inst.pltype) { - memcpy(dec_params, &decoder_params_, sizeof(WebRtcACMCodecParams)); - return true; - } - if (payload_type == decoder_params_32khz_.codec_inst.pltype) { - memcpy(dec_params, &decoder_params_32khz_, sizeof(WebRtcACMCodecParams)); - return true; - } - } - return false; -} - -void ACMISAC::SaveDecoderParamSafe(const WebRtcACMCodecParams* codec_params) { - // set decoder sampling frequency. - if (codec_params->codec_inst.plfreq == 32000 || - codec_params->codec_inst.plfreq == 48000) { - memcpy(&decoder_params_32khz_, codec_params, sizeof(WebRtcACMCodecParams)); - } else { - memcpy(&decoder_params_, codec_params, sizeof(WebRtcACMCodecParams)); - } -} - -int16_t ACMISAC::REDPayloadISAC(const int32_t isac_rate, - const int16_t isac_bw_estimate, - uint8_t* payload, - int16_t* payload_len_bytes) { - int16_t status; - ReadLockScoped rl(codec_wrapper_lock_); - status = Transcode(payload, payload_len_bytes, isac_bw_estimate, isac_rate, - true); - return status; -} - -#endif - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_isac.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_isac.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_isac.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_isac.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ISAC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ISAC_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -namespace webrtc { - -namespace acm1 { - -struct ACMISACInst; - -enum IsacCodingMode { - ADAPTIVE, - CHANNEL_INDEPENDENT -}; - -class ACMISAC : public ACMGenericCodec { - public: - explicit ACMISAC(int16_t codec_id); - virtual ~ACMISAC(); - - // for FEC - virtual ACMGenericCodec* CreateInstance(void) OVERRIDE; - - virtual int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) OVERRIDE; - - virtual int16_t InternalInitEncoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - virtual int16_t InternalInitDecoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - int16_t DeliverCachedIsacData(uint8_t* bitstream, - int16_t* bitstream_len_byte, - uint32_t* timestamp, - WebRtcACMEncodingType* encoding_type, - const uint16_t isac_rate, - const uint8_t isac_bwestimate); - - int16_t DeliverCachedData(uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */, - uint32_t* /* timestamp */, - WebRtcACMEncodingType* /* encoding_type */) { - return -1; - } - - virtual int16_t UpdateDecoderSampFreq(int16_t codec_id) OVERRIDE; - - virtual int16_t UpdateEncoderSampFreq(uint16_t samp_freq_hz) OVERRIDE; - - virtual int16_t EncoderSampFreq(uint16_t& samp_freq_hz) OVERRIDE; - - virtual int32_t ConfigISACBandwidthEstimator( - const uint8_t init_frame_size_msec, - const uint16_t init_rate_bit_per_sec, - const bool enforce_frame_size) OVERRIDE; - - virtual int32_t SetISACMaxPayloadSize( - const uint16_t max_payload_len_bytes) OVERRIDE; - - virtual int32_t SetISACMaxRate(const uint32_t max_rate_bit_per_sec) OVERRIDE; - - virtual int16_t REDPayloadISAC(const int32_t isac_rate, - const int16_t isac_bw_estimate, - uint8_t* payload, - int16_t* payload_len_bytes) OVERRIDE; - - protected: - virtual int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type) OVERRIDE; - - virtual int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) OVERRIDE; - - virtual void DestructEncoderSafe() OVERRIDE; - - virtual void DestructDecoderSafe() OVERRIDE; - - virtual int16_t SetBitRateSafe(const int32_t bit_rate) OVERRIDE; - - virtual int32_t GetEstimatedBandwidthSafe() OVERRIDE; - - virtual int32_t SetEstimatedBandwidthSafe( - int32_t estimated_bandwidth) OVERRIDE; - - virtual int32_t GetRedPayloadSafe(uint8_t* red_payload, - int16_t* payload_bytes) OVERRIDE; - - virtual int16_t InternalCreateEncoder() OVERRIDE; - - virtual int16_t InternalCreateDecoder() OVERRIDE; - - virtual void InternalDestructEncoderInst(void* ptr_inst) OVERRIDE; - - int16_t Transcode(uint8_t* bitstream, - int16_t* bitstream_len_byte, - int16_t q_bwe, - int32_t rate, - bool is_red); - - virtual void CurrentRate(int32_t& rate_bit_per_sec) OVERRIDE; - - void UpdateFrameLen(); - - virtual bool DecoderParamsSafe(WebRtcACMCodecParams* dec_params, - const uint8_t payload_type) OVERRIDE; - - virtual void SaveDecoderParamSafe( - const WebRtcACMCodecParams* codec_params) OVERRIDE; - - ACMISACInst* codec_inst_ptr_; - bool is_enc_initialized_; - IsacCodingMode isac_coding_mode_; - bool enforce_frame_size_; - int32_t isac_current_bn_; - uint16_t samples_in_10ms_audio_; - WebRtcACMCodecParams decoder_params_32khz_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ISAC_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_isac_macros.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_isac_macros.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_isac_macros.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_isac_macros.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ISAC_MACROS_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ISAC_MACROS_H_ - -#include "webrtc/engine_configurations.h" - -namespace webrtc { - -namespace acm1 { - -#ifdef WEBRTC_CODEC_ISAC -#define ACM_ISAC_CREATE WebRtcIsac_Create -#define ACM_ISAC_FREE WebRtcIsac_Free -#define ACM_ISAC_ENCODERINIT WebRtcIsac_EncoderInit -#define ACM_ISAC_ENCODE WebRtcIsac_Encode -#define ACM_ISAC_DECODERINIT WebRtcIsac_DecoderInit -#define ACM_ISAC_DECODE_BWE WebRtcIsac_UpdateBwEstimate -#define ACM_ISAC_DECODE_B WebRtcIsac_Decode -#define ACM_ISAC_DECODEPLC WebRtcIsac_DecodePlc -#define ACM_ISAC_CONTROL WebRtcIsac_Control -#define ACM_ISAC_CONTROL_BWE WebRtcIsac_ControlBwe -#define ACM_ISAC_GETFRAMELEN WebRtcIsac_ReadFrameLen -#define ACM_ISAC_GETERRORCODE WebRtcIsac_GetErrorCode -#define ACM_ISAC_GETSENDBITRATE WebRtcIsac_GetUplinkBw -#define ACM_ISAC_SETMAXPAYLOADSIZE WebRtcIsac_SetMaxPayloadSize -#define ACM_ISAC_SETMAXRATE WebRtcIsac_SetMaxRate -#define ACM_ISAC_GETNEWBITSTREAM WebRtcIsac_GetNewBitStream -#define ACM_ISAC_GETSENDBWE WebRtcIsac_GetDownLinkBwIndex -#define ACM_ISAC_SETBWE WebRtcIsac_UpdateUplinkBw -#define ACM_ISAC_GETBWE WebRtcIsac_ReadBwIndex -#define ACM_ISAC_GETNEWFRAMELEN WebRtcIsac_GetNewFrameLen -#define ACM_ISAC_STRUCT ISACStruct -#define ACM_ISAC_GETENCSAMPRATE WebRtcIsac_EncSampRate -#define ACM_ISAC_GETDECSAMPRATE WebRtcIsac_DecSampRate -#endif - -#ifdef WEBRTC_CODEC_ISACFX -#define ACM_ISAC_CREATE WebRtcIsacfix_Create -#define ACM_ISAC_FREE WebRtcIsacfix_Free -#define ACM_ISAC_ENCODERINIT WebRtcIsacfix_EncoderInit -#define ACM_ISAC_ENCODE WebRtcIsacfix_Encode -#define ACM_ISAC_DECODERINIT WebRtcIsacfix_DecoderInit -#define ACM_ISAC_DECODE_BWE WebRtcIsacfix_UpdateBwEstimate -#define ACM_ISAC_DECODE_B WebRtcIsacfix_Decode -#define ACM_ISAC_DECODEPLC WebRtcIsacfix_DecodePlc -#define ACM_ISAC_CONTROL ACMISACFixControl // local Impl -#define ACM_ISAC_CONTROL_BWE ACMISACFixControlBWE // local Impl -#define ACM_ISAC_GETFRAMELEN WebRtcIsacfix_ReadFrameLen -#define ACM_ISAC_GETERRORCODE WebRtcIsacfix_GetErrorCode -#define ACM_ISAC_GETSENDBITRATE ACMISACFixGetSendBitrate // local Impl -#define ACM_ISAC_SETMAXPAYLOADSIZE WebRtcIsacfix_SetMaxPayloadSize -#define ACM_ISAC_SETMAXRATE WebRtcIsacfix_SetMaxRate -#define ACM_ISAC_GETNEWBITSTREAM ACMISACFixGetNewBitstream // local Impl -#define ACM_ISAC_GETSENDBWE ACMISACFixGetSendBWE // local Impl -#define ACM_ISAC_SETBWE WebRtcIsacfix_UpdateUplinkBw -#define ACM_ISAC_GETBWE WebRtcIsacfix_ReadBwIndex -#define ACM_ISAC_GETNEWFRAMELEN WebRtcIsacfix_GetNewFrameLen -#define ACM_ISAC_STRUCT ISACFIX_MainStruct -#define ACM_ISAC_GETENCSAMPRATE ACMISACFixGetEncSampRate // local Impl -#define ACM_ISAC_GETDECSAMPRATE ACMISACFixGetDecSampRate // local Impl -#endif - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_ISAC_MACROS_H_ - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_neteq.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_neteq.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_neteq.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_neteq.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,1151 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" - -#include // malloc - -#include // sort -#include - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/common_types.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_internal.h" -#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" -#include "webrtc/system_wrappers/interface/trace_event.h" - -namespace webrtc { - -namespace acm1 { - -#define RTP_HEADER_SIZE 12 -#define NETEQ_INIT_FREQ 8000 -#define NETEQ_INIT_FREQ_KHZ (NETEQ_INIT_FREQ/1000) -#define NETEQ_ERR_MSG_LEN_BYTE (WEBRTC_NETEQ_MAX_ERROR_NAME + 1) - -ACMNetEQ::ACMNetEQ() - : id_(0), - current_samp_freq_khz_(NETEQ_INIT_FREQ_KHZ), - avt_playout_(false), - playout_mode_(voice), - neteq_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), - vad_status_(false), - vad_mode_(VADNormal), - decode_lock_(RWLockWrapper::CreateRWLock()), - num_slaves_(0), - received_stereo_(false), - master_slave_info_(NULL), - previous_audio_activity_(AudioFrame::kVadUnknown), - callback_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), - min_of_max_num_packets_(0), - min_of_buffer_size_bytes_(0), - per_packet_overhead_bytes_(0), - av_sync_(false), - minimum_delay_ms_(0), - maximum_delay_ms_(0) { - for (int n = 0; n < MAX_NUM_SLAVE_NETEQ + 1; n++) { - is_initialized_[n] = false; - ptr_vadinst_[n] = NULL; - inst_[n] = NULL; - inst_mem_[n] = NULL; - neteq_packet_buffer_[n] = NULL; - } -} - -ACMNetEQ::~ACMNetEQ() { - { - CriticalSectionScoped lock(neteq_crit_sect_); - RemoveNetEQSafe(0); // Master. - RemoveSlavesSafe(); - } - if (neteq_crit_sect_ != NULL) { - delete neteq_crit_sect_; - } - - if (decode_lock_ != NULL) { - delete decode_lock_; - } - - if (callback_crit_sect_ != NULL) { - delete callback_crit_sect_; - } -} - -int32_t ACMNetEQ::Init() { - CriticalSectionScoped lock(neteq_crit_sect_); - - for (int16_t idx = 0; idx < num_slaves_ + 1; idx++) { - if (InitByIdxSafe(idx) < 0) { - return -1; - } - // delete VAD instance and start fresh if required. - if (ptr_vadinst_[idx] != NULL) { - WebRtcVad_Free(ptr_vadinst_[idx]); - ptr_vadinst_[idx] = NULL; - } - if (vad_status_) { - // Has to enable VAD - if (EnableVADByIdxSafe(idx) < 0) { - // Failed to enable VAD. - // Delete VAD instance, if it is created - if (ptr_vadinst_[idx] != NULL) { - WebRtcVad_Free(ptr_vadinst_[idx]); - ptr_vadinst_[idx] = NULL; - } - // We are at initialization of NetEq, if failed to - // enable VAD, we delete the NetEq instance. - if (inst_mem_[idx] != NULL) { - free(inst_mem_[idx]); - inst_mem_[idx] = NULL; - inst_[idx] = NULL; - } - is_initialized_[idx] = false; - return -1; - } - } - is_initialized_[idx] = true; - } - if (EnableVAD() == -1) { - return -1; - } - return 0; -} - -int16_t ACMNetEQ::InitByIdxSafe(const int16_t idx) { - int memory_size_bytes; - if (WebRtcNetEQ_AssignSize(&memory_size_bytes) != 0) { - LogError("AssignSize", idx); - return -1; - } - - if (inst_mem_[idx] != NULL) { - free(inst_mem_[idx]); - inst_mem_[idx] = NULL; - inst_[idx] = NULL; - } - inst_mem_[idx] = malloc(memory_size_bytes); - if (inst_mem_[idx] == NULL) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "InitByIdxSafe: NetEq Initialization error: could not " - "allocate memory for NetEq"); - is_initialized_[idx] = false; - return -1; - } - if (WebRtcNetEQ_Assign(&inst_[idx], inst_mem_[idx]) != 0) { - if (inst_mem_[idx] != NULL) { - free(inst_mem_[idx]); - inst_mem_[idx] = NULL; - inst_[idx] = NULL; - } - LogError("Assign", idx); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "InitByIdxSafe: NetEq Initialization error: could not Assign"); - is_initialized_[idx] = false; - return -1; - } - if (WebRtcNetEQ_Init(inst_[idx], NETEQ_INIT_FREQ) != 0) { - if (inst_mem_[idx] != NULL) { - free(inst_mem_[idx]); - inst_mem_[idx] = NULL; - inst_[idx] = NULL; - } - LogError("Init", idx); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "InitByIdxSafe: NetEq Initialization error: could not " - "initialize NetEq"); - is_initialized_[idx] = false; - return -1; - } - is_initialized_[idx] = true; - return 0; -} - -int16_t ACMNetEQ::EnableVADByIdxSafe(const int16_t idx) { - if (ptr_vadinst_[idx] == NULL) { - if (WebRtcVad_Create(&ptr_vadinst_[idx]) < 0) { - ptr_vadinst_[idx] = NULL; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "EnableVADByIdxSafe: NetEq Initialization error: could not " - "create VAD"); - return -1; - } - } - - if (WebRtcNetEQ_SetVADInstance( - inst_[idx], ptr_vadinst_[idx], - (WebRtcNetEQ_VADInitFunction) WebRtcVad_Init, - (WebRtcNetEQ_VADSetmodeFunction) WebRtcVad_set_mode, - (WebRtcNetEQ_VADFunction) WebRtcVad_Process) < 0) { - LogError("setVADinstance", idx); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "EnableVADByIdxSafe: NetEq Initialization error: could not " - "set VAD instance"); - return -1; - } - - if (WebRtcNetEQ_SetVADMode(inst_[idx], vad_mode_) < 0) { - LogError("setVADmode", idx); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "EnableVADByIdxSafe: NetEq Initialization error: could not " - "set VAD mode"); - return -1; - } - return 0; -} - -int32_t ACMNetEQ::AllocatePacketBuffer( - const WebRtcNetEQDecoder* used_codecs, - int16_t num_codecs) { - // Due to WebRtcNetEQ_GetRecommendedBufferSize - // the following has to be int otherwise we will have compiler error - // if not casted - - CriticalSectionScoped lock(neteq_crit_sect_); - for (int16_t idx = 0; idx < num_slaves_ + 1; idx++) { - if (AllocatePacketBufferByIdxSafe(used_codecs, num_codecs, idx) < 0) { - return -1; - } - } - return 0; -} - -int16_t ACMNetEQ::AllocatePacketBufferByIdxSafe( - const WebRtcNetEQDecoder* used_codecs, - int16_t num_codecs, - const int16_t idx) { - int max_num_packets; - int buffer_size_in_bytes; - int per_packet_overhead_bytes; - - if (!is_initialized_[idx]) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "AllocatePacketBufferByIdxSafe: NetEq is not initialized."); - return -1; - } - if (WebRtcNetEQ_GetRecommendedBufferSize(inst_[idx], used_codecs, - num_codecs, - kTCPXLargeJitter, - &max_num_packets, - &buffer_size_in_bytes, - &per_packet_overhead_bytes) != 0) { - LogError("GetRecommendedBufferSize", idx); - return -1; - } - if (idx == 0) { - min_of_buffer_size_bytes_ = buffer_size_in_bytes; - min_of_max_num_packets_ = max_num_packets; - per_packet_overhead_bytes_ = per_packet_overhead_bytes; - } else { - min_of_buffer_size_bytes_ = std::min(min_of_buffer_size_bytes_, - buffer_size_in_bytes); - min_of_max_num_packets_ = std::min(min_of_max_num_packets_, - max_num_packets); - } - if (neteq_packet_buffer_[idx] != NULL) { - free(neteq_packet_buffer_[idx]); - neteq_packet_buffer_[idx] = NULL; - } - - neteq_packet_buffer_[idx] = (int16_t *) malloc(buffer_size_in_bytes); - if (neteq_packet_buffer_[idx] == NULL) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "AllocatePacketBufferByIdxSafe: NetEq Initialization error: " - "could not allocate memory for NetEq Packet Buffer"); - return -1; - } - if (WebRtcNetEQ_AssignBuffer(inst_[idx], max_num_packets, - neteq_packet_buffer_[idx], - buffer_size_in_bytes) != 0) { - if (neteq_packet_buffer_[idx] != NULL) { - free(neteq_packet_buffer_[idx]); - neteq_packet_buffer_[idx] = NULL; - } - LogError("AssignBuffer", idx); - return -1; - } - return 0; -} - -int32_t ACMNetEQ::SetAVTPlayout(const bool enable) { - CriticalSectionScoped lock(neteq_crit_sect_); - if (avt_playout_ != enable) { - for (int16_t idx = 0; idx < num_slaves_ + 1; idx++) { - if (!is_initialized_[idx]) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "SetAVTPlayout: NetEq is not initialized."); - return -1; - } - if (WebRtcNetEQ_SetAVTPlayout(inst_[idx], (enable) ? 1 : 0) < 0) { - LogError("SetAVTPlayout", idx); - return -1; - } - } - } - avt_playout_ = enable; - return 0; -} - -bool ACMNetEQ::avt_playout() const { - CriticalSectionScoped lock(neteq_crit_sect_); - return avt_playout_; -} - -int32_t ACMNetEQ::CurrentSampFreqHz() const { - CriticalSectionScoped lock(neteq_crit_sect_); - if (!is_initialized_[0]) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "CurrentSampFreqHz: NetEq is not initialized."); - return -1; - } - return (int32_t)(1000 * current_samp_freq_khz_); -} - -int32_t ACMNetEQ::SetPlayoutMode(const AudioPlayoutMode mode) { - CriticalSectionScoped lock(neteq_crit_sect_); - if (playout_mode_ == mode) - return 0; - - enum WebRtcNetEQPlayoutMode playout_mode = kPlayoutOff; - enum WebRtcNetEQBGNMode background_noise_mode = kBGNOn; - switch (mode) { - case voice: - playout_mode = kPlayoutOn; - background_noise_mode = kBGNOn; - break; - case fax: - playout_mode = kPlayoutFax; - WebRtcNetEQ_GetBGNMode(inst_[0], &background_noise_mode); // No change. - break; - case streaming: - playout_mode = kPlayoutStreaming; - background_noise_mode = kBGNOff; - break; - case off: - playout_mode = kPlayoutOff; - background_noise_mode = kBGNOff; - break; - } - - int err = 0; - for (int16_t idx = 0; idx < num_slaves_ + 1; idx++) { - if (!is_initialized_[idx]) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "SetPlayoutMode: NetEq is not initialized."); - return -1; - } - - if (WebRtcNetEQ_SetPlayoutMode(inst_[idx], playout_mode) < 0) { - LogError("SetPlayoutMode", idx); - err = -1; - } - - if (WebRtcNetEQ_SetBGNMode(inst_[idx], kBGNOff) < 0) { - LogError("SetPlayoutMode::SetBGNMode", idx); - err = -1; - } - } - if (err == 0) - playout_mode_ = mode; - return err; -} - -AudioPlayoutMode ACMNetEQ::playout_mode() const { - CriticalSectionScoped lock(neteq_crit_sect_); - return playout_mode_; -} - -int32_t ACMNetEQ::NetworkStatistics( - ACMNetworkStatistics* statistics) const { - WebRtcNetEQ_NetworkStatistics stats; - CriticalSectionScoped lock(neteq_crit_sect_); - if (!is_initialized_[0]) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "NetworkStatistics: NetEq is not initialized."); - return -1; - } - if (WebRtcNetEQ_GetNetworkStatistics(inst_[0], &stats) == 0) { - statistics->currentAccelerateRate = stats.currentAccelerateRate; - statistics->currentBufferSize = stats.currentBufferSize; - statistics->jitterPeaksFound = (stats.jitterPeaksFound > 0); - statistics->currentDiscardRate = stats.currentDiscardRate; - statistics->currentExpandRate = stats.currentExpandRate; - statistics->currentPacketLossRate = stats.currentPacketLossRate; - statistics->currentPreemptiveRate = stats.currentPreemptiveRate; - statistics->preferredBufferSize = stats.preferredBufferSize; - statistics->clockDriftPPM = stats.clockDriftPPM; - statistics->addedSamples = stats.addedSamples; - } else { - LogError("getNetworkStatistics", 0); - return -1; - } - const int kArrayLen = 100; - int waiting_times[kArrayLen]; - int waiting_times_len = WebRtcNetEQ_GetRawFrameWaitingTimes(inst_[0], - kArrayLen, - waiting_times); - if (waiting_times_len > 0) { - std::vector waiting_times_vec(waiting_times, - waiting_times + waiting_times_len); - std::sort(waiting_times_vec.begin(), waiting_times_vec.end()); - size_t size = waiting_times_vec.size(); - assert(size == static_cast(waiting_times_len)); - if (size % 2 == 0) { - statistics->medianWaitingTimeMs = (waiting_times_vec[size / 2 - 1] + - waiting_times_vec[size / 2]) / 2; - } else { - statistics->medianWaitingTimeMs = waiting_times_vec[size / 2]; - } - statistics->minWaitingTimeMs = waiting_times_vec.front(); - statistics->maxWaitingTimeMs = waiting_times_vec.back(); - double sum = 0; - for (size_t i = 0; i < size; ++i) { - sum += waiting_times_vec[i]; - } - statistics->meanWaitingTimeMs = static_cast(sum / size); - } else if (waiting_times_len == 0) { - statistics->meanWaitingTimeMs = -1; - statistics->medianWaitingTimeMs = -1; - statistics->minWaitingTimeMs = -1; - statistics->maxWaitingTimeMs = -1; - } else { - LogError("getRawFrameWaitingTimes", 0); - return -1; - } - return 0; -} - -// Should only be called in AV-sync mode. -int ACMNetEQ::RecIn(const WebRtcRTPHeader& rtp_info, - uint32_t receive_timestamp) { - assert(av_sync_); - - // Translate to NetEq structure. - WebRtcNetEQ_RTPInfo neteq_rtpinfo; - neteq_rtpinfo.payloadType = rtp_info.header.payloadType; - neteq_rtpinfo.sequenceNumber = rtp_info.header.sequenceNumber; - neteq_rtpinfo.timeStamp = rtp_info.header.timestamp; - neteq_rtpinfo.SSRC = rtp_info.header.ssrc; - neteq_rtpinfo.markerBit = rtp_info.header.markerBit; - - CriticalSectionScoped lock(neteq_crit_sect_); - - // Master should be initialized. - assert(is_initialized_[0]); - - // Push into Master. - int status = WebRtcNetEQ_RecInSyncRTP(inst_[0], &neteq_rtpinfo, - receive_timestamp); - if (status < 0) { - LogError("RecInSyncRTP", 0); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "RecIn (sync): NetEq, error in pushing in Master"); - return -1; - } - - // If the received stream is stereo, insert a sync payload into slave. - if (rtp_info.type.Audio.channel == 2) { - // Slave should be initialized. - assert(is_initialized_[1]); - - // PUSH into Slave - status = WebRtcNetEQ_RecInSyncRTP(inst_[1], &neteq_rtpinfo, - receive_timestamp); - if (status < 0) { - LogError("RecInRTPStruct", 1); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "RecIn (sync): NetEq, error in pushing in Slave"); - return -1; - } - } - return status; -} - -int32_t ACMNetEQ::RecIn(const uint8_t* incoming_payload, - const int32_t length_payload, - const WebRtcRTPHeader& rtp_info, - uint32_t receive_timestamp) { - int16_t payload_length = static_cast(length_payload); - - // Translate to NetEq structure. - WebRtcNetEQ_RTPInfo neteq_rtpinfo; - neteq_rtpinfo.payloadType = rtp_info.header.payloadType; - neteq_rtpinfo.sequenceNumber = rtp_info.header.sequenceNumber; - neteq_rtpinfo.timeStamp = rtp_info.header.timestamp; - neteq_rtpinfo.SSRC = rtp_info.header.ssrc; - neteq_rtpinfo.markerBit = rtp_info.header.markerBit; - - CriticalSectionScoped lock(neteq_crit_sect_); - - int status; - // In case of stereo payload, first half of the data should be pushed into - // master, and the second half into slave. - if (rtp_info.type.Audio.channel == 2) { - payload_length = payload_length / 2; - } - - // Check that master is initialized. - if (!is_initialized_[0]) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "RecIn: NetEq is not initialized."); - return -1; - } - // Push into Master. - status = WebRtcNetEQ_RecInRTPStruct(inst_[0], &neteq_rtpinfo, - incoming_payload, payload_length, - receive_timestamp); - if (status < 0) { - LogError("RecInRTPStruct", 0); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "RecIn: NetEq, error in pushing in Master"); - return -1; - } - - // If the received stream is stereo, insert second half of paket into slave. - if (rtp_info.type.Audio.channel == 2) { - if (!is_initialized_[1]) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "RecIn: NetEq is not initialized."); - return -1; - } - // Push into Slave. - status = WebRtcNetEQ_RecInRTPStruct(inst_[1], &neteq_rtpinfo, - &incoming_payload[payload_length], - payload_length, receive_timestamp); - if (status < 0) { - LogError("RecInRTPStruct", 1); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "RecIn: NetEq, error in pushing in Slave"); - return -1; - } - } - - return 0; -} - -int32_t ACMNetEQ::RecOut(AudioFrame& audio_frame) { - enum WebRtcNetEQOutputType type; - int16_t payload_len_sample; - enum WebRtcNetEQOutputType type_master; - enum WebRtcNetEQOutputType type_slave; - - int16_t payload_len_sample_slave; - - CriticalSectionScoped lockNetEq(neteq_crit_sect_); - - if (!received_stereo_) { - if (!is_initialized_[0]) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "RecOut: NetEq is not initialized."); - return -1; - } - { - WriteLockScoped lockCodec(*decode_lock_); - if (WebRtcNetEQ_RecOut(inst_[0], &(audio_frame.data_[0]), - &payload_len_sample) != 0) { - LogError("RecOut", 0); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "RecOut: NetEq, error in pulling out for mono case"); - // Check for errors that can be recovered from: - // RECOUT_ERROR_SAMPLEUNDERRUN = 2003 - int error_code = WebRtcNetEQ_GetErrorCode(inst_[0]); - if (error_code != 2003) { - // Cannot recover; return an error - return -1; - } - } - } - WebRtcNetEQ_GetSpeechOutputType(inst_[0], &type); - audio_frame.num_channels_ = 1; - } else { - if (!is_initialized_[0] || !is_initialized_[1]) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "RecOut: NetEq is not initialized."); - return -1; - } - int16_t payload_master[480]; - int16_t payload_slave[480]; - { - WriteLockScoped lockCodec(*decode_lock_); - if (WebRtcNetEQ_RecOutMasterSlave(inst_[0], payload_master, - &payload_len_sample, master_slave_info_, - 1) != 0) { - LogError("RecOutMasterSlave", 0); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "RecOut: NetEq, error in pulling out for master"); - - // Check for errors that can be recovered from: - // RECOUT_ERROR_SAMPLEUNDERRUN = 2003 - int error_code = WebRtcNetEQ_GetErrorCode(inst_[0]); - if (error_code != 2003) { - // Cannot recover; return an error - return -1; - } - } - if (WebRtcNetEQ_RecOutMasterSlave(inst_[1], payload_slave, - &payload_len_sample_slave, - master_slave_info_, 0) != 0) { - LogError("RecOutMasterSlave", 1); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "RecOut: NetEq, error in pulling out for slave"); - - // Check for errors that can be recovered from: - // RECOUT_ERROR_SAMPLEUNDERRUN = 2003 - int error_code = WebRtcNetEQ_GetErrorCode(inst_[1]); - if (error_code != 2003) { - // Cannot recover; return an error - return -1; - } - } - } - - if (payload_len_sample != payload_len_sample_slave) { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, - "RecOut: mismatch between the lenght of the decoded audio " - "by Master (%d samples) and Slave (%d samples).", - payload_len_sample, payload_len_sample_slave); - if (payload_len_sample > payload_len_sample_slave) { - memset(&payload_slave[payload_len_sample_slave], 0, - (payload_len_sample - payload_len_sample_slave) * - sizeof(int16_t)); - } - } - - for (int16_t n = 0; n < payload_len_sample; n++) { - audio_frame.data_[n << 1] = payload_master[n]; - audio_frame.data_[(n << 1) + 1] = payload_slave[n]; - } - audio_frame.num_channels_ = 2; - - WebRtcNetEQ_GetSpeechOutputType(inst_[0], &type_master); - WebRtcNetEQ_GetSpeechOutputType(inst_[1], &type_slave); - if ((type_master == kOutputNormal) || (type_slave == kOutputNormal)) { - type = kOutputNormal; - } else { - type = type_master; - } - } - - audio_frame.samples_per_channel_ = - static_cast(payload_len_sample); - // NetEq always returns 10 ms of audio. - current_samp_freq_khz_ = - static_cast(audio_frame.samples_per_channel_) / 10.0f; - audio_frame.sample_rate_hz_ = audio_frame.samples_per_channel_ * 100; - if (vad_status_) { - if (type == kOutputVADPassive) { - audio_frame.vad_activity_ = AudioFrame::kVadPassive; - audio_frame.speech_type_ = AudioFrame::kNormalSpeech; - } else if (type == kOutputNormal) { - audio_frame.vad_activity_ = AudioFrame::kVadActive; - audio_frame.speech_type_ = AudioFrame::kNormalSpeech; - } else if (type == kOutputPLC) { - audio_frame.vad_activity_ = previous_audio_activity_; - audio_frame.speech_type_ = AudioFrame::kPLC; - } else if (type == kOutputCNG) { - audio_frame.vad_activity_ = AudioFrame::kVadPassive; - audio_frame.speech_type_ = AudioFrame::kCNG; - } else { - audio_frame.vad_activity_ = AudioFrame::kVadPassive; - audio_frame.speech_type_ = AudioFrame::kPLCCNG; - } - } else { - // Always return kVadUnknown when receive VAD is inactive - audio_frame.vad_activity_ = AudioFrame::kVadUnknown; - if (type == kOutputNormal) { - audio_frame.speech_type_ = AudioFrame::kNormalSpeech; - } else if (type == kOutputPLC) { - audio_frame.speech_type_ = AudioFrame::kPLC; - } else if (type == kOutputPLCtoCNG) { - audio_frame.speech_type_ = AudioFrame::kPLCCNG; - } else if (type == kOutputCNG) { - audio_frame.speech_type_ = AudioFrame::kCNG; - } else { - // type is kOutputVADPassive which - // we don't expect to get if vad_status_ is false - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, - "RecOut: NetEq returned kVadPassive while vad_status_ is " - "false."); - audio_frame.vad_activity_ = AudioFrame::kVadUnknown; - audio_frame.speech_type_ = AudioFrame::kNormalSpeech; - } - } - previous_audio_activity_ = audio_frame.vad_activity_; - - WebRtcNetEQ_ProcessingActivity processing_stats; - WebRtcNetEQ_GetProcessingActivity(inst_[0], &processing_stats); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, id_, - "ACM::RecOut accelerate_bgn=%d accelerate_normal=%d" - " expand_bgn=%d expand_normal=%d" - " preemptive_bgn=%d preemptive_normal=%d" - " merge_bgn=%d merge_normal=%d", - processing_stats.accelerate_bgn_samples, - processing_stats.accelerate_normal_samples, - processing_stats.expand_bgn_sampels, - processing_stats.expand_normal_samples, - processing_stats.preemptive_expand_bgn_samples, - processing_stats.preemptive_expand_normal_samples, - processing_stats.merge_expand_bgn_samples, - processing_stats.merge_expand_normal_samples); - return 0; -} - -// When ACMGenericCodec has set the codec specific parameters in codec_def -// it calls AddCodec() to add the new codec to the NetEQ database. -int32_t ACMNetEQ::AddCodec(WebRtcNetEQ_CodecDef* codec_def, - bool to_master) { - if (codec_def == NULL) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "ACMNetEQ::AddCodec: error, codec_def is NULL"); - return -1; - } - CriticalSectionScoped lock(neteq_crit_sect_); - - int16_t idx; - if (to_master) { - idx = 0; - } else { - idx = 1; - } - - if (!is_initialized_[idx]) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "ACMNetEQ::AddCodec: NetEq is not initialized."); - return -1; - } - if (WebRtcNetEQ_CodecDbAdd(inst_[idx], codec_def) < 0) { - LogError("CodecDB_Add", idx); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "ACMNetEQ::AddCodec: NetEq, error in adding codec"); - return -1; - } else { - return 0; - } -} - -// Creates a Word16 RTP packet out of a Word8 payload and an rtp info struct. -// Must be byte order safe. -void ACMNetEQ::RTPPack(int16_t* rtp_packet, const int8_t* payload, - const int32_t payload_length_bytes, - const WebRtcRTPHeader& rtp_info) { - int32_t idx = 0; - WEBRTC_SPL_SET_BYTE(rtp_packet, (int8_t) 0x80, idx); - idx++; - WEBRTC_SPL_SET_BYTE(rtp_packet, rtp_info.header.payloadType, idx); - idx++; - WEBRTC_SPL_SET_BYTE(rtp_packet, - WEBRTC_SPL_GET_BYTE(&(rtp_info.header.sequenceNumber), 1), - idx); - idx++; - WEBRTC_SPL_SET_BYTE(rtp_packet, - WEBRTC_SPL_GET_BYTE(&(rtp_info.header.sequenceNumber), 0), - idx); - idx++; - WEBRTC_SPL_SET_BYTE(rtp_packet, - WEBRTC_SPL_GET_BYTE(&(rtp_info.header.timestamp), 3), - idx); - idx++; - WEBRTC_SPL_SET_BYTE(rtp_packet, - WEBRTC_SPL_GET_BYTE(&(rtp_info.header.timestamp), 2), - idx); - idx++; - WEBRTC_SPL_SET_BYTE(rtp_packet, - WEBRTC_SPL_GET_BYTE(&(rtp_info.header.timestamp), 1), - idx); - idx++; - WEBRTC_SPL_SET_BYTE(rtp_packet, - WEBRTC_SPL_GET_BYTE(&(rtp_info.header.timestamp), 0), - idx); - idx++; - WEBRTC_SPL_SET_BYTE(rtp_packet, - WEBRTC_SPL_GET_BYTE(&(rtp_info.header.ssrc), 3), idx); - idx++; - WEBRTC_SPL_SET_BYTE(rtp_packet, WEBRTC_SPL_GET_BYTE(&(rtp_info.header.ssrc), - 2), idx); - idx++; - WEBRTC_SPL_SET_BYTE(rtp_packet, WEBRTC_SPL_GET_BYTE(&(rtp_info.header.ssrc), - 1), idx); - idx++; - WEBRTC_SPL_SET_BYTE(rtp_packet, WEBRTC_SPL_GET_BYTE(&(rtp_info.header.ssrc), - 0), idx); - idx++; - for (int16_t i = 0; i < payload_length_bytes; i++) { - WEBRTC_SPL_SET_BYTE(rtp_packet, payload[i], idx); - idx++; - } - if (payload_length_bytes & 1) { - // Our 16 bits buffer is one byte too large, set that - // last byte to zero. - WEBRTC_SPL_SET_BYTE(rtp_packet, 0x0, idx); - } -} - -int16_t ACMNetEQ::EnableVAD() { - CriticalSectionScoped lock(neteq_crit_sect_); - if (vad_status_) { - return 0; - } - for (int16_t idx = 0; idx < num_slaves_ + 1; idx++) { - if (!is_initialized_[idx]) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "SetVADStatus: NetEq is not initialized."); - return -1; - } - // VAD was off and we have to turn it on - if (EnableVADByIdxSafe(idx) < 0) { - return -1; - } - - // Set previous VAD status to PASSIVE - previous_audio_activity_ = AudioFrame::kVadPassive; - } - vad_status_ = true; - return 0; -} - -ACMVADMode ACMNetEQ::vad_mode() const { - CriticalSectionScoped lock(neteq_crit_sect_); - return vad_mode_; -} - -int16_t ACMNetEQ::SetVADMode(const ACMVADMode mode) { - CriticalSectionScoped lock(neteq_crit_sect_); - if ((mode < VADNormal) || (mode > VADVeryAggr)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "SetVADMode: NetEq error: could not set VAD mode, mode is not " - "supported"); - return -1; - } else { - for (int16_t idx = 0; idx < num_slaves_ + 1; idx++) { - if (!is_initialized_[idx]) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "SetVADMode: NetEq is not initialized."); - return -1; - } - if (WebRtcNetEQ_SetVADMode(inst_[idx], mode) < 0) { - LogError("SetVADmode", idx); - return -1; - } - } - vad_mode_ = mode; - return 0; - } -} - -int32_t ACMNetEQ::FlushBuffers() { - CriticalSectionScoped lock(neteq_crit_sect_); - for (int16_t idx = 0; idx < num_slaves_ + 1; idx++) { - if (!is_initialized_[idx]) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "FlushBuffers: NetEq is not initialized."); - return -1; - } - if (WebRtcNetEQ_FlushBuffers(inst_[idx]) < 0) { - LogError("FlushBuffers", idx); - return -1; - } - } - return 0; -} - -int16_t ACMNetEQ::RemoveCodec(WebRtcNetEQDecoder codec_idx, - bool is_stereo) { - // sanity check - if ((codec_idx <= kDecoderReservedStart) || - (codec_idx >= kDecoderReservedEnd)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "RemoveCodec: NetEq error: could not Remove Codec, codec " - "index out of range"); - return -1; - } - CriticalSectionScoped lock(neteq_crit_sect_); - if (!is_initialized_[0]) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "RemoveCodec: NetEq is not initialized."); - return -1; - } - - if (WebRtcNetEQ_CodecDbRemove(inst_[0], codec_idx) < 0) { - LogError("CodecDB_Remove", 0); - return -1; - } - - if (is_stereo) { - if (WebRtcNetEQ_CodecDbRemove(inst_[1], codec_idx) < 0) { - LogError("CodecDB_Remove", 1); - return -1; - } - } - - return 0; -} - -int16_t ACMNetEQ::SetBackgroundNoiseMode( - const ACMBackgroundNoiseMode mode) { - CriticalSectionScoped lock(neteq_crit_sect_); - for (int16_t idx = 0; idx < num_slaves_ + 1; idx++) { - if (!is_initialized_[idx]) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "SetBackgroundNoiseMode: NetEq is not initialized."); - return -1; - } - if (WebRtcNetEQ_SetBGNMode(inst_[idx], (WebRtcNetEQBGNMode) mode) < 0) { - LogError("SetBGNMode", idx); - return -1; - } - } - return 0; -} - -int16_t ACMNetEQ::BackgroundNoiseMode(ACMBackgroundNoiseMode& mode) { - WebRtcNetEQBGNMode my_mode; - CriticalSectionScoped lock(neteq_crit_sect_); - if (!is_initialized_[0]) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "BackgroundNoiseMode: NetEq is not initialized."); - return -1; - } - if (WebRtcNetEQ_GetBGNMode(inst_[0], &my_mode) < 0) { - LogError("WebRtcNetEQ_GetBGNMode", 0); - return -1; - } else { - mode = (ACMBackgroundNoiseMode) my_mode; - } - return 0; -} - -void ACMNetEQ::set_id(int32_t id) { - CriticalSectionScoped lock(neteq_crit_sect_); - id_ = id; -} - -void ACMNetEQ::LogError(const char* neteq_func_name, - const int16_t idx) const { - char error_name[NETEQ_ERR_MSG_LEN_BYTE]; - char my_func_name[50]; - int neteq_error_code = WebRtcNetEQ_GetErrorCode(inst_[idx]); - WebRtcNetEQ_GetErrorName(neteq_error_code, error_name, - NETEQ_ERR_MSG_LEN_BYTE - 1); - strncpy(my_func_name, neteq_func_name, 49); - error_name[NETEQ_ERR_MSG_LEN_BYTE - 1] = '\0'; - my_func_name[49] = '\0'; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "NetEq-%d Error in function %s, error-code: %d, error-string: " - " %s", idx, my_func_name, neteq_error_code, error_name); -} - -int32_t ACMNetEQ::PlayoutTimestamp(uint32_t& timestamp) { - CriticalSectionScoped lock(neteq_crit_sect_); - if (WebRtcNetEQ_GetSpeechTimeStamp(inst_[0], ×tamp) < 0) { - LogError("GetSpeechTimeStamp", 0); - return -1; - } else { - return 0; - } -} - -void ACMNetEQ::RemoveSlaves() { - CriticalSectionScoped lock(neteq_crit_sect_); - RemoveSlavesSafe(); -} - -void ACMNetEQ::RemoveSlavesSafe() { - for (int i = 1; i < num_slaves_ + 1; i++) { - RemoveNetEQSafe(i); - } - - if (master_slave_info_ != NULL) { - free(master_slave_info_); - master_slave_info_ = NULL; - } - num_slaves_ = 0; -} - -void ACMNetEQ::RemoveNetEQSafe(int index) { - if (inst_mem_[index] != NULL) { - free(inst_mem_[index]); - inst_mem_[index] = NULL; - inst_[index] = NULL; - } - if (neteq_packet_buffer_[index] != NULL) { - free(neteq_packet_buffer_[index]); - neteq_packet_buffer_[index] = NULL; - } - if (ptr_vadinst_[index] != NULL) { - WebRtcVad_Free(ptr_vadinst_[index]); - ptr_vadinst_[index] = NULL; - } -} - -int16_t ACMNetEQ::AddSlave(const WebRtcNetEQDecoder* used_codecs, - int16_t num_codecs) { - CriticalSectionScoped lock(neteq_crit_sect_); - const int16_t slave_idx = 1; - if (num_slaves_ < 1) { - // initialize the receiver, this also sets up VAD. - if (InitByIdxSafe(slave_idx) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "AddSlave: AddSlave Failed, Could not Initialize"); - return -1; - } - - // Allocate buffer. - if (AllocatePacketBufferByIdxSafe(used_codecs, num_codecs, - slave_idx) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "AddSlave: AddSlave Failed, Could not Allocate Packet " - "Buffer"); - return -1; - } - - if (master_slave_info_ != NULL) { - free(master_slave_info_); - master_slave_info_ = NULL; - } - int ms_info_size = WebRtcNetEQ_GetMasterSlaveInfoSize(); - master_slave_info_ = malloc(ms_info_size); - - if (master_slave_info_ == NULL) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "AddSlave: AddSlave Failed, Could not Allocate memory for " - "Master-Slave Info"); - return -1; - } - - // We accept this as initialized NetEQ, the rest is to synchronize - // Slave with Master. - num_slaves_ = 1; - is_initialized_[slave_idx] = true; - - // Set AVT - if (WebRtcNetEQ_SetAVTPlayout(inst_[slave_idx], - (avt_playout_) ? 1 : 0) < 0) { - LogError("SetAVTPlayout", slave_idx); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "AddSlave: AddSlave Failed, Could not set AVT playout."); - return -1; - } - - // Set Background Noise - WebRtcNetEQBGNMode current_mode; - if (WebRtcNetEQ_GetBGNMode(inst_[0], ¤t_mode) < 0) { - LogError("GetBGNMode", 0); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "AAddSlave: AddSlave Failed, Could not Get BGN form " - "Master."); - return -1; - } - - if (WebRtcNetEQ_SetBGNMode(inst_[slave_idx], - (WebRtcNetEQBGNMode) current_mode) < 0) { - LogError("SetBGNMode", slave_idx); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "AddSlave: AddSlave Failed, Could not set BGN mode."); - return -1; - } - - enum WebRtcNetEQPlayoutMode playout_mode = kPlayoutOff; - switch (playout_mode_) { - case voice: - playout_mode = kPlayoutOn; - break; - case fax: - playout_mode = kPlayoutFax; - break; - case streaming: - playout_mode = kPlayoutStreaming; - break; - case off: - playout_mode = kPlayoutOff; - break; - } - if (WebRtcNetEQ_SetPlayoutMode(inst_[slave_idx], playout_mode) < 0) { - LogError("SetPlayoutMode", 1); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "AddSlave: AddSlave Failed, Could not Set Playout Mode."); - return -1; - } - - // Set AV-sync for the slave. - WebRtcNetEQ_EnableAVSync(inst_[slave_idx], av_sync_ ? 1 : 0); - - // Set minimum delay. - if (minimum_delay_ms_ > 0) - WebRtcNetEQ_SetMinimumDelay(inst_[slave_idx], minimum_delay_ms_); - - // Set maximum delay. - if (maximum_delay_ms_ > 0) - WebRtcNetEQ_SetMaximumDelay(inst_[slave_idx], maximum_delay_ms_); - } - - return 0; -} - -void ACMNetEQ::set_received_stereo(bool received_stereo) { - CriticalSectionScoped lock(neteq_crit_sect_); - received_stereo_ = received_stereo; -} - -uint8_t ACMNetEQ::num_slaves() { - CriticalSectionScoped lock(neteq_crit_sect_); - return num_slaves_; -} - -void ACMNetEQ::EnableAVSync(bool enable) { - CriticalSectionScoped lock(neteq_crit_sect_); - av_sync_ = enable; - for (int i = 0; i < num_slaves_ + 1; ++i) { - assert(is_initialized_[i]); - WebRtcNetEQ_EnableAVSync(inst_[i], enable ? 1 : 0); - } -} - -int ACMNetEQ::SetMinimumDelay(int minimum_delay_ms) { - CriticalSectionScoped lock(neteq_crit_sect_); - for (int i = 0; i < num_slaves_ + 1; ++i) { - assert(is_initialized_[i]); - if (WebRtcNetEQ_SetMinimumDelay(inst_[i], minimum_delay_ms) < 0) - return -1; - } - minimum_delay_ms_ = minimum_delay_ms; - return 0; -} - -int ACMNetEQ::SetMaximumDelay(int maximum_delay_ms) { - CriticalSectionScoped lock(neteq_crit_sect_); - for (int i = 0; i < num_slaves_ + 1; ++i) { - assert(is_initialized_[i]); - if (WebRtcNetEQ_SetMaximumDelay(inst_[i], maximum_delay_ms) < 0) - return -1; - } - maximum_delay_ms_ = maximum_delay_ms; - return 0; -} - -int ACMNetEQ::LeastRequiredDelayMs() const { - CriticalSectionScoped lock(neteq_crit_sect_); - assert(is_initialized_[0]); - - // Sufficient to query the master. - return WebRtcNetEQ_GetRequiredDelayMs(inst_[0]); -} - -bool ACMNetEQ::DecodedRtpInfo(int* sequence_number, uint32_t* timestamp) const { - CriticalSectionScoped lock(neteq_crit_sect_); - if (WebRtcNetEQ_DecodedRtpInfo(inst_[0], sequence_number, timestamp) < 0) - return false; - return true; -} - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_neteq.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_neteq.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_neteq.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_neteq.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,399 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_NETEQ_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_NETEQ_H_ - -#include "webrtc/common_audio/vad/include/webrtc_vad.h" -#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -class CriticalSectionWrapper; -class RWLockWrapper; -struct CodecInst; - -namespace acm1 { - -#define MAX_NUM_SLAVE_NETEQ 1 - -class ACMNetEQ { - public: - enum JitterBuffer { - kMasterJb = 0, - kSlaveJb = 1 - }; - - // Constructor of the class - ACMNetEQ(); - - // Destructor of the class. - ~ACMNetEQ(); - - // - // Init() - // Allocates memory for NetEQ and VAD and initializes them. - // - // Return value : 0 if ok. - // -1 if NetEQ or VAD returned an error or - // if out of memory. - // - int32_t Init(); - - // - // RecIn() - // Gives the payload to NetEQ. - // - // Input: - // - incoming_payload : Incoming audio payload. - // - length_payload : Length of incoming audio payload. - // - rtp_info : RTP header for the incoming payload containing - // information about payload type, sequence number, - // timestamp, SSRC and marker bit. - // - receive_timestamp : received timestamp. - // - // Return value : 0 if ok. - // <0 if NetEQ returned an error. - // - int32_t RecIn(const uint8_t* incoming_payload, - const int32_t length_payload, - const WebRtcRTPHeader& rtp_info, - uint32_t receive_timestamp); - - // - // RecIn() - // Insert a sync payload to NetEq. Should only be called if |av_sync_| is - // enabled; - // - // Input: - // - rtp_info : RTP header for the incoming payload containing - // information about payload type, sequence number, - // timestamp, SSRC and marker bit. - // - receive_timestamp : received timestamp. - // - // Return value : 0 if ok. - // <0 if NetEQ returned an error. - // - int RecIn(const WebRtcRTPHeader& rtp_info, uint32_t receive_timestamp); - - // - // RecOut() - // Asks NetEQ for 10 ms of decoded audio. - // - // Input: - // -audio_frame : an audio frame were output data and - // associated parameters are written to. - // - // Return value : 0 if ok. - // -1 if NetEQ returned an error. - // - int32_t RecOut(AudioFrame& audio_frame); - - // - // AddCodec() - // Adds a new codec to the NetEQ codec database. - // - // Input: - // - codec_def : The codec to be added. - // - to_master : true if the codec has to be added to Master - // NetEq, otherwise will be added to the Slave - // NetEQ. - // - // Return value : 0 if ok. - // <0 if NetEQ returned an error. - // - int32_t AddCodec(WebRtcNetEQ_CodecDef *codec_def, - bool to_master = true); - - // - // AllocatePacketBuffer() - // Allocates the NetEQ packet buffer. - // - // Input: - // - used_codecs : An array of the codecs to be used by NetEQ. - // - num_codecs : Number of codecs in used_codecs. - // - // Return value : 0 if ok. - // <0 if NetEQ returned an error. - // - int32_t AllocatePacketBuffer(const WebRtcNetEQDecoder* used_codecs, - int16_t num_codecs); - - // - // SetAVTPlayout() - // Enable/disable playout of AVT payloads. - // - // Input: - // - enable : Enable if true, disable if false. - // - // Return value : 0 if ok. - // <0 if NetEQ returned an error. - // - int32_t SetAVTPlayout(const bool enable); - - // - // AVTPlayout() - // Get the current AVT playout state. - // - // Return value : True if AVT playout is enabled. - // False if AVT playout is disabled. - // - bool avt_playout() const; - - // - // CurrentSampFreqHz() - // Get the current sampling frequency in Hz. - // - // Return value : Sampling frequency in Hz. - // - int32_t CurrentSampFreqHz() const; - - // - // SetPlayoutMode() - // Sets the playout mode to voice or fax. - // - // Input: - // - mode : The playout mode to be used, voice, - // fax, or streaming. - // - // Return value : 0 if ok. - // <0 if NetEQ returned an error. - // - int32_t SetPlayoutMode(const AudioPlayoutMode mode); - - // - // PlayoutMode() - // Get the current playout mode. - // - // Return value : The current playout mode. - // - AudioPlayoutMode playout_mode() const; - - // - // NetworkStatistics() - // Get the current network statistics from NetEQ. - // - // Output: - // - statistics : The current network statistics. - // - // Return value : 0 if ok. - // <0 if NetEQ returned an error. - // - int32_t NetworkStatistics(ACMNetworkStatistics* statistics) const; - - // - // VADMode() - // Get the current VAD Mode. - // - // Return value : The current VAD mode. - // - ACMVADMode vad_mode() const; - - // - // SetVADMode() - // Set the VAD mode. - // - // Input: - // - mode : The new VAD mode. - // - // Return value : 0 if ok. - // -1 if an error occurred. - // - int16_t SetVADMode(const ACMVADMode mode); - - // - // DecodeLock() - // Get the decode lock used to protect decoder instances while decoding. - // - // Return value : Pointer to the decode lock. - // - RWLockWrapper* DecodeLock() const { - return decode_lock_; - } - - // - // FlushBuffers() - // Flushes the NetEQ packet and speech buffers. - // - // Return value : 0 if ok. - // -1 if NetEQ returned an error. - // - int32_t FlushBuffers(); - - // - // RemoveCodec() - // Removes a codec from the NetEQ codec database. - // - // Input: - // - codec_idx : Codec to be removed. - // - // Return value : 0 if ok. - // -1 if an error occurred. - // - int16_t RemoveCodec(WebRtcNetEQDecoder codec_idx, - bool is_stereo = false); - - // - // SetBackgroundNoiseMode() - // Set the mode of the background noise. - // - // Input: - // - mode : an enumerator specifying the mode of the - // background noise. - // - // Return value : 0 if succeeded, - // -1 if failed to set the mode. - // - int16_t SetBackgroundNoiseMode(const ACMBackgroundNoiseMode mode); - - // - // BackgroundNoiseMode() - // return the mode of the background noise. - // - // Return value : The mode of background noise. - // - int16_t BackgroundNoiseMode(ACMBackgroundNoiseMode& mode); - - void set_id(int32_t id); - - int32_t PlayoutTimestamp(uint32_t& timestamp); - - void set_received_stereo(bool received_stereo); - - uint8_t num_slaves(); - - // Delete all slaves. - void RemoveSlaves(); - - int16_t AddSlave(const WebRtcNetEQDecoder* used_codecs, - int16_t num_codecs); - - void BufferSpec(int& num_packets, int& size_bytes, int& overhead_bytes) { - num_packets = min_of_max_num_packets_; - size_bytes = min_of_buffer_size_bytes_; - overhead_bytes = per_packet_overhead_bytes_; - } - - // - // Set AV-sync mode. - // - void EnableAVSync(bool enable); - - // - // Get sequence number and timestamp of the last decoded RTP. - // - bool DecodedRtpInfo(int* sequence_number, uint32_t* timestamp) const; - - // - // Set a minimum delay in NetEq. Unless channel condition dictates a longer - // delay, the given delay is maintained by NetEq. - // - int SetMinimumDelay(int minimum_delay_ms); - - // - // Set a maximum delay in NetEq. - // - int SetMaximumDelay(int maximum_delay_ms); - - // - // The shortest latency, in milliseconds, required by jitter buffer. This - // is computed based on inter-arrival times and playout mode of NetEq. The - // actual delay is the maximum of least-required-delay and the minimum-delay - // specified by SetMinumumPlayoutDelay() API. - // - int LeastRequiredDelayMs() const ; - - private: - // - // RTPPack() - // Creates a Word16 RTP packet out of the payload data in Word16 and - // a WebRtcRTPHeader. - // - // Input: - // - payload : Payload to be packetized. - // - payload_length_bytes : Length of the payload in bytes. - // - rtp_info : RTP header structure. - // - // Output: - // - rtp_packet : The RTP packet. - // - static void RTPPack(int16_t* rtp_packet, const int8_t* payload, - const int32_t payload_length_bytes, - const WebRtcRTPHeader& rtp_info); - - void LogError(const char* neteq_func_name, const int16_t idx) const; - - int16_t InitByIdxSafe(const int16_t idx); - - // - // EnableVAD() - // Enable VAD. - // - // Return value : 0 if ok. - // -1 if an error occurred. - // - int16_t EnableVAD(); - - int16_t EnableVADByIdxSafe(const int16_t idx); - - int16_t AllocatePacketBufferByIdxSafe( - const WebRtcNetEQDecoder* used_codecs, - int16_t num_codecs, - const int16_t idx); - - // Delete the NetEQ corresponding to |index|. - void RemoveNetEQSafe(int index); - - void RemoveSlavesSafe(); - - void* inst_[MAX_NUM_SLAVE_NETEQ + 1]; - void* inst_mem_[MAX_NUM_SLAVE_NETEQ + 1]; - - int16_t* neteq_packet_buffer_[MAX_NUM_SLAVE_NETEQ + 1]; - - int32_t id_; - float current_samp_freq_khz_; - bool avt_playout_; - AudioPlayoutMode playout_mode_; - CriticalSectionWrapper* neteq_crit_sect_; - - WebRtcVadInst* ptr_vadinst_[MAX_NUM_SLAVE_NETEQ + 1]; - - bool vad_status_; - ACMVADMode vad_mode_; - RWLockWrapper* decode_lock_; - bool is_initialized_[MAX_NUM_SLAVE_NETEQ + 1]; - uint8_t num_slaves_; - bool received_stereo_; - void* master_slave_info_; - AudioFrame::VADActivity previous_audio_activity_; - - CriticalSectionWrapper* callback_crit_sect_; - // Minimum of "max number of packets," among all NetEq instances. - int min_of_max_num_packets_; - // Minimum of buffer-size among all NetEq instances. - int min_of_buffer_size_bytes_; - int per_packet_overhead_bytes_; - - // Keep track of AV-sync. Just used to set the slave when a slave is added. - bool av_sync_; - - int minimum_delay_ms_; - int maximum_delay_ms_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_NETEQ_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_neteq_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_neteq_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_neteq_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_neteq_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// This file contains unit tests for ACM's NetEQ wrapper (class ACMNetEQ). - -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" - -#include - -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" -#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" -#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -namespace acm1 { - -class AcmNetEqTest : public ::testing::Test { - protected: - static const size_t kMaxPayloadLen = 5760; // 60 ms, 48 kHz, 16 bit samples. - static const int kPcm16WbPayloadType = 94; - AcmNetEqTest() {} - virtual void SetUp(); - virtual void TearDown() {} - - void InsertZeroPacket(uint16_t sequence_number, - uint32_t timestamp, - uint8_t payload_type, - uint32_t ssrc, - bool marker_bit, - size_t len_payload_bytes); - void PullData(int expected_num_samples); - - ACMNetEQ neteq_; -}; - -void AcmNetEqTest::SetUp() { - ASSERT_EQ(0, neteq_.Init()); - ASSERT_EQ(0, neteq_.AllocatePacketBuffer(ACMCodecDB::NetEQDecoders(), - ACMCodecDB::kNumCodecs)); - WebRtcNetEQ_CodecDef codec_def; - SET_CODEC_PAR(codec_def, kDecoderPCM16Bwb, kPcm16WbPayloadType, NULL, 16000); - SET_PCM16B_WB_FUNCTIONS(codec_def); - ASSERT_EQ(0, neteq_.AddCodec(&codec_def, true)); -} - -void AcmNetEqTest::InsertZeroPacket(uint16_t sequence_number, - uint32_t timestamp, - uint8_t payload_type, - uint32_t ssrc, - bool marker_bit, - size_t len_payload_bytes) { - ASSERT_TRUE(len_payload_bytes <= kMaxPayloadLen); - uint16_t payload[kMaxPayloadLen] = {0}; - WebRtcRTPHeader rtp_header; - rtp_header.header.sequenceNumber = sequence_number; - rtp_header.header.timestamp = timestamp; - rtp_header.header.ssrc = ssrc; - rtp_header.header.payloadType = payload_type; - rtp_header.header.markerBit = marker_bit; - rtp_header.type.Audio.channel = 1; - // Receive timestamp can be set to send timestamp in this test. - ASSERT_EQ(0, neteq_.RecIn(reinterpret_cast(payload), - len_payload_bytes, rtp_header, timestamp)); -} - -void AcmNetEqTest::PullData(int expected_num_samples) { - AudioFrame out_frame; - ASSERT_EQ(0, neteq_.RecOut(out_frame)); - ASSERT_EQ(expected_num_samples, out_frame.samples_per_channel_); -} - -TEST_F(AcmNetEqTest, NetworkStatistics) { - // Use fax mode to avoid time-scaling. This is to simplify the testing of - // packet waiting times in the packet buffer. - neteq_.SetPlayoutMode(fax); - // Insert 31 dummy packets at once. Each packet contains 10 ms 16 kHz audio. - int num_frames = 30; - const int kSamples = 10 * 16; - const int kPayloadBytes = kSamples * 2; - int i, j; - for (i = 0; i < num_frames; ++i) { - InsertZeroPacket(i, i * kSamples, kPcm16WbPayloadType, 0x1234, false, - kPayloadBytes); - } - // Pull out data once. - PullData(kSamples); - // Insert one more packet (to produce different mean and median). - i = num_frames; - InsertZeroPacket(i, i * kSamples, kPcm16WbPayloadType, 0x1234, false, - kPayloadBytes); - // Pull out all data. - for (j = 1; j < num_frames + 1; ++j) { - PullData(kSamples); - } - - ACMNetworkStatistics stats; - ASSERT_EQ(0, neteq_.NetworkStatistics(&stats)); - EXPECT_EQ(0, stats.currentBufferSize); - EXPECT_EQ(0, stats.preferredBufferSize); - EXPECT_FALSE(stats.jitterPeaksFound); - EXPECT_EQ(0, stats.currentPacketLossRate); - EXPECT_EQ(0, stats.currentDiscardRate); - EXPECT_EQ(0, stats.currentExpandRate); - EXPECT_EQ(0, stats.currentPreemptiveRate); - EXPECT_EQ(0, stats.currentAccelerateRate); - EXPECT_EQ(-916, stats.clockDriftPPM); // Initial value is slightly off. - EXPECT_EQ(300, stats.maxWaitingTimeMs); - EXPECT_EQ(10, stats.minWaitingTimeMs); - EXPECT_EQ(159, stats.meanWaitingTimeMs); - EXPECT_EQ(160, stats.medianWaitingTimeMs); -} - -TEST_F(AcmNetEqTest, TestZeroLengthWaitingTimesVector) { - // Insert one packet. - const int kSamples = 10 * 16; - const int kPayloadBytes = kSamples * 2; - int i = 0; - InsertZeroPacket(i, i * kSamples, kPcm16WbPayloadType, 0x1234, false, - kPayloadBytes); - // Do not pull out any data. - - ACMNetworkStatistics stats; - ASSERT_EQ(0, neteq_.NetworkStatistics(&stats)); - EXPECT_EQ(0, stats.currentBufferSize); - EXPECT_EQ(0, stats.preferredBufferSize); - EXPECT_FALSE(stats.jitterPeaksFound); - EXPECT_EQ(0, stats.currentPacketLossRate); - EXPECT_EQ(0, stats.currentDiscardRate); - EXPECT_EQ(0, stats.currentExpandRate); - EXPECT_EQ(0, stats.currentPreemptiveRate); - EXPECT_EQ(0, stats.currentAccelerateRate); - EXPECT_EQ(-916, stats.clockDriftPPM); // Initial value is slightly off. - EXPECT_EQ(-1, stats.minWaitingTimeMs); - EXPECT_EQ(-1, stats.maxWaitingTimeMs); - EXPECT_EQ(-1, stats.meanWaitingTimeMs); - EXPECT_EQ(-1, stats.medianWaitingTimeMs); -} - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_opus.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_opus.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_opus.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_opus.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,319 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_opus.h" - -#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/trace.h" - -#ifdef WEBRTC_CODEC_OPUS -#include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" -#endif - -namespace webrtc { - -namespace acm1 { - -#ifndef WEBRTC_CODEC_OPUS - -ACMOpus::ACMOpus(int16_t /* codec_id */) - : encoder_inst_ptr_(NULL), - decoder_inst_ptr_(NULL), - sample_freq_(0), - bitrate_(0), - channels_(1) { - return; -} - -ACMOpus::~ACMOpus() { - return; -} - -int16_t ACMOpus::InternalEncode(uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - return -1; -} - -int16_t ACMOpus::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return -1; -} - -int16_t ACMOpus::InternalInitEncoder(WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int16_t ACMOpus::InternalInitDecoder(WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int32_t ACMOpus::CodecDef(WebRtcNetEQ_CodecDef& /* codec_def */, - const CodecInst& /* codec_inst */) { - return -1; -} - -ACMGenericCodec* ACMOpus::CreateInstance(void) { - return NULL; -} - -int16_t ACMOpus::InternalCreateEncoder() { - return -1; -} - -void ACMOpus::DestructEncoderSafe() { - return; -} - -int16_t ACMOpus::InternalCreateDecoder() { - return -1; -} - -void ACMOpus::DestructDecoderSafe() { - return; -} - -void ACMOpus::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - -int16_t ACMOpus::SetBitRateSafe(const int32_t /*rate*/) { - return -1; -} - -bool ACMOpus::IsTrueStereoCodec() { - return true; -} - -void ACMOpus::SplitStereoPacket(uint8_t* /*payload*/, - int32_t* /*payload_length*/) {} - -#else //===================== Actual Implementation ======================= - -ACMOpus::ACMOpus(int16_t codec_id) - : encoder_inst_ptr_(NULL), - decoder_inst_ptr_(NULL), - sample_freq_(32000), // Default sampling frequency. - bitrate_(20000), // Default bit-rate. - channels_(1) { // Default mono - codec_id_ = codec_id; - - // Opus has internal DTX, but we don't use it for now. - has_internal_dtx_ = false; - - if (codec_id_ != ACMCodecDB::kOpus) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Wrong codec id for Opus."); - sample_freq_ = -1; - bitrate_ = -1; - } - return; -} - -ACMOpus::~ACMOpus() { - if (encoder_inst_ptr_ != NULL) { - WebRtcOpus_EncoderFree(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } - if (decoder_inst_ptr_ != NULL) { - WebRtcOpus_DecoderFree(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } - return; -} - -int16_t ACMOpus::InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) { - // Call Encoder. - *bitstream_len_byte = WebRtcOpus_Encode(encoder_inst_ptr_, - &in_audio_[in_audio_ix_read_], - frame_len_smpl_, - MAX_PAYLOAD_SIZE_BYTE, bitstream); - // Check for error reported from encoder. - if (*bitstream_len_byte < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "InternalEncode: Encode error for Opus"); - *bitstream_len_byte = 0; - return -1; - } - - // Increment the read index. This tells the caller how far - // we have gone forward in reading the audio buffer. - in_audio_ix_read_ += frame_len_smpl_ * channels_; - - return *bitstream_len_byte; -} - -int16_t ACMOpus::DecodeSafe(uint8_t* bitstream, int16_t bitstream_len_byte, - int16_t* audio, int16_t* audio_samples, - int8_t* speech_type) { - return 0; -} - -int16_t ACMOpus::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { - int16_t ret; - if (encoder_inst_ptr_ != NULL) { - WebRtcOpus_EncoderFree(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } - ret = WebRtcOpus_EncoderCreate(&encoder_inst_ptr_, - codec_params->codec_inst.channels); - // Store number of channels. - channels_ = codec_params->codec_inst.channels; - - if (ret < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Encoder creation failed for Opus"); - return ret; - } - ret = WebRtcOpus_SetBitRate(encoder_inst_ptr_, - codec_params->codec_inst.rate); - if (ret < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Setting initial bitrate failed for Opus"); - return ret; - } - - // Store bitrate. - bitrate_ = codec_params->codec_inst.rate; - - return 0; -} - -int16_t ACMOpus::InternalInitDecoder(WebRtcACMCodecParams* codec_params) { - if (decoder_inst_ptr_ == NULL) { - if (WebRtcOpus_DecoderCreate(&decoder_inst_ptr_, - codec_params->codec_inst.channels) < 0) { - return -1; - } - } - - // Number of channels in decoder should match the number in |codec_params|. - assert(codec_params->codec_inst.channels == - WebRtcOpus_DecoderChannels(decoder_inst_ptr_)); - - if (WebRtcOpus_DecoderInit(decoder_inst_ptr_) < 0) { - return -1; - } - if (WebRtcOpus_DecoderInitSlave(decoder_inst_ptr_) < 0) { - return -1; - } - return 0; -} - -int32_t ACMOpus::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - if (!decoder_initialized_) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "CodeDef: Decoder uninitialized for Opus"); - return -1; - } - - // Fill up the structure by calling "SET_CODEC_PAR" & "SET_OPUS_FUNCTION." - // Then call NetEQ to add the codec to its database. - // TODO(tlegrand): Decoder is registered in NetEQ as a 32 kHz decoder, which - // is true until we have a full 48 kHz system, and remove the downsampling - // in the Opus decoder wrapper. - SET_CODEC_PAR(codec_def, kDecoderOpus, codec_inst.pltype, - decoder_inst_ptr_, 32000); - - // If this is the master of NetEQ, regular decoder will be added, otherwise - // the slave decoder will be used. - if (is_master_) { - SET_OPUS_FUNCTIONS(codec_def); - } else { - SET_OPUSSLAVE_FUNCTIONS(codec_def); - } - - return 0; -} - -ACMGenericCodec* ACMOpus::CreateInstance(void) { - return NULL; -} - -int16_t ACMOpus::InternalCreateEncoder() { - // Real encoder will be created in InternalInitEncoder. - return 0; -} - -void ACMOpus::DestructEncoderSafe() { - if (encoder_inst_ptr_) { - WebRtcOpus_EncoderFree(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } -} - -int16_t ACMOpus::InternalCreateDecoder() { - // Real decoder will be created in InternalInitDecoder - return 0; -} - -void ACMOpus::DestructDecoderSafe() { - decoder_initialized_ = false; - if (decoder_inst_ptr_) { - WebRtcOpus_DecoderFree(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } -} - -void ACMOpus::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcOpus_EncoderFree(reinterpret_cast(ptr_inst)); - } - return; -} - -int16_t ACMOpus::SetBitRateSafe(const int32_t rate) { - if (rate < 6000 || rate > 510000) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "SetBitRateSafe: Invalid rate Opus"); - return -1; - } - - bitrate_ = rate; - - // Ask the encoder for the new rate. - if (WebRtcOpus_SetBitRate(encoder_inst_ptr_, bitrate_) >= 0) { - encoder_params_.codec_inst.rate = bitrate_; - return 0; - } - - return -1; -} - -bool ACMOpus::IsTrueStereoCodec() { - return true; -} - -// Copy the stereo packet so that NetEq will insert into both master and slave. -void ACMOpus::SplitStereoPacket(uint8_t* payload, int32_t* payload_length) { - // Check for valid inputs. - assert(payload != NULL); - assert(*payload_length > 0); - - // Duplicate the payload. - memcpy(&payload[*payload_length], &payload[0], - sizeof(uint8_t) * (*payload_length)); - // Double the size of the packet. - *payload_length *= 2; -} - -#endif // WEBRTC_CODEC_OPUS - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_opus.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_opus.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_opus.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_opus.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_OPUS_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_OPUS_H_ - -#include "webrtc/common_audio/resampler/include/resampler.h" -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -struct WebRtcOpusEncInst; -struct WebRtcOpusDecInst; - -namespace webrtc { - -namespace acm1 { - -class ACMOpus : public ACMGenericCodec { - public: - explicit ACMOpus(int16_t codec_id); - virtual ~ACMOpus(); - - virtual ACMGenericCodec* CreateInstance(void) OVERRIDE; - - virtual int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) OVERRIDE; - - virtual int16_t InternalInitEncoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - virtual int16_t InternalInitDecoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - protected: - virtual int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type) OVERRIDE; - - virtual int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) OVERRIDE; - - virtual void DestructEncoderSafe() OVERRIDE; - - virtual void DestructDecoderSafe() OVERRIDE; - - virtual int16_t InternalCreateEncoder() OVERRIDE; - - virtual int16_t InternalCreateDecoder() OVERRIDE; - - virtual void InternalDestructEncoderInst(void* ptr_inst) OVERRIDE; - - virtual int16_t SetBitRateSafe(const int32_t rate) OVERRIDE; - - virtual bool IsTrueStereoCodec() OVERRIDE; - - virtual void SplitStereoPacket(uint8_t* payload, - int32_t* payload_length) OVERRIDE; - - WebRtcOpusEncInst* encoder_inst_ptr_; - WebRtcOpusDecInst* decoder_inst_ptr_; - uint16_t sample_freq_; - uint32_t bitrate_; - int channels_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_OPUS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcm16b.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcm16b.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcm16b.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcm16b.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,251 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_pcm16b.h" - -#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/trace.h" - -#ifdef WEBRTC_CODEC_PCM16 -#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" -#endif - -namespace webrtc { - -namespace acm1 { - -#ifndef WEBRTC_CODEC_PCM16 - -ACMPCM16B::ACMPCM16B(int16_t /* codec_id */) { - return; -} - -ACMPCM16B::~ACMPCM16B() { - return; -} - -int16_t ACMPCM16B::InternalEncode( - uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - return -1; -} - -int16_t ACMPCM16B::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return -1; -} - -int16_t ACMPCM16B::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int16_t ACMPCM16B::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int32_t ACMPCM16B::CodecDef(WebRtcNetEQ_CodecDef& /* codec_def */, - const CodecInst& /* codec_inst */) { - return -1; -} - -ACMGenericCodec* ACMPCM16B::CreateInstance(void) { - return NULL; -} - -int16_t ACMPCM16B::InternalCreateEncoder() { - return -1; -} - -int16_t ACMPCM16B::InternalCreateDecoder() { - return -1; -} - -void ACMPCM16B::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - -void ACMPCM16B::DestructEncoderSafe() { - return; -} - -void ACMPCM16B::DestructDecoderSafe() { - return; -} - -void ACMPCM16B::SplitStereoPacket(uint8_t* /*payload*/, - int32_t* /*payload_length*/) { -} - -#else //===================== Actual Implementation ======================= -ACMPCM16B::ACMPCM16B(int16_t codec_id) { - codec_id_ = codec_id; - sampling_freq_hz_ = ACMCodecDB::CodecFreq(codec_id_); -} - -ACMPCM16B::~ACMPCM16B() { - return; -} - -int16_t ACMPCM16B::InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) { - *bitstream_len_byte = WebRtcPcm16b_Encode(&in_audio_[in_audio_ix_read_], - frame_len_smpl_ * num_channels_, - bitstream); - // Increment the read index to tell the caller that how far - // we have gone forward in reading the audio buffer. - in_audio_ix_read_ += frame_len_smpl_ * num_channels_; - return *bitstream_len_byte; -} - -int16_t ACMPCM16B::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return 0; -} - -int16_t ACMPCM16B::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - // This codec does not need initialization, PCM has no instance. - return 0; -} - -int16_t ACMPCM16B::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - // This codec does not need initialization, PCM has no instance. - return 0; -} - -int32_t ACMPCM16B::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - // Fill up the structure by calling "SET_CODEC_PAR" & "SET_PCMU_FUNCTION". - // Then call NetEQ to add the codec to it's database. - if (codec_inst.channels == 1) { - switch (sampling_freq_hz_) { - case 8000: { - SET_CODEC_PAR(codec_def, kDecoderPCM16B, codec_inst.pltype, NULL, 8000); - SET_PCM16B_FUNCTIONS(codec_def); - break; - } - case 16000: { - SET_CODEC_PAR(codec_def, kDecoderPCM16Bwb, codec_inst.pltype, NULL, - 16000); - SET_PCM16B_WB_FUNCTIONS(codec_def); - break; - } - case 32000: { - SET_CODEC_PAR(codec_def, kDecoderPCM16Bswb32kHz, codec_inst.pltype, - NULL, 32000); - SET_PCM16B_SWB32_FUNCTIONS(codec_def); - break; - } - default: { - return -1; - } - } - } else { - switch (sampling_freq_hz_) { - case 8000: { - SET_CODEC_PAR(codec_def, kDecoderPCM16B_2ch, codec_inst.pltype, NULL, - 8000); - SET_PCM16B_FUNCTIONS(codec_def); - break; - } - case 16000: { - SET_CODEC_PAR(codec_def, kDecoderPCM16Bwb_2ch, codec_inst.pltype, - NULL, 16000); - SET_PCM16B_WB_FUNCTIONS(codec_def); - break; - } - case 32000: { - SET_CODEC_PAR(codec_def, kDecoderPCM16Bswb32kHz_2ch, codec_inst.pltype, - NULL, 32000); - SET_PCM16B_SWB32_FUNCTIONS(codec_def); - break; - } - default: { - return -1; - } - } - } - return 0; -} - -ACMGenericCodec* ACMPCM16B::CreateInstance(void) { - return NULL; -} - -int16_t ACMPCM16B::InternalCreateEncoder() { - // PCM has no instance. - return 0; -} - -int16_t ACMPCM16B::InternalCreateDecoder() { - // PCM has no instance. - return 0; -} - -void ACMPCM16B::InternalDestructEncoderInst(void* /* ptr_inst */) { - // PCM has no instance. - return; -} - -void ACMPCM16B::DestructEncoderSafe() { - // PCM has no instance. - encoder_exist_ = false; - encoder_initialized_ = false; - return; -} - -void ACMPCM16B::DestructDecoderSafe() { - // PCM has no instance. - decoder_exist_ = false; - decoder_initialized_ = false; - return; -} - -// Split the stereo packet and place left and right channel after each other -// in the payload vector. -void ACMPCM16B::SplitStereoPacket(uint8_t* payload, int32_t* payload_length) { - uint8_t right_byte_msb; - uint8_t right_byte_lsb; - - // Check for valid inputs. - assert(payload != NULL); - assert(*payload_length > 0); - - // Move two bytes representing right channel each loop, and place it at the - // end of the bytestream vector. After looping the data is reordered to: - // l1 l2 l3 l4 ... l(N-1) lN r1 r2 r3 r4 ... r(N-1) r(N), - // where N is the total number of samples. - - for (int i = 0; i < *payload_length / 2; i += 2) { - right_byte_msb = payload[i + 2]; - right_byte_lsb = payload[i + 3]; - memmove(&payload[i + 2], &payload[i + 4], *payload_length - i - 4); - payload[*payload_length - 2] = right_byte_msb; - payload[*payload_length - 1] = right_byte_lsb; - } -} -#endif - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcm16b.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcm16b.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcm16b.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcm16b.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCM16B_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCM16B_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -namespace webrtc { - -namespace acm1 { - -class ACMPCM16B : public ACMGenericCodec { - public: - explicit ACMPCM16B(int16_t codec_id); - virtual ~ACMPCM16B(); - - // for FEC - virtual ACMGenericCodec* CreateInstance(void) OVERRIDE; - - virtual int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) OVERRIDE; - - virtual int16_t InternalInitEncoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - virtual int16_t InternalInitDecoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - protected: - virtual int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type) OVERRIDE; - - virtual int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) OVERRIDE; - - virtual void DestructEncoderSafe() OVERRIDE; - - virtual void DestructDecoderSafe() OVERRIDE; - - virtual int16_t InternalCreateEncoder() OVERRIDE; - - virtual int16_t InternalCreateDecoder() OVERRIDE; - - virtual void InternalDestructEncoderInst(void* ptr_inst) OVERRIDE; - - virtual void SplitStereoPacket(uint8_t* payload, - int32_t* payload_length) OVERRIDE; - - int32_t sampling_freq_hz_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCM16B_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcma.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcma.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcma.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcma.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_pcma.h" - -#include "webrtc/modules/audio_coding/codecs/g711/include/g711_interface.h" -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/trace.h" - -// Codec interface - -namespace webrtc { - -namespace acm1 { - -ACMPCMA::ACMPCMA(int16_t codec_id) { - codec_id_ = codec_id; -} - -ACMPCMA::~ACMPCMA() { - return; -} - -int16_t ACMPCMA::InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) { - *bitstream_len_byte = WebRtcG711_EncodeA(NULL, &in_audio_[in_audio_ix_read_], - frame_len_smpl_ * num_channels_, - (int16_t*) bitstream); - // Increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer. - in_audio_ix_read_ += frame_len_smpl_ * num_channels_; - return *bitstream_len_byte; -} - -int16_t ACMPCMA::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return 0; -} - -int16_t ACMPCMA::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - // This codec does not need initialization, PCM has no instance. - return 0; -} - -int16_t ACMPCMA::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - // This codec does not need initialization, PCM has no instance. - return 0; -} - -int32_t ACMPCMA::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_PCMA_FUNCTION." - // Then call NetEQ to add the codec to it's database. - if (codec_inst.channels == 1) { - // Mono mode. - SET_CODEC_PAR(codec_def, kDecoderPCMa, codec_inst.pltype, NULL, 8000); - } else { - // Stereo mode. - SET_CODEC_PAR(codec_def, kDecoderPCMa_2ch, codec_inst.pltype, NULL, 8000); - } - SET_PCMA_FUNCTIONS(codec_def); - return 0; -} - -ACMGenericCodec* ACMPCMA::CreateInstance(void) { - return NULL; -} - -int16_t ACMPCMA::InternalCreateEncoder() { - // PCM has no instance. - return 0; -} - -int16_t ACMPCMA::InternalCreateDecoder() { - // PCM has no instance. - return 0; -} - -void ACMPCMA::InternalDestructEncoderInst(void* /* ptr_inst */) { - // PCM has no instance. - return; -} - -void ACMPCMA::DestructEncoderSafe() { - // PCM has no instance. - return; -} - -void ACMPCMA::DestructDecoderSafe() { - // PCM has no instance. - decoder_initialized_ = false; - decoder_exist_ = false; - return; -} - -// Split the stereo packet and place left and right channel after each other -// in the payload vector. -void ACMPCMA::SplitStereoPacket(uint8_t* payload, int32_t* payload_length) { - uint8_t right_byte; - - // Check for valid inputs. - assert(payload != NULL); - assert(*payload_length > 0); - - // Move one bytes representing right channel each loop, and place it at the - // end of the bytestream vector. After looping the data is reordered to: - // l1 l2 l3 l4 ... l(N-1) lN r1 r2 r3 r4 ... r(N-1) r(N), - // where N is the total number of samples. - for (int i = 0; i < *payload_length / 2; i++) { - right_byte = payload[i + 1]; - memmove(&payload[i + 1], &payload[i + 2], *payload_length - i - 2); - payload[*payload_length - 1] = right_byte; - } -} - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcma.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcma.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcma.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcma.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCMA_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCMA_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -namespace webrtc { - -namespace acm1 { - -class ACMPCMA : public ACMGenericCodec { - public: - explicit ACMPCMA(int16_t codec_id); - virtual ~ACMPCMA(); - - // for FEC - virtual ACMGenericCodec* CreateInstance(void) OVERRIDE; - - virtual int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) OVERRIDE; - - virtual int16_t InternalInitEncoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - virtual int16_t InternalInitDecoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - protected: - virtual int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type) OVERRIDE; - - virtual int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) OVERRIDE; - - virtual void DestructEncoderSafe() OVERRIDE; - - virtual void DestructDecoderSafe() OVERRIDE; - - virtual int16_t InternalCreateEncoder() OVERRIDE; - - virtual int16_t InternalCreateDecoder() OVERRIDE; - - virtual void InternalDestructEncoderInst(void* ptr_inst) OVERRIDE; - - virtual void SplitStereoPacket(uint8_t* payload, - int32_t* payload_length) OVERRIDE; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCMA_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcmu.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcmu.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcmu.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcmu.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_pcmu.h" - -#include "webrtc/modules/audio_coding/codecs/g711/include/g711_interface.h" -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/trace.h" - -// Codec interface - -namespace webrtc { - -namespace acm1 { - -ACMPCMU::ACMPCMU(int16_t codec_id) { - codec_id_ = codec_id; -} - -ACMPCMU::~ACMPCMU() { - return; -} - -int16_t ACMPCMU::InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) { - *bitstream_len_byte = WebRtcG711_EncodeU(NULL, &in_audio_[in_audio_ix_read_], - frame_len_smpl_ * num_channels_, - (int16_t*)bitstream); - // Increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer. - in_audio_ix_read_ += frame_len_smpl_ * num_channels_; - return *bitstream_len_byte; -} - -int16_t ACMPCMU::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return 0; -} - -int16_t ACMPCMU::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - // This codec does not need initialization, PCM has no instance. - return 0; -} - -int16_t ACMPCMU::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - // This codec does not need initialization, PCM has no instance. - return 0; -} - -int32_t ACMPCMU::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_PCMU_FUNCTION." - // Then call NetEQ to add the codec to it's database. - if (codec_inst.channels == 1) { - // Mono mode. - SET_CODEC_PAR(codec_def, kDecoderPCMu, codec_inst.pltype, NULL, 8000); - } else { - // Stereo mode. - SET_CODEC_PAR(codec_def, kDecoderPCMu_2ch, codec_inst.pltype, NULL, 8000); - } - SET_PCMU_FUNCTIONS(codec_def); - return 0; -} - -ACMGenericCodec* ACMPCMU::CreateInstance(void) { - return NULL; -} - -int16_t ACMPCMU::InternalCreateEncoder() { - // PCM has no instance. - return 0; -} - -int16_t ACMPCMU::InternalCreateDecoder() { - // PCM has no instance. - return 0; -} - -void ACMPCMU::InternalDestructEncoderInst(void* /* ptr_inst */) { - // PCM has no instance. - return; -} - -void ACMPCMU::DestructEncoderSafe() { - // PCM has no instance. - encoder_exist_ = false; - encoder_initialized_ = false; - return; -} - -void ACMPCMU::DestructDecoderSafe() { - // PCM has no instance. - decoder_initialized_ = false; - decoder_exist_ = false; - return; -} - -// Split the stereo packet and place left and right channel after each other -// in the payload vector. -void ACMPCMU::SplitStereoPacket(uint8_t* payload, int32_t* payload_length) { - uint8_t right_byte; - - // Check for valid inputs. - assert(payload != NULL); - assert(*payload_length > 0); - - // Move one bytes representing right channel each loop, and place it at the - // end of the bytestream vector. After looping the data is reordered to: - // l1 l2 l3 l4 ... l(N-1) lN r1 r2 r3 r4 ... r(N-1) r(N), - // where N is the total number of samples. - for (int i = 0; i < *payload_length / 2; i++) { - right_byte = payload[i + 1]; - memmove(&payload[i + 1], &payload[i + 2], *payload_length - i - 2); - payload[*payload_length - 1] = right_byte; - } -} - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcmu.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcmu.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcmu.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_pcmu.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCMU_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCMU_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -namespace webrtc { - -namespace acm1 { - -class ACMPCMU : public ACMGenericCodec { - public: - explicit ACMPCMU(int16_t codec_id); - virtual ~ACMPCMU(); - - // for FEC - virtual ACMGenericCodec* CreateInstance(void) OVERRIDE; - - virtual int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) OVERRIDE; - - virtual int16_t InternalInitEncoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - virtual int16_t InternalInitDecoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - protected: - virtual int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type) OVERRIDE; - - virtual int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) OVERRIDE; - - virtual void DestructEncoderSafe() OVERRIDE; - - virtual void DestructDecoderSafe() OVERRIDE; - - virtual int16_t InternalCreateEncoder() OVERRIDE; - - virtual int16_t InternalCreateDecoder() OVERRIDE; - - virtual void InternalDestructEncoderInst(void* ptr_inst) OVERRIDE; - - virtual void SplitStereoPacket(uint8_t* payload, - int32_t* payload_length) OVERRIDE; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_PCMU_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_red.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_red.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_red.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_red.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_red.h" - -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/trace.h" - -namespace webrtc { - -namespace acm1 { - -ACMRED::ACMRED(int16_t codec_id) { - codec_id_ = codec_id; -} - -ACMRED::~ACMRED() { - return; -} - -int16_t ACMRED::InternalEncode(uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - // RED is never used as an encoder - // RED has no instance - return 0; -} - -int16_t ACMRED::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return 0; -} - -int16_t ACMRED::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - // This codec does not need initialization, - // RED has no instance - return 0; -} - -int16_t ACMRED::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - // This codec does not need initialization, - // RED has no instance - return 0; -} - -int32_t ACMRED::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - if (!decoder_initialized_) { - // Todo: - // log error - return -1; - } - - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_PCMU_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codec_def), kDecoderRED, codec_inst.pltype, NULL, 8000); - SET_RED_FUNCTIONS((codec_def)); - return 0; -} - -ACMGenericCodec* ACMRED::CreateInstance(void) { - return NULL; -} - -int16_t ACMRED::InternalCreateEncoder() { - // RED has no instance - return 0; -} - -int16_t ACMRED::InternalCreateDecoder() { - // RED has no instance - return 0; -} - -void ACMRED::InternalDestructEncoderInst(void* /* ptr_inst */) { - // RED has no instance - return; -} - -void ACMRED::DestructEncoderSafe() { - // RED has no instance - return; -} - -void ACMRED::DestructDecoderSafe() { - // RED has no instance - return; -} - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_red.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_red.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_red.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_red.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_RED_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_RED_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -namespace webrtc { - -namespace acm1 { - -class ACMRED : public ACMGenericCodec { - public: - explicit ACMRED(int16_t codec_id); - virtual ~ACMRED(); - - // for FEC - virtual ACMGenericCodec* CreateInstance(void) OVERRIDE; - - virtual int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) OVERRIDE; - - virtual int16_t InternalInitEncoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - virtual int16_t InternalInitDecoder( - WebRtcACMCodecParams* codec_params) OVERRIDE; - - protected: - virtual int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type) OVERRIDE; - - virtual int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) OVERRIDE; - - virtual void DestructEncoderSafe() OVERRIDE; - - virtual void DestructDecoderSafe() OVERRIDE; - - virtual int16_t InternalCreateEncoder() OVERRIDE; - - virtual int16_t InternalCreateDecoder() OVERRIDE; - - virtual void InternalDestructEncoderInst(void* ptr_inst) OVERRIDE; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_RED_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_resampler.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_resampler.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_resampler.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_resampler.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_resampler.h" - -#include - -#include "webrtc/common_audio/resampler/include/push_resampler.h" -#include "webrtc/system_wrappers/interface/logging.h" - -namespace webrtc { - -namespace acm1 { - -ACMResampler::ACMResampler() { -} - -ACMResampler::~ACMResampler() { -} - -int16_t ACMResampler::Resample10Msec(const int16_t* in_audio, - int32_t in_freq_hz, - int16_t* out_audio, - int32_t out_freq_hz, - uint8_t num_audio_channels) { - if (in_freq_hz == out_freq_hz) { - size_t length = static_cast(in_freq_hz * num_audio_channels / 100); - memcpy(out_audio, in_audio, length * sizeof(int16_t)); - return static_cast(in_freq_hz / 100); - } - - // |max_length| is the maximum number of samples for 10ms at 48kHz. - // TODO(turajs): is this actually the capacity of the |out_audio| buffer? - int max_length = 480 * num_audio_channels; - int in_length = in_freq_hz / 100 * num_audio_channels; - - if (resampler_.InitializeIfNeeded(in_freq_hz, out_freq_hz, - num_audio_channels) != 0) { - LOG_FERR3(LS_ERROR, InitializeIfNeeded, in_freq_hz, out_freq_hz, - num_audio_channels); - return -1; - } - - int out_length = resampler_.Resample(in_audio, in_length, out_audio, - max_length); - if (out_length == -1) { - LOG_FERR4(LS_ERROR, Resample, in_audio, in_length, out_audio, max_length); - return -1; - } - - return out_length / num_audio_channels; -} - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_resampler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_resampler.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_resampler.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_resampler.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_RESAMPLER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_RESAMPLER_H_ - -#include "webrtc/common_audio/resampler/include/push_resampler.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -namespace acm1 { - -class ACMResampler { - public: - ACMResampler(); - ~ACMResampler(); - - int16_t Resample10Msec(const int16_t* in_audio, - const int32_t in_freq_hz, - int16_t* out_audio, - const int32_t out_freq_hz, - uint8_t num_audio_channels); - - private: - PushResampler resampler_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_RESAMPLER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_speex.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_speex.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_speex.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_speex.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,471 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/acm_speex.h" - -#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/system_wrappers/interface/trace.h" - -#ifdef WEBRTC_CODEC_SPEEX -// NOTE! Speex is not included in the open-source package. Modify this file or -// your codec API to match the function calls and names of used Speex API file. -#include "speex_interface.h" -#endif - -namespace webrtc { - -namespace acm1 { - -#ifndef WEBRTC_CODEC_SPEEX -ACMSPEEX::ACMSPEEX(int16_t /* codec_id */) - : encoder_inst_ptr_(NULL), - decoder_inst_ptr_(NULL), - compl_mode_(0), - vbr_enabled_(false), - encoding_rate_(-1), - sampling_frequency_(-1), - samples_in_20ms_audio_(-1) { - return; -} - -ACMSPEEX::~ACMSPEEX() { - return; -} - -int16_t ACMSPEEX::InternalEncode( - uint8_t* /* bitstream */, - int16_t* /* bitstream_len_byte */) { - return -1; -} - -int16_t ACMSPEEX::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return -1; -} - -int16_t ACMSPEEX::EnableDTX() { - return -1; -} - -int16_t ACMSPEEX::DisableDTX() { - return -1; -} - -int16_t ACMSPEEX::InternalInitEncoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int16_t ACMSPEEX::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - return -1; -} - -int32_t ACMSPEEX::CodecDef(WebRtcNetEQ_CodecDef& /* codec_def */, - const CodecInst& /* codec_inst */) { - return -1; -} - -ACMGenericCodec* ACMSPEEX::CreateInstance(void) { - return NULL; -} - -int16_t ACMSPEEX::InternalCreateEncoder() { - return -1; -} - -void ACMSPEEX::DestructEncoderSafe() { - return; -} - -int16_t ACMSPEEX::InternalCreateDecoder() { - return -1; -} - -void ACMSPEEX::DestructDecoderSafe() { - return; -} - -int16_t ACMSPEEX::SetBitRateSafe(const int32_t /* rate */) { - return -1; -} - -void ACMSPEEX::InternalDestructEncoderInst(void* /* ptr_inst */) { - return; -} - -#ifdef UNUSEDSPEEX -int16_t ACMSPEEX::EnableVBR() { - return -1; -} - -int16_t ACMSPEEX::DisableVBR() { - return -1; -} - -int16_t ACMSPEEX::SetComplMode(int16_t mode) { - return -1; -} -#endif - -#else //===================== Actual Implementation ======================= - -ACMSPEEX::ACMSPEEX(int16_t codec_id) - : encoder_inst_ptr_(NULL), - decoder_inst_ptr_(NULL) { - codec_id_ = codec_id; - - // Set sampling frequency, frame size and rate Speex - if (codec_id_ == ACMCodecDB::kSPEEX8) { - sampling_frequency_ = 8000; - samples_in_20ms_audio_ = 160; - encoding_rate_ = 11000; - } else if (codec_id_ == ACMCodecDB::kSPEEX16) { - sampling_frequency_ = 16000; - samples_in_20ms_audio_ = 320; - encoding_rate_ = 22000; - } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Wrong codec id for Speex."); - - sampling_frequency_ = -1; - samples_in_20ms_audio_ = -1; - encoding_rate_ = -1; - } - - has_internal_dtx_ = true; - dtx_enabled_ = false; - vbr_enabled_ = false; - compl_mode_ = 3; // default complexity value - - return; -} - -ACMSPEEX::~ACMSPEEX() { - if (encoder_inst_ptr_ != NULL) { - WebRtcSpeex_FreeEnc(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } - if (decoder_inst_ptr_ != NULL) { - WebRtcSpeex_FreeDec(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } - return; -} - -int16_t ACMSPEEX::InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) { - int16_t status; - int16_t num_encoded_samples = 0; - int16_t n = 0; - - while (num_encoded_samples < frame_len_smpl_) { - status = WebRtcSpeex_Encode(encoder_inst_ptr_, - &in_audio_[in_audio_ix_read_], encoding_rate_); - - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - in_audio_ix_read_ += samples_in_20ms_audio_; - num_encoded_samples += samples_in_20ms_audio_; - - if (status < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Error in Speex encoder"); - return status; - } - - // Update VAD, if internal DTX is used - if (has_internal_dtx_ && dtx_enabled_) { - vad_label_[n++] = status; - vad_label_[n++] = status; - } - - if (status == 0) { - // This frame is detected as inactive. We need send whatever - // encoded so far. - *bitstream_len_byte = WebRtcSpeex_GetBitstream(encoder_inst_ptr_, - (int16_t*)bitstream); - return *bitstream_len_byte; - } - } - - *bitstream_len_byte = WebRtcSpeex_GetBitstream(encoder_inst_ptr_, - (int16_t*)bitstream); - return *bitstream_len_byte; -} - -int16_t ACMSPEEX::DecodeSafe(uint8_t* /* bitstream */, - int16_t /* bitstream_len_byte */, - int16_t* /* audio */, - int16_t* /* audio_samples */, - int8_t* /* speech_type */) { - return 0; -} - -int16_t ACMSPEEX::EnableDTX() { - if (dtx_enabled_) { - return 0; - } else if (encoder_exist_) { // check if encoder exist - // enable DTX - if (WebRtcSpeex_EncoderInit(encoder_inst_ptr_, (vbr_enabled_ ? 1 : 0), - compl_mode_, 1) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Cannot enable DTX for Speex"); - return -1; - } - dtx_enabled_ = true; - return 0; - } else { - return -1; - } - - return 0; -} - -int16_t ACMSPEEX::DisableDTX() { - if (!dtx_enabled_) { - return 0; - } else if (encoder_exist_) { // check if encoder exist - // disable DTX - if (WebRtcSpeex_EncoderInit(encoder_inst_ptr_, (vbr_enabled_ ? 1 : 0), - compl_mode_, 0) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Cannot disable DTX for Speex"); - return -1; - } - dtx_enabled_ = false; - return 0; - } else { - // encoder doesn't exists, therefore disabling is harmless - return 0; - } - - return 0; -} - -int16_t ACMSPEEX::InternalInitEncoder( - WebRtcACMCodecParams* codec_params) { - // sanity check - if (encoder_inst_ptr_ == NULL) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Cannot initialize Speex encoder, instance does not exist"); - return -1; - } - - int16_t status = SetBitRateSafe((codec_params->codecInstant).rate); - status += - (WebRtcSpeex_EncoderInit(encoder_inst_ptr_, vbr_enabled_, compl_mode_, - ((codec_params->enable_dtx) ? 1 : 0)) < 0) ? - -1 : 0; - - if (status >= 0) { - return 0; - } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Error in initialization of Speex encoder"); - return -1; - } -} - -int16_t ACMSPEEX::InternalInitDecoder( - WebRtcACMCodecParams* /* codec_params */) { - int16_t status; - - // sanity check - if (decoder_inst_ptr_ == NULL) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Cannot initialize Speex decoder, instance does not exist"); - return -1; - } - status = ((WebRtcSpeex_DecoderInit(decoder_inst_ptr_) < 0) ? -1 : 0); - - if (status >= 0) { - return 0; - } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Error in initialization of Speex decoder"); - return -1; - } -} - -int32_t ACMSPEEX::CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst) { - if (!decoder_initialized_) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Error, Speex decoder is not initialized"); - return -1; - } - - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_SPEEX_FUNCTION." - // Then call NetEQ to add the codec to its - // database. - - switch (sampling_frequency_) { - case 8000: { - SET_CODEC_PAR((codec_def), kDecoderSPEEX_8, codec_inst.pltype, - decoder_inst_ptr_, 8000); - break; - } - case 16000: { - SET_CODEC_PAR((codec_def), kDecoderSPEEX_16, codec_inst.pltype, - decoder_inst_ptr_, 16000); - break; - } - default: { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Unsupported sampling frequency for Speex"); - - return -1; - } - } - - SET_SPEEX_FUNCTIONS((codec_def)); - return 0; -} - -ACMGenericCodec* ACMSPEEX::CreateInstance(void) { - return NULL; -} - -int16_t ACMSPEEX::InternalCreateEncoder() { - return WebRtcSpeex_CreateEnc(&encoder_inst_ptr_, sampling_frequency_); -} - -void ACMSPEEX::DestructEncoderSafe() { - if (encoder_inst_ptr_ != NULL) { - WebRtcSpeex_FreeEnc(encoder_inst_ptr_); - encoder_inst_ptr_ = NULL; - } - // there is no encoder set the following - encoder_exist_ = false; - encoder_initialized_ = false; - encoding_rate_ = 0; -} - -int16_t ACMSPEEX::InternalCreateDecoder() { - return WebRtcSpeex_CreateDec(&decoder_inst_ptr_, sampling_frequency_, 1); -} - -void ACMSPEEX::DestructDecoderSafe() { - if (decoder_inst_ptr_ != NULL) { - WebRtcSpeex_FreeDec(decoder_inst_ptr_); - decoder_inst_ptr_ = NULL; - } - // there is no encoder instance set the followings - decoder_exist_ = false; - decoder_initialized_ = false; -} - -int16_t ACMSPEEX::SetBitRateSafe(const int32_t rate) { - // Check if changed rate - if (rate == encoding_rate_) { - return 0; - } else if (rate > 2000) { - encoding_rate_ = rate; - encoder_params_.codecInstant.rate = rate; - } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Unsupported encoding rate for Speex"); - - return -1; - } - - return 0; -} - -void ACMSPEEX::InternalDestructEncoderInst(void* ptr_inst) { - if (ptr_inst != NULL) { - WebRtcSpeex_FreeEnc((SPEEX_encinst_t_*) ptr_inst); - } - return; -} - -#ifdef UNUSEDSPEEX - -// This API is currently not in use. If requested to be able to enable/disable -// VBR an ACM API need to be added. -int16_t ACMSPEEX::EnableVBR() { - if (vbr_enabled_) { - return 0; - } else if (encoder_exist_) { // check if encoder exist - // enable Variable Bit Rate (VBR) - if (WebRtcSpeex_EncoderInit(encoder_inst_ptr_, 1, compl_mode_, - (dtx_enabled_ ? 1 : 0)) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Cannot enable VBR mode for Speex"); - - return -1; - } - vbr_enabled_ = true; - return 0; - } else { - return -1; - } -} - -// This API is currently not in use. If requested to be able to enable/disable -// VBR an ACM API need to be added. -int16_t ACMSPEEX::DisableVBR() { - if (!vbr_enabled_) { - return 0; - } else if (encoder_exist_) { // check if encoder exist - // disable DTX - if (WebRtcSpeex_EncoderInit(encoder_inst_ptr_, 0, compl_mode_, - (dtx_enabled_ ? 1 : 0)) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Cannot disable DTX for Speex"); - - return -1; - } - vbr_enabled_ = false; - return 0; - } else { - // encoder doesn't exists, therefore disabling is harmless - return 0; - } -} - -// This API is currently not in use. If requested to be able to set complexity -// an ACM API need to be added. -int16_t ACMSPEEX::SetComplMode(int16_t mode) { - // Check if new mode - if (mode == compl_mode_) { - return 0; - } else if (encoder_exist_) { // check if encoder exist - // Set new mode - if (WebRtcSpeex_EncoderInit(encoder_inst_ptr_, 0, mode, - (dtx_enabled_ ? 1 : 0)) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, unique_id_, - "Error in complexity mode for Speex"); - return -1; - } - compl_mode_ = mode; - return 0; - } else { - // encoder doesn't exists, therefore disabling is harmless - return 0; - } -} - -#endif - -#endif - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_speex.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_speex.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_speex.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_speex.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_SPEEX_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_SPEEX_H_ - -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" - -// forward declaration -struct SPEEX_encinst_t_; -struct SPEEX_decinst_t_; - -namespace webrtc { - -namespace acm1 { - -class ACMSPEEX : public ACMGenericCodec { - public: - explicit ACMSPEEX(int16_t codec_id); - ~ACMSPEEX(); - - // for FEC - ACMGenericCodec* CreateInstance(void); - - int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte); - - int16_t InternalInitEncoder(WebRtcACMCodecParams *codec_params); - - int16_t InternalInitDecoder(WebRtcACMCodecParams *codec_params); - - protected: - int16_t DecodeSafe(uint8_t* bitstream, - int16_t bitstream_len_byte, - int16_t* audio, - int16_t* audio_samples, - int8_t* speech_type); - - int32_t CodecDef(WebRtcNetEQ_CodecDef& codec_def, - const CodecInst& codec_inst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - int16_t InternalCreateEncoder(); - - int16_t InternalCreateDecoder(); - - void InternalDestructEncoderInst(void* ptr_inst); - - int16_t SetBitRateSafe(const int32_t rate); - - int16_t EnableDTX(); - - int16_t DisableDTX(); - -#ifdef UNUSEDSPEEX - int16_t EnableVBR(); - - int16_t DisableVBR(); - - int16_t SetComplMode(int16_t mode); -#endif - - SPEEX_encinst_t_* encoder_inst_ptr_; - SPEEX_decinst_t_* decoder_inst_ptr_; - int16_t compl_mode_; - bool vbr_enabled_; - int32_t encoding_rate_; - int16_t sampling_frequency_; - uint16_t samples_in_20ms_audio_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_ACM_SPEEX_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_audio_coding -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := \ - acm_cng.cc \ - acm_codec_database.cc \ - acm_dtmf_detection.cc \ - acm_dtmf_playout.cc \ - acm_g722.cc \ - acm_generic_codec.cc \ - acm_ilbc.cc \ - acm_isac.cc \ - acm_neteq.cc \ - acm_pcm16b.cc \ - acm_pcma.cc \ - acm_pcmu.cc \ - acm_red.cc \ - acm_resampler.cc \ - audio_coding_module.cc \ - audio_coding_module_impl.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../codecs/cng/include \ - $(LOCAL_PATH)/../../codecs/g711/include \ - $(LOCAL_PATH)/../../codecs/g722/include \ - $(LOCAL_PATH)/../../codecs/ilbc/interface \ - $(LOCAL_PATH)/../../codecs/iSAC/main/interface \ - $(LOCAL_PATH)/../../codecs/iSAC/fix/interface \ - $(LOCAL_PATH)/../../codecs/pcm16b/include \ - $(LOCAL_PATH)/../../neteq/interface \ - $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../../../../common_audio/resampler/include \ - $(LOCAL_PATH)/../../../../common_audio/signal_processing/include \ - $(LOCAL_PATH)/../../../../common_audio/vad/include \ - $(LOCAL_PATH)/../../../../system_wrappers/interface - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/audio_coding_module.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/audio_coding_module.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/audio_coding_module.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/audio_coding_module.gypi 1970-01-01 00:00:00.000000000 +0000 @@ -1,179 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'variables': { - 'audio_coding_dependencies': [ - 'CNG', - 'NetEq', - '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', - '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', - ], - 'audio_coding_defines': [], - 'conditions': [ - ['include_opus==1', { - 'audio_coding_dependencies': ['webrtc_opus',], - 'audio_coding_defines': ['WEBRTC_CODEC_OPUS',], - }], - ['include_g711==1', { - 'audio_coding_dependencies': ['G711',], - 'audio_coding_defines': ['WEBRTC_CODEC_G711',], - }], - ['include_g722==1', { - 'audio_coding_dependencies': ['G722',], - 'audio_coding_defines': ['WEBRTC_CODEC_G722',], - }], - ['include_ilbc==1', { - 'audio_coding_dependencies': ['iLBC',], - 'audio_coding_defines': ['WEBRTC_CODEC_ILBC',], - }], - ['include_isac==1', { - 'audio_coding_dependencies': ['iSAC', 'iSACFix',], - 'audio_coding_defines': ['WEBRTC_CODEC_ISAC', 'WEBRTC_CODEC_ISACFX',], - }], - ['include_pcm16b==1', { - 'audio_coding_dependencies': ['PCM16B',], - 'audio_coding_defines': ['WEBRTC_CODEC_PCM16',], - }], - ], - }, - 'targets': [ - { - 'target_name': 'audio_coding_module', - 'type': 'static_library', - 'defines': [ - '<@(audio_coding_defines)', - ], - 'dependencies': [ - '<@(audio_coding_dependencies)', - 'acm2', - ], - 'include_dirs': [ - '../interface', - '../../../interface', - '<(webrtc_root)', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - '../../../interface', - '<(webrtc_root)', - ], - }, - 'sources': [ - '../interface/audio_coding_module.h', - '../interface/audio_coding_module_typedefs.h', - 'acm_cng.cc', - 'acm_cng.h', - 'acm_codec_database.cc', - 'acm_codec_database.h', - 'acm_dtmf_detection.cc', - 'acm_dtmf_detection.h', - 'acm_dtmf_playout.cc', - 'acm_dtmf_playout.h', - 'acm_generic_codec.cc', - 'acm_generic_codec.h', - 'acm_neteq.cc', - 'acm_neteq.h', - 'acm_red.cc', - 'acm_red.h', - 'acm_resampler.cc', - 'acm_resampler.h', - 'audio_coding_module_impl.cc', - 'audio_coding_module_impl.h', - ], - 'conditions': [ - ['include_opus==1', { - 'sources': [ - 'acm_opus.cc', - 'acm_opus.h', - ], - }], - ['include_g711==1', { - 'sources': [ - 'acm_pcma.cc', - 'acm_pcma.h', - 'acm_pcmu.cc', - 'acm_pcmu.h', - ], - }], - ['include_g722==1', { - 'sources': [ - 'acm_g722.cc', - 'acm_g722.h', - 'acm_g7221.cc', - 'acm_g7221.h', - 'acm_g7221c.cc', - 'acm_g7221c.h', - ], - }], - ['include_ilbc==1', { - 'sources': [ - 'acm_ilbc.cc', - 'acm_ilbc.h', - ], - }], - ['include_isac==1', { - 'sources': [ - 'acm_isac.cc', - 'acm_isac.h', - 'acm_isac_macros.h', - ], - }], - ['include_pcm16b==1', { - 'sources': [ - 'acm_pcm16b.cc', - 'acm_pcm16b.h', - ], - }], - ], - }, - ], - 'conditions': [ - ['include_tests==1', { - 'targets': [ - { - 'target_name': 'delay_test', - 'type': 'executable', - 'dependencies': [ - 'audio_coding_module', - '<(DEPTH)/testing/gtest.gyp:gtest', - '<(webrtc_root)/test/test.gyp:test_support', - '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', - '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', - ], - 'sources': [ - '../test/delay_test.cc', - '../test/Channel.cc', - '../test/PCMFile.cc', - '../test/utility.cc', - ], - }, # delay_test - { - 'target_name': 'insert_packet_with_timing', - 'type': 'executable', - 'dependencies': [ - 'audio_coding_module', - '<(DEPTH)/testing/gtest.gyp:gtest', - '<(webrtc_root)/test/test.gyp:test_support', - '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', - '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', - ], - 'sources': [ - '../test/insert_packet_with_timing.cc', - '../test/Channel.cc', - '../test/PCMFile.cc', - ], - }, # delay_test - ], - }], - ], - 'includes': [ - '../acm2/audio_coding_module.gypi', - ], -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,3048 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h" - -#include -#include - -#include // For std::max. - -#include "webrtc/engine_configurations.h" -#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" -#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" -#include "webrtc/modules/audio_coding/main/acm2/call_statistics.h" -#include "webrtc/modules/audio_coding/main/source/acm_dtmf_detection.h" -#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h" -#include "webrtc/modules/audio_coding/main/source/acm_resampler.h" -#include "webrtc/modules/audio_coding/main/acm2/nack.h" -#include "webrtc/system_wrappers/interface/clock.h" -#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/logging.h" -#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" -#include "webrtc/system_wrappers/interface/tick_util.h" -#include "webrtc/system_wrappers/interface/trace.h" -#include "webrtc/system_wrappers/interface/trace_event.h" - -namespace webrtc { - -namespace acm1 { - -enum { - kACMToneEnd = 999 -}; - -// Maximum number of bytes in one packet (PCM16B, 20 ms packets, stereo). -enum { - kMaxPacketSize = 2560 -}; - -// Maximum number of payloads that can be packed in one RED payload. For -// regular FEC, we only pack two payloads. In case of dual-streaming, in worst -// case we might pack 3 payloads in one RED payload. -enum { - kNumFecFragmentationVectors = 2, - kMaxNumFragmentationVectors = 3 -}; - -static const uint32_t kMaskTimestamp = 0x03ffffff; -static const int kDefaultTimestampDiff = 960; // 20 ms @ 48 kHz. - -// If packet N is arrived all packets prior to N - |kNackThresholdPackets| which -// are not received are considered as lost, and appear in NACK list. -static const int kNackThresholdPackets = 2; - -namespace { - -bool IsCodecRED(const CodecInst* codec) { - return (STR_CASE_CMP(codec->plname, "RED") == 0); -} - -bool IsCodecRED(int index) { - return (IsCodecRED(&ACMCodecDB::database_[index])); -} - -bool IsCodecCN(const CodecInst* codec) { - return (STR_CASE_CMP(codec->plname, "CN") == 0); -} - -bool IsCodecCN(int index) { - return (IsCodecCN(&ACMCodecDB::database_[index])); -} - -// Stereo-to-mono can be used as in-place. -int DownMix(const AudioFrame& frame, int length_out_buff, int16_t* out_buff) { - if (length_out_buff < frame.samples_per_channel_) { - return -1; - } - for (int n = 0; n < frame.samples_per_channel_; ++n) - out_buff[n] = (frame.data_[2 * n] + frame.data_[2 * n + 1]) >> 1; - return 0; -} - -// Mono-to-stereo can be used as in-place. -int UpMix(const AudioFrame& frame, int length_out_buff, int16_t* out_buff) { - if (length_out_buff < frame.samples_per_channel_) { - return -1; - } - for (int n = frame.samples_per_channel_ - 1; n >= 0; --n) { - out_buff[2 * n + 1] = frame.data_[n]; - out_buff[2 * n] = frame.data_[n]; - } - return 0; -} - -// Return 1 if timestamp t1 is less than timestamp t2, while compensating for -// wrap-around. -int TimestampLessThan(uint32_t t1, uint32_t t2) { - uint32_t kHalfFullRange = static_cast(0xFFFFFFFF) / 2; - if (t1 == t2) { - return 0; - } else if (t1 < t2) { - if (t2 - t1 < kHalfFullRange) - return 1; - return 0; - } else { - if (t1 - t2 < kHalfFullRange) - return 0; - return 1; - } -} - -} // namespace - -AudioCodingModuleImpl::AudioCodingModuleImpl(const int32_t id, Clock* clock) - : packetization_callback_(NULL), - id_(id), - last_timestamp_(0xD87F3F9F), - last_in_timestamp_(0xD87F3F9F), - send_codec_inst_(), - cng_nb_pltype_(255), - cng_wb_pltype_(255), - cng_swb_pltype_(255), - cng_fb_pltype_(255), - red_pltype_(255), - vad_enabled_(false), - dtx_enabled_(false), - vad_mode_(VADNormal), - stereo_receive_registered_(false), - stereo_send_(false), - prev_received_channel_(0), - expected_channels_(1), - current_send_codec_idx_(-1), - current_receive_codec_idx_(-1), - send_codec_registered_(false), - acm_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), - vad_callback_(NULL), - last_recv_audio_codec_pltype_(255), - is_first_red_(true), - fec_enabled_(false), - last_fec_timestamp_(0), - receive_red_pltype_(255), - previous_pltype_(255), - dummy_rtp_header_(NULL), - recv_pl_frame_size_smpls_(0), - receiver_initialized_(false), - dtmf_detector_(NULL), - dtmf_callback_(NULL), - last_detected_tone_(kACMToneEnd), - callback_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), - secondary_send_codec_inst_(), - initial_delay_ms_(0), - num_packets_accumulated_(0), - num_bytes_accumulated_(0), - accumulated_audio_ms_(0), - first_payload_received_(false), - last_incoming_send_timestamp_(0), - track_neteq_buffer_(false), - playout_ts_(0), - av_sync_(false), - last_timestamp_diff_(kDefaultTimestampDiff), - last_sequence_number_(0), - last_ssrc_(0), - last_packet_was_sync_(false), - clock_(clock), - nack_(), - nack_enabled_(false) { - - // Nullify send codec memory, set payload type and set codec name to - // invalid values. - const char no_name[] = "noCodecRegistered"; - strncpy(send_codec_inst_.plname, no_name, RTP_PAYLOAD_NAME_SIZE - 1); - send_codec_inst_.pltype = -1; - - strncpy(secondary_send_codec_inst_.plname, no_name, - RTP_PAYLOAD_NAME_SIZE - 1); - secondary_send_codec_inst_.pltype = -1; - - for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) { - codecs_[i] = NULL; - registered_pltypes_[i] = -1; - stereo_receive_[i] = false; - slave_codecs_[i] = NULL; - mirror_codec_idx_[i] = -1; - } - - neteq_.set_id(id_); - - // Allocate memory for RED. - red_buffer_ = new uint8_t[MAX_PAYLOAD_SIZE_BYTE]; - - // TODO(turajs): This might not be exactly how this class is supposed to work. - // The external usage might be that |fragmentationVectorSize| has to match - // the allocated space for the member-arrays, while here, we allocate - // according to the maximum number of fragmentations and change - // |fragmentationVectorSize| on-the-fly based on actual number of - // fragmentations. However, due to copying to local variable before calling - // SendData, the RTP module receives a "valid" fragmentation, where allocated - // space matches |fragmentationVectorSize|, therefore, this should not cause - // any problem. A better approach is not using RTPFragmentationHeader as - // member variable, instead, use an ACM-specific structure to hold RED-related - // data. See module_common_type.h for the definition of - // RTPFragmentationHeader. - fragmentation_.VerifyAndAllocateFragmentationHeader( - kMaxNumFragmentationVectors); - - // Register the default payload type for RED and for CNG at sampling rates of - // 8, 16, 32 and 48 kHz. - for (int i = (ACMCodecDB::kNumCodecs - 1); i >= 0; i--) { - if (IsCodecRED(i)) { - red_pltype_ = static_cast(ACMCodecDB::database_[i].pltype); - } else if (IsCodecCN(i)) { - if (ACMCodecDB::database_[i].plfreq == 8000) { - cng_nb_pltype_ = static_cast(ACMCodecDB::database_[i].pltype); - } else if (ACMCodecDB::database_[i].plfreq == 16000) { - cng_wb_pltype_ = static_cast(ACMCodecDB::database_[i].pltype); - } else if (ACMCodecDB::database_[i].plfreq == 32000) { - cng_swb_pltype_ = static_cast(ACMCodecDB::database_[i].pltype); - } else if (ACMCodecDB::database_[i].plfreq == 48000) { - cng_fb_pltype_ = static_cast(ACMCodecDB::database_[i].pltype); - } - } - } - - if (InitializeReceiverSafe() < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot initialize receiver"); - } - WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceAudioCoding, id, "Created"); -} - -AudioCodingModuleImpl::~AudioCodingModuleImpl() { - { - CriticalSectionScoped lock(acm_crit_sect_); - current_send_codec_idx_ = -1; - - for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) { - if (codecs_[i] != NULL) { - // True stereo codecs share the same memory for master and - // slave, so slave codec need to be nullified here, since the - // memory will be deleted. - if (slave_codecs_[i] == codecs_[i]) { - slave_codecs_[i] = NULL; - } - - // Mirror index holds the address of the codec memory. - assert(mirror_codec_idx_[i] > -1); - if (codecs_[mirror_codec_idx_[i]] != NULL) { - delete codecs_[mirror_codec_idx_[i]]; - codecs_[mirror_codec_idx_[i]] = NULL; - } - - codecs_[i] = NULL; - } - - if (slave_codecs_[i] != NULL) { - // Delete memory for stereo usage of mono codecs. - assert(mirror_codec_idx_[i] > -1); - if (slave_codecs_[mirror_codec_idx_[i]] != NULL) { - delete slave_codecs_[mirror_codec_idx_[i]]; - slave_codecs_[mirror_codec_idx_[i]] = NULL; - } - slave_codecs_[i] = NULL; - } - } - - if (dtmf_detector_ != NULL) { - delete dtmf_detector_; - dtmf_detector_ = NULL; - } - if (dummy_rtp_header_ != NULL) { - delete dummy_rtp_header_; - dummy_rtp_header_ = NULL; - } - if (red_buffer_ != NULL) { - delete[] red_buffer_; - red_buffer_ = NULL; - } - } - - delete callback_crit_sect_; - callback_crit_sect_ = NULL; - - delete acm_crit_sect_; - acm_crit_sect_ = NULL; - WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceAudioCoding, id_, - "Destroyed"); -} - -int32_t AudioCodingModuleImpl::ChangeUniqueId(const int32_t id) { - { - CriticalSectionScoped lock(acm_crit_sect_); - id_ = id; - - for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) { - if (codecs_[i] != NULL) { - codecs_[i]->SetUniqueID(id); - } - } - } - - neteq_.set_id(id_); - return 0; -} - -// Returns the number of milliseconds until the module want a -// worker thread to call Process. -int32_t AudioCodingModuleImpl::TimeUntilNextProcess() { - CriticalSectionScoped lock(acm_crit_sect_); - - if (!HaveValidEncoder("TimeUntilNextProcess")) { - return -1; - } - return codecs_[current_send_codec_idx_]->SamplesLeftToEncode() / - (send_codec_inst_.plfreq / 1000); -} - -int32_t AudioCodingModuleImpl::Process() { - bool dual_stream; - { - CriticalSectionScoped lock(acm_crit_sect_); - dual_stream = (secondary_encoder_.get() != NULL); - } - if (dual_stream) { - return ProcessDualStream(); - } - return ProcessSingleStream(); -} - -int AudioCodingModuleImpl::EncodeFragmentation(int fragmentation_index, - int payload_type, - uint32_t current_timestamp, - ACMGenericCodec* encoder, - uint8_t* stream) { - int16_t len_bytes = MAX_PAYLOAD_SIZE_BYTE; - uint32_t rtp_timestamp; - WebRtcACMEncodingType encoding_type; - if (encoder->Encode(stream, &len_bytes, &rtp_timestamp, &encoding_type) < 0) { - return -1; - } - assert(encoding_type == kActiveNormalEncoded); - assert(len_bytes > 0); - - fragmentation_.fragmentationLength[fragmentation_index] = len_bytes; - fragmentation_.fragmentationPlType[fragmentation_index] = payload_type; - fragmentation_.fragmentationTimeDiff[fragmentation_index] = - static_cast(current_timestamp - rtp_timestamp); - fragmentation_.fragmentationVectorSize++; - return len_bytes; -} - -// Primary payloads are sent immediately, whereas a single secondary payload is -// buffered to be combined with "the next payload." -// Normally "the next payload" would be a primary payload. In case two -// consecutive secondary payloads are generated with no primary payload in -// between, then two secondary payloads are packed in one RED. -int AudioCodingModuleImpl::ProcessDualStream() { - uint8_t stream[kMaxNumFragmentationVectors * MAX_PAYLOAD_SIZE_BYTE]; - uint32_t current_timestamp; - int16_t length_bytes = 0; - RTPFragmentationHeader my_fragmentation; - - uint8_t my_red_payload_type; - - { - CriticalSectionScoped lock(acm_crit_sect_); - // Check if there is an encoder before. - if (!HaveValidEncoder("ProcessDualStream") || - secondary_encoder_.get() == NULL) { - return -1; - } - ACMGenericCodec* primary_encoder = codecs_[current_send_codec_idx_]; - // If primary encoder has a full frame of audio to generate payload. - bool primary_ready_to_encode = primary_encoder->HasFrameToEncode(); - // If the secondary encoder has a frame of audio to generate a payload. - bool secondary_ready_to_encode = secondary_encoder_->HasFrameToEncode(); - - if (!primary_ready_to_encode && !secondary_ready_to_encode) { - // Nothing to send. - return 0; - } - int len_bytes_previous_secondary = static_cast( - fragmentation_.fragmentationLength[2]); - assert(len_bytes_previous_secondary <= MAX_PAYLOAD_SIZE_BYTE); - bool has_previous_payload = len_bytes_previous_secondary > 0; - - uint32_t primary_timestamp = primary_encoder->EarliestTimestamp(); - uint32_t secondary_timestamp = secondary_encoder_->EarliestTimestamp(); - - if (!has_previous_payload && !primary_ready_to_encode && - secondary_ready_to_encode) { - // Secondary payload will be the ONLY bit-stream. Encode by secondary - // encoder, store the payload, and return. No packet is sent. - int16_t len_bytes = MAX_PAYLOAD_SIZE_BYTE; - WebRtcACMEncodingType encoding_type; - if (secondary_encoder_->Encode(red_buffer_, &len_bytes, - &last_fec_timestamp_, - &encoding_type) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "ProcessDual(): Encoding of secondary encoder Failed"); - return -1; - } - assert(len_bytes > 0); - assert(encoding_type == kActiveNormalEncoded); - assert(len_bytes <= MAX_PAYLOAD_SIZE_BYTE); - fragmentation_.fragmentationLength[2] = len_bytes; - return 0; - } - - // Initialize with invalid but different values, so later can have sanity - // check if they are different. - int index_primary = -1; - int index_secondary = -2; - int index_previous_secondary = -3; - - if (primary_ready_to_encode) { - index_primary = secondary_ready_to_encode ? - TimestampLessThan(primary_timestamp, secondary_timestamp) : 0; - index_primary += has_previous_payload ? - TimestampLessThan(primary_timestamp, last_fec_timestamp_) : 0; - } - - if (secondary_ready_to_encode) { - // Timestamp of secondary payload can only be less than primary payload, - // but is always larger than the timestamp of previous secondary payload. - index_secondary = primary_ready_to_encode ? - (1 - TimestampLessThan(primary_timestamp, secondary_timestamp)) : 0; - } - - if (has_previous_payload) { - index_previous_secondary = primary_ready_to_encode ? - (1 - TimestampLessThan(primary_timestamp, last_fec_timestamp_)) : 0; - // If secondary is ready it always have a timestamp larger than previous - // secondary. So the index is either 0 or 1. - index_previous_secondary += secondary_ready_to_encode ? 1 : 0; - } - - // Indices must not be equal. - assert(index_primary != index_secondary); - assert(index_primary != index_previous_secondary); - assert(index_secondary != index_previous_secondary); - - // One of the payloads has to be at position zero. - assert(index_primary == 0 || index_secondary == 0 || - index_previous_secondary == 0); - - // Timestamp of the RED payload. - if (index_primary == 0) { - current_timestamp = primary_timestamp; - } else if (index_secondary == 0) { - current_timestamp = secondary_timestamp; - } else { - current_timestamp = last_fec_timestamp_; - } - - fragmentation_.fragmentationVectorSize = 0; - if (has_previous_payload) { - assert(index_previous_secondary >= 0 && - index_previous_secondary < kMaxNumFragmentationVectors); - assert(len_bytes_previous_secondary <= MAX_PAYLOAD_SIZE_BYTE); - memcpy(&stream[index_previous_secondary * MAX_PAYLOAD_SIZE_BYTE], - red_buffer_, sizeof(stream[0]) * len_bytes_previous_secondary); - fragmentation_.fragmentationLength[index_previous_secondary] = - len_bytes_previous_secondary; - fragmentation_.fragmentationPlType[index_previous_secondary] = - secondary_send_codec_inst_.pltype; - fragmentation_.fragmentationTimeDiff[index_previous_secondary] = - static_cast(current_timestamp - last_fec_timestamp_); - fragmentation_.fragmentationVectorSize++; - } - - if (primary_ready_to_encode) { - assert(index_primary >= 0 && index_primary < kMaxNumFragmentationVectors); - int i = index_primary * MAX_PAYLOAD_SIZE_BYTE; - if (EncodeFragmentation(index_primary, send_codec_inst_.pltype, - current_timestamp, primary_encoder, - &stream[i]) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "ProcessDualStream(): Encoding of primary encoder Failed"); - return -1; - } - } - - if (secondary_ready_to_encode) { - assert(index_secondary >= 0 && - index_secondary < kMaxNumFragmentationVectors - 1); - int i = index_secondary * MAX_PAYLOAD_SIZE_BYTE; - if (EncodeFragmentation(index_secondary, - secondary_send_codec_inst_.pltype, - current_timestamp, secondary_encoder_.get(), - &stream[i]) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "ProcessDualStream(): Encoding of secondary encoder " - "Failed"); - return -1; - } - } - // Copy to local variable, as it will be used outside the ACM lock. - my_fragmentation.CopyFrom(fragmentation_); - my_red_payload_type = red_pltype_; - length_bytes = 0; - for (int n = 0; n < fragmentation_.fragmentationVectorSize; n++) { - length_bytes += fragmentation_.fragmentationLength[n]; - } - } - - { - CriticalSectionScoped lock(callback_crit_sect_); - if (packetization_callback_ != NULL) { - // Callback with payload data, including redundant data (FEC/RED). - if (packetization_callback_->SendData(kAudioFrameSpeech, - my_red_payload_type, - current_timestamp, stream, - length_bytes, - &my_fragmentation) < 0) { - return -1; - } - } - } - - { - CriticalSectionScoped lock(acm_crit_sect_); - // Now that data is sent, clean up fragmentation. - ResetFragmentation(0); - } - return 0; -} - -// Process any pending tasks such as timeouts. -int AudioCodingModuleImpl::ProcessSingleStream() { - // Make room for 1 RED payload. - uint8_t stream[2 * MAX_PAYLOAD_SIZE_BYTE]; - int16_t length_bytes = 2 * MAX_PAYLOAD_SIZE_BYTE; - int16_t red_length_bytes = length_bytes; - uint32_t rtp_timestamp; - int16_t status; - WebRtcACMEncodingType encoding_type; - FrameType frame_type = kAudioFrameSpeech; - uint8_t current_payload_type = 0; - bool has_data_to_send = false; - bool fec_active = false; - RTPFragmentationHeader my_fragmentation; - - // Keep the scope of the ACM critical section limited. - { - CriticalSectionScoped lock(acm_crit_sect_); - // Check if there is an encoder before. - if (!HaveValidEncoder("ProcessSingleStream")) { - return -1; - } - status = codecs_[current_send_codec_idx_]->Encode(stream, &length_bytes, - &rtp_timestamp, - &encoding_type); - if (status < 0) { - // Encode failed. - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "ProcessSingleStream(): Encoding Failed"); - length_bytes = 0; - return -1; - } else if (status == 0) { - // Not enough data. - return 0; - } else { - switch (encoding_type) { - case kNoEncoding: { - current_payload_type = previous_pltype_; - frame_type = kFrameEmpty; - length_bytes = 0; - break; - } - case kActiveNormalEncoded: - case kPassiveNormalEncoded: { - current_payload_type = static_cast(send_codec_inst_.pltype); - frame_type = kAudioFrameSpeech; - break; - } - case kPassiveDTXNB: { - current_payload_type = cng_nb_pltype_; - frame_type = kAudioFrameCN; - is_first_red_ = true; - break; - } - case kPassiveDTXWB: { - current_payload_type = cng_wb_pltype_; - frame_type = kAudioFrameCN; - is_first_red_ = true; - break; - } - case kPassiveDTXSWB: { - current_payload_type = cng_swb_pltype_; - frame_type = kAudioFrameCN; - is_first_red_ = true; - break; - } - case kPassiveDTXFB: { - current_payload_type = cng_fb_pltype_; - frame_type = kAudioFrameCN; - is_first_red_ = true; - break; - } - } - has_data_to_send = true; - previous_pltype_ = current_payload_type; - - // Redundancy encode is done here. The two bitstreams packetized into - // one RTP packet and the fragmentation points are set. - // Only apply RED on speech data. - if ((fec_enabled_) && - ((encoding_type == kActiveNormalEncoded) || - (encoding_type == kPassiveNormalEncoded))) { - // FEC is enabled within this scope. - // - // Note that, a special solution exists for iSAC since it is the only - // codec for which GetRedPayload has a non-empty implementation. - // - // Summary of the FEC scheme below (use iSAC as example): - // - // 1st (is_first_red_ is true) encoded iSAC frame (primary #1) => - // - call GetRedPayload() and store redundancy for packet #1 in - // second fragment of RED buffer (old data) - // - drop the primary iSAC frame - // - don't call SendData - // 2nd (is_first_red_ is false) encoded iSAC frame (primary #2) => - // - store primary #2 in 1st fragment of RED buffer and send the - // combined packet - // - the transmitted packet contains primary #2 (new) and - // reduncancy for packet #1 (old) - // - call GetRed_Payload() and store redundancy for packet #2 in - // second fragment of RED buffer - // - // ... - // - // Nth encoded iSAC frame (primary #N) => - // - store primary #N in 1st fragment of RED buffer and send the - // combined packet - // - the transmitted packet contains primary #N (new) and - // reduncancy for packet #(N-1) (old) - // - call GetRedPayload() and store redundancy for packet #N in - // second fragment of RED buffer - // - // For all other codecs, GetRedPayload does nothing and returns -1 => - // redundant data is only a copy. - // - // First combined packet contains : #2 (new) and #1 (old) - // Second combined packet contains: #3 (new) and #2 (old) - // Third combined packet contains : #4 (new) and #3 (old) - // - // Hence, even if every second packet is dropped, perfect - // reconstruction is possible. - fec_active = true; - - has_data_to_send = false; - // Skip the following part for the first packet in a RED session. - if (!is_first_red_) { - // Rearrange stream such that FEC packets are included. - // Replace stream now that we have stored current stream. - memcpy(stream + fragmentation_.fragmentationOffset[1], red_buffer_, - fragmentation_.fragmentationLength[1]); - // Update the fragmentation time difference vector, in number of - // timestamps. - uint16_t time_since_last = static_cast(rtp_timestamp - - last_fec_timestamp_); - - // Update fragmentation vectors. - fragmentation_.fragmentationPlType[1] = - fragmentation_.fragmentationPlType[0]; - fragmentation_.fragmentationTimeDiff[1] = time_since_last; - has_data_to_send = true; - } - - // Insert new packet length. - fragmentation_.fragmentationLength[0] = length_bytes; - - // Insert new packet payload type. - fragmentation_.fragmentationPlType[0] = current_payload_type; - last_fec_timestamp_ = rtp_timestamp; - - // Can be modified by the GetRedPayload() call if iSAC is utilized. - red_length_bytes = length_bytes; - - // A fragmentation header is provided => packetization according to - // RFC 2198 (RTP Payload for Redundant Audio Data) will be used. - // First fragment is the current data (new). - // Second fragment is the previous data (old). - length_bytes = static_cast( - fragmentation_.fragmentationLength[0] + - fragmentation_.fragmentationLength[1]); - - // Get, and store, redundant data from the encoder based on the recently - // encoded frame. - // NOTE - only iSAC contains an implementation; all other codecs does - // nothing and returns -1. - if (codecs_[current_send_codec_idx_]->GetRedPayload( - red_buffer_, - &red_length_bytes) == -1) { - // The codec was not iSAC => use current encoder output as redundant - // data instead (trivial FEC scheme). - memcpy(red_buffer_, stream, red_length_bytes); - } - - is_first_red_ = false; - // Update payload type with RED payload type. - current_payload_type = red_pltype_; - // We have packed 2 payloads. - fragmentation_.fragmentationVectorSize = kNumFecFragmentationVectors; - - // Copy to local variable, as it will be used outside ACM lock. - my_fragmentation.CopyFrom(fragmentation_); - // Store RED length. - fragmentation_.fragmentationLength[1] = red_length_bytes; - } - } - } - - if (has_data_to_send) { - CriticalSectionScoped lock(callback_crit_sect_); - - if (packetization_callback_ != NULL) { - if (fec_active) { - // Callback with payload data, including redundant data (FEC/RED). - packetization_callback_->SendData(frame_type, current_payload_type, - rtp_timestamp, stream, - length_bytes, - &my_fragmentation); - } else { - // Callback with payload data. - packetization_callback_->SendData(frame_type, current_payload_type, - rtp_timestamp, stream, - length_bytes, NULL); - } - } - - if (vad_callback_ != NULL) { - // Callback with VAD decision. - vad_callback_->InFrameType(static_cast(encoding_type)); - } - } - return length_bytes; -} - -///////////////////////////////////////// -// Sender -// - -// Initialize send codec. -int32_t AudioCodingModuleImpl::InitializeSender() { - CriticalSectionScoped lock(acm_crit_sect_); - - // Start with invalid values. - send_codec_registered_ = false; - current_send_codec_idx_ = -1; - send_codec_inst_.plname[0] = '\0'; - - // Delete all encoders to start fresh. - for (int id = 0; id < ACMCodecDB::kMaxNumCodecs; id++) { - if (codecs_[id] != NULL) { - codecs_[id]->DestructEncoder(); - } - } - - // Initialize FEC/RED. - is_first_red_ = true; - if (fec_enabled_ || secondary_encoder_.get() != NULL) { - if (red_buffer_ != NULL) { - memset(red_buffer_, 0, MAX_PAYLOAD_SIZE_BYTE); - } - if (fec_enabled_) { - ResetFragmentation(kNumFecFragmentationVectors); - } else { - ResetFragmentation(0); - } - } - - return 0; -} - -int32_t AudioCodingModuleImpl::ResetEncoder() { - CriticalSectionScoped lock(acm_crit_sect_); - if (!HaveValidEncoder("ResetEncoder")) { - return -1; - } - return codecs_[current_send_codec_idx_]->ResetEncoder(); -} - -void AudioCodingModuleImpl::UnregisterSendCodec() { - CriticalSectionScoped lock(acm_crit_sect_); - send_codec_registered_ = false; - current_send_codec_idx_ = -1; - // If send Codec is unregistered then remove the secondary codec as well. - if (secondary_encoder_.get() != NULL) - secondary_encoder_.reset(); - return; -} - -ACMGenericCodec* AudioCodingModuleImpl::CreateCodec(const CodecInst& codec) { - ACMGenericCodec* my_codec = NULL; - - my_codec = ACMCodecDB::CreateCodecInstance(&codec); - if (my_codec == NULL) { - // Error, could not create the codec. - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "ACMCodecDB::CreateCodecInstance() failed in CreateCodec()"); - return my_codec; - } - my_codec->SetUniqueID(id_); - my_codec->SetNetEqDecodeLock(neteq_.DecodeLock()); - - return my_codec; -} - -// Check if the given codec is a valid to be registered as send codec. -static int IsValidSendCodec(const CodecInst& send_codec, - bool is_primary_encoder, - int acm_id, - int* mirror_id) { - if ((send_codec.channels != 1) && (send_codec.channels != 2)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id, - "Wrong number of channels (%d, only mono and stereo are " - "supported) for %s encoder", send_codec.channels, - is_primary_encoder ? "primary" : "secondary"); - return -1; - } - - int codec_id = ACMCodecDB::CodecNumber(&send_codec, mirror_id); - if (codec_id < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id, - "Invalid settings for the send codec."); - return -1; - } - - // TODO(tlegrand): Remove this check. Already taken care of in - // ACMCodecDB::CodecNumber(). - // Check if the payload-type is valid - if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id, - "Invalid payload-type %d for %s.", send_codec.pltype, - send_codec.plname); - return -1; - } - - // Telephone-event cannot be a send codec. - if (!STR_CASE_CMP(send_codec.plname, "telephone-event")) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id, - "telephone-event cannot be a send codec"); - *mirror_id = -1; - return -1; - } - - if (ACMCodecDB::codec_settings_[codec_id].channel_support - < send_codec.channels) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id, - "%d number of channels not supportedn for %s.", - send_codec.channels, send_codec.plname); - *mirror_id = -1; - return -1; - } - - if (!is_primary_encoder) { - // If registering the secondary encoder, then RED and CN are not valid - // choices as encoder. - if (IsCodecRED(&send_codec)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id, - "RED cannot be secondary codec"); - *mirror_id = -1; - return -1; - } - - if (IsCodecCN(&send_codec)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, acm_id, - "DTX cannot be secondary codec"); - *mirror_id = -1; - return -1; - } - } - return codec_id; -} - -int AudioCodingModuleImpl::RegisterSecondarySendCodec( - const CodecInst& send_codec) { - CriticalSectionScoped lock(acm_crit_sect_); - if (!send_codec_registered_) { - return -1; - } - // Primary and Secondary codecs should have the same sampling rates. - if (send_codec.plfreq != send_codec_inst_.plfreq) { - return -1; - } - int mirror_id; - int codec_id = IsValidSendCodec(send_codec, false, id_, &mirror_id); - if (codec_id < 0) { - return -1; - } - ACMGenericCodec* encoder = CreateCodec(send_codec); - WebRtcACMCodecParams codec_params; - // Initialize the codec before registering. For secondary codec VAD & DTX are - // disabled. - memcpy(&(codec_params.codec_inst), &send_codec, sizeof(CodecInst)); - codec_params.enable_vad = false; - codec_params.enable_dtx = false; - codec_params.vad_mode = VADNormal; - // Force initialization. - if (encoder->InitEncoder(&codec_params, true) < 0) { - // Could not initialize, therefore cannot be registered. - delete encoder; - return -1; - } - secondary_encoder_.reset(encoder); - memcpy(&secondary_send_codec_inst_, &send_codec, sizeof(send_codec)); - - // Disable VAD & DTX. - SetVADSafe(false, false, VADNormal); - - // Cleaning. - if (red_buffer_) { - memset(red_buffer_, 0, MAX_PAYLOAD_SIZE_BYTE); - } - ResetFragmentation(0); - return 0; -} - -void AudioCodingModuleImpl::UnregisterSecondarySendCodec() { - CriticalSectionScoped lock(acm_crit_sect_); - if (secondary_encoder_.get() == NULL) { - return; - } - secondary_encoder_.reset(); - ResetFragmentation(0); -} - -int AudioCodingModuleImpl::SecondarySendCodec( - CodecInst* secondary_codec) const { - CriticalSectionScoped lock(acm_crit_sect_); - if (secondary_encoder_.get() == NULL) { - return -1; - } - memcpy(secondary_codec, &secondary_send_codec_inst_, - sizeof(secondary_send_codec_inst_)); - return 0; -} - -// Can be called multiple times for Codec, CNG, RED. -int32_t AudioCodingModuleImpl::RegisterSendCodec( - const CodecInst& send_codec) { - int mirror_id; - int codec_id = IsValidSendCodec(send_codec, true, id_, &mirror_id); - - CriticalSectionScoped lock(acm_crit_sect_); - - // Check for reported errors from function IsValidSendCodec(). - if (codec_id < 0) { - if (!send_codec_registered_) { - // This values has to be NULL if there is no codec registered. - current_send_codec_idx_ = -1; - } - return -1; - } - - // RED can be registered with other payload type. If not registered a default - // payload type is used. - if (IsCodecRED(&send_codec)) { - // TODO(tlegrand): Remove this check. Already taken care of in - // ACMCodecDB::CodecNumber(). - // Check if the payload-type is valid - if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Invalid payload-type %d for %s.", send_codec.pltype, - send_codec.plname); - return -1; - } - // Set RED payload type. - red_pltype_ = static_cast(send_codec.pltype); - return 0; - } - - // CNG can be registered with other payload type. If not registered the - // default payload types from codec database will be used. - if (IsCodecCN(&send_codec)) { - // CNG is registered. - switch (send_codec.plfreq) { - case 8000: { - cng_nb_pltype_ = static_cast(send_codec.pltype); - break; - } - case 16000: { - cng_wb_pltype_ = static_cast(send_codec.pltype); - break; - } - case 32000: { - cng_swb_pltype_ = static_cast(send_codec.pltype); - break; - } - case 48000: { - cng_fb_pltype_ = static_cast(send_codec.pltype); - break; - } - default: { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "RegisterSendCodec() failed, invalid frequency for CNG " - "registration"); - return -1; - } - } - return 0; - } - - // Set Stereo, and make sure VAD and DTX is turned off. - if (send_codec.channels == 2) { - stereo_send_ = true; - if (vad_enabled_ || dtx_enabled_) { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, - "VAD/DTX is turned off, not supported when sending stereo."); - } - vad_enabled_ = false; - dtx_enabled_ = false; - } else { - stereo_send_ = false; - } - - // Check if the codec is already registered as send codec. - bool is_send_codec; - if (send_codec_registered_) { - int send_codec_mirror_id; - int send_codec_id = ACMCodecDB::CodecNumber(&send_codec_inst_, - &send_codec_mirror_id); - assert(send_codec_id >= 0); - is_send_codec = (send_codec_id == codec_id) || - (mirror_id == send_codec_mirror_id); - } else { - is_send_codec = false; - } - - // If there is secondary codec registered and the new send codec has a - // sampling rate different than that of secondary codec, then unregister the - // secondary codec. - if (secondary_encoder_.get() != NULL && - secondary_send_codec_inst_.plfreq != send_codec.plfreq) { - secondary_encoder_.reset(); - ResetFragmentation(0); - } - - // If new codec, or new settings, register. - if (!is_send_codec) { - if (codecs_[mirror_id] == NULL) { - codecs_[mirror_id] = CreateCodec(send_codec); - if (codecs_[mirror_id] == NULL) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot Create the codec"); - return -1; - } - mirror_codec_idx_[mirror_id] = mirror_id; - } - - if (mirror_id != codec_id) { - codecs_[codec_id] = codecs_[mirror_id]; - mirror_codec_idx_[codec_id] = mirror_id; - } - - ACMGenericCodec* codec_ptr = codecs_[codec_id]; - WebRtcACMCodecParams codec_params; - - memcpy(&(codec_params.codec_inst), &send_codec, sizeof(CodecInst)); - codec_params.enable_vad = vad_enabled_; - codec_params.enable_dtx = dtx_enabled_; - codec_params.vad_mode = vad_mode_; - // Force initialization. - if (codec_ptr->InitEncoder(&codec_params, true) < 0) { - // Could not initialize the encoder. - - // Check if already have a registered codec. - // Depending on that different messages are logged. - if (!send_codec_registered_) { - current_send_codec_idx_ = -1; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot Initialize the encoder No Encoder is registered"); - } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot Initialize the encoder, continue encoding with " - "the previously registered codec"); - } - return -1; - } - - // Update states. - dtx_enabled_ = codec_params.enable_dtx; - vad_enabled_ = codec_params.enable_vad; - vad_mode_ = codec_params.vad_mode; - - // Everything is fine so we can replace the previous codec with this one. - if (send_codec_registered_) { - // If we change codec we start fresh with FEC. - // This is not strictly required by the standard. - is_first_red_ = true; - - codec_ptr->SetVAD(&dtx_enabled_, &vad_enabled_, &vad_mode_); - } - - current_send_codec_idx_ = codec_id; - send_codec_registered_ = true; - memcpy(&send_codec_inst_, &send_codec, sizeof(CodecInst)); - previous_pltype_ = send_codec_inst_.pltype; - return 0; - } else { - // If codec is the same as already registered check if any parameters - // has changed compared to the current values. - // If any parameter is valid then apply it and record. - bool force_init = false; - - if (mirror_id != codec_id) { - codecs_[codec_id] = codecs_[mirror_id]; - mirror_codec_idx_[codec_id] = mirror_id; - } - - // Check the payload type. - if (send_codec.pltype != send_codec_inst_.pltype) { - // At this point check if the given payload type is valid. - // Record it later when the sampling frequency is changed - // successfully. - if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Out of range payload type"); - return -1; - } - } - - // If there is a codec that ONE instance of codec supports multiple - // sampling frequencies, then we need to take care of it here. - // one such a codec is iSAC. Both WB and SWB are encoded and decoded - // with one iSAC instance. Therefore, we need to update the encoder - // frequency if required. - if (send_codec_inst_.plfreq != send_codec.plfreq) { - force_init = true; - - // If sampling frequency is changed we have to start fresh with RED. - is_first_red_ = true; - } - - // If packet size or number of channels has changed, we need to - // re-initialize the encoder. - if (send_codec_inst_.pacsize != send_codec.pacsize) { - force_init = true; - } - if (send_codec_inst_.channels != send_codec.channels) { - force_init = true; - } - - if (force_init) { - WebRtcACMCodecParams codec_params; - - memcpy(&(codec_params.codec_inst), &send_codec, sizeof(CodecInst)); - codec_params.enable_vad = vad_enabled_; - codec_params.enable_dtx = dtx_enabled_; - codec_params.vad_mode = vad_mode_; - - // Force initialization. - if (codecs_[current_send_codec_idx_]->InitEncoder(&codec_params, - true) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Could not change the codec packet-size."); - return -1; - } - - send_codec_inst_.plfreq = send_codec.plfreq; - send_codec_inst_.pacsize = send_codec.pacsize; - send_codec_inst_.channels = send_codec.channels; - } - - // If the change of sampling frequency has been successful then - // we store the payload-type. - send_codec_inst_.pltype = send_codec.pltype; - - // Check if a change in Rate is required. - if (send_codec.rate != send_codec_inst_.rate) { - if (codecs_[codec_id]->SetBitRate(send_codec.rate) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Could not change the codec rate."); - return -1; - } - send_codec_inst_.rate = send_codec.rate; - } - previous_pltype_ = send_codec_inst_.pltype; - - return 0; - } -} - -// Get current send codec. -int32_t AudioCodingModuleImpl::SendCodec( - CodecInst* current_codec) const { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, - "SendCodec()"); - CriticalSectionScoped lock(acm_crit_sect_); - - assert(current_codec); - if (!send_codec_registered_) { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, - "SendCodec Failed, no codec is registered"); - - return -1; - } - WebRtcACMCodecParams encoder_param; - codecs_[current_send_codec_idx_]->EncoderParams(&encoder_param); - encoder_param.codec_inst.pltype = send_codec_inst_.pltype; - memcpy(current_codec, &(encoder_param.codec_inst), sizeof(CodecInst)); - - return 0; -} - -// Get current send frequency. -int32_t AudioCodingModuleImpl::SendFrequency() const { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, - "SendFrequency()"); - CriticalSectionScoped lock(acm_crit_sect_); - - if (!send_codec_registered_) { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, - "SendFrequency Failed, no codec is registered"); - - return -1; - } - - return send_codec_inst_.plfreq; -} - -// Get encode bitrate. -// Adaptive rate codecs return their current encode target rate, while other -// codecs return there longterm avarage or their fixed rate. -int32_t AudioCodingModuleImpl::SendBitrate() const { - CriticalSectionScoped lock(acm_crit_sect_); - - if (!send_codec_registered_) { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, - "SendBitrate Failed, no codec is registered"); - - return -1; - } - - WebRtcACMCodecParams encoder_param; - codecs_[current_send_codec_idx_]->EncoderParams(&encoder_param); - - return encoder_param.codec_inst.rate; -} - -// Set available bandwidth, inform the encoder about the estimated bandwidth -// received from the remote party. -int32_t AudioCodingModuleImpl::SetReceivedEstimatedBandwidth( - const int32_t bw) { - return codecs_[current_send_codec_idx_]->SetEstimatedBandwidth(bw); -} - -// Register a transport callback which will be called to deliver -// the encoded buffers. -int32_t AudioCodingModuleImpl::RegisterTransportCallback( - AudioPacketizationCallback* transport) { - CriticalSectionScoped lock(callback_crit_sect_); - packetization_callback_ = transport; - return 0; -} - -// Add 10MS of raw (PCM) audio data to the encoder. -int32_t AudioCodingModuleImpl::Add10MsData( - const AudioFrame& audio_frame) { - if (audio_frame.samples_per_channel_ <= 0) { - assert(false); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot Add 10 ms audio, payload length is negative or " - "zero"); - return -1; - } - - // Allow for 8, 16, 32 and 48kHz input audio. - if ((audio_frame.sample_rate_hz_ != 8000) - && (audio_frame.sample_rate_hz_ != 16000) - && (audio_frame.sample_rate_hz_ != 32000) - && (audio_frame.sample_rate_hz_ != 48000)) { - assert(false); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot Add 10 ms audio, input frequency not valid"); - return -1; - } - - // If the length and frequency matches. We currently just support raw PCM. - if ((audio_frame.sample_rate_hz_ / 100) - != audio_frame.samples_per_channel_) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot Add 10 ms audio, input frequency and length doesn't" - " match"); - return -1; - } - - if (audio_frame.num_channels_ != 1 && audio_frame.num_channels_ != 2) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot Add 10 ms audio, invalid number of channels."); - return -1; - } - - CriticalSectionScoped lock(acm_crit_sect_); - // Do we have a codec registered? - if (!HaveValidEncoder("Add10MsData")) { - return -1; - } - - const AudioFrame* ptr_frame; - // Perform a resampling, also down-mix if it is required and can be - // performed before resampling (a down mix prior to resampling will take - // place if both primary and secondary encoders are mono and input is in - // stereo). - if (PreprocessToAddData(audio_frame, &ptr_frame) < 0) { - return -1; - } - TRACE_EVENT_ASYNC_BEGIN1("webrtc", "Audio", ptr_frame->timestamp_, - "now", clock_->TimeInMilliseconds()); - - // Check whether we need an up-mix or down-mix? - bool remix = ptr_frame->num_channels_ != send_codec_inst_.channels; - if (secondary_encoder_.get() != NULL) { - remix = remix || - (ptr_frame->num_channels_ != secondary_send_codec_inst_.channels); - } - - // If a re-mix is required (up or down), this buffer will store re-mixed - // version of the input. - int16_t buffer[WEBRTC_10MS_PCM_AUDIO]; - if (remix) { - if (ptr_frame->num_channels_ == 1) { - if (UpMix(*ptr_frame, WEBRTC_10MS_PCM_AUDIO, buffer) < 0) - return -1; - } else { - if (DownMix(*ptr_frame, WEBRTC_10MS_PCM_AUDIO, buffer) < 0) - return -1; - } - } - - // When adding data to encoders this pointer is pointing to an audio buffer - // with correct number of channels. - const int16_t* ptr_audio = ptr_frame->data_; - - // For pushing data to primary, point the |ptr_audio| to correct buffer. - if (send_codec_inst_.channels != ptr_frame->num_channels_) - ptr_audio = buffer; - - if (codecs_[current_send_codec_idx_]->Add10MsData( - ptr_frame->timestamp_, ptr_audio, ptr_frame->samples_per_channel_, - send_codec_inst_.channels) < 0) - return -1; - - if (secondary_encoder_.get() != NULL) { - // For pushing data to secondary, point the |ptr_audio| to correct buffer. - ptr_audio = ptr_frame->data_; - if (secondary_send_codec_inst_.channels != ptr_frame->num_channels_) - ptr_audio = buffer; - - if (secondary_encoder_->Add10MsData( - ptr_frame->timestamp_, ptr_audio, ptr_frame->samples_per_channel_, - secondary_send_codec_inst_.channels) < 0) - return -1; - } - - return 0; -} - -// Perform a resampling and down-mix if required. We down-mix only if -// encoder is mono and input is stereo. In case of dual-streaming, both -// encoders has to be mono for down-mix to take place. -// |*ptr_out| will point to the pre-processed audio-frame. If no pre-processing -// is required, |*ptr_out| points to |in_frame|. -int AudioCodingModuleImpl::PreprocessToAddData(const AudioFrame& in_frame, - const AudioFrame** ptr_out) { - // Primary and secondary (if exists) should have the same sampling rate. - assert((secondary_encoder_.get() != NULL) ? - secondary_send_codec_inst_.plfreq == send_codec_inst_.plfreq : true); - - bool resample = static_cast(in_frame.sample_rate_hz_) != - send_codec_inst_.plfreq; - - // This variable is true if primary codec and secondary codec (if exists) - // are both mono and input is stereo. - bool down_mix; - if (secondary_encoder_.get() != NULL) { - down_mix = (in_frame.num_channels_ == 2) && - (send_codec_inst_.channels == 1) && - (secondary_send_codec_inst_.channels == 1); - } else { - down_mix = (in_frame.num_channels_ == 2) && - (send_codec_inst_.channels == 1); - } - - if (!down_mix && !resample) { - // No pre-processing is required. - last_in_timestamp_ = in_frame.timestamp_; - last_timestamp_ = in_frame.timestamp_; - *ptr_out = &in_frame; - return 0; - } - - *ptr_out = &preprocess_frame_; - preprocess_frame_.num_channels_ = in_frame.num_channels_; - int16_t audio[WEBRTC_10MS_PCM_AUDIO]; - const int16_t* src_ptr_audio = in_frame.data_; - int16_t* dest_ptr_audio = preprocess_frame_.data_; - if (down_mix) { - // If a resampling is required the output of a down-mix is written into a - // local buffer, otherwise, it will be written to the output frame. - if (resample) - dest_ptr_audio = audio; - if (DownMix(in_frame, WEBRTC_10MS_PCM_AUDIO, dest_ptr_audio) < 0) - return -1; - preprocess_frame_.num_channels_ = 1; - // Set the input of the resampler is the down-mixed signal. - src_ptr_audio = audio; - } - - preprocess_frame_.timestamp_ = in_frame.timestamp_; - preprocess_frame_.samples_per_channel_ = in_frame.samples_per_channel_; - preprocess_frame_.sample_rate_hz_ = in_frame.sample_rate_hz_; - // If it is required, we have to do a resampling. - if (resample) { - // The result of the resampler is written to output frame. - dest_ptr_audio = preprocess_frame_.data_; - - uint32_t timestamp_diff; - - // Calculate the timestamp of this frame. - if (last_in_timestamp_ > in_frame.timestamp_) { - // A wrap around has happened. - timestamp_diff = (static_cast(0xFFFFFFFF) - last_in_timestamp_) - + in_frame.timestamp_; - } else { - timestamp_diff = in_frame.timestamp_ - last_in_timestamp_; - } - preprocess_frame_.timestamp_ = last_timestamp_ + - static_cast(timestamp_diff * - (static_cast(send_codec_inst_.plfreq) / - static_cast(in_frame.sample_rate_hz_))); - - preprocess_frame_.samples_per_channel_ = input_resampler_.Resample10Msec( - src_ptr_audio, in_frame.sample_rate_hz_, dest_ptr_audio, - send_codec_inst_.plfreq, preprocess_frame_.num_channels_); - - if (preprocess_frame_.samples_per_channel_ < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot add 10 ms audio, resmapling failed"); - return -1; - } - preprocess_frame_.sample_rate_hz_ = send_codec_inst_.plfreq; - } - last_in_timestamp_ = in_frame.timestamp_; - last_timestamp_ = preprocess_frame_.timestamp_; - - return 0; -} - -///////////////////////////////////////// -// (FEC) Forward Error Correction -// - -bool AudioCodingModuleImpl::FECStatus() const { - CriticalSectionScoped lock(acm_crit_sect_); - return fec_enabled_; -} - -// Configure FEC status i.e on/off. -int32_t -AudioCodingModuleImpl::SetFECStatus( -#ifdef WEBRTC_CODEC_RED - const bool enable_fec) { - CriticalSectionScoped lock(acm_crit_sect_); - - if (fec_enabled_ != enable_fec) { - // Reset the RED buffer. - memset(red_buffer_, 0, MAX_PAYLOAD_SIZE_BYTE); - - // Reset fragmentation buffers. - ResetFragmentation(kNumFecFragmentationVectors); - // Set fec_enabled_. - fec_enabled_ = enable_fec; - } - is_first_red_ = true; // Make sure we restart FEC. - return 0; -#else - const bool /* enable_fec */) { - fec_enabled_ = false; - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, - " WEBRTC_CODEC_RED is undefined => fec_enabled_ = %d", - fec_enabled_); - return -1; -#endif -} - -///////////////////////////////////////// -// (VAD) Voice Activity Detection -// -int32_t AudioCodingModuleImpl::SetVAD(bool enable_dtx, bool enable_vad, - ACMVADMode mode) { - CriticalSectionScoped lock(acm_crit_sect_); - return SetVADSafe(enable_dtx, enable_vad, mode); -} - -int AudioCodingModuleImpl::SetVADSafe(bool enable_dtx, bool enable_vad, - ACMVADMode mode) { - // Sanity check of the mode. - if ((mode != VADNormal) && (mode != VADLowBitrate) - && (mode != VADAggr) && (mode != VADVeryAggr)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Invalid VAD Mode %d, no change is made to VAD/DTX status", - static_cast(mode)); - return -1; - } - - // Check that the send codec is mono. We don't support VAD/DTX for stereo - // sending. - if ((enable_dtx || enable_vad) && stereo_send_) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "VAD/DTX not supported for stereo sending."); - dtx_enabled_ = false; - vad_enabled_ = false; - vad_mode_ = mode; - return -1; - } - - // We don't support VAD/DTX when dual-streaming is enabled, i.e. - // secondary-encoder is registered. - if ((enable_dtx || enable_vad) && secondary_encoder_.get() != NULL) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "VAD/DTX not supported when dual-streaming is enabled."); - dtx_enabled_ = false; - vad_enabled_ = false; - vad_mode_ = mode; - return -1; - } - - // Store VAD/DTX settings. Values can be changed in the call to "SetVAD" - // below. - dtx_enabled_ = enable_dtx; - vad_enabled_ = enable_vad; - vad_mode_ = mode; - - // If a send codec is registered, set VAD/DTX for the codec. - if (HaveValidEncoder("SetVAD")) { - if (codecs_[current_send_codec_idx_]->SetVAD(&dtx_enabled_, &vad_enabled_, - &vad_mode_) < 0) { - // SetVAD failed. - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "SetVAD failed"); - dtx_enabled_ = false; - vad_enabled_ = false; - return -1; - } - } - - return 0; -} - -// Get VAD/DTX settings. -// TODO(tlegrand): Change this method to void. -int32_t AudioCodingModuleImpl::VAD(bool* dtx_enabled, bool* vad_enabled, - ACMVADMode* mode) const { - CriticalSectionScoped lock(acm_crit_sect_); - - *dtx_enabled = dtx_enabled_; - *vad_enabled = vad_enabled_; - *mode = vad_mode_; - - return 0; -} - -///////////////////////////////////////// -// Receiver -// - -int32_t AudioCodingModuleImpl::InitializeReceiver() { - CriticalSectionScoped lock(acm_crit_sect_); - return InitializeReceiverSafe(); -} - -// Initialize receiver, resets codec database etc. -int32_t AudioCodingModuleImpl::InitializeReceiverSafe() { - initial_delay_ms_ = 0; - num_packets_accumulated_ = 0; - num_bytes_accumulated_ = 0; - accumulated_audio_ms_ = 0; - first_payload_received_ = 0; - last_incoming_send_timestamp_ = 0; - track_neteq_buffer_ = false; - playout_ts_ = 0; - // If the receiver is already initialized then we want to destroy any - // existing decoders. After a call to this function, we should have a clean - // start-up. - if (receiver_initialized_) { - for (int i = 0; i < ACMCodecDB::kNumCodecs; i++) { - if (UnregisterReceiveCodecSafe(i) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "InitializeReceiver() failed, Could not unregister codec"); - return -1; - } - } - } - if (neteq_.Init() != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "InitializeReceiver() failed, Could not initialize NetEQ"); - return -1; - } - neteq_.set_id(id_); - if (neteq_.AllocatePacketBuffer(ACMCodecDB::NetEQDecoders(), - ACMCodecDB::kNumCodecs) != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "NetEQ cannot allocate_packet Buffer"); - return -1; - } - - // Register RED and CN. - for (int i = 0; i < ACMCodecDB::kNumCodecs; i++) { - if (IsCodecRED(i) || IsCodecCN(i)) { - if (RegisterRecCodecMSSafe(ACMCodecDB::database_[i], i, i, - ACMNetEQ::kMasterJb) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot register master codec."); - return -1; - } - registered_pltypes_[i] = ACMCodecDB::database_[i].pltype; - } - } - - receiver_initialized_ = true; - return 0; -} - -// Reset the decoder state. -int32_t AudioCodingModuleImpl::ResetDecoder() { - CriticalSectionScoped lock(acm_crit_sect_); - - for (int id = 0; id < ACMCodecDB::kMaxNumCodecs; id++) { - if ((codecs_[id] != NULL) && (registered_pltypes_[id] != -1)) { - if (codecs_[id]->ResetDecoder(registered_pltypes_[id]) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "ResetDecoder failed:"); - return -1; - } - } - } - return neteq_.FlushBuffers(); -} - -// Get current receive frequency. -int32_t AudioCodingModuleImpl::ReceiveFrequency() const { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, - "ReceiveFrequency()"); - WebRtcACMCodecParams codec_params; - - CriticalSectionScoped lock(acm_crit_sect_); - if (DecoderParamByPlType(last_recv_audio_codec_pltype_, codec_params) < 0) { - return neteq_.CurrentSampFreqHz(); - } else if (codec_params.codec_inst.plfreq == 48000) { - // TODO(tlegrand): Remove this option when we have full 48 kHz support. - return 32000; - } else { - return codec_params.codec_inst.plfreq; - } -} - -// Get current playout frequency. -int32_t AudioCodingModuleImpl::PlayoutFrequency() const { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, - "PlayoutFrequency()"); - - CriticalSectionScoped lock(acm_crit_sect_); - - return neteq_.CurrentSampFreqHz(); -} - -// Register possible receive codecs, can be called multiple times, -// for codecs, CNG (NB, WB and SWB), DTMF, RED. -int32_t AudioCodingModuleImpl::RegisterReceiveCodec( - const CodecInst& receive_codec) { - CriticalSectionScoped lock(acm_crit_sect_); - - if (receive_codec.channels > 2) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "More than 2 audio channel is not supported."); - return -1; - } - - int mirror_id; - int codec_id = ACMCodecDB::ReceiverCodecNumber(&receive_codec, &mirror_id); - - if (codec_id < 0 || codec_id >= ACMCodecDB::kNumCodecs) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Wrong codec params to be registered as receive codec"); - return -1; - } - // Check if the payload-type is valid. - if (!ACMCodecDB::ValidPayloadType(receive_codec.pltype)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Invalid payload-type %d for %s.", receive_codec.pltype, - receive_codec.plname); - return -1; - } - - if (!receiver_initialized_) { - if (InitializeReceiverSafe() < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot initialize reciver, so failed registering a codec."); - return -1; - } - } - - // If codec already registered, unregister. Except for CN where we only - // unregister if payload type is changing. - if ((registered_pltypes_[codec_id] == receive_codec.pltype) - && IsCodecCN(&receive_codec)) { - // Codec already registered as receiver with this payload type. Nothing - // to be done. - return 0; - } else if (registered_pltypes_[codec_id] != -1) { - if (UnregisterReceiveCodecSafe(codec_id) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot register master codec."); - return -1; - } - } - - if (RegisterRecCodecMSSafe(receive_codec, codec_id, mirror_id, - ACMNetEQ::kMasterJb) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot register master codec."); - return -1; - } - - // TODO(andrew): Refactor how the slave is initialized. Can we instead - // always start up a slave and pre-register CN and RED? We should be able - // to get rid of stereo_receive_registered_. - // http://code.google.com/p/webrtc/issues/detail?id=453 - - // Register stereo codecs with the slave, or, if we've had already seen a - // stereo codec, register CN or RED as a special case. - if (receive_codec.channels == 2 || - (stereo_receive_registered_ && (IsCodecCN(&receive_codec) || - IsCodecRED(&receive_codec)))) { - // TODO(andrew): refactor this block to combine with InitStereoSlave(). - - if (!stereo_receive_registered_) { - // This is the first time a stereo codec has been registered. Make - // some stereo preparations. - - // Add a stereo slave. - assert(neteq_.num_slaves() == 0); - if (neteq_.AddSlave(ACMCodecDB::NetEQDecoders(), - ACMCodecDB::kNumCodecs) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot add slave jitter buffer to NetEQ."); - return -1; - } - - // Register any existing CN or RED codecs with the slave and as stereo. - for (int i = 0; i < ACMCodecDB::kNumCodecs; i++) { - if (registered_pltypes_[i] != -1 && (IsCodecRED(i) || IsCodecCN(i))) { - stereo_receive_[i] = true; - - CodecInst codec; - memcpy(&codec, &ACMCodecDB::database_[i], sizeof(CodecInst)); - codec.pltype = registered_pltypes_[i]; - if (RegisterRecCodecMSSafe(codec, i, i, ACMNetEQ::kSlaveJb) < 0) { - WEBRTC_TRACE(kTraceError, kTraceAudioCoding, id_, - "Cannot register slave codec."); - return -1; - } - } - } - } - - if (RegisterRecCodecMSSafe(receive_codec, codec_id, mirror_id, - ACMNetEQ::kSlaveJb) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot register slave codec."); - return -1; - } - - if (!stereo_receive_[codec_id] && - (last_recv_audio_codec_pltype_ == receive_codec.pltype)) { - // The last received payload type is the same as the one we are - // registering. Expected number of channels to receive is one (mono), - // but we are now registering the receiving codec as stereo (number of - // channels is 2). - // Set |last_recv_audio_coded_pltype_| to invalid value to trigger a - // flush in NetEq, and a reset of expected number of channels next time a - // packet is received in AudioCodingModuleImpl::IncomingPacket(). - last_recv_audio_codec_pltype_ = -1; - } - - stereo_receive_[codec_id] = true; - stereo_receive_registered_ = true; - } else { - if (last_recv_audio_codec_pltype_ == receive_codec.pltype && - expected_channels_ == 2) { - // The last received payload type is the same as the one we are - // registering. Expected number of channels to receive is two (stereo), - // but we are now registering the receiving codec as mono (number of - // channels is 1). - // Set |last_recv_audio_coded_pl_type_| to invalid value to trigger a - // flush in NetEq, and a reset of expected number of channels next time a - // packet is received in AudioCodingModuleImpl::IncomingPacket(). - last_recv_audio_codec_pltype_ = -1; - } - stereo_receive_[codec_id] = false; - } - - registered_pltypes_[codec_id] = receive_codec.pltype; - - if (IsCodecRED(&receive_codec)) { - receive_red_pltype_ = receive_codec.pltype; - } - return 0; -} - -int32_t AudioCodingModuleImpl::RegisterRecCodecMSSafe( - const CodecInst& receive_codec, int16_t codec_id, - int16_t mirror_id, ACMNetEQ::JitterBuffer jitter_buffer) { - ACMGenericCodec** codecs; - if (jitter_buffer == ACMNetEQ::kMasterJb) { - codecs = &codecs_[0]; - } else if (jitter_buffer == ACMNetEQ::kSlaveJb) { - codecs = &slave_codecs_[0]; - if (codecs_[codec_id]->IsTrueStereoCodec()) { - // True stereo codecs need to use the same codec memory - // for both master and slave. - slave_codecs_[mirror_id] = codecs_[mirror_id]; - mirror_codec_idx_[mirror_id] = mirror_id; - } - } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "RegisterReceiveCodecMSSafe failed, jitter_buffer is neither " - "master or slave "); - return -1; - } - - if (codecs[mirror_id] == NULL) { - codecs[mirror_id] = CreateCodec(receive_codec); - if (codecs[mirror_id] == NULL) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot create codec to register as receive codec"); - return -1; - } - mirror_codec_idx_[mirror_id] = mirror_id; - } - if (mirror_id != codec_id) { - codecs[codec_id] = codecs[mirror_id]; - mirror_codec_idx_[codec_id] = mirror_id; - } - - codecs[codec_id]->SetIsMaster(jitter_buffer == ACMNetEQ::kMasterJb); - - int16_t status = 0; - WebRtcACMCodecParams codec_params; - memcpy(&(codec_params.codec_inst), &receive_codec, sizeof(CodecInst)); - codec_params.enable_vad = false; - codec_params.enable_dtx = false; - codec_params.vad_mode = VADNormal; - if (!codecs[codec_id]->DecoderInitialized()) { - // Force initialization. - status = codecs[codec_id]->InitDecoder(&codec_params, true); - if (status < 0) { - // Could not initialize the decoder, we don't want to - // continue if we could not initialize properly. - WEBRTC_TRACE( - webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "could not initialize the receive codec, codec not registered"); - - return -1; - } - } else if (mirror_id != codec_id) { - // Currently this only happens for iSAC. - // We have to store the decoder parameters. - codecs[codec_id]->SaveDecoderParam(&codec_params); - } - - if (codecs[codec_id]->RegisterInNetEq(&neteq_, receive_codec) != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Receive codec could not be registered in NetEQ"); - return -1; - } - // Guarantee that the same payload-type that is - // registered in NetEQ is stored in the codec. - codecs[codec_id]->SaveDecoderParam(&codec_params); - - return status; -} - -// Get current received codec. -int32_t AudioCodingModuleImpl::ReceiveCodec( - CodecInst* current_codec) const { - WebRtcACMCodecParams decoder_param; - CriticalSectionScoped lock(acm_crit_sect_); - - for (int id = 0; id < ACMCodecDB::kMaxNumCodecs; id++) { - if (codecs_[id] != NULL) { - if (codecs_[id]->DecoderInitialized()) { - if (codecs_[id]->DecoderParams(&decoder_param, - last_recv_audio_codec_pltype_)) { - memcpy(current_codec, &decoder_param.codec_inst, - sizeof(CodecInst)); - return 0; - } - } - } - } - - // If we are here then we haven't found any codec. Set codec pltype to -1 to - // indicate that the structure is invalid and return -1. - current_codec->pltype = -1; - return -1; -} - -// Incoming packet from network parsed and ready for decode. -int32_t AudioCodingModuleImpl::IncomingPacket( - const uint8_t* incoming_payload, - const int32_t payload_length, - const WebRtcRTPHeader& rtp_info) { - WebRtcRTPHeader rtp_header; - - memcpy(&rtp_header, &rtp_info, sizeof(WebRtcRTPHeader)); - - if (payload_length < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "IncomingPacket() Error, payload-length cannot be negative"); - return -1; - } - - { - // Store the payload Type. This will be used to retrieve "received codec" - // and "received frequency." - CriticalSectionScoped lock(acm_crit_sect_); - - // Check there are packets missed between the last injected packet, and the - // latest received packet. If so and we are in AV-sync mode then we would - // like to fill the gap. Shouldn't be the first payload. - if (av_sync_ && first_payload_received_ && - rtp_info.header.sequenceNumber > last_sequence_number_ + 1) { - // If the last packet pushed was sync-packet account for all missing - // packets. Otherwise leave some room for PLC. - if (last_packet_was_sync_) { - while (rtp_info.header.sequenceNumber > last_sequence_number_ + 2) { - PushSyncPacketSafe(); - } - } else { - // Leave two packet room for NetEq perform PLC. - if (rtp_info.header.sequenceNumber > last_sequence_number_ + 3) { - last_sequence_number_ += 2; - last_incoming_send_timestamp_ += last_timestamp_diff_ * 2; - last_receive_timestamp_ += 2 * last_timestamp_diff_; - while (rtp_info.header.sequenceNumber > last_sequence_number_ + 1) - PushSyncPacketSafe(); - } - } - } - - uint8_t my_payload_type; - - // Check if this is an RED payload. - if (rtp_info.header.payloadType == receive_red_pltype_) { - // Get the primary payload-type. - my_payload_type = incoming_payload[0] & 0x7F; - } else { - my_payload_type = rtp_info.header.payloadType; - } - - // If payload is audio, check if received payload is different from - // previous. - if (!rtp_info.type.Audio.isCNG) { - // This is Audio not CNG. - - if (my_payload_type != last_recv_audio_codec_pltype_) { - // We detect a change in payload type. It is necessary for iSAC - // we are going to use ONE iSAC instance for decoding both WB and - // SWB payloads. If payload is changed there might be a need to reset - // sampling rate of decoder. depending what we have received "now". - for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) { - if (registered_pltypes_[i] == my_payload_type) { - if (UpdateUponReceivingCodec(i) != 0) - return -1; - break; - } - } - // Codec is changed, there might be a jump in timestamp, therefore, - // we have to reset some variables that track NetEq buffer. - if (track_neteq_buffer_ || av_sync_) { - last_incoming_send_timestamp_ = rtp_info.header.timestamp; - } - - if (nack_enabled_) { - assert(nack_.get()); - // Codec is changed, reset NACK and update sampling rate. - nack_->Reset(); - nack_->UpdateSampleRate( - ACMCodecDB::database_[current_receive_codec_idx_].plfreq); - } - } - last_recv_audio_codec_pltype_ = my_payload_type; - } - - // Current timestamp based on the receiver sampling frequency. - last_receive_timestamp_ = NowTimestamp(current_receive_codec_idx_); - - if (nack_enabled_) { - assert(nack_.get()); - nack_->UpdateLastReceivedPacket(rtp_header.header.sequenceNumber, - rtp_header.header.timestamp); - } - } - - int per_neteq_payload_length = payload_length; - // Split the payload for stereo packets, so that first half of payload - // vector holds left channel, and second half holds right channel. - if (expected_channels_ == 2) { - if (!rtp_info.type.Audio.isCNG) { - // Create a new vector for the payload, maximum payload size. - int32_t length = payload_length; - uint8_t payload[kMaxPacketSize]; - assert(payload_length <= kMaxPacketSize); - memcpy(payload, incoming_payload, payload_length); - codecs_[current_receive_codec_idx_]->SplitStereoPacket(payload, &length); - rtp_header.type.Audio.channel = 2; - per_neteq_payload_length = length / 2; - // Insert packet into NetEQ. - if (neteq_.RecIn(payload, length, rtp_header, - last_receive_timestamp_) < 0) - return -1; - } else { - // If we receive a CNG packet while expecting stereo, we ignore the - // packet and continue. CNG is not supported for stereo. - return 0; - } - } else { - if (neteq_.RecIn(incoming_payload, payload_length, rtp_header, - last_receive_timestamp_) < 0) - return -1; - } - - { - CriticalSectionScoped lock(acm_crit_sect_); - - // Update buffering uses |last_incoming_send_timestamp_| so it should be - // before the next block. - if (track_neteq_buffer_) - UpdateBufferingSafe(rtp_header, per_neteq_payload_length); - - if (av_sync_) { - if (rtp_info.header.sequenceNumber == last_sequence_number_ + 1) { - last_timestamp_diff_ = rtp_info.header.timestamp - - last_incoming_send_timestamp_; - } - last_sequence_number_ = rtp_info.header.sequenceNumber; - last_ssrc_ = rtp_info.header.ssrc; - last_packet_was_sync_ = false; - } - - if (av_sync_ || track_neteq_buffer_) { - last_incoming_send_timestamp_ = rtp_info.header.timestamp; - } - - // Set the following regardless of tracking NetEq buffer or being in - // AV-sync mode. Only if the received packet is not CNG. - if (!rtp_info.type.Audio.isCNG) - first_payload_received_ = true; - } - return 0; -} - -int AudioCodingModuleImpl::UpdateUponReceivingCodec(int index) { - if (codecs_[index] == NULL) { - WEBRTC_TRACE(kTraceError, kTraceAudioCoding, id_, - "IncomingPacket() error: payload type found but " - "corresponding codec is NULL"); - return -1; - } - codecs_[index]->UpdateDecoderSampFreq(index); - neteq_.set_received_stereo(stereo_receive_[index]); - current_receive_codec_idx_ = index; - - // If we have a change in the expected number of channels, flush packet - // buffers in NetEQ. - if ((stereo_receive_[index] && (expected_channels_ == 1)) || - (!stereo_receive_[index] && (expected_channels_ == 2))) { - neteq_.FlushBuffers(); - codecs_[index]->ResetDecoder(registered_pltypes_[index]); - } - - if (stereo_receive_[index] && (expected_channels_ == 1)) { - // When switching from a mono to stereo codec reset the slave. - if (InitStereoSlave() != 0) - return -1; - } - - // Store number of channels we expect to receive for the current payload type. - if (stereo_receive_[index]) { - expected_channels_ = 2; - } else { - expected_channels_ = 1; - } - - // Reset previous received channel. - prev_received_channel_ = 0; - return 0; -} - -bool AudioCodingModuleImpl::IsCodecForSlave(int index) const { - return (registered_pltypes_[index] != -1 && stereo_receive_[index]); -} - -int AudioCodingModuleImpl::InitStereoSlave() { - neteq_.RemoveSlaves(); - - if (neteq_.AddSlave(ACMCodecDB::NetEQDecoders(), - ACMCodecDB::kNumCodecs) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot add slave jitter buffer to NetEQ."); - return -1; - } - - // Register all needed codecs with slave. - for (int i = 0; i < ACMCodecDB::kNumCodecs; i++) { - if (codecs_[i] != NULL && IsCodecForSlave(i)) { - WebRtcACMCodecParams decoder_params; - if (codecs_[i]->DecoderParams(&decoder_params, registered_pltypes_[i])) { - if (RegisterRecCodecMSSafe(decoder_params.codec_inst, - i, ACMCodecDB::MirrorID(i), - ACMNetEQ::kSlaveJb) < 0) { - WEBRTC_TRACE(kTraceError, kTraceAudioCoding, id_, - "Cannot register slave codec."); - return -1; - } - } - } - } - return 0; -} - -int AudioCodingModuleImpl::SetMinimumPlayoutDelay(int time_ms) { - { - CriticalSectionScoped lock(acm_crit_sect_); - // Don't let the extra delay modified while accumulating buffers in NetEq. - if (track_neteq_buffer_ && first_payload_received_) - return 0; - } - return neteq_.SetMinimumDelay(time_ms); -} - -int AudioCodingModuleImpl::SetMaximumPlayoutDelay(int time_ms) { - return neteq_.SetMaximumDelay(time_ms); -} - -// Get Dtmf playout status. -bool AudioCodingModuleImpl::DtmfPlayoutStatus() const { -#ifndef WEBRTC_CODEC_AVT - return false; -#else - return neteq_.avt_playout(); -#endif -} - -// Configure Dtmf playout status i.e on/off playout the incoming outband -// Dtmf tone. -int32_t AudioCodingModuleImpl::SetDtmfPlayoutStatus( -#ifndef WEBRTC_CODEC_AVT - const bool /* enable */) { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, - "SetDtmfPlayoutStatus() failed: AVT is not supported."); - return -1; -#else - const bool enable) { - return neteq_.SetAVTPlayout(enable); -#endif -} - -// Estimate the Bandwidth based on the incoming stream, needed for one way -// audio where the RTCP send the BW estimate. -// This is also done in the RTP module. -int32_t AudioCodingModuleImpl::DecoderEstimatedBandwidth() const { - CodecInst codec; - int16_t codec_id = -1; - int pltype_wb; - int pltype_swb; - - // Get iSAC settings. - for (int id = 0; id < ACMCodecDB::kNumCodecs; id++) { - // Store codec settings for codec number "codeCntr" in the output struct. - ACMCodecDB::Codec(id, &codec); - - if (!STR_CASE_CMP(codec.plname, "isac")) { - codec_id = 1; - pltype_wb = codec.pltype; - - ACMCodecDB::Codec(id + 1, &codec); - pltype_swb = codec.pltype; - - break; - } - } - - if (codec_id < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "DecoderEstimatedBandwidth failed"); - return -1; - } - - if ((last_recv_audio_codec_pltype_ == pltype_wb) || - (last_recv_audio_codec_pltype_ == pltype_swb)) { - return codecs_[codec_id]->GetEstimatedBandwidth(); - } else { - return -1; - } -} - -// Set playout mode for: voice, fax, or streaming. -int32_t AudioCodingModuleImpl::SetPlayoutMode( - const AudioPlayoutMode mode) { - if ((mode != voice) && (mode != fax) && (mode != streaming) && - (mode != off)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Invalid playout mode."); - return -1; - } - return neteq_.SetPlayoutMode(mode); -} - -// Get playout mode voice, fax. -AudioPlayoutMode AudioCodingModuleImpl::PlayoutMode() const { - return neteq_.playout_mode(); -} - -// Get 10 milliseconds of raw audio data to play out. -// Automatic resample to the requested frequency. -int32_t AudioCodingModuleImpl::PlayoutData10Ms( - int32_t desired_freq_hz, AudioFrame* audio_frame) { - TRACE_EVENT_ASYNC_BEGIN0("webrtc", "ACM::PlayoutData10Ms", this); - bool stereo_mode; - - if (GetSilence(desired_freq_hz, audio_frame)) { - TRACE_EVENT_ASYNC_END1("webrtc", "ACM::PlayoutData10Ms", this, - "silence", true); - return 0; // Silence is generated, return. - } - - // RecOut always returns 10 ms. - if (neteq_.RecOut(audio_frame_) != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "PlayoutData failed, RecOut Failed"); - return -1; - } - int decoded_seq_num; - uint32_t decoded_timestamp; - bool update_nack = - neteq_.DecodedRtpInfo(&decoded_seq_num, &decoded_timestamp) && - nack_enabled_; // Update NACK only if it is enabled. - audio_frame->num_channels_ = audio_frame_.num_channels_; - audio_frame->vad_activity_ = audio_frame_.vad_activity_; - audio_frame->speech_type_ = audio_frame_.speech_type_; - - stereo_mode = (audio_frame_.num_channels_ > 1); - - // For stereo playout: - // Master and Slave samples are interleaved starting with Master. - const uint16_t receive_freq = - static_cast(audio_frame_.sample_rate_hz_); - bool tone_detected = false; - int16_t last_detected_tone; - int16_t tone; - - // Limit the scope of ACM Critical section. - { - CriticalSectionScoped lock(acm_crit_sect_); - - // Update call statistics. - call_stats_.DecodedByNetEq(audio_frame->speech_type_); - - if (update_nack) { - assert(nack_.get()); - nack_->UpdateLastDecodedPacket(decoded_seq_num, decoded_timestamp); - } - - // If we are in AV-sync and have already received an audio packet, but the - // latest packet is too late, then insert sync packet. - if (av_sync_ && first_payload_received_ && - NowTimestamp(current_receive_codec_idx_) > 5 * last_timestamp_diff_ + - last_receive_timestamp_) { - if (!last_packet_was_sync_) { - // If the last packet inserted has been a regular packet Skip two - // packets to give room for PLC. - last_incoming_send_timestamp_ += 2 * last_timestamp_diff_; - last_sequence_number_ += 2; - last_receive_timestamp_ += 2 * last_timestamp_diff_; - } - - // One sync packet. - if (PushSyncPacketSafe() < 0) - return -1; - } - - if ((receive_freq != desired_freq_hz) && (desired_freq_hz != -1)) { - TRACE_EVENT_ASYNC_END2("webrtc", "ACM::PlayoutData10Ms", this, - "seqnum", decoded_seq_num, - "now", clock_->TimeInMilliseconds()); - // Resample payload_data. - int16_t temp_len = output_resampler_.Resample10Msec( - audio_frame_.data_, receive_freq, audio_frame->data_, - desired_freq_hz, audio_frame_.num_channels_); - - if (temp_len < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "PlayoutData failed, resampler failed"); - return -1; - } - - // Set the payload data length from the resampler. - audio_frame->samples_per_channel_ = static_cast(temp_len); - // Set the sampling frequency. - audio_frame->sample_rate_hz_ = desired_freq_hz; - } else { - TRACE_EVENT_ASYNC_END2("webrtc", "ACM::PlayoutData10Ms", this, - "seqnum", decoded_seq_num, - "now", clock_->TimeInMilliseconds()); - memcpy(audio_frame->data_, audio_frame_.data_, - audio_frame_.samples_per_channel_ * audio_frame->num_channels_ - * sizeof(int16_t)); - // Set the payload length. - audio_frame->samples_per_channel_ = - audio_frame_.samples_per_channel_; - // Set the sampling frequency. - audio_frame->sample_rate_hz_ = receive_freq; - } - - // Tone detection done for master channel. - if (dtmf_detector_ != NULL) { - // Dtmf Detection. - if (audio_frame->sample_rate_hz_ == 8000) { - // Use audio_frame->data_ then Dtmf detector doesn't - // need resampling. - if (!stereo_mode) { - dtmf_detector_->Detect(audio_frame->data_, - audio_frame->samples_per_channel_, - audio_frame->sample_rate_hz_, tone_detected, - tone); - } else { - // We are in 8 kHz so the master channel needs only 80 samples. - int16_t master_channel[80]; - for (int n = 0; n < 80; n++) { - master_channel[n] = audio_frame->data_[n << 1]; - } - dtmf_detector_->Detect(master_channel, - audio_frame->samples_per_channel_, - audio_frame->sample_rate_hz_, tone_detected, - tone); - } - } else { - // Do the detection on the audio that we got from NetEQ (audio_frame_). - if (!stereo_mode) { - dtmf_detector_->Detect(audio_frame_.data_, - audio_frame_.samples_per_channel_, - receive_freq, tone_detected, tone); - } else { - int16_t master_channel[WEBRTC_10MS_PCM_AUDIO]; - for (int n = 0; n < audio_frame_.samples_per_channel_; n++) { - master_channel[n] = audio_frame_.data_[n << 1]; - } - dtmf_detector_->Detect(master_channel, - audio_frame_.samples_per_channel_, - receive_freq, tone_detected, tone); - } - } - } - - // We want to do this while we are in acm_crit_sect_. - // (Doesn't really need to initialize the following - // variable but Linux complains if we don't.) - last_detected_tone = kACMToneEnd; - if (tone_detected) { - last_detected_tone = last_detected_tone_; - last_detected_tone_ = tone; - } - } - - if (tone_detected) { - // We will deal with callback here, so enter callback critical section. - CriticalSectionScoped lock(callback_crit_sect_); - - if (dtmf_callback_ != NULL) { - if (tone != kACMToneEnd) { - // just a tone - dtmf_callback_->IncomingDtmf(static_cast(tone), false); - } else if ((tone == kACMToneEnd) && (last_detected_tone != kACMToneEnd)) { - // The tone is "END" and the previously detected tone is - // not "END," so call fir an end. - dtmf_callback_->IncomingDtmf(static_cast(last_detected_tone), - true); - } - } - } - - audio_frame->id_ = id_; - audio_frame->energy_ = -1; - audio_frame->timestamp_ = 0; - - return 0; -} - -///////////////////////////////////////// -// Statistics -// - -int32_t AudioCodingModuleImpl::NetworkStatistics( - ACMNetworkStatistics* statistics) { - int32_t status; - status = neteq_.NetworkStatistics(statistics); - return status; -} - -void AudioCodingModuleImpl::DestructEncoderInst(void* inst) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, id_, - "DestructEncoderInst()"); - if (!HaveValidEncoder("DestructEncoderInst")) { - return; - } - - codecs_[current_send_codec_idx_]->DestructEncoderInst(inst); -} - -int16_t AudioCodingModuleImpl::AudioBuffer( - WebRtcACMAudioBuff& buffer) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, id_, - "AudioBuffer()"); - if (!HaveValidEncoder("AudioBuffer")) { - return -1; - } - buffer.last_in_timestamp = last_in_timestamp_; - return codecs_[current_send_codec_idx_]->AudioBuffer(buffer); -} - -int16_t AudioCodingModuleImpl::SetAudioBuffer( - WebRtcACMAudioBuff& buffer) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, id_, - "SetAudioBuffer()"); - if (!HaveValidEncoder("SetAudioBuffer")) { - return -1; - } - return codecs_[current_send_codec_idx_]->SetAudioBuffer(buffer); -} - -uint32_t AudioCodingModuleImpl::EarliestTimestamp() const { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, id_, - "EarliestTimestamp()"); - if (!HaveValidEncoder("EarliestTimestamp")) { - return -1; - } - return codecs_[current_send_codec_idx_]->EarliestTimestamp(); -} - -int32_t AudioCodingModuleImpl::RegisterVADCallback( - ACMVADCallback* vad_callback) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, id_, - "RegisterVADCallback()"); - CriticalSectionScoped lock(callback_crit_sect_); - vad_callback_ = vad_callback; - return 0; -} - -// TODO(turajs): Remove this API if it is not used. -// TODO(tlegrand): Modify this function to work for stereo, and add tests. -// TODO(turajs): Receive timestamp in this method is incremented by frame-size -// and does not reflect the true receive frame-size. Therefore, subsequent -// jitter computations are not accurate. -int32_t AudioCodingModuleImpl::IncomingPayload( - const uint8_t* incoming_payload, const int32_t payload_length, - const uint8_t payload_type, const uint32_t timestamp) { - if (payload_length < 0) { - // Log error in trace file. - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "IncomingPacket() Error, payload-length cannot be negative"); - return -1; - } - - if (dummy_rtp_header_ == NULL) { - // This is the first time that we are using |dummy_rtp_header_| - // so we have to create it. - WebRtcACMCodecParams codec_params; - dummy_rtp_header_ = new WebRtcRTPHeader; - if (dummy_rtp_header_ == NULL) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "IncomingPayload() Error, out of memory"); - return -1; - } - dummy_rtp_header_->header.payloadType = payload_type; - // Don't matter in this case. - dummy_rtp_header_->header.ssrc = 0; - dummy_rtp_header_->header.markerBit = false; - // Start with random numbers. - dummy_rtp_header_->header.sequenceNumber = rand(); - dummy_rtp_header_->header.timestamp = - (static_cast(rand()) << 16) + - static_cast(rand()); - dummy_rtp_header_->type.Audio.channel = 1; - - if (DecoderParamByPlType(payload_type, codec_params) < 0) { - // We didn't find a codec with the given payload. - // Something is wrong we exit, but we delete |dummy_rtp_header_| - // and set it to NULL to start clean next time. - delete dummy_rtp_header_; - dummy_rtp_header_ = NULL; - return -1; - } - recv_pl_frame_size_smpls_ = codec_params.codec_inst.pacsize; - } - - if (payload_type != dummy_rtp_header_->header.payloadType) { - // Payload type has changed since the last time we might need to - // update the frame-size. - WebRtcACMCodecParams codec_params; - if (DecoderParamByPlType(payload_type, codec_params) < 0) { - // We didn't find a codec with the given payload. - return -1; - } - recv_pl_frame_size_smpls_ = codec_params.codec_inst.pacsize; - dummy_rtp_header_->header.payloadType = payload_type; - } - - if (timestamp > 0) { - dummy_rtp_header_->header.timestamp = timestamp; - } - - // Store the payload Type. this will be used to retrieve "received codec" - // and "received frequency." - last_recv_audio_codec_pltype_ = payload_type; - - last_receive_timestamp_ += recv_pl_frame_size_smpls_; - // Insert in NetEQ. - if (neteq_.RecIn(incoming_payload, payload_length, *dummy_rtp_header_, - last_receive_timestamp_) < 0) { - return -1; - } - - // Get ready for the next payload. - dummy_rtp_header_->header.sequenceNumber++; - dummy_rtp_header_->header.timestamp += recv_pl_frame_size_smpls_; - return 0; -} - -int16_t AudioCodingModuleImpl::DecoderParamByPlType( - const uint8_t payload_type, - WebRtcACMCodecParams& codec_params) const { - CriticalSectionScoped lock(acm_crit_sect_); - for (int16_t id = 0; id < ACMCodecDB::kMaxNumCodecs; - id++) { - if (codecs_[id] != NULL) { - if (codecs_[id]->DecoderInitialized()) { - if (codecs_[id]->DecoderParams(&codec_params, payload_type)) { - return 0; - } - } - } - } - // If we are here it means that we could not find a - // codec with that payload type. reset the values to - // not acceptable values and return -1. - codec_params.codec_inst.plname[0] = '\0'; - codec_params.codec_inst.pacsize = 0; - codec_params.codec_inst.rate = 0; - codec_params.codec_inst.pltype = -1; - return -1; -} - -int16_t AudioCodingModuleImpl::DecoderListIDByPlName( - const char* name, const uint16_t frequency) const { - WebRtcACMCodecParams codec_params; - CriticalSectionScoped lock(acm_crit_sect_); - for (int16_t id = 0; id < ACMCodecDB::kMaxNumCodecs; id++) { - if ((codecs_[id] != NULL)) { - if (codecs_[id]->DecoderInitialized()) { - assert(registered_pltypes_[id] >= 0); - assert(registered_pltypes_[id] <= 255); - codecs_[id]->DecoderParams( - &codec_params, static_cast(registered_pltypes_[id])); - if (!STR_CASE_CMP(codec_params.codec_inst.plname, name)) { - // Check if the given sampling frequency matches. - // A zero sampling frequency means we matching the names - // is sufficient and we don't need to check for the - // frequencies. - // Currently it is only iSAC which has one name but two - // sampling frequencies. - if ((frequency == 0)|| - (codec_params.codec_inst.plfreq == frequency)) { - return id; - } - } - } - } - } - // If we are here it means that we could not find a - // codec with that payload type. return -1. - return -1; -} - -int32_t AudioCodingModuleImpl::LastEncodedTimestamp( - uint32_t& timestamp) const { - CriticalSectionScoped lock(acm_crit_sect_); - if (!HaveValidEncoder("LastEncodedTimestamp")) { - return -1; - } - timestamp = codecs_[current_send_codec_idx_]->LastEncodedTimestamp(); - return 0; -} - -int32_t AudioCodingModuleImpl::ReplaceInternalDTXWithWebRtc( - bool use_webrtc_dtx) { - CriticalSectionScoped lock(acm_crit_sect_); - - if (!HaveValidEncoder("ReplaceInternalDTXWithWebRtc")) { - WEBRTC_TRACE( - webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Cannot replace codec internal DTX when no send codec is registered."); - return -1; - } - - int32_t res = codecs_[current_send_codec_idx_]->ReplaceInternalDTX( - use_webrtc_dtx); - // Check if VAD is turned on, or if there is any error. - if (res == 1) { - vad_enabled_ = true; - } else if (res < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Failed to set ReplaceInternalDTXWithWebRtc(%d)", - use_webrtc_dtx); - return res; - } - - return 0; -} - -int32_t AudioCodingModuleImpl::IsInternalDTXReplacedWithWebRtc( - bool* uses_webrtc_dtx) { - CriticalSectionScoped lock(acm_crit_sect_); - - if (!HaveValidEncoder("IsInternalDTXReplacedWithWebRtc")) { - return -1; - } - if (codecs_[current_send_codec_idx_]->IsInternalDTXReplaced(uses_webrtc_dtx) - < 0) { - return -1; - } - return 0; -} - -int AudioCodingModuleImpl::SetISACMaxRate(int max_bit_per_sec) { - CriticalSectionScoped lock(acm_crit_sect_); - - if (!HaveValidEncoder("SetISACMaxRate")) { - return -1; - } - - return codecs_[current_send_codec_idx_]->SetISACMaxRate(max_bit_per_sec); -} - -int AudioCodingModuleImpl::SetISACMaxPayloadSize(int max_size_bytes) { - CriticalSectionScoped lock(acm_crit_sect_); - - if (!HaveValidEncoder("SetISACMaxPayloadSize")) { - return -1; - } - - return codecs_[current_send_codec_idx_]->SetISACMaxPayloadSize( - max_size_bytes); -} - -int32_t AudioCodingModuleImpl::ConfigISACBandwidthEstimator( - int frame_size_ms, - int rate_bit_per_sec, - bool enforce_frame_size) { - CriticalSectionScoped lock(acm_crit_sect_); - - if (!HaveValidEncoder("ConfigISACBandwidthEstimator")) { - return -1; - } - - return codecs_[current_send_codec_idx_]->ConfigISACBandwidthEstimator( - frame_size_ms, rate_bit_per_sec, enforce_frame_size); -} - -int32_t AudioCodingModuleImpl::PlayoutTimestamp( - uint32_t* timestamp) { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, - "PlayoutTimestamp()"); - { - CriticalSectionScoped lock(acm_crit_sect_); - if (track_neteq_buffer_) { - *timestamp = playout_ts_; - return 0; - } - } - return neteq_.PlayoutTimestamp(*timestamp); -} - -bool AudioCodingModuleImpl::HaveValidEncoder(const char* caller_name) const { - if ((!send_codec_registered_) || (current_send_codec_idx_ < 0) || - (current_send_codec_idx_ >= ACMCodecDB::kNumCodecs)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "%s failed: No send codec is registered.", caller_name); - return false; - } - if ((current_send_codec_idx_ < 0) || - (current_send_codec_idx_ >= ACMCodecDB::kNumCodecs)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "%s failed: Send codec index out of range.", caller_name); - return false; - } - if (codecs_[current_send_codec_idx_] == NULL) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "%s failed: Send codec is NULL pointer.", caller_name); - return false; - } - return true; -} - -int AudioCodingModuleImpl::UnregisterReceiveCodec(uint8_t payload_type) { - CriticalSectionScoped lock(acm_crit_sect_); - int id; - - // Search through the list of registered payload types. - for (id = 0; id < ACMCodecDB::kMaxNumCodecs; id++) { - if (registered_pltypes_[id] == payload_type) { - // We have found the id registered with the payload type. - break; - } - } - - if (id >= ACMCodecDB::kNumCodecs) { - // Payload type was not registered. No need to unregister. - return 0; - } - - // Unregister the codec with the given payload type. - return UnregisterReceiveCodecSafe(id); -} - -int32_t AudioCodingModuleImpl::UnregisterReceiveCodecSafe( - const int16_t codec_id) { - const WebRtcNetEQDecoder *neteq_decoder = ACMCodecDB::NetEQDecoders(); - int16_t mirror_id = ACMCodecDB::MirrorID(codec_id); - bool stereo_receiver = false; - - if (codecs_[codec_id] != NULL) { - if (registered_pltypes_[codec_id] != -1) { - // Store stereo information for future use. - stereo_receiver = stereo_receive_[codec_id]; - - // Before deleting the decoder instance unregister from NetEQ. - if (neteq_.RemoveCodec(neteq_decoder[codec_id], - stereo_receive_[codec_id]) < 0) { - CodecInst codec; - ACMCodecDB::Codec(codec_id, &codec); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, id_, - "Unregistering %s-%d from NetEQ failed.", codec.plname, - codec.plfreq); - return -1; - } - - // CN is a special case for NetEQ, all three sampling frequencies - // are unregistered if one is deleted. - if (IsCodecCN(codec_id)) { - for (int i = 0; i < ACMCodecDB::kNumCodecs; i++) { - if (IsCodecCN(i)) { - stereo_receive_[i] = false; - registered_pltypes_[i] = -1; - } - } - } else { - if (codec_id == mirror_id) { - codecs_[codec_id]->DestructDecoder(); - if (stereo_receive_[codec_id]) { - slave_codecs_[codec_id]->DestructDecoder(); - stereo_receive_[codec_id] = false; - } - } - } - - // Check if this is the last registered stereo receive codec. - if (stereo_receiver) { - bool no_stereo = true; - - for (int i = 0; i < ACMCodecDB::kNumCodecs; i++) { - if (stereo_receive_[i]) { - // We still have stereo codecs registered. - no_stereo = false; - break; - } - } - - // If we don't have any stereo codecs left, change status. - if (no_stereo) { - neteq_.RemoveSlaves(); // No longer need the slave. - stereo_receive_registered_ = false; - } - } - } - } - - if (registered_pltypes_[codec_id] == receive_red_pltype_) { - // RED is going to be unregistered, set to an invalid value. - receive_red_pltype_ = 255; - } - registered_pltypes_[codec_id] = -1; - - return 0; -} - -int32_t AudioCodingModuleImpl::REDPayloadISAC( - const int32_t isac_rate, const int16_t isac_bw_estimate, - uint8_t* payload, int16_t* length_bytes) { - if (!HaveValidEncoder("EncodeData")) { - return -1; - } - int16_t status; - status = codecs_[current_send_codec_idx_]->REDPayloadISAC(isac_rate, - isac_bw_estimate, - payload, - length_bytes); - return status; -} - -void AudioCodingModuleImpl::ResetFragmentation(int vector_size) { - for (int n = 0; n < kMaxNumFragmentationVectors; n++) { - fragmentation_.fragmentationOffset[n] = n * MAX_PAYLOAD_SIZE_BYTE; - } - memset(fragmentation_.fragmentationLength, 0, kMaxNumFragmentationVectors * - sizeof(fragmentation_.fragmentationLength[0])); - memset(fragmentation_.fragmentationTimeDiff, 0, kMaxNumFragmentationVectors * - sizeof(fragmentation_.fragmentationTimeDiff[0])); - memset(fragmentation_.fragmentationPlType, 0, kMaxNumFragmentationVectors * - sizeof(fragmentation_.fragmentationPlType[0])); - fragmentation_.fragmentationVectorSize = - static_cast(vector_size); -} - -// TODO(turajs): Add second parameter to enable/disable AV-sync. -int AudioCodingModuleImpl::SetInitialPlayoutDelay(int delay_ms) { - if (delay_ms < 0 || delay_ms > 10000) { - return -1; - } - - CriticalSectionScoped lock(acm_crit_sect_); - - // Receiver should be initialized before this call processed. - if (!receiver_initialized_) { - InitializeReceiverSafe(); - } - - if (first_payload_received_) { - // Too late for this API. Only works before a call is started. - return -1; - } - initial_delay_ms_ = delay_ms; - - // If initial delay is zero, NetEq buffer should not be tracked, also we - // don't want to be in AV-sync mode. - track_neteq_buffer_ = delay_ms > 0; - av_sync_ = delay_ms > 0; - - neteq_.EnableAVSync(av_sync_); - return neteq_.SetMinimumDelay(delay_ms); -} - -bool AudioCodingModuleImpl::GetSilence(int desired_sample_rate_hz, - AudioFrame* frame) { - CriticalSectionScoped lock(acm_crit_sect_); - if (initial_delay_ms_ == 0 || !track_neteq_buffer_) { - return false; - } - - if (accumulated_audio_ms_ >= initial_delay_ms_) { - // We have enough data stored that match our initial delay target. - track_neteq_buffer_ = false; - return false; - } - - // Record call to silence generator. - call_stats_.DecodedBySilenceGenerator(); - - // We stop accumulating packets, if the number of packets or the total size - // exceeds a threshold. - int max_num_packets; - int buffer_size_bytes; - int per_payload_overhead_bytes; - neteq_.BufferSpec(max_num_packets, buffer_size_bytes, - per_payload_overhead_bytes); - int total_bytes_accumulated = num_bytes_accumulated_ + - num_packets_accumulated_ * per_payload_overhead_bytes; - if (num_packets_accumulated_ > max_num_packets * 0.9 || - total_bytes_accumulated > buffer_size_bytes * 0.9) { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, - "GetSilence: Initial delay couldn't be achieved." - " num_packets_accumulated=%d, total_bytes_accumulated=%d", - num_packets_accumulated_, num_bytes_accumulated_); - track_neteq_buffer_ = false; - return false; - } - - if (desired_sample_rate_hz > 0) { - frame->sample_rate_hz_ = desired_sample_rate_hz; - } else { - frame->sample_rate_hz_ = 0; - if (current_receive_codec_idx_ >= 0) { - frame->sample_rate_hz_ = - ACMCodecDB::database_[current_receive_codec_idx_].plfreq; - } else { - // No payload received yet, use the default sampling rate of NetEq. - frame->sample_rate_hz_ = neteq_.CurrentSampFreqHz(); - } - } - frame->num_channels_ = expected_channels_; - frame->samples_per_channel_ = frame->sample_rate_hz_ / 100; // Always 10 ms. - frame->speech_type_ = AudioFrame::kCNG; - frame->vad_activity_ = AudioFrame::kVadPassive; - frame->energy_ = 0; - int samples = frame->samples_per_channel_ * frame->num_channels_; - memset(frame->data_, 0, samples * sizeof(int16_t)); - return true; -} - -// Must be called within the scope of ACM critical section. -int AudioCodingModuleImpl::PushSyncPacketSafe() { - assert(av_sync_); - last_sequence_number_++; - last_incoming_send_timestamp_ += last_timestamp_diff_; - last_receive_timestamp_ += last_timestamp_diff_; - - WebRtcRTPHeader rtp_info; - rtp_info.header.payloadType = last_recv_audio_codec_pltype_; - rtp_info.header.ssrc = last_ssrc_; - rtp_info.header.markerBit = false; - rtp_info.header.sequenceNumber = last_sequence_number_; - rtp_info.header.timestamp = last_incoming_send_timestamp_; - rtp_info.type.Audio.channel = stereo_receive_[current_receive_codec_idx_] ? - 2 : 1; - last_packet_was_sync_ = true; - int payload_len_bytes = neteq_.RecIn(rtp_info, last_receive_timestamp_); - - if (payload_len_bytes < 0) - return -1; - - // This is to account for sync packets inserted during the buffering phase. - if (track_neteq_buffer_) - UpdateBufferingSafe(rtp_info, payload_len_bytes); - - return 0; -} - -// Must be called within the scope of ACM critical section. -void AudioCodingModuleImpl::UpdateBufferingSafe(const WebRtcRTPHeader& rtp_info, - int payload_len_bytes) { - const int in_sample_rate_khz = - (ACMCodecDB::database_[current_receive_codec_idx_].plfreq / 1000); - if (first_payload_received_ && - rtp_info.header.timestamp > last_incoming_send_timestamp_ && - in_sample_rate_khz > 0) { - accumulated_audio_ms_ += (rtp_info.header.timestamp - - last_incoming_send_timestamp_) / in_sample_rate_khz; - } - - num_packets_accumulated_++; - num_bytes_accumulated_ += payload_len_bytes; - - playout_ts_ = static_cast( - rtp_info.header.timestamp - static_cast( - initial_delay_ms_ * in_sample_rate_khz)); -} - -uint32_t AudioCodingModuleImpl::NowTimestamp(int codec_id) { - // Down-cast the time to (32-6)-bit since we only care about - // the least significant bits. (32-6) bits cover 2^(32-6) = 67108864 ms. - // we masked 6 most significant bits of 32-bit so we don't lose resolution - // when do the following multiplication. - int sample_rate_khz = ACMCodecDB::database_[codec_id].plfreq / 1000; - const uint32_t now_in_ms = static_cast( - clock_->TimeInMilliseconds() & kMaskTimestamp); - return static_cast(sample_rate_khz * now_in_ms); -} - -std::vector AudioCodingModuleImpl::GetNackList( - int round_trip_time_ms) const { - CriticalSectionScoped lock(acm_crit_sect_); - if (round_trip_time_ms < 0) { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, id_, - "GetNackList: round trip time cannot be negative." - " round_trip_time_ms=%d", round_trip_time_ms); - } - if (nack_enabled_ && round_trip_time_ms >= 0) { - assert(nack_.get()); - return nack_->GetNackList(round_trip_time_ms); - } - std::vector empty_list; - return empty_list; -} - -int AudioCodingModuleImpl::LeastRequiredDelayMs() const { - return std::max(neteq_.LeastRequiredDelayMs(), initial_delay_ms_); -} - -int AudioCodingModuleImpl::EnableNack(size_t max_nack_list_size) { - // Don't do anything if |max_nack_list_size| is out of range. - if (max_nack_list_size == 0 || - max_nack_list_size > acm2::Nack::kNackListSizeLimit) - return -1; - - CriticalSectionScoped lock(acm_crit_sect_); - if (!nack_enabled_) { - nack_.reset(acm2::Nack::Create(kNackThresholdPackets)); - nack_enabled_ = true; - - // Sampling rate might need to be updated if we change from disable to - // enable. Do it if the receive codec is valid. - if (current_receive_codec_idx_ >= 0) { - nack_->UpdateSampleRate( - ACMCodecDB::database_[current_receive_codec_idx_].plfreq); - } - } - return nack_->SetMaxNackListSize(max_nack_list_size); -} - -void AudioCodingModuleImpl::DisableNack() { - CriticalSectionScoped lock(acm_crit_sect_); - nack_.reset(); // Memory is released. - nack_enabled_ = false; -} - -const char* AudioCodingModuleImpl::Version() const { - return kLegacyAcmVersion; -} - -void AudioCodingModuleImpl::GetDecodingCallStatistics( - AudioDecodingCallStats* call_stats) const { - CriticalSectionScoped lock(acm_crit_sect_); - *call_stats = call_stats_.GetDecodingStatistics(); -} - -} // namespace acm1 - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,455 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_AUDIO_CODING_MODULE_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_AUDIO_CODING_MODULE_IMPL_H_ - -#include - -#include "webrtc/common_types.h" -#include "webrtc/engine_configurations.h" -#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" -#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" -#include "webrtc/modules/audio_coding/main/source/acm_neteq.h" -#include "webrtc/modules/audio_coding/main/source/acm_resampler.h" -#include "webrtc/modules/audio_coding/main/acm2/call_statistics.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" - -namespace webrtc { - -struct WebRtcACMAudioBuff; -struct WebRtcACMCodecParams; -class CriticalSectionWrapper; -class RWLockWrapper; -class Clock; - -namespace acm2 { -class Nack; -} - -namespace acm1 { - -class ACMDTMFDetection; -class ACMGenericCodec; - -class AudioCodingModuleImpl : public AudioCodingModule { - public: - AudioCodingModuleImpl(const int32_t id, Clock* clock); - ~AudioCodingModuleImpl(); - - virtual const char* Version() const; - - // Change the unique identifier of this object. - virtual int32_t ChangeUniqueId(const int32_t id); - - // Returns the number of milliseconds until the module want a worker thread - // to call Process. - int32_t TimeUntilNextProcess(); - - // Process any pending tasks such as timeouts. - int32_t Process(); - - ///////////////////////////////////////// - // Sender - // - - // Initialize send codec. - int32_t InitializeSender(); - - // Reset send codec. - int32_t ResetEncoder(); - - // Can be called multiple times for Codec, CNG, RED. - int32_t RegisterSendCodec(const CodecInst& send_codec); - - // Register Secondary codec for dual-streaming. Dual-streaming is activated - // right after the secondary codec is registered. - int RegisterSecondarySendCodec(const CodecInst& send_codec); - - // Unregister the secondary codec. Dual-streaming is deactivated right after - // deregistering secondary codec. - void UnregisterSecondarySendCodec(); - - // Get the secondary codec. - int SecondarySendCodec(CodecInst* secondary_codec) const; - - // Get current send codec. - int32_t SendCodec(CodecInst* current_codec) const; - - // Get current send frequency. - int32_t SendFrequency() const; - - // Get encode bit-rate. - // Adaptive rate codecs return their current encode target rate, while other - // codecs return there long-term average or their fixed rate. - int32_t SendBitrate() const; - - // Set available bandwidth, inform the encoder about the - // estimated bandwidth received from the remote party. - virtual int32_t SetReceivedEstimatedBandwidth(const int32_t bw); - - // Register a transport callback which will be - // called to deliver the encoded buffers. - int32_t RegisterTransportCallback(AudioPacketizationCallback* transport); - - // Add 10 ms of raw (PCM) audio data to the encoder. - int32_t Add10MsData(const AudioFrame& audio_frame); - - ///////////////////////////////////////// - // (FEC) Forward Error Correction - // - - // Configure FEC status i.e on/off. - int32_t SetFECStatus(const bool enable_fec); - - // Get FEC status. - bool FECStatus() const; - - ///////////////////////////////////////// - // (VAD) Voice Activity Detection - // and - // (CNG) Comfort Noise Generation - // - - int32_t SetVAD(bool enable_dtx = true, - bool enable_vad = false, - ACMVADMode mode = VADNormal); - - int32_t VAD(bool* dtx_enabled, bool* vad_enabled, ACMVADMode* mode) const; - - int32_t RegisterVADCallback(ACMVADCallback* vad_callback); - - ///////////////////////////////////////// - // Receiver - // - - // Initialize receiver, resets codec database etc. - int32_t InitializeReceiver(); - - // Reset the decoder state. - int32_t ResetDecoder(); - - // Get current receive frequency. - int32_t ReceiveFrequency() const; - - // Get current playout frequency. - int32_t PlayoutFrequency() const; - - // Register possible receive codecs, can be called multiple times, - // for codecs, CNG, DTMF, RED. - int32_t RegisterReceiveCodec(const CodecInst& receive_codec); - - // Get current received codec. - int32_t ReceiveCodec(CodecInst* current_codec) const; - - // Incoming packet from network parsed and ready for decode. - int32_t IncomingPacket(const uint8_t* incoming_payload, - const int32_t payload_length, - const WebRtcRTPHeader& rtp_info); - - // Incoming payloads, without rtp-info, the rtp-info will be created in ACM. - // One usage for this API is when pre-encoded files are pushed in ACM. - int32_t IncomingPayload(const uint8_t* incoming_payload, - const int32_t payload_length, - const uint8_t payload_type, - const uint32_t timestamp = 0); - - // NetEq minimum playout delay (used for lip-sync). The actual target delay - // is the max of |time_ms| and the required delay dictated by the channel. - int SetMinimumPlayoutDelay(int time_ms); - - // NetEq maximum playout delay. The actual target delay is the min of - // |time_ms| and the required delay dictated by the channel. - int SetMaximumPlayoutDelay(int time_ms); - - // The shortest latency, in milliseconds, required by jitter buffer. This - // is computed based on inter-arrival times and playout mode of NetEq. The - // actual delay is the maximum of least-required-delay and the minimum-delay - // specified by SetMinumumPlayoutDelay() API. - // - int LeastRequiredDelayMs() const ; - - // Configure Dtmf playout status i.e on/off playout the incoming outband Dtmf - // tone. - int32_t SetDtmfPlayoutStatus(const bool enable); - - // Get Dtmf playout status. - bool DtmfPlayoutStatus() const; - - // Estimate the Bandwidth based on the incoming stream, needed - // for one way audio where the RTCP send the BW estimate. - // This is also done in the RTP module . - int32_t DecoderEstimatedBandwidth() const; - - // Set playout mode voice, fax. - int32_t SetPlayoutMode(const AudioPlayoutMode mode); - - // Get playout mode voice, fax. - AudioPlayoutMode PlayoutMode() const; - - // Get playout timestamp. - int32_t PlayoutTimestamp(uint32_t* timestamp); - - // Get 10 milliseconds of raw audio data to play out, and - // automatic resample to the requested frequency if > 0. - int32_t PlayoutData10Ms(int32_t desired_freq_hz, - AudioFrame* audio_frame); - - ///////////////////////////////////////// - // Statistics - // - - int32_t NetworkStatistics(ACMNetworkStatistics* statistics); - - void DestructEncoderInst(void* inst); - - int16_t AudioBuffer(WebRtcACMAudioBuff& buffer); - - // GET RED payload for iSAC. The method id called when 'this' ACM is - // the default ACM. - int32_t REDPayloadISAC(const int32_t isac_rate, - const int16_t isac_bw_estimate, - uint8_t* payload, - int16_t* length_bytes); - - int16_t SetAudioBuffer(WebRtcACMAudioBuff& buffer); - - uint32_t EarliestTimestamp() const; - - int32_t LastEncodedTimestamp(uint32_t& timestamp) const; - - int32_t ReplaceInternalDTXWithWebRtc(const bool use_webrtc_dtx); - - int32_t IsInternalDTXReplacedWithWebRtc(bool* uses_webrtc_dtx); - - int SetISACMaxRate(int max_bit_per_sec); - - int SetISACMaxPayloadSize(int max_size_bytes); - - int32_t ConfigISACBandwidthEstimator( - int frame_size_ms, - int rate_bit_per_sec, - bool enforce_frame_size = false); - - int UnregisterReceiveCodec(uint8_t payload_type); - - std::vector GetNackList(int round_trip_time_ms) const; - - protected: - void UnregisterSendCodec(); - - int32_t UnregisterReceiveCodecSafe(const int16_t id); - - ACMGenericCodec* CreateCodec(const CodecInst& codec); - - int16_t DecoderParamByPlType(const uint8_t payload_type, - WebRtcACMCodecParams& codec_params) const; - - int16_t DecoderListIDByPlName( - const char* name, const uint16_t frequency = 0) const; - - int32_t InitializeReceiverSafe(); - - bool HaveValidEncoder(const char* caller_name) const; - - int32_t RegisterRecCodecMSSafe(const CodecInst& receive_codec, - int16_t codec_id, - int16_t mirror_id, - ACMNetEQ::JitterBuffer jitter_buffer); - - // Set VAD/DTX status. This function does not acquire a lock, and it is - // created to be called only from inside a critical section. - int SetVADSafe(bool enable_dtx, bool enable_vad, ACMVADMode mode); - - // Process buffered audio when dual-streaming is not enabled (When RED is - // enabled still this function is used.) - int ProcessSingleStream(); - - // Process buffered audio when dual-streaming is enabled, i.e. secondary send - // codec is registered. - int ProcessDualStream(); - - // Preprocessing of input audio, including resampling and down-mixing if - // required, before pushing audio into encoder's buffer. - // - // in_frame: input audio-frame - // ptr_out: pointer to output audio_frame. If no preprocessing is required - // |ptr_out| will be pointing to |in_frame|, otherwise pointing to - // |preprocess_frame_|. - // - // Return value: - // -1: if encountering an error. - // 0: otherwise. - int PreprocessToAddData(const AudioFrame& in_frame, - const AudioFrame** ptr_out); - - // Set initial playout delay. - // -delay_ms: delay in millisecond. - // - // Return value: - // -1: if cannot set the delay. - // 0: if delay set successfully. - int SetInitialPlayoutDelay(int delay_ms); - - // Enable NACK and set the maximum size of the NACK list. - int EnableNack(size_t max_nack_list_size); - - // Disable NACK. - void DisableNack(); - - void GetDecodingCallStatistics(AudioDecodingCallStats* call_stats) const; - - private: - // Change required states after starting to receive the codec corresponding - // to |index|. - int UpdateUponReceivingCodec(int index); - - // Remove all slaves and initialize a stereo slave with required codecs - // from the master. - int InitStereoSlave(); - - // Returns true if the codec's |index| is registered with the master and - // is a stereo codec, RED or CN. - bool IsCodecForSlave(int index) const; - - int EncodeFragmentation(int fragmentation_index, int payload_type, - uint32_t current_timestamp, - ACMGenericCodec* encoder, - uint8_t* stream); - - void ResetFragmentation(int vector_size); - - bool GetSilence(int desired_sample_rate_hz, AudioFrame* frame); - - // Push a synchronization packet into NetEq. Such packets result in a frame - // of zeros (not decoded by the corresponding decoder). The size of the frame - // is the same as last decoding. NetEq has a special payload for this. - // Call within the scope of ACM critical section. - int PushSyncPacketSafe(); - - // Update the parameters required in initial phase of buffering, when - // initial playout delay is requested. Call within the scope of ACM critical - // section. - void UpdateBufferingSafe(const WebRtcRTPHeader& rtp_info, - int payload_len_bytes); - - // - // Return the timestamp of current time, computed according to sampling rate - // of the codec identified by |codec_id|. - // - uint32_t NowTimestamp(int codec_id); - - AudioPacketizationCallback* packetization_callback_; - int32_t id_; - uint32_t last_timestamp_; - uint32_t last_in_timestamp_; - CodecInst send_codec_inst_; - uint8_t cng_nb_pltype_; - uint8_t cng_wb_pltype_; - uint8_t cng_swb_pltype_; - uint8_t cng_fb_pltype_; - uint8_t red_pltype_; - bool vad_enabled_; - bool dtx_enabled_; - ACMVADMode vad_mode_; - ACMGenericCodec* codecs_[ACMCodecDB::kMaxNumCodecs]; - ACMGenericCodec* slave_codecs_[ACMCodecDB::kMaxNumCodecs]; - int16_t mirror_codec_idx_[ACMCodecDB::kMaxNumCodecs]; - bool stereo_receive_[ACMCodecDB::kMaxNumCodecs]; - bool stereo_receive_registered_; - bool stereo_send_; - int prev_received_channel_; - int expected_channels_; - int32_t current_send_codec_idx_; - int current_receive_codec_idx_; - bool send_codec_registered_; - ACMResampler input_resampler_; - ACMResampler output_resampler_; - ACMNetEQ neteq_; - CriticalSectionWrapper* acm_crit_sect_; - ACMVADCallback* vad_callback_; - uint8_t last_recv_audio_codec_pltype_; - - // RED/FEC. - bool is_first_red_; - bool fec_enabled_; - // TODO(turajs): |red_buffer_| is allocated in constructor, why having them - // as pointers and not an array. If concerned about the memory, then make a - // set-up function to allocate them only when they are going to be used, i.e. - // FEC or Dual-streaming is enabled. - uint8_t* red_buffer_; - // TODO(turajs): we actually don't need |fragmentation_| as a member variable. - // It is sufficient to keep the length & payload type of previous payload in - // member variables. - RTPFragmentationHeader fragmentation_; - uint32_t last_fec_timestamp_; - // If no RED is registered as receive codec this - // will have an invalid value. - uint8_t receive_red_pltype_; - - // This is to keep track of CN instances where we can send DTMFs. - uint8_t previous_pltype_; - - // This keeps track of payload types associated with codecs_[]. - // We define it as signed variable and initialize with -1 to indicate - // unused elements. - int16_t registered_pltypes_[ACMCodecDB::kMaxNumCodecs]; - - // Used when payloads are pushed into ACM without any RTP info - // One example is when pre-encoded bit-stream is pushed from - // a file. - WebRtcRTPHeader* dummy_rtp_header_; - uint16_t recv_pl_frame_size_smpls_; - - bool receiver_initialized_; - ACMDTMFDetection* dtmf_detector_; - - AudioCodingFeedback* dtmf_callback_; - int16_t last_detected_tone_; - CriticalSectionWrapper* callback_crit_sect_; - - AudioFrame audio_frame_; - AudioFrame preprocess_frame_; - CodecInst secondary_send_codec_inst_; - scoped_ptr secondary_encoder_; - - // Initial delay. - int initial_delay_ms_; - int num_packets_accumulated_; - int num_bytes_accumulated_; - int accumulated_audio_ms_; - int first_payload_received_; - uint32_t last_incoming_send_timestamp_; - bool track_neteq_buffer_; - uint32_t playout_ts_; - - // AV-sync is enabled. In AV-sync mode, sync packet pushed during long packet - // losses. - bool av_sync_; - - // Latest send timestamp difference of two consecutive packets. - uint32_t last_timestamp_diff_; - uint16_t last_sequence_number_; - uint32_t last_ssrc_; - bool last_packet_was_sync_; - int64_t last_receive_timestamp_; - - Clock* clock_; - scoped_ptr nack_; - bool nack_enabled_; - - acm2::CallStatistics call_stats_; -}; - -} // namespace acm1 - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_SOURCE_AUDIO_CODING_MODULE_IMPL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/ACMTest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/ACMTest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/ACMTest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/ACMTest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "ACMTest.h" - -ACMTest::~ACMTest() {} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/ACMTest.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/ACMTest.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/ACMTest.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/ACMTest.h 2015-02-03 14:33:34.000000000 +0000 @@ -14,7 +14,7 @@ class ACMTest { public: ACMTest() {} - virtual ~ACMTest() = 0; + virtual ~ACMTest() {} virtual void Perform() = 0; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/APITest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/APITest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/APITest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/APITest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -56,8 +56,8 @@ } APITest::APITest(const Config& config) - : _acmA(config.Get().Create(1)), - _acmB(config.Get().Create(2)), + : _acmA(AudioCodingModule::Create(1)), + _acmB(AudioCodingModule::Create(2)), _channel_A2B(NULL), _channel_B2A(NULL), _writeToFile(true), @@ -503,7 +503,7 @@ break; default: fprintf(stderr, "Wrong Test Number\n"); - getchar(); + getc(stdin); exit(1); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/Channel.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/Channel.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/Channel.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/Channel.cc 2015-02-03 14:33:34.000000000 +0000 @@ -120,6 +120,7 @@ return status; } +// TODO(turajs): rewite this method. void Channel::CalcStatistics(WebRtcRTPHeader& rtpInfo, uint16_t payloadSize) { int n; if ((rtpInfo.header.payloadType != _lastPayloadType) @@ -188,6 +189,8 @@ currentPayloadStr->lastPayloadLenByte = payloadSize; currentPayloadStr->lastTimestamp = rtpInfo.header.timestamp; currentPayloadStr->payloadType = rtpInfo.header.payloadType; + memset(currentPayloadStr->frameSizeStats, 0, MAX_NUM_FRAMESIZES * + sizeof(ACMTestFrameSizeStats)); } } else { n = 0; @@ -199,6 +202,8 @@ _payloadStats[n].lastPayloadLenByte = payloadSize; _payloadStats[n].lastTimestamp = rtpInfo.header.timestamp; _payloadStats[n].payloadType = rtpInfo.header.payloadType; + memset(_payloadStats[n].frameSizeStats, 0, MAX_NUM_FRAMESIZES * + sizeof(ACMTestFrameSizeStats)); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/Channel.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/Channel.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/Channel.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/Channel.h 2015-02-03 14:33:34.000000000 +0000 @@ -24,6 +24,7 @@ #define MAX_NUM_PAYLOADS 50 #define MAX_NUM_FRAMESIZES 6 +// TODO(turajs): Write constructor for this structure. struct ACMTestFrameSizeStats { uint16_t frameSizeSample; int16_t maxPayloadLen; @@ -34,6 +35,7 @@ double usageLenSec; }; +// TODO(turajs): Write constructor for this structure. struct ACMTestPayloadStats { bool newPacket; int16_t payloadType; @@ -48,10 +50,11 @@ Channel(int16_t chID = -1); ~Channel(); - int32_t SendData(const FrameType frameType, const uint8_t payloadType, - const uint32_t timeStamp, const uint8_t* payloadData, - const uint16_t payloadSize, - const RTPFragmentationHeader* fragmentation); + virtual int32_t SendData( + const FrameType frameType, const uint8_t payloadType, + const uint32_t timeStamp, const uint8_t* payloadData, + const uint16_t payloadSize, + const RTPFragmentationHeader* fragmentation) OVERRIDE; void RegisterReceiverACM(AudioCodingModule *acm); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/delay_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/delay_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/delay_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/delay_test.cc 2015-02-03 14:33:34.000000000 +0000 @@ -35,7 +35,6 @@ DEFINE_int32(delay, 0, "Delay in millisecond."); DEFINE_int32(init_delay, 0, "Initial delay in millisecond."); DEFINE_bool(dtx, false, "Enable DTX at the sender side."); -DEFINE_bool(acm2, false, "Run the test with ACM2."); DEFINE_bool(packet_loss, false, "Apply packet loss, c.f. Channel{.cc, .h}."); DEFINE_bool(fec, false, "Use Forward Error Correction (FEC)."); @@ -64,9 +63,9 @@ class DelayTest { public: - explicit DelayTest(const Config& config) - : acm_a_(config.Get().Create(0)), - acm_b_(config.Get().Create(1)), + DelayTest() + : acm_a_(AudioCodingModule::Create(0)), + acm_b_(AudioCodingModule::Create(1)), channel_a2b_(new Channel), test_cntr_(0), encoding_sample_rate_hz_(8000) {} @@ -162,8 +161,8 @@ void ConfigAcm(const AcmSettings& config) { ASSERT_EQ(0, acm_a_->SetVAD(config.dtx, config.dtx, VADAggr)) << "Failed to set VAD.\n"; - ASSERT_EQ(0, acm_a_->SetFECStatus(config.fec)) << - "Failed to set FEC.\n"; + ASSERT_EQ(0, acm_a_->SetREDStatus(config.fec)) << + "Failed to set RED.\n"; } void ConfigChannel(bool packet_loss) { @@ -245,7 +244,6 @@ int main(int argc, char* argv[]) { google::ParseCommandLineFlags(&argc, &argv, true); - webrtc::Config config; webrtc::TestSettings test_setting; strcpy(test_setting.codec.name, FLAGS_codec.c_str()); @@ -266,13 +264,7 @@ test_setting.acm.fec = FLAGS_fec; test_setting.packet_loss = FLAGS_packet_loss; - if (FLAGS_acm2) { - webrtc::UseNewAcm(&config); - } else { - webrtc::UseLegacyAcm(&config); - } - - webrtc::DelayTest delay_test(config); + webrtc::DelayTest delay_test; delay_test.Initialize(); delay_test.Perform(&test_setting, 1, 240, "delay_test"); return 0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/dual_stream_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/dual_stream_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/dual_stream_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/dual_stream_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -8,8 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "gtest/gtest.h" -#include "webrtc/common.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" #include "webrtc/modules/audio_coding/main/test/PCMFile.h" @@ -22,9 +21,10 @@ namespace webrtc { -class DualStreamTest : public AudioPacketizationCallback { - public: - explicit DualStreamTest(const Config& config); +class DualStreamTest : public AudioPacketizationCallback, + public ::testing::Test { + protected: + DualStreamTest(); ~DualStreamTest(); void RunTest(int frame_size_primary_samples, @@ -35,12 +35,11 @@ void ApiTest(); - protected: - - int32_t SendData(FrameType frameType, uint8_t payload_type, - uint32_t timestamp, const uint8_t* payload_data, - uint16_t payload_size, - const RTPFragmentationHeader* fragmentation); + virtual int32_t SendData( + FrameType frameType, uint8_t payload_type, + uint32_t timestamp, const uint8_t* payload_data, + uint16_t payload_size, + const RTPFragmentationHeader* fragmentation) OVERRIDE; void Perform(bool start_in_sync, int num_channels_input); @@ -93,10 +92,10 @@ bool received_payload_[kMaxNumStreams]; }; -DualStreamTest::DualStreamTest(const Config& config) - : acm_dual_stream_(config.Get().Create(0)), - acm_ref_primary_(config.Get().Create(1)), - acm_ref_secondary_(config.Get().Create(2)), +DualStreamTest::DualStreamTest() + : acm_dual_stream_(AudioCodingModule::Create(0)), + acm_ref_primary_(AudioCodingModule::Create(1)), + acm_ref_secondary_(AudioCodingModule::Create(2)), payload_ref_is_stored_(), payload_dual_is_stored_(), timestamp_ref_(), @@ -388,17 +387,106 @@ return 0; } -void DualStreamTest::RunTest(int frame_size_primary_samples, - int num_channels_primary, - int sampling_rate, - bool start_in_sync, - int num_channels_input) { - InitializeSender( - frame_size_primary_samples, num_channels_primary, sampling_rate); - Perform(start_in_sync, num_channels_input); -}; +// Mono input, mono primary WB 20 ms frame. +TEST_F(DualStreamTest, + DISABLED_ON_ANDROID(BitExactSyncMonoInputMonoPrimaryWb20Ms)) { + InitializeSender(20, 1, 16000); + Perform(true, 1); +} + +// Mono input, stereo primary WB 20 ms frame. +TEST_F(DualStreamTest, + DISABLED_ON_ANDROID(BitExactSyncMonoInput_StereoPrimaryWb20Ms)) { + InitializeSender(20, 2, 16000); + Perform(true, 1); +} + +// Mono input, mono primary SWB 20 ms frame. +TEST_F(DualStreamTest, + DISABLED_ON_ANDROID(BitExactSyncMonoInputMonoPrimarySwb20Ms)) { + InitializeSender(20, 1, 32000); + Perform(true, 1); +} + +// Mono input, stereo primary SWB 20 ms frame. +TEST_F(DualStreamTest, + DISABLED_ON_ANDROID(BitExactSyncMonoInputStereoPrimarySwb20Ms)) { + InitializeSender(20, 2, 32000); + Perform(true, 1); +} + +// Mono input, mono primary WB 40 ms frame. +TEST_F(DualStreamTest, + DISABLED_ON_ANDROID(BitExactSyncMonoInputMonoPrimaryWb40Ms)) { + InitializeSender(40, 1, 16000); + Perform(true, 1); +} + +// Mono input, stereo primary WB 40 ms frame +TEST_F(DualStreamTest, + DISABLED_ON_ANDROID(BitExactSyncMonoInputStereoPrimaryWb40Ms)) { + InitializeSender(40, 2, 16000); + Perform(true, 1); +} + +// Stereo input, mono primary WB 20 ms frame. +TEST_F(DualStreamTest, + DISABLED_ON_ANDROID(BitExactSyncStereoInputMonoPrimaryWb20Ms)) { + InitializeSender(20, 1, 16000); + Perform(true, 2); +} + +// Stereo input, stereo primary WB 20 ms frame. +TEST_F(DualStreamTest, + DISABLED_ON_ANDROID(BitExactSyncStereoInputStereoPrimaryWb20Ms)) { + InitializeSender(20, 2, 16000); + Perform(true, 2); +} + +// Stereo input, mono primary SWB 20 ms frame. +TEST_F(DualStreamTest, + DISABLED_ON_ANDROID(BitExactSyncStereoInputMonoPrimarySwb20Ms)) { + InitializeSender(20, 1, 32000); + Perform(true, 2); +} + +// Stereo input, stereo primary SWB 20 ms frame. +TEST_F(DualStreamTest, + DISABLED_ON_ANDROID(BitExactSyncStereoInputStereoPrimarySwb20Ms)) { + InitializeSender(20, 2, 32000); + Perform(true, 2); +} + +// Stereo input, mono primary WB 40 ms frame. +TEST_F(DualStreamTest, + DISABLED_ON_ANDROID(BitExactSyncStereoInputMonoPrimaryWb40Ms)) { + InitializeSender(40, 1, 16000); + Perform(true, 2); +} + +// Stereo input, stereo primary WB 40 ms frame. +TEST_F(DualStreamTest, + DISABLED_ON_ANDROID(BitExactSyncStereoInputStereoPrimaryWb40Ms)) { + InitializeSender(40, 2, 16000); + Perform(true, 2); +} + +// Asynchronous test, ACM is fed with data then secondary coder is registered. +// Mono input, mono primary WB 20 ms frame. +TEST_F(DualStreamTest, + DISABLED_ON_ANDROID(BitExactAsyncMonoInputMonoPrimaryWb20Ms)) { + InitializeSender(20, 1, 16000); + Perform(false, 1); +} + +// Mono input, mono primary WB 20 ms frame. +TEST_F(DualStreamTest, + DISABLED_ON_ANDROID(BitExactAsyncMonoInputMonoPrimaryWb40Ms)) { + InitializeSender(40, 1, 16000); + Perform(false, 1); +} -void DualStreamTest::ApiTest() { +TEST_F(DualStreamTest, DISABLED_ON_ANDROID(Api)) { PopulateCodecInstances(20, 1, 16000); CodecInst my_codec; ASSERT_EQ(0, acm_dual_stream_->InitializeSender()); @@ -449,171 +537,4 @@ EXPECT_EQ(VADVeryAggr, vad_mode); } -namespace { - -DualStreamTest* CreateLegacy() { - Config config; - UseLegacyAcm(&config); - DualStreamTest* test = new DualStreamTest(config); - return test; -} - -DualStreamTest* CreateNew() { - Config config; - UseNewAcm(&config); - DualStreamTest* test = new DualStreamTest(config); - return test; -} - -} // namespace - -// Mono input, mono primary WB 20 ms frame. -TEST(DualStreamTest, - DISABLED_ON_ANDROID(BitExactSyncMonoInputMonoPrimaryWb20Ms)) { - scoped_ptr test(CreateLegacy()); - test->RunTest(20, 1, 16000, true, 1); - - test.reset(CreateNew()); - test->RunTest(20, 1, 16000, true, 1); -} - -// Mono input, stereo primary WB 20 ms frame. -TEST(DualStreamTest, - DISABLED_ON_ANDROID(BitExactSyncMonoInput_StereoPrimaryWb20Ms)) { - scoped_ptr test(CreateLegacy()); - test->RunTest(20, 2, 16000, true, 1); - - test.reset(CreateNew()); - test->RunTest(20, 2, 16000, true, 1); -} - -// Mono input, mono primary SWB 20 ms frame. -TEST(DualStreamTest, - DISABLED_ON_ANDROID(BitExactSyncMonoInputMonoPrimarySwb20Ms)) { - scoped_ptr test(CreateLegacy()); - test->RunTest(20, 1, 32000, true, 1); - - test.reset(CreateNew()); - test->RunTest(20, 1, 32000, true, 1); -} - -// Mono input, stereo primary SWB 20 ms frame. -TEST(DualStreamTest, - DISABLED_ON_ANDROID(BitExactSyncMonoInputStereoPrimarySwb20Ms)) { - scoped_ptr test(CreateLegacy()); - test->RunTest(20, 2, 32000, true, 1); - - test.reset(CreateNew()); - test->RunTest(20, 2, 32000, true, 1); -} - -// Mono input, mono primary WB 40 ms frame. -TEST(DualStreamTest, - DISABLED_ON_ANDROID(BitExactSyncMonoInputMonoPrimaryWb40Ms)) { - scoped_ptr test(CreateNew()); - test->RunTest(40, 1, 16000, true, 1); - - test.reset(CreateNew()); - test->RunTest(40, 1, 16000, true, 1); -} - -// Mono input, stereo primary WB 40 ms frame -TEST(DualStreamTest, - DISABLED_ON_ANDROID(BitExactSyncMonoInputStereoPrimaryWb40Ms)) { - scoped_ptr test(CreateNew()); - test->RunTest(40, 2, 16000, true, 1); - - test.reset(CreateNew()); - test->RunTest(40, 2, 16000, true, 1); -} - -// Stereo input, mono primary WB 20 ms frame. -TEST(DualStreamTest, - DISABLED_ON_ANDROID(BitExactSyncStereoInputMonoPrimaryWb20Ms)) { - scoped_ptr test(CreateLegacy()); - test->RunTest(20, 1, 16000, true, 2); - - test.reset(CreateNew()); - test->RunTest(20, 1, 16000, true, 2); -} - -// Stereo input, stereo primary WB 20 ms frame. -TEST(DualStreamTest, - DISABLED_ON_ANDROID(BitExactSyncStereoInputStereoPrimaryWb20Ms)) { - scoped_ptr test(CreateLegacy()); - test->RunTest(20, 2, 16000, true, 2); - - test.reset(CreateNew()); - test->RunTest(20, 2, 16000, true, 2); -} - -// Stereo input, mono primary SWB 20 ms frame. -TEST(DualStreamTest, - DISABLED_ON_ANDROID(BitExactSyncStereoInputMonoPrimarySwb20Ms)) { - scoped_ptr test(CreateLegacy()); - test->RunTest(20, 1, 32000, true, 2); - - test.reset(CreateNew()); - test->RunTest(20, 1, 32000, true, 2); -} - -// Stereo input, stereo primary SWB 20 ms frame. -TEST(DualStreamTest, - DISABLED_ON_ANDROID(BitExactSyncStereoInputStereoPrimarySwb20Ms)) { - scoped_ptr test(CreateLegacy()); - test->RunTest(20, 2, 32000, true, 2); - - test.reset(CreateNew()); - test->RunTest(20, 2, 32000, true, 2); -} - -// Stereo input, mono primary WB 40 ms frame. -TEST(DualStreamTest, - DISABLED_ON_ANDROID(BitExactSyncStereoInputMonoPrimaryWb40Ms)) { - scoped_ptr test(CreateLegacy()); - test->RunTest(40, 1, 16000, true, 2); - - test.reset(CreateNew()); - test->RunTest(40, 1, 16000, true, 2); -} - -// Stereo input, stereo primary WB 40 ms frame. -TEST(DualStreamTest, - DISABLED_ON_ANDROID(BitExactSyncStereoInputStereoPrimaryWb40Ms)) { - scoped_ptr test(CreateLegacy()); - test->RunTest(40, 2, 16000, true, 2); - - test.reset(CreateNew()); - test->RunTest(40, 2, 16000, true, 2); -} - -// Asynchronous test, ACM is fed with data then secondary coder is registered. -// Mono input, mono primary WB 20 ms frame. -TEST(DualStreamTest, - DISABLED_ON_ANDROID(BitExactAsyncMonoInputMonoPrimaryWb20Ms)) { - scoped_ptr test(CreateLegacy()); - test->RunTest(20, 1, 16000, false, 1); - - test.reset(CreateNew()); - test->RunTest(20, 1, 16000, false, 1); -} - -// Mono input, mono primary WB 20 ms frame. -TEST(DualStreamTest, - DISABLED_ON_ANDROID(BitExactAsyncMonoInputMonoPrimaryWb40Ms)) { - scoped_ptr test(CreateLegacy()); - test->RunTest(40, 1, 16000, false, 1); - - test.reset(CreateNew()); - test->RunTest(40, 1, 16000, false, 1); -} - -TEST(DualStreamTest, DISABLED_ON_ANDROID(ApiTest)) { - scoped_ptr test(CreateLegacy()); - test->ApiTest(); - - test.reset(CreateNew()); - test->ApiTest(); -} - } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/EncodeDecodeTest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/EncodeDecodeTest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/EncodeDecodeTest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/EncodeDecodeTest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -10,16 +10,12 @@ #include "webrtc/modules/audio_coding/main/test/EncodeDecodeTest.h" +#include #include #include -#include - -#include -#include #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/common_types.h" -#include "webrtc/common.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" #include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" #include "webrtc/modules/audio_coding/main/test/utility.h" @@ -55,16 +51,19 @@ _packetization(NULL) { } -void Sender::Setup(AudioCodingModule *acm, RTPStream *rtpStream) { +void Sender::Setup(AudioCodingModule *acm, RTPStream *rtpStream, + std::string in_file_name, int sample_rate, int channels) { acm->InitializeSender(); struct CodecInst sendCodec; int noOfCodecs = acm->NumberOfCodecs(); int codecNo; // Open input file - const std::string file_name = webrtc::test::ResourcePath( - "audio_coding/testfile32kHz", "pcm"); - _pcmFile.Open(file_name, 32000, "rb"); + const std::string file_name = webrtc::test::ResourcePath(in_file_name, "pcm"); + _pcmFile.Open(file_name, sample_rate, "rb"); + if (channels == 2) { + _pcmFile.ReadStereo(true); + } // Set the codec for the current test. if ((testMode == 0) || (testMode == 1)) { @@ -82,10 +81,8 @@ } EXPECT_EQ(0, acm->Codec(codecNo, &sendCodec)); - // Default number of channels is 2 for CELT, so we change to 1 in this test. - if (!strcmp(sendCodec.plname, "CELT")) { - sendCodec.channels = 1; - } + + sendCodec.channels = channels; EXPECT_EQ(0, acm->RegisterSendCodec(sendCodec)); _packetization = new TestPacketization(rtpStream, sendCodec.plfreq); @@ -126,21 +123,28 @@ _payloadSizeBytes(MAX_INCOMING_PAYLOAD) { } -void Receiver::Setup(AudioCodingModule *acm, RTPStream *rtpStream) { - struct CodecInst recvCodec; +void Receiver::Setup(AudioCodingModule *acm, RTPStream *rtpStream, + std::string out_file_name, int channels) { + struct CodecInst recvCodec = CodecInst(); int noOfCodecs; EXPECT_EQ(0, acm->InitializeReceiver()); noOfCodecs = acm->NumberOfCodecs(); for (int i = 0; i < noOfCodecs; i++) { - EXPECT_EQ(0, acm->Codec(static_cast(i), &recvCodec)); - EXPECT_EQ(0, acm->RegisterReceiveCodec(recvCodec)); + EXPECT_EQ(0, acm->Codec(i, &recvCodec)); + if (recvCodec.channels == channels) + EXPECT_EQ(0, acm->RegisterReceiveCodec(recvCodec)); + // Forces mono/stereo for Opus. + if (!strcmp(recvCodec.plname, "opus")) { + recvCodec.channels = channels; + EXPECT_EQ(0, acm->RegisterReceiveCodec(recvCodec)); + } } int playSampFreq; std::string file_name; std::stringstream file_stream; - file_stream << webrtc::test::OutputPath() << "encodeDecode_out" + file_stream << webrtc::test::OutputPath() << out_file_name << static_cast(codeId) << ".pcm"; file_name = file_stream.str(); _rtpStream = rtpStream; @@ -157,7 +161,7 @@ printf("which means output frequency equal to received signal frequency"); printf("\n\nChoose output sampling frequency: "); ASSERT_GT(scanf("%d", &playSampFreq), 0); - file_name = webrtc::test::OutputPath() + "encodeDecode_out.pcm"; + file_name = webrtc::test::OutputPath() + out_file_name + ".pcm"; _pcmFile.Open(file_name, playSampFreq, "wb+"); } @@ -214,7 +218,8 @@ if (_playoutLengthSmpls == 0) { return false; } - _pcmFile.Write10MsData(audioFrame.data_, audioFrame.samples_per_channel_); + _pcmFile.Write10MsData(audioFrame.data_, + audioFrame.samples_per_channel_ * audioFrame.num_channels_); return true; } @@ -242,16 +247,14 @@ } } -EncodeDecodeTest::EncodeDecodeTest(const Config& config) - : config_(config) { +EncodeDecodeTest::EncodeDecodeTest() { _testMode = 2; Trace::CreateTrace(); Trace::SetTraceFile( (webrtc::test::OutputPath() + "acm_encdec_trace.txt").c_str()); } -EncodeDecodeTest::EncodeDecodeTest(int testMode, const Config& config) - : config_(config) { +EncodeDecodeTest::EncodeDecodeTest(int testMode) { //testMode == 0 for autotest //testMode == 1 for testing all codecs/parameters //testMode > 1 for specific user-input test (as it was used before) @@ -273,8 +276,7 @@ codePars[1] = 0; codePars[2] = 0; - scoped_ptr acm( - config_.Get().Create(0)); + scoped_ptr acm(AudioCodingModule::Create(0)); struct CodecInst sendCodecTmp; numCodecs = acm->NumberOfCodecs(); @@ -305,16 +307,15 @@ // Only encode using real mono encoders, not telephone-event and cng. for (int loopPars = 1; loopPars <= numPars[codeId]; loopPars++) { // Encode all data to file. - EncodeToFile(1, codeId, codePars, _testMode); + std::string fileName = EncodeToFile(1, codeId, codePars, _testMode); RTPFile rtpFile; - std::string fileName = webrtc::test::OutputPath() + "outFile.rtp"; rtpFile.Open(fileName.c_str(), "rb"); _receiver.codeId = codeId; rtpFile.ReadHeader(); - _receiver.Setup(acm.get(), &rtpFile); + _receiver.Setup(acm.get(), &rtpFile, "encodeDecode_out", 1); _receiver.Run(); _receiver.Teardown(); rtpFile.Close(); @@ -327,12 +328,14 @@ } } -void EncodeDecodeTest::EncodeToFile(int fileType, int codeId, int* codePars, - int testMode) { - scoped_ptr acm( - config_.Get().Create(1)); +std::string EncodeDecodeTest::EncodeToFile(int fileType, + int codeId, + int* codePars, + int testMode) { + scoped_ptr acm(AudioCodingModule::Create(1)); RTPFile rtpFile; - std::string fileName = webrtc::test::OutputPath() + "outFile.rtp"; + std::string fileName = webrtc::test::TempFilename(webrtc::test::OutputPath(), + "encode_decode_rtp"); rtpFile.Open(fileName.c_str(), "wb+"); rtpFile.WriteHeader(); @@ -340,13 +343,15 @@ _sender.testMode = testMode; _sender.codeId = codeId; - _sender.Setup(acm.get(), &rtpFile); + _sender.Setup(acm.get(), &rtpFile, "audio_coding/testfile32kHz", 32000, 1); struct CodecInst sendCodecInst; if (acm->SendCodec(&sendCodecInst) >= 0) { _sender.Run(); } _sender.Teardown(); rtpFile.Close(); + + return fileName; } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/EncodeDecodeTest.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/EncodeDecodeTest.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/EncodeDecodeTest.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/EncodeDecodeTest.h 2015-02-03 14:33:34.000000000 +0000 @@ -12,6 +12,7 @@ #define WEBRTC_MODULES_AUDIO_CODING_MAIN_TEST_ENCODEDECODETEST_H_ #include +#include #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" #include "webrtc/modules/audio_coding/main/test/ACMTest.h" @@ -23,17 +24,16 @@ #define MAX_INCOMING_PAYLOAD 8096 -class Config; - // TestPacketization callback which writes the encoded payloads to file class TestPacketization : public AudioPacketizationCallback { public: TestPacketization(RTPStream *rtpStream, uint16_t frequency); ~TestPacketization(); - virtual int32_t SendData(const FrameType frameType, const uint8_t payloadType, - const uint32_t timeStamp, const uint8_t* payloadData, - const uint16_t payloadSize, - const RTPFragmentationHeader* fragmentation); + virtual int32_t SendData( + const FrameType frameType, const uint8_t payloadType, + const uint32_t timeStamp, const uint8_t* payloadData, + const uint16_t payloadSize, + const RTPFragmentationHeader* fragmentation) OVERRIDE; private: static void MakeRTPheader(uint8_t* rtpHeader, uint8_t payloadType, @@ -46,7 +46,8 @@ class Sender { public: Sender(); - void Setup(AudioCodingModule *acm, RTPStream *rtpStream); + void Setup(AudioCodingModule *acm, RTPStream *rtpStream, + std::string in_file_name, int sample_rate, int channels); void Teardown(); void Run(); bool Add10MsData(); @@ -55,8 +56,10 @@ uint8_t testMode; uint8_t codeId; - private: + protected: AudioCodingModule* _acm; + + private: PCMFile _pcmFile; AudioFrame _audioFrame; TestPacketization* _packetization; @@ -65,10 +68,12 @@ class Receiver { public: Receiver(); - void Setup(AudioCodingModule *acm, RTPStream *rtpStream); + virtual ~Receiver() {}; + void Setup(AudioCodingModule *acm, RTPStream *rtpStream, + std::string out_file_name, int channels); void Teardown(); void Run(); - bool IncomingPacket(); + virtual bool IncomingPacket(); bool PlayoutData(); //for auto_test and logging @@ -76,33 +81,36 @@ uint8_t testMode; private: - AudioCodingModule* _acm; - RTPStream* _rtpStream; PCMFile _pcmFile; int16_t* _playoutBuffer; uint16_t _playoutLengthSmpls; - uint8_t _incomingPayload[MAX_INCOMING_PAYLOAD]; - uint16_t _payloadSizeBytes; - uint16_t _realPayloadSizeBytes; int32_t _frequency; bool _firstTime; + + protected: + AudioCodingModule* _acm; + uint8_t _incomingPayload[MAX_INCOMING_PAYLOAD]; + RTPStream* _rtpStream; WebRtcRTPHeader _rtpInfo; + uint16_t _realPayloadSizeBytes; + uint16_t _payloadSizeBytes; uint32_t _nextTime; }; class EncodeDecodeTest : public ACMTest { public: - explicit EncodeDecodeTest(const Config& config); - EncodeDecodeTest(int testMode, const Config& config); - virtual void Perform(); + EncodeDecodeTest(); + explicit EncodeDecodeTest(int testMode); + virtual void Perform() OVERRIDE; uint16_t _playoutFreq; uint8_t _testMode; private: - void EncodeToFile(int fileType, int codeId, int* codePars, int testMode); - - const Config& config_; + std::string EncodeToFile(int fileType, + int codeId, + int* codePars, + int testMode); protected: Sender _sender; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/initial_delay_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/initial_delay_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/initial_delay_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/initial_delay_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -15,8 +15,7 @@ #include -#include "gtest/gtest.h" -#include "webrtc/common.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/common_types.h" #include "webrtc/engine_configurations.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" @@ -44,11 +43,11 @@ } -class InitialPlayoutDelayTest { - public: - explicit InitialPlayoutDelayTest(const Config& config) - : acm_a_(config.Get().Create(0)), - acm_b_(config.Get().Create(1)), +class InitialPlayoutDelayTest : public ::testing::Test { + protected: + InitialPlayoutDelayTest() + : acm_a_(AudioCodingModule::Create(0)), + acm_b_(AudioCodingModule::Create(1)), channel_a2b_(NULL) {} ~InitialPlayoutDelayTest() { @@ -85,39 +84,43 @@ void NbMono() { CodecInst codec; AudioCodingModule::Codec("L16", &codec, 8000, 1); - Run(codec, 2000); + codec.pacsize = codec.plfreq * 30 / 1000; // 30 ms packets. + Run(codec, 1000); } void WbMono() { CodecInst codec; AudioCodingModule::Codec("L16", &codec, 16000, 1); - Run(codec, 2000); + codec.pacsize = codec.plfreq * 30 / 1000; // 30 ms packets. + Run(codec, 1000); } void SwbMono() { CodecInst codec; AudioCodingModule::Codec("L16", &codec, 32000, 1); - Run(codec, 1500); // NetEq buffer is not sufficiently large for 3 sec of - // PCM16 super-wideband. + codec.pacsize = codec.plfreq * 10 / 1000; // 10 ms packets. + Run(codec, 400); // Memory constraints limit the buffer at <500 ms. } void NbStereo() { CodecInst codec; AudioCodingModule::Codec("L16", &codec, 8000, 2); - Run(codec, 2000); + codec.pacsize = codec.plfreq * 30 / 1000; // 30 ms packets. + Run(codec, 1000); } void WbStereo() { CodecInst codec; AudioCodingModule::Codec("L16", &codec, 16000, 2); - Run(codec, 1500); + codec.pacsize = codec.plfreq * 30 / 1000; // 30 ms packets. + Run(codec, 1000); } void SwbStereo() { CodecInst codec; AudioCodingModule::Codec("L16", &codec, 32000, 2); - Run(codec, 600); // NetEq buffer is not sufficiently large for 3 sec of - // PCM16 super-wideband. + codec.pacsize = codec.plfreq * 10 / 1000; // 10 ms packets. + Run(codec, 400); // Memory constraints limit the buffer at <500 ms. } private: @@ -137,7 +140,7 @@ uint32_t timestamp = 0; double rms = 0; - acm_a_->RegisterSendCodec(codec); + ASSERT_EQ(0, acm_a_->RegisterSendCodec(codec)); acm_b_->SetInitialPlayoutDelay(initial_delay_ms); while (rms < kAmp / 2) { in_audio_frame.timestamp_ = timestamp; @@ -158,72 +161,16 @@ Channel* channel_a2b_; }; -namespace { - -InitialPlayoutDelayTest* CreateLegacy() { - Config config; - UseLegacyAcm(&config); - InitialPlayoutDelayTest* test = new InitialPlayoutDelayTest(config); - test->SetUp(); - return test; -} - -InitialPlayoutDelayTest* CreateNew() { - Config config; - UseNewAcm(&config); - InitialPlayoutDelayTest* test = new InitialPlayoutDelayTest(config); - test->SetUp(); - return test; -} +TEST_F(InitialPlayoutDelayTest, NbMono) { NbMono(); } -} // namespace +TEST_F(InitialPlayoutDelayTest, WbMono) { WbMono(); } -TEST(InitialPlayoutDelayTest, NbMono) { - scoped_ptr test(CreateLegacy()); - test->NbMono(); +TEST_F(InitialPlayoutDelayTest, SwbMono) { SwbMono(); } - test.reset(CreateNew()); - test->NbMono(); -} - -TEST(InitialPlayoutDelayTest, WbMono) { - scoped_ptr test(CreateLegacy()); - test->WbMono(); +TEST_F(InitialPlayoutDelayTest, NbStereo) { NbStereo(); } - test.reset(CreateNew()); - test->WbMono(); -} +TEST_F(InitialPlayoutDelayTest, WbStereo) { WbStereo(); } -TEST(InitialPlayoutDelayTest, SwbMono) { - scoped_ptr test(CreateLegacy()); - test->SwbMono(); - - test.reset(CreateNew()); - test->SwbMono(); -} - -TEST(InitialPlayoutDelayTest, NbStereo) { - scoped_ptr test(CreateLegacy()); - test->NbStereo(); - - test.reset(CreateNew()); - test->NbStereo(); -} - -TEST(InitialPlayoutDelayTest, WbStereo) { - scoped_ptr test(CreateLegacy()); - test->WbStereo(); - - test.reset(CreateNew()); - test->WbStereo(); -} - -TEST(InitialPlayoutDelayTest, SwbStereo) { - scoped_ptr test(CreateLegacy()); - test->SwbStereo(); - - test.reset(CreateNew()); - test->SwbStereo(); -} +TEST_F(InitialPlayoutDelayTest, SwbStereo) { SwbStereo(); } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/insert_packet_with_timing.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/insert_packet_with_timing.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/insert_packet_with_timing.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/insert_packet_with_timing.cc 2015-02-03 14:33:34.000000000 +0000 @@ -11,7 +11,7 @@ #include #include "gflags/gflags.h" -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/common_types.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" #include "webrtc/modules/audio_coding/main/test/Channel.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/iSACTest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/iSACTest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/iSACTest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/iSACTest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -86,11 +86,10 @@ return 0; } -ISACTest::ISACTest(int testMode, const Config& config) - : _acmA(config.Get().Create(1)), - _acmB(config.Get().Create(2)), - _testMode(testMode) { -} +ISACTest::ISACTest(int testMode) + : _acmA(AudioCodingModule::Create(1)), + _acmB(AudioCodingModule::Create(2)), + _testMode(testMode) {} ISACTest::~ISACTest() {} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/iSACTest.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/iSACTest.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/iSACTest.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/iSACTest.h 2015-02-03 14:33:34.000000000 +0000 @@ -13,7 +13,6 @@ #include -#include "webrtc/common.h" #include "webrtc/common_types.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" #include "webrtc/modules/audio_coding/main/test/ACMTest.h" @@ -27,8 +26,6 @@ namespace webrtc { -class Config; - struct ACMTestISACConfig { int32_t currentRateBitPerSec; int16_t currentFrameSizeMsec; @@ -42,7 +39,7 @@ class ISACTest : public ACMTest { public: - ISACTest(int testMode, const Config& config); + explicit ISACTest(int testMode); ~ISACTest(); void Perform(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/opus_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/opus_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/opus_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/opus_test.cc 2015-02-03 14:33:34.000000000 +0000 @@ -15,13 +15,12 @@ #include #include "testing/gtest/include/gtest/gtest.h" -#include "webrtc/common.h" // Config. #include "webrtc/common_types.h" #include "webrtc/engine_configurations.h" #include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" -#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h" -#include "webrtc/modules/audio_coding/main/source/acm_opus.h" +#include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h" +#include "webrtc/modules/audio_coding/main/acm2/acm_opus.h" #include "webrtc/modules/audio_coding/main/test/TestStereo.h" #include "webrtc/modules/audio_coding/main/test/utility.h" #include "webrtc/system_wrappers/interface/trace.h" @@ -29,13 +28,12 @@ namespace webrtc { -OpusTest::OpusTest(const Config& config) - : acm_receiver_(config.Get().Create(0)), +OpusTest::OpusTest() + : acm_receiver_(AudioCodingModule::Create(0)), channel_a2b_(NULL), counter_(0), payload_type_(255), - rtp_timestamp_(0) { -} + rtp_timestamp_(0) {} OpusTest::~OpusTest() { if (channel_a2b_ != NULL) { @@ -213,12 +211,15 @@ int frame_length, int percent_loss) { AudioFrame audio_frame; int32_t out_freq_hz_b = out_file_.SamplingFrequency(); - int16_t audio[480 * 12 * 2]; // Can hold 120 ms stereo audio. - int16_t out_audio[480 * 12 * 2]; // Can hold 120 ms stereo audio. + const int kBufferSizeSamples = 480 * 12 * 2; // Can hold 120 ms stereo audio. + int16_t audio[kBufferSizeSamples]; + int16_t out_audio[kBufferSizeSamples]; int16_t audio_type; int written_samples = 0; int read_samples = 0; int decoded_samples = 0; + bool first_packet = true; + uint32_t start_time_stamp = 0; channel->reset_payload_size(); counter_ = 0; @@ -227,6 +228,15 @@ EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_mono_encoder_, bitrate)); EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_stereo_encoder_, bitrate)); +#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) || defined(WEBRTC_ARCH_ARM) + // If we are on Android, iOS and/or ARM, use a lower complexity setting as + // default. + const int kOpusComplexity5 = 5; + EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_mono_encoder_, kOpusComplexity5)); + EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_stereo_encoder_, + kOpusComplexity5)); +#endif + // Make sure the runtime is less than 60 seconds to pass Android test. for (size_t audio_length = 0; audio_length < 10000; audio_length += 10) { bool lost_packet = false; @@ -245,11 +255,13 @@ } // If input audio is sampled at 32 kHz, resampling to 48 kHz is required. - EXPECT_EQ(480, resampler_.Resample10Msec(audio_frame.data_, - audio_frame.sample_rate_hz_, - &audio[written_samples], - 48000, - channels)); + EXPECT_EQ(480, + resampler_.Resample10Msec(audio_frame.data_, + audio_frame.sample_rate_hz_, + 48000, + channels, + kBufferSizeSamples - written_samples, + &audio[written_samples])); written_samples += 480 * channels; // Sometimes we need to loop over the audio vector to produce the right @@ -314,6 +326,10 @@ // Send data to the channel. "channel" will handle the loss simulation. channel->SendData(kAudioFrameSpeech, payload_type_, rtp_timestamp_, bitstream, bitstream_len_byte, NULL); + if (first_packet) { + first_packet = false; + start_time_stamp = rtp_timestamp_; + } rtp_timestamp_ += frame_length; read_samples += frame_length * channels; } @@ -333,6 +349,13 @@ // Write stand-alone speech to file. out_file_standalone_.Write10MsData(out_audio, decoded_samples * channels); + + if (audio_frame.timestamp_ > start_time_stamp) { + // Number of channels should be the same for both stand-alone and + // ACM-decoding. + EXPECT_EQ(audio_frame.num_channels_, channels); + } + decoded_samples = 0; } @@ -352,13 +375,13 @@ file_stream << webrtc::test::OutputPath() << "opustest_out_" << test_number << ".pcm"; file_name = file_stream.str(); - out_file_.Open(file_name, 32000, "wb"); + out_file_.Open(file_name, 48000, "wb"); file_stream.str(""); file_name = file_stream.str(); file_stream << webrtc::test::OutputPath() << "opusstandalone_out_" << test_number << ".pcm"; file_name = file_stream.str(); - out_file_standalone_.Open(file_name, 32000, "wb"); + out_file_standalone_.Open(file_name, 48000, "wb"); } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/opus_test.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/opus_test.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/opus_test.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/opus_test.h 2015-02-03 14:33:34.000000000 +0000 @@ -13,8 +13,8 @@ #include -#include "webrtc/modules/audio_coding/main/source/acm_opus.h" -#include "webrtc/modules/audio_coding/main/source/acm_resampler.h" +#include "webrtc/modules/audio_coding/main/acm2/acm_opus.h" +#include "webrtc/modules/audio_coding/main/acm2/acm_resampler.h" #include "webrtc/modules/audio_coding/main/test/ACMTest.h" #include "webrtc/modules/audio_coding/main/test/Channel.h" #include "webrtc/modules/audio_coding/main/test/PCMFile.h" @@ -23,11 +23,9 @@ namespace webrtc { -class Config; - class OpusTest : public ACMTest { public: - explicit OpusTest(const Config& config); + OpusTest(); ~OpusTest(); void Perform(); @@ -47,7 +45,7 @@ int counter_; uint8_t payload_type_; int rtp_timestamp_; - acm1::ACMResampler resampler_; + acm2::ACMResampler resampler_; WebRtcOpusEncInst* opus_mono_encoder_; WebRtcOpusEncInst* opus_stereo_encoder_; WebRtcOpusDecInst* opus_mono_decoder_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/PacketLossTest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/PacketLossTest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/PacketLossTest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/PacketLossTest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/main/test/PacketLossTest.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/common.h" +#include "webrtc/test/testsupport/fileutils.h" + +namespace webrtc { + +ReceiverWithPacketLoss::ReceiverWithPacketLoss() + : loss_rate_(0), + burst_length_(1), + packet_counter_(0), + lost_packet_counter_(0), + burst_lost_counter_(burst_length_) { +} + +void ReceiverWithPacketLoss::Setup(AudioCodingModule *acm, + RTPStream *rtpStream, + std::string out_file_name, + int channels, + int loss_rate, + int burst_length) { + loss_rate_ = loss_rate; + burst_length_ = burst_length; + burst_lost_counter_ = burst_length_; // To prevent first packet gets lost. + std::stringstream ss; + ss << out_file_name << "_" << loss_rate_ << "_" << burst_length_ << "_"; + Receiver::Setup(acm, rtpStream, ss.str(), channels); +} + +bool ReceiverWithPacketLoss::IncomingPacket() { + if (!_rtpStream->EndOfFile()) { + if (packet_counter_ == 0) { + _realPayloadSizeBytes = _rtpStream->Read(&_rtpInfo, _incomingPayload, + _payloadSizeBytes, &_nextTime); + if (_realPayloadSizeBytes == 0) { + if (_rtpStream->EndOfFile()) { + packet_counter_ = 0; + return true; + } else { + return false; + } + } + } + + if (!PacketLost()) { + _acm->IncomingPacket(_incomingPayload, _realPayloadSizeBytes, _rtpInfo); + } + packet_counter_++; + _realPayloadSizeBytes = _rtpStream->Read(&_rtpInfo, _incomingPayload, + _payloadSizeBytes, &_nextTime); + if (_realPayloadSizeBytes == 0 && _rtpStream->EndOfFile()) { + packet_counter_ = 0; + lost_packet_counter_ = 0; + } + } + return true; +} + +bool ReceiverWithPacketLoss::PacketLost() { + if (burst_lost_counter_ < burst_length_) { + lost_packet_counter_++; + burst_lost_counter_++; + return true; + } + + if (lost_packet_counter_ * 100 < loss_rate_ * packet_counter_) { + lost_packet_counter_++; + burst_lost_counter_ = 1; + return true; + } + return false; +} + +SenderWithFEC::SenderWithFEC() + : expected_loss_rate_(0) { +} + +void SenderWithFEC::Setup(AudioCodingModule *acm, RTPStream *rtpStream, + std::string in_file_name, int sample_rate, + int channels, int expected_loss_rate) { + Sender::Setup(acm, rtpStream, in_file_name, sample_rate, channels); + EXPECT_TRUE(SetFEC(true)); + EXPECT_TRUE(SetPacketLossRate(expected_loss_rate)); +} + +bool SenderWithFEC::SetFEC(bool enable_fec) { + if (_acm->SetCodecFEC(enable_fec) == 0) { + return true; + } + return false; +} + +bool SenderWithFEC::SetPacketLossRate(int expected_loss_rate) { + if (_acm->SetPacketLossRate(expected_loss_rate) == 0) { + expected_loss_rate_ = expected_loss_rate; + return true; + } + return false; +} + +PacketLossTest::PacketLossTest(int channels, int expected_loss_rate, + int actual_loss_rate, int burst_length) + : channels_(channels), + in_file_name_(channels_ == 1 ? "audio_coding/testfile32kHz" : + "audio_coding/teststereo32kHz"), + sample_rate_hz_(32000), + sender_(new SenderWithFEC), + receiver_(new ReceiverWithPacketLoss), + expected_loss_rate_(expected_loss_rate), + actual_loss_rate_(actual_loss_rate), + burst_length_(burst_length) { +} + +void PacketLossTest::Perform() { +#ifndef WEBRTC_CODEC_OPUS + return; +#else + scoped_ptr acm(AudioCodingModule::Create(0)); + + int codec_id = acm->Codec("opus", 48000, channels_); + + RTPFile rtpFile; + std::string fileName = webrtc::test::TempFilename(webrtc::test::OutputPath(), + "packet_loss_test"); + + // Encode to file + rtpFile.Open(fileName.c_str(), "wb+"); + rtpFile.WriteHeader(); + + sender_->testMode = 0; + sender_->codeId = codec_id; + + sender_->Setup(acm.get(), &rtpFile, in_file_name_, sample_rate_hz_, channels_, + expected_loss_rate_); + struct CodecInst sendCodecInst; + if (acm->SendCodec(&sendCodecInst) >= 0) { + sender_->Run(); + } + sender_->Teardown(); + rtpFile.Close(); + + // Decode to file + rtpFile.Open(fileName.c_str(), "rb"); + rtpFile.ReadHeader(); + + receiver_->testMode = 0; + receiver_->codeId = codec_id; + + receiver_->Setup(acm.get(), &rtpFile, "packetLoss_out", channels_, + actual_loss_rate_, burst_length_); + receiver_->Run(); + receiver_->Teardown(); + rtpFile.Close(); +#endif +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/PacketLossTest.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/PacketLossTest.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/PacketLossTest.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/PacketLossTest.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_TEST_PACKETLOSSTEST_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_TEST_PACKETLOSSTEST_H_ + +#include +#include "webrtc/modules/audio_coding/main/test/EncodeDecodeTest.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class ReceiverWithPacketLoss : public Receiver { + public: + ReceiverWithPacketLoss(); + void Setup(AudioCodingModule *acm, RTPStream *rtpStream, + std::string out_file_name, int channels, int loss_rate, + int burst_length); + bool IncomingPacket() OVERRIDE; + protected: + bool PacketLost(); + int loss_rate_; + int burst_length_; + int packet_counter_; + int lost_packet_counter_; + int burst_lost_counter_; +}; + +class SenderWithFEC : public Sender { + public: + SenderWithFEC(); + void Setup(AudioCodingModule *acm, RTPStream *rtpStream, + std::string in_file_name, int sample_rate, int channels, + int expected_loss_rate); + bool SetPacketLossRate(int expected_loss_rate); + bool SetFEC(bool enable_fec); + protected: + int expected_loss_rate_; +}; + +class PacketLossTest : public ACMTest { + public: + PacketLossTest(int channels, int expected_loss_rate_, int actual_loss_rate, + int burst_length); + void Perform(); + protected: + int channels_; + std::string in_file_name_; + int sample_rate_hz_; + scoped_ptr sender_; + scoped_ptr receiver_; + int expected_loss_rate_; + int actual_loss_rate_; + int burst_length_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_TEST_PACKETLOSSTEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/PCMFile.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/PCMFile.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/PCMFile.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/PCMFile.cc 2015-02-03 14:33:34.000000000 +0000 @@ -14,8 +14,8 @@ #include #include -#include "gtest/gtest.h" -#include "module_common_types.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/interface/module_common_types.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/RTPFile.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/RTPFile.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/RTPFile.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/RTPFile.cc 2015-02-03 14:33:34.000000000 +0000 @@ -20,8 +20,9 @@ #include "audio_coding_module.h" #include "engine_configurations.h" -#include "gtest/gtest.h" // TODO (tlegrand): Consider removing usage of gtest. #include "rw_lock_wrapper.h" +// TODO(tlegrand): Consider removing usage of gtest. +#include "testing/gtest/include/gtest/gtest.h" namespace webrtc { @@ -233,11 +234,11 @@ if (plen == 0) { return 0; } - if (payloadSize < (lengthBytes - 20)) { - return -1; - } if (lengthBytes < 20) { - return -1; + return 0; + } + if (payloadSize < (lengthBytes - 20)) { + return 0; } lengthBytes -= 20; EXPECT_EQ(lengthBytes, fread(payloadData, 1, lengthBytes, _rtpFile)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/RTPFile.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/RTPFile.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/RTPFile.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/RTPFile.h 2015-02-03 14:33:34.000000000 +0000 @@ -65,14 +65,14 @@ ~RTPBuffer(); - void Write(const uint8_t payloadType, const uint32_t timeStamp, - const int16_t seqNo, const uint8_t* payloadData, - const uint16_t payloadSize, uint32_t frequency); + virtual void Write(const uint8_t payloadType, const uint32_t timeStamp, + const int16_t seqNo, const uint8_t* payloadData, + const uint16_t payloadSize, uint32_t frequency) OVERRIDE; - uint16_t Read(WebRtcRTPHeader* rtpInfo, uint8_t* payloadData, - uint16_t payloadSize, uint32_t* offset); + virtual uint16_t Read(WebRtcRTPHeader* rtpInfo, uint8_t* payloadData, + uint16_t payloadSize, uint32_t* offset) OVERRIDE; - virtual bool EndOfFile() const; + virtual bool EndOfFile() const OVERRIDE; private: RWLockWrapper* _queueRWLock; @@ -97,14 +97,14 @@ void ReadHeader(); - void Write(const uint8_t payloadType, const uint32_t timeStamp, - const int16_t seqNo, const uint8_t* payloadData, - const uint16_t payloadSize, uint32_t frequency); + virtual void Write(const uint8_t payloadType, const uint32_t timeStamp, + const int16_t seqNo, const uint8_t* payloadData, + const uint16_t payloadSize, uint32_t frequency) OVERRIDE; - uint16_t Read(WebRtcRTPHeader* rtpInfo, uint8_t* payloadData, - uint16_t payloadSize, uint32_t* offset); + virtual uint16_t Read(WebRtcRTPHeader* rtpInfo, uint8_t* payloadData, + uint16_t payloadSize, uint32_t* offset) OVERRIDE; - bool EndOfFile() const { + virtual bool EndOfFile() const OVERRIDE { return _rtpEOF; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/target_delay_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/target_delay_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/target_delay_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/target_delay_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -8,8 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "gtest/gtest.h" -#include "webrtc/common.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/common_types.h" #include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" @@ -22,11 +21,9 @@ namespace webrtc { - -class TargetDelayTest { - public: - explicit TargetDelayTest(const Config& config) - : acm_(config.Get().Create(0)) {} +class TargetDelayTest : public ::testing::Test { + protected: + TargetDelayTest() : acm_(AudioCodingModule::Create(0)) {} ~TargetDelayTest() {} @@ -202,65 +199,24 @@ uint8_t payload_[kPayloadLenBytes]; }; - -namespace { - -TargetDelayTest* CreateLegacy() { - Config config; - UseLegacyAcm(&config); - TargetDelayTest* test = new TargetDelayTest(config); - test->SetUp(); - return test; +TEST_F(TargetDelayTest, DISABLED_ON_ANDROID(OutOfRangeInput)) { + OutOfRangeInput(); } -TargetDelayTest* CreateNew() { - Config config; - UseNewAcm(&config); - TargetDelayTest* test = new TargetDelayTest(config); - test->SetUp(); - return test; +TEST_F(TargetDelayTest, DISABLED_ON_ANDROID(NoTargetDelayBufferSizeChanges)) { + NoTargetDelayBufferSizeChanges(); } -} // namespace - -TEST(TargetDelayTest, DISABLED_ON_ANDROID(OutOfRangeInput)) { - scoped_ptr test(CreateLegacy()); - test->OutOfRangeInput(); - - test.reset(CreateNew()); - test->OutOfRangeInput(); +TEST_F(TargetDelayTest, DISABLED_ON_ANDROID(WithTargetDelayBufferNotChanging)) { + WithTargetDelayBufferNotChanging(); } -TEST(TargetDelayTest, DISABLED_ON_ANDROID(NoTargetDelayBufferSizeChanges)) { - scoped_ptr test(CreateLegacy()); - test->NoTargetDelayBufferSizeChanges(); - - test.reset(CreateNew()); - test->NoTargetDelayBufferSizeChanges(); +TEST_F(TargetDelayTest, DISABLED_ON_ANDROID(RequiredDelayAtCorrectRange)) { + RequiredDelayAtCorrectRange(); } -TEST(TargetDelayTest, DISABLED_ON_ANDROID(WithTargetDelayBufferNotChanging)) { - scoped_ptr test(CreateLegacy()); - test->WithTargetDelayBufferNotChanging(); - - test.reset(CreateNew()); - test->WithTargetDelayBufferNotChanging(); -} - -TEST(TargetDelayTest, DISABLED_ON_ANDROID(RequiredDelayAtCorrectRange)) { - scoped_ptr test(CreateLegacy()); - test->RequiredDelayAtCorrectRange(); - - test.reset(CreateNew()); - test->RequiredDelayAtCorrectRange(); -} - -TEST(TargetDelayTest, DISABLED_ON_ANDROID(TargetDelayBufferMinMax)) { - scoped_ptr test(CreateLegacy()); - test->TargetDelayBufferMinMax(); - - test.reset(CreateNew()); - test->TargetDelayBufferMinMax(); +TEST_F(TargetDelayTest, DISABLED_ON_ANDROID(TargetDelayBufferMinMax)) { + TargetDelayBufferMinMax(); } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestAllCodecs.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestAllCodecs.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestAllCodecs.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestAllCodecs.cc 2015-02-03 14:33:34.000000000 +0000 @@ -13,7 +13,7 @@ #include #include -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/common_types.h" #include "webrtc/engine_configurations.h" @@ -99,9 +99,9 @@ payload_size_ = 0; } -TestAllCodecs::TestAllCodecs(int test_mode, const Config& config) - : acm_a_(config.Get().Create(0)), - acm_b_(config.Get().Create(1)), +TestAllCodecs::TestAllCodecs(int test_mode) + : acm_a_(AudioCodingModule::Create(0)), + acm_b_(AudioCodingModule::Create(1)), channel_a_to_b_(NULL), test_count_(0), packet_size_samples_(0), @@ -710,10 +710,10 @@ } // Store the expected packet size in bytes, used to validate the received - // packet. If variable rate codec (extra_byte == -1), set to -1 (65535). + // packet. If variable rate codec (extra_byte == -1), set to -1. if (extra_byte != -1) { // Add 0.875 to always round up to a whole byte - packet_size_bytes_ = static_cast(static_cast(packet_size + packet_size_bytes_ = static_cast(static_cast(packet_size * rate) / static_cast(sampling_freq_hz * 8) + 0.875) + extra_byte; } else { @@ -768,8 +768,8 @@ // Verify that the received packet size matches the settings. receive_size = channel->payload_size(); if (receive_size) { - if ((receive_size != packet_size_bytes_) && - (packet_size_bytes_ < 65535)) { + if ((static_cast(receive_size) != packet_size_bytes_) && + (packet_size_bytes_ > -1)) { error_count++; } @@ -777,8 +777,9 @@ // is used to avoid problems when switching codec or frame size in the // test. timestamp_diff = channel->timestamp_diff(); - if ((counter > 10) && (timestamp_diff != packet_size_samples_) && - (packet_size_samples_ < 65535)) + if ((counter > 10) && + (static_cast(timestamp_diff) != packet_size_samples_) && + (packet_size_samples_ > -1)) error_count++; } @@ -819,4 +820,3 @@ } } // namespace webrtc - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestAllCodecs.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestAllCodecs.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestAllCodecs.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestAllCodecs.h 2015-02-03 14:33:34.000000000 +0000 @@ -11,7 +11,6 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_TEST_TESTALLCODECS_H_ #define WEBRTC_MODULES_AUDIO_CODING_MAIN_TEST_TESTALLCODECS_H_ -#include "webrtc/common.h" #include "webrtc/modules/audio_coding/main/test/ACMTest.h" #include "webrtc/modules/audio_coding/main/test/Channel.h" #include "webrtc/modules/audio_coding/main/test/PCMFile.h" @@ -29,10 +28,11 @@ void RegisterReceiverACM(AudioCodingModule* acm); - int32_t SendData(FrameType frame_type, uint8_t payload_type, - uint32_t timestamp, const uint8_t* payload_data, - uint16_t payload_size, - const RTPFragmentationHeader* fragmentation); + virtual int32_t SendData( + FrameType frame_type, uint8_t payload_type, + uint32_t timestamp, const uint8_t* payload_data, + uint16_t payload_size, + const RTPFragmentationHeader* fragmentation) OVERRIDE; uint16_t payload_size(); uint32_t timestamp_diff(); @@ -50,10 +50,10 @@ class TestAllCodecs : public ACMTest { public: - TestAllCodecs(int test_mode, const Config& config); + explicit TestAllCodecs(int test_mode); ~TestAllCodecs(); - void Perform(); + virtual void Perform() OVERRIDE; private: // The default value of '-1' indicates that the registration is based only on @@ -74,8 +74,8 @@ PCMFile infile_a_; PCMFile outfile_b_; int test_count_; - uint16_t packet_size_samples_; - uint16_t packet_size_bytes_; + int packet_size_samples_; + int packet_size_bytes_; }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/Tester.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/Tester.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/Tester.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/Tester.cc 2015-02-03 14:33:34.000000000 +0000 @@ -13,18 +13,17 @@ #include #include "testing/gtest/include/gtest/gtest.h" -#include "webrtc/common.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" #include "webrtc/modules/audio_coding/main/test/APITest.h" #include "webrtc/modules/audio_coding/main/test/EncodeDecodeTest.h" #include "webrtc/modules/audio_coding/main/test/iSACTest.h" #include "webrtc/modules/audio_coding/main/test/opus_test.h" +#include "webrtc/modules/audio_coding/main/test/PacketLossTest.h" #include "webrtc/modules/audio_coding/main/test/TestAllCodecs.h" -#include "webrtc/modules/audio_coding/main/test/TestFEC.h" +#include "webrtc/modules/audio_coding/main/test/TestRedFec.h" #include "webrtc/modules/audio_coding/main/test/TestStereo.h" #include "webrtc/modules/audio_coding/main/test/TestVADDTX.h" #include "webrtc/modules/audio_coding/main/test/TwoWayCommunication.h" -#include "webrtc/modules/audio_coding/main/test/utility.h" #include "webrtc/system_wrappers/interface/trace.h" #include "webrtc/test/testsupport/fileutils.h" #include "webrtc/test/testsupport/gtest_disable.h" @@ -39,14 +38,7 @@ Trace::CreateTrace(); Trace::SetTraceFile((webrtc::test::OutputPath() + "acm_allcodecs_trace.txt").c_str()); - webrtc::Config config; - - UseLegacyAcm(&config); - webrtc::TestAllCodecs(ACM_TEST_MODE, config).Perform(); - - UseNewAcm(&config); - webrtc::TestAllCodecs(ACM_TEST_MODE, config).Perform(); - + webrtc::TestAllCodecs(ACM_TEST_MODE).Perform(); Trace::ReturnTrace(); } @@ -54,29 +46,15 @@ Trace::CreateTrace(); Trace::SetTraceFile((webrtc::test::OutputPath() + "acm_encodedecode_trace.txt").c_str()); - webrtc::Config config; - - UseLegacyAcm(&config); - webrtc::EncodeDecodeTest(ACM_TEST_MODE, config).Perform(); - - UseNewAcm(&config); - webrtc::EncodeDecodeTest(ACM_TEST_MODE, config).Perform(); - + webrtc::EncodeDecodeTest(ACM_TEST_MODE).Perform(); Trace::ReturnTrace(); } -TEST(AudioCodingModuleTest, DISABLED_ON_ANDROID(TestFEC)) { +TEST(AudioCodingModuleTest, DISABLED_ON_ANDROID(TestRedFec)) { Trace::CreateTrace(); Trace::SetTraceFile((webrtc::test::OutputPath() + "acm_fec_trace.txt").c_str()); - webrtc::Config config; - - UseLegacyAcm(&config); - webrtc::TestFEC(config).Perform(); - - UseNewAcm(&config); - webrtc::TestFEC(config).Perform(); - + webrtc::TestRedFec().Perform(); Trace::ReturnTrace(); } @@ -84,14 +62,7 @@ Trace::CreateTrace(); Trace::SetTraceFile((webrtc::test::OutputPath() + "acm_isac_trace.txt").c_str()); - webrtc::Config config; - - UseLegacyAcm(&config); - webrtc::ISACTest(ACM_TEST_MODE, config).Perform(); - - UseNewAcm(&config); - webrtc::ISACTest(ACM_TEST_MODE, config).Perform(); - + webrtc::ISACTest(ACM_TEST_MODE).Perform(); Trace::ReturnTrace(); } @@ -99,14 +70,7 @@ Trace::CreateTrace(); Trace::SetTraceFile((webrtc::test::OutputPath() + "acm_twowaycom_trace.txt").c_str()); - webrtc::Config config; - - UseLegacyAcm(&config); - webrtc::TwoWayCommunication(ACM_TEST_MODE, config).Perform(); - - UseNewAcm(&config); - webrtc::TwoWayCommunication(ACM_TEST_MODE, config).Perform(); - + webrtc::TwoWayCommunication(ACM_TEST_MODE).Perform(); Trace::ReturnTrace(); } @@ -114,14 +78,7 @@ Trace::CreateTrace(); Trace::SetTraceFile((webrtc::test::OutputPath() + "acm_stereo_trace.txt").c_str()); - - webrtc::Config config; - UseLegacyAcm(&config); - - webrtc::TestStereo(ACM_TEST_MODE, config).Perform(); - UseNewAcm(&config); - - webrtc::TestStereo(ACM_TEST_MODE, config).Perform(); + webrtc::TestStereo(ACM_TEST_MODE).Perform(); Trace::ReturnTrace(); } @@ -129,14 +86,7 @@ Trace::CreateTrace(); Trace::SetTraceFile((webrtc::test::OutputPath() + "acm_vaddtx_trace.txt").c_str()); - webrtc::Config config; - - UseLegacyAcm(&config); - webrtc::TestVADDTX(config).Perform(); - - UseNewAcm(&config); - webrtc::TestVADDTX(config).Perform(); - + webrtc::TestVADDTX().Perform(); Trace::ReturnTrace(); } @@ -144,14 +94,39 @@ Trace::CreateTrace(); Trace::SetTraceFile((webrtc::test::OutputPath() + "acm_opus_trace.txt").c_str()); - webrtc::Config config; + webrtc::OpusTest().Perform(); + Trace::ReturnTrace(); +} + +TEST(AudioCodingModuleTest, TestPacketLoss) { + Trace::CreateTrace(); + Trace::SetTraceFile((webrtc::test::OutputPath() + + "acm_packetloss_trace.txt").c_str()); + webrtc::PacketLossTest(1, 10, 10, 1).Perform(); + Trace::ReturnTrace(); +} - UseLegacyAcm(&config); - webrtc::OpusTest(config).Perform(); +TEST(AudioCodingModuleTest, TestPacketLossBurst) { + Trace::CreateTrace(); + Trace::SetTraceFile((webrtc::test::OutputPath() + + "acm_packetloss_burst_trace.txt").c_str()); + webrtc::PacketLossTest(1, 10, 10, 2).Perform(); + Trace::ReturnTrace(); +} - UseNewAcm(&config); - webrtc::OpusTest(config).Perform(); +TEST(AudioCodingModuleTest, TestPacketLossStereo) { + Trace::CreateTrace(); + Trace::SetTraceFile((webrtc::test::OutputPath() + + "acm_packetloss_trace.txt").c_str()); + webrtc::PacketLossTest(2, 10, 10, 1).Perform(); + Trace::ReturnTrace(); +} +TEST(AudioCodingModuleTest, TestPacketLossStereoBurst) { + Trace::CreateTrace(); + Trace::SetTraceFile((webrtc::test::OutputPath() + + "acm_packetloss_burst_trace.txt").c_str()); + webrtc::PacketLossTest(2, 10, 10, 2).Perform(); Trace::ReturnTrace(); } @@ -162,14 +137,7 @@ Trace::CreateTrace(); Trace::SetTraceFile((webrtc::test::OutputPath() + "acm_apitest_trace.txt").c_str()); - webrtc::Config config; - - UseLegacyAcm(&config); - webrtc::APITest(config).Perform(); - - UseNewAcm(&config); - webrtc::APITest(config).Perform(); - + webrtc::APITest().Perform(); Trace::ReturnTrace(); } #endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestFEC.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestFEC.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestFEC.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestFEC.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,272 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/main/test/TestFEC.h" - -#include -#include - -#include "webrtc/common.h" -#include "webrtc/common_types.h" -#include "webrtc/engine_configurations.h" -#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" -#include "webrtc/modules/audio_coding/main/test/utility.h" -#include "webrtc/system_wrappers/interface/trace.h" -#include "webrtc/test/testsupport/fileutils.h" - -namespace webrtc { - -TestFEC::TestFEC(const Config& config) - : _acmA(config.Get().Create(0)), - _acmB(config.Get().Create(1)), - _channelA2B(NULL), - _testCntr(0) { -} - -TestFEC::~TestFEC() { - if (_channelA2B != NULL) { - delete _channelA2B; - _channelA2B = NULL; - } -} - -void TestFEC::Perform() { - const std::string file_name = webrtc::test::ResourcePath( - "audio_coding/testfile32kHz", "pcm"); - _inFileA.Open(file_name, 32000, "rb"); - - ASSERT_EQ(0, _acmA->InitializeReceiver()); - ASSERT_EQ(0, _acmB->InitializeReceiver()); - - uint8_t numEncoders = _acmA->NumberOfCodecs(); - CodecInst myCodecParam; - for (uint8_t n = 0; n < numEncoders; n++) { - EXPECT_EQ(0, _acmB->Codec(n, &myCodecParam)); - EXPECT_EQ(0, _acmB->RegisterReceiveCodec(myCodecParam)); - } - - // Create and connect the channel - _channelA2B = new Channel; - _acmA->RegisterTransportCallback(_channelA2B); - _channelA2B->RegisterReceiverACM(_acmB.get()); - -#ifndef WEBRTC_CODEC_G722 - EXPECT_TRUE(false); - printf("G722 needs to be activated to run this test\n"); - return; -#endif - char nameG722[] = "G722"; - EXPECT_EQ(0, RegisterSendCodec('A', nameG722, 16000)); - char nameCN[] = "CN"; - EXPECT_EQ(0, RegisterSendCodec('A', nameCN, 16000)); - char nameRED[] = "RED"; - EXPECT_EQ(0, RegisterSendCodec('A', nameRED)); - OpenOutFile(_testCntr); - EXPECT_EQ(0, SetVAD(true, true, VADAggr)); - EXPECT_EQ(0, _acmA->SetFECStatus(false)); - EXPECT_FALSE(_acmA->FECStatus()); - Run(); - _outFileB.Close(); - - EXPECT_EQ(0, _acmA->SetFECStatus(true)); - EXPECT_TRUE(_acmA->FECStatus()); - OpenOutFile(_testCntr); - Run(); - _outFileB.Close(); - - char nameISAC[] = "iSAC"; - RegisterSendCodec('A', nameISAC, 16000); - OpenOutFile(_testCntr); - EXPECT_EQ(0, SetVAD(true, true, VADVeryAggr)); - EXPECT_EQ(0, _acmA->SetFECStatus(false)); - EXPECT_FALSE(_acmA->FECStatus()); - Run(); - _outFileB.Close(); - - EXPECT_EQ(0, _acmA->SetFECStatus(true)); - EXPECT_TRUE(_acmA->FECStatus()); - OpenOutFile(_testCntr); - Run(); - _outFileB.Close(); - - RegisterSendCodec('A', nameISAC, 32000); - OpenOutFile(_testCntr); - EXPECT_EQ(0, SetVAD(true, true, VADVeryAggr)); - EXPECT_EQ(0, _acmA->SetFECStatus(false)); - EXPECT_FALSE(_acmA->FECStatus()); - Run(); - _outFileB.Close(); - - EXPECT_EQ(0, _acmA->SetFECStatus(true)); - EXPECT_TRUE(_acmA->FECStatus()); - OpenOutFile(_testCntr); - Run(); - _outFileB.Close(); - - RegisterSendCodec('A', nameISAC, 32000); - OpenOutFile(_testCntr); - EXPECT_EQ(0, SetVAD(false, false, VADNormal)); - EXPECT_EQ(0, _acmA->SetFECStatus(true)); - EXPECT_TRUE(_acmA->FECStatus()); - Run(); - - RegisterSendCodec('A', nameISAC, 16000); - EXPECT_TRUE(_acmA->FECStatus()); - Run(); - - RegisterSendCodec('A', nameISAC, 32000); - EXPECT_TRUE(_acmA->FECStatus()); - Run(); - - RegisterSendCodec('A', nameISAC, 16000); - EXPECT_TRUE(_acmA->FECStatus()); - Run(); - _outFileB.Close(); - - _channelA2B->SetFECTestWithPacketLoss(true); - - EXPECT_EQ(0, RegisterSendCodec('A', nameG722)); - EXPECT_EQ(0, RegisterSendCodec('A', nameCN, 16000)); - OpenOutFile(_testCntr); - EXPECT_EQ(0, SetVAD(true, true, VADAggr)); - EXPECT_EQ(0, _acmA->SetFECStatus(false)); - EXPECT_FALSE(_acmA->FECStatus()); - Run(); - _outFileB.Close(); - - EXPECT_EQ(0, _acmA->SetFECStatus(true)); - EXPECT_TRUE(_acmA->FECStatus()); - OpenOutFile(_testCntr); - Run(); - _outFileB.Close(); - - RegisterSendCodec('A', nameISAC, 16000); - OpenOutFile(_testCntr); - EXPECT_EQ(0, SetVAD(true, true, VADVeryAggr)); - EXPECT_EQ(0, _acmA->SetFECStatus(false)); - EXPECT_FALSE(_acmA->FECStatus()); - Run(); - _outFileB.Close(); - - EXPECT_EQ(0, _acmA->SetFECStatus(true)); - EXPECT_TRUE(_acmA->FECStatus()); - OpenOutFile(_testCntr); - Run(); - _outFileB.Close(); - - RegisterSendCodec('A', nameISAC, 32000); - OpenOutFile(_testCntr); - EXPECT_EQ(0, SetVAD(true, true, VADVeryAggr)); - EXPECT_EQ(0, _acmA->SetFECStatus(false)); - EXPECT_FALSE(_acmA->FECStatus()); - Run(); - _outFileB.Close(); - - EXPECT_EQ(0, _acmA->SetFECStatus(true)); - EXPECT_TRUE(_acmA->FECStatus()); - OpenOutFile(_testCntr); - Run(); - _outFileB.Close(); - - RegisterSendCodec('A', nameISAC, 32000); - OpenOutFile(_testCntr); - EXPECT_EQ(0, SetVAD(false, false, VADNormal)); - EXPECT_EQ(0, _acmA->SetFECStatus(true)); - EXPECT_TRUE(_acmA->FECStatus()); - Run(); - - RegisterSendCodec('A', nameISAC, 16000); - EXPECT_TRUE(_acmA->FECStatus()); - Run(); - - RegisterSendCodec('A', nameISAC, 32000); - EXPECT_TRUE(_acmA->FECStatus()); - Run(); - - RegisterSendCodec('A', nameISAC, 16000); - EXPECT_TRUE(_acmA->FECStatus()); - Run(); - _outFileB.Close(); -} - -int32_t TestFEC::SetVAD(bool enableDTX, bool enableVAD, ACMVADMode vadMode) { - return _acmA->SetVAD(enableDTX, enableVAD, vadMode); -} - -int16_t TestFEC::RegisterSendCodec(char side, char* codecName, - int32_t samplingFreqHz) { - std::cout << std::flush; - AudioCodingModule* myACM; - switch (side) { - case 'A': { - myACM = _acmA.get(); - break; - } - case 'B': { - myACM = _acmB.get(); - break; - } - default: - return -1; - } - - if (myACM == NULL) { - assert(false); - return -1; - } - CodecInst myCodecParam; - EXPECT_GT(AudioCodingModule::Codec(codecName, &myCodecParam, - samplingFreqHz, 1), -1); - EXPECT_GT(myACM->RegisterSendCodec(myCodecParam), -1); - - // Initialization was successful. - return 0; -} - -void TestFEC::Run() { - AudioFrame audioFrame; - - uint16_t msecPassed = 0; - uint32_t secPassed = 0; - int32_t outFreqHzB = _outFileB.SamplingFrequency(); - - while (!_inFileA.EndOfFile()) { - EXPECT_GT(_inFileA.Read10MsData(audioFrame), 0); - EXPECT_EQ(0, _acmA->Add10MsData(audioFrame)); - EXPECT_GT(_acmA->Process(), -1); - EXPECT_EQ(0, _acmB->PlayoutData10Ms(outFreqHzB, &audioFrame)); - _outFileB.Write10MsData(audioFrame.data_, audioFrame.samples_per_channel_); - msecPassed += 10; - if (msecPassed >= 1000) { - msecPassed = 0; - secPassed++; - } - // Test that toggling FEC on and off works. - if (((secPassed % 5) == 4) && (msecPassed == 0) && (_testCntr > 14)) { - EXPECT_EQ(0, _acmA->SetFECStatus(false)); - } - if (((secPassed % 5) == 4) && (msecPassed >= 990) && (_testCntr > 14)) { - EXPECT_EQ(0, _acmA->SetFECStatus(true)); - } - } - _inFileA.Rewind(); -} - -void TestFEC::OpenOutFile(int16_t test_number) { - std::string file_name; - std::stringstream file_stream; - file_stream << webrtc::test::OutputPath(); - file_stream << "TestFEC_outFile_"; - file_stream << test_number << ".pcm"; - file_name = file_stream.str(); - _outFileB.Open(file_name, 16000, "wb"); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestFEC.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestFEC.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestFEC.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestFEC.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_TESTFEC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_MAIN_TESTFEC_H_ - -#include "webrtc/modules/audio_coding/main/test/ACMTest.h" -#include "webrtc/modules/audio_coding/main/test/Channel.h" -#include "webrtc/modules/audio_coding/main/test/PCMFile.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" - -namespace webrtc { - -class Config; - -class TestFEC : public ACMTest { - public: - explicit TestFEC(const Config& config); - ~TestFEC(); - - void Perform(); - private: - // The default value of '-1' indicates that the registration is based only on - // codec name and a sampling frequency matching is not required. This is - // useful for codecs which support several sampling frequency. - int16_t RegisterSendCodec(char side, char* codecName, - int32_t sampFreqHz = -1); - void Run(); - void OpenOutFile(int16_t testNumber); - int32_t SetVAD(bool enableDTX, bool enableVAD, ACMVADMode vadMode); - scoped_ptr _acmA; - scoped_ptr _acmB; - - Channel* _channelA2B; - - PCMFile _inFileA; - PCMFile _outFileB; - int16_t _testCntr; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_TESTFEC_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestRedFec.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestRedFec.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestRedFec.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestRedFec.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/main/test/TestRedFec.h" + +#include + +#include "webrtc/common.h" +#include "webrtc/common_types.h" +#include "webrtc/engine_configurations.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" +#include "webrtc/modules/audio_coding/main/test/utility.h" +#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/test/testsupport/fileutils.h" + +namespace webrtc { + +TestRedFec::TestRedFec() + : _acmA(AudioCodingModule::Create(0)), + _acmB(AudioCodingModule::Create(1)), + _channelA2B(NULL), + _testCntr(0) { +} + +TestRedFec::~TestRedFec() { + if (_channelA2B != NULL) { + delete _channelA2B; + _channelA2B = NULL; + } +} + +void TestRedFec::Perform() { + const std::string file_name = webrtc::test::ResourcePath( + "audio_coding/testfile32kHz", "pcm"); + _inFileA.Open(file_name, 32000, "rb"); + + ASSERT_EQ(0, _acmA->InitializeReceiver()); + ASSERT_EQ(0, _acmB->InitializeReceiver()); + + uint8_t numEncoders = _acmA->NumberOfCodecs(); + CodecInst myCodecParam; + for (uint8_t n = 0; n < numEncoders; n++) { + EXPECT_EQ(0, _acmB->Codec(n, &myCodecParam)); + // Default number of channels is 2 for opus, so we change to 1 in this test. + if (!strcmp(myCodecParam.plname, "opus")) { + myCodecParam.channels = 1; + } + EXPECT_EQ(0, _acmB->RegisterReceiveCodec(myCodecParam)); + } + + // Create and connect the channel + _channelA2B = new Channel; + _acmA->RegisterTransportCallback(_channelA2B); + _channelA2B->RegisterReceiverACM(_acmB.get()); + +#ifndef WEBRTC_CODEC_G722 + EXPECT_TRUE(false); + printf("G722 needs to be activated to run this test\n"); + return; +#endif + char nameG722[] = "G722"; + EXPECT_EQ(0, RegisterSendCodec('A', nameG722, 16000)); + char nameCN[] = "CN"; + EXPECT_EQ(0, RegisterSendCodec('A', nameCN, 16000)); + char nameRED[] = "RED"; + EXPECT_EQ(0, RegisterSendCodec('A', nameRED)); + OpenOutFile(_testCntr); + EXPECT_EQ(0, SetVAD(true, true, VADAggr)); + EXPECT_EQ(0, _acmA->SetREDStatus(false)); + EXPECT_FALSE(_acmA->REDStatus()); + Run(); + _outFileB.Close(); + + EXPECT_EQ(0, _acmA->SetREDStatus(true)); + EXPECT_TRUE(_acmA->REDStatus()); + OpenOutFile(_testCntr); + Run(); + _outFileB.Close(); + + char nameISAC[] = "iSAC"; + RegisterSendCodec('A', nameISAC, 16000); + OpenOutFile(_testCntr); + EXPECT_EQ(0, SetVAD(true, true, VADVeryAggr)); + EXPECT_EQ(0, _acmA->SetREDStatus(false)); + EXPECT_FALSE(_acmA->REDStatus()); + Run(); + _outFileB.Close(); + + EXPECT_EQ(0, _acmA->SetREDStatus(true)); + EXPECT_TRUE(_acmA->REDStatus()); + OpenOutFile(_testCntr); + Run(); + _outFileB.Close(); + + RegisterSendCodec('A', nameISAC, 32000); + OpenOutFile(_testCntr); + EXPECT_EQ(0, SetVAD(true, true, VADVeryAggr)); + EXPECT_EQ(0, _acmA->SetREDStatus(false)); + EXPECT_FALSE(_acmA->REDStatus()); + Run(); + _outFileB.Close(); + + EXPECT_EQ(0, _acmA->SetREDStatus(true)); + EXPECT_TRUE(_acmA->REDStatus()); + OpenOutFile(_testCntr); + Run(); + _outFileB.Close(); + + RegisterSendCodec('A', nameISAC, 32000); + OpenOutFile(_testCntr); + EXPECT_EQ(0, SetVAD(false, false, VADNormal)); + EXPECT_EQ(0, _acmA->SetREDStatus(true)); + EXPECT_TRUE(_acmA->REDStatus()); + Run(); + + RegisterSendCodec('A', nameISAC, 16000); + EXPECT_TRUE(_acmA->REDStatus()); + Run(); + + RegisterSendCodec('A', nameISAC, 32000); + EXPECT_TRUE(_acmA->REDStatus()); + Run(); + + RegisterSendCodec('A', nameISAC, 16000); + EXPECT_TRUE(_acmA->REDStatus()); + Run(); + _outFileB.Close(); + + _channelA2B->SetFECTestWithPacketLoss(true); + + EXPECT_EQ(0, RegisterSendCodec('A', nameG722)); + EXPECT_EQ(0, RegisterSendCodec('A', nameCN, 16000)); + OpenOutFile(_testCntr); + EXPECT_EQ(0, SetVAD(true, true, VADAggr)); + EXPECT_EQ(0, _acmA->SetREDStatus(false)); + EXPECT_FALSE(_acmA->REDStatus()); + Run(); + _outFileB.Close(); + + EXPECT_EQ(0, _acmA->SetREDStatus(true)); + EXPECT_TRUE(_acmA->REDStatus()); + OpenOutFile(_testCntr); + Run(); + _outFileB.Close(); + + RegisterSendCodec('A', nameISAC, 16000); + OpenOutFile(_testCntr); + EXPECT_EQ(0, SetVAD(true, true, VADVeryAggr)); + EXPECT_EQ(0, _acmA->SetREDStatus(false)); + EXPECT_FALSE(_acmA->REDStatus()); + Run(); + _outFileB.Close(); + + EXPECT_EQ(0, _acmA->SetREDStatus(true)); + EXPECT_TRUE(_acmA->REDStatus()); + OpenOutFile(_testCntr); + Run(); + _outFileB.Close(); + + RegisterSendCodec('A', nameISAC, 32000); + OpenOutFile(_testCntr); + EXPECT_EQ(0, SetVAD(true, true, VADVeryAggr)); + EXPECT_EQ(0, _acmA->SetREDStatus(false)); + EXPECT_FALSE(_acmA->REDStatus()); + Run(); + _outFileB.Close(); + + EXPECT_EQ(0, _acmA->SetREDStatus(true)); + EXPECT_TRUE(_acmA->REDStatus()); + OpenOutFile(_testCntr); + Run(); + _outFileB.Close(); + + RegisterSendCodec('A', nameISAC, 32000); + OpenOutFile(_testCntr); + EXPECT_EQ(0, SetVAD(false, false, VADNormal)); + EXPECT_EQ(0, _acmA->SetREDStatus(true)); + EXPECT_TRUE(_acmA->REDStatus()); + Run(); + + RegisterSendCodec('A', nameISAC, 16000); + EXPECT_TRUE(_acmA->REDStatus()); + Run(); + + RegisterSendCodec('A', nameISAC, 32000); + EXPECT_TRUE(_acmA->REDStatus()); + Run(); + + RegisterSendCodec('A', nameISAC, 16000); + EXPECT_TRUE(_acmA->REDStatus()); + Run(); + _outFileB.Close(); + +#ifndef WEBRTC_CODEC_OPUS + EXPECT_TRUE(false); + printf("Opus needs to be activated to run this test\n"); + return; +#endif + + char nameOpus[] = "opus"; + RegisterSendCodec('A', nameOpus, 48000); + + EXPECT_TRUE(_acmA->REDStatus()); + + // _channelA2B imposes 25% packet loss rate. + EXPECT_EQ(0, _acmA->SetPacketLossRate(25)); + + // Codec FEC and RED are mutually exclusive. + EXPECT_EQ(-1, _acmA->SetCodecFEC(true)); + + EXPECT_EQ(0, _acmA->SetREDStatus(false)); + EXPECT_EQ(0, _acmA->SetCodecFEC(true)); + + // Codec FEC and RED are mutually exclusive. + EXPECT_EQ(-1, _acmA->SetREDStatus(true)); + + EXPECT_TRUE(_acmA->CodecFEC()); + OpenOutFile(_testCntr); + Run(); + + // Switch to ISAC with RED. + RegisterSendCodec('A', nameISAC, 32000); + EXPECT_EQ(0, SetVAD(false, false, VADNormal)); + + // ISAC does not support FEC, so FEC should be turned off automatically. + EXPECT_FALSE(_acmA->CodecFEC()); + + EXPECT_EQ(0, _acmA->SetREDStatus(true)); + EXPECT_TRUE(_acmA->REDStatus()); + Run(); + + // Switch to Opus again. + RegisterSendCodec('A', nameOpus, 48000); + EXPECT_EQ(0, _acmA->SetCodecFEC(false)); + EXPECT_EQ(0, _acmA->SetREDStatus(false)); + Run(); + + EXPECT_EQ(0, _acmA->SetCodecFEC(true)); + _outFileB.Close(); + + // Codecs does not support internal FEC, cannot enable FEC. + RegisterSendCodec('A', nameG722, 16000); + EXPECT_FALSE(_acmA->REDStatus()); + EXPECT_EQ(-1, _acmA->SetCodecFEC(true)); + EXPECT_FALSE(_acmA->CodecFEC()); + + RegisterSendCodec('A', nameISAC, 16000); + EXPECT_FALSE(_acmA->REDStatus()); + EXPECT_EQ(-1, _acmA->SetCodecFEC(true)); + EXPECT_FALSE(_acmA->CodecFEC()); + + // Codecs does not support internal FEC, disable FEC does not trigger failure. + RegisterSendCodec('A', nameG722, 16000); + EXPECT_FALSE(_acmA->REDStatus()); + EXPECT_EQ(0, _acmA->SetCodecFEC(false)); + EXPECT_FALSE(_acmA->CodecFEC()); + + RegisterSendCodec('A', nameISAC, 16000); + EXPECT_FALSE(_acmA->REDStatus()); + EXPECT_EQ(0, _acmA->SetCodecFEC(false)); + EXPECT_FALSE(_acmA->CodecFEC()); +} + +int32_t TestRedFec::SetVAD(bool enableDTX, bool enableVAD, ACMVADMode vadMode) { + return _acmA->SetVAD(enableDTX, enableVAD, vadMode); +} + +int16_t TestRedFec::RegisterSendCodec(char side, char* codecName, + int32_t samplingFreqHz) { + std::cout << std::flush; + AudioCodingModule* myACM; + switch (side) { + case 'A': { + myACM = _acmA.get(); + break; + } + case 'B': { + myACM = _acmB.get(); + break; + } + default: + return -1; + } + + if (myACM == NULL) { + assert(false); + return -1; + } + CodecInst myCodecParam; + EXPECT_GT(AudioCodingModule::Codec(codecName, &myCodecParam, + samplingFreqHz, 1), -1); + EXPECT_GT(myACM->RegisterSendCodec(myCodecParam), -1); + + // Initialization was successful. + return 0; +} + +void TestRedFec::Run() { + AudioFrame audioFrame; + + uint16_t msecPassed = 0; + uint32_t secPassed = 0; + int32_t outFreqHzB = _outFileB.SamplingFrequency(); + + while (!_inFileA.EndOfFile()) { + EXPECT_GT(_inFileA.Read10MsData(audioFrame), 0); + EXPECT_EQ(0, _acmA->Add10MsData(audioFrame)); + EXPECT_GT(_acmA->Process(), -1); + EXPECT_EQ(0, _acmB->PlayoutData10Ms(outFreqHzB, &audioFrame)); + _outFileB.Write10MsData(audioFrame.data_, audioFrame.samples_per_channel_); + msecPassed += 10; + if (msecPassed >= 1000) { + msecPassed = 0; + secPassed++; + } + // Test that toggling RED on and off works. + if (((secPassed % 5) == 4) && (msecPassed == 0) && (_testCntr > 14)) { + EXPECT_EQ(0, _acmA->SetREDStatus(false)); + } + if (((secPassed % 5) == 4) && (msecPassed >= 990) && (_testCntr > 14)) { + EXPECT_EQ(0, _acmA->SetREDStatus(true)); + } + } + _inFileA.Rewind(); +} + +void TestRedFec::OpenOutFile(int16_t test_number) { + std::string file_name; + std::stringstream file_stream; + file_stream << webrtc::test::OutputPath(); + file_stream << "TestRedFec_outFile_"; + file_stream << test_number << ".pcm"; + file_name = file_stream.str(); + _outFileB.Open(file_name, 16000, "wb"); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestRedFec.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestRedFec.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestRedFec.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestRedFec.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_TESTREDFEC_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_TESTREDFEC_H_ + +#include +#include "webrtc/modules/audio_coding/main/test/ACMTest.h" +#include "webrtc/modules/audio_coding/main/test/Channel.h" +#include "webrtc/modules/audio_coding/main/test/PCMFile.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class Config; + +class TestRedFec : public ACMTest { + public: + explicit TestRedFec(); + ~TestRedFec(); + + void Perform(); + private: + // The default value of '-1' indicates that the registration is based only on + // codec name and a sampling frequency matching is not required. This is + // useful for codecs which support several sampling frequency. + int16_t RegisterSendCodec(char side, char* codecName, + int32_t sampFreqHz = -1); + void Run(); + void OpenOutFile(int16_t testNumber); + int32_t SetVAD(bool enableDTX, bool enableVAD, ACMVADMode vadMode); + scoped_ptr _acmA; + scoped_ptr _acmB; + + Channel* _channelA2B; + + PCMFile _inFileA; + PCMFile _outFileB; + int16_t _testCntr; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_TESTREDFEC_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestStereo.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestStereo.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestStereo.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestStereo.cc 2015-02-03 14:33:34.000000000 +0000 @@ -14,8 +14,7 @@ #include -#include "gtest/gtest.h" -#include "webrtc/common.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/common_types.h" #include "webrtc/engine_configurations.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" @@ -76,7 +75,7 @@ rtp_info); if (frame_type != kAudioFrameCN) { - payload_size_ = payload_size; + payload_size_ = static_cast(payload_size); } else { payload_size_ = -1; } @@ -89,7 +88,7 @@ } uint16_t TestPackStereo::payload_size() { - return payload_size_; + return static_cast(payload_size_); } uint32_t TestPackStereo::timestamp_diff() { @@ -108,9 +107,9 @@ lost_packet_ = lost; } -TestStereo::TestStereo(int test_mode, const Config& config) - : acm_a_(config.Get().Create(0)), - acm_b_(config.Get().Create(1)), +TestStereo::TestStereo(int test_mode) + : acm_a_(AudioCodingModule::Create(0)), + acm_b_(AudioCodingModule::Create(1)), channel_a2b_(NULL), test_cntr_(0), pack_size_samp_(0), @@ -808,6 +807,8 @@ uint32_t time_stamp_diff; channel->reset_payload_size(); int error_count = 0; + int variable_bytes = 0; + int variable_packets = 0; while (1) { // Simulate packet loss by setting |packet_loss_| to "true" in @@ -839,11 +840,16 @@ // Run sender side of ACM EXPECT_GT(acm_a_->Process(), -1); - // Verify that the received packet size matches the settings + // Verify that the received packet size matches the settings. rec_size = channel->payload_size(); if ((0 < rec_size) & (rec_size < 65535)) { - // Opus is variable rate, skip this test. - if (strcmp(send_codec_name_, "opus")) { + if (strcmp(send_codec_name_, "opus") == 0) { + // Opus is a variable rate codec, hence calculate the average packet + // size, and later make sure the average is in the right range. + variable_bytes += rec_size; + variable_packets++; + } else { + // For fixed rate codecs, check that packet size is correct. if ((rec_size != pack_size_bytes_ * out_channels) && (pack_size_bytes_ < 65535)) { error_count++; @@ -867,6 +873,13 @@ EXPECT_EQ(0, error_count); + // Check that packet size is in the right range for variable rate codecs, + // such as Opus. + if (variable_packets > 0) { + variable_bytes /= variable_packets; + EXPECT_NEAR(variable_bytes, pack_size_bytes_, 3); + } + if (in_file_mono_->EndOfFile()) { in_file_mono_->Rewind(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestStereo.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestStereo.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestStereo.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestStereo.h 2015-02-03 14:33:34.000000000 +0000 @@ -20,8 +20,6 @@ namespace webrtc { -class Config; - enum StereoMonoMode { kNotSet, kMono, @@ -35,12 +33,13 @@ void RegisterReceiverACM(AudioCodingModule* acm); - virtual int32_t SendData(const FrameType frame_type, - const uint8_t payload_type, - const uint32_t timestamp, - const uint8_t* payload_data, - const uint16_t payload_size, - const RTPFragmentationHeader* fragmentation); + virtual int32_t SendData( + const FrameType frame_type, + const uint8_t payload_type, + const uint32_t timestamp, + const uint8_t* payload_data, + const uint16_t payload_size, + const RTPFragmentationHeader* fragmentation) OVERRIDE; uint16_t payload_size(); uint32_t timestamp_diff(); @@ -54,7 +53,7 @@ uint32_t timestamp_diff_; uint32_t last_in_timestamp_; uint64_t total_bytes_; - uint16_t payload_size_; + int payload_size_; StereoMonoMode codec_mode_; // Simulate packet losses bool lost_packet_; @@ -62,10 +61,10 @@ class TestStereo : public ACMTest { public: - TestStereo(int test_mode, const Config& config); + explicit TestStereo(int test_mode); ~TestStereo(); - void Perform(); + virtual void Perform() OVERRIDE; private: // The default value of '-1' indicates that the registration is based only on // codec name and a sampling frequncy matching is not required. This is useful diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestVADDTX.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestVADDTX.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestVADDTX.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestVADDTX.cc 2015-02-03 14:33:34.000000000 +0000 @@ -12,7 +12,6 @@ #include -#include "webrtc/common.h" #include "webrtc/common_types.h" #include "webrtc/engine_configurations.h" #include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" @@ -23,11 +22,10 @@ namespace webrtc { -TestVADDTX::TestVADDTX(const Config& config) - : _acmA(config.Get().Create(0)), - _acmB(config.Get().Create(1)), - _channelA2B(NULL) { -} +TestVADDTX::TestVADDTX() + : _acmA(AudioCodingModule::Create(0)), + _acmB(AudioCodingModule::Create(1)), + _channelA2B(NULL) {} TestVADDTX::~TestVADDTX() { if (_channelA2B != NULL) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestVADDTX.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestVADDTX.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestVADDTX.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TestVADDTX.h 2015-02-03 14:33:34.000000000 +0000 @@ -18,8 +18,6 @@ namespace webrtc { -class Config; - typedef struct { bool statusDTX; bool statusVAD; @@ -49,7 +47,7 @@ class TestVADDTX : public ACMTest { public: - explicit TestVADDTX(const Config& config); + TestVADDTX(); ~TestVADDTX(); void Perform(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TimedTrace.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TimedTrace.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TimedTrace.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TimedTrace.h 2015-02-03 14:33:34.000000000 +0000 @@ -11,7 +11,7 @@ #ifndef TIMED_TRACE_H #define TIMED_TRACE_H -#include "typedefs.h" +#include "webrtc/typedefs.h" #include #include diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TwoWayCommunication.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TwoWayCommunication.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TwoWayCommunication.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TwoWayCommunication.cc 2015-02-03 14:33:34.000000000 +0000 @@ -18,9 +18,8 @@ #include #endif -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/engine_configurations.h" -#include "webrtc/common.h" #include "webrtc/common_types.h" #include "webrtc/modules/audio_coding/main/test/PCMFile.h" #include "webrtc/modules/audio_coding/main/test/utility.h" @@ -31,12 +30,12 @@ #define MAX_FILE_NAME_LENGTH_BYTE 500 -TwoWayCommunication::TwoWayCommunication(int testMode, const Config& config) - : _acmA(config.Get().Create(1)), - _acmB(config.Get().Create(2)), - _acmRefA(config.Get().Create(3)), - _acmRefB(config.Get().Create(4)), - _testMode(testMode) { } +TwoWayCommunication::TwoWayCommunication(int testMode) + : _acmA(AudioCodingModule::Create(1)), + _acmB(AudioCodingModule::Create(2)), + _acmRefA(AudioCodingModule::Create(3)), + _acmRefB(AudioCodingModule::Create(4)), + _testMode(testMode) {} TwoWayCommunication::~TwoWayCommunication() { delete _channel_A2B; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TwoWayCommunication.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TwoWayCommunication.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TwoWayCommunication.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/TwoWayCommunication.h 2015-02-03 14:33:34.000000000 +0000 @@ -20,11 +20,9 @@ namespace webrtc { -class Config; - class TwoWayCommunication : public ACMTest { public: - TwoWayCommunication(int testMode, const Config& config); + explicit TwoWayCommunication(int testMode); ~TwoWayCommunication(); void Perform(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/utility.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/utility.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/utility.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/main/test/utility.cc 2015-02-03 14:33:34.000000000 +0000 @@ -330,14 +330,4 @@ return 0; } -void UseLegacyAcm(webrtc::Config* config) { - config->Set( - new webrtc::AudioCodingModuleFactory()); -} - -void UseNewAcm(webrtc::Config* config) { - config->Set( - new webrtc::NewAudioCodingModuleFactory()); -} - } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/accelerate.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/accelerate.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/accelerate.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/accelerate.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,493 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the Accelerate algorithm that is used to reduce - * the delay by removing a part of the audio stream. - */ - -#include "dsp.h" - -#include "signal_processing_library.h" - -#include "dsp_helpfunctions.h" -#include "neteq_error_codes.h" - -#define ACCELERATE_CORR_LEN 50 -#define ACCELERATE_MIN_LAG 10 -#define ACCELERATE_MAX_LAG 60 -#define ACCELERATE_DOWNSAMPLED_LEN (ACCELERATE_CORR_LEN + ACCELERATE_MAX_LAG) - -/* Scratch usage: - - Type Name size startpos endpos - int16_t pw16_downSampSpeech 110 0 109 - int32_t pw32_corr 2*50 110 209 - int16_t pw16_corr 50 0 49 - - Total: 110+2*50 - */ - -#define SCRATCH_PW16_DS_SPEECH 0 -#define SCRATCH_PW32_CORR ACCELERATE_DOWNSAMPLED_LEN -#define SCRATCH_PW16_CORR 0 - -/**************************************************************************** - * WebRtcNetEQ_Accelerate(...) - * - * This function tries to shorten the audio data by removing one or several - * pitch periods. The operation is only carried out if the correlation is - * strong or if the signal energy is very low. - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector. - * - decoded : Pointer to newly decoded speech. - * - len : Length of decoded speech. - * - BGNonly : If non-zero, Accelerate will only remove the last - * DEFAULT_TIME_ADJUST seconds of the input. - * No signal matching is done. - * - * Output: - * - inst : Updated instance - * - outData : Pointer to a memory space where the output data - * should be stored - * - pw16_len : Number of samples written to outData. - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_Accelerate(DSPInst_t *inst, -#ifdef SCRATCH - int16_t *pw16_scratchPtr, -#endif - const int16_t *pw16_decoded, int len, - int16_t *pw16_outData, int16_t *pw16_len, - int16_t BGNonly) -{ - -#ifdef SCRATCH - /* Use scratch memory for internal temporary vectors */ - int16_t *pw16_downSampSpeech = pw16_scratchPtr + SCRATCH_PW16_DS_SPEECH; - int32_t *pw32_corr = (int32_t*) (pw16_scratchPtr + SCRATCH_PW32_CORR); - int16_t *pw16_corr = pw16_scratchPtr + SCRATCH_PW16_CORR; -#else - /* Allocate memory for temporary vectors */ - int16_t pw16_downSampSpeech[ACCELERATE_DOWNSAMPLED_LEN]; - int32_t pw32_corr[ACCELERATE_CORR_LEN]; - int16_t pw16_corr[ACCELERATE_CORR_LEN]; -#endif - int16_t w16_decodedMax = 0; - int16_t w16_tmp; - int16_t w16_tmp2; - int32_t w32_tmp; - int32_t w32_tmp2; - - const int16_t w16_startLag = ACCELERATE_MIN_LAG; - const int16_t w16_endLag = ACCELERATE_MAX_LAG; - const int16_t w16_corrLen = ACCELERATE_CORR_LEN; - const int16_t *pw16_vec1, *pw16_vec2; - int16_t *pw16_vectmp; - int16_t w16_inc, w16_startfact; - int16_t w16_bestIndex, w16_bestVal; - int16_t w16_VAD = 1; - int16_t fsMult; - int16_t fsMult120; - int32_t w32_en1, w32_en2, w32_cc; - int16_t w16_en1, w16_en2; - int16_t w16_en1Scale, w16_en2Scale; - int16_t w16_sqrtEn1En2; - int16_t w16_bestCorr = 0; - int ok; - -#ifdef NETEQ_STEREO - MasterSlaveInfo *msInfo = inst->msInfo; -#endif - - fsMult = WebRtcNetEQ_CalcFsMult(inst->fs); /* Calculate fs/8000 */ - - /* Pre-calculate common multiplication with fsMult */ - fsMult120 = (int16_t) WEBRTC_SPL_MUL_16_16(fsMult, 120); /* 15 ms */ - - inst->ExpandInst.w16_consecExp = 0; /* Last was not expand any more */ - - /* Sanity check for len variable; must be (almost) 30 ms - (120*fsMult + max(bestIndex)) */ - if (len < (int16_t) WEBRTC_SPL_MUL_16_16((120 + 119), fsMult)) - { - /* Length of decoded data too short */ - inst->w16_mode = MODE_UNSUCCESS_ACCELERATE; - *pw16_len = len; - - /* simply move all data from decoded to outData */ - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, (int16_t) len); - - return NETEQ_OTHER_ERROR; - } - - /***********************************/ - /* Special operations for BGN only */ - /***********************************/ - - /* Check if "background noise only" flag is set */ - if (BGNonly) - { - /* special operation for BGN only; simply remove a chunk of data */ - w16_bestIndex = DEFAULT_TIME_ADJUST * WEBRTC_SPL_LSHIFT_W16(fsMult, 3); /* X*fs/1000 */ - - /* Sanity check for bestIndex */ - if (w16_bestIndex > len) - { /* not good, do nothing instead */ - inst->w16_mode = MODE_UNSUCCESS_ACCELERATE; - *pw16_len = len; - - /* simply move all data from decoded to outData */ - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, (int16_t) len); - - return NETEQ_OTHER_ERROR; - } - - /* set length parameter */ - *pw16_len = len - w16_bestIndex; /* we remove bestIndex samples */ - - /* copy to output */ - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, *pw16_len); - - /* set mode */ - inst->w16_mode = MODE_LOWEN_ACCELERATE; - - /* update statistics */ - inst->statInst.accelerateLength += w16_bestIndex; - /* Short-term activity statistics. */ - inst->activity_stats.accelerate_bgn_samples += w16_bestIndex; - - return 0; - } /* end of special code for BGN mode */ - -#ifdef NETEQ_STEREO - - /* Sanity for msInfo */ - if (msInfo == NULL) - { - /* this should not happen here */ - return MASTER_SLAVE_ERROR; - } - - if (msInfo->msMode != NETEQ_SLAVE) - { - /* Find correlation lag only for non-slave instances */ - -#endif - - /****************************************************************/ - /* Find the strongest correlation lag by downsampling to 4 kHz, */ - /* calculating correlation for downsampled signal and finding */ - /* the strongest correlation peak. */ - /****************************************************************/ - - /* find maximum absolute value */ - w16_decodedMax = WebRtcSpl_MaxAbsValueW16(pw16_decoded, (int16_t) len); - - /* downsample the decoded speech to 4 kHz */ - ok = WebRtcNetEQ_DownSampleTo4kHz(pw16_decoded, len, inst->fs, pw16_downSampSpeech, - ACCELERATE_DOWNSAMPLED_LEN, 1 /* compensate delay*/); - if (ok != 0) - { - /* error */ - inst->w16_mode = MODE_UNSUCCESS_ACCELERATE; - *pw16_len = len; - /* simply move all data from decoded to outData */ - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, (int16_t) len); - return NETEQ_OTHER_ERROR; - } - - /* - * Set scaling factor for cross correlation to protect against overflow - * (log2(50) => 6) - */ - w16_tmp = 6 - WebRtcSpl_NormW32(WEBRTC_SPL_MUL_16_16(w16_decodedMax, w16_decodedMax)); - w16_tmp = WEBRTC_SPL_MAX(0, w16_tmp); - - /* Perform correlation from lag 10 to lag 60 in 4 kHz domain */ - WebRtcNetEQ_CrossCorr( - pw32_corr, &pw16_downSampSpeech[w16_endLag], - &pw16_downSampSpeech[w16_endLag - w16_startLag], w16_corrLen, - (int16_t) (w16_endLag - w16_startLag), w16_tmp, -1); - - /* Normalize correlation to 14 bits and put in a int16_t vector */ - w32_tmp = WebRtcSpl_MaxAbsValueW32(pw32_corr, w16_corrLen); - w16_tmp = 17 - WebRtcSpl_NormW32(w32_tmp); - w16_tmp = WEBRTC_SPL_MAX(0, w16_tmp); - - WebRtcSpl_VectorBitShiftW32ToW16(pw16_corr, w16_corrLen, pw32_corr, w16_tmp); - -#ifdef NETEQ_STEREO - } /* end if (msInfo->msMode != NETEQ_SLAVE) */ - - if ((msInfo->msMode == NETEQ_MASTER) || (msInfo->msMode == NETEQ_MONO)) - { - /* Find the strongest correlation peak by using the parabolic fit method */ - WebRtcNetEQ_PeakDetection(pw16_corr, (int16_t) w16_corrLen, 1, fsMult, - &w16_bestIndex, &w16_bestVal); - /* 0 <= bestIndex <= (2*corrLen - 1)*fsMult = 99*fsMult */ - - /* Compensate bestIndex for displaced starting position */ - w16_bestIndex = w16_bestIndex + w16_startLag * WEBRTC_SPL_LSHIFT_W16(fsMult, 1); - /* 20*fsMult <= bestIndex <= 119*fsMult */ - - msInfo->bestIndex = w16_bestIndex; - } - else if (msInfo->msMode == NETEQ_SLAVE) - { - if (msInfo->extraInfo == ACC_FAIL) - { - /* Master has signaled an unsuccessful accelerate */ - w16_bestIndex = 0; - } - else - { - /* Get best index from master */ - w16_bestIndex = msInfo->bestIndex; - } - } - else - { - /* Invalid mode */ - return MASTER_SLAVE_ERROR; - } - -#else /* NETEQ_STEREO */ - - /* Find the strongest correlation peak by using the parabolic fit method */ - WebRtcNetEQ_PeakDetection(pw16_corr, (int16_t) w16_corrLen, 1, fsMult, - &w16_bestIndex, &w16_bestVal); - /* 0 <= bestIndex <= (2*corrLen - 1)*fsMult = 99*fsMult */ - - /* Compensate bestIndex for displaced starting position */ - w16_bestIndex = w16_bestIndex + w16_startLag * WEBRTC_SPL_LSHIFT_W16(fsMult, 1); - /* 20*fsMult <= bestIndex <= 119*fsMult */ - -#endif /* NETEQ_STEREO */ - -#ifdef NETEQ_STEREO - - if (msInfo->msMode != NETEQ_SLAVE) - { - /* Calculate correlation only for non-slave instances */ - -#endif /* NETEQ_STEREO */ - - /*****************************************************/ - /* Calculate correlation bestCorr for the found lag. */ - /* Also do a simple VAD decision. */ - /*****************************************************/ - - /* - * Calculate scaling to ensure that bestIndex samples can be square-summed - * without overflowing - */ - w16_tmp = (31 - - WebRtcSpl_NormW32(WEBRTC_SPL_MUL_16_16(w16_decodedMax, w16_decodedMax))); - w16_tmp += (31 - WebRtcSpl_NormW32(w16_bestIndex)); - w16_tmp -= 31; - w16_tmp = WEBRTC_SPL_MAX(0, w16_tmp); - - /* vec1 starts at 15 ms minus one pitch period */ - pw16_vec1 = &pw16_decoded[fsMult120 - w16_bestIndex]; - /* vec2 start at 15 ms */ - pw16_vec2 = &pw16_decoded[fsMult120]; - - /* Calculate energies for vec1 and vec2 */ - w32_en1 = WebRtcNetEQ_DotW16W16((int16_t*) pw16_vec1, - (int16_t*) pw16_vec1, w16_bestIndex, w16_tmp); - w32_en2 = WebRtcNetEQ_DotW16W16((int16_t*) pw16_vec2, - (int16_t*) pw16_vec2, w16_bestIndex, w16_tmp); - - /* Calculate cross-correlation at the found lag */ - w32_cc = WebRtcNetEQ_DotW16W16((int16_t*) pw16_vec1, (int16_t*) pw16_vec2, - w16_bestIndex, w16_tmp); - - /* Check VAD constraint - ((en1+en2)/(2*bestIndex)) <= 8*inst->BGNInst.energy */ - w32_tmp = WEBRTC_SPL_RSHIFT_W32(w32_en1 + w32_en2, 4); /* (en1+en2)/(2*8) */ - if (inst->BGNInst.w16_initialized == 1) - { - w32_tmp2 = inst->BGNInst.w32_energy; - } - else - { - /* if BGN parameters have not been estimated, use a fixed threshold */ - w32_tmp2 = 75000; - } - w16_tmp2 = 16 - WebRtcSpl_NormW32(w32_tmp2); - w16_tmp2 = WEBRTC_SPL_MAX(0, w16_tmp2); - w32_tmp = WEBRTC_SPL_RSHIFT_W32(w32_tmp, w16_tmp2); - w16_tmp2 = (int16_t) WEBRTC_SPL_RSHIFT_W32(w32_tmp2, w16_tmp2); - w32_tmp2 = WEBRTC_SPL_MUL_16_16(w16_bestIndex, w16_tmp2); - - /* Scale w32_tmp properly before comparing with w32_tmp2 */ - /* (w16_tmp is scaling before energy calculation, thus 2*w16_tmp) */ - if (WebRtcSpl_NormW32(w32_tmp) < WEBRTC_SPL_LSHIFT_W32(w16_tmp,1)) - { - /* Cannot scale only w32_tmp, must scale w32_temp2 too */ - int16_t tempshift = WebRtcSpl_NormW32(w32_tmp); - w32_tmp = WEBRTC_SPL_LSHIFT_W32(w32_tmp, tempshift); - w32_tmp2 = WEBRTC_SPL_RSHIFT_W32(w32_tmp2, - WEBRTC_SPL_LSHIFT_W32(w16_tmp,1) - tempshift); - } - else - { - w32_tmp = WEBRTC_SPL_LSHIFT_W32(w32_tmp, - WEBRTC_SPL_LSHIFT_W32(w16_tmp,1)); - } - - if (w32_tmp <= w32_tmp2) /*((en1+en2)/(2*bestIndex)) <= 8*inst->BGNInst.energy */ - { - /* The signal seems to be passive speech */ - w16_VAD = 0; - w16_bestCorr = 0; /* Correlation does not matter */ - } - else - { - /* The signal is active speech */ - w16_VAD = 1; - - /* Calculate correlation (cc/sqrt(en1*en2)) */ - - /* Start with calculating scale values */ - w16_en1Scale = 16 - WebRtcSpl_NormW32(w32_en1); - w16_en1Scale = WEBRTC_SPL_MAX(0, w16_en1Scale); - w16_en2Scale = 16 - WebRtcSpl_NormW32(w32_en2); - w16_en2Scale = WEBRTC_SPL_MAX(0, w16_en2Scale); - - /* Make sure total scaling is even (to simplify scale factor after sqrt) */ - if ((w16_en1Scale + w16_en2Scale) & 1) - { - w16_en1Scale += 1; - } - - /* Convert energies to int16_t */ - w16_en1 = (int16_t) WEBRTC_SPL_RSHIFT_W32(w32_en1, w16_en1Scale); - w16_en2 = (int16_t) WEBRTC_SPL_RSHIFT_W32(w32_en2, w16_en2Scale); - - /* Calculate energy product */ - w32_tmp = WEBRTC_SPL_MUL_16_16(w16_en1, w16_en2); - - /* Calculate square-root of energy product */ - w16_sqrtEn1En2 = (int16_t) WebRtcSpl_SqrtFloor(w32_tmp); - - /* Calculate cc/sqrt(en1*en2) in Q14 */ - w16_tmp = 14 - WEBRTC_SPL_RSHIFT_W16(w16_en1Scale+w16_en2Scale, 1); - w32_cc = WEBRTC_SPL_SHIFT_W32(w32_cc, w16_tmp); - w32_cc = WEBRTC_SPL_MAX(0, w32_cc); /* Don't divide with negative number */ - w16_bestCorr = (int16_t) WebRtcSpl_DivW32W16(w32_cc, w16_sqrtEn1En2); - w16_bestCorr = WEBRTC_SPL_MIN(16384, w16_bestCorr); /* set maximum to 1.0 */ - } - -#ifdef NETEQ_STEREO - - } /* end if (msInfo->msMode != NETEQ_SLAVE) */ - -#endif /* NETEQ_STEREO */ - - /************************************************/ - /* Check accelerate criteria and remove samples */ - /************************************************/ - - /* Check for strong correlation (>0.9) or passive speech */ -#ifdef NETEQ_STEREO - if ((((w16_bestCorr > 14746) || (w16_VAD == 0)) && (msInfo->msMode != NETEQ_SLAVE)) - || ((msInfo->msMode == NETEQ_SLAVE) && (msInfo->extraInfo != ACC_FAIL))) -#else - if ((w16_bestCorr > 14746) || (w16_VAD == 0)) -#endif - { - /* Do accelerate operation by overlap add */ - - /* - * Calculate cross-fading slope so that the fading factor goes from - * 1 (16384 in Q14) to 0 in one pitch period (bestIndex). - */ - w16_inc = (int16_t) WebRtcSpl_DivW32W16((int32_t) 16384, - (int16_t) (w16_bestIndex + 1)); /* in Q14 */ - - /* Initiate fading factor */ - w16_startfact = 16384 - w16_inc; - - /* vec1 starts at 15 ms minus one pitch period */ - pw16_vec1 = &pw16_decoded[fsMult120 - w16_bestIndex]; - /* vec2 start at 15 ms */ - pw16_vec2 = &pw16_decoded[fsMult120]; - - /* Copy unmodified part [0 to 15 ms minus 1 pitch period] */ - w16_tmp = (fsMult120 - w16_bestIndex); - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, w16_tmp); - - /* Generate interpolated part of length bestIndex (1 pitch period) */ - pw16_vectmp = pw16_outData + w16_tmp; /* start of interpolation output */ - /* Reuse mixing function from Expand */ - WebRtcNetEQ_MixVoiceUnvoice(pw16_vectmp, (int16_t*) pw16_vec1, - (int16_t*) pw16_vec2, &w16_startfact, w16_inc, w16_bestIndex); - - /* Move the last part (also unmodified) */ - /* Take from decoded at 15 ms + 1 pitch period */ - pw16_vec2 = &pw16_decoded[fsMult120 + w16_bestIndex]; - WEBRTC_SPL_MEMMOVE_W16(&pw16_outData[fsMult120], pw16_vec2, - (int16_t) (len - fsMult120 - w16_bestIndex)); - - /* Set the mode flag */ - if (w16_VAD) - { - inst->w16_mode = MODE_SUCCESS_ACCELERATE; - } - else - { - inst->w16_mode = MODE_LOWEN_ACCELERATE; - } - - /* Calculate resulting length = original length - pitch period */ - *pw16_len = len - w16_bestIndex; - - /* Update in-call statistics */ - inst->statInst.accelerateLength += w16_bestIndex; - /* Short-term activity statistics. */ - inst->activity_stats.accelarate_normal_samples += w16_bestIndex; - - return 0; - } - else - { - /* Accelerate not allowed */ - -#ifdef NETEQ_STEREO - /* Signal to slave(s) that this was unsuccessful */ - if (msInfo->msMode == NETEQ_MASTER) - { - msInfo->extraInfo = ACC_FAIL; - } -#endif - - /* Set mode flag to unsuccessful accelerate */ - inst->w16_mode = MODE_UNSUCCESS_ACCELERATE; - - /* Length is unmodified */ - *pw16_len = len; - - /* Simply move all data from decoded to outData */ - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, (int16_t) len); - - return 0; - } -} - -#undef SCRATCH_PW16_DS_SPEECH -#undef SCRATCH_PW32_CORR -#undef SCRATCH_PW16_CORR diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/accelerate.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/accelerate.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/accelerate.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/accelerate.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/accelerate.h" + +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" + +namespace webrtc { + +Accelerate::ReturnCodes Accelerate::Process( + const int16_t* input, + size_t input_length, + AudioMultiVector* output, + int16_t* length_change_samples) { + // Input length must be (almost) 30 ms. + static const int k15ms = 120; // 15 ms = 120 samples at 8 kHz sample rate. + if (num_channels_ == 0 || static_cast(input_length) / num_channels_ < + (2 * k15ms - 1) * fs_mult_) { + // Length of input data too short to do accelerate. Simply move all data + // from input to output. + output->PushBackInterleaved(input, input_length); + return kError; + } + return TimeStretch::Process(input, input_length, output, + length_change_samples); +} + +void Accelerate::SetParametersForPassiveSpeech(size_t /*len*/, + int16_t* best_correlation, + int* /*peak_index*/) const { + // When the signal does not contain any active speech, the correlation does + // not matter. Simply set it to zero. + *best_correlation = 0; +} + +Accelerate::ReturnCodes Accelerate::CheckCriteriaAndStretch( + const int16_t* input, size_t input_length, size_t peak_index, + int16_t best_correlation, bool active_speech, + AudioMultiVector* output) const { + // Check for strong correlation or passive speech. + if ((best_correlation > kCorrelationThreshold) || !active_speech) { + // Do accelerate operation by overlap add. + + // Pre-calculate common multiplication with |fs_mult_|. + // 120 corresponds to 15 ms. + size_t fs_mult_120 = fs_mult_ * 120; + + assert(fs_mult_120 >= peak_index); // Should be handled in Process(). + // Copy first part; 0 to 15 ms. + output->PushBackInterleaved(input, fs_mult_120 * num_channels_); + // Copy the |peak_index| starting at 15 ms to |temp_vector|. + AudioMultiVector temp_vector(num_channels_); + temp_vector.PushBackInterleaved(&input[fs_mult_120 * num_channels_], + peak_index * num_channels_); + // Cross-fade |temp_vector| onto the end of |output|. + output->CrossFade(temp_vector, peak_index); + // Copy the last unmodified part, 15 ms + pitch period until the end. + output->PushBackInterleaved( + &input[(fs_mult_120 + peak_index) * num_channels_], + input_length - (fs_mult_120 + peak_index) * num_channels_); + + if (active_speech) { + return kSuccess; + } else { + return kSuccessLowEnergy; + } + } else { + // Accelerate not allowed. Simply move all data from decoded to outData. + output->PushBackInterleaved(input, input_length); + return kNoStretch; + } +} + +Accelerate* AccelerateFactory::Create( + int sample_rate_hz, + size_t num_channels, + const BackgroundNoise& background_noise) const { + return new Accelerate(sample_rate_hz, num_channels, background_noise); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/accelerate.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/accelerate.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/accelerate.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/accelerate.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_ACCELERATE_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_ACCELERATE_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" +#include "webrtc/modules/audio_coding/neteq/time_stretch.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Forward declarations. +class BackgroundNoise; + +// This class implements the Accelerate operation. Most of the work is done +// in the base class TimeStretch, which is shared with the PreemptiveExpand +// operation. In the Accelerate class, the operations that are specific to +// Accelerate are implemented. +class Accelerate : public TimeStretch { + public: + Accelerate(int sample_rate_hz, size_t num_channels, + const BackgroundNoise& background_noise) + : TimeStretch(sample_rate_hz, num_channels, background_noise) { + } + + virtual ~Accelerate() {} + + // This method performs the actual Accelerate operation. The samples are + // read from |input|, of length |input_length| elements, and are written to + // |output|. The number of samples removed through time-stretching is + // is provided in the output |length_change_samples|. The method returns + // the outcome of the operation as an enumerator value. + ReturnCodes Process(const int16_t* input, + size_t input_length, + AudioMultiVector* output, + int16_t* length_change_samples); + + protected: + // Sets the parameters |best_correlation| and |peak_index| to suitable + // values when the signal contains no active speech. + virtual void SetParametersForPassiveSpeech(size_t len, + int16_t* best_correlation, + int* peak_index) const OVERRIDE; + + // Checks the criteria for performing the time-stretching operation and, + // if possible, performs the time-stretching. + virtual ReturnCodes CheckCriteriaAndStretch( + const int16_t* input, size_t input_length, size_t peak_index, + int16_t best_correlation, bool active_speech, + AudioMultiVector* output) const OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(Accelerate); +}; + +struct AccelerateFactory { + AccelerateFactory() {} + virtual ~AccelerateFactory() {} + + virtual Accelerate* Create(int sample_rate_hz, + size_t num_channels, + const BackgroundNoise& background_noise) const; +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_ACCELERATE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_neteq -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := \ - accelerate.c \ - automode.c \ - bgn_update.c \ - bufstats_decision.c \ - cng_internal.c \ - codec_db.c \ - correlator.c \ - dsp.c \ - dsp_helpfunctions.c \ - dtmf_buffer.c \ - dtmf_tonegen.c \ - expand.c \ - mcu_address_init.c \ - mcu_dsp_common.c \ - mcu_reset.c \ - merge.c \ - min_distortion.c \ - mix_voice_unvoice.c \ - mute_signal.c \ - normal.c \ - packet_buffer.c \ - peak_detection.c \ - preemptive_expand.c \ - random_vector.c \ - recin.c \ - recout.c \ - rtcp.c \ - rtp.c \ - set_fs.c \ - signal_mcu.c \ - split_and_insert.c \ - unmute_signal.c \ - webrtc_neteq.c - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) \ - '-DNETEQ_VOICEENGINE_CODECS' - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/interface \ - $(LOCAL_PATH)/../codecs/cng/include \ - $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../../../common_audio/signal_processing/include - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_classifier.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_classifier.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_classifier.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_classifier.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/audio_classifier.h" + +#include +#include + +namespace webrtc { + +static const int kDefaultSampleRateHz = 48000; +static const int kDefaultFrameRateHz = 50; +static const int kDefaultFrameSizeSamples = + kDefaultSampleRateHz / kDefaultFrameRateHz; +static const float kDefaultThreshold = 0.5f; + +AudioClassifier::AudioClassifier() + : analysis_info_(), + is_music_(false), + music_probability_(0), + // This actually assigns the pointer to a static constant struct + // rather than creates a struct and |celt_mode_| does not need + // to be deleted. + celt_mode_(opus_custom_mode_create(kDefaultSampleRateHz, + kDefaultFrameSizeSamples, + NULL)), + analysis_state_() { + assert(celt_mode_); +} + +AudioClassifier::~AudioClassifier() {} + +bool AudioClassifier::Analysis(const int16_t* input, + int input_length, + int channels) { + // Must be 20 ms frames at 48 kHz sampling. + assert((input_length / channels) == kDefaultFrameSizeSamples); + + // Only mono or stereo are allowed. + assert(channels == 1 || channels == 2); + + // Call Opus' classifier, defined in + // "third_party/opus/src/src/analysis.h", with lsb_depth = 16. + // Also uses a down-mixing function downmix_int, defined in + // "third_party/opus/src/src/opus_private.h", with + // constants c1 = 0, and c2 = -2. + run_analysis(&analysis_state_, + celt_mode_, + input, + kDefaultFrameSizeSamples, + kDefaultFrameSizeSamples, + 0, + -2, + channels, + kDefaultSampleRateHz, + 16, + downmix_int, + &analysis_info_); + music_probability_ = analysis_info_.music_prob; + is_music_ = music_probability_ > kDefaultThreshold; + return is_music_; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_classifier.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_classifier.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_classifier.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_classifier.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_AUDIO_CLASSIFIER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_AUDIO_CLASSIFIER_H_ + +#if defined(__cplusplus) +extern "C" { +#endif +#include "celt.h" +#include "analysis.h" +#include "opus_private.h" +#if defined(__cplusplus) +} +#endif + +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// This class provides a speech/music classification and is a wrapper over the +// Opus classifier. It currently only supports 48 kHz mono or stereo with a +// frame size of 20 ms. + +class AudioClassifier { + public: + AudioClassifier(); + virtual ~AudioClassifier(); + + // Classifies one frame of audio data in input, + // input_length : must be channels * 960; + // channels : must be 1 (mono) or 2 (stereo). + bool Analysis(const int16_t* input, int input_length, int channels); + + // Gets the current classification : true = music, false = speech. + virtual bool is_music() const { return is_music_; } + + // Gets the current music probability. + float music_probability() const { return music_probability_; } + + private: + AnalysisInfo analysis_info_; + bool is_music_; + float music_probability_; + const CELTMode* celt_mode_; + TonalityAnalysisState analysis_state_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_AUDIO_CLASSIFIER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_classifier_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_classifier_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_classifier_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_classifier_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/audio_classifier.h" + +#include +#include +#include +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/test/testsupport/fileutils.h" + +namespace webrtc { + +static const size_t kFrameSize = 960; + +TEST(AudioClassifierTest, AllZeroInput) { + int16_t in_mono[kFrameSize] = {0}; + + // Test all-zero vectors and let the classifier converge from its default + // to the expected value. + AudioClassifier zero_classifier; + for (int i = 0; i < 100; ++i) { + zero_classifier.Analysis(in_mono, kFrameSize, 1); + } + EXPECT_TRUE(zero_classifier.is_music()); +} + +void RunAnalysisTest(const std::string& audio_filename, + const std::string& data_filename, + size_t channels) { + AudioClassifier classifier; + scoped_ptr in(new int16_t[channels * kFrameSize]); + bool is_music_ref; + + FILE* audio_file = fopen(audio_filename.c_str(), "rb"); + ASSERT_TRUE(audio_file != NULL) << "Failed to open file " << audio_filename + << std::endl; + FILE* data_file = fopen(data_filename.c_str(), "rb"); + ASSERT_TRUE(audio_file != NULL) << "Failed to open file " << audio_filename + << std::endl; + while (fread(in.get(), sizeof(int16_t), channels * kFrameSize, audio_file) == + channels * kFrameSize) { + bool is_music = + classifier.Analysis(in.get(), channels * kFrameSize, channels); + EXPECT_EQ(is_music, classifier.is_music()); + ASSERT_EQ(1u, fread(&is_music_ref, sizeof(is_music_ref), 1, data_file)); + EXPECT_EQ(is_music_ref, is_music); + } + fclose(audio_file); + fclose(data_file); +} + +TEST(AudioClassifierTest, DoAnalysisMono) { + RunAnalysisTest(test::ResourcePath("short_mixed_mono_48", "pcm"), + test::ResourcePath("short_mixed_mono_48", "dat"), + 1); +} + +TEST(AudioClassifierTest, DoAnalysisStereo) { + RunAnalysisTest(test::ResourcePath("short_mixed_stereo_48", "pcm"), + test::ResourcePath("short_mixed_stereo_48", "dat"), + 2); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" + +#include + +#include "webrtc/base/checks.h" +#include "webrtc/modules/audio_coding/neteq/audio_decoder_impl.h" + +namespace webrtc { + +int AudioDecoder::DecodeRedundant(const uint8_t* encoded, + size_t encoded_len, + int16_t* decoded, + SpeechType* speech_type) { + return Decode(encoded, encoded_len, decoded, speech_type); +} + +bool AudioDecoder::HasDecodePlc() const { return false; } + +int AudioDecoder::DecodePlc(int num_frames, int16_t* decoded) { return -1; } + +int AudioDecoder::IncomingPacket(const uint8_t* payload, + size_t payload_len, + uint16_t rtp_sequence_number, + uint32_t rtp_timestamp, + uint32_t arrival_timestamp) { + return 0; +} + +int AudioDecoder::ErrorCode() { return 0; } + +int AudioDecoder::PacketDuration(const uint8_t* encoded, size_t encoded_len) { + return kNotImplemented; +} + +int AudioDecoder::PacketDurationRedundant(const uint8_t* encoded, + size_t encoded_len) const { + return kNotImplemented; +} + +bool AudioDecoder::PacketHasFec(const uint8_t* encoded, + size_t encoded_len) const { + return false; +} + +CNG_dec_inst* AudioDecoder::CngDecoderInstance() { + FATAL() << "Not a CNG decoder"; + return NULL; +} + +bool AudioDecoder::CodecSupported(NetEqDecoder codec_type) { + switch (codec_type) { + case kDecoderPCMu: + case kDecoderPCMa: + case kDecoderPCMu_2ch: + case kDecoderPCMa_2ch: +#ifdef WEBRTC_CODEC_ILBC + case kDecoderILBC: +#endif +#if defined(WEBRTC_CODEC_ISACFX) || defined(WEBRTC_CODEC_ISAC) + case kDecoderISAC: +#endif +#ifdef WEBRTC_CODEC_ISAC + case kDecoderISACswb: + case kDecoderISACfb: +#endif +#ifdef WEBRTC_CODEC_PCM16 + case kDecoderPCM16B: + case kDecoderPCM16Bwb: + case kDecoderPCM16Bswb32kHz: + case kDecoderPCM16Bswb48kHz: + case kDecoderPCM16B_2ch: + case kDecoderPCM16Bwb_2ch: + case kDecoderPCM16Bswb32kHz_2ch: + case kDecoderPCM16Bswb48kHz_2ch: + case kDecoderPCM16B_5ch: +#endif +#ifdef WEBRTC_CODEC_G722 + case kDecoderG722: + case kDecoderG722_2ch: +#endif +#ifdef WEBRTC_CODEC_CELT + case kDecoderCELT_32: + case kDecoderCELT_32_2ch: +#endif +#ifdef WEBRTC_CODEC_OPUS + case kDecoderOpus: + case kDecoderOpus_2ch: +#endif + case kDecoderRED: + case kDecoderAVT: + case kDecoderCNGnb: + case kDecoderCNGwb: + case kDecoderCNGswb32kHz: + case kDecoderCNGswb48kHz: + case kDecoderArbitrary: { + return true; + } + default: { + return false; + } + } +} + +int AudioDecoder::CodecSampleRateHz(NetEqDecoder codec_type) { + switch (codec_type) { + case kDecoderPCMu: + case kDecoderPCMa: + case kDecoderPCMu_2ch: + case kDecoderPCMa_2ch: +#ifdef WEBRTC_CODEC_ILBC + case kDecoderILBC: +#endif +#ifdef WEBRTC_CODEC_PCM16 + case kDecoderPCM16B: + case kDecoderPCM16B_2ch: + case kDecoderPCM16B_5ch: +#endif + case kDecoderCNGnb: { + return 8000; + } +#if defined(WEBRTC_CODEC_ISACFX) || defined(WEBRTC_CODEC_ISAC) + case kDecoderISAC: +#endif +#ifdef WEBRTC_CODEC_PCM16 + case kDecoderPCM16Bwb: + case kDecoderPCM16Bwb_2ch: +#endif +#ifdef WEBRTC_CODEC_G722 + case kDecoderG722: + case kDecoderG722_2ch: +#endif + case kDecoderCNGwb: { + return 16000; + } +#ifdef WEBRTC_CODEC_ISAC + case kDecoderISACswb: + case kDecoderISACfb: +#endif +#ifdef WEBRTC_CODEC_PCM16 + case kDecoderPCM16Bswb32kHz: + case kDecoderPCM16Bswb32kHz_2ch: +#endif +#ifdef WEBRTC_CODEC_CELT + case kDecoderCELT_32: + case kDecoderCELT_32_2ch: +#endif + case kDecoderCNGswb32kHz: { + return 32000; + } +#ifdef WEBRTC_CODEC_PCM16 + case kDecoderPCM16Bswb48kHz: + case kDecoderPCM16Bswb48kHz_2ch: { + return 48000; + } +#endif +#ifdef WEBRTC_CODEC_OPUS + case kDecoderOpus: + case kDecoderOpus_2ch: { + return 48000; + } +#endif + case kDecoderCNGswb48kHz: { + // TODO(tlegrand): Remove limitation once ACM has full 48 kHz support. + return 32000; + } + default: { + return -1; // Undefined sample rate. + } + } +} + +AudioDecoder* AudioDecoder::CreateAudioDecoder(NetEqDecoder codec_type) { + if (!CodecSupported(codec_type)) { + return NULL; + } + switch (codec_type) { + case kDecoderPCMu: + return new AudioDecoderPcmU; + case kDecoderPCMa: + return new AudioDecoderPcmA; + case kDecoderPCMu_2ch: + return new AudioDecoderPcmUMultiCh(2); + case kDecoderPCMa_2ch: + return new AudioDecoderPcmAMultiCh(2); +#ifdef WEBRTC_CODEC_ILBC + case kDecoderILBC: + return new AudioDecoderIlbc; +#endif +#if defined(WEBRTC_CODEC_ISACFX) + case kDecoderISAC: + return new AudioDecoderIsacFix; +#elif defined(WEBRTC_CODEC_ISAC) + case kDecoderISAC: + return new AudioDecoderIsac(16000); + case kDecoderISACswb: + case kDecoderISACfb: + return new AudioDecoderIsac(32000); +#endif +#ifdef WEBRTC_CODEC_PCM16 + case kDecoderPCM16B: + case kDecoderPCM16Bwb: + case kDecoderPCM16Bswb32kHz: + case kDecoderPCM16Bswb48kHz: + return new AudioDecoderPcm16B; + case kDecoderPCM16B_2ch: + case kDecoderPCM16Bwb_2ch: + case kDecoderPCM16Bswb32kHz_2ch: + case kDecoderPCM16Bswb48kHz_2ch: + return new AudioDecoderPcm16BMultiCh(2); + case kDecoderPCM16B_5ch: + return new AudioDecoderPcm16BMultiCh(5); +#endif +#ifdef WEBRTC_CODEC_G722 + case kDecoderG722: + return new AudioDecoderG722; + case kDecoderG722_2ch: + return new AudioDecoderG722Stereo; +#endif +#ifdef WEBRTC_CODEC_CELT + case kDecoderCELT_32: + return new AudioDecoderCelt(1); + case kDecoderCELT_32_2ch: + return new AudioDecoderCelt(2); +#endif +#ifdef WEBRTC_CODEC_OPUS + case kDecoderOpus: + return new AudioDecoderOpus(1); + case kDecoderOpus_2ch: + return new AudioDecoderOpus(2); +#endif + case kDecoderCNGnb: + case kDecoderCNGwb: + case kDecoderCNGswb32kHz: + case kDecoderCNGswb48kHz: + return new AudioDecoderCng; + case kDecoderRED: + case kDecoderAVT: + case kDecoderArbitrary: + default: { + return NULL; + } + } +} + +AudioDecoder::SpeechType AudioDecoder::ConvertSpeechType(int16_t type) { + switch (type) { + case 0: // TODO(hlundin): Both iSAC and Opus return 0 for speech. + case 1: + return kSpeech; + case 2: + return kComfortNoise; + default: + assert(false); + return kSpeech; + } +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder_impl.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder_impl.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,464 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/audio_decoder_impl.h" + +#include +#include // memmove + +#include "webrtc/base/checks.h" +#ifdef WEBRTC_CODEC_CELT +#include "webrtc/modules/audio_coding/codecs/celt/include/celt_interface.h" +#endif +#include "webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h" +#include "webrtc/modules/audio_coding/codecs/g711/include/g711_interface.h" +#ifdef WEBRTC_CODEC_G722 +#include "webrtc/modules/audio_coding/codecs/g722/include/g722_interface.h" +#endif +#ifdef WEBRTC_CODEC_ILBC +#include "webrtc/modules/audio_coding/codecs/ilbc/interface/ilbc.h" +#endif +#ifdef WEBRTC_CODEC_ISACFX +#include "webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h" +#endif +#ifdef WEBRTC_CODEC_ISAC +#include "webrtc/modules/audio_coding/codecs/isac/main/interface/isac.h" +#endif +#ifdef WEBRTC_CODEC_OPUS +#include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" +#endif +#ifdef WEBRTC_CODEC_PCM16 +#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" +#endif + +namespace webrtc { + +// PCMu +int AudioDecoderPcmU::Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type) { + int16_t temp_type = 1; // Default is speech. + int16_t ret = WebRtcG711_DecodeU( + reinterpret_cast(const_cast(encoded)), + static_cast(encoded_len), decoded, &temp_type); + *speech_type = ConvertSpeechType(temp_type); + return ret; +} + +int AudioDecoderPcmU::PacketDuration(const uint8_t* encoded, + size_t encoded_len) { + // One encoded byte per sample per channel. + return static_cast(encoded_len / channels_); +} + +// PCMa +int AudioDecoderPcmA::Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type) { + int16_t temp_type = 1; // Default is speech. + int16_t ret = WebRtcG711_DecodeA( + reinterpret_cast(const_cast(encoded)), + static_cast(encoded_len), decoded, &temp_type); + *speech_type = ConvertSpeechType(temp_type); + return ret; +} + +int AudioDecoderPcmA::PacketDuration(const uint8_t* encoded, + size_t encoded_len) { + // One encoded byte per sample per channel. + return static_cast(encoded_len / channels_); +} + +// PCM16B +#ifdef WEBRTC_CODEC_PCM16 +AudioDecoderPcm16B::AudioDecoderPcm16B() {} + +int AudioDecoderPcm16B::Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type) { + int16_t temp_type = 1; // Default is speech. + int16_t ret = WebRtcPcm16b_DecodeW16( + reinterpret_cast(const_cast(encoded)), + static_cast(encoded_len), decoded, &temp_type); + *speech_type = ConvertSpeechType(temp_type); + return ret; +} + +int AudioDecoderPcm16B::PacketDuration(const uint8_t* encoded, + size_t encoded_len) { + // Two encoded byte per sample per channel. + return static_cast(encoded_len / (2 * channels_)); +} + +AudioDecoderPcm16BMultiCh::AudioDecoderPcm16BMultiCh(int num_channels) { + DCHECK(num_channels > 0); + channels_ = num_channels; +} +#endif + +// iLBC +#ifdef WEBRTC_CODEC_ILBC +AudioDecoderIlbc::AudioDecoderIlbc() { + WebRtcIlbcfix_DecoderCreate(&dec_state_); +} + +AudioDecoderIlbc::~AudioDecoderIlbc() { + WebRtcIlbcfix_DecoderFree(dec_state_); +} + +int AudioDecoderIlbc::Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type) { + int16_t temp_type = 1; // Default is speech. + int16_t ret = WebRtcIlbcfix_Decode(dec_state_, + reinterpret_cast(encoded), + static_cast(encoded_len), decoded, + &temp_type); + *speech_type = ConvertSpeechType(temp_type); + return ret; +} + +int AudioDecoderIlbc::DecodePlc(int num_frames, int16_t* decoded) { + return WebRtcIlbcfix_NetEqPlc(dec_state_, decoded, num_frames); +} + +int AudioDecoderIlbc::Init() { + return WebRtcIlbcfix_Decoderinit30Ms(dec_state_); +} +#endif + +// iSAC float +#ifdef WEBRTC_CODEC_ISAC +AudioDecoderIsac::AudioDecoderIsac(int decode_sample_rate_hz) { + DCHECK(decode_sample_rate_hz == 16000 || decode_sample_rate_hz == 32000); + WebRtcIsac_Create(&isac_state_); + WebRtcIsac_SetDecSampRate(isac_state_, decode_sample_rate_hz); +} + +AudioDecoderIsac::~AudioDecoderIsac() { + WebRtcIsac_Free(isac_state_); +} + +int AudioDecoderIsac::Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type) { + int16_t temp_type = 1; // Default is speech. + int16_t ret = WebRtcIsac_Decode(isac_state_, + encoded, + static_cast(encoded_len), decoded, + &temp_type); + *speech_type = ConvertSpeechType(temp_type); + return ret; +} + +int AudioDecoderIsac::DecodeRedundant(const uint8_t* encoded, + size_t encoded_len, int16_t* decoded, + SpeechType* speech_type) { + int16_t temp_type = 1; // Default is speech. + int16_t ret = WebRtcIsac_DecodeRcu(isac_state_, + encoded, + static_cast(encoded_len), decoded, + &temp_type); + *speech_type = ConvertSpeechType(temp_type); + return ret; +} + +int AudioDecoderIsac::DecodePlc(int num_frames, int16_t* decoded) { + return WebRtcIsac_DecodePlc(isac_state_, decoded, num_frames); +} + +int AudioDecoderIsac::Init() { + return WebRtcIsac_DecoderInit(isac_state_); +} + +int AudioDecoderIsac::IncomingPacket(const uint8_t* payload, + size_t payload_len, + uint16_t rtp_sequence_number, + uint32_t rtp_timestamp, + uint32_t arrival_timestamp) { + return WebRtcIsac_UpdateBwEstimate(isac_state_, + payload, + static_cast(payload_len), + rtp_sequence_number, + rtp_timestamp, + arrival_timestamp); +} + +int AudioDecoderIsac::ErrorCode() { + return WebRtcIsac_GetErrorCode(isac_state_); +} +#endif + +// iSAC fix +#ifdef WEBRTC_CODEC_ISACFX +AudioDecoderIsacFix::AudioDecoderIsacFix() { + WebRtcIsacfix_Create(&isac_state_); +} + +AudioDecoderIsacFix::~AudioDecoderIsacFix() { + WebRtcIsacfix_Free(isac_state_); +} + +int AudioDecoderIsacFix::Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type) { + int16_t temp_type = 1; // Default is speech. + int16_t ret = WebRtcIsacfix_Decode(isac_state_, + encoded, + static_cast(encoded_len), decoded, + &temp_type); + *speech_type = ConvertSpeechType(temp_type); + return ret; +} + +int AudioDecoderIsacFix::Init() { + return WebRtcIsacfix_DecoderInit(isac_state_); +} + +int AudioDecoderIsacFix::IncomingPacket(const uint8_t* payload, + size_t payload_len, + uint16_t rtp_sequence_number, + uint32_t rtp_timestamp, + uint32_t arrival_timestamp) { + return WebRtcIsacfix_UpdateBwEstimate( + isac_state_, + payload, + static_cast(payload_len), + rtp_sequence_number, rtp_timestamp, arrival_timestamp); +} + +int AudioDecoderIsacFix::ErrorCode() { + return WebRtcIsacfix_GetErrorCode(isac_state_); +} +#endif + +// G.722 +#ifdef WEBRTC_CODEC_G722 +AudioDecoderG722::AudioDecoderG722() { + WebRtcG722_CreateDecoder(&dec_state_); +} + +AudioDecoderG722::~AudioDecoderG722() { + WebRtcG722_FreeDecoder(dec_state_); +} + +int AudioDecoderG722::Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type) { + int16_t temp_type = 1; // Default is speech. + int16_t ret = WebRtcG722_Decode( + dec_state_, + const_cast(reinterpret_cast(encoded)), + static_cast(encoded_len), decoded, &temp_type); + *speech_type = ConvertSpeechType(temp_type); + return ret; +} + +int AudioDecoderG722::Init() { + return WebRtcG722_DecoderInit(dec_state_); +} + +int AudioDecoderG722::PacketDuration(const uint8_t* encoded, + size_t encoded_len) { + // 1/2 encoded byte per sample per channel. + return static_cast(2 * encoded_len / channels_); +} + +AudioDecoderG722Stereo::AudioDecoderG722Stereo() { + channels_ = 2; + WebRtcG722_CreateDecoder(&dec_state_left_); + WebRtcG722_CreateDecoder(&dec_state_right_); +} + +AudioDecoderG722Stereo::~AudioDecoderG722Stereo() { + WebRtcG722_FreeDecoder(dec_state_left_); + WebRtcG722_FreeDecoder(dec_state_right_); +} + +int AudioDecoderG722Stereo::Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type) { + int16_t temp_type = 1; // Default is speech. + // De-interleave the bit-stream into two separate payloads. + uint8_t* encoded_deinterleaved = new uint8_t[encoded_len]; + SplitStereoPacket(encoded, encoded_len, encoded_deinterleaved); + // Decode left and right. + int16_t ret = WebRtcG722_Decode( + dec_state_left_, + reinterpret_cast(encoded_deinterleaved), + static_cast(encoded_len / 2), decoded, &temp_type); + if (ret >= 0) { + int decoded_len = ret; + ret = WebRtcG722_Decode( + dec_state_right_, + reinterpret_cast(&encoded_deinterleaved[encoded_len / 2]), + static_cast(encoded_len / 2), &decoded[decoded_len], &temp_type); + if (ret == decoded_len) { + decoded_len += ret; + // Interleave output. + for (int k = decoded_len / 2; k < decoded_len; k++) { + int16_t temp = decoded[k]; + memmove(&decoded[2 * k - decoded_len + 2], + &decoded[2 * k - decoded_len + 1], + (decoded_len - k - 1) * sizeof(int16_t)); + decoded[2 * k - decoded_len + 1] = temp; + } + ret = decoded_len; // Return total number of samples. + } + } + *speech_type = ConvertSpeechType(temp_type); + delete [] encoded_deinterleaved; + return ret; +} + +int AudioDecoderG722Stereo::Init() { + int r = WebRtcG722_DecoderInit(dec_state_left_); + if (r != 0) + return r; + return WebRtcG722_DecoderInit(dec_state_right_); +} + +// Split the stereo packet and place left and right channel after each other +// in the output array. +void AudioDecoderG722Stereo::SplitStereoPacket(const uint8_t* encoded, + size_t encoded_len, + uint8_t* encoded_deinterleaved) { + assert(encoded); + // Regroup the 4 bits/sample so |l1 l2| |r1 r2| |l3 l4| |r3 r4| ..., + // where "lx" is 4 bits representing left sample number x, and "rx" right + // sample. Two samples fit in one byte, represented with |...|. + for (size_t i = 0; i + 1 < encoded_len; i += 2) { + uint8_t right_byte = ((encoded[i] & 0x0F) << 4) + (encoded[i + 1] & 0x0F); + encoded_deinterleaved[i] = (encoded[i] & 0xF0) + (encoded[i + 1] >> 4); + encoded_deinterleaved[i + 1] = right_byte; + } + + // Move one byte representing right channel each loop, and place it at the + // end of the bytestream vector. After looping the data is reordered to: + // |l1 l2| |l3 l4| ... |l(N-1) lN| |r1 r2| |r3 r4| ... |r(N-1) r(N)|, + // where N is the total number of samples. + for (size_t i = 0; i < encoded_len / 2; i++) { + uint8_t right_byte = encoded_deinterleaved[i + 1]; + memmove(&encoded_deinterleaved[i + 1], &encoded_deinterleaved[i + 2], + encoded_len - i - 2); + encoded_deinterleaved[encoded_len - 1] = right_byte; + } +} +#endif + +// CELT +#ifdef WEBRTC_CODEC_CELT +AudioDecoderCelt::AudioDecoderCelt(int num_channels) { + DCHECK(num_channels == 1 || num_channels == 2); + channels_ = num_channels; + WebRtcCelt_CreateDec(reinterpret_cast(&state_), + static_cast(channels_)); +} + +AudioDecoderCelt::~AudioDecoderCelt() { + WebRtcCelt_FreeDec(static_cast(state_)); +} + +int AudioDecoderCelt::Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type) { + int16_t temp_type = 1; // Default to speech. + int ret = WebRtcCelt_DecodeUniversal(static_cast(state_), + encoded, static_cast(encoded_len), + decoded, &temp_type); + *speech_type = ConvertSpeechType(temp_type); + if (ret < 0) { + return -1; + } + // Return the total number of samples. + return ret * static_cast(channels_); +} + +int AudioDecoderCelt::Init() { + return WebRtcCelt_DecoderInit(static_cast(state_)); +} + +bool AudioDecoderCelt::HasDecodePlc() const { return true; } + +int AudioDecoderCelt::DecodePlc(int num_frames, int16_t* decoded) { + int ret = WebRtcCelt_DecodePlc(static_cast(state_), + decoded, num_frames); + if (ret < 0) { + return -1; + } + // Return the total number of samples. + return ret * static_cast(channels_); +} +#endif + +// Opus +#ifdef WEBRTC_CODEC_OPUS +AudioDecoderOpus::AudioDecoderOpus(int num_channels) { + DCHECK(num_channels == 1 || num_channels == 2); + channels_ = num_channels; + WebRtcOpus_DecoderCreate(&dec_state_, static_cast(channels_)); +} + +AudioDecoderOpus::~AudioDecoderOpus() { + WebRtcOpus_DecoderFree(dec_state_); +} + +int AudioDecoderOpus::Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type) { + int16_t temp_type = 1; // Default is speech. + int16_t ret = WebRtcOpus_DecodeNew(dec_state_, encoded, + static_cast(encoded_len), decoded, + &temp_type); + if (ret > 0) + ret *= static_cast(channels_); // Return total number of samples. + *speech_type = ConvertSpeechType(temp_type); + return ret; +} + +int AudioDecoderOpus::DecodeRedundant(const uint8_t* encoded, + size_t encoded_len, int16_t* decoded, + SpeechType* speech_type) { + int16_t temp_type = 1; // Default is speech. + int16_t ret = WebRtcOpus_DecodeFec(dec_state_, encoded, + static_cast(encoded_len), decoded, + &temp_type); + if (ret > 0) + ret *= static_cast(channels_); // Return total number of samples. + *speech_type = ConvertSpeechType(temp_type); + return ret; +} + +int AudioDecoderOpus::Init() { + return WebRtcOpus_DecoderInitNew(dec_state_); +} + +int AudioDecoderOpus::PacketDuration(const uint8_t* encoded, + size_t encoded_len) { + return WebRtcOpus_DurationEst(dec_state_, + encoded, static_cast(encoded_len)); +} + +int AudioDecoderOpus::PacketDurationRedundant(const uint8_t* encoded, + size_t encoded_len) const { + return WebRtcOpus_FecDurationEst(encoded, static_cast(encoded_len)); +} + +bool AudioDecoderOpus::PacketHasFec(const uint8_t* encoded, + size_t encoded_len) const { + int fec; + fec = WebRtcOpus_PacketHasFec(encoded, static_cast(encoded_len)); + return (fec == 1); +} +#endif + +AudioDecoderCng::AudioDecoderCng() { + CHECK_EQ(0, WebRtcCng_CreateDec(&dec_state_)); +} + +AudioDecoderCng::~AudioDecoderCng() { + WebRtcCng_FreeDec(dec_state_); +} + +int AudioDecoderCng::Init() { + return WebRtcCng_InitDec(dec_state_); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder_impl.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder_impl.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_AUDIO_DECODER_IMPL_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_AUDIO_DECODER_IMPL_H_ + +#include + +#ifndef AUDIO_DECODER_UNITTEST +// If this is compiled as a part of the audio_deoder_unittest, the codec +// selection is made in the gypi file instead of in engine_configurations.h. +#include "webrtc/engine_configurations.h" +#endif +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h" +#ifdef WEBRTC_CODEC_G722 +#include "webrtc/modules/audio_coding/codecs/g722/include/g722_interface.h" +#endif +#ifdef WEBRTC_CODEC_ILBC +#include "webrtc/modules/audio_coding/codecs/ilbc/interface/ilbc.h" +#endif +#ifdef WEBRTC_CODEC_ISACFX +#include "webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h" +#endif +#ifdef WEBRTC_CODEC_ISAC +#include "webrtc/modules/audio_coding/codecs/isac/main/interface/isac.h" +#endif +#ifdef WEBRTC_CODEC_OPUS +#include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" +#endif +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +class AudioDecoderPcmU : public AudioDecoder { + public: + AudioDecoderPcmU() {} + virtual int Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type); + virtual int Init() { return 0; } + virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); + + private: + DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcmU); +}; + +class AudioDecoderPcmA : public AudioDecoder { + public: + AudioDecoderPcmA() {} + virtual int Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type); + virtual int Init() { return 0; } + virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); + + private: + DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcmA); +}; + +class AudioDecoderPcmUMultiCh : public AudioDecoderPcmU { + public: + explicit AudioDecoderPcmUMultiCh(size_t channels) : AudioDecoderPcmU() { + assert(channels > 0); + channels_ = channels; + } + + private: + DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcmUMultiCh); +}; + +class AudioDecoderPcmAMultiCh : public AudioDecoderPcmA { + public: + explicit AudioDecoderPcmAMultiCh(size_t channels) : AudioDecoderPcmA() { + assert(channels > 0); + channels_ = channels; + } + + private: + DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcmAMultiCh); +}; + +#ifdef WEBRTC_CODEC_PCM16 +// This class handles all four types (i.e., sample rates) of PCM16B codecs. +// The type is specified in the constructor parameter |type|. +class AudioDecoderPcm16B : public AudioDecoder { + public: + AudioDecoderPcm16B(); + virtual int Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type); + virtual int Init() { return 0; } + virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); + + private: + DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcm16B); +}; + +// This class handles all four types (i.e., sample rates) of PCM16B codecs. +// The type is specified in the constructor parameter |type|, and the number +// of channels is derived from the type. +class AudioDecoderPcm16BMultiCh : public AudioDecoderPcm16B { + public: + explicit AudioDecoderPcm16BMultiCh(int num_channels); + + private: + DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcm16BMultiCh); +}; +#endif + +#ifdef WEBRTC_CODEC_ILBC +class AudioDecoderIlbc : public AudioDecoder { + public: + AudioDecoderIlbc(); + virtual ~AudioDecoderIlbc(); + virtual int Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type); + virtual bool HasDecodePlc() const { return true; } + virtual int DecodePlc(int num_frames, int16_t* decoded); + virtual int Init(); + + private: + iLBC_decinst_t* dec_state_; + DISALLOW_COPY_AND_ASSIGN(AudioDecoderIlbc); +}; +#endif + +#ifdef WEBRTC_CODEC_ISAC +class AudioDecoderIsac : public AudioDecoder { + public: + explicit AudioDecoderIsac(int decode_sample_rate_hz); + virtual ~AudioDecoderIsac(); + virtual int Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type); + virtual int DecodeRedundant(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type); + virtual bool HasDecodePlc() const { return true; } + virtual int DecodePlc(int num_frames, int16_t* decoded); + virtual int Init(); + virtual int IncomingPacket(const uint8_t* payload, + size_t payload_len, + uint16_t rtp_sequence_number, + uint32_t rtp_timestamp, + uint32_t arrival_timestamp); + virtual int ErrorCode(); + + private: + ISACStruct* isac_state_; + DISALLOW_COPY_AND_ASSIGN(AudioDecoderIsac); +}; +#endif + +#ifdef WEBRTC_CODEC_ISACFX +class AudioDecoderIsacFix : public AudioDecoder { + public: + AudioDecoderIsacFix(); + virtual ~AudioDecoderIsacFix(); + virtual int Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type); + virtual int Init(); + virtual int IncomingPacket(const uint8_t* payload, + size_t payload_len, + uint16_t rtp_sequence_number, + uint32_t rtp_timestamp, + uint32_t arrival_timestamp); + virtual int ErrorCode(); + + private: + ISACFIX_MainStruct* isac_state_; + DISALLOW_COPY_AND_ASSIGN(AudioDecoderIsacFix); +}; +#endif + +#ifdef WEBRTC_CODEC_G722 +class AudioDecoderG722 : public AudioDecoder { + public: + AudioDecoderG722(); + virtual ~AudioDecoderG722(); + virtual int Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type); + virtual bool HasDecodePlc() const { return false; } + virtual int Init(); + virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); + + private: + G722DecInst* dec_state_; + DISALLOW_COPY_AND_ASSIGN(AudioDecoderG722); +}; + +class AudioDecoderG722Stereo : public AudioDecoder { + public: + AudioDecoderG722Stereo(); + virtual ~AudioDecoderG722Stereo(); + virtual int Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type); + virtual int Init(); + + private: + // Splits the stereo-interleaved payload in |encoded| into separate payloads + // for left and right channels. The separated payloads are written to + // |encoded_deinterleaved|, which must hold at least |encoded_len| samples. + // The left channel starts at offset 0, while the right channel starts at + // offset encoded_len / 2 into |encoded_deinterleaved|. + void SplitStereoPacket(const uint8_t* encoded, size_t encoded_len, + uint8_t* encoded_deinterleaved); + + G722DecInst* dec_state_left_; + G722DecInst* dec_state_right_; + + DISALLOW_COPY_AND_ASSIGN(AudioDecoderG722Stereo); +}; +#endif + +#ifdef WEBRTC_CODEC_CELT +class AudioDecoderCelt : public AudioDecoder { + public: + explicit AudioDecoderCelt(int num_channels); + virtual ~AudioDecoderCelt(); + + virtual int Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type); + virtual int Init(); + virtual bool HasDecodePlc() const; + virtual int DecodePlc(int num_frames, int16_t* decoded); + + private: + DISALLOW_COPY_AND_ASSIGN(AudioDecoderCelt); +}; +#endif + +#ifdef WEBRTC_CODEC_OPUS +class AudioDecoderOpus : public AudioDecoder { + public: + explicit AudioDecoderOpus(int num_channels); + virtual ~AudioDecoderOpus(); + virtual int Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type); + virtual int DecodeRedundant(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type); + virtual int Init(); + virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); + virtual int PacketDurationRedundant(const uint8_t* encoded, + size_t encoded_len) const; + virtual bool PacketHasFec(const uint8_t* encoded, size_t encoded_len) const; + + private: + OpusDecInst* dec_state_; + DISALLOW_COPY_AND_ASSIGN(AudioDecoderOpus); +}; +#endif + +// AudioDecoderCng is a special type of AudioDecoder. It inherits from +// AudioDecoder just to fit in the DecoderDatabase. None of the class methods +// should be used, except constructor, destructor, and accessors. +// TODO(hlundin): Consider the possibility to create a super-class to +// AudioDecoder that is stored in DecoderDatabase. Then AudioDecoder and a +// specific CngDecoder class could both inherit from that class. +class AudioDecoderCng : public AudioDecoder { + public: + explicit AudioDecoderCng(); + virtual ~AudioDecoderCng(); + virtual int Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type) { return -1; } + virtual int Init(); + virtual int IncomingPacket(const uint8_t* payload, + size_t payload_len, + uint16_t rtp_sequence_number, + uint32_t rtp_timestamp, + uint32_t arrival_timestamp) { return -1; } + + virtual CNG_dec_inst* CngDecoderInstance() OVERRIDE { return dec_state_; } + + private: + CNG_dec_inst* dec_state_; + DISALLOW_COPY_AND_ASSIGN(AudioDecoderCng); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_AUDIO_DECODER_IMPL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,876 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/audio_decoder_impl.h" + +#include +#include + +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" +#ifdef WEBRTC_CODEC_CELT +#include "webrtc/modules/audio_coding/codecs/celt/include/celt_interface.h" +#endif +#include "webrtc/modules/audio_coding/codecs/g711/include/g711_interface.h" +#include "webrtc/modules/audio_coding/codecs/g711/include/audio_encoder_pcm.h" +#include "webrtc/modules/audio_coding/codecs/g722/include/g722_interface.h" +#include "webrtc/modules/audio_coding/codecs/ilbc/interface/ilbc.h" +#include "webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h" +#include "webrtc/modules/audio_coding/codecs/isac/main/interface/isac.h" +#include "webrtc/modules/audio_coding/codecs/opus/interface/audio_encoder_opus.h" +#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" +#include "webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.h" +#include "webrtc/system_wrappers/interface/data_log.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/test/testsupport/fileutils.h" + +namespace webrtc { + +namespace { +// The absolute difference between the input and output (the first channel) is +// compared vs |tolerance|. The parameter |delay| is used to correct for codec +// delays. +void CompareInputOutput(const std::vector& input, + const std::vector& output, + size_t num_samples, + size_t channels, + int tolerance, + int delay) { + ASSERT_LE(num_samples, input.size()); + ASSERT_LE(num_samples * channels, output.size()); + for (unsigned int n = 0; n < num_samples - delay; ++n) { + ASSERT_NEAR(input[n], output[channels * n + delay], tolerance) + << "Exit test on first diff; n = " << n; + DataLog::InsertCell("CodecTest", "input", input[n]); + DataLog::InsertCell("CodecTest", "output", output[channels * n]); + DataLog::NextRow("CodecTest"); + } +} + +// The absolute difference between the first two channels in |output| is +// compared vs |tolerance|. +void CompareTwoChannels(const std::vector& output, + size_t samples_per_channel, + size_t channels, + int tolerance) { + ASSERT_GE(channels, 2u); + ASSERT_LE(samples_per_channel * channels, output.size()); + for (unsigned int n = 0; n < samples_per_channel; ++n) + ASSERT_NEAR(output[channels * n], output[channels * n + 1], tolerance) + << "Stereo samples differ."; +} + +// Calculates mean-squared error between input and output (the first channel). +// The parameter |delay| is used to correct for codec delays. +double MseInputOutput(const std::vector& input, + const std::vector& output, + size_t num_samples, + size_t channels, + int delay) { + assert(delay < static_cast(num_samples)); + assert(num_samples <= input.size()); + assert(num_samples * channels <= output.size()); + if (num_samples == 0) + return 0.0; + double squared_sum = 0.0; + for (unsigned int n = 0; n < num_samples - delay; ++n) { + squared_sum += (input[n] - output[channels * n + delay]) * + (input[n] - output[channels * n + delay]); + } + return squared_sum / (num_samples - delay); +} +} // namespace + +class AudioDecoderTest : public ::testing::Test { + protected: + AudioDecoderTest() + : input_audio_(webrtc::test::ProjectRootPath() + + "resources/audio_coding/testfile32kHz.pcm", + 32000), + codec_input_rate_hz_(32000), // Legacy default value. + encoded_(NULL), + frame_size_(0), + data_length_(0), + encoded_bytes_(0), + channels_(1), + output_timestamp_(0), + decoder_(NULL) {} + + virtual ~AudioDecoderTest() {} + + virtual void SetUp() { + if (audio_encoder_) + codec_input_rate_hz_ = audio_encoder_->sample_rate_hz(); + // Create arrays. + ASSERT_GT(data_length_, 0u) << "The test must set data_length_ > 0"; + // Longest encoded data is produced by PCM16b with 2 bytes per sample. + encoded_ = new uint8_t[data_length_ * 2]; + // Logging to view input and output in Matlab. + // Use 'gyp -Denable_data_logging=1' to enable logging. + DataLog::CreateLog(); + DataLog::AddTable("CodecTest"); + DataLog::AddColumn("CodecTest", "input", 1); + DataLog::AddColumn("CodecTest", "output", 1); + } + + virtual void TearDown() { + delete decoder_; + decoder_ = NULL; + // Delete arrays. + delete [] encoded_; + encoded_ = NULL; + // Close log. + DataLog::ReturnLog(); + } + + virtual void InitEncoder() { } + + // TODO(henrik.lundin) Change return type to size_t once most/all overriding + // implementations are gone. + virtual int EncodeFrame(const int16_t* input, + size_t input_len_samples, + uint8_t* output) { + size_t enc_len_bytes = 0; + scoped_ptr interleaved_input( + new int16_t[channels_ * input_len_samples]); + for (int i = 0; i < audio_encoder_->Num10MsFramesInNextPacket(); ++i) { + EXPECT_EQ(0u, enc_len_bytes); + + // Duplicate the mono input signal to however many channels the test + // wants. + test::InputAudioFile::DuplicateInterleaved( + input, input_len_samples, channels_, interleaved_input.get()); + + EXPECT_TRUE(audio_encoder_->Encode( + 0, interleaved_input.get(), audio_encoder_->sample_rate_hz() / 100, + data_length_ * 2, output, &enc_len_bytes, &output_timestamp_)); + } + return static_cast(enc_len_bytes); + } + + // Encodes and decodes audio. The absolute difference between the input and + // output is compared vs |tolerance|, and the mean-squared error is compared + // with |mse|. The encoded stream should contain |expected_bytes|. For stereo + // audio, the absolute difference between the two channels is compared vs + // |channel_diff_tolerance|. + void EncodeDecodeTest(size_t expected_bytes, int tolerance, double mse, + int delay = 0, int channel_diff_tolerance = 0) { + ASSERT_GE(tolerance, 0) << "Test must define a tolerance >= 0"; + ASSERT_GE(channel_diff_tolerance, 0) << + "Test must define a channel_diff_tolerance >= 0"; + size_t processed_samples = 0u; + encoded_bytes_ = 0u; + InitEncoder(); + EXPECT_EQ(0, decoder_->Init()); + std::vector input; + std::vector decoded; + while (processed_samples + frame_size_ <= data_length_) { + // Extend input vector with |frame_size_|. + input.resize(input.size() + frame_size_, 0); + // Read from input file. + ASSERT_GE(input.size() - processed_samples, frame_size_); + ASSERT_TRUE(input_audio_.Read( + frame_size_, codec_input_rate_hz_, &input[processed_samples])); + size_t enc_len = EncodeFrame( + &input[processed_samples], frame_size_, &encoded_[encoded_bytes_]); + // Make sure that frame_size_ * channels_ samples are allocated and free. + decoded.resize((processed_samples + frame_size_) * channels_, 0); + AudioDecoder::SpeechType speech_type; + size_t dec_len = decoder_->Decode(&encoded_[encoded_bytes_], + enc_len, + &decoded[processed_samples * channels_], + &speech_type); + EXPECT_EQ(frame_size_ * channels_, dec_len); + encoded_bytes_ += enc_len; + processed_samples += frame_size_; + } + // For some codecs it doesn't make sense to check expected number of bytes, + // since the number can vary for different platforms. Opus and iSAC are + // such codecs. In this case expected_bytes is set to 0. + if (expected_bytes) { + EXPECT_EQ(expected_bytes, encoded_bytes_); + } + CompareInputOutput( + input, decoded, processed_samples, channels_, tolerance, delay); + if (channels_ == 2) + CompareTwoChannels( + decoded, processed_samples, channels_, channel_diff_tolerance); + EXPECT_LE( + MseInputOutput(input, decoded, processed_samples, channels_, delay), + mse); + } + + // Encodes a payload and decodes it twice with decoder re-init before each + // decode. Verifies that the decoded result is the same. + void ReInitTest() { + InitEncoder(); + scoped_ptr input(new int16_t[frame_size_]); + ASSERT_TRUE( + input_audio_.Read(frame_size_, codec_input_rate_hz_, input.get())); + size_t enc_len = EncodeFrame(input.get(), frame_size_, encoded_); + size_t dec_len; + AudioDecoder::SpeechType speech_type1, speech_type2; + EXPECT_EQ(0, decoder_->Init()); + scoped_ptr output1(new int16_t[frame_size_ * channels_]); + dec_len = decoder_->Decode(encoded_, enc_len, output1.get(), &speech_type1); + ASSERT_LE(dec_len, frame_size_ * channels_); + EXPECT_EQ(frame_size_ * channels_, dec_len); + // Re-init decoder and decode again. + EXPECT_EQ(0, decoder_->Init()); + scoped_ptr output2(new int16_t[frame_size_ * channels_]); + dec_len = decoder_->Decode(encoded_, enc_len, output2.get(), &speech_type2); + ASSERT_LE(dec_len, frame_size_ * channels_); + EXPECT_EQ(frame_size_ * channels_, dec_len); + for (unsigned int n = 0; n < frame_size_; ++n) { + ASSERT_EQ(output1[n], output2[n]) << "Exit test on first diff; n = " << n; + } + EXPECT_EQ(speech_type1, speech_type2); + } + + // Call DecodePlc and verify that the correct number of samples is produced. + void DecodePlcTest() { + InitEncoder(); + scoped_ptr input(new int16_t[frame_size_]); + ASSERT_TRUE( + input_audio_.Read(frame_size_, codec_input_rate_hz_, input.get())); + size_t enc_len = EncodeFrame(input.get(), frame_size_, encoded_); + AudioDecoder::SpeechType speech_type; + EXPECT_EQ(0, decoder_->Init()); + scoped_ptr output(new int16_t[frame_size_ * channels_]); + size_t dec_len = + decoder_->Decode(encoded_, enc_len, output.get(), &speech_type); + EXPECT_EQ(frame_size_ * channels_, dec_len); + // Call DecodePlc and verify that we get one frame of data. + // (Overwrite the output from the above Decode call, but that does not + // matter.) + dec_len = decoder_->DecodePlc(1, output.get()); + EXPECT_EQ(frame_size_ * channels_, dec_len); + } + + test::ResampleInputAudioFile input_audio_; + int codec_input_rate_hz_; + uint8_t* encoded_; + size_t frame_size_; + size_t data_length_; + size_t encoded_bytes_; + size_t channels_; + uint32_t output_timestamp_; + AudioDecoder* decoder_; + scoped_ptr audio_encoder_; +}; + +class AudioDecoderPcmUTest : public AudioDecoderTest { + protected: + AudioDecoderPcmUTest() : AudioDecoderTest() { + frame_size_ = 160; + data_length_ = 10 * frame_size_; + decoder_ = new AudioDecoderPcmU; + AudioEncoderPcmU::Config config; + config.frame_size_ms = static_cast(frame_size_ / 8); + audio_encoder_.reset(new AudioEncoderPcmU(config)); + } +}; + +class AudioDecoderPcmATest : public AudioDecoderTest { + protected: + AudioDecoderPcmATest() : AudioDecoderTest() { + frame_size_ = 160; + data_length_ = 10 * frame_size_; + decoder_ = new AudioDecoderPcmA; + AudioEncoderPcmA::Config config; + config.frame_size_ms = static_cast(frame_size_ / 8); + audio_encoder_.reset(new AudioEncoderPcmA(config)); + } +}; + +class AudioDecoderPcm16BTest : public AudioDecoderTest { + protected: + AudioDecoderPcm16BTest() : AudioDecoderTest() { + codec_input_rate_hz_ = 8000; + frame_size_ = 160; + data_length_ = 10 * frame_size_; + decoder_ = new AudioDecoderPcm16B; + assert(decoder_); + } + + virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, + uint8_t* output) { + int enc_len_bytes = WebRtcPcm16b_EncodeW16( + const_cast(input), static_cast(input_len_samples), + reinterpret_cast(output)); + EXPECT_EQ(2 * input_len_samples, static_cast(enc_len_bytes)); + return enc_len_bytes; + } +}; + +class AudioDecoderIlbcTest : public AudioDecoderTest { + protected: + AudioDecoderIlbcTest() : AudioDecoderTest() { + codec_input_rate_hz_ = 8000; + frame_size_ = 240; + data_length_ = 10 * frame_size_; + decoder_ = new AudioDecoderIlbc; + assert(decoder_); + WebRtcIlbcfix_EncoderCreate(&encoder_); + } + + ~AudioDecoderIlbcTest() { + WebRtcIlbcfix_EncoderFree(encoder_); + } + + virtual void InitEncoder() { + ASSERT_EQ(0, WebRtcIlbcfix_EncoderInit(encoder_, 30)); // 30 ms. + } + + virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, + uint8_t* output) { + int enc_len_bytes = + WebRtcIlbcfix_Encode(encoder_, input, + static_cast(input_len_samples), + reinterpret_cast(output)); + EXPECT_EQ(50, enc_len_bytes); + return enc_len_bytes; + } + + // Overload the default test since iLBC's function WebRtcIlbcfix_NetEqPlc does + // not return any data. It simply resets a few states and returns 0. + void DecodePlcTest() { + InitEncoder(); + scoped_ptr input(new int16_t[frame_size_]); + ASSERT_TRUE( + input_audio_.Read(frame_size_, codec_input_rate_hz_, input.get())); + size_t enc_len = EncodeFrame(input.get(), frame_size_, encoded_); + AudioDecoder::SpeechType speech_type; + EXPECT_EQ(0, decoder_->Init()); + scoped_ptr output(new int16_t[frame_size_ * channels_]); + size_t dec_len = + decoder_->Decode(encoded_, enc_len, output.get(), &speech_type); + EXPECT_EQ(frame_size_, dec_len); + // Simply call DecodePlc and verify that we get 0 as return value. + EXPECT_EQ(0, decoder_->DecodePlc(1, output.get())); + } + + iLBC_encinst_t* encoder_; +}; + +class AudioDecoderIsacFloatTest : public AudioDecoderTest { + protected: + AudioDecoderIsacFloatTest() : AudioDecoderTest() { + codec_input_rate_hz_ = 16000; + input_size_ = 160; + frame_size_ = 480; + data_length_ = 10 * frame_size_; + decoder_ = new AudioDecoderIsac(16000); + assert(decoder_); + WebRtcIsac_Create(&encoder_); + WebRtcIsac_SetEncSampRate(encoder_, 16000); + } + + ~AudioDecoderIsacFloatTest() { + WebRtcIsac_Free(encoder_); + } + + virtual void InitEncoder() { + ASSERT_EQ(0, WebRtcIsac_EncoderInit(encoder_, 1)); // Fixed mode. + ASSERT_EQ(0, WebRtcIsac_Control(encoder_, 32000, 30)); // 32 kbps, 30 ms. + } + + virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, + uint8_t* output) { + // Insert 3 * 10 ms. Expect non-zero output on third call. + EXPECT_EQ(0, WebRtcIsac_Encode(encoder_, input, output)); + input += input_size_; + EXPECT_EQ(0, WebRtcIsac_Encode(encoder_, input, output)); + input += input_size_; + int enc_len_bytes = WebRtcIsac_Encode(encoder_, input, output); + EXPECT_GT(enc_len_bytes, 0); + return enc_len_bytes; + } + + ISACStruct* encoder_; + int input_size_; +}; + +class AudioDecoderIsacSwbTest : public AudioDecoderTest { + protected: + AudioDecoderIsacSwbTest() : AudioDecoderTest() { + codec_input_rate_hz_ = 32000; + input_size_ = 320; + frame_size_ = 960; + data_length_ = 10 * frame_size_; + decoder_ = new AudioDecoderIsac(32000); + assert(decoder_); + WebRtcIsac_Create(&encoder_); + WebRtcIsac_SetEncSampRate(encoder_, 32000); + } + + ~AudioDecoderIsacSwbTest() { + WebRtcIsac_Free(encoder_); + } + + virtual void InitEncoder() { + ASSERT_EQ(0, WebRtcIsac_EncoderInit(encoder_, 1)); // Fixed mode. + ASSERT_EQ(0, WebRtcIsac_Control(encoder_, 32000, 30)); // 32 kbps, 30 ms. + } + + virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, + uint8_t* output) { + // Insert 3 * 10 ms. Expect non-zero output on third call. + EXPECT_EQ(0, WebRtcIsac_Encode(encoder_, input, output)); + input += input_size_; + EXPECT_EQ(0, WebRtcIsac_Encode(encoder_, input, output)); + input += input_size_; + int enc_len_bytes = WebRtcIsac_Encode(encoder_, input, output); + EXPECT_GT(enc_len_bytes, 0); + return enc_len_bytes; + } + + ISACStruct* encoder_; + int input_size_; +}; + +class AudioDecoderIsacFixTest : public AudioDecoderTest { + protected: + AudioDecoderIsacFixTest() : AudioDecoderTest() { + codec_input_rate_hz_ = 16000; + input_size_ = 160; + frame_size_ = 480; + data_length_ = 10 * frame_size_; + decoder_ = new AudioDecoderIsacFix; + assert(decoder_); + WebRtcIsacfix_Create(&encoder_); + } + + ~AudioDecoderIsacFixTest() { + WebRtcIsacfix_Free(encoder_); + } + + virtual void InitEncoder() { + ASSERT_EQ(0, WebRtcIsacfix_EncoderInit(encoder_, 1)); // Fixed mode. + ASSERT_EQ(0, + WebRtcIsacfix_Control(encoder_, 32000, 30)); // 32 kbps, 30 ms. + } + + virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, + uint8_t* output) { + // Insert 3 * 10 ms. Expect non-zero output on third call. + EXPECT_EQ(0, WebRtcIsacfix_Encode(encoder_, input, output)); + input += input_size_; + EXPECT_EQ(0, WebRtcIsacfix_Encode(encoder_, input, output)); + input += input_size_; + int enc_len_bytes = WebRtcIsacfix_Encode(encoder_, input, output); + EXPECT_GT(enc_len_bytes, 0); + return enc_len_bytes; + } + + ISACFIX_MainStruct* encoder_; + int input_size_; +}; + +class AudioDecoderG722Test : public AudioDecoderTest { + protected: + AudioDecoderG722Test() : AudioDecoderTest() { + codec_input_rate_hz_ = 16000; + frame_size_ = 160; + data_length_ = 10 * frame_size_; + decoder_ = new AudioDecoderG722; + assert(decoder_); + WebRtcG722_CreateEncoder(&encoder_); + } + + ~AudioDecoderG722Test() { + WebRtcG722_FreeEncoder(encoder_); + } + + virtual void InitEncoder() { + ASSERT_EQ(0, WebRtcG722_EncoderInit(encoder_)); + } + + virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, + uint8_t* output) { + int enc_len_bytes = + WebRtcG722_Encode(encoder_, const_cast(input), + static_cast(input_len_samples), + reinterpret_cast(output)); + EXPECT_EQ(80, enc_len_bytes); + return enc_len_bytes; + } + + G722EncInst* encoder_; +}; + +class AudioDecoderG722StereoTest : public AudioDecoderG722Test { + protected: + AudioDecoderG722StereoTest() : AudioDecoderG722Test() { + channels_ = 2; + // Delete the |decoder_| that was created by AudioDecoderG722Test and + // create an AudioDecoderG722Stereo object instead. + delete decoder_; + decoder_ = new AudioDecoderG722Stereo; + assert(decoder_); + } + + virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, + uint8_t* output) { + uint8_t* temp_output = new uint8_t[data_length_ * 2]; + // Encode a mono payload using the base test class. + int mono_enc_len_bytes = + AudioDecoderG722Test::EncodeFrame(input, input_len_samples, + temp_output); + // The bit-stream consists of 4-bit samples: + // +--------+--------+--------+ + // | s0 s1 | s2 s3 | s4 s5 | + // +--------+--------+--------+ + // + // Duplicate them to the |output| such that the stereo stream becomes: + // +--------+--------+--------+ + // | s0 s0 | s1 s1 | s2 s2 | + // +--------+--------+--------+ + EXPECT_LE(mono_enc_len_bytes * 2, static_cast(data_length_ * 2)); + uint8_t* output_ptr = output; + for (int i = 0; i < mono_enc_len_bytes; ++i) { + *output_ptr = (temp_output[i] & 0xF0) + (temp_output[i] >> 4); + ++output_ptr; + *output_ptr = (temp_output[i] << 4) + (temp_output[i] & 0x0F); + ++output_ptr; + } + delete [] temp_output; + return mono_enc_len_bytes * 2; + } +}; + +#ifdef WEBRTC_CODEC_CELT +class AudioDecoderCeltTest : public AudioDecoderTest { + protected: + static const int kEncodingRateBitsPerSecond = 64000; + AudioDecoderCeltTest() : AudioDecoderTest(), encoder_(NULL) { + frame_size_ = 640; + data_length_ = 10 * frame_size_; + decoder_ = AudioDecoder::CreateAudioDecoder(kDecoderCELT_32); + assert(decoder_); + WebRtcCelt_CreateEnc(&encoder_, static_cast(channels_)); + } + + ~AudioDecoderCeltTest() { + WebRtcCelt_FreeEnc(encoder_); + } + + virtual void InitEncoder() { + assert(encoder_); + ASSERT_EQ(0, WebRtcCelt_EncoderInit( + encoder_, static_cast(channels_), kEncodingRateBitsPerSecond)); + } + + virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, + uint8_t* output) { + assert(encoder_); + return WebRtcCelt_Encode(encoder_, input, output); + } + + CELT_encinst_t* encoder_; +}; + +class AudioDecoderCeltStereoTest : public AudioDecoderTest { + protected: + static const int kEncodingRateBitsPerSecond = 64000; + AudioDecoderCeltStereoTest() : AudioDecoderTest(), encoder_(NULL) { + channels_ = 2; + frame_size_ = 640; + data_length_ = 10 * frame_size_; + decoder_ = AudioDecoder::CreateAudioDecoder(kDecoderCELT_32_2ch); + assert(decoder_); + stereo_input_ = new int16_t[frame_size_ * channels_]; + WebRtcCelt_CreateEnc(&encoder_, static_cast(channels_)); + } + + ~AudioDecoderCeltStereoTest() { + delete [] stereo_input_; + WebRtcCelt_FreeEnc(encoder_); + } + + virtual void InitEncoder() { + assert(encoder_); + ASSERT_EQ(0, WebRtcCelt_EncoderInit( + encoder_, static_cast(channels_), kEncodingRateBitsPerSecond)); + } + + virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, + uint8_t* output) { + assert(encoder_); + assert(stereo_input_); + for (size_t n = 0; n < frame_size_; ++n) { + stereo_input_[n * 2] = stereo_input_[n * 2 + 1] = input[n]; + } + return WebRtcCelt_Encode(encoder_, stereo_input_, output); + } + + int16_t* stereo_input_; + CELT_encinst_t* encoder_; +}; + +#endif + +class AudioDecoderOpusTest : public AudioDecoderTest { + protected: + AudioDecoderOpusTest() : AudioDecoderTest() { + codec_input_rate_hz_ = 48000; + frame_size_ = 480; + data_length_ = 10 * frame_size_; + decoder_ = new AudioDecoderOpus(1); + AudioEncoderOpus::Config config; + config.frame_size_ms = static_cast(frame_size_) / 48; + audio_encoder_.reset(new AudioEncoderOpus(config)); + } +}; + +class AudioDecoderOpusStereoTest : public AudioDecoderOpusTest { + protected: + AudioDecoderOpusStereoTest() : AudioDecoderOpusTest() { + channels_ = 2; + delete decoder_; + decoder_ = new AudioDecoderOpus(2); + AudioEncoderOpus::Config config; + config.frame_size_ms = static_cast(frame_size_) / 48; + config.num_channels = 2; + audio_encoder_.reset(new AudioEncoderOpus(config)); + } +}; + +TEST_F(AudioDecoderPcmUTest, EncodeDecode) { + int tolerance = 251; + double mse = 1734.0; + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCMu)); + EncodeDecodeTest(data_length_, tolerance, mse); + ReInitTest(); + EXPECT_FALSE(decoder_->HasDecodePlc()); +} + +TEST_F(AudioDecoderPcmATest, EncodeDecode) { + int tolerance = 308; + double mse = 1931.0; + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCMa)); + EncodeDecodeTest(data_length_, tolerance, mse); + ReInitTest(); + EXPECT_FALSE(decoder_->HasDecodePlc()); +} + +TEST_F(AudioDecoderPcm16BTest, EncodeDecode) { + int tolerance = 0; + double mse = 0.0; + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16B)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bwb)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bswb32kHz)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bswb48kHz)); + EncodeDecodeTest(2 * data_length_, tolerance, mse); + ReInitTest(); + EXPECT_FALSE(decoder_->HasDecodePlc()); +} + +TEST_F(AudioDecoderIlbcTest, EncodeDecode) { + int tolerance = 6808; + double mse = 2.13e6; + int delay = 80; // Delay from input to output. + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderILBC)); + EncodeDecodeTest(500, tolerance, mse, delay); + ReInitTest(); + EXPECT_TRUE(decoder_->HasDecodePlc()); + DecodePlcTest(); +} + +TEST_F(AudioDecoderIsacFloatTest, EncodeDecode) { + int tolerance = 3399; + double mse = 434951.0; + int delay = 48; // Delay from input to output. + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISAC)); + EncodeDecodeTest(0, tolerance, mse, delay); + ReInitTest(); + EXPECT_TRUE(decoder_->HasDecodePlc()); + DecodePlcTest(); +} + +TEST_F(AudioDecoderIsacSwbTest, EncodeDecode) { + int tolerance = 19757; + double mse = 8.18e6; + int delay = 160; // Delay from input to output. + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISACswb)); + EncodeDecodeTest(0, tolerance, mse, delay); + ReInitTest(); + EXPECT_TRUE(decoder_->HasDecodePlc()); + DecodePlcTest(); +} + +TEST_F(AudioDecoderIsacFixTest, DISABLED_EncodeDecode) { + int tolerance = 11034; + double mse = 3.46e6; + int delay = 54; // Delay from input to output. + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISAC)); + EncodeDecodeTest(735, tolerance, mse, delay); + ReInitTest(); + EXPECT_FALSE(decoder_->HasDecodePlc()); +} + +TEST_F(AudioDecoderG722Test, EncodeDecode) { + int tolerance = 6176; + double mse = 238630.0; + int delay = 22; // Delay from input to output. + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderG722)); + EncodeDecodeTest(data_length_ / 2, tolerance, mse, delay); + ReInitTest(); + EXPECT_FALSE(decoder_->HasDecodePlc()); +} + +TEST_F(AudioDecoderG722StereoTest, CreateAndDestroy) { + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderG722_2ch)); +} + +TEST_F(AudioDecoderG722StereoTest, EncodeDecode) { + int tolerance = 6176; + int channel_diff_tolerance = 0; + double mse = 238630.0; + int delay = 22; // Delay from input to output. + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderG722_2ch)); + EncodeDecodeTest(data_length_, tolerance, mse, delay, channel_diff_tolerance); + ReInitTest(); + EXPECT_FALSE(decoder_->HasDecodePlc()); +} + +TEST_F(AudioDecoderOpusTest, EncodeDecode) { + int tolerance = 6176; + double mse = 238630.0; + int delay = 22; // Delay from input to output. + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderOpus)); + EncodeDecodeTest(0, tolerance, mse, delay); + ReInitTest(); + EXPECT_FALSE(decoder_->HasDecodePlc()); +} + +TEST_F(AudioDecoderOpusStereoTest, EncodeDecode) { + int tolerance = 6176; + int channel_diff_tolerance = 0; + double mse = 238630.0; + int delay = 22; // Delay from input to output. + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderOpus_2ch)); + EncodeDecodeTest(0, tolerance, mse, delay, channel_diff_tolerance); + ReInitTest(); + EXPECT_FALSE(decoder_->HasDecodePlc()); +} + +#ifdef WEBRTC_CODEC_CELT +// In the two following CELT tests, the low amplitude of the test signal allow +// us to have such low error thresholds, i.e. |tolerance|, |mse|. Furthermore, +// in general, stereo signals with identical channels do not result in identical +// encoded channels. +TEST_F(AudioDecoderCeltTest, EncodeDecode) { + int tolerance = 20; + double mse = 17.0; + int delay = 80; // Delay from input to output in samples. + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderCELT_32)); + EncodeDecodeTest(1600, tolerance, mse, delay); + ReInitTest(); + EXPECT_TRUE(decoder_->HasDecodePlc()); + DecodePlcTest(); +} + +TEST_F(AudioDecoderCeltStereoTest, EncodeDecode) { + int tolerance = 20; + // If both channels are identical, CELT not necessarily decodes identical + // channels. However, for this input this is the case. + int channel_diff_tolerance = 0; + double mse = 20.0; + // Delay from input to output in samples, accounting for stereo. + int delay = 160; + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderCELT_32_2ch)); + EncodeDecodeTest(1600, tolerance, mse, delay, channel_diff_tolerance); + ReInitTest(); + EXPECT_TRUE(decoder_->HasDecodePlc()); + DecodePlcTest(); +} +#endif + +TEST(AudioDecoder, CodecSampleRateHz) { + EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderPCMu)); + EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderPCMa)); + EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderPCMu_2ch)); + EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderPCMa_2ch)); + EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderILBC)); + EXPECT_EQ(16000, AudioDecoder::CodecSampleRateHz(kDecoderISAC)); + EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderISACswb)); + EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderISACfb)); + EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16B)); + EXPECT_EQ(16000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16Bwb)); + EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16Bswb32kHz)); + EXPECT_EQ(48000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16Bswb48kHz)); + EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16B_2ch)); + EXPECT_EQ(16000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16Bwb_2ch)); + EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16Bswb32kHz_2ch)); + EXPECT_EQ(48000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16Bswb48kHz_2ch)); + EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16B_5ch)); + EXPECT_EQ(16000, AudioDecoder::CodecSampleRateHz(kDecoderG722)); + EXPECT_EQ(16000, AudioDecoder::CodecSampleRateHz(kDecoderG722_2ch)); + EXPECT_EQ(-1, AudioDecoder::CodecSampleRateHz(kDecoderRED)); + EXPECT_EQ(-1, AudioDecoder::CodecSampleRateHz(kDecoderAVT)); + EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderCNGnb)); + EXPECT_EQ(16000, AudioDecoder::CodecSampleRateHz(kDecoderCNGwb)); + EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderCNGswb32kHz)); + EXPECT_EQ(48000, AudioDecoder::CodecSampleRateHz(kDecoderOpus)); + EXPECT_EQ(48000, AudioDecoder::CodecSampleRateHz(kDecoderOpus_2ch)); + // TODO(tlegrand): Change 32000 to 48000 below once ACM has 48 kHz support. + EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderCNGswb48kHz)); + EXPECT_EQ(-1, AudioDecoder::CodecSampleRateHz(kDecoderArbitrary)); +#ifdef WEBRTC_CODEC_CELT + EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderCELT_32)); + EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderCELT_32_2ch)); +#else + EXPECT_EQ(-1, AudioDecoder::CodecSampleRateHz(kDecoderCELT_32)); + EXPECT_EQ(-1, AudioDecoder::CodecSampleRateHz(kDecoderCELT_32_2ch)); +#endif +} + +TEST(AudioDecoder, CodecSupported) { + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCMu)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCMa)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCMu_2ch)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCMa_2ch)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderILBC)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISAC)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISACswb)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISACfb)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16B)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bwb)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bswb32kHz)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bswb48kHz)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16B_2ch)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bwb_2ch)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bswb32kHz_2ch)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bswb48kHz_2ch)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16B_5ch)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderG722)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderG722_2ch)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderRED)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderAVT)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderCNGnb)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderCNGwb)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderCNGswb32kHz)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderCNGswb48kHz)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderArbitrary)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderOpus)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderOpus_2ch)); +#ifdef WEBRTC_CODEC_CELT + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderCELT_32)); + EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderCELT_32_2ch)); +#else + EXPECT_FALSE(AudioDecoder::CodecSupported(kDecoderCELT_32)); + EXPECT_FALSE(AudioDecoder::CodecSupported(kDecoderCELT_32_2ch)); +#endif +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder_unittests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder_unittests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder_unittests.isolate 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_decoder_unittests.isolate 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,33 @@ +# Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. +{ + 'conditions': [ + ['OS=="android"', { + 'variables': { + 'files': [ + '<(DEPTH)/resources/', + '<(DEPTH)/data/', + ], + }, + }], + ['OS=="linux" or OS=="mac" or OS=="win"', { + 'variables': { + 'command': [ + '<(DEPTH)/testing/test_env.py', + '<(PRODUCT_DIR)/audio_decoder_unittests<(EXECUTABLE_SUFFIX)', + ], + 'files': [ + '<(DEPTH)/DEPS', + '<(DEPTH)/resources/audio_coding/testfile32kHz.pcm', + '<(DEPTH)/testing/test_env.py', + '<(PRODUCT_DIR)/audio_decoder_unittests<(EXECUTABLE_SUFFIX)', + ], + }, + }], + ], +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_multi_vector.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_multi_vector.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_multi_vector.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_multi_vector.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" + +#include + +#include + +#include "webrtc/typedefs.h" + +namespace webrtc { + +AudioMultiVector::AudioMultiVector(size_t N) { + assert(N > 0); + if (N < 1) N = 1; + for (size_t n = 0; n < N; ++n) { + channels_.push_back(new AudioVector); + } + num_channels_ = N; +} + +AudioMultiVector::AudioMultiVector(size_t N, size_t initial_size) { + assert(N > 0); + if (N < 1) N = 1; + for (size_t n = 0; n < N; ++n) { + channels_.push_back(new AudioVector(initial_size)); + } + num_channels_ = N; +} + +AudioMultiVector::~AudioMultiVector() { + std::vector::iterator it = channels_.begin(); + while (it != channels_.end()) { + delete (*it); + ++it; + } +} + +void AudioMultiVector::Clear() { + for (size_t i = 0; i < num_channels_; ++i) { + channels_[i]->Clear(); + } +} + +void AudioMultiVector::Zeros(size_t length) { + for (size_t i = 0; i < num_channels_; ++i) { + channels_[i]->Clear(); + channels_[i]->Extend(length); + } +} + +void AudioMultiVector::CopyTo(AudioMultiVector* copy_to) const { + if (copy_to) { + for (size_t i = 0; i < num_channels_; ++i) { + channels_[i]->CopyTo(&(*copy_to)[i]); + } + } +} + +void AudioMultiVector::PushBackInterleaved(const int16_t* append_this, + size_t length) { + assert(length % num_channels_ == 0); + if (num_channels_ == 1) { + // Special case to avoid extra allocation and data shuffling. + channels_[0]->PushBack(append_this, length); + return; + } + size_t length_per_channel = length / num_channels_; + int16_t* temp_array = new int16_t[length_per_channel]; // Temporary storage. + for (size_t channel = 0; channel < num_channels_; ++channel) { + // Copy elements to |temp_array|. + // Set |source_ptr| to first element of this channel. + const int16_t* source_ptr = &append_this[channel]; + for (size_t i = 0; i < length_per_channel; ++i) { + temp_array[i] = *source_ptr; + source_ptr += num_channels_; // Jump to next element of this channel. + } + channels_[channel]->PushBack(temp_array, length_per_channel); + } + delete [] temp_array; +} + +void AudioMultiVector::PushBack(const AudioMultiVector& append_this) { + assert(num_channels_ == append_this.num_channels_); + if (num_channels_ == append_this.num_channels_) { + for (size_t i = 0; i < num_channels_; ++i) { + channels_[i]->PushBack(append_this[i]); + } + } +} + +void AudioMultiVector::PushBackFromIndex(const AudioMultiVector& append_this, + size_t index) { + assert(index < append_this.Size()); + index = std::min(index, append_this.Size() - 1); + size_t length = append_this.Size() - index; + assert(num_channels_ == append_this.num_channels_); + if (num_channels_ == append_this.num_channels_) { + for (size_t i = 0; i < num_channels_; ++i) { + channels_[i]->PushBack(&append_this[i][index], length); + } + } +} + +void AudioMultiVector::PopFront(size_t length) { + for (size_t i = 0; i < num_channels_; ++i) { + channels_[i]->PopFront(length); + } +} + +void AudioMultiVector::PopBack(size_t length) { + for (size_t i = 0; i < num_channels_; ++i) { + channels_[i]->PopBack(length); + } +} + +size_t AudioMultiVector::ReadInterleaved(size_t length, + int16_t* destination) const { + return ReadInterleavedFromIndex(0, length, destination); +} + +size_t AudioMultiVector::ReadInterleavedFromIndex(size_t start_index, + size_t length, + int16_t* destination) const { + if (!destination) { + return 0; + } + size_t index = 0; // Number of elements written to |destination| so far. + assert(start_index <= Size()); + start_index = std::min(start_index, Size()); + if (length + start_index > Size()) { + length = Size() - start_index; + } + if (num_channels_ == 1) { + // Special case to avoid the nested for loop below. + memcpy(destination, &(*this)[0][start_index], length * sizeof(int16_t)); + return length; + } + for (size_t i = 0; i < length; ++i) { + for (size_t channel = 0; channel < num_channels_; ++channel) { + destination[index] = (*this)[channel][i + start_index]; + ++index; + } + } + return index; +} + +size_t AudioMultiVector::ReadInterleavedFromEnd(size_t length, + int16_t* destination) const { + length = std::min(length, Size()); // Cannot read more than Size() elements. + return ReadInterleavedFromIndex(Size() - length, length, destination); +} + +void AudioMultiVector::OverwriteAt(const AudioMultiVector& insert_this, + size_t length, + size_t position) { + assert(num_channels_ == insert_this.num_channels_); + // Cap |length| at the length of |insert_this|. + assert(length <= insert_this.Size()); + length = std::min(length, insert_this.Size()); + if (num_channels_ == insert_this.num_channels_) { + for (size_t i = 0; i < num_channels_; ++i) { + channels_[i]->OverwriteAt(&insert_this[i][0], length, position); + } + } +} + +void AudioMultiVector::CrossFade(const AudioMultiVector& append_this, + size_t fade_length) { + assert(num_channels_ == append_this.num_channels_); + if (num_channels_ == append_this.num_channels_) { + for (size_t i = 0; i < num_channels_; ++i) { + channels_[i]->CrossFade(append_this[i], fade_length); + } + } +} + +size_t AudioMultiVector::Size() const { + assert(channels_[0]); + return channels_[0]->Size(); +} + +void AudioMultiVector::AssertSize(size_t required_size) { + if (Size() < required_size) { + size_t extend_length = required_size - Size(); + for (size_t channel = 0; channel < num_channels_; ++channel) { + channels_[channel]->Extend(extend_length); + } + } +} + +bool AudioMultiVector::Empty() const { + assert(channels_[0]); + return channels_[0]->Empty(); +} + +void AudioMultiVector::CopyChannel(size_t from_channel, size_t to_channel) { + assert(from_channel < num_channels_); + assert(to_channel < num_channels_); + channels_[from_channel]->CopyTo(channels_[to_channel]); +} + +const AudioVector& AudioMultiVector::operator[](size_t index) const { + return *(channels_[index]); +} + +AudioVector& AudioMultiVector::operator[](size_t index) { + return *(channels_[index]); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_multi_vector.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_multi_vector.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_multi_vector.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_multi_vector.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_AUDIO_MULTI_VECTOR_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_AUDIO_MULTI_VECTOR_H_ + +#include // Access to size_t. + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/audio_vector.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +class AudioMultiVector { + public: + // Creates an empty AudioMultiVector with |N| audio channels. |N| must be + // larger than 0. + explicit AudioMultiVector(size_t N); + + // Creates an AudioMultiVector with |N| audio channels, each channel having + // an initial size. |N| must be larger than 0. + AudioMultiVector(size_t N, size_t initial_size); + + virtual ~AudioMultiVector(); + + // Deletes all values and make the vector empty. + virtual void Clear(); + + // Clears the vector and inserts |length| zeros into each channel. + virtual void Zeros(size_t length); + + // Copies all values from this vector to |copy_to|. Any contents in |copy_to| + // are deleted. After the operation is done, |copy_to| will be an exact + // replica of this object. The source and the destination must have the same + // number of channels. + virtual void CopyTo(AudioMultiVector* copy_to) const; + + // Appends the contents of array |append_this| to the end of this + // object. The array is assumed to be channel-interleaved. |length| must be + // an even multiple of this object's number of channels. + // The length of this object is increased with the |length| divided by the + // number of channels. + virtual void PushBackInterleaved(const int16_t* append_this, size_t length); + + // Appends the contents of AudioMultiVector |append_this| to this object. The + // length of this object is increased with the length of |append_this|. + virtual void PushBack(const AudioMultiVector& append_this); + + // Appends the contents of AudioMultiVector |append_this| to this object, + // taken from |index| up until the end of |append_this|. The length of this + // object is increased. + virtual void PushBackFromIndex(const AudioMultiVector& append_this, + size_t index); + + // Removes |length| elements from the beginning of this object, from each + // channel. + virtual void PopFront(size_t length); + + // Removes |length| elements from the end of this object, from each + // channel. + virtual void PopBack(size_t length); + + // Reads |length| samples from each channel and writes them interleaved to + // |destination|. The total number of elements written to |destination| is + // returned, i.e., |length| * number of channels. If the AudioMultiVector + // contains less than |length| samples per channel, this is reflected in the + // return value. + virtual size_t ReadInterleaved(size_t length, int16_t* destination) const; + + // Like ReadInterleaved() above, but reads from |start_index| instead of from + // the beginning. + virtual size_t ReadInterleavedFromIndex(size_t start_index, + size_t length, + int16_t* destination) const; + + // Like ReadInterleaved() above, but reads from the end instead of from + // the beginning. + virtual size_t ReadInterleavedFromEnd(size_t length, + int16_t* destination) const; + + // Overwrites each channel in this AudioMultiVector with values taken from + // |insert_this|. The values are taken from the beginning of |insert_this| and + // are inserted starting at |position|. |length| values are written into each + // channel. If |length| and |position| are selected such that the new data + // extends beyond the end of the current AudioVector, the vector is extended + // to accommodate the new data. |length| is limited to the length of + // |insert_this|. + virtual void OverwriteAt(const AudioMultiVector& insert_this, + size_t length, + size_t position); + + // Appends |append_this| to the end of the current vector. Lets the two + // vectors overlap by |fade_length| samples (per channel), and cross-fade + // linearly in this region. + virtual void CrossFade(const AudioMultiVector& append_this, + size_t fade_length); + + // Returns the number of channels. + virtual size_t Channels() const { return num_channels_; } + + // Returns the number of elements per channel in this AudioMultiVector. + virtual size_t Size() const; + + // Verify that each channel can hold at least |required_size| elements. If + // not, extend accordingly. + virtual void AssertSize(size_t required_size); + + virtual bool Empty() const; + + // Copies the data between two channels in the AudioMultiVector. The method + // does not add any new channel. Thus, |from_channel| and |to_channel| must + // both be valid channel numbers. + virtual void CopyChannel(size_t from_channel, size_t to_channel); + + // Accesses and modifies a channel (i.e., an AudioVector object) of this + // AudioMultiVector. + const AudioVector& operator[](size_t index) const; + AudioVector& operator[](size_t index); + + protected: + std::vector channels_; + size_t num_channels_; + + private: + DISALLOW_COPY_AND_ASSIGN(AudioMultiVector); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_AUDIO_MULTI_VECTOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_multi_vector_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_multi_vector_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_multi_vector_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_multi_vector_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" + +#include +#include + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// This is a value-parameterized test. The test cases are instantiated with +// different values for the test parameter, which is used to determine the +// number of channels in the AudioMultiBuffer. Note that it is not possible +// to combine typed testing with value-parameterized testing, and since the +// tests for AudioVector already covers a number of different type parameters, +// this test focuses on testing different number of channels, and keeping the +// value type constant. + +class AudioMultiVectorTest : public ::testing::TestWithParam { + protected: + AudioMultiVectorTest() + : num_channels_(GetParam()), // Get the test parameter. + interleaved_length_(num_channels_ * array_length()) { + array_interleaved_ = new int16_t[num_channels_ * array_length()]; + } + + ~AudioMultiVectorTest() { + delete [] array_interleaved_; + } + + virtual void SetUp() { + // Populate test arrays. + for (size_t i = 0; i < array_length(); ++i) { + array_[i] = static_cast(i); + } + int16_t* ptr = array_interleaved_; + // Write 100, 101, 102, ... for first channel. + // Write 200, 201, 202, ... for second channel. + // And so on. + for (size_t i = 0; i < array_length(); ++i) { + for (size_t j = 1; j <= num_channels_; ++j) { + *ptr = j * 100 + i; + ++ptr; + } + } + } + + size_t array_length() const { + return sizeof(array_) / sizeof(array_[0]); + } + + const size_t num_channels_; + size_t interleaved_length_; + int16_t array_[10]; + int16_t* array_interleaved_; +}; + +// Create and destroy AudioMultiVector objects, both empty and with a predefined +// length. +TEST_P(AudioMultiVectorTest, CreateAndDestroy) { + AudioMultiVector vec1(num_channels_); + EXPECT_TRUE(vec1.Empty()); + EXPECT_EQ(num_channels_, vec1.Channels()); + EXPECT_EQ(0u, vec1.Size()); + + size_t initial_size = 17; + AudioMultiVector vec2(num_channels_, initial_size); + EXPECT_FALSE(vec2.Empty()); + EXPECT_EQ(num_channels_, vec2.Channels()); + EXPECT_EQ(initial_size, vec2.Size()); +} + +// Test the subscript operator [] for getting and setting. +TEST_P(AudioMultiVectorTest, SubscriptOperator) { + AudioMultiVector vec(num_channels_, array_length()); + for (size_t channel = 0; channel < num_channels_; ++channel) { + for (size_t i = 0; i < array_length(); ++i) { + vec[channel][i] = static_cast(i); + // Make sure to use the const version. + const AudioVector& audio_vec = vec[channel]; + EXPECT_EQ(static_cast(i), audio_vec[i]); + } + } +} + +// Test the PushBackInterleaved method and the CopyFrom method. The Clear +// method is also invoked. +TEST_P(AudioMultiVectorTest, PushBackInterleavedAndCopy) { + AudioMultiVector vec(num_channels_); + vec.PushBackInterleaved(array_interleaved_, interleaved_length_); + AudioMultiVector vec_copy(num_channels_); + vec.CopyTo(&vec_copy); // Copy from |vec| to |vec_copy|. + ASSERT_EQ(num_channels_, vec.Channels()); + ASSERT_EQ(array_length(), vec.Size()); + ASSERT_EQ(num_channels_, vec_copy.Channels()); + ASSERT_EQ(array_length(), vec_copy.Size()); + for (size_t channel = 0; channel < vec.Channels(); ++channel) { + for (size_t i = 0; i < array_length(); ++i) { + EXPECT_EQ(static_cast((channel + 1) * 100 + i), vec[channel][i]); + EXPECT_EQ(vec[channel][i], vec_copy[channel][i]); + } + } + + // Clear |vec| and verify that it is empty. + vec.Clear(); + EXPECT_TRUE(vec.Empty()); + + // Now copy the empty vector and verify that the copy becomes empty too. + vec.CopyTo(&vec_copy); + EXPECT_TRUE(vec_copy.Empty()); +} + +// Try to copy to a NULL pointer. Nothing should happen. +TEST_P(AudioMultiVectorTest, CopyToNull) { + AudioMultiVector vec(num_channels_); + AudioMultiVector* vec_copy = NULL; + vec.PushBackInterleaved(array_interleaved_, interleaved_length_); + vec.CopyTo(vec_copy); +} + +// Test the PushBack method with another AudioMultiVector as input argument. +TEST_P(AudioMultiVectorTest, PushBackVector) { + AudioMultiVector vec1(num_channels_, array_length()); + AudioMultiVector vec2(num_channels_, array_length()); + // Set the first vector to [0, 1, ..., array_length() - 1] + + // 100 * channel_number. + // Set the second vector to [array_length(), array_length() + 1, ..., + // 2 * array_length() - 1] + 100 * channel_number. + for (size_t channel = 0; channel < num_channels_; ++channel) { + for (size_t i = 0; i < array_length(); ++i) { + vec1[channel][i] = static_cast(i + 100 * channel); + vec2[channel][i] = + static_cast(i + 100 * channel + array_length()); + } + } + // Append vec2 to the back of vec1. + vec1.PushBack(vec2); + ASSERT_EQ(2u * array_length(), vec1.Size()); + for (size_t channel = 0; channel < num_channels_; ++channel) { + for (size_t i = 0; i < 2 * array_length(); ++i) { + EXPECT_EQ(static_cast(i + 100 * channel), vec1[channel][i]); + } + } +} + +// Test the PushBackFromIndex method. +TEST_P(AudioMultiVectorTest, PushBackFromIndex) { + AudioMultiVector vec1(num_channels_); + vec1.PushBackInterleaved(array_interleaved_, interleaved_length_); + AudioMultiVector vec2(num_channels_); + + // Append vec1 to the back of vec2 (which is empty). Read vec1 from the second + // last element. + vec2.PushBackFromIndex(vec1, array_length() - 2); + ASSERT_EQ(2u, vec2.Size()); + for (size_t channel = 0; channel < num_channels_; ++channel) { + for (size_t i = 0; i < 2; ++i) { + EXPECT_EQ(array_interleaved_[channel + num_channels_ * + (array_length() - 2 + i)], vec2[channel][i]); + } + } +} + +// Starts with pushing some values to the vector, then test the Zeros method. +TEST_P(AudioMultiVectorTest, Zeros) { + AudioMultiVector vec(num_channels_); + vec.PushBackInterleaved(array_interleaved_, interleaved_length_); + vec.Zeros(2 * array_length()); + ASSERT_EQ(num_channels_, vec.Channels()); + ASSERT_EQ(2u * array_length(), vec.Size()); + for (size_t channel = 0; channel < num_channels_; ++channel) { + for (size_t i = 0; i < 2 * array_length(); ++i) { + EXPECT_EQ(0, vec[channel][i]); + } + } +} + +// Test the ReadInterleaved method +TEST_P(AudioMultiVectorTest, ReadInterleaved) { + AudioMultiVector vec(num_channels_); + vec.PushBackInterleaved(array_interleaved_, interleaved_length_); + int16_t* output = new int16_t[interleaved_length_]; + // Read 5 samples. + size_t read_samples = 5; + EXPECT_EQ(num_channels_ * read_samples, + vec.ReadInterleaved(read_samples, output)); + EXPECT_EQ(0, + memcmp(array_interleaved_, output, read_samples * sizeof(int16_t))); + + // Read too many samples. Expect to get all samples from the vector. + EXPECT_EQ(interleaved_length_, + vec.ReadInterleaved(array_length() + 1, output)); + EXPECT_EQ(0, + memcmp(array_interleaved_, output, read_samples * sizeof(int16_t))); + + delete [] output; +} + +// Try to read to a NULL pointer. Expected to return 0. +TEST_P(AudioMultiVectorTest, ReadInterleavedToNull) { + AudioMultiVector vec(num_channels_); + vec.PushBackInterleaved(array_interleaved_, interleaved_length_); + int16_t* output = NULL; + // Read 5 samples. + size_t read_samples = 5; + EXPECT_EQ(0u, vec.ReadInterleaved(read_samples, output)); +} + +// Test the PopFront method. +TEST_P(AudioMultiVectorTest, PopFront) { + AudioMultiVector vec(num_channels_); + vec.PushBackInterleaved(array_interleaved_, interleaved_length_); + vec.PopFront(1); // Remove one element from each channel. + ASSERT_EQ(array_length() - 1u, vec.Size()); + // Let |ptr| point to the second element of the first channel in the + // interleaved array. + int16_t* ptr = &array_interleaved_[num_channels_]; + for (size_t i = 0; i < array_length() - 1; ++i) { + for (size_t channel = 0; channel < num_channels_; ++channel) { + EXPECT_EQ(*ptr, vec[channel][i]); + ++ptr; + } + } + vec.PopFront(array_length()); // Remove more elements than vector size. + EXPECT_EQ(0u, vec.Size()); +} + +// Test the PopBack method. +TEST_P(AudioMultiVectorTest, PopBack) { + AudioMultiVector vec(num_channels_); + vec.PushBackInterleaved(array_interleaved_, interleaved_length_); + vec.PopBack(1); // Remove one element from each channel. + ASSERT_EQ(array_length() - 1u, vec.Size()); + // Let |ptr| point to the first element of the first channel in the + // interleaved array. + int16_t* ptr = array_interleaved_; + for (size_t i = 0; i < array_length() - 1; ++i) { + for (size_t channel = 0; channel < num_channels_; ++channel) { + EXPECT_EQ(*ptr, vec[channel][i]); + ++ptr; + } + } + vec.PopBack(array_length()); // Remove more elements than vector size. + EXPECT_EQ(0u, vec.Size()); +} + +// Test the AssertSize method. +TEST_P(AudioMultiVectorTest, AssertSize) { + AudioMultiVector vec(num_channels_, array_length()); + EXPECT_EQ(array_length(), vec.Size()); + // Start with asserting with smaller sizes than already allocated. + vec.AssertSize(0); + vec.AssertSize(array_length() - 1); + // Nothing should have changed. + EXPECT_EQ(array_length(), vec.Size()); + // Assert with one element longer than already allocated. + vec.AssertSize(array_length() + 1); + // Expect vector to have grown. + EXPECT_EQ(array_length() + 1, vec.Size()); + // Also check the individual AudioVectors. + for (size_t channel = 0; channel < vec.Channels(); ++channel) { + EXPECT_EQ(array_length() + 1u, vec[channel].Size()); + } +} + +// Test the PushBack method with another AudioMultiVector as input argument. +TEST_P(AudioMultiVectorTest, OverwriteAt) { + AudioMultiVector vec1(num_channels_); + vec1.PushBackInterleaved(array_interleaved_, interleaved_length_); + AudioMultiVector vec2(num_channels_); + vec2.Zeros(3); // 3 zeros in each channel. + // Overwrite vec2 at position 5. + vec1.OverwriteAt(vec2, 3, 5); + // Verify result. + // Length remains the same. + ASSERT_EQ(array_length(), vec1.Size()); + int16_t* ptr = array_interleaved_; + for (size_t i = 0; i < array_length() - 1; ++i) { + for (size_t channel = 0; channel < num_channels_; ++channel) { + if (i >= 5 && i <= 7) { + // Elements 5, 6, 7 should have been replaced with zeros. + EXPECT_EQ(0, vec1[channel][i]); + } else { + EXPECT_EQ(*ptr, vec1[channel][i]); + } + ++ptr; + } + } +} + +// Test the CopyChannel method, when the test is instantiated with at least two +// channels. +TEST_P(AudioMultiVectorTest, CopyChannel) { + if (num_channels_ < 2) + return; + + AudioMultiVector vec(num_channels_); + vec.PushBackInterleaved(array_interleaved_, interleaved_length_); + // Create a reference copy. + AudioMultiVector ref(num_channels_); + ref.PushBack(vec); + // Copy from first to last channel. + vec.CopyChannel(0, num_channels_ - 1); + // Verify that the first and last channels are identical; the others should + // be left untouched. + for (size_t i = 0; i < array_length(); ++i) { + // Verify that all but the last channel are untouched. + for (size_t channel = 0; channel < num_channels_ - 1; ++channel) { + EXPECT_EQ(ref[channel][i], vec[channel][i]); + } + // Verify that the last and the first channels are identical. + EXPECT_EQ(vec[0][i], vec[num_channels_ - 1][i]); + } +} + +INSTANTIATE_TEST_CASE_P(TestNumChannels, + AudioMultiVectorTest, + ::testing::Values(static_cast(1), + static_cast(2), + static_cast(5))); +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_vector.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_vector.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_vector.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_vector.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/audio_vector.h" + +#include + +#include + +#include "webrtc/typedefs.h" + +namespace webrtc { + +void AudioVector::Clear() { + first_free_ix_ = 0; +} + +void AudioVector::CopyTo(AudioVector* copy_to) const { + if (copy_to) { + copy_to->Reserve(Size()); + assert(copy_to->capacity_ >= Size()); + memcpy(copy_to->array_.get(), array_.get(), Size() * sizeof(int16_t)); + copy_to->first_free_ix_ = first_free_ix_; + } +} + +void AudioVector::PushFront(const AudioVector& prepend_this) { + size_t insert_length = prepend_this.Size(); + Reserve(Size() + insert_length); + memmove(&array_[insert_length], &array_[0], Size() * sizeof(int16_t)); + memcpy(&array_[0], &prepend_this.array_[0], insert_length * sizeof(int16_t)); + first_free_ix_ += insert_length; +} + +void AudioVector::PushFront(const int16_t* prepend_this, size_t length) { + // Same operation as InsertAt beginning. + InsertAt(prepend_this, length, 0); +} + +void AudioVector::PushBack(const AudioVector& append_this) { + PushBack(append_this.array_.get(), append_this.Size()); +} + +void AudioVector::PushBack(const int16_t* append_this, size_t length) { + Reserve(Size() + length); + memcpy(&array_[first_free_ix_], append_this, length * sizeof(int16_t)); + first_free_ix_ += length; +} + +void AudioVector::PopFront(size_t length) { + if (length >= Size()) { + // Remove all elements. + Clear(); + } else { + size_t remaining_samples = Size() - length; + memmove(&array_[0], &array_[length], remaining_samples * sizeof(int16_t)); + first_free_ix_ -= length; + } +} + +void AudioVector::PopBack(size_t length) { + // Never remove more than what is in the array. + length = std::min(length, Size()); + first_free_ix_ -= length; +} + +void AudioVector::Extend(size_t extra_length) { + Reserve(Size() + extra_length); + memset(&array_[first_free_ix_], 0, extra_length * sizeof(int16_t)); + first_free_ix_ += extra_length; +} + +void AudioVector::InsertAt(const int16_t* insert_this, + size_t length, + size_t position) { + Reserve(Size() + length); + // Cap the position at the current vector length, to be sure the iterator + // does not extend beyond the end of the vector. + position = std::min(Size(), position); + int16_t* insert_position_ptr = &array_[position]; + size_t samples_to_move = Size() - position; + memmove(insert_position_ptr + length, insert_position_ptr, + samples_to_move * sizeof(int16_t)); + memcpy(insert_position_ptr, insert_this, length * sizeof(int16_t)); + first_free_ix_ += length; +} + +void AudioVector::InsertZerosAt(size_t length, + size_t position) { + Reserve(Size() + length); + // Cap the position at the current vector length, to be sure the iterator + // does not extend beyond the end of the vector. + position = std::min(capacity_, position); + int16_t* insert_position_ptr = &array_[position]; + size_t samples_to_move = Size() - position; + memmove(insert_position_ptr + length, insert_position_ptr, + samples_to_move * sizeof(int16_t)); + memset(insert_position_ptr, 0, length * sizeof(int16_t)); + first_free_ix_ += length; +} + +void AudioVector::OverwriteAt(const int16_t* insert_this, + size_t length, + size_t position) { + // Cap the insert position at the current array length. + position = std::min(Size(), position); + Reserve(position + length); + memcpy(&array_[position], insert_this, length * sizeof(int16_t)); + if (position + length > Size()) { + // Array was expanded. + first_free_ix_ += position + length - Size(); + } +} + +void AudioVector::CrossFade(const AudioVector& append_this, + size_t fade_length) { + // Fade length cannot be longer than the current vector or |append_this|. + assert(fade_length <= Size()); + assert(fade_length <= append_this.Size()); + fade_length = std::min(fade_length, Size()); + fade_length = std::min(fade_length, append_this.Size()); + size_t position = Size() - fade_length; + // Cross fade the overlapping regions. + // |alpha| is the mixing factor in Q14. + // TODO(hlundin): Consider skipping +1 in the denominator to produce a + // smoother cross-fade, in particular at the end of the fade. + int alpha_step = 16384 / (static_cast(fade_length) + 1); + int alpha = 16384; + for (size_t i = 0; i < fade_length; ++i) { + alpha -= alpha_step; + array_[position + i] = (alpha * array_[position + i] + + (16384 - alpha) * append_this[i] + 8192) >> 14; + } + assert(alpha >= 0); // Verify that the slope was correct. + // Append what is left of |append_this|. + size_t samples_to_push_back = append_this.Size() - fade_length; + if (samples_to_push_back > 0) + PushBack(&append_this[fade_length], samples_to_push_back); +} + +const int16_t& AudioVector::operator[](size_t index) const { + return array_[index]; +} + +int16_t& AudioVector::operator[](size_t index) { + return array_[index]; +} + +void AudioVector::Reserve(size_t n) { + if (capacity_ < n) { + scoped_ptr temp_array(new int16_t[n]); + memcpy(temp_array.get(), array_.get(), Size() * sizeof(int16_t)); + array_.swap(temp_array); + capacity_ = n; + } +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_vector.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_vector.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_vector.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_vector.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_AUDIO_VECTOR_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_AUDIO_VECTOR_H_ + +#include // Access to size_t. + +#include "webrtc/base/constructormagic.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +class AudioVector { + public: + // Creates an empty AudioVector. + AudioVector() + : array_(new int16_t[kDefaultInitialSize]), + first_free_ix_(0), + capacity_(kDefaultInitialSize) {} + + // Creates an AudioVector with an initial size. + explicit AudioVector(size_t initial_size) + : array_(new int16_t[initial_size]), + first_free_ix_(initial_size), + capacity_(initial_size) { + memset(array_.get(), 0, initial_size * sizeof(int16_t)); + } + + virtual ~AudioVector() {} + + // Deletes all values and make the vector empty. + virtual void Clear(); + + // Copies all values from this vector to |copy_to|. Any contents in |copy_to| + // are deleted before the copy operation. After the operation is done, + // |copy_to| will be an exact replica of this object. + virtual void CopyTo(AudioVector* copy_to) const; + + // Prepends the contents of AudioVector |prepend_this| to this object. The + // length of this object is increased with the length of |prepend_this|. + virtual void PushFront(const AudioVector& prepend_this); + + // Same as above, but with an array |prepend_this| with |length| elements as + // source. + virtual void PushFront(const int16_t* prepend_this, size_t length); + + // Same as PushFront but will append to the end of this object. + virtual void PushBack(const AudioVector& append_this); + + // Same as PushFront but will append to the end of this object. + virtual void PushBack(const int16_t* append_this, size_t length); + + // Removes |length| elements from the beginning of this object. + virtual void PopFront(size_t length); + + // Removes |length| elements from the end of this object. + virtual void PopBack(size_t length); + + // Extends this object with |extra_length| elements at the end. The new + // elements are initialized to zero. + virtual void Extend(size_t extra_length); + + // Inserts |length| elements taken from the array |insert_this| and insert + // them at |position|. The length of the AudioVector is increased by |length|. + // |position| = 0 means that the new values are prepended to the vector. + // |position| = Size() means that the new values are appended to the vector. + virtual void InsertAt(const int16_t* insert_this, size_t length, + size_t position); + + // Like InsertAt, but inserts |length| zero elements at |position|. + virtual void InsertZerosAt(size_t length, size_t position); + + // Overwrites |length| elements of this AudioVector with values taken from the + // array |insert_this|, starting at |position|. The definition of |position| + // is the same as for InsertAt(). If |length| and |position| are selected + // such that the new data extends beyond the end of the current AudioVector, + // the vector is extended to accommodate the new data. + virtual void OverwriteAt(const int16_t* insert_this, + size_t length, + size_t position); + + // Appends |append_this| to the end of the current vector. Lets the two + // vectors overlap by |fade_length| samples, and cross-fade linearly in this + // region. + virtual void CrossFade(const AudioVector& append_this, size_t fade_length); + + // Returns the number of elements in this AudioVector. + virtual size_t Size() const { return first_free_ix_; } + + // Returns true if this AudioVector is empty. + virtual bool Empty() const { return (first_free_ix_ == 0); } + + // Accesses and modifies an element of AudioVector. + const int16_t& operator[](size_t index) const; + int16_t& operator[](size_t index); + + private: + static const size_t kDefaultInitialSize = 10; + + void Reserve(size_t n); + + scoped_ptr array_; + size_t first_free_ix_; // The first index after the last sample in array_. + // Note that this index may point outside of array_. + size_t capacity_; // Allocated number of samples in the array. + + DISALLOW_COPY_AND_ASSIGN(AudioVector); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_AUDIO_VECTOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_vector_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_vector_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_vector_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/audio_vector_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/audio_vector.h" + +#include +#include + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +class AudioVectorTest : public ::testing::Test { + protected: + virtual void SetUp() { + // Populate test array. + for (size_t i = 0; i < array_length(); ++i) { + array_[i] = i; + } + } + + size_t array_length() const { + return sizeof(array_) / sizeof(array_[0]); + } + + int16_t array_[10]; +}; + +// Create and destroy AudioVector objects, both empty and with a predefined +// length. +TEST_F(AudioVectorTest, CreateAndDestroy) { + AudioVector vec1; + EXPECT_TRUE(vec1.Empty()); + EXPECT_EQ(0u, vec1.Size()); + + size_t initial_size = 17; + AudioVector vec2(initial_size); + EXPECT_FALSE(vec2.Empty()); + EXPECT_EQ(initial_size, vec2.Size()); +} + +// Test the subscript operator [] for getting and setting. +TEST_F(AudioVectorTest, SubscriptOperator) { + AudioVector vec(array_length()); + for (size_t i = 0; i < array_length(); ++i) { + vec[i] = static_cast(i); + const int16_t& value = vec[i]; // Make sure to use the const version. + EXPECT_EQ(static_cast(i), value); + } +} + +// Test the PushBack method and the CopyFrom method. The Clear method is also +// invoked. +TEST_F(AudioVectorTest, PushBackAndCopy) { + AudioVector vec; + AudioVector vec_copy; + vec.PushBack(array_, array_length()); + vec.CopyTo(&vec_copy); // Copy from |vec| to |vec_copy|. + ASSERT_EQ(array_length(), vec.Size()); + ASSERT_EQ(array_length(), vec_copy.Size()); + for (size_t i = 0; i < array_length(); ++i) { + EXPECT_EQ(array_[i], vec[i]); + EXPECT_EQ(array_[i], vec_copy[i]); + } + + // Clear |vec| and verify that it is empty. + vec.Clear(); + EXPECT_TRUE(vec.Empty()); + + // Now copy the empty vector and verify that the copy becomes empty too. + vec.CopyTo(&vec_copy); + EXPECT_TRUE(vec_copy.Empty()); +} + +// Try to copy to a NULL pointer. Nothing should happen. +TEST_F(AudioVectorTest, CopyToNull) { + AudioVector vec; + AudioVector* vec_copy = NULL; + vec.PushBack(array_, array_length()); + vec.CopyTo(vec_copy); +} + +// Test the PushBack method with another AudioVector as input argument. +TEST_F(AudioVectorTest, PushBackVector) { + static const size_t kLength = 10; + AudioVector vec1(kLength); + AudioVector vec2(kLength); + // Set the first vector to [0, 1, ..., kLength - 1]. + // Set the second vector to [kLength, kLength + 1, ..., 2 * kLength - 1]. + for (size_t i = 0; i < kLength; ++i) { + vec1[i] = static_cast(i); + vec2[i] = static_cast(i + kLength); + } + // Append vec2 to the back of vec1. + vec1.PushBack(vec2); + ASSERT_EQ(2 * kLength, vec1.Size()); + for (size_t i = 0; i < 2 * kLength; ++i) { + EXPECT_EQ(static_cast(i), vec1[i]); + } +} + +// Test the PushFront method. +TEST_F(AudioVectorTest, PushFront) { + AudioVector vec; + vec.PushFront(array_, array_length()); + ASSERT_EQ(array_length(), vec.Size()); + for (size_t i = 0; i < array_length(); ++i) { + EXPECT_EQ(array_[i], vec[i]); + } +} + +// Test the PushFront method with another AudioVector as input argument. +TEST_F(AudioVectorTest, PushFrontVector) { + static const size_t kLength = 10; + AudioVector vec1(kLength); + AudioVector vec2(kLength); + // Set the first vector to [0, 1, ..., kLength - 1]. + // Set the second vector to [kLength, kLength + 1, ..., 2 * kLength - 1]. + for (size_t i = 0; i < kLength; ++i) { + vec1[i] = static_cast(i); + vec2[i] = static_cast(i + kLength); + } + // Prepend vec1 to the front of vec2. + vec2.PushFront(vec1); + ASSERT_EQ(2 * kLength, vec2.Size()); + for (size_t i = 0; i < 2 * kLength; ++i) { + EXPECT_EQ(static_cast(i), vec2[i]); + } +} + +// Test the PopFront method. +TEST_F(AudioVectorTest, PopFront) { + AudioVector vec; + vec.PushBack(array_, array_length()); + vec.PopFront(1); // Remove one element. + EXPECT_EQ(array_length() - 1u, vec.Size()); + for (size_t i = 0; i < array_length() - 1; ++i) { + EXPECT_EQ(static_cast(i + 1), vec[i]); + } + vec.PopFront(array_length()); // Remove more elements than vector size. + EXPECT_EQ(0u, vec.Size()); +} + +// Test the PopBack method. +TEST_F(AudioVectorTest, PopBack) { + AudioVector vec; + vec.PushBack(array_, array_length()); + vec.PopBack(1); // Remove one element. + EXPECT_EQ(array_length() - 1u, vec.Size()); + for (size_t i = 0; i < array_length() - 1; ++i) { + EXPECT_EQ(static_cast(i), vec[i]); + } + vec.PopBack(array_length()); // Remove more elements than vector size. + EXPECT_EQ(0u, vec.Size()); +} + +// Test the Extend method. +TEST_F(AudioVectorTest, Extend) { + AudioVector vec; + vec.PushBack(array_, array_length()); + vec.Extend(5); // Extend with 5 elements, which should all be zeros. + ASSERT_EQ(array_length() + 5u, vec.Size()); + // Verify that all are zero. + for (size_t i = array_length(); i < array_length() + 5; ++i) { + EXPECT_EQ(0, vec[i]); + } +} + +// Test the InsertAt method with an insert position in the middle of the vector. +TEST_F(AudioVectorTest, InsertAt) { + AudioVector vec; + vec.PushBack(array_, array_length()); + static const int kNewLength = 5; + int16_t new_array[kNewLength]; + // Set array elements to {100, 101, 102, ... }. + for (int i = 0; i < kNewLength; ++i) { + new_array[i] = 100 + i; + } + int insert_position = 5; + vec.InsertAt(new_array, kNewLength, insert_position); + // Verify that the vector looks as follows: + // {0, 1, ..., |insert_position| - 1, 100, 101, ..., 100 + kNewLength - 1, + // |insert_position|, |insert_position| + 1, ..., kLength - 1}. + size_t pos = 0; + for (int i = 0; i < insert_position; ++i) { + EXPECT_EQ(array_[i], vec[pos]); + ++pos; + } + for (int i = 0; i < kNewLength; ++i) { + EXPECT_EQ(new_array[i], vec[pos]); + ++pos; + } + for (size_t i = insert_position; i < array_length(); ++i) { + EXPECT_EQ(array_[i], vec[pos]); + ++pos; + } +} + +// Test the InsertZerosAt method with an insert position in the middle of the +// vector. Use the InsertAt method as reference. +TEST_F(AudioVectorTest, InsertZerosAt) { + AudioVector vec; + AudioVector vec_ref; + vec.PushBack(array_, array_length()); + vec_ref.PushBack(array_, array_length()); + static const int kNewLength = 5; + int insert_position = 5; + vec.InsertZerosAt(kNewLength, insert_position); + int16_t new_array[kNewLength] = {0}; // All zero elements. + vec_ref.InsertAt(new_array, kNewLength, insert_position); + // Verify that the vectors are identical. + ASSERT_EQ(vec_ref.Size(), vec.Size()); + for (size_t i = 0; i < vec.Size(); ++i) { + EXPECT_EQ(vec_ref[i], vec[i]); + } +} + +// Test the InsertAt method with an insert position at the start of the vector. +TEST_F(AudioVectorTest, InsertAtBeginning) { + AudioVector vec; + vec.PushBack(array_, array_length()); + static const int kNewLength = 5; + int16_t new_array[kNewLength]; + // Set array elements to {100, 101, 102, ... }. + for (int i = 0; i < kNewLength; ++i) { + new_array[i] = 100 + i; + } + int insert_position = 0; + vec.InsertAt(new_array, kNewLength, insert_position); + // Verify that the vector looks as follows: + // {100, 101, ..., 100 + kNewLength - 1, + // 0, 1, ..., kLength - 1}. + size_t pos = 0; + for (int i = 0; i < kNewLength; ++i) { + EXPECT_EQ(new_array[i], vec[pos]); + ++pos; + } + for (size_t i = insert_position; i < array_length(); ++i) { + EXPECT_EQ(array_[i], vec[pos]); + ++pos; + } +} + +// Test the InsertAt method with an insert position at the end of the vector. +TEST_F(AudioVectorTest, InsertAtEnd) { + AudioVector vec; + vec.PushBack(array_, array_length()); + static const int kNewLength = 5; + int16_t new_array[kNewLength]; + // Set array elements to {100, 101, 102, ... }. + for (int i = 0; i < kNewLength; ++i) { + new_array[i] = 100 + i; + } + int insert_position = array_length(); + vec.InsertAt(new_array, kNewLength, insert_position); + // Verify that the vector looks as follows: + // {0, 1, ..., kLength - 1, 100, 101, ..., 100 + kNewLength - 1 }. + size_t pos = 0; + for (size_t i = 0; i < array_length(); ++i) { + EXPECT_EQ(array_[i], vec[pos]); + ++pos; + } + for (int i = 0; i < kNewLength; ++i) { + EXPECT_EQ(new_array[i], vec[pos]); + ++pos; + } +} + +// Test the InsertAt method with an insert position beyond the end of the +// vector. Verify that a position beyond the end of the vector does not lead to +// an error. The expected outcome is the same as if the vector end was used as +// input position. That is, the input position should be capped at the maximum +// allowed value. +TEST_F(AudioVectorTest, InsertBeyondEnd) { + AudioVector vec; + vec.PushBack(array_, array_length()); + static const int kNewLength = 5; + int16_t new_array[kNewLength]; + // Set array elements to {100, 101, 102, ... }. + for (int i = 0; i < kNewLength; ++i) { + new_array[i] = 100 + i; + } + int insert_position = array_length() + 10; // Too large. + vec.InsertAt(new_array, kNewLength, insert_position); + // Verify that the vector looks as follows: + // {0, 1, ..., kLength - 1, 100, 101, ..., 100 + kNewLength - 1 }. + size_t pos = 0; + for (size_t i = 0; i < array_length(); ++i) { + EXPECT_EQ(array_[i], vec[pos]); + ++pos; + } + for (int i = 0; i < kNewLength; ++i) { + EXPECT_EQ(new_array[i], vec[pos]); + ++pos; + } +} + +// Test the OverwriteAt method with a position such that all of the new values +// fit within the old vector. +TEST_F(AudioVectorTest, OverwriteAt) { + AudioVector vec; + vec.PushBack(array_, array_length()); + static const int kNewLength = 5; + int16_t new_array[kNewLength]; + // Set array elements to {100, 101, 102, ... }. + for (int i = 0; i < kNewLength; ++i) { + new_array[i] = 100 + i; + } + size_t insert_position = 2; + vec.OverwriteAt(new_array, kNewLength, insert_position); + // Verify that the vector looks as follows: + // {0, ..., |insert_position| - 1, 100, 101, ..., 100 + kNewLength - 1, + // |insert_position|, |insert_position| + 1, ..., kLength - 1}. + size_t pos = 0; + for (pos = 0; pos < insert_position; ++pos) { + EXPECT_EQ(array_[pos], vec[pos]); + } + for (int i = 0; i < kNewLength; ++i) { + EXPECT_EQ(new_array[i], vec[pos]); + ++pos; + } + for (; pos < array_length(); ++pos) { + EXPECT_EQ(array_[pos], vec[pos]); + } +} + +// Test the OverwriteAt method with a position such that some of the new values +// extend beyond the end of the current vector. This is valid, and the vector is +// expected to expand to accommodate the new values. +TEST_F(AudioVectorTest, OverwriteBeyondEnd) { + AudioVector vec; + vec.PushBack(array_, array_length()); + static const int kNewLength = 5; + int16_t new_array[kNewLength]; + // Set array elements to {100, 101, 102, ... }. + for (int i = 0; i < kNewLength; ++i) { + new_array[i] = 100 + i; + } + int insert_position = array_length() - 2; + vec.OverwriteAt(new_array, kNewLength, insert_position); + ASSERT_EQ(array_length() - 2u + kNewLength, vec.Size()); + // Verify that the vector looks as follows: + // {0, ..., |insert_position| - 1, 100, 101, ..., 100 + kNewLength - 1, + // |insert_position|, |insert_position| + 1, ..., kLength - 1}. + int pos = 0; + for (pos = 0; pos < insert_position; ++pos) { + EXPECT_EQ(array_[pos], vec[pos]); + } + for (int i = 0; i < kNewLength; ++i) { + EXPECT_EQ(new_array[i], vec[pos]); + ++pos; + } + // Verify that we checked to the end of |vec|. + EXPECT_EQ(vec.Size(), static_cast(pos)); +} + +TEST_F(AudioVectorTest, CrossFade) { + static const size_t kLength = 100; + static const size_t kFadeLength = 10; + AudioVector vec1(kLength); + AudioVector vec2(kLength); + // Set all vector elements to 0 in |vec1| and 100 in |vec2|. + for (size_t i = 0; i < kLength; ++i) { + vec1[i] = 0; + vec2[i] = 100; + } + vec1.CrossFade(vec2, kFadeLength); + ASSERT_EQ(2 * kLength - kFadeLength, vec1.Size()); + // First part untouched. + for (size_t i = 0; i < kLength - kFadeLength; ++i) { + EXPECT_EQ(0, vec1[i]); + } + // Check mixing zone. + for (size_t i = 0 ; i < kFadeLength; ++i) { + EXPECT_NEAR((i + 1) * 100 / (kFadeLength + 1), + vec1[kLength - kFadeLength + i], 1); + } + // Second part untouched. + for (size_t i = kLength; i < vec1.Size(); ++i) { + EXPECT_EQ(100, vec1[i]); + } +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/automode.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/automode.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/automode.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/automode.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,783 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the implementation of automatic buffer level optimization. - */ - -#include "automode.h" - -#include - -#include "signal_processing_library.h" - -#include "neteq_defines.h" - -#ifdef NETEQ_DELAY_LOGGING -/* special code for offline delay logging */ -#include -#include "delay_logging.h" - -extern FILE *delay_fid2; /* file pointer to delay log file */ -#endif /* NETEQ_DELAY_LOGGING */ - -// These two functions are copied from module_common_types.h, but adapted for C. -int WebRtcNetEQ_IsNewerSequenceNumber(uint16_t sequence_number, - uint16_t prev_sequence_number) { - return sequence_number != prev_sequence_number && - ((uint16_t) (sequence_number - prev_sequence_number)) < 0x8000; -} - -int WebRtcNetEQ_IsNewerTimestamp(uint32_t timestamp, uint32_t prev_timestamp) { - return timestamp != prev_timestamp && - ((uint32_t) (timestamp - prev_timestamp)) < 0x80000000; -} - -int WebRtcNetEQ_UpdateIatStatistics(AutomodeInst_t *inst, int maxBufLen, - uint16_t seqNumber, uint32_t timeStamp, - int32_t fsHz, int mdCodec, int streamingMode) -{ - uint32_t timeIat; /* inter-arrival time */ - int i; - int32_t tempsum = 0; /* temp summation */ - int32_t tempvar; /* temporary variable */ - int retval = 0; /* return value */ - int16_t packetLenSamp; /* packet speech length in samples */ - - /****************/ - /* Sanity check */ - /****************/ - - if (maxBufLen <= 1 || fsHz <= 0) - { - /* maxBufLen must be at least 2 and fsHz must both be strictly positive */ - return -1; - } - - /****************************/ - /* Update packet statistics */ - /****************************/ - - /* Try calculating packet length from current and previous timestamps */ - if (!WebRtcNetEQ_IsNewerTimestamp(timeStamp, inst->lastTimeStamp) || - !WebRtcNetEQ_IsNewerSequenceNumber(seqNumber, inst->lastSeqNo)) - { - /* Wrong timestamp or sequence order; revert to backup plan */ - packetLenSamp = inst->packetSpeechLenSamp; /* use stored value */ - } - else - { - /* calculate timestamps per packet */ - packetLenSamp = (int16_t) WebRtcSpl_DivU32U16(timeStamp - inst->lastTimeStamp, - seqNumber - inst->lastSeqNo); - } - - /* Check that the packet size is positive; if not, the statistics cannot be updated. */ - if (inst->firstPacketReceived && packetLenSamp > 0) - { /* packet size ok */ - - /* calculate inter-arrival time in integer packets (rounding down) */ - timeIat = WebRtcSpl_DivW32W16(inst->packetIatCountSamp, packetLenSamp); - - /* Special operations for streaming mode */ - if (streamingMode != 0) - { - /* - * Calculate IAT in Q8, including fractions of a packet (i.e., more accurate - * than timeIat). - */ - int16_t timeIatQ8 = (int16_t) WebRtcSpl_DivW32W16( - WEBRTC_SPL_LSHIFT_W32(inst->packetIatCountSamp, 8), packetLenSamp); - - /* - * Calculate cumulative sum iat with sequence number compensation (ideal arrival - * times makes this sum zero). - */ - inst->cSumIatQ8 += (timeIatQ8 - - WEBRTC_SPL_LSHIFT_W32(seqNumber - inst->lastSeqNo, 8)); - - /* subtract drift term */ - inst->cSumIatQ8 -= CSUM_IAT_DRIFT; - - /* ensure not negative */ - inst->cSumIatQ8 = WEBRTC_SPL_MAX(inst->cSumIatQ8, 0); - - /* remember max */ - if (inst->cSumIatQ8 > inst->maxCSumIatQ8) - { - inst->maxCSumIatQ8 = inst->cSumIatQ8; - inst->maxCSumUpdateTimer = 0; - } - - /* too long since the last maximum was observed; decrease max value */ - if (inst->maxCSumUpdateTimer > (uint32_t) WEBRTC_SPL_MUL_32_16(fsHz, - MAX_STREAMING_PEAK_PERIOD)) - { - inst->maxCSumIatQ8 -= 4; /* remove 1000*4/256 = 15.6 ms/s */ - } - } /* end of streaming mode */ - - /* check for discontinuous packet sequence and re-ordering */ - if (WebRtcNetEQ_IsNewerSequenceNumber(seqNumber, inst->lastSeqNo + 1)) - { - /* Compensate for gap in the sequence numbers. - * Reduce IAT with expected extra time due to lost packets, but ensure that - * the IAT is not negative. - */ - timeIat -= WEBRTC_SPL_MIN(timeIat, - (uint16_t) (seqNumber - (uint16_t) (inst->lastSeqNo + 1))); - } - else if (!WebRtcNetEQ_IsNewerSequenceNumber(seqNumber, inst->lastSeqNo)) - { - /* compensate for re-ordering */ - timeIat += (uint16_t) (inst->lastSeqNo + 1 - seqNumber); - } - - /* saturate IAT at maximum value */ - timeIat = WEBRTC_SPL_MIN( timeIat, MAX_IAT ); - - /* update iatProb = forgetting_factor * iatProb for all elements */ - for (i = 0; i <= MAX_IAT; i++) - { - int32_t tempHi, tempLo; /* Temporary variables */ - - /* - * Multiply iatProbFact (Q15) with iatProb (Q30) and right-shift 15 steps - * to come back to Q30. The operation is done in two steps: - */ - - /* - * 1) Multiply the high 16 bits (15 bits + sign) of iatProb. Shift iatProb - * 16 steps right to get the high 16 bits in a int16_t prior to - * multiplication, and left-shift with 1 afterwards to come back to - * Q30 = (Q15 * (Q30>>16)) << 1. - */ - tempHi = WEBRTC_SPL_MUL_16_16(inst->iatProbFact, - (int16_t) WEBRTC_SPL_RSHIFT_W32(inst->iatProb[i], 16)); - tempHi = WEBRTC_SPL_LSHIFT_W32(tempHi, 1); /* left-shift 1 step */ - - /* - * 2) Isolate and multiply the low 16 bits of iatProb. Right-shift 15 steps - * afterwards to come back to Q30 = (Q15 * Q30) >> 15. - */ - tempLo = inst->iatProb[i] & 0x0000FFFF; /* sift out the 16 low bits */ - tempLo = WEBRTC_SPL_MUL_16_U16(inst->iatProbFact, - (uint16_t) tempLo); - tempLo = WEBRTC_SPL_RSHIFT_W32(tempLo, 15); - - /* Finally, add the high and low parts */ - inst->iatProb[i] = tempHi + tempLo; - - /* Sum all vector elements while we are at it... */ - tempsum += inst->iatProb[i]; - } - - /* - * Increase the probability for the currently observed inter-arrival time - * with 1 - iatProbFact. The factor is in Q15, iatProb in Q30; - * hence, left-shift 15 steps to obtain result in Q30. - */ - inst->iatProb[timeIat] += (32768 - inst->iatProbFact) << 15; - - tempsum += (32768 - inst->iatProbFact) << 15; /* add to vector sum */ - - /* - * Update iatProbFact (changes only during the first seconds after reset) - * The factor converges to IAT_PROB_FACT. - */ - inst->iatProbFact += (IAT_PROB_FACT - inst->iatProbFact + 3) >> 2; - - /* iatProb should sum up to 1 (in Q30). */ - tempsum -= 1 << 30; /* should be zero */ - - /* Check if it does, correct if it doesn't. */ - if (tempsum > 0) - { - /* tempsum too large => decrease a few values in the beginning */ - i = 0; - while (i <= MAX_IAT && tempsum > 0) - { - /* Remove iatProb[i] / 16 from iatProb, but not more than tempsum */ - tempvar = WEBRTC_SPL_MIN(tempsum, inst->iatProb[i] >> 4); - inst->iatProb[i++] -= tempvar; - tempsum -= tempvar; - } - } - else if (tempsum < 0) - { - /* tempsum too small => increase a few values in the beginning */ - i = 0; - while (i <= MAX_IAT && tempsum < 0) - { - /* Add iatProb[i] / 16 to iatProb, but not more than tempsum */ - tempvar = WEBRTC_SPL_MIN(-tempsum, inst->iatProb[i] >> 4); - inst->iatProb[i++] += tempvar; - tempsum += tempvar; - } - } - - /* Calculate optimal buffer level based on updated statistics */ - tempvar = (int32_t) WebRtcNetEQ_CalcOptimalBufLvl(inst, fsHz, mdCodec, timeIat, - streamingMode); - if (tempvar > 0) - { - int high_lim_delay; - /* Convert the minimum delay from milliseconds to packets in Q8. - * |fsHz| is sampling rate in Hertz, and |packetLenSamp| - * is the number of samples per packet (according to the last - * decoding). - */ - int32_t minimum_delay_q8 = ((inst->minimum_delay_ms * - (fsHz / 1000)) << 8) / packetLenSamp; - - int32_t maximum_delay_q8 = ((inst->maximum_delay_ms * - (fsHz / 1000)) << 8) / packetLenSamp; - - inst->optBufLevel = tempvar; - - if (streamingMode != 0) - { - inst->optBufLevel = WEBRTC_SPL_MAX(inst->optBufLevel, - inst->maxCSumIatQ8); - } - - /* The required delay. */ - inst->required_delay_q8 = inst->optBufLevel; - - // Maintain the target delay. - inst->optBufLevel = WEBRTC_SPL_MAX(inst->optBufLevel, - minimum_delay_q8); - - if (maximum_delay_q8 > 0) { - // Make sure that max is at least one packet length. - maximum_delay_q8 = WEBRTC_SPL_MAX(maximum_delay_q8, (1 << 8)); - inst->optBufLevel = WEBRTC_SPL_MIN(inst->optBufLevel, - maximum_delay_q8); - } - /*********/ - /* Limit */ - /*********/ - - /* Subtract extra delay from maxBufLen */ - if (inst->extraDelayMs > 0 && inst->packetSpeechLenSamp > 0) - { - maxBufLen -= inst->extraDelayMs / inst->packetSpeechLenSamp * fsHz / 1000; - maxBufLen = WEBRTC_SPL_MAX(maxBufLen, 1); // sanity: at least one packet - } - - maxBufLen = WEBRTC_SPL_LSHIFT_W32(maxBufLen, 8); /* shift to Q8 */ - - /* Enforce upper limit; 75% of maxBufLen */ - /* 1/2 + 1/4 = 75% */ - high_lim_delay = (maxBufLen >> 1) + (maxBufLen >> 2); - inst->optBufLevel = WEBRTC_SPL_MIN(inst->optBufLevel, - high_lim_delay); - inst->required_delay_q8 = WEBRTC_SPL_MIN(inst->required_delay_q8, - high_lim_delay); - } - else - { - retval = (int) tempvar; - } - - } /* end if */ - - /*******************************/ - /* Update post-call statistics */ - /*******************************/ - - /* Calculate inter-arrival time in ms = packetIatCountSamp / (fsHz / 1000) */ - timeIat = WEBRTC_SPL_UDIV( - WEBRTC_SPL_UMUL_32_16(inst->packetIatCountSamp, (int16_t) 1000), - (uint32_t) fsHz); - - /* Increase counter corresponding to current inter-arrival time */ - if (timeIat > 2000) - { - inst->countIAT2000ms++; - } - else if (timeIat > 1000) - { - inst->countIAT1000ms++; - } - else if (timeIat > 500) - { - inst->countIAT500ms++; - } - - if (timeIat > inst->longestIATms) - { - /* update maximum value */ - inst->longestIATms = timeIat; - } - - /***********************************/ - /* Prepare for next packet arrival */ - /***********************************/ - - inst->packetIatCountSamp = 0; /* reset inter-arrival time counter */ - - inst->lastSeqNo = seqNumber; /* remember current sequence number */ - - inst->lastTimeStamp = timeStamp; /* remember current timestamp */ - - inst->firstPacketReceived = 1; - - return retval; -} - - -int16_t WebRtcNetEQ_CalcOptimalBufLvl(AutomodeInst_t *inst, int32_t fsHz, - int mdCodec, uint32_t timeIatPkts, - int streamingMode) -{ - - int32_t sum1 = 1 << 30; /* assign to 1 in Q30 */ - int16_t B; - uint16_t Bopt; - int i; - int32_t betaInv; /* optimization parameter */ - -#ifdef NETEQ_DELAY_LOGGING - /* special code for offline delay logging */ - int temp_var; -#endif - - /****************/ - /* Sanity check */ - /****************/ - - if (fsHz <= 0) - { - /* fsHz must be strictly positive */ - return -1; - } - - /***********************************************/ - /* Get betaInv parameter based on playout mode */ - /***********************************************/ - - if (streamingMode) - { - /* streaming (listen-only) mode */ - betaInv = AUTOMODE_STREAMING_BETA_INV_Q30; - } - else - { - /* normal mode */ - betaInv = AUTOMODE_BETA_INV_Q30; - } - - /*******************************************************************/ - /* Calculate optimal buffer level without considering jitter peaks */ - /*******************************************************************/ - - /* - * Find the B for which the probability of observing an inter-arrival time larger - * than or equal to B is less than or equal to betaInv. - */ - B = 0; /* start from the beginning of iatProb */ - sum1 -= inst->iatProb[B]; /* ensure that optimal level is not less than 1 */ - - do - { - /* - * Subtract the probabilities one by one until the sum is no longer greater - * than betaInv. - */ - sum1 -= inst->iatProb[++B]; - } - while ((sum1 > betaInv) && (B < MAX_IAT)); - - Bopt = B; /* This is our primary value for the optimal buffer level Bopt */ - - if (mdCodec) - { - /* - * Use alternative cost function when multiple description codec is in use. - * Do not have to re-calculate all points, just back off a few steps from - * previous value of B. - */ - int32_t sum2 = sum1; /* copy sum1 */ - - while ((sum2 <= betaInv + inst->iatProb[Bopt]) && (Bopt > 0)) - { - /* Go backwards in the sum until the modified cost function solution is found */ - sum2 += inst->iatProb[Bopt--]; - } - - Bopt++; /* This is the optimal level when using an MD codec */ - - /* Now, Bopt and B can have different values. */ - } - -#ifdef NETEQ_DELAY_LOGGING - /* special code for offline delay logging */ - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_OPTBUF; - if (fwrite( &temp_var, sizeof(int), 1, delay_fid2 ) != 1) { - return -1; - } - temp_var = (int) (Bopt * inst->packetSpeechLenSamp); -#endif - - /******************************************************************/ - /* Make levelFiltFact adaptive: Larger B <=> larger levelFiltFact */ - /******************************************************************/ - - switch (B) - { - case 0: - case 1: - { - inst->levelFiltFact = 251; - break; - } - case 2: - case 3: - { - inst->levelFiltFact = 252; - break; - } - case 4: - case 5: - case 6: - case 7: - { - inst->levelFiltFact = 253; - break; - } - default: /* B > 7 */ - { - inst->levelFiltFact = 254; - break; - } - } - - /************************/ - /* Peak mode operations */ - /************************/ - - /* Compare current IAT with peak threshold - * - * If IAT > optimal level + threshold (+1 for MD codecs) - * or if IAT > 2 * optimal level (note: optimal level is in Q8): - */ - if (timeIatPkts > (uint32_t) (Bopt + inst->peakThresholdPkt + (mdCodec != 0)) - || timeIatPkts > (uint32_t) WEBRTC_SPL_LSHIFT_U16(Bopt, 1)) - { - /* A peak is observed */ - - if (inst->peakIndex == -1) - { - /* this is the first peak; prepare for next peak */ - inst->peakIndex = 0; - /* set the mode-disable counter */ - inst->peakModeDisabled = WEBRTC_SPL_LSHIFT_W16(1, NUM_PEAKS_REQUIRED-2); - } - else if (inst->peakIatCountSamp - <= - (uint32_t) WEBRTC_SPL_MUL_32_16(fsHz, MAX_PEAK_PERIOD)) - { - /* This is not the first peak and the period time is valid */ - - /* store time elapsed since last peak */ - inst->peakPeriodSamp[inst->peakIndex] = inst->peakIatCountSamp; - - /* saturate height to 16 bits */ - inst->peakHeightPkt[inst->peakIndex] - = - (int16_t) WEBRTC_SPL_MIN(timeIatPkts, WEBRTC_SPL_WORD16_MAX); - - /* increment peakIndex and wrap/modulo */ - inst->peakIndex = (inst->peakIndex + 1) & PEAK_INDEX_MASK; - - /* process peak vectors */ - inst->curPeakHeight = 0; - inst->curPeakPeriod = 0; - - for (i = 0; i < NUM_PEAKS; i++) - { - /* Find maximum of peak heights and peak periods */ - inst->curPeakHeight - = WEBRTC_SPL_MAX(inst->curPeakHeight, inst->peakHeightPkt[i]); - inst->curPeakPeriod - = WEBRTC_SPL_MAX(inst->curPeakPeriod, inst->peakPeriodSamp[i]); - - } - - inst->peakModeDisabled >>= 1; /* decrease mode-disable "counter" */ - - } - else if (inst->peakIatCountSamp > (uint32_t) WEBRTC_SPL_MUL_32_16(fsHz, - WEBRTC_SPL_LSHIFT_W16(MAX_PEAK_PERIOD, 1))) - { - /* - * More than 2 * MAX_PEAK_PERIOD has elapsed since last peak; - * too long time => reset peak statistics - */ - inst->curPeakHeight = 0; - inst->curPeakPeriod = 0; - for (i = 0; i < NUM_PEAKS; i++) - { - inst->peakHeightPkt[i] = 0; - inst->peakPeriodSamp[i] = 0; - } - - inst->peakIndex = -1; /* Next peak is first peak */ - inst->peakIatCountSamp = 0; - } - - inst->peakIatCountSamp = 0; /* Reset peak interval timer */ - } /* end if peak is observed */ - - /* Evaluate peak mode conditions */ - - /* - * If not disabled (enough peaks have been observed) and - * time since last peak is less than two peak periods. - */ - inst->peakFound = 0; - if ((!inst->peakModeDisabled) && (inst->peakIatCountSamp - <= WEBRTC_SPL_LSHIFT_W32(inst->curPeakPeriod , 1))) - { - /* Engage peak mode */ - inst->peakFound = 1; - /* Set optimal buffer level to curPeakHeight (if it's not already larger) */ - Bopt = WEBRTC_SPL_MAX(Bopt, inst->curPeakHeight); - -#ifdef NETEQ_DELAY_LOGGING - /* special code for offline delay logging */ - temp_var = (int) -(Bopt * inst->packetSpeechLenSamp); -#endif - } - - /* Scale Bopt to Q8 */ - Bopt = WEBRTC_SPL_LSHIFT_U16(Bopt,8); - -#ifdef NETEQ_DELAY_LOGGING - /* special code for offline delay logging */ - if (fwrite( &temp_var, sizeof(int), 1, delay_fid2 ) != 1) { - return -1; - } -#endif - - /* Sanity check: Bopt must be strictly positive */ - if (Bopt <= 0) - { - Bopt = WEBRTC_SPL_LSHIFT_W16(1, 8); /* 1 in Q8 */ - } - - return Bopt; /* return value in Q8 */ -} - - -int WebRtcNetEQ_BufferLevelFilter(int32_t curSizeMs8, AutomodeInst_t *inst, - int sampPerCall, int16_t fsMult) -{ - - int16_t curSizeFrames; - - /****************/ - /* Sanity check */ - /****************/ - - if (sampPerCall <= 0 || fsMult <= 0) - { - /* sampPerCall and fsMult must both be strictly positive */ - return -1; - } - - /* Check if packet size has been detected */ - if (inst->packetSpeechLenSamp > 0) - { - /* - * Current buffer level in packet lengths - * = (curSizeMs8 * fsMult) / packetSpeechLenSamp - */ - curSizeFrames = (int16_t) WebRtcSpl_DivW32W16( - WEBRTC_SPL_MUL_32_16(curSizeMs8, fsMult), inst->packetSpeechLenSamp); - } - else - { - curSizeFrames = 0; - } - - /* Filter buffer level */ - if (inst->levelFiltFact > 0) /* check that filter factor is set */ - { - /* Filter: - * buffLevelFilt = levelFiltFact * buffLevelFilt - * + (1-levelFiltFact) * curSizeFrames - * - * levelFiltFact is in Q8 - */ - inst->buffLevelFilt = ((inst->levelFiltFact * inst->buffLevelFilt) >> 8) + - (256 - inst->levelFiltFact) * curSizeFrames; - } - - /* Account for time-scale operations (accelerate and pre-emptive expand) */ - if (inst->prevTimeScale) - { - /* - * Time-scaling has been performed since last filter update. - * Subtract the sampleMemory from buffLevelFilt after converting sampleMemory - * from samples to packets in Q8. Make sure that the filtered value is - * non-negative. - */ - inst->buffLevelFilt = WEBRTC_SPL_MAX( inst->buffLevelFilt - - WebRtcSpl_DivW32W16( - WEBRTC_SPL_LSHIFT_W32(inst->sampleMemory, 8), /* sampleMemory in Q8 */ - inst->packetSpeechLenSamp ), /* divide by packetSpeechLenSamp */ - 0); - - /* - * Reset flag and set timescaleHoldOff timer to prevent further time-scaling - * for some time. - */ - inst->prevTimeScale = 0; - inst->timescaleHoldOff = AUTOMODE_TIMESCALE_LIMIT; - } - - /* Update time counters and HoldOff timer */ - inst->packetIatCountSamp += sampPerCall; /* packet inter-arrival time */ - inst->peakIatCountSamp += sampPerCall; /* peak inter-arrival time */ - inst->timescaleHoldOff >>= 1; /* time-scaling limiter */ - inst->maxCSumUpdateTimer += sampPerCall; /* cumulative-sum timer */ - - return 0; - -} - - -int WebRtcNetEQ_SetPacketSpeechLen(AutomodeInst_t *inst, int16_t newLenSamp, - int32_t fsHz) -{ - - /* Sanity check for newLenSamp and fsHz */ - if (newLenSamp <= 0 || fsHz <= 0) - { - return -1; - } - - inst->packetSpeechLenSamp = newLenSamp; /* Store packet size in instance */ - - /* Make NetEQ wait for first regular packet before starting the timer */ - inst->lastPackCNGorDTMF = 1; - - inst->packetIatCountSamp = 0; /* Reset packet time counter */ - - /* - * Calculate peak threshold from packet size. The threshold is defined as - * the (fractional) number of packets that corresponds to PEAK_HEIGHT - * (in Q8 seconds). That is, threshold = PEAK_HEIGHT/256 * fsHz / packLen. - */ - inst->peakThresholdPkt = (uint16_t) WebRtcSpl_DivW32W16ResW16( - WEBRTC_SPL_MUL_16_16_RSFT(PEAK_HEIGHT, - (int16_t) WEBRTC_SPL_RSHIFT_W32(fsHz, 6), 2), inst->packetSpeechLenSamp); - - return 0; -} - - -int WebRtcNetEQ_ResetAutomode(AutomodeInst_t *inst, int maxBufLenPackets) -{ - - int i; - uint16_t tempprob = 0x4002; /* 16384 + 2 = 100000000000010 binary; */ - - /* Sanity check for maxBufLenPackets */ - if (maxBufLenPackets <= 1) - { - /* Invalid value; set to 10 instead (arbitary small number) */ - maxBufLenPackets = 10; - } - - /* Reset filtered buffer level */ - inst->buffLevelFilt = 0; - - /* Reset packet size to unknown */ - inst->packetSpeechLenSamp = 0; - - /* - * Flag that last packet was special payload, so that automode will treat the next speech - * payload as the first payload received. - */ - inst->lastPackCNGorDTMF = 1; - - /* Reset peak detection parameters */ - inst->peakModeDisabled = 1; /* disable peak mode */ - inst->peakIatCountSamp = 0; - inst->peakIndex = -1; /* indicates that no peak is registered */ - inst->curPeakHeight = 0; - inst->curPeakPeriod = 0; - for (i = 0; i < NUM_PEAKS; i++) - { - inst->peakHeightPkt[i] = 0; - inst->peakPeriodSamp[i] = 0; - } - - /* - * Set the iatProb PDF vector to an exponentially decaying distribution - * iatProb[i] = 0.5^(i+1), i = 0, 1, 2, ... - * iatProb is in Q30. - */ - for (i = 0; i <= MAX_IAT; i++) - { - /* iatProb[i] = 0.5^(i+1) = iatProb[i-1] / 2 */ - tempprob = WEBRTC_SPL_RSHIFT_U16(tempprob, 1); - /* store in PDF vector */ - inst->iatProb[i] = WEBRTC_SPL_LSHIFT_W32((int32_t) tempprob, 16); - } - - /* - * Calculate the optimal buffer level corresponding to the initial PDF. - * No need to call WebRtcNetEQ_CalcOptimalBufLvl() since we have just hard-coded - * all the variables that the buffer level depends on => we know the result - */ - inst->optBufLevel = WEBRTC_SPL_MIN(4, - (maxBufLenPackets >> 1) + (maxBufLenPackets >> 1)); /* 75% of maxBufLenPackets */ - inst->required_delay_q8 = inst->optBufLevel; - inst->levelFiltFact = 253; - - /* - * Reset the iat update forgetting factor to 0 to make the impact of the first - * incoming packets greater. - */ - inst->iatProbFact = 0; - - /* Reset packet inter-arrival time counter */ - inst->packetIatCountSamp = 0; - - /* Clear time-scaling related variables */ - inst->prevTimeScale = 0; - inst->timescaleHoldOff = AUTOMODE_TIMESCALE_LIMIT; /* don't allow time-scaling immediately */ - - inst->cSumIatQ8 = 0; - inst->maxCSumIatQ8 = 0; - - return 0; -} - -int32_t WebRtcNetEQ_AverageIAT(const AutomodeInst_t *inst) { - int i; - int32_t sum_q24 = 0; - assert(inst); - for (i = 0; i <= MAX_IAT; ++i) { - /* Shift 6 to fit worst case: 2^30 * 64. */ - sum_q24 += (inst->iatProb[i] >> 6) * i; - } - /* Subtract the nominal inter-arrival time 1 = 2^24 in Q24. */ - sum_q24 -= (1 << 24); - /* - * Multiply with 1000000 / 2^24 = 15625 / 2^18 to get in parts-per-million. - * Shift 7 to Q17 first, then multiply with 15625 and shift another 11. - */ - return ((sum_q24 >> 7) * 15625) >> 11; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/automode.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/automode.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/automode.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/automode.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,274 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the functionality for automatic buffer level optimization. - */ - -#ifndef AUTOMODE_H -#define AUTOMODE_H - -#include "typedefs.h" - -/*************/ -/* Constants */ -/*************/ - -/* The beta parameter defines the trade-off between delay and underrun probability. */ -/* It is defined through its inverse in Q30 */ -#define AUTOMODE_BETA_INV_Q30 53687091 /* 1/20 in Q30 */ -#define AUTOMODE_STREAMING_BETA_INV_Q30 536871 /* 1/2000 in Q30 */ - -/* Forgetting factor for the inter-arrival time statistics */ -#define IAT_PROB_FACT 32745 /* 0.9993 in Q15 */ - -/* Maximum inter-arrival time to register (in "packet-times") */ -#define MAX_IAT 64 -#define PEAK_HEIGHT 20 /* 0.08s in Q8 */ - -/* The value (1<<5) sets maximum accelerate "speed" to about 100 ms/s */ -#define AUTOMODE_TIMESCALE_LIMIT (1<<5) - -/* Peak mode related parameters */ -/* Number of peaks in peak vector; must be a power of 2 */ -#define NUM_PEAKS 8 - -/* Must be NUM_PEAKS-1 */ -#define PEAK_INDEX_MASK 0x0007 - -/* Longest accepted peak distance */ -#define MAX_PEAK_PERIOD 10 -#define MAX_STREAMING_PEAK_PERIOD 600 /* 10 minutes */ - -/* Number of peaks required before peak mode can be engaged */ -#define NUM_PEAKS_REQUIRED 3 - -/* Drift term for cumulative sum */ -#define CSUM_IAT_DRIFT 2 - -/*******************/ -/* Automode struct */ -/*******************/ - -/* The automode struct is a sub-struct of the - bufstats-struct (BufstatsInst_t). */ - -typedef struct -{ - - /* Filtered current buffer level */ - uint16_t levelFiltFact; /* filter forgetting factor in Q8 */ - int buffLevelFilt; /* filtered buffer level in Q8 */ - - /* Inter-arrival time (iat) statistics */ - int32_t iatProb[MAX_IAT + 1]; /* iat probabilities in Q30 */ - int16_t iatProbFact; /* iat forgetting factor in Q15 */ - uint32_t packetIatCountSamp; /* time (in timestamps) elapsed since last - packet arrival, based on RecOut calls */ - int optBufLevel; /* current optimal buffer level in Q8 */ - - /* Packet related information */ - int16_t packetSpeechLenSamp; /* speech samples per incoming packet */ - int16_t lastPackCNGorDTMF; /* indicates that the last received packet - contained special information */ - uint16_t lastSeqNo; /* sequence number for last packet received */ - uint32_t lastTimeStamp; /* timestamp for the last packet received */ - int firstPacketReceived; /* set to zero implicitly when the instance is - filled with zeros */ - int32_t sampleMemory; /* memory position for keeping track of how many - samples we cut during expand */ - int16_t prevTimeScale; /* indicates that the last mode was an accelerate - or pre-emptive expand operation */ - uint32_t timescaleHoldOff; /* counter that is shifted one step right each - RecOut call; time-scaling allowed when it has - reached 0 */ - int16_t extraDelayMs; /* extra delay for sync with video */ - - int minimum_delay_ms; /* Desired delay, NetEq maintains this amount of - delay unless jitter statistics suggests a higher value. */ - int maximum_delay_ms; /* Max desired delay, NetEq will not go above this - amount of delay even if jitter statistics suggests a higher value. */ - - int required_delay_q8; /* Smallest delay required. This is computed - according to inter-arrival time and playout mode. It has the same unit - as |optBufLevel|. */ - - /* Peak-detection */ - /* vector with the latest peak periods (peak spacing in samples) */ - uint32_t peakPeriodSamp[NUM_PEAKS]; - /* vector with the latest peak heights (in packets) */ - int16_t peakHeightPkt[NUM_PEAKS]; - int16_t peakIndex; /* index for the vectors peakPeriodSamp and peakHeightPkt; - -1 if still waiting for first peak */ - uint16_t peakThresholdPkt; /* definition of peak (in packets); - calculated from PEAK_HEIGHT */ - uint32_t peakIatCountSamp; /* samples elapsed since last peak was observed */ - uint32_t curPeakPeriod; /* current maximum of peakPeriodSamp vector */ - int16_t curPeakHeight; /* derived from peakHeightPkt vector; - used as optimal buffer level in peak mode */ - int16_t peakModeDisabled; /* ==0 if peak mode can be engaged; >0 if not */ - uint16_t peakFound; /* 1 if peaks are detected and extra delay is applied; - * 0 otherwise. */ - - /* Post-call statistics */ - uint32_t countIAT500ms; /* number of times we got small network outage */ - uint32_t countIAT1000ms; /* number of times we got medium network outage */ - uint32_t countIAT2000ms; /* number of times we got large network outage */ - uint32_t longestIATms; /* mSec duration of longest network outage */ - - int16_t cSumIatQ8; /* cumulative sum of inter-arrival times */ - int16_t maxCSumIatQ8; /* max cumulative sum IAT */ - uint32_t maxCSumUpdateTimer;/* time elapsed since maximum was observed */ -} AutomodeInst_t; - -/*************/ -/* Functions */ -/*************/ - -/**************************************************************************** - * WebRtcNetEQ_UpdateIatStatistics(...) - * - * Update the packet inter-arrival time statistics when a new packet arrives. - * This function should be called for every arriving packet, with some - * exceptions when using DTX/VAD and DTMF. A new optimal buffer level is - * calculated after the update. - * - * Input: - * - inst : Automode instance - * - maxBufLen : Maximum number of packets the buffer can hold - * - seqNumber : RTP sequence number of incoming packet - * - timeStamp : RTP timestamp of incoming packet - * - fsHz : Sample rate in Hz - * - mdCodec : Non-zero if the current codec is a multiple- - * description codec - * - streamingMode : A non-zero value will increase jitter robustness (and delay) - * - * Output: - * - inst : Updated automode instance - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_UpdateIatStatistics(AutomodeInst_t *inst, int maxBufLen, - uint16_t seqNumber, uint32_t timeStamp, - int32_t fsHz, int mdCodec, int streamingMode); - -/**************************************************************************** - * WebRtcNetEQ_CalcOptimalBufLvl(...) - * - * Calculate the optimal buffer level based on packet inter-arrival time - * statistics. - * - * Input: - * - inst : Automode instance - * - fsHz : Sample rate in Hz - * - mdCodec : Non-zero if the current codec is a multiple- - * description codec - * - timeIatPkts : Currently observed inter-arrival time in packets - * - streamingMode : A non-zero value will increase jitter robustness (and delay) - * - * Output: - * - inst : Updated automode instance - * - * Return value : >0 - Optimal buffer level - * <0 - Error - */ - -int16_t WebRtcNetEQ_CalcOptimalBufLvl(AutomodeInst_t *inst, int32_t fsHz, - int mdCodec, uint32_t timeIatPkts, - int streamingMode); - -/**************************************************************************** - * WebRtcNetEQ_BufferLevelFilter(...) - * - * Update filtered buffer level. The function must be called once for each - * RecOut call, since the timing of automode hinges on counters that are - * updated by this function. - * - * Input: - * - curSizeMs8 : Total length of unused speech data in packet buffer - * and sync buffer, in ms * 8 - * - inst : Automode instance - * - sampPerCall : Number of samples per RecOut call - * - fsMult : Sample rate in Hz divided by 8000 - * - * Output: - * - inst : Updated automode instance - * - * Return value : 0 - Ok - * : <0 - Error - */ - -int WebRtcNetEQ_BufferLevelFilter(int32_t curSizeMs8, AutomodeInst_t *inst, - int sampPerCall, int16_t fsMult); - -/**************************************************************************** - * WebRtcNetEQ_SetPacketSpeechLen(...) - * - * Provide the number of speech samples extracted from a packet to the - * automode instance. Several of the calculations within automode depend - * on knowing the packet size. - * - * - * Input: - * - inst : Automode instance - * - newLenSamp : Number of samples per RecOut call - * - fsHz : Sample rate in Hz - * - * Output: - * - inst : Updated automode instance - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_SetPacketSpeechLen(AutomodeInst_t *inst, int16_t newLenSamp, - int32_t fsHz); - -/**************************************************************************** - * WebRtcNetEQ_ResetAutomode(...) - * - * Reset the automode instance. - * - * - * Input: - * - inst : Automode instance - * - maxBufLenPackets : Maximum number of packets that the packet - * buffer can hold (>1) - * - * Output: - * - inst : Updated automode instance - * - * Return value : 0 - Ok - */ - -int WebRtcNetEQ_ResetAutomode(AutomodeInst_t *inst, int maxBufLenPackets); - -/**************************************************************************** - * WebRtcNetEQ_AverageIAT(...) - * - * Calculate the average inter-arrival time based on current statistics. - * The average is expressed in parts per million relative the nominal. That is, - * if the average inter-arrival time is equal to the nominal frame time, - * the return value is zero. A positive value corresponds to packet spacing - * being too large, while a negative value means that the packets arrive with - * less spacing than expected. - * - * - * Input: - * - inst : Automode instance. - * - * Return value : Average relative inter-arrival time in samples. - */ - -int32_t WebRtcNetEQ_AverageIAT(const AutomodeInst_t *inst); - -#endif /* AUTOMODE_H */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/background_noise.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/background_noise.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/background_noise.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/background_noise.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/background_noise.h" + +#include +#include // memcpy + +#include // min, max + +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" +#include "webrtc/modules/audio_coding/neteq/post_decode_vad.h" + +namespace webrtc { + +BackgroundNoise::BackgroundNoise(size_t num_channels) + : num_channels_(num_channels), + channel_parameters_(new ChannelParameters[num_channels_]), + mode_(NetEq::kBgnOn) { + Reset(); +} + +BackgroundNoise::~BackgroundNoise() {} + +void BackgroundNoise::Reset() { + initialized_ = false; + for (size_t channel = 0; channel < num_channels_; ++channel) { + channel_parameters_[channel].Reset(); + } + // Keep _bgnMode as it is. +} + +void BackgroundNoise::Update(const AudioMultiVector& input, + const PostDecodeVad& vad) { + if (vad.running() && vad.active_speech()) { + // Do not update the background noise parameters if we know that the signal + // is active speech. + return; + } + + int32_t auto_correlation[kMaxLpcOrder + 1]; + int16_t fiter_output[kMaxLpcOrder + kResidualLength]; + int16_t reflection_coefficients[kMaxLpcOrder]; + int16_t lpc_coefficients[kMaxLpcOrder + 1]; + + for (size_t channel_ix = 0; channel_ix < num_channels_; ++channel_ix) { + ChannelParameters& parameters = channel_parameters_[channel_ix]; + int16_t temp_signal_array[kVecLen + kMaxLpcOrder] = {0}; + int16_t* temp_signal = &temp_signal_array[kMaxLpcOrder]; + memcpy(temp_signal, + &input[channel_ix][input.Size() - kVecLen], + sizeof(int16_t) * kVecLen); + + int32_t sample_energy = CalculateAutoCorrelation(temp_signal, kVecLen, + auto_correlation); + + if ((!vad.running() && + sample_energy < parameters.energy_update_threshold) || + (vad.running() && !vad.active_speech())) { + // Generate LPC coefficients. + if (auto_correlation[0] > 0) { + // Regardless of whether the filter is actually updated or not, + // update energy threshold levels, since we have in fact observed + // a low energy signal. + if (sample_energy < parameters.energy_update_threshold) { + // Never go under 1.0 in average sample energy. + parameters.energy_update_threshold = std::max(sample_energy, 1); + parameters.low_energy_update_threshold = 0; + } + + // Only update BGN if filter is stable, i.e., if return value from + // Levinson-Durbin function is 1. + if (WebRtcSpl_LevinsonDurbin(auto_correlation, lpc_coefficients, + reflection_coefficients, + kMaxLpcOrder) != 1) { + return; + } + } else { + // Center value in auto-correlation is not positive. Do not update. + return; + } + + // Generate the CNG gain factor by looking at the energy of the residual. + WebRtcSpl_FilterMAFastQ12(temp_signal + kVecLen - kResidualLength, + fiter_output, lpc_coefficients, + kMaxLpcOrder + 1, kResidualLength); + int32_t residual_energy = WebRtcSpl_DotProductWithScale(fiter_output, + fiter_output, + kResidualLength, + 0); + + // Check spectral flatness. + // Comparing the residual variance with the input signal variance tells + // if the spectrum is flat or not. + // If 20 * residual_energy >= sample_energy << 6, the spectrum is flat + // enough. Also ensure that the energy is non-zero. + if ((residual_energy * 20 >= (sample_energy << 6)) && + (sample_energy > 0)) { + // Spectrum is flat enough; save filter parameters. + // |temp_signal| + |kVecLen| - |kMaxLpcOrder| points at the first of the + // |kMaxLpcOrder| samples in the residual signal, which will form the + // filter state for the next noise generation. + SaveParameters(channel_ix, lpc_coefficients, + temp_signal + kVecLen - kMaxLpcOrder, sample_energy, + residual_energy); + } + } else { + // Will only happen if post-decode VAD is disabled and |sample_energy| is + // not low enough. Increase the threshold for update so that it increases + // by a factor 4 in 4 seconds. + IncrementEnergyThreshold(channel_ix, sample_energy); + } + } + return; +} + +int32_t BackgroundNoise::Energy(size_t channel) const { + assert(channel < num_channels_); + return channel_parameters_[channel].energy; +} + +void BackgroundNoise::SetMuteFactor(size_t channel, int16_t value) { + assert(channel < num_channels_); + channel_parameters_[channel].mute_factor = value; +} + +int16_t BackgroundNoise::MuteFactor(size_t channel) const { + assert(channel < num_channels_); + return channel_parameters_[channel].mute_factor; +} + +const int16_t* BackgroundNoise::Filter(size_t channel) const { + assert(channel < num_channels_); + return channel_parameters_[channel].filter; +} + +const int16_t* BackgroundNoise::FilterState(size_t channel) const { + assert(channel < num_channels_); + return channel_parameters_[channel].filter_state; +} + +void BackgroundNoise::SetFilterState(size_t channel, const int16_t* input, + size_t length) { + assert(channel < num_channels_); + length = std::min(length, static_cast(kMaxLpcOrder)); + memcpy(channel_parameters_[channel].filter_state, input, + length * sizeof(int16_t)); +} + +int16_t BackgroundNoise::Scale(size_t channel) const { + assert(channel < num_channels_); + return channel_parameters_[channel].scale; +} +int16_t BackgroundNoise::ScaleShift(size_t channel) const { + assert(channel < num_channels_); + return channel_parameters_[channel].scale_shift; +} + +int32_t BackgroundNoise::CalculateAutoCorrelation( + const int16_t* signal, int length, int32_t* auto_correlation) const { + int16_t signal_max = WebRtcSpl_MaxAbsValueW16(signal, length); + int correlation_scale = kLogVecLen - + WebRtcSpl_NormW32(signal_max * signal_max); + correlation_scale = std::max(0, correlation_scale); + + static const int kCorrelationStep = -1; + WebRtcSpl_CrossCorrelation(auto_correlation, signal, signal, length, + kMaxLpcOrder + 1, correlation_scale, + kCorrelationStep); + + // Number of shifts to normalize energy to energy/sample. + int energy_sample_shift = kLogVecLen - correlation_scale; + return auto_correlation[0] >> energy_sample_shift; +} + +void BackgroundNoise::IncrementEnergyThreshold(size_t channel, + int32_t sample_energy) { + // TODO(hlundin): Simplify the below threshold update. What this code + // does is simply "threshold += (increment * threshold) >> 16", but due + // to the limited-width operations, it is not exactly the same. The + // difference should be inaudible, but bit-exactness would not be + // maintained. + assert(channel < num_channels_); + ChannelParameters& parameters = channel_parameters_[channel]; + int32_t temp_energy = + WEBRTC_SPL_MUL_16_16_RSFT(kThresholdIncrement, + parameters.low_energy_update_threshold, 16); + temp_energy += kThresholdIncrement * + (parameters.energy_update_threshold & 0xFF); + temp_energy += (kThresholdIncrement * + ((parameters.energy_update_threshold>>8) & 0xFF)) << 8; + parameters.low_energy_update_threshold += temp_energy; + + parameters.energy_update_threshold += kThresholdIncrement * + (parameters.energy_update_threshold>>16); + parameters.energy_update_threshold += + parameters.low_energy_update_threshold >> 16; + parameters.low_energy_update_threshold = + parameters.low_energy_update_threshold & 0x0FFFF; + + // Update maximum energy. + // Decrease by a factor 1/1024 each time. + parameters.max_energy = parameters.max_energy - + (parameters.max_energy >> 10); + if (sample_energy > parameters.max_energy) { + parameters.max_energy = sample_energy; + } + + // Set |energy_update_threshold| to no less than 60 dB lower than + // |max_energy_|. Adding 524288 assures proper rounding. + int32_t energy_update_threshold = (parameters.max_energy + 524288) >> 20; + if (energy_update_threshold > parameters.energy_update_threshold) { + parameters.energy_update_threshold = energy_update_threshold; + } +} + +void BackgroundNoise::SaveParameters(size_t channel, + const int16_t* lpc_coefficients, + const int16_t* filter_state, + int32_t sample_energy, + int32_t residual_energy) { + assert(channel < num_channels_); + ChannelParameters& parameters = channel_parameters_[channel]; + memcpy(parameters.filter, lpc_coefficients, + (kMaxLpcOrder+1) * sizeof(int16_t)); + memcpy(parameters.filter_state, filter_state, + kMaxLpcOrder * sizeof(int16_t)); + // Save energy level and update energy threshold levels. + // Never get under 1.0 in average sample energy. + parameters.energy = std::max(sample_energy, 1); + parameters.energy_update_threshold = parameters.energy; + parameters.low_energy_update_threshold = 0; + + // Normalize residual_energy to 29 or 30 bits before sqrt. + int norm_shift = WebRtcSpl_NormW32(residual_energy) - 1; + if (norm_shift & 0x1) { + norm_shift -= 1; // Even number of shifts required. + } + assert(norm_shift >= 0); // Should always be positive. + residual_energy = residual_energy << norm_shift; + + // Calculate scale and shift factor. + parameters.scale = WebRtcSpl_SqrtFloor(residual_energy); + // Add 13 to the |scale_shift_|, since the random numbers table is in + // Q13. + // TODO(hlundin): Move the "13" to where the |scale_shift_| is used? + parameters.scale_shift = 13 + ((kLogResidualLength + norm_shift) / 2); + + initialized_ = true; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/background_noise.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/background_noise.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/background_noise.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/background_noise.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_BACKGROUND_NOISE_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_BACKGROUND_NOISE_H_ + +#include // size_t + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Forward declarations. +class PostDecodeVad; + +// This class handles estimation of background noise parameters. +class BackgroundNoise { + public: + // TODO(hlundin): For 48 kHz support, increase kMaxLpcOrder to 10. + // Will work anyway, but probably sound a little worse. + static const int kMaxLpcOrder = 8; // 32000 / 8000 + 4. + + explicit BackgroundNoise(size_t num_channels); + virtual ~BackgroundNoise(); + + void Reset(); + + // Updates the parameter estimates based on the signal currently in the + // |sync_buffer|, and on the latest decision in |vad| if it is running. + void Update(const AudioMultiVector& sync_buffer, + const PostDecodeVad& vad); + + // Returns |energy_| for |channel|. + int32_t Energy(size_t channel) const; + + // Sets the value of |mute_factor_| for |channel| to |value|. + void SetMuteFactor(size_t channel, int16_t value); + + // Returns |mute_factor_| for |channel|. + int16_t MuteFactor(size_t channel) const; + + // Returns a pointer to |filter_| for |channel|. + const int16_t* Filter(size_t channel) const; + + // Returns a pointer to |filter_state_| for |channel|. + const int16_t* FilterState(size_t channel) const; + + // Copies |length| elements from |input| to the filter state. Will not copy + // more than |kMaxLpcOrder| elements. + void SetFilterState(size_t channel, const int16_t* input, size_t length); + + // Returns |scale_| for |channel|. + int16_t Scale(size_t channel) const; + + // Returns |scale_shift_| for |channel|. + int16_t ScaleShift(size_t channel) const; + + // Accessors. + bool initialized() const { return initialized_; } + NetEq::BackgroundNoiseMode mode() const { return mode_; } + + // Sets the mode of the background noise playout for cases when there is long + // duration of packet loss. + void set_mode(NetEq::BackgroundNoiseMode mode) { mode_ = mode; } + + private: + static const int kThresholdIncrement = 229; // 0.0035 in Q16. + static const int kVecLen = 256; + static const int kLogVecLen = 8; // log2(kVecLen). + static const int kResidualLength = 64; + static const int kLogResidualLength = 6; // log2(kResidualLength) + + struct ChannelParameters { + // Constructor. + ChannelParameters() { + Reset(); + } + + void Reset() { + energy = 2500; + max_energy = 0; + energy_update_threshold = 500000; + low_energy_update_threshold = 0; + memset(filter_state, 0, sizeof(filter_state)); + memset(filter, 0, sizeof(filter)); + filter[0] = 4096; + mute_factor = 0, + scale = 20000; + scale_shift = 24; + } + + int32_t energy; + int32_t max_energy; + int32_t energy_update_threshold; + int32_t low_energy_update_threshold; + int16_t filter_state[kMaxLpcOrder]; + int16_t filter[kMaxLpcOrder + 1]; + int16_t mute_factor; + int16_t scale; + int16_t scale_shift; + }; + + int32_t CalculateAutoCorrelation(const int16_t* signal, + int length, + int32_t* auto_correlation) const; + + // Increments the energy threshold by a factor 1 + |kThresholdIncrement|. + void IncrementEnergyThreshold(size_t channel, int32_t sample_energy); + + // Updates the filter parameters. + void SaveParameters(size_t channel, + const int16_t* lpc_coefficients, + const int16_t* filter_state, + int32_t sample_energy, + int32_t residual_energy); + + size_t num_channels_; + scoped_ptr channel_parameters_; + bool initialized_; + NetEq::BackgroundNoiseMode mode_; + + DISALLOW_COPY_AND_ASSIGN(BackgroundNoise); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_BACKGROUND_NOISE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/background_noise_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/background_noise_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/background_noise_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/background_noise_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for BackgroundNoise class. + +#include "webrtc/modules/audio_coding/neteq/background_noise.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace webrtc { + +TEST(BackgroundNoise, CreateAndDestroy) { + size_t channels = 1; + BackgroundNoise bgn(channels); +} + +// TODO(hlundin): Write more tests. + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/bgn_update.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/bgn_update.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/bgn_update.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/bgn_update.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,247 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the function for updating the background noise estimate. - */ - -#include "dsp.h" - -#include "signal_processing_library.h" - -#include "dsp_helpfunctions.h" - -/* Scratch usage: - Designed for BGN_LPC_ORDER <= 10 - - Type Name size startpos endpos - int32_t pw32_autoCorr 22 0 21 (Length (BGN_LPC_ORDER + 1)*2) - int16_t pw16_tempVec 10 22 31 (Length BGN_LPC_ORDER) - int16_t pw16_rc 10 32 41 (Length BGN_LPC_ORDER) - int16_t pw16_outVec 74 0 73 (Length BGN_LPC_ORDER + 64) - - Total: 74 - */ - -#if (BGN_LPC_ORDER > 10) && (defined SCRATCH) -#error BGN_LPC_ORDER is too large for current scratch memory allocation -#endif - -#define SCRATCH_PW32_AUTO_CORR 0 -#define SCRATCH_PW16_TEMP_VEC 22 -#define SCRATCH_PW16_RC 32 -#define SCRATCH_PW16_OUT_VEC 0 - -#define NETEQFIX_BGNFRAQINCQ16 229 /* 0.0035 in Q16 */ - -/**************************************************************************** - * WebRtcNetEQ_BGNUpdate(...) - * - * This function updates the background noise parameter estimates. - * - * Input: - * - inst : NetEQ instance, where the speech history is stored. - * - scratchPtr : Pointer to scratch vector. - * - * Output: - * - inst : Updated information about the BGN characteristics. - * - * Return value : No return value - */ - -void WebRtcNetEQ_BGNUpdate( -#ifdef SCRATCH - DSPInst_t *inst, int16_t *pw16_scratchPtr -#else - DSPInst_t *inst -#endif -) -{ - const int16_t w16_vecLen = 256; - BGNInst_t *BGN_Inst = &(inst->BGNInst); -#ifdef SCRATCH - int32_t *pw32_autoCorr = (int32_t*) (pw16_scratchPtr + SCRATCH_PW32_AUTO_CORR); - int16_t *pw16_tempVec = pw16_scratchPtr + SCRATCH_PW16_TEMP_VEC; - int16_t *pw16_rc = pw16_scratchPtr + SCRATCH_PW16_RC; - int16_t *pw16_outVec = pw16_scratchPtr + SCRATCH_PW16_OUT_VEC; -#else - int32_t pw32_autoCorr[BGN_LPC_ORDER + 1]; - int16_t pw16_tempVec[BGN_LPC_ORDER]; - int16_t pw16_outVec[BGN_LPC_ORDER + 64]; - int16_t pw16_rc[BGN_LPC_ORDER]; -#endif - int16_t pw16_A[BGN_LPC_ORDER + 1]; - int32_t w32_tmp; - int16_t *pw16_vec; - int16_t w16_maxSample; - int16_t w16_tmp, w16_tmp2; - int16_t w16_enSampleShift; - int32_t w32_en, w32_enBGN; - int32_t w32_enUpdateThreashold; - int16_t stability; - - pw16_vec = inst->pw16_speechHistory + inst->w16_speechHistoryLen - w16_vecLen; - -#ifdef NETEQ_VAD - if( !inst->VADInst.VADEnabled /* we are not using post-decode VAD */ - || inst->VADInst.VADDecision == 0 ) - { /* ... or, post-decode VAD says passive speaker */ -#endif /* NETEQ_VAD */ - - /*Insert zeros to guarantee that boundary values do not distort autocorrelation */ - WEBRTC_SPL_MEMCPY_W16(pw16_tempVec, pw16_vec - BGN_LPC_ORDER, BGN_LPC_ORDER); - WebRtcSpl_MemSetW16(pw16_vec - BGN_LPC_ORDER, 0, BGN_LPC_ORDER); - - w16_maxSample = WebRtcSpl_MaxAbsValueW16(pw16_vec, w16_vecLen); - w16_tmp = 8 /* log2(w16_veclen) = 8 */ - - WebRtcSpl_NormW32(WEBRTC_SPL_MUL_16_16(w16_maxSample, w16_maxSample)); - w16_tmp = WEBRTC_SPL_MAX(0, w16_tmp); - - WebRtcNetEQ_CrossCorr(pw32_autoCorr, pw16_vec, pw16_vec, w16_vecLen, BGN_LPC_ORDER + 1, - w16_tmp, -1); - - /* Copy back data */ - WEBRTC_SPL_MEMCPY_W16(pw16_vec - BGN_LPC_ORDER, pw16_tempVec, BGN_LPC_ORDER); - - w16_enSampleShift = 8 - w16_tmp; /* Number of shifts to get energy/sample */ - /* pw32_autoCorr[0]>>w16_enSampleShift */ - w32_en = WEBRTC_SPL_RSHIFT_W32(pw32_autoCorr[0], w16_enSampleShift); - if ((w32_en < BGN_Inst->w32_energyUpdate -#ifdef NETEQ_VAD - /* post-decode VAD disabled and w32_en sufficiently low */ - && !inst->VADInst.VADEnabled) - /* ... or, post-decode VAD says passive speaker */ - || (inst->VADInst.VADEnabled && inst->VADInst.VADDecision == 0) -#else - ) /* just close the extra parenthesis */ -#endif /* NETEQ_VAD */ - ) - { - /* Generate LPC coefficients */ - if (pw32_autoCorr[0] > 0) - { - /* regardless of whether the filter is actually updated or not, - update energy threshold levels, since we have in fact observed - a low energy signal */ - if (w32_en < BGN_Inst->w32_energyUpdate) - { - /* Never get under 1.0 in average sample energy */ - BGN_Inst->w32_energyUpdate = WEBRTC_SPL_MAX(w32_en, 1); - BGN_Inst->w32_energyUpdateLow = 0; - } - - stability = WebRtcSpl_LevinsonDurbin(pw32_autoCorr, pw16_A, pw16_rc, BGN_LPC_ORDER); - /* Only update BGN if filter is stable */ - if (stability != 1) - { - return; - } - } - else - { - /* Do not update */ - return; - } - /* Generate the CNG gain factor by looking at the energy of the residual */ - WebRtcSpl_FilterMAFastQ12(pw16_vec + w16_vecLen - 64, pw16_outVec, pw16_A, - BGN_LPC_ORDER + 1, 64); - w32_enBGN = WebRtcNetEQ_DotW16W16(pw16_outVec, pw16_outVec, 64, 0); - /* Dot product should never overflow since it is BGN and residual! */ - - /* - * Check spectral flatness - * Comparing the residual variance with the input signal variance tells - * if the spectrum is flat or not. - * (20*w32_enBGN) >= (w32_en<<6) - * Also ensure that the energy is non-zero. - */ - if ((WEBRTC_SPL_MUL_32_16(w32_enBGN, 20) >= WEBRTC_SPL_LSHIFT_W32(w32_en, 6)) - && (w32_en > 0)) - { - /* spectrum is flat enough; save filter parameters */ - - WEBRTC_SPL_MEMCPY_W16(BGN_Inst->pw16_filter, pw16_A, BGN_LPC_ORDER+1); - WEBRTC_SPL_MEMCPY_W16(BGN_Inst->pw16_filterState, - pw16_vec + w16_vecLen - BGN_LPC_ORDER, BGN_LPC_ORDER); - - /* Save energy level */ - BGN_Inst->w32_energy = WEBRTC_SPL_MAX(w32_en, 1); - - /* Update energy threshold levels */ - /* Never get under 1.0 in average sample energy */ - BGN_Inst->w32_energyUpdate = WEBRTC_SPL_MAX(w32_en, 1); - BGN_Inst->w32_energyUpdateLow = 0; - - /* Normalize w32_enBGN to 29 or 30 bits before sqrt */ - w16_tmp2 = WebRtcSpl_NormW32(w32_enBGN) - 1; - if (w16_tmp2 & 0x1) - { - w16_tmp2 -= 1; /* Even number of shifts required */ - } - w32_enBGN = WEBRTC_SPL_SHIFT_W32(w32_enBGN, w16_tmp2); - - /* Calculate scale and shift factor */ - BGN_Inst->w16_scale = (int16_t) WebRtcSpl_SqrtFloor(w32_enBGN); - BGN_Inst->w16_scaleShift = 13 + ((6 + w16_tmp2) >> 1); /* RANDN table is in Q13, */ - /* 6=log2(64) */ - - BGN_Inst->w16_initialized = 1; - } - - } - else - { - /* - * Will only happen if post-decode VAD is disabled and w32_en is not low enough. - * Increase the threshold for update so that it increases by a factor 4 in four - * seconds. - * energy = energy * 1.0035 - */ - w32_tmp = WEBRTC_SPL_MUL_16_16_RSFT(NETEQFIX_BGNFRAQINCQ16, - BGN_Inst->w32_energyUpdateLow, 16); - w32_tmp += WEBRTC_SPL_MUL_16_16(NETEQFIX_BGNFRAQINCQ16, - (int16_t)(BGN_Inst->w32_energyUpdate & 0xFF)); - w32_tmp += (WEBRTC_SPL_MUL_16_16(NETEQFIX_BGNFRAQINCQ16, - (int16_t)((BGN_Inst->w32_energyUpdate>>8) & 0xFF)) << 8); - BGN_Inst->w32_energyUpdateLow += w32_tmp; - - BGN_Inst->w32_energyUpdate += WEBRTC_SPL_MUL_16_16(NETEQFIX_BGNFRAQINCQ16, - (int16_t)(BGN_Inst->w32_energyUpdate>>16)); - BGN_Inst->w32_energyUpdate += BGN_Inst->w32_energyUpdateLow >> 16; - BGN_Inst->w32_energyUpdateLow = (BGN_Inst->w32_energyUpdateLow & 0x0FFFF); - - /* Update maximum energy */ - /* Decrease by a factor 1/1024 each time */ - BGN_Inst->w32_energyMax = BGN_Inst->w32_energyMax - (BGN_Inst->w32_energyMax >> 10); - if (w32_en > BGN_Inst->w32_energyMax) - { - BGN_Inst->w32_energyMax = w32_en; - } - - /* Set update level to at the minimum 60.21dB lower then the maximum energy */ - w32_enUpdateThreashold = (BGN_Inst->w32_energyMax + 524288) >> 20; - if (w32_enUpdateThreashold > BGN_Inst->w32_energyUpdate) - { - BGN_Inst->w32_energyUpdate = w32_enUpdateThreashold; - } - } - -#ifdef NETEQ_VAD -} /* closing initial if-statement */ -#endif /* NETEQ_VAD */ - - return; -} - -#undef SCRATCH_PW32_AUTO_CORR -#undef SCRATCH_PW16_TEMP_VEC -#undef SCRATCH_PW16_RC -#undef SCRATCH_PW16_OUT_VEC - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/buffer_level_filter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/buffer_level_filter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/buffer_level_filter.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/buffer_level_filter.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/buffer_level_filter.h" + +#include // Provide access to std::max. + +namespace webrtc { + +BufferLevelFilter::BufferLevelFilter() { + Reset(); +} + +void BufferLevelFilter::Reset() { + filtered_current_level_ = 0; + level_factor_ = 253; +} + +void BufferLevelFilter::Update(int buffer_size_packets, + int time_stretched_samples, + int packet_len_samples) { + // Filter: + // |filtered_current_level_| = |level_factor_| * |filtered_current_level_| + + // (1 - |level_factor_|) * |buffer_size_packets| + // |level_factor_| and |filtered_current_level_| are in Q8. + // |buffer_size_packets| is in Q0. + filtered_current_level_ = ((level_factor_ * filtered_current_level_) >> 8) + + ((256 - level_factor_) * buffer_size_packets); + + // Account for time-scale operations (accelerate and pre-emptive expand). + if (time_stretched_samples && packet_len_samples > 0) { + // Time-scaling has been performed since last filter update. Subtract the + // value of |time_stretched_samples| from |filtered_current_level_| after + // converting |time_stretched_samples| from samples to packets in Q8. + // Make sure that the filtered value remains non-negative. + filtered_current_level_ = std::max(0, + filtered_current_level_ - + (time_stretched_samples << 8) / packet_len_samples); + } +} + +void BufferLevelFilter::SetTargetBufferLevel(int target_buffer_level) { + if (target_buffer_level <= 1) { + level_factor_ = 251; + } else if (target_buffer_level <= 3) { + level_factor_ = 252; + } else if (target_buffer_level <= 7) { + level_factor_ = 253; + } else { + level_factor_ = 254; + } +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/buffer_level_filter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/buffer_level_filter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/buffer_level_filter.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/buffer_level_filter.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_BUFFER_LEVEL_FILTER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_BUFFER_LEVEL_FILTER_H_ + +#include "webrtc/base/constructormagic.h" + +namespace webrtc { + +class BufferLevelFilter { + public: + BufferLevelFilter(); + virtual ~BufferLevelFilter() {} + virtual void Reset(); + + // Updates the filter. Current buffer size is |buffer_size_packets| (Q0). + // If |time_stretched_samples| is non-zero, the value is converted to the + // corresponding number of packets, and is subtracted from the filtered + // value (thus bypassing the filter operation). |packet_len_samples| is the + // number of audio samples carried in each incoming packet. + virtual void Update(int buffer_size_packets, int time_stretched_samples, + int packet_len_samples); + + // Set the current target buffer level (obtained from + // DelayManager::base_target_level()). Used to select the appropriate + // filter coefficient. + virtual void SetTargetBufferLevel(int target_buffer_level); + + virtual int filtered_current_level() const { return filtered_current_level_; } + + private: + int level_factor_; // Filter factor for the buffer level filter in Q8. + int filtered_current_level_; // Filtered current buffer level in Q8. + + DISALLOW_COPY_AND_ASSIGN(BufferLevelFilter); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_BUFFER_LEVEL_FILTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/buffer_level_filter_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/buffer_level_filter_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/buffer_level_filter_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/buffer_level_filter_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for BufferLevelFilter class. + +#include "webrtc/modules/audio_coding/neteq/buffer_level_filter.h" + +#include // Access to pow function. + +#include "testing/gtest/include/gtest/gtest.h" + +namespace webrtc { + +TEST(BufferLevelFilter, CreateAndDestroy) { + BufferLevelFilter* filter = new BufferLevelFilter(); + EXPECT_EQ(0, filter->filtered_current_level()); + delete filter; +} + +TEST(BufferLevelFilter, ConvergenceTest) { + BufferLevelFilter filter; + for (int times = 10; times <= 50; times += 10) { + for (int value = 100; value <= 200; value += 10) { + filter.Reset(); + filter.SetTargetBufferLevel(1); // Makes filter coefficient 251/256. + std::ostringstream ss; + ss << "times = " << times << ", value = " << value; + SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. + for (int i = 0; i < times; ++i) { + filter.Update(value, 0 /* time_stretched_samples */, + 160 /* packet_len_samples */); + } + // Expect the filtered value to be (theoretically) + // (1 - (251/256) ^ |times|) * |value|. + double expected_value_double = + (1 - pow(251.0 / 256.0, times)) * value; + int expected_value = static_cast(expected_value_double); + // filtered_current_level() returns the value in Q8. + // The actual value may differ slightly from the expected value due to + // intermediate-stage rounding errors in the filter implementation. + // This is why we have to use EXPECT_NEAR with a tolerance of +/-1. + EXPECT_NEAR(expected_value, filter.filtered_current_level() >> 8, 1); + } + } +} + +// Verify that target buffer level impacts on the filter convergence. +TEST(BufferLevelFilter, FilterFactor) { + BufferLevelFilter filter; + // Update 10 times with value 100. + const int kTimes = 10; + const int kValue = 100; + + filter.SetTargetBufferLevel(3); // Makes filter coefficient 252/256. + for (int i = 0; i < kTimes; ++i) { + filter.Update(kValue, 0 /* time_stretched_samples */, + 160 /* packet_len_samples */); + } + // Expect the filtered value to be + // (1 - (252/256) ^ |kTimes|) * |kValue|. + int expected_value = 14; + // filtered_current_level() returns the value in Q8. + EXPECT_EQ(expected_value, filter.filtered_current_level() >> 8); + + filter.Reset(); + filter.SetTargetBufferLevel(7); // Makes filter coefficient 253/256. + for (int i = 0; i < kTimes; ++i) { + filter.Update(kValue, 0 /* time_stretched_samples */, + 160 /* packet_len_samples */); + } + // Expect the filtered value to be + // (1 - (253/256) ^ |kTimes|) * |kValue|. + expected_value = 11; + // filtered_current_level() returns the value in Q8. + EXPECT_EQ(expected_value, filter.filtered_current_level() >> 8); + + filter.Reset(); + filter.SetTargetBufferLevel(8); // Makes filter coefficient 254/256. + for (int i = 0; i < kTimes; ++i) { + filter.Update(kValue, 0 /* time_stretched_samples */, + 160 /* packet_len_samples */); + } + // Expect the filtered value to be + // (1 - (254/256) ^ |kTimes|) * |kValue|. + expected_value = 7; + // filtered_current_level() returns the value in Q8. + EXPECT_EQ(expected_value, filter.filtered_current_level() >> 8); +} + + +TEST(BufferLevelFilter, TimeStretchedSamples) { + BufferLevelFilter filter; + filter.SetTargetBufferLevel(1); // Makes filter coefficient 251/256. + // Update 10 times with value 100. + const int kTimes = 10; + const int kValue = 100; + const int kPacketSizeSamples = 160; + const int kNumPacketsStretched = 2; + const int kTimeStretchedSamples = kNumPacketsStretched * kPacketSizeSamples; + for (int i = 0; i < kTimes; ++i) { + // Packet size set to 0. Do not expect the parameter + // |kTimeStretchedSamples| to have any effect. + filter.Update(kValue, kTimeStretchedSamples, 0 /* packet_len_samples */); + } + // Expect the filtered value to be + // (1 - (251/256) ^ |kTimes|) * |kValue|. + const int kExpectedValue = 17; + // filtered_current_level() returns the value in Q8. + EXPECT_EQ(kExpectedValue, filter.filtered_current_level() >> 8); + + // Update filter again, now with non-zero value for packet length. + // Set the current filtered value to be the input, in order to isolate the + // impact of |kTimeStretchedSamples|. + filter.Update(filter.filtered_current_level() >> 8, kTimeStretchedSamples, + kPacketSizeSamples); + EXPECT_EQ(kExpectedValue - kNumPacketsStretched, + filter.filtered_current_level() >> 8); + // Try negative value and verify that we come back to the previous result. + filter.Update(filter.filtered_current_level() >> 8, -kTimeStretchedSamples, + kPacketSizeSamples); + EXPECT_EQ(kExpectedValue, filter.filtered_current_level() >> 8); +} + +TEST(BufferLevelFilter, TimeStretchedSamplesNegativeUnevenFrames) { + BufferLevelFilter filter; + filter.SetTargetBufferLevel(1); // Makes filter coefficient 251/256. + // Update 10 times with value 100. + const int kTimes = 10; + const int kValue = 100; + const int kPacketSizeSamples = 160; + const int kTimeStretchedSamples = -3.1415 * kPacketSizeSamples; + for (int i = 0; i < kTimes; ++i) { + // Packet size set to 0. Do not expect the parameter + // |kTimeStretchedSamples| to have any effect. + filter.Update(kValue, kTimeStretchedSamples, 0 /* packet_len_samples */); + } + // Expect the filtered value to be + // (1 - (251/256) ^ |kTimes|) * |kValue|. + const int kExpectedValue = 17; + // filtered_current_level() returns the value in Q8. + EXPECT_EQ(kExpectedValue, filter.filtered_current_level() >> 8); + + // Update filter again, now with non-zero value for packet length. + // Set the current filtered value to be the input, in order to isolate the + // impact of |kTimeStretchedSamples|. + filter.Update(filter.filtered_current_level() >> 8, kTimeStretchedSamples, + kPacketSizeSamples); + EXPECT_EQ(21, filter.filtered_current_level() >> 8); + // Try negative value and verify that we come back to the previous result. + filter.Update(filter.filtered_current_level() >> 8, -kTimeStretchedSamples, + kPacketSizeSamples); + EXPECT_EQ(kExpectedValue, filter.filtered_current_level() >> 8); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/buffer_stats.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/buffer_stats.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/buffer_stats.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/buffer_stats.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Calculates and stores the packet buffer statistics. - */ - -#ifndef BUFFER_STATS_H -#define BUFFER_STATS_H - -#include "automode.h" -#include "webrtc_neteq.h" /* to define enum WebRtcNetEQPlayoutMode */ - -/* NetEQ related decisions */ -#define BUFSTATS_DO_NORMAL 0 -#define BUFSTATS_DO_ACCELERATE 1 -#define BUFSTATS_DO_MERGE 2 -#define BUFSTATS_DO_EXPAND 3 -#define BUFSTAT_REINIT 4 -#define BUFSTATS_DO_RFC3389CNG_PACKET 5 -#define BUFSTATS_DO_RFC3389CNG_NOPACKET 6 -#define BUFSTATS_DO_INTERNAL_CNG_NOPACKET 7 -#define BUFSTATS_DO_PREEMPTIVE_EXPAND 8 -#define BUFSTAT_REINIT_DECODER 9 -#define BUFSTATS_DO_DTMF_ONLY 10 -/* Decisions related to when NetEQ is switched off (or in FAX mode) */ -#define BUFSTATS_DO_ALTERNATIVE_PLC 11 -#define BUFSTATS_DO_ALTERNATIVE_PLC_INC_TS 12 -#define BUFSTATS_DO_AUDIO_REPETITION 13 -#define BUFSTATS_DO_AUDIO_REPETITION_INC_TS 14 - -/* Reinit decoder states after this number of expands (upon arrival of new packet) */ -#define REINIT_AFTER_EXPANDS 100 - -/* Wait no longer than this number of RecOut calls before using an "early" packet */ -#define MAX_WAIT_FOR_PACKET 10 - -/* CNG modes */ -#define CNG_OFF 0 -#define CNG_RFC3389_ON 1 -#define CNG_INTERNAL_ON 2 - -typedef struct -{ - - /* store statistical data here */ - int16_t w16_cngOn; /* remember if CNG is interrupted by other event (e.g. DTMF) */ - int16_t w16_noExpand; - int32_t uw32_CNGplayedTS; - - /* VQmon data */ - uint16_t avgDelayMsQ8; - int16_t maxDelayMs; - - AutomodeInst_t Automode_inst; - -} BufstatsInst_t; - -/**************************************************************************** - * WebRtcNetEQ_BufstatsDecision() - * - * Gives a decision about what action that is currently desired - * - * - * Input: - * inst: The bufstat instance - * cur_size: Current buffer size in ms in Q3 domain - * targetTS: The desired timestamp to start playout from - * availableTS: The closest future value available in buffer - * noPacket 1 if no packet is available, makes availableTS undefined - * prevPlayMode mode of last NetEq playout - * timestampsPerCall number of timestamp for 10ms - * - * Output: - * Returns: A decision, as defined above (see top of file) - * - */ - -uint16_t WebRtcNetEQ_BufstatsDecision(BufstatsInst_t *inst, int16_t frameSize, - int32_t cur_size, uint32_t targetTS, - uint32_t availableTS, int noPacket, - int cngPacket, int prevPlayMode, - enum WebRtcNetEQPlayoutMode playoutMode, - int timestampsPerCall, int NoOfExpandCalls, - int16_t fs_mult, - int16_t lastModeBGNonly, int playDtmf); - -#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/bufstats_decision.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/bufstats_decision.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/bufstats_decision.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/bufstats_decision.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,427 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the function where the main decision logic for buffer level - * adaptation happens. - */ - -#include "buffer_stats.h" - -#include - -#include "signal_processing_library.h" - -#include "automode.h" -#include "neteq_defines.h" -#include "neteq_error_codes.h" -#include "webrtc_neteq.h" - -#define NETEQ_BUFSTAT_20MS_Q7 2560 /* = 20 ms in Q7 */ - -uint16_t WebRtcNetEQ_BufstatsDecision(BufstatsInst_t *inst, int16_t frameSize, - int32_t cur_size, uint32_t targetTS, - uint32_t availableTS, int noPacket, - int cngPacket, int prevPlayMode, - enum WebRtcNetEQPlayoutMode playoutMode, - int timestampsPerCall, int NoOfExpandCalls, - int16_t fs_mult, - int16_t lastModeBGNonly, int playDtmf) -{ - - int currentDelayMs; - int32_t currSizeSamples = cur_size; - int extraDelayPacketsQ8 = 0; - - /* Avoid overflow if the buffer size should be really large (cur_size is limited 256ms) */ - int32_t curr_sizeQ7 = WEBRTC_SPL_LSHIFT_W32(cur_size, 4); - int level_limit_hi, level_limit_lo; - - inst->Automode_inst.prevTimeScale &= (prevPlayMode == MODE_SUCCESS_ACCELERATE - || prevPlayMode == MODE_LOWEN_ACCELERATE || prevPlayMode == MODE_SUCCESS_PREEMPTIVE - || prevPlayMode == MODE_LOWEN_PREEMPTIVE); - - if ((prevPlayMode != MODE_RFC3389CNG) && (prevPlayMode != MODE_CODEC_INTERNAL_CNG)) - { - /* - * Do not update buffer history if currently playing CNG - * since it will bias the filtered buffer level. - */ - WebRtcNetEQ_BufferLevelFilter(cur_size, &(inst->Automode_inst), timestampsPerCall, - fs_mult); - } - else - { - /* only update time counters */ - inst->Automode_inst.packetIatCountSamp += timestampsPerCall; /* packet inter-arrival time */ - inst->Automode_inst.peakIatCountSamp += timestampsPerCall; /* peak inter-arrival time */ - inst->Automode_inst.timescaleHoldOff >>= 1; /* time-scaling limiter */ - } - cur_size = WEBRTC_SPL_MIN(curr_sizeQ7, WEBRTC_SPL_WORD16_MAX); - - /* Calculate VQmon related variables */ - /* avgDelay = avgDelay*(511/512) + currentDelay*(1/512) (sample ms delay in Q8) */ - inst->avgDelayMsQ8 = (int16_t) (WEBRTC_SPL_MUL_16_16_RSFT(inst->avgDelayMsQ8,511,9) - + (cur_size >> 9)); - - /* Update maximum delay if needed */ - currentDelayMs = (curr_sizeQ7 >> 7); - if (currentDelayMs > inst->maxDelayMs) - { - inst->maxDelayMs = currentDelayMs; - } - - /* NetEQ is on with normal or steaming mode */ - if (playoutMode == kPlayoutOn || playoutMode == kPlayoutStreaming) - { - /* Guard for errors, so that it should not get stuck in error mode */ - if (prevPlayMode == MODE_ERROR) - { - if (noPacket) - { - return BUFSTATS_DO_EXPAND; - } - else - { - return BUFSTAT_REINIT; - } - } - - if (prevPlayMode != MODE_EXPAND && prevPlayMode != MODE_FADE_TO_BGN) - { - inst->w16_noExpand = 1; - } - else - { - inst->w16_noExpand = 0; - } - - if (cngPacket) - { - /* signed difference between wanted and available TS */ - int32_t diffTS = (inst->uw32_CNGplayedTS + targetTS) - availableTS; - int32_t optimal_level_samp = (inst->Automode_inst.optBufLevel * - inst->Automode_inst.packetSpeechLenSamp) >> 8; - int32_t excess_waiting_time_samp = -diffTS - optimal_level_samp; - - if (excess_waiting_time_samp > optimal_level_samp / 2) - { - /* The waiting time for this packet will be longer than 1.5 - * times the wanted buffer delay. Advance the clock to cut - * waiting time down to the optimal. - */ - inst->uw32_CNGplayedTS += excess_waiting_time_samp; - diffTS += excess_waiting_time_samp; - } - - if ((diffTS) < 0 && (prevPlayMode == MODE_RFC3389CNG)) - { - /* Not time to play this packet yet. Wait another round before using this - * packet. Keep on playing CNG from previous CNG parameters. */ - return BUFSTATS_DO_RFC3389CNG_NOPACKET; - } - - /* otherwise, go for the CNG packet now */ - return BUFSTATS_DO_RFC3389CNG_PACKET; - } - - /*Check for expand/cng */ - if (noPacket) - { - if (inst->w16_cngOn == CNG_RFC3389_ON) - { - /* keep on playing CNG */ - return BUFSTATS_DO_RFC3389CNG_NOPACKET; - } - else if (inst->w16_cngOn == CNG_INTERNAL_ON) - { - /* keep on playing internal CNG */ - return BUFSTATS_DO_INTERNAL_CNG_NOPACKET; - } - else if (playDtmf == 1) - { - /* we have not audio data, but can play DTMF */ - return BUFSTATS_DO_DTMF_ONLY; - } - else - { - /* nothing to play => do Expand */ - return BUFSTATS_DO_EXPAND; - } - } - - /* - * If the expand period was very long, reset NetEQ since it is likely that the - * sender was restarted. - */ - if (NoOfExpandCalls > REINIT_AFTER_EXPANDS) return BUFSTAT_REINIT_DECODER; - - /* Calculate extra delay in Q8 packets */ - if (inst->Automode_inst.extraDelayMs > 0 && inst->Automode_inst.packetSpeechLenSamp - > 0) - { - - /* (extra delay in samples in Q8) */ - extraDelayPacketsQ8 = - ((inst->Automode_inst.extraDelayMs * 8 * fs_mult) << 8) / - inst->Automode_inst.packetSpeechLenSamp; - } - - /* Check if needed packet is available */ - if (targetTS == availableTS) - { - - /* If last mode was not expand, and there is no DTMF to play */ - if (inst->w16_noExpand == 1 && playDtmf == 0) - { - /* If so check for accelerate */ - - level_limit_lo = ((inst->Automode_inst.optBufLevel) >> 1) /* 50 % */ - + ((inst->Automode_inst.optBufLevel) >> 2); /* ... + 25% = 75% */ - - /* set upper limit to optBufLevel, but make sure that window is at least 20ms */ - level_limit_hi = WEBRTC_SPL_MAX(inst->Automode_inst.optBufLevel, - level_limit_lo + - WebRtcSpl_DivW32W16ResW16((WEBRTC_SPL_MUL(20*8, fs_mult) << 8), - inst->Automode_inst.packetSpeechLenSamp)); - - /* if extra delay is non-zero, add it */ - if (extraDelayPacketsQ8 > 0) - { - level_limit_hi += extraDelayPacketsQ8; - level_limit_lo += extraDelayPacketsQ8; - } - - if (((inst->Automode_inst.buffLevelFilt >= level_limit_hi) && - (inst->Automode_inst.timescaleHoldOff == 0)) || - (inst->Automode_inst.buffLevelFilt >= level_limit_hi << 2)) - { - /* - * Buffer level higher than limit and time-scaling allowed, - * OR buffer level _really_ high. - */ - return BUFSTATS_DO_ACCELERATE; - } - else if ((inst->Automode_inst.buffLevelFilt < level_limit_lo) - && (inst->Automode_inst.timescaleHoldOff == 0)) - { - return BUFSTATS_DO_PREEMPTIVE_EXPAND; - } - } - return BUFSTATS_DO_NORMAL; - } - - /* Check for Merge */ - else if (availableTS > targetTS) - { - - /* Check that we do not play a packet "too early" */ - if ((prevPlayMode == MODE_EXPAND) - && (availableTS - targetTS - < (uint32_t) WEBRTC_SPL_MUL_16_16((int16_t)timestampsPerCall, - (int16_t)REINIT_AFTER_EXPANDS)) - && (NoOfExpandCalls < MAX_WAIT_FOR_PACKET) - && (availableTS - > targetTS - + WEBRTC_SPL_MUL_16_16((int16_t)timestampsPerCall, - (int16_t)NoOfExpandCalls)) - && (inst->Automode_inst.buffLevelFilt <= inst->Automode_inst.optBufLevel - + extraDelayPacketsQ8)) - { - if (playDtmf == 1) - { - /* we still have DTMF to play, so do not perform expand */ - return BUFSTATS_DO_DTMF_ONLY; - } - else - { - /* nothing to play */ - return BUFSTATS_DO_EXPAND; - } - } - - /* If previous was CNG period or BGNonly then no merge is needed */ - if ((prevPlayMode == MODE_RFC3389CNG) || (prevPlayMode == MODE_CODEC_INTERNAL_CNG) - || lastModeBGNonly) - { - /* - * Keep the same delay as before the CNG (or maximum 70 ms in buffer as safety - * precaution), but make sure that the number of samples in buffer is no - * higher than 4 times the optimal level. - */ - int32_t diffTS = (inst->uw32_CNGplayedTS + targetTS) - availableTS; - int val = ((inst->Automode_inst.optBufLevel + - extraDelayPacketsQ8) * - inst->Automode_inst.packetSpeechLenSamp) >> 6; - if (diffTS >= 0 || val < currSizeSamples) - { - /* it is time to play this new packet */ - return BUFSTATS_DO_NORMAL; - } - else - { - /* it is too early to play this new packet => keep on playing CNG */ - if (prevPlayMode == MODE_RFC3389CNG) - { - return BUFSTATS_DO_RFC3389CNG_NOPACKET; - } - else if (prevPlayMode == MODE_CODEC_INTERNAL_CNG) - { - return BUFSTATS_DO_INTERNAL_CNG_NOPACKET; - } - else if (playDtmf == 1) - { - /* we have not audio data, but can play DTMF */ - return BUFSTATS_DO_DTMF_ONLY; - } - else /* lastModeBGNonly */ - { - /* signal expand, but this will result in BGN again */ - return BUFSTATS_DO_EXPAND; - } - } - } - - /* Do not merge unless we have done a Expand before (for complexity reasons) */ - if ((inst->w16_noExpand == 0) || ((frameSize < timestampsPerCall) && (cur_size - > NETEQ_BUFSTAT_20MS_Q7))) - { - return BUFSTATS_DO_MERGE; - } - else if (playDtmf == 1) - { - /* play DTMF instead of expand */ - return BUFSTATS_DO_DTMF_ONLY; - } - else - { - return BUFSTATS_DO_EXPAND; - } - } - } - else - { /* kPlayoutOff or kPlayoutFax */ - if (cngPacket) - { - if (((int32_t) ((inst->uw32_CNGplayedTS + targetTS) - availableTS)) >= 0) - { - /* time to play this packet now */ - return BUFSTATS_DO_RFC3389CNG_PACKET; - } - else - { - /* wait before playing this packet */ - return BUFSTATS_DO_RFC3389CNG_NOPACKET; - } - } - if (noPacket) - { - /* - * No packet => - * 1. If in CNG mode play as usual - * 2. Otherwise use other method to generate data and hold TS value - */ - if (inst->w16_cngOn == CNG_RFC3389_ON) - { - /* keep on playing CNG */ - return BUFSTATS_DO_RFC3389CNG_NOPACKET; - } - else if (inst->w16_cngOn == CNG_INTERNAL_ON) - { - /* keep on playing internal CNG */ - return BUFSTATS_DO_INTERNAL_CNG_NOPACKET; - } - else - { - /* nothing to play => invent some data to play out */ - if (playoutMode == kPlayoutOff) - { - return BUFSTATS_DO_ALTERNATIVE_PLC; - } - else if (playoutMode == kPlayoutFax) - { - return BUFSTATS_DO_AUDIO_REPETITION; - } - else - { - /* UNDEFINED, should not get here... */ - assert(0); - return BUFSTAT_REINIT; - } - } - } - else if (targetTS == availableTS) - { - return BUFSTATS_DO_NORMAL; - } - else - { - if (((int32_t) ((inst->uw32_CNGplayedTS + targetTS) - availableTS)) >= 0) - { - return BUFSTATS_DO_NORMAL; - } - else if (playoutMode == kPlayoutOff) - { - /* - * If currently playing CNG, continue with that. Don't increase TS - * since uw32_CNGplayedTS will be increased. - */ - if (inst->w16_cngOn == CNG_RFC3389_ON) - { - return BUFSTATS_DO_RFC3389CNG_NOPACKET; - } - else if (inst->w16_cngOn == CNG_INTERNAL_ON) - { - return BUFSTATS_DO_INTERNAL_CNG_NOPACKET; - } - else - { - /* - * Otherwise, do PLC and increase TS while waiting for the time to - * play this packet. - */ - return BUFSTATS_DO_ALTERNATIVE_PLC_INC_TS; - } - } - else if (playoutMode == kPlayoutFax) - { - /* - * If currently playing CNG, continue with that don't increase TS since - * uw32_CNGplayedTS will be increased. - */ - if (inst->w16_cngOn == CNG_RFC3389_ON) - { - return BUFSTATS_DO_RFC3389CNG_NOPACKET; - } - else if (inst->w16_cngOn == CNG_INTERNAL_ON) - { - return BUFSTATS_DO_INTERNAL_CNG_NOPACKET; - } - else - { - /* - * Otherwise, do audio repetition and increase TS while waiting for the - * time to play this packet. - */ - return BUFSTATS_DO_AUDIO_REPETITION_INC_TS; - } - } - else - { - /* UNDEFINED, should not get here... */ - assert(0); - return BUFSTAT_REINIT; - } - } - } - /* We should not get here (but sometimes we do anyway...) */ - return BUFSTAT_REINIT; -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/cng_internal.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/cng_internal.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/cng_internal.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/cng_internal.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the function for obtaining comfort noise from noise parameters - * according to IETF RFC 3389. - */ - -#include "dsp.h" - -#include "signal_processing_library.h" -#include "webrtc_cng.h" - -#include "dsp_helpfunctions.h" -#include "neteq_error_codes.h" - -/**************************************************************************** - * WebRtcNetEQ_Cng(...) - * - * This function produces CNG according to RFC 3389. - * - * Input: - * - inst : NetEQ DSP instance - * - len : Number of samples to produce (max 640 or - * 640 - fsHz*5/8000 for first-time CNG, governed by - * the definition of WEBRTC_CNG_MAX_OUTSIZE_ORDER in - * webrtc_cng.h) - * - * Output: - * - pw16_outData : Output CNG - * - * Return value : 0 - Ok - * <0 - Error - */ - -#ifdef NETEQ_CNG_CODEC -/* Must compile NetEQ with CNG support to enable this function */ - -int WebRtcNetEQ_Cng(DSPInst_t *inst, int16_t *pw16_outData, int len) -{ - int16_t w16_winMute = 0; /* mixing factor for overlap data */ - int16_t w16_winUnMute = 0; /* mixing factor for comfort noise */ - int16_t w16_winMuteInc = 0; /* mixing factor increment (negative) */ - int16_t w16_winUnMuteInc = 0; /* mixing factor increment */ - int i; - - /* - * Check if last RecOut call was other than RFC3389, - * that is, this call is the first of a CNG period. - */ - if (inst->w16_mode != MODE_RFC3389CNG) - { - /* Reset generation and overlap slightly with old data */ - - /* Generate len samples + overlap */ - if (WebRtcCng_Generate(inst->CNG_Codec_inst, pw16_outData, - (int16_t) (len + inst->ExpandInst.w16_overlap), 1) < 0) - { - /* error returned */ - return -WebRtcCng_GetErrorCodeDec(inst->CNG_Codec_inst); - } - - /* Set windowing parameters depending on sample rate */ - if (inst->fs == 8000) - { - /* Windowing in Q15 */ - w16_winMute = NETEQ_OVERLAP_WINMUTE_8KHZ_START; - w16_winMuteInc = NETEQ_OVERLAP_WINMUTE_8KHZ_INC; - w16_winUnMute = NETEQ_OVERLAP_WINUNMUTE_8KHZ_START; - w16_winUnMuteInc = NETEQ_OVERLAP_WINUNMUTE_8KHZ_INC; -#ifdef NETEQ_WIDEBAND - } - else if (inst->fs == 16000) - { - /* Windowing in Q15 */ - w16_winMute = NETEQ_OVERLAP_WINMUTE_16KHZ_START; - w16_winMuteInc = NETEQ_OVERLAP_WINMUTE_16KHZ_INC; - w16_winUnMute = NETEQ_OVERLAP_WINUNMUTE_16KHZ_START; - w16_winUnMuteInc = NETEQ_OVERLAP_WINUNMUTE_16KHZ_INC; -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - } - else if (inst->fs == 32000) - { - /* Windowing in Q15 */ - w16_winMute = NETEQ_OVERLAP_WINMUTE_32KHZ_START; - w16_winMuteInc = NETEQ_OVERLAP_WINMUTE_32KHZ_INC; - w16_winUnMute = NETEQ_OVERLAP_WINUNMUTE_32KHZ_START; - w16_winUnMuteInc = NETEQ_OVERLAP_WINUNMUTE_32KHZ_INC; -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - } - else if (inst->fs == 48000) - { - /* Windowing in Q15 */ - w16_winMute = NETEQ_OVERLAP_WINMUTE_48KHZ_START; - w16_winMuteInc = NETEQ_OVERLAP_WINMUTE_48KHZ_INC; - w16_winUnMute = NETEQ_OVERLAP_WINUNMUTE_48KHZ_START; - w16_winUnMuteInc = NETEQ_OVERLAP_WINUNMUTE_48KHZ_INC; -#endif - } - else - { - /* Unsupported sample rate (should not be possible) */ - return NETEQ_OTHER_ERROR; - } - - /* Do overlap add between new vector and overlap */ - for (i = 0; i < inst->ExpandInst.w16_overlap; i++) - { - /* overlapVec[i] = WinMute * overlapVec[i] + WinUnMute * outData[i] */ - inst->ExpandInst.pw16_overlapVec[i] = (int16_t) WEBRTC_SPL_RSHIFT_W32( - WEBRTC_SPL_MUL_16_16( - inst->ExpandInst.pw16_overlapVec[i], w16_winMute) + - WEBRTC_SPL_MUL_16_16(pw16_outData[i], w16_winUnMute) - + 16384, 15); /* shift with proper rounding */ - - w16_winMute += w16_winMuteInc; /* decrease mute factor (inc<0) */ - w16_winUnMute += w16_winUnMuteInc; /* increase unmute factor (inc>0) */ - - } - - /* - * Shift the contents of the outData buffer by overlap samples, since we - * already used these first samples in the overlapVec above - */ - - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_outData+inst->ExpandInst.w16_overlap, len); - - } - else - { - /* This is a subsequent CNG call; no special overlap needed */ - - /* Generate len samples */ - if (WebRtcCng_Generate(inst->CNG_Codec_inst, pw16_outData, (int16_t) len, 0) < 0) - { - /* error returned */ - return -WebRtcCng_GetErrorCodeDec(inst->CNG_Codec_inst); - } - } - - return 0; - -} - -#endif /* NETEQ_CNG_CODEC */ - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/codec_db.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/codec_db.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/codec_db.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/codec_db.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,782 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Implementation of the codec database. - */ - -#include "codec_db.h" - -#include /* to define NULL */ - -#include "signal_processing_library.h" - -#include "neteq_error_codes.h" - -/* - * Resets the codec database. - */ - -int WebRtcNetEQ_DbReset(CodecDbInst_t *inst) -{ - int i; - - WebRtcSpl_MemSetW16((int16_t*) inst, 0, - sizeof(CodecDbInst_t) / sizeof(int16_t)); - - for (i = 0; i < NUM_TOTAL_CODECS; i++) - { - inst->position[i] = -1; - } - - for (i = 0; i < NUM_CODECS; i++) - { - inst->payloadType[i] = -1; - } - - for (i = 0; i < NUM_CNG_CODECS; i++) - { - inst->CNGpayloadType[i] = -1; - } - - return 0; -} - -/* - * Adds a new codec to the database. - */ - -int WebRtcNetEQ_DbAdd(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codec, - int16_t payloadType, FuncDecode funcDecode, - FuncDecode funcDecodeRCU, FuncDecodePLC funcDecodePLC, - FuncDecodeInit funcDecodeInit, FuncAddLatePkt funcAddLatePkt, - FuncGetMDinfo funcGetMDinfo, FuncGetPitchInfo funcGetPitch, - FuncUpdBWEst funcUpdBWEst, FuncDurationEst funcDurationEst, - FuncGetErrorCode funcGetErrorCode, void* codec_state, - uint16_t codec_fs) -{ - - int temp; - int insertCNGcodec = 0, overwriteCNGcodec = 0, CNGpos = -1; - -#ifndef NETEQ_RED_CODEC - if (codec == kDecoderRED) - { - return CODEC_DB_UNSUPPORTED_CODEC; - } -#endif - if (((int) codec <= (int) kDecoderReservedStart) || ((int) codec - >= (int) kDecoderReservedEnd)) - { - return CODEC_DB_UNSUPPORTED_CODEC; - } - - if ((codec_fs != 8000) -#ifdef NETEQ_WIDEBAND - &&(codec_fs!=16000) -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - &&(codec_fs!=32000) -#endif -#if defined(NETEQ_48KHZ_WIDEBAND) || defined(NETEQ_OPUS_CODEC) - &&(codec_fs!=48000) -#endif - ) - { - return CODEC_DB_UNSUPPORTED_FS; - } - - /* Ensure that the codec type is supported */ - switch (codec) - { -#ifdef NETEQ_PCM16B_CODEC - case kDecoderPCM16B : - case kDecoderPCM16B_2ch : -#endif -#ifdef NETEQ_G711_CODEC - case kDecoderPCMu : - case kDecoderPCMa : - case kDecoderPCMu_2ch : - case kDecoderPCMa_2ch : -#endif -#ifdef NETEQ_ILBC_CODEC - case kDecoderILBC : -#endif -#ifdef NETEQ_ISAC_CODEC - case kDecoderISAC : -#endif -#ifdef NETEQ_ISAC_SWB_CODEC - case kDecoderISACswb : -#endif -#ifdef NETEQ_ISAC_FB_CODEC - case kDecoderISACfb : -#endif -#ifdef NETEQ_OPUS_CODEC - case kDecoderOpus : -#endif -#ifdef NETEQ_G722_CODEC - case kDecoderG722 : - case kDecoderG722_2ch : -#endif -#ifdef NETEQ_WIDEBAND - case kDecoderPCM16Bwb : - case kDecoderPCM16Bwb_2ch : -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - case kDecoderPCM16Bswb32kHz : - case kDecoderPCM16Bswb32kHz_2ch : -#endif -#ifdef NETEQ_CNG_CODEC - case kDecoderCNG : -#endif -#ifdef NETEQ_ATEVENT_DECODE - case kDecoderAVT : -#endif -#ifdef NETEQ_RED_CODEC - case kDecoderRED : -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - case kDecoderPCM16Bswb48kHz : -#endif -#ifdef NETEQ_ARBITRARY_CODEC - case kDecoderArbitrary: -#endif -#ifdef NETEQ_G729_CODEC - case kDecoderG729: -#endif -#ifdef NETEQ_G729_1_CODEC - case kDecoderG729_1 : -#endif -#ifdef NETEQ_G726_CODEC - case kDecoderG726_16 : - case kDecoderG726_24 : - case kDecoderG726_32 : - case kDecoderG726_40 : -#endif -#ifdef NETEQ_G722_1_CODEC - case kDecoderG722_1_16 : - case kDecoderG722_1_24 : - case kDecoderG722_1_32 : -#endif -#ifdef NETEQ_G722_1C_CODEC - case kDecoderG722_1C_24 : - case kDecoderG722_1C_32 : - case kDecoderG722_1C_48 : -#endif -#ifdef NETEQ_SPEEX_CODEC - case kDecoderSPEEX_8 : - case kDecoderSPEEX_16 : -#endif -#ifdef NETEQ_CELT_CODEC - case kDecoderCELT_32 : - case kDecoderCELT_32_2ch : -#endif -#ifdef NETEQ_GSMFR_CODEC - case kDecoderGSMFR : -#endif -#ifdef NETEQ_AMR_CODEC - case kDecoderAMR : -#endif -#ifdef NETEQ_AMRWB_CODEC - case kDecoderAMRWB : -#endif - { - /* If we end up here, the inserted codec is supported => Do nothing */ - break; - } - default: - { - /* If we get to this point, the inserted codec is not supported */ - return CODEC_DB_UNSUPPORTED_CODEC; - } - } - - /* Check to see if payload type is taken */ - if (WebRtcNetEQ_DbGetCodec(inst, payloadType) > 0) - { - return CODEC_DB_PAYLOAD_TAKEN; - } - - /* Special case for CNG codecs */ - if (codec == kDecoderCNG) - { - /* check if this is first CNG codec to be registered */ - if (WebRtcNetEQ_DbGetPayload(inst, codec) == CODEC_DB_NOT_EXIST2) - { - /* no other CNG codec found */ - insertCNGcodec = 1; - } - - /* find the appropriate insert position in CNG payload vector */ - switch (codec_fs) - { - case 8000: - CNGpos = 0; - /* - * The 8 kHz CNG payload type is the one associated with the regular codec DB - * should override any other setting. - * Overwrite if this isn't the first CNG - */ - overwriteCNGcodec = !insertCNGcodec; - break; -#ifdef NETEQ_WIDEBAND - case 16000: - CNGpos = 1; - break; -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - case 32000: - CNGpos = 2; - break; -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - case 48000: - CNGpos = 3; - break; -#endif - default: - /* If we get to this point, the inserted codec is not supported */ - return CODEC_DB_UNSUPPORTED_CODEC; - } - - /* insert CNG payload type */ - inst->CNGpayloadType[CNGpos] = payloadType; - - } - - if ((codec != kDecoderCNG) || (insertCNGcodec == 1) || (overwriteCNGcodec == 1)) - { - /* Check if we have reached the maximum numbers of simultaneous codecs */ - if (inst->nrOfCodecs == NUM_CODECS) return CODEC_DB_FULL; - - /* Check that codec has not already been initialized to DB => - remove it and reinitialize according to new spec */ - if ((inst->position[codec] != -1) && (overwriteCNGcodec != 1)) - { /* if registering multiple CNG codecs, don't remove, just overwrite */ - WebRtcNetEQ_DbRemove(inst, codec); - } - - if (overwriteCNGcodec == 1) - { - temp = inst->position[codec]; - } - else - { - temp = inst->nrOfCodecs; /* Store this codecs position */ - inst->position[codec] = temp; - inst->nrOfCodecs++; - } - - inst->payloadType[temp] = payloadType; - - /* Copy to database */ - inst->codec_state[temp] = codec_state; - inst->funcDecode[temp] = funcDecode; - inst->funcDecodeRCU[temp] = funcDecodeRCU; - inst->funcAddLatePkt[temp] = funcAddLatePkt; - inst->funcDecodeInit[temp] = funcDecodeInit; - inst->funcDecodePLC[temp] = funcDecodePLC; - inst->funcGetMDinfo[temp] = funcGetMDinfo; - inst->funcGetPitch[temp] = funcGetPitch; - inst->funcUpdBWEst[temp] = funcUpdBWEst; - inst->funcDurationEst[temp] = funcDurationEst; - inst->funcGetErrorCode[temp] = funcGetErrorCode; - inst->codec_fs[temp] = codec_fs; - - } - - return 0; -} - -/* - * Removes a codec from the database. - */ - -int WebRtcNetEQ_DbRemove(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codec) -{ - int i; - int pos = -1; - -#ifndef NETEQ_RED_CODEC - if (codec == kDecoderRED) - { - return CODEC_DB_UNSUPPORTED_CODEC; - } -#endif - if (((int) codec <= (int) kDecoderReservedStart) || ((int) codec - >= (int) kDecoderReservedEnd)) - { - return CODEC_DB_UNSUPPORTED_CODEC; - } - - pos = inst->position[codec]; - if (pos == -1) - { - return CODEC_DB_NOT_EXIST4; - } - else - { - /* Remove this codec */ - inst->position[codec] = -1; - for (i = pos; i < (inst->nrOfCodecs - 1); i++) - { - inst->payloadType[i] = inst->payloadType[i + 1]; - inst->codec_state[i] = inst->codec_state[i + 1]; - inst->funcDecode[i] = inst->funcDecode[i + 1]; - inst->funcDecodeRCU[i] = inst->funcDecodeRCU[i + 1]; - inst->funcAddLatePkt[i] = inst->funcAddLatePkt[i + 1]; - inst->funcDecodeInit[i] = inst->funcDecodeInit[i + 1]; - inst->funcDecodePLC[i] = inst->funcDecodePLC[i + 1]; - inst->funcGetMDinfo[i] = inst->funcGetMDinfo[i + 1]; - inst->funcGetPitch[i] = inst->funcGetPitch[i + 1]; - inst->funcDurationEst[i] = inst->funcDurationEst[i + 1]; - inst->funcUpdBWEst[i] = inst->funcUpdBWEst[i + 1]; - inst->funcGetErrorCode[i] = inst->funcGetErrorCode[i + 1]; - inst->codec_fs[i] = inst->codec_fs[i + 1]; - } - inst->payloadType[i] = -1; - inst->codec_state[i] = NULL; - inst->funcDecode[i] = NULL; - inst->funcDecodeRCU[i] = NULL; - inst->funcAddLatePkt[i] = NULL; - inst->funcDecodeInit[i] = NULL; - inst->funcDecodePLC[i] = NULL; - inst->funcGetMDinfo[i] = NULL; - inst->funcGetPitch[i] = NULL; - inst->funcDurationEst[i] = NULL; - inst->funcUpdBWEst[i] = NULL; - inst->funcGetErrorCode[i] = NULL; - inst->codec_fs[i] = 0; - /* Move down all the codecs above this one */ - for (i = 0; i < NUM_TOTAL_CODECS; i++) - { - if (inst->position[i] >= pos) - { - inst->position[i] = inst->position[i] - 1; - } - } - inst->nrOfCodecs--; - - if (codec == kDecoderCNG) - { - /* also remove all registered CNG payload types */ - for (i = 0; i < NUM_CNG_CODECS; i++) - { - inst->CNGpayloadType[i] = -1; - } - } - } - return 0; -} - -/* - * Get the decoder function pointers for a codec. - */ - -int WebRtcNetEQ_DbGetPtrs(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codec, - CodecFuncInst_t *ptr_inst) -{ - - int pos = inst->position[codec]; - if ((codec <= kDecoderReservedStart) || (codec >= kDecoderReservedEnd) || (codec - > NUM_TOTAL_CODECS)) - { - /* ERROR */ - pos = -1; - } - if (pos >= 0) - { - ptr_inst->codec_state = inst->codec_state[pos]; - ptr_inst->funcAddLatePkt = inst->funcAddLatePkt[pos]; - ptr_inst->funcDecode = inst->funcDecode[pos]; - ptr_inst->funcDecodeRCU = inst->funcDecodeRCU[pos]; - ptr_inst->funcDecodeInit = inst->funcDecodeInit[pos]; - ptr_inst->funcDecodePLC = inst->funcDecodePLC[pos]; - ptr_inst->funcGetMDinfo = inst->funcGetMDinfo[pos]; - ptr_inst->funcUpdBWEst = inst->funcUpdBWEst[pos]; - ptr_inst->funcGetErrorCode = inst->funcGetErrorCode[pos]; - ptr_inst->codec_fs = inst->codec_fs[pos]; - return 0; - } - else - { - WebRtcSpl_MemSetW16((int16_t*) ptr_inst, 0, - sizeof(CodecFuncInst_t) / sizeof(int16_t)); - return CODEC_DB_NOT_EXIST1; - } -} - -/* - * Returns payload number given a codec identifier. - */ - -int WebRtcNetEQ_DbGetPayload(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codecID) -{ - if (inst->position[codecID] == -1) - return CODEC_DB_NOT_EXIST2; - else - return (inst->payloadType[inst->position[codecID]]); - -} - -/* - * Returns codec identifier given a payload number. - * Returns -1 if the payload type does not exist. - */ - -int WebRtcNetEQ_DbGetCodec(const CodecDbInst_t *inst, int payloadType) -{ - int i, pos; - - for (i = 0; i < NUM_TOTAL_CODECS; i++) - { - pos = inst->position[i]; - if (pos != -1) - { - if (inst->payloadType[pos] == payloadType) return i; - } - } - - /* did not find payload type */ - /* check if it's a CNG codec */ - if (WebRtcNetEQ_DbIsCNGPayload(inst, payloadType)) - { - return kDecoderCNG; - } - - /* found no match */ - return CODEC_DB_NOT_EXIST3; -} - -/* - * Extracts the Payload Split information of the codec with the specified payloadType. - */ - -int WebRtcNetEQ_DbGetSplitInfo(SplitInfo_t *inst, enum WebRtcNetEQDecoder codecID, - int codedsize) -{ - - switch (codecID) - { -#ifdef NETEQ_ISAC_CODEC - case kDecoderISAC: -#endif -#ifdef NETEQ_ISAC_SWB_CODEC - case kDecoderISACswb: -#endif -#ifdef NETEQ_ISAC_FB_CODEC - case kDecoderISACfb: -#endif -#ifdef NETEQ_OPUS_CODEC - case kDecoderOpus: -#endif -#ifdef NETEQ_ARBITRARY_CODEC - case kDecoderArbitrary: -#endif -#ifdef NETEQ_AMR_CODEC - case kDecoderAMR: -#endif -#ifdef NETEQ_AMRWB_CODEC - case kDecoderAMRWB: -#endif -#ifdef NETEQ_G726_CODEC - /* Treat G726 as non-splittable to simplify the implementation */ - case kDecoderG726_16: - case kDecoderG726_24: - case kDecoderG726_32: - case kDecoderG726_40: -#endif -#ifdef NETEQ_SPEEX_CODEC - case kDecoderSPEEX_8: - case kDecoderSPEEX_16: -#endif -#ifdef NETEQ_CELT_CODEC - case kDecoderCELT_32 : - case kDecoderCELT_32_2ch : -#endif -#ifdef NETEQ_G729_1_CODEC - case kDecoderG729_1: -#endif - { - /* These codecs' payloads are not splittable */ - inst->deltaBytes = NO_SPLIT; - return 0; - } - - /* - * Sample based coders are a special case. - * In this case, deltaTime signals the number of bytes per timestamp unit times 2 - * in log2 domain. - */ -#if (defined NETEQ_G711_CODEC) - case kDecoderPCMu: - case kDecoderPCMa: - case kDecoderPCMu_2ch: - case kDecoderPCMa_2ch: - { - inst->deltaBytes = -12; - inst->deltaTime = 1; - return 0; - } -#endif -#if (defined NETEQ_G722_CODEC) - case kDecoderG722: - case kDecoderG722_2ch: - { - inst->deltaBytes = -14; - inst->deltaTime = 0; - return 0; - } -#endif -#if (defined NETEQ_PCM16B_CODEC) - case kDecoderPCM16B: - case kDecoderPCM16B_2ch: - { - inst->deltaBytes = -12; - inst->deltaTime = 2; - return 0; - } -#endif -#if ((defined NETEQ_PCM16B_CODEC)&&(defined NETEQ_WIDEBAND)) - case kDecoderPCM16Bwb: - case kDecoderPCM16Bwb_2ch: - { - inst->deltaBytes = -14; - inst->deltaTime = 2; - return 0; - } -#endif -#if ((defined NETEQ_PCM16B_CODEC)&&(defined NETEQ_32KHZ_WIDEBAND)) - case kDecoderPCM16Bswb32kHz: - case kDecoderPCM16Bswb32kHz_2ch: - { - inst->deltaBytes = -18; - inst->deltaTime = 2; - return 0; - } -#endif -#if ((defined NETEQ_PCM16B_CODEC)&&(defined NETEQ_48KHZ_WIDEBAND)) - case kDecoderPCM16Bswb48kHz: - { - inst->deltaBytes = -22; - inst->deltaTime = 2; - return 0; - } -#endif - - /* Splittable payloads */ -#ifdef NETEQ_G722_1_CODEC - case kDecoderG722_1_16: - { - inst->deltaBytes = 40; - inst->deltaTime = 320; - return 0; - } - case kDecoderG722_1_24: - { - inst->deltaBytes = 60; - inst->deltaTime = 320; - return 0; - } - case kDecoderG722_1_32: - { - inst->deltaBytes = 80; - inst->deltaTime = 320; - return 0; - } -#endif -#ifdef NETEQ_G722_1C_CODEC - case kDecoderG722_1C_24: - { - inst->deltaBytes = 60; - inst->deltaTime = 640; - return 0; - } - case kDecoderG722_1C_32: - { - inst->deltaBytes = 80; - inst->deltaTime = 640; - return 0; - } - case kDecoderG722_1C_48: - { - inst->deltaBytes = 120; - inst->deltaTime = 640; - return 0; - } -#endif -#ifdef NETEQ_G729_CODEC - case kDecoderG729: - { - inst->deltaBytes = 10; - inst->deltaTime = 80; - return 0; - } -#endif -#ifdef NETEQ_ILBC_CODEC - case kDecoderILBC: - { - /* Check for splitting of iLBC packets. - * If payload size is a multiple of 50 bytes it should be split into 30ms frames. - * If payload size is a multiple of 38 bytes it should be split into 20ms frames. - * Least common multiplier between 38 and 50 is 950, so the payload size must be less than - * 950 bytes in order to resolve the frames unambiguously. - * Currently max 12 frames in one bundle. - */ - switch (codedsize) - { - case 50: - case 100: - case 150: - case 200: - case 250: - case 300: - case 350: - case 400: - case 450: - case 500: - case 550: - case 600: - { - inst->deltaBytes = 50; - inst->deltaTime = 240; - break; - } - case 38: - case 76: - case 114: - case 152: - case 190: - case 228: - case 266: - case 304: - case 342: - case 380: - case 418: - case 456: - { - inst->deltaBytes = 38; - inst->deltaTime = 160; - break; - } - default: - { - return AMBIGUOUS_ILBC_FRAME_SIZE; /* Something not supported... */ - } - } - return 0; - } -#endif -#ifdef NETEQ_GSMFR_CODEC - case kDecoderGSMFR: - { - inst->deltaBytes = 33; - inst->deltaTime = 160; - return 0; - } -#endif - default: - { /*Unknown codec */ - inst->deltaBytes = NO_SPLIT; - return CODEC_DB_UNKNOWN_CODEC; - } - } /* end of switch */ -} - -/* - * Returns 1 if codec is multiple description, 0 otherwise. - * NOTE: This function is a stub, since there currently are no MD codecs. - */ -int WebRtcNetEQ_DbIsMDCodec(enum WebRtcNetEQDecoder codecID) -{ - if (0) /* Add test for MD codecs here */ - return 1; - else - return 0; -} - -/* - * Returns 1 if payload type is registered as a CNG codec, 0 otherwise - */ -int WebRtcNetEQ_DbIsCNGPayload(const CodecDbInst_t *inst, int payloadType) -{ -#ifdef NETEQ_CNG_CODEC - int i; - - for(i=0; iCNGpayloadType[i] != -1) && (inst->CNGpayloadType[i] == payloadType) ) - { - return 1; - } - } -#endif - - return 0; - -} - -/* - * Return the sample rate for the codec with the given payload type, 0 if error - */ -uint16_t WebRtcNetEQ_DbGetSampleRate(CodecDbInst_t *inst, int payloadType) -{ - int i; - CodecFuncInst_t codecInst; - - /* Sanity */ - if (inst == NULL) - { - /* return 0 Hz */ - return 0; - } - - /* Check among CNG payloads */ - for (i = 0; i < NUM_CNG_CODECS; i++) - { - if ((inst->CNGpayloadType[i] != -1) && (inst->CNGpayloadType[i] == payloadType)) - { - switch (i) - { -#ifdef NETEQ_WIDEBAND - case 1: - return 16000; -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - case 2: - return 32000; -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - case 3: - return 48000; -#endif - default: - return 8000; - } - } - } - - /* Not a CNG payload, check the other payloads */ - i = WebRtcNetEQ_DbGetCodec(inst, payloadType); - if (i >= 0) - { - if (WebRtcNetEQ_DbGetPtrs(inst, (enum WebRtcNetEQDecoder) i, &codecInst) != 0) - { - /* Unexpected error, return 0 Hz */ - return 0; - } - return codecInst.codec_fs; - } - - /* If we end up here, we got an error, return 0 Hz */ - return 0; - -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/codec_db_defines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/codec_db_defines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/codec_db_defines.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/codec_db_defines.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Some definitions related to the codec database. - */ - -#ifndef CODEC_DB_DEFINES_H -#define CODEC_DB_DEFINES_H - -#include "typedefs.h" - -#define NUM_CODECS 47 /* probably too large with the limited set of supported codecs*/ -#define NUM_TOTAL_CODECS kDecoderReservedEnd - -/* - * Pointer to decoder function. - */ -typedef int16_t (*FuncDecode)(void* state, int16_t* encoded, int16_t len, - int16_t* decoded, int16_t* speechType); - -/* - * Pointer to PLC function. - */ -typedef int16_t (*FuncDecodePLC)(void* state, int16_t* decodec, - int16_t frames); - -/* - * Pointer to decoder init function. - */ -typedef int16_t (*FuncDecodeInit)(void* state); - -/* - * Pointer to add late packet function. - */ -typedef int16_t - (*FuncAddLatePkt)(void* state, int16_t* encoded, int16_t len); - -/* - * Pointer to get MD infofunction. - */ -typedef int16_t (*FuncGetMDinfo)(void* state); - -/* - * Pointer to pitch info function. - * Return 0 for unvoiced, -1 if pitch not availiable. - */ -typedef int16_t (*FuncGetPitchInfo)(void* state, int16_t* encoded, - int16_t* length); - -/* - * Pointer to the update bandwidth estimate function - */ -typedef int16_t (*FuncUpdBWEst)(void* state, const uint16_t *encoded, - int32_t packet_size, - uint16_t rtp_seq_number, uint32_t send_ts, - uint32_t arr_ts); - -/* - * Pointer to the frame size estimate function. - * Returns the estimated number of samples in the packet. - */ -typedef int (*FuncDurationEst)(void* state, const uint8_t* payload, - int payload_length_bytes); - -/* - * Pointer to error code function - */ -typedef int16_t (*FuncGetErrorCode)(void* state); - -typedef struct CodecFuncInst_t_ -{ - - FuncDecode funcDecode; - FuncDecode funcDecodeRCU; - FuncDecodePLC funcDecodePLC; - FuncDecodeInit funcDecodeInit; - FuncAddLatePkt funcAddLatePkt; - FuncGetMDinfo funcGetMDinfo; - FuncUpdBWEst funcUpdBWEst; /* Currently in use for the ISAC family (without LC) only*/ - FuncDurationEst funcDurationEst; - FuncGetErrorCode funcGetErrorCode; - void * codec_state; - uint16_t codec_fs; - uint32_t timeStamp; - -} CodecFuncInst_t; - -#endif - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/codec_db.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/codec_db.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/codec_db.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/codec_db.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Interface for the codec database. - */ - -#ifndef CODEC_DB_H -#define CODEC_DB_H - -#include "typedefs.h" - -#include "webrtc_neteq.h" -#include "codec_db_defines.h" -#include "neteq_defines.h" - -#if defined(NETEQ_48KHZ_WIDEBAND) - #define NUM_CNG_CODECS 4 -#elif defined(NETEQ_32KHZ_WIDEBAND) - #define NUM_CNG_CODECS 3 -#elif defined(NETEQ_WIDEBAND) - #define NUM_CNG_CODECS 2 -#else - #define NUM_CNG_CODECS 1 -#endif - -typedef struct -{ - - int16_t position[NUM_TOTAL_CODECS]; - int16_t nrOfCodecs; - - int16_t payloadType[NUM_CODECS]; - FuncDecode funcDecode[NUM_CODECS]; - FuncDecode funcDecodeRCU[NUM_CODECS]; - FuncDecodePLC funcDecodePLC[NUM_CODECS]; - FuncDecodeInit funcDecodeInit[NUM_CODECS]; - FuncAddLatePkt funcAddLatePkt[NUM_CODECS]; - FuncGetMDinfo funcGetMDinfo[NUM_CODECS]; - FuncGetPitchInfo funcGetPitch[NUM_CODECS]; - FuncUpdBWEst funcUpdBWEst[NUM_CODECS]; - FuncDurationEst funcDurationEst[NUM_CODECS]; - FuncGetErrorCode funcGetErrorCode[NUM_CODECS]; - void * codec_state[NUM_CODECS]; - uint16_t codec_fs[NUM_CODECS]; - int16_t CNGpayloadType[NUM_CNG_CODECS]; - -} CodecDbInst_t; - -#define NO_SPLIT -1 /* codec payload cannot be split */ - -typedef struct -{ - int16_t deltaBytes; - int16_t deltaTime; -} SplitInfo_t; - -/* - * Resets the codec database. - */ -int WebRtcNetEQ_DbReset(CodecDbInst_t *inst); - -/* - * Adds a new codec to the database. - */ -int WebRtcNetEQ_DbAdd(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codec, - int16_t payloadType, FuncDecode funcDecode, - FuncDecode funcDecodeRCU, FuncDecodePLC funcDecodePLC, - FuncDecodeInit funcDecodeInit, FuncAddLatePkt funcAddLatePkt, - FuncGetMDinfo funcGetMDinfo, FuncGetPitchInfo funcGetPitch, - FuncUpdBWEst funcUpdBWEst, FuncDurationEst funcDurationEst, - FuncGetErrorCode funcGetErrorCode, void* codec_state, - uint16_t codec_fs); - -/* - * Removes a codec from the database. - */ -int WebRtcNetEQ_DbRemove(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codec); - -/* - * Get the decoder function pointers for a codec. - */ -int WebRtcNetEQ_DbGetPtrs(CodecDbInst_t *inst, enum WebRtcNetEQDecoder, - CodecFuncInst_t *ptr_inst); - -/* - * Returns payload number given a codec identifier. - */ - -int WebRtcNetEQ_DbGetPayload(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codecID); - -/* - * Returns codec identifier given a payload number. - */ - -int WebRtcNetEQ_DbGetCodec(const CodecDbInst_t *inst, int payloadType); - -/* - * Extracts the Payload Split information of the codec with the specified payloadType. - */ - -int WebRtcNetEQ_DbGetSplitInfo(SplitInfo_t *inst, enum WebRtcNetEQDecoder codecID, - int codedsize); - -/* - * Returns 1 if codec is multiple description type, 0 otherwise. - */ -int WebRtcNetEQ_DbIsMDCodec(enum WebRtcNetEQDecoder codecID); - -/* - * Returns 1 if payload type is registered as a CNG codec, 0 otherwise. - */ -int WebRtcNetEQ_DbIsCNGPayload(const CodecDbInst_t *inst, int payloadType); - -/* - * Return the sample rate for the codec with the given payload type, 0 if error. - */ -uint16_t WebRtcNetEQ_DbGetSampleRate(CodecDbInst_t *inst, int payloadType); - -#endif - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/comfort_noise.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/comfort_noise.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/comfort_noise.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/comfort_noise.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/comfort_noise.h" + +#include + +#include "webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h" +#include "webrtc/modules/audio_coding/neteq/decoder_database.h" +#include "webrtc/modules/audio_coding/neteq/dsp_helper.h" +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" +#include "webrtc/modules/audio_coding/neteq/sync_buffer.h" + +namespace webrtc { + +void ComfortNoise::Reset() { + first_call_ = true; + internal_error_code_ = 0; +} + +int ComfortNoise::UpdateParameters(Packet* packet) { + assert(packet); // Existence is verified by caller. + // Get comfort noise decoder. + AudioDecoder* cng_decoder = decoder_database_->GetDecoder( + packet->header.payloadType); + if (!cng_decoder) { + delete [] packet->payload; + delete packet; + return kUnknownPayloadType; + } + decoder_database_->SetActiveCngDecoder(packet->header.payloadType); + CNG_dec_inst* cng_inst = cng_decoder->CngDecoderInstance(); + int16_t ret = WebRtcCng_UpdateSid(cng_inst, + packet->payload, + packet->payload_length); + delete [] packet->payload; + delete packet; + if (ret < 0) { + internal_error_code_ = WebRtcCng_GetErrorCodeDec(cng_inst); + return kInternalError; + } + return kOK; +} + +int ComfortNoise::Generate(size_t requested_length, + AudioMultiVector* output) { + // TODO(hlundin): Change to an enumerator and skip assert. + assert(fs_hz_ == 8000 || fs_hz_ == 16000 || fs_hz_ == 32000 || + fs_hz_ == 48000); + // Not adapted for multi-channel yet. + if (output->Channels() != 1) { + return kMultiChannelNotSupported; + } + + size_t number_of_samples = requested_length; + int16_t new_period = 0; + if (first_call_) { + // Generate noise and overlap slightly with old data. + number_of_samples = requested_length + overlap_length_; + new_period = 1; + } + output->AssertSize(number_of_samples); + // Get the decoder from the database. + AudioDecoder* cng_decoder = decoder_database_->GetActiveCngDecoder(); + if (!cng_decoder) { + return kUnknownPayloadType; + } + CNG_dec_inst* cng_inst = cng_decoder->CngDecoderInstance(); + // The expression &(*output)[0][0] is a pointer to the first element in + // the first channel. + if (WebRtcCng_Generate(cng_inst, &(*output)[0][0], + static_cast(number_of_samples), + new_period) < 0) { + // Error returned. + output->Zeros(requested_length); + internal_error_code_ = WebRtcCng_GetErrorCodeDec(cng_inst); + return kInternalError; + } + + if (first_call_) { + // Set tapering window parameters. Values are in Q15. + int16_t muting_window; // Mixing factor for overlap data. + int16_t muting_window_increment; // Mixing factor increment (negative). + int16_t unmuting_window; // Mixing factor for comfort noise. + int16_t unmuting_window_increment; // Mixing factor increment. + if (fs_hz_ == 8000) { + muting_window = DspHelper::kMuteFactorStart8kHz; + muting_window_increment = DspHelper::kMuteFactorIncrement8kHz; + unmuting_window = DspHelper::kUnmuteFactorStart8kHz; + unmuting_window_increment = DspHelper::kUnmuteFactorIncrement8kHz; + } else if (fs_hz_ == 16000) { + muting_window = DspHelper::kMuteFactorStart16kHz; + muting_window_increment = DspHelper::kMuteFactorIncrement16kHz; + unmuting_window = DspHelper::kUnmuteFactorStart16kHz; + unmuting_window_increment = DspHelper::kUnmuteFactorIncrement16kHz; + } else if (fs_hz_ == 32000) { + muting_window = DspHelper::kMuteFactorStart32kHz; + muting_window_increment = DspHelper::kMuteFactorIncrement32kHz; + unmuting_window = DspHelper::kUnmuteFactorStart32kHz; + unmuting_window_increment = DspHelper::kUnmuteFactorIncrement32kHz; + } else { // fs_hz_ == 48000 + muting_window = DspHelper::kMuteFactorStart48kHz; + muting_window_increment = DspHelper::kMuteFactorIncrement48kHz; + unmuting_window = DspHelper::kUnmuteFactorStart48kHz; + unmuting_window_increment = DspHelper::kUnmuteFactorIncrement48kHz; + } + + // Do overlap-add between new vector and overlap. + size_t start_ix = sync_buffer_->Size() - overlap_length_; + for (size_t i = 0; i < overlap_length_; i++) { + /* overlapVec[i] = WinMute * overlapVec[i] + WinUnMute * outData[i] */ + // The expression (*output)[0][i] is the i-th element in the first + // channel. + (*sync_buffer_)[0][start_ix + i] = + (((*sync_buffer_)[0][start_ix + i] * muting_window) + + ((*output)[0][i] * unmuting_window) + 16384) >> 15; + muting_window += muting_window_increment; + unmuting_window += unmuting_window_increment; + } + // Remove |overlap_length_| samples from the front of |output| since they + // were mixed into |sync_buffer_| above. + output->PopFront(overlap_length_); + } + first_call_ = false; + return kOK; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/comfort_noise.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/comfort_noise.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/comfort_noise.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/comfort_noise.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_COMFORT_NOISE_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_COMFORT_NOISE_H_ + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Forward declarations. +class DecoderDatabase; +class SyncBuffer; +struct Packet; + +// This class acts as an interface to the CNG generator. +class ComfortNoise { + public: + enum ReturnCodes { + kOK = 0, + kUnknownPayloadType, + kInternalError, + kMultiChannelNotSupported + }; + + ComfortNoise(int fs_hz, DecoderDatabase* decoder_database, + SyncBuffer* sync_buffer) + : fs_hz_(fs_hz), + first_call_(true), + overlap_length_(5 * fs_hz_ / 8000), + decoder_database_(decoder_database), + sync_buffer_(sync_buffer), + internal_error_code_(0) { + } + + // Resets the state. Should be called before each new comfort noise period. + void Reset(); + + // Update the comfort noise generator with the parameters in |packet|. + // Will delete the packet. + int UpdateParameters(Packet* packet); + + // Generates |requested_length| samples of comfort noise and writes to + // |output|. If this is the first in call after Reset (or first after creating + // the object), it will also mix in comfort noise at the end of the + // SyncBuffer object provided in the constructor. + int Generate(size_t requested_length, AudioMultiVector* output); + + // Returns the last error code that was produced by the comfort noise + // decoder. Returns 0 if no error has been encountered since the last reset. + int internal_error_code() { return internal_error_code_; } + + private: + int fs_hz_; + bool first_call_; + size_t overlap_length_; + DecoderDatabase* decoder_database_; + SyncBuffer* sync_buffer_; + int internal_error_code_; + DISALLOW_COPY_AND_ASSIGN(ComfortNoise); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_COMFORT_NOISE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/comfort_noise_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/comfort_noise_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/comfort_noise_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/comfort_noise_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for ComfortNoise class. + +#include "webrtc/modules/audio_coding/neteq/comfort_noise.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/neteq/mock/mock_decoder_database.h" +#include "webrtc/modules/audio_coding/neteq/sync_buffer.h" + +namespace webrtc { + +TEST(ComfortNoise, CreateAndDestroy) { + int fs = 8000; + MockDecoderDatabase db; + SyncBuffer sync_buffer(1, 1000); + ComfortNoise cn(fs, &db, &sync_buffer); + EXPECT_CALL(db, Die()); // Called when |db| goes out of scope. +} + +// TODO(hlundin): Write more tests. + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/correlator.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/correlator.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/correlator.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/correlator.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "dsp.h" - -#include "signal_processing_library.h" - -#include "dsp_helpfunctions.h" - -/* Scratch usage: - - Type Name size startpos endpos - int16_t pw16_corrVec 62 0 61 - int16_t pw16_data_ds 124 0 123 - int32_t pw32_corr 2*54 124 231 - - Total: 232 - */ - -#define SCRATCH_pw16_corrVec 0 -#define SCRATCH_pw16_data_ds 0 -#define SCRATCH_pw32_corr 124 - -#define NETEQ_CORRELATOR_DSVECLEN 124 /* 124 = 60 + 10 + 54 */ - -int16_t WebRtcNetEQ_Correlator(DSPInst_t *inst, -#ifdef SCRATCH - int16_t *pw16_scratchPtr, -#endif - int16_t *pw16_data, - int16_t w16_dataLen, - int16_t *pw16_corrOut, - int16_t *pw16_corrScale) -{ - int16_t w16_corrLen = 60; -#ifdef SCRATCH - int16_t *pw16_data_ds = pw16_scratchPtr + SCRATCH_pw16_corrVec; - int32_t *pw32_corr = (int32_t*) (pw16_scratchPtr + SCRATCH_pw32_corr); - /* int16_t *pw16_corrVec = pw16_scratchPtr + SCRATCH_pw16_corrVec;*/ -#else - int16_t pw16_data_ds[NETEQ_CORRELATOR_DSVECLEN]; - int32_t pw32_corr[54]; - /* int16_t pw16_corrVec[4+54+4];*/ -#endif - /* int16_t *pw16_corr=&pw16_corrVec[4];*/ - int16_t w16_maxVal; - int32_t w32_maxVal; - int16_t w16_normVal; - int16_t w16_normVal2; - /* int16_t w16_corrUpsLen;*/ - int16_t *pw16_B = NULL; - int16_t w16_Blen = 0; - int16_t w16_factor = 0; - - /* Set constants depending on frequency used */ - if (inst->fs == 8000) - { - w16_Blen = 3; - w16_factor = 2; - pw16_B = (int16_t*) WebRtcNetEQ_kDownsample8kHzTbl; -#ifdef NETEQ_WIDEBAND - } - else if (inst->fs==16000) - { - w16_Blen = 5; - w16_factor = 4; - pw16_B = (int16_t*)WebRtcNetEQ_kDownsample16kHzTbl; -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - } - else if (inst->fs==32000) - { - w16_Blen = 7; - w16_factor = 8; - pw16_B = (int16_t*)WebRtcNetEQ_kDownsample32kHzTbl; -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - } - else /* if inst->fs==48000 */ - { - w16_Blen = 7; - w16_factor = 12; - pw16_B = (int16_t*)WebRtcNetEQ_kDownsample48kHzTbl; -#endif - } - - /* Downsample data in order to work on a 4 kHz sampled signal */ - WebRtcSpl_DownsampleFast( - pw16_data + w16_dataLen - (NETEQ_CORRELATOR_DSVECLEN * w16_factor), - (int16_t) (NETEQ_CORRELATOR_DSVECLEN * w16_factor), pw16_data_ds, - NETEQ_CORRELATOR_DSVECLEN, pw16_B, w16_Blen, w16_factor, (int16_t) 0); - - /* Normalize downsampled vector to using entire 16 bit */ - w16_maxVal = WebRtcSpl_MaxAbsValueW16(pw16_data_ds, 124); - w16_normVal = 16 - WebRtcSpl_NormW32((int32_t) w16_maxVal); - WebRtcSpl_VectorBitShiftW16(pw16_data_ds, NETEQ_CORRELATOR_DSVECLEN, pw16_data_ds, - w16_normVal); - - /* Correlate from lag 10 to lag 60 (20..120 in NB and 40..240 in WB) */ - - WebRtcNetEQ_CrossCorr( - pw32_corr, &pw16_data_ds[NETEQ_CORRELATOR_DSVECLEN - w16_corrLen], - &pw16_data_ds[NETEQ_CORRELATOR_DSVECLEN - w16_corrLen - 10], 60, 54, - 6 /*maxValue... shifts*/, -1); - - /* - * Move data from w32 to w16 vector. - * Normalize downsampled vector to using all 14 bits - */ - w32_maxVal = WebRtcSpl_MaxAbsValueW32(pw32_corr, 54); - w16_normVal2 = 18 - WebRtcSpl_NormW32(w32_maxVal); - w16_normVal2 = WEBRTC_SPL_MAX(w16_normVal2, 0); - - WebRtcSpl_VectorBitShiftW32ToW16(pw16_corrOut, 54, pw32_corr, w16_normVal2); - - /* Total scale factor (right shifts) of correlation value */ - *pw16_corrScale = 2 * w16_normVal + 6 + w16_normVal2; - - return (50 + 1); -} - -#undef SCRATCH_pw16_corrVec -#undef SCRATCH_pw16_data_ds -#undef SCRATCH_pw32_corr - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/decision_logic.h" + +#include + +#include "webrtc/modules/audio_coding/neteq/buffer_level_filter.h" +#include "webrtc/modules/audio_coding/neteq/decision_logic_fax.h" +#include "webrtc/modules/audio_coding/neteq/decision_logic_normal.h" +#include "webrtc/modules/audio_coding/neteq/delay_manager.h" +#include "webrtc/modules/audio_coding/neteq/expand.h" +#include "webrtc/modules/audio_coding/neteq/packet_buffer.h" +#include "webrtc/modules/audio_coding/neteq/sync_buffer.h" +#include "webrtc/system_wrappers/interface/logging.h" + +namespace webrtc { + +DecisionLogic* DecisionLogic::Create(int fs_hz, + int output_size_samples, + NetEqPlayoutMode playout_mode, + DecoderDatabase* decoder_database, + const PacketBuffer& packet_buffer, + DelayManager* delay_manager, + BufferLevelFilter* buffer_level_filter) { + switch (playout_mode) { + case kPlayoutOn: + case kPlayoutStreaming: + return new DecisionLogicNormal(fs_hz, + output_size_samples, + playout_mode, + decoder_database, + packet_buffer, + delay_manager, + buffer_level_filter); + case kPlayoutFax: + case kPlayoutOff: + return new DecisionLogicFax(fs_hz, + output_size_samples, + playout_mode, + decoder_database, + packet_buffer, + delay_manager, + buffer_level_filter); + } + // This line cannot be reached, but must be here to avoid compiler errors. + assert(false); + return NULL; +} + +DecisionLogic::DecisionLogic(int fs_hz, + int output_size_samples, + NetEqPlayoutMode playout_mode, + DecoderDatabase* decoder_database, + const PacketBuffer& packet_buffer, + DelayManager* delay_manager, + BufferLevelFilter* buffer_level_filter) + : decoder_database_(decoder_database), + packet_buffer_(packet_buffer), + delay_manager_(delay_manager), + buffer_level_filter_(buffer_level_filter), + cng_state_(kCngOff), + generated_noise_samples_(0), + packet_length_samples_(0), + sample_memory_(0), + prev_time_scale_(false), + timescale_hold_off_(kMinTimescaleInterval), + num_consecutive_expands_(0), + playout_mode_(playout_mode) { + delay_manager_->set_streaming_mode(playout_mode_ == kPlayoutStreaming); + SetSampleRate(fs_hz, output_size_samples); +} + +void DecisionLogic::Reset() { + cng_state_ = kCngOff; + generated_noise_samples_ = 0; + packet_length_samples_ = 0; + sample_memory_ = 0; + prev_time_scale_ = false; + timescale_hold_off_ = 0; + num_consecutive_expands_ = 0; +} + +void DecisionLogic::SoftReset() { + packet_length_samples_ = 0; + sample_memory_ = 0; + prev_time_scale_ = false; + timescale_hold_off_ = kMinTimescaleInterval; +} + +void DecisionLogic::SetSampleRate(int fs_hz, int output_size_samples) { + // TODO(hlundin): Change to an enumerator and skip assert. + assert(fs_hz == 8000 || fs_hz == 16000 || fs_hz == 32000 || fs_hz == 48000); + fs_mult_ = fs_hz / 8000; + output_size_samples_ = output_size_samples; +} + +Operations DecisionLogic::GetDecision(const SyncBuffer& sync_buffer, + const Expand& expand, + int decoder_frame_length, + const RTPHeader* packet_header, + Modes prev_mode, + bool play_dtmf, bool* reset_decoder) { + if (prev_mode == kModeRfc3389Cng || + prev_mode == kModeCodecInternalCng || + prev_mode == kModeExpand) { + // If last mode was CNG (or Expand, since this could be covering up for + // a lost CNG packet), increase the |generated_noise_samples_| counter. + generated_noise_samples_ += output_size_samples_; + // Remember that CNG is on. This is needed if comfort noise is interrupted + // by DTMF. + if (prev_mode == kModeRfc3389Cng) { + cng_state_ = kCngRfc3389On; + } else if (prev_mode == kModeCodecInternalCng) { + cng_state_ = kCngInternalOn; + } + } + + const int samples_left = static_cast( + sync_buffer.FutureLength() - expand.overlap_length()); + const int cur_size_samples = + samples_left + packet_buffer_.NumSamplesInBuffer(decoder_database_, + decoder_frame_length); + LOG(LS_VERBOSE) << "Buffers: " << packet_buffer_.NumPacketsInBuffer() << + " packets * " << decoder_frame_length << " samples/packet + " << + samples_left << " samples in sync buffer = " << cur_size_samples; + + prev_time_scale_ = prev_time_scale_ && + (prev_mode == kModeAccelerateSuccess || + prev_mode == kModeAccelerateLowEnergy || + prev_mode == kModePreemptiveExpandSuccess || + prev_mode == kModePreemptiveExpandLowEnergy); + + FilterBufferLevel(cur_size_samples, prev_mode); + + return GetDecisionSpecialized(sync_buffer, expand, decoder_frame_length, + packet_header, prev_mode, play_dtmf, + reset_decoder); +} + +void DecisionLogic::ExpandDecision(Operations operation) { + if (operation == kExpand) { + num_consecutive_expands_++; + } else { + num_consecutive_expands_ = 0; + } +} + +void DecisionLogic::FilterBufferLevel(int buffer_size_samples, + Modes prev_mode) { + const int elapsed_time_ms = output_size_samples_ / (8 * fs_mult_); + delay_manager_->UpdateCounters(elapsed_time_ms); + + // Do not update buffer history if currently playing CNG since it will bias + // the filtered buffer level. + if ((prev_mode != kModeRfc3389Cng) && (prev_mode != kModeCodecInternalCng)) { + buffer_level_filter_->SetTargetBufferLevel( + delay_manager_->base_target_level()); + + int buffer_size_packets = 0; + if (packet_length_samples_ > 0) { + // Calculate size in packets. + buffer_size_packets = buffer_size_samples / packet_length_samples_; + } + int sample_memory_local = 0; + if (prev_time_scale_) { + sample_memory_local = sample_memory_; + timescale_hold_off_ = kMinTimescaleInterval; + } + buffer_level_filter_->Update(buffer_size_packets, sample_memory_local, + packet_length_samples_); + prev_time_scale_ = false; + } + + timescale_hold_off_ = std::max(timescale_hold_off_ - 1, 0); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_fax.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_fax.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_fax.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_fax.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/decision_logic_fax.h" + +#include + +#include + +#include "webrtc/modules/audio_coding/neteq/decoder_database.h" +#include "webrtc/modules/audio_coding/neteq/sync_buffer.h" + +namespace webrtc { + +Operations DecisionLogicFax::GetDecisionSpecialized( + const SyncBuffer& sync_buffer, + const Expand& expand, + int decoder_frame_length, + const RTPHeader* packet_header, + Modes prev_mode, + bool play_dtmf, + bool* reset_decoder) { + assert(playout_mode_ == kPlayoutFax || playout_mode_ == kPlayoutOff); + uint32_t target_timestamp = sync_buffer.end_timestamp(); + uint32_t available_timestamp = 0; + int is_cng_packet = 0; + if (packet_header) { + available_timestamp = packet_header->timestamp; + is_cng_packet = + decoder_database_->IsComfortNoise(packet_header->payloadType); + } + if (is_cng_packet) { + if (static_cast((generated_noise_samples_ + target_timestamp) + - available_timestamp) >= 0) { + // Time to play this packet now. + return kRfc3389Cng; + } else { + // Wait before playing this packet. + return kRfc3389CngNoPacket; + } + } + if (!packet_header) { + // No packet. If in CNG mode, play as usual. Otherwise, use other method to + // generate data. + if (cng_state_ == kCngRfc3389On) { + // Continue playing comfort noise. + return kRfc3389CngNoPacket; + } else if (cng_state_ == kCngInternalOn) { + // Continue playing codec-internal comfort noise. + return kCodecInternalCng; + } else { + // Nothing to play. Generate some data to play out. + switch (playout_mode_) { + case kPlayoutOff: + return kAlternativePlc; + case kPlayoutFax: + return kAudioRepetition; + default: + assert(false); + return kUndefined; + } + } + } else if (target_timestamp == available_timestamp) { + return kNormal; + } else { + if (static_cast((generated_noise_samples_ + target_timestamp) + - available_timestamp) >= 0) { + return kNormal; + } else { + // If currently playing comfort noise, continue with that. Do not + // increase the timestamp counter since generated_noise_samples_ will + // be increased. + if (cng_state_ == kCngRfc3389On) { + return kRfc3389CngNoPacket; + } else if (cng_state_ == kCngInternalOn) { + return kCodecInternalCng; + } else { + // Otherwise, do packet-loss concealment and increase the + // timestamp while waiting for the time to play this packet. + switch (playout_mode_) { + case kPlayoutOff: + return kAlternativePlcIncreaseTimestamp; + case kPlayoutFax: + return kAudioRepetitionIncreaseTimestamp; + default: + assert(0); + return kUndefined; + } + } + } + } +} + + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_fax.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_fax.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_fax.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_fax.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_FAX_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_FAX_H_ + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/decision_logic.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Implementation of the DecisionLogic class for playout modes kPlayoutFax and +// kPlayoutOff. +class DecisionLogicFax : public DecisionLogic { + public: + // Constructor. + DecisionLogicFax(int fs_hz, + int output_size_samples, + NetEqPlayoutMode playout_mode, + DecoderDatabase* decoder_database, + const PacketBuffer& packet_buffer, + DelayManager* delay_manager, + BufferLevelFilter* buffer_level_filter) + : DecisionLogic(fs_hz, output_size_samples, playout_mode, + decoder_database, packet_buffer, delay_manager, + buffer_level_filter) { + } + + // Destructor. + virtual ~DecisionLogicFax() {} + + protected: + // Returns the operation that should be done next. |sync_buffer| and |expand| + // are provided for reference. |decoder_frame_length| is the number of samples + // obtained from the last decoded frame. If there is a packet available, the + // packet header should be supplied in |packet_header|; otherwise it should + // be NULL. The mode resulting form the last call to NetEqImpl::GetAudio is + // supplied in |prev_mode|. If there is a DTMF event to play, |play_dtmf| + // should be set to true. The output variable |reset_decoder| will be set to + // true if a reset is required; otherwise it is left unchanged (i.e., it can + // remain true if it was true before the call). + virtual Operations GetDecisionSpecialized(const SyncBuffer& sync_buffer, + const Expand& expand, + int decoder_frame_length, + const RTPHeader* packet_header, + Modes prev_mode, + bool play_dtmf, + bool* reset_decoder) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(DecisionLogicFax); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_FAX_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_H_ + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/defines.h" +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Forward declarations. +class BufferLevelFilter; +class DecoderDatabase; +class DelayManager; +class Expand; +class PacketBuffer; +class SyncBuffer; +struct RTPHeader; + +// This is the base class for the decision tree implementations. Derived classes +// must implement the method GetDecisionSpecialized(). +class DecisionLogic { + public: + // Static factory function which creates different types of objects depending + // on the |playout_mode|. + static DecisionLogic* Create(int fs_hz, + int output_size_samples, + NetEqPlayoutMode playout_mode, + DecoderDatabase* decoder_database, + const PacketBuffer& packet_buffer, + DelayManager* delay_manager, + BufferLevelFilter* buffer_level_filter); + + // Constructor. + DecisionLogic(int fs_hz, + int output_size_samples, + NetEqPlayoutMode playout_mode, + DecoderDatabase* decoder_database, + const PacketBuffer& packet_buffer, + DelayManager* delay_manager, + BufferLevelFilter* buffer_level_filter); + + // Destructor. + virtual ~DecisionLogic() {} + + // Resets object to a clean state. + void Reset(); + + // Resets parts of the state. Typically done when switching codecs. + void SoftReset(); + + // Sets the sample rate and the output block size. + void SetSampleRate(int fs_hz, int output_size_samples); + + // Returns the operation that should be done next. |sync_buffer| and |expand| + // are provided for reference. |decoder_frame_length| is the number of samples + // obtained from the last decoded frame. If there is a packet available, the + // packet header should be supplied in |packet_header|; otherwise it should + // be NULL. The mode resulting form the last call to NetEqImpl::GetAudio is + // supplied in |prev_mode|. If there is a DTMF event to play, |play_dtmf| + // should be set to true. The output variable |reset_decoder| will be set to + // true if a reset is required; otherwise it is left unchanged (i.e., it can + // remain true if it was true before the call). + // This method end with calling GetDecisionSpecialized to get the actual + // return value. + Operations GetDecision(const SyncBuffer& sync_buffer, + const Expand& expand, + int decoder_frame_length, + const RTPHeader* packet_header, + Modes prev_mode, + bool play_dtmf, + bool* reset_decoder); + + // These methods test the |cng_state_| for different conditions. + bool CngRfc3389On() const { return cng_state_ == kCngRfc3389On; } + bool CngOff() const { return cng_state_ == kCngOff; } + + // Resets the |cng_state_| to kCngOff. + void SetCngOff() { cng_state_ = kCngOff; } + + // Reports back to DecisionLogic whether the decision to do expand remains or + // not. Note that this is necessary, since an expand decision can be changed + // to kNormal in NetEqImpl::GetDecision if there is still enough data in the + // sync buffer. + virtual void ExpandDecision(Operations operation); + + // Adds |value| to |sample_memory_|. + void AddSampleMemory(int32_t value) { + sample_memory_ += value; + } + + // Accessors and mutators. + void set_sample_memory(int32_t value) { sample_memory_ = value; } + int generated_noise_samples() const { return generated_noise_samples_; } + void set_generated_noise_samples(int value) { + generated_noise_samples_ = value; + } + int packet_length_samples() const { return packet_length_samples_; } + void set_packet_length_samples(int value) { + packet_length_samples_ = value; + } + void set_prev_time_scale(bool value) { prev_time_scale_ = value; } + NetEqPlayoutMode playout_mode() const { return playout_mode_; } + + protected: + // The value 6 sets maximum time-stretch rate to about 100 ms/s. + static const int kMinTimescaleInterval = 6; + + enum CngState { + kCngOff, + kCngRfc3389On, + kCngInternalOn + }; + + // Returns the operation that should be done next. |sync_buffer| and |expand| + // are provided for reference. |decoder_frame_length| is the number of samples + // obtained from the last decoded frame. If there is a packet available, the + // packet header should be supplied in |packet_header|; otherwise it should + // be NULL. The mode resulting form the last call to NetEqImpl::GetAudio is + // supplied in |prev_mode|. If there is a DTMF event to play, |play_dtmf| + // should be set to true. The output variable |reset_decoder| will be set to + // true if a reset is required; otherwise it is left unchanged (i.e., it can + // remain true if it was true before the call). + // Should be implemented by derived classes. + virtual Operations GetDecisionSpecialized(const SyncBuffer& sync_buffer, + const Expand& expand, + int decoder_frame_length, + const RTPHeader* packet_header, + Modes prev_mode, + bool play_dtmf, + bool* reset_decoder) = 0; + + // Updates the |buffer_level_filter_| with the current buffer level + // |buffer_size_packets|. + void FilterBufferLevel(int buffer_size_packets, Modes prev_mode); + + DecoderDatabase* decoder_database_; + const PacketBuffer& packet_buffer_; + DelayManager* delay_manager_; + BufferLevelFilter* buffer_level_filter_; + int fs_mult_; + int output_size_samples_; + CngState cng_state_; // Remember if comfort noise is interrupted by other + // event (e.g., DTMF). + int generated_noise_samples_; + int packet_length_samples_; + int sample_memory_; + bool prev_time_scale_; + int timescale_hold_off_; + int num_consecutive_expands_; + const NetEqPlayoutMode playout_mode_; + + private: + DISALLOW_COPY_AND_ASSIGN(DecisionLogic); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_normal.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_normal.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_normal.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_normal.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/decision_logic_normal.h" + +#include + +#include + +#include "webrtc/modules/audio_coding/neteq/buffer_level_filter.h" +#include "webrtc/modules/audio_coding/neteq/decoder_database.h" +#include "webrtc/modules/audio_coding/neteq/delay_manager.h" +#include "webrtc/modules/audio_coding/neteq/expand.h" +#include "webrtc/modules/audio_coding/neteq/packet_buffer.h" +#include "webrtc/modules/audio_coding/neteq/sync_buffer.h" +#include "webrtc/modules/interface/module_common_types.h" + +namespace webrtc { + +Operations DecisionLogicNormal::GetDecisionSpecialized( + const SyncBuffer& sync_buffer, + const Expand& expand, + int decoder_frame_length, + const RTPHeader* packet_header, + Modes prev_mode, + bool play_dtmf, + bool* reset_decoder) { + assert(playout_mode_ == kPlayoutOn || playout_mode_ == kPlayoutStreaming); + // Guard for errors, to avoid getting stuck in error mode. + if (prev_mode == kModeError) { + if (!packet_header) { + return kExpand; + } else { + return kUndefined; // Use kUndefined to flag for a reset. + } + } + + uint32_t target_timestamp = sync_buffer.end_timestamp(); + uint32_t available_timestamp = 0; + bool is_cng_packet = false; + if (packet_header) { + available_timestamp = packet_header->timestamp; + is_cng_packet = + decoder_database_->IsComfortNoise(packet_header->payloadType); + } + + if (is_cng_packet) { + return CngOperation(prev_mode, target_timestamp, available_timestamp); + } + + // Handle the case with no packet at all available (except maybe DTMF). + if (!packet_header) { + return NoPacket(play_dtmf); + } + + // If the expand period was very long, reset NetEQ since it is likely that the + // sender was restarted. + if (num_consecutive_expands_ > kReinitAfterExpands) { + *reset_decoder = true; + return kNormal; + } + + const uint32_t five_seconds_samples = 5 * 8000 * fs_mult_; + // Check if the required packet is available. + if (target_timestamp == available_timestamp) { + return ExpectedPacketAvailable(prev_mode, play_dtmf); + } else if (!PacketBuffer::IsObsoleteTimestamp( + available_timestamp, target_timestamp, five_seconds_samples)) { + return FuturePacketAvailable(sync_buffer, expand, decoder_frame_length, + prev_mode, target_timestamp, + available_timestamp, play_dtmf); + } else { + // This implies that available_timestamp < target_timestamp, which can + // happen when a new stream or codec is received. Signal for a reset. + return kUndefined; + } +} + +Operations DecisionLogicNormal::CngOperation(Modes prev_mode, + uint32_t target_timestamp, + uint32_t available_timestamp) { + // Signed difference between target and available timestamp. + int32_t timestamp_diff = (generated_noise_samples_ + target_timestamp) - + available_timestamp; + int32_t optimal_level_samp = + (delay_manager_->TargetLevel() * packet_length_samples_) >> 8; + int32_t excess_waiting_time_samp = -timestamp_diff - optimal_level_samp; + + if (excess_waiting_time_samp > optimal_level_samp / 2) { + // The waiting time for this packet will be longer than 1.5 + // times the wanted buffer delay. Advance the clock to cut + // waiting time down to the optimal. + generated_noise_samples_ += excess_waiting_time_samp; + timestamp_diff += excess_waiting_time_samp; + } + + if (timestamp_diff < 0 && prev_mode == kModeRfc3389Cng) { + // Not time to play this packet yet. Wait another round before using this + // packet. Keep on playing CNG from previous CNG parameters. + return kRfc3389CngNoPacket; + } else { + // Otherwise, go for the CNG packet now. + return kRfc3389Cng; + } +} + +Operations DecisionLogicNormal::NoPacket(bool play_dtmf) { + if (cng_state_ == kCngRfc3389On) { + // Keep on playing comfort noise. + return kRfc3389CngNoPacket; + } else if (cng_state_ == kCngInternalOn) { + // Keep on playing codec internal comfort noise. + return kCodecInternalCng; + } else if (play_dtmf) { + return kDtmf; + } else { + // Nothing to play, do expand. + return kExpand; + } +} + +Operations DecisionLogicNormal::ExpectedPacketAvailable(Modes prev_mode, + bool play_dtmf) { + if (prev_mode != kModeExpand && !play_dtmf) { + // Check criterion for time-stretching. + int low_limit, high_limit; + delay_manager_->BufferLimits(&low_limit, &high_limit); + if ((buffer_level_filter_->filtered_current_level() >= high_limit && + TimescaleAllowed()) || + buffer_level_filter_->filtered_current_level() >= high_limit << 2) { + // Buffer level higher than limit and time-scaling allowed, + // or buffer level really high. + return kAccelerate; + } else if ((buffer_level_filter_->filtered_current_level() < low_limit) + && TimescaleAllowed()) { + return kPreemptiveExpand; + } + } + return kNormal; +} + +Operations DecisionLogicNormal::FuturePacketAvailable( + const SyncBuffer& sync_buffer, + const Expand& expand, + int decoder_frame_length, + Modes prev_mode, + uint32_t target_timestamp, + uint32_t available_timestamp, + bool play_dtmf) { + // Required packet is not available, but a future packet is. + // Check if we should continue with an ongoing expand because the new packet + // is too far into the future. + uint32_t timestamp_leap = available_timestamp - target_timestamp; + if ((prev_mode == kModeExpand) && + !ReinitAfterExpands(timestamp_leap) && + !MaxWaitForPacket() && + PacketTooEarly(timestamp_leap) && + UnderTargetLevel()) { + if (play_dtmf) { + // Still have DTMF to play, so do not do expand. + return kDtmf; + } else { + // Nothing to play. + return kExpand; + } + } + + const int samples_left = static_cast(sync_buffer.FutureLength() - + expand.overlap_length()); + const int cur_size_samples = samples_left + + packet_buffer_.NumPacketsInBuffer() * decoder_frame_length; + + // If previous was comfort noise, then no merge is needed. + if (prev_mode == kModeRfc3389Cng || + prev_mode == kModeCodecInternalCng) { + // Keep the same delay as before the CNG (or maximum 70 ms in buffer as + // safety precaution), but make sure that the number of samples in buffer + // is no higher than 4 times the optimal level. (Note that TargetLevel() + // is in Q8.) + int32_t timestamp_diff = (generated_noise_samples_ + target_timestamp) - + available_timestamp; + if (timestamp_diff >= 0 || + cur_size_samples > + 4 * ((delay_manager_->TargetLevel() * packet_length_samples_) >> 8)) { + // Time to play this new packet. + return kNormal; + } else { + // Too early to play this new packet; keep on playing comfort noise. + if (prev_mode == kModeRfc3389Cng) { + return kRfc3389CngNoPacket; + } else { // prevPlayMode == kModeCodecInternalCng. + return kCodecInternalCng; + } + } + } + // Do not merge unless we have done an expand before. + // (Convert kAllowMergeWithoutExpand from ms to samples by multiplying with + // fs_mult_ * 8 = fs / 1000.) + if (prev_mode == kModeExpand || + (decoder_frame_length < output_size_samples_ && + cur_size_samples > kAllowMergeWithoutExpandMs * fs_mult_ * 8)) { + return kMerge; + } else if (play_dtmf) { + // Play DTMF instead of expand. + return kDtmf; + } else { + return kExpand; + } +} + +bool DecisionLogicNormal::UnderTargetLevel() const { + return buffer_level_filter_->filtered_current_level() <= + delay_manager_->TargetLevel(); +} + +bool DecisionLogicNormal::ReinitAfterExpands(uint32_t timestamp_leap) const { + return timestamp_leap >= + static_cast(output_size_samples_ * kReinitAfterExpands); +} + +bool DecisionLogicNormal::PacketTooEarly(uint32_t timestamp_leap) const { + return timestamp_leap > + static_cast(output_size_samples_ * num_consecutive_expands_); +} + +bool DecisionLogicNormal::MaxWaitForPacket() const { + return num_consecutive_expands_ >= kMaxWaitForPacket; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_normal.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_normal.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_normal.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_normal.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_NORMAL_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_NORMAL_H_ + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/decision_logic.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Implementation of the DecisionLogic class for playout modes kPlayoutOn and +// kPlayoutStreaming. +class DecisionLogicNormal : public DecisionLogic { + public: + // Constructor. + DecisionLogicNormal(int fs_hz, + int output_size_samples, + NetEqPlayoutMode playout_mode, + DecoderDatabase* decoder_database, + const PacketBuffer& packet_buffer, + DelayManager* delay_manager, + BufferLevelFilter* buffer_level_filter) + : DecisionLogic(fs_hz, output_size_samples, playout_mode, + decoder_database, packet_buffer, delay_manager, + buffer_level_filter) { + } + + // Destructor. + virtual ~DecisionLogicNormal() {} + + protected: + static const int kAllowMergeWithoutExpandMs = 20; // 20 ms. + static const int kReinitAfterExpands = 100; + static const int kMaxWaitForPacket = 10; + + // Returns the operation that should be done next. |sync_buffer| and |expand| + // are provided for reference. |decoder_frame_length| is the number of samples + // obtained from the last decoded frame. If there is a packet available, the + // packet header should be supplied in |packet_header|; otherwise it should + // be NULL. The mode resulting form the last call to NetEqImpl::GetAudio is + // supplied in |prev_mode|. If there is a DTMF event to play, |play_dtmf| + // should be set to true. The output variable |reset_decoder| will be set to + // true if a reset is required; otherwise it is left unchanged (i.e., it can + // remain true if it was true before the call). + virtual Operations GetDecisionSpecialized(const SyncBuffer& sync_buffer, + const Expand& expand, + int decoder_frame_length, + const RTPHeader* packet_header, + Modes prev_mode, bool play_dtmf, + bool* reset_decoder); + + // Returns the operation to do given that the expected packet is not + // available, but a packet further into the future is at hand. + virtual Operations FuturePacketAvailable( + const SyncBuffer& sync_buffer, + const Expand& expand, + int decoder_frame_length, Modes prev_mode, + uint32_t target_timestamp, + uint32_t available_timestamp, + bool play_dtmf); + + // Returns the operation to do given that the expected packet is available. + virtual Operations ExpectedPacketAvailable(Modes prev_mode, bool play_dtmf); + + // Returns the operation given that no packets are available (except maybe + // a DTMF event, flagged by setting |play_dtmf| true). + virtual Operations NoPacket(bool play_dtmf); + + private: + // Returns the operation given that the next available packet is a comfort + // noise payload (RFC 3389 only, not codec-internal). + Operations CngOperation(Modes prev_mode, uint32_t target_timestamp, + uint32_t available_timestamp); + + // Checks if enough time has elapsed since the last successful timescale + // operation was done (i.e., accelerate or preemptive expand). + bool TimescaleAllowed() const { return timescale_hold_off_ == 0; } + + // Checks if the current (filtered) buffer level is under the target level. + bool UnderTargetLevel() const; + + // Checks if |timestamp_leap| is so long into the future that a reset due + // to exceeding kReinitAfterExpands will be done. + bool ReinitAfterExpands(uint32_t timestamp_leap) const; + + // Checks if we still have not done enough expands to cover the distance from + // the last decoded packet to the next available packet, the distance beeing + // conveyed in |timestamp_leap|. + bool PacketTooEarly(uint32_t timestamp_leap) const; + + // Checks if num_consecutive_expands_ >= kMaxWaitForPacket. + bool MaxWaitForPacket() const; + + DISALLOW_COPY_AND_ASSIGN(DecisionLogicNormal); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_NORMAL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decision_logic_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for DecisionLogic class and derived classes. + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/neteq/buffer_level_filter.h" +#include "webrtc/modules/audio_coding/neteq/decoder_database.h" +#include "webrtc/modules/audio_coding/neteq/decision_logic.h" +#include "webrtc/modules/audio_coding/neteq/delay_manager.h" +#include "webrtc/modules/audio_coding/neteq/delay_peak_detector.h" +#include "webrtc/modules/audio_coding/neteq/packet_buffer.h" + +namespace webrtc { + +TEST(DecisionLogic, CreateAndDestroy) { + int fs_hz = 8000; + int output_size_samples = fs_hz / 100; // Samples per 10 ms. + DecoderDatabase decoder_database; + PacketBuffer packet_buffer(10); + DelayPeakDetector delay_peak_detector; + DelayManager delay_manager(240, &delay_peak_detector); + BufferLevelFilter buffer_level_filter; + DecisionLogic* logic = DecisionLogic::Create(fs_hz, output_size_samples, + kPlayoutOn, &decoder_database, + packet_buffer, &delay_manager, + &buffer_level_filter); + delete logic; + logic = DecisionLogic::Create(fs_hz, output_size_samples, + kPlayoutStreaming, + &decoder_database, + packet_buffer, &delay_manager, + &buffer_level_filter); + delete logic; + logic = DecisionLogic::Create(fs_hz, output_size_samples, + kPlayoutFax, + &decoder_database, + packet_buffer, &delay_manager, + &buffer_level_filter); + delete logic; + logic = DecisionLogic::Create(fs_hz, output_size_samples, + kPlayoutOff, + &decoder_database, + packet_buffer, &delay_manager, + &buffer_level_filter); + delete logic; +} + +// TODO(hlundin): Write more tests. + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decoder_database.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decoder_database.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decoder_database.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decoder_database.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/decoder_database.h" + +#include +#include // pair + +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" + +namespace webrtc { + +DecoderDatabase::DecoderDatabase() + : active_decoder_(-1), active_cng_decoder_(-1) {} + +DecoderDatabase::~DecoderDatabase() {} + +DecoderDatabase::DecoderInfo::~DecoderInfo() { + if (!external) delete decoder; +} + +bool DecoderDatabase::Empty() const { return decoders_.empty(); } + +int DecoderDatabase::Size() const { return static_cast(decoders_.size()); } + +void DecoderDatabase::Reset() { + decoders_.clear(); + active_decoder_ = -1; + active_cng_decoder_ = -1; +} + +int DecoderDatabase::RegisterPayload(uint8_t rtp_payload_type, + NetEqDecoder codec_type) { + if (rtp_payload_type > kMaxRtpPayloadType) { + return kInvalidRtpPayloadType; + } + if (!AudioDecoder::CodecSupported(codec_type)) { + return kCodecNotSupported; + } + int fs_hz = AudioDecoder::CodecSampleRateHz(codec_type); + std::pair ret; + DecoderInfo info(codec_type, fs_hz, NULL, false); + ret = decoders_.insert(std::make_pair(rtp_payload_type, info)); + if (ret.second == false) { + // Database already contains a decoder with type |rtp_payload_type|. + return kDecoderExists; + } + return kOK; +} + +int DecoderDatabase::InsertExternal(uint8_t rtp_payload_type, + NetEqDecoder codec_type, + int fs_hz, + AudioDecoder* decoder) { + if (rtp_payload_type > 0x7F) { + return kInvalidRtpPayloadType; + } + if (!AudioDecoder::CodecSupported(codec_type)) { + return kCodecNotSupported; + } + if (fs_hz != 8000 && fs_hz != 16000 && fs_hz != 32000 && fs_hz != 48000) { + return kInvalidSampleRate; + } + if (!decoder) { + return kInvalidPointer; + } + decoder->Init(); + std::pair ret; + DecoderInfo info(codec_type, fs_hz, decoder, true); + ret = decoders_.insert( + std::pair(rtp_payload_type, info)); + if (ret.second == false) { + // Database already contains a decoder with type |rtp_payload_type|. + return kDecoderExists; + } + return kOK; +} + +int DecoderDatabase::Remove(uint8_t rtp_payload_type) { + if (decoders_.erase(rtp_payload_type) == 0) { + // No decoder with that |rtp_payload_type|. + return kDecoderNotFound; + } + if (active_decoder_ == rtp_payload_type) { + active_decoder_ = -1; // No active decoder. + } + if (active_cng_decoder_ == rtp_payload_type) { + active_cng_decoder_ = -1; // No active CNG decoder. + } + return kOK; +} + +const DecoderDatabase::DecoderInfo* DecoderDatabase::GetDecoderInfo( + uint8_t rtp_payload_type) const { + DecoderMap::const_iterator it = decoders_.find(rtp_payload_type); + if (it == decoders_.end()) { + // Decoder not found. + return NULL; + } + return &(*it).second; +} + +uint8_t DecoderDatabase::GetRtpPayloadType( + NetEqDecoder codec_type) const { + DecoderMap::const_iterator it; + for (it = decoders_.begin(); it != decoders_.end(); ++it) { + if ((*it).second.codec_type == codec_type) { + // Match found. + return (*it).first; + } + } + // No match. + return kRtpPayloadTypeError; +} + +AudioDecoder* DecoderDatabase::GetDecoder(uint8_t rtp_payload_type) { + if (IsDtmf(rtp_payload_type) || IsRed(rtp_payload_type)) { + // These are not real decoders. + return NULL; + } + DecoderMap::iterator it = decoders_.find(rtp_payload_type); + if (it == decoders_.end()) { + // Decoder not found. + return NULL; + } + DecoderInfo* info = &(*it).second; + if (!info->decoder) { + // Create the decoder object. + AudioDecoder* decoder = AudioDecoder::CreateAudioDecoder(info->codec_type); + assert(decoder); // Should not be able to have an unsupported codec here. + info->decoder = decoder; + info->decoder->Init(); + } + return info->decoder; +} + +bool DecoderDatabase::IsType(uint8_t rtp_payload_type, + NetEqDecoder codec_type) const { + DecoderMap::const_iterator it = decoders_.find(rtp_payload_type); + if (it == decoders_.end()) { + // Decoder not found. + return false; + } + return ((*it).second.codec_type == codec_type); +} + +bool DecoderDatabase::IsComfortNoise(uint8_t rtp_payload_type) const { + if (IsType(rtp_payload_type, kDecoderCNGnb) || + IsType(rtp_payload_type, kDecoderCNGwb) || + IsType(rtp_payload_type, kDecoderCNGswb32kHz) || + IsType(rtp_payload_type, kDecoderCNGswb48kHz)) { + return true; + } else { + return false; + } +} + +bool DecoderDatabase::IsDtmf(uint8_t rtp_payload_type) const { + return IsType(rtp_payload_type, kDecoderAVT); +} + +bool DecoderDatabase::IsRed(uint8_t rtp_payload_type) const { + return IsType(rtp_payload_type, kDecoderRED); +} + +int DecoderDatabase::SetActiveDecoder(uint8_t rtp_payload_type, + bool* new_decoder) { + // Check that |rtp_payload_type| exists in the database. + DecoderMap::const_iterator it = decoders_.find(rtp_payload_type); + if (it == decoders_.end()) { + // Decoder not found. + return kDecoderNotFound; + } + assert(new_decoder); + *new_decoder = false; + if (active_decoder_ < 0) { + // This is the first active decoder. + *new_decoder = true; + } else if (active_decoder_ != rtp_payload_type) { + // Moving from one active decoder to another. Delete the first one. + DecoderMap::iterator it = decoders_.find(active_decoder_); + if (it == decoders_.end()) { + // Decoder not found. This should not be possible. + assert(false); + return kDecoderNotFound; + } + if (!(*it).second.external) { + // Delete the AudioDecoder object, unless it is an externally created + // decoder. + delete (*it).second.decoder; + (*it).second.decoder = NULL; + } + *new_decoder = true; + } + active_decoder_ = rtp_payload_type; + return kOK; +} + +AudioDecoder* DecoderDatabase::GetActiveDecoder() { + if (active_decoder_ < 0) { + // No active decoder. + return NULL; + } + return GetDecoder(active_decoder_); +} + +int DecoderDatabase::SetActiveCngDecoder(uint8_t rtp_payload_type) { + // Check that |rtp_payload_type| exists in the database. + DecoderMap::const_iterator it = decoders_.find(rtp_payload_type); + if (it == decoders_.end()) { + // Decoder not found. + return kDecoderNotFound; + } + if (active_cng_decoder_ >= 0 && active_cng_decoder_ != rtp_payload_type) { + // Moving from one active CNG decoder to another. Delete the first one. + DecoderMap::iterator it = decoders_.find(active_cng_decoder_); + if (it == decoders_.end()) { + // Decoder not found. This should not be possible. + assert(false); + return kDecoderNotFound; + } + if (!(*it).second.external) { + // Delete the AudioDecoder object, unless it is an externally created + // decoder. + delete (*it).second.decoder; + (*it).second.decoder = NULL; + } + } + active_cng_decoder_ = rtp_payload_type; + return kOK; +} + +AudioDecoder* DecoderDatabase::GetActiveCngDecoder() { + if (active_cng_decoder_ < 0) { + // No active CNG decoder. + return NULL; + } + return GetDecoder(active_cng_decoder_); +} + +int DecoderDatabase::CheckPayloadTypes(const PacketList& packet_list) const { + PacketList::const_iterator it; + for (it = packet_list.begin(); it != packet_list.end(); ++it) { + if (decoders_.find((*it)->header.payloadType) == decoders_.end()) { + // Payload type is not found. + return kDecoderNotFound; + } + } + return kOK; +} + + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decoder_database.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decoder_database.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decoder_database.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decoder_database.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_DECODER_DATABASE_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_DECODER_DATABASE_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/common_types.h" // NULL +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" +#include "webrtc/modules/audio_coding/neteq/packet.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Forward declaration. +class AudioDecoder; + +class DecoderDatabase { + public: + enum DatabaseReturnCodes { + kOK = 0, + kInvalidRtpPayloadType = -1, + kCodecNotSupported = -2, + kInvalidSampleRate = -3, + kDecoderExists = -4, + kDecoderNotFound = -5, + kInvalidPointer = -6 + }; + + // Struct used to store decoder info in the database. + struct DecoderInfo { + // Constructors. + DecoderInfo() + : codec_type(kDecoderArbitrary), + fs_hz(8000), + decoder(NULL), + external(false) { + } + DecoderInfo(NetEqDecoder ct, int fs, AudioDecoder* dec, bool ext) + : codec_type(ct), + fs_hz(fs), + decoder(dec), + external(ext) { + } + // Destructor. (Defined in decoder_database.cc.) + ~DecoderInfo(); + + NetEqDecoder codec_type; + int fs_hz; + AudioDecoder* decoder; + bool external; + }; + + static const uint8_t kMaxRtpPayloadType = 0x7F; // Max for a 7-bit number. + // Maximum value for 8 bits, and an invalid RTP payload type (since it is + // only 7 bits). + static const uint8_t kRtpPayloadTypeError = 0xFF; + + DecoderDatabase(); + + virtual ~DecoderDatabase(); + + // Returns true if the database is empty. + virtual bool Empty() const; + + // Returns the number of decoders registered in the database. + virtual int Size() const; + + // Resets the database, erasing all registered payload types, and deleting + // any AudioDecoder objects that were not externally created and inserted + // using InsertExternal(). + virtual void Reset(); + + // Registers |rtp_payload_type| as a decoder of type |codec_type|. Returns + // kOK on success; otherwise an error code. + virtual int RegisterPayload(uint8_t rtp_payload_type, + NetEqDecoder codec_type); + + // Registers an externally created AudioDecoder object, and associates it + // as a decoder of type |codec_type| with |rtp_payload_type|. + virtual int InsertExternal(uint8_t rtp_payload_type, + NetEqDecoder codec_type, + int fs_hz, AudioDecoder* decoder); + + // Removes the entry for |rtp_payload_type| from the database. + // Returns kDecoderNotFound or kOK depending on the outcome of the operation. + virtual int Remove(uint8_t rtp_payload_type); + + // Returns a pointer to the DecoderInfo struct for |rtp_payload_type|. If + // no decoder is registered with that |rtp_payload_type|, NULL is returned. + virtual const DecoderInfo* GetDecoderInfo(uint8_t rtp_payload_type) const; + + // Returns one RTP payload type associated with |codec_type|, or + // kDecoderNotFound if no entry exists for that value. Note that one + // |codec_type| may be registered with several RTP payload types, and the + // method may return any of them. + virtual uint8_t GetRtpPayloadType(NetEqDecoder codec_type) const; + + // Returns a pointer to the AudioDecoder object associated with + // |rtp_payload_type|, or NULL if none is registered. If the AudioDecoder + // object does not exist for that decoder, the object is created. + virtual AudioDecoder* GetDecoder(uint8_t rtp_payload_type); + + // Returns true if |rtp_payload_type| is registered as a |codec_type|. + virtual bool IsType(uint8_t rtp_payload_type, + NetEqDecoder codec_type) const; + + // Returns true if |rtp_payload_type| is registered as comfort noise. + virtual bool IsComfortNoise(uint8_t rtp_payload_type) const; + + // Returns true if |rtp_payload_type| is registered as DTMF. + virtual bool IsDtmf(uint8_t rtp_payload_type) const; + + // Returns true if |rtp_payload_type| is registered as RED. + virtual bool IsRed(uint8_t rtp_payload_type) const; + + // Sets the active decoder to be |rtp_payload_type|. If this call results in a + // change of active decoder, |new_decoder| is set to true. The previous active + // decoder's AudioDecoder object is deleted. + virtual int SetActiveDecoder(uint8_t rtp_payload_type, bool* new_decoder); + + // Returns the current active decoder, or NULL if no active decoder exists. + virtual AudioDecoder* GetActiveDecoder(); + + // Sets the active comfort noise decoder to be |rtp_payload_type|. If this + // call results in a change of active comfort noise decoder, the previous + // active decoder's AudioDecoder object is deleted. + virtual int SetActiveCngDecoder(uint8_t rtp_payload_type); + + // Returns the current active comfort noise decoder, or NULL if no active + // comfort noise decoder exists. + virtual AudioDecoder* GetActiveCngDecoder(); + + // Returns kOK if all packets in |packet_list| carry payload types that are + // registered in the database. Otherwise, returns kDecoderNotFound. + virtual int CheckPayloadTypes(const PacketList& packet_list) const; + + private: + typedef std::map DecoderMap; + + DecoderMap decoders_; + int active_decoder_; + int active_cng_decoder_; + + DISALLOW_COPY_AND_ASSIGN(DecoderDatabase); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_DECODER_DATABASE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decoder_database_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decoder_database_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decoder_database_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/decoder_database_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/decoder_database.h" + +#include +#include + +#include + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +#include "webrtc/modules/audio_coding/neteq/mock/mock_audio_decoder.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace webrtc { + +TEST(DecoderDatabase, CreateAndDestroy) { + DecoderDatabase db; + EXPECT_EQ(0, db.Size()); + EXPECT_TRUE(db.Empty()); +} + +TEST(DecoderDatabase, InsertAndRemove) { + DecoderDatabase db; + const uint8_t kPayloadType = 0; + EXPECT_EQ(DecoderDatabase::kOK, + db.RegisterPayload(kPayloadType, kDecoderPCMu)); + EXPECT_EQ(1, db.Size()); + EXPECT_FALSE(db.Empty()); + EXPECT_EQ(DecoderDatabase::kOK, db.Remove(kPayloadType)); + EXPECT_EQ(0, db.Size()); + EXPECT_TRUE(db.Empty()); +} + +TEST(DecoderDatabase, GetDecoderInfo) { + DecoderDatabase db; + const uint8_t kPayloadType = 0; + EXPECT_EQ(DecoderDatabase::kOK, + db.RegisterPayload(kPayloadType, kDecoderPCMu)); + const DecoderDatabase::DecoderInfo* info; + info = db.GetDecoderInfo(kPayloadType); + ASSERT_TRUE(info != NULL); + EXPECT_EQ(kDecoderPCMu, info->codec_type); + EXPECT_EQ(NULL, info->decoder); + EXPECT_EQ(8000, info->fs_hz); + EXPECT_FALSE(info->external); + info = db.GetDecoderInfo(kPayloadType + 1); // Other payload type. + EXPECT_TRUE(info == NULL); // Should not be found. +} + +TEST(DecoderDatabase, GetRtpPayloadType) { + DecoderDatabase db; + const uint8_t kPayloadType = 0; + EXPECT_EQ(DecoderDatabase::kOK, + db.RegisterPayload(kPayloadType, kDecoderPCMu)); + EXPECT_EQ(kPayloadType, db.GetRtpPayloadType(kDecoderPCMu)); + const uint8_t expected_value = DecoderDatabase::kRtpPayloadTypeError; + EXPECT_EQ(expected_value, + db.GetRtpPayloadType(kDecoderISAC)); // iSAC is not registered. +} + +TEST(DecoderDatabase, GetDecoder) { + DecoderDatabase db; + const uint8_t kPayloadType = 0; + EXPECT_EQ(DecoderDatabase::kOK, + db.RegisterPayload(kPayloadType, kDecoderPCM16B)); + AudioDecoder* dec = db.GetDecoder(kPayloadType); + ASSERT_TRUE(dec != NULL); +} + +TEST(DecoderDatabase, TypeTests) { + DecoderDatabase db; + const uint8_t kPayloadTypePcmU = 0; + const uint8_t kPayloadTypeCng = 13; + const uint8_t kPayloadTypeDtmf = 100; + const uint8_t kPayloadTypeRed = 101; + const uint8_t kPayloadNotUsed = 102; + // Load into database. + EXPECT_EQ(DecoderDatabase::kOK, + db.RegisterPayload(kPayloadTypePcmU, kDecoderPCMu)); + EXPECT_EQ(DecoderDatabase::kOK, + db.RegisterPayload(kPayloadTypeCng, kDecoderCNGnb)); + EXPECT_EQ(DecoderDatabase::kOK, + db.RegisterPayload(kPayloadTypeDtmf, kDecoderAVT)); + EXPECT_EQ(DecoderDatabase::kOK, + db.RegisterPayload(kPayloadTypeRed, kDecoderRED)); + EXPECT_EQ(4, db.Size()); + // Test. + EXPECT_FALSE(db.IsComfortNoise(kPayloadNotUsed)); + EXPECT_FALSE(db.IsDtmf(kPayloadNotUsed)); + EXPECT_FALSE(db.IsRed(kPayloadNotUsed)); + EXPECT_FALSE(db.IsComfortNoise(kPayloadTypePcmU)); + EXPECT_FALSE(db.IsDtmf(kPayloadTypePcmU)); + EXPECT_FALSE(db.IsRed(kPayloadTypePcmU)); + EXPECT_FALSE(db.IsType(kPayloadTypePcmU, kDecoderISAC)); + EXPECT_TRUE(db.IsType(kPayloadTypePcmU, kDecoderPCMu)); + EXPECT_TRUE(db.IsComfortNoise(kPayloadTypeCng)); + EXPECT_TRUE(db.IsDtmf(kPayloadTypeDtmf)); + EXPECT_TRUE(db.IsRed(kPayloadTypeRed)); +} + +TEST(DecoderDatabase, ExternalDecoder) { + DecoderDatabase db; + const uint8_t kPayloadType = 0; + MockAudioDecoder decoder; + // Load into database. + EXPECT_EQ(DecoderDatabase::kOK, + db.InsertExternal(kPayloadType, kDecoderPCMu, 8000, + &decoder)); + EXPECT_EQ(1, db.Size()); + // Get decoder and make sure we get the external one. + EXPECT_EQ(&decoder, db.GetDecoder(kPayloadType)); + // Get the decoder info struct and check it too. + const DecoderDatabase::DecoderInfo* info; + info = db.GetDecoderInfo(kPayloadType); + ASSERT_TRUE(info != NULL); + EXPECT_EQ(kDecoderPCMu, info->codec_type); + EXPECT_EQ(&decoder, info->decoder); + EXPECT_EQ(8000, info->fs_hz); + EXPECT_TRUE(info->external); + // Expect not to delete the decoder when removing it from the database, since + // it was declared externally. + EXPECT_CALL(decoder, Die()).Times(0); + EXPECT_EQ(DecoderDatabase::kOK, db.Remove(kPayloadType)); + EXPECT_TRUE(db.Empty()); + + EXPECT_CALL(decoder, Die()).Times(1); // Will be called when |db| is deleted. +} + +TEST(DecoderDatabase, CheckPayloadTypes) { + DecoderDatabase db; + // Load a number of payloads into the database. Payload types are 0, 1, ..., + // while the decoder type is the same for all payload types (this does not + // matter for the test). + const int kNumPayloads = 10; + for (uint8_t payload_type = 0; payload_type < kNumPayloads; ++payload_type) { + EXPECT_EQ(DecoderDatabase::kOK, + db.RegisterPayload(payload_type, kDecoderArbitrary)); + } + PacketList packet_list; + for (int i = 0; i < kNumPayloads + 1; ++i) { + // Create packet with payload type |i|. The last packet will have a payload + // type that is not registered in the decoder database. + Packet* packet = new Packet; + packet->header.payloadType = i; + packet_list.push_back(packet); + } + + // Expect to return false, since the last packet is of an unknown type. + EXPECT_EQ(DecoderDatabase::kDecoderNotFound, + db.CheckPayloadTypes(packet_list)); + + delete packet_list.back(); + packet_list.pop_back(); // Remove the unknown one. + + EXPECT_EQ(DecoderDatabase::kOK, db.CheckPayloadTypes(packet_list)); + + // Delete all packets. + PacketList::iterator it = packet_list.begin(); + while (it != packet_list.end()) { + delete packet_list.front(); + it = packet_list.erase(it); + } +} + +// Test the methods for setting and getting active speech and CNG decoders. +TEST(DecoderDatabase, ActiveDecoders) { + DecoderDatabase db; + // Load payload types. + ASSERT_EQ(DecoderDatabase::kOK, db.RegisterPayload(0, kDecoderPCMu)); + ASSERT_EQ(DecoderDatabase::kOK, db.RegisterPayload(103, kDecoderISAC)); + ASSERT_EQ(DecoderDatabase::kOK, db.RegisterPayload(13, kDecoderCNGnb)); + // Verify that no decoders are active from the start. + EXPECT_EQ(NULL, db.GetActiveDecoder()); + EXPECT_EQ(NULL, db.GetActiveCngDecoder()); + + // Set active speech codec. + bool changed; // Should be true when the active decoder changed. + EXPECT_EQ(DecoderDatabase::kOK, db.SetActiveDecoder(0, &changed)); + EXPECT_TRUE(changed); + AudioDecoder* decoder = db.GetActiveDecoder(); + ASSERT_FALSE(decoder == NULL); // Should get a decoder here. + + // Set the same again. Expect no change. + EXPECT_EQ(DecoderDatabase::kOK, db.SetActiveDecoder(0, &changed)); + EXPECT_FALSE(changed); + decoder = db.GetActiveDecoder(); + ASSERT_FALSE(decoder == NULL); // Should get a decoder here. + + // Change active decoder. + EXPECT_EQ(DecoderDatabase::kOK, db.SetActiveDecoder(103, &changed)); + EXPECT_TRUE(changed); + decoder = db.GetActiveDecoder(); + ASSERT_FALSE(decoder == NULL); // Should get a decoder here. + + // Remove the active decoder, and verify that the active becomes NULL. + EXPECT_EQ(DecoderDatabase::kOK, db.Remove(103)); + EXPECT_EQ(NULL, db.GetActiveDecoder()); + + // Set active CNG codec. + EXPECT_EQ(DecoderDatabase::kOK, db.SetActiveCngDecoder(13)); + decoder = db.GetActiveCngDecoder(); + ASSERT_FALSE(decoder == NULL); // Should get a decoder here. + + // Remove the active CNG decoder, and verify that the active becomes NULL. + EXPECT_EQ(DecoderDatabase::kOK, db.Remove(13)); + EXPECT_EQ(NULL, db.GetActiveCngDecoder()); + + // Try to set non-existing codecs as active. + EXPECT_EQ(DecoderDatabase::kDecoderNotFound, + db.SetActiveDecoder(17, &changed)); + EXPECT_EQ(DecoderDatabase::kDecoderNotFound, + db.SetActiveCngDecoder(17)); +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/defines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/defines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/defines.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/defines.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_DEFINES_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_DEFINES_H_ + +namespace webrtc { + +enum Operations { + kNormal = 0, + kMerge, + kExpand, + kAccelerate, + kPreemptiveExpand, + kRfc3389Cng, + kRfc3389CngNoPacket, + kCodecInternalCng, + kDtmf, + kAlternativePlc, + kAlternativePlcIncreaseTimestamp, + kAudioRepetition, + kAudioRepetitionIncreaseTimestamp, + kUndefined = -1 +}; + +enum Modes { + kModeNormal = 0, + kModeExpand, + kModeMerge, + kModeAccelerateSuccess, + kModeAccelerateLowEnergy, + kModeAccelerateFail, + kModePreemptiveExpandSuccess, + kModePreemptiveExpandLowEnergy, + kModePreemptiveExpandFail, + kModeRfc3389Cng, + kModeCodecInternalCng, + kModeDtmf, + kModeError, + kModeUndefined = -1 +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_DEFINES_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_logging.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_logging.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_logging.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_logging.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Contains definitions for the delay logging functionality. Only used for debugging and - * tracing purposes. - */ - -#ifndef DELAY_LOGGING_H -#define DELAY_LOGGING_H - -#define NETEQ_DELAY_LOGGING_VERSION_STRING "2.0" - -#define NETEQ_DELAY_LOGGING_SIGNAL_RECIN 1 -#define NETEQ_DELAY_LOGGING_SIGNAL_FLUSH 2 -#define NETEQ_DELAY_LOGGING_SIGNAL_CLOCK 3 -#define NETEQ_DELAY_LOGGING_SIGNAL_EOF 4 -#define NETEQ_DELAY_LOGGING_SIGNAL_DECODE 5 -#define NETEQ_DELAY_LOGGING_SIGNAL_CHANGE_FS 6 -#define NETEQ_DELAY_LOGGING_SIGNAL_MERGE_INFO 7 -#define NETEQ_DELAY_LOGGING_SIGNAL_EXPAND_INFO 8 -#define NETEQ_DELAY_LOGGING_SIGNAL_ACCELERATE_INFO 9 -#define NETEQ_DELAY_LOGGING_SIGNAL_PREEMPTIVE_INFO 10 -#define NETEQ_DELAY_LOGGING_SIGNAL_OPTBUF 11 -#define NETEQ_DELAY_LOGGING_SIGNAL_DECODE_ONE_DESC 12 - -#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_manager.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_manager.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_manager.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_manager.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,425 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/delay_manager.h" + +#include +#include + +#include // max, min + +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/modules/audio_coding/neteq/delay_peak_detector.h" +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/system_wrappers/interface/logging.h" + +namespace webrtc { + +DelayManager::DelayManager(int max_packets_in_buffer, + DelayPeakDetector* peak_detector) + : first_packet_received_(false), + max_packets_in_buffer_(max_packets_in_buffer), + iat_vector_(kMaxIat + 1, 0), + iat_factor_(0), + packet_iat_count_ms_(0), + base_target_level_(4), // In Q0 domain. + target_level_(base_target_level_ << 8), // In Q8 domain. + packet_len_ms_(0), + streaming_mode_(false), + last_seq_no_(0), + last_timestamp_(0), + minimum_delay_ms_(0), + least_required_delay_ms_(target_level_), + maximum_delay_ms_(target_level_), + iat_cumulative_sum_(0), + max_iat_cumulative_sum_(0), + max_timer_ms_(0), + peak_detector_(*peak_detector), + last_pack_cng_or_dtmf_(1) { + assert(peak_detector); // Should never be NULL. + Reset(); +} + +DelayManager::~DelayManager() {} + +const DelayManager::IATVector& DelayManager::iat_vector() const { + return iat_vector_; +} + +// Set the histogram vector to an exponentially decaying distribution +// iat_vector_[i] = 0.5^(i+1), i = 0, 1, 2, ... +// iat_vector_ is in Q30. +void DelayManager::ResetHistogram() { + // Set temp_prob to (slightly more than) 1 in Q14. This ensures that the sum + // of iat_vector_ is 1. + uint16_t temp_prob = 0x4002; // 16384 + 2 = 100000000000010 binary. + IATVector::iterator it = iat_vector_.begin(); + for (; it < iat_vector_.end(); it++) { + temp_prob >>= 1; + (*it) = temp_prob << 16; + } + base_target_level_ = 4; + target_level_ = base_target_level_ << 8; +} + +int DelayManager::Update(uint16_t sequence_number, + uint32_t timestamp, + int sample_rate_hz) { + if (sample_rate_hz <= 0) { + return -1; + } + + if (!first_packet_received_) { + // Prepare for next packet arrival. + packet_iat_count_ms_ = 0; + last_seq_no_ = sequence_number; + last_timestamp_ = timestamp; + first_packet_received_ = true; + return 0; + } + + // Try calculating packet length from current and previous timestamps. + int packet_len_ms; + if (!IsNewerTimestamp(timestamp, last_timestamp_) || + !IsNewerSequenceNumber(sequence_number, last_seq_no_)) { + // Wrong timestamp or sequence order; use stored value. + packet_len_ms = packet_len_ms_; + } else { + // Calculate timestamps per packet and derive packet length in ms. + int packet_len_samp = + static_cast(timestamp - last_timestamp_) / + static_cast(sequence_number - last_seq_no_); + packet_len_ms = (1000 * packet_len_samp) / sample_rate_hz; + } + + if (packet_len_ms > 0) { + // Cannot update statistics unless |packet_len_ms| is valid. + // Calculate inter-arrival time (IAT) in integer "packet times" + // (rounding down). This is the value used as index to the histogram + // vector |iat_vector_|. + int iat_packets = packet_iat_count_ms_ / packet_len_ms; + + if (streaming_mode_) { + UpdateCumulativeSums(packet_len_ms, sequence_number); + } + + // Check for discontinuous packet sequence and re-ordering. + if (IsNewerSequenceNumber(sequence_number, last_seq_no_ + 1)) { + // Compensate for gap in the sequence numbers. Reduce IAT with the + // expected extra time due to lost packets, but ensure that the IAT is + // not negative. + iat_packets -= static_cast(sequence_number - last_seq_no_ - 1); + iat_packets = std::max(iat_packets, 0); + } else if (!IsNewerSequenceNumber(sequence_number, last_seq_no_)) { + iat_packets += static_cast(last_seq_no_ + 1 - sequence_number); + } + + // Saturate IAT at maximum value. + const int max_iat = kMaxIat; + iat_packets = std::min(iat_packets, max_iat); + UpdateHistogram(iat_packets); + // Calculate new |target_level_| based on updated statistics. + target_level_ = CalculateTargetLevel(iat_packets); + if (streaming_mode_) { + target_level_ = std::max(target_level_, max_iat_cumulative_sum_); + } + + LimitTargetLevel(); + } // End if (packet_len_ms > 0). + + // Prepare for next packet arrival. + packet_iat_count_ms_ = 0; + last_seq_no_ = sequence_number; + last_timestamp_ = timestamp; + return 0; +} + +void DelayManager::UpdateCumulativeSums(int packet_len_ms, + uint16_t sequence_number) { + // Calculate IAT in Q8, including fractions of a packet (i.e., more + // accurate than |iat_packets|. + int iat_packets_q8 = (packet_iat_count_ms_ << 8) / packet_len_ms; + // Calculate cumulative sum IAT with sequence number compensation. The sum + // is zero if there is no clock-drift. + iat_cumulative_sum_ += (iat_packets_q8 - + (static_cast(sequence_number - last_seq_no_) << 8)); + // Subtract drift term. + iat_cumulative_sum_ -= kCumulativeSumDrift; + // Ensure not negative. + iat_cumulative_sum_ = std::max(iat_cumulative_sum_, 0); + if (iat_cumulative_sum_ > max_iat_cumulative_sum_) { + // Found a new maximum. + max_iat_cumulative_sum_ = iat_cumulative_sum_; + max_timer_ms_ = 0; + } + if (max_timer_ms_ > kMaxStreamingPeakPeriodMs) { + // Too long since the last maximum was observed; decrease max value. + max_iat_cumulative_sum_ -= kCumulativeSumDrift; + } +} + +// Each element in the vector is first multiplied by the forgetting factor +// |iat_factor_|. Then the vector element indicated by |iat_packets| is then +// increased (additive) by 1 - |iat_factor_|. This way, the probability of +// |iat_packets| is slightly increased, while the sum of the histogram remains +// constant (=1). +// Due to inaccuracies in the fixed-point arithmetic, the histogram may no +// longer sum up to 1 (in Q30) after the update. To correct this, a correction +// term is added or subtracted from the first element (or elements) of the +// vector. +// The forgetting factor |iat_factor_| is also updated. When the DelayManager +// is reset, the factor is set to 0 to facilitate rapid convergence in the +// beginning. With each update of the histogram, the factor is increased towards +// the steady-state value |kIatFactor_|. +void DelayManager::UpdateHistogram(size_t iat_packets) { + assert(iat_packets < iat_vector_.size()); + int vector_sum = 0; // Sum up the vector elements as they are processed. + // Multiply each element in |iat_vector_| with |iat_factor_|. + for (IATVector::iterator it = iat_vector_.begin(); + it != iat_vector_.end(); ++it) { + *it = (static_cast(*it) * iat_factor_) >> 15; + vector_sum += *it; + } + + // Increase the probability for the currently observed inter-arrival time + // by 1 - |iat_factor_|. The factor is in Q15, |iat_vector_| in Q30. + // Thus, left-shift 15 steps to obtain result in Q30. + iat_vector_[iat_packets] += (32768 - iat_factor_) << 15; + vector_sum += (32768 - iat_factor_) << 15; // Add to vector sum. + + // |iat_vector_| should sum up to 1 (in Q30), but it may not due to + // fixed-point rounding errors. + vector_sum -= 1 << 30; // Should be zero. Compensate if not. + if (vector_sum != 0) { + // Modify a few values early in |iat_vector_|. + int flip_sign = vector_sum > 0 ? -1 : 1; + IATVector::iterator it = iat_vector_.begin(); + while (it != iat_vector_.end() && abs(vector_sum) > 0) { + // Add/subtract 1/16 of the element, but not more than |vector_sum|. + int correction = flip_sign * std::min(abs(vector_sum), (*it) >> 4); + *it += correction; + vector_sum += correction; + ++it; + } + } + assert(vector_sum == 0); // Verify that the above is correct. + + // Update |iat_factor_| (changes only during the first seconds after a reset). + // The factor converges to |kIatFactor_|. + iat_factor_ += (kIatFactor_ - iat_factor_ + 3) >> 2; +} + +// Enforces upper and lower limits for |target_level_|. The upper limit is +// chosen to be minimum of i) 75% of |max_packets_in_buffer_|, to leave some +// headroom for natural fluctuations around the target, and ii) equivalent of +// |maximum_delay_ms_| in packets. Note that in practice, if no +// |maximum_delay_ms_| is specified, this does not have any impact, since the +// target level is far below the buffer capacity in all reasonable cases. +// The lower limit is equivalent of |minimum_delay_ms_| in packets. We update +// |least_required_level_| while the above limits are applied. +// TODO(hlundin): Move this check to the buffer logistics class. +void DelayManager::LimitTargetLevel() { + least_required_delay_ms_ = (target_level_ * packet_len_ms_) >> 8; + + if (packet_len_ms_ > 0 && minimum_delay_ms_ > 0) { + int minimum_delay_packet_q8 = (minimum_delay_ms_ << 8) / packet_len_ms_; + target_level_ = std::max(target_level_, minimum_delay_packet_q8); + } + + if (maximum_delay_ms_ > 0 && packet_len_ms_ > 0) { + int maximum_delay_packet_q8 = (maximum_delay_ms_ << 8) / packet_len_ms_; + target_level_ = std::min(target_level_, maximum_delay_packet_q8); + } + + // Shift to Q8, then 75%.; + int max_buffer_packets_q8 = (3 * (max_packets_in_buffer_ << 8)) / 4; + target_level_ = std::min(target_level_, max_buffer_packets_q8); + + // Sanity check, at least 1 packet (in Q8). + target_level_ = std::max(target_level_, 1 << 8); +} + +int DelayManager::CalculateTargetLevel(int iat_packets) { + int limit_probability = kLimitProbability; + if (streaming_mode_) { + limit_probability = kLimitProbabilityStreaming; + } + + // Calculate target buffer level from inter-arrival time histogram. + // Find the |iat_index| for which the probability of observing an + // inter-arrival time larger than or equal to |iat_index| is less than or + // equal to |limit_probability|. The sought probability is estimated using + // the histogram as the reverse cumulant PDF, i.e., the sum of elements from + // the end up until |iat_index|. Now, since the sum of all elements is 1 + // (in Q30) by definition, and since the solution is often a low value for + // |iat_index|, it is more efficient to start with |sum| = 1 and subtract + // elements from the start of the histogram. + size_t index = 0; // Start from the beginning of |iat_vector_|. + int sum = 1 << 30; // Assign to 1 in Q30. + sum -= iat_vector_[index]; // Ensure that target level is >= 1. + + do { + // Subtract the probabilities one by one until the sum is no longer greater + // than limit_probability. + ++index; + sum -= iat_vector_[index]; + } while ((sum > limit_probability) && (index < iat_vector_.size() - 1)); + + // This is the base value for the target buffer level. + int target_level = static_cast(index); + base_target_level_ = static_cast(index); + + // Update detector for delay peaks. + bool delay_peak_found = peak_detector_.Update(iat_packets, target_level); + if (delay_peak_found) { + target_level = std::max(target_level, peak_detector_.MaxPeakHeight()); + } + + // Sanity check. |target_level| must be strictly positive. + target_level = std::max(target_level, 1); + // Scale to Q8 and assign to member variable. + target_level_ = target_level << 8; + return target_level_; +} + +int DelayManager::SetPacketAudioLength(int length_ms) { + if (length_ms <= 0) { + LOG_F(LS_ERROR) << "length_ms = " << length_ms; + return -1; + } + packet_len_ms_ = length_ms; + peak_detector_.SetPacketAudioLength(packet_len_ms_); + packet_iat_count_ms_ = 0; + last_pack_cng_or_dtmf_ = 1; // TODO(hlundin): Legacy. Remove? + return 0; +} + + +void DelayManager::Reset() { + packet_len_ms_ = 0; // Packet size unknown. + streaming_mode_ = false; + peak_detector_.Reset(); + ResetHistogram(); // Resets target levels too. + iat_factor_ = 0; // Adapt the histogram faster for the first few packets. + packet_iat_count_ms_ = 0; + max_timer_ms_ = 0; + iat_cumulative_sum_ = 0; + max_iat_cumulative_sum_ = 0; + last_pack_cng_or_dtmf_ = 1; +} + +int DelayManager::AverageIAT() const { + int32_t sum_q24 = 0; + // Using an int for the upper limit of the following for-loop so the + // loop-counter can be int. Otherwise we need a cast where |sum_q24| is + // updated. + const int iat_vec_size = static_cast(iat_vector_.size()); + assert(iat_vector_.size() == 65); // Algorithm is hard-coded for this size. + for (int i = 0; i < iat_vec_size; ++i) { + // Shift 6 to fit worst case: 2^30 * 64. + sum_q24 += (iat_vector_[i] >> 6) * i; + } + // Subtract the nominal inter-arrival time 1 = 2^24 in Q24. + sum_q24 -= (1 << 24); + // Multiply with 1000000 / 2^24 = 15625 / 2^18 to get in parts-per-million. + // Shift 7 to Q17 first, then multiply with 15625 and shift another 11. + return ((sum_q24 >> 7) * 15625) >> 11; +} + +bool DelayManager::PeakFound() const { + return peak_detector_.peak_found(); +} + +void DelayManager::UpdateCounters(int elapsed_time_ms) { + packet_iat_count_ms_ += elapsed_time_ms; + peak_detector_.IncrementCounter(elapsed_time_ms); + max_timer_ms_ += elapsed_time_ms; +} + +void DelayManager::ResetPacketIatCount() { packet_iat_count_ms_ = 0; } + +// Note that |low_limit| and |higher_limit| are not assigned to +// |minimum_delay_ms_| and |maximum_delay_ms_| defined by the client of this +// class. They are computed from |target_level_| and used for decision making. +void DelayManager::BufferLimits(int* lower_limit, int* higher_limit) const { + if (!lower_limit || !higher_limit) { + LOG_F(LS_ERROR) << "NULL pointers supplied as input"; + assert(false); + return; + } + + int window_20ms = 0x7FFF; // Default large value for legacy bit-exactness. + if (packet_len_ms_ > 0) { + window_20ms = (20 << 8) / packet_len_ms_; + } + + // |target_level_| is in Q8 already. + *lower_limit = (target_level_ * 3) / 4; + // |higher_limit| is equal to |target_level_|, but should at + // least be 20 ms higher than |lower_limit_|. + *higher_limit = std::max(target_level_, *lower_limit + window_20ms); +} + +int DelayManager::TargetLevel() const { + return target_level_; +} + +void DelayManager::LastDecoderType(NetEqDecoder decoder_type) { + if (decoder_type == kDecoderAVT || + decoder_type == kDecoderCNGnb || + decoder_type == kDecoderCNGwb || + decoder_type == kDecoderCNGswb32kHz || + decoder_type == kDecoderCNGswb48kHz) { + last_pack_cng_or_dtmf_ = 1; + } else if (last_pack_cng_or_dtmf_ != 0) { + last_pack_cng_or_dtmf_ = -1; + } +} + +bool DelayManager::SetMinimumDelay(int delay_ms) { + // Minimum delay shouldn't be more than maximum delay, if any maximum is set. + // Also, if possible check |delay| to less than 75% of + // |max_packets_in_buffer_|. + if ((maximum_delay_ms_ > 0 && delay_ms > maximum_delay_ms_) || + (packet_len_ms_ > 0 && + delay_ms > 3 * max_packets_in_buffer_ * packet_len_ms_ / 4)) { + return false; + } + minimum_delay_ms_ = delay_ms; + return true; +} + +bool DelayManager::SetMaximumDelay(int delay_ms) { + if (delay_ms == 0) { + // Zero input unsets the maximum delay. + maximum_delay_ms_ = 0; + return true; + } else if (delay_ms < minimum_delay_ms_ || delay_ms < packet_len_ms_) { + // Maximum delay shouldn't be less than minimum delay or less than a packet. + return false; + } + maximum_delay_ms_ = delay_ms; + return true; +} + +int DelayManager::least_required_delay_ms() const { + return least_required_delay_ms_; +} + +int DelayManager::base_target_level() const { return base_target_level_; } +void DelayManager::set_streaming_mode(bool value) { streaming_mode_ = value; } +int DelayManager::last_pack_cng_or_dtmf() const { + return last_pack_cng_or_dtmf_; +} + +void DelayManager::set_last_pack_cng_or_dtmf(int value) { + last_pack_cng_or_dtmf_ = value; +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_manager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_manager.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_manager.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_manager.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_DELAY_MANAGER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_DELAY_MANAGER_H_ + +#include // Provide access to size_t. + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Forward declaration. +class DelayPeakDetector; + +class DelayManager { + public: + typedef std::vector IATVector; + + // Create a DelayManager object. Notify the delay manager that the packet + // buffer can hold no more than |max_packets_in_buffer| packets (i.e., this + // is the number of packet slots in the buffer). Supply a PeakDetector + // object to the DelayManager. + DelayManager(int max_packets_in_buffer, DelayPeakDetector* peak_detector); + + virtual ~DelayManager(); + + // Read the inter-arrival time histogram. Mainly for testing purposes. + virtual const IATVector& iat_vector() const; + + // Updates the delay manager with a new incoming packet, with + // |sequence_number| and |timestamp| from the RTP header. This updates the + // inter-arrival time histogram and other statistics, as well as the + // associated DelayPeakDetector. A new target buffer level is calculated. + // Returns 0 on success, -1 on failure (invalid sample rate). + virtual int Update(uint16_t sequence_number, + uint32_t timestamp, + int sample_rate_hz); + + // Calculates a new target buffer level. Called from the Update() method. + // Sets target_level_ (in Q8) and returns the same value. Also calculates + // and updates base_target_level_, which is the target buffer level before + // taking delay peaks into account. + virtual int CalculateTargetLevel(int iat_packets); + + // Notifies the DelayManager of how much audio data is carried in each packet. + // The method updates the DelayPeakDetector too, and resets the inter-arrival + // time counter. Returns 0 on success, -1 on failure. + virtual int SetPacketAudioLength(int length_ms); + + // Resets the DelayManager and the associated DelayPeakDetector. + virtual void Reset(); + + // Calculates the average inter-arrival time deviation from the histogram. + // The result is returned as parts-per-million deviation from the nominal + // inter-arrival time. That is, if the average inter-arrival time is equal to + // the nominal frame time, the return value is zero. A positive value + // corresponds to packet spacing being too large, while a negative value means + // that the packets arrive with less spacing than expected. + virtual int AverageIAT() const; + + // Returns true if peak-mode is active. That is, delay peaks were observed + // recently. This method simply asks for the same information from the + // DelayPeakDetector object. + virtual bool PeakFound() const; + + // Notifies the counters in DelayManager and DelayPeakDetector that + // |elapsed_time_ms| have elapsed. + virtual void UpdateCounters(int elapsed_time_ms); + + // Reset the inter-arrival time counter to 0. + virtual void ResetPacketIatCount(); + + // Writes the lower and higher limits which the buffer level should stay + // within to the corresponding pointers. The values are in (fractions of) + // packets in Q8. + virtual void BufferLimits(int* lower_limit, int* higher_limit) const; + + // Gets the target buffer level, in (fractions of) packets in Q8. This value + // includes any extra delay set through the set_extra_delay_ms() method. + virtual int TargetLevel() const; + + virtual void LastDecoderType(NetEqDecoder decoder_type); + + // Accessors and mutators. + // Assuming |delay| is in valid range. + virtual bool SetMinimumDelay(int delay_ms); + virtual bool SetMaximumDelay(int delay_ms); + virtual int least_required_delay_ms() const; + virtual int base_target_level() const; + virtual void set_streaming_mode(bool value); + virtual int last_pack_cng_or_dtmf() const; + virtual void set_last_pack_cng_or_dtmf(int value); + + private: + static const int kLimitProbability = 53687091; // 1/20 in Q30. + static const int kLimitProbabilityStreaming = 536871; // 1/2000 in Q30. + static const int kMaxStreamingPeakPeriodMs = 600000; // 10 minutes in ms. + static const int kCumulativeSumDrift = 2; // Drift term for cumulative sum + // |iat_cumulative_sum_|. + // Steady-state forgetting factor for |iat_vector_|, 0.9993 in Q15. + static const int kIatFactor_ = 32745; + static const int kMaxIat = 64; // Max inter-arrival time to register. + + // Sets |iat_vector_| to the default start distribution and sets the + // |base_target_level_| and |target_level_| to the corresponding values. + void ResetHistogram(); + + // Updates |iat_cumulative_sum_| and |max_iat_cumulative_sum_|. (These are + // used by the streaming mode.) This method is called by Update(). + void UpdateCumulativeSums(int packet_len_ms, uint16_t sequence_number); + + // Updates the histogram |iat_vector_|. The probability for inter-arrival time + // equal to |iat_packets| (in integer packets) is increased slightly, while + // all other entries are decreased. This method is called by Update(). + void UpdateHistogram(size_t iat_packets); + + // Makes sure that |target_level_| is not too large, taking + // |max_packets_in_buffer_| and |extra_delay_ms_| into account. This method is + // called by Update(). + void LimitTargetLevel(); + + bool first_packet_received_; + const int max_packets_in_buffer_; // Capacity of the packet buffer. + IATVector iat_vector_; // Histogram of inter-arrival times. + int iat_factor_; // Forgetting factor for updating the IAT histogram (Q15). + int packet_iat_count_ms_; // Milliseconds elapsed since last packet. + int base_target_level_; // Currently preferred buffer level before peak + // detection and streaming mode (Q0). + // TODO(turajs) change the comment according to the implementation of + // minimum-delay. + int target_level_; // Currently preferred buffer level in (fractions) + // of packets (Q8), before adding any extra delay. + int packet_len_ms_; // Length of audio in each incoming packet [ms]. + bool streaming_mode_; + uint16_t last_seq_no_; // Sequence number for last received packet. + uint32_t last_timestamp_; // Timestamp for the last received packet. + int minimum_delay_ms_; // Externally set minimum delay. + int least_required_delay_ms_; // Smallest preferred buffer level (same unit + // as |target_level_|), before applying + // |minimum_delay_ms_| and/or |maximum_delay_ms_|. + int maximum_delay_ms_; // Externally set maximum allowed delay. + int iat_cumulative_sum_; // Cumulative sum of delta inter-arrival times. + int max_iat_cumulative_sum_; // Max of |iat_cumulative_sum_|. + int max_timer_ms_; // Time elapsed since maximum was observed. + DelayPeakDetector& peak_detector_; + int last_pack_cng_or_dtmf_; + + DISALLOW_COPY_AND_ASSIGN(DelayManager); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_DELAY_MANAGER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_manager_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_manager_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_manager_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_manager_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for DelayManager class. + +#include "webrtc/modules/audio_coding/neteq/delay_manager.h" + +#include + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/neteq/mock/mock_delay_peak_detector.h" + +namespace webrtc { + +using ::testing::Return; +using ::testing::_; + +class DelayManagerTest : public ::testing::Test { + protected: + static const int kMaxNumberOfPackets = 240; + static const int kTimeStepMs = 10; + static const int kFs = 8000; + static const int kFrameSizeMs = 20; + static const int kTsIncrement = kFrameSizeMs * kFs / 1000; + + DelayManagerTest(); + virtual void SetUp(); + virtual void TearDown(); + void SetPacketAudioLength(int lengt_ms); + void InsertNextPacket(); + void IncreaseTime(int inc_ms); + + DelayManager* dm_; + MockDelayPeakDetector detector_; + uint16_t seq_no_; + uint32_t ts_; +}; + +DelayManagerTest::DelayManagerTest() + : dm_(NULL), + seq_no_(0x1234), + ts_(0x12345678) { +} + +void DelayManagerTest::SetUp() { + EXPECT_CALL(detector_, Reset()) + .Times(1); + dm_ = new DelayManager(kMaxNumberOfPackets, &detector_); +} + +void DelayManagerTest::SetPacketAudioLength(int lengt_ms) { + EXPECT_CALL(detector_, SetPacketAudioLength(lengt_ms)); + dm_->SetPacketAudioLength(lengt_ms); +} + +void DelayManagerTest::InsertNextPacket() { + EXPECT_EQ(0, dm_->Update(seq_no_, ts_, kFs)); + seq_no_ += 1; + ts_ += kTsIncrement; +} + +void DelayManagerTest::IncreaseTime(int inc_ms) { + for (int t = 0; t < inc_ms; t += kTimeStepMs) { + EXPECT_CALL(detector_, IncrementCounter(kTimeStepMs)) + .Times(1); + dm_->UpdateCounters(kTimeStepMs); + } +} +void DelayManagerTest::TearDown() { + EXPECT_CALL(detector_, Die()); + delete dm_; +} + +TEST_F(DelayManagerTest, CreateAndDestroy) { + // Nothing to do here. The test fixture creates and destroys the DelayManager + // object. +} + +TEST_F(DelayManagerTest, VectorInitialization) { + const DelayManager::IATVector& vec = dm_->iat_vector(); + double sum = 0.0; + for (size_t i = 0; i < vec.size(); i++) { + EXPECT_NEAR(ldexp(pow(0.5, static_cast(i + 1)), 30), vec[i], 65536); + // Tolerance 65536 in Q30 corresponds to a delta of approximately 0.00006. + sum += vec[i]; + } + EXPECT_EQ(1 << 30, static_cast(sum)); // Should be 1 in Q30. +} + +TEST_F(DelayManagerTest, SetPacketAudioLength) { + const int kLengthMs = 30; + // Expect DelayManager to pass on the new length to the detector object. + EXPECT_CALL(detector_, SetPacketAudioLength(kLengthMs)) + .Times(1); + EXPECT_EQ(0, dm_->SetPacketAudioLength(kLengthMs)); + EXPECT_EQ(-1, dm_->SetPacketAudioLength(-1)); // Illegal parameter value. +} + +TEST_F(DelayManagerTest, PeakFound) { + // Expect DelayManager to pass on the question to the detector. + // Call twice, and let the detector return true the first time and false the + // second time. + EXPECT_CALL(detector_, peak_found()) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + EXPECT_TRUE(dm_->PeakFound()); + EXPECT_FALSE(dm_->PeakFound()); +} + +TEST_F(DelayManagerTest, UpdateCounters) { + // Expect DelayManager to pass on the counter update to the detector. + EXPECT_CALL(detector_, IncrementCounter(kTimeStepMs)) + .Times(1); + dm_->UpdateCounters(kTimeStepMs); +} + +TEST_F(DelayManagerTest, UpdateNormal) { + SetPacketAudioLength(kFrameSizeMs); + // First packet arrival. + InsertNextPacket(); + // Advance time by one frame size. + IncreaseTime(kFrameSizeMs); + // Second packet arrival. + // Expect detector update method to be called once with inter-arrival time + // equal to 1 packet, and (base) target level equal to 1 as well. + // Return false to indicate no peaks found. + EXPECT_CALL(detector_, Update(1, 1)) + .WillOnce(Return(false)); + InsertNextPacket(); + EXPECT_EQ(1 << 8, dm_->TargetLevel()); // In Q8. + EXPECT_EQ(1, dm_->base_target_level()); + int lower, higher; + dm_->BufferLimits(&lower, &higher); + // Expect |lower| to be 75% of target level, and |higher| to be target level, + // but also at least 20 ms higher than |lower|, which is the limiting case + // here. + EXPECT_EQ((1 << 8) * 3 / 4, lower); + EXPECT_EQ(lower + (20 << 8) / kFrameSizeMs, higher); +} + +TEST_F(DelayManagerTest, UpdateLongInterArrivalTime) { + SetPacketAudioLength(kFrameSizeMs); + // First packet arrival. + InsertNextPacket(); + // Advance time by two frame size. + IncreaseTime(2 * kFrameSizeMs); + // Second packet arrival. + // Expect detector update method to be called once with inter-arrival time + // equal to 1 packet, and (base) target level equal to 1 as well. + // Return false to indicate no peaks found. + EXPECT_CALL(detector_, Update(2, 2)) + .WillOnce(Return(false)); + InsertNextPacket(); + EXPECT_EQ(2 << 8, dm_->TargetLevel()); // In Q8. + EXPECT_EQ(2, dm_->base_target_level()); + int lower, higher; + dm_->BufferLimits(&lower, &higher); + // Expect |lower| to be 75% of target level, and |higher| to be target level, + // but also at least 20 ms higher than |lower|, which is the limiting case + // here. + EXPECT_EQ((2 << 8) * 3 / 4, lower); + EXPECT_EQ(lower + (20 << 8) / kFrameSizeMs, higher); +} + +TEST_F(DelayManagerTest, UpdatePeakFound) { + SetPacketAudioLength(kFrameSizeMs); + // First packet arrival. + InsertNextPacket(); + // Advance time by one frame size. + IncreaseTime(kFrameSizeMs); + // Second packet arrival. + // Expect detector update method to be called once with inter-arrival time + // equal to 1 packet, and (base) target level equal to 1 as well. + // Return true to indicate that peaks are found. Let the peak height be 5. + EXPECT_CALL(detector_, Update(1, 1)) + .WillOnce(Return(true)); + EXPECT_CALL(detector_, MaxPeakHeight()) + .WillOnce(Return(5)); + InsertNextPacket(); + EXPECT_EQ(5 << 8, dm_->TargetLevel()); + EXPECT_EQ(1, dm_->base_target_level()); // Base target level is w/o peaks. + int lower, higher; + dm_->BufferLimits(&lower, &higher); + // Expect |lower| to be 75% of target level, and |higher| to be target level. + EXPECT_EQ((5 << 8) * 3 / 4, lower); + EXPECT_EQ(5 << 8, higher); +} + +TEST_F(DelayManagerTest, TargetDelay) { + SetPacketAudioLength(kFrameSizeMs); + // First packet arrival. + InsertNextPacket(); + // Advance time by one frame size. + IncreaseTime(kFrameSizeMs); + // Second packet arrival. + // Expect detector update method to be called once with inter-arrival time + // equal to 1 packet, and (base) target level equal to 1 as well. + // Return false to indicate no peaks found. + EXPECT_CALL(detector_, Update(1, 1)) + .WillOnce(Return(false)); + InsertNextPacket(); + const int kExpectedTarget = 1; + EXPECT_EQ(kExpectedTarget << 8, dm_->TargetLevel()); // In Q8. + EXPECT_EQ(1, dm_->base_target_level()); + int lower, higher; + dm_->BufferLimits(&lower, &higher); + // Expect |lower| to be 75% of base target level, and |higher| to be + // lower + 20 ms headroom. + EXPECT_EQ((1 << 8) * 3 / 4, lower); + EXPECT_EQ(lower + (20 << 8) / kFrameSizeMs, higher); +} + +TEST_F(DelayManagerTest, MaxAndRequiredDelay) { + const int kExpectedTarget = 5; + const int kTimeIncrement = kExpectedTarget * kFrameSizeMs; + SetPacketAudioLength(kFrameSizeMs); + // First packet arrival. + InsertNextPacket(); + // Second packet arrival. + // Expect detector update method to be called once with inter-arrival time + // equal to |kExpectedTarget| packet. Return true to indicate peaks found. + EXPECT_CALL(detector_, Update(kExpectedTarget, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(detector_, MaxPeakHeight()) + .WillRepeatedly(Return(kExpectedTarget)); + IncreaseTime(kTimeIncrement); + InsertNextPacket(); + + // No limit is set. + EXPECT_EQ(kExpectedTarget << 8, dm_->TargetLevel()); + + int kMaxDelayPackets = kExpectedTarget - 2; + int kMaxDelayMs = kMaxDelayPackets * kFrameSizeMs; + EXPECT_TRUE(dm_->SetMaximumDelay(kMaxDelayMs)); + IncreaseTime(kTimeIncrement); + InsertNextPacket(); + EXPECT_EQ(kExpectedTarget * kFrameSizeMs, dm_->least_required_delay_ms()); + EXPECT_EQ(kMaxDelayPackets << 8, dm_->TargetLevel()); + + // Target level at least should be one packet. + EXPECT_FALSE(dm_->SetMaximumDelay(kFrameSizeMs - 1)); +} + +TEST_F(DelayManagerTest, MinAndRequiredDelay) { + const int kExpectedTarget = 5; + const int kTimeIncrement = kExpectedTarget * kFrameSizeMs; + SetPacketAudioLength(kFrameSizeMs); + // First packet arrival. + InsertNextPacket(); + // Second packet arrival. + // Expect detector update method to be called once with inter-arrival time + // equal to |kExpectedTarget| packet. Return true to indicate peaks found. + EXPECT_CALL(detector_, Update(kExpectedTarget, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(detector_, MaxPeakHeight()) + .WillRepeatedly(Return(kExpectedTarget)); + IncreaseTime(kTimeIncrement); + InsertNextPacket(); + + // No limit is applied. + EXPECT_EQ(kExpectedTarget << 8, dm_->TargetLevel()); + + int kMinDelayPackets = kExpectedTarget + 2; + int kMinDelayMs = kMinDelayPackets * kFrameSizeMs; + dm_->SetMinimumDelay(kMinDelayMs); + IncreaseTime(kTimeIncrement); + InsertNextPacket(); + EXPECT_EQ(kExpectedTarget * kFrameSizeMs, dm_->least_required_delay_ms()); + EXPECT_EQ(kMinDelayPackets << 8, dm_->TargetLevel()); +} + +TEST_F(DelayManagerTest, Failures) { + // Wrong sample rate. + EXPECT_EQ(-1, dm_->Update(0, 0, -1)); + // Wrong packet size. + EXPECT_EQ(-1, dm_->SetPacketAudioLength(0)); + EXPECT_EQ(-1, dm_->SetPacketAudioLength(-1)); + + // Minimum delay higher than a maximum delay is not accepted. + EXPECT_TRUE(dm_->SetMaximumDelay(10)); + EXPECT_FALSE(dm_->SetMinimumDelay(20)); + + // Maximum delay less than minimum delay is not accepted. + EXPECT_TRUE(dm_->SetMaximumDelay(100)); + EXPECT_TRUE(dm_->SetMinimumDelay(80)); + EXPECT_FALSE(dm_->SetMaximumDelay(60)); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_peak_detector.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_peak_detector.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_peak_detector.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_peak_detector.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/delay_peak_detector.h" + +#include // max + +namespace webrtc { + +// The DelayPeakDetector keeps track of severe inter-arrival times, called +// delay peaks. When a peak is observed, the "height" (the time elapsed since +// the previous packet arrival) and the peak "period" (the time since the last +// observed peak) is recorded in a vector. When enough peaks have been observed, +// peak-mode is engaged and the DelayManager asks the DelayPeakDetector for +// the worst peak height. + +DelayPeakDetector::DelayPeakDetector() + : peak_found_(false), + peak_detection_threshold_(0), + peak_period_counter_ms_(-1) { +} + +void DelayPeakDetector::Reset() { + peak_period_counter_ms_ = -1; // Indicate that next peak is the first. + peak_found_ = false; + peak_history_.clear(); +} + +// Calculates the threshold in number of packets. +void DelayPeakDetector::SetPacketAudioLength(int length_ms) { + if (length_ms > 0) { + peak_detection_threshold_ = kPeakHeightMs / length_ms; + } +} + +int DelayPeakDetector::MaxPeakHeight() const { + int max_height = -1; // Returns -1 for an empty history. + std::list::const_iterator it; + for (it = peak_history_.begin(); it != peak_history_.end(); ++it) { + max_height = std::max(max_height, it->peak_height_packets); + } + return max_height; +} + +int DelayPeakDetector::MaxPeakPeriod() const { + int max_period = -1; // Returns -1 for an empty history. + std::list::const_iterator it; + for (it = peak_history_.begin(); it != peak_history_.end(); ++it) { + max_period = std::max(max_period, it->period_ms); + } + return max_period; +} + +bool DelayPeakDetector::Update(int inter_arrival_time, int target_level) { + if (inter_arrival_time > target_level + peak_detection_threshold_ || + inter_arrival_time > 2 * target_level) { + // A delay peak is observed. + if (peak_period_counter_ms_ == -1) { + // This is the first peak. Reset the period counter. + peak_period_counter_ms_ = 0; + } else if (peak_period_counter_ms_ <= kMaxPeakPeriodMs) { + // This is not the first peak, and the period is valid. + // Store peak data in the vector. + Peak peak_data; + peak_data.period_ms = peak_period_counter_ms_; + peak_data.peak_height_packets = inter_arrival_time; + peak_history_.push_back(peak_data); + while (peak_history_.size() > kMaxNumPeaks) { + // Delete the oldest data point. + peak_history_.pop_front(); + } + peak_period_counter_ms_ = 0; + } else if (peak_period_counter_ms_ <= 2 * kMaxPeakPeriodMs) { + // Invalid peak due to too long period. Reset period counter and start + // looking for next peak. + peak_period_counter_ms_ = 0; + } else { + // More than 2 times the maximum period has elapsed since the last peak + // was registered. It seams that the network conditions have changed. + // Reset the peak statistics. + Reset(); + } + } + return CheckPeakConditions(); +} + +void DelayPeakDetector::IncrementCounter(int inc_ms) { + if (peak_period_counter_ms_ >= 0) { + peak_period_counter_ms_ += inc_ms; + } +} + +bool DelayPeakDetector::CheckPeakConditions() { + size_t s = peak_history_.size(); + if (s >= kMinPeaksToTrigger && + peak_period_counter_ms_ <= 2 * MaxPeakPeriod()) { + peak_found_ = true; + } else { + peak_found_ = false; + } + return peak_found_; +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_peak_detector.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_peak_detector.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_peak_detector.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_peak_detector.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_DELAY_PEAK_DETECTOR_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_DELAY_PEAK_DETECTOR_H_ + +#include // size_t + +#include + +#include "webrtc/base/constructormagic.h" + +namespace webrtc { + +class DelayPeakDetector { + public: + DelayPeakDetector(); + virtual ~DelayPeakDetector() {} + virtual void Reset(); + + // Notifies the DelayPeakDetector of how much audio data is carried in each + // packet. + virtual void SetPacketAudioLength(int length_ms); + + // Returns true if peak-mode is active. That is, delay peaks were observed + // recently. + virtual bool peak_found() { return peak_found_; } + + // Calculates and returns the maximum delay peak height. Returns -1 if no + // delay peaks have been observed recently. The unit is number of packets. + virtual int MaxPeakHeight() const; + + // Calculates and returns the maximum delay peak distance in ms. + // Returns -1 if no delay peaks have been observed recently. + virtual int MaxPeakPeriod() const; + + // Updates the DelayPeakDetector with a new inter-arrival time (in packets) + // and the current target buffer level (needed to decide if a peak is observed + // or not). Returns true if peak-mode is active, false if not. + virtual bool Update(int inter_arrival_time, int target_level); + + // Increments the |peak_period_counter_ms_| with |inc_ms|. Only increments + // the counter if it is non-negative. A negative denotes that no peak has + // been observed. + virtual void IncrementCounter(int inc_ms); + + private: + static const size_t kMaxNumPeaks = 8; + static const size_t kMinPeaksToTrigger = 2; + static const int kPeakHeightMs = 78; + static const int kMaxPeakPeriodMs = 10000; + + typedef struct { + int period_ms; + int peak_height_packets; + } Peak; + + bool CheckPeakConditions(); + + std::list peak_history_; + bool peak_found_; + int peak_detection_threshold_; + int peak_period_counter_ms_; + + DISALLOW_COPY_AND_ASSIGN(DelayPeakDetector); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_DELAY_PEAK_DETECTOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_peak_detector_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_peak_detector_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_peak_detector_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/delay_peak_detector_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for DelayPeakDetector class. + +#include "webrtc/modules/audio_coding/neteq/delay_peak_detector.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace webrtc { + +TEST(DelayPeakDetector, CreateAndDestroy) { + DelayPeakDetector* detector = new DelayPeakDetector(); + EXPECT_FALSE(detector->peak_found()); + delete detector; +} + +TEST(DelayPeakDetector, EmptyHistory) { + DelayPeakDetector detector; + EXPECT_EQ(-1, detector.MaxPeakHeight()); + EXPECT_EQ(-1, detector.MaxPeakPeriod()); +} + +// Inject a series of packet arrivals into the detector. Three of the packets +// have suffered delays. After the third delay peak, peak-mode is expected to +// start. This should then continue until it is disengaged due to lack of peaks. +TEST(DelayPeakDetector, TriggerPeakMode) { + DelayPeakDetector detector; + const int kPacketSizeMs = 30; + detector.SetPacketAudioLength(kPacketSizeMs); + + // Load up normal arrival times; 0 ms, 30 ms, 60 ms, 90 ms, ... + const int kNumPackets = 1000; + int arrival_times_ms[kNumPackets]; + for (int i = 0; i < kNumPackets; ++i) { + arrival_times_ms[i] = i * kPacketSizeMs; + } + + // Delay three packets. + const int kPeakDelayMs = 100; + // First delay peak. + arrival_times_ms[100] += kPeakDelayMs; + // Second delay peak. + arrival_times_ms[200] += kPeakDelayMs; + // Third delay peak. Trigger peak-mode after this packet. + arrival_times_ms[400] += kPeakDelayMs; + // The second peak period is the longest, 200 packets. + const int kWorstPeakPeriod = 200 * kPacketSizeMs; + int peak_mode_start_ms = arrival_times_ms[400]; + // Expect to disengage after no peaks are observed for two period times. + int peak_mode_end_ms = peak_mode_start_ms + 2 * kWorstPeakPeriod; + + // Load into detector. + int time = 0; + int next = 1; // Start with the second packet to get a proper IAT. + while (next < kNumPackets) { + while (next < kNumPackets && arrival_times_ms[next] <= time) { + int iat_packets = (arrival_times_ms[next] - arrival_times_ms[next - 1]) / + kPacketSizeMs; + const int kTargetBufferLevel = 1; // Define peaks to be iat > 2. + if (time < peak_mode_start_ms || time > peak_mode_end_ms) { + EXPECT_FALSE(detector.Update(iat_packets, kTargetBufferLevel)); + } else { + EXPECT_TRUE(detector.Update(iat_packets, kTargetBufferLevel)); + EXPECT_EQ(kWorstPeakPeriod, detector.MaxPeakPeriod()); + EXPECT_EQ(kPeakDelayMs / kPacketSizeMs + 1, detector.MaxPeakHeight()); + } + ++next; + } + detector.IncrementCounter(10); + time += 10; // Increase time 10 ms. + } +} + +// Same test as TriggerPeakMode, but with base target buffer level increased to +// 2, in order to raise the bar for delay peaks to inter-arrival times > 4. +// The delay pattern has peaks with delay = 3, thus should not trigger. +TEST(DelayPeakDetector, DoNotTriggerPeakMode) { + DelayPeakDetector detector; + const int kPacketSizeMs = 30; + detector.SetPacketAudioLength(kPacketSizeMs); + + // Load up normal arrival times; 0 ms, 30 ms, 60 ms, 90 ms, ... + const int kNumPackets = 1000; + int arrival_times_ms[kNumPackets]; + for (int i = 0; i < kNumPackets; ++i) { + arrival_times_ms[i] = i * kPacketSizeMs; + } + + // Delay three packets. + const int kPeakDelayMs = 100; + // First delay peak. + arrival_times_ms[100] += kPeakDelayMs; + // Second delay peak. + arrival_times_ms[200] += kPeakDelayMs; + // Third delay peak. + arrival_times_ms[400] += kPeakDelayMs; + + // Load into detector. + int time = 0; + int next = 1; // Start with the second packet to get a proper IAT. + while (next < kNumPackets) { + while (next < kNumPackets && arrival_times_ms[next] <= time) { + int iat_packets = (arrival_times_ms[next] - arrival_times_ms[next - 1]) / + kPacketSizeMs; + const int kTargetBufferLevel = 2; // Define peaks to be iat > 4. + EXPECT_FALSE(detector.Update(iat_packets, kTargetBufferLevel)); + ++next; + } + detector.IncrementCounter(10); + time += 10; // Increase time 10 ms. + } +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,532 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains some DSP initialization functions and - * constant table definitions. - */ - -#include "dsp.h" - -#include "signal_processing_library.h" - -#include "neteq_error_codes.h" - -/* Filter coefficients used when downsampling from the indicated - sample rates (8, 16, 32, 48 kHz) to 4 kHz. - Coefficients are in Q12. */ - -/* {0.3, 0.4, 0.3} */ -const int16_t WebRtcNetEQ_kDownsample8kHzTbl[] = { 1229, 1638, 1229 }; - -#ifdef NETEQ_WIDEBAND -/* {0.15, 0.2, 0.3, 0.2, 0.15} */ -const int16_t WebRtcNetEQ_kDownsample16kHzTbl[] = -{ 614, 819, 1229, 819, 614}; -#endif - -#ifdef NETEQ_32KHZ_WIDEBAND -/* {0.1425, 0.1251, 0.1525, 0.1628, 0.1525, 0.1251, 0.1425} */ -const int16_t WebRtcNetEQ_kDownsample32kHzTbl[] = -{ 584, 512, 625, 667, 625, 512, 584}; -#endif - -#ifdef NETEQ_48KHZ_WIDEBAND -/* {0.2487, 0.0952, 0.1042, 0.1074, 0.1042, 0.0952, 0.2487} */ -const int16_t WebRtcNetEQ_kDownsample48kHzTbl[] = -{ 1019, 390, 427, 440, 427, 390, 1019}; -#endif - -/* Constants used in expand function WebRtcNetEQ_Expand */ - -/* Q12: -1.264421 + 4.8659148*x - 4.0092827*x^2 + 1.4100529*x^3 */ -const int16_t WebRtcNetEQ_kMixFractionFuncTbl[4] = { -5179, 19931, -16422, 5776 }; - -/* Tabulated divisions to save complexity */ -/* 1049/{0, .., 6} */ -const int16_t WebRtcNetEQ_k1049div[7] = { 0, 1049, 524, 349, 262, 209, 174 }; - -/* 2097/{0, .., 6} */ -const int16_t WebRtcNetEQ_k2097div[7] = { 0, 2097, 1048, 699, 524, 419, 349 }; - -/* 5243/{0, .., 6} */ -const int16_t WebRtcNetEQ_k5243div[7] = { 0, 5243, 2621, 1747, 1310, 1048, 873 }; - -#ifdef WEBRTC_NETEQ_40BITACC_TEST -/* - * Run NetEQ with simulated 40-bit accumulator to run bit-exact to a DSP - * implementation where the main (spl and NetEQ) functions have been - * 40-bit optimized. For testing purposes. - */ - -/**************************************************************************** - * WebRtcNetEQ_40BitAccCrossCorr(...) - * - * Calculates the Cross correlation between two sequences seq1 and seq2. Seq1 - * is fixed and seq2 slides as the pointer is increased with step - * - * Input: - * - seq1 : First sequence (fixed throughout the correlation) - * - seq2 : Second sequence (slided step_seq2 for each - * new correlation) - * - dimSeq : Number of samples to use in the cross correlation. - * Should be no larger than 1024 to avoid overflow. - * - dimCrossCorr : Number of CrossCorrelations to calculate (start - * position for seq2 is updated for each new one) - * - rShift : Number of right shifts to use - * - step_seq2 : How many (positive or negative) steps the seq2 - * pointer should be updated for each new cross - * correlation value - * - * Output: - * - crossCorr : The cross correlation in Q-rShift - */ - -void WebRtcNetEQ_40BitAccCrossCorr(int32_t *crossCorr, - int16_t *seq1, - int16_t *seq2, - int16_t dimSeq, - int16_t dimCrossCorr, - int16_t rShift, - int16_t step_seq2) -{ - int i, j; - int16_t *seq1Ptr, *seq2Ptr; - int64_t acc; - - for (i = 0; i < dimCrossCorr; i++) - { - /* Set the pointer to the static vector, set the pointer to - the sliding vector and initialize crossCorr */ - seq1Ptr = seq1; - seq2Ptr = seq2 + (step_seq2 * i); - acc = 0; - - /* Perform the cross correlation */ - for (j = 0; j < dimSeq; j++) - { - acc += WEBRTC_SPL_MUL_16_16((*seq1Ptr), (*seq2Ptr)); - seq1Ptr++; - seq2Ptr++; - } - - (*crossCorr) = (int32_t) (acc >> rShift); - crossCorr++; - } -} - -/**************************************************************************** - * WebRtcNetEQ_40BitAccDotW16W16(...) - * - * Calculates the dot product between two vectors (int16_t) - * - * Input: - * - vector1 : Vector 1 - * - vector2 : Vector 2 - * - len : Number of samples in vector - * Should be no larger than 1024 to avoid overflow. - * - scaling : The number of left shifts required to avoid overflow - * in the dot product - * Return value : The dot product - */ - -int32_t WebRtcNetEQ_40BitAccDotW16W16(int16_t *vector1, - int16_t *vector2, - int len, - int scaling) -{ - int32_t sum; - int i; - int64_t acc; - - acc = 0; - for (i = 0; i < len; i++) - { - acc += WEBRTC_SPL_MUL_16_16(*vector1++, *vector2++); - } - - sum = (int32_t) (acc >> scaling); - - return(sum); -} - -#endif /* WEBRTC_NETEQ_40BITACC_TEST */ - -/**************************************************************************** - * WebRtcNetEQ_DSPInit(...) - * - * Initializes DSP side of NetEQ. - * - * Input: - * - inst : NetEq DSP instance - * - fs : Initial sample rate (may change when decoding data) - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - * : non-zero - error - */ - -int WebRtcNetEQ_DSPInit(DSPInst_t *inst, uint16_t fs) -{ - - int res = 0; - int16_t fs_mult; - - /* Pointers and values to save before clearing the instance */ -#ifdef NETEQ_CNG_CODEC - void *savedPtr1 = inst->CNG_Codec_inst; -#endif - void *savedPtr2 = inst->pw16_readAddress; - void *savedPtr3 = inst->pw16_writeAddress; - void *savedPtr4 = inst->main_inst; -#ifdef NETEQ_VAD - void *savedVADptr = inst->VADInst.VADState; - VADInitFunction savedVADinit = inst->VADInst.initFunction; - VADSetmodeFunction savedVADsetmode = inst->VADInst.setmodeFunction; - VADFunction savedVADfunc = inst->VADInst.VADFunction; - int16_t savedVADEnabled = inst->VADInst.VADEnabled; - int savedVADMode = inst->VADInst.VADMode; -#endif /* NETEQ_VAD */ - DSPStats_t saveStats; - int16_t saveMsPerCall = inst->millisecondsPerCall; - enum BGNMode saveBgnMode = inst->BGNInst.bgnMode; -#ifdef NETEQ_STEREO - MasterSlaveInfo* saveMSinfo = inst->msInfo; -#endif - - /* copy contents of statInst to avoid clearing */WEBRTC_SPL_MEMCPY_W16(&saveStats, &(inst->statInst), - sizeof(DSPStats_t)/sizeof(int16_t)); - - /* check that the sample rate is valid */ - if ((fs != 8000) -#ifdef NETEQ_WIDEBAND - &&(fs!=16000) -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - &&(fs!=32000) -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - &&(fs!=48000) -#endif - ) - { - /* invalid rate */ - return (CODEC_DB_UNSUPPORTED_FS); - } - - /* calcualte fs/8000 */ - fs_mult = WebRtcSpl_DivW32W16ResW16(fs, 8000); - - /* Set everything to zero since most variables should be zero at start */ - WebRtcSpl_MemSetW16((int16_t *) inst, 0, sizeof(DSPInst_t) / sizeof(int16_t)); - - /* Restore saved pointers */ -#ifdef NETEQ_CNG_CODEC - inst->CNG_Codec_inst = (CNG_dec_inst *)savedPtr1; -#endif - inst->pw16_readAddress = (int16_t *) savedPtr2; - inst->pw16_writeAddress = (int16_t *) savedPtr3; - inst->main_inst = savedPtr4; -#ifdef NETEQ_VAD - inst->VADInst.VADState = savedVADptr; - inst->VADInst.initFunction = savedVADinit; - inst->VADInst.setmodeFunction = savedVADsetmode; - inst->VADInst.VADFunction = savedVADfunc; - inst->VADInst.VADEnabled = savedVADEnabled; - inst->VADInst.VADMode = savedVADMode; -#endif /* NETEQ_VAD */ - - /* Initialize main part */ - inst->fs = fs; - inst->millisecondsPerCall = saveMsPerCall; - inst->timestampsPerCall = inst->millisecondsPerCall * 8 * fs_mult; - inst->ExpandInst.w16_overlap = 5 * fs_mult; - inst->endPosition = 565 * fs_mult; - inst->curPosition = inst->endPosition - inst->ExpandInst.w16_overlap; - inst->w16_seedInc = 1; - inst->uw16_seed = 777; - inst->w16_muteFactor = 16384; /* 1.0 in Q14 */ - inst->w16_frameLen = 3 * inst->timestampsPerCall; /* Dummy initialize to 30ms */ - - inst->w16_speechHistoryLen = 256 * fs_mult; - inst->pw16_speechHistory = &inst->speechBuffer[inst->endPosition - - inst->w16_speechHistoryLen]; - inst->ExpandInst.pw16_overlapVec = &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - - inst->ExpandInst.w16_overlap]); - - /* Reusage of memory in speechBuffer inside Expand */ - inst->ExpandInst.pw16_expVecs[0] = &inst->speechBuffer[0]; - inst->ExpandInst.pw16_expVecs[1] = &inst->speechBuffer[126 * fs_mult]; - inst->ExpandInst.pw16_arState = &inst->speechBuffer[2 * 126 * fs_mult]; - inst->ExpandInst.pw16_arFilter = &inst->speechBuffer[2 * 126 * fs_mult - + UNVOICED_LPC_ORDER]; - /* Ends at 2*126*fs_mult+UNVOICED_LPC_ORDER+(UNVOICED_LPC_ORDER+1) */ - - inst->ExpandInst.w16_expandMuteFactor = 16384; /* 1.0 in Q14 */ - - /* Initialize BGN part */ - inst->BGNInst.pw16_filter[0] = 4096; - inst->BGNInst.w16_scale = 20000; - inst->BGNInst.w16_scaleShift = 24; - inst->BGNInst.w32_energyUpdate = 500000; - inst->BGNInst.w32_energyUpdateLow = 0; - inst->BGNInst.w32_energy = 2500; - inst->BGNInst.w16_initialized = 0; - inst->BGNInst.bgnMode = saveBgnMode; - - /* Recreate statistics counters */WEBRTC_SPL_MEMCPY_W16(&(inst->statInst), &saveStats, - sizeof(DSPStats_t)/sizeof(int16_t)); - -#ifdef NETEQ_STEREO - /* Write back the pointer. */ - inst->msInfo = saveMSinfo; -#endif - -#ifdef NETEQ_CNG_CODEC - if (inst->CNG_Codec_inst!=NULL) - { - /* initialize comfort noise generator */ - res |= WebRtcCng_InitDec(inst->CNG_Codec_inst); - } -#endif - -#ifdef NETEQ_VAD - /* initialize PostDecode VAD instance - (don't bother checking for NULL instance, this is done inside init function) */ - res |= WebRtcNetEQ_InitVAD(&inst->VADInst, fs); -#endif /* NETEQ_VAD */ - - return (res); -} - -/**************************************************************************** - * WebRtcNetEQ_AddressInit(...) - * - * Initializes the shared-memory communication on the DSP side. - * - * Input: - * - inst : NetEQ DSP instance - * - data2McuAddress : Pointer to memory where DSP writes / MCU reads - * - data2DspAddress : Pointer to memory where MCU writes / DSP reads - * - mainInst : NetEQ main instance - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - */ - -int WebRtcNetEQ_AddressInit(DSPInst_t *inst, const void *data2McuAddress, - const void *data2DspAddress, const void *mainInst) -{ - - /* set shared-memory addresses in the DSP instance */ - inst->pw16_readAddress = (int16_t *) data2DspAddress; - inst->pw16_writeAddress = (int16_t *) data2McuAddress; - - /* set pointer to main NetEQ instance */ - inst->main_inst = (void *) mainInst; - - /* set output frame size to 10 ms = 80 samples in narrowband */ - inst->millisecondsPerCall = 10; - inst->timestampsPerCall = 80; - - return (0); - -} - -/**************************************************************************** - * NETEQDSP_clearInCallStats(...) - * - * Reset in-call statistics variables on DSP side. - * - * Input: - * - inst : NetEQ DSP instance - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - */ - -int WebRtcNetEQ_ClearInCallStats(DSPInst_t *inst) -{ - /* Reset statistics counters */ - inst->statInst.accelerateLength = 0; - inst->statInst.expandLength = 0; - inst->statInst.preemptiveLength = 0; - inst->statInst.addedSamples = 0; - return (0); -} - -/**************************************************************************** - * WebRtcNetEQ_ClearPostCallStats(...) - * - * Reset post-call statistics variables on DSP side. - * - * Input: - * - inst : NetEQ DSP instance - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - */ - -int WebRtcNetEQ_ClearPostCallStats(DSPInst_t *inst) -{ - - /* Reset statistics counters */ - inst->statInst.expandedVoiceSamples = 0; - inst->statInst.expandedNoiseSamples = 0; - return (0); -} - -/**************************************************************************** - * WebRtcNetEQ_ClearActivityStats(...) - * - * Reset processing activity statistics. - * - * Input: - * - inst : NetEQ DSP instance - * - * Output: - * - inst : Updated instance - * - */ - -void WebRtcNetEQ_ClearActivityStats(DSPInst_t *inst) { - memset(&inst->activity_stats, 0, sizeof(ActivityStats)); -} - -#ifdef NETEQ_VAD - -/**************************************************************************** - * WebRtcNetEQ_InitVAD(...) - * - * Initializes post-decode VAD instance. - * - * Input: - * - VADinst : PostDecodeVAD instance - * - fs : Initial sample rate - * - * Output: - * - VADinst : Updated instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_InitVAD(PostDecodeVAD_t *VADInst, uint16_t fs) -{ - - int res = 0; - - /* initially, disable the post-decode VAD */ - VADInst->VADEnabled = 0; - - if (VADInst->VADState != NULL /* if VAD state is provided */ - && VADInst->initFunction != NULL /* and all function ... */ - && VADInst->setmodeFunction != NULL /* ... pointers ... */ - && VADInst->VADFunction != NULL) /* ... are defined */ - { - res = VADInst->initFunction( VADInst->VADState ); /* call VAD init function */ - res |= WebRtcNetEQ_SetVADModeInternal( VADInst, VADInst->VADMode ); - - if (res!=0) - { - /* something is wrong; play it safe and set the VADstate to NULL */ - VADInst->VADState = NULL; - } - else if (fs<=16000) - { - /* enable VAD if NB or WB (VAD cannot handle SWB) */ - VADInst->VADEnabled = 1; - } - } - - /* reset SID/CNG interval counter */ - VADInst->SIDintervalCounter = 0; - - /* initialize with active-speaker decision */ - VADInst->VADDecision = 1; - - return(res); - -} - -/**************************************************************************** - * WebRtcNetEQ_SetVADModeInternal(...) - * - * Set the VAD mode in the VAD struct, and communicate it to the VAD instance - * if it exists. - * - * Input: - * - VADinst : PostDecodeVAD instance - * - mode : Mode number passed on to the VAD function - * - * Output: - * - VADinst : Updated instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_SetVADModeInternal(PostDecodeVAD_t *VADInst, int mode) -{ - - int res = 0; - - VADInst->VADMode = mode; - - if (VADInst->VADState != NULL) - { - /* call setmode function */ - res = VADInst->setmodeFunction(VADInst->VADState, mode); - } - - return(res); - -} - -#endif /* NETEQ_VAD */ - -/**************************************************************************** - * WebRtcNetEQ_FlushSpeechBuffer(...) - * - * Flush the speech buffer. - * - * Input: - * - inst : NetEq DSP instance - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - * : non-zero - error - */ - -int WebRtcNetEQ_FlushSpeechBuffer(DSPInst_t *inst) -{ - int16_t fs_mult; - - /* calcualte fs/8000 */ - fs_mult = WebRtcSpl_DivW32W16ResW16(inst->fs, 8000); - - /* clear buffer */ - WebRtcSpl_MemSetW16(inst->speechBuffer, 0, SPEECH_BUF_SIZE); - inst->endPosition = 565 * fs_mult; - inst->curPosition = inst->endPosition - inst->ExpandInst.w16_overlap; - - return 0; -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,807 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains some DSP initialization functions, - * constant table definitions and other parameters. - * Also contains definitions of all DSP-side data structures. - */ - - -#ifndef DSP_H -#define DSP_H - -#include "typedefs.h" - -#include "webrtc_cng.h" - -#include "codec_db_defines.h" -#include "neteq_defines.h" -#include "neteq_statistics.h" - -#ifdef NETEQ_ATEVENT_DECODE -#include "dtmf_tonegen.h" -#endif - - - -/*****************************/ -/* Pre-processor definitions */ -/*****************************/ - -/* FSMULT is the sample rate divided by 8000 */ -#if defined(NETEQ_48KHZ_WIDEBAND) - #define FSMULT 6 -#elif defined(NETEQ_32KHZ_WIDEBAND) - #define FSMULT 4 -#elif defined(NETEQ_WIDEBAND) - #define FSMULT 2 -#else - #define FSMULT 1 -#endif - -/* Size of the speech buffer (or synchronization buffer). */ -/* 60 ms decoding + 10 ms syncbuff + 0.625ms lookahead */ -#define SPEECH_BUF_SIZE (565 * FSMULT) - -/* Misc definitions */ -#define BGN_LPC_ORDER (4 + FSMULT) /* 5, 6, 8, or 10 */ -#define UNVOICED_LPC_ORDER 6 -#define RANDVEC_NO_OF_SAMPLES 256 - -/* Number of milliseconds to remove/add during accelerate/pre-emptive expand - under BGNonly operation */ -#define DEFAULT_TIME_ADJUST 8 - -/* Number of RecOut calls without CNG/SID before re-enabling post-decode VAD */ -#define POST_DECODE_VAD_AUTO_ENABLE 3000 - -/* 8kHz windowing in Q15 (over 5 samples) */ -#define NETEQ_OVERLAP_WINMUTE_8KHZ_START 27307 -#define NETEQ_OVERLAP_WINMUTE_8KHZ_INC -5461 -#define NETEQ_OVERLAP_WINUNMUTE_8KHZ_START 5461 -#define NETEQ_OVERLAP_WINUNMUTE_8KHZ_INC 5461 -/* 16kHz windowing in Q15 (over 10 samples) */ -#define NETEQ_OVERLAP_WINMUTE_16KHZ_START 29789 -#define NETEQ_OVERLAP_WINMUTE_16KHZ_INC -2979 -#define NETEQ_OVERLAP_WINUNMUTE_16KHZ_START 2979 -#define NETEQ_OVERLAP_WINUNMUTE_16KHZ_INC 2979 -/* 32kHz windowing in Q15 (over 20 samples) */ -#define NETEQ_OVERLAP_WINMUTE_32KHZ_START 31208 -#define NETEQ_OVERLAP_WINMUTE_32KHZ_INC -1560 -#define NETEQ_OVERLAP_WINUNMUTE_32KHZ_START 1560 -#define NETEQ_OVERLAP_WINUNMUTE_32KHZ_INC 1560 -/* 48kHz windowing in Q15 (over 30 samples) */ -#define NETEQ_OVERLAP_WINMUTE_48KHZ_START 31711 -#define NETEQ_OVERLAP_WINMUTE_48KHZ_INC -1057 -#define NETEQ_OVERLAP_WINUNMUTE_48KHZ_START 1057 -#define NETEQ_OVERLAP_WINUNMUTE_48KHZ_INC 1057 - -/* Fade BGN towards zero after this many Expand calls */ -#define FADE_BGN_TIME 200 - - -/*******************/ -/* Constant tables */ -/*******************/ - -extern const int16_t WebRtcNetEQ_kDownsample8kHzTbl[]; -extern const int16_t WebRtcNetEQ_kDownsample16kHzTbl[]; -extern const int16_t WebRtcNetEQ_kDownsample32kHzTbl[]; -extern const int16_t WebRtcNetEQ_kDownsample48kHzTbl[]; -extern const int16_t WebRtcNetEQ_kRandnTbl[]; -extern const int16_t WebRtcNetEQ_kMixFractionFuncTbl[]; -extern const int16_t WebRtcNetEQ_k1049div[]; -extern const int16_t WebRtcNetEQ_k2097div[]; -extern const int16_t WebRtcNetEQ_k5243div[]; - - - -/************/ -/* Typedefs */ -/************/ - -enum BGNMode -{ - BGN_ON, /* default "normal" behavior with eternal noise */ - BGN_FADE, /* noise fades to zero after some time */ - BGN_OFF /* background noise is always zero */ -}; - -#ifdef NETEQ_STEREO -enum MasterSlaveMode -{ - NETEQ_MONO, /* stand-alone instance */ - NETEQ_MASTER, /* master instance in a spatial/stereo configuration */ - NETEQ_SLAVE /* slave instance in a spatial/stereo configuration */ -}; - -enum MasterSlaveExtraInfo -{ - NO_INFO, /* no info to convey */ - ACC_FAIL, /* signal that accelerate failed */ - PE_EXP_FAIL, /* signal that pre-emptive expand failed */ - DTMF_OVERDUB, /* signal that DTMF overdub is generated */ - DTMF_ONLY /* signal that DTMF only is played */ -}; -#endif - -/****************************/ -/* DSP-side data structures */ -/****************************/ - -/* Background noise (BGN) instance for storing BGN parameters - (sub-instance of NETEQDSP_inst) */ -typedef struct BGNInst_t_ -{ - - int32_t w32_energy; - int32_t w32_energyMax; - int32_t w32_energyUpdate; - int32_t w32_energyUpdateLow; - int16_t pw16_filterState[BGN_LPC_ORDER]; - int16_t pw16_filter[BGN_LPC_ORDER + 1]; - int16_t w16_mutefactor; - int16_t w16_scale; - int16_t w16_scaleShift; - int16_t w16_initialized; - enum BGNMode bgnMode; - -} BGNInst_t; - -/* Expansion instance (sub-instance of NETEQDSP_inst) */ -typedef struct ExpandInst_t_ -{ - - int16_t w16_overlap; /* Constant, 5 for NB and 10 for WB */ - int16_t w16_consecExp; /* Number of consecutive expand calls */ - int16_t *pw16_arFilter; /* length [UNVOICED_LPC_ORDER+1] */ - int16_t *pw16_arState; /* length [UNVOICED_LPC_ORDER] */ - int16_t w16_arGain; - int16_t w16_arGainScale; - int16_t w16_vFraction; /* Q14 */ - int16_t w16_currentVFraction; /* Q14 */ - int16_t *pw16_expVecs[2]; - int16_t w16_lags[3]; - int16_t w16_maxLag; - int16_t *pw16_overlapVec; /* last samples of speech history */ - int16_t w16_lagsDirection; - int16_t w16_lagsPosition; - int16_t w16_expandMuteFactor; /* Q14 */ - int16_t w16_stopMuting; - int16_t w16_onset; - int16_t w16_muteSlope; /* Q20 */ - -} ExpandInst_t; - -#ifdef NETEQ_VAD - -/* - * VAD function pointer types, replicating the typedefs in webrtc_neteq_internal.h. - * These function pointers match the definitions of WebRtc VAD functions WebRtcVad_Init, - * WebRtcVad_set_mode and WebRtcVad_Process, respectively, all found in webrtc_vad.h. - */ -typedef int (*VADInitFunction)(void *VAD_inst); -typedef int (*VADSetmodeFunction)(void *VAD_inst, int mode); -typedef int (*VADFunction)(void *VAD_inst, int fs, int16_t *frame, - int frameLen); - -/* Post-decode VAD instance (sub-instance of NETEQDSP_inst) */ -typedef struct PostDecodeVAD_t_ -{ - - void *VADState; /* pointer to a VAD instance */ - - int16_t VADEnabled; /* 1 if enabled, 0 if disabled */ - int VADMode; /* mode parameter to pass to the VAD function */ - int VADDecision; /* 1 for active, 0 for passive */ - int16_t SIDintervalCounter; /* reset when decoding CNG/SID frame, - increment for each recout call */ - - /* Function pointers */ - VADInitFunction initFunction; /* VAD init function */ - VADSetmodeFunction setmodeFunction; /* VAD setmode function */ - VADFunction VADFunction; /* VAD function */ - -} PostDecodeVAD_t; - -#endif /* NETEQ_VAD */ - -#ifdef NETEQ_STEREO -#define MAX_MS_DECODES 10 - -typedef struct -{ - /* Stand-alone, master, or slave */ - enum MasterSlaveMode msMode; - - enum MasterSlaveExtraInfo extraInfo; - - uint16_t instruction; - int16_t distLag; - int16_t corrLag; - int16_t bestIndex; - - uint32_t endTimestamp; - uint16_t samplesLeftWithOverlap; - -} MasterSlaveInfo; -#endif - - -/* "Main" NetEQ DSP instance */ -typedef struct DSPInst_t_ -{ - - /* MCU/DSP Communication layer */ - int16_t *pw16_readAddress; - int16_t *pw16_writeAddress; - void *main_inst; - - /* Output frame size in ms and samples */ - int16_t millisecondsPerCall; - int16_t timestampsPerCall; - - /* - * Example of speech buffer - * - * ----------------------------------------------------------- - * | History T-60 to T | Future | - * ----------------------------------------------------------- - * ^ ^ - * | | - * curPosition endPosition - * - * History is gradually shifted out to the left when inserting - * new data at the end. - */ - - int16_t speechBuffer[SPEECH_BUF_SIZE]; /* History/future speech buffer */ - int curPosition; /* Next sample to play */ - int endPosition; /* Position that ends future data */ - uint32_t endTimestamp; /* Timestamp value at end of future data */ - uint32_t videoSyncTimestamp; /* (Estimated) timestamp of the last - played sample (usually same as - endTimestamp-(endPosition-curPosition) - except during Expand and CNG) */ - uint16_t fs; /* sample rate in Hz */ - int16_t w16_frameLen; /* decoder frame length in samples */ - int16_t w16_mode; /* operation used during last RecOut call */ - int16_t w16_muteFactor; /* speech mute factor in Q14 */ - int16_t *pw16_speechHistory; /* beginning of speech history during Expand */ - int16_t w16_speechHistoryLen; /* 256 for NB and 512 for WB */ - - /* random noise seed parameters */ - int16_t w16_seedInc; - uint32_t uw16_seed; - - /* VQmon related variable */ - int16_t w16_concealedTS; - - /*****************/ - /* Sub-instances */ - /*****************/ - - /* Decoder data */ - CodecFuncInst_t codec_ptr_inst; - -#ifdef NETEQ_CNG_CODEC - /* CNG "decoder" instance */ - CNG_dec_inst *CNG_Codec_inst; -#endif /* NETEQ_CNG_CODEC */ - -#ifdef NETEQ_ATEVENT_DECODE - /* DTMF generator instance */ - dtmf_tone_inst_t DTMFInst; -#endif /* NETEQ_CNG_CODEC */ - -#ifdef NETEQ_VAD - /* Post-decode VAD instance */ - PostDecodeVAD_t VADInst; -#endif /* NETEQ_VAD */ - - /* Expand instance (defined above) */ - ExpandInst_t ExpandInst; - - /* Background noise instance (defined above) */ - BGNInst_t BGNInst; - - /* Internal statistics instance */ - DSPStats_t statInst; - - /* Internal instance for short-term processing activity. */ - ActivityStats activity_stats; - -#ifdef NETEQ_STEREO - /* Pointer to Master/Slave info */ - MasterSlaveInfo *msInfo; -#endif - -} DSPInst_t; - - -/*************************/ -/* Function declarations */ -/*************************/ - -/**************************************************************************** - * WebRtcNetEQ_DSPInit(...) - * - * Initializes DSP side of NetEQ. - * - * Input: - * - inst : NetEq DSP instance - * - fs : Initial sample rate (may change when decoding data) - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - * : non-zero - error - */ - -int WebRtcNetEQ_DSPInit(DSPInst_t *inst, uint16_t fs); - -/**************************************************************************** - * WebRtcNetEQ_AddressInit(...) - * - * Initializes the shared-memory communication on the DSP side. - * - * Input: - * - inst : NetEQ DSP instance - * - data2McuAddress : Pointer to memory where DSP writes / MCU reads - * - data2DspAddress : Pointer to memory where MCU writes / DSP reads - * - mainInst : NetEQ main instance - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - */ - -int WebRtcNetEQ_AddressInit(DSPInst_t *inst, const void *data2McuAddress, - const void *data2DspAddress, const void *mainInst); - -/**************************************************************************** - * WebRtcNetEQ_ClearInCallStats(...) - * - * Reset in-call statistics variables on DSP side. - * - * Input: - * - inst : NetEQ DSP instance - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - */ - -int WebRtcNetEQ_ClearInCallStats(DSPInst_t *inst); - -/**************************************************************************** - * WebRtcNetEQ_ClearPostCallStats(...) - * - * Reset post-call statistics variables on DSP side. - * - * Input: - * - inst : NetEQ DSP instance - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - */ - -int WebRtcNetEQ_ClearPostCallStats(DSPInst_t *inst); - -/**************************************************************************** - * WebRtcNetEQ_ClearActivityStats(...) - * - * Reset processing activity statistics. - * - * Input: - * - inst : NetEQ DSP instance - * - * Output: - * - inst : Updated instance - * - */ - -void WebRtcNetEQ_ClearActivityStats(DSPInst_t *inst); - -/**************************************************************************** - * WebRtcNetEQ_RecOutInternal(...) - * - * This function asks NetEQ for more speech/audio data. - * - * Input: - * - inst : NetEQ instance, i.e. the user that requests more - * speech/audio data. - * - outdata : Pointer to a memory space where the output data - * should be stored. - * - BGNonly : If non-zero, RecOut will only produce background - * noise. It will still draw packets from the packet - * buffer, but they will never be decoded. - * - av_sync : 1 if NetEQ is in AV-sync, 0 otherwise. - * - * Output: - * - inst : Updated user information - * - len : Number of samples that were outputted from NetEq - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RecOutInternal(DSPInst_t *inst, int16_t *pw16_outData, - int16_t *pw16_len, int16_t BGNonly, int av_sync); - -/**************************************************************************** - * WebRtcNetEQ_Normal(...) - * - * This function has the possibility to modify data that is played out in Normal - * mode, for example adjust the gain of the signal. The length of the signal - * can not be changed. - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector - * - decoded : Pointer to vector of new data from decoder - * - len : Number of input samples - * - * Output: - * - inst : Updated user information - * - pw16_len : Pointer to varibale where the number of samples - * produced will be written - * - * Return value : >=0 - Number of samples written to outData - * -1 - Error - */ - -int WebRtcNetEQ_Normal(DSPInst_t *inst, -#ifdef SCRATCH - int16_t *pw16_scratchPtr, -#endif - int16_t *pw16_decoded, int16_t len, - int16_t *pw16_outData, int16_t *pw16_len); - -/**************************************************************************** - * WebRtcNetEQ_Expand(...) - * - * This function produces one "chunk" of expansion data (PLC audio). The - * lenght of the produced audio depends on the speech history. - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector - * - BGNonly : If non-zero, Expand will only produce background - * noise. - * - pw16_len : Desired number of samples (only for BGN mode). - * - * Output: - * - inst : Updated user information - * - outdata : Pointer to a memory space where the output data - * should be stored - * - pw16_len : Number of samples that were outputted from NetEq - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_Expand(DSPInst_t *inst, -#ifdef SCRATCH - int16_t *pw16_scratchPtr, -#endif - int16_t *pw16_outData, int16_t *pw16_len, - int16_t BGNonly); - -/**************************************************************************** - * WebRtcNetEQ_GenerateBGN(...) - * - * This function generates and writes len samples of background noise to the - * output vector. The Expand function will be called repeteadly until the - * correct number of samples is produced. - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector - * - len : Desired length of produced BGN. - * - * - * Output: - * - pw16_outData : Pointer to a memory space where the output data - * should be stored - * - * Return value : >=0 - Number of noise samples produced and written - * to output - * -1 - Error - */ - -int WebRtcNetEQ_GenerateBGN(DSPInst_t *inst, -#ifdef SCRATCH - int16_t *pw16_scratchPtr, -#endif - int16_t *pw16_outData, int16_t len); - -/**************************************************************************** - * WebRtcNetEQ_PreEmptiveExpand(...) - * - * This function tries to extend the audio data by repeating one or several - * pitch periods. The operation is only carried out if the correlation is - * strong or if the signal energy is very low. The algorithm is the - * reciprocal of the Accelerate algorithm. - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector. - * - decoded : Pointer to newly decoded speech. - * - len : Length of decoded speech. - * - oldDataLen : Length of the part of decoded that has already been played out. - * - BGNonly : If non-zero, Pre-emptive Expand will only copy - * the first DEFAULT_TIME_ADJUST seconds of the - * input and append to the end. No signal matching is - * done. - * - * Output: - * - inst : Updated instance - * - outData : Pointer to a memory space where the output data - * should be stored. The vector must be at least - * min(len + 120*fs/8000, NETEQ_MAX_OUTPUT_SIZE) - * elements long. - * - pw16_len : Number of samples written to outData. - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_PreEmptiveExpand(DSPInst_t *inst, -#ifdef SCRATCH - int16_t *pw16_scratchPtr, -#endif - const int16_t *pw16_decoded, int len, int oldDataLen, - int16_t *pw16_outData, int16_t *pw16_len, - int16_t BGNonly); - -/**************************************************************************** - * WebRtcNetEQ_Accelerate(...) - * - * This function tries to shorten the audio data by removing one or several - * pitch periods. The operation is only carried out if the correlation is - * strong or if the signal energy is very low. - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector. - * - decoded : Pointer to newly decoded speech. - * - len : Length of decoded speech. - * - BGNonly : If non-zero, Accelerate will only remove the last - * DEFAULT_TIME_ADJUST seconds of the intput. - * No signal matching is done. - * - * - * Output: - * - inst : Updated instance - * - outData : Pointer to a memory space where the output data - * should be stored - * - pw16_len : Number of samples written to outData. - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_Accelerate(DSPInst_t *inst, -#ifdef SCRATCH - int16_t *pw16_scratchPtr, -#endif - const int16_t *pw16_decoded, int len, - int16_t *pw16_outData, int16_t *pw16_len, - int16_t BGNonly); - -/**************************************************************************** - * WebRtcNetEQ_Merge(...) - * - * This function is used to merge new data from the decoder to the exisiting - * stream in the synchronization buffer. The merge operation is typically - * done after a packet loss, where the end of the expanded data does not - * fit naturally with the new decoded data. - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector. - * - decoded : Pointer to new decoded speech. - * - len : Number of samples in pw16_decoded. - * - * - * Output: - * - inst : Updated user information - * - outData : Pointer to a memory space where the output data - * should be stored - * - pw16_len : Number of samples written to pw16_outData - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_Merge(DSPInst_t *inst, -#ifdef SCRATCH - int16_t *pw16_scratchPtr, -#endif - int16_t *pw16_decoded, int len, int16_t *pw16_outData, - int16_t *pw16_len); - -/**************************************************************************** - * WebRtcNetEQ_Cng(...) - * - * This function produces CNG according to RFC 3389 - * - * Input: - * - inst : NetEQ DSP instance - * - len : Number of samples to produce - * - * Output: - * - pw16_outData : Output CNG - * - * Return value : 0 - Ok - * <0 - Error - */ - -#ifdef NETEQ_CNG_CODEC -/* Must compile NetEQ with CNG support to enable this function */ - -int WebRtcNetEQ_Cng(DSPInst_t *inst, int16_t *pw16_outData, int len); - -#endif /* NETEQ_CNG_CODEC */ - -/**************************************************************************** - * WebRtcNetEQ_BGNUpdate(...) - * - * This function updates the background noise parameter estimates. - * - * Input: - * - inst : NetEQ instance, where the speech history is stored. - * - scratchPtr : Pointer to scratch vector. - * - * Output: - * - inst : Updated information about the BGN characteristics. - * - * Return value : No return value - */ - -void WebRtcNetEQ_BGNUpdate( -#ifdef SCRATCH - DSPInst_t *inst, int16_t *pw16_scratchPtr -#else - DSPInst_t *inst -#endif - ); - -#ifdef NETEQ_VAD -/* Functions used by post-decode VAD */ - -/**************************************************************************** - * WebRtcNetEQ_InitVAD(...) - * - * Initializes post-decode VAD instance. - * - * Input: - * - VADinst : PostDecodeVAD instance - * - fs : Initial sample rate - * - * Output: - * - VADinst : Updated instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_InitVAD(PostDecodeVAD_t *VADInst, uint16_t fs); - -/**************************************************************************** - * WebRtcNetEQ_SetVADModeInternal(...) - * - * Set the VAD mode in the VAD struct, and communicate it to the VAD instance - * if it exists. - * - * Input: - * - VADinst : PostDecodeVAD instance - * - mode : Mode number passed on to the VAD function - * - * Output: - * - VADinst : Updated instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_SetVADModeInternal(PostDecodeVAD_t *VADInst, int mode); - -#endif /* NETEQ_VAD */ - -/**************************************************************************** - * WebRtcNetEQ_FlushSpeechBuffer(...) - * - * Flush the speech buffer. - * - * Input: - * - inst : NetEq DSP instance - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - * : non-zero - error - */ - -int WebRtcNetEQ_FlushSpeechBuffer(DSPInst_t *inst); - -#ifndef WEBRTC_NETEQ_40BITACC_TEST - -#include "signal_processing_library.h" -/* Map to regular SPL functions */ -#define WebRtcNetEQ_CrossCorr WebRtcSpl_CrossCorrelation -#define WebRtcNetEQ_DotW16W16 WebRtcSpl_DotProductWithScale - -#else /* WEBRTC_NETEQ_40BITACC_TEST defined */ -/* Run NetEQ with simulated 40-bit accumulator to run bit-exact to a DSP - implementation where the main (splib and NetEQ) functions have been - 40-bit optimized. */ - -/* Map to special 40-bit optimized functions, defined below */ -#define WebRtcNetEQ_CrossCorr WebRtcNetEQ_40BitAccCrossCorr -#define WebRtcNetEQ_DotW16W16 WebRtcNetEQ_40BitAccDotW16W16 - -/**************************************************************************** - * WebRtcNetEQ_40BitAccCrossCorr(...) - * - * Calculates the Cross correlation between two sequences seq1 and seq2. Seq1 - * is fixed and seq2 slides as the pointer is increased with step - * - * Input: - * - seq1 : First sequence (fixed throughout the correlation) - * - seq2 : Second sequence (slided step_seq2 for each - * new correlation) - * - dimSeq : Number of samples to use in the cross correlation. - * Should be no larger than 1024 to avoid overflow. - * - dimCrossCorr : Number of CrossCorrelations to calculate (start - * position for seq2 is updated for each new one) - * - rShift : Number of right shifts to use - * - step_seq2 : How many (positive or negative) steps the seq2 - * pointer should be updated for each new cross - * correlation value - * - * Output: - * - crossCorr : The cross correlation in Q-rShift - */ - -void WebRtcNetEQ_40BitAccCrossCorr(int32_t *crossCorr, int16_t *seq1, - int16_t *seq2, int16_t dimSeq, - int16_t dimCrossCorr, int16_t rShift, - int16_t step_seq2); - -/**************************************************************************** - * WebRtcNetEQ_40BitAccDotW16W16(...) - * - * Calculates the dot product between two vectors (int16_t) - * - * Input: - * - vector1 : Vector 1 - * - vector2 : Vector 2 - * - len : Number of samples in vector - * Should be no larger than 1024 to avoid overflow. - * - scaling : The number of right shifts (after multiplication) - * required to avoid overflow in the dot product. - * Return value : The dot product - */ - -int32_t WebRtcNetEQ_40BitAccDotW16W16(int16_t *vector1, int16_t *vector2, - int len, int scaling); - -#endif /* WEBRTC_NETEQ_40BITACC_TEST */ - -#endif /* DSP_H */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helper.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helper.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helper.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helper.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/dsp_helper.h" + +#include +#include // Access to memset. + +#include // Access to min, max. + +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" + +namespace webrtc { + +// Table of constants used in method DspHelper::ParabolicFit(). +const int16_t DspHelper::kParabolaCoefficients[17][3] = { + { 120, 32, 64 }, + { 140, 44, 75 }, + { 150, 50, 80 }, + { 160, 57, 85 }, + { 180, 72, 96 }, + { 200, 89, 107 }, + { 210, 98, 112 }, + { 220, 108, 117 }, + { 240, 128, 128 }, + { 260, 150, 139 }, + { 270, 162, 144 }, + { 280, 174, 149 }, + { 300, 200, 160 }, + { 320, 228, 171 }, + { 330, 242, 176 }, + { 340, 257, 181 }, + { 360, 288, 192 } }; + +// Filter coefficients used when downsampling from the indicated sample rates +// (8, 16, 32, 48 kHz) to 4 kHz. Coefficients are in Q12. The corresponding Q0 +// values are provided in the comments before each array. + +// Q0 values: {0.3, 0.4, 0.3}. +const int16_t DspHelper::kDownsample8kHzTbl[3] = { 1229, 1638, 1229 }; + +// Q0 values: {0.15, 0.2, 0.3, 0.2, 0.15}. +const int16_t DspHelper::kDownsample16kHzTbl[5] = { 614, 819, 1229, 819, 614 }; + +// Q0 values: {0.1425, 0.1251, 0.1525, 0.1628, 0.1525, 0.1251, 0.1425}. +const int16_t DspHelper::kDownsample32kHzTbl[7] = { + 584, 512, 625, 667, 625, 512, 584 }; + +// Q0 values: {0.2487, 0.0952, 0.1042, 0.1074, 0.1042, 0.0952, 0.2487}. +const int16_t DspHelper::kDownsample48kHzTbl[7] = { + 1019, 390, 427, 440, 427, 390, 1019 }; + +int DspHelper::RampSignal(const int16_t* input, + size_t length, + int factor, + int increment, + int16_t* output) { + int factor_q20 = (factor << 6) + 32; + // TODO(hlundin): Add 32 to factor_q20 when converting back to Q14? + for (size_t i = 0; i < length; ++i) { + output[i] = (factor * input[i] + 8192) >> 14; + factor_q20 += increment; + factor_q20 = std::max(factor_q20, 0); // Never go negative. + factor = std::min(factor_q20 >> 6, 16384); + } + return factor; +} + +int DspHelper::RampSignal(int16_t* signal, + size_t length, + int factor, + int increment) { + return RampSignal(signal, length, factor, increment, signal); +} + +int DspHelper::RampSignal(AudioMultiVector* signal, + size_t start_index, + size_t length, + int factor, + int increment) { + assert(start_index + length <= signal->Size()); + if (start_index + length > signal->Size()) { + // Wrong parameters. Do nothing and return the scale factor unaltered. + return factor; + } + int end_factor = 0; + // Loop over the channels, starting at the same |factor| each time. + for (size_t channel = 0; channel < signal->Channels(); ++channel) { + end_factor = + RampSignal(&(*signal)[channel][start_index], length, factor, increment); + } + return end_factor; +} + +void DspHelper::PeakDetection(int16_t* data, int data_length, + int num_peaks, int fs_mult, + int* peak_index, int16_t* peak_value) { + int16_t min_index = 0; + int16_t max_index = 0; + + for (int i = 0; i <= num_peaks - 1; i++) { + if (num_peaks == 1) { + // Single peak. The parabola fit assumes that an extra point is + // available; worst case it gets a zero on the high end of the signal. + // TODO(hlundin): This can potentially get much worse. It breaks the + // API contract, that the length of |data| is |data_length|. + data_length++; + } + + peak_index[i] = WebRtcSpl_MaxIndexW16(data, data_length - 1); + + if (i != num_peaks - 1) { + min_index = std::max(0, peak_index[i] - 2); + max_index = std::min(data_length - 1, peak_index[i] + 2); + } + + if ((peak_index[i] != 0) && (peak_index[i] != (data_length - 2))) { + ParabolicFit(&data[peak_index[i] - 1], fs_mult, &peak_index[i], + &peak_value[i]); + } else { + if (peak_index[i] == data_length - 2) { + if (data[peak_index[i]] > data[peak_index[i] + 1]) { + ParabolicFit(&data[peak_index[i] - 1], fs_mult, &peak_index[i], + &peak_value[i]); + } else if (data[peak_index[i]] <= data[peak_index[i] + 1]) { + // Linear approximation. + peak_value[i] = (data[peak_index[i]] + data[peak_index[i] + 1]) >> 1; + peak_index[i] = (peak_index[i] * 2 + 1) * fs_mult; + } + } else { + peak_value[i] = data[peak_index[i]]; + peak_index[i] = peak_index[i] * 2 * fs_mult; + } + } + + if (i != num_peaks - 1) { + memset(&data[min_index], 0, + sizeof(data[0]) * (max_index - min_index + 1)); + } + } +} + +void DspHelper::ParabolicFit(int16_t* signal_points, int fs_mult, + int* peak_index, int16_t* peak_value) { + uint16_t fit_index[13]; + if (fs_mult == 1) { + fit_index[0] = 0; + fit_index[1] = 8; + fit_index[2] = 16; + } else if (fs_mult == 2) { + fit_index[0] = 0; + fit_index[1] = 4; + fit_index[2] = 8; + fit_index[3] = 12; + fit_index[4] = 16; + } else if (fs_mult == 4) { + fit_index[0] = 0; + fit_index[1] = 2; + fit_index[2] = 4; + fit_index[3] = 6; + fit_index[4] = 8; + fit_index[5] = 10; + fit_index[6] = 12; + fit_index[7] = 14; + fit_index[8] = 16; + } else { + fit_index[0] = 0; + fit_index[1] = 1; + fit_index[2] = 3; + fit_index[3] = 4; + fit_index[4] = 5; + fit_index[5] = 7; + fit_index[6] = 8; + fit_index[7] = 9; + fit_index[8] = 11; + fit_index[9] = 12; + fit_index[10] = 13; + fit_index[11] = 15; + fit_index[12] = 16; + } + + // num = -3 * signal_points[0] + 4 * signal_points[1] - signal_points[2]; + // den = signal_points[0] - 2 * signal_points[1] + signal_points[2]; + int32_t num = (signal_points[0] * -3) + (signal_points[1] * 4) + - signal_points[2]; + int32_t den = signal_points[0] + (signal_points[1] * -2) + signal_points[2]; + int32_t temp = num * 120; + int flag = 1; + int16_t stp = kParabolaCoefficients[fit_index[fs_mult]][0] + - kParabolaCoefficients[fit_index[fs_mult - 1]][0]; + int16_t strt = (kParabolaCoefficients[fit_index[fs_mult]][0] + + kParabolaCoefficients[fit_index[fs_mult - 1]][0]) / 2; + int16_t lmt; + if (temp < -den * strt) { + lmt = strt - stp; + while (flag) { + if ((flag == fs_mult) || (temp > -den * lmt)) { + *peak_value = (den * kParabolaCoefficients[fit_index[fs_mult - flag]][1] + + num * kParabolaCoefficients[fit_index[fs_mult - flag]][2] + + signal_points[0] * 256) / 256; + *peak_index = *peak_index * 2 * fs_mult - flag; + flag = 0; + } else { + flag++; + lmt -= stp; + } + } + } else if (temp > -den * (strt + stp)) { + lmt = strt + 2 * stp; + while (flag) { + if ((flag == fs_mult) || (temp < -den * lmt)) { + int32_t temp_term_1 = + den * kParabolaCoefficients[fit_index[fs_mult+flag]][1]; + int32_t temp_term_2 = + num * kParabolaCoefficients[fit_index[fs_mult+flag]][2]; + int32_t temp_term_3 = signal_points[0] * 256; + *peak_value = (temp_term_1 + temp_term_2 + temp_term_3) / 256; + *peak_index = *peak_index * 2 * fs_mult + flag; + flag = 0; + } else { + flag++; + lmt += stp; + } + } + } else { + *peak_value = signal_points[1]; + *peak_index = *peak_index * 2 * fs_mult; + } +} + +int DspHelper::MinDistortion(const int16_t* signal, int min_lag, + int max_lag, int length, + int32_t* distortion_value) { + int best_index = -1; + int32_t min_distortion = WEBRTC_SPL_WORD32_MAX; + for (int i = min_lag; i <= max_lag; i++) { + int32_t sum_diff = 0; + const int16_t* data1 = signal; + const int16_t* data2 = signal - i; + for (int j = 0; j < length; j++) { + sum_diff += WEBRTC_SPL_ABS_W32(data1[j] - data2[j]); + } + // Compare with previous minimum. + if (sum_diff < min_distortion) { + min_distortion = sum_diff; + best_index = i; + } + } + *distortion_value = min_distortion; + return best_index; +} + +void DspHelper::CrossFade(const int16_t* input1, const int16_t* input2, + size_t length, int16_t* mix_factor, + int16_t factor_decrement, int16_t* output) { + int16_t factor = *mix_factor; + int16_t complement_factor = 16384 - factor; + for (size_t i = 0; i < length; i++) { + output[i] = + (factor * input1[i] + complement_factor * input2[i] + 8192) >> 14; + factor -= factor_decrement; + complement_factor += factor_decrement; + } + *mix_factor = factor; +} + +void DspHelper::UnmuteSignal(const int16_t* input, size_t length, + int16_t* factor, int16_t increment, + int16_t* output) { + uint16_t factor_16b = *factor; + int32_t factor_32b = (static_cast(factor_16b) << 6) + 32; + for (size_t i = 0; i < length; i++) { + output[i] = (factor_16b * input[i] + 8192) >> 14; + factor_32b = std::max(factor_32b + increment, 0); + factor_16b = std::min(16384, factor_32b >> 6); + } + *factor = factor_16b; +} + +void DspHelper::MuteSignal(int16_t* signal, int16_t mute_slope, size_t length) { + int32_t factor = (16384 << 6) + 32; + for (size_t i = 0; i < length; i++) { + signal[i] = ((factor >> 6) * signal[i] + 8192) >> 14; + factor -= mute_slope; + } +} + +int DspHelper::DownsampleTo4kHz(const int16_t* input, size_t input_length, + int output_length, int input_rate_hz, + bool compensate_delay, int16_t* output) { + // Set filter parameters depending on input frequency. + // NOTE: The phase delay values are wrong compared to the true phase delay + // of the filters. However, the error is preserved (through the +1 term) for + // consistency. + const int16_t* filter_coefficients; // Filter coefficients. + int16_t filter_length; // Number of coefficients. + int16_t filter_delay; // Phase delay in samples. + int16_t factor; // Conversion rate (inFsHz / 8000). + switch (input_rate_hz) { + case 8000: { + filter_length = 3; + factor = 2; + filter_coefficients = kDownsample8kHzTbl; + filter_delay = 1 + 1; + break; + } + case 16000: { + filter_length = 5; + factor = 4; + filter_coefficients = kDownsample16kHzTbl; + filter_delay = 2 + 1; + break; + } + case 32000: { + filter_length = 7; + factor = 8; + filter_coefficients = kDownsample32kHzTbl; + filter_delay = 3 + 1; + break; + } + case 48000: { + filter_length = 7; + factor = 12; + filter_coefficients = kDownsample48kHzTbl; + filter_delay = 3 + 1; + break; + } + default: { + assert(false); + return -1; + } + } + + if (!compensate_delay) { + // Disregard delay compensation. + filter_delay = 0; + } + + // Returns -1 if input signal is too short; 0 otherwise. + return WebRtcSpl_DownsampleFast( + &input[filter_length - 1], static_cast(input_length) - + (filter_length - 1), output, output_length, filter_coefficients, + filter_length, factor, filter_delay); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helper.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helper.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helper.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helper.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_DSP_HELPER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_DSP_HELPER_H_ + +#include // Access to size_t. + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// This class contains various signal processing functions, all implemented as +// static methods. +class DspHelper { + public: + // Filter coefficients used when downsampling from the indicated sample rates + // (8, 16, 32, 48 kHz) to 4 kHz. Coefficients are in Q12. + static const int16_t kDownsample8kHzTbl[3]; + static const int16_t kDownsample16kHzTbl[5]; + static const int16_t kDownsample32kHzTbl[7]; + static const int16_t kDownsample48kHzTbl[7]; + + // Constants used to mute and unmute over 5 samples. The coefficients are + // in Q15. + static const int kMuteFactorStart8kHz = 27307; + static const int kMuteFactorIncrement8kHz = -5461; + static const int kUnmuteFactorStart8kHz = 5461; + static const int kUnmuteFactorIncrement8kHz = 5461; + static const int kMuteFactorStart16kHz = 29789; + static const int kMuteFactorIncrement16kHz = -2979; + static const int kUnmuteFactorStart16kHz = 2979; + static const int kUnmuteFactorIncrement16kHz = 2979; + static const int kMuteFactorStart32kHz = 31208; + static const int kMuteFactorIncrement32kHz = -1560; + static const int kUnmuteFactorStart32kHz = 1560; + static const int kUnmuteFactorIncrement32kHz = 1560; + static const int kMuteFactorStart48kHz = 31711; + static const int kMuteFactorIncrement48kHz = -1057; + static const int kUnmuteFactorStart48kHz = 1057; + static const int kUnmuteFactorIncrement48kHz = 1057; + + // Multiplies the signal with a gradually changing factor. + // The first sample is multiplied with |factor| (in Q14). For each sample, + // |factor| is increased (additive) by the |increment| (in Q20), which can + // be negative. Returns the scale factor after the last increment. + static int RampSignal(const int16_t* input, + size_t length, + int factor, + int increment, + int16_t* output); + + // Same as above, but with the samples of |signal| being modified in-place. + static int RampSignal(int16_t* signal, + size_t length, + int factor, + int increment); + + // Same as above, but processes |length| samples from |signal|, starting at + // |start_index|. + static int RampSignal(AudioMultiVector* signal, + size_t start_index, + size_t length, + int factor, + int increment); + + // Peak detection with parabolic fit. Looks for |num_peaks| maxima in |data|, + // having length |data_length| and sample rate multiplier |fs_mult|. The peak + // locations and values are written to the arrays |peak_index| and + // |peak_value|, respectively. Both arrays must hold at least |num_peaks| + // elements. + static void PeakDetection(int16_t* data, int data_length, + int num_peaks, int fs_mult, + int* peak_index, int16_t* peak_value); + + // Estimates the height and location of a maximum. The three values in the + // array |signal_points| are used as basis for a parabolic fit, which is then + // used to find the maximum in an interpolated signal. The |signal_points| are + // assumed to be from a 4 kHz signal, while the maximum, written to + // |peak_index| and |peak_value| is given in the full sample rate, as + // indicated by the sample rate multiplier |fs_mult|. + static void ParabolicFit(int16_t* signal_points, int fs_mult, + int* peak_index, int16_t* peak_value); + + // Calculates the sum-abs-diff for |signal| when compared to a displaced + // version of itself. Returns the displacement lag that results in the minimum + // distortion. The resulting distortion is written to |distortion_value|. + // The values of |min_lag| and |max_lag| are boundaries for the search. + static int MinDistortion(const int16_t* signal, int min_lag, + int max_lag, int length, int32_t* distortion_value); + + // Mixes |length| samples from |input1| and |input2| together and writes the + // result to |output|. The gain for |input1| starts at |mix_factor| (Q14) and + // is decreased by |factor_decrement| (Q14) for each sample. The gain for + // |input2| is the complement 16384 - mix_factor. + static void CrossFade(const int16_t* input1, const int16_t* input2, + size_t length, int16_t* mix_factor, + int16_t factor_decrement, int16_t* output); + + // Scales |input| with an increasing gain. Applies |factor| (Q14) to the first + // sample and increases the gain by |increment| (Q20) for each sample. The + // result is written to |output|. |length| samples are processed. + static void UnmuteSignal(const int16_t* input, size_t length, int16_t* factor, + int16_t increment, int16_t* output); + + // Starts at unity gain and gradually fades out |signal|. For each sample, + // the gain is reduced by |mute_slope| (Q14). |length| samples are processed. + static void MuteSignal(int16_t* signal, int16_t mute_slope, size_t length); + + // Downsamples |input| from |sample_rate_hz| to 4 kHz sample rate. The input + // has |input_length| samples, and the method will write |output_length| + // samples to |output|. Compensates for the phase delay of the downsampling + // filters if |compensate_delay| is true. Returns -1 if the input is too short + // to produce |output_length| samples, otherwise 0. + static int DownsampleTo4kHz(const int16_t* input, size_t input_length, + int output_length, int input_rate_hz, + bool compensate_delay, int16_t* output); + + private: + // Table of constants used in method DspHelper::ParabolicFit(). + static const int16_t kParabolaCoefficients[17][3]; + + DISALLOW_COPY_AND_ASSIGN(DspHelper); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_DSP_HELPER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helper_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helper_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helper_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helper_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/dsp_helper.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +TEST(DspHelper, RampSignalArray) { + static const int kLen = 100; + int16_t input[kLen]; + int16_t output[kLen]; + // Fill input with 1000. + for (int i = 0; i < kLen; ++i) { + input[i] = 1000; + } + int start_factor = 0; + // Ramp from 0 to 1 (in Q14) over the array. Note that |increment| is in Q20, + // while the factor is in Q14, hence the shift by 6. + int increment = (16384 << 6) / kLen; + + // Test first method. + int stop_factor = DspHelper::RampSignal(input, kLen, start_factor, increment, + output); + EXPECT_EQ(16383, stop_factor); // Almost reach 1 in Q14. + for (int i = 0; i < kLen; ++i) { + EXPECT_EQ(1000 * i / kLen, output[i]); + } + + // Test second method. (Note that this modifies |input|.) + stop_factor = DspHelper::RampSignal(input, kLen, start_factor, increment); + EXPECT_EQ(16383, stop_factor); // Almost reach 1 in Q14. + for (int i = 0; i < kLen; ++i) { + EXPECT_EQ(1000 * i / kLen, input[i]); + } +} + +TEST(DspHelper, RampSignalAudioMultiVector) { + static const int kLen = 100; + static const int kChannels = 5; + AudioMultiVector input(kChannels, kLen * 3); + // Fill input with 1000. + for (int i = 0; i < kLen * 3; ++i) { + for (int channel = 0; channel < kChannels; ++channel) { + input[channel][i] = 1000; + } + } + // We want to start ramping at |start_index| and keep ramping for |kLen| + // samples. + int start_index = kLen; + int start_factor = 0; + // Ramp from 0 to 1 (in Q14) in |kLen| samples. Note that |increment| is in + // Q20, while the factor is in Q14, hence the shift by 6. + int increment = (16384 << 6) / kLen; + + int stop_factor = DspHelper::RampSignal(&input, start_index, kLen, + start_factor, increment); + EXPECT_EQ(16383, stop_factor); // Almost reach 1 in Q14. + // Verify that the first |kLen| samples are left untouched. + int i; + for (i = 0; i < kLen; ++i) { + for (int channel = 0; channel < kChannels; ++channel) { + EXPECT_EQ(1000, input[channel][i]); + } + } + // Verify that the next block of |kLen| samples are ramped. + for (; i < 2 * kLen; ++i) { + for (int channel = 0; channel < kChannels; ++channel) { + EXPECT_EQ(1000 * (i - kLen) / kLen, input[channel][i]); + } + } + // Verify the last |kLen| samples are left untouched. + for (; i < 3 * kLen; ++i) { + for (int channel = 0; channel < kChannels; ++channel) { + EXPECT_EQ(1000, input[channel][i]); + } + } +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helpfunctions.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helpfunctions.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helpfunctions.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helpfunctions.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains some help functions that did not fit elsewhere. - */ - -#include "dsp_helpfunctions.h" - - -int16_t WebRtcNetEQ_CalcFsMult(uint16_t fsHz) -{ - switch (fsHz) - { - case 8000: - { - return 1; - } - case 16000: - { - return 2; - } - case 32000: - { - return 4; - } - case 48000: - { - return 6; - } - default: - { - return 1; - } - } -} - - -int WebRtcNetEQ_DownSampleTo4kHz(const int16_t *in, int inLen, uint16_t inFsHz, - int16_t *out, int outLen, int compensateDelay) -{ - int16_t *B; /* filter coefficients */ - int16_t Blen; /* number of coefficients */ - int16_t filterDelay; /* phase delay in samples */ - int16_t factor; /* conversion rate (inFsHz/8000) */ - int ok; - - /* Set constants depending on frequency used */ - /* NOTE: The phase delay values are wrong compared to the true phase delay - of the filters. However, the error is preserved (through the +1 term) - for consistency. */ - switch (inFsHz) - { - case 8000: - { - Blen = 3; - factor = 2; - B = (int16_t*) WebRtcNetEQ_kDownsample8kHzTbl; - filterDelay = 1 + 1; - break; - } -#ifdef NETEQ_WIDEBAND - case 16000: - { - Blen = 5; - factor = 4; - B = (int16_t*) WebRtcNetEQ_kDownsample16kHzTbl; - filterDelay = 2 + 1; - break; - } -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - case 32000: - { - Blen = 7; - factor = 8; - B = (int16_t*) WebRtcNetEQ_kDownsample32kHzTbl; - filterDelay = 3 + 1; - break; - } -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - case 48000: - { - Blen = 7; - factor = 12; - B = (int16_t*) WebRtcNetEQ_kDownsample48kHzTbl; - filterDelay = 3 + 1; - break; - } -#endif - default: - { - /* unsupported or wrong sample rate */ - return -1; - } - } - - if (!compensateDelay) - { - /* disregard delay compensation */ - filterDelay = 0; - } - - ok = WebRtcSpl_DownsampleFast((int16_t*) &in[Blen - 1], - (int16_t) (inLen - (Blen - 1)), /* number of input samples */ - out, (int16_t) outLen, /* number of output samples to produce */ - B, Blen, factor, filterDelay); /* filter parameters */ - - return ok; /* return value is -1 if input signal is too short */ - -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helpfunctions.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helpfunctions.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helpfunctions.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dsp_helpfunctions.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,220 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Various help functions used by the DSP functions. - */ - -#ifndef DSP_HELPFUNCTIONS_H -#define DSP_HELPFUNCTIONS_H - -#include "typedefs.h" - -#include "dsp.h" - -/**************************************************************************** - * WebRtcNetEQ_Correlator(...) - * - * Calculate signal correlation. - * - * Input: - * - inst : DSP instance - * - data : Speech history to do expand from (older history in data[-4..-1]) - * - dataLen : Length of data - * - * Output: - * - corrOut : CC of downsampled signal - * - corrScale : Scale factor for correlation (-Qdomain) - * - * Return value : Length of correlated data - */ - -int16_t WebRtcNetEQ_Correlator(DSPInst_t *inst, -#ifdef SCRATCH - int16_t *pw16_scratchPtr, -#endif - int16_t *pw16_data, int16_t w16_dataLen, - int16_t *pw16_corrOut, - int16_t *pw16_corrScale); - -/**************************************************************************** - * WebRtcNetEQ_PeakDetection(...) - * - * Peak detection with parabolic fit. - * - * Input: - * - data : Data sequence for peak detection - * - dataLen : Length of data - * - nmbPeaks : Number of peaks to detect - * - fs_mult : Sample rate multiplier - * - * Output: - * - corrIndex : Index of the peak - * - winner : Value of the peak - * - * Return value : 0 for ok - */ - -int16_t WebRtcNetEQ_PeakDetection(int16_t *pw16_data, int16_t w16_dataLen, - int16_t w16_nmbPeaks, int16_t fs_mult, - int16_t *pw16_corrIndex, - int16_t *pw16_winners); - -/**************************************************************************** - * WebRtcNetEQ_PrblFit(...) - * - * Three-point parbola fit. - * - * Input: - * - 3pts : Three input samples - * - fs_mult : Sample rate multiplier - * - * Output: - * - Ind : Index of the peak - * - outVal : Value of the peak - * - * Return value : 0 for ok - */ - -int16_t WebRtcNetEQ_PrblFit(int16_t *pw16_3pts, int16_t *pw16_Ind, - int16_t *pw16_outVal, int16_t fs_mult); - -/**************************************************************************** - * WebRtcNetEQ_MinDistortion(...) - * - * Find the lag that results in minimum distortion. - * - * Input: - * - data : Start of speech to perform distortion on, second vector is assumed - * to be data[-Lag] - * - minLag : Start lag - * - maxLag : End lag - * - len : Length to correlate - * - * Output: - * - dist : Distorion value - * - * Return value : Lag for minimum distortion - */ - -int16_t WebRtcNetEQ_MinDistortion(const int16_t *pw16_data, - int16_t w16_minLag, int16_t w16_maxLag, - int16_t len, int32_t *pw16_dist); - -/**************************************************************************** - * WebRtcNetEQ_RandomVec(...) - * - * Generate random vector. - * - * Input: - * - seed : Current seed (input/output) - * - len : Number of samples to generate - * - incVal : Jump step - * - * Output: - * - randVec : Generated random vector - */ - -void WebRtcNetEQ_RandomVec(uint32_t *w32_seed, int16_t *pw16_randVec, - int16_t w16_len, int16_t w16_incval); - -/**************************************************************************** - * WebRtcNetEQ_MixVoiceUnvoice(...) - * - * Mix voiced and unvoiced signal. - * - * Input: - * - voicedVec : Voiced input signal - * - unvoicedVec : Unvoiced input signal - * - current_vfraction : Current mixing factor - * - vfraction_change : Mixing factor change per sample - * - N : Number of samples - * - * Output: - * - outData : Mixed signal - */ - -void WebRtcNetEQ_MixVoiceUnvoice(int16_t *pw16_outData, int16_t *pw16_voicedVec, - int16_t *pw16_unvoicedVec, - int16_t *w16_current_vfraction, - int16_t w16_vfraction_change, int16_t N); - -/**************************************************************************** - * WebRtcNetEQ_UnmuteSignal(...) - * - * Gradually reduce attenuation. - * - * Input: - * - inVec : Input signal - * - startMuteFact : Starting attenuation - * - unmuteFact : Factor to "unmute" with (Q20) - * - N : Number of samples - * - * Output: - * - outVec : Output signal - */ - -void WebRtcNetEQ_UnmuteSignal(int16_t *pw16_inVec, int16_t *startMuteFact, - int16_t *pw16_outVec, int16_t unmuteFact, - int16_t N); - -/**************************************************************************** - * WebRtcNetEQ_MuteSignal(...) - * - * Gradually increase attenuation. - * - * Input: - * - inout : Input/output signal - * - muteSlope : Slope of muting - * - N : Number of samples - */ - -void WebRtcNetEQ_MuteSignal(int16_t *pw16_inout, int16_t muteSlope, - int16_t N); - -/**************************************************************************** - * WebRtcNetEQ_CalcFsMult(...) - * - * Calculate the sample rate divided by 8000. - * - * Input: - * - fsHz : Sample rate in Hz in {8000, 16000, 32000, 48000}. - * - * Return value : fsHz/8000 for the valid values, 1 for other inputs - */ - -int16_t WebRtcNetEQ_CalcFsMult(uint16_t fsHz); - -/**************************************************************************** - * WebRtcNetEQ_DownSampleTo4kHz(...) - * - * Lowpass filter and downsample a signal to 4 kHz sample rate. - * - * Input: - * - in : Input signal samples. - * - inLen : Number of input samples. - * - inFsHz : Input sample rate in Hz. - * - outLen : Desired number of samples in decimated signal. - * - compensateDelay : If non-zero, compensate for the phase delay of - * of the anti-alias filter. - * - * Output: - * - out : Output signal samples. - * - * Return value : 0 - Ok - * -1 - Error - * - */ - -int WebRtcNetEQ_DownSampleTo4kHz(const int16_t *in, int inLen, uint16_t inFsHz, - int16_t *out, int outLen, int compensateDelay); - -#endif - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_buffer.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_buffer.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_buffer.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_buffer.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,232 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Implementation of packet buffer for DTMF messages. - */ - -#include "dtmf_buffer.h" - -#include "typedefs.h" /* to define endianness */ -#include "signal_processing_library.h" - -#include "neteq_error_codes.h" - - -#ifdef NETEQ_ATEVENT_DECODE - -int16_t WebRtcNetEQ_DtmfRemoveEvent(dtmf_inst_t *DTMFdec_inst) -{ - - int i; - for (i = 0; i < 3; i++) - { - DTMFdec_inst->EventQueue[i] = DTMFdec_inst->EventQueue[i + 1]; - DTMFdec_inst->EventQueueVolume[i] = DTMFdec_inst->EventQueueVolume[i + 1]; - DTMFdec_inst->EventQueueEnded[i] = DTMFdec_inst->EventQueueEnded[i + 1]; - DTMFdec_inst->EventQueueStartTime[i] = DTMFdec_inst->EventQueueStartTime[i + 1]; - DTMFdec_inst->EventQueueEndTime[i] = DTMFdec_inst->EventQueueEndTime[i + 1]; - } - DTMFdec_inst->EventBufferSize--; - DTMFdec_inst->EventQueue[3] = -1; - DTMFdec_inst->EventQueueVolume[3] = 0; - DTMFdec_inst->EventQueueEnded[3] = 0; - DTMFdec_inst->EventQueueStartTime[3] = 0; - DTMFdec_inst->EventQueueEndTime[3] = 0; - - return 0; -} - -int16_t WebRtcNetEQ_DtmfDecoderInit(dtmf_inst_t *DTMFdec_inst, uint16_t fs, - int16_t MaxPLCtime) -{ - int i; - if (((fs != 8000) && (fs != 16000) && (fs != 32000) && (fs != 48000)) || (MaxPLCtime < 0)) - { - return DTMF_DEC_PARAMETER_ERROR; - } - if (fs == 8000) - DTMFdec_inst->framelen = 80; - else if (fs == 16000) - DTMFdec_inst->framelen = 160; - else if (fs == 32000) - DTMFdec_inst->framelen = 320; - else - /* fs == 48000 */ - DTMFdec_inst->framelen = 480; - - DTMFdec_inst->MaxPLCtime = MaxPLCtime; - DTMFdec_inst->CurrentPLCtime = 0; - DTMFdec_inst->EventBufferSize = 0; - for (i = 0; i < 4; i++) - { - DTMFdec_inst->EventQueue[i] = -1; - DTMFdec_inst->EventQueueVolume[i] = 0; - DTMFdec_inst->EventQueueEnded[i] = 0; - DTMFdec_inst->EventQueueStartTime[i] = 0; - DTMFdec_inst->EventQueueEndTime[i] = 0; - } - return 0; -} - -int16_t WebRtcNetEQ_DtmfInsertEvent(dtmf_inst_t *DTMFdec_inst, - const int16_t *encoded, int16_t len, - uint32_t timeStamp) -{ - - int i; - int16_t value; - const int16_t *EventStart; - int16_t endEvent; - int16_t Volume; - int16_t Duration; - int16_t position = -1; - - /* Extract event */ - if (len == 4) - { - EventStart = encoded; -#ifdef WEBRTC_ARCH_BIG_ENDIAN - value=((*EventStart)>>8); - endEvent=((*EventStart)&0x80)>>7; - Volume=((*EventStart)&0x3F); - Duration=EventStart[1]; -#else - value = ((*EventStart) & 0xFF); - endEvent = ((*EventStart) & 0x8000) >> 15; - Volume = ((*EventStart) & 0x3F00) >> 8; - Duration = (((((uint16_t) EventStart[1]) >> 8) & 0xFF) - | (((uint16_t) (EventStart[1] & 0xFF)) << 8)); -#endif - /* Only events between 0-15 are supported (DTMF tones) */ - if ((value < 0) || (value > 15)) - { - return 0; - } - - /* Discard all DTMF tones with really low volume (<-36dbm0) */ - if (Volume > 36) - { - return 0; - } - - /*Are there any unended events of the same type? */ - for (i = 0; i < DTMFdec_inst->EventBufferSize; i++) - { - /* Going through the whole queue even when we have found a match will - ensure that we add to the latest applicable event */ - if ((DTMFdec_inst->EventQueue[i] == value) && (!DTMFdec_inst->EventQueueEnded[i] - || endEvent)) position = i; - } - if (position > -1) - { - DTMFdec_inst->EventQueueVolume[position] = Volume; - if ((timeStamp + Duration) > DTMFdec_inst->EventQueueEndTime[position]) DTMFdec_inst->EventQueueEndTime[position] - = DTMFdec_inst->EventQueueStartTime[position] + Duration; - if (endEvent) DTMFdec_inst->EventQueueEnded[position] = 1; - } - else - { - if (DTMFdec_inst->EventBufferSize == MAX_DTMF_QUEUE_SIZE) - { /* Buffer full */ - /* Remove one event */ - DTMFdec_inst->EventBufferSize--; - } - /* Store data in the instance on a new position*/ - DTMFdec_inst->EventQueue[DTMFdec_inst->EventBufferSize] = value; - DTMFdec_inst->EventQueueVolume[DTMFdec_inst->EventBufferSize] = Volume; - DTMFdec_inst->EventQueueEnded[DTMFdec_inst->EventBufferSize] = endEvent; - DTMFdec_inst->EventQueueStartTime[DTMFdec_inst->EventBufferSize] = timeStamp; - DTMFdec_inst->EventQueueEndTime[DTMFdec_inst->EventBufferSize] = timeStamp - + Duration; - DTMFdec_inst->EventBufferSize++; - } - return 0; - } - return DTMF_INSERT_ERROR; -} - -int16_t WebRtcNetEQ_DtmfDecode(dtmf_inst_t *DTMFdec_inst, int16_t *event, - int16_t *volume, uint32_t currTimeStamp) -{ - - if (DTMFdec_inst->EventBufferSize < 1) return 0; /* No events to play */ - - /* We have events, is it time to play them? */ - if (currTimeStamp < DTMFdec_inst->EventQueueStartTime[0]) - { - /*No, just return zero */ - return 0; - } - - /* Continue on the event that is currently ongoing */ - *event = DTMFdec_inst->EventQueue[0]; - *volume = DTMFdec_inst->EventQueueVolume[0]; - - if (DTMFdec_inst->EventQueueEndTime[0] >= (currTimeStamp + DTMFdec_inst->framelen)) - { - - /* Still at least framLen to play */ - - DTMFdec_inst->CurrentPLCtime = 0; - if ((DTMFdec_inst->EventQueueEndTime[0] == (currTimeStamp + DTMFdec_inst->framelen)) - && (DTMFdec_inst->EventQueueEnded[0])) - { /* We are done */ - /*Remove the event from Queue*/ - WebRtcNetEQ_DtmfRemoveEvent(DTMFdec_inst); - } - return DTMFdec_inst->framelen; - - } - else - { - if ((DTMFdec_inst->EventQueueEnded[0]) || (DTMFdec_inst->EventQueue[1] > -1)) - { - /* - * Less than frameLen to play and end of event or already received next event. - * Give our a whole frame size of audio to simplify things. - */ - - /*Remove the event from Queue*/ - WebRtcNetEQ_DtmfRemoveEvent(DTMFdec_inst); - DTMFdec_inst->CurrentPLCtime = 0; - - return DTMFdec_inst->framelen; - - } - else - { - /* Less than frameLen to play and not end of event. */ - DTMFdec_inst->CurrentPLCtime = (int16_t) (currTimeStamp - - DTMFdec_inst->EventQueueEndTime[0]); - - if ((DTMFdec_inst->CurrentPLCtime > DTMFdec_inst->MaxPLCtime) - || (DTMFdec_inst->CurrentPLCtime < -DTMFdec_inst->MaxPLCtime)) - { - /*Remove the event from queue*/ - WebRtcNetEQ_DtmfRemoveEvent(DTMFdec_inst); - DTMFdec_inst->CurrentPLCtime = 0; - } - - /* If we have a new event that it's time to play */ - if ((DTMFdec_inst->EventQueue[1] > -1) && (DTMFdec_inst->EventQueueStartTime[1] - >= (currTimeStamp + DTMFdec_inst->framelen))) - { - /*Remove the event from queue*/ - WebRtcNetEQ_DtmfRemoveEvent(DTMFdec_inst); - DTMFdec_inst->CurrentPLCtime = 0; - } - - return DTMFdec_inst->framelen; - } - } -} - -#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_buffer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_buffer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_buffer.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_buffer.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/dtmf_buffer.h" + +#include +#include // max + +// Modify the code to obtain backwards bit-exactness. Once bit-exactness is no +// longer required, this #define should be removed (and the code that it +// enables). +#define LEGACY_BITEXACT + +namespace webrtc { + +// The ParseEvent method parses 4 bytes from |payload| according to this format +// from RFC 4733: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | event |E|R| volume | duration | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// Legend (adapted from RFC 4733) +// - event: The event field is a number between 0 and 255 identifying a +// specific telephony event. The buffer will not accept any event +// numbers larger than 15. +// - E: If set to a value of one, the "end" bit indicates that this +// packet contains the end of the event. For long-lasting events +// that have to be split into segments, only the final packet for +// the final segment will have the E bit set. +// - R: Reserved. +// - volume: For DTMF digits and other events representable as tones, this +// field describes the power level of the tone, expressed in dBm0 +// after dropping the sign. Power levels range from 0 to -63 dBm0. +// Thus, larger values denote lower volume. The buffer discards +// values larger than 36 (i.e., lower than -36 dBm0). +// - duration: The duration field indicates the duration of the event or segment +// being reported, in timestamp units, expressed as an unsigned +// integer in network byte order. For a non-zero value, the event +// or segment began at the instant identified by the RTP timestamp +// and has so far lasted as long as indicated by this parameter. +// The event may or may not have ended. If the event duration +// exceeds the maximum representable by the duration field, the +// event is split into several contiguous segments. The buffer will +// discard zero-duration events. +// +int DtmfBuffer::ParseEvent(uint32_t rtp_timestamp, + const uint8_t* payload, + int payload_length_bytes, + DtmfEvent* event) { + if (!payload || !event) { + return kInvalidPointer; + } + if (payload_length_bytes < 4) { + return kPayloadTooShort; + } + + event->event_no = payload[0]; + event->end_bit = ((payload[1] & 0x80) != 0); + event->volume = (payload[1] & 0x3F); + event->duration = payload[2] << 8 | payload[3]; + event->timestamp = rtp_timestamp; + return kOK; +} + +// Inserts a DTMF event into the buffer. The event should be parsed from the +// bit stream using the ParseEvent method above before inserting it in the +// buffer. +// DTMF events can be quite long, and in most cases the duration of the event +// is not known when the first packet describing it is sent. To deal with that, +// the RFC 4733 specifies that multiple packets are sent for one and the same +// event as it is being created (typically, as the user is pressing the key). +// These packets will all share the same start timestamp and event number, +// while the duration will be the cumulative duration from the start. When +// inserting a new event, the InsertEvent method tries to find a matching event +// already in the buffer. If so, the new event is simply merged with the +// existing one. +int DtmfBuffer::InsertEvent(const DtmfEvent& event) { + if (event.event_no < 0 || event.event_no > 15 || + event.volume < 0 || event.volume > 36 || + event.duration <= 0 || event.duration > 65535) { + return kInvalidEventParameters; + } + DtmfList::iterator it = buffer_.begin(); + while (it != buffer_.end()) { + if (MergeEvents(it, event)) { + // A matching event was found and the new event was merged. + return kOK; + } + ++it; + } + buffer_.push_back(event); + // Sort the buffer using CompareEvents to rank the events. + buffer_.sort(CompareEvents); + return kOK; +} + +bool DtmfBuffer::GetEvent(uint32_t current_timestamp, DtmfEvent* event) { + DtmfList::iterator it = buffer_.begin(); + while (it != buffer_.end()) { + // |event_end| is an estimate of where the current event ends. If the end + // bit is set, we know that the event ends at |timestamp| + |duration|. + uint32_t event_end = it->timestamp + it->duration; +#ifdef LEGACY_BITEXACT + bool next_available = false; +#endif + if (!it->end_bit) { + // If the end bit is not set, we allow extrapolation of the event for + // some time. + event_end += max_extrapolation_samples_; + DtmfList::iterator next = it; + ++next; + if (next != buffer_.end()) { + // If there is a next event in the buffer, we will not extrapolate over + // the start of that new event. + event_end = std::min(event_end, next->timestamp); +#ifdef LEGACY_BITEXACT + next_available = true; +#endif + } + } + if (current_timestamp >= it->timestamp + && current_timestamp <= event_end) { // TODO(hlundin): Change to <. + // Found a matching event. + if (event) { + event->event_no = it->event_no; + event->end_bit = it->end_bit; + event->volume = it->volume; + event->duration = it->duration; + event->timestamp = it->timestamp; + } +#ifdef LEGACY_BITEXACT + if (it->end_bit && + current_timestamp + frame_len_samples_ >= event_end) { + // We are done playing this. Erase the event. + buffer_.erase(it); + } +#endif + return true; + } else if (current_timestamp > event_end) { // TODO(hlundin): Change to >=. + // Erase old event. Operation returns a valid pointer to the next element + // in the list. +#ifdef LEGACY_BITEXACT + if (!next_available) { + if (event) { + event->event_no = it->event_no; + event->end_bit = it->end_bit; + event->volume = it->volume; + event->duration = it->duration; + event->timestamp = it->timestamp; + } + it = buffer_.erase(it); + return true; + } else { + it = buffer_.erase(it); + } +#else + it = buffer_.erase(it); +#endif + } else { + ++it; + } + } + return false; +} + +int DtmfBuffer::SetSampleRate(int fs_hz) { + if (fs_hz != 8000 && + fs_hz != 16000 && + fs_hz != 32000 && + fs_hz != 48000) { + return kInvalidSampleRate; + } + max_extrapolation_samples_ = 7 * fs_hz / 100; + frame_len_samples_ = fs_hz / 100; + return kOK; +} + +// The method returns true if the two events are considered to be the same. +// The are defined as equal if they share the same timestamp and event number. +// The special case with long-lasting events that have to be split into segments +// is not handled in this method. These will be treated as separate events in +// the buffer. +bool DtmfBuffer::SameEvent(const DtmfEvent& a, const DtmfEvent& b) { + return (a.event_no == b.event_no) && (a.timestamp == b.timestamp); +} + +bool DtmfBuffer::MergeEvents(DtmfList::iterator it, const DtmfEvent& event) { + if (SameEvent(*it, event)) { + if (!it->end_bit) { + // Do not extend the duration of an event for which the end bit was + // already received. + it->duration = std::max(event.duration, it->duration); + } + if (event.end_bit) { + it->end_bit = true; + } + return true; + } else { + return false; + } +} + +// Returns true if |a| goes before |b| in the sorting order ("|a| < |b|"). +// The events are ranked using their start timestamp (taking wrap-around into +// account). In the unlikely situation that two events share the same start +// timestamp, the event number is used to rank the two. Note that packets +// that belong to the same events, and therefore sharing the same start +// timestamp, have already been merged before the sort method is called. +bool DtmfBuffer::CompareEvents(const DtmfEvent& a, const DtmfEvent& b) { + if (a.timestamp == b.timestamp) { + return a.event_no < b.event_no; + } + // Take wrap-around into account. + return (static_cast(b.timestamp - a.timestamp) < 0xFFFFFFFF / 2); +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_buffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_buffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_buffer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_buffer.h 2015-02-03 14:33:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source @@ -8,94 +8,109 @@ * be found in the AUTHORS file in the root of the source tree. */ -/* - * Packet buffer for DTMF messages. - */ - -#ifndef DTMF_BUFFER_H -#define DTMF_BUFFER_H - -#include "typedefs.h" - -#include "neteq_defines.h" - -/* Include this code only if ATEVENT (DTMF) is defined in */ -#ifdef NETEQ_ATEVENT_DECODE - -#define MAX_DTMF_QUEUE_SIZE 4 - -typedef struct dtmf_inst_t_ -{ - int16_t MaxPLCtime; - int16_t CurrentPLCtime; - int16_t EventQueue[MAX_DTMF_QUEUE_SIZE]; - int16_t EventQueueVolume[MAX_DTMF_QUEUE_SIZE]; - int16_t EventQueueEnded[MAX_DTMF_QUEUE_SIZE]; - uint32_t EventQueueStartTime[MAX_DTMF_QUEUE_SIZE]; - uint32_t EventQueueEndTime[MAX_DTMF_QUEUE_SIZE]; - int16_t EventBufferSize; - int16_t framelen; -} dtmf_inst_t; +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_DTMF_BUFFER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_DTMF_BUFFER_H_ -/**************************************************************************** - * WebRtcNetEQ_DtmfDecoderInit(...) - * - * This function initializes a DTMF instance. - * - * Input: - * - DTMF_decinst_t : DTMF instance - * - fs : The sample rate used for the DTMF - * - MaxPLCtime : Maximum length for a PLC before zeros should be inserted - * - * Return value : 0 - Ok - * -1 - Error - */ - -int16_t WebRtcNetEQ_DtmfDecoderInit(dtmf_inst_t *DTMFdec_inst, uint16_t fs, - int16_t MaxPLCtime); +#include +#include // size_t -/**************************************************************************** - * WebRtcNetEQ_DtmfInsertEvent(...) - * - * This function decodes a packet with DTMF frames. - * - * Input: - * - DTMFdec_inst : DTMF instance - * - encoded : Encoded DTMF frame(s) - * - len : Bytes in encoded vector - * - * - * Return value : 0 - Ok - * -1 - Error - */ - -int16_t WebRtcNetEQ_DtmfInsertEvent(dtmf_inst_t *DTMFdec_inst, - const int16_t *encoded, int16_t len, - uint32_t timeStamp); - -/**************************************************************************** - * WebRtcNetEQ_DtmfDecode(...) - * - * This function decodes a packet with DTMF frame(s). Output will be the - * event that should be played for next 10 ms. - * - * Input: - * - DTMFdec_inst : DTMF instance - * - currTimeStamp : The current playout timestamp - * - * Output: - * - event : Event number to be played - * - volume : Event volume to be played - * - * Return value : >0 - There is a event to be played - * 0 - No event to be played - * -1 - Error - */ +#include "webrtc/base/constructormagic.h" +#include "webrtc/typedefs.h" -int16_t WebRtcNetEQ_DtmfDecode(dtmf_inst_t *DTMFdec_inst, int16_t *event, - int16_t *volume, uint32_t currTimeStamp); +namespace webrtc { + +struct DtmfEvent { + uint32_t timestamp; + int event_no; + int volume; + int duration; + bool end_bit; + + // Constructors + DtmfEvent() + : timestamp(0), + event_no(0), + volume(0), + duration(0), + end_bit(false) { + } + DtmfEvent(uint32_t ts, int ev, int vol, int dur, bool end) + : timestamp(ts), + event_no(ev), + volume(vol), + duration(dur), + end_bit(end) { + } +}; + +// This is the buffer holding DTMF events while waiting for them to be played. +class DtmfBuffer { + public: + enum BufferReturnCodes { + kOK = 0, + kInvalidPointer, + kPayloadTooShort, + kInvalidEventParameters, + kInvalidSampleRate + }; + + // Set up the buffer for use at sample rate |fs_hz|. + explicit DtmfBuffer(int fs_hz) { + SetSampleRate(fs_hz); + } + + virtual ~DtmfBuffer() {} + + // Flushes the buffer. + virtual void Flush() { buffer_.clear(); } + + // Static method to parse 4 bytes from |payload| as a DTMF event (RFC 4733) + // and write the parsed information into the struct |event|. Input variable + // |rtp_timestamp| is simply copied into the struct. + static int ParseEvent(uint32_t rtp_timestamp, + const uint8_t* payload, + int payload_length_bytes, + DtmfEvent* event); + + // Inserts |event| into the buffer. The method looks for a matching event and + // merges the two if a match is found. + virtual int InsertEvent(const DtmfEvent& event); + + // Checks if a DTMF event should be played at time |current_timestamp|. If so, + // the method returns true; otherwise false. The parameters of the event to + // play will be written to |event|. + virtual bool GetEvent(uint32_t current_timestamp, DtmfEvent* event); + + // Number of events in the buffer. + virtual size_t Length() const { return buffer_.size(); } + + virtual bool Empty() const { return buffer_.empty(); } + + // Set a new sample rate. + virtual int SetSampleRate(int fs_hz); + + private: + typedef std::list DtmfList; + + int max_extrapolation_samples_; + int frame_len_samples_; // TODO(hlundin): Remove this later. + + // Compares two events and returns true if they are the same. + static bool SameEvent(const DtmfEvent& a, const DtmfEvent& b); + + // Merges |event| to the event pointed out by |it|. The method checks that + // the two events are the same (using the SameEvent method), and merges them + // if that was the case, returning true. If the events are not the same, false + // is returned. + bool MergeEvents(DtmfList::iterator it, const DtmfEvent& event); + + // Method used by the sort algorithm to rank events in the buffer. + static bool CompareEvents(const DtmfEvent& a, const DtmfEvent& b); -#endif /* NETEQ_ATEVENT_DECODE */ + DtmfList buffer_; -#endif /* DTMF_BUFFER_H */ + DISALLOW_COPY_AND_ASSIGN(DtmfBuffer); +}; +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_DTMF_BUFFER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_buffer_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_buffer_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_buffer_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_buffer_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/dtmf_buffer.h" + +#ifdef WIN32 +#include // ntohl() +#else +#include // ntohl() +#endif + +#include + +#include "testing/gtest/include/gtest/gtest.h" + +// Modify the tests so that they pass with the modifications done to DtmfBuffer +// for backwards bit-exactness. Once bit-exactness is no longer required, this +// #define should be removed (and the code that it enables). +#define LEGACY_BITEXACT + +namespace webrtc { + +static int sample_rate_hz = 8000; + +static uint32_t MakeDtmfPayload(int event, bool end, int volume, int duration) { + uint32_t payload = 0; +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | event |E|R| volume | duration | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + payload |= (event & 0x00FF) << 24; + payload |= (end ? 0x00800000 : 0x00000000); + payload |= (volume & 0x003F) << 16; + payload |= (duration & 0xFFFF); + payload = ntohl(payload); + return payload; +} + +static bool EqualEvents(const DtmfEvent& a, + const DtmfEvent& b) { + return (a.duration == b.duration + && a.end_bit == b.end_bit + && a.event_no == b.event_no + && a.timestamp == b.timestamp + && a.volume == b.volume); +} + +TEST(DtmfBuffer, CreateAndDestroy) { + DtmfBuffer* buffer = new DtmfBuffer(sample_rate_hz); + delete buffer; +} + +// Test the event parser. +TEST(DtmfBuffer, ParseEvent) { + int event_no = 7; + bool end_bit = true; + int volume = 17; + int duration = 4711; + uint32_t timestamp = 0x12345678; + uint32_t payload = MakeDtmfPayload(event_no, end_bit, volume, duration); + uint8_t* payload_ptr = reinterpret_cast(&payload); + DtmfEvent event; + EXPECT_EQ(DtmfBuffer::kOK, + DtmfBuffer::ParseEvent(timestamp, payload_ptr, sizeof(payload), + &event)); + EXPECT_EQ(duration, event.duration); + EXPECT_EQ(end_bit, event.end_bit); + EXPECT_EQ(event_no, event.event_no); + EXPECT_EQ(timestamp, event.timestamp); + EXPECT_EQ(volume, event.volume); + + EXPECT_EQ(DtmfBuffer::kInvalidPointer, + DtmfBuffer::ParseEvent(timestamp, NULL, 4, &event)); + + EXPECT_EQ(DtmfBuffer::kInvalidPointer, + DtmfBuffer::ParseEvent(timestamp, payload_ptr, 4, NULL)); + + EXPECT_EQ(DtmfBuffer::kPayloadTooShort, + DtmfBuffer::ParseEvent(timestamp, payload_ptr, 3, &event)); +} + +TEST(DtmfBuffer, SimpleInsertAndGet) { + int event_no = 7; + bool end_bit = true; + int volume = 17; + int duration = 4711; + uint32_t timestamp = 0x12345678; + DtmfEvent event(timestamp, event_no, volume, duration, end_bit); + DtmfBuffer buffer(sample_rate_hz); + EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event)); + EXPECT_EQ(1u, buffer.Length()); + EXPECT_FALSE(buffer.Empty()); + DtmfEvent out_event; + // Too early to get event. + EXPECT_FALSE(buffer.GetEvent(timestamp - 10, &out_event)); + EXPECT_EQ(1u, buffer.Length()); + EXPECT_FALSE(buffer.Empty()); + // Get the event at its starting timestamp. + EXPECT_TRUE(buffer.GetEvent(timestamp, &out_event)); + EXPECT_TRUE(EqualEvents(event, out_event)); + EXPECT_EQ(1u, buffer.Length()); + EXPECT_FALSE(buffer.Empty()); + // Get the event some time into the event. + EXPECT_TRUE(buffer.GetEvent(timestamp + duration / 2, &out_event)); + EXPECT_TRUE(EqualEvents(event, out_event)); + EXPECT_EQ(1u, buffer.Length()); + EXPECT_FALSE(buffer.Empty()); + // Give a "current" timestamp after the event has ended. +#ifdef LEGACY_BITEXACT + EXPECT_TRUE(buffer.GetEvent(timestamp + duration + 10, &out_event)); +#endif + EXPECT_FALSE(buffer.GetEvent(timestamp + duration + 10, &out_event)); + EXPECT_EQ(0u, buffer.Length()); + EXPECT_TRUE(buffer.Empty()); +} + +TEST(DtmfBuffer, MergingPackets) { + int event_no = 0; + bool end_bit = false; + int volume = 17; + int duration = 80; + uint32_t timestamp = 0x12345678; + DtmfEvent event(timestamp, event_no, volume, duration, end_bit); + DtmfBuffer buffer(sample_rate_hz); + EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event)); + + event.duration += 80; + EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event)); + + event.duration += 80; + event.end_bit = true; + EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event)); + + EXPECT_EQ(1u, buffer.Length()); + + DtmfEvent out_event; + EXPECT_TRUE(buffer.GetEvent(timestamp, &out_event)); + EXPECT_TRUE(EqualEvents(event, out_event)); +} + +// This test case inserts one shorter event completely overlapped by one longer +// event. The expected outcome is that only the longer event is played. +TEST(DtmfBuffer, OverlappingEvents) { + int event_no = 0; + bool end_bit = true; + int volume = 1; + int duration = 80; + uint32_t timestamp = 0x12345678 + 80; + DtmfEvent short_event(timestamp, event_no, volume, duration, end_bit); + DtmfBuffer buffer(sample_rate_hz); + EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(short_event)); + + event_no = 10; + end_bit = false; + timestamp = 0x12345678; + DtmfEvent long_event(timestamp, event_no, volume, duration, end_bit); + EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(long_event)); + + long_event.duration += 80; + EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(long_event)); + + long_event.duration += 80; + long_event.end_bit = true; + EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(long_event)); + + EXPECT_EQ(2u, buffer.Length()); + + DtmfEvent out_event; + // Expect to get the long event. + EXPECT_TRUE(buffer.GetEvent(timestamp, &out_event)); + EXPECT_TRUE(EqualEvents(long_event, out_event)); + // Expect no more events. +#ifdef LEGACY_BITEXACT + EXPECT_TRUE(buffer.GetEvent(timestamp + long_event.duration + 10, + &out_event)); + EXPECT_TRUE(EqualEvents(long_event, out_event)); + EXPECT_TRUE(buffer.GetEvent(timestamp + long_event.duration + 10, + &out_event)); + EXPECT_TRUE(EqualEvents(short_event, out_event)); +#else + EXPECT_FALSE(buffer.GetEvent(timestamp + long_event.duration + 10, + &out_event)); +#endif + EXPECT_TRUE(buffer.Empty()); +} + +TEST(DtmfBuffer, ExtrapolationTime) { + int event_no = 0; + bool end_bit = false; + int volume = 1; + int duration = 80; + uint32_t timestamp = 0x12345678; + DtmfEvent event1(timestamp, event_no, volume, duration, end_bit); + DtmfBuffer buffer(sample_rate_hz); + EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event1)); + EXPECT_EQ(1u, buffer.Length()); + + DtmfEvent out_event; + // Get the event at the start. + EXPECT_TRUE(buffer.GetEvent(timestamp, &out_event)); + EXPECT_TRUE(EqualEvents(event1, out_event)); + // Also get the event 100 samples after the end of the event (since we're + // missing the end bit). + uint32_t timestamp_now = timestamp + duration + 100; + EXPECT_TRUE(buffer.GetEvent(timestamp_now, &out_event)); + EXPECT_TRUE(EqualEvents(event1, out_event)); + // Insert another event starting back-to-back with the previous event. + timestamp += duration; + event_no = 1; + DtmfEvent event2(timestamp, event_no, volume, duration, end_bit); + EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event2)); + EXPECT_EQ(2u, buffer.Length()); + // Now we expect to get the new event when supplying |timestamp_now|. + EXPECT_TRUE(buffer.GetEvent(timestamp_now, &out_event)); + EXPECT_TRUE(EqualEvents(event2, out_event)); + // Expect the the first event to be erased now. + EXPECT_EQ(1u, buffer.Length()); + // Move |timestamp_now| to more than 560 samples after the end of the second + // event. Expect that event to be erased. + timestamp_now = timestamp + duration + 600; +#ifdef LEGACY_BITEXACT + EXPECT_TRUE(buffer.GetEvent(timestamp_now, &out_event)); +#endif + EXPECT_FALSE(buffer.GetEvent(timestamp_now, &out_event)); + EXPECT_TRUE(buffer.Empty()); +} + +TEST(DtmfBuffer, TimestampWraparound) { + int event_no = 0; + bool end_bit = true; + int volume = 1; + int duration = 80; + uint32_t timestamp1 = 0xFFFFFFFF - duration; + DtmfEvent event1(timestamp1, event_no, volume, duration, end_bit); + uint32_t timestamp2 = 0; + DtmfEvent event2(timestamp2, event_no, volume, duration, end_bit); + DtmfBuffer buffer(sample_rate_hz); + EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event1)); + EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event2)); + EXPECT_EQ(2u, buffer.Length()); + DtmfEvent out_event; + EXPECT_TRUE(buffer.GetEvent(timestamp1, &out_event)); + EXPECT_TRUE(EqualEvents(event1, out_event)); +#ifdef LEGACY_BITEXACT + EXPECT_EQ(1u, buffer.Length()); +#else + EXPECT_EQ(2u, buffer.Length()); +#endif + + buffer.Flush(); + // Reverse the insert order. Expect same results. + EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event2)); + EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event1)); + EXPECT_EQ(2u, buffer.Length()); + EXPECT_TRUE(buffer.GetEvent(timestamp1, &out_event)); + EXPECT_TRUE(EqualEvents(event1, out_event)); +#ifdef LEGACY_BITEXACT + EXPECT_EQ(1u, buffer.Length()); +#else + EXPECT_EQ(2u, buffer.Length()); +#endif +} + +TEST(DtmfBuffer, InvalidEvents) { + int event_no = 0; + bool end_bit = true; + int volume = 1; + int duration = 80; + uint32_t timestamp = 0x12345678; + DtmfEvent event(timestamp, event_no, volume, duration, end_bit); + DtmfBuffer buffer(sample_rate_hz); + + // Invalid event number. + event.event_no = -1; + EXPECT_EQ(DtmfBuffer::kInvalidEventParameters, buffer.InsertEvent(event)); + event.event_no = 16; + EXPECT_EQ(DtmfBuffer::kInvalidEventParameters, buffer.InsertEvent(event)); + event.event_no = 0; // Valid value; + + // Invalid volume. + event.volume = -1; + EXPECT_EQ(DtmfBuffer::kInvalidEventParameters, buffer.InsertEvent(event)); + event.volume = 37; + EXPECT_EQ(DtmfBuffer::kInvalidEventParameters, buffer.InsertEvent(event)); + event.volume = 0; // Valid value; + + // Invalid duration. + event.duration = -1; + EXPECT_EQ(DtmfBuffer::kInvalidEventParameters, buffer.InsertEvent(event)); + event.duration = 0; + EXPECT_EQ(DtmfBuffer::kInvalidEventParameters, buffer.InsertEvent(event)); + event.duration = 0xFFFF + 1; + EXPECT_EQ(DtmfBuffer::kInvalidEventParameters, buffer.InsertEvent(event)); + event.duration = 1; // Valid value; + + // Finish with a valid event, just to verify that all is ok. + EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event)); +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tonegen.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tonegen.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tonegen.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tonegen.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,367 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the DTMF tone generator and its parameters. - * - * A sinusoid is generated using the recursive oscillator model - * - * y[n] = sin(w*n + phi) = 2*cos(w) * y[n-1] - y[n-2] - * = a * y[n-1] - y[n-2] - * - * initialized with - * y[-2] = 0 - * y[-1] = sin(w) - * - * A DTMF signal is a combination of two sinusoids, depending - * on which event is sent (i.e, which key is pressed). The following - * table maps each key (event codes in parentheses) into two tones: - * - * 1209 Hz 1336 Hz 1477 Hz 1633 Hz - * 697 Hz 1 (ev. 1) 2 (ev. 2) 3 (ev. 3) A (ev. 12) - * 770 Hz 4 (ev. 4) 5 (ev. 5) 6 (ev. 6) B (ev. 13) - * 852 Hz 7 (ev. 7) 8 (ev. 8) 9 (ev. 9) C (ev. 14) - * 941 Hz * (ev. 10) 0 (ev. 0) # (ev. 11) D (ev. 15) - * - * The two tones are added to form the DTMF signal. - * - */ - -#include "dtmf_tonegen.h" - -#include "signal_processing_library.h" - -#include "neteq_error_codes.h" - -#ifdef NETEQ_ATEVENT_DECODE -/* Must compile NetEQ with DTMF support to enable the functionality */ - -/*******************/ -/* Constant tables */ -/*******************/ - -/* - * All tables corresponding to the oscillator model are organized so that - * the coefficients for a specific frequency is found in the same position - * in every table. The positions for the tones follow this layout: - * - * dummyVector[8] = - * { - * 697 Hz, 770 Hz, 852 Hz, 941 Hz, - * 1209 Hz, 1336 Hz, 1477 Hz, 1633 Hz - * }; - */ - -/* - * Tables for the constant a = 2*cos(w) = 2*cos(2*pi*f/fs) - * in the oscillator model, for 8, 16, 32 and 48 kHz sample rate. - * Table values in Q14. - */ - -const int16_t WebRtcNetEQ_dtfm_aTbl8Khz[8] = -{ - 27980, 26956, 25701, 24219, - 19073, 16325, 13085, 9315 -}; - -#ifdef NETEQ_WIDEBAND -const int16_t WebRtcNetEQ_dtfm_aTbl16Khz[8]= -{ - 31548, 31281, 30951, 30556, - 29144, 28361, 27409, 26258 -}; -#endif - -#ifdef NETEQ_32KHZ_WIDEBAND -const int16_t WebRtcNetEQ_dtfm_aTbl32Khz[8]= -{ - 32462, 32394, 32311, 32210, - 31849, 31647, 31400, 31098 -}; -#endif - -#ifdef NETEQ_48KHZ_WIDEBAND -const int16_t WebRtcNetEQ_dtfm_aTbl48Khz[8]= -{ - 32632, 32602, 32564, 32520, - 32359, 32268, 32157, 32022 -}; -#endif - -/* - * Initialization values y[-1] = sin(w) = sin(2*pi*f/fs), for 8, 16, 32 and 48 kHz sample rate. - * Table values in Q14. - */ - -const int16_t WebRtcNetEQ_dtfm_yInitTab8Khz[8] = -{ - 8528, 9315, 10163, 11036, - 13323, 14206,15021, 15708 -}; - -#ifdef NETEQ_WIDEBAND -const int16_t WebRtcNetEQ_dtfm_yInitTab16Khz[8]= -{ - 4429, 4879, 5380, 5918, - 7490, 8207, 8979, 9801 -}; -#endif - -#ifdef NETEQ_32KHZ_WIDEBAND -const int16_t WebRtcNetEQ_dtfm_yInitTab32Khz[8]= -{ - 2235, 2468, 2728, 3010, - 3853, 4249, 4685, 5164 -}; -#endif - -#ifdef NETEQ_48KHZ_WIDEBAND -const int16_t WebRtcNetEQ_dtfm_yInitTab48Khz[8]= -{ - 1493, 1649, 1823, 2013, - 2582, 2851, 3148, 3476 -}; -#endif - -/* Volume in dBm0 from 0 to -63, where 0 is the first table entry. - Everything below -36 is discarded, wherefore the table stops at -36. - Table entries are in Q14. - */ - -const int16_t WebRtcNetEQ_dtfm_dBm0[37] = { 16141, 14386, 12821, 11427, 10184, 9077, 8090, - 7210, 6426, 5727, 5104, 4549, 4054, 3614, - 3221, 2870, 2558, 2280, 2032, 1811, 1614, - 1439, 1282, 1143, 1018, 908, 809, 721, 643, - 573, 510, 455, 405, 361, 322, 287, 256 }; - -/**************************************************************************** - * WebRtcNetEQ_DTMFGenerate(...) - * - * Generate 10 ms DTMF signal according to input parameters. - * - * Input: - * - DTMFdecInst : DTMF instance - * - value : DTMF event number (0-15) - * - volume : Volume of generated signal (0-36) - * Volume is given in negative dBm0, i.e., volume == 0 - * means 0 dBm0 while volume == 36 mean -36 dBm0. - * - sampFreq : Sample rate in Hz - * - * Output: - * - signal : Pointer to vector where DTMF signal is stored; - * Vector must be at least sampFreq/100 samples long. - * - DTMFdecInst : Updated DTMF instance - * - * Return value : >0 - Number of samples written to signal - * : <0 - error - */ - -int16_t WebRtcNetEQ_DTMFGenerate(dtmf_tone_inst_t *DTMFdecInst, int16_t value, - int16_t volume, int16_t *signal, - uint16_t sampFreq, int16_t extFrameLen) -{ - const int16_t *aTbl; /* pointer to a-coefficient table */ - const int16_t *yInitTable; /* pointer to initialization value table */ - int16_t a1 = 0; /* a-coefficient for first tone (low tone) */ - int16_t a2 = 0; /* a-coefficient for second tone (high tone) */ - int i; - int frameLen; /* number of samples to generate */ - int lowIndex = 0; /* Default to avoid compiler warnings. */ - int highIndex = 4; /* Default to avoid compiler warnings. */ - int32_t tempVal; - int16_t tempValLow; - int16_t tempValHigh; - - /* Sanity check for volume */ - if ((volume < 0) || (volume > 36)) - { - return DTMF_DEC_PARAMETER_ERROR; - } - - /* Sanity check for extFrameLen */ - if (extFrameLen < -1) - { - return DTMF_DEC_PARAMETER_ERROR; - } - - /* Select oscillator coefficient tables based on sample rate */ - if (sampFreq == 8000) - { - aTbl = WebRtcNetEQ_dtfm_aTbl8Khz; - yInitTable = WebRtcNetEQ_dtfm_yInitTab8Khz; - frameLen = 80; -#ifdef NETEQ_WIDEBAND - } - else if (sampFreq == 16000) - { - aTbl = WebRtcNetEQ_dtfm_aTbl16Khz; - yInitTable = WebRtcNetEQ_dtfm_yInitTab16Khz; - frameLen = 160; -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - } - else if (sampFreq == 32000) - { - aTbl = WebRtcNetEQ_dtfm_aTbl32Khz; - yInitTable = WebRtcNetEQ_dtfm_yInitTab32Khz; - frameLen = 320; -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - } - else if (sampFreq == 48000) - { - aTbl = WebRtcNetEQ_dtfm_aTbl48Khz; - yInitTable = WebRtcNetEQ_dtfm_yInitTab48Khz; - frameLen = 480; -#endif - } - else - { - /* unsupported sample rate */ - return DTMF_GEN_UNKNOWN_SAMP_FREQ; - } - - if (extFrameLen >= 0) - { - frameLen = extFrameLen; - } - - /* select low frequency based on event value */ - switch (value) - { - case 1: - case 2: - case 3: - case 12: /* first row on keypad */ - { - lowIndex = 0; /* low frequency: 697 Hz */ - break; - } - case 4: - case 5: - case 6: - case 13: /* second row on keypad */ - { - lowIndex = 1; /* low frequency: 770 Hz */ - break; - } - case 7: - case 8: - case 9: - case 14: /* third row on keypad */ - { - lowIndex = 2; /* low frequency: 852 Hz */ - break; - } - case 0: - case 10: - case 11: - case 15: /* fourth row on keypad */ - { - lowIndex = 3; /* low frequency: 941 Hz */ - break; - } - default: - { - return DTMF_DEC_PARAMETER_ERROR; - } - } /* end switch */ - - /* select high frequency based on event value */ - switch (value) - { - case 1: - case 4: - case 7: - case 10: /* first column on keypad */ - { - highIndex = 4; /* high frequency: 1209 Hz */ - break; - } - case 2: - case 5: - case 8: - case 0: /* second column on keypad */ - { - highIndex = 5;/* high frequency: 1336 Hz */ - break; - } - case 3: - case 6: - case 9: - case 11: /* third column on keypad */ - { - highIndex = 6;/* high frequency: 1477 Hz */ - break; - } - case 12: - case 13: - case 14: - case 15: /* fourth column on keypad (special) */ - { - highIndex = 7;/* high frequency: 1633 Hz */ - break; - } - } /* end switch */ - - /* select coefficients based on results from switches above */ - a1 = aTbl[lowIndex]; /* coefficient for first (low) tone */ - a2 = aTbl[highIndex]; /* coefficient for second (high) tone */ - - if (DTMFdecInst->reinit) - { - /* set initial values for the recursive model */ - DTMFdecInst->oldOutputLow[0] = yInitTable[lowIndex]; - DTMFdecInst->oldOutputLow[1] = 0; - DTMFdecInst->oldOutputHigh[0] = yInitTable[highIndex]; - DTMFdecInst->oldOutputHigh[1] = 0; - - /* reset reinit flag */ - DTMFdecInst->reinit = 0; - } - - /* generate signal sample by sample */ - for (i = 0; i < frameLen; i++) - { - - /* Use rescursion formula y[n] = a*y[n-1] - y[n-2] */ - tempValLow - = (int16_t) (((WEBRTC_SPL_MUL_16_16(a1, DTMFdecInst->oldOutputLow[1]) - + 8192) >> 14) - DTMFdecInst->oldOutputLow[0]); - tempValHigh - = (int16_t) (((WEBRTC_SPL_MUL_16_16(a2, DTMFdecInst->oldOutputHigh[1]) - + 8192) >> 14) - DTMFdecInst->oldOutputHigh[0]); - - /* Update recursion memory */ - DTMFdecInst->oldOutputLow[0] = DTMFdecInst->oldOutputLow[1]; - DTMFdecInst->oldOutputLow[1] = tempValLow; - DTMFdecInst->oldOutputHigh[0] = DTMFdecInst->oldOutputHigh[1]; - DTMFdecInst->oldOutputHigh[1] = tempValHigh; - - /* scale high tone with 32768 (15 left shifts) - and low tone with 23171 (3dB lower than high tone) */ - tempVal = WEBRTC_SPL_MUL_16_16(DTMF_AMP_LOW, tempValLow) - + WEBRTC_SPL_LSHIFT_W32((int32_t)tempValHigh, 15); - - /* Norm the signal to Q14 (with proper rounding) */ - tempVal = (tempVal + 16384) >> 15; - - /* Scale the signal to correct dbM0 value */ - signal[i] = (int16_t) WEBRTC_SPL_RSHIFT_W32( - (WEBRTC_SPL_MUL_16_16(tempVal, WebRtcNetEQ_dtfm_dBm0[volume]) - + 8192), 14); /* volume value is in Q14; use proper rounding */ - } - - return frameLen; - -} - -#endif /* NETEQ_ATEVENT_DECODE */ - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tone_generator.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tone_generator.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tone_generator.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tone_generator.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This class provides a generator for DTMF tones. The tone generation is based +// on a sinusoid recursion. Each sinusoid is generated using a recursion +// formula; x[n] = a * x[n-1] - x[n-2], where the coefficient +// a = 2*cos(2*pi*f/fs). The recursion is started with x[-1] = 0 and +// x[-2] = sin(2*pi*f/fs). (Note that with this initialization, the resulting +// sinusoid gets a "negative" rotation; x[n] = sin(-2*pi*f/fs * n + phi), but +// kept this way due to historical reasons.) +// TODO(hlundin): Change to positive rotation? +// +// Each key on the telephone keypad corresponds to an "event", 0-15. Each event +// is mapped to a tone pair, with a low and a high frequency. There are four +// low and four high frequencies, each corresponding to a row and column, +// respectively, on the keypad as illustrated below. +// +// 1209 Hz 1336 Hz 1477 Hz 1633 Hz +// 697 Hz 1 2 3 12 +// 770 Hz 4 5 6 13 +// 852 Hz 7 8 9 14 +// 941 Hz 10 0 11 15 + +#include "webrtc/modules/audio_coding/neteq/dtmf_tone_generator.h" + +#include + +namespace webrtc { + +// The filter coefficient a = 2*cos(2*pi*f/fs) for the low frequency tone, for +// sample rates fs = {8000, 16000, 32000, 48000} Hz, and events 0 through 15. +// Values are in Q14. +const int DtmfToneGenerator::kCoeff1[4][16] = { + { 24219, 27980, 27980, 27980, 26956, 26956, 26956, 25701, 25701, 25701, + 24219, 24219, 27980, 26956, 25701, 24219 }, + { 30556, 31548, 31548, 31548, 31281, 31281, 31281, 30951, 30951, 30951, + 30556, 30556, 31548, 31281, 30951, 30556 }, + { 32210, 32462, 32462, 32462, 32394, 32394, 32394, 32311, 32311, 32311, + 32210, 32210, 32462, 32394, 32311, 32210 }, + { 32520, 32632, 32632, 32632, 32602, 32602, 32602, 32564, 32564, 32564, + 32520, 32520, 32632, 32602, 32564, 32520 } }; + +// The filter coefficient a = 2*cos(2*pi*f/fs) for the high frequency tone, for +// sample rates fs = {8000, 16000, 32000, 48000} Hz, and events 0 through 15. +// Values are in Q14. +const int DtmfToneGenerator::kCoeff2[4][16] = { + { 16325, 19073, 16325, 13085, 19073, 16325, 13085, 19073, 16325, 13085, + 19073, 13085, 9315, 9315, 9315, 9315}, + { 28361, 29144, 28361, 27409, 29144, 28361, 27409, 29144, 28361, 27409, + 29144, 27409, 26258, 26258, 26258, 26258}, + { 31647, 31849, 31647, 31400, 31849, 31647, 31400, 31849, 31647, 31400, + 31849, 31400, 31098, 31098, 31098, 31098}, + { 32268, 32359, 32268, 32157, 32359, 32268, 32157, 32359, 32268, 32157, + 32359, 32157, 32022, 32022, 32022, 32022} }; + +// The initialization value x[-2] = sin(2*pi*f/fs) for the low frequency tone, +// for sample rates fs = {8000, 16000, 32000, 48000} Hz, and events 0-15. +// Values are in Q14. +const int DtmfToneGenerator::kInitValue1[4][16] = { + { 11036, 8528, 8528, 8528, 9315, 9315, 9315, 10163, 10163, 10163, 11036, + 11036, 8528, 9315, 10163, 11036}, + { 5918, 4429, 4429, 4429, 4879, 4879, 4879, 5380, 5380, 5380, 5918, 5918, + 4429, 4879, 5380, 5918}, + { 3010, 2235, 2235, 2235, 2468, 2468, 2468, 2728, 2728, 2728, 3010, 3010, + 2235, 2468, 2728, 3010}, + { 2013, 1493, 1493, 1493, 1649, 1649, 1649, 1823, 1823, 1823, 2013, 2013, + 1493, 1649, 1823, 2013 } }; + +// The initialization value x[-2] = sin(2*pi*f/fs) for the high frequency tone, +// for sample rates fs = {8000, 16000, 32000, 48000} Hz, and events 0-15. +// Values are in Q14. +const int DtmfToneGenerator::kInitValue2[4][16] = { + { 14206, 13323, 14206, 15021, 13323, 14206, 15021, 13323, 14206, 15021, + 13323, 15021, 15708, 15708, 15708, 15708}, + { 8207, 7490, 8207, 8979, 7490, 8207, 8979, 7490, 8207, 8979, 7490, 8979, + 9801, 9801, 9801, 9801}, + { 4249, 3853, 4249, 4685, 3853, 4249, 4685, 3853, 4249, 4685, 3853, 4685, + 5164, 5164, 5164, 5164}, + { 2851, 2582, 2851, 3148, 2582, 2851, 3148, 2582, 2851, 3148, 2582, 3148, + 3476, 3476, 3476, 3476} }; + +// Amplitude multipliers for volume values 0 through 36, corresponding to +// 0 dBm0 through -36 dBm0. Values are in Q14. +const int DtmfToneGenerator::kAmplitude[37] = { + 16141, 14386, 12821, 11427, 10184, 9077, 8090, 7210, 6426, 5727, 5104, 4549, + 4054, 3614, 3221, 2870, 2558, 2280, 2032, 1811, 1614, 1439, 1282, 1143, + 1018, 908, 809, 721, 643, 573, 510, 455, 405, 361, 322, 287, 256 }; + +// Constructor. +DtmfToneGenerator::DtmfToneGenerator() + : initialized_(false), + coeff1_(0), + coeff2_(0), + amplitude_(0) { +} + +// Initialize the DTMF generator with sample rate fs Hz (8000, 16000, 32000, +// 48000), event (0-15) and attenuation (0-36 dB). +// Returns 0 on success, otherwise an error code. +int DtmfToneGenerator::Init(int fs, int event, int attenuation) { + initialized_ = false; + int fs_index; + if (fs == 8000) { + fs_index = 0; + } else if (fs == 16000) { + fs_index = 1; + } else if (fs == 32000) { + fs_index = 2; + } else if (fs == 48000) { + fs_index = 3; + } else { + assert(false); + fs_index = 1; // Default to 8000 Hz. + } + + if (event < 0 || event > 15) { + return kParameterError; // Invalid event number. + } + + if (attenuation < 0 || attenuation > 36) { + return kParameterError; // Invalid attenuation. + } + + // Look up oscillator coefficient for low and high frequencies. + coeff1_ = kCoeff1[fs_index][event]; + coeff2_ = kCoeff2[fs_index][event]; + // Look up amplitude multiplier. + amplitude_ = kAmplitude[attenuation]; + // Initialize sample history. + sample_history1_[0] = kInitValue1[fs_index][event]; + sample_history1_[1] = 0; + sample_history2_[0] = kInitValue2[fs_index][event]; + sample_history2_[1] = 0; + + initialized_ = true; + return 0; +} + +// Reset tone generator to uninitialized state. +void DtmfToneGenerator::Reset() { + initialized_ = false; +} + +// Generate num_samples of DTMF signal and write to |output|. +int DtmfToneGenerator::Generate(int num_samples, + AudioMultiVector* output) { + if (!initialized_) { + return kNotInitialized; + } + + if (num_samples < 0 || !output) { + return kParameterError; + } + + output->AssertSize(num_samples); + for (int i = 0; i < num_samples; ++i) { + // Use recursion formula y[n] = a * y[n - 1] - y[n - 2]. + int16_t temp_val_low = ((coeff1_ * sample_history1_[1] + 8192) >> 14) + - sample_history1_[0]; + int16_t temp_val_high = ((coeff2_ * sample_history2_[1] + 8192) >> 14) + - sample_history2_[0]; + + // Update recursion memory. + sample_history1_[0] = sample_history1_[1]; + sample_history1_[1] = temp_val_low; + sample_history2_[0] = sample_history2_[1]; + sample_history2_[1] = temp_val_high; + + // Attenuate the low frequency tone 3 dB. + int32_t temp_val = kAmpMultiplier * temp_val_low + (temp_val_high << 15); + // Normalize the signal to Q14 with proper rounding. + temp_val = (temp_val + 16384) >> 15; + // Scale the signal to correct volume. + (*output)[0][i] = + static_cast((temp_val * amplitude_ + 8192) >> 14); + } + // Copy first channel to all other channels. + for (size_t channel = 1; channel < output->Channels(); ++channel) { + output->CopyChannel(0, channel); + } + + return num_samples; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tone_generator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tone_generator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tone_generator.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tone_generator.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_DTMF_TONE_GENERATOR_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_DTMF_TONE_GENERATOR_H_ + + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// This class provides a generator for DTMF tones. +class DtmfToneGenerator { + public: + enum ReturnCodes { + kNotInitialized = -1, + kParameterError = -2, + }; + + DtmfToneGenerator(); + virtual ~DtmfToneGenerator() {} + virtual int Init(int fs, int event, int attenuation); + virtual void Reset(); + virtual int Generate(int num_samples, AudioMultiVector* output); + virtual bool initialized() const { return initialized_; } + + private: + static const int kCoeff1[4][16]; // 1st oscillator model coefficient table. + static const int kCoeff2[4][16]; // 2nd oscillator model coefficient table. + static const int kInitValue1[4][16]; // Initialization for 1st oscillator. + static const int kInitValue2[4][16]; // Initialization for 2nd oscillator. + static const int kAmplitude[37]; // Amplitude for 0 through -36 dBm0. + static const int16_t kAmpMultiplier = 23171; // 3 dB attenuation (in Q15). + + bool initialized_; // True if generator is initialized properly. + int coeff1_; // 1st oscillator coefficient for this event. + int coeff2_; // 2nd oscillator coefficient for this event. + int amplitude_; // Amplitude for this event. + int16_t sample_history1_[2]; // Last 2 samples for the 1st oscillator. + int16_t sample_history2_[2]; // Last 2 samples for the 2nd oscillator. + + DISALLOW_COPY_AND_ASSIGN(DtmfToneGenerator); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_DTMF_TONE_GENERATOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tone_generator_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tone_generator_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tone_generator_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tone_generator_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for DtmfToneGenerator class. + +#include "webrtc/modules/audio_coding/neteq/dtmf_tone_generator.h" + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" + +namespace webrtc { + +class DtmfToneGeneratorTest : public ::testing::Test { + protected: + static const double kLowFreqHz[16]; + static const double kHighFreqHz[16]; + // This is the attenuation applied to all cases. + const double kBaseAttenuation = 16141.0 / 16384.0; + const double k3dbAttenuation = 23171.0 / 32768; + const int kNumSamples = 10; + + void TestAllTones(int fs_hz, int channels) { + AudioMultiVector signal(channels); + + for (int event = 0; event <= 15; ++event) { + std::ostringstream ss; + ss << "Checking event " << event << " at sample rate " << fs_hz; + SCOPED_TRACE(ss.str()); + const int kAttenuation = 0; + ASSERT_EQ(0, tone_gen_.Init(fs_hz, event, kAttenuation)); + EXPECT_TRUE(tone_gen_.initialized()); + EXPECT_EQ(kNumSamples, tone_gen_.Generate(kNumSamples, &signal)); + + double f1 = kLowFreqHz[event]; + double f2 = kHighFreqHz[event]; + const double pi = 3.14159265358979323846; + + for (int n = 0; n < kNumSamples; ++n) { + double x = k3dbAttenuation * sin(2.0 * pi * f1 / fs_hz * (-n - 1)) + + sin(2.0 * pi * f2 / fs_hz * (-n - 1)); + x *= kBaseAttenuation; + x = ldexp(x, 14); // Scale to Q14. + for (int channel = 0; channel < channels; ++channel) { + EXPECT_NEAR(x, static_cast(signal[channel][n]), 25); + } + } + + tone_gen_.Reset(); + EXPECT_FALSE(tone_gen_.initialized()); + } + } + + void TestAmplitudes(int fs_hz, int channels) { + AudioMultiVector signal(channels); + AudioMultiVector ref_signal(channels); + + const int event_vec[] = {0, 4, 9, 13}; // Test a few events. + for (int e = 0; e < 4; ++e) { + int event = event_vec[e]; + // Create full-scale reference. + ASSERT_EQ(0, tone_gen_.Init(fs_hz, event, 0)); // 0 attenuation. + EXPECT_EQ(kNumSamples, tone_gen_.Generate(kNumSamples, &ref_signal)); + // Test every 5 steps (to save time). + for (int attenuation = 1; attenuation <= 36; attenuation += 5) { + std::ostringstream ss; + ss << "Checking event " << event << " at sample rate " << fs_hz; + ss << "; attenuation " << attenuation; + SCOPED_TRACE(ss.str()); + ASSERT_EQ(0, tone_gen_.Init(fs_hz, event, attenuation)); + EXPECT_EQ(kNumSamples, tone_gen_.Generate(kNumSamples, &signal)); + for (int n = 0; n < kNumSamples; ++n) { + double attenuation_factor = + pow(10, -static_cast(attenuation) / 20); + // Verify that the attenuation is correct. + for (int channel = 0; channel < channels; ++channel) { + EXPECT_NEAR(attenuation_factor * ref_signal[channel][n], + signal[channel][n], + 2); + } + } + + tone_gen_.Reset(); + } + } + } + + DtmfToneGenerator tone_gen_; +}; + +// Low and high frequencies for events 0 through 15. +const double DtmfToneGeneratorTest::kLowFreqHz[16] = { + 941.0, 697.0, 697.0, 697.0, 770.0, 770.0, 770.0, 852.0, + 852.0, 852.0, 941.0, 941.0, 697.0, 770.0, 852.0, 941.0}; +const double DtmfToneGeneratorTest::kHighFreqHz[16] = { + 1336.0, 1209.0, 1336.0, 1477.0, 1209.0, 1336.0, 1477.0, 1209.0, + 1336.0, 1477.0, 1209.0, 1477.0, 1633.0, 1633.0, 1633.0, 1633.0}; + +TEST_F(DtmfToneGeneratorTest, Test8000Mono) { + TestAllTones(8000, 1); + TestAmplitudes(8000, 1); +} + +TEST_F(DtmfToneGeneratorTest, Test16000Mono) { + TestAllTones(16000, 1); + TestAmplitudes(16000, 1); +} + +TEST_F(DtmfToneGeneratorTest, Test32000Mono) { + TestAllTones(32000, 1); + TestAmplitudes(32000, 1); +} + +TEST_F(DtmfToneGeneratorTest, Test48000Mono) { + TestAllTones(48000, 1); + TestAmplitudes(48000, 1); +} + +TEST_F(DtmfToneGeneratorTest, Test8000Stereo) { + TestAllTones(8000, 2); + TestAmplitudes(8000, 2); +} + +TEST_F(DtmfToneGeneratorTest, Test16000Stereo) { + TestAllTones(16000, 2); + TestAmplitudes(16000, 2); +} + +TEST_F(DtmfToneGeneratorTest, Test32000Stereo) { + TestAllTones(32000, 2); + TestAmplitudes(32000, 2); +} + +TEST_F(DtmfToneGeneratorTest, Test48000Stereo) { + TestAllTones(48000, 2); + TestAmplitudes(48000, 2); +} + +TEST(DtmfToneGenerator, TestErrors) { + DtmfToneGenerator tone_gen; + const int kNumSamples = 10; + AudioMultiVector signal(1); // One channel. + + // Try to generate tones without initializing. + EXPECT_EQ(DtmfToneGenerator::kNotInitialized, + tone_gen.Generate(kNumSamples, &signal)); + + const int fs = 16000; // Valid sample rate. + const int event = 7; // Valid event. + const int attenuation = 0; // Valid attenuation. + // Initialize with invalid event -1. + EXPECT_EQ(DtmfToneGenerator::kParameterError, + tone_gen.Init(fs, -1, attenuation)); + // Initialize with invalid event 16. + EXPECT_EQ(DtmfToneGenerator::kParameterError, + tone_gen.Init(fs, 16, attenuation)); + // Initialize with invalid attenuation -1. + EXPECT_EQ(DtmfToneGenerator::kParameterError, tone_gen.Init(fs, event, -1)); + // Initialize with invalid attenuation 37. + EXPECT_EQ(DtmfToneGenerator::kParameterError, tone_gen.Init(fs, event, 37)); + EXPECT_FALSE(tone_gen.initialized()); // Should still be uninitialized. + + // Initialize with valid parameters. + ASSERT_EQ(0, tone_gen.Init(fs, event, attenuation)); + EXPECT_TRUE(tone_gen.initialized()); + // Negative number of samples. + EXPECT_EQ(DtmfToneGenerator::kParameterError, tone_gen.Generate(-1, &signal)); + // NULL pointer to destination. + EXPECT_EQ(DtmfToneGenerator::kParameterError, + tone_gen.Generate(kNumSamples, NULL)); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tonegen.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tonegen.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tonegen.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/dtmf_tonegen.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the DTMF tone generator function. - */ - -#ifndef DTMF_TONEGEN_H -#define DTMF_TONEGEN_H - -#include "typedefs.h" - -#include "neteq_defines.h" - -#ifdef NETEQ_ATEVENT_DECODE -/* Must compile NetEQ with DTMF support to enable the functionality */ - -#define DTMF_AMP_LOW 23171 /* 3 dB lower than the high frequency */ - -/* The DTMF generator struct (part of DSP main struct DSPInst_t) */ -typedef struct dtmf_tone_inst_t_ -{ - - int16_t reinit; /* non-zero if the oscillator model should - be reinitialized for next event */ - int16_t oldOutputLow[2]; /* oscillator recursion history (low tone) */ - int16_t oldOutputHigh[2]; /* oscillator recursion history (high tone) */ - - int lastDtmfSample; /* index to the first non-DTMF sample in the - speech history, if non-negative */ -}dtmf_tone_inst_t; - -/**************************************************************************** - * WebRtcNetEQ_DTMFGenerate(...) - * - * Generate 10 ms DTMF signal according to input parameters. - * - * Input: - * - DTMFdecInst : DTMF instance - * - value : DTMF event number (0-15) - * - volume : Volume of generated signal (0-36) - * Volume is given in negative dBm0, i.e., volume == 0 - * means 0 dBm0 while volume == 36 mean -36 dBm0. - * - sampFreq : Sample rate in Hz - * - * Output: - * - signal : Pointer to vector where DTMF signal is stored; - * Vector must be at least sampFreq/100 samples long. - * - DTMFdecInst : Updated DTMF instance - * - * Return value : >0 - Number of samples written to signal - * : <0 - Error - */ - -int16_t WebRtcNetEQ_DTMFGenerate(dtmf_tone_inst_t *DTMFdecInst, - int16_t value, - int16_t volume, - int16_t *signal, - uint16_t sampFreq, - int16_t frameLen -); - -#endif /* NETEQ_ATEVENT_DECODE */ - -#endif /* DTMF_TONEGEN_H */ - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/expand.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/expand.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/expand.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/expand.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1220 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This is the function to expand from the speech history, to produce concealment data or - * increasing delay. - */ - -#include "dsp.h" - -#include - -#include "signal_processing_library.h" - -#include "dsp_helpfunctions.h" -#include "neteq_error_codes.h" - -#define CHECK_NO_OF_CORRMAX 3 -#define DISTLEN 20 -#define LPCANALASYSLEN 160 - -/* Scratch usage: - - Type Name size startpos endpos - (First part of first expand) - int16_t pw16_bestCorrIndex 3 0 2 - int16_t pw16_bestCorr 3 3 5 - int16_t pw16_bestDistIndex 3 6 8 - int16_t pw16_bestDist 3 9 11 - int16_t pw16_corrVec 102*fs/8000 12 11+102*fs/8000 - func WebRtcNetEQ_Correlator 232 12+102*fs/8000 243+102*fs/8000 - - (Second part of first expand) - int32_t pw32_corr2 99*fs/8000+1 0 99*fs/8000 - int32_t pw32_autoCorr 2*7 0 13 - int16_t pw16_rc 6 14 19 - - Signal combination: - int16_t pw16_randVec 30+120*fs/8000 0 29+120*fs/8000 - int16_t pw16_scaledRandVec 125*fs/8000 30+120*fs/8000 29+245*fs/8000 - int16_t pw16_unvoicedVecSpace 10+125*fs/8000 30+245*fs/8000 39+370*fs/8000 - - Total: 40+370*fs/8000 (size depends on UNVOICED_LPC_ORDER and BGN_LPC_ORDER) - */ - -#if ((BGN_LPC_ORDER > 10) || (UNVOICED_LPC_ORDER > 10)) && (defined SCRATCH) -#error BGN_LPC_ORDER and/or BGN_LPC_ORDER are too large for current scratch memory allocation -#endif - -#define SCRATCH_PW16_BEST_CORR_INDEX 0 -#define SCRATCH_PW16_BEST_CORR 3 -#define SCRATCH_PW16_BEST_DIST_INDEX 6 -#define SCRATCH_PW16_BEST_DIST 9 -#define SCRATCH_PW16_CORR_VEC 12 -#define SCRATCH_PW16_CORR2 0 -#define SCRATCH_PW32_AUTO_CORR 0 -#define SCRATCH_PW16_RC 14 -#define SCRATCH_PW16_RAND_VEC 0 - -#if (defined(NETEQ_48KHZ_WIDEBAND)) -#define SCRATCH_NETEQDSP_CORRELATOR 624 -#define SCRATCH_PW16_SCALED_RAND_VEC 750 -#define SCRATCH_PW16_UNVOICED_VEC_SPACE 1500 -#elif (defined(NETEQ_32KHZ_WIDEBAND)) -#define SCRATCH_NETEQDSP_CORRELATOR 420 -#define SCRATCH_PW16_SCALED_RAND_VEC 510 -#define SCRATCH_PW16_UNVOICED_VEC_SPACE 1010 -#elif (defined(NETEQ_WIDEBAND)) -#define SCRATCH_NETEQDSP_CORRELATOR 216 -#define SCRATCH_PW16_SCALED_RAND_VEC 270 -#define SCRATCH_PW16_UNVOICED_VEC_SPACE 520 -#else /* NB */ -#define SCRATCH_NETEQDSP_CORRELATOR 114 -#define SCRATCH_PW16_SCALED_RAND_VEC 150 -#define SCRATCH_PW16_UNVOICED_VEC_SPACE 275 -#endif - -/**************************************************************************** - * WebRtcNetEQ_Expand(...) - * - * This function produces one "chunk" of expansion data (PLC audio). The - * length of the produced audio depends on the speech history. - * - * Input: - * - inst : DSP instance - * - scratchPtr : Pointer to scratch vector - * - outdata : Pointer to a memory space where the output data - * should be stored - * - BGNonly : If non-zero, "expand" will only produce background noise. - * - pw16_len : Desired number of samples (only for BGN mode). - * - * Output: - * - inst : Updated instance - * - pw16_len : Number of samples that were output from NetEq - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_Expand(DSPInst_t *inst, -#ifdef SCRATCH - int16_t *pw16_scratchPtr, -#endif - int16_t *pw16_outData, int16_t *pw16_len, - int16_t BGNonly) -{ - - int16_t fs_mult; - ExpandInst_t *ExpandState = &(inst->ExpandInst); - BGNInst_t *BGNState = &(inst->BGNInst); - int i; -#ifdef SCRATCH - int16_t *pw16_randVec = pw16_scratchPtr + SCRATCH_PW16_RAND_VEC; - int16_t *pw16_scaledRandVec = pw16_scratchPtr + SCRATCH_PW16_SCALED_RAND_VEC; - int16_t *pw16_unvoicedVecSpace = pw16_scratchPtr + SCRATCH_PW16_UNVOICED_VEC_SPACE; -#else - int16_t pw16_randVec[FSMULT * 120 + 30]; /* 150 for NB and 270 for WB */ - int16_t pw16_scaledRandVec[FSMULT * 125]; /* 125 for NB and 250 for WB */ - int16_t pw16_unvoicedVecSpace[BGN_LPC_ORDER + FSMULT * 125]; -#endif - /* 125 for NB and 250 for WB etc. Reuse pw16_outData[] for this vector */ - int16_t *pw16_voicedVecStorage = pw16_outData; - int16_t *pw16_voicedVec = &pw16_voicedVecStorage[ExpandState->w16_overlap]; - int16_t *pw16_unvoicedVec = pw16_unvoicedVecSpace + UNVOICED_LPC_ORDER; - int16_t *pw16_cngVec = pw16_unvoicedVecSpace + BGN_LPC_ORDER; - int16_t w16_expVecsLen, w16_lag = 0, w16_expVecPos; - int16_t w16_randLen; - int16_t w16_vfractionChange; /* in Q14 */ - int16_t w16_winMute = 0, w16_winMuteInc = 0, w16_winUnMute = 0, w16_winUnMuteInc = 0; - int32_t w32_tmp; - int16_t w16_tmp, w16_tmp2; - int16_t stability; - enum BGNMode bgnMode = inst->BGNInst.bgnMode; - - /* Pre-calculate common multiplications with fs_mult */ - int16_t fsMult4; - int16_t fsMult20; - int16_t fsMult120; - int16_t fsMultDistLen; - int16_t fsMultLPCAnalasysLen; - -#ifdef NETEQ_STEREO - MasterSlaveInfo *msInfo = inst->msInfo; -#endif - - /* fs is uint16_t (to hold fs=48000) */ - fs_mult = WebRtcNetEQ_CalcFsMult(inst->fs); /* calculate fs/8000 */ - - /* Pre-calculate common multiplications with fs_mult */ - fsMult4 = (int16_t) WEBRTC_SPL_MUL_16_16(fs_mult, 4); - fsMult20 = (int16_t) WEBRTC_SPL_MUL_16_16(fs_mult, 20); - fsMult120 = (int16_t) WEBRTC_SPL_MUL_16_16(fs_mult, 120); - fsMultDistLen = (int16_t) WEBRTC_SPL_MUL_16_16(fs_mult, DISTLEN); - fsMultLPCAnalasysLen = (int16_t) WEBRTC_SPL_MUL_16_16(fs_mult, LPCANALASYSLEN); - - /* - * Perform all the initial setup if it's the first expansion. - * If background noise (BGN) only, this setup is not needed. - */ - if (ExpandState->w16_consecExp == 0 && !BGNonly) - { - /* Setup more variables */ -#ifdef SCRATCH - int32_t *pw32_autoCorr = (int32_t*) (pw16_scratchPtr - + SCRATCH_PW32_AUTO_CORR); - int16_t *pw16_rc = pw16_scratchPtr + SCRATCH_PW16_RC; - int16_t *pw16_bestCorrIndex = pw16_scratchPtr + SCRATCH_PW16_BEST_CORR_INDEX; - int16_t *pw16_bestCorr = pw16_scratchPtr + SCRATCH_PW16_BEST_CORR; - int16_t *pw16_bestDistIndex = pw16_scratchPtr + SCRATCH_PW16_BEST_DIST_INDEX; - int16_t *pw16_bestDist = pw16_scratchPtr + SCRATCH_PW16_BEST_DIST; - int16_t *pw16_corrVec = pw16_scratchPtr + SCRATCH_PW16_CORR_VEC; - int32_t *pw32_corr2 = (int32_t*) (pw16_scratchPtr + SCRATCH_PW16_CORR2); -#else - int32_t pw32_autoCorr[UNVOICED_LPC_ORDER+1]; - int16_t pw16_rc[UNVOICED_LPC_ORDER]; - int16_t pw16_corrVec[FSMULT*102]; /* 102 for NB */ - int16_t pw16_bestCorrIndex[CHECK_NO_OF_CORRMAX]; - int16_t pw16_bestCorr[CHECK_NO_OF_CORRMAX]; - int16_t pw16_bestDistIndex[CHECK_NO_OF_CORRMAX]; - int16_t pw16_bestDist[CHECK_NO_OF_CORRMAX]; - int32_t pw32_corr2[(99*FSMULT)+1]; -#endif - int32_t pw32_bestDist[CHECK_NO_OF_CORRMAX]; - int16_t w16_ind = 0; - int16_t w16_corrVecLen; - int16_t w16_corrScale; - int16_t w16_distScale; - int16_t w16_indMin, w16_indMax; - int16_t w16_len; - int32_t w32_en1, w32_en2, w32_cc; - int16_t w16_en1Scale, w16_en2Scale; - int16_t w16_en1, w16_en2; - int32_t w32_en1_mul_en2; - int16_t w16_sqrt_en1en2; - int16_t w16_ccShiftL; - int16_t w16_bestcorr; /* Correlation in Q14 */ - int16_t *pw16_vec1, *pw16_vec2; - int16_t w16_factor; - int16_t w16_DistLag, w16_CorrLag, w16_diffLag; - int16_t w16_energyLen; - int16_t w16_slope; - int16_t w16_startInd; - int16_t w16_noOfcorr2; - int16_t w16_scale; - - /* Initialize some variables */ - ExpandState->w16_lagsDirection = 1; - ExpandState->w16_lagsPosition = -1; - ExpandState->w16_expandMuteFactor = 16384; /* Start from 1.0 (Q14) */ - BGNState->w16_mutefactor = 0; /* Start with 0 gain for BGN (value in Q14) */ - inst->w16_seedInc = 1; - -#ifdef NETEQ_STEREO - /* Sanity for msInfo */ - if (msInfo == NULL) - { - /* this should not happen here */ - return MASTER_SLAVE_ERROR; - } - - /* - * Do not calculate correlations for slave instance(s) - * unless lag info from master is corrupt - */ - if ((msInfo->msMode != NETEQ_SLAVE) - || ((msInfo->distLag <= 0) || (msInfo->corrLag <= 0))) - { -#endif - /* Calculate correlation vector in downsampled domain (4 kHz sample rate) */ - w16_corrVecLen = WebRtcNetEQ_Correlator(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQDSP_CORRELATOR, -#endif - inst->pw16_speechHistory, inst->w16_speechHistoryLen, pw16_corrVec, - &w16_corrScale); - - /* Find peaks in correlation vector using parabolic fit method */ - WebRtcNetEQ_PeakDetection(pw16_corrVec, w16_corrVecLen, CHECK_NO_OF_CORRMAX, fs_mult, - pw16_bestCorrIndex, pw16_bestCorr); - - /* - * Adjust peak locations; cross-correlation lags start at 2.5 ms - * (20*fs_mult samples) - */ - pw16_bestCorrIndex[0] += fsMult20; - pw16_bestCorrIndex[1] += fsMult20; - pw16_bestCorrIndex[2] += fsMult20; - - /* Calculate distortion around the 3 (CHECK_NO_OF_CORRMAX) best lags */ - w16_distScale = 0; - for (i = 0; i < CHECK_NO_OF_CORRMAX; i++) - { - w16_tmp = fsMult20; - w16_tmp2 = pw16_bestCorrIndex[i] - fsMult4; - w16_indMin = WEBRTC_SPL_MAX(w16_tmp, w16_tmp2); - w16_tmp = fsMult120 - 1; - w16_tmp2 = pw16_bestCorrIndex[i] + fsMult4; - w16_indMax = WEBRTC_SPL_MIN(w16_tmp, w16_tmp2); - - pw16_bestDistIndex[i] = WebRtcNetEQ_MinDistortion( - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - fsMultDistLen]), - w16_indMin, w16_indMax, fsMultDistLen, &pw32_bestDist[i]); - - w16_distScale - = WEBRTC_SPL_MAX(16 - WebRtcSpl_NormW32(pw32_bestDist[i]), w16_distScale); - - } - - /* Shift the distortion values to fit in int16_t */ - WebRtcSpl_VectorBitShiftW32ToW16(pw16_bestDist, CHECK_NO_OF_CORRMAX, pw32_bestDist, - w16_distScale); - - /* - * Find index of maximum criteria, where crit[i] = bestCorr[i])/(bestDist[i]) - * Do this by a cross multiplication. - */ - - w32_en1 = WEBRTC_SPL_MUL_16_16((int32_t) pw16_bestCorr[0],pw16_bestDist[1]); - w32_en2 = WEBRTC_SPL_MUL_16_16((int32_t) pw16_bestCorr[1],pw16_bestDist[0]); - if (w32_en1 >= w32_en2) - { - /* 0 wins over 1 */ - w32_en1 - = WEBRTC_SPL_MUL_16_16((int32_t) pw16_bestCorr[0], pw16_bestDist[2]); - w32_en2 - = WEBRTC_SPL_MUL_16_16((int32_t) pw16_bestCorr[2], pw16_bestDist[0]); - if (w32_en1 >= w32_en2) - { - /* 0 wins over 2 */ - w16_ind = 0; - } - else - { - /* 2 wins over 0 */ - w16_ind = 2; - } - } - else - { - /* 1 wins over 0 */ - w32_en1 - = WEBRTC_SPL_MUL_16_16((int32_t) pw16_bestCorr[1],pw16_bestDist[2]); - w32_en2 - = WEBRTC_SPL_MUL_16_16((int32_t) pw16_bestCorr[2],pw16_bestDist[1]); - if ((int32_t) w32_en1 >= (int32_t) w32_en2) - { - /* 1 wins over 2 */ - w16_ind = 1; - } - else - { - /* 2 wins over 1 */ - w16_ind = 2; - } - } - -#ifdef NETEQ_STEREO - } - - /* Store DistLag and CorrLag of the position with highest criteria */ - if ((msInfo->msMode == NETEQ_MASTER) || (msInfo->msMode == NETEQ_MONO) - || ((msInfo->msMode == NETEQ_SLAVE) && (msInfo->distLag <= 0 || msInfo->corrLag - <= 0))) - { - /* lags not provided externally */ - w16_DistLag = pw16_bestDistIndex[w16_ind]; - w16_CorrLag = pw16_bestCorrIndex[w16_ind]; - if (msInfo->msMode == NETEQ_MASTER) - { - msInfo->distLag = w16_DistLag; - msInfo->corrLag = w16_CorrLag; - } - } - else if (msInfo->msMode == NETEQ_SLAVE) - { - /* lags provided externally (from master) */ - w16_DistLag = msInfo->distLag; - w16_CorrLag = msInfo->corrLag; - - /* sanity for lag values */ - if ((w16_DistLag <= 0) || (w16_CorrLag <= 0)) - { - return MASTER_SLAVE_ERROR; - } - } - else - { - /* Invalid mode */ - return MASTER_SLAVE_ERROR; - } -#else /* not NETEQ_STEREO */ - w16_DistLag = pw16_bestDistIndex[w16_ind]; - w16_CorrLag = pw16_bestCorrIndex[w16_ind]; -#endif - - ExpandState->w16_maxLag = WEBRTC_SPL_MAX(w16_DistLag, w16_CorrLag); - - /* Calculate the exact best correlation (in the range within CorrLag-DistLag) */ - w16_len = w16_DistLag + 10; - w16_len = WEBRTC_SPL_MIN(w16_len, fsMult120); - w16_len = WEBRTC_SPL_MAX(w16_len, 60 * fs_mult); - - w16_startInd = WEBRTC_SPL_MIN(w16_DistLag, w16_CorrLag); - w16_noOfcorr2 = WEBRTC_SPL_ABS_W16((w16_DistLag-w16_CorrLag)) + 1; - /* w16_noOfcorr2 maximum value is 99*fs_mult + 1 */ - - /* Calculate suitable scaling */ - w16_tmp - = WebRtcSpl_MaxAbsValueW16( - &inst->pw16_speechHistory[inst->w16_speechHistoryLen - w16_len - w16_startInd - - w16_noOfcorr2], - (int16_t) (w16_len + w16_startInd + w16_noOfcorr2 - 1)); - w16_corrScale = ((31 - WebRtcSpl_NormW32(WEBRTC_SPL_MUL_16_16(w16_tmp, w16_tmp))) - + (31 - WebRtcSpl_NormW32(w16_len))) - 31; - w16_corrScale = WEBRTC_SPL_MAX(0, w16_corrScale); - - /* - * Perform the correlation, store in pw32_corr2 - */ - - WebRtcNetEQ_CrossCorr(pw32_corr2, - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - w16_len]), - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - w16_len - w16_startInd]), - w16_len, w16_noOfcorr2, w16_corrScale, -1); - - /* Find maximizing index */ - w16_ind = WebRtcSpl_MaxIndexW32(pw32_corr2, w16_noOfcorr2); - w32_cc = pw32_corr2[w16_ind]; /* this is maximum correlation */ - w16_ind = w16_ind + w16_startInd; /* correct index for start offset */ - - /* Calculate energies */ - w32_en1 = WebRtcNetEQ_DotW16W16( - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - w16_len]), - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - w16_len]), w16_len, - w16_corrScale); - w32_en2 = WebRtcNetEQ_DotW16W16( - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - w16_len - w16_ind]), - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - w16_len - w16_ind]), - w16_len, w16_corrScale); - - /* Calculate the correlation value w16_bestcorr */ - if ((w32_en1 > 0) && (w32_en2 > 0)) - { - w16_en1Scale = 16 - WebRtcSpl_NormW32(w32_en1); - w16_en1Scale = WEBRTC_SPL_MAX(0, w16_en1Scale); - w16_en2Scale = 16 - WebRtcSpl_NormW32(w32_en2); - w16_en2Scale = WEBRTC_SPL_MAX(0, w16_en2Scale); - /* Make sure total scaling is even (to simplify scale factor after sqrt) */ - if ((w16_en1Scale + w16_en2Scale) & 1) - { - /* if sum is odd */ - w16_en1Scale += 1; - } - w16_en1 = (int16_t) WEBRTC_SPL_RSHIFT_W32(w32_en1, w16_en1Scale); - w16_en2 = (int16_t) WEBRTC_SPL_RSHIFT_W32(w32_en2, w16_en2Scale); - w32_en1_mul_en2 = WEBRTC_SPL_MUL_16_16(w16_en1, w16_en2); - w16_sqrt_en1en2 = (int16_t) WebRtcSpl_SqrtFloor(w32_en1_mul_en2); - - /* Calculate cc/sqrt(en1*en2) in Q14 */ - w16_ccShiftL = 14 - ((w16_en1Scale + w16_en2Scale) >> 1); - w32_cc = WEBRTC_SPL_SHIFT_W32(w32_cc, w16_ccShiftL); - w16_bestcorr = (int16_t) WebRtcSpl_DivW32W16(w32_cc, w16_sqrt_en1en2); - w16_bestcorr = WEBRTC_SPL_MIN(16384, w16_bestcorr); /* set maximum to 1.0 */ - - } - else - { - /* if either en1 or en2 is zero */ - w16_bestcorr = 0; - } - - /* - * Extract the two vectors, pw16_expVecs[0][] and pw16_expVecs[1][], - * from the SpeechHistory[] - */ - w16_expVecsLen = ExpandState->w16_maxLag + ExpandState->w16_overlap; - pw16_vec1 = &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - w16_expVecsLen]); - pw16_vec2 = pw16_vec1 - w16_DistLag; - /* Normalize the second vector to the same energy as the first */ - w32_en1 = WebRtcNetEQ_DotW16W16(pw16_vec1, pw16_vec1, w16_expVecsLen, w16_corrScale); - w32_en2 = WebRtcNetEQ_DotW16W16(pw16_vec2, pw16_vec2, w16_expVecsLen, w16_corrScale); - - /* - * Confirm that energy factor sqrt(w32_en1/w32_en2) is within difference 0.5 - 2.0 - * w32_en1/w32_en2 within 0.25 - 4 - */ - if (((w32_en1 >> 2) < w32_en2) && ((w32_en1) > (w32_en2 >> 2))) - { - - /* Energy constraint fulfilled => use both vectors and scale them accordingly */ - w16_en2Scale = 16 - WebRtcSpl_NormW32(w32_en2); - w16_en2Scale = WEBRTC_SPL_MAX(0, w16_en2Scale); - w16_en1Scale = w16_en2Scale - 13; - - /* calculate w32_en1/w32_en2 in Q13 */ - w32_en1_mul_en2 = WebRtcSpl_DivW32W16( - WEBRTC_SPL_SHIFT_W32(w32_en1, -w16_en1Scale), - (int16_t) (WEBRTC_SPL_RSHIFT_W32(w32_en2, w16_en2Scale))); - - /* calculate factor in Q13 (sqrt of en1/en2 in Q26) */ - w16_factor = (int16_t) WebRtcSpl_SqrtFloor( - WEBRTC_SPL_LSHIFT_W32(w32_en1_mul_en2, 13)); - - /* Copy the two vectors and give them the same energy */ - - WEBRTC_SPL_MEMCPY_W16(ExpandState->pw16_expVecs[0], pw16_vec1, w16_expVecsLen); - WebRtcSpl_AffineTransformVector(ExpandState->pw16_expVecs[1], pw16_vec2, - w16_factor, 4096, 13, w16_expVecsLen); - - } - else - { - /* Energy change constraint not fulfilled => only use last vector */ - - WEBRTC_SPL_MEMCPY_W16(ExpandState->pw16_expVecs[0], pw16_vec1, w16_expVecsLen); - WEBRTC_SPL_MEMCPY_W16(ExpandState->pw16_expVecs[1], ExpandState->pw16_expVecs[0], - w16_expVecsLen); - - /* Set the w16_factor since it is used by muting slope */ - if (((w32_en1 >> 2) < w32_en2) || (w32_en2 == 0)) - { - w16_factor = 4096; /* 0.5 in Q13*/ - } - else - { - w16_factor = 16384; /* 2.0 in Q13*/ - } - } - - /* Set the 3 lag values */ - w16_diffLag = w16_DistLag - w16_CorrLag; - if (w16_diffLag == 0) - { - /* DistLag and CorrLag are equal */ - ExpandState->w16_lags[0] = w16_DistLag; - ExpandState->w16_lags[1] = w16_DistLag; - ExpandState->w16_lags[2] = w16_DistLag; - } - else - { - /* DistLag and CorrLag are not equal; use different combinations of the two */ - ExpandState->w16_lags[0] = w16_DistLag; /* DistLag only */ - ExpandState->w16_lags[1] = ((w16_DistLag + w16_CorrLag) >> 1); /* 50/50 */ - /* Third lag, move one half-step towards CorrLag (in both cases) */ - if (w16_diffLag > 0) - { - ExpandState->w16_lags[2] = (w16_DistLag + w16_CorrLag - 1) >> 1; - } - else - { - ExpandState->w16_lags[2] = (w16_DistLag + w16_CorrLag + 1) >> 1; - } - } - - /************************************************* - * Calculate the LPC and the gain of the filters * - *************************************************/ - - /* Calculate scale value needed for autocorrelation */ - w16_tmp = WebRtcSpl_MaxAbsValueW16( - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - fsMultLPCAnalasysLen]), - fsMultLPCAnalasysLen); - - w16_tmp = 16 - WebRtcSpl_NormW32(w16_tmp); - w16_tmp = WEBRTC_SPL_MIN(w16_tmp,0); - w16_tmp = (w16_tmp << 1) + 7; - w16_tmp = WEBRTC_SPL_MAX(w16_tmp,0); - - /* set w16_ind to simplify the following expressions */ - w16_ind = inst->w16_speechHistoryLen - fsMultLPCAnalasysLen - UNVOICED_LPC_ORDER; - - /* store first UNVOICED_LPC_ORDER samples in pw16_rc */ - - WEBRTC_SPL_MEMCPY_W16(pw16_rc, &inst->pw16_speechHistory[w16_ind], UNVOICED_LPC_ORDER); - - /* set first samples to zero */ - WebRtcSpl_MemSetW16(&inst->pw16_speechHistory[w16_ind], 0, UNVOICED_LPC_ORDER); - - /* Calculate UNVOICED_LPC_ORDER+1 lags of the ACF */ - - WebRtcNetEQ_CrossCorr( - pw32_autoCorr, &(inst->pw16_speechHistory[w16_ind + UNVOICED_LPC_ORDER]), - &(inst->pw16_speechHistory[w16_ind + UNVOICED_LPC_ORDER]), fsMultLPCAnalasysLen, - UNVOICED_LPC_ORDER + 1, w16_tmp, -1); - - /* Recover the stored samples from pw16_rc */ - - WEBRTC_SPL_MEMCPY_W16(&inst->pw16_speechHistory[w16_ind], pw16_rc, UNVOICED_LPC_ORDER); - - if (pw32_autoCorr[0] > 0) - { /* check that variance is positive */ - - /* estimate AR filter parameters using Levinson-Durbin algorithm - (UNVOICED_LPC_ORDER+1 filter coefficients) */ - stability = WebRtcSpl_LevinsonDurbin(pw32_autoCorr, ExpandState->pw16_arFilter, - pw16_rc, UNVOICED_LPC_ORDER); - - /* Only update BGN if filter is stable */ - if (stability != 1) - { - /* Set first coefficient to 4096 (1.0 in Q12)*/ - ExpandState->pw16_arFilter[0] = 4096; - /* Set remaining UNVOICED_LPC_ORDER coefficients to zero */ - WebRtcSpl_MemSetW16(ExpandState->pw16_arFilter + 1, 0, UNVOICED_LPC_ORDER); - } - - } - - if (w16_DistLag < 40) - { - w16_energyLen = 2 * w16_DistLag; - } - else - { - w16_energyLen = w16_DistLag; - } - w16_randLen = w16_energyLen + 30; /* Startup part */ - - /* Extract a noise segment */ - if (w16_randLen <= RANDVEC_NO_OF_SAMPLES) - { - WEBRTC_SPL_MEMCPY_W16(pw16_randVec, - (int16_t*) WebRtcNetEQ_kRandnTbl, w16_randLen); - } - else - { /* only applies to SWB where length could be larger than 256 */ -#if FSMULT >= 2 /* Makes pw16_randVec longer than RANDVEC_NO_OF_SAMPLES. */ - WEBRTC_SPL_MEMCPY_W16(pw16_randVec, (int16_t*) WebRtcNetEQ_kRandnTbl, - RANDVEC_NO_OF_SAMPLES); - inst->w16_seedInc = (inst->w16_seedInc + 2) & (RANDVEC_NO_OF_SAMPLES - 1); - assert(w16_randLen <= FSMULT * 120 + 30); - WebRtcNetEQ_RandomVec(&inst->uw16_seed, &pw16_randVec[RANDVEC_NO_OF_SAMPLES], - (int16_t) (w16_randLen - RANDVEC_NO_OF_SAMPLES), inst->w16_seedInc); -#else - assert(0); -#endif - } - - /* Set up state vector and calculate scale factor for unvoiced filtering */ - - WEBRTC_SPL_MEMCPY_W16(ExpandState->pw16_arState, - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - UNVOICED_LPC_ORDER]), - UNVOICED_LPC_ORDER); - WEBRTC_SPL_MEMCPY_W16(pw16_unvoicedVec - UNVOICED_LPC_ORDER, - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - 128 - UNVOICED_LPC_ORDER]), - UNVOICED_LPC_ORDER); - WebRtcSpl_FilterMAFastQ12(&inst->pw16_speechHistory[inst->w16_speechHistoryLen - 128], - pw16_unvoicedVec, ExpandState->pw16_arFilter, UNVOICED_LPC_ORDER + 1, 128); - if (WebRtcSpl_MaxAbsValueW16(pw16_unvoicedVec, 128) > 4000) - { - w16_scale = 4; - } - else - { - w16_scale = 0; - } - w32_tmp = WebRtcNetEQ_DotW16W16(pw16_unvoicedVec, pw16_unvoicedVec, 128, w16_scale); - - /* Normalize w32_tmp to 28 or 29 bits to preserve sqrt() accuracy */ - w16_tmp = WebRtcSpl_NormW32(w32_tmp) - 3; - w16_tmp += ((w16_tmp & 0x1) ^ 0x1); /* Make sure we do an odd number of shifts since we - from earlier have 7 shifts from dividing with 128.*/ - w32_tmp = WEBRTC_SPL_SHIFT_W32(w32_tmp, w16_tmp); - w32_tmp = WebRtcSpl_SqrtFloor(w32_tmp); - ExpandState->w16_arGainScale = 13 + ((w16_tmp + 7 - w16_scale) >> 1); - ExpandState->w16_arGain = (int16_t) w32_tmp; - - /******************************************************************** - * Calculate vfraction from bestcorr * - * if (bestcorr>0.480665) * - * vfraction = ((bestcorr-0.4)/(1-0.4)).^2 * - * else vfraction = 0 * - * * - * approximation (coefficients in Q12): * - * if (x>0.480665) (y(x)<0.3) * - * y(x) = -1.264421 + 4.8659148*x - 4.0092827*x^2 + 1.4100529*x^3 * - * else y(x) = 0; * - ********************************************************************/ - - if (w16_bestcorr > 7875) - { - /* if x>0.480665 */ - int16_t w16_x1, w16_x2, w16_x3; - w16_x1 = w16_bestcorr; - w32_tmp = WEBRTC_SPL_MUL_16_16((int32_t) w16_x1, w16_x1); - w16_x2 = (int16_t) WEBRTC_SPL_RSHIFT_W32(w32_tmp, 14); - w32_tmp = WEBRTC_SPL_MUL_16_16(w16_x1, w16_x2); - w16_x3 = (int16_t) WEBRTC_SPL_RSHIFT_W32(w32_tmp, 14); - w32_tmp - = (int32_t) WEBRTC_SPL_LSHIFT_W32((int32_t) WebRtcNetEQ_kMixFractionFuncTbl[0], 14); - w32_tmp - += (int32_t) WEBRTC_SPL_MUL_16_16(WebRtcNetEQ_kMixFractionFuncTbl[1], w16_x1); - w32_tmp - += (int32_t) WEBRTC_SPL_MUL_16_16(WebRtcNetEQ_kMixFractionFuncTbl[2], w16_x2); - w32_tmp - += (int32_t) WEBRTC_SPL_MUL_16_16(WebRtcNetEQ_kMixFractionFuncTbl[3], w16_x3); - ExpandState->w16_vFraction = (int16_t) WEBRTC_SPL_RSHIFT_W32(w32_tmp, 12); - ExpandState->w16_vFraction = WEBRTC_SPL_MIN(ExpandState->w16_vFraction, 16384); - ExpandState->w16_vFraction = WEBRTC_SPL_MAX(ExpandState->w16_vFraction, 0); - } - else - { - ExpandState->w16_vFraction = 0; - } - - /*********************************************************************** - * Calculate muting slope, reuse value from earlier scaling of ExpVecs * - ***********************************************************************/ - w16_slope = w16_factor; - - if (w16_slope > 12288) - { - /* w16_slope > 1.5 ? */ - /* Calculate (1-(1/slope))/w16_DistLag = (slope-1)/(w16_DistLag*slope) */ - w32_tmp = w16_slope - 8192; - w32_tmp = WEBRTC_SPL_LSHIFT_W32(w32_tmp, 12); /* Value in Q25 (13+12=25) */ - w16_tmp = (int16_t) WEBRTC_SPL_MUL_16_16_RSFT(w16_DistLag, - w16_slope, 8); /* Value in Q5 (13-8=5) */ - w16_tmp = (int16_t) WebRtcSpl_DivW32W16(w32_tmp, - w16_tmp); /* Res in Q20 (25-5=20) */ - - if (w16_slope > 14746) - { /* w16_slope > 1.8 ? */ - ExpandState->w16_muteSlope = (w16_tmp + 1) >> 1; - } - else - { - ExpandState->w16_muteSlope = (w16_tmp + 4) >> 3; - } - ExpandState->w16_onset = 1; - } - else if (ExpandState->w16_vFraction > 13107) - { - /* w16_vFraction > 0.8 ? */ - if (w16_slope > 8028) - { - /* w16_vFraction > 0.98 ? */ - ExpandState->w16_muteSlope = 0; - } - else - { - /* Calculate (1-slope)/w16_DistLag */ - w32_tmp = 8192 - w16_slope; - w32_tmp = WEBRTC_SPL_LSHIFT_W32(w32_tmp, 7); /* Value in Q20 (13+7=20) */ - ExpandState->w16_muteSlope = (int16_t) WebRtcSpl_DivW32W16(w32_tmp, - w16_DistLag); /* Res in Q20 (20-0=20) */ - } - ExpandState->w16_onset = 0; - } - else - { - /* - * Use the minimum of 0.005 (0.9 on 50 samples in NB and the slope) - * and ((1-slope)/w16_DistLag) - */ - w32_tmp = 8192 - w16_slope; - w32_tmp = WEBRTC_SPL_LSHIFT_W32(w32_tmp, 7); /* Value in Q20 (13+7=20) */ - w32_tmp = WEBRTC_SPL_MAX(w32_tmp, 0); - ExpandState->w16_muteSlope = (int16_t) WebRtcSpl_DivW32W16(w32_tmp, - w16_DistLag); /* Res in Q20 (20-0=20) */ - w16_tmp = WebRtcNetEQ_k5243div[fs_mult]; /* 0.005/fs_mult = 5243/fs_mult */ - ExpandState->w16_muteSlope = WEBRTC_SPL_MAX(w16_tmp, ExpandState->w16_muteSlope); - ExpandState->w16_onset = 0; - } - } - else - { - /* This is not the first Expansion, parameters are already estimated. */ - - /* Extract a noise segment */ - if (BGNonly) /* If we should produce nothing but background noise */ - { - if (*pw16_len > 0) - { - /* - * Set length to input parameter length, but not more than length - * of pw16_randVec - */ - w16_lag = WEBRTC_SPL_MIN(*pw16_len, FSMULT * 120 + 30); - } - else - { - /* set length to 15 ms */ - w16_lag = fsMult120; - } - w16_randLen = w16_lag; - } - else - { - w16_randLen = ExpandState->w16_maxLag; - } - - if (w16_randLen <= RANDVEC_NO_OF_SAMPLES) - { - inst->w16_seedInc = (inst->w16_seedInc + 2) & (RANDVEC_NO_OF_SAMPLES - 1); - WebRtcNetEQ_RandomVec(&inst->uw16_seed, pw16_randVec, w16_randLen, - inst->w16_seedInc); - } - else - { /* only applies to SWB where length could be larger than 256 */ -#if FSMULT >= 2 /* Makes pw16_randVec longer than RANDVEC_NO_OF_SAMPLES. */ - inst->w16_seedInc = (inst->w16_seedInc + 2) & (RANDVEC_NO_OF_SAMPLES - 1); - WebRtcNetEQ_RandomVec(&inst->uw16_seed, pw16_randVec, RANDVEC_NO_OF_SAMPLES, - inst->w16_seedInc); - inst->w16_seedInc = (inst->w16_seedInc + 2) & (RANDVEC_NO_OF_SAMPLES - 1); - assert(w16_randLen <= FSMULT * 120 + 30); - WebRtcNetEQ_RandomVec(&inst->uw16_seed, &pw16_randVec[RANDVEC_NO_OF_SAMPLES], - (int16_t) (w16_randLen - RANDVEC_NO_OF_SAMPLES), inst->w16_seedInc); -#else - assert(0); -#endif - } - } /* end if(first expand or BGNonly) ... else ... */ - - if (!BGNonly) /* Voiced and unvoiced parts not used if generating BGN only */ - { - - /************************************************* - * Generate signal * - *************************************************/ - - /* - * Voiced part - */ - - /* Linearly mute the use_vfraction value from 1 to vfraction */ - if (ExpandState->w16_consecExp == 0) - { - ExpandState->w16_currentVFraction = 16384; /* 1.0 in Q14 */ - } - - ExpandState->w16_lagsPosition = ExpandState->w16_lagsPosition - + ExpandState->w16_lagsDirection; - - /* Change direction if needed */ - if (ExpandState->w16_lagsPosition == 0) - { - ExpandState->w16_lagsDirection = 1; - } - if (ExpandState->w16_lagsPosition == 2) - { - ExpandState->w16_lagsDirection = -1; - } - - /* Generate a weighted vector with the selected lag */ - w16_expVecsLen = ExpandState->w16_maxLag + ExpandState->w16_overlap; - w16_lag = ExpandState->w16_lags[ExpandState->w16_lagsPosition]; - /* Copy lag+overlap data */ - w16_expVecPos = w16_expVecsLen - w16_lag - ExpandState->w16_overlap; - w16_tmp = w16_lag + ExpandState->w16_overlap; - if (ExpandState->w16_lagsPosition == 0) - { - WEBRTC_SPL_MEMCPY_W16(pw16_voicedVecStorage, - &(ExpandState->pw16_expVecs[0][w16_expVecPos]), w16_tmp); - } - else if (ExpandState->w16_lagsPosition == 1) - { - WebRtcSpl_ScaleAndAddVectorsWithRound(&ExpandState->pw16_expVecs[0][w16_expVecPos], 3, - &ExpandState->pw16_expVecs[1][w16_expVecPos], 1, 2, pw16_voicedVecStorage, - w16_tmp); - - } - else if (ExpandState->w16_lagsPosition == 2) - { - WebRtcSpl_ScaleAndAddVectorsWithRound(&ExpandState->pw16_expVecs[0][w16_expVecPos], 1, - &ExpandState->pw16_expVecs[1][w16_expVecPos], 1, 1, pw16_voicedVecStorage, - w16_tmp); - } - - if (inst->fs == 8000) - { - /* Windowing in Q15 */ - w16_winMute = NETEQ_OVERLAP_WINMUTE_8KHZ_START; - w16_winMuteInc = NETEQ_OVERLAP_WINMUTE_8KHZ_INC; - w16_winUnMute = NETEQ_OVERLAP_WINUNMUTE_8KHZ_START; - w16_winUnMuteInc = NETEQ_OVERLAP_WINUNMUTE_8KHZ_INC; -#ifdef NETEQ_WIDEBAND - } - else if (inst->fs == 16000) - { - /* Windowing in Q15 */ - w16_winMute = NETEQ_OVERLAP_WINMUTE_16KHZ_START; - w16_winMuteInc = NETEQ_OVERLAP_WINMUTE_16KHZ_INC; - w16_winUnMute = NETEQ_OVERLAP_WINUNMUTE_16KHZ_START; - w16_winUnMuteInc = NETEQ_OVERLAP_WINUNMUTE_16KHZ_INC; -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - } - else if (inst->fs == 32000) - { - /* Windowing in Q15 */ - w16_winMute = NETEQ_OVERLAP_WINMUTE_32KHZ_START; - w16_winMuteInc = NETEQ_OVERLAP_WINMUTE_32KHZ_INC; - w16_winUnMute = NETEQ_OVERLAP_WINUNMUTE_32KHZ_START; - w16_winUnMuteInc = NETEQ_OVERLAP_WINUNMUTE_32KHZ_INC; -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - } - else /* if (inst->fs==48000) */ - { - /* Windowing in Q15 */ - w16_winMute = NETEQ_OVERLAP_WINMUTE_48KHZ_START; - w16_winMuteInc = NETEQ_OVERLAP_WINMUTE_48KHZ_INC; - w16_winUnMute = NETEQ_OVERLAP_WINUNMUTE_48KHZ_START; - w16_winUnMuteInc = NETEQ_OVERLAP_WINUNMUTE_48KHZ_INC; -#endif - } - - /* Smooth the expanded if it has not been muted to or vfraction is larger than 0.5 */ - if ((ExpandState->w16_expandMuteFactor > 819) && (ExpandState->w16_currentVFraction - > 8192)) - { - for (i = 0; i < ExpandState->w16_overlap; i++) - { - /* Do overlap add between new vector and overlap */ - ExpandState->pw16_overlapVec[i] = (int16_t) WEBRTC_SPL_RSHIFT_W32( - WEBRTC_SPL_MUL_16_16(ExpandState->pw16_overlapVec[i], w16_winMute) + - WEBRTC_SPL_MUL_16_16( - WEBRTC_SPL_MUL_16_16_RSFT(ExpandState->w16_expandMuteFactor, - pw16_voicedVecStorage[i], 14), w16_winUnMute) + 16384, 15); - w16_winMute += w16_winMuteInc; - w16_winUnMute += w16_winUnMuteInc; - } - } - else if (ExpandState->w16_expandMuteFactor == 0 -#ifdef NETEQ_STEREO - && msInfo->msMode == NETEQ_MONO /* only if mono mode is selected */ -#endif - ) - { - /* if ExpandState->w16_expandMuteFactor = 0 => all is CNG component - set the output length to 15ms (for best CNG production) */ - w16_tmp = fsMult120; - ExpandState->w16_maxLag = w16_tmp; - ExpandState->w16_lags[0] = w16_tmp; - ExpandState->w16_lags[1] = w16_tmp; - ExpandState->w16_lags[2] = w16_tmp; - } - - /* - * Unvoiced part - */ - - WEBRTC_SPL_MEMCPY_W16(pw16_unvoicedVec - UNVOICED_LPC_ORDER, - ExpandState->pw16_arState, - UNVOICED_LPC_ORDER); - if (ExpandState->w16_arGainScale > 0) - { - w32_tmp = ((int32_t) 1) << (ExpandState->w16_arGainScale - 1); - } - else - { - w32_tmp = 0; - } - - /* Note that shift value can be >16 which complicates things for some DSPs */ - WebRtcSpl_AffineTransformVector(pw16_scaledRandVec, pw16_randVec, - ExpandState->w16_arGain, w32_tmp, ExpandState->w16_arGainScale, w16_lag); - - WebRtcSpl_FilterARFastQ12(pw16_scaledRandVec, pw16_unvoicedVec, - ExpandState->pw16_arFilter, UNVOICED_LPC_ORDER + 1, w16_lag); - - WEBRTC_SPL_MEMCPY_W16(ExpandState->pw16_arState, - &(pw16_unvoicedVec[w16_lag - UNVOICED_LPC_ORDER]), - UNVOICED_LPC_ORDER); - - /* - * Voiced + Unvoiced - */ - - /* For lag = - <=31*fs_mult => go from 1 to 0 in about 8 ms - (>=31..<=63)*fs_mult => go from 1 to 0 in about 16 ms - >=64*fs_mult => go from 1 to 0 in about 32 ms - */ - w16_tmp = (31 - WebRtcSpl_NormW32(ExpandState->w16_maxLag)) - 5; /* getbits(w16_maxLag) -5 */ - w16_vfractionChange = (int16_t) WEBRTC_SPL_RSHIFT_W32(256, w16_tmp); - if (ExpandState->w16_stopMuting == 1) - { - w16_vfractionChange = 0; - } - - /* Create combined signal (unmuted) by shifting in more and more of unvoiced part */ - w16_tmp = 8 - w16_tmp; /* getbits(w16_vfractionChange) */ - w16_tmp = (ExpandState->w16_currentVFraction - ExpandState->w16_vFraction) >> w16_tmp; - w16_tmp = WEBRTC_SPL_MIN(w16_tmp, w16_lag); - WebRtcNetEQ_MixVoiceUnvoice(pw16_outData, pw16_voicedVec, pw16_unvoicedVec, - &ExpandState->w16_currentVFraction, w16_vfractionChange, w16_tmp); - - if (w16_tmp < w16_lag) - { - if (w16_vfractionChange != 0) - { - ExpandState->w16_currentVFraction = ExpandState->w16_vFraction; - } - w16_tmp2 = 16384 - ExpandState->w16_currentVFraction; - WebRtcSpl_ScaleAndAddVectorsWithRound(pw16_voicedVec + w16_tmp, - ExpandState->w16_currentVFraction, pw16_unvoicedVec + w16_tmp, w16_tmp2, 14, - pw16_outData + w16_tmp, (int16_t) (w16_lag - w16_tmp)); - } - - /* Select muting factor */ - if (ExpandState->w16_consecExp == 3) - { - /* 0.95 on 50 samples in NB (0.0010/fs_mult in Q20) */ - ExpandState->w16_muteSlope = WEBRTC_SPL_MAX(ExpandState->w16_muteSlope, - WebRtcNetEQ_k1049div[fs_mult]); - } - if (ExpandState->w16_consecExp == 7) - { - /* 0.90 on 50 samples in NB (0.0020/fs_mult in Q20) */ - ExpandState->w16_muteSlope = WEBRTC_SPL_MAX(ExpandState->w16_muteSlope, - WebRtcNetEQ_k2097div[fs_mult]); - } - - /* Mute segment according to slope value */ - if ((ExpandState->w16_consecExp != 0) || (ExpandState->w16_onset != 1)) - { - /* Mute to the previous level, then continue with the muting */ - WebRtcSpl_AffineTransformVector(pw16_outData, pw16_outData, - ExpandState->w16_expandMuteFactor, 8192, 14, w16_lag); - - if ((ExpandState->w16_stopMuting != 1)) - { - WebRtcNetEQ_MuteSignal(pw16_outData, ExpandState->w16_muteSlope, w16_lag); - - w16_tmp = 16384 - (int16_t) ((WEBRTC_SPL_MUL_16_16(w16_lag, - ExpandState->w16_muteSlope) + 8192) >> 6); /* 20-14 = 6 */ - w16_tmp = (int16_t) ((WEBRTC_SPL_MUL_16_16(w16_tmp, - ExpandState->w16_expandMuteFactor) + 8192) >> 14); - - /* Guard against getting stuck with very small (but sometimes audible) gain */ - if ((ExpandState->w16_consecExp > 3) && (w16_tmp - >= ExpandState->w16_expandMuteFactor)) - { - ExpandState->w16_expandMuteFactor = 0; - } - else - { - ExpandState->w16_expandMuteFactor = w16_tmp; - } - } - } - - } /* end if(!BGNonly) */ - - /* - * BGN - */ - - if (BGNState->w16_initialized == 1) - { - /* BGN parameters are initialized; use them */ - - WEBRTC_SPL_MEMCPY_W16(pw16_cngVec - BGN_LPC_ORDER, - BGNState->pw16_filterState, - BGN_LPC_ORDER); - - if (BGNState->w16_scaleShift > 1) - { - w32_tmp = ((int32_t) 1) << (BGNState->w16_scaleShift - 1); - } - else - { - w32_tmp = 0; - } - - /* Scale random vector to correct energy level */ - /* Note that shift value can be >16 which complicates things for some DSPs */ - WebRtcSpl_AffineTransformVector(pw16_scaledRandVec, pw16_randVec, - BGNState->w16_scale, w32_tmp, BGNState->w16_scaleShift, w16_lag); - - WebRtcSpl_FilterARFastQ12(pw16_scaledRandVec, pw16_cngVec, BGNState->pw16_filter, - BGN_LPC_ORDER + 1, w16_lag); - - WEBRTC_SPL_MEMCPY_W16(BGNState->pw16_filterState, - &(pw16_cngVec[w16_lag-BGN_LPC_ORDER]), - BGN_LPC_ORDER); - - /* Unmute the insertion of background noise */ - - if (bgnMode == BGN_FADE && ExpandState->w16_consecExp >= FADE_BGN_TIME - && BGNState->w16_mutefactor > 0) - { - /* fade BGN to zero */ - /* calculate muting slope, approx 2^18/fsHz */ - int16_t muteFactor; - if (fs_mult == 1) - { - muteFactor = -32; - } - else if (fs_mult == 2) - { - muteFactor = -16; - } - else if (fs_mult == 4) - { - muteFactor = -8; - } - else - { - muteFactor = -5; - } - /* use UnmuteSignal function with negative slope */ - WebRtcNetEQ_UnmuteSignal(pw16_cngVec, &BGNState->w16_mutefactor, /* In Q14 */ - pw16_cngVec, muteFactor, /* In Q20 */ - w16_lag); - } - else if (BGNState->w16_mutefactor < 16384 && !BGNonly) - { - /* if (w16_mutefactor < 1) and not BGN only (since then we use no muting) */ - - /* - * If BGN_OFF, or if BNG_FADE has started fading, - * mutefactor should not be increased. - */ - if (ExpandState->w16_stopMuting != 1 && bgnMode != BGN_OFF && !(bgnMode - == BGN_FADE && ExpandState->w16_consecExp >= FADE_BGN_TIME)) - { - WebRtcNetEQ_UnmuteSignal(pw16_cngVec, &BGNState->w16_mutefactor, /* In Q14 */ - pw16_cngVec, ExpandState->w16_muteSlope, /* In Q20 */ - w16_lag); - } - else - { - /* BGN_ON and stop muting, or - * BGN_OFF (mute factor is always 0), or - * BGN_FADE has reached 0 */ - WebRtcSpl_AffineTransformVector(pw16_cngVec, pw16_cngVec, - BGNState->w16_mutefactor, 8192, 14, w16_lag); - } - } - } - else - { - /* BGN parameters have not been initialized; use zero noise */ - WebRtcSpl_MemSetW16(pw16_cngVec, 0, w16_lag); - } - - if (BGNonly) - { - /* Copy BGN to outdata */ - for (i = 0; i < w16_lag; i++) - { - pw16_outData[i] = pw16_cngVec[i]; - } - } - else - { - /* Add CNG vector to the Voiced + Unvoiced vectors */ - for (i = 0; i < w16_lag; i++) - { - pw16_outData[i] = pw16_outData[i] + pw16_cngVec[i]; - } - - /* increase call number */ - ExpandState->w16_consecExp = ExpandState->w16_consecExp + 1; - if (ExpandState->w16_consecExp < 0) /* Guard against overflow */ - ExpandState->w16_consecExp = FADE_BGN_TIME; /* "Arbitrary" large num of expands */ - } - - inst->w16_mode = MODE_EXPAND; - *pw16_len = w16_lag; - - /* Update in-call and post-call statistics */ - if (ExpandState->w16_stopMuting != 1 || BGNonly) - { - /* - * Only do this if StopMuting != 1 or if explicitly BGNonly, otherwise Expand is - * called from Merge or Normal and special measures must be taken. - */ - inst->statInst.expandLength += (uint32_t) *pw16_len; - if (ExpandState->w16_expandMuteFactor == 0 || BGNonly) - { - /* Only noise expansion */ - inst->statInst.expandedNoiseSamples += *pw16_len; - /* Short-term activity statistics. */ - inst->activity_stats.expand_bgn_samples += *pw16_len; - } - else - { - /* Voice expand (note: not necessarily _voiced_) */ - inst->statInst.expandedVoiceSamples += *pw16_len; - /* Short-term activity statistics. */ - inst->activity_stats.expand_normal_samples += *pw16_len; - } - } - - return 0; -} - -/**************************************************************************** - * WebRtcNetEQ_GenerateBGN(...) - * - * This function generates and writes len samples of background noise to the - * output vector. The Expand function will be called repeatedly until the - * correct number of samples is produced. - * - * Input: - * - inst : NetEq instance, i.e. the user that requests more - * speech/audio data - * - scratchPtr : Pointer to scratch vector - * - len : Desired length of produced BGN. - * - * - * Output: - * - pw16_outData : Pointer to a memory space where the output data - * should be stored - * - * Return value : >=0 - Number of noise samples produced and written - * to output - * -1 - Error - */ - -int WebRtcNetEQ_GenerateBGN(DSPInst_t *inst, -#ifdef SCRATCH - int16_t *pw16_scratchPtr, -#endif - int16_t *pw16_outData, int16_t len) -{ - - int16_t pos = 0; - int16_t tempLen = len; - - while (tempLen > 0) - { - /* while we still need more noise samples, call Expand to obtain background noise */ - WebRtcNetEQ_Expand(inst, -#ifdef SCRATCH - pw16_scratchPtr, -#endif - &pw16_outData[pos], &tempLen, 1 /*BGNonly*/); - - pos += tempLen; /* we got this many samples */ - tempLen = len - pos; /* this is the number of samples we still need */ - } - - return pos; -} - -#undef SCRATCH_PW16_BEST_CORR_INDEX -#undef SCRATCH_PW16_BEST_CORR -#undef SCRATCH_PW16_BEST_DIST_INDEX -#undef SCRATCH_PW16_BEST_DIST -#undef SCRATCH_PW16_CORR_VEC -#undef SCRATCH_PW16_CORR2 -#undef SCRATCH_PW32_AUTO_CORR -#undef SCRATCH_PW16_RC -#undef SCRATCH_PW16_RAND_VEC -#undef SCRATCH_NETEQDSP_CORRELATOR -#undef SCRATCH_PW16_SCALED_RAND_VEC -#undef SCRATCH_PW16_UNVOICED_VEC_SPACE - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/expand.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/expand.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/expand.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/expand.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,905 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/expand.h" + +#include +#include // memset + +#include // min, max +#include // numeric_limits + +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/modules/audio_coding/neteq/background_noise.h" +#include "webrtc/modules/audio_coding/neteq/dsp_helper.h" +#include "webrtc/modules/audio_coding/neteq/random_vector.h" +#include "webrtc/modules/audio_coding/neteq/sync_buffer.h" + +namespace webrtc { + +void Expand::Reset() { + first_expand_ = true; + consecutive_expands_ = 0; + max_lag_ = 0; + for (size_t ix = 0; ix < num_channels_; ++ix) { + channel_parameters_[ix].expand_vector0.Clear(); + channel_parameters_[ix].expand_vector1.Clear(); + } +} + +int Expand::Process(AudioMultiVector* output) { + int16_t random_vector[kMaxSampleRate / 8000 * 120 + 30]; + int16_t scaled_random_vector[kMaxSampleRate / 8000 * 125]; + static const int kTempDataSize = 3600; + int16_t temp_data[kTempDataSize]; // TODO(hlundin) Remove this. + int16_t* voiced_vector_storage = temp_data; + int16_t* voiced_vector = &voiced_vector_storage[overlap_length_]; + static const int kNoiseLpcOrder = BackgroundNoise::kMaxLpcOrder; + int16_t unvoiced_array_memory[kNoiseLpcOrder + kMaxSampleRate / 8000 * 125]; + int16_t* unvoiced_vector = unvoiced_array_memory + kUnvoicedLpcOrder; + int16_t* noise_vector = unvoiced_array_memory + kNoiseLpcOrder; + + int fs_mult = fs_hz_ / 8000; + + if (first_expand_) { + // Perform initial setup if this is the first expansion since last reset. + AnalyzeSignal(random_vector); + first_expand_ = false; + } else { + // This is not the first expansion, parameters are already estimated. + // Extract a noise segment. + int16_t rand_length = max_lag_; + // This only applies to SWB where length could be larger than 256. + assert(rand_length <= kMaxSampleRate / 8000 * 120 + 30); + GenerateRandomVector(2, rand_length, random_vector); + } + + + // Generate signal. + UpdateLagIndex(); + + // Voiced part. + // Generate a weighted vector with the current lag. + size_t expansion_vector_length = max_lag_ + overlap_length_; + size_t current_lag = expand_lags_[current_lag_index_]; + // Copy lag+overlap data. + size_t expansion_vector_position = expansion_vector_length - current_lag - + overlap_length_; + size_t temp_length = current_lag + overlap_length_; + for (size_t channel_ix = 0; channel_ix < num_channels_; ++channel_ix) { + ChannelParameters& parameters = channel_parameters_[channel_ix]; + if (current_lag_index_ == 0) { + // Use only expand_vector0. + assert(expansion_vector_position + temp_length <= + parameters.expand_vector0.Size()); + memcpy(voiced_vector_storage, + ¶meters.expand_vector0[expansion_vector_position], + sizeof(int16_t) * temp_length); + } else if (current_lag_index_ == 1) { + // Mix 3/4 of expand_vector0 with 1/4 of expand_vector1. + WebRtcSpl_ScaleAndAddVectorsWithRound( + ¶meters.expand_vector0[expansion_vector_position], 3, + ¶meters.expand_vector1[expansion_vector_position], 1, 2, + voiced_vector_storage, static_cast(temp_length)); + } else if (current_lag_index_ == 2) { + // Mix 1/2 of expand_vector0 with 1/2 of expand_vector1. + assert(expansion_vector_position + temp_length <= + parameters.expand_vector0.Size()); + assert(expansion_vector_position + temp_length <= + parameters.expand_vector1.Size()); + WebRtcSpl_ScaleAndAddVectorsWithRound( + ¶meters.expand_vector0[expansion_vector_position], 1, + ¶meters.expand_vector1[expansion_vector_position], 1, 1, + voiced_vector_storage, static_cast(temp_length)); + } + + // Get tapering window parameters. Values are in Q15. + int16_t muting_window, muting_window_increment; + int16_t unmuting_window, unmuting_window_increment; + if (fs_hz_ == 8000) { + muting_window = DspHelper::kMuteFactorStart8kHz; + muting_window_increment = DspHelper::kMuteFactorIncrement8kHz; + unmuting_window = DspHelper::kUnmuteFactorStart8kHz; + unmuting_window_increment = DspHelper::kUnmuteFactorIncrement8kHz; + } else if (fs_hz_ == 16000) { + muting_window = DspHelper::kMuteFactorStart16kHz; + muting_window_increment = DspHelper::kMuteFactorIncrement16kHz; + unmuting_window = DspHelper::kUnmuteFactorStart16kHz; + unmuting_window_increment = DspHelper::kUnmuteFactorIncrement16kHz; + } else if (fs_hz_ == 32000) { + muting_window = DspHelper::kMuteFactorStart32kHz; + muting_window_increment = DspHelper::kMuteFactorIncrement32kHz; + unmuting_window = DspHelper::kUnmuteFactorStart32kHz; + unmuting_window_increment = DspHelper::kUnmuteFactorIncrement32kHz; + } else { // fs_ == 48000 + muting_window = DspHelper::kMuteFactorStart48kHz; + muting_window_increment = DspHelper::kMuteFactorIncrement48kHz; + unmuting_window = DspHelper::kUnmuteFactorStart48kHz; + unmuting_window_increment = DspHelper::kUnmuteFactorIncrement48kHz; + } + + // Smooth the expanded if it has not been muted to a low amplitude and + // |current_voice_mix_factor| is larger than 0.5. + if ((parameters.mute_factor > 819) && + (parameters.current_voice_mix_factor > 8192)) { + size_t start_ix = sync_buffer_->Size() - overlap_length_; + for (size_t i = 0; i < overlap_length_; i++) { + // Do overlap add between new vector and overlap. + (*sync_buffer_)[channel_ix][start_ix + i] = + (((*sync_buffer_)[channel_ix][start_ix + i] * muting_window) + + (((parameters.mute_factor * voiced_vector_storage[i]) >> 14) * + unmuting_window) + 16384) >> 15; + muting_window += muting_window_increment; + unmuting_window += unmuting_window_increment; + } + } else if (parameters.mute_factor == 0) { + // The expanded signal will consist of only comfort noise if + // mute_factor = 0. Set the output length to 15 ms for best noise + // production. + // TODO(hlundin): This has been disabled since the length of + // parameters.expand_vector0 and parameters.expand_vector1 no longer + // match with expand_lags_, causing invalid reads and writes. Is it a good + // idea to enable this again, and solve the vector size problem? +// max_lag_ = fs_mult * 120; +// expand_lags_[0] = fs_mult * 120; +// expand_lags_[1] = fs_mult * 120; +// expand_lags_[2] = fs_mult * 120; + } + + // Unvoiced part. + // Filter |scaled_random_vector| through |ar_filter_|. + memcpy(unvoiced_vector - kUnvoicedLpcOrder, parameters.ar_filter_state, + sizeof(int16_t) * kUnvoicedLpcOrder); + int32_t add_constant = 0; + if (parameters.ar_gain_scale > 0) { + add_constant = 1 << (parameters.ar_gain_scale - 1); + } + WebRtcSpl_AffineTransformVector(scaled_random_vector, random_vector, + parameters.ar_gain, add_constant, + parameters.ar_gain_scale, + static_cast(current_lag)); + WebRtcSpl_FilterARFastQ12(scaled_random_vector, unvoiced_vector, + parameters.ar_filter, kUnvoicedLpcOrder + 1, + static_cast(current_lag)); + memcpy(parameters.ar_filter_state, + &(unvoiced_vector[current_lag - kUnvoicedLpcOrder]), + sizeof(int16_t) * kUnvoicedLpcOrder); + + // Combine voiced and unvoiced contributions. + + // Set a suitable cross-fading slope. + // For lag = + // <= 31 * fs_mult => go from 1 to 0 in about 8 ms; + // (>= 31 .. <= 63) * fs_mult => go from 1 to 0 in about 16 ms; + // >= 64 * fs_mult => go from 1 to 0 in about 32 ms. + // temp_shift = getbits(max_lag_) - 5. + int temp_shift = (31 - WebRtcSpl_NormW32(max_lag_)) - 5; + int16_t mix_factor_increment = 256 >> temp_shift; + if (stop_muting_) { + mix_factor_increment = 0; + } + + // Create combined signal by shifting in more and more of unvoiced part. + temp_shift = 8 - temp_shift; // = getbits(mix_factor_increment). + size_t temp_lenght = (parameters.current_voice_mix_factor - + parameters.voice_mix_factor) >> temp_shift; + temp_lenght = std::min(temp_lenght, current_lag); + DspHelper::CrossFade(voiced_vector, unvoiced_vector, temp_lenght, + ¶meters.current_voice_mix_factor, + mix_factor_increment, temp_data); + + // End of cross-fading period was reached before end of expanded signal + // path. Mix the rest with a fixed mixing factor. + if (temp_lenght < current_lag) { + if (mix_factor_increment != 0) { + parameters.current_voice_mix_factor = parameters.voice_mix_factor; + } + int temp_scale = 16384 - parameters.current_voice_mix_factor; + WebRtcSpl_ScaleAndAddVectorsWithRound( + voiced_vector + temp_lenght, parameters.current_voice_mix_factor, + unvoiced_vector + temp_lenght, temp_scale, 14, + temp_data + temp_lenght, static_cast(current_lag - temp_lenght)); + } + + // Select muting slope depending on how many consecutive expands we have + // done. + if (consecutive_expands_ == 3) { + // Let the mute factor decrease from 1.0 to 0.95 in 6.25 ms. + // mute_slope = 0.0010 / fs_mult in Q20. + parameters.mute_slope = std::max(parameters.mute_slope, + static_cast(1049 / fs_mult)); + } + if (consecutive_expands_ == 7) { + // Let the mute factor decrease from 1.0 to 0.90 in 6.25 ms. + // mute_slope = 0.0020 / fs_mult in Q20. + parameters.mute_slope = std::max(parameters.mute_slope, + static_cast(2097 / fs_mult)); + } + + // Mute segment according to slope value. + if ((consecutive_expands_ != 0) || !parameters.onset) { + // Mute to the previous level, then continue with the muting. + WebRtcSpl_AffineTransformVector(temp_data, temp_data, + parameters.mute_factor, 8192, + 14, static_cast(current_lag)); + + if (!stop_muting_) { + DspHelper::MuteSignal(temp_data, parameters.mute_slope, current_lag); + + // Shift by 6 to go from Q20 to Q14. + // TODO(hlundin): Adding 8192 before shifting 6 steps seems wrong. + // Legacy. + int16_t gain = static_cast(16384 - + (((current_lag * parameters.mute_slope) + 8192) >> 6)); + gain = ((gain * parameters.mute_factor) + 8192) >> 14; + + // Guard against getting stuck with very small (but sometimes audible) + // gain. + if ((consecutive_expands_ > 3) && (gain >= parameters.mute_factor)) { + parameters.mute_factor = 0; + } else { + parameters.mute_factor = gain; + } + } + } + + // Background noise part. + GenerateBackgroundNoise(random_vector, + channel_ix, + channel_parameters_[channel_ix].mute_slope, + TooManyExpands(), + current_lag, + unvoiced_array_memory); + + // Add background noise to the combined voiced-unvoiced signal. + for (size_t i = 0; i < current_lag; i++) { + temp_data[i] = temp_data[i] + noise_vector[i]; + } + if (channel_ix == 0) { + output->AssertSize(current_lag); + } else { + assert(output->Size() == current_lag); + } + memcpy(&(*output)[channel_ix][0], temp_data, + sizeof(temp_data[0]) * current_lag); + } + + // Increase call number and cap it. + consecutive_expands_ = consecutive_expands_ >= kMaxConsecutiveExpands ? + kMaxConsecutiveExpands : consecutive_expands_ + 1; + return 0; +} + +void Expand::SetParametersForNormalAfterExpand() { + current_lag_index_ = 0; + lag_index_direction_ = 0; + stop_muting_ = true; // Do not mute signal any more. +} + +void Expand::SetParametersForMergeAfterExpand() { + current_lag_index_ = -1; /* out of the 3 possible ones */ + lag_index_direction_ = 1; /* make sure we get the "optimal" lag */ + stop_muting_ = true; +} + +void Expand::InitializeForAnExpandPeriod() { + lag_index_direction_ = 1; + current_lag_index_ = -1; + stop_muting_ = false; + random_vector_->set_seed_increment(1); + consecutive_expands_ = 0; + for (size_t ix = 0; ix < num_channels_; ++ix) { + channel_parameters_[ix].current_voice_mix_factor = 16384; // 1.0 in Q14. + channel_parameters_[ix].mute_factor = 16384; // 1.0 in Q14. + // Start with 0 gain for background noise. + background_noise_->SetMuteFactor(ix, 0); + } +} + +bool Expand::TooManyExpands() { + return consecutive_expands_ >= kMaxConsecutiveExpands; +} + +void Expand::AnalyzeSignal(int16_t* random_vector) { + int32_t auto_correlation[kUnvoicedLpcOrder + 1]; + int16_t reflection_coeff[kUnvoicedLpcOrder]; + int16_t correlation_vector[kMaxSampleRate / 8000 * 102]; + int best_correlation_index[kNumCorrelationCandidates]; + int16_t best_correlation[kNumCorrelationCandidates]; + int16_t best_distortion_index[kNumCorrelationCandidates]; + int16_t best_distortion[kNumCorrelationCandidates]; + int32_t correlation_vector2[(99 * kMaxSampleRate / 8000) + 1]; + int32_t best_distortion_w32[kNumCorrelationCandidates]; + static const int kNoiseLpcOrder = BackgroundNoise::kMaxLpcOrder; + int16_t unvoiced_array_memory[kNoiseLpcOrder + kMaxSampleRate / 8000 * 125]; + int16_t* unvoiced_vector = unvoiced_array_memory + kUnvoicedLpcOrder; + + int fs_mult = fs_hz_ / 8000; + + // Pre-calculate common multiplications with fs_mult. + int fs_mult_4 = fs_mult * 4; + int fs_mult_20 = fs_mult * 20; + int fs_mult_120 = fs_mult * 120; + int fs_mult_dist_len = fs_mult * kDistortionLength; + int fs_mult_lpc_analysis_len = fs_mult * kLpcAnalysisLength; + + const size_t signal_length = 256 * fs_mult; + const int16_t* audio_history = + &(*sync_buffer_)[0][sync_buffer_->Size() - signal_length]; + + // Initialize. + InitializeForAnExpandPeriod(); + + // Calculate correlation in downsampled domain (4 kHz sample rate). + int16_t correlation_scale; + int correlation_length = 51; // TODO(hlundin): Legacy bit-exactness. + // If it is decided to break bit-exactness |correlation_length| should be + // initialized to the return value of Correlation(). + Correlation(audio_history, signal_length, correlation_vector, + &correlation_scale); + + // Find peaks in correlation vector. + DspHelper::PeakDetection(correlation_vector, correlation_length, + kNumCorrelationCandidates, fs_mult, + best_correlation_index, best_correlation); + + // Adjust peak locations; cross-correlation lags start at 2.5 ms + // (20 * fs_mult samples). + best_correlation_index[0] += fs_mult_20; + best_correlation_index[1] += fs_mult_20; + best_correlation_index[2] += fs_mult_20; + + // Calculate distortion around the |kNumCorrelationCandidates| best lags. + int distortion_scale = 0; + for (int i = 0; i < kNumCorrelationCandidates; i++) { + int16_t min_index = std::max(fs_mult_20, + best_correlation_index[i] - fs_mult_4); + int16_t max_index = std::min(fs_mult_120 - 1, + best_correlation_index[i] + fs_mult_4); + best_distortion_index[i] = DspHelper::MinDistortion( + &(audio_history[signal_length - fs_mult_dist_len]), min_index, + max_index, fs_mult_dist_len, &best_distortion_w32[i]); + distortion_scale = std::max(16 - WebRtcSpl_NormW32(best_distortion_w32[i]), + distortion_scale); + } + // Shift the distortion values to fit in 16 bits. + WebRtcSpl_VectorBitShiftW32ToW16(best_distortion, kNumCorrelationCandidates, + best_distortion_w32, distortion_scale); + + // Find the maximizing index |i| of the cost function + // f[i] = best_correlation[i] / best_distortion[i]. + int32_t best_ratio = std::numeric_limits::min(); + int best_index = -1; + for (int i = 0; i < kNumCorrelationCandidates; ++i) { + int32_t ratio; + if (best_distortion[i] > 0) { + ratio = (best_correlation[i] << 16) / best_distortion[i]; + } else if (best_correlation[i] == 0) { + ratio = 0; // No correlation set result to zero. + } else { + ratio = std::numeric_limits::max(); // Denominator is zero. + } + if (ratio > best_ratio) { + best_index = i; + best_ratio = ratio; + } + } + + int distortion_lag = best_distortion_index[best_index]; + int correlation_lag = best_correlation_index[best_index]; + max_lag_ = std::max(distortion_lag, correlation_lag); + + // Calculate the exact best correlation in the range between + // |correlation_lag| and |distortion_lag|. + correlation_length = distortion_lag + 10; + correlation_length = std::min(correlation_length, fs_mult_120); + correlation_length = std::max(correlation_length, 60 * fs_mult); + + int start_index = std::min(distortion_lag, correlation_lag); + int correlation_lags = WEBRTC_SPL_ABS_W16((distortion_lag-correlation_lag)) + + 1; + assert(correlation_lags <= 99 * fs_mult + 1); // Cannot be larger. + + for (size_t channel_ix = 0; channel_ix < num_channels_; ++channel_ix) { + ChannelParameters& parameters = channel_parameters_[channel_ix]; + // Calculate suitable scaling. + int16_t signal_max = WebRtcSpl_MaxAbsValueW16( + &audio_history[signal_length - correlation_length - start_index + - correlation_lags], + correlation_length + start_index + correlation_lags - 1); + correlation_scale = ((31 - WebRtcSpl_NormW32(signal_max * signal_max)) + + (31 - WebRtcSpl_NormW32(correlation_length))) - 31; + correlation_scale = std::max(static_cast(0), correlation_scale); + + // Calculate the correlation, store in |correlation_vector2|. + WebRtcSpl_CrossCorrelation( + correlation_vector2, + &(audio_history[signal_length - correlation_length]), + &(audio_history[signal_length - correlation_length - start_index]), + correlation_length, correlation_lags, correlation_scale, -1); + + // Find maximizing index. + best_index = WebRtcSpl_MaxIndexW32(correlation_vector2, correlation_lags); + int32_t max_correlation = correlation_vector2[best_index]; + // Compensate index with start offset. + best_index = best_index + start_index; + + // Calculate energies. + int32_t energy1 = WebRtcSpl_DotProductWithScale( + &(audio_history[signal_length - correlation_length]), + &(audio_history[signal_length - correlation_length]), + correlation_length, correlation_scale); + int32_t energy2 = WebRtcSpl_DotProductWithScale( + &(audio_history[signal_length - correlation_length - best_index]), + &(audio_history[signal_length - correlation_length - best_index]), + correlation_length, correlation_scale); + + // Calculate the correlation coefficient between the two portions of the + // signal. + int16_t corr_coefficient; + if ((energy1 > 0) && (energy2 > 0)) { + int energy1_scale = std::max(16 - WebRtcSpl_NormW32(energy1), 0); + int energy2_scale = std::max(16 - WebRtcSpl_NormW32(energy2), 0); + // Make sure total scaling is even (to simplify scale factor after sqrt). + if ((energy1_scale + energy2_scale) & 1) { + // If sum is odd, add 1 to make it even. + energy1_scale += 1; + } + int16_t scaled_energy1 = energy1 >> energy1_scale; + int16_t scaled_energy2 = energy2 >> energy2_scale; + int16_t sqrt_energy_product = WebRtcSpl_SqrtFloor( + scaled_energy1 * scaled_energy2); + // Calculate max_correlation / sqrt(energy1 * energy2) in Q14. + int cc_shift = 14 - (energy1_scale + energy2_scale) / 2; + max_correlation = WEBRTC_SPL_SHIFT_W32(max_correlation, cc_shift); + corr_coefficient = WebRtcSpl_DivW32W16(max_correlation, + sqrt_energy_product); + corr_coefficient = std::min(static_cast(16384), + corr_coefficient); // Cap at 1.0 in Q14. + } else { + corr_coefficient = 0; + } + + // Extract the two vectors expand_vector0 and expand_vector1 from + // |audio_history|. + int16_t expansion_length = static_cast(max_lag_ + overlap_length_); + const int16_t* vector1 = &(audio_history[signal_length - expansion_length]); + const int16_t* vector2 = vector1 - distortion_lag; + // Normalize the second vector to the same energy as the first. + energy1 = WebRtcSpl_DotProductWithScale(vector1, vector1, expansion_length, + correlation_scale); + energy2 = WebRtcSpl_DotProductWithScale(vector2, vector2, expansion_length, + correlation_scale); + // Confirm that amplitude ratio sqrt(energy1 / energy2) is within 0.5 - 2.0, + // i.e., energy1 / energy1 is within 0.25 - 4. + int16_t amplitude_ratio; + if ((energy1 / 4 < energy2) && (energy1 > energy2 / 4)) { + // Energy constraint fulfilled. Use both vectors and scale them + // accordingly. + int16_t scaled_energy2 = std::max(16 - WebRtcSpl_NormW32(energy2), 0); + int16_t scaled_energy1 = scaled_energy2 - 13; + // Calculate scaled_energy1 / scaled_energy2 in Q13. + int32_t energy_ratio = WebRtcSpl_DivW32W16( + WEBRTC_SPL_SHIFT_W32(energy1, -scaled_energy1), + energy2 >> scaled_energy2); + // Calculate sqrt ratio in Q13 (sqrt of en1/en2 in Q26). + amplitude_ratio = WebRtcSpl_SqrtFloor(energy_ratio << 13); + // Copy the two vectors and give them the same energy. + parameters.expand_vector0.Clear(); + parameters.expand_vector0.PushBack(vector1, expansion_length); + parameters.expand_vector1.Clear(); + if (parameters.expand_vector1.Size() < + static_cast(expansion_length)) { + parameters.expand_vector1.Extend( + expansion_length - parameters.expand_vector1.Size()); + } + WebRtcSpl_AffineTransformVector(¶meters.expand_vector1[0], + const_cast(vector2), + amplitude_ratio, + 4096, + 13, + expansion_length); + } else { + // Energy change constraint not fulfilled. Only use last vector. + parameters.expand_vector0.Clear(); + parameters.expand_vector0.PushBack(vector1, expansion_length); + // Copy from expand_vector0 to expand_vector1. + parameters.expand_vector0.CopyTo(¶meters.expand_vector1); + // Set the energy_ratio since it is used by muting slope. + if ((energy1 / 4 < energy2) || (energy2 == 0)) { + amplitude_ratio = 4096; // 0.5 in Q13. + } else { + amplitude_ratio = 16384; // 2.0 in Q13. + } + } + + // Set the 3 lag values. + int lag_difference = distortion_lag - correlation_lag; + if (lag_difference == 0) { + // |distortion_lag| and |correlation_lag| are equal. + expand_lags_[0] = distortion_lag; + expand_lags_[1] = distortion_lag; + expand_lags_[2] = distortion_lag; + } else { + // |distortion_lag| and |correlation_lag| are not equal; use different + // combinations of the two. + // First lag is |distortion_lag| only. + expand_lags_[0] = distortion_lag; + // Second lag is the average of the two. + expand_lags_[1] = (distortion_lag + correlation_lag) / 2; + // Third lag is the average again, but rounding towards |correlation_lag|. + if (lag_difference > 0) { + expand_lags_[2] = (distortion_lag + correlation_lag - 1) / 2; + } else { + expand_lags_[2] = (distortion_lag + correlation_lag + 1) / 2; + } + } + + // Calculate the LPC and the gain of the filters. + // Calculate scale value needed for auto-correlation. + correlation_scale = WebRtcSpl_MaxAbsValueW16( + &(audio_history[signal_length - fs_mult_lpc_analysis_len]), + fs_mult_lpc_analysis_len); + + correlation_scale = std::min(16 - WebRtcSpl_NormW32(correlation_scale), 0); + correlation_scale = std::max(correlation_scale * 2 + 7, 0); + + // Calculate kUnvoicedLpcOrder + 1 lags of the auto-correlation function. + size_t temp_index = signal_length - fs_mult_lpc_analysis_len - + kUnvoicedLpcOrder; + // Copy signal to temporary vector to be able to pad with leading zeros. + int16_t* temp_signal = new int16_t[fs_mult_lpc_analysis_len + + kUnvoicedLpcOrder]; + memset(temp_signal, 0, + sizeof(int16_t) * (fs_mult_lpc_analysis_len + kUnvoicedLpcOrder)); + memcpy(&temp_signal[kUnvoicedLpcOrder], + &audio_history[temp_index + kUnvoicedLpcOrder], + sizeof(int16_t) * fs_mult_lpc_analysis_len); + WebRtcSpl_CrossCorrelation(auto_correlation, + &temp_signal[kUnvoicedLpcOrder], + &temp_signal[kUnvoicedLpcOrder], + fs_mult_lpc_analysis_len, kUnvoicedLpcOrder + 1, + correlation_scale, -1); + delete [] temp_signal; + + // Verify that variance is positive. + if (auto_correlation[0] > 0) { + // Estimate AR filter parameters using Levinson-Durbin algorithm; + // kUnvoicedLpcOrder + 1 filter coefficients. + int16_t stability = WebRtcSpl_LevinsonDurbin(auto_correlation, + parameters.ar_filter, + reflection_coeff, + kUnvoicedLpcOrder); + + // Keep filter parameters only if filter is stable. + if (stability != 1) { + // Set first coefficient to 4096 (1.0 in Q12). + parameters.ar_filter[0] = 4096; + // Set remaining |kUnvoicedLpcOrder| coefficients to zero. + WebRtcSpl_MemSetW16(parameters.ar_filter + 1, 0, kUnvoicedLpcOrder); + } + } + + if (channel_ix == 0) { + // Extract a noise segment. + int16_t noise_length; + if (distortion_lag < 40) { + noise_length = 2 * distortion_lag + 30; + } else { + noise_length = distortion_lag + 30; + } + if (noise_length <= RandomVector::kRandomTableSize) { + memcpy(random_vector, RandomVector::kRandomTable, + sizeof(int16_t) * noise_length); + } else { + // Only applies to SWB where length could be larger than + // |kRandomTableSize|. + memcpy(random_vector, RandomVector::kRandomTable, + sizeof(int16_t) * RandomVector::kRandomTableSize); + assert(noise_length <= kMaxSampleRate / 8000 * 120 + 30); + random_vector_->IncreaseSeedIncrement(2); + random_vector_->Generate( + noise_length - RandomVector::kRandomTableSize, + &random_vector[RandomVector::kRandomTableSize]); + } + } + + // Set up state vector and calculate scale factor for unvoiced filtering. + memcpy(parameters.ar_filter_state, + &(audio_history[signal_length - kUnvoicedLpcOrder]), + sizeof(int16_t) * kUnvoicedLpcOrder); + memcpy(unvoiced_vector - kUnvoicedLpcOrder, + &(audio_history[signal_length - 128 - kUnvoicedLpcOrder]), + sizeof(int16_t) * kUnvoicedLpcOrder); + WebRtcSpl_FilterMAFastQ12( + const_cast(&audio_history[signal_length - 128]), + unvoiced_vector, parameters.ar_filter, kUnvoicedLpcOrder + 1, 128); + int16_t unvoiced_prescale; + if (WebRtcSpl_MaxAbsValueW16(unvoiced_vector, 128) > 4000) { + unvoiced_prescale = 4; + } else { + unvoiced_prescale = 0; + } + int32_t unvoiced_energy = WebRtcSpl_DotProductWithScale(unvoiced_vector, + unvoiced_vector, + 128, + unvoiced_prescale); + + // Normalize |unvoiced_energy| to 28 or 29 bits to preserve sqrt() accuracy. + int16_t unvoiced_scale = WebRtcSpl_NormW32(unvoiced_energy) - 3; + // Make sure we do an odd number of shifts since we already have 7 shifts + // from dividing with 128 earlier. This will make the total scale factor + // even, which is suitable for the sqrt. + unvoiced_scale += ((unvoiced_scale & 0x1) ^ 0x1); + unvoiced_energy = WEBRTC_SPL_SHIFT_W32(unvoiced_energy, unvoiced_scale); + int32_t unvoiced_gain = WebRtcSpl_SqrtFloor(unvoiced_energy); + parameters.ar_gain_scale = 13 + + (unvoiced_scale + 7 - unvoiced_prescale) / 2; + parameters.ar_gain = unvoiced_gain; + + // Calculate voice_mix_factor from corr_coefficient. + // Let x = corr_coefficient. Then, we compute: + // if (x > 0.48) + // voice_mix_factor = (-5179 + 19931x - 16422x^2 + 5776x^3) / 4096; + // else + // voice_mix_factor = 0; + if (corr_coefficient > 7875) { + int16_t x1, x2, x3; + x1 = corr_coefficient; // |corr_coefficient| is in Q14. + x2 = (x1 * x1) >> 14; // Shift 14 to keep result in Q14. + x3 = (x1 * x2) >> 14; + static const int kCoefficients[4] = { -5179, 19931, -16422, 5776 }; + int32_t temp_sum = kCoefficients[0] << 14; + temp_sum += kCoefficients[1] * x1; + temp_sum += kCoefficients[2] * x2; + temp_sum += kCoefficients[3] * x3; + parameters.voice_mix_factor = temp_sum / 4096; + parameters.voice_mix_factor = std::min(parameters.voice_mix_factor, + static_cast(16384)); + parameters.voice_mix_factor = std::max(parameters.voice_mix_factor, + static_cast(0)); + } else { + parameters.voice_mix_factor = 0; + } + + // Calculate muting slope. Reuse value from earlier scaling of + // |expand_vector0| and |expand_vector1|. + int16_t slope = amplitude_ratio; + if (slope > 12288) { + // slope > 1.5. + // Calculate (1 - (1 / slope)) / distortion_lag = + // (slope - 1) / (distortion_lag * slope). + // |slope| is in Q13, so 1 corresponds to 8192. Shift up to Q25 before + // the division. + // Shift the denominator from Q13 to Q5 before the division. The result of + // the division will then be in Q20. + int16_t temp_ratio = WebRtcSpl_DivW32W16((slope - 8192) << 12, + (distortion_lag * slope) >> 8); + if (slope > 14746) { + // slope > 1.8. + // Divide by 2, with proper rounding. + parameters.mute_slope = (temp_ratio + 1) / 2; + } else { + // Divide by 8, with proper rounding. + parameters.mute_slope = (temp_ratio + 4) / 8; + } + parameters.onset = true; + } else { + // Calculate (1 - slope) / distortion_lag. + // Shift |slope| by 7 to Q20 before the division. The result is in Q20. + parameters.mute_slope = WebRtcSpl_DivW32W16((8192 - slope) << 7, + distortion_lag); + if (parameters.voice_mix_factor <= 13107) { + // Make sure the mute factor decreases from 1.0 to 0.9 in no more than + // 6.25 ms. + // mute_slope >= 0.005 / fs_mult in Q20. + parameters.mute_slope = std::max(static_cast(5243 / fs_mult), + parameters.mute_slope); + } else if (slope > 8028) { + parameters.mute_slope = 0; + } + parameters.onset = false; + } + } +} + +int16_t Expand::Correlation(const int16_t* input, size_t input_length, + int16_t* output, int16_t* output_scale) const { + // Set parameters depending on sample rate. + const int16_t* filter_coefficients; + int16_t num_coefficients; + int16_t downsampling_factor; + if (fs_hz_ == 8000) { + num_coefficients = 3; + downsampling_factor = 2; + filter_coefficients = DspHelper::kDownsample8kHzTbl; + } else if (fs_hz_ == 16000) { + num_coefficients = 5; + downsampling_factor = 4; + filter_coefficients = DspHelper::kDownsample16kHzTbl; + } else if (fs_hz_ == 32000) { + num_coefficients = 7; + downsampling_factor = 8; + filter_coefficients = DspHelper::kDownsample32kHzTbl; + } else { // fs_hz_ == 48000. + num_coefficients = 7; + downsampling_factor = 12; + filter_coefficients = DspHelper::kDownsample48kHzTbl; + } + + // Correlate from lag 10 to lag 60 in downsampled domain. + // (Corresponds to 20-120 for narrow-band, 40-240 for wide-band, and so on.) + static const int kCorrelationStartLag = 10; + static const int kNumCorrelationLags = 54; + static const int kCorrelationLength = 60; + // Downsample to 4 kHz sample rate. + static const int kDownsampledLength = kCorrelationStartLag + + kNumCorrelationLags + kCorrelationLength; + int16_t downsampled_input[kDownsampledLength]; + static const int kFilterDelay = 0; + WebRtcSpl_DownsampleFast( + input + input_length - kDownsampledLength * downsampling_factor, + kDownsampledLength * downsampling_factor, downsampled_input, + kDownsampledLength, filter_coefficients, num_coefficients, + downsampling_factor, kFilterDelay); + + // Normalize |downsampled_input| to using all 16 bits. + int16_t max_value = WebRtcSpl_MaxAbsValueW16(downsampled_input, + kDownsampledLength); + int16_t norm_shift = 16 - WebRtcSpl_NormW32(max_value); + WebRtcSpl_VectorBitShiftW16(downsampled_input, kDownsampledLength, + downsampled_input, norm_shift); + + int32_t correlation[kNumCorrelationLags]; + static const int kCorrelationShift = 6; + WebRtcSpl_CrossCorrelation( + correlation, + &downsampled_input[kDownsampledLength - kCorrelationLength], + &downsampled_input[kDownsampledLength - kCorrelationLength + - kCorrelationStartLag], + kCorrelationLength, kNumCorrelationLags, kCorrelationShift, -1); + + // Normalize and move data from 32-bit to 16-bit vector. + int32_t max_correlation = WebRtcSpl_MaxAbsValueW32(correlation, + kNumCorrelationLags); + int16_t norm_shift2 = std::max(18 - WebRtcSpl_NormW32(max_correlation), 0); + WebRtcSpl_VectorBitShiftW32ToW16(output, kNumCorrelationLags, correlation, + norm_shift2); + // Total scale factor (right shifts) of correlation value. + *output_scale = 2 * norm_shift + kCorrelationShift + norm_shift2; + return kNumCorrelationLags; +} + +void Expand::UpdateLagIndex() { + current_lag_index_ = current_lag_index_ + lag_index_direction_; + // Change direction if needed. + if (current_lag_index_ <= 0) { + lag_index_direction_ = 1; + } + if (current_lag_index_ >= kNumLags - 1) { + lag_index_direction_ = -1; + } +} + +Expand* ExpandFactory::Create(BackgroundNoise* background_noise, + SyncBuffer* sync_buffer, + RandomVector* random_vector, + int fs, + size_t num_channels) const { + return new Expand(background_noise, sync_buffer, random_vector, fs, + num_channels); +} + +// TODO(turajs): This can be moved to BackgroundNoise class. +void Expand::GenerateBackgroundNoise(int16_t* random_vector, + size_t channel, + int16_t mute_slope, + bool too_many_expands, + size_t num_noise_samples, + int16_t* buffer) { + static const int kNoiseLpcOrder = BackgroundNoise::kMaxLpcOrder; + int16_t scaled_random_vector[kMaxSampleRate / 8000 * 125]; + assert(static_cast(kMaxSampleRate / 8000 * 125) >= num_noise_samples); + int16_t* noise_samples = &buffer[kNoiseLpcOrder]; + if (background_noise_->initialized()) { + // Use background noise parameters. + memcpy(noise_samples - kNoiseLpcOrder, + background_noise_->FilterState(channel), + sizeof(int16_t) * kNoiseLpcOrder); + + int dc_offset = 0; + if (background_noise_->ScaleShift(channel) > 1) { + dc_offset = 1 << (background_noise_->ScaleShift(channel) - 1); + } + + // Scale random vector to correct energy level. + WebRtcSpl_AffineTransformVector( + scaled_random_vector, random_vector, + background_noise_->Scale(channel), dc_offset, + background_noise_->ScaleShift(channel), + static_cast(num_noise_samples)); + + WebRtcSpl_FilterARFastQ12(scaled_random_vector, noise_samples, + background_noise_->Filter(channel), + kNoiseLpcOrder + 1, + static_cast(num_noise_samples)); + + background_noise_->SetFilterState( + channel, + &(noise_samples[num_noise_samples - kNoiseLpcOrder]), + kNoiseLpcOrder); + + // Unmute the background noise. + int16_t bgn_mute_factor = background_noise_->MuteFactor(channel); + NetEq::BackgroundNoiseMode bgn_mode = background_noise_->mode(); + if (bgn_mode == NetEq::kBgnFade && too_many_expands && + bgn_mute_factor > 0) { + // Fade BGN to zero. + // Calculate muting slope, approximately -2^18 / fs_hz. + int16_t mute_slope; + if (fs_hz_ == 8000) { + mute_slope = -32; + } else if (fs_hz_ == 16000) { + mute_slope = -16; + } else if (fs_hz_ == 32000) { + mute_slope = -8; + } else { + mute_slope = -5; + } + // Use UnmuteSignal function with negative slope. + // |bgn_mute_factor| is in Q14. |mute_slope| is in Q20. + DspHelper::UnmuteSignal(noise_samples, + num_noise_samples, + &bgn_mute_factor, + mute_slope, + noise_samples); + } else if (bgn_mute_factor < 16384) { + // If mode is kBgnOn, or if kBgnFade has started fading, + // use regular |mute_slope|. + if (!stop_muting_ && bgn_mode != NetEq::kBgnOff && + !(bgn_mode == NetEq::kBgnFade && too_many_expands)) { + DspHelper::UnmuteSignal(noise_samples, + static_cast(num_noise_samples), + &bgn_mute_factor, + mute_slope, + noise_samples); + } else { + // kBgnOn and stop muting, or + // kBgnOff (mute factor is always 0), or + // kBgnFade has reached 0. + WebRtcSpl_AffineTransformVector(noise_samples, noise_samples, + bgn_mute_factor, 8192, 14, + static_cast(num_noise_samples)); + } + } + // Update mute_factor in BackgroundNoise class. + background_noise_->SetMuteFactor(channel, bgn_mute_factor); + } else { + // BGN parameters have not been initialized; use zero noise. + memset(noise_samples, 0, sizeof(int16_t) * num_noise_samples); + } +} + +void Expand::GenerateRandomVector(int seed_increment, + size_t length, + int16_t* random_vector) { + // TODO(turajs): According to hlundin The loop should not be needed. Should be + // just as good to generate all of the vector in one call. + size_t samples_generated = 0; + const size_t kMaxRandSamples = RandomVector::kRandomTableSize; + while (samples_generated < length) { + size_t rand_length = std::min(length - samples_generated, kMaxRandSamples); + random_vector_->IncreaseSeedIncrement(seed_increment); + random_vector_->Generate(rand_length, &random_vector[samples_generated]); + samples_generated += rand_length; + } +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/expand.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/expand.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/expand.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/expand.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_EXPAND_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_EXPAND_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Forward declarations. +class BackgroundNoise; +class RandomVector; +class SyncBuffer; + +// This class handles extrapolation of audio data from the sync_buffer to +// produce packet-loss concealment. +// TODO(hlundin): Refactor this class to divide the long methods into shorter +// ones. +class Expand { + public: + Expand(BackgroundNoise* background_noise, + SyncBuffer* sync_buffer, + RandomVector* random_vector, + int fs, + size_t num_channels) + : random_vector_(random_vector), + sync_buffer_(sync_buffer), + first_expand_(true), + fs_hz_(fs), + num_channels_(num_channels), + consecutive_expands_(0), + background_noise_(background_noise), + overlap_length_(5 * fs / 8000), + lag_index_direction_(0), + current_lag_index_(0), + stop_muting_(false), + channel_parameters_(new ChannelParameters[num_channels_]) { + assert(fs == 8000 || fs == 16000 || fs == 32000 || fs == 48000); + assert(fs <= kMaxSampleRate); // Should not be possible. + assert(num_channels_ > 0); + memset(expand_lags_, 0, sizeof(expand_lags_)); + Reset(); + } + + virtual ~Expand() {} + + // Resets the object. + virtual void Reset(); + + // The main method to produce concealment data. The data is appended to the + // end of |output|. + virtual int Process(AudioMultiVector* output); + + // Prepare the object to do extra expansion during normal operation following + // a period of expands. + virtual void SetParametersForNormalAfterExpand(); + + // Prepare the object to do extra expansion during merge operation following + // a period of expands. + virtual void SetParametersForMergeAfterExpand(); + + // Sets the mute factor for |channel| to |value|. + void SetMuteFactor(int16_t value, size_t channel) { + assert(channel < num_channels_); + channel_parameters_[channel].mute_factor = value; + } + + // Returns the mute factor for |channel|. + int16_t MuteFactor(size_t channel) { + assert(channel < num_channels_); + return channel_parameters_[channel].mute_factor; + } + + // Accessors and mutators. + virtual size_t overlap_length() const { return overlap_length_; } + int16_t max_lag() const { return max_lag_; } + + protected: + static const int kMaxConsecutiveExpands = 200; + void GenerateRandomVector(int seed_increment, + size_t length, + int16_t* random_vector); + + void GenerateBackgroundNoise(int16_t* random_vector, + size_t channel, + int16_t mute_slope, + bool too_many_expands, + size_t num_noise_samples, + int16_t* buffer); + + // Initializes member variables at the beginning of an expand period. + void InitializeForAnExpandPeriod(); + + bool TooManyExpands(); + + // Analyzes the signal history in |sync_buffer_|, and set up all parameters + // necessary to produce concealment data. + void AnalyzeSignal(int16_t* random_vector); + + RandomVector* random_vector_; + SyncBuffer* sync_buffer_; + bool first_expand_; + const int fs_hz_; + const size_t num_channels_; + int consecutive_expands_; + + private: + static const int kUnvoicedLpcOrder = 6; + static const int kNumCorrelationCandidates = 3; + static const int kDistortionLength = 20; + static const int kLpcAnalysisLength = 160; + static const int kMaxSampleRate = 48000; + static const int kNumLags = 3; + + struct ChannelParameters { + // Constructor. + ChannelParameters() + : mute_factor(16384), + ar_gain(0), + ar_gain_scale(0), + voice_mix_factor(0), + current_voice_mix_factor(0), + onset(false), + mute_slope(0) { + memset(ar_filter, 0, sizeof(ar_filter)); + memset(ar_filter_state, 0, sizeof(ar_filter_state)); + } + int16_t mute_factor; + int16_t ar_filter[kUnvoicedLpcOrder + 1]; + int16_t ar_filter_state[kUnvoicedLpcOrder]; + int16_t ar_gain; + int16_t ar_gain_scale; + int16_t voice_mix_factor; /* Q14 */ + int16_t current_voice_mix_factor; /* Q14 */ + AudioVector expand_vector0; + AudioVector expand_vector1; + bool onset; + int16_t mute_slope; /* Q20 */ + }; + + // Calculate the auto-correlation of |input|, with length |input_length| + // samples. The correlation is calculated from a downsampled version of + // |input|, and is written to |output|. The scale factor is written to + // |output_scale|. Returns the length of the correlation vector. + int16_t Correlation(const int16_t* input, size_t input_length, + int16_t* output, int16_t* output_scale) const; + + void UpdateLagIndex(); + + BackgroundNoise* background_noise_; + const size_t overlap_length_; + int16_t max_lag_; + size_t expand_lags_[kNumLags]; + int lag_index_direction_; + int current_lag_index_; + bool stop_muting_; + scoped_ptr channel_parameters_; + + DISALLOW_COPY_AND_ASSIGN(Expand); +}; + +struct ExpandFactory { + ExpandFactory() {} + virtual ~ExpandFactory() {} + + virtual Expand* Create(BackgroundNoise* background_noise, + SyncBuffer* sync_buffer, + RandomVector* random_vector, + int fs, + size_t num_channels) const; +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_EXPAND_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/expand_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/expand_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/expand_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/expand_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for Expand class. + +#include "webrtc/modules/audio_coding/neteq/expand.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/neteq/background_noise.h" +#include "webrtc/modules/audio_coding/neteq/random_vector.h" +#include "webrtc/modules/audio_coding/neteq/sync_buffer.h" + +namespace webrtc { + +TEST(Expand, CreateAndDestroy) { + int fs = 8000; + size_t channels = 1; + BackgroundNoise bgn(channels); + SyncBuffer sync_buffer(1, 1000); + RandomVector random_vector; + Expand expand(&bgn, &sync_buffer, &random_vector, fs, channels); +} + +TEST(Expand, CreateUsingFactory) { + int fs = 8000; + size_t channels = 1; + BackgroundNoise bgn(channels); + SyncBuffer sync_buffer(1, 1000); + RandomVector random_vector; + ExpandFactory expand_factory; + Expand* expand = + expand_factory.Create(&bgn, &sync_buffer, &random_vector, fs, channels); + EXPECT_TRUE(expand != NULL); + delete expand; +} + +// TODO(hlundin): Write more tests. + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/audio_decoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/audio_decoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/audio_decoder.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/audio_decoder.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_INTERFACE_AUDIO_DECODER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_INTERFACE_AUDIO_DECODER_H_ + +#include // NULL + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +enum NetEqDecoder { + kDecoderPCMu, + kDecoderPCMa, + kDecoderPCMu_2ch, + kDecoderPCMa_2ch, + kDecoderILBC, + kDecoderISAC, + kDecoderISACswb, + kDecoderISACfb, + kDecoderPCM16B, + kDecoderPCM16Bwb, + kDecoderPCM16Bswb32kHz, + kDecoderPCM16Bswb48kHz, + kDecoderPCM16B_2ch, + kDecoderPCM16Bwb_2ch, + kDecoderPCM16Bswb32kHz_2ch, + kDecoderPCM16Bswb48kHz_2ch, + kDecoderPCM16B_5ch, + kDecoderG722, + kDecoderG722_2ch, + kDecoderRED, + kDecoderAVT, + kDecoderCNGnb, + kDecoderCNGwb, + kDecoderCNGswb32kHz, + kDecoderCNGswb48kHz, + kDecoderArbitrary, + kDecoderOpus, + kDecoderOpus_2ch, + kDecoderCELT_32, + kDecoderCELT_32_2ch, +}; + +// This is the interface class for decoders in NetEQ. Each codec type will have +// and implementation of this class. +class AudioDecoder { + public: + enum SpeechType { + kSpeech = 1, + kComfortNoise = 2 + }; + + // Used by PacketDuration below. Save the value -1 for errors. + enum { kNotImplemented = -2 }; + + AudioDecoder() : channels_(1) {} + virtual ~AudioDecoder() {} + + // Decodes |encode_len| bytes from |encoded| and writes the result in + // |decoded|. The number of samples from all channels produced is in + // the return value. If the decoder produced comfort noise, |speech_type| + // is set to kComfortNoise, otherwise it is kSpeech. + virtual int Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type) = 0; + + // Same as Decode(), but interfaces to the decoders redundant decode function. + // The default implementation simply calls the regular Decode() method. + virtual int DecodeRedundant(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type); + + // Indicates if the decoder implements the DecodePlc method. + virtual bool HasDecodePlc() const; + + // Calls the packet-loss concealment of the decoder to update the state after + // one or several lost packets. + virtual int DecodePlc(int num_frames, int16_t* decoded); + + // Initializes the decoder. + virtual int Init() = 0; + + // Notifies the decoder of an incoming packet to NetEQ. + virtual int IncomingPacket(const uint8_t* payload, + size_t payload_len, + uint16_t rtp_sequence_number, + uint32_t rtp_timestamp, + uint32_t arrival_timestamp); + + // Returns the last error code from the decoder. + virtual int ErrorCode(); + + // Returns the duration in samples of the payload in |encoded| which is + // |encoded_len| bytes long. Returns kNotImplemented if no duration estimate + // is available, or -1 in case of an error. + virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); + + // Returns the duration in samples of the redandant payload in |encoded| which + // is |encoded_len| bytes long. Returns kNotImplemented if no duration + // estimate is available, or -1 in case of an error. + virtual int PacketDurationRedundant(const uint8_t* encoded, + size_t encoded_len) const; + + // Detects whether a packet has forward error correction. The packet is + // comprised of the samples in |encoded| which is |encoded_len| bytes long. + // Returns true if the packet has FEC and false otherwise. + virtual bool PacketHasFec(const uint8_t* encoded, size_t encoded_len) const; + + // If this is a CNG decoder, return the underlying CNG_dec_inst*. If this + // isn't a CNG decoder, don't call this method. + virtual CNG_dec_inst* CngDecoderInstance(); + + // Returns true if |codec_type| is supported. + static bool CodecSupported(NetEqDecoder codec_type); + + // Returns the sample rate for |codec_type|. + static int CodecSampleRateHz(NetEqDecoder codec_type); + + // Creates an AudioDecoder object of type |codec_type|. Returns NULL for + // for unsupported codecs, and when creating an AudioDecoder is not + // applicable (e.g., for RED and DTMF/AVT types). + static AudioDecoder* CreateAudioDecoder(NetEqDecoder codec_type); + + size_t channels() const { return channels_; } + + protected: + static SpeechType ConvertSpeechType(int16_t type); + + size_t channels_; + + private: + DISALLOW_COPY_AND_ASSIGN(AudioDecoder); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_INTERFACE_AUDIO_DECODER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/neteq.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/neteq.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/neteq.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/neteq.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_INTERFACE_NETEQ_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_INTERFACE_NETEQ_H_ + +#include // Provide access to size_t. + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/common_types.h" +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Forward declarations. +struct WebRtcRTPHeader; + +struct NetEqNetworkStatistics { + uint16_t current_buffer_size_ms; // Current jitter buffer size in ms. + uint16_t preferred_buffer_size_ms; // Target buffer size in ms. + uint16_t jitter_peaks_found; // 1 if adding extra delay due to peaky + // jitter; 0 otherwise. + uint16_t packet_loss_rate; // Loss rate (network + late) in Q14. + uint16_t packet_discard_rate; // Late loss rate in Q14. + uint16_t expand_rate; // Fraction (of original stream) of synthesized + // speech inserted through expansion (in Q14). + uint16_t preemptive_rate; // Fraction of data inserted through pre-emptive + // expansion (in Q14). + uint16_t accelerate_rate; // Fraction of data removed through acceleration + // (in Q14). + int32_t clockdrift_ppm; // Average clock-drift in parts-per-million + // (positive or negative). + int added_zero_samples; // Number of zero samples added in "off" mode. +}; + +enum NetEqOutputType { + kOutputNormal, + kOutputPLC, + kOutputCNG, + kOutputPLCtoCNG, + kOutputVADPassive +}; + +enum NetEqPlayoutMode { + kPlayoutOn, + kPlayoutOff, + kPlayoutFax, + kPlayoutStreaming +}; + +// This is the interface class for NetEq. +class NetEq { + public: + enum BackgroundNoiseMode { + kBgnOn, // Default behavior with eternal noise. + kBgnFade, // Noise fades to zero after some time. + kBgnOff // Background noise is always zero. + }; + + struct Config { + Config() + : sample_rate_hz(16000), + enable_audio_classifier(false), + max_packets_in_buffer(50), + // |max_delay_ms| has the same effect as calling SetMaximumDelay(). + max_delay_ms(2000), + background_noise_mode(kBgnOff), + playout_mode(kPlayoutOn) {} + + int sample_rate_hz; // Initial vale. Will change with input data. + bool enable_audio_classifier; + int max_packets_in_buffer; + int max_delay_ms; + BackgroundNoiseMode background_noise_mode; + NetEqPlayoutMode playout_mode; + }; + + enum ReturnCodes { + kOK = 0, + kFail = -1, + kNotImplemented = -2 + }; + + enum ErrorCodes { + kNoError = 0, + kOtherError, + kInvalidRtpPayloadType, + kUnknownRtpPayloadType, + kCodecNotSupported, + kDecoderExists, + kDecoderNotFound, + kInvalidSampleRate, + kInvalidPointer, + kAccelerateError, + kPreemptiveExpandError, + kComfortNoiseErrorCode, + kDecoderErrorCode, + kOtherDecoderError, + kInvalidOperation, + kDtmfParameterError, + kDtmfParsingError, + kDtmfInsertError, + kStereoNotSupported, + kSampleUnderrun, + kDecodedTooMuch, + kFrameSplitError, + kRedundancySplitError, + kPacketBufferCorruption, + kSyncPacketNotAccepted + }; + + // Creates a new NetEq object, with parameters set in |config|. The |config| + // object will only have to be valid for the duration of the call to this + // method. + static NetEq* Create(const NetEq::Config& config); + + virtual ~NetEq() {} + + // Inserts a new packet into NetEq. The |receive_timestamp| is an indication + // of the time when the packet was received, and should be measured with + // the same tick rate as the RTP timestamp of the current payload. + // Returns 0 on success, -1 on failure. + virtual int InsertPacket(const WebRtcRTPHeader& rtp_header, + const uint8_t* payload, + int length_bytes, + uint32_t receive_timestamp) = 0; + + // Inserts a sync-packet into packet queue. Sync-packets are decoded to + // silence and are intended to keep AV-sync intact in an event of long packet + // losses when Video NACK is enabled but Audio NACK is not. Clients of NetEq + // might insert sync-packet when they observe that buffer level of NetEq is + // decreasing below a certain threshold, defined by the application. + // Sync-packets should have the same payload type as the last audio payload + // type, i.e. they cannot have DTMF or CNG payload type, nor a codec change + // can be implied by inserting a sync-packet. + // Returns kOk on success, kFail on failure. + virtual int InsertSyncPacket(const WebRtcRTPHeader& rtp_header, + uint32_t receive_timestamp) = 0; + + // Instructs NetEq to deliver 10 ms of audio data. The data is written to + // |output_audio|, which can hold (at least) |max_length| elements. + // The number of channels that were written to the output is provided in + // the output variable |num_channels|, and each channel contains + // |samples_per_channel| elements. If more than one channel is written, + // the samples are interleaved. + // The speech type is written to |type|, if |type| is not NULL. + // Returns kOK on success, or kFail in case of an error. + virtual int GetAudio(size_t max_length, int16_t* output_audio, + int* samples_per_channel, int* num_channels, + NetEqOutputType* type) = 0; + + // Associates |rtp_payload_type| with |codec| and stores the information in + // the codec database. Returns 0 on success, -1 on failure. + virtual int RegisterPayloadType(enum NetEqDecoder codec, + uint8_t rtp_payload_type) = 0; + + // Provides an externally created decoder object |decoder| to insert in the + // decoder database. The decoder implements a decoder of type |codec| and + // associates it with |rtp_payload_type|. Returns kOK on success, + // kFail on failure. + virtual int RegisterExternalDecoder(AudioDecoder* decoder, + enum NetEqDecoder codec, + uint8_t rtp_payload_type) = 0; + + // Removes |rtp_payload_type| from the codec database. Returns 0 on success, + // -1 on failure. + virtual int RemovePayloadType(uint8_t rtp_payload_type) = 0; + + // Sets a minimum delay in millisecond for packet buffer. The minimum is + // maintained unless a higher latency is dictated by channel condition. + // Returns true if the minimum is successfully applied, otherwise false is + // returned. + virtual bool SetMinimumDelay(int delay_ms) = 0; + + // Sets a maximum delay in milliseconds for packet buffer. The latency will + // not exceed the given value, even required delay (given the channel + // conditions) is higher. Calling this method has the same effect as setting + // the |max_delay_ms| value in the NetEq::Config struct. + virtual bool SetMaximumDelay(int delay_ms) = 0; + + // The smallest latency required. This is computed bases on inter-arrival + // time and internal NetEq logic. Note that in computing this latency none of + // the user defined limits (applied by calling setMinimumDelay() and/or + // SetMaximumDelay()) are applied. + virtual int LeastRequiredDelayMs() const = 0; + + // Not implemented. + virtual int SetTargetDelay() = 0; + + // Not implemented. + virtual int TargetDelay() = 0; + + // Not implemented. + virtual int CurrentDelay() = 0; + + // Sets the playout mode to |mode|. + // Deprecated. Set the mode in the Config struct passed to the constructor. + // TODO(henrik.lundin) Delete. + virtual void SetPlayoutMode(NetEqPlayoutMode mode) = 0; + + // Returns the current playout mode. + // Deprecated. + // TODO(henrik.lundin) Delete. + virtual NetEqPlayoutMode PlayoutMode() const = 0; + + // Writes the current network statistics to |stats|. The statistics are reset + // after the call. + virtual int NetworkStatistics(NetEqNetworkStatistics* stats) = 0; + + // Writes the last packet waiting times (in ms) to |waiting_times|. The number + // of values written is no more than 100, but may be smaller if the interface + // is polled again before 100 packets has arrived. + virtual void WaitingTimes(std::vector* waiting_times) = 0; + + // Writes the current RTCP statistics to |stats|. The statistics are reset + // and a new report period is started with the call. + virtual void GetRtcpStatistics(RtcpStatistics* stats) = 0; + + // Same as RtcpStatistics(), but does not reset anything. + virtual void GetRtcpStatisticsNoReset(RtcpStatistics* stats) = 0; + + // Enables post-decode VAD. When enabled, GetAudio() will return + // kOutputVADPassive when the signal contains no speech. + virtual void EnableVad() = 0; + + // Disables post-decode VAD. + virtual void DisableVad() = 0; + + // Gets the RTP timestamp for the last sample delivered by GetAudio(). + // Returns true if the RTP timestamp is valid, otherwise false. + virtual bool GetPlayoutTimestamp(uint32_t* timestamp) = 0; + + // Not implemented. + virtual int SetTargetNumberOfChannels() = 0; + + // Not implemented. + virtual int SetTargetSampleRate() = 0; + + // Returns the error code for the last occurred error. If no error has + // occurred, 0 is returned. + virtual int LastError() const = 0; + + // Returns the error code last returned by a decoder (audio or comfort noise). + // When LastError() returns kDecoderErrorCode or kComfortNoiseErrorCode, check + // this method to get the decoder's error code. + virtual int LastDecoderError() = 0; + + // Flushes both the packet buffer and the sync buffer. + virtual void FlushBuffers() = 0; + + // Current usage of packet-buffer and it's limits. + virtual void PacketBufferStatistics(int* current_num_packets, + int* max_num_packets) const = 0; + + // Get sequence number and timestamp of the latest RTP. + // This method is to facilitate NACK. + virtual int DecodedRtpInfo(int* sequence_number, + uint32_t* timestamp) const = 0; + + protected: + NetEq() {} + + private: + DISALLOW_COPY_AND_ASSIGN(NetEq); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_INTERFACE_NETEQ_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,230 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This is the main API for NetEQ. Helper macros are located in webrtc_neteq_help_macros.h, - * while some internal API functions are found in webrtc_neteq_internal.h. - */ - -#include "typedefs.h" - -#ifndef WEBRTC_NETEQ_H -#define WEBRTC_NETEQ_H - -#ifdef __cplusplus -extern "C" -{ -#endif - -/********************************************************** - * Definitions - */ - -enum WebRtcNetEQDecoder -{ - kDecoderReservedStart, - kDecoderPCMu, - kDecoderPCMa, - kDecoderPCMu_2ch, - kDecoderPCMa_2ch, - kDecoderILBC, - kDecoderISAC, - kDecoderISACswb, - kDecoderISACfb, - kDecoderPCM16B, - kDecoderPCM16Bwb, - kDecoderPCM16Bswb32kHz, - kDecoderPCM16Bswb48kHz, - kDecoderPCM16B_2ch, - kDecoderPCM16Bwb_2ch, - kDecoderPCM16Bswb32kHz_2ch, - kDecoderG722, - kDecoderG722_2ch, - kDecoderRED, - kDecoderAVT, - kDecoderCNG, - kDecoderArbitrary, - kDecoderG729, - kDecoderG729_1, - kDecoderG726_16, - kDecoderG726_24, - kDecoderG726_32, - kDecoderG726_40, - kDecoderG722_1_16, - kDecoderG722_1_24, - kDecoderG722_1_32, - kDecoderG722_1C_24, - kDecoderG722_1C_32, - kDecoderG722_1C_48, - kDecoderOpus, - kDecoderSPEEX_8, - kDecoderSPEEX_16, - kDecoderCELT_32, - kDecoderCELT_32_2ch, - kDecoderGSMFR, - kDecoderAMR, - kDecoderAMRWB, - kDecoderReservedEnd -}; - -enum WebRtcNetEQNetworkType -{ - kUDPNormal, - kUDPVideoSync, - kTCPNormal, - kTCPLargeJitter, - kTCPXLargeJitter -}; - -enum WebRtcNetEQOutputType -{ - kOutputNormal, - kOutputPLC, - kOutputCNG, - kOutputPLCtoCNG, - kOutputVADPassive -}; - -enum WebRtcNetEQPlayoutMode -{ - kPlayoutOn, kPlayoutOff, kPlayoutFax, kPlayoutStreaming -}; - -/* Available modes for background noise (inserted after long expands) */ -enum WebRtcNetEQBGNMode -{ - kBGNOn, /* default "normal" behavior with eternal noise */ - kBGNFade, /* noise fades to zero after some time */ - kBGNOff -/* background noise is always zero */ -}; - -/************************************************* - * Definitions of decoder calls and the default - * API function calls for each codec - */ - -typedef int16_t (*WebRtcNetEQ_FuncDecode)(void* state, int16_t* encoded, - int16_t len, int16_t* decoded, - int16_t* speechType); -typedef int16_t (*WebRtcNetEQ_FuncDecodePLC)(void* state, int16_t* decoded, - int16_t frames); -typedef int16_t (*WebRtcNetEQ_FuncDecodeInit)(void* state); -typedef int16_t (*WebRtcNetEQ_FuncAddLatePkt)(void* state, int16_t* encoded, - int16_t len); -typedef int16_t (*WebRtcNetEQ_FuncGetMDinfo)(void* state); -typedef int16_t (*WebRtcNetEQ_FuncGetPitchInfo)(void* state, int16_t* encoded, - int16_t* length); -typedef int16_t (*WebRtcNetEQ_FuncUpdBWEst)(void* state, const uint16_t *encoded, - int32_t packet_size, - uint16_t rtp_seq_number, - uint32_t send_ts, - uint32_t arr_ts); -typedef int (*WebRtcNetEQ_FuncDurationEst)(void* state, const uint8_t* payload, - int payload_length_bytes); -typedef int16_t (*WebRtcNetEQ_FuncGetErrorCode)(void* state); - -/********************************************************** - * Structures - */ - -typedef struct -{ - enum WebRtcNetEQDecoder codec; - int16_t payloadType; - WebRtcNetEQ_FuncDecode funcDecode; - WebRtcNetEQ_FuncDecode funcDecodeRCU; - WebRtcNetEQ_FuncDecodePLC funcDecodePLC; - WebRtcNetEQ_FuncDecodeInit funcDecodeInit; - WebRtcNetEQ_FuncAddLatePkt funcAddLatePkt; - WebRtcNetEQ_FuncGetMDinfo funcGetMDinfo; - WebRtcNetEQ_FuncGetPitchInfo funcGetPitch; - WebRtcNetEQ_FuncUpdBWEst funcUpdBWEst; - WebRtcNetEQ_FuncDurationEst funcDurationEst; - WebRtcNetEQ_FuncGetErrorCode funcGetErrorCode; - void* codec_state; - uint16_t codec_fs; -} WebRtcNetEQ_CodecDef; - -typedef struct -{ - uint16_t fraction_lost; - uint32_t cum_lost; - uint32_t ext_max; - uint32_t jitter; -} WebRtcNetEQ_RTCPStat; - -/********************************************************** - * NETEQ Functions - */ - -/* Info functions */ - -#define WEBRTC_NETEQ_MAX_ERROR_NAME 40 -int WebRtcNetEQ_GetErrorCode(void *inst); -int WebRtcNetEQ_GetErrorName(int errorCode, char *errorName, int maxStrLen); - -/* Instance memory assign functions */ - -int WebRtcNetEQ_AssignSize(int *sizeinbytes); -int WebRtcNetEQ_Assign(void **inst, void *NETEQ_inst_Addr); -int WebRtcNetEQ_GetRecommendedBufferSize(void *inst, const enum WebRtcNetEQDecoder *codec, - int noOfCodecs, enum WebRtcNetEQNetworkType nwType, - int *MaxNoOfPackets, int *sizeinbytes, - int* per_packet_overhead_bytes); -int WebRtcNetEQ_AssignBuffer(void *inst, int MaxNoOfPackets, void *NETEQ_Buffer_Addr, - int sizeinbytes); - -/* Init functions */ - -int WebRtcNetEQ_Init(void *inst, uint16_t fs); -int WebRtcNetEQ_SetAVTPlayout(void *inst, int PlayoutAVTon); -int WebRtcNetEQ_SetExtraDelay(void *inst, int DelayInMs); -int WebRtcNetEQ_SetPlayoutMode(void *inst, enum WebRtcNetEQPlayoutMode playoutMode); -int WebRtcNetEQ_SetBGNMode(void *inst, enum WebRtcNetEQBGNMode bgnMode); -int WebRtcNetEQ_GetBGNMode(const void *inst, enum WebRtcNetEQBGNMode *bgnMode); - -/* Codec Database functions */ - -int WebRtcNetEQ_CodecDbReset(void *inst); -int WebRtcNetEQ_CodecDbAdd(void *inst, WebRtcNetEQ_CodecDef *codecInst); -int WebRtcNetEQ_CodecDbRemove(void *inst, enum WebRtcNetEQDecoder codec); -int WebRtcNetEQ_CodecDbGetSizeInfo(void *inst, int16_t *UsedEntries, - int16_t *MaxEntries); -int WebRtcNetEQ_CodecDbGetCodecInfo(void *inst, int16_t Entry, - enum WebRtcNetEQDecoder *codec); - -/* Real-time functions */ - -int WebRtcNetEQ_RecIn(void *inst, int16_t *p_w16datagramstart, int16_t w16_RTPlen, - uint32_t uw32_timeRec); -int WebRtcNetEQ_RecOut(void *inst, int16_t *pw16_outData, int16_t *pw16_len); -int WebRtcNetEQ_GetRTCPStats(void *inst, WebRtcNetEQ_RTCPStat *RTCP_inst); -int WebRtcNetEQ_GetRTCPStatsNoReset(void *inst, WebRtcNetEQ_RTCPStat *RTCP_inst); -int WebRtcNetEQ_GetSpeechTimeStamp(void *inst, uint32_t *timestamp); -int WebRtcNetEQ_DecodedRtpInfo(const void* inst, - int* sequence_number, - uint32_t* timestamp); -int WebRtcNetEQ_GetSpeechOutputType(void *inst, enum WebRtcNetEQOutputType *outputType); - -/* VQmon related functions */ -int WebRtcNetEQ_VQmonRecOutStatistics(void *inst, uint16_t *validVoiceDurationMs, - uint16_t *concealedVoiceDurationMs, - uint8_t *concealedVoiceFlags); -int WebRtcNetEQ_VQmonGetConfiguration(void *inst, uint16_t *absMaxDelayMs, - uint8_t *adaptationRate); -int WebRtcNetEQ_VQmonGetRxStatistics(void *inst, uint16_t *avgDelayMs, - uint16_t *maxDelayMs); - -#ifdef __cplusplus -} -#endif - -#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,454 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains some helper macros that can be used when loading the - * NetEQ codec database. - */ - -#ifndef WEBRTC_NETEQ_HELP_MACROS_H -#define WEBRTC_NETEQ_HELP_MACROS_H - -#ifndef NULL -#define NULL 0 -#endif - -/********************************************************** - * Help macros for NetEQ initialization - */ - -#define SET_CODEC_PAR(inst,decoder,pt,state,fs) \ - inst.codec=decoder; \ - inst.payloadType=pt; \ - inst.codec_state=state; \ - inst.codec_fs=fs; - -#define SET_PCMU_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG711_DecodeU; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=WebRtcG711_DurationEst; \ - inst.funcGetErrorCode=NULL; - -#define SET_PCMA_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG711_DecodeA; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=WebRtcG711_DurationEst; \ - inst.funcGetErrorCode=NULL; - -#define SET_ILBC_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcIlbcfix_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcIlbcfix_NetEqPlc; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcIlbcfix_Decoderinit30Ms; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_ISAC_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcIsac_Decode; \ - inst.funcDecodeRCU=(WebRtcNetEQ_FuncDecode)WebRtcIsac_DecodeRcu; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcIsac_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=(WebRtcNetEQ_FuncUpdBWEst)WebRtcIsac_UpdateBwEstimate; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=(WebRtcNetEQ_FuncGetErrorCode)WebRtcIsac_GetErrorCode; - -#define SET_ISACfix_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcIsacfix_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcIsacfix_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=(WebRtcNetEQ_FuncUpdBWEst)WebRtcIsacfix_UpdateBwEstimate; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=(WebRtcNetEQ_FuncGetErrorCode)WebRtcIsacfix_GetErrorCode; - -#define SET_ISACSWB_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcIsac_Decode; \ - inst.funcDecodeRCU=(WebRtcNetEQ_FuncDecode)WebRtcIsac_DecodeRcu; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcIsac_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=(WebRtcNetEQ_FuncUpdBWEst)WebRtcIsac_UpdateBwEstimate; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=(WebRtcNetEQ_FuncGetErrorCode)WebRtcIsac_GetErrorCode; - -#define SET_ISACFB_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcIsac_Decode; \ - inst.funcDecodeRCU=(WebRtcNetEQ_FuncDecode)WebRtcIsac_DecodeRcu; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcIsac_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=(WebRtcNetEQ_FuncUpdBWEst)WebRtcIsac_UpdateBwEstimate; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=(WebRtcNetEQ_FuncGetErrorCode)WebRtcIsac_GetErrorCode; - -#define SET_G729_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG729_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcG729_DecodePlc; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG729_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G729_1_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG7291_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG7291_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=(WebRtcNetEQ_FuncUpdBWEst)WebRtcG7291_DecodeBwe; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_PCM16B_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcPcm16b_DecodeW16; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_PCM16B_WB_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcPcm16b_DecodeW16; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_PCM16B_SWB32_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcPcm16b_DecodeW16; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_PCM16B_SWB48_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcPcm16b_DecodeW16; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G722_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG722_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG722_DecoderInit;\ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G722_1_16_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG7221_Decode16; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcG7221_DecodePlc16; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG7221_DecoderInit16; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G722_1_24_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG7221_Decode24; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcG7221_DecodePlc24; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG7221_DecoderInit24; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G722_1_32_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG7221_Decode32; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcG7221_DecodePlc32; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG7221_DecoderInit32; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G722_1C_24_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG7221C_Decode24; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcG7221C_DecodePlc24; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG7221C_DecoderInit24; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G722_1C_32_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG7221C_Decode32; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcG7221C_DecodePlc32; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG7221C_DecoderInit32; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G722_1C_48_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG7221C_Decode48; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcG7221C_DecodePlc48; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG7221C_DecoderInit48; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_AMR_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcAmr_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcAmr_DecodePlc; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcAmr_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_AMRWB_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcAmrWb_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcAmrWb_DecodePlc; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcAmrWb_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_GSMFR_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcGSMFR_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcGSMFR_DecodePlc; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcGSMFR_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G726_16_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG726_decode16; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG726_decoderinit16; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G726_24_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG726_decode24; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG726_decoderinit24; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G726_32_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG726_decode32; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG726_decoderinit32; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G726_40_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG726_decode40; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG726_decoderinit40; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_OPUS_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcOpus_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcOpus_DecodePlcMaster; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcOpus_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=(WebRtcNetEQ_FuncDurationEst)WebRtcOpus_DurationEst; \ - inst.funcGetErrorCode=NULL; - -#define SET_OPUSSLAVE_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcOpus_DecodeSlave; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcOpus_DecodePlcSlave; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcOpus_DecoderInitSlave; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=(WebRtcNetEQ_FuncDurationEst)WebRtcOpus_DurationEst; \ - inst.funcGetErrorCode=NULL; - -#define SET_SPEEX_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcSpeex_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcSpeex_DecodePlc; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcSpeex_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_CELT_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcCelt_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcCelt_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_CELTSLAVE_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcCelt_DecodeSlave; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcCelt_DecoderInitSlave; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_RED_FUNCTIONS(inst) \ - inst.funcDecode=NULL; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_AVT_FUNCTIONS(inst) \ - inst.funcDecode=NULL; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_CNG_FUNCTIONS(inst) \ - inst.funcDecode=NULL; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcDurationEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#endif /* WEBRTC_NETEQ_HELP_MACROS_H */ - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_internal.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_internal.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_internal.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_internal.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,336 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the internal API functions. - */ - -#include "typedefs.h" - -#ifndef WEBRTC_NETEQ_INTERNAL_H -#define WEBRTC_NETEQ_INTERNAL_H - -#ifdef __cplusplus -extern "C" -{ -#endif - -typedef struct -{ - uint8_t payloadType; - uint16_t sequenceNumber; - uint32_t timeStamp; - uint32_t SSRC; - uint8_t markerBit; -} WebRtcNetEQ_RTPInfo; - -/**************************************************************************** - * WebRtcNetEQ_RecInRTPStruct(...) - * - * Alternative RecIn function, used when the RTP data has already been - * parsed into an RTP info struct (WebRtcNetEQ_RTPInfo). - * - * Input: - * - inst : NetEQ instance - * - rtpInfo : Pointer to RTP info - * - payloadPtr : Pointer to the RTP payload (first byte after header) - * - payloadLenBytes : Length (in bytes) of the payload in payloadPtr - * - timeRec : Receive time (in timestamps of the used codec) - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNetEQ_RecInRTPStruct(void *inst, WebRtcNetEQ_RTPInfo *rtpInfo, - const uint8_t *payloadPtr, int16_t payloadLenBytes, - uint32_t timeRec); - -/**************************************************************************** - * WebRtcNetEQ_GetMasterSlaveInfoSize(...) - * - * Get size in bytes for master/slave struct msInfo used in - * WebRtcNetEQ_RecOutMasterSlave. - * - * Return value : Struct size in bytes - * - */ - -int WebRtcNetEQ_GetMasterSlaveInfoSize(); - -/**************************************************************************** - * WebRtcNetEQ_RecOutMasterSlave(...) - * - * RecOut function for running several NetEQ instances in master/slave mode. - * One master can be used to control several slaves. - * The MasterSlaveInfo struct must be allocated outside NetEQ. - * Use function WebRtcNetEQ_GetMasterSlaveInfoSize to get the size needed. - * - * Input: - * - inst : NetEQ instance - * - isMaster : Non-zero indicates that this is the master channel - * - msInfo : (slave only) Information from master - * - * Output: - * - inst : Updated NetEQ instance - * - pw16_outData : Pointer to vector where output should be written - * - pw16_len : Pointer to variable where output length is returned - * - msInfo : (master only) Information to slave(s) - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RecOutMasterSlave(void *inst, int16_t *pw16_outData, - int16_t *pw16_len, void *msInfo, - int16_t isMaster); - -typedef struct -{ - uint16_t currentBufferSize; /* Current jitter buffer size in ms. */ - uint16_t preferredBufferSize; /* Preferred buffer size in ms. */ - uint16_t jitterPeaksFound; /* 1 if adding extra delay due to peaky - * jitter; 0 otherwise. */ - uint16_t currentPacketLossRate; /* Loss rate (network + late) (Q14). */ - uint16_t currentDiscardRate; /* Late loss rate (Q14). */ - uint16_t currentExpandRate; /* Fraction (of original stream) of - * synthesized speech inserted through - * expansion (in Q14). */ - uint16_t currentPreemptiveRate; /* Fraction of data inserted through - * pre-emptive expansion (in Q14). */ - uint16_t currentAccelerateRate; /* Fraction of data removed through - * acceleration (in Q14). */ - int32_t clockDriftPPM; /* Average clock-drift in parts-per- - * million (positive or negative). */ - int addedSamples; /* Number of zero samples added in off - * mode */ -} WebRtcNetEQ_NetworkStatistics; - -/* - * Get the "in-call" statistics from NetEQ. - * The statistics are reset after the query. - */ -int WebRtcNetEQ_GetNetworkStatistics(void *inst, WebRtcNetEQ_NetworkStatistics *stats); - - -typedef struct { - /* Samples removed from background noise only segments. */ - int accelerate_bgn_samples; - - /* Samples removed from normal audio segments. */ - int accelerate_normal_samples; - - /* Number of samples synthesized during background noise only segments. */ - int expand_bgn_sampels; - - /* Number of samples synthesized during normal audio segments. */ - int expand_normal_samples; - - /* Number of samples synthesized during background noise only segments, - * in preemptive mode. */ - int preemptive_expand_bgn_samples; - - /* Number of samples synthesized during normal audio segments, in preemptive - * mode. */ - int preemptive_expand_normal_samples; - - /* Number of samples synthesized during background noise only segments, - * while merging. */ - int merge_expand_bgn_samples; - - /* Number of samples synthesized during normal audio segments, while - * merging. */ - int merge_expand_normal_samples; -} WebRtcNetEQ_ProcessingActivity; - -/* - * Get the processing activities from NetEQ. - * The statistics are reset after the query. - * This API is meant to obtain processing activities in high granularity, - * e.g. per RecOut() call. - */ -void WebRtcNetEQ_GetProcessingActivity(void* inst, - WebRtcNetEQ_ProcessingActivity* stat); - -/* - * Get the raw waiting times for decoded frames. The function writes the last - * recorded waiting times (from frame arrival to frame decoding) to the memory - * pointed to by waitingTimeMs. The number of elements written is in the return - * value. No more than maxLength elements are written. Statistics are reset on - * each query. - */ -int WebRtcNetEQ_GetRawFrameWaitingTimes(void *inst, - int max_length, - int* waiting_times_ms); - -/***********************************************/ -/* Functions for post-decode VAD functionality */ -/***********************************************/ - -/* NetEQ must be compiled with the flag NETEQ_VAD enabled for these functions to work. */ - -/* - * VAD function pointer types - * - * These function pointers match the definitions of webrtc VAD functions WebRtcVad_Init, - * WebRtcVad_set_mode and WebRtcVad_Process, respectively, all found in webrtc_vad.h. - */ -typedef int (*WebRtcNetEQ_VADInitFunction)(void *VAD_inst); -typedef int (*WebRtcNetEQ_VADSetmodeFunction)(void *VAD_inst, int mode); -typedef int (*WebRtcNetEQ_VADFunction)(void *VAD_inst, int fs, - int16_t *frame, int frameLen); - -/**************************************************************************** - * WebRtcNetEQ_SetVADInstance(...) - * - * Provide a pointer to an allocated VAD instance. If function is never - * called or it is called with NULL pointer as VAD_inst, the post-decode - * VAD functionality is disabled. Also provide pointers to init, setmode - * and VAD functions. These are typically pointers to WebRtcVad_Init, - * WebRtcVad_set_mode and WebRtcVad_Process, respectively, all found in the - * interface file webrtc_vad.h. - * - * Input: - * - NetEQ_inst : NetEQ instance - * - VADinst : VAD instance - * - initFunction : Pointer to VAD init function - * - setmodeFunction : Pointer to VAD setmode function - * - VADfunction : Pointer to VAD function - * - * Output: - * - NetEQ_inst : Updated NetEQ instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_SetVADInstance(void *NetEQ_inst, void *VAD_inst, - WebRtcNetEQ_VADInitFunction initFunction, - WebRtcNetEQ_VADSetmodeFunction setmodeFunction, - WebRtcNetEQ_VADFunction VADFunction); - -/**************************************************************************** - * WebRtcNetEQ_SetVADMode(...) - * - * Pass an aggressiveness mode parameter to the post-decode VAD instance. - * If this function is never called, mode 0 (quality mode) is used as default. - * - * Input: - * - inst : NetEQ instance - * - mode : mode parameter (same range as WebRtc VAD mode) - * - * Output: - * - inst : Updated NetEQ instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_SetVADMode(void *NetEQ_inst, int mode); - -/**************************************************************************** - * WebRtcNetEQ_RecOutNoDecode(...) - * - * Special RecOut that does not do any decoding. - * - * Input: - * - inst : NetEQ instance - * - * Output: - * - inst : Updated NetEQ instance - * - pw16_outData : Pointer to vector where output should be written - * - pw16_len : Pointer to variable where output length is returned - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RecOutNoDecode(void *inst, int16_t *pw16_outData, - int16_t *pw16_len); - -/**************************************************************************** - * WebRtcNetEQ_FlushBuffers(...) - * - * Flush packet and speech buffers. Does not reset codec database or - * jitter statistics. - * - * Input: - * - inst : NetEQ instance - * - * Output: - * - inst : Updated NetEQ instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_FlushBuffers(void *inst); - -/***************************************************************************** - * void WebRtcNetEq_EnableAVSync(...) - * - * Enable AV-sync. If Enabled, NetEq will screen for sync payloads. For - * each sync payload a silence frame is generated. - * - * Input: - * - inst : NetEQ instance - * - enable : non-zero to enable, otherwise disabled. - * - * Output: - * - inst : Updated NetEQ instance - * - */ - -void WebRtcNetEQ_EnableAVSync(void* inst, int enable); - -/**************************************************************************** - * WebRtcNetEQ_RecInSyncRTP(...) - * - * Insert a sync packet with the given RTP specification. - * - * Input: - * - inst : NetEQ instance - * - rtpInfo : Pointer to RTP info - * - receive_timestamp : Receive time (in timestamps of the used codec) - * - * Output: - * - inst : Updated NetEQ instance - * - * Return value : if succeeded it returns the number of bytes pushed - * in, otherwise returns -1. - */ - -int WebRtcNetEQ_RecInSyncRTP(void* inst, - WebRtcNetEQ_RTPInfo* rtp_info, - uint32_t receive_timestamp); - -/* - * Set a minimum latency for the jitter buffer. The overall delay is the max of - * |minimum_delay_ms| and the latency that is internally computed based on the - * inter-arrival times. - */ -int WebRtcNetEQ_SetMinimumDelay(void *inst, int minimum_delay_ms); - -/* - * Set a maximum latency for the jitter buffer. The overall delay is the min of - * |maximum_delay_ms| and the latency that is internally computed based on the - * inter-arrival times. - */ -int WebRtcNetEQ_SetMaximumDelay(void *inst, int maximum_delay_ms); - -/* - * Get the least required delay in milliseconds given inter-arrival times - * and playout mode. - */ -int WebRtcNetEQ_GetRequiredDelayMs(const void* inst); - -#ifdef __cplusplus -} -#endif - -#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu_address_init.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu_address_init.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu_address_init.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu_address_init.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "mcu.h" - -#include /* to define NULL */ - -/* - * Initializes MCU with read address and write address - */ -int WebRtcNetEQ_McuAddressInit(MCUInst_t *inst, void * Data2McuAddress, - void * Data2DspAddress, void *main_inst) -{ - - inst->pw16_readAddress = (int16_t*) Data2McuAddress; - inst->pw16_writeAddress = (int16_t*) Data2DspAddress; - inst->main_inst = main_inst; - - inst->millisecondsPerCall = 10; - - /* Do expansions in the beginning */ - if (inst->pw16_writeAddress != NULL) inst->pw16_writeAddress[0] = DSP_INSTR_EXPAND; - - return (0); -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu_dsp_common.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu_dsp_common.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu_dsp_common.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu_dsp_common.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Communication between MCU and DSP sides. - */ - -#include "mcu_dsp_common.h" - -#include - -/* Initialize instances with read and write address */ -int WebRtcNetEQ_DSPinit(MainInst_t *inst) -{ - int res = 0; - - res |= WebRtcNetEQ_AddressInit(&inst->DSPinst, NULL, NULL, inst); - res |= WebRtcNetEQ_McuAddressInit(&inst->MCUinst, NULL, NULL, inst); - - return res; - -} - -/* The DSP side will call this function to interrupt the MCU side */ -int WebRtcNetEQ_DSP2MCUinterrupt(MainInst_t *inst, int16_t *pw16_shared_mem) -{ - inst->MCUinst.pw16_readAddress = pw16_shared_mem; - inst->MCUinst.pw16_writeAddress = pw16_shared_mem; - return WebRtcNetEQ_SignalMcu(&inst->MCUinst); -} - -int WebRtcNetEQ_IsSyncPayload(const void* payload, int payload_len_bytes) { - if (payload_len_bytes != SYNC_PAYLOAD_LEN_BYTES || - memcmp(payload, kSyncPayload, SYNC_PAYLOAD_LEN_BYTES) != 0) { - return 0; - } - return 1; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu_dsp_common.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu_dsp_common.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu_dsp_common.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu_dsp_common.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * The main NetEQ instance, which is where the DSP and MCU sides join. - */ - -#ifndef MCU_DSP_COMMON_H -#define MCU_DSP_COMMON_H - -#include "typedefs.h" - -#include "dsp.h" -#include "mcu.h" - -/* Define size of shared memory area. */ -#if defined(NETEQ_48KHZ_WIDEBAND) - #define SHARED_MEM_SIZE (6*640) -#elif defined(NETEQ_32KHZ_WIDEBAND) - #define SHARED_MEM_SIZE (4*640) -#elif defined(NETEQ_WIDEBAND) - #define SHARED_MEM_SIZE (2*640) -#else - #define SHARED_MEM_SIZE 640 -#endif - -#define SYNC_PAYLOAD_LEN_BYTES 7 -static const uint8_t kSyncPayload[SYNC_PAYLOAD_LEN_BYTES] = { - 'a', 'v', 's', 'y', 'n', 'c', '\0' }; - -/* Struct to hold the NetEQ instance */ -typedef struct -{ - DSPInst_t DSPinst; /* DSP part of the NetEQ instance */ - MCUInst_t MCUinst; /* MCU part of the NetEQ instance */ - int16_t ErrorCode; /* Store last error code */ -#ifdef NETEQ_STEREO - int16_t masterSlave; /* 0 = not set, 1 = master, 2 = slave */ -#endif /* NETEQ_STEREO */ -} MainInst_t; - -/* Struct used for communication between DSP and MCU sides of NetEQ */ -typedef struct -{ - uint32_t playedOutTS; /* Timestamp position at end of DSP data */ - uint16_t samplesLeft; /* Number of samples stored */ - int16_t MD; /* Multiple description codec information */ - int16_t lastMode; /* Latest mode of NetEQ playout */ - int16_t frameLen; /* Frame length of previously decoded packet */ -} DSP2MCU_info_t; - -/* Initialize instances with read and write address */ -int WebRtcNetEQ_DSPinit(MainInst_t *inst); - -/* The DSP side will call this function to interrupt the MCU side */ -int WebRtcNetEQ_DSP2MCUinterrupt(MainInst_t *inst, int16_t *pw16_shared_mem); - -/* Returns 1 if the given payload matches |kSyncPayload| payload, otherwise - * 0 is returned. */ -int WebRtcNetEQ_IsSyncPayload(const void* payload, int payload_len_bytes); - -#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,300 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * MCU struct and functions related to the MCU side operations. - */ - -#ifndef MCU_H -#define MCU_H - -#include "typedefs.h" - -#include "codec_db.h" -#include "rtcp.h" -#include "packet_buffer.h" -#include "buffer_stats.h" -#include "neteq_statistics.h" - -#ifdef NETEQ_ATEVENT_DECODE -#include "dtmf_buffer.h" -#endif - -#define MAX_ONE_DESC 5 /* cannot do more than this many consecutive one-descriptor decodings */ -#define MAX_LOSS_REPORT_PERIOD 60 /* number of seconds between auto-reset */ - -enum TsScaling -{ - kTSnoScaling = 0, - kTSscalingTwo, - kTSscalingTwoThirds, - kTSscalingFourThirds -}; - -enum { kLenWaitingTimes = 100 }; - -typedef struct -{ - - int16_t current_Codec; - int16_t current_Payload; - uint32_t timeStamp; /* Next timestamp that should be played */ - int16_t millisecondsPerCall; - uint16_t timestampsPerCall; /* Output chunk size */ - uint16_t fs; - uint32_t ssrc; /* Current ssrc */ - int16_t new_codec; - int16_t first_packet; - - /* MCU/DSP Communication layer */ - int16_t *pw16_readAddress; - int16_t *pw16_writeAddress; - void *main_inst; - - CodecDbInst_t codec_DB_inst; /* Information about all the codecs, i.e. which - functions to use and which codpoints that - have been assigned */ - SplitInfo_t PayloadSplit_inst; /* Information about how the current codec - payload should be splitted */ - WebRtcNetEQ_RTCP_t RTCP_inst; /* RTCP statistics */ - PacketBuf_t PacketBuffer_inst; /* The packet buffer */ - BufstatsInst_t BufferStat_inst; /* Statistics that are used to make decision - for what the DSP should perform */ -#ifdef NETEQ_ATEVENT_DECODE - dtmf_inst_t DTMF_inst; -#endif - int NoOfExpandCalls; - int16_t AVT_PlayoutOn; - enum WebRtcNetEQPlayoutMode NetEqPlayoutMode; - - int16_t one_desc; /* Number of times running on one desc */ - - uint32_t lostTS; /* Number of timestamps lost */ - uint32_t lastReportTS; /* Timestamp elapsed since last report was given */ - - int waiting_times[kLenWaitingTimes]; /* Waiting time statistics storage. */ - int len_waiting_times; - int next_waiting_time_index; - - uint32_t externalTS; - uint32_t internalTS; - int16_t TSscalingInitialized; - enum TsScaling scalingFactor; - - /* AV-sync enabled. In AV-sync NetEq screens packets for specific sync - * packets. Sync packets are not decoded by a decoder but generate all-zero - * signal with the same number of samples as previously decoded payload. - * Also in AV-sync mode the sample-size of a sync payload is reported as - * previous frame-size. */ - int av_sync; - -#ifdef NETEQ_STEREO - int usingStereo; -#endif - - /* The sequence number of the latest decoded RTP payload. */ - int decoded_packet_sequence_number; - uint32_t decoded_packet_timestamp; -} MCUInst_t; - -/**************************************************************************** - * WebRtcNetEQ_McuReset(...) - * - * Reset the MCU instance. - * - * Input: - * - inst : MCU instance - * - * Return value : 0 - Ok - * <0 - Error - */ -int WebRtcNetEQ_McuReset(MCUInst_t *inst); - -/**************************************************************************** - * WebRtcNetEQ_ResetMcuInCallStats(...) - * - * Reset MCU-side statistics variables for the in-call statistics. - * - * Input: - * - inst : MCU instance - * - * Return value : 0 - Ok - * <0 - Error - */ -int WebRtcNetEQ_ResetMcuInCallStats(MCUInst_t *inst); - -/**************************************************************************** - * WebRtcNetEQ_ResetWaitingTimeStats(...) - * - * Reset waiting-time statistics. - * - * Input: - * - inst : MCU instance. - * - * Return value : n/a - */ -void WebRtcNetEQ_ResetWaitingTimeStats(MCUInst_t *inst); - -/**************************************************************************** - * WebRtcNetEQ_LogWaitingTime(...) - * - * Log waiting-time to the statistics. - * - * Input: - * - inst : MCU instance. - * - waiting_time : Waiting time in "RecOut calls" (i.e., 1 call = 10 ms). - * - * Return value : n/a - */ -void WebRtcNetEQ_StoreWaitingTime(MCUInst_t *inst, int waiting_time); - -/**************************************************************************** - * WebRtcNetEQ_ResetMcuJitterStat(...) - * - * Reset MCU-side statistics variables for the post-call statistics. - * - * Input: - * - inst : MCU instance - * - * Return value : 0 - Ok - * <0 - Error - */ -int WebRtcNetEQ_ResetMcuJitterStat(MCUInst_t *inst); - -/**************************************************************************** - * WebRtcNetEQ_McuAddressInit(...) - * - * Initializes MCU with read address and write address. - * - * Input: - * - inst : MCU instance - * - Data2McuAddress : Pointer to MCU address - * - Data2DspAddress : Pointer to DSP address - * - main_inst : Pointer to NetEQ main instance - * - * Return value : 0 - Ok - * <0 - Error - */ -int WebRtcNetEQ_McuAddressInit(MCUInst_t *inst, void * Data2McuAddress, - void * Data2DspAddress, void *main_inst); - -/**************************************************************************** - * WebRtcNetEQ_McuSetFs(...) - * - * Initializes MCU with read address and write address. - * - * Input: - * - inst : MCU instance - * - fs_hz : Sample rate in Hz -- 8000, 16000, 32000, (48000) - * - * Return value : 0 - Ok - * <0 - Error - */ -int WebRtcNetEQ_McuSetFs(MCUInst_t *inst, uint16_t fs_hz); - -/**************************************************************************** - * WebRtcNetEQ_SignalMcu(...) - * - * Signal the MCU that data is available and ask for a RecOut decision. - * - * Input: - * - inst : MCU instance - * - av_sync : 1 if NetEQ is in AV-sync mode, otherwise 0. - * - * Return value : 0 - Ok - * <0 - Error - */ -int WebRtcNetEQ_SignalMcu(MCUInst_t *inst); - -/**************************************************************************** - * WebRtcNetEQ_RecInInternal(...) - * - * This function inserts a packet into the jitter buffer. - * - * Input: - * - MCU_inst : MCU instance - * - RTPpacket : The RTP packet, parsed into NetEQ's internal RTP struct - * - uw32_timeRec : Time stamp for the arrival of the packet (not RTP timestamp) - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RecInInternal(MCUInst_t *MCU_inst, RTPPacket_t *RTPpacket, - uint32_t uw32_timeRec); - -/**************************************************************************** - * WebRtcNetEQ_RecInInternal(...) - * - * Split the packet according to split_inst and inserts the parts into - * Buffer_inst. - * - * Input: - * - MCU_inst : MCU instance - * - RTPpacket : The RTP packet, parsed into NetEQ's internal RTP struct - * - uw32_timeRec : Time stamp for the arrival of the packet (not RTP timestamp) - * - av_sync : indicates if AV-sync is enabled, 1 enabled, - * 0 disabled. - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNetEQ_SplitAndInsertPayload(RTPPacket_t* packet, - PacketBuf_t* Buffer_inst, - SplitInfo_t* split_inst, - int16_t* flushed, - int av_sync); - -/**************************************************************************** - * WebRtcNetEQ_GetTimestampScaling(...) - * - * Update information about timestamp scaling for a payload type - * in MCU_inst->scalingFactor. - * - * Input: - * - MCU_inst : MCU instance - * - rtpPayloadType : RTP payload number - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_GetTimestampScaling(MCUInst_t *MCU_inst, int rtpPayloadType); - -/**************************************************************************** - * WebRtcNetEQ_ScaleTimestampExternalToInternal(...) - * - * Convert from external to internal timestamp using current scaling info. - * - * Input: - * - MCU_inst : MCU instance - * - externalTS : External timestamp - * - * Return value : Internal timestamp - */ - -uint32_t WebRtcNetEQ_ScaleTimestampExternalToInternal(const MCUInst_t *MCU_inst, - uint32_t externalTS); - -/**************************************************************************** - * WebRtcNetEQ_ScaleTimestampInternalToExternal(...) - * - * Convert from external to internal timestamp using current scaling info. - * - * Input: - * - MCU_inst : MCU instance - * - externalTS : Internal timestamp - * - * Return value : External timestamp - */ - -uint32_t WebRtcNetEQ_ScaleTimestampInternalToExternal(const MCUInst_t *MCU_inst, - uint32_t internalTS); -#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu_reset.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu_reset.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu_reset.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mcu_reset.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Reset MCU side data. - */ - -#include "mcu.h" - -#include -#include - -#include "automode.h" - -int WebRtcNetEQ_McuReset(MCUInst_t *inst) -{ - -#ifdef NETEQ_ATEVENT_DECODE - int ok; -#endif - - /* MCU/DSP Communication layer */ - inst->pw16_readAddress = NULL; - inst->pw16_writeAddress = NULL; - inst->main_inst = NULL; - inst->one_desc = 0; - inst->BufferStat_inst.Automode_inst.extraDelayMs = 0; - inst->BufferStat_inst.Automode_inst.minimum_delay_ms = 0; - inst->BufferStat_inst.Automode_inst.maximum_delay_ms = 10000; - inst->NetEqPlayoutMode = kPlayoutOn; - inst->av_sync = 0; - - WebRtcNetEQ_DbReset(&inst->codec_DB_inst); - memset(&inst->PayloadSplit_inst, 0, sizeof(SplitInfo_t)); - - /* Clear the Packet buffer and the pointer to memory storage */ - WebRtcNetEQ_PacketBufferFlush(&inst->PacketBuffer_inst); - inst->PacketBuffer_inst.memorySizeW16 = 0; - inst->PacketBuffer_inst.maxInsertPositions = 0; - - /* Clear the decision and delay history */ - memset(&inst->BufferStat_inst, 0, sizeof(BufstatsInst_t)); -#ifdef NETEQ_ATEVENT_DECODE - ok = WebRtcNetEQ_DtmfDecoderInit(&inst->DTMF_inst, 8000, 560); - if (ok != 0) - { - return ok; - } -#endif - inst->NoOfExpandCalls = 0; - inst->current_Codec = -1; - inst->current_Payload = -1; - - inst->millisecondsPerCall = 10; - inst->timestampsPerCall = inst->millisecondsPerCall * 8; - inst->fs = 8000; - inst->first_packet = 1; - - WebRtcNetEQ_ResetMcuInCallStats(inst); - - WebRtcNetEQ_ResetWaitingTimeStats(inst); - - WebRtcNetEQ_ResetMcuJitterStat(inst); - - WebRtcNetEQ_ResetAutomode(&(inst->BufferStat_inst.Automode_inst), - inst->PacketBuffer_inst.maxInsertPositions); - - return 0; -} - -/* - * Reset MCU-side statistics variables for the in-call statistics. - */ - -int WebRtcNetEQ_ResetMcuInCallStats(MCUInst_t *inst) -{ - inst->lostTS = 0; - inst->lastReportTS = 0; - inst->PacketBuffer_inst.discardedPackets = 0; - - return 0; -} - -/* - * Reset waiting-time statistics. - */ - -void WebRtcNetEQ_ResetWaitingTimeStats(MCUInst_t *inst) { - memset(inst->waiting_times, 0, - kLenWaitingTimes * sizeof(inst->waiting_times[0])); - inst->len_waiting_times = 0; - inst->next_waiting_time_index = 0; -} - -/* - * Store waiting-time in the statistics. - */ - -void WebRtcNetEQ_StoreWaitingTime(MCUInst_t *inst, int waiting_time) { - assert(inst->next_waiting_time_index < kLenWaitingTimes); - inst->waiting_times[inst->next_waiting_time_index] = waiting_time; - inst->next_waiting_time_index++; - if (inst->next_waiting_time_index >= kLenWaitingTimes) { - inst->next_waiting_time_index = 0; - } - if (inst->len_waiting_times < kLenWaitingTimes) { - inst->len_waiting_times++; - } -} - -/* - * Reset all MCU-side statistics variables for the post-call statistics. - */ - -int WebRtcNetEQ_ResetMcuJitterStat(MCUInst_t *inst) -{ - inst->BufferStat_inst.Automode_inst.countIAT500ms = 0; - inst->BufferStat_inst.Automode_inst.countIAT1000ms = 0; - inst->BufferStat_inst.Automode_inst.countIAT2000ms = 0; - inst->BufferStat_inst.Automode_inst.longestIATms = 0; - - return 0; -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/merge.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/merge.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/merge.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/merge.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,570 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This is the function to merge a new packet with expanded data after a packet loss. - */ - -#include "dsp.h" - -#include "signal_processing_library.h" - -#include "dsp_helpfunctions.h" -#include "neteq_error_codes.h" - -/**************************************************************************** - * WebRtcNetEQ_Merge(...) - * - * This function... - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector. - * - decoded : Pointer to new decoded speech. - * - len : Number of samples in pw16_decoded. - * - * - * Output: - * - inst : Updated user information - * - outData : Pointer to a memory space where the output data - * should be stored - * - pw16_len : Number of samples written to pw16_outData - * - * Return value : 0 - Ok - * <0 - Error - */ - -/* Scratch usage: - - Type Name size startpos endpos - int16_t pw16_expanded 210*fs/8000 0 209*fs/8000 - int16_t pw16_expandedLB 100 210*fs/8000 99+210*fs/8000 - int16_t pw16_decodedLB 40 100+210*fs/8000 139+210*fs/8000 - int32_t pw32_corr 2*60 140+210*fs/8000 260+210*fs/8000 - int16_t pw16_corrVec 68 210*fs/8000 67+210*fs/8000 - - [gap in scratch vector] - - func WebRtcNetEQ_Expand 40+370*fs/8000 126*fs/8000 39+496*fs/8000 - - Total: 40+496*fs/8000 - */ - -#define SCRATCH_pw16_expanded 0 -#if (defined(NETEQ_48KHZ_WIDEBAND)) -#define SCRATCH_pw16_expandedLB 1260 -#define SCRATCH_pw16_decodedLB 1360 -#define SCRATCH_pw32_corr 1400 -#define SCRATCH_pw16_corrVec 1260 -#define SCRATCH_NETEQ_EXPAND 756 -#elif (defined(NETEQ_32KHZ_WIDEBAND)) -#define SCRATCH_pw16_expandedLB 840 -#define SCRATCH_pw16_decodedLB 940 -#define SCRATCH_pw32_corr 980 -#define SCRATCH_pw16_corrVec 840 -#define SCRATCH_NETEQ_EXPAND 504 -#elif (defined(NETEQ_WIDEBAND)) -#define SCRATCH_pw16_expandedLB 420 -#define SCRATCH_pw16_decodedLB 520 -#define SCRATCH_pw32_corr 560 -#define SCRATCH_pw16_corrVec 420 -#define SCRATCH_NETEQ_EXPAND 252 -#else /* NB */ -#define SCRATCH_pw16_expandedLB 210 -#define SCRATCH_pw16_decodedLB 310 -#define SCRATCH_pw32_corr 350 -#define SCRATCH_pw16_corrVec 210 -#define SCRATCH_NETEQ_EXPAND 126 -#endif - -int WebRtcNetEQ_Merge(DSPInst_t *inst, -#ifdef SCRATCH - int16_t *pw16_scratchPtr, -#endif - int16_t *pw16_decoded, int len, int16_t *pw16_outData, - int16_t *pw16_len) -{ - - int16_t fs_mult; - int16_t fs_shift; - int32_t w32_En_new_frame, w32_En_old_frame; - int16_t w16_expmax, w16_newmax; - int16_t w16_tmp, w16_tmp2; - int32_t w32_tmp; -#ifdef SCRATCH - int16_t *pw16_expanded = pw16_scratchPtr + SCRATCH_pw16_expanded; - int16_t *pw16_expandedLB = pw16_scratchPtr + SCRATCH_pw16_expandedLB; - int16_t *pw16_decodedLB = pw16_scratchPtr + SCRATCH_pw16_decodedLB; - int32_t *pw32_corr = (int32_t*) (pw16_scratchPtr + SCRATCH_pw32_corr); - int16_t *pw16_corrVec = pw16_scratchPtr + SCRATCH_pw16_corrVec; -#else - int16_t pw16_expanded[(125+80+5)*FSMULT]; - int16_t pw16_expandedLB[100]; - int16_t pw16_decodedLB[40]; - int32_t pw32_corr[60]; - int16_t pw16_corrVec[4+60+4]; -#endif - int16_t *pw16_corr = &pw16_corrVec[4]; - int16_t w16_stopPos = 0, w16_bestIndex, w16_interpLen; - int16_t w16_bestVal; /* bestVal is dummy */ - int16_t w16_startfact, w16_inc; - int16_t w16_expandedLen; - int16_t w16_startPos; - int16_t w16_expLen, w16_newLen = 0; - int16_t *pw16_decodedOut; - int16_t w16_muted; - - int w16_decodedLen = len; - -#ifdef NETEQ_STEREO - MasterSlaveInfo *msInfo = inst->msInfo; -#endif - - fs_mult = WebRtcSpl_DivW32W16ResW16(inst->fs, 8000); - fs_shift = 30 - WebRtcSpl_NormW32(fs_mult); /* Note that this is not "exact" for 48kHz */ - - /************************************* - * Generate data to merge with - *************************************/ - /* - * Check how much data that is left since earlier - * (at least there should be the overlap)... - */ - w16_startPos = inst->endPosition - inst->curPosition; - /* Get one extra expansion to merge and overlap with */ - inst->ExpandInst.w16_stopMuting = 1; - inst->ExpandInst.w16_lagsDirection = 1; /* make sure we get the "optimal" lag */ - inst->ExpandInst.w16_lagsPosition = -1; /* out of the 3 possible ones */ - w16_expandedLen = 0; /* Does not fill any function currently */ - - if (w16_startPos >= 210 * FSMULT) - { - /* - * The number of samples available in the sync buffer is more than what fits in - * pw16_expanded.Keep the first 210*FSMULT samples, but shift them towards the end of - * the buffer. This is ok, since all of the buffer will be expand data anyway, so as - * long as the beginning is left untouched, we're fine. - */ - - w16_tmp = w16_startPos - 210 * FSMULT; /* length difference */ - - WEBRTC_SPL_MEMMOVE_W16(&inst->speechBuffer[inst->curPosition+w16_tmp] , - &inst->speechBuffer[inst->curPosition], 210*FSMULT); - - inst->curPosition += w16_tmp; /* move start position of sync buffer accordingly */ - w16_startPos = 210 * FSMULT; /* this is the truncated length */ - } - - WebRtcNetEQ_Expand(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_EXPAND, -#endif - pw16_expanded, /* let Expand write to beginning of pw16_expanded to avoid overflow */ - &w16_newLen, 0); - - /* - * Now shift the data in pw16_expanded to where it belongs. - * Truncate all that ends up outside the vector. - */ - - WEBRTC_SPL_MEMMOVE_W16(&pw16_expanded[w16_startPos], pw16_expanded, - WEBRTC_SPL_MIN(w16_newLen, - WEBRTC_SPL_MAX(210*FSMULT - w16_startPos, 0) ) ); - - inst->ExpandInst.w16_stopMuting = 0; - - /* Copy what is left since earlier into the expanded vector */ - - WEBRTC_SPL_MEMCPY_W16(pw16_expanded, &inst->speechBuffer[inst->curPosition], w16_startPos); - - /* - * Do "ugly" copy and paste from the expanded in order to generate more data - * to correlate (but not interpolate) with. - */ - w16_expandedLen = (120 + 80 + 2) * fs_mult; - w16_expLen = w16_startPos + w16_newLen; - - if (w16_expLen < w16_expandedLen) - { - while ((w16_expLen + w16_newLen) < w16_expandedLen) - { - WEBRTC_SPL_MEMCPY_W16(&pw16_expanded[w16_expLen], &pw16_expanded[w16_startPos], - w16_newLen); - w16_expLen += w16_newLen; - } - - /* Copy last part (fraction of a whole expansion) */ - - WEBRTC_SPL_MEMCPY_W16(&pw16_expanded[w16_expLen], &pw16_expanded[w16_startPos], - (w16_expandedLen-w16_expLen)); - } - w16_expLen = w16_expandedLen; - - /* Adjust muting factor (main muting factor times expand muting factor) */ - inst->w16_muteFactor - = (int16_t) WEBRTC_SPL_MUL_16_16_RSFT(inst->w16_muteFactor, - inst->ExpandInst.w16_expandMuteFactor, 14); - - /* Adjust muting factor if new vector is more or less of the BGN energy */ - len = WEBRTC_SPL_MIN(64*fs_mult, w16_decodedLen); - w16_expmax = WebRtcSpl_MaxAbsValueW16(pw16_expanded, (int16_t) len); - w16_newmax = WebRtcSpl_MaxAbsValueW16(pw16_decoded, (int16_t) len); - - /* Calculate energy of old data */ - w16_tmp = 6 + fs_shift - WebRtcSpl_NormW32(WEBRTC_SPL_MUL_16_16(w16_expmax, w16_expmax)); - w16_tmp = WEBRTC_SPL_MAX(w16_tmp,0); - w32_En_old_frame = WebRtcNetEQ_DotW16W16(pw16_expanded, pw16_expanded, len, w16_tmp); - - /* Calculate energy of new data */ - w16_tmp2 = 6 + fs_shift - WebRtcSpl_NormW32(WEBRTC_SPL_MUL_16_16(w16_newmax, w16_newmax)); - w16_tmp2 = WEBRTC_SPL_MAX(w16_tmp2,0); - w32_En_new_frame = WebRtcNetEQ_DotW16W16(pw16_decoded, pw16_decoded, len, w16_tmp2); - - /* Align to same Q-domain */ - if (w16_tmp2 > w16_tmp) - { - w32_En_old_frame = WEBRTC_SPL_RSHIFT_W32(w32_En_old_frame, (w16_tmp2-w16_tmp)); - } - else - { - w32_En_new_frame = WEBRTC_SPL_RSHIFT_W32(w32_En_new_frame, (w16_tmp-w16_tmp2)); - } - - /* Calculate muting factor to use for new frame */ - if (w32_En_new_frame > w32_En_old_frame) - { - /* Normalize w32_En_new_frame to 14 bits */ - w16_tmp = WebRtcSpl_NormW32(w32_En_new_frame) - 17; - w32_En_new_frame = WEBRTC_SPL_SHIFT_W32(w32_En_new_frame, w16_tmp); - - /* - * Put w32_En_old_frame in a domain 14 higher, so that - * w32_En_old_frame/w32_En_new_frame is in Q14 - */ - w16_tmp = w16_tmp + 14; - w32_En_old_frame = WEBRTC_SPL_SHIFT_W32(w32_En_old_frame, w16_tmp); - w16_tmp - = WebRtcSpl_DivW32W16ResW16(w32_En_old_frame, (int16_t) w32_En_new_frame); - /* Calculate sqrt(w32_En_old_frame/w32_En_new_frame) in Q14 */ - w16_muted = (int16_t) WebRtcSpl_SqrtFloor( - WEBRTC_SPL_LSHIFT_W32((int32_t)w16_tmp,14)); - } - else - { - w16_muted = 16384; /* Set = 1.0 when old frame has higher energy than new */ - } - - /* Set the raise the continued muting factor w16_muted if w16_muteFactor is lower */ - if (w16_muted > inst->w16_muteFactor) - { - inst->w16_muteFactor = WEBRTC_SPL_MIN(w16_muted, 16384); - } - -#ifdef NETEQ_STEREO - - /* Sanity for msInfo */ - if (msInfo == NULL) - { - /* this should not happen here */ - return MASTER_SLAVE_ERROR; - } - - /* do not downsample and calculate correlations for slave instance(s) */ - if ((msInfo->msMode == NETEQ_MASTER) || (msInfo->msMode == NETEQ_MONO)) - { -#endif - - /********************************************* - * Downsample to 4kHz and find best overlap - *********************************************/ - - /* Downsample to 4 kHz */ - if (inst->fs == 8000) - { - WebRtcSpl_DownsampleFast(&pw16_expanded[2], (int16_t) (w16_expandedLen - 2), - pw16_expandedLB, (int16_t) (100), - (int16_t*) WebRtcNetEQ_kDownsample8kHzTbl, (int16_t) 3, - (int16_t) 2, (int16_t) 0); - if (w16_decodedLen <= 80) - { - /* Not quite long enough, so we have to cheat a bit... */ - int16_t temp_len = w16_decodedLen - 2; - w16_tmp = temp_len / 2; - WebRtcSpl_DownsampleFast(&pw16_decoded[2], temp_len, - pw16_decodedLB, w16_tmp, - (int16_t*) WebRtcNetEQ_kDownsample8kHzTbl, - (int16_t) 3, (int16_t) 2, (int16_t) 0); - WebRtcSpl_MemSetW16(&pw16_decodedLB[w16_tmp], 0, (40 - w16_tmp)); - } - else - { - WebRtcSpl_DownsampleFast(&pw16_decoded[2], - (int16_t) (w16_decodedLen - 2), pw16_decodedLB, - (int16_t) (40), (int16_t*) WebRtcNetEQ_kDownsample8kHzTbl, - (int16_t) 3, (int16_t) 2, (int16_t) 0); - } -#ifdef NETEQ_WIDEBAND - } - else if (inst->fs==16000) - { - WebRtcSpl_DownsampleFast( - &pw16_expanded[4], (int16_t)(w16_expandedLen-4), - pw16_expandedLB, (int16_t)(100), - (int16_t*)WebRtcNetEQ_kDownsample16kHzTbl, (int16_t)5, - (int16_t)4, (int16_t)0); - if (w16_decodedLen<=160) - { - /* Not quite long enough, so we have to cheat a bit... */ - int16_t temp_len = w16_decodedLen - 4; - w16_tmp = temp_len / 4; - WebRtcSpl_DownsampleFast( - &pw16_decoded[4], temp_len, - pw16_decodedLB, w16_tmp, - (int16_t*)WebRtcNetEQ_kDownsample16kHzTbl, (int16_t)5, - (int16_t)4, (int16_t)0); - WebRtcSpl_MemSetW16(&pw16_decodedLB[w16_tmp], 0, (40-w16_tmp)); - } - else - { - WebRtcSpl_DownsampleFast( - &pw16_decoded[4], (int16_t)(w16_decodedLen-4), - pw16_decodedLB, (int16_t)(40), - (int16_t*)WebRtcNetEQ_kDownsample16kHzTbl, (int16_t)5, - (int16_t)4, (int16_t)0); - } -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - } - else if (inst->fs==32000) - { - /* - * TODO(hlundin) Why is the offset into pw16_expanded 6? - */ - WebRtcSpl_DownsampleFast( - &pw16_expanded[6], (int16_t)(w16_expandedLen-6), - pw16_expandedLB, (int16_t)(100), - (int16_t*)WebRtcNetEQ_kDownsample32kHzTbl, (int16_t)7, - (int16_t)8, (int16_t)0); - if (w16_decodedLen<=320) - { - /* Not quite long enough, so we have to cheat a bit... */ - int16_t temp_len = w16_decodedLen - 6; - w16_tmp = temp_len / 8; - WebRtcSpl_DownsampleFast( - &pw16_decoded[6], temp_len, - pw16_decodedLB, w16_tmp, - (int16_t*)WebRtcNetEQ_kDownsample32kHzTbl, (int16_t)7, - (int16_t)8, (int16_t)0); - WebRtcSpl_MemSetW16(&pw16_decodedLB[w16_tmp], 0, (40-w16_tmp)); - } - else - { - WebRtcSpl_DownsampleFast( - &pw16_decoded[6], (int16_t)(w16_decodedLen-6), - pw16_decodedLB, (int16_t)(40), - (int16_t*)WebRtcNetEQ_kDownsample32kHzTbl, (int16_t)7, - (int16_t)8, (int16_t)0); - } -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - } - else /* if (inst->fs==48000) */ - { - /* - * TODO(hlundin) Why is the offset into pw16_expanded 6? - */ - WebRtcSpl_DownsampleFast( - &pw16_expanded[6], (int16_t)(w16_expandedLen-6), - pw16_expandedLB, (int16_t)(100), - (int16_t*)WebRtcNetEQ_kDownsample48kHzTbl, (int16_t)7, - (int16_t)12, (int16_t)0); - if (w16_decodedLen<=320) - { - /* Not quite long enough, so we have to cheat a bit... */ - /* - * TODO(hlundin): Is this correct? Downsampling is a factor 12 - * but w16_tmp = temp_len / 8. - * (Was w16_tmp = ((w16_decodedLen-6)>>3) before re-write.) - */ - int16_t temp_len = w16_decodedLen - 6; - w16_tmp = temp_len / 8; - WebRtcSpl_DownsampleFast( - &pw16_decoded[6], temp_len, - pw16_decodedLB, w16_tmp, - (int16_t*)WebRtcNetEQ_kDownsample48kHzTbl, (int16_t)7, - (int16_t)12, (int16_t)0); - WebRtcSpl_MemSetW16(&pw16_decodedLB[w16_tmp], 0, (40-w16_tmp)); - } - else - { - WebRtcSpl_DownsampleFast( - &pw16_decoded[6], (int16_t)(w16_decodedLen-6), - pw16_decodedLB, (int16_t)(40), - (int16_t*)WebRtcNetEQ_kDownsample48kHzTbl, (int16_t)7, - (int16_t)12, (int16_t)0); - } -#endif - } - - /* Calculate correlation without any normalization (40 samples) */ - w16_tmp = WebRtcSpl_DivW32W16ResW16((int32_t) inst->ExpandInst.w16_maxLag, - (int16_t) (fs_mult * 2)) + 1; - w16_stopPos = WEBRTC_SPL_MIN(60, w16_tmp); - w32_tmp = WEBRTC_SPL_MUL_16_16(w16_expmax, w16_newmax); - if (w32_tmp > 26843546) - { - w16_tmp = 3; - } - else - { - w16_tmp = 0; - } - - WebRtcNetEQ_CrossCorr(pw32_corr, pw16_decodedLB, pw16_expandedLB, 40, - (int16_t) w16_stopPos, w16_tmp, 1); - - /* Normalize correlation to 14 bits and put in a int16_t vector */ - WebRtcSpl_MemSetW16(pw16_corrVec, 0, (4 + 60 + 4)); - w32_tmp = WebRtcSpl_MaxAbsValueW32(pw32_corr, w16_stopPos); - w16_tmp = 17 - WebRtcSpl_NormW32(w32_tmp); - w16_tmp = WEBRTC_SPL_MAX(0, w16_tmp); - - WebRtcSpl_VectorBitShiftW32ToW16(pw16_corr, w16_stopPos, pw32_corr, w16_tmp); - - /* Calculate allowed starting point for peak finding. - The peak location bestIndex must fulfill two criteria: - (1) w16_bestIndex+w16_decodedLen < inst->timestampsPerCall+inst->ExpandInst.w16_overlap - (2) w16_bestIndex+w16_decodedLen < w16_startPos */ - w16_tmp = WEBRTC_SPL_MAX(0, WEBRTC_SPL_MAX(w16_startPos, - inst->timestampsPerCall+inst->ExpandInst.w16_overlap) - w16_decodedLen); - /* Downscale starting index to 4kHz domain */ - w16_tmp2 = WebRtcSpl_DivW32W16ResW16((int32_t) w16_tmp, - (int16_t) (fs_mult << 1)); - -#ifdef NETEQ_STEREO - } /* end if (msInfo->msMode != NETEQ_SLAVE) */ - - if ((msInfo->msMode == NETEQ_MASTER) || (msInfo->msMode == NETEQ_MONO)) - { - /* This is master or mono instance; find peak */ - WebRtcNetEQ_PeakDetection(&pw16_corr[w16_tmp2], w16_stopPos, 1, fs_mult, &w16_bestIndex, - &w16_bestVal); - w16_bestIndex += w16_tmp; /* compensate for modified starting index */ - msInfo->bestIndex = w16_bestIndex; - } - else if (msInfo->msMode == NETEQ_SLAVE) - { - /* Get peak location from master instance */ - w16_bestIndex = msInfo->bestIndex; - } - else - { - /* Invalid mode */ - return MASTER_SLAVE_ERROR; - } - -#else /* NETEQ_STEREO */ - - /* Find peak */ - WebRtcNetEQ_PeakDetection(&pw16_corr[w16_tmp2], w16_stopPos, 1, fs_mult, &w16_bestIndex, - &w16_bestVal); - w16_bestIndex += w16_tmp; /* compensate for modified starting index */ - -#endif /* NETEQ_STEREO */ - - /* - * Ensure that underrun does not occur for 10ms case => we have to get at least - * 10ms + overlap . (This should never happen thanks to the above modification of - * peak-finding starting point.) - * */ - while ((w16_bestIndex + w16_decodedLen) < (inst->timestampsPerCall - + inst->ExpandInst.w16_overlap) || w16_bestIndex + w16_decodedLen < w16_startPos) - { - w16_bestIndex += w16_newLen; /* Jump one lag ahead */ - } - pw16_decodedOut = pw16_outData + w16_bestIndex; - - /* Mute the new decoded data if needed (and unmute it linearly) */ - w16_interpLen = WEBRTC_SPL_MIN(60*fs_mult, - w16_expandedLen-w16_bestIndex); /* this is the overlapping part of pw16_expanded */ - w16_interpLen = WEBRTC_SPL_MIN(w16_interpLen, w16_decodedLen); - w16_inc = WebRtcSpl_DivW32W16ResW16(4194, - fs_mult); /* in Q20, 0.004 for NB and 0.002 for WB */ - if (inst->w16_muteFactor < 16384) - { - WebRtcNetEQ_UnmuteSignal(pw16_decoded, &inst->w16_muteFactor, pw16_decoded, w16_inc, - (int16_t) w16_interpLen); - WebRtcNetEQ_UnmuteSignal(&pw16_decoded[w16_interpLen], &inst->w16_muteFactor, - &pw16_decodedOut[w16_interpLen], w16_inc, - (int16_t) (w16_decodedLen - w16_interpLen)); - } - else - { - /* No muting needed */ - - WEBRTC_SPL_MEMMOVE_W16(&pw16_decodedOut[w16_interpLen], &pw16_decoded[w16_interpLen], - (w16_decodedLen-w16_interpLen)); - } - - /* Do overlap and interpolate linearly */ - w16_inc = WebRtcSpl_DivW32W16ResW16(16384, (int16_t) (w16_interpLen + 1)); /* Q14 */ - w16_startfact = (16384 - w16_inc); - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_expanded, w16_bestIndex); - WebRtcNetEQ_MixVoiceUnvoice(pw16_decodedOut, &pw16_expanded[w16_bestIndex], pw16_decoded, - &w16_startfact, w16_inc, w16_interpLen); - - inst->w16_mode = MODE_MERGE; - inst->ExpandInst.w16_consecExp = 0; /* Last was not expand any more */ - - /* New added length (w16_startPos samples were borrowed) */ - *pw16_len = w16_bestIndex + w16_decodedLen - w16_startPos; - - /* Update VQmon parameter */ - inst->w16_concealedTS += (*pw16_len - w16_decodedLen); - inst->w16_concealedTS = WEBRTC_SPL_MAX(0, inst->w16_concealedTS); - - /* Update in-call and post-call statistics */ - if (inst->ExpandInst.w16_expandMuteFactor == 0) - { - /* expansion generates noise only */ - inst->statInst.expandedNoiseSamples += (*pw16_len - w16_decodedLen); - /* Short-term activity statistics. */ - inst->activity_stats.merge_expand_bgn_samples += - (*pw16_len - w16_decodedLen); - } - else - { - /* expansion generates more than only noise */ - inst->statInst.expandedVoiceSamples += (*pw16_len - w16_decodedLen); - /* Short-term activity statistics. */ - inst->activity_stats.merge_expand_normal_samples += - (*pw16_len - w16_decodedLen); - } - inst->statInst.expandLength += (*pw16_len - w16_decodedLen); - - - /* Copy back the first part of the data to the speechHistory */ - - WEBRTC_SPL_MEMCPY_W16(&inst->speechBuffer[inst->curPosition], pw16_outData, w16_startPos); - - - /* Move data to within outData */ - - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, &pw16_outData[w16_startPos], (*pw16_len)); - - return 0; -} - -#undef SCRATCH_pw16_expanded -#undef SCRATCH_pw16_expandedLB -#undef SCRATCH_pw16_decodedLB -#undef SCRATCH_pw32_corr -#undef SCRATCH_pw16_corrVec -#undef SCRATCH_NETEQ_EXPAND diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/merge.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/merge.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/merge.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/merge.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/merge.h" + +#include +#include // memmove, memcpy, memset, size_t + +#include // min, max + +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" +#include "webrtc/modules/audio_coding/neteq/dsp_helper.h" +#include "webrtc/modules/audio_coding/neteq/expand.h" +#include "webrtc/modules/audio_coding/neteq/sync_buffer.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +int Merge::Process(int16_t* input, size_t input_length, + int16_t* external_mute_factor_array, + AudioMultiVector* output) { + // TODO(hlundin): Change to an enumerator and skip assert. + assert(fs_hz_ == 8000 || fs_hz_ == 16000 || fs_hz_ == 32000 || + fs_hz_ == 48000); + assert(fs_hz_ <= kMaxSampleRate); // Should not be possible. + + int old_length; + int expand_period; + // Get expansion data to overlap and mix with. + int expanded_length = GetExpandedSignal(&old_length, &expand_period); + + // Transfer input signal to an AudioMultiVector. + AudioMultiVector input_vector(num_channels_); + input_vector.PushBackInterleaved(input, input_length); + size_t input_length_per_channel = input_vector.Size(); + assert(input_length_per_channel == input_length / num_channels_); + + int16_t best_correlation_index = 0; + size_t output_length = 0; + + for (size_t channel = 0; channel < num_channels_; ++channel) { + int16_t* input_channel = &input_vector[channel][0]; + int16_t* expanded_channel = &expanded_[channel][0]; + int16_t expanded_max, input_max; + int16_t new_mute_factor = SignalScaling( + input_channel, static_cast(input_length_per_channel), + expanded_channel, &expanded_max, &input_max); + + // Adjust muting factor (product of "main" muting factor and expand muting + // factor). + int16_t* external_mute_factor = &external_mute_factor_array[channel]; + *external_mute_factor = + (*external_mute_factor * expand_->MuteFactor(channel)) >> 14; + + // Update |external_mute_factor| if it is lower than |new_mute_factor|. + if (new_mute_factor > *external_mute_factor) { + *external_mute_factor = std::min(new_mute_factor, + static_cast(16384)); + } + + if (channel == 0) { + // Downsample, correlate, and find strongest correlation period for the + // master (i.e., first) channel only. + // Downsample to 4kHz sample rate. + Downsample(input_channel, static_cast(input_length_per_channel), + expanded_channel, expanded_length); + + // Calculate the lag of the strongest correlation period. + best_correlation_index = CorrelateAndPeakSearch( + expanded_max, input_max, old_length, + static_cast(input_length_per_channel), expand_period); + } + + static const int kTempDataSize = 3600; + int16_t temp_data[kTempDataSize]; // TODO(hlundin) Remove this. + int16_t* decoded_output = temp_data + best_correlation_index; + + // Mute the new decoded data if needed (and unmute it linearly). + // This is the overlapping part of expanded_signal. + int interpolation_length = std::min( + kMaxCorrelationLength * fs_mult_, + expanded_length - best_correlation_index); + interpolation_length = std::min(interpolation_length, + static_cast(input_length_per_channel)); + if (*external_mute_factor < 16384) { + // Set a suitable muting slope (Q20). 0.004 for NB, 0.002 for WB, + // and so on. + int increment = 4194 / fs_mult_; + *external_mute_factor = DspHelper::RampSignal(input_channel, + interpolation_length, + *external_mute_factor, + increment); + DspHelper::UnmuteSignal(&input_channel[interpolation_length], + input_length_per_channel - interpolation_length, + external_mute_factor, increment, + &decoded_output[interpolation_length]); + } else { + // No muting needed. + memmove( + &decoded_output[interpolation_length], + &input_channel[interpolation_length], + sizeof(int16_t) * (input_length_per_channel - interpolation_length)); + } + + // Do overlap and mix linearly. + int increment = 16384 / (interpolation_length + 1); // In Q14. + int16_t mute_factor = 16384 - increment; + memmove(temp_data, expanded_channel, + sizeof(int16_t) * best_correlation_index); + DspHelper::CrossFade(&expanded_channel[best_correlation_index], + input_channel, interpolation_length, + &mute_factor, increment, decoded_output); + + output_length = best_correlation_index + input_length_per_channel; + if (channel == 0) { + assert(output->Empty()); // Output should be empty at this point. + output->AssertSize(output_length); + } else { + assert(output->Size() == output_length); + } + memcpy(&(*output)[channel][0], temp_data, + sizeof(temp_data[0]) * output_length); + } + + // Copy back the first part of the data to |sync_buffer_| and remove it from + // |output|. + sync_buffer_->ReplaceAtIndex(*output, old_length, sync_buffer_->next_index()); + output->PopFront(old_length); + + // Return new added length. |old_length| samples were borrowed from + // |sync_buffer_|. + return static_cast(output_length) - old_length; +} + +int Merge::GetExpandedSignal(int* old_length, int* expand_period) { + // Check how much data that is left since earlier. + *old_length = static_cast(sync_buffer_->FutureLength()); + // Should never be less than overlap_length. + assert(*old_length >= static_cast(expand_->overlap_length())); + // Generate data to merge the overlap with using expand. + expand_->SetParametersForMergeAfterExpand(); + + if (*old_length >= 210 * kMaxSampleRate / 8000) { + // TODO(hlundin): Write test case for this. + // The number of samples available in the sync buffer is more than what fits + // in expanded_signal. Keep the first 210 * kMaxSampleRate / 8000 samples, + // but shift them towards the end of the buffer. This is ok, since all of + // the buffer will be expand data anyway, so as long as the beginning is + // left untouched, we're fine. + int16_t length_diff = *old_length - 210 * kMaxSampleRate / 8000; + sync_buffer_->InsertZerosAtIndex(length_diff, sync_buffer_->next_index()); + *old_length = 210 * kMaxSampleRate / 8000; + // This is the truncated length. + } + // This assert should always be true thanks to the if statement above. + assert(210 * kMaxSampleRate / 8000 - *old_length >= 0); + + AudioMultiVector expanded_temp(num_channels_); + expand_->Process(&expanded_temp); + *expand_period = static_cast(expanded_temp.Size()); // Samples per + // channel. + + expanded_.Clear(); + // Copy what is left since earlier into the expanded vector. + expanded_.PushBackFromIndex(*sync_buffer_, sync_buffer_->next_index()); + assert(expanded_.Size() == static_cast(*old_length)); + assert(expanded_temp.Size() > 0); + // Do "ugly" copy and paste from the expanded in order to generate more data + // to correlate (but not interpolate) with. + const int required_length = (120 + 80 + 2) * fs_mult_; + if (expanded_.Size() < static_cast(required_length)) { + while (expanded_.Size() < static_cast(required_length)) { + // Append one more pitch period each time. + expanded_.PushBack(expanded_temp); + } + // Trim the length to exactly |required_length|. + expanded_.PopBack(expanded_.Size() - required_length); + } + assert(expanded_.Size() >= static_cast(required_length)); + return required_length; +} + +int16_t Merge::SignalScaling(const int16_t* input, int input_length, + const int16_t* expanded_signal, + int16_t* expanded_max, int16_t* input_max) const { + // Adjust muting factor if new vector is more or less of the BGN energy. + const int mod_input_length = std::min(64 * fs_mult_, input_length); + *expanded_max = WebRtcSpl_MaxAbsValueW16(expanded_signal, mod_input_length); + *input_max = WebRtcSpl_MaxAbsValueW16(input, mod_input_length); + + // Calculate energy of expanded signal. + // |log_fs_mult| is log2(fs_mult_), but is not exact for 48000 Hz. + int log_fs_mult = 30 - WebRtcSpl_NormW32(fs_mult_); + int expanded_shift = 6 + log_fs_mult + - WebRtcSpl_NormW32(*expanded_max * *expanded_max); + expanded_shift = std::max(expanded_shift, 0); + int32_t energy_expanded = WebRtcSpl_DotProductWithScale(expanded_signal, + expanded_signal, + mod_input_length, + expanded_shift); + + // Calculate energy of input signal. + int input_shift = 6 + log_fs_mult - + WebRtcSpl_NormW32(*input_max * *input_max); + input_shift = std::max(input_shift, 0); + int32_t energy_input = WebRtcSpl_DotProductWithScale(input, input, + mod_input_length, + input_shift); + + // Align to the same Q-domain. + if (input_shift > expanded_shift) { + energy_expanded = energy_expanded >> (input_shift - expanded_shift); + } else { + energy_input = energy_input >> (expanded_shift - input_shift); + } + + // Calculate muting factor to use for new frame. + int16_t mute_factor; + if (energy_input > energy_expanded) { + // Normalize |energy_input| to 14 bits. + int16_t temp_shift = WebRtcSpl_NormW32(energy_input) - 17; + energy_input = WEBRTC_SPL_SHIFT_W32(energy_input, temp_shift); + // Put |energy_expanded| in a domain 14 higher, so that + // energy_expanded / energy_input is in Q14. + energy_expanded = WEBRTC_SPL_SHIFT_W32(energy_expanded, temp_shift + 14); + // Calculate sqrt(energy_expanded / energy_input) in Q14. + mute_factor = WebRtcSpl_SqrtFloor((energy_expanded / energy_input) << 14); + } else { + // Set to 1 (in Q14) when |expanded| has higher energy than |input|. + mute_factor = 16384; + } + + return mute_factor; +} + +// TODO(hlundin): There are some parameter values in this method that seem +// strange. Compare with Expand::Correlation. +void Merge::Downsample(const int16_t* input, int input_length, + const int16_t* expanded_signal, int expanded_length) { + const int16_t* filter_coefficients; + int num_coefficients; + int decimation_factor = fs_hz_ / 4000; + static const int kCompensateDelay = 0; + int length_limit = fs_hz_ / 100; // 10 ms in samples. + if (fs_hz_ == 8000) { + filter_coefficients = DspHelper::kDownsample8kHzTbl; + num_coefficients = 3; + } else if (fs_hz_ == 16000) { + filter_coefficients = DspHelper::kDownsample16kHzTbl; + num_coefficients = 5; + } else if (fs_hz_ == 32000) { + filter_coefficients = DspHelper::kDownsample32kHzTbl; + num_coefficients = 7; + } else { // fs_hz_ == 48000 + filter_coefficients = DspHelper::kDownsample48kHzTbl; + num_coefficients = 7; + } + int signal_offset = num_coefficients - 1; + WebRtcSpl_DownsampleFast(&expanded_signal[signal_offset], + expanded_length - signal_offset, + expanded_downsampled_, kExpandDownsampLength, + filter_coefficients, num_coefficients, + decimation_factor, kCompensateDelay); + if (input_length <= length_limit) { + // Not quite long enough, so we have to cheat a bit. + int16_t temp_len = input_length - signal_offset; + // TODO(hlundin): Should |downsamp_temp_len| be corrected for round-off + // errors? I.e., (temp_len + decimation_factor - 1) / decimation_factor? + int16_t downsamp_temp_len = temp_len / decimation_factor; + WebRtcSpl_DownsampleFast(&input[signal_offset], temp_len, + input_downsampled_, downsamp_temp_len, + filter_coefficients, num_coefficients, + decimation_factor, kCompensateDelay); + memset(&input_downsampled_[downsamp_temp_len], 0, + sizeof(int16_t) * (kInputDownsampLength - downsamp_temp_len)); + } else { + WebRtcSpl_DownsampleFast(&input[signal_offset], + input_length - signal_offset, input_downsampled_, + kInputDownsampLength, filter_coefficients, + num_coefficients, decimation_factor, + kCompensateDelay); + } +} + +int16_t Merge::CorrelateAndPeakSearch(int16_t expanded_max, int16_t input_max, + int start_position, int input_length, + int expand_period) const { + // Calculate correlation without any normalization. + const int max_corr_length = kMaxCorrelationLength; + int stop_position_downsamp = std::min( + max_corr_length, expand_->max_lag() / (fs_mult_ * 2) + 1); + int16_t correlation_shift = 0; + if (expanded_max * input_max > 26843546) { + correlation_shift = 3; + } + + int32_t correlation[kMaxCorrelationLength]; + WebRtcSpl_CrossCorrelation(correlation, input_downsampled_, + expanded_downsampled_, kInputDownsampLength, + stop_position_downsamp, correlation_shift, 1); + + // Normalize correlation to 14 bits and copy to a 16-bit array. + const int pad_length = static_cast(expand_->overlap_length() - 1); + const int correlation_buffer_size = 2 * pad_length + kMaxCorrelationLength; + scoped_ptr correlation16(new int16_t[correlation_buffer_size]); + memset(correlation16.get(), 0, correlation_buffer_size * sizeof(int16_t)); + int16_t* correlation_ptr = &correlation16[pad_length]; + int32_t max_correlation = WebRtcSpl_MaxAbsValueW32(correlation, + stop_position_downsamp); + int16_t norm_shift = std::max(0, 17 - WebRtcSpl_NormW32(max_correlation)); + WebRtcSpl_VectorBitShiftW32ToW16(correlation_ptr, stop_position_downsamp, + correlation, norm_shift); + + // Calculate allowed starting point for peak finding. + // The peak location bestIndex must fulfill two criteria: + // (1) w16_bestIndex + input_length < + // timestamps_per_call_ + expand_->overlap_length(); + // (2) w16_bestIndex + input_length < start_position. + int start_index = timestamps_per_call_ + + static_cast(expand_->overlap_length()); + start_index = std::max(start_position, start_index); + start_index = std::max(start_index - input_length, 0); + // Downscale starting index to 4kHz domain. (fs_mult_ * 2 = fs_hz_ / 4000.) + int start_index_downsamp = start_index / (fs_mult_ * 2); + + // Calculate a modified |stop_position_downsamp| to account for the increased + // start index |start_index_downsamp| and the effective array length. + int modified_stop_pos = + std::min(stop_position_downsamp, + kMaxCorrelationLength + pad_length - start_index_downsamp); + int best_correlation_index; + int16_t best_correlation; + static const int kNumCorrelationCandidates = 1; + DspHelper::PeakDetection(&correlation_ptr[start_index_downsamp], + modified_stop_pos, kNumCorrelationCandidates, + fs_mult_, &best_correlation_index, + &best_correlation); + // Compensate for modified start index. + best_correlation_index += start_index; + + // Ensure that underrun does not occur for 10ms case => we have to get at + // least 10ms + overlap . (This should never happen thanks to the above + // modification of peak-finding starting point.) + while ((best_correlation_index + input_length) < + static_cast(timestamps_per_call_ + expand_->overlap_length()) || + best_correlation_index + input_length < start_position) { + assert(false); // Should never happen. + best_correlation_index += expand_period; // Jump one lag ahead. + } + return best_correlation_index; +} + +int Merge::RequiredFutureSamples() { + return static_cast(fs_hz_ / 100 * num_channels_); // 10 ms. +} + + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/merge.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/merge.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/merge.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/merge.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_MERGE_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_MERGE_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Forward declarations. +class Expand; +class SyncBuffer; + +// This class handles the transition from expansion to normal operation. +// When a packet is not available for decoding when needed, the expand operation +// is called to generate extrapolation data. If the missing packet arrives, +// i.e., it was just delayed, it can be decoded and appended directly to the +// end of the expanded data (thanks to how the Expand class operates). However, +// if a later packet arrives instead, the loss is a fact, and the new data must +// be stitched together with the end of the expanded data. This stitching is +// what the Merge class does. +class Merge { + public: + Merge(int fs_hz, size_t num_channels, Expand* expand, SyncBuffer* sync_buffer) + : fs_hz_(fs_hz), + num_channels_(num_channels), + fs_mult_(fs_hz_ / 8000), + timestamps_per_call_(fs_hz_ / 100), + expand_(expand), + sync_buffer_(sync_buffer), + expanded_(num_channels_) { + assert(num_channels_ > 0); + } + + virtual ~Merge() {} + + // The main method to produce the audio data. The decoded data is supplied in + // |input|, having |input_length| samples in total for all channels + // (interleaved). The result is written to |output|. The number of channels + // allocated in |output| defines the number of channels that will be used when + // de-interleaving |input|. The values in |external_mute_factor_array| (Q14) + // will be used to scale the audio, and is updated in the process. The array + // must have |num_channels_| elements. + virtual int Process(int16_t* input, size_t input_length, + int16_t* external_mute_factor_array, + AudioMultiVector* output); + + virtual int RequiredFutureSamples(); + + protected: + const int fs_hz_; + const size_t num_channels_; + + private: + static const int kMaxSampleRate = 48000; + static const int kExpandDownsampLength = 100; + static const int kInputDownsampLength = 40; + static const int kMaxCorrelationLength = 60; + + // Calls |expand_| to get more expansion data to merge with. The data is + // written to |expanded_signal_|. Returns the length of the expanded data, + // while |expand_period| will be the number of samples in one expansion period + // (typically one pitch period). The value of |old_length| will be the number + // of samples that were taken from the |sync_buffer_|. + int GetExpandedSignal(int* old_length, int* expand_period); + + // Analyzes |input| and |expanded_signal| to find maximum values. Returns + // a muting factor (Q14) to be used on the new data. + int16_t SignalScaling(const int16_t* input, int input_length, + const int16_t* expanded_signal, + int16_t* expanded_max, int16_t* input_max) const; + + // Downsamples |input| (|input_length| samples) and |expanded_signal| to + // 4 kHz sample rate. The downsampled signals are written to + // |input_downsampled_| and |expanded_downsampled_|, respectively. + void Downsample(const int16_t* input, int input_length, + const int16_t* expanded_signal, int expanded_length); + + // Calculates cross-correlation between |input_downsampled_| and + // |expanded_downsampled_|, and finds the correlation maximum. The maximizing + // lag is returned. + int16_t CorrelateAndPeakSearch(int16_t expanded_max, int16_t input_max, + int start_position, int input_length, + int expand_period) const; + + const int fs_mult_; // fs_hz_ / 8000. + const int timestamps_per_call_; + Expand* expand_; + SyncBuffer* sync_buffer_; + int16_t expanded_downsampled_[kExpandDownsampLength]; + int16_t input_downsampled_[kInputDownsampLength]; + AudioMultiVector expanded_; + + DISALLOW_COPY_AND_ASSIGN(Merge); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_MERGE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/merge_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/merge_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/merge_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/merge_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for Merge class. + +#include "webrtc/modules/audio_coding/neteq/merge.h" + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/neteq/background_noise.h" +#include "webrtc/modules/audio_coding/neteq/expand.h" +#include "webrtc/modules/audio_coding/neteq/random_vector.h" +#include "webrtc/modules/audio_coding/neteq/sync_buffer.h" + +namespace webrtc { + +TEST(Merge, CreateAndDestroy) { + int fs = 8000; + size_t channels = 1; + BackgroundNoise bgn(channels); + SyncBuffer sync_buffer(1, 1000); + RandomVector random_vector; + Expand expand(&bgn, &sync_buffer, &random_vector, fs, channels); + Merge merge(fs, channels, &expand, &sync_buffer); +} + +// TODO(hlundin): Write more tests. + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/min_distortion.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/min_distortion.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/min_distortion.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/min_distortion.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Calculate best overlap fit according to distortion measure. - */ - -#include "dsp_helpfunctions.h" - -#include "signal_processing_library.h" - -int16_t WebRtcNetEQ_MinDistortion(const int16_t *pw16_data, - int16_t w16_minLag, int16_t w16_maxLag, - int16_t len, int32_t *pw16_dist) -{ - int i, j; - const int16_t *pw16_data1; - const int16_t *pw16_data2; - int32_t w32_diff; - int32_t w32_sumdiff; - int16_t bestIndex = -1; - int32_t minDist = WEBRTC_SPL_WORD32_MAX; - - for (i = w16_minLag; i <= w16_maxLag; i++) - { - w32_sumdiff = 0; - pw16_data1 = pw16_data; - pw16_data2 = pw16_data - i; - - for (j = 0; j < len; j++) - { - w32_diff = pw16_data1[j] - pw16_data2[j]; - w32_sumdiff += WEBRTC_SPL_ABS_W32(w32_diff); - } - - /* Compare with previous minimum */ - if (w32_sumdiff < minDist) - { - minDist = w32_sumdiff; - bestIndex = i; - } - } - - *pw16_dist = minDist; - - return bestIndex; -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mix_voice_unvoice.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mix_voice_unvoice.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mix_voice_unvoice.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mix_voice_unvoice.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This function mixes a voiced signal with an unvoiced signal and - * updates the weight on a sample by sample basis. - */ - -#include "dsp_helpfunctions.h" - -#include "signal_processing_library.h" - -void WebRtcNetEQ_MixVoiceUnvoice(int16_t *pw16_outData, int16_t *pw16_voicedVec, - int16_t *pw16_unvoicedVec, - int16_t *w16_current_vfraction, - int16_t w16_vfraction_change, int16_t N) -{ - int i; - int16_t w16_tmp2; - int16_t vfraction = *w16_current_vfraction; - - w16_tmp2 = 16384 - vfraction; - for (i = 0; i < N; i++) - { - pw16_outData[i] = (int16_t) WEBRTC_SPL_RSHIFT_W32( - WEBRTC_SPL_MUL_16_16(vfraction, pw16_voicedVec[i]) + - WEBRTC_SPL_MUL_16_16(w16_tmp2, pw16_unvoicedVec[i]) + 8192, - 14); - vfraction -= w16_vfraction_change; - w16_tmp2 += w16_vfraction_change; - } - *w16_current_vfraction = vfraction; -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_audio_decoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_audio_decoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_audio_decoder.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_audio_decoder.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_AUDIO_DECODER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_AUDIO_DECODER_H_ + +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" + +#include "testing/gmock/include/gmock/gmock.h" + +namespace webrtc { + +class MockAudioDecoder : public AudioDecoder { + public: + MockAudioDecoder() {} + virtual ~MockAudioDecoder() { Die(); } + MOCK_METHOD0(Die, void()); + MOCK_METHOD4(Decode, int(const uint8_t*, size_t, int16_t*, + AudioDecoder::SpeechType*)); + MOCK_CONST_METHOD0(HasDecodePlc, bool()); + MOCK_METHOD2(DecodePlc, int(int, int16_t*)); + MOCK_METHOD0(Init, int()); + MOCK_METHOD5(IncomingPacket, int(const uint8_t*, size_t, uint16_t, uint32_t, + uint32_t)); + MOCK_METHOD0(ErrorCode, int()); + MOCK_CONST_METHOD0(codec_type, NetEqDecoder()); + MOCK_METHOD1(CodecSupported, bool(NetEqDecoder)); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_AUDIO_DECODER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_audio_vector.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_audio_vector.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_audio_vector.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_audio_vector.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_AUDIO_VECTOR_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_AUDIO_VECTOR_H_ + +#include "webrtc/modules/audio_coding/neteq/audio_vector.h" + +#include "testing/gmock/include/gmock/gmock.h" + +namespace webrtc { + +class MockAudioVector : public AudioVector { + public: + MOCK_METHOD0(Clear, + void()); + MOCK_CONST_METHOD1(CopyFrom, + void(AudioVector* copy_to)); + MOCK_METHOD1(PushFront, + void(const AudioVector& prepend_this)); + MOCK_METHOD2(PushFront, + void(const T* prepend_this, size_t length)); + MOCK_METHOD1(PushBack, + void(const AudioVector& append_this)); + MOCK_METHOD2(PushBack, + void(const T* append_this, size_t length)); + MOCK_METHOD1(PopFront, + void(size_t length)); + MOCK_METHOD1(PopBack, + void(size_t length)); + MOCK_METHOD1(Extend, + void(size_t extra_length)); + MOCK_METHOD3(InsertAt, + void(const T* insert_this, size_t length, size_t position)); + MOCK_METHOD3(OverwriteAt, + void(const T* insert_this, size_t length, size_t position)); + MOCK_CONST_METHOD0(Size, + size_t()); + MOCK_CONST_METHOD0(Empty, + bool()); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_AUDIO_VECTOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_buffer_level_filter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_buffer_level_filter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_buffer_level_filter.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_buffer_level_filter.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_BUFFER_LEVEL_FILTER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_BUFFER_LEVEL_FILTER_H_ + +#include "webrtc/modules/audio_coding/neteq/buffer_level_filter.h" + +#include "testing/gmock/include/gmock/gmock.h" + +namespace webrtc { + +class MockBufferLevelFilter : public BufferLevelFilter { + public: + virtual ~MockBufferLevelFilter() { Die(); } + MOCK_METHOD0(Die, + void()); + MOCK_METHOD0(Reset, + void()); + MOCK_METHOD3(Update, + void(int buffer_size_packets, int time_stretched_samples, + int packet_len_samples)); + MOCK_METHOD1(SetTargetBufferLevel, + void(int target_buffer_level)); + MOCK_CONST_METHOD0(filtered_current_level, + int()); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_BUFFER_LEVEL_FILTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_decoder_database.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_decoder_database.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_decoder_database.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_decoder_database.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_DECODER_DATABASE_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_DECODER_DATABASE_H_ + +#include "webrtc/modules/audio_coding/neteq/decoder_database.h" + +#include "testing/gmock/include/gmock/gmock.h" + +namespace webrtc { + +class MockDecoderDatabase : public DecoderDatabase { + public: + virtual ~MockDecoderDatabase() { Die(); } + MOCK_METHOD0(Die, void()); + MOCK_CONST_METHOD0(Empty, + bool()); + MOCK_CONST_METHOD0(Size, + int()); + MOCK_METHOD0(Reset, + void()); + MOCK_METHOD2(RegisterPayload, + int(uint8_t rtp_payload_type, NetEqDecoder codec_type)); + MOCK_METHOD4(InsertExternal, + int(uint8_t rtp_payload_type, NetEqDecoder codec_type, int fs_hz, + AudioDecoder* decoder)); + MOCK_METHOD1(Remove, + int(uint8_t rtp_payload_type)); + MOCK_CONST_METHOD1(GetDecoderInfo, + const DecoderInfo*(uint8_t rtp_payload_type)); + MOCK_CONST_METHOD1(GetRtpPayloadType, + uint8_t(NetEqDecoder codec_type)); + MOCK_METHOD1(GetDecoder, + AudioDecoder*(uint8_t rtp_payload_type)); + MOCK_CONST_METHOD2(IsType, + bool(uint8_t rtp_payload_type, NetEqDecoder codec_type)); + MOCK_CONST_METHOD1(IsComfortNoise, + bool(uint8_t rtp_payload_type)); + MOCK_CONST_METHOD1(IsDtmf, + bool(uint8_t rtp_payload_type)); + MOCK_CONST_METHOD1(IsRed, + bool(uint8_t rtp_payload_type)); + MOCK_METHOD2(SetActiveDecoder, + int(uint8_t rtp_payload_type, bool* new_decoder)); + MOCK_METHOD0(GetActiveDecoder, + AudioDecoder*()); + MOCK_METHOD1(SetActiveCngDecoder, + int(uint8_t rtp_payload_type)); + MOCK_METHOD0(GetActiveCngDecoder, + AudioDecoder*()); + MOCK_CONST_METHOD1(CheckPayloadTypes, + int(const PacketList& packet_list)); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_DECODER_DATABASE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_delay_manager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_delay_manager.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_delay_manager.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_delay_manager.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_DELAY_MANAGER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_DELAY_MANAGER_H_ + +#include "webrtc/modules/audio_coding/neteq/delay_manager.h" + +#include "testing/gmock/include/gmock/gmock.h" + +namespace webrtc { + +class MockDelayManager : public DelayManager { + public: + MockDelayManager(int max_packets_in_buffer, DelayPeakDetector* peak_detector) + : DelayManager(max_packets_in_buffer, peak_detector) {} + virtual ~MockDelayManager() { Die(); } + MOCK_METHOD0(Die, void()); + MOCK_CONST_METHOD0(iat_vector, + const IATVector&()); + MOCK_METHOD3(Update, + int(uint16_t sequence_number, uint32_t timestamp, int sample_rate_hz)); + MOCK_METHOD1(CalculateTargetLevel, + int(int iat_packets)); + MOCK_METHOD1(SetPacketAudioLength, + int(int length_ms)); + MOCK_METHOD0(Reset, + void()); + MOCK_CONST_METHOD0(AverageIAT, + int()); + MOCK_CONST_METHOD0(PeakFound, + bool()); + MOCK_METHOD1(UpdateCounters, + void(int elapsed_time_ms)); + MOCK_METHOD0(ResetPacketIatCount, + void()); + MOCK_CONST_METHOD2(BufferLimits, + void(int* lower_limit, int* higher_limit)); + MOCK_CONST_METHOD0(TargetLevel, + int()); + MOCK_METHOD1(LastDecoderType, + void(NetEqDecoder decoder_type)); + MOCK_METHOD1(set_extra_delay_ms, + void(int16_t delay)); + MOCK_CONST_METHOD0(base_target_level, + int()); + MOCK_METHOD1(set_streaming_mode, + void(bool value)); + MOCK_CONST_METHOD0(last_pack_cng_or_dtmf, + int()); + MOCK_METHOD1(set_last_pack_cng_or_dtmf, + void(int value)); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_DELAY_MANAGER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_delay_peak_detector.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_delay_peak_detector.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_delay_peak_detector.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_delay_peak_detector.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_DELAY_PEAK_DETECTOR_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_DELAY_PEAK_DETECTOR_H_ + +#include "webrtc/modules/audio_coding/neteq/delay_peak_detector.h" + +#include "testing/gmock/include/gmock/gmock.h" + +namespace webrtc { + +class MockDelayPeakDetector : public DelayPeakDetector { + public: + virtual ~MockDelayPeakDetector() { Die(); } + MOCK_METHOD0(Die, void()); + MOCK_METHOD0(Reset, void()); + MOCK_METHOD1(SetPacketAudioLength, void(int length_ms)); + MOCK_METHOD0(peak_found, bool()); + MOCK_CONST_METHOD0(MaxPeakHeight, int()); + MOCK_CONST_METHOD0(MaxPeakPeriod, int()); + MOCK_METHOD2(Update, bool(int inter_arrival_time, int target_level)); + MOCK_METHOD1(IncrementCounter, void(int inc_ms)); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_DELAY_PEAK_DETECTOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_dtmf_buffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_dtmf_buffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_dtmf_buffer.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_dtmf_buffer.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_DTMF_BUFFER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_DTMF_BUFFER_H_ + +#include "webrtc/modules/audio_coding/neteq/dtmf_buffer.h" + +#include "testing/gmock/include/gmock/gmock.h" + +namespace webrtc { + +class MockDtmfBuffer : public DtmfBuffer { + public: + MockDtmfBuffer(int fs) : DtmfBuffer(fs) {} + virtual ~MockDtmfBuffer() { Die(); } + MOCK_METHOD0(Die, void()); + MOCK_METHOD0(Flush, + void()); + MOCK_METHOD1(InsertEvent, + int(const DtmfEvent& event)); + MOCK_METHOD2(GetEvent, + bool(uint32_t current_timestamp, DtmfEvent* event)); + MOCK_CONST_METHOD0(Length, + size_t()); + MOCK_CONST_METHOD0(Empty, + bool()); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_DTMF_BUFFER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_dtmf_tone_generator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_dtmf_tone_generator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_dtmf_tone_generator.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_dtmf_tone_generator.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_DTMF_TONE_GENERATOR_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_DTMF_TONE_GENERATOR_H_ + +#include "webrtc/modules/audio_coding/neteq/dtmf_tone_generator.h" + +#include "testing/gmock/include/gmock/gmock.h" + +namespace webrtc { + +class MockDtmfToneGenerator : public DtmfToneGenerator { + public: + virtual ~MockDtmfToneGenerator() { Die(); } + MOCK_METHOD0(Die, void()); + MOCK_METHOD3(Init, + int(int fs, int event, int attenuation)); + MOCK_METHOD0(Reset, + void()); + MOCK_METHOD2(Generate, + int(int num_samples, AudioMultiVector* output)); + MOCK_CONST_METHOD0(initialized, + bool()); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_DTMF_TONE_GENERATOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_expand.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_expand.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_expand.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_expand.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_EXPAND_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_EXPAND_H_ + +#include "webrtc/modules/audio_coding/neteq/expand.h" + +#include "testing/gmock/include/gmock/gmock.h" + +namespace webrtc { + +class MockExpand : public Expand { + public: + MockExpand(BackgroundNoise* background_noise, + SyncBuffer* sync_buffer, + RandomVector* random_vector, + int fs, + size_t num_channels) + : Expand(background_noise, sync_buffer, random_vector, fs, num_channels) { + } + virtual ~MockExpand() { Die(); } + MOCK_METHOD0(Die, void()); + MOCK_METHOD0(Reset, + void()); + MOCK_METHOD1(Process, + int(AudioMultiVector* output)); + MOCK_METHOD0(SetParametersForNormalAfterExpand, + void()); + MOCK_METHOD0(SetParametersForMergeAfterExpand, + void()); + MOCK_CONST_METHOD0(overlap_length, + size_t()); +}; + +} // namespace webrtc + +namespace webrtc { + +class MockExpandFactory : public ExpandFactory { + public: + MOCK_CONST_METHOD5(Create, + Expand*(BackgroundNoise* background_noise, + SyncBuffer* sync_buffer, + RandomVector* random_vector, + int fs, + size_t num_channels)); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_EXPAND_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_external_decoder_pcm16b.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_external_decoder_pcm16b.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_external_decoder_pcm16b.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_external_decoder_pcm16b.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_EXTERNAL_DECODER_PCM16B_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_EXTERNAL_DECODER_PCM16B_H_ + +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +using ::testing::_; +using ::testing::Invoke; + +// Implement an external version of the PCM16b decoder. This is a copy from +// audio_decoder_impl.{cc, h}. +class ExternalPcm16B : public AudioDecoder { + public: + ExternalPcm16B() {} + + virtual int Decode(const uint8_t* encoded, size_t encoded_len, + int16_t* decoded, SpeechType* speech_type) { + int16_t temp_type; + int16_t ret = WebRtcPcm16b_DecodeW16( + reinterpret_cast(const_cast(encoded)), + static_cast(encoded_len), decoded, &temp_type); + *speech_type = ConvertSpeechType(temp_type); + return ret; + } + + virtual int Init() { return 0; } + + private: + DISALLOW_COPY_AND_ASSIGN(ExternalPcm16B); +}; + +// Create a mock of ExternalPcm16B which delegates all calls to the real object. +// The reason is that we can then track that the correct calls are being made. +class MockExternalPcm16B : public ExternalPcm16B { + public: + MockExternalPcm16B() { + // By default, all calls are delegated to the real object. + ON_CALL(*this, Decode(_, _, _, _)) + .WillByDefault(Invoke(&real_, &ExternalPcm16B::Decode)); + ON_CALL(*this, HasDecodePlc()) + .WillByDefault(Invoke(&real_, &ExternalPcm16B::HasDecodePlc)); + ON_CALL(*this, DecodePlc(_, _)) + .WillByDefault(Invoke(&real_, &ExternalPcm16B::DecodePlc)); + ON_CALL(*this, Init()) + .WillByDefault(Invoke(&real_, &ExternalPcm16B::Init)); + ON_CALL(*this, IncomingPacket(_, _, _, _, _)) + .WillByDefault(Invoke(&real_, &ExternalPcm16B::IncomingPacket)); + ON_CALL(*this, ErrorCode()) + .WillByDefault(Invoke(&real_, &ExternalPcm16B::ErrorCode)); + } + virtual ~MockExternalPcm16B() { Die(); } + + MOCK_METHOD0(Die, void()); + MOCK_METHOD4(Decode, + int(const uint8_t* encoded, size_t encoded_len, int16_t* decoded, + SpeechType* speech_type)); + MOCK_CONST_METHOD0(HasDecodePlc, + bool()); + MOCK_METHOD2(DecodePlc, + int(int num_frames, int16_t* decoded)); + MOCK_METHOD0(Init, + int()); + MOCK_METHOD5(IncomingPacket, + int(const uint8_t* payload, size_t payload_len, + uint16_t rtp_sequence_number, uint32_t rtp_timestamp, + uint32_t arrival_timestamp)); + MOCK_METHOD0(ErrorCode, + int()); + MOCK_CONST_METHOD0(codec_type, + NetEqDecoder()); + + private: + ExternalPcm16B real_; +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_EXTERNAL_DECODER_PCM16B_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_PACKET_BUFFER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_PACKET_BUFFER_H_ + +#include "webrtc/modules/audio_coding/neteq/packet_buffer.h" + +#include "testing/gmock/include/gmock/gmock.h" + +namespace webrtc { + +class MockPacketBuffer : public PacketBuffer { + public: + MockPacketBuffer(size_t max_number_of_packets) + : PacketBuffer(max_number_of_packets) {} + virtual ~MockPacketBuffer() { Die(); } + MOCK_METHOD0(Die, void()); + MOCK_METHOD0(Flush, + void()); + MOCK_CONST_METHOD0(Empty, + bool()); + MOCK_METHOD1(InsertPacket, + int(Packet* packet)); + MOCK_METHOD4(InsertPacketList, + int(PacketList* packet_list, + const DecoderDatabase& decoder_database, + uint8_t* current_rtp_payload_type, + uint8_t* current_cng_rtp_payload_type)); + MOCK_CONST_METHOD1(NextTimestamp, + int(uint32_t* next_timestamp)); + MOCK_CONST_METHOD2(NextHigherTimestamp, + int(uint32_t timestamp, uint32_t* next_timestamp)); + MOCK_CONST_METHOD0(NextRtpHeader, + const RTPHeader*()); + MOCK_METHOD1(GetNextPacket, + Packet*(int* discard_count)); + MOCK_METHOD0(DiscardNextPacket, + int()); + MOCK_METHOD2(DiscardOldPackets, + int(uint32_t timestamp_limit, uint32_t horizon_samples)); + MOCK_METHOD1(DiscardAllOldPackets, + int(uint32_t timestamp_limit)); + MOCK_CONST_METHOD0(NumPacketsInBuffer, + int()); + MOCK_METHOD1(IncrementWaitingTimes, + void(int)); + MOCK_CONST_METHOD0(current_memory_bytes, + int()); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_PACKET_BUFFER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_payload_splitter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_payload_splitter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_payload_splitter.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mock/mock_payload_splitter.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_PAYLOAD_SPLITTER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_PAYLOAD_SPLITTER_H_ + +#include "webrtc/modules/audio_coding/neteq/payload_splitter.h" + +#include "testing/gmock/include/gmock/gmock.h" + +namespace webrtc { + +class MockPayloadSplitter : public PayloadSplitter { + public: + MOCK_METHOD1(SplitRed, + int(PacketList* packet_list)); + MOCK_METHOD2(SplitFec, + int(PacketList* packet_list, DecoderDatabase* decoder_database)); + MOCK_METHOD2(CheckRedPayloads, + int(PacketList* packet_list, const DecoderDatabase& decoder_database)); + MOCK_METHOD2(SplitAudio, + int(PacketList* packet_list, const DecoderDatabase& decoder_database)); + MOCK_METHOD4(SplitBySamples, + void(const Packet* packet, int bytes_per_ms, int timestamps_per_ms, + PacketList* new_packets)); + MOCK_METHOD4(SplitByFrames, + int(const Packet* packet, int bytes_per_frame, int timestamps_per_frame, + PacketList* new_packets)); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_PAYLOAD_SPLITTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mute_signal.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mute_signal.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mute_signal.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/mute_signal.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This function mutes a signal linearly on a sample by sample basis. - */ - -#include "dsp_helpfunctions.h" - -#include "signal_processing_library.h" - -void WebRtcNetEQ_MuteSignal(int16_t *pw16_inout, int16_t muteSlope, - int16_t N) -{ - int i; - int32_t w32_tmp = 1048608; /* (16384<<6 + 32) */ - - for (i = 0; i < N; i++) - { - pw16_inout[i] - = (int16_t) ((WEBRTC_SPL_MUL_16_16((int16_t)(w32_tmp>>6), pw16_inout[i]) - + 8192) >> 14); - w32_tmp -= muteSlope; - } -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" + +#include "webrtc/modules/audio_coding/neteq/accelerate.h" +#include "webrtc/modules/audio_coding/neteq/buffer_level_filter.h" +#include "webrtc/modules/audio_coding/neteq/decoder_database.h" +#include "webrtc/modules/audio_coding/neteq/delay_manager.h" +#include "webrtc/modules/audio_coding/neteq/delay_peak_detector.h" +#include "webrtc/modules/audio_coding/neteq/dtmf_buffer.h" +#include "webrtc/modules/audio_coding/neteq/dtmf_tone_generator.h" +#include "webrtc/modules/audio_coding/neteq/expand.h" +#include "webrtc/modules/audio_coding/neteq/neteq_impl.h" +#include "webrtc/modules/audio_coding/neteq/packet_buffer.h" +#include "webrtc/modules/audio_coding/neteq/payload_splitter.h" +#include "webrtc/modules/audio_coding/neteq/preemptive_expand.h" +#include "webrtc/modules/audio_coding/neteq/timestamp_scaler.h" + +namespace webrtc { + +// Creates all classes needed and inject them into a new NetEqImpl object. +// Return the new object. +NetEq* NetEq::Create(const NetEq::Config& config) { + BufferLevelFilter* buffer_level_filter = new BufferLevelFilter; + DecoderDatabase* decoder_database = new DecoderDatabase; + DelayPeakDetector* delay_peak_detector = new DelayPeakDetector; + DelayManager* delay_manager = + new DelayManager(config.max_packets_in_buffer, delay_peak_detector); + delay_manager->SetMaximumDelay(config.max_delay_ms); + DtmfBuffer* dtmf_buffer = new DtmfBuffer(config.sample_rate_hz); + DtmfToneGenerator* dtmf_tone_generator = new DtmfToneGenerator; + PacketBuffer* packet_buffer = new PacketBuffer(config.max_packets_in_buffer); + PayloadSplitter* payload_splitter = new PayloadSplitter; + TimestampScaler* timestamp_scaler = new TimestampScaler(*decoder_database); + AccelerateFactory* accelerate_factory = new AccelerateFactory; + ExpandFactory* expand_factory = new ExpandFactory; + PreemptiveExpandFactory* preemptive_expand_factory = + new PreemptiveExpandFactory; + return new NetEqImpl(config, + buffer_level_filter, + decoder_database, + delay_manager, + delay_peak_detector, + dtmf_buffer, + dtmf_tone_generator, + packet_buffer, + payload_splitter, + timestamp_scaler, + accelerate_factory, + expand_factory, + preemptive_expand_factory); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_defines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_defines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_defines.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_defines.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,379 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/***************************************************************************************** - * - * Compilation flags in NetEQ: - * - ***************************************************************************************** - * - ***** Platform flags ****** - * - * SCRATCH Run NetEQ with "Scratch memory" to save some stack memory. - * Definition can be used on all platforms - * - ***** Summary flags ****** - * - * NETEQ_ALL_SPECIAL_CODECS Add support for special codecs (CN/RED/DTMF) - * - * NETEQ_ALL_NB_CODECS Add support for all NB codecs (except CN/RED/DTMF) - * - * NETEQ_ALL_WB_CODECS Add support for all WB codecs (except CN/RED/DTMF) - * - * NETEQ_VOICEENGINE_CODECS Support for all NB, WB and SWB32 codecs and CN, RED and DTMF - * - * NETEQ_ALL_CODECS Support for all NB, WB, SWB 32kHz and SWB 48kHz as well as - * CN, RED and DTMF - * - ***** Sampling frequency ****** - * (Note: usually not needed when Summary flags are used) - * - * NETEQ_WIDEBAND Wideband enabled - * - * NETEQ_32KHZ_WIDEBAND Super wideband @ 32kHz enabled - * - * NETEQ_48KHZ_WIDEBAND Super wideband @ 48kHz enabled - * - ***** Special Codec ****** - * (Note: not needed if NETEQ_ALL_CODECS is used) - * - * NETEQ_RED_CODEC With this flag you enable NetEQ to understand redundancy in - * the RTP. NetEQ will use the redundancy if it's the same - * codec - * - * NETEQ_CNG_CODEC Enable DTX with the CN payload - * - * NETEQ_ATEVENT_DECODE Enable AVT event and play out the corresponding DTMF tone - * - ***** Speech Codecs ***** - * (Note: Not needed if Summary flags are used) - * - * NETEQ_G711_CODEC Enable G.711 u- and A-law - * - * NETEQ_PCM16B_CODEC Enable uncompressed 16-bit - * - * NETEQ_ILBC_CODEC Enable iLBC - * - * NETEQ_ISAC_CODEC Enable iSAC - * - * NETEQ_ISAC_SWB_CODEC Enable iSAC-SWB - * - * Note that the decoder of iSAC full-band operates at 32 kHz, that is the - * decoded signal is at 32 kHz. - * NETEQ_ISAC_FB_CODEC Enable iSAC-FB - * - * NETEQ_OPUS_CODEC Enable Opus - * - * NETEQ_G722_CODEC Enable G.722 - * - * NETEQ_G729_CODEC Enable G.729 - * - * NETEQ_G729_1_CODEC Enable G.729.1 - * - * NETEQ_G726_CODEC Enable G.726 - * - * NETEQ_G722_1_CODEC Enable G722.1 - * - * NETEQ_G722_1C_CODEC Enable G722.1 Annex C - * - * NETEQ_OPUS_CODEC Enable Opus - * - * NETEQ_SPEEX_CODEC Enable Speex (at 8 and 16 kHz sample rate) - * - * NETEQ_CELT_CODEC Enable Celt (at 32 kHz sample rate) - * - * NETEQ_GSMFR_CODEC Enable GSM-FR - * - * NETEQ_AMR_CODEC Enable AMR (narrowband) - * - * NETEQ_AMRWB_CODEC Enable AMR-WB - * - * NETEQ_CNG_CODEC Enable DTX with the CNG payload - * - * NETEQ_ATEVENT_DECODE Enable AVT event and play out the corresponding DTMF tone - * - ***** Test flags ****** - * - * WEBRTC_NETEQ_40BITACC_TEST Run NetEQ with simulated 40-bit accumulator to run - * bit-exact to a DSP implementation where the main (splib - * and NetEQ) functions have been 40-bit optimized - * - ***************************************************************************************** - */ - -#if !defined NETEQ_DEFINES_H -#define NETEQ_DEFINES_H - -/* Data block structure for MCU to DSP communication: - * - * - * First 3 16-bit words are pre-header that contains instructions and timestamp update - * Fourth 16-bit word is length of data block 1 - * Rest is payload data - * - * 0 48 64 80 - * -------------...---------------------------------------------------------------------- - * | PreHeader ... | Length 1 | Payload data 1 ...... | Lenght 2| Data block 2.... | ... - * -------------...---------------------------------------------------------------------- - * - * - * Preheader: - * 4 MSB can be either of: - */ - -#define DSP_INSTR_NORMAL 0x1000 -/* Payload data will contain the encoded frames */ - -#define DSP_INSTR_MERGE 0x2000 -/* Payload data block 1 will contain the encoded frame */ -/* Info block will contain the number of missing samples */ - -#define DSP_INSTR_EXPAND 0x3000 -/* Payload data will be empty */ - -#define DSP_INSTR_ACCELERATE 0x4000 -/* Payload data will contain the encoded frame */ - -#define DSP_INSTR_DO_RFC3389CNG 0x5000 -/* Payload data will contain the SID frame if there is one*/ - -#define DSP_INSTR_DTMF_GENERATE 0x6000 -/* Payload data will be one int16_t with the current DTMF value and one - * int16_t with the current volume value - */ -#define DSP_INSTR_NORMAL_ONE_DESC 0x7000 -/* No encoded frames */ - -#define DSP_INSTR_DO_CODEC_INTERNAL_CNG 0x8000 -/* Codec has a built-in VAD/DTX scheme (use the above for "no transmission") */ - -#define DSP_INSTR_PREEMPTIVE_EXPAND 0x9000 -/* Payload data will contain the encoded frames, if any */ - -#define DSP_INSTR_DO_ALTERNATIVE_PLC 0xB000 -/* NetEQ switched off and packet missing... */ - -#define DSP_INSTR_DO_ALTERNATIVE_PLC_INC_TS 0xC000 -/* NetEQ switched off and packet missing... */ - -#define DSP_INSTR_DO_AUDIO_REPETITION 0xD000 -/* NetEQ switched off and packet missing... */ - -#define DSP_INSTR_DO_AUDIO_REPETITION_INC_TS 0xE000 -/* NetEQ switched off and packet missing... */ - -#define DSP_INSTR_FADE_TO_BGN 0xF000 -/* Exception handling: fade out to BGN (expand) */ - -/* - * Next 4 bits signal additional data that needs to be transmitted - */ - -#define DSP_CODEC_NO_CHANGE 0x0100 -#define DSP_CODEC_NEW_CODEC 0x0200 -#define DSP_CODEC_ADD_LATE_PKT 0x0300 -#define DSP_CODEC_RESET 0x0400 -#define DSP_DTMF_PAYLOAD 0x0010 - -/* - * The most significant bit of the payload-length - * is used to flag whether the associated payload - * is redundant payload. This currently useful only for - * iSAC, where redundant payloads have to be treated - * differently. Every time the length is read it must be - * masked by DSP_CODEC_MASK_RED_FLAG to ignore the flag. - * Use DSP_CODEC_RED_FLAG to set or retrieve the flag. - */ -#define DSP_CODEC_MASK_RED_FLAG 0x7FFF -#define DSP_CODEC_RED_FLAG 0x8000 - -/* - * The first block of payload data consist of decode function pointers, - * and then the speech blocks. - * - */ - - -/* - * The playout modes that NetEq produced (i.e. gives more info about if the - * Accelerate was successful or not) - */ - -#define MODE_NORMAL 0x0000 -#define MODE_EXPAND 0x0001 -#define MODE_MERGE 0x0002 -#define MODE_SUCCESS_ACCELERATE 0x0003 -#define MODE_UNSUCCESS_ACCELERATE 0x0004 -#define MODE_RFC3389CNG 0x0005 -#define MODE_LOWEN_ACCELERATE 0x0006 -#define MODE_DTMF 0x0007 -#define MODE_ONE_DESCRIPTOR 0x0008 -#define MODE_CODEC_INTERNAL_CNG 0x0009 -#define MODE_SUCCESS_PREEMPTIVE 0x000A -#define MODE_UNSUCCESS_PREEMPTIVE 0x000B -#define MODE_LOWEN_PREEMPTIVE 0x000C -#define MODE_FADE_TO_BGN 0x000D - -#define MODE_ERROR 0x0010 - -#define MODE_AWAITING_CODEC_PTR 0x0100 - -#define MODE_BGN_ONLY 0x0200 - -#define MODE_MASTER_DTMF_SIGNAL 0x0400 - -#define MODE_USING_STEREO 0x0800 - - - -/***********************/ -/* Group codec defines */ -/***********************/ - -#if (defined(NETEQ_ALL_SPECIAL_CODECS)) - #define NETEQ_CNG_CODEC - #define NETEQ_ATEVENT_DECODE - #define NETEQ_RED_CODEC - #define NETEQ_VAD - #define NETEQ_ARBITRARY_CODEC -#endif - -#if (defined(NETEQ_ALL_NB_CODECS)) /* Except RED, DTMF and CNG */ - #define NETEQ_PCM16B_CODEC - #define NETEQ_G711_CODEC - #define NETEQ_ILBC_CODEC - #define NETEQ_G729_CODEC - #define NETEQ_G726_CODEC - #define NETEQ_GSMFR_CODEC - #define NETEQ_OPUS_CODEC - #define NETEQ_AMR_CODEC -#endif - -#if (defined(NETEQ_ALL_WB_CODECS)) /* Except RED, DTMF and CNG */ - #define NETEQ_ISAC_CODEC - #define NETEQ_G722_CODEC - #define NETEQ_G722_1_CODEC - #define NETEQ_G729_1_CODEC - #define NETEQ_OPUS_CODEC - #define NETEQ_SPEEX_CODEC - #define NETEQ_AMRWB_CODEC - #define NETEQ_WIDEBAND -#endif - -#if (defined(NETEQ_ALL_WB32_CODECS)) /* AAC, RED, DTMF and CNG */ - #define NETEQ_ISAC_SWB_CODEC - #define NETEQ_32KHZ_WIDEBAND - #define NETEQ_G722_1C_CODEC - #define NETEQ_CELT_CODEC - #define NETEQ_OPUS_CODEC -#endif - -#if (defined(NETEQ_VOICEENGINE_CODECS)) - /* Special codecs */ - #define NETEQ_CNG_CODEC - #define NETEQ_ATEVENT_DECODE - #define NETEQ_RED_CODEC - #define NETEQ_VAD - #define NETEQ_ARBITRARY_CODEC - - /* Narrowband codecs */ - #define NETEQ_PCM16B_CODEC - #define NETEQ_G711_CODEC - #define NETEQ_ILBC_CODEC - #define NETEQ_AMR_CODEC - #define NETEQ_G729_CODEC - #define NETEQ_GSMFR_CODEC - - /* Wideband codecs */ - #define NETEQ_WIDEBAND - #define NETEQ_ISAC_CODEC - #define NETEQ_G722_CODEC - #define NETEQ_G722_1_CODEC - #define NETEQ_G729_1_CODEC - #define NETEQ_AMRWB_CODEC - #define NETEQ_SPEEX_CODEC - - /* Super wideband 32kHz codecs */ - #define NETEQ_ISAC_SWB_CODEC - #define NETEQ_32KHZ_WIDEBAND - #define NETEQ_G722_1C_CODEC - #define NETEQ_CELT_CODEC - - /* hack in 48 kHz support */ - #define NETEQ_48KHZ_WIDEBAND - - /* Fullband 48 kHz codecs */ - #define NETEQ_OPUS_CODEC - #define NETEQ_ISAC_FB_CODEC -#endif - -#if (defined(NETEQ_ALL_CODECS)) - /* Special codecs */ - #define NETEQ_CNG_CODEC - #define NETEQ_ATEVENT_DECODE - #define NETEQ_RED_CODEC - #define NETEQ_VAD - #define NETEQ_ARBITRARY_CODEC - - /* Narrowband codecs */ - #define NETEQ_PCM16B_CODEC - #define NETEQ_G711_CODEC - #define NETEQ_ILBC_CODEC - #define NETEQ_G729_CODEC - #define NETEQ_G726_CODEC - #define NETEQ_GSMFR_CODEC - #define NETEQ_AMR_CODEC - - /* Wideband codecs */ - #define NETEQ_WIDEBAND - #define NETEQ_ISAC_CODEC - #define NETEQ_G722_CODEC - #define NETEQ_G722_1_CODEC - #define NETEQ_G729_1_CODEC - #define NETEQ_SPEEX_CODEC - #define NETEQ_AMRWB_CODEC - - /* Super wideband 32kHz codecs */ - #define NETEQ_ISAC_SWB_CODEC - #define NETEQ_32KHZ_WIDEBAND - #define NETEQ_G722_1C_CODEC - #define NETEQ_CELT_CODEC - - /* Super wideband 48kHz codecs */ - #define NETEQ_48KHZ_WIDEBAND - #define NETEQ_OPUS_CODEC - #define NETEQ_ISAC_FB_CODEC -#endif - -/* Max output size from decoding one frame */ -#if defined(NETEQ_48KHZ_WIDEBAND) - #define NETEQ_MAX_FRAME_SIZE 5760 /* 120 ms super wideband */ - #define NETEQ_MAX_OUTPUT_SIZE 6480 /* 120+15 ms super wideband (120 ms - * decoded + 15 ms for merge overlap) */ -#elif defined(NETEQ_32KHZ_WIDEBAND) - #define NETEQ_MAX_FRAME_SIZE 3840 /* 120 ms super wideband */ - #define NETEQ_MAX_OUTPUT_SIZE 4320 /* 120+15 ms super wideband (120 ms - * decoded + 15 ms for merge overlap) */ -#elif defined(NETEQ_WIDEBAND) - #define NETEQ_MAX_FRAME_SIZE 1920 /* 120 ms wideband */ - #define NETEQ_MAX_OUTPUT_SIZE 2160 /* 120+15 ms wideband (120 ms decoded + - * 15 ms for merge overlap) */ -#else - #define NETEQ_MAX_FRAME_SIZE 960 /* 120 ms narrowband */ - #define NETEQ_MAX_OUTPUT_SIZE 1080 /* 120+15 ms narrowband (120 ms decoded - * + 15 ms for merge overlap) */ -#endif - - -/* Enable stereo */ -#define NETEQ_STEREO - -#endif /* #if !defined NETEQ_DEFINES_H */ - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_error_codes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_error_codes.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_error_codes.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_error_codes.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Definition of error codes. - * - * NOTE: When modifying the error codes, - * also modify the function WebRtcNetEQ_GetErrorCode! - */ - -#if !defined NETEQ_ERROR_CODES_H -#define NETEQ_ERROR_CODES_H - -/* Misc Error */ -#define NETEQ_OTHER_ERROR -1000 - -/* Misc Recout Errors */ -#define FAULTY_INSTRUCTION -1001 -#define FAULTY_NETWORK_TYPE -1002 -#define FAULTY_DELAYVALUE -1003 -#define FAULTY_PLAYOUTMODE -1004 -#define CORRUPT_INSTANCE -1005 -#define ILLEGAL_MASTER_SLAVE_SWITCH -1006 -#define MASTER_SLAVE_ERROR -1007 - -/* Misc Recout problems */ -#define UNKNOWN_BUFSTAT_DECISION -2001 -#define RECOUT_ERROR_DECODING -2002 -#define RECOUT_ERROR_SAMPLEUNDERRUN -2003 -#define RECOUT_ERROR_DECODED_TOO_MUCH -2004 - -/* Misc RecIn problems */ -#define RECIN_CNG_ERROR -3001 -#define RECIN_UNKNOWNPAYLOAD -3002 -#define RECIN_BUFFERINSERT_ERROR -3003 -#define RECIN_SYNC_RTP_CHANGED_CODEC -3004 -#define RECIN_SYNC_RTP_NOT_ACCEPTABLE -3005 - -/* PBUFFER/BUFSTAT ERRORS */ -#define PBUFFER_INIT_ERROR -4001 -#define PBUFFER_INSERT_ERROR1 -4002 -#define PBUFFER_INSERT_ERROR2 -4003 -#define PBUFFER_INSERT_ERROR3 -4004 -#define PBUFFER_INSERT_ERROR4 -4005 -#define PBUFFER_INSERT_ERROR5 -4006 -#define UNKNOWN_G723_HEADER -4007 -#define PBUFFER_NONEXISTING_PACKET -4008 -#define PBUFFER_NOT_INITIALIZED -4009 -#define AMBIGUOUS_ILBC_FRAME_SIZE -4010 - -/* CODEC DATABASE ERRORS */ -#define CODEC_DB_FULL -5001 -#define CODEC_DB_NOT_EXIST1 -5002 -#define CODEC_DB_NOT_EXIST2 -5003 -#define CODEC_DB_NOT_EXIST3 -5004 -#define CODEC_DB_NOT_EXIST4 -5005 -#define CODEC_DB_UNKNOWN_CODEC -5006 -#define CODEC_DB_PAYLOAD_TAKEN -5007 -#define CODEC_DB_UNSUPPORTED_CODEC -5008 -#define CODEC_DB_UNSUPPORTED_FS -5009 - -/* DTMF ERRORS */ -#define DTMF_DEC_PARAMETER_ERROR -6001 -#define DTMF_INSERT_ERROR -6002 -#define DTMF_GEN_UNKNOWN_SAMP_FREQ -6003 -#define DTMF_NOT_SUPPORTED -6004 - -/* RTP/PACKET ERRORS */ -#define RED_SPLIT_ERROR1 -7001 -#define RED_SPLIT_ERROR2 -7002 -#define RTP_TOO_SHORT_PACKET -7003 -#define RTP_CORRUPT_PACKET -7004 - -#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_external_decoder_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_external_decoder_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_external_decoder_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_external_decoder_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,486 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Test to verify correct operation for externally created decoders. + +#include +#include + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" +#include "webrtc/modules/audio_coding/neteq/mock/mock_external_decoder_pcm16b.h" +#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" +#include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h" +#include "webrtc/system_wrappers/interface/compile_assert.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/test/testsupport/fileutils.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace webrtc { + +using ::testing::_; +using ::testing::Return; + +// This test encodes a few packets of PCM16b 32 kHz data and inserts it into two +// different NetEq instances. The first instance uses the internal version of +// the decoder object, while the second one uses an externally created decoder +// object (ExternalPcm16B wrapped in MockExternalPcm16B, both defined above). +// The test verifies that the output from both instances match. +class NetEqExternalDecoderTest : public ::testing::Test { + protected: + static const int kTimeStepMs = 10; + static const int kMaxBlockSize = 480; // 10 ms @ 48 kHz. + static const uint8_t kPayloadType = 95; + static const int kSampleRateHz = 32000; + + NetEqExternalDecoderTest() + : sample_rate_hz_(kSampleRateHz), + samples_per_ms_(sample_rate_hz_ / 1000), + frame_size_ms_(10), + frame_size_samples_(frame_size_ms_ * samples_per_ms_), + output_size_samples_(frame_size_ms_ * samples_per_ms_), + external_decoder_(new MockExternalPcm16B), + rtp_generator_(new test::RtpGenerator(samples_per_ms_)), + payload_size_bytes_(0), + last_send_time_(0), + last_arrival_time_(0) { + config_.sample_rate_hz = sample_rate_hz_; + neteq_external_ = NetEq::Create(config_); + neteq_ = NetEq::Create(config_); + input_ = new int16_t[frame_size_samples_]; + encoded_ = new uint8_t[2 * frame_size_samples_]; + } + + ~NetEqExternalDecoderTest() { + delete neteq_external_; + delete neteq_; + // We will now delete the decoder ourselves, so expecting Die to be called. + EXPECT_CALL(*external_decoder_, Die()).Times(1); + delete [] input_; + delete [] encoded_; + } + + virtual void SetUp() { + const std::string file_name = + webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); + input_file_.reset(new test::InputAudioFile(file_name)); + assert(sample_rate_hz_ == 32000); + NetEqDecoder decoder = kDecoderPCM16Bswb32kHz; + EXPECT_CALL(*external_decoder_, Init()); + // NetEq is not allowed to delete the external decoder (hence Times(0)). + EXPECT_CALL(*external_decoder_, Die()).Times(0); + ASSERT_EQ(NetEq::kOK, + neteq_external_->RegisterExternalDecoder( + external_decoder_.get(), decoder, kPayloadType)); + ASSERT_EQ(NetEq::kOK, + neteq_->RegisterPayloadType(decoder, kPayloadType)); + } + + virtual void TearDown() {} + + int GetNewPackets() { + if (!input_file_->Read(frame_size_samples_, input_)) { + return -1; + } + payload_size_bytes_ = WebRtcPcm16b_Encode(input_, frame_size_samples_, + encoded_); + if (frame_size_samples_ * 2 != payload_size_bytes_) { + return -1; + } + int next_send_time = rtp_generator_->GetRtpHeader( + kPayloadType, frame_size_samples_, &rtp_header_); + return next_send_time; + } + + virtual void VerifyOutput(size_t num_samples) const { + for (size_t i = 0; i < num_samples; ++i) { + ASSERT_EQ(output_[i], output_external_[i]) << + "Diff in sample " << i << "."; + } + } + + virtual int GetArrivalTime(int send_time) { + int arrival_time = last_arrival_time_ + (send_time - last_send_time_); + last_send_time_ = send_time; + last_arrival_time_ = arrival_time; + return arrival_time; + } + + virtual bool Lost() { return false; } + + virtual void InsertPackets(int next_arrival_time) { + // Insert packet in regular instance. + ASSERT_EQ( + NetEq::kOK, + neteq_->InsertPacket( + rtp_header_, encoded_, payload_size_bytes_, next_arrival_time)); + // Insert packet in external decoder instance. + EXPECT_CALL(*external_decoder_, + IncomingPacket(_, + payload_size_bytes_, + rtp_header_.header.sequenceNumber, + rtp_header_.header.timestamp, + next_arrival_time)); + ASSERT_EQ( + NetEq::kOK, + neteq_external_->InsertPacket( + rtp_header_, encoded_, payload_size_bytes_, next_arrival_time)); + } + + virtual void GetOutputAudio() { + NetEqOutputType output_type; + // Get audio from regular instance. + int samples_per_channel; + int num_channels; + EXPECT_EQ(NetEq::kOK, + neteq_->GetAudio(kMaxBlockSize, + output_, + &samples_per_channel, + &num_channels, + &output_type)); + EXPECT_EQ(1, num_channels); + EXPECT_EQ(output_size_samples_, samples_per_channel); + // Get audio from external decoder instance. + ASSERT_EQ(NetEq::kOK, + neteq_external_->GetAudio(kMaxBlockSize, + output_external_, + &samples_per_channel, + &num_channels, + &output_type)); + EXPECT_EQ(1, num_channels); + EXPECT_EQ(output_size_samples_, samples_per_channel); + } + + virtual int NumExpectedDecodeCalls(int num_loops) const { return num_loops; } + + void RunTest(int num_loops) { + // Get next input packets (mono and multi-channel). + int next_send_time; + int next_arrival_time; + do { + next_send_time = GetNewPackets(); + ASSERT_NE(-1, next_send_time); + next_arrival_time = GetArrivalTime(next_send_time); + } while (Lost()); // If lost, immediately read the next packet. + + EXPECT_CALL(*external_decoder_, Decode(_, payload_size_bytes_, _, _)) + .Times(NumExpectedDecodeCalls(num_loops)); + + int time_now = 0; + for (int k = 0; k < num_loops; ++k) { + while (time_now >= next_arrival_time) { + InsertPackets(next_arrival_time); + + // Get next input packet. + do { + next_send_time = GetNewPackets(); + ASSERT_NE(-1, next_send_time); + next_arrival_time = GetArrivalTime(next_send_time); + } while (Lost()); // If lost, immediately read the next packet. + } + + GetOutputAudio(); + + std::ostringstream ss; + ss << "Lap number " << k << "."; + SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. + // Compare mono and multi-channel. + ASSERT_NO_FATAL_FAILURE(VerifyOutput(output_size_samples_)); + + time_now += kTimeStepMs; + } + } + + NetEq::Config config_; + int sample_rate_hz_; + int samples_per_ms_; + const int frame_size_ms_; + int frame_size_samples_; + int output_size_samples_; + NetEq* neteq_external_; + NetEq* neteq_; + scoped_ptr external_decoder_; + scoped_ptr rtp_generator_; + int16_t* input_; + uint8_t* encoded_; + int16_t output_[kMaxBlockSize]; + int16_t output_external_[kMaxBlockSize]; + WebRtcRTPHeader rtp_header_; + int payload_size_bytes_; + int last_send_time_; + int last_arrival_time_; + scoped_ptr input_file_; +}; + +TEST_F(NetEqExternalDecoderTest, RunTest) { + RunTest(100); // Run 100 laps @ 10 ms each in the test loop. +} + +class LargeTimestampJumpTest : public NetEqExternalDecoderTest { + protected: + enum TestStates { + kInitialPhase, + kNormalPhase, + kExpandPhase, + kFadedExpandPhase, + kRecovered + }; + + LargeTimestampJumpTest() + : NetEqExternalDecoderTest(), test_state_(kInitialPhase) { + sample_rate_hz_ = 8000; + samples_per_ms_ = sample_rate_hz_ / 1000; + frame_size_samples_ = frame_size_ms_ * samples_per_ms_; + output_size_samples_ = frame_size_ms_ * samples_per_ms_; + EXPECT_CALL(*external_decoder_, Die()).Times(1); + external_decoder_.reset(new MockExternalPcm16B); + } + + void SetUp() OVERRIDE { + const std::string file_name = + webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); + input_file_.reset(new test::InputAudioFile(file_name)); + assert(sample_rate_hz_ == 8000); + NetEqDecoder decoder = kDecoderPCM16B; + EXPECT_CALL(*external_decoder_, Init()); + EXPECT_CALL(*external_decoder_, HasDecodePlc()) + .WillRepeatedly(Return(false)); + // NetEq is not allowed to delete the external decoder (hence Times(0)). + EXPECT_CALL(*external_decoder_, Die()).Times(0); + ASSERT_EQ(NetEq::kOK, + neteq_external_->RegisterExternalDecoder( + external_decoder_.get(), decoder, kPayloadType)); + ASSERT_EQ(NetEq::kOK, neteq_->RegisterPayloadType(decoder, kPayloadType)); + } + + void InsertPackets(int next_arrival_time) OVERRIDE { + // Insert packet in external decoder instance. + EXPECT_CALL(*external_decoder_, + IncomingPacket(_, + payload_size_bytes_, + rtp_header_.header.sequenceNumber, + rtp_header_.header.timestamp, + next_arrival_time)); + ASSERT_EQ( + NetEq::kOK, + neteq_external_->InsertPacket( + rtp_header_, encoded_, payload_size_bytes_, next_arrival_time)); + } + + void GetOutputAudio() OVERRIDE { + NetEqOutputType output_type; + int samples_per_channel; + int num_channels; + // Get audio from external decoder instance. + ASSERT_EQ(NetEq::kOK, + neteq_external_->GetAudio(kMaxBlockSize, + output_external_, + &samples_per_channel, + &num_channels, + &output_type)); + EXPECT_EQ(1, num_channels); + EXPECT_EQ(output_size_samples_, samples_per_channel); + UpdateState(output_type); + } + + virtual void UpdateState(NetEqOutputType output_type) { + switch (test_state_) { + case kInitialPhase: { + if (output_type == kOutputNormal) { + test_state_ = kNormalPhase; + } + break; + } + case kNormalPhase: { + if (output_type == kOutputPLC) { + test_state_ = kExpandPhase; + } + break; + } + case kExpandPhase: { + if (output_type == kOutputPLCtoCNG) { + test_state_ = kFadedExpandPhase; + } else if (output_type == kOutputNormal) { + test_state_ = kRecovered; + } + break; + } + case kFadedExpandPhase: { + if (output_type == kOutputNormal) { + test_state_ = kRecovered; + } + break; + } + case kRecovered: { + break; + } + } + } + + void VerifyOutput(size_t num_samples) const OVERRIDE { + if (test_state_ == kExpandPhase || test_state_ == kFadedExpandPhase) { + // Don't verify the output in this phase of the test. + return; + } + for (size_t i = 0; i < num_samples; ++i) { + if (output_external_[i] != 0) + return; + } + EXPECT_TRUE(false) + << "Expected at least one non-zero sample in each output block."; + } + + int NumExpectedDecodeCalls(int num_loops) const OVERRIDE { + // Some packets at the end of the stream won't be decoded. When the jump in + // timestamp happens, NetEq will do Expand during one GetAudio call. In the + // next call it will decode the packet after the jump, but the net result is + // that the delay increased by 1 packet. In another call, a Pre-emptive + // Expand operation is performed, leading to delay increase by 1 packet. In + // total, the test will end with a 2-packet delay, which results in the 2 + // last packets not being decoded. + return num_loops - 2; + } + + TestStates test_state_; +}; + +TEST_F(LargeTimestampJumpTest, JumpLongerThanHalfRange) { + // Set the timestamp series to start at 2880, increase to 7200, then jump to + // 2869342376. The sequence numbers start at 42076 and increase by 1 for each + // packet, also when the timestamp jumps. + static const uint16_t kStartSeqeunceNumber = 42076; + static const uint32_t kStartTimestamp = 2880; + static const uint32_t kJumpFromTimestamp = 7200; + static const uint32_t kJumpToTimestamp = 2869342376; + COMPILE_ASSERT(kJumpFromTimestamp < kJumpToTimestamp, + timestamp_jump_should_not_result_in_wrap); + COMPILE_ASSERT( + static_cast(kJumpToTimestamp - kJumpFromTimestamp) > 0x7FFFFFFF, + jump_should_be_larger_than_half_range); + // Replace the default RTP generator with one that jumps in timestamp. + rtp_generator_.reset(new test::TimestampJumpRtpGenerator(samples_per_ms_, + kStartSeqeunceNumber, + kStartTimestamp, + kJumpFromTimestamp, + kJumpToTimestamp)); + + RunTest(130); // Run 130 laps @ 10 ms each in the test loop. + EXPECT_EQ(kRecovered, test_state_); +} + +TEST_F(LargeTimestampJumpTest, JumpLongerThanHalfRangeAndWrap) { + // Make a jump larger than half the 32-bit timestamp range. Set the start + // timestamp such that the jump will result in a wrap around. + static const uint16_t kStartSeqeunceNumber = 42076; + // Set the jump length slightly larger than 2^31. + static const uint32_t kStartTimestamp = 3221223116; + static const uint32_t kJumpFromTimestamp = 3221223216; + static const uint32_t kJumpToTimestamp = 1073744278; + COMPILE_ASSERT(kJumpToTimestamp < kJumpFromTimestamp, + timestamp_jump_should_result_in_wrap); + COMPILE_ASSERT( + static_cast(kJumpToTimestamp - kJumpFromTimestamp) > 0x7FFFFFFF, + jump_should_be_larger_than_half_range); + // Replace the default RTP generator with one that jumps in timestamp. + rtp_generator_.reset(new test::TimestampJumpRtpGenerator(samples_per_ms_, + kStartSeqeunceNumber, + kStartTimestamp, + kJumpFromTimestamp, + kJumpToTimestamp)); + + RunTest(130); // Run 130 laps @ 10 ms each in the test loop. + EXPECT_EQ(kRecovered, test_state_); +} + +class ShortTimestampJumpTest : public LargeTimestampJumpTest { + protected: + void UpdateState(NetEqOutputType output_type) OVERRIDE { + switch (test_state_) { + case kInitialPhase: { + if (output_type == kOutputNormal) { + test_state_ = kNormalPhase; + } + break; + } + case kNormalPhase: { + if (output_type == kOutputPLC) { + test_state_ = kExpandPhase; + } + break; + } + case kExpandPhase: { + if (output_type == kOutputNormal) { + test_state_ = kRecovered; + } + break; + } + case kRecovered: { + break; + } + default: { FAIL(); } + } + } + + int NumExpectedDecodeCalls(int num_loops) const OVERRIDE { + // Some packets won't be decoded because of the timestamp jump. + return num_loops - 2; + } +}; + +TEST_F(ShortTimestampJumpTest, JumpShorterThanHalfRange) { + // Make a jump shorter than half the 32-bit timestamp range. Set the start + // timestamp such that the jump will not result in a wrap around. + static const uint16_t kStartSeqeunceNumber = 42076; + // Set the jump length slightly smaller than 2^31. + static const uint32_t kStartTimestamp = 4711; + static const uint32_t kJumpFromTimestamp = 4811; + static const uint32_t kJumpToTimestamp = 2147483747; + COMPILE_ASSERT(kJumpFromTimestamp < kJumpToTimestamp, + timestamp_jump_should_not_result_in_wrap); + COMPILE_ASSERT( + static_cast(kJumpToTimestamp - kJumpFromTimestamp) < 0x7FFFFFFF, + jump_should_be_smaller_than_half_range); + // Replace the default RTP generator with one that jumps in timestamp. + rtp_generator_.reset(new test::TimestampJumpRtpGenerator(samples_per_ms_, + kStartSeqeunceNumber, + kStartTimestamp, + kJumpFromTimestamp, + kJumpToTimestamp)); + + RunTest(130); // Run 130 laps @ 10 ms each in the test loop. + EXPECT_EQ(kRecovered, test_state_); +} + +TEST_F(ShortTimestampJumpTest, JumpShorterThanHalfRangeAndWrap) { + // Make a jump shorter than half the 32-bit timestamp range. Set the start + // timestamp such that the jump will result in a wrap around. + static const uint16_t kStartSeqeunceNumber = 42076; + // Set the jump length slightly smaller than 2^31. + static const uint32_t kStartTimestamp = 3221227827; + static const uint32_t kJumpFromTimestamp = 3221227927; + static const uint32_t kJumpToTimestamp = 1073739567; + COMPILE_ASSERT(kJumpToTimestamp < kJumpFromTimestamp, + timestamp_jump_should_result_in_wrap); + COMPILE_ASSERT( + static_cast(kJumpToTimestamp - kJumpFromTimestamp) < 0x7FFFFFFF, + jump_should_be_smaller_than_half_range); + // Replace the default RTP generator with one that jumps in timestamp. + rtp_generator_.reset(new test::TimestampJumpRtpGenerator(samples_per_ms_, + kStartSeqeunceNumber, + kStartTimestamp, + kJumpFromTimestamp, + kJumpToTimestamp)); + + RunTest(130); // Run 130 laps @ 10 ms each in the test loop. + EXPECT_EQ(kRecovered, test_state_); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq.gypi 2015-02-03 14:33:34.000000000 +0000 @@ -7,243 +7,254 @@ # be found in the AUTHORS file in the root of the source tree. { + 'variables': { + 'codecs': [ + 'G711', + 'PCM16B', + 'CNG', + ], + 'neteq_defines': [], + 'conditions': [ + ['include_g722==1', { + 'neteq_dependencies': ['G722'], + 'neteq_defines': ['WEBRTC_CODEC_G722',], + }], + ['include_ilbc==1', { + 'neteq_dependencies': ['iLBC'], + 'neteq_defines': ['WEBRTC_CODEC_ILBC',], + }], + ['include_isac==1', { + 'neteq_dependencies': ['iSAC', 'iSACFix',], + 'neteq_defines': ['WEBRTC_CODEC_ISAC', 'WEBRTC_CODEC_ISACFIX',], + }], + ['include_opus==1', { + 'codecs': ['webrtc_opus'], + 'neteq_dependencies': ['webrtc_opus'], + 'neteq_defines': ['WEBRTC_CODEC_OPUS',], + 'conditions': [ + ['build_with_mozilla==0', { + 'neteq_dependencies': [ + '<(DEPTH)/third_party/opus/opus.gyp:opus', + ], + }], + ], + }], + ], + 'neteq_dependencies': [ + '<@(codecs)', + '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + ], + + }, 'targets': [ { - 'target_name': 'NetEq', + 'target_name': 'neteq', 'type': 'static_library', - 'dependencies': [ - 'CNG', - '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', - ], - 'defines': [ - 'NETEQ_VOICEENGINE_CODECS', # TODO: Should create a Chrome define which - 'SCRATCH', # specifies a subset of codecs to support. - ], 'include_dirs': [ - 'interface', - '<(webrtc_root)', + '../../../../../../media/opus/celt', ], 'direct_dependent_settings': { 'include_dirs': [ - 'interface', - '<(webrtc_root)', - ], + '../../../../../../media/opus/celt', + ], }, + 'dependencies': [ + '<@(neteq_dependencies)', + ], + 'defines': [ + '<@(neteq_defines)', + ], + 'conditions': [ + ['build_with_mozilla==0', { + 'include_dirs': [ + # Need Opus header files for the audio classifier. + '<(DEPTH)/third_party/opus/src/celt', + '<(DEPTH)/third_party/opus/src/src', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + # Need Opus header files for the audio classifier. + '<(DEPTH)/third_party/opus/src/celt', + '<(DEPTH)/third_party/opus/src/src', + ], + }, + 'export_dependent_settings': [ + '<(DEPTH)/third_party/opus/opus.gyp:opus', + ], + }], + ['build_with_mozilla==1', { + 'include_dirs': [ + # Need Opus header files for the audio classifier. + '<(DEPTH)/../../../media/opus/celt', +# '<(DEPTH)/third_party/opus/src/src', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '../../../../../../media/opus/celt', + # Need Opus header files for the audio classifier. + '<(DEPTH)/../../../media/opus/celt', +# '<(DEPTH)/third_party/opus/src/src', + ], + }, + }], + ], 'sources': [ - 'interface/webrtc_neteq.h', - 'interface/webrtc_neteq_help_macros.h', - 'interface/webrtc_neteq_internal.h', - 'accelerate.c', - 'automode.c', - 'automode.h', - 'bgn_update.c', - 'buffer_stats.h', - 'bufstats_decision.c', - 'cng_internal.c', - 'codec_db.c', - 'codec_db.h', - 'codec_db_defines.h', - 'correlator.c', - 'delay_logging.h', - 'dsp.c', - 'dsp.h', - 'dsp_helpfunctions.c', - 'dsp_helpfunctions.h', - 'dtmf_buffer.c', + 'interface/audio_decoder.h', + 'interface/neteq.h', + 'accelerate.cc', + 'accelerate.h', + 'audio_classifier.cc', + 'audio_classifier.h', + 'audio_decoder_impl.cc', + 'audio_decoder_impl.h', + 'audio_decoder.cc', + 'audio_multi_vector.cc', + 'audio_multi_vector.h', + 'audio_vector.cc', + 'audio_vector.h', + 'background_noise.cc', + 'background_noise.h', + 'buffer_level_filter.cc', + 'buffer_level_filter.h', + 'comfort_noise.cc', + 'comfort_noise.h', + 'decision_logic.cc', + 'decision_logic.h', + 'decision_logic_fax.cc', + 'decision_logic_fax.h', + 'decision_logic_normal.cc', + 'decision_logic_normal.h', + 'decoder_database.cc', + 'decoder_database.h', + 'defines.h', + 'delay_manager.cc', + 'delay_manager.h', + 'delay_peak_detector.cc', + 'delay_peak_detector.h', + 'dsp_helper.cc', + 'dsp_helper.h', + 'dtmf_buffer.cc', 'dtmf_buffer.h', - 'dtmf_tonegen.c', - 'dtmf_tonegen.h', - 'expand.c', - 'mcu.h', - 'mcu_address_init.c', - 'mcu_dsp_common.c', - 'mcu_dsp_common.h', - 'mcu_reset.c', - 'merge.c', - 'min_distortion.c', - 'mix_voice_unvoice.c', - 'mute_signal.c', - 'neteq_defines.h', - 'neteq_error_codes.h', - 'neteq_statistics.h', - 'normal.c', - 'packet_buffer.c', + 'dtmf_tone_generator.cc', + 'dtmf_tone_generator.h', + 'expand.cc', + 'expand.h', + 'merge.cc', + 'merge.h', + 'neteq_impl.cc', + 'neteq_impl.h', + 'neteq.cc', + 'statistics_calculator.cc', + 'statistics_calculator.h', + 'normal.cc', + 'normal.h', + 'packet_buffer.cc', 'packet_buffer.h', - 'peak_detection.c', - 'preemptive_expand.c', - 'random_vector.c', - 'recin.c', - 'recout.c', - 'rtcp.c', + 'payload_splitter.cc', + 'payload_splitter.h', + 'post_decode_vad.cc', + 'post_decode_vad.h', + 'preemptive_expand.cc', + 'preemptive_expand.h', + 'random_vector.cc', + 'random_vector.h', + 'rtcp.cc', 'rtcp.h', - 'rtp.c', - 'rtp.h', - 'set_fs.c', - 'signal_mcu.c', - 'split_and_insert.c', - 'unmute_signal.c', - 'webrtc_neteq.c', + 'sync_buffer.cc', + 'sync_buffer.h', + 'timestamp_scaler.cc', + 'timestamp_scaler.h', + 'time_stretch.cc', + 'time_stretch.h', ], }, ], # targets 'conditions': [ ['include_tests==1', { + 'includes': ['neteq_tests.gypi',], 'targets': [ { - 'target_name': 'neteq_unittests', + 'target_name': 'audio_decoder_unittests', 'type': '<(gtest_target_type)', 'dependencies': [ - 'NetEq', - 'NetEqTestTools', + '<@(codecs)', 'neteq_unittest_tools', '<(DEPTH)/testing/gtest.gyp:gtest', + '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', '<(webrtc_root)/test/test.gyp:test_support_main', ], - 'sources': [ - 'webrtc_neteq_unittest.cc', +# FIX for include_isac/etc + 'defines': [ + 'AUDIO_DECODER_UNITTEST', + 'WEBRTC_CODEC_G722', + 'WEBRTC_CODEC_ILBC', + 'WEBRTC_CODEC_ISACFX', + 'WEBRTC_CODEC_ISAC', + 'WEBRTC_CODEC_PCM16', + '<@(neteq_defines)', ], - # Disable warnings to enable Win64 build, issue 1323. - 'msvs_disabled_warnings': [ - 4267, # size_t to int truncation. + 'sources': [ + 'audio_decoder_impl.cc', + 'audio_decoder_impl.h', + 'audio_decoder_unittest.cc', + 'audio_decoder.cc', + 'interface/audio_decoder.h', ], 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are - # using Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['OS=="android"', { 'dependencies': [ '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code', ], }], ], - }, # neteq_unittests - { - 'target_name': 'NetEqRTPplay', - 'type': 'executable', - 'dependencies': [ - 'NetEq', # NetEQ library defined above - 'NetEqTestTools', # Test helpers - 'G711', - 'G722', - 'PCM16B', - 'iLBC', - 'iSAC', - 'CNG', - ], - 'defines': [ - # TODO: Make codec selection conditional on definitions in target NetEq - 'CODEC_ILBC', - 'CODEC_PCM16B', - 'CODEC_G711', - 'CODEC_G722', - 'CODEC_ISAC', - 'CODEC_PCM16B_WB', - 'CODEC_ISAC_SWB', - 'CODEC_ISAC_FB', - 'CODEC_PCM16B_32KHZ', - 'CODEC_CNGCODEC8', - 'CODEC_CNGCODEC16', - 'CODEC_CNGCODEC32', - 'CODEC_ATEVENT_DECODE', - 'CODEC_RED', - ], - 'include_dirs': [ - '.', - 'test', - ], - 'sources': [ - 'test/NetEqRTPplay.cc', - ], - # Disable warnings to enable Win64 build, issue 1323. - 'msvs_disabled_warnings': [ - 4267, # size_t to int truncation. - ], - }, + }, # audio_decoder_unittests { - 'target_name': 'neteq3_speed_test', - 'type': 'executable', - 'dependencies': [ - 'NetEq', - 'PCM16B', - 'neteq_unittest_tools', - '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', - '<(webrtc_root)/test/test.gyp:test_support_main', - ], - 'sources': [ - 'test/neteq_speed_test.cc', - ], - }, - - { - 'target_name': 'NetEqTestTools', - # Collection of useful functions used in other tests + 'target_name': 'neteq_unittest_tools', 'type': 'static_library', - 'variables': { - # Expects RTP packets without payloads when enabled. - 'neteq_dummy_rtp%': 0, - }, 'dependencies': [ - 'G711', - 'G722', - 'PCM16B', - 'iLBC', - 'iSAC', - 'CNG', - '<(DEPTH)/testing/gtest.gyp:gtest', + 'rtp_rtcp', + '<(webrtc_root)/test/test.gyp:rtp_test_utils', ], 'direct_dependent_settings': { 'include_dirs': [ - 'interface', - 'test', + 'tools', ], }, - 'defines': [ - # TODO: Make codec selection conditional on definitions in target NetEq - 'CODEC_ILBC', - 'CODEC_PCM16B', - 'CODEC_G711', - 'CODEC_G722', - 'CODEC_ISAC', - 'CODEC_PCM16B_WB', - 'CODEC_ISAC_SWB', - 'CODEC_ISAC_FB', - 'CODEC_PCM16B_32KHZ', - 'CODEC_CNGCODEC8', - 'CODEC_CNGCODEC16', - 'CODEC_CNGCODEC32', - 'CODEC_ATEVENT_DECODE', - 'CODEC_RED', - ], 'include_dirs': [ - 'interface', - 'test', + 'tools', ], 'sources': [ - 'test/NETEQTEST_CodecClass.cc', - 'test/NETEQTEST_CodecClass.h', - 'test/NETEQTEST_DummyRTPpacket.cc', - 'test/NETEQTEST_DummyRTPpacket.h', - 'test/NETEQTEST_NetEQClass.cc', - 'test/NETEQTEST_NetEQClass.h', - 'test/NETEQTEST_RTPpacket.cc', - 'test/NETEQTEST_RTPpacket.h', - ], - # Disable warnings to enable Win64 build, issue 1323. - 'msvs_disabled_warnings': [ - 4267, # size_t to int truncation. + 'tools/audio_checksum.h', + 'tools/audio_loop.cc', + 'tools/audio_loop.h', + 'tools/audio_sink.h', + 'tools/constant_pcm_packet_source.cc', + 'tools/constant_pcm_packet_source.h', + 'tools/input_audio_file.cc', + 'tools/input_audio_file.h', + 'tools/output_audio_file.h', + 'tools/packet.cc', + 'tools/packet.h', + 'tools/packet_source.h', + 'tools/resample_input_audio_file.cc', + 'tools/resample_input_audio_file.h', + 'tools/rtp_file_source.cc', + 'tools/rtp_file_source.h', + 'tools/rtp_generator.cc', + 'tools/rtp_generator.h', ], - }, + }, # neteq_unittest_tools ], # targets 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are using - # Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['OS=="android"', { 'targets': [ { - 'target_name': 'neteq_unittests_apk_target', + 'target_name': 'audio_decoder_unittests_apk_target', 'type': 'none', 'dependencies': [ - '<(apk_tests_path):neteq_unittests_apk', + '<(apk_tests_path):audio_decoder_unittests_apk', ], }, ], @@ -251,17 +262,16 @@ ['test_isolation_mode != "noop"', { 'targets': [ { - 'target_name': 'neteq_unittests_run', + 'target_name': 'audio_decoder_unittests_run', 'type': 'none', 'dependencies': [ - 'neteq_unittests', + 'audio_decoder_unittests', ], 'includes': [ '../../../build/isolate.gypi', - 'neteq_unittests.isolate', ], 'sources': [ - 'neteq_unittests.isolate', + 'audio_decoder_unittests.isolate', ], }, ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_impl.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_impl.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,1945 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/neteq_impl.h" + +#include +#include // memset + +#include + +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/modules/audio_coding/neteq/accelerate.h" +#include "webrtc/modules/audio_coding/neteq/background_noise.h" +#include "webrtc/modules/audio_coding/neteq/buffer_level_filter.h" +#include "webrtc/modules/audio_coding/neteq/comfort_noise.h" +#include "webrtc/modules/audio_coding/neteq/decision_logic.h" +#include "webrtc/modules/audio_coding/neteq/decoder_database.h" +#include "webrtc/modules/audio_coding/neteq/defines.h" +#include "webrtc/modules/audio_coding/neteq/delay_manager.h" +#include "webrtc/modules/audio_coding/neteq/delay_peak_detector.h" +#include "webrtc/modules/audio_coding/neteq/dtmf_buffer.h" +#include "webrtc/modules/audio_coding/neteq/dtmf_tone_generator.h" +#include "webrtc/modules/audio_coding/neteq/expand.h" +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" +#include "webrtc/modules/audio_coding/neteq/merge.h" +#include "webrtc/modules/audio_coding/neteq/normal.h" +#include "webrtc/modules/audio_coding/neteq/packet_buffer.h" +#include "webrtc/modules/audio_coding/neteq/packet.h" +#include "webrtc/modules/audio_coding/neteq/payload_splitter.h" +#include "webrtc/modules/audio_coding/neteq/post_decode_vad.h" +#include "webrtc/modules/audio_coding/neteq/preemptive_expand.h" +#include "webrtc/modules/audio_coding/neteq/sync_buffer.h" +#include "webrtc/modules/audio_coding/neteq/timestamp_scaler.h" +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/logging.h" + +// Modify the code to obtain backwards bit-exactness. Once bit-exactness is no +// longer required, this #define should be removed (and the code that it +// enables). +#define LEGACY_BITEXACT + +namespace webrtc { + +NetEqImpl::NetEqImpl(const NetEq::Config& config, + BufferLevelFilter* buffer_level_filter, + DecoderDatabase* decoder_database, + DelayManager* delay_manager, + DelayPeakDetector* delay_peak_detector, + DtmfBuffer* dtmf_buffer, + DtmfToneGenerator* dtmf_tone_generator, + PacketBuffer* packet_buffer, + PayloadSplitter* payload_splitter, + TimestampScaler* timestamp_scaler, + AccelerateFactory* accelerate_factory, + ExpandFactory* expand_factory, + PreemptiveExpandFactory* preemptive_expand_factory, + bool create_components) + : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + buffer_level_filter_(buffer_level_filter), + decoder_database_(decoder_database), + delay_manager_(delay_manager), + delay_peak_detector_(delay_peak_detector), + dtmf_buffer_(dtmf_buffer), + dtmf_tone_generator_(dtmf_tone_generator), + packet_buffer_(packet_buffer), + payload_splitter_(payload_splitter), + timestamp_scaler_(timestamp_scaler), + vad_(new PostDecodeVad()), + expand_factory_(expand_factory), + accelerate_factory_(accelerate_factory), + preemptive_expand_factory_(preemptive_expand_factory), + last_mode_(kModeNormal), + decoded_buffer_length_(kMaxFrameSize), + decoded_buffer_(new int16_t[decoded_buffer_length_]), + playout_timestamp_(0), + new_codec_(false), + timestamp_(0), + reset_decoder_(false), + current_rtp_payload_type_(0xFF), // Invalid RTP payload type. + current_cng_rtp_payload_type_(0xFF), // Invalid RTP payload type. + ssrc_(0), + first_packet_(true), + error_code_(0), + decoder_error_code_(0), + background_noise_mode_(config.background_noise_mode), + playout_mode_(config.playout_mode), + decoded_packet_sequence_number_(-1), + decoded_packet_timestamp_(0) { + int fs = config.sample_rate_hz; + if (fs != 8000 && fs != 16000 && fs != 32000 && fs != 48000) { + LOG(LS_ERROR) << "Sample rate " << fs << " Hz not supported. " << + "Changing to 8000 Hz."; + fs = 8000; + } + LOG(LS_VERBOSE) << "Create NetEqImpl object with fs = " << fs << "."; + fs_hz_ = fs; + fs_mult_ = fs / 8000; + output_size_samples_ = kOutputSizeMs * 8 * fs_mult_; + decoder_frame_length_ = 3 * output_size_samples_; + WebRtcSpl_Init(); + if (create_components) { + SetSampleRateAndChannels(fs, 1); // Default is 1 channel. + } +} + +NetEqImpl::~NetEqImpl() { + LOG(LS_INFO) << "Deleting NetEqImpl object."; +} + +int NetEqImpl::InsertPacket(const WebRtcRTPHeader& rtp_header, + const uint8_t* payload, + int length_bytes, + uint32_t receive_timestamp) { + CriticalSectionScoped lock(crit_sect_.get()); + LOG(LS_VERBOSE) << "InsertPacket: ts=" << rtp_header.header.timestamp << + ", sn=" << rtp_header.header.sequenceNumber << + ", pt=" << static_cast(rtp_header.header.payloadType) << + ", ssrc=" << rtp_header.header.ssrc << + ", len=" << length_bytes; + int error = InsertPacketInternal(rtp_header, payload, length_bytes, + receive_timestamp, false); + if (error != 0) { + LOG_FERR1(LS_WARNING, InsertPacketInternal, error); + error_code_ = error; + return kFail; + } + return kOK; +} + +int NetEqImpl::InsertSyncPacket(const WebRtcRTPHeader& rtp_header, + uint32_t receive_timestamp) { + CriticalSectionScoped lock(crit_sect_.get()); + LOG(LS_VERBOSE) << "InsertPacket-Sync: ts=" + << rtp_header.header.timestamp << + ", sn=" << rtp_header.header.sequenceNumber << + ", pt=" << static_cast(rtp_header.header.payloadType) << + ", ssrc=" << rtp_header.header.ssrc; + + const uint8_t kSyncPayload[] = { 's', 'y', 'n', 'c' }; + int error = InsertPacketInternal( + rtp_header, kSyncPayload, sizeof(kSyncPayload), receive_timestamp, true); + + if (error != 0) { + LOG_FERR1(LS_WARNING, InsertPacketInternal, error); + error_code_ = error; + return kFail; + } + return kOK; +} + +int NetEqImpl::GetAudio(size_t max_length, int16_t* output_audio, + int* samples_per_channel, int* num_channels, + NetEqOutputType* type) { + CriticalSectionScoped lock(crit_sect_.get()); + LOG(LS_VERBOSE) << "GetAudio"; + int error = GetAudioInternal(max_length, output_audio, samples_per_channel, + num_channels); + LOG(LS_VERBOSE) << "Produced " << *samples_per_channel << + " samples/channel for " << *num_channels << " channel(s)"; + if (error != 0) { + LOG_FERR1(LS_WARNING, GetAudioInternal, error); + error_code_ = error; + return kFail; + } + if (type) { + *type = LastOutputType(); + } + return kOK; +} + +int NetEqImpl::RegisterPayloadType(enum NetEqDecoder codec, + uint8_t rtp_payload_type) { + CriticalSectionScoped lock(crit_sect_.get()); + LOG_API2(static_cast(rtp_payload_type), codec); + int ret = decoder_database_->RegisterPayload(rtp_payload_type, codec); + if (ret != DecoderDatabase::kOK) { + LOG_FERR2(LS_WARNING, RegisterPayload, rtp_payload_type, codec); + switch (ret) { + case DecoderDatabase::kInvalidRtpPayloadType: + error_code_ = kInvalidRtpPayloadType; + break; + case DecoderDatabase::kCodecNotSupported: + error_code_ = kCodecNotSupported; + break; + case DecoderDatabase::kDecoderExists: + error_code_ = kDecoderExists; + break; + default: + error_code_ = kOtherError; + } + return kFail; + } + return kOK; +} + +int NetEqImpl::RegisterExternalDecoder(AudioDecoder* decoder, + enum NetEqDecoder codec, + uint8_t rtp_payload_type) { + CriticalSectionScoped lock(crit_sect_.get()); + LOG_API2(static_cast(rtp_payload_type), codec); + if (!decoder) { + LOG(LS_ERROR) << "Cannot register external decoder with NULL pointer"; + assert(false); + return kFail; + } + const int sample_rate_hz = AudioDecoder::CodecSampleRateHz(codec); + int ret = decoder_database_->InsertExternal(rtp_payload_type, codec, + sample_rate_hz, decoder); + if (ret != DecoderDatabase::kOK) { + LOG_FERR2(LS_WARNING, InsertExternal, rtp_payload_type, codec); + switch (ret) { + case DecoderDatabase::kInvalidRtpPayloadType: + error_code_ = kInvalidRtpPayloadType; + break; + case DecoderDatabase::kCodecNotSupported: + error_code_ = kCodecNotSupported; + break; + case DecoderDatabase::kDecoderExists: + error_code_ = kDecoderExists; + break; + case DecoderDatabase::kInvalidSampleRate: + error_code_ = kInvalidSampleRate; + break; + case DecoderDatabase::kInvalidPointer: + error_code_ = kInvalidPointer; + break; + default: + error_code_ = kOtherError; + } + return kFail; + } + return kOK; +} + +int NetEqImpl::RemovePayloadType(uint8_t rtp_payload_type) { + CriticalSectionScoped lock(crit_sect_.get()); + LOG_API1(static_cast(rtp_payload_type)); + int ret = decoder_database_->Remove(rtp_payload_type); + if (ret == DecoderDatabase::kOK) { + return kOK; + } else if (ret == DecoderDatabase::kDecoderNotFound) { + error_code_ = kDecoderNotFound; + } else { + error_code_ = kOtherError; + } + LOG_FERR1(LS_WARNING, Remove, rtp_payload_type); + return kFail; +} + +bool NetEqImpl::SetMinimumDelay(int delay_ms) { + CriticalSectionScoped lock(crit_sect_.get()); + if (delay_ms >= 0 && delay_ms < 10000) { + assert(delay_manager_.get()); + return delay_manager_->SetMinimumDelay(delay_ms); + } + return false; +} + +bool NetEqImpl::SetMaximumDelay(int delay_ms) { + CriticalSectionScoped lock(crit_sect_.get()); + if (delay_ms >= 0 && delay_ms < 10000) { + assert(delay_manager_.get()); + return delay_manager_->SetMaximumDelay(delay_ms); + } + return false; +} + +int NetEqImpl::LeastRequiredDelayMs() const { + CriticalSectionScoped lock(crit_sect_.get()); + assert(delay_manager_.get()); + return delay_manager_->least_required_delay_ms(); +} + +// Deprecated. +// TODO(henrik.lundin) Delete. +void NetEqImpl::SetPlayoutMode(NetEqPlayoutMode mode) { + CriticalSectionScoped lock(crit_sect_.get()); + if (mode != playout_mode_) { + playout_mode_ = mode; + CreateDecisionLogic(); + } +} + +// Deprecated. +// TODO(henrik.lundin) Delete. +NetEqPlayoutMode NetEqImpl::PlayoutMode() const { + CriticalSectionScoped lock(crit_sect_.get()); + return playout_mode_; +} + +int NetEqImpl::NetworkStatistics(NetEqNetworkStatistics* stats) { + CriticalSectionScoped lock(crit_sect_.get()); + assert(decoder_database_.get()); + const int total_samples_in_buffers = packet_buffer_->NumSamplesInBuffer( + decoder_database_.get(), decoder_frame_length_) + + static_cast(sync_buffer_->FutureLength()); + assert(delay_manager_.get()); + assert(decision_logic_.get()); + stats_.GetNetworkStatistics(fs_hz_, total_samples_in_buffers, + decoder_frame_length_, *delay_manager_.get(), + *decision_logic_.get(), stats); + return 0; +} + +void NetEqImpl::WaitingTimes(std::vector* waiting_times) { + CriticalSectionScoped lock(crit_sect_.get()); + stats_.WaitingTimes(waiting_times); +} + +void NetEqImpl::GetRtcpStatistics(RtcpStatistics* stats) { + CriticalSectionScoped lock(crit_sect_.get()); + if (stats) { + rtcp_.GetStatistics(false, stats); + } +} + +void NetEqImpl::GetRtcpStatisticsNoReset(RtcpStatistics* stats) { + CriticalSectionScoped lock(crit_sect_.get()); + if (stats) { + rtcp_.GetStatistics(true, stats); + } +} + +void NetEqImpl::EnableVad() { + CriticalSectionScoped lock(crit_sect_.get()); + assert(vad_.get()); + vad_->Enable(); +} + +void NetEqImpl::DisableVad() { + CriticalSectionScoped lock(crit_sect_.get()); + assert(vad_.get()); + vad_->Disable(); +} + +bool NetEqImpl::GetPlayoutTimestamp(uint32_t* timestamp) { + CriticalSectionScoped lock(crit_sect_.get()); + if (first_packet_) { + // We don't have a valid RTP timestamp until we have decoded our first + // RTP packet. + return false; + } + *timestamp = timestamp_scaler_->ToExternal(playout_timestamp_); + return true; +} + +int NetEqImpl::LastError() const { + CriticalSectionScoped lock(crit_sect_.get()); + return error_code_; +} + +int NetEqImpl::LastDecoderError() { + CriticalSectionScoped lock(crit_sect_.get()); + return decoder_error_code_; +} + +void NetEqImpl::FlushBuffers() { + CriticalSectionScoped lock(crit_sect_.get()); + LOG_API0(); + packet_buffer_->Flush(); + assert(sync_buffer_.get()); + assert(expand_.get()); + sync_buffer_->Flush(); + sync_buffer_->set_next_index(sync_buffer_->next_index() - + expand_->overlap_length()); + // Set to wait for new codec. + first_packet_ = true; +} + +void NetEqImpl::PacketBufferStatistics(int* current_num_packets, + int* max_num_packets) const { + CriticalSectionScoped lock(crit_sect_.get()); + packet_buffer_->BufferStat(current_num_packets, max_num_packets); +} + +int NetEqImpl::DecodedRtpInfo(int* sequence_number, uint32_t* timestamp) const { + CriticalSectionScoped lock(crit_sect_.get()); + if (decoded_packet_sequence_number_ < 0) + return -1; + *sequence_number = decoded_packet_sequence_number_; + *timestamp = decoded_packet_timestamp_; + return 0; +} + +const SyncBuffer* NetEqImpl::sync_buffer_for_test() const { + CriticalSectionScoped lock(crit_sect_.get()); + return sync_buffer_.get(); +} + +// Methods below this line are private. + +int NetEqImpl::InsertPacketInternal(const WebRtcRTPHeader& rtp_header, + const uint8_t* payload, + int length_bytes, + uint32_t receive_timestamp, + bool is_sync_packet) { + if (!payload) { + LOG_F(LS_ERROR) << "payload == NULL"; + return kInvalidPointer; + } + // Sanity checks for sync-packets. + if (is_sync_packet) { + if (decoder_database_->IsDtmf(rtp_header.header.payloadType) || + decoder_database_->IsRed(rtp_header.header.payloadType) || + decoder_database_->IsComfortNoise(rtp_header.header.payloadType)) { + LOG_F(LS_ERROR) << "Sync-packet with an unacceptable payload type " + << rtp_header.header.payloadType; + return kSyncPacketNotAccepted; + } + if (first_packet_ || + rtp_header.header.payloadType != current_rtp_payload_type_ || + rtp_header.header.ssrc != ssrc_) { + // Even if |current_rtp_payload_type_| is 0xFF, sync-packet isn't + // accepted. + LOG_F(LS_ERROR) << "Changing codec, SSRC or first packet " + "with sync-packet."; + return kSyncPacketNotAccepted; + } + } + PacketList packet_list; + RTPHeader main_header; + { + // Convert to Packet. + // Create |packet| within this separate scope, since it should not be used + // directly once it's been inserted in the packet list. This way, |packet| + // is not defined outside of this block. + Packet* packet = new Packet; + packet->header.markerBit = false; + packet->header.payloadType = rtp_header.header.payloadType; + packet->header.sequenceNumber = rtp_header.header.sequenceNumber; + packet->header.timestamp = rtp_header.header.timestamp; + packet->header.ssrc = rtp_header.header.ssrc; + packet->header.numCSRCs = 0; + packet->payload_length = length_bytes; + packet->primary = true; + packet->waiting_time = 0; + packet->payload = new uint8_t[packet->payload_length]; + packet->sync_packet = is_sync_packet; + if (!packet->payload) { + LOG_F(LS_ERROR) << "Payload pointer is NULL."; + } + assert(payload); // Already checked above. + memcpy(packet->payload, payload, packet->payload_length); + // Insert packet in a packet list. + packet_list.push_back(packet); + // Save main payloads header for later. + memcpy(&main_header, &packet->header, sizeof(main_header)); + } + + bool update_sample_rate_and_channels = false; + // Reinitialize NetEq if it's needed (changed SSRC or first call). + if ((main_header.ssrc != ssrc_) || first_packet_) { + rtcp_.Init(main_header.sequenceNumber); + first_packet_ = false; + + // Flush the packet buffer and DTMF buffer. + packet_buffer_->Flush(); + dtmf_buffer_->Flush(); + + // Store new SSRC. + ssrc_ = main_header.ssrc; + + // Update audio buffer timestamp. + sync_buffer_->IncreaseEndTimestamp(main_header.timestamp - timestamp_); + + // Update codecs. + timestamp_ = main_header.timestamp; + current_rtp_payload_type_ = main_header.payloadType; + + // Set MCU to update codec on next SignalMCU call. + new_codec_ = true; + + // Reset timestamp scaling. + timestamp_scaler_->Reset(); + + // Triger an update of sampling rate and the number of channels. + update_sample_rate_and_channels = true; + } + + // Update RTCP statistics, only for regular packets. + if (!is_sync_packet) + rtcp_.Update(main_header, receive_timestamp); + + // Check for RED payload type, and separate payloads into several packets. + if (decoder_database_->IsRed(main_header.payloadType)) { + assert(!is_sync_packet); // We had a sanity check for this. + if (payload_splitter_->SplitRed(&packet_list) != PayloadSplitter::kOK) { + LOG_FERR1(LS_WARNING, SplitRed, packet_list.size()); + PacketBuffer::DeleteAllPackets(&packet_list); + return kRedundancySplitError; + } + // Only accept a few RED payloads of the same type as the main data, + // DTMF events and CNG. + payload_splitter_->CheckRedPayloads(&packet_list, *decoder_database_); + // Update the stored main payload header since the main payload has now + // changed. + memcpy(&main_header, &packet_list.front()->header, sizeof(main_header)); + } + + // Check payload types. + if (decoder_database_->CheckPayloadTypes(packet_list) == + DecoderDatabase::kDecoderNotFound) { + LOG_FERR1(LS_WARNING, CheckPayloadTypes, packet_list.size()); + PacketBuffer::DeleteAllPackets(&packet_list); + return kUnknownRtpPayloadType; + } + + // Scale timestamp to internal domain (only for some codecs). + timestamp_scaler_->ToInternal(&packet_list); + + // Process DTMF payloads. Cycle through the list of packets, and pick out any + // DTMF payloads found. + PacketList::iterator it = packet_list.begin(); + while (it != packet_list.end()) { + Packet* current_packet = (*it); + assert(current_packet); + assert(current_packet->payload); + if (decoder_database_->IsDtmf(current_packet->header.payloadType)) { + assert(!current_packet->sync_packet); // We had a sanity check for this. + DtmfEvent event; + int ret = DtmfBuffer::ParseEvent( + current_packet->header.timestamp, + current_packet->payload, + current_packet->payload_length, + &event); + if (ret != DtmfBuffer::kOK) { + LOG_FERR2(LS_WARNING, ParseEvent, ret, + current_packet->payload_length); + PacketBuffer::DeleteAllPackets(&packet_list); + return kDtmfParsingError; + } + if (dtmf_buffer_->InsertEvent(event) != DtmfBuffer::kOK) { + LOG_FERR0(LS_WARNING, InsertEvent); + PacketBuffer::DeleteAllPackets(&packet_list); + return kDtmfInsertError; + } + // TODO(hlundin): Let the destructor of Packet handle the payload. + delete [] current_packet->payload; + delete current_packet; + it = packet_list.erase(it); + } else { + ++it; + } + } + + // Check for FEC in packets, and separate payloads into several packets. + int ret = payload_splitter_->SplitFec(&packet_list, decoder_database_.get()); + if (ret != PayloadSplitter::kOK) { + LOG_FERR1(LS_WARNING, SplitFec, packet_list.size()); + PacketBuffer::DeleteAllPackets(&packet_list); + switch (ret) { + case PayloadSplitter::kUnknownPayloadType: + return kUnknownRtpPayloadType; + default: + return kOtherError; + } + } + + // Split payloads into smaller chunks. This also verifies that all payloads + // are of a known payload type. SplitAudio() method is protected against + // sync-packets. + ret = payload_splitter_->SplitAudio(&packet_list, *decoder_database_); + if (ret != PayloadSplitter::kOK) { + LOG_FERR1(LS_WARNING, SplitAudio, packet_list.size()); + PacketBuffer::DeleteAllPackets(&packet_list); + switch (ret) { + case PayloadSplitter::kUnknownPayloadType: + return kUnknownRtpPayloadType; + case PayloadSplitter::kFrameSplitError: + return kFrameSplitError; + default: + return kOtherError; + } + } + + // Update bandwidth estimate, if the packet is not sync-packet. + if (!packet_list.empty() && !packet_list.front()->sync_packet) { + // The list can be empty here if we got nothing but DTMF payloads. + AudioDecoder* decoder = + decoder_database_->GetDecoder(main_header.payloadType); + assert(decoder); // Should always get a valid object, since we have + // already checked that the payload types are known. + decoder->IncomingPacket(packet_list.front()->payload, + packet_list.front()->payload_length, + packet_list.front()->header.sequenceNumber, + packet_list.front()->header.timestamp, + receive_timestamp); + } + + // Insert packets in buffer. + int temp_bufsize = packet_buffer_->NumPacketsInBuffer(); + ret = packet_buffer_->InsertPacketList( + &packet_list, + *decoder_database_, + ¤t_rtp_payload_type_, + ¤t_cng_rtp_payload_type_); + if (ret == PacketBuffer::kFlushed) { + // Reset DSP timestamp etc. if packet buffer flushed. + new_codec_ = true; + update_sample_rate_and_channels = true; + LOG_F(LS_WARNING) << "Packet buffer flushed"; + } else if (ret != PacketBuffer::kOK) { + LOG_FERR1(LS_WARNING, InsertPacketList, packet_list.size()); + PacketBuffer::DeleteAllPackets(&packet_list); + return kOtherError; + } + if (current_rtp_payload_type_ != 0xFF) { + const DecoderDatabase::DecoderInfo* dec_info = + decoder_database_->GetDecoderInfo(current_rtp_payload_type_); + if (!dec_info) { + assert(false); // Already checked that the payload type is known. + } + } + + if (update_sample_rate_and_channels && !packet_buffer_->Empty()) { + // We do not use |current_rtp_payload_type_| to |set payload_type|, but + // get the next RTP header from |packet_buffer_| to obtain the payload type. + // The reason for it is the following corner case. If NetEq receives a + // CNG packet with a sample rate different than the current CNG then it + // flushes its buffer, assuming send codec must have been changed. However, + // payload type of the hypothetically new send codec is not known. + const RTPHeader* rtp_header = packet_buffer_->NextRtpHeader(); + assert(rtp_header); + int payload_type = rtp_header->payloadType; + AudioDecoder* decoder = decoder_database_->GetDecoder(payload_type); + assert(decoder); // Payloads are already checked to be valid. + const DecoderDatabase::DecoderInfo* decoder_info = + decoder_database_->GetDecoderInfo(payload_type); + assert(decoder_info); + if (decoder_info->fs_hz != fs_hz_ || + decoder->channels() != algorithm_buffer_->Channels()) + SetSampleRateAndChannels(decoder_info->fs_hz, decoder->channels()); + } + + // TODO(hlundin): Move this code to DelayManager class. + const DecoderDatabase::DecoderInfo* dec_info = + decoder_database_->GetDecoderInfo(main_header.payloadType); + assert(dec_info); // Already checked that the payload type is known. + delay_manager_->LastDecoderType(dec_info->codec_type); + if (delay_manager_->last_pack_cng_or_dtmf() == 0) { + // Calculate the total speech length carried in each packet. + temp_bufsize = packet_buffer_->NumPacketsInBuffer() - temp_bufsize; + temp_bufsize *= decoder_frame_length_; + + if ((temp_bufsize > 0) && + (temp_bufsize != decision_logic_->packet_length_samples())) { + decision_logic_->set_packet_length_samples(temp_bufsize); + delay_manager_->SetPacketAudioLength((1000 * temp_bufsize) / fs_hz_); + } + + // Update statistics. + if ((int32_t) (main_header.timestamp - timestamp_) >= 0 && + !new_codec_) { + // Only update statistics if incoming packet is not older than last played + // out packet, and if new codec flag is not set. + delay_manager_->Update(main_header.sequenceNumber, main_header.timestamp, + fs_hz_); + } + } else if (delay_manager_->last_pack_cng_or_dtmf() == -1) { + // This is first "normal" packet after CNG or DTMF. + // Reset packet time counter and measure time until next packet, + // but don't update statistics. + delay_manager_->set_last_pack_cng_or_dtmf(0); + delay_manager_->ResetPacketIatCount(); + } + return 0; +} + +int NetEqImpl::GetAudioInternal(size_t max_length, int16_t* output, + int* samples_per_channel, int* num_channels) { + PacketList packet_list; + DtmfEvent dtmf_event; + Operations operation; + bool play_dtmf; + int return_value = GetDecision(&operation, &packet_list, &dtmf_event, + &play_dtmf); + if (return_value != 0) { + LOG_FERR1(LS_WARNING, GetDecision, return_value); + assert(false); + last_mode_ = kModeError; + return return_value; + } + LOG(LS_VERBOSE) << "GetDecision returned operation=" << operation << + " and " << packet_list.size() << " packet(s)"; + + AudioDecoder::SpeechType speech_type; + int length = 0; + int decode_return_value = Decode(&packet_list, &operation, + &length, &speech_type); + + assert(vad_.get()); + bool sid_frame_available = + (operation == kRfc3389Cng && !packet_list.empty()); + vad_->Update(decoded_buffer_.get(), length, speech_type, + sid_frame_available, fs_hz_); + + algorithm_buffer_->Clear(); + switch (operation) { + case kNormal: { + DoNormal(decoded_buffer_.get(), length, speech_type, play_dtmf); + break; + } + case kMerge: { + DoMerge(decoded_buffer_.get(), length, speech_type, play_dtmf); + break; + } + case kExpand: { + return_value = DoExpand(play_dtmf); + break; + } + case kAccelerate: { + return_value = DoAccelerate(decoded_buffer_.get(), length, speech_type, + play_dtmf); + break; + } + case kPreemptiveExpand: { + return_value = DoPreemptiveExpand(decoded_buffer_.get(), length, + speech_type, play_dtmf); + break; + } + case kRfc3389Cng: + case kRfc3389CngNoPacket: { + return_value = DoRfc3389Cng(&packet_list, play_dtmf); + break; + } + case kCodecInternalCng: { + // This handles the case when there is no transmission and the decoder + // should produce internal comfort noise. + // TODO(hlundin): Write test for codec-internal CNG. + DoCodecInternalCng(); + break; + } + case kDtmf: { + // TODO(hlundin): Write test for this. + return_value = DoDtmf(dtmf_event, &play_dtmf); + break; + } + case kAlternativePlc: { + // TODO(hlundin): Write test for this. + DoAlternativePlc(false); + break; + } + case kAlternativePlcIncreaseTimestamp: { + // TODO(hlundin): Write test for this. + DoAlternativePlc(true); + break; + } + case kAudioRepetitionIncreaseTimestamp: { + // TODO(hlundin): Write test for this. + sync_buffer_->IncreaseEndTimestamp(output_size_samples_); + // Skipping break on purpose. Execution should move on into the + // next case. + } + case kAudioRepetition: { + // TODO(hlundin): Write test for this. + // Copy last |output_size_samples_| from |sync_buffer_| to + // |algorithm_buffer|. + algorithm_buffer_->PushBackFromIndex( + *sync_buffer_, sync_buffer_->Size() - output_size_samples_); + expand_->Reset(); + break; + } + case kUndefined: { + LOG_F(LS_ERROR) << "Invalid operation kUndefined."; + assert(false); // This should not happen. + last_mode_ = kModeError; + return kInvalidOperation; + } + } // End of switch. + if (return_value < 0) { + return return_value; + } + + if (last_mode_ != kModeRfc3389Cng) { + comfort_noise_->Reset(); + } + + // Copy from |algorithm_buffer| to |sync_buffer_|. + sync_buffer_->PushBack(*algorithm_buffer_); + + // Extract data from |sync_buffer_| to |output|. + size_t num_output_samples_per_channel = output_size_samples_; + size_t num_output_samples = output_size_samples_ * sync_buffer_->Channels(); + if (num_output_samples > max_length) { + LOG(LS_WARNING) << "Output array is too short. " << max_length << " < " << + output_size_samples_ << " * " << sync_buffer_->Channels(); + num_output_samples = max_length; + num_output_samples_per_channel = static_cast( + max_length / sync_buffer_->Channels()); + } + int samples_from_sync = static_cast( + sync_buffer_->GetNextAudioInterleaved(num_output_samples_per_channel, + output)); + *num_channels = static_cast(sync_buffer_->Channels()); + LOG(LS_VERBOSE) << "Sync buffer (" << *num_channels << " channel(s)):" << + " insert " << algorithm_buffer_->Size() << " samples, extract " << + samples_from_sync << " samples"; + if (samples_from_sync != output_size_samples_) { + LOG_F(LS_ERROR) << "samples_from_sync != output_size_samples_"; + // TODO(minyue): treatment of under-run, filling zeros + memset(output, 0, num_output_samples * sizeof(int16_t)); + *samples_per_channel = output_size_samples_; + return kSampleUnderrun; + } + *samples_per_channel = output_size_samples_; + + // Should always have overlap samples left in the |sync_buffer_|. + assert(sync_buffer_->FutureLength() >= expand_->overlap_length()); + + if (play_dtmf) { + return_value = DtmfOverdub(dtmf_event, sync_buffer_->Channels(), output); + } + + // Update the background noise parameters if last operation wrote data + // straight from the decoder to the |sync_buffer_|. That is, none of the + // operations that modify the signal can be followed by a parameter update. + if ((last_mode_ == kModeNormal) || + (last_mode_ == kModeAccelerateFail) || + (last_mode_ == kModePreemptiveExpandFail) || + (last_mode_ == kModeRfc3389Cng) || + (last_mode_ == kModeCodecInternalCng)) { + background_noise_->Update(*sync_buffer_, *vad_.get()); + } + + if (operation == kDtmf) { + // DTMF data was written the end of |sync_buffer_|. + // Update index to end of DTMF data in |sync_buffer_|. + sync_buffer_->set_dtmf_index(sync_buffer_->Size()); + } + + if (last_mode_ != kModeExpand) { + // If last operation was not expand, calculate the |playout_timestamp_| from + // the |sync_buffer_|. However, do not update the |playout_timestamp_| if it + // would be moved "backwards". + uint32_t temp_timestamp = sync_buffer_->end_timestamp() - + static_cast(sync_buffer_->FutureLength()); + if (static_cast(temp_timestamp - playout_timestamp_) > 0) { + playout_timestamp_ = temp_timestamp; + } + } else { + // Use dead reckoning to estimate the |playout_timestamp_|. + playout_timestamp_ += output_size_samples_; + } + + if (decode_return_value) return decode_return_value; + return return_value; +} + +int NetEqImpl::GetDecision(Operations* operation, + PacketList* packet_list, + DtmfEvent* dtmf_event, + bool* play_dtmf) { + // Initialize output variables. + *play_dtmf = false; + *operation = kUndefined; + + // Increment time counters. + packet_buffer_->IncrementWaitingTimes(); + stats_.IncreaseCounter(output_size_samples_, fs_hz_); + + assert(sync_buffer_.get()); + uint32_t end_timestamp = sync_buffer_->end_timestamp(); + if (!new_codec_) { + const uint32_t five_seconds_samples = 5 * fs_hz_; + packet_buffer_->DiscardOldPackets(end_timestamp, five_seconds_samples); + } + const RTPHeader* header = packet_buffer_->NextRtpHeader(); + + if (decision_logic_->CngRfc3389On() || last_mode_ == kModeRfc3389Cng) { + // Because of timestamp peculiarities, we have to "manually" disallow using + // a CNG packet with the same timestamp as the one that was last played. + // This can happen when using redundancy and will cause the timing to shift. + while (header && decoder_database_->IsComfortNoise(header->payloadType) && + (end_timestamp >= header->timestamp || + end_timestamp + decision_logic_->generated_noise_samples() > + header->timestamp)) { + // Don't use this packet, discard it. + if (packet_buffer_->DiscardNextPacket() != PacketBuffer::kOK) { + assert(false); // Must be ok by design. + } + // Check buffer again. + if (!new_codec_) { + packet_buffer_->DiscardOldPackets(end_timestamp, 5 * fs_hz_); + } + header = packet_buffer_->NextRtpHeader(); + } + } + + assert(expand_.get()); + const int samples_left = static_cast(sync_buffer_->FutureLength() - + expand_->overlap_length()); + if (last_mode_ == kModeAccelerateSuccess || + last_mode_ == kModeAccelerateLowEnergy || + last_mode_ == kModePreemptiveExpandSuccess || + last_mode_ == kModePreemptiveExpandLowEnergy) { + // Subtract (samples_left + output_size_samples_) from sampleMemory. + decision_logic_->AddSampleMemory(-(samples_left + output_size_samples_)); + } + + // Check if it is time to play a DTMF event. + if (dtmf_buffer_->GetEvent(end_timestamp + + decision_logic_->generated_noise_samples(), + dtmf_event)) { + *play_dtmf = true; + } + + // Get instruction. + assert(sync_buffer_.get()); + assert(expand_.get()); + *operation = decision_logic_->GetDecision(*sync_buffer_, + *expand_, + decoder_frame_length_, + header, + last_mode_, + *play_dtmf, + &reset_decoder_); + + // Check if we already have enough samples in the |sync_buffer_|. If so, + // change decision to normal, unless the decision was merge, accelerate, or + // preemptive expand. + if (samples_left >= output_size_samples_ && + *operation != kMerge && + *operation != kAccelerate && + *operation != kPreemptiveExpand) { + *operation = kNormal; + return 0; + } + + decision_logic_->ExpandDecision(*operation); + + // Check conditions for reset. + if (new_codec_ || *operation == kUndefined) { + // The only valid reason to get kUndefined is that new_codec_ is set. + assert(new_codec_); + if (*play_dtmf && !header) { + timestamp_ = dtmf_event->timestamp; + } else { + assert(header); + if (!header) { + LOG_F(LS_ERROR) << "Packet missing where it shouldn't."; + return -1; + } + timestamp_ = header->timestamp; + if (*operation == kRfc3389CngNoPacket +#ifndef LEGACY_BITEXACT + // Without this check, it can happen that a non-CNG packet is sent to + // the CNG decoder as if it was a SID frame. This is clearly a bug, + // but is kept for now to maintain bit-exactness with the test + // vectors. + && decoder_database_->IsComfortNoise(header->payloadType) +#endif + ) { + // Change decision to CNG packet, since we do have a CNG packet, but it + // was considered too early to use. Now, use it anyway. + *operation = kRfc3389Cng; + } else if (*operation != kRfc3389Cng) { + *operation = kNormal; + } + } + // Adjust |sync_buffer_| timestamp before setting |end_timestamp| to the + // new value. + sync_buffer_->IncreaseEndTimestamp(timestamp_ - end_timestamp); + end_timestamp = timestamp_; + new_codec_ = false; + decision_logic_->SoftReset(); + buffer_level_filter_->Reset(); + delay_manager_->Reset(); + stats_.ResetMcu(); + } + + int required_samples = output_size_samples_; + const int samples_10_ms = 80 * fs_mult_; + const int samples_20_ms = 2 * samples_10_ms; + const int samples_30_ms = 3 * samples_10_ms; + + switch (*operation) { + case kExpand: { + timestamp_ = end_timestamp; + return 0; + } + case kRfc3389CngNoPacket: + case kCodecInternalCng: { + return 0; + } + case kDtmf: { + // TODO(hlundin): Write test for this. + // Update timestamp. + timestamp_ = end_timestamp; + if (decision_logic_->generated_noise_samples() > 0 && + last_mode_ != kModeDtmf) { + // Make a jump in timestamp due to the recently played comfort noise. + uint32_t timestamp_jump = decision_logic_->generated_noise_samples(); + sync_buffer_->IncreaseEndTimestamp(timestamp_jump); + timestamp_ += timestamp_jump; + } + decision_logic_->set_generated_noise_samples(0); + return 0; + } + case kAccelerate: { + // In order to do a accelerate we need at least 30 ms of audio data. + if (samples_left >= samples_30_ms) { + // Already have enough data, so we do not need to extract any more. + decision_logic_->set_sample_memory(samples_left); + decision_logic_->set_prev_time_scale(true); + return 0; + } else if (samples_left >= samples_10_ms && + decoder_frame_length_ >= samples_30_ms) { + // Avoid decoding more data as it might overflow the playout buffer. + *operation = kNormal; + return 0; + } else if (samples_left < samples_20_ms && + decoder_frame_length_ < samples_30_ms) { + // Build up decoded data by decoding at least 20 ms of audio data. Do + // not perform accelerate yet, but wait until we only need to do one + // decoding. + required_samples = 2 * output_size_samples_; + *operation = kNormal; + } + // If none of the above is true, we have one of two possible situations: + // (1) 20 ms <= samples_left < 30 ms and decoder_frame_length_ < 30 ms; or + // (2) samples_left < 10 ms and decoder_frame_length_ >= 30 ms. + // In either case, we move on with the accelerate decision, and decode one + // frame now. + break; + } + case kPreemptiveExpand: { + // In order to do a preemptive expand we need at least 30 ms of decoded + // audio data. + if ((samples_left >= samples_30_ms) || + (samples_left >= samples_10_ms && + decoder_frame_length_ >= samples_30_ms)) { + // Already have enough data, so we do not need to extract any more. + // Or, avoid decoding more data as it might overflow the playout buffer. + // Still try preemptive expand, though. + decision_logic_->set_sample_memory(samples_left); + decision_logic_->set_prev_time_scale(true); + return 0; + } + if (samples_left < samples_20_ms && + decoder_frame_length_ < samples_30_ms) { + // Build up decoded data by decoding at least 20 ms of audio data. + // Still try to perform preemptive expand. + required_samples = 2 * output_size_samples_; + } + // Move on with the preemptive expand decision. + break; + } + case kMerge: { + required_samples = + std::max(merge_->RequiredFutureSamples(), required_samples); + break; + } + default: { + // Do nothing. + } + } + + // Get packets from buffer. + int extracted_samples = 0; + if (header && + *operation != kAlternativePlc && + *operation != kAlternativePlcIncreaseTimestamp && + *operation != kAudioRepetition && + *operation != kAudioRepetitionIncreaseTimestamp) { + sync_buffer_->IncreaseEndTimestamp(header->timestamp - end_timestamp); + if (decision_logic_->CngOff()) { + // Adjustment of timestamp only corresponds to an actual packet loss + // if comfort noise is not played. If comfort noise was just played, + // this adjustment of timestamp is only done to get back in sync with the + // stream timestamp; no loss to report. + stats_.LostSamples(header->timestamp - end_timestamp); + } + + if (*operation != kRfc3389Cng) { + // We are about to decode and use a non-CNG packet. + decision_logic_->SetCngOff(); + } + // Reset CNG timestamp as a new packet will be delivered. + // (Also if this is a CNG packet, since playedOutTS is updated.) + decision_logic_->set_generated_noise_samples(0); + + extracted_samples = ExtractPackets(required_samples, packet_list); + if (extracted_samples < 0) { + LOG_F(LS_WARNING) << "Failed to extract packets from buffer."; + return kPacketBufferCorruption; + } + } + + if (*operation == kAccelerate || + *operation == kPreemptiveExpand) { + decision_logic_->set_sample_memory(samples_left + extracted_samples); + decision_logic_->set_prev_time_scale(true); + } + + if (*operation == kAccelerate) { + // Check that we have enough data (30ms) to do accelerate. + if (extracted_samples + samples_left < samples_30_ms) { + // TODO(hlundin): Write test for this. + // Not enough, do normal operation instead. + *operation = kNormal; + } + } + + timestamp_ = end_timestamp; + return 0; +} + +int NetEqImpl::Decode(PacketList* packet_list, Operations* operation, + int* decoded_length, + AudioDecoder::SpeechType* speech_type) { + *speech_type = AudioDecoder::kSpeech; + AudioDecoder* decoder = NULL; + if (!packet_list->empty()) { + const Packet* packet = packet_list->front(); + int payload_type = packet->header.payloadType; + if (!decoder_database_->IsComfortNoise(payload_type)) { + decoder = decoder_database_->GetDecoder(payload_type); + assert(decoder); + if (!decoder) { + LOG_FERR1(LS_WARNING, GetDecoder, payload_type); + PacketBuffer::DeleteAllPackets(packet_list); + return kDecoderNotFound; + } + bool decoder_changed; + decoder_database_->SetActiveDecoder(payload_type, &decoder_changed); + if (decoder_changed) { + // We have a new decoder. Re-init some values. + const DecoderDatabase::DecoderInfo* decoder_info = decoder_database_ + ->GetDecoderInfo(payload_type); + assert(decoder_info); + if (!decoder_info) { + LOG_FERR1(LS_WARNING, GetDecoderInfo, payload_type); + PacketBuffer::DeleteAllPackets(packet_list); + return kDecoderNotFound; + } + // If sampling rate or number of channels has changed, we need to make + // a reset. + if (decoder_info->fs_hz != fs_hz_ || + decoder->channels() != algorithm_buffer_->Channels()) { + // TODO(tlegrand): Add unittest to cover this event. + SetSampleRateAndChannels(decoder_info->fs_hz, decoder->channels()); + } + sync_buffer_->set_end_timestamp(timestamp_); + playout_timestamp_ = timestamp_; + } + } + } + + if (reset_decoder_) { + // TODO(hlundin): Write test for this. + // Reset decoder. + if (decoder) { + decoder->Init(); + } + // Reset comfort noise decoder. + AudioDecoder* cng_decoder = decoder_database_->GetActiveCngDecoder(); + if (cng_decoder) { + cng_decoder->Init(); + } + reset_decoder_ = false; + } + +#ifdef LEGACY_BITEXACT + // Due to a bug in old SignalMCU, it could happen that CNG operation was + // decided, but a speech packet was provided. The speech packet will be used + // to update the comfort noise decoder, as if it was a SID frame, which is + // clearly wrong. + if (*operation == kRfc3389Cng) { + return 0; + } +#endif + + *decoded_length = 0; + // Update codec-internal PLC state. + if ((*operation == kMerge) && decoder && decoder->HasDecodePlc()) { + decoder->DecodePlc(1, &decoded_buffer_[*decoded_length]); + } + + int return_value = DecodeLoop(packet_list, operation, decoder, + decoded_length, speech_type); + + if (*decoded_length < 0) { + // Error returned from the decoder. + *decoded_length = 0; + sync_buffer_->IncreaseEndTimestamp(decoder_frame_length_); + int error_code = 0; + if (decoder) + error_code = decoder->ErrorCode(); + if (error_code != 0) { + // Got some error code from the decoder. + decoder_error_code_ = error_code; + return_value = kDecoderErrorCode; + } else { + // Decoder does not implement error codes. Return generic error. + return_value = kOtherDecoderError; + } + LOG_FERR2(LS_WARNING, DecodeLoop, error_code, packet_list->size()); + *operation = kExpand; // Do expansion to get data instead. + } + if (*speech_type != AudioDecoder::kComfortNoise) { + // Don't increment timestamp if codec returned CNG speech type + // since in this case, the we will increment the CNGplayedTS counter. + // Increase with number of samples per channel. + assert(*decoded_length == 0 || + (decoder && decoder->channels() == sync_buffer_->Channels())); + sync_buffer_->IncreaseEndTimestamp( + *decoded_length / static_cast(sync_buffer_->Channels())); + } + return return_value; +} + +int NetEqImpl::DecodeLoop(PacketList* packet_list, Operations* operation, + AudioDecoder* decoder, int* decoded_length, + AudioDecoder::SpeechType* speech_type) { + Packet* packet = NULL; + if (!packet_list->empty()) { + packet = packet_list->front(); + } + // Do decoding. + while (packet && + !decoder_database_->IsComfortNoise(packet->header.payloadType)) { + assert(decoder); // At this point, we must have a decoder object. + // The number of channels in the |sync_buffer_| should be the same as the + // number decoder channels. + assert(sync_buffer_->Channels() == decoder->channels()); + assert(decoded_buffer_length_ >= kMaxFrameSize * decoder->channels()); + assert(*operation == kNormal || *operation == kAccelerate || + *operation == kMerge || *operation == kPreemptiveExpand); + packet_list->pop_front(); + int payload_length = packet->payload_length; + int16_t decode_length; + if (packet->sync_packet) { + // Decode to silence with the same frame size as the last decode. + LOG(LS_VERBOSE) << "Decoding sync-packet: " << + " ts=" << packet->header.timestamp << + ", sn=" << packet->header.sequenceNumber << + ", pt=" << static_cast(packet->header.payloadType) << + ", ssrc=" << packet->header.ssrc << + ", len=" << packet->payload_length; + memset(&decoded_buffer_[*decoded_length], 0, decoder_frame_length_ * + decoder->channels() * sizeof(decoded_buffer_[0])); + decode_length = decoder_frame_length_; + } else if (!packet->primary) { + // This is a redundant payload; call the special decoder method. + LOG(LS_VERBOSE) << "Decoding packet (redundant):" << + " ts=" << packet->header.timestamp << + ", sn=" << packet->header.sequenceNumber << + ", pt=" << static_cast(packet->header.payloadType) << + ", ssrc=" << packet->header.ssrc << + ", len=" << packet->payload_length; + decode_length = decoder->DecodeRedundant( + packet->payload, packet->payload_length, + &decoded_buffer_[*decoded_length], speech_type); + } else { + LOG(LS_VERBOSE) << "Decoding packet: ts=" << packet->header.timestamp << + ", sn=" << packet->header.sequenceNumber << + ", pt=" << static_cast(packet->header.payloadType) << + ", ssrc=" << packet->header.ssrc << + ", len=" << packet->payload_length; + decode_length = decoder->Decode(packet->payload, + packet->payload_length, + &decoded_buffer_[*decoded_length], + speech_type); + } + + delete[] packet->payload; + delete packet; + packet = NULL; + if (decode_length > 0) { + *decoded_length += decode_length; + // Update |decoder_frame_length_| with number of samples per channel. + decoder_frame_length_ = decode_length / + static_cast(decoder->channels()); + LOG(LS_VERBOSE) << "Decoded " << decode_length << " samples (" << + decoder->channels() << " channel(s) -> " << decoder_frame_length_ << + " samples per channel)"; + } else if (decode_length < 0) { + // Error. + LOG_FERR2(LS_WARNING, Decode, decode_length, payload_length); + *decoded_length = -1; + PacketBuffer::DeleteAllPackets(packet_list); + break; + } + if (*decoded_length > static_cast(decoded_buffer_length_)) { + // Guard against overflow. + LOG_F(LS_WARNING) << "Decoded too much."; + PacketBuffer::DeleteAllPackets(packet_list); + return kDecodedTooMuch; + } + if (!packet_list->empty()) { + packet = packet_list->front(); + } else { + packet = NULL; + } + } // End of decode loop. + + // If the list is not empty at this point, either a decoding error terminated + // the while-loop, or list must hold exactly one CNG packet. + assert(packet_list->empty() || *decoded_length < 0 || + (packet_list->size() == 1 && packet && + decoder_database_->IsComfortNoise(packet->header.payloadType))); + return 0; +} + +void NetEqImpl::DoNormal(const int16_t* decoded_buffer, size_t decoded_length, + AudioDecoder::SpeechType speech_type, bool play_dtmf) { + assert(normal_.get()); + assert(mute_factor_array_.get()); + normal_->Process(decoded_buffer, decoded_length, last_mode_, + mute_factor_array_.get(), algorithm_buffer_.get()); + if (decoded_length != 0) { + last_mode_ = kModeNormal; + } + + // If last packet was decoded as an inband CNG, set mode to CNG instead. + if ((speech_type == AudioDecoder::kComfortNoise) + || ((last_mode_ == kModeCodecInternalCng) + && (decoded_length == 0))) { + // TODO(hlundin): Remove second part of || statement above. + last_mode_ = kModeCodecInternalCng; + } + + if (!play_dtmf) { + dtmf_tone_generator_->Reset(); + } +} + +void NetEqImpl::DoMerge(int16_t* decoded_buffer, size_t decoded_length, + AudioDecoder::SpeechType speech_type, bool play_dtmf) { + assert(mute_factor_array_.get()); + assert(merge_.get()); + int new_length = merge_->Process(decoded_buffer, decoded_length, + mute_factor_array_.get(), + algorithm_buffer_.get()); + + // Update in-call and post-call statistics. + if (expand_->MuteFactor(0) == 0) { + // Expand generates only noise. + stats_.ExpandedNoiseSamples(new_length - static_cast(decoded_length)); + } else { + // Expansion generates more than only noise. + stats_.ExpandedVoiceSamples(new_length - static_cast(decoded_length)); + } + + last_mode_ = kModeMerge; + // If last packet was decoded as an inband CNG, set mode to CNG instead. + if (speech_type == AudioDecoder::kComfortNoise) { + last_mode_ = kModeCodecInternalCng; + } + expand_->Reset(); + if (!play_dtmf) { + dtmf_tone_generator_->Reset(); + } +} + +int NetEqImpl::DoExpand(bool play_dtmf) { + while ((sync_buffer_->FutureLength() - expand_->overlap_length()) < + static_cast(output_size_samples_)) { + algorithm_buffer_->Clear(); + int return_value = expand_->Process(algorithm_buffer_.get()); + int length = static_cast(algorithm_buffer_->Size()); + + // Update in-call and post-call statistics. + if (expand_->MuteFactor(0) == 0) { + // Expand operation generates only noise. + stats_.ExpandedNoiseSamples(length); + } else { + // Expand operation generates more than only noise. + stats_.ExpandedVoiceSamples(length); + } + + last_mode_ = kModeExpand; + + if (return_value < 0) { + return return_value; + } + + sync_buffer_->PushBack(*algorithm_buffer_); + algorithm_buffer_->Clear(); + } + if (!play_dtmf) { + dtmf_tone_generator_->Reset(); + } + return 0; +} + +int NetEqImpl::DoAccelerate(int16_t* decoded_buffer, size_t decoded_length, + AudioDecoder::SpeechType speech_type, + bool play_dtmf) { + const size_t required_samples = 240 * fs_mult_; // Must have 30 ms. + size_t borrowed_samples_per_channel = 0; + size_t num_channels = algorithm_buffer_->Channels(); + size_t decoded_length_per_channel = decoded_length / num_channels; + if (decoded_length_per_channel < required_samples) { + // Must move data from the |sync_buffer_| in order to get 30 ms. + borrowed_samples_per_channel = static_cast(required_samples - + decoded_length_per_channel); + memmove(&decoded_buffer[borrowed_samples_per_channel * num_channels], + decoded_buffer, + sizeof(int16_t) * decoded_length); + sync_buffer_->ReadInterleavedFromEnd(borrowed_samples_per_channel, + decoded_buffer); + decoded_length = required_samples * num_channels; + } + + int16_t samples_removed; + Accelerate::ReturnCodes return_code = accelerate_->Process( + decoded_buffer, decoded_length, algorithm_buffer_.get(), + &samples_removed); + stats_.AcceleratedSamples(samples_removed); + switch (return_code) { + case Accelerate::kSuccess: + last_mode_ = kModeAccelerateSuccess; + break; + case Accelerate::kSuccessLowEnergy: + last_mode_ = kModeAccelerateLowEnergy; + break; + case Accelerate::kNoStretch: + last_mode_ = kModeAccelerateFail; + break; + case Accelerate::kError: + // TODO(hlundin): Map to kModeError instead? + last_mode_ = kModeAccelerateFail; + return kAccelerateError; + } + + if (borrowed_samples_per_channel > 0) { + // Copy borrowed samples back to the |sync_buffer_|. + size_t length = algorithm_buffer_->Size(); + if (length < borrowed_samples_per_channel) { + // This destroys the beginning of the buffer, but will not cause any + // problems. + sync_buffer_->ReplaceAtIndex(*algorithm_buffer_, + sync_buffer_->Size() - + borrowed_samples_per_channel); + sync_buffer_->PushFrontZeros(borrowed_samples_per_channel - length); + algorithm_buffer_->PopFront(length); + assert(algorithm_buffer_->Empty()); + } else { + sync_buffer_->ReplaceAtIndex(*algorithm_buffer_, + borrowed_samples_per_channel, + sync_buffer_->Size() - + borrowed_samples_per_channel); + algorithm_buffer_->PopFront(borrowed_samples_per_channel); + } + } + + // If last packet was decoded as an inband CNG, set mode to CNG instead. + if (speech_type == AudioDecoder::kComfortNoise) { + last_mode_ = kModeCodecInternalCng; + } + if (!play_dtmf) { + dtmf_tone_generator_->Reset(); + } + expand_->Reset(); + return 0; +} + +int NetEqImpl::DoPreemptiveExpand(int16_t* decoded_buffer, + size_t decoded_length, + AudioDecoder::SpeechType speech_type, + bool play_dtmf) { + const size_t required_samples = 240 * fs_mult_; // Must have 30 ms. + size_t num_channels = algorithm_buffer_->Channels(); + int borrowed_samples_per_channel = 0; + int old_borrowed_samples_per_channel = 0; + size_t decoded_length_per_channel = decoded_length / num_channels; + if (decoded_length_per_channel < required_samples) { + // Must move data from the |sync_buffer_| in order to get 30 ms. + borrowed_samples_per_channel = static_cast(required_samples - + decoded_length_per_channel); + // Calculate how many of these were already played out. + old_borrowed_samples_per_channel = static_cast( + borrowed_samples_per_channel - sync_buffer_->FutureLength()); + old_borrowed_samples_per_channel = std::max( + 0, old_borrowed_samples_per_channel); + memmove(&decoded_buffer[borrowed_samples_per_channel * num_channels], + decoded_buffer, + sizeof(int16_t) * decoded_length); + sync_buffer_->ReadInterleavedFromEnd(borrowed_samples_per_channel, + decoded_buffer); + decoded_length = required_samples * num_channels; + } + + int16_t samples_added; + PreemptiveExpand::ReturnCodes return_code = preemptive_expand_->Process( + decoded_buffer, static_cast(decoded_length), + old_borrowed_samples_per_channel, + algorithm_buffer_.get(), &samples_added); + stats_.PreemptiveExpandedSamples(samples_added); + switch (return_code) { + case PreemptiveExpand::kSuccess: + last_mode_ = kModePreemptiveExpandSuccess; + break; + case PreemptiveExpand::kSuccessLowEnergy: + last_mode_ = kModePreemptiveExpandLowEnergy; + break; + case PreemptiveExpand::kNoStretch: + last_mode_ = kModePreemptiveExpandFail; + break; + case PreemptiveExpand::kError: + // TODO(hlundin): Map to kModeError instead? + last_mode_ = kModePreemptiveExpandFail; + return kPreemptiveExpandError; + } + + if (borrowed_samples_per_channel > 0) { + // Copy borrowed samples back to the |sync_buffer_|. + sync_buffer_->ReplaceAtIndex( + *algorithm_buffer_, borrowed_samples_per_channel, + sync_buffer_->Size() - borrowed_samples_per_channel); + algorithm_buffer_->PopFront(borrowed_samples_per_channel); + } + + // If last packet was decoded as an inband CNG, set mode to CNG instead. + if (speech_type == AudioDecoder::kComfortNoise) { + last_mode_ = kModeCodecInternalCng; + } + if (!play_dtmf) { + dtmf_tone_generator_->Reset(); + } + expand_->Reset(); + return 0; +} + +int NetEqImpl::DoRfc3389Cng(PacketList* packet_list, bool play_dtmf) { + if (!packet_list->empty()) { + // Must have exactly one SID frame at this point. + assert(packet_list->size() == 1); + Packet* packet = packet_list->front(); + packet_list->pop_front(); + if (!decoder_database_->IsComfortNoise(packet->header.payloadType)) { +#ifdef LEGACY_BITEXACT + // This can happen due to a bug in GetDecision. Change the payload type + // to a CNG type, and move on. Note that this means that we are in fact + // sending a non-CNG payload to the comfort noise decoder for decoding. + // Clearly wrong, but will maintain bit-exactness with legacy. + if (fs_hz_ == 8000) { + packet->header.payloadType = + decoder_database_->GetRtpPayloadType(kDecoderCNGnb); + } else if (fs_hz_ == 16000) { + packet->header.payloadType = + decoder_database_->GetRtpPayloadType(kDecoderCNGwb); + } else if (fs_hz_ == 32000) { + packet->header.payloadType = + decoder_database_->GetRtpPayloadType(kDecoderCNGswb32kHz); + } else if (fs_hz_ == 48000) { + packet->header.payloadType = + decoder_database_->GetRtpPayloadType(kDecoderCNGswb48kHz); + } + assert(decoder_database_->IsComfortNoise(packet->header.payloadType)); +#else + LOG(LS_ERROR) << "Trying to decode non-CNG payload as CNG."; + return kOtherError; +#endif + } + // UpdateParameters() deletes |packet|. + if (comfort_noise_->UpdateParameters(packet) == + ComfortNoise::kInternalError) { + LOG_FERR0(LS_WARNING, UpdateParameters); + algorithm_buffer_->Zeros(output_size_samples_); + return -comfort_noise_->internal_error_code(); + } + } + int cn_return = comfort_noise_->Generate(output_size_samples_, + algorithm_buffer_.get()); + expand_->Reset(); + last_mode_ = kModeRfc3389Cng; + if (!play_dtmf) { + dtmf_tone_generator_->Reset(); + } + if (cn_return == ComfortNoise::kInternalError) { + LOG_FERR1(LS_WARNING, comfort_noise_->Generate, cn_return); + decoder_error_code_ = comfort_noise_->internal_error_code(); + return kComfortNoiseErrorCode; + } else if (cn_return == ComfortNoise::kUnknownPayloadType) { + LOG_FERR1(LS_WARNING, comfort_noise_->Generate, cn_return); + return kUnknownRtpPayloadType; + } + return 0; +} + +void NetEqImpl::DoCodecInternalCng() { + int length = 0; + // TODO(hlundin): Will probably need a longer buffer for multi-channel. + int16_t decoded_buffer[kMaxFrameSize]; + AudioDecoder* decoder = decoder_database_->GetActiveDecoder(); + if (decoder) { + const uint8_t* dummy_payload = NULL; + AudioDecoder::SpeechType speech_type; + length = decoder->Decode(dummy_payload, 0, decoded_buffer, &speech_type); + } + assert(mute_factor_array_.get()); + normal_->Process(decoded_buffer, length, last_mode_, mute_factor_array_.get(), + algorithm_buffer_.get()); + last_mode_ = kModeCodecInternalCng; + expand_->Reset(); +} + +int NetEqImpl::DoDtmf(const DtmfEvent& dtmf_event, bool* play_dtmf) { + // This block of the code and the block further down, handling |dtmf_switch| + // are commented out. Otherwise playing out-of-band DTMF would fail in VoE + // test, DtmfTest.ManualSuccessfullySendsOutOfBandTelephoneEvents. This is + // equivalent to |dtmf_switch| always be false. + // + // See http://webrtc-codereview.appspot.com/1195004/ for discussion + // On this issue. This change might cause some glitches at the point of + // switch from audio to DTMF. Issue 1545 is filed to track this. + // + // bool dtmf_switch = false; + // if ((last_mode_ != kModeDtmf) && dtmf_tone_generator_->initialized()) { + // // Special case; see below. + // // We must catch this before calling Generate, since |initialized| is + // // modified in that call. + // dtmf_switch = true; + // } + + int dtmf_return_value = 0; + if (!dtmf_tone_generator_->initialized()) { + // Initialize if not already done. + dtmf_return_value = dtmf_tone_generator_->Init(fs_hz_, dtmf_event.event_no, + dtmf_event.volume); + } + + if (dtmf_return_value == 0) { + // Generate DTMF signal. + dtmf_return_value = dtmf_tone_generator_->Generate(output_size_samples_, + algorithm_buffer_.get()); + } + + if (dtmf_return_value < 0) { + algorithm_buffer_->Zeros(output_size_samples_); + return dtmf_return_value; + } + + // if (dtmf_switch) { + // // This is the special case where the previous operation was DTMF + // // overdub, but the current instruction is "regular" DTMF. We must make + // // sure that the DTMF does not have any discontinuities. The first DTMF + // // sample that we generate now must be played out immediately, therefore + // // it must be copied to the speech buffer. + // // TODO(hlundin): This code seems incorrect. (Legacy.) Write test and + // // verify correct operation. + // assert(false); + // // Must generate enough data to replace all of the |sync_buffer_| + // // "future". + // int required_length = sync_buffer_->FutureLength(); + // assert(dtmf_tone_generator_->initialized()); + // dtmf_return_value = dtmf_tone_generator_->Generate(required_length, + // algorithm_buffer_); + // assert((size_t) required_length == algorithm_buffer_->Size()); + // if (dtmf_return_value < 0) { + // algorithm_buffer_->Zeros(output_size_samples_); + // return dtmf_return_value; + // } + // + // // Overwrite the "future" part of the speech buffer with the new DTMF + // // data. + // // TODO(hlundin): It seems that this overwriting has gone lost. + // // Not adapted for multi-channel yet. + // assert(algorithm_buffer_->Channels() == 1); + // if (algorithm_buffer_->Channels() != 1) { + // LOG(LS_WARNING) << "DTMF not supported for more than one channel"; + // return kStereoNotSupported; + // } + // // Shuffle the remaining data to the beginning of algorithm buffer. + // algorithm_buffer_->PopFront(sync_buffer_->FutureLength()); + // } + + sync_buffer_->IncreaseEndTimestamp(output_size_samples_); + expand_->Reset(); + last_mode_ = kModeDtmf; + + // Set to false because the DTMF is already in the algorithm buffer. + *play_dtmf = false; + return 0; +} + +void NetEqImpl::DoAlternativePlc(bool increase_timestamp) { + AudioDecoder* decoder = decoder_database_->GetActiveDecoder(); + int length; + if (decoder && decoder->HasDecodePlc()) { + // Use the decoder's packet-loss concealment. + // TODO(hlundin): Will probably need a longer buffer for multi-channel. + int16_t decoded_buffer[kMaxFrameSize]; + length = decoder->DecodePlc(1, decoded_buffer); + if (length > 0) { + algorithm_buffer_->PushBackInterleaved(decoded_buffer, length); + } else { + length = 0; + } + } else { + // Do simple zero-stuffing. + length = output_size_samples_; + algorithm_buffer_->Zeros(length); + // By not advancing the timestamp, NetEq inserts samples. + stats_.AddZeros(length); + } + if (increase_timestamp) { + sync_buffer_->IncreaseEndTimestamp(length); + } + expand_->Reset(); +} + +int NetEqImpl::DtmfOverdub(const DtmfEvent& dtmf_event, size_t num_channels, + int16_t* output) const { + size_t out_index = 0; + int overdub_length = output_size_samples_; // Default value. + + if (sync_buffer_->dtmf_index() > sync_buffer_->next_index()) { + // Special operation for transition from "DTMF only" to "DTMF overdub". + out_index = std::min( + sync_buffer_->dtmf_index() - sync_buffer_->next_index(), + static_cast(output_size_samples_)); + overdub_length = output_size_samples_ - static_cast(out_index); + } + + AudioMultiVector dtmf_output(num_channels); + int dtmf_return_value = 0; + if (!dtmf_tone_generator_->initialized()) { + dtmf_return_value = dtmf_tone_generator_->Init(fs_hz_, dtmf_event.event_no, + dtmf_event.volume); + } + if (dtmf_return_value == 0) { + dtmf_return_value = dtmf_tone_generator_->Generate(overdub_length, + &dtmf_output); + assert((size_t) overdub_length == dtmf_output.Size()); + } + dtmf_output.ReadInterleaved(overdub_length, &output[out_index]); + return dtmf_return_value < 0 ? dtmf_return_value : 0; +} + +int NetEqImpl::ExtractPackets(int required_samples, PacketList* packet_list) { + bool first_packet = true; + uint8_t prev_payload_type = 0; + uint32_t prev_timestamp = 0; + uint16_t prev_sequence_number = 0; + bool next_packet_available = false; + + const RTPHeader* header = packet_buffer_->NextRtpHeader(); + assert(header); + if (!header) { + return -1; + } + uint32_t first_timestamp = header->timestamp; + int extracted_samples = 0; + + // Packet extraction loop. + do { + timestamp_ = header->timestamp; + int discard_count = 0; + Packet* packet = packet_buffer_->GetNextPacket(&discard_count); + // |header| may be invalid after the |packet_buffer_| operation. + header = NULL; + if (!packet) { + LOG_FERR1(LS_ERROR, GetNextPacket, discard_count) << + "Should always be able to extract a packet here"; + assert(false); // Should always be able to extract a packet here. + return -1; + } + stats_.PacketsDiscarded(discard_count); + // Store waiting time in ms; packets->waiting_time is in "output blocks". + stats_.StoreWaitingTime(packet->waiting_time * kOutputSizeMs); + assert(packet->payload_length > 0); + packet_list->push_back(packet); // Store packet in list. + + if (first_packet) { + first_packet = false; + decoded_packet_sequence_number_ = prev_sequence_number = + packet->header.sequenceNumber; + decoded_packet_timestamp_ = prev_timestamp = packet->header.timestamp; + prev_payload_type = packet->header.payloadType; + } + + // Store number of extracted samples. + int packet_duration = 0; + AudioDecoder* decoder = decoder_database_->GetDecoder( + packet->header.payloadType); + if (decoder) { + if (packet->sync_packet) { + packet_duration = decoder_frame_length_; + } else { + packet_duration = packet->primary ? + decoder->PacketDuration(packet->payload, packet->payload_length) : + decoder->PacketDurationRedundant(packet->payload, + packet->payload_length); + } + } else { + LOG_FERR1(LS_WARNING, GetDecoder, packet->header.payloadType) << + "Could not find a decoder for a packet about to be extracted."; + assert(false); + } + if (packet_duration <= 0) { + // Decoder did not return a packet duration. Assume that the packet + // contains the same number of samples as the previous one. + packet_duration = decoder_frame_length_; + } + extracted_samples = packet->header.timestamp - first_timestamp + + packet_duration; + + // Check what packet is available next. + header = packet_buffer_->NextRtpHeader(); + next_packet_available = false; + if (header && prev_payload_type == header->payloadType) { + int16_t seq_no_diff = header->sequenceNumber - prev_sequence_number; + int32_t ts_diff = header->timestamp - prev_timestamp; + if (seq_no_diff == 1 || + (seq_no_diff == 0 && ts_diff == decoder_frame_length_)) { + // The next sequence number is available, or the next part of a packet + // that was split into pieces upon insertion. + next_packet_available = true; + } + prev_sequence_number = header->sequenceNumber; + } + } while (extracted_samples < required_samples && next_packet_available); + + if (extracted_samples > 0) { + // Delete old packets only when we are going to decode something. Otherwise, + // we could end up in the situation where we never decode anything, since + // all incoming packets are considered too old but the buffer will also + // never be flooded and flushed. + packet_buffer_->DiscardAllOldPackets(timestamp_); + } + + return extracted_samples; +} + +void NetEqImpl::UpdatePlcComponents(int fs_hz, size_t channels) { + // Delete objects and create new ones. + expand_.reset(expand_factory_->Create(background_noise_.get(), + sync_buffer_.get(), &random_vector_, + fs_hz, channels)); + merge_.reset(new Merge(fs_hz, channels, expand_.get(), sync_buffer_.get())); +} + +void NetEqImpl::SetSampleRateAndChannels(int fs_hz, size_t channels) { + LOG_API2(fs_hz, channels); + // TODO(hlundin): Change to an enumerator and skip assert. + assert(fs_hz == 8000 || fs_hz == 16000 || fs_hz == 32000 || fs_hz == 48000); + assert(channels > 0); + + fs_hz_ = fs_hz; + fs_mult_ = fs_hz / 8000; + output_size_samples_ = kOutputSizeMs * 8 * fs_mult_; + decoder_frame_length_ = 3 * output_size_samples_; // Initialize to 30ms. + + last_mode_ = kModeNormal; + + // Create a new array of mute factors and set all to 1. + mute_factor_array_.reset(new int16_t[channels]); + for (size_t i = 0; i < channels; ++i) { + mute_factor_array_[i] = 16384; // 1.0 in Q14. + } + + // Reset comfort noise decoder, if there is one active. + AudioDecoder* cng_decoder = decoder_database_->GetActiveCngDecoder(); + if (cng_decoder) { + cng_decoder->Init(); + } + + // Reinit post-decode VAD with new sample rate. + assert(vad_.get()); // Cannot be NULL here. + vad_->Init(); + + // Delete algorithm buffer and create a new one. + algorithm_buffer_.reset(new AudioMultiVector(channels)); + + // Delete sync buffer and create a new one. + sync_buffer_.reset(new SyncBuffer(channels, kSyncBufferSize * fs_mult_)); + + // Delete BackgroundNoise object and create a new one. + background_noise_.reset(new BackgroundNoise(channels)); + background_noise_->set_mode(background_noise_mode_); + + // Reset random vector. + random_vector_.Reset(); + + UpdatePlcComponents(fs_hz, channels); + + // Move index so that we create a small set of future samples (all 0). + sync_buffer_->set_next_index(sync_buffer_->next_index() - + expand_->overlap_length()); + + normal_.reset(new Normal(fs_hz, decoder_database_.get(), *background_noise_, + expand_.get())); + accelerate_.reset( + accelerate_factory_->Create(fs_hz, channels, *background_noise_)); + preemptive_expand_.reset(preemptive_expand_factory_->Create( + fs_hz, channels, + *background_noise_, + static_cast(expand_->overlap_length()))); + + // Delete ComfortNoise object and create a new one. + comfort_noise_.reset(new ComfortNoise(fs_hz, decoder_database_.get(), + sync_buffer_.get())); + + // Verify that |decoded_buffer_| is long enough. + if (decoded_buffer_length_ < kMaxFrameSize * channels) { + // Reallocate to larger size. + decoded_buffer_length_ = kMaxFrameSize * channels; + decoded_buffer_.reset(new int16_t[decoded_buffer_length_]); + } + + // Create DecisionLogic if it is not created yet, then communicate new sample + // rate and output size to DecisionLogic object. + if (!decision_logic_.get()) { + CreateDecisionLogic(); + } + decision_logic_->SetSampleRate(fs_hz_, output_size_samples_); +} + +NetEqOutputType NetEqImpl::LastOutputType() { + assert(vad_.get()); + assert(expand_.get()); + if (last_mode_ == kModeCodecInternalCng || last_mode_ == kModeRfc3389Cng) { + return kOutputCNG; + } else if (last_mode_ == kModeExpand && expand_->MuteFactor(0) == 0) { + // Expand mode has faded down to background noise only (very long expand). + return kOutputPLCtoCNG; + } else if (last_mode_ == kModeExpand) { + return kOutputPLC; + } else if (vad_->running() && !vad_->active_speech()) { + return kOutputVADPassive; + } else { + return kOutputNormal; + } +} + +void NetEqImpl::CreateDecisionLogic() { + decision_logic_.reset(DecisionLogic::Create(fs_hz_, output_size_samples_, + playout_mode_, + decoder_database_.get(), + *packet_buffer_.get(), + delay_manager_.get(), + buffer_level_filter_.get())); +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_impl.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_impl.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_NETEQ_IMPL_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_NETEQ_IMPL_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/thread_annotations.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" +#include "webrtc/modules/audio_coding/neteq/defines.h" +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" +#include "webrtc/modules/audio_coding/neteq/packet.h" // Declare PacketList. +#include "webrtc/modules/audio_coding/neteq/random_vector.h" +#include "webrtc/modules/audio_coding/neteq/rtcp.h" +#include "webrtc/modules/audio_coding/neteq/statistics_calculator.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Forward declarations. +class Accelerate; +class BackgroundNoise; +class BufferLevelFilter; +class ComfortNoise; +class CriticalSectionWrapper; +class DecisionLogic; +class DecoderDatabase; +class DelayManager; +class DelayPeakDetector; +class DtmfBuffer; +class DtmfToneGenerator; +class Expand; +class Merge; +class Normal; +class PacketBuffer; +class PayloadSplitter; +class PostDecodeVad; +class PreemptiveExpand; +class RandomVector; +class SyncBuffer; +class TimestampScaler; +struct AccelerateFactory; +struct DtmfEvent; +struct ExpandFactory; +struct PreemptiveExpandFactory; + +class NetEqImpl : public webrtc::NetEq { + public: + // Creates a new NetEqImpl object. The object will assume ownership of all + // injected dependencies, and will delete them when done. + NetEqImpl(const NetEq::Config& config, + BufferLevelFilter* buffer_level_filter, + DecoderDatabase* decoder_database, + DelayManager* delay_manager, + DelayPeakDetector* delay_peak_detector, + DtmfBuffer* dtmf_buffer, + DtmfToneGenerator* dtmf_tone_generator, + PacketBuffer* packet_buffer, + PayloadSplitter* payload_splitter, + TimestampScaler* timestamp_scaler, + AccelerateFactory* accelerate_factory, + ExpandFactory* expand_factory, + PreemptiveExpandFactory* preemptive_expand_factory, + bool create_components = true); + + virtual ~NetEqImpl(); + + // Inserts a new packet into NetEq. The |receive_timestamp| is an indication + // of the time when the packet was received, and should be measured with + // the same tick rate as the RTP timestamp of the current payload. + // Returns 0 on success, -1 on failure. + virtual int InsertPacket(const WebRtcRTPHeader& rtp_header, + const uint8_t* payload, + int length_bytes, + uint32_t receive_timestamp) OVERRIDE; + + // Inserts a sync-packet into packet queue. Sync-packets are decoded to + // silence and are intended to keep AV-sync intact in an event of long packet + // losses when Video NACK is enabled but Audio NACK is not. Clients of NetEq + // might insert sync-packet when they observe that buffer level of NetEq is + // decreasing below a certain threshold, defined by the application. + // Sync-packets should have the same payload type as the last audio payload + // type, i.e. they cannot have DTMF or CNG payload type, nor a codec change + // can be implied by inserting a sync-packet. + // Returns kOk on success, kFail on failure. + virtual int InsertSyncPacket(const WebRtcRTPHeader& rtp_header, + uint32_t receive_timestamp) OVERRIDE; + + // Instructs NetEq to deliver 10 ms of audio data. The data is written to + // |output_audio|, which can hold (at least) |max_length| elements. + // The number of channels that were written to the output is provided in + // the output variable |num_channels|, and each channel contains + // |samples_per_channel| elements. If more than one channel is written, + // the samples are interleaved. + // The speech type is written to |type|, if |type| is not NULL. + // Returns kOK on success, or kFail in case of an error. + virtual int GetAudio(size_t max_length, int16_t* output_audio, + int* samples_per_channel, int* num_channels, + NetEqOutputType* type) OVERRIDE; + + // Associates |rtp_payload_type| with |codec| and stores the information in + // the codec database. Returns kOK on success, kFail on failure. + virtual int RegisterPayloadType(enum NetEqDecoder codec, + uint8_t rtp_payload_type) OVERRIDE; + + // Provides an externally created decoder object |decoder| to insert in the + // decoder database. The decoder implements a decoder of type |codec| and + // associates it with |rtp_payload_type|. Returns kOK on success, kFail on + // failure. + virtual int RegisterExternalDecoder(AudioDecoder* decoder, + enum NetEqDecoder codec, + uint8_t rtp_payload_type) OVERRIDE; + + // Removes |rtp_payload_type| from the codec database. Returns 0 on success, + // -1 on failure. + virtual int RemovePayloadType(uint8_t rtp_payload_type) OVERRIDE; + + virtual bool SetMinimumDelay(int delay_ms) OVERRIDE; + + virtual bool SetMaximumDelay(int delay_ms) OVERRIDE; + + virtual int LeastRequiredDelayMs() const OVERRIDE; + + virtual int SetTargetDelay() OVERRIDE { return kNotImplemented; } + + virtual int TargetDelay() OVERRIDE { return kNotImplemented; } + + virtual int CurrentDelay() OVERRIDE { return kNotImplemented; } + + // Sets the playout mode to |mode|. + // Deprecated. + // TODO(henrik.lundin) Delete. + virtual void SetPlayoutMode(NetEqPlayoutMode mode) OVERRIDE; + + // Returns the current playout mode. + // Deprecated. + // TODO(henrik.lundin) Delete. + virtual NetEqPlayoutMode PlayoutMode() const OVERRIDE; + + // Writes the current network statistics to |stats|. The statistics are reset + // after the call. + virtual int NetworkStatistics(NetEqNetworkStatistics* stats) OVERRIDE; + + // Writes the last packet waiting times (in ms) to |waiting_times|. The number + // of values written is no more than 100, but may be smaller if the interface + // is polled again before 100 packets has arrived. + virtual void WaitingTimes(std::vector* waiting_times) OVERRIDE; + + // Writes the current RTCP statistics to |stats|. The statistics are reset + // and a new report period is started with the call. + virtual void GetRtcpStatistics(RtcpStatistics* stats) OVERRIDE; + + // Same as RtcpStatistics(), but does not reset anything. + virtual void GetRtcpStatisticsNoReset(RtcpStatistics* stats) OVERRIDE; + + // Enables post-decode VAD. When enabled, GetAudio() will return + // kOutputVADPassive when the signal contains no speech. + virtual void EnableVad() OVERRIDE; + + // Disables post-decode VAD. + virtual void DisableVad() OVERRIDE; + + virtual bool GetPlayoutTimestamp(uint32_t* timestamp) OVERRIDE; + + virtual int SetTargetNumberOfChannels() OVERRIDE { return kNotImplemented; } + + virtual int SetTargetSampleRate() OVERRIDE { return kNotImplemented; } + + // Returns the error code for the last occurred error. If no error has + // occurred, 0 is returned. + virtual int LastError() const OVERRIDE; + + // Returns the error code last returned by a decoder (audio or comfort noise). + // When LastError() returns kDecoderErrorCode or kComfortNoiseErrorCode, check + // this method to get the decoder's error code. + virtual int LastDecoderError() OVERRIDE; + + // Flushes both the packet buffer and the sync buffer. + virtual void FlushBuffers() OVERRIDE; + + virtual void PacketBufferStatistics(int* current_num_packets, + int* max_num_packets) const OVERRIDE; + + // Get sequence number and timestamp of the latest RTP. + // This method is to facilitate NACK. + virtual int DecodedRtpInfo(int* sequence_number, + uint32_t* timestamp) const OVERRIDE; + + // This accessor method is only intended for testing purposes. + const SyncBuffer* sync_buffer_for_test() const; + + protected: + static const int kOutputSizeMs = 10; + static const int kMaxFrameSize = 2880; // 60 ms @ 48 kHz. + // TODO(hlundin): Provide a better value for kSyncBufferSize. + static const int kSyncBufferSize = 2 * kMaxFrameSize; + + // Inserts a new packet into NetEq. This is used by the InsertPacket method + // above. Returns 0 on success, otherwise an error code. + // TODO(hlundin): Merge this with InsertPacket above? + int InsertPacketInternal(const WebRtcRTPHeader& rtp_header, + const uint8_t* payload, + int length_bytes, + uint32_t receive_timestamp, + bool is_sync_packet) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Delivers 10 ms of audio data. The data is written to |output|, which can + // hold (at least) |max_length| elements. The number of channels that were + // written to the output is provided in the output variable |num_channels|, + // and each channel contains |samples_per_channel| elements. If more than one + // channel is written, the samples are interleaved. + // Returns 0 on success, otherwise an error code. + int GetAudioInternal(size_t max_length, + int16_t* output, + int* samples_per_channel, + int* num_channels) EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Provides a decision to the GetAudioInternal method. The decision what to + // do is written to |operation|. Packets to decode are written to + // |packet_list|, and a DTMF event to play is written to |dtmf_event|. When + // DTMF should be played, |play_dtmf| is set to true by the method. + // Returns 0 on success, otherwise an error code. + int GetDecision(Operations* operation, + PacketList* packet_list, + DtmfEvent* dtmf_event, + bool* play_dtmf) EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Decodes the speech packets in |packet_list|, and writes the results to + // |decoded_buffer|, which is allocated to hold |decoded_buffer_length| + // elements. The length of the decoded data is written to |decoded_length|. + // The speech type -- speech or (codec-internal) comfort noise -- is written + // to |speech_type|. If |packet_list| contains any SID frames for RFC 3389 + // comfort noise, those are not decoded. + int Decode(PacketList* packet_list, + Operations* operation, + int* decoded_length, + AudioDecoder::SpeechType* speech_type) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Sub-method to Decode(). Performs the actual decoding. + int DecodeLoop(PacketList* packet_list, + Operations* operation, + AudioDecoder* decoder, + int* decoded_length, + AudioDecoder::SpeechType* speech_type) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Sub-method which calls the Normal class to perform the normal operation. + void DoNormal(const int16_t* decoded_buffer, + size_t decoded_length, + AudioDecoder::SpeechType speech_type, + bool play_dtmf) EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Sub-method which calls the Merge class to perform the merge operation. + void DoMerge(int16_t* decoded_buffer, + size_t decoded_length, + AudioDecoder::SpeechType speech_type, + bool play_dtmf) EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Sub-method which calls the Expand class to perform the expand operation. + int DoExpand(bool play_dtmf) EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Sub-method which calls the Accelerate class to perform the accelerate + // operation. + int DoAccelerate(int16_t* decoded_buffer, + size_t decoded_length, + AudioDecoder::SpeechType speech_type, + bool play_dtmf) EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Sub-method which calls the PreemptiveExpand class to perform the + // preemtive expand operation. + int DoPreemptiveExpand(int16_t* decoded_buffer, + size_t decoded_length, + AudioDecoder::SpeechType speech_type, + bool play_dtmf) EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Sub-method which calls the ComfortNoise class to generate RFC 3389 comfort + // noise. |packet_list| can either contain one SID frame to update the + // noise parameters, or no payload at all, in which case the previously + // received parameters are used. + int DoRfc3389Cng(PacketList* packet_list, bool play_dtmf) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Calls the audio decoder to generate codec-internal comfort noise when + // no packet was received. + void DoCodecInternalCng() EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Calls the DtmfToneGenerator class to generate DTMF tones. + int DoDtmf(const DtmfEvent& dtmf_event, bool* play_dtmf) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Produces packet-loss concealment using alternative methods. If the codec + // has an internal PLC, it is called to generate samples. Otherwise, the + // method performs zero-stuffing. + void DoAlternativePlc(bool increase_timestamp) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Overdub DTMF on top of |output|. + int DtmfOverdub(const DtmfEvent& dtmf_event, + size_t num_channels, + int16_t* output) const EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Extracts packets from |packet_buffer_| to produce at least + // |required_samples| samples. The packets are inserted into |packet_list|. + // Returns the number of samples that the packets in the list will produce, or + // -1 in case of an error. + int ExtractPackets(int required_samples, PacketList* packet_list) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Resets various variables and objects to new values based on the sample rate + // |fs_hz| and |channels| number audio channels. + void SetSampleRateAndChannels(int fs_hz, size_t channels) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Returns the output type for the audio produced by the latest call to + // GetAudio(). + NetEqOutputType LastOutputType() EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Updates Expand and Merge. + virtual void UpdatePlcComponents(int fs_hz, size_t channels) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Creates DecisionLogic object with the mode given by |playout_mode_|. + virtual void CreateDecisionLogic() EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + const scoped_ptr crit_sect_; + const scoped_ptr buffer_level_filter_ + GUARDED_BY(crit_sect_); + const scoped_ptr decoder_database_ GUARDED_BY(crit_sect_); + const scoped_ptr delay_manager_ GUARDED_BY(crit_sect_); + const scoped_ptr delay_peak_detector_ + GUARDED_BY(crit_sect_); + const scoped_ptr dtmf_buffer_ GUARDED_BY(crit_sect_); + const scoped_ptr dtmf_tone_generator_ + GUARDED_BY(crit_sect_); + const scoped_ptr packet_buffer_ GUARDED_BY(crit_sect_); + const scoped_ptr payload_splitter_ GUARDED_BY(crit_sect_); + const scoped_ptr timestamp_scaler_ GUARDED_BY(crit_sect_); + const scoped_ptr vad_ GUARDED_BY(crit_sect_); + const scoped_ptr expand_factory_ GUARDED_BY(crit_sect_); + const scoped_ptr accelerate_factory_ + GUARDED_BY(crit_sect_); + const scoped_ptr preemptive_expand_factory_ + GUARDED_BY(crit_sect_); + + scoped_ptr background_noise_ GUARDED_BY(crit_sect_); + scoped_ptr decision_logic_ GUARDED_BY(crit_sect_); + scoped_ptr algorithm_buffer_ GUARDED_BY(crit_sect_); + scoped_ptr sync_buffer_ GUARDED_BY(crit_sect_); + scoped_ptr expand_ GUARDED_BY(crit_sect_); + scoped_ptr normal_ GUARDED_BY(crit_sect_); + scoped_ptr merge_ GUARDED_BY(crit_sect_); + scoped_ptr accelerate_ GUARDED_BY(crit_sect_); + scoped_ptr preemptive_expand_ GUARDED_BY(crit_sect_); + RandomVector random_vector_ GUARDED_BY(crit_sect_); + scoped_ptr comfort_noise_ GUARDED_BY(crit_sect_); + Rtcp rtcp_ GUARDED_BY(crit_sect_); + StatisticsCalculator stats_ GUARDED_BY(crit_sect_); + int fs_hz_ GUARDED_BY(crit_sect_); + int fs_mult_ GUARDED_BY(crit_sect_); + int output_size_samples_ GUARDED_BY(crit_sect_); + int decoder_frame_length_ GUARDED_BY(crit_sect_); + Modes last_mode_ GUARDED_BY(crit_sect_); + scoped_ptr mute_factor_array_ GUARDED_BY(crit_sect_); + size_t decoded_buffer_length_ GUARDED_BY(crit_sect_); + scoped_ptr decoded_buffer_ GUARDED_BY(crit_sect_); + uint32_t playout_timestamp_ GUARDED_BY(crit_sect_); + bool new_codec_ GUARDED_BY(crit_sect_); + uint32_t timestamp_ GUARDED_BY(crit_sect_); + bool reset_decoder_ GUARDED_BY(crit_sect_); + uint8_t current_rtp_payload_type_ GUARDED_BY(crit_sect_); + uint8_t current_cng_rtp_payload_type_ GUARDED_BY(crit_sect_); + uint32_t ssrc_ GUARDED_BY(crit_sect_); + bool first_packet_ GUARDED_BY(crit_sect_); + int error_code_ GUARDED_BY(crit_sect_); // Store last error code. + int decoder_error_code_ GUARDED_BY(crit_sect_); + const BackgroundNoiseMode background_noise_mode_ GUARDED_BY(crit_sect_); + NetEqPlayoutMode playout_mode_ GUARDED_BY(crit_sect_); + + // These values are used by NACK module to estimate time-to-play of + // a missing packet. Occasionally, NetEq might decide to decode more + // than one packet. Therefore, these values store sequence number and + // timestamp of the first packet pulled from the packet buffer. In + // such cases, these values do not exactly represent the sequence number + // or timestamp associated with a 10ms audio pulled from NetEq. NACK + // module is designed to compensate for this. + int decoded_packet_sequence_number_ GUARDED_BY(crit_sect_); + uint32_t decoded_packet_timestamp_ GUARDED_BY(crit_sect_); + + private: + DISALLOW_COPY_AND_ASSIGN(NetEqImpl); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_NETEQ_IMPL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,590 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" +#include "webrtc/modules/audio_coding/neteq/neteq_impl.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/neteq/accelerate.h" +#include "webrtc/modules/audio_coding/neteq/expand.h" +#include "webrtc/modules/audio_coding/neteq/mock/mock_audio_decoder.h" +#include "webrtc/modules/audio_coding/neteq/mock/mock_buffer_level_filter.h" +#include "webrtc/modules/audio_coding/neteq/mock/mock_decoder_database.h" +#include "webrtc/modules/audio_coding/neteq/mock/mock_delay_manager.h" +#include "webrtc/modules/audio_coding/neteq/mock/mock_delay_peak_detector.h" +#include "webrtc/modules/audio_coding/neteq/mock/mock_dtmf_buffer.h" +#include "webrtc/modules/audio_coding/neteq/mock/mock_dtmf_tone_generator.h" +#include "webrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h" +#include "webrtc/modules/audio_coding/neteq/mock/mock_payload_splitter.h" +#include "webrtc/modules/audio_coding/neteq/preemptive_expand.h" +#include "webrtc/modules/audio_coding/neteq/sync_buffer.h" +#include "webrtc/modules/audio_coding/neteq/timestamp_scaler.h" + +using ::testing::Return; +using ::testing::ReturnNull; +using ::testing::_; +using ::testing::SetArgPointee; +using ::testing::SetArrayArgument; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::WithArg; +using ::testing::Pointee; + +namespace webrtc { + +// This function is called when inserting a packet list into the mock packet +// buffer. The purpose is to delete all inserted packets properly, to avoid +// memory leaks in the test. +int DeletePacketsAndReturnOk(PacketList* packet_list) { + PacketBuffer::DeleteAllPackets(packet_list); + return PacketBuffer::kOK; +} + +class NetEqImplTest : public ::testing::Test { + protected: + NetEqImplTest() + : neteq_(NULL), + config_(), + mock_buffer_level_filter_(NULL), + buffer_level_filter_(NULL), + use_mock_buffer_level_filter_(true), + mock_decoder_database_(NULL), + decoder_database_(NULL), + use_mock_decoder_database_(true), + mock_delay_peak_detector_(NULL), + delay_peak_detector_(NULL), + use_mock_delay_peak_detector_(true), + mock_delay_manager_(NULL), + delay_manager_(NULL), + use_mock_delay_manager_(true), + mock_dtmf_buffer_(NULL), + dtmf_buffer_(NULL), + use_mock_dtmf_buffer_(true), + mock_dtmf_tone_generator_(NULL), + dtmf_tone_generator_(NULL), + use_mock_dtmf_tone_generator_(true), + mock_packet_buffer_(NULL), + packet_buffer_(NULL), + use_mock_packet_buffer_(true), + mock_payload_splitter_(NULL), + payload_splitter_(NULL), + use_mock_payload_splitter_(true), + timestamp_scaler_(NULL) { + config_.sample_rate_hz = 8000; + } + + void CreateInstance() { + if (use_mock_buffer_level_filter_) { + mock_buffer_level_filter_ = new MockBufferLevelFilter; + buffer_level_filter_ = mock_buffer_level_filter_; + } else { + buffer_level_filter_ = new BufferLevelFilter; + } + if (use_mock_decoder_database_) { + mock_decoder_database_ = new MockDecoderDatabase; + EXPECT_CALL(*mock_decoder_database_, GetActiveCngDecoder()) + .WillOnce(ReturnNull()); + decoder_database_ = mock_decoder_database_; + } else { + decoder_database_ = new DecoderDatabase; + } + if (use_mock_delay_peak_detector_) { + mock_delay_peak_detector_ = new MockDelayPeakDetector; + EXPECT_CALL(*mock_delay_peak_detector_, Reset()).Times(1); + delay_peak_detector_ = mock_delay_peak_detector_; + } else { + delay_peak_detector_ = new DelayPeakDetector; + } + if (use_mock_delay_manager_) { + mock_delay_manager_ = new MockDelayManager(config_.max_packets_in_buffer, + delay_peak_detector_); + EXPECT_CALL(*mock_delay_manager_, set_streaming_mode(false)).Times(1); + delay_manager_ = mock_delay_manager_; + } else { + delay_manager_ = + new DelayManager(config_.max_packets_in_buffer, delay_peak_detector_); + } + if (use_mock_dtmf_buffer_) { + mock_dtmf_buffer_ = new MockDtmfBuffer(config_.sample_rate_hz); + dtmf_buffer_ = mock_dtmf_buffer_; + } else { + dtmf_buffer_ = new DtmfBuffer(config_.sample_rate_hz); + } + if (use_mock_dtmf_tone_generator_) { + mock_dtmf_tone_generator_ = new MockDtmfToneGenerator; + dtmf_tone_generator_ = mock_dtmf_tone_generator_; + } else { + dtmf_tone_generator_ = new DtmfToneGenerator; + } + if (use_mock_packet_buffer_) { + mock_packet_buffer_ = new MockPacketBuffer(config_.max_packets_in_buffer); + packet_buffer_ = mock_packet_buffer_; + } else { + packet_buffer_ = new PacketBuffer(config_.max_packets_in_buffer); + } + if (use_mock_payload_splitter_) { + mock_payload_splitter_ = new MockPayloadSplitter; + payload_splitter_ = mock_payload_splitter_; + } else { + payload_splitter_ = new PayloadSplitter; + } + timestamp_scaler_ = new TimestampScaler(*decoder_database_); + AccelerateFactory* accelerate_factory = new AccelerateFactory; + ExpandFactory* expand_factory = new ExpandFactory; + PreemptiveExpandFactory* preemptive_expand_factory = + new PreemptiveExpandFactory; + + neteq_ = new NetEqImpl(config_, + buffer_level_filter_, + decoder_database_, + delay_manager_, + delay_peak_detector_, + dtmf_buffer_, + dtmf_tone_generator_, + packet_buffer_, + payload_splitter_, + timestamp_scaler_, + accelerate_factory, + expand_factory, + preemptive_expand_factory); + ASSERT_TRUE(neteq_ != NULL); + } + + void UseNoMocks() { + ASSERT_TRUE(neteq_ == NULL) << "Must call UseNoMocks before CreateInstance"; + use_mock_buffer_level_filter_ = false; + use_mock_decoder_database_ = false; + use_mock_delay_peak_detector_ = false; + use_mock_delay_manager_ = false; + use_mock_dtmf_buffer_ = false; + use_mock_dtmf_tone_generator_ = false; + use_mock_packet_buffer_ = false; + use_mock_payload_splitter_ = false; + } + + virtual ~NetEqImplTest() { + if (use_mock_buffer_level_filter_) { + EXPECT_CALL(*mock_buffer_level_filter_, Die()).Times(1); + } + if (use_mock_decoder_database_) { + EXPECT_CALL(*mock_decoder_database_, Die()).Times(1); + } + if (use_mock_delay_manager_) { + EXPECT_CALL(*mock_delay_manager_, Die()).Times(1); + } + if (use_mock_delay_peak_detector_) { + EXPECT_CALL(*mock_delay_peak_detector_, Die()).Times(1); + } + if (use_mock_dtmf_buffer_) { + EXPECT_CALL(*mock_dtmf_buffer_, Die()).Times(1); + } + if (use_mock_dtmf_tone_generator_) { + EXPECT_CALL(*mock_dtmf_tone_generator_, Die()).Times(1); + } + if (use_mock_packet_buffer_) { + EXPECT_CALL(*mock_packet_buffer_, Die()).Times(1); + } + delete neteq_; + } + + NetEqImpl* neteq_; + NetEq::Config config_; + MockBufferLevelFilter* mock_buffer_level_filter_; + BufferLevelFilter* buffer_level_filter_; + bool use_mock_buffer_level_filter_; + MockDecoderDatabase* mock_decoder_database_; + DecoderDatabase* decoder_database_; + bool use_mock_decoder_database_; + MockDelayPeakDetector* mock_delay_peak_detector_; + DelayPeakDetector* delay_peak_detector_; + bool use_mock_delay_peak_detector_; + MockDelayManager* mock_delay_manager_; + DelayManager* delay_manager_; + bool use_mock_delay_manager_; + MockDtmfBuffer* mock_dtmf_buffer_; + DtmfBuffer* dtmf_buffer_; + bool use_mock_dtmf_buffer_; + MockDtmfToneGenerator* mock_dtmf_tone_generator_; + DtmfToneGenerator* dtmf_tone_generator_; + bool use_mock_dtmf_tone_generator_; + MockPacketBuffer* mock_packet_buffer_; + PacketBuffer* packet_buffer_; + bool use_mock_packet_buffer_; + MockPayloadSplitter* mock_payload_splitter_; + PayloadSplitter* payload_splitter_; + bool use_mock_payload_splitter_; + TimestampScaler* timestamp_scaler_; +}; + + +// This tests the interface class NetEq. +// TODO(hlundin): Move to separate file? +TEST(NetEq, CreateAndDestroy) { + NetEq::Config config; + NetEq* neteq = NetEq::Create(config); + delete neteq; +} + +TEST_F(NetEqImplTest, RegisterPayloadType) { + CreateInstance(); + uint8_t rtp_payload_type = 0; + NetEqDecoder codec_type = kDecoderPCMu; + EXPECT_CALL(*mock_decoder_database_, + RegisterPayload(rtp_payload_type, codec_type)); + neteq_->RegisterPayloadType(codec_type, rtp_payload_type); +} + +TEST_F(NetEqImplTest, RemovePayloadType) { + CreateInstance(); + uint8_t rtp_payload_type = 0; + EXPECT_CALL(*mock_decoder_database_, Remove(rtp_payload_type)) + .WillOnce(Return(DecoderDatabase::kDecoderNotFound)); + // Check that kFail is returned when database returns kDecoderNotFound. + EXPECT_EQ(NetEq::kFail, neteq_->RemovePayloadType(rtp_payload_type)); +} + +TEST_F(NetEqImplTest, InsertPacket) { + CreateInstance(); + const int kPayloadLength = 100; + const uint8_t kPayloadType = 0; + const uint16_t kFirstSequenceNumber = 0x1234; + const uint32_t kFirstTimestamp = 0x12345678; + const uint32_t kSsrc = 0x87654321; + const uint32_t kFirstReceiveTime = 17; + uint8_t payload[kPayloadLength] = {0}; + WebRtcRTPHeader rtp_header; + rtp_header.header.payloadType = kPayloadType; + rtp_header.header.sequenceNumber = kFirstSequenceNumber; + rtp_header.header.timestamp = kFirstTimestamp; + rtp_header.header.ssrc = kSsrc; + + // Create a mock decoder object. + MockAudioDecoder mock_decoder; + // BWE update function called with first packet. + EXPECT_CALL(mock_decoder, IncomingPacket(_, + kPayloadLength, + kFirstSequenceNumber, + kFirstTimestamp, + kFirstReceiveTime)); + // BWE update function called with second packet. + EXPECT_CALL(mock_decoder, IncomingPacket(_, + kPayloadLength, + kFirstSequenceNumber + 1, + kFirstTimestamp + 160, + kFirstReceiveTime + 155)); + EXPECT_CALL(mock_decoder, Die()).Times(1); // Called when deleted. + + // Expectations for decoder database. + EXPECT_CALL(*mock_decoder_database_, IsRed(kPayloadType)) + .WillRepeatedly(Return(false)); // This is not RED. + EXPECT_CALL(*mock_decoder_database_, CheckPayloadTypes(_)) + .Times(2) + .WillRepeatedly(Return(DecoderDatabase::kOK)); // Payload type is valid. + EXPECT_CALL(*mock_decoder_database_, IsDtmf(kPayloadType)) + .WillRepeatedly(Return(false)); // This is not DTMF. + EXPECT_CALL(*mock_decoder_database_, GetDecoder(kPayloadType)) + .Times(3) + .WillRepeatedly(Return(&mock_decoder)); + EXPECT_CALL(*mock_decoder_database_, IsComfortNoise(kPayloadType)) + .WillRepeatedly(Return(false)); // This is not CNG. + DecoderDatabase::DecoderInfo info; + info.codec_type = kDecoderPCMu; + EXPECT_CALL(*mock_decoder_database_, GetDecoderInfo(kPayloadType)) + .WillRepeatedly(Return(&info)); + + // Expectations for packet buffer. + EXPECT_CALL(*mock_packet_buffer_, NumPacketsInBuffer()) + .WillOnce(Return(0)) // First packet. + .WillOnce(Return(1)) // Second packet. + .WillOnce(Return(2)); // Second packet, checking after it was inserted. + EXPECT_CALL(*mock_packet_buffer_, Empty()) + .WillOnce(Return(false)); // Called once after first packet is inserted. + EXPECT_CALL(*mock_packet_buffer_, Flush()) + .Times(1); + EXPECT_CALL(*mock_packet_buffer_, InsertPacketList(_, _, _, _)) + .Times(2) + .WillRepeatedly(DoAll(SetArgPointee<2>(kPayloadType), + WithArg<0>(Invoke(DeletePacketsAndReturnOk)))); + // SetArgPointee<2>(kPayloadType) means that the third argument (zero-based + // index) is a pointer, and the variable pointed to is set to kPayloadType. + // Also invoke the function DeletePacketsAndReturnOk to properly delete all + // packets in the list (to avoid memory leaks in the test). + EXPECT_CALL(*mock_packet_buffer_, NextRtpHeader()) + .Times(1) + .WillOnce(Return(&rtp_header.header)); + + // Expectations for DTMF buffer. + EXPECT_CALL(*mock_dtmf_buffer_, Flush()) + .Times(1); + + // Expectations for delay manager. + { + // All expectations within this block must be called in this specific order. + InSequence sequence; // Dummy variable. + // Expectations when the first packet is inserted. + EXPECT_CALL(*mock_delay_manager_, LastDecoderType(kDecoderPCMu)) + .Times(1); + EXPECT_CALL(*mock_delay_manager_, last_pack_cng_or_dtmf()) + .Times(2) + .WillRepeatedly(Return(-1)); + EXPECT_CALL(*mock_delay_manager_, set_last_pack_cng_or_dtmf(0)) + .Times(1); + EXPECT_CALL(*mock_delay_manager_, ResetPacketIatCount()).Times(1); + // Expectations when the second packet is inserted. Slightly different. + EXPECT_CALL(*mock_delay_manager_, LastDecoderType(kDecoderPCMu)) + .Times(1); + EXPECT_CALL(*mock_delay_manager_, last_pack_cng_or_dtmf()) + .WillOnce(Return(0)); + EXPECT_CALL(*mock_delay_manager_, SetPacketAudioLength(30)) + .WillOnce(Return(0)); + } + + // Expectations for payload splitter. + EXPECT_CALL(*mock_payload_splitter_, SplitAudio(_, _)) + .Times(2) + .WillRepeatedly(Return(PayloadSplitter::kOK)); + + // Insert first packet. + neteq_->InsertPacket(rtp_header, payload, kPayloadLength, kFirstReceiveTime); + + // Insert second packet. + rtp_header.header.timestamp += 160; + rtp_header.header.sequenceNumber += 1; + neteq_->InsertPacket(rtp_header, payload, kPayloadLength, + kFirstReceiveTime + 155); +} + +TEST_F(NetEqImplTest, InsertPacketsUntilBufferIsFull) { + UseNoMocks(); + CreateInstance(); + + const int kPayloadLengthSamples = 80; + const size_t kPayloadLengthBytes = 2 * kPayloadLengthSamples; // PCM 16-bit. + const uint8_t kPayloadType = 17; // Just an arbitrary number. + const uint32_t kReceiveTime = 17; // Value doesn't matter for this test. + uint8_t payload[kPayloadLengthBytes] = {0}; + WebRtcRTPHeader rtp_header; + rtp_header.header.payloadType = kPayloadType; + rtp_header.header.sequenceNumber = 0x1234; + rtp_header.header.timestamp = 0x12345678; + rtp_header.header.ssrc = 0x87654321; + + EXPECT_EQ(NetEq::kOK, + neteq_->RegisterPayloadType(kDecoderPCM16B, kPayloadType)); + + // Insert packets. The buffer should not flush. + for (int i = 1; i <= config_.max_packets_in_buffer; ++i) { + EXPECT_EQ(NetEq::kOK, + neteq_->InsertPacket( + rtp_header, payload, kPayloadLengthBytes, kReceiveTime)); + rtp_header.header.timestamp += kPayloadLengthSamples; + rtp_header.header.sequenceNumber += 1; + EXPECT_EQ(i, packet_buffer_->NumPacketsInBuffer()); + } + + // Insert one more packet and make sure the buffer got flushed. That is, it + // should only hold one single packet. + EXPECT_EQ(NetEq::kOK, + neteq_->InsertPacket( + rtp_header, payload, kPayloadLengthBytes, kReceiveTime)); + EXPECT_EQ(1, packet_buffer_->NumPacketsInBuffer()); + const RTPHeader* test_header = packet_buffer_->NextRtpHeader(); + EXPECT_EQ(rtp_header.header.timestamp, test_header->timestamp); + EXPECT_EQ(rtp_header.header.sequenceNumber, test_header->sequenceNumber); +} + +// This test verifies that timestamps propagate from the incoming packets +// through to the sync buffer and to the playout timestamp. +TEST_F(NetEqImplTest, VerifyTimestampPropagation) { + UseNoMocks(); + CreateInstance(); + + const uint8_t kPayloadType = 17; // Just an arbitrary number. + const uint32_t kReceiveTime = 17; // Value doesn't matter for this test. + const int kSampleRateHz = 8000; + const int kPayloadLengthSamples = 10 * kSampleRateHz / 1000; // 10 ms. + const size_t kPayloadLengthBytes = kPayloadLengthSamples; + uint8_t payload[kPayloadLengthBytes] = {0}; + WebRtcRTPHeader rtp_header; + rtp_header.header.payloadType = kPayloadType; + rtp_header.header.sequenceNumber = 0x1234; + rtp_header.header.timestamp = 0x12345678; + rtp_header.header.ssrc = 0x87654321; + + // This is a dummy decoder that produces as many output samples as the input + // has bytes. The output is an increasing series, starting at 1 for the first + // sample, and then increasing by 1 for each sample. + class CountingSamplesDecoder : public AudioDecoder { + public: + CountingSamplesDecoder() : next_value_(1) {} + + // Produce as many samples as input bytes (|encoded_len|). + virtual int Decode(const uint8_t* encoded, + size_t encoded_len, + int16_t* decoded, + SpeechType* speech_type) { + for (size_t i = 0; i < encoded_len; ++i) { + decoded[i] = next_value_++; + } + *speech_type = kSpeech; + return encoded_len; + } + + virtual int Init() { + next_value_ = 1; + return 0; + } + + uint16_t next_value() const { return next_value_; } + + private: + int16_t next_value_; + } decoder_; + + EXPECT_EQ(NetEq::kOK, + neteq_->RegisterExternalDecoder( + &decoder_, kDecoderPCM16B, kPayloadType)); + + // Insert one packet. + EXPECT_EQ(NetEq::kOK, + neteq_->InsertPacket( + rtp_header, payload, kPayloadLengthBytes, kReceiveTime)); + + // Pull audio once. + const int kMaxOutputSize = 10 * kSampleRateHz / 1000; + int16_t output[kMaxOutputSize]; + int samples_per_channel; + int num_channels; + NetEqOutputType type; + EXPECT_EQ( + NetEq::kOK, + neteq_->GetAudio( + kMaxOutputSize, output, &samples_per_channel, &num_channels, &type)); + ASSERT_EQ(kMaxOutputSize, samples_per_channel); + EXPECT_EQ(1, num_channels); + EXPECT_EQ(kOutputNormal, type); + + // Start with a simple check that the fake decoder is behaving as expected. + EXPECT_EQ(kPayloadLengthSamples, decoder_.next_value() - 1); + + // The value of the last of the output samples is the same as the number of + // samples played from the decoded packet. Thus, this number + the RTP + // timestamp should match the playout timestamp. + uint32_t timestamp = 0; + EXPECT_TRUE(neteq_->GetPlayoutTimestamp(×tamp)); + EXPECT_EQ(rtp_header.header.timestamp + output[samples_per_channel - 1], + timestamp); + + // Check the timestamp for the last value in the sync buffer. This should + // be one full frame length ahead of the RTP timestamp. + const SyncBuffer* sync_buffer = neteq_->sync_buffer_for_test(); + ASSERT_TRUE(sync_buffer != NULL); + EXPECT_EQ(rtp_header.header.timestamp + kPayloadLengthSamples, + sync_buffer->end_timestamp()); + + // Check that the number of samples still to play from the sync buffer add + // up with what was already played out. + EXPECT_EQ(kPayloadLengthSamples - output[samples_per_channel - 1], + static_cast(sync_buffer->FutureLength())); +} + +TEST_F(NetEqImplTest, ReorderedPacket) { + UseNoMocks(); + CreateInstance(); + + const uint8_t kPayloadType = 17; // Just an arbitrary number. + const uint32_t kReceiveTime = 17; // Value doesn't matter for this test. + const int kSampleRateHz = 8000; + const int kPayloadLengthSamples = 10 * kSampleRateHz / 1000; // 10 ms. + const size_t kPayloadLengthBytes = kPayloadLengthSamples; + uint8_t payload[kPayloadLengthBytes] = {0}; + WebRtcRTPHeader rtp_header; + rtp_header.header.payloadType = kPayloadType; + rtp_header.header.sequenceNumber = 0x1234; + rtp_header.header.timestamp = 0x12345678; + rtp_header.header.ssrc = 0x87654321; + + // Create a mock decoder object. + MockAudioDecoder mock_decoder; + EXPECT_CALL(mock_decoder, Init()).WillRepeatedly(Return(0)); + EXPECT_CALL(mock_decoder, IncomingPacket(_, kPayloadLengthBytes, _, _, _)) + .WillRepeatedly(Return(0)); + int16_t dummy_output[kPayloadLengthSamples] = {0}; + // The below expectation will make the mock decoder write + // |kPayloadLengthSamples| zeros to the output array, and mark it as speech. + EXPECT_CALL(mock_decoder, Decode(Pointee(0), kPayloadLengthBytes, _, _)) + .WillOnce(DoAll(SetArrayArgument<2>(dummy_output, + dummy_output + kPayloadLengthSamples), + SetArgPointee<3>(AudioDecoder::kSpeech), + Return(kPayloadLengthSamples))); + EXPECT_EQ(NetEq::kOK, + neteq_->RegisterExternalDecoder( + &mock_decoder, kDecoderPCM16B, kPayloadType)); + + // Insert one packet. + EXPECT_EQ(NetEq::kOK, + neteq_->InsertPacket( + rtp_header, payload, kPayloadLengthBytes, kReceiveTime)); + + // Pull audio once. + const int kMaxOutputSize = 10 * kSampleRateHz / 1000; + int16_t output[kMaxOutputSize]; + int samples_per_channel; + int num_channels; + NetEqOutputType type; + EXPECT_EQ( + NetEq::kOK, + neteq_->GetAudio( + kMaxOutputSize, output, &samples_per_channel, &num_channels, &type)); + ASSERT_EQ(kMaxOutputSize, samples_per_channel); + EXPECT_EQ(1, num_channels); + EXPECT_EQ(kOutputNormal, type); + + // Insert two more packets. The first one is out of order, and is already too + // old, the second one is the expected next packet. + rtp_header.header.sequenceNumber -= 1; + rtp_header.header.timestamp -= kPayloadLengthSamples; + payload[0] = 1; + EXPECT_EQ(NetEq::kOK, + neteq_->InsertPacket( + rtp_header, payload, kPayloadLengthBytes, kReceiveTime)); + rtp_header.header.sequenceNumber += 2; + rtp_header.header.timestamp += 2 * kPayloadLengthSamples; + payload[0] = 2; + EXPECT_EQ(NetEq::kOK, + neteq_->InsertPacket( + rtp_header, payload, kPayloadLengthBytes, kReceiveTime)); + + // Expect only the second packet to be decoded (the one with "2" as the first + // payload byte). + EXPECT_CALL(mock_decoder, Decode(Pointee(2), kPayloadLengthBytes, _, _)) + .WillOnce(DoAll(SetArrayArgument<2>(dummy_output, + dummy_output + kPayloadLengthSamples), + SetArgPointee<3>(AudioDecoder::kSpeech), + Return(kPayloadLengthSamples))); + + // Pull audio once. + EXPECT_EQ( + NetEq::kOK, + neteq_->GetAudio( + kMaxOutputSize, output, &samples_per_channel, &num_channels, &type)); + ASSERT_EQ(kMaxOutputSize, samples_per_channel); + EXPECT_EQ(1, num_channels); + EXPECT_EQ(kOutputNormal, type); + + // Now check the packet buffer, and make sure it is empty, since the + // out-of-order packet should have been discarded. + EXPECT_TRUE(packet_buffer_->Empty()); + + EXPECT_CALL(mock_decoder, Die()); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_statistics.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_statistics.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_statistics.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_statistics.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Definitions of statistics data structures for MCU and DSP sides. - */ - -#include "typedefs.h" - -#ifndef NETEQ_STATISTICS_H -#define NETEQ_STATISTICS_H - -/* - * Statistics struct on DSP side - */ -typedef struct -{ - - /* variables for in-call statistics; queried through WebRtcNetEQ_GetNetworkStatistics */ - uint32_t expandLength; /* number of samples produced through expand */ - uint32_t preemptiveLength; /* number of samples produced through pre-emptive - expand */ - uint32_t accelerateLength; /* number of samples removed through accelerate */ - int addedSamples; /* number of samples inserted in off mode */ - - /* variables for post-call statistics; queried through WebRtcNetEQ_GetJitterStatistics */ - uint32_t expandedVoiceSamples; /* number of voice samples produced through expand */ - uint32_t expandedNoiseSamples; /* number of noise (background) samples produced - through expand */ - -} DSPStats_t; - -typedef struct { - int preemptive_expand_bgn_samples; - int preemptive_expand_normal_samples; - - int expand_bgn_samples; - int expand_normal_samples; - - int merge_expand_bgn_samples; - int merge_expand_normal_samples; - - int accelerate_bgn_samples; - int accelarate_normal_samples; -} ActivityStats; - - -#endif - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_stereo_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_stereo_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_stereo_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_stereo_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Test to verify correct stereo and multi-channel operation. + +#include +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" +#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" +#include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/test/testsupport/fileutils.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace webrtc { + +struct TestParameters { + int frame_size; + int sample_rate; + int num_channels; +}; + +// This is a parameterized test. The test parameters are supplied through a +// TestParameters struct, which is obtained through the GetParam() method. +// +// The objective of the test is to create a mono input signal and a +// multi-channel input signal, where each channel is identical to the mono +// input channel. The two input signals are processed through their respective +// NetEq instances. After that, the output signals are compared. The expected +// result is that each channel in the multi-channel output is identical to the +// mono output. +class NetEqStereoTest : public ::testing::TestWithParam { + protected: + static const int kTimeStepMs = 10; + static const int kMaxBlockSize = 480; // 10 ms @ 48 kHz. + static const uint8_t kPayloadTypeMono = 95; + static const uint8_t kPayloadTypeMulti = 96; + + NetEqStereoTest() + : num_channels_(GetParam().num_channels), + sample_rate_hz_(GetParam().sample_rate), + samples_per_ms_(sample_rate_hz_ / 1000), + frame_size_ms_(GetParam().frame_size), + frame_size_samples_(frame_size_ms_ * samples_per_ms_), + output_size_samples_(10 * samples_per_ms_), + rtp_generator_mono_(samples_per_ms_), + rtp_generator_(samples_per_ms_), + payload_size_bytes_(0), + multi_payload_size_bytes_(0), + last_send_time_(0), + last_arrival_time_(0) { + NetEq::Config config; + config.sample_rate_hz = sample_rate_hz_; + neteq_mono_ = NetEq::Create(config); + neteq_ = NetEq::Create(config); + input_ = new int16_t[frame_size_samples_]; + encoded_ = new uint8_t[2 * frame_size_samples_]; + input_multi_channel_ = new int16_t[frame_size_samples_ * num_channels_]; + encoded_multi_channel_ = new uint8_t[frame_size_samples_ * 2 * + num_channels_]; + output_multi_channel_ = new int16_t[kMaxBlockSize * num_channels_]; + } + + ~NetEqStereoTest() { + delete neteq_mono_; + delete neteq_; + delete [] input_; + delete [] encoded_; + delete [] input_multi_channel_; + delete [] encoded_multi_channel_; + delete [] output_multi_channel_; + } + + virtual void SetUp() { + const std::string file_name = + webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); + input_file_.reset(new test::InputAudioFile(file_name)); + NetEqDecoder mono_decoder; + NetEqDecoder multi_decoder; + switch (sample_rate_hz_) { + case 8000: + mono_decoder = kDecoderPCM16B; + if (num_channels_ == 2) { + multi_decoder = kDecoderPCM16B_2ch; + } else if (num_channels_ == 5) { + multi_decoder = kDecoderPCM16B_5ch; + } else { + FAIL() << "Only 2 and 5 channels supported for 8000 Hz."; + } + break; + case 16000: + mono_decoder = kDecoderPCM16Bwb; + if (num_channels_ == 2) { + multi_decoder = kDecoderPCM16Bwb_2ch; + } else { + FAIL() << "More than 2 channels is not supported for 16000 Hz."; + } + break; + case 32000: + mono_decoder = kDecoderPCM16Bswb32kHz; + if (num_channels_ == 2) { + multi_decoder = kDecoderPCM16Bswb32kHz_2ch; + } else { + FAIL() << "More than 2 channels is not supported for 32000 Hz."; + } + break; + case 48000: + mono_decoder = kDecoderPCM16Bswb48kHz; + if (num_channels_ == 2) { + multi_decoder = kDecoderPCM16Bswb48kHz_2ch; + } else { + FAIL() << "More than 2 channels is not supported for 48000 Hz."; + } + break; + default: + FAIL() << "We shouldn't get here."; + } + ASSERT_EQ(NetEq::kOK, + neteq_mono_->RegisterPayloadType(mono_decoder, + kPayloadTypeMono)); + ASSERT_EQ(NetEq::kOK, + neteq_->RegisterPayloadType(multi_decoder, + kPayloadTypeMulti)); + } + + virtual void TearDown() {} + + int GetNewPackets() { + if (!input_file_->Read(frame_size_samples_, input_)) { + return -1; + } + payload_size_bytes_ = WebRtcPcm16b_Encode(input_, frame_size_samples_, + encoded_); + if (frame_size_samples_ * 2 != payload_size_bytes_) { + return -1; + } + int next_send_time = rtp_generator_mono_.GetRtpHeader(kPayloadTypeMono, + frame_size_samples_, + &rtp_header_mono_); + test::InputAudioFile::DuplicateInterleaved(input_, frame_size_samples_, + num_channels_, + input_multi_channel_); + multi_payload_size_bytes_ = WebRtcPcm16b_Encode( + input_multi_channel_, frame_size_samples_ * num_channels_, + encoded_multi_channel_); + if (frame_size_samples_ * 2 * num_channels_ != multi_payload_size_bytes_) { + return -1; + } + rtp_generator_.GetRtpHeader(kPayloadTypeMulti, frame_size_samples_, + &rtp_header_); + return next_send_time; + } + + void VerifyOutput(size_t num_samples) { + for (size_t i = 0; i < num_samples; ++i) { + for (int j = 0; j < num_channels_; ++j) { + ASSERT_EQ(output_[i], output_multi_channel_[i * num_channels_ + j]) << + "Diff in sample " << i << ", channel " << j << "."; + } + } + } + + virtual int GetArrivalTime(int send_time) { + int arrival_time = last_arrival_time_ + (send_time - last_send_time_); + last_send_time_ = send_time; + last_arrival_time_ = arrival_time; + return arrival_time; + } + + virtual bool Lost() { return false; } + + void RunTest(int num_loops) { + // Get next input packets (mono and multi-channel). + int next_send_time; + int next_arrival_time; + do { + next_send_time = GetNewPackets(); + ASSERT_NE(-1, next_send_time); + next_arrival_time = GetArrivalTime(next_send_time); + } while (Lost()); // If lost, immediately read the next packet. + + int time_now = 0; + for (int k = 0; k < num_loops; ++k) { + while (time_now >= next_arrival_time) { + // Insert packet in mono instance. + ASSERT_EQ(NetEq::kOK, + neteq_mono_->InsertPacket(rtp_header_mono_, encoded_, + payload_size_bytes_, + next_arrival_time)); + // Insert packet in multi-channel instance. + ASSERT_EQ(NetEq::kOK, + neteq_->InsertPacket(rtp_header_, encoded_multi_channel_, + multi_payload_size_bytes_, + next_arrival_time)); + // Get next input packets (mono and multi-channel). + do { + next_send_time = GetNewPackets(); + ASSERT_NE(-1, next_send_time); + next_arrival_time = GetArrivalTime(next_send_time); + } while (Lost()); // If lost, immediately read the next packet. + } + NetEqOutputType output_type; + // Get audio from mono instance. + int samples_per_channel; + int num_channels; + EXPECT_EQ(NetEq::kOK, + neteq_mono_->GetAudio(kMaxBlockSize, output_, + &samples_per_channel, &num_channels, + &output_type)); + EXPECT_EQ(1, num_channels); + EXPECT_EQ(output_size_samples_, samples_per_channel); + // Get audio from multi-channel instance. + ASSERT_EQ(NetEq::kOK, + neteq_->GetAudio(kMaxBlockSize * num_channels_, + output_multi_channel_, + &samples_per_channel, &num_channels, + &output_type)); + EXPECT_EQ(num_channels_, num_channels); + EXPECT_EQ(output_size_samples_, samples_per_channel); + std::ostringstream ss; + ss << "Lap number " << k << "."; + SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. + // Compare mono and multi-channel. + ASSERT_NO_FATAL_FAILURE(VerifyOutput(output_size_samples_)); + + time_now += kTimeStepMs; + } + } + + const int num_channels_; + const int sample_rate_hz_; + const int samples_per_ms_; + const int frame_size_ms_; + const int frame_size_samples_; + const int output_size_samples_; + NetEq* neteq_mono_; + NetEq* neteq_; + test::RtpGenerator rtp_generator_mono_; + test::RtpGenerator rtp_generator_; + int16_t* input_; + int16_t* input_multi_channel_; + uint8_t* encoded_; + uint8_t* encoded_multi_channel_; + int16_t output_[kMaxBlockSize]; + int16_t* output_multi_channel_; + WebRtcRTPHeader rtp_header_mono_; + WebRtcRTPHeader rtp_header_; + int payload_size_bytes_; + int multi_payload_size_bytes_; + int last_send_time_; + int last_arrival_time_; + scoped_ptr input_file_; +}; + +class NetEqStereoTestNoJitter : public NetEqStereoTest { + protected: + NetEqStereoTestNoJitter() + : NetEqStereoTest() { + // Start the sender 100 ms before the receiver to pre-fill the buffer. + // This is to avoid doing preemptive expand early in the test. + // TODO(hlundin): Mock the decision making instead to control the modes. + last_arrival_time_ = -100; + } +}; + +TEST_P(NetEqStereoTestNoJitter, DISABLED_ON_ANDROID(RunTest)) { + RunTest(8); +} + +class NetEqStereoTestPositiveDrift : public NetEqStereoTest { + protected: + NetEqStereoTestPositiveDrift() + : NetEqStereoTest(), + drift_factor(0.9) { + // Start the sender 100 ms before the receiver to pre-fill the buffer. + // This is to avoid doing preemptive expand early in the test. + // TODO(hlundin): Mock the decision making instead to control the modes. + last_arrival_time_ = -100; + } + virtual int GetArrivalTime(int send_time) { + int arrival_time = last_arrival_time_ + + drift_factor * (send_time - last_send_time_); + last_send_time_ = send_time; + last_arrival_time_ = arrival_time; + return arrival_time; + } + + double drift_factor; +}; + +TEST_P(NetEqStereoTestPositiveDrift, DISABLED_ON_ANDROID(RunTest)) { + RunTest(100); +} + +class NetEqStereoTestNegativeDrift : public NetEqStereoTestPositiveDrift { + protected: + NetEqStereoTestNegativeDrift() + : NetEqStereoTestPositiveDrift() { + drift_factor = 1.1; + last_arrival_time_ = 0; + } +}; + +TEST_P(NetEqStereoTestNegativeDrift, DISABLED_ON_ANDROID(RunTest)) { + RunTest(100); +} + +class NetEqStereoTestDelays : public NetEqStereoTest { + protected: + static const int kDelayInterval = 10; + static const int kDelay = 1000; + NetEqStereoTestDelays() + : NetEqStereoTest(), + frame_index_(0) { + } + + virtual int GetArrivalTime(int send_time) { + // Deliver immediately, unless we have a back-log. + int arrival_time = std::min(last_arrival_time_, send_time); + if (++frame_index_ % kDelayInterval == 0) { + // Delay this packet. + arrival_time += kDelay; + } + last_send_time_ = send_time; + last_arrival_time_ = arrival_time; + return arrival_time; + } + + int frame_index_; +}; + +TEST_P(NetEqStereoTestDelays, DISABLED_ON_ANDROID(RunTest)) { + RunTest(1000); +} + +class NetEqStereoTestLosses : public NetEqStereoTest { + protected: + static const int kLossInterval = 10; + NetEqStereoTestLosses() + : NetEqStereoTest(), + frame_index_(0) { + } + + virtual bool Lost() { + return (++frame_index_) % kLossInterval == 0; + } + + int frame_index_; +}; + +TEST_P(NetEqStereoTestLosses, DISABLED_ON_ANDROID(RunTest)) { + RunTest(100); +} + + +// Creates a list of parameter sets. +std::list GetTestParameters() { + std::list l; + const int sample_rates[] = {8000, 16000, 32000}; + const int num_rates = sizeof(sample_rates) / sizeof(sample_rates[0]); + // Loop through sample rates. + for (int rate_index = 0; rate_index < num_rates; ++rate_index) { + int sample_rate = sample_rates[rate_index]; + // Loop through all frame sizes between 10 and 60 ms. + for (int frame_size = 10; frame_size <= 60; frame_size += 10) { + TestParameters p; + p.frame_size = frame_size; + p.sample_rate = sample_rate; + p.num_channels = 2; + l.push_back(p); + if (sample_rate == 8000) { + // Add a five-channel test for 8000 Hz. + p.num_channels = 5; + l.push_back(p); + } + } + } + return l; +} + +// Pretty-printing the test parameters in case of an error. +void PrintTo(const TestParameters& p, ::std::ostream* os) { + *os << "{frame_size = " << p.frame_size << + ", num_channels = " << p.num_channels << + ", sample_rate = " << p.sample_rate << "}"; +} + +// Instantiate the tests. Each test is instantiated using the function above, +// so that all different parameter combinations are tested. +INSTANTIATE_TEST_CASE_P(MultiChannel, + NetEqStereoTestNoJitter, + ::testing::ValuesIn(GetTestParameters())); + +INSTANTIATE_TEST_CASE_P(MultiChannel, + NetEqStereoTestPositiveDrift, + ::testing::ValuesIn(GetTestParameters())); + +INSTANTIATE_TEST_CASE_P(MultiChannel, + NetEqStereoTestNegativeDrift, + ::testing::ValuesIn(GetTestParameters())); + +INSTANTIATE_TEST_CASE_P(MultiChannel, + NetEqStereoTestDelays, + ::testing::ValuesIn(GetTestParameters())); + +INSTANTIATE_TEST_CASE_P(MultiChannel, + NetEqStereoTestLosses, + ::testing::ValuesIn(GetTestParameters())); + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_tests.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_tests.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_tests.gypi 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_tests.gypi 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,263 @@ +# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +{ + 'targets': [ + { + 'target_name': 'neteq_rtpplay', + 'type': 'executable', + 'dependencies': [ + 'neteq', + 'neteq_unittest_tools', + 'PCM16B', + '<(webrtc_root)/test/test.gyp:test_support_main', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + ], + 'sources': [ + 'tools/neteq_rtpplay.cc', + ], + 'defines': [ + ], + }, # neteq_rtpplay + + { + 'target_name': 'RTPencode', + 'type': 'executable', + 'dependencies': [ + # TODO(hlundin): Make RTPencode use ACM to encode files. + 'neteq_test_tools',# Test helpers + 'G711', + 'G722', + 'PCM16B', + 'iLBC', + 'iSAC', + 'CNG', + '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', + ], + 'defines': [ + 'CODEC_ILBC', + 'CODEC_PCM16B', + 'CODEC_G711', + 'CODEC_G722', + 'CODEC_ISAC', + 'CODEC_PCM16B_WB', + 'CODEC_ISAC_SWB', + 'CODEC_PCM16B_32KHZ', + 'CODEC_PCM16B_48KHZ', + 'CODEC_CNGCODEC8', + 'CODEC_CNGCODEC16', + 'CODEC_CNGCODEC32', + 'CODEC_ATEVENT_DECODE', + 'CODEC_RED', + ], + 'include_dirs': [ + 'interface', + 'test', + '<(webrtc_root)', + ], + 'sources': [ + 'test/RTPencode.cc', + ], + # Disable warnings to enable Win64 build, issue 1323. + 'msvs_disabled_warnings': [ + 4267, # size_t to int truncation. + ], + }, + + { + 'target_name': 'RTPjitter', + 'type': 'executable', + 'dependencies': [ + '<(DEPTH)/testing/gtest.gyp:gtest', + ], + 'sources': [ + 'test/RTPjitter.cc', + ], + }, + + { + 'target_name': 'rtp_analyze', + 'type': 'executable', + 'dependencies': [ + 'neteq_unittest_tools', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers_default', + ], + 'sources': [ + 'tools/rtp_analyze.cc', + ], + }, + + { + 'target_name': 'RTPchange', + 'type': 'executable', + 'dependencies': [ + 'neteq_test_tools', + '<(DEPTH)/testing/gtest.gyp:gtest', + ], + 'sources': [ + 'test/RTPchange.cc', + ], + }, + + { + 'target_name': 'RTPtimeshift', + 'type': 'executable', + 'dependencies': [ + 'neteq_test_tools', + '<(DEPTH)/testing/gtest.gyp:gtest', + ], + 'sources': [ + 'test/RTPtimeshift.cc', + ], + }, + + { + 'target_name': 'RTPcat', + 'type': 'executable', + 'dependencies': [ + 'neteq_test_tools', + '<(DEPTH)/testing/gtest.gyp:gtest', + ], + 'sources': [ + 'test/RTPcat.cc', + ], + }, + + { + 'target_name': 'rtp_to_text', + 'type': 'executable', + 'dependencies': [ + 'neteq_test_tools', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + ], + 'sources': [ + 'test/rtp_to_text.cc', + ], + }, + + { + 'target_name': 'audio_classifier_test', + 'type': 'executable', + 'dependencies': [ + 'neteq', + ], + 'sources': [ + 'test/audio_classifier_test.cc', + ], + }, + + { + 'target_name': 'neteq_test_support', + 'type': 'static_library', + 'dependencies': [ + 'neteq', + 'PCM16B', + 'neteq_unittest_tools', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + ], + 'sources': [ + 'tools/neteq_performance_test.cc', + 'tools/neteq_performance_test.h', + 'tools/neteq_quality_test.cc', + 'tools/neteq_quality_test.h', + ], + }, # neteq_test_support + + { + 'target_name': 'neteq_speed_test', + 'type': 'executable', + 'dependencies': [ + 'neteq', + 'neteq_test_support', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + '<(webrtc_root)/test/test.gyp:test_support_main', + ], + 'sources': [ + 'test/neteq_speed_test.cc', + ], + }, + + { + 'target_name': 'neteq_opus_fec_quality_test', + 'type': 'executable', + 'dependencies': [ + 'neteq', + 'neteq_test_support', + 'webrtc_opus', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + '<(webrtc_root)/test/test.gyp:test_support_main', + ], + 'sources': [ + 'test/neteq_opus_fec_quality_test.cc', + ], + }, + + { + 'target_name': 'neteq_isac_quality_test', + 'type': 'executable', + 'dependencies': [ + 'neteq', + 'neteq_test_support', + 'iSACFix', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + '<(webrtc_root)/test/test.gyp:test_support_main', + ], + 'sources': [ + 'test/neteq_isac_quality_test.cc', + ], + }, + + { + 'target_name': 'neteq_test_tools', + # Collection of useful functions used in other tests. + 'type': 'static_library', + 'variables': { + # Expects RTP packets without payloads when enabled. + 'neteq_dummy_rtp%': 0, + }, + 'dependencies': [ + 'G711', + 'G722', + 'PCM16B', + 'iLBC', + 'iSAC', + 'CNG', + '<(DEPTH)/testing/gtest.gyp:gtest', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + 'interface', + 'test', + '<(webrtc_root)', + ], + }, + 'defines': [ + ], + 'include_dirs': [ + 'interface', + 'test', + '<(webrtc_root)', + ], + 'sources': [ + 'test/NETEQTEST_DummyRTPpacket.cc', + 'test/NETEQTEST_DummyRTPpacket.h', + 'test/NETEQTEST_RTPpacket.cc', + 'test/NETEQTEST_RTPpacket.h', + ], + # Disable warnings to enable Win64 build, issue 1323. + 'msvs_disabled_warnings': [ + 4267, # size_t to int truncation. + ], + }, + ], # targets +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,1470 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file includes unit tests for NetEQ. + */ + +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" + +#include +#include +#include // memset + +#include +#include +#include +#include + +#include "gflags/gflags.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.h" +#include "webrtc/modules/audio_coding/neteq/tools/audio_loop.h" +#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" +#include "webrtc/test/testsupport/fileutils.h" +#include "webrtc/test/testsupport/gtest_disable.h" +#include "webrtc/typedefs.h" + +DEFINE_bool(gen_ref, false, "Generate reference files."); + +namespace webrtc { + +static bool IsAllZero(const int16_t* buf, int buf_length) { + bool all_zero = true; + for (int n = 0; n < buf_length && all_zero; ++n) + all_zero = buf[n] == 0; + return all_zero; +} + +static bool IsAllNonZero(const int16_t* buf, int buf_length) { + bool all_non_zero = true; + for (int n = 0; n < buf_length && all_non_zero; ++n) + all_non_zero = buf[n] != 0; + return all_non_zero; +} + +class RefFiles { + public: + RefFiles(const std::string& input_file, const std::string& output_file); + ~RefFiles(); + template void ProcessReference(const T& test_results); + template void ProcessReference( + const T (&test_results)[n], + size_t length); + template void WriteToFile( + const T (&test_results)[n], + size_t length); + template void ReadFromFileAndCompare( + const T (&test_results)[n], + size_t length); + void WriteToFile(const NetEqNetworkStatistics& stats); + void ReadFromFileAndCompare(const NetEqNetworkStatistics& stats); + void WriteToFile(const RtcpStatistics& stats); + void ReadFromFileAndCompare(const RtcpStatistics& stats); + + FILE* input_fp_; + FILE* output_fp_; +}; + +RefFiles::RefFiles(const std::string &input_file, + const std::string &output_file) + : input_fp_(NULL), + output_fp_(NULL) { + if (!input_file.empty()) { + input_fp_ = fopen(input_file.c_str(), "rb"); + EXPECT_TRUE(input_fp_ != NULL); + } + if (!output_file.empty()) { + output_fp_ = fopen(output_file.c_str(), "wb"); + EXPECT_TRUE(output_fp_ != NULL); + } +} + +RefFiles::~RefFiles() { + if (input_fp_) { + EXPECT_EQ(EOF, fgetc(input_fp_)); // Make sure that we reached the end. + fclose(input_fp_); + } + if (output_fp_) fclose(output_fp_); +} + +template +void RefFiles::ProcessReference(const T& test_results) { + WriteToFile(test_results); + ReadFromFileAndCompare(test_results); +} + +template +void RefFiles::ProcessReference(const T (&test_results)[n], size_t length) { + WriteToFile(test_results, length); + ReadFromFileAndCompare(test_results, length); +} + +template +void RefFiles::WriteToFile(const T (&test_results)[n], size_t length) { + if (output_fp_) { + ASSERT_EQ(length, fwrite(&test_results, sizeof(T), length, output_fp_)); + } +} + +template +void RefFiles::ReadFromFileAndCompare(const T (&test_results)[n], + size_t length) { + if (input_fp_) { + // Read from ref file. + T* ref = new T[length]; + ASSERT_EQ(length, fread(ref, sizeof(T), length, input_fp_)); + // Compare + ASSERT_EQ(0, memcmp(&test_results, ref, sizeof(T) * length)); + delete [] ref; + } +} + +void RefFiles::WriteToFile(const NetEqNetworkStatistics& stats) { + if (output_fp_) { + ASSERT_EQ(1u, fwrite(&stats, sizeof(NetEqNetworkStatistics), 1, + output_fp_)); + } +} + +void RefFiles::ReadFromFileAndCompare( + const NetEqNetworkStatistics& stats) { + if (input_fp_) { + // Read from ref file. + size_t stat_size = sizeof(NetEqNetworkStatistics); + NetEqNetworkStatistics ref_stats; + ASSERT_EQ(1u, fread(&ref_stats, stat_size, 1, input_fp_)); + // Compare + ASSERT_EQ(0, memcmp(&stats, &ref_stats, stat_size)); + } +} + +void RefFiles::WriteToFile(const RtcpStatistics& stats) { + if (output_fp_) { + ASSERT_EQ(1u, fwrite(&(stats.fraction_lost), sizeof(stats.fraction_lost), 1, + output_fp_)); + ASSERT_EQ(1u, fwrite(&(stats.cumulative_lost), + sizeof(stats.cumulative_lost), 1, output_fp_)); + ASSERT_EQ(1u, fwrite(&(stats.extended_max_sequence_number), + sizeof(stats.extended_max_sequence_number), 1, + output_fp_)); + ASSERT_EQ(1u, fwrite(&(stats.jitter), sizeof(stats.jitter), 1, + output_fp_)); + } +} + +void RefFiles::ReadFromFileAndCompare( + const RtcpStatistics& stats) { + if (input_fp_) { + // Read from ref file. + RtcpStatistics ref_stats; + ASSERT_EQ(1u, fread(&(ref_stats.fraction_lost), + sizeof(ref_stats.fraction_lost), 1, input_fp_)); + ASSERT_EQ(1u, fread(&(ref_stats.cumulative_lost), + sizeof(ref_stats.cumulative_lost), 1, input_fp_)); + ASSERT_EQ(1u, fread(&(ref_stats.extended_max_sequence_number), + sizeof(ref_stats.extended_max_sequence_number), 1, + input_fp_)); + ASSERT_EQ(1u, fread(&(ref_stats.jitter), sizeof(ref_stats.jitter), 1, + input_fp_)); + // Compare + ASSERT_EQ(ref_stats.fraction_lost, stats.fraction_lost); + ASSERT_EQ(ref_stats.cumulative_lost, stats.cumulative_lost); + ASSERT_EQ(ref_stats.extended_max_sequence_number, + stats.extended_max_sequence_number); + ASSERT_EQ(ref_stats.jitter, stats.jitter); + } +} + +class NetEqDecodingTest : public ::testing::Test { + protected: + // NetEQ must be polled for data once every 10 ms. Thus, neither of the + // constants below can be changed. + static const int kTimeStepMs = 10; + static const int kBlockSize8kHz = kTimeStepMs * 8; + static const int kBlockSize16kHz = kTimeStepMs * 16; + static const int kBlockSize32kHz = kTimeStepMs * 32; + static const int kMaxBlockSize = kBlockSize32kHz; + static const int kInitSampleRateHz = 8000; + + NetEqDecodingTest(); + virtual void SetUp(); + virtual void TearDown(); + void SelectDecoders(NetEqDecoder* used_codec); + void LoadDecoders(); + void OpenInputFile(const std::string &rtp_file); + void Process(NETEQTEST_RTPpacket* rtp_ptr, int* out_len); + void DecodeAndCompare(const std::string& rtp_file, + const std::string& ref_file, + const std::string& stat_ref_file, + const std::string& rtcp_ref_file); + static void PopulateRtpInfo(int frame_index, + int timestamp, + WebRtcRTPHeader* rtp_info); + static void PopulateCng(int frame_index, + int timestamp, + WebRtcRTPHeader* rtp_info, + uint8_t* payload, + int* payload_len); + + void WrapTest(uint16_t start_seq_no, uint32_t start_timestamp, + const std::set& drop_seq_numbers, + bool expect_seq_no_wrap, bool expect_timestamp_wrap); + + void LongCngWithClockDrift(double drift_factor, + double network_freeze_ms, + bool pull_audio_during_freeze, + int delay_tolerance_ms, + int max_time_to_speech_ms); + + void DuplicateCng(); + + uint32_t PlayoutTimestamp(); + + NetEq* neteq_; + NetEq::Config config_; + FILE* rtp_fp_; + unsigned int sim_clock_; + int16_t out_data_[kMaxBlockSize]; + int output_sample_rate_; + int algorithmic_delay_ms_; +}; + +// Allocating the static const so that it can be passed by reference. +const int NetEqDecodingTest::kTimeStepMs; +const int NetEqDecodingTest::kBlockSize8kHz; +const int NetEqDecodingTest::kBlockSize16kHz; +const int NetEqDecodingTest::kBlockSize32kHz; +const int NetEqDecodingTest::kMaxBlockSize; +const int NetEqDecodingTest::kInitSampleRateHz; + +NetEqDecodingTest::NetEqDecodingTest() + : neteq_(NULL), + config_(), + rtp_fp_(NULL), + sim_clock_(0), + output_sample_rate_(kInitSampleRateHz), + algorithmic_delay_ms_(0) { + config_.sample_rate_hz = kInitSampleRateHz; + memset(out_data_, 0, sizeof(out_data_)); +} + +void NetEqDecodingTest::SetUp() { + neteq_ = NetEq::Create(config_); + NetEqNetworkStatistics stat; + ASSERT_EQ(0, neteq_->NetworkStatistics(&stat)); + algorithmic_delay_ms_ = stat.current_buffer_size_ms; + ASSERT_TRUE(neteq_); + LoadDecoders(); +} + +void NetEqDecodingTest::TearDown() { + delete neteq_; + if (rtp_fp_) + fclose(rtp_fp_); +} + +void NetEqDecodingTest::LoadDecoders() { + // Load PCMu. + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderPCMu, 0)); + // Load PCMa. + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderPCMa, 8)); +#ifndef WEBRTC_ANDROID + // Load iLBC. + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderILBC, 102)); +#endif // WEBRTC_ANDROID + // Load iSAC. + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderISAC, 103)); +#ifndef WEBRTC_ANDROID + // Load iSAC SWB. + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderISACswb, 104)); + // Load iSAC FB. + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderISACfb, 105)); +#endif // WEBRTC_ANDROID + // Load PCM16B nb. + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderPCM16B, 93)); + // Load PCM16B wb. + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderPCM16Bwb, 94)); + // Load PCM16B swb32. + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderPCM16Bswb32kHz, 95)); + // Load CNG 8 kHz. + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderCNGnb, 13)); + // Load CNG 16 kHz. + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderCNGwb, 98)); +} + +void NetEqDecodingTest::OpenInputFile(const std::string &rtp_file) { + rtp_fp_ = fopen(rtp_file.c_str(), "rb"); + ASSERT_TRUE(rtp_fp_ != NULL); + ASSERT_EQ(0, NETEQTEST_RTPpacket::skipFileHeader(rtp_fp_)); +} + +void NetEqDecodingTest::Process(NETEQTEST_RTPpacket* rtp, int* out_len) { + // Check if time to receive. + while ((sim_clock_ >= rtp->time()) && + (rtp->dataLen() >= 0)) { + if (rtp->dataLen() > 0) { + WebRtcRTPHeader rtpInfo; + rtp->parseHeader(&rtpInfo); + ASSERT_EQ(0, neteq_->InsertPacket( + rtpInfo, + rtp->payload(), + rtp->payloadLen(), + rtp->time() * (output_sample_rate_ / 1000))); + } + // Get next packet. + ASSERT_NE(-1, rtp->readFromFile(rtp_fp_)); + } + + // Get audio from NetEq. + NetEqOutputType type; + int num_channels; + ASSERT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, out_len, + &num_channels, &type)); + ASSERT_TRUE((*out_len == kBlockSize8kHz) || + (*out_len == kBlockSize16kHz) || + (*out_len == kBlockSize32kHz)); + output_sample_rate_ = *out_len / 10 * 1000; + + // Increase time. + sim_clock_ += kTimeStepMs; +} + +void NetEqDecodingTest::DecodeAndCompare(const std::string& rtp_file, + const std::string& ref_file, + const std::string& stat_ref_file, + const std::string& rtcp_ref_file) { + OpenInputFile(rtp_file); + + std::string ref_out_file = ""; + if (ref_file.empty()) { + ref_out_file = webrtc::test::OutputPath() + "neteq_universal_ref.pcm"; + } + RefFiles ref_files(ref_file, ref_out_file); + + std::string stat_out_file = ""; + if (stat_ref_file.empty()) { + stat_out_file = webrtc::test::OutputPath() + "neteq_network_stats.dat"; + } + RefFiles network_stat_files(stat_ref_file, stat_out_file); + + std::string rtcp_out_file = ""; + if (rtcp_ref_file.empty()) { + rtcp_out_file = webrtc::test::OutputPath() + "neteq_rtcp_stats.dat"; + } + RefFiles rtcp_stat_files(rtcp_ref_file, rtcp_out_file); + + NETEQTEST_RTPpacket rtp; + ASSERT_GT(rtp.readFromFile(rtp_fp_), 0); + int i = 0; + while (rtp.dataLen() >= 0) { + std::ostringstream ss; + ss << "Lap number " << i++ << " in DecodeAndCompare while loop"; + SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. + int out_len = 0; + ASSERT_NO_FATAL_FAILURE(Process(&rtp, &out_len)); + ASSERT_NO_FATAL_FAILURE(ref_files.ProcessReference(out_data_, out_len)); + + // Query the network statistics API once per second + if (sim_clock_ % 1000 == 0) { + // Process NetworkStatistics. + NetEqNetworkStatistics network_stats; + ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats)); + ASSERT_NO_FATAL_FAILURE( + network_stat_files.ProcessReference(network_stats)); + + // Process RTCPstat. + RtcpStatistics rtcp_stats; + neteq_->GetRtcpStatistics(&rtcp_stats); + ASSERT_NO_FATAL_FAILURE(rtcp_stat_files.ProcessReference(rtcp_stats)); + } + } +} + +void NetEqDecodingTest::PopulateRtpInfo(int frame_index, + int timestamp, + WebRtcRTPHeader* rtp_info) { + rtp_info->header.sequenceNumber = frame_index; + rtp_info->header.timestamp = timestamp; + rtp_info->header.ssrc = 0x1234; // Just an arbitrary SSRC. + rtp_info->header.payloadType = 94; // PCM16b WB codec. + rtp_info->header.markerBit = 0; +} + +void NetEqDecodingTest::PopulateCng(int frame_index, + int timestamp, + WebRtcRTPHeader* rtp_info, + uint8_t* payload, + int* payload_len) { + rtp_info->header.sequenceNumber = frame_index; + rtp_info->header.timestamp = timestamp; + rtp_info->header.ssrc = 0x1234; // Just an arbitrary SSRC. + rtp_info->header.payloadType = 98; // WB CNG. + rtp_info->header.markerBit = 0; + payload[0] = 64; // Noise level -64 dBov, quite arbitrarily chosen. + *payload_len = 1; // Only noise level, no spectral parameters. +} + +TEST_F(NetEqDecodingTest, DISABLED_ON_ANDROID(TestBitExactness)) { + const std::string input_rtp_file = webrtc::test::ProjectRootPath() + + "resources/audio_coding/neteq_universal_new.rtp"; + // Note that neteq4_universal_ref.pcm and neteq4_universal_ref_win_32.pcm + // are identical. The latter could have been removed, but if clients still + // have a copy of the file, the test will fail. + const std::string input_ref_file = + webrtc::test::ResourcePath("audio_coding/neteq4_universal_ref", "pcm"); +#if defined(_MSC_VER) && (_MSC_VER >= 1700) + // For Visual Studio 2012 and later, we will have to use the generic reference + // file, rather than the windows-specific one. + const std::string network_stat_ref_file = webrtc::test::ProjectRootPath() + + "resources/audio_coding/neteq4_network_stats.dat"; +#else + const std::string network_stat_ref_file = + webrtc::test::ResourcePath("audio_coding/neteq4_network_stats", "dat"); +#endif + const std::string rtcp_stat_ref_file = + webrtc::test::ResourcePath("audio_coding/neteq4_rtcp_stats", "dat"); + + if (FLAGS_gen_ref) { + DecodeAndCompare(input_rtp_file, "", "", ""); + } else { + DecodeAndCompare(input_rtp_file, + input_ref_file, + network_stat_ref_file, + rtcp_stat_ref_file); + } +} + +// Use fax mode to avoid time-scaling. This is to simplify the testing of +// packet waiting times in the packet buffer. +class NetEqDecodingTestFaxMode : public NetEqDecodingTest { + protected: + NetEqDecodingTestFaxMode() : NetEqDecodingTest() { + config_.playout_mode = kPlayoutFax; + } +}; + +TEST_F(NetEqDecodingTestFaxMode, TestFrameWaitingTimeStatistics) { + // Insert 30 dummy packets at once. Each packet contains 10 ms 16 kHz audio. + size_t num_frames = 30; + const int kSamples = 10 * 16; + const int kPayloadBytes = kSamples * 2; + for (size_t i = 0; i < num_frames; ++i) { + uint16_t payload[kSamples] = {0}; + WebRtcRTPHeader rtp_info; + rtp_info.header.sequenceNumber = i; + rtp_info.header.timestamp = i * kSamples; + rtp_info.header.ssrc = 0x1234; // Just an arbitrary SSRC. + rtp_info.header.payloadType = 94; // PCM16b WB codec. + rtp_info.header.markerBit = 0; + ASSERT_EQ(0, neteq_->InsertPacket( + rtp_info, + reinterpret_cast(payload), + kPayloadBytes, 0)); + } + // Pull out all data. + for (size_t i = 0; i < num_frames; ++i) { + int out_len; + int num_channels; + NetEqOutputType type; + ASSERT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, &out_len, + &num_channels, &type)); + ASSERT_EQ(kBlockSize16kHz, out_len); + } + + std::vector waiting_times; + neteq_->WaitingTimes(&waiting_times); + EXPECT_EQ(num_frames, waiting_times.size()); + // Since all frames are dumped into NetEQ at once, but pulled out with 10 ms + // spacing (per definition), we expect the delay to increase with 10 ms for + // each packet. + for (size_t i = 0; i < waiting_times.size(); ++i) { + EXPECT_EQ(static_cast(i + 1) * 10, waiting_times[i]); + } + + // Check statistics again and make sure it's been reset. + neteq_->WaitingTimes(&waiting_times); + int len = waiting_times.size(); + EXPECT_EQ(0, len); + + // Process > 100 frames, and make sure that that we get statistics + // only for 100 frames. Note the new SSRC, causing NetEQ to reset. + num_frames = 110; + for (size_t i = 0; i < num_frames; ++i) { + uint16_t payload[kSamples] = {0}; + WebRtcRTPHeader rtp_info; + rtp_info.header.sequenceNumber = i; + rtp_info.header.timestamp = i * kSamples; + rtp_info.header.ssrc = 0x1235; // Just an arbitrary SSRC. + rtp_info.header.payloadType = 94; // PCM16b WB codec. + rtp_info.header.markerBit = 0; + ASSERT_EQ(0, neteq_->InsertPacket( + rtp_info, + reinterpret_cast(payload), + kPayloadBytes, 0)); + int out_len; + int num_channels; + NetEqOutputType type; + ASSERT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, &out_len, + &num_channels, &type)); + ASSERT_EQ(kBlockSize16kHz, out_len); + } + + neteq_->WaitingTimes(&waiting_times); + EXPECT_EQ(100u, waiting_times.size()); +} + +TEST_F(NetEqDecodingTest, TestAverageInterArrivalTimeNegative) { + const int kNumFrames = 3000; // Needed for convergence. + int frame_index = 0; + const int kSamples = 10 * 16; + const int kPayloadBytes = kSamples * 2; + while (frame_index < kNumFrames) { + // Insert one packet each time, except every 10th time where we insert two + // packets at once. This will create a negative clock-drift of approx. 10%. + int num_packets = (frame_index % 10 == 0 ? 2 : 1); + for (int n = 0; n < num_packets; ++n) { + uint8_t payload[kPayloadBytes] = {0}; + WebRtcRTPHeader rtp_info; + PopulateRtpInfo(frame_index, frame_index * kSamples, &rtp_info); + ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, 0)); + ++frame_index; + } + + // Pull out data once. + int out_len; + int num_channels; + NetEqOutputType type; + ASSERT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, &out_len, + &num_channels, &type)); + ASSERT_EQ(kBlockSize16kHz, out_len); + } + + NetEqNetworkStatistics network_stats; + ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats)); + EXPECT_EQ(-103196, network_stats.clockdrift_ppm); +} + +TEST_F(NetEqDecodingTest, TestAverageInterArrivalTimePositive) { + const int kNumFrames = 5000; // Needed for convergence. + int frame_index = 0; + const int kSamples = 10 * 16; + const int kPayloadBytes = kSamples * 2; + for (int i = 0; i < kNumFrames; ++i) { + // Insert one packet each time, except every 10th time where we don't insert + // any packet. This will create a positive clock-drift of approx. 11%. + int num_packets = (i % 10 == 9 ? 0 : 1); + for (int n = 0; n < num_packets; ++n) { + uint8_t payload[kPayloadBytes] = {0}; + WebRtcRTPHeader rtp_info; + PopulateRtpInfo(frame_index, frame_index * kSamples, &rtp_info); + ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, 0)); + ++frame_index; + } + + // Pull out data once. + int out_len; + int num_channels; + NetEqOutputType type; + ASSERT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, &out_len, + &num_channels, &type)); + ASSERT_EQ(kBlockSize16kHz, out_len); + } + + NetEqNetworkStatistics network_stats; + ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats)); + EXPECT_EQ(110946, network_stats.clockdrift_ppm); +} + +void NetEqDecodingTest::LongCngWithClockDrift(double drift_factor, + double network_freeze_ms, + bool pull_audio_during_freeze, + int delay_tolerance_ms, + int max_time_to_speech_ms) { + uint16_t seq_no = 0; + uint32_t timestamp = 0; + const int kFrameSizeMs = 30; + const int kSamples = kFrameSizeMs * 16; + const int kPayloadBytes = kSamples * 2; + double next_input_time_ms = 0.0; + double t_ms; + int out_len; + int num_channels; + NetEqOutputType type; + + // Insert speech for 5 seconds. + const int kSpeechDurationMs = 5000; + for (t_ms = 0; t_ms < kSpeechDurationMs; t_ms += 10) { + // Each turn in this for loop is 10 ms. + while (next_input_time_ms <= t_ms) { + // Insert one 30 ms speech frame. + uint8_t payload[kPayloadBytes] = {0}; + WebRtcRTPHeader rtp_info; + PopulateRtpInfo(seq_no, timestamp, &rtp_info); + ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, 0)); + ++seq_no; + timestamp += kSamples; + next_input_time_ms += static_cast(kFrameSizeMs) * drift_factor; + } + // Pull out data once. + ASSERT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, &out_len, + &num_channels, &type)); + ASSERT_EQ(kBlockSize16kHz, out_len); + } + + EXPECT_EQ(kOutputNormal, type); + int32_t delay_before = timestamp - PlayoutTimestamp(); + + // Insert CNG for 1 minute (= 60000 ms). + const int kCngPeriodMs = 100; + const int kCngPeriodSamples = kCngPeriodMs * 16; // Period in 16 kHz samples. + const int kCngDurationMs = 60000; + for (; t_ms < kSpeechDurationMs + kCngDurationMs; t_ms += 10) { + // Each turn in this for loop is 10 ms. + while (next_input_time_ms <= t_ms) { + // Insert one CNG frame each 100 ms. + uint8_t payload[kPayloadBytes]; + int payload_len; + WebRtcRTPHeader rtp_info; + PopulateCng(seq_no, timestamp, &rtp_info, payload, &payload_len); + ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, payload_len, 0)); + ++seq_no; + timestamp += kCngPeriodSamples; + next_input_time_ms += static_cast(kCngPeriodMs) * drift_factor; + } + // Pull out data once. + ASSERT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, &out_len, + &num_channels, &type)); + ASSERT_EQ(kBlockSize16kHz, out_len); + } + + EXPECT_EQ(kOutputCNG, type); + + if (network_freeze_ms > 0) { + // First keep pulling audio for |network_freeze_ms| without inserting + // any data, then insert CNG data corresponding to |network_freeze_ms| + // without pulling any output audio. + const double loop_end_time = t_ms + network_freeze_ms; + for (; t_ms < loop_end_time; t_ms += 10) { + // Pull out data once. + ASSERT_EQ(0, + neteq_->GetAudio( + kMaxBlockSize, out_data_, &out_len, &num_channels, &type)); + ASSERT_EQ(kBlockSize16kHz, out_len); + EXPECT_EQ(kOutputCNG, type); + } + bool pull_once = pull_audio_during_freeze; + // If |pull_once| is true, GetAudio will be called once half-way through + // the network recovery period. + double pull_time_ms = (t_ms + next_input_time_ms) / 2; + while (next_input_time_ms <= t_ms) { + if (pull_once && next_input_time_ms >= pull_time_ms) { + pull_once = false; + // Pull out data once. + ASSERT_EQ( + 0, + neteq_->GetAudio( + kMaxBlockSize, out_data_, &out_len, &num_channels, &type)); + ASSERT_EQ(kBlockSize16kHz, out_len); + EXPECT_EQ(kOutputCNG, type); + t_ms += 10; + } + // Insert one CNG frame each 100 ms. + uint8_t payload[kPayloadBytes]; + int payload_len; + WebRtcRTPHeader rtp_info; + PopulateCng(seq_no, timestamp, &rtp_info, payload, &payload_len); + ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, payload_len, 0)); + ++seq_no; + timestamp += kCngPeriodSamples; + next_input_time_ms += kCngPeriodMs * drift_factor; + } + } + + // Insert speech again until output type is speech. + double speech_restart_time_ms = t_ms; + while (type != kOutputNormal) { + // Each turn in this for loop is 10 ms. + while (next_input_time_ms <= t_ms) { + // Insert one 30 ms speech frame. + uint8_t payload[kPayloadBytes] = {0}; + WebRtcRTPHeader rtp_info; + PopulateRtpInfo(seq_no, timestamp, &rtp_info); + ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, 0)); + ++seq_no; + timestamp += kSamples; + next_input_time_ms += kFrameSizeMs * drift_factor; + } + // Pull out data once. + ASSERT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, &out_len, + &num_channels, &type)); + ASSERT_EQ(kBlockSize16kHz, out_len); + // Increase clock. + t_ms += 10; + } + + // Check that the speech starts again within reasonable time. + double time_until_speech_returns_ms = t_ms - speech_restart_time_ms; + EXPECT_LT(time_until_speech_returns_ms, max_time_to_speech_ms); + int32_t delay_after = timestamp - PlayoutTimestamp(); + // Compare delay before and after, and make sure it differs less than 20 ms. + EXPECT_LE(delay_after, delay_before + delay_tolerance_ms * 16); + EXPECT_GE(delay_after, delay_before - delay_tolerance_ms * 16); +} + +TEST_F(NetEqDecodingTest, LongCngWithNegativeClockDrift) { + // Apply a clock drift of -25 ms / s (sender faster than receiver). + const double kDriftFactor = 1000.0 / (1000.0 + 25.0); + const double kNetworkFreezeTimeMs = 0.0; + const bool kGetAudioDuringFreezeRecovery = false; + const int kDelayToleranceMs = 20; + const int kMaxTimeToSpeechMs = 100; + LongCngWithClockDrift(kDriftFactor, + kNetworkFreezeTimeMs, + kGetAudioDuringFreezeRecovery, + kDelayToleranceMs, + kMaxTimeToSpeechMs); +} + +TEST_F(NetEqDecodingTest, LongCngWithPositiveClockDrift) { + // Apply a clock drift of +25 ms / s (sender slower than receiver). + const double kDriftFactor = 1000.0 / (1000.0 - 25.0); + const double kNetworkFreezeTimeMs = 0.0; + const bool kGetAudioDuringFreezeRecovery = false; + const int kDelayToleranceMs = 20; + const int kMaxTimeToSpeechMs = 100; + LongCngWithClockDrift(kDriftFactor, + kNetworkFreezeTimeMs, + kGetAudioDuringFreezeRecovery, + kDelayToleranceMs, + kMaxTimeToSpeechMs); +} + +TEST_F(NetEqDecodingTest, LongCngWithNegativeClockDriftNetworkFreeze) { + // Apply a clock drift of -25 ms / s (sender faster than receiver). + const double kDriftFactor = 1000.0 / (1000.0 + 25.0); + const double kNetworkFreezeTimeMs = 5000.0; + const bool kGetAudioDuringFreezeRecovery = false; + const int kDelayToleranceMs = 50; + const int kMaxTimeToSpeechMs = 200; + LongCngWithClockDrift(kDriftFactor, + kNetworkFreezeTimeMs, + kGetAudioDuringFreezeRecovery, + kDelayToleranceMs, + kMaxTimeToSpeechMs); +} + +TEST_F(NetEqDecodingTest, LongCngWithPositiveClockDriftNetworkFreeze) { + // Apply a clock drift of +25 ms / s (sender slower than receiver). + const double kDriftFactor = 1000.0 / (1000.0 - 25.0); + const double kNetworkFreezeTimeMs = 5000.0; + const bool kGetAudioDuringFreezeRecovery = false; + const int kDelayToleranceMs = 20; + const int kMaxTimeToSpeechMs = 100; + LongCngWithClockDrift(kDriftFactor, + kNetworkFreezeTimeMs, + kGetAudioDuringFreezeRecovery, + kDelayToleranceMs, + kMaxTimeToSpeechMs); +} + +TEST_F(NetEqDecodingTest, LongCngWithPositiveClockDriftNetworkFreezeExtraPull) { + // Apply a clock drift of +25 ms / s (sender slower than receiver). + const double kDriftFactor = 1000.0 / (1000.0 - 25.0); + const double kNetworkFreezeTimeMs = 5000.0; + const bool kGetAudioDuringFreezeRecovery = true; + const int kDelayToleranceMs = 20; + const int kMaxTimeToSpeechMs = 100; + LongCngWithClockDrift(kDriftFactor, + kNetworkFreezeTimeMs, + kGetAudioDuringFreezeRecovery, + kDelayToleranceMs, + kMaxTimeToSpeechMs); +} + +TEST_F(NetEqDecodingTest, LongCngWithoutClockDrift) { + const double kDriftFactor = 1.0; // No drift. + const double kNetworkFreezeTimeMs = 0.0; + const bool kGetAudioDuringFreezeRecovery = false; + const int kDelayToleranceMs = 10; + const int kMaxTimeToSpeechMs = 50; + LongCngWithClockDrift(kDriftFactor, + kNetworkFreezeTimeMs, + kGetAudioDuringFreezeRecovery, + kDelayToleranceMs, + kMaxTimeToSpeechMs); +} + +TEST_F(NetEqDecodingTest, UnknownPayloadType) { + const int kPayloadBytes = 100; + uint8_t payload[kPayloadBytes] = {0}; + WebRtcRTPHeader rtp_info; + PopulateRtpInfo(0, 0, &rtp_info); + rtp_info.header.payloadType = 1; // Not registered as a decoder. + EXPECT_EQ(NetEq::kFail, + neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, 0)); + EXPECT_EQ(NetEq::kUnknownRtpPayloadType, neteq_->LastError()); +} + +TEST_F(NetEqDecodingTest, DISABLED_ON_ANDROID(DecoderError)) { + const int kPayloadBytes = 100; + uint8_t payload[kPayloadBytes] = {0}; + WebRtcRTPHeader rtp_info; + PopulateRtpInfo(0, 0, &rtp_info); + rtp_info.header.payloadType = 103; // iSAC, but the payload is invalid. + EXPECT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, 0)); + NetEqOutputType type; + // Set all of |out_data_| to 1, and verify that it was set to 0 by the call + // to GetAudio. + for (int i = 0; i < kMaxBlockSize; ++i) { + out_data_[i] = 1; + } + int num_channels; + int samples_per_channel; + EXPECT_EQ(NetEq::kFail, + neteq_->GetAudio(kMaxBlockSize, out_data_, + &samples_per_channel, &num_channels, &type)); + // Verify that there is a decoder error to check. + EXPECT_EQ(NetEq::kDecoderErrorCode, neteq_->LastError()); + // Code 6730 is an iSAC error code. + EXPECT_EQ(6730, neteq_->LastDecoderError()); + // Verify that the first 160 samples are set to 0, and that the remaining + // samples are left unmodified. + static const int kExpectedOutputLength = 160; // 10 ms at 16 kHz sample rate. + for (int i = 0; i < kExpectedOutputLength; ++i) { + std::ostringstream ss; + ss << "i = " << i; + SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. + EXPECT_EQ(0, out_data_[i]); + } + for (int i = kExpectedOutputLength; i < kMaxBlockSize; ++i) { + std::ostringstream ss; + ss << "i = " << i; + SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. + EXPECT_EQ(1, out_data_[i]); + } +} + +TEST_F(NetEqDecodingTest, GetAudioBeforeInsertPacket) { + NetEqOutputType type; + // Set all of |out_data_| to 1, and verify that it was set to 0 by the call + // to GetAudio. + for (int i = 0; i < kMaxBlockSize; ++i) { + out_data_[i] = 1; + } + int num_channels; + int samples_per_channel; + EXPECT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, + &samples_per_channel, + &num_channels, &type)); + // Verify that the first block of samples is set to 0. + static const int kExpectedOutputLength = + kInitSampleRateHz / 100; // 10 ms at initial sample rate. + for (int i = 0; i < kExpectedOutputLength; ++i) { + std::ostringstream ss; + ss << "i = " << i; + SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. + EXPECT_EQ(0, out_data_[i]); + } +} + +class NetEqBgnTest : public NetEqDecodingTest { + protected: + virtual void TestCondition(double sum_squared_noise, + bool should_be_faded) = 0; + + void CheckBgn(int sampling_rate_hz) { + int expected_samples_per_channel = 0; + uint8_t payload_type = 0xFF; // Invalid. + if (sampling_rate_hz == 8000) { + expected_samples_per_channel = kBlockSize8kHz; + payload_type = 93; // PCM 16, 8 kHz. + } else if (sampling_rate_hz == 16000) { + expected_samples_per_channel = kBlockSize16kHz; + payload_type = 94; // PCM 16, 16 kHZ. + } else if (sampling_rate_hz == 32000) { + expected_samples_per_channel = kBlockSize32kHz; + payload_type = 95; // PCM 16, 32 kHz. + } else { + ASSERT_TRUE(false); // Unsupported test case. + } + + NetEqOutputType type; + int16_t output[kBlockSize32kHz]; // Maximum size is chosen. + test::AudioLoop input; + // We are using the same 32 kHz input file for all tests, regardless of + // |sampling_rate_hz|. The output may sound weird, but the test is still + // valid. + ASSERT_TRUE(input.Init( + webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"), + 10 * sampling_rate_hz, // Max 10 seconds loop length. + expected_samples_per_channel)); + + // Payload of 10 ms of PCM16 32 kHz. + uint8_t payload[kBlockSize32kHz * sizeof(int16_t)]; + WebRtcRTPHeader rtp_info; + PopulateRtpInfo(0, 0, &rtp_info); + rtp_info.header.payloadType = payload_type; + + int number_channels = 0; + int samples_per_channel = 0; + + uint32_t receive_timestamp = 0; + for (int n = 0; n < 10; ++n) { // Insert few packets and get audio. + int enc_len_bytes = + WebRtcPcm16b_EncodeW16(input.GetNextBlock(), + expected_samples_per_channel, + reinterpret_cast(payload)); + ASSERT_EQ(enc_len_bytes, expected_samples_per_channel * 2); + + number_channels = 0; + samples_per_channel = 0; + ASSERT_EQ(0, + neteq_->InsertPacket( + rtp_info, payload, enc_len_bytes, receive_timestamp)); + ASSERT_EQ(0, + neteq_->GetAudio(kBlockSize32kHz, + output, + &samples_per_channel, + &number_channels, + &type)); + ASSERT_EQ(1, number_channels); + ASSERT_EQ(expected_samples_per_channel, samples_per_channel); + ASSERT_EQ(kOutputNormal, type); + + // Next packet. + rtp_info.header.timestamp += expected_samples_per_channel; + rtp_info.header.sequenceNumber++; + receive_timestamp += expected_samples_per_channel; + } + + number_channels = 0; + samples_per_channel = 0; + + // Get audio without inserting packets, expecting PLC and PLC-to-CNG. Pull + // one frame without checking speech-type. This is the first frame pulled + // without inserting any packet, and might not be labeled as PLC. + ASSERT_EQ(0, + neteq_->GetAudio(kBlockSize32kHz, + output, + &samples_per_channel, + &number_channels, + &type)); + ASSERT_EQ(1, number_channels); + ASSERT_EQ(expected_samples_per_channel, samples_per_channel); + + // To be able to test the fading of background noise we need at lease to + // pull 611 frames. + const int kFadingThreshold = 611; + + // Test several CNG-to-PLC packet for the expected behavior. The number 20 + // is arbitrary, but sufficiently large to test enough number of frames. + const int kNumPlcToCngTestFrames = 20; + bool plc_to_cng = false; + for (int n = 0; n < kFadingThreshold + kNumPlcToCngTestFrames; ++n) { + number_channels = 0; + samples_per_channel = 0; + memset(output, 1, sizeof(output)); // Set to non-zero. + ASSERT_EQ(0, + neteq_->GetAudio(kBlockSize32kHz, + output, + &samples_per_channel, + &number_channels, + &type)); + ASSERT_EQ(1, number_channels); + ASSERT_EQ(expected_samples_per_channel, samples_per_channel); + if (type == kOutputPLCtoCNG) { + plc_to_cng = true; + double sum_squared = 0; + for (int k = 0; k < number_channels * samples_per_channel; ++k) + sum_squared += output[k] * output[k]; + TestCondition(sum_squared, n > kFadingThreshold); + } else { + EXPECT_EQ(kOutputPLC, type); + } + } + EXPECT_TRUE(plc_to_cng); // Just to be sure that PLC-to-CNG has occurred. + } +}; + +class NetEqBgnTestOn : public NetEqBgnTest { + protected: + NetEqBgnTestOn() : NetEqBgnTest() { + config_.background_noise_mode = NetEq::kBgnOn; + } + + void TestCondition(double sum_squared_noise, bool /*should_be_faded*/) { + EXPECT_NE(0, sum_squared_noise); + } +}; + +class NetEqBgnTestOff : public NetEqBgnTest { + protected: + NetEqBgnTestOff() : NetEqBgnTest() { + config_.background_noise_mode = NetEq::kBgnOff; + } + + void TestCondition(double sum_squared_noise, bool /*should_be_faded*/) { + EXPECT_EQ(0, sum_squared_noise); + } +}; + +class NetEqBgnTestFade : public NetEqBgnTest { + protected: + NetEqBgnTestFade() : NetEqBgnTest() { + config_.background_noise_mode = NetEq::kBgnFade; + } + + void TestCondition(double sum_squared_noise, bool should_be_faded) { + if (should_be_faded) + EXPECT_EQ(0, sum_squared_noise); + } +}; + +TEST_F(NetEqBgnTestOn, RunTest) { + CheckBgn(8000); + CheckBgn(16000); + CheckBgn(32000); +} + +TEST_F(NetEqBgnTestOff, RunTest) { + CheckBgn(8000); + CheckBgn(16000); + CheckBgn(32000); +} + +TEST_F(NetEqBgnTestFade, RunTest) { + CheckBgn(8000); + CheckBgn(16000); + CheckBgn(32000); +} + +TEST_F(NetEqDecodingTest, SyncPacketInsert) { + WebRtcRTPHeader rtp_info; + uint32_t receive_timestamp = 0; + // For the readability use the following payloads instead of the defaults of + // this test. + uint8_t kPcm16WbPayloadType = 1; + uint8_t kCngNbPayloadType = 2; + uint8_t kCngWbPayloadType = 3; + uint8_t kCngSwb32PayloadType = 4; + uint8_t kCngSwb48PayloadType = 5; + uint8_t kAvtPayloadType = 6; + uint8_t kRedPayloadType = 7; + uint8_t kIsacPayloadType = 9; // Payload type 8 is already registered. + + // Register decoders. + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderPCM16Bwb, + kPcm16WbPayloadType)); + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderCNGnb, kCngNbPayloadType)); + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderCNGwb, kCngWbPayloadType)); + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderCNGswb32kHz, + kCngSwb32PayloadType)); + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderCNGswb48kHz, + kCngSwb48PayloadType)); + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderAVT, kAvtPayloadType)); + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderRED, kRedPayloadType)); + ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderISAC, kIsacPayloadType)); + + PopulateRtpInfo(0, 0, &rtp_info); + rtp_info.header.payloadType = kPcm16WbPayloadType; + + // The first packet injected cannot be sync-packet. + EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); + + // Payload length of 10 ms PCM16 16 kHz. + const int kPayloadBytes = kBlockSize16kHz * sizeof(int16_t); + uint8_t payload[kPayloadBytes] = {0}; + ASSERT_EQ(0, neteq_->InsertPacket( + rtp_info, payload, kPayloadBytes, receive_timestamp)); + + // Next packet. Last packet contained 10 ms audio. + rtp_info.header.sequenceNumber++; + rtp_info.header.timestamp += kBlockSize16kHz; + receive_timestamp += kBlockSize16kHz; + + // Unacceptable payload types CNG, AVT (DTMF), RED. + rtp_info.header.payloadType = kCngNbPayloadType; + EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); + + rtp_info.header.payloadType = kCngWbPayloadType; + EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); + + rtp_info.header.payloadType = kCngSwb32PayloadType; + EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); + + rtp_info.header.payloadType = kCngSwb48PayloadType; + EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); + + rtp_info.header.payloadType = kAvtPayloadType; + EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); + + rtp_info.header.payloadType = kRedPayloadType; + EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); + + // Change of codec cannot be initiated with a sync packet. + rtp_info.header.payloadType = kIsacPayloadType; + EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); + + // Change of SSRC is not allowed with a sync packet. + rtp_info.header.payloadType = kPcm16WbPayloadType; + ++rtp_info.header.ssrc; + EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); + + --rtp_info.header.ssrc; + EXPECT_EQ(0, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); +} + +// First insert several noise like packets, then sync-packets. Decoding all +// packets should not produce error, statistics should not show any packet loss +// and sync-packets should decode to zero. +// TODO(turajs) we will have a better test if we have a referece NetEq, and +// when Sync packets are inserted in "test" NetEq we insert all-zero payload +// in reference NetEq and compare the output of those two. +TEST_F(NetEqDecodingTest, SyncPacketDecode) { + WebRtcRTPHeader rtp_info; + PopulateRtpInfo(0, 0, &rtp_info); + const int kPayloadBytes = kBlockSize16kHz * sizeof(int16_t); + uint8_t payload[kPayloadBytes]; + int16_t decoded[kBlockSize16kHz]; + int algorithmic_frame_delay = algorithmic_delay_ms_ / 10 + 1; + for (int n = 0; n < kPayloadBytes; ++n) { + payload[n] = (rand() & 0xF0) + 1; // Non-zero random sequence. + } + // Insert some packets which decode to noise. We are not interested in + // actual decoded values. + NetEqOutputType output_type; + int num_channels; + int samples_per_channel; + uint32_t receive_timestamp = 0; + for (int n = 0; n < 100; ++n) { + ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, + receive_timestamp)); + ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded, + &samples_per_channel, &num_channels, + &output_type)); + ASSERT_EQ(kBlockSize16kHz, samples_per_channel); + ASSERT_EQ(1, num_channels); + + rtp_info.header.sequenceNumber++; + rtp_info.header.timestamp += kBlockSize16kHz; + receive_timestamp += kBlockSize16kHz; + } + const int kNumSyncPackets = 10; + + // Make sure sufficient number of sync packets are inserted that we can + // conduct a test. + ASSERT_GT(kNumSyncPackets, algorithmic_frame_delay); + // Insert sync-packets, the decoded sequence should be all-zero. + for (int n = 0; n < kNumSyncPackets; ++n) { + ASSERT_EQ(0, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); + ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded, + &samples_per_channel, &num_channels, + &output_type)); + ASSERT_EQ(kBlockSize16kHz, samples_per_channel); + ASSERT_EQ(1, num_channels); + if (n > algorithmic_frame_delay) { + EXPECT_TRUE(IsAllZero(decoded, samples_per_channel * num_channels)); + } + rtp_info.header.sequenceNumber++; + rtp_info.header.timestamp += kBlockSize16kHz; + receive_timestamp += kBlockSize16kHz; + } + + // We insert regular packets, if sync packet are not correctly buffered then + // network statistics would show some packet loss. + for (int n = 0; n <= algorithmic_frame_delay + 10; ++n) { + ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, + receive_timestamp)); + ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded, + &samples_per_channel, &num_channels, + &output_type)); + if (n >= algorithmic_frame_delay + 1) { + // Expect that this frame contain samples from regular RTP. + EXPECT_TRUE(IsAllNonZero(decoded, samples_per_channel * num_channels)); + } + rtp_info.header.sequenceNumber++; + rtp_info.header.timestamp += kBlockSize16kHz; + receive_timestamp += kBlockSize16kHz; + } + NetEqNetworkStatistics network_stats; + ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats)); + // Expecting a "clean" network. + EXPECT_EQ(0, network_stats.packet_loss_rate); + EXPECT_EQ(0, network_stats.expand_rate); + EXPECT_EQ(0, network_stats.accelerate_rate); + EXPECT_LE(network_stats.preemptive_rate, 150); +} + +// Test if the size of the packet buffer reported correctly when containing +// sync packets. Also, test if network packets override sync packets. That is to +// prefer decoding a network packet to a sync packet, if both have same sequence +// number and timestamp. +TEST_F(NetEqDecodingTest, SyncPacketBufferSizeAndOverridenByNetworkPackets) { + WebRtcRTPHeader rtp_info; + PopulateRtpInfo(0, 0, &rtp_info); + const int kPayloadBytes = kBlockSize16kHz * sizeof(int16_t); + uint8_t payload[kPayloadBytes]; + int16_t decoded[kBlockSize16kHz]; + for (int n = 0; n < kPayloadBytes; ++n) { + payload[n] = (rand() & 0xF0) + 1; // Non-zero random sequence. + } + // Insert some packets which decode to noise. We are not interested in + // actual decoded values. + NetEqOutputType output_type; + int num_channels; + int samples_per_channel; + uint32_t receive_timestamp = 0; + int algorithmic_frame_delay = algorithmic_delay_ms_ / 10 + 1; + for (int n = 0; n < algorithmic_frame_delay; ++n) { + ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, + receive_timestamp)); + ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded, + &samples_per_channel, &num_channels, + &output_type)); + ASSERT_EQ(kBlockSize16kHz, samples_per_channel); + ASSERT_EQ(1, num_channels); + rtp_info.header.sequenceNumber++; + rtp_info.header.timestamp += kBlockSize16kHz; + receive_timestamp += kBlockSize16kHz; + } + const int kNumSyncPackets = 10; + + WebRtcRTPHeader first_sync_packet_rtp_info; + memcpy(&first_sync_packet_rtp_info, &rtp_info, sizeof(rtp_info)); + + // Insert sync-packets, but no decoding. + for (int n = 0; n < kNumSyncPackets; ++n) { + ASSERT_EQ(0, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); + rtp_info.header.sequenceNumber++; + rtp_info.header.timestamp += kBlockSize16kHz; + receive_timestamp += kBlockSize16kHz; + } + NetEqNetworkStatistics network_stats; + ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats)); + EXPECT_EQ(kNumSyncPackets * 10 + algorithmic_delay_ms_, + network_stats.current_buffer_size_ms); + + // Rewind |rtp_info| to that of the first sync packet. + memcpy(&rtp_info, &first_sync_packet_rtp_info, sizeof(rtp_info)); + + // Insert. + for (int n = 0; n < kNumSyncPackets; ++n) { + ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, + receive_timestamp)); + rtp_info.header.sequenceNumber++; + rtp_info.header.timestamp += kBlockSize16kHz; + receive_timestamp += kBlockSize16kHz; + } + + // Decode. + for (int n = 0; n < kNumSyncPackets; ++n) { + ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded, + &samples_per_channel, &num_channels, + &output_type)); + ASSERT_EQ(kBlockSize16kHz, samples_per_channel); + ASSERT_EQ(1, num_channels); + EXPECT_TRUE(IsAllNonZero(decoded, samples_per_channel * num_channels)); + } +} + +void NetEqDecodingTest::WrapTest(uint16_t start_seq_no, + uint32_t start_timestamp, + const std::set& drop_seq_numbers, + bool expect_seq_no_wrap, + bool expect_timestamp_wrap) { + uint16_t seq_no = start_seq_no; + uint32_t timestamp = start_timestamp; + const int kBlocksPerFrame = 3; // Number of 10 ms blocks per frame. + const int kFrameSizeMs = kBlocksPerFrame * kTimeStepMs; + const int kSamples = kBlockSize16kHz * kBlocksPerFrame; + const int kPayloadBytes = kSamples * sizeof(int16_t); + double next_input_time_ms = 0.0; + int16_t decoded[kBlockSize16kHz]; + int num_channels; + int samples_per_channel; + NetEqOutputType output_type; + uint32_t receive_timestamp = 0; + + // Insert speech for 2 seconds. + const int kSpeechDurationMs = 2000; + int packets_inserted = 0; + uint16_t last_seq_no; + uint32_t last_timestamp; + bool timestamp_wrapped = false; + bool seq_no_wrapped = false; + for (double t_ms = 0; t_ms < kSpeechDurationMs; t_ms += 10) { + // Each turn in this for loop is 10 ms. + while (next_input_time_ms <= t_ms) { + // Insert one 30 ms speech frame. + uint8_t payload[kPayloadBytes] = {0}; + WebRtcRTPHeader rtp_info; + PopulateRtpInfo(seq_no, timestamp, &rtp_info); + if (drop_seq_numbers.find(seq_no) == drop_seq_numbers.end()) { + // This sequence number was not in the set to drop. Insert it. + ASSERT_EQ(0, + neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, + receive_timestamp)); + ++packets_inserted; + } + NetEqNetworkStatistics network_stats; + ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats)); + + // Due to internal NetEq logic, preferred buffer-size is about 4 times the + // packet size for first few packets. Therefore we refrain from checking + // the criteria. + if (packets_inserted > 4) { + // Expect preferred and actual buffer size to be no more than 2 frames. + EXPECT_LE(network_stats.preferred_buffer_size_ms, kFrameSizeMs * 2); + EXPECT_LE(network_stats.current_buffer_size_ms, kFrameSizeMs * 2 + + algorithmic_delay_ms_); + } + last_seq_no = seq_no; + last_timestamp = timestamp; + + ++seq_no; + timestamp += kSamples; + receive_timestamp += kSamples; + next_input_time_ms += static_cast(kFrameSizeMs); + + seq_no_wrapped |= seq_no < last_seq_no; + timestamp_wrapped |= timestamp < last_timestamp; + } + // Pull out data once. + ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded, + &samples_per_channel, &num_channels, + &output_type)); + ASSERT_EQ(kBlockSize16kHz, samples_per_channel); + ASSERT_EQ(1, num_channels); + + // Expect delay (in samples) to be less than 2 packets. + EXPECT_LE(timestamp - PlayoutTimestamp(), + static_cast(kSamples * 2)); + } + // Make sure we have actually tested wrap-around. + ASSERT_EQ(expect_seq_no_wrap, seq_no_wrapped); + ASSERT_EQ(expect_timestamp_wrap, timestamp_wrapped); +} + +TEST_F(NetEqDecodingTest, SequenceNumberWrap) { + // Start with a sequence number that will soon wrap. + std::set drop_seq_numbers; // Don't drop any packets. + WrapTest(0xFFFF - 10, 0, drop_seq_numbers, true, false); +} + +TEST_F(NetEqDecodingTest, SequenceNumberWrapAndDrop) { + // Start with a sequence number that will soon wrap. + std::set drop_seq_numbers; + drop_seq_numbers.insert(0xFFFF); + drop_seq_numbers.insert(0x0); + WrapTest(0xFFFF - 10, 0, drop_seq_numbers, true, false); +} + +TEST_F(NetEqDecodingTest, TimestampWrap) { + // Start with a timestamp that will soon wrap. + std::set drop_seq_numbers; + WrapTest(0, 0xFFFFFFFF - 3000, drop_seq_numbers, false, true); +} + +TEST_F(NetEqDecodingTest, TimestampAndSequenceNumberWrap) { + // Start with a timestamp and a sequence number that will wrap at the same + // time. + std::set drop_seq_numbers; + WrapTest(0xFFFF - 10, 0xFFFFFFFF - 5000, drop_seq_numbers, true, true); +} + +void NetEqDecodingTest::DuplicateCng() { + uint16_t seq_no = 0; + uint32_t timestamp = 0; + const int kFrameSizeMs = 10; + const int kSampleRateKhz = 16; + const int kSamples = kFrameSizeMs * kSampleRateKhz; + const int kPayloadBytes = kSamples * 2; + + const int algorithmic_delay_samples = std::max( + algorithmic_delay_ms_ * kSampleRateKhz, 5 * kSampleRateKhz / 8); + // Insert three speech packet. Three are needed to get the frame length + // correct. + int out_len; + int num_channels; + NetEqOutputType type; + uint8_t payload[kPayloadBytes] = {0}; + WebRtcRTPHeader rtp_info; + for (int i = 0; i < 3; ++i) { + PopulateRtpInfo(seq_no, timestamp, &rtp_info); + ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, 0)); + ++seq_no; + timestamp += kSamples; + + // Pull audio once. + ASSERT_EQ(0, + neteq_->GetAudio( + kMaxBlockSize, out_data_, &out_len, &num_channels, &type)); + ASSERT_EQ(kBlockSize16kHz, out_len); + } + // Verify speech output. + EXPECT_EQ(kOutputNormal, type); + + // Insert same CNG packet twice. + const int kCngPeriodMs = 100; + const int kCngPeriodSamples = kCngPeriodMs * kSampleRateKhz; + int payload_len; + PopulateCng(seq_no, timestamp, &rtp_info, payload, &payload_len); + // This is the first time this CNG packet is inserted. + ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, payload_len, 0)); + + // Pull audio once and make sure CNG is played. + ASSERT_EQ(0, + neteq_->GetAudio( + kMaxBlockSize, out_data_, &out_len, &num_channels, &type)); + ASSERT_EQ(kBlockSize16kHz, out_len); + EXPECT_EQ(kOutputCNG, type); + EXPECT_EQ(timestamp - algorithmic_delay_samples, PlayoutTimestamp()); + + // Insert the same CNG packet again. Note that at this point it is old, since + // we have already decoded the first copy of it. + ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, payload_len, 0)); + + // Pull audio until we have played |kCngPeriodMs| of CNG. Start at 10 ms since + // we have already pulled out CNG once. + for (int cng_time_ms = 10; cng_time_ms < kCngPeriodMs; cng_time_ms += 10) { + ASSERT_EQ(0, + neteq_->GetAudio( + kMaxBlockSize, out_data_, &out_len, &num_channels, &type)); + ASSERT_EQ(kBlockSize16kHz, out_len); + EXPECT_EQ(kOutputCNG, type); + EXPECT_EQ(timestamp - algorithmic_delay_samples, + PlayoutTimestamp()); + } + + // Insert speech again. + ++seq_no; + timestamp += kCngPeriodSamples; + PopulateRtpInfo(seq_no, timestamp, &rtp_info); + ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, 0)); + + // Pull audio once and verify that the output is speech again. + ASSERT_EQ(0, + neteq_->GetAudio( + kMaxBlockSize, out_data_, &out_len, &num_channels, &type)); + ASSERT_EQ(kBlockSize16kHz, out_len); + EXPECT_EQ(kOutputNormal, type); + EXPECT_EQ(timestamp + kSamples - algorithmic_delay_samples, + PlayoutTimestamp()); +} + +uint32_t NetEqDecodingTest::PlayoutTimestamp() { + uint32_t playout_timestamp = 0; + EXPECT_TRUE(neteq_->GetPlayoutTimestamp(&playout_timestamp)); + return playout_timestamp; +} + +TEST_F(NetEqDecodingTest, DiscardDuplicateCng) { DuplicateCng(); } +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_unittests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_unittests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_unittests.isolate 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_unittests.isolate 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -# Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. -{ - 'conditions': [ - ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. - 'variables': { - 'isolate_dependency_untracked': [ - '../../../../../data/', - '../../../../../resources/', - ], - }, - }], - ['OS=="linux" or OS=="mac" or OS=="win"', { - 'variables': { - 'command': [ - '../../../../testing/test_env.py', - '<(PRODUCT_DIR)/neteq_unittests<(EXECUTABLE_SUFFIX)', - ], - 'isolate_dependency_touched': [ - '../../../../DEPS', - ], - 'isolate_dependency_tracked': [ - '../../../../resources/audio_coding/neteq_network_stats.dat', - '../../../../resources/audio_coding/neteq_rtcp_stats.dat', - '../../../../resources/audio_coding/neteq_universal.rtp', - '../../../../resources/audio_coding/neteq_universal_ref.pcm', - '../../../../resources/audio_coding/testfile32kHz.pcm', - '../../../../testing/test_env.py', - '<(PRODUCT_DIR)/neteq_unittests<(EXECUTABLE_SUFFIX)', - ], - 'isolate_dependency_untracked': [ - '../../../../tools/swarming_client/', - ], - }, - }], - ], -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/normal.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/normal.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/normal.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/normal.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,279 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the function for handling "normal" speech operation. - */ -#include "dsp.h" - -#include "signal_processing_library.h" - -#include "dsp_helpfunctions.h" - -/* Scratch usage: - - Type Name size startpos endpos - int16_t pw16_expanded 125*fs/8000 0 125*fs/8000-1 - - func WebRtcNetEQ_Expand 40+370*fs/8000 125*fs/8000 39+495*fs/8000 - - Total: 40+495*fs/8000 - */ - -#define SCRATCH_PW16_EXPANDED 0 -#if (defined(NETEQ_48KHZ_WIDEBAND)) -#define SCRATCH_NETEQ_EXPAND 756 -#elif (defined(NETEQ_32KHZ_WIDEBAND)) -#define SCRATCH_NETEQ_EXPAND 504 -#elif (defined(NETEQ_WIDEBAND)) -#define SCRATCH_NETEQ_EXPAND 252 -#else /* NB */ -#define SCRATCH_NETEQ_EXPAND 126 -#endif - -/**************************************************************************** - * WebRtcNetEQ_Normal(...) - * - * This function has the possibility to modify data that is played out in Normal - * mode, for example adjust the gain of the signal. The length of the signal - * can not be changed. - * - * Input: - * - inst : NetEq instance, i.e. the user that requests more - * speech/audio data - * - scratchPtr : Pointer to scratch vector - * - decoded : Pointer to vector of new data from decoder - * (Vector contents may be altered by the function) - * - len : Number of input samples - * - * Output: - * - inst : Updated user information - * - outData : Pointer to a memory space where the output data - * should be stored - * - pw16_len : Pointer to variable where the number of samples - * produced will be written - * - * Return value : >=0 - Number of samples written to outData - * -1 - Error - */ - -int WebRtcNetEQ_Normal(DSPInst_t *inst, -#ifdef SCRATCH - int16_t *pw16_scratchPtr, -#endif - int16_t *pw16_decoded, int16_t len, - int16_t *pw16_outData, int16_t *pw16_len) -{ - - int i; - int16_t fs_mult; - int16_t fs_shift; - int32_t w32_En_speech; - int16_t enLen; - int16_t w16_muted; - int16_t w16_inc, w16_frac; - int16_t w16_tmp; - int32_t w32_tmp; - - /* Sanity check */ - if (len < 0) - { - /* Cannot have negative length of input vector */ - return (-1); - } - - if (len == 0) - { - /* Still got some data to play => continue with the same mode */ - *pw16_len = len; - return (len); - } - - fs_mult = WebRtcSpl_DivW32W16ResW16(inst->fs, 8000); - fs_shift = 30 - WebRtcSpl_NormW32(fs_mult); /* Note that this is not "exact" for 48kHz */ - - /* - * Check if last RecOut call resulted in an Expand or a FadeToBGN. If so, we have to take - * care of some cross-fading and unmuting. - */ - if (inst->w16_mode == MODE_EXPAND || inst->w16_mode == MODE_FADE_TO_BGN) - { - - /* Define memory where temporary result from Expand algorithm can be stored. */ -#ifdef SCRATCH - int16_t *pw16_expanded = pw16_scratchPtr + SCRATCH_PW16_EXPANDED; -#else - int16_t pw16_expanded[FSMULT * 125]; -#endif - int16_t expandedLen = 0; - int16_t w16_decodedMax; - - /* Find largest value in new data */ - w16_decodedMax = WebRtcSpl_MaxAbsValueW16(pw16_decoded, (int16_t) len); - - /* Generate interpolation data using Expand */ - /* First, set Expand parameters to appropriate values. */ - inst->ExpandInst.w16_lagsPosition = 0; - inst->ExpandInst.w16_lagsDirection = 0; - inst->ExpandInst.w16_stopMuting = 1; /* Do not mute signal any more */ - - /* Call Expand */ - WebRtcNetEQ_Expand(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_EXPAND, -#endif - pw16_expanded, &expandedLen, (int16_t) (inst->w16_mode == MODE_FADE_TO_BGN)); - - inst->ExpandInst.w16_stopMuting = 0; /* Restore value */ - inst->ExpandInst.w16_consecExp = 0; /* Last was not Expand any more */ - - /* Adjust muting factor (main muting factor times expand muting factor) */ - if (inst->w16_mode == MODE_FADE_TO_BGN) - { - /* If last mode was FadeToBGN, the mute factor should be zero. */ - inst->w16_muteFactor = 0; - } - else - { - /* w16_muteFactor * w16_expandMuteFactor */ - inst->w16_muteFactor - = (int16_t) WEBRTC_SPL_MUL_16_16_RSFT(inst->w16_muteFactor, - inst->ExpandInst.w16_expandMuteFactor, 14); - } - - /* Adjust muting factor if needed (to BGN level) */ - enLen = WEBRTC_SPL_MIN(fs_mult<<6, len); /* min( fs_mult * 64, len ) */ - w16_tmp = 6 + fs_shift - WebRtcSpl_NormW32( - WEBRTC_SPL_MUL_16_16(w16_decodedMax, w16_decodedMax)); - w16_tmp = WEBRTC_SPL_MAX(w16_tmp, 0); - w32_En_speech = WebRtcNetEQ_DotW16W16(pw16_decoded, pw16_decoded, enLen, w16_tmp); - w32_En_speech = WebRtcSpl_DivW32W16(w32_En_speech, (int16_t) (enLen >> w16_tmp)); - - if ((w32_En_speech != 0) && (w32_En_speech > inst->BGNInst.w32_energy)) - { - /* Normalize new frame energy to 15 bits */ - w16_tmp = WebRtcSpl_NormW32(w32_En_speech) - 16; - /* we want inst->BGNInst.energy/En_speech in Q14 */ - w32_tmp = WEBRTC_SPL_SHIFT_W32(inst->BGNInst.w32_energy, (w16_tmp+14)); - w16_tmp = (int16_t) WEBRTC_SPL_SHIFT_W32(w32_En_speech, w16_tmp); - w16_tmp = (int16_t) WebRtcSpl_DivW32W16(w32_tmp, w16_tmp); - w16_muted = (int16_t) WebRtcSpl_SqrtFloor( - WEBRTC_SPL_LSHIFT_W32((int32_t) w16_tmp, - 14)); /* w16_muted in Q14 (sqrt(Q28)) */ - } - else - { - w16_muted = 16384; /* 1.0 in Q14 */ - } - if (w16_muted > inst->w16_muteFactor) - { - inst->w16_muteFactor = WEBRTC_SPL_MIN(w16_muted, 16384); - } - - /* If muted increase by 0.64 for every 20 ms (NB/WB 0.0040/0.0020 in Q14) */ - w16_inc = WebRtcSpl_DivW32W16ResW16(64, fs_mult); - for (i = 0; i < len; i++) - { - /* scale with mute factor */ - w32_tmp = WEBRTC_SPL_MUL_16_16(pw16_decoded[i], inst->w16_muteFactor); - /* shift 14 with proper rounding */ - pw16_decoded[i] = (int16_t) WEBRTC_SPL_RSHIFT_W32((w32_tmp + 8192), 14); - /* increase mute_factor towards 16384 */ - inst->w16_muteFactor = WEBRTC_SPL_MIN(16384, (inst->w16_muteFactor+w16_inc)); - } - - /* - * Interpolate the expanded data into the new vector - * (NB/WB/SWB32/SWB40 8/16/32/32 samples) - */ - fs_shift = WEBRTC_SPL_MIN(3, fs_shift); /* Set to 3 for >32kHz */ - w16_inc = 4 >> fs_shift; - w16_frac = w16_inc; - for (i = 0; i < 8 * fs_mult; i++) - { - pw16_decoded[i] = (int16_t) WEBRTC_SPL_RSHIFT_W32( - (WEBRTC_SPL_MUL_16_16(w16_frac, pw16_decoded[i]) + - WEBRTC_SPL_MUL_16_16((32 - w16_frac), pw16_expanded[i]) + 8), - 5); - w16_frac += w16_inc; - } - -#ifdef NETEQ_CNG_CODEC - } - else if (inst->w16_mode==MODE_RFC3389CNG) - { /* previous was RFC 3389 CNG...*/ - int16_t pw16_CngInterp[32]; - /* Reset mute factor and start up fresh */ - inst->w16_muteFactor = 16384; - if (inst->CNG_Codec_inst != NULL) - { - /* Generate long enough for 32kHz */ - if(WebRtcCng_Generate(inst->CNG_Codec_inst,pw16_CngInterp, 32, 0)<0) - { - /* error returned; set return vector to all zeros */ - WebRtcSpl_MemSetW16(pw16_CngInterp, 0, 32); - } - } - else - { - /* - * If no CNG instance is defined, just copy from the decoded data. - * (This will result in interpolating the decoded with itself.) - */ - WEBRTC_SPL_MEMCPY_W16(pw16_CngInterp, pw16_decoded, fs_mult * 8); - } - /* - * Interpolate the CNG into the new vector - * (NB/WB/SWB32kHz/SWB48kHz 8/16/32/32 samples) - */ - fs_shift = WEBRTC_SPL_MIN(3, fs_shift); /* Set to 3 for >32kHz */ - w16_inc = 4>>fs_shift; - w16_frac = w16_inc; - for (i = 0; i < 8 * fs_mult; i++) - { - pw16_decoded[i] = (int16_t) WEBRTC_SPL_RSHIFT_W32( - (WEBRTC_SPL_MUL_16_16(w16_frac, pw16_decoded[i]) + - WEBRTC_SPL_MUL_16_16((32-w16_frac), pw16_CngInterp[i]) + 8), - 5); - w16_frac += w16_inc; - } -#endif - - } - else if (inst->w16_muteFactor < 16384) - { - /* - * Previous was neither of Expand, FadeToBGN or RFC3389_CNG, but we are still - * ramping up from previous muting. - * If muted increase by 0.64 for every 20 ms (NB/WB 0.0040/0.0020 in Q14) - */ - w16_inc = WebRtcSpl_DivW32W16ResW16(64, fs_mult); - for (i = 0; i < len; i++) - { - /* scale with mute factor */ - w32_tmp = WEBRTC_SPL_MUL_16_16(pw16_decoded[i], inst->w16_muteFactor); - /* shift 14 with proper rounding */ - pw16_decoded[i] = (int16_t) WEBRTC_SPL_RSHIFT_W32((w32_tmp + 8192), 14); - /* increase mute_factor towards 16384 */ - inst->w16_muteFactor = WEBRTC_SPL_MIN(16384, (inst->w16_muteFactor+w16_inc)); - } - } - - /* Copy data to other buffer */WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, len); - - inst->w16_mode = MODE_NORMAL; - *pw16_len = len; - return (len); - -} - -#undef SCRATCH_PW16_EXPANDED -#undef SCRATCH_NETEQ_EXPAND - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/normal.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/normal.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/normal.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/normal.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/normal.h" + +#include // memset, memcpy + +#include // min + +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" +#include "webrtc/modules/audio_coding/neteq/background_noise.h" +#include "webrtc/modules/audio_coding/neteq/decoder_database.h" +#include "webrtc/modules/audio_coding/neteq/expand.h" +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" + +namespace webrtc { + +int Normal::Process(const int16_t* input, + size_t length, + Modes last_mode, + int16_t* external_mute_factor_array, + AudioMultiVector* output) { + if (length == 0) { + // Nothing to process. + output->Clear(); + return static_cast(length); + } + + assert(output->Empty()); + // Output should be empty at this point. + if (length % output->Channels() != 0) { + // The length does not match the number of channels. + output->Clear(); + return 0; + } + output->PushBackInterleaved(input, length); + int16_t* signal = &(*output)[0][0]; + + const unsigned fs_mult = fs_hz_ / 8000; + assert(fs_mult > 0); + // fs_shift = log2(fs_mult), rounded down. + // Note that |fs_shift| is not "exact" for 48 kHz. + // TODO(hlundin): Investigate this further. + const int fs_shift = 30 - WebRtcSpl_NormW32(fs_mult); + + // Check if last RecOut call resulted in an Expand. If so, we have to take + // care of some cross-fading and unmuting. + if (last_mode == kModeExpand) { + // Generate interpolation data using Expand. + // First, set Expand parameters to appropriate values. + expand_->SetParametersForNormalAfterExpand(); + + // Call Expand. + AudioMultiVector expanded(output->Channels()); + expand_->Process(&expanded); + expand_->Reset(); + + for (size_t channel_ix = 0; channel_ix < output->Channels(); ++channel_ix) { + // Adjust muting factor (main muting factor times expand muting factor). + external_mute_factor_array[channel_ix] = static_cast( + WEBRTC_SPL_MUL_16_16_RSFT(external_mute_factor_array[channel_ix], + expand_->MuteFactor(channel_ix), 14)); + + int16_t* signal = &(*output)[channel_ix][0]; + size_t length_per_channel = length / output->Channels(); + // Find largest absolute value in new data. + int16_t decoded_max = WebRtcSpl_MaxAbsValueW16( + signal, static_cast(length_per_channel)); + // Adjust muting factor if needed (to BGN level). + int energy_length = std::min(static_cast(fs_mult * 64), + static_cast(length_per_channel)); + int scaling = 6 + fs_shift + - WebRtcSpl_NormW32(decoded_max * decoded_max); + scaling = std::max(scaling, 0); // |scaling| should always be >= 0. + int32_t energy = WebRtcSpl_DotProductWithScale(signal, signal, + energy_length, scaling); + if ((energy_length >> scaling) > 0) { + energy = energy / (energy_length >> scaling); + } else { + energy = 0; + } + + int mute_factor; + if ((energy != 0) && + (energy > background_noise_.Energy(channel_ix))) { + // Normalize new frame energy to 15 bits. + scaling = WebRtcSpl_NormW32(energy) - 16; + // We want background_noise_.energy() / energy in Q14. + int32_t bgn_energy = + background_noise_.Energy(channel_ix) << (scaling+14); + int16_t energy_scaled = energy << scaling; + int16_t ratio = WebRtcSpl_DivW32W16(bgn_energy, energy_scaled); + mute_factor = WebRtcSpl_SqrtFloor(static_cast(ratio) << 14); + } else { + mute_factor = 16384; // 1.0 in Q14. + } + if (mute_factor > external_mute_factor_array[channel_ix]) { + external_mute_factor_array[channel_ix] = std::min(mute_factor, 16384); + } + + // If muted increase by 0.64 for every 20 ms (NB/WB 0.0040/0.0020 in Q14). + int16_t increment = 64 / fs_mult; + for (size_t i = 0; i < length_per_channel; i++) { + // Scale with mute factor. + assert(channel_ix < output->Channels()); + assert(i < output->Size()); + int32_t scaled_signal = (*output)[channel_ix][i] * + external_mute_factor_array[channel_ix]; + // Shift 14 with proper rounding. + (*output)[channel_ix][i] = (scaled_signal + 8192) >> 14; + // Increase mute_factor towards 16384. + external_mute_factor_array[channel_ix] = + std::min(external_mute_factor_array[channel_ix] + increment, 16384); + } + + // Interpolate the expanded data into the new vector. + // (NB/WB/SWB32/SWB48 8/16/32/48 samples.) + assert(fs_shift < 3); // Will always be 0, 1, or, 2. + increment = 4 >> fs_shift; + int fraction = increment; + for (size_t i = 0; i < 8 * fs_mult; i++) { + // TODO(hlundin): Add 16 instead of 8 for correct rounding. Keeping 8 + // now for legacy bit-exactness. + assert(channel_ix < output->Channels()); + assert(i < output->Size()); + (*output)[channel_ix][i] = + (fraction * (*output)[channel_ix][i] + + (32 - fraction) * expanded[channel_ix][i] + 8) >> 5; + fraction += increment; + } + } + } else if (last_mode == kModeRfc3389Cng) { + assert(output->Channels() == 1); // Not adapted for multi-channel yet. + static const int kCngLength = 32; + int16_t cng_output[kCngLength]; + // Reset mute factor and start up fresh. + external_mute_factor_array[0] = 16384; + AudioDecoder* cng_decoder = decoder_database_->GetActiveCngDecoder(); + + if (cng_decoder) { + // Generate long enough for 32kHz. + if (WebRtcCng_Generate(cng_decoder->CngDecoderInstance(), cng_output, + kCngLength, 0) < 0) { + // Error returned; set return vector to all zeros. + memset(cng_output, 0, sizeof(cng_output)); + } + } else { + // If no CNG instance is defined, just copy from the decoded data. + // (This will result in interpolating the decoded with itself.) + memcpy(cng_output, signal, fs_mult * 8 * sizeof(int16_t)); + } + // Interpolate the CNG into the new vector. + // (NB/WB/SWB32/SWB48 8/16/32/48 samples.) + assert(fs_shift < 3); // Will always be 0, 1, or, 2. + int16_t increment = 4 >> fs_shift; + int16_t fraction = increment; + for (size_t i = 0; i < 8 * fs_mult; i++) { + // TODO(hlundin): Add 16 instead of 8 for correct rounding. Keeping 8 now + // for legacy bit-exactness. + signal[i] = + (fraction * signal[i] + (32 - fraction) * cng_output[i] + 8) >> 5; + fraction += increment; + } + } else if (external_mute_factor_array[0] < 16384) { + // Previous was neither of Expand, FadeToBGN or RFC3389_CNG, but we are + // still ramping up from previous muting. + // If muted increase by 0.64 for every 20 ms (NB/WB 0.0040/0.0020 in Q14). + int16_t increment = 64 / fs_mult; + size_t length_per_channel = length / output->Channels(); + for (size_t i = 0; i < length_per_channel; i++) { + for (size_t channel_ix = 0; channel_ix < output->Channels(); + ++channel_ix) { + // Scale with mute factor. + assert(channel_ix < output->Channels()); + assert(i < output->Size()); + int32_t scaled_signal = (*output)[channel_ix][i] * + external_mute_factor_array[channel_ix]; + // Shift 14 with proper rounding. + (*output)[channel_ix][i] = (scaled_signal + 8192) >> 14; + // Increase mute_factor towards 16384. + external_mute_factor_array[channel_ix] = + std::min(16384, external_mute_factor_array[channel_ix] + increment); + } + } + } + + return static_cast(length); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/normal.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/normal.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/normal.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/normal.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_NORMAL_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_NORMAL_H_ + +#include // Access to size_t. + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" +#include "webrtc/modules/audio_coding/neteq/defines.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Forward declarations. +class BackgroundNoise; +class DecoderDatabase; +class Expand; + +// This class provides the "Normal" DSP operation, that is performed when +// there is no data loss, no need to stretch the timing of the signal, and +// no other "special circumstances" are at hand. +class Normal { + public: + Normal(int fs_hz, DecoderDatabase* decoder_database, + const BackgroundNoise& background_noise, + Expand* expand) + : fs_hz_(fs_hz), + decoder_database_(decoder_database), + background_noise_(background_noise), + expand_(expand) { + } + + virtual ~Normal() {} + + // Performs the "Normal" operation. The decoder data is supplied in |input|, + // having |length| samples in total for all channels (interleaved). The + // result is written to |output|. The number of channels allocated in + // |output| defines the number of channels that will be used when + // de-interleaving |input|. |last_mode| contains the mode used in the previous + // GetAudio call (i.e., not the current one), and |external_mute_factor| is + // a pointer to the mute factor in the NetEqImpl class. + int Process(const int16_t* input, size_t length, + Modes last_mode, + int16_t* external_mute_factor_array, + AudioMultiVector* output); + + private: + int fs_hz_; + DecoderDatabase* decoder_database_; + const BackgroundNoise& background_noise_; + Expand* expand_; + + DISALLOW_COPY_AND_ASSIGN(Normal); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_NORMAL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/normal_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/normal_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/normal_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/normal_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for Normal class. + +#include "webrtc/modules/audio_coding/neteq/normal.h" + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" +#include "webrtc/modules/audio_coding/neteq/background_noise.h" +#include "webrtc/modules/audio_coding/neteq/expand.h" +#include "webrtc/modules/audio_coding/neteq/mock/mock_decoder_database.h" +#include "webrtc/modules/audio_coding/neteq/mock/mock_expand.h" +#include "webrtc/modules/audio_coding/neteq/random_vector.h" +#include "webrtc/modules/audio_coding/neteq/sync_buffer.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +using ::testing::_; + +namespace webrtc { + +TEST(Normal, CreateAndDestroy) { + MockDecoderDatabase db; + int fs = 8000; + size_t channels = 1; + BackgroundNoise bgn(channels); + SyncBuffer sync_buffer(1, 1000); + RandomVector random_vector; + Expand expand(&bgn, &sync_buffer, &random_vector, fs, channels); + Normal normal(fs, &db, bgn, &expand); + EXPECT_CALL(db, Die()); // Called when |db| goes out of scope. +} + +TEST(Normal, AvoidDivideByZero) { + WebRtcSpl_Init(); + MockDecoderDatabase db; + int fs = 8000; + size_t channels = 1; + BackgroundNoise bgn(channels); + SyncBuffer sync_buffer(1, 1000); + RandomVector random_vector; + MockExpand expand(&bgn, &sync_buffer, &random_vector, fs, channels); + Normal normal(fs, &db, bgn, &expand); + + int16_t input[1000] = {0}; + scoped_ptr mute_factor_array(new int16_t[channels]); + for (size_t i = 0; i < channels; ++i) { + mute_factor_array[i] = 16384; + } + AudioMultiVector output(channels); + + // Zero input length. + EXPECT_EQ( + 0, + normal.Process(input, 0, kModeExpand, mute_factor_array.get(), &output)); + EXPECT_EQ(0u, output.Size()); + + // Try to make energy_length >> scaling = 0; + EXPECT_CALL(expand, SetParametersForNormalAfterExpand()); + EXPECT_CALL(expand, Process(_)); + EXPECT_CALL(expand, Reset()); + // If input_size_samples < 64, then energy_length in Normal::Process() will + // be equal to input_size_samples. Since the input is all zeros, decoded_max + // will be zero, and scaling will be >= 6. Thus, energy_length >> scaling = 0, + // and using this as a denominator would lead to problems. + int input_size_samples = 63; + EXPECT_EQ(input_size_samples, + normal.Process(input, + input_size_samples, + kModeExpand, + mute_factor_array.get(), + &output)); + + EXPECT_CALL(db, Die()); // Called when |db| goes out of scope. + EXPECT_CALL(expand, Die()); // Called when |expand| goes out of scope. +} + +TEST(Normal, InputLengthAndChannelsDoNotMatch) { + WebRtcSpl_Init(); + MockDecoderDatabase db; + int fs = 8000; + size_t channels = 2; + BackgroundNoise bgn(channels); + SyncBuffer sync_buffer(channels, 1000); + RandomVector random_vector; + MockExpand expand(&bgn, &sync_buffer, &random_vector, fs, channels); + Normal normal(fs, &db, bgn, &expand); + + int16_t input[1000] = {0}; + scoped_ptr mute_factor_array(new int16_t[channels]); + for (size_t i = 0; i < channels; ++i) { + mute_factor_array[i] = 16384; + } + AudioMultiVector output(channels); + + // Let the number of samples be one sample less than 80 samples per channel. + size_t input_len = 80 * channels - 1; + EXPECT_EQ( + 0, + normal.Process( + input, input_len, kModeExpand, mute_factor_array.get(), &output)); + EXPECT_EQ(0u, output.Size()); + + EXPECT_CALL(db, Die()); // Called when |db| goes out of scope. + EXPECT_CALL(expand, Die()); // Called when |expand| goes out of scope. +} + +// TODO(hlundin): Write more tests. + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -1,3 +1,11 @@ henrik.lundin@webrtc.org tina.legrand@webrtc.org turaj@webrtc.org +minyue@webrtc.org + +per-file *.isolate=kjellander@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet_buffer.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet_buffer.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet_buffer.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet_buffer.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,856 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Implementation of the actual packet buffer data structure. - */ - -#include -#include "packet_buffer.h" - -#include /* to define NULL */ - -#include "signal_processing_library.h" - -#include "mcu_dsp_common.h" - -#include "neteq_error_codes.h" - -#ifdef NETEQ_DELAY_LOGGING -/* special code for offline delay logging */ -#include "delay_logging.h" -#include - -extern FILE *delay_fid2; /* file pointer to delay log file */ -extern uint32_t tot_received_packets; -#endif /* NETEQ_DELAY_LOGGING */ - - -int WebRtcNetEQ_PacketBufferInit(PacketBuf_t *bufferInst, int maxNoOfPackets, - int16_t *pw16_memory, int memorySize) -{ - int i; - int pos = 0; - - /* Sanity check */ - if ((memorySize < PBUFFER_MIN_MEMORY_SIZE) || (pw16_memory == NULL) - || (maxNoOfPackets < 2) || (maxNoOfPackets > 600)) - { - /* Invalid parameters */ - return (PBUFFER_INIT_ERROR); - } - - /* Clear the buffer instance */ - WebRtcSpl_MemSetW16((int16_t*) bufferInst, 0, - sizeof(PacketBuf_t) / sizeof(int16_t)); - - /* Clear the buffer memory */ - WebRtcSpl_MemSetW16((int16_t*) pw16_memory, 0, memorySize); - - /* Set maximum number of packets */ - bufferInst->maxInsertPositions = maxNoOfPackets; - - /* Initialize array pointers */ - /* After each pointer has been set, the index pos is advanced to point immediately - * after the the recently allocated vector. Note that one step for the pos index - * corresponds to a int16_t. - */ - - bufferInst->timeStamp = (uint32_t*) &pw16_memory[pos]; - pos += maxNoOfPackets << 1; /* advance maxNoOfPackets * uint32_t */ - - bufferInst->payloadLocation = (int16_t**) &pw16_memory[pos]; - pos += maxNoOfPackets * (sizeof(int16_t*) / sizeof(int16_t)); /* advance */ - - bufferInst->seqNumber = (uint16_t*) &pw16_memory[pos]; - pos += maxNoOfPackets; /* advance maxNoOfPackets * uint16_t */ - - bufferInst->payloadType = &pw16_memory[pos]; - pos += maxNoOfPackets; /* advance maxNoOfPackets * int16_t */ - - bufferInst->payloadLengthBytes = &pw16_memory[pos]; - pos += maxNoOfPackets; /* advance maxNoOfPackets * int16_t */ - - bufferInst->rcuPlCntr = &pw16_memory[pos]; - pos += maxNoOfPackets; /* advance maxNoOfPackets * int16_t */ - - bufferInst->waitingTime = (int*) (&pw16_memory[pos]); - /* Advance maxNoOfPackets * sizeof(waitingTime element). */ - pos += maxNoOfPackets * - sizeof(*bufferInst->waitingTime) / sizeof(*pw16_memory); - - /* The payload memory starts after the slot arrays */ - bufferInst->startPayloadMemory = &pw16_memory[pos]; - bufferInst->currentMemoryPos = bufferInst->startPayloadMemory; - bufferInst->memorySizeW16 = (memorySize - pos); /* Remaining memory */ - - /* Initialize each payload slot as empty with infinite delay */ - for (i = 0; i < bufferInst->maxInsertPositions; i++) - { - bufferInst->payloadType[i] = -1; - } - - /* Reset buffer parameters */ - bufferInst->numPacketsInBuffer = 0; - bufferInst->packSizeSamples = 0; - bufferInst->insertPosition = 0; - - /* Reset buffer statistics */ - bufferInst->discardedPackets = 0; - - return (0); -} - - -int WebRtcNetEQ_PacketBufferFlush(PacketBuf_t *bufferInst) -{ - int i; - - /* Sanity check */ - if (bufferInst->startPayloadMemory == NULL) - { - /* Packet buffer has not been initialized */ - /* Don't do the flushing operation, since we do not - know the state of the struct variables */ - return (0); - } - - /* Set all payload lengths to zero */ - WebRtcSpl_MemSetW16(bufferInst->payloadLengthBytes, 0, bufferInst->maxInsertPositions); - - /* Reset buffer variables */ - bufferInst->numPacketsInBuffer = 0; - bufferInst->currentMemoryPos = bufferInst->startPayloadMemory; - bufferInst->insertPosition = 0; - - /* Clear all slots, starting with the last one */ - for (i = (bufferInst->maxInsertPositions - 1); i >= 0; i--) - { - bufferInst->payloadType[i] = -1; - bufferInst->timeStamp[i] = 0; - bufferInst->seqNumber[i] = 0; - } - - return (0); -} - - -int WebRtcNetEQ_PacketBufferInsert(PacketBuf_t *bufferInst, const RTPPacket_t *RTPpacket, - int16_t *flushed, int av_sync) -{ - int nextPos; - int i; - -#ifdef NETEQ_DELAY_LOGGING - /* special code for offline delay logging */ - int temp_var; -#endif /* NETEQ_DELAY_LOGGING */ - - /* Initialize to "no flush" */ - *flushed = 0; - - /* Sanity check */ - if (bufferInst->startPayloadMemory == NULL) - { - /* packet buffer has not been initialized */ - return (-1); - } - - /* Sanity check for payload length - (payloadLen in bytes and memory size in int16_t) */ - if ((RTPpacket->payloadLen > (bufferInst->memorySizeW16 << 1)) || (RTPpacket->payloadLen - <= 0)) - { - /* faulty or too long payload length */ - return (-1); - } - - /* If we are in AV-sync mode, there is a risk that we have inserted a sync - * packet but now received the real version of it. Or because of some timing - * we might be overwriting a true payload with sync (I'm not sure why this - * should happen in regular case, but in some FEC enabled case happens). - * Go through packets and delete the sync version of the packet in hand. Or - * if this is sync packet and the regular version of it exists in the buffer - * refrain from inserting. - * - * TODO(turajs): Could we get this for free if we had set the RCU-counter of - * the sync packet to a number larger than 2? - */ - if (av_sync) { - for (i = 0; i < bufferInst->maxInsertPositions; ++i) { - /* Check if sequence numbers match and the payload actually exists. */ - if (bufferInst->seqNumber[i] == RTPpacket->seqNumber && - bufferInst->payloadLengthBytes[i] > 0) { - if (WebRtcNetEQ_IsSyncPayload(RTPpacket->payload, - RTPpacket->payloadLen)) { - return 0; - } - - if (WebRtcNetEQ_IsSyncPayload(bufferInst->payloadLocation[i], - bufferInst->payloadLengthBytes[i])) { - /* Clear the position in the buffer. */ - bufferInst->payloadType[i] = -1; - bufferInst->payloadLengthBytes[i] = 0; - - /* Reduce packet counter by one. */ - bufferInst->numPacketsInBuffer--; - /* TODO(turajs) if this is the latest packet better we rewind - * insertPosition and related variables. */ - break; /* There should be only one match. */ - } - } - } - } - - /* Find a position in the buffer for this packet */ - if (bufferInst->numPacketsInBuffer != 0) - { - /* Get the next slot */ - bufferInst->insertPosition++; - if (bufferInst->insertPosition >= bufferInst->maxInsertPositions) - { - /* "Wrap around" and start from the beginning */ - bufferInst->insertPosition = 0; - } - - /* Check if there is enough space for the new packet */ - if (bufferInst->currentMemoryPos + ((RTPpacket->payloadLen + 1) >> 1) - >= &bufferInst->startPayloadMemory[bufferInst->memorySizeW16]) - { - int16_t *tempMemAddress; - - /* - * Payload does not fit at the end of the memory, put it in the beginning - * instead - */ - bufferInst->currentMemoryPos = bufferInst->startPayloadMemory; - - /* - * Now, we must search for the next non-empty payload, - * finding the one with the lowest start address for the payload - */ - tempMemAddress = &bufferInst->startPayloadMemory[bufferInst->memorySizeW16]; - nextPos = -1; - - /* Loop through all slots again */ - for (i = 0; i < bufferInst->maxInsertPositions; i++) - { - /* Look for the non-empty slot with the lowest - payload location address */ - if (bufferInst->payloadLengthBytes[i] != 0 && bufferInst->payloadLocation[i] - < tempMemAddress) - { - tempMemAddress = bufferInst->payloadLocation[i]; - nextPos = i; - } - } - - /* Check that we did find a previous payload */ - if (nextPos == -1) - { - /* The buffer is corrupt => flush and return error */ - WebRtcNetEQ_PacketBufferFlush(bufferInst); - *flushed = 1; - return (-1); - } - } - else - { - /* Payload fits at the end of memory. */ - - /* Find the next non-empty slot. */ - nextPos = bufferInst->insertPosition + 1; - - /* Increase nextPos until a non-empty slot is found or end of array is encountered*/ - while ((bufferInst->payloadLengthBytes[nextPos] == 0) && (nextPos - < bufferInst->maxInsertPositions)) - { - nextPos++; - } - - if (nextPos == bufferInst->maxInsertPositions) - { - /* - * Reached the end of the array, so there must be a packet in the first - * position instead - */ - nextPos = 0; - - /* Increase nextPos until a non-empty slot is found */ - while (bufferInst->payloadLengthBytes[nextPos] == 0) - { - nextPos++; - } - } - } /* end if-else */ - - /* - * Check if the new payload will extend into a payload later in memory. - * If so, the buffer is full. - */ - if ((bufferInst->currentMemoryPos <= bufferInst->payloadLocation[nextPos]) - && ((&bufferInst->currentMemoryPos[(RTPpacket->payloadLen + 1) >> 1]) - > bufferInst->payloadLocation[nextPos])) - { - /* Buffer is full, so the buffer must be flushed */ - WebRtcNetEQ_PacketBufferFlush(bufferInst); - *flushed = 1; - } - - if (bufferInst->payloadLengthBytes[bufferInst->insertPosition] != 0) - { - /* All positions are already taken and entire buffer should be flushed */ - WebRtcNetEQ_PacketBufferFlush(bufferInst); - *flushed = 1; - } - - } - else - { - /* Buffer is empty, just insert the packet at the beginning */ - bufferInst->currentMemoryPos = bufferInst->startPayloadMemory; - bufferInst->insertPosition = 0; - } - - /* Insert packet in the found position */ - if (RTPpacket->starts_byte1 == 0) - { - /* Payload is 16-bit aligned => just copy it */ - - WEBRTC_SPL_MEMCPY_W8(bufferInst->currentMemoryPos, - RTPpacket->payload, RTPpacket->payloadLen); - } - else - { - /* Payload is not 16-bit aligned => align it during copy operation */ - for (i = 0; i < RTPpacket->payloadLen; i++) - { - /* copy the (i+1)-th byte to the i-th byte */ - - WEBRTC_SPL_SET_BYTE(bufferInst->currentMemoryPos, - (WEBRTC_SPL_GET_BYTE(RTPpacket->payload, (i + 1))), i); - } - } - - /* Copy the packet information */ - bufferInst->payloadLocation[bufferInst->insertPosition] = bufferInst->currentMemoryPos; - bufferInst->payloadLengthBytes[bufferInst->insertPosition] = RTPpacket->payloadLen; - bufferInst->payloadType[bufferInst->insertPosition] = RTPpacket->payloadType; - bufferInst->seqNumber[bufferInst->insertPosition] = RTPpacket->seqNumber; - bufferInst->timeStamp[bufferInst->insertPosition] = RTPpacket->timeStamp; - bufferInst->rcuPlCntr[bufferInst->insertPosition] = RTPpacket->rcuPlCntr; - bufferInst->waitingTime[bufferInst->insertPosition] = 0; - /* Update buffer parameters */ - bufferInst->numPacketsInBuffer++; - bufferInst->currentMemoryPos += (RTPpacket->payloadLen + 1) >> 1; - -#ifdef NETEQ_DELAY_LOGGING - /* special code for offline delay logging */ - if (*flushed) - { - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_FLUSH; - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } - } - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_RECIN; - if ((fwrite(&temp_var, sizeof(int), - 1, delay_fid2) != 1) || - (fwrite(&RTPpacket->timeStamp, sizeof(uint32_t), - 1, delay_fid2) != 1) || - (fwrite(&RTPpacket->seqNumber, sizeof(uint16_t), - 1, delay_fid2) != 1) || - (fwrite(&RTPpacket->payloadType, sizeof(int), - 1, delay_fid2) != 1) || - (fwrite(&RTPpacket->payloadLen, sizeof(int16_t), - 1, delay_fid2) != 1)) { - return -1; - } - tot_received_packets++; -#endif /* NETEQ_DELAY_LOGGING */ - - return (0); -} - - -int WebRtcNetEQ_PacketBufferExtract(PacketBuf_t *bufferInst, RTPPacket_t *RTPpacket, - int bufferPosition, int *waitingTime) -{ - - /* Sanity check */ - if (bufferInst->startPayloadMemory == NULL) - { - /* packet buffer has not been initialized */ - return (PBUFFER_NOT_INITIALIZED); - } - - if (bufferPosition < 0 || bufferPosition >= bufferInst->maxInsertPositions) - { - /* buffer position is outside valid range */ - return (NETEQ_OTHER_ERROR); - } - - /* Check that there is a valid payload in the specified position */ - if (bufferInst->payloadLengthBytes[bufferPosition] <= 0) - { - /* The position does not contain a valid payload */ - RTPpacket->payloadLen = 0; /* Set zero length */ - return (PBUFFER_NONEXISTING_PACKET); /* Return error */ - } - - /* Payload exists => extract payload data */ - - /* Copy the actual data payload to RTP packet struct */ - - WEBRTC_SPL_MEMCPY_W16((int16_t*) RTPpacket->payload, - bufferInst->payloadLocation[bufferPosition], - (bufferInst->payloadLengthBytes[bufferPosition] + 1) >> 1); /*length in int16_t*/ - - /* Copy payload parameters */ - RTPpacket->payloadLen = bufferInst->payloadLengthBytes[bufferPosition]; - RTPpacket->payloadType = bufferInst->payloadType[bufferPosition]; - RTPpacket->seqNumber = bufferInst->seqNumber[bufferPosition]; - RTPpacket->timeStamp = bufferInst->timeStamp[bufferPosition]; - RTPpacket->rcuPlCntr = bufferInst->rcuPlCntr[bufferPosition]; - *waitingTime = bufferInst->waitingTime[bufferPosition]; - RTPpacket->starts_byte1 = 0; /* payload is 16-bit aligned */ - - /* Clear the position in the packet buffer */ - bufferInst->payloadType[bufferPosition] = -1; - bufferInst->payloadLengthBytes[bufferPosition] = 0; - bufferInst->seqNumber[bufferPosition] = 0; - bufferInst->timeStamp[bufferPosition] = 0; - bufferInst->waitingTime[bufferPosition] = 0; - bufferInst->payloadLocation[bufferPosition] = bufferInst->startPayloadMemory; - - /* Reduce packet counter with one */ - bufferInst->numPacketsInBuffer--; - - return (0); -} - -int WebRtcNetEQ_PacketBufferFindLowestTimestamp(PacketBuf_t* buffer_inst, - uint32_t current_time_stamp, - uint32_t* time_stamp, - int* buffer_position, - int erase_old_packets, - int16_t* payload_type) { - int32_t time_stamp_diff = WEBRTC_SPL_WORD32_MAX; /* Smallest diff found. */ - int32_t new_diff; - int i; - int16_t rcu_payload_cntr; - if (buffer_inst->startPayloadMemory == NULL) { - /* Packet buffer has not been initialized. */ - return PBUFFER_NOT_INITIALIZED; - } - - /* Initialize all return values. */ - *time_stamp = 0; - *payload_type = -1; /* Indicates that no packet was found. */ - *buffer_position = -1; /* Indicates that no packet was found. */ - rcu_payload_cntr = WEBRTC_SPL_WORD16_MAX; /* Indicates no packet found. */ - - /* Check if buffer is empty. */ - if (buffer_inst->numPacketsInBuffer <= 0) { - return 0; - } - - /* Loop through all slots in buffer. */ - if (erase_old_packets) { /* If old payloads should be discarded. */ - for (i = 0; i < buffer_inst->maxInsertPositions; ++i) { - /* Calculate difference between this slot and current_time_stamp. */ - new_diff = (int32_t)(buffer_inst->timeStamp[i] - current_time_stamp); - - /* Check if payload should be discarded. */ - if ((new_diff < 0) /* Payload is too old */ - && (new_diff > -30000) /* Account for TS wrap-around. */ - && (buffer_inst->payloadLengthBytes[i] > 0)) { /* Payload exists. */ - /* Throw away old packet. */ - - /* Clear the position in the buffer. */ - buffer_inst->payloadType[i] = -1; - buffer_inst->payloadLengthBytes[i] = 0; - - /* Reduce packet counter by one. */ - buffer_inst->numPacketsInBuffer--; - /* Increase discard counter for in-call statistics. */ - buffer_inst->discardedPackets++; - } else if (((new_diff < time_stamp_diff) - || ((new_diff == time_stamp_diff) - && (buffer_inst->rcuPlCntr[i] < rcu_payload_cntr))) - && (buffer_inst->payloadLengthBytes[i] > 0)) { - /* New diff is smaller than previous diffs or we have a candidate with a - * time stamp as previous candidate but better RCU-counter; - * and the payload exists. - */ - /* Save this position as the best candidate. */ - *buffer_position = i; - time_stamp_diff = new_diff; - *payload_type = buffer_inst->payloadType[i]; - rcu_payload_cntr = buffer_inst->rcuPlCntr[i]; - } - } - } else { - for (i = 0; i < buffer_inst->maxInsertPositions; ++i) { - /* Calculate difference between this slot and current_time_stamp. */ - new_diff = (int32_t)(buffer_inst->timeStamp[i] - current_time_stamp); - - /* Check if this is the oldest packet. */ - if (((new_diff < time_stamp_diff) - || ((new_diff == time_stamp_diff) - && (buffer_inst->rcuPlCntr[i] < rcu_payload_cntr))) - && (buffer_inst->payloadLengthBytes[i] > 0)) { - /* New diff is smaller than previous diffs or we have a candidate with a - * time_stamp as previous candidate but better RCU-counter; - * and the payload exists. - */ - /* Save this position as the best candidate. */ - *buffer_position = i; - time_stamp_diff = new_diff; - *payload_type = buffer_inst->payloadType[i]; - rcu_payload_cntr = buffer_inst->rcuPlCntr[i]; - } - } - } - - /* Check that we did find a real position. */ - if (*buffer_position >= 0) { - /* Get the time_stamp for the best position. */ - *time_stamp = buffer_inst->timeStamp[*buffer_position]; - } - - return 0; -} - -int WebRtcNetEQ_PacketBufferGetPacketSize(const PacketBuf_t* buffer_inst, - int buffer_pos, - const CodecDbInst_t* codec_database, - int codec_pos, int last_duration, - int av_sync) { - if (codec_database->funcDurationEst[codec_pos] == NULL) { - return last_duration; - } - - if (av_sync != 0 && - WebRtcNetEQ_IsSyncPayload(buffer_inst->payloadLocation[buffer_pos], - buffer_inst->payloadLengthBytes[buffer_pos])) { - // In AV-sync and sync payload, report |last_duration| as current duration. - return last_duration; - } - - return (*codec_database->funcDurationEst[codec_pos])( - codec_database->codec_state[codec_pos], - (const uint8_t *)buffer_inst->payloadLocation[buffer_pos], - buffer_inst->payloadLengthBytes[buffer_pos]); -} - -int32_t WebRtcNetEQ_PacketBufferGetSize(const PacketBuf_t* buffer_inst, - const CodecDbInst_t* codec_database, - int av_sync) { - int i, count; - int last_duration; - int last_codec_pos; - int last_payload_type; - int32_t size_samples; - - count = 0; - last_duration = buffer_inst->packSizeSamples; - last_codec_pos = -1; - last_payload_type = -1; - size_samples = 0; - - /* Loop through all slots in the buffer */ - for (i = 0; i < buffer_inst->maxInsertPositions; i++) { - /* Only count the packets with non-zero size */ - if (buffer_inst->payloadLengthBytes[i] != 0) { - int payload_type; - int codec_pos; - /* Figure out the codec database entry for this payload_type. */ - payload_type = buffer_inst->payloadType[i]; - /* Remember the last one, to avoid the database search. */ - if(payload_type == last_payload_type) { - codec_pos = last_codec_pos; - } - else { - codec_pos = WebRtcNetEQ_DbGetCodec(codec_database, - payload_type); - if (codec_pos >= 0) { - codec_pos = codec_database->position[codec_pos]; - } - } - last_codec_pos = codec_pos; - last_payload_type = payload_type; - if (codec_pos >= 0) { - /* - * Right now WebRtcNetEQ_PacketBufferGetPacketSize either always - * returns last_duration or always computes the real duration without - * looking at last_duration. If an implementation really wanted to use - * last_duration to compute a changing duration, we would have to - * iterate through the packets in chronological order by timestamp. - */ - /* Check for error before setting. */ - int temp_last_duration = WebRtcNetEQ_PacketBufferGetPacketSize( - buffer_inst, i, codec_database, codec_pos, - last_duration, av_sync); - if (temp_last_duration >= 0) - last_duration = temp_last_duration; - } - /* Add in the size of this packet. */ - size_samples += last_duration; - count++; - } - } - - /* Sanity check; size cannot be negative */ - if (size_samples < 0) { - size_samples = 0; - } - return size_samples; -} - -void WebRtcNetEQ_IncrementWaitingTimes(PacketBuf_t *buffer_inst) { - int i; - /* Loop through all slots in the buffer. */ - for (i = 0; i < buffer_inst->maxInsertPositions; ++i) { - /* Only increment waiting time for the packets with non-zero size. */ - if (buffer_inst->payloadLengthBytes[i] != 0) { - buffer_inst->waitingTime[i]++; - } - } -} - -int WebRtcNetEQ_GetDefaultCodecSettings(const enum WebRtcNetEQDecoder *codecID, - int noOfCodecs, int *maxBytes, - int *maxSlots, - int* per_slot_overhead_bytes) -{ - int i; - int ok = 0; - int16_t w16_tmp; - int16_t codecBytes; - int16_t codecBuffers; - - /* Initialize return variables to zero */ - *maxBytes = 0; - *maxSlots = 0; - - /* Loop through all codecs supplied to function */ - for (i = 0; i < noOfCodecs; i++) - { - /* Find current codec and set parameters accordingly */ - - if ((codecID[i] == kDecoderPCMu) || (codecID[i] == kDecoderPCMu_2ch)) - { - codecBytes = 1680; /* Up to 210ms @ 64kbps */ - codecBuffers = 30; /* Down to 5ms frames */ - } - else if ((codecID[i] == kDecoderPCMa) || - (codecID[i] == kDecoderPCMa_2ch)) - { - codecBytes = 1680; /* Up to 210ms @ 64kbps */ - codecBuffers = 30; /* Down to 5ms frames */ - } - else if (codecID[i] == kDecoderILBC) - { - codecBytes = 380; /* 200ms @ 15.2kbps (20ms frames) */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderISAC) - { - codecBytes = 960; /* 240ms @ 32kbps (60ms frames) */ - codecBuffers = 8; - } - else if ((codecID[i] == kDecoderISACswb) || - (codecID[i] == kDecoderISACfb)) - { - codecBytes = 1560; /* 240ms @ 52kbps (30ms frames) */ - codecBuffers = 8; - } - else if (codecID[i] == kDecoderOpus) - { - codecBytes = 15300; /* 240ms @ 510kbps (60ms frames) */ - codecBuffers = 30; /* Replicating the value for PCMu/a */ - } - else if (codecID[i] == kDecoderOpus) - { - codecBytes = 15300; /* 240ms @ 510kbps (60ms frames) */ - codecBuffers = 30; /* ?? Codec supports down to 2.5-60 ms frames */ - } - else if ((codecID[i] == kDecoderPCM16B) || - (codecID[i] == kDecoderPCM16B_2ch)) - { - codecBytes = 3360; /* 210ms */ - codecBuffers = 15; - } - else if ((codecID[i] == kDecoderPCM16Bwb) || - (codecID[i] == kDecoderPCM16Bwb_2ch)) - { - codecBytes = 6720; /* 210ms */ - codecBuffers = 15; - } - else if ((codecID[i] == kDecoderPCM16Bswb32kHz) || - (codecID[i] == kDecoderPCM16Bswb32kHz_2ch)) - { - codecBytes = 13440; /* 210ms */ - codecBuffers = 15; - } - else if (codecID[i] == kDecoderPCM16Bswb48kHz) - { - codecBytes = 20160; /* 210ms */ - codecBuffers = 15; - } - else if ((codecID[i] == kDecoderG722) || - (codecID[i] == kDecoderG722_2ch)) - { - codecBytes = 1680; /* 210ms @ 64kbps */ - codecBuffers = 15; - } - else if (codecID[i] == kDecoderRED) - { - codecBytes = 0; /* Should not be max... */ - codecBuffers = 0; - } - else if (codecID[i] == kDecoderAVT) - { - codecBytes = 0; /* Should not be max... */ - codecBuffers = 0; - } - else if (codecID[i] == kDecoderCNG) - { - codecBytes = 0; /* Should not be max... */ - codecBuffers = 0; - } - else if (codecID[i] == kDecoderG729) - { - codecBytes = 210; /* 210ms @ 8kbps */ - codecBuffers = 20; /* max 200ms supported for 10ms frames */ - } - else if (codecID[i] == kDecoderG729_1) - { - codecBytes = 840; /* 210ms @ 32kbps */ - codecBuffers = 10; /* max 200ms supported for 20ms frames */ - } - else if (codecID[i] == kDecoderG726_16) - { - codecBytes = 400; /* 200ms @ 16kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG726_24) - { - codecBytes = 600; /* 200ms @ 24kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG726_32) - { - codecBytes = 800; /* 200ms @ 32kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG726_40) - { - codecBytes = 1000; /* 200ms @ 40kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG722_1_16) - { - codecBytes = 420; /* 210ms @ 16kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG722_1_24) - { - codecBytes = 630; /* 210ms @ 24kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG722_1_32) - { - codecBytes = 840; /* 210ms @ 32kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG722_1C_24) - { - codecBytes = 630; /* 210ms @ 24kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG722_1C_32) - { - codecBytes = 840; /* 210ms @ 32kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG722_1C_48) - { - codecBytes = 1260; /* 210ms @ 48kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderSPEEX_8) - { - codecBytes = 1250; /* 210ms @ 50kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderSPEEX_16) - { - codecBytes = 1250; /* 210ms @ 50kbps */ - codecBuffers = 10; - } - else if ((codecID[i] == kDecoderCELT_32) || - (codecID[i] == kDecoderCELT_32_2ch)) - { - codecBytes = 1250; /* 210ms @ 50kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderGSMFR) - { - codecBytes = 340; /* 200ms */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderAMR) - { - codecBytes = 384; /* 240ms @ 12.2kbps+headers (60ms frames) */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderAMRWB) - { - codecBytes = 744; - codecBuffers = 10; - } - else if (codecID[i] == kDecoderArbitrary) - { - codecBytes = 6720; /* Assume worst case uncompressed WB 210ms */ - codecBuffers = 15; - } - else - { - /*Unknow codec */ - codecBytes = 0; - codecBuffers = 0; - ok = CODEC_DB_UNKNOWN_CODEC; - } - - /* Update max variables */ - *maxBytes = WEBRTC_SPL_MAX((*maxBytes), codecBytes); - *maxSlots = WEBRTC_SPL_MAX((*maxSlots), codecBuffers); - - } /* end of for loop */ - - /* - * Add size needed by the additional pointers for each slot inside struct, - * as indicated on each line below. - */ - w16_tmp = (sizeof(uint32_t) /* timeStamp */ - + sizeof(int16_t*) /* payloadLocation */ - + sizeof(uint16_t) /* seqNumber */ - + sizeof(int16_t) /* payloadType */ - + sizeof(int16_t) /* payloadLengthBytes */ - + sizeof(int16_t) /* rcuPlCntr */ - + sizeof(int)); /* waitingTime */ - /* Add the extra size per slot to the memory count */ - *maxBytes += w16_tmp * (*maxSlots); - - *per_slot_overhead_bytes = w16_tmp; - return ok; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet_buffer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet_buffer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet_buffer.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet_buffer.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This is the implementation of the PacketBuffer class. It is mostly based on +// an STL list. The list is kept sorted at all times so that the next packet to +// decode is at the beginning of the list. + +#include "webrtc/modules/audio_coding/neteq/packet_buffer.h" + +#include // find_if() + +#include "webrtc/modules/audio_coding/neteq/decoder_database.h" +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" + +namespace webrtc { + +// Predicate used when inserting packets in the buffer list. +// Operator() returns true when |packet| goes before |new_packet|. +class NewTimestampIsLarger { + public: + explicit NewTimestampIsLarger(const Packet* new_packet) + : new_packet_(new_packet) { + } + bool operator()(Packet* packet) { + return (*new_packet_ >= *packet); + } + + private: + const Packet* new_packet_; +}; + +PacketBuffer::PacketBuffer(size_t max_number_of_packets) + : max_number_of_packets_(max_number_of_packets) {} + +// Destructor. All packets in the buffer will be destroyed. +PacketBuffer::~PacketBuffer() { + Flush(); +} + +// Flush the buffer. All packets in the buffer will be destroyed. +void PacketBuffer::Flush() { + DeleteAllPackets(&buffer_); +} + +int PacketBuffer::InsertPacket(Packet* packet) { + if (!packet || !packet->payload) { + if (packet) { + delete packet; + } + return kInvalidPacket; + } + + int return_val = kOK; + + if (buffer_.size() >= max_number_of_packets_) { + // Buffer is full. Flush it. + Flush(); + return_val = kFlushed; + } + + // Get an iterator pointing to the place in the buffer where the new packet + // should be inserted. The list is searched from the back, since the most + // likely case is that the new packet should be near the end of the list. + PacketList::reverse_iterator rit = std::find_if( + buffer_.rbegin(), buffer_.rend(), + NewTimestampIsLarger(packet)); + + // The new packet is to be inserted to the right of |rit|. If it has the same + // timestamp as |rit|, which has a higher priority, do not insert the new + // packet to list. + if (rit != buffer_.rend() && + packet->header.timestamp == (*rit)->header.timestamp) { + delete [] packet->payload; + delete packet; + return return_val; + } + + // The new packet is to be inserted to the left of |it|. If it has the same + // timestamp as |it|, which has a lower priority, replace |it| with the new + // packet. + PacketList::iterator it = rit.base(); + if (it != buffer_.end() && + packet->header.timestamp == (*it)->header.timestamp) { + delete [] (*it)->payload; + delete *it; + it = buffer_.erase(it); + } + buffer_.insert(it, packet); // Insert the packet at that position. + + return return_val; +} + +int PacketBuffer::InsertPacketList(PacketList* packet_list, + const DecoderDatabase& decoder_database, + uint8_t* current_rtp_payload_type, + uint8_t* current_cng_rtp_payload_type) { + bool flushed = false; + while (!packet_list->empty()) { + Packet* packet = packet_list->front(); + if (decoder_database.IsComfortNoise(packet->header.payloadType)) { + if (*current_cng_rtp_payload_type != 0xFF && + *current_cng_rtp_payload_type != packet->header.payloadType) { + // New CNG payload type implies new codec type. + *current_rtp_payload_type = 0xFF; + Flush(); + flushed = true; + } + *current_cng_rtp_payload_type = packet->header.payloadType; + } else if (!decoder_database.IsDtmf(packet->header.payloadType)) { + // This must be speech. + if (*current_rtp_payload_type != 0xFF && + *current_rtp_payload_type != packet->header.payloadType) { + *current_cng_rtp_payload_type = 0xFF; + Flush(); + flushed = true; + } + *current_rtp_payload_type = packet->header.payloadType; + } + int return_val = InsertPacket(packet); + packet_list->pop_front(); + if (return_val == kFlushed) { + // The buffer flushed, but this is not an error. We can still continue. + flushed = true; + } else if (return_val != kOK) { + // An error occurred. Delete remaining packets in list and return. + DeleteAllPackets(packet_list); + return return_val; + } + } + return flushed ? kFlushed : kOK; +} + +int PacketBuffer::NextTimestamp(uint32_t* next_timestamp) const { + if (Empty()) { + return kBufferEmpty; + } + if (!next_timestamp) { + return kInvalidPointer; + } + *next_timestamp = buffer_.front()->header.timestamp; + return kOK; +} + +int PacketBuffer::NextHigherTimestamp(uint32_t timestamp, + uint32_t* next_timestamp) const { + if (Empty()) { + return kBufferEmpty; + } + if (!next_timestamp) { + return kInvalidPointer; + } + PacketList::const_iterator it; + for (it = buffer_.begin(); it != buffer_.end(); ++it) { + if ((*it)->header.timestamp >= timestamp) { + // Found a packet matching the search. + *next_timestamp = (*it)->header.timestamp; + return kOK; + } + } + return kNotFound; +} + +const RTPHeader* PacketBuffer::NextRtpHeader() const { + if (Empty()) { + return NULL; + } + return const_cast(&(buffer_.front()->header)); +} + +Packet* PacketBuffer::GetNextPacket(int* discard_count) { + if (Empty()) { + // Buffer is empty. + return NULL; + } + + Packet* packet = buffer_.front(); + // Assert that the packet sanity checks in InsertPacket method works. + assert(packet && packet->payload); + buffer_.pop_front(); + + // Discard other packets with the same timestamp. These are duplicates or + // redundant payloads that should not be used. + int discards = 0; + + while (!Empty() && + buffer_.front()->header.timestamp == packet->header.timestamp) { + if (DiscardNextPacket() != kOK) { + assert(false); // Must be ok by design. + } + ++discards; + } + // The way of inserting packet should not cause any packet discarding here. + // TODO(minyue): remove |discard_count|. + assert(discards == 0); + if (discard_count) + *discard_count = discards; + + return packet; +} + +int PacketBuffer::DiscardNextPacket() { + if (Empty()) { + return kBufferEmpty; + } + // Assert that the packet sanity checks in InsertPacket method works. + assert(buffer_.front()); + assert(buffer_.front()->payload); + DeleteFirstPacket(&buffer_); + return kOK; +} + +int PacketBuffer::DiscardOldPackets(uint32_t timestamp_limit, + uint32_t horizon_samples) { + while (!Empty() && timestamp_limit != buffer_.front()->header.timestamp && + IsObsoleteTimestamp(buffer_.front()->header.timestamp, + timestamp_limit, + horizon_samples)) { + if (DiscardNextPacket() != kOK) { + assert(false); // Must be ok by design. + } + } + return 0; +} + +int PacketBuffer::NumSamplesInBuffer(DecoderDatabase* decoder_database, + int last_decoded_length) const { + PacketList::const_iterator it; + int num_samples = 0; + int last_duration = last_decoded_length; + for (it = buffer_.begin(); it != buffer_.end(); ++it) { + Packet* packet = (*it); + AudioDecoder* decoder = + decoder_database->GetDecoder(packet->header.payloadType); + if (decoder) { + int duration; + if (packet->sync_packet) { + duration = last_duration; + } else if (packet->primary) { + duration = + decoder->PacketDuration(packet->payload, packet->payload_length); + } else { + continue; + } + if (duration >= 0) { + last_duration = duration; // Save the most up-to-date (valid) duration. + } + } + num_samples += last_duration; + } + return num_samples; +} + +void PacketBuffer::IncrementWaitingTimes(int inc) { + PacketList::iterator it; + for (it = buffer_.begin(); it != buffer_.end(); ++it) { + (*it)->waiting_time += inc; + } +} + +bool PacketBuffer::DeleteFirstPacket(PacketList* packet_list) { + if (packet_list->empty()) { + return false; + } + Packet* first_packet = packet_list->front(); + delete [] first_packet->payload; + delete first_packet; + packet_list->pop_front(); + return true; +} + +void PacketBuffer::DeleteAllPackets(PacketList* packet_list) { + while (DeleteFirstPacket(packet_list)) { + // Continue while the list is not empty. + } +} + +void PacketBuffer::BufferStat(int* num_packets, int* max_num_packets) const { + *num_packets = static_cast(buffer_.size()); + *max_num_packets = static_cast(max_number_of_packets_); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet_buffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet_buffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet_buffer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet_buffer.h 2015-02-03 14:33:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source @@ -8,247 +8,152 @@ * be found in the AUTHORS file in the root of the source tree. */ -/* - * Interface for the actual packet buffer data structure. - */ - -#ifndef PACKET_BUFFER_H -#define PACKET_BUFFER_H - -#include "typedefs.h" - -#include "webrtc_neteq.h" -#include "rtp.h" - -/* Define minimum allowed buffer memory, in 16-bit words */ -#define PBUFFER_MIN_MEMORY_SIZE 150 - -/****************************/ -/* The packet buffer struct */ -/****************************/ - -typedef struct -{ - - /* Variables common to the entire buffer */ - uint16_t packSizeSamples; /* packet size in samples of last decoded packet */ - int16_t *startPayloadMemory; /* pointer to the payload memory */ - int memorySizeW16; /* the size (in int16_t) of the payload memory */ - int16_t *currentMemoryPos; /* The memory position to insert next payload */ - int numPacketsInBuffer; /* The number of packets in the buffer */ - int insertPosition; /* The position to insert next packet */ - int maxInsertPositions; /* Maximum number of packets allowed */ - - /* Arrays with one entry per packet slot */ - /* NOTE: If these are changed, the changes must be accounted for at the end of - the function WebRtcNetEQ_GetDefaultCodecSettings(). */ - uint32_t *timeStamp; /* Timestamp in slot n */ - int16_t **payloadLocation; /* Memory location of payload in slot n */ - uint16_t *seqNumber; /* Sequence number in slot n */ - int16_t *payloadType; /* Payload type of packet in slot n */ - int16_t *payloadLengthBytes; /* Payload length of packet in slot n */ - int16_t *rcuPlCntr; /* zero for non-RCU payload, 1 for main payload - 2 for redundant payload */ - int *waitingTime; - - /* Statistics counter */ - uint16_t discardedPackets; /* Number of discarded packets */ - -} PacketBuf_t; - -/*************************/ -/* Function declarations */ -/*************************/ - -/**************************************************************************** - * WebRtcNetEQ_PacketBufferInit(...) - * - * This function initializes the packet buffer. - * - * Input: - * - bufferInst : Buffer instance to be initialized - * - noOfPackets : Maximum number of packets that buffer should hold - * - memory : Pointer to the storage memory for the payloads - * - memorySize : The size of the payload memory (in int16_t) - * - * Output: - * - bufferInst : Updated buffer instance - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_PacketBufferInit(PacketBuf_t *bufferInst, int maxNoOfPackets, - int16_t *pw16_memory, int memorySize); - -/**************************************************************************** - * WebRtcNetEQ_PacketBufferFlush(...) - * - * This function flushes all the packets in the buffer. - * - * Input: - * - bufferInst : Buffer instance to be flushed - * - * Output: - * - bufferInst : Flushed buffer instance - * - * Return value : 0 - Ok - */ - -int WebRtcNetEQ_PacketBufferFlush(PacketBuf_t *bufferInst); - -/**************************************************************************** - * WebRtcNetEQ_PacketBufferInsert(...) - * - * This function inserts an RTP packet into the packet buffer. - * - * Input: - * - bufferInst : Buffer instance - * - RTPpacket : An RTP packet struct (with payload, sequence - * number, etc.) - * - av_sync : 1 indicates AV-sync enabled, 0 disabled. - * - * Output: - * - bufferInst : Updated buffer instance - * - flushed : 1 if buffer was flushed, 0 otherwise - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_PacketBufferInsert(PacketBuf_t *bufferInst, const RTPPacket_t *RTPpacket, - int16_t *flushed, int av_sync); - -/**************************************************************************** - * WebRtcNetEQ_PacketBufferExtract(...) - * - * This function extracts a payload from the buffer. - * - * Input: - * - bufferInst : Buffer instance - * - bufferPosition: Position of the packet that should be extracted - * - * Output: - * - RTPpacket : An RTP packet struct (with payload, sequence - * number, etc) - * - bufferInst : Updated buffer instance - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_PacketBufferExtract(PacketBuf_t *bufferInst, RTPPacket_t *RTPpacket, - int bufferPosition, int *waitingTime); - -/**************************************************************************** - * WebRtcNetEQ_PacketBufferFindLowestTimestamp(...) - * - * This function finds the next packet with the lowest timestamp. - * - * Input: - * - buffer_inst : Buffer instance. - * - current_time_stamp : The timestamp to compare packet timestamps with. - * - erase_old_packets : If non-zero, erase packets older than currentTS. - * - * Output: - * - time_stamp : Lowest timestamp that was found. - * - buffer_position : Position of this packet (-1 if there are no - * packets in the buffer). - * - payload_type : Payload type of the found payload. - * - * Return value : 0 - Ok; - * < 0 - Error. - */ - -int WebRtcNetEQ_PacketBufferFindLowestTimestamp(PacketBuf_t* buffer_inst, - uint32_t current_time_stamp, - uint32_t* time_stamp, - int* buffer_position, - int erase_old_packets, - int16_t* payload_type); - -/**************************************************************************** - * WebRtcNetEQ_PacketBufferGetPacketSize(...) - * - * Calculate and return an estimate of the data length (in samples) of the - * given packet. If no estimate is available (because we do not know how to - * compute packet durations for the associated payload type), last_duration - * will be returned instead. - * - * Input: - * - buffer_inst : Buffer instance - * - buffer_pos : The index of the buffer of which to estimate the - * duration - * - codec_database : Codec database instance - * - codec_pos : The codec database entry associated with the payload - * type of the specified buffer. - * - last_duration : The duration of the previous frame. - * - av_sync : 1 indicates AV-sync enabled, 0 disabled. - * - * Return value : The buffer size in samples - */ - -int WebRtcNetEQ_PacketBufferGetPacketSize(const PacketBuf_t* buffer_inst, - int buffer_pos, - const CodecDbInst_t* codec_database, - int codec_pos, int last_duration, - int av_sync); - -/**************************************************************************** - * WebRtcNetEQ_PacketBufferGetSize(...) - * - * Calculate and return an estimate of the total data length (in samples) - * currently in the buffer. The estimate is calculated as the number of - * packets currently in the buffer (which does not have any remaining waiting - * time), multiplied with the number of samples obtained from the last - * decoded packet. - * - * Input: - * - buffer_inst : Buffer instance - * - codec_database : Codec database instance - * - av_sync : 1 indicates AV-sync enabled, 0 disabled. - * - * Return value : The buffer size in samples - */ - -int32_t WebRtcNetEQ_PacketBufferGetSize(const PacketBuf_t* buffer_inst, - const CodecDbInst_t* codec_database, - int av_sync); - -/**************************************************************************** - * WebRtcNetEQ_IncrementWaitingTimes(...) - * - * Increment the waiting time for all packets in the buffer by one. - * - * Input: - * - bufferInst : Buffer instance - * - * Return value : n/a - */ - -void WebRtcNetEQ_IncrementWaitingTimes(PacketBuf_t *buffer_inst); - -/**************************************************************************** - * WebRtcNetEQ_GetDefaultCodecSettings(...) - * - * Calculates a recommended buffer size for a specific set of codecs. - * - * Input: - * - codecID : An array of codec types that will be used - * - noOfCodecs : Number of codecs in array codecID - * - * Output: - * - maxBytes : Recommended buffer memory size in bytes - * - maxSlots : Recommended number of slots in buffer - * - per_slot_overhead_bytes : overhead in bytes for each slot in buffer. - * - * Return value : 0 - Ok - * <0 - Error - */ +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_PACKET_BUFFER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_PACKET_BUFFER_H_ -int WebRtcNetEQ_GetDefaultCodecSettings(const enum WebRtcNetEQDecoder *codecID, - int noOfCodecs, int *maxBytes, - int *maxSlots, - int* per_slot_overhead_bytes); +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/packet.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Forward declaration. +class DecoderDatabase; + +// This is the actual buffer holding the packets before decoding. +class PacketBuffer { + public: + enum BufferReturnCodes { + kOK = 0, + kFlushed, + kNotFound, + kBufferEmpty, + kInvalidPacket, + kInvalidPointer + }; + + // Constructor creates a buffer which can hold a maximum of + // |max_number_of_packets| packets. + PacketBuffer(size_t max_number_of_packets); + + // Deletes all packets in the buffer before destroying the buffer. + virtual ~PacketBuffer(); + + // Flushes the buffer and deletes all packets in it. + virtual void Flush(); + + // Returns true for an empty buffer. + virtual bool Empty() const { return buffer_.empty(); } + + // Inserts |packet| into the buffer. The buffer will take over ownership of + // the packet object. + // Returns PacketBuffer::kOK on success, PacketBuffer::kFlushed if the buffer + // was flushed due to overfilling. + virtual int InsertPacket(Packet* packet); + + // Inserts a list of packets into the buffer. The buffer will take over + // ownership of the packet objects. + // Returns PacketBuffer::kOK if all packets were inserted successfully. + // If the buffer was flushed due to overfilling, only a subset of the list is + // inserted, and PacketBuffer::kFlushed is returned. + // The last three parameters are included for legacy compatibility. + // TODO(hlundin): Redesign to not use current_*_payload_type and + // decoder_database. + virtual int InsertPacketList(PacketList* packet_list, + const DecoderDatabase& decoder_database, + uint8_t* current_rtp_payload_type, + uint8_t* current_cng_rtp_payload_type); + + // Gets the timestamp for the first packet in the buffer and writes it to the + // output variable |next_timestamp|. + // Returns PacketBuffer::kBufferEmpty if the buffer is empty, + // PacketBuffer::kOK otherwise. + virtual int NextTimestamp(uint32_t* next_timestamp) const; + + // Gets the timestamp for the first packet in the buffer with a timestamp no + // lower than the input limit |timestamp|. The result is written to the output + // variable |next_timestamp|. + // Returns PacketBuffer::kBufferEmpty if the buffer is empty, + // PacketBuffer::kOK otherwise. + virtual int NextHigherTimestamp(uint32_t timestamp, + uint32_t* next_timestamp) const; + + // Returns a (constant) pointer the RTP header of the first packet in the + // buffer. Returns NULL if the buffer is empty. + virtual const RTPHeader* NextRtpHeader() const; + + // Extracts the first packet in the buffer and returns a pointer to it. + // Returns NULL if the buffer is empty. The caller is responsible for deleting + // the packet. + // Subsequent packets with the same timestamp as the one extracted will be + // discarded and properly deleted. The number of discarded packets will be + // written to the output variable |discard_count|. + virtual Packet* GetNextPacket(int* discard_count); + + // Discards the first packet in the buffer. The packet is deleted. + // Returns PacketBuffer::kBufferEmpty if the buffer is empty, + // PacketBuffer::kOK otherwise. + virtual int DiscardNextPacket(); + + // Discards all packets that are (strictly) older than timestamp_limit, + // but newer than timestamp_limit - horizon_samples. Setting horizon_samples + // to zero implies that the horizon is set to half the timestamp range. That + // is, if a packet is more than 2^31 timestamps into the future compared with + // timestamp_limit (including wrap-around), it is considered old. + // Returns number of packets discarded. + virtual int DiscardOldPackets(uint32_t timestamp_limit, + uint32_t horizon_samples); + + // Discards all packets that are (strictly) older than timestamp_limit. + virtual int DiscardAllOldPackets(uint32_t timestamp_limit) { + return DiscardOldPackets(timestamp_limit, 0); + } + + // Returns the number of packets in the buffer, including duplicates and + // redundant packets. + virtual int NumPacketsInBuffer() const { + return static_cast(buffer_.size()); + } + + // Returns the number of samples in the buffer, including samples carried in + // duplicate and redundant packets. + virtual int NumSamplesInBuffer(DecoderDatabase* decoder_database, + int last_decoded_length) const; + + // Increase the waiting time counter for every packet in the buffer by |inc|. + // The default value for |inc| is 1. + virtual void IncrementWaitingTimes(int inc = 1); + + virtual void BufferStat(int* num_packets, int* max_num_packets) const; + + // Static method that properly deletes the first packet, and its payload + // array, in |packet_list|. Returns false if |packet_list| already was empty, + // otherwise true. + static bool DeleteFirstPacket(PacketList* packet_list); + + // Static method that properly deletes all packets, and their payload arrays, + // in |packet_list|. + static void DeleteAllPackets(PacketList* packet_list); + + // Static method returning true if |timestamp| is older than |timestamp_limit| + // but less than |horizon_samples| behind |timestamp_limit|. For instance, + // with timestamp_limit = 100 and horizon_samples = 10, a timestamp in the + // range (90, 100) is considered obsolete, and will yield true. + // Setting |horizon_samples| to 0 is the same as setting it to 2^31, i.e., + // half the 32-bit timestamp range. + static bool IsObsoleteTimestamp(uint32_t timestamp, + uint32_t timestamp_limit, + uint32_t horizon_samples) { + return IsNewerTimestamp(timestamp_limit, timestamp) && + (horizon_samples == 0 || + IsNewerTimestamp(timestamp, timestamp_limit - horizon_samples)); + } + + private: + size_t max_number_of_packets_; + PacketList buffer_; + DISALLOW_COPY_AND_ASSIGN(PacketBuffer); +}; -#endif /* PACKET_BUFFER_H */ +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_PACKET_BUFFER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for PacketBuffer class. + +#include "webrtc/modules/audio_coding/neteq/packet_buffer.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/neteq/mock/mock_decoder_database.h" +#include "webrtc/modules/audio_coding/neteq/packet.h" + +using ::testing::Return; +using ::testing::_; + +namespace webrtc { + +// Helper class to generate packets. Packets must be deleted by the user. +class PacketGenerator { + public: + PacketGenerator(uint16_t seq_no, uint32_t ts, uint8_t pt, int frame_size); + virtual ~PacketGenerator() {} + void Reset(uint16_t seq_no, uint32_t ts, uint8_t pt, int frame_size); + Packet* NextPacket(int payload_size_bytes); + + uint16_t seq_no_; + uint32_t ts_; + uint8_t pt_; + int frame_size_; +}; + +PacketGenerator::PacketGenerator(uint16_t seq_no, uint32_t ts, uint8_t pt, + int frame_size) { + Reset(seq_no, ts, pt, frame_size); +} + +void PacketGenerator::Reset(uint16_t seq_no, uint32_t ts, uint8_t pt, + int frame_size) { + seq_no_ = seq_no; + ts_ = ts; + pt_ = pt; + frame_size_ = frame_size; +} + +Packet* PacketGenerator::NextPacket(int payload_size_bytes) { + Packet* packet = new Packet; + packet->header.sequenceNumber = seq_no_; + packet->header.timestamp = ts_; + packet->header.payloadType = pt_; + packet->header.markerBit = false; + packet->header.ssrc = 0x12345678; + packet->header.numCSRCs = 0; + packet->header.paddingLength = 0; + packet->payload_length = payload_size_bytes; + packet->primary = true; + packet->payload = new uint8_t[payload_size_bytes]; + ++seq_no_; + ts_ += frame_size_; + return packet; +} + +struct PacketsToInsert { + uint16_t sequence_number; + uint32_t timestamp; + uint8_t payload_type; + bool primary; + // Order of this packet to appear upon extraction, after inserting a series + // of packets. A negative number means that it should have been discarded + // before extraction. + int extract_order; +}; + +// Start of test definitions. + +TEST(PacketBuffer, CreateAndDestroy) { + PacketBuffer* buffer = new PacketBuffer(10); // 10 packets. + EXPECT_TRUE(buffer->Empty()); + delete buffer; +} + +TEST(PacketBuffer, InsertPacket) { + PacketBuffer buffer(10); // 10 packets. + PacketGenerator gen(17u, 4711u, 0, 10); + + const int payload_len = 100; + Packet* packet = gen.NextPacket(payload_len); + + EXPECT_EQ(0, buffer.InsertPacket(packet)); + uint32_t next_ts; + EXPECT_EQ(PacketBuffer::kOK, buffer.NextTimestamp(&next_ts)); + EXPECT_EQ(4711u, next_ts); + EXPECT_FALSE(buffer.Empty()); + EXPECT_EQ(1, buffer.NumPacketsInBuffer()); + const RTPHeader* hdr = buffer.NextRtpHeader(); + EXPECT_EQ(&(packet->header), hdr); // Compare pointer addresses. + + // Do not explicitly flush buffer or delete packet to test that it is deleted + // with the buffer. (Tested with Valgrind or similar tool.) +} + +// Test to flush buffer. +TEST(PacketBuffer, FlushBuffer) { + PacketBuffer buffer(10); // 10 packets. + PacketGenerator gen(0, 0, 0, 10); + const int payload_len = 10; + + // Insert 10 small packets; should be ok. + for (int i = 0; i < 10; ++i) { + Packet* packet = gen.NextPacket(payload_len); + EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacket(packet)); + } + EXPECT_EQ(10, buffer.NumPacketsInBuffer()); + EXPECT_FALSE(buffer.Empty()); + + buffer.Flush(); + // Buffer should delete the payloads itself. + EXPECT_EQ(0, buffer.NumPacketsInBuffer()); + EXPECT_TRUE(buffer.Empty()); +} + +// Test to fill the buffer over the limits, and verify that it flushes. +TEST(PacketBuffer, OverfillBuffer) { + PacketBuffer buffer(10); // 10 packets. + PacketGenerator gen(0, 0, 0, 10); + + // Insert 10 small packets; should be ok. + const int payload_len = 10; + int i; + for (i = 0; i < 10; ++i) { + Packet* packet = gen.NextPacket(payload_len); + EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacket(packet)); + } + EXPECT_EQ(10, buffer.NumPacketsInBuffer()); + uint32_t next_ts; + EXPECT_EQ(PacketBuffer::kOK, buffer.NextTimestamp(&next_ts)); + EXPECT_EQ(0u, next_ts); // Expect first inserted packet to be first in line. + + // Insert 11th packet; should flush the buffer and insert it after flushing. + Packet* packet = gen.NextPacket(payload_len); + EXPECT_EQ(PacketBuffer::kFlushed, buffer.InsertPacket(packet)); + EXPECT_EQ(1, buffer.NumPacketsInBuffer()); + EXPECT_EQ(PacketBuffer::kOK, buffer.NextTimestamp(&next_ts)); + // Expect last inserted packet to be first in line. + EXPECT_EQ(packet->header.timestamp, next_ts); + + // Flush buffer to delete all packets. + buffer.Flush(); +} + +// Test inserting a list of packets. +TEST(PacketBuffer, InsertPacketList) { + PacketBuffer buffer(10); // 10 packets. + PacketGenerator gen(0, 0, 0, 10); + PacketList list; + const int payload_len = 10; + + // Insert 10 small packets. + for (int i = 0; i < 10; ++i) { + Packet* packet = gen.NextPacket(payload_len); + list.push_back(packet); + } + + MockDecoderDatabase decoder_database; + EXPECT_CALL(decoder_database, IsComfortNoise(0)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(decoder_database, IsDtmf(0)) + .WillRepeatedly(Return(false)); + uint8_t current_pt = 0xFF; + uint8_t current_cng_pt = 0xFF; + EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacketList(&list, + decoder_database, + ¤t_pt, + ¤t_cng_pt)); + EXPECT_TRUE(list.empty()); // The PacketBuffer should have depleted the list. + EXPECT_EQ(10, buffer.NumPacketsInBuffer()); + EXPECT_EQ(0, current_pt); // Current payload type changed to 0. + EXPECT_EQ(0xFF, current_cng_pt); // CNG payload type not changed. + + buffer.Flush(); // Clean up. + + EXPECT_CALL(decoder_database, Die()); // Called when object is deleted. +} + +// Test inserting a list of packets. Last packet is of a different payload type. +// Expecting the buffer to flush. +// TODO(hlundin): Remove this test when legacy operation is no longer needed. +TEST(PacketBuffer, InsertPacketListChangePayloadType) { + PacketBuffer buffer(10); // 10 packets. + PacketGenerator gen(0, 0, 0, 10); + PacketList list; + const int payload_len = 10; + + // Insert 10 small packets. + for (int i = 0; i < 10; ++i) { + Packet* packet = gen.NextPacket(payload_len); + list.push_back(packet); + } + // Insert 11th packet of another payload type (not CNG). + Packet* packet = gen.NextPacket(payload_len); + packet->header.payloadType = 1; + list.push_back(packet); + + + MockDecoderDatabase decoder_database; + EXPECT_CALL(decoder_database, IsComfortNoise(_)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(decoder_database, IsDtmf(_)) + .WillRepeatedly(Return(false)); + uint8_t current_pt = 0xFF; + uint8_t current_cng_pt = 0xFF; + EXPECT_EQ(PacketBuffer::kFlushed, buffer.InsertPacketList(&list, + decoder_database, + ¤t_pt, + ¤t_cng_pt)); + EXPECT_TRUE(list.empty()); // The PacketBuffer should have depleted the list. + EXPECT_EQ(1, buffer.NumPacketsInBuffer()); // Only the last packet. + EXPECT_EQ(1, current_pt); // Current payload type changed to 0. + EXPECT_EQ(0xFF, current_cng_pt); // CNG payload type not changed. + + buffer.Flush(); // Clean up. + + EXPECT_CALL(decoder_database, Die()); // Called when object is deleted. +} + +TEST(PacketBuffer, ExtractOrderRedundancy) { + PacketBuffer buffer(100); // 100 packets. + const int kPackets = 18; + const int kFrameSize = 10; + const int kPayloadLength = 10; + + PacketsToInsert packet_facts[kPackets] = { + {0xFFFD, 0xFFFFFFD7, 0, true, 0}, + {0xFFFE, 0xFFFFFFE1, 0, true, 1}, + {0xFFFE, 0xFFFFFFD7, 1, false, -1}, + {0xFFFF, 0xFFFFFFEB, 0, true, 2}, + {0xFFFF, 0xFFFFFFE1, 1, false, -1}, + {0x0000, 0xFFFFFFF5, 0, true, 3}, + {0x0000, 0xFFFFFFEB, 1, false, -1}, + {0x0001, 0xFFFFFFFF, 0, true, 4}, + {0x0001, 0xFFFFFFF5, 1, false, -1}, + {0x0002, 0x0000000A, 0, true, 5}, + {0x0002, 0xFFFFFFFF, 1, false, -1}, + {0x0003, 0x0000000A, 1, false, -1}, + {0x0004, 0x0000001E, 0, true, 7}, + {0x0004, 0x00000014, 1, false, 6}, + {0x0005, 0x0000001E, 0, true, -1}, + {0x0005, 0x00000014, 1, false, -1}, + {0x0006, 0x00000028, 0, true, 8}, + {0x0006, 0x0000001E, 1, false, -1}, + }; + + const int kExpectPacketsInBuffer = 9; + + std::vector expect_order(kExpectPacketsInBuffer); + + PacketGenerator gen(0, 0, 0, kFrameSize); + + for (int i = 0; i < kPackets; ++i) { + gen.Reset(packet_facts[i].sequence_number, + packet_facts[i].timestamp, + packet_facts[i].payload_type, + kFrameSize); + Packet* packet = gen.NextPacket(kPayloadLength); + packet->primary = packet_facts[i].primary; + EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacket(packet)); + if (packet_facts[i].extract_order >= 0) { + expect_order[packet_facts[i].extract_order] = packet; + } + } + + EXPECT_EQ(kExpectPacketsInBuffer, buffer.NumPacketsInBuffer()); + + int drop_count; + for (int i = 0; i < kExpectPacketsInBuffer; ++i) { + Packet* packet = buffer.GetNextPacket(&drop_count); + EXPECT_EQ(0, drop_count); + EXPECT_EQ(packet, expect_order[i]); // Compare pointer addresses. + delete[] packet->payload; + delete packet; + } + EXPECT_TRUE(buffer.Empty()); +} + +TEST(PacketBuffer, DiscardPackets) { + PacketBuffer buffer(100); // 100 packets. + const uint16_t start_seq_no = 17; + const uint32_t start_ts = 4711; + const uint32_t ts_increment = 10; + PacketGenerator gen(start_seq_no, start_ts, 0, ts_increment); + PacketList list; + const int payload_len = 10; + + // Insert 10 small packets. + for (int i = 0; i < 10; ++i) { + Packet* packet = gen.NextPacket(payload_len); + buffer.InsertPacket(packet); + } + EXPECT_EQ(10, buffer.NumPacketsInBuffer()); + + // Discard them one by one and make sure that the right packets are at the + // front of the buffer. + uint32_t current_ts = start_ts; + for (int i = 0; i < 10; ++i) { + uint32_t ts; + EXPECT_EQ(PacketBuffer::kOK, buffer.NextTimestamp(&ts)); + EXPECT_EQ(current_ts, ts); + EXPECT_EQ(PacketBuffer::kOK, buffer.DiscardNextPacket()); + current_ts += ts_increment; + } + EXPECT_TRUE(buffer.Empty()); +} + +TEST(PacketBuffer, Reordering) { + PacketBuffer buffer(100); // 100 packets. + const uint16_t start_seq_no = 17; + const uint32_t start_ts = 4711; + const uint32_t ts_increment = 10; + PacketGenerator gen(start_seq_no, start_ts, 0, ts_increment); + const int payload_len = 10; + + // Generate 10 small packets and insert them into a PacketList. Insert every + // odd packet to the front, and every even packet to the back, thus creating + // a (rather strange) reordering. + PacketList list; + for (int i = 0; i < 10; ++i) { + Packet* packet = gen.NextPacket(payload_len); + if (i % 2) { + list.push_front(packet); + } else { + list.push_back(packet); + } + } + + MockDecoderDatabase decoder_database; + EXPECT_CALL(decoder_database, IsComfortNoise(0)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(decoder_database, IsDtmf(0)) + .WillRepeatedly(Return(false)); + uint8_t current_pt = 0xFF; + uint8_t current_cng_pt = 0xFF; + + EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacketList(&list, + decoder_database, + ¤t_pt, + ¤t_cng_pt)); + EXPECT_EQ(10, buffer.NumPacketsInBuffer()); + + // Extract them and make sure that come out in the right order. + uint32_t current_ts = start_ts; + for (int i = 0; i < 10; ++i) { + Packet* packet = buffer.GetNextPacket(NULL); + ASSERT_FALSE(packet == NULL); + EXPECT_EQ(current_ts, packet->header.timestamp); + current_ts += ts_increment; + delete [] packet->payload; + delete packet; + } + EXPECT_TRUE(buffer.Empty()); + + EXPECT_CALL(decoder_database, Die()); // Called when object is deleted. +} + +TEST(PacketBuffer, Failures) { + const uint16_t start_seq_no = 17; + const uint32_t start_ts = 4711; + const uint32_t ts_increment = 10; + int payload_len = 100; + PacketGenerator gen(start_seq_no, start_ts, 0, ts_increment); + + PacketBuffer* buffer = new PacketBuffer(100); // 100 packets. + Packet* packet = NULL; + EXPECT_EQ(PacketBuffer::kInvalidPacket, buffer->InsertPacket(packet)); + packet = gen.NextPacket(payload_len); + delete [] packet->payload; + packet->payload = NULL; + EXPECT_EQ(PacketBuffer::kInvalidPacket, buffer->InsertPacket(packet)); + // Packet is deleted by the PacketBuffer. + + // Buffer should still be empty. Test all empty-checks. + uint32_t temp_ts; + EXPECT_EQ(PacketBuffer::kBufferEmpty, buffer->NextTimestamp(&temp_ts)); + EXPECT_EQ(PacketBuffer::kBufferEmpty, + buffer->NextHigherTimestamp(0, &temp_ts)); + EXPECT_EQ(NULL, buffer->NextRtpHeader()); + EXPECT_EQ(NULL, buffer->GetNextPacket(NULL)); + EXPECT_EQ(PacketBuffer::kBufferEmpty, buffer->DiscardNextPacket()); + EXPECT_EQ(0, buffer->DiscardAllOldPackets(0)); // 0 packets discarded. + + // Insert one packet to make the buffer non-empty. + packet = gen.NextPacket(payload_len); + EXPECT_EQ(PacketBuffer::kOK, buffer->InsertPacket(packet)); + EXPECT_EQ(PacketBuffer::kInvalidPointer, buffer->NextTimestamp(NULL)); + EXPECT_EQ(PacketBuffer::kInvalidPointer, + buffer->NextHigherTimestamp(0, NULL)); + delete buffer; + + // Insert packet list of three packets, where the second packet has an invalid + // payload. Expect first packet to be inserted, and the remaining two to be + // discarded. + buffer = new PacketBuffer(100); // 100 packets. + PacketList list; + list.push_back(gen.NextPacket(payload_len)); // Valid packet. + packet = gen.NextPacket(payload_len); + delete [] packet->payload; + packet->payload = NULL; // Invalid. + list.push_back(packet); + list.push_back(gen.NextPacket(payload_len)); // Valid packet. + MockDecoderDatabase decoder_database; + EXPECT_CALL(decoder_database, IsComfortNoise(0)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(decoder_database, IsDtmf(0)) + .WillRepeatedly(Return(false)); + uint8_t current_pt = 0xFF; + uint8_t current_cng_pt = 0xFF; + EXPECT_EQ(PacketBuffer::kInvalidPacket, + buffer->InsertPacketList(&list, + decoder_database, + ¤t_pt, + ¤t_cng_pt)); + EXPECT_TRUE(list.empty()); // The PacketBuffer should have depleted the list. + EXPECT_EQ(1, buffer->NumPacketsInBuffer()); + delete buffer; + EXPECT_CALL(decoder_database, Die()); // Called when object is deleted. +} + +// Test packet comparison function. +// The function should return true if the first packet "goes before" the second. +TEST(PacketBuffer, ComparePackets) { + PacketGenerator gen(0, 0, 0, 10); + Packet* a = gen.NextPacket(10); // SN = 0, TS = 0. + Packet* b = gen.NextPacket(10); // SN = 1, TS = 10. + EXPECT_FALSE(*a == *b); + EXPECT_TRUE(*a != *b); + EXPECT_TRUE(*a < *b); + EXPECT_FALSE(*a > *b); + EXPECT_TRUE(*a <= *b); + EXPECT_FALSE(*a >= *b); + + // Testing wrap-around case; 'a' is earlier but has a larger timestamp value. + a->header.timestamp = 0xFFFFFFFF - 10; + EXPECT_FALSE(*a == *b); + EXPECT_TRUE(*a != *b); + EXPECT_TRUE(*a < *b); + EXPECT_FALSE(*a > *b); + EXPECT_TRUE(*a <= *b); + EXPECT_FALSE(*a >= *b); + + // Test equal packets. + EXPECT_TRUE(*a == *a); + EXPECT_FALSE(*a != *a); + EXPECT_FALSE(*a < *a); + EXPECT_FALSE(*a > *a); + EXPECT_TRUE(*a <= *a); + EXPECT_TRUE(*a >= *a); + + // Test equal timestamps but different sequence numbers (0 and 1). + a->header.timestamp = b->header.timestamp; + EXPECT_FALSE(*a == *b); + EXPECT_TRUE(*a != *b); + EXPECT_TRUE(*a < *b); + EXPECT_FALSE(*a > *b); + EXPECT_TRUE(*a <= *b); + EXPECT_FALSE(*a >= *b); + + // Test equal timestamps but different sequence numbers (32767 and 1). + a->header.sequenceNumber = 0xFFFF; + EXPECT_FALSE(*a == *b); + EXPECT_TRUE(*a != *b); + EXPECT_TRUE(*a < *b); + EXPECT_FALSE(*a > *b); + EXPECT_TRUE(*a <= *b); + EXPECT_FALSE(*a >= *b); + + // Test equal timestamps and sequence numbers, but only 'b' is primary. + a->header.sequenceNumber = b->header.sequenceNumber; + a->primary = false; + b->primary = true; + EXPECT_FALSE(*a == *b); + EXPECT_TRUE(*a != *b); + EXPECT_FALSE(*a < *b); + EXPECT_TRUE(*a > *b); + EXPECT_FALSE(*a <= *b); + EXPECT_TRUE(*a >= *b); + + delete [] a->payload; + delete a; + delete [] b->payload; + delete b; +} + +// Test the DeleteFirstPacket DeleteAllPackets methods. +TEST(PacketBuffer, DeleteAllPackets) { + PacketGenerator gen(0, 0, 0, 10); + PacketList list; + const int payload_len = 10; + + // Insert 10 small packets. + for (int i = 0; i < 10; ++i) { + Packet* packet = gen.NextPacket(payload_len); + list.push_back(packet); + } + EXPECT_TRUE(PacketBuffer::DeleteFirstPacket(&list)); + EXPECT_EQ(9u, list.size()); + PacketBuffer::DeleteAllPackets(&list); + EXPECT_TRUE(list.empty()); + EXPECT_FALSE(PacketBuffer::DeleteFirstPacket(&list)); +} + +namespace { +void TestIsObsoleteTimestamp(uint32_t limit_timestamp) { + // Check with zero horizon, which implies that the horizon is at 2^31, i.e., + // half the timestamp range. + static const uint32_t kZeroHorizon = 0; + static const uint32_t k2Pow31Minus1 = 0x7FFFFFFF; + // Timestamp on the limit is not old. + EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp, limit_timestamp, kZeroHorizon)); + // 1 sample behind is old. + EXPECT_TRUE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp - 1, limit_timestamp, kZeroHorizon)); + // 2^31 - 1 samples behind is old. + EXPECT_TRUE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp - k2Pow31Minus1, limit_timestamp, kZeroHorizon)); + // 1 sample ahead is not old. + EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp + 1, limit_timestamp, kZeroHorizon)); + // 2^31 samples ahead is not old. + EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp + (1 << 31), limit_timestamp, kZeroHorizon)); + + // Fixed horizon at 10 samples. + static const uint32_t kHorizon = 10; + // Timestamp on the limit is not old. + EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp, limit_timestamp, kHorizon)); + // 1 sample behind is old. + EXPECT_TRUE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp - 1, limit_timestamp, kHorizon)); + // 9 samples behind is old. + EXPECT_TRUE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp - 9, limit_timestamp, kHorizon)); + // 10 samples behind is not old. + EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp - 10, limit_timestamp, kHorizon)); + // 2^31 - 1 samples behind is not old. + EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp - k2Pow31Minus1, limit_timestamp, kHorizon)); + // 1 sample ahead is not old. + EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp + 1, limit_timestamp, kHorizon)); + // 2^31 samples ahead is not old. + EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp + (1 << 31), limit_timestamp, kHorizon)); +} +} // namespace + +// Test the IsObsoleteTimestamp method with different limit timestamps. +TEST(PacketBuffer, IsObsoleteTimestamp) { + TestIsObsoleteTimestamp(0); + TestIsObsoleteTimestamp(1); + TestIsObsoleteTimestamp(0xFFFFFFFF); // -1 in uint32_t. + TestIsObsoleteTimestamp(0x80000000); // 2^31. + TestIsObsoleteTimestamp(0x80000001); // 2^31 + 1. + TestIsObsoleteTimestamp(0x7FFFFFFF); // 2^31 - 1. +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/packet.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_PACKET_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_PACKET_H_ + +#include + +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Struct for holding RTP packets. +struct Packet { + RTPHeader header; + uint8_t* payload; // Datagram excluding RTP header and header extension. + int payload_length; + bool primary; // Primary, i.e., not redundant payload. + int waiting_time; + bool sync_packet; + + // Constructor. + Packet() + : payload(NULL), + payload_length(0), + primary(true), + waiting_time(0), + sync_packet(false) { + } + + // Comparison operators. Establish a packet ordering based on (1) timestamp, + // (2) sequence number, (3) regular packet vs sync-packet and (4) redundancy. + // Timestamp and sequence numbers are compared taking wrap-around into + // account. If both timestamp and sequence numbers are identical and one of + // the packets is sync-packet, the regular packet is considered earlier. For + // two regular packets with the same sequence number and timestamp a primary + // payload is considered "smaller" than a secondary. + bool operator==(const Packet& rhs) const { + return (this->header.timestamp == rhs.header.timestamp && + this->header.sequenceNumber == rhs.header.sequenceNumber && + this->primary == rhs.primary && + this->sync_packet == rhs.sync_packet); + } + bool operator!=(const Packet& rhs) const { return !operator==(rhs); } + bool operator<(const Packet& rhs) const { + if (this->header.timestamp == rhs.header.timestamp) { + if (this->header.sequenceNumber == rhs.header.sequenceNumber) { + // Timestamp and sequence numbers are identical. A sync packet should + // be recognized "larger" (i.e. "later") compared to a "network packet" + // (regular packet from network not sync-packet). If none of the packets + // are sync-packets, then deem the left hand side to be "smaller" + // (i.e., "earlier") if it is primary, and right hand side is not. + // + // The condition on sync packets to be larger than "network packets," + // given same RTP sequence number and timestamp, guarantees that a + // "network packet" to be inserted in an earlier position into + // |packet_buffer_| compared to a sync packet of same timestamp and + // sequence number. + if (rhs.sync_packet) + return true; + if (this->sync_packet) + return false; + return (this->primary && !rhs.primary); + } + return (static_cast(rhs.header.sequenceNumber + - this->header.sequenceNumber) < 0xFFFF / 2); + } + return (static_cast(rhs.header.timestamp + - this->header.timestamp) < 0xFFFFFFFF / 2); + } + bool operator>(const Packet& rhs) const { return rhs.operator<(*this); } + bool operator<=(const Packet& rhs) const { return !operator>(rhs); } + bool operator>=(const Packet& rhs) const { return !operator<(rhs); } +}; + +// A list of packets. +typedef std::list PacketList; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_PACKET_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/payload_splitter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/payload_splitter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/payload_splitter.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/payload_splitter.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/payload_splitter.h" + +#include + +#include "webrtc/modules/audio_coding/neteq/decoder_database.h" + +namespace webrtc { + +// The method loops through a list of packets {A, B, C, ...}. Each packet is +// split into its corresponding RED payloads, {A1, A2, ...}, which is +// temporarily held in the list |new_packets|. +// When the first packet in |packet_list| has been processed, the orignal packet +// is replaced by the new ones in |new_packets|, so that |packet_list| becomes: +// {A1, A2, ..., B, C, ...}. The method then continues with B, and C, until all +// the original packets have been replaced by their split payloads. +int PayloadSplitter::SplitRed(PacketList* packet_list) { + int ret = kOK; + PacketList::iterator it = packet_list->begin(); + while (it != packet_list->end()) { + PacketList new_packets; // An empty list to store the split packets in. + Packet* red_packet = (*it); + assert(red_packet->payload); + uint8_t* payload_ptr = red_packet->payload; + + // Read RED headers (according to RFC 2198): + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |F| block PT | timestamp offset | block length | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // Last RED header: + // 0 1 2 3 4 5 6 7 + // +-+-+-+-+-+-+-+-+ + // |0| Block PT | + // +-+-+-+-+-+-+-+-+ + + bool last_block = false; + int sum_length = 0; + while (!last_block) { + Packet* new_packet = new Packet; + new_packet->header = red_packet->header; + // Check the F bit. If F == 0, this was the last block. + last_block = ((*payload_ptr & 0x80) == 0); + // Bits 1 through 7 are payload type. + new_packet->header.payloadType = payload_ptr[0] & 0x7F; + if (last_block) { + // No more header data to read. + ++sum_length; // Account for RED header size of 1 byte. + new_packet->payload_length = red_packet->payload_length - sum_length; + new_packet->primary = true; // Last block is always primary. + payload_ptr += 1; // Advance to first payload byte. + } else { + // Bits 8 through 21 are timestamp offset. + int timestamp_offset = (payload_ptr[1] << 6) + + ((payload_ptr[2] & 0xFC) >> 2); + new_packet->header.timestamp = red_packet->header.timestamp - + timestamp_offset; + // Bits 22 through 31 are payload length. + new_packet->payload_length = ((payload_ptr[2] & 0x03) << 8) + + payload_ptr[3]; + new_packet->primary = false; + payload_ptr += 4; // Advance to next RED header. + } + sum_length += new_packet->payload_length; + sum_length += 4; // Account for RED header size of 4 bytes. + // Store in new list of packets. + new_packets.push_back(new_packet); + } + + // Populate the new packets with payload data. + // |payload_ptr| now points at the first payload byte. + PacketList::iterator new_it; + for (new_it = new_packets.begin(); new_it != new_packets.end(); ++new_it) { + int payload_length = (*new_it)->payload_length; + if (payload_ptr + payload_length > + red_packet->payload + red_packet->payload_length) { + // The block lengths in the RED headers do not match the overall packet + // length. Something is corrupt. Discard this and the remaining + // payloads from this packet. + while (new_it != new_packets.end()) { + // Payload should not have been allocated yet. + assert(!(*new_it)->payload); + delete (*new_it); + new_it = new_packets.erase(new_it); + } + ret = kRedLengthMismatch; + break; + } + (*new_it)->payload = new uint8_t[payload_length]; + memcpy((*new_it)->payload, payload_ptr, payload_length); + payload_ptr += payload_length; + } + // Reverse the order of the new packets, so that the primary payload is + // always first. + new_packets.reverse(); + // Insert new packets into original list, before the element pointed to by + // iterator |it|. + packet_list->splice(it, new_packets, new_packets.begin(), + new_packets.end()); + // Delete old packet payload. + delete [] (*it)->payload; + delete (*it); + // Remove |it| from the packet list. This operation effectively moves the + // iterator |it| to the next packet in the list. Thus, we do not have to + // increment it manually. + it = packet_list->erase(it); + } + return ret; +} + +int PayloadSplitter::SplitFec(PacketList* packet_list, + DecoderDatabase* decoder_database) { + PacketList::iterator it = packet_list->begin(); + // Iterate through all packets in |packet_list|. + while (it != packet_list->end()) { + Packet* packet = (*it); // Just to make the notation more intuitive. + // Get codec type for this payload. + uint8_t payload_type = packet->header.payloadType; + const DecoderDatabase::DecoderInfo* info = + decoder_database->GetDecoderInfo(payload_type); + if (!info) { + return kUnknownPayloadType; + } + // No splitting for a sync-packet. + if (packet->sync_packet) { + ++it; + continue; + } + + // Not an FEC packet. + AudioDecoder* decoder = decoder_database->GetDecoder(payload_type); + // decoder should not return NULL. + assert(decoder != NULL); + if (!decoder || + !decoder->PacketHasFec(packet->payload, packet->payload_length)) { + ++it; + continue; + } + + switch (info->codec_type) { + case kDecoderOpus: + case kDecoderOpus_2ch: { + Packet* new_packet = new Packet; + + new_packet->header = packet->header; + int duration = decoder-> + PacketDurationRedundant(packet->payload, packet->payload_length); + new_packet->header.timestamp -= duration; + new_packet->payload = new uint8_t[packet->payload_length]; + memcpy(new_packet->payload, packet->payload, packet->payload_length); + new_packet->payload_length = packet->payload_length; + new_packet->primary = false; + new_packet->waiting_time = packet->waiting_time; + new_packet->sync_packet = packet->sync_packet; + + packet_list->insert(it, new_packet); + break; + } + default: { + return kFecSplitError; + } + } + + ++it; + } + return kOK; +} + +int PayloadSplitter::CheckRedPayloads(PacketList* packet_list, + const DecoderDatabase& decoder_database) { + PacketList::iterator it = packet_list->begin(); + int main_payload_type = -1; + int num_deleted_packets = 0; + while (it != packet_list->end()) { + uint8_t this_payload_type = (*it)->header.payloadType; + if (!decoder_database.IsDtmf(this_payload_type) && + !decoder_database.IsComfortNoise(this_payload_type)) { + if (main_payload_type == -1) { + // This is the first packet in the list which is non-DTMF non-CNG. + main_payload_type = this_payload_type; + } else { + if (this_payload_type != main_payload_type) { + // We do not allow redundant payloads of a different type. + // Discard this payload. + delete [] (*it)->payload; + delete (*it); + // Remove |it| from the packet list. This operation effectively + // moves the iterator |it| to the next packet in the list. Thus, we + // do not have to increment it manually. + it = packet_list->erase(it); + ++num_deleted_packets; + continue; + } + } + } + ++it; + } + return num_deleted_packets; +} + +int PayloadSplitter::SplitAudio(PacketList* packet_list, + const DecoderDatabase& decoder_database) { + PacketList::iterator it = packet_list->begin(); + // Iterate through all packets in |packet_list|. + while (it != packet_list->end()) { + Packet* packet = (*it); // Just to make the notation more intuitive. + // Get codec type for this payload. + const DecoderDatabase::DecoderInfo* info = + decoder_database.GetDecoderInfo(packet->header.payloadType); + if (!info) { + return kUnknownPayloadType; + } + // No splitting for a sync-packet. + if (packet->sync_packet) { + ++it; + continue; + } + PacketList new_packets; + switch (info->codec_type) { + case kDecoderPCMu: + case kDecoderPCMa: { + // 8 bytes per ms; 8 timestamps per ms. + SplitBySamples(packet, 8, 8, &new_packets); + break; + } + case kDecoderPCMu_2ch: + case kDecoderPCMa_2ch: { + // 2 * 8 bytes per ms; 8 timestamps per ms. + SplitBySamples(packet, 2 * 8, 8, &new_packets); + break; + } + case kDecoderG722: { + // 8 bytes per ms; 16 timestamps per ms. + SplitBySamples(packet, 8, 16, &new_packets); + break; + } + case kDecoderPCM16B: { + // 16 bytes per ms; 8 timestamps per ms. + SplitBySamples(packet, 16, 8, &new_packets); + break; + } + case kDecoderPCM16Bwb: { + // 32 bytes per ms; 16 timestamps per ms. + SplitBySamples(packet, 32, 16, &new_packets); + break; + } + case kDecoderPCM16Bswb32kHz: { + // 64 bytes per ms; 32 timestamps per ms. + SplitBySamples(packet, 64, 32, &new_packets); + break; + } + case kDecoderPCM16Bswb48kHz: { + // 96 bytes per ms; 48 timestamps per ms. + SplitBySamples(packet, 96, 48, &new_packets); + break; + } + case kDecoderPCM16B_2ch: { + // 2 * 16 bytes per ms; 8 timestamps per ms. + SplitBySamples(packet, 2 * 16, 8, &new_packets); + break; + } + case kDecoderPCM16Bwb_2ch: { + // 2 * 32 bytes per ms; 16 timestamps per ms. + SplitBySamples(packet, 2 * 32, 16, &new_packets); + break; + } + case kDecoderPCM16Bswb32kHz_2ch: { + // 2 * 64 bytes per ms; 32 timestamps per ms. + SplitBySamples(packet, 2 * 64, 32, &new_packets); + break; + } + case kDecoderPCM16Bswb48kHz_2ch: { + // 2 * 96 bytes per ms; 48 timestamps per ms. + SplitBySamples(packet, 2 * 96, 48, &new_packets); + break; + } + case kDecoderPCM16B_5ch: { + // 5 * 16 bytes per ms; 8 timestamps per ms. + SplitBySamples(packet, 5 * 16, 8, &new_packets); + break; + } + case kDecoderILBC: { + int bytes_per_frame; + int timestamps_per_frame; + if (packet->payload_length >= 950) { + return kTooLargePayload; + } else if (packet->payload_length % 38 == 0) { + // 20 ms frames. + bytes_per_frame = 38; + timestamps_per_frame = 160; + } else if (packet->payload_length % 50 == 0) { + // 30 ms frames. + bytes_per_frame = 50; + timestamps_per_frame = 240; + } else { + return kFrameSplitError; + } + int ret = SplitByFrames(packet, bytes_per_frame, timestamps_per_frame, + &new_packets); + if (ret < 0) { + return ret; + } else if (ret == kNoSplit) { + // Do not split at all. Simply advance to the next packet in the list. + ++it; + // We do not have any new packets to insert, and should not delete the + // old one. Skip the code after the switch case, and jump straight to + // the next packet in the while loop. + continue; + } + break; + } + default: { + // Do not split at all. Simply advance to the next packet in the list. + ++it; + // We do not have any new packets to insert, and should not delete the + // old one. Skip the code after the switch case, and jump straight to + // the next packet in the while loop. + continue; + } + } + // Insert new packets into original list, before the element pointed to by + // iterator |it|. + packet_list->splice(it, new_packets, new_packets.begin(), + new_packets.end()); + // Delete old packet payload. + delete [] (*it)->payload; + delete (*it); + // Remove |it| from the packet list. This operation effectively moves the + // iterator |it| to the next packet in the list. Thus, we do not have to + // increment it manually. + it = packet_list->erase(it); + } + return kOK; +} + +void PayloadSplitter::SplitBySamples(const Packet* packet, + int bytes_per_ms, + int timestamps_per_ms, + PacketList* new_packets) { + assert(packet); + assert(new_packets); + + int split_size_bytes = packet->payload_length; + + // Find a "chunk size" >= 20 ms and < 40 ms. + int min_chunk_size = bytes_per_ms * 20; + // Reduce the split size by half as long as |split_size_bytes| is at least + // twice the minimum chunk size (so that the resulting size is at least as + // large as the minimum chunk size). + while (split_size_bytes >= 2 * min_chunk_size) { + split_size_bytes >>= 1; + } + int timestamps_per_chunk = + split_size_bytes * timestamps_per_ms / bytes_per_ms; + uint32_t timestamp = packet->header.timestamp; + + uint8_t* payload_ptr = packet->payload; + int len = packet->payload_length; + while (len >= (2 * split_size_bytes)) { + Packet* new_packet = new Packet; + new_packet->payload_length = split_size_bytes; + new_packet->header = packet->header; + new_packet->header.timestamp = timestamp; + timestamp += timestamps_per_chunk; + new_packet->primary = packet->primary; + new_packet->payload = new uint8_t[split_size_bytes]; + memcpy(new_packet->payload, payload_ptr, split_size_bytes); + payload_ptr += split_size_bytes; + new_packets->push_back(new_packet); + len -= split_size_bytes; + } + + if (len > 0) { + Packet* new_packet = new Packet; + new_packet->payload_length = len; + new_packet->header = packet->header; + new_packet->header.timestamp = timestamp; + new_packet->primary = packet->primary; + new_packet->payload = new uint8_t[len]; + memcpy(new_packet->payload, payload_ptr, len); + new_packets->push_back(new_packet); + } +} + +int PayloadSplitter::SplitByFrames(const Packet* packet, + int bytes_per_frame, + int timestamps_per_frame, + PacketList* new_packets) { + if (packet->payload_length % bytes_per_frame != 0) { + return kFrameSplitError; + } + + int num_frames = packet->payload_length / bytes_per_frame; + if (num_frames == 1) { + // Special case. Do not split the payload. + return kNoSplit; + } + + uint32_t timestamp = packet->header.timestamp; + uint8_t* payload_ptr = packet->payload; + int len = packet->payload_length; + while (len > 0) { + assert(len >= bytes_per_frame); + Packet* new_packet = new Packet; + new_packet->payload_length = bytes_per_frame; + new_packet->header = packet->header; + new_packet->header.timestamp = timestamp; + timestamp += timestamps_per_frame; + new_packet->primary = packet->primary; + new_packet->payload = new uint8_t[bytes_per_frame]; + memcpy(new_packet->payload, payload_ptr, bytes_per_frame); + payload_ptr += bytes_per_frame; + new_packets->push_back(new_packet); + len -= bytes_per_frame; + } + return kOK; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/payload_splitter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/payload_splitter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/payload_splitter.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/payload_splitter.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_PAYLOAD_SPLITTER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_PAYLOAD_SPLITTER_H_ + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/packet.h" + +namespace webrtc { + +// Forward declarations. +class DecoderDatabase; + +// This class handles splitting of payloads into smaller parts. +// The class does not have any member variables, and the methods could have +// been made static. The reason for not making them static is testability. +// With this design, the splitting functionality can be mocked during testing +// of the NetEqImpl class. +class PayloadSplitter { + public: + enum SplitterReturnCodes { + kOK = 0, + kNoSplit = 1, + kTooLargePayload = -1, + kFrameSplitError = -2, + kUnknownPayloadType = -3, + kRedLengthMismatch = -4, + kFecSplitError = -5, + }; + + PayloadSplitter() {} + + virtual ~PayloadSplitter() {} + + // Splits each packet in |packet_list| into its separate RED payloads. Each + // RED payload is packetized into a Packet. The original elements in + // |packet_list| are properly deleted, and replaced by the new packets. + // Note that all packets in |packet_list| must be RED payloads, i.e., have + // RED headers according to RFC 2198 at the very beginning of the payload. + // Returns kOK or an error. + virtual int SplitRed(PacketList* packet_list); + + // Iterates through |packet_list| and, duplicate each audio payload that has + // FEC as new packet for redundant decoding. The decoder database is needed to + // get information about which payload type each packet contains. + virtual int SplitFec(PacketList* packet_list, + DecoderDatabase* decoder_database); + + // Checks all packets in |packet_list|. Packets that are DTMF events or + // comfort noise payloads are kept. Except that, only one single payload type + // is accepted. Any packet with another payload type is discarded. + virtual int CheckRedPayloads(PacketList* packet_list, + const DecoderDatabase& decoder_database); + + // Iterates through |packet_list| and, if possible, splits each audio payload + // into suitable size chunks. The result is written back to |packet_list| as + // new packets. The decoder database is needed to get information about which + // payload type each packet contains. + virtual int SplitAudio(PacketList* packet_list, + const DecoderDatabase& decoder_database); + + private: + // Splits the payload in |packet|. The payload is assumed to be from a + // sample-based codec. + virtual void SplitBySamples(const Packet* packet, + int bytes_per_ms, + int timestamps_per_ms, + PacketList* new_packets); + + // Splits the payload in |packet|. The payload will be split into chunks of + // size |bytes_per_frame|, corresponding to a |timestamps_per_frame| + // RTP timestamps. + virtual int SplitByFrames(const Packet* packet, + int bytes_per_frame, + int timestamps_per_frame, + PacketList* new_packets); + + DISALLOW_COPY_AND_ASSIGN(PayloadSplitter); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_PAYLOAD_SPLITTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/payload_splitter_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/payload_splitter_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/payload_splitter_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/payload_splitter_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,777 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for PayloadSplitter class. + +#include "webrtc/modules/audio_coding/neteq/payload_splitter.h" + +#include + +#include // pair + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/neteq/mock/mock_decoder_database.h" +#include "webrtc/modules/audio_coding/neteq/packet.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +using ::testing::Return; +using ::testing::ReturnNull; + +namespace webrtc { + +static const int kRedPayloadType = 100; +static const int kPayloadLength = 10; +static const int kRedHeaderLength = 4; // 4 bytes RED header. +static const uint16_t kSequenceNumber = 0; +static const uint32_t kBaseTimestamp = 0x12345678; + +// RED headers (according to RFC 2198): +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |F| block PT | timestamp offset | block length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// Last RED header: +// 0 1 2 3 4 5 6 7 +// +-+-+-+-+-+-+-+-+ +// |0| Block PT | +// +-+-+-+-+-+-+-+-+ + +// Creates a RED packet, with |num_payloads| payloads, with payload types given +// by the values in array |payload_types| (which must be of length +// |num_payloads|). Each redundant payload is |timestamp_offset| samples +// "behind" the the previous payload. +Packet* CreateRedPayload(int num_payloads, + uint8_t* payload_types, + int timestamp_offset) { + Packet* packet = new Packet; + packet->header.payloadType = kRedPayloadType; + packet->header.timestamp = kBaseTimestamp; + packet->header.sequenceNumber = kSequenceNumber; + packet->payload_length = (kPayloadLength + 1) + + (num_payloads - 1) * (kPayloadLength + kRedHeaderLength); + uint8_t* payload = new uint8_t[packet->payload_length]; + uint8_t* payload_ptr = payload; + for (int i = 0; i < num_payloads; ++i) { + // Write the RED headers. + if (i == num_payloads - 1) { + // Special case for last payload. + *payload_ptr = payload_types[i] & 0x7F; // F = 0; + ++payload_ptr; + break; + } + *payload_ptr = payload_types[i] & 0x7F; + // Not the last block; set F = 1. + *payload_ptr |= 0x80; + ++payload_ptr; + int this_offset = (num_payloads - i - 1) * timestamp_offset; + *payload_ptr = this_offset >> 6; + ++payload_ptr; + assert(kPayloadLength <= 1023); // Max length described by 10 bits. + *payload_ptr = ((this_offset & 0x3F) << 2) | (kPayloadLength >> 8); + ++payload_ptr; + *payload_ptr = kPayloadLength & 0xFF; + ++payload_ptr; + } + for (int i = 0; i < num_payloads; ++i) { + // Write |i| to all bytes in each payload. + memset(payload_ptr, i, kPayloadLength); + payload_ptr += kPayloadLength; + } + packet->payload = payload; + return packet; +} + + +// A possible Opus packet that contains FEC is the following. +// The frame is 20 ms in duration. +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |0|0|0|0|1|0|0|0|x|1|x|x|x|x|x|x|x| | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | +// | Compressed frame 1 (N-2 bytes)... : +// : | +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +Packet* CreateOpusFecPacket(uint8_t payload_type, int payload_length, + uint8_t payload_value) { + Packet* packet = new Packet; + packet->header.payloadType = payload_type; + packet->header.timestamp = kBaseTimestamp; + packet->header.sequenceNumber = kSequenceNumber; + packet->payload_length = payload_length; + uint8_t* payload = new uint8_t[packet->payload_length]; + payload[0] = 0x08; + payload[1] = 0x40; + memset(&payload[2], payload_value, payload_length - 2); + packet->payload = payload; + return packet; +} + +// Create a packet with all payload bytes set to |payload_value|. +Packet* CreatePacket(uint8_t payload_type, int payload_length, + uint8_t payload_value) { + Packet* packet = new Packet; + packet->header.payloadType = payload_type; + packet->header.timestamp = kBaseTimestamp; + packet->header.sequenceNumber = kSequenceNumber; + packet->payload_length = payload_length; + uint8_t* payload = new uint8_t[packet->payload_length]; + memset(payload, payload_value, payload_length); + packet->payload = payload; + return packet; +} + +// Checks that |packet| has the attributes given in the remaining parameters. +void VerifyPacket(const Packet* packet, + int payload_length, + uint8_t payload_type, + uint16_t sequence_number, + uint32_t timestamp, + uint8_t payload_value, + bool primary = true) { + EXPECT_EQ(payload_length, packet->payload_length); + EXPECT_EQ(payload_type, packet->header.payloadType); + EXPECT_EQ(sequence_number, packet->header.sequenceNumber); + EXPECT_EQ(timestamp, packet->header.timestamp); + EXPECT_EQ(primary, packet->primary); + ASSERT_FALSE(packet->payload == NULL); + for (int i = 0; i < packet->payload_length; ++i) { + EXPECT_EQ(payload_value, packet->payload[i]); + } +} + +// Start of test definitions. + +TEST(PayloadSplitter, CreateAndDestroy) { + PayloadSplitter* splitter = new PayloadSplitter; + delete splitter; +} + +// Packet A is split into A1 and A2. +TEST(RedPayloadSplitter, OnePacketTwoPayloads) { + uint8_t payload_types[] = {0, 0}; + const int kTimestampOffset = 160; + Packet* packet = CreateRedPayload(2, payload_types, kTimestampOffset); + PacketList packet_list; + packet_list.push_back(packet); + PayloadSplitter splitter; + EXPECT_EQ(PayloadSplitter::kOK, splitter.SplitRed(&packet_list)); + ASSERT_EQ(2u, packet_list.size()); + // Check first packet. The first in list should always be the primary payload. + packet = packet_list.front(); + VerifyPacket(packet, kPayloadLength, payload_types[1], kSequenceNumber, + kBaseTimestamp, 1, true); + delete [] packet->payload; + delete packet; + packet_list.pop_front(); + // Check second packet. + packet = packet_list.front(); + VerifyPacket(packet, kPayloadLength, payload_types[0], kSequenceNumber, + kBaseTimestamp - kTimestampOffset, 0, false); + delete [] packet->payload; + delete packet; +} + +// Packets A and B are not split at all. Only the RED header in each packet is +// removed. +TEST(RedPayloadSplitter, TwoPacketsOnePayload) { + uint8_t payload_types[] = {0}; + const int kTimestampOffset = 160; + // Create first packet, with a single RED payload. + Packet* packet = CreateRedPayload(1, payload_types, kTimestampOffset); + PacketList packet_list; + packet_list.push_back(packet); + // Create second packet, with a single RED payload. + packet = CreateRedPayload(1, payload_types, kTimestampOffset); + // Manually change timestamp and sequence number of second packet. + packet->header.timestamp += kTimestampOffset; + packet->header.sequenceNumber++; + packet_list.push_back(packet); + PayloadSplitter splitter; + EXPECT_EQ(PayloadSplitter::kOK, splitter.SplitRed(&packet_list)); + ASSERT_EQ(2u, packet_list.size()); + // Check first packet. + packet = packet_list.front(); + VerifyPacket(packet, kPayloadLength, payload_types[0], kSequenceNumber, + kBaseTimestamp, 0, true); + delete [] packet->payload; + delete packet; + packet_list.pop_front(); + // Check second packet. + packet = packet_list.front(); + VerifyPacket(packet, kPayloadLength, payload_types[0], kSequenceNumber + 1, + kBaseTimestamp + kTimestampOffset, 0, true); + delete [] packet->payload; + delete packet; +} + +// Packets A and B are split into packets A1, A2, A3, B1, B2, B3, with +// attributes as follows: +// +// A1* A2 A3 B1* B2 B3 +// Payload type 0 1 2 0 1 2 +// Timestamp b b-o b-2o b+o b b-o +// Sequence number 0 0 0 1 1 1 +// +// b = kBaseTimestamp, o = kTimestampOffset, * = primary. +TEST(RedPayloadSplitter, TwoPacketsThreePayloads) { + uint8_t payload_types[] = {2, 1, 0}; // Primary is the last one. + const int kTimestampOffset = 160; + // Create first packet, with 3 RED payloads. + Packet* packet = CreateRedPayload(3, payload_types, kTimestampOffset); + PacketList packet_list; + packet_list.push_back(packet); + // Create first packet, with 3 RED payloads. + packet = CreateRedPayload(3, payload_types, kTimestampOffset); + // Manually change timestamp and sequence number of second packet. + packet->header.timestamp += kTimestampOffset; + packet->header.sequenceNumber++; + packet_list.push_back(packet); + PayloadSplitter splitter; + EXPECT_EQ(PayloadSplitter::kOK, splitter.SplitRed(&packet_list)); + ASSERT_EQ(6u, packet_list.size()); + // Check first packet, A1. + packet = packet_list.front(); + VerifyPacket(packet, kPayloadLength, payload_types[2], kSequenceNumber, + kBaseTimestamp, 2, true); + delete [] packet->payload; + delete packet; + packet_list.pop_front(); + // Check second packet, A2. + packet = packet_list.front(); + VerifyPacket(packet, kPayloadLength, payload_types[1], kSequenceNumber, + kBaseTimestamp - kTimestampOffset, 1, false); + delete [] packet->payload; + delete packet; + packet_list.pop_front(); + // Check third packet, A3. + packet = packet_list.front(); + VerifyPacket(packet, kPayloadLength, payload_types[0], kSequenceNumber, + kBaseTimestamp - 2 * kTimestampOffset, 0, false); + delete [] packet->payload; + delete packet; + packet_list.pop_front(); + // Check fourth packet, B1. + packet = packet_list.front(); + VerifyPacket(packet, kPayloadLength, payload_types[2], kSequenceNumber + 1, + kBaseTimestamp + kTimestampOffset, 2, true); + delete [] packet->payload; + delete packet; + packet_list.pop_front(); + // Check fifth packet, B2. + packet = packet_list.front(); + VerifyPacket(packet, kPayloadLength, payload_types[1], kSequenceNumber + 1, + kBaseTimestamp, 1, false); + delete [] packet->payload; + delete packet; + packet_list.pop_front(); + // Check sixth packet, B3. + packet = packet_list.front(); + VerifyPacket(packet, kPayloadLength, payload_types[0], kSequenceNumber + 1, + kBaseTimestamp - kTimestampOffset, 0, false); + delete [] packet->payload; + delete packet; +} + +// Creates a list with 4 packets with these payload types: +// 0 = CNGnb +// 1 = PCMu +// 2 = DTMF (AVT) +// 3 = iLBC +// We expect the method CheckRedPayloads to discard the iLBC packet, since it +// is a non-CNG, non-DTMF payload of another type than the first speech payload +// found in the list (which is PCMu). +TEST(RedPayloadSplitter, CheckRedPayloads) { + PacketList packet_list; + for (int i = 0; i <= 3; ++i) { + // Create packet with payload type |i|, payload length 10 bytes, all 0. + Packet* packet = CreatePacket(i, 10, 0); + packet_list.push_back(packet); + } + + // Use a real DecoderDatabase object here instead of a mock, since it is + // easier to just register the payload types and let the actual implementation + // do its job. + DecoderDatabase decoder_database; + decoder_database.RegisterPayload(0, kDecoderCNGnb); + decoder_database.RegisterPayload(1, kDecoderPCMu); + decoder_database.RegisterPayload(2, kDecoderAVT); + decoder_database.RegisterPayload(3, kDecoderILBC); + + PayloadSplitter splitter; + splitter.CheckRedPayloads(&packet_list, decoder_database); + + ASSERT_EQ(3u, packet_list.size()); // Should have dropped the last packet. + // Verify packets. The loop verifies that payload types 0, 1, and 2 are in the + // list. + for (int i = 0; i <= 2; ++i) { + Packet* packet = packet_list.front(); + VerifyPacket(packet, 10, i, kSequenceNumber, kBaseTimestamp, 0, true); + delete [] packet->payload; + delete packet; + packet_list.pop_front(); + } + EXPECT_TRUE(packet_list.empty()); +} + +// Packet A is split into A1, A2 and A3. But the length parameter is off, so +// the last payloads should be discarded. +TEST(RedPayloadSplitter, WrongPayloadLength) { + uint8_t payload_types[] = {0, 0, 0}; + const int kTimestampOffset = 160; + Packet* packet = CreateRedPayload(3, payload_types, kTimestampOffset); + // Manually tamper with the payload length of the packet. + // This is one byte too short for the second payload (out of three). + // We expect only the first payload to be returned. + packet->payload_length -= kPayloadLength + 1; + PacketList packet_list; + packet_list.push_back(packet); + PayloadSplitter splitter; + EXPECT_EQ(PayloadSplitter::kRedLengthMismatch, + splitter.SplitRed(&packet_list)); + ASSERT_EQ(1u, packet_list.size()); + // Check first packet. + packet = packet_list.front(); + VerifyPacket(packet, kPayloadLength, payload_types[0], kSequenceNumber, + kBaseTimestamp - 2 * kTimestampOffset, 0, false); + delete [] packet->payload; + delete packet; + packet_list.pop_front(); +} + +// Test that iSAC, iSAC-swb, RED, DTMF, CNG, and "Arbitrary" payloads do not +// get split. +TEST(AudioPayloadSplitter, NonSplittable) { + // Set up packets with different RTP payload types. The actual values do not + // matter, since we are mocking the decoder database anyway. + PacketList packet_list; + for (int i = 0; i < 6; ++i) { + // Let the payload type be |i|, and the payload value 10 * |i|. + packet_list.push_back(CreatePacket(i, kPayloadLength, 10 * i)); + } + + MockDecoderDatabase decoder_database; + // Tell the mock decoder database to return DecoderInfo structs with different + // codec types. + // Use scoped pointers to avoid having to delete them later. + scoped_ptr info0( + new DecoderDatabase::DecoderInfo(kDecoderISAC, 16000, NULL, false)); + EXPECT_CALL(decoder_database, GetDecoderInfo(0)) + .WillRepeatedly(Return(info0.get())); + scoped_ptr info1( + new DecoderDatabase::DecoderInfo(kDecoderISACswb, 32000, NULL, false)); + EXPECT_CALL(decoder_database, GetDecoderInfo(1)) + .WillRepeatedly(Return(info1.get())); + scoped_ptr info2( + new DecoderDatabase::DecoderInfo(kDecoderRED, 8000, NULL, false)); + EXPECT_CALL(decoder_database, GetDecoderInfo(2)) + .WillRepeatedly(Return(info2.get())); + scoped_ptr info3( + new DecoderDatabase::DecoderInfo(kDecoderAVT, 8000, NULL, false)); + EXPECT_CALL(decoder_database, GetDecoderInfo(3)) + .WillRepeatedly(Return(info3.get())); + scoped_ptr info4( + new DecoderDatabase::DecoderInfo(kDecoderCNGnb, 8000, NULL, false)); + EXPECT_CALL(decoder_database, GetDecoderInfo(4)) + .WillRepeatedly(Return(info4.get())); + scoped_ptr info5( + new DecoderDatabase::DecoderInfo(kDecoderArbitrary, 8000, NULL, false)); + EXPECT_CALL(decoder_database, GetDecoderInfo(5)) + .WillRepeatedly(Return(info5.get())); + + PayloadSplitter splitter; + EXPECT_EQ(0, splitter.SplitAudio(&packet_list, decoder_database)); + EXPECT_EQ(6u, packet_list.size()); + + // Check that all payloads are intact. + uint8_t payload_type = 0; + PacketList::iterator it = packet_list.begin(); + while (it != packet_list.end()) { + VerifyPacket((*it), kPayloadLength, payload_type, kSequenceNumber, + kBaseTimestamp, 10 * payload_type); + ++payload_type; + delete [] (*it)->payload; + delete (*it); + it = packet_list.erase(it); + } + + // The destructor is called when decoder_database goes out of scope. + EXPECT_CALL(decoder_database, Die()); +} + +// Test unknown payload type. +TEST(AudioPayloadSplitter, UnknownPayloadType) { + PacketList packet_list; + static const uint8_t kPayloadType = 17; // Just a random number. + int kPayloadLengthBytes = 4711; // Random number. + packet_list.push_back(CreatePacket(kPayloadType, kPayloadLengthBytes, 0)); + + MockDecoderDatabase decoder_database; + // Tell the mock decoder database to return NULL when asked for decoder info. + // This signals that the decoder database does not recognize the payload type. + EXPECT_CALL(decoder_database, GetDecoderInfo(kPayloadType)) + .WillRepeatedly(ReturnNull()); + + PayloadSplitter splitter; + EXPECT_EQ(PayloadSplitter::kUnknownPayloadType, + splitter.SplitAudio(&packet_list, decoder_database)); + EXPECT_EQ(1u, packet_list.size()); + + + // Delete the packets and payloads to avoid having the test leak memory. + PacketList::iterator it = packet_list.begin(); + while (it != packet_list.end()) { + delete [] (*it)->payload; + delete (*it); + it = packet_list.erase(it); + } + + // The destructor is called when decoder_database goes out of scope. + EXPECT_CALL(decoder_database, Die()); +} + +class SplitBySamplesTest : public ::testing::TestWithParam { + protected: + virtual void SetUp() { + decoder_type_ = GetParam(); + switch (decoder_type_) { + case kDecoderPCMu: + case kDecoderPCMa: + bytes_per_ms_ = 8; + samples_per_ms_ = 8; + break; + case kDecoderPCMu_2ch: + case kDecoderPCMa_2ch: + bytes_per_ms_ = 2 * 8; + samples_per_ms_ = 8; + break; + case kDecoderG722: + bytes_per_ms_ = 8; + samples_per_ms_ = 16; + break; + case kDecoderPCM16B: + bytes_per_ms_ = 16; + samples_per_ms_ = 8; + break; + case kDecoderPCM16Bwb: + bytes_per_ms_ = 32; + samples_per_ms_ = 16; + break; + case kDecoderPCM16Bswb32kHz: + bytes_per_ms_ = 64; + samples_per_ms_ = 32; + break; + case kDecoderPCM16Bswb48kHz: + bytes_per_ms_ = 96; + samples_per_ms_ = 48; + break; + case kDecoderPCM16B_2ch: + bytes_per_ms_ = 2 * 16; + samples_per_ms_ = 8; + break; + case kDecoderPCM16Bwb_2ch: + bytes_per_ms_ = 2 * 32; + samples_per_ms_ = 16; + break; + case kDecoderPCM16Bswb32kHz_2ch: + bytes_per_ms_ = 2 * 64; + samples_per_ms_ = 32; + break; + case kDecoderPCM16Bswb48kHz_2ch: + bytes_per_ms_ = 2 * 96; + samples_per_ms_ = 48; + break; + case kDecoderPCM16B_5ch: + bytes_per_ms_ = 5 * 16; + samples_per_ms_ = 8; + break; + default: + assert(false); + break; + } + } + int bytes_per_ms_; + int samples_per_ms_; + NetEqDecoder decoder_type_; +}; + +// Test splitting sample-based payloads. +TEST_P(SplitBySamplesTest, PayloadSizes) { + PacketList packet_list; + static const uint8_t kPayloadType = 17; // Just a random number. + for (int payload_size_ms = 10; payload_size_ms <= 60; payload_size_ms += 10) { + // The payload values are set to be the same as the payload_size, so that + // one can distinguish from which packet the split payloads come from. + int payload_size_bytes = payload_size_ms * bytes_per_ms_; + packet_list.push_back(CreatePacket(kPayloadType, payload_size_bytes, + payload_size_ms)); + } + + MockDecoderDatabase decoder_database; + // Tell the mock decoder database to return DecoderInfo structs with different + // codec types. + // Use scoped pointers to avoid having to delete them later. + // (Sample rate is set to 8000 Hz, but does not matter.) + scoped_ptr info( + new DecoderDatabase::DecoderInfo(decoder_type_, 8000, NULL, false)); + EXPECT_CALL(decoder_database, GetDecoderInfo(kPayloadType)) + .WillRepeatedly(Return(info.get())); + + PayloadSplitter splitter; + EXPECT_EQ(0, splitter.SplitAudio(&packet_list, decoder_database)); + // The payloads are expected to be split as follows: + // 10 ms -> 10 ms + // 20 ms -> 20 ms + // 30 ms -> 30 ms + // 40 ms -> 20 + 20 ms + // 50 ms -> 25 + 25 ms + // 60 ms -> 30 + 30 ms + int expected_size_ms[] = {10, 20, 30, 20, 20, 25, 25, 30, 30}; + int expected_payload_value[] = {10, 20, 30, 40, 40, 50, 50, 60, 60}; + int expected_timestamp_offset_ms[] = {0, 0, 0, 0, 20, 0, 25, 0, 30}; + size_t expected_num_packets = + sizeof(expected_size_ms) / sizeof(expected_size_ms[0]); + EXPECT_EQ(expected_num_packets, packet_list.size()); + + PacketList::iterator it = packet_list.begin(); + int i = 0; + while (it != packet_list.end()) { + int length_bytes = expected_size_ms[i] * bytes_per_ms_; + uint32_t expected_timestamp = kBaseTimestamp + + expected_timestamp_offset_ms[i] * samples_per_ms_; + VerifyPacket((*it), length_bytes, kPayloadType, kSequenceNumber, + expected_timestamp, expected_payload_value[i]); + delete [] (*it)->payload; + delete (*it); + it = packet_list.erase(it); + ++i; + } + + // The destructor is called when decoder_database goes out of scope. + EXPECT_CALL(decoder_database, Die()); +} + +INSTANTIATE_TEST_CASE_P( + PayloadSplitter, SplitBySamplesTest, + ::testing::Values(kDecoderPCMu, kDecoderPCMa, kDecoderPCMu_2ch, + kDecoderPCMa_2ch, kDecoderG722, kDecoderPCM16B, + kDecoderPCM16Bwb, kDecoderPCM16Bswb32kHz, + kDecoderPCM16Bswb48kHz, kDecoderPCM16B_2ch, + kDecoderPCM16Bwb_2ch, kDecoderPCM16Bswb32kHz_2ch, + kDecoderPCM16Bswb48kHz_2ch, kDecoderPCM16B_5ch)); + + +class SplitIlbcTest : public ::testing::TestWithParam > { + protected: + virtual void SetUp() { + const std::pair parameters = GetParam(); + num_frames_ = parameters.first; + frame_length_ms_ = parameters.second; + frame_length_bytes_ = (frame_length_ms_ == 20) ? 38 : 50; + } + size_t num_frames_; + int frame_length_ms_; + int frame_length_bytes_; +}; + +// Test splitting sample-based payloads. +TEST_P(SplitIlbcTest, NumFrames) { + PacketList packet_list; + static const uint8_t kPayloadType = 17; // Just a random number. + const int frame_length_samples = frame_length_ms_ * 8; + int payload_length_bytes = frame_length_bytes_ * num_frames_; + Packet* packet = CreatePacket(kPayloadType, payload_length_bytes, 0); + // Fill payload with increasing integers {0, 1, 2, ...}. + for (int i = 0; i < packet->payload_length; ++i) { + packet->payload[i] = static_cast(i); + } + packet_list.push_back(packet); + + MockDecoderDatabase decoder_database; + // Tell the mock decoder database to return DecoderInfo structs with different + // codec types. + // Use scoped pointers to avoid having to delete them later. + scoped_ptr info( + new DecoderDatabase::DecoderInfo(kDecoderILBC, 8000, NULL, false)); + EXPECT_CALL(decoder_database, GetDecoderInfo(kPayloadType)) + .WillRepeatedly(Return(info.get())); + + PayloadSplitter splitter; + EXPECT_EQ(0, splitter.SplitAudio(&packet_list, decoder_database)); + EXPECT_EQ(num_frames_, packet_list.size()); + + PacketList::iterator it = packet_list.begin(); + int frame_num = 0; + uint8_t payload_value = 0; + while (it != packet_list.end()) { + Packet* packet = (*it); + EXPECT_EQ(kBaseTimestamp + frame_length_samples * frame_num, + packet->header.timestamp); + EXPECT_EQ(frame_length_bytes_, packet->payload_length); + EXPECT_EQ(kPayloadType, packet->header.payloadType); + EXPECT_EQ(kSequenceNumber, packet->header.sequenceNumber); + EXPECT_EQ(true, packet->primary); + ASSERT_FALSE(packet->payload == NULL); + for (int i = 0; i < packet->payload_length; ++i) { + EXPECT_EQ(payload_value, packet->payload[i]); + ++payload_value; + } + delete [] (*it)->payload; + delete (*it); + it = packet_list.erase(it); + ++frame_num; + } + + // The destructor is called when decoder_database goes out of scope. + EXPECT_CALL(decoder_database, Die()); +} + +// Test 1 through 5 frames of 20 and 30 ms size. +// Also test the maximum number of frames in one packet for 20 and 30 ms. +// The maximum is defined by the largest payload length that can be uniquely +// resolved to a frame size of either 38 bytes (20 ms) or 50 bytes (30 ms). +INSTANTIATE_TEST_CASE_P( + PayloadSplitter, SplitIlbcTest, + ::testing::Values(std::pair(1, 20), // 1 frame, 20 ms. + std::pair(2, 20), // 2 frames, 20 ms. + std::pair(3, 20), // And so on. + std::pair(4, 20), + std::pair(5, 20), + std::pair(24, 20), + std::pair(1, 30), + std::pair(2, 30), + std::pair(3, 30), + std::pair(4, 30), + std::pair(5, 30), + std::pair(18, 30))); + +// Test too large payload size. +TEST(IlbcPayloadSplitter, TooLargePayload) { + PacketList packet_list; + static const uint8_t kPayloadType = 17; // Just a random number. + int kPayloadLengthBytes = 950; + Packet* packet = CreatePacket(kPayloadType, kPayloadLengthBytes, 0); + packet_list.push_back(packet); + + MockDecoderDatabase decoder_database; + scoped_ptr info( + new DecoderDatabase::DecoderInfo(kDecoderILBC, 8000, NULL, false)); + EXPECT_CALL(decoder_database, GetDecoderInfo(kPayloadType)) + .WillRepeatedly(Return(info.get())); + + PayloadSplitter splitter; + EXPECT_EQ(PayloadSplitter::kTooLargePayload, + splitter.SplitAudio(&packet_list, decoder_database)); + EXPECT_EQ(1u, packet_list.size()); + + // Delete the packets and payloads to avoid having the test leak memory. + PacketList::iterator it = packet_list.begin(); + while (it != packet_list.end()) { + delete [] (*it)->payload; + delete (*it); + it = packet_list.erase(it); + } + + // The destructor is called when decoder_database goes out of scope. + EXPECT_CALL(decoder_database, Die()); +} + +// Payload not an integer number of frames. +TEST(IlbcPayloadSplitter, UnevenPayload) { + PacketList packet_list; + static const uint8_t kPayloadType = 17; // Just a random number. + int kPayloadLengthBytes = 39; // Not an even number of frames. + Packet* packet = CreatePacket(kPayloadType, kPayloadLengthBytes, 0); + packet_list.push_back(packet); + + MockDecoderDatabase decoder_database; + scoped_ptr info( + new DecoderDatabase::DecoderInfo(kDecoderILBC, 8000, NULL, false)); + EXPECT_CALL(decoder_database, GetDecoderInfo(kPayloadType)) + .WillRepeatedly(Return(info.get())); + + PayloadSplitter splitter; + EXPECT_EQ(PayloadSplitter::kFrameSplitError, + splitter.SplitAudio(&packet_list, decoder_database)); + EXPECT_EQ(1u, packet_list.size()); + + // Delete the packets and payloads to avoid having the test leak memory. + PacketList::iterator it = packet_list.begin(); + while (it != packet_list.end()) { + delete [] (*it)->payload; + delete (*it); + it = packet_list.erase(it); + } + + // The destructor is called when decoder_database goes out of scope. + EXPECT_CALL(decoder_database, Die()); +} + +TEST(FecPayloadSplitter, MixedPayload) { + PacketList packet_list; + DecoderDatabase decoder_database; + + decoder_database.RegisterPayload(0, kDecoderOpus); + decoder_database.RegisterPayload(1, kDecoderPCMu); + + Packet* packet = CreateOpusFecPacket(0, 10, 0xFF); + packet_list.push_back(packet); + + packet = CreatePacket(0, 10, 0); // Non-FEC Opus payload. + packet_list.push_back(packet); + + packet = CreatePacket(1, 10, 0); // Non-Opus payload. + packet_list.push_back(packet); + + PayloadSplitter splitter; + EXPECT_EQ(PayloadSplitter::kOK, + splitter.SplitFec(&packet_list, &decoder_database)); + EXPECT_EQ(4u, packet_list.size()); + + // Check first packet. + packet = packet_list.front(); + EXPECT_EQ(0, packet->header.payloadType); + EXPECT_EQ(kBaseTimestamp - 20 * 48, packet->header.timestamp); + EXPECT_EQ(10, packet->payload_length); + EXPECT_FALSE(packet->primary); + delete [] packet->payload; + delete packet; + packet_list.pop_front(); + + // Check second packet. + packet = packet_list.front(); + EXPECT_EQ(0, packet->header.payloadType); + EXPECT_EQ(kBaseTimestamp, packet->header.timestamp); + EXPECT_EQ(10, packet->payload_length); + EXPECT_TRUE(packet->primary); + delete [] packet->payload; + delete packet; + packet_list.pop_front(); + + // Check third packet. + packet = packet_list.front(); + VerifyPacket(packet, 10, 0, kSequenceNumber, kBaseTimestamp, 0, true); + delete [] packet->payload; + delete packet; + packet_list.pop_front(); + + // Check fourth packet. + packet = packet_list.front(); + VerifyPacket(packet, 10, 1, kSequenceNumber, kBaseTimestamp, 0, true); + delete [] packet->payload; + delete packet; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/peak_detection.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/peak_detection.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/peak_detection.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/peak_detection.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,232 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Implementation of the peak detection used for finding correlation peaks. - */ - -#include "dsp_helpfunctions.h" - -#include "signal_processing_library.h" - -/* Table of constants used in parabolic fit function WebRtcNetEQ_PrblFit */ -const int16_t WebRtcNetEQ_kPrblCf[17][3] = { { 120, 32, 64 }, { 140, 44, 75 }, - { 150, 50, 80 }, { 160, 57, 85 }, - { 180, 72, 96 }, { 200, 89, 107 }, - { 210, 98, 112 }, { 220, 108, 117 }, - { 240, 128, 128 }, { 260, 150, 139 }, - { 270, 162, 144 }, { 280, 174, 149 }, - { 300, 200, 160 }, { 320, 228, 171 }, - { 330, 242, 176 }, { 340, 257, 181 }, - { 360, 288, 192 } }; - -int16_t WebRtcNetEQ_PeakDetection(int16_t *pw16_data, int16_t w16_dataLen, - int16_t w16_nmbPeaks, int16_t fs_mult, - int16_t *pw16_winIndex, - int16_t *pw16_winValue) -{ - /* Local variables */ - int i; - int16_t w16_tmp; - int16_t w16_tmp2; - int16_t indMin = 0; - int16_t indMax = 0; - - /* Peak detection */ - - for (i = 0; i <= (w16_nmbPeaks - 1); i++) - { - if (w16_nmbPeaks == 1) - { - /* - * Single peak - * The parabola fit assumes that an extra point is available; worst case it gets - * a zero on the high end of the signal. - */ - w16_dataLen++; - } - - pw16_winIndex[i] = WebRtcSpl_MaxIndexW16(pw16_data, (int16_t) (w16_dataLen - 1)); - - if (i != w16_nmbPeaks - 1) - { - w16_tmp = pw16_winIndex[i] - 2; /* *fs_mult; */ - indMin = WEBRTC_SPL_MAX(0, w16_tmp); - w16_tmp = pw16_winIndex[i] + 2; /* *fs_mult; */ - w16_tmp2 = w16_dataLen - 1; - indMax = WEBRTC_SPL_MIN(w16_tmp2, w16_tmp); - } - - if ((pw16_winIndex[i] != 0) && (pw16_winIndex[i] != (w16_dataLen - 2))) - { - /* Parabola fit*/ - WebRtcNetEQ_PrblFit(&(pw16_data[pw16_winIndex[i] - 1]), &(pw16_winIndex[i]), - &(pw16_winValue[i]), fs_mult); - } - else - { - if (pw16_winIndex[i] == (w16_dataLen - 2)) - { - if (pw16_data[pw16_winIndex[i]] > pw16_data[pw16_winIndex[i] + 1]) - { - WebRtcNetEQ_PrblFit(&(pw16_data[pw16_winIndex[i] - 1]), - &(pw16_winIndex[i]), &(pw16_winValue[i]), fs_mult); - } - else if (pw16_data[pw16_winIndex[i]] <= pw16_data[pw16_winIndex[i] + 1]) - { - pw16_winValue[i] = (pw16_data[pw16_winIndex[i]] - + pw16_data[pw16_winIndex[i] + 1]) >> 1; /* lin approx */ - pw16_winIndex[i] = (pw16_winIndex[i] * 2 + 1) * fs_mult; - } - } - else - { - pw16_winValue[i] = pw16_data[pw16_winIndex[i]]; - pw16_winIndex[i] = pw16_winIndex[i] * 2 * fs_mult; - } - } - - if (i != w16_nmbPeaks - 1) - { - WebRtcSpl_MemSetW16(&(pw16_data[indMin]), 0, (indMax - indMin + 1)); - /* for (j=indMin; j<=indMax; j++) pw16_data[j] = 0; */ - } - } - - return 0; -} - -int16_t WebRtcNetEQ_PrblFit(int16_t *pw16_3pts, int16_t *pw16_Ind, - int16_t *pw16_outVal, int16_t fs_mult) -{ - /* Variables */ - int32_t Num, Den; - int32_t temp; - int16_t flag, stp, strt, lmt; - uint16_t PFind[13]; - - if (fs_mult == 1) - { - PFind[0] = 0; - PFind[1] = 8; - PFind[2] = 16; - } - else if (fs_mult == 2) - { - PFind[0] = 0; - PFind[1] = 4; - PFind[2] = 8; - PFind[3] = 12; - PFind[4] = 16; - } - else if (fs_mult == 4) - { - PFind[0] = 0; - PFind[1] = 2; - PFind[2] = 4; - PFind[3] = 6; - PFind[4] = 8; - PFind[5] = 10; - PFind[6] = 12; - PFind[7] = 14; - PFind[8] = 16; - } - else - { - PFind[0] = 0; - PFind[1] = 1; - PFind[2] = 3; - PFind[3] = 4; - PFind[4] = 5; - PFind[5] = 7; - PFind[6] = 8; - PFind[7] = 9; - PFind[8] = 11; - PFind[9] = 12; - PFind[10] = 13; - PFind[11] = 15; - PFind[12] = 16; - } - - /* Num = -3*pw16_3pts[0] + 4*pw16_3pts[1] - pw16_3pts[2]; */ - /* Den = pw16_3pts[0] - 2*pw16_3pts[1] + pw16_3pts[2]; */ - Num = WEBRTC_SPL_MUL_16_16(pw16_3pts[0],-3) + WEBRTC_SPL_MUL_16_16(pw16_3pts[1],4) - - pw16_3pts[2]; - - Den = pw16_3pts[0] + WEBRTC_SPL_MUL_16_16(pw16_3pts[1],-2) + pw16_3pts[2]; - - temp = (int32_t) WEBRTC_SPL_MUL(Num, (int32_t)120); /* need 32_16 really */ - flag = 1; - stp = WebRtcNetEQ_kPrblCf[PFind[fs_mult]][0] - WebRtcNetEQ_kPrblCf[PFind[fs_mult - 1]][0]; - strt = (WebRtcNetEQ_kPrblCf[PFind[fs_mult]][0] - + WebRtcNetEQ_kPrblCf[PFind[fs_mult - 1]][0]) >> 1; - - if (temp < (int32_t) WEBRTC_SPL_MUL(-Den,(int32_t)strt)) - { - lmt = strt - stp; - while (flag) - { - if ((flag == fs_mult) || (temp - > (int32_t) WEBRTC_SPL_MUL(-Den,(int32_t)lmt))) - { - *pw16_outVal - = (int16_t) - (((int32_t) ((int32_t) WEBRTC_SPL_MUL(Den,(int32_t)WebRtcNetEQ_kPrblCf[PFind[fs_mult-flag]][1]) - + (int32_t) WEBRTC_SPL_MUL(Num,(int32_t)WebRtcNetEQ_kPrblCf[PFind[fs_mult-flag]][2]) - + WEBRTC_SPL_MUL_16_16(pw16_3pts[0],256))) >> 8); - *pw16_Ind = (*pw16_Ind) * (fs_mult << 1) - flag; - flag = 0; - } - else - { - flag++; - lmt -= stp; - } - } - } - else if (temp > (int32_t) WEBRTC_SPL_MUL(-Den,(int32_t)(strt+stp))) - { - lmt = strt + (stp << 1); - while (flag) - { - if ((flag == fs_mult) || (temp - < (int32_t) WEBRTC_SPL_MUL(-Den,(int32_t)lmt))) - { - int32_t temp_term_1, temp_term_2, temp_term_3; - - temp_term_1 = WEBRTC_SPL_MUL(Den, - (int32_t) WebRtcNetEQ_kPrblCf[PFind[fs_mult+flag]][1]); - temp_term_2 = WEBRTC_SPL_MUL(Num, - (int32_t) WebRtcNetEQ_kPrblCf[PFind[fs_mult+flag]][2]); - temp_term_3 = WEBRTC_SPL_MUL_16_16(pw16_3pts[0],256); - - *pw16_outVal - = (int16_t) ((temp_term_1 + temp_term_2 + temp_term_3) >> 8); - - *pw16_Ind = (*pw16_Ind) * (fs_mult << 1) + flag; - flag = 0; - } - else - { - flag++; - lmt += stp; - } - } - - } - else - { - *pw16_outVal = pw16_3pts[1]; - *pw16_Ind = (*pw16_Ind) * 2 * fs_mult; - } - - return 0; -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/post_decode_vad.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/post_decode_vad.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/post_decode_vad.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/post_decode_vad.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/post_decode_vad.h" + +namespace webrtc { + +PostDecodeVad::~PostDecodeVad() { + if (vad_instance_) + WebRtcVad_Free(vad_instance_); +} + +void PostDecodeVad::Enable() { + if (!vad_instance_) { + // Create the instance. + if (WebRtcVad_Create(&vad_instance_) != 0) { + // Failed to create instance. + Disable(); + return; + } + } + Init(); + enabled_ = true; +} + +void PostDecodeVad::Disable() { + enabled_ = false; + running_ = false; +} + +void PostDecodeVad::Init() { + running_ = false; + if (vad_instance_) { + WebRtcVad_Init(vad_instance_); + WebRtcVad_set_mode(vad_instance_, kVadMode); + running_ = true; + } +} + +void PostDecodeVad::Update(int16_t* signal, int length, + AudioDecoder::SpeechType speech_type, + bool sid_frame, + int fs_hz) { + if (!vad_instance_ || !enabled_) { + return; + } + + if (speech_type == AudioDecoder::kComfortNoise || sid_frame || + fs_hz > 16000) { + // TODO(hlundin): Remove restriction on fs_hz. + running_ = false; + active_speech_ = true; + sid_interval_counter_ = 0; + } else if (!running_) { + ++sid_interval_counter_; + } + + if (sid_interval_counter_ >= kVadAutoEnable) { + Init(); + } + + if (length > 0 && running_) { + int vad_sample_index = 0; + active_speech_ = false; + // Loop through frame sizes 30, 20, and 10 ms. + for (int vad_frame_size_ms = 30; vad_frame_size_ms >= 10; + vad_frame_size_ms -= 10) { + int vad_frame_size_samples = vad_frame_size_ms * fs_hz / 1000; + while (length - vad_sample_index >= vad_frame_size_samples) { + int vad_return = WebRtcVad_Process( + vad_instance_, fs_hz, &signal[vad_sample_index], + vad_frame_size_samples); + active_speech_ |= (vad_return == 1); + vad_sample_index += vad_frame_size_samples; + } + } + } +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/post_decode_vad.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/post_decode_vad.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/post_decode_vad.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/post_decode_vad.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_POST_DECODE_VAD_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_POST_DECODE_VAD_H_ + +#include // size_t + +#include "webrtc/base/constructormagic.h" +#include "webrtc/common_audio/vad/include/webrtc_vad.h" +#include "webrtc/common_types.h" // NULL +#include "webrtc/modules/audio_coding/neteq/defines.h" +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" +#include "webrtc/modules/audio_coding/neteq/packet.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +class PostDecodeVad { + public: + PostDecodeVad() + : enabled_(false), + running_(false), + active_speech_(true), + sid_interval_counter_(0), + vad_instance_(NULL) { + } + + virtual ~PostDecodeVad(); + + // Enables post-decode VAD. + void Enable(); + + // Disables post-decode VAD. + void Disable(); + + // Initializes post-decode VAD. + void Init(); + + // Updates post-decode VAD with the audio data in |signal| having |length| + // samples. The data is of type |speech_type|, at the sample rate |fs_hz|. + void Update(int16_t* signal, int length, + AudioDecoder::SpeechType speech_type, bool sid_frame, int fs_hz); + + // Accessors. + bool enabled() const { return enabled_; } + bool running() const { return running_; } + bool active_speech() const { return active_speech_; } + + private: + static const int kVadMode = 0; // Sets aggressiveness to "Normal". + // Number of Update() calls without CNG/SID before re-enabling VAD. + static const int kVadAutoEnable = 3000; + + bool enabled_; + bool running_; + bool active_speech_; + int sid_interval_counter_; + ::VadInst* vad_instance_; + + DISALLOW_COPY_AND_ASSIGN(PostDecodeVad); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_POST_DECODE_VAD_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/post_decode_vad_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/post_decode_vad_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/post_decode_vad_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/post_decode_vad_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for PostDecodeVad class. + +#include "webrtc/modules/audio_coding/neteq/post_decode_vad.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace webrtc { + +TEST(PostDecodeVad, CreateAndDestroy) { + PostDecodeVad vad; +} + +// TODO(hlundin): Write more tests. + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/preemptive_expand.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/preemptive_expand.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/preemptive_expand.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/preemptive_expand.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,527 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the Pre-emptive Expand algorithm that is used to increase - * the delay by repeating a part of the audio stream. - */ - -#include "dsp.h" - -#include "signal_processing_library.h" - -#include "dsp_helpfunctions.h" -#include "neteq_error_codes.h" - -#define PREEMPTIVE_CORR_LEN 50 -#define PREEMPTIVE_MIN_LAG 10 -#define PREEMPTIVE_MAX_LAG 60 -#define PREEMPTIVE_DOWNSAMPLED_LEN (PREEMPTIVE_CORR_LEN + PREEMPTIVE_MAX_LAG) - -/* Scratch usage: - - Type Name size startpos endpos - int16_t pw16_downSampSpeech 110 0 109 - int32_t pw32_corr 2*50 110 209 - int16_t pw16_corr 50 0 49 - - Total: 110+2*50 - */ - -#define SCRATCH_PW16_DS_SPEECH 0 -#define SCRATCH_PW32_CORR PREEMPTIVE_DOWNSAMPLED_LEN -#define SCRATCH_PW16_CORR 0 - -/**************************************************************************** - * WebRtcNetEQ_PreEmptiveExpand(...) - * - * This function tries to extend the audio data by repeating one or several - * pitch periods. The operation is only carried out if the correlation is - * strong or if the signal energy is very low. The algorithm is the - * reciprocal of the Accelerate algorithm. - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector. - * - decoded : Pointer to newly decoded speech. - * - len : Length of decoded speech. - * - oldDataLen : Length of the part of decoded that has already been played out. - * - BGNonly : If non-zero, Pre-emptive Expand will only copy - * the first DEFAULT_TIME_ADJUST seconds of the - * input and append to the end. No signal matching is - * done. - * - * Output: - * - inst : Updated instance - * - outData : Pointer to a memory space where the output data - * should be stored. The vector must be at least - * min(len + 120*fs/8000, NETEQ_MAX_OUTPUT_SIZE) - * elements long. - * - pw16_len : Number of samples written to outData. - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_PreEmptiveExpand(DSPInst_t *inst, -#ifdef SCRATCH - int16_t *pw16_scratchPtr, -#endif - const int16_t *pw16_decoded, int len, int oldDataLen, - int16_t *pw16_outData, int16_t *pw16_len, - int16_t BGNonly) -{ - -#ifdef SCRATCH - /* Use scratch memory for internal temporary vectors */ - int16_t *pw16_downSampSpeech = pw16_scratchPtr + SCRATCH_PW16_DS_SPEECH; - int32_t *pw32_corr = (int32_t*) (pw16_scratchPtr + SCRATCH_PW32_CORR); - int16_t *pw16_corr = pw16_scratchPtr + SCRATCH_PW16_CORR; -#else - /* Allocate memory for temporary vectors */ - int16_t pw16_downSampSpeech[PREEMPTIVE_DOWNSAMPLED_LEN]; - int32_t pw32_corr[PREEMPTIVE_CORR_LEN]; - int16_t pw16_corr[PREEMPTIVE_CORR_LEN]; -#endif - int16_t w16_decodedMax = 0; - int16_t w16_tmp = 0; - int16_t w16_tmp2; - int32_t w32_tmp; - int32_t w32_tmp2; - - const int16_t w16_startLag = PREEMPTIVE_MIN_LAG; - const int16_t w16_endLag = PREEMPTIVE_MAX_LAG; - const int16_t w16_corrLen = PREEMPTIVE_CORR_LEN; - const int16_t *pw16_vec1, *pw16_vec2; - int16_t *pw16_vectmp; - int16_t w16_inc, w16_startfact; - int16_t w16_bestIndex, w16_bestVal; - int16_t w16_VAD = 1; - int16_t fsMult; - int16_t fsMult120; - int32_t w32_en1, w32_en2, w32_cc; - int16_t w16_en1, w16_en2; - int16_t w16_en1Scale, w16_en2Scale; - int16_t w16_sqrtEn1En2; - int16_t w16_bestCorr = 0; - int ok; - -#ifdef NETEQ_STEREO - MasterSlaveInfo *msInfo = inst->msInfo; -#endif - - fsMult = WebRtcNetEQ_CalcFsMult(inst->fs); /* Calculate fs/8000 */ - - /* Pre-calculate common multiplication with fsMult */ - fsMult120 = (int16_t) WEBRTC_SPL_MUL_16_16(fsMult, 120); /* 15 ms */ - - inst->ExpandInst.w16_consecExp = 0; /* Last was not expand any more */ - - /* - * Sanity check for len variable; must be (almost) 30 ms (120*fsMult + max(bestIndex)). - * Also, the new part must be at least .625 ms (w16_overlap). - */ - if (len < (int16_t) WEBRTC_SPL_MUL_16_16((120 + 119), fsMult) || oldDataLen >= len - - inst->ExpandInst.w16_overlap) - { - /* Length of decoded data too short */ - inst->w16_mode = MODE_UNSUCCESS_PREEMPTIVE; - *pw16_len = len; - - - /* simply move all data from decoded to outData */ - - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, (int16_t) len); - - return NETEQ_OTHER_ERROR; - } - - /***********************************/ - /* Special operations for BGN only */ - /***********************************/ - - /* Check if "background noise only" flag is set */ - if (BGNonly) - { - /* special operation for BGN only; simply insert a chunk of data */ - w16_bestIndex = DEFAULT_TIME_ADJUST * (fsMult << 3); /* X*fs/1000 */ - - /* Sanity check for bestIndex */ - if (w16_bestIndex > len) - { /* not good, do nothing instead */ - inst->w16_mode = MODE_UNSUCCESS_PREEMPTIVE; - *pw16_len = len; - - - /* simply move all data from decoded to outData */ - - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, (int16_t) len); - - return NETEQ_OTHER_ERROR; - } - - /* set length parameter */ - *pw16_len = len + w16_bestIndex; - - - /* copy to output */ - - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, len); - WEBRTC_SPL_MEMCPY_W16(&pw16_outData[len], pw16_decoded, w16_bestIndex); - - /* set mode */ - inst->w16_mode = MODE_LOWEN_PREEMPTIVE; - - /* update statistics */ - inst->statInst.preemptiveLength += w16_bestIndex; - /* Short-term activity statistics. */ - inst->activity_stats.preemptive_expand_bgn_samples += w16_bestIndex; - - return 0; - } /* end of special code for BGN mode */ - -#ifdef NETEQ_STEREO - - /* Sanity for msInfo */ - if (msInfo == NULL) - { - /* this should not happen here */ - return MASTER_SLAVE_ERROR; - } - - if ((msInfo->msMode == NETEQ_MASTER) || (msInfo->msMode == NETEQ_MONO)) - { - /* Find correlation lag only for non-slave instances */ - -#endif - - /****************************************************************/ - /* Find the strongest correlation lag by downsampling to 4 kHz, */ - /* calculating correlation for downsampled signal and finding */ - /* the strongest correlation peak. */ - /****************************************************************/ - - /* find maximum absolute value */ - w16_decodedMax = WebRtcSpl_MaxAbsValueW16(pw16_decoded, (int16_t) len); - - /* downsample the decoded speech to 4 kHz */ - ok = WebRtcNetEQ_DownSampleTo4kHz(pw16_decoded, len, inst->fs, pw16_downSampSpeech, - PREEMPTIVE_DOWNSAMPLED_LEN, 1 /* compensate delay*/); - if (ok != 0) - { - /* error */ - inst->w16_mode = MODE_UNSUCCESS_PREEMPTIVE; - *pw16_len = len; - - - /* simply move all data from decoded to outData */ - - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, (int16_t) len); - - return NETEQ_OTHER_ERROR; - } - - /* - * Set scaling factor for cross correlation to protect against - * overflow (log2(50) => 6) - */ - w16_tmp = 6 - WebRtcSpl_NormW32(WEBRTC_SPL_MUL_16_16(w16_decodedMax, w16_decodedMax)); - w16_tmp = WEBRTC_SPL_MAX(0, w16_tmp); - - /* Perform correlation from lag 10 to lag 60 in 4 kHz domain */WebRtcNetEQ_CrossCorr( - pw32_corr, &pw16_downSampSpeech[w16_endLag], - &pw16_downSampSpeech[w16_endLag - w16_startLag], w16_corrLen, - (int16_t) (w16_endLag - w16_startLag), w16_tmp, -1); - - /* Normalize correlation to 14 bits and put in a int16_t vector */ - w32_tmp = WebRtcSpl_MaxAbsValueW32(pw32_corr, w16_corrLen); - w16_tmp = 17 - WebRtcSpl_NormW32(w32_tmp); - w16_tmp = WEBRTC_SPL_MAX(0, w16_tmp); - - WebRtcSpl_VectorBitShiftW32ToW16(pw16_corr, w16_corrLen, pw32_corr, w16_tmp); - - /* Find limits for peak finding, in order to avoid overful NetEQ algorithm buffer. */ - /* Calculate difference between MAX_OUTPUT_SIZE and len in 4 kHz domain. */ - w16_tmp = WebRtcSpl_DivW32W16ResW16((int32_t) (NETEQ_MAX_OUTPUT_SIZE - len), - (int16_t) (fsMult << 1)) - w16_startLag; - w16_tmp = WEBRTC_SPL_MIN(w16_corrLen, w16_tmp); /* no more than corrLen = 50 */ - -#ifdef NETEQ_STEREO - } /* end if (msInfo->msMode != NETEQ_SLAVE) */ - - if ((msInfo->msMode == NETEQ_MASTER) || (msInfo->msMode == NETEQ_MONO)) - { - /* Find the strongest correlation peak by using the parabolic fit method */ - WebRtcNetEQ_PeakDetection(pw16_corr, w16_tmp, 1, fsMult, &w16_bestIndex, &w16_bestVal); - /* 0 <= bestIndex <= (2*w16_tmp - 1)*fsMult <= (2*corrLen - 1)*fsMult = 99*fsMult */ - - /* Compensate bestIndex for displaced starting position */ - w16_bestIndex = w16_bestIndex + w16_startLag * WEBRTC_SPL_LSHIFT_W16(fsMult, 1); - /* 20*fsMult <= bestIndex <= 119*fsMult */ - - msInfo->bestIndex = w16_bestIndex; - } - else if (msInfo->msMode == NETEQ_SLAVE) - { - if (msInfo->extraInfo == PE_EXP_FAIL) - { - /* Master has signaled an unsuccessful preemptive expand */ - w16_bestIndex = 0; - } - else - { - /* Get best index from master */ - w16_bestIndex = msInfo->bestIndex; - } - } - else - { - /* Invalid mode */ - return (MASTER_SLAVE_ERROR); - } - -#else /* NETEQ_STEREO */ - - /* Find the strongest correlation peak by using the parabolic fit method */ - WebRtcNetEQ_PeakDetection(pw16_corr, w16_tmp, 1, fsMult, &w16_bestIndex, &w16_bestVal); - /* 0 <= bestIndex <= (2*w16_tmp - 1)*fsMult <= (2*corrLen - 1)*fsMult = 99*fsMult */ - - /* Compensate bestIndex for displaced starting position */ - w16_bestIndex = w16_bestIndex + w16_startLag * WEBRTC_SPL_LSHIFT_W16(fsMult, 1); - /* 20*fsMult <= bestIndex <= 119*fsMult */ - -#endif /* NETEQ_STEREO */ - -#ifdef NETEQ_STEREO - - if ((msInfo->msMode == NETEQ_MASTER) || (msInfo->msMode == NETEQ_MONO)) - { - /* Calculate correlation only for non-slave instances */ - -#endif /* NETEQ_STEREO */ - - /*****************************************************/ - /* Calculate correlation bestCorr for the found lag. */ - /* Also do a simple VAD decision. */ - /*****************************************************/ - - /* - * Calculate scaling to ensure that bestIndex samples can be square-summed - * without overflowing - */ - w16_tmp = (31 - - WebRtcSpl_NormW32(WEBRTC_SPL_MUL_16_16(w16_decodedMax, w16_decodedMax))); - w16_tmp += (31 - WebRtcSpl_NormW32(w16_bestIndex)); - w16_tmp -= 31; - w16_tmp = WEBRTC_SPL_MAX(0, w16_tmp); - - /* vec1 starts at 15 ms minus one pitch period */ - pw16_vec1 = &pw16_decoded[fsMult120 - w16_bestIndex]; - /* vec2 start at 15 ms */ - pw16_vec2 = &pw16_decoded[fsMult120]; - - /* Calculate energies for vec1 and vec2 */ - w32_en1 = WebRtcNetEQ_DotW16W16((int16_t*) pw16_vec1, - (int16_t*) pw16_vec1, w16_bestIndex, w16_tmp); - w32_en2 = WebRtcNetEQ_DotW16W16((int16_t*) pw16_vec2, - (int16_t*) pw16_vec2, w16_bestIndex, w16_tmp); - - /* Calculate cross-correlation at the found lag */ - w32_cc = WebRtcNetEQ_DotW16W16((int16_t*) pw16_vec1, (int16_t*) pw16_vec2, - w16_bestIndex, w16_tmp); - - /* Check VAD constraint - ((en1+en2)/(2*bestIndex)) <= 8*inst->BGNInst.energy */ - w32_tmp = WEBRTC_SPL_RSHIFT_W32(w32_en1 + w32_en2, 4); /* (en1+en2)/(2*8) */ - if (inst->BGNInst.w16_initialized == 1) - { - w32_tmp2 = inst->BGNInst.w32_energy; - } - else - { - /* if BGN parameters have not been estimated, use a fixed threshold */ - w32_tmp2 = 75000; - } - w16_tmp2 = 16 - WebRtcSpl_NormW32(w32_tmp2); - w16_tmp2 = WEBRTC_SPL_MAX(0, w16_tmp2); - w32_tmp = WEBRTC_SPL_RSHIFT_W32(w32_tmp, w16_tmp2); - w16_tmp2 = (int16_t) WEBRTC_SPL_RSHIFT_W32(w32_tmp2, w16_tmp2); - w32_tmp2 = WEBRTC_SPL_MUL_16_16(w16_bestIndex, w16_tmp2); - - /* Scale w32_tmp properly before comparing with w32_tmp2 */ - /* (w16_tmp is scaling before energy calculation, thus 2*w16_tmp) */ - if (WebRtcSpl_NormW32(w32_tmp) < WEBRTC_SPL_LSHIFT_W32(w16_tmp,1)) - { - /* Cannot scale only w32_tmp, must scale w32_temp2 too */ - int16_t tempshift = WebRtcSpl_NormW32(w32_tmp); - w32_tmp = WEBRTC_SPL_LSHIFT_W32(w32_tmp, tempshift); - w32_tmp2 = WEBRTC_SPL_RSHIFT_W32(w32_tmp2, - WEBRTC_SPL_LSHIFT_W32(w16_tmp,1) - tempshift); - } - else - { - w32_tmp = WEBRTC_SPL_LSHIFT_W32(w32_tmp, - WEBRTC_SPL_LSHIFT_W32(w16_tmp,1)); - } - - if (w32_tmp <= w32_tmp2) /*((en1+en2)/(2*bestIndex)) <= 8*inst->BGNInst.energy */ - { - /* The signal seems to be passive speech */ - w16_VAD = 0; - w16_bestCorr = 0; /* Correlation does not matter */ - - /* For low energy expansion, the new data can be less than 15 ms, - but we must ensure that bestIndex is not larger than the new data. */ - w16_bestIndex = WEBRTC_SPL_MIN( w16_bestIndex, len - oldDataLen ); - } - else - { - /* The signal is active speech */ - w16_VAD = 1; - - /* Calculate correlation (cc/sqrt(en1*en2)) */ - - /* Start with calculating scale values */ - w16_en1Scale = 16 - WebRtcSpl_NormW32(w32_en1); - w16_en1Scale = WEBRTC_SPL_MAX(0, w16_en1Scale); - w16_en2Scale = 16 - WebRtcSpl_NormW32(w32_en2); - w16_en2Scale = WEBRTC_SPL_MAX(0, w16_en2Scale); - - /* Make sure total scaling is even (to simplify scale factor after sqrt) */ - if ((w16_en1Scale + w16_en2Scale) & 1) - { - w16_en1Scale += 1; - } - - /* Convert energies to int16_t */ - w16_en1 = (int16_t) WEBRTC_SPL_RSHIFT_W32(w32_en1, w16_en1Scale); - w16_en2 = (int16_t) WEBRTC_SPL_RSHIFT_W32(w32_en2, w16_en2Scale); - - /* Calculate energy product */ - w32_tmp = WEBRTC_SPL_MUL_16_16(w16_en1, w16_en2); - - /* Calculate square-root of energy product */ - w16_sqrtEn1En2 = (int16_t) WebRtcSpl_SqrtFloor(w32_tmp); - - /* Calculate cc/sqrt(en1*en2) in Q14 */ - w16_tmp = 14 - ((w16_en1Scale + w16_en2Scale) >> 1); - w32_cc = WEBRTC_SPL_SHIFT_W32(w32_cc, w16_tmp); - w32_cc = WEBRTC_SPL_MAX(0, w32_cc); /* Don't divide with negative number */ - w16_bestCorr = (int16_t) WebRtcSpl_DivW32W16(w32_cc, w16_sqrtEn1En2); - w16_bestCorr = WEBRTC_SPL_MIN(16384, w16_bestCorr); /* set maximum to 1.0 */ - } - -#ifdef NETEQ_STEREO - - } /* end if (msInfo->msMode != NETEQ_SLAVE) */ - -#endif /* NETEQ_STEREO */ - - /*******************************************************/ - /* Check preemptive expand criteria and insert samples */ - /*******************************************************/ - - /* Check for strong correlation (>0.9) and at least 15 ms new data, - or passive speech */ -#ifdef NETEQ_STEREO - if (((((w16_bestCorr > 14746) && (oldDataLen <= fsMult120)) || (w16_VAD == 0)) - && (msInfo->msMode != NETEQ_SLAVE)) || ((msInfo->msMode == NETEQ_SLAVE) - && (msInfo->extraInfo != PE_EXP_FAIL))) -#else - if (((w16_bestCorr > 14746) && (oldDataLen <= fsMult120)) - || (w16_VAD == 0)) -#endif - { - /* Do expand operation by overlap add */ - - /* Set length of the first part, not to be modified */ - int16_t w16_startIndex = WEBRTC_SPL_MAX(oldDataLen, fsMult120); - - /* - * Calculate cross-fading slope so that the fading factor goes from - * 1 (16384 in Q14) to 0 in one pitch period (bestIndex). - */ - w16_inc = (int16_t) WebRtcSpl_DivW32W16((int32_t) 16384, - (int16_t) (w16_bestIndex + 1)); /* in Q14 */ - - /* Initiate fading factor */ - w16_startfact = 16384 - w16_inc; - - /* vec1 starts at 15 ms minus one pitch period */ - pw16_vec1 = &pw16_decoded[w16_startIndex - w16_bestIndex]; - /* vec2 start at 15 ms */ - pw16_vec2 = &pw16_decoded[w16_startIndex]; - - - /* Copy unmodified part [0 to 15 ms] */ - - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, w16_startIndex); - - /* Generate interpolated part of length bestIndex (1 pitch period) */ - pw16_vectmp = pw16_outData + w16_startIndex; - /* Reuse mixing function from Expand */ - WebRtcNetEQ_MixVoiceUnvoice(pw16_vectmp, (int16_t*) pw16_vec2, - (int16_t*) pw16_vec1, &w16_startfact, w16_inc, w16_bestIndex); - - /* Move the last part (also unmodified) */ - /* Take from decoded at 15 ms */ - pw16_vec2 = &pw16_decoded[w16_startIndex]; - WEBRTC_SPL_MEMMOVE_W16(&pw16_outData[w16_startIndex + w16_bestIndex], pw16_vec2, - (int16_t) (len - w16_startIndex)); - - /* Set the mode flag */ - if (w16_VAD) - { - inst->w16_mode = MODE_SUCCESS_PREEMPTIVE; - } - else - { - inst->w16_mode = MODE_LOWEN_PREEMPTIVE; - } - - /* Calculate resulting length = original length + pitch period */ - *pw16_len = len + w16_bestIndex; - - /* Update in-call statistics */ - inst->statInst.preemptiveLength += w16_bestIndex; - /* Short-term activity statistics. */ - inst->activity_stats.preemptive_expand_normal_samples += w16_bestIndex; - return 0; - } - else - { - /* Preemptive Expand not allowed */ - -#ifdef NETEQ_STEREO - /* Signal to slave(s) that this was unsuccessful */ - if (msInfo->msMode == NETEQ_MASTER) - { - msInfo->extraInfo = PE_EXP_FAIL; - } -#endif - - /* Set mode flag to unsuccessful preemptive expand */ - inst->w16_mode = MODE_UNSUCCESS_PREEMPTIVE; - - /* Length is unmodified */ - *pw16_len = len; - - - /* Simply move all data from decoded to outData */ - - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, (int16_t) len); - - return 0; - } -} - -#undef SCRATCH_PW16_DS_SPEECH -#undef SCRATCH_PW32_CORR -#undef SCRATCH_PW16_CORR diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/preemptive_expand.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/preemptive_expand.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/preemptive_expand.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/preemptive_expand.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/preemptive_expand.h" + +#include // min, max + +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" + +namespace webrtc { + +PreemptiveExpand::ReturnCodes PreemptiveExpand::Process( + const int16_t* input, + int input_length, + int old_data_length, + AudioMultiVector* output, + int16_t* length_change_samples) { + old_data_length_per_channel_ = old_data_length; + // Input length must be (almost) 30 ms. + // Also, the new part must be at least |overlap_samples_| elements. + static const int k15ms = 120; // 15 ms = 120 samples at 8 kHz sample rate. + if (num_channels_ == 0 || + input_length / num_channels_ < (2 * k15ms - 1) * fs_mult_ || + old_data_length >= input_length / num_channels_ - overlap_samples_) { + // Length of input data too short to do preemptive expand. Simply move all + // data from input to output. + output->PushBackInterleaved(input, input_length); + return kError; + } + return TimeStretch::Process(input, input_length, output, + length_change_samples); +} + +void PreemptiveExpand::SetParametersForPassiveSpeech(size_t len, + int16_t* best_correlation, + int* peak_index) const { + // When the signal does not contain any active speech, the correlation does + // not matter. Simply set it to zero. + *best_correlation = 0; + + // For low energy expansion, the new data can be less than 15 ms, + // but we must ensure that best_correlation is not larger than the length of + // the new data. + // but we must ensure that best_correlation is not larger than the new data. + *peak_index = std::min(*peak_index, + static_cast(len - old_data_length_per_channel_)); +} + +PreemptiveExpand::ReturnCodes PreemptiveExpand::CheckCriteriaAndStretch( + const int16_t *input, size_t input_length, size_t peak_index, + int16_t best_correlation, bool active_speech, + AudioMultiVector* output) const { + // Pre-calculate common multiplication with |fs_mult_|. + // 120 corresponds to 15 ms. + int fs_mult_120 = fs_mult_ * 120; + assert(old_data_length_per_channel_ >= 0); // Make sure it's been set. + // Check for strong correlation (>0.9 in Q14) and at least 15 ms new data, + // or passive speech. + if (((best_correlation > kCorrelationThreshold) && + (old_data_length_per_channel_ <= fs_mult_120)) || + !active_speech) { + // Do accelerate operation by overlap add. + + // Set length of the first part, not to be modified. + size_t unmodified_length = std::max(old_data_length_per_channel_, + fs_mult_120); + // Copy first part, including cross-fade region. + output->PushBackInterleaved( + input, (unmodified_length + peak_index) * num_channels_); + // Copy the last |peak_index| samples up to 15 ms to |temp_vector|. + AudioMultiVector temp_vector(num_channels_); + temp_vector.PushBackInterleaved( + &input[(unmodified_length - peak_index) * num_channels_], + peak_index * num_channels_); + // Cross-fade |temp_vector| onto the end of |output|. + output->CrossFade(temp_vector, peak_index); + // Copy the last unmodified part, 15 ms + pitch period until the end. + output->PushBackInterleaved( + &input[unmodified_length * num_channels_], + input_length - unmodified_length * num_channels_); + + if (active_speech) { + return kSuccess; + } else { + return kSuccessLowEnergy; + } + } else { + // Accelerate not allowed. Simply move all data from decoded to outData. + output->PushBackInterleaved(input, input_length); + return kNoStretch; + } +} + +PreemptiveExpand* PreemptiveExpandFactory::Create( + int sample_rate_hz, + size_t num_channels, + const BackgroundNoise& background_noise, + int overlap_samples) const { + return new PreemptiveExpand( + sample_rate_hz, num_channels, background_noise, overlap_samples); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/preemptive_expand.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/preemptive_expand.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/preemptive_expand.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/preemptive_expand.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_PREEMPTIVE_EXPAND_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_PREEMPTIVE_EXPAND_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" +#include "webrtc/modules/audio_coding/neteq/time_stretch.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Forward declarations. +class BackgroundNoise; + +// This class implements the PreemptiveExpand operation. Most of the work is +// done in the base class TimeStretch, which is shared with the Accelerate +// operation. In the PreemptiveExpand class, the operations that are specific to +// PreemptiveExpand are implemented. +class PreemptiveExpand : public TimeStretch { + public: + PreemptiveExpand(int sample_rate_hz, + size_t num_channels, + const BackgroundNoise& background_noise, + int overlap_samples) + : TimeStretch(sample_rate_hz, num_channels, background_noise), + old_data_length_per_channel_(-1), + overlap_samples_(overlap_samples) { + } + + virtual ~PreemptiveExpand() {} + + // This method performs the actual PreemptiveExpand operation. The samples are + // read from |input|, of length |input_length| elements, and are written to + // |output|. The number of samples added through time-stretching is + // is provided in the output |length_change_samples|. The method returns + // the outcome of the operation as an enumerator value. + ReturnCodes Process(const int16_t *pw16_decoded, + int len, + int old_data_len, + AudioMultiVector* output, + int16_t* length_change_samples); + + protected: + // Sets the parameters |best_correlation| and |peak_index| to suitable + // values when the signal contains no active speech. + virtual void SetParametersForPassiveSpeech(size_t len, + int16_t* w16_bestCorr, + int* w16_bestIndex) const; + + // Checks the criteria for performing the time-stretching operation and, + // if possible, performs the time-stretching. + virtual ReturnCodes CheckCriteriaAndStretch( + const int16_t *pw16_decoded, size_t len, size_t w16_bestIndex, + int16_t w16_bestCorr, bool w16_VAD, + AudioMultiVector* output) const; + + private: + int old_data_length_per_channel_; + int overlap_samples_; + + DISALLOW_COPY_AND_ASSIGN(PreemptiveExpand); +}; + +struct PreemptiveExpandFactory { + PreemptiveExpandFactory() {} + virtual ~PreemptiveExpandFactory() {} + + virtual PreemptiveExpand* Create( + int sample_rate_hz, + size_t num_channels, + const BackgroundNoise& background_noise, + int overlap_samples) const; +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_PREEMPTIVE_EXPAND_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/random_vector.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/random_vector.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/random_vector.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/random_vector.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This function generates a pseudo-random vector. - */ - -#include "dsp_helpfunctions.h" - -/* - * Values are normalized so that - * sqrt(dot(pw16_NETEQFIX_RANDN_TBL,pw16_NETEQFIX_RANDN_TBL)/256)=2^13 - */ -const int16_t WebRtcNetEQ_kRandnTbl[RANDVEC_NO_OF_SAMPLES] = -{ - 2680, 5532, 441, 5520, 16170, -5146, -1024, -8733, 3115, 9598, -10380, -4959, -1280, -21716, 7133, -1522, - 13458, -3902, 2789, -675, 3441, 5016, -13599, -4003, -2739, 3922, -7209, 13352, -11617, -7241, 12905, -2314, - 5426, 10121, -9702, 11207, -13542, 1373, 816, -5934, -12504, 4798, 1811, 4112, -613, 201, -10367, -2960, - -2419, 3442, 4299, -6116, -6092, 1552, -1650, -480, -1237, 18720, -11858, -8303, -8212, 865, -2890, -16968, - 12052, -5845, -5912, 9777, -5665, -6294, 5426, -4737, -6335, 1652, 761, 3832, 641, -8552, -9084, -5753, - 8146, 12156, -4915, 15086, -1231, -1869, 11749, -9319, -6403, 11407, 6232, -1683, 24340, -11166, 4017, -10448, - 3153, -2936, 6212, 2891, -866, -404, -4807, -2324, -1917, -2388, -6470, -3895, -10300, 5323, -5403, 2205, - 4640, 7022, -21186, -6244, -882, -10031, -3395, -12885, 7155, -5339, 5079, -2645, -9515, 6622, 14651, 15852, - 359, 122, 8246, -3502, -6696, -3679, -13535, -1409, -704, -7403, -4007, 1798, 279, -420, -12796, -14219, - 1141, 3359, 11434, 7049, -6684, -7473, 14283, -4115, -9123, -8969, 4152, 4117, 13792, 5742, 16168, 8661, - -1609, -6095, 1881, 14380, -5588, 6758, -6425, -22969, -7269, 7031, 1119, -1611, -5850, -11281, 3559, -8952, - -10146, -4667, -16251, -1538, 2062, -1012, -13073, 227, -3142, -5265, 20, 5770, -7559, 4740, -4819, 992, - -8208, -7130, -4652, 6725, 7369, -1036, 13144, -1588, -5304, -2344, -449, -5705, -8894, 5205, -17904, -11188, - -1022, 4852, 10101, -5255, -4200, -752, 7941, -1543, 5959, 14719, 13346, 17045, -15605, -1678, -1600, -9230, - 68, 23348, 1172, 7750, 11212, -18227, 9956, 4161, 883, 3947, 4341, 1014, -4889, -2603, 1246, -5630, - -3596, -870, -1298, 2784, -3317, -6612, -20541, 4166, 4181, -8625, 3562, 12890, 4761, 3205, -12259, -8579 -}; - - -void WebRtcNetEQ_RandomVec(uint32_t *w32_seed, int16_t *pw16_randVec, - int16_t w16_len, int16_t w16_incval) -{ - int i; - int16_t w16_pos; - for (i = 0; i < w16_len; i++) - { - *w32_seed = (*w32_seed) + w16_incval; - w16_pos = (int16_t) ((*w32_seed) & (RANDVEC_NO_OF_SAMPLES - 1)); - pw16_randVec[i] = WebRtcNetEQ_kRandnTbl[w16_pos]; - } -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/random_vector.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/random_vector.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/random_vector.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/random_vector.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/random_vector.h" + +namespace webrtc { + +const int16_t RandomVector::kRandomTable[RandomVector::kRandomTableSize] = { + 2680, 5532, 441, 5520, 16170, -5146, -1024, -8733, 3115, 9598, -10380, + -4959, -1280, -21716, 7133, -1522, 13458, -3902, 2789, -675, 3441, 5016, + -13599, -4003, -2739, 3922, -7209, 13352, -11617, -7241, 12905, -2314, 5426, + 10121, -9702, 11207, -13542, 1373, 816, -5934, -12504, 4798, 1811, 4112, + -613, 201, -10367, -2960, -2419, 3442, 4299, -6116, -6092, 1552, -1650, + -480, -1237, 18720, -11858, -8303, -8212, 865, -2890, -16968, 12052, -5845, + -5912, 9777, -5665, -6294, 5426, -4737, -6335, 1652, 761, 3832, 641, -8552, + -9084, -5753, 8146, 12156, -4915, 15086, -1231, -1869, 11749, -9319, -6403, + 11407, 6232, -1683, 24340, -11166, 4017, -10448, 3153, -2936, 6212, 2891, + -866, -404, -4807, -2324, -1917, -2388, -6470, -3895, -10300, 5323, -5403, + 2205, 4640, 7022, -21186, -6244, -882, -10031, -3395, -12885, 7155, -5339, + 5079, -2645, -9515, 6622, 14651, 15852, 359, 122, 8246, -3502, -6696, -3679, + -13535, -1409, -704, -7403, -4007, 1798, 279, -420, -12796, -14219, 1141, + 3359, 11434, 7049, -6684, -7473, 14283, -4115, -9123, -8969, 4152, 4117, + 13792, 5742, 16168, 8661, -1609, -6095, 1881, 14380, -5588, 6758, -6425, + -22969, -7269, 7031, 1119, -1611, -5850, -11281, 3559, -8952, -10146, -4667, + -16251, -1538, 2062, -1012, -13073, 227, -3142, -5265, 20, 5770, -7559, + 4740, -4819, 992, -8208, -7130, -4652, 6725, 7369, -1036, 13144, -1588, + -5304, -2344, -449, -5705, -8894, 5205, -17904, -11188, -1022, 4852, 10101, + -5255, -4200, -752, 7941, -1543, 5959, 14719, 13346, 17045, -15605, -1678, + -1600, -9230, 68, 23348, 1172, 7750, 11212, -18227, 9956, 4161, 883, 3947, + 4341, 1014, -4889, -2603, 1246, -5630, -3596, -870, -1298, 2784, -3317, + -6612, -20541, 4166, 4181, -8625, 3562, 12890, 4761, 3205, -12259, -8579 }; + +void RandomVector::Reset() { + seed_ = 777; + seed_increment_ = 1; +} + +void RandomVector::Generate(size_t length, int16_t* output) { + for (size_t i = 0; i < length; i++) { + seed_ += seed_increment_; + size_t position = seed_ & (kRandomTableSize - 1); + output[i] = kRandomTable[position]; + } +} + +void RandomVector::IncreaseSeedIncrement(int16_t increase_by) { + seed_increment_+= increase_by; + seed_increment_ &= kRandomTableSize - 1; +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/random_vector.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/random_vector.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/random_vector.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/random_vector.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_RANDOM_VECTOR_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_RANDOM_VECTOR_H_ + +#include // size_t + +#include "webrtc/base/constructormagic.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// This class generates pseudo-random samples. +class RandomVector { + public: + static const int kRandomTableSize = 256; + static const int16_t kRandomTable[kRandomTableSize]; + + RandomVector() + : seed_(777), + seed_increment_(1) { + } + + void Reset(); + + void Generate(size_t length, int16_t* output); + + void IncreaseSeedIncrement(int16_t increase_by); + + // Accessors and mutators. + int16_t seed_increment() { return seed_increment_; } + void set_seed_increment(int16_t value) { seed_increment_ = value; } + + private: + uint32_t seed_; + int16_t seed_increment_; + + DISALLOW_COPY_AND_ASSIGN(RandomVector); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_RANDOM_VECTOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/random_vector_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/random_vector_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/random_vector_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/random_vector_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for RandomVector class. + +#include "webrtc/modules/audio_coding/neteq/random_vector.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace webrtc { + +TEST(RandomVector, CreateAndDestroy) { + RandomVector random_vector; +} + +// TODO(hlundin): Write more tests. + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/recin.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/recin.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/recin.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/recin.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,531 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Implementation of the RecIn function, which is the main function for inserting RTP - * packets into NetEQ. - */ - -#include "mcu.h" - -#include - -#include "automode.h" -#include "dtmf_buffer.h" -#include "mcu_dsp_common.h" -#include "neteq_defines.h" -#include "neteq_error_codes.h" -#include "signal_processing_library.h" - -int WebRtcNetEQ_RecInInternal(MCUInst_t *MCU_inst, RTPPacket_t *RTPpacketInput, - uint32_t uw32_timeRec) -{ - RTPPacket_t RTPpacket[2]; - int i_k; - int i_ok = 0, i_No_Of_Payloads = 1; - int16_t flushed = 0; - int16_t codecPos; - int curr_Codec; - int16_t isREDPayload = 0; - int32_t temp_bufsize; - int is_sync_rtp = MCU_inst->av_sync && WebRtcNetEQ_IsSyncPayload( - RTPpacketInput->payload, RTPpacketInput->payloadLen); -#ifdef NETEQ_RED_CODEC - RTPPacket_t* RTPpacketPtr[2]; /* Support for redundancy up to 2 payloads */ - RTPpacketPtr[0] = &RTPpacket[0]; - RTPpacketPtr[1] = &RTPpacket[1]; -#endif - - temp_bufsize = WebRtcNetEQ_PacketBufferGetSize(&MCU_inst->PacketBuffer_inst, - &MCU_inst->codec_DB_inst, - MCU_inst->av_sync); - /* - * Copy from input RTP packet to local copy - * (mainly to enable multiple payloads using RED) - */ - - WEBRTC_SPL_MEMCPY_W8(&RTPpacket[0], RTPpacketInput, sizeof(RTPPacket_t)); - - /* Reinitialize NetEq if it's needed (changed SSRC or first call) */ - - if ((RTPpacket[0].ssrc != MCU_inst->ssrc) || (MCU_inst->first_packet == 1)) - { - WebRtcNetEQ_RTCPInit(&MCU_inst->RTCP_inst, RTPpacket[0].seqNumber); - MCU_inst->first_packet = 0; - - /* Flush the buffer */ - WebRtcNetEQ_PacketBufferFlush(&MCU_inst->PacketBuffer_inst); - - /* Store new SSRC */ - MCU_inst->ssrc = RTPpacket[0].ssrc; - - /* Update codecs */ - MCU_inst->timeStamp = RTPpacket[0].timeStamp; - MCU_inst->current_Payload = RTPpacket[0].payloadType; - - /*Set MCU to update codec on next SignalMCU call */ - MCU_inst->new_codec = 1; - - /* Reset timestamp scaling */ - MCU_inst->TSscalingInitialized = 0; - - } - - if (!is_sync_rtp) { /* Update only if it not sync packet. */ - /* Call RTCP statistics if it is not sync packet. */ - i_ok |= WebRtcNetEQ_RTCPUpdate(&(MCU_inst->RTCP_inst), - RTPpacket[0].seqNumber, - RTPpacket[0].timeStamp, uw32_timeRec); - } - - /* If Redundancy is supported and this is the redundancy payload, separate the payloads */ -#ifdef NETEQ_RED_CODEC - if (RTPpacket[0].payloadType == WebRtcNetEQ_DbGetPayload(&MCU_inst->codec_DB_inst, - kDecoderRED)) - { - if (is_sync_rtp) - { - /* Sync packet should not have RED payload type. */ - return RECIN_SYNC_RTP_NOT_ACCEPTABLE; - } - - /* Split the payload into a main and a redundancy payloads */ - i_ok = WebRtcNetEQ_RedundancySplit(RTPpacketPtr, 2, &i_No_Of_Payloads); - if (i_ok < 0) - { - /* error returned */ - return i_ok; - } - - /* - * Only accept a few redundancies of the same type as the main data, - * AVT events and CNG. - */ - if ((i_No_Of_Payloads > 1) && (RTPpacket[0].payloadType != RTPpacket[1].payloadType) - && (RTPpacket[0].payloadType != WebRtcNetEQ_DbGetPayload(&MCU_inst->codec_DB_inst, - kDecoderAVT)) && (RTPpacket[1].payloadType != WebRtcNetEQ_DbGetPayload( - &MCU_inst->codec_DB_inst, kDecoderAVT)) && (!WebRtcNetEQ_DbIsCNGPayload( - &MCU_inst->codec_DB_inst, RTPpacket[0].payloadType)) - && (!WebRtcNetEQ_DbIsCNGPayload(&MCU_inst->codec_DB_inst, RTPpacket[1].payloadType))) - { - i_No_Of_Payloads = 1; - } - isREDPayload = 1; - } -#endif - - /* loop over the number of payloads */ - for (i_k = 0; i_k < i_No_Of_Payloads; i_k++) - { - - if (isREDPayload == 1) - { - RTPpacket[i_k].rcuPlCntr = i_k; - } - else - { - RTPpacket[i_k].rcuPlCntr = 0; - } - - /* Force update of SplitInfo if it's iLBC because of potential change between 20/30ms */ - if (RTPpacket[i_k].payloadType == WebRtcNetEQ_DbGetPayload(&MCU_inst->codec_DB_inst, - kDecoderILBC) && !is_sync_rtp) /* Don't update if sync RTP. */ - { - i_ok = WebRtcNetEQ_DbGetSplitInfo( - &MCU_inst->PayloadSplit_inst, - (enum WebRtcNetEQDecoder) WebRtcNetEQ_DbGetCodec(&MCU_inst->codec_DB_inst, - RTPpacket[i_k].payloadType), RTPpacket[i_k].payloadLen); - if (i_ok < 0) - { - /* error returned */ - return i_ok; - } - } - - /* Get information about timestamp scaling for this payload type */ - i_ok = WebRtcNetEQ_GetTimestampScaling(MCU_inst, RTPpacket[i_k].payloadType); - if (i_ok < 0) - { - /* error returned */ - return i_ok; - } - - if (MCU_inst->TSscalingInitialized == 0 && MCU_inst->scalingFactor != kTSnoScaling) - { - /* Must initialize scaling with current timestamps */ - MCU_inst->externalTS = RTPpacket[i_k].timeStamp; - MCU_inst->internalTS = RTPpacket[i_k].timeStamp; - MCU_inst->TSscalingInitialized = 1; - } - - /* Adjust timestamp if timestamp scaling is needed (e.g. SILK or G.722) */ - if (MCU_inst->TSscalingInitialized == 1) - { - uint32_t newTS = WebRtcNetEQ_ScaleTimestampExternalToInternal(MCU_inst, - RTPpacket[i_k].timeStamp); - - /* save the incoming timestamp for next time */ - MCU_inst->externalTS = RTPpacket[i_k].timeStamp; - - /* add the scaled difference to last scaled timestamp and save ... */ - MCU_inst->internalTS = newTS; - - RTPpacket[i_k].timeStamp = newTS; - } - - /* Is this a DTMF packet?*/ - if (RTPpacket[i_k].payloadType == WebRtcNetEQ_DbGetPayload(&MCU_inst->codec_DB_inst, - kDecoderAVT)) - { - if (is_sync_rtp) - { - /* Sync RTP should not have AVT payload type. */ - return RECIN_SYNC_RTP_NOT_ACCEPTABLE; - } - -#ifdef NETEQ_ATEVENT_DECODE - if (MCU_inst->AVT_PlayoutOn) - { - i_ok = WebRtcNetEQ_DtmfInsertEvent(&MCU_inst->DTMF_inst, - RTPpacket[i_k].payload, RTPpacket[i_k].payloadLen, - RTPpacket[i_k].timeStamp); - if (i_ok != 0) - { - return i_ok; - } - } -#endif -#ifdef NETEQ_STEREO - if (MCU_inst->usingStereo == 0) - { - /* do not set this for DTMF packets when using stereo mode */ - MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF = 1; - } -#else - MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF = 1; -#endif - } - else if (WebRtcNetEQ_DbIsCNGPayload(&MCU_inst->codec_DB_inst, - RTPpacket[i_k].payloadType)) - { - /* Is this a CNG packet? how should we handle this?*/ -#ifdef NETEQ_CNG_CODEC - /* Get CNG sample rate */ - uint16_t fsCng = WebRtcNetEQ_DbGetSampleRate(&MCU_inst->codec_DB_inst, - RTPpacket[i_k].payloadType); - if (is_sync_rtp) - { - /* Sync RTP should not have CNG payload type. */ - return RECIN_SYNC_RTP_NOT_ACCEPTABLE; - } - - /* Force sampling frequency to 32000 Hz CNG 48000 Hz. */ - /* TODO(tlegrand): remove limitation once ACM has full 48 kHz - * support. */ - if (fsCng > 32000) { - fsCng = 32000; - } - if ((fsCng != MCU_inst->fs) && (fsCng > 8000)) - { - /* - * We have received CNG with a different sample rate from what we are using - * now (must be > 8000, since we may use only one CNG type (default) for all - * frequencies). Flush buffer and signal new codec. - */ - WebRtcNetEQ_PacketBufferFlush(&MCU_inst->PacketBuffer_inst); - MCU_inst->new_codec = 1; - MCU_inst->current_Codec = -1; - } - i_ok = WebRtcNetEQ_PacketBufferInsert(&MCU_inst->PacketBuffer_inst, - &RTPpacket[i_k], &flushed, MCU_inst->av_sync); - if (i_ok < 0) - { - return RECIN_CNG_ERROR; - } - MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF = 1; -#else /* NETEQ_CNG_CODEC not defined */ - return RECIN_UNKNOWNPAYLOAD; -#endif /* NETEQ_CNG_CODEC */ - } - else - { - /* Reinitialize the splitting if the payload and/or the payload length has changed */ - curr_Codec = WebRtcNetEQ_DbGetCodec(&MCU_inst->codec_DB_inst, - RTPpacket[i_k].payloadType); - if (curr_Codec != MCU_inst->current_Codec) - { - if (curr_Codec < 0) - { - return RECIN_UNKNOWNPAYLOAD; - } - if (is_sync_rtp) - { - /* Sync RTP should not cause codec change. */ - return RECIN_SYNC_RTP_CHANGED_CODEC; - } - MCU_inst->current_Codec = curr_Codec; - MCU_inst->current_Payload = RTPpacket[i_k].payloadType; - i_ok = WebRtcNetEQ_DbGetSplitInfo(&MCU_inst->PayloadSplit_inst, - (enum WebRtcNetEQDecoder) MCU_inst->current_Codec, - RTPpacket[i_k].payloadLen); - if (i_ok < 0) - { /* error returned */ - return i_ok; - } - WebRtcNetEQ_PacketBufferFlush(&MCU_inst->PacketBuffer_inst); - MCU_inst->new_codec = 1; - } - - /* Parse the payload and insert it into the buffer */ - i_ok = WebRtcNetEQ_SplitAndInsertPayload(&RTPpacket[i_k], - &MCU_inst->PacketBuffer_inst, &MCU_inst->PayloadSplit_inst, - &flushed, MCU_inst->av_sync); - if (i_ok < 0) - { - return i_ok; - } - if (MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF != 0) - { - /* first normal packet after CNG or DTMF */ - MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF = -1; - } - } - /* Reset DSP timestamp etc. if packet buffer flushed */ - if (flushed) - { - MCU_inst->new_codec = 1; - } - } - - /* - * If not sync RTP, update Bandwidth Estimate. - * Only send the main payload to BWE. - */ - if (!is_sync_rtp && - (curr_Codec = WebRtcNetEQ_DbGetCodec(&MCU_inst->codec_DB_inst, - RTPpacket[0].payloadType)) >= 0) - { - codecPos = MCU_inst->codec_DB_inst.position[curr_Codec]; - if (MCU_inst->codec_DB_inst.funcUpdBWEst[codecPos] != NULL) /* codec has BWE function */ - { - if (RTPpacket[0].starts_byte1) /* check for shifted byte alignment */ - { - /* re-align to 16-bit alignment */ - for (i_k = 0; i_k < RTPpacket[0].payloadLen; i_k++) - { - WEBRTC_SPL_SET_BYTE(RTPpacket[0].payload, - WEBRTC_SPL_GET_BYTE(RTPpacket[0].payload, i_k+1), - i_k); - } - RTPpacket[0].starts_byte1 = 0; - } - - MCU_inst->codec_DB_inst.funcUpdBWEst[codecPos]( - MCU_inst->codec_DB_inst.codec_state[codecPos], - (const uint16_t *) RTPpacket[0].payload, - (int32_t) RTPpacket[0].payloadLen, RTPpacket[0].seqNumber, - (uint32_t) RTPpacket[0].timeStamp, (uint32_t) uw32_timeRec); - } - } - - if (MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF == 0) - { - /* Calculate the total speech length carried in each packet */ - temp_bufsize = WebRtcNetEQ_PacketBufferGetSize( - &MCU_inst->PacketBuffer_inst, &MCU_inst->codec_DB_inst, - MCU_inst->av_sync) - temp_bufsize; - - if ((temp_bufsize > 0) && (MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF - == 0) && (temp_bufsize - != MCU_inst->BufferStat_inst.Automode_inst.packetSpeechLenSamp)) - { - /* Change the auto-mode parameters if packet length has changed */ - WebRtcNetEQ_SetPacketSpeechLen(&(MCU_inst->BufferStat_inst.Automode_inst), - (int16_t) temp_bufsize, MCU_inst->fs); - } - - /* update statistics */ - if ((int32_t) (RTPpacket[0].timeStamp - MCU_inst->timeStamp) >= 0 - && !MCU_inst->new_codec) - { - /* - * Only update statistics if incoming packet is not older than last played out - * packet, and if new codec flag is not set. - */ - WebRtcNetEQ_UpdateIatStatistics(&MCU_inst->BufferStat_inst.Automode_inst, - MCU_inst->PacketBuffer_inst.maxInsertPositions, RTPpacket[0].seqNumber, - RTPpacket[0].timeStamp, MCU_inst->fs, - WebRtcNetEQ_DbIsMDCodec((enum WebRtcNetEQDecoder) MCU_inst->current_Codec), - (MCU_inst->NetEqPlayoutMode == kPlayoutStreaming)); - } - } - else if (MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF == -1) - { - /* - * This is first "normal" packet after CNG or DTMF. - * Reset packet time counter and measure time until next packet, - * but don't update statistics. - */ - MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF = 0; - MCU_inst->BufferStat_inst.Automode_inst.packetIatCountSamp = 0; - } - return 0; - -} - -int WebRtcNetEQ_GetTimestampScaling(MCUInst_t *MCU_inst, int rtpPayloadType) -{ - enum WebRtcNetEQDecoder codec; - int codecNumber; - - codecNumber = WebRtcNetEQ_DbGetCodec(&MCU_inst->codec_DB_inst, rtpPayloadType); - if (codecNumber < 0) - { - /* error */ - return codecNumber; - } - - /* cast to enumerator */ - codec = (enum WebRtcNetEQDecoder) codecNumber; - - /* - * The factor obtained below is the number with which the RTP timestamp must be - * multiplied to get the true sample count. - */ - switch (codec) - { - case kDecoderG722: - case kDecoderG722_2ch: - { - /* Use timestamp scaling with factor 2 (two output samples per RTP timestamp) */ - MCU_inst->scalingFactor = kTSscalingTwo; - break; - } - case kDecoderISACfb: - case kDecoderOpus: - { - /* We resample Opus internally to 32 kHz, and isac-fb decodes at - * 32 kHz, but timestamps are counted at 48 kHz. So there are two - * output samples per three RTP timestamp ticks. */ - MCU_inst->scalingFactor = kTSscalingTwoThirds; - break; - } - - case kDecoderAVT: - case kDecoderCNG: - { - /* TODO(tlegrand): remove scaling once ACM has full 48 kHz - * support. */ - uint16_t sample_freq = - WebRtcNetEQ_DbGetSampleRate(&MCU_inst->codec_DB_inst, - rtpPayloadType); - if (sample_freq == 48000) { - MCU_inst->scalingFactor = kTSscalingTwoThirds; - } - - /* For sample_freq <= 32 kHz, do not change the timestamp scaling - * settings. */ - break; - } - default: - { - /* do not use timestamp scaling */ - MCU_inst->scalingFactor = kTSnoScaling; - break; - } - } - return 0; -} - -uint32_t WebRtcNetEQ_ScaleTimestampExternalToInternal(const MCUInst_t *MCU_inst, - uint32_t externalTS) -{ - int32_t timestampDiff; - uint32_t internalTS; - - /* difference between this and last incoming timestamp */ - timestampDiff = externalTS - MCU_inst->externalTS; - - switch (MCU_inst->scalingFactor) - { - case kTSscalingTwo: - { - /* multiply with 2 */ - timestampDiff = WEBRTC_SPL_LSHIFT_W32(timestampDiff, 1); - break; - } - case kTSscalingTwoThirds: - { - /* multiply with 2/3 */ - timestampDiff = WEBRTC_SPL_LSHIFT_W32(timestampDiff, 1); - timestampDiff = WebRtcSpl_DivW32W16(timestampDiff, 3); - break; - } - case kTSscalingFourThirds: - { - /* multiply with 4/3 */ - timestampDiff = WEBRTC_SPL_LSHIFT_W32(timestampDiff, 2); - timestampDiff = WebRtcSpl_DivW32W16(timestampDiff, 3); - break; - } - default: - { - /* no scaling */ - } - } - - /* add the scaled difference to last scaled timestamp and save ... */ - internalTS = MCU_inst->internalTS + timestampDiff; - - return internalTS; -} - -uint32_t WebRtcNetEQ_ScaleTimestampInternalToExternal(const MCUInst_t *MCU_inst, - uint32_t internalTS) -{ - int32_t timestampDiff; - uint32_t externalTS; - - /* difference between this and last incoming timestamp */ - timestampDiff = (int32_t) internalTS - MCU_inst->internalTS; - - switch (MCU_inst->scalingFactor) - { - case kTSscalingTwo: - { - /* divide by 2 */ - timestampDiff = WEBRTC_SPL_RSHIFT_W32(timestampDiff, 1); - break; - } - case kTSscalingTwoThirds: - { - /* multiply with 3/2 */ - timestampDiff = WEBRTC_SPL_MUL_32_16(timestampDiff, 3); - timestampDiff = WEBRTC_SPL_RSHIFT_W32(timestampDiff, 1); - break; - } - case kTSscalingFourThirds: - { - /* multiply with 3/4 */ - timestampDiff = WEBRTC_SPL_MUL_32_16(timestampDiff, 3); - timestampDiff = WEBRTC_SPL_RSHIFT_W32(timestampDiff, 2); - break; - } - default: - { - /* no scaling */ - } - } - - /* add the scaled difference to last scaled timestamp and save ... */ - externalTS = MCU_inst->externalTS + timestampDiff; - - return externalTS; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/recout.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/recout.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/recout.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/recout.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1502 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Implementation of RecOut function, which is the main function for the audio output - * process. This function must be called (through the NetEQ API) once every 10 ms. - */ - -#include "dsp.h" - -#include -#include /* to define NULL */ - -#include "signal_processing_library.h" - -#include "dsp_helpfunctions.h" -#include "neteq_error_codes.h" -#include "neteq_defines.h" -#include "mcu_dsp_common.h" - -/* Audio types */ -#define TYPE_SPEECH 1 -#define TYPE_CNG 2 - -#ifdef NETEQ_DELAY_LOGGING -#include "delay_logging.h" -#include -#pragma message("*******************************************************************") -#pragma message("You have specified to use NETEQ_DELAY_LOGGING in the NetEQ library.") -#pragma message("Make sure that your test application supports this.") -#pragma message("*******************************************************************") -#endif - -/* Scratch usage: - - Type Name size startpos endpos - int16_t pw16_NetEqAlgorithm_buffer 1080*fs/8000 0 1080*fs/8000-1 - struct dspInfo 6 1080*fs/8000 1085*fs/8000 - - func WebRtcNetEQ_Normal 40+495*fs/8000 0 39+495*fs/8000 - func WebRtcNetEQ_Merge 40+496*fs/8000 0 39+496*fs/8000 - func WebRtcNetEQ_Expand 40+370*fs/8000 126*fs/800 39+496*fs/8000 - func WebRtcNetEQ_Accelerate 210 240*fs/8000 209+240*fs/8000 - func WebRtcNetEQ_BGNUpdate 69 480*fs/8000 68+480*fs/8000 - - Total: 1086*fs/8000 - */ - -#define SCRATCH_ALGORITHM_BUFFER 0 -#define SCRATCH_NETEQ_NORMAL 0 -#define SCRATCH_NETEQ_MERGE 0 - -#if (defined(NETEQ_48KHZ_WIDEBAND)) -#define SCRATCH_DSP_INFO 6480 -#define SCRATCH_NETEQ_ACCELERATE 1440 -#define SCRATCH_NETEQ_BGN_UPDATE 2880 -#define SCRATCH_NETEQ_EXPAND 756 -#elif (defined(NETEQ_32KHZ_WIDEBAND)) -#define SCRATCH_DSP_INFO 4320 -#define SCRATCH_NETEQ_ACCELERATE 960 -#define SCRATCH_NETEQ_BGN_UPDATE 1920 -#define SCRATCH_NETEQ_EXPAND 504 -#elif (defined(NETEQ_WIDEBAND)) -#define SCRATCH_DSP_INFO 2160 -#define SCRATCH_NETEQ_ACCELERATE 480 -#define SCRATCH_NETEQ_BGN_UPDATE 960 -#define SCRATCH_NETEQ_EXPAND 252 -#else /* NB */ -#define SCRATCH_DSP_INFO 1080 -#define SCRATCH_NETEQ_ACCELERATE 240 -#define SCRATCH_NETEQ_BGN_UPDATE 480 -#define SCRATCH_NETEQ_EXPAND 126 -#endif - -#if (defined(NETEQ_48KHZ_WIDEBAND)) -#define SIZE_SCRATCH_BUFFER 6516 -#elif (defined(NETEQ_32KHZ_WIDEBAND)) -#define SIZE_SCRATCH_BUFFER 4344 -#elif (defined(NETEQ_WIDEBAND)) -#define SIZE_SCRATCH_BUFFER 2172 -#else /* NB */ -#define SIZE_SCRATCH_BUFFER 1086 -#endif - -#ifdef NETEQ_DELAY_LOGGING -extern FILE *delay_fid2; /* file pointer to delay log file */ -extern uint32_t tot_received_packets; -#endif - - -int WebRtcNetEQ_RecOutInternal(DSPInst_t *inst, int16_t *pw16_outData, - int16_t *pw16_len, int16_t BGNonly, - int av_sync) -{ - - int16_t blockLen, payloadLen, len = 0, pos; - int16_t w16_tmp1, w16_tmp2, w16_tmp3, DataEnough; - int16_t *blockPtr; - int16_t MD = 0; - - int16_t speechType = TYPE_SPEECH; - uint16_t instr; - uint16_t uw16_tmp; -#ifdef SCRATCH - char pw8_ScratchBuffer[((SIZE_SCRATCH_BUFFER + 1) * 2)]; - int16_t *pw16_scratchPtr = (int16_t*) pw8_ScratchBuffer; - /* pad with 240*fs_mult to match the overflow guard below */ - int16_t pw16_decoded_buffer[NETEQ_MAX_FRAME_SIZE+240*6]; - int16_t *pw16_NetEqAlgorithm_buffer = pw16_scratchPtr - + SCRATCH_ALGORITHM_BUFFER; - DSP2MCU_info_t *dspInfo = (DSP2MCU_info_t*) (pw16_scratchPtr + SCRATCH_DSP_INFO); -#else - /* pad with 240*fs_mult to match the overflow guard below */ - int16_t pw16_decoded_buffer[NETEQ_MAX_FRAME_SIZE+240*6]; - int16_t pw16_NetEqAlgorithm_buffer[NETEQ_MAX_OUTPUT_SIZE+240*6]; - DSP2MCU_info_t dspInfoStruct; - DSP2MCU_info_t *dspInfo = &dspInfoStruct; -#endif - int16_t fs_mult; - int borrowedSamples; - int oldBorrowedSamples; - int return_value = 0; - int16_t lastModeBGNonly = (inst->w16_mode & MODE_BGN_ONLY) != 0; /* check BGN flag */ - void *mainInstBackup = inst->main_inst; - -#ifdef NETEQ_DELAY_LOGGING - int temp_var; -#endif - int16_t dtmfValue = -1; - int16_t dtmfVolume = -1; - int playDtmf = 0; -#ifdef NETEQ_ATEVENT_DECODE - int dtmfSwitch = 0; -#endif -#ifdef NETEQ_STEREO - MasterSlaveInfo *msInfo = inst->msInfo; -#endif - int16_t *sharedMem = pw16_NetEqAlgorithm_buffer; /* Reuse memory SHARED_MEM_SIZE size */ - inst->pw16_readAddress = sharedMem; - inst->pw16_writeAddress = sharedMem; - - /* Get information about if there is one descriptor left */ - if (inst->codec_ptr_inst.funcGetMDinfo != NULL) - { - MD = inst->codec_ptr_inst.funcGetMDinfo(inst->codec_ptr_inst.codec_state); - if (MD > 0) - MD = 1; - else - MD = 0; - } - -#ifdef NETEQ_STEREO - if ((msInfo->msMode == NETEQ_SLAVE) && (inst->codec_ptr_inst.funcDecode != NULL)) - { - /* - * Valid function pointers indicate that we have decoded something, - * and that the timestamp information is correct. - */ - - /* Get the information from master to correct synchronization */ - uint32_t currentMasterTimestamp; - uint32_t currentSlaveTimestamp; - - currentMasterTimestamp = msInfo->endTimestamp - msInfo->samplesLeftWithOverlap; - currentSlaveTimestamp = inst->endTimestamp - (inst->endPosition - inst->curPosition); - - /* Partition the uint32_t space in three: [0 0.25) [0.25 0.75] (0.75 1] - * We consider a wrap to have occurred if the timestamps are in - * different edge partitions. - */ - if (currentSlaveTimestamp < 0x40000000 && - currentMasterTimestamp > 0xc0000000) { - // Slave has wrapped. - currentSlaveTimestamp += (0xffffffff - currentMasterTimestamp) + 1; - currentMasterTimestamp = 0; - } else if (currentMasterTimestamp < 0x40000000 && - currentSlaveTimestamp > 0xc0000000) { - // Master has wrapped. - currentMasterTimestamp += (0xffffffff - currentSlaveTimestamp) + 1; - currentSlaveTimestamp = 0; - } - - if (currentSlaveTimestamp < currentMasterTimestamp) - { - /* brute-force discard a number of samples to catch up */ - inst->curPosition += currentMasterTimestamp - currentSlaveTimestamp; - - } - else if (currentSlaveTimestamp > currentMasterTimestamp) - { - /* back off current position to slow down */ - inst->curPosition -= currentSlaveTimestamp - currentMasterTimestamp; - } - - /* make sure we have at least "overlap" samples left */ - inst->curPosition = WEBRTC_SPL_MIN(inst->curPosition, - inst->endPosition - inst->ExpandInst.w16_overlap); - - /* make sure we do not end up outside the speech history */ - inst->curPosition = WEBRTC_SPL_MAX(inst->curPosition, 0); - } -#endif - - /* Write status data to shared memory */ - dspInfo->playedOutTS = inst->endTimestamp; - dspInfo->samplesLeft = inst->endPosition - inst->curPosition - - inst->ExpandInst.w16_overlap; - dspInfo->MD = MD; - dspInfo->lastMode = inst->w16_mode; - dspInfo->frameLen = inst->w16_frameLen; - - /* Force update of codec if codec function is NULL */ - if (inst->codec_ptr_inst.funcDecode == NULL) - { - dspInfo->lastMode |= MODE_AWAITING_CODEC_PTR; - } - -#ifdef NETEQ_STEREO - if (msInfo->msMode == NETEQ_SLAVE && (msInfo->extraInfo == DTMF_OVERDUB - || msInfo->extraInfo == DTMF_ONLY)) - { - /* Signal that the master instance generated DTMF tones */ - dspInfo->lastMode |= MODE_MASTER_DTMF_SIGNAL; - } - - if (msInfo->msMode != NETEQ_MONO) - { - /* We are using stereo mode; signal this to MCU side */ - dspInfo->lastMode |= MODE_USING_STEREO; - } -#endif - - WEBRTC_SPL_MEMCPY_W8(inst->pw16_writeAddress,dspInfo,sizeof(DSP2MCU_info_t)); - - /* Signal MCU with "interrupt" call to main inst*/ -#ifdef NETEQ_STEREO - assert(msInfo != NULL); - if (msInfo->msMode == NETEQ_MASTER) - { - /* clear info to slave */ - WebRtcSpl_MemSetW16((int16_t *) msInfo, 0, - sizeof(MasterSlaveInfo) / sizeof(int16_t)); - /* re-set mode */ - msInfo->msMode = NETEQ_MASTER; - - /* Store some information to slave */ - msInfo->endTimestamp = inst->endTimestamp; - msInfo->samplesLeftWithOverlap = inst->endPosition - inst->curPosition; - } -#endif - - /* - * This call will trigger the MCU side to make a decision based on buffer contents and - * decision history. Instructions, encoded data and function pointers will be written - * to the shared memory. - */ - return_value = WebRtcNetEQ_DSP2MCUinterrupt((MainInst_t *) inst->main_inst, sharedMem); - - /* Read MCU data and instructions */ - instr = (uint16_t) (inst->pw16_readAddress[0] & 0xf000); - -#ifdef NETEQ_STEREO - if (msInfo->msMode == NETEQ_MASTER) - { - msInfo->instruction = instr; - } - else if (msInfo->msMode == NETEQ_SLAVE) - { - /* Nothing to do */ - } -#endif - - /* check for error returned from MCU side, if so, return error */ - if (return_value < 0) - { - inst->w16_mode = MODE_ERROR; - dspInfo->lastMode = MODE_ERROR; - return return_value; - } - - blockPtr = &((inst->pw16_readAddress)[3]); - - /* Check for DTMF payload flag */ - if ((inst->pw16_readAddress[0] & DSP_DTMF_PAYLOAD) != 0) - { - playDtmf = 1; - dtmfValue = blockPtr[1]; - dtmfVolume = blockPtr[2]; - blockPtr += 3; - -#ifdef NETEQ_STEREO - if (msInfo->msMode == NETEQ_MASTER) - { - /* signal to slave that master is using DTMF */ - msInfo->extraInfo = DTMF_OVERDUB; - } -#endif - } - - blockLen = (((*blockPtr) & DSP_CODEC_MASK_RED_FLAG) + 1) >> 1; /* In # of int16_t */ - payloadLen = ((*blockPtr) & DSP_CODEC_MASK_RED_FLAG); - blockPtr++; - - /* Do we have to change our decoder? */ - if ((inst->pw16_readAddress[0] & 0x0f00) == DSP_CODEC_NEW_CODEC) - { - WEBRTC_SPL_MEMCPY_W16(&inst->codec_ptr_inst,blockPtr,(payloadLen+1)>>1); - if (inst->codec_ptr_inst.codec_fs != 0) - { - return_value = WebRtcNetEQ_DSPInit(inst, inst->codec_ptr_inst.codec_fs); - if (return_value != 0) - { /* error returned */ - instr = DSP_INSTR_FADE_TO_BGN; /* emergency instruction */ - } -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_CHANGE_FS; - if ((fwrite(&temp_var, sizeof(int), - 1, delay_fid2) != 1) || - (fwrite(&inst->fs, sizeof(uint16_t), - 1, delay_fid2) != 1)) { - return -1; - } -#endif - } - - /* Copy it again since the init destroys this part */ - - WEBRTC_SPL_MEMCPY_W16(&inst->codec_ptr_inst,blockPtr,(payloadLen+1)>>1); - inst->endTimestamp = inst->codec_ptr_inst.timeStamp; - inst->videoSyncTimestamp = inst->codec_ptr_inst.timeStamp; - blockPtr += blockLen; - blockLen = (((*blockPtr) & DSP_CODEC_MASK_RED_FLAG) + 1) >> 1; - payloadLen = ((*blockPtr) & DSP_CODEC_MASK_RED_FLAG); - blockPtr++; - if (inst->codec_ptr_inst.funcDecodeInit != NULL) - { - inst->codec_ptr_inst.funcDecodeInit(inst->codec_ptr_inst.codec_state); - } - -#ifdef NETEQ_CNG_CODEC - - /* Also update the CNG state as this might be uninitialized */ - - WEBRTC_SPL_MEMCPY_W16(&inst->CNG_Codec_inst,blockPtr,(payloadLen+1)>>1); - blockPtr += blockLen; - blockLen = (((*blockPtr) & DSP_CODEC_MASK_RED_FLAG) + 1) >> 1; - payloadLen = ((*blockPtr) & DSP_CODEC_MASK_RED_FLAG); - blockPtr++; - if (inst->CNG_Codec_inst != NULL) - { - WebRtcCng_InitDec(inst->CNG_Codec_inst); - } -#endif - } - else if ((inst->pw16_readAddress[0] & 0x0f00) == DSP_CODEC_RESET) - { - /* Reset the current codec (but not DSP struct) */ - if (inst->codec_ptr_inst.funcDecodeInit != NULL) - { - inst->codec_ptr_inst.funcDecodeInit(inst->codec_ptr_inst.codec_state); - } - -#ifdef NETEQ_CNG_CODEC - /* And reset CNG */ - if (inst->CNG_Codec_inst != NULL) - { - WebRtcCng_InitDec(inst->CNG_Codec_inst); - } -#endif /*NETEQ_CNG_CODEC*/ - } - - fs_mult = WebRtcNetEQ_CalcFsMult(inst->fs); - - /* Add late packet? */ - if ((inst->pw16_readAddress[0] & 0x0f00) == DSP_CODEC_ADD_LATE_PKT) - { - if (inst->codec_ptr_inst.funcAddLatePkt != NULL) - { - /* Only do this if the codec has support for Add Late Pkt */ - inst->codec_ptr_inst.funcAddLatePkt(inst->codec_ptr_inst.codec_state, blockPtr, - payloadLen); - } - blockPtr += blockLen; - blockLen = (((*blockPtr) & DSP_CODEC_MASK_RED_FLAG) + 1) >> 1; /* In # of Word16 */ - payloadLen = ((*blockPtr) & DSP_CODEC_MASK_RED_FLAG); - blockPtr++; - } - - /* Do we have to decode data? */ - if ((instr == DSP_INSTR_NORMAL) || (instr == DSP_INSTR_ACCELERATE) || (instr - == DSP_INSTR_MERGE) || (instr == DSP_INSTR_PREEMPTIVE_EXPAND)) - { - /* Do we need to update codec-internal PLC state? */ - if ((instr == DSP_INSTR_MERGE) && (inst->codec_ptr_inst.funcDecodePLC != NULL)) - { - len = 0; - len = inst->codec_ptr_inst.funcDecodePLC(inst->codec_ptr_inst.codec_state, - &pw16_decoded_buffer[len], 1); - } - len = 0; - - /* Do decoding */ - while ((blockLen > 0) && (len < (240 * fs_mult))) /* Guard somewhat against overflow */ - { - if (inst->codec_ptr_inst.funcDecode != NULL) - { - int16_t dec_Len; - if (!BGNonly) - { - /* Check if this is a sync payload. */ - if (av_sync && WebRtcNetEQ_IsSyncPayload(blockPtr, - payloadLen)) { - /* Zero-stuffing with same size as the last frame. */ - dec_Len = inst->w16_frameLen; - memset(&pw16_decoded_buffer[len], 0, dec_Len * - sizeof(pw16_decoded_buffer[len])); - } else { - /* Do decoding as normal - * - * blockPtr is pointing to payload, at this point, - * the most significant bit of *(blockPtr - 1) is a flag if - * set to 1 indicates that the following payload is the - * redundant payload. - */ - if (((*(blockPtr - 1) & DSP_CODEC_RED_FLAG) != 0) - && (inst->codec_ptr_inst.funcDecodeRCU != NULL)) - { - dec_Len = inst->codec_ptr_inst.funcDecodeRCU( - inst->codec_ptr_inst.codec_state, blockPtr, - payloadLen, &pw16_decoded_buffer[len], &speechType); - } - else - { - /* Regular decoding. */ - dec_Len = inst->codec_ptr_inst.funcDecode( - inst->codec_ptr_inst.codec_state, blockPtr, - payloadLen, &pw16_decoded_buffer[len], &speechType); - } - } - } - else - { - /* - * Background noise mode: don't decode, just produce the same length BGN. - * Don't call Expand for BGN here, since Expand uses the memory where the - * bitstreams are stored (sharemem). - */ - dec_Len = inst->w16_frameLen; - } - - if (dec_Len > 0) - { - len += dec_Len; - /* Update frameLen */ - inst->w16_frameLen = dec_Len; - } - else if (dec_Len < 0) - { - /* Error */ - len = -1; - break; - } - /* - * Sanity check (although we might still write outside memory when this - * happens...) - */ - if (len > NETEQ_MAX_FRAME_SIZE) - { - WebRtcSpl_MemSetW16(pw16_outData, 0, inst->timestampsPerCall); - *pw16_len = inst->timestampsPerCall; - inst->w16_mode = MODE_ERROR; - dspInfo->lastMode = MODE_ERROR; - return RECOUT_ERROR_DECODED_TOO_MUCH; - } - - /* Verify that instance was not corrupted by decoder */ - if (mainInstBackup != inst->main_inst) - { - /* Instance is corrupt */ - return CORRUPT_INSTANCE; - } - - } - blockPtr += blockLen; - blockLen = (((*blockPtr) & DSP_CODEC_MASK_RED_FLAG) + 1) >> 1; /* In # of Word16 */ - payloadLen = ((*blockPtr) & DSP_CODEC_MASK_RED_FLAG); - blockPtr++; - } - - if (len < 0) - { - len = 0; - inst->endTimestamp += inst->w16_frameLen; /* advance one frame */ - if (inst->codec_ptr_inst.funcGetErrorCode != NULL) - { - return_value = -inst->codec_ptr_inst.funcGetErrorCode( - inst->codec_ptr_inst.codec_state); - } - else - { - return_value = RECOUT_ERROR_DECODING; - } - instr = DSP_INSTR_FADE_TO_BGN; - } - if (speechType != TYPE_CNG) - { - /* - * Don't increment timestamp if codec returned CNG speech type - * since in this case, the MCU side will increment the CNGplayedTS counter. - */ - inst->endTimestamp += len; - } - } - else if (instr == DSP_INSTR_NORMAL_ONE_DESC) - { - if (inst->codec_ptr_inst.funcDecode != NULL) - { - len = inst->codec_ptr_inst.funcDecode(inst->codec_ptr_inst.codec_state, NULL, 0, - pw16_decoded_buffer, &speechType); -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_DECODE_ONE_DESC; - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } - if (fwrite(&inst->endTimestamp, sizeof(uint32_t), - 1, delay_fid2) != 1) { - return -1; - } - if (fwrite(&dspInfo->samplesLeft, sizeof(uint16_t), - 1, delay_fid2) != 1) { - return -1; - } - tot_received_packets++; -#endif - } - if (speechType != TYPE_CNG) - { - /* - * Don't increment timestamp if codec returned CNG speech type - * since in this case, the MCU side will increment the CNGplayedTS counter. - */ - inst->endTimestamp += len; - } - - /* Verify that instance was not corrupted by decoder */ - if (mainInstBackup != inst->main_inst) - { - /* Instance is corrupt */ - return CORRUPT_INSTANCE; - } - - if (len <= 0) - { - len = 0; - if (inst->codec_ptr_inst.funcGetErrorCode != NULL) - { - return_value = -inst->codec_ptr_inst.funcGetErrorCode( - inst->codec_ptr_inst.codec_state); - } - else - { - return_value = RECOUT_ERROR_DECODING; - } - if ((inst->codec_ptr_inst.funcDecodeInit != NULL) - && (inst->codec_ptr_inst.codec_state != NULL)) - { - /* Reinitialize codec state as something is obviously wrong */ - inst->codec_ptr_inst.funcDecodeInit(inst->codec_ptr_inst.codec_state); - } - inst->endTimestamp += inst->w16_frameLen; /* advance one frame */ - instr = DSP_INSTR_FADE_TO_BGN; - } - } - - if (len == 0 && lastModeBGNonly) /* no new data */ - { - BGNonly = 1; /* force BGN this time too */ - } - -#ifdef NETEQ_VAD - if ((speechType == TYPE_CNG) /* decoder responded with codec-internal CNG */ - || ((instr == DSP_INSTR_DO_RFC3389CNG) && (blockLen > 0)) /* ... or, SID frame */ - || (inst->fs > 16000)) /* ... or, if not NB or WB */ - { - /* disable post-decode VAD upon first sign of send-side DTX/VAD active, or if SWB */ - inst->VADInst.VADEnabled = 0; - inst->VADInst.VADDecision = 1; /* set to always active, just to be on the safe side */ - inst->VADInst.SIDintervalCounter = 0; /* reset SID interval counter */ - } - else if (!inst->VADInst.VADEnabled) /* VAD disabled and no SID/CNG data observed this time */ - { - inst->VADInst.SIDintervalCounter++; /* increase counter */ - } - - /* check for re-enabling the VAD */ - if (inst->VADInst.SIDintervalCounter >= POST_DECODE_VAD_AUTO_ENABLE) - { - /* - * It's been a while since the last CNG/SID frame was observed => re-enable VAD. - * (Do not care to look for a VAD instance, since this is done inside the init - * function) - */ - WebRtcNetEQ_InitVAD(&inst->VADInst, inst->fs); - } - - if (len > 0 /* if we decoded any data */ - && inst->VADInst.VADEnabled /* and VAD enabled */ - && inst->fs <= 16000) /* can only do VAD for NB and WB */ - { - int VADframeSize; /* VAD frame size in ms */ - int VADSamplePtr = 0; - - inst->VADInst.VADDecision = 0; - - if (inst->VADInst.VADFunction != NULL) /* make sure that VAD function is provided */ - { - /* divide the data into groups, as large as possible */ - for (VADframeSize = 30; VADframeSize >= 10; VADframeSize -= 10) - { - /* loop through 30, 20, 10 */ - - while (inst->VADInst.VADDecision == 0 - && len - VADSamplePtr >= VADframeSize * fs_mult * 8) - { - /* - * Only continue until first active speech found, and as long as there is - * one VADframeSize left. - */ - - /* call VAD with new decoded data */ - inst->VADInst.VADDecision |= inst->VADInst.VADFunction( - inst->VADInst.VADState, (int) inst->fs, - (int16_t *) &pw16_decoded_buffer[VADSamplePtr], - (VADframeSize * fs_mult * 8)); - - VADSamplePtr += VADframeSize * fs_mult * 8; /* increment sample counter */ - } - } - } - else - { /* VAD function is NULL */ - inst->VADInst.VADDecision = 1; /* set decision to active */ - inst->VADInst.VADEnabled = 0; /* disable VAD since we have no VAD function */ - } - - } -#endif /* NETEQ_VAD */ - - /* Adjust timestamp if needed */ - uw16_tmp = (uint16_t) inst->pw16_readAddress[1]; - inst->endTimestamp += (((uint32_t) uw16_tmp) << 16); - uw16_tmp = (uint16_t) inst->pw16_readAddress[2]; - inst->endTimestamp += uw16_tmp; - - if (BGNonly && len > 0) - { - /* - * If BGN mode, we did not produce any data at decoding. - * Do it now instead. - */ - - WebRtcNetEQ_GenerateBGN(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_EXPAND, -#endif - pw16_decoded_buffer, len); - } - - /* Switch on the instruction received from the MCU side. */ - switch (instr) - { - case DSP_INSTR_NORMAL: - - /* Allow for signal processing to apply gain-back etc */ - WebRtcNetEQ_Normal(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_NORMAL, -#endif - pw16_decoded_buffer, len, pw16_NetEqAlgorithm_buffer, &len); - - /* If last packet was decoded as a inband CNG set mode to CNG instead */ - if ((speechType == TYPE_CNG) || ((inst->w16_mode == MODE_CODEC_INTERNAL_CNG) - && (len == 0))) - { - inst->w16_mode = MODE_CODEC_INTERNAL_CNG; - } - -#ifdef NETEQ_ATEVENT_DECODE - if (playDtmf == 0) - { - inst->DTMFInst.reinit = 1; - } -#endif - break; - case DSP_INSTR_NORMAL_ONE_DESC: - - /* Allow for signal processing to apply gain-back etc */ - WebRtcNetEQ_Normal(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_NORMAL, -#endif - pw16_decoded_buffer, len, pw16_NetEqAlgorithm_buffer, &len); -#ifdef NETEQ_ATEVENT_DECODE - if (playDtmf == 0) - { - inst->DTMFInst.reinit = 1; - } -#endif - inst->w16_mode = MODE_ONE_DESCRIPTOR; - break; - case DSP_INSTR_MERGE: -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_MERGE_INFO; - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } - temp_var = -len; -#endif - /* Call Merge with history*/ - return_value = WebRtcNetEQ_Merge(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_MERGE, -#endif - pw16_decoded_buffer, len, pw16_NetEqAlgorithm_buffer, &len); - - if (return_value < 0) - { - /* error */ - return return_value; - } - -#ifdef NETEQ_DELAY_LOGGING - temp_var += len; - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } -#endif - /* If last packet was decoded as a inband CNG set mode to CNG instead */ - if (speechType == TYPE_CNG) inst->w16_mode = MODE_CODEC_INTERNAL_CNG; -#ifdef NETEQ_ATEVENT_DECODE - if (playDtmf == 0) - { - inst->DTMFInst.reinit = 1; - } -#endif - break; - - case DSP_INSTR_EXPAND: - len = 0; - pos = 0; - while ((inst->endPosition - inst->curPosition - inst->ExpandInst.w16_overlap + pos) - < (inst->timestampsPerCall)) - { - return_value = WebRtcNetEQ_Expand(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_EXPAND, -#endif - pw16_NetEqAlgorithm_buffer, &len, BGNonly); - if (return_value < 0) - { - /* error */ - return return_value; - } - - /* - * Update buffer, but only end part (otherwise expand state is destroyed - * since it reuses speechBuffer[] memory - */ - - WEBRTC_SPL_MEMMOVE_W16(inst->pw16_speechHistory, - inst->pw16_speechHistory + len, - (inst->w16_speechHistoryLen-len)); - WEBRTC_SPL_MEMCPY_W16(&inst->pw16_speechHistory[inst->w16_speechHistoryLen-len], - pw16_NetEqAlgorithm_buffer, len); - - inst->curPosition -= len; - - /* Update variables for VQmon */ - inst->w16_concealedTS += len; -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_EXPAND_INFO; - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } - temp_var = len; - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } -#endif - len = 0; /* already written the data, so do not write it again further down. */ - } -#ifdef NETEQ_ATEVENT_DECODE - if (playDtmf == 0) - { - inst->DTMFInst.reinit = 1; - } -#endif - break; - - case DSP_INSTR_ACCELERATE: - if (len < 3 * 80 * fs_mult) - { - /* We need to move data from the speechBuffer[] in order to get 30 ms */ - borrowedSamples = 3 * 80 * fs_mult - len; - - WEBRTC_SPL_MEMMOVE_W16(&pw16_decoded_buffer[borrowedSamples], - pw16_decoded_buffer, len); - WEBRTC_SPL_MEMCPY_W16(pw16_decoded_buffer, - &(inst->speechBuffer[inst->endPosition-borrowedSamples]), - borrowedSamples); - - return_value = WebRtcNetEQ_Accelerate(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_ACCELERATE, -#endif - pw16_decoded_buffer, 3 * inst->timestampsPerCall, - pw16_NetEqAlgorithm_buffer, &len, BGNonly); - - if (return_value < 0) - { - /* error */ - return return_value; - } - - /* Copy back samples to the buffer */ - if (len < borrowedSamples) - { - /* - * This destroys the beginning of the buffer, but will not cause any - * problems - */ - - WEBRTC_SPL_MEMCPY_W16(&inst->speechBuffer[inst->endPosition-borrowedSamples], - pw16_NetEqAlgorithm_buffer, len); - WEBRTC_SPL_MEMMOVE_W16(&inst->speechBuffer[borrowedSamples-len], - inst->speechBuffer, - (inst->endPosition-(borrowedSamples-len))); - - inst->curPosition += (borrowedSamples - len); -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_ACCELERATE_INFO; - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } - temp_var = 3 * inst->timestampsPerCall - len; - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } -#endif - len = 0; - } - else - { - WEBRTC_SPL_MEMCPY_W16(&inst->speechBuffer[inst->endPosition-borrowedSamples], - pw16_NetEqAlgorithm_buffer, borrowedSamples); - WEBRTC_SPL_MEMMOVE_W16(pw16_NetEqAlgorithm_buffer, - &pw16_NetEqAlgorithm_buffer[borrowedSamples], - (len-borrowedSamples)); -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_ACCELERATE_INFO; - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } - temp_var = 3 * inst->timestampsPerCall - len; - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } -#endif - len = len - borrowedSamples; - } - - } - else - { -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_ACCELERATE_INFO; - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } - temp_var = len; -#endif - return_value = WebRtcNetEQ_Accelerate(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_ACCELERATE, -#endif - pw16_decoded_buffer, len, pw16_NetEqAlgorithm_buffer, &len, BGNonly); - - if (return_value < 0) - { - /* error */ - return return_value; - } - -#ifdef NETEQ_DELAY_LOGGING - temp_var -= len; - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } -#endif - } - /* If last packet was decoded as a inband CNG set mode to CNG instead */ - if (speechType == TYPE_CNG) inst->w16_mode = MODE_CODEC_INTERNAL_CNG; -#ifdef NETEQ_ATEVENT_DECODE - if (playDtmf == 0) - { - inst->DTMFInst.reinit = 1; - } -#endif - break; - - case DSP_INSTR_DO_RFC3389CNG: -#ifdef NETEQ_CNG_CODEC - if (blockLen > 0) - { - if (WebRtcCng_UpdateSid(inst->CNG_Codec_inst, (uint8_t*) blockPtr, - payloadLen) < 0) - { - /* error returned from CNG function */ - return_value = -WebRtcCng_GetErrorCodeDec(inst->CNG_Codec_inst); - len = inst->timestampsPerCall; - WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0, len); - break; - } - } - - if (BGNonly) - { - /* Get data from BGN function instead of CNG */ - len = WebRtcNetEQ_GenerateBGN(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_EXPAND, -#endif - pw16_NetEqAlgorithm_buffer, inst->timestampsPerCall); - if (len != inst->timestampsPerCall) - { - /* this is not good, treat this as an error */ - return_value = -1; - } - } - else - { - return_value = WebRtcNetEQ_Cng(inst, pw16_NetEqAlgorithm_buffer, - inst->timestampsPerCall); - } - len = inst->timestampsPerCall; - inst->ExpandInst.w16_consecExp = 0; - inst->w16_mode = MODE_RFC3389CNG; -#ifdef NETEQ_ATEVENT_DECODE - if (playDtmf == 0) - { - inst->DTMFInst.reinit = 1; - } -#endif - - if (return_value < 0) - { - /* error returned */ - WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0, len); - } - - break; -#else - return FAULTY_INSTRUCTION; -#endif - case DSP_INSTR_DO_CODEC_INTERNAL_CNG: - /* - * This represents the case when there is no transmission and the decoder should - * do internal CNG. - */ - len = 0; - if (inst->codec_ptr_inst.funcDecode != NULL && !BGNonly) - { - len = inst->codec_ptr_inst.funcDecode(inst->codec_ptr_inst.codec_state, - blockPtr, 0, pw16_decoded_buffer, &speechType); - } - else - { - /* get BGN data */ - len = WebRtcNetEQ_GenerateBGN(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_EXPAND, -#endif - pw16_decoded_buffer, inst->timestampsPerCall); - } - WebRtcNetEQ_Normal(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_NORMAL, -#endif - pw16_decoded_buffer, len, pw16_NetEqAlgorithm_buffer, &len); - inst->w16_mode = MODE_CODEC_INTERNAL_CNG; - inst->ExpandInst.w16_consecExp = 0; - break; - - case DSP_INSTR_DTMF_GENERATE: -#ifdef NETEQ_ATEVENT_DECODE - dtmfSwitch = 0; - if ((inst->w16_mode != MODE_DTMF) && (inst->DTMFInst.reinit == 0)) - { - /* Special case; see below. - * We must catch this before calling DTMFGenerate, - * since reinit is set to 0 in that call. - */ - dtmfSwitch = 1; - } - - len = WebRtcNetEQ_DTMFGenerate(&inst->DTMFInst, dtmfValue, dtmfVolume, - pw16_NetEqAlgorithm_buffer, inst->fs, -1); - if (len < 0) - { - /* error occurred */ - return_value = len; - len = inst->timestampsPerCall; - WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0, len); - } - - if (dtmfSwitch == 1) - { - /* - * This is the special case where the previous operation was DTMF overdub. - * but the current instruction is "regular" DTMF. We must make sure that the - * DTMF does not have any discontinuities. The first DTMF sample that we - * generate now must be played out immediately, wherefore it must be copied to - * the speech buffer. - */ - - /* - * Generate extra DTMF data to fill the space between - * curPosition and endPosition - */ - int16_t tempLen; - - tempLen = WebRtcNetEQ_DTMFGenerate(&inst->DTMFInst, dtmfValue, dtmfVolume, - &pw16_NetEqAlgorithm_buffer[len], inst->fs, - inst->endPosition - inst->curPosition); - if (tempLen < 0) - { - /* error occurred */ - return_value = tempLen; - len = inst->endPosition - inst->curPosition; - WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0, - inst->endPosition - inst->curPosition); - } - - /* Add to total length */ - len += tempLen; - - /* Overwrite the "future" part of the speech buffer with the new DTMF data */ - - WEBRTC_SPL_MEMCPY_W16(&inst->speechBuffer[inst->curPosition], - pw16_NetEqAlgorithm_buffer, - inst->endPosition - inst->curPosition); - - /* Shuffle the remaining data to the beginning of algorithm buffer */ - len -= (inst->endPosition - inst->curPosition); - WEBRTC_SPL_MEMMOVE_W16(pw16_NetEqAlgorithm_buffer, - &pw16_NetEqAlgorithm_buffer[inst->endPosition - inst->curPosition], - len); - } - - inst->endTimestamp += inst->timestampsPerCall; - inst->DTMFInst.reinit = 0; - inst->ExpandInst.w16_consecExp = 0; - inst->w16_mode = MODE_DTMF; - BGNonly = 0; /* override BGN only and let DTMF through */ - - playDtmf = 0; /* set to zero because the DTMF is already in the Algorithm buffer */ - /* - * If playDtmf is 1, an extra DTMF vector will be generated and overdubbed - * on the output. - */ - -#ifdef NETEQ_STEREO - if (msInfo->msMode == NETEQ_MASTER) - { - /* signal to slave that master is using DTMF only */ - msInfo->extraInfo = DTMF_ONLY; - } -#endif - - break; -#else - inst->w16_mode = MODE_ERROR; - dspInfo->lastMode = MODE_ERROR; - return FAULTY_INSTRUCTION; -#endif - - case DSP_INSTR_DO_ALTERNATIVE_PLC: - if (inst->codec_ptr_inst.funcDecodePLC != 0) - { - len = inst->codec_ptr_inst.funcDecodePLC(inst->codec_ptr_inst.codec_state, - pw16_NetEqAlgorithm_buffer, 1); - } - else - { - len = inst->timestampsPerCall; - /* ZeroStuffing... */ - WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0, len); - /* By not advancing the timestamp, NetEq inserts samples. */ - inst->statInst.addedSamples += len; - } - inst->ExpandInst.w16_consecExp = 0; - break; - case DSP_INSTR_DO_ALTERNATIVE_PLC_INC_TS: - if (inst->codec_ptr_inst.funcDecodePLC != 0) - { - len = inst->codec_ptr_inst.funcDecodePLC(inst->codec_ptr_inst.codec_state, - pw16_NetEqAlgorithm_buffer, 1); - } - else - { - len = inst->timestampsPerCall; - /* ZeroStuffing... */ - WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0, len); - } - inst->ExpandInst.w16_consecExp = 0; - inst->endTimestamp += len; - break; - case DSP_INSTR_DO_AUDIO_REPETITION: - len = inst->timestampsPerCall; - /* copy->paste... */ - WEBRTC_SPL_MEMCPY_W16(pw16_NetEqAlgorithm_buffer, - &inst->speechBuffer[inst->endPosition-len], len); - inst->ExpandInst.w16_consecExp = 0; - break; - case DSP_INSTR_DO_AUDIO_REPETITION_INC_TS: - len = inst->timestampsPerCall; - /* copy->paste... */ - WEBRTC_SPL_MEMCPY_W16(pw16_NetEqAlgorithm_buffer, - &inst->speechBuffer[inst->endPosition-len], len); - inst->ExpandInst.w16_consecExp = 0; - inst->endTimestamp += len; - break; - - case DSP_INSTR_PREEMPTIVE_EXPAND: - if (len < 3 * inst->timestampsPerCall) - { - /* borrow samples from sync buffer if necessary */ - borrowedSamples = 3 * inst->timestampsPerCall - len; /* borrow this many samples */ - /* calculate how many of these are already played out */ - oldBorrowedSamples = WEBRTC_SPL_MAX(0, - borrowedSamples - (inst->endPosition - inst->curPosition)); - WEBRTC_SPL_MEMMOVE_W16(&pw16_decoded_buffer[borrowedSamples], - pw16_decoded_buffer, len); - WEBRTC_SPL_MEMCPY_W16(pw16_decoded_buffer, - &(inst->speechBuffer[inst->endPosition-borrowedSamples]), - borrowedSamples); - } - else - { - borrowedSamples = 0; - oldBorrowedSamples = 0; - } - -#ifdef NETEQ_DELAY_LOGGING - w16_tmp1 = len; -#endif - /* do the expand */ - return_value = WebRtcNetEQ_PreEmptiveExpand(inst, -#ifdef SCRATCH - /* use same scratch memory as Accelerate */ - pw16_scratchPtr + SCRATCH_NETEQ_ACCELERATE, -#endif - pw16_decoded_buffer, len + borrowedSamples, oldBorrowedSamples, - pw16_NetEqAlgorithm_buffer, &len, BGNonly); - - if (return_value < 0) - { - /* error */ - return return_value; - } - - if (borrowedSamples > 0) - { - /* return borrowed samples */ - - /* Copy back to last part of speechBuffer from beginning of output buffer */ - WEBRTC_SPL_MEMCPY_W16( &(inst->speechBuffer[inst->endPosition-borrowedSamples]), - pw16_NetEqAlgorithm_buffer, - borrowedSamples); - - len -= borrowedSamples; /* remove the borrowed samples from new total length */ - - /* Move to beginning of output buffer from end of output buffer */ - WEBRTC_SPL_MEMMOVE_W16( pw16_NetEqAlgorithm_buffer, - &pw16_NetEqAlgorithm_buffer[borrowedSamples], - len); - } - -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_PREEMPTIVE_INFO; - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } - temp_var = len - w16_tmp1; /* number of samples added */ - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } -#endif - /* If last packet was decoded as inband CNG, set mode to CNG instead */ - if (speechType == TYPE_CNG) inst->w16_mode = MODE_CODEC_INTERNAL_CNG; -#ifdef NETEQ_ATEVENT_DECODE - if (playDtmf == 0) - { - inst->DTMFInst.reinit = 1; - } -#endif - break; - - case DSP_INSTR_FADE_TO_BGN: - { - int tempReturnValue; - /* do not overwrite return_value, since it likely contains an error code */ - - /* calculate interpolation length */ - w16_tmp3 = WEBRTC_SPL_MIN(inst->endPosition - inst->curPosition, - inst->timestampsPerCall); - /* check that it will fit in pw16_NetEqAlgorithm_buffer */ - if (w16_tmp3 + inst->w16_frameLen > NETEQ_MAX_OUTPUT_SIZE) - { - w16_tmp3 = NETEQ_MAX_OUTPUT_SIZE - inst->w16_frameLen; - } - - /* call Expand */ - len = inst->timestampsPerCall + inst->ExpandInst.w16_overlap; - pos = 0; - - tempReturnValue = WebRtcNetEQ_Expand(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_EXPAND, -#endif - pw16_NetEqAlgorithm_buffer, &len, 1); - - if (tempReturnValue < 0) - { - /* error */ - /* this error value will override return_value */ - return tempReturnValue; - } - - pos += len; /* got len samples from expand */ - - /* copy to fill the demand */ - while (pos + len <= inst->w16_frameLen + w16_tmp3) - { - WEBRTC_SPL_MEMCPY_W16(&pw16_NetEqAlgorithm_buffer[pos], - pw16_NetEqAlgorithm_buffer, len); - pos += len; - } - - /* fill with fraction of the expand vector if needed */ - if (pos < inst->w16_frameLen + w16_tmp3) - { - WEBRTC_SPL_MEMCPY_W16(&pw16_NetEqAlgorithm_buffer[pos], pw16_NetEqAlgorithm_buffer, - inst->w16_frameLen + w16_tmp3 - pos); - } - - len = inst->w16_frameLen + w16_tmp3; /* truncate any surplus samples since we don't want these */ - - /* - * Mix with contents in sync buffer. Find largest power of two that is less than - * interpolate length divide 16384 with this number; result is in w16_tmp2. - */ - w16_tmp1 = 2; - w16_tmp2 = 16384; - while (w16_tmp1 <= w16_tmp3) - { - w16_tmp2 >>= 1; /* divide with 2 */ - w16_tmp1 <<= 1; /* increase with a factor of 2 */ - } - - w16_tmp1 = 0; - pos = 0; - while (w16_tmp1 < 16384) - { - inst->speechBuffer[inst->curPosition + pos] - = - (int16_t) WEBRTC_SPL_RSHIFT_W32( - WEBRTC_SPL_MUL_16_16( inst->speechBuffer[inst->endPosition - w16_tmp3 + pos], - 16384-w16_tmp1 ) + - WEBRTC_SPL_MUL_16_16( pw16_NetEqAlgorithm_buffer[pos], w16_tmp1 ), - 14 ); - w16_tmp1 += w16_tmp2; - pos++; - } - - /* overwrite remainder of speech buffer */ - - WEBRTC_SPL_MEMCPY_W16( &inst->speechBuffer[inst->endPosition - w16_tmp3 + pos], - &pw16_NetEqAlgorithm_buffer[pos], w16_tmp3 - pos); - - len -= w16_tmp3; - /* shift algorithm buffer */ - - WEBRTC_SPL_MEMMOVE_W16( pw16_NetEqAlgorithm_buffer, - &pw16_NetEqAlgorithm_buffer[w16_tmp3], - len ); - - /* Update variables for VQmon */ - inst->w16_concealedTS += len; - - inst->w16_mode = MODE_FADE_TO_BGN; -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_EXPAND_INFO; - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } - temp_var = len; - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } -#endif - - break; - } - - default: - inst->w16_mode = MODE_ERROR; - dspInfo->lastMode = MODE_ERROR; - return FAULTY_INSTRUCTION; - } /* end of grand switch */ - - /* Copy data directly to output buffer */ - - w16_tmp2 = 0; - if ((inst->endPosition + len - inst->curPosition - inst->ExpandInst.w16_overlap) - >= inst->timestampsPerCall) - { - w16_tmp2 = inst->endPosition - inst->curPosition; - w16_tmp2 = WEBRTC_SPL_MAX(w16_tmp2, 0); /* Additional error protection, just in case */ - w16_tmp1 = WEBRTC_SPL_MIN(w16_tmp2, inst->timestampsPerCall); - w16_tmp2 = inst->timestampsPerCall - w16_tmp1; - WEBRTC_SPL_MEMCPY_W16(pw16_outData, &inst->speechBuffer[inst->curPosition], w16_tmp1); - WEBRTC_SPL_MEMCPY_W16(&pw16_outData[w16_tmp1], pw16_NetEqAlgorithm_buffer, w16_tmp2); - DataEnough = 1; - } - else - { - DataEnough = 0; - } - - if (playDtmf != 0) - { -#ifdef NETEQ_ATEVENT_DECODE - int16_t outDataIndex = 0; - int16_t overdubLen = -1; /* default len */ - int16_t dtmfLen; - - /* - * Overdub the output with DTMF. Note that this is not executed if the - * DSP_INSTR_DTMF_GENERATE operation is performed above. - */ - if (inst->DTMFInst.lastDtmfSample - inst->curPosition > 0) - { - /* special operation for transition from "DTMF only" to "DTMF overdub" */ - outDataIndex - = WEBRTC_SPL_MIN(inst->DTMFInst.lastDtmfSample - inst->curPosition, - inst->timestampsPerCall); - overdubLen = inst->timestampsPerCall - outDataIndex; - } - - dtmfLen = WebRtcNetEQ_DTMFGenerate(&inst->DTMFInst, dtmfValue, dtmfVolume, - &pw16_outData[outDataIndex], inst->fs, overdubLen); - if (dtmfLen < 0) - { - /* error occurred */ - return_value = dtmfLen; - } - inst->DTMFInst.reinit = 0; -#else - inst->w16_mode = MODE_ERROR; - dspInfo->lastMode = MODE_ERROR; - return FAULTY_INSTRUCTION; -#endif - } - - /* - * Shuffle speech buffer to allow more data. Move data from pw16_NetEqAlgorithm_buffer - * to speechBuffer. - */ - if (instr != DSP_INSTR_EXPAND) - { - w16_tmp1 = WEBRTC_SPL_MIN(inst->endPosition, len); - WEBRTC_SPL_MEMMOVE_W16(inst->speechBuffer, inst->speechBuffer + w16_tmp1, - (inst->endPosition-w16_tmp1)); - WEBRTC_SPL_MEMCPY_W16(&inst->speechBuffer[inst->endPosition-w16_tmp1], - &pw16_NetEqAlgorithm_buffer[len-w16_tmp1], w16_tmp1); -#ifdef NETEQ_ATEVENT_DECODE - /* Update index to end of DTMF data in speech buffer */ - if (instr == DSP_INSTR_DTMF_GENERATE) - { - /* We have written DTMF data to the end of speech buffer */ - inst->DTMFInst.lastDtmfSample = inst->endPosition; - } - else if (inst->DTMFInst.lastDtmfSample > 0) - { - /* The end of DTMF data in speech buffer has been shuffled */ - inst->DTMFInst.lastDtmfSample -= w16_tmp1; - } -#endif - /* - * Update the BGN history if last operation was not expand (nor Merge, Accelerate - * or Pre-emptive expand, to save complexity). - */ - if ((inst->w16_mode != MODE_EXPAND) && (inst->w16_mode != MODE_MERGE) - && (inst->w16_mode != MODE_SUCCESS_ACCELERATE) && (inst->w16_mode - != MODE_LOWEN_ACCELERATE) && (inst->w16_mode != MODE_SUCCESS_PREEMPTIVE) - && (inst->w16_mode != MODE_LOWEN_PREEMPTIVE) && (inst->w16_mode - != MODE_FADE_TO_BGN) && (inst->w16_mode != MODE_DTMF) && (!BGNonly)) - { - WebRtcNetEQ_BGNUpdate(inst -#ifdef SCRATCH - , pw16_scratchPtr + SCRATCH_NETEQ_BGN_UPDATE -#endif - ); - } - } - else /* instr == DSP_INSTR_EXPAND */ - { - /* Nothing should be done since data is already copied to output. */ - } - - inst->curPosition -= len; - - /* - * Extra protection in case something should go totally wrong in terms of sizes... - * If everything is ok this should NEVER happen. - */ - if (inst->curPosition < -inst->timestampsPerCall) - { - inst->curPosition = -inst->timestampsPerCall; - } - - if ((instr != DSP_INSTR_EXPAND) && (instr != DSP_INSTR_MERGE) && (instr - != DSP_INSTR_FADE_TO_BGN)) - { - /* Reset concealed TS parameter if it does not seem to have been flushed */ - if (inst->w16_concealedTS > inst->timestampsPerCall) - { - inst->w16_concealedTS = 0; - } - } - - /* - * Double-check that we actually have 10 ms to play. If we haven't, there has been a - * serious error.The decoder might have returned way too few samples - */ - if (!DataEnough) - { - /* This should not happen. Set outdata to zeros, and return error. */ - WebRtcSpl_MemSetW16(pw16_outData, 0, inst->timestampsPerCall); - *pw16_len = inst->timestampsPerCall; - inst->w16_mode = MODE_ERROR; - dspInfo->lastMode = MODE_ERROR; - return RECOUT_ERROR_SAMPLEUNDERRUN; - } - - /* - * Update Videosync timestamp (this special timestamp is needed since the endTimestamp - * stops during CNG and Expand periods. - */ - if ((inst->w16_mode != MODE_EXPAND) && (inst->w16_mode != MODE_RFC3389CNG)) - { - uint32_t uw32_tmpTS; - uw32_tmpTS = inst->endTimestamp - (inst->endPosition - inst->curPosition); - if ((int32_t) (uw32_tmpTS - inst->videoSyncTimestamp) > 0) - { - inst->videoSyncTimestamp = uw32_tmpTS; - } - } - else - { - inst->videoSyncTimestamp += inst->timestampsPerCall; - } - - /* After this, regardless of what has happened, deliver 10 ms of future data */ - inst->curPosition += inst->timestampsPerCall; - *pw16_len = inst->timestampsPerCall; - - /* Remember if BGNonly was used */ - if (BGNonly) - { - inst->w16_mode |= MODE_BGN_ONLY; - } - - return return_value; -} - -#undef SCRATCH_ALGORITHM_BUFFER -#undef SCRATCH_NETEQ_NORMAL -#undef SCRATCH_NETEQ_MERGE -#undef SCRATCH_NETEQ_BGN_UPDATE -#undef SCRATCH_NETEQ_EXPAND -#undef SCRATCH_DSP_INFO -#undef SCRATCH_NETEQ_ACCELERATE -#undef SIZE_SCRATCH_BUFFER diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtcp.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtcp.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtcp.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtcp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Implementation of RTCP statistics reporting. - */ - -#include "rtcp.h" - -#include - -#include "signal_processing_library.h" - -int WebRtcNetEQ_RTCPInit(WebRtcNetEQ_RTCP_t *RTCP_inst, uint16_t uw16_seqNo) -{ - /* - * Initialize everything to zero and then set the start values for the RTP packet stream. - */ - WebRtcSpl_MemSetW16((int16_t*) RTCP_inst, 0, - sizeof(WebRtcNetEQ_RTCP_t) / sizeof(int16_t)); - RTCP_inst->base_seq = uw16_seqNo; - RTCP_inst->max_seq = uw16_seqNo; - return 0; -} - -int WebRtcNetEQ_RTCPUpdate(WebRtcNetEQ_RTCP_t *RTCP_inst, uint16_t uw16_seqNo, - uint32_t uw32_timeStamp, uint32_t uw32_recTime) -{ - int16_t w16_SeqDiff; - int32_t w32_TimeDiff; - int32_t w32_JitterDiff; - - /* - * Update number of received packets, and largest packet number received. - */ - RTCP_inst->received++; - w16_SeqDiff = uw16_seqNo - RTCP_inst->max_seq; - if (w16_SeqDiff >= 0) - { - if (uw16_seqNo < RTCP_inst->max_seq) - { - /* Wrap around detected */ - RTCP_inst->cycles++; - } - RTCP_inst->max_seq = uw16_seqNo; - } - - /* Calculate Jitter, and update previous timestamps */ - /* Note that the value in RTCP_inst->jitter is in Q4. */ - if (RTCP_inst->received > 1) - { - w32_TimeDiff = (uw32_recTime - (uw32_timeStamp - RTCP_inst->transit)); - w32_TimeDiff = WEBRTC_SPL_ABS_W32(w32_TimeDiff); - w32_JitterDiff = WEBRTC_SPL_LSHIFT_W16(w32_TimeDiff, 4) - RTCP_inst->jitter; - RTCP_inst->jitter = RTCP_inst->jitter + WEBRTC_SPL_RSHIFT_W32((w32_JitterDiff + 8), 4); - } - RTCP_inst->transit = (uw32_timeStamp - uw32_recTime); - return 0; -} - -int WebRtcNetEQ_RTCPGetStats(WebRtcNetEQ_RTCP_t *RTCP_inst, - uint16_t *puw16_fraction_lost, - uint32_t *puw32_cum_lost, uint32_t *puw32_ext_max, - uint32_t *puw32_jitter, int16_t doNotReset) -{ - uint32_t uw32_exp_nr, uw32_exp_interval, uw32_rec_interval; - int32_t w32_lost; - - /* Extended highest sequence number received */ - *puw32_ext_max - = (uint32_t) WEBRTC_SPL_LSHIFT_W32((uint32_t)RTCP_inst->cycles, 16) - + RTCP_inst->max_seq; - - /* - * Calculate expected number of packets and compare it to the number of packets that - * were actually received => the cumulative number of packets lost can be extracted. - */ - uw32_exp_nr = *puw32_ext_max - RTCP_inst->base_seq + 1; - if (RTCP_inst->received == 0) - { - /* no packets received, assume none lost */ - *puw32_cum_lost = 0; - } - else if (uw32_exp_nr > RTCP_inst->received) - { - *puw32_cum_lost = uw32_exp_nr - RTCP_inst->received; - if (*puw32_cum_lost > (uint32_t) 0xFFFFFF) - { - *puw32_cum_lost = 0xFFFFFF; - } - } - else - { - *puw32_cum_lost = 0; - } - - /* Fraction lost (Since last report) */ - uw32_exp_interval = uw32_exp_nr - RTCP_inst->exp_prior; - if (!doNotReset) - { - RTCP_inst->exp_prior = uw32_exp_nr; - } - uw32_rec_interval = RTCP_inst->received - RTCP_inst->rec_prior; - if (!doNotReset) - { - RTCP_inst->rec_prior = RTCP_inst->received; - } - w32_lost = (int32_t) (uw32_exp_interval - uw32_rec_interval); - if (uw32_exp_interval == 0 || w32_lost <= 0 || RTCP_inst->received == 0) - { - *puw16_fraction_lost = 0; - } - else - { - *puw16_fraction_lost = (uint16_t) (WEBRTC_SPL_LSHIFT_W32(w32_lost, 8) - / uw32_exp_interval); - } - if (*puw16_fraction_lost > 0xFF) - { - *puw16_fraction_lost = 0xFF; - } - - /* Inter-arrival jitter */ - *puw32_jitter = (RTCP_inst->jitter) >> 4; /* scaling from Q4 */ - return 0; -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtcp.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtcp.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtcp.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtcp.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/rtcp.h" + +#include + +#include + +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/modules/interface/module_common_types.h" + +namespace webrtc { + +void Rtcp::Init(uint16_t start_sequence_number) { + cycles_ = 0; + max_seq_no_ = start_sequence_number; + base_seq_no_ = start_sequence_number; + received_packets_ = 0; + received_packets_prior_ = 0; + expected_prior_ = 0; + jitter_ = 0; + transit_ = 0; +} + +void Rtcp::Update(const RTPHeader& rtp_header, uint32_t receive_timestamp) { + // Update number of received packets, and largest packet number received. + received_packets_++; + int16_t sn_diff = rtp_header.sequenceNumber - max_seq_no_; + if (sn_diff >= 0) { + if (rtp_header.sequenceNumber < max_seq_no_) { + // Wrap-around detected. + cycles_++; + } + max_seq_no_ = rtp_header.sequenceNumber; + } + + // Calculate jitter according to RFC 3550, and update previous timestamps. + // Note that the value in |jitter_| is in Q4. + if (received_packets_ > 1) { + int32_t ts_diff = receive_timestamp - (rtp_header.timestamp - transit_); + ts_diff = WEBRTC_SPL_ABS_W32(ts_diff); + int32_t jitter_diff = (ts_diff << 4) - jitter_; + // Calculate 15 * jitter_ / 16 + jitter_diff / 16 (with proper rounding). + jitter_ = jitter_ + ((jitter_diff + 8) >> 4); + } + transit_ = rtp_header.timestamp - receive_timestamp; +} + +void Rtcp::GetStatistics(bool no_reset, RtcpStatistics* stats) { + // Extended highest sequence number received. + stats->extended_max_sequence_number = + (static_cast(cycles_) << 16) + max_seq_no_; + + // Calculate expected number of packets and compare it with the number of + // packets that were actually received. The cumulative number of lost packets + // can be extracted. + uint32_t expected_packets = + stats->extended_max_sequence_number - base_seq_no_ + 1; + if (received_packets_ == 0) { + // No packets received, assume none lost. + stats->cumulative_lost = 0; + } else if (expected_packets > received_packets_) { + stats->cumulative_lost = expected_packets - received_packets_; + if (stats->cumulative_lost > 0xFFFFFF) { + stats->cumulative_lost = 0xFFFFFF; + } + } else { + stats->cumulative_lost = 0; + } + + // Fraction lost since last report. + uint32_t expected_since_last = expected_packets - expected_prior_; + uint32_t received_since_last = received_packets_ - received_packets_prior_; + if (!no_reset) { + expected_prior_ = expected_packets; + received_packets_prior_ = received_packets_; + } + int32_t lost = expected_since_last - received_since_last; + if (expected_since_last == 0 || lost <= 0 || received_packets_ == 0) { + stats->fraction_lost = 0; + } else { + stats->fraction_lost = std::min(0xFFU, (lost << 8) / expected_since_last); + } + + stats->jitter = jitter_ >> 4; // Scaling from Q4. +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtcp.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtcp.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtcp.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtcp.h 2015-02-03 14:33:34.000000000 +0000 @@ -8,95 +8,51 @@ * be found in the AUTHORS file in the root of the source tree. */ -/* - * RTCP statistics reporting. - */ - -#ifndef RTCP_H -#define RTCP_H - -#include "typedefs.h" - -typedef struct -{ - uint16_t cycles; /* The number of wrap-arounds for the sequence number */ - uint16_t max_seq; /* The maximum sequence number received - (starts from 0 again after wrap around) */ - uint16_t base_seq; /* The sequence number of the first packet that arrived */ - uint32_t received; /* The number of packets that has been received */ - uint32_t rec_prior; /* Number of packets received when last report was generated */ - uint32_t exp_prior; /* Number of packets that should have been received if no - packets were lost. Stored value from last report. */ - uint32_t jitter; /* Jitter statistics at this instance (calculated according to RFC) */ - int32_t transit; /* Clock difference for previous packet (RTPtimestamp - LOCALtime_rec) */ -} WebRtcNetEQ_RTCP_t; - -/**************************************************************************** - * WebRtcNetEQ_RTCPInit(...) - * - * This function calculates the parameters that are needed for the RTCP - * report. - * - * Input: - * - RTCP_inst : RTCP instance, that contains information about the - * packets that have been received etc. - * - seqNo : Packet number of the first received frame. - * - * Return value : 0 - Ok - * -1 - Error - */ +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_RTCP_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_RTCP_H_ -int WebRtcNetEQ_RTCPInit(WebRtcNetEQ_RTCP_t *RTCP_inst, uint16_t uw16_seqNo); - -/**************************************************************************** - * WebRtcNetEQ_RTCPUpdate(...) - * - * This function calculates the parameters that are needed for the RTCP - * report. - * - * Input: - * - RTCP_inst : RTCP instance, that contains information about the - * packets that have been received etc. - * - seqNo : Packet number of the first received frame. - * - timeStamp : Time stamp from the RTP header. - * - recTime : Time (in RTP timestamps) when this packet was received. - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RTCPUpdate(WebRtcNetEQ_RTCP_t *RTCP_inst, uint16_t uw16_seqNo, - uint32_t uw32_timeStamp, uint32_t uw32_recTime); - -/**************************************************************************** - * WebRtcNetEQ_RTCPGetStats(...) - * - * This function calculates the parameters that are needed for the RTCP - * report. - * - * Input: - * - RTCP_inst : RTCP instance, that contains information about the - * packets that have been received etc. - * - doNotReset : If non-zero, the fraction lost statistics will not - * be reset. - * - * Output: - * - RTCP_inst : Updated RTCP information (some statistics are - * reset when generating this report) - * - fraction_lost : Number of lost RTP packets divided by the number of - * expected packets, since the last RTCP Report. - * - cum_lost : Cumulative number of lost packets during this - * session. - * - ext_max : Extended highest sequence number received. - * - jitter : Inter-arrival jitter. - * - * Return value : 0 - Ok - * -1 - Error - */ +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Forward declaration. +struct RTPHeader; + +class Rtcp { + public: + Rtcp() { + Init(0); + } + + ~Rtcp() {} + + // Resets the RTCP statistics, and sets the first received sequence number. + void Init(uint16_t start_sequence_number); + + // Updates the RTCP statistics with a new received packet. + void Update(const RTPHeader& rtp_header, uint32_t receive_timestamp); + + // Returns the current RTCP statistics. If |no_reset| is true, the statistics + // are not reset, otherwise they are. + void GetStatistics(bool no_reset, RtcpStatistics* stats); + + private: + uint16_t cycles_; // The number of wrap-arounds for the sequence number. + uint16_t max_seq_no_; // The maximum sequence number received. Starts over + // from 0 after wrap-around. + uint16_t base_seq_no_; // The sequence number of the first received packet. + uint32_t received_packets_; // The number of packets that have been received. + uint32_t received_packets_prior_; // Number of packets received when last + // report was generated. + uint32_t expected_prior_; // Expected number of packets, at the time of the + // last report. + uint32_t jitter_; // Current jitter value. + int32_t transit_; // Clock difference for previous packet. -int WebRtcNetEQ_RTCPGetStats(WebRtcNetEQ_RTCP_t *RTCP_inst, - uint16_t *puw16_fraction_lost, - uint32_t *puw32_cum_lost, uint32_t *puw32_ext_max, - uint32_t *puw32_jitter, int16_t doNotReset); + DISALLOW_COPY_AND_ASSIGN(Rtcp); +}; -#endif +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_RTCP_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtp.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtp.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtp.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,240 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * RTP related functions. - */ - -#include "rtp.h" - -#include "typedefs.h" /* to define endianness */ - -#include "neteq_error_codes.h" - -int WebRtcNetEQ_RTPPayloadInfo(int16_t* pw16_Datagram, int i_DatagramLen, - RTPPacket_t* RTPheader) -{ - int i_P, i_X, i_CC, i_startPosition; - int i_IPver; - int i_extlength = -1; /* Default value is there is no extension */ - int i_padlength = 0; /* Default value if there is no padding */ - - if (i_DatagramLen < 12) - { - return RTP_TOO_SHORT_PACKET; - } - -#ifdef WEBRTC_ARCH_BIG_ENDIAN - i_IPver = (((uint16_t) (pw16_Datagram[0] & 0xC000)) >> 14); /* Extract the version */ - i_P = (((uint16_t) (pw16_Datagram[0] & 0x2000)) >> 13); /* Extract the P bit */ - i_X = (((uint16_t) (pw16_Datagram[0] & 0x1000)) >> 12); /* Extract the X bit */ - i_CC = ((uint16_t) (pw16_Datagram[0] >> 8) & 0xF); /* Get the CC number */ - RTPheader->payloadType = pw16_Datagram[0] & 0x7F; /* Get the coder type */ - RTPheader->seqNumber = pw16_Datagram[1]; /* Get the sequence number */ - RTPheader->timeStamp = ((((uint32_t) ((uint16_t) pw16_Datagram[2])) << 16) - | (uint16_t) (pw16_Datagram[3])); /* Get timestamp */ - RTPheader->ssrc = (((uint32_t) pw16_Datagram[4]) << 16) - + (((uint32_t) pw16_Datagram[5])); /* Get the SSRC */ - - if (i_X == 1) - { - /* Extension header exists. Find out how many int32_t it consists of. */ - i_extlength = pw16_Datagram[7 + 2 * i_CC]; - } - if (i_P == 1) - { - /* Padding exists. Find out how many bytes the padding consists of. */ - if (i_DatagramLen & 0x1) - { - /* odd number of bytes => last byte in higher byte */ - i_padlength = (((uint16_t) pw16_Datagram[i_DatagramLen >> 1]) >> 8); - } - else - { - /* even number of bytes => last byte in lower byte */ - i_padlength = ((pw16_Datagram[(i_DatagramLen >> 1) - 1]) & 0xFF); - } - } -#else /* WEBRTC_ARCH_LITTLE_ENDIAN */ - i_IPver = (((uint16_t) (pw16_Datagram[0] & 0xC0)) >> 6); /* Extract the IP version */ - i_P = (((uint16_t) (pw16_Datagram[0] & 0x20)) >> 5); /* Extract the P bit */ - i_X = (((uint16_t) (pw16_Datagram[0] & 0x10)) >> 4); /* Extract the X bit */ - i_CC = (uint16_t) (pw16_Datagram[0] & 0xF); /* Get the CC number */ - RTPheader->payloadType = (pw16_Datagram[0] >> 8) & 0x7F; /* Get the coder type */ - RTPheader->seqNumber = (((((uint16_t) pw16_Datagram[1]) >> 8) & 0xFF) - | (((uint16_t) (pw16_Datagram[1] & 0xFF)) << 8)); /* Get the packet number */ - RTPheader->timeStamp = ((((uint16_t) pw16_Datagram[2]) & 0xFF) << 24) - | ((((uint16_t) pw16_Datagram[2]) & 0xFF00) << 8) - | ((((uint16_t) pw16_Datagram[3]) >> 8) & 0xFF) - | ((((uint16_t) pw16_Datagram[3]) & 0xFF) << 8); /* Get timestamp */ - RTPheader->ssrc = ((((uint16_t) pw16_Datagram[4]) & 0xFF) << 24) - | ((((uint16_t) pw16_Datagram[4]) & 0xFF00) << 8) - | ((((uint16_t) pw16_Datagram[5]) >> 8) & 0xFF) - | ((((uint16_t) pw16_Datagram[5]) & 0xFF) << 8); /* Get the SSRC */ - - if (i_X == 1) - { - /* Extension header exists. Find out how many int32_t it consists of. */ - i_extlength = (((((uint16_t) pw16_Datagram[7 + 2 * i_CC]) >> 8) & 0xFF) - | (((uint16_t) (pw16_Datagram[7 + 2 * i_CC] & 0xFF)) << 8)); - } - if (i_P == 1) - { - /* Padding exists. Find out how many bytes the padding consists of. */ - if (i_DatagramLen & 0x1) - { - /* odd number of bytes => last byte in higher byte */ - i_padlength = (pw16_Datagram[i_DatagramLen >> 1] & 0xFF); - } - else - { - /* even number of bytes => last byte in lower byte */ - i_padlength = (((uint16_t) pw16_Datagram[(i_DatagramLen >> 1) - 1]) >> 8); - } - } -#endif - - i_startPosition = 12 + 4 * (i_extlength + 1) + 4 * i_CC; - RTPheader->payload = &pw16_Datagram[i_startPosition >> 1]; - RTPheader->payloadLen = i_DatagramLen - i_startPosition - i_padlength; - RTPheader->starts_byte1 = 0; - - if ((i_IPver != 2) || (RTPheader->payloadLen <= 0) || (RTPheader->payloadLen >= 16000) - || (i_startPosition < 12) || (i_startPosition > i_DatagramLen)) - { - return RTP_CORRUPT_PACKET; - } - - return 0; -} - -#ifdef NETEQ_RED_CODEC - -int WebRtcNetEQ_RedundancySplit(RTPPacket_t* RTPheader[], int i_MaximumPayloads, - int *i_No_Of_Payloads) -{ - const int16_t *pw16_data = RTPheader[0]->payload; /* Pointer to the data */ - uint16_t uw16_offsetTimeStamp = 65535, uw16_secondPayload = 65535; - int i_blockLength, i_k; - int i_discardedBlockLength = 0; - int singlePayload = 0; - -#ifdef WEBRTC_ARCH_BIG_ENDIAN - if ((pw16_data[0] & 0x8000) == 0) - { - /* Only one payload in this packet*/ - singlePayload = 1; - /* set the blocklength to -4 to deduce the non-existent 4-byte RED header */ - i_blockLength = -4; - RTPheader[0]->payloadType = ((((uint16_t)pw16_data[0]) & 0x7F00) >> 8); - } - else - { - /* Discard all but the two last payloads. */ - while (((pw16_data[2] & 0x8000) != 0) && - (pw16_data<((RTPheader[0]->payload)+((RTPheader[0]->payloadLen+1)>>1)))) - { - i_discardedBlockLength += (4+(((uint16_t)pw16_data[1]) & 0x3FF)); - pw16_data+=2; - } - if (pw16_data>=(RTPheader[0]->payload+((RTPheader[0]->payloadLen+1)>>1))) - { - return RED_SPLIT_ERROR2; /* Error, we are outside the packet */ - } - singlePayload = 0; /* the packet contains more than one payload */ - uw16_secondPayload = ((((uint16_t)pw16_data[0]) & 0x7F00) >> 8); - RTPheader[0]->payloadType = ((((uint16_t)pw16_data[2]) & 0x7F00) >> 8); - uw16_offsetTimeStamp = ((((uint16_t)pw16_data[0]) & 0xFF) << 6) + - ((((uint16_t)pw16_data[1]) & 0xFC00) >> 10); - i_blockLength = (((uint16_t)pw16_data[1]) & 0x3FF); - } -#else /* WEBRTC_ARCH_LITTLE_ENDIAN */ - if ((pw16_data[0] & 0x80) == 0) - { - /* Only one payload in this packet */ - singlePayload = 1; - /* set the blocklength to -4 to deduce the non-existent 4-byte RED header */ - i_blockLength = -4; - RTPheader[0]->payloadType = (((uint16_t) pw16_data[0]) & 0x7F); - } - else - { - /* Discard all but the two last payloads. */ - while (((pw16_data[2] & 0x80) != 0) && (pw16_data < ((RTPheader[0]->payload) - + ((RTPheader[0]->payloadLen + 1) >> 1)))) - { - i_discardedBlockLength += (4 + ((((uint16_t) pw16_data[1]) & 0x3) << 8) - + ((((uint16_t) pw16_data[1]) & 0xFF00) >> 8)); - pw16_data += 2; - } - if (pw16_data >= (RTPheader[0]->payload + ((RTPheader[0]->payloadLen + 1) >> 1))) - { - return RED_SPLIT_ERROR2; /* Error, we are outside the packet */; - } - singlePayload = 0; /* the packet contains more than one payload */ - uw16_secondPayload = (((uint16_t) pw16_data[0]) & 0x7F); - RTPheader[0]->payloadType = (((uint16_t) pw16_data[2]) & 0x7F); - uw16_offsetTimeStamp = ((((uint16_t) pw16_data[0]) & 0xFF00) >> 2) - + ((((uint16_t) pw16_data[1]) & 0xFC) >> 2); - i_blockLength = ((((uint16_t) pw16_data[1]) & 0x3) << 8) - + ((((uint16_t) pw16_data[1]) & 0xFF00) >> 8); - } -#endif - - if (i_MaximumPayloads < 2 || singlePayload == 1) - { - /* Reject the redundancy; or no redundant payload present. */ - for (i_k = 1; i_k < i_MaximumPayloads; i_k++) - { - RTPheader[i_k]->payloadType = -1; - RTPheader[i_k]->payloadLen = 0; - } - - /* update the pointer for the main data */ - pw16_data = &pw16_data[(5 + i_blockLength) >> 1]; - RTPheader[0]->starts_byte1 = (5 + i_blockLength) & 0x1; - RTPheader[0]->payloadLen = RTPheader[0]->payloadLen - (i_blockLength + 5) - - i_discardedBlockLength; - RTPheader[0]->payload = pw16_data; - - *i_No_Of_Payloads = 1; - - } - else - { - /* Redundancy accepted, put the redundancy in second RTPheader. */ - RTPheader[1]->payloadType = uw16_secondPayload; - RTPheader[1]->payload = &pw16_data[5 >> 1]; - RTPheader[1]->starts_byte1 = 5 & 0x1; - RTPheader[1]->seqNumber = RTPheader[0]->seqNumber; - RTPheader[1]->timeStamp = RTPheader[0]->timeStamp - uw16_offsetTimeStamp; - RTPheader[1]->ssrc = RTPheader[0]->ssrc; - RTPheader[1]->payloadLen = i_blockLength; - - /* Modify first RTP packet, so that it contains the main data. */ - RTPheader[0]->payload = &pw16_data[(5 + i_blockLength) >> 1]; - RTPheader[0]->starts_byte1 = (5 + i_blockLength) & 0x1; - RTPheader[0]->payloadLen = RTPheader[0]->payloadLen - (i_blockLength + 5) - - i_discardedBlockLength; - - /* Clear the following payloads. */ - for (i_k = 2; i_k < i_MaximumPayloads; i_k++) - { - RTPheader[i_k]->payloadType = -1; - RTPheader[i_k]->payloadLen = 0; - } - - *i_No_Of_Payloads = 2; - } - return 0; -} - -#endif - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtp.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtp.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtp.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/rtp.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * RTP data struct and related functions. - */ - -#ifndef RTP_H -#define RTP_H - -#include "typedefs.h" - -#include "codec_db.h" - -typedef struct -{ - uint16_t seqNumber; - uint32_t timeStamp; - uint32_t ssrc; - int payloadType; - const int16_t *payload; - int16_t payloadLen; - int16_t starts_byte1; - int16_t rcuPlCntr; -} RTPPacket_t; - -/**************************************************************************** - * WebRtcNetEQ_RTPPayloadInfo(...) - * - * Converts a datagram into an RTP header struct. - * - * Input: - * - Datagram : UDP datagram from the network - * - DatagramLen : Length in bytes of the datagram - * - * Output: - * - RTPheader : Structure with the datagram info - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RTPPayloadInfo(int16_t* pw16_Datagram, int i_DatagramLen, - RTPPacket_t* RTPheader); - -/**************************************************************************** - * WebRtcNetEQ_RedundancySplit(...) - * - * Splits a Redundancy RTP struct into two RTP structs. User has to check - * that it's really the redundancy payload. No such check is done inside this - * function. - * - * Input: - * - RTPheader : First header holds the whole RTP packet (with the redundancy payload) - * - MaximumPayloads: - * The maximum number of RTP payloads that should be - * extracted (1+maximum_no_of_Redundancies). - * - * Output: - * - RTPheader : First header holds the main RTP data, while 2..N - * holds the redundancy data. - * - No_Of - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RedundancySplit(RTPPacket_t* RTPheader[], int i_MaximumPayloads, - int *i_No_Of_Payloads); - -#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/set_fs.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/set_fs.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/set_fs.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/set_fs.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Function were the sample rate is set. - */ - -#include "mcu.h" - -#include "dtmf_buffer.h" -#include "neteq_error_codes.h" - -int WebRtcNetEQ_McuSetFs(MCUInst_t *inst, uint16_t fs) -{ - int16_t ok = 0; - - switch (fs) - { - case 8000: - { -#ifdef NETEQ_ATEVENT_DECODE - ok = WebRtcNetEQ_DtmfDecoderInit(&inst->DTMF_inst, 8000, 560); -#endif - inst->timestampsPerCall = inst->millisecondsPerCall * 8; - break; - } - -#ifdef NETEQ_WIDEBAND - case 16000: - { -#ifdef NETEQ_ATEVENT_DECODE - ok = WebRtcNetEQ_DtmfDecoderInit(&inst->DTMF_inst, 16000, 1120); -#endif - inst->timestampsPerCall = inst->millisecondsPerCall * 16; - break; - } -#endif - -#ifdef NETEQ_32KHZ_WIDEBAND - case 32000: - { -#ifdef NETEQ_ATEVENT_DECODE - ok = WebRtcNetEQ_DtmfDecoderInit(&inst->DTMF_inst, 32000, 2240); -#endif - inst->timestampsPerCall = inst->millisecondsPerCall * 32; - break; - } -#endif - -#ifdef NETEQ_48KHZ_WIDEBAND - case 48000: - { -#ifdef NETEQ_ATEVENT_DECODE - ok = WebRtcNetEQ_DtmfDecoderInit(&inst->DTMF_inst, 48000, 3360); -#endif - inst->timestampsPerCall = inst->millisecondsPerCall * 48; - break; - } -#endif - - default: - { - /* Not supported yet */ - return CODEC_DB_UNSUPPORTED_FS; - } - } /* end switch */ - - inst->fs = fs; - - return ok; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/signal_mcu.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/signal_mcu.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/signal_mcu.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/signal_mcu.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,820 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Signal the MCU that data is available and ask for a RecOut decision. - */ - -#include "mcu.h" - -#include - -#include "signal_processing_library.h" - -#include "automode.h" -#include "dtmf_buffer.h" -#include "mcu_dsp_common.h" -#include "neteq_error_codes.h" - -#ifdef NETEQ_DELAY_LOGGING -#include "delay_logging.h" -#include - -extern FILE *delay_fid2; /* file pointer to delay log file */ -#endif - - -/* - * Update the frame size, if we can. - */ -static int WebRtcNetEQ_UpdatePackSizeSamples(MCUInst_t* inst, int buffer_pos, - int payload_type, - int pack_size_samples) { - if (buffer_pos >= 0) { - int codec_pos; - codec_pos = WebRtcNetEQ_DbGetCodec(&inst->codec_DB_inst, payload_type); - if (codec_pos >= 0) { - codec_pos = inst->codec_DB_inst.position[codec_pos]; - if (codec_pos >= 0) { - int temp_packet_size_samples = WebRtcNetEQ_PacketBufferGetPacketSize( - &inst->PacketBuffer_inst, buffer_pos, &inst->codec_DB_inst, - codec_pos, pack_size_samples, inst->av_sync); - if (temp_packet_size_samples > 0) - return temp_packet_size_samples; - return pack_size_samples; - } - } - } - return pack_size_samples; -} - -/* - * Signals the MCU that DSP status data is available. - */ -int WebRtcNetEQ_SignalMcu(MCUInst_t *inst) -{ - - int i_bufferpos, i_res; - uint16_t uw16_instr; - DSP2MCU_info_t dspInfo; - int16_t *blockPtr, blockLen; - uint32_t uw32_availableTS; - RTPPacket_t temp_pkt; - int32_t w32_bufsize, w32_tmp; - int16_t payloadType = -1; - int16_t wantedNoOfTimeStamps; - int32_t totalTS; - int16_t oldPT, latePacketExist = 0; - uint32_t oldTS, prevTS, uw32_tmp; - uint16_t prevSeqNo; - int16_t nextSeqNoAvail; - int16_t fs_mult, w16_tmp; - int16_t lastModeBGNonly = 0; -#ifdef NETEQ_DELAY_LOGGING - int temp_var; -#endif - int playDtmf = 0; - - fs_mult = WebRtcSpl_DivW32W16ResW16(inst->fs, 8000); - - /* Increment counter since last statistics report */ - inst->lastReportTS += inst->timestampsPerCall; - - /* Increment waiting time for all packets. */ - WebRtcNetEQ_IncrementWaitingTimes(&inst->PacketBuffer_inst); - - /* Read info from DSP so we now current status */ - - WEBRTC_SPL_MEMCPY_W8(&dspInfo,inst->pw16_readAddress,sizeof(DSP2MCU_info_t)); - - /* Set blockPtr to first payload block */ - blockPtr = &inst->pw16_writeAddress[3]; - - /* Clear instruction word and number of lost samples (2*int16_t) */ - inst->pw16_writeAddress[0] = 0; - inst->pw16_writeAddress[1] = 0; - inst->pw16_writeAddress[2] = 0; - - if ((dspInfo.lastMode & MODE_AWAITING_CODEC_PTR) != 0) - { - /* - * Make sure state is adjusted so that a codec update is - * performed when first packet arrives. - */ - if (inst->new_codec != 1) - { - inst->current_Codec = -1; - } - dspInfo.lastMode = (dspInfo.lastMode ^ MODE_AWAITING_CODEC_PTR); - } - -#ifdef NETEQ_STEREO - if ((dspInfo.lastMode & MODE_MASTER_DTMF_SIGNAL) != 0) - { - playDtmf = 1; /* force DTMF decision */ - dspInfo.lastMode = (dspInfo.lastMode ^ MODE_MASTER_DTMF_SIGNAL); - } - - if ((dspInfo.lastMode & MODE_USING_STEREO) != 0) - { - if (inst->usingStereo == 0) - { - /* stereo mode changed; reset automode instance to re-synchronize statistics */ - WebRtcNetEQ_ResetAutomode(&(inst->BufferStat_inst.Automode_inst), - inst->PacketBuffer_inst.maxInsertPositions); - } - inst->usingStereo = 1; - dspInfo.lastMode = (dspInfo.lastMode ^ MODE_USING_STEREO); - } - else - { - inst->usingStereo = 0; - } -#endif - - /* detect if BGN_ONLY flag is set in lastMode */ - if ((dspInfo.lastMode & MODE_BGN_ONLY) != 0) - { - lastModeBGNonly = 1; /* remember flag */ - dspInfo.lastMode ^= MODE_BGN_ONLY; /* clear the flag */ - } - - if ((dspInfo.lastMode == MODE_RFC3389CNG) || (dspInfo.lastMode == MODE_CODEC_INTERNAL_CNG) - || (dspInfo.lastMode == MODE_EXPAND)) - { - /* - * If last mode was CNG (or Expand, since this could be covering up for a lost CNG - * packet), increase the CNGplayedTS counter. - */ - inst->BufferStat_inst.uw32_CNGplayedTS += inst->timestampsPerCall; - - if (dspInfo.lastMode == MODE_RFC3389CNG) - { - /* remember that RFC3389CNG is on (needed if CNG is interrupted by DTMF) */ - inst->BufferStat_inst.w16_cngOn = CNG_RFC3389_ON; - } - else if (dspInfo.lastMode == MODE_CODEC_INTERNAL_CNG) - { - /* remember that internal CNG is on (needed if CNG is interrupted by DTMF) */ - inst->BufferStat_inst.w16_cngOn = CNG_INTERNAL_ON; - } - - } - - /* Update packet size from previously decoded packet */ - if (dspInfo.frameLen > 0) - { - inst->PacketBuffer_inst.packSizeSamples = dspInfo.frameLen; - } - - /* Look for late packet (unless codec has changed) */ - if (inst->new_codec != 1) - { - if (WebRtcNetEQ_DbIsMDCodec((enum WebRtcNetEQDecoder) inst->current_Codec)) - { - WebRtcNetEQ_PacketBufferFindLowestTimestamp(&inst->PacketBuffer_inst, - inst->timeStamp, &uw32_availableTS, &i_bufferpos, 1, &payloadType); - if ((inst->new_codec != 1) && (inst->timeStamp == uw32_availableTS) - && (inst->timeStamp < dspInfo.playedOutTS) && (i_bufferpos != -1) - && (WebRtcNetEQ_DbGetPayload(&(inst->codec_DB_inst), - (enum WebRtcNetEQDecoder) inst->current_Codec) == payloadType)) - { - int waitingTime; - temp_pkt.payload = blockPtr + 1; - i_res = WebRtcNetEQ_PacketBufferExtract(&inst->PacketBuffer_inst, &temp_pkt, - i_bufferpos, &waitingTime); - if (i_res < 0) - { /* error returned */ - return i_res; - } - WebRtcNetEQ_StoreWaitingTime(inst, waitingTime); - *blockPtr = temp_pkt.payloadLen; - /* set the flag if this is a redundant payload */ - if (temp_pkt.rcuPlCntr > 0) - { - *blockPtr = (*blockPtr) | (DSP_CODEC_RED_FLAG); - } - blockPtr += ((temp_pkt.payloadLen + 1) >> 1) + 1; - - /* - * Close the data with a zero size block, in case we will not write any - * more data. - */ - *blockPtr = 0; - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0xf0ff) - | DSP_CODEC_ADD_LATE_PKT; - latePacketExist = 1; - } - } - } - - i_res = WebRtcNetEQ_PacketBufferFindLowestTimestamp(&inst->PacketBuffer_inst, - dspInfo.playedOutTS, &uw32_availableTS, &i_bufferpos, (inst->new_codec == 0), - &payloadType); - if (i_res < 0) - { /* error returned */ - return i_res; - } - - if (inst->BufferStat_inst.w16_cngOn == CNG_RFC3389_ON) - { - /* - * Because of timestamp peculiarities, we have to "manually" disallow using a CNG - * packet with the same timestamp as the one that was last played. This can happen - * when using redundancy and will cause the timing to shift. - */ - while (i_bufferpos != -1 && WebRtcNetEQ_DbIsCNGPayload(&inst->codec_DB_inst, - payloadType) && dspInfo.playedOutTS >= uw32_availableTS) - { - - /* Don't use this packet, discard it */ - inst->PacketBuffer_inst.payloadType[i_bufferpos] = -1; - inst->PacketBuffer_inst.payloadLengthBytes[i_bufferpos] = 0; - inst->PacketBuffer_inst.numPacketsInBuffer--; - - /* Check buffer again */ - WebRtcNetEQ_PacketBufferFindLowestTimestamp(&inst->PacketBuffer_inst, - dspInfo.playedOutTS, &uw32_availableTS, &i_bufferpos, (inst->new_codec == 0), - &payloadType); - } - } - - /* Check packet buffer */ - w32_bufsize = WebRtcNetEQ_PacketBufferGetSize(&inst->PacketBuffer_inst, - &inst->codec_DB_inst, inst->av_sync); - - if (dspInfo.lastMode == MODE_SUCCESS_ACCELERATE || dspInfo.lastMode - == MODE_LOWEN_ACCELERATE || dspInfo.lastMode == MODE_SUCCESS_PREEMPTIVE - || dspInfo.lastMode == MODE_LOWEN_PREEMPTIVE) - { - /* Subtract (dspInfo.samplesLeft + inst->timestampsPerCall) from sampleMemory */ - inst->BufferStat_inst.Automode_inst.sampleMemory -= dspInfo.samplesLeft - + inst->timestampsPerCall; - } - - /* calculate total current buffer size (in ms*8), including sync buffer */ - w32_bufsize = WebRtcSpl_DivW32W16((w32_bufsize + dspInfo.samplesLeft), fs_mult); - -#ifdef NETEQ_ATEVENT_DECODE - /* DTMF data will affect the decision */ - if (WebRtcNetEQ_DtmfDecode(&inst->DTMF_inst, blockPtr + 1, blockPtr + 2, - dspInfo.playedOutTS + inst->BufferStat_inst.uw32_CNGplayedTS) > 0) - { - playDtmf = 1; - - /* Flag DTMF payload */ - inst->pw16_writeAddress[0] = inst->pw16_writeAddress[0] | DSP_DTMF_PAYLOAD; - - /* Block Length in bytes */ - blockPtr[0] = 4; - /* Advance to next payload position */ - blockPtr += 3; - } -#endif - - /* Update the frame size, if we can. */ - inst->PacketBuffer_inst.packSizeSamples = - WebRtcNetEQ_UpdatePackSizeSamples(inst, i_bufferpos, payloadType, - inst->PacketBuffer_inst.packSizeSamples); - /* Update statistics and make decision */ - uw16_instr = WebRtcNetEQ_BufstatsDecision(&inst->BufferStat_inst, - inst->PacketBuffer_inst.packSizeSamples, w32_bufsize, dspInfo.playedOutTS, - uw32_availableTS, i_bufferpos == -1, - WebRtcNetEQ_DbIsCNGPayload(&inst->codec_DB_inst, payloadType), dspInfo.lastMode, - inst->NetEqPlayoutMode, inst->timestampsPerCall, inst->NoOfExpandCalls, fs_mult, - lastModeBGNonly, playDtmf); - - /* Check if time to reset loss counter */ - if (inst->lastReportTS > WEBRTC_SPL_UMUL(inst->fs, MAX_LOSS_REPORT_PERIOD)) - { - /* reset loss counter */ - WebRtcNetEQ_ResetMcuInCallStats(inst); - } - - /* Check sync buffer size */ - if ((dspInfo.samplesLeft >= inst->timestampsPerCall) && (uw16_instr - != BUFSTATS_DO_ACCELERATE) && (uw16_instr != BUFSTATS_DO_MERGE) && (uw16_instr - != BUFSTATS_DO_PREEMPTIVE_EXPAND)) - { - *blockPtr = 0; - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) | DSP_INSTR_NORMAL; - return 0; - } - - if (uw16_instr == BUFSTATS_DO_EXPAND) - { - inst->NoOfExpandCalls++; - } - else - { - /* reset counter */ - inst->NoOfExpandCalls = 0; - } - - /* New codec or big change in packet number? */ - if ((inst->new_codec) || (uw16_instr == BUFSTAT_REINIT)) - { - CodecFuncInst_t cinst; - - /* Clear other instructions */ - blockPtr = &inst->pw16_writeAddress[3]; - /* Clear instruction word */ - inst->pw16_writeAddress[0] = 0; - - inst->timeStamp = uw32_availableTS; - dspInfo.playedOutTS = uw32_availableTS; - if (inst->current_Codec != -1) - { - i_res = WebRtcNetEQ_DbGetPtrs(&inst->codec_DB_inst, - (enum WebRtcNetEQDecoder) inst->current_Codec, &cinst); - if (i_res < 0) - { /* error returned */ - return i_res; - } - } - else - { - /* The main codec has not been initialized yet (first packets are DTMF or CNG). */ - if (WebRtcNetEQ_DbIsCNGPayload(&inst->codec_DB_inst, payloadType)) - { - /* The currently extracted packet is CNG; get CNG fs */ - uint16_t tempFs; - - tempFs = WebRtcNetEQ_DbGetSampleRate(&inst->codec_DB_inst, payloadType); - /* TODO(tlegrand): Remove this limitation once ACM has full - * 48 kHz support. */ - if (tempFs > 32000) - { - inst->fs = 32000; - } - else if (tempFs > 0) - { - inst->fs = tempFs; - } - } - WebRtcSpl_MemSetW16((int16_t*) &cinst, 0, - sizeof(CodecFuncInst_t) / sizeof(int16_t)); - cinst.codec_fs = inst->fs; - } - cinst.timeStamp = inst->timeStamp; - blockLen = (sizeof(CodecFuncInst_t)) >> (sizeof(int16_t) - 1); /* in Word16 */ - *blockPtr = blockLen * 2; - blockPtr++; - WEBRTC_SPL_MEMCPY_W8(blockPtr,&cinst,sizeof(CodecFuncInst_t)); - blockPtr += blockLen; - inst->new_codec = 0; - - /* Reinitialize the MCU fs */ - i_res = WebRtcNetEQ_McuSetFs(inst, cinst.codec_fs); - if (i_res < 0) - { /* error returned */ - return i_res; - } - - /* Set the packet size by guessing */ - inst->PacketBuffer_inst.packSizeSamples = - WebRtcNetEQ_UpdatePackSizeSamples(inst, i_bufferpos, payloadType, - inst->timestampsPerCall * 3); - - WebRtcNetEQ_ResetAutomode(&(inst->BufferStat_inst.Automode_inst), - inst->PacketBuffer_inst.maxInsertPositions); - -#ifdef NETEQ_CNG_CODEC - /* Also insert CNG state as this might be needed by DSP */ - i_res = WebRtcNetEQ_DbGetPtrs(&inst->codec_DB_inst, kDecoderCNG, &cinst); - if ((i_res < 0) && (i_res != CODEC_DB_NOT_EXIST1)) - { - /* other error returned */ - /* (CODEC_DB_NOT_EXIST1 simply indicates that CNG is not used */ - return i_res; - } - else - { - /* CNG exists */ - blockLen = (sizeof(cinst.codec_state)) >> (sizeof(int16_t) - 1); - *blockPtr = blockLen * 2; - blockPtr++; - WEBRTC_SPL_MEMCPY_W8(blockPtr,&cinst.codec_state,sizeof(cinst.codec_state)); - blockPtr += blockLen; - } -#endif - - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0xf0ff) - | DSP_CODEC_NEW_CODEC; - - if (uw16_instr == BUFSTATS_DO_RFC3389CNG_NOPACKET) - { - /* - * Change decision to CNG packet, since we do have a CNG packet, but it was - * considered too early to use. Now, use it anyway. - */ - uw16_instr = BUFSTATS_DO_RFC3389CNG_PACKET; - } - else if (uw16_instr != BUFSTATS_DO_RFC3389CNG_PACKET) - { - uw16_instr = BUFSTATS_DO_NORMAL; - } - - /* reset loss counter */ - WebRtcNetEQ_ResetMcuInCallStats(inst); - } - - /* Should we just reset the decoder? */ - if (uw16_instr == BUFSTAT_REINIT_DECODER) - { - /* Change decision to normal and flag decoder reset */ - uw16_instr = BUFSTATS_DO_NORMAL; - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0xf0ff) | DSP_CODEC_RESET; - } - - /* Expand requires no new packet */ - if (uw16_instr == BUFSTATS_DO_EXPAND) - { - - inst->timeStamp = dspInfo.playedOutTS; - - /* Have we got one descriptor left? */ - if (WebRtcNetEQ_DbIsMDCodec((enum WebRtcNetEQDecoder) inst->current_Codec) - && (dspInfo.MD || latePacketExist)) - { - - if (dspInfo.lastMode != MODE_ONE_DESCRIPTOR) - { - /* this is the first "consecutive" one-descriptor decoding; reset counter */ - inst->one_desc = 0; - } - if (inst->one_desc < MAX_ONE_DESC) - { - /* use that one descriptor */ - inst->one_desc++; /* increase counter */ - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_NORMAL_ONE_DESC; - - /* decrease counter since we did no Expand */ - inst->NoOfExpandCalls = WEBRTC_SPL_MAX(inst->NoOfExpandCalls - 1, 0); - return 0; - } - else - { - /* too many consecutive one-descriptor decodings; do expand instead */ - inst->one_desc = 0; /* reset counter */ - } - - } - - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) | DSP_INSTR_EXPAND; - return 0; - } - - /* Merge is not needed if we still have a descriptor */ - if ((uw16_instr == BUFSTATS_DO_MERGE) && (dspInfo.MD != 0)) - { - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_NORMAL_ONE_DESC; - *blockPtr = 0; - return 0; - } - - /* Do CNG without trying to extract any packets from buffer */ - if (uw16_instr == BUFSTATS_DO_RFC3389CNG_NOPACKET) - { - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_DO_RFC3389CNG; - *blockPtr = 0; - return 0; - } - - /* Do built-in CNG without extracting any new packets from buffer */ - if (uw16_instr == BUFSTATS_DO_INTERNAL_CNG_NOPACKET) - { - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_DO_CODEC_INTERNAL_CNG; - *blockPtr = 0; - return 0; - } - - /* Do DTMF without extracting any new packets from buffer */ - if (uw16_instr == BUFSTATS_DO_DTMF_ONLY) - { - uint32_t timeStampJump = 0; - - /* Update timestamp */ - if ((inst->BufferStat_inst.uw32_CNGplayedTS > 0) && (dspInfo.lastMode != MODE_DTMF)) - { - /* Jump in timestamps if needed */ - timeStampJump = inst->BufferStat_inst.uw32_CNGplayedTS; - inst->pw16_writeAddress[1] = (uint16_t) (timeStampJump >> 16); - inst->pw16_writeAddress[2] = (uint16_t) (timeStampJump & 0xFFFF); - } - - inst->timeStamp = dspInfo.playedOutTS + timeStampJump; - - inst->BufferStat_inst.uw32_CNGplayedTS = 0; - inst->NoOfExpandCalls = 0; - - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_DTMF_GENERATE; - *blockPtr = 0; - return 0; - } - - if (uw16_instr == BUFSTATS_DO_ACCELERATE) - { - /* In order to do a Accelerate we need at least 30 ms of data */ - if (dspInfo.samplesLeft >= (3 * 80 * fs_mult)) - { - /* Already have enough data, so we do not need to extract any more */ - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_ACCELERATE; - *blockPtr = 0; - inst->BufferStat_inst.Automode_inst.sampleMemory - = (int32_t) dspInfo.samplesLeft; - inst->BufferStat_inst.Automode_inst.prevTimeScale = 1; - return 0; - } - else if ((dspInfo.samplesLeft >= (1 * 80 * fs_mult)) - && (inst->PacketBuffer_inst.packSizeSamples >= (240 * fs_mult))) - { - /* Avoid decoding more data as it might overflow playout buffer */ - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_NORMAL; - *blockPtr = 0; - return 0; - } - else if ((dspInfo.samplesLeft < (1 * 80 * fs_mult)) - && (inst->PacketBuffer_inst.packSizeSamples >= (240 * fs_mult))) - { - /* For >= 30ms allow Accelerate with a decoding to avoid overflow in playout buffer */ - wantedNoOfTimeStamps = inst->timestampsPerCall; - } - else if (dspInfo.samplesLeft >= (2 * 80 * fs_mult)) - { - /* We need to decode another 10 ms in order to do an Accelerate */ - wantedNoOfTimeStamps = inst->timestampsPerCall; - } - else - { - /* - * Build up decoded data by decoding at least 20 ms of data. - * Do not perform Accelerate yet, but wait until we only need to do one decoding. - */ - wantedNoOfTimeStamps = 2 * inst->timestampsPerCall; - uw16_instr = BUFSTATS_DO_NORMAL; - } - } - else if (uw16_instr == BUFSTATS_DO_PREEMPTIVE_EXPAND) - { - /* In order to do a Preemptive Expand we need at least 30 ms of data */ - if (dspInfo.samplesLeft >= (3 * 80 * fs_mult)) - { - /* Already have enough data, so we do not need to extract any more */ - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_PREEMPTIVE_EXPAND; - *blockPtr = 0; - inst->BufferStat_inst.Automode_inst.sampleMemory - = (int32_t) dspInfo.samplesLeft; - inst->BufferStat_inst.Automode_inst.prevTimeScale = 1; - return 0; - } - else if ((dspInfo.samplesLeft >= (1 * 80 * fs_mult)) - && (inst->PacketBuffer_inst.packSizeSamples >= (240 * fs_mult))) - { - /* - * Avoid decoding more data as it might overflow playout buffer; - * still try Preemptive Expand though. - */ - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_PREEMPTIVE_EXPAND; - *blockPtr = 0; - inst->BufferStat_inst.Automode_inst.sampleMemory - = (int32_t) dspInfo.samplesLeft; - inst->BufferStat_inst.Automode_inst.prevTimeScale = 1; - return 0; - } - else if ((dspInfo.samplesLeft < (1 * 80 * fs_mult)) - && (inst->PacketBuffer_inst.packSizeSamples >= (240 * fs_mult))) - { - /* - * For >= 30ms allow Preemptive Expand with a decoding to avoid overflow in - * playout buffer - */ - wantedNoOfTimeStamps = inst->timestampsPerCall; - } - else if (dspInfo.samplesLeft >= (2 * 80 * fs_mult)) - { - /* We need to decode another 10 ms in order to do an Preemptive Expand */ - wantedNoOfTimeStamps = inst->timestampsPerCall; - } - else - { - /* - * Build up decoded data by decoding at least 20 ms of data, - * Still try to perform Preemptive Expand. - */ - wantedNoOfTimeStamps = 2 * inst->timestampsPerCall; - } - } - else - { - wantedNoOfTimeStamps = inst->timestampsPerCall; - } - - /* Otherwise get data from buffer, try to get at least 10ms */ - totalTS = 0; - oldTS = uw32_availableTS; - if ((i_bufferpos > -1) && (uw16_instr != BUFSTATS_DO_ALTERNATIVE_PLC) && (uw16_instr - != BUFSTATS_DO_ALTERNATIVE_PLC_INC_TS) && (uw16_instr != BUFSTATS_DO_AUDIO_REPETITION) - && (uw16_instr != BUFSTATS_DO_AUDIO_REPETITION_INC_TS)) - { - uw32_tmp = (uw32_availableTS - dspInfo.playedOutTS); - inst->pw16_writeAddress[1] = (uint16_t) (uw32_tmp >> 16); - inst->pw16_writeAddress[2] = (uint16_t) (uw32_tmp & 0xFFFF); - if (inst->BufferStat_inst.w16_cngOn == CNG_OFF) - { - /* - * Adjustment of TS only corresponds to an actual packet loss - * if comfort noise is not played. If comfort noise was just played, - * this adjustment of TS is only done to get back in sync with the - * stream TS; no loss to report. - */ - inst->lostTS += uw32_tmp; - } - - if (uw16_instr != BUFSTATS_DO_RFC3389CNG_PACKET) - { - /* We are about to decode and use a non-CNG packet => CNG period is ended */ - inst->BufferStat_inst.w16_cngOn = CNG_OFF; - } - - /* - * Reset CNG timestamp as a new packet will be delivered. - * (Also if CNG packet, since playedOutTS is updated.) - */ - inst->BufferStat_inst.uw32_CNGplayedTS = 0; - - prevSeqNo = inst->PacketBuffer_inst.seqNumber[i_bufferpos]; - prevTS = inst->PacketBuffer_inst.timeStamp[i_bufferpos]; - oldPT = inst->PacketBuffer_inst.payloadType[i_bufferpos]; - - /* These values are used by NACK module to estimate time-to-play of - * a missing packet. Occasionally, NetEq might decide to decode more - * than one packet. Therefore, these values store sequence number and - * timestamp of the first packet pulled from the packet buffer. In - * such cases, these values do not exactly represent the sequence number - * or timestamp associated with a 10ms audio pulled from NetEq. NACK - * module is designed to compensate for this. - */ - inst->decoded_packet_sequence_number = prevSeqNo; - inst->decoded_packet_timestamp = prevTS; - - /* clear flag bits */ - inst->pw16_writeAddress[0] = inst->pw16_writeAddress[0] & 0xFF3F; - do - { - int waitingTime; - inst->timeStamp = uw32_availableTS; - /* Write directly to shared memory */ - temp_pkt.payload = blockPtr + 1; - i_res = WebRtcNetEQ_PacketBufferExtract(&inst->PacketBuffer_inst, &temp_pkt, - i_bufferpos, &waitingTime); - - if (i_res < 0) - { - /* error returned */ - return i_res; - } - WebRtcNetEQ_StoreWaitingTime(inst, waitingTime); - -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_DECODE; - if ((fwrite(&temp_var, sizeof(int), - 1, delay_fid2) != 1) || - (fwrite(&temp_pkt.timeStamp, sizeof(uint32_t), - 1, delay_fid2) != 1) || - (fwrite(&dspInfo.samplesLeft, sizeof(uint16_t), - 1, delay_fid2) != 1)) { - return -1; - } -#endif - - *blockPtr = temp_pkt.payloadLen; - /* set the flag if this is a redundant payload */ - if (temp_pkt.rcuPlCntr > 0) - { - *blockPtr = (*blockPtr) | (DSP_CODEC_RED_FLAG); - } - blockPtr += ((temp_pkt.payloadLen + 1) >> 1) + 1; - - if (i_bufferpos > -1) - { - /* - * Store number of TS extracted (last extracted is assumed to be of - * packSizeSamples). - */ - totalTS = uw32_availableTS - oldTS + inst->PacketBuffer_inst.packSizeSamples; - } - /* Check what next packet is available */ - WebRtcNetEQ_PacketBufferFindLowestTimestamp(&inst->PacketBuffer_inst, - inst->timeStamp, &uw32_availableTS, &i_bufferpos, 0, &payloadType); - - nextSeqNoAvail = 0; - if ((i_bufferpos > -1) && (oldPT - == inst->PacketBuffer_inst.payloadType[i_bufferpos])) - { - w16_tmp = inst->PacketBuffer_inst.seqNumber[i_bufferpos] - prevSeqNo; - w32_tmp = inst->PacketBuffer_inst.timeStamp[i_bufferpos] - prevTS; - if ((w16_tmp == 1) || /* Next packet */ - ((w16_tmp == 0) && (w32_tmp == inst->PacketBuffer_inst.packSizeSamples))) - { /* or packet split into frames */ - nextSeqNoAvail = 1; - } - prevSeqNo = inst->PacketBuffer_inst.seqNumber[i_bufferpos]; - } - /* Update the frame size, if we can. */ - inst->PacketBuffer_inst.packSizeSamples = - WebRtcNetEQ_UpdatePackSizeSamples(inst, i_bufferpos, - payloadType, inst->PacketBuffer_inst.packSizeSamples); - } - while ((totalTS < wantedNoOfTimeStamps) && (nextSeqNoAvail == 1)); - } - - if ((uw16_instr == BUFSTATS_DO_ACCELERATE) - || (uw16_instr == BUFSTATS_DO_PREEMPTIVE_EXPAND)) - { - /* Check that we have enough data (30ms) to do the Accelearate */ - if ((totalTS + dspInfo.samplesLeft) < WEBRTC_SPL_MUL(3,inst->timestampsPerCall) - && (uw16_instr == BUFSTATS_DO_ACCELERATE)) - { - /* Not enough, do normal operation instead */ - uw16_instr = BUFSTATS_DO_NORMAL; - } - else - { - inst->BufferStat_inst.Automode_inst.sampleMemory - = (int32_t) dspInfo.samplesLeft + totalTS; - inst->BufferStat_inst.Automode_inst.prevTimeScale = 1; - } - } - - /* Close the data with a zero size block */ - *blockPtr = 0; - - /* Write data to DSP */ - switch (uw16_instr) - { - case BUFSTATS_DO_NORMAL: - /* Normal with decoding included */ - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_NORMAL; - break; - case BUFSTATS_DO_ACCELERATE: - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_ACCELERATE; - break; - case BUFSTATS_DO_MERGE: - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_MERGE; - break; - case BUFSTATS_DO_RFC3389CNG_PACKET: - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_DO_RFC3389CNG; - break; - case BUFSTATS_DO_ALTERNATIVE_PLC: - inst->pw16_writeAddress[1] = 0; - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_DO_ALTERNATIVE_PLC; - break; - case BUFSTATS_DO_ALTERNATIVE_PLC_INC_TS: - inst->pw16_writeAddress[1] = 0; - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_DO_ALTERNATIVE_PLC_INC_TS; - break; - case BUFSTATS_DO_AUDIO_REPETITION: - inst->pw16_writeAddress[1] = 0; - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_DO_AUDIO_REPETITION; - break; - case BUFSTATS_DO_AUDIO_REPETITION_INC_TS: - inst->pw16_writeAddress[1] = 0; - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_DO_AUDIO_REPETITION_INC_TS; - break; - case BUFSTATS_DO_PREEMPTIVE_EXPAND: - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_PREEMPTIVE_EXPAND; - break; - default: - return UNKNOWN_BUFSTAT_DECISION; - } - - inst->timeStamp = dspInfo.playedOutTS; - return 0; - -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/split_and_insert.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/split_and_insert.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/split_and_insert.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/split_and_insert.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Split an RTP payload (if possible and suitable) and insert into packet buffer. - */ - -#include "mcu.h" - -#include - -#include "mcu_dsp_common.h" -#include "neteq_error_codes.h" -#include "signal_processing_library.h" - -int WebRtcNetEQ_SplitAndInsertPayload(RTPPacket_t* packet, - PacketBuf_t* Buffer_inst, - SplitInfo_t* split_inst, - int16_t* flushed, - int av_sync) -{ - - int i_ok; - int len; - int i; - RTPPacket_t temp_packet; - int16_t localFlushed = 0; - const int16_t *pw16_startPayload; - const int is_sync_rtp = av_sync && - WebRtcNetEQ_IsSyncPayload(packet->payload, packet->payloadLen); - *flushed = 0; - - len = packet->payloadLen; - - /* Copy to temp packet that can be modified. */ - - WEBRTC_SPL_MEMCPY_W8(&temp_packet,packet,sizeof(RTPPacket_t)); - - if (split_inst->deltaBytes == NO_SPLIT || - is_sync_rtp) /* Don't split sync RTPs just insert. */ - { - /* Not splittable codec */ - i_ok = WebRtcNetEQ_PacketBufferInsert(Buffer_inst, packet, - &localFlushed, av_sync); - *flushed |= localFlushed; - if (i_ok < 0) - { - return PBUFFER_INSERT_ERROR5; - } - } - else if (split_inst->deltaBytes < -10) - { - /* G711, PCM16B or G722, use "soft splitting" */ - int split_size = packet->payloadLen; - int mult = WEBRTC_SPL_ABS_W32(split_inst->deltaBytes) - 10; - - /* Find "chunk size" >= 20 ms and < 40 ms - * split_inst->deltaTime in this case contains the number of bytes per - * timestamp unit times 2 - */ - while (split_size >= ((80 << split_inst->deltaTime) * mult)) - { - split_size >>= 1; - } - - /* Make the size an even value. */ - if (split_size > 1) - { - split_size >>= 1; - split_size *= 2; - } - - temp_packet.payloadLen = split_size; - pw16_startPayload = temp_packet.payload; - i = 0; - while (len >= (2 * split_size)) - { - /* insert every chunk */ - i_ok = WebRtcNetEQ_PacketBufferInsert(Buffer_inst, &temp_packet, - &localFlushed, av_sync); - *flushed |= localFlushed; - temp_packet.timeStamp += ((2 * split_size) >> split_inst->deltaTime); - i++; - temp_packet.payload = &(pw16_startPayload[(i * split_size) >> 1]); - temp_packet.starts_byte1 = temp_packet.starts_byte1 ^ (split_size & 0x1); - - len -= split_size; - if (i_ok < 0) - { - return PBUFFER_INSERT_ERROR1; - } - } - - /* Insert the rest */ - temp_packet.payloadLen = len; - i_ok = WebRtcNetEQ_PacketBufferInsert(Buffer_inst, &temp_packet, - &localFlushed, av_sync); - *flushed |= localFlushed; - if (i_ok < 0) - { - return PBUFFER_INSERT_ERROR2; - } - } - else - { - /* Frame based codec, use hard splitting. */ - i = 0; - pw16_startPayload = temp_packet.payload; - while (len >= split_inst->deltaBytes) - { - - temp_packet.payloadLen = split_inst->deltaBytes; - i_ok = WebRtcNetEQ_PacketBufferInsert(Buffer_inst, &temp_packet, - &localFlushed, av_sync); - *flushed |= localFlushed; - i++; - temp_packet.payload = &(pw16_startPayload[(i * split_inst->deltaBytes) >> 1]); - temp_packet.timeStamp += split_inst->deltaTime; - temp_packet.starts_byte1 = temp_packet.starts_byte1 ^ (split_inst->deltaBytes - & 0x1); - - if (i_ok < 0) - { - return PBUFFER_INSERT_ERROR3; - } - len -= split_inst->deltaBytes; - - } - if (len > 0) - { - /* Must be a either an error or a SID frame at the end of the packet. */ - temp_packet.payloadLen = len; - i_ok = WebRtcNetEQ_PacketBufferInsert(Buffer_inst, &temp_packet, - &localFlushed, av_sync); - *flushed |= localFlushed; - if (i_ok < 0) - { - return PBUFFER_INSERT_ERROR4; - } - } - } - - return 0; -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/statistics_calculator.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/statistics_calculator.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/statistics_calculator.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/statistics_calculator.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/statistics_calculator.h" + +#include +#include // memset + +#include "webrtc/modules/audio_coding/neteq/decision_logic.h" +#include "webrtc/modules/audio_coding/neteq/delay_manager.h" + +namespace webrtc { + +StatisticsCalculator::StatisticsCalculator() + : preemptive_samples_(0), + accelerate_samples_(0), + added_zero_samples_(0), + expanded_voice_samples_(0), + expanded_noise_samples_(0), + discarded_packets_(0), + lost_timestamps_(0), + timestamps_since_last_report_(0), + len_waiting_times_(0), + next_waiting_time_index_(0) { + memset(waiting_times_, 0, kLenWaitingTimes * sizeof(waiting_times_[0])); +} + +void StatisticsCalculator::Reset() { + preemptive_samples_ = 0; + accelerate_samples_ = 0; + added_zero_samples_ = 0; + expanded_voice_samples_ = 0; + expanded_noise_samples_ = 0; +} + +void StatisticsCalculator::ResetMcu() { + discarded_packets_ = 0; + lost_timestamps_ = 0; + timestamps_since_last_report_ = 0; +} + +void StatisticsCalculator::ResetWaitingTimeStatistics() { + memset(waiting_times_, 0, kLenWaitingTimes * sizeof(waiting_times_[0])); + len_waiting_times_ = 0; + next_waiting_time_index_ = 0; +} + +void StatisticsCalculator::ExpandedVoiceSamples(int num_samples) { + expanded_voice_samples_ += num_samples; +} + +void StatisticsCalculator::ExpandedNoiseSamples(int num_samples) { + expanded_noise_samples_ += num_samples; +} + +void StatisticsCalculator::PreemptiveExpandedSamples(int num_samples) { + preemptive_samples_ += num_samples; +} + +void StatisticsCalculator::AcceleratedSamples(int num_samples) { + accelerate_samples_ += num_samples; +} + +void StatisticsCalculator::AddZeros(int num_samples) { + added_zero_samples_ += num_samples; +} + +void StatisticsCalculator::PacketsDiscarded(int num_packets) { + discarded_packets_ += num_packets; +} + +void StatisticsCalculator::LostSamples(int num_samples) { + lost_timestamps_ += num_samples; +} + +void StatisticsCalculator::IncreaseCounter(int num_samples, int fs_hz) { + timestamps_since_last_report_ += num_samples; + if (timestamps_since_last_report_ > + static_cast(fs_hz * kMaxReportPeriod)) { + lost_timestamps_ = 0; + timestamps_since_last_report_ = 0; + discarded_packets_ = 0; + } +} + +void StatisticsCalculator::StoreWaitingTime(int waiting_time_ms) { + assert(next_waiting_time_index_ < kLenWaitingTimes); + waiting_times_[next_waiting_time_index_] = waiting_time_ms; + next_waiting_time_index_++; + if (next_waiting_time_index_ >= kLenWaitingTimes) { + next_waiting_time_index_ = 0; + } + if (len_waiting_times_ < kLenWaitingTimes) { + len_waiting_times_++; + } +} + +void StatisticsCalculator::GetNetworkStatistics( + int fs_hz, + int num_samples_in_buffers, + int samples_per_packet, + const DelayManager& delay_manager, + const DecisionLogic& decision_logic, + NetEqNetworkStatistics *stats) { + if (fs_hz <= 0 || !stats) { + assert(false); + return; + } + + stats->added_zero_samples = added_zero_samples_; + stats->current_buffer_size_ms = num_samples_in_buffers * 1000 / fs_hz; + const int ms_per_packet = decision_logic.packet_length_samples() / + (fs_hz / 1000); + stats->preferred_buffer_size_ms = (delay_manager.TargetLevel() >> 8) * + ms_per_packet; + stats->jitter_peaks_found = delay_manager.PeakFound(); + stats->clockdrift_ppm = delay_manager.AverageIAT(); + + stats->packet_loss_rate = + CalculateQ14Ratio(lost_timestamps_, timestamps_since_last_report_); + + const unsigned discarded_samples = discarded_packets_ * samples_per_packet; + stats->packet_discard_rate = + CalculateQ14Ratio(discarded_samples, timestamps_since_last_report_); + + stats->accelerate_rate = + CalculateQ14Ratio(accelerate_samples_, timestamps_since_last_report_); + + stats->preemptive_rate = + CalculateQ14Ratio(preemptive_samples_, timestamps_since_last_report_); + + stats->expand_rate = + CalculateQ14Ratio(expanded_voice_samples_ + expanded_noise_samples_, + timestamps_since_last_report_); + + // Reset counters. + ResetMcu(); + Reset(); +} + +void StatisticsCalculator::WaitingTimes(std::vector* waiting_times) { + if (!waiting_times) { + return; + } + waiting_times->assign(waiting_times_, waiting_times_ + len_waiting_times_); + ResetWaitingTimeStatistics(); +} + +int StatisticsCalculator::CalculateQ14Ratio(uint32_t numerator, + uint32_t denominator) { + if (numerator == 0) { + return 0; + } else if (numerator < denominator) { + // Ratio must be smaller than 1 in Q14. + assert((numerator << 14) / denominator < (1 << 14)); + return (numerator << 14) / denominator; + } else { + // Will not produce a ratio larger than 1, since this is probably an error. + return 1 << 14; + } +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/statistics_calculator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/statistics_calculator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/statistics_calculator.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/statistics_calculator.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_STATISTICS_CALCULATOR_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_STATISTICS_CALCULATOR_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Forward declarations. +class DecisionLogic; +class DelayManager; + +// This class handles various network statistics in NetEq. +class StatisticsCalculator { + public: + StatisticsCalculator(); + + virtual ~StatisticsCalculator() {} + + // Resets most of the counters. + void Reset(); + + // Resets the counters that are not handled by Reset(). + void ResetMcu(); + + // Resets the waiting time statistics. + void ResetWaitingTimeStatistics(); + + // Reports that |num_samples| samples were produced through expansion, and + // that the expansion produced other than just noise samples. + void ExpandedVoiceSamples(int num_samples); + + // Reports that |num_samples| samples were produced through expansion, and + // that the expansion produced only noise samples. + void ExpandedNoiseSamples(int num_samples); + + // Reports that |num_samples| samples were produced through preemptive + // expansion. + void PreemptiveExpandedSamples(int num_samples); + + // Reports that |num_samples| samples were removed through accelerate. + void AcceleratedSamples(int num_samples); + + // Reports that |num_samples| zeros were inserted into the output. + void AddZeros(int num_samples); + + // Reports that |num_packets| packets were discarded. + void PacketsDiscarded(int num_packets); + + // Reports that |num_samples| were lost. + void LostSamples(int num_samples); + + // Increases the report interval counter with |num_samples| at a sample rate + // of |fs_hz|. + void IncreaseCounter(int num_samples, int fs_hz); + + // Stores new packet waiting time in waiting time statistics. + void StoreWaitingTime(int waiting_time_ms); + + // Returns the current network statistics in |stats|. The current sample rate + // is |fs_hz|, the total number of samples in packet buffer and sync buffer + // yet to play out is |num_samples_in_buffers|, and the number of samples per + // packet is |samples_per_packet|. + void GetNetworkStatistics(int fs_hz, + int num_samples_in_buffers, + int samples_per_packet, + const DelayManager& delay_manager, + const DecisionLogic& decision_logic, + NetEqNetworkStatistics *stats); + + void WaitingTimes(std::vector* waiting_times); + + private: + static const int kMaxReportPeriod = 60; // Seconds before auto-reset. + static const int kLenWaitingTimes = 100; + + // Calculates numerator / denominator, and returns the value in Q14. + static int CalculateQ14Ratio(uint32_t numerator, uint32_t denominator); + + uint32_t preemptive_samples_; + uint32_t accelerate_samples_; + int added_zero_samples_; + uint32_t expanded_voice_samples_; + uint32_t expanded_noise_samples_; + int discarded_packets_; + uint32_t lost_timestamps_; + uint32_t timestamps_since_last_report_; + int waiting_times_[kLenWaitingTimes]; // Used as a circular buffer. + int len_waiting_times_; + int next_waiting_time_index_; + + DISALLOW_COPY_AND_ASSIGN(StatisticsCalculator); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_STATISTICS_CALCULATOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/sync_buffer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/sync_buffer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/sync_buffer.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/sync_buffer.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include // Access to min. + +#include "webrtc/modules/audio_coding/neteq/sync_buffer.h" + +namespace webrtc { + +size_t SyncBuffer::FutureLength() const { + return Size() - next_index_; +} + +void SyncBuffer::PushBack(const AudioMultiVector& append_this) { + size_t samples_added = append_this.Size(); + AudioMultiVector::PushBack(append_this); + AudioMultiVector::PopFront(samples_added); + if (samples_added <= next_index_) { + next_index_ -= samples_added; + } else { + // This means that we are pushing out future data that was never used. +// assert(false); + // TODO(hlundin): This assert must be disabled to support 60 ms frames. + // This should not happen even for 60 ms frames, but it does. Investigate + // why. + next_index_ = 0; + } + dtmf_index_ -= std::min(dtmf_index_, samples_added); +} + +void SyncBuffer::PushFrontZeros(size_t length) { + InsertZerosAtIndex(length, 0); +} + +void SyncBuffer::InsertZerosAtIndex(size_t length, size_t position) { + position = std::min(position, Size()); + length = std::min(length, Size() - position); + AudioMultiVector::PopBack(length); + for (size_t channel = 0; channel < Channels(); ++channel) { + channels_[channel]->InsertZerosAt(length, position); + } + if (next_index_ >= position) { + // We are moving the |next_index_| sample. + set_next_index(next_index_ + length); // Overflow handled by subfunction. + } + if (dtmf_index_ > 0 && dtmf_index_ >= position) { + // We are moving the |dtmf_index_| sample. + set_dtmf_index(dtmf_index_ + length); // Overflow handled by subfunction. + } +} + +void SyncBuffer::ReplaceAtIndex(const AudioMultiVector& insert_this, + size_t length, + size_t position) { + position = std::min(position, Size()); // Cap |position| in the valid range. + length = std::min(length, Size() - position); + AudioMultiVector::OverwriteAt(insert_this, length, position); +} + +void SyncBuffer::ReplaceAtIndex(const AudioMultiVector& insert_this, + size_t position) { + ReplaceAtIndex(insert_this, insert_this.Size(), position); +} + +size_t SyncBuffer::GetNextAudioInterleaved(size_t requested_len, + int16_t* output) { + if (!output) { + assert(false); + return 0; + } + size_t samples_to_read = std::min(FutureLength(), requested_len); + ReadInterleavedFromIndex(next_index_, samples_to_read, output); + next_index_ += samples_to_read; + return samples_to_read; +} + +void SyncBuffer::IncreaseEndTimestamp(uint32_t increment) { + end_timestamp_ += increment; +} + +void SyncBuffer::Flush() { + Zeros(Size()); + next_index_ = Size(); + end_timestamp_ = 0; + dtmf_index_ = 0; +} + +void SyncBuffer::set_next_index(size_t value) { + // Cannot set |next_index_| larger than the size of the buffer. + next_index_ = std::min(value, Size()); +} + +void SyncBuffer::set_dtmf_index(size_t value) { + // Cannot set |dtmf_index_| larger than the size of the buffer. + dtmf_index_ = std::min(value, Size()); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/sync_buffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/sync_buffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/sync_buffer.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/sync_buffer.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_SYNC_BUFFER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_SYNC_BUFFER_H_ + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +class SyncBuffer : public AudioMultiVector { + public: + SyncBuffer(size_t channels, size_t length) + : AudioMultiVector(channels, length), + next_index_(length), + end_timestamp_(0), + dtmf_index_(0) {} + + virtual ~SyncBuffer() {} + + // Returns the number of samples yet to play out form the buffer. + size_t FutureLength() const; + + // Adds the contents of |append_this| to the back of the SyncBuffer. Removes + // the same number of samples from the beginning of the SyncBuffer, to + // maintain a constant buffer size. The |next_index_| is updated to reflect + // the move of the beginning of "future" data. + void PushBack(const AudioMultiVector& append_this); + + // Adds |length| zeros to the beginning of each channel. Removes + // the same number of samples from the end of the SyncBuffer, to + // maintain a constant buffer size. The |next_index_| is updated to reflect + // the move of the beginning of "future" data. + // Note that this operation may delete future samples that are waiting to + // be played. + void PushFrontZeros(size_t length); + + // Inserts |length| zeros into each channel at index |position|. The size of + // the SyncBuffer is kept constant, which means that the last |length| + // elements in each channel will be purged. + virtual void InsertZerosAtIndex(size_t length, size_t position); + + // Overwrites each channel in this SyncBuffer with values taken from + // |insert_this|. The values are taken from the beginning of |insert_this| and + // are inserted starting at |position|. |length| values are written into each + // channel. The size of the SyncBuffer is kept constant. That is, if |length| + // and |position| are selected such that the new data would extend beyond the + // end of the current SyncBuffer, the buffer is not extended. + // The |next_index_| is not updated. + virtual void ReplaceAtIndex(const AudioMultiVector& insert_this, + size_t length, + size_t position); + + // Same as the above method, but where all of |insert_this| is written (with + // the same constraints as above, that the SyncBuffer is not extended). + virtual void ReplaceAtIndex(const AudioMultiVector& insert_this, + size_t position); + + // Reads |requested_len| samples from each channel and writes them interleaved + // into |output|. The |next_index_| is updated to point to the sample to read + // next time. + size_t GetNextAudioInterleaved(size_t requested_len, int16_t* output); + + // Adds |increment| to |end_timestamp_|. + void IncreaseEndTimestamp(uint32_t increment); + + // Flushes the buffer. The buffer will contain only zeros after the flush, and + // |next_index_| will point to the end, like when the buffer was first + // created. + void Flush(); + + const AudioVector& Channel(size_t n) const { return *channels_[n]; } + AudioVector& Channel(size_t n) { return *channels_[n]; } + + // Accessors and mutators. + size_t next_index() const { return next_index_; } + void set_next_index(size_t value); + uint32_t end_timestamp() const { return end_timestamp_; } + void set_end_timestamp(uint32_t value) { end_timestamp_ = value; } + size_t dtmf_index() const { return dtmf_index_; } + void set_dtmf_index(size_t value); + + private: + size_t next_index_; + uint32_t end_timestamp_; // The timestamp of the last sample in the buffer. + size_t dtmf_index_; // Index to the first non-DTMF sample in the buffer. + + DISALLOW_COPY_AND_ASSIGN(SyncBuffer); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_SYNC_BUFFER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/sync_buffer_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/sync_buffer_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/sync_buffer_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/sync_buffer_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/sync_buffer.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace webrtc { + +TEST(SyncBuffer, CreateAndDestroy) { + // Create a SyncBuffer with two channels and 10 samples each. + static const size_t kLen = 10; + static const size_t kChannels = 2; + SyncBuffer sync_buffer(kChannels, kLen); + EXPECT_EQ(kChannels, sync_buffer.Channels()); + EXPECT_EQ(kLen, sync_buffer.Size()); + // When the buffer is empty, the next index to play out is at the end. + EXPECT_EQ(kLen, sync_buffer.next_index()); + // Verify that all elements are zero. + for (size_t channel = 0; channel < kChannels; ++channel) { + for (size_t i = 0; i < kLen; ++i) { + EXPECT_EQ(0, sync_buffer[channel][i]); + } + } +} + +TEST(SyncBuffer, SetNextIndex) { + // Create a SyncBuffer with two channels and 100 samples each. + static const size_t kLen = 100; + static const size_t kChannels = 2; + SyncBuffer sync_buffer(kChannels, kLen); + sync_buffer.set_next_index(0); + EXPECT_EQ(0u, sync_buffer.next_index()); + sync_buffer.set_next_index(kLen / 2); + EXPECT_EQ(kLen / 2, sync_buffer.next_index()); + sync_buffer.set_next_index(kLen); + EXPECT_EQ(kLen, sync_buffer.next_index()); + // Try to set larger than the buffer size; should cap at buffer size. + sync_buffer.set_next_index(kLen + 1); + EXPECT_EQ(kLen, sync_buffer.next_index()); +} + +TEST(SyncBuffer, PushBackAndFlush) { + // Create a SyncBuffer with two channels and 100 samples each. + static const size_t kLen = 100; + static const size_t kChannels = 2; + SyncBuffer sync_buffer(kChannels, kLen); + static const size_t kNewLen = 10; + AudioMultiVector new_data(kChannels, kNewLen); + // Populate |new_data|. + for (size_t channel = 0; channel < kChannels; ++channel) { + for (size_t i = 0; i < kNewLen; ++i) { + new_data[channel][i] = i; + } + } + // Push back |new_data| into |sync_buffer|. This operation should pop out + // data from the front of |sync_buffer|, so that the size of the buffer + // remains the same. The |next_index_| should also move with the same length. + sync_buffer.PushBack(new_data); + ASSERT_EQ(kLen, sync_buffer.Size()); + // Verify that |next_index_| moved accordingly. + EXPECT_EQ(kLen - kNewLen, sync_buffer.next_index()); + // Verify the new contents. + for (size_t channel = 0; channel < kChannels; ++channel) { + for (size_t i = 0; i < kNewLen; ++i) { + EXPECT_EQ(new_data[channel][i], + sync_buffer[channel][sync_buffer.next_index() + i]); + } + } + + // Now flush the buffer, and verify that it is all zeros, and that next_index + // points to the end. + sync_buffer.Flush(); + ASSERT_EQ(kLen, sync_buffer.Size()); + EXPECT_EQ(kLen, sync_buffer.next_index()); + for (size_t channel = 0; channel < kChannels; ++channel) { + for (size_t i = 0; i < kLen; ++i) { + EXPECT_EQ(0, sync_buffer[channel][i]); + } + } +} + +TEST(SyncBuffer, PushFrontZeros) { + // Create a SyncBuffer with two channels and 100 samples each. + static const size_t kLen = 100; + static const size_t kChannels = 2; + SyncBuffer sync_buffer(kChannels, kLen); + static const size_t kNewLen = 10; + AudioMultiVector new_data(kChannels, kNewLen); + // Populate |new_data|. + for (size_t channel = 0; channel < kChannels; ++channel) { + for (size_t i = 0; i < kNewLen; ++i) { + new_data[channel][i] = 1000 + i; + } + } + sync_buffer.PushBack(new_data); + EXPECT_EQ(kLen, sync_buffer.Size()); + + // Push |kNewLen| - 1 zeros into each channel in the front of the SyncBuffer. + sync_buffer.PushFrontZeros(kNewLen - 1); + EXPECT_EQ(kLen, sync_buffer.Size()); // Size should remain the same. + // Verify that |next_index_| moved accordingly. Should be at the end - 1. + EXPECT_EQ(kLen - 1, sync_buffer.next_index()); + // Verify the zeros. + for (size_t channel = 0; channel < kChannels; ++channel) { + for (size_t i = 0; i < kNewLen - 1; ++i) { + EXPECT_EQ(0, sync_buffer[channel][i]); + } + } + // Verify that the correct data is at the end of the SyncBuffer. + for (size_t channel = 0; channel < kChannels; ++channel) { + EXPECT_EQ(1000, sync_buffer[channel][sync_buffer.next_index()]); + } +} + +TEST(SyncBuffer, GetNextAudioInterleaved) { + // Create a SyncBuffer with two channels and 100 samples each. + static const size_t kLen = 100; + static const size_t kChannels = 2; + SyncBuffer sync_buffer(kChannels, kLen); + static const size_t kNewLen = 10; + AudioMultiVector new_data(kChannels, kNewLen); + // Populate |new_data|. + for (size_t channel = 0; channel < kChannels; ++channel) { + for (size_t i = 0; i < kNewLen; ++i) { + new_data[channel][i] = i; + } + } + // Push back |new_data| into |sync_buffer|. This operation should pop out + // data from the front of |sync_buffer|, so that the size of the buffer + // remains the same. The |next_index_| should also move with the same length. + sync_buffer.PushBack(new_data); + + // Read to interleaved output. Read in two batches, where each read operation + // should automatically update the |net_index_| in the SyncBuffer. + int16_t output[kChannels * kNewLen]; + // Note that |samples_read| is the number of samples read from each channel. + // That is, the number of samples written to |output| is + // |samples_read| * |kChannels|. + size_t samples_read = sync_buffer.GetNextAudioInterleaved(kNewLen / 2, + output); + samples_read += + sync_buffer.GetNextAudioInterleaved(kNewLen / 2, + &output[samples_read * kChannels]); + EXPECT_EQ(kNewLen, samples_read); + + // Verify the data. + int16_t* output_ptr = output; + for (size_t i = 0; i < kNewLen; ++i) { + for (size_t channel = 0; channel < kChannels; ++channel) { + EXPECT_EQ(new_data[channel][i], *output_ptr); + ++output_ptr; + } + } +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/audio_classifier_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/audio_classifier_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/audio_classifier_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/audio_classifier_test.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/audio_classifier.h" + +#include +#include +#include +#include + +#include +#include + +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +int main(int argc, char* argv[]) { + if (argc != 5) { + std::cout << "Usage: " << argv[0] << + " channels output_type " + << std::endl << std::endl; + std::cout << "Where channels can be 1 (mono) or 2 (interleaved stereo),"; + std::cout << " outputs can be 1 (classification (boolean)) or 2"; + std::cout << " (classification and music probability (float))," + << std::endl; + std::cout << "and the sampling frequency is assumed to be 48 kHz." + << std::endl; + return -1; + } + + const int kFrameSizeSamples = 960; + int channels = atoi(argv[1]); + if (channels < 1 || channels > 2) { + std::cout << "Disallowed number of channels " << channels << std::endl; + return -1; + } + + int outputs = atoi(argv[2]); + if (outputs < 1 || outputs > 2) { + std::cout << "Disallowed number of outputs " << outputs << std::endl; + return -1; + } + + const int data_size = channels * kFrameSizeSamples; + webrtc::scoped_ptr in(new int16_t[data_size]); + + std::string input_filename = argv[3]; + std::string output_filename = argv[4]; + + std::cout << "Input file: " << input_filename << std::endl; + std::cout << "Output file: " << output_filename << std::endl; + + FILE* in_file = fopen(input_filename.c_str(), "rb"); + if (!in_file) { + std::cout << "Cannot open input file " << input_filename << std::endl; + return -1; + } + + FILE* out_file = fopen(output_filename.c_str(), "wb"); + if (!out_file) { + std::cout << "Cannot open output file " << output_filename << std::endl; + return -1; + } + + webrtc::AudioClassifier classifier; + int frame_counter = 0; + int music_counter = 0; + while (fread(in.get(), sizeof(*in.get()), + data_size, in_file) == (size_t) data_size) { + bool is_music = classifier.Analysis(in.get(), data_size, channels); + if (!fwrite(&is_music, sizeof(is_music), 1, out_file)) { + std::cout << "Error writing." << std::endl; + return -1; + } + if (is_music) { + music_counter++; + } + std::cout << "frame " << frame_counter << " decision " << is_music; + if (outputs == 2) { + float music_prob = classifier.music_probability(); + if (!fwrite(&music_prob, sizeof(music_prob), 1, out_file)) { + std::cout << "Error writing." << std::endl; + return -1; + } + std::cout << " music prob " << music_prob; + } + std::cout << std::endl; + frame_counter++; + } + std::cout << frame_counter << " frames processed." << std::endl; + if (frame_counter > 0) { + float music_percentage = music_counter / static_cast(frame_counter); + std::cout << music_percentage << " percent music." << std::endl; + } + + fclose(in_file); + fclose(out_file); + return 0; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/delay_tool/plot_neteq_delay.m thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/delay_tool/plot_neteq_delay.m --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/delay_tool/plot_neteq_delay.m 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/delay_tool/plot_neteq_delay.m 2015-02-03 14:33:34.000000000 +0000 @@ -57,6 +57,7 @@ s.playout_delay=s.playout_delay(ix); s.pt=s.pt(ix); s.optbuf=s.optbuf(ix); + plen=plen(ix); s.decode=s.decode(ix); end @@ -75,30 +76,17 @@ s.playout_delay=s.playout_delay(sort_ix); s.pt=s.pt(sort_ix); -ts_unw = unwrap_ts(s.ts); -unwrapped = any(ts_unw ~= s.ts); -send_t = ts_unw - ts_unw(1); - +send_t=s.ts-s.ts(1); if length(s.fs)<1 warning('No info about sample rate found in file. Using default 8000.'); s.fs(1)=8000; s.fschange_ts(1)=min(s.ts); -elseif s.fschange_ts(1) ~= s.ts(1) - if ~unwrapped - s.fschange_ts(1) = s.ts(1); - else - error('TS wrapped, and sample rate change info is not found at the start of file => problem...') - end +elseif s.fschange_ts(1)>min(s.ts) + s.fschange_ts(1)=min(s.ts); end end_ix=length(send_t); for k=length(s.fs):-1:1 - if (k < length(s.fs) && s.fschange_ts(k) > s.fschange_ts(k+1)) - % The sample rate changes are out of order, probably due to - % packet re-ordering. - warning('fschange_ts is out of order') - continue % Skip to the next one. - end start_ix=find(s.ts==s.fschange_ts(k)); send_t(start_ix:end_ix)=send_t(start_ix:end_ix)/s.fs(k)*1000; s.playout_delay(start_ix:end_ix)=s.playout_delay(start_ix:end_ix)/s.fs(k)*1000; @@ -154,14 +142,12 @@ mean_delay = mean(s.decode(use_ix)+s.playout_delay(use_ix)-send_t(use_ix)); neteq_delay = mean(s.decode(use_ix)+s.playout_delay(use_ix)-s.arrival(use_ix)); -max_neteq_delay = max(s.decode(use_ix)+s.playout_delay(use_ix)-s.arrival(use_ix)); Npack=max(s.sn(delayskip_ix:end))-min(s.sn(delayskip_ix:end))+1; nw_lossrate=(Npack-length(s.sn(delayskip_ix:end)))/Npack; neteq_lossrate=(length(s.sn(delayskip_ix:end))-length(use_ix))/Npack; delay_struct=struct('mean_delay',mean_delay,'neteq_delay',neteq_delay,... - 'max_neteq_delay', max_neteq_delay,... 'nw_lossrate',nw_lossrate,'neteq_lossrate',neteq_lossrate,... 'tot_expand',round(s.tot_expand),'tot_accelerate',round(s.tot_accelerate),... 'tot_preemptive',round(s.tot_preemptive),'tot_time',tot_time,... @@ -174,7 +160,7 @@ else delayvalues=[]; end -end + % SUBFUNCTIONS % @@ -193,15 +179,9 @@ x(n+1:end)=x(n+1:end)-65536; end - jumps=find(abs((diff(x)-1))>65000); + jumps=find(abs((diff(x(n+1:end))-1))>65000); end y=x; -end - -function y = unwrap_ts(x) - max_u32 = 4294967295; % 0xFFFFFFFF - % Use the unwrap function made for unrwapping phase angle in radians. - y = round(max_u32 / (2*pi) * unwrap(x * 2*pi / max_u32)); -end +return; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/neteq_isac_quality_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/neteq_isac_quality_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/neteq_isac_quality_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/neteq_isac_quality_test.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h" +#include "webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h" +#include "webrtc/test/testsupport/fileutils.h" + +using google::RegisterFlagValidator; +using google::ParseCommandLineFlags; +using std::string; +using testing::InitGoogleTest; + +namespace webrtc { +namespace test { + +static const int kIsacBlockDurationMs = 30; +static const int kIsacInputSamplingKhz = 16; +static const int kIsacOutputSamplingKhz = 16; + +// Define switch for input file name. +static bool ValidateInFilename(const char* flagname, const string& value) { + FILE* fid = fopen(value.c_str(), "rb"); + if (fid != NULL) { + fclose(fid); + return true; + } + printf("Invalid input filename."); + return false; +} + +DEFINE_string(in_filename, + ResourcePath("audio_coding/speech_mono_16kHz", "pcm"), + "Filename for input audio (should be 16 kHz sampled mono)."); + +static const bool in_filename_dummy = + RegisterFlagValidator(&FLAGS_in_filename, &ValidateInFilename); + +// Define switch for output file name. +static bool ValidateOutFilename(const char* flagname, const string& value) { + FILE* fid = fopen(value.c_str(), "wb"); + if (fid != NULL) { + fclose(fid); + return true; + } + printf("Invalid output filename."); + return false; +} + +DEFINE_string(out_filename, OutputPath() + "neteq4_isac_quality_test.pcm", + "Name of output audio file."); + +static const bool out_filename_dummy = + RegisterFlagValidator(&FLAGS_out_filename, &ValidateOutFilename); + +// Define switch for bir rate. +static bool ValidateBitRate(const char* flagname, int32_t value) { + if (value >= 10 && value <= 32) + return true; + printf("Invalid bit rate, should be between 10 and 32 kbps."); + return false; +} + +DEFINE_int32(bit_rate_kbps, 32, "Target bit rate (kbps)."); + +static const bool bit_rate_dummy = + RegisterFlagValidator(&FLAGS_bit_rate_kbps, &ValidateBitRate); + +// Define switch for runtime. +static bool ValidateRuntime(const char* flagname, int32_t value) { + if (value > 0) + return true; + printf("Invalid runtime, should be greater than 0."); + return false; +} + +DEFINE_int32(runtime_ms, 10000, "Simulated runtime (milliseconds)."); + +static const bool runtime_dummy = + RegisterFlagValidator(&FLAGS_runtime_ms, &ValidateRuntime); + +class NetEqIsacQualityTest : public NetEqQualityTest { + protected: + NetEqIsacQualityTest(); + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + virtual int EncodeBlock(int16_t* in_data, int block_size_samples, + uint8_t* payload, int max_bytes); + private: + ISACFIX_MainStruct* isac_encoder_; + int bit_rate_kbps_; +}; + +NetEqIsacQualityTest::NetEqIsacQualityTest() + : NetEqQualityTest(kIsacBlockDurationMs, kIsacInputSamplingKhz, + kIsacOutputSamplingKhz, + kDecoderISAC, + 1, + FLAGS_in_filename, + FLAGS_out_filename), + isac_encoder_(NULL), + bit_rate_kbps_(FLAGS_bit_rate_kbps) { +} + +void NetEqIsacQualityTest::SetUp() { + // Create encoder memory. + WebRtcIsacfix_Create(&isac_encoder_); + ASSERT_TRUE(isac_encoder_ != NULL); + EXPECT_EQ(0, WebRtcIsacfix_EncoderInit(isac_encoder_, 1)); + // Set bitrate and block length. + EXPECT_EQ(0, WebRtcIsacfix_Control(isac_encoder_, bit_rate_kbps_ * 1000, + kIsacBlockDurationMs)); + NetEqQualityTest::SetUp(); +} + +void NetEqIsacQualityTest::TearDown() { + // Free memory. + EXPECT_EQ(0, WebRtcIsacfix_Free(isac_encoder_)); + NetEqQualityTest::TearDown(); +} + +int NetEqIsacQualityTest::EncodeBlock(int16_t* in_data, + int block_size_samples, + uint8_t* payload, int max_bytes) { + // ISAC takes 10 ms for every call. + const int subblocks = kIsacBlockDurationMs / 10; + const int subblock_length = 10 * kIsacInputSamplingKhz; + int value = 0; + + int pointer = 0; + for (int idx = 0; idx < subblocks; idx++, pointer += subblock_length) { + // The Isac encoder does not perform encoding (and returns 0) until it + // receives a sequence of sub-blocks that amount to the frame duration. + EXPECT_EQ(0, value); + value = WebRtcIsacfix_Encode(isac_encoder_, &in_data[pointer], payload); + } + EXPECT_GT(value, 0); + return value; +} + +TEST_F(NetEqIsacQualityTest, Test) { + Simulate(FLAGS_runtime_ms); +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/neteq_opus_fec_quality_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/neteq_opus_fec_quality_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/neteq_opus_fec_quality_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/neteq_opus_fec_quality_test.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" +#include "webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h" +#include "webrtc/test/testsupport/fileutils.h" + +using google::RegisterFlagValidator; +using google::ParseCommandLineFlags; +using std::string; +using testing::InitGoogleTest; + +namespace webrtc { +namespace test { + +static const int kOpusBlockDurationMs = 20; +static const int kOpusSamplingKhz = 48; + +// Define switch for input file name. +static bool ValidateInFilename(const char* flagname, const string& value) { + FILE* fid = fopen(value.c_str(), "rb"); + if (fid != NULL) { + fclose(fid); + return true; + } + printf("Invalid input filename."); + return false; +} + +DEFINE_string(in_filename, + ResourcePath("audio_coding/speech_mono_32_48kHz", "pcm"), + "Filename for input audio (should be 48 kHz sampled raw data)."); + +static const bool in_filename_dummy = + RegisterFlagValidator(&FLAGS_in_filename, &ValidateInFilename); + +// Define switch for output file name. +static bool ValidateOutFilename(const char* flagname, const string& value) { + FILE* fid = fopen(value.c_str(), "wb"); + if (fid != NULL) { + fclose(fid); + return true; + } + printf("Invalid output filename."); + return false; +} + +DEFINE_string(out_filename, OutputPath() + "neteq4_opus_fec_quality_test.pcm", + "Name of output audio file."); + +static const bool out_filename_dummy = + RegisterFlagValidator(&FLAGS_out_filename, &ValidateOutFilename); + +// Define switch for channels. +static bool ValidateChannels(const char* flagname, int32_t value) { + if (value == 1 || value == 2) + return true; + printf("Invalid number of channels, should be either 1 or 2."); + return false; +} + +DEFINE_int32(channels, 1, "Number of channels in input audio."); + +static const bool channels_dummy = + RegisterFlagValidator(&FLAGS_channels, &ValidateChannels); + +// Define switch for bit rate. +static bool ValidateBitRate(const char* flagname, int32_t value) { + if (value >= 6 && value <= 510) + return true; + printf("Invalid bit rate, should be between 6 and 510 kbps."); + return false; +} + +DEFINE_int32(bit_rate_kbps, 32, "Target bit rate (kbps)."); + +static const bool bit_rate_dummy = + RegisterFlagValidator(&FLAGS_bit_rate_kbps, &ValidateBitRate); + +// Define switch for reported packet loss rate. +static bool ValidatePacketLossRate(const char* flagname, int32_t value) { + if (value >= 0 && value <= 100) + return true; + printf("Invalid packet loss percentile, should be between 0 and 100."); + return false; +} + +DEFINE_int32(reported_loss_rate, 10, "Reported percentile of packet loss."); + +static const bool reported_loss_rate_dummy = + RegisterFlagValidator(&FLAGS_reported_loss_rate, &ValidatePacketLossRate); + +// Define switch for runtime. +static bool ValidateRuntime(const char* flagname, int32_t value) { + if (value > 0) + return true; + printf("Invalid runtime, should be greater than 0."); + return false; +} + +DEFINE_int32(runtime_ms, 10000, "Simulated runtime (milliseconds)."); +static const bool runtime_dummy = + RegisterFlagValidator(&FLAGS_runtime_ms, &ValidateRuntime); + +DEFINE_bool(fec, true, "Whether to enable FEC for encoding."); + +class NetEqOpusFecQualityTest : public NetEqQualityTest { + protected: + NetEqOpusFecQualityTest(); + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + virtual int EncodeBlock(int16_t* in_data, int block_size_samples, + uint8_t* payload, int max_bytes); + private: + WebRtcOpusEncInst* opus_encoder_; + int channels_; + int bit_rate_kbps_; + bool fec_; + int target_loss_rate_; +}; + +NetEqOpusFecQualityTest::NetEqOpusFecQualityTest() + : NetEqQualityTest(kOpusBlockDurationMs, kOpusSamplingKhz, + kOpusSamplingKhz, + (FLAGS_channels == 1) ? kDecoderOpus : kDecoderOpus_2ch, + FLAGS_channels, + FLAGS_in_filename, + FLAGS_out_filename), + opus_encoder_(NULL), + channels_(FLAGS_channels), + bit_rate_kbps_(FLAGS_bit_rate_kbps), + fec_(FLAGS_fec), + target_loss_rate_(FLAGS_reported_loss_rate) { +} + +void NetEqOpusFecQualityTest::SetUp() { + // Create encoder memory. + WebRtcOpus_EncoderCreate(&opus_encoder_, channels_); + ASSERT_TRUE(opus_encoder_ != NULL); + // Set bitrate. + EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, bit_rate_kbps_ * 1000)); + if (fec_) { + EXPECT_EQ(0, WebRtcOpus_EnableFec(opus_encoder_)); + } + EXPECT_EQ(0, WebRtcOpus_SetPacketLossRate(opus_encoder_, + target_loss_rate_)); + NetEqQualityTest::SetUp(); +} + +void NetEqOpusFecQualityTest::TearDown() { + // Free memory. + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); + NetEqQualityTest::TearDown(); +} + +int NetEqOpusFecQualityTest::EncodeBlock(int16_t* in_data, + int block_size_samples, + uint8_t* payload, int max_bytes) { + int value = WebRtcOpus_Encode(opus_encoder_, in_data, + block_size_samples, max_bytes, + payload); + EXPECT_GT(value, 0); + return value; +} + +TEST_F(NetEqOpusFecQualityTest, Test) { + Simulate(FLAGS_runtime_ms); +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/neteq_performance_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/neteq_performance_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/neteq_performance_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/neteq_performance_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/neteq/tools/neteq_performance_test.h" +#include "webrtc/test/testsupport/perf_test.h" +#include "webrtc/typedefs.h" + +// Runs a test with 10% packet losses and 10% clock drift, to exercise +// both loss concealment and time-stretching code. +TEST(NetEqPerformanceTest, Run) { + const int kSimulationTimeMs = 10000000; + const int kLossPeriod = 10; // Drop every 10th packet. + const double kDriftFactor = 0.1; + int64_t runtime = webrtc::test::NetEqPerformanceTest::Run( + kSimulationTimeMs, kLossPeriod, kDriftFactor); + ASSERT_GT(runtime, 0); + webrtc::test::PrintResult( + "neteq_performance", "", "10_pl_10_drift", runtime, "ms", true); +} + +// Runs a test with neither packet losses nor clock drift, to put +// emphasis on the "good-weather" code path, which is presumably much +// more lightweight. +TEST(NetEqPerformanceTest, RunClean) { + const int kSimulationTimeMs = 10000000; + const int kLossPeriod = 0; // No losses. + const double kDriftFactor = 0.0; // No clock drift. + int64_t runtime = webrtc::test::NetEqPerformanceTest::Run( + kSimulationTimeMs, kLossPeriod, kDriftFactor); + ASSERT_GT(runtime, 0); + webrtc::test::PrintResult( + "neteq_performance", "", "0_pl_0_drift", runtime, "ms", true); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NetEqRTPplay.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NetEqRTPplay.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NetEqRTPplay.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NetEqRTPplay.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,1811 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -//TODO(hlundin): Reformat file to meet style guide. - -#include -#include - -/* header includes */ -#include "typedefs.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_internal.h" -#include "webrtc_neteq_help_macros.h" -#include "neteq_error_codes.h" // for the API test - -#include "NETEQTEST_RTPpacket.h" -#include "NETEQTEST_DummyRTPpacket.h" -#include "NETEQTEST_NetEQClass.h" -#include "NETEQTEST_CodecClass.h" - -#ifdef WEBRTC_ANDROID -#include // isalpha -#endif -#include -#include -#include -#include -#include - -#ifdef WIN32 -#include -#include -#endif - -#ifdef WEBRTC_LINUX -#include -#include -#include -#endif - -//#include "vld.h" - -//#define NETEQ_DELAY_LOGGING -//#define DUMMY_SLAVE_CHANNEL - -#ifdef NETEQ_DELAY_LOGGING -#include "delay_logging.h" -#define DUMMY_SLAVE_CHANNEL // do not use a slave channel, only generate zeros instead -#endif - - -/************************/ -/* Define payload types */ -/************************/ - -// Payload types are defined in the textfile ptypes.txt, and can be changed after compilation. - - - -/*********************/ -/* Misc. definitions */ -/*********************/ - -#define TIME_STEP 1 -#define FIRSTLINELEN 40 -#define MAX_NETEQ_BUFFERSIZE 170000 //100000 -#define CHECK_ZERO(a) {int errCode = a; char tempErrName[WEBRTC_NETEQ_MAX_ERROR_NAME]; if((errCode)!=0){errCode = WebRtcNetEQ_GetErrorCode(inst); WebRtcNetEQ_GetErrorName(errCode, tempErrName, WEBRTC_NETEQ_MAX_ERROR_NAME); printf("\n %s \n line: %d \n error at %s\n Error Code = %d\n",__FILE__,__LINE__,#a, errCode); exit(0);}} -#define CHECK_NOT_NULL(a) if((a)==NULL){printf("\n %s \n line: %d \nerror at %s\n",__FILE__,__LINE__,#a );return(-1);} -//#define PLAY_CLEAN // ignore arrival times and let the packets arrive according to RTP timestamps -#define HDR_SIZE 8 // rtpplay packet header size in bytes -//#define JUNK_DATA // scramble the payloads to test error resilience -//#define ZERO_TS_START - -#ifdef JUNK_DATA - #define SEED_FILE "randseed.txt" -#endif - -#ifdef WIN32 -#define MY_MAX_DRIVE _MAX_DRIVE -#define MY_MAX_PATH _MAX_PATH -#define MY_MAX_FNAME _MAX_FNAME -#define MY_MAX_EXT _MAX_EXT - -#elif defined(WEBRTC_LINUX) -#include -#define MY_MAX_PATH PATH_MAX - -#elif defined(WEBRTC_MAC) -#include -#define MY_MAX_PATH PATH_MAX -#endif // WEBRTC_MAC - -/************/ -/* Typedefs */ -/************/ - -typedef struct { - enum WebRtcNetEQDecoder codec; - enum stereoModes stereo; - NETEQTEST_Decoder * decoder[2]; - int fs; -} decoderStruct; - - -/*************************/ -/* Function declarations */ -/*************************/ - -void stereoInterleave(int16_t *data, int16_t totalLen); -int getNextRecoutTime(FILE *fp, uint32_t *nextTime); -void getNextExtraDelay(FILE *fp, uint32_t *t, int *d); -bool splitStereo(NETEQTEST_RTPpacket* rtp, NETEQTEST_RTPpacket* rtpSlave, - const int16_t *stereoPtype, const enum stereoModes *stereoMode, int noOfStereoCodecs, - const int16_t *cngPtype, int noOfCngCodecs, - bool *isStereo); -void parsePtypeFile(FILE *ptypeFile, std::map* decoders); -int populateUsedCodec(std::map* decoders, enum WebRtcNetEQDecoder *usedCodec); -void createAndInsertDecoders (NETEQTEST_NetEQClass *neteq, std::map* decoders, int channelNumber); -void free_coders(std::map & decoders); -int doAPItest(); -bool changeStereoMode(NETEQTEST_RTPpacket & rtp, std::map & decoders, enum stereoModes *stereoMode); - - - -/********************/ -/* Global variables */ -/********************/ - -int16_t NetEqPacketBuffer[MAX_NETEQ_BUFFERSIZE>>1]; -int16_t NetEqPacketBufferSlave[MAX_NETEQ_BUFFERSIZE>>1]; - -#ifdef NETEQ_DELAY_LOGGING -extern "C" { - FILE *delay_fid2; /* file pointer */ - uint32_t tot_received_packets=0; -} -#endif - -#ifdef DEF_BUILD_DATE -extern char BUILD_DATE; -#endif - -uint32_t writtenSamples = 0; -uint32_t simClock=0; - -int main(int argc, char* argv[]) -{ - std::vector NetEQvector; - - enum WebRtcNetEQDecoder usedCodec[kDecoderReservedEnd-1]; - int noOfCodecs; - int ok; - int16_t out_data[640*2]; - int16_t outLen, writeLen; - int fs = 8000; - WebRtcNetEQ_RTCPStat RTCPstat; -#ifdef WIN32 - char outdrive[MY_MAX_DRIVE]; - char outpath[MY_MAX_PATH]; - char outfile[MY_MAX_FNAME]; - char outext[MY_MAX_EXT]; -#endif - char outfilename[MY_MAX_PATH]; -#ifdef NETEQ_DELAY_LOGGING - float clock_float; - int temp_var; -#endif -#ifdef JUNK_DATA - FILE *seedfile; -#endif - FILE *recoutTimes = NULL; - FILE *extraDelays = NULL; - WebRtcNetEQPlayoutMode streamingMode = kPlayoutOn; - bool preParseRTP = false; - bool rtpOnly = false; - int packetLen = 0; - int packetCount = 0; - std::map decoders; - bool dummyRtp = false; - bool noDecode = false; - bool filterSSRC = false; - uint32_t ssrc; - -#ifdef DEF_BUILD_DATE - printf("Build time: %s\n", __BUILD_DATE); -#endif - - /* check number of parameters */ - if ((argc < 3) -#ifdef WIN32 // implicit output file name possible for windows - && (argc < 2) -#endif - ) { - /* print help text and exit */ - printf("Test program for NetEQ.\n"); - printf("The program reads an RTP stream from file and inserts it into NetEQ.\n"); - printf("The format of the RTP stream file should be the same as for rtpplay,\n"); - printf("and can be obtained e.g., from Ethereal by using\n"); - printf("Statistics -> RTP -> Show All Streams -> [select a stream] -> Save As\n\n"); - printf("Usage:\n\n"); -#ifdef WIN32 - printf("%s RTPfile [outfile] [-options]\n", argv[0]); -#else - printf("%s RTPfile outfile [-options]\n", argv[0]); -#endif - printf("where:\n"); - - printf("RTPfile : RTP stream input file\n\n"); - - printf("outfile : PCM speech output file\n"); - printf(" Output file name is derived from RTP file name if omitted\n\n"); - - printf("-options are optional switches:\n"); - printf("\t-recout datfile : supply recout times\n"); - printf("\t-extradelay datfile : supply extra delay settings and timing\n"); - printf("\t-streaming : engage streaming mode\n"); - printf("\t-fax : engage fax mode\n"); - printf("\t-preparsertp : use RecIn with pre-parsed RTP\n"); - printf("\t-rtponly packLenBytes : input file consists of constant size RTP packets without RTPplay headers\n"); - printf("\t-dummyrtp : input file contains only RTP headers\n"); - printf("\t-nodecode : no decoding will be done\n"); - printf("\t-ssrc 0xNNNNNNNN : discard all other SSRCs\n"); - //printf("\t-switchms : switch from mono to stereo (copy channel) after 10 seconds\n"); - //printf("\t-duplicate : use two instances with identical input (2-channel mono)\n"); - - return(0); - } - - if (strcmp(argv[1], "-apitest")==0) { - // do API test and then return - ok=doAPItest(); - - if (ok==0) - printf("API test successful!\n"); - else - printf("API test failed!\n"); - - return(ok); - } - - FILE* in_file=fopen(argv[1],"rb"); - CHECK_NOT_NULL(in_file); - printf("Input file: %s\n",argv[1]); - - int argIx = 2; // index of next argument from command line - - if ( argc >= 3 && argv[2][0] != '-' ) { // output name given on command line - strcpy(outfilename, argv[2]); - argIx++; - } else { // derive output name from input name -#ifdef WIN32 - _splitpath(argv[1],outdrive,outpath,outfile,outext); - _makepath(outfilename,outdrive,outpath,outfile,"pcm"); -#else - fprintf(stderr,"Output file name must be specified.\n"); - return(-1); -#endif - } - FILE* out_file=fopen(outfilename,"wb"); - if (out_file==NULL) { - fprintf(stderr,"Could not open file %s for writing\n", outfilename); - return(-1); - } - printf("Output file: %s\n",outfilename); - - // Parse for more arguments, all beginning with '-' - while( argIx < argc ) { - if (argv[argIx][0] != '-') { - fprintf(stderr,"Unknown input argument %s\n", argv[argIx]); - return(-1); - } - - if( strcmp(argv[argIx], "-recout") == 0 ) { - argIx++; - recoutTimes = fopen(argv[argIx], "rb"); - CHECK_NOT_NULL(recoutTimes); - argIx++; - } - else if( strcmp(argv[argIx], "-extradelay") == 0 ) { - argIx++; - extraDelays = fopen(argv[argIx], "rb"); - CHECK_NOT_NULL(extraDelays); - argIx++; - } - else if( strcmp(argv[argIx], "-streaming") == 0 ) { - argIx++; - streamingMode = kPlayoutStreaming; - } - else if( strcmp(argv[argIx], "-fax") == 0 ) { - argIx++; - streamingMode = kPlayoutFax; - } - else if( strcmp(argv[argIx], "-preparsertp") == 0 ) { - argIx++; - preParseRTP = true; - } - else if( strcmp(argv[argIx], "-rtponly") == 0 ) { - argIx++; - rtpOnly = true; - packetLen = atoi(argv[argIx]); - argIx++; - if (packetLen <= 0) - { - printf("Wrong packet size used with argument -rtponly.\n"); - exit(1); - } - } - else if (strcmp(argv[argIx], "-dummyrtp") == 0 - || strcmp(argv[argIx], "-dummy") == 0) - { - argIx++; - dummyRtp = true; - noDecode = true; // force noDecode since there are no payloads - } - else if (strcmp(argv[argIx], "-nodecode") == 0) - { - argIx++; - noDecode = true; - } - else if (strcmp(argv[argIx], "-ssrc") == 0) - { - argIx++; - filterSSRC = true; - if (sscanf(argv[argIx], "%X", &ssrc) != 1) - { - printf("Could not read SSRC argument.\n"); - exit(1); - } - argIx++; - } - //else if( strcmp(argv[argIx], "-switchms") == 0 ) { - // argIx++; - // switchMS = true; - //} - //else if( strcmp(argv[argIx], "-duplicate") == 0 ) { - // argIx++; - // duplicatePayload = true; - //} - else { - fprintf(stderr,"Unknown input argument %s\n", argv[argIx]); - return(-1); - } - } - - - -#ifdef NETEQ_DELAY_LOGGING - char delayfile[MY_MAX_PATH]; -#ifdef WIN32 - _splitpath(outfilename,outdrive,outpath,outfile,outext); - _makepath(delayfile,outdrive,outpath,outfile,"d"); -#else - sprintf(delayfile, "%s.d", outfilename); -#endif - delay_fid2 = fopen(delayfile,"wb"); - fprintf(delay_fid2, "#!NetEQ_Delay_Logging%s\n", NETEQ_DELAY_LOGGING_VERSION_STRING); -#endif - - char ptypesfile[MY_MAX_PATH]; -#ifdef WIN32 - _splitpath(argv[0],outdrive,outpath,outfile,outext); - _makepath(ptypesfile,outdrive,outpath,"ptypes","txt"); -#elif defined(WEBRTC_ANDROID) - strcpy(ptypesfile, "/sdcard/ptypes.txt"); -#else - // TODO(hlundin): Include path to ptypes, as for WIN32 above. - strcpy(ptypesfile, "ptypes.txt"); -#endif - FILE *ptypeFile = fopen(ptypesfile,"rt"); - if (!ptypeFile) { - // Check if we can find the file at the usual place in the trunk. - if (strstr(argv[0], "out/Debug/")) { - int path_len = strstr(argv[0], "out/Debug/") - argv[0]; - strncpy(ptypesfile, argv[0], path_len); - ptypesfile[path_len] = '\0'; - strcat(ptypesfile, - "webrtc/modules/audio_coding/neteq/test/ptypes.txt"); - ptypeFile = fopen(ptypesfile,"rt"); - } - } - CHECK_NOT_NULL(ptypeFile); - printf("Ptypes file: %s\n\n", ptypesfile); - - parsePtypeFile(ptypeFile, &decoders); - fclose(ptypeFile); - - noOfCodecs = populateUsedCodec(&decoders, usedCodec); - - - /* read RTP file header */ - if (!rtpOnly) - { - if (NETEQTEST_RTPpacket::skipFileHeader(in_file) != 0) - { - fprintf(stderr, "Wrong format in RTP file.\n"); - return -1; - } - } - - /* check payload type for first speech packet */ - long tempFilePos = ftell(in_file); - enum stereoModes stereoMode = stereoModeMono; - - NETEQTEST_RTPpacket *rtp; - NETEQTEST_RTPpacket *slaveRtp; - if (!dummyRtp) - { - rtp = new NETEQTEST_RTPpacket(); - slaveRtp = new NETEQTEST_RTPpacket(); - } - else - { - rtp = new NETEQTEST_DummyRTPpacket(); - slaveRtp = new NETEQTEST_DummyRTPpacket(); - } - - /* Uncomment and edit the line(s) below to block some payload types. */ - //rtp->blockPT(72); - //rtp->blockPT(23); - - /* Select a specific SSRC. */ - if (filterSSRC) { - rtp->selectSSRC(ssrc); - } - - if (!rtpOnly) - { - while (rtp->readFromFile(in_file) >= 0) - { - if (decoders.count(rtp->payloadType()) > 0 - && decoders[rtp->payloadType()].codec != kDecoderRED - && decoders[rtp->payloadType()].codec != kDecoderAVT - && decoders[rtp->payloadType()].codec != kDecoderCNG ) - { - stereoMode = decoders[rtp->payloadType()].stereo; - fs = decoders[rtp->payloadType()].fs; - break; - } - } - } - else - { - while (rtp->readFixedFromFile(in_file, packetLen) >= 0) - { - if (decoders.count(rtp->payloadType()) > 0 - && decoders[rtp->payloadType()].codec != kDecoderRED - && decoders[rtp->payloadType()].codec != kDecoderAVT - && decoders[rtp->payloadType()].codec != kDecoderCNG ) - { - stereoMode = decoders[rtp->payloadType()].stereo; - fs = decoders[rtp->payloadType()].fs; - break; - } - } - } - - fseek(in_file, tempFilePos, SEEK_SET /* from beginning */); - - /* read first packet */ - if (!rtpOnly) - { - rtp->readFromFile(in_file); - } - else - { - rtp->readFixedFromFile(in_file, packetLen); - rtp->setTime((1000 * rtp->timeStamp()) / fs); - } - if (!rtp) - { - printf("\nWarning: RTP file is empty\n\n"); - } - - - /* Initialize NetEQ instances */ - int numInst = 1; - if (stereoMode > stereoModeMono) - { - numInst = 2; - } - - for (int i = 0; i < numInst; i++) - { - // create memory, allocate, initialize, and allocate packet buffer memory - NetEQvector.push_back (new NETEQTEST_NetEQClass(usedCodec, noOfCodecs, static_cast(fs), kTCPLargeJitter)); - - createAndInsertDecoders (NetEQvector[i], &decoders, i /* channel */); - - WebRtcNetEQ_SetAVTPlayout(NetEQvector[i]->instance(),1); // enable DTMF playout - - WebRtcNetEQ_SetPlayoutMode(NetEQvector[i]->instance(), streamingMode); - - NetEQvector[i]->usePreparseRTP(preParseRTP); - - NetEQvector[i]->setNoDecode(noDecode); - - if (numInst > 1) - { - // we are using master/slave mode - if (i == 0) - { - // first instance is master - NetEQvector[i]->setMaster(); - } - else - { - // all other are slaves - NetEQvector[i]->setSlave(); - } - } - } - - -#ifdef ZERO_TS_START - uint32_t firstTS = rtp->timeStamp(); - rtp->setTimeStamp(0); -#else - uint32_t firstTS = 0; -#endif - - // check stereo mode - if (stereoMode > stereoModeMono) - { - if(rtp->splitStereo(slaveRtp, stereoMode)) - { - printf("Error in splitStereo\n"); - } - } - -#ifdef PLAY_CLEAN - uint32_t prevTS = rtp->timeStamp(); - uint32_t currTS, prev_time; -#endif - -#ifdef JUNK_DATA - unsigned int random_seed = (unsigned int) /*1196764538; */time(NULL); - srand(random_seed); - - if ( (seedfile = fopen(SEED_FILE, "a+t") ) == NULL ) { - fprintf(stderr, "Error: Could not open file %s\n", SEED_FILE); - } - else { - fprintf(seedfile, "%u\n", random_seed); - fclose(seedfile); - } -#endif - - uint32_t nextRecoutTime; - int lastRecout = getNextRecoutTime(recoutTimes, &nextRecoutTime); // does nothing if recoutTimes == NULL - - if (recoutTimes) - simClock = (rtp->time() < nextRecoutTime ? rtp->time(): nextRecoutTime); - else - simClock = rtp->time(); // start immediately with first packet - - uint32_t start_clock = simClock; - - uint32_t nextExtraDelayTime; - int extraDelay = -1; - getNextExtraDelay(extraDelays, &nextExtraDelayTime, &extraDelay); - - void *msInfo; - msInfo = malloc(WebRtcNetEQ_GetMasterSlaveInfoSize()); - if(msInfo == NULL) - return(-1); - - while(rtp->dataLen() >= 0 || (recoutTimes && !lastRecout)) { -// printf("simClock = %Lu\n", simClock); - -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_CLOCK; - clock_float = (float) simClock; - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } - if (fwrite(&clock_float, sizeof(float), 1, delay_fid2) != 1) { - return -1; - } -#endif - /* time to set extra delay */ - if (extraDelay > -1 && simClock >= nextExtraDelayTime) { - // set extra delay for all instances - for (int i = 0; i < numInst; i++) - { - WebRtcNetEQ_SetExtraDelay(NetEQvector[i]->instance(), extraDelay); - } - getNextExtraDelay(extraDelays, &nextExtraDelayTime, &extraDelay); - } - - /* check if time to receive */ - while (simClock >= rtp->time() && rtp->dataLen() >= 0) - { - if (rtp->dataLen() > 0) - { - - // insert main packet - NetEQvector[0]->recIn(*rtp); - - if (stereoMode > stereoModeMono - && slaveRtp->dataLen() > 0) - { - // insert slave packet - NetEQvector[1]->recIn(*slaveRtp); - } - - } - - /* get next packet */ -#ifdef PLAY_CLEAN - prev_time = rtp->time(); -#endif - if (!rtpOnly) - { - rtp->readFromFile(in_file); - } - else - { - rtp->readFixedFromFile(in_file, packetLen); - rtp->setTime((1000 * rtp->timeStamp()) / fs); - } - - if (rtp->dataLen() >= 0) - { - rtp->setTimeStamp(rtp->timeStamp() - firstTS); - } - - packetCount++; - - if (changeStereoMode(*rtp, decoders, &stereoMode)) - { - printf("Warning: stereo mode changed\n"); - } - - if (stereoMode > stereoModeMono) - { - if(rtp->splitStereo(slaveRtp, stereoMode)) - { - printf("Error in splitStereo\n"); - } - } - -#ifdef PLAY_CLEAN - currTS = rtp->timeStamp(); - rtp->setTime(prev_time + (currTS-prevTS)/(fs/1000)); - prevTS = currTS; -#endif - } - - /* check if time to RecOut */ - if ( (!recoutTimes && (simClock%10)==0) // recout times not given from file - || ( recoutTimes && (simClock >= nextRecoutTime) ) ) // recout times given from file - { - if (stereoMode > stereoModeMono) - { - // stereo - int16_t tempLen; - tempLen = NetEQvector[0]->recOut( out_data, msInfo ); // master - outLen = NetEQvector[1]->recOut( &out_data[tempLen], msInfo ); // slave - - assert(tempLen == outLen); - - writeLen = outLen * 2; - stereoInterleave(out_data, writeLen); - } - else - { - // mono - outLen = NetEQvector[0]->recOut( out_data ); - writeLen = outLen; - } - - // write to file - if (fwrite(out_data, writeLen, 2, out_file) != 2) { - return -1; - } - writtenSamples += writeLen; - - - lastRecout = getNextRecoutTime(recoutTimes, &nextRecoutTime); // does nothing if recoutTimes == NULL - - /* ask for statistics */ - WebRtcNetEQ_NetworkStatistics inCallStats; - WebRtcNetEQ_GetNetworkStatistics(NetEQvector[0]->instance(), &inCallStats); - - } - - /* increase time */ - simClock+=TIME_STEP; - } - - fclose(in_file); - fclose(out_file); - -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_EOF; - if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) { - return -1; - } - if (fwrite(&tot_received_packets, sizeof(uint32_t), - 1, delay_fid2) != 1) { - return -1; - } - fprintf(delay_fid2,"End of file\n"); - fclose(delay_fid2); -#endif - - WebRtcNetEQ_GetRTCPStats(NetEQvector[0]->instance(), &RTCPstat); - printf("RTCP statistics:\n"); - printf(" cum_lost : %d\n", (int) RTCPstat.cum_lost); - printf(" ext_max : %d\n", (int) RTCPstat.ext_max); - printf(" fraction_lost : %d (%f%%)\n", RTCPstat.fraction_lost, (float)(100.0*RTCPstat.fraction_lost/256.0)); - printf(" jitter : %d\n", (int) RTCPstat.jitter); - - printf("\n Call duration ms : %u\n", simClock-start_clock); - - printf("\nComplexity estimates (including sub-components):\n"); - printf(" RecIn complexity : %.2f MCPS\n", NetEQvector[0]->getRecInTime() / ((float) 1000*(simClock-start_clock))); - printf(" RecOut complexity : %.2f MCPS\n", NetEQvector[0]->getRecOutTime() / ((float) 1000*(simClock-start_clock))); - - free_coders(decoders); - //free_coders(0 /* first channel */); - // if (stereoMode > stereoModeMono) { - // free_coders(1 /* second channel */); - // } - free(msInfo); - - for (std::vector::iterator it = NetEQvector.begin(); - it < NetEQvector.end(); delete *it++) { - } - - printf("\nSimulation done!\n"); - -#ifdef JUNK_DATA - if ( (seedfile = fopen(SEED_FILE, "a+t") ) == NULL ) { - fprintf(stderr, "Error: Could not open file %s\n", SEED_FILE); - } - else { - fprintf(seedfile, "ok\n\n"); - fclose(seedfile); - } -#endif - - - // Log complexity to file -/* FILE *statfile; - statfile = fopen("complexity.txt","at"); - fprintf(statfile,"%.4f, %.4f\n", (float) totTime_RecIn.QuadPart / ((float) 1000*(simClock-start_clock)), (float) totTime_RecOut.QuadPart / ((float) 1000*(simClock-start_clock))); - fclose(statfile);*/ - - return(0); - -} - - - - - -/****************/ -/* Subfunctions */ -/****************/ - -bool splitStereo(NETEQTEST_RTPpacket* rtp, NETEQTEST_RTPpacket* rtpSlave, - const int16_t *stereoPtype, const enum stereoModes *stereoMode, int noOfStereoCodecs, - const int16_t *cngPtype, int noOfCngCodecs, - bool *isStereo) -{ - - // init - //bool isStereo = false; - enum stereoModes tempStereoMode = stereoModeMono; - bool isCng = false; - - // check payload length - if (rtp->dataLen() <= 0) { - //*isStereo = false; // don't change - return(*isStereo); - } - - // check payload type - int16_t ptype = rtp->payloadType(); - - // is this a cng payload? - for (int k = 0; k < noOfCngCodecs; k++) { - if (ptype == cngPtype[k]) { - // do not change stereo state - isCng = true; - tempStereoMode = stereoModeFrame; - } - } - - if (!isCng) - { - *isStereo = false; - - // is this payload type a stereo codec? which type? - for (int k = 0; k < noOfStereoCodecs; k++) { - if (ptype == stereoPtype[k]) { - tempStereoMode = stereoMode[k]; - *isStereo = true; - break; // exit for loop - } - } - } - - if (*isStereo) - { - // split the payload if stereo - - if(rtp->splitStereo(rtpSlave, tempStereoMode)) - { - printf("Error in splitStereo\n"); - } - - } - - return(*isStereo); - -} - -void stereoInterleave(int16_t *data, int16_t totalLen) -{ - int k; - - for(k = totalLen/2; k < totalLen; k++) { - int16_t temp = data[k]; - memmove(&data[2*k - totalLen + 2], &data[2*k - totalLen + 1], (totalLen - k -1) * sizeof(int16_t)); - data[2*k - totalLen + 1] = temp; - } -} - - -int getNextRecoutTime(FILE *fp, uint32_t *nextTime) { - - float tempTime; - - if (!fp) { - return -1; - } - - if (fread(&tempTime, sizeof(float), 1, fp) != 0) { - // not end of file - *nextTime = (uint32_t) tempTime; - return 0; - } - - *nextTime = 0; - fclose(fp); - - return 1; -} - -void getNextExtraDelay(FILE *fp, uint32_t *t, int *d) { - - float temp[2]; - - if(!fp) { - *d = -1; - return; - } - - if (fread(&temp, sizeof(float), 2, fp) != 0) { - // not end of file - *t = (uint32_t) temp[0]; - *d = (int) temp[1]; - return; - } - - *d = -1; - fclose(fp); - - return; -} - - -void parsePtypeFile(FILE *ptypeFile, std::map* decoders) -{ - int n, pt; - char codec[100]; - decoderStruct tempDecoder; - - // read first line - n = fscanf(ptypeFile, "%s %i\n", codec, &pt); - - while (n==2) - { - memset(&tempDecoder, 0, sizeof(decoderStruct)); - tempDecoder.stereo = stereoModeMono; - - if( pt >= 0 // < 0 disables this codec - && isalpha(codec[0]) ) // and is a letter - { - - /* check for stereo */ - int L = strlen(codec); - bool isStereo = false; - - if (codec[L-1] == '*') { - // stereo codec - isStereo = true; - - // remove '*' - codec[L-1] = '\0'; - } - -#ifdef CODEC_G711 - if(strcmp(codec, "pcmu") == 0) { - tempDecoder.codec = kDecoderPCMu; - tempDecoder.fs = 8000; - } - else if(strcmp(codec, "pcma") == 0) { - tempDecoder.codec = kDecoderPCMa; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_IPCMU - else if(strcmp(codec, "eg711u") == 0) { - tempDecoder.codec = kDecoderEG711u; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_IPCMA - else if(strcmp(codec, "eg711a") == 0) { - tempDecoder.codec = kDecoderEG711a; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_ILBC - else if(strcmp(codec, "ilbc") == 0) { - tempDecoder.codec = kDecoderILBC; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_ISAC - else if(strcmp(codec, "isac") == 0) { - tempDecoder.codec = kDecoderISAC; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_ISACLC - else if(strcmp(codec, "isaclc") == 0) { - tempDecoder.codec = NETEQ_CODEC_ISACLC; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_ISAC_SWB - else if(strcmp(codec, "isacswb") == 0) { - tempDecoder.codec = kDecoderISACswb; - tempDecoder.fs = 32000; - } -#endif -#ifdef CODEC_ISAC_FB - else if(strcmp(codec, "isacfb") == 0) { - tempDecoder.codec = kDecoderISACfb; - tempDecoder.fs = 32000; - } -#endif -#ifdef CODEC_IPCMWB - else if(strcmp(codec, "ipcmwb") == 0) { - tempDecoder.codec = kDecoderIPCMwb; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_G722 - else if(strcmp(codec, "g722") == 0) { - tempDecoder.codec = kDecoderG722; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_G722_1_16 - else if(strcmp(codec, "g722_1_16") == 0) { - tempDecoder.codec = kDecoderG722_1_16; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_G722_1_24 - else if(strcmp(codec, "g722_1_24") == 0) { - tempDecoder.codec = kDecoderG722_1_24; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_G722_1_32 - else if(strcmp(codec, "g722_1_32") == 0) { - tempDecoder.codec = kDecoderG722_1_32; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_G722_1C_24 - else if(strcmp(codec, "g722_1c_24") == 0) { - tempDecoder.codec = kDecoderG722_1C_24; - tempDecoder.fs = 32000; - } -#endif -#ifdef CODEC_G722_1C_32 - else if(strcmp(codec, "g722_1c_32") == 0) { - tempDecoder.codec = kDecoderG722_1C_32; - tempDecoder.fs = 32000; - } -#endif -#ifdef CODEC_G722_1C_48 - else if(strcmp(codec, "g722_1c_48") == 0) { - tempDecoder.codec = kDecoderG722_1C_48; - tempDecoder.fs = 32000; - } -#endif -#ifdef CODEC_G723 - else if(strcmp(codec, "g723") == 0) { - tempDecoder.codec = NETEQ_CODEC_G723; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_G726 - else if(strcmp(codec, "g726_16") == 0) { - tempDecoder.codec = kDecoderG726_16; - tempDecoder.fs = 8000; - } - else if(strcmp(codec, "g726_24") == 0) { - tempDecoder.codec = kDecoderG726_24; - tempDecoder.fs = 8000; - } - else if(strcmp(codec, "g726_32") == 0) { - tempDecoder.codec = kDecoderG726_32; - tempDecoder.fs = 8000; - } - else if(strcmp(codec, "g726_40") == 0) { - tempDecoder.codec = kDecoderG726_40; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_G729 - else if(strcmp(codec, "g729") == 0) { - tempDecoder.codec = kDecoderG729; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_G729D - else if(strcmp(codec, "g729d") == 0) { - tempDecoder.codec = NETEQ_CODEC_G729D; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_G729_1 - else if(strcmp(codec, "g729_1") == 0) { - tempDecoder.codec = kDecoderG729_1; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_GSMFR - else if(strcmp(codec, "gsmfr") == 0) { - tempDecoder.codec = kDecoderGSMFR; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_GSMEFR - else if(strcmp(codec, "gsmefr") == 0) { - tempDecoder.codec = NETEQ_CODEC_GSMEFR; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_AMR - else if(strcmp(codec, "amr") == 0) { - tempDecoder.codec = kDecoderAMR; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_AMRWB - else if(strcmp(codec, "amrwb") == 0) { - tempDecoder.codec = kDecoderAMRWB; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_DVI4 - else if(strcmp(codec, "dvi4") == 0) { - tempDecoder.codec = NETEQ_CODEC_DVI4; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_SPEEX_8 - else if(strcmp(codec, "speex8") == 0) { - tempDecoder.codec = kDecoderSPEEX_8; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_SPEEX_16 - else if(strcmp(codec, "speex16") == 0) { - tempDecoder.codec = kDecoderSPEEX_16; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_CELT_32 - else if(strcmp(codec, "celt32") == 0) { - tempDecoder.codec = kDecoderCELT_32; - tempDecoder.fs = 32000; - } -#endif -#ifdef CODEC_SILK_NB - else if(strcmp(codec, "silk8") == 0) { - tempDecoder.codec = NETEQ_CODEC_SILK_8; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_SILK_WB - else if(strcmp(codec, "silk12") == 0) { - tempDecoder.codec = NETEQ_CODEC_SILK_12; - tempDecoder.fs = 16000; - } - else if(strcmp(codec, "silk16") == 0) { - tempDecoder.codec = NETEQ_CODEC_SILK_16; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_SILK_SWB - else if(strcmp(codec, "silk24") == 0) { - tempDecoder.codec = NETEQ_CODEC_SILK_24; - tempDecoder.fs = 32000; - } -#endif -#ifdef CODEC_MELPE - else if(strcmp(codec, "melpe") == 0) { - tempDecoder.codec = NETEQ_CODEC_MELPE; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_PCM16B - else if(strcmp(codec, "pcm16b") == 0) { - tempDecoder.codec = kDecoderPCM16B; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_PCM16B_WB - else if(strcmp(codec, "pcm16b_wb") == 0) { - tempDecoder.codec = kDecoderPCM16Bwb; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_PCM16B_32KHZ - else if(strcmp(codec, "pcm16b_swb32khz") == 0) { - tempDecoder.codec = kDecoderPCM16Bswb32kHz; - tempDecoder.fs = 32000; - } -#endif -#ifdef CODEC_PCM16B_48KHZ - else if(strcmp(codec, "pcm16b_swb48khz") == 0) { - tempDecoder.codec = kDecoderPCM16Bswb48kHz; - tempDecoder.fs = 48000; - } -#endif -#ifdef CODEC_CNGCODEC8 - else if(strcmp(codec, "cn") == 0) { - tempDecoder.codec = kDecoderCNG; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_CNGCODEC16 - else if(strcmp(codec, "cn_wb") == 0) { - tempDecoder.codec = kDecoderCNG; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_CNGCODEC32 - else if(strcmp(codec, "cn_swb32") == 0) { - tempDecoder.codec = kDecoderCNG; - tempDecoder.fs = 32000; - } -#endif -#ifdef CODEC_CNGCODEC48 - else if(strcmp(codec, "cn_swb48") == 0) { - tempDecoder.codec = kDecoderCNG; - tempDecoder.fs = 48000; - } -#endif -#ifdef CODEC_ATEVENT_DECODE - else if(strcmp(codec, "avt") == 0) { - tempDecoder.codec = kDecoderAVT; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_RED - else if(strcmp(codec, "red") == 0) { - tempDecoder.codec = kDecoderRED; - tempDecoder.fs = 8000; - } -#endif - else if(isalpha(codec[0])) { - printf("Unsupported codec %s\n", codec); - // read next line and continue while loop - n = fscanf(ptypeFile, "%s %i\n", codec, &pt); - continue; - } - else { - // name is not recognized, and does not start with a letter - // hence, it is commented out - // read next line and continue while loop - n = fscanf(ptypeFile, "%s %i\n", codec, &pt); - continue; - } - - // handle stereo - if (tempDecoder.codec == kDecoderCNG) - { - // always set stereo mode for CNG, even if it is not marked at stereo - tempDecoder.stereo = stereoModeFrame; - } - else if(isStereo) - { - switch(tempDecoder.codec) { - // sample based codecs - case kDecoderPCMu: - case kDecoderPCMa: - case kDecoderG722: - { - // 1 octet per sample - tempDecoder.stereo = stereoModeSample1; - break; - } - case kDecoderPCM16B: - case kDecoderPCM16Bwb: - case kDecoderPCM16Bswb32kHz: - case kDecoderPCM16Bswb48kHz: - { - // 2 octets per sample - tempDecoder.stereo = stereoModeSample2; - break; - } - - case kDecoderCELT_32: - { - tempDecoder.stereo = stereoModeDuplicate; - break; - } - // fixed-rate frame codecs -// case kDecoderG729: -// case NETEQ_CODEC_G729D: -// case NETEQ_CODEC_G729E: -// case kDecoderG722_1_16: -// case kDecoderG722_1_24: -// case kDecoderG722_1_32: -// case kDecoderG722_1C_24: -// case kDecoderG722_1C_32: -// case kDecoderG722_1C_48: -// case NETEQ_CODEC_MELPE: -// { -// tempDecoder.stereo = stereoModeFrame; -// break; -// } - default: - { - printf("Cannot use codec %s as stereo codec\n", codec); - exit(0); - } - } - } - - if (pt > 127) - { - printf("Payload type must be less than 128\n"); - exit(0); - } - - // insert into codecs map - (*decoders)[static_cast(pt)] = tempDecoder; - - } - - n = fscanf(ptypeFile, "%s %i\n", codec, &pt); - } // end while - -} - - -bool changeStereoMode(NETEQTEST_RTPpacket & rtp, std::map & decoders, enum stereoModes *stereoMode) -{ - if (decoders.count(rtp.payloadType()) > 0 - && decoders[rtp.payloadType()].codec != kDecoderRED - && decoders[rtp.payloadType()].codec != kDecoderAVT - && decoders[rtp.payloadType()].codec != kDecoderCNG ) - { - if (decoders[rtp.payloadType()].stereo != *stereoMode) - { - *stereoMode = decoders[rtp.payloadType()].stereo; - return true; // stereo mode did change - } - } - - return false; // stereo mode did not change -} - - -int populateUsedCodec(std::map* decoders, enum WebRtcNetEQDecoder *usedCodec) -{ - int numCodecs = 0; - - std::map::iterator it; - - it = decoders->begin(); - - for (int i = 0; i < static_cast(decoders->size()); i++, it++) - { - usedCodec[numCodecs] = (*it).second.codec; - numCodecs++; - } - - return numCodecs; -} - - -void createAndInsertDecoders (NETEQTEST_NetEQClass *neteq, std::map* decoders, int channelNumber) -{ - std::map::iterator it; - - for (it = decoders->begin(); it != decoders->end(); it++) - { - if (channelNumber == 0 || - ((*it).second.stereo > stereoModeMono )) - { - // create decoder instance - uint8_t pt = static_cast( (*it).first ); - NETEQTEST_Decoder **dec = &((*it).second.decoder[channelNumber]); - enum WebRtcNetEQDecoder type = (*it).second.codec; - - switch (type) - { -#ifdef CODEC_G711 - case kDecoderPCMu: - *dec = new decoder_PCMU( pt ); - break; - case kDecoderPCMa: - *dec = new decoder_PCMA( pt ); - break; -#endif -#ifdef CODEC_IPCMU - case kDecoderEG711u: - *dec = new decoder_IPCMU( pt ); - break; -#endif -#ifdef CODEC_IPCMA - case kDecoderEG711a: - *dec = new decoder_IPCMA( pt ); - break; -#endif -#ifdef CODEC_IPCMWB - case kDecoderIPCMwb: - *dec = new decoder_IPCMWB( pt ); - break; -#endif -#ifdef CODEC_ILBC - case kDecoderILBC: - *dec = new decoder_ILBC( pt ); - break; -#endif -#ifdef CODEC_ISAC - case kDecoderISAC: - *dec = new decoder_iSAC( pt ); - break; -#endif -#ifdef CODEC_ISAC_SWB - case kDecoderISACswb: - *dec = new decoder_iSACSWB( pt ); - break; -#endif -#ifdef CODEC_ISAC_FB - case kDecoderISACfb: - *dec = new decoder_iSACFB(pt); - break; -#endif -#ifdef CODEC_G729 - case kDecoderG729: - *dec = new decoder_G729( pt ); - break; - case NETEQ_CODEC_G729D: - printf("Error: G729D not supported\n"); - break; -#endif -#ifdef CODEC_G729E - case NETEQ_CODEC_G729E: - *dec = new decoder_G729E( pt ); - break; -#endif -#ifdef CODEC_G729_1 - case kDecoderG729_1: - *dec = new decoder_G729_1( pt ); - break; -#endif -#ifdef CODEC_G723 - case NETEQ_CODEC_G723: - *dec = new decoder_G723( pt ); - break; -#endif -#ifdef CODEC_PCM16B - case kDecoderPCM16B: - *dec = new decoder_PCM16B_NB( pt ); - break; -#endif -#ifdef CODEC_PCM16B_WB - case kDecoderPCM16Bwb: - *dec = new decoder_PCM16B_WB( pt ); - break; -#endif -#ifdef CODEC_PCM16B_32KHZ - case kDecoderPCM16Bswb32kHz: - *dec = new decoder_PCM16B_SWB32( pt ); - break; -#endif -#ifdef CODEC_PCM16B_48KHZ - case kDecoderPCM16Bswb48kHz: - *dec = new decoder_PCM16B_SWB48( pt ); - break; -#endif -#ifdef CODEC_DVI4 - case NETEQ_CODEC_DVI4: - *dec = new decoder_DVI4( pt ); - break; -#endif -#ifdef CODEC_G722 - case kDecoderG722: - *dec = new decoder_G722( pt ); - break; -#endif -#ifdef CODEC_G722_1_16 - case kDecoderG722_1_16: - *dec = new decoder_G722_1_16( pt ); - break; -#endif -#ifdef CODEC_G722_1_24 - case kDecoderG722_1_24: - *dec = new decoder_G722_1_24( pt ); - break; -#endif -#ifdef CODEC_G722_1_32 - case kDecoderG722_1_32: - *dec = new decoder_G722_1_32( pt ); - break; -#endif -#ifdef CODEC_G722_1C_24 - case kDecoderG722_1C_24: - *dec = new decoder_G722_1C_24( pt ); - break; -#endif -#ifdef CODEC_G722_1C_32 - case kDecoderG722_1C_32: - *dec = new decoder_G722_1C_32( pt ); - break; -#endif -#ifdef CODEC_G722_1C_48 - case kDecoderG722_1C_48: - *dec = new decoder_G722_1C_48( pt ); - break; -#endif -#ifdef CODEC_AMR - case kDecoderAMR: - *dec = new decoder_AMR( pt ); - break; -#endif -#ifdef CODEC_AMRWB - case kDecoderAMRWB: - *dec = new decoder_AMRWB( pt ); - break; -#endif -#ifdef CODEC_GSMFR - case kDecoderGSMFR: - *dec = new decoder_GSMFR( pt ); - break; -#endif -#ifdef CODEC_GSMEFR - case NETEQ_CODEC_GSMEFR: - *dec = new decoder_GSMEFR( pt ); - break; -#endif -#ifdef CODEC_G726 - case kDecoderG726_16: - *dec = new decoder_G726_16( pt ); - break; - case kDecoderG726_24: - *dec = new decoder_G726_24( pt ); - break; - case kDecoderG726_32: - *dec = new decoder_G726_32( pt ); - break; - case kDecoderG726_40: - *dec = new decoder_G726_40( pt ); - break; -#endif -#ifdef CODEC_MELPE - case NETEQ_CODEC_MELPE: -#if (_MSC_VER >= 1400) && !defined(_WIN64) // only for Visual 2005 or later, and not for x64 - *dec = new decoder_MELPE( pt ); -#endif - break; -#endif -#ifdef CODEC_SPEEX_8 - case kDecoderSPEEX_8: - *dec = new decoder_SPEEX( pt, 8000 ); - break; -#endif -#ifdef CODEC_SPEEX_16 - case kDecoderSPEEX_16: - *dec = new decoder_SPEEX( pt, 16000 ); - break; -#endif -#ifdef CODEC_CELT_32 - case kDecoderCELT_32: - if (channelNumber == 0) - *dec = new decoder_CELT( pt, 32000 ); - else - *dec = new decoder_CELTslave( pt, 32000 ); - break; -#endif -#ifdef CODEC_RED - case kDecoderRED: - *dec = new decoder_RED( pt ); - break; -#endif -#ifdef CODEC_ATEVENT_DECODE - case kDecoderAVT: - *dec = new decoder_AVT( pt ); - break; -#endif -#if (defined(CODEC_CNGCODEC8) || defined(CODEC_CNGCODEC16) || \ - defined(CODEC_CNGCODEC32) || defined(CODEC_CNGCODEC48)) - case kDecoderCNG: - *dec = new decoder_CNG( pt, static_cast((*it).second.fs) ); - break; -#endif -#ifdef CODEC_ISACLC - case NETEQ_CODEC_ISACLC: - *dec = new decoder_iSACLC( pt ); - break; -#endif -#ifdef CODEC_SILK_NB - case NETEQ_CODEC_SILK_8: -#if (_MSC_VER >= 1400) && !defined(_WIN64) // only for Visual 2005 or later, and not for x64 - *dec = new decoder_SILK8( pt ); -#endif - break; -#endif -#ifdef CODEC_SILK_WB - case NETEQ_CODEC_SILK_12: -#if (_MSC_VER >= 1400) && !defined(_WIN64) // only for Visual 2005 or later, and not for x64 - *dec = new decoder_SILK12( pt ); -#endif - break; -#endif -#ifdef CODEC_SILK_WB - case NETEQ_CODEC_SILK_16: -#if (_MSC_VER >= 1400) && !defined(_WIN64) // only for Visual 2005 or later, and not for x64 - *dec = new decoder_SILK16( pt ); -#endif - break; -#endif -#ifdef CODEC_SILK_SWB - case NETEQ_CODEC_SILK_24: -#if (_MSC_VER >= 1400) && !defined(_WIN64) // only for Visual 2005 or later, and not for x64 - *dec = new decoder_SILK24( pt ); -#endif - break; -#endif - - default: - printf("Unknown codec type encountered in createAndInsertDecoders\n"); - exit(0); - } - - // insert into codec DB - if (*dec) - { - (*dec)->loadToNetEQ(*neteq); - } - } - } - -} - - -void free_coders(std::map & decoders) -{ - std::map::iterator it; - - for (it = decoders.begin(); it != decoders.end(); it++) - { - if ((*it).second.decoder[0]) - { - delete (*it).second.decoder[0]; - } - - if ((*it).second.decoder[1]) - { - delete (*it).second.decoder[1]; - } - } -} - - - -#include "pcm16b.h" -#include "g711_interface.h" -#include "isac.h" - -int doAPItest() { - - void *inst; - enum WebRtcNetEQDecoder usedCodec; - int NetEqBufferMaxPackets, BufferSizeInBytes; - WebRtcNetEQ_CodecDef codecInst; - WebRtcNetEQ_RTCPStat RTCPstat; - uint32_t timestamp; - int memorySize; - int ok; - int overhead_bytes; - - printf("API-test:\n\n"); - - /* test that API functions return -1 if instance is NULL */ -#define CHECK_MINUS_ONE(x) {int errCode = x; if((errCode)!=-1){printf("\n API test failed at line %d: %s. Function did not return -1 as expected\n",__LINE__,#x); return(-1);}} -//#define RESET_ERROR(x) ((MainInst_t*) x)->ErrorCode = 0; - inst = NULL; - - CHECK_MINUS_ONE(WebRtcNetEQ_GetErrorCode(inst)) - CHECK_MINUS_ONE(WebRtcNetEQ_Assign(&inst, NULL)) -// printf("WARNING: Test of WebRtcNetEQ_Assign() is disabled due to a bug.\n"); - usedCodec=kDecoderPCMu; - CHECK_MINUS_ONE(WebRtcNetEQ_GetRecommendedBufferSize(inst, &usedCodec, 1, kTCPLargeJitter, &NetEqBufferMaxPackets, &BufferSizeInBytes, &overhead_bytes)) - CHECK_MINUS_ONE(WebRtcNetEQ_AssignBuffer(inst, NetEqBufferMaxPackets, NetEqPacketBuffer, BufferSizeInBytes)) - - CHECK_MINUS_ONE(WebRtcNetEQ_Init(inst, 8000)) - CHECK_MINUS_ONE(WebRtcNetEQ_SetAVTPlayout(inst, 0)) - CHECK_MINUS_ONE(WebRtcNetEQ_SetExtraDelay(inst, 17)) - CHECK_MINUS_ONE(WebRtcNetEQ_SetPlayoutMode(inst, kPlayoutOn)) - - CHECK_MINUS_ONE(WebRtcNetEQ_CodecDbReset(inst)) - CHECK_MINUS_ONE(WebRtcNetEQ_CodecDbAdd(inst, &codecInst)) - CHECK_MINUS_ONE(WebRtcNetEQ_CodecDbRemove(inst, usedCodec)) - int16_t temp1, temp2; - CHECK_MINUS_ONE(WebRtcNetEQ_CodecDbGetSizeInfo(inst, &temp1, &temp2)) - CHECK_MINUS_ONE(WebRtcNetEQ_CodecDbGetCodecInfo(inst, 0, &usedCodec)) - - CHECK_MINUS_ONE(WebRtcNetEQ_RecIn(inst, &temp1, 17, 4711)) - CHECK_MINUS_ONE(WebRtcNetEQ_RecOut(inst, &temp1, &temp2)) - CHECK_MINUS_ONE(WebRtcNetEQ_GetRTCPStats(inst, &RTCPstat)); // error here!!! - CHECK_MINUS_ONE(WebRtcNetEQ_GetSpeechTimeStamp(inst, ×tamp)) - WebRtcNetEQOutputType temptype; - CHECK_MINUS_ONE(WebRtcNetEQ_GetSpeechOutputType(inst, &temptype)) - - uint8_t tempFlags; - uint16_t utemp1, utemp2; - CHECK_MINUS_ONE(WebRtcNetEQ_VQmonRecOutStatistics(inst, &utemp1, &utemp2, &tempFlags)) - CHECK_MINUS_ONE(WebRtcNetEQ_VQmonGetRxStatistics(inst, &utemp1, &utemp2)) - - WebRtcNetEQ_AssignSize(&memorySize); - CHECK_ZERO(WebRtcNetEQ_Assign(&inst, malloc(memorySize))) - - /* init with wrong sample frequency */ - CHECK_MINUS_ONE(WebRtcNetEQ_Init(inst, 17)) - - /* init with correct fs */ - CHECK_ZERO(WebRtcNetEQ_Init(inst, 8000)) - - /* GetRecommendedBufferSize with wrong codec */ - usedCodec=kDecoderReservedStart; - ok = WebRtcNetEQ_GetRecommendedBufferSize(inst, &usedCodec, 1, kTCPLargeJitter , &NetEqBufferMaxPackets, &BufferSizeInBytes, &overhead_bytes); - if((ok!=-1) || ((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_UNKNOWN_CODEC))){ - printf("WebRtcNetEQ_GetRecommendedBufferSize() did not return proper error code for wrong codec.\n"); - printf("return value = %d; error code = %d\n", ok, WebRtcNetEQ_GetErrorCode(inst)); - } - //RESET_ERROR(inst) - - /* GetRecommendedBufferSize with wrong network type */ - usedCodec = kDecoderPCMu; - ok=WebRtcNetEQ_GetRecommendedBufferSize(inst, &usedCodec, 1, (enum WebRtcNetEQNetworkType) 4711 , &NetEqBufferMaxPackets, &BufferSizeInBytes, &overhead_bytes); - if ((ok!=-1) || ((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-FAULTY_NETWORK_TYPE))) { - printf("WebRtcNetEQ_GetRecommendedBufferSize() did not return proper error code for wrong network type.\n"); - printf("return value = %d; error code = %d\n", ok, WebRtcNetEQ_GetErrorCode(inst)); - //RESET_ERROR(inst) - } - CHECK_ZERO(WebRtcNetEQ_GetRecommendedBufferSize(inst, &usedCodec, 1, kTCPLargeJitter , &NetEqBufferMaxPackets, &BufferSizeInBytes, &overhead_bytes)) - - /* try to do RecIn before assigning the packet buffer */ -/* makeRTPheader(rtp_data, NETEQ_CODEC_AVT_PT, 17,4711, 1235412312); - makeDTMFpayload(&rtp_data[12], 1, 1, 10, 100); - ok = WebRtcNetEQ_RecIn(inst, (short *) rtp_data, 12+4, 4711); - printf("return value = %d; error code = %d\n", ok, WebRtcNetEQ_GetErrorCode(inst));*/ - - /* check all limits of WebRtcNetEQ_AssignBuffer */ - ok=WebRtcNetEQ_AssignBuffer(inst, NetEqBufferMaxPackets, NetEqPacketBuffer, 149<<1); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-PBUFFER_INIT_ERROR))) { - printf("WebRtcNetEQ_AssignBuffer() did not return proper error code for wrong sizeinbytes\n"); - } - ok=WebRtcNetEQ_AssignBuffer(inst, NetEqBufferMaxPackets, NULL, BufferSizeInBytes); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-PBUFFER_INIT_ERROR))) { - printf("WebRtcNetEQ_AssignBuffer() did not return proper error code for NULL memory pointer\n"); - } - ok=WebRtcNetEQ_AssignBuffer(inst, 1, NetEqPacketBuffer, BufferSizeInBytes); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-PBUFFER_INIT_ERROR))) { - printf("WebRtcNetEQ_AssignBuffer() did not return proper error code for wrong MaxNoOfPackets\n"); - } - ok=WebRtcNetEQ_AssignBuffer(inst, 601, NetEqPacketBuffer, BufferSizeInBytes); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-PBUFFER_INIT_ERROR))) { - printf("WebRtcNetEQ_AssignBuffer() did not return proper error code for wrong MaxNoOfPackets\n"); - } - - /* do correct assignbuffer */ - CHECK_ZERO(WebRtcNetEQ_AssignBuffer(inst, NetEqBufferMaxPackets, NetEqPacketBuffer, BufferSizeInBytes)) - - ok=WebRtcNetEQ_SetExtraDelay(inst, -1); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-FAULTY_DELAYVALUE))) { - printf("WebRtcNetEQ_SetExtraDelay() did not return proper error code for too small delay\n"); - } - ok=WebRtcNetEQ_SetExtraDelay(inst, 1001); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-FAULTY_DELAYVALUE))) { - printf("WebRtcNetEQ_SetExtraDelay() did not return proper error code for too large delay\n"); - } - - ok=WebRtcNetEQ_SetPlayoutMode(inst,(enum WebRtcNetEQPlayoutMode) 4711); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-FAULTY_PLAYOUTMODE))) { - printf("WebRtcNetEQ_SetPlayoutMode() did not return proper error code for wrong mode\n"); - } - - /* number of codecs should return zero before adding any codecs */ - WebRtcNetEQ_CodecDbGetSizeInfo(inst, &temp1, &temp2); - if(temp1!=0) - printf("WebRtcNetEQ_CodecDbGetSizeInfo() return non-zero number of codecs in DB before adding any codecs\n"); - - /* get info from empty database */ - ok=WebRtcNetEQ_CodecDbGetCodecInfo(inst, 17, &usedCodec); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_NOT_EXIST1))) { - printf("WebRtcNetEQ_CodecDbGetCodecInfo() did not return proper error code for out-of-range entry number\n"); - } - - /* remove codec from empty database */ - ok=WebRtcNetEQ_CodecDbRemove(inst,kDecoderPCMa); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_NOT_EXIST4))) { - printf("WebRtcNetEQ_CodecDbRemove() did not return proper error code when removing codec that has not been added\n"); - } - - /* add codec with unsupported fs */ -#ifdef CODEC_PCM16B -#ifndef NETEQ_48KHZ_WIDEBAND - SET_CODEC_PAR(codecInst,kDecoderPCM16Bswb48kHz,77,NULL,48000); - SET_PCM16B_SWB48_FUNCTIONS(codecInst); - ok=WebRtcNetEQ_CodecDbAdd(inst, &codecInst); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_UNSUPPORTED_FS))) { - printf("WebRtcNetEQ_CodecDbAdd() did not return proper error code when adding codec with unsupported sample freq\n"); - } -#else - printf("Could not test adding codec with unsupported sample frequency since NetEQ is compiled with 48kHz support.\n"); -#endif -#else - printf("Could not test adding codec with unsupported sample frequency since NetEQ is compiled without PCM16B support.\n"); -#endif - - /* add two codecs with identical payload types */ - SET_CODEC_PAR(codecInst,kDecoderPCMa,17,NULL,8000); - SET_PCMA_FUNCTIONS(codecInst); - CHECK_ZERO(WebRtcNetEQ_CodecDbAdd(inst, &codecInst)) - - SET_CODEC_PAR(codecInst,kDecoderPCMu,17,NULL,8000); - SET_PCMU_FUNCTIONS(codecInst); - ok=WebRtcNetEQ_CodecDbAdd(inst, &codecInst); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_PAYLOAD_TAKEN))) { - printf("WebRtcNetEQ_CodecDbAdd() did not return proper error code when adding two codecs with identical payload types\n"); - } - - /* try adding several payload types for CNG codecs */ - SET_CODEC_PAR(codecInst,kDecoderCNG,105,NULL,16000); - SET_CNG_FUNCTIONS(codecInst); - CHECK_ZERO(WebRtcNetEQ_CodecDbAdd(inst, &codecInst)) - SET_CODEC_PAR(codecInst,kDecoderCNG,13,NULL,8000); - SET_CNG_FUNCTIONS(codecInst); - CHECK_ZERO(WebRtcNetEQ_CodecDbAdd(inst, &codecInst)) - - /* try adding a speech codec over a CNG codec */ - SET_CODEC_PAR(codecInst,kDecoderISAC,105,NULL,16000); /* same as WB CNG above */ - SET_ISAC_FUNCTIONS(codecInst); - ok=WebRtcNetEQ_CodecDbAdd(inst, &codecInst); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_PAYLOAD_TAKEN))) { - printf("WebRtcNetEQ_CodecDbAdd() did not return proper error code when adding a speech codec over a CNG codec\n"); - } - - /* try adding a CNG codec over a speech codec */ - SET_CODEC_PAR(codecInst,kDecoderCNG,17,NULL,32000); /* same as PCMU above */ - SET_CNG_FUNCTIONS(codecInst); - ok=WebRtcNetEQ_CodecDbAdd(inst, &codecInst); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_PAYLOAD_TAKEN))) { - printf("WebRtcNetEQ_CodecDbAdd() did not return proper error code when adding a speech codec over a CNG codec\n"); - } - - - /* remove codec out of range */ - ok=WebRtcNetEQ_CodecDbRemove(inst,kDecoderReservedStart); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_UNSUPPORTED_CODEC))) { - printf("WebRtcNetEQ_CodecDbRemove() did not return proper error code when removing codec that is out of range\n"); - } - ok=WebRtcNetEQ_CodecDbRemove(inst,kDecoderReservedEnd); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_UNSUPPORTED_CODEC))) { - printf("WebRtcNetEQ_CodecDbRemove() did not return proper error code when removing codec that is out of range\n"); - } - - /*SET_CODEC_PAR(codecInst,kDecoderEG711a,NETEQ_CODEC_EG711A_PT,NetEqiPCMAState,8000); - SET_IPCMA_FUNCTIONS(codecInst); - CHECK_ZERO(WebRtcNetEQ_CodecDbAdd(inst, &codecInst)) -*/ - free(inst); - - return(0); - -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/neteq_speed_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/neteq_speed_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/neteq_speed_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/neteq_speed_test.cc 2015-02-03 14:33:34.000000000 +0000 @@ -13,19 +13,9 @@ #include #include "gflags/gflags.h" -#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_internal.h" -#include "webrtc/modules/audio_coding/neteq4/tools/audio_loop.h" -#include "webrtc/modules/audio_coding/neteq4/tools/rtp_generator.h" -#include "webrtc/test/testsupport/fileutils.h" +#include "webrtc/modules/audio_coding/neteq/tools/neteq_performance_test.h" #include "webrtc/typedefs.h" -using webrtc::test::AudioLoop; -using webrtc::test::RtpGenerator; -using webrtc::WebRtcRTPHeader; - // Flag validators. static bool ValidateRuntime(const char* flagname, int value) { if (value > 0) // Value is ok. @@ -60,15 +50,6 @@ google::RegisterFlagValidator(&FLAGS_drift, &ValidateDriftfactor); int main(int argc, char* argv[]) { - static const int kMaxChannels = 1; - static const int kMaxSamplesPerMs = 48000 / 1000; - static const int kOutputBlockSizeMs = 10; - const std::string kInputFileName = - webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); - const int kSampRateHz = 32000; - const WebRtcNetEQDecoder kDecoderType = kDecoderPCM16Bswb32kHz; - const int kPayloadType = 95; - std::string program_name = argv[0]; std::string usage = "Tool for measuring the speed of NetEq.\n" "Usage: " + program_name + " [options]\n\n" @@ -85,149 +66,15 @@ return 0; } - // Initialize NetEq instance. - int error; - int inst_size_bytes; - error = WebRtcNetEQ_AssignSize(&inst_size_bytes); - if (error) { - std::cerr << "Error returned from WebRtcNetEQ_AssignSize." << std::endl; - exit(1); - } - char* inst_mem = new char[inst_size_bytes]; - void* neteq_inst; - error = WebRtcNetEQ_Assign(&neteq_inst, inst_mem); - if (error) { - std::cerr << "Error returned from WebRtcNetEQ_Assign." << std::endl; - exit(1); - } - // Select decoders. - WebRtcNetEQDecoder decoder_list[] = {kDecoderType}; - int max_number_of_packets; - int buffer_size_bytes; - int overhead_bytes_dummy; - error = WebRtcNetEQ_GetRecommendedBufferSize( - neteq_inst, decoder_list, sizeof(decoder_list) / sizeof(decoder_list[1]), - kTCPLargeJitter, &max_number_of_packets, &buffer_size_bytes, - &overhead_bytes_dummy); - if (error) { - std::cerr << "Error returned from WebRtcNetEQ_GetRecommendedBufferSize." - << std::endl; - exit(1); - } - char* buffer_mem = new char[buffer_size_bytes]; - error = WebRtcNetEQ_AssignBuffer(neteq_inst, max_number_of_packets, - buffer_mem, buffer_size_bytes); - if (error) { - std::cerr << "Error returned from WebRtcNetEQ_AssignBuffer." << std::endl; - exit(1); - } - error = WebRtcNetEQ_Init(neteq_inst, kSampRateHz); - if (error) { - std::cerr << "Error returned from WebRtcNetEQ_Init." << std::endl; - exit(1); - } - - // Register decoder. - WebRtcNetEQ_CodecDef codec_definition; - SET_CODEC_PAR(codec_definition, kDecoderType, kPayloadType, NULL, - kSampRateHz); - SET_PCM16B_SWB32_FUNCTIONS(codec_definition); - error = WebRtcNetEQ_CodecDbAdd(neteq_inst, &codec_definition); - if (error) { - std::cerr << "Cannot register decoder." << std::endl; - exit(1); - } - - // Set up AudioLoop object. - AudioLoop audio_loop; - const size_t kMaxLoopLengthSamples = kSampRateHz * 10; // 10 second loop. - const size_t kInputBlockSizeSamples = 60 * kSampRateHz / 1000; // 60 ms. - if (!audio_loop.Init(kInputFileName, kMaxLoopLengthSamples, - kInputBlockSizeSamples)) { - std::cerr << "Cannot initialize AudioLoop object." << std::endl; - exit(1); - } - - int32_t time_now_ms = 0; - - // Get first input packet. - WebRtcRTPHeader rtp_header; - RtpGenerator rtp_gen(kSampRateHz / 1000); - // Start with positive drift first half of simulation. - double drift_factor = 0.1; - rtp_gen.set_drift_factor(drift_factor); - bool drift_flipped = false; - int32_t packet_input_time_ms = - rtp_gen.GetRtpHeader(kPayloadType, kInputBlockSizeSamples, &rtp_header); - const int16_t* input_samples = audio_loop.GetNextBlock(); - if (!input_samples) exit(1); - uint8_t input_payload[kInputBlockSizeSamples * sizeof(int16_t)]; - int payload_len = WebRtcPcm16b_Encode(const_cast(input_samples), - kInputBlockSizeSamples, - input_payload); - assert(payload_len == kInputBlockSizeSamples * sizeof(int16_t)); - - // Main loop. - while (time_now_ms < FLAGS_runtime_ms) { - while (packet_input_time_ms <= time_now_ms) { - // Drop every N packets, where N = FLAGS_lossrate. - bool lost = false; - if (FLAGS_lossrate > 0) { - lost = ((rtp_header.header.sequenceNumber - 1) % FLAGS_lossrate) == 0; - } - if (!lost) { - WebRtcNetEQ_RTPInfo rtp_info; - rtp_info.payloadType = rtp_header.header.payloadType; - rtp_info.sequenceNumber = rtp_header.header.sequenceNumber; - rtp_info.timeStamp = rtp_header.header.timestamp; - rtp_info.SSRC = rtp_header.header.ssrc; - rtp_info.markerBit = rtp_header.header.markerBit; - // Insert packet. - error = WebRtcNetEQ_RecInRTPStruct( - neteq_inst, &rtp_info, input_payload, payload_len, - packet_input_time_ms * kSampRateHz / 1000); - if (error != 0) { - std::cerr << "WebRtcNetEQ_RecInRTPStruct returned error code " << - WebRtcNetEQ_GetErrorCode(neteq_inst) << std::endl; - exit(1); - } - } - - // Get next packet. - packet_input_time_ms = rtp_gen.GetRtpHeader(kPayloadType, - kInputBlockSizeSamples, - &rtp_header); - input_samples = audio_loop.GetNextBlock(); - if (!input_samples) exit(1); - payload_len = WebRtcPcm16b_Encode(const_cast(input_samples), - kInputBlockSizeSamples, - input_payload); - assert(payload_len == kInputBlockSizeSamples * sizeof(int16_t)); - } - - // Get output audio, but don't do anything with it. - static const int kOutDataLen = kOutputBlockSizeMs * kMaxSamplesPerMs * - kMaxChannels; - int16_t out_data[kOutDataLen]; - int16_t samples_per_channel; - error = WebRtcNetEQ_RecOut(neteq_inst, out_data, &samples_per_channel); - if (error != 0) { - std::cerr << "WebRtcNetEQ_RecOut returned error code " << - WebRtcNetEQ_GetErrorCode(neteq_inst) << std::endl; - exit(1); - } - assert(samples_per_channel == kSampRateHz * 10 / 1000); - - time_now_ms += kOutputBlockSizeMs; - if (time_now_ms >= FLAGS_runtime_ms / 2 && !drift_flipped) { - // Apply negative drift second half of simulation. - rtp_gen.set_drift_factor(-drift_factor); - drift_flipped = true; - } + int64_t result = + webrtc::test::NetEqPerformanceTest::Run(FLAGS_runtime_ms, FLAGS_lossrate, + FLAGS_drift); + if (result <= 0) { + std::cout << "There was an error" << std::endl; + return -1; } std::cout << "Simulation done" << std::endl; - delete [] buffer_mem; - delete [] inst_mem; + std::cout << "Runtime = " << result << " ms" << std::endl; return 0; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_CodecClass.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_CodecClass.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_CodecClass.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_CodecClass.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,704 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "NETEQTEST_CodecClass.h" - -#include // exit - -#include "webrtc_neteq_help_macros.h" - -NETEQTEST_Decoder::NETEQTEST_Decoder(enum WebRtcNetEQDecoder type, uint16_t fs, const char * name, uint8_t pt) -: -_decoder(NULL), -_decoderType(type), -_pt(pt), -_fs(fs), -_name(name) -{ -} - -int NETEQTEST_Decoder::loadToNetEQ(NETEQTEST_NetEQClass & neteq, WebRtcNetEQ_CodecDef & codecInst) -{ - SET_CODEC_PAR(codecInst, _decoderType, _pt, _decoder, _fs); - int err = neteq.loadCodec(codecInst); - - if (err) - { - printf("Error loading codec %s into NetEQ database\n", _name.c_str()); - } - - return(err); -} - - -// iSAC -#ifdef CODEC_ISAC -#include "isac.h" - -decoder_iSAC::decoder_iSAC(uint8_t pt) -: -NETEQTEST_Decoder(kDecoderISAC, 16000, "iSAC", pt) -{ - int16_t err = WebRtcIsac_Create((ISACStruct **) &_decoder); - if (err) - { - exit(EXIT_FAILURE); - } - - WebRtcIsac_EncoderInit((ISACStruct *) _decoder, 0); - WebRtcIsac_SetDecSampRate((ISACStruct *) _decoder, 16000); -} - - -decoder_iSAC::~decoder_iSAC() -{ - if (_decoder) - { - WebRtcIsac_Free((ISACStruct *) _decoder); - _decoder = NULL; - } -} - - -int decoder_iSAC::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_ISAC_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); - -} -#endif - -#ifdef CODEC_ISAC_SWB -decoder_iSACSWB::decoder_iSACSWB(uint8_t pt) -: -NETEQTEST_Decoder(kDecoderISACswb, 32000, "iSAC swb", pt) -{ - int16_t err = WebRtcIsac_Create((ISACStruct **) &_decoder); - if (err) - { - exit(EXIT_FAILURE); - } - - WebRtcIsac_EncoderInit((ISACStruct *) _decoder, 0); - WebRtcIsac_SetDecSampRate((ISACStruct *) _decoder, 32000); -} - -decoder_iSACSWB::~decoder_iSACSWB() -{ - if (_decoder) - { - WebRtcIsac_Free((ISACStruct *) _decoder); - _decoder = NULL; - } -} - -int decoder_iSACSWB::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_ISACSWB_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); - -} -#endif - -#ifdef CODEC_ISAC_FB -decoder_iSACFB::decoder_iSACFB(uint8_t pt) - : NETEQTEST_Decoder(kDecoderISACfb, 32000, "iSAC fb", pt) { - int16_t err = WebRtcIsac_Create((ISACStruct **) &_decoder); - if (err) { - exit(EXIT_FAILURE); - } - - WebRtcIsac_EncoderInit((ISACStruct *) _decoder, 0); - WebRtcIsac_SetDecSampRate((ISACStruct *) _decoder, 32000); -} - -decoder_iSACFB::~decoder_iSACFB() { - if (_decoder) { - WebRtcIsac_Free((ISACStruct *) _decoder); - _decoder = NULL; - } -} - -int decoder_iSACFB::loadToNetEQ(NETEQTEST_NetEQClass & neteq){ - WebRtcNetEQ_CodecDef codecInst; - SET_ISACFB_FUNCTIONS(codecInst); - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -// PCM u/A -#ifdef CODEC_G711 -#include "g711_interface.h" - -decoder_PCMU::decoder_PCMU(uint8_t pt) -: -NETEQTEST_Decoder(kDecoderPCMu, 8000, "G.711-u", pt) -{ - // no state to crate or init -} - -int decoder_PCMU::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_PCMU_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); - -} - -decoder_PCMA::decoder_PCMA(uint8_t pt) -: -NETEQTEST_Decoder(kDecoderPCMa, 8000, "G.711-A", pt) -{ - // no state to crate or init -} - -int decoder_PCMA::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_PCMA_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -// Linear PCM16b -#if (defined(CODEC_PCM16B) || defined(CODEC_PCM16B_WB) || \ - defined(CODEC_PCM16B_32KHZ) || defined(CODEC_PCM16B_48KHZ)) -#include "pcm16b.h" -#endif - -#ifdef CODEC_PCM16B -int decoder_PCM16B_NB::loadToNetEQ(NETEQTEST_NetEQClass &neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_PCM16B_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_PCM16B_WB -int decoder_PCM16B_WB::loadToNetEQ(NETEQTEST_NetEQClass &neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_PCM16B_WB_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_PCM16B_32KHZ -int decoder_PCM16B_SWB32::loadToNetEQ(NETEQTEST_NetEQClass &neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_PCM16B_SWB32_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_PCM16B_48KHZ -int decoder_PCM16B_SWB48::loadToNetEQ(NETEQTEST_NetEQClass &neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_PCM16B_SWB48_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_ILBC -#include "ilbc.h" -decoder_ILBC::decoder_ILBC(uint8_t pt) -: -NETEQTEST_Decoder(kDecoderILBC, 8000, "iLBC", pt) -{ - int16_t err = WebRtcIlbcfix_DecoderCreate((iLBC_decinst_t **) &_decoder); - if (err) - { - exit(EXIT_FAILURE); - } -} - -decoder_ILBC::~decoder_ILBC() -{ - WebRtcIlbcfix_DecoderFree((iLBC_decinst_t *) _decoder); -} - -int decoder_ILBC::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_ILBC_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_G729 -#include "G729Interface.h" -decoder_G729::decoder_G729(uint8_t pt) -: -NETEQTEST_Decoder(kDecoderG729, 8000, "G.729", pt) -{ - int16_t err = WebRtcG729_CreateDec((G729_decinst_t **) &_decoder); - if (err) - { - exit(EXIT_FAILURE); - } -} - -decoder_G729::~decoder_G729() -{ - WebRtcG729_FreeDec((G729_decinst_t *) _decoder); -} - -int decoder_G729::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G729_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_G729_1 -#include "G729_1Interface.h" -decoder_G729_1::decoder_G729_1(uint8_t pt) -: -NETEQTEST_Decoder(kDecoderG729_1, 16000, "G.729.1", pt) -{ - int16_t err = WebRtcG7291_Create((G729_1_inst_t **) &_decoder); - if (err) - { - exit(EXIT_FAILURE); - } -} - -decoder_G729_1::~decoder_G729_1() -{ - WebRtcG7291_Free((G729_1_inst_t *) _decoder); -} - -int decoder_G729_1::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G729_1_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_G722 -#include "g722_interface.h" -decoder_G722::decoder_G722(uint8_t pt) -: -NETEQTEST_Decoder(kDecoderG722, 16000, "G.722", pt) -{ - int16_t err = WebRtcG722_CreateDecoder((G722DecInst **) &_decoder); - if (err) - { - exit(EXIT_FAILURE); - } -} - -decoder_G722::~decoder_G722() -{ - WebRtcG722_FreeDecoder((G722DecInst *) _decoder); -} - -int decoder_G722::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G722_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#if (defined(CODEC_G722_1_16) || defined(CODEC_G722_1_24) || \ - defined(CODEC_G722_1_32) || defined(CODEC_G722_1C_24) || \ - defined(CODEC_G722_1C_32) || defined(CODEC_G722_1C_48)) -#include "G722_1Interface.h" -#endif - -#ifdef CODEC_G722_1_16 -decoder_G722_1_16::decoder_G722_1_16(uint8_t pt) -: -NETEQTEST_Decoder(kDecoderG722_1_16, 16000, "G.722.1 (16 kbps)", pt) -{ - if (WebRtcG7221_CreateDec16((G722_1_16_decinst_t **) &_decoder)) - { - exit(EXIT_FAILURE); - } -} - -decoder_G722_1_16::~decoder_G722_1_16() -{ - WebRtcG7221_FreeDec16((G722_1_16_decinst_t *) _decoder); -} - -int decoder_G722_1_16::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G722_1_16_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_G722_1_24 -decoder_G722_1_24::decoder_G722_1_24(uint8_t pt) -: -NETEQTEST_Decoder(kDecoderG722_1_24, 16000, "G.722.1 (24 kbps)", pt) -{ - if (WebRtcG7221_CreateDec24((G722_1_24_decinst_t **) &_decoder)) - { - exit(EXIT_FAILURE); - } -} - -decoder_G722_1_24::~decoder_G722_1_24() -{ - WebRtcG7221_FreeDec24((G722_1_24_decinst_t *) _decoder); -} - -int decoder_G722_1_24::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G722_1_24_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_G722_1_32 -decoder_G722_1_32::decoder_G722_1_32(uint8_t pt) -: -NETEQTEST_Decoder(kDecoderG722_1_32, 16000, "G.722.1 (32 kbps)", pt) -{ - if (WebRtcG7221_CreateDec32((G722_1_32_decinst_t **) &_decoder)) - { - exit(EXIT_FAILURE); - } -} - -decoder_G722_1_32::~decoder_G722_1_32() -{ - WebRtcG7221_FreeDec32((G722_1_32_decinst_t *) _decoder); -} - -int decoder_G722_1_32::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G722_1_32_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_G722_1C_24 -decoder_G722_1C_24::decoder_G722_1C_24(uint8_t pt) -: -NETEQTEST_Decoder(kDecoderG722_1C_24, 32000, "G.722.1C (24 kbps)", pt) -{ - if (WebRtcG7221C_CreateDec24((G722_1C_24_decinst_t **) &_decoder)) - exit(EXIT_FAILURE); -} - -decoder_G722_1C_24::~decoder_G722_1C_24() -{ - WebRtcG7221C_FreeDec24((G722_1C_24_decinst_t *) _decoder); -} - -int decoder_G722_1C_24::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G722_1C_24_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_G722_1C_32 -decoder_G722_1C_32::decoder_G722_1C_32(uint8_t pt) -: -NETEQTEST_Decoder(kDecoderG722_1C_32, 32000, "G.722.1C (32 kbps)", pt) -{ - if (WebRtcG7221C_CreateDec32((G722_1C_32_decinst_t **) &_decoder)) - exit(EXIT_FAILURE); -} - -decoder_G722_1C_32::~decoder_G722_1C_32() -{ - WebRtcG7221C_FreeDec32((G722_1C_32_decinst_t *) _decoder); -} - -int decoder_G722_1C_32::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G722_1C_32_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_G722_1C_48 -decoder_G722_1C_48::decoder_G722_1C_48(uint8_t pt) -: -NETEQTEST_Decoder(kDecoderG722_1C_48, 32000, "G.722.1C (48 kbps)", pt) -{ - if (WebRtcG7221C_CreateDec48((G722_1C_48_decinst_t **) &_decoder)) - exit(EXIT_FAILURE); -} - -decoder_G722_1C_48::~decoder_G722_1C_48() -{ - WebRtcG7221C_FreeDec48((G722_1C_48_decinst_t *) _decoder); -} - -int decoder_G722_1C_48::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G722_1C_48_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_AMR -#include "AMRInterface.h" -#include "AMRCreation.h" -decoder_AMR::decoder_AMR(uint8_t pt) -: -NETEQTEST_Decoder(kDecoderAMR, 8000, "AMR", pt) -{ - if (WebRtcAmr_CreateDec((AMR_decinst_t **) &_decoder)) - exit(EXIT_FAILURE); - - WebRtcAmr_DecodeBitmode((AMR_decinst_t *) _decoder, AMRBandwidthEfficient); -} - -decoder_AMR::~decoder_AMR() -{ - WebRtcAmr_FreeDec((AMR_decinst_t *) _decoder); -} - -int decoder_AMR::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_AMR_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_AMRWB -#include "AMRWBInterface.h" -#include "AMRWBCreation.h" -decoder_AMRWB::decoder_AMRWB(uint8_t pt) -: -NETEQTEST_Decoder(kDecoderAMRWB, 16000, "AMR wb", pt) -{ - if (WebRtcAmrWb_CreateDec((AMRWB_decinst_t **) &_decoder)) - exit(EXIT_FAILURE); - - WebRtcAmrWb_DecodeBitmode((AMRWB_decinst_t *) _decoder, AMRBandwidthEfficient); -} - -decoder_AMRWB::~decoder_AMRWB() -{ - WebRtcAmrWb_FreeDec((AMRWB_decinst_t *) _decoder); -} - -int decoder_AMRWB::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_AMRWB_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_GSMFR -#include "GSMFRInterface.h" -#include "GSMFRCreation.h" -decoder_GSMFR::decoder_GSMFR(uint8_t pt) -: -NETEQTEST_Decoder(kDecoderGSMFR, 8000, "GSM-FR", pt) -{ - if (WebRtcGSMFR_CreateDec((GSMFR_decinst_t **) &_decoder)) - exit(EXIT_FAILURE); -} - -decoder_GSMFR::~decoder_GSMFR() -{ - WebRtcGSMFR_FreeDec((GSMFR_decinst_t *) _decoder); -} - -int decoder_GSMFR::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_GSMFR_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#if (defined(CODEC_SPEEX_8) || defined (CODEC_SPEEX_16)) -#include "SpeexInterface.h" -decoder_SPEEX::decoder_SPEEX(uint8_t pt, uint16_t fs) -: -NETEQTEST_Decoder(fs == 8000 ? kDecoderSPEEX_8 : kDecoderSPEEX_16, - fs, "SPEEX", pt) -{ - if (fs != 8000 && fs != 16000) - throw std::exception("Wrong sample rate for SPEEX"); - - if (WebRtcSpeex_CreateDec((SPEEX_decinst_t **) &_decoder, fs, 1)) - exit(EXIT_FAILURE); -} - -decoder_SPEEX::~decoder_SPEEX() -{ - WebRtcSpeex_FreeDec((SPEEX_decinst_t *) _decoder); -} - -int decoder_SPEEX::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_SPEEX_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_CELT_32 -#include "celt_interface.h" -decoder_CELT::decoder_CELT(uint8_t pt, uint16_t fs) -: -NETEQTEST_Decoder(kDecoderCELT_32, fs, "CELT", pt) -{ - if (WebRtcCelt_CreateDec((CELT_decinst_t **) &_decoder, 2)) - exit(EXIT_FAILURE); -} - -decoder_CELT::~decoder_CELT() -{ - WebRtcCelt_FreeDec((CELT_decinst_t *) _decoder); -} - -int decoder_CELT::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_CELT_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} - -decoder_CELTslave::decoder_CELTslave(uint8_t pt, uint16_t fs) -: -NETEQTEST_Decoder(kDecoderCELT_32, fs, "CELT", pt) -{ - if (WebRtcCelt_CreateDec((CELT_decinst_t **) &_decoder, 2)) - exit(EXIT_FAILURE); -} - -decoder_CELTslave::~decoder_CELTslave() -{ - WebRtcCelt_FreeDec((CELT_decinst_t *) _decoder); -} - -int decoder_CELTslave::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_CELTSLAVE_FUNCTIONS(codecInst); - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_RED -int decoder_RED::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_RED_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_ATEVENT_DECODE -int decoder_AVT::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_AVT_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#if (defined(CODEC_CNGCODEC8) || defined(CODEC_CNGCODEC16) || \ - defined(CODEC_CNGCODEC32) || defined(CODEC_CNGCODEC48)) -#include "webrtc_cng.h" -decoder_CNG::decoder_CNG(uint8_t pt, uint16_t fs) -: -NETEQTEST_Decoder(kDecoderCNG, fs, "CNG", pt) -{ - if (fs != 8000 && fs != 16000 && fs != 32000 && fs != 48000) - exit(EXIT_FAILURE); - - if (WebRtcCng_CreateDec((CNG_dec_inst **) &_decoder)) - exit(EXIT_FAILURE); -} - -decoder_CNG::~decoder_CNG() -{ - WebRtcCng_FreeDec((CNG_dec_inst *) _decoder); -} - -int decoder_CNG::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_CNG_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_CodecClass.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_CodecClass.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_CodecClass.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_CodecClass.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,316 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef NETEQTEST_CODECCLASS_H -#define NETEQTEST_CODECCLASS_H - -#include -#include - -#include "typedefs.h" -#include "webrtc_neteq.h" -#include "NETEQTEST_NetEQClass.h" - -class NETEQTEST_Decoder -{ -public: - NETEQTEST_Decoder(enum WebRtcNetEQDecoder type, uint16_t fs, const char * name, uint8_t pt = 0); - virtual ~NETEQTEST_Decoder() {}; - - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) = 0; - - int getName(char * name, int maxLen) const { strncpy( name, _name.c_str(), maxLen ); return 0;}; - - void setPT(uint8_t pt) { _pt = pt; }; - uint16_t getFs() const { return (_fs); }; - enum WebRtcNetEQDecoder getType() const { return (_decoderType); }; - uint8_t getPT() const { return (_pt); }; - -protected: - int loadToNetEQ(NETEQTEST_NetEQClass & neteq, WebRtcNetEQ_CodecDef & codecInst); - - void * _decoder; - enum WebRtcNetEQDecoder _decoderType; - uint8_t _pt; - uint16_t _fs; - std::string _name; - -private: -}; - - -class decoder_iSAC : public NETEQTEST_Decoder -{ -public: - decoder_iSAC(uint8_t pt = 0); - virtual ~decoder_iSAC(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - - -class decoder_iSACSWB : public NETEQTEST_Decoder -{ -public: - decoder_iSACSWB(uint8_t pt = 0); - virtual ~decoder_iSACSWB(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - - -class decoder_iSACFB : public NETEQTEST_Decoder { - public: - decoder_iSACFB(uint8_t pt = 0); - virtual ~decoder_iSACFB(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - - -class decoder_PCMU : public NETEQTEST_Decoder -{ -public: - decoder_PCMU(uint8_t pt = 0); - virtual ~decoder_PCMU() {}; - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - - -class decoder_PCMA : public NETEQTEST_Decoder -{ -public: - decoder_PCMA(uint8_t pt = 0); - virtual ~decoder_PCMA() {}; - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_PCM16B_NB : public NETEQTEST_Decoder -{ -public: - decoder_PCM16B_NB(uint8_t pt = 0) : NETEQTEST_Decoder(kDecoderPCM16B, 8000, "PCM16 nb", pt) {}; - virtual ~decoder_PCM16B_NB() {}; - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; -class decoder_PCM16B_WB : public NETEQTEST_Decoder -{ -public: - decoder_PCM16B_WB(uint8_t pt = 0) : NETEQTEST_Decoder(kDecoderPCM16Bwb, 16000, "PCM16 wb", pt) {}; - virtual ~decoder_PCM16B_WB() {}; - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; -class decoder_PCM16B_SWB32 : public NETEQTEST_Decoder -{ -public: - decoder_PCM16B_SWB32(uint8_t pt = 0) : NETEQTEST_Decoder(kDecoderPCM16Bswb32kHz, 32000, "PCM16 swb32", pt) {}; - virtual ~decoder_PCM16B_SWB32() {}; - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_PCM16B_SWB48 : public NETEQTEST_Decoder -{ -public: - decoder_PCM16B_SWB48(uint8_t pt = 0) : NETEQTEST_Decoder(kDecoderPCM16Bswb48kHz, 48000, "PCM16 swb48", pt) {}; - virtual ~decoder_PCM16B_SWB48() {}; - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_ILBC : public NETEQTEST_Decoder -{ -public: - decoder_ILBC(uint8_t pt = 0); - virtual ~decoder_ILBC(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - - -class decoder_G729 : public NETEQTEST_Decoder -{ -public: - decoder_G729(uint8_t pt = 0); - virtual ~decoder_G729(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_G729_1 : public NETEQTEST_Decoder -{ -public: - decoder_G729_1(uint8_t pt = 0); - virtual ~decoder_G729_1(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - - -class decoder_G722 : public NETEQTEST_Decoder -{ -public: - decoder_G722(uint8_t pt = 0); - virtual ~decoder_G722(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - - -class decoder_G722_1_16 : public NETEQTEST_Decoder -{ -public: - decoder_G722_1_16(uint8_t pt = 0); - virtual ~decoder_G722_1_16(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_G722_1_24 : public NETEQTEST_Decoder -{ -public: - decoder_G722_1_24(uint8_t pt = 0); - virtual ~decoder_G722_1_24(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_G722_1_32 : public NETEQTEST_Decoder -{ -public: - decoder_G722_1_32(uint8_t pt = 0); - virtual ~decoder_G722_1_32(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - - -class decoder_G722_1C_24 : public NETEQTEST_Decoder -{ -public: - decoder_G722_1C_24(uint8_t pt = 0); - virtual ~decoder_G722_1C_24(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_G722_1C_32 : public NETEQTEST_Decoder -{ -public: - decoder_G722_1C_32(uint8_t pt = 0); - virtual ~decoder_G722_1C_32(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_G722_1C_48 : public NETEQTEST_Decoder -{ -public: - decoder_G722_1C_48(uint8_t pt = 0); - virtual ~decoder_G722_1C_48(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - - -class decoder_AMR : public NETEQTEST_Decoder -{ -public: - decoder_AMR(uint8_t pt = 0); - virtual ~decoder_AMR(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_AMRWB : public NETEQTEST_Decoder -{ -public: - decoder_AMRWB(uint8_t pt = 0); - virtual ~decoder_AMRWB(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_GSMFR : public NETEQTEST_Decoder -{ -public: - decoder_GSMFR(uint8_t pt = 0); - virtual ~decoder_GSMFR(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_G726 : public NETEQTEST_Decoder -{ -public: - //virtual decoder_G726(uint8_t pt = 0) = 0; - decoder_G726(enum WebRtcNetEQDecoder type, const char * name, uint8_t pt = 0); - virtual ~decoder_G726(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) = 0; -}; - -class decoder_G726_16 : public decoder_G726 -{ -public: - decoder_G726_16(uint8_t pt = 0) : decoder_G726(kDecoderG726_16, "G.726 (16 kbps)", pt) {}; - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_G726_24 : public decoder_G726 -{ -public: - decoder_G726_24(uint8_t pt = 0) : decoder_G726(kDecoderG726_24, "G.726 (24 kbps)", pt) {}; - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_G726_32 : public decoder_G726 -{ -public: - decoder_G726_32(uint8_t pt = 0) : decoder_G726(kDecoderG726_32, "G.726 (32 kbps)", pt) {}; - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_G726_40 : public decoder_G726 -{ -public: - decoder_G726_40(uint8_t pt = 0) : decoder_G726(kDecoderG726_40, "G.726 (40 kbps)", pt) {}; - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_SPEEX : public NETEQTEST_Decoder -{ -public: - decoder_SPEEX(uint8_t pt = 0, uint16_t fs = 8000); - virtual ~decoder_SPEEX(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_CELT : public NETEQTEST_Decoder -{ -public: - decoder_CELT(uint8_t pt = 0, uint16_t fs = 32000); - virtual ~decoder_CELT(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; -class decoder_CELTslave : public NETEQTEST_Decoder -{ -public: - decoder_CELTslave(uint8_t pt = 0, uint16_t fs = 32000); - virtual ~decoder_CELTslave(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_RED : public NETEQTEST_Decoder -{ -public: - decoder_RED(uint8_t pt = 0) : NETEQTEST_Decoder(kDecoderRED, 8000, "RED", pt) {}; - virtual ~decoder_RED() {}; - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -class decoder_AVT : public NETEQTEST_Decoder -{ -public: - decoder_AVT(uint8_t pt = 0) : NETEQTEST_Decoder(kDecoderAVT, 8000, "AVT", pt) {}; - virtual ~decoder_AVT() {}; - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - - -class decoder_CNG : public NETEQTEST_Decoder -{ -public: - decoder_CNG(uint8_t pt = 0, uint16_t fs = 8000); - virtual ~decoder_CNG(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) OVERRIDE; -}; - -#endif //NETEQTEST_CODECCLASS_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_DummyRTPpacket.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_DummyRTPpacket.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_DummyRTPpacket.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_DummyRTPpacket.cc 2015-02-03 14:33:34.000000000 +0000 @@ -14,8 +14,6 @@ #include #include -#include // max - #ifdef WIN32 #include #else @@ -31,123 +29,113 @@ uint16_t length, plen; uint32_t offset; - int packetLen; + int packetLen = 0; bool readNextPacket = true; while (readNextPacket) { - readNextPacket = false; - if (fread(&length, 2, 1, fp) == 0) - { - reset(); - return -2; - } - length = ntohs(length); - - if (fread(&plen, 2, 1, fp) == 0) - { - reset(); - return -1; - } - packetLen = ntohs(plen); - - if (fread(&offset, 4, 1, fp) == 0) - { - reset(); - return -1; - } - // Store in local variable until we have passed the reset below. - uint32_t receiveTime = ntohl(offset); - - // Use length here because a plen of 0 specifies rtcp. - length = (uint16_t) (length - _kRDHeaderLen); - - // check buffer size - if (_datagram && _memSize < length + 1) - { - reset(); - } - - if (!_datagram) - { - // Add one extra byte, to be able to fake a dummy payload of one byte. - _datagram = new uint8_t[length + 1]; - _memSize = length + 1; - } - memset(_datagram, 0, length + 1); - - if (length == 0) - { - _datagramLen = 0; - return packetLen; - } - - // Read basic header - if (fread(_datagram, 1, _kBasicHeaderLen, fp) - != (size_t)_kBasicHeaderLen) - { - reset(); - return -1; - } - _receiveTime = receiveTime; - _datagramLen = _kBasicHeaderLen; - int header_length = _kBasicHeaderLen; - - // Parse the basic header - WebRtcNetEQ_RTPInfo tempRTPinfo; - int P, X, CC; - parseBasicHeader(&tempRTPinfo, &P, &X, &CC); - - // Check if we have to extend the header - if (X != 0 || CC != 0) - { - int newLen = _kBasicHeaderLen + CC * 4 + X * 4; - assert(_memSize >= newLen + 1); - - // Read extension from file - size_t readLen = newLen - _kBasicHeaderLen; - if (fread(_datagram + _kBasicHeaderLen, 1, readLen, - fp) != readLen) - { - reset(); - return -1; - } - _datagramLen = newLen; - header_length = newLen; - - if (X != 0) - { - int totHdrLen = calcHeaderLength(X, CC); - assert(_memSize >= totHdrLen); - - // Read extension from file - size_t readLen = totHdrLen - newLen; - if (fread(_datagram + newLen, 1, readLen, fp) - != readLen) - { + readNextPacket = false; + if (fread(&length, 2, 1, fp) == 0) + { + reset(); + return -2; + } + length = ntohs(length); + + if (fread(&plen, 2, 1, fp) == 0) + { reset(); return -1; - } - _datagramLen = totHdrLen; - header_length = totHdrLen; - } - } - // Make sure that we have at least one byte of dummy payload. - _datagramLen = std::max(static_cast(length), header_length + 1); - assert(_datagramLen <= _memSize); - - if (!_blockList.empty() && _blockList.count(payloadType()) > 0) - { - // discard this payload - readNextPacket = true; - } - - if (_filterSSRC && _selectSSRC != SSRC()) - { - // Discard this payload. - readNextPacket = true; - } + } + packetLen = ntohs(plen); + + if (fread(&offset, 4, 1, fp) == 0) + { + reset(); + return -1; + } + // Store in local variable until we have passed the reset below. + uint32_t receiveTime = ntohl(offset); + + // Use length here because a plen of 0 specifies rtcp. + length = (uint16_t) (length - _kRDHeaderLen); + + // check buffer size + if (_datagram && _memSize < length + 1) + { + reset(); + } + + if (!_datagram) + { + // Add one extra byte, to be able to fake a dummy payload of 1 byte. + _datagram = new uint8_t[length + 1]; + _memSize = length + 1; + } + memset(_datagram, 0, length + 1); + + if (length == 0) + { + _datagramLen = 0; + _rtpParsed = false; + return packetLen; + } + + // Read basic header + if (fread((unsigned short *) _datagram, 1, _kBasicHeaderLen, fp) + != (size_t)_kBasicHeaderLen) + { + reset(); + return -1; + } + _receiveTime = receiveTime; + _datagramLen = _kBasicHeaderLen; + + // Parse the basic header + webrtc::WebRtcRTPHeader tempRTPinfo; + int P, X, CC; + parseBasicHeader(&tempRTPinfo, &P, &X, &CC); + + // Check if we have to extend the header + if (X != 0 || CC != 0) + { + int newLen = _kBasicHeaderLen + CC * 4 + X * 4; + assert(_memSize >= newLen); + + // Read extension from file + size_t readLen = newLen - _kBasicHeaderLen; + if (fread(&_datagram[_kBasicHeaderLen], 1, readLen, fp) != readLen) + { + reset(); + return -1; + } + _datagramLen = newLen; + + if (X != 0) + { + int totHdrLen = calcHeaderLength(X, CC); + assert(_memSize >= totHdrLen); + + // Read extension from file + size_t readLen = totHdrLen - newLen; + if (fread(&_datagram[newLen], 1, readLen, fp) != readLen) + { + reset(); + return -1; + } + _datagramLen = totHdrLen; + } + } + _datagramLen = length; + + if (!_blockList.empty() && _blockList.count(payloadType()) > 0) + { + readNextPacket = true; + } } + _rtpParsed = false; + assert(_memSize > _datagramLen); + _payloadLen = 1; // Set the length to 1 byte. return packetLen; } @@ -208,3 +196,9 @@ } +void NETEQTEST_DummyRTPpacket::parseHeader() { + NETEQTEST_RTPpacket::parseHeader(); + // Change _payloadLen to 1 byte. The memory should always be big enough. + assert(_memSize > _datagramLen); + _payloadLen = 1; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_DummyRTPpacket.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_DummyRTPpacket.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_DummyRTPpacket.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_DummyRTPpacket.h 2015-02-03 14:33:34.000000000 +0000 @@ -17,6 +17,7 @@ public: virtual int readFromFile(FILE* fp) OVERRIDE; virtual int writeToFile(FILE* fp) OVERRIDE; + virtual void parseHeader() OVERRIDE; }; #endif // NETEQTEST_DUMMYRTPPACKET_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_NetEQClass.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_NetEQClass.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_NetEQClass.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_NetEQClass.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,395 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "NETEQTEST_NetEQClass.h" - - -NETEQTEST_NetEQClass::NETEQTEST_NetEQClass() - : - _inst(NULL), - _instMem(NULL), - _bufferMem(NULL), - _preparseRTP(false), - _fsmult(1), - _isMaster(true), - _noDecode(false) -{ -#ifdef WINDOWS_TIMING - _totTimeRecIn.QuadPart = 0; - _totTimeRecOut.QuadPart = 0; -#endif -} - -NETEQTEST_NetEQClass::NETEQTEST_NetEQClass(enum WebRtcNetEQDecoder *usedCodec, int noOfCodecs, - uint16_t fs, WebRtcNetEQNetworkType nwType) - : - _inst(NULL), - _instMem(NULL), - _bufferMem(NULL), - _preparseRTP(false), - _fsmult(1), - _isMaster(true), - _noDecode(false) -{ -#ifdef WINDOWS_TIMING - _totTimeRecIn.QuadPart = 0; - _totTimeRecOut.QuadPart = 0; -#endif - - if (assign() == 0) - { - if (init(fs) == 0) - { - assignBuffer(usedCodec, noOfCodecs, nwType); - } - } -} - - -NETEQTEST_NetEQClass::~NETEQTEST_NetEQClass() -{ - if (_instMem) - { - delete [] _instMem; - _instMem = NULL; - } - - if (_bufferMem) - { - delete [] _bufferMem; - _bufferMem = NULL; - } - - _inst = NULL; -} - -int NETEQTEST_NetEQClass::assign() -{ - int memSize; - - WebRtcNetEQ_AssignSize(&memSize); - - if (_instMem) - { - delete [] _instMem; - _instMem = NULL; - } - - _instMem = new int8_t[memSize]; - - int ret = WebRtcNetEQ_Assign(&_inst, _instMem); - - if (ret) - { - printError(); - } - - return (ret); -} - - -int NETEQTEST_NetEQClass::init(uint16_t fs) -{ - int ret; - - if (!_inst) - { - // not assigned - ret = assign(); - - if (ret != 0) - { - printError(); - return (ret); - } - } - - ret = WebRtcNetEQ_Init(_inst, fs); - - if (ret != 0) - { - printError(); - } - - return (ret); - -} - - -int NETEQTEST_NetEQClass::assignBuffer(enum WebRtcNetEQDecoder *usedCodec, int noOfCodecs, WebRtcNetEQNetworkType nwType) -{ - int numPackets, memSize, ret, overhead_bytes; - - if (!_inst) - { - // not assigned - ret = assign(); - - if (ret != 0) - { - printError(); - return (ret); - } - - ret = init(); - - if (ret != 0) - { - printError(); - return (ret); - } - } - - ret = WebRtcNetEQ_GetRecommendedBufferSize(_inst, usedCodec, noOfCodecs, - nwType, &numPackets, &memSize, - &overhead_bytes); - - if (ret != 0) - { - printError(); - return (ret); - } - - if (_bufferMem) - { - delete [] _bufferMem; - _bufferMem = NULL; - } - - _bufferMem = new int8_t[memSize]; - - memset(_bufferMem, -1, memSize); - - ret = WebRtcNetEQ_AssignBuffer(_inst, numPackets, _bufferMem, memSize); - - if (ret != 0) - { - printError(); - } - - return (ret); -} - -int NETEQTEST_NetEQClass::loadCodec(WebRtcNetEQ_CodecDef &codecInst) -{ - int err = WebRtcNetEQ_CodecDbAdd(_inst, &codecInst); - - if (err) - { - printError(); - } - - return (err); -} - -void NETEQTEST_NetEQClass::printError() -{ - if (_inst) - { - int errorCode = WebRtcNetEQ_GetErrorCode(_inst); - - if (errorCode) - { - char errorName[WEBRTC_NETEQ_MAX_ERROR_NAME]; - - WebRtcNetEQ_GetErrorName(errorCode, errorName, WEBRTC_NETEQ_MAX_ERROR_NAME); - - printf("Error %i: %s\n", errorCode, errorName); - } - } -} - -void NETEQTEST_NetEQClass::printError(NETEQTEST_RTPpacket &rtp) -{ - // print regular error info - printError(); - - // print extra info from packet - printf("\tRTP: TS=%u, SN=%u, PT=%u, M=%i, len=%i\n", - rtp.timeStamp(), rtp.sequenceNumber(), rtp.payloadType(), - rtp.markerBit(), rtp.payloadLen()); - -} - -int NETEQTEST_NetEQClass::recIn(NETEQTEST_RTPpacket &rtp) -{ - - int err; -#ifdef WINDOWS_TIMING - LARGE_INTEGER countA, countB; -#endif - - if (_preparseRTP) - { - WebRtcNetEQ_RTPInfo rtpInfo; - // parse RTP header - rtp.parseHeader(rtpInfo); - -#ifdef WINDOWS_TIMING - QueryPerformanceCounter(&countA); // get start count for processor -#endif - - err = WebRtcNetEQ_RecInRTPStruct(_inst, &rtpInfo, rtp.payload(), rtp.payloadLen(), rtp.time() * _fsmult * 8); - -#ifdef WINDOWS_TIMING - QueryPerformanceCounter(&countB); // get stop count for processor - _totTimeRecIn.QuadPart += (countB.QuadPart - countA.QuadPart); -#endif - - } - else - { - -#ifdef WINDOWS_TIMING - QueryPerformanceCounter(&countA); // get start count for processor -#endif - - err = WebRtcNetEQ_RecIn(_inst, (int16_t *) rtp.datagram(), rtp.dataLen(), rtp.time() * _fsmult * 8); - -#ifdef WINDOWS_TIMING - QueryPerformanceCounter(&countB); // get stop count for processor - _totTimeRecIn.QuadPart += (countB.QuadPart - countA.QuadPart); -#endif - - } - - if (err) - { - printError(rtp); - } - - return (err); - -} - - -int16_t NETEQTEST_NetEQClass::recOut(int16_t *outData, void *msInfo, enum WebRtcNetEQOutputType *outputType) -{ - int err; - int16_t outLen = 0; -#ifdef WINDOWS_TIMING - LARGE_INTEGER countA, countB; -#endif - -#ifdef WINDOWS_TIMING - QueryPerformanceCounter(&countA); // get start count for processor -#endif - - if (!msInfo) - { - // no msInfo given, do mono mode - if (_noDecode) - { - err = WebRtcNetEQ_RecOutNoDecode(_inst, outData, &outLen); - } - else - { - err = WebRtcNetEQ_RecOut(_inst, outData, &outLen); - } - } - else - { - // master/slave mode - err = WebRtcNetEQ_RecOutMasterSlave(_inst, outData, &outLen, msInfo, static_cast(_isMaster)); - } - -#ifdef WINDOWS_TIMING - QueryPerformanceCounter(&countB); // get stop count for processor - _totTimeRecOut.QuadPart += (countB.QuadPart - countA.QuadPart); -#endif - - if (err) - { - printError(); - } - else - { - int newfsmult = static_cast(outLen / 80); - - if (newfsmult != _fsmult) - { -#ifdef NETEQTEST_PRINT_WARNINGS - printf("Warning: output sample rate changed\n"); -#endif // NETEQTEST_PRINT_WARNINGS - _fsmult = newfsmult; - } - } - - if (outputType != NULL) - { - err = WebRtcNetEQ_GetSpeechOutputType(_inst, outputType); - - if (err) - { - printError(); - } - } - - return (outLen); -} - - -uint32_t NETEQTEST_NetEQClass::getSpeechTimeStamp() -{ - - uint32_t ts = 0; - int err; - - err = WebRtcNetEQ_GetSpeechTimeStamp(_inst, &ts); - - if (err) - { - printError(); - ts = 0; - } - - return (ts); - -} - -WebRtcNetEQOutputType NETEQTEST_NetEQClass::getOutputType() { - WebRtcNetEQOutputType type; - - int err = WebRtcNetEQ_GetSpeechOutputType(_inst, &type); - if (err) - { - printError(); - type = kOutputNormal; - } - return (type); -} - -//NETEQTEST_NetEQVector::NETEQTEST_NetEQVector(int numChannels) -//: -//channels(numChannels, new NETEQTEST_NetEQClass()) -//{ -// //for (int i = 0; i < numChannels; i++) -// //{ -// // channels.push_back(new NETEQTEST_NetEQClass()); -// //} -//} -// -//NETEQTEST_NetEQVector::NETEQTEST_NetEQVector(int numChannels, enum WebRtcNetEQDecoder *usedCodec, int noOfCodecs, -// uint16_t fs, WebRtcNetEQNetworkType nwType) -// : -//channels(numChannels, new NETEQTEST_NetEQClass(usedCodec, noOfCodecs, fs, nwType)) -//{ -// //for (int i = 0; i < numChannels; i++) -// //{ -// // channels.push_back(new NETEQTEST_NetEQClass(usedCodec, noOfCodecs, fs, nwType)); -// //} -//} -// -//NETEQTEST_NetEQVector::~NETEQTEST_NetEQVector() -//{ -//} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_NetEQClass.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_NetEQClass.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_NetEQClass.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_NetEQClass.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef NETEQTEST_NETEQCLASS_H -#define NETEQTEST_NETEQCLASS_H - -#include -#include - -#include "webrtc_neteq.h" -#include "webrtc_neteq_internal.h" - -#include "NETEQTEST_RTPpacket.h" - -#ifdef WIN32 -#define WINDOWS_TIMING // complexity measurement only implemented for windows -//TODO(hlundin):Add complexity testing for Linux. -#include -#endif - -class NETEQTEST_NetEQClass -{ -public: - NETEQTEST_NetEQClass(); - NETEQTEST_NetEQClass(enum WebRtcNetEQDecoder *usedCodec, int noOfCodecs, - uint16_t fs = 8000, WebRtcNetEQNetworkType nwType = kTCPLargeJitter); - ~NETEQTEST_NetEQClass(); - - int assign(); - int init(uint16_t fs = 8000); - int assignBuffer(enum WebRtcNetEQDecoder *usedCodec, int noOfCodecs, WebRtcNetEQNetworkType nwType = kTCPLargeJitter); - int loadCodec(WebRtcNetEQ_CodecDef & codecInst); - int recIn(NETEQTEST_RTPpacket & rtp); - int16_t recOut(int16_t *outData, void *msInfo = NULL, enum WebRtcNetEQOutputType *outputType = NULL); - uint32_t getSpeechTimeStamp(); - WebRtcNetEQOutputType getOutputType(); - - void * instance() { return (_inst); }; - void usePreparseRTP( bool useIt = true ) { _preparseRTP = useIt; }; - bool usingPreparseRTP() { return (_preparseRTP); }; - void setMaster( bool isMaster = true ) { _isMaster = isMaster; }; - void setSlave() { _isMaster = false; }; - void setNoDecode(bool noDecode = true) { _noDecode = noDecode; }; - bool isMaster() { return (_isMaster); }; - bool isSlave() { return (!_isMaster); }; - bool isNoDecode() { return _noDecode; }; - -#ifdef WINDOWS_TIMING - double getRecInTime() { return (static_cast( _totTimeRecIn.QuadPart )); }; - double getRecOutTime() { return (static_cast( _totTimeRecOut.QuadPart )); }; -#else - double getRecInTime() { return (0.0); }; - double getRecOutTime() { return (0.0); }; - -#endif - - void printError(); - void printError(NETEQTEST_RTPpacket & rtp); - -private: - void * _inst; - int8_t * _instMem; - int8_t * _bufferMem; - bool _preparseRTP; - int _fsmult; - bool _isMaster; - bool _noDecode; -#ifdef WINDOWS_TIMING - LARGE_INTEGER _totTimeRecIn; - LARGE_INTEGER _totTimeRecOut; -#endif -}; - -#endif //NETEQTEST_NETEQCLASS_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.cc 2015-02-03 14:33:34.000000000 +0000 @@ -32,9 +32,7 @@ _payloadLen(0), _rtpParsed(false), _receiveTime(0), -_lost(false), -_selectSSRC(0), -_filterSSRC(false) +_lost(false) { memset(&_rtpInfo, 0, sizeof(_rtpInfo)); _blockList.clear(); @@ -107,7 +105,7 @@ uint16_t length, plen; uint32_t offset; - int packetLen; + int packetLen = 0; bool readNextPacket = true; while (readNextPacket) { @@ -162,13 +160,9 @@ { readNextPacket = true; } - - if (_filterSSRC && _selectSSRC != SSRC()) - { - readNextPacket = true; - } } + _rtpParsed = false; return(packetLen); } @@ -208,6 +202,7 @@ return readFromFile(fp); } + _rtpParsed = false; return length; } @@ -262,11 +257,6 @@ _blockList[pt] = true; } -void NETEQTEST_RTPpacket::selectSSRC(uint32_t ssrc) -{ - _selectSSRC = ssrc; - _filterSSRC = true; -} void NETEQTEST_RTPpacket::parseHeader() { @@ -290,18 +280,20 @@ } -void NETEQTEST_RTPpacket::parseHeader(WebRtcNetEQ_RTPInfo & rtpInfo) -{ - if (!_rtpParsed) - { - // parse the header - parseHeader(); - } - - memcpy(&rtpInfo, &_rtpInfo, sizeof(WebRtcNetEQ_RTPInfo)); +void NETEQTEST_RTPpacket::parseHeader(webrtc::WebRtcRTPHeader* rtp_header) { + if (!_rtpParsed) { + parseHeader(); + } + if (rtp_header) { + rtp_header->header.markerBit = _rtpInfo.header.markerBit; + rtp_header->header.payloadType = _rtpInfo.header.payloadType; + rtp_header->header.sequenceNumber = _rtpInfo.header.sequenceNumber; + rtp_header->header.timestamp = _rtpInfo.header.timestamp; + rtp_header->header.ssrc = _rtpInfo.header.ssrc; + } } -WebRtcNetEQ_RTPInfo const * NETEQTEST_RTPpacket::RTPinfo() const +const webrtc::WebRtcRTPHeader* NETEQTEST_RTPpacket::RTPinfo() const { if (_rtpParsed) { @@ -360,7 +352,7 @@ uint8_t NETEQTEST_RTPpacket::payloadType() const { - WebRtcNetEQ_RTPInfo tempRTPinfo; + webrtc::WebRtcRTPHeader tempRTPinfo; if(_datagram && _datagramLen >= _kBasicHeaderLen) { @@ -371,12 +363,12 @@ return 0; } - return tempRTPinfo.payloadType; + return tempRTPinfo.header.payloadType; } uint16_t NETEQTEST_RTPpacket::sequenceNumber() const { - WebRtcNetEQ_RTPInfo tempRTPinfo; + webrtc::WebRtcRTPHeader tempRTPinfo; if(_datagram && _datagramLen >= _kBasicHeaderLen) { @@ -387,12 +379,12 @@ return 0; } - return tempRTPinfo.sequenceNumber; + return tempRTPinfo.header.sequenceNumber; } uint32_t NETEQTEST_RTPpacket::timeStamp() const { - WebRtcNetEQ_RTPInfo tempRTPinfo; + webrtc::WebRtcRTPHeader tempRTPinfo; if(_datagram && _datagramLen >= _kBasicHeaderLen) { @@ -403,12 +395,12 @@ return 0; } - return tempRTPinfo.timeStamp; + return tempRTPinfo.header.timestamp; } uint32_t NETEQTEST_RTPpacket::SSRC() const { - WebRtcNetEQ_RTPInfo tempRTPinfo; + webrtc::WebRtcRTPHeader tempRTPinfo; if(_datagram && _datagramLen >= _kBasicHeaderLen) { @@ -419,12 +411,12 @@ return 0; } - return tempRTPinfo.SSRC; + return tempRTPinfo.header.ssrc; } uint8_t NETEQTEST_RTPpacket::markerBit() const { - WebRtcNetEQ_RTPInfo tempRTPinfo; + webrtc::WebRtcRTPHeader tempRTPinfo; if(_datagram && _datagramLen >= _kBasicHeaderLen) { @@ -435,7 +427,7 @@ return 0; } - return tempRTPinfo.markerBit; + return tempRTPinfo.header.markerBit; } @@ -450,7 +442,7 @@ if (!_rtpParsed) { - _rtpInfo.payloadType = pt; + _rtpInfo.header.payloadType = pt; } _datagram[1]=(unsigned char)(pt & 0xFF); @@ -469,7 +461,7 @@ if (!_rtpParsed) { - _rtpInfo.sequenceNumber = sn; + _rtpInfo.header.sequenceNumber = sn; } _datagram[2]=(unsigned char)((sn>>8)&0xFF); @@ -489,7 +481,7 @@ if (!_rtpParsed) { - _rtpInfo.timeStamp = ts; + _rtpInfo.header.timestamp = ts; } _datagram[4]=(unsigned char)((ts>>24)&0xFF); @@ -511,7 +503,7 @@ if (!_rtpParsed) { - _rtpInfo.SSRC = ssrc; + _rtpInfo.header.ssrc = ssrc; } _datagram[8]=(unsigned char)((ssrc>>24)&0xFF); @@ -533,7 +525,7 @@ if (_rtpParsed) { - _rtpInfo.markerBit = mb; + _rtpInfo.header.markerBit = mb; } if (mb) @@ -549,7 +541,7 @@ } -int NETEQTEST_RTPpacket::setRTPheader(const WebRtcNetEQ_RTPInfo *RTPinfo) +int NETEQTEST_RTPpacket::setRTPheader(const webrtc::WebRtcRTPHeader* RTPinfo) { if (_datagramLen < 12) { @@ -558,11 +550,11 @@ } makeRTPheader(_datagram, - RTPinfo->payloadType, - RTPinfo->sequenceNumber, - RTPinfo->timeStamp, - RTPinfo->SSRC, - RTPinfo->markerBit); + RTPinfo->header.payloadType, + RTPinfo->header.sequenceNumber, + RTPinfo->header.timestamp, + RTPinfo->header.ssrc, + RTPinfo->header.markerBit); return 0; } @@ -660,7 +652,7 @@ } uint16_t - NETEQTEST_RTPpacket::parseRTPheader(WebRtcNetEQ_RTPInfo *RTPinfo, + NETEQTEST_RTPpacket::parseRTPheader(webrtc::WebRtcRTPHeader* RTPinfo, uint8_t **payloadPtr) const { int16_t *rtp_data = (int16_t *) _datagram; @@ -682,7 +674,7 @@ } -void NETEQTEST_RTPpacket::parseBasicHeader(WebRtcNetEQ_RTPInfo *RTPinfo, +void NETEQTEST_RTPpacket::parseBasicHeader(webrtc::WebRtcRTPHeader* RTPinfo, int *i_P, int *i_X, int *i_CC) const { int16_t *rtp_data = (int16_t *) _datagram; @@ -696,19 +688,20 @@ *i_X=(((uint16_t)(rtp_data[0] & 0x10))>>4); /* Extract the X bit */ *i_CC=(uint16_t)(rtp_data[0] & 0xF); /* Get the CC number */ /* Get the marker bit */ - RTPinfo->markerBit = (uint8_t) ((rtp_data[0] >> 15) & 0x01); + RTPinfo->header.markerBit = (uint8_t) ((rtp_data[0] >> 15) & 0x01); /* Get the coder type */ - RTPinfo->payloadType = (uint8_t) ((rtp_data[0] >> 8) & 0x7F); + RTPinfo->header.payloadType = (uint8_t) ((rtp_data[0] >> 8) & 0x7F); /* Get the packet number */ - RTPinfo->sequenceNumber = ((( ((uint16_t)rtp_data[1]) >> 8) & 0xFF) | + RTPinfo->header.sequenceNumber = + ((( ((uint16_t)rtp_data[1]) >> 8) & 0xFF) | ( ((uint16_t)(rtp_data[1] & 0xFF)) << 8)); /* Get timestamp */ - RTPinfo->timeStamp = ((((uint16_t)rtp_data[2]) & 0xFF) << 24) | + RTPinfo->header.timestamp = ((((uint16_t)rtp_data[2]) & 0xFF) << 24) | ((((uint16_t)rtp_data[2]) & 0xFF00) << 8) | ((((uint16_t)rtp_data[3]) >> 8) & 0xFF) | ((((uint16_t)rtp_data[3]) & 0xFF) << 8); /* Get the SSRC */ - RTPinfo->SSRC=((((uint16_t)rtp_data[4]) & 0xFF) << 24) | + RTPinfo->header.ssrc = ((((uint16_t)rtp_data[4]) & 0xFF) << 24) | ((((uint16_t)rtp_data[4]) & 0xFF00) << 8) | ((((uint16_t)rtp_data[5]) >> 8) & 0xFF) | ((((uint16_t)rtp_data[5]) & 0xFF) << 8); @@ -817,7 +810,7 @@ // Get the RTP header for the RED payload indicated by argument index. // The first RED payload is index = 0. -int NETEQTEST_RTPpacket::extractRED(int index, WebRtcNetEQ_RTPInfo& red) +int NETEQTEST_RTPpacket::extractRED(int index, webrtc::WebRtcRTPHeader& red) { // // 0 1 2 3 @@ -844,12 +837,12 @@ if (num_encodings == index) { // Header found. - red.payloadType = ptr[0] & 0x7F; + red.header.payloadType = ptr[0] & 0x7F; uint32_t offset = (ptr[1] << 6) + ((ptr[2] & 0xFC) >> 2); - red.sequenceNumber = sequenceNumber(); - red.timeStamp = timeStamp() - offset; - red.markerBit = markerBit(); - red.SSRC = SSRC(); + red.header.sequenceNumber = sequenceNumber(); + red.header.timestamp = timeStamp() - offset; + red.header.markerBit = markerBit(); + red.header.ssrc = SSRC(); return len; } ++num_encodings; @@ -859,11 +852,11 @@ if ((ptr < payloadEndPtr) && (num_encodings == index)) { // Last header. - red.payloadType = ptr[0] & 0x7F; - red.sequenceNumber = sequenceNumber(); - red.timeStamp = timeStamp(); - red.markerBit = markerBit(); - red.SSRC = SSRC(); + red.header.payloadType = ptr[0] & 0x7F; + red.header.sequenceNumber = sequenceNumber(); + red.header.timestamp = timeStamp(); + red.header.markerBit = markerBit(); + red.header.ssrc = SSRC(); ++ptr; return payloadLen() - (ptr - payload()) - total_len; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.h 2015-02-03 14:33:34.000000000 +0000 @@ -13,8 +13,8 @@ #include #include -#include "typedefs.h" -#include "webrtc_neteq_internal.h" +#include "webrtc/typedefs.h" +#include "webrtc/modules/interface/module_common_types.h" enum stereoModes { stereoModeMono, @@ -36,11 +36,10 @@ int readFixedFromFile(FILE *fp, size_t len); virtual int writeToFile(FILE *fp); void blockPT(uint8_t pt); - void selectSSRC(uint32_t ssrc); //int16_t payloadType(); - void parseHeader(); - void parseHeader(WebRtcNetEQ_RTPInfo & rtpInfo); - WebRtcNetEQ_RTPInfo const * RTPinfo() const; + virtual void parseHeader(); + void parseHeader(webrtc::WebRtcRTPHeader* rtp_header); + const webrtc::WebRtcRTPHeader* RTPinfo() const; uint8_t * datagram() const; uint8_t * payload() const; int16_t payloadLen(); @@ -62,11 +61,11 @@ int setMarkerBit(uint8_t mb); void setTime(uint32_t receiveTime) { _receiveTime = receiveTime; }; - int setRTPheader(const WebRtcNetEQ_RTPInfo *RTPinfo); + int setRTPheader(const webrtc::WebRtcRTPHeader* RTPinfo); int splitStereo(NETEQTEST_RTPpacket* slaveRtp, enum stereoModes mode); - int extractRED(int index, WebRtcNetEQ_RTPInfo& red); + int extractRED(int index, webrtc::WebRtcRTPHeader& red); void scramblePayload(void); @@ -75,19 +74,17 @@ int _memSize; int16_t _datagramLen; int16_t _payloadLen; - WebRtcNetEQ_RTPInfo _rtpInfo; + webrtc::WebRtcRTPHeader _rtpInfo; bool _rtpParsed; uint32_t _receiveTime; bool _lost; std::map _blockList; - uint32_t _selectSSRC; - bool _filterSSRC; protected: static const int _kRDHeaderLen; static const int _kBasicHeaderLen; - void parseBasicHeader(WebRtcNetEQ_RTPInfo *RTPinfo, int *i_P, int *i_X, + void parseBasicHeader(webrtc::WebRtcRTPHeader* RTPinfo, int *i_P, int *i_X, int *i_CC) const; int calcHeaderLength(int i_X, int i_CC) const; @@ -95,7 +92,7 @@ void makeRTPheader(unsigned char* rtp_data, uint8_t payloadType, uint16_t seqNo, uint32_t timestamp, uint32_t ssrc, uint8_t markerBit) const; - uint16_t parseRTPheader(WebRtcNetEQ_RTPInfo *RTPinfo, + uint16_t parseRTPheader(webrtc::WebRtcRTPHeader* RTPinfo, uint8_t **payloadPtr = NULL) const; uint16_t parseRTPheader(uint8_t **payloadPtr = NULL) { return parseRTPheader(&_rtpInfo, payloadPtr);}; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/PayloadTypes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/PayloadTypes.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/PayloadTypes.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/PayloadTypes.h 2015-02-03 14:33:34.000000000 +0000 @@ -35,7 +35,6 @@ #define NETEQ_CODEC_ISAC_PT 103 #define NETEQ_CODEC_ISACLC_PT 119 #define NETEQ_CODEC_ISACSWB_PT 104 -#define NETEQ_CODEC_ISACFB_PT 124 #define NETEQ_CODEC_AVT_PT 106 #define NETEQ_CODEC_G722_1_16_PT 108 #define NETEQ_CODEC_G722_1_24_PT 109 @@ -54,7 +53,7 @@ #define NETEQ_CODEC_CN_SWB_PT 126 #define NETEQ_CODEC_G729_1_PT 107 #define NETEQ_CODEC_G729D_PT 123 -//#define NETEQ_CODEC_MELPE_PT 124 +#define NETEQ_CODEC_MELPE_PT 124 #define NETEQ_CODEC_CELT32_PT 114 /* Extra dynamic codepoints */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/ptypes.txt thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/ptypes.txt --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/ptypes.txt 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/ptypes.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -pcmu 0 -pcma 8 -cn 13 -ilbc 102 -isac 103 -isacswb 104 -isacfb 124 -avt 106 -red 117 -cn_wb 98 -cn_swb32 99 -pcm16b 93 -pcm16b_wb 94 -pcm16b_swb32khz 95 -g722 9 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPanalyze.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPanalyze.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPanalyze.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPanalyze.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include - -#include "modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.h" -#include "modules/audio_coding/neteq/test/NETEQTEST_DummyRTPpacket.h" - -//#define WEBRTC_DUMMY_RTP - -enum { - kRedPayloadType = 127 -}; - -int main(int argc, char* argv[]) { - FILE* in_file = fopen(argv[1], "rb"); - if (!in_file) { - printf("Cannot open input file %s\n", argv[1]); - return -1; - } - printf("Input file: %s\n", argv[1]); - - FILE* out_file = fopen(argv[2], "wt"); - if (!out_file) { - printf("Cannot open output file %s\n", argv[2]); - return -1; - } - printf("Output file: %s\n\n", argv[2]); - - // Print file header. - fprintf(out_file, "SeqNo TimeStamp SendTime Size PT M SSRC\n"); - - // Read file header. - NETEQTEST_RTPpacket::skipFileHeader(in_file); -#ifdef WEBRTC_DUMMY_RTP - NETEQTEST_DummyRTPpacket packet; -#else - NETEQTEST_RTPpacket packet; -#endif - - while (packet.readFromFile(in_file) >= 0) { - // Write packet data to file. - fprintf(out_file, "%5u %10u %10u %5i %5i %2i %#08X\n", - packet.sequenceNumber(), packet.timeStamp(), packet.time(), - packet.dataLen(), packet.payloadType(), packet.markerBit(), - packet.SSRC()); - if (packet.payloadType() == kRedPayloadType) { - WebRtcNetEQ_RTPInfo red_header; - int len; - int red_index = 0; - while ((len = packet.extractRED(red_index++, red_header)) >= 0) { - fprintf(out_file, "* %5u %10u %10u %5i %5i\n", - red_header.sequenceNumber, red_header.timeStamp, - packet.time(), len, red_header.payloadType); - } - assert(red_index > 1); // We must get at least one payload. - } - } - - fclose(in_file); - fclose(out_file); - - return 0; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPcat.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPcat.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPcat.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPcat.cc 2015-02-03 14:33:34.000000000 +0000 @@ -13,8 +13,8 @@ #include #include -#include "gtest/gtest.h" -#include "modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.h" #define FIRSTLINELEN 40 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPchange.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPchange.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPchange.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPchange.cc 2015-02-03 14:33:34.000000000 +0000 @@ -13,8 +13,8 @@ #include #include -#include "modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.h" -#include "modules/audio_coding/neteq/test/NETEQTEST_DummyRTPpacket.h" +#include "webrtc/modules/audio_coding/neteq/test/NETEQTEST_DummyRTPpacket.h" +#include "webrtc/modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.h" #define FIRSTLINELEN 40 //#define WEBRTC_DUMMY_RTP @@ -59,7 +59,7 @@ uint32_t send_time; while (fscanf(stat_file, - "%hu %u %u %*i %*i %*i %*x\n", &seq_no, &ts, &send_time) == 3) { + "%hu %u %u %*i %*i\n", &seq_no, &ts, &send_time) == 3) { std::pair temp_pair = std::pair(seq_no, ts); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPencode.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPencode.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPencode.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPencode.cc 2015-02-03 14:33:34.000000000 +0000 @@ -11,13 +11,9 @@ //TODO(hlundin): Reformat file to meet style guide. /* header includes */ -#include "stdio.h" -#include "typedefs.h" -#include "webrtc_neteq.h" // needed for enum WebRtcNetEQDecoder -#include +#include #include #include - #ifdef WIN32 #include #endif @@ -25,6 +21,12 @@ #include #endif +#include + +#include "webrtc/typedefs.h" +// needed for NetEqDecoder +#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h" +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" /************************/ /* Define payload types */ @@ -69,10 +71,10 @@ /* Function declarations */ /*************************/ -void NetEQTest_GetCodec_and_PT(char * name, enum WebRtcNetEQDecoder *codec, int *PT, int frameLen, int *fs, int *bitrate, int *useRed); -int NetEQTest_init_coders(enum WebRtcNetEQDecoder coder, int enc_frameSize, int bitrate, int sampfreq , int vad, int numChannels); -void defineCodecs(enum WebRtcNetEQDecoder *usedCodec, int *noOfCodecs ); -int NetEQTest_free_coders(enum WebRtcNetEQDecoder coder, int numChannels); +void NetEQTest_GetCodec_and_PT(char * name, webrtc::NetEqDecoder *codec, int *PT, int frameLen, int *fs, int *bitrate, int *useRed); +int NetEQTest_init_coders(webrtc::NetEqDecoder coder, int enc_frameSize, int bitrate, int sampfreq , int vad, int numChannels); +void defineCodecs(webrtc::NetEqDecoder *usedCodec, int *noOfCodecs ); +int NetEQTest_free_coders(webrtc::NetEqDecoder coder, int numChannels); int NetEQTest_encode(int coder, int16_t *indata, int frameLen, unsigned char * encoded,int sampleRate , int * vad, int useVAD, int bitrate, int numChannels); void makeRTPheader(unsigned char* rtp_data, int payloadType, int seqNo, uint32_t timestamp, uint32_t ssrc); int makeRedundantHeader(unsigned char* rtp_data, int *payloadType, int numPayloads, uint32_t *timestamp, uint16_t *blockLen, @@ -110,7 +112,7 @@ #ifdef CODEC_ILBC #include "ilbc.h" #endif -#if (defined CODEC_ISAC || defined CODEC_ISAC_SWB || defined CODEC_ISAC_FB) +#if (defined CODEC_ISAC || defined CODEC_ISAC_SWB) #include "isac.h" #endif #ifdef NETEQ_ISACFIX_CODEC @@ -217,9 +219,6 @@ #ifdef CODEC_ISAC_SWB ISACStruct *ISACSWB_inst[2]; #endif -#ifdef CODEC_ISAC_FB - ISACStruct *ISACFB_inst[2]; -#endif #ifdef CODEC_GSMFR GSMFR_encinst_t *GSMFRenc_inst[2]; #endif @@ -236,15 +235,12 @@ #ifdef CODEC_CELT_32 CELT_encinst_t *CELT32enc_inst[2]; #endif -#ifdef CODEC_G711 - void *G711state[2]={NULL, NULL}; -#endif int main(int argc, char* argv[]) { int packet_size, fs; - enum WebRtcNetEQDecoder usedCodec; + webrtc::NetEqDecoder usedCodec; int payloadType; int bitrate = 0; int useVAD, vad; @@ -262,7 +258,7 @@ uint32_t red_TS[2] = {0}; uint16_t red_len[2] = {0}; int RTPheaderLen=12; - unsigned char red_data[8000]; + uint8_t red_data[8000]; #ifdef INSERT_OLD_PACKETS uint16_t old_length, old_plen; int old_enc_len; @@ -365,9 +361,6 @@ #ifdef CODEC_ISAC_SWB printf(" : isacswb iSAC SWB (32kHz and 32.0-52.0 kbps). To set rate specify a rate parameter as last parameter\n"); #endif -#ifdef CODEC_ISAC_FB - printf(" : isacfb iSAC FB (48kHz encoder 32kHz decoder and 32.0-52.0 kbps). To set rate specify a rate parameter as last parameter\n"); -#endif #ifdef CODEC_GSMFR printf(" : gsmfr GSM FR codec (8kHz and 13kbps)\n"); #endif @@ -443,18 +436,18 @@ switch(usedCodec) { // sample based codecs - case kDecoderPCMu: - case kDecoderPCMa: - case kDecoderG722: + case webrtc::kDecoderPCMu: + case webrtc::kDecoderPCMa: + case webrtc::kDecoderG722: { // 1 octet per sample stereoMode = STEREO_MODE_SAMPLE_1; break; } - case kDecoderPCM16B: - case kDecoderPCM16Bwb: - case kDecoderPCM16Bswb32kHz: - case kDecoderPCM16Bswb48kHz: + case webrtc::kDecoderPCM16B: + case webrtc::kDecoderPCM16Bwb: + case webrtc::kDecoderPCM16Bswb32kHz: + case webrtc::kDecoderPCM16Bswb48kHz: { // 2 octets per sample stereoMode = STEREO_MODE_SAMPLE_2; @@ -462,24 +455,6 @@ } // fixed-rate frame codecs (with internal VAD) - case kDecoderG729: - { - if(useVAD) { - printf("Cannot use codec-internal VAD and stereo\n"); - exit(0); - } - // break intentionally omitted - } - case kDecoderG722_1_16: - case kDecoderG722_1_24: - case kDecoderG722_1_32: - case kDecoderG722_1C_24: - case kDecoderG722_1C_32: - case kDecoderG722_1C_48: - { - stereoMode = STEREO_MODE_FRAME; - break; - } default: { printf("Cannot use codec %s as stereo codec\n", argv[4]); @@ -488,18 +463,17 @@ } } - if ((usedCodec == kDecoderISAC) || (usedCodec == kDecoderISACswb) || - (usedCodec == kDecoderISACfb)) + if ((usedCodec == webrtc::kDecoderISAC) || (usedCodec == webrtc::kDecoderISACswb)) { if (argc != 7) { - if (usedCodec == kDecoderISAC) + if (usedCodec == webrtc::kDecoderISAC) { bitrate = 32000; printf( "Running iSAC at default bitrate of 32000 bps (to specify explicitly add the bps as last parameter)\n"); } - else // usedCodec == kDecoderISACswb || usedCodec == kDecoderISACfb + else // (usedCodec==webrtc::kDecoderISACswb) { bitrate = 56000; printf( @@ -509,7 +483,7 @@ else { bitrate = atoi(argv[6]); - if (usedCodec == kDecoderISAC) + if (usedCodec == webrtc::kDecoderISAC) { if ((bitrate < 10000) || (bitrate > 32000)) { @@ -520,12 +494,12 @@ } printf("Running iSAC at bitrate of %i bps\n", bitrate); } - else // usedCodec == kDecoderISACswb || usedCodec == kDecoderISACfb + else // (usedCodec==webrtc::kDecoderISACswb) { if ((bitrate < 32000) || (bitrate > 56000)) { printf( - "Error: iSAC SWB/FB bitrate must be between 32000 and 56000 bps (%i is invalid)\n", + "Error: iSAC SWB bitrate must be between 32000 and 56000 bps (%i is invalid)\n", bitrate); exit(0); } @@ -775,10 +749,11 @@ if(useRed) { /* move data to redundancy store */ #ifdef CODEC_ISAC - if(usedCodec==kDecoderISAC) + if(usedCodec==webrtc::kDecoderISAC) { assert(!usingStereo); // Cannot handle stereo yet - red_len[0] = WebRtcIsac_GetRedPayload(ISAC_inst[0], (int16_t*)red_data); + red_len[0] = + WebRtcIsac_GetRedPayload(ISAC_inst[0], red_data); } else { @@ -825,205 +800,73 @@ /* Subfunctions */ /****************/ -void NetEQTest_GetCodec_and_PT(char * name, enum WebRtcNetEQDecoder *codec, int *PT, int frameLen, int *fs, int *bitrate, int *useRed) { +void NetEQTest_GetCodec_and_PT(char * name, webrtc::NetEqDecoder *codec, int *PT, int frameLen, int *fs, int *bitrate, int *useRed) { *bitrate = 0; /* Default bitrate setting */ *useRed = 0; /* Default no redundancy */ if(!strcmp(name,"pcmu")){ - *codec=kDecoderPCMu; + *codec=webrtc::kDecoderPCMu; *PT=NETEQ_CODEC_PCMU_PT; *fs=8000; } else if(!strcmp(name,"pcma")){ - *codec=kDecoderPCMa; + *codec=webrtc::kDecoderPCMa; *PT=NETEQ_CODEC_PCMA_PT; *fs=8000; } else if(!strcmp(name,"pcm16b")){ - *codec=kDecoderPCM16B; + *codec=webrtc::kDecoderPCM16B; *PT=NETEQ_CODEC_PCM16B_PT; *fs=8000; } else if(!strcmp(name,"pcm16b_wb")){ - *codec=kDecoderPCM16Bwb; + *codec=webrtc::kDecoderPCM16Bwb; *PT=NETEQ_CODEC_PCM16B_WB_PT; *fs=16000; } else if(!strcmp(name,"pcm16b_swb32")){ - *codec=kDecoderPCM16Bswb32kHz; + *codec=webrtc::kDecoderPCM16Bswb32kHz; *PT=NETEQ_CODEC_PCM16B_SWB32KHZ_PT; *fs=32000; } else if(!strcmp(name,"pcm16b_swb48")){ - *codec=kDecoderPCM16Bswb48kHz; + *codec=webrtc::kDecoderPCM16Bswb48kHz; *PT=NETEQ_CODEC_PCM16B_SWB48KHZ_PT; *fs=48000; } else if(!strcmp(name,"g722")){ - *codec=kDecoderG722; + *codec=webrtc::kDecoderG722; *PT=NETEQ_CODEC_G722_PT; *fs=16000; } - else if(!strcmp(name,"g722.1_16")){ - *codec=kDecoderG722_1_16; - *PT=NETEQ_CODEC_G722_1_16_PT; - *fs=16000; - } - else if(!strcmp(name,"g722.1_24")){ - *codec=kDecoderG722_1_24; - *PT=NETEQ_CODEC_G722_1_24_PT; - *fs=16000; - } - else if(!strcmp(name,"g722.1_32")){ - *codec=kDecoderG722_1_32; - *PT=NETEQ_CODEC_G722_1_32_PT; - *fs=16000; - } - else if(!strcmp(name,"g722.1C_24")){ - *codec=kDecoderG722_1C_24; - *PT=NETEQ_CODEC_G722_1C_24_PT; - *fs=32000; - } - else if(!strcmp(name,"g722.1C_32")){ - *codec=kDecoderG722_1C_32; - *PT=NETEQ_CODEC_G722_1C_32_PT; - *fs=32000; - } - else if(!strcmp(name,"g722.1C_48")){ - *codec=kDecoderG722_1C_48; - *PT=NETEQ_CODEC_G722_1C_48_PT; - *fs=32000; - } - else if(!strcmp(name,"g726_16")){ - *fs=8000; - *codec=kDecoderG726_16; - *PT=NETEQ_CODEC_G726_16_PT; - *bitrate=16; - } - else if(!strcmp(name,"g726_24")){ - *fs=8000; - *codec=kDecoderG726_24; - *PT=NETEQ_CODEC_G726_24_PT; - *bitrate=24; - } - else if(!strcmp(name,"g726_32")){ - *fs=8000; - *codec=kDecoderG726_32; - *PT=NETEQ_CODEC_G726_32_PT; - *bitrate=32; - } - else if(!strcmp(name,"g726_40")){ - *fs=8000; - *codec=kDecoderG726_40; - *PT=NETEQ_CODEC_G726_40_PT; - *bitrate=40; - } - else if((!strcmp(name,"amr4.75k"))||(!strcmp(name,"amr5.15k"))||(!strcmp(name,"amr5.9k"))|| - (!strcmp(name,"amr6.7k"))||(!strcmp(name,"amr7.4k"))||(!strcmp(name,"amr7.95k"))|| - (!strcmp(name,"amr10.2k"))||(!strcmp(name,"amr12.2k"))) { - *fs=8000; - if (!strcmp(name,"amr4.75k")) - *bitrate = 0; - if (!strcmp(name,"amr5.15k")) - *bitrate = 1; - if (!strcmp(name,"amr5.9k")) - *bitrate = 2; - if (!strcmp(name,"amr6.7k")) - *bitrate = 3; - if (!strcmp(name,"amr7.4k")) - *bitrate = 4; - if (!strcmp(name,"amr7.95k")) - *bitrate = 5; - if (!strcmp(name,"amr10.2k")) - *bitrate = 6; - if (!strcmp(name,"amr12.2k")) - *bitrate = 7; - *codec=kDecoderAMR; - *PT=NETEQ_CODEC_AMR_PT; - } - else if((!strcmp(name,"amrwb7k"))||(!strcmp(name,"amrwb9k"))||(!strcmp(name,"amrwb12k"))|| - (!strcmp(name,"amrwb14k"))||(!strcmp(name,"amrwb16k"))||(!strcmp(name,"amrwb18k"))|| - (!strcmp(name,"amrwb20k"))||(!strcmp(name,"amrwb23k"))||(!strcmp(name,"amrwb24k"))) { - *fs=16000; - if (!strcmp(name,"amrwb7k")) - *bitrate = 7000; - if (!strcmp(name,"amrwb9k")) - *bitrate = 9000; - if (!strcmp(name,"amrwb12k")) - *bitrate = 12000; - if (!strcmp(name,"amrwb14k")) - *bitrate = 14000; - if (!strcmp(name,"amrwb16k")) - *bitrate = 16000; - if (!strcmp(name,"amrwb18k")) - *bitrate = 18000; - if (!strcmp(name,"amrwb20k")) - *bitrate = 20000; - if (!strcmp(name,"amrwb23k")) - *bitrate = 23000; - if (!strcmp(name,"amrwb24k")) - *bitrate = 24000; - *codec=kDecoderAMRWB; - *PT=NETEQ_CODEC_AMRWB_PT; - } else if((!strcmp(name,"ilbc"))&&((frameLen%240==0)||(frameLen%160==0))){ *fs=8000; - *codec=kDecoderILBC; + *codec=webrtc::kDecoderILBC; *PT=NETEQ_CODEC_ILBC_PT; } else if(!strcmp(name,"isac")){ *fs=16000; - *codec=kDecoderISAC; + *codec=webrtc::kDecoderISAC; *PT=NETEQ_CODEC_ISAC_PT; } - else if(!strcmp(name,"isacswb")){ - *fs=32000; - *codec=kDecoderISACswb; - *PT=NETEQ_CODEC_ISACSWB_PT; - } - else if(!strcmp(name,"isacfb")){ - *fs=48000; - *codec=kDecoderISACfb; - *PT=NETEQ_CODEC_ISACFB_PT; - } - else if(!strcmp(name,"g729")){ - *fs=8000; - *codec=kDecoderG729; - *PT=NETEQ_CODEC_G729_PT; - } - else if(!strcmp(name,"g729.1")){ - *fs=16000; - *codec=kDecoderG729_1; - *PT=NETEQ_CODEC_G729_1_PT; - } - else if(!strcmp(name,"gsmfr")){ - *fs=8000; - *codec=kDecoderGSMFR; - *PT=NETEQ_CODEC_GSMFR_PT; - } - else if(!strcmp(name,"speex8")){ - *fs=8000; - *codec=kDecoderSPEEX_8; - *PT=NETEQ_CODEC_SPEEX8_PT; - } - else if(!strcmp(name,"speex16")){ - *fs=16000; - *codec=kDecoderSPEEX_16; - *PT=NETEQ_CODEC_SPEEX16_PT; + else if(!strcmp(name,"isacswb")){ + *fs=32000; + *codec=webrtc::kDecoderISACswb; + *PT=NETEQ_CODEC_ISACSWB_PT; } else if(!strcmp(name,"celt32")){ *fs=32000; - *codec=kDecoderCELT_32; + *codec=webrtc::kDecoderCELT_32; *PT=NETEQ_CODEC_CELT32_PT; } else if(!strcmp(name,"red_pcm")){ - *codec=kDecoderPCMa; + *codec=webrtc::kDecoderPCMa; *PT=NETEQ_CODEC_PCMA_PT; /* this will be the PT for the sub-headers */ *fs=8000; *useRed = 1; } else if(!strcmp(name,"red_isac")){ - *codec=kDecoderISAC; + *codec=webrtc::kDecoderISAC; *PT=NETEQ_CODEC_ISAC_PT; /* this will be the PT for the sub-headers */ *fs=16000; *useRed = 1; @@ -1037,7 +880,7 @@ -int NetEQTest_init_coders(enum WebRtcNetEQDecoder coder, int enc_frameSize, int bitrate, int sampfreq , int vad, int numChannels){ +int NetEQTest_init_coders(webrtc::NetEqDecoder coder, int enc_frameSize, int bitrate, int sampfreq , int vad, int numChannels){ int ok=0; @@ -1073,27 +916,26 @@ #endif switch (coder) { - case kDecoderReservedStart : // dummy codec #ifdef CODEC_PCM16B - case kDecoderPCM16B : + case webrtc::kDecoderPCM16B : #endif #ifdef CODEC_PCM16B_WB - case kDecoderPCM16Bwb : + case webrtc::kDecoderPCM16Bwb : #endif #ifdef CODEC_PCM16B_32KHZ - case kDecoderPCM16Bswb32kHz : + case webrtc::kDecoderPCM16Bswb32kHz : #endif #ifdef CODEC_PCM16B_48KHZ - case kDecoderPCM16Bswb48kHz : + case webrtc::kDecoderPCM16Bswb48kHz : #endif #ifdef CODEC_G711 - case kDecoderPCMu : - case kDecoderPCMa : + case webrtc::kDecoderPCMu : + case webrtc::kDecoderPCMa : #endif // do nothing break; #ifdef CODEC_G729 - case kDecoderG729: + case webrtc::kDecoderG729: if (sampfreq==8000) { if ((enc_frameSize==80)||(enc_frameSize==160)||(enc_frameSize==240)||(enc_frameSize==320)||(enc_frameSize==400)||(enc_frameSize==480)) { ok=WebRtcG729_CreateEnc(&G729enc_inst[k]); @@ -1116,7 +958,7 @@ break; #endif #ifdef CODEC_G729_1 - case kDecoderG729_1: + case webrtc::kDecoderG729_1: if (sampfreq==16000) { if ((enc_frameSize==320)||(enc_frameSize==640)||(enc_frameSize==960) ) { @@ -1142,7 +984,7 @@ break; #endif #ifdef CODEC_SPEEX_8 - case kDecoderSPEEX_8 : + case webrtc::kDecoderSPEEX_8 : if (sampfreq==8000) { if ((enc_frameSize==160)||(enc_frameSize==320)||(enc_frameSize==480)) { ok=WebRtcSpeex_CreateEnc(&SPEEX8enc_inst[k], sampfreq); @@ -1166,7 +1008,7 @@ break; #endif #ifdef CODEC_SPEEX_16 - case kDecoderSPEEX_16 : + case webrtc::kDecoderSPEEX_16 : if (sampfreq==16000) { if ((enc_frameSize==320)||(enc_frameSize==640)||(enc_frameSize==960)) { ok=WebRtcSpeex_CreateEnc(&SPEEX16enc_inst[k], sampfreq); @@ -1190,7 +1032,7 @@ break; #endif #ifdef CODEC_CELT_32 - case kDecoderCELT_32 : + case webrtc::kDecoderCELT_32 : if (sampfreq==32000) { if (enc_frameSize==320) { ok=WebRtcCelt_CreateEnc(&CELT32enc_inst[k], 1 /*mono*/); @@ -1211,7 +1053,7 @@ #endif #ifdef CODEC_G722_1_16 - case kDecoderG722_1_16 : + case webrtc::kDecoderG722_1_16 : if (sampfreq==16000) { ok=WebRtcG7221_CreateEnc16(&G722_1_16enc_inst[k]); if (ok!=0) { @@ -1231,7 +1073,7 @@ break; #endif #ifdef CODEC_G722_1_24 - case kDecoderG722_1_24 : + case webrtc::kDecoderG722_1_24 : if (sampfreq==16000) { ok=WebRtcG7221_CreateEnc24(&G722_1_24enc_inst[k]); if (ok!=0) { @@ -1251,7 +1093,7 @@ break; #endif #ifdef CODEC_G722_1_32 - case kDecoderG722_1_32 : + case webrtc::kDecoderG722_1_32 : if (sampfreq==16000) { ok=WebRtcG7221_CreateEnc32(&G722_1_32enc_inst[k]); if (ok!=0) { @@ -1271,7 +1113,7 @@ break; #endif #ifdef CODEC_G722_1C_24 - case kDecoderG722_1C_24 : + case webrtc::kDecoderG722_1C_24 : if (sampfreq==32000) { ok=WebRtcG7221C_CreateEnc24(&G722_1C_24enc_inst[k]); if (ok!=0) { @@ -1291,7 +1133,7 @@ break; #endif #ifdef CODEC_G722_1C_32 - case kDecoderG722_1C_32 : + case webrtc::kDecoderG722_1C_32 : if (sampfreq==32000) { ok=WebRtcG7221C_CreateEnc32(&G722_1C_32enc_inst[k]); if (ok!=0) { @@ -1311,7 +1153,7 @@ break; #endif #ifdef CODEC_G722_1C_48 - case kDecoderG722_1C_48 : + case webrtc::kDecoderG722_1C_48 : if (sampfreq==32000) { ok=WebRtcG7221C_CreateEnc48(&G722_1C_48enc_inst[k]); if (ok!=0) { @@ -1331,7 +1173,7 @@ break; #endif #ifdef CODEC_G722 - case kDecoderG722 : + case webrtc::kDecoderG722 : if (sampfreq==16000) { if (enc_frameSize%2==0) { } else { @@ -1347,7 +1189,7 @@ break; #endif #ifdef CODEC_AMR - case kDecoderAMR : + case webrtc::kDecoderAMR : if (sampfreq==8000) { ok=WebRtcAmr_CreateEnc(&AMRenc_inst[k]); if (ok!=0) { @@ -1368,7 +1210,7 @@ break; #endif #ifdef CODEC_AMRWB - case kDecoderAMRWB : + case webrtc::kDecoderAMRWB : if (sampfreq==16000) { ok=WebRtcAmrWb_CreateEnc(&AMRWBenc_inst[k]); if (ok!=0) { @@ -1408,7 +1250,7 @@ break; #endif #ifdef CODEC_ILBC - case kDecoderILBC : + case webrtc::kDecoderILBC : if (sampfreq==8000) { ok=WebRtcIlbcfix_EncoderCreate(&iLBCenc_inst[k]); if (ok!=0) { @@ -1434,7 +1276,7 @@ break; #endif #ifdef CODEC_ISAC - case kDecoderISAC: + case webrtc::kDecoderISAC: if (sampfreq==16000) { ok=WebRtcIsac_Create(&ISAC_inst[k]); if (ok!=0) { @@ -1458,7 +1300,7 @@ break; #endif #ifdef NETEQ_ISACFIX_CODEC - case kDecoderISAC: + case webrtc::kDecoderISAC: if (sampfreq==16000) { ok=WebRtcIsacfix_Create(&ISAC_inst[k]); if (ok!=0) { @@ -1482,7 +1324,7 @@ break; #endif #ifdef CODEC_ISAC_SWB - case kDecoderISACswb: + case webrtc::kDecoderISACswb: if (sampfreq==32000) { ok=WebRtcIsac_Create(&ISACSWB_inst[k]); if (ok!=0) { @@ -1510,40 +1352,8 @@ } break; #endif -#ifdef CODEC_ISAC_FB - case kDecoderISACfb: - if (sampfreq == 48000) { - ok = WebRtcIsac_Create(&ISACFB_inst[k]); - if (ok != 0) { - printf("Error: Couldn't allocate memory for iSAC FB " - "instance\n"); - exit(0); - } - if (enc_frameSize != 1440) { - printf("\nError - iSAC FB only supports frameSize 30 ms\n"); - exit(0); - } - ok = WebRtcIsac_SetEncSampRate(ISACFB_inst[k], 48000); - if (ok != 0) { - printf("Error: Couldn't set sample rate for iSAC FB " - "instance\n"); - exit(0); - } - WebRtcIsac_EncoderInit(ISACFB_inst[k], 1); - if ((bitrate < 32000) || (bitrate > 56000)) { - printf("\nError - iSAC FB bitrate has to be between 32000 and" - "56000 bps (not %i)\n", bitrate); - exit(0); - } - WebRtcIsac_Control(ISACFB_inst[k], bitrate, 30); - } else { - printf("\nError - iSAC FB only support 48 kHz sampling rate.\n"); - exit(0); - } - break; -#endif #ifdef CODEC_GSMFR - case kDecoderGSMFR: + case webrtc::kDecoderGSMFR: if (sampfreq==8000) { ok=WebRtcGSMFR_CreateEnc(&GSMFRenc_inst[k]); if (ok!=0) { @@ -1579,7 +1389,7 @@ -int NetEQTest_free_coders(enum WebRtcNetEQDecoder coder, int numChannels) { +int NetEQTest_free_coders(webrtc::NetEqDecoder coder, int numChannels) { for (int k = 0; k < numChannels; k++) { @@ -1591,123 +1401,117 @@ switch (coder) { - case kDecoderReservedStart : // dummy codec #ifdef CODEC_PCM16B - case kDecoderPCM16B : + case webrtc::kDecoderPCM16B : #endif #ifdef CODEC_PCM16B_WB - case kDecoderPCM16Bwb : + case webrtc::kDecoderPCM16Bwb : #endif #ifdef CODEC_PCM16B_32KHZ - case kDecoderPCM16Bswb32kHz : + case webrtc::kDecoderPCM16Bswb32kHz : #endif #ifdef CODEC_PCM16B_48KHZ - case kDecoderPCM16Bswb48kHz : + case webrtc::kDecoderPCM16Bswb48kHz : #endif #ifdef CODEC_G711 - case kDecoderPCMu : - case kDecoderPCMa : + case webrtc::kDecoderPCMu : + case webrtc::kDecoderPCMa : #endif // do nothing break; #ifdef CODEC_G729 - case kDecoderG729: + case webrtc::kDecoderG729: WebRtcG729_FreeEnc(G729enc_inst[k]); break; #endif #ifdef CODEC_G729_1 - case kDecoderG729_1: + case webrtc::kDecoderG729_1: WebRtcG7291_Free(G729_1_inst[k]); break; #endif #ifdef CODEC_SPEEX_8 - case kDecoderSPEEX_8 : + case webrtc::kDecoderSPEEX_8 : WebRtcSpeex_FreeEnc(SPEEX8enc_inst[k]); break; #endif #ifdef CODEC_SPEEX_16 - case kDecoderSPEEX_16 : + case webrtc::kDecoderSPEEX_16 : WebRtcSpeex_FreeEnc(SPEEX16enc_inst[k]); break; #endif #ifdef CODEC_CELT_32 - case kDecoderCELT_32 : + case webrtc::kDecoderCELT_32 : WebRtcCelt_FreeEnc(CELT32enc_inst[k]); break; #endif #ifdef CODEC_G722_1_16 - case kDecoderG722_1_16 : + case webrtc::kDecoderG722_1_16 : WebRtcG7221_FreeEnc16(G722_1_16enc_inst[k]); break; #endif #ifdef CODEC_G722_1_24 - case kDecoderG722_1_24 : + case webrtc::kDecoderG722_1_24 : WebRtcG7221_FreeEnc24(G722_1_24enc_inst[k]); break; #endif #ifdef CODEC_G722_1_32 - case kDecoderG722_1_32 : + case webrtc::kDecoderG722_1_32 : WebRtcG7221_FreeEnc32(G722_1_32enc_inst[k]); break; #endif #ifdef CODEC_G722_1C_24 - case kDecoderG722_1C_24 : + case webrtc::kDecoderG722_1C_24 : WebRtcG7221C_FreeEnc24(G722_1C_24enc_inst[k]); break; #endif #ifdef CODEC_G722_1C_32 - case kDecoderG722_1C_32 : + case webrtc::kDecoderG722_1C_32 : WebRtcG7221C_FreeEnc32(G722_1C_32enc_inst[k]); break; #endif #ifdef CODEC_G722_1C_48 - case kDecoderG722_1C_48 : + case webrtc::kDecoderG722_1C_48 : WebRtcG7221C_FreeEnc48(G722_1C_48enc_inst[k]); break; #endif #ifdef CODEC_G722 - case kDecoderG722 : + case webrtc::kDecoderG722 : WebRtcG722_FreeEncoder(g722EncState[k]); break; #endif #ifdef CODEC_AMR - case kDecoderAMR : + case webrtc::kDecoderAMR : WebRtcAmr_FreeEnc(AMRenc_inst[k]); break; #endif #ifdef CODEC_AMRWB - case kDecoderAMRWB : + case webrtc::kDecoderAMRWB : WebRtcAmrWb_FreeEnc(AMRWBenc_inst[k]); break; #endif #ifdef CODEC_ILBC - case kDecoderILBC : + case webrtc::kDecoderILBC : WebRtcIlbcfix_EncoderFree(iLBCenc_inst[k]); break; #endif #ifdef CODEC_ISAC - case kDecoderISAC: + case webrtc::kDecoderISAC: WebRtcIsac_Free(ISAC_inst[k]); break; #endif #ifdef NETEQ_ISACFIX_CODEC - case kDecoderISAC: + case webrtc::kDecoderISAC: WebRtcIsacfix_Free(ISAC_inst[k]); break; #endif #ifdef CODEC_ISAC_SWB - case kDecoderISACswb: + case webrtc::kDecoderISACswb: WebRtcIsac_Free(ISACSWB_inst[k]); break; #endif -#ifdef CODEC_ISAC_FB - case kDecoderISACfb: - WebRtcIsac_Free(ISACFB_inst[k]); - break; -#endif #ifdef CODEC_GSMFR - case kDecoderGSMFR: + case webrtc::kDecoderGSMFR: WebRtcGSMFR_FreeEnc(GSMFRenc_inst[k]); break; #endif @@ -1737,9 +1541,7 @@ *vad =1; // check VAD first - if(useVAD&& - (coder!=kDecoderG729)&&(coder!=kDecoderAMR)&& - (coder!=kDecoderSPEEX_8)&&(coder!=kDecoderSPEEX_16)) + if(useVAD) { *vad = 0; @@ -1795,168 +1597,65 @@ for (int k = 0; k < numChannels; k++) { /* Encode with the selected coder type */ - if (coder==kDecoderPCMu) { /*g711 u-law */ + if (coder==webrtc::kDecoderPCMu) { /*g711 u-law */ #ifdef CODEC_G711 - cdlen = WebRtcG711_EncodeU(G711state[k], indata, frameLen, (int16_t*) encoded); + cdlen = WebRtcG711_EncodeU(indata, frameLen, (int16_t*) encoded); #endif } - else if (coder==kDecoderPCMa) { /*g711 A-law */ + else if (coder==webrtc::kDecoderPCMa) { /*g711 A-law */ #ifdef CODEC_G711 - cdlen = WebRtcG711_EncodeA(G711state[k], indata, frameLen, (int16_t*) encoded); + cdlen = WebRtcG711_EncodeA(indata, frameLen, (int16_t*) encoded); } #endif #ifdef CODEC_PCM16B - else if ((coder==kDecoderPCM16B)||(coder==kDecoderPCM16Bwb)|| - (coder==kDecoderPCM16Bswb32kHz)||(coder==kDecoderPCM16Bswb48kHz)) { /*pcm16b (8kHz, 16kHz, 32kHz or 48kHz) */ + else if ((coder==webrtc::kDecoderPCM16B)||(coder==webrtc::kDecoderPCM16Bwb)|| + (coder==webrtc::kDecoderPCM16Bswb32kHz)||(coder==webrtc::kDecoderPCM16Bswb48kHz)) { /*pcm16b (8kHz, 16kHz, 32kHz or 48kHz) */ cdlen = WebRtcPcm16b_EncodeW16(indata, frameLen, (int16_t*) encoded); } #endif #ifdef CODEC_G722 - else if (coder==kDecoderG722) { /*g722 */ + else if (coder==webrtc::kDecoderG722) { /*g722 */ cdlen=WebRtcG722_Encode(g722EncState[k], indata, frameLen, (int16_t*)encoded); - cdlen=frameLen>>1; - } -#endif -#ifdef CODEC_G722_1_16 - else if (coder==kDecoderG722_1_16) { /* g722.1 16kbit/s mode */ - cdlen=WebRtcG7221_Encode16((G722_1_16_encinst_t*)G722_1_16enc_inst[k], indata, frameLen, (int16_t*)encoded); - } -#endif -#ifdef CODEC_G722_1_24 - else if (coder==kDecoderG722_1_24) { /* g722.1 24kbit/s mode*/ - cdlen=WebRtcG7221_Encode24((G722_1_24_encinst_t*)G722_1_24enc_inst[k], indata, frameLen, (int16_t*)encoded); - } -#endif -#ifdef CODEC_G722_1_32 - else if (coder==kDecoderG722_1_32) { /* g722.1 32kbit/s mode */ - cdlen=WebRtcG7221_Encode32((G722_1_32_encinst_t*)G722_1_32enc_inst[k], indata, frameLen, (int16_t*)encoded); - } -#endif -#ifdef CODEC_G722_1C_24 - else if (coder==kDecoderG722_1C_24) { /* g722.1 32 kHz 24kbit/s mode*/ - cdlen=WebRtcG7221C_Encode24((G722_1C_24_encinst_t*)G722_1C_24enc_inst[k], indata, frameLen, (int16_t*)encoded); - } -#endif -#ifdef CODEC_G722_1C_32 - else if (coder==kDecoderG722_1C_32) { /* g722.1 32 kHz 32kbit/s mode */ - cdlen=WebRtcG7221C_Encode32((G722_1C_32_encinst_t*)G722_1C_32enc_inst[k], indata, frameLen, (int16_t*)encoded); - } -#endif -#ifdef CODEC_G722_1C_48 - else if (coder==kDecoderG722_1C_48) { /* g722.1 32 kHz 48kbit/s mode */ - cdlen=WebRtcG7221C_Encode48((G722_1C_48_encinst_t*)G722_1C_48enc_inst[k], indata, frameLen, (int16_t*)encoded); - } -#endif -#ifdef CODEC_G729 - else if (coder==kDecoderG729) { /*g729 */ - int16_t dataPos=0; - int16_t len=0; - cdlen = 0; - for (dataPos=0;dataPos>1); } #endif #ifdef CODEC_ILBC - else if (coder==kDecoderILBC) { /*iLBC */ + else if (coder==webrtc::kDecoderILBC) { /*iLBC */ cdlen=WebRtcIlbcfix_Encode(iLBCenc_inst[k], indata,frameLen,(int16_t*)encoded); } #endif #if (defined(CODEC_ISAC) || defined(NETEQ_ISACFIX_CODEC)) // TODO(hlundin): remove all NETEQ_ISACFIX_CODEC - else if (coder==kDecoderISAC) { /*iSAC */ + else if (coder==webrtc::kDecoderISAC) { /*iSAC */ int noOfCalls=0; cdlen=0; while (cdlen<=0) { #ifdef CODEC_ISAC /* floating point */ - cdlen=WebRtcIsac_Encode(ISAC_inst[k],&indata[noOfCalls*160],(int16_t*)encoded); + cdlen = WebRtcIsac_Encode(ISAC_inst[k], + &indata[noOfCalls * 160], + encoded); #else /* fixed point */ - cdlen=WebRtcIsacfix_Encode(ISAC_inst[k],&indata[noOfCalls*160],(int16_t*)encoded); + cdlen = WebRtcIsacfix_Encode(ISAC_inst[k], + &indata[noOfCalls * 160], + encoded); #endif noOfCalls++; } } #endif #ifdef CODEC_ISAC_SWB - else if (coder==kDecoderISACswb) { /* iSAC SWB */ + else if (coder==webrtc::kDecoderISACswb) { /* iSAC SWB */ int noOfCalls=0; cdlen=0; while (cdlen<=0) { - cdlen=WebRtcIsac_Encode(ISACSWB_inst[k],&indata[noOfCalls*320],(int16_t*)encoded); + cdlen = WebRtcIsac_Encode(ISACSWB_inst[k], + &indata[noOfCalls * 320], + encoded); noOfCalls++; } } #endif -#ifdef CODEC_ISAC_FB - else if (coder == kDecoderISACfb) { /* iSAC FB */ - int noOfCalls = 0; - cdlen = 0; - while (cdlen <= 0) { - cdlen = WebRtcIsac_Encode(ISACFB_inst[k], - &indata[noOfCalls * 480], - (int16_t*)encoded); - noOfCalls++; - } - } -#endif -#ifdef CODEC_GSMFR - else if (coder==kDecoderGSMFR) { /* GSM FR */ - cdlen=WebRtcGSMFR_Encode(GSMFRenc_inst[k], indata, frameLen, (int16_t*)encoded); - } -#endif -#ifdef CODEC_SPEEX_8 - else if (coder==kDecoderSPEEX_8) { /* Speex */ - int encodedLen = 0; - int retVal = 1; - while (retVal == 1 && encodedLen < frameLen) { - retVal = WebRtcSpeex_Encode(SPEEX8enc_inst[k], &indata[encodedLen], 15000); - encodedLen += 20*8; /* 20 ms */ - } - if( (retVal == 0 && encodedLen != frameLen) || retVal < 0) { - printf("Error encoding speex frame!\n"); - exit(0); - } - cdlen=WebRtcSpeex_GetBitstream(SPEEX8enc_inst[k], (int16_t*)encoded); - } -#endif -#ifdef CODEC_SPEEX_16 - else if (coder==kDecoderSPEEX_16) { /* Speex */ - int encodedLen = 0; - int retVal = 1; - while (retVal == 1 && encodedLen < frameLen) { - retVal = WebRtcSpeex_Encode(SPEEX16enc_inst[k], &indata[encodedLen], 15000); - encodedLen += 20*16; /* 20 ms */ - } - if( (retVal == 0 && encodedLen != frameLen) || retVal < 0) { - printf("Error encoding speex frame!\n"); - exit(0); - } - cdlen=WebRtcSpeex_GetBitstream(SPEEX16enc_inst[k], (int16_t*)encoded); - } -#endif #ifdef CODEC_CELT_32 - else if (coder==kDecoderCELT_32) { /* Celt */ + else if (coder==webrtc::kDecoderCELT_32) { /* Celt */ int encodedLen = 0; cdlen = 0; while (cdlen <= 0) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPjitter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPjitter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPjitter.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPjitter.cc 2015-02-03 14:33:34.000000000 +0000 @@ -11,9 +11,10 @@ //TODO(hlundin): Reformat file to meet style guide. /* header includes */ -#include "typedefs.h" +#include #include #include +#include #ifdef WIN32 #include #include @@ -21,21 +22,24 @@ #ifdef WEBRTC_LINUX #include #endif -#include -#include "gtest/gtest.h" +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/typedefs.h" /*********************/ /* Misc. definitions */ /*********************/ #define FIRSTLINELEN 40 -#define CHECK_ZERO(a) {int errCode = a; if((errCode)!=0){fprintf(stderr,"\n %s \n line: %d \n error at %s\n Error Code = %d\n",__FILE__,__LINE__,#a, WebRtcNetEQ_GetErrorCode(inst)); exit(0);}} -#define CHECK_NOT_NULL(a) if((a)==NULL){fprintf(stderr,"\n %s \n line: %d \nerror at %s\n",__FILE__,__LINE__,#a );return(-1);} +#define CHECK_NOT_NULL(a) if((a)==NULL){ \ + fprintf(stderr,"\n %s \n line: %d \nerror at %s\n",__FILE__,__LINE__,#a ); \ + return(-1);} struct arr_time { - float time; - uint32_t ix; + float time; + uint32_t ix; }; int filelen(FILE *fid) @@ -59,142 +63,167 @@ int main(int argc, char* argv[]) { - unsigned int dat_len, rtp_len, Npack, k; - arr_time *time_vec; - char firstline[FIRSTLINELEN]; - unsigned char *rtp_vec = NULL, **packet_ptr, *temp_packet; - const unsigned int kRtpDumpHeaderSize = 4 + 4 + 4 + 2 + 2; - uint16_t len; - uint32_t *offset; + unsigned int dat_len, rtp_len, Npack, k; + arr_time *time_vec; + char firstline[FIRSTLINELEN]; + unsigned char* rtp_vec = NULL; + unsigned char** packet_ptr = NULL; + unsigned char* temp_packet = NULL; + const unsigned int kRtpDumpHeaderSize = 4 + 4 + 4 + 2 + 2; + uint16_t len; + uint32_t *offset; /* check number of parameters */ - if (argc != 4) { - /* print help text and exit */ - printf("Apply jitter on RTP stream.\n"); - printf("The program reads an RTP stream and packet timing from two files.\n"); - printf("The RTP stream is modified to have the same jitter as described in the timing files.\n"); - printf("The format of the RTP stream file should be the same as for rtpplay,\n"); - printf("and can be obtained e.g., from Ethereal by using\n"); - printf("Statistics -> RTP -> Show All Streams -> [select a stream] -> Save As\n\n"); - printf("Usage:\n\n"); - printf("%s RTP_infile dat_file RTP_outfile\n", argv[0]); - printf("where:\n"); - - printf("RTP_infile : RTP stream input file\n\n"); - - printf("dat_file : file with packet arrival times in ms\n\n"); - - printf("RTP_outfile : RTP stream output file\n\n"); - - return(0); - } - - FILE* in_file=fopen(argv[1],"rb"); - CHECK_NOT_NULL(in_file); - printf("Input file: %s\n",argv[1]); - FILE* dat_file=fopen(argv[2],"rb"); - CHECK_NOT_NULL(dat_file); - printf("Dat-file: %s\n",argv[2]); - FILE* out_file=fopen(argv[3],"wb"); - CHECK_NOT_NULL(out_file); - printf("Output file: %s\n\n",argv[3]); - - time_vec = (arr_time *) malloc(sizeof(arr_time)*(filelen(dat_file)/sizeof(float)) + 1000); // add 1000 bytes to avoid (rare) strange error - if (time_vec==NULL) { - fprintf(stderr, "Error: could not allocate memory for reading dat file\n"); - goto closing; - } - - dat_len=0; - while(fread(&(time_vec[dat_len].time),sizeof(float),1,dat_file)>0) { - time_vec[dat_len].ix=dat_len; - dat_len++; - } - - qsort(time_vec,dat_len,sizeof(arr_time),compare_arr_time); - - - rtp_vec = (unsigned char *) malloc(sizeof(unsigned char)*filelen(in_file)); - if (rtp_vec==NULL) { - fprintf(stderr,"Error: could not allocate memory for reading rtp file\n"); - goto closing; - } - - // read file header and write directly to output file - EXPECT_TRUE(fgets(firstline, FIRSTLINELEN, in_file) != NULL); - EXPECT_GT(fputs(firstline, out_file), 0); - EXPECT_EQ(kRtpDumpHeaderSize, fread(firstline, 1, kRtpDumpHeaderSize, - in_file)); - EXPECT_EQ(kRtpDumpHeaderSize, fwrite(firstline, 1, kRtpDumpHeaderSize, - out_file)); - - // read all RTP packets into vector - rtp_len=0; - Npack=0; - len=(uint16_t) fread(&rtp_vec[rtp_len], sizeof(unsigned char), 2, in_file); // read length of first packet - while(len==2) { - len = ntohs(*((uint16_t *)(rtp_vec + rtp_len))); - rtp_len += 2; - if(fread(&rtp_vec[rtp_len], sizeof(unsigned char), len-2, in_file)!=(unsigned) (len-2)) { - fprintf(stderr,"Error: currupt packet length\n"); - goto closing; - } - rtp_len += len-2; - Npack++; - len=(uint16_t) fread(&rtp_vec[rtp_len], sizeof(unsigned char), 2, in_file); // read length of next packet - } - - packet_ptr = (unsigned char **) malloc(Npack*sizeof(unsigned char*)); - - packet_ptr[0]=rtp_vec; - k=1; - while(k= 0 ) { - *offset = htonl((uint32_t) time_vec[k].time); - } - else { - *offset = htonl((uint32_t) 0); - fprintf(stderr, "Warning: negative receive time in dat file transformed to 0.\n"); - } - - // write packet to file - if (fwrite(temp_packet, sizeof(unsigned char), - ntohs(*((uint16_t*) temp_packet)), - out_file) != - ntohs(*((uint16_t*) temp_packet))) { - return -1; - } - } - } + if (argc != 4) { + /* print help text and exit */ + printf("Apply jitter on RTP stream.\n"); + printf("Reads an RTP stream and packet timing from two files.\n"); + printf("The RTP stream is modified to have the same jitter as described in " + "the timing files.\n"); + printf("The format of the RTP stream file should be the same as for \n"); + printf("rtpplay, and can be obtained e.g., from Ethereal by using\n"); + printf("Statistics -> RTP -> Show All Streams -> [select a stream] -> " + "Save As\n\n"); + printf("Usage:\n\n"); + printf("%s RTP_infile dat_file RTP_outfile\n", argv[0]); + printf("where:\n"); + + printf("RTP_infile : RTP stream input file\n\n"); + + printf("dat_file : file with packet arrival times in ms\n\n"); + + printf("RTP_outfile : RTP stream output file\n\n"); + + return(0); + } + + FILE* in_file=fopen(argv[1],"rb"); + CHECK_NOT_NULL(in_file); + printf("Input file: %s\n",argv[1]); + FILE* dat_file=fopen(argv[2],"rb"); + CHECK_NOT_NULL(dat_file); + printf("Dat-file: %s\n",argv[2]); + FILE* out_file=fopen(argv[3],"wb"); + CHECK_NOT_NULL(out_file); + printf("Output file: %s\n\n",argv[3]); + + // add 1000 bytes to avoid (rare) strange error. + time_vec = (arr_time *) malloc(sizeof(arr_time) + *(filelen(dat_file)/sizeof(float)) + 1000); + if (time_vec==NULL) { + fprintf(stderr, "Error: could not allocate memory for reading dat file\n"); + goto closing; + } + + dat_len=0; + while(fread(&(time_vec[dat_len].time),sizeof(float),1,dat_file)>0) { + time_vec[dat_len].ix=dat_len; + dat_len++; + } + + if (dat_len == 0) { + fprintf(stderr, "Error: dat_file is empty, no arrival time is given.\n"); + goto closing; + } + + qsort(time_vec,dat_len,sizeof(arr_time),compare_arr_time); + + + rtp_vec = (unsigned char *) malloc(sizeof(unsigned char)*filelen(in_file)); + if (rtp_vec==NULL) { + fprintf(stderr,"Error: could not allocate memory for reading rtp file\n"); + goto closing; + } + + // read file header and write directly to output file + EXPECT_TRUE(fgets(firstline, FIRSTLINELEN, in_file) != NULL); + EXPECT_GT(fputs(firstline, out_file), 0); + EXPECT_EQ(kRtpDumpHeaderSize, fread(firstline, 1, kRtpDumpHeaderSize, + in_file)); + EXPECT_EQ(kRtpDumpHeaderSize, fwrite(firstline, 1, kRtpDumpHeaderSize, + out_file)); + + // read all RTP packets into vector + rtp_len=0; + Npack=0; + + // read length of first packet. + len=(uint16_t) fread(&rtp_vec[rtp_len], sizeof(unsigned char), 2, in_file); + while(len==2) { + len = ntohs(*((uint16_t *)(rtp_vec + rtp_len))); + rtp_len += 2; + if(fread(&rtp_vec[rtp_len], sizeof(unsigned char), + len-2, in_file)!=(unsigned) (len-2)) { + fprintf(stderr,"Error: currupt packet length\n"); + goto closing; + } + rtp_len += len-2; + Npack++; + + // read length of next packet. + len=(uint16_t) fread(&rtp_vec[rtp_len], sizeof(unsigned char), 2, in_file); + } + + if (Npack == 0) { + fprintf(stderr, "Error: No RTP packet found.\n"); + goto closing; + } + + packet_ptr = (unsigned char **) malloc(Npack*sizeof(unsigned char*)); + + packet_ptr[0]=rtp_vec; + k=1; + while(k= 0 ) { + *offset = htonl((uint32_t) time_vec[k].time); + } + else { + *offset = htonl((uint32_t) 0); + fprintf(stderr, "Warning: negative receive time in dat file transformed" + " to 0.\n"); + } + + // write packet to file + if (fwrite(temp_packet, sizeof(unsigned char), + ntohs(*((uint16_t*) temp_packet)), + out_file) != + ntohs(*((uint16_t*) temp_packet))) { + return -1; + } + } + } closing: - free(time_vec); - free(rtp_vec); - fclose(in_file); - fclose(dat_file); - fclose(out_file); + free(time_vec); + free(rtp_vec); + if (packet_ptr != NULL) { + free(packet_ptr); + } + fclose(in_file); + fclose(dat_file); + fclose(out_file); - return(0); + return(0); } int compare_arr_time(const void *xp, const void *yp) { - if(((arr_time *)xp)->time == ((arr_time *)yp)->time) - return(0); - else if(((arr_time *)xp)->time > ((arr_time *)yp)->time) - return(1); + if(((arr_time *)xp)->time == ((arr_time *)yp)->time) + return(0); + else if(((arr_time *)xp)->time > ((arr_time *)yp)->time) + return(1); - return(-1); + return(-1); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPtimeshift.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPtimeshift.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPtimeshift.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/test/RTPtimeshift.cc 2015-02-03 14:33:34.000000000 +0000 @@ -13,7 +13,7 @@ #include #include "NETEQTEST_RTPpacket.h" -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" /*********************/ /* Misc. definitions */ @@ -22,68 +22,78 @@ #define FIRSTLINELEN 40 -int main(int argc, char* argv[]) { - if (argc < 4 || argc > 6) { - printf( - "Usage: RTPtimeshift in.rtp out.rtp newStartTS " - "[newStartSN [newStartArrTime]]\n"); - exit(1); - } - - FILE *inFile = fopen(argv[1], "rb"); - if (!inFile) { - printf("Cannot open input file %s\n", argv[1]); - return (-1); - } - printf("Input RTP file: %s\n", argv[1]); - - FILE *outFile = fopen(argv[2], "wb"); - if (!outFile) { - printf("Cannot open output file %s\n", argv[2]); - return (-1); - } - printf("Output RTP file: %s\n\n", argv[2]); - - // read file header and write directly to output file - const unsigned int kRtpDumpHeaderSize = 4 + 4 + 4 + 2 + 2; - char firstline[FIRSTLINELEN]; - EXPECT_TRUE(fgets(firstline, FIRSTLINELEN, inFile) != NULL); - EXPECT_GT(fputs(firstline, outFile), 0); - EXPECT_EQ(kRtpDumpHeaderSize, - fread(firstline, 1, kRtpDumpHeaderSize, inFile)); - EXPECT_EQ(kRtpDumpHeaderSize, - fwrite(firstline, 1, kRtpDumpHeaderSize, outFile)); - NETEQTEST_RTPpacket packet; - int packLen = packet.readFromFile(inFile); - if (packLen < 0) { - exit(1); - } - - // get new start TS and start SeqNo from arguments - uint32_t TSdiff = atoi(argv[3]) - packet.timeStamp(); - uint16_t SNdiff = 0; - uint32_t ATdiff = 0; - if (argc > 4) { - SNdiff = atoi(argv[4]) - packet.sequenceNumber(); - if (argc > 5) { - ATdiff = atoi(argv[5]) - packet.time(); +int main(int argc, char* argv[]) +{ + if(argc < 4 || argc > 6) + { + printf("Usage: RTPtimeshift in.rtp out.rtp newStartTS [newStartSN [newStartArrTime]]\n"); + exit(1); } - } - while (packLen >= 0) { + FILE *inFile=fopen(argv[1],"rb"); + if (!inFile) + { + printf("Cannot open input file %s\n", argv[1]); + return(-1); + } + printf("Input RTP file: %s\n",argv[1]); + + FILE *outFile=fopen(argv[2],"wb"); + if (!outFile) + { + printf("Cannot open output file %s\n", argv[2]); + return(-1); + } + printf("Output RTP file: %s\n\n",argv[2]); - packet.setTimeStamp(packet.timeStamp() + TSdiff); - packet.setSequenceNumber(packet.sequenceNumber() + SNdiff); - packet.setTime(packet.time() + ATdiff); + // read file header and write directly to output file + const unsigned int kRtpDumpHeaderSize = 4 + 4 + 4 + 2 + 2; + char firstline[FIRSTLINELEN]; + EXPECT_TRUE(fgets(firstline, FIRSTLINELEN, inFile) != NULL); + EXPECT_GT(fputs(firstline, outFile), 0); + EXPECT_EQ(kRtpDumpHeaderSize, + fread(firstline, 1, kRtpDumpHeaderSize, inFile)); + EXPECT_EQ(kRtpDumpHeaderSize, + fwrite(firstline, 1, kRtpDumpHeaderSize, outFile)); + NETEQTEST_RTPpacket packet; + int packLen = packet.readFromFile(inFile); + if (packLen < 0) + { + exit(1); + } + + // get new start TS and start SeqNo from arguments + uint32_t TSdiff = atoi(argv[3]) - packet.timeStamp(); + uint16_t SNdiff = 0; + uint32_t ATdiff = 0; + if (argc > 4) + { + int startSN = atoi(argv[4]); + if (startSN >= 0) + SNdiff = startSN - packet.sequenceNumber(); + if (argc > 5) + { + int startTS = atoi(argv[5]); + if (startTS >= 0) + ATdiff = startTS - packet.time(); + } + } - packet.writeToFile(outFile); + while (packLen >= 0) + { - packLen = packet.readFromFile(inFile); + packet.setTimeStamp(packet.timeStamp() + TSdiff); + packet.setSequenceNumber(packet.sequenceNumber() + SNdiff); + packet.setTime(packet.time() + ATdiff); - } + packet.writeToFile(outFile); + + packLen = packet.readFromFile(inFile); + + } - fclose(inFile); - fclose(outFile); + fclose(inFile); + fclose(outFile); - return 0; + return 0; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/timestamp_scaler.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/timestamp_scaler.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/timestamp_scaler.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/timestamp_scaler.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/timestamp_scaler.h" + +#include "webrtc/modules/audio_coding/neteq/decoder_database.h" +#include "webrtc/modules/audio_coding/neteq/defines.h" +#include "webrtc/system_wrappers/interface/logging.h" + +namespace webrtc { + +void TimestampScaler::ToInternal(Packet* packet) { + if (!packet) { + return; + } + packet->header.timestamp = ToInternal(packet->header.timestamp, + packet->header.payloadType); +} + +void TimestampScaler::ToInternal(PacketList* packet_list) { + PacketList::iterator it; + for (it = packet_list->begin(); it != packet_list->end(); ++it) { + ToInternal(*it); + } +} + +uint32_t TimestampScaler::ToInternal(uint32_t external_timestamp, + uint8_t rtp_payload_type) { + const DecoderDatabase::DecoderInfo* info = + decoder_database_.GetDecoderInfo(rtp_payload_type); + if (!info) { + // Payload type is unknown. Do not scale. + return external_timestamp; + } + switch (info->codec_type) { + case kDecoderG722: + case kDecoderG722_2ch: { + // Use timestamp scaling with factor 2 (two output samples per RTP + // timestamp). + numerator_ = 2; + denominator_ = 1; + break; + } + case kDecoderISACfb: + case kDecoderCNGswb48kHz: { + // Use timestamp scaling with factor 2/3 (32 kHz sample rate, but RTP + // timestamps run on 48 kHz). + // TODO(tlegrand): Remove scaling for kDecoderCNGswb48kHz once ACM has + // full 48 kHz support. + numerator_ = 2; + denominator_ = 3; + } + case kDecoderAVT: + case kDecoderCNGnb: + case kDecoderCNGwb: + case kDecoderCNGswb32kHz: { + // Do not change the timestamp scaling settings for DTMF or CNG. + break; + } + default: { + // Do not use timestamp scaling for any other codec. + numerator_ = 1; + denominator_ = 1; + break; + } + } + + if (!(numerator_ == 1 && denominator_ == 1)) { + // We have a scale factor != 1. + if (!first_packet_received_) { + external_ref_ = external_timestamp; + internal_ref_ = external_timestamp; + first_packet_received_ = true; + } + int32_t external_diff = external_timestamp - external_ref_; + assert(denominator_ > 0); // Should not be possible. + external_ref_ = external_timestamp; + internal_ref_ += (external_diff * numerator_) / denominator_; + LOG(LS_VERBOSE) << "Converting timestamp: " << external_timestamp << + " -> " << internal_ref_; + return internal_ref_; + } else { + // No scaling. + return external_timestamp; + } +} + + +uint32_t TimestampScaler::ToExternal(uint32_t internal_timestamp) const { + if (!first_packet_received_ || (numerator_ == 1 && denominator_ == 1)) { + // Not initialized, or scale factor is 1. + return internal_timestamp; + } else { + int32_t internal_diff = internal_timestamp - internal_ref_; + assert(numerator_ > 0); // Should not be possible. + // Do not update references in this method. + // Switch |denominator_| and |numerator_| to convert the other way. + return external_ref_ + (internal_diff * denominator_) / numerator_; + } +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/timestamp_scaler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/timestamp_scaler.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/timestamp_scaler.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/timestamp_scaler.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TIMESTAMP_SCALER_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TIMESTAMP_SCALER_H_ + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/packet.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Forward declaration. +class DecoderDatabase; + +// This class scales timestamps for codecs that need timestamp scaling. +// This is done for codecs where one RTP timestamp does not correspond to +// one sample. +class TimestampScaler { + public: + explicit TimestampScaler(const DecoderDatabase& decoder_database) + : first_packet_received_(false), + numerator_(1), + denominator_(1), + external_ref_(0), + internal_ref_(0), + decoder_database_(decoder_database) {} + + virtual ~TimestampScaler() {} + + // Start over. + virtual void Reset() { first_packet_received_ = false; } + + // Scale the timestamp in |packet| from external to internal. + virtual void ToInternal(Packet* packet); + + // Scale the timestamp for all packets in |packet_list| from external to + // internal. + virtual void ToInternal(PacketList* packet_list); + + // Returns the internal equivalent of |external_timestamp|, given the + // RTP payload type |rtp_payload_type|. + virtual uint32_t ToInternal(uint32_t external_timestamp, + uint8_t rtp_payload_type); + + // Scales back to external timestamp. This is the inverse of ToInternal(). + virtual uint32_t ToExternal(uint32_t internal_timestamp) const; + + private: + bool first_packet_received_; + int numerator_; + int denominator_; + uint32_t external_ref_; + uint32_t internal_ref_; + const DecoderDatabase& decoder_database_; + + DISALLOW_COPY_AND_ASSIGN(TimestampScaler); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TIMESTAMP_SCALER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/timestamp_scaler_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/timestamp_scaler_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/timestamp_scaler_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/timestamp_scaler_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/timestamp_scaler.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/neteq/mock/mock_decoder_database.h" +#include "webrtc/modules/audio_coding/neteq/packet.h" + +using ::testing::Return; +using ::testing::ReturnNull; +using ::testing::_; + +namespace webrtc { + +TEST(TimestampScaler, TestNoScaling) { + MockDecoderDatabase db; + DecoderDatabase::DecoderInfo info; + info.codec_type = kDecoderPCMu; // Does not use scaled timestamps. + static const uint8_t kRtpPayloadType = 0; + EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) + .WillRepeatedly(Return(&info)); + + TimestampScaler scaler(db); + // Test both sides of the timestamp wrap-around. + for (uint32_t timestamp = 0xFFFFFFFF - 5; timestamp != 5; ++timestamp) { + // Scale to internal timestamp. + EXPECT_EQ(timestamp, scaler.ToInternal(timestamp, kRtpPayloadType)); + // Scale back. + EXPECT_EQ(timestamp, scaler.ToExternal(timestamp)); + } + + EXPECT_CALL(db, Die()); // Called when database object is deleted. +} + +TEST(TimestampScaler, TestNoScalingLargeStep) { + MockDecoderDatabase db; + DecoderDatabase::DecoderInfo info; + info.codec_type = kDecoderPCMu; // Does not use scaled timestamps. + static const uint8_t kRtpPayloadType = 0; + EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) + .WillRepeatedly(Return(&info)); + + TimestampScaler scaler(db); + // Test both sides of the timestamp wrap-around. + static const uint32_t kStep = 160; + uint32_t start_timestamp = 0; + // |external_timestamp| will be a large positive value. + start_timestamp = start_timestamp - 5 * kStep; + for (uint32_t timestamp = start_timestamp; timestamp != 5 * kStep; + timestamp += kStep) { + // Scale to internal timestamp. + EXPECT_EQ(timestamp, scaler.ToInternal(timestamp, kRtpPayloadType)); + // Scale back. + EXPECT_EQ(timestamp, scaler.ToExternal(timestamp)); + } + + EXPECT_CALL(db, Die()); // Called when database object is deleted. +} + +TEST(TimestampScaler, TestG722) { + MockDecoderDatabase db; + DecoderDatabase::DecoderInfo info; + info.codec_type = kDecoderG722; // Uses a factor 2 scaling. + static const uint8_t kRtpPayloadType = 17; + EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) + .WillRepeatedly(Return(&info)); + + TimestampScaler scaler(db); + // Test both sides of the timestamp wrap-around. + uint32_t external_timestamp = 0xFFFFFFFF - 5; + uint32_t internal_timestamp = external_timestamp; + for (; external_timestamp != 5; ++external_timestamp) { + // Scale to internal timestamp. + EXPECT_EQ(internal_timestamp, + scaler.ToInternal(external_timestamp, kRtpPayloadType)); + // Scale back. + EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); + internal_timestamp += 2; + } + + EXPECT_CALL(db, Die()); // Called when database object is deleted. +} + +TEST(TimestampScaler, TestG722LargeStep) { + MockDecoderDatabase db; + DecoderDatabase::DecoderInfo info; + info.codec_type = kDecoderG722; // Uses a factor 2 scaling. + static const uint8_t kRtpPayloadType = 17; + EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) + .WillRepeatedly(Return(&info)); + + TimestampScaler scaler(db); + // Test both sides of the timestamp wrap-around. + static const uint32_t kStep = 320; + uint32_t external_timestamp = 0; + // |external_timestamp| will be a large positive value. + external_timestamp = external_timestamp - 5 * kStep; + uint32_t internal_timestamp = external_timestamp; + for (; external_timestamp != 5 * kStep; external_timestamp += kStep) { + // Scale to internal timestamp. + EXPECT_EQ(internal_timestamp, + scaler.ToInternal(external_timestamp, kRtpPayloadType)); + // Scale back. + EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); + // Internal timestamp should be incremented with twice the step. + internal_timestamp += 2 * kStep; + } + + EXPECT_CALL(db, Die()); // Called when database object is deleted. +} + +TEST(TimestampScaler, TestG722WithCng) { + MockDecoderDatabase db; + DecoderDatabase::DecoderInfo info_g722, info_cng; + info_g722.codec_type = kDecoderG722; // Uses a factor 2 scaling. + info_cng.codec_type = kDecoderCNGwb; + static const uint8_t kRtpPayloadTypeG722 = 17; + static const uint8_t kRtpPayloadTypeCng = 13; + EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadTypeG722)) + .WillRepeatedly(Return(&info_g722)); + EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadTypeCng)) + .WillRepeatedly(Return(&info_cng)); + + TimestampScaler scaler(db); + // Test both sides of the timestamp wrap-around. + uint32_t external_timestamp = 0xFFFFFFFF - 5; + uint32_t internal_timestamp = external_timestamp; + bool next_is_cng = false; + for (; external_timestamp != 5; ++external_timestamp) { + // Alternate between G.722 and CNG every other packet. + if (next_is_cng) { + // Scale to internal timestamp. + EXPECT_EQ(internal_timestamp, + scaler.ToInternal(external_timestamp, kRtpPayloadTypeCng)); + next_is_cng = false; + } else { + // Scale to internal timestamp. + EXPECT_EQ(internal_timestamp, + scaler.ToInternal(external_timestamp, kRtpPayloadTypeG722)); + next_is_cng = true; + } + // Scale back. + EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); + internal_timestamp += 2; + } + + EXPECT_CALL(db, Die()); // Called when database object is deleted. +} + +// Make sure that the method ToInternal(Packet* packet) is wired up correctly. +// Since it is simply calling the other ToInternal method, we are not doing +// as many tests here. +TEST(TimestampScaler, TestG722Packet) { + MockDecoderDatabase db; + DecoderDatabase::DecoderInfo info; + info.codec_type = kDecoderG722; // Does uses a factor 2 scaling. + static const uint8_t kRtpPayloadType = 17; + EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) + .WillRepeatedly(Return(&info)); + + TimestampScaler scaler(db); + // Test both sides of the timestamp wrap-around. + uint32_t external_timestamp = 0xFFFFFFFF - 5; + uint32_t internal_timestamp = external_timestamp; + Packet packet; + packet.header.payloadType = kRtpPayloadType; + for (; external_timestamp != 5; ++external_timestamp) { + packet.header.timestamp = external_timestamp; + // Scale to internal timestamp. + scaler.ToInternal(&packet); + EXPECT_EQ(internal_timestamp, packet.header.timestamp); + internal_timestamp += 2; + } + + EXPECT_CALL(db, Die()); // Called when database object is deleted. +} + +// Make sure that the method ToInternal(PacketList* packet_list) is wired up +// correctly. Since it is simply calling the ToInternal(Packet* packet) method, +// we are not doing as many tests here. +TEST(TimestampScaler, TestG722PacketList) { + MockDecoderDatabase db; + DecoderDatabase::DecoderInfo info; + info.codec_type = kDecoderG722; // Uses a factor 2 scaling. + static const uint8_t kRtpPayloadType = 17; + EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) + .WillRepeatedly(Return(&info)); + + TimestampScaler scaler(db); + // Test both sides of the timestamp wrap-around. + uint32_t external_timestamp = 0xFFFFFFFF - 5; + uint32_t internal_timestamp = external_timestamp; + Packet packet1; + packet1.header.payloadType = kRtpPayloadType; + packet1.header.timestamp = external_timestamp; + Packet packet2; + packet2.header.payloadType = kRtpPayloadType; + packet2.header.timestamp = external_timestamp + 10; + PacketList packet_list; + packet_list.push_back(&packet1); + packet_list.push_back(&packet2); + + scaler.ToInternal(&packet_list); + EXPECT_EQ(internal_timestamp, packet1.header.timestamp); + EXPECT_EQ(internal_timestamp + 20, packet2.header.timestamp); + + EXPECT_CALL(db, Die()); // Called when database object is deleted. +} + +TEST(TimestampScaler, TestG722Reset) { + MockDecoderDatabase db; + DecoderDatabase::DecoderInfo info; + info.codec_type = kDecoderG722; // Uses a factor 2 scaling. + static const uint8_t kRtpPayloadType = 17; + EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) + .WillRepeatedly(Return(&info)); + + TimestampScaler scaler(db); + // Test both sides of the timestamp wrap-around. + uint32_t external_timestamp = 0xFFFFFFFF - 5; + uint32_t internal_timestamp = external_timestamp; + for (; external_timestamp != 5; ++external_timestamp) { + // Scale to internal timestamp. + EXPECT_EQ(internal_timestamp, + scaler.ToInternal(external_timestamp, kRtpPayloadType)); + // Scale back. + EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); + internal_timestamp += 2; + } + // Reset the scaler. After this, we expect the internal and external to start + // over at the same value again. + scaler.Reset(); + internal_timestamp = external_timestamp; + for (; external_timestamp != 15; ++external_timestamp) { + // Scale to internal timestamp. + EXPECT_EQ(internal_timestamp, + scaler.ToInternal(external_timestamp, kRtpPayloadType)); + // Scale back. + EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); + internal_timestamp += 2; + } + + EXPECT_CALL(db, Die()); // Called when database object is deleted. +} + +// TODO(minyue): This test becomes trivial since Opus does not need a timestamp +// scaler. Therefore, this test may be removed in future. There is no harm to +// keep it, since it can be taken as a test case for the situation of a trivial +// timestamp scaler. +TEST(TimestampScaler, TestOpusLargeStep) { + MockDecoderDatabase db; + DecoderDatabase::DecoderInfo info; + info.codec_type = kDecoderOpus; + static const uint8_t kRtpPayloadType = 17; + EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) + .WillRepeatedly(Return(&info)); + + TimestampScaler scaler(db); + // Test both sides of the timestamp wrap-around. + static const uint32_t kStep = 960; + uint32_t external_timestamp = 0; + // |external_timestamp| will be a large positive value. + external_timestamp = external_timestamp - 5 * kStep; + uint32_t internal_timestamp = external_timestamp; + for (; external_timestamp != 5 * kStep; external_timestamp += kStep) { + // Scale to internal timestamp. + EXPECT_EQ(internal_timestamp, + scaler.ToInternal(external_timestamp, kRtpPayloadType)); + // Scale back. + EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); + internal_timestamp += kStep; + } + + EXPECT_CALL(db, Die()); // Called when database object is deleted. +} + +TEST(TimestampScaler, TestIsacFbLargeStep) { + MockDecoderDatabase db; + DecoderDatabase::DecoderInfo info; + info.codec_type = kDecoderISACfb; + static const uint8_t kRtpPayloadType = 17; + EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) + .WillRepeatedly(Return(&info)); + + TimestampScaler scaler(db); + // Test both sides of the timestamp wrap-around. + static const uint32_t kStep = 960; + uint32_t external_timestamp = 0; + // |external_timestamp| will be a large positive value. + external_timestamp = external_timestamp - 5 * kStep; + uint32_t internal_timestamp = external_timestamp; + for (; external_timestamp != 5 * kStep; external_timestamp += kStep) { + // Scale to internal timestamp. + EXPECT_EQ(internal_timestamp, + scaler.ToInternal(external_timestamp, kRtpPayloadType)); + // Scale back. + EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); + // Internal timestamp should be incremented with two-thirds the step. + internal_timestamp += 2 * kStep / 3; + } + + EXPECT_CALL(db, Die()); // Called when database object is deleted. +} + +TEST(TimestampScaler, Failures) { + static const uint8_t kRtpPayloadType = 17; + MockDecoderDatabase db; + EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) + .WillOnce(ReturnNull()); // Return NULL to indicate unknown payload type. + + TimestampScaler scaler(db); + uint32_t timestamp = 4711; // Some number. + EXPECT_EQ(timestamp, scaler.ToInternal(timestamp, kRtpPayloadType)); + + Packet* packet = NULL; + scaler.ToInternal(packet); // Should not crash. That's all we can test. + + EXPECT_CALL(db, Die()); // Called when database object is deleted. +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/time_stretch.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/time_stretch.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/time_stretch.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/time_stretch.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/time_stretch.h" + +#include // min, max + +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/modules/audio_coding/neteq/background_noise.h" +#include "webrtc/modules/audio_coding/neteq/dsp_helper.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +TimeStretch::ReturnCodes TimeStretch::Process( + const int16_t* input, + size_t input_len, + AudioMultiVector* output, + int16_t* length_change_samples) { + + // Pre-calculate common multiplication with |fs_mult_|. + int fs_mult_120 = fs_mult_ * 120; // Corresponds to 15 ms. + + const int16_t* signal; + scoped_ptr signal_array; + size_t signal_len; + if (num_channels_ == 1) { + signal = input; + signal_len = input_len; + } else { + // We want |signal| to be only the first channel of |input|, which is + // interleaved. Thus, we take the first sample, skip forward |num_channels| + // samples, and continue like that. + signal_len = input_len / num_channels_; + signal_array.reset(new int16_t[signal_len]); + signal = signal_array.get(); + size_t j = master_channel_; + for (size_t i = 0; i < signal_len; ++i) { + signal_array[i] = input[j]; + j += num_channels_; + } + } + + // Find maximum absolute value of input signal. + max_input_value_ = WebRtcSpl_MaxAbsValueW16(signal, + static_cast(signal_len)); + + // Downsample to 4 kHz sample rate and calculate auto-correlation. + DspHelper::DownsampleTo4kHz(signal, signal_len, kDownsampledLen, + sample_rate_hz_, true /* compensate delay*/, + downsampled_input_); + AutoCorrelation(); + + // Find the strongest correlation peak. + static const int kNumPeaks = 1; + int peak_index; + int16_t peak_value; + DspHelper::PeakDetection(auto_correlation_, kCorrelationLen, kNumPeaks, + fs_mult_, &peak_index, &peak_value); + // Assert that |peak_index| stays within boundaries. + assert(peak_index >= 0); + assert(peak_index <= (2 * kCorrelationLen - 1) * fs_mult_); + + // Compensate peak_index for displaced starting position. The displacement + // happens in AutoCorrelation(). Here, |kMinLag| is in the down-sampled 4 kHz + // domain, while the |peak_index| is in the original sample rate; hence, the + // multiplication by fs_mult_ * 2. + peak_index += kMinLag * fs_mult_ * 2; + // Assert that |peak_index| stays within boundaries. + assert(peak_index >= 20 * fs_mult_); + assert(peak_index <= 20 * fs_mult_ + (2 * kCorrelationLen - 1) * fs_mult_); + + // Calculate scaling to ensure that |peak_index| samples can be square-summed + // without overflowing. + int scaling = 31 - WebRtcSpl_NormW32(max_input_value_ * max_input_value_) - + WebRtcSpl_NormW32(peak_index); + scaling = std::max(0, scaling); + + // |vec1| starts at 15 ms minus one pitch period. + const int16_t* vec1 = &signal[fs_mult_120 - peak_index]; + // |vec2| start at 15 ms. + const int16_t* vec2 = &signal[fs_mult_120]; + // Calculate energies for |vec1| and |vec2|, assuming they both contain + // |peak_index| samples. + int32_t vec1_energy = + WebRtcSpl_DotProductWithScale(vec1, vec1, peak_index, scaling); + int32_t vec2_energy = + WebRtcSpl_DotProductWithScale(vec2, vec2, peak_index, scaling); + + // Calculate cross-correlation between |vec1| and |vec2|. + int32_t cross_corr = + WebRtcSpl_DotProductWithScale(vec1, vec2, peak_index, scaling); + + // Check if the signal seems to be active speech or not (simple VAD). + bool active_speech = SpeechDetection(vec1_energy, vec2_energy, peak_index, + scaling); + + int16_t best_correlation; + if (!active_speech) { + SetParametersForPassiveSpeech(signal_len, &best_correlation, &peak_index); + } else { + // Calculate correlation: + // cross_corr / sqrt(vec1_energy * vec2_energy). + + // Start with calculating scale values. + int energy1_scale = std::max(0, 16 - WebRtcSpl_NormW32(vec1_energy)); + int energy2_scale = std::max(0, 16 - WebRtcSpl_NormW32(vec2_energy)); + + // Make sure total scaling is even (to simplify scale factor after sqrt). + if ((energy1_scale + energy2_scale) & 1) { + // The sum is odd. + energy1_scale += 1; + } + + // Scale energies to int16_t. + int16_t vec1_energy_int16 = + static_cast(vec1_energy >> energy1_scale); + int16_t vec2_energy_int16 = + static_cast(vec2_energy >> energy2_scale); + + // Calculate square-root of energy product. + int16_t sqrt_energy_prod = WebRtcSpl_SqrtFloor(vec1_energy_int16 * + vec2_energy_int16); + + // Calculate cross_corr / sqrt(en1*en2) in Q14. + int temp_scale = 14 - (energy1_scale + energy2_scale) / 2; + cross_corr = WEBRTC_SPL_SHIFT_W32(cross_corr, temp_scale); + cross_corr = std::max(0, cross_corr); // Don't use if negative. + best_correlation = WebRtcSpl_DivW32W16(cross_corr, sqrt_energy_prod); + // Make sure |best_correlation| is no larger than 1 in Q14. + best_correlation = std::min(static_cast(16384), best_correlation); + } + + + // Check accelerate criteria and stretch the signal. + ReturnCodes return_value = CheckCriteriaAndStretch( + input, input_len, peak_index, best_correlation, active_speech, output); + switch (return_value) { + case kSuccess: + *length_change_samples = peak_index; + break; + case kSuccessLowEnergy: + *length_change_samples = peak_index; + break; + case kNoStretch: + case kError: + *length_change_samples = 0; + break; + } + return return_value; +} + +void TimeStretch::AutoCorrelation() { + // Set scaling factor for cross correlation to protect against overflow. + int scaling = kLogCorrelationLen - WebRtcSpl_NormW32( + max_input_value_ * max_input_value_); + scaling = std::max(0, scaling); + + // Calculate correlation from lag kMinLag to lag kMaxLag in 4 kHz domain. + int32_t auto_corr[kCorrelationLen]; + WebRtcSpl_CrossCorrelation(auto_corr, &downsampled_input_[kMaxLag], + &downsampled_input_[kMaxLag - kMinLag], + kCorrelationLen, kMaxLag - kMinLag, scaling, -1); + + // Normalize correlation to 14 bits and write to |auto_correlation_|. + int32_t max_corr = WebRtcSpl_MaxAbsValueW32(auto_corr, kCorrelationLen); + scaling = std::max(0, 17 - WebRtcSpl_NormW32(max_corr)); + WebRtcSpl_VectorBitShiftW32ToW16(auto_correlation_, kCorrelationLen, + auto_corr, scaling); +} + +bool TimeStretch::SpeechDetection(int32_t vec1_energy, int32_t vec2_energy, + int peak_index, int scaling) const { + // Check if the signal seems to be active speech or not (simple VAD). + // If (vec1_energy + vec2_energy) / (2 * peak_index) <= + // 8 * background_noise_energy, then we say that the signal contains no + // active speech. + // Rewrite the inequality as: + // (vec1_energy + vec2_energy) / 16 <= peak_index * background_noise_energy. + // The two sides of the inequality will be denoted |left_side| and + // |right_side|. + int32_t left_side = (vec1_energy + vec2_energy) / 16; + int32_t right_side; + if (background_noise_.initialized()) { + right_side = background_noise_.Energy(master_channel_); + } else { + // If noise parameters have not been estimated, use a fixed threshold. + right_side = 75000; + } + int right_scale = 16 - WebRtcSpl_NormW32(right_side); + right_scale = std::max(0, right_scale); + left_side = left_side >> right_scale; + right_side = peak_index * (right_side >> right_scale); + + // Scale |left_side| properly before comparing with |right_side|. + // (|scaling| is the scale factor before energy calculation, thus the scale + // factor for the energy is 2 * scaling.) + if (WebRtcSpl_NormW32(left_side) < 2 * scaling) { + // Cannot scale only |left_side|, must scale |right_side| too. + int temp_scale = WebRtcSpl_NormW32(left_side); + left_side = left_side << temp_scale; + right_side = right_side >> (2 * scaling - temp_scale); + } else { + left_side = left_side << 2 * scaling; + } + return left_side > right_side; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/time_stretch.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/time_stretch.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/time_stretch.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/time_stretch.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TIME_STRETCH_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TIME_STRETCH_H_ + +#include +#include // memset, size_t + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Forward declarations. +class BackgroundNoise; + +// This is the base class for Accelerate and PreemptiveExpand. This class +// cannot be instantiated, but must be used through either of the derived +// classes. +class TimeStretch { + public: + enum ReturnCodes { + kSuccess = 0, + kSuccessLowEnergy = 1, + kNoStretch = 2, + kError = -1 + }; + + TimeStretch(int sample_rate_hz, size_t num_channels, + const BackgroundNoise& background_noise) + : sample_rate_hz_(sample_rate_hz), + fs_mult_(sample_rate_hz / 8000), + num_channels_(static_cast(num_channels)), + master_channel_(0), // First channel is master. + background_noise_(background_noise), + max_input_value_(0) { + assert(sample_rate_hz_ == 8000 || + sample_rate_hz_ == 16000 || + sample_rate_hz_ == 32000 || + sample_rate_hz_ == 48000); + assert(num_channels_ > 0); + assert(static_cast(master_channel_) < num_channels_); + memset(auto_correlation_, 0, sizeof(auto_correlation_)); + } + + virtual ~TimeStretch() {} + + // This method performs the processing common to both Accelerate and + // PreemptiveExpand. + ReturnCodes Process(const int16_t* input, + size_t input_len, + AudioMultiVector* output, + int16_t* length_change_samples); + + protected: + // Sets the parameters |best_correlation| and |peak_index| to suitable + // values when the signal contains no active speech. This method must be + // implemented by the sub-classes. + virtual void SetParametersForPassiveSpeech(size_t input_length, + int16_t* best_correlation, + int* peak_index) const = 0; + + // Checks the criteria for performing the time-stretching operation and, + // if possible, performs the time-stretching. This method must be implemented + // by the sub-classes. + virtual ReturnCodes CheckCriteriaAndStretch( + const int16_t* input, size_t input_length, size_t peak_index, + int16_t best_correlation, bool active_speech, + AudioMultiVector* output) const = 0; + + static const int kCorrelationLen = 50; + static const int kLogCorrelationLen = 6; // >= log2(kCorrelationLen). + static const int kMinLag = 10; + static const int kMaxLag = 60; + static const int kDownsampledLen = kCorrelationLen + kMaxLag; + static const int kCorrelationThreshold = 14746; // 0.9 in Q14. + + const int sample_rate_hz_; + const int fs_mult_; // Sample rate multiplier = sample_rate_hz_ / 8000. + const int num_channels_; + const size_t master_channel_; + const BackgroundNoise& background_noise_; + int16_t max_input_value_; + int16_t downsampled_input_[kDownsampledLen]; + // Adding 1 to the size of |auto_correlation_| because of how it is used + // by the peak-detection algorithm. + int16_t auto_correlation_[kCorrelationLen + 1]; + + private: + // Calculates the auto-correlation of |downsampled_input_| and writes the + // result to |auto_correlation_|. + void AutoCorrelation(); + + // Performs a simple voice-activity detection based on the input parameters. + bool SpeechDetection(int32_t vec1_energy, int32_t vec2_energy, + int peak_index, int scaling) const; + + DISALLOW_COPY_AND_ASSIGN(TimeStretch); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TIME_STRETCH_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/time_stretch_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/time_stretch_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/time_stretch_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/time_stretch_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for Accelerate and PreemptiveExpand classes. + +#include "webrtc/modules/audio_coding/neteq/accelerate.h" +#include "webrtc/modules/audio_coding/neteq/preemptive_expand.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/neteq/background_noise.h" + +namespace webrtc { + +TEST(TimeStretch, CreateAndDestroy) { + const int kSampleRate = 8000; + const size_t kNumChannels = 1; + const int kOverlapSamples = 5 * kSampleRate / 8000; + BackgroundNoise bgn(kNumChannels); + Accelerate accelerate(kSampleRate, kNumChannels, bgn); + PreemptiveExpand preemptive_expand( + kSampleRate, kNumChannels, bgn, kOverlapSamples); +} + +TEST(TimeStretch, CreateUsingFactory) { + const int kSampleRate = 8000; + const size_t kNumChannels = 1; + const int kOverlapSamples = 5 * kSampleRate / 8000; + BackgroundNoise bgn(kNumChannels); + + AccelerateFactory accelerate_factory; + Accelerate* accelerate = + accelerate_factory.Create(kSampleRate, kNumChannels, bgn); + EXPECT_TRUE(accelerate != NULL); + delete accelerate; + + PreemptiveExpandFactory preemptive_expand_factory; + PreemptiveExpand* preemptive_expand = preemptive_expand_factory.Create( + kSampleRate, kNumChannels, bgn, kOverlapSamples); + EXPECT_TRUE(preemptive_expand != NULL); + delete preemptive_expand; +} + +// TODO(hlundin): Write more tests. + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/audio_checksum.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/audio_checksum.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/audio_checksum.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/audio_checksum.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_AUDIO_CHECKSUM_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_AUDIO_CHECKSUM_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/md5digest.h" +#include "webrtc/base/stringencode.h" +#include "webrtc/modules/audio_coding/neteq/tools/audio_sink.h" +#include "webrtc/system_wrappers/interface/compile_assert.h" +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace test { + +class AudioChecksum : public AudioSink { + public: + AudioChecksum() : finished_(false) {} + + virtual bool WriteArray(const int16_t* audio, size_t num_samples) OVERRIDE { + if (finished_) + return false; + +#ifndef WEBRTC_ARCH_LITTLE_ENDIAN +#error "Big-endian gives a different checksum" +#endif + checksum_.Update(audio, num_samples * sizeof(*audio)); + return true; + } + + // Finalizes the computations, and returns the checksum. + std::string Finish() { + if (!finished_) { + finished_ = true; + checksum_.Finish(checksum_result_, rtc::Md5Digest::kSize); + } + return rtc::hex_encode(checksum_result_, rtc::Md5Digest::kSize); + } + + private: + rtc::Md5Digest checksum_; + char checksum_result_[rtc::Md5Digest::kSize]; + bool finished_; + + DISALLOW_COPY_AND_ASSIGN(AudioChecksum); +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_AUDIO_CHECKSUM_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/audio_loop.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/audio_loop.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/audio_loop.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/audio_loop.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/tools/audio_loop.h" + +#include +#include +#include + +namespace webrtc { +namespace test { + +bool AudioLoop::Init(const std::string file_name, + size_t max_loop_length_samples, + size_t block_length_samples) { + FILE* fp = fopen(file_name.c_str(), "rb"); + if (!fp) return false; + + audio_array_.reset(new int16_t[max_loop_length_samples + + block_length_samples]); + size_t samples_read = fread(audio_array_.get(), sizeof(int16_t), + max_loop_length_samples, fp); + fclose(fp); + + // Block length must be shorter than the loop length. + if (block_length_samples > samples_read) return false; + + // Add an extra block length of samples to the end of the array, starting + // over again from the beginning of the array. This is done to simplify + // the reading process when reading over the end of the loop. + memcpy(&audio_array_[samples_read], audio_array_.get(), + block_length_samples * sizeof(int16_t)); + + loop_length_samples_ = samples_read; + block_length_samples_ = block_length_samples; + return true; +} + +const int16_t* AudioLoop::GetNextBlock() { + // Check that the AudioLoop is initialized. + if (block_length_samples_ == 0) return NULL; + + const int16_t* output_ptr = &audio_array_[next_index_]; + next_index_ = (next_index_ + block_length_samples_) % loop_length_samples_; + return output_ptr; +} + + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/audio_loop.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/audio_loop.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/audio_loop.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/audio_loop.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_AUDIO_LOOP_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_AUDIO_LOOP_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace test { + +// Class serving as an infinite source of audio, realized by looping an audio +// clip. +class AudioLoop { + public: + AudioLoop() + : next_index_(0), + loop_length_samples_(0), + block_length_samples_(0) { + } + + virtual ~AudioLoop() {} + + // Initializes the AudioLoop by reading from |file_name|. The loop will be no + // longer than |max_loop_length_samples|, if the length of the file is + // greater. Otherwise, the loop length is the same as the file length. + // The audio will be delivered in blocks of |block_length_samples|. + // Returns false if the initialization failed, otherwise true. + bool Init(const std::string file_name, size_t max_loop_length_samples, + size_t block_length_samples); + + // Returns a pointer to the next block of audio. The number given as + // |block_length_samples| to the Init() function determines how many samples + // that can be safely read from the pointer. + const int16_t* GetNextBlock(); + + private: + size_t next_index_; + size_t loop_length_samples_; + size_t block_length_samples_; + scoped_ptr audio_array_; + + DISALLOW_COPY_AND_ASSIGN(AudioLoop); +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_AUDIO_LOOP_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/audio_sink.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/audio_sink.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/audio_sink.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/audio_sink.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_AUDIO_SINK_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_AUDIO_SINK_H_ + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace test { + +// Interface class for an object receiving raw output audio from test +// applications. +class AudioSink { + public: + AudioSink() {} + virtual ~AudioSink() {} + + // Writes |num_samples| from |audio| to the AudioSink. Returns true if + // successful, otherwise false. + virtual bool WriteArray(const int16_t* audio, size_t num_samples) = 0; + + // Writes |audio_frame| to the AudioSink. Returns true if successful, + // otherwise false. + bool WriteAudioFrame(const AudioFrame& audio_frame) { + return WriteArray( + audio_frame.data_, + audio_frame.samples_per_channel_ * audio_frame.num_channels_); + } + + private: + DISALLOW_COPY_AND_ASSIGN(AudioSink); +}; + +// Forks the output audio to two AudioSink objects. +class AudioSinkFork : public AudioSink { + public: + AudioSinkFork(AudioSink* left, AudioSink* right) + : left_sink_(left), right_sink_(right) {} + + virtual bool WriteArray(const int16_t* audio, size_t num_samples) OVERRIDE { + return left_sink_->WriteArray(audio, num_samples) && + right_sink_->WriteArray(audio, num_samples); + } + + private: + AudioSink* left_sink_; + AudioSink* right_sink_; + + DISALLOW_COPY_AND_ASSIGN(AudioSinkFork); +}; +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_AUDIO_SINK_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h" + +#include + +#include "webrtc/base/checks.h" +#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet.h" + +namespace webrtc { +namespace test { + +ConstantPcmPacketSource::ConstantPcmPacketSource(size_t payload_len_samples, + int16_t sample_value, + int sample_rate_hz, + int payload_type) + : payload_len_samples_(payload_len_samples), + packet_len_bytes_(2 * payload_len_samples_ + kHeaderLenBytes), + samples_per_ms_(sample_rate_hz / 1000), + next_arrival_time_ms_(0.0), + payload_type_(payload_type), + seq_number_(0), + timestamp_(0), + payload_ssrc_(0xABCD1234) { + int encoded_len = WebRtcPcm16b_EncodeW16(&sample_value, 1, &encoded_sample_); + CHECK_EQ(encoded_len, 2); +} + +Packet* ConstantPcmPacketSource::NextPacket() { + CHECK_GT(packet_len_bytes_, kHeaderLenBytes); + uint8_t* packet_memory = new uint8_t[packet_len_bytes_]; + // Fill the payload part of the packet memory with the pre-encoded value. + std::fill_n(reinterpret_cast(packet_memory + kHeaderLenBytes), + payload_len_samples_, + encoded_sample_); + WriteHeader(packet_memory); + // |packet| assumes ownership of |packet_memory|. + Packet* packet = + new Packet(packet_memory, packet_len_bytes_, next_arrival_time_ms_); + next_arrival_time_ms_ += payload_len_samples_ / samples_per_ms_; + return packet; +} + +void ConstantPcmPacketSource::WriteHeader(uint8_t* packet_memory) { + packet_memory[0] = 0x80; + packet_memory[1] = payload_type_ & 0xFF; + packet_memory[2] = (seq_number_ >> 8) & 0xFF; + packet_memory[3] = seq_number_ & 0xFF; + packet_memory[4] = (timestamp_ >> 24) & 0xFF; + packet_memory[5] = (timestamp_ >> 16) & 0xFF; + packet_memory[6] = (timestamp_ >> 8) & 0xFF; + packet_memory[7] = timestamp_ & 0xFF; + packet_memory[8] = (payload_ssrc_ >> 24) & 0xFF; + packet_memory[9] = (payload_ssrc_ >> 16) & 0xFF; + packet_memory[10] = (payload_ssrc_ >> 8) & 0xFF; + packet_memory[11] = payload_ssrc_ & 0xFF; + ++seq_number_; + timestamp_ += static_cast(payload_len_samples_); +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_CONSTANT_PCM_PACKET_SOURCE_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_CONSTANT_PCM_PACKET_SOURCE_H_ + +#include +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/common_types.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet_source.h" + +namespace webrtc { +namespace test { + +// This class implements a packet source that delivers PCM16b encoded packets +// with a constant sample value. The payload length, constant sample value, +// sample rate, and payload type are all set in the constructor. +class ConstantPcmPacketSource : public PacketSource { + public: + ConstantPcmPacketSource(size_t payload_len_samples, + int16_t sample_value, + int sample_rate_hz, + int payload_type); + + // Returns a pointer to the next packet. Will never return NULL. That is, + // the source is infinite. + Packet* NextPacket() OVERRIDE; + + private: + void WriteHeader(uint8_t* packet_memory); + + const size_t kHeaderLenBytes = 12; + const size_t payload_len_samples_; + const size_t packet_len_bytes_; + int16_t encoded_sample_; + const int samples_per_ms_; + double next_arrival_time_ms_; + const int payload_type_; + uint16_t seq_number_; + uint32_t timestamp_; + const uint32_t payload_ssrc_; + + DISALLOW_COPY_AND_ASSIGN(ConstantPcmPacketSource); +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_CONSTANT_PCM_PACKET_SOURCE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/input_audio_file.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/input_audio_file.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/input_audio_file.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/input_audio_file.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" + +namespace webrtc { +namespace test { + +InputAudioFile::InputAudioFile(const std::string file_name) { + fp_ = fopen(file_name.c_str(), "rb"); +} + +InputAudioFile::~InputAudioFile() { fclose(fp_); } + +bool InputAudioFile::Read(size_t samples, int16_t* destination) { + if (!fp_) { + return false; + } + size_t samples_read = fread(destination, sizeof(int16_t), samples, fp_); + if (samples_read < samples) { + // Rewind and read the missing samples. + rewind(fp_); + size_t missing_samples = samples - samples_read; + if (fread(destination, sizeof(int16_t), missing_samples, fp_) < + missing_samples) { + // Could not read enough even after rewinding the file. + return false; + } + } + return true; +} + +void InputAudioFile::DuplicateInterleaved(const int16_t* source, size_t samples, + size_t channels, + int16_t* destination) { + // Start from the end of |source| and |destination|, and work towards the + // beginning. This is to allow in-place interleaving of the same array (i.e., + // |source| and |destination| are the same array). + for (int i = static_cast(samples - 1); i >= 0; --i) { + for (int j = static_cast(channels - 1); j >= 0; --j) { + destination[i * channels + j] = source[i]; + } + } +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/input_audio_file.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/input_audio_file.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/input_audio_file.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/input_audio_file.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_INPUT_AUDIO_FILE_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_INPUT_AUDIO_FILE_H_ + +#include + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace test { + +// Class for handling a looping input audio file. +class InputAudioFile { + public: + explicit InputAudioFile(const std::string file_name); + + virtual ~InputAudioFile(); + + // Reads |samples| elements from source file to |destination|. Returns true + // if the read was successful, otherwise false. If the file end is reached, + // the file is rewound and reading continues from the beginning. + // The output |destination| must have the capacity to hold |samples| elements. + bool Read(size_t samples, int16_t* destination); + + // Creates a multi-channel signal from a mono signal. Each sample is repeated + // |channels| times to create an interleaved multi-channel signal where all + // channels are identical. The output |destination| must have the capacity to + // hold samples * channels elements. Note that |source| and |destination| can + // be the same array (i.e., point to the same address). + static void DuplicateInterleaved(const int16_t* source, size_t samples, + size_t channels, int16_t* destination); + + private: + FILE* fp_; + DISALLOW_COPY_AND_ASSIGN(InputAudioFile); +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_INPUT_AUDIO_FILE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/input_audio_file_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/input_audio_file_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/input_audio_file_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/input_audio_file_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for test InputAudioFile class. + +#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace webrtc { +namespace test { + +TEST(TestInputAudioFile, DuplicateInterleaveSeparateSrcDst) { + static const size_t kSamples = 10; + static const size_t kChannels = 2; + int16_t input[kSamples]; + for (size_t i = 0; i < kSamples; ++i) { + input[i] = i; + } + int16_t output[kSamples * kChannels]; + InputAudioFile::DuplicateInterleaved(input, kSamples, kChannels, output); + + // Verify output + int16_t* output_ptr = output; + for (size_t i = 0; i < kSamples; ++i) { + for (size_t j = 0; j < kChannels; ++j) { + EXPECT_EQ(static_cast(i), *output_ptr++); + } + } +} + +TEST(TestInputAudioFile, DuplicateInterleaveSameSrcDst) { + static const size_t kSamples = 10; + static const size_t kChannels = 5; + int16_t input[kSamples * kChannels]; + for (size_t i = 0; i < kSamples; ++i) { + input[i] = i; + } + InputAudioFile::DuplicateInterleaved(input, kSamples, kChannels, input); + + // Verify output + int16_t* output_ptr = input; + for (size_t i = 0; i < kSamples; ++i) { + for (size_t j = 0; j < kChannels; ++j) { + EXPECT_EQ(static_cast(i), *output_ptr++); + } + } +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_performance_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_performance_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_performance_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_performance_test.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/tools/neteq_performance_test.h" + +#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" +#include "webrtc/modules/audio_coding/neteq/tools/audio_loop.h" +#include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/test/testsupport/fileutils.h" +#include "webrtc/typedefs.h" + +using webrtc::NetEq; +using webrtc::test::AudioLoop; +using webrtc::test::RtpGenerator; +using webrtc::WebRtcRTPHeader; + +namespace webrtc { +namespace test { + +int64_t NetEqPerformanceTest::Run(int runtime_ms, + int lossrate, + double drift_factor) { + const std::string kInputFileName = + webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); + const int kSampRateHz = 32000; + const webrtc::NetEqDecoder kDecoderType = webrtc::kDecoderPCM16Bswb32kHz; + const int kPayloadType = 95; + + // Initialize NetEq instance. + NetEq::Config config; + config.sample_rate_hz = kSampRateHz; + NetEq* neteq = NetEq::Create(config); + // Register decoder in |neteq|. + if (neteq->RegisterPayloadType(kDecoderType, kPayloadType) != 0) + return -1; + + // Set up AudioLoop object. + AudioLoop audio_loop; + const size_t kMaxLoopLengthSamples = kSampRateHz * 10; // 10 second loop. + const size_t kInputBlockSizeSamples = 60 * kSampRateHz / 1000; // 60 ms. + if (!audio_loop.Init(kInputFileName, kMaxLoopLengthSamples, + kInputBlockSizeSamples)) + return -1; + + int32_t time_now_ms = 0; + + // Get first input packet. + WebRtcRTPHeader rtp_header; + RtpGenerator rtp_gen(kSampRateHz / 1000); + // Start with positive drift first half of simulation. + rtp_gen.set_drift_factor(drift_factor); + bool drift_flipped = false; + int32_t packet_input_time_ms = + rtp_gen.GetRtpHeader(kPayloadType, kInputBlockSizeSamples, &rtp_header); + const int16_t* input_samples = audio_loop.GetNextBlock(); + if (!input_samples) exit(1); + uint8_t input_payload[kInputBlockSizeSamples * sizeof(int16_t)]; + int payload_len = WebRtcPcm16b_Encode(const_cast(input_samples), + kInputBlockSizeSamples, + input_payload); + assert(payload_len == kInputBlockSizeSamples * sizeof(int16_t)); + + // Main loop. + webrtc::Clock* clock = webrtc::Clock::GetRealTimeClock(); + int64_t start_time_ms = clock->TimeInMilliseconds(); + while (time_now_ms < runtime_ms) { + while (packet_input_time_ms <= time_now_ms) { + // Drop every N packets, where N = FLAGS_lossrate. + bool lost = false; + if (lossrate > 0) { + lost = ((rtp_header.header.sequenceNumber - 1) % lossrate) == 0; + } + if (!lost) { + // Insert packet. + int error = neteq->InsertPacket( + rtp_header, input_payload, payload_len, + packet_input_time_ms * kSampRateHz / 1000); + if (error != NetEq::kOK) + return -1; + } + + // Get next packet. + packet_input_time_ms = rtp_gen.GetRtpHeader(kPayloadType, + kInputBlockSizeSamples, + &rtp_header); + input_samples = audio_loop.GetNextBlock(); + if (!input_samples) return -1; + payload_len = WebRtcPcm16b_Encode(const_cast(input_samples), + kInputBlockSizeSamples, + input_payload); + assert(payload_len == kInputBlockSizeSamples * sizeof(int16_t)); + } + + // Get output audio, but don't do anything with it. + static const int kMaxChannels = 1; + static const int kMaxSamplesPerMs = 48000 / 1000; + static const int kOutputBlockSizeMs = 10; + static const int kOutDataLen = kOutputBlockSizeMs * kMaxSamplesPerMs * + kMaxChannels; + int16_t out_data[kOutDataLen]; + int num_channels; + int samples_per_channel; + int error = neteq->GetAudio(kOutDataLen, out_data, &samples_per_channel, + &num_channels, NULL); + if (error != NetEq::kOK) + return -1; + + assert(samples_per_channel == kSampRateHz * 10 / 1000); + + time_now_ms += kOutputBlockSizeMs; + if (time_now_ms >= runtime_ms / 2 && !drift_flipped) { + // Apply negative drift second half of simulation. + rtp_gen.set_drift_factor(-drift_factor); + drift_flipped = true; + } + } + int64_t end_time_ms = clock->TimeInMilliseconds(); + delete neteq; + return end_time_ms - start_time_ms; +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_performance_test.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_performance_test.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_performance_test.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_performance_test.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_PERFORMANCE_TEST_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_PERFORMANCE_TEST_H_ + +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace test { + +class NetEqPerformanceTest { + public: + // Runs a performance test with parameters as follows: + // |runtime_ms|: the simulation time, i.e., the duration of the audio data. + // |lossrate|: drop one out of |lossrate| packets, e.g., one out of 10. + // |drift_factor|: clock drift in [0, 1]. + // Returns the runtime in ms. + static int64_t Run(int runtime_ms, int lossrate, double drift_factor); +}; + +} // namespace test +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_PERFORMANCE_TEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include +#include "webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h" + +namespace webrtc { +namespace test { + +const uint8_t kPayloadType = 95; +const int kOutputSizeMs = 10; +const int kInitSeed = 0x12345678; +const int kPacketLossTimeUnitMs = 10; + +// Define switch for packet loss rate. +static bool ValidatePacketLossRate(const char* /* flag_name */, int32_t value) { + if (value >= 0 && value <= 100) + return true; + printf("Invalid packet loss percentile, should be between 0 and 100."); + return false; +} + +DEFINE_int32(packet_loss_rate, 10, "Percentile of packet loss."); + +static const bool packet_loss_rate_dummy = + RegisterFlagValidator(&FLAGS_packet_loss_rate, &ValidatePacketLossRate); + +// Define switch for random loss mode. +static bool ValidateRandomLossMode(const char* /* flag_name */, int32_t value) { + if (value >= 0 && value <= 2) + return true; + printf("Invalid random packet loss mode, should be between 0 and 2."); + return false; +} + +DEFINE_int32(random_loss_mode, 1, + "Random loss mode: 0--no loss, 1--uniform loss, 2--Gilbert Elliot loss."); +static const bool random_loss_mode_dummy = + RegisterFlagValidator(&FLAGS_random_loss_mode, &ValidateRandomLossMode); + +// Define switch for burst length. +static bool ValidateBurstLength(const char* /* flag_name */, int32_t value) { + if (value >= kPacketLossTimeUnitMs) + return true; + printf("Invalid burst length, should be greater than %d ms.", + kPacketLossTimeUnitMs); + return false; +} + +DEFINE_int32(burst_length, 30, + "Burst length in milliseconds, only valid for Gilbert Elliot loss."); + +static const bool burst_length_dummy = + RegisterFlagValidator(&FLAGS_burst_length, &ValidateBurstLength); + +// Define switch for drift factor. +static bool ValidateDriftFactor(const char* /* flag_name */, double value) { + if (value > -0.1) + return true; + printf("Invalid drift factor, should be greater than -0.1."); + return false; +} + +DEFINE_double(drift_factor, 0.0, "Time drift factor."); + +static const bool drift_factor_dummy = + RegisterFlagValidator(&FLAGS_drift_factor, &ValidateDriftFactor); + +// ProbTrans00Solver() is to calculate the transition probability from no-loss +// state to itself in a modified Gilbert Elliot packet loss model. The result is +// to achieve the target packet loss rate |loss_rate|, when a packet is not +// lost only if all |units| drawings within the duration of the packet result in +// no-loss. +static double ProbTrans00Solver(int units, double loss_rate, + double prob_trans_10) { + if (units == 1) + return prob_trans_10 / (1.0f - loss_rate) - prob_trans_10; +// 0 == prob_trans_00 ^ (units - 1) + (1 - loss_rate) / prob_trans_10 * +// prob_trans_00 - (1 - loss_rate) * (1 + 1 / prob_trans_10). +// There is a unique solution between 0.0 and 1.0, due to the monotonicity and +// an opposite sign at 0.0 and 1.0. +// For simplicity, we reformulate the equation as +// f(x) = x ^ (units - 1) + a x + b. +// Its derivative is +// f'(x) = (units - 1) x ^ (units - 2) + a. +// The derivative is strictly greater than 0 when x is between 0 and 1. +// We use Newton's method to solve the equation, iteration is +// x(k+1) = x(k) - f(x) / f'(x); + const double kPrecision = 0.001f; + const int kIterations = 100; + const double a = (1.0f - loss_rate) / prob_trans_10; + const double b = (loss_rate - 1.0f) * (1.0f + 1.0f / prob_trans_10); + double x = 0.0f; // Starting point; + double f = b; + double f_p; + int iter = 0; + while ((f >= kPrecision || f <= -kPrecision) && iter < kIterations) { + f_p = (units - 1.0f) * pow(x, units - 2) + a; + x -= f / f_p; + if (x > 1.0f) { + x = 1.0f; + } else if (x < 0.0f) { + x = 0.0f; + } + f = pow(x, units - 1) + a * x + b; + iter ++; + } + return x; +} + +NetEqQualityTest::NetEqQualityTest(int block_duration_ms, + int in_sampling_khz, + int out_sampling_khz, + enum NetEqDecoder decoder_type, + int channels, + std::string in_filename, + std::string out_filename) + : decoded_time_ms_(0), + decodable_time_ms_(0), + drift_factor_(FLAGS_drift_factor), + packet_loss_rate_(FLAGS_packet_loss_rate), + block_duration_ms_(block_duration_ms), + in_sampling_khz_(in_sampling_khz), + out_sampling_khz_(out_sampling_khz), + decoder_type_(decoder_type), + channels_(channels), + in_filename_(in_filename), + out_filename_(out_filename), + log_filename_(out_filename + ".log"), + in_size_samples_(in_sampling_khz_ * block_duration_ms_), + out_size_samples_(out_sampling_khz_ * kOutputSizeMs), + payload_size_bytes_(0), + max_payload_bytes_(0), + in_file_(new InputAudioFile(in_filename_)), + out_file_(NULL), + log_file_(NULL), + rtp_generator_(new RtpGenerator(in_sampling_khz_, 0, 0, + decodable_time_ms_)), + total_payload_size_bytes_(0) { + NetEq::Config config; + config.sample_rate_hz = out_sampling_khz_ * 1000; + neteq_.reset(NetEq::Create(config)); + max_payload_bytes_ = in_size_samples_ * channels_ * sizeof(int16_t); + in_data_.reset(new int16_t[in_size_samples_ * channels_]); + payload_.reset(new uint8_t[max_payload_bytes_]); + out_data_.reset(new int16_t[out_size_samples_ * channels_]); +} + +bool NoLoss::Lost() { + return false; +} + +UniformLoss::UniformLoss(double loss_rate) + : loss_rate_(loss_rate) { +} + +bool UniformLoss::Lost() { + int drop_this = rand(); + return (drop_this < loss_rate_ * RAND_MAX); +} + +GilbertElliotLoss::GilbertElliotLoss(double prob_trans_11, double prob_trans_01) + : prob_trans_11_(prob_trans_11), + prob_trans_01_(prob_trans_01), + lost_last_(false), + uniform_loss_model_(new UniformLoss(0)) { +} + +bool GilbertElliotLoss::Lost() { + // Simulate bursty channel (Gilbert model). + // (1st order) Markov chain model with memory of the previous/last + // packet state (lost or received). + if (lost_last_) { + // Previous packet was not received. + uniform_loss_model_->set_loss_rate(prob_trans_11_); + return lost_last_ = uniform_loss_model_->Lost(); + } else { + uniform_loss_model_->set_loss_rate(prob_trans_01_); + return lost_last_ = uniform_loss_model_->Lost(); + } +} + +void NetEqQualityTest::SetUp() { + out_file_ = fopen(out_filename_.c_str(), "wb"); + log_file_ = fopen(log_filename_.c_str(), "wt"); + ASSERT_TRUE(out_file_ != NULL); + ASSERT_EQ(0, neteq_->RegisterPayloadType(decoder_type_, kPayloadType)); + rtp_generator_->set_drift_factor(drift_factor_); + + int units = block_duration_ms_ / kPacketLossTimeUnitMs; + switch (FLAGS_random_loss_mode) { + case 1: { + // |unit_loss_rate| is the packet loss rate for each unit time interval + // (kPacketLossTimeUnitMs). Since a packet loss event is generated if any + // of |block_duration_ms_ / kPacketLossTimeUnitMs| unit time intervals of + // a full packet duration is drawn with a loss, |unit_loss_rate| fulfills + // (1 - unit_loss_rate) ^ (block_duration_ms_ / kPacketLossTimeUnitMs) == + // 1 - packet_loss_rate. + double unit_loss_rate = (1.0f - pow(1.0f - 0.01f * packet_loss_rate_, + 1.0f / units)); + loss_model_.reset(new UniformLoss(unit_loss_rate)); + break; + } + case 2: { + // |FLAGS_burst_length| should be integer times of kPacketLossTimeUnitMs. + ASSERT_EQ(0, FLAGS_burst_length % kPacketLossTimeUnitMs); + + // We do not allow 100 percent packet loss in Gilbert Elliot model, which + // makes no sense. + ASSERT_GT(100, packet_loss_rate_); + + // To guarantee the overall packet loss rate, transition probabilities + // need to satisfy: + // pi_0 * (1 - prob_trans_01_) ^ units + + // pi_1 * prob_trans_10_ ^ (units - 1) == 1 - loss_rate + // pi_0 = prob_trans_10 / (prob_trans_10 + prob_trans_01_) + // is the stationary state probability of no-loss + // pi_1 = prob_trans_01_ / (prob_trans_10 + prob_trans_01_) + // is the stationary state probability of loss + // After a derivation prob_trans_00 should satisfy: + // prob_trans_00 ^ (units - 1) = (loss_rate - 1) / prob_trans_10 * + // prob_trans_00 + (1 - loss_rate) * (1 + 1 / prob_trans_10). + double loss_rate = 0.01f * packet_loss_rate_; + double prob_trans_10 = 1.0f * kPacketLossTimeUnitMs / FLAGS_burst_length; + double prob_trans_00 = ProbTrans00Solver(units, loss_rate, prob_trans_10); + loss_model_.reset(new GilbertElliotLoss(1.0f - prob_trans_10, + 1.0f - prob_trans_00)); + break; + } + default: { + loss_model_.reset(new NoLoss); + break; + } + } + + // Make sure that the packet loss profile is same for all derived tests. + srand(kInitSeed); +} + +void NetEqQualityTest::TearDown() { + fclose(out_file_); +} + +bool NetEqQualityTest::PacketLost() { + int cycles = block_duration_ms_ / kPacketLossTimeUnitMs; + + // The loop is to make sure that codecs with different block lengths share the + // same packet loss profile. + bool lost = false; + for (int idx = 0; idx < cycles; idx ++) { + if (loss_model_->Lost()) { + // The packet will be lost if any of the drawings indicates a loss, but + // the loop has to go on to make sure that codecs with different block + // lengths keep the same pace. + lost = true; + } + } + return lost; +} + +int NetEqQualityTest::Transmit() { + int packet_input_time_ms = + rtp_generator_->GetRtpHeader(kPayloadType, in_size_samples_, + &rtp_header_); + if (payload_size_bytes_ > 0) { + fprintf(log_file_, "Packet at %d ms", packet_input_time_ms); + if (!PacketLost()) { + int ret = neteq_->InsertPacket(rtp_header_, &payload_[0], + payload_size_bytes_, + packet_input_time_ms * in_sampling_khz_); + if (ret != NetEq::kOK) + return -1; + fprintf(log_file_, " OK.\n"); + } else { + fprintf(log_file_, " Lost.\n"); + } + } + return packet_input_time_ms; +} + +int NetEqQualityTest::DecodeBlock() { + int channels; + int samples; + int ret = neteq_->GetAudio(out_size_samples_ * channels_, &out_data_[0], + &samples, &channels, NULL); + + if (ret != NetEq::kOK) { + return -1; + } else { + assert(channels == channels_); + assert(samples == kOutputSizeMs * out_sampling_khz_); + fwrite(&out_data_[0], sizeof(int16_t), samples * channels, out_file_); + return samples; + } +} + +void NetEqQualityTest::Simulate(int end_time_ms) { + int audio_size_samples; + + while (decoded_time_ms_ < end_time_ms) { + // Assume 10 packets in packets buffer. + while (decodable_time_ms_ - 10 * block_duration_ms_ < decoded_time_ms_) { + ASSERT_TRUE(in_file_->Read(in_size_samples_ * channels_, &in_data_[0])); + payload_size_bytes_ = EncodeBlock(&in_data_[0], + in_size_samples_, &payload_[0], + max_payload_bytes_); + total_payload_size_bytes_ += payload_size_bytes_; + decodable_time_ms_ = Transmit() + block_duration_ms_; + } + audio_size_samples = DecodeBlock(); + if (audio_size_samples > 0) { + decoded_time_ms_ += audio_size_samples / out_sampling_khz_; + } + } + fprintf(log_file_, "%f", 8.0f * total_payload_size_bytes_ / end_time_ms); +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_QUALITY_TEST_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_QUALITY_TEST_H_ + +#include +#include +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" +#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" +#include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/typedefs.h" + +using google::RegisterFlagValidator; + +namespace webrtc { +namespace test { + +class LossModel { + public: + virtual ~LossModel() {}; + virtual bool Lost() = 0; +}; + +class NoLoss : public LossModel { + public: + virtual bool Lost() OVERRIDE; +}; + +class UniformLoss : public LossModel { + public: + UniformLoss(double loss_rate); + virtual bool Lost() OVERRIDE; + void set_loss_rate(double loss_rate) { loss_rate_ = loss_rate; } + + private: + double loss_rate_; +}; + +class GilbertElliotLoss : public LossModel { + public: + GilbertElliotLoss(double prob_trans_11, double prob_trans_01); + virtual bool Lost() OVERRIDE; + + private: + // Prob. of losing current packet, when previous packet is lost. + double prob_trans_11_; + // Prob. of losing current packet, when previous packet is not lost. + double prob_trans_01_; + bool lost_last_; + scoped_ptr uniform_loss_model_; +}; + +class NetEqQualityTest : public ::testing::Test { + protected: + NetEqQualityTest(int block_duration_ms, + int in_sampling_khz, + int out_sampling_khz, + enum NetEqDecoder decoder_type, + int channels, + std::string in_filename, + std::string out_filename); + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + + // EncodeBlock(...) does the following: + // 1. encodes a block of audio, saved in |in_data| and has a length of + // |block_size_samples| (samples per channel), + // 2. save the bit stream to |payload| of |max_bytes| bytes in size, + // 3. returns the length of the payload (in bytes), + virtual int EncodeBlock(int16_t* in_data, int block_size_samples, + uint8_t* payload, int max_bytes) = 0; + + // PacketLost(...) determines weather a packet sent at an indicated time gets + // lost or not. + bool PacketLost(); + + // DecodeBlock() decodes a block of audio using the payload stored in + // |payload_| with the length of |payload_size_bytes_| (bytes). The decoded + // audio is to be stored in |out_data_|. + int DecodeBlock(); + + // Transmit() uses |rtp_generator_| to generate a packet and passes it to + // |neteq_|. + int Transmit(); + + // Simulate(...) runs encoding / transmitting / decoding up to |end_time_ms| + // (miliseconds), the resulted audio is stored in the file with the name of + // |out_filename_|. + void Simulate(int end_time_ms); + + private: + int decoded_time_ms_; + int decodable_time_ms_; + double drift_factor_; + int packet_loss_rate_; + const int block_duration_ms_; + const int in_sampling_khz_; + const int out_sampling_khz_; + const enum NetEqDecoder decoder_type_; + const int channels_; + const std::string in_filename_; + const std::string out_filename_; + const std::string log_filename_; + + // Number of samples per channel in a frame. + const int in_size_samples_; + + // Expected output number of samples per channel in a frame. + const int out_size_samples_; + + int payload_size_bytes_; + int max_payload_bytes_; + + scoped_ptr in_file_; + FILE* out_file_; + FILE* log_file_; + + scoped_ptr rtp_generator_; + scoped_ptr neteq_; + scoped_ptr loss_model_; + + scoped_ptr in_data_; + scoped_ptr payload_; + scoped_ptr out_data_; + WebRtcRTPHeader rtp_header_; + + long total_payload_size_bytes_; +}; + +} // namespace test +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_QUALITY_TEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,697 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// TODO(hlundin): The functionality in this file should be moved into one or +// several classes. + +#include +#include +#include // For ULONG_MAX returned by strtoul. +#include +#include // For strtoul. + +#include +#include +#include + +#include "google/gflags.h" +#include "webrtc/base/checks.h" +#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" +#include "webrtc/modules/audio_coding/neteq/interface/neteq.h" +#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet.h" +#include "webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h" +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/test/testsupport/fileutils.h" +#include "webrtc/typedefs.h" + +using webrtc::NetEq; +using webrtc::WebRtcRTPHeader; + +namespace { +// Parses the input string for a valid SSRC (at the start of the string). If a +// valid SSRC is found, it is written to the output variable |ssrc|, and true is +// returned. Otherwise, false is returned. +bool ParseSsrc(const std::string& str, uint32_t* ssrc) { + if (str.empty()) + return true; + int base = 10; + // Look for "0x" or "0X" at the start and change base to 16 if found. + if ((str.compare(0, 2, "0x") == 0) || (str.compare(0, 2, "0X") == 0)) + base = 16; + errno = 0; + char* end_ptr; + unsigned long value = strtoul(str.c_str(), &end_ptr, base); + if (value == ULONG_MAX && errno == ERANGE) + return false; // Value out of range for unsigned long. + if (sizeof(unsigned long) > sizeof(uint32_t) && value > 0xFFFFFFFF) + return false; // Value out of range for uint32_t. + if (end_ptr - str.c_str() < static_cast(str.length())) + return false; // Part of the string was not parsed. + *ssrc = static_cast(value); + return true; +} + +} // namespace + +// Flag validators. +static bool ValidatePayloadType(const char* flagname, int32_t value) { + if (value >= 0 && value <= 127) // Value is ok. + return true; + printf("Invalid value for --%s: %d\n", flagname, static_cast(value)); + return false; +} + +static bool ValidateSsrcValue(const char* flagname, const std::string& str) { + uint32_t dummy_ssrc; + return ParseSsrc(str, &dummy_ssrc); +} + +// Define command line flags. +DEFINE_int32(pcmu, 0, "RTP payload type for PCM-u"); +static const bool pcmu_dummy = + google::RegisterFlagValidator(&FLAGS_pcmu, &ValidatePayloadType); +DEFINE_int32(pcma, 8, "RTP payload type for PCM-a"); +static const bool pcma_dummy = + google::RegisterFlagValidator(&FLAGS_pcma, &ValidatePayloadType); +DEFINE_int32(ilbc, 102, "RTP payload type for iLBC"); +static const bool ilbc_dummy = + google::RegisterFlagValidator(&FLAGS_ilbc, &ValidatePayloadType); +DEFINE_int32(isac, 103, "RTP payload type for iSAC"); +static const bool isac_dummy = + google::RegisterFlagValidator(&FLAGS_isac, &ValidatePayloadType); +DEFINE_int32(isac_swb, 104, "RTP payload type for iSAC-swb (32 kHz)"); +static const bool isac_swb_dummy = + google::RegisterFlagValidator(&FLAGS_isac_swb, &ValidatePayloadType); +DEFINE_int32(opus, 111, "RTP payload type for Opus"); +static const bool opus_dummy = + google::RegisterFlagValidator(&FLAGS_opus, &ValidatePayloadType); +DEFINE_int32(pcm16b, 93, "RTP payload type for PCM16b-nb (8 kHz)"); +static const bool pcm16b_dummy = + google::RegisterFlagValidator(&FLAGS_pcm16b, &ValidatePayloadType); +DEFINE_int32(pcm16b_wb, 94, "RTP payload type for PCM16b-wb (16 kHz)"); +static const bool pcm16b_wb_dummy = + google::RegisterFlagValidator(&FLAGS_pcm16b_wb, &ValidatePayloadType); +DEFINE_int32(pcm16b_swb32, 95, "RTP payload type for PCM16b-swb32 (32 kHz)"); +static const bool pcm16b_swb32_dummy = + google::RegisterFlagValidator(&FLAGS_pcm16b_swb32, &ValidatePayloadType); +DEFINE_int32(pcm16b_swb48, 96, "RTP payload type for PCM16b-swb48 (48 kHz)"); +static const bool pcm16b_swb48_dummy = + google::RegisterFlagValidator(&FLAGS_pcm16b_swb48, &ValidatePayloadType); +DEFINE_int32(g722, 9, "RTP payload type for G.722"); +static const bool g722_dummy = + google::RegisterFlagValidator(&FLAGS_g722, &ValidatePayloadType); +DEFINE_int32(avt, 106, "RTP payload type for AVT/DTMF"); +static const bool avt_dummy = + google::RegisterFlagValidator(&FLAGS_avt, &ValidatePayloadType); +DEFINE_int32(red, 117, "RTP payload type for redundant audio (RED)"); +static const bool red_dummy = + google::RegisterFlagValidator(&FLAGS_red, &ValidatePayloadType); +DEFINE_int32(cn_nb, 13, "RTP payload type for comfort noise (8 kHz)"); +static const bool cn_nb_dummy = + google::RegisterFlagValidator(&FLAGS_cn_nb, &ValidatePayloadType); +DEFINE_int32(cn_wb, 98, "RTP payload type for comfort noise (16 kHz)"); +static const bool cn_wb_dummy = + google::RegisterFlagValidator(&FLAGS_cn_wb, &ValidatePayloadType); +DEFINE_int32(cn_swb32, 99, "RTP payload type for comfort noise (32 kHz)"); +static const bool cn_swb32_dummy = + google::RegisterFlagValidator(&FLAGS_cn_swb32, &ValidatePayloadType); +DEFINE_int32(cn_swb48, 100, "RTP payload type for comfort noise (48 kHz)"); +static const bool cn_swb48_dummy = + google::RegisterFlagValidator(&FLAGS_cn_swb48, &ValidatePayloadType); +DEFINE_bool(codec_map, false, "Prints the mapping between RTP payload type and " + "codec"); +DEFINE_string(replacement_audio_file, "", + "A PCM file that will be used to populate ""dummy"" RTP packets"); +DEFINE_string(ssrc, + "", + "Only use packets with this SSRC (decimal or hex, the latter " + "starting with 0x)"); +static const bool hex_ssrc_dummy = + google::RegisterFlagValidator(&FLAGS_ssrc, &ValidateSsrcValue); + +// Declaring helper functions (defined further down in this file). +std::string CodecName(webrtc::NetEqDecoder codec); +void RegisterPayloadTypes(NetEq* neteq); +void PrintCodecMapping(); +size_t ReplacePayload(webrtc::test::InputAudioFile* replacement_audio_file, + webrtc::scoped_ptr* replacement_audio, + webrtc::scoped_ptr* payload, + size_t* payload_mem_size_bytes, + size_t* frame_size_samples, + WebRtcRTPHeader* rtp_header, + const webrtc::test::Packet* next_packet); +int CodecSampleRate(uint8_t payload_type); +int CodecTimestampRate(uint8_t payload_type); +bool IsComfortNosie(uint8_t payload_type); + +int main(int argc, char* argv[]) { + static const int kMaxChannels = 5; + static const int kMaxSamplesPerMs = 48000 / 1000; + static const int kOutputBlockSizeMs = 10; + + std::string program_name = argv[0]; + std::string usage = "Tool for decoding an RTP dump file using NetEq.\n" + "Run " + program_name + " --helpshort for usage.\n" + "Example usage:\n" + program_name + + " input.rtp output.pcm\n"; + google::SetUsageMessage(usage); + google::ParseCommandLineFlags(&argc, &argv, true); + + if (FLAGS_codec_map) { + PrintCodecMapping(); + } + + if (argc != 3) { + if (FLAGS_codec_map) { + // We have already printed the codec map. Just end the program. + return 0; + } + // Print usage information. + std::cout << google::ProgramUsage(); + return 0; + } + + printf("Input file: %s\n", argv[1]); + webrtc::scoped_ptr file_source( + webrtc::test::RtpFileSource::Create(argv[1])); + assert(file_source.get()); + + // Check if an SSRC value was provided. + if (!FLAGS_ssrc.empty()) { + uint32_t ssrc; + CHECK(ParseSsrc(FLAGS_ssrc, &ssrc)) << "Flag verification has failed."; + file_source->SelectSsrc(ssrc); + } + + FILE* out_file = fopen(argv[2], "wb"); + if (!out_file) { + std::cerr << "Cannot open output file " << argv[2] << std::endl; + exit(1); + } + std::cout << "Output file: " << argv[2] << std::endl; + + // Check if a replacement audio file was provided, and if so, open it. + bool replace_payload = false; + webrtc::scoped_ptr replacement_audio_file; + if (!FLAGS_replacement_audio_file.empty()) { + replacement_audio_file.reset( + new webrtc::test::InputAudioFile(FLAGS_replacement_audio_file)); + replace_payload = true; + } + + // Enable tracing. + webrtc::Trace::CreateTrace(); + webrtc::Trace::SetTraceFile((webrtc::test::OutputPath() + + "neteq_trace.txt").c_str()); + webrtc::Trace::set_level_filter(webrtc::kTraceAll); + + // Initialize NetEq instance. + int sample_rate_hz = 16000; + NetEq::Config config; + config.sample_rate_hz = sample_rate_hz; + NetEq* neteq = NetEq::Create(config); + RegisterPayloadTypes(neteq); + + // Read first packet. + webrtc::scoped_ptr packet(file_source->NextPacket()); + if (!packet) { + printf( + "Warning: input file is empty, or the filters did not match any " + "packets\n"); + webrtc::Trace::ReturnTrace(); + return 0; + } + bool packet_available = true; + + // Set up variables for audio replacement if needed. + webrtc::scoped_ptr next_packet; + bool next_packet_available = false; + size_t input_frame_size_timestamps = 0; + webrtc::scoped_ptr replacement_audio; + webrtc::scoped_ptr payload; + size_t payload_mem_size_bytes = 0; + if (replace_payload) { + // Initially assume that the frame size is 30 ms at the initial sample rate. + // This value will be replaced with the correct one as soon as two + // consecutive packets are found. + input_frame_size_timestamps = 30 * sample_rate_hz / 1000; + replacement_audio.reset(new int16_t[input_frame_size_timestamps]); + payload_mem_size_bytes = 2 * input_frame_size_timestamps; + payload.reset(new uint8_t[payload_mem_size_bytes]); + next_packet.reset(file_source->NextPacket()); + assert(next_packet); + next_packet_available = true; + } + + // This is the main simulation loop. + // Set the simulation clock to start immediately with the first packet. + int time_now_ms = packet->time_ms(); + int next_input_time_ms = time_now_ms; + int next_output_time_ms = time_now_ms; + if (time_now_ms % kOutputBlockSizeMs != 0) { + // Make sure that next_output_time_ms is rounded up to the next multiple + // of kOutputBlockSizeMs. (Legacy bit-exactness.) + next_output_time_ms += + kOutputBlockSizeMs - time_now_ms % kOutputBlockSizeMs; + } + while (packet_available) { + // Check if it is time to insert packet. + while (time_now_ms >= next_input_time_ms && packet_available) { + assert(packet->virtual_payload_length_bytes() > 0); + // Parse RTP header. + WebRtcRTPHeader rtp_header; + packet->ConvertHeader(&rtp_header); + const uint8_t* payload_ptr = packet->payload(); + size_t payload_len = packet->payload_length_bytes(); + if (replace_payload) { + payload_len = ReplacePayload(replacement_audio_file.get(), + &replacement_audio, + &payload, + &payload_mem_size_bytes, + &input_frame_size_timestamps, + &rtp_header, + next_packet.get()); + payload_ptr = payload.get(); + } + int error = + neteq->InsertPacket(rtp_header, + payload_ptr, + static_cast(payload_len), + packet->time_ms() * sample_rate_hz / 1000); + if (error != NetEq::kOK) { + if (neteq->LastError() == NetEq::kUnknownRtpPayloadType) { + std::cerr << "RTP Payload type " + << static_cast(rtp_header.header.payloadType) + << " is unknown." << std::endl; + std::cerr << "Use --codec_map to view default mapping." << std::endl; + std::cerr << "Use --helpshort for information on how to make custom " + "mappings." << std::endl; + } else { + std::cerr << "InsertPacket returned error code " << neteq->LastError() + << std::endl; + std::cerr << "Header data:" << std::endl; + std::cerr << " PT = " + << static_cast(rtp_header.header.payloadType) + << std::endl; + std::cerr << " SN = " << rtp_header.header.sequenceNumber + << std::endl; + std::cerr << " TS = " << rtp_header.header.timestamp << std::endl; + } + } + + // Get next packet from file. + webrtc::test::Packet* temp_packet = file_source->NextPacket(); + if (temp_packet) { + packet.reset(temp_packet); + } else { + packet_available = false; + } + if (replace_payload) { + // At this point |packet| contains the packet *after* |next_packet|. + // Swap Packet objects between |packet| and |next_packet|. + packet.swap(next_packet); + // Swap the status indicators unless they're already the same. + if (packet_available != next_packet_available) { + packet_available = !packet_available; + next_packet_available = !next_packet_available; + } + } + next_input_time_ms = packet->time_ms(); + } + + // Check if it is time to get output audio. + if (time_now_ms >= next_output_time_ms) { + static const int kOutDataLen = kOutputBlockSizeMs * kMaxSamplesPerMs * + kMaxChannels; + int16_t out_data[kOutDataLen]; + int num_channels; + int samples_per_channel; + int error = neteq->GetAudio(kOutDataLen, out_data, &samples_per_channel, + &num_channels, NULL); + if (error != NetEq::kOK) { + std::cerr << "GetAudio returned error code " << + neteq->LastError() << std::endl; + } else { + // Calculate sample rate from output size. + sample_rate_hz = 1000 * samples_per_channel / kOutputBlockSizeMs; + } + + // Write to file. + // TODO(hlundin): Make writing to file optional. + size_t write_len = samples_per_channel * num_channels; + if (fwrite(out_data, sizeof(out_data[0]), write_len, out_file) != + write_len) { + std::cerr << "Error while writing to file" << std::endl; + webrtc::Trace::ReturnTrace(); + exit(1); + } + next_output_time_ms += kOutputBlockSizeMs; + } + // Advance time to next event. + time_now_ms = std::min(next_input_time_ms, next_output_time_ms); + } + + std::cout << "Simulation done" << std::endl; + + fclose(out_file); + delete neteq; + webrtc::Trace::ReturnTrace(); + return 0; +} + + +// Help functions. + +// Maps a codec type to a printable name string. +std::string CodecName(webrtc::NetEqDecoder codec) { + switch (codec) { + case webrtc::kDecoderPCMu: + return "PCM-u"; + case webrtc::kDecoderPCMa: + return "PCM-a"; + case webrtc::kDecoderILBC: + return "iLBC"; + case webrtc::kDecoderISAC: + return "iSAC"; + case webrtc::kDecoderISACswb: + return "iSAC-swb (32 kHz)"; + case webrtc::kDecoderOpus: + return "Opus"; + case webrtc::kDecoderPCM16B: + return "PCM16b-nb (8 kHz)"; + case webrtc::kDecoderPCM16Bwb: + return "PCM16b-wb (16 kHz)"; + case webrtc::kDecoderPCM16Bswb32kHz: + return "PCM16b-swb32 (32 kHz)"; + case webrtc::kDecoderPCM16Bswb48kHz: + return "PCM16b-swb48 (48 kHz)"; + case webrtc::kDecoderG722: + return "G.722"; + case webrtc::kDecoderRED: + return "redundant audio (RED)"; + case webrtc::kDecoderAVT: + return "AVT/DTMF"; + case webrtc::kDecoderCNGnb: + return "comfort noise (8 kHz)"; + case webrtc::kDecoderCNGwb: + return "comfort noise (16 kHz)"; + case webrtc::kDecoderCNGswb32kHz: + return "comfort noise (32 kHz)"; + case webrtc::kDecoderCNGswb48kHz: + return "comfort noise (48 kHz)"; + default: + assert(false); + return "undefined"; + } +} + +// Registers all decoders in |neteq|. +void RegisterPayloadTypes(NetEq* neteq) { + assert(neteq); + int error; + error = neteq->RegisterPayloadType(webrtc::kDecoderPCMu, FLAGS_pcmu); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_pcmu << + " as " << CodecName(webrtc::kDecoderPCMu).c_str() << std::endl; + exit(1); + } + error = neteq->RegisterPayloadType(webrtc::kDecoderPCMa, FLAGS_pcma); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_pcma << + " as " << CodecName(webrtc::kDecoderPCMa).c_str() << std::endl; + exit(1); + } + error = neteq->RegisterPayloadType(webrtc::kDecoderILBC, FLAGS_ilbc); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_ilbc << + " as " << CodecName(webrtc::kDecoderILBC).c_str() << std::endl; + exit(1); + } + error = neteq->RegisterPayloadType(webrtc::kDecoderISAC, FLAGS_isac); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_isac << + " as " << CodecName(webrtc::kDecoderISAC).c_str() << std::endl; + exit(1); + } + error = neteq->RegisterPayloadType(webrtc::kDecoderISACswb, FLAGS_isac_swb); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_isac_swb << + " as " << CodecName(webrtc::kDecoderISACswb).c_str() << std::endl; + exit(1); + } + error = neteq->RegisterPayloadType(webrtc::kDecoderOpus, FLAGS_opus); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_opus << " as " + << CodecName(webrtc::kDecoderOpus).c_str() << std::endl; + exit(1); + } + error = neteq->RegisterPayloadType(webrtc::kDecoderPCM16B, FLAGS_pcm16b); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_pcm16b << + " as " << CodecName(webrtc::kDecoderPCM16B).c_str() << std::endl; + exit(1); + } + error = neteq->RegisterPayloadType(webrtc::kDecoderPCM16Bwb, + FLAGS_pcm16b_wb); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_pcm16b_wb << + " as " << CodecName(webrtc::kDecoderPCM16Bwb).c_str() << std::endl; + exit(1); + } + error = neteq->RegisterPayloadType(webrtc::kDecoderPCM16Bswb32kHz, + FLAGS_pcm16b_swb32); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_pcm16b_swb32 << + " as " << CodecName(webrtc::kDecoderPCM16Bswb32kHz).c_str() << + std::endl; + exit(1); + } + error = neteq->RegisterPayloadType(webrtc::kDecoderPCM16Bswb48kHz, + FLAGS_pcm16b_swb48); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_pcm16b_swb48 << + " as " << CodecName(webrtc::kDecoderPCM16Bswb48kHz).c_str() << + std::endl; + exit(1); + } + error = neteq->RegisterPayloadType(webrtc::kDecoderG722, FLAGS_g722); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_g722 << + " as " << CodecName(webrtc::kDecoderG722).c_str() << std::endl; + exit(1); + } + error = neteq->RegisterPayloadType(webrtc::kDecoderAVT, FLAGS_avt); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_avt << + " as " << CodecName(webrtc::kDecoderAVT).c_str() << std::endl; + exit(1); + } + error = neteq->RegisterPayloadType(webrtc::kDecoderRED, FLAGS_red); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_red << + " as " << CodecName(webrtc::kDecoderRED).c_str() << std::endl; + exit(1); + } + error = neteq->RegisterPayloadType(webrtc::kDecoderCNGnb, FLAGS_cn_nb); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_cn_nb << + " as " << CodecName(webrtc::kDecoderCNGnb).c_str() << std::endl; + exit(1); + } + error = neteq->RegisterPayloadType(webrtc::kDecoderCNGwb, FLAGS_cn_wb); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_cn_wb << + " as " << CodecName(webrtc::kDecoderCNGwb).c_str() << std::endl; + exit(1); + } + error = neteq->RegisterPayloadType(webrtc::kDecoderCNGswb32kHz, + FLAGS_cn_swb32); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_cn_swb32 << + " as " << CodecName(webrtc::kDecoderCNGswb32kHz).c_str() << std::endl; + exit(1); + } + error = neteq->RegisterPayloadType(webrtc::kDecoderCNGswb48kHz, + FLAGS_cn_swb48); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_cn_swb48 << + " as " << CodecName(webrtc::kDecoderCNGswb48kHz).c_str() << std::endl; + exit(1); + } +} + +void PrintCodecMapping() { + std::cout << CodecName(webrtc::kDecoderPCMu).c_str() << ": " << FLAGS_pcmu << + std::endl; + std::cout << CodecName(webrtc::kDecoderPCMa).c_str() << ": " << FLAGS_pcma << + std::endl; + std::cout << CodecName(webrtc::kDecoderILBC).c_str() << ": " << FLAGS_ilbc << + std::endl; + std::cout << CodecName(webrtc::kDecoderISAC).c_str() << ": " << FLAGS_isac << + std::endl; + std::cout << CodecName(webrtc::kDecoderISACswb).c_str() << ": " << + FLAGS_isac_swb << std::endl; + std::cout << CodecName(webrtc::kDecoderOpus).c_str() << ": " << FLAGS_opus + << std::endl; + std::cout << CodecName(webrtc::kDecoderPCM16B).c_str() << ": " << + FLAGS_pcm16b << std::endl; + std::cout << CodecName(webrtc::kDecoderPCM16Bwb).c_str() << ": " << + FLAGS_pcm16b_wb << std::endl; + std::cout << CodecName(webrtc::kDecoderPCM16Bswb32kHz).c_str() << ": " << + FLAGS_pcm16b_swb32 << std::endl; + std::cout << CodecName(webrtc::kDecoderPCM16Bswb48kHz).c_str() << ": " << + FLAGS_pcm16b_swb48 << std::endl; + std::cout << CodecName(webrtc::kDecoderG722).c_str() << ": " << FLAGS_g722 << + std::endl; + std::cout << CodecName(webrtc::kDecoderAVT).c_str() << ": " << FLAGS_avt << + std::endl; + std::cout << CodecName(webrtc::kDecoderRED).c_str() << ": " << FLAGS_red << + std::endl; + std::cout << CodecName(webrtc::kDecoderCNGnb).c_str() << ": " << + FLAGS_cn_nb << std::endl; + std::cout << CodecName(webrtc::kDecoderCNGwb).c_str() << ": " << + FLAGS_cn_wb << std::endl; + std::cout << CodecName(webrtc::kDecoderCNGswb32kHz).c_str() << ": " << + FLAGS_cn_swb32 << std::endl; + std::cout << CodecName(webrtc::kDecoderCNGswb48kHz).c_str() << ": " << + FLAGS_cn_swb48 << std::endl; +} + +size_t ReplacePayload(webrtc::test::InputAudioFile* replacement_audio_file, + webrtc::scoped_ptr* replacement_audio, + webrtc::scoped_ptr* payload, + size_t* payload_mem_size_bytes, + size_t* frame_size_samples, + WebRtcRTPHeader* rtp_header, + const webrtc::test::Packet* next_packet) { + size_t payload_len = 0; + // Check for CNG. + if (IsComfortNosie(rtp_header->header.payloadType)) { + // If CNG, simply insert a zero-energy one-byte payload. + if (*payload_mem_size_bytes < 1) { + (*payload).reset(new uint8_t[1]); + *payload_mem_size_bytes = 1; + } + (*payload)[0] = 127; // Max attenuation of CNG. + payload_len = 1; + } else { + assert(next_packet->virtual_payload_length_bytes() > 0); + // Check if payload length has changed. + if (next_packet->header().sequenceNumber == + rtp_header->header.sequenceNumber + 1) { + if (*frame_size_samples != + next_packet->header().timestamp - rtp_header->header.timestamp) { + *frame_size_samples = + next_packet->header().timestamp - rtp_header->header.timestamp; + (*replacement_audio).reset( + new int16_t[*frame_size_samples]); + *payload_mem_size_bytes = 2 * *frame_size_samples; + (*payload).reset(new uint8_t[*payload_mem_size_bytes]); + } + } + // Get new speech. + assert((*replacement_audio).get()); + if (CodecTimestampRate(rtp_header->header.payloadType) != + CodecSampleRate(rtp_header->header.payloadType) || + rtp_header->header.payloadType == FLAGS_red || + rtp_header->header.payloadType == FLAGS_avt) { + // Some codecs have different sample and timestamp rates. And neither + // RED nor DTMF is supported for replacement. + std::cerr << "Codec not supported for audio replacement." << + std::endl; + webrtc::Trace::ReturnTrace(); + exit(1); + } + assert(*frame_size_samples > 0); + if (!replacement_audio_file->Read(*frame_size_samples, + (*replacement_audio).get())) { + std::cerr << "Could not read replacement audio file." << std::endl; + webrtc::Trace::ReturnTrace(); + exit(1); + } + // Encode it as PCM16. + assert((*payload).get()); + payload_len = WebRtcPcm16b_Encode((*replacement_audio).get(), + static_cast(*frame_size_samples), + (*payload).get()); + assert(payload_len == 2 * *frame_size_samples); + // Change payload type to PCM16. + switch (CodecSampleRate(rtp_header->header.payloadType)) { + case 8000: + rtp_header->header.payloadType = FLAGS_pcm16b; + break; + case 16000: + rtp_header->header.payloadType = FLAGS_pcm16b_wb; + break; + case 32000: + rtp_header->header.payloadType = FLAGS_pcm16b_swb32; + break; + case 48000: + rtp_header->header.payloadType = FLAGS_pcm16b_swb48; + break; + default: + std::cerr << "Payload type " << + static_cast(rtp_header->header.payloadType) << + " not supported or unknown." << std::endl; + webrtc::Trace::ReturnTrace(); + exit(1); + } + } + return payload_len; +} + +int CodecSampleRate(uint8_t payload_type) { + if (payload_type == FLAGS_pcmu || + payload_type == FLAGS_pcma || + payload_type == FLAGS_ilbc || + payload_type == FLAGS_pcm16b || + payload_type == FLAGS_cn_nb) { + return 8000; + } else if (payload_type == FLAGS_isac || + payload_type == FLAGS_pcm16b_wb || + payload_type == FLAGS_g722 || + payload_type == FLAGS_cn_wb) { + return 16000; + } else if (payload_type == FLAGS_isac_swb || + payload_type == FLAGS_pcm16b_swb32 || + payload_type == FLAGS_cn_swb32) { + return 32000; + } else if (payload_type == FLAGS_opus || payload_type == FLAGS_pcm16b_swb48 || + payload_type == FLAGS_cn_swb48) { + return 48000; + } else if (payload_type == FLAGS_avt || + payload_type == FLAGS_red) { + return 0; + } else { + return -1; + } +} + +int CodecTimestampRate(uint8_t payload_type) { + if (payload_type == FLAGS_g722) { + return 8000; + } else { + return CodecSampleRate(payload_type); + } +} + +bool IsComfortNosie(uint8_t payload_type) { + if (payload_type == FLAGS_cn_nb || + payload_type == FLAGS_cn_wb || + payload_type == FLAGS_cn_swb32 || + payload_type == FLAGS_cn_swb48) { + return true; + } else { + return false; + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/output_audio_file.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/output_audio_file.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/output_audio_file.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/output_audio_file.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_OUTPUT_AUDIO_FILE_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_OUTPUT_AUDIO_FILE_H_ + +#include +#include +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/neteq/tools/audio_sink.h" + +namespace webrtc { +namespace test { + +class OutputAudioFile : public AudioSink { + public: + // Creates an OutputAudioFile, opening a file named |file_name| for writing. + // The file format is 16-bit signed host-endian PCM. + explicit OutputAudioFile(const std::string& file_name) { + out_file_ = fopen(file_name.c_str(), "wb"); + } + + virtual ~OutputAudioFile() { + if (out_file_) + fclose(out_file_); + } + + virtual bool WriteArray(const int16_t* audio, size_t num_samples) OVERRIDE { + assert(out_file_); + return fwrite(audio, sizeof(*audio), num_samples, out_file_) == num_samples; + } + + private: + FILE* out_file_; + + DISALLOW_COPY_AND_ASSIGN(OutputAudioFile); +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_OUTPUT_AUDIO_FILE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/packet.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/packet.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/packet.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/packet.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/tools/packet.h" + +#include + +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" + +namespace webrtc { +namespace test { + +Packet::Packet(uint8_t* packet_memory, + size_t allocated_bytes, + double time_ms, + const RtpHeaderParser& parser) + : payload_memory_(packet_memory), + payload_(NULL), + packet_length_bytes_(allocated_bytes), + payload_length_bytes_(0), + virtual_packet_length_bytes_(allocated_bytes), + virtual_payload_length_bytes_(0), + time_ms_(time_ms) { + valid_header_ = ParseHeader(parser); +} + +Packet::Packet(uint8_t* packet_memory, + size_t allocated_bytes, + size_t virtual_packet_length_bytes, + double time_ms, + const RtpHeaderParser& parser) + : payload_memory_(packet_memory), + payload_(NULL), + packet_length_bytes_(allocated_bytes), + payload_length_bytes_(0), + virtual_packet_length_bytes_(virtual_packet_length_bytes), + virtual_payload_length_bytes_(0), + time_ms_(time_ms) { + valid_header_ = ParseHeader(parser); +} + +Packet::Packet(uint8_t* packet_memory, size_t allocated_bytes, double time_ms) + : payload_memory_(packet_memory), + payload_(NULL), + packet_length_bytes_(allocated_bytes), + payload_length_bytes_(0), + virtual_packet_length_bytes_(allocated_bytes), + virtual_payload_length_bytes_(0), + time_ms_(time_ms) { + scoped_ptr parser(RtpHeaderParser::Create()); + valid_header_ = ParseHeader(*parser); +} + +Packet::Packet(uint8_t* packet_memory, + size_t allocated_bytes, + size_t virtual_packet_length_bytes, + double time_ms) + : payload_memory_(packet_memory), + payload_(NULL), + packet_length_bytes_(allocated_bytes), + payload_length_bytes_(0), + virtual_packet_length_bytes_(virtual_packet_length_bytes), + virtual_payload_length_bytes_(0), + time_ms_(time_ms) { + scoped_ptr parser(RtpHeaderParser::Create()); + valid_header_ = ParseHeader(*parser); +} + +bool Packet::ExtractRedHeaders(std::list* headers) const { + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |1| block PT | timestamp offset | block length | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |1| ... | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |0| block PT | + // +-+-+-+-+-+-+-+-+ + // + + assert(payload_); + const uint8_t* payload_ptr = payload_; + const uint8_t* payload_end_ptr = payload_ptr + payload_length_bytes_; + + // Find all RED headers with the extension bit set to 1. That is, all headers + // but the last one. + while ((payload_ptr < payload_end_ptr) && (*payload_ptr & 0x80)) { + RTPHeader* header = new RTPHeader; + CopyToHeader(header); + header->payloadType = payload_ptr[0] & 0x7F; + uint32_t offset = (payload_ptr[1] << 6) + ((payload_ptr[2] & 0xFC) >> 2); + header->timestamp -= offset; + headers->push_front(header); + payload_ptr += 4; + } + // Last header. + assert(payload_ptr < payload_end_ptr); + if (payload_ptr >= payload_end_ptr) { + return false; // Payload too short. + } + RTPHeader* header = new RTPHeader; + CopyToHeader(header); + header->payloadType = payload_ptr[0] & 0x7F; + headers->push_front(header); + return true; +} + +void Packet::DeleteRedHeaders(std::list* headers) { + while (!headers->empty()) { + delete headers->front(); + headers->pop_front(); + } +} + +void Packet::ConvertHeader(WebRtcRTPHeader* copy_to) const { + memcpy(©_to->header, &header_, sizeof(header_)); + copy_to->frameType = kAudioFrameSpeech; + copy_to->type.Audio.numEnergy = 0; + copy_to->type.Audio.channel = 1; + copy_to->type.Audio.isCNG = false; +} + +bool Packet::ParseHeader(const RtpHeaderParser& parser) { + bool valid_header = parser.Parse( + payload_memory_.get(), static_cast(packet_length_bytes_), &header_); + assert(valid_header); + if (!valid_header) { + return false; + } + assert(header_.headerLength <= packet_length_bytes_); + payload_ = &payload_memory_[header_.headerLength]; + assert(packet_length_bytes_ >= header_.headerLength); + payload_length_bytes_ = packet_length_bytes_ - header_.headerLength; + assert(virtual_packet_length_bytes_ >= header_.headerLength); + virtual_payload_length_bytes_ = + virtual_packet_length_bytes_ - header_.headerLength; + return true; +} + +void Packet::CopyToHeader(RTPHeader* destination) const { + destination->markerBit = header_.markerBit; + destination->payloadType = header_.payloadType; + destination->sequenceNumber = header_.sequenceNumber; + destination->timestamp = header_.timestamp; + destination->ssrc = header_.ssrc; + destination->numCSRCs = header_.numCSRCs; + destination->paddingLength = header_.paddingLength; + destination->headerLength = header_.headerLength; + destination->payload_type_frequency = header_.payload_type_frequency; + memcpy(&destination->arrOfCSRCs, + &header_.arrOfCSRCs, + sizeof(header_.arrOfCSRCs)); + memcpy( + &destination->extension, &header_.extension, sizeof(header_.extension)); +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/packet.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/packet.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/packet.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/packet.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_PACKET_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_PACKET_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/common_types.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +class RtpHeaderParser; +struct WebRtcRTPHeader; + +namespace test { + +// Class for handling RTP packets in test applications. +class Packet { + public: + // Creates a packet, with the packet payload (including header bytes) in + // |packet_memory|. The length of |packet_memory| is |allocated_bytes|. + // The new object assumes ownership of |packet_memory| and will delete it + // when the Packet object is deleted. The |time_ms| is an extra time + // associated with this packet, typically used to denote arrival time. + // The first bytes in |packet_memory| will be parsed using |parser|. + Packet(uint8_t* packet_memory, + size_t allocated_bytes, + double time_ms, + const RtpHeaderParser& parser); + + // Same as above, but with the extra argument |virtual_packet_length_bytes|. + // This is typically used when reading RTP dump files that only contain the + // RTP headers, and no payload (a.k.a RTP dummy files or RTP light). The + // |virtual_packet_length_bytes| tells what size the packet had on wire, + // including the now discarded payload, whereas |allocated_bytes| is the + // length of the remaining payload (typically only the RTP header). + Packet(uint8_t* packet_memory, + size_t allocated_bytes, + size_t virtual_packet_length_bytes, + double time_ms, + const RtpHeaderParser& parser); + + // The following two constructors are the same as above, but without a + // parser. Note that when the object is constructed using any of these + // methods, the header will be parsed using a default RtpHeaderParser object. + // In particular, RTP header extensions won't be parsed. + Packet(uint8_t* packet_memory, size_t allocated_bytes, double time_ms); + + Packet(uint8_t* packet_memory, + size_t allocated_bytes, + size_t virtual_packet_length_bytes, + double time_ms); + + virtual ~Packet() {} + + // Parses the first bytes of the RTP payload, interpreting them as RED headers + // according to RFC 2198. The headers will be inserted into |headers|. The + // caller of the method assumes ownership of the objects in the list, and + // must delete them properly. + bool ExtractRedHeaders(std::list* headers) const; + + // Deletes all RTPHeader objects in |headers|, but does not delete |headers| + // itself. + static void DeleteRedHeaders(std::list* headers); + + const uint8_t* payload() const { return payload_; } + + size_t packet_length_bytes() const { return packet_length_bytes_; } + + size_t payload_length_bytes() const { return payload_length_bytes_; } + + size_t virtual_packet_length_bytes() const { + return virtual_packet_length_bytes_; + } + + size_t virtual_payload_length_bytes() const { + return virtual_payload_length_bytes_; + } + + const RTPHeader& header() const { return header_; } + + // Copies the packet header information, converting from the native RTPHeader + // type to WebRtcRTPHeader. + void ConvertHeader(WebRtcRTPHeader* copy_to) const; + + void set_time_ms(double time) { time_ms_ = time; } + double time_ms() const { return time_ms_; } + bool valid_header() const { return valid_header_; } + + private: + bool ParseHeader(const RtpHeaderParser& parser); + void CopyToHeader(RTPHeader* destination) const; + + RTPHeader header_; + scoped_ptr payload_memory_; + const uint8_t* payload_; // First byte after header. + const size_t packet_length_bytes_; // Total length of packet. + size_t payload_length_bytes_; // Length of the payload, after RTP header. + // Zero for dummy RTP packets. + // Virtual lengths are used when parsing RTP header files (dummy RTP files). + const size_t virtual_packet_length_bytes_; + size_t virtual_payload_length_bytes_; + double time_ms_; // Used to denote a packet's arrival time. + bool valid_header_; // Set by the RtpHeaderParser. + + DISALLOW_COPY_AND_ASSIGN(Packet); +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_PACKET_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/packet_source.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/packet_source.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/packet_source.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/packet_source.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_PACKET_SOURCE_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_PACKET_SOURCE_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace test { + +class Packet; + +// Interface class for an object delivering RTP packets to test applications. +class PacketSource { + public: + PacketSource() : use_ssrc_filter_(false), ssrc_(0) {} + virtual ~PacketSource() {} + + // Returns a pointer to the next packet. Returns NULL if the source is + // depleted, or if an error occurred. + virtual Packet* NextPacket() = 0; + + virtual void FilterOutPayloadType(uint8_t payload_type) { + filter_.set(payload_type, true); + } + + virtual void SelectSsrc(uint32_t ssrc) { + use_ssrc_filter_ = true; + ssrc_ = ssrc; + } + + protected: + std::bitset<128> filter_; // Payload type is 7 bits in the RFC. + // If SSRC filtering discards all packet that do not match the SSRC. + bool use_ssrc_filter_; // True when SSRC filtering is active. + uint32_t ssrc_; // The selected SSRC. All other SSRCs will be discarded. + + private: + DISALLOW_COPY_AND_ASSIGN(PacketSource); +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_PACKET_SOURCE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/packet_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/packet_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/packet_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/packet_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for test Packet class. + +#include "webrtc/modules/audio_coding/neteq/tools/packet.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace webrtc { +namespace test { + +namespace { +const int kHeaderLengthBytes = 12; + +void MakeRtpHeader(int payload_type, + int seq_number, + uint32_t timestamp, + uint32_t ssrc, + uint8_t* rtp_data) { + rtp_data[0] = 0x80; + rtp_data[1] = payload_type & 0xFF; + rtp_data[2] = (seq_number >> 8) & 0xFF; + rtp_data[3] = (seq_number) & 0xFF; + rtp_data[4] = (timestamp >> 24) & 0xFF; + rtp_data[5] = (timestamp >> 16) & 0xFF; + rtp_data[6] = (timestamp >> 8) & 0xFF; + rtp_data[7] = timestamp & 0xFF; + rtp_data[8] = (ssrc >> 24) & 0xFF; + rtp_data[9] = (ssrc >> 16) & 0xFF; + rtp_data[10] = (ssrc >> 8) & 0xFF; + rtp_data[11] = ssrc & 0xFF; +} +} // namespace + +TEST(TestPacket, RegularPacket) { + const size_t kPacketLengthBytes = 100; + uint8_t* packet_memory = new uint8_t[kPacketLengthBytes]; + const uint8_t kPayloadType = 17; + const uint16_t kSequenceNumber = 4711; + const uint32_t kTimestamp = 47114711; + const uint32_t kSsrc = 0x12345678; + MakeRtpHeader( + kPayloadType, kSequenceNumber, kTimestamp, kSsrc, packet_memory); + const double kPacketTime = 1.0; + // Hand over ownership of |packet_memory| to |packet|. + Packet packet(packet_memory, kPacketLengthBytes, kPacketTime); + ASSERT_TRUE(packet.valid_header()); + EXPECT_EQ(kPayloadType, packet.header().payloadType); + EXPECT_EQ(kSequenceNumber, packet.header().sequenceNumber); + EXPECT_EQ(kTimestamp, packet.header().timestamp); + EXPECT_EQ(kSsrc, packet.header().ssrc); + EXPECT_EQ(0, packet.header().numCSRCs); + EXPECT_EQ(kPacketLengthBytes, packet.packet_length_bytes()); + EXPECT_EQ(kPacketLengthBytes - kHeaderLengthBytes, + packet.payload_length_bytes()); + EXPECT_EQ(kPacketLengthBytes, packet.virtual_packet_length_bytes()); + EXPECT_EQ(kPacketLengthBytes - kHeaderLengthBytes, + packet.virtual_payload_length_bytes()); + EXPECT_EQ(kPacketTime, packet.time_ms()); +} + +TEST(TestPacket, DummyPacket) { + const size_t kPacketLengthBytes = kHeaderLengthBytes; // Only RTP header. + const size_t kVirtualPacketLengthBytes = 100; + uint8_t* packet_memory = new uint8_t[kPacketLengthBytes]; + const uint8_t kPayloadType = 17; + const uint16_t kSequenceNumber = 4711; + const uint32_t kTimestamp = 47114711; + const uint32_t kSsrc = 0x12345678; + MakeRtpHeader( + kPayloadType, kSequenceNumber, kTimestamp, kSsrc, packet_memory); + const double kPacketTime = 1.0; + // Hand over ownership of |packet_memory| to |packet|. + Packet packet(packet_memory, + kPacketLengthBytes, + kVirtualPacketLengthBytes, + kPacketTime); + ASSERT_TRUE(packet.valid_header()); + EXPECT_EQ(kPayloadType, packet.header().payloadType); + EXPECT_EQ(kSequenceNumber, packet.header().sequenceNumber); + EXPECT_EQ(kTimestamp, packet.header().timestamp); + EXPECT_EQ(kSsrc, packet.header().ssrc); + EXPECT_EQ(0, packet.header().numCSRCs); + EXPECT_EQ(kPacketLengthBytes, packet.packet_length_bytes()); + EXPECT_EQ(kPacketLengthBytes - kHeaderLengthBytes, + packet.payload_length_bytes()); + EXPECT_EQ(kVirtualPacketLengthBytes, packet.virtual_packet_length_bytes()); + EXPECT_EQ(kVirtualPacketLengthBytes - kHeaderLengthBytes, + packet.virtual_payload_length_bytes()); + EXPECT_EQ(kPacketTime, packet.time_ms()); +} + +namespace { +// Writes one RED block header starting at |rtp_data|, according to RFC 2198. +// returns the number of bytes written (1 or 4). +// +// Format if |last_payoad| is false: +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |1| block PT | timestamp offset | block length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// Format if |last_payoad| is true: +// 0 1 2 3 4 5 6 7 +// +-+-+-+-+-+-+-+-+ +// |0| Block PT | +// +-+-+-+-+-+-+-+-+ + +int MakeRedHeader(int payload_type, + uint32_t timestamp_offset, + int block_length, + bool last_payload, + uint8_t* rtp_data) { + rtp_data[0] = 0x80 | (payload_type & 0x7F); // Set the first bit to 1. + if (last_payload) { + rtp_data[0] &= 0x7F; // Reset the first but to 0 to indicate last block. + return 1; + } + rtp_data[1] = timestamp_offset >> 6; + rtp_data[2] = (timestamp_offset & 0x3F) << 2; + rtp_data[2] |= block_length >> 8; + rtp_data[3] = block_length & 0xFF; + return 4; +} +} // namespace + +TEST(TestPacket, RED) { + const size_t kPacketLengthBytes = 100; + uint8_t* packet_memory = new uint8_t[kPacketLengthBytes]; + const uint8_t kRedPayloadType = 17; + const uint16_t kSequenceNumber = 4711; + const uint32_t kTimestamp = 47114711; + const uint32_t kSsrc = 0x12345678; + MakeRtpHeader( + kRedPayloadType, kSequenceNumber, kTimestamp, kSsrc, packet_memory); + // Create four RED headers. + // Payload types are just the same as the block index the offset is 100 times + // the block index. + const int kRedBlocks = 4; + uint8_t* payload_ptr = + &packet_memory[kHeaderLengthBytes]; // First byte after header. + for (int i = 0; i < kRedBlocks; ++i) { + int payload_type = i; + // Offset value is not used for the last block. + uint32_t timestamp_offset = 100 * i; + int block_length = 10 * i; + bool last_block = (i == kRedBlocks - 1) ? true : false; + payload_ptr += MakeRedHeader( + payload_type, timestamp_offset, block_length, last_block, payload_ptr); + } + const double kPacketTime = 1.0; + // Hand over ownership of |packet_memory| to |packet|. + Packet packet(packet_memory, kPacketLengthBytes, kPacketTime); + ASSERT_TRUE(packet.valid_header()); + EXPECT_EQ(kRedPayloadType, packet.header().payloadType); + EXPECT_EQ(kSequenceNumber, packet.header().sequenceNumber); + EXPECT_EQ(kTimestamp, packet.header().timestamp); + EXPECT_EQ(kSsrc, packet.header().ssrc); + EXPECT_EQ(0, packet.header().numCSRCs); + EXPECT_EQ(kPacketLengthBytes, packet.packet_length_bytes()); + EXPECT_EQ(kPacketLengthBytes - kHeaderLengthBytes, + packet.payload_length_bytes()); + EXPECT_EQ(kPacketLengthBytes, packet.virtual_packet_length_bytes()); + EXPECT_EQ(kPacketLengthBytes - kHeaderLengthBytes, + packet.virtual_payload_length_bytes()); + EXPECT_EQ(kPacketTime, packet.time_ms()); + std::list red_headers; + EXPECT_TRUE(packet.ExtractRedHeaders(&red_headers)); + EXPECT_EQ(kRedBlocks, static_cast(red_headers.size())); + int block_index = 0; + for (std::list::reverse_iterator it = red_headers.rbegin(); + it != red_headers.rend(); + ++it) { + // Reading list from the back, since the extraction puts the main payload + // (which is the last one on wire) first. + RTPHeader* red_block = *it; + EXPECT_EQ(block_index, red_block->payloadType); + EXPECT_EQ(kSequenceNumber, red_block->sequenceNumber); + if (block_index == kRedBlocks - 1) { + // Last block has zero offset per definition. + EXPECT_EQ(kTimestamp, red_block->timestamp); + } else { + EXPECT_EQ(kTimestamp - 100 * block_index, red_block->timestamp); + } + EXPECT_EQ(kSsrc, red_block->ssrc); + EXPECT_EQ(0, red_block->numCSRCs); + ++block_index; + } + Packet::DeleteRedHeaders(&red_headers); +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.h" + +#include "webrtc/base/checks.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { +namespace test { + +bool ResampleInputAudioFile::Read(size_t samples, + int output_rate_hz, + int16_t* destination) { + const size_t samples_to_read = samples * file_rate_hz_ / output_rate_hz; + CHECK_EQ(samples_to_read * output_rate_hz, samples * file_rate_hz_) + << "Frame size and sample rates don't add up to an integer."; + scoped_ptr temp_destination(new int16_t[samples_to_read]); + if (!InputAudioFile::Read(samples_to_read, temp_destination.get())) + return false; + resampler_.ResetIfNeeded( + file_rate_hz_, output_rate_hz, kResamplerSynchronous); + int output_length = 0; + CHECK_EQ(resampler_.Push(temp_destination.get(), + static_cast(samples_to_read), + destination, + static_cast(samples), + output_length), + 0); + CHECK_EQ(static_cast(samples), output_length); + return true; +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RESAMPLE_INPUT_AUDIO_FILE_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RESAMPLE_INPUT_AUDIO_FILE_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/common_audio/resampler/include/resampler.h" +#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace test { + +// Class for handling a looping input audio file with resampling. +class ResampleInputAudioFile : public InputAudioFile { + public: + ResampleInputAudioFile(const std::string file_name, int file_rate_hz) + : InputAudioFile(file_name), file_rate_hz_(file_rate_hz) {} + + bool Read(size_t samples, int output_rate_hz, int16_t* destination); + + private: + const int file_rate_hz_; + Resampler resampler_; + DISALLOW_COPY_AND_ASSIGN(ResampleInputAudioFile); +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RESAMPLE_INPUT_AUDIO_FILE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_analyze.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_analyze.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_analyze.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_analyze.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include +#include + +#include "google/gflags.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet.h" +#include "webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +// Flag validator. +static bool ValidatePayloadType(const char* flagname, int32_t value) { + if (value >= 0 && value <= 127) // Value is ok. + return true; + printf("Invalid value for --%s: %d\n", flagname, static_cast(value)); + return false; +} +static bool ValidateExtensionId(const char* flagname, int32_t value) { + if (value > 0 && value <= 255) // Value is ok. + return true; + printf("Invalid value for --%s: %d\n", flagname, static_cast(value)); + return false; +} + +// Define command line flags. +DEFINE_int32(red, 117, "RTP payload type for RED"); +static const bool red_dummy = + google::RegisterFlagValidator(&FLAGS_red, &ValidatePayloadType); +DEFINE_int32(audio_level, 1, "Extension ID for audio level (RFC 6464)"); +static const bool audio_level_dummy = + google::RegisterFlagValidator(&FLAGS_audio_level, &ValidateExtensionId); + +int main(int argc, char* argv[]) { + std::string program_name = argv[0]; + std::string usage = + "Tool for parsing an RTP dump file to text output.\n" + "Run " + + program_name + + " --helpshort for usage.\n" + "Example usage:\n" + + program_name + " input.rtp output.txt\n\n" + + "Output is sent to stdout if no output file is given." + + "Note that this tool can read files with our without payloads."; + google::SetUsageMessage(usage); + google::ParseCommandLineFlags(&argc, &argv, true); + + if (argc != 2 && argc != 3) { + // Print usage information. + printf("%s", google::ProgramUsage()); + return 0; + } + + printf("Input file: %s\n", argv[1]); + webrtc::scoped_ptr file_source( + webrtc::test::RtpFileSource::Create(argv[1])); + assert(file_source.get()); + // Set RTP extension ID. + bool print_audio_level = false; + if (!google::GetCommandLineFlagInfoOrDie("audio_level").is_default) { + print_audio_level = true; + file_source->RegisterRtpHeaderExtension(webrtc::kRtpExtensionAudioLevel, + FLAGS_audio_level); + } + + FILE* out_file; + if (argc == 3) { + out_file = fopen(argv[2], "wt"); + if (!out_file) { + printf("Cannot open output file %s\n", argv[2]); + return -1; + } + printf("Output file: %s\n\n", argv[2]); + } else { + out_file = stdout; + } + + // Print file header. + fprintf(out_file, "SeqNo TimeStamp SendTime Size PT M SSRC"); + if (print_audio_level) { + fprintf(out_file, " AuLvl (V)"); + } + fprintf(out_file, "\n"); + + webrtc::scoped_ptr packet; + while (true) { + packet.reset(file_source->NextPacket()); + if (!packet.get()) { + // End of file reached. + break; + } + // Write packet data to file. + fprintf(out_file, + "%5u %10u %10u %5i %5i %2i %#08X", + packet->header().sequenceNumber, + packet->header().timestamp, + static_cast(packet->time_ms()), + static_cast(packet->packet_length_bytes()), + packet->header().payloadType, + packet->header().markerBit, + packet->header().ssrc); + if (print_audio_level && packet->header().extension.hasAudioLevel) { + // |audioLevel| consists of one bit for "V" and then 7 bits level. + fprintf(out_file, + " %5u (%1i)", + packet->header().extension.audioLevel & 0x7F, + (packet->header().extension.audioLevel & 0x80) == 0 ? 0 : 1); + } + fprintf(out_file, "\n"); + + if (packet->header().payloadType == FLAGS_red) { + std::list red_headers; + packet->ExtractRedHeaders(&red_headers); + while (!red_headers.empty()) { + webrtc::RTPHeader* red = red_headers.front(); + assert(red); + fprintf(out_file, + "* %5u %10u %10u %5i\n", + red->sequenceNumber, + red->timestamp, + static_cast(packet->time_ms()), + red->payloadType); + red_headers.pop_front(); + delete red; + } + } + } + + fclose(out_file); + + return 0; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_file_source.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_file_source.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_file_source.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_file_source.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h" + +#include +#include +#ifdef WIN32 +#include +#else +#include +#endif + +#include "webrtc/base/checks.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet.h" +#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" +#include "webrtc/test/rtp_file_reader.h" + +namespace webrtc { +namespace test { + +RtpFileSource* RtpFileSource::Create(const std::string& file_name) { + RtpFileSource* source = new RtpFileSource(); + CHECK(source->OpenFile(file_name)); + return source; +} + +RtpFileSource::~RtpFileSource() { +} + +bool RtpFileSource::RegisterRtpHeaderExtension(RTPExtensionType type, + uint8_t id) { + assert(parser_.get()); + return parser_->RegisterRtpHeaderExtension(type, id); +} + +Packet* RtpFileSource::NextPacket() { + while (true) { + RtpFileReader::Packet temp_packet; + if (!rtp_reader_->NextPacket(&temp_packet)) { + return NULL; + } + if (temp_packet.length == 0) { + // May be an RTCP packet. + // Read the next one. + continue; + } + scoped_ptr packet_memory(new uint8_t[temp_packet.length]); + memcpy(packet_memory.get(), temp_packet.data, temp_packet.length); + scoped_ptr packet(new Packet(packet_memory.release(), + temp_packet.length, + temp_packet.original_length, + temp_packet.time_ms, + *parser_.get())); + if (!packet->valid_header()) { + assert(false); + return NULL; + } + if (filter_.test(packet->header().payloadType) || + (use_ssrc_filter_ && packet->header().ssrc != ssrc_)) { + // This payload type should be filtered out. Continue to the next packet. + continue; + } + return packet.release(); + } +} + +RtpFileSource::RtpFileSource() + : PacketSource(), + parser_(RtpHeaderParser::Create()) {} + +bool RtpFileSource::OpenFile(const std::string& file_name) { + rtp_reader_.reset(RtpFileReader::Create(RtpFileReader::kRtpDump, file_name)); + if (rtp_reader_) + return true; + rtp_reader_.reset(RtpFileReader::Create(RtpFileReader::kPcap, file_name)); + if (!rtp_reader_) { + FATAL() << "Couldn't open input file as either a rtpdump or .pcap. Note " + "that .pcapng is not supported."; + } + return true; +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RTP_FILE_SOURCE_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RTP_FILE_SOURCE_H_ + +#include +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/common_types.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet_source.h" +#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class RtpHeaderParser; + +namespace test { + +class RtpFileReader; + +class RtpFileSource : public PacketSource { + public: + // Creates an RtpFileSource reading from |file_name|. If the file cannot be + // opened, or has the wrong format, NULL will be returned. + static RtpFileSource* Create(const std::string& file_name); + + virtual ~RtpFileSource(); + + // Registers an RTP header extension and binds it to |id|. + virtual bool RegisterRtpHeaderExtension(RTPExtensionType type, uint8_t id); + + // Returns a pointer to the next packet. Returns NULL if end of file was + // reached, or if a the data was corrupt. + virtual Packet* NextPacket() OVERRIDE; + + private: + static const int kFirstLineLength = 40; + static const int kRtpFileHeaderSize = 4 + 4 + 4 + 2 + 2; + static const size_t kPacketHeaderSize = 8; + + RtpFileSource(); + + bool OpenFile(const std::string& file_name); + + scoped_ptr rtp_reader_; + scoped_ptr parser_; + + DISALLOW_COPY_AND_ASSIGN(RtpFileSource); +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RTP_FILE_SOURCE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_generator.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_generator.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_generator.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_generator.cc 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h" + +namespace webrtc { +namespace test { + +uint32_t RtpGenerator::GetRtpHeader(uint8_t payload_type, + size_t payload_length_samples, + WebRtcRTPHeader* rtp_header) { + assert(rtp_header); + if (!rtp_header) { + return 0; + } + rtp_header->header.sequenceNumber = seq_number_++; + rtp_header->header.timestamp = timestamp_; + timestamp_ += static_cast(payload_length_samples); + rtp_header->header.payloadType = payload_type; + rtp_header->header.markerBit = false; + rtp_header->header.ssrc = ssrc_; + rtp_header->header.numCSRCs = 0; + rtp_header->frameType = kAudioFrameSpeech; + + uint32_t this_send_time = next_send_time_ms_; + assert(samples_per_ms_ > 0); + next_send_time_ms_ += ((1.0 + drift_factor_) * payload_length_samples) / + samples_per_ms_; + return this_send_time; +} + +void RtpGenerator::set_drift_factor(double factor) { + if (factor > -1.0) { + drift_factor_ = factor; + } +} + +uint32_t TimestampJumpRtpGenerator::GetRtpHeader(uint8_t payload_type, + size_t payload_length_samples, + WebRtcRTPHeader* rtp_header) { + uint32_t ret = RtpGenerator::GetRtpHeader( + payload_type, payload_length_samples, rtp_header); + if (timestamp_ - static_cast(payload_length_samples) <= + jump_from_timestamp_ && + timestamp_ > jump_from_timestamp_) { + // We just moved across the |jump_from_timestamp_| timestamp. Do the jump. + timestamp_ = jump_to_timestamp_; + } + return ret; +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_generator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_generator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_generator.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/tools/rtp_generator.h 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RTP_GENERATOR_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RTP_GENERATOR_H_ + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace test { + +// Class for generating RTP headers. +class RtpGenerator { + public: + RtpGenerator(int samples_per_ms, + uint16_t start_seq_number = 0, + uint32_t start_timestamp = 0, + uint32_t start_send_time_ms = 0, + uint32_t ssrc = 0x12345678) + : seq_number_(start_seq_number), + timestamp_(start_timestamp), + next_send_time_ms_(start_send_time_ms), + ssrc_(ssrc), + samples_per_ms_(samples_per_ms), + drift_factor_(0.0) { + } + + virtual ~RtpGenerator() {} + + // Writes the next RTP header to |rtp_header|, which will be of type + // |payload_type|. Returns the send time for this packet (in ms). The value of + // |payload_length_samples| determines the send time for the next packet. + virtual uint32_t GetRtpHeader(uint8_t payload_type, + size_t payload_length_samples, + WebRtcRTPHeader* rtp_header); + + void set_drift_factor(double factor); + + protected: + uint16_t seq_number_; + uint32_t timestamp_; + uint32_t next_send_time_ms_; + const uint32_t ssrc_; + const int samples_per_ms_; + double drift_factor_; + + private: + DISALLOW_COPY_AND_ASSIGN(RtpGenerator); +}; + +class TimestampJumpRtpGenerator : public RtpGenerator { + public: + TimestampJumpRtpGenerator(int samples_per_ms, + uint16_t start_seq_number, + uint32_t start_timestamp, + uint32_t jump_from_timestamp, + uint32_t jump_to_timestamp) + : RtpGenerator(samples_per_ms, start_seq_number, start_timestamp), + jump_from_timestamp_(jump_from_timestamp), + jump_to_timestamp_(jump_to_timestamp) {} + + uint32_t GetRtpHeader(uint8_t payload_type, + size_t payload_length_samples, + WebRtcRTPHeader* rtp_header) OVERRIDE; + + private: + uint32_t jump_from_timestamp_; + uint32_t jump_to_timestamp_; + DISALLOW_COPY_AND_ASSIGN(TimestampJumpRtpGenerator); +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RTP_GENERATOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/unmute_signal.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/unmute_signal.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/unmute_signal.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/unmute_signal.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This function "unmutes" a vector on a sample by sample basis. - */ - -#include "dsp_helpfunctions.h" - -#include "signal_processing_library.h" - - -void WebRtcNetEQ_UnmuteSignal(int16_t *pw16_inVec, int16_t *startMuteFact, - int16_t *pw16_outVec, int16_t unmuteFact, - int16_t N) -{ - int i; - uint16_t w16_tmp; - int32_t w32_tmp; - - w16_tmp = (uint16_t) *startMuteFact; - w32_tmp = WEBRTC_SPL_LSHIFT_W32((int32_t)w16_tmp,6) + 32; - for (i = 0; i < N; i++) - { - pw16_outVec[i] - = (int16_t) ((WEBRTC_SPL_MUL_16_16(w16_tmp, pw16_inVec[i]) + 8192) >> 14); - w32_tmp += unmuteFact; - w32_tmp = WEBRTC_SPL_MAX(0, w32_tmp); - w16_tmp = (uint16_t) WEBRTC_SPL_RSHIFT_W32(w32_tmp, 6); /* 20 - 14 = 6 */ - w16_tmp = WEBRTC_SPL_MIN(16384, w16_tmp); - } - *startMuteFact = (int16_t) w16_tmp; -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/webrtc_neteq.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/webrtc_neteq.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/webrtc_neteq.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/webrtc_neteq.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1769 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Implementation of main NetEQ API. - */ - -#include "webrtc_neteq.h" -#include "webrtc_neteq_internal.h" - -#include -#include - -#include "typedefs.h" -#include "signal_processing_library.h" - -#include "neteq_error_codes.h" -#include "mcu_dsp_common.h" -#include "rtcp.h" - -#define RETURN_ON_ERROR( macroExpr, macroInstPtr ) { \ - if ((macroExpr) != 0) { \ - if ((macroExpr) == -1) { \ - (macroInstPtr)->ErrorCode = - (NETEQ_OTHER_ERROR); \ - } else { \ - (macroInstPtr)->ErrorCode = -((int16_t) (macroExpr)); \ - } \ - return(-1); \ - } } - -int WebRtcNetEQ_strncpy(char *strDest, int numberOfElements, - const char *strSource, int count) -{ - /* check vector lengths */ - if (count > numberOfElements) - { - strDest[0] = '\0'; - return (-1); - } - else - { - strncpy(strDest, strSource, count); - return (0); - } -} - -/********************************************************** - * NETEQ Functions - */ - -/***************************************** - * Error functions - */ - -int WebRtcNetEQ_GetErrorCode(void *inst) -{ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - return (NetEqMainInst->ErrorCode); -} - -int WebRtcNetEQ_GetErrorName(int errorCode, char *errorName, int maxStrLen) -{ - if ((errorName == NULL) || (maxStrLen <= 0)) - { - return (-1); - } - - if (errorCode < 0) - { - errorCode = -errorCode; // absolute value - } - - switch (errorCode) - { - case 1: // could be -1 - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "OTHER_ERROR", maxStrLen); - break; - } - case 1001: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "FAULTY_INSTRUCTION", maxStrLen); - break; - } - case 1002: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "FAULTY_NETWORK_TYPE", maxStrLen); - break; - } - case 1003: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "FAULTY_DELAYVALUE", maxStrLen); - break; - } - case 1004: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "FAULTY_PLAYOUTMODE", maxStrLen); - break; - } - case 1005: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "CORRUPT_INSTANCE", maxStrLen); - break; - } - case 1006: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "ILLEGAL_MASTER_SLAVE_SWITCH", maxStrLen); - break; - } - case 1007: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "MASTER_SLAVE_ERROR", maxStrLen); - break; - } - case 2001: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "UNKNOWN_BUFSTAT_DECISION", maxStrLen); - break; - } - case 2002: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RECOUT_ERROR_DECODING", maxStrLen); - break; - } - case 2003: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RECOUT_ERROR_SAMPLEUNDERRUN", maxStrLen); - break; - } - case 2004: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RECOUT_ERROR_DECODED_TOO_MUCH", - maxStrLen); - break; - } - case 3001: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RECIN_CNG_ERROR", maxStrLen); - break; - } - case 3002: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RECIN_UNKNOWNPAYLOAD", maxStrLen); - break; - } - case 3003: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RECIN_BUFFERINSERT_ERROR", maxStrLen); - break; - } - case 4001: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "PBUFFER_INIT_ERROR", maxStrLen); - break; - } - case 4002: - case 4003: - case 4004: - case 4005: - case 4006: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "PBUFFER_INSERT_ERROR1", maxStrLen); - break; - } - case 4007: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "UNKNOWN_G723_HEADER", maxStrLen); - break; - } - case 4008: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "PBUFFER_NONEXISTING_PACKET", maxStrLen); - break; - } - case 4009: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "PBUFFER_NOT_INITIALIZED", maxStrLen); - break; - } - case 4010: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "AMBIGUOUS_ILBC_FRAME_SIZE", maxStrLen); - break; - } - case 5001: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "CODEC_DB_FULL", maxStrLen); - break; - } - case 5002: - case 5003: - case 5004: - case 5005: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "CODEC_DB_NOT_EXIST", maxStrLen); - break; - } - case 5006: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "CODEC_DB_UNKNOWN_CODEC", maxStrLen); - break; - } - case 5007: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "CODEC_DB_PAYLOAD_TAKEN", maxStrLen); - break; - } - case 5008: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "CODEC_DB_UNSUPPORTED_CODEC", maxStrLen); - break; - } - case 5009: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "CODEC_DB_UNSUPPORTED_FS", maxStrLen); - break; - } - case 6001: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "DTMF_DEC_PARAMETER_ERROR", maxStrLen); - break; - } - case 6002: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "DTMF_INSERT_ERROR", maxStrLen); - break; - } - case 6003: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "DTMF_GEN_UNKNOWN_SAMP_FREQ", maxStrLen); - break; - } - case 6004: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "DTMF_NOT_SUPPORTED", maxStrLen); - break; - } - case 7001: - case 7002: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RED_SPLIT_ERROR", maxStrLen); - break; - } - case 7003: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RTP_TOO_SHORT_PACKET", maxStrLen); - break; - } - case 7004: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RTP_CORRUPT_PACKET", maxStrLen); - break; - } - default: - { - /* check for decoder error ranges */ - if (errorCode >= 6010 && errorCode <= 6810) - { - /* iSAC error code */ - WebRtcNetEQ_strncpy(errorName, maxStrLen, "iSAC ERROR", maxStrLen); - break; - } - - WebRtcNetEQ_strncpy(errorName, maxStrLen, "UNKNOWN_ERROR", maxStrLen); - return (-1); - } - } - - return (0); -} - -/* Assign functions (create not allowed in order to avoid malloc in lib) */ -int WebRtcNetEQ_AssignSize(int *sizeinbytes) -{ - *sizeinbytes = (sizeof(MainInst_t) * 2) / sizeof(int16_t); - return (0); -} - -int WebRtcNetEQ_Assign(void **inst, void *NETEQ_inst_Addr) -{ - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) NETEQ_inst_Addr; - *inst = NETEQ_inst_Addr; - if (*inst == NULL) return (-1); - - WebRtcSpl_Init(); - - /* Clear memory */ - WebRtcSpl_MemSetW16((int16_t*) NetEqMainInst, 0, - (sizeof(MainInst_t) / sizeof(int16_t))); - ok = WebRtcNetEQ_McuReset(&NetEqMainInst->MCUinst); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (0); -} - -int WebRtcNetEQ_GetRecommendedBufferSize(void *inst, const enum WebRtcNetEQDecoder *codec, - int noOfCodecs, enum WebRtcNetEQNetworkType nwType, - int *MaxNoOfPackets, int *sizeinbytes, - int* per_packet_overhead_bytes) -{ - int ok = 0; - int multiplier; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - *MaxNoOfPackets = 0; - *sizeinbytes = 0; - ok = WebRtcNetEQ_GetDefaultCodecSettings(codec, noOfCodecs, sizeinbytes, - MaxNoOfPackets, - per_packet_overhead_bytes); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - if (nwType == kUDPNormal) - { - multiplier = 1; - } - else if (nwType == kUDPVideoSync) - { - multiplier = 4; - } - else if (nwType == kTCPNormal) - { - multiplier = 4; - } - else if (nwType == kTCPLargeJitter) - { - multiplier = 8; - } - else if (nwType == kTCPXLargeJitter) - { - multiplier = 12; - } - else - { - NetEqMainInst->ErrorCode = -FAULTY_NETWORK_TYPE; - return (-1); - } - *MaxNoOfPackets = (*MaxNoOfPackets) * multiplier; - *sizeinbytes = (*sizeinbytes) * multiplier; - return 0; -} - -int WebRtcNetEQ_AssignBuffer(void *inst, int MaxNoOfPackets, void *NETEQ_Buffer_Addr, - int sizeinbytes) -{ - int ok; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - ok = WebRtcNetEQ_PacketBufferInit(&NetEqMainInst->MCUinst.PacketBuffer_inst, - MaxNoOfPackets, (int16_t*) NETEQ_Buffer_Addr, (sizeinbytes >> 1)); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -/************************************************ - * Init functions - */ - -/**************************************************************************** - * WebRtcNetEQ_Init(...) - * - * Initialize NetEQ. - * - * Input: - * - inst : NetEQ instance - * - fs : Initial sample rate in Hz (may change with payload) - * - * Output: - * - inst : Initialized NetEQ instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_Init(void *inst, uint16_t fs) -{ - int ok = 0; - - /* Typecast inst to internal instance format */ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - - if (NetEqMainInst == NULL) - { - return (-1); - } - -#ifdef NETEQ_VAD - /* Start out with no PostDecode VAD instance */ - NetEqMainInst->DSPinst.VADInst.VADState = NULL; - /* Also set all VAD function pointers to NULL */ - NetEqMainInst->DSPinst.VADInst.initFunction = NULL; - NetEqMainInst->DSPinst.VADInst.setmodeFunction = NULL; - NetEqMainInst->DSPinst.VADInst.VADFunction = NULL; -#endif /* NETEQ_VAD */ - - ok = WebRtcNetEQ_DSPinit(NetEqMainInst); /* Init addresses between MCU and DSP */ - RETURN_ON_ERROR(ok, NetEqMainInst); - - ok = WebRtcNetEQ_DSPInit(&NetEqMainInst->DSPinst, fs); /* Init dsp side */ - RETURN_ON_ERROR(ok, NetEqMainInst); - /* set BGN mode to default, since it is not cleared by DSP init function */ - NetEqMainInst->DSPinst.BGNInst.bgnMode = BGN_ON; - - /* init statistics functions and counters */ - ok = WebRtcNetEQ_ClearInCallStats(&NetEqMainInst->DSPinst); - RETURN_ON_ERROR(ok, NetEqMainInst); - ok = WebRtcNetEQ_ClearPostCallStats(&NetEqMainInst->DSPinst); - RETURN_ON_ERROR(ok, NetEqMainInst); - ok = WebRtcNetEQ_ResetMcuJitterStat(&NetEqMainInst->MCUinst); - RETURN_ON_ERROR(ok, NetEqMainInst); - - /* flush packet buffer */ - ok = WebRtcNetEQ_PacketBufferFlush(&NetEqMainInst->MCUinst.PacketBuffer_inst); - RETURN_ON_ERROR(ok, NetEqMainInst); - - /* set some variables to initial values */ - NetEqMainInst->MCUinst.current_Codec = -1; - NetEqMainInst->MCUinst.current_Payload = -1; - NetEqMainInst->MCUinst.first_packet = 1; - NetEqMainInst->MCUinst.one_desc = 0; - NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.extraDelayMs = 0; - NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.minimum_delay_ms = 0; - NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.maximum_delay_ms = - 10000; - NetEqMainInst->MCUinst.NoOfExpandCalls = 0; - NetEqMainInst->MCUinst.fs = fs; - - /* Not in AV-sync by default. */ - NetEqMainInst->MCUinst.av_sync = 0; - -#ifdef NETEQ_ATEVENT_DECODE - /* init DTMF decoder */ - ok = WebRtcNetEQ_DtmfDecoderInit(&(NetEqMainInst->MCUinst.DTMF_inst),fs,560); - RETURN_ON_ERROR(ok, NetEqMainInst); -#endif - - /* init RTCP statistics */ - WebRtcNetEQ_RTCPInit(&(NetEqMainInst->MCUinst.RTCP_inst), 0); - - /* set BufferStat struct to zero */ - WebRtcSpl_MemSetW16((int16_t*) &(NetEqMainInst->MCUinst.BufferStat_inst), 0, - sizeof(BufstatsInst_t) / sizeof(int16_t)); - - /* reset automode */ - WebRtcNetEQ_ResetAutomode(&(NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst), - NetEqMainInst->MCUinst.PacketBuffer_inst.maxInsertPositions); - - NetEqMainInst->ErrorCode = 0; - -#ifdef NETEQ_STEREO - /* set master/slave info to undecided */ - NetEqMainInst->masterSlave = 0; -#endif - - /* Set to an invalid value. */ - NetEqMainInst->MCUinst.decoded_packet_sequence_number = -1; - NetEqMainInst->MCUinst.decoded_packet_timestamp = 0; - - return (ok); -} - -int WebRtcNetEQ_FlushBuffers(void *inst) -{ - int ok = 0; - - /* Typecast inst to internal instance format */ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - - if (NetEqMainInst == NULL) - { - return (-1); - } - - /* Flush packet buffer */ - ok = WebRtcNetEQ_PacketBufferFlush(&NetEqMainInst->MCUinst.PacketBuffer_inst); - RETURN_ON_ERROR(ok, NetEqMainInst); - - /* Set MCU to wait for new codec */ - NetEqMainInst->MCUinst.first_packet = 1; - - /* Flush speech buffer */ - ok = WebRtcNetEQ_FlushSpeechBuffer(&NetEqMainInst->DSPinst); - RETURN_ON_ERROR(ok, NetEqMainInst); - - return 0; -} - -int WebRtcNetEQ_SetAVTPlayout(void *inst, int PlayoutAVTon) -{ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); -#ifdef NETEQ_ATEVENT_DECODE - NetEqMainInst->MCUinst.AVT_PlayoutOn = PlayoutAVTon; - return(0); -#else - if (PlayoutAVTon != 0) - { - NetEqMainInst->ErrorCode = -DTMF_NOT_SUPPORTED; - return (-1); - } - else - { - return (0); - } -#endif -} - -int WebRtcNetEQ_SetExtraDelay(void *inst, int DelayInMs) { - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - if ((DelayInMs < 0) || (DelayInMs > 10000)) { - NetEqMainInst->ErrorCode = -FAULTY_DELAYVALUE; - return (-1); - } - NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.extraDelayMs = DelayInMs; - return (0); -} - -int WebRtcNetEQ_SetMinimumDelay(void *inst, int minimum_delay_ms) { - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return -1; - if (minimum_delay_ms < 0 || minimum_delay_ms > 10000) { - NetEqMainInst->ErrorCode = -FAULTY_DELAYVALUE; - return -1; - } - if ((NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.maximum_delay_ms > - 0) && (minimum_delay_ms > - NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.maximum_delay_ms)) { - NetEqMainInst->ErrorCode = -FAULTY_DELAYVALUE; - return -1; - } - NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.minimum_delay_ms = - minimum_delay_ms; - return 0; -} - -int WebRtcNetEQ_SetMaximumDelay(void *inst, int maximum_delay_ms) { - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return -1; - if (maximum_delay_ms < 0 || maximum_delay_ms > 10000) { - NetEqMainInst->ErrorCode = -FAULTY_DELAYVALUE; - return -1; - } - if (maximum_delay_ms < - NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.minimum_delay_ms) { - NetEqMainInst->ErrorCode = -FAULTY_DELAYVALUE; - return -1; - } - NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.maximum_delay_ms = - maximum_delay_ms; - return 0; -} - -int WebRtcNetEQ_SetPlayoutMode(void *inst, enum WebRtcNetEQPlayoutMode playoutMode) -{ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - if ((playoutMode != kPlayoutOn) && (playoutMode != kPlayoutOff) && (playoutMode - != kPlayoutFax) && (playoutMode != kPlayoutStreaming)) - { - NetEqMainInst->ErrorCode = -FAULTY_PLAYOUTMODE; - return (-1); - } - else - { - NetEqMainInst->MCUinst.NetEqPlayoutMode = playoutMode; - return (0); - } -} - -int WebRtcNetEQ_SetBGNMode(void *inst, enum WebRtcNetEQBGNMode bgnMode) -{ - - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - - /* Instance sanity */ - if (NetEqMainInst == NULL) return (-1); - - /* Check for corrupt/cleared instance */ - if (NetEqMainInst->MCUinst.main_inst != NetEqMainInst) - { - /* Instance is corrupt */ - NetEqMainInst->ErrorCode = CORRUPT_INSTANCE; - return (-1); - } - - NetEqMainInst->DSPinst.BGNInst.bgnMode = (enum BGNMode) bgnMode; - - return (0); -} - -int WebRtcNetEQ_GetBGNMode(const void *inst, enum WebRtcNetEQBGNMode *bgnMode) -{ - - const MainInst_t *NetEqMainInst = (const MainInst_t*) inst; - - /* Instance sanity */ - if (NetEqMainInst == NULL) return (-1); - - *bgnMode = (enum WebRtcNetEQBGNMode) NetEqMainInst->DSPinst.BGNInst.bgnMode; - - return (0); -} - -/************************************************ - * CodecDB functions - */ - -int WebRtcNetEQ_CodecDbReset(void *inst) -{ - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - ok = WebRtcNetEQ_DbReset(&NetEqMainInst->MCUinst.codec_DB_inst); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - - /* set function pointers to NULL to prevent RecOut from using the codec */ - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecode = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecodeRCU = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcAddLatePkt = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecode = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecodeInit = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecodePLC = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcGetMDinfo = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcUpdBWEst = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcGetErrorCode = NULL; - - return (0); -} - -int WebRtcNetEQ_CodecDbGetSizeInfo(void *inst, int16_t *UsedEntries, - int16_t *MaxEntries) -{ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - *MaxEntries = NUM_CODECS; - *UsedEntries = NetEqMainInst->MCUinst.codec_DB_inst.nrOfCodecs; - return (0); -} - -int WebRtcNetEQ_CodecDbGetCodecInfo(void *inst, int16_t Entry, - enum WebRtcNetEQDecoder *codec) -{ - int i; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - *codec = (enum WebRtcNetEQDecoder) 0; - if ((Entry >= 0) && (Entry < NetEqMainInst->MCUinst.codec_DB_inst.nrOfCodecs)) - { - for (i = 0; i < NUM_TOTAL_CODECS; i++) - { - if (NetEqMainInst->MCUinst.codec_DB_inst.position[i] == Entry) - { - *codec = (enum WebRtcNetEQDecoder) i; - } - } - } - else - { - NetEqMainInst->ErrorCode = -(CODEC_DB_NOT_EXIST1); - return (-1); - } - return (0); -} - -int WebRtcNetEQ_CodecDbAdd(void *inst, WebRtcNetEQ_CodecDef *codecInst) -{ - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - ok = WebRtcNetEQ_DbAdd(&NetEqMainInst->MCUinst.codec_DB_inst, codecInst->codec, - codecInst->payloadType, codecInst->funcDecode, codecInst->funcDecodeRCU, - codecInst->funcDecodePLC, codecInst->funcDecodeInit, codecInst->funcAddLatePkt, - codecInst->funcGetMDinfo, codecInst->funcGetPitch, codecInst->funcUpdBWEst, - codecInst->funcDurationEst, codecInst->funcGetErrorCode, - codecInst->codec_state, codecInst->codec_fs); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -int WebRtcNetEQ_CodecDbRemove(void *inst, enum WebRtcNetEQDecoder codec) -{ - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - - /* check if currently used codec is being removed */ - if (NetEqMainInst->MCUinst.current_Codec == (int16_t) codec) - { - /* set function pointers to NULL to prevent RecOut from using the codec */ - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecode = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecodeRCU = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcAddLatePkt = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecode = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecodeInit = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecodePLC = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcGetMDinfo = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcUpdBWEst = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcGetErrorCode = NULL; - } - - ok = WebRtcNetEQ_DbRemove(&NetEqMainInst->MCUinst.codec_DB_inst, codec); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -/********************************* - * Real-time functions - */ - -int WebRtcNetEQ_RecIn(void *inst, int16_t *p_w16datagramstart, int16_t w16_RTPlen, - uint32_t uw32_timeRec) -{ - int ok = 0; - RTPPacket_t RTPpacket; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - - /* Check for corrupt/cleared instance */ - if (NetEqMainInst->MCUinst.main_inst != NetEqMainInst) - { - /* Instance is corrupt */ - NetEqMainInst->ErrorCode = CORRUPT_INSTANCE; - return (-1); - } - - /* Parse RTP header */ - ok = WebRtcNetEQ_RTPPayloadInfo(p_w16datagramstart, w16_RTPlen, &RTPpacket); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - - ok = WebRtcNetEQ_RecInInternal(&NetEqMainInst->MCUinst, &RTPpacket, uw32_timeRec); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -/**************************************************************************** - * WebRtcNetEQ_RecInRTPStruct(...) - * - * Alternative RecIn function, used when the RTP data has already been - * parsed into an RTP info struct (WebRtcNetEQ_RTPInfo). - * - * Input: - * - inst : NetEQ instance - * - rtpInfo : Pointer to RTP info - * - payloadPtr : Pointer to the RTP payload (first byte after header) - * - payloadLenBytes : Length (in bytes) of the payload in payloadPtr - * - timeRec : Receive time (in timestamps of the used codec) - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNetEQ_RecInRTPStruct(void *inst, WebRtcNetEQ_RTPInfo *rtpInfo, - const uint8_t *payloadPtr, int16_t payloadLenBytes, - uint32_t uw32_timeRec) -{ - int ok = 0; - RTPPacket_t RTPpacket; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) - { - return (-1); - } - - /* Check for corrupt/cleared instance */ - if (NetEqMainInst->MCUinst.main_inst != NetEqMainInst) - { - /* Instance is corrupt */ - NetEqMainInst->ErrorCode = CORRUPT_INSTANCE; - return (-1); - } - - /* Load NetEQ's RTP struct from Module RTP struct */ - RTPpacket.payloadType = rtpInfo->payloadType; - RTPpacket.seqNumber = rtpInfo->sequenceNumber; - RTPpacket.timeStamp = rtpInfo->timeStamp; - RTPpacket.ssrc = rtpInfo->SSRC; - RTPpacket.payload = (const int16_t*) payloadPtr; - RTPpacket.payloadLen = payloadLenBytes; - RTPpacket.starts_byte1 = 0; - - ok = WebRtcNetEQ_RecInInternal(&NetEqMainInst->MCUinst, &RTPpacket, uw32_timeRec); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -int WebRtcNetEQ_RecOut(void *inst, int16_t *pw16_outData, int16_t *pw16_len) -{ - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; -#ifdef NETEQ_STEREO - MasterSlaveInfo msInfo; - msInfo.msMode = NETEQ_MONO; -#endif - - if (NetEqMainInst == NULL) return (-1); - - /* Check for corrupt/cleared instance */ - if (NetEqMainInst->DSPinst.main_inst != NetEqMainInst) - { - /* Instance is corrupt */ - NetEqMainInst->ErrorCode = CORRUPT_INSTANCE; - return (-1); - } - -#ifdef NETEQ_STEREO - NetEqMainInst->DSPinst.msInfo = &msInfo; -#endif - - ok = WebRtcNetEQ_RecOutInternal(&NetEqMainInst->DSPinst, pw16_outData, - pw16_len, 0 /* not BGN only */, NetEqMainInst->MCUinst.av_sync); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -/**************************************************************************** - * WebRtcNetEQ_RecOutMasterSlave(...) - * - * RecOut function for running several NetEQ instances in master/slave mode. - * One master can be used to control several slaves. - * - * Input: - * - inst : NetEQ instance - * - isMaster : Non-zero indicates that this is the master channel - * - msInfo : (slave only) Information from master - * - * Output: - * - inst : Updated NetEQ instance - * - pw16_outData : Pointer to vector where output should be written - * - pw16_len : Pointer to variable where output length is returned - * - msInfo : (master only) Information to slave(s) - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RecOutMasterSlave(void *inst, int16_t *pw16_outData, - int16_t *pw16_len, void *msInfo, - int16_t isMaster) -{ -#ifndef NETEQ_STEREO - /* Stereo not supported */ - return(-1); -#else - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - - if (NetEqMainInst == NULL) return (-1); - - /* Check for corrupt/cleared instance */ - if (NetEqMainInst->DSPinst.main_inst != NetEqMainInst) - { - /* Instance is corrupt */ - NetEqMainInst->ErrorCode = CORRUPT_INSTANCE; - return (-1); - } - - if (msInfo == NULL) - { - /* msInfo not provided */ - NetEqMainInst->ErrorCode = NETEQ_OTHER_ERROR; - return (-1); - } - - /* translate from external to internal Master/Slave information */ - NetEqMainInst->DSPinst.msInfo = (MasterSlaveInfo *) msInfo; - - /* check that we have not done a master/slave switch without first re-initializing */ - if ((NetEqMainInst->masterSlave == 1 && !isMaster) || /* switch from master to slave */ - (NetEqMainInst->masterSlave == 2 && isMaster)) /* switch from slave to master */ - { - NetEqMainInst->ErrorCode = ILLEGAL_MASTER_SLAVE_SWITCH; - return (-1); - } - - if (!isMaster) - { - /* this is the slave */ - NetEqMainInst->masterSlave = 2; - NetEqMainInst->DSPinst.msInfo->msMode = NETEQ_SLAVE; - } - else - { - NetEqMainInst->DSPinst.msInfo->msMode = NETEQ_MASTER; - } - - ok = WebRtcNetEQ_RecOutInternal(&NetEqMainInst->DSPinst, pw16_outData, - pw16_len, 0 /* not BGN only */, NetEqMainInst->MCUinst.av_sync); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - - if (isMaster) - { - /* this is the master */ - NetEqMainInst->masterSlave = 1; - } - - return (ok); -#endif -} - -int WebRtcNetEQ_GetMasterSlaveInfoSize() -{ -#ifdef NETEQ_STEREO - return (sizeof(MasterSlaveInfo)); -#else - return(-1); -#endif -} - -/* Special RecOut that does not do any decoding. */ -int WebRtcNetEQ_RecOutNoDecode(void *inst, int16_t *pw16_outData, - int16_t *pw16_len) -{ - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; -#ifdef NETEQ_STEREO - MasterSlaveInfo msInfo; -#endif - - if (NetEqMainInst == NULL) return (-1); - - /* Check for corrupt/cleared instance */ - if (NetEqMainInst->DSPinst.main_inst != NetEqMainInst) - { - /* Instance is corrupt */ - NetEqMainInst->ErrorCode = CORRUPT_INSTANCE; - return (-1); - } - -#ifdef NETEQ_STEREO - /* keep same mode as before */ - switch (NetEqMainInst->masterSlave) - { - case 1: - { - msInfo.msMode = NETEQ_MASTER; - break; - } - case 2: - { - msInfo.msMode = NETEQ_SLAVE; - break; - } - default: - { - msInfo.msMode = NETEQ_MONO; - break; - } - } - - NetEqMainInst->DSPinst.msInfo = &msInfo; -#endif - - ok = WebRtcNetEQ_RecOutInternal(&NetEqMainInst->DSPinst, pw16_outData, - pw16_len, 1 /* BGN only */, NetEqMainInst->MCUinst.av_sync); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -int WebRtcNetEQ_GetRTCPStats(void *inst, WebRtcNetEQ_RTCPStat *RTCP_inst) -{ - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - ok = WebRtcNetEQ_RTCPGetStats(&NetEqMainInst->MCUinst.RTCP_inst, - &RTCP_inst->fraction_lost, &RTCP_inst->cum_lost, &RTCP_inst->ext_max, - &RTCP_inst->jitter, 0); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -int WebRtcNetEQ_GetRTCPStatsNoReset(void *inst, WebRtcNetEQ_RTCPStat *RTCP_inst) -{ - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - ok = WebRtcNetEQ_RTCPGetStats(&NetEqMainInst->MCUinst.RTCP_inst, - &RTCP_inst->fraction_lost, &RTCP_inst->cum_lost, &RTCP_inst->ext_max, - &RTCP_inst->jitter, 1); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -int WebRtcNetEQ_GetSpeechTimeStamp(void *inst, uint32_t *timestamp) -{ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - - if (NetEqMainInst->MCUinst.TSscalingInitialized) - { - *timestamp = WebRtcNetEQ_ScaleTimestampInternalToExternal(&NetEqMainInst->MCUinst, - NetEqMainInst->DSPinst.videoSyncTimestamp); - } - else - { - *timestamp = NetEqMainInst->DSPinst.videoSyncTimestamp; - } - - return (0); -} - -/**************************************************************************** - * WebRtcNetEQ_GetSpeechOutputType(...) - * - * Get the output type for the audio provided by the latest call to - * WebRtcNetEQ_RecOut(). - * - * kOutputNormal = normal audio (possibly processed) - * kOutputPLC = loss concealment through stretching audio - * kOutputCNG = comfort noise (codec-internal or RFC3389) - * kOutputPLCtoCNG = background noise only due to long expand or error - * kOutputVADPassive = PostDecode VAD signalling passive speaker - * - * Input: - * - inst : NetEQ instance - * - * Output: - * - outputType : Output type from enum list WebRtcNetEQOutputType - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_GetSpeechOutputType(void *inst, enum WebRtcNetEQOutputType *outputType) -{ - /* Typecast to internal instance type */ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - - if (NetEqMainInst == NULL) - { - return (-1); - } - - if ((NetEqMainInst->DSPinst.w16_mode & MODE_BGN_ONLY) != 0) - { - /* If last mode was background noise only */ - *outputType = kOutputPLCtoCNG; - - } - else if ((NetEqMainInst->DSPinst.w16_mode == MODE_CODEC_INTERNAL_CNG) - || (NetEqMainInst->DSPinst.w16_mode == MODE_RFC3389CNG)) - { - /* If CN or internal CNG */ - *outputType = kOutputCNG; - - } - else if ((NetEqMainInst->DSPinst.w16_mode == MODE_EXPAND) - && (NetEqMainInst->DSPinst.ExpandInst.w16_expandMuteFactor == 0)) - { - /* Expand mode has faded down to background noise only (very long expand) */ - *outputType = kOutputPLCtoCNG; - - } - else if (NetEqMainInst->DSPinst.w16_mode == MODE_EXPAND) - { - /* PLC mode */ - *outputType = kOutputPLC; - -#ifdef NETEQ_VAD - } - else if ( NetEqMainInst->DSPinst.VADInst.VADDecision == 0 ) - { - /* post-decode VAD says passive speaker */ - *outputType = kOutputVADPassive; -#endif /* NETEQ_VAD */ - - } - else - { - /* Normal speech output type (can still be manipulated, e.g., accelerated) */ - *outputType = kOutputNormal; - } - - return (0); -} - -/********************************** - * Functions related to VQmon - */ - -#define WEBRTC_NETEQ_CONCEALMENTFLAG_LOST 0x01 -#define WEBRTC_NETEQ_CONCEALMENTFLAG_DISCARDED 0x02 -#define WEBRTC_NETEQ_CONCEALMENTFLAG_SUPRESS 0x04 -#define WEBRTC_NETEQ_CONCEALMENTFLAG_CNGACTIVE 0x80 - -int WebRtcNetEQ_VQmonRecOutStatistics(void *inst, uint16_t *validVoiceDurationMs, - uint16_t *concealedVoiceDurationMs, - uint8_t *concealedVoiceFlags) -{ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - int16_t fs_mult; - int16_t ms_lost; - if (NetEqMainInst == NULL) return (-1); - fs_mult = WebRtcSpl_DivW32W16ResW16(NetEqMainInst->MCUinst.fs, 8000); - - ms_lost = WebRtcSpl_DivW32W16ResW16( - (int32_t) NetEqMainInst->DSPinst.w16_concealedTS, (int16_t) (8 * fs_mult)); - if (ms_lost > NetEqMainInst->DSPinst.millisecondsPerCall) ms_lost - = NetEqMainInst->DSPinst.millisecondsPerCall; - - *validVoiceDurationMs = NetEqMainInst->DSPinst.millisecondsPerCall - ms_lost; - *concealedVoiceDurationMs = ms_lost; - if (ms_lost > 0) - { - *concealedVoiceFlags = WEBRTC_NETEQ_CONCEALMENTFLAG_LOST; - } - else - { - *concealedVoiceFlags = 0; - } - NetEqMainInst->DSPinst.w16_concealedTS -= ms_lost * (8 * fs_mult); - - return (0); -} - -int WebRtcNetEQ_VQmonGetConfiguration(void *inst, uint16_t *absMaxDelayMs, - uint8_t *adaptationRate) -{ - /* Dummy check the inst, just to avoid compiler warnings. */ - if (inst == NULL) - { - /* Do nothing. */ - } - - /* Hardcoded variables that are used for VQmon as jitter buffer parameters */ - *absMaxDelayMs = 240; - *adaptationRate = 1; - return (0); -} - -int WebRtcNetEQ_VQmonGetRxStatistics(void *inst, uint16_t *avgDelayMs, - uint16_t *maxDelayMs) -{ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - *avgDelayMs = (uint16_t) (NetEqMainInst->MCUinst.BufferStat_inst.avgDelayMsQ8 >> 8); - *maxDelayMs = (uint16_t) NetEqMainInst->MCUinst.BufferStat_inst.maxDelayMs; - return (0); -} - -/************************************* - * Statistics functions - */ - -/* Get the "in-call" statistics from NetEQ. - * The statistics are reset after the query. */ -int WebRtcNetEQ_GetNetworkStatistics(void *inst, WebRtcNetEQ_NetworkStatistics *stats) - -{ - - uint16_t tempU16; - uint32_t tempU32, tempU32_2; - int numShift; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - - /* Instance sanity */ - if (NetEqMainInst == NULL) return (-1); - - stats->addedSamples = NetEqMainInst->DSPinst.statInst.addedSamples; - - /*******************/ - /* Get buffer size */ - /*******************/ - - if (NetEqMainInst->MCUinst.fs != 0) - { - int32_t temp32; - /* Query packet buffer for number of samples. */ - temp32 = WebRtcNetEQ_PacketBufferGetSize( - &NetEqMainInst->MCUinst.PacketBuffer_inst, - &NetEqMainInst->MCUinst.codec_DB_inst, - NetEqMainInst->MCUinst.av_sync); - - /* Divide by sample rate. - * Calculate temp32 * 1000 / fs to get result in ms. */ - stats->currentBufferSize = (uint16_t) - WebRtcSpl_DivU32U16(temp32 * 1000, NetEqMainInst->MCUinst.fs); - - /* Add number of samples yet to play in sync buffer. */ - temp32 = (int32_t) (NetEqMainInst->DSPinst.endPosition - - NetEqMainInst->DSPinst.curPosition); - stats->currentBufferSize += (uint16_t) - WebRtcSpl_DivU32U16(temp32 * 1000, NetEqMainInst->MCUinst.fs); - } - else - { - /* Sample rate not initialized. */ - stats->currentBufferSize = 0; - } - - /***************************/ - /* Get optimal buffer size */ - /***************************/ - - if (NetEqMainInst->MCUinst.fs != 0) - { - /* preferredBufferSize = Bopt * packSizeSamples / (fs/1000) */ - stats->preferredBufferSize - = (uint16_t) WEBRTC_SPL_MUL_16_16( - (int16_t) ((NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.optBufLevel) >> 8), /* optimal buffer level in packets shifted to Q0 */ - WebRtcSpl_DivW32W16ResW16( - (int32_t) NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.packetSpeechLenSamp, /* samples per packet */ - WebRtcSpl_DivW32W16ResW16( (int32_t) NetEqMainInst->MCUinst.fs, (int16_t) 1000 ) /* samples per ms */ - ) ); - - /* add extra delay */ - if (NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.extraDelayMs > 0) - { - stats->preferredBufferSize - += NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.extraDelayMs; - } - } - else - { - /* sample rate not initialized */ - stats->preferredBufferSize = 0; - } - - /***********************************/ - /* Check if jitter peaks are found */ - /***********************************/ - - stats->jitterPeaksFound = - NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.peakFound; - - /***********************/ - /* Calculate loss rate */ - /***********************/ - - /* timestamps elapsed since last report */ - tempU32 = NetEqMainInst->MCUinst.lastReportTS; - - if (NetEqMainInst->MCUinst.lostTS == 0) - { - /* no losses */ - stats->currentPacketLossRate = 0; - } - else if (NetEqMainInst->MCUinst.lostTS < tempU32) - { - /* calculate shifts; we want the result in Q14 */ - numShift = WebRtcSpl_NormU32(NetEqMainInst->MCUinst.lostTS); /* numerator shift for normalize */ - - if (numShift < 14) - { - /* cannot shift numerator 14 steps; shift denominator too */ - tempU32 = WEBRTC_SPL_RSHIFT_U32(tempU32, 14-numShift); /* right-shift */ - } - else - { - /* shift no more than 14 steps */ - numShift = 14; - } - - if (tempU32 == 0) - { - /* check for zero denominator; result should be zero in this case */ - stats->currentPacketLossRate = 0; - } - else - { - /* check that denominator fits in signed 16-bit */ - while (tempU32 > WEBRTC_SPL_WORD16_MAX) - { - tempU32 >>= 1; /* right-shift 1 step */ - numShift--; /* compensate in numerator */ - } - tempU16 = (uint16_t) tempU32; - - /* do the shift of numerator */ - tempU32 - = WEBRTC_SPL_SHIFT_W32( (uint32_t) NetEqMainInst->MCUinst.lostTS, numShift); - - stats->currentPacketLossRate = (uint16_t) WebRtcSpl_DivU32U16(tempU32, - tempU16); - } - } - else - { - /* lost count is larger than elapsed time count; probably timestamp wrap-around or something else wrong */ - /* set loss rate = 1 */ - stats->currentPacketLossRate = 1 << 14; /* 1 in Q14 */ - } - - /**************************/ - /* Calculate discard rate */ - /**************************/ - - /* timestamps elapsed since last report */ - tempU32 = NetEqMainInst->MCUinst.lastReportTS; - - /* number of discarded samples */ - tempU32_2 - = WEBRTC_SPL_MUL_16_U16( (int16_t) NetEqMainInst->MCUinst.PacketBuffer_inst.packSizeSamples, - NetEqMainInst->MCUinst.PacketBuffer_inst.discardedPackets); - - if (tempU32_2 == 0) - { - /* no discarded samples */ - stats->currentDiscardRate = 0; - } - else if (tempU32_2 < tempU32) - { - /* calculate shifts; we want the result in Q14 */ - numShift = WebRtcSpl_NormU32(tempU32_2); /* numerator shift for normalize */ - - if (numShift < 14) - { - /* cannot shift numerator 14 steps; shift denominator too */ - tempU32 = WEBRTC_SPL_RSHIFT_U32(tempU32, 14-numShift); /* right-shift */ - } - else - { - /* shift no more than 14 steps */ - numShift = 14; - } - - if (tempU32 == 0) - { - /* check for zero denominator; result should be zero in this case */ - stats->currentDiscardRate = 0; - } - else - { - /* check that denominator fits in signed 16-bit */ - while (tempU32 > WEBRTC_SPL_WORD16_MAX) - { - tempU32 >>= 1; /* right-shift 1 step */ - numShift--; /* compensate in numerator */ - } - tempU16 = (uint16_t) tempU32; - - /* do the shift of numerator */ - tempU32 = WEBRTC_SPL_SHIFT_W32( tempU32_2, numShift); - - stats->currentDiscardRate = (uint16_t) WebRtcSpl_DivU32U16(tempU32, tempU16); - } - } - else - { - /* lost count is larger than elapsed time count; probably timestamp wrap-around or something else wrong */ - /* set loss rate = 1 */ - stats->currentDiscardRate = 1 << 14; /* 1 in Q14 */ - } - - /*************************************************************/ - /* Calculate Accelerate, Expand and Pre-emptive Expand rates */ - /*************************************************************/ - - /* timestamps elapsed since last report */ - tempU32 = NetEqMainInst->MCUinst.lastReportTS; - - if (NetEqMainInst->DSPinst.statInst.accelerateLength == 0) - { - /* no accelerate */ - stats->currentAccelerateRate = 0; - } - else if (NetEqMainInst->DSPinst.statInst.accelerateLength < tempU32) - { - /* calculate shifts; we want the result in Q14 */ - numShift = WebRtcSpl_NormU32(NetEqMainInst->DSPinst.statInst.accelerateLength); /* numerator shift for normalize */ - - if (numShift < 14) - { - /* cannot shift numerator 14 steps; shift denominator too */ - tempU32 = WEBRTC_SPL_RSHIFT_U32(tempU32, 14-numShift); /* right-shift */ - } - else - { - /* shift no more than 14 steps */ - numShift = 14; - } - - if (tempU32 == 0) - { - /* check for zero denominator; result should be zero in this case */ - stats->currentAccelerateRate = 0; - } - else - { - /* check that denominator fits in signed 16-bit */ - while (tempU32 > WEBRTC_SPL_WORD16_MAX) - { - tempU32 >>= 1; /* right-shift 1 step */ - numShift--; /* compensate in numerator */ - } - tempU16 = (uint16_t) tempU32; - - /* do the shift of numerator */ - tempU32 - = WEBRTC_SPL_SHIFT_W32( NetEqMainInst->DSPinst.statInst.accelerateLength, numShift); - - stats->currentAccelerateRate = (uint16_t) WebRtcSpl_DivU32U16(tempU32, - tempU16); - } - } - else - { - /* lost count is larger than elapsed time count; probably timestamp wrap-around or something else wrong */ - /* set loss rate = 1 */ - stats->currentAccelerateRate = 1 << 14; /* 1 in Q14 */ - } - - /* timestamps elapsed since last report */ - tempU32 = NetEqMainInst->MCUinst.lastReportTS; - - if (NetEqMainInst->DSPinst.statInst.expandLength == 0) - { - /* no expand */ - stats->currentExpandRate = 0; - } - else if (NetEqMainInst->DSPinst.statInst.expandLength < tempU32) - { - /* calculate shifts; we want the result in Q14 */ - numShift = WebRtcSpl_NormU32(NetEqMainInst->DSPinst.statInst.expandLength); /* numerator shift for normalize */ - - if (numShift < 14) - { - /* cannot shift numerator 14 steps; shift denominator too */ - tempU32 = WEBRTC_SPL_RSHIFT_U32(tempU32, 14-numShift); /* right-shift */ - } - else - { - /* shift no more than 14 steps */ - numShift = 14; - } - - if (tempU32 == 0) - { - /* check for zero denominator; result should be zero in this case */ - stats->currentExpandRate = 0; - } - else - { - /* check that denominator fits in signed 16-bit */ - while (tempU32 > WEBRTC_SPL_WORD16_MAX) - { - tempU32 >>= 1; /* right-shift 1 step */ - numShift--; /* compensate in numerator */ - } - tempU16 = (uint16_t) tempU32; - - /* do the shift of numerator */ - tempU32 - = WEBRTC_SPL_SHIFT_W32( NetEqMainInst->DSPinst.statInst.expandLength, numShift); - - stats->currentExpandRate = (uint16_t) WebRtcSpl_DivU32U16(tempU32, tempU16); - } - } - else - { - /* lost count is larger than elapsed time count; probably timestamp wrap-around or something else wrong */ - /* set loss rate = 1 */ - stats->currentExpandRate = 1 << 14; /* 1 in Q14 */ - } - - /* timestamps elapsed since last report */ - tempU32 = NetEqMainInst->MCUinst.lastReportTS; - - if (NetEqMainInst->DSPinst.statInst.preemptiveLength == 0) - { - /* no pre-emptive expand */ - stats->currentPreemptiveRate = 0; - } - else if (NetEqMainInst->DSPinst.statInst.preemptiveLength < tempU32) - { - /* calculate shifts; we want the result in Q14 */ - numShift = WebRtcSpl_NormU32(NetEqMainInst->DSPinst.statInst.preemptiveLength); /* numerator shift for normalize */ - - if (numShift < 14) - { - /* cannot shift numerator 14 steps; shift denominator too */ - tempU32 = WEBRTC_SPL_RSHIFT_U32(tempU32, 14-numShift); /* right-shift */ - } - else - { - /* shift no more than 14 steps */ - numShift = 14; - } - - if (tempU32 == 0) - { - /* check for zero denominator; result should be zero in this case */ - stats->currentPreemptiveRate = 0; - } - else - { - /* check that denominator fits in signed 16-bit */ - while (tempU32 > WEBRTC_SPL_WORD16_MAX) - { - tempU32 >>= 1; /* right-shift 1 step */ - numShift--; /* compensate in numerator */ - } - tempU16 = (uint16_t) tempU32; - - /* do the shift of numerator */ - tempU32 - = WEBRTC_SPL_SHIFT_W32( NetEqMainInst->DSPinst.statInst.preemptiveLength, numShift); - - stats->currentPreemptiveRate = (uint16_t) WebRtcSpl_DivU32U16(tempU32, - tempU16); - } - } - else - { - /* lost count is larger than elapsed time count; probably timestamp wrap-around or something else wrong */ - /* set loss rate = 1 */ - stats->currentPreemptiveRate = 1 << 14; /* 1 in Q14 */ - } - - stats->clockDriftPPM = WebRtcNetEQ_AverageIAT( - &NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst); - - /* reset counters */ - WebRtcNetEQ_ResetMcuInCallStats(&(NetEqMainInst->MCUinst)); - WebRtcNetEQ_ClearInCallStats(&(NetEqMainInst->DSPinst)); - - return (0); -} - -int WebRtcNetEQ_GetRawFrameWaitingTimes(void *inst, - int max_length, - int* waiting_times_ms) { - int i = 0; - MainInst_t *main_inst = (MainInst_t*) inst; - if (main_inst == NULL) return -1; - - while ((i < max_length) && (i < main_inst->MCUinst.len_waiting_times)) { - waiting_times_ms[i] = main_inst->MCUinst.waiting_times[i] * - main_inst->DSPinst.millisecondsPerCall; - ++i; - } - assert(i <= kLenWaitingTimes); - WebRtcNetEQ_ResetWaitingTimeStats(&main_inst->MCUinst); - return i; -} - -/**************************************************************************** - * WebRtcNetEQ_SetVADInstance(...) - * - * Provide a pointer to an allocated VAD instance. If function is never - * called or it is called with NULL pointer as VAD_inst, the post-decode - * VAD functionality is disabled. Also provide pointers to init, setmode - * and VAD functions. These are typically pointers to WebRtcVad_Init, - * WebRtcVad_set_mode and WebRtcVad_Process, respectively, all found in the - * interface file webrtc_vad.h. - * - * Input: - * - NetEQ_inst : NetEQ instance - * - VADinst : VAD instance - * - initFunction : Pointer to VAD init function - * - setmodeFunction : Pointer to VAD setmode function - * - VADfunction : Pointer to VAD function - * - * Output: - * - NetEQ_inst : Updated NetEQ instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_SetVADInstance(void *NetEQ_inst, void *VAD_inst, - WebRtcNetEQ_VADInitFunction initFunction, - WebRtcNetEQ_VADSetmodeFunction setmodeFunction, - WebRtcNetEQ_VADFunction VADFunction) -{ - - /* Typecast to internal instance type */ - MainInst_t *NetEqMainInst = (MainInst_t*) NetEQ_inst; - if (NetEqMainInst == NULL) - { - return (-1); - } - -#ifdef NETEQ_VAD - - /* Store pointer in PostDecode VAD struct */ - NetEqMainInst->DSPinst.VADInst.VADState = VAD_inst; - - /* Store function pointers */ - NetEqMainInst->DSPinst.VADInst.initFunction = initFunction; - NetEqMainInst->DSPinst.VADInst.setmodeFunction = setmodeFunction; - NetEqMainInst->DSPinst.VADInst.VADFunction = VADFunction; - - /* Call init function and return the result (ok or fail) */ - return(WebRtcNetEQ_InitVAD(&NetEqMainInst->DSPinst.VADInst, NetEqMainInst->DSPinst.fs)); - -#else /* NETEQ_VAD not defined */ - return (-1); -#endif /* NETEQ_VAD */ - -} - -/**************************************************************************** - * WebRtcNetEQ_SetVADMode(...) - * - * Pass an aggressiveness mode parameter to the post-decode VAD instance. - * If this function is never called, mode 0 (quality mode) is used as default. - * - * Input: - * - inst : NetEQ instance - * - mode : mode parameter (same range as WebRtc VAD mode) - * - * Output: - * - inst : Updated NetEQ instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_SetVADMode(void *inst, int mode) -{ - - /* Typecast to internal instance type */ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) - { - return (-1); - } - -#ifdef NETEQ_VAD - - /* Set mode and return result */ - return(WebRtcNetEQ_SetVADModeInternal(&NetEqMainInst->DSPinst.VADInst, mode)); - -#else /* NETEQ_VAD not defined */ - return (-1); -#endif /* NETEQ_VAD */ - -} - -void WebRtcNetEQ_GetProcessingActivity(void *inst, - WebRtcNetEQ_ProcessingActivity *stats) { - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - - stats->accelerate_bgn_samples = - NetEqMainInst->DSPinst.activity_stats.accelerate_bgn_samples; - stats->accelerate_normal_samples = - NetEqMainInst->DSPinst.activity_stats.accelarate_normal_samples; - - stats->expand_bgn_sampels = - NetEqMainInst->DSPinst.activity_stats.expand_bgn_samples; - stats->expand_normal_samples = - NetEqMainInst->DSPinst.activity_stats.expand_normal_samples; - - stats->preemptive_expand_bgn_samples = - NetEqMainInst->DSPinst.activity_stats.preemptive_expand_bgn_samples; - stats->preemptive_expand_normal_samples = - NetEqMainInst->DSPinst.activity_stats.preemptive_expand_normal_samples; - - stats->merge_expand_bgn_samples = - NetEqMainInst->DSPinst.activity_stats.merge_expand_bgn_samples; - stats->merge_expand_normal_samples = - NetEqMainInst->DSPinst.activity_stats.merge_expand_normal_samples; - - WebRtcNetEQ_ClearActivityStats(&NetEqMainInst->DSPinst); -} - -void WebRtcNetEQ_EnableAVSync(void* inst, int enable) { - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - NetEqMainInst->MCUinst.av_sync = (enable != 0) ? 1 : 0; -} - -int WebRtcNetEQ_RecInSyncRTP(void* inst, WebRtcNetEQ_RTPInfo* rtp_info, - uint32_t receive_timestamp) { - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst->MCUinst.av_sync == 0) - return -1; - if (WebRtcNetEQ_RecInRTPStruct(inst, rtp_info, kSyncPayload, - SYNC_PAYLOAD_LEN_BYTES, - receive_timestamp) < 0) { - return -1; - } - return SYNC_PAYLOAD_LEN_BYTES; -} - -int WebRtcNetEQ_GetRequiredDelayMs(const void* inst) { - const MainInst_t* NetEqMainInst = (MainInst_t*)inst; - const AutomodeInst_t* auto_mode = (NetEqMainInst == NULL) ? NULL : - &NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst; - - /* Instance sanity */ - if (NetEqMainInst == NULL || auto_mode == NULL) - return 0; - - if (NetEqMainInst->MCUinst.fs == 0) - return 0; // Sampling rate not initialized. - - /* |required_delay_q8| has the unit of packets in Q8 domain, therefore, - * the corresponding delay is - * required_delay_ms = (1000 * required_delay_q8 * samples_per_packet / - * sample_rate_hz) / 256; - */ - return (auto_mode->required_delay_q8 * - ((auto_mode->packetSpeechLenSamp * 1000) / NetEqMainInst->MCUinst.fs) + - 128) >> 8; -} - -int WebRtcNetEQ_DecodedRtpInfo(const void* inst, - int* sequence_number, - uint32_t* timestamp) { - const MainInst_t *NetEqMainInst = (inst == NULL) ? NULL : - (const MainInst_t*) inst; - if (NetEqMainInst->MCUinst.decoded_packet_sequence_number < 0) - return -1; - *sequence_number = NetEqMainInst->MCUinst.decoded_packet_sequence_number; - *timestamp = NetEqMainInst->MCUinst.decoded_packet_timestamp; - return 0; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/webrtc_neteq_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/webrtc_neteq_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/webrtc_neteq_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq/webrtc_neteq_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,778 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file includes unit tests for NetEQ. - */ - -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h" - -#include -#include // memset - -#include -#include -#include -#include - -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" -#include "webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_internal.h" -#include "webrtc/modules/audio_coding/neteq/test/NETEQTEST_CodecClass.h" -#include "webrtc/modules/audio_coding/neteq/test/NETEQTEST_NetEQClass.h" -#include "webrtc/modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.h" -#include "webrtc/modules/audio_coding/neteq4/tools/input_audio_file.h" -#include "webrtc/test/testsupport/fileutils.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -class RefFiles { - public: - RefFiles(const std::string& input_file, const std::string& output_file); - ~RefFiles(); - template void ProcessReference(const T& test_results); - template void ProcessReference( - const T (&test_results)[n], - size_t length); - template void WriteToFile( - const T (&test_results)[n], - size_t length); - template void ReadFromFileAndCompare( - const T (&test_results)[n], - size_t length); - void WriteToFile(const WebRtcNetEQ_NetworkStatistics& stats); - void ReadFromFileAndCompare(const WebRtcNetEQ_NetworkStatistics& stats); - void WriteToFile(const WebRtcNetEQ_RTCPStat& stats); - void ReadFromFileAndCompare(const WebRtcNetEQ_RTCPStat& stats); - - FILE* input_fp_; - FILE* output_fp_; -}; - -RefFiles::RefFiles(const std::string &input_file, - const std::string &output_file) - : input_fp_(NULL), - output_fp_(NULL) { - if (!input_file.empty()) { - input_fp_ = fopen(input_file.c_str(), "rb"); - EXPECT_TRUE(input_fp_ != NULL); - } - if (!output_file.empty()) { - output_fp_ = fopen(output_file.c_str(), "wb"); - EXPECT_TRUE(output_fp_ != NULL); - } -} - -RefFiles::~RefFiles() { - if (input_fp_) { - EXPECT_EQ(EOF, fgetc(input_fp_)); // Make sure that we reached the end. - fclose(input_fp_); - } - if (output_fp_) fclose(output_fp_); -} - -template -void RefFiles::ProcessReference(const T& test_results) { - WriteToFile(test_results); - ReadFromFileAndCompare(test_results); -} - -template -void RefFiles::ProcessReference(const T (&test_results)[n], size_t length) { - WriteToFile(test_results, length); - ReadFromFileAndCompare(test_results, length); -} - -template -void RefFiles::WriteToFile(const T (&test_results)[n], size_t length) { - if (output_fp_) { - ASSERT_EQ(length, fwrite(&test_results, sizeof(T), length, output_fp_)); - } -} - -template -void RefFiles::ReadFromFileAndCompare(const T (&test_results)[n], - size_t length) { - if (input_fp_) { - // Read from ref file. - T* ref = new T[length]; - ASSERT_EQ(length, fread(ref, sizeof(T), length, input_fp_)); - // Compare - ASSERT_EQ(0, memcmp(&test_results, ref, sizeof(T) * length)); - delete [] ref; - } -} - -void RefFiles::WriteToFile(const WebRtcNetEQ_NetworkStatistics& stats) { - if (output_fp_) { - ASSERT_EQ(1u, fwrite(&stats, sizeof(WebRtcNetEQ_NetworkStatistics), 1, - output_fp_)); - } -} - -void RefFiles::ReadFromFileAndCompare( - const WebRtcNetEQ_NetworkStatistics& stats) { - if (input_fp_) { - // Read from ref file. - size_t stat_size = sizeof(WebRtcNetEQ_NetworkStatistics); - WebRtcNetEQ_NetworkStatistics ref_stats; - ASSERT_EQ(1u, fread(&ref_stats, stat_size, 1, input_fp_)); - // Compare - EXPECT_EQ(0, memcmp(&stats, &ref_stats, stat_size)); - } -} - -void RefFiles::WriteToFile(const WebRtcNetEQ_RTCPStat& stats) { - if (output_fp_) { - ASSERT_EQ(1u, fwrite(&(stats.fraction_lost), sizeof(stats.fraction_lost), 1, - output_fp_)); - ASSERT_EQ(1u, fwrite(&(stats.cum_lost), sizeof(stats.cum_lost), 1, - output_fp_)); - ASSERT_EQ(1u, fwrite(&(stats.ext_max), sizeof(stats.ext_max), 1, - output_fp_)); - ASSERT_EQ(1u, fwrite(&(stats.jitter), sizeof(stats.jitter), 1, - output_fp_)); - } -} - -void RefFiles::ReadFromFileAndCompare( - const WebRtcNetEQ_RTCPStat& stats) { - if (input_fp_) { - // Read from ref file. - WebRtcNetEQ_RTCPStat ref_stats; - ASSERT_EQ(1u, fread(&(ref_stats.fraction_lost), - sizeof(ref_stats.fraction_lost), 1, input_fp_)); - ASSERT_EQ(1u, fread(&(ref_stats.cum_lost), sizeof(ref_stats.cum_lost), 1, - input_fp_)); - ASSERT_EQ(1u, fread(&(ref_stats.ext_max), sizeof(ref_stats.ext_max), 1, - input_fp_)); - ASSERT_EQ(1u, fread(&(ref_stats.jitter), sizeof(ref_stats.jitter), 1, - input_fp_)); - // Compare - EXPECT_EQ(ref_stats.fraction_lost, stats.fraction_lost); - EXPECT_EQ(ref_stats.cum_lost, stats.cum_lost); - EXPECT_EQ(ref_stats.ext_max, stats.ext_max); - EXPECT_EQ(ref_stats.jitter, stats.jitter); - } -} - -class NetEqDecodingTest : public ::testing::Test { - protected: - // NetEQ must be polled for data once every 10 ms. Thus, neither of the - // constants below can be changed. - static const int kTimeStepMs = 10; - static const int kBlockSize8kHz = kTimeStepMs * 8; - static const int kBlockSize16kHz = kTimeStepMs * 16; - static const int kBlockSize32kHz = kTimeStepMs * 32; - static const int kMaxBlockSize = kBlockSize32kHz; - - NetEqDecodingTest(); - virtual void SetUp(); - virtual void TearDown(); - void SelectDecoders(WebRtcNetEQDecoder* used_codec); - void LoadDecoders(); - void OpenInputFile(const std::string &rtp_file); - void Process(NETEQTEST_RTPpacket* rtp_ptr, int16_t* out_len); - void DecodeAndCompare(const std::string &rtp_file, - const std::string &ref_file); - void DecodeAndCheckStats(const std::string &rtp_file, - const std::string &stat_ref_file, - const std::string &rtcp_ref_file); - static void PopulateRtpInfo(int frame_index, - int timestamp, - WebRtcNetEQ_RTPInfo* rtp_info); - static void PopulateCng(int frame_index, - int timestamp, - WebRtcNetEQ_RTPInfo* rtp_info, - uint8_t* payload, - int* payload_len); - void WrapTest(uint16_t start_seq_no, uint32_t start_timestamp, - const std::set& drop_seq_numbers); - - NETEQTEST_NetEQClass* neteq_inst_; - std::vector dec_; - FILE* rtp_fp_; - unsigned int sim_clock_; - int16_t out_data_[kMaxBlockSize]; -}; - -NetEqDecodingTest::NetEqDecodingTest() - : neteq_inst_(NULL), - rtp_fp_(NULL), - sim_clock_(0) { - memset(out_data_, 0, sizeof(out_data_)); -} - -void NetEqDecodingTest::SetUp() { - WebRtcNetEQDecoder usedCodec[kDecoderReservedEnd - 1]; - - SelectDecoders(usedCodec); - neteq_inst_ = new NETEQTEST_NetEQClass(usedCodec, dec_.size(), 8000, - kTCPLargeJitter); - ASSERT_TRUE(neteq_inst_); - LoadDecoders(); -} - -void NetEqDecodingTest::TearDown() { - if (neteq_inst_) - delete neteq_inst_; - for (size_t i = 0; i < dec_.size(); ++i) { - if (dec_[i]) - delete dec_[i]; - } - if (rtp_fp_) - fclose(rtp_fp_); -} - -void NetEqDecodingTest::SelectDecoders(WebRtcNetEQDecoder* used_codec) { - *used_codec++ = kDecoderPCMu; - dec_.push_back(new decoder_PCMU(0)); - *used_codec++ = kDecoderPCMa; - dec_.push_back(new decoder_PCMA(8)); - *used_codec++ = kDecoderILBC; - dec_.push_back(new decoder_ILBC(102)); - *used_codec++ = kDecoderISAC; - dec_.push_back(new decoder_iSAC(103)); - *used_codec++ = kDecoderISACswb; - dec_.push_back(new decoder_iSACSWB(104)); - *used_codec++ = kDecoderISACfb; - dec_.push_back(new decoder_iSACFB(105)); - *used_codec++ = kDecoderPCM16B; - dec_.push_back(new decoder_PCM16B_NB(93)); - *used_codec++ = kDecoderPCM16Bwb; - dec_.push_back(new decoder_PCM16B_WB(94)); - *used_codec++ = kDecoderPCM16Bswb32kHz; - dec_.push_back(new decoder_PCM16B_SWB32(95)); - *used_codec++ = kDecoderCNG; - dec_.push_back(new decoder_CNG(13, 8000)); - *used_codec++ = kDecoderCNG; - dec_.push_back(new decoder_CNG(98, 16000)); -} - -void NetEqDecodingTest::LoadDecoders() { - for (size_t i = 0; i < dec_.size(); ++i) { - ASSERT_EQ(0, dec_[i]->loadToNetEQ(*neteq_inst_)); - } -} - -void NetEqDecodingTest::OpenInputFile(const std::string &rtp_file) { - rtp_fp_ = fopen(rtp_file.c_str(), "rb"); - ASSERT_TRUE(rtp_fp_ != NULL); - ASSERT_EQ(0, NETEQTEST_RTPpacket::skipFileHeader(rtp_fp_)); -} - -void NetEqDecodingTest::Process(NETEQTEST_RTPpacket* rtp, int16_t* out_len) { - // Check if time to receive. - while ((sim_clock_ >= rtp->time()) && - (rtp->dataLen() >= 0)) { - if (rtp->dataLen() > 0) { - ASSERT_EQ(0, neteq_inst_->recIn(*rtp)); - } - // Get next packet. - ASSERT_NE(-1, rtp->readFromFile(rtp_fp_)); - } - - // RecOut - *out_len = neteq_inst_->recOut(out_data_); - ASSERT_TRUE((*out_len == kBlockSize8kHz) || - (*out_len == kBlockSize16kHz) || - (*out_len == kBlockSize32kHz)); - - // Increase time. - sim_clock_ += kTimeStepMs; -} - -void NetEqDecodingTest::DecodeAndCompare(const std::string &rtp_file, - const std::string &ref_file) { - OpenInputFile(rtp_file); - - std::string ref_out_file = ""; - if (ref_file.empty()) { - ref_out_file = webrtc::test::OutputPath() + "neteq_out.pcm"; - } - RefFiles ref_files(ref_file, ref_out_file); - - NETEQTEST_RTPpacket rtp; - ASSERT_GT(rtp.readFromFile(rtp_fp_), 0); - int i = 0; - while (rtp.dataLen() >= 0) { - std::ostringstream ss; - ss << "Lap number " << i++ << " in DecodeAndCompare while loop"; - SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. - int16_t out_len; - ASSERT_NO_FATAL_FAILURE(Process(&rtp, &out_len)); - ASSERT_NO_FATAL_FAILURE(ref_files.ProcessReference(out_data_, out_len)); - } -} - -void NetEqDecodingTest::DecodeAndCheckStats(const std::string &rtp_file, - const std::string &stat_ref_file, - const std::string &rtcp_ref_file) { - OpenInputFile(rtp_file); - std::string stat_out_file = ""; - if (stat_ref_file.empty()) { - stat_out_file = webrtc::test::OutputPath() + - "neteq_network_stats.dat"; - } - RefFiles network_stat_files(stat_ref_file, stat_out_file); - - std::string rtcp_out_file = ""; - if (rtcp_ref_file.empty()) { - rtcp_out_file = webrtc::test::OutputPath() + - "neteq_rtcp_stats.dat"; - } - RefFiles rtcp_stat_files(rtcp_ref_file, rtcp_out_file); - - NETEQTEST_RTPpacket rtp; - ASSERT_GT(rtp.readFromFile(rtp_fp_), 0); - while (rtp.dataLen() >= 0) { - int16_t out_len; - Process(&rtp, &out_len); - - // Query the network statistics API once per second - if (sim_clock_ % 1000 == 0) { - // Process NetworkStatistics. - WebRtcNetEQ_NetworkStatistics network_stats; - ASSERT_EQ(0, WebRtcNetEQ_GetNetworkStatistics(neteq_inst_->instance(), - &network_stats)); - network_stat_files.ProcessReference(network_stats); - - // Process RTCPstat. - WebRtcNetEQ_RTCPStat rtcp_stats; - ASSERT_EQ(0, WebRtcNetEQ_GetRTCPStats(neteq_inst_->instance(), - &rtcp_stats)); - rtcp_stat_files.ProcessReference(rtcp_stats); - } - } -} - -void NetEqDecodingTest::PopulateRtpInfo(int frame_index, - int timestamp, - WebRtcNetEQ_RTPInfo* rtp_info) { - rtp_info->sequenceNumber = frame_index; - rtp_info->timeStamp = timestamp; - rtp_info->SSRC = 0x1234; // Just an arbitrary SSRC. - rtp_info->payloadType = 94; // PCM16b WB codec. - rtp_info->markerBit = 0; -} - -void NetEqDecodingTest::PopulateCng(int frame_index, - int timestamp, - WebRtcNetEQ_RTPInfo* rtp_info, - uint8_t* payload, - int* payload_len) { - rtp_info->sequenceNumber = frame_index; - rtp_info->timeStamp = timestamp; - rtp_info->SSRC = 0x1234; // Just an arbitrary SSRC. - rtp_info->payloadType = 98; // WB CNG. - rtp_info->markerBit = 0; - payload[0] = 64; // Noise level -64 dBov, quite arbitrarily chosen. - *payload_len = 1; // Only noise level, no spectral parameters. -} - -#if (defined(_WIN32) && defined(WEBRTC_ARCH_64_BITS)) || defined(WEBRTC_ANDROID) -// Disabled for Windows 64-bit until webrtc:1460 is fixed. -#define MAYBE_TestBitExactness DISABLED_TestBitExactness -#else -#define MAYBE_TestBitExactness TestBitExactness -#endif - -TEST_F(NetEqDecodingTest, MAYBE_TestBitExactness) { - const std::string kInputRtpFile = webrtc::test::ProjectRootPath() + - "resources/audio_coding/neteq_universal.rtp"; -#if defined(_MSC_VER) && (_MSC_VER >= 1700) - // For Visual Studio 2012 and later, we will have to use the generic reference - // file, rather than the windows-specific one. - const std::string kInputRefFile = webrtc::test::ProjectRootPath() + - "resources/audio_coding/neteq_universal_ref.pcm"; -#else - const std::string kInputRefFile = - webrtc::test::ResourcePath("audio_coding/neteq_universal_ref", "pcm"); -#endif - DecodeAndCompare(kInputRtpFile, kInputRefFile); -} - -TEST_F(NetEqDecodingTest, TestNetworkStatistics) { - const std::string kInputRtpFile = webrtc::test::ProjectRootPath() + - "resources/audio_coding/neteq_universal.rtp"; -#if defined(_MSC_VER) && (_MSC_VER >= 1700) - // For Visual Studio 2012 and later, we will have to use the generic reference - // file, rather than the windows-specific one. - const std::string kNetworkStatRefFile = webrtc::test::ProjectRootPath() + - "resources/audio_coding/neteq_network_stats.dat"; -#else - const std::string kNetworkStatRefFile = - webrtc::test::ResourcePath("audio_coding/neteq_network_stats", "dat"); -#endif - const std::string kRtcpStatRefFile = - webrtc::test::ResourcePath("audio_coding/neteq_rtcp_stats", "dat"); - DecodeAndCheckStats(kInputRtpFile, kNetworkStatRefFile, kRtcpStatRefFile); -} - -TEST_F(NetEqDecodingTest, TestFrameWaitingTimeStatistics) { - // Use fax mode to avoid time-scaling. This is to simplify the testing of - // packet waiting times in the packet buffer. - ASSERT_EQ(0, - WebRtcNetEQ_SetPlayoutMode(neteq_inst_->instance(), kPlayoutFax)); - // Insert 30 dummy packets at once. Each packet contains 10 ms 16 kHz audio. - int num_frames = 30; - const int kSamples = 10 * 16; - const int kPayloadBytes = kSamples * 2; - for (int i = 0; i < num_frames; ++i) { - uint16_t payload[kSamples] = {0}; - WebRtcNetEQ_RTPInfo rtp_info; - rtp_info.sequenceNumber = i; - rtp_info.timeStamp = i * kSamples; - rtp_info.SSRC = 0x1234; // Just an arbitrary SSRC. - rtp_info.payloadType = 94; // PCM16b WB codec. - rtp_info.markerBit = 0; - ASSERT_EQ(0, WebRtcNetEQ_RecInRTPStruct(neteq_inst_->instance(), &rtp_info, - reinterpret_cast(payload), - kPayloadBytes, 0)); - } - // Pull out all data. - for (int i = 0; i < num_frames; ++i) { - ASSERT_TRUE(kBlockSize16kHz == neteq_inst_->recOut(out_data_)); - } - const int kVecLen = 110; // More than kLenWaitingTimes in mcu.h. - int waiting_times[kVecLen]; - int len = WebRtcNetEQ_GetRawFrameWaitingTimes(neteq_inst_->instance(), - kVecLen, waiting_times); - EXPECT_EQ(num_frames, len); - // Since all frames are dumped into NetEQ at once, but pulled out with 10 ms - // spacing (per definition), we expect the delay to increase with 10 ms for - // each packet. - for (int i = 0; i < len; ++i) { - EXPECT_EQ((i + 1) * 10, waiting_times[i]); - } - - // Check statistics again and make sure it's been reset. - EXPECT_EQ(0, WebRtcNetEQ_GetRawFrameWaitingTimes(neteq_inst_->instance(), - kVecLen, waiting_times)); - - // Process > 100 frames, and make sure that that we get statistics - // only for 100 frames. Note the new SSRC, causing NetEQ to reset. - num_frames = 110; - for (int i = 0; i < num_frames; ++i) { - uint16_t payload[kSamples] = {0}; - WebRtcNetEQ_RTPInfo rtp_info; - rtp_info.sequenceNumber = i; - rtp_info.timeStamp = i * kSamples; - rtp_info.SSRC = 0x1235; // Just an arbitrary SSRC. - rtp_info.payloadType = 94; // PCM16b WB codec. - rtp_info.markerBit = 0; - ASSERT_EQ(0, WebRtcNetEQ_RecInRTPStruct(neteq_inst_->instance(), &rtp_info, - reinterpret_cast(payload), - kPayloadBytes, 0)); - ASSERT_TRUE(kBlockSize16kHz == neteq_inst_->recOut(out_data_)); - } - - len = WebRtcNetEQ_GetRawFrameWaitingTimes(neteq_inst_->instance(), - kVecLen, waiting_times); - EXPECT_EQ(100, len); -} - -TEST_F(NetEqDecodingTest, TestAverageInterArrivalTimeNegative) { - const int kNumFrames = 3000; // Needed for convergence. - int frame_index = 0; - const int kSamples = 10 * 16; - const int kPayloadBytes = kSamples * 2; - while (frame_index < kNumFrames) { - // Insert one packet each time, except every 10th time where we insert two - // packets at once. This will create a negative clock-drift of approx. 10%. - int num_packets = (frame_index % 10 == 0 ? 2 : 1); - for (int n = 0; n < num_packets; ++n) { - uint8_t payload[kPayloadBytes] = {0}; - WebRtcNetEQ_RTPInfo rtp_info; - PopulateRtpInfo(frame_index, frame_index * kSamples, &rtp_info); - ASSERT_EQ(0, - WebRtcNetEQ_RecInRTPStruct(neteq_inst_->instance(), - &rtp_info, - payload, - kPayloadBytes, 0)); - ++frame_index; - } - - // Pull out data once. - ASSERT_TRUE(kBlockSize16kHz == neteq_inst_->recOut(out_data_)); - } - - WebRtcNetEQ_NetworkStatistics network_stats; - ASSERT_EQ(0, WebRtcNetEQ_GetNetworkStatistics(neteq_inst_->instance(), - &network_stats)); - EXPECT_EQ(-103196, network_stats.clockDriftPPM); -} - -TEST_F(NetEqDecodingTest, TestAverageInterArrivalTimePositive) { - const int kNumFrames = 5000; // Needed for convergence. - int frame_index = 0; - const int kSamples = 10 * 16; - const int kPayloadBytes = kSamples * 2; - for (int i = 0; i < kNumFrames; ++i) { - // Insert one packet each time, except every 10th time where we don't insert - // any packet. This will create a positive clock-drift of approx. 11%. - int num_packets = (i % 10 == 9 ? 0 : 1); - for (int n = 0; n < num_packets; ++n) { - uint8_t payload[kPayloadBytes] = {0}; - WebRtcNetEQ_RTPInfo rtp_info; - PopulateRtpInfo(frame_index, frame_index * kSamples, &rtp_info); - ASSERT_EQ(0, - WebRtcNetEQ_RecInRTPStruct(neteq_inst_->instance(), - &rtp_info, - payload, - kPayloadBytes, 0)); - ++frame_index; - } - - // Pull out data once. - ASSERT_TRUE(kBlockSize16kHz == neteq_inst_->recOut(out_data_)); - } - - WebRtcNetEQ_NetworkStatistics network_stats; - ASSERT_EQ(0, WebRtcNetEQ_GetNetworkStatistics(neteq_inst_->instance(), - &network_stats)); - EXPECT_EQ(110946, network_stats.clockDriftPPM); -} - -TEST_F(NetEqDecodingTest, LongCngWithClockDrift) { - uint16_t seq_no = 0; - uint32_t timestamp = 0; - const int kFrameSizeMs = 30; - const int kSamples = kFrameSizeMs * 16; - const int kPayloadBytes = kSamples * 2; - // Apply a clock drift of -25 ms / s (sender faster than receiver). - const double kDriftFactor = 1000.0 / (1000.0 + 25.0); - double next_input_time_ms = 0.0; - double t_ms; - - // Insert speech for 5 seconds. - const int kSpeechDurationMs = 5000; - for (t_ms = 0; t_ms < kSpeechDurationMs; t_ms += 10) { - // Each turn in this for loop is 10 ms. - while (next_input_time_ms <= t_ms) { - // Insert one 30 ms speech frame. - uint8_t payload[kPayloadBytes] = {0}; - WebRtcNetEQ_RTPInfo rtp_info; - PopulateRtpInfo(seq_no, timestamp, &rtp_info); - ASSERT_EQ(0, - WebRtcNetEQ_RecInRTPStruct(neteq_inst_->instance(), - &rtp_info, - payload, - kPayloadBytes, 0)); - ++seq_no; - timestamp += kSamples; - next_input_time_ms += static_cast(kFrameSizeMs) * kDriftFactor; - } - // Pull out data once. - ASSERT_TRUE(kBlockSize16kHz == neteq_inst_->recOut(out_data_)); - } - - EXPECT_EQ(kOutputNormal, neteq_inst_->getOutputType()); - int32_t delay_before = timestamp - neteq_inst_->getSpeechTimeStamp(); - - // Insert CNG for 1 minute (= 60000 ms). - const int kCngPeriodMs = 100; - const int kCngPeriodSamples = kCngPeriodMs * 16; // Period in 16 kHz samples. - const int kCngDurationMs = 60000; - for (; t_ms < kSpeechDurationMs + kCngDurationMs; t_ms += 10) { - // Each turn in this for loop is 10 ms. - while (next_input_time_ms <= t_ms) { - // Insert one CNG frame each 100 ms. - uint8_t payload[kPayloadBytes]; - int payload_len; - WebRtcNetEQ_RTPInfo rtp_info; - PopulateCng(seq_no, timestamp, &rtp_info, payload, &payload_len); - ASSERT_EQ(0, - WebRtcNetEQ_RecInRTPStruct(neteq_inst_->instance(), - &rtp_info, - payload, - payload_len, 0)); - ++seq_no; - timestamp += kCngPeriodSamples; - next_input_time_ms += static_cast(kCngPeriodMs) * kDriftFactor; - } - // Pull out data once. - ASSERT_TRUE(kBlockSize16kHz == neteq_inst_->recOut(out_data_)); - } - - EXPECT_EQ(kOutputCNG, neteq_inst_->getOutputType()); - - // Insert speech again until output type is speech. - while (neteq_inst_->getOutputType() != kOutputNormal) { - // Each turn in this for loop is 10 ms. - while (next_input_time_ms <= t_ms) { - // Insert one 30 ms speech frame. - uint8_t payload[kPayloadBytes] = {0}; - WebRtcNetEQ_RTPInfo rtp_info; - PopulateRtpInfo(seq_no, timestamp, &rtp_info); - ASSERT_EQ(0, - WebRtcNetEQ_RecInRTPStruct(neteq_inst_->instance(), - &rtp_info, - payload, - kPayloadBytes, 0)); - ++seq_no; - timestamp += kSamples; - next_input_time_ms += static_cast(kFrameSizeMs) * kDriftFactor; - } - // Pull out data once. - ASSERT_TRUE(kBlockSize16kHz == neteq_inst_->recOut(out_data_)); - // Increase clock. - t_ms += 10; - } - - int32_t delay_after = timestamp - neteq_inst_->getSpeechTimeStamp(); - // Compare delay before and after, and make sure it differs less than 20 ms. - EXPECT_LE(delay_after, delay_before + 20 * 16); - EXPECT_GE(delay_after, delay_before - 20 * 16); -} - -TEST_F(NetEqDecodingTest, NoInputDataStereo) { - void *ms_info; - ms_info = malloc(WebRtcNetEQ_GetMasterSlaveInfoSize()); - neteq_inst_->setMaster(); - - // Slave instance without decoders (because it is easier). - WebRtcNetEQDecoder usedCodec[kDecoderReservedEnd - 1]; - usedCodec[0] = kDecoderPCMu; - NETEQTEST_NetEQClass* slave_inst = - new NETEQTEST_NetEQClass(usedCodec, 1, 8000, kTCPLargeJitter); - ASSERT_TRUE(slave_inst); - NETEQTEST_Decoder* dec = new decoder_PCMU(0); - ASSERT_TRUE(dec != NULL); - dec->loadToNetEQ(*slave_inst); - slave_inst->setSlave(); - - // Pull out data. - const int kNumFrames = 100; - for (int i = 0; i < kNumFrames; ++i) { - ASSERT_TRUE(kBlockSize8kHz == neteq_inst_->recOut(out_data_, ms_info)); - ASSERT_TRUE(kBlockSize8kHz == slave_inst->recOut(out_data_, ms_info)); - } - - delete dec; - delete slave_inst; - free(ms_info); -} - -TEST_F(NetEqDecodingTest, TestExtraDelay) { - static const int kNumFrames = 120000; // Needed for convergence. - int frame_index = 0; - static const int kFrameSizeSamples = 30 * 16; - static const int kPayloadBytes = kFrameSizeSamples * 2; - test::InputAudioFile input_file( - webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm")); - int16_t input[kFrameSizeSamples]; - // Buffers of NetEq cannot accommodate larger delays for PCM16. - static const int kExtraDelayMs = 3200; - ASSERT_EQ(0, WebRtcNetEQ_SetExtraDelay(neteq_inst_->instance(), - kExtraDelayMs)); - for (int i = 0; i < kNumFrames; ++i) { - ASSERT_TRUE(input_file.Read(kFrameSizeSamples, input)); - WebRtcNetEQ_RTPInfo rtp_info; - PopulateRtpInfo(frame_index, frame_index * kFrameSizeSamples, &rtp_info); - uint8_t* payload = reinterpret_cast(input); - ASSERT_EQ(0, - WebRtcNetEQ_RecInRTPStruct(neteq_inst_->instance(), - &rtp_info, - payload, - kPayloadBytes, 0)); - ++frame_index; - // Pull out data. - for (int j = 0; j < 3; ++j) { - ASSERT_TRUE(kBlockSize16kHz == neteq_inst_->recOut(out_data_)); - } - if (i % 100 == 0) { - WebRtcNetEQ_NetworkStatistics network_stats; - ASSERT_EQ(0, WebRtcNetEQ_GetNetworkStatistics(neteq_inst_->instance(), - &network_stats)); - const int expected_lower_limit = - std::min(i * 0.083 - 210, 0.9 * network_stats.preferredBufferSize); - EXPECT_GE(network_stats.currentBufferSize, expected_lower_limit); - const int expected_upper_limit = - std::min(i * 0.083 + 255, 1.2 * network_stats.preferredBufferSize); - EXPECT_LE(network_stats.currentBufferSize, expected_upper_limit); - } - } -} - -void NetEqDecodingTest::WrapTest(uint16_t start_seq_no, - uint32_t start_timestamp, - const std::set& drop_seq_numbers) { - uint16_t seq_no = start_seq_no; - uint32_t timestamp = start_timestamp; - const int kFrameSizeMs = 30; - const int kSamples = kFrameSizeMs * 16; - const int kPayloadBytes = kSamples * 2; - double next_input_time_ms = 0.0; - - // Insert speech for 1 second. - const int kSpeechDurationMs = 1000; - for (double t_ms = 0; t_ms < kSpeechDurationMs; t_ms += 10) { - // Each turn in this for loop is 10 ms. - while (next_input_time_ms <= t_ms) { - // Insert one 30 ms speech frame. - uint8_t payload[kPayloadBytes] = {0}; - WebRtcNetEQ_RTPInfo rtp_info; - PopulateRtpInfo(seq_no, timestamp, &rtp_info); - if (drop_seq_numbers.find(seq_no) == drop_seq_numbers.end()) { - // This sequence number was not in the set to drop. Insert it. - ASSERT_EQ(0, - WebRtcNetEQ_RecInRTPStruct(neteq_inst_->instance(), - &rtp_info, - payload, - kPayloadBytes, 0)); - } - ++seq_no; - timestamp += kSamples; - next_input_time_ms += static_cast(kFrameSizeMs); - WebRtcNetEQ_NetworkStatistics network_stats; - ASSERT_EQ(0, WebRtcNetEQ_GetNetworkStatistics(neteq_inst_->instance(), - &network_stats)); - // Expect preferred and actual buffer size to be no more than 2 frames. - EXPECT_LE(network_stats.preferredBufferSize, kFrameSizeMs * 2); - EXPECT_LE(network_stats.currentBufferSize, kFrameSizeMs * 2); - } - // Pull out data once. - ASSERT_TRUE(kBlockSize16kHz == neteq_inst_->recOut(out_data_)); - // Expect delay (in samples) to be less than 2 packets. - EXPECT_LE(timestamp - neteq_inst_->getSpeechTimeStamp(), - static_cast(kSamples * 2)); - } -} - -TEST_F(NetEqDecodingTest, SequenceNumberWrap) { - // Start with a sequence number that will soon wrap. - std::set drop_seq_numbers; // Don't drop any packets. - WrapTest(0xFFFF - 5, 0, drop_seq_numbers); -} - -TEST_F(NetEqDecodingTest, SequenceNumberWrapAndDrop) { - // Start with a sequence number that will soon wrap. - std::set drop_seq_numbers; - drop_seq_numbers.insert(0xFFFF); - drop_seq_numbers.insert(0x0); - WrapTest(0xFFFF - 5, 0, drop_seq_numbers); -} - -TEST_F(NetEqDecodingTest, TimestampWrap) { - // Start with a timestamp that will soon wrap. - std::set drop_seq_numbers; - WrapTest(0, 0xFFFFFFFF - 1000, drop_seq_numbers); -} - -TEST_F(NetEqDecodingTest, TimestampAndSequenceNumberWrap) { - // Start with a timestamp and a sequence number that will wrap at the same - // time. - std::set drop_seq_numbers; - WrapTest(0xFFFF - 2, 0xFFFFFFFF - 1000, drop_seq_numbers); -} - -} // namespace diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/accelerate.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/accelerate.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/accelerate.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/accelerate.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/accelerate.h" - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" - -namespace webrtc { - -Accelerate::ReturnCodes Accelerate::Process( - const int16_t* input, - size_t input_length, - AudioMultiVector* output, - int16_t* length_change_samples) { - // Input length must be (almost) 30 ms. - static const int k15ms = 120; // 15 ms = 120 samples at 8 kHz sample rate. - if (num_channels_ == 0 || static_cast(input_length) / num_channels_ < - (2 * k15ms - 1) * fs_mult_) { - // Length of input data too short to do accelerate. Simply move all data - // from input to output. - output->PushBackInterleaved(input, input_length); - return kError; - } - return TimeStretch::Process(input, input_length, output, - length_change_samples); -} - -void Accelerate::SetParametersForPassiveSpeech(size_t /*len*/, - int16_t* best_correlation, - int* /*peak_index*/) const { - // When the signal does not contain any active speech, the correlation does - // not matter. Simply set it to zero. - *best_correlation = 0; -} - -Accelerate::ReturnCodes Accelerate::CheckCriteriaAndStretch( - const int16_t* input, size_t input_length, size_t peak_index, - int16_t best_correlation, bool active_speech, - AudioMultiVector* output) const { - // Check for strong correlation or passive speech. - if ((best_correlation > kCorrelationThreshold) || !active_speech) { - // Do accelerate operation by overlap add. - - // Pre-calculate common multiplication with |fs_mult_|. - // 120 corresponds to 15 ms. - size_t fs_mult_120 = fs_mult_ * 120; - - assert(fs_mult_120 >= peak_index); // Should be handled in Process(). - // Copy first part; 0 to 15 ms. - output->PushBackInterleaved(input, fs_mult_120 * num_channels_); - // Copy the |peak_index| starting at 15 ms to |temp_vector|. - AudioMultiVector temp_vector(num_channels_); - temp_vector.PushBackInterleaved(&input[fs_mult_120 * num_channels_], - peak_index * num_channels_); - // Cross-fade |temp_vector| onto the end of |output|. - output->CrossFade(temp_vector, peak_index); - // Copy the last unmodified part, 15 ms + pitch period until the end. - output->PushBackInterleaved( - &input[(fs_mult_120 + peak_index) * num_channels_], - input_length - (fs_mult_120 + peak_index) * num_channels_); - - if (active_speech) { - return kSuccess; - } else { - return kSuccessLowEnergy; - } - } else { - // Accelerate not allowed. Simply move all data from decoded to outData. - output->PushBackInterleaved(input, input_length); - return kNoStretch; - } -} - -Accelerate* AccelerateFactory::Create( - int sample_rate_hz, - size_t num_channels, - const BackgroundNoise& background_noise) const { - return new Accelerate(sample_rate_hz, num_channels, background_noise); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/accelerate.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/accelerate.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/accelerate.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/accelerate.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_ACCELERATE_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_ACCELERATE_H_ - -#include - -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" -#include "webrtc/modules/audio_coding/neteq4/time_stretch.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Forward declarations. -class BackgroundNoise; - -// This class implements the Accelerate operation. Most of the work is done -// in the base class TimeStretch, which is shared with the PreemptiveExpand -// operation. In the Accelerate class, the operations that are specific to -// Accelerate are implemented. -class Accelerate : public TimeStretch { - public: - Accelerate(int sample_rate_hz, size_t num_channels, - const BackgroundNoise& background_noise) - : TimeStretch(sample_rate_hz, num_channels, background_noise) { - } - - virtual ~Accelerate() {} - - // This method performs the actual Accelerate operation. The samples are - // read from |input|, of length |input_length| elements, and are written to - // |output|. The number of samples removed through time-stretching is - // is provided in the output |length_change_samples|. The method returns - // the outcome of the operation as an enumerator value. - ReturnCodes Process(const int16_t* input, - size_t input_length, - AudioMultiVector* output, - int16_t* length_change_samples); - - protected: - // Sets the parameters |best_correlation| and |peak_index| to suitable - // values when the signal contains no active speech. - virtual void SetParametersForPassiveSpeech(size_t len, - int16_t* best_correlation, - int* peak_index) const OVERRIDE; - - // Checks the criteria for performing the time-stretching operation and, - // if possible, performs the time-stretching. - virtual ReturnCodes CheckCriteriaAndStretch( - const int16_t* input, size_t input_length, size_t peak_index, - int16_t best_correlation, bool active_speech, - AudioMultiVector* output) const OVERRIDE; - - private: - DISALLOW_COPY_AND_ASSIGN(Accelerate); -}; - -struct AccelerateFactory { - AccelerateFactory() {} - virtual ~AccelerateFactory() {} - - virtual Accelerate* Create(int sample_rate_hz, - size_t num_channels, - const BackgroundNoise& background_noise) const; -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_ACCELERATE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,254 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" - -#include - -#include "webrtc/modules/audio_coding/neteq4/audio_decoder_impl.h" - -namespace webrtc { - -int AudioDecoder::DecodeRedundant(const uint8_t* encoded, - size_t encoded_len, - int16_t* decoded, - SpeechType* speech_type) { - return Decode(encoded, encoded_len, decoded, speech_type); -} - -bool AudioDecoder::HasDecodePlc() const { return false; } - -int AudioDecoder::DecodePlc(int num_frames, int16_t* decoded) { return -1; } - -int AudioDecoder::IncomingPacket(const uint8_t* payload, - size_t payload_len, - uint16_t rtp_sequence_number, - uint32_t rtp_timestamp, - uint32_t arrival_timestamp) { - return 0; -} - -int AudioDecoder::ErrorCode() { return 0; } - -int AudioDecoder::PacketDuration(const uint8_t* encoded, size_t encoded_len) { - return kNotImplemented; -} - -NetEqDecoder AudioDecoder::codec_type() const { return codec_type_; } - -bool AudioDecoder::CodecSupported(NetEqDecoder codec_type) { - switch (codec_type) { - case kDecoderPCMu: - case kDecoderPCMa: - case kDecoderPCMu_2ch: - case kDecoderPCMa_2ch: -#ifdef WEBRTC_CODEC_ILBC - case kDecoderILBC: -#endif -#if defined(WEBRTC_CODEC_ISACFX) || defined(WEBRTC_CODEC_ISAC) - case kDecoderISAC: -#endif -#ifdef WEBRTC_CODEC_ISAC - case kDecoderISACswb: - case kDecoderISACfb: -#endif -#ifdef WEBRTC_CODEC_PCM16 - case kDecoderPCM16B: - case kDecoderPCM16Bwb: - case kDecoderPCM16Bswb32kHz: - case kDecoderPCM16Bswb48kHz: - case kDecoderPCM16B_2ch: - case kDecoderPCM16Bwb_2ch: - case kDecoderPCM16Bswb32kHz_2ch: - case kDecoderPCM16Bswb48kHz_2ch: - case kDecoderPCM16B_5ch: -#endif -#ifdef WEBRTC_CODEC_G722 - case kDecoderG722: - case kDecoderG722_2ch: -#endif -#ifdef WEBRTC_CODEC_CELT - case kDecoderCELT_32: - case kDecoderCELT_32_2ch: -#endif -#ifdef WEBRTC_CODEC_OPUS - case kDecoderOpus: - case kDecoderOpus_2ch: -#endif - case kDecoderRED: - case kDecoderAVT: - case kDecoderCNGnb: - case kDecoderCNGwb: - case kDecoderCNGswb32kHz: - case kDecoderCNGswb48kHz: - case kDecoderArbitrary: { - return true; - } - default: { - return false; - } - } -} - -int AudioDecoder::CodecSampleRateHz(NetEqDecoder codec_type) { - switch (codec_type) { - case kDecoderPCMu: - case kDecoderPCMa: - case kDecoderPCMu_2ch: - case kDecoderPCMa_2ch: -#ifdef WEBRTC_CODEC_ILBC - case kDecoderILBC: -#endif -#ifdef WEBRTC_CODEC_PCM16 - case kDecoderPCM16B: - case kDecoderPCM16B_2ch: - case kDecoderPCM16B_5ch: -#endif - case kDecoderCNGnb: { - return 8000; - } -#if defined(WEBRTC_CODEC_ISACFX) || defined(WEBRTC_CODEC_ISAC) - case kDecoderISAC: -#endif -#ifdef WEBRTC_CODEC_PCM16 - case kDecoderPCM16Bwb: - case kDecoderPCM16Bwb_2ch: -#endif -#ifdef WEBRTC_CODEC_G722 - case kDecoderG722: - case kDecoderG722_2ch: -#endif - case kDecoderCNGwb: { - return 16000; - } -#ifdef WEBRTC_CODEC_ISAC - case kDecoderISACswb: - case kDecoderISACfb: -#endif -#ifdef WEBRTC_CODEC_PCM16 - case kDecoderPCM16Bswb32kHz: - case kDecoderPCM16Bswb32kHz_2ch: -#endif -#ifdef WEBRTC_CODEC_CELT - case kDecoderCELT_32: - case kDecoderCELT_32_2ch: -#endif - case kDecoderCNGswb32kHz: { - return 32000; - } -#ifdef WEBRTC_CODEC_PCM16 - case kDecoderPCM16Bswb48kHz: - case kDecoderPCM16Bswb48kHz_2ch: { - return 48000; - } -#endif -#ifdef WEBRTC_CODEC_OPUS - case kDecoderOpus: - case kDecoderOpus_2ch: { - return 32000; - } -#endif - case kDecoderCNGswb48kHz: { - // TODO(tlegrand): Remove limitation once ACM has full 48 kHz support. - return 32000; - } - default: { - return -1; // Undefined sample rate. - } - } -} - -AudioDecoder* AudioDecoder::CreateAudioDecoder(NetEqDecoder codec_type) { - if (!CodecSupported(codec_type)) { - return NULL; - } - switch (codec_type) { - case kDecoderPCMu: - return new AudioDecoderPcmU; - case kDecoderPCMa: - return new AudioDecoderPcmA; - case kDecoderPCMu_2ch: - return new AudioDecoderPcmUMultiCh(2); - case kDecoderPCMa_2ch: - return new AudioDecoderPcmAMultiCh(2); -#ifdef WEBRTC_CODEC_ILBC - case kDecoderILBC: - return new AudioDecoderIlbc; -#endif -#if defined(WEBRTC_CODEC_ISACFX) - case kDecoderISAC: - return new AudioDecoderIsacFix; -#elif defined(WEBRTC_CODEC_ISAC) - case kDecoderISAC: - return new AudioDecoderIsac; -#endif -#ifdef WEBRTC_CODEC_ISAC - case kDecoderISACswb: - return new AudioDecoderIsacSwb; - case kDecoderISACfb: - return new AudioDecoderIsacFb; -#endif -#ifdef WEBRTC_CODEC_PCM16 - case kDecoderPCM16B: - case kDecoderPCM16Bwb: - case kDecoderPCM16Bswb32kHz: - case kDecoderPCM16Bswb48kHz: - return new AudioDecoderPcm16B(codec_type); - case kDecoderPCM16B_2ch: - case kDecoderPCM16Bwb_2ch: - case kDecoderPCM16Bswb32kHz_2ch: - case kDecoderPCM16Bswb48kHz_2ch: - case kDecoderPCM16B_5ch: - return new AudioDecoderPcm16BMultiCh(codec_type); -#endif -#ifdef WEBRTC_CODEC_G722 - case kDecoderG722: - return new AudioDecoderG722; - case kDecoderG722_2ch: - return new AudioDecoderG722Stereo; -#endif -#ifdef WEBRTC_CODEC_CELT - case kDecoderCELT_32: - case kDecoderCELT_32_2ch: - return new AudioDecoderCelt(codec_type); -#endif -#ifdef WEBRTC_CODEC_OPUS - case kDecoderOpus: - case kDecoderOpus_2ch: - return new AudioDecoderOpus(codec_type); -#endif - case kDecoderCNGnb: - case kDecoderCNGwb: - case kDecoderCNGswb32kHz: - case kDecoderCNGswb48kHz: - return new AudioDecoderCng(codec_type); - case kDecoderRED: - case kDecoderAVT: - case kDecoderArbitrary: - default: { - return NULL; - } - } -} - -AudioDecoder::SpeechType AudioDecoder::ConvertSpeechType(int16_t type) { - switch (type) { - case 0: // TODO(hlundin): Both iSAC and Opus return 0 for speech. - case 1: - return kSpeech; - case 2: - return kComfortNoise; - default: - assert(false); - return kSpeech; - } -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder_impl.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,491 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/audio_decoder_impl.h" - -#include -#include // memmove - -#ifdef WEBRTC_CODEC_CELT -#include "webrtc/modules/audio_coding/codecs/celt/include/celt_interface.h" -#endif -#include "webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h" -#include "webrtc/modules/audio_coding/codecs/g711/include/g711_interface.h" -#ifdef WEBRTC_CODEC_G722 -#include "webrtc/modules/audio_coding/codecs/g722/include/g722_interface.h" -#endif -#ifdef WEBRTC_CODEC_ILBC -#include "webrtc/modules/audio_coding/codecs/ilbc/interface/ilbc.h" -#endif -#ifdef WEBRTC_CODEC_ISACFX -#include "webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h" -#endif -#ifdef WEBRTC_CODEC_ISAC -#include "webrtc/modules/audio_coding/codecs/isac/main/interface/isac.h" -#endif -#ifdef WEBRTC_CODEC_OPUS -#include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" -#endif -#ifdef WEBRTC_CODEC_PCM16 -#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" -#endif - -namespace webrtc { - -// PCMu -int AudioDecoderPcmU::Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type) { - int16_t temp_type = 1; // Default is speech. - int16_t ret = WebRtcG711_DecodeU( - state_, reinterpret_cast(const_cast(encoded)), - static_cast(encoded_len), decoded, &temp_type); - *speech_type = ConvertSpeechType(temp_type); - return ret; -} - -int AudioDecoderPcmU::PacketDuration(const uint8_t* encoded, - size_t encoded_len) { - // One encoded byte per sample per channel. - return static_cast(encoded_len / channels_); -} - -// PCMa -int AudioDecoderPcmA::Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type) { - int16_t temp_type = 1; // Default is speech. - int16_t ret = WebRtcG711_DecodeA( - state_, reinterpret_cast(const_cast(encoded)), - static_cast(encoded_len), decoded, &temp_type); - *speech_type = ConvertSpeechType(temp_type); - return ret; -} - -int AudioDecoderPcmA::PacketDuration(const uint8_t* encoded, - size_t encoded_len) { - // One encoded byte per sample per channel. - return static_cast(encoded_len / channels_); -} - -// PCM16B -#ifdef WEBRTC_CODEC_PCM16 -AudioDecoderPcm16B::AudioDecoderPcm16B(enum NetEqDecoder type) - : AudioDecoder(type) { - assert(type == kDecoderPCM16B || - type == kDecoderPCM16Bwb || - type == kDecoderPCM16Bswb32kHz || - type == kDecoderPCM16Bswb48kHz); -} - -int AudioDecoderPcm16B::Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type) { - int16_t temp_type = 1; // Default is speech. - int16_t ret = WebRtcPcm16b_DecodeW16( - state_, reinterpret_cast(const_cast(encoded)), - static_cast(encoded_len), decoded, &temp_type); - *speech_type = ConvertSpeechType(temp_type); - return ret; -} - -int AudioDecoderPcm16B::PacketDuration(const uint8_t* encoded, - size_t encoded_len) { - // Two encoded byte per sample per channel. - return static_cast(encoded_len / (2 * channels_)); -} - -AudioDecoderPcm16BMultiCh::AudioDecoderPcm16BMultiCh( - enum NetEqDecoder type) - : AudioDecoderPcm16B(kDecoderPCM16B) { // This will be changed below. - codec_type_ = type; // Changing to actual type here. - switch (codec_type_) { - case kDecoderPCM16B_2ch: - case kDecoderPCM16Bwb_2ch: - case kDecoderPCM16Bswb32kHz_2ch: - case kDecoderPCM16Bswb48kHz_2ch: - channels_ = 2; - break; - case kDecoderPCM16B_5ch: - channels_ = 5; - break; - default: - assert(false); - } -} -#endif - -// iLBC -#ifdef WEBRTC_CODEC_ILBC -AudioDecoderIlbc::AudioDecoderIlbc() : AudioDecoder(kDecoderILBC) { - WebRtcIlbcfix_DecoderCreate(reinterpret_cast(&state_)); -} - -AudioDecoderIlbc::~AudioDecoderIlbc() { - WebRtcIlbcfix_DecoderFree(static_cast(state_)); -} - -int AudioDecoderIlbc::Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type) { - int16_t temp_type = 1; // Default is speech. - int16_t ret = WebRtcIlbcfix_Decode(static_cast(state_), - reinterpret_cast(encoded), - static_cast(encoded_len), decoded, - &temp_type); - *speech_type = ConvertSpeechType(temp_type); - return ret; -} - -int AudioDecoderIlbc::DecodePlc(int num_frames, int16_t* decoded) { - return WebRtcIlbcfix_NetEqPlc(static_cast(state_), - decoded, num_frames); -} - -int AudioDecoderIlbc::Init() { - return WebRtcIlbcfix_Decoderinit30Ms(static_cast(state_)); -} -#endif - -// iSAC float -#ifdef WEBRTC_CODEC_ISAC -AudioDecoderIsac::AudioDecoderIsac() : AudioDecoder(kDecoderISAC) { - WebRtcIsac_Create(reinterpret_cast(&state_)); - WebRtcIsac_SetDecSampRate(static_cast(state_), 16000); -} - -AudioDecoderIsac::~AudioDecoderIsac() { - WebRtcIsac_Free(static_cast(state_)); -} - -int AudioDecoderIsac::Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type) { - int16_t temp_type = 1; // Default is speech. - int16_t ret = WebRtcIsac_Decode(static_cast(state_), - reinterpret_cast(encoded), - static_cast(encoded_len), decoded, - &temp_type); - *speech_type = ConvertSpeechType(temp_type); - return ret; -} - -int AudioDecoderIsac::DecodeRedundant(const uint8_t* encoded, - size_t encoded_len, int16_t* decoded, - SpeechType* speech_type) { - int16_t temp_type = 1; // Default is speech. - int16_t ret = WebRtcIsac_DecodeRcu(static_cast(state_), - reinterpret_cast(encoded), - static_cast(encoded_len), decoded, - &temp_type); - *speech_type = ConvertSpeechType(temp_type); - return ret; -} - -int AudioDecoderIsac::DecodePlc(int num_frames, int16_t* decoded) { - return WebRtcIsac_DecodePlc(static_cast(state_), - decoded, num_frames); -} - -int AudioDecoderIsac::Init() { - return WebRtcIsac_DecoderInit(static_cast(state_)); -} - -int AudioDecoderIsac::IncomingPacket(const uint8_t* payload, - size_t payload_len, - uint16_t rtp_sequence_number, - uint32_t rtp_timestamp, - uint32_t arrival_timestamp) { - return WebRtcIsac_UpdateBwEstimate(static_cast(state_), - reinterpret_cast(payload), - static_cast(payload_len), - rtp_sequence_number, - rtp_timestamp, - arrival_timestamp); -} - -int AudioDecoderIsac::ErrorCode() { - return WebRtcIsac_GetErrorCode(static_cast(state_)); -} - -// iSAC SWB -AudioDecoderIsacSwb::AudioDecoderIsacSwb() : AudioDecoderIsac() { - codec_type_ = kDecoderISACswb; - WebRtcIsac_SetDecSampRate(static_cast(state_), 32000); -} - -// iSAC FB -AudioDecoderIsacFb::AudioDecoderIsacFb() : AudioDecoderIsacSwb() { - codec_type_ = kDecoderISACfb; -} -#endif - -// iSAC fix -#ifdef WEBRTC_CODEC_ISACFX -AudioDecoderIsacFix::AudioDecoderIsacFix() : AudioDecoder(kDecoderISAC) { - WebRtcIsacfix_Create(reinterpret_cast(&state_)); -} - -AudioDecoderIsacFix::~AudioDecoderIsacFix() { - WebRtcIsacfix_Free(static_cast(state_)); -} - -int AudioDecoderIsacFix::Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type) { - int16_t temp_type = 1; // Default is speech. - int16_t ret = WebRtcIsacfix_Decode(static_cast(state_), - reinterpret_cast(encoded), - static_cast(encoded_len), decoded, - &temp_type); - *speech_type = ConvertSpeechType(temp_type); - return ret; -} - -int AudioDecoderIsacFix::Init() { - return WebRtcIsacfix_DecoderInit(static_cast(state_)); -} - -int AudioDecoderIsacFix::IncomingPacket(const uint8_t* payload, - size_t payload_len, - uint16_t rtp_sequence_number, - uint32_t rtp_timestamp, - uint32_t arrival_timestamp) { - return WebRtcIsacfix_UpdateBwEstimate( - static_cast(state_), - reinterpret_cast(payload), - static_cast(payload_len), - rtp_sequence_number, rtp_timestamp, arrival_timestamp); -} - -int AudioDecoderIsacFix::ErrorCode() { - return WebRtcIsacfix_GetErrorCode(static_cast(state_)); -} -#endif - -// G.722 -#ifdef WEBRTC_CODEC_G722 -AudioDecoderG722::AudioDecoderG722() : AudioDecoder(kDecoderG722) { - WebRtcG722_CreateDecoder(reinterpret_cast(&state_)); -} - -AudioDecoderG722::~AudioDecoderG722() { - WebRtcG722_FreeDecoder(static_cast(state_)); -} - -int AudioDecoderG722::Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type) { - int16_t temp_type = 1; // Default is speech. - int16_t ret = WebRtcG722_Decode( - static_cast(state_), - const_cast(reinterpret_cast(encoded)), - static_cast(encoded_len), decoded, &temp_type); - *speech_type = ConvertSpeechType(temp_type); - return ret; -} - -int AudioDecoderG722::Init() { - return WebRtcG722_DecoderInit(static_cast(state_)); -} - -int AudioDecoderG722::PacketDuration(const uint8_t* encoded, - size_t encoded_len) { - // 1/2 encoded byte per sample per channel. - return static_cast(2 * encoded_len / channels_); -} - -AudioDecoderG722Stereo::AudioDecoderG722Stereo() - : AudioDecoderG722(), - state_left_(state_), // Base member |state_| is used for left channel. - state_right_(NULL) { - channels_ = 2; - // |state_left_| already created by the base class AudioDecoderG722. - WebRtcG722_CreateDecoder(reinterpret_cast(&state_right_)); -} - -AudioDecoderG722Stereo::~AudioDecoderG722Stereo() { - // |state_left_| will be freed by the base class AudioDecoderG722. - WebRtcG722_FreeDecoder(static_cast(state_right_)); -} - -int AudioDecoderG722Stereo::Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type) { - int16_t temp_type = 1; // Default is speech. - // De-interleave the bit-stream into two separate payloads. - uint8_t* encoded_deinterleaved = new uint8_t[encoded_len]; - SplitStereoPacket(encoded, encoded_len, encoded_deinterleaved); - // Decode left and right. - int16_t ret = WebRtcG722_Decode( - static_cast(state_left_), - reinterpret_cast(encoded_deinterleaved), - static_cast(encoded_len / 2), decoded, &temp_type); - if (ret >= 0) { - int decoded_len = ret; - ret = WebRtcG722_Decode( - static_cast(state_right_), - reinterpret_cast(&encoded_deinterleaved[encoded_len / 2]), - static_cast(encoded_len / 2), &decoded[decoded_len], &temp_type); - if (ret == decoded_len) { - decoded_len += ret; - // Interleave output. - for (int k = decoded_len / 2; k < decoded_len; k++) { - int16_t temp = decoded[k]; - memmove(&decoded[2 * k - decoded_len + 2], - &decoded[2 * k - decoded_len + 1], - (decoded_len - k - 1) * sizeof(int16_t)); - decoded[2 * k - decoded_len + 1] = temp; - } - ret = decoded_len; // Return total number of samples. - } - } - *speech_type = ConvertSpeechType(temp_type); - delete [] encoded_deinterleaved; - return ret; -} - -int AudioDecoderG722Stereo::Init() { - int ret = WebRtcG722_DecoderInit(static_cast(state_right_)); - if (ret != 0) { - return ret; - } - return AudioDecoderG722::Init(); -} - -// Split the stereo packet and place left and right channel after each other -// in the output array. -void AudioDecoderG722Stereo::SplitStereoPacket(const uint8_t* encoded, - size_t encoded_len, - uint8_t* encoded_deinterleaved) { - assert(encoded); - // Regroup the 4 bits/sample so |l1 l2| |r1 r2| |l3 l4| |r3 r4| ..., - // where "lx" is 4 bits representing left sample number x, and "rx" right - // sample. Two samples fit in one byte, represented with |...|. - for (size_t i = 0; i + 1 < encoded_len; i += 2) { - uint8_t right_byte = ((encoded[i] & 0x0F) << 4) + (encoded[i + 1] & 0x0F); - encoded_deinterleaved[i] = (encoded[i] & 0xF0) + (encoded[i + 1] >> 4); - encoded_deinterleaved[i + 1] = right_byte; - } - - // Move one byte representing right channel each loop, and place it at the - // end of the bytestream vector. After looping the data is reordered to: - // |l1 l2| |l3 l4| ... |l(N-1) lN| |r1 r2| |r3 r4| ... |r(N-1) r(N)|, - // where N is the total number of samples. - for (size_t i = 0; i < encoded_len / 2; i++) { - uint8_t right_byte = encoded_deinterleaved[i + 1]; - memmove(&encoded_deinterleaved[i + 1], &encoded_deinterleaved[i + 2], - encoded_len - i - 2); - encoded_deinterleaved[encoded_len - 1] = right_byte; - } -} -#endif - -// CELT -#ifdef WEBRTC_CODEC_CELT -AudioDecoderCelt::AudioDecoderCelt(enum NetEqDecoder type) - : AudioDecoder(type) { - assert(type == kDecoderCELT_32 || type == kDecoderCELT_32_2ch); - if (type == kDecoderCELT_32) { - channels_ = 1; - } else { - channels_ = 2; - } - WebRtcCelt_CreateDec(reinterpret_cast(&state_), - static_cast(channels_)); -} - -AudioDecoderCelt::~AudioDecoderCelt() { - WebRtcCelt_FreeDec(static_cast(state_)); -} - -int AudioDecoderCelt::Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type) { - int16_t temp_type = 1; // Default to speech. - int ret = WebRtcCelt_DecodeUniversal(static_cast(state_), - encoded, static_cast(encoded_len), - decoded, &temp_type); - *speech_type = ConvertSpeechType(temp_type); - if (ret < 0) { - return -1; - } - // Return the total number of samples. - return ret * static_cast(channels_); -} - -int AudioDecoderCelt::Init() { - return WebRtcCelt_DecoderInit(static_cast(state_)); -} - -bool AudioDecoderCelt::HasDecodePlc() const { return true; } - -int AudioDecoderCelt::DecodePlc(int num_frames, int16_t* decoded) { - int ret = WebRtcCelt_DecodePlc(static_cast(state_), - decoded, num_frames); - if (ret < 0) { - return -1; - } - // Return the total number of samples. - return ret * static_cast(channels_); -} -#endif - -// Opus -#ifdef WEBRTC_CODEC_OPUS -AudioDecoderOpus::AudioDecoderOpus(enum NetEqDecoder type) - : AudioDecoder(type) { - if (type == kDecoderOpus_2ch) { - channels_ = 2; - } else { - channels_ = 1; - } - WebRtcOpus_DecoderCreate(reinterpret_cast(&state_), - static_cast(channels_)); -} - -AudioDecoderOpus::~AudioDecoderOpus() { - WebRtcOpus_DecoderFree(static_cast(state_)); -} - -int AudioDecoderOpus::Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type) { - int16_t temp_type = 1; // Default is speech. - int16_t ret = WebRtcOpus_DecodeNew(static_cast(state_), encoded, - static_cast(encoded_len), decoded, - &temp_type); - if (ret > 0) - ret *= static_cast(channels_); // Return total number of samples. - *speech_type = ConvertSpeechType(temp_type); - return ret; -} - -int AudioDecoderOpus::Init() { - return WebRtcOpus_DecoderInitNew(static_cast(state_)); -} - -int AudioDecoderOpus::PacketDuration(const uint8_t* encoded, - size_t encoded_len) { - return WebRtcOpus_DurationEst(static_cast(state_), - encoded, static_cast(encoded_len)); -} -#endif - -AudioDecoderCng::AudioDecoderCng(enum NetEqDecoder type) - : AudioDecoder(type) { - assert(type == kDecoderCNGnb || type == kDecoderCNGwb || - kDecoderCNGswb32kHz || type == kDecoderCNGswb48kHz); - WebRtcCng_CreateDec(reinterpret_cast(&state_)); - assert(state_); -} - -AudioDecoderCng::~AudioDecoderCng() { - if (state_) { - WebRtcCng_FreeDec(static_cast(state_)); - } -} - -int AudioDecoderCng::Init() { - assert(state_); - return WebRtcCng_InitDec(static_cast(state_)); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder_impl.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,271 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_AUDIO_DECODER_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_AUDIO_DECODER_IMPL_H_ - -#include - -#ifndef AUDIO_DECODER_UNITTEST -// If this is compiled as a part of the audio_deoder_unittest, the codec -// selection is made in the gypi file instead of in engine_configurations.h. -#include "webrtc/engine_configurations.h" -#endif -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -class AudioDecoderPcmU : public AudioDecoder { - public: - AudioDecoderPcmU() : AudioDecoder(kDecoderPCMu) {} - virtual int Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type); - virtual int Init() { return 0; } - virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcmU); -}; - -class AudioDecoderPcmA : public AudioDecoder { - public: - AudioDecoderPcmA() : AudioDecoder(kDecoderPCMa) {} - virtual int Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type); - virtual int Init() { return 0; } - virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcmA); -}; - -class AudioDecoderPcmUMultiCh : public AudioDecoderPcmU { - public: - explicit AudioDecoderPcmUMultiCh(size_t channels) : AudioDecoderPcmU() { - assert(channels > 0); - channels_ = channels; - } - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcmUMultiCh); -}; - -class AudioDecoderPcmAMultiCh : public AudioDecoderPcmA { - public: - explicit AudioDecoderPcmAMultiCh(size_t channels) : AudioDecoderPcmA() { - assert(channels > 0); - channels_ = channels; - } - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcmAMultiCh); -}; - -#ifdef WEBRTC_CODEC_PCM16 -// This class handles all four types (i.e., sample rates) of PCM16B codecs. -// The type is specified in the constructor parameter |type|. -class AudioDecoderPcm16B : public AudioDecoder { - public: - explicit AudioDecoderPcm16B(enum NetEqDecoder type); - virtual int Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type); - virtual int Init() { return 0; } - virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcm16B); -}; - -// This class handles all four types (i.e., sample rates) of PCM16B codecs. -// The type is specified in the constructor parameter |type|, and the number -// of channels is derived from the type. -class AudioDecoderPcm16BMultiCh : public AudioDecoderPcm16B { - public: - explicit AudioDecoderPcm16BMultiCh(enum NetEqDecoder type); - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcm16BMultiCh); -}; -#endif - -#ifdef WEBRTC_CODEC_ILBC -class AudioDecoderIlbc : public AudioDecoder { - public: - AudioDecoderIlbc(); - virtual ~AudioDecoderIlbc(); - virtual int Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type); - virtual bool HasDecodePlc() const { return true; } - virtual int DecodePlc(int num_frames, int16_t* decoded); - virtual int Init(); - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderIlbc); -}; -#endif - -#ifdef WEBRTC_CODEC_ISAC -class AudioDecoderIsac : public AudioDecoder { - public: - AudioDecoderIsac(); - virtual ~AudioDecoderIsac(); - virtual int Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type); - virtual int DecodeRedundant(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type); - virtual bool HasDecodePlc() const { return true; } - virtual int DecodePlc(int num_frames, int16_t* decoded); - virtual int Init(); - virtual int IncomingPacket(const uint8_t* payload, - size_t payload_len, - uint16_t rtp_sequence_number, - uint32_t rtp_timestamp, - uint32_t arrival_timestamp); - virtual int ErrorCode(); - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderIsac); -}; - -class AudioDecoderIsacSwb : public AudioDecoderIsac { - public: - AudioDecoderIsacSwb(); - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderIsacSwb); -}; - -class AudioDecoderIsacFb : public AudioDecoderIsacSwb { - public: - AudioDecoderIsacFb(); - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderIsacFb); -}; -#endif - -#ifdef WEBRTC_CODEC_ISACFX -class AudioDecoderIsacFix : public AudioDecoder { - public: - AudioDecoderIsacFix(); - virtual ~AudioDecoderIsacFix(); - virtual int Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type); - virtual int Init(); - virtual int IncomingPacket(const uint8_t* payload, - size_t payload_len, - uint16_t rtp_sequence_number, - uint32_t rtp_timestamp, - uint32_t arrival_timestamp); - virtual int ErrorCode(); - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderIsacFix); -}; -#endif - -#ifdef WEBRTC_CODEC_G722 -class AudioDecoderG722 : public AudioDecoder { - public: - AudioDecoderG722(); - virtual ~AudioDecoderG722(); - virtual int Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type); - virtual bool HasDecodePlc() const { return false; } - virtual int Init(); - virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderG722); -}; - -class AudioDecoderG722Stereo : public AudioDecoderG722 { - public: - AudioDecoderG722Stereo(); - virtual ~AudioDecoderG722Stereo(); - virtual int Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type); - virtual int Init(); - - private: - // Splits the stereo-interleaved payload in |encoded| into separate payloads - // for left and right channels. The separated payloads are written to - // |encoded_deinterleaved|, which must hold at least |encoded_len| samples. - // The left channel starts at offset 0, while the right channel starts at - // offset encoded_len / 2 into |encoded_deinterleaved|. - void SplitStereoPacket(const uint8_t* encoded, size_t encoded_len, - uint8_t* encoded_deinterleaved); - - void* const state_left_; - void* state_right_; - - DISALLOW_COPY_AND_ASSIGN(AudioDecoderG722Stereo); -}; -#endif - -#ifdef WEBRTC_CODEC_CELT -class AudioDecoderCelt : public AudioDecoder { - public: - explicit AudioDecoderCelt(enum NetEqDecoder type); - virtual ~AudioDecoderCelt(); - - virtual int Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type); - virtual int Init(); - virtual bool HasDecodePlc() const; - virtual int DecodePlc(int num_frames, int16_t* decoded); - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderCelt); -}; -#endif - -#ifdef WEBRTC_CODEC_OPUS -class AudioDecoderOpus : public AudioDecoder { - public: - explicit AudioDecoderOpus(enum NetEqDecoder type); - virtual ~AudioDecoderOpus(); - virtual int Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type); - virtual int Init(); - virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderOpus); -}; -#endif - -// AudioDecoderCng is a special type of AudioDecoder. It inherits from -// AudioDecoder just to fit in the DecoderDatabase. None of the class methods -// should be used, except constructor, destructor, and accessors. -// TODO(hlundin): Consider the possibility to create a super-class to -// AudioDecoder that is stored in DecoderDatabase. Then AudioDecoder and a -// specific CngDecoder class could both inherit from that class. -class AudioDecoderCng : public AudioDecoder { - public: - explicit AudioDecoderCng(enum NetEqDecoder type); - virtual ~AudioDecoderCng(); - virtual int Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type) { return -1; } - virtual int Init(); - virtual int IncomingPacket(const uint8_t* payload, - size_t payload_len, - uint16_t rtp_sequence_number, - uint32_t rtp_timestamp, - uint32_t arrival_timestamp) { return -1; } - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderCng); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_AUDIO_DECODER_IMPL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,931 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/audio_decoder_impl.h" - -#include -#include - -#include - -#include "gtest/gtest.h" -#include "webrtc/common_audio/resampler/include/resampler.h" -#ifdef WEBRTC_CODEC_CELT -#include "webrtc/modules/audio_coding/codecs/celt/include/celt_interface.h" -#endif -#include "webrtc/modules/audio_coding/codecs/g711/include/g711_interface.h" -#include "webrtc/modules/audio_coding/codecs/g722/include/g722_interface.h" -#include "webrtc/modules/audio_coding/codecs/ilbc/interface/ilbc.h" -#include "webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h" -#include "webrtc/modules/audio_coding/codecs/isac/main/interface/isac.h" -#include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" -#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" -#include "webrtc/system_wrappers/interface/data_log.h" -#include "webrtc/test/testsupport/fileutils.h" - -namespace webrtc { - -class AudioDecoderTest : public ::testing::Test { - protected: - AudioDecoderTest() - : input_fp_(NULL), - input_(NULL), - encoded_(NULL), - decoded_(NULL), - frame_size_(0), - data_length_(0), - encoded_bytes_(0), - channels_(1), - decoder_(NULL) { - input_file_ = webrtc::test::ProjectRootPath() + - "resources/audio_coding/testfile32kHz.pcm"; - } - - virtual ~AudioDecoderTest() {} - - virtual void SetUp() { - // Create arrays. - ASSERT_GT(data_length_, 0u) << "The test must set data_length_ > 0"; - input_ = new int16_t[data_length_]; - encoded_ = new uint8_t[data_length_ * 2]; - decoded_ = new int16_t[data_length_ * channels_]; - // Open input file. - input_fp_ = fopen(input_file_.c_str(), "rb"); - ASSERT_TRUE(input_fp_ != NULL) << "Failed to open file " << input_file_; - // Read data to |input_|. - ASSERT_EQ(data_length_, - fread(input_, sizeof(int16_t), data_length_, input_fp_)) << - "Could not read enough data from file"; - // Logging to view input and output in Matlab. - // Use 'gyp -Denable_data_logging=1' to enable logging. - DataLog::CreateLog(); - DataLog::AddTable("CodecTest"); - DataLog::AddColumn("CodecTest", "input", 1); - DataLog::AddColumn("CodecTest", "output", 1); - } - - virtual void TearDown() { - delete decoder_; - decoder_ = NULL; - // Close input file. - fclose(input_fp_); - // Delete arrays. - delete [] input_; - input_ = NULL; - delete [] encoded_; - encoded_ = NULL; - delete [] decoded_; - decoded_ = NULL; - // Close log. - DataLog::ReturnLog(); - } - - virtual void InitEncoder() { } - - // This method must be implemented for all tests derived from this class. - virtual int EncodeFrame(const int16_t* input, size_t input_len, - uint8_t* output) = 0; - - // Encodes and decodes audio. The absolute difference between the input and - // output is compared vs |tolerance|, and the mean-squared error is compared - // with |mse|. The encoded stream should contain |expected_bytes|. For stereo - // audio, the absolute difference between the two channels is compared vs - // |channel_diff_tolerance|. - void EncodeDecodeTest(size_t expected_bytes, int tolerance, double mse, - int delay = 0, int channel_diff_tolerance = 0) { - ASSERT_GE(tolerance, 0) << "Test must define a tolerance >= 0"; - ASSERT_GE(channel_diff_tolerance, 0) << - "Test must define a channel_diff_tolerance >= 0"; - size_t processed_samples = 0u; - encoded_bytes_ = 0u; - InitEncoder(); - EXPECT_EQ(0, decoder_->Init()); - while (processed_samples + frame_size_ <= data_length_) { - size_t enc_len = EncodeFrame(&input_[processed_samples], frame_size_, - &encoded_[encoded_bytes_]); - AudioDecoder::SpeechType speech_type; - size_t dec_len = decoder_->Decode(&encoded_[encoded_bytes_], enc_len, - &decoded_[processed_samples * - channels_], - &speech_type); - EXPECT_EQ(frame_size_ * channels_, dec_len); - encoded_bytes_ += enc_len; - processed_samples += frame_size_; - } - // For some codecs it doesn't make sense to check expected number of bytes, - // since the number can vary for different platforms. Opus and iSAC are - // such codecs. In this case expected_bytes is set to 0. - if (expected_bytes) { - EXPECT_EQ(expected_bytes, encoded_bytes_); - } - CompareInputOutput(processed_samples, tolerance, delay); - if (channels_ == 2) - CompareTwoChannels(processed_samples, channel_diff_tolerance); - EXPECT_LE(MseInputOutput(processed_samples, delay), mse); - } - - // The absolute difference between the input and output (the first channel) is - // compared vs |tolerance|. The parameter |delay| is used to correct for codec - // delays. - virtual void CompareInputOutput(size_t num_samples, int tolerance, - int delay) const { - assert(num_samples <= data_length_); - for (unsigned int n = 0; n < num_samples - delay; ++n) { - ASSERT_NEAR(input_[n], decoded_[channels_ * n + delay], tolerance) << - "Exit test on first diff; n = " << n; - DataLog::InsertCell("CodecTest", "input", input_[n]); - DataLog::InsertCell("CodecTest", "output", decoded_[channels_ * n]); - DataLog::NextRow("CodecTest"); - } - } - - // The absolute difference between the two channels in a stereo is compared vs - // |tolerance|. - virtual void CompareTwoChannels(size_t samples_per_channel, - int tolerance) const { - assert(samples_per_channel <= data_length_); - for (unsigned int n = 0; n < samples_per_channel; ++n) - ASSERT_NEAR(decoded_[channels_ * n], decoded_[channels_ * n + 1], - tolerance) << "Stereo samples differ."; - } - - // Calculates mean-squared error between input and output (the first channel). - // The parameter |delay| is used to correct for codec delays. - virtual double MseInputOutput(size_t num_samples, int delay) const { - assert(num_samples <= data_length_); - if (num_samples == 0) return 0.0; - double squared_sum = 0.0; - for (unsigned int n = 0; n < num_samples - delay; ++n) { - squared_sum += (input_[n] - decoded_[channels_ * n + delay]) * - (input_[n] - decoded_[channels_ * n + delay]); - } - return squared_sum / (num_samples - delay); - } - - // Encodes a payload and decodes it twice with decoder re-init before each - // decode. Verifies that the decoded result is the same. - void ReInitTest() { - uint8_t* encoded = encoded_; - uint8_t* encoded_copy = encoded_ + 2 * frame_size_; - int16_t* output1 = decoded_; - int16_t* output2 = decoded_ + frame_size_; - InitEncoder(); - size_t enc_len = EncodeFrame(input_, frame_size_, encoded); - size_t dec_len; - // Copy payload since iSAC fix destroys it during decode. - // Issue: http://code.google.com/p/webrtc/issues/detail?id=845. - // TODO(hlundin): Remove if the iSAC bug gets fixed. - memcpy(encoded_copy, encoded, enc_len); - AudioDecoder::SpeechType speech_type1, speech_type2; - EXPECT_EQ(0, decoder_->Init()); - dec_len = decoder_->Decode(encoded, enc_len, output1, &speech_type1); - EXPECT_EQ(frame_size_ * channels_, dec_len); - // Re-init decoder and decode again. - EXPECT_EQ(0, decoder_->Init()); - dec_len = decoder_->Decode(encoded_copy, enc_len, output2, &speech_type2); - EXPECT_EQ(frame_size_ * channels_, dec_len); - for (unsigned int n = 0; n < frame_size_; ++n) { - ASSERT_EQ(output1[n], output2[n]) << "Exit test on first diff; n = " << n; - } - EXPECT_EQ(speech_type1, speech_type2); - } - - // Call DecodePlc and verify that the correct number of samples is produced. - void DecodePlcTest() { - InitEncoder(); - size_t enc_len = EncodeFrame(input_, frame_size_, encoded_); - AudioDecoder::SpeechType speech_type; - EXPECT_EQ(0, decoder_->Init()); - size_t dec_len = - decoder_->Decode(encoded_, enc_len, decoded_, &speech_type); - EXPECT_EQ(frame_size_ * channels_, dec_len); - // Call DecodePlc and verify that we get one frame of data. - // (Overwrite the output from the above Decode call, but that does not - // matter.) - dec_len = decoder_->DecodePlc(1, decoded_); - EXPECT_EQ(frame_size_ * channels_, dec_len); - } - - std::string input_file_; - FILE* input_fp_; - int16_t* input_; - uint8_t* encoded_; - int16_t* decoded_; - size_t frame_size_; - size_t data_length_; - size_t encoded_bytes_; - size_t channels_; - AudioDecoder* decoder_; -}; - -class AudioDecoderPcmUTest : public AudioDecoderTest { - protected: - AudioDecoderPcmUTest() : AudioDecoderTest() { - frame_size_ = 160; - data_length_ = 10 * frame_size_; - decoder_ = new AudioDecoderPcmU; - assert(decoder_); - } - - virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, - uint8_t* output) { - int enc_len_bytes = - WebRtcG711_EncodeU(NULL, const_cast(input), - static_cast(input_len_samples), - reinterpret_cast(output)); - EXPECT_EQ(input_len_samples, static_cast(enc_len_bytes)); - return enc_len_bytes; - } -}; - -class AudioDecoderPcmATest : public AudioDecoderTest { - protected: - AudioDecoderPcmATest() : AudioDecoderTest() { - frame_size_ = 160; - data_length_ = 10 * frame_size_; - decoder_ = new AudioDecoderPcmA; - assert(decoder_); - } - - virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, - uint8_t* output) { - int enc_len_bytes = - WebRtcG711_EncodeA(NULL, const_cast(input), - static_cast(input_len_samples), - reinterpret_cast(output)); - EXPECT_EQ(input_len_samples, static_cast(enc_len_bytes)); - return enc_len_bytes; - } -}; - -class AudioDecoderPcm16BTest : public AudioDecoderTest { - protected: - AudioDecoderPcm16BTest() : AudioDecoderTest() { - frame_size_ = 160; - data_length_ = 10 * frame_size_; - decoder_ = new AudioDecoderPcm16B(kDecoderPCM16B); - assert(decoder_); - } - - virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, - uint8_t* output) { - int enc_len_bytes = WebRtcPcm16b_EncodeW16( - const_cast(input), static_cast(input_len_samples), - reinterpret_cast(output)); - EXPECT_EQ(2 * input_len_samples, static_cast(enc_len_bytes)); - return enc_len_bytes; - } -}; - -class AudioDecoderIlbcTest : public AudioDecoderTest { - protected: - AudioDecoderIlbcTest() : AudioDecoderTest() { - frame_size_ = 240; - data_length_ = 10 * frame_size_; - decoder_ = new AudioDecoderIlbc; - assert(decoder_); - WebRtcIlbcfix_EncoderCreate(&encoder_); - } - - ~AudioDecoderIlbcTest() { - WebRtcIlbcfix_EncoderFree(encoder_); - } - - virtual void InitEncoder() { - ASSERT_EQ(0, WebRtcIlbcfix_EncoderInit(encoder_, 30)); // 30 ms. - } - - virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, - uint8_t* output) { - int enc_len_bytes = - WebRtcIlbcfix_Encode(encoder_, input, - static_cast(input_len_samples), - reinterpret_cast(output)); - EXPECT_EQ(50, enc_len_bytes); - return enc_len_bytes; - } - - // Overload the default test since iLBC's function WebRtcIlbcfix_NetEqPlc does - // not return any data. It simply resets a few states and returns 0. - void DecodePlcTest() { - InitEncoder(); - size_t enc_len = EncodeFrame(input_, frame_size_, encoded_); - AudioDecoder::SpeechType speech_type; - EXPECT_EQ(0, decoder_->Init()); - size_t dec_len = - decoder_->Decode(encoded_, enc_len, decoded_, &speech_type); - EXPECT_EQ(frame_size_, dec_len); - // Simply call DecodePlc and verify that we get 0 as return value. - EXPECT_EQ(0, decoder_->DecodePlc(1, decoded_)); - } - - iLBC_encinst_t* encoder_; -}; - -class AudioDecoderIsacFloatTest : public AudioDecoderTest { - protected: - AudioDecoderIsacFloatTest() : AudioDecoderTest() { - input_size_ = 160; - frame_size_ = 480; - data_length_ = 10 * frame_size_; - decoder_ = new AudioDecoderIsac; - assert(decoder_); - WebRtcIsac_Create(&encoder_); - WebRtcIsac_SetEncSampRate(encoder_, 16000); - } - - ~AudioDecoderIsacFloatTest() { - WebRtcIsac_Free(encoder_); - } - - virtual void InitEncoder() { - ASSERT_EQ(0, WebRtcIsac_EncoderInit(encoder_, 1)); // Fixed mode. - ASSERT_EQ(0, WebRtcIsac_Control(encoder_, 32000, 30)); // 32 kbps, 30 ms. - } - - virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, - uint8_t* output) { - // Insert 3 * 10 ms. Expect non-zero output on third call. - EXPECT_EQ(0, WebRtcIsac_Encode(encoder_, input, - reinterpret_cast(output))); - input += input_size_; - EXPECT_EQ(0, WebRtcIsac_Encode(encoder_, input, - reinterpret_cast(output))); - input += input_size_; - int enc_len_bytes = - WebRtcIsac_Encode(encoder_, input, reinterpret_cast(output)); - EXPECT_GT(enc_len_bytes, 0); - return enc_len_bytes; - } - - ISACStruct* encoder_; - int input_size_; -}; - -class AudioDecoderIsacSwbTest : public AudioDecoderTest { - protected: - AudioDecoderIsacSwbTest() : AudioDecoderTest() { - input_size_ = 320; - frame_size_ = 960; - data_length_ = 10 * frame_size_; - decoder_ = new AudioDecoderIsacSwb; - assert(decoder_); - WebRtcIsac_Create(&encoder_); - WebRtcIsac_SetEncSampRate(encoder_, 32000); - } - - ~AudioDecoderIsacSwbTest() { - WebRtcIsac_Free(encoder_); - } - - virtual void InitEncoder() { - ASSERT_EQ(0, WebRtcIsac_EncoderInit(encoder_, 1)); // Fixed mode. - ASSERT_EQ(0, WebRtcIsac_Control(encoder_, 32000, 30)); // 32 kbps, 30 ms. - } - - virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, - uint8_t* output) { - // Insert 3 * 10 ms. Expect non-zero output on third call. - EXPECT_EQ(0, WebRtcIsac_Encode(encoder_, input, - reinterpret_cast(output))); - input += input_size_; - EXPECT_EQ(0, WebRtcIsac_Encode(encoder_, input, - reinterpret_cast(output))); - input += input_size_; - int enc_len_bytes = - WebRtcIsac_Encode(encoder_, input, reinterpret_cast(output)); - EXPECT_GT(enc_len_bytes, 0); - return enc_len_bytes; - } - - ISACStruct* encoder_; - int input_size_; -}; - -// This test is identical to AudioDecoderIsacSwbTest, except that it creates -// an AudioDecoderIsacFb decoder object. -class AudioDecoderIsacFbTest : public AudioDecoderIsacSwbTest { - protected: - AudioDecoderIsacFbTest() : AudioDecoderIsacSwbTest() { - // Delete the |decoder_| that was created by AudioDecoderIsacSwbTest and - // create an AudioDecoderIsacFb object instead. - delete decoder_; - decoder_ = new AudioDecoderIsacFb; - assert(decoder_); - } -}; - -class AudioDecoderIsacFixTest : public AudioDecoderTest { - protected: - AudioDecoderIsacFixTest() : AudioDecoderTest() { - input_size_ = 160; - frame_size_ = 480; - data_length_ = 10 * frame_size_; - decoder_ = new AudioDecoderIsacFix; - assert(decoder_); - WebRtcIsacfix_Create(&encoder_); - } - - ~AudioDecoderIsacFixTest() { - WebRtcIsacfix_Free(encoder_); - } - - virtual void InitEncoder() { - ASSERT_EQ(0, WebRtcIsacfix_EncoderInit(encoder_, 1)); // Fixed mode. - ASSERT_EQ(0, - WebRtcIsacfix_Control(encoder_, 32000, 30)); // 32 kbps, 30 ms. - } - - virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, - uint8_t* output) { - // Insert 3 * 10 ms. Expect non-zero output on third call. - EXPECT_EQ(0, WebRtcIsacfix_Encode(encoder_, input, - reinterpret_cast(output))); - input += input_size_; - EXPECT_EQ(0, WebRtcIsacfix_Encode(encoder_, input, - reinterpret_cast(output))); - input += input_size_; - int enc_len_bytes = WebRtcIsacfix_Encode( - encoder_, input, reinterpret_cast(output)); - EXPECT_GT(enc_len_bytes, 0); - return enc_len_bytes; - } - - ISACFIX_MainStruct* encoder_; - int input_size_; -}; - -class AudioDecoderG722Test : public AudioDecoderTest { - protected: - AudioDecoderG722Test() : AudioDecoderTest() { - frame_size_ = 160; - data_length_ = 10 * frame_size_; - decoder_ = new AudioDecoderG722; - assert(decoder_); - WebRtcG722_CreateEncoder(&encoder_); - } - - ~AudioDecoderG722Test() { - WebRtcG722_FreeEncoder(encoder_); - } - - virtual void InitEncoder() { - ASSERT_EQ(0, WebRtcG722_EncoderInit(encoder_)); - } - - virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, - uint8_t* output) { - int enc_len_bytes = - WebRtcG722_Encode(encoder_, const_cast(input), - static_cast(input_len_samples), - reinterpret_cast(output)); - EXPECT_EQ(80, enc_len_bytes); - return enc_len_bytes; - } - - G722EncInst* encoder_; -}; - -class AudioDecoderG722StereoTest : public AudioDecoderG722Test { - protected: - AudioDecoderG722StereoTest() : AudioDecoderG722Test() { - channels_ = 2; - // Delete the |decoder_| that was created by AudioDecoderG722Test and - // create an AudioDecoderG722Stereo object instead. - delete decoder_; - decoder_ = new AudioDecoderG722Stereo; - assert(decoder_); - } - - virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, - uint8_t* output) { - uint8_t* temp_output = new uint8_t[data_length_ * 2]; - // Encode a mono payload using the base test class. - int mono_enc_len_bytes = - AudioDecoderG722Test::EncodeFrame(input, input_len_samples, - temp_output); - // The bit-stream consists of 4-bit samples: - // +--------+--------+--------+ - // | s0 s1 | s2 s3 | s4 s5 | - // +--------+--------+--------+ - // - // Duplicate them to the |output| such that the stereo stream becomes: - // +--------+--------+--------+ - // | s0 s0 | s1 s1 | s2 s2 | - // +--------+--------+--------+ - EXPECT_LE(mono_enc_len_bytes * 2, static_cast(data_length_ * 2)); - uint8_t* output_ptr = output; - for (int i = 0; i < mono_enc_len_bytes; ++i) { - *output_ptr = (temp_output[i] & 0xF0) + (temp_output[i] >> 4); - ++output_ptr; - *output_ptr = (temp_output[i] << 4) + (temp_output[i] & 0x0F); - ++output_ptr; - } - delete [] temp_output; - return mono_enc_len_bytes * 2; - } -}; - -#ifdef WEBRTC_CODEC_CELT -class AudioDecoderCeltTest : public AudioDecoderTest { - protected: - static const int kEncodingRateBitsPerSecond = 64000; - AudioDecoderCeltTest() : AudioDecoderTest(), encoder_(NULL) { - frame_size_ = 640; - data_length_ = 10 * frame_size_; - decoder_ = AudioDecoder::CreateAudioDecoder(kDecoderCELT_32); - assert(decoder_); - WebRtcCelt_CreateEnc(&encoder_, static_cast(channels_)); - } - - ~AudioDecoderCeltTest() { - WebRtcCelt_FreeEnc(encoder_); - } - - virtual void InitEncoder() { - assert(encoder_); - ASSERT_EQ(0, WebRtcCelt_EncoderInit( - encoder_, static_cast(channels_), kEncodingRateBitsPerSecond)); - } - - virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, - uint8_t* output) { - assert(encoder_); - return WebRtcCelt_Encode(encoder_, input, output); - } - - CELT_encinst_t* encoder_; -}; - -class AudioDecoderCeltStereoTest : public AudioDecoderTest { - protected: - static const int kEncodingRateBitsPerSecond = 64000; - AudioDecoderCeltStereoTest() : AudioDecoderTest(), encoder_(NULL) { - channels_ = 2; - frame_size_ = 640; - data_length_ = 10 * frame_size_; - decoder_ = AudioDecoder::CreateAudioDecoder(kDecoderCELT_32_2ch); - assert(decoder_); - stereo_input_ = new int16_t[frame_size_ * channels_]; - WebRtcCelt_CreateEnc(&encoder_, static_cast(channels_)); - } - - ~AudioDecoderCeltStereoTest() { - delete [] stereo_input_; - WebRtcCelt_FreeEnc(encoder_); - } - - virtual void InitEncoder() { - assert(encoder_); - ASSERT_EQ(0, WebRtcCelt_EncoderInit( - encoder_, static_cast(channels_), kEncodingRateBitsPerSecond)); - } - - virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, - uint8_t* output) { - assert(encoder_); - assert(stereo_input_); - for (size_t n = 0; n < frame_size_; ++n) { - stereo_input_[n * 2] = stereo_input_[n * 2 + 1] = input[n]; - } - return WebRtcCelt_Encode(encoder_, stereo_input_, output); - } - - int16_t* stereo_input_; - CELT_encinst_t* encoder_; -}; - -#endif - -class AudioDecoderOpusTest : public AudioDecoderTest { - protected: - AudioDecoderOpusTest() : AudioDecoderTest() { - frame_size_ = 320; - data_length_ = 10 * frame_size_; - decoder_ = new AudioDecoderOpus(kDecoderOpus); - assert(decoder_); - WebRtcOpus_EncoderCreate(&encoder_, 1); - } - - ~AudioDecoderOpusTest() { - WebRtcOpus_EncoderFree(encoder_); - } - - virtual void InitEncoder() {} - - virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, - uint8_t* output) { - // Upsample from 32 to 48 kHz. - Resampler rs; - rs.Reset(32000, 48000, kResamplerSynchronous); - const int max_resamp_len_samples = static_cast(input_len_samples) * - 3 / 2; - int16_t* resamp_input = new int16_t[max_resamp_len_samples]; - int resamp_len_samples; - EXPECT_EQ(0, rs.Push(input, static_cast(input_len_samples), - resamp_input, max_resamp_len_samples, - resamp_len_samples)); - EXPECT_EQ(max_resamp_len_samples, resamp_len_samples); - int enc_len_bytes = - WebRtcOpus_Encode(encoder_, resamp_input, resamp_len_samples, - static_cast(data_length_), output); - EXPECT_GT(enc_len_bytes, 0); - delete [] resamp_input; - return enc_len_bytes; - } - - OpusEncInst* encoder_; -}; - -class AudioDecoderOpusStereoTest : public AudioDecoderTest { - protected: - AudioDecoderOpusStereoTest() : AudioDecoderTest() { - channels_ = 2; - frame_size_ = 320; - data_length_ = 10 * frame_size_; - decoder_ = new AudioDecoderOpus(kDecoderOpus_2ch); - assert(decoder_); - WebRtcOpus_EncoderCreate(&encoder_, 2); - } - - ~AudioDecoderOpusStereoTest() { - WebRtcOpus_EncoderFree(encoder_); - } - - virtual void InitEncoder() {} - - virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, - uint8_t* output) { - // Create stereo by duplicating each sample in |input|. - const int input_stereo_samples = static_cast(input_len_samples) * 2; - int16_t* input_stereo = new int16_t[input_stereo_samples]; - for (size_t i = 0; i < input_len_samples; i++) - input_stereo[i * 2] = input_stereo[i * 2 + 1] = input[i]; - // Upsample from 32 to 48 kHz. - Resampler rs; - rs.Reset(32000, 48000, kResamplerSynchronousStereo); - const int max_resamp_len_samples = input_stereo_samples * 3 / 2; - int16_t* resamp_input = new int16_t[max_resamp_len_samples]; - int resamp_len_samples; - EXPECT_EQ(0, rs.Push(input_stereo, input_stereo_samples, resamp_input, - max_resamp_len_samples, resamp_len_samples)); - EXPECT_EQ(max_resamp_len_samples, resamp_len_samples); - int enc_len_bytes = - WebRtcOpus_Encode(encoder_, resamp_input, resamp_len_samples / 2, - static_cast(data_length_), output); - EXPECT_GT(enc_len_bytes, 0); - delete [] resamp_input; - delete [] input_stereo; - return enc_len_bytes; - } - - OpusEncInst* encoder_; -}; - -TEST_F(AudioDecoderPcmUTest, EncodeDecode) { - int tolerance = 251; - double mse = 1734.0; - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCMu)); - EncodeDecodeTest(data_length_, tolerance, mse); - ReInitTest(); - EXPECT_FALSE(decoder_->HasDecodePlc()); -} - -TEST_F(AudioDecoderPcmATest, EncodeDecode) { - int tolerance = 308; - double mse = 1931.0; - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCMa)); - EncodeDecodeTest(data_length_, tolerance, mse); - ReInitTest(); - EXPECT_FALSE(decoder_->HasDecodePlc()); -} - -TEST_F(AudioDecoderPcm16BTest, EncodeDecode) { - int tolerance = 0; - double mse = 0.0; - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16B)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bwb)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bswb32kHz)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bswb48kHz)); - EncodeDecodeTest(2 * data_length_, tolerance, mse); - ReInitTest(); - EXPECT_FALSE(decoder_->HasDecodePlc()); -} - -TEST_F(AudioDecoderIlbcTest, EncodeDecode) { - int tolerance = 6808; - double mse = 2.13e6; - int delay = 80; // Delay from input to output. - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderILBC)); - EncodeDecodeTest(500, tolerance, mse, delay); - ReInitTest(); - EXPECT_TRUE(decoder_->HasDecodePlc()); - DecodePlcTest(); -} - -TEST_F(AudioDecoderIsacFloatTest, EncodeDecode) { - int tolerance = 3399; - double mse = 434951.0; - int delay = 48; // Delay from input to output. - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISAC)); - EncodeDecodeTest(0, tolerance, mse, delay); - ReInitTest(); - EXPECT_TRUE(decoder_->HasDecodePlc()); - DecodePlcTest(); -} - -TEST_F(AudioDecoderIsacSwbTest, EncodeDecode) { - int tolerance = 19757; - double mse = 8.18e6; - int delay = 160; // Delay from input to output. - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISACswb)); - EncodeDecodeTest(0, tolerance, mse, delay); - ReInitTest(); - EXPECT_TRUE(decoder_->HasDecodePlc()); - DecodePlcTest(); -} - -TEST_F(AudioDecoderIsacFbTest, EncodeDecode) { - int tolerance = 19757; - double mse = 8.18e6; - int delay = 160; // Delay from input to output. - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISACswb)); - EncodeDecodeTest(0, tolerance, mse, delay); - ReInitTest(); - EXPECT_TRUE(decoder_->HasDecodePlc()); - DecodePlcTest(); -} - -TEST_F(AudioDecoderIsacFixTest, DISABLED_EncodeDecode) { - int tolerance = 11034; - double mse = 3.46e6; - int delay = 54; // Delay from input to output. - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISAC)); - EncodeDecodeTest(735, tolerance, mse, delay); - ReInitTest(); - EXPECT_FALSE(decoder_->HasDecodePlc()); -} - -TEST_F(AudioDecoderG722Test, EncodeDecode) { - int tolerance = 6176; - double mse = 238630.0; - int delay = 22; // Delay from input to output. - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderG722)); - EncodeDecodeTest(data_length_ / 2, tolerance, mse, delay); - ReInitTest(); - EXPECT_FALSE(decoder_->HasDecodePlc()); -} - -TEST_F(AudioDecoderG722StereoTest, CreateAndDestroy) { - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderG722_2ch)); -} - -TEST_F(AudioDecoderG722StereoTest, EncodeDecode) { - int tolerance = 6176; - int channel_diff_tolerance = 0; - double mse = 238630.0; - int delay = 22; // Delay from input to output. - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderG722_2ch)); - EncodeDecodeTest(data_length_, tolerance, mse, delay, channel_diff_tolerance); - ReInitTest(); - EXPECT_FALSE(decoder_->HasDecodePlc()); -} - -TEST_F(AudioDecoderOpusTest, EncodeDecode) { - int tolerance = 6176; - double mse = 238630.0; - int delay = 22; // Delay from input to output. - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderOpus)); - EncodeDecodeTest(0, tolerance, mse, delay); - ReInitTest(); - EXPECT_FALSE(decoder_->HasDecodePlc()); -} - -TEST_F(AudioDecoderOpusStereoTest, EncodeDecode) { - int tolerance = 6176; - int channel_diff_tolerance = 0; - double mse = 238630.0; - int delay = 22; // Delay from input to output. - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderOpus_2ch)); - EncodeDecodeTest(0, tolerance, mse, delay, channel_diff_tolerance); - ReInitTest(); - EXPECT_FALSE(decoder_->HasDecodePlc()); -} - -#ifdef WEBRTC_CODEC_CELT -// In the two following CELT tests, the low amplitude of the test signal allow -// us to have such low error thresholds, i.e. |tolerance|, |mse|. Furthermore, -// in general, stereo signals with identical channels do not result in identical -// encoded channels. -TEST_F(AudioDecoderCeltTest, EncodeDecode) { - int tolerance = 20; - double mse = 17.0; - int delay = 80; // Delay from input to output in samples. - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderCELT_32)); - EncodeDecodeTest(1600, tolerance, mse, delay); - ReInitTest(); - EXPECT_TRUE(decoder_->HasDecodePlc()); - DecodePlcTest(); -} - -TEST_F(AudioDecoderCeltStereoTest, EncodeDecode) { - int tolerance = 20; - // If both channels are identical, CELT not necessarily decodes identical - // channels. However, for this input this is the case. - int channel_diff_tolerance = 0; - double mse = 20.0; - // Delay from input to output in samples, accounting for stereo. - int delay = 160; - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderCELT_32_2ch)); - EncodeDecodeTest(1600, tolerance, mse, delay, channel_diff_tolerance); - ReInitTest(); - EXPECT_TRUE(decoder_->HasDecodePlc()); - DecodePlcTest(); -} -#endif - -TEST(AudioDecoder, CodecSampleRateHz) { - EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderPCMu)); - EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderPCMa)); - EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderPCMu_2ch)); - EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderPCMa_2ch)); - EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderILBC)); - EXPECT_EQ(16000, AudioDecoder::CodecSampleRateHz(kDecoderISAC)); - EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderISACswb)); - EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderISACfb)); - EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16B)); - EXPECT_EQ(16000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16Bwb)); - EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16Bswb32kHz)); - EXPECT_EQ(48000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16Bswb48kHz)); - EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16B_2ch)); - EXPECT_EQ(16000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16Bwb_2ch)); - EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16Bswb32kHz_2ch)); - EXPECT_EQ(48000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16Bswb48kHz_2ch)); - EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16B_5ch)); - EXPECT_EQ(16000, AudioDecoder::CodecSampleRateHz(kDecoderG722)); - EXPECT_EQ(16000, AudioDecoder::CodecSampleRateHz(kDecoderG722_2ch)); - EXPECT_EQ(-1, AudioDecoder::CodecSampleRateHz(kDecoderRED)); - EXPECT_EQ(-1, AudioDecoder::CodecSampleRateHz(kDecoderAVT)); - EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderCNGnb)); - EXPECT_EQ(16000, AudioDecoder::CodecSampleRateHz(kDecoderCNGwb)); - EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderCNGswb32kHz)); - // TODO(tlegrand): Change 32000 to 48000 below once ACM has 48 kHz support. - EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderCNGswb48kHz)); - EXPECT_EQ(-1, AudioDecoder::CodecSampleRateHz(kDecoderArbitrary)); - EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderOpus)); - EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderOpus_2ch)); -#ifdef WEBRTC_CODEC_CELT - EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderCELT_32)); - EXPECT_EQ(32000, AudioDecoder::CodecSampleRateHz(kDecoderCELT_32_2ch)); -#else - EXPECT_EQ(-1, AudioDecoder::CodecSampleRateHz(kDecoderCELT_32)); - EXPECT_EQ(-1, AudioDecoder::CodecSampleRateHz(kDecoderCELT_32_2ch)); -#endif -} - -TEST(AudioDecoder, CodecSupported) { - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCMu)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCMa)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCMu_2ch)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCMa_2ch)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderILBC)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISAC)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISACswb)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISACfb)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16B)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bwb)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bswb32kHz)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bswb48kHz)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16B_2ch)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bwb_2ch)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bswb32kHz_2ch)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bswb48kHz_2ch)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16B_5ch)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderG722)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderG722_2ch)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderRED)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderAVT)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderCNGnb)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderCNGwb)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderCNGswb32kHz)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderCNGswb48kHz)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderArbitrary)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderOpus)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderOpus_2ch)); -#ifdef WEBRTC_CODEC_CELT - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderCELT_32)); - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderCELT_32_2ch)); -#else - EXPECT_FALSE(AudioDecoder::CodecSupported(kDecoderCELT_32)); - EXPECT_FALSE(AudioDecoder::CodecSupported(kDecoderCELT_32_2ch)); -#endif -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder_unittests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder_unittests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder_unittests.isolate 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_decoder_unittests.isolate 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -# Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. -{ - 'conditions': [ - ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. - 'variables': { - 'isolate_dependency_untracked': [ - '../../../../../resources/', - '../../../../../data/', - ], - }, - }], - ['OS=="linux" or OS=="mac" or OS=="win"', { - 'variables': { - 'command': [ - '../../../../testing/test_env.py', - '<(PRODUCT_DIR)/audio_decoder_unittests<(EXECUTABLE_SUFFIX)', - ], - 'isolate_dependency_touched': [ - '../../../../DEPS', - ], - 'isolate_dependency_tracked': [ - '../../../../resources/audio_coding/testfile32kHz.pcm', - '../../../../testing/test_env.py', - '<(PRODUCT_DIR)/audio_decoder_unittests<(EXECUTABLE_SUFFIX)', - ], - 'isolate_dependency_untracked': [ - '../../../../tools/swarming_client/', - ], - }, - }], - ], -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_multi_vector.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_multi_vector.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_multi_vector.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_multi_vector.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,213 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" - -#include - -#include - -#include "webrtc/typedefs.h" - -namespace webrtc { - -AudioMultiVector::AudioMultiVector(size_t N) { - assert(N > 0); - if (N < 1) N = 1; - for (size_t n = 0; n < N; ++n) { - channels_.push_back(new AudioVector); - } - num_channels_ = N; -} - -AudioMultiVector::AudioMultiVector(size_t N, size_t initial_size) { - assert(N > 0); - if (N < 1) N = 1; - for (size_t n = 0; n < N; ++n) { - channels_.push_back(new AudioVector(initial_size)); - } - num_channels_ = N; -} - -AudioMultiVector::~AudioMultiVector() { - std::vector::iterator it = channels_.begin(); - while (it != channels_.end()) { - delete (*it); - ++it; - } -} - -void AudioMultiVector::Clear() { - for (size_t i = 0; i < num_channels_; ++i) { - channels_[i]->Clear(); - } -} - -void AudioMultiVector::Zeros(size_t length) { - for (size_t i = 0; i < num_channels_; ++i) { - channels_[i]->Clear(); - channels_[i]->Extend(length); - } -} - -void AudioMultiVector::CopyFrom(AudioMultiVector* copy_to) const { - if (copy_to) { - for (size_t i = 0; i < num_channels_; ++i) { - channels_[i]->CopyFrom(&(*copy_to)[i]); - } - } -} - -void AudioMultiVector::PushBackInterleaved(const int16_t* append_this, - size_t length) { - assert(length % num_channels_ == 0); - if (num_channels_ == 1) { - // Special case to avoid extra allocation and data shuffling. - channels_[0]->PushBack(append_this, length); - return; - } - size_t length_per_channel = length / num_channels_; - int16_t* temp_array = new int16_t[length_per_channel]; // Temporary storage. - for (size_t channel = 0; channel < num_channels_; ++channel) { - // Copy elements to |temp_array|. - // Set |source_ptr| to first element of this channel. - const int16_t* source_ptr = &append_this[channel]; - for (size_t i = 0; i < length_per_channel; ++i) { - temp_array[i] = *source_ptr; - source_ptr += num_channels_; // Jump to next element of this channel. - } - channels_[channel]->PushBack(temp_array, length_per_channel); - } - delete [] temp_array; -} - -void AudioMultiVector::PushBack(const AudioMultiVector& append_this) { - assert(num_channels_ == append_this.num_channels_); - if (num_channels_ == append_this.num_channels_) { - for (size_t i = 0; i < num_channels_; ++i) { - channels_[i]->PushBack(append_this[i]); - } - } -} - -void AudioMultiVector::PushBackFromIndex(const AudioMultiVector& append_this, - size_t index) { - assert(index < append_this.Size()); - index = std::min(index, append_this.Size() - 1); - size_t length = append_this.Size() - index; - assert(num_channels_ == append_this.num_channels_); - if (num_channels_ == append_this.num_channels_) { - for (size_t i = 0; i < num_channels_; ++i) { - channels_[i]->PushBack(&append_this[i][index], length); - } - } -} - -void AudioMultiVector::PopFront(size_t length) { - for (size_t i = 0; i < num_channels_; ++i) { - channels_[i]->PopFront(length); - } -} - -void AudioMultiVector::PopBack(size_t length) { - for (size_t i = 0; i < num_channels_; ++i) { - channels_[i]->PopBack(length); - } -} - -size_t AudioMultiVector::ReadInterleaved(size_t length, - int16_t* destination) const { - return ReadInterleavedFromIndex(0, length, destination); -} - -size_t AudioMultiVector::ReadInterleavedFromIndex(size_t start_index, - size_t length, - int16_t* destination) const { - if (!destination) { - return 0; - } - size_t index = 0; // Number of elements written to |destination| so far. - assert(start_index <= Size()); - start_index = std::min(start_index, Size()); - if (length + start_index > Size()) { - length = Size() - start_index; - } - if (num_channels_ == 1) { - // Special case to avoid the nested for loop below. - memcpy(destination, &(*this)[0][start_index], length * sizeof(int16_t)); - return length; - } - for (size_t i = 0; i < length; ++i) { - for (size_t channel = 0; channel < num_channels_; ++channel) { - destination[index] = (*this)[channel][i + start_index]; - ++index; - } - } - return index; -} - -size_t AudioMultiVector::ReadInterleavedFromEnd(size_t length, - int16_t* destination) const { - length = std::min(length, Size()); // Cannot read more than Size() elements. - return ReadInterleavedFromIndex(Size() - length, length, destination); -} - -void AudioMultiVector::OverwriteAt(const AudioMultiVector& insert_this, - size_t length, - size_t position) { - assert(num_channels_ == insert_this.num_channels_); - // Cap |length| at the length of |insert_this|. - assert(length <= insert_this.Size()); - length = std::min(length, insert_this.Size()); - if (num_channels_ == insert_this.num_channels_) { - for (size_t i = 0; i < num_channels_; ++i) { - channels_[i]->OverwriteAt(&insert_this[i][0], length, position); - } - } -} - -void AudioMultiVector::CrossFade(const AudioMultiVector& append_this, - size_t fade_length) { - assert(num_channels_ == append_this.num_channels_); - if (num_channels_ == append_this.num_channels_) { - for (size_t i = 0; i < num_channels_; ++i) { - channels_[i]->CrossFade(append_this[i], fade_length); - } - } -} - -size_t AudioMultiVector::Size() const { - assert(channels_[0]); - return channels_[0]->Size(); -} - -void AudioMultiVector::AssertSize(size_t required_size) { - if (Size() < required_size) { - size_t extend_length = required_size - Size(); - for (size_t channel = 0; channel < num_channels_; ++channel) { - channels_[channel]->Extend(extend_length); - } - } -} - -bool AudioMultiVector::Empty() const { - assert(channels_[0]); - return channels_[0]->Empty(); -} - -const AudioVector& AudioMultiVector::operator[](size_t index) const { - return *(channels_[index]); -} - -AudioVector& AudioMultiVector::operator[](size_t index) { - return *(channels_[index]); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_multi_vector.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_multi_vector.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_multi_vector.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_multi_vector.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_AUDIO_MULTI_VECTOR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_AUDIO_MULTI_VECTOR_H_ - -#include // Access to size_t. - -#include - -#include "webrtc/modules/audio_coding/neteq4/audio_vector.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -class AudioMultiVector { - public: - // Creates an empty AudioMultiVector with |N| audio channels. |N| must be - // larger than 0. - explicit AudioMultiVector(size_t N); - - // Creates an AudioMultiVector with |N| audio channels, each channel having - // an initial size. |N| must be larger than 0. - AudioMultiVector(size_t N, size_t initial_size); - - virtual ~AudioMultiVector(); - - // Deletes all values and make the vector empty. - virtual void Clear(); - - // Clears the vector and inserts |length| zeros into each channel. - virtual void Zeros(size_t length); - - // Copies all values from this vector to |copy_to|. Any contents in |copy_to| - // are deleted. After the operation is done, |copy_to| will be an exact - // replica of this object. The source and the destination must have the same - // number of channels. - virtual void CopyFrom(AudioMultiVector* copy_to) const; - - // Appends the contents of array |append_this| to the end of this - // object. The array is assumed to be channel-interleaved. |length| must be - // an even multiple of this object's number of channels. - // The length of this object is increased with the |length| divided by the - // number of channels. - virtual void PushBackInterleaved(const int16_t* append_this, size_t length); - - // Appends the contents of AudioMultiVector |append_this| to this object. The - // length of this object is increased with the length of |append_this|. - virtual void PushBack(const AudioMultiVector& append_this); - - // Appends the contents of AudioMultiVector |append_this| to this object, - // taken from |index| up until the end of |append_this|. The length of this - // object is increased. - virtual void PushBackFromIndex(const AudioMultiVector& append_this, - size_t index); - - // Removes |length| elements from the beginning of this object, from each - // channel. - virtual void PopFront(size_t length); - - // Removes |length| elements from the end of this object, from each - // channel. - virtual void PopBack(size_t length); - - // Reads |length| samples from each channel and writes them interleaved to - // |destination|. The total number of elements written to |destination| is - // returned, i.e., |length| * number of channels. If the AudioMultiVector - // contains less than |length| samples per channel, this is reflected in the - // return value. - virtual size_t ReadInterleaved(size_t length, int16_t* destination) const; - - // Like ReadInterleaved() above, but reads from |start_index| instead of from - // the beginning. - virtual size_t ReadInterleavedFromIndex(size_t start_index, - size_t length, - int16_t* destination) const; - - // Like ReadInterleaved() above, but reads from the end instead of from - // the beginning. - virtual size_t ReadInterleavedFromEnd(size_t length, - int16_t* destination) const; - - // Overwrites each channel in this AudioMultiVector with values taken from - // |insert_this|. The values are taken from the beginning of |insert_this| and - // are inserted starting at |position|. |length| values are written into each - // channel. If |length| and |position| are selected such that the new data - // extends beyond the end of the current AudioVector, the vector is extended - // to accommodate the new data. |length| is limited to the length of - // |insert_this|. - virtual void OverwriteAt(const AudioMultiVector& insert_this, - size_t length, - size_t position); - - // Appends |append_this| to the end of the current vector. Lets the two - // vectors overlap by |fade_length| samples (per channel), and cross-fade - // linearly in this region. - virtual void CrossFade(const AudioMultiVector& append_this, - size_t fade_length); - - // Returns the number of channels. - virtual size_t Channels() const { return num_channels_; } - - // Returns the number of elements per channel in this AudioMultiVector. - virtual size_t Size() const; - - // Verify that each channel can hold at least |required_size| elements. If - // not, extend accordingly. - virtual void AssertSize(size_t required_size); - - virtual bool Empty() const; - - // Accesses and modifies a channel (i.e., an AudioVector object) of this - // AudioMultiVector. - const AudioVector& operator[](size_t index) const; - AudioVector& operator[](size_t index); - - protected: - std::vector channels_; - size_t num_channels_; - - private: - DISALLOW_COPY_AND_ASSIGN(AudioMultiVector); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_AUDIO_MULTI_VECTOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_multi_vector_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_multi_vector_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_multi_vector_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_multi_vector_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,308 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" - -#include -#include - -#include - -#include "gtest/gtest.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// This is a value-parameterized test. The test cases are instantiated with -// different values for the test parameter, which is used to determine the -// number of channels in the AudioMultiBuffer. Note that it is not possible -// to combine typed testing with value-parameterized testing, and since the -// tests for AudioVector already covers a number of different type parameters, -// this test focuses on testing different number of channels, and keeping the -// value type constant. - -class AudioMultiVectorTest : public ::testing::TestWithParam { - protected: - AudioMultiVectorTest() - : num_channels_(GetParam()), // Get the test parameter. - interleaved_length_(num_channels_ * array_length()) { - array_interleaved_ = new int16_t[num_channels_ * array_length()]; - } - - ~AudioMultiVectorTest() { - delete [] array_interleaved_; - } - - virtual void SetUp() { - // Populate test arrays. - for (size_t i = 0; i < array_length(); ++i) { - array_[i] = static_cast(i); - } - int16_t* ptr = array_interleaved_; - // Write 100, 101, 102, ... for first channel. - // Write 200, 201, 202, ... for second channel. - // And so on. - for (size_t i = 0; i < array_length(); ++i) { - for (size_t j = 1; j <= num_channels_; ++j) { - *ptr = j * 100 + i; - ++ptr; - } - } - } - - size_t array_length() const { - return sizeof(array_) / sizeof(array_[0]); - } - - const size_t num_channels_; - size_t interleaved_length_; - int16_t array_[10]; - int16_t* array_interleaved_; -}; - -// Create and destroy AudioMultiVector objects, both empty and with a predefined -// length. -TEST_P(AudioMultiVectorTest, CreateAndDestroy) { - AudioMultiVector vec1(num_channels_); - EXPECT_TRUE(vec1.Empty()); - EXPECT_EQ(num_channels_, vec1.Channels()); - EXPECT_EQ(0u, vec1.Size()); - - size_t initial_size = 17; - AudioMultiVector vec2(num_channels_, initial_size); - EXPECT_FALSE(vec2.Empty()); - EXPECT_EQ(num_channels_, vec2.Channels()); - EXPECT_EQ(initial_size, vec2.Size()); -} - -// Test the subscript operator [] for getting and setting. -TEST_P(AudioMultiVectorTest, SubscriptOperator) { - AudioMultiVector vec(num_channels_, array_length()); - for (size_t channel = 0; channel < num_channels_; ++channel) { - for (size_t i = 0; i < array_length(); ++i) { - vec[channel][i] = static_cast(i); - // Make sure to use the const version. - const AudioVector& audio_vec = vec[channel]; - EXPECT_EQ(static_cast(i), audio_vec[i]); - } - } -} - -// Test the PushBackInterleaved method and the CopyFrom method. The Clear -// method is also invoked. -TEST_P(AudioMultiVectorTest, PushBackInterleavedAndCopy) { - AudioMultiVector vec(num_channels_); - vec.PushBackInterleaved(array_interleaved_, interleaved_length_); - AudioMultiVector vec_copy(num_channels_); - vec.CopyFrom(&vec_copy); // Copy from |vec| to |vec_copy|. - ASSERT_EQ(num_channels_, vec.Channels()); - ASSERT_EQ(array_length(), vec.Size()); - ASSERT_EQ(num_channels_, vec_copy.Channels()); - ASSERT_EQ(array_length(), vec_copy.Size()); - for (size_t channel = 0; channel < vec.Channels(); ++channel) { - for (size_t i = 0; i < array_length(); ++i) { - EXPECT_EQ(static_cast((channel + 1) * 100 + i), vec[channel][i]); - EXPECT_EQ(vec[channel][i], vec_copy[channel][i]); - } - } - - // Clear |vec| and verify that it is empty. - vec.Clear(); - EXPECT_TRUE(vec.Empty()); - - // Now copy the empty vector and verify that the copy becomes empty too. - vec.CopyFrom(&vec_copy); - EXPECT_TRUE(vec_copy.Empty()); -} - -// Try to copy to a NULL pointer. Nothing should happen. -TEST_P(AudioMultiVectorTest, CopyToNull) { - AudioMultiVector vec(num_channels_); - AudioMultiVector* vec_copy = NULL; - vec.PushBackInterleaved(array_interleaved_, interleaved_length_); - vec.CopyFrom(vec_copy); -} - -// Test the PushBack method with another AudioMultiVector as input argument. -TEST_P(AudioMultiVectorTest, PushBackVector) { - AudioMultiVector vec1(num_channels_, array_length()); - AudioMultiVector vec2(num_channels_, array_length()); - // Set the first vector to [0, 1, ..., array_length() - 1] + - // 100 * channel_number. - // Set the second vector to [array_length(), array_length() + 1, ..., - // 2 * array_length() - 1] + 100 * channel_number. - for (size_t channel = 0; channel < num_channels_; ++channel) { - for (size_t i = 0; i < array_length(); ++i) { - vec1[channel][i] = static_cast(i + 100 * channel); - vec2[channel][i] = - static_cast(i + 100 * channel + array_length()); - } - } - // Append vec2 to the back of vec1. - vec1.PushBack(vec2); - ASSERT_EQ(2u * array_length(), vec1.Size()); - for (size_t channel = 0; channel < num_channels_; ++channel) { - for (size_t i = 0; i < 2 * array_length(); ++i) { - EXPECT_EQ(static_cast(i + 100 * channel), vec1[channel][i]); - } - } -} - -// Test the PushBackFromIndex method. -TEST_P(AudioMultiVectorTest, PushBackFromIndex) { - AudioMultiVector vec1(num_channels_); - vec1.PushBackInterleaved(array_interleaved_, interleaved_length_); - AudioMultiVector vec2(num_channels_); - - // Append vec1 to the back of vec2 (which is empty). Read vec1 from the second - // last element. - vec2.PushBackFromIndex(vec1, array_length() - 2); - ASSERT_EQ(2u, vec2.Size()); - for (size_t channel = 0; channel < num_channels_; ++channel) { - for (size_t i = 0; i < 2; ++i) { - EXPECT_EQ(array_interleaved_[channel + num_channels_ * - (array_length() - 2 + i)], vec2[channel][i]); - } - } -} - -// Starts with pushing some values to the vector, then test the Zeros method. -TEST_P(AudioMultiVectorTest, Zeros) { - AudioMultiVector vec(num_channels_); - vec.PushBackInterleaved(array_interleaved_, interleaved_length_); - vec.Zeros(2 * array_length()); - ASSERT_EQ(num_channels_, vec.Channels()); - ASSERT_EQ(2u * array_length(), vec.Size()); - for (size_t channel = 0; channel < num_channels_; ++channel) { - for (size_t i = 0; i < 2 * array_length(); ++i) { - EXPECT_EQ(0, vec[channel][i]); - } - } -} - -// Test the ReadInterleaved method -TEST_P(AudioMultiVectorTest, ReadInterleaved) { - AudioMultiVector vec(num_channels_); - vec.PushBackInterleaved(array_interleaved_, interleaved_length_); - int16_t* output = new int16_t[interleaved_length_]; - // Read 5 samples. - size_t read_samples = 5; - EXPECT_EQ(num_channels_ * read_samples, - vec.ReadInterleaved(read_samples, output)); - EXPECT_EQ(0, - memcmp(array_interleaved_, output, read_samples * sizeof(int16_t))); - - // Read too many samples. Expect to get all samples from the vector. - EXPECT_EQ(interleaved_length_, - vec.ReadInterleaved(array_length() + 1, output)); - EXPECT_EQ(0, - memcmp(array_interleaved_, output, read_samples * sizeof(int16_t))); - - delete [] output; -} - -// Try to read to a NULL pointer. Expected to return 0. -TEST_P(AudioMultiVectorTest, ReadInterleavedToNull) { - AudioMultiVector vec(num_channels_); - vec.PushBackInterleaved(array_interleaved_, interleaved_length_); - int16_t* output = NULL; - // Read 5 samples. - size_t read_samples = 5; - EXPECT_EQ(0u, vec.ReadInterleaved(read_samples, output)); -} - -// Test the PopFront method. -TEST_P(AudioMultiVectorTest, PopFront) { - AudioMultiVector vec(num_channels_); - vec.PushBackInterleaved(array_interleaved_, interleaved_length_); - vec.PopFront(1); // Remove one element from each channel. - ASSERT_EQ(array_length() - 1u, vec.Size()); - // Let |ptr| point to the second element of the first channel in the - // interleaved array. - int16_t* ptr = &array_interleaved_[num_channels_]; - for (size_t i = 0; i < array_length() - 1; ++i) { - for (size_t channel = 0; channel < num_channels_; ++channel) { - EXPECT_EQ(*ptr, vec[channel][i]); - ++ptr; - } - } - vec.PopFront(array_length()); // Remove more elements than vector size. - EXPECT_EQ(0u, vec.Size()); -} - -// Test the PopBack method. -TEST_P(AudioMultiVectorTest, PopBack) { - AudioMultiVector vec(num_channels_); - vec.PushBackInterleaved(array_interleaved_, interleaved_length_); - vec.PopBack(1); // Remove one element from each channel. - ASSERT_EQ(array_length() - 1u, vec.Size()); - // Let |ptr| point to the first element of the first channel in the - // interleaved array. - int16_t* ptr = array_interleaved_; - for (size_t i = 0; i < array_length() - 1; ++i) { - for (size_t channel = 0; channel < num_channels_; ++channel) { - EXPECT_EQ(*ptr, vec[channel][i]); - ++ptr; - } - } - vec.PopBack(array_length()); // Remove more elements than vector size. - EXPECT_EQ(0u, vec.Size()); -} - -// Test the AssertSize method. -TEST_P(AudioMultiVectorTest, AssertSize) { - AudioMultiVector vec(num_channels_, array_length()); - EXPECT_EQ(array_length(), vec.Size()); - // Start with asserting with smaller sizes than already allocated. - vec.AssertSize(0); - vec.AssertSize(array_length() - 1); - // Nothing should have changed. - EXPECT_EQ(array_length(), vec.Size()); - // Assert with one element longer than already allocated. - vec.AssertSize(array_length() + 1); - // Expect vector to have grown. - EXPECT_EQ(array_length() + 1, vec.Size()); - // Also check the individual AudioVectors. - for (size_t channel = 0; channel < vec.Channels(); ++channel) { - EXPECT_EQ(array_length() + 1u, vec[channel].Size()); - } -} - -// Test the PushBack method with another AudioMultiVector as input argument. -TEST_P(AudioMultiVectorTest, OverwriteAt) { - AudioMultiVector vec1(num_channels_); - vec1.PushBackInterleaved(array_interleaved_, interleaved_length_); - AudioMultiVector vec2(num_channels_); - vec2.Zeros(3); // 3 zeros in each channel. - // Overwrite vec2 at position 5. - vec1.OverwriteAt(vec2, 3, 5); - // Verify result. - // Length remains the same. - ASSERT_EQ(array_length(), vec1.Size()); - int16_t* ptr = array_interleaved_; - for (size_t i = 0; i < array_length() - 1; ++i) { - for (size_t channel = 0; channel < num_channels_; ++channel) { - if (i >= 5 && i <= 7) { - // Elements 5, 6, 7 should have been replaced with zeros. - EXPECT_EQ(0, vec1[channel][i]); - } else { - EXPECT_EQ(*ptr, vec1[channel][i]); - } - ++ptr; - } - } -} - -INSTANTIATE_TEST_CASE_P(TestNumChannels, - AudioMultiVectorTest, - ::testing::Values(static_cast(1), - static_cast(2), - static_cast(5))); -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_vector.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_vector.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_vector.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_vector.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/audio_vector.h" - -#include - -#include - -#include "webrtc/typedefs.h" - -namespace webrtc { - -void AudioVector::Clear() { - first_free_ix_ = 0; -} - -void AudioVector::CopyFrom(AudioVector* copy_to) const { - if (copy_to) { - copy_to->Reserve(Size()); - assert(copy_to->capacity_ >= Size()); - memcpy(copy_to->array_.get(), array_.get(), Size() * sizeof(int16_t)); - copy_to->first_free_ix_ = first_free_ix_; - } -} - -void AudioVector::PushFront(const AudioVector& prepend_this) { - size_t insert_length = prepend_this.Size(); - Reserve(Size() + insert_length); - memmove(&array_[insert_length], &array_[0], Size() * sizeof(int16_t)); - memcpy(&array_[0], &prepend_this.array_[0], insert_length * sizeof(int16_t)); - first_free_ix_ += insert_length; -} - -void AudioVector::PushFront(const int16_t* prepend_this, size_t length) { - // Same operation as InsertAt beginning. - InsertAt(prepend_this, length, 0); -} - -void AudioVector::PushBack(const AudioVector& append_this) { - PushBack(append_this.array_.get(), append_this.Size()); -} - -void AudioVector::PushBack(const int16_t* append_this, size_t length) { - Reserve(Size() + length); - memcpy(&array_[first_free_ix_], append_this, length * sizeof(int16_t)); - first_free_ix_ += length; -} - -void AudioVector::PopFront(size_t length) { - if (length >= Size()) { - // Remove all elements. - Clear(); - } else { - size_t remaining_samples = Size() - length; - memmove(&array_[0], &array_[length], remaining_samples * sizeof(int16_t)); - first_free_ix_ -= length; - } -} - -void AudioVector::PopBack(size_t length) { - // Never remove more than what is in the array. - length = std::min(length, Size()); - first_free_ix_ -= length; -} - -void AudioVector::Extend(size_t extra_length) { - Reserve(Size() + extra_length); - memset(&array_[first_free_ix_], 0, extra_length * sizeof(int16_t)); - first_free_ix_ += extra_length; -} - -void AudioVector::InsertAt(const int16_t* insert_this, - size_t length, - size_t position) { - Reserve(Size() + length); - // Cap the position at the current vector length, to be sure the iterator - // does not extend beyond the end of the vector. - position = std::min(Size(), position); - int16_t* insert_position_ptr = &array_[position]; - size_t samples_to_move = Size() - position; - memmove(insert_position_ptr + length, insert_position_ptr, - samples_to_move * sizeof(int16_t)); - memcpy(insert_position_ptr, insert_this, length * sizeof(int16_t)); - first_free_ix_ += length; -} - -void AudioVector::InsertZerosAt(size_t length, - size_t position) { - Reserve(Size() + length); - // Cap the position at the current vector length, to be sure the iterator - // does not extend beyond the end of the vector. - position = std::min(capacity_, position); - int16_t* insert_position_ptr = &array_[position]; - size_t samples_to_move = Size() - position; - memmove(insert_position_ptr + length, insert_position_ptr, - samples_to_move * sizeof(int16_t)); - memset(insert_position_ptr, 0, length * sizeof(int16_t)); - first_free_ix_ += length; -} - -void AudioVector::OverwriteAt(const int16_t* insert_this, - size_t length, - size_t position) { - // Cap the insert position at the current array length. - position = std::min(Size(), position); - Reserve(position + length); - memcpy(&array_[position], insert_this, length * sizeof(int16_t)); - if (position + length > Size()) { - // Array was expanded. - first_free_ix_ += position + length - Size(); - } -} - -void AudioVector::CrossFade(const AudioVector& append_this, - size_t fade_length) { - // Fade length cannot be longer than the current vector or |append_this|. - assert(fade_length <= Size()); - assert(fade_length <= append_this.Size()); - fade_length = std::min(fade_length, Size()); - fade_length = std::min(fade_length, append_this.Size()); - size_t position = Size() - fade_length; - // Cross fade the overlapping regions. - // |alpha| is the mixing factor in Q14. - // TODO(hlundin): Consider skipping +1 in the denominator to produce a - // smoother cross-fade, in particular at the end of the fade. - int alpha_step = 16384 / (static_cast(fade_length) + 1); - int alpha = 16384; - for (size_t i = 0; i < fade_length; ++i) { - alpha -= alpha_step; - array_[position + i] = (alpha * array_[position + i] + - (16384 - alpha) * append_this[i] + 8192) >> 14; - } - assert(alpha >= 0); // Verify that the slope was correct. - // Append what is left of |append_this|. - size_t samples_to_push_back = append_this.Size() - fade_length; - if (samples_to_push_back > 0) - PushBack(&append_this[fade_length], samples_to_push_back); -} - -const int16_t& AudioVector::operator[](size_t index) const { - return array_[index]; -} - -int16_t& AudioVector::operator[](size_t index) { - return array_[index]; -} - -void AudioVector::Reserve(size_t n) { - if (capacity_ < n) { - scoped_ptr temp_array(new int16_t[n]); - memcpy(temp_array.get(), array_.get(), Size() * sizeof(int16_t)); - array_.swap(temp_array); - capacity_ = n; - } -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_vector.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_vector.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_vector.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_vector.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_AUDIO_VECTOR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_AUDIO_VECTOR_H_ - -#include // Access to size_t. - -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -class AudioVector { - public: - // Creates an empty AudioVector. - AudioVector() - : array_(new int16_t[kDefaultInitialSize]), - first_free_ix_(0), - capacity_(kDefaultInitialSize) {} - - // Creates an AudioVector with an initial size. - explicit AudioVector(size_t initial_size) - : array_(new int16_t[initial_size]), - first_free_ix_(initial_size), - capacity_(initial_size) { - memset(array_.get(), 0, initial_size * sizeof(int16_t)); - } - - virtual ~AudioVector() {} - - // Deletes all values and make the vector empty. - virtual void Clear(); - - // Copies all values from this vector to |copy_to|. Any contents in |copy_to| - // are deleted before the copy operation. After the operation is done, - // |copy_to| will be an exact replica of this object. - virtual void CopyFrom(AudioVector* copy_to) const; - - // Prepends the contents of AudioVector |prepend_this| to this object. The - // length of this object is increased with the length of |prepend_this|. - virtual void PushFront(const AudioVector& prepend_this); - - // Same as above, but with an array |prepend_this| with |length| elements as - // source. - virtual void PushFront(const int16_t* prepend_this, size_t length); - - // Same as PushFront but will append to the end of this object. - virtual void PushBack(const AudioVector& append_this); - - // Same as PushFront but will append to the end of this object. - virtual void PushBack(const int16_t* append_this, size_t length); - - // Removes |length| elements from the beginning of this object. - virtual void PopFront(size_t length); - - // Removes |length| elements from the end of this object. - virtual void PopBack(size_t length); - - // Extends this object with |extra_length| elements at the end. The new - // elements are initialized to zero. - virtual void Extend(size_t extra_length); - - // Inserts |length| elements taken from the array |insert_this| and insert - // them at |position|. The length of the AudioVector is increased by |length|. - // |position| = 0 means that the new values are prepended to the vector. - // |position| = Size() means that the new values are appended to the vector. - virtual void InsertAt(const int16_t* insert_this, size_t length, - size_t position); - - // Like InsertAt, but inserts |length| zero elements at |position|. - virtual void InsertZerosAt(size_t length, size_t position); - - // Overwrites |length| elements of this AudioVector with values taken from the - // array |insert_this|, starting at |position|. The definition of |position| - // is the same as for InsertAt(). If |length| and |position| are selected - // such that the new data extends beyond the end of the current AudioVector, - // the vector is extended to accommodate the new data. - virtual void OverwriteAt(const int16_t* insert_this, - size_t length, - size_t position); - - // Appends |append_this| to the end of the current vector. Lets the two - // vectors overlap by |fade_length| samples, and cross-fade linearly in this - // region. - virtual void CrossFade(const AudioVector& append_this, size_t fade_length); - - // Returns the number of elements in this AudioVector. - virtual size_t Size() const { return first_free_ix_; } - - // Returns true if this AudioVector is empty. - virtual bool Empty() const { return (first_free_ix_ == 0); } - - // Accesses and modifies an element of AudioVector. - const int16_t& operator[](size_t index) const; - int16_t& operator[](size_t index); - - private: - static const size_t kDefaultInitialSize = 10; - - void Reserve(size_t n); - - scoped_ptr array_; - size_t first_free_ix_; // The first index after the last sample in array_. - // Note that this index may point outside of array_. - size_t capacity_; // Allocated number of samples in the array. - - DISALLOW_COPY_AND_ASSIGN(AudioVector); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_AUDIO_VECTOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_vector_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_vector_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_vector_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/audio_vector_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,394 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/audio_vector.h" - -#include -#include - -#include - -#include "gtest/gtest.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -class AudioVectorTest : public ::testing::Test { - protected: - virtual void SetUp() { - // Populate test array. - for (size_t i = 0; i < array_length(); ++i) { - array_[i] = i; - } - } - - size_t array_length() const { - return sizeof(array_) / sizeof(array_[0]); - } - - int16_t array_[10]; -}; - -// Create and destroy AudioVector objects, both empty and with a predefined -// length. -TEST_F(AudioVectorTest, CreateAndDestroy) { - AudioVector vec1; - EXPECT_TRUE(vec1.Empty()); - EXPECT_EQ(0u, vec1.Size()); - - size_t initial_size = 17; - AudioVector vec2(initial_size); - EXPECT_FALSE(vec2.Empty()); - EXPECT_EQ(initial_size, vec2.Size()); -} - -// Test the subscript operator [] for getting and setting. -TEST_F(AudioVectorTest, SubscriptOperator) { - AudioVector vec(array_length()); - for (size_t i = 0; i < array_length(); ++i) { - vec[i] = static_cast(i); - const int16_t& value = vec[i]; // Make sure to use the const version. - EXPECT_EQ(static_cast(i), value); - } -} - -// Test the PushBack method and the CopyFrom method. The Clear method is also -// invoked. -TEST_F(AudioVectorTest, PushBackAndCopy) { - AudioVector vec; - AudioVector vec_copy; - vec.PushBack(array_, array_length()); - vec.CopyFrom(&vec_copy); // Copy from |vec| to |vec_copy|. - ASSERT_EQ(array_length(), vec.Size()); - ASSERT_EQ(array_length(), vec_copy.Size()); - for (size_t i = 0; i < array_length(); ++i) { - EXPECT_EQ(array_[i], vec[i]); - EXPECT_EQ(array_[i], vec_copy[i]); - } - - // Clear |vec| and verify that it is empty. - vec.Clear(); - EXPECT_TRUE(vec.Empty()); - - // Now copy the empty vector and verify that the copy becomes empty too. - vec.CopyFrom(&vec_copy); - EXPECT_TRUE(vec_copy.Empty()); -} - -// Try to copy to a NULL pointer. Nothing should happen. -TEST_F(AudioVectorTest, CopyToNull) { - AudioVector vec; - AudioVector* vec_copy = NULL; - vec.PushBack(array_, array_length()); - vec.CopyFrom(vec_copy); -} - -// Test the PushBack method with another AudioVector as input argument. -TEST_F(AudioVectorTest, PushBackVector) { - static const size_t kLength = 10; - AudioVector vec1(kLength); - AudioVector vec2(kLength); - // Set the first vector to [0, 1, ..., kLength - 1]. - // Set the second vector to [kLength, kLength + 1, ..., 2 * kLength - 1]. - for (size_t i = 0; i < kLength; ++i) { - vec1[i] = static_cast(i); - vec2[i] = static_cast(i + kLength); - } - // Append vec2 to the back of vec1. - vec1.PushBack(vec2); - ASSERT_EQ(2 * kLength, vec1.Size()); - for (size_t i = 0; i < 2 * kLength; ++i) { - EXPECT_EQ(static_cast(i), vec1[i]); - } -} - -// Test the PushFront method. -TEST_F(AudioVectorTest, PushFront) { - AudioVector vec; - vec.PushFront(array_, array_length()); - ASSERT_EQ(array_length(), vec.Size()); - for (size_t i = 0; i < array_length(); ++i) { - EXPECT_EQ(array_[i], vec[i]); - } -} - -// Test the PushFront method with another AudioVector as input argument. -TEST_F(AudioVectorTest, PushFrontVector) { - static const size_t kLength = 10; - AudioVector vec1(kLength); - AudioVector vec2(kLength); - // Set the first vector to [0, 1, ..., kLength - 1]. - // Set the second vector to [kLength, kLength + 1, ..., 2 * kLength - 1]. - for (size_t i = 0; i < kLength; ++i) { - vec1[i] = static_cast(i); - vec2[i] = static_cast(i + kLength); - } - // Prepend vec1 to the front of vec2. - vec2.PushFront(vec1); - ASSERT_EQ(2 * kLength, vec2.Size()); - for (size_t i = 0; i < 2 * kLength; ++i) { - EXPECT_EQ(static_cast(i), vec2[i]); - } -} - -// Test the PopFront method. -TEST_F(AudioVectorTest, PopFront) { - AudioVector vec; - vec.PushBack(array_, array_length()); - vec.PopFront(1); // Remove one element. - EXPECT_EQ(array_length() - 1u, vec.Size()); - for (size_t i = 0; i < array_length() - 1; ++i) { - EXPECT_EQ(static_cast(i + 1), vec[i]); - } - vec.PopFront(array_length()); // Remove more elements than vector size. - EXPECT_EQ(0u, vec.Size()); -} - -// Test the PopBack method. -TEST_F(AudioVectorTest, PopBack) { - AudioVector vec; - vec.PushBack(array_, array_length()); - vec.PopBack(1); // Remove one element. - EXPECT_EQ(array_length() - 1u, vec.Size()); - for (size_t i = 0; i < array_length() - 1; ++i) { - EXPECT_EQ(static_cast(i), vec[i]); - } - vec.PopBack(array_length()); // Remove more elements than vector size. - EXPECT_EQ(0u, vec.Size()); -} - -// Test the Extend method. -TEST_F(AudioVectorTest, Extend) { - AudioVector vec; - vec.PushBack(array_, array_length()); - vec.Extend(5); // Extend with 5 elements, which should all be zeros. - ASSERT_EQ(array_length() + 5u, vec.Size()); - // Verify that all are zero. - for (size_t i = array_length(); i < array_length() + 5; ++i) { - EXPECT_EQ(0, vec[i]); - } -} - -// Test the InsertAt method with an insert position in the middle of the vector. -TEST_F(AudioVectorTest, InsertAt) { - AudioVector vec; - vec.PushBack(array_, array_length()); - static const int kNewLength = 5; - int16_t new_array[kNewLength]; - // Set array elements to {100, 101, 102, ... }. - for (int i = 0; i < kNewLength; ++i) { - new_array[i] = 100 + i; - } - int insert_position = 5; - vec.InsertAt(new_array, kNewLength, insert_position); - // Verify that the vector looks as follows: - // {0, 1, ..., |insert_position| - 1, 100, 101, ..., 100 + kNewLength - 1, - // |insert_position|, |insert_position| + 1, ..., kLength - 1}. - size_t pos = 0; - for (int i = 0; i < insert_position; ++i) { - EXPECT_EQ(array_[i], vec[pos]); - ++pos; - } - for (int i = 0; i < kNewLength; ++i) { - EXPECT_EQ(new_array[i], vec[pos]); - ++pos; - } - for (size_t i = insert_position; i < array_length(); ++i) { - EXPECT_EQ(array_[i], vec[pos]); - ++pos; - } -} - -// Test the InsertZerosAt method with an insert position in the middle of the -// vector. Use the InsertAt method as reference. -TEST_F(AudioVectorTest, InsertZerosAt) { - AudioVector vec; - AudioVector vec_ref; - vec.PushBack(array_, array_length()); - vec_ref.PushBack(array_, array_length()); - static const int kNewLength = 5; - int insert_position = 5; - vec.InsertZerosAt(kNewLength, insert_position); - int16_t new_array[kNewLength] = {0}; // All zero elements. - vec_ref.InsertAt(new_array, kNewLength, insert_position); - // Verify that the vectors are identical. - ASSERT_EQ(vec_ref.Size(), vec.Size()); - for (size_t i = 0; i < vec.Size(); ++i) { - EXPECT_EQ(vec_ref[i], vec[i]); - } -} - -// Test the InsertAt method with an insert position at the start of the vector. -TEST_F(AudioVectorTest, InsertAtBeginning) { - AudioVector vec; - vec.PushBack(array_, array_length()); - static const int kNewLength = 5; - int16_t new_array[kNewLength]; - // Set array elements to {100, 101, 102, ... }. - for (int i = 0; i < kNewLength; ++i) { - new_array[i] = 100 + i; - } - int insert_position = 0; - vec.InsertAt(new_array, kNewLength, insert_position); - // Verify that the vector looks as follows: - // {100, 101, ..., 100 + kNewLength - 1, - // 0, 1, ..., kLength - 1}. - size_t pos = 0; - for (int i = 0; i < kNewLength; ++i) { - EXPECT_EQ(new_array[i], vec[pos]); - ++pos; - } - for (size_t i = insert_position; i < array_length(); ++i) { - EXPECT_EQ(array_[i], vec[pos]); - ++pos; - } -} - -// Test the InsertAt method with an insert position at the end of the vector. -TEST_F(AudioVectorTest, InsertAtEnd) { - AudioVector vec; - vec.PushBack(array_, array_length()); - static const int kNewLength = 5; - int16_t new_array[kNewLength]; - // Set array elements to {100, 101, 102, ... }. - for (int i = 0; i < kNewLength; ++i) { - new_array[i] = 100 + i; - } - int insert_position = array_length(); - vec.InsertAt(new_array, kNewLength, insert_position); - // Verify that the vector looks as follows: - // {0, 1, ..., kLength - 1, 100, 101, ..., 100 + kNewLength - 1 }. - size_t pos = 0; - for (size_t i = 0; i < array_length(); ++i) { - EXPECT_EQ(array_[i], vec[pos]); - ++pos; - } - for (int i = 0; i < kNewLength; ++i) { - EXPECT_EQ(new_array[i], vec[pos]); - ++pos; - } -} - -// Test the InsertAt method with an insert position beyond the end of the -// vector. Verify that a position beyond the end of the vector does not lead to -// an error. The expected outcome is the same as if the vector end was used as -// input position. That is, the input position should be capped at the maximum -// allowed value. -TEST_F(AudioVectorTest, InsertBeyondEnd) { - AudioVector vec; - vec.PushBack(array_, array_length()); - static const int kNewLength = 5; - int16_t new_array[kNewLength]; - // Set array elements to {100, 101, 102, ... }. - for (int i = 0; i < kNewLength; ++i) { - new_array[i] = 100 + i; - } - int insert_position = array_length() + 10; // Too large. - vec.InsertAt(new_array, kNewLength, insert_position); - // Verify that the vector looks as follows: - // {0, 1, ..., kLength - 1, 100, 101, ..., 100 + kNewLength - 1 }. - size_t pos = 0; - for (size_t i = 0; i < array_length(); ++i) { - EXPECT_EQ(array_[i], vec[pos]); - ++pos; - } - for (int i = 0; i < kNewLength; ++i) { - EXPECT_EQ(new_array[i], vec[pos]); - ++pos; - } -} - -// Test the OverwriteAt method with a position such that all of the new values -// fit within the old vector. -TEST_F(AudioVectorTest, OverwriteAt) { - AudioVector vec; - vec.PushBack(array_, array_length()); - static const int kNewLength = 5; - int16_t new_array[kNewLength]; - // Set array elements to {100, 101, 102, ... }. - for (int i = 0; i < kNewLength; ++i) { - new_array[i] = 100 + i; - } - size_t insert_position = 2; - vec.OverwriteAt(new_array, kNewLength, insert_position); - // Verify that the vector looks as follows: - // {0, ..., |insert_position| - 1, 100, 101, ..., 100 + kNewLength - 1, - // |insert_position|, |insert_position| + 1, ..., kLength - 1}. - size_t pos = 0; - for (pos = 0; pos < insert_position; ++pos) { - EXPECT_EQ(array_[pos], vec[pos]); - } - for (int i = 0; i < kNewLength; ++i) { - EXPECT_EQ(new_array[i], vec[pos]); - ++pos; - } - for (; pos < array_length(); ++pos) { - EXPECT_EQ(array_[pos], vec[pos]); - } -} - -// Test the OverwriteAt method with a position such that some of the new values -// extend beyond the end of the current vector. This is valid, and the vector is -// expected to expand to accommodate the new values. -TEST_F(AudioVectorTest, OverwriteBeyondEnd) { - AudioVector vec; - vec.PushBack(array_, array_length()); - static const int kNewLength = 5; - int16_t new_array[kNewLength]; - // Set array elements to {100, 101, 102, ... }. - for (int i = 0; i < kNewLength; ++i) { - new_array[i] = 100 + i; - } - int insert_position = array_length() - 2; - vec.OverwriteAt(new_array, kNewLength, insert_position); - ASSERT_EQ(array_length() - 2u + kNewLength, vec.Size()); - // Verify that the vector looks as follows: - // {0, ..., |insert_position| - 1, 100, 101, ..., 100 + kNewLength - 1, - // |insert_position|, |insert_position| + 1, ..., kLength - 1}. - int pos = 0; - for (pos = 0; pos < insert_position; ++pos) { - EXPECT_EQ(array_[pos], vec[pos]); - } - for (int i = 0; i < kNewLength; ++i) { - EXPECT_EQ(new_array[i], vec[pos]); - ++pos; - } - // Verify that we checked to the end of |vec|. - EXPECT_EQ(vec.Size(), static_cast(pos)); -} - -TEST_F(AudioVectorTest, CrossFade) { - static const size_t kLength = 100; - static const size_t kFadeLength = 10; - AudioVector vec1(kLength); - AudioVector vec2(kLength); - // Set all vector elements to 0 in |vec1| and 100 in |vec2|. - for (size_t i = 0; i < kLength; ++i) { - vec1[i] = 0; - vec2[i] = 100; - } - vec1.CrossFade(vec2, kFadeLength); - ASSERT_EQ(2 * kLength - kFadeLength, vec1.Size()); - // First part untouched. - for (size_t i = 0; i < kLength - kFadeLength; ++i) { - EXPECT_EQ(0, vec1[i]); - } - // Check mixing zone. - for (size_t i = 0 ; i < kFadeLength; ++i) { - EXPECT_NEAR((i + 1) * 100 / (kFadeLength + 1), - vec1[kLength - kFadeLength + i], 1); - } - // Second part untouched. - for (size_t i = kLength; i < vec1.Size(); ++i) { - EXPECT_EQ(100, vec1[i]); - } -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/background_noise.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/background_noise.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/background_noise.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/background_noise.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,260 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/background_noise.h" - -#include -#include // memcpy - -#include // min, max - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" -#include "webrtc/modules/audio_coding/neteq4/post_decode_vad.h" - -namespace webrtc { - -BackgroundNoise::BackgroundNoise(size_t num_channels) - : num_channels_(num_channels), - channel_parameters_(new ChannelParameters[num_channels_]), - mode_(kBgnOn) { - Reset(); -} - -BackgroundNoise::~BackgroundNoise() {} - -void BackgroundNoise::Reset() { - initialized_ = false; - for (size_t channel = 0; channel < num_channels_; ++channel) { - channel_parameters_[channel].Reset(); - } - // Keep _bgnMode as it is. -} - -void BackgroundNoise::Update(const AudioMultiVector& input, - const PostDecodeVad& vad) { - if (vad.running() && vad.active_speech()) { - // Do not update the background noise parameters if we know that the signal - // is active speech. - return; - } - - int32_t auto_correlation[kMaxLpcOrder + 1]; - int16_t fiter_output[kMaxLpcOrder + kResidualLength]; - int16_t reflection_coefficients[kMaxLpcOrder]; - int16_t lpc_coefficients[kMaxLpcOrder + 1]; - - for (size_t channel_ix = 0; channel_ix < num_channels_; ++channel_ix) { - ChannelParameters& parameters = channel_parameters_[channel_ix]; - int16_t temp_signal_array[kVecLen + kMaxLpcOrder] = {0}; - int16_t* temp_signal = &temp_signal_array[kMaxLpcOrder]; - memcpy(temp_signal, - &input[channel_ix][input.Size() - kVecLen], - sizeof(int16_t) * kVecLen); - - int32_t sample_energy = CalculateAutoCorrelation(temp_signal, kVecLen, - auto_correlation); - - if ((!vad.running() && - sample_energy < parameters.energy_update_threshold) || - (vad.running() && !vad.active_speech())) { - // Generate LPC coefficients. - if (auto_correlation[0] > 0) { - // Regardless of whether the filter is actually updated or not, - // update energy threshold levels, since we have in fact observed - // a low energy signal. - if (sample_energy < parameters.energy_update_threshold) { - // Never go under 1.0 in average sample energy. - parameters.energy_update_threshold = std::max(sample_energy, 1); - parameters.low_energy_update_threshold = 0; - } - - // Only update BGN if filter is stable, i.e., if return value from - // Levinson-Durbin function is 1. - if (WebRtcSpl_LevinsonDurbin(auto_correlation, lpc_coefficients, - reflection_coefficients, - kMaxLpcOrder) != 1) { - return; - } - } else { - // Center value in auto-correlation is not positive. Do not update. - return; - } - - // Generate the CNG gain factor by looking at the energy of the residual. - WebRtcSpl_FilterMAFastQ12(temp_signal + kVecLen - kResidualLength, - fiter_output, lpc_coefficients, - kMaxLpcOrder + 1, kResidualLength); - int32_t residual_energy = WebRtcSpl_DotProductWithScale(fiter_output, - fiter_output, - kResidualLength, - 0); - - // Check spectral flatness. - // Comparing the residual variance with the input signal variance tells - // if the spectrum is flat or not. - // If 20 * residual_energy >= sample_energy << 6, the spectrum is flat - // enough. Also ensure that the energy is non-zero. - if ((residual_energy * 20 >= (sample_energy << 6)) && - (sample_energy > 0)) { - // Spectrum is flat enough; save filter parameters. - // |temp_signal| + |kVecLen| - |kMaxLpcOrder| points at the first of the - // |kMaxLpcOrder| samples in the residual signal, which will form the - // filter state for the next noise generation. - SaveParameters(channel_ix, lpc_coefficients, - temp_signal + kVecLen - kMaxLpcOrder, sample_energy, - residual_energy); - } - } else { - // Will only happen if post-decode VAD is disabled and |sample_energy| is - // not low enough. Increase the threshold for update so that it increases - // by a factor 4 in 4 seconds. - IncrementEnergyThreshold(channel_ix, sample_energy); - } - } - return; -} - -int32_t BackgroundNoise::Energy(size_t channel) const { - assert(channel < num_channels_); - return channel_parameters_[channel].energy; -} - -void BackgroundNoise::SetMuteFactor(size_t channel, int16_t value) { - assert(channel < num_channels_); - channel_parameters_[channel].mute_factor = value; -} - -int16_t BackgroundNoise::MuteFactor(size_t channel) const { - assert(channel < num_channels_); - return channel_parameters_[channel].mute_factor; -} - -const int16_t* BackgroundNoise::Filter(size_t channel) const { - assert(channel < num_channels_); - return channel_parameters_[channel].filter; -} - -const int16_t* BackgroundNoise::FilterState(size_t channel) const { - assert(channel < num_channels_); - return channel_parameters_[channel].filter_state; -} - -void BackgroundNoise::SetFilterState(size_t channel, const int16_t* input, - size_t length) { - assert(channel < num_channels_); - length = std::min(length, static_cast(kMaxLpcOrder)); - memcpy(channel_parameters_[channel].filter_state, input, - length * sizeof(int16_t)); -} - -int16_t BackgroundNoise::Scale(size_t channel) const { - assert(channel < num_channels_); - return channel_parameters_[channel].scale; -} -int16_t BackgroundNoise::ScaleShift(size_t channel) const { - assert(channel < num_channels_); - return channel_parameters_[channel].scale_shift; -} - -int32_t BackgroundNoise::CalculateAutoCorrelation( - const int16_t* signal, int length, int32_t* auto_correlation) const { - int16_t signal_max = WebRtcSpl_MaxAbsValueW16(signal, length); - int correlation_scale = kLogVecLen - - WebRtcSpl_NormW32(signal_max * signal_max); - correlation_scale = std::max(0, correlation_scale); - - static const int kCorrelationStep = -1; - WebRtcSpl_CrossCorrelation(auto_correlation, signal, signal, length, - kMaxLpcOrder + 1, correlation_scale, - kCorrelationStep); - - // Number of shifts to normalize energy to energy/sample. - int energy_sample_shift = kLogVecLen - correlation_scale; - return auto_correlation[0] >> energy_sample_shift; -} - -void BackgroundNoise::IncrementEnergyThreshold(size_t channel, - int32_t sample_energy) { - // TODO(hlundin): Simplify the below threshold update. What this code - // does is simply "threshold += (increment * threshold) >> 16", but due - // to the limited-width operations, it is not exactly the same. The - // difference should be inaudible, but bit-exactness would not be - // maintained. - assert(channel < num_channels_); - ChannelParameters& parameters = channel_parameters_[channel]; - int32_t temp_energy = - WEBRTC_SPL_MUL_16_16_RSFT(kThresholdIncrement, - parameters.low_energy_update_threshold, 16); - temp_energy += kThresholdIncrement * - (parameters.energy_update_threshold & 0xFF); - temp_energy += (kThresholdIncrement * - ((parameters.energy_update_threshold>>8) & 0xFF)) << 8; - parameters.low_energy_update_threshold += temp_energy; - - parameters.energy_update_threshold += kThresholdIncrement * - (parameters.energy_update_threshold>>16); - parameters.energy_update_threshold += - parameters.low_energy_update_threshold >> 16; - parameters.low_energy_update_threshold = - parameters.low_energy_update_threshold & 0x0FFFF; - - // Update maximum energy. - // Decrease by a factor 1/1024 each time. - parameters.max_energy = parameters.max_energy - - (parameters.max_energy >> 10); - if (sample_energy > parameters.max_energy) { - parameters.max_energy = sample_energy; - } - - // Set |energy_update_threshold| to no less than 60 dB lower than - // |max_energy_|. Adding 524288 assures proper rounding. - int32_t energy_update_threshold = (parameters.max_energy + 524288) >> 20; - if (energy_update_threshold > parameters.energy_update_threshold) { - parameters.energy_update_threshold = energy_update_threshold; - } -} - -void BackgroundNoise::SaveParameters(size_t channel, - const int16_t* lpc_coefficients, - const int16_t* filter_state, - int32_t sample_energy, - int32_t residual_energy) { - assert(channel < num_channels_); - ChannelParameters& parameters = channel_parameters_[channel]; - memcpy(parameters.filter, lpc_coefficients, - (kMaxLpcOrder+1) * sizeof(int16_t)); - memcpy(parameters.filter_state, filter_state, - kMaxLpcOrder * sizeof(int16_t)); - // Save energy level and update energy threshold levels. - // Never get under 1.0 in average sample energy. - parameters.energy = std::max(sample_energy, 1); - parameters.energy_update_threshold = parameters.energy; - parameters.low_energy_update_threshold = 0; - - // Normalize residual_energy to 29 or 30 bits before sqrt. - int norm_shift = WebRtcSpl_NormW32(residual_energy) - 1; - if (norm_shift & 0x1) { - norm_shift -= 1; // Even number of shifts required. - } - assert(norm_shift >= 0); // Should always be positive. - residual_energy = residual_energy << norm_shift; - - // Calculate scale and shift factor. - parameters.scale = WebRtcSpl_SqrtFloor(residual_energy); - // Add 13 to the |scale_shift_|, since the random numbers table is in - // Q13. - // TODO(hlundin): Move the "13" to where the |scale_shift_| is used? - parameters.scale_shift = 13 + ((kLogResidualLength + norm_shift) / 2); - - initialized_ = true; -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/background_noise.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/background_noise.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/background_noise.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/background_noise.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_BACKGROUND_NOISE_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_BACKGROUND_NOISE_H_ - -#include // size_t - -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" -#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Forward declarations. -class PostDecodeVad; - -// This class handles estimation of background noise parameters. -class BackgroundNoise { - public: - // TODO(hlundin): For 48 kHz support, increase kMaxLpcOrder to 10. - // Will work anyway, but probably sound a little worse. - static const int kMaxLpcOrder = 8; // 32000 / 8000 + 4. - - explicit BackgroundNoise(size_t num_channels); - virtual ~BackgroundNoise(); - - void Reset(); - - // Updates the parameter estimates based on the signal currently in the - // |sync_buffer|, and on the latest decision in |vad| if it is running. - void Update(const AudioMultiVector& sync_buffer, - const PostDecodeVad& vad); - - // Returns |energy_| for |channel|. - int32_t Energy(size_t channel) const; - - // Sets the value of |mute_factor_| for |channel| to |value|. - void SetMuteFactor(size_t channel, int16_t value); - - // Returns |mute_factor_| for |channel|. - int16_t MuteFactor(size_t channel) const; - - // Returns a pointer to |filter_| for |channel|. - const int16_t* Filter(size_t channel) const; - - // Returns a pointer to |filter_state_| for |channel|. - const int16_t* FilterState(size_t channel) const; - - // Copies |length| elements from |input| to the filter state. Will not copy - // more than |kMaxLpcOrder| elements. - void SetFilterState(size_t channel, const int16_t* input, size_t length); - - // Returns |scale_| for |channel|. - int16_t Scale(size_t channel) const; - - // Returns |scale_shift_| for |channel|. - int16_t ScaleShift(size_t channel) const; - - // Accessors. - bool initialized() const { return initialized_; } - NetEqBackgroundNoiseMode mode() const { return mode_; } - - // Sets the mode of the background noise playout for cases when there is long - // duration of packet loss. - void set_mode(NetEqBackgroundNoiseMode mode) { mode_ = mode; } - - private: - static const int kThresholdIncrement = 229; // 0.0035 in Q16. - static const int kVecLen = 256; - static const int kLogVecLen = 8; // log2(kVecLen). - static const int kResidualLength = 64; - static const int kLogResidualLength = 6; // log2(kResidualLength) - - struct ChannelParameters { - // Constructor. - ChannelParameters() { - Reset(); - } - - void Reset() { - energy = 2500; - max_energy = 0; - energy_update_threshold = 500000; - low_energy_update_threshold = 0; - memset(filter_state, 0, sizeof(filter_state)); - memset(filter, 0, sizeof(filter)); - filter[0] = 4096; - mute_factor = 0, - scale = 20000; - scale_shift = 24; - } - - int32_t energy; - int32_t max_energy; - int32_t energy_update_threshold; - int32_t low_energy_update_threshold; - int16_t filter_state[kMaxLpcOrder]; - int16_t filter[kMaxLpcOrder + 1]; - int16_t mute_factor; - int16_t scale; - int16_t scale_shift; - }; - - int32_t CalculateAutoCorrelation(const int16_t* signal, - int length, - int32_t* auto_correlation) const; - - // Increments the energy threshold by a factor 1 + |kThresholdIncrement|. - void IncrementEnergyThreshold(size_t channel, int32_t sample_energy); - - // Updates the filter parameters. - void SaveParameters(size_t channel, - const int16_t* lpc_coefficients, - const int16_t* filter_state, - int32_t sample_energy, - int32_t residual_energy); - - size_t num_channels_; - scoped_array channel_parameters_; - bool initialized_; - NetEqBackgroundNoiseMode mode_; - - DISALLOW_COPY_AND_ASSIGN(BackgroundNoise); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_BACKGROUND_NOISE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/background_noise_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/background_noise_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/background_noise_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/background_noise_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Unit tests for BackgroundNoise class. - -#include "webrtc/modules/audio_coding/neteq4/background_noise.h" - -#include "gtest/gtest.h" - -namespace webrtc { - -TEST(BackgroundNoise, CreateAndDestroy) { - size_t channels = 1; - BackgroundNoise bgn(channels); -} - -// TODO(hlundin): Write more tests. - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/buffer_level_filter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/buffer_level_filter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/buffer_level_filter.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/buffer_level_filter.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/buffer_level_filter.h" - -#include // Provide access to std::max. - -namespace webrtc { - -BufferLevelFilter::BufferLevelFilter() { - Reset(); -} - -void BufferLevelFilter::Reset() { - filtered_current_level_ = 0; - level_factor_ = 253; -} - -void BufferLevelFilter::Update(int buffer_size_packets, - int time_stretched_samples, - int packet_len_samples) { - // Filter: - // |filtered_current_level_| = |level_factor_| * |filtered_current_level_| + - // (1 - |level_factor_|) * |buffer_size_packets| - // |level_factor_| and |filtered_current_level_| are in Q8. - // |buffer_size_packets| is in Q0. - filtered_current_level_ = ((level_factor_ * filtered_current_level_) >> 8) + - ((256 - level_factor_) * buffer_size_packets); - - // Account for time-scale operations (accelerate and pre-emptive expand). - if (time_stretched_samples && packet_len_samples > 0) { - // Time-scaling has been performed since last filter update. Subtract the - // value of |time_stretched_samples| from |filtered_current_level_| after - // converting |time_stretched_samples| from samples to packets in Q8. - // Make sure that the filtered value remains non-negative. - filtered_current_level_ = std::max(0, - filtered_current_level_ - - (time_stretched_samples << 8) / packet_len_samples); - } -} - -void BufferLevelFilter::SetTargetBufferLevel(int target_buffer_level) { - if (target_buffer_level <= 1) { - level_factor_ = 251; - } else if (target_buffer_level <= 3) { - level_factor_ = 252; - } else if (target_buffer_level <= 7) { - level_factor_ = 253; - } else { - level_factor_ = 254; - } -} -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/buffer_level_filter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/buffer_level_filter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/buffer_level_filter.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/buffer_level_filter.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_BUFFER_LEVEL_FILTER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_BUFFER_LEVEL_FILTER_H_ - -#include "webrtc/system_wrappers/interface/constructor_magic.h" - -namespace webrtc { - -class BufferLevelFilter { - public: - BufferLevelFilter(); - virtual ~BufferLevelFilter() {} - virtual void Reset(); - - // Updates the filter. Current buffer size is |buffer_size_packets| (Q0). - // If |time_stretched_samples| is non-zero, the value is converted to the - // corresponding number of packets, and is subtracted from the filtered - // value (thus bypassing the filter operation). |packet_len_samples| is the - // number of audio samples carried in each incoming packet. - virtual void Update(int buffer_size_packets, int time_stretched_samples, - int packet_len_samples); - - // Set the current target buffer level (obtained from - // DelayManager::base_target_level()). Used to select the appropriate - // filter coefficient. - virtual void SetTargetBufferLevel(int target_buffer_level); - - virtual int filtered_current_level() const { return filtered_current_level_; } - - private: - int level_factor_; // Filter factor for the buffer level filter in Q8. - int filtered_current_level_; // Filtered current buffer level in Q8. - - DISALLOW_COPY_AND_ASSIGN(BufferLevelFilter); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_BUFFER_LEVEL_FILTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/buffer_level_filter_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/buffer_level_filter_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/buffer_level_filter_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/buffer_level_filter_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,162 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Unit tests for BufferLevelFilter class. - -#include "webrtc/modules/audio_coding/neteq4/buffer_level_filter.h" - -#include // Access to pow function. - -#include "gtest/gtest.h" - -namespace webrtc { - -TEST(BufferLevelFilter, CreateAndDestroy) { - BufferLevelFilter* filter = new BufferLevelFilter(); - EXPECT_EQ(0, filter->filtered_current_level()); - delete filter; -} - -TEST(BufferLevelFilter, ConvergenceTest) { - BufferLevelFilter filter; - for (int times = 10; times <= 50; times += 10) { - for (int value = 100; value <= 200; value += 10) { - filter.Reset(); - filter.SetTargetBufferLevel(1); // Makes filter coefficient 251/256. - std::ostringstream ss; - ss << "times = " << times << ", value = " << value; - SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. - for (int i = 0; i < times; ++i) { - filter.Update(value, 0 /* time_stretched_samples */, - 160 /* packet_len_samples */); - } - // Expect the filtered value to be (theoretically) - // (1 - (251/256) ^ |times|) * |value|. - double expected_value_double = - (1 - pow(251.0 / 256.0, times)) * value; - int expected_value = static_cast(expected_value_double); - // filtered_current_level() returns the value in Q8. - // The actual value may differ slightly from the expected value due to - // intermediate-stage rounding errors in the filter implementation. - // This is why we have to use EXPECT_NEAR with a tolerance of +/-1. - EXPECT_NEAR(expected_value, filter.filtered_current_level() >> 8, 1); - } - } -} - -// Verify that target buffer level impacts on the filter convergence. -TEST(BufferLevelFilter, FilterFactor) { - BufferLevelFilter filter; - // Update 10 times with value 100. - const int kTimes = 10; - const int kValue = 100; - - filter.SetTargetBufferLevel(3); // Makes filter coefficient 252/256. - for (int i = 0; i < kTimes; ++i) { - filter.Update(kValue, 0 /* time_stretched_samples */, - 160 /* packet_len_samples */); - } - // Expect the filtered value to be - // (1 - (252/256) ^ |kTimes|) * |kValue|. - int expected_value = 14; - // filtered_current_level() returns the value in Q8. - EXPECT_EQ(expected_value, filter.filtered_current_level() >> 8); - - filter.Reset(); - filter.SetTargetBufferLevel(7); // Makes filter coefficient 253/256. - for (int i = 0; i < kTimes; ++i) { - filter.Update(kValue, 0 /* time_stretched_samples */, - 160 /* packet_len_samples */); - } - // Expect the filtered value to be - // (1 - (253/256) ^ |kTimes|) * |kValue|. - expected_value = 11; - // filtered_current_level() returns the value in Q8. - EXPECT_EQ(expected_value, filter.filtered_current_level() >> 8); - - filter.Reset(); - filter.SetTargetBufferLevel(8); // Makes filter coefficient 254/256. - for (int i = 0; i < kTimes; ++i) { - filter.Update(kValue, 0 /* time_stretched_samples */, - 160 /* packet_len_samples */); - } - // Expect the filtered value to be - // (1 - (254/256) ^ |kTimes|) * |kValue|. - expected_value = 7; - // filtered_current_level() returns the value in Q8. - EXPECT_EQ(expected_value, filter.filtered_current_level() >> 8); -} - - -TEST(BufferLevelFilter, TimeStretchedSamples) { - BufferLevelFilter filter; - filter.SetTargetBufferLevel(1); // Makes filter coefficient 251/256. - // Update 10 times with value 100. - const int kTimes = 10; - const int kValue = 100; - const int kPacketSizeSamples = 160; - const int kNumPacketsStretched = 2; - const int kTimeStretchedSamples = kNumPacketsStretched * kPacketSizeSamples; - for (int i = 0; i < kTimes; ++i) { - // Packet size set to 0. Do not expect the parameter - // |kTimeStretchedSamples| to have any effect. - filter.Update(kValue, kTimeStretchedSamples, 0 /* packet_len_samples */); - } - // Expect the filtered value to be - // (1 - (251/256) ^ |kTimes|) * |kValue|. - const int kExpectedValue = 17; - // filtered_current_level() returns the value in Q8. - EXPECT_EQ(kExpectedValue, filter.filtered_current_level() >> 8); - - // Update filter again, now with non-zero value for packet length. - // Set the current filtered value to be the input, in order to isolate the - // impact of |kTimeStretchedSamples|. - filter.Update(filter.filtered_current_level() >> 8, kTimeStretchedSamples, - kPacketSizeSamples); - EXPECT_EQ(kExpectedValue - kNumPacketsStretched, - filter.filtered_current_level() >> 8); - // Try negative value and verify that we come back to the previous result. - filter.Update(filter.filtered_current_level() >> 8, -kTimeStretchedSamples, - kPacketSizeSamples); - EXPECT_EQ(kExpectedValue, filter.filtered_current_level() >> 8); -} - -TEST(BufferLevelFilter, TimeStretchedSamplesNegativeUnevenFrames) { - BufferLevelFilter filter; - filter.SetTargetBufferLevel(1); // Makes filter coefficient 251/256. - // Update 10 times with value 100. - const int kTimes = 10; - const int kValue = 100; - const int kPacketSizeSamples = 160; - const int kTimeStretchedSamples = -3.1415 * kPacketSizeSamples; - for (int i = 0; i < kTimes; ++i) { - // Packet size set to 0. Do not expect the parameter - // |kTimeStretchedSamples| to have any effect. - filter.Update(kValue, kTimeStretchedSamples, 0 /* packet_len_samples */); - } - // Expect the filtered value to be - // (1 - (251/256) ^ |kTimes|) * |kValue|. - const int kExpectedValue = 17; - // filtered_current_level() returns the value in Q8. - EXPECT_EQ(kExpectedValue, filter.filtered_current_level() >> 8); - - // Update filter again, now with non-zero value for packet length. - // Set the current filtered value to be the input, in order to isolate the - // impact of |kTimeStretchedSamples|. - filter.Update(filter.filtered_current_level() >> 8, kTimeStretchedSamples, - kPacketSizeSamples); - EXPECT_EQ(21, filter.filtered_current_level() >> 8); - // Try negative value and verify that we come back to the previous result. - filter.Update(filter.filtered_current_level() >> 8, -kTimeStretchedSamples, - kPacketSizeSamples); - EXPECT_EQ(kExpectedValue, filter.filtered_current_level() >> 8); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/comfort_noise.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/comfort_noise.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/comfort_noise.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/comfort_noise.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/comfort_noise.h" - -#include - -#include "webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h" -#include "webrtc/modules/audio_coding/neteq4/decoder_database.h" -#include "webrtc/modules/audio_coding/neteq4/dsp_helper.h" -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" -#include "webrtc/modules/audio_coding/neteq4/sync_buffer.h" - -namespace webrtc { - -void ComfortNoise::Reset() { - first_call_ = true; - internal_error_code_ = 0; -} - -int ComfortNoise::UpdateParameters(Packet* packet) { - assert(packet); // Existence is verified by caller. - // Get comfort noise decoder. - AudioDecoder* cng_decoder = decoder_database_->GetDecoder( - packet->header.payloadType); - if (!cng_decoder) { - delete [] packet->payload; - delete packet; - return kUnknownPayloadType; - } - decoder_database_->SetActiveCngDecoder(packet->header.payloadType); - CNG_dec_inst* cng_inst = static_cast(cng_decoder->state()); - int16_t ret = WebRtcCng_UpdateSid(cng_inst, - packet->payload, - packet->payload_length); - delete [] packet->payload; - delete packet; - if (ret < 0) { - internal_error_code_ = WebRtcCng_GetErrorCodeDec(cng_inst); - return kInternalError; - } - return kOK; -} - -int ComfortNoise::Generate(size_t requested_length, - AudioMultiVector* output) { - // TODO(hlundin): Change to an enumerator and skip assert. - assert(fs_hz_ == 8000 || fs_hz_ == 16000 || fs_hz_ == 32000 || - fs_hz_ == 48000); - // Not adapted for multi-channel yet. - if (output->Channels() != 1) { - return kMultiChannelNotSupported; - } - - size_t number_of_samples = requested_length; - int16_t new_period = 0; - if (first_call_) { - // Generate noise and overlap slightly with old data. - number_of_samples = requested_length + overlap_length_; - new_period = 1; - } - output->AssertSize(number_of_samples); - // Get the decoder from the database. - AudioDecoder* cng_decoder = decoder_database_->GetActiveCngDecoder(); - if (!cng_decoder) { - return kUnknownPayloadType; - } - CNG_dec_inst* cng_inst = static_cast(cng_decoder->state()); - // The expression &(*output)[0][0] is a pointer to the first element in - // the first channel. - if (WebRtcCng_Generate(cng_inst, &(*output)[0][0], - static_cast(number_of_samples), - new_period) < 0) { - // Error returned. - output->Zeros(requested_length); - internal_error_code_ = WebRtcCng_GetErrorCodeDec(cng_inst); - return kInternalError; - } - - if (first_call_) { - // Set tapering window parameters. Values are in Q15. - int16_t muting_window; // Mixing factor for overlap data. - int16_t muting_window_increment; // Mixing factor increment (negative). - int16_t unmuting_window; // Mixing factor for comfort noise. - int16_t unmuting_window_increment; // Mixing factor increment. - if (fs_hz_ == 8000) { - muting_window = DspHelper::kMuteFactorStart8kHz; - muting_window_increment = DspHelper::kMuteFactorIncrement8kHz; - unmuting_window = DspHelper::kUnmuteFactorStart8kHz; - unmuting_window_increment = DspHelper::kUnmuteFactorIncrement8kHz; - } else if (fs_hz_ == 16000) { - muting_window = DspHelper::kMuteFactorStart16kHz; - muting_window_increment = DspHelper::kMuteFactorIncrement16kHz; - unmuting_window = DspHelper::kUnmuteFactorStart16kHz; - unmuting_window_increment = DspHelper::kUnmuteFactorIncrement16kHz; - } else if (fs_hz_ == 32000) { - muting_window = DspHelper::kMuteFactorStart32kHz; - muting_window_increment = DspHelper::kMuteFactorIncrement32kHz; - unmuting_window = DspHelper::kUnmuteFactorStart32kHz; - unmuting_window_increment = DspHelper::kUnmuteFactorIncrement32kHz; - } else { // fs_hz_ == 48000 - muting_window = DspHelper::kMuteFactorStart48kHz; - muting_window_increment = DspHelper::kMuteFactorIncrement48kHz; - unmuting_window = DspHelper::kUnmuteFactorStart48kHz; - unmuting_window_increment = DspHelper::kUnmuteFactorIncrement48kHz; - } - - // Do overlap-add between new vector and overlap. - size_t start_ix = sync_buffer_->Size() - overlap_length_; - for (size_t i = 0; i < overlap_length_; i++) { - /* overlapVec[i] = WinMute * overlapVec[i] + WinUnMute * outData[i] */ - // The expression (*output)[0][i] is the i-th element in the first - // channel. - (*sync_buffer_)[0][start_ix + i] = - (((*sync_buffer_)[0][start_ix + i] * muting_window) + - ((*output)[0][i] * unmuting_window) + 16384) >> 15; - muting_window += muting_window_increment; - unmuting_window += unmuting_window_increment; - } - // Remove |overlap_length_| samples from the front of |output| since they - // were mixed into |sync_buffer_| above. - output->PopFront(overlap_length_); - } - first_call_ = false; - return kOK; -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/comfort_noise.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/comfort_noise.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/comfort_noise.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/comfort_noise.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_COMFORT_NOISE_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_COMFORT_NOISE_H_ - -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Forward declarations. -class DecoderDatabase; -class SyncBuffer; -struct Packet; - -// This class acts as an interface to the CNG generator. -class ComfortNoise { - public: - enum ReturnCodes { - kOK = 0, - kUnknownPayloadType, - kInternalError, - kMultiChannelNotSupported - }; - - ComfortNoise(int fs_hz, DecoderDatabase* decoder_database, - SyncBuffer* sync_buffer) - : fs_hz_(fs_hz), - first_call_(true), - overlap_length_(5 * fs_hz_ / 8000), - decoder_database_(decoder_database), - sync_buffer_(sync_buffer), - internal_error_code_(0) { - } - - // Resets the state. Should be called before each new comfort noise period. - void Reset(); - - // Update the comfort noise generator with the parameters in |packet|. - // Will delete the packet. - int UpdateParameters(Packet* packet); - - // Generates |requested_length| samples of comfort noise and writes to - // |output|. If this is the first in call after Reset (or first after creating - // the object), it will also mix in comfort noise at the end of the - // SyncBuffer object provided in the constructor. - int Generate(size_t requested_length, AudioMultiVector* output); - - // Returns the last error code that was produced by the comfort noise - // decoder. Returns 0 if no error has been encountered since the last reset. - int internal_error_code() { return internal_error_code_; } - - private: - int fs_hz_; - bool first_call_; - size_t overlap_length_; - DecoderDatabase* decoder_database_; - SyncBuffer* sync_buffer_; - int internal_error_code_; - DISALLOW_COPY_AND_ASSIGN(ComfortNoise); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_COMFORT_NOISE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/comfort_noise_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/comfort_noise_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/comfort_noise_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/comfort_noise_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Unit tests for ComfortNoise class. - -#include "webrtc/modules/audio_coding/neteq4/comfort_noise.h" - -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq4/mock/mock_decoder_database.h" -#include "webrtc/modules/audio_coding/neteq4/sync_buffer.h" - -namespace webrtc { - -TEST(ComfortNoise, CreateAndDestroy) { - int fs = 8000; - MockDecoderDatabase db; - SyncBuffer sync_buffer(1, 1000); - ComfortNoise cn(fs, &db, &sync_buffer); - EXPECT_CALL(db, Die()); // Called when |db| goes out of scope. -} - -// TODO(hlundin): Write more tests. - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,185 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/decision_logic.h" - -#include - -#include "webrtc/modules/audio_coding/neteq4/buffer_level_filter.h" -#include "webrtc/modules/audio_coding/neteq4/decision_logic_fax.h" -#include "webrtc/modules/audio_coding/neteq4/decision_logic_normal.h" -#include "webrtc/modules/audio_coding/neteq4/delay_manager.h" -#include "webrtc/modules/audio_coding/neteq4/expand.h" -#include "webrtc/modules/audio_coding/neteq4/packet_buffer.h" -#include "webrtc/modules/audio_coding/neteq4/sync_buffer.h" -#include "webrtc/system_wrappers/interface/logging.h" - -namespace webrtc { - -DecisionLogic* DecisionLogic::Create(int fs_hz, - int output_size_samples, - NetEqPlayoutMode playout_mode, - DecoderDatabase* decoder_database, - const PacketBuffer& packet_buffer, - DelayManager* delay_manager, - BufferLevelFilter* buffer_level_filter) { - switch (playout_mode) { - case kPlayoutOn: - case kPlayoutStreaming: - return new DecisionLogicNormal(fs_hz, - output_size_samples, - playout_mode, - decoder_database, - packet_buffer, - delay_manager, - buffer_level_filter); - case kPlayoutFax: - case kPlayoutOff: - return new DecisionLogicFax(fs_hz, - output_size_samples, - playout_mode, - decoder_database, - packet_buffer, - delay_manager, - buffer_level_filter); - } - // This line cannot be reached, but must be here to avoid compiler errors. - assert(false); - return NULL; -} - -DecisionLogic::DecisionLogic(int fs_hz, - int output_size_samples, - NetEqPlayoutMode playout_mode, - DecoderDatabase* decoder_database, - const PacketBuffer& packet_buffer, - DelayManager* delay_manager, - BufferLevelFilter* buffer_level_filter) - : decoder_database_(decoder_database), - packet_buffer_(packet_buffer), - delay_manager_(delay_manager), - buffer_level_filter_(buffer_level_filter), - cng_state_(kCngOff), - generated_noise_samples_(0), - packet_length_samples_(0), - sample_memory_(0), - prev_time_scale_(false), - timescale_hold_off_(kMinTimescaleInterval), - num_consecutive_expands_(0), - playout_mode_(playout_mode) { - delay_manager_->set_streaming_mode(playout_mode_ == kPlayoutStreaming); - SetSampleRate(fs_hz, output_size_samples); -} - -void DecisionLogic::Reset() { - cng_state_ = kCngOff; - generated_noise_samples_ = 0; - packet_length_samples_ = 0; - sample_memory_ = 0; - prev_time_scale_ = false; - timescale_hold_off_ = 0; - num_consecutive_expands_ = 0; -} - -void DecisionLogic::SoftReset() { - packet_length_samples_ = 0; - sample_memory_ = 0; - prev_time_scale_ = false; - timescale_hold_off_ = kMinTimescaleInterval; -} - -void DecisionLogic::SetSampleRate(int fs_hz, int output_size_samples) { - // TODO(hlundin): Change to an enumerator and skip assert. - assert(fs_hz == 8000 || fs_hz == 16000 || fs_hz == 32000 || fs_hz == 48000); - fs_mult_ = fs_hz / 8000; - output_size_samples_ = output_size_samples; -} - -Operations DecisionLogic::GetDecision(const SyncBuffer& sync_buffer, - const Expand& expand, - int decoder_frame_length, - const RTPHeader* packet_header, - Modes prev_mode, - bool play_dtmf, bool* reset_decoder) { - if (prev_mode == kModeRfc3389Cng || - prev_mode == kModeCodecInternalCng || - prev_mode == kModeExpand) { - // If last mode was CNG (or Expand, since this could be covering up for - // a lost CNG packet), increase the |generated_noise_samples_| counter. - generated_noise_samples_ += output_size_samples_; - // Remember that CNG is on. This is needed if comfort noise is interrupted - // by DTMF. - if (prev_mode == kModeRfc3389Cng) { - cng_state_ = kCngRfc3389On; - } else if (prev_mode == kModeCodecInternalCng) { - cng_state_ = kCngInternalOn; - } - } - - const int samples_left = static_cast( - sync_buffer.FutureLength() - expand.overlap_length()); - const int cur_size_samples = - samples_left + packet_buffer_.NumSamplesInBuffer(decoder_database_, - decoder_frame_length); - LOG(LS_VERBOSE) << "Buffers: " << packet_buffer_.NumPacketsInBuffer() << - " packets * " << decoder_frame_length << " samples/packet + " << - samples_left << " samples in sync buffer = " << cur_size_samples; - - prev_time_scale_ = prev_time_scale_ && - (prev_mode == kModeAccelerateSuccess || - prev_mode == kModeAccelerateLowEnergy || - prev_mode == kModePreemptiveExpandSuccess || - prev_mode == kModePreemptiveExpandLowEnergy); - - FilterBufferLevel(cur_size_samples, prev_mode); - - return GetDecisionSpecialized(sync_buffer, expand, decoder_frame_length, - packet_header, prev_mode, play_dtmf, - reset_decoder); -} - -void DecisionLogic::ExpandDecision(bool is_expand_decision) { - if (is_expand_decision) { - num_consecutive_expands_++; - } else { - num_consecutive_expands_ = 0; - } -} - -void DecisionLogic::FilterBufferLevel(int buffer_size_samples, - Modes prev_mode) { - const int elapsed_time_ms = output_size_samples_ / (8 * fs_mult_); - delay_manager_->UpdateCounters(elapsed_time_ms); - - // Do not update buffer history if currently playing CNG since it will bias - // the filtered buffer level. - if ((prev_mode != kModeRfc3389Cng) && (prev_mode != kModeCodecInternalCng)) { - buffer_level_filter_->SetTargetBufferLevel( - delay_manager_->base_target_level()); - - int buffer_size_packets = 0; - if (packet_length_samples_ > 0) { - // Calculate size in packets. - buffer_size_packets = buffer_size_samples / packet_length_samples_; - } - int sample_memory_local = 0; - if (prev_time_scale_) { - sample_memory_local = sample_memory_; - timescale_hold_off_ = kMinTimescaleInterval; - } - buffer_level_filter_->Update(buffer_size_packets, sample_memory_local, - packet_length_samples_); - prev_time_scale_ = false; - } - - timescale_hold_off_ = std::max(timescale_hold_off_ - 1, 0); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_fax.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_fax.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_fax.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_fax.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/decision_logic_fax.h" - -#include - -#include - -#include "webrtc/modules/audio_coding/neteq4/decoder_database.h" -#include "webrtc/modules/audio_coding/neteq4/sync_buffer.h" - -namespace webrtc { - -Operations DecisionLogicFax::GetDecisionSpecialized( - const SyncBuffer& sync_buffer, - const Expand& expand, - int decoder_frame_length, - const RTPHeader* packet_header, - Modes prev_mode, - bool play_dtmf, - bool* reset_decoder) { - assert(playout_mode_ == kPlayoutFax || playout_mode_ == kPlayoutOff); - uint32_t target_timestamp = sync_buffer.end_timestamp(); - uint32_t available_timestamp = 0; - int is_cng_packet = 0; - if (packet_header) { - available_timestamp = packet_header->timestamp; - is_cng_packet = - decoder_database_->IsComfortNoise(packet_header->payloadType); - } - if (is_cng_packet) { - if (static_cast((generated_noise_samples_ + target_timestamp) - - available_timestamp) >= 0) { - // Time to play this packet now. - return kRfc3389Cng; - } else { - // Wait before playing this packet. - return kRfc3389CngNoPacket; - } - } - if (!packet_header) { - // No packet. If in CNG mode, play as usual. Otherwise, use other method to - // generate data. - if (cng_state_ == kCngRfc3389On) { - // Continue playing comfort noise. - return kRfc3389CngNoPacket; - } else if (cng_state_ == kCngInternalOn) { - // Continue playing codec-internal comfort noise. - return kCodecInternalCng; - } else { - // Nothing to play. Generate some data to play out. - switch (playout_mode_) { - case kPlayoutOff: - return kAlternativePlc; - case kPlayoutFax: - return kAudioRepetition; - default: - assert(false); - return kUndefined; - } - } - } else if (target_timestamp == available_timestamp) { - return kNormal; - } else { - if (static_cast((generated_noise_samples_ + target_timestamp) - - available_timestamp) >= 0) { - return kNormal; - } else { - // If currently playing comfort noise, continue with that. Do not - // increase the timestamp counter since generated_noise_samples_ will - // be increased. - if (cng_state_ == kCngRfc3389On) { - return kRfc3389CngNoPacket; - } else if (cng_state_ == kCngInternalOn) { - return kCodecInternalCng; - } else { - // Otherwise, do packet-loss concealment and increase the - // timestamp while waiting for the time to play this packet. - switch (playout_mode_) { - case kPlayoutOff: - return kAlternativePlcIncreaseTimestamp; - case kPlayoutFax: - return kAudioRepetitionIncreaseTimestamp; - default: - assert(0); - return kUndefined; - } - } - } - } -} - - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_fax.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_fax.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_fax.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_fax.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DECISION_LOGIC_FAX_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DECISION_LOGIC_FAX_H_ - -#include "webrtc/modules/audio_coding/neteq4/decision_logic.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Implementation of the DecisionLogic class for playout modes kPlayoutFax and -// kPlayoutOff. -class DecisionLogicFax : public DecisionLogic { - public: - // Constructor. - DecisionLogicFax(int fs_hz, - int output_size_samples, - NetEqPlayoutMode playout_mode, - DecoderDatabase* decoder_database, - const PacketBuffer& packet_buffer, - DelayManager* delay_manager, - BufferLevelFilter* buffer_level_filter) - : DecisionLogic(fs_hz, output_size_samples, playout_mode, - decoder_database, packet_buffer, delay_manager, - buffer_level_filter) { - } - - // Destructor. - virtual ~DecisionLogicFax() {} - - protected: - // Returns the operation that should be done next. |sync_buffer| and |expand| - // are provided for reference. |decoder_frame_length| is the number of samples - // obtained from the last decoded frame. If there is a packet available, the - // packet header should be supplied in |packet_header|; otherwise it should - // be NULL. The mode resulting form the last call to NetEqImpl::GetAudio is - // supplied in |prev_mode|. If there is a DTMF event to play, |play_dtmf| - // should be set to true. The output variable |reset_decoder| will be set to - // true if a reset is required; otherwise it is left unchanged (i.e., it can - // remain true if it was true before the call). - virtual Operations GetDecisionSpecialized(const SyncBuffer& sync_buffer, - const Expand& expand, - int decoder_frame_length, - const RTPHeader* packet_header, - Modes prev_mode, - bool play_dtmf, - bool* reset_decoder) OVERRIDE; - - private: - DISALLOW_COPY_AND_ASSIGN(DecisionLogicFax); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DECISION_LOGIC_FAX_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DECISION_LOGIC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DECISION_LOGIC_H_ - -#include "webrtc/modules/audio_coding/neteq4/defines.h" -#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Forward declarations. -class BufferLevelFilter; -class DecoderDatabase; -class DelayManager; -class Expand; -class PacketBuffer; -class SyncBuffer; -struct RTPHeader; - -// This is the base class for the decision tree implementations. Derived classes -// must implement the method GetDecisionSpecialized(). -class DecisionLogic { - public: - // Static factory function which creates different types of objects depending - // on the |playout_mode|. - static DecisionLogic* Create(int fs_hz, - int output_size_samples, - NetEqPlayoutMode playout_mode, - DecoderDatabase* decoder_database, - const PacketBuffer& packet_buffer, - DelayManager* delay_manager, - BufferLevelFilter* buffer_level_filter); - - // Constructor. - DecisionLogic(int fs_hz, - int output_size_samples, - NetEqPlayoutMode playout_mode, - DecoderDatabase* decoder_database, - const PacketBuffer& packet_buffer, - DelayManager* delay_manager, - BufferLevelFilter* buffer_level_filter); - - // Destructor. - virtual ~DecisionLogic() {} - - // Resets object to a clean state. - void Reset(); - - // Resets parts of the state. Typically done when switching codecs. - void SoftReset(); - - // Sets the sample rate and the output block size. - void SetSampleRate(int fs_hz, int output_size_samples); - - // Returns the operation that should be done next. |sync_buffer| and |expand| - // are provided for reference. |decoder_frame_length| is the number of samples - // obtained from the last decoded frame. If there is a packet available, the - // packet header should be supplied in |packet_header|; otherwise it should - // be NULL. The mode resulting form the last call to NetEqImpl::GetAudio is - // supplied in |prev_mode|. If there is a DTMF event to play, |play_dtmf| - // should be set to true. The output variable |reset_decoder| will be set to - // true if a reset is required; otherwise it is left unchanged (i.e., it can - // remain true if it was true before the call). - // This method end with calling GetDecisionSpecialized to get the actual - // return value. - Operations GetDecision(const SyncBuffer& sync_buffer, - const Expand& expand, - int decoder_frame_length, - const RTPHeader* packet_header, - Modes prev_mode, - bool play_dtmf, - bool* reset_decoder); - - // These methods test the |cng_state_| for different conditions. - bool CngRfc3389On() const { return cng_state_ == kCngRfc3389On; } - bool CngOff() const { return cng_state_ == kCngOff; } - - // Resets the |cng_state_| to kCngOff. - void SetCngOff() { cng_state_ = kCngOff; } - - // Reports back to DecisionLogic whether the decision to do expand remains or - // not. Note that this is necessary, since an expand decision can be changed - // to kNormal in NetEqImpl::GetDecision if there is still enough data in the - // sync buffer. - void ExpandDecision(bool is_expand_decision); - - // Adds |value| to |sample_memory_|. - void AddSampleMemory(int32_t value) { - sample_memory_ += value; - } - - // Accessors and mutators. - void set_sample_memory(int32_t value) { sample_memory_ = value; } - int generated_noise_samples() const { return generated_noise_samples_; } - void set_generated_noise_samples(int value) { - generated_noise_samples_ = value; - } - int packet_length_samples() const { return packet_length_samples_; } - void set_packet_length_samples(int value) { - packet_length_samples_ = value; - } - void set_prev_time_scale(bool value) { prev_time_scale_ = value; } - NetEqPlayoutMode playout_mode() const { return playout_mode_; } - - protected: - // The value 6 sets maximum time-stretch rate to about 100 ms/s. - static const int kMinTimescaleInterval = 6; - - enum CngState { - kCngOff, - kCngRfc3389On, - kCngInternalOn - }; - - // Returns the operation that should be done next. |sync_buffer| and |expand| - // are provided for reference. |decoder_frame_length| is the number of samples - // obtained from the last decoded frame. If there is a packet available, the - // packet header should be supplied in |packet_header|; otherwise it should - // be NULL. The mode resulting form the last call to NetEqImpl::GetAudio is - // supplied in |prev_mode|. If there is a DTMF event to play, |play_dtmf| - // should be set to true. The output variable |reset_decoder| will be set to - // true if a reset is required; otherwise it is left unchanged (i.e., it can - // remain true if it was true before the call). - // Should be implemented by derived classes. - virtual Operations GetDecisionSpecialized(const SyncBuffer& sync_buffer, - const Expand& expand, - int decoder_frame_length, - const RTPHeader* packet_header, - Modes prev_mode, - bool play_dtmf, - bool* reset_decoder) = 0; - - // Updates the |buffer_level_filter_| with the current buffer level - // |buffer_size_packets|. - void FilterBufferLevel(int buffer_size_packets, Modes prev_mode); - - DecoderDatabase* decoder_database_; - const PacketBuffer& packet_buffer_; - DelayManager* delay_manager_; - BufferLevelFilter* buffer_level_filter_; - int fs_mult_; - int output_size_samples_; - CngState cng_state_; // Remember if comfort noise is interrupted by other - // event (e.g., DTMF). - int generated_noise_samples_; - int packet_length_samples_; - int sample_memory_; - bool prev_time_scale_; - int timescale_hold_off_; - int num_consecutive_expands_; - const NetEqPlayoutMode playout_mode_; - - private: - DISALLOW_COPY_AND_ASSIGN(DecisionLogic); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DECISION_LOGIC_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_normal.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_normal.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_normal.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_normal.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,235 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/decision_logic_normal.h" - -#include - -#include - -#include "webrtc/modules/audio_coding/neteq4/buffer_level_filter.h" -#include "webrtc/modules/audio_coding/neteq4/decoder_database.h" -#include "webrtc/modules/audio_coding/neteq4/delay_manager.h" -#include "webrtc/modules/audio_coding/neteq4/expand.h" -#include "webrtc/modules/audio_coding/neteq4/packet_buffer.h" -#include "webrtc/modules/audio_coding/neteq4/sync_buffer.h" -#include "webrtc/modules/interface/module_common_types.h" - -namespace webrtc { - -Operations DecisionLogicNormal::GetDecisionSpecialized( - const SyncBuffer& sync_buffer, - const Expand& expand, - int decoder_frame_length, - const RTPHeader* packet_header, - Modes prev_mode, - bool play_dtmf, - bool* reset_decoder) { - assert(playout_mode_ == kPlayoutOn || playout_mode_ == kPlayoutStreaming); - // Guard for errors, to avoid getting stuck in error mode. - if (prev_mode == kModeError) { - if (!packet_header) { - return kExpand; - } else { - return kUndefined; // Use kUndefined to flag for a reset. - } - } - - uint32_t target_timestamp = sync_buffer.end_timestamp(); - uint32_t available_timestamp = 0; - bool is_cng_packet = false; - if (packet_header) { - available_timestamp = packet_header->timestamp; - is_cng_packet = - decoder_database_->IsComfortNoise(packet_header->payloadType); - } - - if (is_cng_packet) { - return CngOperation(prev_mode, target_timestamp, available_timestamp); - } - - // Handle the case with no packet at all available (except maybe DTMF). - if (!packet_header) { - return NoPacket(play_dtmf); - } - - // If the expand period was very long, reset NetEQ since it is likely that the - // sender was restarted. - if (num_consecutive_expands_ > kReinitAfterExpands) { - *reset_decoder = true; - return kNormal; - } - - // Check if the required packet is available. - if (target_timestamp == available_timestamp) { - return ExpectedPacketAvailable(prev_mode, play_dtmf); - } else if (IsNewerTimestamp(available_timestamp, target_timestamp)) { - return FuturePacketAvailable(sync_buffer, expand, decoder_frame_length, - prev_mode, target_timestamp, - available_timestamp, play_dtmf); - } else { - // This implies that available_timestamp < target_timestamp, which can - // happen when a new stream or codec is received. Signal for a reset. - return kUndefined; - } -} - -Operations DecisionLogicNormal::CngOperation(Modes prev_mode, - uint32_t target_timestamp, - uint32_t available_timestamp) { - // Signed difference between target and available timestamp. - int32_t timestamp_diff = (generated_noise_samples_ + target_timestamp) - - available_timestamp; - int32_t optimal_level_samp = - (delay_manager_->TargetLevel() * packet_length_samples_) >> 8; - int32_t excess_waiting_time_samp = -timestamp_diff - optimal_level_samp; - - if (excess_waiting_time_samp > optimal_level_samp / 2) { - // The waiting time for this packet will be longer than 1.5 - // times the wanted buffer delay. Advance the clock to cut - // waiting time down to the optimal. - generated_noise_samples_ += excess_waiting_time_samp; - timestamp_diff += excess_waiting_time_samp; - } - - if (timestamp_diff < 0 && prev_mode == kModeRfc3389Cng) { - // Not time to play this packet yet. Wait another round before using this - // packet. Keep on playing CNG from previous CNG parameters. - return kRfc3389CngNoPacket; - } else { - // Otherwise, go for the CNG packet now. - return kRfc3389Cng; - } -} - -Operations DecisionLogicNormal::NoPacket(bool play_dtmf) { - if (cng_state_ == kCngRfc3389On) { - // Keep on playing comfort noise. - return kRfc3389CngNoPacket; - } else if (cng_state_ == kCngInternalOn) { - // Keep on playing codec internal comfort noise. - return kCodecInternalCng; - } else if (play_dtmf) { - return kDtmf; - } else { - // Nothing to play, do expand. - return kExpand; - } -} - -Operations DecisionLogicNormal::ExpectedPacketAvailable(Modes prev_mode, - bool play_dtmf) { - if (prev_mode != kModeExpand && !play_dtmf) { - // Check criterion for time-stretching. - int low_limit, high_limit; - delay_manager_->BufferLimits(&low_limit, &high_limit); - if ((buffer_level_filter_->filtered_current_level() >= high_limit && - TimescaleAllowed()) || - buffer_level_filter_->filtered_current_level() >= high_limit << 2) { - // Buffer level higher than limit and time-scaling allowed, - // or buffer level really high. - return kAccelerate; - } else if ((buffer_level_filter_->filtered_current_level() < low_limit) - && TimescaleAllowed()) { - return kPreemptiveExpand; - } - } - return kNormal; -} - -Operations DecisionLogicNormal::FuturePacketAvailable( - const SyncBuffer& sync_buffer, - const Expand& expand, - int decoder_frame_length, - Modes prev_mode, - uint32_t target_timestamp, - uint32_t available_timestamp, - bool play_dtmf) { - // Required packet is not available, but a future packet is. - // Check if we should continue with an ongoing expand because the new packet - // is too far into the future. - uint32_t timestamp_leap = available_timestamp - target_timestamp; - if ((prev_mode == kModeExpand) && - !ReinitAfterExpands(timestamp_leap) && - !MaxWaitForPacket() && - PacketTooEarly(timestamp_leap) && - UnderTargetLevel()) { - if (play_dtmf) { - // Still have DTMF to play, so do not do expand. - return kDtmf; - } else { - // Nothing to play. - return kExpand; - } - } - - const int samples_left = static_cast(sync_buffer.FutureLength() - - expand.overlap_length()); - const int cur_size_samples = samples_left + - packet_buffer_.NumPacketsInBuffer() * decoder_frame_length; - - // If previous was comfort noise, then no merge is needed. - if (prev_mode == kModeRfc3389Cng || - prev_mode == kModeCodecInternalCng) { - // Keep the same delay as before the CNG (or maximum 70 ms in buffer as - // safety precaution), but make sure that the number of samples in buffer - // is no higher than 4 times the optimal level. (Note that TargetLevel() - // is in Q8.) - int32_t timestamp_diff = (generated_noise_samples_ + target_timestamp) - - available_timestamp; - if (timestamp_diff >= 0 || - cur_size_samples > - 4 * ((delay_manager_->TargetLevel() * packet_length_samples_) >> 8)) { - // Time to play this new packet. - return kNormal; - } else { - // Too early to play this new packet; keep on playing comfort noise. - if (prev_mode == kModeRfc3389Cng) { - return kRfc3389CngNoPacket; - } else { // prevPlayMode == kModeCodecInternalCng. - return kCodecInternalCng; - } - } - } - // Do not merge unless we have done an expand before. - // (Convert kAllowMergeWithoutExpand from ms to samples by multiplying with - // fs_mult_ * 8 = fs / 1000.) - if (prev_mode == kModeExpand || - (decoder_frame_length < output_size_samples_ && - cur_size_samples > kAllowMergeWithoutExpandMs * fs_mult_ * 8)) { - return kMerge; - } else if (play_dtmf) { - // Play DTMF instead of expand. - return kDtmf; - } else { - return kExpand; - } -} - -bool DecisionLogicNormal::UnderTargetLevel() const { - return buffer_level_filter_->filtered_current_level() <= - delay_manager_->TargetLevel(); -} - -bool DecisionLogicNormal::ReinitAfterExpands(uint32_t timestamp_leap) const { - return timestamp_leap >= - static_cast(output_size_samples_ * kReinitAfterExpands); -} - -bool DecisionLogicNormal::PacketTooEarly(uint32_t timestamp_leap) const { - return timestamp_leap > - static_cast(output_size_samples_ * num_consecutive_expands_); -} - -bool DecisionLogicNormal::MaxWaitForPacket() const { - return num_consecutive_expands_ >= kMaxWaitForPacket; -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_normal.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_normal.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_normal.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_normal.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DECISION_LOGIC_NORMAL_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DECISION_LOGIC_NORMAL_H_ - -#include "webrtc/modules/audio_coding/neteq4/decision_logic.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Implementation of the DecisionLogic class for playout modes kPlayoutOn and -// kPlayoutStreaming. -class DecisionLogicNormal : public DecisionLogic { - public: - // Constructor. - DecisionLogicNormal(int fs_hz, - int output_size_samples, - NetEqPlayoutMode playout_mode, - DecoderDatabase* decoder_database, - const PacketBuffer& packet_buffer, - DelayManager* delay_manager, - BufferLevelFilter* buffer_level_filter) - : DecisionLogic(fs_hz, output_size_samples, playout_mode, - decoder_database, packet_buffer, delay_manager, - buffer_level_filter) { - } - - // Destructor. - virtual ~DecisionLogicNormal() {} - - protected: - // Returns the operation that should be done next. |sync_buffer| and |expand| - // are provided for reference. |decoder_frame_length| is the number of samples - // obtained from the last decoded frame. If there is a packet available, the - // packet header should be supplied in |packet_header|; otherwise it should - // be NULL. The mode resulting form the last call to NetEqImpl::GetAudio is - // supplied in |prev_mode|. If there is a DTMF event to play, |play_dtmf| - // should be set to true. The output variable |reset_decoder| will be set to - // true if a reset is required; otherwise it is left unchanged (i.e., it can - // remain true if it was true before the call). - virtual Operations GetDecisionSpecialized(const SyncBuffer& sync_buffer, - const Expand& expand, - int decoder_frame_length, - const RTPHeader* packet_header, - Modes prev_mode, bool play_dtmf, - bool* reset_decoder); - - private: - static const int kAllowMergeWithoutExpandMs = 20; // 20 ms. - static const int kReinitAfterExpands = 100; - static const int kMaxWaitForPacket = 10; - - // Returns the operation given that the next available packet is a comfort - // noise payload (RFC 3389 only, not codec-internal). - Operations CngOperation(Modes prev_mode, uint32_t target_timestamp, - uint32_t available_timestamp); - - // Returns the operation given that no packets are available (except maybe - // a DTMF event, flagged by setting |play_dtmf| true). - Operations NoPacket(bool play_dtmf); - - // Returns the operation to do given that the expected packet is available. - Operations ExpectedPacketAvailable(Modes prev_mode, bool play_dtmf); - - // Returns the operation to do given that the expected packet is not - // available, but a packet further into the future is at hand. - Operations FuturePacketAvailable(const SyncBuffer& sync_buffer, - const Expand& expand, - int decoder_frame_length, Modes prev_mode, - uint32_t target_timestamp, - uint32_t available_timestamp, - bool play_dtmf); - - // Checks if enough time has elapsed since the last successful timescale - // operation was done (i.e., accelerate or preemptive expand). - bool TimescaleAllowed() const { return timescale_hold_off_ == 0; } - - // Checks if the current (filtered) buffer level is under the target level. - bool UnderTargetLevel() const; - - // Checks if |timestamp_leap| is so long into the future that a reset due - // to exceeding kReinitAfterExpands will be done. - bool ReinitAfterExpands(uint32_t timestamp_leap) const; - - // Checks if we still have not done enough expands to cover the distance from - // the last decoded packet to the next available packet, the distance beeing - // conveyed in |timestamp_leap|. - bool PacketTooEarly(uint32_t timestamp_leap) const; - - // Checks if num_consecutive_expands_ >= kMaxWaitForPacket. - bool MaxWaitForPacket() const; - - DISALLOW_COPY_AND_ASSIGN(DecisionLogicNormal); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DECISION_LOGIC_NORMAL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decision_logic_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Unit tests for DecisionLogic class and derived classes. - -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq4/buffer_level_filter.h" -#include "webrtc/modules/audio_coding/neteq4/decoder_database.h" -#include "webrtc/modules/audio_coding/neteq4/decision_logic.h" -#include "webrtc/modules/audio_coding/neteq4/delay_manager.h" -#include "webrtc/modules/audio_coding/neteq4/delay_peak_detector.h" -#include "webrtc/modules/audio_coding/neteq4/packet_buffer.h" - -namespace webrtc { - -TEST(DecisionLogic, CreateAndDestroy) { - int fs_hz = 8000; - int output_size_samples = fs_hz / 100; // Samples per 10 ms. - DecoderDatabase decoder_database; - PacketBuffer packet_buffer(10, 1000); - DelayPeakDetector delay_peak_detector; - DelayManager delay_manager(240, &delay_peak_detector); - BufferLevelFilter buffer_level_filter; - DecisionLogic* logic = DecisionLogic::Create(fs_hz, output_size_samples, - kPlayoutOn, &decoder_database, - packet_buffer, &delay_manager, - &buffer_level_filter); - delete logic; - logic = DecisionLogic::Create(fs_hz, output_size_samples, - kPlayoutStreaming, - &decoder_database, - packet_buffer, &delay_manager, - &buffer_level_filter); - delete logic; - logic = DecisionLogic::Create(fs_hz, output_size_samples, - kPlayoutFax, - &decoder_database, - packet_buffer, &delay_manager, - &buffer_level_filter); - delete logic; - logic = DecisionLogic::Create(fs_hz, output_size_samples, - kPlayoutOff, - &decoder_database, - packet_buffer, &delay_manager, - &buffer_level_filter); - delete logic; -} - -// TODO(hlundin): Write more tests. - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decoder_database.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decoder_database.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decoder_database.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decoder_database.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,260 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/decoder_database.h" - -#include -#include // pair - -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" - -namespace webrtc { - -DecoderDatabase::DecoderDatabase() - : active_decoder_(-1), active_cng_decoder_(-1) {} - -DecoderDatabase::~DecoderDatabase() {} - -DecoderDatabase::DecoderInfo::~DecoderInfo() { - if (!external) delete decoder; -} - -bool DecoderDatabase::Empty() const { return decoders_.empty(); } - -int DecoderDatabase::Size() const { return static_cast(decoders_.size()); } - -void DecoderDatabase::Reset() { - decoders_.clear(); - active_decoder_ = -1; - active_cng_decoder_ = -1; -} - -int DecoderDatabase::RegisterPayload(uint8_t rtp_payload_type, - NetEqDecoder codec_type) { - if (rtp_payload_type > kMaxRtpPayloadType) { - return kInvalidRtpPayloadType; - } - if (!AudioDecoder::CodecSupported(codec_type)) { - return kCodecNotSupported; - } - int fs_hz = AudioDecoder::CodecSampleRateHz(codec_type); - std::pair ret; - DecoderInfo info(codec_type, fs_hz, NULL, false); - ret = decoders_.insert(std::make_pair(rtp_payload_type, info)); - if (ret.second == false) { - // Database already contains a decoder with type |rtp_payload_type|. - return kDecoderExists; - } - return kOK; -} - -int DecoderDatabase::InsertExternal(uint8_t rtp_payload_type, - NetEqDecoder codec_type, - int fs_hz, - AudioDecoder* decoder) { - if (rtp_payload_type > 0x7F) { - return kInvalidRtpPayloadType; - } - if (!AudioDecoder::CodecSupported(codec_type)) { - return kCodecNotSupported; - } - if (fs_hz != 8000 && fs_hz != 16000 && fs_hz != 32000 && fs_hz != 48000) { - return kInvalidSampleRate; - } - if (!decoder) { - return kInvalidPointer; - } - decoder->Init(); - std::pair ret; - DecoderInfo info(codec_type, fs_hz, decoder, true); - ret = decoders_.insert( - std::pair(rtp_payload_type, info)); - if (ret.second == false) { - // Database already contains a decoder with type |rtp_payload_type|. - return kDecoderExists; - } - return kOK; -} - -int DecoderDatabase::Remove(uint8_t rtp_payload_type) { - if (decoders_.erase(rtp_payload_type) == 0) { - // No decoder with that |rtp_payload_type|. - return kDecoderNotFound; - } - if (active_decoder_ == rtp_payload_type) { - active_decoder_ = -1; // No active decoder. - } - if (active_cng_decoder_ == rtp_payload_type) { - active_cng_decoder_ = -1; // No active CNG decoder. - } - return kOK; -} - -const DecoderDatabase::DecoderInfo* DecoderDatabase::GetDecoderInfo( - uint8_t rtp_payload_type) const { - DecoderMap::const_iterator it = decoders_.find(rtp_payload_type); - if (it == decoders_.end()) { - // Decoder not found. - return NULL; - } - return &(*it).second; -} - -uint8_t DecoderDatabase::GetRtpPayloadType( - NetEqDecoder codec_type) const { - DecoderMap::const_iterator it; - for (it = decoders_.begin(); it != decoders_.end(); ++it) { - if ((*it).second.codec_type == codec_type) { - // Match found. - return (*it).first; - } - } - // No match. - return kRtpPayloadTypeError; -} - -AudioDecoder* DecoderDatabase::GetDecoder(uint8_t rtp_payload_type) { - if (IsDtmf(rtp_payload_type) || IsRed(rtp_payload_type)) { - // These are not real decoders. - return NULL; - } - DecoderMap::iterator it = decoders_.find(rtp_payload_type); - if (it == decoders_.end()) { - // Decoder not found. - return NULL; - } - DecoderInfo* info = &(*it).second; - if (!info->decoder) { - // Create the decoder object. - AudioDecoder* decoder = AudioDecoder::CreateAudioDecoder(info->codec_type); - assert(decoder); // Should not be able to have an unsupported codec here. - info->decoder = decoder; - info->decoder->Init(); - } - return info->decoder; -} - -bool DecoderDatabase::IsType(uint8_t rtp_payload_type, - NetEqDecoder codec_type) const { - DecoderMap::const_iterator it = decoders_.find(rtp_payload_type); - if (it == decoders_.end()) { - // Decoder not found. - return false; - } - return ((*it).second.codec_type == codec_type); -} - -bool DecoderDatabase::IsComfortNoise(uint8_t rtp_payload_type) const { - if (IsType(rtp_payload_type, kDecoderCNGnb) || - IsType(rtp_payload_type, kDecoderCNGwb) || - IsType(rtp_payload_type, kDecoderCNGswb32kHz) || - IsType(rtp_payload_type, kDecoderCNGswb48kHz)) { - return true; - } else { - return false; - } -} - -bool DecoderDatabase::IsDtmf(uint8_t rtp_payload_type) const { - return IsType(rtp_payload_type, kDecoderAVT); -} - -bool DecoderDatabase::IsRed(uint8_t rtp_payload_type) const { - return IsType(rtp_payload_type, kDecoderRED); -} - -int DecoderDatabase::SetActiveDecoder(uint8_t rtp_payload_type, - bool* new_decoder) { - // Check that |rtp_payload_type| exists in the database. - DecoderMap::const_iterator it = decoders_.find(rtp_payload_type); - if (it == decoders_.end()) { - // Decoder not found. - return kDecoderNotFound; - } - assert(new_decoder); - *new_decoder = false; - if (active_decoder_ < 0) { - // This is the first active decoder. - *new_decoder = true; - } else if (active_decoder_ != rtp_payload_type) { - // Moving from one active decoder to another. Delete the first one. - DecoderMap::iterator it = decoders_.find(active_decoder_); - if (it == decoders_.end()) { - // Decoder not found. This should not be possible. - assert(false); - return kDecoderNotFound; - } - if (!(*it).second.external) { - // Delete the AudioDecoder object, unless it is an externally created - // decoder. - delete (*it).second.decoder; - (*it).second.decoder = NULL; - } - *new_decoder = true; - } - active_decoder_ = rtp_payload_type; - return kOK; -} - -AudioDecoder* DecoderDatabase::GetActiveDecoder() { - if (active_decoder_ < 0) { - // No active decoder. - return NULL; - } - return GetDecoder(active_decoder_); -} - -int DecoderDatabase::SetActiveCngDecoder(uint8_t rtp_payload_type) { - // Check that |rtp_payload_type| exists in the database. - DecoderMap::const_iterator it = decoders_.find(rtp_payload_type); - if (it == decoders_.end()) { - // Decoder not found. - return kDecoderNotFound; - } - if (active_cng_decoder_ >= 0 && active_cng_decoder_ != rtp_payload_type) { - // Moving from one active CNG decoder to another. Delete the first one. - DecoderMap::iterator it = decoders_.find(active_cng_decoder_); - if (it == decoders_.end()) { - // Decoder not found. This should not be possible. - assert(false); - return kDecoderNotFound; - } - if (!(*it).second.external) { - // Delete the AudioDecoder object, unless it is an externally created - // decoder. - delete (*it).second.decoder; - (*it).second.decoder = NULL; - } - } - active_cng_decoder_ = rtp_payload_type; - return kOK; -} - -AudioDecoder* DecoderDatabase::GetActiveCngDecoder() { - if (active_cng_decoder_ < 0) { - // No active CNG decoder. - return NULL; - } - return GetDecoder(active_cng_decoder_); -} - -int DecoderDatabase::CheckPayloadTypes(const PacketList& packet_list) const { - PacketList::const_iterator it; - for (it = packet_list.begin(); it != packet_list.end(); ++it) { - if (decoders_.find((*it)->header.payloadType) == decoders_.end()) { - // Payload type is not found. - return kDecoderNotFound; - } - } - return kOK; -} - - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decoder_database.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decoder_database.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decoder_database.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decoder_database.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DECODER_DATABASE_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DECODER_DATABASE_H_ - -#include - -#include "webrtc/common_types.h" // NULL -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" -#include "webrtc/modules/audio_coding/neteq4/packet.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Forward declaration. -class AudioDecoder; - -class DecoderDatabase { - public: - enum DatabaseReturnCodes { - kOK = 0, - kInvalidRtpPayloadType = -1, - kCodecNotSupported = -2, - kInvalidSampleRate = -3, - kDecoderExists = -4, - kDecoderNotFound = -5, - kInvalidPointer = -6 - }; - - // Struct used to store decoder info in the database. - struct DecoderInfo { - // Constructors. - DecoderInfo() - : codec_type(kDecoderArbitrary), - fs_hz(8000), - decoder(NULL), - external(false) { - } - DecoderInfo(NetEqDecoder ct, int fs, AudioDecoder* dec, bool ext) - : codec_type(ct), - fs_hz(fs), - decoder(dec), - external(ext) { - } - // Destructor. (Defined in decoder_database.cc.) - ~DecoderInfo(); - - NetEqDecoder codec_type; - int fs_hz; - AudioDecoder* decoder; - bool external; - }; - - static const uint8_t kMaxRtpPayloadType = 0x7F; // Max for a 7-bit number. - // Maximum value for 8 bits, and an invalid RTP payload type (since it is - // only 7 bits). - static const uint8_t kRtpPayloadTypeError = 0xFF; - - DecoderDatabase(); - - virtual ~DecoderDatabase(); - - // Returns true if the database is empty. - virtual bool Empty() const; - - // Returns the number of decoders registered in the database. - virtual int Size() const; - - // Resets the database, erasing all registered payload types, and deleting - // any AudioDecoder objects that were not externally created and inserted - // using InsertExternal(). - virtual void Reset(); - - // Registers |rtp_payload_type| as a decoder of type |codec_type|. Returns - // kOK on success; otherwise an error code. - virtual int RegisterPayload(uint8_t rtp_payload_type, - NetEqDecoder codec_type); - - // Registers an externally created AudioDecoder object, and associates it - // as a decoder of type |codec_type| with |rtp_payload_type|. - virtual int InsertExternal(uint8_t rtp_payload_type, - NetEqDecoder codec_type, - int fs_hz, AudioDecoder* decoder); - - // Removes the entry for |rtp_payload_type| from the database. - // Returns kDecoderNotFound or kOK depending on the outcome of the operation. - virtual int Remove(uint8_t rtp_payload_type); - - // Returns a pointer to the DecoderInfo struct for |rtp_payload_type|. If - // no decoder is registered with that |rtp_payload_type|, NULL is returned. - virtual const DecoderInfo* GetDecoderInfo(uint8_t rtp_payload_type) const; - - // Returns one RTP payload type associated with |codec_type|, or - // kDecoderNotFound if no entry exists for that value. Note that one - // |codec_type| may be registered with several RTP payload types, and the - // method may return any of them. - virtual uint8_t GetRtpPayloadType(NetEqDecoder codec_type) const; - - // Returns a pointer to the AudioDecoder object associated with - // |rtp_payload_type|, or NULL if none is registered. If the AudioDecoder - // object does not exist for that decoder, the object is created. - virtual AudioDecoder* GetDecoder(uint8_t rtp_payload_type); - - // Returns true if |rtp_payload_type| is registered as a |codec_type|. - virtual bool IsType(uint8_t rtp_payload_type, - NetEqDecoder codec_type) const; - - // Returns true if |rtp_payload_type| is registered as comfort noise. - virtual bool IsComfortNoise(uint8_t rtp_payload_type) const; - - // Returns true if |rtp_payload_type| is registered as DTMF. - virtual bool IsDtmf(uint8_t rtp_payload_type) const; - - // Returns true if |rtp_payload_type| is registered as RED. - virtual bool IsRed(uint8_t rtp_payload_type) const; - - // Sets the active decoder to be |rtp_payload_type|. If this call results in a - // change of active decoder, |new_decoder| is set to true. The previous active - // decoder's AudioDecoder object is deleted. - virtual int SetActiveDecoder(uint8_t rtp_payload_type, bool* new_decoder); - - // Returns the current active decoder, or NULL if no active decoder exists. - virtual AudioDecoder* GetActiveDecoder(); - - // Sets the active comfort noise decoder to be |rtp_payload_type|. If this - // call results in a change of active comfort noise decoder, the previous - // active decoder's AudioDecoder object is deleted. - virtual int SetActiveCngDecoder(uint8_t rtp_payload_type); - - // Returns the current active comfort noise decoder, or NULL if no active - // comfort noise decoder exists. - virtual AudioDecoder* GetActiveCngDecoder(); - - // Returns kOK if all packets in |packet_list| carry payload types that are - // registered in the database. Otherwise, returns kDecoderNotFound. - virtual int CheckPayloadTypes(const PacketList& packet_list) const; - - private: - typedef std::map DecoderMap; - - DecoderMap decoders_; - int active_decoder_; - int active_cng_decoder_; - - DISALLOW_COPY_AND_ASSIGN(DecoderDatabase); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DECODER_DATABASE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decoder_database_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decoder_database_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decoder_database_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/decoder_database_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,228 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/decoder_database.h" - -#include -#include - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "webrtc/modules/audio_coding/neteq4/mock/mock_audio_decoder.h" -#include "webrtc/test/testsupport/gtest_disable.h" - -namespace webrtc { - -TEST(DecoderDatabase, CreateAndDestroy) { - DecoderDatabase db; - EXPECT_EQ(0, db.Size()); - EXPECT_TRUE(db.Empty()); -} - -TEST(DecoderDatabase, InsertAndRemove) { - DecoderDatabase db; - const uint8_t kPayloadType = 0; - EXPECT_EQ(DecoderDatabase::kOK, - db.RegisterPayload(kPayloadType, kDecoderPCMu)); - EXPECT_EQ(1, db.Size()); - EXPECT_FALSE(db.Empty()); - EXPECT_EQ(DecoderDatabase::kOK, db.Remove(kPayloadType)); - EXPECT_EQ(0, db.Size()); - EXPECT_TRUE(db.Empty()); -} - -TEST(DecoderDatabase, GetDecoderInfo) { - DecoderDatabase db; - const uint8_t kPayloadType = 0; - EXPECT_EQ(DecoderDatabase::kOK, - db.RegisterPayload(kPayloadType, kDecoderPCMu)); - const DecoderDatabase::DecoderInfo* info; - info = db.GetDecoderInfo(kPayloadType); - ASSERT_TRUE(info != NULL); - EXPECT_EQ(kDecoderPCMu, info->codec_type); - EXPECT_EQ(NULL, info->decoder); - EXPECT_EQ(8000, info->fs_hz); - EXPECT_FALSE(info->external); - info = db.GetDecoderInfo(kPayloadType + 1); // Other payload type. - EXPECT_TRUE(info == NULL); // Should not be found. -} - -TEST(DecoderDatabase, GetRtpPayloadType) { - DecoderDatabase db; - const uint8_t kPayloadType = 0; - EXPECT_EQ(DecoderDatabase::kOK, - db.RegisterPayload(kPayloadType, kDecoderPCMu)); - EXPECT_EQ(kPayloadType, db.GetRtpPayloadType(kDecoderPCMu)); - const uint8_t expected_value = DecoderDatabase::kRtpPayloadTypeError; - EXPECT_EQ(expected_value, - db.GetRtpPayloadType(kDecoderISAC)); // iSAC is not registered. -} - -TEST(DecoderDatabase, DISABLED_ON_ANDROID(GetDecoder)) { - DecoderDatabase db; - const uint8_t kPayloadType = 0; - EXPECT_EQ(DecoderDatabase::kOK, - db.RegisterPayload(kPayloadType, kDecoderILBC)); - AudioDecoder* dec = db.GetDecoder(kPayloadType); - ASSERT_TRUE(dec != NULL); -} - -TEST(DecoderDatabase, TypeTests) { - DecoderDatabase db; - const uint8_t kPayloadTypePcmU = 0; - const uint8_t kPayloadTypeCng = 13; - const uint8_t kPayloadTypeDtmf = 100; - const uint8_t kPayloadTypeRed = 101; - const uint8_t kPayloadNotUsed = 102; - // Load into database. - EXPECT_EQ(DecoderDatabase::kOK, - db.RegisterPayload(kPayloadTypePcmU, kDecoderPCMu)); - EXPECT_EQ(DecoderDatabase::kOK, - db.RegisterPayload(kPayloadTypeCng, kDecoderCNGnb)); - EXPECT_EQ(DecoderDatabase::kOK, - db.RegisterPayload(kPayloadTypeDtmf, kDecoderAVT)); - EXPECT_EQ(DecoderDatabase::kOK, - db.RegisterPayload(kPayloadTypeRed, kDecoderRED)); - EXPECT_EQ(4, db.Size()); - // Test. - EXPECT_FALSE(db.IsComfortNoise(kPayloadNotUsed)); - EXPECT_FALSE(db.IsDtmf(kPayloadNotUsed)); - EXPECT_FALSE(db.IsRed(kPayloadNotUsed)); - EXPECT_FALSE(db.IsComfortNoise(kPayloadTypePcmU)); - EXPECT_FALSE(db.IsDtmf(kPayloadTypePcmU)); - EXPECT_FALSE(db.IsRed(kPayloadTypePcmU)); - EXPECT_FALSE(db.IsType(kPayloadTypePcmU, kDecoderISAC)); - EXPECT_TRUE(db.IsType(kPayloadTypePcmU, kDecoderPCMu)); - EXPECT_TRUE(db.IsComfortNoise(kPayloadTypeCng)); - EXPECT_TRUE(db.IsDtmf(kPayloadTypeDtmf)); - EXPECT_TRUE(db.IsRed(kPayloadTypeRed)); -} - -TEST(DecoderDatabase, ExternalDecoder) { - DecoderDatabase db; - const uint8_t kPayloadType = 0; - MockAudioDecoder decoder; - // Load into database. - EXPECT_EQ(DecoderDatabase::kOK, - db.InsertExternal(kPayloadType, kDecoderPCMu, 8000, - &decoder)); - EXPECT_EQ(1, db.Size()); - // Get decoder and make sure we get the external one. - EXPECT_EQ(&decoder, db.GetDecoder(kPayloadType)); - // Get the decoder info struct and check it too. - const DecoderDatabase::DecoderInfo* info; - info = db.GetDecoderInfo(kPayloadType); - ASSERT_TRUE(info != NULL); - EXPECT_EQ(kDecoderPCMu, info->codec_type); - EXPECT_EQ(&decoder, info->decoder); - EXPECT_EQ(8000, info->fs_hz); - EXPECT_TRUE(info->external); - // Expect not to delete the decoder when removing it from the database, since - // it was declared externally. - EXPECT_CALL(decoder, Die()).Times(0); - EXPECT_EQ(DecoderDatabase::kOK, db.Remove(kPayloadType)); - EXPECT_TRUE(db.Empty()); - - EXPECT_CALL(decoder, Die()).Times(1); // Will be called when |db| is deleted. -} - -TEST(DecoderDatabase, CheckPayloadTypes) { - DecoderDatabase db; - // Load a number of payloads into the database. Payload types are 0, 1, ..., - // while the decoder type is the same for all payload types (this does not - // matter for the test). - const int kNumPayloads = 10; - for (uint8_t payload_type = 0; payload_type < kNumPayloads; ++payload_type) { - EXPECT_EQ(DecoderDatabase::kOK, - db.RegisterPayload(payload_type, kDecoderArbitrary)); - } - PacketList packet_list; - for (int i = 0; i < kNumPayloads + 1; ++i) { - // Create packet with payload type |i|. The last packet will have a payload - // type that is not registered in the decoder database. - Packet* packet = new Packet; - packet->header.payloadType = i; - packet_list.push_back(packet); - } - - // Expect to return false, since the last packet is of an unknown type. - EXPECT_EQ(DecoderDatabase::kDecoderNotFound, - db.CheckPayloadTypes(packet_list)); - - delete packet_list.back(); - packet_list.pop_back(); // Remove the unknown one. - - EXPECT_EQ(DecoderDatabase::kOK, db.CheckPayloadTypes(packet_list)); - - // Delete all packets. - PacketList::iterator it = packet_list.begin(); - while (it != packet_list.end()) { - delete packet_list.front(); - it = packet_list.erase(it); - } -} - -// Test the methods for setting and getting active speech and CNG decoders. -TEST(DecoderDatabase, ActiveDecoders) { - DecoderDatabase db; - // Load payload types. - ASSERT_EQ(DecoderDatabase::kOK, db.RegisterPayload(0, kDecoderPCMu)); - ASSERT_EQ(DecoderDatabase::kOK, db.RegisterPayload(103, kDecoderISAC)); - ASSERT_EQ(DecoderDatabase::kOK, db.RegisterPayload(13, kDecoderCNGnb)); - // Verify that no decoders are active from the start. - EXPECT_EQ(NULL, db.GetActiveDecoder()); - EXPECT_EQ(NULL, db.GetActiveCngDecoder()); - - // Set active speech codec. - bool changed; // Should be true when the active decoder changed. - EXPECT_EQ(DecoderDatabase::kOK, db.SetActiveDecoder(0, &changed)); - EXPECT_TRUE(changed); - AudioDecoder* decoder = db.GetActiveDecoder(); - ASSERT_FALSE(decoder == NULL); // Should get a decoder here. - EXPECT_EQ(kDecoderPCMu, decoder->codec_type()); - - // Set the same again. Expect no change. - EXPECT_EQ(DecoderDatabase::kOK, db.SetActiveDecoder(0, &changed)); - EXPECT_FALSE(changed); - decoder = db.GetActiveDecoder(); - ASSERT_FALSE(decoder == NULL); // Should get a decoder here. - EXPECT_EQ(kDecoderPCMu, decoder->codec_type()); - - // Change active decoder. - EXPECT_EQ(DecoderDatabase::kOK, db.SetActiveDecoder(103, &changed)); - EXPECT_TRUE(changed); - decoder = db.GetActiveDecoder(); - ASSERT_FALSE(decoder == NULL); // Should get a decoder here. - EXPECT_EQ(kDecoderISAC, decoder->codec_type()); - - // Remove the active decoder, and verify that the active becomes NULL. - EXPECT_EQ(DecoderDatabase::kOK, db.Remove(103)); - EXPECT_EQ(NULL, db.GetActiveDecoder()); - - // Set active CNG codec. - EXPECT_EQ(DecoderDatabase::kOK, db.SetActiveCngDecoder(13)); - decoder = db.GetActiveCngDecoder(); - ASSERT_FALSE(decoder == NULL); // Should get a decoder here. - EXPECT_EQ(kDecoderCNGnb, decoder->codec_type()); - - // Remove the active CNG decoder, and verify that the active becomes NULL. - EXPECT_EQ(DecoderDatabase::kOK, db.Remove(13)); - EXPECT_EQ(NULL, db.GetActiveCngDecoder()); - - // Try to set non-existing codecs as active. - EXPECT_EQ(DecoderDatabase::kDecoderNotFound, - db.SetActiveDecoder(17, &changed)); - EXPECT_EQ(DecoderDatabase::kDecoderNotFound, - db.SetActiveCngDecoder(17)); -} -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/defines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/defines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/defines.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/defines.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DEFINES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DEFINES_H_ - -namespace webrtc { - -enum Operations { - kNormal = 0, - kMerge, - kExpand, - kAccelerate, - kPreemptiveExpand, - kRfc3389Cng, - kRfc3389CngNoPacket, - kCodecInternalCng, - kDtmf, - kAlternativePlc, - kAlternativePlcIncreaseTimestamp, - kAudioRepetition, - kAudioRepetitionIncreaseTimestamp, - kUndefined = -1 -}; - -enum Modes { - kModeNormal = 0, - kModeExpand, - kModeMerge, - kModeAccelerateSuccess, - kModeAccelerateLowEnergy, - kModeAccelerateFail, - kModePreemptiveExpandSuccess, - kModePreemptiveExpandLowEnergy, - kModePreemptiveExpandFail, - kModeRfc3389Cng, - kModeCodecInternalCng, - kModeDtmf, - kModeError, - kModeUndefined = -1 -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DEFINES_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_manager.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_manager.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_manager.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_manager.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,425 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/delay_manager.h" - -#include -#include - -#include // max, min - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_coding/neteq4/delay_peak_detector.h" -#include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/system_wrappers/interface/logging.h" - -namespace webrtc { - -DelayManager::DelayManager(int max_packets_in_buffer, - DelayPeakDetector* peak_detector) - : first_packet_received_(false), - max_packets_in_buffer_(max_packets_in_buffer), - iat_vector_(kMaxIat + 1, 0), - iat_factor_(0), - packet_iat_count_ms_(0), - base_target_level_(4), // In Q0 domain. - target_level_(base_target_level_ << 8), // In Q8 domain. - packet_len_ms_(0), - streaming_mode_(false), - last_seq_no_(0), - last_timestamp_(0), - minimum_delay_ms_(0), - least_required_delay_ms_(target_level_), - maximum_delay_ms_(target_level_), - iat_cumulative_sum_(0), - max_iat_cumulative_sum_(0), - max_timer_ms_(0), - peak_detector_(*peak_detector), - last_pack_cng_or_dtmf_(1) { - assert(peak_detector); // Should never be NULL. - Reset(); -} - -DelayManager::~DelayManager() {} - -const DelayManager::IATVector& DelayManager::iat_vector() const { - return iat_vector_; -} - -// Set the histogram vector to an exponentially decaying distribution -// iat_vector_[i] = 0.5^(i+1), i = 0, 1, 2, ... -// iat_vector_ is in Q30. -void DelayManager::ResetHistogram() { - // Set temp_prob to (slightly more than) 1 in Q14. This ensures that the sum - // of iat_vector_ is 1. - uint16_t temp_prob = 0x4002; // 16384 + 2 = 100000000000010 binary. - IATVector::iterator it = iat_vector_.begin(); - for (; it < iat_vector_.end(); it++) { - temp_prob >>= 1; - (*it) = temp_prob << 16; - } - base_target_level_ = 4; - target_level_ = base_target_level_ << 8; -} - -int DelayManager::Update(uint16_t sequence_number, - uint32_t timestamp, - int sample_rate_hz) { - if (sample_rate_hz <= 0) { - return -1; - } - - if (!first_packet_received_) { - // Prepare for next packet arrival. - packet_iat_count_ms_ = 0; - last_seq_no_ = sequence_number; - last_timestamp_ = timestamp; - first_packet_received_ = true; - return 0; - } - - // Try calculating packet length from current and previous timestamps. - int packet_len_ms; - if (!IsNewerTimestamp(timestamp, last_timestamp_) || - !IsNewerSequenceNumber(sequence_number, last_seq_no_)) { - // Wrong timestamp or sequence order; use stored value. - packet_len_ms = packet_len_ms_; - } else { - // Calculate timestamps per packet and derive packet length in ms. - int packet_len_samp = - static_cast(timestamp - last_timestamp_) / - static_cast(sequence_number - last_seq_no_); - packet_len_ms = (1000 * packet_len_samp) / sample_rate_hz; - } - - if (packet_len_ms > 0) { - // Cannot update statistics unless |packet_len_ms| is valid. - // Calculate inter-arrival time (IAT) in integer "packet times" - // (rounding down). This is the value used as index to the histogram - // vector |iat_vector_|. - int iat_packets = packet_iat_count_ms_ / packet_len_ms; - - if (streaming_mode_) { - UpdateCumulativeSums(packet_len_ms, sequence_number); - } - - // Check for discontinuous packet sequence and re-ordering. - if (IsNewerSequenceNumber(sequence_number, last_seq_no_ + 1)) { - // Compensate for gap in the sequence numbers. Reduce IAT with the - // expected extra time due to lost packets, but ensure that the IAT is - // not negative. - iat_packets -= static_cast(sequence_number - last_seq_no_ - 1); - iat_packets = std::max(iat_packets, 0); - } else if (!IsNewerSequenceNumber(sequence_number, last_seq_no_)) { - iat_packets += static_cast(last_seq_no_ + 1 - sequence_number); - } - - // Saturate IAT at maximum value. - const int max_iat = kMaxIat; - iat_packets = std::min(iat_packets, max_iat); - UpdateHistogram(iat_packets); - // Calculate new |target_level_| based on updated statistics. - target_level_ = CalculateTargetLevel(iat_packets); - if (streaming_mode_) { - target_level_ = std::max(target_level_, max_iat_cumulative_sum_); - } - - LimitTargetLevel(); - } // End if (packet_len_ms > 0). - - // Prepare for next packet arrival. - packet_iat_count_ms_ = 0; - last_seq_no_ = sequence_number; - last_timestamp_ = timestamp; - return 0; -} - -void DelayManager::UpdateCumulativeSums(int packet_len_ms, - uint16_t sequence_number) { - // Calculate IAT in Q8, including fractions of a packet (i.e., more - // accurate than |iat_packets|. - int iat_packets_q8 = (packet_iat_count_ms_ << 8) / packet_len_ms; - // Calculate cumulative sum IAT with sequence number compensation. The sum - // is zero if there is no clock-drift. - iat_cumulative_sum_ += (iat_packets_q8 - - (static_cast(sequence_number - last_seq_no_) << 8)); - // Subtract drift term. - iat_cumulative_sum_ -= kCumulativeSumDrift; - // Ensure not negative. - iat_cumulative_sum_ = std::max(iat_cumulative_sum_, 0); - if (iat_cumulative_sum_ > max_iat_cumulative_sum_) { - // Found a new maximum. - max_iat_cumulative_sum_ = iat_cumulative_sum_; - max_timer_ms_ = 0; - } - if (max_timer_ms_ > kMaxStreamingPeakPeriodMs) { - // Too long since the last maximum was observed; decrease max value. - max_iat_cumulative_sum_ -= kCumulativeSumDrift; - } -} - -// Each element in the vector is first multiplied by the forgetting factor -// |iat_factor_|. Then the vector element indicated by |iat_packets| is then -// increased (additive) by 1 - |iat_factor_|. This way, the probability of -// |iat_packets| is slightly increased, while the sum of the histogram remains -// constant (=1). -// Due to inaccuracies in the fixed-point arithmetic, the histogram may no -// longer sum up to 1 (in Q30) after the update. To correct this, a correction -// term is added or subtracted from the first element (or elements) of the -// vector. -// The forgetting factor |iat_factor_| is also updated. When the DelayManager -// is reset, the factor is set to 0 to facilitate rapid convergence in the -// beginning. With each update of the histogram, the factor is increased towards -// the steady-state value |kIatFactor_|. -void DelayManager::UpdateHistogram(size_t iat_packets) { - assert(iat_packets < iat_vector_.size()); - int vector_sum = 0; // Sum up the vector elements as they are processed. - // Multiply each element in |iat_vector_| with |iat_factor_|. - for (IATVector::iterator it = iat_vector_.begin(); - it != iat_vector_.end(); ++it) { - *it = (static_cast(*it) * iat_factor_) >> 15; - vector_sum += *it; - } - - // Increase the probability for the currently observed inter-arrival time - // by 1 - |iat_factor_|. The factor is in Q15, |iat_vector_| in Q30. - // Thus, left-shift 15 steps to obtain result in Q30. - iat_vector_[iat_packets] += (32768 - iat_factor_) << 15; - vector_sum += (32768 - iat_factor_) << 15; // Add to vector sum. - - // |iat_vector_| should sum up to 1 (in Q30), but it may not due to - // fixed-point rounding errors. - vector_sum -= 1 << 30; // Should be zero. Compensate if not. - if (vector_sum != 0) { - // Modify a few values early in |iat_vector_|. - int flip_sign = vector_sum > 0 ? -1 : 1; - IATVector::iterator it = iat_vector_.begin(); - while (it != iat_vector_.end() && abs(vector_sum) > 0) { - // Add/subtract 1/16 of the element, but not more than |vector_sum|. - int correction = flip_sign * std::min(abs(vector_sum), (*it) >> 4); - *it += correction; - vector_sum += correction; - ++it; - } - } - assert(vector_sum == 0); // Verify that the above is correct. - - // Update |iat_factor_| (changes only during the first seconds after a reset). - // The factor converges to |kIatFactor_|. - iat_factor_ += (kIatFactor_ - iat_factor_ + 3) >> 2; -} - -// Enforces upper and lower limits for |target_level_|. The upper limit is -// chosen to be minimum of i) 75% of |max_packets_in_buffer_|, to leave some -// headroom for natural fluctuations around the target, and ii) equivalent of -// |maximum_delay_ms_| in packets. Note that in practice, if no -// |maximum_delay_ms_| is specified, this does not have any impact, since the -// target level is far below the buffer capacity in all reasonable cases. -// The lower limit is equivalent of |minimum_delay_ms_| in packets. We update -// |least_required_level_| while the above limits are applied. -// TODO(hlundin): Move this check to the buffer logistics class. -void DelayManager::LimitTargetLevel() { - least_required_delay_ms_ = (target_level_ * packet_len_ms_) >> 8; - - if (packet_len_ms_ > 0 && minimum_delay_ms_ > 0) { - int minimum_delay_packet_q8 = (minimum_delay_ms_ << 8) / packet_len_ms_; - target_level_ = std::max(target_level_, minimum_delay_packet_q8); - } - - if (maximum_delay_ms_ > 0 && packet_len_ms_ > 0) { - int maximum_delay_packet_q8 = (maximum_delay_ms_ << 8) / packet_len_ms_; - target_level_ = std::min(target_level_, maximum_delay_packet_q8); - } - - // Shift to Q8, then 75%.; - int max_buffer_packets_q8 = (3 * (max_packets_in_buffer_ << 8)) / 4; - target_level_ = std::min(target_level_, max_buffer_packets_q8); - - // Sanity check, at least 1 packet (in Q8). - target_level_ = std::max(target_level_, 1 << 8); -} - -int DelayManager::CalculateTargetLevel(int iat_packets) { - int limit_probability = kLimitProbability; - if (streaming_mode_) { - limit_probability = kLimitProbabilityStreaming; - } - - // Calculate target buffer level from inter-arrival time histogram. - // Find the |iat_index| for which the probability of observing an - // inter-arrival time larger than or equal to |iat_index| is less than or - // equal to |limit_probability|. The sought probability is estimated using - // the histogram as the reverse cumulant PDF, i.e., the sum of elements from - // the end up until |iat_index|. Now, since the sum of all elements is 1 - // (in Q30) by definition, and since the solution is often a low value for - // |iat_index|, it is more efficient to start with |sum| = 1 and subtract - // elements from the start of the histogram. - size_t index = 0; // Start from the beginning of |iat_vector_|. - int sum = 1 << 30; // Assign to 1 in Q30. - sum -= iat_vector_[index]; // Ensure that target level is >= 1. - - do { - // Subtract the probabilities one by one until the sum is no longer greater - // than limit_probability. - ++index; - sum -= iat_vector_[index]; - } while ((sum > limit_probability) && (index < iat_vector_.size() - 1)); - - // This is the base value for the target buffer level. - int target_level = static_cast(index); - base_target_level_ = static_cast(index); - - // Update detector for delay peaks. - bool delay_peak_found = peak_detector_.Update(iat_packets, target_level); - if (delay_peak_found) { - target_level = std::max(target_level, peak_detector_.MaxPeakHeight()); - } - - // Sanity check. |target_level| must be strictly positive. - target_level = std::max(target_level, 1); - // Scale to Q8 and assign to member variable. - target_level_ = target_level << 8; - return target_level_; -} - -int DelayManager::SetPacketAudioLength(int length_ms) { - if (length_ms <= 0) { - LOG_F(LS_ERROR) << "length_ms = " << length_ms; - return -1; - } - packet_len_ms_ = length_ms; - peak_detector_.SetPacketAudioLength(packet_len_ms_); - packet_iat_count_ms_ = 0; - last_pack_cng_or_dtmf_ = 1; // TODO(hlundin): Legacy. Remove? - return 0; -} - - -void DelayManager::Reset() { - packet_len_ms_ = 0; // Packet size unknown. - streaming_mode_ = false; - peak_detector_.Reset(); - ResetHistogram(); // Resets target levels too. - iat_factor_ = 0; // Adapt the histogram faster for the first few packets. - packet_iat_count_ms_ = 0; - max_timer_ms_ = 0; - iat_cumulative_sum_ = 0; - max_iat_cumulative_sum_ = 0; - last_pack_cng_or_dtmf_ = 1; -} - -int DelayManager::AverageIAT() const { - int32_t sum_q24 = 0; - // Using an int for the upper limit of the following for-loop so the - // loop-counter can be int. Otherwise we need a cast where |sum_q24| is - // updated. - const int iat_vec_size = static_cast(iat_vector_.size()); - assert(iat_vector_.size() == 65); // Algorithm is hard-coded for this size. - for (int i = 0; i < iat_vec_size; ++i) { - // Shift 6 to fit worst case: 2^30 * 64. - sum_q24 += (iat_vector_[i] >> 6) * i; - } - // Subtract the nominal inter-arrival time 1 = 2^24 in Q24. - sum_q24 -= (1 << 24); - // Multiply with 1000000 / 2^24 = 15625 / 2^18 to get in parts-per-million. - // Shift 7 to Q17 first, then multiply with 15625 and shift another 11. - return ((sum_q24 >> 7) * 15625) >> 11; -} - -bool DelayManager::PeakFound() const { - return peak_detector_.peak_found(); -} - -void DelayManager::UpdateCounters(int elapsed_time_ms) { - packet_iat_count_ms_ += elapsed_time_ms; - peak_detector_.IncrementCounter(elapsed_time_ms); - max_timer_ms_ += elapsed_time_ms; -} - -void DelayManager::ResetPacketIatCount() { packet_iat_count_ms_ = 0; } - -// Note that |low_limit| and |higher_limit| are not assigned to -// |minimum_delay_ms_| and |maximum_delay_ms_| defined by the client of this -// class. They are computed from |target_level_| and used for decision making. -void DelayManager::BufferLimits(int* lower_limit, int* higher_limit) const { - if (!lower_limit || !higher_limit) { - LOG_F(LS_ERROR) << "NULL pointers supplied as input"; - assert(false); - return; - } - - int window_20ms = 0x7FFF; // Default large value for legacy bit-exactness. - if (packet_len_ms_ > 0) { - window_20ms = (20 << 8) / packet_len_ms_; - } - - // |target_level_| is in Q8 already. - *lower_limit = (target_level_ * 3) / 4; - // |higher_limit| is equal to |target_level_|, but should at - // least be 20 ms higher than |lower_limit_|. - *higher_limit = std::max(target_level_, *lower_limit + window_20ms); -} - -int DelayManager::TargetLevel() const { - return target_level_; -} - -void DelayManager::LastDecoderType(NetEqDecoder decoder_type) { - if (decoder_type == kDecoderAVT || - decoder_type == kDecoderCNGnb || - decoder_type == kDecoderCNGwb || - decoder_type == kDecoderCNGswb32kHz || - decoder_type == kDecoderCNGswb48kHz) { - last_pack_cng_or_dtmf_ = 1; - } else if (last_pack_cng_or_dtmf_ != 0) { - last_pack_cng_or_dtmf_ = -1; - } -} - -bool DelayManager::SetMinimumDelay(int delay_ms) { - // Minimum delay shouldn't be more than maximum delay, if any maximum is set. - // Also, if possible check |delay| to less than 75% of - // |max_packets_in_buffer_|. - if ((maximum_delay_ms_ > 0 && delay_ms > maximum_delay_ms_) || - (packet_len_ms_ > 0 && - delay_ms > 3 * max_packets_in_buffer_ * packet_len_ms_ / 4)) { - return false; - } - minimum_delay_ms_ = delay_ms; - return true; -} - -bool DelayManager::SetMaximumDelay(int delay_ms) { - if (delay_ms == 0) { - // Zero input unsets the maximum delay. - maximum_delay_ms_ = 0; - return true; - } else if (delay_ms < minimum_delay_ms_ || delay_ms < packet_len_ms_) { - // Maximum delay shouldn't be less than minimum delay or less than a packet. - return false; - } - maximum_delay_ms_ = delay_ms; - return true; -} - -int DelayManager::least_required_delay_ms() const { - return least_required_delay_ms_; -} - -int DelayManager::base_target_level() const { return base_target_level_; } -void DelayManager::set_streaming_mode(bool value) { streaming_mode_ = value; } -int DelayManager::last_pack_cng_or_dtmf() const { - return last_pack_cng_or_dtmf_; -} - -void DelayManager::set_last_pack_cng_or_dtmf(int value) { - last_pack_cng_or_dtmf_ = value; -} -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_manager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_manager.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_manager.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_manager.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DELAY_MANAGER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DELAY_MANAGER_H_ - -#include // Provide access to size_t. - -#include - -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Forward declaration. -class DelayPeakDetector; - -class DelayManager { - public: - typedef std::vector IATVector; - - // Create a DelayManager object. Notify the delay manager that the packet - // buffer can hold no more than |max_packets_in_buffer| packets (i.e., this - // is the number of packet slots in the buffer). Supply a PeakDetector - // object to the DelayManager. - DelayManager(int max_packets_in_buffer, DelayPeakDetector* peak_detector); - - virtual ~DelayManager(); - - // Read the inter-arrival time histogram. Mainly for testing purposes. - virtual const IATVector& iat_vector() const; - - // Updates the delay manager with a new incoming packet, with - // |sequence_number| and |timestamp| from the RTP header. This updates the - // inter-arrival time histogram and other statistics, as well as the - // associated DelayPeakDetector. A new target buffer level is calculated. - // Returns 0 on success, -1 on failure (invalid sample rate). - virtual int Update(uint16_t sequence_number, - uint32_t timestamp, - int sample_rate_hz); - - // Calculates a new target buffer level. Called from the Update() method. - // Sets target_level_ (in Q8) and returns the same value. Also calculates - // and updates base_target_level_, which is the target buffer level before - // taking delay peaks into account. - virtual int CalculateTargetLevel(int iat_packets); - - // Notifies the DelayManager of how much audio data is carried in each packet. - // The method updates the DelayPeakDetector too, and resets the inter-arrival - // time counter. Returns 0 on success, -1 on failure. - virtual int SetPacketAudioLength(int length_ms); - - // Resets the DelayManager and the associated DelayPeakDetector. - virtual void Reset(); - - // Calculates the average inter-arrival time deviation from the histogram. - // The result is returned as parts-per-million deviation from the nominal - // inter-arrival time. That is, if the average inter-arrival time is equal to - // the nominal frame time, the return value is zero. A positive value - // corresponds to packet spacing being too large, while a negative value means - // that the packets arrive with less spacing than expected. - virtual int AverageIAT() const; - - // Returns true if peak-mode is active. That is, delay peaks were observed - // recently. This method simply asks for the same information from the - // DelayPeakDetector object. - virtual bool PeakFound() const; - - // Notifies the counters in DelayManager and DelayPeakDetector that - // |elapsed_time_ms| have elapsed. - virtual void UpdateCounters(int elapsed_time_ms); - - // Reset the inter-arrival time counter to 0. - virtual void ResetPacketIatCount(); - - // Writes the lower and higher limits which the buffer level should stay - // within to the corresponding pointers. The values are in (fractions of) - // packets in Q8. - virtual void BufferLimits(int* lower_limit, int* higher_limit) const; - - // Gets the target buffer level, in (fractions of) packets in Q8. This value - // includes any extra delay set through the set_extra_delay_ms() method. - virtual int TargetLevel() const; - - virtual void LastDecoderType(NetEqDecoder decoder_type); - - // Accessors and mutators. - // Assuming |delay| is in valid range. - virtual bool SetMinimumDelay(int delay_ms); - virtual bool SetMaximumDelay(int delay_ms); - virtual int least_required_delay_ms() const; - virtual int base_target_level() const; - virtual void set_streaming_mode(bool value); - virtual int last_pack_cng_or_dtmf() const; - virtual void set_last_pack_cng_or_dtmf(int value); - - private: - static const int kLimitProbability = 53687091; // 1/20 in Q30. - static const int kLimitProbabilityStreaming = 536871; // 1/2000 in Q30. - static const int kMaxStreamingPeakPeriodMs = 600000; // 10 minutes in ms. - static const int kCumulativeSumDrift = 2; // Drift term for cumulative sum - // |iat_cumulative_sum_|. - // Steady-state forgetting factor for |iat_vector_|, 0.9993 in Q15. - static const int kIatFactor_ = 32745; - static const int kMaxIat = 64; // Max inter-arrival time to register. - - // Sets |iat_vector_| to the default start distribution and sets the - // |base_target_level_| and |target_level_| to the corresponding values. - void ResetHistogram(); - - // Updates |iat_cumulative_sum_| and |max_iat_cumulative_sum_|. (These are - // used by the streaming mode.) This method is called by Update(). - void UpdateCumulativeSums(int packet_len_ms, uint16_t sequence_number); - - // Updates the histogram |iat_vector_|. The probability for inter-arrival time - // equal to |iat_packets| (in integer packets) is increased slightly, while - // all other entries are decreased. This method is called by Update(). - void UpdateHistogram(size_t iat_packets); - - // Makes sure that |target_level_| is not too large, taking - // |max_packets_in_buffer_| and |extra_delay_ms_| into account. This method is - // called by Update(). - void LimitTargetLevel(); - - bool first_packet_received_; - const int max_packets_in_buffer_; // Capacity of the packet buffer. - IATVector iat_vector_; // Histogram of inter-arrival times. - int iat_factor_; // Forgetting factor for updating the IAT histogram (Q15). - int packet_iat_count_ms_; // Milliseconds elapsed since last packet. - int base_target_level_; // Currently preferred buffer level before peak - // detection and streaming mode (Q0). - // TODO(turajs) change the comment according to the implementation of - // minimum-delay. - int target_level_; // Currently preferred buffer level in (fractions) - // of packets (Q8), before adding any extra delay. - int packet_len_ms_; // Length of audio in each incoming packet [ms]. - bool streaming_mode_; - uint16_t last_seq_no_; // Sequence number for last received packet. - uint32_t last_timestamp_; // Timestamp for the last received packet. - int minimum_delay_ms_; // Externally set minimum delay. - int least_required_delay_ms_; // Smallest preferred buffer level (same unit - // as |target_level_|), before applying - // |minimum_delay_ms_| and/or |maximum_delay_ms_|. - int maximum_delay_ms_; // Externally set maximum allowed delay. - int iat_cumulative_sum_; // Cumulative sum of delta inter-arrival times. - int max_iat_cumulative_sum_; // Max of |iat_cumulative_sum_|. - int max_timer_ms_; // Time elapsed since maximum was observed. - DelayPeakDetector& peak_detector_; - int last_pack_cng_or_dtmf_; - - DISALLOW_COPY_AND_ASSIGN(DelayManager); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DELAY_MANAGER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_manager_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_manager_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_manager_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_manager_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,297 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Unit tests for DelayManager class. - -#include "webrtc/modules/audio_coding/neteq4/delay_manager.h" - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq4/mock/mock_delay_peak_detector.h" - -namespace webrtc { - -using ::testing::Return; -using ::testing::_; - -class DelayManagerTest : public ::testing::Test { - protected: - static const int kMaxNumberOfPackets = 240; - static const int kTimeStepMs = 10; - static const int kFs = 8000; - static const int kFrameSizeMs = 20; - static const int kTsIncrement = kFrameSizeMs * kFs / 1000; - - DelayManagerTest(); - virtual void SetUp(); - virtual void TearDown(); - void SetPacketAudioLength(int lengt_ms); - void InsertNextPacket(); - void IncreaseTime(int inc_ms); - - DelayManager* dm_; - MockDelayPeakDetector detector_; - uint16_t seq_no_; - uint32_t ts_; -}; - -DelayManagerTest::DelayManagerTest() - : dm_(NULL), - seq_no_(0x1234), - ts_(0x12345678) { -} - -void DelayManagerTest::SetUp() { - EXPECT_CALL(detector_, Reset()) - .Times(1); - dm_ = new DelayManager(kMaxNumberOfPackets, &detector_); -} - -void DelayManagerTest::SetPacketAudioLength(int lengt_ms) { - EXPECT_CALL(detector_, SetPacketAudioLength(lengt_ms)); - dm_->SetPacketAudioLength(lengt_ms); -} - -void DelayManagerTest::InsertNextPacket() { - EXPECT_EQ(0, dm_->Update(seq_no_, ts_, kFs)); - seq_no_ += 1; - ts_ += kTsIncrement; -} - -void DelayManagerTest::IncreaseTime(int inc_ms) { - for (int t = 0; t < inc_ms; t += kTimeStepMs) { - EXPECT_CALL(detector_, IncrementCounter(kTimeStepMs)) - .Times(1); - dm_->UpdateCounters(kTimeStepMs); - } -} -void DelayManagerTest::TearDown() { - EXPECT_CALL(detector_, Die()); - delete dm_; -} - -TEST_F(DelayManagerTest, CreateAndDestroy) { - // Nothing to do here. The test fixture creates and destroys the DelayManager - // object. -} - -TEST_F(DelayManagerTest, VectorInitialization) { - const DelayManager::IATVector& vec = dm_->iat_vector(); - double sum = 0.0; - for (size_t i = 0; i < vec.size(); i++) { - EXPECT_NEAR(ldexp(pow(0.5, static_cast(i + 1)), 30), vec[i], 65536); - // Tolerance 65536 in Q30 corresponds to a delta of approximately 0.00006. - sum += vec[i]; - } - EXPECT_EQ(1 << 30, static_cast(sum)); // Should be 1 in Q30. -} - -TEST_F(DelayManagerTest, SetPacketAudioLength) { - const int kLengthMs = 30; - // Expect DelayManager to pass on the new length to the detector object. - EXPECT_CALL(detector_, SetPacketAudioLength(kLengthMs)) - .Times(1); - EXPECT_EQ(0, dm_->SetPacketAudioLength(kLengthMs)); - EXPECT_EQ(-1, dm_->SetPacketAudioLength(-1)); // Illegal parameter value. -} - -TEST_F(DelayManagerTest, PeakFound) { - // Expect DelayManager to pass on the question to the detector. - // Call twice, and let the detector return true the first time and false the - // second time. - EXPECT_CALL(detector_, peak_found()) - .WillOnce(Return(true)) - .WillOnce(Return(false)); - EXPECT_TRUE(dm_->PeakFound()); - EXPECT_FALSE(dm_->PeakFound()); -} - -TEST_F(DelayManagerTest, UpdateCounters) { - // Expect DelayManager to pass on the counter update to the detector. - EXPECT_CALL(detector_, IncrementCounter(kTimeStepMs)) - .Times(1); - dm_->UpdateCounters(kTimeStepMs); -} - -TEST_F(DelayManagerTest, UpdateNormal) { - SetPacketAudioLength(kFrameSizeMs); - // First packet arrival. - InsertNextPacket(); - // Advance time by one frame size. - IncreaseTime(kFrameSizeMs); - // Second packet arrival. - // Expect detector update method to be called once with inter-arrival time - // equal to 1 packet, and (base) target level equal to 1 as well. - // Return false to indicate no peaks found. - EXPECT_CALL(detector_, Update(1, 1)) - .WillOnce(Return(false)); - InsertNextPacket(); - EXPECT_EQ(1 << 8, dm_->TargetLevel()); // In Q8. - EXPECT_EQ(1, dm_->base_target_level()); - int lower, higher; - dm_->BufferLimits(&lower, &higher); - // Expect |lower| to be 75% of target level, and |higher| to be target level, - // but also at least 20 ms higher than |lower|, which is the limiting case - // here. - EXPECT_EQ((1 << 8) * 3 / 4, lower); - EXPECT_EQ(lower + (20 << 8) / kFrameSizeMs, higher); -} - -TEST_F(DelayManagerTest, UpdateLongInterArrivalTime) { - SetPacketAudioLength(kFrameSizeMs); - // First packet arrival. - InsertNextPacket(); - // Advance time by two frame size. - IncreaseTime(2 * kFrameSizeMs); - // Second packet arrival. - // Expect detector update method to be called once with inter-arrival time - // equal to 1 packet, and (base) target level equal to 1 as well. - // Return false to indicate no peaks found. - EXPECT_CALL(detector_, Update(2, 2)) - .WillOnce(Return(false)); - InsertNextPacket(); - EXPECT_EQ(2 << 8, dm_->TargetLevel()); // In Q8. - EXPECT_EQ(2, dm_->base_target_level()); - int lower, higher; - dm_->BufferLimits(&lower, &higher); - // Expect |lower| to be 75% of target level, and |higher| to be target level, - // but also at least 20 ms higher than |lower|, which is the limiting case - // here. - EXPECT_EQ((2 << 8) * 3 / 4, lower); - EXPECT_EQ(lower + (20 << 8) / kFrameSizeMs, higher); -} - -TEST_F(DelayManagerTest, UpdatePeakFound) { - SetPacketAudioLength(kFrameSizeMs); - // First packet arrival. - InsertNextPacket(); - // Advance time by one frame size. - IncreaseTime(kFrameSizeMs); - // Second packet arrival. - // Expect detector update method to be called once with inter-arrival time - // equal to 1 packet, and (base) target level equal to 1 as well. - // Return true to indicate that peaks are found. Let the peak height be 5. - EXPECT_CALL(detector_, Update(1, 1)) - .WillOnce(Return(true)); - EXPECT_CALL(detector_, MaxPeakHeight()) - .WillOnce(Return(5)); - InsertNextPacket(); - EXPECT_EQ(5 << 8, dm_->TargetLevel()); - EXPECT_EQ(1, dm_->base_target_level()); // Base target level is w/o peaks. - int lower, higher; - dm_->BufferLimits(&lower, &higher); - // Expect |lower| to be 75% of target level, and |higher| to be target level. - EXPECT_EQ((5 << 8) * 3 / 4, lower); - EXPECT_EQ(5 << 8, higher); -} - -TEST_F(DelayManagerTest, TargetDelay) { - SetPacketAudioLength(kFrameSizeMs); - // First packet arrival. - InsertNextPacket(); - // Advance time by one frame size. - IncreaseTime(kFrameSizeMs); - // Second packet arrival. - // Expect detector update method to be called once with inter-arrival time - // equal to 1 packet, and (base) target level equal to 1 as well. - // Return false to indicate no peaks found. - EXPECT_CALL(detector_, Update(1, 1)) - .WillOnce(Return(false)); - InsertNextPacket(); - const int kExpectedTarget = 1; - EXPECT_EQ(kExpectedTarget << 8, dm_->TargetLevel()); // In Q8. - EXPECT_EQ(1, dm_->base_target_level()); - int lower, higher; - dm_->BufferLimits(&lower, &higher); - // Expect |lower| to be 75% of base target level, and |higher| to be - // lower + 20 ms headroom. - EXPECT_EQ((1 << 8) * 3 / 4, lower); - EXPECT_EQ(lower + (20 << 8) / kFrameSizeMs, higher); -} - -TEST_F(DelayManagerTest, MaxAndRequiredDelay) { - const int kExpectedTarget = 5; - const int kTimeIncrement = kExpectedTarget * kFrameSizeMs; - SetPacketAudioLength(kFrameSizeMs); - // First packet arrival. - InsertNextPacket(); - // Second packet arrival. - // Expect detector update method to be called once with inter-arrival time - // equal to |kExpectedTarget| packet. Return true to indicate peaks found. - EXPECT_CALL(detector_, Update(kExpectedTarget, _)) - .WillRepeatedly(Return(true)); - EXPECT_CALL(detector_, MaxPeakHeight()) - .WillRepeatedly(Return(kExpectedTarget)); - IncreaseTime(kTimeIncrement); - InsertNextPacket(); - - // No limit is set. - EXPECT_EQ(kExpectedTarget << 8, dm_->TargetLevel()); - - int kMaxDelayPackets = kExpectedTarget - 2; - int kMaxDelayMs = kMaxDelayPackets * kFrameSizeMs; - EXPECT_TRUE(dm_->SetMaximumDelay(kMaxDelayMs)); - IncreaseTime(kTimeIncrement); - InsertNextPacket(); - EXPECT_EQ(kExpectedTarget * kFrameSizeMs, dm_->least_required_delay_ms()); - EXPECT_EQ(kMaxDelayPackets << 8, dm_->TargetLevel()); - - // Target level at least should be one packet. - EXPECT_FALSE(dm_->SetMaximumDelay(kFrameSizeMs - 1)); -} - -TEST_F(DelayManagerTest, MinAndRequiredDelay) { - const int kExpectedTarget = 5; - const int kTimeIncrement = kExpectedTarget * kFrameSizeMs; - SetPacketAudioLength(kFrameSizeMs); - // First packet arrival. - InsertNextPacket(); - // Second packet arrival. - // Expect detector update method to be called once with inter-arrival time - // equal to |kExpectedTarget| packet. Return true to indicate peaks found. - EXPECT_CALL(detector_, Update(kExpectedTarget, _)) - .WillRepeatedly(Return(true)); - EXPECT_CALL(detector_, MaxPeakHeight()) - .WillRepeatedly(Return(kExpectedTarget)); - IncreaseTime(kTimeIncrement); - InsertNextPacket(); - - // No limit is applied. - EXPECT_EQ(kExpectedTarget << 8, dm_->TargetLevel()); - - int kMinDelayPackets = kExpectedTarget + 2; - int kMinDelayMs = kMinDelayPackets * kFrameSizeMs; - dm_->SetMinimumDelay(kMinDelayMs); - IncreaseTime(kTimeIncrement); - InsertNextPacket(); - EXPECT_EQ(kExpectedTarget * kFrameSizeMs, dm_->least_required_delay_ms()); - EXPECT_EQ(kMinDelayPackets << 8, dm_->TargetLevel()); -} - -TEST_F(DelayManagerTest, Failures) { - // Wrong sample rate. - EXPECT_EQ(-1, dm_->Update(0, 0, -1)); - // Wrong packet size. - EXPECT_EQ(-1, dm_->SetPacketAudioLength(0)); - EXPECT_EQ(-1, dm_->SetPacketAudioLength(-1)); - - // Minimum delay higher than a maximum delay is not accepted. - EXPECT_TRUE(dm_->SetMaximumDelay(10)); - EXPECT_FALSE(dm_->SetMinimumDelay(20)); - - // Maximum delay less than minimum delay is not accepted. - EXPECT_TRUE(dm_->SetMaximumDelay(100)); - EXPECT_TRUE(dm_->SetMinimumDelay(80)); - EXPECT_FALSE(dm_->SetMaximumDelay(60)); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_peak_detector.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_peak_detector.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_peak_detector.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_peak_detector.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/delay_peak_detector.h" - -#include // max - -namespace webrtc { - -// The DelayPeakDetector keeps track of severe inter-arrival times, called -// delay peaks. When a peak is observed, the "height" (the time elapsed since -// the previous packet arrival) and the peak "period" (the time since the last -// observed peak) is recorded in a vector. When enough peaks have been observed, -// peak-mode is engaged and the DelayManager asks the DelayPeakDetector for -// the worst peak height. - -DelayPeakDetector::DelayPeakDetector() - : peak_found_(false), - peak_detection_threshold_(0), - peak_period_counter_ms_(-1) { -} - -void DelayPeakDetector::Reset() { - peak_period_counter_ms_ = -1; // Indicate that next peak is the first. - peak_found_ = false; - peak_history_.clear(); -} - -// Calculates the threshold in number of packets. -void DelayPeakDetector::SetPacketAudioLength(int length_ms) { - if (length_ms > 0) { - peak_detection_threshold_ = kPeakHeightMs / length_ms; - } -} - -int DelayPeakDetector::MaxPeakHeight() const { - int max_height = -1; // Returns -1 for an empty history. - std::list::const_iterator it; - for (it = peak_history_.begin(); it != peak_history_.end(); ++it) { - max_height = std::max(max_height, it->peak_height_packets); - } - return max_height; -} - -int DelayPeakDetector::MaxPeakPeriod() const { - int max_period = -1; // Returns -1 for an empty history. - std::list::const_iterator it; - for (it = peak_history_.begin(); it != peak_history_.end(); ++it) { - max_period = std::max(max_period, it->period_ms); - } - return max_period; -} - -bool DelayPeakDetector::Update(int inter_arrival_time, int target_level) { - if (inter_arrival_time > target_level + peak_detection_threshold_ || - inter_arrival_time > 2 * target_level) { - // A delay peak is observed. - if (peak_period_counter_ms_ == -1) { - // This is the first peak. Reset the period counter. - peak_period_counter_ms_ = 0; - } else if (peak_period_counter_ms_ <= kMaxPeakPeriodMs) { - // This is not the first peak, and the period is valid. - // Store peak data in the vector. - Peak peak_data; - peak_data.period_ms = peak_period_counter_ms_; - peak_data.peak_height_packets = inter_arrival_time; - peak_history_.push_back(peak_data); - while (peak_history_.size() > kMaxNumPeaks) { - // Delete the oldest data point. - peak_history_.pop_front(); - } - peak_period_counter_ms_ = 0; - } else if (peak_period_counter_ms_ <= 2 * kMaxPeakPeriodMs) { - // Invalid peak due to too long period. Reset period counter and start - // looking for next peak. - peak_period_counter_ms_ = 0; - } else { - // More than 2 times the maximum period has elapsed since the last peak - // was registered. It seams that the network conditions have changed. - // Reset the peak statistics. - Reset(); - } - } - return CheckPeakConditions(); -} - -void DelayPeakDetector::IncrementCounter(int inc_ms) { - if (peak_period_counter_ms_ >= 0) { - peak_period_counter_ms_ += inc_ms; - } -} - -bool DelayPeakDetector::CheckPeakConditions() { - size_t s = peak_history_.size(); - if (s >= kMinPeaksToTrigger && - peak_period_counter_ms_ <= 2 * MaxPeakPeriod()) { - peak_found_ = true; - } else { - peak_found_ = false; - } - return peak_found_; -} -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_peak_detector.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_peak_detector.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_peak_detector.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_peak_detector.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DELAY_PEAK_DETECTOR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DELAY_PEAK_DETECTOR_H_ - -#include // size_t - -#include - -#include "webrtc/system_wrappers/interface/constructor_magic.h" - -namespace webrtc { - -class DelayPeakDetector { - public: - DelayPeakDetector(); - virtual ~DelayPeakDetector() {} - virtual void Reset(); - - // Notifies the DelayPeakDetector of how much audio data is carried in each - // packet. - virtual void SetPacketAudioLength(int length_ms); - - // Returns true if peak-mode is active. That is, delay peaks were observed - // recently. - virtual bool peak_found() { return peak_found_; } - - // Calculates and returns the maximum delay peak height. Returns -1 if no - // delay peaks have been observed recently. The unit is number of packets. - virtual int MaxPeakHeight() const; - - // Calculates and returns the maximum delay peak distance in ms. - // Returns -1 if no delay peaks have been observed recently. - virtual int MaxPeakPeriod() const; - - // Updates the DelayPeakDetector with a new inter-arrival time (in packets) - // and the current target buffer level (needed to decide if a peak is observed - // or not). Returns true if peak-mode is active, false if not. - virtual bool Update(int inter_arrival_time, int target_level); - - // Increments the |peak_period_counter_ms_| with |inc_ms|. Only increments - // the counter if it is non-negative. A negative denotes that no peak has - // been observed. - virtual void IncrementCounter(int inc_ms); - - private: - static const size_t kMaxNumPeaks = 8; - static const size_t kMinPeaksToTrigger = 2; - static const int kPeakHeightMs = 78; - static const int kMaxPeakPeriodMs = 10000; - - typedef struct { - int period_ms; - int peak_height_packets; - } Peak; - - bool CheckPeakConditions(); - - std::list peak_history_; - bool peak_found_; - int peak_detection_threshold_; - int peak_period_counter_ms_; - - DISALLOW_COPY_AND_ASSIGN(DelayPeakDetector); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DELAY_PEAK_DETECTOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_peak_detector_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_peak_detector_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_peak_detector_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/delay_peak_detector_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Unit tests for DelayPeakDetector class. - -#include "webrtc/modules/audio_coding/neteq4/delay_peak_detector.h" - -#include "gtest/gtest.h" - -namespace webrtc { - -TEST(DelayPeakDetector, CreateAndDestroy) { - DelayPeakDetector* detector = new DelayPeakDetector(); - EXPECT_FALSE(detector->peak_found()); - delete detector; -} - -TEST(DelayPeakDetector, EmptyHistory) { - DelayPeakDetector detector; - EXPECT_EQ(-1, detector.MaxPeakHeight()); - EXPECT_EQ(-1, detector.MaxPeakPeriod()); -} - -// Inject a series of packet arrivals into the detector. Three of the packets -// have suffered delays. After the third delay peak, peak-mode is expected to -// start. This should then continue until it is disengaged due to lack of peaks. -TEST(DelayPeakDetector, TriggerPeakMode) { - DelayPeakDetector detector; - const int kPacketSizeMs = 30; - detector.SetPacketAudioLength(kPacketSizeMs); - - // Load up normal arrival times; 0 ms, 30 ms, 60 ms, 90 ms, ... - const int kNumPackets = 1000; - int arrival_times_ms[kNumPackets]; - for (int i = 0; i < kNumPackets; ++i) { - arrival_times_ms[i] = i * kPacketSizeMs; - } - - // Delay three packets. - const int kPeakDelayMs = 100; - // First delay peak. - arrival_times_ms[100] += kPeakDelayMs; - // Second delay peak. - arrival_times_ms[200] += kPeakDelayMs; - // Third delay peak. Trigger peak-mode after this packet. - arrival_times_ms[400] += kPeakDelayMs; - // The second peak period is the longest, 200 packets. - const int kWorstPeakPeriod = 200 * kPacketSizeMs; - int peak_mode_start_ms = arrival_times_ms[400]; - // Expect to disengage after no peaks are observed for two period times. - int peak_mode_end_ms = peak_mode_start_ms + 2 * kWorstPeakPeriod; - - // Load into detector. - int time = 0; - int next = 1; // Start with the second packet to get a proper IAT. - while (next < kNumPackets) { - while (next < kNumPackets && arrival_times_ms[next] <= time) { - int iat_packets = (arrival_times_ms[next] - arrival_times_ms[next - 1]) / - kPacketSizeMs; - const int kTargetBufferLevel = 1; // Define peaks to be iat > 2. - if (time < peak_mode_start_ms || time > peak_mode_end_ms) { - EXPECT_FALSE(detector.Update(iat_packets, kTargetBufferLevel)); - } else { - EXPECT_TRUE(detector.Update(iat_packets, kTargetBufferLevel)); - EXPECT_EQ(kWorstPeakPeriod, detector.MaxPeakPeriod()); - EXPECT_EQ(kPeakDelayMs / kPacketSizeMs + 1, detector.MaxPeakHeight()); - } - ++next; - } - detector.IncrementCounter(10); - time += 10; // Increase time 10 ms. - } -} - -// Same test as TriggerPeakMode, but with base target buffer level increased to -// 2, in order to raise the bar for delay peaks to inter-arrival times > 4. -// The delay pattern has peaks with delay = 3, thus should not trigger. -TEST(DelayPeakDetector, DoNotTriggerPeakMode) { - DelayPeakDetector detector; - const int kPacketSizeMs = 30; - detector.SetPacketAudioLength(kPacketSizeMs); - - // Load up normal arrival times; 0 ms, 30 ms, 60 ms, 90 ms, ... - const int kNumPackets = 1000; - int arrival_times_ms[kNumPackets]; - for (int i = 0; i < kNumPackets; ++i) { - arrival_times_ms[i] = i * kPacketSizeMs; - } - - // Delay three packets. - const int kPeakDelayMs = 100; - // First delay peak. - arrival_times_ms[100] += kPeakDelayMs; - // Second delay peak. - arrival_times_ms[200] += kPeakDelayMs; - // Third delay peak. - arrival_times_ms[400] += kPeakDelayMs; - - // Load into detector. - int time = 0; - int next = 1; // Start with the second packet to get a proper IAT. - while (next < kNumPackets) { - while (next < kNumPackets && arrival_times_ms[next] <= time) { - int iat_packets = (arrival_times_ms[next] - arrival_times_ms[next - 1]) / - kPacketSizeMs; - const int kTargetBufferLevel = 2; // Define peaks to be iat > 4. - EXPECT_FALSE(detector.Update(iat_packets, kTargetBufferLevel)); - ++next; - } - detector.IncrementCounter(10); - time += 10; // Increase time 10 ms. - } -} -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dsp_helper.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dsp_helper.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dsp_helper.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dsp_helper.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,353 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/dsp_helper.h" - -#include -#include // Access to memset. - -#include // Access to min, max. - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" - -namespace webrtc { - -// Table of constants used in method DspHelper::ParabolicFit(). -const int16_t DspHelper::kParabolaCoefficients[17][3] = { - { 120, 32, 64 }, - { 140, 44, 75 }, - { 150, 50, 80 }, - { 160, 57, 85 }, - { 180, 72, 96 }, - { 200, 89, 107 }, - { 210, 98, 112 }, - { 220, 108, 117 }, - { 240, 128, 128 }, - { 260, 150, 139 }, - { 270, 162, 144 }, - { 280, 174, 149 }, - { 300, 200, 160 }, - { 320, 228, 171 }, - { 330, 242, 176 }, - { 340, 257, 181 }, - { 360, 288, 192 } }; - -// Filter coefficients used when downsampling from the indicated sample rates -// (8, 16, 32, 48 kHz) to 4 kHz. Coefficients are in Q12. The corresponding Q0 -// values are provided in the comments before each array. - -// Q0 values: {0.3, 0.4, 0.3}. -const int16_t DspHelper::kDownsample8kHzTbl[3] = { 1229, 1638, 1229 }; - -// Q0 values: {0.15, 0.2, 0.3, 0.2, 0.15}. -const int16_t DspHelper::kDownsample16kHzTbl[5] = { 614, 819, 1229, 819, 614 }; - -// Q0 values: {0.1425, 0.1251, 0.1525, 0.1628, 0.1525, 0.1251, 0.1425}. -const int16_t DspHelper::kDownsample32kHzTbl[7] = { - 584, 512, 625, 667, 625, 512, 584 }; - -// Q0 values: {0.2487, 0.0952, 0.1042, 0.1074, 0.1042, 0.0952, 0.2487}. -const int16_t DspHelper::kDownsample48kHzTbl[7] = { - 1019, 390, 427, 440, 427, 390, 1019 }; - -int DspHelper::RampSignal(const int16_t* input, - size_t length, - int factor, - int increment, - int16_t* output) { - int factor_q20 = (factor << 6) + 32; - // TODO(hlundin): Add 32 to factor_q20 when converting back to Q14? - for (size_t i = 0; i < length; ++i) { - output[i] = (factor * input[i] + 8192) >> 14; - factor_q20 += increment; - factor_q20 = std::max(factor_q20, 0); // Never go negative. - factor = std::min(factor_q20 >> 6, 16384); - } - return factor; -} - -int DspHelper::RampSignal(int16_t* signal, - size_t length, - int factor, - int increment) { - return RampSignal(signal, length, factor, increment, signal); -} - -int DspHelper::RampSignal(AudioMultiVector* signal, - size_t start_index, - size_t length, - int factor, - int increment) { - assert(start_index + length <= signal->Size()); - if (start_index + length > signal->Size()) { - // Wrong parameters. Do nothing and return the scale factor unaltered. - return factor; - } - int end_factor = 0; - // Loop over the channels, starting at the same |factor| each time. - for (size_t channel = 0; channel < signal->Channels(); ++channel) { - end_factor = - RampSignal(&(*signal)[channel][start_index], length, factor, increment); - } - return end_factor; -} - -void DspHelper::PeakDetection(int16_t* data, int data_length, - int num_peaks, int fs_mult, - int* peak_index, int16_t* peak_value) { - int16_t min_index = 0; - int16_t max_index = 0; - - for (int i = 0; i <= num_peaks - 1; i++) { - if (num_peaks == 1) { - // Single peak. The parabola fit assumes that an extra point is - // available; worst case it gets a zero on the high end of the signal. - // TODO(hlundin): This can potentially get much worse. It breaks the - // API contract, that the length of |data| is |data_length|. - data_length++; - } - - peak_index[i] = WebRtcSpl_MaxIndexW16(data, data_length - 1); - - if (i != num_peaks - 1) { - min_index = std::max(0, peak_index[i] - 2); - max_index = std::min(data_length - 1, peak_index[i] + 2); - } - - if ((peak_index[i] != 0) && (peak_index[i] != (data_length - 2))) { - ParabolicFit(&data[peak_index[i] - 1], fs_mult, &peak_index[i], - &peak_value[i]); - } else { - if (peak_index[i] == data_length - 2) { - if (data[peak_index[i]] > data[peak_index[i] + 1]) { - ParabolicFit(&data[peak_index[i] - 1], fs_mult, &peak_index[i], - &peak_value[i]); - } else if (data[peak_index[i]] <= data[peak_index[i] + 1]) { - // Linear approximation. - peak_value[i] = (data[peak_index[i]] + data[peak_index[i] + 1]) >> 1; - peak_index[i] = (peak_index[i] * 2 + 1) * fs_mult; - } - } else { - peak_value[i] = data[peak_index[i]]; - peak_index[i] = peak_index[i] * 2 * fs_mult; - } - } - - if (i != num_peaks - 1) { - memset(&data[min_index], 0, - sizeof(data[0]) * (max_index - min_index + 1)); - } - } -} - -void DspHelper::ParabolicFit(int16_t* signal_points, int fs_mult, - int* peak_index, int16_t* peak_value) { - uint16_t fit_index[13]; - if (fs_mult == 1) { - fit_index[0] = 0; - fit_index[1] = 8; - fit_index[2] = 16; - } else if (fs_mult == 2) { - fit_index[0] = 0; - fit_index[1] = 4; - fit_index[2] = 8; - fit_index[3] = 12; - fit_index[4] = 16; - } else if (fs_mult == 4) { - fit_index[0] = 0; - fit_index[1] = 2; - fit_index[2] = 4; - fit_index[3] = 6; - fit_index[4] = 8; - fit_index[5] = 10; - fit_index[6] = 12; - fit_index[7] = 14; - fit_index[8] = 16; - } else { - fit_index[0] = 0; - fit_index[1] = 1; - fit_index[2] = 3; - fit_index[3] = 4; - fit_index[4] = 5; - fit_index[5] = 7; - fit_index[6] = 8; - fit_index[7] = 9; - fit_index[8] = 11; - fit_index[9] = 12; - fit_index[10] = 13; - fit_index[11] = 15; - fit_index[12] = 16; - } - - // num = -3 * signal_points[0] + 4 * signal_points[1] - signal_points[2]; - // den = signal_points[0] - 2 * signal_points[1] + signal_points[2]; - int32_t num = (signal_points[0] * -3) + (signal_points[1] * 4) - - signal_points[2]; - int32_t den = signal_points[0] + (signal_points[1] * -2) + signal_points[2]; - int32_t temp = num * 120; - int flag = 1; - int16_t stp = kParabolaCoefficients[fit_index[fs_mult]][0] - - kParabolaCoefficients[fit_index[fs_mult - 1]][0]; - int16_t strt = (kParabolaCoefficients[fit_index[fs_mult]][0] - + kParabolaCoefficients[fit_index[fs_mult - 1]][0]) / 2; - int16_t lmt; - if (temp < -den * strt) { - lmt = strt - stp; - while (flag) { - if ((flag == fs_mult) || (temp > -den * lmt)) { - *peak_value = (den * kParabolaCoefficients[fit_index[fs_mult - flag]][1] - + num * kParabolaCoefficients[fit_index[fs_mult - flag]][2] - + signal_points[0] * 256) / 256; - *peak_index = *peak_index * 2 * fs_mult - flag; - flag = 0; - } else { - flag++; - lmt -= stp; - } - } - } else if (temp > -den * (strt + stp)) { - lmt = strt + 2 * stp; - while (flag) { - if ((flag == fs_mult) || (temp < -den * lmt)) { - int32_t temp_term_1 = - den * kParabolaCoefficients[fit_index[fs_mult+flag]][1]; - int32_t temp_term_2 = - num * kParabolaCoefficients[fit_index[fs_mult+flag]][2]; - int32_t temp_term_3 = signal_points[0] * 256; - *peak_value = (temp_term_1 + temp_term_2 + temp_term_3) / 256; - *peak_index = *peak_index * 2 * fs_mult + flag; - flag = 0; - } else { - flag++; - lmt += stp; - } - } - } else { - *peak_value = signal_points[1]; - *peak_index = *peak_index * 2 * fs_mult; - } -} - -int DspHelper::MinDistortion(const int16_t* signal, int min_lag, - int max_lag, int length, - int32_t* distortion_value) { - int best_index = -1; - int32_t min_distortion = WEBRTC_SPL_WORD32_MAX; - for (int i = min_lag; i <= max_lag; i++) { - int32_t sum_diff = 0; - const int16_t* data1 = signal; - const int16_t* data2 = signal - i; - for (int j = 0; j < length; j++) { - sum_diff += WEBRTC_SPL_ABS_W32(data1[j] - data2[j]); - } - // Compare with previous minimum. - if (sum_diff < min_distortion) { - min_distortion = sum_diff; - best_index = i; - } - } - *distortion_value = min_distortion; - return best_index; -} - -void DspHelper::CrossFade(const int16_t* input1, const int16_t* input2, - size_t length, int16_t* mix_factor, - int16_t factor_decrement, int16_t* output) { - int16_t factor = *mix_factor; - int16_t complement_factor = 16384 - factor; - for (size_t i = 0; i < length; i++) { - output[i] = - (factor * input1[i] + complement_factor * input2[i] + 8192) >> 14; - factor -= factor_decrement; - complement_factor += factor_decrement; - } - *mix_factor = factor; -} - -void DspHelper::UnmuteSignal(const int16_t* input, size_t length, - int16_t* factor, int16_t increment, - int16_t* output) { - uint16_t factor_16b = *factor; - int32_t factor_32b = (static_cast(factor_16b) << 6) + 32; - for (size_t i = 0; i < length; i++) { - output[i] = (factor_16b * input[i] + 8192) >> 14; - factor_32b = std::max(factor_32b + increment, 0); - factor_16b = std::min(16384, factor_32b >> 6); - } - *factor = factor_16b; -} - -void DspHelper::MuteSignal(int16_t* signal, int16_t mute_slope, size_t length) { - int32_t factor = (16384 << 6) + 32; - for (size_t i = 0; i < length; i++) { - signal[i] = ((factor >> 6) * signal[i] + 8192) >> 14; - factor -= mute_slope; - } -} - -int DspHelper::DownsampleTo4kHz(const int16_t* input, size_t input_length, - int output_length, int input_rate_hz, - bool compensate_delay, int16_t* output) { - // Set filter parameters depending on input frequency. - // NOTE: The phase delay values are wrong compared to the true phase delay - // of the filters. However, the error is preserved (through the +1 term) for - // consistency. - const int16_t* filter_coefficients; // Filter coefficients. - int16_t filter_length; // Number of coefficients. - int16_t filter_delay; // Phase delay in samples. - int16_t factor; // Conversion rate (inFsHz / 8000). - switch (input_rate_hz) { - case 8000: { - filter_length = 3; - factor = 2; - filter_coefficients = kDownsample8kHzTbl; - filter_delay = 1 + 1; - break; - } - case 16000: { - filter_length = 5; - factor = 4; - filter_coefficients = kDownsample16kHzTbl; - filter_delay = 2 + 1; - break; - } - case 32000: { - filter_length = 7; - factor = 8; - filter_coefficients = kDownsample32kHzTbl; - filter_delay = 3 + 1; - break; - } - case 48000: { - filter_length = 7; - factor = 12; - filter_coefficients = kDownsample48kHzTbl; - filter_delay = 3 + 1; - break; - } - default: { - assert(false); - return -1; - } - } - - if (!compensate_delay) { - // Disregard delay compensation. - filter_delay = 0; - } - - // Returns -1 if input signal is too short; 0 otherwise. - return WebRtcSpl_DownsampleFast( - &input[filter_length - 1], static_cast(input_length) - - (filter_length - 1), output, output_length, filter_coefficients, - filter_length, factor, filter_delay); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dsp_helper.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dsp_helper.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dsp_helper.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dsp_helper.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DSP_HELPER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DSP_HELPER_H_ - -#include // Access to size_t. - -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// This class contains various signal processing functions, all implemented as -// static methods. -class DspHelper { - public: - // Filter coefficients used when downsampling from the indicated sample rates - // (8, 16, 32, 48 kHz) to 4 kHz. Coefficients are in Q12. - static const int16_t kDownsample8kHzTbl[3]; - static const int16_t kDownsample16kHzTbl[5]; - static const int16_t kDownsample32kHzTbl[7]; - static const int16_t kDownsample48kHzTbl[7]; - - // Constants used to mute and unmute over 5 samples. The coefficients are - // in Q15. - static const int kMuteFactorStart8kHz = 27307; - static const int kMuteFactorIncrement8kHz = -5461; - static const int kUnmuteFactorStart8kHz = 5461; - static const int kUnmuteFactorIncrement8kHz = 5461; - static const int kMuteFactorStart16kHz = 29789; - static const int kMuteFactorIncrement16kHz = -2979; - static const int kUnmuteFactorStart16kHz = 2979; - static const int kUnmuteFactorIncrement16kHz = 2979; - static const int kMuteFactorStart32kHz = 31208; - static const int kMuteFactorIncrement32kHz = -1560; - static const int kUnmuteFactorStart32kHz = 1560; - static const int kUnmuteFactorIncrement32kHz = 1560; - static const int kMuteFactorStart48kHz = 31711; - static const int kMuteFactorIncrement48kHz = -1057; - static const int kUnmuteFactorStart48kHz = 1057; - static const int kUnmuteFactorIncrement48kHz = 1057; - - // Multiplies the signal with a gradually changing factor. - // The first sample is multiplied with |factor| (in Q14). For each sample, - // |factor| is increased (additive) by the |increment| (in Q20), which can - // be negative. Returns the scale factor after the last increment. - static int RampSignal(const int16_t* input, - size_t length, - int factor, - int increment, - int16_t* output); - - // Same as above, but with the samples of |signal| being modified in-place. - static int RampSignal(int16_t* signal, - size_t length, - int factor, - int increment); - - // Same as above, but processes |length| samples from |signal|, starting at - // |start_index|. - static int RampSignal(AudioMultiVector* signal, - size_t start_index, - size_t length, - int factor, - int increment); - - // Peak detection with parabolic fit. Looks for |num_peaks| maxima in |data|, - // having length |data_length| and sample rate multiplier |fs_mult|. The peak - // locations and values are written to the arrays |peak_index| and - // |peak_value|, respectively. Both arrays must hold at least |num_peaks| - // elements. - static void PeakDetection(int16_t* data, int data_length, - int num_peaks, int fs_mult, - int* peak_index, int16_t* peak_value); - - // Estimates the height and location of a maximum. The three values in the - // array |signal_points| are used as basis for a parabolic fit, which is then - // used to find the maximum in an interpolated signal. The |signal_points| are - // assumed to be from a 4 kHz signal, while the maximum, written to - // |peak_index| and |peak_value| is given in the full sample rate, as - // indicated by the sample rate multiplier |fs_mult|. - static void ParabolicFit(int16_t* signal_points, int fs_mult, - int* peak_index, int16_t* peak_value); - - // Calculates the sum-abs-diff for |signal| when compared to a displaced - // version of itself. Returns the displacement lag that results in the minimum - // distortion. The resulting distortion is written to |distortion_value|. - // The values of |min_lag| and |max_lag| are boundaries for the search. - static int MinDistortion(const int16_t* signal, int min_lag, - int max_lag, int length, int32_t* distortion_value); - - // Mixes |length| samples from |input1| and |input2| together and writes the - // result to |output|. The gain for |input1| starts at |mix_factor| (Q14) and - // is decreased by |factor_decrement| (Q14) for each sample. The gain for - // |input2| is the complement 16384 - mix_factor. - static void CrossFade(const int16_t* input1, const int16_t* input2, - size_t length, int16_t* mix_factor, - int16_t factor_decrement, int16_t* output); - - // Scales |input| with an increasing gain. Applies |factor| (Q14) to the first - // sample and increases the gain by |increment| (Q20) for each sample. The - // result is written to |output|. |length| samples are processed. - static void UnmuteSignal(const int16_t* input, size_t length, int16_t* factor, - int16_t increment, int16_t* output); - - // Starts at unity gain and gradually fades out |signal|. For each sample, - // the gain is reduced by |mute_slope| (Q14). |length| samples are processed. - static void MuteSignal(int16_t* signal, int16_t mute_slope, size_t length); - - // Downsamples |input| from |sample_rate_hz| to 4 kHz sample rate. The input - // has |input_length| samples, and the method will write |output_length| - // samples to |output|. Compensates for the phase delay of the downsampling - // filters if |compensate_delay| is true. Returns -1 if the input is too short - // to produce |output_length| samples, otherwise 0. - static int DownsampleTo4kHz(const int16_t* input, size_t input_length, - int output_length, int input_rate_hz, - bool compensate_delay, int16_t* output); - - private: - // Table of constants used in method DspHelper::ParabolicFit(). - static const int16_t kParabolaCoefficients[17][3]; - - DISALLOW_COPY_AND_ASSIGN(DspHelper); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DSP_HELPER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dsp_helper_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dsp_helper_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dsp_helper_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dsp_helper_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/dsp_helper.h" - -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -TEST(DspHelper, RampSignalArray) { - static const int kLen = 100; - int16_t input[kLen]; - int16_t output[kLen]; - // Fill input with 1000. - for (int i = 0; i < kLen; ++i) { - input[i] = 1000; - } - int start_factor = 0; - // Ramp from 0 to 1 (in Q14) over the array. Note that |increment| is in Q20, - // while the factor is in Q14, hence the shift by 6. - int increment = (16384 << 6) / kLen; - - // Test first method. - int stop_factor = DspHelper::RampSignal(input, kLen, start_factor, increment, - output); - EXPECT_EQ(16383, stop_factor); // Almost reach 1 in Q14. - for (int i = 0; i < kLen; ++i) { - EXPECT_EQ(1000 * i / kLen, output[i]); - } - - // Test second method. (Note that this modifies |input|.) - stop_factor = DspHelper::RampSignal(input, kLen, start_factor, increment); - EXPECT_EQ(16383, stop_factor); // Almost reach 1 in Q14. - for (int i = 0; i < kLen; ++i) { - EXPECT_EQ(1000 * i / kLen, input[i]); - } -} - -TEST(DspHelper, RampSignalAudioMultiVector) { - static const int kLen = 100; - static const int kChannels = 5; - AudioMultiVector input(kChannels, kLen * 3); - // Fill input with 1000. - for (int i = 0; i < kLen * 3; ++i) { - for (int channel = 0; channel < kChannels; ++channel) { - input[channel][i] = 1000; - } - } - // We want to start ramping at |start_index| and keep ramping for |kLen| - // samples. - int start_index = kLen; - int start_factor = 0; - // Ramp from 0 to 1 (in Q14) in |kLen| samples. Note that |increment| is in - // Q20, while the factor is in Q14, hence the shift by 6. - int increment = (16384 << 6) / kLen; - - int stop_factor = DspHelper::RampSignal(&input, start_index, kLen, - start_factor, increment); - EXPECT_EQ(16383, stop_factor); // Almost reach 1 in Q14. - // Verify that the first |kLen| samples are left untouched. - int i; - for (i = 0; i < kLen; ++i) { - for (int channel = 0; channel < kChannels; ++channel) { - EXPECT_EQ(1000, input[channel][i]); - } - } - // Verify that the next block of |kLen| samples are ramped. - for (; i < 2 * kLen; ++i) { - for (int channel = 0; channel < kChannels; ++channel) { - EXPECT_EQ(1000 * (i - kLen) / kLen, input[channel][i]); - } - } - // Verify the last |kLen| samples are left untouched. - for (; i < 3 * kLen; ++i) { - for (int channel = 0; channel < kChannels; ++channel) { - EXPECT_EQ(1000, input[channel][i]); - } - } -} -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_buffer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_buffer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_buffer.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_buffer.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/dtmf_buffer.h" - -#include -#include // max - -// Modify the code to obtain backwards bit-exactness. Once bit-exactness is no -// longer required, this #define should be removed (and the code that it -// enables). -#define LEGACY_BITEXACT - -namespace webrtc { - -// The ParseEvent method parses 4 bytes from |payload| according to this format -// from RFC 4733: -// -// 0 1 2 3 -// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | event |E|R| volume | duration | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// -// Legend (adapted from RFC 4733) -// - event: The event field is a number between 0 and 255 identifying a -// specific telephony event. The buffer will not accept any event -// numbers larger than 15. -// - E: If set to a value of one, the "end" bit indicates that this -// packet contains the end of the event. For long-lasting events -// that have to be split into segments, only the final packet for -// the final segment will have the E bit set. -// - R: Reserved. -// - volume: For DTMF digits and other events representable as tones, this -// field describes the power level of the tone, expressed in dBm0 -// after dropping the sign. Power levels range from 0 to -63 dBm0. -// Thus, larger values denote lower volume. The buffer discards -// values larger than 36 (i.e., lower than -36 dBm0). -// - duration: The duration field indicates the duration of the event or segment -// being reported, in timestamp units, expressed as an unsigned -// integer in network byte order. For a non-zero value, the event -// or segment began at the instant identified by the RTP timestamp -// and has so far lasted as long as indicated by this parameter. -// The event may or may not have ended. If the event duration -// exceeds the maximum representable by the duration field, the -// event is split into several contiguous segments. The buffer will -// discard zero-duration events. -// -int DtmfBuffer::ParseEvent(uint32_t rtp_timestamp, - const uint8_t* payload, - int payload_length_bytes, - DtmfEvent* event) { - if (!payload || !event) { - return kInvalidPointer; - } - if (payload_length_bytes < 4) { - return kPayloadTooShort; - } - - event->event_no = payload[0]; - event->end_bit = ((payload[1] & 0x80) != 0); - event->volume = (payload[1] & 0x3F); - event->duration = payload[2] << 8 | payload[3]; - event->timestamp = rtp_timestamp; - return kOK; -} - -// Inserts a DTMF event into the buffer. The event should be parsed from the -// bit stream using the ParseEvent method above before inserting it in the -// buffer. -// DTMF events can be quite long, and in most cases the duration of the event -// is not known when the first packet describing it is sent. To deal with that, -// the RFC 4733 specifies that multiple packets are sent for one and the same -// event as it is being created (typically, as the user is pressing the key). -// These packets will all share the same start timestamp and event number, -// while the duration will be the cumulative duration from the start. When -// inserting a new event, the InsertEvent method tries to find a matching event -// already in the buffer. If so, the new event is simply merged with the -// existing one. -int DtmfBuffer::InsertEvent(const DtmfEvent& event) { - if (event.event_no < 0 || event.event_no > 15 || - event.volume < 0 || event.volume > 36 || - event.duration <= 0 || event.duration > 65535) { - return kInvalidEventParameters; - } - DtmfList::iterator it = buffer_.begin(); - while (it != buffer_.end()) { - if (MergeEvents(it, event)) { - // A matching event was found and the new event was merged. - return kOK; - } - ++it; - } - buffer_.push_back(event); - // Sort the buffer using CompareEvents to rank the events. - buffer_.sort(CompareEvents); - return kOK; -} - -bool DtmfBuffer::GetEvent(uint32_t current_timestamp, DtmfEvent* event) { - DtmfList::iterator it = buffer_.begin(); - while (it != buffer_.end()) { - // |event_end| is an estimate of where the current event ends. If the end - // bit is set, we know that the event ends at |timestamp| + |duration|. - uint32_t event_end = it->timestamp + it->duration; -#ifdef LEGACY_BITEXACT - bool next_available = false; -#endif - if (!it->end_bit) { - // If the end bit is not set, we allow extrapolation of the event for - // some time. - event_end += max_extrapolation_samples_; - DtmfList::iterator next = it; - ++next; - if (next != buffer_.end()) { - // If there is a next event in the buffer, we will not extrapolate over - // the start of that new event. - event_end = std::min(event_end, next->timestamp); -#ifdef LEGACY_BITEXACT - next_available = true; -#endif - } - } - if (current_timestamp >= it->timestamp - && current_timestamp <= event_end) { // TODO(hlundin): Change to <. - // Found a matching event. - if (event) { - event->event_no = it->event_no; - event->end_bit = it->end_bit; - event->volume = it->volume; - event->duration = it->duration; - event->timestamp = it->timestamp; - } -#ifdef LEGACY_BITEXACT - if (it->end_bit && - current_timestamp + frame_len_samples_ >= event_end) { - // We are done playing this. Erase the event. - buffer_.erase(it); - } -#endif - return true; - } else if (current_timestamp > event_end) { // TODO(hlundin): Change to >=. - // Erase old event. Operation returns a valid pointer to the next element - // in the list. -#ifdef LEGACY_BITEXACT - if (!next_available) { - if (event) { - event->event_no = it->event_no; - event->end_bit = it->end_bit; - event->volume = it->volume; - event->duration = it->duration; - event->timestamp = it->timestamp; - } - it = buffer_.erase(it); - return true; - } else { - it = buffer_.erase(it); - } -#else - it = buffer_.erase(it); -#endif - } else { - ++it; - } - } - return false; -} - -int DtmfBuffer::SetSampleRate(int fs_hz) { - if (fs_hz != 8000 && - fs_hz != 16000 && - fs_hz != 32000 && - fs_hz != 48000) { - return kInvalidSampleRate; - } - max_extrapolation_samples_ = 7 * fs_hz / 100; - frame_len_samples_ = fs_hz / 100; - return kOK; -} - -// The method returns true if the two events are considered to be the same. -// The are defined as equal if they share the same timestamp and event number. -// The special case with long-lasting events that have to be split into segments -// is not handled in this method. These will be treated as separate events in -// the buffer. -bool DtmfBuffer::SameEvent(const DtmfEvent& a, const DtmfEvent& b) { - return (a.event_no == b.event_no) && (a.timestamp == b.timestamp); -} - -bool DtmfBuffer::MergeEvents(DtmfList::iterator it, const DtmfEvent& event) { - if (SameEvent(*it, event)) { - if (!it->end_bit) { - // Do not extend the duration of an event for which the end bit was - // already received. - it->duration = std::max(event.duration, it->duration); - } - if (event.end_bit) { - it->end_bit = true; - } - return true; - } else { - return false; - } -} - -// Returns true if |a| goes before |b| in the sorting order ("|a| < |b|"). -// The events are ranked using their start timestamp (taking wrap-around into -// account). In the unlikely situation that two events share the same start -// timestamp, the event number is used to rank the two. Note that packets -// that belong to the same events, and therefore sharing the same start -// timestamp, have already been merged before the sort method is called. -bool DtmfBuffer::CompareEvents(const DtmfEvent& a, const DtmfEvent& b) { - if (a.timestamp == b.timestamp) { - return a.event_no < b.event_no; - } - // Take wrap-around into account. - return (static_cast(b.timestamp - a.timestamp) < 0xFFFFFFFF / 2); -} -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_buffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_buffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_buffer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_buffer.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DTMF_BUFFER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DTMF_BUFFER_H_ - -#include -#include // size_t - -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -struct DtmfEvent { - uint32_t timestamp; - int event_no; - int volume; - int duration; - bool end_bit; - - // Constructors - DtmfEvent() - : timestamp(0), - event_no(0), - volume(0), - duration(0), - end_bit(false) { - } - DtmfEvent(uint32_t ts, int ev, int vol, int dur, bool end) - : timestamp(ts), - event_no(ev), - volume(vol), - duration(dur), - end_bit(end) { - } -}; - -// This is the buffer holding DTMF events while waiting for them to be played. -class DtmfBuffer { - public: - enum BufferReturnCodes { - kOK = 0, - kInvalidPointer, - kPayloadTooShort, - kInvalidEventParameters, - kInvalidSampleRate - }; - - // Set up the buffer for use at sample rate |fs_hz|. - explicit DtmfBuffer(int fs_hz) { - SetSampleRate(fs_hz); - } - - virtual ~DtmfBuffer() {} - - // Flushes the buffer. - virtual void Flush() { buffer_.clear(); } - - // Static method to parse 4 bytes from |payload| as a DTMF event (RFC 4733) - // and write the parsed information into the struct |event|. Input variable - // |rtp_timestamp| is simply copied into the struct. - static int ParseEvent(uint32_t rtp_timestamp, - const uint8_t* payload, - int payload_length_bytes, - DtmfEvent* event); - - // Inserts |event| into the buffer. The method looks for a matching event and - // merges the two if a match is found. - virtual int InsertEvent(const DtmfEvent& event); - - // Checks if a DTMF event should be played at time |current_timestamp|. If so, - // the method returns true; otherwise false. The parameters of the event to - // play will be written to |event|. - virtual bool GetEvent(uint32_t current_timestamp, DtmfEvent* event); - - // Number of events in the buffer. - virtual size_t Length() const { return buffer_.size(); } - - virtual bool Empty() const { return buffer_.empty(); } - - // Set a new sample rate. - virtual int SetSampleRate(int fs_hz); - - private: - typedef std::list DtmfList; - - int max_extrapolation_samples_; - int frame_len_samples_; // TODO(hlundin): Remove this later. - - // Compares two events and returns true if they are the same. - static bool SameEvent(const DtmfEvent& a, const DtmfEvent& b); - - // Merges |event| to the event pointed out by |it|. The method checks that - // the two events are the same (using the SameEvent method), and merges them - // if that was the case, returning true. If the events are not the same, false - // is returned. - bool MergeEvents(DtmfList::iterator it, const DtmfEvent& event); - - // Method used by the sort algorithm to rank events in the buffer. - static bool CompareEvents(const DtmfEvent& a, const DtmfEvent& b); - - DtmfList buffer_; - - DISALLOW_COPY_AND_ASSIGN(DtmfBuffer); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DTMF_BUFFER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_buffer_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_buffer_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_buffer_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_buffer_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,307 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/dtmf_buffer.h" - -#ifdef WIN32 -#include // ntohl() -#else -#include // ntohl() -#endif - -#include - -#include "gtest/gtest.h" - -// Modify the tests so that they pass with the modifications done to DtmfBuffer -// for backwards bit-exactness. Once bit-exactness is no longer required, this -// #define should be removed (and the code that it enables). -#define LEGACY_BITEXACT - -namespace webrtc { - -static int sample_rate_hz = 8000; - -static uint32_t MakeDtmfPayload(int event, bool end, int volume, int duration) { - uint32_t payload = 0; -// 0 1 2 3 -// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | event |E|R| volume | duration | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - payload |= (event & 0x00FF) << 24; - payload |= (end ? 0x00800000 : 0x00000000); - payload |= (volume & 0x003F) << 16; - payload |= (duration & 0xFFFF); - payload = ntohl(payload); - return payload; -} - -static bool EqualEvents(const DtmfEvent& a, - const DtmfEvent& b) { - return (a.duration == b.duration - && a.end_bit == b.end_bit - && a.event_no == b.event_no - && a.timestamp == b.timestamp - && a.volume == b.volume); -} - -TEST(DtmfBuffer, CreateAndDestroy) { - DtmfBuffer* buffer = new DtmfBuffer(sample_rate_hz); - delete buffer; -} - -// Test the event parser. -TEST(DtmfBuffer, ParseEvent) { - int event_no = 7; - bool end_bit = true; - int volume = 17; - int duration = 4711; - uint32_t timestamp = 0x12345678; - uint32_t payload = MakeDtmfPayload(event_no, end_bit, volume, duration); - uint8_t* payload_ptr = reinterpret_cast(&payload); - DtmfEvent event; - EXPECT_EQ(DtmfBuffer::kOK, - DtmfBuffer::ParseEvent(timestamp, payload_ptr, sizeof(payload), - &event)); - EXPECT_EQ(duration, event.duration); - EXPECT_EQ(end_bit, event.end_bit); - EXPECT_EQ(event_no, event.event_no); - EXPECT_EQ(timestamp, event.timestamp); - EXPECT_EQ(volume, event.volume); - - EXPECT_EQ(DtmfBuffer::kInvalidPointer, - DtmfBuffer::ParseEvent(timestamp, NULL, 4, &event)); - - EXPECT_EQ(DtmfBuffer::kInvalidPointer, - DtmfBuffer::ParseEvent(timestamp, payload_ptr, 4, NULL)); - - EXPECT_EQ(DtmfBuffer::kPayloadTooShort, - DtmfBuffer::ParseEvent(timestamp, payload_ptr, 3, &event)); -} - -TEST(DtmfBuffer, SimpleInsertAndGet) { - int event_no = 7; - bool end_bit = true; - int volume = 17; - int duration = 4711; - uint32_t timestamp = 0x12345678; - DtmfEvent event(timestamp, event_no, volume, duration, end_bit); - DtmfBuffer buffer(sample_rate_hz); - EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event)); - EXPECT_EQ(1u, buffer.Length()); - EXPECT_FALSE(buffer.Empty()); - DtmfEvent out_event; - // Too early to get event. - EXPECT_FALSE(buffer.GetEvent(timestamp - 10, &out_event)); - EXPECT_EQ(1u, buffer.Length()); - EXPECT_FALSE(buffer.Empty()); - // Get the event at its starting timestamp. - EXPECT_TRUE(buffer.GetEvent(timestamp, &out_event)); - EXPECT_TRUE(EqualEvents(event, out_event)); - EXPECT_EQ(1u, buffer.Length()); - EXPECT_FALSE(buffer.Empty()); - // Get the event some time into the event. - EXPECT_TRUE(buffer.GetEvent(timestamp + duration / 2, &out_event)); - EXPECT_TRUE(EqualEvents(event, out_event)); - EXPECT_EQ(1u, buffer.Length()); - EXPECT_FALSE(buffer.Empty()); - // Give a "current" timestamp after the event has ended. -#ifdef LEGACY_BITEXACT - EXPECT_TRUE(buffer.GetEvent(timestamp + duration + 10, &out_event)); -#endif - EXPECT_FALSE(buffer.GetEvent(timestamp + duration + 10, &out_event)); - EXPECT_EQ(0u, buffer.Length()); - EXPECT_TRUE(buffer.Empty()); -} - -TEST(DtmfBuffer, MergingPackets) { - int event_no = 0; - bool end_bit = false; - int volume = 17; - int duration = 80; - uint32_t timestamp = 0x12345678; - DtmfEvent event(timestamp, event_no, volume, duration, end_bit); - DtmfBuffer buffer(sample_rate_hz); - EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event)); - - event.duration += 80; - EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event)); - - event.duration += 80; - event.end_bit = true; - EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event)); - - EXPECT_EQ(1u, buffer.Length()); - - DtmfEvent out_event; - EXPECT_TRUE(buffer.GetEvent(timestamp, &out_event)); - EXPECT_TRUE(EqualEvents(event, out_event)); -} - -// This test case inserts one shorter event completely overlapped by one longer -// event. The expected outcome is that only the longer event is played. -TEST(DtmfBuffer, OverlappingEvents) { - int event_no = 0; - bool end_bit = true; - int volume = 1; - int duration = 80; - uint32_t timestamp = 0x12345678 + 80; - DtmfEvent short_event(timestamp, event_no, volume, duration, end_bit); - DtmfBuffer buffer(sample_rate_hz); - EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(short_event)); - - event_no = 10; - end_bit = false; - timestamp = 0x12345678; - DtmfEvent long_event(timestamp, event_no, volume, duration, end_bit); - EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(long_event)); - - long_event.duration += 80; - EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(long_event)); - - long_event.duration += 80; - long_event.end_bit = true; - EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(long_event)); - - EXPECT_EQ(2u, buffer.Length()); - - DtmfEvent out_event; - // Expect to get the long event. - EXPECT_TRUE(buffer.GetEvent(timestamp, &out_event)); - EXPECT_TRUE(EqualEvents(long_event, out_event)); - // Expect no more events. -#ifdef LEGACY_BITEXACT - EXPECT_TRUE(buffer.GetEvent(timestamp + long_event.duration + 10, - &out_event)); - EXPECT_TRUE(EqualEvents(long_event, out_event)); - EXPECT_TRUE(buffer.GetEvent(timestamp + long_event.duration + 10, - &out_event)); - EXPECT_TRUE(EqualEvents(short_event, out_event)); -#else - EXPECT_FALSE(buffer.GetEvent(timestamp + long_event.duration + 10, - &out_event)); -#endif - EXPECT_TRUE(buffer.Empty()); -} - -TEST(DtmfBuffer, ExtrapolationTime) { - int event_no = 0; - bool end_bit = false; - int volume = 1; - int duration = 80; - uint32_t timestamp = 0x12345678; - DtmfEvent event1(timestamp, event_no, volume, duration, end_bit); - DtmfBuffer buffer(sample_rate_hz); - EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event1)); - EXPECT_EQ(1u, buffer.Length()); - - DtmfEvent out_event; - // Get the event at the start. - EXPECT_TRUE(buffer.GetEvent(timestamp, &out_event)); - EXPECT_TRUE(EqualEvents(event1, out_event)); - // Also get the event 100 samples after the end of the event (since we're - // missing the end bit). - uint32_t timestamp_now = timestamp + duration + 100; - EXPECT_TRUE(buffer.GetEvent(timestamp_now, &out_event)); - EXPECT_TRUE(EqualEvents(event1, out_event)); - // Insert another event starting back-to-back with the previous event. - timestamp += duration; - event_no = 1; - DtmfEvent event2(timestamp, event_no, volume, duration, end_bit); - EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event2)); - EXPECT_EQ(2u, buffer.Length()); - // Now we expect to get the new event when supplying |timestamp_now|. - EXPECT_TRUE(buffer.GetEvent(timestamp_now, &out_event)); - EXPECT_TRUE(EqualEvents(event2, out_event)); - // Expect the the first event to be erased now. - EXPECT_EQ(1u, buffer.Length()); - // Move |timestamp_now| to more than 560 samples after the end of the second - // event. Expect that event to be erased. - timestamp_now = timestamp + duration + 600; -#ifdef LEGACY_BITEXACT - EXPECT_TRUE(buffer.GetEvent(timestamp_now, &out_event)); -#endif - EXPECT_FALSE(buffer.GetEvent(timestamp_now, &out_event)); - EXPECT_TRUE(buffer.Empty()); -} - -TEST(DtmfBuffer, TimestampWraparound) { - int event_no = 0; - bool end_bit = true; - int volume = 1; - int duration = 80; - uint32_t timestamp1 = 0xFFFFFFFF - duration; - DtmfEvent event1(timestamp1, event_no, volume, duration, end_bit); - uint32_t timestamp2 = 0; - DtmfEvent event2(timestamp2, event_no, volume, duration, end_bit); - DtmfBuffer buffer(sample_rate_hz); - EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event1)); - EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event2)); - EXPECT_EQ(2u, buffer.Length()); - DtmfEvent out_event; - EXPECT_TRUE(buffer.GetEvent(timestamp1, &out_event)); - EXPECT_TRUE(EqualEvents(event1, out_event)); -#ifdef LEGACY_BITEXACT - EXPECT_EQ(1u, buffer.Length()); -#else - EXPECT_EQ(2u, buffer.Length()); -#endif - - buffer.Flush(); - // Reverse the insert order. Expect same results. - EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event2)); - EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event1)); - EXPECT_EQ(2u, buffer.Length()); - EXPECT_TRUE(buffer.GetEvent(timestamp1, &out_event)); - EXPECT_TRUE(EqualEvents(event1, out_event)); -#ifdef LEGACY_BITEXACT - EXPECT_EQ(1u, buffer.Length()); -#else - EXPECT_EQ(2u, buffer.Length()); -#endif -} - -TEST(DtmfBuffer, InvalidEvents) { - int event_no = 0; - bool end_bit = true; - int volume = 1; - int duration = 80; - uint32_t timestamp = 0x12345678; - DtmfEvent event(timestamp, event_no, volume, duration, end_bit); - DtmfBuffer buffer(sample_rate_hz); - - // Invalid event number. - event.event_no = -1; - EXPECT_EQ(DtmfBuffer::kInvalidEventParameters, buffer.InsertEvent(event)); - event.event_no = 16; - EXPECT_EQ(DtmfBuffer::kInvalidEventParameters, buffer.InsertEvent(event)); - event.event_no = 0; // Valid value; - - // Invalid volume. - event.volume = -1; - EXPECT_EQ(DtmfBuffer::kInvalidEventParameters, buffer.InsertEvent(event)); - event.volume = 37; - EXPECT_EQ(DtmfBuffer::kInvalidEventParameters, buffer.InsertEvent(event)); - event.volume = 0; // Valid value; - - // Invalid duration. - event.duration = -1; - EXPECT_EQ(DtmfBuffer::kInvalidEventParameters, buffer.InsertEvent(event)); - event.duration = 0; - EXPECT_EQ(DtmfBuffer::kInvalidEventParameters, buffer.InsertEvent(event)); - event.duration = 0xFFFF + 1; - EXPECT_EQ(DtmfBuffer::kInvalidEventParameters, buffer.InsertEvent(event)); - event.duration = 1; // Valid value; - - // Finish with a valid event, just to verify that all is ok. - EXPECT_EQ(DtmfBuffer::kOK, buffer.InsertEvent(event)); -} -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_tone_generator.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_tone_generator.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_tone_generator.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_tone_generator.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,192 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// This class provides a generator for DTMF tones. The tone generation is based -// on a sinusoid recursion. Each sinusoid is generated using a recursion -// formula; x[n] = a * x[n-1] - x[n-2], where the coefficient -// a = 2*cos(2*pi*f/fs). The recursion is started with x[-1] = 0 and -// x[-2] = sin(2*pi*f/fs). (Note that with this initialization, the resulting -// sinusoid gets a "negative" rotation; x[n] = sin(-2*pi*f/fs * n + phi), but -// kept this way due to historical reasons.) -// TODO(hlundin): Change to positive rotation? -// -// Each key on the telephone keypad corresponds to an "event", 0-15. Each event -// is mapped to a tone pair, with a low and a high frequency. There are four -// low and four high frequencies, each corresponding to a row and column, -// respectively, on the keypad as illustrated below. -// -// 1209 Hz 1336 Hz 1477 Hz 1633 Hz -// 697 Hz 1 2 3 12 -// 770 Hz 4 5 6 13 -// 852 Hz 7 8 9 14 -// 941 Hz 10 0 11 15 - -#include "webrtc/modules/audio_coding/neteq4/dtmf_tone_generator.h" - -#include - -namespace webrtc { - -// The filter coefficient a = 2*cos(2*pi*f/fs) for the low frequency tone, for -// sample rates fs = {8000, 16000, 32000, 48000} Hz, and events 0 through 15. -// Values are in Q14. -const int DtmfToneGenerator::kCoeff1[4][16] = { - { 24219, 27980, 27980, 27980, 26956, 26956, 26956, 25701, 25701, 25701, - 24219, 24219, 27980, 26956, 25701, 24219 }, - { 30556, 31548, 31548, 31548, 31281, 31281, 31281, 30951, 30951, 30951, - 30556, 30556, 31548, 31281, 30951, 30556 }, - { 32210, 32462, 32462, 32462, 32394, 32394, 32394, 32311, 32311, 32311, - 32210, 32210, 32462, 32394, 32311, 32210 }, - { 32520, 32632, 32632, 32632, 32602, 32602, 32602, 32564, 32564, 32564, - 32520, 32520, 32632, 32602, 32564, 32520 } }; - -// The filter coefficient a = 2*cos(2*pi*f/fs) for the high frequency tone, for -// sample rates fs = {8000, 16000, 32000, 48000} Hz, and events 0 through 15. -// Values are in Q14. -const int DtmfToneGenerator::kCoeff2[4][16] = { - { 16325, 19073, 16325, 13085, 19073, 16325, 13085, 19073, 16325, 13085, - 19073, 13085, 9315, 9315, 9315, 9315}, - { 28361, 29144, 28361, 27409, 29144, 28361, 27409, 29144, 28361, 27409, - 29144, 27409, 26258, 26258, 26258, 26258}, - { 31647, 31849, 31647, 31400, 31849, 31647, 31400, 31849, 31647, 31400, - 31849, 31400, 31098, 31098, 31098, 31098}, - { 32268, 32359, 32268, 32157, 32359, 32268, 32157, 32359, 32268, 32157, - 32359, 32157, 32022, 32022, 32022, 32022} }; - -// The initialization value x[-2] = sin(2*pi*f/fs) for the low frequency tone, -// for sample rates fs = {8000, 16000, 32000, 48000} Hz, and events 0-15. -// Values are in Q14. -const int DtmfToneGenerator::kInitValue1[4][16] = { - { 11036, 8528, 8528, 8528, 9315, 9315, 9315, 10163, 10163, 10163, 11036, - 11036, 8528, 9315, 10163, 11036}, - { 5918, 4429, 4429, 4429, 4879, 4879, 4879, 5380, 5380, 5380, 5918, 5918, - 4429, 4879, 5380, 5918}, - { 3010, 2235, 2235, 2235, 2468, 2468, 2468, 2728, 2728, 2728, 3010, 3010, - 2235, 2468, 2728, 3010}, - { 2013, 1493, 1493, 1493, 1649, 1649, 1649, 1823, 1823, 1823, 2013, 2013, - 1493, 1649, 1823, 2013 } }; - -// The initialization value x[-2] = sin(2*pi*f/fs) for the high frequency tone, -// for sample rates fs = {8000, 16000, 32000, 48000} Hz, and events 0-15. -// Values are in Q14. -const int DtmfToneGenerator::kInitValue2[4][16] = { - { 14206, 13323, 14206, 15021, 13323, 14206, 15021, 13323, 14206, 15021, - 13323, 15021, 15708, 15708, 15708, 15708}, - { 8207, 7490, 8207, 8979, 7490, 8207, 8979, 7490, 8207, 8979, 7490, 8979, - 9801, 9801, 9801, 9801}, - { 4249, 3853, 4249, 4685, 3853, 4249, 4685, 3853, 4249, 4685, 3853, 4685, - 5164, 5164, 5164, 5164}, - { 2851, 2582, 2851, 3148, 2582, 2851, 3148, 2582, 2851, 3148, 2582, 3148, - 3476, 3476, 3476, 3476} }; - -// Amplitude multipliers for volume values 0 through 36, corresponding to -// 0 dBm0 through -36 dBm0. Values are in Q14. -const int DtmfToneGenerator::kAmplitude[37] = { - 16141, 14386, 12821, 11427, 10184, 9077, 8090, 7210, 6426, 5727, 5104, 4549, - 4054, 3614, 3221, 2870, 2558, 2280, 2032, 1811, 1614, 1439, 1282, 1143, - 1018, 908, 809, 721, 643, 573, 510, 455, 405, 361, 322, 287, 256 }; - -// Constructor. -DtmfToneGenerator::DtmfToneGenerator() - : initialized_(false), - coeff1_(0), - coeff2_(0), - amplitude_(0) { -} - -// Initialize the DTMF generator with sample rate fs Hz (8000, 16000, 32000, -// 48000), event (0-15) and attenuation (0-36 dB). -// Returns 0 on success, otherwise an error code. -int DtmfToneGenerator::Init(int fs, int event, int attenuation) { - initialized_ = false; - int fs_index; - if (fs == 8000) { - fs_index = 0; - } else if (fs == 16000) { - fs_index = 1; - } else if (fs == 32000) { - fs_index = 2; - } else if (fs == 48000) { - fs_index = 3; - } else { - assert(false); - fs_index = 1; // Default to 8000 Hz. - } - - if (event < 0 || event > 15) { - return kParameterError; // Invalid event number. - } - - if (attenuation < 0 || attenuation > 36) { - return kParameterError; // Invalid attenuation. - } - - // Look up oscillator coefficient for low and high frequencies. - coeff1_ = kCoeff1[fs_index][event]; - coeff2_ = kCoeff2[fs_index][event]; - // Look up amplitude multiplier. - amplitude_ = kAmplitude[attenuation]; - // Initialize sample history. - sample_history1_[0] = kInitValue1[fs_index][event]; - sample_history1_[1] = 0; - sample_history2_[0] = kInitValue2[fs_index][event]; - sample_history2_[1] = 0; - - initialized_ = true; - return 0; -} - -// Reset tone generator to uninitialized state. -void DtmfToneGenerator::Reset() { - initialized_ = false; -} - -// Generate num_samples of DTMF signal and write to |output|. -int DtmfToneGenerator::Generate(int num_samples, - AudioMultiVector* output) { - if (!initialized_) { - return kNotInitialized; - } - - if (num_samples < 0 || !output) { - return kParameterError; - } - assert(output->Channels() == 1); // Not adapted for multi-channel yet. - if (output->Channels() != 1) { - return kStereoNotSupported; - } - - output->AssertSize(num_samples); - for (int i = 0; i < num_samples; ++i) { - // Use recursion formula y[n] = a * y[n - 1] - y[n - 2]. - int16_t temp_val_low = ((coeff1_ * sample_history1_[1] + 8192) >> 14) - - sample_history1_[0]; - int16_t temp_val_high = ((coeff2_ * sample_history2_[1] + 8192) >> 14) - - sample_history2_[0]; - - // Update recursion memory. - sample_history1_[0] = sample_history1_[1]; - sample_history1_[1] = temp_val_low; - sample_history2_[0] = sample_history2_[1]; - sample_history2_[1] = temp_val_high; - - // Attenuate the low frequency tone 3 dB. - int32_t temp_val = kAmpMultiplier * temp_val_low + (temp_val_high << 15); - // Normalize the signal to Q14 with proper rounding. - temp_val = (temp_val + 16384) >> 15; - // Scale the signal to correct volume. - (*output)[0][i] = - static_cast((temp_val * amplitude_ + 8192) >> 14); - } - - return num_samples; -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_tone_generator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_tone_generator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_tone_generator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_tone_generator.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DTMF_TONE_GENERATOR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DTMF_TONE_GENERATOR_H_ - - -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// This class provides a generator for DTMF tones. -class DtmfToneGenerator { - public: - enum ReturnCodes { - kNotInitialized = -1, - kParameterError = -2, - kStereoNotSupported = -3, - }; - - DtmfToneGenerator(); - virtual ~DtmfToneGenerator() {} - virtual int Init(int fs, int event, int attenuation); - virtual void Reset(); - virtual int Generate(int num_samples, AudioMultiVector* output); - virtual bool initialized() const { return initialized_; } - - private: - static const int kCoeff1[4][16]; // 1st oscillator model coefficient table. - static const int kCoeff2[4][16]; // 2nd oscillator model coefficient table. - static const int kInitValue1[4][16]; // Initialization for 1st oscillator. - static const int kInitValue2[4][16]; // Initialization for 2nd oscillator. - static const int kAmplitude[37]; // Amplitude for 0 through -36 dBm0. - static const int16_t kAmpMultiplier = 23171; // 3 dB attenuation (in Q15). - - bool initialized_; // True if generator is initialized properly. - int coeff1_; // 1st oscillator coefficient for this event. - int coeff2_; // 2nd oscillator coefficient for this event. - int amplitude_; // Amplitude for this event. - int16_t sample_history1_[2]; // Last 2 samples for the 1st oscillator. - int16_t sample_history2_[2]; // Last 2 samples for the 2nd oscillator. - - DISALLOW_COPY_AND_ASSIGN(DtmfToneGenerator); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_DTMF_TONE_GENERATOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_tone_generator_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_tone_generator_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_tone_generator_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/dtmf_tone_generator_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Unit tests for DtmfToneGenerator class. - -#include "webrtc/modules/audio_coding/neteq4/dtmf_tone_generator.h" - -#include - -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" - -namespace webrtc { - -TEST(DtmfToneGenerator, CreateAndDestroy) { - DtmfToneGenerator* tone_gen = new DtmfToneGenerator(); - delete tone_gen; -} - -TEST(DtmfToneGenerator, TestErrors) { - DtmfToneGenerator tone_gen; - const int kNumSamples = 10; - AudioMultiVector signal(1); // One channel. - - // Try to generate tones without initializing. - EXPECT_EQ(DtmfToneGenerator::kNotInitialized, - tone_gen.Generate(kNumSamples, &signal)); - - const int fs = 16000; // Valid sample rate. - const int event = 7; // Valid event. - const int attenuation = 0; // Valid attenuation. - // Initialize with invalid event -1. - EXPECT_EQ(DtmfToneGenerator::kParameterError, - tone_gen.Init(fs, -1, attenuation)); - // Initialize with invalid event 16. - EXPECT_EQ(DtmfToneGenerator::kParameterError, - tone_gen.Init(fs, 16, attenuation)); - // Initialize with invalid attenuation -1. - EXPECT_EQ(DtmfToneGenerator::kParameterError, tone_gen.Init(fs, event, -1)); - // Initialize with invalid attenuation 37. - EXPECT_EQ(DtmfToneGenerator::kParameterError, tone_gen.Init(fs, event, 37)); - EXPECT_FALSE(tone_gen.initialized()); // Should still be uninitialized. - - // Initialize with valid parameters. - ASSERT_EQ(0, tone_gen.Init(fs, event, attenuation)); - EXPECT_TRUE(tone_gen.initialized()); - // Negative number of samples. - EXPECT_EQ(DtmfToneGenerator::kParameterError, tone_gen.Generate(-1, &signal)); - // NULL pointer to destination. - EXPECT_EQ(DtmfToneGenerator::kParameterError, - tone_gen.Generate(kNumSamples, NULL)); -} - -TEST(DtmfToneGenerator, TestTones) { - DtmfToneGenerator tone_gen; - const int kAttenuation = 0; - const int kNumSamples = 10; - AudioMultiVector signal(1); // One channel. - - // Low and high frequencies for events 0 through 15. - const double low_freq_hz[] = { 941.0, 697.0, 697.0, 697.0, 770.0, 770.0, - 770.0, 852.0, 852.0, 852.0, 941.0, 941.0, 697.0, 770.0, 852.0, 941.0 }; - const double hi_freq_hz[] = { 1336.0, 1209.0, 1336.0, 1477.0, 1209.0, 1336.0, - 1477.0, 1209.0, 1336.0, 1477.0, 1209.0, 1477.0, 1633.0, 1633.0, 1633.0, - 1633.0 }; - const double attenuate_3dB = 23171.0 / 32768; // 3 dB attenuation. - const double base_attenuation = 16141.0 / 16384.0; // This is the attenuation - // applied to all cases. - const int fs_vec[] = { 8000, 16000, 32000, 48000 }; - for (int f = 0; f < 4; ++f) { - int fs = fs_vec[f]; - for (int event = 0; event <= 15; ++event) { - std::ostringstream ss; - ss << "Checking event " << event << " at sample rate " << fs; - SCOPED_TRACE(ss.str()); - ASSERT_EQ(0, tone_gen.Init(fs, event, kAttenuation)); - EXPECT_TRUE(tone_gen.initialized()); - EXPECT_EQ(kNumSamples, tone_gen.Generate(kNumSamples, &signal)); - - double f1 = low_freq_hz[event]; - double f2 = hi_freq_hz[event]; - const double pi = 3.14159265358979323846; - - for (int n = 0; n < kNumSamples; ++n) { - double x = attenuate_3dB * sin(2.0 * pi * f1 / fs * (-n - 1)) - + sin(2.0 * pi * f2 / fs * (-n - 1)); - x *= base_attenuation; - x = ldexp(x, 14); // Scale to Q14. - static const int kChannel = 0; - EXPECT_NEAR(x, static_cast(signal[kChannel][n]), 25); - } - - tone_gen.Reset(); - EXPECT_FALSE(tone_gen.initialized()); - } - } -} - -TEST(DtmfToneGenerator, TestAmplitudes) { - DtmfToneGenerator tone_gen; - const int kNumSamples = 10; - AudioMultiVector signal(1); // One channel. - AudioMultiVector ref_signal(1); // One channel. - - const int fs_vec[] = { 8000, 16000, 32000, 48000 }; - const int event_vec[] = { 0, 4, 9, 13 }; // Test a few events. - for (int f = 0; f < 4; ++f) { - int fs = fs_vec[f]; - int event = event_vec[f]; - // Create full-scale reference. - ASSERT_EQ(0, tone_gen.Init(fs, event, 0)); // 0 attenuation. - EXPECT_EQ(kNumSamples, tone_gen.Generate(kNumSamples, &ref_signal)); - // Test every 5 steps (to save time). - for (int attenuation = 1; attenuation <= 36; attenuation += 5) { - std::ostringstream ss; - ss << "Checking event " << event << " at sample rate " << fs; - ss << "; attenuation " << attenuation; - SCOPED_TRACE(ss.str()); - ASSERT_EQ(0, tone_gen.Init(fs, event, attenuation)); - EXPECT_EQ(kNumSamples, tone_gen.Generate(kNumSamples, &signal)); - for (int n = 0; n < kNumSamples; ++n) { - double attenuation_factor = - pow(10, -static_cast(attenuation)/20); - // Verify that the attenuation is correct. - static const int kChannel = 0; - EXPECT_NEAR(attenuation_factor * ref_signal[kChannel][n], - signal[kChannel][n], 2); - } - - tone_gen.Reset(); - } - } -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/expand.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/expand.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/expand.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/expand.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,877 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/expand.h" - -#include -#include // memset - -#include // min, max -#include // numeric_limits - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_coding/neteq4/background_noise.h" -#include "webrtc/modules/audio_coding/neteq4/dsp_helper.h" -#include "webrtc/modules/audio_coding/neteq4/random_vector.h" -#include "webrtc/modules/audio_coding/neteq4/sync_buffer.h" - -namespace webrtc { - -void Expand::Reset() { - first_expand_ = true; - consecutive_expands_ = 0; - max_lag_ = 0; - for (size_t ix = 0; ix < num_channels_; ++ix) { - channel_parameters_[ix].expand_vector0.Clear(); - channel_parameters_[ix].expand_vector1.Clear(); - } -} - -int Expand::Process(AudioMultiVector* output) { - int16_t random_vector[kMaxSampleRate / 8000 * 120 + 30]; - int16_t scaled_random_vector[kMaxSampleRate / 8000 * 125]; - static const int kTempDataSize = 3600; - int16_t temp_data[kTempDataSize]; // TODO(hlundin) Remove this. - int16_t* voiced_vector_storage = temp_data; - int16_t* voiced_vector = &voiced_vector_storage[overlap_length_]; - static const int kNoiseLpcOrder = BackgroundNoise::kMaxLpcOrder; - int16_t unvoiced_array_memory[kNoiseLpcOrder + kMaxSampleRate / 8000 * 125]; - int16_t* unvoiced_vector = unvoiced_array_memory + kUnvoicedLpcOrder; - int16_t* noise_vector = unvoiced_array_memory + kNoiseLpcOrder; - - int fs_mult = fs_hz_ / 8000; - - if (first_expand_) { - // Perform initial setup if this is the first expansion since last reset. - AnalyzeSignal(random_vector); - first_expand_ = false; - } else { - // This is not the first expansion, parameters are already estimated. - // Extract a noise segment. - int16_t rand_length = max_lag_; - // TODO(hlundin): This if-statement should not be needed. Should be just - // as good to generate all of the vector in one call in either case. - if (rand_length <= RandomVector::kRandomTableSize) { - random_vector_->IncreaseSeedIncrement(2); - random_vector_->Generate(rand_length, random_vector); - } else { - // This only applies to SWB where length could be larger than 256. - assert(rand_length <= kMaxSampleRate / 8000 * 120 + 30); - random_vector_->IncreaseSeedIncrement(2); - random_vector_->Generate(RandomVector::kRandomTableSize, random_vector); - random_vector_->IncreaseSeedIncrement(2); - random_vector_->Generate(rand_length - RandomVector::kRandomTableSize, - &random_vector[RandomVector::kRandomTableSize]); - } - } - - - // Generate signal. - UpdateLagIndex(); - - // Voiced part. - // Generate a weighted vector with the current lag. - size_t expansion_vector_length = max_lag_ + overlap_length_; - size_t current_lag = expand_lags_[current_lag_index_]; - // Copy lag+overlap data. - size_t expansion_vector_position = expansion_vector_length - current_lag - - overlap_length_; - size_t temp_length = current_lag + overlap_length_; - for (size_t channel_ix = 0; channel_ix < num_channels_; ++channel_ix) { - ChannelParameters& parameters = channel_parameters_[channel_ix]; - if (current_lag_index_ == 0) { - // Use only expand_vector0. - assert(expansion_vector_position + temp_length <= - parameters.expand_vector0.Size()); - memcpy(voiced_vector_storage, - ¶meters.expand_vector0[expansion_vector_position], - sizeof(int16_t) * temp_length); - } else if (current_lag_index_ == 1) { - // Mix 3/4 of expand_vector0 with 1/4 of expand_vector1. - WebRtcSpl_ScaleAndAddVectorsWithRound( - ¶meters.expand_vector0[expansion_vector_position], 3, - ¶meters.expand_vector1[expansion_vector_position], 1, 2, - voiced_vector_storage, static_cast(temp_length)); - } else if (current_lag_index_ == 2) { - // Mix 1/2 of expand_vector0 with 1/2 of expand_vector1. - assert(expansion_vector_position + temp_length <= - parameters.expand_vector0.Size()); - assert(expansion_vector_position + temp_length <= - parameters.expand_vector1.Size()); - WebRtcSpl_ScaleAndAddVectorsWithRound( - ¶meters.expand_vector0[expansion_vector_position], 1, - ¶meters.expand_vector1[expansion_vector_position], 1, 1, - voiced_vector_storage, static_cast(temp_length)); - } - - // Get tapering window parameters. Values are in Q15. - int16_t muting_window, muting_window_increment; - int16_t unmuting_window, unmuting_window_increment; - if (fs_hz_ == 8000) { - muting_window = DspHelper::kMuteFactorStart8kHz; - muting_window_increment = DspHelper::kMuteFactorIncrement8kHz; - unmuting_window = DspHelper::kUnmuteFactorStart8kHz; - unmuting_window_increment = DspHelper::kUnmuteFactorIncrement8kHz; - } else if (fs_hz_ == 16000) { - muting_window = DspHelper::kMuteFactorStart16kHz; - muting_window_increment = DspHelper::kMuteFactorIncrement16kHz; - unmuting_window = DspHelper::kUnmuteFactorStart16kHz; - unmuting_window_increment = DspHelper::kUnmuteFactorIncrement16kHz; - } else if (fs_hz_ == 32000) { - muting_window = DspHelper::kMuteFactorStart32kHz; - muting_window_increment = DspHelper::kMuteFactorIncrement32kHz; - unmuting_window = DspHelper::kUnmuteFactorStart32kHz; - unmuting_window_increment = DspHelper::kUnmuteFactorIncrement32kHz; - } else { // fs_ == 48000 - muting_window = DspHelper::kMuteFactorStart48kHz; - muting_window_increment = DspHelper::kMuteFactorIncrement48kHz; - unmuting_window = DspHelper::kUnmuteFactorStart48kHz; - unmuting_window_increment = DspHelper::kUnmuteFactorIncrement48kHz; - } - - // Smooth the expanded if it has not been muted to a low amplitude and - // |current_voice_mix_factor| is larger than 0.5. - if ((parameters.mute_factor > 819) && - (parameters.current_voice_mix_factor > 8192)) { - size_t start_ix = sync_buffer_->Size() - overlap_length_; - for (size_t i = 0; i < overlap_length_; i++) { - // Do overlap add between new vector and overlap. - (*sync_buffer_)[channel_ix][start_ix + i] = - (((*sync_buffer_)[channel_ix][start_ix + i] * muting_window) + - (((parameters.mute_factor * voiced_vector_storage[i]) >> 14) * - unmuting_window) + 16384) >> 15; - muting_window += muting_window_increment; - unmuting_window += unmuting_window_increment; - } - } else if (parameters.mute_factor == 0) { - // The expanded signal will consist of only comfort noise if - // mute_factor = 0. Set the output length to 15 ms for best noise - // production. - // TODO(hlundin): This has been disabled since the length of - // parameters.expand_vector0 and parameters.expand_vector1 no longer - // match with expand_lags_, causing invalid reads and writes. Is it a good - // idea to enable this again, and solve the vector size problem? -// max_lag_ = fs_mult * 120; -// expand_lags_[0] = fs_mult * 120; -// expand_lags_[1] = fs_mult * 120; -// expand_lags_[2] = fs_mult * 120; - } - - // Unvoiced part. - // Filter |scaled_random_vector| through |ar_filter_|. - memcpy(unvoiced_vector - kUnvoicedLpcOrder, parameters.ar_filter_state, - sizeof(int16_t) * kUnvoicedLpcOrder); - int32_t add_constant = 0; - if (parameters.ar_gain_scale > 0) { - add_constant = 1 << (parameters.ar_gain_scale - 1); - } - WebRtcSpl_AffineTransformVector(scaled_random_vector, random_vector, - parameters.ar_gain, add_constant, - parameters.ar_gain_scale, - static_cast(current_lag)); - WebRtcSpl_FilterARFastQ12(scaled_random_vector, unvoiced_vector, - parameters.ar_filter, kUnvoicedLpcOrder + 1, - static_cast(current_lag)); - memcpy(parameters.ar_filter_state, - &(unvoiced_vector[current_lag - kUnvoicedLpcOrder]), - sizeof(int16_t) * kUnvoicedLpcOrder); - - // Combine voiced and unvoiced contributions. - - // Set a suitable cross-fading slope. - // For lag = - // <= 31 * fs_mult => go from 1 to 0 in about 8 ms; - // (>= 31 .. <= 63) * fs_mult => go from 1 to 0 in about 16 ms; - // >= 64 * fs_mult => go from 1 to 0 in about 32 ms. - // temp_shift = getbits(max_lag_) - 5. - int temp_shift = (31 - WebRtcSpl_NormW32(max_lag_)) - 5; - int16_t mix_factor_increment = 256 >> temp_shift; - if (stop_muting_) { - mix_factor_increment = 0; - } - - // Create combined signal by shifting in more and more of unvoiced part. - temp_shift = 8 - temp_shift; // = getbits(mix_factor_increment). - size_t temp_lenght = (parameters.current_voice_mix_factor - - parameters.voice_mix_factor) >> temp_shift; - temp_lenght = std::min(temp_lenght, current_lag); - DspHelper::CrossFade(voiced_vector, unvoiced_vector, temp_lenght, - ¶meters.current_voice_mix_factor, - mix_factor_increment, temp_data); - - // End of cross-fading period was reached before end of expanded signal - // path. Mix the rest with a fixed mixing factor. - if (temp_lenght < current_lag) { - if (mix_factor_increment != 0) { - parameters.current_voice_mix_factor = parameters.voice_mix_factor; - } - int temp_scale = 16384 - parameters.current_voice_mix_factor; - WebRtcSpl_ScaleAndAddVectorsWithRound( - voiced_vector + temp_lenght, parameters.current_voice_mix_factor, - unvoiced_vector + temp_lenght, temp_scale, 14, - temp_data + temp_lenght, static_cast(current_lag - temp_lenght)); - } - - // Select muting slope depending on how many consecutive expands we have - // done. - if (consecutive_expands_ == 3) { - // Let the mute factor decrease from 1.0 to 0.95 in 6.25 ms. - // mute_slope = 0.0010 / fs_mult in Q20. - parameters.mute_slope = std::max(parameters.mute_slope, - static_cast(1049 / fs_mult)); - } - if (consecutive_expands_ == 7) { - // Let the mute factor decrease from 1.0 to 0.90 in 6.25 ms. - // mute_slope = 0.0020 / fs_mult in Q20. - parameters.mute_slope = std::max(parameters.mute_slope, - static_cast(2097 / fs_mult)); - } - - // Mute segment according to slope value. - if ((consecutive_expands_ != 0) || !parameters.onset) { - // Mute to the previous level, then continue with the muting. - WebRtcSpl_AffineTransformVector(temp_data, temp_data, - parameters.mute_factor, 8192, - 14, static_cast(current_lag)); - - if (!stop_muting_) { - DspHelper::MuteSignal(temp_data, parameters.mute_slope, current_lag); - - // Shift by 6 to go from Q20 to Q14. - // TODO(hlundin): Adding 8192 before shifting 6 steps seems wrong. - // Legacy. - int16_t gain = static_cast(16384 - - (((current_lag * parameters.mute_slope) + 8192) >> 6)); - gain = ((gain * parameters.mute_factor) + 8192) >> 14; - - // Guard against getting stuck with very small (but sometimes audible) - // gain. - if ((consecutive_expands_ > 3) && (gain >= parameters.mute_factor)) { - parameters.mute_factor = 0; - } else { - parameters.mute_factor = gain; - } - } - } - - // Background noise part. - // TODO(hlundin): Move to separate method? In BackgroundNoise class? - if (background_noise_->initialized()) { - // Use background noise parameters. - memcpy(noise_vector - kNoiseLpcOrder, - background_noise_->FilterState(channel_ix), - sizeof(int16_t) * kNoiseLpcOrder); - - if (background_noise_->ScaleShift(channel_ix) > 1) { - add_constant = 1 << (background_noise_->ScaleShift(channel_ix) - 1); - } else { - add_constant = 0; - } - - // Scale random vector to correct energy level. - WebRtcSpl_AffineTransformVector( - scaled_random_vector, random_vector, - background_noise_->Scale(channel_ix), add_constant, - background_noise_->ScaleShift(channel_ix), - static_cast(current_lag)); - - WebRtcSpl_FilterARFastQ12(scaled_random_vector, noise_vector, - background_noise_->Filter(channel_ix), - kNoiseLpcOrder + 1, - static_cast(current_lag)); - - background_noise_->SetFilterState( - channel_ix, - &(noise_vector[current_lag - kNoiseLpcOrder]), - kNoiseLpcOrder); - - // Unmute the background noise. - int16_t bgn_mute_factor = background_noise_->MuteFactor(channel_ix); - NetEqBackgroundNoiseMode bgn_mode = background_noise_->mode(); - if (bgn_mode == kBgnFade && - consecutive_expands_ >= kMaxConsecutiveExpands && - bgn_mute_factor > 0) { - // Fade BGN to zero. - // Calculate muting slope, approximately -2^18 / fs_hz. - int16_t mute_slope; - if (fs_hz_ == 8000) { - mute_slope = -32; - } else if (fs_hz_ == 16000) { - mute_slope = -16; - } else if (fs_hz_ == 32000) { - mute_slope = -8; - } else { - mute_slope = -5; - } - // Use UnmuteSignal function with negative slope. - // |bgn_mute_factor| is in Q14. |mute_slope| is in Q20. - DspHelper::UnmuteSignal(noise_vector, current_lag, &bgn_mute_factor, - mute_slope, noise_vector); - } else if (bgn_mute_factor < 16384) { - // If mode is kBgnOff, or if kBgnFade has started fading, - // Use regular |mute_slope|. - if (!stop_muting_ && bgn_mode != kBgnOff && - !(bgn_mode == kBgnFade && - consecutive_expands_ >= kMaxConsecutiveExpands)) { - DspHelper::UnmuteSignal(noise_vector, static_cast(current_lag), - &bgn_mute_factor, parameters.mute_slope, - noise_vector); - } else { - // kBgnOn and stop muting, or - // kBgnOff (mute factor is always 0), or - // kBgnFade has reached 0. - WebRtcSpl_AffineTransformVector(noise_vector, noise_vector, - bgn_mute_factor, 8192, 14, - static_cast(current_lag)); - } - } - // Update mute_factor in BackgroundNoise class. - background_noise_->SetMuteFactor(channel_ix, bgn_mute_factor); - } else { - // BGN parameters have not been initialized; use zero noise. - memset(noise_vector, 0, sizeof(int16_t) * current_lag); - } - - // Add background noise to the combined voiced-unvoiced signal. - for (size_t i = 0; i < current_lag; i++) { - temp_data[i] = temp_data[i] + noise_vector[i]; - } - if (channel_ix == 0) { - output->AssertSize(current_lag); - } else { - assert(output->Size() == current_lag); - } - memcpy(&(*output)[channel_ix][0], temp_data, - sizeof(temp_data[0]) * current_lag); - } - - // Increase call number and cap it. - ++consecutive_expands_; - if (consecutive_expands_ > kMaxConsecutiveExpands) { - consecutive_expands_ = kMaxConsecutiveExpands; - } - - return 0; -} - -void Expand::SetParametersForNormalAfterExpand() { - current_lag_index_ = 0; - lag_index_direction_ = 0; - stop_muting_ = true; // Do not mute signal any more. -} - -void Expand::SetParametersForMergeAfterExpand() { - current_lag_index_ = -1; /* out of the 3 possible ones */ - lag_index_direction_ = 1; /* make sure we get the "optimal" lag */ - stop_muting_ = true; -} - -void Expand::AnalyzeSignal(int16_t* random_vector) { - int32_t auto_correlation[kUnvoicedLpcOrder + 1]; - int16_t reflection_coeff[kUnvoicedLpcOrder]; - int16_t correlation_vector[kMaxSampleRate / 8000 * 102]; - int best_correlation_index[kNumCorrelationCandidates]; - int16_t best_correlation[kNumCorrelationCandidates]; - int16_t best_distortion_index[kNumCorrelationCandidates]; - int16_t best_distortion[kNumCorrelationCandidates]; - int32_t correlation_vector2[(99 * kMaxSampleRate / 8000) + 1]; - int32_t best_distortion_w32[kNumCorrelationCandidates]; - static const int kNoiseLpcOrder = BackgroundNoise::kMaxLpcOrder; - int16_t unvoiced_array_memory[kNoiseLpcOrder + kMaxSampleRate / 8000 * 125]; - int16_t* unvoiced_vector = unvoiced_array_memory + kUnvoicedLpcOrder; - - int fs_mult = fs_hz_ / 8000; - - // Pre-calculate common multiplications with fs_mult. - int fs_mult_4 = fs_mult * 4; - int fs_mult_20 = fs_mult * 20; - int fs_mult_120 = fs_mult * 120; - int fs_mult_dist_len = fs_mult * kDistortionLength; - int fs_mult_lpc_analysis_len = fs_mult * kLpcAnalysisLength; - - const size_t signal_length = 256 * fs_mult; - const int16_t* audio_history = - &(*sync_buffer_)[0][sync_buffer_->Size() - signal_length]; - - // Initialize some member variables. - lag_index_direction_ = 1; - current_lag_index_ = -1; - stop_muting_ = false; - random_vector_->set_seed_increment(1); - consecutive_expands_ = 0; - for (size_t ix = 0; ix < num_channels_; ++ix) { - channel_parameters_[ix].current_voice_mix_factor = 16384; // 1.0 in Q14. - channel_parameters_[ix].mute_factor = 16384; // 1.0 in Q14. - // Start with 0 gain for background noise. - background_noise_->SetMuteFactor(ix, 0); - } - - // Calculate correlation in downsampled domain (4 kHz sample rate). - int16_t correlation_scale; - int correlation_length = 51; // TODO(hlundin): Legacy bit-exactness. - // If it is decided to break bit-exactness |correlation_length| should be - // initialized to the return value of Correlation(). - Correlation(audio_history, signal_length, correlation_vector, - &correlation_scale); - - // Find peaks in correlation vector. - DspHelper::PeakDetection(correlation_vector, correlation_length, - kNumCorrelationCandidates, fs_mult, - best_correlation_index, best_correlation); - - // Adjust peak locations; cross-correlation lags start at 2.5 ms - // (20 * fs_mult samples). - best_correlation_index[0] += fs_mult_20; - best_correlation_index[1] += fs_mult_20; - best_correlation_index[2] += fs_mult_20; - - // Calculate distortion around the |kNumCorrelationCandidates| best lags. - int distortion_scale = 0; - for (int i = 0; i < kNumCorrelationCandidates; i++) { - int16_t min_index = std::max(fs_mult_20, - best_correlation_index[i] - fs_mult_4); - int16_t max_index = std::min(fs_mult_120 - 1, - best_correlation_index[i] + fs_mult_4); - best_distortion_index[i] = DspHelper::MinDistortion( - &(audio_history[signal_length - fs_mult_dist_len]), min_index, - max_index, fs_mult_dist_len, &best_distortion_w32[i]); - distortion_scale = std::max(16 - WebRtcSpl_NormW32(best_distortion_w32[i]), - distortion_scale); - } - // Shift the distortion values to fit in 16 bits. - WebRtcSpl_VectorBitShiftW32ToW16(best_distortion, kNumCorrelationCandidates, - best_distortion_w32, distortion_scale); - - // Find the maximizing index |i| of the cost function - // f[i] = best_correlation[i] / best_distortion[i]. - int32_t best_ratio = std::numeric_limits::min(); - int best_index = -1; - for (int i = 0; i < kNumCorrelationCandidates; ++i) { - int32_t ratio; - if (best_distortion[i] > 0) { - ratio = (best_correlation[i] << 16) / best_distortion[i]; - } else if (best_correlation[i] == 0) { - ratio = 0; // No correlation set result to zero. - } else { - ratio = std::numeric_limits::max(); // Denominator is zero. - } - if (ratio > best_ratio) { - best_index = i; - best_ratio = ratio; - } - } - - int distortion_lag = best_distortion_index[best_index]; - int correlation_lag = best_correlation_index[best_index]; - max_lag_ = std::max(distortion_lag, correlation_lag); - - // Calculate the exact best correlation in the range between - // |correlation_lag| and |distortion_lag|. - correlation_length = distortion_lag + 10; - correlation_length = std::min(correlation_length, fs_mult_120); - correlation_length = std::max(correlation_length, 60 * fs_mult); - - int start_index = std::min(distortion_lag, correlation_lag); - int correlation_lags = WEBRTC_SPL_ABS_W16((distortion_lag-correlation_lag)) - + 1; - assert(correlation_lags <= 99 * fs_mult + 1); // Cannot be larger. - - for (size_t channel_ix = 0; channel_ix < num_channels_; ++channel_ix) { - ChannelParameters& parameters = channel_parameters_[channel_ix]; - // Calculate suitable scaling. - int16_t signal_max = WebRtcSpl_MaxAbsValueW16( - &audio_history[signal_length - correlation_length - start_index - - correlation_lags], - correlation_length + start_index + correlation_lags - 1); - correlation_scale = ((31 - WebRtcSpl_NormW32(signal_max * signal_max)) - + (31 - WebRtcSpl_NormW32(correlation_length))) - 31; - correlation_scale = std::max(static_cast(0), correlation_scale); - - // Calculate the correlation, store in |correlation_vector2|. - WebRtcSpl_CrossCorrelation( - correlation_vector2, - &(audio_history[signal_length - correlation_length]), - &(audio_history[signal_length - correlation_length - start_index]), - correlation_length, correlation_lags, correlation_scale, -1); - - // Find maximizing index. - best_index = WebRtcSpl_MaxIndexW32(correlation_vector2, correlation_lags); - int32_t max_correlation = correlation_vector2[best_index]; - // Compensate index with start offset. - best_index = best_index + start_index; - - // Calculate energies. - int32_t energy1 = WebRtcSpl_DotProductWithScale( - &(audio_history[signal_length - correlation_length]), - &(audio_history[signal_length - correlation_length]), - correlation_length, correlation_scale); - int32_t energy2 = WebRtcSpl_DotProductWithScale( - &(audio_history[signal_length - correlation_length - best_index]), - &(audio_history[signal_length - correlation_length - best_index]), - correlation_length, correlation_scale); - - // Calculate the correlation coefficient between the two portions of the - // signal. - int16_t corr_coefficient; - if ((energy1 > 0) && (energy2 > 0)) { - int energy1_scale = std::max(16 - WebRtcSpl_NormW32(energy1), 0); - int energy2_scale = std::max(16 - WebRtcSpl_NormW32(energy2), 0); - // Make sure total scaling is even (to simplify scale factor after sqrt). - if ((energy1_scale + energy2_scale) & 1) { - // If sum is odd, add 1 to make it even. - energy1_scale += 1; - } - int16_t scaled_energy1 = energy1 >> energy1_scale; - int16_t scaled_energy2 = energy2 >> energy2_scale; - int16_t sqrt_energy_product = WebRtcSpl_SqrtFloor( - scaled_energy1 * scaled_energy2); - // Calculate max_correlation / sqrt(energy1 * energy2) in Q14. - int cc_shift = 14 - (energy1_scale + energy2_scale) / 2; - max_correlation = WEBRTC_SPL_SHIFT_W32(max_correlation, cc_shift); - corr_coefficient = WebRtcSpl_DivW32W16(max_correlation, - sqrt_energy_product); - corr_coefficient = std::min(static_cast(16384), - corr_coefficient); // Cap at 1.0 in Q14. - } else { - corr_coefficient = 0; - } - - // Extract the two vectors expand_vector0 and expand_vector1 from - // |audio_history|. - int16_t expansion_length = static_cast(max_lag_ + overlap_length_); - const int16_t* vector1 = &(audio_history[signal_length - expansion_length]); - const int16_t* vector2 = vector1 - distortion_lag; - // Normalize the second vector to the same energy as the first. - energy1 = WebRtcSpl_DotProductWithScale(vector1, vector1, expansion_length, - correlation_scale); - energy2 = WebRtcSpl_DotProductWithScale(vector2, vector2, expansion_length, - correlation_scale); - // Confirm that amplitude ratio sqrt(energy1 / energy2) is within 0.5 - 2.0, - // i.e., energy1 / energy1 is within 0.25 - 4. - int16_t amplitude_ratio; - if ((energy1 / 4 < energy2) && (energy1 > energy2 / 4)) { - // Energy constraint fulfilled. Use both vectors and scale them - // accordingly. - int16_t scaled_energy2 = std::max(16 - WebRtcSpl_NormW32(energy2), 0); - int16_t scaled_energy1 = scaled_energy2 - 13; - // Calculate scaled_energy1 / scaled_energy2 in Q13. - int32_t energy_ratio = WebRtcSpl_DivW32W16( - WEBRTC_SPL_SHIFT_W32(energy1, -scaled_energy1), - WEBRTC_SPL_RSHIFT_W32(energy2, scaled_energy2)); - // Calculate sqrt ratio in Q13 (sqrt of en1/en2 in Q26). - amplitude_ratio = WebRtcSpl_SqrtFloor(energy_ratio << 13); - // Copy the two vectors and give them the same energy. - parameters.expand_vector0.Clear(); - parameters.expand_vector0.PushBack(vector1, expansion_length); - parameters.expand_vector1.Clear(); - if (parameters.expand_vector1.Size() < - static_cast(expansion_length)) { - parameters.expand_vector1.Extend( - expansion_length - parameters.expand_vector1.Size()); - } - WebRtcSpl_AffineTransformVector(¶meters.expand_vector1[0], - const_cast(vector2), - amplitude_ratio, - 4096, - 13, - expansion_length); - } else { - // Energy change constraint not fulfilled. Only use last vector. - parameters.expand_vector0.Clear(); - parameters.expand_vector0.PushBack(vector1, expansion_length); - // Copy from expand_vector0 to expand_vector1. - parameters.expand_vector0.CopyFrom(¶meters.expand_vector1); - // Set the energy_ratio since it is used by muting slope. - if ((energy1 / 4 < energy2) || (energy2 == 0)) { - amplitude_ratio = 4096; // 0.5 in Q13. - } else { - amplitude_ratio = 16384; // 2.0 in Q13. - } - } - - // Set the 3 lag values. - int lag_difference = distortion_lag - correlation_lag; - if (lag_difference == 0) { - // |distortion_lag| and |correlation_lag| are equal. - expand_lags_[0] = distortion_lag; - expand_lags_[1] = distortion_lag; - expand_lags_[2] = distortion_lag; - } else { - // |distortion_lag| and |correlation_lag| are not equal; use different - // combinations of the two. - // First lag is |distortion_lag| only. - expand_lags_[0] = distortion_lag; - // Second lag is the average of the two. - expand_lags_[1] = (distortion_lag + correlation_lag) / 2; - // Third lag is the average again, but rounding towards |correlation_lag|. - if (lag_difference > 0) { - expand_lags_[2] = (distortion_lag + correlation_lag - 1) / 2; - } else { - expand_lags_[2] = (distortion_lag + correlation_lag + 1) / 2; - } - } - - // Calculate the LPC and the gain of the filters. - // Calculate scale value needed for auto-correlation. - correlation_scale = WebRtcSpl_MaxAbsValueW16( - &(audio_history[signal_length - fs_mult_lpc_analysis_len]), - fs_mult_lpc_analysis_len); - - correlation_scale = std::min(16 - WebRtcSpl_NormW32(correlation_scale), 0); - correlation_scale = std::max(correlation_scale * 2 + 7, 0); - - // Calculate kUnvoicedLpcOrder + 1 lags of the auto-correlation function. - size_t temp_index = signal_length - fs_mult_lpc_analysis_len - - kUnvoicedLpcOrder; - // Copy signal to temporary vector to be able to pad with leading zeros. - int16_t* temp_signal = new int16_t[fs_mult_lpc_analysis_len - + kUnvoicedLpcOrder]; - memset(temp_signal, 0, - sizeof(int16_t) * (fs_mult_lpc_analysis_len + kUnvoicedLpcOrder)); - memcpy(&temp_signal[kUnvoicedLpcOrder], - &audio_history[temp_index + kUnvoicedLpcOrder], - sizeof(int16_t) * fs_mult_lpc_analysis_len); - WebRtcSpl_CrossCorrelation(auto_correlation, - &temp_signal[kUnvoicedLpcOrder], - &temp_signal[kUnvoicedLpcOrder], - fs_mult_lpc_analysis_len, kUnvoicedLpcOrder + 1, - correlation_scale, -1); - delete [] temp_signal; - - // Verify that variance is positive. - if (auto_correlation[0] > 0) { - // Estimate AR filter parameters using Levinson-Durbin algorithm; - // kUnvoicedLpcOrder + 1 filter coefficients. - int16_t stability = WebRtcSpl_LevinsonDurbin(auto_correlation, - parameters.ar_filter, - reflection_coeff, - kUnvoicedLpcOrder); - - // Keep filter parameters only if filter is stable. - if (stability != 1) { - // Set first coefficient to 4096 (1.0 in Q12). - parameters.ar_filter[0] = 4096; - // Set remaining |kUnvoicedLpcOrder| coefficients to zero. - WebRtcSpl_MemSetW16(parameters.ar_filter + 1, 0, kUnvoicedLpcOrder); - } - } - - if (channel_ix == 0) { - // Extract a noise segment. - int16_t noise_length; - if (distortion_lag < 40) { - noise_length = 2 * distortion_lag + 30; - } else { - noise_length = distortion_lag + 30; - } - if (noise_length <= RandomVector::kRandomTableSize) { - memcpy(random_vector, RandomVector::kRandomTable, - sizeof(int16_t) * noise_length); - } else { - // Only applies to SWB where length could be larger than - // |kRandomTableSize|. - memcpy(random_vector, RandomVector::kRandomTable, - sizeof(int16_t) * RandomVector::kRandomTableSize); - assert(noise_length <= kMaxSampleRate / 8000 * 120 + 30); - random_vector_->IncreaseSeedIncrement(2); - random_vector_->Generate( - noise_length - RandomVector::kRandomTableSize, - &random_vector[RandomVector::kRandomTableSize]); - } - } - - // Set up state vector and calculate scale factor for unvoiced filtering. - memcpy(parameters.ar_filter_state, - &(audio_history[signal_length - kUnvoicedLpcOrder]), - sizeof(int16_t) * kUnvoicedLpcOrder); - memcpy(unvoiced_vector - kUnvoicedLpcOrder, - &(audio_history[signal_length - 128 - kUnvoicedLpcOrder]), - sizeof(int16_t) * kUnvoicedLpcOrder); - WebRtcSpl_FilterMAFastQ12( - const_cast(&audio_history[signal_length - 128]), - unvoiced_vector, parameters.ar_filter, kUnvoicedLpcOrder + 1, 128); - int16_t unvoiced_prescale; - if (WebRtcSpl_MaxAbsValueW16(unvoiced_vector, 128) > 4000) { - unvoiced_prescale = 4; - } else { - unvoiced_prescale = 0; - } - int32_t unvoiced_energy = WebRtcSpl_DotProductWithScale(unvoiced_vector, - unvoiced_vector, - 128, - unvoiced_prescale); - - // Normalize |unvoiced_energy| to 28 or 29 bits to preserve sqrt() accuracy. - int16_t unvoiced_scale = WebRtcSpl_NormW32(unvoiced_energy) - 3; - // Make sure we do an odd number of shifts since we already have 7 shifts - // from dividing with 128 earlier. This will make the total scale factor - // even, which is suitable for the sqrt. - unvoiced_scale += ((unvoiced_scale & 0x1) ^ 0x1); - unvoiced_energy = WEBRTC_SPL_SHIFT_W32(unvoiced_energy, unvoiced_scale); - int32_t unvoiced_gain = WebRtcSpl_SqrtFloor(unvoiced_energy); - parameters.ar_gain_scale = 13 - + (unvoiced_scale + 7 - unvoiced_prescale) / 2; - parameters.ar_gain = unvoiced_gain; - - // Calculate voice_mix_factor from corr_coefficient. - // Let x = corr_coefficient. Then, we compute: - // if (x > 0.48) - // voice_mix_factor = (-5179 + 19931x - 16422x^2 + 5776x^3) / 4096; - // else - // voice_mix_factor = 0; - if (corr_coefficient > 7875) { - int16_t x1, x2, x3; - x1 = corr_coefficient; // |corr_coefficient| is in Q14. - x2 = (x1 * x1) >> 14; // Shift 14 to keep result in Q14. - x3 = (x1 * x2) >> 14; - static const int kCoefficients[4] = { -5179, 19931, -16422, 5776 }; - int32_t temp_sum = kCoefficients[0] << 14; - temp_sum += kCoefficients[1] * x1; - temp_sum += kCoefficients[2] * x2; - temp_sum += kCoefficients[3] * x3; - parameters.voice_mix_factor = temp_sum / 4096; - parameters.voice_mix_factor = std::min(parameters.voice_mix_factor, - static_cast(16384)); - parameters.voice_mix_factor = std::max(parameters.voice_mix_factor, - static_cast(0)); - } else { - parameters.voice_mix_factor = 0; - } - - // Calculate muting slope. Reuse value from earlier scaling of - // |expand_vector0| and |expand_vector1|. - int16_t slope = amplitude_ratio; - if (slope > 12288) { - // slope > 1.5. - // Calculate (1 - (1 / slope)) / distortion_lag = - // (slope - 1) / (distortion_lag * slope). - // |slope| is in Q13, so 1 corresponds to 8192. Shift up to Q25 before - // the division. - // Shift the denominator from Q13 to Q5 before the division. The result of - // the division will then be in Q20. - int16_t temp_ratio = WebRtcSpl_DivW32W16((slope - 8192) << 12, - (distortion_lag * slope) >> 8); - if (slope > 14746) { - // slope > 1.8. - // Divide by 2, with proper rounding. - parameters.mute_slope = (temp_ratio + 1) / 2; - } else { - // Divide by 8, with proper rounding. - parameters.mute_slope = (temp_ratio + 4) / 8; - } - parameters.onset = true; - } else { - // Calculate (1 - slope) / distortion_lag. - // Shift |slope| by 7 to Q20 before the division. The result is in Q20. - parameters.mute_slope = WebRtcSpl_DivW32W16((8192 - slope) << 7, - distortion_lag); - if (parameters.voice_mix_factor <= 13107) { - // Make sure the mute factor decreases from 1.0 to 0.9 in no more than - // 6.25 ms. - // mute_slope >= 0.005 / fs_mult in Q20. - parameters.mute_slope = std::max(static_cast(5243 / fs_mult), - parameters.mute_slope); - } else if (slope > 8028) { - parameters.mute_slope = 0; - } - parameters.onset = false; - } - } -} - -int16_t Expand::Correlation(const int16_t* input, size_t input_length, - int16_t* output, int16_t* output_scale) const { - // Set parameters depending on sample rate. - const int16_t* filter_coefficients; - int16_t num_coefficients; - int16_t downsampling_factor; - if (fs_hz_ == 8000) { - num_coefficients = 3; - downsampling_factor = 2; - filter_coefficients = DspHelper::kDownsample8kHzTbl; - } else if (fs_hz_ == 16000) { - num_coefficients = 5; - downsampling_factor = 4; - filter_coefficients = DspHelper::kDownsample16kHzTbl; - } else if (fs_hz_ == 32000) { - num_coefficients = 7; - downsampling_factor = 8; - filter_coefficients = DspHelper::kDownsample32kHzTbl; - } else { // fs_hz_ == 48000. - num_coefficients = 7; - downsampling_factor = 12; - filter_coefficients = DspHelper::kDownsample48kHzTbl; - } - - // Correlate from lag 10 to lag 60 in downsampled domain. - // (Corresponds to 20-120 for narrow-band, 40-240 for wide-band, and so on.) - static const int kCorrelationStartLag = 10; - static const int kNumCorrelationLags = 54; - static const int kCorrelationLength = 60; - // Downsample to 4 kHz sample rate. - static const int kDownsampledLength = kCorrelationStartLag - + kNumCorrelationLags + kCorrelationLength; - int16_t downsampled_input[kDownsampledLength]; - static const int kFilterDelay = 0; - WebRtcSpl_DownsampleFast( - input + input_length - kDownsampledLength * downsampling_factor, - kDownsampledLength * downsampling_factor, downsampled_input, - kDownsampledLength, filter_coefficients, num_coefficients, - downsampling_factor, kFilterDelay); - - // Normalize |downsampled_input| to using all 16 bits. - int16_t max_value = WebRtcSpl_MaxAbsValueW16(downsampled_input, - kDownsampledLength); - int16_t norm_shift = 16 - WebRtcSpl_NormW32(max_value); - WebRtcSpl_VectorBitShiftW16(downsampled_input, kDownsampledLength, - downsampled_input, norm_shift); - - int32_t correlation[kNumCorrelationLags]; - static const int kCorrelationShift = 6; - WebRtcSpl_CrossCorrelation( - correlation, - &downsampled_input[kDownsampledLength - kCorrelationLength], - &downsampled_input[kDownsampledLength - kCorrelationLength - - kCorrelationStartLag], - kCorrelationLength, kNumCorrelationLags, kCorrelationShift, -1); - - // Normalize and move data from 32-bit to 16-bit vector. - int32_t max_correlation = WebRtcSpl_MaxAbsValueW32(correlation, - kNumCorrelationLags); - int16_t norm_shift2 = std::max(18 - WebRtcSpl_NormW32(max_correlation), 0); - WebRtcSpl_VectorBitShiftW32ToW16(output, kNumCorrelationLags, correlation, - norm_shift2); - // Total scale factor (right shifts) of correlation value. - *output_scale = 2 * norm_shift + kCorrelationShift + norm_shift2; - return kNumCorrelationLags; -} - -void Expand::UpdateLagIndex() { - current_lag_index_ = current_lag_index_ + lag_index_direction_; - // Change direction if needed. - if (current_lag_index_ <= 0) { - lag_index_direction_ = 1; - } - if (current_lag_index_ >= kNumLags - 1) { - lag_index_direction_ = -1; - } -} - -Expand* ExpandFactory::Create(BackgroundNoise* background_noise, - SyncBuffer* sync_buffer, - RandomVector* random_vector, - int fs, - size_t num_channels) const { - return new Expand(background_noise, sync_buffer, random_vector, fs, - num_channels); -} - - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/expand.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/expand.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/expand.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/expand.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_EXPAND_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_EXPAND_H_ - -#include - -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Forward declarations. -class BackgroundNoise; -class RandomVector; -class SyncBuffer; - -// This class handles extrapolation of audio data from the sync_buffer to -// produce packet-loss concealment. -// TODO(hlundin): Refactor this class to divide the long methods into shorter -// ones. -class Expand { - public: - Expand(BackgroundNoise* background_noise, - SyncBuffer* sync_buffer, - RandomVector* random_vector, - int fs, - size_t num_channels) - : background_noise_(background_noise), - sync_buffer_(sync_buffer), - random_vector_(random_vector), - first_expand_(true), - fs_hz_(fs), - num_channels_(num_channels), - overlap_length_(5 * fs / 8000), - lag_index_direction_(0), - current_lag_index_(0), - stop_muting_(false), - channel_parameters_(new ChannelParameters[num_channels_]) { - assert(fs == 8000 || fs == 16000 || fs == 32000 || fs == 48000); - assert(fs <= kMaxSampleRate); // Should not be possible. - assert(num_channels_ > 0); - memset(expand_lags_, 0, sizeof(expand_lags_)); - Reset(); - } - - virtual ~Expand() {} - - // Resets the object. - void Reset(); - - // The main method to produce concealment data. The data is appended to the - // end of |output|. - int Process(AudioMultiVector* output); - - // Prepare the object to do extra expansion during normal operation following - // a period of expands. - void SetParametersForNormalAfterExpand(); - - // Prepare the object to do extra expansion during merge operation following - // a period of expands. - void SetParametersForMergeAfterExpand(); - - // Sets the mute factor for |channel| to |value|. - void SetMuteFactor(int16_t value, size_t channel) { - assert(channel < num_channels_); - channel_parameters_[channel].mute_factor = value; - } - - // Returns the mute factor for |channel|. - int16_t MuteFactor(size_t channel) { - assert(channel < num_channels_); - return channel_parameters_[channel].mute_factor; - } - - // Accessors and mutators. - size_t overlap_length() const { return overlap_length_; } - int16_t max_lag() const { return max_lag_; } - - private: - static const int kUnvoicedLpcOrder = 6; - static const int kNumCorrelationCandidates = 3; - static const int kDistortionLength = 20; - static const int kLpcAnalysisLength = 160; - static const int kMaxSampleRate = 48000; - static const int kNumLags = 3; - static const int kMaxConsecutiveExpands = 200; - - struct ChannelParameters { - // Constructor. - ChannelParameters() - : mute_factor(16384), - ar_gain(0), - ar_gain_scale(0), - voice_mix_factor(0), - current_voice_mix_factor(0), - onset(false), - mute_slope(0) { - memset(ar_filter, 0, sizeof(ar_filter)); - memset(ar_filter_state, 0, sizeof(ar_filter_state)); - } - int16_t mute_factor; - int16_t ar_filter[kUnvoicedLpcOrder + 1]; - int16_t ar_filter_state[kUnvoicedLpcOrder]; - int16_t ar_gain; - int16_t ar_gain_scale; - int16_t voice_mix_factor; /* Q14 */ - int16_t current_voice_mix_factor; /* Q14 */ - AudioVector expand_vector0; - AudioVector expand_vector1; - bool onset; - int16_t mute_slope; /* Q20 */ - }; - - // Analyze the signal history in |sync_buffer_|, and set up all parameters - // necessary to produce concealment data. - void AnalyzeSignal(int16_t* random_vector); - - // Calculate the auto-correlation of |input|, with length |input_length| - // samples. The correlation is calculated from a downsampled version of - // |input|, and is written to |output|. The scale factor is written to - // |output_scale|. Returns the length of the correlation vector. - int16_t Correlation(const int16_t* input, size_t input_length, - int16_t* output, int16_t* output_scale) const; - - void UpdateLagIndex(); - - BackgroundNoise* background_noise_; - SyncBuffer* sync_buffer_; - RandomVector* random_vector_; - bool first_expand_; - const int fs_hz_; - const size_t num_channels_; - const size_t overlap_length_; - int consecutive_expands_; - int16_t max_lag_; - size_t expand_lags_[kNumLags]; - int lag_index_direction_; - int current_lag_index_; - bool stop_muting_; - scoped_array channel_parameters_; - - DISALLOW_COPY_AND_ASSIGN(Expand); -}; - -struct ExpandFactory { - ExpandFactory() {} - virtual ~ExpandFactory() {} - - virtual Expand* Create(BackgroundNoise* background_noise, - SyncBuffer* sync_buffer, - RandomVector* random_vector, - int fs, - size_t num_channels) const; -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_EXPAND_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/expand_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/expand_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/expand_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/expand_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Unit tests for Expand class. - -#include "webrtc/modules/audio_coding/neteq4/expand.h" - -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq4/background_noise.h" -#include "webrtc/modules/audio_coding/neteq4/random_vector.h" -#include "webrtc/modules/audio_coding/neteq4/sync_buffer.h" - -namespace webrtc { - -TEST(Expand, CreateAndDestroy) { - int fs = 8000; - size_t channels = 1; - BackgroundNoise bgn(channels); - SyncBuffer sync_buffer(1, 1000); - RandomVector random_vector; - Expand expand(&bgn, &sync_buffer, &random_vector, fs, channels); -} - -TEST(Expand, CreateUsingFactory) { - int fs = 8000; - size_t channels = 1; - BackgroundNoise bgn(channels); - SyncBuffer sync_buffer(1, 1000); - RandomVector random_vector; - ExpandFactory expand_factory; - Expand* expand = - expand_factory.Create(&bgn, &sync_buffer, &random_vector, fs, channels); - EXPECT_TRUE(expand != NULL); - delete expand; -} - -// TODO(hlundin): Write more tests. - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_INTERFACE_AUDIO_DECODER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_INTERFACE_AUDIO_DECODER_H_ - -#include // NULL - -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -enum NetEqDecoder { - kDecoderPCMu, - kDecoderPCMa, - kDecoderPCMu_2ch, - kDecoderPCMa_2ch, - kDecoderILBC, - kDecoderISAC, - kDecoderISACswb, - kDecoderISACfb, - kDecoderPCM16B, - kDecoderPCM16Bwb, - kDecoderPCM16Bswb32kHz, - kDecoderPCM16Bswb48kHz, - kDecoderPCM16B_2ch, - kDecoderPCM16Bwb_2ch, - kDecoderPCM16Bswb32kHz_2ch, - kDecoderPCM16Bswb48kHz_2ch, - kDecoderPCM16B_5ch, - kDecoderG722, - kDecoderG722_2ch, - kDecoderRED, - kDecoderAVT, - kDecoderCNGnb, - kDecoderCNGwb, - kDecoderCNGswb32kHz, - kDecoderCNGswb48kHz, - kDecoderArbitrary, - kDecoderOpus, - kDecoderOpus_2ch, - kDecoderCELT_32, - kDecoderCELT_32_2ch, -}; - -// This is the interface class for decoders in NetEQ. Each codec type will have -// and implementation of this class. -class AudioDecoder { - public: - enum SpeechType { - kSpeech = 1, - kComfortNoise = 2 - }; - - // Used by PacketDuration below. Save the value -1 for errors. - enum { kNotImplemented = -2 }; - - explicit AudioDecoder(enum NetEqDecoder type) - : codec_type_(type), - channels_(1), - state_(NULL) { - } - - virtual ~AudioDecoder() {} - - // Decodes |encode_len| bytes from |encoded| and writes the result in - // |decoded|. The number of samples from all channels produced is in - // the return value. If the decoder produced comfort noise, |speech_type| - // is set to kComfortNoise, otherwise it is kSpeech. - virtual int Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type) = 0; - - // Same as Decode(), but interfaces to the decoders redundant decode function. - // The default implementation simply calls the regular Decode() method. - virtual int DecodeRedundant(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type); - - // Indicates if the decoder implements the DecodePlc method. - virtual bool HasDecodePlc() const; - - // Calls the packet-loss concealment of the decoder to update the state after - // one or several lost packets. - virtual int DecodePlc(int num_frames, int16_t* decoded); - - // Initializes the decoder. - virtual int Init() = 0; - - // Notifies the decoder of an incoming packet to NetEQ. - virtual int IncomingPacket(const uint8_t* payload, - size_t payload_len, - uint16_t rtp_sequence_number, - uint32_t rtp_timestamp, - uint32_t arrival_timestamp); - - // Returns the last error code from the decoder. - virtual int ErrorCode(); - - // Returns the duration in samples of the payload in |encoded| which is - // |encoded_len| bytes long. Returns kNotImplemented if no duration estimate - // is available, or -1 in case of an error. - virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); - - virtual NetEqDecoder codec_type() const; - - // Returns the underlying decoder state. - void* state() { return state_; } - - // Returns true if |codec_type| is supported. - static bool CodecSupported(NetEqDecoder codec_type); - - // Returns the sample rate for |codec_type|. - static int CodecSampleRateHz(NetEqDecoder codec_type); - - // Creates an AudioDecoder object of type |codec_type|. Returns NULL for - // for unsupported codecs, and when creating an AudioDecoder is not - // applicable (e.g., for RED and DTMF/AVT types). - static AudioDecoder* CreateAudioDecoder(NetEqDecoder codec_type); - - size_t channels() const { return channels_; } - - protected: - static SpeechType ConvertSpeechType(int16_t type); - - enum NetEqDecoder codec_type_; - size_t channels_; - void* state_; - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoder); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_INTERFACE_AUDIO_DECODER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/interface/neteq.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/interface/neteq.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/interface/neteq.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/interface/neteq.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,267 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_INTERFACE_NETEQ_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_INTERFACE_NETEQ_H_ - -#include // Provide access to size_t. - -#include - -#include "webrtc/common_types.h" -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Forward declarations. -struct WebRtcRTPHeader; - -struct NetEqNetworkStatistics { - uint16_t current_buffer_size_ms; // Current jitter buffer size in ms. - uint16_t preferred_buffer_size_ms; // Target buffer size in ms. - uint16_t jitter_peaks_found; // 1 if adding extra delay due to peaky - // jitter; 0 otherwise. - uint16_t packet_loss_rate; // Loss rate (network + late) in Q14. - uint16_t packet_discard_rate; // Late loss rate in Q14. - uint16_t expand_rate; // Fraction (of original stream) of synthesized - // speech inserted through expansion (in Q14). - uint16_t preemptive_rate; // Fraction of data inserted through pre-emptive - // expansion (in Q14). - uint16_t accelerate_rate; // Fraction of data removed through acceleration - // (in Q14). - int32_t clockdrift_ppm; // Average clock-drift in parts-per-million - // (positive or negative). - int added_zero_samples; // Number of zero samples added in "off" mode. -}; - -enum NetEqOutputType { - kOutputNormal, - kOutputPLC, - kOutputCNG, - kOutputPLCtoCNG, - kOutputVADPassive -}; - -enum NetEqPlayoutMode { - kPlayoutOn, - kPlayoutOff, - kPlayoutFax, - kPlayoutStreaming -}; - -enum NetEqBackgroundNoiseMode { - kBgnOn, // Default behavior with eternal noise. - kBgnFade, // Noise fades to zero after some time. - kBgnOff // Background noise is always zero. -}; - -// This is the interface class for NetEq. -class NetEq { - public: - enum ReturnCodes { - kOK = 0, - kFail = -1, - kNotImplemented = -2 - }; - - enum ErrorCodes { - kNoError = 0, - kOtherError, - kInvalidRtpPayloadType, - kUnknownRtpPayloadType, - kCodecNotSupported, - kDecoderExists, - kDecoderNotFound, - kInvalidSampleRate, - kInvalidPointer, - kAccelerateError, - kPreemptiveExpandError, - kComfortNoiseErrorCode, - kDecoderErrorCode, - kOtherDecoderError, - kInvalidOperation, - kDtmfParameterError, - kDtmfParsingError, - kDtmfInsertError, - kStereoNotSupported, - kSampleUnderrun, - kDecodedTooMuch, - kFrameSplitError, - kRedundancySplitError, - kPacketBufferCorruption, - kOversizePacket, - kSyncPacketNotAccepted - }; - - static const int kMaxNumPacketsInBuffer = 240; // TODO(hlundin): Remove. - static const int kMaxBytesInBuffer = 113280; // TODO(hlundin): Remove. - - // Creates a new NetEq object, starting at the sample rate |sample_rate_hz|. - // (Note that it will still change the sample rate depending on what payloads - // are being inserted; |sample_rate_hz| is just for startup configuration.) - static NetEq* Create(int sample_rate_hz); - - virtual ~NetEq() {} - - // Inserts a new packet into NetEq. The |receive_timestamp| is an indication - // of the time when the packet was received, and should be measured with - // the same tick rate as the RTP timestamp of the current payload. - // Returns 0 on success, -1 on failure. - virtual int InsertPacket(const WebRtcRTPHeader& rtp_header, - const uint8_t* payload, - int length_bytes, - uint32_t receive_timestamp) = 0; - - // Inserts a sync-packet into packet queue. Sync-packets are decoded to - // silence and are intended to keep AV-sync intact in an event of long packet - // losses when Video NACK is enabled but Audio NACK is not. Clients of NetEq - // might insert sync-packet when they observe that buffer level of NetEq is - // decreasing below a certain threshold, defined by the application. - // Sync-packets should have the same payload type as the last audio payload - // type, i.e. they cannot have DTMF or CNG payload type, nor a codec change - // can be implied by inserting a sync-packet. - // Returns kOk on success, kFail on failure. - virtual int InsertSyncPacket(const WebRtcRTPHeader& rtp_header, - uint32_t receive_timestamp) = 0; - - // Instructs NetEq to deliver 10 ms of audio data. The data is written to - // |output_audio|, which can hold (at least) |max_length| elements. - // The number of channels that were written to the output is provided in - // the output variable |num_channels|, and each channel contains - // |samples_per_channel| elements. If more than one channel is written, - // the samples are interleaved. - // The speech type is written to |type|, if |type| is not NULL. - // Returns kOK on success, or kFail in case of an error. - virtual int GetAudio(size_t max_length, int16_t* output_audio, - int* samples_per_channel, int* num_channels, - NetEqOutputType* type) = 0; - - // Associates |rtp_payload_type| with |codec| and stores the information in - // the codec database. Returns 0 on success, -1 on failure. - virtual int RegisterPayloadType(enum NetEqDecoder codec, - uint8_t rtp_payload_type) = 0; - - // Provides an externally created decoder object |decoder| to insert in the - // decoder database. The decoder implements a decoder of type |codec| and - // associates it with |rtp_payload_type|. The decoder operates at the - // frequency |sample_rate_hz|. Returns kOK on success, kFail on failure. - virtual int RegisterExternalDecoder(AudioDecoder* decoder, - enum NetEqDecoder codec, - int sample_rate_hz, - uint8_t rtp_payload_type) = 0; - - // Removes |rtp_payload_type| from the codec database. Returns 0 on success, - // -1 on failure. - virtual int RemovePayloadType(uint8_t rtp_payload_type) = 0; - - // Sets a minimum delay in millisecond for packet buffer. The minimum is - // maintained unless a higher latency is dictated by channel condition. - // Returns true if the minimum is successfully applied, otherwise false is - // returned. - virtual bool SetMinimumDelay(int delay_ms) = 0; - - // Sets a maximum delay in milliseconds for packet buffer. The latency will - // not exceed the given value, even required delay (given the channel - // conditions) is higher. - virtual bool SetMaximumDelay(int delay_ms) = 0; - - // The smallest latency required. This is computed bases on inter-arrival - // time and internal NetEq logic. Note that in computing this latency none of - // the user defined limits (applied by calling setMinimumDelay() and/or - // SetMaximumDelay()) are applied. - virtual int LeastRequiredDelayMs() const = 0; - - // Not implemented. - virtual int SetTargetDelay() = 0; - - // Not implemented. - virtual int TargetDelay() = 0; - - // Not implemented. - virtual int CurrentDelay() = 0; - - // Sets the playout mode to |mode|. - virtual void SetPlayoutMode(NetEqPlayoutMode mode) = 0; - - // Returns the current playout mode. - virtual NetEqPlayoutMode PlayoutMode() const = 0; - - // Writes the current network statistics to |stats|. The statistics are reset - // after the call. - virtual int NetworkStatistics(NetEqNetworkStatistics* stats) = 0; - - // Writes the last packet waiting times (in ms) to |waiting_times|. The number - // of values written is no more than 100, but may be smaller if the interface - // is polled again before 100 packets has arrived. - virtual void WaitingTimes(std::vector* waiting_times) = 0; - - // Writes the current RTCP statistics to |stats|. The statistics are reset - // and a new report period is started with the call. - virtual void GetRtcpStatistics(RtcpStatistics* stats) = 0; - - // Same as RtcpStatistics(), but does not reset anything. - virtual void GetRtcpStatisticsNoReset(RtcpStatistics* stats) = 0; - - // Enables post-decode VAD. When enabled, GetAudio() will return - // kOutputVADPassive when the signal contains no speech. - virtual void EnableVad() = 0; - - // Disables post-decode VAD. - virtual void DisableVad() = 0; - - // Returns the RTP timestamp for the last sample delivered by GetAudio(). - virtual uint32_t PlayoutTimestamp() = 0; - - // Not implemented. - virtual int SetTargetNumberOfChannels() = 0; - - // Not implemented. - virtual int SetTargetSampleRate() = 0; - - // Returns the error code for the last occurred error. If no error has - // occurred, 0 is returned. - virtual int LastError() = 0; - - // Returns the error code last returned by a decoder (audio or comfort noise). - // When LastError() returns kDecoderErrorCode or kComfortNoiseErrorCode, check - // this method to get the decoder's error code. - virtual int LastDecoderError() = 0; - - // Flushes both the packet buffer and the sync buffer. - virtual void FlushBuffers() = 0; - - // Current usage of packet-buffer and it's limits. - virtual void PacketBufferStatistics(int* current_num_packets, - int* max_num_packets, - int* current_memory_size_bytes, - int* max_memory_size_bytes) const = 0; - - // Get sequence number and timestamp of the latest RTP. - // This method is to facilitate NACK. - virtual int DecodedRtpInfo(int* sequence_number, - uint32_t* timestamp) const = 0; - - // Sets the background noise mode. - virtual void SetBackgroundNoiseMode(NetEqBackgroundNoiseMode mode) = 0; - - // Gets the background noise mode. - virtual NetEqBackgroundNoiseMode BackgroundNoiseMode() const = 0; - - protected: - NetEq() {} - - private: - DISALLOW_COPY_AND_ASSIGN(NetEq); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_INTERFACE_NETEQ_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/merge.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/merge.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/merge.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/merge.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,360 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/merge.h" - -#include -#include // memmove, memcpy, memset, size_t - -#include // min, max - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" -#include "webrtc/modules/audio_coding/neteq4/dsp_helper.h" -#include "webrtc/modules/audio_coding/neteq4/expand.h" -#include "webrtc/modules/audio_coding/neteq4/sync_buffer.h" - -namespace webrtc { - -int Merge::Process(int16_t* input, size_t input_length, - int16_t* external_mute_factor_array, - AudioMultiVector* output) { - // TODO(hlundin): Change to an enumerator and skip assert. - assert(fs_hz_ == 8000 || fs_hz_ == 16000 || fs_hz_ == 32000 || - fs_hz_ == 48000); - assert(fs_hz_ <= kMaxSampleRate); // Should not be possible. - - int old_length; - int expand_period; - // Get expansion data to overlap and mix with. - int expanded_length = GetExpandedSignal(&old_length, &expand_period); - - // Transfer input signal to an AudioMultiVector. - AudioMultiVector input_vector(num_channels_); - input_vector.PushBackInterleaved(input, input_length); - size_t input_length_per_channel = input_vector.Size(); - assert(input_length_per_channel == input_length / num_channels_); - - int16_t best_correlation_index = 0; - size_t output_length = 0; - - for (size_t channel = 0; channel < num_channels_; ++channel) { - int16_t* input_channel = &input_vector[channel][0]; - int16_t* expanded_channel = &expanded_[channel][0]; - int16_t expanded_max, input_max; - int16_t new_mute_factor = SignalScaling( - input_channel, static_cast(input_length_per_channel), - expanded_channel, &expanded_max, &input_max); - - // Adjust muting factor (product of "main" muting factor and expand muting - // factor). - int16_t* external_mute_factor = &external_mute_factor_array[channel]; - *external_mute_factor = - (*external_mute_factor * expand_->MuteFactor(channel)) >> 14; - - // Update |external_mute_factor| if it is lower than |new_mute_factor|. - if (new_mute_factor > *external_mute_factor) { - *external_mute_factor = std::min(new_mute_factor, - static_cast(16384)); - } - - if (channel == 0) { - // Downsample, correlate, and find strongest correlation period for the - // master (i.e., first) channel only. - // Downsample to 4kHz sample rate. - Downsample(input_channel, static_cast(input_length_per_channel), - expanded_channel, expanded_length); - - // Calculate the lag of the strongest correlation period. - best_correlation_index = CorrelateAndPeakSearch( - expanded_max, input_max, old_length, - static_cast(input_length_per_channel), expand_period); - } - - static const int kTempDataSize = 3600; - int16_t temp_data[kTempDataSize]; // TODO(hlundin) Remove this. - int16_t* decoded_output = temp_data + best_correlation_index; - - // Mute the new decoded data if needed (and unmute it linearly). - // This is the overlapping part of expanded_signal. - int interpolation_length = std::min( - kMaxCorrelationLength * fs_mult_, - expanded_length - best_correlation_index); - interpolation_length = std::min(interpolation_length, - static_cast(input_length_per_channel)); - if (*external_mute_factor < 16384) { - // Set a suitable muting slope (Q20). 0.004 for NB, 0.002 for WB, - // and so on. - int increment = 4194 / fs_mult_; - *external_mute_factor = DspHelper::RampSignal(input_channel, - interpolation_length, - *external_mute_factor, - increment); - DspHelper::UnmuteSignal(&input_channel[interpolation_length], - input_length_per_channel - interpolation_length, - external_mute_factor, increment, - &decoded_output[interpolation_length]); - } else { - // No muting needed. - memmove( - &decoded_output[interpolation_length], - &input_channel[interpolation_length], - sizeof(int16_t) * (input_length_per_channel - interpolation_length)); - } - - // Do overlap and mix linearly. - int increment = 16384 / (interpolation_length + 1); // In Q14. - int16_t mute_factor = 16384 - increment; - memmove(temp_data, expanded_channel, - sizeof(int16_t) * best_correlation_index); - DspHelper::CrossFade(&expanded_channel[best_correlation_index], - input_channel, interpolation_length, - &mute_factor, increment, decoded_output); - - output_length = best_correlation_index + input_length_per_channel; - if (channel == 0) { - assert(output->Empty()); // Output should be empty at this point. - output->AssertSize(output_length); - } else { - assert(output->Size() == output_length); - } - memcpy(&(*output)[channel][0], temp_data, - sizeof(temp_data[0]) * output_length); - } - - // Copy back the first part of the data to |sync_buffer_| and remove it from - // |output|. - sync_buffer_->ReplaceAtIndex(*output, old_length, sync_buffer_->next_index()); - output->PopFront(old_length); - - // Return new added length. |old_length| samples were borrowed from - // |sync_buffer_|. - return static_cast(output_length) - old_length; -} - -int Merge::GetExpandedSignal(int* old_length, int* expand_period) { - // Check how much data that is left since earlier. - *old_length = static_cast(sync_buffer_->FutureLength()); - // Should never be less than overlap_length. - assert(*old_length >= static_cast(expand_->overlap_length())); - // Generate data to merge the overlap with using expand. - expand_->SetParametersForMergeAfterExpand(); - - if (*old_length >= 210 * kMaxSampleRate / 8000) { - // TODO(hlundin): Write test case for this. - // The number of samples available in the sync buffer is more than what fits - // in expanded_signal. Keep the first 210 * kMaxSampleRate / 8000 samples, - // but shift them towards the end of the buffer. This is ok, since all of - // the buffer will be expand data anyway, so as long as the beginning is - // left untouched, we're fine. - int16_t length_diff = *old_length - 210 * kMaxSampleRate / 8000; - sync_buffer_->InsertZerosAtIndex(length_diff, sync_buffer_->next_index()); - *old_length = 210 * kMaxSampleRate / 8000; - // This is the truncated length. - } - // This assert should always be true thanks to the if statement above. - assert(210 * kMaxSampleRate / 8000 - *old_length >= 0); - - AudioMultiVector expanded_temp(num_channels_); - expand_->Process(&expanded_temp); - *expand_period = static_cast(expanded_temp.Size()); // Samples per - // channel. - - expanded_.Clear(); - // Copy what is left since earlier into the expanded vector. - expanded_.PushBackFromIndex(*sync_buffer_, sync_buffer_->next_index()); - assert(expanded_.Size() == static_cast(*old_length)); - assert(expanded_temp.Size() > 0); - // Do "ugly" copy and paste from the expanded in order to generate more data - // to correlate (but not interpolate) with. - const int required_length = (120 + 80 + 2) * fs_mult_; - if (expanded_.Size() < static_cast(required_length)) { - while (expanded_.Size() < static_cast(required_length)) { - // Append one more pitch period each time. - expanded_.PushBack(expanded_temp); - } - // Trim the length to exactly |required_length|. - expanded_.PopBack(expanded_.Size() - required_length); - } - assert(expanded_.Size() >= static_cast(required_length)); - return required_length; -} - -int16_t Merge::SignalScaling(const int16_t* input, int input_length, - const int16_t* expanded_signal, - int16_t* expanded_max, int16_t* input_max) const { - // Adjust muting factor if new vector is more or less of the BGN energy. - const int mod_input_length = std::min(64 * fs_mult_, input_length); - *expanded_max = WebRtcSpl_MaxAbsValueW16(expanded_signal, mod_input_length); - *input_max = WebRtcSpl_MaxAbsValueW16(input, mod_input_length); - - // Calculate energy of expanded signal. - // |log_fs_mult| is log2(fs_mult_), but is not exact for 48000 Hz. - int log_fs_mult = 30 - WebRtcSpl_NormW32(fs_mult_); - int expanded_shift = 6 + log_fs_mult - - WebRtcSpl_NormW32(*expanded_max * *expanded_max); - expanded_shift = std::max(expanded_shift, 0); - int32_t energy_expanded = WebRtcSpl_DotProductWithScale(expanded_signal, - expanded_signal, - mod_input_length, - expanded_shift); - - // Calculate energy of input signal. - int input_shift = 6 + log_fs_mult - - WebRtcSpl_NormW32(*input_max * *input_max); - input_shift = std::max(input_shift, 0); - int32_t energy_input = WebRtcSpl_DotProductWithScale(input, input, - mod_input_length, - input_shift); - - // Align to the same Q-domain. - if (input_shift > expanded_shift) { - energy_expanded = energy_expanded >> (input_shift - expanded_shift); - } else { - energy_input = energy_input >> (expanded_shift - input_shift); - } - - // Calculate muting factor to use for new frame. - int16_t mute_factor; - if (energy_input > energy_expanded) { - // Normalize |energy_input| to 14 bits. - int16_t temp_shift = WebRtcSpl_NormW32(energy_input) - 17; - energy_input = WEBRTC_SPL_SHIFT_W32(energy_input, temp_shift); - // Put |energy_expanded| in a domain 14 higher, so that - // energy_expanded / energy_input is in Q14. - energy_expanded = WEBRTC_SPL_SHIFT_W32(energy_expanded, temp_shift + 14); - // Calculate sqrt(energy_expanded / energy_input) in Q14. - mute_factor = WebRtcSpl_SqrtFloor((energy_expanded / energy_input) << 14); - } else { - // Set to 1 (in Q14) when |expanded| has higher energy than |input|. - mute_factor = 16384; - } - - return mute_factor; -} - -// TODO(hlundin): There are some parameter values in this method that seem -// strange. Compare with Expand::Correlation. -void Merge::Downsample(const int16_t* input, int input_length, - const int16_t* expanded_signal, int expanded_length) { - const int16_t* filter_coefficients; - int num_coefficients; - int decimation_factor = fs_hz_ / 4000; - static const int kCompensateDelay = 0; - int length_limit = fs_hz_ / 100; - if (fs_hz_ == 8000) { - filter_coefficients = DspHelper::kDownsample8kHzTbl; - num_coefficients = 3; - } else if (fs_hz_ == 16000) { - filter_coefficients = DspHelper::kDownsample16kHzTbl; - num_coefficients = 5; - } else if (fs_hz_ == 32000) { - filter_coefficients = DspHelper::kDownsample32kHzTbl; - num_coefficients = 7; - } else { // fs_hz_ == 48000 - filter_coefficients = DspHelper::kDownsample48kHzTbl; - num_coefficients = 7; - // TODO(hlundin) Why is |length_limit| not 480 (legacy)? - length_limit = 320; - } - int signal_offset = num_coefficients - 1; - WebRtcSpl_DownsampleFast(&expanded_signal[signal_offset], - expanded_length - signal_offset, - expanded_downsampled_, kExpandDownsampLength, - filter_coefficients, num_coefficients, - decimation_factor, kCompensateDelay); - if (input_length <= length_limit) { - // Not quite long enough, so we have to cheat a bit. - int16_t temp_len = input_length - signal_offset; - // TODO(hlundin): Should |downsamp_temp_len| be corrected for round-off - // errors? I.e., (temp_len + decimation_factor - 1) / decimation_factor? - int16_t downsamp_temp_len = temp_len / decimation_factor; - WebRtcSpl_DownsampleFast(&input[signal_offset], temp_len, - input_downsampled_, downsamp_temp_len, - filter_coefficients, num_coefficients, - decimation_factor, kCompensateDelay); - memset(&input_downsampled_[downsamp_temp_len], 0, - sizeof(int16_t) * (kInputDownsampLength - downsamp_temp_len)); - } else { - WebRtcSpl_DownsampleFast(&input[signal_offset], - input_length - signal_offset, input_downsampled_, - kInputDownsampLength, filter_coefficients, - num_coefficients, decimation_factor, - kCompensateDelay); - } -} - -int16_t Merge::CorrelateAndPeakSearch(int16_t expanded_max, int16_t input_max, - int start_position, int input_length, - int expand_period) const { - // Calculate correlation without any normalization. - const int max_corr_length = kMaxCorrelationLength; - int stop_position_downsamp = std::min( - max_corr_length, expand_->max_lag() / (fs_mult_ * 2) + 1); - int16_t correlation_shift = 0; - if (expanded_max * input_max > 26843546) { - correlation_shift = 3; - } - - int32_t correlation[kMaxCorrelationLength]; - WebRtcSpl_CrossCorrelation(correlation, input_downsampled_, - expanded_downsampled_, kInputDownsampLength, - stop_position_downsamp, correlation_shift, 1); - - // Normalize correlation to 14 bits and copy to a 16-bit array. - static const int kPadLength = 4; - int16_t correlation16[kPadLength + kMaxCorrelationLength + kPadLength] = {0}; - int16_t* correlation_ptr = &correlation16[kPadLength]; - int32_t max_correlation = WebRtcSpl_MaxAbsValueW32(correlation, - stop_position_downsamp); - int16_t norm_shift = std::max(0, 17 - WebRtcSpl_NormW32(max_correlation)); - WebRtcSpl_VectorBitShiftW32ToW16(correlation_ptr, stop_position_downsamp, - correlation, norm_shift); - - // Calculate allowed starting point for peak finding. - // The peak location bestIndex must fulfill two criteria: - // (1) w16_bestIndex + input_length < - // timestamps_per_call_ + expand_->overlap_length(); - // (2) w16_bestIndex + input_length < start_position. - int start_index = timestamps_per_call_ + - static_cast(expand_->overlap_length()); - start_index = std::max(start_position, start_index); - start_index = std::max(start_index - input_length, 0); - // Downscale starting index to 4kHz domain. (fs_mult_ * 2 = fs_hz_ / 4000.) - int start_index_downsamp = start_index / (fs_mult_ * 2); - - // Calculate a modified |stop_position_downsamp| to account for the increased - // start index |start_index_downsamp| and the effective array length. - int modified_stop_pos = - std::min(stop_position_downsamp, - kMaxCorrelationLength + kPadLength - start_index_downsamp); - int best_correlation_index; - int16_t best_correlation; - static const int kNumCorrelationCandidates = 1; - DspHelper::PeakDetection(&correlation_ptr[start_index_downsamp], - modified_stop_pos, kNumCorrelationCandidates, - fs_mult_, &best_correlation_index, - &best_correlation); - // Compensate for modified start index. - best_correlation_index += start_index; - - // Ensure that underrun does not occur for 10ms case => we have to get at - // least 10ms + overlap . (This should never happen thanks to the above - // modification of peak-finding starting point.) - while ((best_correlation_index + input_length) < - static_cast(timestamps_per_call_ + expand_->overlap_length()) || - best_correlation_index + input_length < start_position) { - assert(false); // Should never happen. - best_correlation_index += expand_period; // Jump one lag ahead. - } - return best_correlation_index; -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/merge.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/merge.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/merge.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/merge.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MERGE_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MERGE_H_ - -#include - -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Forward declarations. -class Expand; -class SyncBuffer; - -// This class handles the transition from expansion to normal operation. -// When a packet is not available for decoding when needed, the expand operation -// is called to generate extrapolation data. If the missing packet arrives, -// i.e., it was just delayed, it can be decoded and appended directly to the -// end of the expanded data (thanks to how the Expand class operates). However, -// if a later packet arrives instead, the loss is a fact, and the new data must -// be stitched together with the end of the expanded data. This stitching is -// what the Merge class does. -class Merge { - public: - Merge(int fs_hz, size_t num_channels, Expand* expand, SyncBuffer* sync_buffer) - : fs_hz_(fs_hz), - fs_mult_(fs_hz_ / 8000), - num_channels_(num_channels), - timestamps_per_call_(fs_hz_ / 100), - expand_(expand), - sync_buffer_(sync_buffer), - expanded_(num_channels_) { - assert(num_channels_ > 0); - } - - // The main method to produce the audio data. The decoded data is supplied in - // |input|, having |input_length| samples in total for all channels - // (interleaved). The result is written to |output|. The number of channels - // allocated in |output| defines the number of channels that will be used when - // de-interleaving |input|. The values in |external_mute_factor_array| (Q14) - // will be used to scale the audio, and is updated in the process. The array - // must have |num_channels_| elements. - int Process(int16_t* input, size_t input_length, - int16_t* external_mute_factor_array, - AudioMultiVector* output); - - private: - static const int kMaxSampleRate = 48000; - static const int kExpandDownsampLength = 100; - static const int kInputDownsampLength = 40; - static const int kMaxCorrelationLength = 60; - - // Calls |expand_| to get more expansion data to merge with. The data is - // written to |expanded_signal_|. Returns the length of the expanded data, - // while |expand_period| will be the number of samples in one expansion period - // (typically one pitch period). The value of |old_length| will be the number - // of samples that were taken from the |sync_buffer_|. - int GetExpandedSignal(int* old_length, int* expand_period); - - // Analyzes |input| and |expanded_signal| to find maximum values. Returns - // a muting factor (Q14) to be used on the new data. - int16_t SignalScaling(const int16_t* input, int input_length, - const int16_t* expanded_signal, - int16_t* expanded_max, int16_t* input_max) const; - - // Downsamples |input| (|input_length| samples) and |expanded_signal| to - // 4 kHz sample rate. The downsampled signals are written to - // |input_downsampled_| and |expanded_downsampled_|, respectively. - void Downsample(const int16_t* input, int input_length, - const int16_t* expanded_signal, int expanded_length); - - // Calculates cross-correlation between |input_downsampled_| and - // |expanded_downsampled_|, and finds the correlation maximum. The maximizing - // lag is returned. - int16_t CorrelateAndPeakSearch(int16_t expanded_max, int16_t input_max, - int start_position, int input_length, - int expand_period) const; - - const int fs_hz_; - const int fs_mult_; // fs_hz_ / 8000. - const size_t num_channels_; - const int timestamps_per_call_; - Expand* expand_; - SyncBuffer* sync_buffer_; - int16_t expanded_downsampled_[kExpandDownsampLength]; - int16_t input_downsampled_[kInputDownsampLength]; - AudioMultiVector expanded_; - - DISALLOW_COPY_AND_ASSIGN(Merge); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MERGE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/merge_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/merge_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/merge_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/merge_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Unit tests for Merge class. - -#include "webrtc/modules/audio_coding/neteq4/merge.h" - -#include - -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq4/background_noise.h" -#include "webrtc/modules/audio_coding/neteq4/expand.h" -#include "webrtc/modules/audio_coding/neteq4/random_vector.h" -#include "webrtc/modules/audio_coding/neteq4/sync_buffer.h" - -namespace webrtc { - -TEST(Merge, CreateAndDestroy) { - int fs = 8000; - size_t channels = 1; - BackgroundNoise bgn(channels); - SyncBuffer sync_buffer(1, 1000); - RandomVector random_vector; - Expand expand(&bgn, &sync_buffer, &random_vector, fs, channels); - Merge merge(fs, channels, &expand, &sync_buffer); -} - -// TODO(hlundin): Write more tests. - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_audio_decoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_audio_decoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_audio_decoder.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_audio_decoder.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_AUDIO_DECODER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_AUDIO_DECODER_H_ - -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" - -#include "gmock/gmock.h" - -namespace webrtc { - -class MockAudioDecoder : public AudioDecoder { - public: - MockAudioDecoder() : AudioDecoder(kDecoderArbitrary) {} - virtual ~MockAudioDecoder() { Die(); } - MOCK_METHOD0(Die, void()); - MOCK_METHOD4(Decode, int(const uint8_t*, size_t, int16_t*, - AudioDecoder::SpeechType*)); - MOCK_CONST_METHOD0(HasDecodePlc, bool()); - MOCK_METHOD2(DecodePlc, int(int, int16_t*)); - MOCK_METHOD0(Init, int()); - MOCK_METHOD5(IncomingPacket, int(const uint8_t*, size_t, uint16_t, uint32_t, - uint32_t)); - MOCK_METHOD0(ErrorCode, int()); - MOCK_CONST_METHOD0(codec_type, NetEqDecoder()); - MOCK_METHOD1(CodecSupported, bool(NetEqDecoder)); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_AUDIO_DECODER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_audio_vector.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_audio_vector.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_audio_vector.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_audio_vector.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_AUDIO_VECTOR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_AUDIO_VECTOR_H_ - -#include "webrtc/modules/audio_coding/neteq4/audio_vector.h" - -#include "gmock/gmock.h" - -namespace webrtc { - -class MockAudioVector : public AudioVector { - public: - MOCK_METHOD0(Clear, - void()); - MOCK_CONST_METHOD1(CopyFrom, - void(AudioVector* copy_to)); - MOCK_METHOD1(PushFront, - void(const AudioVector& prepend_this)); - MOCK_METHOD2(PushFront, - void(const T* prepend_this, size_t length)); - MOCK_METHOD1(PushBack, - void(const AudioVector& append_this)); - MOCK_METHOD2(PushBack, - void(const T* append_this, size_t length)); - MOCK_METHOD1(PopFront, - void(size_t length)); - MOCK_METHOD1(PopBack, - void(size_t length)); - MOCK_METHOD1(Extend, - void(size_t extra_length)); - MOCK_METHOD3(InsertAt, - void(const T* insert_this, size_t length, size_t position)); - MOCK_METHOD3(OverwriteAt, - void(const T* insert_this, size_t length, size_t position)); - MOCK_CONST_METHOD0(Size, - size_t()); - MOCK_CONST_METHOD0(Empty, - bool()); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_AUDIO_VECTOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_buffer_level_filter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_buffer_level_filter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_buffer_level_filter.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_buffer_level_filter.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_BUFFER_LEVEL_FILTER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_BUFFER_LEVEL_FILTER_H_ - -#include "webrtc/modules/audio_coding/neteq4/buffer_level_filter.h" - -#include "gmock/gmock.h" - -namespace webrtc { - -class MockBufferLevelFilter : public BufferLevelFilter { - public: - virtual ~MockBufferLevelFilter() { Die(); } - MOCK_METHOD0(Die, - void()); - MOCK_METHOD0(Reset, - void()); - MOCK_METHOD3(Update, - void(int buffer_size_packets, int time_stretched_samples, - int packet_len_samples)); - MOCK_METHOD1(SetTargetBufferLevel, - void(int target_buffer_level)); - MOCK_CONST_METHOD0(filtered_current_level, - int()); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_BUFFER_LEVEL_FILTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_decoder_database.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_decoder_database.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_decoder_database.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_decoder_database.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_DECODER_DATABASE_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_DECODER_DATABASE_H_ - -#include "webrtc/modules/audio_coding/neteq4/decoder_database.h" - -#include "gmock/gmock.h" - -namespace webrtc { - -class MockDecoderDatabase : public DecoderDatabase { - public: - virtual ~MockDecoderDatabase() { Die(); } - MOCK_METHOD0(Die, void()); - MOCK_CONST_METHOD0(Empty, - bool()); - MOCK_CONST_METHOD0(Size, - int()); - MOCK_METHOD0(Reset, - void()); - MOCK_METHOD2(RegisterPayload, - int(uint8_t rtp_payload_type, NetEqDecoder codec_type)); - MOCK_METHOD4(InsertExternal, - int(uint8_t rtp_payload_type, NetEqDecoder codec_type, int fs_hz, - AudioDecoder* decoder)); - MOCK_METHOD1(Remove, - int(uint8_t rtp_payload_type)); - MOCK_CONST_METHOD1(GetDecoderInfo, - const DecoderInfo*(uint8_t rtp_payload_type)); - MOCK_CONST_METHOD1(GetRtpPayloadType, - uint8_t(NetEqDecoder codec_type)); - MOCK_METHOD1(GetDecoder, - AudioDecoder*(uint8_t rtp_payload_type)); - MOCK_CONST_METHOD2(IsType, - bool(uint8_t rtp_payload_type, NetEqDecoder codec_type)); - MOCK_CONST_METHOD1(IsComfortNoise, - bool(uint8_t rtp_payload_type)); - MOCK_CONST_METHOD1(IsDtmf, - bool(uint8_t rtp_payload_type)); - MOCK_CONST_METHOD1(IsRed, - bool(uint8_t rtp_payload_type)); - MOCK_METHOD2(SetActiveDecoder, - int(uint8_t rtp_payload_type, bool* new_decoder)); - MOCK_METHOD0(GetActiveDecoder, - AudioDecoder*()); - MOCK_METHOD1(SetActiveCngDecoder, - int(uint8_t rtp_payload_type)); - MOCK_METHOD0(GetActiveCngDecoder, - AudioDecoder*()); - MOCK_CONST_METHOD1(CheckPayloadTypes, - int(const PacketList& packet_list)); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_DECODER_DATABASE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_delay_manager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_delay_manager.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_delay_manager.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_delay_manager.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_DELAY_MANAGER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_DELAY_MANAGER_H_ - -#include "webrtc/modules/audio_coding/neteq4/delay_manager.h" - -#include "gmock/gmock.h" - -namespace webrtc { - -class MockDelayManager : public DelayManager { - public: - MockDelayManager(int max_packets_in_buffer, DelayPeakDetector* peak_detector) - : DelayManager(max_packets_in_buffer, peak_detector) {} - virtual ~MockDelayManager() { Die(); } - MOCK_METHOD0(Die, void()); - MOCK_CONST_METHOD0(iat_vector, - const IATVector&()); - MOCK_METHOD3(Update, - int(uint16_t sequence_number, uint32_t timestamp, int sample_rate_hz)); - MOCK_METHOD1(CalculateTargetLevel, - int(int iat_packets)); - MOCK_METHOD1(SetPacketAudioLength, - int(int length_ms)); - MOCK_METHOD0(Reset, - void()); - MOCK_CONST_METHOD0(AverageIAT, - int()); - MOCK_CONST_METHOD0(PeakFound, - bool()); - MOCK_METHOD1(UpdateCounters, - void(int elapsed_time_ms)); - MOCK_METHOD0(ResetPacketIatCount, - void()); - MOCK_CONST_METHOD2(BufferLimits, - void(int* lower_limit, int* higher_limit)); - MOCK_CONST_METHOD0(TargetLevel, - int()); - MOCK_METHOD1(LastDecoderType, - void(NetEqDecoder decoder_type)); - MOCK_METHOD1(set_extra_delay_ms, - void(int16_t delay)); - MOCK_CONST_METHOD0(base_target_level, - int()); - MOCK_METHOD1(set_streaming_mode, - void(bool value)); - MOCK_CONST_METHOD0(last_pack_cng_or_dtmf, - int()); - MOCK_METHOD1(set_last_pack_cng_or_dtmf, - void(int value)); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_DELAY_MANAGER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_delay_peak_detector.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_delay_peak_detector.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_delay_peak_detector.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_delay_peak_detector.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_DELAY_PEAK_DETECTOR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_DELAY_PEAK_DETECTOR_H_ - -#include "webrtc/modules/audio_coding/neteq4/delay_peak_detector.h" - -#include "gmock/gmock.h" - -namespace webrtc { - -class MockDelayPeakDetector : public DelayPeakDetector { - public: - virtual ~MockDelayPeakDetector() { Die(); } - MOCK_METHOD0(Die, void()); - MOCK_METHOD0(Reset, void()); - MOCK_METHOD1(SetPacketAudioLength, void(int length_ms)); - MOCK_METHOD0(peak_found, bool()); - MOCK_CONST_METHOD0(MaxPeakHeight, int()); - MOCK_CONST_METHOD0(MaxPeakPeriod, int()); - MOCK_METHOD2(Update, bool(int inter_arrival_time, int target_level)); - MOCK_METHOD1(IncrementCounter, void(int inc_ms)); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_DELAY_PEAK_DETECTOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_dtmf_buffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_dtmf_buffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_dtmf_buffer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_dtmf_buffer.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_DTMF_BUFFER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_DTMF_BUFFER_H_ - -#include "webrtc/modules/audio_coding/neteq4/dtmf_buffer.h" - -#include "gmock/gmock.h" - -namespace webrtc { - -class MockDtmfBuffer : public DtmfBuffer { - public: - MockDtmfBuffer(int fs) : DtmfBuffer(fs) {} - virtual ~MockDtmfBuffer() { Die(); } - MOCK_METHOD0(Die, void()); - MOCK_METHOD0(Flush, - void()); - MOCK_METHOD1(InsertEvent, - int(const DtmfEvent& event)); - MOCK_METHOD2(GetEvent, - bool(uint32_t current_timestamp, DtmfEvent* event)); - MOCK_CONST_METHOD0(Length, - size_t()); - MOCK_CONST_METHOD0(Empty, - bool()); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_DTMF_BUFFER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_dtmf_tone_generator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_dtmf_tone_generator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_dtmf_tone_generator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_dtmf_tone_generator.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_DTMF_TONE_GENERATOR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_DTMF_TONE_GENERATOR_H_ - -#include "webrtc/modules/audio_coding/neteq4/dtmf_tone_generator.h" - -#include "gmock/gmock.h" - -namespace webrtc { - -class MockDtmfToneGenerator : public DtmfToneGenerator { - public: - virtual ~MockDtmfToneGenerator() { Die(); } - MOCK_METHOD0(Die, void()); - MOCK_METHOD3(Init, - int(int fs, int event, int attenuation)); - MOCK_METHOD0(Reset, - void()); - MOCK_METHOD2(Generate, - int(int num_samples, AudioMultiVector* output)); - MOCK_CONST_METHOD0(initialized, - bool()); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_DTMF_TONE_GENERATOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_external_decoder_pcm16b.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_external_decoder_pcm16b.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_external_decoder_pcm16b.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_external_decoder_pcm16b.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_EXTERNAL_DECODER_PCM16B_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_EXTERNAL_DECODER_PCM16B_H_ - -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" - -#include "gmock/gmock.h" -#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -using ::testing::_; -using ::testing::Invoke; - -// Implement an external version of the PCM16b decoder. This is a copy from -// audio_decoder_impl.{cc, h}. -class ExternalPcm16B : public AudioDecoder { - public: - explicit ExternalPcm16B(enum NetEqDecoder type) - : AudioDecoder(type) { - } - - virtual int Decode(const uint8_t* encoded, size_t encoded_len, - int16_t* decoded, SpeechType* speech_type) { - int16_t temp_type; - int16_t ret = WebRtcPcm16b_DecodeW16( - state_, reinterpret_cast(const_cast(encoded)), - static_cast(encoded_len), decoded, &temp_type); - *speech_type = ConvertSpeechType(temp_type); - return ret; - } - - virtual int Init() { return 0; } - - private: - DISALLOW_COPY_AND_ASSIGN(ExternalPcm16B); -}; - -// Create a mock of ExternalPcm16B which delegates all calls to the real object. -// The reason is that we can then track that the correct calls are being made. -class MockExternalPcm16B : public ExternalPcm16B { - public: - explicit MockExternalPcm16B(enum NetEqDecoder type) - : ExternalPcm16B(type), - real_(type) { - // By default, all calls are delegated to the real object. - ON_CALL(*this, Decode(_, _, _, _)) - .WillByDefault(Invoke(&real_, &ExternalPcm16B::Decode)); - ON_CALL(*this, HasDecodePlc()) - .WillByDefault(Invoke(&real_, &ExternalPcm16B::HasDecodePlc)); - ON_CALL(*this, DecodePlc(_, _)) - .WillByDefault(Invoke(&real_, &ExternalPcm16B::DecodePlc)); - ON_CALL(*this, Init()) - .WillByDefault(Invoke(&real_, &ExternalPcm16B::Init)); - ON_CALL(*this, IncomingPacket(_, _, _, _, _)) - .WillByDefault(Invoke(&real_, &ExternalPcm16B::IncomingPacket)); - ON_CALL(*this, ErrorCode()) - .WillByDefault(Invoke(&real_, &ExternalPcm16B::ErrorCode)); - ON_CALL(*this, codec_type()) - .WillByDefault(Invoke(&real_, &ExternalPcm16B::codec_type)); - } - virtual ~MockExternalPcm16B() { Die(); } - - MOCK_METHOD0(Die, void()); - MOCK_METHOD4(Decode, - int(const uint8_t* encoded, size_t encoded_len, int16_t* decoded, - SpeechType* speech_type)); - MOCK_CONST_METHOD0(HasDecodePlc, - bool()); - MOCK_METHOD2(DecodePlc, - int(int num_frames, int16_t* decoded)); - MOCK_METHOD0(Init, - int()); - MOCK_METHOD5(IncomingPacket, - int(const uint8_t* payload, size_t payload_len, - uint16_t rtp_sequence_number, uint32_t rtp_timestamp, - uint32_t arrival_timestamp)); - MOCK_METHOD0(ErrorCode, - int()); - MOCK_CONST_METHOD0(codec_type, - NetEqDecoder()); - - private: - ExternalPcm16B real_; -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_EXTERNAL_DECODER_PCM16B_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_packet_buffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_packet_buffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_packet_buffer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_packet_buffer.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_PACKET_BUFFER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_PACKET_BUFFER_H_ - -#include "webrtc/modules/audio_coding/neteq4/packet_buffer.h" - -#include "gmock/gmock.h" - -namespace webrtc { - -class MockPacketBuffer : public PacketBuffer { - public: - MockPacketBuffer(size_t max_number_of_packets, size_t max_payload_memory) - : PacketBuffer(max_number_of_packets, max_payload_memory) {} - virtual ~MockPacketBuffer() { Die(); } - MOCK_METHOD0(Die, void()); - MOCK_METHOD0(Flush, - void()); - MOCK_CONST_METHOD0(Empty, - bool()); - MOCK_METHOD1(InsertPacket, - int(Packet* packet)); - MOCK_METHOD4(InsertPacketList, - int(PacketList* packet_list, - const DecoderDatabase& decoder_database, - uint8_t* current_rtp_payload_type, - uint8_t* current_cng_rtp_payload_type)); - MOCK_CONST_METHOD1(NextTimestamp, - int(uint32_t* next_timestamp)); - MOCK_CONST_METHOD2(NextHigherTimestamp, - int(uint32_t timestamp, uint32_t* next_timestamp)); - MOCK_CONST_METHOD0(NextRtpHeader, - const RTPHeader*()); - MOCK_METHOD1(GetNextPacket, - Packet*(int* discard_count)); - MOCK_METHOD0(DiscardNextPacket, - int()); - MOCK_METHOD1(DiscardOldPackets, - int(uint32_t timestamp_limit)); - MOCK_CONST_METHOD0(NumPacketsInBuffer, - int()); - MOCK_METHOD1(IncrementWaitingTimes, - void(int)); - MOCK_CONST_METHOD0(current_memory_bytes, - int()); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_PACKET_BUFFER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_payload_splitter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_payload_splitter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_payload_splitter.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/mock/mock_payload_splitter.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_PAYLOAD_SPLITTER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_PAYLOAD_SPLITTER_H_ - -#include "webrtc/modules/audio_coding/neteq4/payload_splitter.h" - -#include "gmock/gmock.h" - -namespace webrtc { - -class MockPayloadSplitter : public PayloadSplitter { - public: - MOCK_METHOD1(SplitRed, - int(PacketList* packet_list)); - MOCK_METHOD2(CheckRedPayloads, - int(PacketList* packet_list, const DecoderDatabase& decoder_database)); - MOCK_METHOD2(SplitAudio, - int(PacketList* packet_list, const DecoderDatabase& decoder_database)); - MOCK_METHOD4(SplitBySamples, - void(const Packet* packet, int bytes_per_ms, int timestamps_per_ms, - PacketList* new_packets)); - MOCK_METHOD4(SplitByFrames, - int(const Packet* packet, int bytes_per_frame, int timestamps_per_frame, - PacketList* new_packets)); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_MOCK_MOCK_PAYLOAD_SPLITTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" - -#include "webrtc/modules/audio_coding/neteq4/accelerate.h" -#include "webrtc/modules/audio_coding/neteq4/buffer_level_filter.h" -#include "webrtc/modules/audio_coding/neteq4/decoder_database.h" -#include "webrtc/modules/audio_coding/neteq4/delay_manager.h" -#include "webrtc/modules/audio_coding/neteq4/delay_peak_detector.h" -#include "webrtc/modules/audio_coding/neteq4/dtmf_buffer.h" -#include "webrtc/modules/audio_coding/neteq4/dtmf_tone_generator.h" -#include "webrtc/modules/audio_coding/neteq4/expand.h" -#include "webrtc/modules/audio_coding/neteq4/neteq_impl.h" -#include "webrtc/modules/audio_coding/neteq4/packet_buffer.h" -#include "webrtc/modules/audio_coding/neteq4/payload_splitter.h" -#include "webrtc/modules/audio_coding/neteq4/preemptive_expand.h" -#include "webrtc/modules/audio_coding/neteq4/timestamp_scaler.h" - -namespace webrtc { - -// Creates all classes needed and inject them into a new NetEqImpl object. -// Return the new object. -NetEq* NetEq::Create(int sample_rate_hz) { - BufferLevelFilter* buffer_level_filter = new BufferLevelFilter; - DecoderDatabase* decoder_database = new DecoderDatabase; - DelayPeakDetector* delay_peak_detector = new DelayPeakDetector; - DelayManager* delay_manager = new DelayManager(kMaxNumPacketsInBuffer, - delay_peak_detector); - DtmfBuffer* dtmf_buffer = new DtmfBuffer(sample_rate_hz); - DtmfToneGenerator* dtmf_tone_generator = new DtmfToneGenerator; - PacketBuffer* packet_buffer = new PacketBuffer(kMaxNumPacketsInBuffer, - kMaxBytesInBuffer); - PayloadSplitter* payload_splitter = new PayloadSplitter; - TimestampScaler* timestamp_scaler = new TimestampScaler(*decoder_database); - AccelerateFactory* accelerate_factory = new AccelerateFactory; - ExpandFactory* expand_factory = new ExpandFactory; - PreemptiveExpandFactory* preemptive_expand_factory = - new PreemptiveExpandFactory; - return new NetEqImpl(sample_rate_hz, - buffer_level_filter, - decoder_database, - delay_manager, - delay_peak_detector, - dtmf_buffer, - dtmf_tone_generator, - packet_buffer, - payload_splitter, - timestamp_scaler, - accelerate_factory, - expand_factory, - preemptive_expand_factory); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_external_decoder_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_external_decoder_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_external_decoder_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_external_decoder_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,209 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Test to verify correct operation for externally created decoders. - -#include -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" -#include "webrtc/modules/audio_coding/neteq4/mock/mock_external_decoder_pcm16b.h" -#include "webrtc/modules/audio_coding/neteq4/tools/input_audio_file.h" -#include "webrtc/modules/audio_coding/neteq4/tools/rtp_generator.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/test/testsupport/fileutils.h" -#include "webrtc/test/testsupport/gtest_disable.h" - -namespace webrtc { - -using ::testing::_; - -// This test encodes a few packets of PCM16b 32 kHz data and inserts it into two -// different NetEq instances. The first instance uses the internal version of -// the decoder object, while the second one uses an externally created decoder -// object (ExternalPcm16B wrapped in MockExternalPcm16B, both defined above). -// The test verifies that the output from both instances match. -class NetEqExternalDecoderTest : public ::testing::Test { - protected: - static const int kTimeStepMs = 10; - static const int kMaxBlockSize = 480; // 10 ms @ 48 kHz. - static const uint8_t kPayloadType = 95; - static const int kSampleRateHz = 32000; - - NetEqExternalDecoderTest() - : sample_rate_hz_(kSampleRateHz), - samples_per_ms_(sample_rate_hz_ / 1000), - frame_size_ms_(10), - frame_size_samples_(frame_size_ms_ * samples_per_ms_), - output_size_samples_(frame_size_ms_ * samples_per_ms_), - neteq_external_(NetEq::Create(sample_rate_hz_)), - neteq_(NetEq::Create(sample_rate_hz_)), - external_decoder_(new MockExternalPcm16B(kDecoderPCM16Bswb32kHz)), - rtp_generator_(samples_per_ms_), - payload_size_bytes_(0), - last_send_time_(0), - last_arrival_time_(0) { - input_ = new int16_t[frame_size_samples_]; - encoded_ = new uint8_t[2 * frame_size_samples_]; - } - - ~NetEqExternalDecoderTest() { - delete neteq_external_; - delete neteq_; - // We will now delete the decoder ourselves, so expecting Die to be called. - EXPECT_CALL(*external_decoder_, Die()).Times(1); - delete external_decoder_; - delete [] input_; - delete [] encoded_; - } - - virtual void SetUp() { - const std::string file_name = - webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); - input_file_.reset(new test::InputAudioFile(file_name)); - assert(sample_rate_hz_ == 32000); - NetEqDecoder decoder = kDecoderPCM16Bswb32kHz; - EXPECT_CALL(*external_decoder_, Init()); - // NetEq is not allowed to delete the external decoder (hence Times(0)). - EXPECT_CALL(*external_decoder_, Die()).Times(0); - ASSERT_EQ(NetEq::kOK, - neteq_external_->RegisterExternalDecoder(external_decoder_, - decoder, - sample_rate_hz_, - kPayloadType)); - ASSERT_EQ(NetEq::kOK, - neteq_->RegisterPayloadType(decoder, kPayloadType)); - } - - virtual void TearDown() {} - - int GetNewPackets() { - if (!input_file_->Read(frame_size_samples_, input_)) { - return -1; - } - payload_size_bytes_ = WebRtcPcm16b_Encode(input_, frame_size_samples_, - encoded_); - if (frame_size_samples_ * 2 != payload_size_bytes_) { - return -1; - } - int next_send_time = rtp_generator_.GetRtpHeader(kPayloadType, - frame_size_samples_, - &rtp_header_); - return next_send_time; - } - - void VerifyOutput(size_t num_samples) { - for (size_t i = 0; i < num_samples; ++i) { - ASSERT_EQ(output_[i], output_external_[i]) << - "Diff in sample " << i << "."; - } - } - - virtual int GetArrivalTime(int send_time) { - int arrival_time = last_arrival_time_ + (send_time - last_send_time_); - last_send_time_ = send_time; - last_arrival_time_ = arrival_time; - return arrival_time; - } - - virtual bool Lost() { return false; } - - void RunTest(int num_loops) { - // Get next input packets (mono and multi-channel). - int next_send_time; - int next_arrival_time; - do { - next_send_time = GetNewPackets(); - ASSERT_NE(-1, next_send_time); - next_arrival_time = GetArrivalTime(next_send_time); - } while (Lost()); // If lost, immediately read the next packet. - - EXPECT_CALL(*external_decoder_, Decode(_, payload_size_bytes_, _, _)) - .Times(num_loops); - - int time_now = 0; - for (int k = 0; k < num_loops; ++k) { - while (time_now >= next_arrival_time) { - // Insert packet in regular instance. - ASSERT_EQ(NetEq::kOK, - neteq_->InsertPacket(rtp_header_, encoded_, - payload_size_bytes_, - next_arrival_time)); - // Insert packet in external decoder instance. - EXPECT_CALL(*external_decoder_, - IncomingPacket(_, payload_size_bytes_, - rtp_header_.header.sequenceNumber, - rtp_header_.header.timestamp, - next_arrival_time)); - ASSERT_EQ(NetEq::kOK, - neteq_external_->InsertPacket(rtp_header_, encoded_, - payload_size_bytes_, - next_arrival_time)); - // Get next input packet. - do { - next_send_time = GetNewPackets(); - ASSERT_NE(-1, next_send_time); - next_arrival_time = GetArrivalTime(next_send_time); - } while (Lost()); // If lost, immediately read the next packet. - } - NetEqOutputType output_type; - // Get audio from regular instance. - int samples_per_channel; - int num_channels; - EXPECT_EQ(NetEq::kOK, - neteq_->GetAudio(kMaxBlockSize, output_, - &samples_per_channel, &num_channels, - &output_type)); - EXPECT_EQ(1, num_channels); - EXPECT_EQ(output_size_samples_, samples_per_channel); - // Get audio from external decoder instance. - ASSERT_EQ(NetEq::kOK, - neteq_external_->GetAudio(kMaxBlockSize, output_external_, - &samples_per_channel, &num_channels, - &output_type)); - EXPECT_EQ(1, num_channels); - EXPECT_EQ(output_size_samples_, samples_per_channel); - std::ostringstream ss; - ss << "Lap number " << k << "."; - SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. - // Compare mono and multi-channel. - ASSERT_NO_FATAL_FAILURE(VerifyOutput(output_size_samples_)); - - time_now += kTimeStepMs; - } - } - - const int sample_rate_hz_; - const int samples_per_ms_; - const int frame_size_ms_; - const int frame_size_samples_; - const int output_size_samples_; - NetEq* neteq_external_; - NetEq* neteq_; - MockExternalPcm16B* external_decoder_; - test::RtpGenerator rtp_generator_; - int16_t* input_; - uint8_t* encoded_; - int16_t output_[kMaxBlockSize]; - int16_t output_external_[kMaxBlockSize]; - WebRtcRTPHeader rtp_header_; - int payload_size_bytes_; - int last_send_time_; - int last_arrival_time_; - scoped_ptr input_file_; -}; - -TEST_F(NetEqExternalDecoderTest, DISABLED_ON_ANDROID(RunTest)) { - RunTest(100); // Run 100 laps @ 10 ms each in the test loop. -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq.gypi 1970-01-01 00:00:00.000000000 +0000 @@ -1,231 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'variables': { - 'neteq_dependencies': [ - 'G711', - 'PCM16B', - 'CNG', - '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', - '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', - ], - 'neteq_defines': [], - 'conditions': [ - ['include_g722==1', { - 'neteq_dependencies': ['G722'], - 'neteq_defines': ['WEBRTC_CODEC_G722',], - }], - ['include_ilbc==1', { - 'neteq_dependencies': ['iLBC'], - 'neteq_defines': ['WEBRTC_CODEC_ILBC',], - }], - ['include_isac==1', { - 'neteq_dependencies': ['iSAC', 'iSACFix',], - 'neteq_defines': ['WEBRTC_CODEC_ISAC', 'WEBRTC_CODEC_ISACFIX',], - }], - ['include_opus==1', { - 'neteq_dependencies': ['webrtc_opus',], - 'neteq_defines': ['WEBRTC_CODEC_OPUS',], - }], - ], - }, - 'targets': [ - { - 'target_name': 'NetEq4', - 'type': 'static_library', - 'dependencies': [ - '<@(neteq_dependencies)', - ], - 'defines': [ - '<@(neteq_defines)', - ], - 'include_dirs': [ - 'interface', - '<(webrtc_root)', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - 'interface', - '<(webrtc_root)', - ], - }, - 'sources': [ - 'interface/audio_decoder.h', - 'interface/neteq.h', - 'accelerate.cc', - 'accelerate.h', - 'audio_decoder_impl.cc', - 'audio_decoder_impl.h', - 'audio_decoder.cc', - 'audio_multi_vector.cc', - 'audio_multi_vector.h', - 'audio_vector.cc', - 'audio_vector.h', - 'background_noise.cc', - 'background_noise.h', - 'buffer_level_filter.cc', - 'buffer_level_filter.h', - 'comfort_noise.cc', - 'comfort_noise.h', - 'decision_logic.cc', - 'decision_logic.h', - 'decision_logic_fax.cc', - 'decision_logic_fax.h', - 'decision_logic_normal.cc', - 'decision_logic_normal.h', - 'decoder_database.cc', - 'decoder_database.h', - 'defines.h', - 'delay_manager.cc', - 'delay_manager.h', - 'delay_peak_detector.cc', - 'delay_peak_detector.h', - 'dsp_helper.cc', - 'dsp_helper.h', - 'dtmf_buffer.cc', - 'dtmf_buffer.h', - 'dtmf_tone_generator.cc', - 'dtmf_tone_generator.h', - 'expand.cc', - 'expand.h', - 'merge.cc', - 'merge.h', - 'neteq_impl.cc', - 'neteq_impl.h', - 'neteq.cc', - 'statistics_calculator.cc', - 'statistics_calculator.h', - 'normal.cc', - 'normal.h', - 'packet_buffer.cc', - 'packet_buffer.h', - 'payload_splitter.cc', - 'payload_splitter.h', - 'post_decode_vad.cc', - 'post_decode_vad.h', - 'preemptive_expand.cc', - 'preemptive_expand.h', - 'random_vector.cc', - 'random_vector.h', - 'rtcp.cc', - 'rtcp.h', - 'sync_buffer.cc', - 'sync_buffer.h', - 'timestamp_scaler.cc', - 'timestamp_scaler.h', - 'time_stretch.cc', - 'time_stretch.h', - ], - }, - ], # targets - 'conditions': [ - ['include_tests==1', { - 'includes': ['neteq_tests.gypi',], - 'targets': [ - { - 'target_name': 'audio_decoder_unittests', - 'type': '<(gtest_target_type)', - 'dependencies': [ - '<@(neteq_dependencies)', - '<(DEPTH)/testing/gtest.gyp:gtest', - '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', - '<(webrtc_root)/test/test.gyp:test_support_main', - ], -# FIX for include_isac/etc - 'defines': [ - 'AUDIO_DECODER_UNITTEST', - 'WEBRTC_CODEC_G722', - 'WEBRTC_CODEC_ILBC', - 'WEBRTC_CODEC_ISACFX', - 'WEBRTC_CODEC_ISAC', - 'WEBRTC_CODEC_PCM16', - '<@(neteq_defines)', - ], - 'sources': [ - 'audio_decoder_impl.cc', - 'audio_decoder_impl.h', - 'audio_decoder_unittest.cc', - 'audio_decoder.cc', - 'interface/audio_decoder.h', - ], - 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are - # using Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { - 'dependencies': [ - '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code', - ], - }], - ], - }, # audio_decoder_unittests - - { - 'target_name': 'neteq_unittest_tools', - 'type': 'static_library', - 'dependencies': [ - '<(DEPTH)/testing/gmock.gyp:gmock', - '<(DEPTH)/testing/gtest.gyp:gtest', - 'PCM16B', # Needed by neteq_performance_test. - ], - 'direct_dependent_settings': { - 'include_dirs': [ - 'tools', - ], - }, - 'include_dirs': [ - 'tools', - ], - 'sources': [ - 'tools/audio_loop.cc', - 'tools/audio_loop.h', - 'tools/input_audio_file.cc', - 'tools/input_audio_file.h', - 'tools/neteq_performance_test.cc', - 'tools/neteq_performance_test.h', - 'tools/rtp_generator.cc', - 'tools/rtp_generator.h', - ], - }, # neteq_unittest_tools - ], # targets - 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are using - # Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { - 'targets': [ - { - 'target_name': 'audio_decoder_unittests_apk_target', - 'type': 'none', - 'dependencies': [ - '<(apk_tests_path):audio_decoder_unittests_apk', - ], - }, - ], - }], - ['test_isolation_mode != "noop"', { - 'targets': [ - { - 'target_name': 'audio_decoder_unittests_run', - 'type': 'none', - 'dependencies': [ - 'audio_decoder_unittests', - ], - 'includes': [ - '../../../build/isolate.gypi', - 'audio_decoder_unittests.isolate', - ], - 'sources': [ - 'audio_decoder_unittests.isolate', - ], - }, - ], - }], - ], - }], # include_tests - ], # conditions -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_impl.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,1910 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/neteq_impl.h" - -#include -#include // memset - -#include - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_coding/neteq4/accelerate.h" -#include "webrtc/modules/audio_coding/neteq4/background_noise.h" -#include "webrtc/modules/audio_coding/neteq4/buffer_level_filter.h" -#include "webrtc/modules/audio_coding/neteq4/comfort_noise.h" -#include "webrtc/modules/audio_coding/neteq4/decision_logic.h" -#include "webrtc/modules/audio_coding/neteq4/decoder_database.h" -#include "webrtc/modules/audio_coding/neteq4/defines.h" -#include "webrtc/modules/audio_coding/neteq4/delay_manager.h" -#include "webrtc/modules/audio_coding/neteq4/delay_peak_detector.h" -#include "webrtc/modules/audio_coding/neteq4/dtmf_buffer.h" -#include "webrtc/modules/audio_coding/neteq4/dtmf_tone_generator.h" -#include "webrtc/modules/audio_coding/neteq4/expand.h" -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" -#include "webrtc/modules/audio_coding/neteq4/merge.h" -#include "webrtc/modules/audio_coding/neteq4/normal.h" -#include "webrtc/modules/audio_coding/neteq4/packet_buffer.h" -#include "webrtc/modules/audio_coding/neteq4/packet.h" -#include "webrtc/modules/audio_coding/neteq4/payload_splitter.h" -#include "webrtc/modules/audio_coding/neteq4/post_decode_vad.h" -#include "webrtc/modules/audio_coding/neteq4/preemptive_expand.h" -#include "webrtc/modules/audio_coding/neteq4/sync_buffer.h" -#include "webrtc/modules/audio_coding/neteq4/timestamp_scaler.h" -#include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/logging.h" - -// Modify the code to obtain backwards bit-exactness. Once bit-exactness is no -// longer required, this #define should be removed (and the code that it -// enables). -#define LEGACY_BITEXACT - -namespace webrtc { - -NetEqImpl::NetEqImpl(int fs, - BufferLevelFilter* buffer_level_filter, - DecoderDatabase* decoder_database, - DelayManager* delay_manager, - DelayPeakDetector* delay_peak_detector, - DtmfBuffer* dtmf_buffer, - DtmfToneGenerator* dtmf_tone_generator, - PacketBuffer* packet_buffer, - PayloadSplitter* payload_splitter, - TimestampScaler* timestamp_scaler, - AccelerateFactory* accelerate_factory, - ExpandFactory* expand_factory, - PreemptiveExpandFactory* preemptive_expand_factory) - : buffer_level_filter_(buffer_level_filter), - decoder_database_(decoder_database), - delay_manager_(delay_manager), - delay_peak_detector_(delay_peak_detector), - dtmf_buffer_(dtmf_buffer), - dtmf_tone_generator_(dtmf_tone_generator), - packet_buffer_(packet_buffer), - payload_splitter_(payload_splitter), - timestamp_scaler_(timestamp_scaler), - vad_(new PostDecodeVad()), - expand_factory_(expand_factory), - accelerate_factory_(accelerate_factory), - preemptive_expand_factory_(preemptive_expand_factory), - last_mode_(kModeNormal), - mute_factor_array_(NULL), - decoded_buffer_length_(kMaxFrameSize), - decoded_buffer_(new int16_t[decoded_buffer_length_]), - playout_timestamp_(0), - new_codec_(false), - timestamp_(0), - reset_decoder_(false), - current_rtp_payload_type_(0xFF), // Invalid RTP payload type. - current_cng_rtp_payload_type_(0xFF), // Invalid RTP payload type. - ssrc_(0), - first_packet_(true), - error_code_(0), - decoder_error_code_(0), - crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), - decoded_packet_sequence_number_(-1), - decoded_packet_timestamp_(0) { - if (fs != 8000 && fs != 16000 && fs != 32000 && fs != 48000) { - LOG(LS_ERROR) << "Sample rate " << fs << " Hz not supported. " << - "Changing to 8000 Hz."; - fs = 8000; - } - LOG(LS_INFO) << "Create NetEqImpl object with fs = " << fs << "."; - fs_hz_ = fs; - fs_mult_ = fs / 8000; - output_size_samples_ = kOutputSizeMs * 8 * fs_mult_; - decoder_frame_length_ = 3 * output_size_samples_; - WebRtcSpl_Init(); - decision_logic_.reset(DecisionLogic::Create(fs_hz_, output_size_samples_, - kPlayoutOn, - decoder_database_.get(), - *packet_buffer_.get(), - delay_manager_.get(), - buffer_level_filter_.get())); - SetSampleRateAndChannels(fs, 1); // Default is 1 channel. -} - -NetEqImpl::~NetEqImpl() { - LOG(LS_INFO) << "Deleting NetEqImpl object."; -} - -int NetEqImpl::InsertPacket(const WebRtcRTPHeader& rtp_header, - const uint8_t* payload, - int length_bytes, - uint32_t receive_timestamp) { - CriticalSectionScoped lock(crit_sect_.get()); - LOG(LS_VERBOSE) << "InsertPacket: ts=" << rtp_header.header.timestamp << - ", sn=" << rtp_header.header.sequenceNumber << - ", pt=" << static_cast(rtp_header.header.payloadType) << - ", ssrc=" << rtp_header.header.ssrc << - ", len=" << length_bytes; - int error = InsertPacketInternal(rtp_header, payload, length_bytes, - receive_timestamp, false); - if (error != 0) { - LOG_FERR1(LS_WARNING, InsertPacketInternal, error); - error_code_ = error; - return kFail; - } - return kOK; -} - -int NetEqImpl::InsertSyncPacket(const WebRtcRTPHeader& rtp_header, - uint32_t receive_timestamp) { - CriticalSectionScoped lock(crit_sect_.get()); - LOG(LS_VERBOSE) << "InsertPacket-Sync: ts=" - << rtp_header.header.timestamp << - ", sn=" << rtp_header.header.sequenceNumber << - ", pt=" << static_cast(rtp_header.header.payloadType) << - ", ssrc=" << rtp_header.header.ssrc; - - const uint8_t kSyncPayload[] = { 's', 'y', 'n', 'c' }; - int error = InsertPacketInternal( - rtp_header, kSyncPayload, sizeof(kSyncPayload), receive_timestamp, true); - - if (error != 0) { - LOG_FERR1(LS_WARNING, InsertPacketInternal, error); - error_code_ = error; - return kFail; - } - return kOK; -} - -int NetEqImpl::GetAudio(size_t max_length, int16_t* output_audio, - int* samples_per_channel, int* num_channels, - NetEqOutputType* type) { - CriticalSectionScoped lock(crit_sect_.get()); - LOG(LS_VERBOSE) << "GetAudio"; - int error = GetAudioInternal(max_length, output_audio, samples_per_channel, - num_channels); - LOG(LS_VERBOSE) << "Produced " << *samples_per_channel << - " samples/channel for " << *num_channels << " channel(s)"; - if (error != 0) { - LOG_FERR1(LS_WARNING, GetAudioInternal, error); - error_code_ = error; - return kFail; - } - if (type) { - *type = LastOutputType(); - } - return kOK; -} - -int NetEqImpl::RegisterPayloadType(enum NetEqDecoder codec, - uint8_t rtp_payload_type) { - CriticalSectionScoped lock(crit_sect_.get()); - LOG_API2(static_cast(rtp_payload_type), codec); - int ret = decoder_database_->RegisterPayload(rtp_payload_type, codec); - if (ret != DecoderDatabase::kOK) { - LOG_FERR2(LS_WARNING, RegisterPayload, rtp_payload_type, codec); - switch (ret) { - case DecoderDatabase::kInvalidRtpPayloadType: - error_code_ = kInvalidRtpPayloadType; - break; - case DecoderDatabase::kCodecNotSupported: - error_code_ = kCodecNotSupported; - break; - case DecoderDatabase::kDecoderExists: - error_code_ = kDecoderExists; - break; - default: - error_code_ = kOtherError; - } - return kFail; - } - return kOK; -} - -int NetEqImpl::RegisterExternalDecoder(AudioDecoder* decoder, - enum NetEqDecoder codec, - int sample_rate_hz, - uint8_t rtp_payload_type) { - CriticalSectionScoped lock(crit_sect_.get()); - LOG_API2(static_cast(rtp_payload_type), codec); - if (!decoder) { - LOG(LS_ERROR) << "Cannot register external decoder with NULL pointer"; - assert(false); - return kFail; - } - int ret = decoder_database_->InsertExternal(rtp_payload_type, codec, - sample_rate_hz, decoder); - if (ret != DecoderDatabase::kOK) { - LOG_FERR2(LS_WARNING, InsertExternal, rtp_payload_type, codec); - switch (ret) { - case DecoderDatabase::kInvalidRtpPayloadType: - error_code_ = kInvalidRtpPayloadType; - break; - case DecoderDatabase::kCodecNotSupported: - error_code_ = kCodecNotSupported; - break; - case DecoderDatabase::kDecoderExists: - error_code_ = kDecoderExists; - break; - case DecoderDatabase::kInvalidSampleRate: - error_code_ = kInvalidSampleRate; - break; - case DecoderDatabase::kInvalidPointer: - error_code_ = kInvalidPointer; - break; - default: - error_code_ = kOtherError; - } - return kFail; - } - return kOK; -} - -int NetEqImpl::RemovePayloadType(uint8_t rtp_payload_type) { - CriticalSectionScoped lock(crit_sect_.get()); - LOG_API1(static_cast(rtp_payload_type)); - int ret = decoder_database_->Remove(rtp_payload_type); - if (ret == DecoderDatabase::kOK) { - return kOK; - } else if (ret == DecoderDatabase::kDecoderNotFound) { - error_code_ = kDecoderNotFound; - } else { - error_code_ = kOtherError; - } - LOG_FERR1(LS_WARNING, Remove, rtp_payload_type); - return kFail; -} - -bool NetEqImpl::SetMinimumDelay(int delay_ms) { - CriticalSectionScoped lock(crit_sect_.get()); - if (delay_ms >= 0 && delay_ms < 10000) { - assert(delay_manager_.get()); - return delay_manager_->SetMinimumDelay(delay_ms); - } - return false; -} - -bool NetEqImpl::SetMaximumDelay(int delay_ms) { - CriticalSectionScoped lock(crit_sect_.get()); - if (delay_ms >= 0 && delay_ms < 10000) { - assert(delay_manager_.get()); - return delay_manager_->SetMaximumDelay(delay_ms); - } - return false; -} - -int NetEqImpl::LeastRequiredDelayMs() const { - CriticalSectionScoped lock(crit_sect_.get()); - assert(delay_manager_.get()); - return delay_manager_->least_required_delay_ms(); -} - -void NetEqImpl::SetPlayoutMode(NetEqPlayoutMode mode) { - CriticalSectionScoped lock(crit_sect_.get()); - if (!decision_logic_.get() || mode != decision_logic_->playout_mode()) { - // The reset() method calls delete for the old object. - decision_logic_.reset(DecisionLogic::Create(fs_hz_, output_size_samples_, - mode, - decoder_database_.get(), - *packet_buffer_.get(), - delay_manager_.get(), - buffer_level_filter_.get())); - } -} - -NetEqPlayoutMode NetEqImpl::PlayoutMode() const { - CriticalSectionScoped lock(crit_sect_.get()); - assert(decision_logic_.get()); - return decision_logic_->playout_mode(); -} - -int NetEqImpl::NetworkStatistics(NetEqNetworkStatistics* stats) { - CriticalSectionScoped lock(crit_sect_.get()); - assert(decoder_database_.get()); - const int total_samples_in_buffers = packet_buffer_->NumSamplesInBuffer( - decoder_database_.get(), decoder_frame_length_) + - static_cast(sync_buffer_->FutureLength()); - assert(delay_manager_.get()); - assert(decision_logic_.get()); - stats_.GetNetworkStatistics(fs_hz_, total_samples_in_buffers, - decoder_frame_length_, *delay_manager_.get(), - *decision_logic_.get(), stats); - return 0; -} - -void NetEqImpl::WaitingTimes(std::vector* waiting_times) { - CriticalSectionScoped lock(crit_sect_.get()); - stats_.WaitingTimes(waiting_times); -} - -void NetEqImpl::GetRtcpStatistics(RtcpStatistics* stats) { - CriticalSectionScoped lock(crit_sect_.get()); - if (stats) { - rtcp_.GetStatistics(false, stats); - } -} - -void NetEqImpl::GetRtcpStatisticsNoReset(RtcpStatistics* stats) { - CriticalSectionScoped lock(crit_sect_.get()); - if (stats) { - rtcp_.GetStatistics(true, stats); - } -} - -void NetEqImpl::EnableVad() { - CriticalSectionScoped lock(crit_sect_.get()); - assert(vad_.get()); - vad_->Enable(); -} - -void NetEqImpl::DisableVad() { - CriticalSectionScoped lock(crit_sect_.get()); - assert(vad_.get()); - vad_->Disable(); -} - -uint32_t NetEqImpl::PlayoutTimestamp() { - CriticalSectionScoped lock(crit_sect_.get()); - return timestamp_scaler_->ToExternal(playout_timestamp_); -} - -int NetEqImpl::LastError() { - CriticalSectionScoped lock(crit_sect_.get()); - return error_code_; -} - -int NetEqImpl::LastDecoderError() { - CriticalSectionScoped lock(crit_sect_.get()); - return decoder_error_code_; -} - -void NetEqImpl::FlushBuffers() { - CriticalSectionScoped lock(crit_sect_.get()); - LOG_API0(); - packet_buffer_->Flush(); - assert(sync_buffer_.get()); - assert(expand_.get()); - sync_buffer_->Flush(); - sync_buffer_->set_next_index(sync_buffer_->next_index() - - expand_->overlap_length()); - // Set to wait for new codec. - first_packet_ = true; -} - -void NetEqImpl::PacketBufferStatistics(int* current_num_packets, - int* max_num_packets, - int* current_memory_size_bytes, - int* max_memory_size_bytes) const { - CriticalSectionScoped lock(crit_sect_.get()); - packet_buffer_->BufferStat(current_num_packets, max_num_packets, - current_memory_size_bytes, max_memory_size_bytes); -} - -int NetEqImpl::DecodedRtpInfo(int* sequence_number, uint32_t* timestamp) const { - CriticalSectionScoped lock(crit_sect_.get()); - if (decoded_packet_sequence_number_ < 0) - return -1; - *sequence_number = decoded_packet_sequence_number_; - *timestamp = decoded_packet_timestamp_; - return 0; -} - -void NetEqImpl::SetBackgroundNoiseMode(NetEqBackgroundNoiseMode mode) { - CriticalSectionScoped lock(crit_sect_.get()); - assert(background_noise_.get()); - background_noise_->set_mode(mode); -} - -NetEqBackgroundNoiseMode NetEqImpl::BackgroundNoiseMode() const { - CriticalSectionScoped lock(crit_sect_.get()); - assert(background_noise_.get()); - return background_noise_->mode(); -} - -// Methods below this line are private. - -int NetEqImpl::InsertPacketInternal(const WebRtcRTPHeader& rtp_header, - const uint8_t* payload, - int length_bytes, - uint32_t receive_timestamp, - bool is_sync_packet) { - if (!payload) { - LOG_F(LS_ERROR) << "payload == NULL"; - return kInvalidPointer; - } - // Sanity checks for sync-packets. - if (is_sync_packet) { - if (decoder_database_->IsDtmf(rtp_header.header.payloadType) || - decoder_database_->IsRed(rtp_header.header.payloadType) || - decoder_database_->IsComfortNoise(rtp_header.header.payloadType)) { - LOG_F(LS_ERROR) << "Sync-packet with an unacceptable payload type " - << rtp_header.header.payloadType; - return kSyncPacketNotAccepted; - } - if (first_packet_ || - rtp_header.header.payloadType != current_rtp_payload_type_ || - rtp_header.header.ssrc != ssrc_) { - // Even if |current_rtp_payload_type_| is 0xFF, sync-packet isn't - // accepted. - LOG_F(LS_ERROR) << "Changing codec, SSRC or first packet " - "with sync-packet."; - return kSyncPacketNotAccepted; - } - } - PacketList packet_list; - RTPHeader main_header; - { - // Convert to Packet. - // Create |packet| within this separate scope, since it should not be used - // directly once it's been inserted in the packet list. This way, |packet| - // is not defined outside of this block. - Packet* packet = new Packet; - packet->header.markerBit = false; - packet->header.payloadType = rtp_header.header.payloadType; - packet->header.sequenceNumber = rtp_header.header.sequenceNumber; - packet->header.timestamp = rtp_header.header.timestamp; - packet->header.ssrc = rtp_header.header.ssrc; - packet->header.numCSRCs = 0; - packet->payload_length = length_bytes; - packet->primary = true; - packet->waiting_time = 0; - packet->payload = new uint8_t[packet->payload_length]; - packet->sync_packet = is_sync_packet; - if (!packet->payload) { - LOG_F(LS_ERROR) << "Payload pointer is NULL."; - } - assert(payload); // Already checked above. - memcpy(packet->payload, payload, packet->payload_length); - // Insert packet in a packet list. - packet_list.push_back(packet); - // Save main payloads header for later. - memcpy(&main_header, &packet->header, sizeof(main_header)); - } - - bool update_sample_rate_and_channels = false; - // Reinitialize NetEq if it's needed (changed SSRC or first call). - if ((main_header.ssrc != ssrc_) || first_packet_) { - rtcp_.Init(main_header.sequenceNumber); - first_packet_ = false; - - // Flush the packet buffer and DTMF buffer. - packet_buffer_->Flush(); - dtmf_buffer_->Flush(); - - // Store new SSRC. - ssrc_ = main_header.ssrc; - - // Update audio buffer timestamp. - sync_buffer_->IncreaseEndTimestamp(main_header.timestamp - timestamp_); - - // Update codecs. - timestamp_ = main_header.timestamp; - current_rtp_payload_type_ = main_header.payloadType; - - // Set MCU to update codec on next SignalMCU call. - new_codec_ = true; - - // Reset timestamp scaling. - timestamp_scaler_->Reset(); - - // Triger an update of sampling rate and the number of channels. - update_sample_rate_and_channels = true; - } - - // Update RTCP statistics, only for regular packets. - if (!is_sync_packet) - rtcp_.Update(main_header, receive_timestamp); - - // Check for RED payload type, and separate payloads into several packets. - if (decoder_database_->IsRed(main_header.payloadType)) { - assert(!is_sync_packet); // We had a sanity check for this. - if (payload_splitter_->SplitRed(&packet_list) != PayloadSplitter::kOK) { - LOG_FERR1(LS_WARNING, SplitRed, packet_list.size()); - PacketBuffer::DeleteAllPackets(&packet_list); - return kRedundancySplitError; - } - // Only accept a few RED payloads of the same type as the main data, - // DTMF events and CNG. - payload_splitter_->CheckRedPayloads(&packet_list, *decoder_database_); - // Update the stored main payload header since the main payload has now - // changed. - memcpy(&main_header, &packet_list.front()->header, sizeof(main_header)); - } - - // Check payload types. - if (decoder_database_->CheckPayloadTypes(packet_list) == - DecoderDatabase::kDecoderNotFound) { - LOG_FERR1(LS_WARNING, CheckPayloadTypes, packet_list.size()); - PacketBuffer::DeleteAllPackets(&packet_list); - return kUnknownRtpPayloadType; - } - - // Scale timestamp to internal domain (only for some codecs). - timestamp_scaler_->ToInternal(&packet_list); - - // Process DTMF payloads. Cycle through the list of packets, and pick out any - // DTMF payloads found. - PacketList::iterator it = packet_list.begin(); - while (it != packet_list.end()) { - Packet* current_packet = (*it); - assert(current_packet); - assert(current_packet->payload); - if (decoder_database_->IsDtmf(current_packet->header.payloadType)) { - assert(!current_packet->sync_packet); // We had a sanity check for this. - DtmfEvent event; - int ret = DtmfBuffer::ParseEvent( - current_packet->header.timestamp, - current_packet->payload, - current_packet->payload_length, - &event); - if (ret != DtmfBuffer::kOK) { - LOG_FERR2(LS_WARNING, ParseEvent, ret, - current_packet->payload_length); - PacketBuffer::DeleteAllPackets(&packet_list); - return kDtmfParsingError; - } - if (dtmf_buffer_->InsertEvent(event) != DtmfBuffer::kOK) { - LOG_FERR0(LS_WARNING, InsertEvent); - PacketBuffer::DeleteAllPackets(&packet_list); - return kDtmfInsertError; - } - // TODO(hlundin): Let the destructor of Packet handle the payload. - delete [] current_packet->payload; - delete current_packet; - it = packet_list.erase(it); - } else { - ++it; - } - } - - // Split payloads into smaller chunks. This also verifies that all payloads - // are of a known payload type. SplitAudio() method is protected against - // sync-packets. - int ret = payload_splitter_->SplitAudio(&packet_list, *decoder_database_); - if (ret != PayloadSplitter::kOK) { - LOG_FERR1(LS_WARNING, SplitAudio, packet_list.size()); - PacketBuffer::DeleteAllPackets(&packet_list); - switch (ret) { - case PayloadSplitter::kUnknownPayloadType: - return kUnknownRtpPayloadType; - case PayloadSplitter::kFrameSplitError: - return kFrameSplitError; - default: - return kOtherError; - } - } - - // Update bandwidth estimate, if the packet is not sync-packet. - if (!packet_list.empty() && !packet_list.front()->sync_packet) { - // The list can be empty here if we got nothing but DTMF payloads. - AudioDecoder* decoder = - decoder_database_->GetDecoder(main_header.payloadType); - assert(decoder); // Should always get a valid object, since we have - // already checked that the payload types are known. - decoder->IncomingPacket(packet_list.front()->payload, - packet_list.front()->payload_length, - packet_list.front()->header.sequenceNumber, - packet_list.front()->header.timestamp, - receive_timestamp); - } - - // Insert packets in buffer. - int temp_bufsize = packet_buffer_->NumPacketsInBuffer(); - ret = packet_buffer_->InsertPacketList( - &packet_list, - *decoder_database_, - ¤t_rtp_payload_type_, - ¤t_cng_rtp_payload_type_); - if (ret == PacketBuffer::kFlushed) { - // Reset DSP timestamp etc. if packet buffer flushed. - new_codec_ = true; - update_sample_rate_and_channels = true; - LOG_F(LS_WARNING) << "Packet buffer flushed"; - } else if (ret == PacketBuffer::kOversizePacket) { - LOG_F(LS_WARNING) << "Packet larger than packet buffer"; - return kOversizePacket; - } else if (ret != PacketBuffer::kOK) { - LOG_FERR1(LS_WARNING, InsertPacketList, packet_list.size()); - PacketBuffer::DeleteAllPackets(&packet_list); - return kOtherError; - } - if (current_rtp_payload_type_ != 0xFF) { - const DecoderDatabase::DecoderInfo* dec_info = - decoder_database_->GetDecoderInfo(current_rtp_payload_type_); - if (!dec_info) { - assert(false); // Already checked that the payload type is known. - } - } - - if (update_sample_rate_and_channels && !packet_buffer_->Empty()) { - // We do not use |current_rtp_payload_type_| to |set payload_type|, but - // get the next RTP header from |packet_buffer_| to obtain the payload type. - // The reason for it is the following corner case. If NetEq receives a - // CNG packet with a sample rate different than the current CNG then it - // flushes its buffer, assuming send codec must have been changed. However, - // payload type of the hypothetically new send codec is not known. - const RTPHeader* rtp_header = packet_buffer_->NextRtpHeader(); - assert(rtp_header); - int payload_type = rtp_header->payloadType; - AudioDecoder* decoder = decoder_database_->GetDecoder(payload_type); - assert(decoder); // Payloads are already checked to be valid. - const DecoderDatabase::DecoderInfo* decoder_info = - decoder_database_->GetDecoderInfo(payload_type); - assert(decoder_info); - if (decoder_info->fs_hz != fs_hz_ || - decoder->channels() != algorithm_buffer_->Channels()) - SetSampleRateAndChannels(decoder_info->fs_hz, decoder->channels()); - } - - // TODO(hlundin): Move this code to DelayManager class. - const DecoderDatabase::DecoderInfo* dec_info = - decoder_database_->GetDecoderInfo(main_header.payloadType); - assert(dec_info); // Already checked that the payload type is known. - delay_manager_->LastDecoderType(dec_info->codec_type); - if (delay_manager_->last_pack_cng_or_dtmf() == 0) { - // Calculate the total speech length carried in each packet. - temp_bufsize = packet_buffer_->NumPacketsInBuffer() - temp_bufsize; - temp_bufsize *= decoder_frame_length_; - - if ((temp_bufsize > 0) && - (temp_bufsize != decision_logic_->packet_length_samples())) { - decision_logic_->set_packet_length_samples(temp_bufsize); - delay_manager_->SetPacketAudioLength((1000 * temp_bufsize) / fs_hz_); - } - - // Update statistics. - if ((int32_t) (main_header.timestamp - timestamp_) >= 0 && - !new_codec_) { - // Only update statistics if incoming packet is not older than last played - // out packet, and if new codec flag is not set. - delay_manager_->Update(main_header.sequenceNumber, main_header.timestamp, - fs_hz_); - } - } else if (delay_manager_->last_pack_cng_or_dtmf() == -1) { - // This is first "normal" packet after CNG or DTMF. - // Reset packet time counter and measure time until next packet, - // but don't update statistics. - delay_manager_->set_last_pack_cng_or_dtmf(0); - delay_manager_->ResetPacketIatCount(); - } - return 0; -} - -int NetEqImpl::GetAudioInternal(size_t max_length, int16_t* output, - int* samples_per_channel, int* num_channels) { - PacketList packet_list; - DtmfEvent dtmf_event; - Operations operation; - bool play_dtmf; - int return_value = GetDecision(&operation, &packet_list, &dtmf_event, - &play_dtmf); - if (return_value != 0) { - LOG_FERR1(LS_WARNING, GetDecision, return_value); - assert(false); - last_mode_ = kModeError; - return return_value; - } - LOG(LS_VERBOSE) << "GetDecision returned operation=" << operation << - " and " << packet_list.size() << " packet(s)"; - - AudioDecoder::SpeechType speech_type; - int length = 0; - int decode_return_value = Decode(&packet_list, &operation, - &length, &speech_type); - - assert(vad_.get()); - bool sid_frame_available = - (operation == kRfc3389Cng && !packet_list.empty()); - vad_->Update(decoded_buffer_.get(), length, speech_type, - sid_frame_available, fs_hz_); - - algorithm_buffer_->Clear(); - switch (operation) { - case kNormal: { - DoNormal(decoded_buffer_.get(), length, speech_type, play_dtmf); - break; - } - case kMerge: { - DoMerge(decoded_buffer_.get(), length, speech_type, play_dtmf); - break; - } - case kExpand: { - return_value = DoExpand(play_dtmf); - break; - } - case kAccelerate: { - return_value = DoAccelerate(decoded_buffer_.get(), length, speech_type, - play_dtmf); - break; - } - case kPreemptiveExpand: { - return_value = DoPreemptiveExpand(decoded_buffer_.get(), length, - speech_type, play_dtmf); - break; - } - case kRfc3389Cng: - case kRfc3389CngNoPacket: { - return_value = DoRfc3389Cng(&packet_list, play_dtmf); - break; - } - case kCodecInternalCng: { - // This handles the case when there is no transmission and the decoder - // should produce internal comfort noise. - // TODO(hlundin): Write test for codec-internal CNG. - DoCodecInternalCng(); - break; - } - case kDtmf: { - // TODO(hlundin): Write test for this. - return_value = DoDtmf(dtmf_event, &play_dtmf); - break; - } - case kAlternativePlc: { - // TODO(hlundin): Write test for this. - DoAlternativePlc(false); - break; - } - case kAlternativePlcIncreaseTimestamp: { - // TODO(hlundin): Write test for this. - DoAlternativePlc(true); - break; - } - case kAudioRepetitionIncreaseTimestamp: { - // TODO(hlundin): Write test for this. - sync_buffer_->IncreaseEndTimestamp(output_size_samples_); - // Skipping break on purpose. Execution should move on into the - // next case. - } - case kAudioRepetition: { - // TODO(hlundin): Write test for this. - // Copy last |output_size_samples_| from |sync_buffer_| to - // |algorithm_buffer|. - algorithm_buffer_->PushBackFromIndex( - *sync_buffer_, sync_buffer_->Size() - output_size_samples_); - expand_->Reset(); - break; - } - case kUndefined: { - LOG_F(LS_ERROR) << "Invalid operation kUndefined."; - assert(false); // This should not happen. - last_mode_ = kModeError; - return kInvalidOperation; - } - } // End of switch. - if (return_value < 0) { - return return_value; - } - - if (last_mode_ != kModeRfc3389Cng) { - comfort_noise_->Reset(); - } - - // Copy from |algorithm_buffer| to |sync_buffer_|. - sync_buffer_->PushBack(*algorithm_buffer_); - - // Extract data from |sync_buffer_| to |output|. - size_t num_output_samples_per_channel = output_size_samples_; - size_t num_output_samples = output_size_samples_ * sync_buffer_->Channels(); - if (num_output_samples > max_length) { - LOG(LS_WARNING) << "Output array is too short. " << max_length << " < " << - output_size_samples_ << " * " << sync_buffer_->Channels(); - num_output_samples = max_length; - num_output_samples_per_channel = static_cast( - max_length / sync_buffer_->Channels()); - } - int samples_from_sync = static_cast( - sync_buffer_->GetNextAudioInterleaved(num_output_samples_per_channel, - output)); - *num_channels = static_cast(sync_buffer_->Channels()); - LOG(LS_VERBOSE) << "Sync buffer (" << *num_channels << " channel(s)):" << - " insert " << algorithm_buffer_->Size() << " samples, extract " << - samples_from_sync << " samples"; - if (samples_from_sync != output_size_samples_) { - LOG_F(LS_ERROR) << "samples_from_sync != output_size_samples_"; - // TODO(minyue): treatment of under-run, filling zeros - memset(output, 0, num_output_samples * sizeof(int16_t)); - *samples_per_channel = output_size_samples_; - return kSampleUnderrun; - } - *samples_per_channel = output_size_samples_; - - // Should always have overlap samples left in the |sync_buffer_|. - assert(sync_buffer_->FutureLength() >= expand_->overlap_length()); - - if (play_dtmf) { - return_value = DtmfOverdub(dtmf_event, sync_buffer_->Channels(), output); - } - - // Update the background noise parameters if last operation wrote data - // straight from the decoder to the |sync_buffer_|. That is, none of the - // operations that modify the signal can be followed by a parameter update. - if ((last_mode_ == kModeNormal) || - (last_mode_ == kModeAccelerateFail) || - (last_mode_ == kModePreemptiveExpandFail) || - (last_mode_ == kModeRfc3389Cng) || - (last_mode_ == kModeCodecInternalCng)) { - background_noise_->Update(*sync_buffer_, *vad_.get()); - } - - if (operation == kDtmf) { - // DTMF data was written the end of |sync_buffer_|. - // Update index to end of DTMF data in |sync_buffer_|. - sync_buffer_->set_dtmf_index(sync_buffer_->Size()); - } - - if ((last_mode_ != kModeExpand) && (last_mode_ != kModeRfc3389Cng)) { - // If last operation was neither expand, nor comfort noise, calculate the - // |playout_timestamp_| from the |sync_buffer_|. However, do not update the - // |playout_timestamp_| if it would be moved "backwards". - uint32_t temp_timestamp = sync_buffer_->end_timestamp() - - static_cast(sync_buffer_->FutureLength()); - if (static_cast(temp_timestamp - playout_timestamp_) > 0) { - playout_timestamp_ = temp_timestamp; - } - } else { - // Use dead reckoning to estimate the |playout_timestamp_|. - playout_timestamp_ += output_size_samples_; - } - - if (decode_return_value) return decode_return_value; - return return_value; -} - -int NetEqImpl::GetDecision(Operations* operation, - PacketList* packet_list, - DtmfEvent* dtmf_event, - bool* play_dtmf) { - // Initialize output variables. - *play_dtmf = false; - *operation = kUndefined; - - // Increment time counters. - packet_buffer_->IncrementWaitingTimes(); - stats_.IncreaseCounter(output_size_samples_, fs_hz_); - - assert(sync_buffer_.get()); - uint32_t end_timestamp = sync_buffer_->end_timestamp(); - if (!new_codec_) { - packet_buffer_->DiscardOldPackets(end_timestamp); - } - const RTPHeader* header = packet_buffer_->NextRtpHeader(); - - if (decision_logic_->CngRfc3389On()) { - // Because of timestamp peculiarities, we have to "manually" disallow using - // a CNG packet with the same timestamp as the one that was last played. - // This can happen when using redundancy and will cause the timing to shift. - while (header && - decoder_database_->IsComfortNoise(header->payloadType) && - end_timestamp >= header->timestamp) { - // Don't use this packet, discard it. - // TODO(hlundin): Write test for this case. - if (packet_buffer_->DiscardNextPacket() != PacketBuffer::kOK) { - assert(false); // Must be ok by design. - } - // Check buffer again. - if (!new_codec_) { - packet_buffer_->DiscardOldPackets(end_timestamp); - } - header = packet_buffer_->NextRtpHeader(); - } - } - - assert(expand_.get()); - const int samples_left = static_cast(sync_buffer_->FutureLength() - - expand_->overlap_length()); - if (last_mode_ == kModeAccelerateSuccess || - last_mode_ == kModeAccelerateLowEnergy || - last_mode_ == kModePreemptiveExpandSuccess || - last_mode_ == kModePreemptiveExpandLowEnergy) { - // Subtract (samples_left + output_size_samples_) from sampleMemory. - decision_logic_->AddSampleMemory(-(samples_left + output_size_samples_)); - } - - // Check if it is time to play a DTMF event. - if (dtmf_buffer_->GetEvent(end_timestamp + - decision_logic_->generated_noise_samples(), - dtmf_event)) { - *play_dtmf = true; - } - - // Get instruction. - assert(sync_buffer_.get()); - assert(expand_.get()); - *operation = decision_logic_->GetDecision(*sync_buffer_, - *expand_, - decoder_frame_length_, - header, - last_mode_, - *play_dtmf, - &reset_decoder_); - - // Check if we already have enough samples in the |sync_buffer_|. If so, - // change decision to normal, unless the decision was merge, accelerate, or - // preemptive expand. - if (samples_left >= output_size_samples_ && - *operation != kMerge && - *operation != kAccelerate && - *operation != kPreemptiveExpand) { - *operation = kNormal; - return 0; - } - - decision_logic_->ExpandDecision(*operation == kExpand); - - // Check conditions for reset. - if (new_codec_ || *operation == kUndefined) { - // The only valid reason to get kUndefined is that new_codec_ is set. - assert(new_codec_); - if (*play_dtmf && !header) { - timestamp_ = dtmf_event->timestamp; - } else { - assert(header); - if (!header) { - LOG_F(LS_ERROR) << "Packet missing where it shouldn't."; - return -1; - } - timestamp_ = header->timestamp; - if (*operation == kRfc3389CngNoPacket -#ifndef LEGACY_BITEXACT - // Without this check, it can happen that a non-CNG packet is sent to - // the CNG decoder as if it was a SID frame. This is clearly a bug, - // but is kept for now to maintain bit-exactness with the test - // vectors. - && decoder_database_->IsComfortNoise(header->payloadType) -#endif - ) { - // Change decision to CNG packet, since we do have a CNG packet, but it - // was considered too early to use. Now, use it anyway. - *operation = kRfc3389Cng; - } else if (*operation != kRfc3389Cng) { - *operation = kNormal; - } - } - // Adjust |sync_buffer_| timestamp before setting |end_timestamp| to the - // new value. - sync_buffer_->IncreaseEndTimestamp(timestamp_ - end_timestamp); - end_timestamp = timestamp_; - new_codec_ = false; - decision_logic_->SoftReset(); - buffer_level_filter_->Reset(); - delay_manager_->Reset(); - stats_.ResetMcu(); - } - - int required_samples = output_size_samples_; - const int samples_10_ms = 80 * fs_mult_; - const int samples_20_ms = 2 * samples_10_ms; - const int samples_30_ms = 3 * samples_10_ms; - - switch (*operation) { - case kExpand: { - timestamp_ = end_timestamp; - return 0; - } - case kRfc3389CngNoPacket: - case kCodecInternalCng: { - return 0; - } - case kDtmf: { - // TODO(hlundin): Write test for this. - // Update timestamp. - timestamp_ = end_timestamp; - if (decision_logic_->generated_noise_samples() > 0 && - last_mode_ != kModeDtmf) { - // Make a jump in timestamp due to the recently played comfort noise. - uint32_t timestamp_jump = decision_logic_->generated_noise_samples(); - sync_buffer_->IncreaseEndTimestamp(timestamp_jump); - timestamp_ += timestamp_jump; - } - decision_logic_->set_generated_noise_samples(0); - return 0; - } - case kAccelerate: { - // In order to do a accelerate we need at least 30 ms of audio data. - if (samples_left >= samples_30_ms) { - // Already have enough data, so we do not need to extract any more. - decision_logic_->set_sample_memory(samples_left); - decision_logic_->set_prev_time_scale(true); - return 0; - } else if (samples_left >= samples_10_ms && - decoder_frame_length_ >= samples_30_ms) { - // Avoid decoding more data as it might overflow the playout buffer. - *operation = kNormal; - return 0; - } else if (samples_left < samples_20_ms && - decoder_frame_length_ < samples_30_ms) { - // Build up decoded data by decoding at least 20 ms of audio data. Do - // not perform accelerate yet, but wait until we only need to do one - // decoding. - required_samples = 2 * output_size_samples_; - *operation = kNormal; - } - // If none of the above is true, we have one of two possible situations: - // (1) 20 ms <= samples_left < 30 ms and decoder_frame_length_ < 30 ms; or - // (2) samples_left < 10 ms and decoder_frame_length_ >= 30 ms. - // In either case, we move on with the accelerate decision, and decode one - // frame now. - break; - } - case kPreemptiveExpand: { - // In order to do a preemptive expand we need at least 30 ms of decoded - // audio data. - if ((samples_left >= samples_30_ms) || - (samples_left >= samples_10_ms && - decoder_frame_length_ >= samples_30_ms)) { - // Already have enough data, so we do not need to extract any more. - // Or, avoid decoding more data as it might overflow the playout buffer. - // Still try preemptive expand, though. - decision_logic_->set_sample_memory(samples_left); - decision_logic_->set_prev_time_scale(true); - return 0; - } - if (samples_left < samples_20_ms && - decoder_frame_length_ < samples_30_ms) { - // Build up decoded data by decoding at least 20 ms of audio data. - // Still try to perform preemptive expand. - required_samples = 2 * output_size_samples_; - } - // Move on with the preemptive expand decision. - break; - } - default: { - // Do nothing. - } - } - - // Get packets from buffer. - int extracted_samples = 0; - if (header && - *operation != kAlternativePlc && - *operation != kAlternativePlcIncreaseTimestamp && - *operation != kAudioRepetition && - *operation != kAudioRepetitionIncreaseTimestamp) { - sync_buffer_->IncreaseEndTimestamp(header->timestamp - end_timestamp); - if (decision_logic_->CngOff()) { - // Adjustment of timestamp only corresponds to an actual packet loss - // if comfort noise is not played. If comfort noise was just played, - // this adjustment of timestamp is only done to get back in sync with the - // stream timestamp; no loss to report. - stats_.LostSamples(header->timestamp - end_timestamp); - } - - if (*operation != kRfc3389Cng) { - // We are about to decode and use a non-CNG packet. - decision_logic_->SetCngOff(); - } - // Reset CNG timestamp as a new packet will be delivered. - // (Also if this is a CNG packet, since playedOutTS is updated.) - decision_logic_->set_generated_noise_samples(0); - - extracted_samples = ExtractPackets(required_samples, packet_list); - if (extracted_samples < 0) { - LOG_F(LS_WARNING) << "Failed to extract packets from buffer."; - return kPacketBufferCorruption; - } - } - - if (*operation == kAccelerate || - *operation == kPreemptiveExpand) { - decision_logic_->set_sample_memory(samples_left + extracted_samples); - decision_logic_->set_prev_time_scale(true); - } - - if (*operation == kAccelerate) { - // Check that we have enough data (30ms) to do accelerate. - if (extracted_samples + samples_left < samples_30_ms) { - // TODO(hlundin): Write test for this. - // Not enough, do normal operation instead. - *operation = kNormal; - } - } - - timestamp_ = end_timestamp; - return 0; -} - -int NetEqImpl::Decode(PacketList* packet_list, Operations* operation, - int* decoded_length, - AudioDecoder::SpeechType* speech_type) { - *speech_type = AudioDecoder::kSpeech; - AudioDecoder* decoder = NULL; - if (!packet_list->empty()) { - const Packet* packet = packet_list->front(); - int payload_type = packet->header.payloadType; - if (!decoder_database_->IsComfortNoise(payload_type)) { - decoder = decoder_database_->GetDecoder(payload_type); - assert(decoder); - if (!decoder) { - LOG_FERR1(LS_WARNING, GetDecoder, payload_type); - PacketBuffer::DeleteAllPackets(packet_list); - return kDecoderNotFound; - } - bool decoder_changed; - decoder_database_->SetActiveDecoder(payload_type, &decoder_changed); - if (decoder_changed) { - // We have a new decoder. Re-init some values. - const DecoderDatabase::DecoderInfo* decoder_info = decoder_database_ - ->GetDecoderInfo(payload_type); - assert(decoder_info); - if (!decoder_info) { - LOG_FERR1(LS_WARNING, GetDecoderInfo, payload_type); - PacketBuffer::DeleteAllPackets(packet_list); - return kDecoderNotFound; - } - // We should have correct sampling rate and number of channels. They - // are set when packets are inserted. - if (decoder_info->fs_hz != fs_hz_ || - decoder->channels() != algorithm_buffer_->Channels()) { - LOG_F(LS_ERROR) << "Sampling rate or number of channels mismatch."; - assert(false); - SetSampleRateAndChannels(decoder_info->fs_hz, decoder->channels()); - } - sync_buffer_->set_end_timestamp(timestamp_); - playout_timestamp_ = timestamp_; - } - } - } - - if (reset_decoder_) { - // TODO(hlundin): Write test for this. - // Reset decoder. - if (decoder) { - decoder->Init(); - } - // Reset comfort noise decoder. - AudioDecoder* cng_decoder = decoder_database_->GetActiveCngDecoder(); - if (cng_decoder) { - cng_decoder->Init(); - } - reset_decoder_ = false; - } - -#ifdef LEGACY_BITEXACT - // Due to a bug in old SignalMCU, it could happen that CNG operation was - // decided, but a speech packet was provided. The speech packet will be used - // to update the comfort noise decoder, as if it was a SID frame, which is - // clearly wrong. - if (*operation == kRfc3389Cng) { - return 0; - } -#endif - - *decoded_length = 0; - // Update codec-internal PLC state. - if ((*operation == kMerge) && decoder && decoder->HasDecodePlc()) { - decoder->DecodePlc(1, &decoded_buffer_[*decoded_length]); - } - - int return_value = DecodeLoop(packet_list, operation, decoder, - decoded_length, speech_type); - - if (*decoded_length < 0) { - // Error returned from the decoder. - *decoded_length = 0; - sync_buffer_->IncreaseEndTimestamp(decoder_frame_length_); - int error_code = 0; - if (decoder) - error_code = decoder->ErrorCode(); - if (error_code != 0) { - // Got some error code from the decoder. - decoder_error_code_ = error_code; - return_value = kDecoderErrorCode; - } else { - // Decoder does not implement error codes. Return generic error. - return_value = kOtherDecoderError; - } - LOG_FERR2(LS_WARNING, DecodeLoop, error_code, packet_list->size()); - *operation = kExpand; // Do expansion to get data instead. - } - if (*speech_type != AudioDecoder::kComfortNoise) { - // Don't increment timestamp if codec returned CNG speech type - // since in this case, the we will increment the CNGplayedTS counter. - // Increase with number of samples per channel. - assert(*decoded_length == 0 || - (decoder && decoder->channels() == sync_buffer_->Channels())); - sync_buffer_->IncreaseEndTimestamp( - *decoded_length / static_cast(sync_buffer_->Channels())); - } - return return_value; -} - -int NetEqImpl::DecodeLoop(PacketList* packet_list, Operations* operation, - AudioDecoder* decoder, int* decoded_length, - AudioDecoder::SpeechType* speech_type) { - Packet* packet = NULL; - if (!packet_list->empty()) { - packet = packet_list->front(); - } - // Do decoding. - while (packet && - !decoder_database_->IsComfortNoise(packet->header.payloadType)) { - assert(decoder); // At this point, we must have a decoder object. - // The number of channels in the |sync_buffer_| should be the same as the - // number decoder channels. - assert(sync_buffer_->Channels() == decoder->channels()); - assert(decoded_buffer_length_ >= kMaxFrameSize * decoder->channels()); - assert(*operation == kNormal || *operation == kAccelerate || - *operation == kMerge || *operation == kPreemptiveExpand); - packet_list->pop_front(); - int payload_length = packet->payload_length; - int16_t decode_length; - if (packet->sync_packet) { - // Decode to silence with the same frame size as the last decode. - LOG(LS_VERBOSE) << "Decoding sync-packet: " << - " ts=" << packet->header.timestamp << - ", sn=" << packet->header.sequenceNumber << - ", pt=" << static_cast(packet->header.payloadType) << - ", ssrc=" << packet->header.ssrc << - ", len=" << packet->payload_length; - memset(&decoded_buffer_[*decoded_length], 0, decoder_frame_length_ * - decoder->channels() * sizeof(decoded_buffer_[0])); - decode_length = decoder_frame_length_; - } else if (!packet->primary) { - // This is a redundant payload; call the special decoder method. - LOG(LS_VERBOSE) << "Decoding packet (redundant):" << - " ts=" << packet->header.timestamp << - ", sn=" << packet->header.sequenceNumber << - ", pt=" << static_cast(packet->header.payloadType) << - ", ssrc=" << packet->header.ssrc << - ", len=" << packet->payload_length; - decode_length = decoder->DecodeRedundant( - packet->payload, packet->payload_length, - &decoded_buffer_[*decoded_length], speech_type); - } else { - LOG(LS_VERBOSE) << "Decoding packet: ts=" << packet->header.timestamp << - ", sn=" << packet->header.sequenceNumber << - ", pt=" << static_cast(packet->header.payloadType) << - ", ssrc=" << packet->header.ssrc << - ", len=" << packet->payload_length; - decode_length = decoder->Decode(packet->payload, - packet->payload_length, - &decoded_buffer_[*decoded_length], - speech_type); - } - - delete[] packet->payload; - delete packet; - packet = NULL; - if (decode_length > 0) { - *decoded_length += decode_length; - // Update |decoder_frame_length_| with number of samples per channel. - decoder_frame_length_ = decode_length / - static_cast(decoder->channels()); - LOG(LS_VERBOSE) << "Decoded " << decode_length << " samples (" << - decoder->channels() << " channel(s) -> " << decoder_frame_length_ << - " samples per channel)"; - } else if (decode_length < 0) { - // Error. - LOG_FERR2(LS_WARNING, Decode, decode_length, payload_length); - *decoded_length = -1; - PacketBuffer::DeleteAllPackets(packet_list); - break; - } - if (*decoded_length > static_cast(decoded_buffer_length_)) { - // Guard against overflow. - LOG_F(LS_WARNING) << "Decoded too much."; - PacketBuffer::DeleteAllPackets(packet_list); - return kDecodedTooMuch; - } - if (!packet_list->empty()) { - packet = packet_list->front(); - } else { - packet = NULL; - } - } // End of decode loop. - - // If the list is not empty at this point, either a decoding error terminated - // the while-loop, or list must hold exactly one CNG packet. - assert(packet_list->empty() || *decoded_length < 0 || - (packet_list->size() == 1 && packet && - decoder_database_->IsComfortNoise(packet->header.payloadType))); - return 0; -} - -void NetEqImpl::DoNormal(const int16_t* decoded_buffer, size_t decoded_length, - AudioDecoder::SpeechType speech_type, bool play_dtmf) { - assert(normal_.get()); - assert(mute_factor_array_.get()); - normal_->Process(decoded_buffer, decoded_length, last_mode_, - mute_factor_array_.get(), algorithm_buffer_.get()); - if (decoded_length != 0) { - last_mode_ = kModeNormal; - } - - // If last packet was decoded as an inband CNG, set mode to CNG instead. - if ((speech_type == AudioDecoder::kComfortNoise) - || ((last_mode_ == kModeCodecInternalCng) - && (decoded_length == 0))) { - // TODO(hlundin): Remove second part of || statement above. - last_mode_ = kModeCodecInternalCng; - } - - if (!play_dtmf) { - dtmf_tone_generator_->Reset(); - } -} - -void NetEqImpl::DoMerge(int16_t* decoded_buffer, size_t decoded_length, - AudioDecoder::SpeechType speech_type, bool play_dtmf) { - assert(mute_factor_array_.get()); - assert(merge_.get()); - int new_length = merge_->Process(decoded_buffer, decoded_length, - mute_factor_array_.get(), - algorithm_buffer_.get()); - - // Update in-call and post-call statistics. - if (expand_->MuteFactor(0) == 0) { - // Expand generates only noise. - stats_.ExpandedNoiseSamples(new_length - static_cast(decoded_length)); - } else { - // Expansion generates more than only noise. - stats_.ExpandedVoiceSamples(new_length - static_cast(decoded_length)); - } - - last_mode_ = kModeMerge; - // If last packet was decoded as an inband CNG, set mode to CNG instead. - if (speech_type == AudioDecoder::kComfortNoise) { - last_mode_ = kModeCodecInternalCng; - } - expand_->Reset(); - if (!play_dtmf) { - dtmf_tone_generator_->Reset(); - } -} - -int NetEqImpl::DoExpand(bool play_dtmf) { - while ((sync_buffer_->FutureLength() - expand_->overlap_length()) < - static_cast(output_size_samples_)) { - algorithm_buffer_->Clear(); - int return_value = expand_->Process(algorithm_buffer_.get()); - int length = static_cast(algorithm_buffer_->Size()); - - // Update in-call and post-call statistics. - if (expand_->MuteFactor(0) == 0) { - // Expand operation generates only noise. - stats_.ExpandedNoiseSamples(length); - } else { - // Expand operation generates more than only noise. - stats_.ExpandedVoiceSamples(length); - } - - last_mode_ = kModeExpand; - - if (return_value < 0) { - return return_value; - } - - sync_buffer_->PushBack(*algorithm_buffer_); - algorithm_buffer_->Clear(); - } - if (!play_dtmf) { - dtmf_tone_generator_->Reset(); - } - return 0; -} - -int NetEqImpl::DoAccelerate(int16_t* decoded_buffer, size_t decoded_length, - AudioDecoder::SpeechType speech_type, - bool play_dtmf) { - const size_t required_samples = 240 * fs_mult_; // Must have 30 ms. - size_t borrowed_samples_per_channel = 0; - size_t num_channels = algorithm_buffer_->Channels(); - size_t decoded_length_per_channel = decoded_length / num_channels; - if (decoded_length_per_channel < required_samples) { - // Must move data from the |sync_buffer_| in order to get 30 ms. - borrowed_samples_per_channel = static_cast(required_samples - - decoded_length_per_channel); - memmove(&decoded_buffer[borrowed_samples_per_channel * num_channels], - decoded_buffer, - sizeof(int16_t) * decoded_length); - sync_buffer_->ReadInterleavedFromEnd(borrowed_samples_per_channel, - decoded_buffer); - decoded_length = required_samples * num_channels; - } - - int16_t samples_removed; - Accelerate::ReturnCodes return_code = accelerate_->Process( - decoded_buffer, decoded_length, algorithm_buffer_.get(), - &samples_removed); - stats_.AcceleratedSamples(samples_removed); - switch (return_code) { - case Accelerate::kSuccess: - last_mode_ = kModeAccelerateSuccess; - break; - case Accelerate::kSuccessLowEnergy: - last_mode_ = kModeAccelerateLowEnergy; - break; - case Accelerate::kNoStretch: - last_mode_ = kModeAccelerateFail; - break; - case Accelerate::kError: - // TODO(hlundin): Map to kModeError instead? - last_mode_ = kModeAccelerateFail; - return kAccelerateError; - } - - if (borrowed_samples_per_channel > 0) { - // Copy borrowed samples back to the |sync_buffer_|. - size_t length = algorithm_buffer_->Size(); - if (length < borrowed_samples_per_channel) { - // This destroys the beginning of the buffer, but will not cause any - // problems. - sync_buffer_->ReplaceAtIndex(*algorithm_buffer_, - sync_buffer_->Size() - - borrowed_samples_per_channel); - sync_buffer_->PushFrontZeros(borrowed_samples_per_channel - length); - algorithm_buffer_->PopFront(length); - assert(algorithm_buffer_->Empty()); - } else { - sync_buffer_->ReplaceAtIndex(*algorithm_buffer_, - borrowed_samples_per_channel, - sync_buffer_->Size() - - borrowed_samples_per_channel); - algorithm_buffer_->PopFront(borrowed_samples_per_channel); - } - } - - // If last packet was decoded as an inband CNG, set mode to CNG instead. - if (speech_type == AudioDecoder::kComfortNoise) { - last_mode_ = kModeCodecInternalCng; - } - if (!play_dtmf) { - dtmf_tone_generator_->Reset(); - } - expand_->Reset(); - return 0; -} - -int NetEqImpl::DoPreemptiveExpand(int16_t* decoded_buffer, - size_t decoded_length, - AudioDecoder::SpeechType speech_type, - bool play_dtmf) { - const size_t required_samples = 240 * fs_mult_; // Must have 30 ms. - size_t num_channels = algorithm_buffer_->Channels(); - int borrowed_samples_per_channel = 0; - int old_borrowed_samples_per_channel = 0; - size_t decoded_length_per_channel = decoded_length / num_channels; - if (decoded_length_per_channel < required_samples) { - // Must move data from the |sync_buffer_| in order to get 30 ms. - borrowed_samples_per_channel = static_cast(required_samples - - decoded_length_per_channel); - // Calculate how many of these were already played out. - old_borrowed_samples_per_channel = static_cast( - borrowed_samples_per_channel - sync_buffer_->FutureLength()); - old_borrowed_samples_per_channel = std::max( - 0, old_borrowed_samples_per_channel); - memmove(&decoded_buffer[borrowed_samples_per_channel * num_channels], - decoded_buffer, - sizeof(int16_t) * decoded_length); - sync_buffer_->ReadInterleavedFromEnd(borrowed_samples_per_channel, - decoded_buffer); - decoded_length = required_samples * num_channels; - } - - int16_t samples_added; - PreemptiveExpand::ReturnCodes return_code = preemptive_expand_->Process( - decoded_buffer, static_cast(decoded_length), - old_borrowed_samples_per_channel, - algorithm_buffer_.get(), &samples_added); - stats_.PreemptiveExpandedSamples(samples_added); - switch (return_code) { - case PreemptiveExpand::kSuccess: - last_mode_ = kModePreemptiveExpandSuccess; - break; - case PreemptiveExpand::kSuccessLowEnergy: - last_mode_ = kModePreemptiveExpandLowEnergy; - break; - case PreemptiveExpand::kNoStretch: - last_mode_ = kModePreemptiveExpandFail; - break; - case PreemptiveExpand::kError: - // TODO(hlundin): Map to kModeError instead? - last_mode_ = kModePreemptiveExpandFail; - return kPreemptiveExpandError; - } - - if (borrowed_samples_per_channel > 0) { - // Copy borrowed samples back to the |sync_buffer_|. - sync_buffer_->ReplaceAtIndex( - *algorithm_buffer_, borrowed_samples_per_channel, - sync_buffer_->Size() - borrowed_samples_per_channel); - algorithm_buffer_->PopFront(borrowed_samples_per_channel); - } - - // If last packet was decoded as an inband CNG, set mode to CNG instead. - if (speech_type == AudioDecoder::kComfortNoise) { - last_mode_ = kModeCodecInternalCng; - } - if (!play_dtmf) { - dtmf_tone_generator_->Reset(); - } - expand_->Reset(); - return 0; -} - -int NetEqImpl::DoRfc3389Cng(PacketList* packet_list, bool play_dtmf) { - if (!packet_list->empty()) { - // Must have exactly one SID frame at this point. - assert(packet_list->size() == 1); - Packet* packet = packet_list->front(); - packet_list->pop_front(); - if (!decoder_database_->IsComfortNoise(packet->header.payloadType)) { -#ifdef LEGACY_BITEXACT - // This can happen due to a bug in GetDecision. Change the payload type - // to a CNG type, and move on. Note that this means that we are in fact - // sending a non-CNG payload to the comfort noise decoder for decoding. - // Clearly wrong, but will maintain bit-exactness with legacy. - if (fs_hz_ == 8000) { - packet->header.payloadType = - decoder_database_->GetRtpPayloadType(kDecoderCNGnb); - } else if (fs_hz_ == 16000) { - packet->header.payloadType = - decoder_database_->GetRtpPayloadType(kDecoderCNGwb); - } else if (fs_hz_ == 32000) { - packet->header.payloadType = - decoder_database_->GetRtpPayloadType(kDecoderCNGswb32kHz); - } else if (fs_hz_ == 48000) { - packet->header.payloadType = - decoder_database_->GetRtpPayloadType(kDecoderCNGswb48kHz); - } - assert(decoder_database_->IsComfortNoise(packet->header.payloadType)); -#else - LOG(LS_ERROR) << "Trying to decode non-CNG payload as CNG."; - return kOtherError; -#endif - } - // UpdateParameters() deletes |packet|. - if (comfort_noise_->UpdateParameters(packet) == - ComfortNoise::kInternalError) { - LOG_FERR0(LS_WARNING, UpdateParameters); - algorithm_buffer_->Zeros(output_size_samples_); - return -comfort_noise_->internal_error_code(); - } - } - int cn_return = comfort_noise_->Generate(output_size_samples_, - algorithm_buffer_.get()); - expand_->Reset(); - last_mode_ = kModeRfc3389Cng; - if (!play_dtmf) { - dtmf_tone_generator_->Reset(); - } - if (cn_return == ComfortNoise::kInternalError) { - LOG_FERR1(LS_WARNING, comfort_noise_->Generate, cn_return); - decoder_error_code_ = comfort_noise_->internal_error_code(); - return kComfortNoiseErrorCode; - } else if (cn_return == ComfortNoise::kUnknownPayloadType) { - LOG_FERR1(LS_WARNING, comfort_noise_->Generate, cn_return); - return kUnknownRtpPayloadType; - } - return 0; -} - -void NetEqImpl::DoCodecInternalCng() { - int length = 0; - // TODO(hlundin): Will probably need a longer buffer for multi-channel. - int16_t decoded_buffer[kMaxFrameSize]; - AudioDecoder* decoder = decoder_database_->GetActiveDecoder(); - if (decoder) { - const uint8_t* dummy_payload = NULL; - AudioDecoder::SpeechType speech_type; - length = decoder->Decode(dummy_payload, 0, decoded_buffer, &speech_type); - } - assert(mute_factor_array_.get()); - normal_->Process(decoded_buffer, length, last_mode_, mute_factor_array_.get(), - algorithm_buffer_.get()); - last_mode_ = kModeCodecInternalCng; - expand_->Reset(); -} - -int NetEqImpl::DoDtmf(const DtmfEvent& dtmf_event, bool* play_dtmf) { - // This block of the code and the block further down, handling |dtmf_switch| - // are commented out. Otherwise playing out-of-band DTMF would fail in VoE - // test, DtmfTest.ManualSuccessfullySendsOutOfBandTelephoneEvents. This is - // equivalent to |dtmf_switch| always be false. - // - // See http://webrtc-codereview.appspot.com/1195004/ for discussion - // On this issue. This change might cause some glitches at the point of - // switch from audio to DTMF. Issue 1545 is filed to track this. - // - // bool dtmf_switch = false; - // if ((last_mode_ != kModeDtmf) && dtmf_tone_generator_->initialized()) { - // // Special case; see below. - // // We must catch this before calling Generate, since |initialized| is - // // modified in that call. - // dtmf_switch = true; - // } - - int dtmf_return_value = 0; - if (!dtmf_tone_generator_->initialized()) { - // Initialize if not already done. - dtmf_return_value = dtmf_tone_generator_->Init(fs_hz_, dtmf_event.event_no, - dtmf_event.volume); - } - - if (dtmf_return_value == 0) { - // Generate DTMF signal. - dtmf_return_value = dtmf_tone_generator_->Generate(output_size_samples_, - algorithm_buffer_.get()); - } - - if (dtmf_return_value < 0) { - algorithm_buffer_->Zeros(output_size_samples_); - return dtmf_return_value; - } - - // if (dtmf_switch) { - // // This is the special case where the previous operation was DTMF - // // overdub, but the current instruction is "regular" DTMF. We must make - // // sure that the DTMF does not have any discontinuities. The first DTMF - // // sample that we generate now must be played out immediately, therefore - // // it must be copied to the speech buffer. - // // TODO(hlundin): This code seems incorrect. (Legacy.) Write test and - // // verify correct operation. - // assert(false); - // // Must generate enough data to replace all of the |sync_buffer_| - // // "future". - // int required_length = sync_buffer_->FutureLength(); - // assert(dtmf_tone_generator_->initialized()); - // dtmf_return_value = dtmf_tone_generator_->Generate(required_length, - // algorithm_buffer_); - // assert((size_t) required_length == algorithm_buffer_->Size()); - // if (dtmf_return_value < 0) { - // algorithm_buffer_->Zeros(output_size_samples_); - // return dtmf_return_value; - // } - // - // // Overwrite the "future" part of the speech buffer with the new DTMF - // // data. - // // TODO(hlundin): It seems that this overwriting has gone lost. - // // Not adapted for multi-channel yet. - // assert(algorithm_buffer_->Channels() == 1); - // if (algorithm_buffer_->Channels() != 1) { - // LOG(LS_WARNING) << "DTMF not supported for more than one channel"; - // return kStereoNotSupported; - // } - // // Shuffle the remaining data to the beginning of algorithm buffer. - // algorithm_buffer_->PopFront(sync_buffer_->FutureLength()); - // } - - sync_buffer_->IncreaseEndTimestamp(output_size_samples_); - expand_->Reset(); - last_mode_ = kModeDtmf; - - // Set to false because the DTMF is already in the algorithm buffer. - *play_dtmf = false; - return 0; -} - -void NetEqImpl::DoAlternativePlc(bool increase_timestamp) { - AudioDecoder* decoder = decoder_database_->GetActiveDecoder(); - int length; - if (decoder && decoder->HasDecodePlc()) { - // Use the decoder's packet-loss concealment. - // TODO(hlundin): Will probably need a longer buffer for multi-channel. - int16_t decoded_buffer[kMaxFrameSize]; - length = decoder->DecodePlc(1, decoded_buffer); - if (length > 0) { - algorithm_buffer_->PushBackInterleaved(decoded_buffer, length); - } else { - length = 0; - } - } else { - // Do simple zero-stuffing. - length = output_size_samples_; - algorithm_buffer_->Zeros(length); - // By not advancing the timestamp, NetEq inserts samples. - stats_.AddZeros(length); - } - if (increase_timestamp) { - sync_buffer_->IncreaseEndTimestamp(length); - } - expand_->Reset(); -} - -int NetEqImpl::DtmfOverdub(const DtmfEvent& dtmf_event, size_t num_channels, - int16_t* output) const { - size_t out_index = 0; - int overdub_length = output_size_samples_; // Default value. - - if (sync_buffer_->dtmf_index() > sync_buffer_->next_index()) { - // Special operation for transition from "DTMF only" to "DTMF overdub". - out_index = std::min( - sync_buffer_->dtmf_index() - sync_buffer_->next_index(), - static_cast(output_size_samples_)); - overdub_length = output_size_samples_ - static_cast(out_index); - } - - AudioMultiVector dtmf_output(num_channels); - int dtmf_return_value = 0; - if (!dtmf_tone_generator_->initialized()) { - dtmf_return_value = dtmf_tone_generator_->Init(fs_hz_, dtmf_event.event_no, - dtmf_event.volume); - } - if (dtmf_return_value == 0) { - dtmf_return_value = dtmf_tone_generator_->Generate(overdub_length, - &dtmf_output); - assert((size_t) overdub_length == dtmf_output.Size()); - } - dtmf_output.ReadInterleaved(overdub_length, &output[out_index]); - return dtmf_return_value < 0 ? dtmf_return_value : 0; -} - -int NetEqImpl::ExtractPackets(int required_samples, PacketList* packet_list) { - bool first_packet = true; - uint8_t prev_payload_type = 0; - uint32_t prev_timestamp = 0; - uint16_t prev_sequence_number = 0; - bool next_packet_available = false; - - const RTPHeader* header = packet_buffer_->NextRtpHeader(); - assert(header); - if (!header) { - return -1; - } - uint32_t first_timestamp = header->timestamp; - int extracted_samples = 0; - - // Packet extraction loop. - do { - timestamp_ = header->timestamp; - int discard_count = 0; - Packet* packet = packet_buffer_->GetNextPacket(&discard_count); - // |header| may be invalid after the |packet_buffer_| operation. - header = NULL; - if (!packet) { - LOG_FERR1(LS_ERROR, GetNextPacket, discard_count) << - "Should always be able to extract a packet here"; - assert(false); // Should always be able to extract a packet here. - return -1; - } - stats_.PacketsDiscarded(discard_count); - // Store waiting time in ms; packets->waiting_time is in "output blocks". - stats_.StoreWaitingTime(packet->waiting_time * kOutputSizeMs); - assert(packet->payload_length > 0); - packet_list->push_back(packet); // Store packet in list. - - if (first_packet) { - first_packet = false; - decoded_packet_sequence_number_ = prev_sequence_number = - packet->header.sequenceNumber; - decoded_packet_timestamp_ = prev_timestamp = packet->header.timestamp; - prev_payload_type = packet->header.payloadType; - } - - // Store number of extracted samples. - int packet_duration = 0; - AudioDecoder* decoder = decoder_database_->GetDecoder( - packet->header.payloadType); - if (decoder) { - packet_duration = packet->sync_packet ? decoder_frame_length_ : - decoder->PacketDuration(packet->payload, packet->payload_length); - } else { - LOG_FERR1(LS_WARNING, GetDecoder, packet->header.payloadType) << - "Could not find a decoder for a packet about to be extracted."; - assert(false); - } - if (packet_duration <= 0) { - // Decoder did not return a packet duration. Assume that the packet - // contains the same number of samples as the previous one. - packet_duration = decoder_frame_length_; - } - extracted_samples = packet->header.timestamp - first_timestamp + - packet_duration; - - // Check what packet is available next. - header = packet_buffer_->NextRtpHeader(); - next_packet_available = false; - if (header && prev_payload_type == header->payloadType) { - int16_t seq_no_diff = header->sequenceNumber - prev_sequence_number; - int32_t ts_diff = header->timestamp - prev_timestamp; - if (seq_no_diff == 1 || - (seq_no_diff == 0 && ts_diff == decoder_frame_length_)) { - // The next sequence number is available, or the next part of a packet - // that was split into pieces upon insertion. - next_packet_available = true; - } - prev_sequence_number = header->sequenceNumber; - } - } while (extracted_samples < required_samples && next_packet_available); - - return extracted_samples; -} - -void NetEqImpl::SetSampleRateAndChannels(int fs_hz, size_t channels) { - LOG_API2(fs_hz, channels); - // TODO(hlundin): Change to an enumerator and skip assert. - assert(fs_hz == 8000 || fs_hz == 16000 || fs_hz == 32000 || fs_hz == 48000); - assert(channels > 0); - - fs_hz_ = fs_hz; - fs_mult_ = fs_hz / 8000; - output_size_samples_ = kOutputSizeMs * 8 * fs_mult_; - decoder_frame_length_ = 3 * output_size_samples_; // Initialize to 30ms. - - last_mode_ = kModeNormal; - - // Create a new array of mute factors and set all to 1. - mute_factor_array_.reset(new int16_t[channels]); - for (size_t i = 0; i < channels; ++i) { - mute_factor_array_[i] = 16384; // 1.0 in Q14. - } - - // Reset comfort noise decoder, if there is one active. - AudioDecoder* cng_decoder = decoder_database_->GetActiveCngDecoder(); - if (cng_decoder) { - cng_decoder->Init(); - } - - // Reinit post-decode VAD with new sample rate. - assert(vad_.get()); // Cannot be NULL here. - vad_->Init(); - - // Delete algorithm buffer and create a new one. - algorithm_buffer_.reset(new AudioMultiVector(channels)); - - // Delete sync buffer and create a new one. - sync_buffer_.reset(new SyncBuffer(channels, kSyncBufferSize * fs_mult_)); - - - // Delete BackgroundNoise object and create a new one, while preserving its - // mode. - NetEqBackgroundNoiseMode current_mode = kBgnOn; - if (background_noise_.get()) - current_mode = background_noise_->mode(); - background_noise_.reset(new BackgroundNoise(channels)); - background_noise_->set_mode(current_mode); - - // Reset random vector. - random_vector_.Reset(); - - // Delete Expand object and create a new one. - expand_.reset(expand_factory_->Create(background_noise_.get(), - sync_buffer_.get(), &random_vector_, - fs_hz, channels)); - // Move index so that we create a small set of future samples (all 0). - sync_buffer_->set_next_index(sync_buffer_->next_index() - - expand_->overlap_length()); - - normal_.reset(new Normal(fs_hz, decoder_database_.get(), *background_noise_, - expand_.get())); - merge_.reset(new Merge(fs_hz, channels, expand_.get(), sync_buffer_.get())); - accelerate_.reset( - accelerate_factory_->Create(fs_hz, channels, *background_noise_)); - preemptive_expand_.reset( - preemptive_expand_factory_->Create(fs_hz, channels, *background_noise_)); - - // Delete ComfortNoise object and create a new one. - comfort_noise_.reset(new ComfortNoise(fs_hz, decoder_database_.get(), - sync_buffer_.get())); - - // Verify that |decoded_buffer_| is long enough. - if (decoded_buffer_length_ < kMaxFrameSize * channels) { - // Reallocate to larger size. - decoded_buffer_length_ = kMaxFrameSize * channels; - decoded_buffer_.reset(new int16_t[decoded_buffer_length_]); - } - - // Communicate new sample rate and output size to DecisionLogic object. - assert(decision_logic_.get()); - decision_logic_->SetSampleRate(fs_hz_, output_size_samples_); -} - -NetEqOutputType NetEqImpl::LastOutputType() { - assert(vad_.get()); - assert(expand_.get()); - if (last_mode_ == kModeCodecInternalCng || last_mode_ == kModeRfc3389Cng) { - return kOutputCNG; - } else if (last_mode_ == kModeExpand && expand_->MuteFactor(0) == 0) { - // Expand mode has faded down to background noise only (very long expand). - return kOutputPLCtoCNG; - } else if (last_mode_ == kModeExpand) { - return kOutputPLC; - } else if (vad_->running() && !vad_->active_speech()) { - return kOutputVADPassive; - } else { - return kOutputNormal; - } -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_impl.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,369 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_NETEQ_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_NETEQ_IMPL_H_ - -#include - -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" -#include "webrtc/modules/audio_coding/neteq4/defines.h" -#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" -#include "webrtc/modules/audio_coding/neteq4/packet.h" // Declare PacketList. -#include "webrtc/modules/audio_coding/neteq4/random_vector.h" -#include "webrtc/modules/audio_coding/neteq4/rtcp.h" -#include "webrtc/modules/audio_coding/neteq4/statistics_calculator.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Forward declarations. -class Accelerate; -class BackgroundNoise; -class BufferLevelFilter; -class ComfortNoise; -class CriticalSectionWrapper; -class DecisionLogic; -class DecoderDatabase; -class DelayManager; -class DelayPeakDetector; -class DtmfBuffer; -class DtmfToneGenerator; -class Expand; -class Merge; -class Normal; -class PacketBuffer; -class PayloadSplitter; -class PostDecodeVad; -class PreemptiveExpand; -class RandomVector; -class SyncBuffer; -class TimestampScaler; -struct AccelerateFactory; -struct DtmfEvent; -struct ExpandFactory; -struct PreemptiveExpandFactory; - -class NetEqImpl : public webrtc::NetEq { - public: - // Creates a new NetEqImpl object. The object will assume ownership of all - // injected dependencies, and will delete them when done. - NetEqImpl(int fs, - BufferLevelFilter* buffer_level_filter, - DecoderDatabase* decoder_database, - DelayManager* delay_manager, - DelayPeakDetector* delay_peak_detector, - DtmfBuffer* dtmf_buffer, - DtmfToneGenerator* dtmf_tone_generator, - PacketBuffer* packet_buffer, - PayloadSplitter* payload_splitter, - TimestampScaler* timestamp_scaler, - AccelerateFactory* accelerate_factory, - ExpandFactory* expand_factory, - PreemptiveExpandFactory* preemptive_expand_factory); - - virtual ~NetEqImpl(); - - // Inserts a new packet into NetEq. The |receive_timestamp| is an indication - // of the time when the packet was received, and should be measured with - // the same tick rate as the RTP timestamp of the current payload. - // Returns 0 on success, -1 on failure. - virtual int InsertPacket(const WebRtcRTPHeader& rtp_header, - const uint8_t* payload, - int length_bytes, - uint32_t receive_timestamp); - - // Inserts a sync-packet into packet queue. Sync-packets are decoded to - // silence and are intended to keep AV-sync intact in an event of long packet - // losses when Video NACK is enabled but Audio NACK is not. Clients of NetEq - // might insert sync-packet when they observe that buffer level of NetEq is - // decreasing below a certain threshold, defined by the application. - // Sync-packets should have the same payload type as the last audio payload - // type, i.e. they cannot have DTMF or CNG payload type, nor a codec change - // can be implied by inserting a sync-packet. - // Returns kOk on success, kFail on failure. - virtual int InsertSyncPacket(const WebRtcRTPHeader& rtp_header, - uint32_t receive_timestamp); - - // Instructs NetEq to deliver 10 ms of audio data. The data is written to - // |output_audio|, which can hold (at least) |max_length| elements. - // The number of channels that were written to the output is provided in - // the output variable |num_channels|, and each channel contains - // |samples_per_channel| elements. If more than one channel is written, - // the samples are interleaved. - // The speech type is written to |type|, if |type| is not NULL. - // Returns kOK on success, or kFail in case of an error. - virtual int GetAudio(size_t max_length, int16_t* output_audio, - int* samples_per_channel, int* num_channels, - NetEqOutputType* type); - - // Associates |rtp_payload_type| with |codec| and stores the information in - // the codec database. Returns kOK on success, kFail on failure. - virtual int RegisterPayloadType(enum NetEqDecoder codec, - uint8_t rtp_payload_type); - - // Provides an externally created decoder object |decoder| to insert in the - // decoder database. The decoder implements a decoder of type |codec| and - // associates it with |rtp_payload_type|. The decoder operates at the - // frequency |sample_rate_hz|. Returns kOK on success, kFail on failure. - virtual int RegisterExternalDecoder(AudioDecoder* decoder, - enum NetEqDecoder codec, - int sample_rate_hz, - uint8_t rtp_payload_type); - - // Removes |rtp_payload_type| from the codec database. Returns 0 on success, - // -1 on failure. - virtual int RemovePayloadType(uint8_t rtp_payload_type); - - virtual bool SetMinimumDelay(int delay_ms); - - virtual bool SetMaximumDelay(int delay_ms); - - virtual int LeastRequiredDelayMs() const; - - virtual int SetTargetDelay() { return kNotImplemented; } - - virtual int TargetDelay() { return kNotImplemented; } - - virtual int CurrentDelay() { return kNotImplemented; } - - // Sets the playout mode to |mode|. - virtual void SetPlayoutMode(NetEqPlayoutMode mode); - - // Returns the current playout mode. - virtual NetEqPlayoutMode PlayoutMode() const; - - // Writes the current network statistics to |stats|. The statistics are reset - // after the call. - virtual int NetworkStatistics(NetEqNetworkStatistics* stats); - - // Writes the last packet waiting times (in ms) to |waiting_times|. The number - // of values written is no more than 100, but may be smaller if the interface - // is polled again before 100 packets has arrived. - virtual void WaitingTimes(std::vector* waiting_times); - - // Writes the current RTCP statistics to |stats|. The statistics are reset - // and a new report period is started with the call. - virtual void GetRtcpStatistics(RtcpStatistics* stats); - - // Same as RtcpStatistics(), but does not reset anything. - virtual void GetRtcpStatisticsNoReset(RtcpStatistics* stats); - - // Enables post-decode VAD. When enabled, GetAudio() will return - // kOutputVADPassive when the signal contains no speech. - virtual void EnableVad(); - - // Disables post-decode VAD. - virtual void DisableVad(); - - // Returns the RTP timestamp for the last sample delivered by GetAudio(). - virtual uint32_t PlayoutTimestamp(); - - virtual int SetTargetNumberOfChannels() { return kNotImplemented; } - - virtual int SetTargetSampleRate() { return kNotImplemented; } - - // Returns the error code for the last occurred error. If no error has - // occurred, 0 is returned. - virtual int LastError(); - - // Returns the error code last returned by a decoder (audio or comfort noise). - // When LastError() returns kDecoderErrorCode or kComfortNoiseErrorCode, check - // this method to get the decoder's error code. - virtual int LastDecoderError(); - - // Flushes both the packet buffer and the sync buffer. - virtual void FlushBuffers(); - - virtual void PacketBufferStatistics(int* current_num_packets, - int* max_num_packets, - int* current_memory_size_bytes, - int* max_memory_size_bytes) const; - - // Get sequence number and timestamp of the latest RTP. - // This method is to facilitate NACK. - virtual int DecodedRtpInfo(int* sequence_number, uint32_t* timestamp) const; - - // Sets background noise mode. - virtual void SetBackgroundNoiseMode(NetEqBackgroundNoiseMode mode); - - // Gets background noise mode. - virtual NetEqBackgroundNoiseMode BackgroundNoiseMode() const; - - private: - static const int kOutputSizeMs = 10; - static const int kMaxFrameSize = 2880; // 60 ms @ 48 kHz. - // TODO(hlundin): Provide a better value for kSyncBufferSize. - static const int kSyncBufferSize = 2 * kMaxFrameSize; - - // Inserts a new packet into NetEq. This is used by the InsertPacket method - // above. Returns 0 on success, otherwise an error code. - // TODO(hlundin): Merge this with InsertPacket above? - int InsertPacketInternal(const WebRtcRTPHeader& rtp_header, - const uint8_t* payload, - int length_bytes, - uint32_t receive_timestamp, - bool is_sync_packet); - - - // Delivers 10 ms of audio data. The data is written to |output|, which can - // hold (at least) |max_length| elements. The number of channels that were - // written to the output is provided in the output variable |num_channels|, - // and each channel contains |samples_per_channel| elements. If more than one - // channel is written, the samples are interleaved. - // Returns 0 on success, otherwise an error code. - int GetAudioInternal(size_t max_length, int16_t* output, - int* samples_per_channel, int* num_channels); - - - // Provides a decision to the GetAudioInternal method. The decision what to - // do is written to |operation|. Packets to decode are written to - // |packet_list|, and a DTMF event to play is written to |dtmf_event|. When - // DTMF should be played, |play_dtmf| is set to true by the method. - // Returns 0 on success, otherwise an error code. - int GetDecision(Operations* operation, - PacketList* packet_list, - DtmfEvent* dtmf_event, - bool* play_dtmf); - - // Decodes the speech packets in |packet_list|, and writes the results to - // |decoded_buffer|, which is allocated to hold |decoded_buffer_length| - // elements. The length of the decoded data is written to |decoded_length|. - // The speech type -- speech or (codec-internal) comfort noise -- is written - // to |speech_type|. If |packet_list| contains any SID frames for RFC 3389 - // comfort noise, those are not decoded. - int Decode(PacketList* packet_list, Operations* operation, - int* decoded_length, AudioDecoder::SpeechType* speech_type); - - // Sub-method to Decode(). Performs the actual decoding. - int DecodeLoop(PacketList* packet_list, Operations* operation, - AudioDecoder* decoder, int* decoded_length, - AudioDecoder::SpeechType* speech_type); - - // Sub-method which calls the Normal class to perform the normal operation. - void DoNormal(const int16_t* decoded_buffer, size_t decoded_length, - AudioDecoder::SpeechType speech_type, bool play_dtmf); - - // Sub-method which calls the Merge class to perform the merge operation. - void DoMerge(int16_t* decoded_buffer, size_t decoded_length, - AudioDecoder::SpeechType speech_type, bool play_dtmf); - - // Sub-method which calls the Expand class to perform the expand operation. - int DoExpand(bool play_dtmf); - - // Sub-method which calls the Accelerate class to perform the accelerate - // operation. - int DoAccelerate(int16_t* decoded_buffer, size_t decoded_length, - AudioDecoder::SpeechType speech_type, bool play_dtmf); - - // Sub-method which calls the PreemptiveExpand class to perform the - // preemtive expand operation. - int DoPreemptiveExpand(int16_t* decoded_buffer, size_t decoded_length, - AudioDecoder::SpeechType speech_type, bool play_dtmf); - - // Sub-method which calls the ComfortNoise class to generate RFC 3389 comfort - // noise. |packet_list| can either contain one SID frame to update the - // noise parameters, or no payload at all, in which case the previously - // received parameters are used. - int DoRfc3389Cng(PacketList* packet_list, bool play_dtmf); - - // Calls the audio decoder to generate codec-internal comfort noise when - // no packet was received. - void DoCodecInternalCng(); - - // Calls the DtmfToneGenerator class to generate DTMF tones. - int DoDtmf(const DtmfEvent& dtmf_event, bool* play_dtmf); - - // Produces packet-loss concealment using alternative methods. If the codec - // has an internal PLC, it is called to generate samples. Otherwise, the - // method performs zero-stuffing. - void DoAlternativePlc(bool increase_timestamp); - - // Overdub DTMF on top of |output|. - int DtmfOverdub(const DtmfEvent& dtmf_event, size_t num_channels, - int16_t* output) const; - - // Extracts packets from |packet_buffer_| to produce at least - // |required_samples| samples. The packets are inserted into |packet_list|. - // Returns the number of samples that the packets in the list will produce, or - // -1 in case of an error. - int ExtractPackets(int required_samples, PacketList* packet_list); - - // Resets various variables and objects to new values based on the sample rate - // |fs_hz| and |channels| number audio channels. - void SetSampleRateAndChannels(int fs_hz, size_t channels); - - // Returns the output type for the audio produced by the latest call to - // GetAudio(). - NetEqOutputType LastOutputType(); - - scoped_ptr background_noise_; - scoped_ptr buffer_level_filter_; - scoped_ptr decoder_database_; - scoped_ptr delay_manager_; - scoped_ptr delay_peak_detector_; - scoped_ptr dtmf_buffer_; - scoped_ptr dtmf_tone_generator_; - scoped_ptr packet_buffer_; - scoped_ptr payload_splitter_; - scoped_ptr timestamp_scaler_; - scoped_ptr decision_logic_; - scoped_ptr vad_; - scoped_ptr algorithm_buffer_; - scoped_ptr sync_buffer_; - scoped_ptr expand_; - scoped_ptr expand_factory_; - scoped_ptr normal_; - scoped_ptr merge_; - scoped_ptr accelerate_; - scoped_ptr accelerate_factory_; - scoped_ptr preemptive_expand_; - scoped_ptr preemptive_expand_factory_; - RandomVector random_vector_; - scoped_ptr comfort_noise_; - Rtcp rtcp_; - StatisticsCalculator stats_; - int fs_hz_; - int fs_mult_; - int output_size_samples_; - int decoder_frame_length_; - Modes last_mode_; - scoped_array mute_factor_array_; - size_t decoded_buffer_length_; - scoped_array decoded_buffer_; - uint32_t playout_timestamp_; - bool new_codec_; - uint32_t timestamp_; - bool reset_decoder_; - uint8_t current_rtp_payload_type_; - uint8_t current_cng_rtp_payload_type_; - uint32_t ssrc_; - bool first_packet_; - int error_code_; // Store last error code. - int decoder_error_code_; - scoped_ptr crit_sect_; - - // These values are used by NACK module to estimate time-to-play of - // a missing packet. Occasionally, NetEq might decide to decode more - // than one packet. Therefore, these values store sequence number and - // timestamp of the first packet pulled from the packet buffer. In - // such cases, these values do not exactly represent the sequence number - // or timestamp associated with a 10ms audio pulled from NetEq. NACK - // module is designed to compensate for this. - int decoded_packet_sequence_number_; - uint32_t decoded_packet_timestamp_; - - DISALLOW_COPY_AND_ASSIGN(NetEqImpl); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_NETEQ_IMPL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_impl_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_impl_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_impl_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_impl_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,240 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" -#include "webrtc/modules/audio_coding/neteq4/neteq_impl.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq4/accelerate.h" -#include "webrtc/modules/audio_coding/neteq4/expand.h" -#include "webrtc/modules/audio_coding/neteq4/mock/mock_audio_decoder.h" -#include "webrtc/modules/audio_coding/neteq4/mock/mock_buffer_level_filter.h" -#include "webrtc/modules/audio_coding/neteq4/mock/mock_decoder_database.h" -#include "webrtc/modules/audio_coding/neteq4/mock/mock_delay_manager.h" -#include "webrtc/modules/audio_coding/neteq4/mock/mock_delay_peak_detector.h" -#include "webrtc/modules/audio_coding/neteq4/mock/mock_dtmf_buffer.h" -#include "webrtc/modules/audio_coding/neteq4/mock/mock_dtmf_tone_generator.h" -#include "webrtc/modules/audio_coding/neteq4/mock/mock_packet_buffer.h" -#include "webrtc/modules/audio_coding/neteq4/mock/mock_payload_splitter.h" -#include "webrtc/modules/audio_coding/neteq4/preemptive_expand.h" -#include "webrtc/modules/audio_coding/neteq4/timestamp_scaler.h" - -using ::testing::Return; -using ::testing::ReturnNull; -using ::testing::_; -using ::testing::SetArgPointee; -using ::testing::InSequence; -using ::testing::Invoke; -using ::testing::WithArg; - -namespace webrtc { - -// This function is called when inserting a packet list into the mock packet -// buffer. The purpose is to delete all inserted packets properly, to avoid -// memory leaks in the test. -int DeletePacketsAndReturnOk(PacketList* packet_list) { - PacketBuffer::DeleteAllPackets(packet_list); - return PacketBuffer::kOK; -} - -class NetEqImplTest : public ::testing::Test { - protected: - static const int kInitSampleRateHz = 8000; - NetEqImplTest() { - buffer_level_filter_ = new MockBufferLevelFilter; - decoder_database_ = new MockDecoderDatabase; - delay_peak_detector_ = new MockDelayPeakDetector; - EXPECT_CALL(*delay_peak_detector_, Reset()).Times(1); - delay_manager_ = new MockDelayManager(NetEq::kMaxNumPacketsInBuffer, - delay_peak_detector_); - dtmf_buffer_ = new MockDtmfBuffer(kInitSampleRateHz); - dtmf_tone_generator_ = new MockDtmfToneGenerator; - packet_buffer_ = new MockPacketBuffer(NetEq::kMaxNumPacketsInBuffer, - NetEq::kMaxBytesInBuffer); - payload_splitter_ = new MockPayloadSplitter; - timestamp_scaler_ = new TimestampScaler(*decoder_database_); - EXPECT_CALL(*decoder_database_, GetActiveCngDecoder()) - .WillOnce(ReturnNull()); - AccelerateFactory* accelerate_factory = new AccelerateFactory; - ExpandFactory* expand_factory = new ExpandFactory; - PreemptiveExpandFactory* preemptive_expand_factory = - new PreemptiveExpandFactory; - - neteq_ = new NetEqImpl(kInitSampleRateHz, - buffer_level_filter_, - decoder_database_, - delay_manager_, - delay_peak_detector_, - dtmf_buffer_, - dtmf_tone_generator_, - packet_buffer_, - payload_splitter_, - timestamp_scaler_, - accelerate_factory, - expand_factory, - preemptive_expand_factory); - } - - virtual ~NetEqImplTest() { - EXPECT_CALL(*buffer_level_filter_, Die()).Times(1); - EXPECT_CALL(*decoder_database_, Die()).Times(1); - EXPECT_CALL(*delay_manager_, Die()).Times(1); - EXPECT_CALL(*delay_peak_detector_, Die()).Times(1); - EXPECT_CALL(*dtmf_buffer_, Die()).Times(1); - EXPECT_CALL(*dtmf_tone_generator_, Die()).Times(1); - EXPECT_CALL(*packet_buffer_, Die()).Times(1); - delete neteq_; - } - - NetEqImpl* neteq_; - MockBufferLevelFilter* buffer_level_filter_; - MockDecoderDatabase* decoder_database_; - MockDelayPeakDetector* delay_peak_detector_; - MockDelayManager* delay_manager_; - MockDtmfBuffer* dtmf_buffer_; - MockDtmfToneGenerator* dtmf_tone_generator_; - MockPacketBuffer* packet_buffer_; - MockPayloadSplitter* payload_splitter_; - TimestampScaler* timestamp_scaler_; -}; - - -// This tests the interface class NetEq. -// TODO(hlundin): Move to separate file? -TEST(NetEq, CreateAndDestroy) { - NetEq* neteq = NetEq::Create(8000); - delete neteq; -} - -TEST_F(NetEqImplTest, RegisterPayloadType) { - uint8_t rtp_payload_type = 0; - NetEqDecoder codec_type = kDecoderPCMu; - EXPECT_CALL(*decoder_database_, - RegisterPayload(rtp_payload_type, codec_type)); - neteq_->RegisterPayloadType(codec_type, rtp_payload_type); -} - -TEST_F(NetEqImplTest, RemovePayloadType) { - uint8_t rtp_payload_type = 0; - EXPECT_CALL(*decoder_database_, - Remove(rtp_payload_type)) - .WillOnce(Return(DecoderDatabase::kDecoderNotFound)); - // Check that kFail is returned when database returns kDecoderNotFound. - EXPECT_EQ(NetEq::kFail, neteq_->RemovePayloadType(rtp_payload_type)); -} - -TEST_F(NetEqImplTest, InsertPacket) { - const int kPayloadLength = 100; - const uint8_t kPayloadType = 0; - const uint16_t kFirstSequenceNumber = 0x1234; - const uint32_t kFirstTimestamp = 0x12345678; - const uint32_t kSsrc = 0x87654321; - const uint32_t kFirstReceiveTime = 17; - uint8_t payload[kPayloadLength] = {0}; - WebRtcRTPHeader rtp_header; - rtp_header.header.payloadType = kPayloadType; - rtp_header.header.sequenceNumber = kFirstSequenceNumber; - rtp_header.header.timestamp = kFirstTimestamp; - rtp_header.header.ssrc = kSsrc; - - // Create a mock decoder object. - MockAudioDecoder mock_decoder; - // BWE update function called with first packet. - EXPECT_CALL(mock_decoder, IncomingPacket(_, - kPayloadLength, - kFirstSequenceNumber, - kFirstTimestamp, - kFirstReceiveTime)); - // BWE update function called with second packet. - EXPECT_CALL(mock_decoder, IncomingPacket(_, - kPayloadLength, - kFirstSequenceNumber + 1, - kFirstTimestamp + 160, - kFirstReceiveTime + 155)); - EXPECT_CALL(mock_decoder, Die()).Times(1); // Called when deleted. - - // Expectations for decoder database. - EXPECT_CALL(*decoder_database_, IsRed(kPayloadType)) - .WillRepeatedly(Return(false)); // This is not RED. - EXPECT_CALL(*decoder_database_, CheckPayloadTypes(_)) - .Times(2) - .WillRepeatedly(Return(DecoderDatabase::kOK)); // Payload type is valid. - EXPECT_CALL(*decoder_database_, IsDtmf(kPayloadType)) - .WillRepeatedly(Return(false)); // This is not DTMF. - EXPECT_CALL(*decoder_database_, GetDecoder(kPayloadType)) - .Times(3) - .WillRepeatedly(Return(&mock_decoder)); - EXPECT_CALL(*decoder_database_, IsComfortNoise(kPayloadType)) - .WillRepeatedly(Return(false)); // This is not CNG. - DecoderDatabase::DecoderInfo info; - info.codec_type = kDecoderPCMu; - EXPECT_CALL(*decoder_database_, GetDecoderInfo(kPayloadType)) - .WillRepeatedly(Return(&info)); - - // Expectations for packet buffer. - EXPECT_CALL(*packet_buffer_, NumPacketsInBuffer()) - .WillOnce(Return(0)) // First packet. - .WillOnce(Return(1)) // Second packet. - .WillOnce(Return(2)); // Second packet, checking after it was inserted. - EXPECT_CALL(*packet_buffer_, Flush()) - .Times(1); - EXPECT_CALL(*packet_buffer_, InsertPacketList(_, _, _, _)) - .Times(2) - .WillRepeatedly(DoAll(SetArgPointee<2>(kPayloadType), - WithArg<0>(Invoke(DeletePacketsAndReturnOk)))); - // SetArgPointee<2>(kPayloadType) means that the third argument (zero-based - // index) is a pointer, and the variable pointed to is set to kPayloadType. - // Also invoke the function DeletePacketsAndReturnOk to properly delete all - // packets in the list (to avoid memory leaks in the test). - EXPECT_CALL(*packet_buffer_, NextRtpHeader()) - .Times(1) - .WillOnce(Return(&rtp_header.header)); - - // Expectations for DTMF buffer. - EXPECT_CALL(*dtmf_buffer_, Flush()) - .Times(1); - - // Expectations for delay manager. - { - // All expectations within this block must be called in this specific order. - InSequence sequence; // Dummy variable. - // Expectations when the first packet is inserted. - EXPECT_CALL(*delay_manager_, LastDecoderType(kDecoderPCMu)) - .Times(1); - EXPECT_CALL(*delay_manager_, last_pack_cng_or_dtmf()) - .Times(2) - .WillRepeatedly(Return(-1)); - EXPECT_CALL(*delay_manager_, set_last_pack_cng_or_dtmf(0)) - .Times(1); - EXPECT_CALL(*delay_manager_, ResetPacketIatCount()).Times(1); - // Expectations when the second packet is inserted. Slightly different. - EXPECT_CALL(*delay_manager_, LastDecoderType(kDecoderPCMu)) - .Times(1); - EXPECT_CALL(*delay_manager_, last_pack_cng_or_dtmf()) - .WillOnce(Return(0)); - } - - // Expectations for payload splitter. - EXPECT_CALL(*payload_splitter_, SplitAudio(_, _)) - .Times(2) - .WillRepeatedly(Return(PayloadSplitter::kOK)); - - // Insert first packet. - neteq_->InsertPacket(rtp_header, payload, kPayloadLength, kFirstReceiveTime); - - // Insert second packet. - rtp_header.header.timestamp += 160; - rtp_header.header.sequenceNumber += 1; - neteq_->InsertPacket(rtp_header, payload, kPayloadLength, - kFirstReceiveTime + 155); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_stereo_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_stereo_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_stereo_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_stereo_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,418 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Test to verify correct stereo and multi-channel operation. - -#include -#include - -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" -#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" -#include "webrtc/modules/audio_coding/neteq4/tools/input_audio_file.h" -#include "webrtc/modules/audio_coding/neteq4/tools/rtp_generator.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/test/testsupport/fileutils.h" -#include "webrtc/test/testsupport/gtest_disable.h" - -namespace webrtc { - -struct TestParameters { - int frame_size; - int sample_rate; - int num_channels; -}; - -// This is a parameterized test. The test parameters are supplied through a -// TestParameters struct, which is obtained through the GetParam() method. -// -// The objective of the test is to create a mono input signal and a -// multi-channel input signal, where each channel is identical to the mono -// input channel. The two input signals are processed through their respective -// NetEq instances. After that, the output signals are compared. The expected -// result is that each channel in the multi-channel output is identical to the -// mono output. -class NetEqStereoTest : public ::testing::TestWithParam { - protected: - static const int kTimeStepMs = 10; - static const int kMaxBlockSize = 480; // 10 ms @ 48 kHz. - static const uint8_t kPayloadTypeMono = 95; - static const uint8_t kPayloadTypeMulti = 96; - - NetEqStereoTest() - : num_channels_(GetParam().num_channels), - sample_rate_hz_(GetParam().sample_rate), - samples_per_ms_(sample_rate_hz_ / 1000), - frame_size_ms_(GetParam().frame_size), - frame_size_samples_(frame_size_ms_ * samples_per_ms_), - output_size_samples_(10 * samples_per_ms_), - neteq_mono_(NetEq::Create(sample_rate_hz_)), - neteq_(NetEq::Create(sample_rate_hz_)), - rtp_generator_mono_(samples_per_ms_), - rtp_generator_(samples_per_ms_), - payload_size_bytes_(0), - multi_payload_size_bytes_(0), - last_send_time_(0), - last_arrival_time_(0) { - input_ = new int16_t[frame_size_samples_]; - encoded_ = new uint8_t[2 * frame_size_samples_]; - input_multi_channel_ = new int16_t[frame_size_samples_ * num_channels_]; - encoded_multi_channel_ = new uint8_t[frame_size_samples_ * 2 * - num_channels_]; - output_multi_channel_ = new int16_t[kMaxBlockSize * num_channels_]; - } - - ~NetEqStereoTest() { - delete neteq_mono_; - delete neteq_; - delete [] input_; - delete [] encoded_; - delete [] input_multi_channel_; - delete [] encoded_multi_channel_; - delete [] output_multi_channel_; - } - - virtual void SetUp() { - const std::string file_name = - webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); - input_file_.reset(new test::InputAudioFile(file_name)); - NetEqDecoder mono_decoder; - NetEqDecoder multi_decoder; - switch (sample_rate_hz_) { - case 8000: - mono_decoder = kDecoderPCM16B; - if (num_channels_ == 2) { - multi_decoder = kDecoderPCM16B_2ch; - } else if (num_channels_ == 5) { - multi_decoder = kDecoderPCM16B_5ch; - } else { - FAIL() << "Only 2 and 5 channels supported for 8000 Hz."; - } - break; - case 16000: - mono_decoder = kDecoderPCM16Bwb; - if (num_channels_ == 2) { - multi_decoder = kDecoderPCM16Bwb_2ch; - } else { - FAIL() << "More than 2 channels is not supported for 16000 Hz."; - } - break; - case 32000: - mono_decoder = kDecoderPCM16Bswb32kHz; - if (num_channels_ == 2) { - multi_decoder = kDecoderPCM16Bswb32kHz_2ch; - } else { - FAIL() << "More than 2 channels is not supported for 32000 Hz."; - } - break; - case 48000: - mono_decoder = kDecoderPCM16Bswb48kHz; - if (num_channels_ == 2) { - multi_decoder = kDecoderPCM16Bswb48kHz_2ch; - } else { - FAIL() << "More than 2 channels is not supported for 48000 Hz."; - } - break; - default: - FAIL() << "We shouldn't get here."; - } - ASSERT_EQ(NetEq::kOK, - neteq_mono_->RegisterPayloadType(mono_decoder, - kPayloadTypeMono)); - ASSERT_EQ(NetEq::kOK, - neteq_->RegisterPayloadType(multi_decoder, - kPayloadTypeMulti)); - } - - virtual void TearDown() {} - - int GetNewPackets() { - if (!input_file_->Read(frame_size_samples_, input_)) { - return -1; - } - payload_size_bytes_ = WebRtcPcm16b_Encode(input_, frame_size_samples_, - encoded_); - if (frame_size_samples_ * 2 != payload_size_bytes_) { - return -1; - } - int next_send_time = rtp_generator_mono_.GetRtpHeader(kPayloadTypeMono, - frame_size_samples_, - &rtp_header_mono_); - test::InputAudioFile::DuplicateInterleaved(input_, frame_size_samples_, - num_channels_, - input_multi_channel_); - multi_payload_size_bytes_ = WebRtcPcm16b_Encode( - input_multi_channel_, frame_size_samples_ * num_channels_, - encoded_multi_channel_); - if (frame_size_samples_ * 2 * num_channels_ != multi_payload_size_bytes_) { - return -1; - } - rtp_generator_.GetRtpHeader(kPayloadTypeMulti, frame_size_samples_, - &rtp_header_); - return next_send_time; - } - - void VerifyOutput(size_t num_samples) { - for (size_t i = 0; i < num_samples; ++i) { - for (int j = 0; j < num_channels_; ++j) { - ASSERT_EQ(output_[i], output_multi_channel_[i * num_channels_ + j]) << - "Diff in sample " << i << ", channel " << j << "."; - } - } - } - - virtual int GetArrivalTime(int send_time) { - int arrival_time = last_arrival_time_ + (send_time - last_send_time_); - last_send_time_ = send_time; - last_arrival_time_ = arrival_time; - return arrival_time; - } - - virtual bool Lost() { return false; } - - void RunTest(int num_loops) { - // Get next input packets (mono and multi-channel). - int next_send_time; - int next_arrival_time; - do { - next_send_time = GetNewPackets(); - ASSERT_NE(-1, next_send_time); - next_arrival_time = GetArrivalTime(next_send_time); - } while (Lost()); // If lost, immediately read the next packet. - - int time_now = 0; - for (int k = 0; k < num_loops; ++k) { - while (time_now >= next_arrival_time) { - // Insert packet in mono instance. - ASSERT_EQ(NetEq::kOK, - neteq_mono_->InsertPacket(rtp_header_mono_, encoded_, - payload_size_bytes_, - next_arrival_time)); - // Insert packet in multi-channel instance. - ASSERT_EQ(NetEq::kOK, - neteq_->InsertPacket(rtp_header_, encoded_multi_channel_, - multi_payload_size_bytes_, - next_arrival_time)); - // Get next input packets (mono and multi-channel). - do { - next_send_time = GetNewPackets(); - ASSERT_NE(-1, next_send_time); - next_arrival_time = GetArrivalTime(next_send_time); - } while (Lost()); // If lost, immediately read the next packet. - } - NetEqOutputType output_type; - // Get audio from mono instance. - int samples_per_channel; - int num_channels; - EXPECT_EQ(NetEq::kOK, - neteq_mono_->GetAudio(kMaxBlockSize, output_, - &samples_per_channel, &num_channels, - &output_type)); - EXPECT_EQ(1, num_channels); - EXPECT_EQ(output_size_samples_, samples_per_channel); - // Get audio from multi-channel instance. - ASSERT_EQ(NetEq::kOK, - neteq_->GetAudio(kMaxBlockSize * num_channels_, - output_multi_channel_, - &samples_per_channel, &num_channels, - &output_type)); - EXPECT_EQ(num_channels_, num_channels); - EXPECT_EQ(output_size_samples_, samples_per_channel); - std::ostringstream ss; - ss << "Lap number " << k << "."; - SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. - // Compare mono and multi-channel. - ASSERT_NO_FATAL_FAILURE(VerifyOutput(output_size_samples_)); - - time_now += kTimeStepMs; - } - } - - const int num_channels_; - const int sample_rate_hz_; - const int samples_per_ms_; - const int frame_size_ms_; - const int frame_size_samples_; - const int output_size_samples_; - NetEq* neteq_mono_; - NetEq* neteq_; - test::RtpGenerator rtp_generator_mono_; - test::RtpGenerator rtp_generator_; - int16_t* input_; - int16_t* input_multi_channel_; - uint8_t* encoded_; - uint8_t* encoded_multi_channel_; - int16_t output_[kMaxBlockSize]; - int16_t* output_multi_channel_; - WebRtcRTPHeader rtp_header_mono_; - WebRtcRTPHeader rtp_header_; - int payload_size_bytes_; - int multi_payload_size_bytes_; - int last_send_time_; - int last_arrival_time_; - scoped_ptr input_file_; -}; - -class NetEqStereoTestNoJitter : public NetEqStereoTest { - protected: - NetEqStereoTestNoJitter() - : NetEqStereoTest() { - // Start the sender 100 ms before the receiver to pre-fill the buffer. - // This is to avoid doing preemptive expand early in the test. - // TODO(hlundin): Mock the decision making instead to control the modes. - last_arrival_time_ = -100; - } -}; - -TEST_P(NetEqStereoTestNoJitter, DISABLED_ON_ANDROID(RunTest)) { - RunTest(8); -} - -class NetEqStereoTestPositiveDrift : public NetEqStereoTest { - protected: - NetEqStereoTestPositiveDrift() - : NetEqStereoTest(), - drift_factor(0.9) { - // Start the sender 100 ms before the receiver to pre-fill the buffer. - // This is to avoid doing preemptive expand early in the test. - // TODO(hlundin): Mock the decision making instead to control the modes. - last_arrival_time_ = -100; - } - virtual int GetArrivalTime(int send_time) { - int arrival_time = last_arrival_time_ + - drift_factor * (send_time - last_send_time_); - last_send_time_ = send_time; - last_arrival_time_ = arrival_time; - return arrival_time; - } - - double drift_factor; -}; - -TEST_P(NetEqStereoTestPositiveDrift, DISABLED_ON_ANDROID(RunTest)) { - RunTest(100); -} - -class NetEqStereoTestNegativeDrift : public NetEqStereoTestPositiveDrift { - protected: - NetEqStereoTestNegativeDrift() - : NetEqStereoTestPositiveDrift() { - drift_factor = 1.1; - last_arrival_time_ = 0; - } -}; - -TEST_P(NetEqStereoTestNegativeDrift, DISABLED_ON_ANDROID(RunTest)) { - RunTest(100); -} - -class NetEqStereoTestDelays : public NetEqStereoTest { - protected: - static const int kDelayInterval = 10; - static const int kDelay = 1000; - NetEqStereoTestDelays() - : NetEqStereoTest(), - frame_index_(0) { - } - - virtual int GetArrivalTime(int send_time) { - // Deliver immediately, unless we have a back-log. - int arrival_time = std::min(last_arrival_time_, send_time); - if (++frame_index_ % kDelayInterval == 0) { - // Delay this packet. - arrival_time += kDelay; - } - last_send_time_ = send_time; - last_arrival_time_ = arrival_time; - return arrival_time; - } - - int frame_index_; -}; - -TEST_P(NetEqStereoTestDelays, DISABLED_ON_ANDROID(RunTest)) { - RunTest(1000); -} - -class NetEqStereoTestLosses : public NetEqStereoTest { - protected: - static const int kLossInterval = 10; - NetEqStereoTestLosses() - : NetEqStereoTest(), - frame_index_(0) { - } - - virtual bool Lost() { - return (++frame_index_) % kLossInterval == 0; - } - - int frame_index_; -}; - -TEST_P(NetEqStereoTestLosses, DISABLED_ON_ANDROID(RunTest)) { - RunTest(100); -} - - -// Creates a list of parameter sets. -std::list GetTestParameters() { - std::list l; - const int sample_rates[] = {8000, 16000, 32000}; - const int num_rates = sizeof(sample_rates) / sizeof(sample_rates[0]); - // Loop through sample rates. - for (int rate_index = 0; rate_index < num_rates; ++rate_index) { - int sample_rate = sample_rates[rate_index]; - // Loop through all frame sizes between 10 and 60 ms. - for (int frame_size = 10; frame_size <= 60; frame_size += 10) { - TestParameters p; - p.frame_size = frame_size; - p.sample_rate = sample_rate; - p.num_channels = 2; - l.push_back(p); - if (sample_rate == 8000) { - // Add a five-channel test for 8000 Hz. - p.num_channels = 5; - l.push_back(p); - } - } - } - return l; -} - -// Pretty-printing the test parameters in case of an error. -void PrintTo(const TestParameters& p, ::std::ostream* os) { - *os << "{frame_size = " << p.frame_size << - ", num_channels = " << p.num_channels << - ", sample_rate = " << p.sample_rate << "}"; -} - -// Instantiate the tests. Each test is instantiated using the function above, -// so that all different parameter combinations are tested. -INSTANTIATE_TEST_CASE_P(MultiChannel, - NetEqStereoTestNoJitter, - ::testing::ValuesIn(GetTestParameters())); - -INSTANTIATE_TEST_CASE_P(MultiChannel, - NetEqStereoTestPositiveDrift, - ::testing::ValuesIn(GetTestParameters())); - -INSTANTIATE_TEST_CASE_P(MultiChannel, - NetEqStereoTestNegativeDrift, - ::testing::ValuesIn(GetTestParameters())); - -INSTANTIATE_TEST_CASE_P(MultiChannel, - NetEqStereoTestDelays, - ::testing::ValuesIn(GetTestParameters())); - -INSTANTIATE_TEST_CASE_P(MultiChannel, - NetEqStereoTestLosses, - ::testing::ValuesIn(GetTestParameters())); - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_tests.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_tests.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_tests.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_tests.gypi 1970-01-01 00:00:00.000000000 +0000 @@ -1,201 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'targets': [ - { - 'target_name': 'neteq_rtpplay', - 'type': 'executable', - 'dependencies': [ - 'NetEq4', - 'NetEq4TestTools', - 'neteq_unittest_tools', - 'PCM16B', - '<(webrtc_root)/test/test.gyp:test_support_main', - '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', - ], - 'sources': [ - 'tools/neteq_rtpplay.cc', - ], - 'defines': [ - ], - }, # neteq_rtpplay - - { - 'target_name': 'RTPencode', - 'type': 'executable', - 'dependencies': [ - # TODO(hlundin): Make RTPencode use ACM to encode files. - 'NetEq4TestTools',# Test helpers - 'G711', - 'G722', - 'PCM16B', - 'iLBC', - 'iSAC', - 'CNG', - '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', - ], - 'defines': [ - 'CODEC_ILBC', - 'CODEC_PCM16B', - 'CODEC_G711', - 'CODEC_G722', - 'CODEC_ISAC', - 'CODEC_PCM16B_WB', - 'CODEC_ISAC_SWB', - 'CODEC_PCM16B_32KHZ', - 'CODEC_CNGCODEC8', - 'CODEC_CNGCODEC16', - 'CODEC_CNGCODEC32', - 'CODEC_ATEVENT_DECODE', - 'CODEC_RED', - ], - 'include_dirs': [ - 'interface', - 'test', - '<(webrtc_root)', - ], - 'sources': [ - 'test/RTPencode.cc', - ], - # Disable warnings to enable Win64 build, issue 1323. - 'msvs_disabled_warnings': [ - 4267, # size_t to int truncation. - ], - }, - - { - 'target_name': 'RTPjitter', - 'type': 'executable', - 'dependencies': [ - '<(DEPTH)/testing/gtest.gyp:gtest', - ], - 'sources': [ - 'test/RTPjitter.cc', - ], - }, - - { - 'target_name': 'RTPanalyze', - 'type': 'executable', - 'dependencies': [ - 'NetEq4TestTools', - '<(DEPTH)/testing/gtest.gyp:gtest', - ], - 'sources': [ - 'test/RTPanalyze.cc', - ], - }, - - { - 'target_name': 'RTPchange', - 'type': 'executable', - 'dependencies': [ - 'NetEq4TestTools', - '<(DEPTH)/testing/gtest.gyp:gtest', - ], - 'sources': [ - 'test/RTPchange.cc', - ], - }, - - { - 'target_name': 'RTPtimeshift', - 'type': 'executable', - 'dependencies': [ - 'NetEq4TestTools', - '<(DEPTH)/testing/gtest.gyp:gtest', - ], - 'sources': [ - 'test/RTPtimeshift.cc', - ], - }, - - { - 'target_name': 'RTPcat', - 'type': 'executable', - 'dependencies': [ - 'NetEq4TestTools', - '<(DEPTH)/testing/gtest.gyp:gtest', - ], - 'sources': [ - 'test/RTPcat.cc', - ], - }, - - { - 'target_name': 'rtp_to_text', - 'type': 'executable', - 'dependencies': [ - 'NetEq4TestTools', - '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', - ], - 'sources': [ - 'test/rtp_to_text.cc', - ], - }, - - { - 'target_name': 'neteq4_speed_test', - 'type': 'executable', - 'dependencies': [ - 'NetEq4', - 'neteq_unittest_tools', - 'PCM16B', - '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', - '<(webrtc_root)/test/test.gyp:test_support_main', - ], - 'sources': [ - 'test/neteq_speed_test.cc', - ], - }, - - { - 'target_name': 'NetEq4TestTools', - # Collection of useful functions used in other tests. - 'type': 'static_library', - 'variables': { - # Expects RTP packets without payloads when enabled. - 'neteq_dummy_rtp%': 0, - }, - 'dependencies': [ - 'G711', - 'G722', - 'PCM16B', - 'iLBC', - 'iSAC', - 'CNG', - '<(DEPTH)/testing/gtest.gyp:gtest', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - 'interface', - 'test', - '<(webrtc_root)', - ], - }, - 'defines': [ - ], - 'include_dirs': [ - 'interface', - 'test', - '<(webrtc_root)', - ], - 'sources': [ - 'test/NETEQTEST_DummyRTPpacket.cc', - 'test/NETEQTEST_DummyRTPpacket.h', - 'test/NETEQTEST_RTPpacket.cc', - 'test/NETEQTEST_RTPpacket.h', - ], - # Disable warnings to enable Win64 build, issue 1323. - 'msvs_disabled_warnings': [ - 4267, # size_t to int truncation. - ], - }, - ], # targets -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/neteq_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,1233 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file includes unit tests for NetEQ. - */ - -#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" - -#include -#include // memset - -#include -#include -#include -#include - -#include "gflags/gflags.h" -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq4/test/NETEQTEST_RTPpacket.h" -#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" -#include "webrtc/test/testsupport/fileutils.h" -#include "webrtc/test/testsupport/gtest_disable.h" -#include "webrtc/typedefs.h" - -DEFINE_bool(gen_ref, false, "Generate reference files."); - -namespace webrtc { - -static bool IsAllZero(const int16_t* buf, int buf_length) { - bool all_zero = true; - for (int n = 0; n < buf_length && all_zero; ++n) - all_zero = buf[n] == 0; - return all_zero; -} - -static bool IsAllNonZero(const int16_t* buf, int buf_length) { - bool all_non_zero = true; - for (int n = 0; n < buf_length && all_non_zero; ++n) - all_non_zero = buf[n] != 0; - return all_non_zero; -} - -class RefFiles { - public: - RefFiles(const std::string& input_file, const std::string& output_file); - ~RefFiles(); - template void ProcessReference(const T& test_results); - template void ProcessReference( - const T (&test_results)[n], - size_t length); - template void WriteToFile( - const T (&test_results)[n], - size_t length); - template void ReadFromFileAndCompare( - const T (&test_results)[n], - size_t length); - void WriteToFile(const NetEqNetworkStatistics& stats); - void ReadFromFileAndCompare(const NetEqNetworkStatistics& stats); - void WriteToFile(const RtcpStatistics& stats); - void ReadFromFileAndCompare(const RtcpStatistics& stats); - - FILE* input_fp_; - FILE* output_fp_; -}; - -RefFiles::RefFiles(const std::string &input_file, - const std::string &output_file) - : input_fp_(NULL), - output_fp_(NULL) { - if (!input_file.empty()) { - input_fp_ = fopen(input_file.c_str(), "rb"); - EXPECT_TRUE(input_fp_ != NULL); - } - if (!output_file.empty()) { - output_fp_ = fopen(output_file.c_str(), "wb"); - EXPECT_TRUE(output_fp_ != NULL); - } -} - -RefFiles::~RefFiles() { - if (input_fp_) { - EXPECT_EQ(EOF, fgetc(input_fp_)); // Make sure that we reached the end. - fclose(input_fp_); - } - if (output_fp_) fclose(output_fp_); -} - -template -void RefFiles::ProcessReference(const T& test_results) { - WriteToFile(test_results); - ReadFromFileAndCompare(test_results); -} - -template -void RefFiles::ProcessReference(const T (&test_results)[n], size_t length) { - WriteToFile(test_results, length); - ReadFromFileAndCompare(test_results, length); -} - -template -void RefFiles::WriteToFile(const T (&test_results)[n], size_t length) { - if (output_fp_) { - ASSERT_EQ(length, fwrite(&test_results, sizeof(T), length, output_fp_)); - } -} - -template -void RefFiles::ReadFromFileAndCompare(const T (&test_results)[n], - size_t length) { - if (input_fp_) { - // Read from ref file. - T* ref = new T[length]; - ASSERT_EQ(length, fread(ref, sizeof(T), length, input_fp_)); - // Compare - ASSERT_EQ(0, memcmp(&test_results, ref, sizeof(T) * length)); - delete [] ref; - } -} - -void RefFiles::WriteToFile(const NetEqNetworkStatistics& stats) { - if (output_fp_) { - ASSERT_EQ(1u, fwrite(&stats, sizeof(NetEqNetworkStatistics), 1, - output_fp_)); - } -} - -void RefFiles::ReadFromFileAndCompare( - const NetEqNetworkStatistics& stats) { - if (input_fp_) { - // Read from ref file. - size_t stat_size = sizeof(NetEqNetworkStatistics); - NetEqNetworkStatistics ref_stats; - ASSERT_EQ(1u, fread(&ref_stats, stat_size, 1, input_fp_)); - // Compare - EXPECT_EQ(0, memcmp(&stats, &ref_stats, stat_size)); - } -} - -void RefFiles::WriteToFile(const RtcpStatistics& stats) { - if (output_fp_) { - ASSERT_EQ(1u, fwrite(&(stats.fraction_lost), sizeof(stats.fraction_lost), 1, - output_fp_)); - ASSERT_EQ(1u, fwrite(&(stats.cumulative_lost), - sizeof(stats.cumulative_lost), 1, output_fp_)); - ASSERT_EQ(1u, fwrite(&(stats.extended_max_sequence_number), - sizeof(stats.extended_max_sequence_number), 1, - output_fp_)); - ASSERT_EQ(1u, fwrite(&(stats.jitter), sizeof(stats.jitter), 1, - output_fp_)); - } -} - -void RefFiles::ReadFromFileAndCompare( - const RtcpStatistics& stats) { - if (input_fp_) { - // Read from ref file. - RtcpStatistics ref_stats; - ASSERT_EQ(1u, fread(&(ref_stats.fraction_lost), - sizeof(ref_stats.fraction_lost), 1, input_fp_)); - ASSERT_EQ(1u, fread(&(ref_stats.cumulative_lost), - sizeof(ref_stats.cumulative_lost), 1, input_fp_)); - ASSERT_EQ(1u, fread(&(ref_stats.extended_max_sequence_number), - sizeof(ref_stats.extended_max_sequence_number), 1, - input_fp_)); - ASSERT_EQ(1u, fread(&(ref_stats.jitter), sizeof(ref_stats.jitter), 1, - input_fp_)); - // Compare - EXPECT_EQ(ref_stats.fraction_lost, stats.fraction_lost); - EXPECT_EQ(ref_stats.cumulative_lost, stats.cumulative_lost); - EXPECT_EQ(ref_stats.extended_max_sequence_number, - stats.extended_max_sequence_number); - EXPECT_EQ(ref_stats.jitter, stats.jitter); - } -} - -class NetEqDecodingTest : public ::testing::Test { - protected: - // NetEQ must be polled for data once every 10 ms. Thus, neither of the - // constants below can be changed. - static const int kTimeStepMs = 10; - static const int kBlockSize8kHz = kTimeStepMs * 8; - static const int kBlockSize16kHz = kTimeStepMs * 16; - static const int kBlockSize32kHz = kTimeStepMs * 32; - static const int kMaxBlockSize = kBlockSize32kHz; - static const int kInitSampleRateHz = 8000; - - NetEqDecodingTest(); - virtual void SetUp(); - virtual void TearDown(); - void SelectDecoders(NetEqDecoder* used_codec); - void LoadDecoders(); - void OpenInputFile(const std::string &rtp_file); - void Process(NETEQTEST_RTPpacket* rtp_ptr, int* out_len); - void DecodeAndCompare(const std::string &rtp_file, - const std::string &ref_file); - void DecodeAndCheckStats(const std::string &rtp_file, - const std::string &stat_ref_file, - const std::string &rtcp_ref_file); - static void PopulateRtpInfo(int frame_index, - int timestamp, - WebRtcRTPHeader* rtp_info); - static void PopulateCng(int frame_index, - int timestamp, - WebRtcRTPHeader* rtp_info, - uint8_t* payload, - int* payload_len); - - void CheckBgnOff(int sampling_rate, NetEqBackgroundNoiseMode bgn_mode); - - void WrapTest(uint16_t start_seq_no, uint32_t start_timestamp, - const std::set& drop_seq_numbers, - bool expect_seq_no_wrap, bool expect_timestamp_wrap); - - void LongCngWithClockDrift(double drift_factor); - - NetEq* neteq_; - FILE* rtp_fp_; - unsigned int sim_clock_; - int16_t out_data_[kMaxBlockSize]; - int output_sample_rate_; -}; - -// Allocating the static const so that it can be passed by reference. -const int NetEqDecodingTest::kTimeStepMs; -const int NetEqDecodingTest::kBlockSize8kHz; -const int NetEqDecodingTest::kBlockSize16kHz; -const int NetEqDecodingTest::kBlockSize32kHz; -const int NetEqDecodingTest::kMaxBlockSize; -const int NetEqDecodingTest::kInitSampleRateHz; - -NetEqDecodingTest::NetEqDecodingTest() - : neteq_(NULL), - rtp_fp_(NULL), - sim_clock_(0), - output_sample_rate_(kInitSampleRateHz) { - memset(out_data_, 0, sizeof(out_data_)); -} - -void NetEqDecodingTest::SetUp() { - neteq_ = NetEq::Create(kInitSampleRateHz); - ASSERT_TRUE(neteq_); - LoadDecoders(); -} - -void NetEqDecodingTest::TearDown() { - delete neteq_; - if (rtp_fp_) - fclose(rtp_fp_); -} - -void NetEqDecodingTest::LoadDecoders() { - // Load PCMu. - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderPCMu, 0)); - // Load PCMa. - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderPCMa, 8)); -#ifndef WEBRTC_ANDROID - // Load iLBC. - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderILBC, 102)); -#endif // WEBRTC_ANDROID - // Load iSAC. - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderISAC, 103)); -#ifndef WEBRTC_ANDROID - // Load iSAC SWB. - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderISACswb, 104)); - // Load iSAC FB. - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderISACfb, 105)); -#endif // WEBRTC_ANDROID - // Load PCM16B nb. - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderPCM16B, 93)); - // Load PCM16B wb. - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderPCM16Bwb, 94)); - // Load PCM16B swb32. - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderPCM16Bswb32kHz, 95)); - // Load CNG 8 kHz. - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderCNGnb, 13)); - // Load CNG 16 kHz. - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderCNGwb, 98)); -} - -void NetEqDecodingTest::OpenInputFile(const std::string &rtp_file) { - rtp_fp_ = fopen(rtp_file.c_str(), "rb"); - ASSERT_TRUE(rtp_fp_ != NULL); - ASSERT_EQ(0, NETEQTEST_RTPpacket::skipFileHeader(rtp_fp_)); -} - -void NetEqDecodingTest::Process(NETEQTEST_RTPpacket* rtp, int* out_len) { - // Check if time to receive. - while ((sim_clock_ >= rtp->time()) && - (rtp->dataLen() >= 0)) { - if (rtp->dataLen() > 0) { - WebRtcRTPHeader rtpInfo; - rtp->parseHeader(&rtpInfo); - ASSERT_EQ(0, neteq_->InsertPacket( - rtpInfo, - rtp->payload(), - rtp->payloadLen(), - rtp->time() * (output_sample_rate_ / 1000))); - } - // Get next packet. - ASSERT_NE(-1, rtp->readFromFile(rtp_fp_)); - } - - // Get audio from NetEq. - NetEqOutputType type; - int num_channels; - ASSERT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, out_len, - &num_channels, &type)); - ASSERT_TRUE((*out_len == kBlockSize8kHz) || - (*out_len == kBlockSize16kHz) || - (*out_len == kBlockSize32kHz)); - output_sample_rate_ = *out_len / 10 * 1000; - - // Increase time. - sim_clock_ += kTimeStepMs; -} - -void NetEqDecodingTest::DecodeAndCompare(const std::string &rtp_file, - const std::string &ref_file) { - OpenInputFile(rtp_file); - - std::string ref_out_file = ""; - if (ref_file.empty()) { - ref_out_file = webrtc::test::OutputPath() + "neteq_universal_ref.pcm"; - } - RefFiles ref_files(ref_file, ref_out_file); - - NETEQTEST_RTPpacket rtp; - ASSERT_GT(rtp.readFromFile(rtp_fp_), 0); - int i = 0; - while (rtp.dataLen() >= 0) { - std::ostringstream ss; - ss << "Lap number " << i++ << " in DecodeAndCompare while loop"; - SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. - int out_len = 0; - ASSERT_NO_FATAL_FAILURE(Process(&rtp, &out_len)); - ASSERT_NO_FATAL_FAILURE(ref_files.ProcessReference(out_data_, out_len)); - } -} - -void NetEqDecodingTest::DecodeAndCheckStats(const std::string &rtp_file, - const std::string &stat_ref_file, - const std::string &rtcp_ref_file) { - OpenInputFile(rtp_file); - std::string stat_out_file = ""; - if (stat_ref_file.empty()) { - stat_out_file = webrtc::test::OutputPath() + - "neteq_network_stats.dat"; - } - RefFiles network_stat_files(stat_ref_file, stat_out_file); - - std::string rtcp_out_file = ""; - if (rtcp_ref_file.empty()) { - rtcp_out_file = webrtc::test::OutputPath() + - "neteq_rtcp_stats.dat"; - } - RefFiles rtcp_stat_files(rtcp_ref_file, rtcp_out_file); - - NETEQTEST_RTPpacket rtp; - ASSERT_GT(rtp.readFromFile(rtp_fp_), 0); - while (rtp.dataLen() >= 0) { - int out_len; - Process(&rtp, &out_len); - - // Query the network statistics API once per second - if (sim_clock_ % 1000 == 0) { - // Process NetworkStatistics. - NetEqNetworkStatistics network_stats; - ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats)); - network_stat_files.ProcessReference(network_stats); - - // Process RTCPstat. - RtcpStatistics rtcp_stats; - neteq_->GetRtcpStatistics(&rtcp_stats); - rtcp_stat_files.ProcessReference(rtcp_stats); - } - } -} - -void NetEqDecodingTest::PopulateRtpInfo(int frame_index, - int timestamp, - WebRtcRTPHeader* rtp_info) { - rtp_info->header.sequenceNumber = frame_index; - rtp_info->header.timestamp = timestamp; - rtp_info->header.ssrc = 0x1234; // Just an arbitrary SSRC. - rtp_info->header.payloadType = 94; // PCM16b WB codec. - rtp_info->header.markerBit = 0; -} - -void NetEqDecodingTest::PopulateCng(int frame_index, - int timestamp, - WebRtcRTPHeader* rtp_info, - uint8_t* payload, - int* payload_len) { - rtp_info->header.sequenceNumber = frame_index; - rtp_info->header.timestamp = timestamp; - rtp_info->header.ssrc = 0x1234; // Just an arbitrary SSRC. - rtp_info->header.payloadType = 98; // WB CNG. - rtp_info->header.markerBit = 0; - payload[0] = 64; // Noise level -64 dBov, quite arbitrarily chosen. - *payload_len = 1; // Only noise level, no spectral parameters. -} - -void NetEqDecodingTest::CheckBgnOff(int sampling_rate_hz, - NetEqBackgroundNoiseMode bgn_mode) { - int expected_samples_per_channel = 0; - uint8_t payload_type = 0xFF; // Invalid. - if (sampling_rate_hz == 8000) { - expected_samples_per_channel = kBlockSize8kHz; - payload_type = 93; // PCM 16, 8 kHz. - } else if (sampling_rate_hz == 16000) { - expected_samples_per_channel = kBlockSize16kHz; - payload_type = 94; // PCM 16, 16 kHZ. - } else if (sampling_rate_hz == 32000) { - expected_samples_per_channel = kBlockSize32kHz; - payload_type = 95; // PCM 16, 32 kHz. - } else { - ASSERT_TRUE(false); // Unsupported test case. - } - - NetEqOutputType type; - int16_t output[kBlockSize32kHz]; // Maximum size is chosen. - int16_t input[kBlockSize32kHz]; // Maximum size is chosen. - - // Payload of 10 ms of PCM16 32 kHz. - uint8_t payload[kBlockSize32kHz * sizeof(int16_t)]; - - // Random payload. - for (int n = 0; n < expected_samples_per_channel; ++n) { - input[n] = (rand() & ((1 << 10) - 1)) - ((1 << 5) - 1); - } - int enc_len_bytes = WebRtcPcm16b_EncodeW16( - input, expected_samples_per_channel, reinterpret_cast(payload)); - ASSERT_EQ(enc_len_bytes, expected_samples_per_channel * 2); - - WebRtcRTPHeader rtp_info; - PopulateRtpInfo(0, 0, &rtp_info); - rtp_info.header.payloadType = payload_type; - - int number_channels = 0; - int samples_per_channel = 0; - - uint32_t receive_timestamp = 0; - for (int n = 0; n < 10; ++n) { // Insert few packets and get audio. - number_channels = 0; - samples_per_channel = 0; - ASSERT_EQ(0, neteq_->InsertPacket( - rtp_info, payload, enc_len_bytes, receive_timestamp)); - ASSERT_EQ(0, neteq_->GetAudio(kBlockSize32kHz, output, &samples_per_channel, - &number_channels, &type)); - ASSERT_EQ(1, number_channels); - ASSERT_EQ(expected_samples_per_channel, samples_per_channel); - ASSERT_EQ(kOutputNormal, type); - - // Next packet. - rtp_info.header.timestamp += expected_samples_per_channel; - rtp_info.header.sequenceNumber++; - receive_timestamp += expected_samples_per_channel; - } - - number_channels = 0; - samples_per_channel = 0; - - // Get audio without inserting packets, expecting PLC and PLC-to-CNG. Pull one - // frame without checking speech-type. This is the first frame pulled without - // inserting any packet, and might not be labeled as PCL. - ASSERT_EQ(0, neteq_->GetAudio(kBlockSize32kHz, output, &samples_per_channel, - &number_channels, &type)); - ASSERT_EQ(1, number_channels); - ASSERT_EQ(expected_samples_per_channel, samples_per_channel); - - // To be able to test the fading of background noise we need at lease to pull - // 610 frames. - const int kFadingThreshold = 610; - - // Test several CNG-to-PLC packet for the expected behavior. The number 20 is - // arbitrary, but sufficiently large to test enough number of frames. - const int kNumPlcToCngTestFrames = 20; - bool plc_to_cng = false; - for (int n = 0; n < kFadingThreshold + kNumPlcToCngTestFrames; ++n) { - number_channels = 0; - samples_per_channel = 0; - memset(output, 1, sizeof(output)); // Set to non-zero. - ASSERT_EQ(0, neteq_->GetAudio(kBlockSize32kHz, output, &samples_per_channel, - &number_channels, &type)); - ASSERT_EQ(1, number_channels); - ASSERT_EQ(expected_samples_per_channel, samples_per_channel); - if (type == kOutputPLCtoCNG) { - plc_to_cng = true; - double sum_squared = 0; - for (int k = 0; k < number_channels * samples_per_channel; ++k) - sum_squared += output[k] * output[k]; - if (bgn_mode == kBgnOn) { - EXPECT_NE(0, sum_squared); - } else if (bgn_mode == kBgnOff || n > kFadingThreshold) { - EXPECT_EQ(0, sum_squared); - } - } else { - EXPECT_EQ(kOutputPLC, type); - } - } - EXPECT_TRUE(plc_to_cng); // Just to be sure that PLC-to-CNG has occurred. -} - -#if defined(_WIN32) && defined(WEBRTC_ARCH_64_BITS) -// Disabled for Windows 64-bit until webrtc:1458 is fixed. -#define MAYBE_TestBitExactness DISABLED_TestBitExactness -#else -#define MAYBE_TestBitExactness TestBitExactness -#endif - -TEST_F(NetEqDecodingTest, DISABLED_ON_ANDROID(MAYBE_TestBitExactness)) { - const std::string input_rtp_file = webrtc::test::ProjectRootPath() + - "resources/audio_coding/neteq_universal_new.rtp"; -#if defined(_MSC_VER) && (_MSC_VER >= 1700) - // For Visual Studio 2012 and later, we will have to use the generic reference - // file, rather than the windows-specific one. - const std::string input_ref_file = webrtc::test::ProjectRootPath() + - "resources/audio_coding/neteq4_universal_ref.pcm"; -#else - const std::string input_ref_file = - webrtc::test::ResourcePath("audio_coding/neteq4_universal_ref", "pcm"); -#endif - - if (FLAGS_gen_ref) { - DecodeAndCompare(input_rtp_file, ""); - } else { - DecodeAndCompare(input_rtp_file, input_ref_file); - } -} - -TEST_F(NetEqDecodingTest, DISABLED_ON_ANDROID(TestNetworkStatistics)) { - const std::string input_rtp_file = webrtc::test::ProjectRootPath() + - "resources/audio_coding/neteq_universal_new.rtp"; -#if defined(_MSC_VER) && (_MSC_VER >= 1700) - // For Visual Studio 2012 and later, we will have to use the generic reference - // file, rather than the windows-specific one. - const std::string network_stat_ref_file = webrtc::test::ProjectRootPath() + - "resources/audio_coding/neteq4_network_stats.dat"; -#else - const std::string network_stat_ref_file = - webrtc::test::ResourcePath("audio_coding/neteq4_network_stats", "dat"); -#endif - const std::string rtcp_stat_ref_file = - webrtc::test::ResourcePath("audio_coding/neteq4_rtcp_stats", "dat"); - if (FLAGS_gen_ref) { - DecodeAndCheckStats(input_rtp_file, "", ""); - } else { - DecodeAndCheckStats(input_rtp_file, network_stat_ref_file, - rtcp_stat_ref_file); - } -} - -// TODO(hlundin): Re-enable test once the statistics interface is up and again. -TEST_F(NetEqDecodingTest, DISABLED_ON_ANDROID(TestFrameWaitingTimeStatistics)) { - // Use fax mode to avoid time-scaling. This is to simplify the testing of - // packet waiting times in the packet buffer. - neteq_->SetPlayoutMode(kPlayoutFax); - ASSERT_EQ(kPlayoutFax, neteq_->PlayoutMode()); - // Insert 30 dummy packets at once. Each packet contains 10 ms 16 kHz audio. - size_t num_frames = 30; - const int kSamples = 10 * 16; - const int kPayloadBytes = kSamples * 2; - for (size_t i = 0; i < num_frames; ++i) { - uint16_t payload[kSamples] = {0}; - WebRtcRTPHeader rtp_info; - rtp_info.header.sequenceNumber = i; - rtp_info.header.timestamp = i * kSamples; - rtp_info.header.ssrc = 0x1234; // Just an arbitrary SSRC. - rtp_info.header.payloadType = 94; // PCM16b WB codec. - rtp_info.header.markerBit = 0; - ASSERT_EQ(0, neteq_->InsertPacket( - rtp_info, - reinterpret_cast(payload), - kPayloadBytes, 0)); - } - // Pull out all data. - for (size_t i = 0; i < num_frames; ++i) { - int out_len; - int num_channels; - NetEqOutputType type; - ASSERT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, &out_len, - &num_channels, &type)); - ASSERT_EQ(kBlockSize16kHz, out_len); - } - - std::vector waiting_times; - neteq_->WaitingTimes(&waiting_times); - EXPECT_EQ(num_frames, waiting_times.size()); - // Since all frames are dumped into NetEQ at once, but pulled out with 10 ms - // spacing (per definition), we expect the delay to increase with 10 ms for - // each packet. - for (size_t i = 0; i < waiting_times.size(); ++i) { - EXPECT_EQ(static_cast(i + 1) * 10, waiting_times[i]); - } - - // Check statistics again and make sure it's been reset. - neteq_->WaitingTimes(&waiting_times); - int len = waiting_times.size(); - EXPECT_EQ(0, len); - - // Process > 100 frames, and make sure that that we get statistics - // only for 100 frames. Note the new SSRC, causing NetEQ to reset. - num_frames = 110; - for (size_t i = 0; i < num_frames; ++i) { - uint16_t payload[kSamples] = {0}; - WebRtcRTPHeader rtp_info; - rtp_info.header.sequenceNumber = i; - rtp_info.header.timestamp = i * kSamples; - rtp_info.header.ssrc = 0x1235; // Just an arbitrary SSRC. - rtp_info.header.payloadType = 94; // PCM16b WB codec. - rtp_info.header.markerBit = 0; - ASSERT_EQ(0, neteq_->InsertPacket( - rtp_info, - reinterpret_cast(payload), - kPayloadBytes, 0)); - int out_len; - int num_channels; - NetEqOutputType type; - ASSERT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, &out_len, - &num_channels, &type)); - ASSERT_EQ(kBlockSize16kHz, out_len); - } - - neteq_->WaitingTimes(&waiting_times); - EXPECT_EQ(100u, waiting_times.size()); -} - -TEST_F(NetEqDecodingTest, - DISABLED_ON_ANDROID(TestAverageInterArrivalTimeNegative)) { - const int kNumFrames = 3000; // Needed for convergence. - int frame_index = 0; - const int kSamples = 10 * 16; - const int kPayloadBytes = kSamples * 2; - while (frame_index < kNumFrames) { - // Insert one packet each time, except every 10th time where we insert two - // packets at once. This will create a negative clock-drift of approx. 10%. - int num_packets = (frame_index % 10 == 0 ? 2 : 1); - for (int n = 0; n < num_packets; ++n) { - uint8_t payload[kPayloadBytes] = {0}; - WebRtcRTPHeader rtp_info; - PopulateRtpInfo(frame_index, frame_index * kSamples, &rtp_info); - ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, 0)); - ++frame_index; - } - - // Pull out data once. - int out_len; - int num_channels; - NetEqOutputType type; - ASSERT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, &out_len, - &num_channels, &type)); - ASSERT_EQ(kBlockSize16kHz, out_len); - } - - NetEqNetworkStatistics network_stats; - ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats)); - EXPECT_EQ(-103196, network_stats.clockdrift_ppm); -} - -TEST_F(NetEqDecodingTest, - DISABLED_ON_ANDROID(TestAverageInterArrivalTimePositive)) { - const int kNumFrames = 5000; // Needed for convergence. - int frame_index = 0; - const int kSamples = 10 * 16; - const int kPayloadBytes = kSamples * 2; - for (int i = 0; i < kNumFrames; ++i) { - // Insert one packet each time, except every 10th time where we don't insert - // any packet. This will create a positive clock-drift of approx. 11%. - int num_packets = (i % 10 == 9 ? 0 : 1); - for (int n = 0; n < num_packets; ++n) { - uint8_t payload[kPayloadBytes] = {0}; - WebRtcRTPHeader rtp_info; - PopulateRtpInfo(frame_index, frame_index * kSamples, &rtp_info); - ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, 0)); - ++frame_index; - } - - // Pull out data once. - int out_len; - int num_channels; - NetEqOutputType type; - ASSERT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, &out_len, - &num_channels, &type)); - ASSERT_EQ(kBlockSize16kHz, out_len); - } - - NetEqNetworkStatistics network_stats; - ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats)); - EXPECT_EQ(110946, network_stats.clockdrift_ppm); -} - -void NetEqDecodingTest::LongCngWithClockDrift(double drift_factor) { - uint16_t seq_no = 0; - uint32_t timestamp = 0; - const int kFrameSizeMs = 30; - const int kSamples = kFrameSizeMs * 16; - const int kPayloadBytes = kSamples * 2; - double next_input_time_ms = 0.0; - double t_ms; - NetEqOutputType type; - - // Insert speech for 5 seconds. - const int kSpeechDurationMs = 5000; - for (t_ms = 0; t_ms < kSpeechDurationMs; t_ms += 10) { - // Each turn in this for loop is 10 ms. - while (next_input_time_ms <= t_ms) { - // Insert one 30 ms speech frame. - uint8_t payload[kPayloadBytes] = {0}; - WebRtcRTPHeader rtp_info; - PopulateRtpInfo(seq_no, timestamp, &rtp_info); - ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, 0)); - ++seq_no; - timestamp += kSamples; - next_input_time_ms += static_cast(kFrameSizeMs) * drift_factor; - } - // Pull out data once. - int out_len; - int num_channels; - ASSERT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, &out_len, - &num_channels, &type)); - ASSERT_EQ(kBlockSize16kHz, out_len); - } - - EXPECT_EQ(kOutputNormal, type); - int32_t delay_before = timestamp - neteq_->PlayoutTimestamp(); - - // Insert CNG for 1 minute (= 60000 ms). - const int kCngPeriodMs = 100; - const int kCngPeriodSamples = kCngPeriodMs * 16; // Period in 16 kHz samples. - const int kCngDurationMs = 60000; - for (; t_ms < kSpeechDurationMs + kCngDurationMs; t_ms += 10) { - // Each turn in this for loop is 10 ms. - while (next_input_time_ms <= t_ms) { - // Insert one CNG frame each 100 ms. - uint8_t payload[kPayloadBytes]; - int payload_len; - WebRtcRTPHeader rtp_info; - PopulateCng(seq_no, timestamp, &rtp_info, payload, &payload_len); - ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, payload_len, 0)); - ++seq_no; - timestamp += kCngPeriodSamples; - next_input_time_ms += static_cast(kCngPeriodMs) * drift_factor; - } - // Pull out data once. - int out_len; - int num_channels; - ASSERT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, &out_len, - &num_channels, &type)); - ASSERT_EQ(kBlockSize16kHz, out_len); - } - - EXPECT_EQ(kOutputCNG, type); - - // Insert speech again until output type is speech. - while (type != kOutputNormal) { - // Each turn in this for loop is 10 ms. - while (next_input_time_ms <= t_ms) { - // Insert one 30 ms speech frame. - uint8_t payload[kPayloadBytes] = {0}; - WebRtcRTPHeader rtp_info; - PopulateRtpInfo(seq_no, timestamp, &rtp_info); - ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, 0)); - ++seq_no; - timestamp += kSamples; - next_input_time_ms += static_cast(kFrameSizeMs) * drift_factor; - } - // Pull out data once. - int out_len; - int num_channels; - ASSERT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, &out_len, - &num_channels, &type)); - ASSERT_EQ(kBlockSize16kHz, out_len); - // Increase clock. - t_ms += 10; - } - - int32_t delay_after = timestamp - neteq_->PlayoutTimestamp(); - // Compare delay before and after, and make sure it differs less than 20 ms. - EXPECT_LE(delay_after, delay_before + 20 * 16); - EXPECT_GE(delay_after, delay_before - 20 * 16); -} - -TEST_F(NetEqDecodingTest, DISABLED_ON_ANDROID(LongCngWithClockNegativeDrift)) { - // Apply a clock drift of -25 ms / s (sender faster than receiver). - const double kDriftFactor = 1000.0 / (1000.0 + 25.0); - LongCngWithClockDrift(kDriftFactor); -} - -// TODO(hlundin): Re-enable this test and fix the issues to make it pass. -TEST_F(NetEqDecodingTest, - DISABLED_ON_ANDROID(DISABLED_LongCngWithClockPositiveDrift)) { - // Apply a clock drift of +25 ms / s (sender slower than receiver). - const double kDriftFactor = 1000.0 / (1000.0 - 25.0); - LongCngWithClockDrift(kDriftFactor); -} - -TEST_F(NetEqDecodingTest, DISABLED_ON_ANDROID(UnknownPayloadType)) { - const int kPayloadBytes = 100; - uint8_t payload[kPayloadBytes] = {0}; - WebRtcRTPHeader rtp_info; - PopulateRtpInfo(0, 0, &rtp_info); - rtp_info.header.payloadType = 1; // Not registered as a decoder. - EXPECT_EQ(NetEq::kFail, - neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, 0)); - EXPECT_EQ(NetEq::kUnknownRtpPayloadType, neteq_->LastError()); -} - -TEST_F(NetEqDecodingTest, DISABLED_ON_ANDROID(OversizePacket)) { - // Payload size is greater than packet buffer size - const int kPayloadBytes = NetEq::kMaxBytesInBuffer + 1; - uint8_t payload[kPayloadBytes] = {0}; - WebRtcRTPHeader rtp_info; - PopulateRtpInfo(0, 0, &rtp_info); - rtp_info.header.payloadType = 103; // iSAC, no packet splitting. - EXPECT_EQ(NetEq::kFail, - neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, 0)); - EXPECT_EQ(NetEq::kOversizePacket, neteq_->LastError()); -} - -TEST_F(NetEqDecodingTest, DISABLED_ON_ANDROID(DecoderError)) { - const int kPayloadBytes = 100; - uint8_t payload[kPayloadBytes] = {0}; - WebRtcRTPHeader rtp_info; - PopulateRtpInfo(0, 0, &rtp_info); - rtp_info.header.payloadType = 103; // iSAC, but the payload is invalid. - EXPECT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, 0)); - NetEqOutputType type; - // Set all of |out_data_| to 1, and verify that it was set to 0 by the call - // to GetAudio. - for (int i = 0; i < kMaxBlockSize; ++i) { - out_data_[i] = 1; - } - int num_channels; - int samples_per_channel; - EXPECT_EQ(NetEq::kFail, - neteq_->GetAudio(kMaxBlockSize, out_data_, - &samples_per_channel, &num_channels, &type)); - // Verify that there is a decoder error to check. - EXPECT_EQ(NetEq::kDecoderErrorCode, neteq_->LastError()); - // Code 6730 is an iSAC error code. - EXPECT_EQ(6730, neteq_->LastDecoderError()); - // Verify that the first 160 samples are set to 0, and that the remaining - // samples are left unmodified. - static const int kExpectedOutputLength = 160; // 10 ms at 16 kHz sample rate. - for (int i = 0; i < kExpectedOutputLength; ++i) { - std::ostringstream ss; - ss << "i = " << i; - SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. - EXPECT_EQ(0, out_data_[i]); - } - for (int i = kExpectedOutputLength; i < kMaxBlockSize; ++i) { - std::ostringstream ss; - ss << "i = " << i; - SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. - EXPECT_EQ(1, out_data_[i]); - } -} - -TEST_F(NetEqDecodingTest, DISABLED_ON_ANDROID(GetAudioBeforeInsertPacket)) { - NetEqOutputType type; - // Set all of |out_data_| to 1, and verify that it was set to 0 by the call - // to GetAudio. - for (int i = 0; i < kMaxBlockSize; ++i) { - out_data_[i] = 1; - } - int num_channels; - int samples_per_channel; - EXPECT_EQ(0, neteq_->GetAudio(kMaxBlockSize, out_data_, - &samples_per_channel, - &num_channels, &type)); - // Verify that the first block of samples is set to 0. - static const int kExpectedOutputLength = - kInitSampleRateHz / 100; // 10 ms at initial sample rate. - for (int i = 0; i < kExpectedOutputLength; ++i) { - std::ostringstream ss; - ss << "i = " << i; - SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. - EXPECT_EQ(0, out_data_[i]); - } -} - -TEST_F(NetEqDecodingTest, DISABLED_ON_ANDROID(BackgroundNoise)) { - neteq_->SetBackgroundNoiseMode(kBgnOn); - CheckBgnOff(8000, kBgnOn); - CheckBgnOff(16000, kBgnOn); - CheckBgnOff(32000, kBgnOn); - EXPECT_EQ(kBgnOn, neteq_->BackgroundNoiseMode()); - - neteq_->SetBackgroundNoiseMode(kBgnOff); - CheckBgnOff(8000, kBgnOff); - CheckBgnOff(16000, kBgnOff); - CheckBgnOff(32000, kBgnOff); - EXPECT_EQ(kBgnOff, neteq_->BackgroundNoiseMode()); - - neteq_->SetBackgroundNoiseMode(kBgnFade); - CheckBgnOff(8000, kBgnFade); - CheckBgnOff(16000, kBgnFade); - CheckBgnOff(32000, kBgnFade); - EXPECT_EQ(kBgnFade, neteq_->BackgroundNoiseMode()); -} - -TEST_F(NetEqDecodingTest, DISABLED_ON_ANDROID(SyncPacketInsert)) { - WebRtcRTPHeader rtp_info; - uint32_t receive_timestamp = 0; - // For the readability use the following payloads instead of the defaults of - // this test. - uint8_t kPcm16WbPayloadType = 1; - uint8_t kCngNbPayloadType = 2; - uint8_t kCngWbPayloadType = 3; - uint8_t kCngSwb32PayloadType = 4; - uint8_t kCngSwb48PayloadType = 5; - uint8_t kAvtPayloadType = 6; - uint8_t kRedPayloadType = 7; - uint8_t kIsacPayloadType = 9; // Payload type 8 is already registered. - - // Register decoders. - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderPCM16Bwb, - kPcm16WbPayloadType)); - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderCNGnb, kCngNbPayloadType)); - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderCNGwb, kCngWbPayloadType)); - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderCNGswb32kHz, - kCngSwb32PayloadType)); - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderCNGswb48kHz, - kCngSwb48PayloadType)); - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderAVT, kAvtPayloadType)); - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderRED, kRedPayloadType)); - ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderISAC, kIsacPayloadType)); - - PopulateRtpInfo(0, 0, &rtp_info); - rtp_info.header.payloadType = kPcm16WbPayloadType; - - // The first packet injected cannot be sync-packet. - EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); - - // Payload length of 10 ms PCM16 16 kHz. - const int kPayloadBytes = kBlockSize16kHz * sizeof(int16_t); - uint8_t payload[kPayloadBytes] = {0}; - ASSERT_EQ(0, neteq_->InsertPacket( - rtp_info, payload, kPayloadBytes, receive_timestamp)); - - // Next packet. Last packet contained 10 ms audio. - rtp_info.header.sequenceNumber++; - rtp_info.header.timestamp += kBlockSize16kHz; - receive_timestamp += kBlockSize16kHz; - - // Unacceptable payload types CNG, AVT (DTMF), RED. - rtp_info.header.payloadType = kCngNbPayloadType; - EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); - - rtp_info.header.payloadType = kCngWbPayloadType; - EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); - - rtp_info.header.payloadType = kCngSwb32PayloadType; - EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); - - rtp_info.header.payloadType = kCngSwb48PayloadType; - EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); - - rtp_info.header.payloadType = kAvtPayloadType; - EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); - - rtp_info.header.payloadType = kRedPayloadType; - EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); - - // Change of codec cannot be initiated with a sync packet. - rtp_info.header.payloadType = kIsacPayloadType; - EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); - - // Change of SSRC is not allowed with a sync packet. - rtp_info.header.payloadType = kPcm16WbPayloadType; - ++rtp_info.header.ssrc; - EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); - - --rtp_info.header.ssrc; - EXPECT_EQ(0, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); -} - -// First insert several noise like packets, then sync-packets. Decoding all -// packets should not produce error, statistics should not show any packet loss -// and sync-packets should decode to zero. -TEST_F(NetEqDecodingTest, DISABLED_ON_ANDROID(SyncPacketDecode)) { - WebRtcRTPHeader rtp_info; - PopulateRtpInfo(0, 0, &rtp_info); - const int kPayloadBytes = kBlockSize16kHz * sizeof(int16_t); - uint8_t payload[kPayloadBytes]; - int16_t decoded[kBlockSize16kHz]; - for (int n = 0; n < kPayloadBytes; ++n) { - payload[n] = (rand() & 0xF0) + 1; // Non-zero random sequence. - } - // Insert some packets which decode to noise. We are not interested in - // actual decoded values. - NetEqOutputType output_type; - int num_channels; - int samples_per_channel; - uint32_t receive_timestamp = 0; - int delay_samples = 0; - for (int n = 0; n < 100; ++n) { - ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, - receive_timestamp)); - ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded, - &samples_per_channel, &num_channels, - &output_type)); - ASSERT_EQ(kBlockSize16kHz, samples_per_channel); - ASSERT_EQ(1, num_channels); - - // Even if there is RTP packet in NetEq's buffer, the first frame pulled - // from NetEq starts with few zero samples. Here we measure this delay. - if (n == 0) { - while (decoded[delay_samples] == 0) delay_samples++; - } - rtp_info.header.sequenceNumber++; - rtp_info.header.timestamp += kBlockSize16kHz; - receive_timestamp += kBlockSize16kHz; - } - const int kNumSyncPackets = 10; - // Insert sync-packets, the decoded sequence should be all-zero. - for (int n = 0; n < kNumSyncPackets; ++n) { - ASSERT_EQ(0, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); - ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded, - &samples_per_channel, &num_channels, - &output_type)); - ASSERT_EQ(kBlockSize16kHz, samples_per_channel); - ASSERT_EQ(1, num_channels); - EXPECT_TRUE(IsAllZero(&decoded[delay_samples], - samples_per_channel * num_channels - delay_samples)); - delay_samples = 0; // Delay only matters in the first frame. - rtp_info.header.sequenceNumber++; - rtp_info.header.timestamp += kBlockSize16kHz; - receive_timestamp += kBlockSize16kHz; - } - // We insert a regular packet, if sync packet are not correctly buffered then - // network statistics would show some packet loss. - ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, - receive_timestamp)); - ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded, - &samples_per_channel, &num_channels, - &output_type)); - // Make sure the last inserted packet is decoded and there are non-zero - // samples. - EXPECT_FALSE(IsAllZero(decoded, samples_per_channel * num_channels)); - NetEqNetworkStatistics network_stats; - ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats)); - // Expecting a "clean" network. - EXPECT_EQ(0, network_stats.packet_loss_rate); - EXPECT_EQ(0, network_stats.expand_rate); - EXPECT_EQ(0, network_stats.accelerate_rate); - EXPECT_EQ(0, network_stats.preemptive_rate); -} - -// Test if the size of the packet buffer reported correctly when containing -// sync packets. Also, test if network packets override sync packets. That is to -// prefer decoding a network packet to a sync packet, if both have same sequence -// number and timestamp. -TEST_F(NetEqDecodingTest, - DISABLED_ON_ANDROID(SyncPacketBufferSizeAndOverridenByNetworkPackets)) { - WebRtcRTPHeader rtp_info; - PopulateRtpInfo(0, 0, &rtp_info); - const int kPayloadBytes = kBlockSize16kHz * sizeof(int16_t); - uint8_t payload[kPayloadBytes]; - int16_t decoded[kBlockSize16kHz]; - for (int n = 0; n < kPayloadBytes; ++n) { - payload[n] = (rand() & 0xF0) + 1; // Non-zero random sequence. - } - // Insert some packets which decode to noise. We are not interested in - // actual decoded values. - NetEqOutputType output_type; - int num_channels; - int samples_per_channel; - uint32_t receive_timestamp = 0; - for (int n = 0; n < 1; ++n) { - ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, - receive_timestamp)); - ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded, - &samples_per_channel, &num_channels, - &output_type)); - ASSERT_EQ(kBlockSize16kHz, samples_per_channel); - ASSERT_EQ(1, num_channels); - rtp_info.header.sequenceNumber++; - rtp_info.header.timestamp += kBlockSize16kHz; - receive_timestamp += kBlockSize16kHz; - } - const int kNumSyncPackets = 10; - - WebRtcRTPHeader first_sync_packet_rtp_info; - memcpy(&first_sync_packet_rtp_info, &rtp_info, sizeof(rtp_info)); - - // Insert sync-packets, but no decoding. - for (int n = 0; n < kNumSyncPackets; ++n) { - ASSERT_EQ(0, neteq_->InsertSyncPacket(rtp_info, receive_timestamp)); - rtp_info.header.sequenceNumber++; - rtp_info.header.timestamp += kBlockSize16kHz; - receive_timestamp += kBlockSize16kHz; - } - NetEqNetworkStatistics network_stats; - ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats)); - EXPECT_EQ(kNumSyncPackets * 10, network_stats.current_buffer_size_ms); - - // Rewind |rtp_info| to that of the first sync packet. - memcpy(&rtp_info, &first_sync_packet_rtp_info, sizeof(rtp_info)); - - // Insert. - for (int n = 0; n < kNumSyncPackets; ++n) { - ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, - receive_timestamp)); - rtp_info.header.sequenceNumber++; - rtp_info.header.timestamp += kBlockSize16kHz; - receive_timestamp += kBlockSize16kHz; - } - - // Decode. - for (int n = 0; n < kNumSyncPackets; ++n) { - ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded, - &samples_per_channel, &num_channels, - &output_type)); - ASSERT_EQ(kBlockSize16kHz, samples_per_channel); - ASSERT_EQ(1, num_channels); - EXPECT_TRUE(IsAllNonZero(decoded, samples_per_channel * num_channels)); - } -} - -void NetEqDecodingTest::WrapTest(uint16_t start_seq_no, - uint32_t start_timestamp, - const std::set& drop_seq_numbers, - bool expect_seq_no_wrap, - bool expect_timestamp_wrap) { - uint16_t seq_no = start_seq_no; - uint32_t timestamp = start_timestamp; - const int kBlocksPerFrame = 3; // Number of 10 ms blocks per frame. - const int kFrameSizeMs = kBlocksPerFrame * kTimeStepMs; - const int kSamples = kBlockSize16kHz * kBlocksPerFrame; - const int kPayloadBytes = kSamples * sizeof(int16_t); - double next_input_time_ms = 0.0; - int16_t decoded[kBlockSize16kHz]; - int num_channels; - int samples_per_channel; - NetEqOutputType output_type; - uint32_t receive_timestamp = 0; - - // Insert speech for 1 second. - const int kSpeechDurationMs = 2000; - int packets_inserted = 0; - uint16_t last_seq_no; - uint32_t last_timestamp; - bool timestamp_wrapped = false; - bool seq_no_wrapped = false; - for (double t_ms = 0; t_ms < kSpeechDurationMs; t_ms += 10) { - // Each turn in this for loop is 10 ms. - while (next_input_time_ms <= t_ms) { - // Insert one 30 ms speech frame. - uint8_t payload[kPayloadBytes] = {0}; - WebRtcRTPHeader rtp_info; - PopulateRtpInfo(seq_no, timestamp, &rtp_info); - if (drop_seq_numbers.find(seq_no) == drop_seq_numbers.end()) { - // This sequence number was not in the set to drop. Insert it. - ASSERT_EQ(0, - neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, - receive_timestamp)); - ++packets_inserted; - } - NetEqNetworkStatistics network_stats; - ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats)); - - // Due to internal NetEq logic, preferred buffer-size is about 4 times the - // packet size for first few packets. Therefore we refrain from checking - // the criteria. - if (packets_inserted > 4) { - // Expect preferred and actual buffer size to be no more than 2 frames. - EXPECT_LE(network_stats.preferred_buffer_size_ms, kFrameSizeMs * 2); - EXPECT_LE(network_stats.current_buffer_size_ms, kFrameSizeMs * 2); - } - last_seq_no = seq_no; - last_timestamp = timestamp; - - ++seq_no; - timestamp += kSamples; - receive_timestamp += kSamples; - next_input_time_ms += static_cast(kFrameSizeMs); - - seq_no_wrapped |= seq_no < last_seq_no; - timestamp_wrapped |= timestamp < last_timestamp; - } - // Pull out data once. - ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded, - &samples_per_channel, &num_channels, - &output_type)); - ASSERT_EQ(kBlockSize16kHz, samples_per_channel); - ASSERT_EQ(1, num_channels); - - // Expect delay (in samples) to be less than 2 packets. - EXPECT_LE(timestamp - neteq_->PlayoutTimestamp(), - static_cast(kSamples * 2)); - } - // Make sure we have actually tested wrap-around. - ASSERT_EQ(expect_seq_no_wrap, seq_no_wrapped); - ASSERT_EQ(expect_timestamp_wrap, timestamp_wrapped); -} - -TEST_F(NetEqDecodingTest, SequenceNumberWrap) { - // Start with a sequence number that will soon wrap. - std::set drop_seq_numbers; // Don't drop any packets. - WrapTest(0xFFFF - 10, 0, drop_seq_numbers, true, false); -} - -TEST_F(NetEqDecodingTest, SequenceNumberWrapAndDrop) { - // Start with a sequence number that will soon wrap. - std::set drop_seq_numbers; - drop_seq_numbers.insert(0xFFFF); - drop_seq_numbers.insert(0x0); - WrapTest(0xFFFF - 10, 0, drop_seq_numbers, true, false); -} - -TEST_F(NetEqDecodingTest, TimestampWrap) { - // Start with a timestamp that will soon wrap. - std::set drop_seq_numbers; - WrapTest(0, 0xFFFFFFFF - 3000, drop_seq_numbers, false, true); -} - -TEST_F(NetEqDecodingTest, TimestampAndSequenceNumberWrap) { - // Start with a timestamp and a sequence number that will wrap at the same - // time. - std::set drop_seq_numbers; - WrapTest(0xFFFF - 10, 0xFFFFFFFF - 5000, drop_seq_numbers, true, true); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/normal.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/normal.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/normal.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/normal.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/normal.h" - -#include // memset, memcpy - -#include // min - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h" -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" -#include "webrtc/modules/audio_coding/neteq4/background_noise.h" -#include "webrtc/modules/audio_coding/neteq4/decoder_database.h" -#include "webrtc/modules/audio_coding/neteq4/expand.h" -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" - -namespace webrtc { - -int Normal::Process(const int16_t* input, - size_t length, - Modes last_mode, - int16_t* external_mute_factor_array, - AudioMultiVector* output) { - if (length == 0) { - // Nothing to process. - output->Clear(); - return static_cast(length); - } - - assert(output->Empty()); - // Output should be empty at this point. - output->PushBackInterleaved(input, length); - int16_t* signal = &(*output)[0][0]; - - const unsigned fs_mult = fs_hz_ / 8000; - assert(fs_mult > 0); - // fs_shift = log2(fs_mult), rounded down. - // Note that |fs_shift| is not "exact" for 48 kHz. - // TODO(hlundin): Investigate this further. - const int fs_shift = 30 - WebRtcSpl_NormW32(fs_mult); - - // Check if last RecOut call resulted in an Expand. If so, we have to take - // care of some cross-fading and unmuting. - if (last_mode == kModeExpand) { - // Generate interpolation data using Expand. - // First, set Expand parameters to appropriate values. - expand_->SetParametersForNormalAfterExpand(); - - // Call Expand. - AudioMultiVector expanded(output->Channels()); - expand_->Process(&expanded); - expand_->Reset(); - - for (size_t channel_ix = 0; channel_ix < output->Channels(); ++channel_ix) { - // Adjust muting factor (main muting factor times expand muting factor). - external_mute_factor_array[channel_ix] = static_cast( - WEBRTC_SPL_MUL_16_16_RSFT(external_mute_factor_array[channel_ix], - expand_->MuteFactor(channel_ix), 14)); - - int16_t* signal = &(*output)[channel_ix][0]; - size_t length_per_channel = length / output->Channels(); - // Find largest absolute value in new data. - int16_t decoded_max = WebRtcSpl_MaxAbsValueW16( - signal, static_cast(length_per_channel)); - // Adjust muting factor if needed (to BGN level). - int energy_length = std::min(static_cast(fs_mult * 64), - static_cast(length_per_channel)); - int scaling = 6 + fs_shift - - WebRtcSpl_NormW32(decoded_max * decoded_max); - scaling = std::max(scaling, 0); // |scaling| should always be >= 0. - int32_t energy = WebRtcSpl_DotProductWithScale(signal, signal, - energy_length, scaling); - energy = energy / (energy_length >> scaling); - - int mute_factor; - if ((energy != 0) && - (energy > background_noise_.Energy(channel_ix))) { - // Normalize new frame energy to 15 bits. - scaling = WebRtcSpl_NormW32(energy) - 16; - // We want background_noise_.energy() / energy in Q14. - int32_t bgn_energy = - background_noise_.Energy(channel_ix) << (scaling+14); - int16_t energy_scaled = energy << scaling; - int16_t ratio = WebRtcSpl_DivW32W16(bgn_energy, energy_scaled); - mute_factor = WebRtcSpl_SqrtFloor(static_cast(ratio) << 14); - } else { - mute_factor = 16384; // 1.0 in Q14. - } - if (mute_factor > external_mute_factor_array[channel_ix]) { - external_mute_factor_array[channel_ix] = std::min(mute_factor, 16384); - } - - // If muted increase by 0.64 for every 20 ms (NB/WB 0.0040/0.0020 in Q14). - int16_t increment = 64 / fs_mult; - for (size_t i = 0; i < length_per_channel; i++) { - // Scale with mute factor. - assert(channel_ix < output->Channels()); - assert(i < output->Size()); - int32_t scaled_signal = (*output)[channel_ix][i] * - external_mute_factor_array[channel_ix]; - // Shift 14 with proper rounding. - (*output)[channel_ix][i] = (scaled_signal + 8192) >> 14; - // Increase mute_factor towards 16384. - external_mute_factor_array[channel_ix] = - std::min(external_mute_factor_array[channel_ix] + increment, 16384); - } - - // Interpolate the expanded data into the new vector. - // (NB/WB/SWB32/SWB48 8/16/32/48 samples.) - assert(fs_shift < 3); // Will always be 0, 1, or, 2. - increment = 4 >> fs_shift; - int fraction = increment; - for (size_t i = 0; i < 8 * fs_mult; i++) { - // TODO(hlundin): Add 16 instead of 8 for correct rounding. Keeping 8 - // now for legacy bit-exactness. - assert(channel_ix < output->Channels()); - assert(i < output->Size()); - (*output)[channel_ix][i] = - (fraction * (*output)[channel_ix][i] + - (32 - fraction) * expanded[channel_ix][i] + 8) >> 5; - fraction += increment; - } - } - } else if (last_mode == kModeRfc3389Cng) { - assert(output->Channels() == 1); // Not adapted for multi-channel yet. - static const int kCngLength = 32; - int16_t cng_output[kCngLength]; - // Reset mute factor and start up fresh. - external_mute_factor_array[0] = 16384; - AudioDecoder* cng_decoder = decoder_database_->GetActiveCngDecoder(); - - if (cng_decoder) { - CNG_dec_inst* cng_inst = static_cast(cng_decoder->state()); - // Generate long enough for 32kHz. - if (WebRtcCng_Generate(cng_inst, cng_output, kCngLength, 0) < 0) { - // Error returned; set return vector to all zeros. - memset(cng_output, 0, sizeof(cng_output)); - } - } else { - // If no CNG instance is defined, just copy from the decoded data. - // (This will result in interpolating the decoded with itself.) - memcpy(cng_output, signal, fs_mult * 8 * sizeof(int16_t)); - } - // Interpolate the CNG into the new vector. - // (NB/WB/SWB32/SWB48 8/16/32/48 samples.) - assert(fs_shift < 3); // Will always be 0, 1, or, 2. - int16_t increment = 4 >> fs_shift; - int16_t fraction = increment; - for (size_t i = 0; i < 8 * fs_mult; i++) { - // TODO(hlundin): Add 16 instead of 8 for correct rounding. Keeping 8 now - // for legacy bit-exactness. - signal[i] = - (fraction * signal[i] + (32 - fraction) * cng_output[i] + 8) >> 5; - fraction += increment; - } - } else if (external_mute_factor_array[0] < 16384) { - // Previous was neither of Expand, FadeToBGN or RFC3389_CNG, but we are - // still ramping up from previous muting. - // If muted increase by 0.64 for every 20 ms (NB/WB 0.0040/0.0020 in Q14). - int16_t increment = 64 / fs_mult; - size_t length_per_channel = length / output->Channels(); - for (size_t i = 0; i < length_per_channel; i++) { - for (size_t channel_ix = 0; channel_ix < output->Channels(); - ++channel_ix) { - // Scale with mute factor. - assert(channel_ix < output->Channels()); - assert(i < output->Size()); - int32_t scaled_signal = (*output)[channel_ix][i] * - external_mute_factor_array[channel_ix]; - // Shift 14 with proper rounding. - (*output)[channel_ix][i] = (scaled_signal + 8192) >> 14; - // Increase mute_factor towards 16384. - external_mute_factor_array[channel_ix] = - std::min(16384, external_mute_factor_array[channel_ix] + increment); - } - } - } - - return static_cast(length); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/normal.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/normal.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/normal.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/normal.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_NORMAL_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_NORMAL_H_ - -#include // Access to size_t. - -#include - -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" -#include "webrtc/modules/audio_coding/neteq4/defines.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Forward declarations. -class BackgroundNoise; -class DecoderDatabase; -class Expand; - -// This class provides the "Normal" DSP operation, that is performed when -// there is no data loss, no need to stretch the timing of the signal, and -// no other "special circumstances" are at hand. -class Normal { - public: - Normal(int fs_hz, DecoderDatabase* decoder_database, - const BackgroundNoise& background_noise, - Expand* expand) - : fs_hz_(fs_hz), - decoder_database_(decoder_database), - background_noise_(background_noise), - expand_(expand) { - } - - virtual ~Normal() {} - - // Performs the "Normal" operation. The decoder data is supplied in |input|, - // having |length| samples in total for all channels (interleaved). The - // result is written to |output|. The number of channels allocated in - // |output| defines the number of channels that will be used when - // de-interleaving |input|. |last_mode| contains the mode used in the previous - // GetAudio call (i.e., not the current one), and |external_mute_factor| is - // a pointer to the mute factor in the NetEqImpl class. - int Process(const int16_t* input, size_t length, - Modes last_mode, - int16_t* external_mute_factor_array, - AudioMultiVector* output); - - private: - int fs_hz_; - DecoderDatabase* decoder_database_; - const BackgroundNoise& background_noise_; - Expand* expand_; - - DISALLOW_COPY_AND_ASSIGN(Normal); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_NORMAL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/normal_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/normal_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/normal_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/normal_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Unit tests for Normal class. - -#include "webrtc/modules/audio_coding/neteq4/normal.h" - -#include - -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq4/background_noise.h" -#include "webrtc/modules/audio_coding/neteq4/expand.h" -#include "webrtc/modules/audio_coding/neteq4/mock/mock_decoder_database.h" -#include "webrtc/modules/audio_coding/neteq4/random_vector.h" -#include "webrtc/modules/audio_coding/neteq4/sync_buffer.h" - -namespace webrtc { - -TEST(Normal, CreateAndDestroy) { - MockDecoderDatabase db; - int fs = 8000; - size_t channels = 1; - BackgroundNoise bgn(channels); - SyncBuffer sync_buffer(1, 1000); - RandomVector random_vector; - Expand expand(&bgn, &sync_buffer, &random_vector, fs, channels); - Normal normal(fs, &db, bgn, &expand); - EXPECT_CALL(db, Die()); // Called when |db| goes out of scope. -} - -// TODO(hlundin): Write more tests. - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/OWNERS 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -henrik.lundin@webrtc.org -tina.legrand@webrtc.org -turaj@webrtc.org -minyue@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/packet_buffer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/packet_buffer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/packet_buffer.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/packet_buffer.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,288 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// This is the implementation of the PacketBuffer class. It is mostly based on -// an STL list. The list is kept sorted at all times so that the next packet to -// decode is at the beginning of the list. - -#include "webrtc/modules/audio_coding/neteq4/packet_buffer.h" - -#include // find_if() - -#include "webrtc/modules/audio_coding/neteq4/decoder_database.h" -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" - -namespace webrtc { - -// Predicate used when inserting packets in the buffer list. -// Operator() returns true when |packet| goes before |new_packet|. -class NewTimestampIsLarger { - public: - explicit NewTimestampIsLarger(const Packet* new_packet) - : new_packet_(new_packet) { - } - bool operator()(Packet* packet) { - return (*new_packet_ >= *packet); - } - - private: - const Packet* new_packet_; -}; - -// Constructor. The arguments define the maximum number of slots and maximum -// payload memory (excluding RTP headers) that the buffer will accept. -PacketBuffer::PacketBuffer(size_t max_number_of_packets, - size_t max_memory_bytes) - : max_number_of_packets_(max_number_of_packets), - max_memory_bytes_(max_memory_bytes), - current_memory_bytes_(0) { -} - -// Destructor. All packets in the buffer will be destroyed. -PacketBuffer::~PacketBuffer() { - Flush(); -} - -// Flush the buffer. All packets in the buffer will be destroyed. -void PacketBuffer::Flush() { - DeleteAllPackets(&buffer_); - current_memory_bytes_ = 0; -} - -int PacketBuffer::InsertPacket(Packet* packet) { - if (!packet || !packet->payload) { - if (packet) { - delete packet; - } - return kInvalidPacket; - } - - int return_val = kOK; - - if ((buffer_.size() >= max_number_of_packets_) || - (current_memory_bytes_ + packet->payload_length - > static_cast(max_memory_bytes_))) { - // Buffer is full. Flush it. - Flush(); - return_val = kFlushed; - if ((buffer_.size() >= max_number_of_packets_) || - (current_memory_bytes_ + packet->payload_length - > static_cast(max_memory_bytes_))) { - // Buffer is still too small for the packet. Either the buffer limits are - // really small, or the packet is really large. Delete the packet and - // return an error. - delete [] packet->payload; - delete packet; - return kOversizePacket; - } - } - - // Get an iterator pointing to the place in the buffer where the new packet - // should be inserted. The list is searched from the back, since the most - // likely case is that the new packet should be near the end of the list. - PacketList::reverse_iterator rit = std::find_if( - buffer_.rbegin(), buffer_.rend(), - NewTimestampIsLarger(packet)); - buffer_.insert(rit.base(), packet); // Insert the packet at that position. - current_memory_bytes_ += packet->payload_length; - - return return_val; -} - -int PacketBuffer::InsertPacketList(PacketList* packet_list, - const DecoderDatabase& decoder_database, - uint8_t* current_rtp_payload_type, - uint8_t* current_cng_rtp_payload_type) { - bool flushed = false; - while (!packet_list->empty()) { - Packet* packet = packet_list->front(); - if (decoder_database.IsComfortNoise(packet->header.payloadType)) { - if (*current_cng_rtp_payload_type != 0xFF && - *current_cng_rtp_payload_type != packet->header.payloadType) { - // New CNG payload type implies new codec type. - *current_rtp_payload_type = 0xFF; - Flush(); - flushed = true; - } - *current_cng_rtp_payload_type = packet->header.payloadType; - } else if (!decoder_database.IsDtmf(packet->header.payloadType)) { - // This must be speech. - if (*current_rtp_payload_type != 0xFF && - *current_rtp_payload_type != packet->header.payloadType) { - *current_cng_rtp_payload_type = 0xFF; - Flush(); - flushed = true; - } - *current_rtp_payload_type = packet->header.payloadType; - } - int return_val = InsertPacket(packet); - packet_list->pop_front(); - if (return_val == kFlushed) { - // The buffer flushed, but this is not an error. We can still continue. - flushed = true; - } else if (return_val != kOK) { - // An error occurred. Delete remaining packets in list and return. - DeleteAllPackets(packet_list); - return return_val; - } - } - return flushed ? kFlushed : kOK; -} - -int PacketBuffer::NextTimestamp(uint32_t* next_timestamp) const { - if (Empty()) { - return kBufferEmpty; - } - if (!next_timestamp) { - return kInvalidPointer; - } - *next_timestamp = buffer_.front()->header.timestamp; - return kOK; -} - -int PacketBuffer::NextHigherTimestamp(uint32_t timestamp, - uint32_t* next_timestamp) const { - if (Empty()) { - return kBufferEmpty; - } - if (!next_timestamp) { - return kInvalidPointer; - } - PacketList::const_iterator it; - for (it = buffer_.begin(); it != buffer_.end(); ++it) { - if ((*it)->header.timestamp >= timestamp) { - // Found a packet matching the search. - *next_timestamp = (*it)->header.timestamp; - return kOK; - } - } - return kNotFound; -} - -const RTPHeader* PacketBuffer::NextRtpHeader() const { - if (Empty()) { - return NULL; - } - return const_cast(&(buffer_.front()->header)); -} - -Packet* PacketBuffer::GetNextPacket(int* discard_count) { - if (Empty()) { - // Buffer is empty. - return NULL; - } - - Packet* packet = buffer_.front(); - // Assert that the packet sanity checks in InsertPacket method works. - assert(packet && packet->payload); - buffer_.pop_front(); - current_memory_bytes_ -= packet->payload_length; - assert(current_memory_bytes_ >= 0); // Assert bookkeeping is correct. - // Discard other packets with the same timestamp. These are duplicates or - // redundant payloads that should not be used. - if (discard_count) { - *discard_count = 0; - } - while (!Empty() && - buffer_.front()->header.timestamp == packet->header.timestamp) { - if (DiscardNextPacket() != kOK) { - assert(false); // Must be ok by design. - } - if (discard_count) { - ++(*discard_count); - } - } - return packet; -} - -int PacketBuffer::DiscardNextPacket() { - if (Empty()) { - return kBufferEmpty; - } - Packet* temp_packet = buffer_.front(); - // Assert that the packet sanity checks in InsertPacket method works. - assert(temp_packet && temp_packet->payload); - current_memory_bytes_ -= temp_packet->payload_length; - assert(current_memory_bytes_ >= 0); // Assert bookkeeping is correct. - DeleteFirstPacket(&buffer_); - return kOK; -} - -int PacketBuffer::DiscardOldPackets(uint32_t timestamp_limit) { - int discard_count = 0; - while (!Empty() && - timestamp_limit != buffer_.front()->header.timestamp && - static_cast(timestamp_limit - - buffer_.front()->header.timestamp) < - 0xFFFFFFFF / 2) { - if (DiscardNextPacket() != kOK) { - assert(false); // Must be ok by design. - } - ++discard_count; - } - return 0; -} - -int PacketBuffer::NumSamplesInBuffer(DecoderDatabase* decoder_database, - int last_decoded_length) const { - PacketList::const_iterator it; - int num_samples = 0; - int last_duration = last_decoded_length; - for (it = buffer_.begin(); it != buffer_.end(); ++it) { - Packet* packet = (*it); - AudioDecoder* decoder = - decoder_database->GetDecoder(packet->header.payloadType); - if (decoder) { - int duration = packet->sync_packet ? last_duration : - decoder->PacketDuration(packet->payload, packet->payload_length); - if (duration >= 0) { - last_duration = duration; // Save the most up-to-date (valid) duration. - } - } - num_samples += last_duration; - } - return num_samples; -} - -void PacketBuffer::IncrementWaitingTimes(int inc) { - PacketList::iterator it; - for (it = buffer_.begin(); it != buffer_.end(); ++it) { - (*it)->waiting_time += inc; - } -} - -bool PacketBuffer::DeleteFirstPacket(PacketList* packet_list) { - if (packet_list->empty()) { - return false; - } - Packet* first_packet = packet_list->front(); - delete [] first_packet->payload; - delete first_packet; - packet_list->pop_front(); - return true; -} - -void PacketBuffer::DeleteAllPackets(PacketList* packet_list) { - while (DeleteFirstPacket(packet_list)) { - // Continue while the list is not empty. - } -} - -void PacketBuffer::BufferStat(int* num_packets, - int* max_num_packets, - int* current_memory_bytes, - int* max_memory_bytes) const { - *num_packets = static_cast(buffer_.size()); - *max_num_packets = static_cast(max_number_of_packets_); - *current_memory_bytes = current_memory_bytes_; - *max_memory_bytes = static_cast(max_memory_bytes_); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/packet_buffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/packet_buffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/packet_buffer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/packet_buffer.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_PACKET_BUFFER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_PACKET_BUFFER_H_ - -#include "webrtc/modules/audio_coding/neteq4/packet.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Forward declaration. -class DecoderDatabase; - -// This is the actual buffer holding the packets before decoding. -class PacketBuffer { - public: - enum BufferReturnCodes { - kOK = 0, - kFlushed, - kNotFound, - kBufferEmpty, - kInvalidPacket, - kInvalidPointer, - kOversizePacket - }; - - // Constructor creates a buffer which can hold a maximum of - // |max_number_of_packets| packets and |max_payload_memory| bytes of payload, - // excluding RTP headers. - PacketBuffer(size_t max_number_of_packets, size_t max_payload_memory); - - // Deletes all packets in the buffer before destroying the buffer. - virtual ~PacketBuffer(); - - // Flushes the buffer and deletes all packets in it. - virtual void Flush(); - - // Returns true for an empty buffer. - virtual bool Empty() const { return buffer_.empty(); } - - // Inserts |packet| into the buffer. The buffer will take over ownership of - // the packet object. - // Returns PacketBuffer::kOK on success, PacketBuffer::kFlushed if the buffer - // was flushed due to overfilling. - virtual int InsertPacket(Packet* packet); - - // Inserts a list of packets into the buffer. The buffer will take over - // ownership of the packet objects. - // Returns PacketBuffer::kOK if all packets were inserted successfully. - // If the buffer was flushed due to overfilling, only a subset of the list is - // inserted, and PacketBuffer::kFlushed is returned. - // The last three parameters are included for legacy compatibility. - // TODO(hlundin): Redesign to not use current_*_payload_type and - // decoder_database. - virtual int InsertPacketList(PacketList* packet_list, - const DecoderDatabase& decoder_database, - uint8_t* current_rtp_payload_type, - uint8_t* current_cng_rtp_payload_type); - - // Gets the timestamp for the first packet in the buffer and writes it to the - // output variable |next_timestamp|. - // Returns PacketBuffer::kBufferEmpty if the buffer is empty, - // PacketBuffer::kOK otherwise. - virtual int NextTimestamp(uint32_t* next_timestamp) const; - - // Gets the timestamp for the first packet in the buffer with a timestamp no - // lower than the input limit |timestamp|. The result is written to the output - // variable |next_timestamp|. - // Returns PacketBuffer::kBufferEmpty if the buffer is empty, - // PacketBuffer::kOK otherwise. - virtual int NextHigherTimestamp(uint32_t timestamp, - uint32_t* next_timestamp) const; - - // Returns a (constant) pointer the RTP header of the first packet in the - // buffer. Returns NULL if the buffer is empty. - virtual const RTPHeader* NextRtpHeader() const; - - // Extracts the first packet in the buffer and returns a pointer to it. - // Returns NULL if the buffer is empty. The caller is responsible for deleting - // the packet. - // Subsequent packets with the same timestamp as the one extracted will be - // discarded and properly deleted. The number of discarded packets will be - // written to the output variable |discard_count|. - virtual Packet* GetNextPacket(int* discard_count); - - // Discards the first packet in the buffer. The packet is deleted. - // Returns PacketBuffer::kBufferEmpty if the buffer is empty, - // PacketBuffer::kOK otherwise. - virtual int DiscardNextPacket(); - - // Discards all packets that are (strictly) older than |timestamp_limit|. - // Returns number of packets discarded. - virtual int DiscardOldPackets(uint32_t timestamp_limit); - - // Returns the number of packets in the buffer, including duplicates and - // redundant packets. - virtual int NumPacketsInBuffer() const { - return static_cast(buffer_.size()); - } - - // Returns the number of samples in the buffer, including samples carried in - // duplicate and redundant packets. - virtual int NumSamplesInBuffer(DecoderDatabase* decoder_database, - int last_decoded_length) const; - - // Increase the waiting time counter for every packet in the buffer by |inc|. - // The default value for |inc| is 1. - virtual void IncrementWaitingTimes(int inc = 1); - - virtual void BufferStat(int* num_packets, - int* max_num_packets, - int* current_memory_bytes, - int* max_memory_bytes) const; - - virtual int current_memory_bytes() const { return current_memory_bytes_; } - - // Static method that properly deletes the first packet, and its payload - // array, in |packet_list|. Returns false if |packet_list| already was empty, - // otherwise true. - static bool DeleteFirstPacket(PacketList* packet_list); - - // Static method that properly deletes all packets, and their payload arrays, - // in |packet_list|. - static void DeleteAllPackets(PacketList* packet_list); - - private: - size_t max_number_of_packets_; - size_t max_memory_bytes_; - int current_memory_bytes_; - PacketList buffer_; - DISALLOW_COPY_AND_ASSIGN(PacketBuffer); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_PACKET_BUFFER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/packet_buffer_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/packet_buffer_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/packet_buffer_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/packet_buffer_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,560 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Unit tests for PacketBuffer class. - -#include "webrtc/modules/audio_coding/neteq4/packet_buffer.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq4/mock/mock_decoder_database.h" -#include "webrtc/modules/audio_coding/neteq4/packet.h" - -using ::testing::Return; -using ::testing::_; - -namespace webrtc { - -// Helper class to generate packets. Packets must be deleted by the user. -class PacketGenerator { - public: - PacketGenerator(uint16_t seq_no, uint32_t ts, uint8_t pt, int frame_size); - virtual ~PacketGenerator() {} - Packet* NextPacket(int payload_size_bytes); - void SkipPacket(); - - uint16_t seq_no_; - uint32_t ts_; - uint8_t pt_; - int frame_size_; -}; - -PacketGenerator::PacketGenerator(uint16_t seq_no, uint32_t ts, uint8_t pt, - int frame_size) - : seq_no_(seq_no), - ts_(ts), - pt_(pt), - frame_size_(frame_size) { -} - -Packet* PacketGenerator::NextPacket(int payload_size_bytes) { - Packet* packet = new Packet; - packet->header.sequenceNumber = seq_no_; - packet->header.timestamp = ts_; - packet->header.payloadType = pt_; - packet->header.markerBit = false; - packet->header.ssrc = 0x12345678; - packet->header.numCSRCs = 0; - packet->header.paddingLength = 0; - packet->payload_length = payload_size_bytes; - packet->primary = true; - packet->payload = new uint8_t[payload_size_bytes]; - ++seq_no_; - ts_ += frame_size_; - return packet; -} - -void PacketGenerator::SkipPacket() { - ++seq_no_; - ts_ += frame_size_; -} - - -// Start of test definitions. - -TEST(PacketBuffer, CreateAndDestroy) { - PacketBuffer* buffer = new PacketBuffer(10, 1000); // 10 packets, 1000 bytes. - EXPECT_TRUE(buffer->Empty()); - delete buffer; -} - -TEST(PacketBuffer, InsertPacket) { - PacketBuffer buffer(10, 1000); // 10 packets, 1000 bytes. - PacketGenerator gen(17u, 4711u, 0, 10); - - const int payload_len = 100; - Packet* packet = gen.NextPacket(payload_len); - - EXPECT_EQ(0, buffer.InsertPacket(packet)); - uint32_t next_ts; - EXPECT_EQ(PacketBuffer::kOK, buffer.NextTimestamp(&next_ts)); - EXPECT_EQ(4711u, next_ts); - EXPECT_FALSE(buffer.Empty()); - EXPECT_EQ(1, buffer.NumPacketsInBuffer()); - EXPECT_EQ(payload_len, buffer.current_memory_bytes()); - const RTPHeader* hdr = buffer.NextRtpHeader(); - EXPECT_EQ(&(packet->header), hdr); // Compare pointer addresses. - - // Do not explicitly flush buffer or delete packet to test that it is deleted - // with the buffer. (Tested with Valgrind or similar tool.) -} - -// Test to flush buffer. -TEST(PacketBuffer, FlushBuffer) { - PacketBuffer buffer(10, 1000); // 10 packets, 1000 bytes. - PacketGenerator gen(0, 0, 0, 10); - const int payload_len = 10; - - // Insert 10 small packets; should be ok. - for (int i = 0; i < 10; ++i) { - Packet* packet = gen.NextPacket(payload_len); - EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacket(packet)); - } - EXPECT_EQ(10, buffer.NumPacketsInBuffer()); - EXPECT_FALSE(buffer.Empty()); - EXPECT_EQ(10 * payload_len, buffer.current_memory_bytes()); - - buffer.Flush(); - // Buffer should delete the payloads itself. - EXPECT_EQ(0, buffer.NumPacketsInBuffer()); - EXPECT_TRUE(buffer.Empty()); - EXPECT_EQ(0, buffer.current_memory_bytes()); -} - -// Test to fill the buffer over the limits, and verify that it flushes. -TEST(PacketBuffer, OverfillBuffer) { - PacketBuffer buffer(10, 1000); // 10 packets, 1000 bytes. - PacketGenerator gen(0, 0, 0, 10); - - // Insert 10 small packets; should be ok. - const int payload_len = 10; - int i; - for (i = 0; i < 10; ++i) { - Packet* packet = gen.NextPacket(payload_len); - EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacket(packet)); - } - EXPECT_EQ(10, buffer.NumPacketsInBuffer()); - EXPECT_EQ(10 * payload_len, buffer.current_memory_bytes()); - uint32_t next_ts; - EXPECT_EQ(PacketBuffer::kOK, buffer.NextTimestamp(&next_ts)); - EXPECT_EQ(0u, next_ts); // Expect first inserted packet to be first in line. - - // Insert 11th packet; should flush the buffer and insert it after flushing. - Packet* packet = gen.NextPacket(payload_len); - EXPECT_EQ(PacketBuffer::kFlushed, buffer.InsertPacket(packet)); - EXPECT_EQ(1, buffer.NumPacketsInBuffer()); - EXPECT_EQ(payload_len, buffer.current_memory_bytes()); - EXPECT_EQ(PacketBuffer::kOK, buffer.NextTimestamp(&next_ts)); - // Expect last inserted packet to be first in line. - EXPECT_EQ(packet->header.timestamp, next_ts); - - // Insert 2 large packets; expect to flush when inserting the second one. - const int large_payload_len = 500; - packet = gen.NextPacket(large_payload_len); - EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacket(packet)); - EXPECT_EQ(2, buffer.NumPacketsInBuffer()); - EXPECT_EQ(payload_len + large_payload_len, buffer.current_memory_bytes()); - - packet = gen.NextPacket(large_payload_len); - EXPECT_EQ(PacketBuffer::kFlushed, buffer.InsertPacket(packet)); - EXPECT_EQ(1, buffer.NumPacketsInBuffer()); - EXPECT_EQ(large_payload_len, buffer.current_memory_bytes()); - - // Flush buffer to delete remaining packets. - buffer.Flush(); -} - -// Test inserting a list of packets. -TEST(PacketBuffer, InsertPacketList) { - PacketBuffer buffer(10, 1000); // 10 packets, 1000 bytes. - PacketGenerator gen(0, 0, 0, 10); - PacketList list; - const int payload_len = 10; - - // Insert 10 small packets. - for (int i = 0; i < 10; ++i) { - Packet* packet = gen.NextPacket(payload_len); - list.push_back(packet); - } - - MockDecoderDatabase decoder_database; - EXPECT_CALL(decoder_database, IsComfortNoise(0)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(decoder_database, IsDtmf(0)) - .WillRepeatedly(Return(false)); - uint8_t current_pt = 0xFF; - uint8_t current_cng_pt = 0xFF; - EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacketList(&list, - decoder_database, - ¤t_pt, - ¤t_cng_pt)); - EXPECT_TRUE(list.empty()); // The PacketBuffer should have depleted the list. - EXPECT_EQ(10, buffer.NumPacketsInBuffer()); - EXPECT_EQ(10 * payload_len, buffer.current_memory_bytes()); - EXPECT_EQ(0, current_pt); // Current payload type changed to 0. - EXPECT_EQ(0xFF, current_cng_pt); // CNG payload type not changed. - - buffer.Flush(); // Clean up. - - EXPECT_CALL(decoder_database, Die()); // Called when object is deleted. -} - -// Test inserting a list of packets. Last packet is of a different payload type. -// Expecting the buffer to flush. -// TODO(hlundin): Remove this test when legacy operation is no longer needed. -TEST(PacketBuffer, InsertPacketListChangePayloadType) { - PacketBuffer buffer(10, 1000); // 10 packets, 1000 bytes. - PacketGenerator gen(0, 0, 0, 10); - PacketList list; - const int payload_len = 10; - - // Insert 10 small packets. - for (int i = 0; i < 10; ++i) { - Packet* packet = gen.NextPacket(payload_len); - list.push_back(packet); - } - // Insert 11th packet of another payload type (not CNG). - Packet* packet = gen.NextPacket(payload_len); - packet->header.payloadType = 1; - list.push_back(packet); - - - MockDecoderDatabase decoder_database; - EXPECT_CALL(decoder_database, IsComfortNoise(_)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(decoder_database, IsDtmf(_)) - .WillRepeatedly(Return(false)); - uint8_t current_pt = 0xFF; - uint8_t current_cng_pt = 0xFF; - EXPECT_EQ(PacketBuffer::kFlushed, buffer.InsertPacketList(&list, - decoder_database, - ¤t_pt, - ¤t_cng_pt)); - EXPECT_TRUE(list.empty()); // The PacketBuffer should have depleted the list. - EXPECT_EQ(1, buffer.NumPacketsInBuffer()); // Only the last packet. - EXPECT_EQ(1 * payload_len, buffer.current_memory_bytes()); - EXPECT_EQ(1, current_pt); // Current payload type changed to 0. - EXPECT_EQ(0xFF, current_cng_pt); // CNG payload type not changed. - - buffer.Flush(); // Clean up. - - EXPECT_CALL(decoder_database, Die()); // Called when object is deleted. -} - -// Test inserting a number of packets, and verifying correct extraction order. -// The packets inserted are as follows: -// Packet no. Seq. no. Primary TS Secondary TS -// 0 0xFFFD 0xFFFFFFD7 - -// 1 0xFFFE 0xFFFFFFE1 0xFFFFFFD7 -// 2 0xFFFF 0xFFFFFFEB 0xFFFFFFE1 -// 3 0x0000 0xFFFFFFF5 0xFFFFFFEB -// 4 0x0001 0xFFFFFFFF 0xFFFFFFF5 -// 5 0x0002 0x0000000A 0xFFFFFFFF -// 6 MISSING--0x0003------0x00000014----0x0000000A--MISSING -// 7 0x0004 0x0000001E 0x00000014 -// 8 0x0005 0x00000028 0x0000001E -// 9 0x0006 0x00000032 0x00000028 -TEST(PacketBuffer, ExtractOrderRedundancy) { - PacketBuffer buffer(100, 1000); // 100 packets, 1000 bytes. - const uint32_t ts_increment = 10; // Samples per packet. - const uint16_t start_seq_no = 0xFFFF - 2; // Wraps after 3 packets. - const uint32_t start_ts = 0xFFFFFFFF - - 4 * ts_increment; // Wraps after 5 packets. - const uint8_t primary_pt = 0; - const uint8_t secondary_pt = 1; - PacketGenerator gen(start_seq_no, start_ts, primary_pt, ts_increment); - // Insert secondary payloads too. (Simulating RED.) - PacketGenerator red_gen(start_seq_no + 1, start_ts, secondary_pt, - ts_increment); - - // Insert 9 small packets (skip one). - for (int i = 0; i < 10; ++i) { - const int payload_len = 10; - if (i == 6) { - // Skip this packet. - gen.SkipPacket(); - red_gen.SkipPacket(); - continue; - } - // Primary payload. - Packet* packet = gen.NextPacket(payload_len); - EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacket(packet)); - if (i >= 1) { - // Secondary payload. - packet = red_gen.NextPacket(payload_len); - packet->primary = false; - EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacket(packet)); - } - } - EXPECT_EQ(17, buffer.NumPacketsInBuffer()); // 9 primary + 8 secondary - - uint16_t current_seq_no = start_seq_no; - uint32_t current_ts = start_ts; - - for (int i = 0; i < 10; ++i) { - // Extract packets. - int drop_count = 0; - Packet* packet = buffer.GetNextPacket(&drop_count); - ASSERT_FALSE(packet == NULL); - if (i == 6) { - // Special case for the dropped primary payload. - // Expect secondary payload, and one step higher sequence number. - EXPECT_EQ(current_seq_no + 1, packet->header.sequenceNumber); - EXPECT_EQ(current_ts, packet->header.timestamp); - EXPECT_FALSE(packet->primary); - EXPECT_EQ(1, packet->header.payloadType); - EXPECT_EQ(0, drop_count); - } else { - EXPECT_EQ(current_seq_no, packet->header.sequenceNumber); - EXPECT_EQ(current_ts, packet->header.timestamp); - EXPECT_TRUE(packet->primary); - EXPECT_EQ(0, packet->header.payloadType); - if (i == 5 || i == 9) { - // No duplicate TS for dropped packet or for last primary payload. - EXPECT_EQ(0, drop_count); - } else { - EXPECT_EQ(1, drop_count); - } - } - ++current_seq_no; - current_ts += ts_increment; - delete [] packet->payload; - delete packet; - } -} - -TEST(PacketBuffer, DiscardPackets) { - PacketBuffer buffer(100, 1000); // 100 packets, 1000 bytes. - const uint16_t start_seq_no = 17; - const uint32_t start_ts = 4711; - const uint32_t ts_increment = 10; - PacketGenerator gen(start_seq_no, start_ts, 0, ts_increment); - PacketList list; - const int payload_len = 10; - - // Insert 10 small packets. - for (int i = 0; i < 10; ++i) { - Packet* packet = gen.NextPacket(payload_len); - buffer.InsertPacket(packet); - } - EXPECT_EQ(10, buffer.NumPacketsInBuffer()); - EXPECT_EQ(10 * payload_len, buffer.current_memory_bytes()); - - // Discard them one by one and make sure that the right packets are at the - // front of the buffer. - uint32_t current_ts = start_ts; - for (int i = 0; i < 10; ++i) { - uint32_t ts; - EXPECT_EQ(PacketBuffer::kOK, buffer.NextTimestamp(&ts)); - EXPECT_EQ(current_ts, ts); - EXPECT_EQ(PacketBuffer::kOK, buffer.DiscardNextPacket()); - current_ts += ts_increment; - } - EXPECT_TRUE(buffer.Empty()); -} - -TEST(PacketBuffer, Reordering) { - PacketBuffer buffer(100, 1000); // 100 packets, 1000 bytes. - const uint16_t start_seq_no = 17; - const uint32_t start_ts = 4711; - const uint32_t ts_increment = 10; - PacketGenerator gen(start_seq_no, start_ts, 0, ts_increment); - const int payload_len = 10; - - // Generate 10 small packets and insert them into a PacketList. Insert every - // odd packet to the front, and every even packet to the back, thus creating - // a (rather strange) reordering. - PacketList list; - for (int i = 0; i < 10; ++i) { - Packet* packet = gen.NextPacket(payload_len); - if (i % 2) { - list.push_front(packet); - } else { - list.push_back(packet); - } - } - - MockDecoderDatabase decoder_database; - EXPECT_CALL(decoder_database, IsComfortNoise(0)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(decoder_database, IsDtmf(0)) - .WillRepeatedly(Return(false)); - uint8_t current_pt = 0xFF; - uint8_t current_cng_pt = 0xFF; - - EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacketList(&list, - decoder_database, - ¤t_pt, - ¤t_cng_pt)); - EXPECT_EQ(10, buffer.NumPacketsInBuffer()); - EXPECT_EQ(10 * payload_len, buffer.current_memory_bytes()); - - // Extract them and make sure that come out in the right order. - uint32_t current_ts = start_ts; - for (int i = 0; i < 10; ++i) { - Packet* packet = buffer.GetNextPacket(NULL); - ASSERT_FALSE(packet == NULL); - EXPECT_EQ(current_ts, packet->header.timestamp); - current_ts += ts_increment; - delete [] packet->payload; - delete packet; - } - EXPECT_TRUE(buffer.Empty()); - - EXPECT_CALL(decoder_database, Die()); // Called when object is deleted. -} - -TEST(PacketBuffer, Failures) { - const uint16_t start_seq_no = 17; - const uint32_t start_ts = 4711; - const uint32_t ts_increment = 10; - int payload_len = 100; - PacketGenerator gen(start_seq_no, start_ts, 0, ts_increment); - - PacketBuffer* buffer = new PacketBuffer(0, 1000); // 0 packets, 1000 bytes. - Packet* packet = gen.NextPacket(payload_len); - EXPECT_EQ(PacketBuffer::kOversizePacket, buffer->InsertPacket(packet)); - delete buffer; - - buffer = new PacketBuffer(100, 10); // 100 packets, 10 bytes. - packet = gen.NextPacket(payload_len); - EXPECT_EQ(PacketBuffer::kOversizePacket, buffer->InsertPacket(packet)); - delete buffer; - - buffer = new PacketBuffer(100, 10000); // 100 packets, 10000 bytes. - packet = NULL; - EXPECT_EQ(PacketBuffer::kInvalidPacket, buffer->InsertPacket(packet)); - packet = gen.NextPacket(payload_len); - delete [] packet->payload; - packet->payload = NULL; - EXPECT_EQ(PacketBuffer::kInvalidPacket, buffer->InsertPacket(packet)); - // Packet is deleted by the PacketBuffer. - - // Buffer should still be empty. Test all empty-checks. - uint32_t temp_ts; - EXPECT_EQ(PacketBuffer::kBufferEmpty, buffer->NextTimestamp(&temp_ts)); - EXPECT_EQ(PacketBuffer::kBufferEmpty, - buffer->NextHigherTimestamp(0, &temp_ts)); - EXPECT_EQ(NULL, buffer->NextRtpHeader()); - EXPECT_EQ(NULL, buffer->GetNextPacket(NULL)); - EXPECT_EQ(PacketBuffer::kBufferEmpty, buffer->DiscardNextPacket()); - EXPECT_EQ(0, buffer->DiscardOldPackets(0)); // 0 packets discarded. - - // Insert one packet to make the buffer non-empty. - packet = gen.NextPacket(payload_len); - EXPECT_EQ(PacketBuffer::kOK, buffer->InsertPacket(packet)); - EXPECT_EQ(PacketBuffer::kInvalidPointer, buffer->NextTimestamp(NULL)); - EXPECT_EQ(PacketBuffer::kInvalidPointer, - buffer->NextHigherTimestamp(0, NULL)); - delete buffer; - - // Insert packet list of three packets, where the second packet has an invalid - // payload. Expect first packet to be inserted, and the remaining two to be - // discarded. - buffer = new PacketBuffer(100, 1000); // 100 packets, 1000 bytes. - PacketList list; - list.push_back(gen.NextPacket(payload_len)); // Valid packet. - packet = gen.NextPacket(payload_len); - delete [] packet->payload; - packet->payload = NULL; // Invalid. - list.push_back(packet); - list.push_back(gen.NextPacket(payload_len)); // Valid packet. - MockDecoderDatabase decoder_database; - EXPECT_CALL(decoder_database, IsComfortNoise(0)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(decoder_database, IsDtmf(0)) - .WillRepeatedly(Return(false)); - uint8_t current_pt = 0xFF; - uint8_t current_cng_pt = 0xFF; - EXPECT_EQ(PacketBuffer::kInvalidPacket, - buffer->InsertPacketList(&list, - decoder_database, - ¤t_pt, - ¤t_cng_pt)); - EXPECT_TRUE(list.empty()); // The PacketBuffer should have depleted the list. - EXPECT_EQ(1, buffer->NumPacketsInBuffer()); - delete buffer; - EXPECT_CALL(decoder_database, Die()); // Called when object is deleted. -} - -// Test packet comparison function. -// The function should return true if the first packet "goes before" the second. -TEST(PacketBuffer, ComparePackets) { - PacketGenerator gen(0, 0, 0, 10); - Packet* a = gen.NextPacket(10); // SN = 0, TS = 0. - Packet* b = gen.NextPacket(10); // SN = 1, TS = 10. - EXPECT_FALSE(*a == *b); - EXPECT_TRUE(*a != *b); - EXPECT_TRUE(*a < *b); - EXPECT_FALSE(*a > *b); - EXPECT_TRUE(*a <= *b); - EXPECT_FALSE(*a >= *b); - - // Testing wrap-around case; 'a' is earlier but has a larger timestamp value. - a->header.timestamp = 0xFFFFFFFF - 10; - EXPECT_FALSE(*a == *b); - EXPECT_TRUE(*a != *b); - EXPECT_TRUE(*a < *b); - EXPECT_FALSE(*a > *b); - EXPECT_TRUE(*a <= *b); - EXPECT_FALSE(*a >= *b); - - // Test equal packets. - EXPECT_TRUE(*a == *a); - EXPECT_FALSE(*a != *a); - EXPECT_FALSE(*a < *a); - EXPECT_FALSE(*a > *a); - EXPECT_TRUE(*a <= *a); - EXPECT_TRUE(*a >= *a); - - // Test equal timestamps but different sequence numbers (0 and 1). - a->header.timestamp = b->header.timestamp; - EXPECT_FALSE(*a == *b); - EXPECT_TRUE(*a != *b); - EXPECT_TRUE(*a < *b); - EXPECT_FALSE(*a > *b); - EXPECT_TRUE(*a <= *b); - EXPECT_FALSE(*a >= *b); - - // Test equal timestamps but different sequence numbers (32767 and 1). - a->header.sequenceNumber = 0xFFFF; - EXPECT_FALSE(*a == *b); - EXPECT_TRUE(*a != *b); - EXPECT_TRUE(*a < *b); - EXPECT_FALSE(*a > *b); - EXPECT_TRUE(*a <= *b); - EXPECT_FALSE(*a >= *b); - - // Test equal timestamps and sequence numbers, but only 'b' is primary. - a->header.sequenceNumber = b->header.sequenceNumber; - a->primary = false; - b->primary = true; - EXPECT_FALSE(*a == *b); - EXPECT_TRUE(*a != *b); - EXPECT_FALSE(*a < *b); - EXPECT_TRUE(*a > *b); - EXPECT_FALSE(*a <= *b); - EXPECT_TRUE(*a >= *b); - - delete [] a->payload; - delete a; - delete [] b->payload; - delete b; -} - -// Test the DeleteFirstPacket DeleteAllPackets methods. -TEST(PacketBuffer, DeleteAllPackets) { - PacketGenerator gen(0, 0, 0, 10); - PacketList list; - const int payload_len = 10; - - // Insert 10 small packets. - for (int i = 0; i < 10; ++i) { - Packet* packet = gen.NextPacket(payload_len); - list.push_back(packet); - } - EXPECT_TRUE(PacketBuffer::DeleteFirstPacket(&list)); - EXPECT_EQ(9u, list.size()); - PacketBuffer::DeleteAllPackets(&list); - EXPECT_TRUE(list.empty()); - EXPECT_FALSE(PacketBuffer::DeleteFirstPacket(&list)); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/packet.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/packet.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/packet.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/packet.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_PACKET_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_PACKET_H_ - -#include - -#include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Struct for holding RTP packets. -struct Packet { - RTPHeader header; - uint8_t* payload; // Datagram excluding RTP header and header extension. - int payload_length; - bool primary; // Primary, i.e., not redundant payload. - int waiting_time; - bool sync_packet; - - // Constructor. - Packet() - : payload(NULL), - payload_length(0), - primary(true), - waiting_time(0), - sync_packet(false) { - } - - // Comparison operators. Establish a packet ordering based on (1) timestamp, - // (2) sequence number, (3) regular packet vs sync-packet and (4) redundancy. - // Timestamp and sequence numbers are compared taking wrap-around into - // account. If both timestamp and sequence numbers are identical and one of - // the packets is sync-packet, the regular packet is considered earlier. For - // two regular packets with the same sequence number and timestamp a primary - // payload is considered "smaller" than a secondary. - bool operator==(const Packet& rhs) const { - return (this->header.timestamp == rhs.header.timestamp && - this->header.sequenceNumber == rhs.header.sequenceNumber && - this->primary == rhs.primary && - this->sync_packet == rhs.sync_packet); - } - bool operator!=(const Packet& rhs) const { return !operator==(rhs); } - bool operator<(const Packet& rhs) const { - if (this->header.timestamp == rhs.header.timestamp) { - if (this->header.sequenceNumber == rhs.header.sequenceNumber) { - // Timestamp and sequence numbers are identical. A sync packet should - // be recognized "larger" (i.e. "later") compared to a "network packet" - // (regular packet from network not sync-packet). If none of the packets - // are sync-packets, then deem the left hand side to be "smaller" - // (i.e., "earlier") if it is primary, and right hand side is not. - // - // The condition on sync packets to be larger than "network packets," - // given same RTP sequence number and timestamp, guarantees that a - // "network packet" to be inserted in an earlier position into - // |packet_buffer_| compared to a sync packet of same timestamp and - // sequence number. - if (rhs.sync_packet) - return true; - if (this->sync_packet) - return false; - return (this->primary && !rhs.primary); - } - return (static_cast(rhs.header.sequenceNumber - - this->header.sequenceNumber) < 0xFFFF / 2); - } - return (static_cast(rhs.header.timestamp - - this->header.timestamp) < 0xFFFFFFFF / 2); - } - bool operator>(const Packet& rhs) const { return rhs.operator<(*this); } - bool operator<=(const Packet& rhs) const { return !operator>(rhs); } - bool operator>=(const Packet& rhs) const { return !operator<(rhs); } -}; - -// A list of packets. -typedef std::list PacketList; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_PACKET_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/payload_splitter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/payload_splitter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/payload_splitter.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/payload_splitter.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,372 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/payload_splitter.h" - -#include - -#include "webrtc/modules/audio_coding/neteq4/decoder_database.h" - -namespace webrtc { - -// The method loops through a list of packets {A, B, C, ...}. Each packet is -// split into its corresponding RED payloads, {A1, A2, ...}, which is -// temporarily held in the list |new_packets|. -// When the first packet in |packet_list| has been processed, the orignal packet -// is replaced by the new ones in |new_packets|, so that |packet_list| becomes: -// {A1, A2, ..., B, C, ...}. The method then continues with B, and C, until all -// the original packets have been replaced by their split payloads. -int PayloadSplitter::SplitRed(PacketList* packet_list) { - int ret = kOK; - PacketList::iterator it = packet_list->begin(); - while (it != packet_list->end()) { - PacketList new_packets; // An empty list to store the split packets in. - Packet* red_packet = (*it); - assert(red_packet->payload); - uint8_t* payload_ptr = red_packet->payload; - - // Read RED headers (according to RFC 2198): - // - // 0 1 2 3 - // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // |F| block PT | timestamp offset | block length | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // Last RED header: - // 0 1 2 3 4 5 6 7 - // +-+-+-+-+-+-+-+-+ - // |0| Block PT | - // +-+-+-+-+-+-+-+-+ - - bool last_block = false; - int sum_length = 0; - while (!last_block) { - Packet* new_packet = new Packet; - new_packet->header = red_packet->header; - // Check the F bit. If F == 0, this was the last block. - last_block = ((*payload_ptr & 0x80) == 0); - // Bits 1 through 7 are payload type. - new_packet->header.payloadType = payload_ptr[0] & 0x7F; - if (last_block) { - // No more header data to read. - ++sum_length; // Account for RED header size of 1 byte. - new_packet->payload_length = red_packet->payload_length - sum_length; - new_packet->primary = true; // Last block is always primary. - payload_ptr += 1; // Advance to first payload byte. - } else { - // Bits 8 through 21 are timestamp offset. - int timestamp_offset = (payload_ptr[1] << 6) + - ((payload_ptr[2] & 0xFC) >> 2); - new_packet->header.timestamp = red_packet->header.timestamp - - timestamp_offset; - // Bits 22 through 31 are payload length. - new_packet->payload_length = ((payload_ptr[2] & 0x03) << 8) + - payload_ptr[3]; - new_packet->primary = false; - payload_ptr += 4; // Advance to next RED header. - } - sum_length += new_packet->payload_length; - sum_length += 4; // Account for RED header size of 4 bytes. - // Store in new list of packets. - new_packets.push_back(new_packet); - } - - // Populate the new packets with payload data. - // |payload_ptr| now points at the first payload byte. - PacketList::iterator new_it; - for (new_it = new_packets.begin(); new_it != new_packets.end(); ++new_it) { - int payload_length = (*new_it)->payload_length; - if (payload_ptr + payload_length > - red_packet->payload + red_packet->payload_length) { - // The block lengths in the RED headers do not match the overall packet - // length. Something is corrupt. Discard this and the remaining - // payloads from this packet. - while (new_it != new_packets.end()) { - // Payload should not have been allocated yet. - assert(!(*new_it)->payload); - delete (*new_it); - new_it = new_packets.erase(new_it); - } - ret = kRedLengthMismatch; - break; - } - (*new_it)->payload = new uint8_t[payload_length]; - memcpy((*new_it)->payload, payload_ptr, payload_length); - payload_ptr += payload_length; - } - // Reverse the order of the new packets, so that the primary payload is - // always first. - new_packets.reverse(); - // Insert new packets into original list, before the element pointed to by - // iterator |it|. - packet_list->splice(it, new_packets, new_packets.begin(), - new_packets.end()); - // Delete old packet payload. - delete [] (*it)->payload; - delete (*it); - // Remove |it| from the packet list. This operation effectively moves the - // iterator |it| to the next packet in the list. Thus, we do not have to - // increment it manually. - it = packet_list->erase(it); - } - return ret; -} - -int PayloadSplitter::CheckRedPayloads(PacketList* packet_list, - const DecoderDatabase& decoder_database) { - PacketList::iterator it = packet_list->begin(); - int main_payload_type = -1; - int num_deleted_packets = 0; - while (it != packet_list->end()) { - uint8_t this_payload_type = (*it)->header.payloadType; - if (!decoder_database.IsDtmf(this_payload_type) && - !decoder_database.IsComfortNoise(this_payload_type)) { - if (main_payload_type == -1) { - // This is the first packet in the list which is non-DTMF non-CNG. - main_payload_type = this_payload_type; - } else { - if (this_payload_type != main_payload_type) { - // We do not allow redundant payloads of a different type. - // Discard this payload. - delete [] (*it)->payload; - delete (*it); - // Remove |it| from the packet list. This operation effectively - // moves the iterator |it| to the next packet in the list. Thus, we - // do not have to increment it manually. - it = packet_list->erase(it); - ++num_deleted_packets; - continue; - } - } - } - ++it; - } - return num_deleted_packets; -} - -int PayloadSplitter::SplitAudio(PacketList* packet_list, - const DecoderDatabase& decoder_database) { - PacketList::iterator it = packet_list->begin(); - // Iterate through all packets in |packet_list|. - while (it != packet_list->end()) { - Packet* packet = (*it); // Just to make the notation more intuitive. - // Get codec type for this payload. - const DecoderDatabase::DecoderInfo* info = - decoder_database.GetDecoderInfo(packet->header.payloadType); - if (!info) { - return kUnknownPayloadType; - } - // No splitting for a sync-packet. - if (packet->sync_packet) { - ++it; - continue; - } - PacketList new_packets; - switch (info->codec_type) { - case kDecoderPCMu: - case kDecoderPCMa: { - // 8 bytes per ms; 8 timestamps per ms. - SplitBySamples(packet, 8, 8, &new_packets); - break; - } - case kDecoderPCMu_2ch: - case kDecoderPCMa_2ch: { - // 2 * 8 bytes per ms; 8 timestamps per ms. - SplitBySamples(packet, 2 * 8, 8, &new_packets); - break; - } - case kDecoderG722: { - // 8 bytes per ms; 16 timestamps per ms. - SplitBySamples(packet, 8, 16, &new_packets); - break; - } - case kDecoderPCM16B: { - // 16 bytes per ms; 8 timestamps per ms. - SplitBySamples(packet, 16, 8, &new_packets); - break; - } - case kDecoderPCM16Bwb: { - // 32 bytes per ms; 16 timestamps per ms. - SplitBySamples(packet, 32, 16, &new_packets); - break; - } - case kDecoderPCM16Bswb32kHz: { - // 64 bytes per ms; 32 timestamps per ms. - SplitBySamples(packet, 64, 32, &new_packets); - break; - } - case kDecoderPCM16Bswb48kHz: { - // 96 bytes per ms; 48 timestamps per ms. - SplitBySamples(packet, 96, 48, &new_packets); - break; - } - case kDecoderPCM16B_2ch: { - // 2 * 16 bytes per ms; 8 timestamps per ms. - SplitBySamples(packet, 2 * 16, 8, &new_packets); - break; - } - case kDecoderPCM16Bwb_2ch: { - // 2 * 32 bytes per ms; 16 timestamps per ms. - SplitBySamples(packet, 2 * 32, 16, &new_packets); - break; - } - case kDecoderPCM16Bswb32kHz_2ch: { - // 2 * 64 bytes per ms; 32 timestamps per ms. - SplitBySamples(packet, 2 * 64, 32, &new_packets); - break; - } - case kDecoderPCM16Bswb48kHz_2ch: { - // 2 * 96 bytes per ms; 48 timestamps per ms. - SplitBySamples(packet, 2 * 96, 48, &new_packets); - break; - } - case kDecoderPCM16B_5ch: { - // 5 * 16 bytes per ms; 8 timestamps per ms. - SplitBySamples(packet, 5 * 16, 8, &new_packets); - break; - } - case kDecoderILBC: { - int bytes_per_frame; - int timestamps_per_frame; - if (packet->payload_length >= 950) { - return kTooLargePayload; - } else if (packet->payload_length % 38 == 0) { - // 20 ms frames. - bytes_per_frame = 38; - timestamps_per_frame = 160; - } else if (packet->payload_length % 50 == 0) { - // 30 ms frames. - bytes_per_frame = 50; - timestamps_per_frame = 240; - } else { - return kFrameSplitError; - } - int ret = SplitByFrames(packet, bytes_per_frame, timestamps_per_frame, - &new_packets); - if (ret < 0) { - return ret; - } else if (ret == kNoSplit) { - // Do not split at all. Simply advance to the next packet in the list. - ++it; - // We do not have any new packets to insert, and should not delete the - // old one. Skip the code after the switch case, and jump straight to - // the next packet in the while loop. - continue; - } - break; - } - default: { - // Do not split at all. Simply advance to the next packet in the list. - ++it; - // We do not have any new packets to insert, and should not delete the - // old one. Skip the code after the switch case, and jump straight to - // the next packet in the while loop. - continue; - } - } - // Insert new packets into original list, before the element pointed to by - // iterator |it|. - packet_list->splice(it, new_packets, new_packets.begin(), - new_packets.end()); - // Delete old packet payload. - delete [] (*it)->payload; - delete (*it); - // Remove |it| from the packet list. This operation effectively moves the - // iterator |it| to the next packet in the list. Thus, we do not have to - // increment it manually. - it = packet_list->erase(it); - } - return 0; -} - -void PayloadSplitter::SplitBySamples(const Packet* packet, - int bytes_per_ms, - int timestamps_per_ms, - PacketList* new_packets) { - assert(packet); - assert(new_packets); - - int split_size_bytes = packet->payload_length; - - // Find a "chunk size" >= 20 ms and < 40 ms. - int min_chunk_size = bytes_per_ms * 20; - // Reduce the split size by half as long as |split_size_bytes| is at least - // twice the minimum chunk size (so that the resulting size is at least as - // large as the minimum chunk size). - while (split_size_bytes >= 2 * min_chunk_size) { - split_size_bytes >>= 1; - } - int timestamps_per_chunk = - split_size_bytes * timestamps_per_ms / bytes_per_ms; - uint32_t timestamp = packet->header.timestamp; - - uint8_t* payload_ptr = packet->payload; - int len = packet->payload_length; - while (len >= (2 * split_size_bytes)) { - Packet* new_packet = new Packet; - new_packet->payload_length = split_size_bytes; - new_packet->header = packet->header; - new_packet->header.timestamp = timestamp; - timestamp += timestamps_per_chunk; - new_packet->primary = packet->primary; - new_packet->payload = new uint8_t[split_size_bytes]; - memcpy(new_packet->payload, payload_ptr, split_size_bytes); - payload_ptr += split_size_bytes; - new_packets->push_back(new_packet); - len -= split_size_bytes; - } - - if (len > 0) { - Packet* new_packet = new Packet; - new_packet->payload_length = len; - new_packet->header = packet->header; - new_packet->header.timestamp = timestamp; - new_packet->primary = packet->primary; - new_packet->payload = new uint8_t[len]; - memcpy(new_packet->payload, payload_ptr, len); - new_packets->push_back(new_packet); - } -} - -int PayloadSplitter::SplitByFrames(const Packet* packet, - int bytes_per_frame, - int timestamps_per_frame, - PacketList* new_packets) { - if (packet->payload_length % bytes_per_frame != 0) { - return kFrameSplitError; - } - - int num_frames = packet->payload_length / bytes_per_frame; - if (num_frames == 1) { - // Special case. Do not split the payload. - return kNoSplit; - } - - uint32_t timestamp = packet->header.timestamp; - uint8_t* payload_ptr = packet->payload; - int len = packet->payload_length; - while (len > 0) { - assert(len >= bytes_per_frame); - Packet* new_packet = new Packet; - new_packet->payload_length = bytes_per_frame; - new_packet->header = packet->header; - new_packet->header.timestamp = timestamp; - timestamp += timestamps_per_frame; - new_packet->primary = packet->primary; - new_packet->payload = new uint8_t[bytes_per_frame]; - memcpy(new_packet->payload, payload_ptr, bytes_per_frame); - payload_ptr += bytes_per_frame; - new_packets->push_back(new_packet); - len -= bytes_per_frame; - } - return kOK; -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/payload_splitter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/payload_splitter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/payload_splitter.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/payload_splitter.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_PAYLOAD_SPLITTER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_PAYLOAD_SPLITTER_H_ - -#include "webrtc/modules/audio_coding/neteq4/packet.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" - -namespace webrtc { - -// Forward declarations. -class DecoderDatabase; - -// This class handles splitting of payloads into smaller parts. -// The class does not have any member variables, and the methods could have -// been made static. The reason for not making them static is testability. -// With this design, the splitting functionality can be mocked during testing -// of the NetEqImpl class. -class PayloadSplitter { - public: - enum SplitterReturnCodes { - kOK = 0, - kNoSplit = 1, - kTooLargePayload = -1, - kFrameSplitError = -2, - kUnknownPayloadType = -3, - kRedLengthMismatch = -4 - }; - - PayloadSplitter() {} - - virtual ~PayloadSplitter() {} - - // Splits each packet in |packet_list| into its separate RED payloads. Each - // RED payload is packetized into a Packet. The original elements in - // |packet_list| are properly deleted, and replaced by the new packets. - // Note that all packets in |packet_list| must be RED payloads, i.e., have - // RED headers according to RFC 2198 at the very beginning of the payload. - // Returns kOK or an error. - virtual int SplitRed(PacketList* packet_list); - - // Checks all packets in |packet_list|. Packets that are DTMF events or - // comfort noise payloads are kept. Except that, only one single payload type - // is accepted. Any packet with another payload type is discarded. - virtual int CheckRedPayloads(PacketList* packet_list, - const DecoderDatabase& decoder_database); - - // Iterates through |packet_list| and, if possible, splits each audio payload - // into suitable size chunks. The result is written back to |packet_list| as - // new packets. The decoder database is needed to get information about which - // payload type each packet contains. - virtual int SplitAudio(PacketList* packet_list, - const DecoderDatabase& decoder_database); - - private: - // Splits the payload in |packet|. The payload is assumed to be from a - // sample-based codec. - virtual void SplitBySamples(const Packet* packet, - int bytes_per_ms, - int timestamps_per_ms, - PacketList* new_packets); - - // Splits the payload in |packet|. The payload will be split into chunks of - // size |bytes_per_frame|, corresponding to a |timestamps_per_frame| - // RTP timestamps. - virtual int SplitByFrames(const Packet* packet, - int bytes_per_frame, - int timestamps_per_frame, - PacketList* new_packets); - - DISALLOW_COPY_AND_ASSIGN(PayloadSplitter); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_PAYLOAD_SPLITTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/payload_splitter_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/payload_splitter_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/payload_splitter_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/payload_splitter_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,694 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Unit tests for PayloadSplitter class. - -#include "webrtc/modules/audio_coding/neteq4/payload_splitter.h" - -#include - -#include // pair - -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq4/mock/mock_decoder_database.h" -#include "webrtc/modules/audio_coding/neteq4/packet.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" - -using ::testing::Return; -using ::testing::ReturnNull; - -namespace webrtc { - -static const int kRedPayloadType = 100; -static const int kPayloadLength = 10; -static const int kRedHeaderLength = 4; // 4 bytes RED header. -static const uint16_t kSequenceNumber = 0; -static const uint32_t kBaseTimestamp = 0x12345678; - -// RED headers (according to RFC 2198): -// -// 0 1 2 3 -// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// |F| block PT | timestamp offset | block length | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// -// Last RED header: -// 0 1 2 3 4 5 6 7 -// +-+-+-+-+-+-+-+-+ -// |0| Block PT | -// +-+-+-+-+-+-+-+-+ - -// Creates a RED packet, with |num_payloads| payloads, with payload types given -// by the values in array |payload_types| (which must be of length -// |num_payloads|). Each redundant payload is |timestamp_offset| samples -// "behind" the the previous payload. -Packet* CreateRedPayload(int num_payloads, - uint8_t* payload_types, - int timestamp_offset) { - Packet* packet = new Packet; - packet->header.payloadType = kRedPayloadType; - packet->header.timestamp = kBaseTimestamp; - packet->header.sequenceNumber = kSequenceNumber; - packet->payload_length = (kPayloadLength + 1) + - (num_payloads - 1) * (kPayloadLength + kRedHeaderLength); - uint8_t* payload = new uint8_t[packet->payload_length]; - uint8_t* payload_ptr = payload; - for (int i = 0; i < num_payloads; ++i) { - // Write the RED headers. - if (i == num_payloads - 1) { - // Special case for last payload. - *payload_ptr = payload_types[i] & 0x7F; // F = 0; - ++payload_ptr; - break; - } - *payload_ptr = payload_types[i] & 0x7F; - // Not the last block; set F = 1. - *payload_ptr |= 0x80; - ++payload_ptr; - int this_offset = (num_payloads - i - 1) * timestamp_offset; - *payload_ptr = this_offset >> 6; - ++payload_ptr; - assert(kPayloadLength <= 1023); // Max length described by 10 bits. - *payload_ptr = ((this_offset & 0x3F) << 2) | (kPayloadLength >> 8); - ++payload_ptr; - *payload_ptr = kPayloadLength & 0xFF; - ++payload_ptr; - } - for (int i = 0; i < num_payloads; ++i) { - // Write |i| to all bytes in each payload. - memset(payload_ptr, i, kPayloadLength); - payload_ptr += kPayloadLength; - } - packet->payload = payload; - return packet; -} - -// Create a packet with all payload bytes set to |payload_value|. -Packet* CreatePacket(uint8_t payload_type, int payload_length, - uint8_t payload_value) { - Packet* packet = new Packet; - packet->header.payloadType = payload_type; - packet->header.timestamp = kBaseTimestamp; - packet->header.sequenceNumber = kSequenceNumber; - packet->payload_length = payload_length; - uint8_t* payload = new uint8_t[packet->payload_length]; - memset(payload, payload_value, payload_length); - packet->payload = payload; - return packet; -} - -// Checks that |packet| has the attributes given in the remaining parameters. -void VerifyPacket(const Packet* packet, - int payload_length, - uint8_t payload_type, - uint16_t sequence_number, - uint32_t timestamp, - uint8_t payload_value, - bool primary = true) { - EXPECT_EQ(payload_length, packet->payload_length); - EXPECT_EQ(payload_type, packet->header.payloadType); - EXPECT_EQ(sequence_number, packet->header.sequenceNumber); - EXPECT_EQ(timestamp, packet->header.timestamp); - EXPECT_EQ(primary, packet->primary); - ASSERT_FALSE(packet->payload == NULL); - for (int i = 0; i < packet->payload_length; ++i) { - EXPECT_EQ(payload_value, packet->payload[i]); - } -} - -// Start of test definitions. - -TEST(PayloadSplitter, CreateAndDestroy) { - PayloadSplitter* splitter = new PayloadSplitter; - delete splitter; -} - -// Packet A is split into A1 and A2. -TEST(RedPayloadSplitter, OnePacketTwoPayloads) { - uint8_t payload_types[] = {0, 0}; - const int kTimestampOffset = 160; - Packet* packet = CreateRedPayload(2, payload_types, kTimestampOffset); - PacketList packet_list; - packet_list.push_back(packet); - PayloadSplitter splitter; - EXPECT_EQ(PayloadSplitter::kOK, splitter.SplitRed(&packet_list)); - ASSERT_EQ(2u, packet_list.size()); - // Check first packet. The first in list should always be the primary payload. - packet = packet_list.front(); - VerifyPacket(packet, kPayloadLength, payload_types[1], kSequenceNumber, - kBaseTimestamp, 1, true); - delete [] packet->payload; - delete packet; - packet_list.pop_front(); - // Check second packet. - packet = packet_list.front(); - VerifyPacket(packet, kPayloadLength, payload_types[0], kSequenceNumber, - kBaseTimestamp - kTimestampOffset, 0, false); - delete [] packet->payload; - delete packet; -} - -// Packets A and B are not split at all. Only the RED header in each packet is -// removed. -TEST(RedPayloadSplitter, TwoPacketsOnePayload) { - uint8_t payload_types[] = {0}; - const int kTimestampOffset = 160; - // Create first packet, with a single RED payload. - Packet* packet = CreateRedPayload(1, payload_types, kTimestampOffset); - PacketList packet_list; - packet_list.push_back(packet); - // Create second packet, with a single RED payload. - packet = CreateRedPayload(1, payload_types, kTimestampOffset); - // Manually change timestamp and sequence number of second packet. - packet->header.timestamp += kTimestampOffset; - packet->header.sequenceNumber++; - packet_list.push_back(packet); - PayloadSplitter splitter; - EXPECT_EQ(PayloadSplitter::kOK, splitter.SplitRed(&packet_list)); - ASSERT_EQ(2u, packet_list.size()); - // Check first packet. - packet = packet_list.front(); - VerifyPacket(packet, kPayloadLength, payload_types[0], kSequenceNumber, - kBaseTimestamp, 0, true); - delete [] packet->payload; - delete packet; - packet_list.pop_front(); - // Check second packet. - packet = packet_list.front(); - VerifyPacket(packet, kPayloadLength, payload_types[0], kSequenceNumber + 1, - kBaseTimestamp + kTimestampOffset, 0, true); - delete [] packet->payload; - delete packet; -} - -// Packets A and B are split into packets A1, A2, A3, B1, B2, B3, with -// attributes as follows: -// -// A1* A2 A3 B1* B2 B3 -// Payload type 0 1 2 0 1 2 -// Timestamp b b-o b-2o b+o b b-o -// Sequence number 0 0 0 1 1 1 -// -// b = kBaseTimestamp, o = kTimestampOffset, * = primary. -TEST(RedPayloadSplitter, TwoPacketsThreePayloads) { - uint8_t payload_types[] = {2, 1, 0}; // Primary is the last one. - const int kTimestampOffset = 160; - // Create first packet, with 3 RED payloads. - Packet* packet = CreateRedPayload(3, payload_types, kTimestampOffset); - PacketList packet_list; - packet_list.push_back(packet); - // Create first packet, with 3 RED payloads. - packet = CreateRedPayload(3, payload_types, kTimestampOffset); - // Manually change timestamp and sequence number of second packet. - packet->header.timestamp += kTimestampOffset; - packet->header.sequenceNumber++; - packet_list.push_back(packet); - PayloadSplitter splitter; - EXPECT_EQ(PayloadSplitter::kOK, splitter.SplitRed(&packet_list)); - ASSERT_EQ(6u, packet_list.size()); - // Check first packet, A1. - packet = packet_list.front(); - VerifyPacket(packet, kPayloadLength, payload_types[2], kSequenceNumber, - kBaseTimestamp, 2, true); - delete [] packet->payload; - delete packet; - packet_list.pop_front(); - // Check second packet, A2. - packet = packet_list.front(); - VerifyPacket(packet, kPayloadLength, payload_types[1], kSequenceNumber, - kBaseTimestamp - kTimestampOffset, 1, false); - delete [] packet->payload; - delete packet; - packet_list.pop_front(); - // Check third packet, A3. - packet = packet_list.front(); - VerifyPacket(packet, kPayloadLength, payload_types[0], kSequenceNumber, - kBaseTimestamp - 2 * kTimestampOffset, 0, false); - delete [] packet->payload; - delete packet; - packet_list.pop_front(); - // Check fourth packet, B1. - packet = packet_list.front(); - VerifyPacket(packet, kPayloadLength, payload_types[2], kSequenceNumber + 1, - kBaseTimestamp + kTimestampOffset, 2, true); - delete [] packet->payload; - delete packet; - packet_list.pop_front(); - // Check fifth packet, B2. - packet = packet_list.front(); - VerifyPacket(packet, kPayloadLength, payload_types[1], kSequenceNumber + 1, - kBaseTimestamp, 1, false); - delete [] packet->payload; - delete packet; - packet_list.pop_front(); - // Check sixth packet, B3. - packet = packet_list.front(); - VerifyPacket(packet, kPayloadLength, payload_types[0], kSequenceNumber + 1, - kBaseTimestamp - kTimestampOffset, 0, false); - delete [] packet->payload; - delete packet; -} - -// Creates a list with 4 packets with these payload types: -// 0 = CNGnb -// 1 = PCMu -// 2 = DTMF (AVT) -// 3 = iLBC -// We expect the method CheckRedPayloads to discard the iLBC packet, since it -// is a non-CNG, non-DTMF payload of another type than the first speech payload -// found in the list (which is PCMu). -TEST(RedPayloadSplitter, CheckRedPayloads) { - PacketList packet_list; - for (int i = 0; i <= 3; ++i) { - // Create packet with payload type |i|, payload length 10 bytes, all 0. - Packet* packet = CreatePacket(i, 10, 0); - packet_list.push_back(packet); - } - - // Use a real DecoderDatabase object here instead of a mock, since it is - // easier to just register the payload types and let the actual implementation - // do its job. - DecoderDatabase decoder_database; - decoder_database.RegisterPayload(0, kDecoderCNGnb); - decoder_database.RegisterPayload(1, kDecoderPCMu); - decoder_database.RegisterPayload(2, kDecoderAVT); - decoder_database.RegisterPayload(3, kDecoderILBC); - - PayloadSplitter splitter; - splitter.CheckRedPayloads(&packet_list, decoder_database); - - ASSERT_EQ(3u, packet_list.size()); // Should have dropped the last packet. - // Verify packets. The loop verifies that payload types 0, 1, and 2 are in the - // list. - for (int i = 0; i <= 2; ++i) { - Packet* packet = packet_list.front(); - VerifyPacket(packet, 10, i, kSequenceNumber, kBaseTimestamp, 0, true); - delete [] packet->payload; - delete packet; - packet_list.pop_front(); - } - EXPECT_TRUE(packet_list.empty()); -} - -// Packet A is split into A1, A2 and A3. But the length parameter is off, so -// the last payloads should be discarded. -TEST(RedPayloadSplitter, WrongPayloadLength) { - uint8_t payload_types[] = {0, 0, 0}; - const int kTimestampOffset = 160; - Packet* packet = CreateRedPayload(3, payload_types, kTimestampOffset); - // Manually tamper with the payload length of the packet. - // This is one byte too short for the second payload (out of three). - // We expect only the first payload to be returned. - packet->payload_length -= kPayloadLength + 1; - PacketList packet_list; - packet_list.push_back(packet); - PayloadSplitter splitter; - EXPECT_EQ(PayloadSplitter::kRedLengthMismatch, - splitter.SplitRed(&packet_list)); - ASSERT_EQ(1u, packet_list.size()); - // Check first packet. - packet = packet_list.front(); - VerifyPacket(packet, kPayloadLength, payload_types[0], kSequenceNumber, - kBaseTimestamp - 2 * kTimestampOffset, 0, false); - delete [] packet->payload; - delete packet; - packet_list.pop_front(); -} - -// Test that iSAC, iSAC-swb, RED, DTMF, CNG, and "Arbitrary" payloads do not -// get split. -TEST(AudioPayloadSplitter, NonSplittable) { - // Set up packets with different RTP payload types. The actual values do not - // matter, since we are mocking the decoder database anyway. - PacketList packet_list; - for (int i = 0; i < 6; ++i) { - // Let the payload type be |i|, and the payload value 10 * |i|. - packet_list.push_back(CreatePacket(i, kPayloadLength, 10 * i)); - } - - MockDecoderDatabase decoder_database; - // Tell the mock decoder database to return DecoderInfo structs with different - // codec types. - // Use scoped pointers to avoid having to delete them later. - scoped_ptr info0( - new DecoderDatabase::DecoderInfo(kDecoderISAC, 16000, NULL, false)); - EXPECT_CALL(decoder_database, GetDecoderInfo(0)) - .WillRepeatedly(Return(info0.get())); - scoped_ptr info1( - new DecoderDatabase::DecoderInfo(kDecoderISACswb, 32000, NULL, false)); - EXPECT_CALL(decoder_database, GetDecoderInfo(1)) - .WillRepeatedly(Return(info1.get())); - scoped_ptr info2( - new DecoderDatabase::DecoderInfo(kDecoderRED, 8000, NULL, false)); - EXPECT_CALL(decoder_database, GetDecoderInfo(2)) - .WillRepeatedly(Return(info2.get())); - scoped_ptr info3( - new DecoderDatabase::DecoderInfo(kDecoderAVT, 8000, NULL, false)); - EXPECT_CALL(decoder_database, GetDecoderInfo(3)) - .WillRepeatedly(Return(info3.get())); - scoped_ptr info4( - new DecoderDatabase::DecoderInfo(kDecoderCNGnb, 8000, NULL, false)); - EXPECT_CALL(decoder_database, GetDecoderInfo(4)) - .WillRepeatedly(Return(info4.get())); - scoped_ptr info5( - new DecoderDatabase::DecoderInfo(kDecoderArbitrary, 8000, NULL, false)); - EXPECT_CALL(decoder_database, GetDecoderInfo(5)) - .WillRepeatedly(Return(info5.get())); - - PayloadSplitter splitter; - EXPECT_EQ(0, splitter.SplitAudio(&packet_list, decoder_database)); - EXPECT_EQ(6u, packet_list.size()); - - // Check that all payloads are intact. - uint8_t payload_type = 0; - PacketList::iterator it = packet_list.begin(); - while (it != packet_list.end()) { - VerifyPacket((*it), kPayloadLength, payload_type, kSequenceNumber, - kBaseTimestamp, 10 * payload_type); - ++payload_type; - delete [] (*it)->payload; - delete (*it); - it = packet_list.erase(it); - } - - // The destructor is called when decoder_database goes out of scope. - EXPECT_CALL(decoder_database, Die()); -} - -// Test unknown payload type. -TEST(AudioPayloadSplitter, UnknownPayloadType) { - PacketList packet_list; - static const uint8_t kPayloadType = 17; // Just a random number. - int kPayloadLengthBytes = 4711; // Random number. - packet_list.push_back(CreatePacket(kPayloadType, kPayloadLengthBytes, 0)); - - MockDecoderDatabase decoder_database; - // Tell the mock decoder database to return NULL when asked for decoder info. - // This signals that the decoder database does not recognize the payload type. - EXPECT_CALL(decoder_database, GetDecoderInfo(kPayloadType)) - .WillRepeatedly(ReturnNull()); - - PayloadSplitter splitter; - EXPECT_EQ(PayloadSplitter::kUnknownPayloadType, - splitter.SplitAudio(&packet_list, decoder_database)); - EXPECT_EQ(1u, packet_list.size()); - - - // Delete the packets and payloads to avoid having the test leak memory. - PacketList::iterator it = packet_list.begin(); - while (it != packet_list.end()) { - delete [] (*it)->payload; - delete (*it); - it = packet_list.erase(it); - } - - // The destructor is called when decoder_database goes out of scope. - EXPECT_CALL(decoder_database, Die()); -} - -class SplitBySamplesTest : public ::testing::TestWithParam { - protected: - virtual void SetUp() { - decoder_type_ = GetParam(); - switch (decoder_type_) { - case kDecoderPCMu: - case kDecoderPCMa: - bytes_per_ms_ = 8; - samples_per_ms_ = 8; - break; - case kDecoderPCMu_2ch: - case kDecoderPCMa_2ch: - bytes_per_ms_ = 2 * 8; - samples_per_ms_ = 8; - break; - case kDecoderG722: - bytes_per_ms_ = 8; - samples_per_ms_ = 16; - break; - case kDecoderPCM16B: - bytes_per_ms_ = 16; - samples_per_ms_ = 8; - break; - case kDecoderPCM16Bwb: - bytes_per_ms_ = 32; - samples_per_ms_ = 16; - break; - case kDecoderPCM16Bswb32kHz: - bytes_per_ms_ = 64; - samples_per_ms_ = 32; - break; - case kDecoderPCM16Bswb48kHz: - bytes_per_ms_ = 96; - samples_per_ms_ = 48; - break; - case kDecoderPCM16B_2ch: - bytes_per_ms_ = 2 * 16; - samples_per_ms_ = 8; - break; - case kDecoderPCM16Bwb_2ch: - bytes_per_ms_ = 2 * 32; - samples_per_ms_ = 16; - break; - case kDecoderPCM16Bswb32kHz_2ch: - bytes_per_ms_ = 2 * 64; - samples_per_ms_ = 32; - break; - case kDecoderPCM16Bswb48kHz_2ch: - bytes_per_ms_ = 2 * 96; - samples_per_ms_ = 48; - break; - case kDecoderPCM16B_5ch: - bytes_per_ms_ = 5 * 16; - samples_per_ms_ = 8; - break; - default: - assert(false); - break; - } - } - int bytes_per_ms_; - int samples_per_ms_; - NetEqDecoder decoder_type_; -}; - -// Test splitting sample-based payloads. -TEST_P(SplitBySamplesTest, PayloadSizes) { - PacketList packet_list; - static const uint8_t kPayloadType = 17; // Just a random number. - for (int payload_size_ms = 10; payload_size_ms <= 60; payload_size_ms += 10) { - // The payload values are set to be the same as the payload_size, so that - // one can distinguish from which packet the split payloads come from. - int payload_size_bytes = payload_size_ms * bytes_per_ms_; - packet_list.push_back(CreatePacket(kPayloadType, payload_size_bytes, - payload_size_ms)); - } - - MockDecoderDatabase decoder_database; - // Tell the mock decoder database to return DecoderInfo structs with different - // codec types. - // Use scoped pointers to avoid having to delete them later. - // (Sample rate is set to 8000 Hz, but does not matter.) - scoped_ptr info( - new DecoderDatabase::DecoderInfo(decoder_type_, 8000, NULL, false)); - EXPECT_CALL(decoder_database, GetDecoderInfo(kPayloadType)) - .WillRepeatedly(Return(info.get())); - - PayloadSplitter splitter; - EXPECT_EQ(0, splitter.SplitAudio(&packet_list, decoder_database)); - // The payloads are expected to be split as follows: - // 10 ms -> 10 ms - // 20 ms -> 20 ms - // 30 ms -> 30 ms - // 40 ms -> 20 + 20 ms - // 50 ms -> 25 + 25 ms - // 60 ms -> 30 + 30 ms - int expected_size_ms[] = {10, 20, 30, 20, 20, 25, 25, 30, 30}; - int expected_payload_value[] = {10, 20, 30, 40, 40, 50, 50, 60, 60}; - int expected_timestamp_offset_ms[] = {0, 0, 0, 0, 20, 0, 25, 0, 30}; - size_t expected_num_packets = - sizeof(expected_size_ms) / sizeof(expected_size_ms[0]); - EXPECT_EQ(expected_num_packets, packet_list.size()); - - PacketList::iterator it = packet_list.begin(); - int i = 0; - while (it != packet_list.end()) { - int length_bytes = expected_size_ms[i] * bytes_per_ms_; - uint32_t expected_timestamp = kBaseTimestamp + - expected_timestamp_offset_ms[i] * samples_per_ms_; - VerifyPacket((*it), length_bytes, kPayloadType, kSequenceNumber, - expected_timestamp, expected_payload_value[i]); - delete [] (*it)->payload; - delete (*it); - it = packet_list.erase(it); - ++i; - } - - // The destructor is called when decoder_database goes out of scope. - EXPECT_CALL(decoder_database, Die()); -} - -INSTANTIATE_TEST_CASE_P( - PayloadSplitter, SplitBySamplesTest, - ::testing::Values(kDecoderPCMu, kDecoderPCMa, kDecoderPCMu_2ch, - kDecoderPCMa_2ch, kDecoderG722, kDecoderPCM16B, - kDecoderPCM16Bwb, kDecoderPCM16Bswb32kHz, - kDecoderPCM16Bswb48kHz, kDecoderPCM16B_2ch, - kDecoderPCM16Bwb_2ch, kDecoderPCM16Bswb32kHz_2ch, - kDecoderPCM16Bswb48kHz_2ch, kDecoderPCM16B_5ch)); - - -class SplitIlbcTest : public ::testing::TestWithParam > { - protected: - virtual void SetUp() { - const std::pair parameters = GetParam(); - num_frames_ = parameters.first; - frame_length_ms_ = parameters.second; - frame_length_bytes_ = (frame_length_ms_ == 20) ? 38 : 50; - } - size_t num_frames_; - int frame_length_ms_; - int frame_length_bytes_; -}; - -// Test splitting sample-based payloads. -TEST_P(SplitIlbcTest, NumFrames) { - PacketList packet_list; - static const uint8_t kPayloadType = 17; // Just a random number. - const int frame_length_samples = frame_length_ms_ * 8; - int payload_length_bytes = frame_length_bytes_ * num_frames_; - Packet* packet = CreatePacket(kPayloadType, payload_length_bytes, 0); - // Fill payload with increasing integers {0, 1, 2, ...}. - for (int i = 0; i < packet->payload_length; ++i) { - packet->payload[i] = static_cast(i); - } - packet_list.push_back(packet); - - MockDecoderDatabase decoder_database; - // Tell the mock decoder database to return DecoderInfo structs with different - // codec types. - // Use scoped pointers to avoid having to delete them later. - scoped_ptr info( - new DecoderDatabase::DecoderInfo(kDecoderILBC, 8000, NULL, false)); - EXPECT_CALL(decoder_database, GetDecoderInfo(kPayloadType)) - .WillRepeatedly(Return(info.get())); - - PayloadSplitter splitter; - EXPECT_EQ(0, splitter.SplitAudio(&packet_list, decoder_database)); - EXPECT_EQ(num_frames_, packet_list.size()); - - PacketList::iterator it = packet_list.begin(); - int frame_num = 0; - uint8_t payload_value = 0; - while (it != packet_list.end()) { - Packet* packet = (*it); - EXPECT_EQ(kBaseTimestamp + frame_length_samples * frame_num, - packet->header.timestamp); - EXPECT_EQ(frame_length_bytes_, packet->payload_length); - EXPECT_EQ(kPayloadType, packet->header.payloadType); - EXPECT_EQ(kSequenceNumber, packet->header.sequenceNumber); - EXPECT_EQ(true, packet->primary); - ASSERT_FALSE(packet->payload == NULL); - for (int i = 0; i < packet->payload_length; ++i) { - EXPECT_EQ(payload_value, packet->payload[i]); - ++payload_value; - } - delete [] (*it)->payload; - delete (*it); - it = packet_list.erase(it); - ++frame_num; - } - - // The destructor is called when decoder_database goes out of scope. - EXPECT_CALL(decoder_database, Die()); -} - -// Test 1 through 5 frames of 20 and 30 ms size. -// Also test the maximum number of frames in one packet for 20 and 30 ms. -// The maximum is defined by the largest payload length that can be uniquely -// resolved to a frame size of either 38 bytes (20 ms) or 50 bytes (30 ms). -INSTANTIATE_TEST_CASE_P( - PayloadSplitter, SplitIlbcTest, - ::testing::Values(std::pair(1, 20), // 1 frame, 20 ms. - std::pair(2, 20), // 2 frames, 20 ms. - std::pair(3, 20), // And so on. - std::pair(4, 20), - std::pair(5, 20), - std::pair(24, 20), - std::pair(1, 30), - std::pair(2, 30), - std::pair(3, 30), - std::pair(4, 30), - std::pair(5, 30), - std::pair(18, 30))); - -// Test too large payload size. -TEST(IlbcPayloadSplitter, TooLargePayload) { - PacketList packet_list; - static const uint8_t kPayloadType = 17; // Just a random number. - int kPayloadLengthBytes = 950; - Packet* packet = CreatePacket(kPayloadType, kPayloadLengthBytes, 0); - packet_list.push_back(packet); - - MockDecoderDatabase decoder_database; - scoped_ptr info( - new DecoderDatabase::DecoderInfo(kDecoderILBC, 8000, NULL, false)); - EXPECT_CALL(decoder_database, GetDecoderInfo(kPayloadType)) - .WillRepeatedly(Return(info.get())); - - PayloadSplitter splitter; - EXPECT_EQ(PayloadSplitter::kTooLargePayload, - splitter.SplitAudio(&packet_list, decoder_database)); - EXPECT_EQ(1u, packet_list.size()); - - // Delete the packets and payloads to avoid having the test leak memory. - PacketList::iterator it = packet_list.begin(); - while (it != packet_list.end()) { - delete [] (*it)->payload; - delete (*it); - it = packet_list.erase(it); - } - - // The destructor is called when decoder_database goes out of scope. - EXPECT_CALL(decoder_database, Die()); -} - -// Payload not an integer number of frames. -TEST(IlbcPayloadSplitter, UnevenPayload) { - PacketList packet_list; - static const uint8_t kPayloadType = 17; // Just a random number. - int kPayloadLengthBytes = 39; // Not an even number of frames. - Packet* packet = CreatePacket(kPayloadType, kPayloadLengthBytes, 0); - packet_list.push_back(packet); - - MockDecoderDatabase decoder_database; - scoped_ptr info( - new DecoderDatabase::DecoderInfo(kDecoderILBC, 8000, NULL, false)); - EXPECT_CALL(decoder_database, GetDecoderInfo(kPayloadType)) - .WillRepeatedly(Return(info.get())); - - PayloadSplitter splitter; - EXPECT_EQ(PayloadSplitter::kFrameSplitError, - splitter.SplitAudio(&packet_list, decoder_database)); - EXPECT_EQ(1u, packet_list.size()); - - // Delete the packets and payloads to avoid having the test leak memory. - PacketList::iterator it = packet_list.begin(); - while (it != packet_list.end()) { - delete [] (*it)->payload; - delete (*it); - it = packet_list.erase(it); - } - - // The destructor is called when decoder_database goes out of scope. - EXPECT_CALL(decoder_database, Die()); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/post_decode_vad.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/post_decode_vad.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/post_decode_vad.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/post_decode_vad.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/post_decode_vad.h" - -namespace webrtc { - -PostDecodeVad::~PostDecodeVad() { - if (vad_instance_) - WebRtcVad_Free(vad_instance_); -} - -void PostDecodeVad::Enable() { - if (!vad_instance_) { - // Create the instance. - if (WebRtcVad_Create(&vad_instance_) != 0) { - // Failed to create instance. - Disable(); - return; - } - } - Init(); - enabled_ = true; -} - -void PostDecodeVad::Disable() { - enabled_ = false; - running_ = false; -} - -void PostDecodeVad::Init() { - running_ = false; - if (vad_instance_) { - WebRtcVad_Init(vad_instance_); - WebRtcVad_set_mode(vad_instance_, kVadMode); - running_ = true; - } -} - -void PostDecodeVad::Update(int16_t* signal, int length, - AudioDecoder::SpeechType speech_type, - bool sid_frame, - int fs_hz) { - if (!vad_instance_ || !enabled_) { - return; - } - - if (speech_type == AudioDecoder::kComfortNoise || sid_frame || - fs_hz > 16000) { - // TODO(hlundin): Remove restriction on fs_hz. - running_ = false; - active_speech_ = true; - sid_interval_counter_ = 0; - } else if (!running_) { - ++sid_interval_counter_; - } - - if (sid_interval_counter_ >= kVadAutoEnable) { - Init(); - } - - if (length > 0 && running_) { - int vad_sample_index = 0; - active_speech_ = false; - // Loop through frame sizes 30, 20, and 10 ms. - for (int vad_frame_size_ms = 30; vad_frame_size_ms >= 10; - vad_frame_size_ms -= 10) { - int vad_frame_size_samples = vad_frame_size_ms * fs_hz / 1000; - while (length - vad_sample_index >= vad_frame_size_samples) { - int vad_return = WebRtcVad_Process( - vad_instance_, fs_hz, &signal[vad_sample_index], - vad_frame_size_samples); - active_speech_ |= (vad_return == 1); - vad_sample_index += vad_frame_size_samples; - } - } - } -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/post_decode_vad.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/post_decode_vad.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/post_decode_vad.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/post_decode_vad.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_POST_DECODE_VAD_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_POST_DECODE_VAD_H_ - -#include // size_t - -#include "webrtc/common_audio/vad/include/webrtc_vad.h" -#include "webrtc/common_types.h" // NULL -#include "webrtc/modules/audio_coding/neteq4/defines.h" -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" -#include "webrtc/modules/audio_coding/neteq4/packet.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -class PostDecodeVad { - public: - PostDecodeVad() - : enabled_(false), - running_(false), - active_speech_(true), - sid_interval_counter_(0), - vad_instance_(NULL) { - } - - virtual ~PostDecodeVad(); - - // Enables post-decode VAD. - void Enable(); - - // Disables post-decode VAD. - void Disable(); - - // Initializes post-decode VAD. - void Init(); - - // Updates post-decode VAD with the audio data in |signal| having |length| - // samples. The data is of type |speech_type|, at the sample rate |fs_hz|. - void Update(int16_t* signal, int length, - AudioDecoder::SpeechType speech_type, bool sid_frame, int fs_hz); - - // Accessors. - bool enabled() const { return enabled_; } - bool running() const { return running_; } - bool active_speech() const { return active_speech_; } - - private: - static const int kVadMode = 0; // Sets aggressiveness to "Normal". - // Number of Update() calls without CNG/SID before re-enabling VAD. - static const int kVadAutoEnable = 3000; - - bool enabled_; - bool running_; - bool active_speech_; - int sid_interval_counter_; - ::VadInst* vad_instance_; - - DISALLOW_COPY_AND_ASSIGN(PostDecodeVad); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_POST_DECODE_VAD_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/post_decode_vad_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/post_decode_vad_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/post_decode_vad_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/post_decode_vad_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Unit tests for PostDecodeVad class. - -#include "webrtc/modules/audio_coding/neteq4/post_decode_vad.h" - -#include "gtest/gtest.h" - -namespace webrtc { - -TEST(PostDecodeVad, CreateAndDestroy) { - PostDecodeVad vad; -} - -// TODO(hlundin): Write more tests. - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/preemptive_expand.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/preemptive_expand.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/preemptive_expand.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/preemptive_expand.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/preemptive_expand.h" - -#include // min, max - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" - -namespace webrtc { - -PreemptiveExpand::ReturnCodes PreemptiveExpand::Process( - const int16_t* input, - int input_length, - int old_data_length, - AudioMultiVector* output, - int16_t* length_change_samples) { - old_data_length_per_channel_ = old_data_length; - // Input length must be (almost) 30 ms. - // Also, the new part must be at least |overlap_samples_| elements. - static const int k15ms = 120; // 15 ms = 120 samples at 8 kHz sample rate. - if (num_channels_ == 0 || - input_length / num_channels_ < (2 * k15ms - 1) * fs_mult_ || - old_data_length >= input_length / num_channels_ - overlap_samples_) { - // Length of input data too short to do preemptive expand. Simply move all - // data from input to output. - output->PushBackInterleaved(input, input_length); - return kError; - } - return TimeStretch::Process(input, input_length, output, - length_change_samples); -} - -void PreemptiveExpand::SetParametersForPassiveSpeech(size_t len, - int16_t* best_correlation, - int* peak_index) const { - // When the signal does not contain any active speech, the correlation does - // not matter. Simply set it to zero. - *best_correlation = 0; - - // For low energy expansion, the new data can be less than 15 ms, - // but we must ensure that best_correlation is not larger than the length of - // the new data. - // but we must ensure that best_correlation is not larger than the new data. - *peak_index = std::min(*peak_index, - static_cast(len - old_data_length_per_channel_)); -} - -PreemptiveExpand::ReturnCodes PreemptiveExpand::CheckCriteriaAndStretch( - const int16_t *input, size_t input_length, size_t peak_index, - int16_t best_correlation, bool active_speech, - AudioMultiVector* output) const { - // Pre-calculate common multiplication with |fs_mult_|. - // 120 corresponds to 15 ms. - int fs_mult_120 = fs_mult_ * 120; - assert(old_data_length_per_channel_ >= 0); // Make sure it's been set. - // Check for strong correlation (>0.9 in Q14) and at least 15 ms new data, - // or passive speech. - if (((best_correlation > kCorrelationThreshold) && - (old_data_length_per_channel_ <= fs_mult_120)) || - !active_speech) { - // Do accelerate operation by overlap add. - - // Set length of the first part, not to be modified. - size_t unmodified_length = std::max(old_data_length_per_channel_, - fs_mult_120); - // Copy first part, including cross-fade region. - output->PushBackInterleaved( - input, (unmodified_length + peak_index) * num_channels_); - // Copy the last |peak_index| samples up to 15 ms to |temp_vector|. - AudioMultiVector temp_vector(num_channels_); - temp_vector.PushBackInterleaved( - &input[(unmodified_length - peak_index) * num_channels_], - peak_index * num_channels_); - // Cross-fade |temp_vector| onto the end of |output|. - output->CrossFade(temp_vector, peak_index); - // Copy the last unmodified part, 15 ms + pitch period until the end. - output->PushBackInterleaved( - &input[unmodified_length * num_channels_], - input_length - unmodified_length * num_channels_); - - if (active_speech) { - return kSuccess; - } else { - return kSuccessLowEnergy; - } - } else { - // Accelerate not allowed. Simply move all data from decoded to outData. - output->PushBackInterleaved(input, input_length); - return kNoStretch; - } -} - -PreemptiveExpand* PreemptiveExpandFactory::Create( - int sample_rate_hz, - size_t num_channels, - const BackgroundNoise& background_noise) const { - return new PreemptiveExpand(sample_rate_hz, num_channels, background_noise); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/preemptive_expand.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/preemptive_expand.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/preemptive_expand.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/preemptive_expand.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_PREEMPTIVE_EXPAND_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_PREEMPTIVE_EXPAND_H_ - -#include - -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" -#include "webrtc/modules/audio_coding/neteq4/time_stretch.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Forward declarations. -class BackgroundNoise; - -// This class implements the PreemptiveExpand operation. Most of the work is -// done in the base class TimeStretch, which is shared with the Accelerate -// operation. In the PreemptiveExpand class, the operations that are specific to -// PreemptiveExpand are implemented. -class PreemptiveExpand : public TimeStretch { - public: - PreemptiveExpand(int sample_rate_hz, size_t num_channels, - const BackgroundNoise& background_noise) - : TimeStretch(sample_rate_hz, num_channels, background_noise), - old_data_length_per_channel_(-1), - overlap_samples_(5 * sample_rate_hz / 8000) { - } - - virtual ~PreemptiveExpand() {} - - // This method performs the actual PreemptiveExpand operation. The samples are - // read from |input|, of length |input_length| elements, and are written to - // |output|. The number of samples added through time-stretching is - // is provided in the output |length_change_samples|. The method returns - // the outcome of the operation as an enumerator value. - ReturnCodes Process(const int16_t *pw16_decoded, - int len, - int old_data_len, - AudioMultiVector* output, - int16_t* length_change_samples); - - protected: - // Sets the parameters |best_correlation| and |peak_index| to suitable - // values when the signal contains no active speech. - virtual void SetParametersForPassiveSpeech(size_t len, - int16_t* w16_bestCorr, - int* w16_bestIndex) const; - - // Checks the criteria for performing the time-stretching operation and, - // if possible, performs the time-stretching. - virtual ReturnCodes CheckCriteriaAndStretch( - const int16_t *pw16_decoded, size_t len, size_t w16_bestIndex, - int16_t w16_bestCorr, bool w16_VAD, - AudioMultiVector* output) const; - - private: - int old_data_length_per_channel_; - int overlap_samples_; - - DISALLOW_COPY_AND_ASSIGN(PreemptiveExpand); -}; - -struct PreemptiveExpandFactory { - PreemptiveExpandFactory() {} - virtual ~PreemptiveExpandFactory() {} - - virtual PreemptiveExpand* Create( - int sample_rate_hz, - size_t num_channels, - const BackgroundNoise& background_noise) const; -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_PREEMPTIVE_EXPAND_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/random_vector.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/random_vector.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/random_vector.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/random_vector.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/random_vector.h" - -namespace webrtc { - -const int16_t RandomVector::kRandomTable[RandomVector::kRandomTableSize] = { - 2680, 5532, 441, 5520, 16170, -5146, -1024, -8733, 3115, 9598, -10380, - -4959, -1280, -21716, 7133, -1522, 13458, -3902, 2789, -675, 3441, 5016, - -13599, -4003, -2739, 3922, -7209, 13352, -11617, -7241, 12905, -2314, 5426, - 10121, -9702, 11207, -13542, 1373, 816, -5934, -12504, 4798, 1811, 4112, - -613, 201, -10367, -2960, -2419, 3442, 4299, -6116, -6092, 1552, -1650, - -480, -1237, 18720, -11858, -8303, -8212, 865, -2890, -16968, 12052, -5845, - -5912, 9777, -5665, -6294, 5426, -4737, -6335, 1652, 761, 3832, 641, -8552, - -9084, -5753, 8146, 12156, -4915, 15086, -1231, -1869, 11749, -9319, -6403, - 11407, 6232, -1683, 24340, -11166, 4017, -10448, 3153, -2936, 6212, 2891, - -866, -404, -4807, -2324, -1917, -2388, -6470, -3895, -10300, 5323, -5403, - 2205, 4640, 7022, -21186, -6244, -882, -10031, -3395, -12885, 7155, -5339, - 5079, -2645, -9515, 6622, 14651, 15852, 359, 122, 8246, -3502, -6696, -3679, - -13535, -1409, -704, -7403, -4007, 1798, 279, -420, -12796, -14219, 1141, - 3359, 11434, 7049, -6684, -7473, 14283, -4115, -9123, -8969, 4152, 4117, - 13792, 5742, 16168, 8661, -1609, -6095, 1881, 14380, -5588, 6758, -6425, - -22969, -7269, 7031, 1119, -1611, -5850, -11281, 3559, -8952, -10146, -4667, - -16251, -1538, 2062, -1012, -13073, 227, -3142, -5265, 20, 5770, -7559, - 4740, -4819, 992, -8208, -7130, -4652, 6725, 7369, -1036, 13144, -1588, - -5304, -2344, -449, -5705, -8894, 5205, -17904, -11188, -1022, 4852, 10101, - -5255, -4200, -752, 7941, -1543, 5959, 14719, 13346, 17045, -15605, -1678, - -1600, -9230, 68, 23348, 1172, 7750, 11212, -18227, 9956, 4161, 883, 3947, - 4341, 1014, -4889, -2603, 1246, -5630, -3596, -870, -1298, 2784, -3317, - -6612, -20541, 4166, 4181, -8625, 3562, 12890, 4761, 3205, -12259, -8579 }; - -void RandomVector::Reset() { - seed_ = 777; - seed_increment_ = 1; -} - -void RandomVector::Generate(size_t length, int16_t* output) { - for (size_t i = 0; i < length; i++) { - seed_ += seed_increment_; - size_t position = seed_ & (kRandomTableSize - 1); - output[i] = kRandomTable[position]; - } -} - -void RandomVector::IncreaseSeedIncrement(int16_t increase_by) { - seed_increment_+= increase_by; - seed_increment_ &= kRandomTableSize - 1; -} -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/random_vector.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/random_vector.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/random_vector.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/random_vector.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_RANDOM_VECTOR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_RANDOM_VECTOR_H_ - -#include // size_t - -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// This class generates pseudo-random samples. -class RandomVector { - public: - static const int kRandomTableSize = 256; - static const int16_t kRandomTable[kRandomTableSize]; - - RandomVector() - : seed_(777), - seed_increment_(1) { - } - - void Reset(); - - void Generate(size_t length, int16_t* output); - - void IncreaseSeedIncrement(int16_t increase_by); - - // Accessors and mutators. - int16_t seed_increment() { return seed_increment_; } - void set_seed_increment(int16_t value) { seed_increment_ = value; } - - private: - uint32_t seed_; - int16_t seed_increment_; - - DISALLOW_COPY_AND_ASSIGN(RandomVector); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_RANDOM_VECTOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/random_vector_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/random_vector_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/random_vector_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/random_vector_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Unit tests for RandomVector class. - -#include "webrtc/modules/audio_coding/neteq4/random_vector.h" - -#include "gtest/gtest.h" - -namespace webrtc { - -TEST(RandomVector, CreateAndDestroy) { - RandomVector random_vector; -} - -// TODO(hlundin): Write more tests. - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/rtcp.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/rtcp.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/rtcp.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/rtcp.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/rtcp.h" - -#include - -#include - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/interface/module_common_types.h" - -namespace webrtc { - -void Rtcp::Init(uint16_t start_sequence_number) { - cycles_ = 0; - max_seq_no_ = start_sequence_number; - base_seq_no_ = start_sequence_number; - received_packets_ = 0; - received_packets_prior_ = 0; - expected_prior_ = 0; - jitter_ = 0; - transit_ = 0; -} - -void Rtcp::Update(const RTPHeader& rtp_header, uint32_t receive_timestamp) { - // Update number of received packets, and largest packet number received. - received_packets_++; - int16_t sn_diff = rtp_header.sequenceNumber - max_seq_no_; - if (sn_diff >= 0) { - if (rtp_header.sequenceNumber < max_seq_no_) { - // Wrap-around detected. - cycles_++; - } - max_seq_no_ = rtp_header.sequenceNumber; - } - - // Calculate jitter according to RFC 3550, and update previous timestamps. - // Note that the value in |jitter_| is in Q4. - if (received_packets_ > 1) { - int32_t ts_diff = receive_timestamp - (rtp_header.timestamp - transit_); - ts_diff = WEBRTC_SPL_ABS_W32(ts_diff); - int32_t jitter_diff = (ts_diff << 4) - jitter_; - // Calculate 15 * jitter_ / 16 + jitter_diff / 16 (with proper rounding). - jitter_ = jitter_ + ((jitter_diff + 8) >> 4); - } - transit_ = rtp_header.timestamp - receive_timestamp; -} - -void Rtcp::GetStatistics(bool no_reset, RtcpStatistics* stats) { - // Extended highest sequence number received. - stats->extended_max_sequence_number = - (static_cast(cycles_) << 16) + max_seq_no_; - - // Calculate expected number of packets and compare it with the number of - // packets that were actually received. The cumulative number of lost packets - // can be extracted. - uint32_t expected_packets = - stats->extended_max_sequence_number - base_seq_no_ + 1; - if (received_packets_ == 0) { - // No packets received, assume none lost. - stats->cumulative_lost = 0; - } else if (expected_packets > received_packets_) { - stats->cumulative_lost = expected_packets - received_packets_; - if (stats->cumulative_lost > 0xFFFFFF) { - stats->cumulative_lost = 0xFFFFFF; - } - } else { - stats->cumulative_lost = 0; - } - - // Fraction lost since last report. - uint32_t expected_since_last = expected_packets - expected_prior_; - uint32_t received_since_last = received_packets_ - received_packets_prior_; - if (!no_reset) { - expected_prior_ = expected_packets; - received_packets_prior_ = received_packets_; - } - int32_t lost = expected_since_last - received_since_last; - if (expected_since_last == 0 || lost <= 0 || received_packets_ == 0) { - stats->fraction_lost = 0; - } else { - stats->fraction_lost = std::min(0xFFU, (lost << 8) / expected_since_last); - } - - stats->jitter = jitter_ >> 4; // Scaling from Q4. -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/rtcp.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/rtcp.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/rtcp.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/rtcp.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_RTCP_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_RTCP_H_ - -#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Forward declaration. -struct RTPHeader; - -class Rtcp { - public: - Rtcp() { - Init(0); - } - - ~Rtcp() {} - - // Resets the RTCP statistics, and sets the first received sequence number. - void Init(uint16_t start_sequence_number); - - // Updates the RTCP statistics with a new received packet. - void Update(const RTPHeader& rtp_header, uint32_t receive_timestamp); - - // Returns the current RTCP statistics. If |no_reset| is true, the statistics - // are not reset, otherwise they are. - void GetStatistics(bool no_reset, RtcpStatistics* stats); - - private: - uint16_t cycles_; // The number of wrap-arounds for the sequence number. - uint16_t max_seq_no_; // The maximum sequence number received. Starts over - // from 0 after wrap-around. - uint16_t base_seq_no_; // The sequence number of the first received packet. - uint32_t received_packets_; // The number of packets that have been received. - uint32_t received_packets_prior_; // Number of packets received when last - // report was generated. - uint32_t expected_prior_; // Expected number of packets, at the time of the - // last report. - uint32_t jitter_; // Current jitter value. - int32_t transit_; // Clock difference for previous packet. - - DISALLOW_COPY_AND_ASSIGN(Rtcp); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_RTCP_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/statistics_calculator.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/statistics_calculator.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/statistics_calculator.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/statistics_calculator.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/statistics_calculator.h" - -#include -#include // memset - -#include "webrtc/modules/audio_coding/neteq4/decision_logic.h" -#include "webrtc/modules/audio_coding/neteq4/delay_manager.h" - -namespace webrtc { - -StatisticsCalculator::StatisticsCalculator() - : preemptive_samples_(0), - accelerate_samples_(0), - added_zero_samples_(0), - expanded_voice_samples_(0), - expanded_noise_samples_(0), - discarded_packets_(0), - lost_timestamps_(0), - last_report_timestamp_(0), - len_waiting_times_(0), - next_waiting_time_index_(0) { - memset(waiting_times_, 0, kLenWaitingTimes * sizeof(waiting_times_[0])); -} - -void StatisticsCalculator::Reset() { - preemptive_samples_ = 0; - accelerate_samples_ = 0; - added_zero_samples_ = 0; - expanded_voice_samples_ = 0; - expanded_noise_samples_ = 0; -} - -void StatisticsCalculator::ResetMcu() { - discarded_packets_ = 0; - lost_timestamps_ = 0; - last_report_timestamp_ = 0; -} - -void StatisticsCalculator::ResetWaitingTimeStatistics() { - memset(waiting_times_, 0, kLenWaitingTimes * sizeof(waiting_times_[0])); - len_waiting_times_ = 0; - next_waiting_time_index_ = 0; -} - -void StatisticsCalculator::ExpandedVoiceSamples(int num_samples) { - expanded_voice_samples_ += num_samples; -} - -void StatisticsCalculator::ExpandedNoiseSamples(int num_samples) { - expanded_noise_samples_ += num_samples; -} - -void StatisticsCalculator::PreemptiveExpandedSamples(int num_samples) { - preemptive_samples_ += num_samples; -} - -void StatisticsCalculator::AcceleratedSamples(int num_samples) { - accelerate_samples_ += num_samples; -} - -void StatisticsCalculator::AddZeros(int num_samples) { - added_zero_samples_ += num_samples; -} - -void StatisticsCalculator::PacketsDiscarded(int num_packets) { - discarded_packets_ += num_packets; -} - -void StatisticsCalculator::LostSamples(int num_samples) { - lost_timestamps_ += num_samples; -} - -void StatisticsCalculator::IncreaseCounter(int num_samples, int fs_hz) { - last_report_timestamp_ += num_samples; - if (last_report_timestamp_ > - static_cast(fs_hz * kMaxReportPeriod)) { - lost_timestamps_ = 0; - last_report_timestamp_ = 0; - discarded_packets_ = 0; - } -} - -void StatisticsCalculator::StoreWaitingTime(int waiting_time_ms) { - assert(next_waiting_time_index_ < kLenWaitingTimes); - waiting_times_[next_waiting_time_index_] = waiting_time_ms; - next_waiting_time_index_++; - if (next_waiting_time_index_ >= kLenWaitingTimes) { - next_waiting_time_index_ = 0; - } - if (len_waiting_times_ < kLenWaitingTimes) { - len_waiting_times_++; - } -} - -void StatisticsCalculator::GetNetworkStatistics( - int fs_hz, - int num_samples_in_buffers, - int samples_per_packet, - const DelayManager& delay_manager, - const DecisionLogic& decision_logic, - NetEqNetworkStatistics *stats) { - if (fs_hz <= 0 || !stats) { - assert(false); - return; - } - - stats->added_zero_samples = added_zero_samples_; - stats->current_buffer_size_ms = num_samples_in_buffers * 1000 / fs_hz; - const int ms_per_packet = decision_logic.packet_length_samples() / - (fs_hz / 1000); - stats->preferred_buffer_size_ms = (delay_manager.TargetLevel() >> 8) * - ms_per_packet; - stats->jitter_peaks_found = delay_manager.PeakFound(); - stats->clockdrift_ppm = delay_manager.AverageIAT(); - - stats->packet_loss_rate = CalculateQ14Ratio(lost_timestamps_, - last_report_timestamp_); - - const unsigned discarded_samples = discarded_packets_ * samples_per_packet; - stats->packet_discard_rate = CalculateQ14Ratio(discarded_samples, - last_report_timestamp_); - - stats->accelerate_rate = CalculateQ14Ratio(accelerate_samples_, - last_report_timestamp_); - - stats->preemptive_rate = CalculateQ14Ratio(preemptive_samples_, - last_report_timestamp_); - - stats->expand_rate = CalculateQ14Ratio(expanded_voice_samples_ + - expanded_noise_samples_, - last_report_timestamp_); - - // Reset counters. - ResetMcu(); - Reset(); -} - -void StatisticsCalculator::WaitingTimes(std::vector* waiting_times) { - if (!waiting_times) { - return; - } - waiting_times->assign(waiting_times_, waiting_times_ + len_waiting_times_); - ResetWaitingTimeStatistics(); -} - -int StatisticsCalculator::CalculateQ14Ratio(uint32_t numerator, - uint32_t denominator) { - if (numerator == 0) { - return 0; - } else if (numerator < denominator) { - // Ratio must be smaller than 1 in Q14. - assert((numerator << 14) / denominator < (1 << 14)); - return (numerator << 14) / denominator; - } else { - // Will not produce a ratio larger than 1, since this is probably an error. - return 1 << 14; - } -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/statistics_calculator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/statistics_calculator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/statistics_calculator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/statistics_calculator.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_STATISTICS_CALCULATOR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_STATISTICS_CALCULATOR_H_ - -#include - -#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Forward declarations. -class DecisionLogic; -class DelayManager; - -// This class handles various network statistics in NetEq. -class StatisticsCalculator { - public: - StatisticsCalculator(); - - virtual ~StatisticsCalculator() {} - - // Resets most of the counters. - void Reset(); - - // Resets the counters that are not handled by Reset(). - void ResetMcu(); - - // Resets the waiting time statistics. - void ResetWaitingTimeStatistics(); - - // Reports that |num_samples| samples were produced through expansion, and - // that the expansion produced other than just noise samples. - void ExpandedVoiceSamples(int num_samples); - - // Reports that |num_samples| samples were produced through expansion, and - // that the expansion produced only noise samples. - void ExpandedNoiseSamples(int num_samples); - - // Reports that |num_samples| samples were produced through preemptive - // expansion. - void PreemptiveExpandedSamples(int num_samples); - - // Reports that |num_samples| samples were removed through accelerate. - void AcceleratedSamples(int num_samples); - - // Reports that |num_samples| zeros were inserted into the output. - void AddZeros(int num_samples); - - // Reports that |num_packets| packets were discarded. - void PacketsDiscarded(int num_packets); - - // Reports that |num_samples| were lost. - void LostSamples(int num_samples); - - // Increases the report interval counter with |num_samples| at a sample rate - // of |fs_hz|. - void IncreaseCounter(int num_samples, int fs_hz); - - // Stores new packet waiting time in waiting time statistics. - void StoreWaitingTime(int waiting_time_ms); - - // Returns the current network statistics in |stats|. The current sample rate - // is |fs_hz|, the total number of samples in packet buffer and sync buffer - // yet to play out is |num_samples_in_buffers|, and the number of samples per - // packet is |samples_per_packet|. - void GetNetworkStatistics(int fs_hz, - int num_samples_in_buffers, - int samples_per_packet, - const DelayManager& delay_manager, - const DecisionLogic& decision_logic, - NetEqNetworkStatistics *stats); - - void WaitingTimes(std::vector* waiting_times); - - private: - static const int kMaxReportPeriod = 60; // Seconds before auto-reset. - static const int kLenWaitingTimes = 100; - - // Calculates numerator / denominator, and returns the value in Q14. - static int CalculateQ14Ratio(uint32_t numerator, uint32_t denominator); - - uint32_t preemptive_samples_; - uint32_t accelerate_samples_; - int added_zero_samples_; - uint32_t expanded_voice_samples_; - uint32_t expanded_noise_samples_; - int discarded_packets_; - uint32_t lost_timestamps_; - uint32_t last_report_timestamp_; - int waiting_times_[kLenWaitingTimes]; // Used as a circular buffer. - int len_waiting_times_; - int next_waiting_time_index_; - - DISALLOW_COPY_AND_ASSIGN(StatisticsCalculator); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_STATISTICS_CALCULATOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/sync_buffer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/sync_buffer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/sync_buffer.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/sync_buffer.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include // Access to min. - -#include "webrtc/modules/audio_coding/neteq4/sync_buffer.h" - -namespace webrtc { - -size_t SyncBuffer::FutureLength() const { - return Size() - next_index_; -} - -void SyncBuffer::PushBack(const AudioMultiVector& append_this) { - size_t samples_added = append_this.Size(); - AudioMultiVector::PushBack(append_this); - AudioMultiVector::PopFront(samples_added); - if (samples_added <= next_index_) { - next_index_ -= samples_added; - } else { - // This means that we are pushing out future data that was never used. -// assert(false); - // TODO(hlundin): This assert must be disabled to support 60 ms frames. - // This should not happen even for 60 ms frames, but it does. Investigate - // why. - next_index_ = 0; - } - dtmf_index_ -= std::min(dtmf_index_, samples_added); -} - -void SyncBuffer::PushFrontZeros(size_t length) { - InsertZerosAtIndex(length, 0); -} - -void SyncBuffer::InsertZerosAtIndex(size_t length, size_t position) { - position = std::min(position, Size()); - length = std::min(length, Size() - position); - AudioMultiVector::PopBack(length); - for (size_t channel = 0; channel < Channels(); ++channel) { - channels_[channel]->InsertZerosAt(length, position); - } - if (next_index_ >= position) { - // We are moving the |next_index_| sample. - set_next_index(next_index_ + length); // Overflow handled by subfunction. - } - if (dtmf_index_ > 0 && dtmf_index_ >= position) { - // We are moving the |dtmf_index_| sample. - set_dtmf_index(dtmf_index_ + length); // Overflow handled by subfunction. - } -} - -void SyncBuffer::ReplaceAtIndex(const AudioMultiVector& insert_this, - size_t length, - size_t position) { - position = std::min(position, Size()); // Cap |position| in the valid range. - length = std::min(length, Size() - position); - AudioMultiVector::OverwriteAt(insert_this, length, position); -} - -void SyncBuffer::ReplaceAtIndex(const AudioMultiVector& insert_this, - size_t position) { - ReplaceAtIndex(insert_this, insert_this.Size(), position); -} - -size_t SyncBuffer::GetNextAudioInterleaved(size_t requested_len, - int16_t* output) { - if (!output) { - assert(false); - return 0; - } - size_t samples_to_read = std::min(FutureLength(), requested_len); - ReadInterleavedFromIndex(next_index_, samples_to_read, output); - next_index_ += samples_to_read; - return samples_to_read; -} - -void SyncBuffer::IncreaseEndTimestamp(uint32_t increment) { - end_timestamp_ += increment; -} - -void SyncBuffer::Flush() { - Zeros(Size()); - next_index_ = Size(); - end_timestamp_ = 0; - dtmf_index_ = 0; -} - -void SyncBuffer::set_next_index(size_t value) { - // Cannot set |next_index_| larger than the size of the buffer. - next_index_ = std::min(value, Size()); -} - -void SyncBuffer::set_dtmf_index(size_t value) { - // Cannot set |dtmf_index_| larger than the size of the buffer. - dtmf_index_ = std::min(value, Size()); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/sync_buffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/sync_buffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/sync_buffer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/sync_buffer.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_SYNC_BUFFER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_SYNC_BUFFER_H_ - -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -class SyncBuffer : public AudioMultiVector { - public: - SyncBuffer(size_t channels, size_t length) - : AudioMultiVector(channels, length), - next_index_(length), - end_timestamp_(0), - dtmf_index_(0) {} - - virtual ~SyncBuffer() {} - - // Returns the number of samples yet to play out form the buffer. - size_t FutureLength() const; - - // Adds the contents of |append_this| to the back of the SyncBuffer. Removes - // the same number of samples from the beginning of the SyncBuffer, to - // maintain a constant buffer size. The |next_index_| is updated to reflect - // the move of the beginning of "future" data. - void PushBack(const AudioMultiVector& append_this); - - // Adds |length| zeros to the beginning of each channel. Removes - // the same number of samples from the end of the SyncBuffer, to - // maintain a constant buffer size. The |next_index_| is updated to reflect - // the move of the beginning of "future" data. - // Note that this operation may delete future samples that are waiting to - // be played. - void PushFrontZeros(size_t length); - - // Inserts |length| zeros into each channel at index |position|. The size of - // the SyncBuffer is kept constant, which means that the last |length| - // elements in each channel will be purged. - virtual void InsertZerosAtIndex(size_t length, size_t position); - - // Overwrites each channel in this SyncBuffer with values taken from - // |insert_this|. The values are taken from the beginning of |insert_this| and - // are inserted starting at |position|. |length| values are written into each - // channel. The size of the SyncBuffer is kept constant. That is, if |length| - // and |position| are selected such that the new data would extend beyond the - // end of the current SyncBuffer, the buffer is not extended. - // The |next_index_| is not updated. - virtual void ReplaceAtIndex(const AudioMultiVector& insert_this, - size_t length, - size_t position); - - // Same as the above method, but where all of |insert_this| is written (with - // the same constraints as above, that the SyncBuffer is not extended). - virtual void ReplaceAtIndex(const AudioMultiVector& insert_this, - size_t position); - - // Reads |requested_len| samples from each channel and writes them interleaved - // into |output|. The |next_index_| is updated to point to the sample to read - // next time. - size_t GetNextAudioInterleaved(size_t requested_len, int16_t* output); - - // Adds |increment| to |end_timestamp_|. - void IncreaseEndTimestamp(uint32_t increment); - - // Flushes the buffer. The buffer will contain only zeros after the flush, and - // |next_index_| will point to the end, like when the buffer was first - // created. - void Flush(); - - const AudioVector& Channel(size_t n) { return *channels_[n]; } - - // Accessors and mutators. - size_t next_index() const { return next_index_; } - void set_next_index(size_t value); - uint32_t end_timestamp() const { return end_timestamp_; } - void set_end_timestamp(uint32_t value) { end_timestamp_ = value; } - size_t dtmf_index() const { return dtmf_index_; } - void set_dtmf_index(size_t value); - - private: - size_t next_index_; - uint32_t end_timestamp_; // The timestamp of the last sample in the buffer. - size_t dtmf_index_; // Index to the first non-DTMF sample in the buffer. - - DISALLOW_COPY_AND_ASSIGN(SyncBuffer); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_SYNC_BUFFER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/sync_buffer_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/sync_buffer_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/sync_buffer_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/sync_buffer_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/sync_buffer.h" - -#include "gtest/gtest.h" - -namespace webrtc { - -TEST(SyncBuffer, CreateAndDestroy) { - // Create a SyncBuffer with two channels and 10 samples each. - static const size_t kLen = 10; - static const size_t kChannels = 2; - SyncBuffer sync_buffer(kChannels, kLen); - EXPECT_EQ(kChannels, sync_buffer.Channels()); - EXPECT_EQ(kLen, sync_buffer.Size()); - // When the buffer is empty, the next index to play out is at the end. - EXPECT_EQ(kLen, sync_buffer.next_index()); - // Verify that all elements are zero. - for (size_t channel = 0; channel < kChannels; ++channel) { - for (size_t i = 0; i < kLen; ++i) { - EXPECT_EQ(0, sync_buffer[channel][i]); - } - } -} - -TEST(SyncBuffer, SetNextIndex) { - // Create a SyncBuffer with two channels and 100 samples each. - static const size_t kLen = 100; - static const size_t kChannels = 2; - SyncBuffer sync_buffer(kChannels, kLen); - sync_buffer.set_next_index(0); - EXPECT_EQ(0u, sync_buffer.next_index()); - sync_buffer.set_next_index(kLen / 2); - EXPECT_EQ(kLen / 2, sync_buffer.next_index()); - sync_buffer.set_next_index(kLen); - EXPECT_EQ(kLen, sync_buffer.next_index()); - // Try to set larger than the buffer size; should cap at buffer size. - sync_buffer.set_next_index(kLen + 1); - EXPECT_EQ(kLen, sync_buffer.next_index()); -} - -TEST(SyncBuffer, PushBackAndFlush) { - // Create a SyncBuffer with two channels and 100 samples each. - static const size_t kLen = 100; - static const size_t kChannels = 2; - SyncBuffer sync_buffer(kChannels, kLen); - static const size_t kNewLen = 10; - AudioMultiVector new_data(kChannels, kNewLen); - // Populate |new_data|. - for (size_t channel = 0; channel < kChannels; ++channel) { - for (size_t i = 0; i < kNewLen; ++i) { - new_data[channel][i] = i; - } - } - // Push back |new_data| into |sync_buffer|. This operation should pop out - // data from the front of |sync_buffer|, so that the size of the buffer - // remains the same. The |next_index_| should also move with the same length. - sync_buffer.PushBack(new_data); - ASSERT_EQ(kLen, sync_buffer.Size()); - // Verify that |next_index_| moved accordingly. - EXPECT_EQ(kLen - kNewLen, sync_buffer.next_index()); - // Verify the new contents. - for (size_t channel = 0; channel < kChannels; ++channel) { - for (size_t i = 0; i < kNewLen; ++i) { - EXPECT_EQ(new_data[channel][i], - sync_buffer[channel][sync_buffer.next_index() + i]); - } - } - - // Now flush the buffer, and verify that it is all zeros, and that next_index - // points to the end. - sync_buffer.Flush(); - ASSERT_EQ(kLen, sync_buffer.Size()); - EXPECT_EQ(kLen, sync_buffer.next_index()); - for (size_t channel = 0; channel < kChannels; ++channel) { - for (size_t i = 0; i < kLen; ++i) { - EXPECT_EQ(0, sync_buffer[channel][i]); - } - } -} - -TEST(SyncBuffer, PushFrontZeros) { - // Create a SyncBuffer with two channels and 100 samples each. - static const size_t kLen = 100; - static const size_t kChannels = 2; - SyncBuffer sync_buffer(kChannels, kLen); - static const size_t kNewLen = 10; - AudioMultiVector new_data(kChannels, kNewLen); - // Populate |new_data|. - for (size_t channel = 0; channel < kChannels; ++channel) { - for (size_t i = 0; i < kNewLen; ++i) { - new_data[channel][i] = 1000 + i; - } - } - sync_buffer.PushBack(new_data); - EXPECT_EQ(kLen, sync_buffer.Size()); - - // Push |kNewLen| - 1 zeros into each channel in the front of the SyncBuffer. - sync_buffer.PushFrontZeros(kNewLen - 1); - EXPECT_EQ(kLen, sync_buffer.Size()); // Size should remain the same. - // Verify that |next_index_| moved accordingly. Should be at the end - 1. - EXPECT_EQ(kLen - 1, sync_buffer.next_index()); - // Verify the zeros. - for (size_t channel = 0; channel < kChannels; ++channel) { - for (size_t i = 0; i < kNewLen - 1; ++i) { - EXPECT_EQ(0, sync_buffer[channel][i]); - } - } - // Verify that the correct data is at the end of the SyncBuffer. - for (size_t channel = 0; channel < kChannels; ++channel) { - EXPECT_EQ(1000, sync_buffer[channel][sync_buffer.next_index()]); - } -} - -TEST(SyncBuffer, GetNextAudioInterleaved) { - // Create a SyncBuffer with two channels and 100 samples each. - static const size_t kLen = 100; - static const size_t kChannels = 2; - SyncBuffer sync_buffer(kChannels, kLen); - static const size_t kNewLen = 10; - AudioMultiVector new_data(kChannels, kNewLen); - // Populate |new_data|. - for (size_t channel = 0; channel < kChannels; ++channel) { - for (size_t i = 0; i < kNewLen; ++i) { - new_data[channel][i] = i; - } - } - // Push back |new_data| into |sync_buffer|. This operation should pop out - // data from the front of |sync_buffer|, so that the size of the buffer - // remains the same. The |next_index_| should also move with the same length. - sync_buffer.PushBack(new_data); - - // Read to interleaved output. Read in two batches, where each read operation - // should automatically update the |net_index_| in the SyncBuffer. - int16_t output[kChannels * kNewLen]; - // Note that |samples_read| is the number of samples read from each channel. - // That is, the number of samples written to |output| is - // |samples_read| * |kChannels|. - size_t samples_read = sync_buffer.GetNextAudioInterleaved(kNewLen / 2, - output); - samples_read += - sync_buffer.GetNextAudioInterleaved(kNewLen / 2, - &output[samples_read * kChannels]); - EXPECT_EQ(kNewLen, samples_read); - - // Verify the data. - int16_t* output_ptr = output; - for (size_t i = 0; i < kNewLen; ++i) { - for (size_t channel = 0; channel < kChannels; ++channel) { - EXPECT_EQ(new_data[channel][i], *output_ptr); - ++output_ptr; - } - } -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/delay_tool/parse_delay_file.m thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/delay_tool/parse_delay_file.m --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/delay_tool/parse_delay_file.m 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/delay_tool/parse_delay_file.m 1970-01-01 00:00:00.000000000 +0000 @@ -1,191 +0,0 @@ -function outStruct = parse_delay_file(file) - -fid = fopen(file, 'rb'); -if fid == -1 - error('Cannot open file %s', file); -end - -textline = fgetl(fid); -if ~strncmp(textline, '#!NetEQ_Delay_Logging', 21) - error('Wrong file format'); -end - -ver = sscanf(textline, '#!NetEQ_Delay_Logging%d.%d'); -if ~all(ver == [2; 0]) - error('Wrong version of delay logging function') -end - - -start_pos = ftell(fid); -fseek(fid, -12, 'eof'); -textline = fgetl(fid); -if ~strncmp(textline, 'End of file', 21) - error('File ending is not correct. Seems like the simulation ended abnormally.'); -end - -fseek(fid,-12-4, 'eof'); -Npackets = fread(fid, 1, 'int32'); -fseek(fid, start_pos, 'bof'); - -rtpts = zeros(Npackets, 1); -seqno = zeros(Npackets, 1); -pt = zeros(Npackets, 1); -plen = zeros(Npackets, 1); -recin_t = nan*ones(Npackets, 1); -decode_t = nan*ones(Npackets, 1); -playout_delay = zeros(Npackets, 1); -optbuf = zeros(Npackets, 1); - -fs_ix = 1; -clock = 0; -ts_ix = 1; -ended = 0; -late_packets = 0; -fs_now = 8000; -last_decode_k = 0; -tot_expand = 0; -tot_accelerate = 0; -tot_preemptive = 0; - -while not(ended) - signal = fread(fid, 1, '*int32'); - - switch signal - case 3 % NETEQ_DELAY_LOGGING_SIGNAL_CLOCK - clock = fread(fid, 1, '*float32'); - - % keep on reading batches of M until the signal is no longer "3" - % read int32 + float32 in one go - % this is to save execution time - temp = [3; 0]; - M = 120; - while all(temp(1,:) == 3) - fp = ftell(fid); - temp = fread(fid, [2 M], '*int32'); - end - - % back up to last clock event - fseek(fid, fp - ftell(fid) + ... - (find(temp(1,:) ~= 3, 1 ) - 2) * 2 * 4 + 4, 'cof'); - % read the last clock value - clock = fread(fid, 1, '*float32'); - - case 1 % NETEQ_DELAY_LOGGING_SIGNAL_RECIN - temp_ts = fread(fid, 1, 'uint32'); - - if late_packets > 0 - temp_ix = ts_ix - 1; - while (temp_ix >= 1) && (rtpts(temp_ix) ~= temp_ts) - % TODO(hlundin): use matlab vector search instead? - temp_ix = temp_ix - 1; - end - - if temp_ix >= 1 - % the ts was found in the vector - late_packets = late_packets - 1; - else - temp_ix = ts_ix; - ts_ix = ts_ix + 1; - end - else - temp_ix = ts_ix; - ts_ix = ts_ix + 1; - end - - rtpts(temp_ix) = temp_ts; - seqno(temp_ix) = fread(fid, 1, 'uint16'); - pt(temp_ix) = fread(fid, 1, 'int32'); - plen(temp_ix) = fread(fid, 1, 'int16'); - recin_t(temp_ix) = clock; - - case 2 % NETEQ_DELAY_LOGGING_SIGNAL_FLUSH - % do nothing - - case 4 % NETEQ_DELAY_LOGGING_SIGNAL_EOF - ended = 1; - - case 5 % NETEQ_DELAY_LOGGING_SIGNAL_DECODE - last_decode_ts = fread(fid, 1, 'uint32'); - temp_delay = fread(fid, 1, 'uint16'); - - k = find(rtpts(1:(ts_ix - 1))==last_decode_ts,1,'last'); - if ~isempty(k) - decode_t(k) = clock; - playout_delay(k) = temp_delay + ... - 5 * fs_now / 8000; % add overlap length - last_decode_k = k; - end - - case 6 % NETEQ_DELAY_LOGGING_SIGNAL_CHANGE_FS - fsvec(fs_ix) = fread(fid, 1, 'uint16'); - fschange_ts(fs_ix) = last_decode_ts; - fs_now = fsvec(fs_ix); - fs_ix = fs_ix + 1; - - case 7 % NETEQ_DELAY_LOGGING_SIGNAL_MERGE_INFO - playout_delay(last_decode_k) = playout_delay(last_decode_k) ... - + fread(fid, 1, 'int32'); - - case 8 % NETEQ_DELAY_LOGGING_SIGNAL_EXPAND_INFO - temp = fread(fid, 1, 'int32'); - if last_decode_k ~= 0 - tot_expand = tot_expand + temp / (fs_now / 1000); - end - - case 9 % NETEQ_DELAY_LOGGING_SIGNAL_ACCELERATE_INFO - temp = fread(fid, 1, 'int32'); - if last_decode_k ~= 0 - tot_accelerate = tot_accelerate + temp / (fs_now / 1000); - end - - case 10 % NETEQ_DELAY_LOGGING_SIGNAL_PREEMPTIVE_INFO - temp = fread(fid, 1, 'int32'); - if last_decode_k ~= 0 - tot_preemptive = tot_preemptive + temp / (fs_now / 1000); - end - - case 11 % NETEQ_DELAY_LOGGING_SIGNAL_OPTBUF - optbuf(last_decode_k) = fread(fid, 1, 'int32'); - - case 12 % NETEQ_DELAY_LOGGING_SIGNAL_DECODE_ONE_DESC - last_decode_ts = fread(fid, 1, 'uint32'); - k = ts_ix - 1; - - while (k >= 1) && (rtpts(k) ~= last_decode_ts) - % TODO(hlundin): use matlab vector search instead? - k = k - 1; - end - - if k < 1 - % packet not received yet - k = ts_ix; - rtpts(ts_ix) = last_decode_ts; - late_packets = late_packets + 1; - end - - decode_t(k) = clock; - playout_delay(k) = fread(fid, 1, 'uint16') + ... - 5 * fs_now / 8000; % add overlap length - last_decode_k = k; - - end - -end - - -fclose(fid); - -outStruct = struct(... - 'ts', rtpts, ... - 'sn', seqno, ... - 'pt', pt,... - 'plen', plen,... - 'arrival', recin_t,... - 'decode', decode_t,... - 'fs', fsvec(:),... - 'fschange_ts', fschange_ts(:),... - 'playout_delay', playout_delay,... - 'tot_expand', tot_expand,... - 'tot_accelerate', tot_accelerate,... - 'tot_preemptive', tot_preemptive,... - 'optbuf', optbuf); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/delay_tool/plot_neteq_delay.m thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/delay_tool/plot_neteq_delay.m --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/delay_tool/plot_neteq_delay.m 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/delay_tool/plot_neteq_delay.m 1970-01-01 00:00:00.000000000 +0000 @@ -1,187 +0,0 @@ -function [delay_struct, delayvalues] = plot_neteq_delay(delayfile, varargin) - -% InfoStruct = plot_neteq_delay(delayfile) -% InfoStruct = plot_neteq_delay(delayfile, 'skipdelay', skip_seconds) -% -% Henrik Lundin, 2006-11-17 -% Henrik Lundin, 2011-05-17 -% - -try - s = parse_delay_file(delayfile); -catch - error(lasterr); -end - -delayskip=0; -noplot=0; -arg_ptr=1; -delaypoints=[]; - -s.sn=unwrap_seqno(s.sn); - -while arg_ptr+1 <= nargin - switch lower(varargin{arg_ptr}) - case {'skipdelay', 'delayskip'} - % skip a number of seconds in the beginning when calculating delays - delayskip = varargin{arg_ptr+1}; - arg_ptr = arg_ptr + 2; - case 'noplot' - noplot=1; - arg_ptr = arg_ptr + 1; - case {'get_delay', 'getdelay'} - % return a vector of delay values for the points in the given vector - delaypoints = varargin{arg_ptr+1}; - arg_ptr = arg_ptr + 2; - otherwise - warning('Unknown switch %s\n', varargin{arg_ptr}); - arg_ptr = arg_ptr + 1; - end -end - -% find lost frames that were covered by one-descriptor decoding -one_desc_ix=find(isnan(s.arrival)); -for k=1:length(one_desc_ix) - ix=find(s.ts==max(s.ts(s.ts(one_desc_ix(k))>s.ts))); - s.sn(one_desc_ix(k))=s.sn(ix)+1; - s.pt(one_desc_ix(k))=s.pt(ix); - s.arrival(one_desc_ix(k))=s.arrival(ix)+s.decode(one_desc_ix(k))-s.decode(ix); -end - -% remove duplicate received frames that were never decoded (RED codec) -if length(unique(s.ts(isfinite(s.ts)))) < length(s.ts(isfinite(s.ts))) - ix=find(isfinite(s.decode)); - s.sn=s.sn(ix); - s.ts=s.ts(ix); - s.arrival=s.arrival(ix); - s.playout_delay=s.playout_delay(ix); - s.pt=s.pt(ix); - s.optbuf=s.optbuf(ix); - plen=plen(ix); - s.decode=s.decode(ix); -end - -% find non-unique sequence numbers -[~,un_ix]=unique(s.sn); -nonun_ix=setdiff(1:length(s.sn),un_ix); -if ~isempty(nonun_ix) - warning('RTP sequence numbers are in error'); -end - -% sort vectors -[s.sn,sort_ix]=sort(s.sn); -s.ts=s.ts(sort_ix); -s.arrival=s.arrival(sort_ix); -s.decode=s.decode(sort_ix); -s.playout_delay=s.playout_delay(sort_ix); -s.pt=s.pt(sort_ix); - -send_t=s.ts-s.ts(1); -if length(s.fs)<1 - warning('No info about sample rate found in file. Using default 8000.'); - s.fs(1)=8000; - s.fschange_ts(1)=min(s.ts); -elseif s.fschange_ts(1)>min(s.ts) - s.fschange_ts(1)=min(s.ts); -end - -end_ix=length(send_t); -for k=length(s.fs):-1:1 - start_ix=find(s.ts==s.fschange_ts(k)); - send_t(start_ix:end_ix)=send_t(start_ix:end_ix)/s.fs(k)*1000; - s.playout_delay(start_ix:end_ix)=s.playout_delay(start_ix:end_ix)/s.fs(k)*1000; - s.optbuf(start_ix:end_ix)=s.optbuf(start_ix:end_ix)/s.fs(k)*1000; - end_ix=start_ix-1; -end - -tot_time=max(send_t)-min(send_t); - -seq_ix=s.sn-min(s.sn)+1; -send_t=send_t+max(min(s.arrival-send_t),0); - -plot_send_t=nan*ones(max(seq_ix),1); -plot_send_t(seq_ix)=send_t; -plot_nw_delay=nan*ones(max(seq_ix),1); -plot_nw_delay(seq_ix)=s.arrival-send_t; - -cng_ix=find(s.pt~=13); % find those packets that are not CNG/SID - -if noplot==0 - h=plot(plot_send_t/1000,plot_nw_delay); - set(h,'color',0.75*[1 1 1]); - hold on - if any(s.optbuf~=0) - peak_ix=find(s.optbuf(cng_ix)<0); % peak mode is labeled with negative values - no_peak_ix=find(s.optbuf(cng_ix)>0); %setdiff(1:length(cng_ix),peak_ix); - h1=plot(send_t(cng_ix(peak_ix))/1000,... - s.arrival(cng_ix(peak_ix))+abs(s.optbuf(cng_ix(peak_ix)))-send_t(cng_ix(peak_ix)),... - 'r.'); - h2=plot(send_t(cng_ix(no_peak_ix))/1000,... - s.arrival(cng_ix(no_peak_ix))+abs(s.optbuf(cng_ix(no_peak_ix)))-send_t(cng_ix(no_peak_ix)),... - 'g.'); - set([h1, h2],'markersize',1) - end - %h=plot(send_t(seq_ix)/1000,s.decode+s.playout_delay-send_t(seq_ix)); - h=plot(send_t(cng_ix)/1000,s.decode(cng_ix)+s.playout_delay(cng_ix)-send_t(cng_ix)); - set(h,'linew',1.5); - hold off - ax1=axis; - axis tight - ax2=axis; - axis([ax2(1:3) ax1(4)]) -end - - -% calculate delays and other parameters - -delayskip_ix = find(send_t-send_t(1)>=delayskip*1000, 1 ); - -use_ix = intersect(cng_ix,... % use those that are not CNG/SID frames... - intersect(find(isfinite(s.decode)),... % ... that did arrive ... - (delayskip_ix:length(s.decode))')); % ... and are sent after delayskip seconds - -mean_delay = mean(s.decode(use_ix)+s.playout_delay(use_ix)-send_t(use_ix)); -neteq_delay = mean(s.decode(use_ix)+s.playout_delay(use_ix)-s.arrival(use_ix)); - -Npack=max(s.sn(delayskip_ix:end))-min(s.sn(delayskip_ix:end))+1; -nw_lossrate=(Npack-length(s.sn(delayskip_ix:end)))/Npack; -neteq_lossrate=(length(s.sn(delayskip_ix:end))-length(use_ix))/Npack; - -delay_struct=struct('mean_delay',mean_delay,'neteq_delay',neteq_delay,... - 'nw_lossrate',nw_lossrate,'neteq_lossrate',neteq_lossrate,... - 'tot_expand',round(s.tot_expand),'tot_accelerate',round(s.tot_accelerate),... - 'tot_preemptive',round(s.tot_preemptive),'tot_time',tot_time,... - 'filename',delayfile,'units','ms','fs',unique(s.fs)); - -if not(isempty(delaypoints)) - delayvalues=interp1(send_t(cng_ix),... - s.decode(cng_ix)+s.playout_delay(cng_ix)-send_t(cng_ix),... - delaypoints,'nearest',NaN); -else - delayvalues=[]; -end - - - -% SUBFUNCTIONS % - -function y=unwrap_seqno(x) - -jumps=find(abs((diff(x)-1))>65000); - -while ~isempty(jumps) - n=jumps(1); - if x(n+1)-x(n) < 0 - % negative jump - x(n+1:end)=x(n+1:end)+65536; - else - % positive jump - x(n+1:end)=x(n+1:end)-65536; - end - - jumps=find(abs((diff(x(n+1:end))-1))>65000); -end - -y=x; - -return; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/neteq_performance_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/neteq_performance_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/neteq_performance_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/neteq_performance_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "testing/gtest/include/gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq4/tools/neteq_performance_test.h" -#include "webrtc/test/testsupport/perf_test.h" -#include "webrtc/typedefs.h" - -// Runs a test with 10% packet losses and 10% clock drift, to exercise -// both loss concealment and time-stretching code. -TEST(NetEqPerformanceTest, Run) { - const int kSimulationTimeMs = 10000000; - const int kLossPeriod = 10; // Drop every 10th packet. - const double kDriftFactor = 0.1; - int64_t runtime = webrtc::test::NetEqPerformanceTest::Run( - kSimulationTimeMs, kLossPeriod, kDriftFactor); - ASSERT_GT(runtime, 0); - webrtc::test::PrintResult( - "neteq_performance", "", "10_pl_10_drift", runtime, "ms", true); -} - -// Runs a test with neither packet losses nor clock drift, to put -// emphasis on the "good-weather" code path, which is presumably much -// more lightweight. -TEST(NetEqPerformanceTest, RunClean) { - const int kSimulationTimeMs = 10000000; - const int kLossPeriod = 0; // No losses. - const double kDriftFactor = 0.0; // No clock drift. - int64_t runtime = webrtc::test::NetEqPerformanceTest::Run( - kSimulationTimeMs, kLossPeriod, kDriftFactor); - ASSERT_GT(runtime, 0); - webrtc::test::PrintResult( - "neteq_performance", "", "0_pl_0_drift", runtime, "ms", true); -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/neteq_speed_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/neteq_speed_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/neteq_speed_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/neteq_speed_test.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include - -#include "gflags/gflags.h" -#include "webrtc/modules/audio_coding/neteq4/tools/neteq_performance_test.h" -#include "webrtc/typedefs.h" - -// Flag validators. -static bool ValidateRuntime(const char* flagname, int value) { - if (value > 0) // Value is ok. - return true; - printf("Invalid value for --%s: %d\n", flagname, static_cast(value)); - return false; -} -static bool ValidateLossrate(const char* flagname, int value) { - if (value >= 0) // Value is ok. - return true; - printf("Invalid value for --%s: %d\n", flagname, static_cast(value)); - return false; -} -static bool ValidateDriftfactor(const char* flagname, double value) { - if (value >= 0.0 && value < 1.0) // Value is ok. - return true; - printf("Invalid value for --%s: %f\n", flagname, value); - return false; -} - -// Define command line flags. -DEFINE_int32(runtime_ms, 10000, "Simulated runtime in ms."); -static const bool runtime_ms_dummy = - google::RegisterFlagValidator(&FLAGS_runtime_ms, &ValidateRuntime); -DEFINE_int32(lossrate, 10, - "Packet lossrate; drop every N packets."); -static const bool lossrate_dummy = - google::RegisterFlagValidator(&FLAGS_lossrate, &ValidateLossrate); -DEFINE_double(drift, 0.1, - "Clockdrift factor."); -static const bool drift_dummy = - google::RegisterFlagValidator(&FLAGS_drift, &ValidateDriftfactor); - -int main(int argc, char* argv[]) { - std::string program_name = argv[0]; - std::string usage = "Tool for measuring the speed of NetEq.\n" - "Usage: " + program_name + " [options]\n\n" - " --runtime_ms=N runtime in ms; default is 10000 ms\n" - " --lossrate=N drop every N packets; default is 10\n" - " --drift=F clockdrift factor between 0.0 and 1.0; " - "default is 0.1\n"; - google::SetUsageMessage(usage); - google::ParseCommandLineFlags(&argc, &argv, true); - - if (argc != 1) { - // Print usage information. - std::cout << google::ProgramUsage(); - return 0; - } - - int64_t result = - webrtc::test::NetEqPerformanceTest::Run(FLAGS_runtime_ms, FLAGS_lossrate, - FLAGS_drift); - if (result <= 0) { - std::cout << "There was an error" << std::endl; - return -1; - } - - std::cout << "Simulation done" << std::endl; - std::cout << "Runtime = " << result << " ms" << std::endl; - return 0; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/NETEQTEST_DummyRTPpacket.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/NETEQTEST_DummyRTPpacket.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/NETEQTEST_DummyRTPpacket.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/NETEQTEST_DummyRTPpacket.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,204 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "NETEQTEST_DummyRTPpacket.h" - -#include -#include -#include - -#ifdef WIN32 -#include -#else -#include // for htons, htonl, etc -#endif - -int NETEQTEST_DummyRTPpacket::readFromFile(FILE *fp) -{ - if (!fp) - { - return -1; - } - - uint16_t length, plen; - uint32_t offset; - int packetLen; - - bool readNextPacket = true; - while (readNextPacket) { - readNextPacket = false; - if (fread(&length, 2, 1, fp) == 0) - { - reset(); - return -2; - } - length = ntohs(length); - - if (fread(&plen, 2, 1, fp) == 0) - { - reset(); - return -1; - } - packetLen = ntohs(plen); - - if (fread(&offset, 4, 1, fp) == 0) - { - reset(); - return -1; - } - // Store in local variable until we have passed the reset below. - uint32_t receiveTime = ntohl(offset); - - // Use length here because a plen of 0 specifies rtcp. - length = (uint16_t) (length - _kRDHeaderLen); - - // check buffer size - if (_datagram && _memSize < length + 1) - { - reset(); - } - - if (!_datagram) - { - // Add one extra byte, to be able to fake a dummy payload of 1 byte. - _datagram = new uint8_t[length + 1]; - _memSize = length + 1; - } - memset(_datagram, 0, length + 1); - - if (length == 0) - { - _datagramLen = 0; - _rtpParsed = false; - return packetLen; - } - - // Read basic header - if (fread((unsigned short *) _datagram, 1, _kBasicHeaderLen, fp) - != (size_t)_kBasicHeaderLen) - { - reset(); - return -1; - } - _receiveTime = receiveTime; - _datagramLen = _kBasicHeaderLen; - - // Parse the basic header - webrtc::WebRtcRTPHeader tempRTPinfo; - int P, X, CC; - parseBasicHeader(&tempRTPinfo, &P, &X, &CC); - - // Check if we have to extend the header - if (X != 0 || CC != 0) - { - int newLen = _kBasicHeaderLen + CC * 4 + X * 4; - assert(_memSize >= newLen); - - // Read extension from file - size_t readLen = newLen - _kBasicHeaderLen; - if (fread(&_datagram[_kBasicHeaderLen], 1, readLen, fp) != readLen) - { - reset(); - return -1; - } - _datagramLen = newLen; - - if (X != 0) - { - int totHdrLen = calcHeaderLength(X, CC); - assert(_memSize >= totHdrLen); - - // Read extension from file - size_t readLen = totHdrLen - newLen; - if (fread(&_datagram[newLen], 1, readLen, fp) != readLen) - { - reset(); - return -1; - } - _datagramLen = totHdrLen; - } - } - _datagramLen = length; - - if (!_blockList.empty() && _blockList.count(payloadType()) > 0) - { - readNextPacket = true; - } - } - - _rtpParsed = false; - assert(_memSize > _datagramLen); - _payloadLen = 1; // Set the length to 1 byte. - return packetLen; - -} - -int NETEQTEST_DummyRTPpacket::writeToFile(FILE *fp) -{ - if (!fp) - { - return -1; - } - - uint16_t length, plen; - uint32_t offset; - - // length including RTPplay header - length = htons(_datagramLen + _kRDHeaderLen); - if (fwrite(&length, 2, 1, fp) != 1) - { - return -1; - } - - // payload length - plen = htons(_datagramLen); - if (fwrite(&plen, 2, 1, fp) != 1) - { - return -1; - } - - // offset (=receive time) - offset = htonl(_receiveTime); - if (fwrite(&offset, 4, 1, fp) != 1) - { - return -1; - } - - // Figure out the length of the RTP header. - int headerLen; - if (_datagramLen == 0) - { - // No payload at all; we are done writing to file. - headerLen = 0; - } - else - { - parseHeader(); - headerLen = _payloadPtr - _datagram; - assert(headerLen >= 0); - } - - // write RTP header - if (fwrite((unsigned short *) _datagram, 1, headerLen, fp) != - static_cast(headerLen)) - { - return -1; - } - - return (headerLen + _kRDHeaderLen); // total number of bytes written - -} - -void NETEQTEST_DummyRTPpacket::parseHeader() { - NETEQTEST_RTPpacket::parseHeader(); - // Change _payloadLen to 1 byte. The memory should always be big enough. - assert(_memSize > _datagramLen); - _payloadLen = 1; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/NETEQTEST_DummyRTPpacket.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/NETEQTEST_DummyRTPpacket.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/NETEQTEST_DummyRTPpacket.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/NETEQTEST_DummyRTPpacket.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef NETEQTEST_DUMMYRTPPACKET_H -#define NETEQTEST_DUMMYRTPPACKET_H - -#include "NETEQTEST_RTPpacket.h" - -class NETEQTEST_DummyRTPpacket : public NETEQTEST_RTPpacket { - public: - virtual int readFromFile(FILE* fp) OVERRIDE; - virtual int writeToFile(FILE* fp) OVERRIDE; - virtual void parseHeader() OVERRIDE; -}; - -#endif // NETEQTEST_DUMMYRTPPACKET_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/NETEQTEST_RTPpacket.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/NETEQTEST_RTPpacket.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/NETEQTEST_RTPpacket.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/NETEQTEST_RTPpacket.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,875 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "NETEQTEST_RTPpacket.h" - -#include -#include // rand -#include - -#ifdef WIN32 -#include -#else -#include // for htons, htonl, etc -#endif - -const int NETEQTEST_RTPpacket::_kRDHeaderLen = 8; -const int NETEQTEST_RTPpacket::_kBasicHeaderLen = 12; - -NETEQTEST_RTPpacket::NETEQTEST_RTPpacket() -: -_datagram(NULL), -_payloadPtr(NULL), -_memSize(0), -_datagramLen(-1), -_payloadLen(0), -_rtpParsed(false), -_receiveTime(0), -_lost(false) -{ - memset(&_rtpInfo, 0, sizeof(_rtpInfo)); - _blockList.clear(); -} - -NETEQTEST_RTPpacket::~NETEQTEST_RTPpacket() -{ - if(_datagram) - { - delete [] _datagram; - } -} - -void NETEQTEST_RTPpacket::reset() -{ - if(_datagram) { - delete [] _datagram; - } - _datagram = NULL; - _memSize = 0; - _datagramLen = -1; - _payloadLen = 0; - _payloadPtr = NULL; - _receiveTime = 0; - memset(&_rtpInfo, 0, sizeof(_rtpInfo)); - _rtpParsed = false; - -} - -int NETEQTEST_RTPpacket::skipFileHeader(FILE *fp) -{ - if (!fp) { - return -1; - } - - const int kFirstLineLength = 40; - char firstline[kFirstLineLength]; - if (fgets(firstline, kFirstLineLength, fp) == NULL) { - return -1; - } - if (strncmp(firstline, "#!rtpplay", 9) == 0) { - if (strncmp(firstline, "#!rtpplay1.0", 12) != 0) { - return -1; - } - } - else if (strncmp(firstline, "#!RTPencode", 11) == 0) { - if (strncmp(firstline, "#!RTPencode1.0", 14) != 0) { - return -1; - } - } - else - { - return -1; - } - - const int kRtpDumpHeaderSize = 4 + 4 + 4 + 2 + 2; - if (fseek(fp, kRtpDumpHeaderSize, SEEK_CUR) != 0) - { - return -1; - } - return 0; -} - -int NETEQTEST_RTPpacket::readFromFile(FILE *fp) -{ - if(!fp) - { - return(-1); - } - - uint16_t length, plen; - uint32_t offset; - int packetLen; - - bool readNextPacket = true; - while (readNextPacket) { - readNextPacket = false; - if (fread(&length,2,1,fp)==0) - { - reset(); - return(-2); - } - length = ntohs(length); - - if (fread(&plen,2,1,fp)==0) - { - reset(); - return(-1); - } - packetLen = ntohs(plen); - - if (fread(&offset,4,1,fp)==0) - { - reset(); - return(-1); - } - // store in local variable until we have passed the reset below - uint32_t receiveTime = ntohl(offset); - - // Use length here because a plen of 0 specifies rtcp - length = (uint16_t) (length - _kRDHeaderLen); - - // check buffer size - if (_datagram && _memSize < length) - { - reset(); - } - - if (!_datagram) - { - _datagram = new uint8_t[length]; - _memSize = length; - } - - if (fread((unsigned short *) _datagram,1,length,fp) != length) - { - reset(); - return(-1); - } - - _datagramLen = length; - _receiveTime = receiveTime; - - if (!_blockList.empty() && _blockList.count(payloadType()) > 0) - { - readNextPacket = true; - } - } - - _rtpParsed = false; - return(packetLen); - -} - - -int NETEQTEST_RTPpacket::readFixedFromFile(FILE *fp, size_t length) -{ - if (!fp) - { - return -1; - } - - // check buffer size - if (_datagram && _memSize < static_cast(length)) - { - reset(); - } - - if (!_datagram) - { - _datagram = new uint8_t[length]; - _memSize = length; - } - - if (fread(_datagram, 1, length, fp) != length) - { - reset(); - return -1; - } - - _datagramLen = length; - _receiveTime = 0; - - if (!_blockList.empty() && _blockList.count(payloadType()) > 0) - { - // discard this payload - return readFromFile(fp); - } - - _rtpParsed = false; - return length; - -} - - -int NETEQTEST_RTPpacket::writeToFile(FILE *fp) -{ - if (!fp) - { - return -1; - } - - uint16_t length, plen; - uint32_t offset; - - // length including RTPplay header - length = htons(_datagramLen + _kRDHeaderLen); - if (fwrite(&length, 2, 1, fp) != 1) - { - return -1; - } - - // payload length - plen = htons(_datagramLen); - if (fwrite(&plen, 2, 1, fp) != 1) - { - return -1; - } - - // offset (=receive time) - offset = htonl(_receiveTime); - if (fwrite(&offset, 4, 1, fp) != 1) - { - return -1; - } - - - // write packet data - if (fwrite(_datagram, 1, _datagramLen, fp) != - static_cast(_datagramLen)) - { - return -1; - } - - return _datagramLen + _kRDHeaderLen; // total number of bytes written - -} - - -void NETEQTEST_RTPpacket::blockPT(uint8_t pt) -{ - _blockList[pt] = true; -} - - -void NETEQTEST_RTPpacket::parseHeader() -{ - if (_rtpParsed) - { - // nothing to do - return; - } - - if (_datagramLen < _kBasicHeaderLen) - { - // corrupt packet? - return; - } - - _payloadLen = parseRTPheader(&_payloadPtr); - - _rtpParsed = true; - - return; - -} - -void NETEQTEST_RTPpacket::parseHeader(webrtc::WebRtcRTPHeader* rtp_header) { - if (!_rtpParsed) { - parseHeader(); - } - if (rtp_header) { - rtp_header->header.markerBit = _rtpInfo.header.markerBit; - rtp_header->header.payloadType = _rtpInfo.header.payloadType; - rtp_header->header.sequenceNumber = _rtpInfo.header.sequenceNumber; - rtp_header->header.timestamp = _rtpInfo.header.timestamp; - rtp_header->header.ssrc = _rtpInfo.header.ssrc; - } -} - -const webrtc::WebRtcRTPHeader* NETEQTEST_RTPpacket::RTPinfo() const -{ - if (_rtpParsed) - { - return &_rtpInfo; - } - else - { - return NULL; - } -} - -uint8_t * NETEQTEST_RTPpacket::datagram() const -{ - if (_datagramLen > 0) - { - return _datagram; - } - else - { - return NULL; - } -} - -uint8_t * NETEQTEST_RTPpacket::payload() const -{ - if (_payloadLen > 0) - { - return _payloadPtr; - } - else - { - return NULL; - } -} - -int16_t NETEQTEST_RTPpacket::payloadLen() -{ - parseHeader(); - return _payloadLen; -} - -int16_t NETEQTEST_RTPpacket::dataLen() const -{ - return _datagramLen; -} - -bool NETEQTEST_RTPpacket::isParsed() const -{ - return _rtpParsed; -} - -bool NETEQTEST_RTPpacket::isLost() const -{ - return _lost; -} - -uint8_t NETEQTEST_RTPpacket::payloadType() const -{ - webrtc::WebRtcRTPHeader tempRTPinfo; - - if(_datagram && _datagramLen >= _kBasicHeaderLen) - { - parseRTPheader(&tempRTPinfo); - } - else - { - return 0; - } - - return tempRTPinfo.header.payloadType; -} - -uint16_t NETEQTEST_RTPpacket::sequenceNumber() const -{ - webrtc::WebRtcRTPHeader tempRTPinfo; - - if(_datagram && _datagramLen >= _kBasicHeaderLen) - { - parseRTPheader(&tempRTPinfo); - } - else - { - return 0; - } - - return tempRTPinfo.header.sequenceNumber; -} - -uint32_t NETEQTEST_RTPpacket::timeStamp() const -{ - webrtc::WebRtcRTPHeader tempRTPinfo; - - if(_datagram && _datagramLen >= _kBasicHeaderLen) - { - parseRTPheader(&tempRTPinfo); - } - else - { - return 0; - } - - return tempRTPinfo.header.timestamp; -} - -uint32_t NETEQTEST_RTPpacket::SSRC() const -{ - webrtc::WebRtcRTPHeader tempRTPinfo; - - if(_datagram && _datagramLen >= _kBasicHeaderLen) - { - parseRTPheader(&tempRTPinfo); - } - else - { - return 0; - } - - return tempRTPinfo.header.ssrc; -} - -uint8_t NETEQTEST_RTPpacket::markerBit() const -{ - webrtc::WebRtcRTPHeader tempRTPinfo; - - if(_datagram && _datagramLen >= _kBasicHeaderLen) - { - parseRTPheader(&tempRTPinfo); - } - else - { - return 0; - } - - return tempRTPinfo.header.markerBit; -} - - - -int NETEQTEST_RTPpacket::setPayloadType(uint8_t pt) -{ - - if (_datagramLen < 12) - { - return -1; - } - - if (!_rtpParsed) - { - _rtpInfo.header.payloadType = pt; - } - - _datagram[1]=(unsigned char)(pt & 0xFF); - - return 0; - -} - -int NETEQTEST_RTPpacket::setSequenceNumber(uint16_t sn) -{ - - if (_datagramLen < 12) - { - return -1; - } - - if (!_rtpParsed) - { - _rtpInfo.header.sequenceNumber = sn; - } - - _datagram[2]=(unsigned char)((sn>>8)&0xFF); - _datagram[3]=(unsigned char)((sn)&0xFF); - - return 0; - -} - -int NETEQTEST_RTPpacket::setTimeStamp(uint32_t ts) -{ - - if (_datagramLen < 12) - { - return -1; - } - - if (!_rtpParsed) - { - _rtpInfo.header.timestamp = ts; - } - - _datagram[4]=(unsigned char)((ts>>24)&0xFF); - _datagram[5]=(unsigned char)((ts>>16)&0xFF); - _datagram[6]=(unsigned char)((ts>>8)&0xFF); - _datagram[7]=(unsigned char)(ts & 0xFF); - - return 0; - -} - -int NETEQTEST_RTPpacket::setSSRC(uint32_t ssrc) -{ - - if (_datagramLen < 12) - { - return -1; - } - - if (!_rtpParsed) - { - _rtpInfo.header.ssrc = ssrc; - } - - _datagram[8]=(unsigned char)((ssrc>>24)&0xFF); - _datagram[9]=(unsigned char)((ssrc>>16)&0xFF); - _datagram[10]=(unsigned char)((ssrc>>8)&0xFF); - _datagram[11]=(unsigned char)(ssrc & 0xFF); - - return 0; - -} - -int NETEQTEST_RTPpacket::setMarkerBit(uint8_t mb) -{ - - if (_datagramLen < 12) - { - return -1; - } - - if (_rtpParsed) - { - _rtpInfo.header.markerBit = mb; - } - - if (mb) - { - _datagram[0] |= 0x01; - } - else - { - _datagram[0] &= 0xFE; - } - - return 0; - -} - -int NETEQTEST_RTPpacket::setRTPheader(const webrtc::WebRtcRTPHeader* RTPinfo) -{ - if (_datagramLen < 12) - { - // this packet is not ok - return -1; - } - - makeRTPheader(_datagram, - RTPinfo->header.payloadType, - RTPinfo->header.sequenceNumber, - RTPinfo->header.timestamp, - RTPinfo->header.ssrc, - RTPinfo->header.markerBit); - - return 0; -} - - -int NETEQTEST_RTPpacket::splitStereo(NETEQTEST_RTPpacket* slaveRtp, - enum stereoModes mode) -{ - // if mono, do nothing - if (mode == stereoModeMono) - { - return 0; - } - - // check that the RTP header info is parsed - parseHeader(); - - // start by copying the main rtp packet - *slaveRtp = *this; - - if(_payloadLen == 0) - { - // do no more - return 0; - } - - if(_payloadLen%2 != 0) - { - // length must be a factor of 2 - return -1; - } - - switch(mode) - { - case stereoModeSample1: - { - // sample based codec with 1-byte samples - splitStereoSample(slaveRtp, 1 /* 1 byte/sample */); - break; - } - case stereoModeSample2: - { - // sample based codec with 2-byte samples - splitStereoSample(slaveRtp, 2 /* 2 bytes/sample */); - break; - } - case stereoModeFrame: - { - // frame based codec - splitStereoFrame(slaveRtp); - break; - } - case stereoModeDuplicate: - { - // frame based codec, send the whole packet to both master and slave - splitStereoDouble(slaveRtp); - break; - } - case stereoModeMono: - { - assert(false); - return -1; - } - } - - return 0; -} - - -void NETEQTEST_RTPpacket::makeRTPheader(unsigned char* rtp_data, uint8_t payloadType, uint16_t seqNo, uint32_t timestamp, uint32_t ssrc, uint8_t markerBit) const -{ - rtp_data[0]=(unsigned char)0x80; - if (markerBit) - { - rtp_data[0] |= 0x01; - } - else - { - rtp_data[0] &= 0xFE; - } - rtp_data[1]=(unsigned char)(payloadType & 0xFF); - rtp_data[2]=(unsigned char)((seqNo>>8)&0xFF); - rtp_data[3]=(unsigned char)((seqNo)&0xFF); - rtp_data[4]=(unsigned char)((timestamp>>24)&0xFF); - rtp_data[5]=(unsigned char)((timestamp>>16)&0xFF); - - rtp_data[6]=(unsigned char)((timestamp>>8)&0xFF); - rtp_data[7]=(unsigned char)(timestamp & 0xFF); - - rtp_data[8]=(unsigned char)((ssrc>>24)&0xFF); - rtp_data[9]=(unsigned char)((ssrc>>16)&0xFF); - - rtp_data[10]=(unsigned char)((ssrc>>8)&0xFF); - rtp_data[11]=(unsigned char)(ssrc & 0xFF); -} - -uint16_t - NETEQTEST_RTPpacket::parseRTPheader(webrtc::WebRtcRTPHeader* RTPinfo, - uint8_t **payloadPtr) const -{ - int16_t *rtp_data = (int16_t *) _datagram; - int i_P, i_X, i_CC; - - assert(_datagramLen >= 12); - parseBasicHeader(RTPinfo, &i_P, &i_X, &i_CC); - - int i_startPosition = calcHeaderLength(i_X, i_CC); - - int i_padlength = calcPadLength(i_P); - - if (payloadPtr) - { - *payloadPtr = (uint8_t*) &rtp_data[i_startPosition >> 1]; - } - - return (uint16_t) (_datagramLen - i_startPosition - i_padlength); -} - - -void NETEQTEST_RTPpacket::parseBasicHeader(webrtc::WebRtcRTPHeader* RTPinfo, - int *i_P, int *i_X, int *i_CC) const -{ - int16_t *rtp_data = (int16_t *) _datagram; - if (_datagramLen < 12) - { - assert(false); - return; - } - - *i_P=(((uint16_t)(rtp_data[0] & 0x20))>>5); /* Extract the P bit */ - *i_X=(((uint16_t)(rtp_data[0] & 0x10))>>4); /* Extract the X bit */ - *i_CC=(uint16_t)(rtp_data[0] & 0xF); /* Get the CC number */ - /* Get the marker bit */ - RTPinfo->header.markerBit = (uint8_t) ((rtp_data[0] >> 15) & 0x01); - /* Get the coder type */ - RTPinfo->header.payloadType = (uint8_t) ((rtp_data[0] >> 8) & 0x7F); - /* Get the packet number */ - RTPinfo->header.sequenceNumber = - ((( ((uint16_t)rtp_data[1]) >> 8) & 0xFF) | - ( ((uint16_t)(rtp_data[1] & 0xFF)) << 8)); - /* Get timestamp */ - RTPinfo->header.timestamp = ((((uint16_t)rtp_data[2]) & 0xFF) << 24) | - ((((uint16_t)rtp_data[2]) & 0xFF00) << 8) | - ((((uint16_t)rtp_data[3]) >> 8) & 0xFF) | - ((((uint16_t)rtp_data[3]) & 0xFF) << 8); - /* Get the SSRC */ - RTPinfo->header.ssrc = ((((uint16_t)rtp_data[4]) & 0xFF) << 24) | - ((((uint16_t)rtp_data[4]) & 0xFF00) << 8) | - ((((uint16_t)rtp_data[5]) >> 8) & 0xFF) | - ((((uint16_t)rtp_data[5]) & 0xFF) << 8); -} - -int NETEQTEST_RTPpacket::calcHeaderLength(int i_X, int i_CC) const -{ - int i_extlength = 0; - int16_t *rtp_data = (int16_t *) _datagram; - - if (i_X == 1) - { - // Extension header exists. - // Find out how many int32_t it consists of. - assert(_datagramLen > 2 * (7 + 2 * i_CC)); - if (_datagramLen > 2 * (7 + 2 * i_CC)) - { - i_extlength = (((((uint16_t) rtp_data[7 + 2 * i_CC]) >> 8) - & 0xFF) | (((uint16_t) (rtp_data[7 + 2 * i_CC] & 0xFF)) - << 8)) + 1; - } - } - - return 12 + 4 * i_extlength + 4 * i_CC; -} - -int NETEQTEST_RTPpacket::calcPadLength(int i_P) const -{ - int16_t *rtp_data = (int16_t *) _datagram; - if (i_P == 1) - { - /* Padding exists. Find out how many bytes the padding consists of. */ - if (_datagramLen & 0x1) - { - /* odd number of bytes => last byte in higher byte */ - return rtp_data[_datagramLen >> 1] & 0xFF; - } - else - { - /* even number of bytes => last byte in lower byte */ - return ((uint16_t) rtp_data[(_datagramLen >> 1) - 1]) >> 8; - } - } - return 0; -} - -void NETEQTEST_RTPpacket::splitStereoSample(NETEQTEST_RTPpacket* slaveRtp, - int stride) -{ - if(!_payloadPtr || !slaveRtp || !slaveRtp->_payloadPtr - || _payloadLen <= 0 || slaveRtp->_memSize < _memSize) - { - return; - } - - uint8_t *readDataPtr = _payloadPtr; - uint8_t *writeDataPtr = _payloadPtr; - uint8_t *slaveData = slaveRtp->_payloadPtr; - - while (readDataPtr - _payloadPtr < _payloadLen) - { - // master data - for (int ix = 0; ix < stride; ix++) { - *writeDataPtr = *readDataPtr; - writeDataPtr++; - readDataPtr++; - } - - // slave data - for (int ix = 0; ix < stride; ix++) { - *slaveData = *readDataPtr; - slaveData++; - readDataPtr++; - } - } - - _payloadLen /= 2; - slaveRtp->_payloadLen = _payloadLen; -} - - -void NETEQTEST_RTPpacket::splitStereoFrame(NETEQTEST_RTPpacket* slaveRtp) -{ - if(!_payloadPtr || !slaveRtp || !slaveRtp->_payloadPtr - || _payloadLen <= 0 || slaveRtp->_memSize < _memSize) - { - return; - } - - memmove(slaveRtp->_payloadPtr, _payloadPtr + _payloadLen/2, _payloadLen/2); - - _payloadLen /= 2; - slaveRtp->_payloadLen = _payloadLen; -} -void NETEQTEST_RTPpacket::splitStereoDouble(NETEQTEST_RTPpacket* slaveRtp) -{ - if(!_payloadPtr || !slaveRtp || !slaveRtp->_payloadPtr - || _payloadLen <= 0 || slaveRtp->_memSize < _memSize) - { - return; - } - - memcpy(slaveRtp->_payloadPtr, _payloadPtr, _payloadLen); - slaveRtp->_payloadLen = _payloadLen; -} - -// Get the RTP header for the RED payload indicated by argument index. -// The first RED payload is index = 0. -int NETEQTEST_RTPpacket::extractRED(int index, webrtc::WebRtcRTPHeader& red) -{ -// -// 0 1 2 3 -// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// |1| block PT | timestamp offset | block length | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// |1| ... | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// |0| block PT | -// +-+-+-+-+-+-+-+-+ -// - - parseHeader(); - - uint8_t* ptr = payload(); - uint8_t* payloadEndPtr = ptr + payloadLen(); - int num_encodings = 0; - int total_len = 0; - - while ((ptr < payloadEndPtr) && (*ptr & 0x80)) - { - int len = ((ptr[2] & 0x03) << 8) + ptr[3]; - if (num_encodings == index) - { - // Header found. - red.header.payloadType = ptr[0] & 0x7F; - uint32_t offset = (ptr[1] << 6) + ((ptr[2] & 0xFC) >> 2); - red.header.sequenceNumber = sequenceNumber(); - red.header.timestamp = timeStamp() - offset; - red.header.markerBit = markerBit(); - red.header.ssrc = SSRC(); - return len; - } - ++num_encodings; - total_len += len; - ptr += 4; - } - if ((ptr < payloadEndPtr) && (num_encodings == index)) - { - // Last header. - red.header.payloadType = ptr[0] & 0x7F; - red.header.sequenceNumber = sequenceNumber(); - red.header.timestamp = timeStamp(); - red.header.markerBit = markerBit(); - red.header.ssrc = SSRC(); - ++ptr; - return payloadLen() - (ptr - payload()) - total_len; - } - return -1; -} - -// Randomize the payload, not the RTP header. -void NETEQTEST_RTPpacket::scramblePayload(void) -{ - parseHeader(); - - for (int i = 0; i < _payloadLen; ++i) - { - _payloadPtr[i] = static_cast(rand()); - } -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/NETEQTEST_RTPpacket.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/NETEQTEST_RTPpacket.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/NETEQTEST_RTPpacket.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/NETEQTEST_RTPpacket.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef NETEQTEST_RTPPACKET_H -#define NETEQTEST_RTPPACKET_H - -#include -#include -#include "webrtc/typedefs.h" -#include "webrtc/modules/interface/module_common_types.h" - -enum stereoModes { - stereoModeMono, - stereoModeSample1, - stereoModeSample2, - stereoModeFrame, - stereoModeDuplicate -}; - -class NETEQTEST_RTPpacket -{ -public: - NETEQTEST_RTPpacket(); - bool operator !() const { return (dataLen() < 0); }; - virtual ~NETEQTEST_RTPpacket(); - void reset(); - static int skipFileHeader(FILE *fp); - virtual int readFromFile(FILE *fp); - int readFixedFromFile(FILE *fp, size_t len); - virtual int writeToFile(FILE *fp); - void blockPT(uint8_t pt); - //int16_t payloadType(); - virtual void parseHeader(); - void parseHeader(webrtc::WebRtcRTPHeader* rtp_header); - const webrtc::WebRtcRTPHeader* RTPinfo() const; - uint8_t * datagram() const; - uint8_t * payload() const; - int16_t payloadLen(); - int16_t dataLen() const; - bool isParsed() const; - bool isLost() const; - uint32_t time() const { return _receiveTime; }; - - uint8_t payloadType() const; - uint16_t sequenceNumber() const; - uint32_t timeStamp() const; - uint32_t SSRC() const; - uint8_t markerBit() const; - - int setPayloadType(uint8_t pt); - int setSequenceNumber(uint16_t sn); - int setTimeStamp(uint32_t ts); - int setSSRC(uint32_t ssrc); - int setMarkerBit(uint8_t mb); - void setTime(uint32_t receiveTime) { _receiveTime = receiveTime; }; - - int setRTPheader(const webrtc::WebRtcRTPHeader* RTPinfo); - - int splitStereo(NETEQTEST_RTPpacket* slaveRtp, enum stereoModes mode); - - int extractRED(int index, webrtc::WebRtcRTPHeader& red); - - void scramblePayload(void); - - uint8_t * _datagram; - uint8_t * _payloadPtr; - int _memSize; - int16_t _datagramLen; - int16_t _payloadLen; - webrtc::WebRtcRTPHeader _rtpInfo; - bool _rtpParsed; - uint32_t _receiveTime; - bool _lost; - std::map _blockList; - -protected: - static const int _kRDHeaderLen; - static const int _kBasicHeaderLen; - - void parseBasicHeader(webrtc::WebRtcRTPHeader* RTPinfo, int *i_P, int *i_X, - int *i_CC) const; - int calcHeaderLength(int i_X, int i_CC) const; - -private: - void makeRTPheader(unsigned char* rtp_data, uint8_t payloadType, - uint16_t seqNo, uint32_t timestamp, - uint32_t ssrc, uint8_t markerBit) const; - uint16_t parseRTPheader(webrtc::WebRtcRTPHeader* RTPinfo, - uint8_t **payloadPtr = NULL) const; - uint16_t parseRTPheader(uint8_t **payloadPtr = NULL) - { return parseRTPheader(&_rtpInfo, payloadPtr);}; - int calcPadLength(int i_P) const; - void splitStereoSample(NETEQTEST_RTPpacket* slaveRtp, int stride); - void splitStereoFrame(NETEQTEST_RTPpacket* slaveRtp); - void splitStereoDouble(NETEQTEST_RTPpacket* slaveRtp); -}; - -#endif //NETEQTEST_RTPPACKET_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/PayloadTypes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/PayloadTypes.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/PayloadTypes.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/PayloadTypes.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* PayloadTypes.h */ -/* Used by NetEqRTPplay application */ - -/* RTP defined codepoints */ -#define NETEQ_CODEC_PCMU_PT 0 -#define NETEQ_CODEC_GSMFR_PT 3 -#define NETEQ_CODEC_G723_PT 4 -#define NETEQ_CODEC_DVI4_PT 125 // 8 kHz version -//#define NETEQ_CODEC_DVI4_16_PT 6 // 16 kHz version -#define NETEQ_CODEC_PCMA_PT 8 -#define NETEQ_CODEC_G722_PT 9 -#define NETEQ_CODEC_CN_PT 13 -//#define NETEQ_CODEC_G728_PT 15 -//#define NETEQ_CODEC_DVI4_11_PT 16 // 11.025 kHz version -//#define NETEQ_CODEC_DVI4_22_PT 17 // 22.050 kHz version -#define NETEQ_CODEC_G729_PT 18 - -/* Dynamic RTP codepoints as defined in VoiceEngine (file VEAPI.cpp) */ -#define NETEQ_CODEC_IPCMWB_PT 97 -#define NETEQ_CODEC_SPEEX8_PT 98 -#define NETEQ_CODEC_SPEEX16_PT 99 -#define NETEQ_CODEC_EG711U_PT 100 -#define NETEQ_CODEC_EG711A_PT 101 -#define NETEQ_CODEC_ILBC_PT 102 -#define NETEQ_CODEC_ISAC_PT 103 -#define NETEQ_CODEC_ISACLC_PT 119 -#define NETEQ_CODEC_ISACSWB_PT 104 -#define NETEQ_CODEC_AVT_PT 106 -#define NETEQ_CODEC_G722_1_16_PT 108 -#define NETEQ_CODEC_G722_1_24_PT 109 -#define NETEQ_CODEC_G722_1_32_PT 110 -#define NETEQ_CODEC_SC3_PT 111 -#define NETEQ_CODEC_AMR_PT 112 -#define NETEQ_CODEC_GSMEFR_PT 113 -//#define NETEQ_CODEC_ILBCRCU_PT 114 -#define NETEQ_CODEC_G726_16_PT 115 -#define NETEQ_CODEC_G726_24_PT 116 -#define NETEQ_CODEC_G726_32_PT 121 -#define NETEQ_CODEC_RED_PT 117 -#define NETEQ_CODEC_G726_40_PT 118 -//#define NETEQ_CODEC_ENERGY_PT 120 -#define NETEQ_CODEC_CN_WB_PT 105 -#define NETEQ_CODEC_CN_SWB_PT 126 -#define NETEQ_CODEC_G729_1_PT 107 -#define NETEQ_CODEC_G729D_PT 123 -#define NETEQ_CODEC_MELPE_PT 124 -#define NETEQ_CODEC_CELT32_PT 114 - -/* Extra dynamic codepoints */ -#define NETEQ_CODEC_AMRWB_PT 120 -#define NETEQ_CODEC_PCM16B_PT 93 -#define NETEQ_CODEC_PCM16B_WB_PT 94 -#define NETEQ_CODEC_PCM16B_SWB32KHZ_PT 95 -#define NETEQ_CODEC_PCM16B_SWB48KHZ_PT 96 -#define NETEQ_CODEC_MPEG4AAC_PT 122 - - -/* Not default in VoiceEngine */ -#define NETEQ_CODEC_G722_1C_24_PT 84 -#define NETEQ_CODEC_G722_1C_32_PT 85 -#define NETEQ_CODEC_G722_1C_48_PT 86 - -#define NETEQ_CODEC_SILK_8_PT 80 -#define NETEQ_CODEC_SILK_12_PT 81 -#define NETEQ_CODEC_SILK_16_PT 82 -#define NETEQ_CODEC_SILK_24_PT 83 - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPanalyze.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPanalyze.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPanalyze.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPanalyze.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include - -#include "webrtc/modules/audio_coding/neteq4/test/NETEQTEST_DummyRTPpacket.h" -#include "webrtc/modules/audio_coding/neteq4/test/NETEQTEST_RTPpacket.h" - -//#define WEBRTC_DUMMY_RTP - -enum { - kRedPayloadType = 127 -}; - -int main(int argc, char* argv[]) { - FILE* in_file = fopen(argv[1], "rb"); - if (!in_file) { - printf("Cannot open input file %s\n", argv[1]); - return -1; - } - printf("Input file: %s\n", argv[1]); - - FILE* out_file = fopen(argv[2], "wt"); - if (!out_file) { - printf("Cannot open output file %s\n", argv[2]); - return -1; - } - printf("Output file: %s\n\n", argv[2]); - - // Print file header. - fprintf(out_file, "SeqNo TimeStamp SendTime Size PT M\n"); - - // Read file header. - NETEQTEST_RTPpacket::skipFileHeader(in_file); -#ifdef WEBRTC_DUMMY_RTP - NETEQTEST_DummyRTPpacket packet; -#else - NETEQTEST_RTPpacket packet; -#endif - - while (packet.readFromFile(in_file) >= 0) { - // Write packet data to file. - fprintf(out_file, "%5u %10u %10u %5i %5i %2i\n", - packet.sequenceNumber(), packet.timeStamp(), packet.time(), - packet.dataLen(), packet.payloadType(), packet.markerBit()); - if (packet.payloadType() == kRedPayloadType) { - webrtc::WebRtcRTPHeader red_header; - int len; - int red_index = 0; - while ((len = packet.extractRED(red_index++, red_header)) >= 0) { - fprintf(out_file, "* %5u %10u %10u %5i %5i\n", - red_header.header.sequenceNumber, red_header.header.timestamp, - packet.time(), len, red_header.header.payloadType); - } - assert(red_index > 1); // We must get at least one payload. - } - } - - fclose(in_file); - fclose(out_file); - - return 0; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPcat.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPcat.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPcat.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPcat.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include -#include - -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq4/test/NETEQTEST_RTPpacket.h" - -#define FIRSTLINELEN 40 - -int main(int argc, char* argv[]) { - if (argc < 3) { - printf("Usage: RTPcat in1.rtp int2.rtp [...] out.rtp\n"); - exit(1); - } - - FILE* in_file = fopen(argv[1], "rb"); - if (!in_file) { - printf("Cannot open input file %s\n", argv[1]); - return -1; - } - - FILE* out_file = fopen(argv[argc - 1], "wb"); // Last parameter is out file. - if (!out_file) { - printf("Cannot open output file %s\n", argv[argc - 1]); - return -1; - } - printf("Output RTP file: %s\n\n", argv[argc - 1]); - - // Read file header and write directly to output file. - char firstline[FIRSTLINELEN]; - const unsigned int kRtpDumpHeaderSize = 4 + 4 + 4 + 2 + 2; - EXPECT_TRUE(fgets(firstline, FIRSTLINELEN, in_file) != NULL); - EXPECT_GT(fputs(firstline, out_file), 0); - EXPECT_EQ(kRtpDumpHeaderSize, fread(firstline, 1, kRtpDumpHeaderSize, - in_file)); - EXPECT_EQ(kRtpDumpHeaderSize, fwrite(firstline, 1, kRtpDumpHeaderSize, - out_file)); - - // Close input file and re-open it later (easier to write the loop below). - fclose(in_file); - - for (int i = 1; i < argc - 1; i++) { - in_file = fopen(argv[i], "rb"); - if (!in_file) { - printf("Cannot open input file %s\n", argv[i]); - return -1; - } - printf("Input RTP file: %s\n", argv[i]); - - NETEQTEST_RTPpacket::skipFileHeader(in_file); - NETEQTEST_RTPpacket packet; - int pack_len = packet.readFromFile(in_file); - if (pack_len < 0) { - exit(1); - } - while (pack_len >= 0) { - packet.writeToFile(out_file); - pack_len = packet.readFromFile(in_file); - } - fclose(in_file); - } - fclose(out_file); - return 0; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPchange.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPchange.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPchange.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPchange.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include -#include - -#include "webrtc/modules/audio_coding/neteq4/test/NETEQTEST_DummyRTPpacket.h" -#include "webrtc/modules/audio_coding/neteq4/test/NETEQTEST_RTPpacket.h" - -#define FIRSTLINELEN 40 -//#define WEBRTC_DUMMY_RTP - -static bool pktCmp(NETEQTEST_RTPpacket *a, NETEQTEST_RTPpacket *b) { - return (a->time() < b->time()); -} - -int main(int argc, char* argv[]) { - FILE* in_file = fopen(argv[1], "rb"); - if (!in_file) { - printf("Cannot open input file %s\n", argv[1]); - return -1; - } - printf("Input RTP file: %s\n", argv[1]); - - FILE* stat_file = fopen(argv[2], "rt"); - if (!stat_file) { - printf("Cannot open timing file %s\n", argv[2]); - return -1; - } - printf("Timing file: %s\n", argv[2]); - - FILE* out_file = fopen(argv[3], "wb"); - if (!out_file) { - printf("Cannot open output file %s\n", argv[3]); - return -1; - } - printf("Output RTP file: %s\n\n", argv[3]); - - // Read all statistics and insert into map. - // Read first line. - char temp_str[100]; - if (fgets(temp_str, 100, stat_file) == NULL) { - printf("Failed to read timing file %s\n", argv[2]); - return -1; - } - // Define map. - std::map, uint32_t> packet_stats; - uint16_t seq_no; - uint32_t ts; - uint32_t send_time; - - while (fscanf(stat_file, - "%hu %u %u %*i %*i\n", &seq_no, &ts, &send_time) == 3) { - std::pair - temp_pair = std::pair(seq_no, ts); - - packet_stats[temp_pair] = send_time; - } - - fclose(stat_file); - - // Read file header and write directly to output file. - char first_line[FIRSTLINELEN]; - if (fgets(first_line, FIRSTLINELEN, in_file) == NULL) { - printf("Failed to read first line of input file %s\n", argv[1]); - return -1; - } - fputs(first_line, out_file); - // start_sec + start_usec + source + port + padding - const unsigned int kRtpDumpHeaderSize = 4 + 4 + 4 + 2 + 2; - if (fread(first_line, 1, kRtpDumpHeaderSize, in_file) - != kRtpDumpHeaderSize) { - printf("Failed to read RTP dump header from input file %s\n", argv[1]); - return -1; - } - if (fwrite(first_line, 1, kRtpDumpHeaderSize, out_file) - != kRtpDumpHeaderSize) { - printf("Failed to write RTP dump header to output file %s\n", argv[3]); - return -1; - } - - std::vector packet_vec; - - while (1) { - // Insert in vector. -#ifdef WEBRTC_DUMMY_RTP - NETEQTEST_RTPpacket *new_packet = new NETEQTEST_DummyRTPpacket(); -#else - NETEQTEST_RTPpacket *new_packet = new NETEQTEST_RTPpacket(); -#endif - if (new_packet->readFromFile(in_file) < 0) { - // End of file. - break; - } - - // Look for new send time in statistics vector. - std::pair temp_pair = - std::pair(new_packet->sequenceNumber(), - new_packet->timeStamp()); - - uint32_t new_send_time = packet_stats[temp_pair]; - new_packet->setTime(new_send_time); // Set new send time. - packet_vec.push_back(new_packet); // Insert in vector. - } - - // Sort the vector according to send times. - std::sort(packet_vec.begin(), packet_vec.end(), pktCmp); - - std::vector::iterator it; - for (it = packet_vec.begin(); it != packet_vec.end(); it++) { - // Write to out file. - if ((*it)->writeToFile(out_file) < 0) { - printf("Error writing to file\n"); - return -1; - } - // Delete packet. - delete *it; - } - - fclose(in_file); - fclose(out_file); - - return 0; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPencode.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPencode.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPencode.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPencode.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,1826 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -//TODO(hlundin): Reformat file to meet style guide. - -/* header includes */ -#include -#include -#include -#ifdef WIN32 -#include -#endif -#ifdef WEBRTC_LINUX -#include -#endif - -#include - -#include "webrtc/typedefs.h" -// needed for NetEqDecoder -#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h" -#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" - -/************************/ -/* Define payload types */ -/************************/ - -#include "PayloadTypes.h" - - - -/*********************/ -/* Misc. definitions */ -/*********************/ - -#define STOPSENDTIME 3000 -#define RESTARTSENDTIME 0 //162500 -#define FIRSTLINELEN 40 -#define CHECK_NOT_NULL(a) if((a)==0){printf("\n %s \n line: %d \nerror at %s\n",__FILE__,__LINE__,#a );return(-1);} - -//#define MULTIPLE_SAME_TIMESTAMP -#define REPEAT_PACKET_DISTANCE 17 -#define REPEAT_PACKET_COUNT 1 // number of extra packets to send - -//#define INSERT_OLD_PACKETS -#define OLD_PACKET 5 // how many seconds too old should the packet be? - -//#define TIMESTAMP_WRAPAROUND - -//#define RANDOM_DATA -//#define RANDOM_PAYLOAD_DATA -#define RANDOM_SEED 10 - -//#define INSERT_DTMF_PACKETS -//#define NO_DTMF_OVERDUB -#define DTMF_PACKET_INTERVAL 2000 -#define DTMF_DURATION 500 - -#define STEREO_MODE_FRAME 0 -#define STEREO_MODE_SAMPLE_1 1 //1 octet per sample -#define STEREO_MODE_SAMPLE_2 2 //2 octets per sample - -/*************************/ -/* Function declarations */ -/*************************/ - -void NetEQTest_GetCodec_and_PT(char * name, webrtc::NetEqDecoder *codec, int *PT, int frameLen, int *fs, int *bitrate, int *useRed); -int NetEQTest_init_coders(webrtc::NetEqDecoder coder, int enc_frameSize, int bitrate, int sampfreq , int vad, int numChannels); -void defineCodecs(webrtc::NetEqDecoder *usedCodec, int *noOfCodecs ); -int NetEQTest_free_coders(webrtc::NetEqDecoder coder, int numChannels); -int NetEQTest_encode(int coder, int16_t *indata, int frameLen, unsigned char * encoded,int sampleRate , int * vad, int useVAD, int bitrate, int numChannels); -void makeRTPheader(unsigned char* rtp_data, int payloadType, int seqNo, uint32_t timestamp, uint32_t ssrc); -int makeRedundantHeader(unsigned char* rtp_data, int *payloadType, int numPayloads, uint32_t *timestamp, uint16_t *blockLen, - int seqNo, uint32_t ssrc); -int makeDTMFpayload(unsigned char* payload_data, int Event, int End, int Volume, int Duration); -void stereoDeInterleave(int16_t* audioSamples, int numSamples); -void stereoInterleave(unsigned char* data, int dataLen, int stride); - -/*********************/ -/* Codec definitions */ -/*********************/ - -#include "webrtc_vad.h" - -#if ((defined CODEC_PCM16B)||(defined NETEQ_ARBITRARY_CODEC)) - #include "pcm16b.h" -#endif -#ifdef CODEC_G711 - #include "g711_interface.h" -#endif -#ifdef CODEC_G729 - #include "G729Interface.h" -#endif -#ifdef CODEC_G729_1 - #include "G729_1Interface.h" -#endif -#ifdef CODEC_AMR - #include "AMRInterface.h" - #include "AMRCreation.h" -#endif -#ifdef CODEC_AMRWB - #include "AMRWBInterface.h" - #include "AMRWBCreation.h" -#endif -#ifdef CODEC_ILBC - #include "ilbc.h" -#endif -#if (defined CODEC_ISAC || defined CODEC_ISAC_SWB) - #include "isac.h" -#endif -#ifdef NETEQ_ISACFIX_CODEC - #include "isacfix.h" - #ifdef CODEC_ISAC - #error Cannot have both ISAC and ISACfix defined. Please de-select one in the beginning of RTPencode.cpp - #endif -#endif -#ifdef CODEC_G722 - #include "g722_interface.h" -#endif -#ifdef CODEC_G722_1_24 - #include "G722_1Interface.h" -#endif -#ifdef CODEC_G722_1_32 - #include "G722_1Interface.h" -#endif -#ifdef CODEC_G722_1_16 - #include "G722_1Interface.h" -#endif -#ifdef CODEC_G722_1C_24 - #include "G722_1Interface.h" -#endif -#ifdef CODEC_G722_1C_32 - #include "G722_1Interface.h" -#endif -#ifdef CODEC_G722_1C_48 - #include "G722_1Interface.h" -#endif -#ifdef CODEC_G726 - #include "G726Creation.h" - #include "G726Interface.h" -#endif -#ifdef CODEC_GSMFR - #include "GSMFRInterface.h" - #include "GSMFRCreation.h" -#endif -#if (defined(CODEC_CNGCODEC8) || defined(CODEC_CNGCODEC16) || \ - defined(CODEC_CNGCODEC32) || defined(CODEC_CNGCODEC48)) - #include "webrtc_cng.h" -#endif -#if ((defined CODEC_SPEEX_8)||(defined CODEC_SPEEX_16)) - #include "SpeexInterface.h" -#endif -#ifdef CODEC_CELT_32 -#include "celt_interface.h" -#endif - - -/***********************************/ -/* Global codec instance variables */ -/***********************************/ - -WebRtcVadInst *VAD_inst[2]; - -#ifdef CODEC_G722 - G722EncInst *g722EncState[2]; -#endif - -#ifdef CODEC_G722_1_24 - G722_1_24_encinst_t *G722_1_24enc_inst[2]; -#endif -#ifdef CODEC_G722_1_32 - G722_1_32_encinst_t *G722_1_32enc_inst[2]; -#endif -#ifdef CODEC_G722_1_16 - G722_1_16_encinst_t *G722_1_16enc_inst[2]; -#endif -#ifdef CODEC_G722_1C_24 - G722_1C_24_encinst_t *G722_1C_24enc_inst[2]; -#endif -#ifdef CODEC_G722_1C_32 - G722_1C_32_encinst_t *G722_1C_32enc_inst[2]; -#endif -#ifdef CODEC_G722_1C_48 - G722_1C_48_encinst_t *G722_1C_48enc_inst[2]; -#endif -#ifdef CODEC_G726 - G726_encinst_t *G726enc_inst[2]; -#endif -#ifdef CODEC_G729 - G729_encinst_t *G729enc_inst[2]; -#endif -#ifdef CODEC_G729_1 - G729_1_inst_t *G729_1_inst[2]; -#endif -#ifdef CODEC_AMR - AMR_encinst_t *AMRenc_inst[2]; - int16_t AMR_bitrate; -#endif -#ifdef CODEC_AMRWB - AMRWB_encinst_t *AMRWBenc_inst[2]; - int16_t AMRWB_bitrate; -#endif -#ifdef CODEC_ILBC - iLBC_encinst_t *iLBCenc_inst[2]; -#endif -#ifdef CODEC_ISAC - ISACStruct *ISAC_inst[2]; -#endif -#ifdef NETEQ_ISACFIX_CODEC - ISACFIX_MainStruct *ISAC_inst[2]; -#endif -#ifdef CODEC_ISAC_SWB - ISACStruct *ISACSWB_inst[2]; -#endif -#ifdef CODEC_GSMFR - GSMFR_encinst_t *GSMFRenc_inst[2]; -#endif -#if (defined(CODEC_CNGCODEC8) || defined(CODEC_CNGCODEC16) || \ - defined(CODEC_CNGCODEC32) || defined(CODEC_CNGCODEC48)) - CNG_enc_inst *CNGenc_inst[2]; -#endif -#ifdef CODEC_SPEEX_8 - SPEEX_encinst_t *SPEEX8enc_inst[2]; -#endif -#ifdef CODEC_SPEEX_16 - SPEEX_encinst_t *SPEEX16enc_inst[2]; -#endif -#ifdef CODEC_CELT_32 - CELT_encinst_t *CELT32enc_inst[2]; -#endif -#ifdef CODEC_G711 - void *G711state[2]={NULL, NULL}; -#endif - - -int main(int argc, char* argv[]) -{ - int packet_size, fs; - webrtc::NetEqDecoder usedCodec; - int payloadType; - int bitrate = 0; - int useVAD, vad; - int useRed=0; - int len, enc_len; - int16_t org_data[4000]; - unsigned char rtp_data[8000]; - int16_t seqNo=0xFFF; - uint32_t ssrc=1235412312; - uint32_t timestamp=0xAC1245; - uint16_t length, plen; - uint32_t offset; - double sendtime = 0; - int red_PT[2] = {0}; - uint32_t red_TS[2] = {0}; - uint16_t red_len[2] = {0}; - int RTPheaderLen=12; - unsigned char red_data[8000]; -#ifdef INSERT_OLD_PACKETS - uint16_t old_length, old_plen; - int old_enc_len; - int first_old_packet=1; - unsigned char old_rtp_data[8000]; - int packet_age=0; -#endif -#ifdef INSERT_DTMF_PACKETS - int NTone = 1; - int DTMFfirst = 1; - uint32_t DTMFtimestamp; - bool dtmfSent = false; -#endif - bool usingStereo = false; - int stereoMode = 0; - int numChannels = 1; - - /* check number of parameters */ - if ((argc != 6) && (argc != 7)) { - /* print help text and exit */ - printf("Application to encode speech into an RTP stream.\n"); - printf("The program reads a PCM file and encodes is using the specified codec.\n"); - printf("The coded speech is packetized in RTP packest and written to the output file.\n"); - printf("The format of the RTP stream file is simlilar to that of rtpplay,\n"); - printf("but with the receive time euqal to 0 for all packets.\n"); - printf("Usage:\n\n"); - printf("%s PCMfile RTPfile frameLen codec useVAD bitrate\n", argv[0]); - printf("where:\n"); - - printf("PCMfile : PCM speech input file\n\n"); - - printf("RTPfile : RTP stream output file\n\n"); - - printf("frameLen : 80...960... Number of samples per packet (limit depends on codec)\n\n"); - - printf("codecName\n"); -#ifdef CODEC_PCM16B - printf(" : pcm16b 16 bit PCM (8kHz)\n"); -#endif -#ifdef CODEC_PCM16B_WB - printf(" : pcm16b_wb 16 bit PCM (16kHz)\n"); -#endif -#ifdef CODEC_PCM16B_32KHZ - printf(" : pcm16b_swb32 16 bit PCM (32kHz)\n"); -#endif -#ifdef CODEC_PCM16B_48KHZ - printf(" : pcm16b_swb48 16 bit PCM (48kHz)\n"); -#endif -#ifdef CODEC_G711 - printf(" : pcma g711 A-law (8kHz)\n"); -#endif -#ifdef CODEC_G711 - printf(" : pcmu g711 u-law (8kHz)\n"); -#endif -#ifdef CODEC_G729 - printf(" : g729 G729 (8kHz and 8kbps) CELP (One-Three frame(s)/packet)\n"); -#endif -#ifdef CODEC_G729_1 - printf(" : g729.1 G729.1 (16kHz) variable rate (8--32 kbps)\n"); -#endif -#ifdef CODEC_G722_1_16 - printf(" : g722.1_16 G722.1 coder (16kHz) (g722.1 with 16kbps)\n"); -#endif -#ifdef CODEC_G722_1_24 - printf(" : g722.1_24 G722.1 coder (16kHz) (the 24kbps version)\n"); -#endif -#ifdef CODEC_G722_1_32 - printf(" : g722.1_32 G722.1 coder (16kHz) (the 32kbps version)\n"); -#endif -#ifdef CODEC_G722_1C_24 - printf(" : g722.1C_24 G722.1 C coder (32kHz) (the 24kbps version)\n"); -#endif -#ifdef CODEC_G722_1C_32 - printf(" : g722.1C_32 G722.1 C coder (32kHz) (the 32kbps version)\n"); -#endif -#ifdef CODEC_G722_1C_48 - printf(" : g722.1C_48 G722.1 C coder (32kHz) (the 48kbps)\n"); -#endif - -#ifdef CODEC_G726 - printf(" : g726_16 G726 coder (8kHz) 16kbps\n"); - printf(" : g726_24 G726 coder (8kHz) 24kbps\n"); - printf(" : g726_32 G726 coder (8kHz) 32kbps\n"); - printf(" : g726_40 G726 coder (8kHz) 40kbps\n"); -#endif -#ifdef CODEC_AMR - printf(" : AMRXk Adaptive Multi Rate CELP codec (8kHz)\n"); - printf(" X = 4.75, 5.15, 5.9, 6.7, 7.4, 7.95, 10.2 or 12.2\n"); -#endif -#ifdef CODEC_AMRWB - printf(" : AMRwbXk Adaptive Multi Rate Wideband CELP codec (16kHz)\n"); - printf(" X = 7, 9, 12, 14, 16, 18, 20, 23 or 24\n"); -#endif -#ifdef CODEC_ILBC - printf(" : ilbc iLBC codec (8kHz and 13.8kbps)\n"); -#endif -#ifdef CODEC_ISAC - printf(" : isac iSAC (16kHz and 32.0 kbps). To set rate specify a rate parameter as last parameter\n"); -#endif -#ifdef CODEC_ISAC_SWB - printf(" : isacswb iSAC SWB (32kHz and 32.0-52.0 kbps). To set rate specify a rate parameter as last parameter\n"); -#endif -#ifdef CODEC_GSMFR - printf(" : gsmfr GSM FR codec (8kHz and 13kbps)\n"); -#endif -#ifdef CODEC_G722 - printf(" : g722 g722 coder (16kHz) (the 64kbps version)\n"); -#endif -#ifdef CODEC_SPEEX_8 - printf(" : speex8 speex coder (8 kHz)\n"); -#endif -#ifdef CODEC_SPEEX_16 - printf(" : speex16 speex coder (16 kHz)\n"); -#endif -#ifdef CODEC_CELT_32 - printf(" : celt32 celt coder (32 kHz)\n"); -#endif -#ifdef CODEC_RED -#ifdef CODEC_G711 - printf(" : red_pcm Redundancy RTP packet with 2*G711A frames\n"); -#endif -#ifdef CODEC_ISAC - printf(" : red_isac Redundancy RTP packet with 2*iSAC frames\n"); -#endif -#endif - printf("\n"); - -#if (defined(CODEC_CNGCODEC8) || defined(CODEC_CNGCODEC16) || \ - defined(CODEC_CNGCODEC32) || defined(CODEC_CNGCODEC48)) - printf("useVAD : 0 Voice Activity Detection is switched off\n"); - printf(" : 1 Voice Activity Detection is switched on\n\n"); -#else - printf("useVAD : 0 Voice Activity Detection switched off (on not supported)\n\n"); -#endif - printf("bitrate : Codec bitrate in bps (only applies to vbr codecs)\n\n"); - - return(0); - } - - FILE* in_file=fopen(argv[1],"rb"); - CHECK_NOT_NULL(in_file); - printf("Input file: %s\n",argv[1]); - FILE* out_file=fopen(argv[2],"wb"); - CHECK_NOT_NULL(out_file); - printf("Output file: %s\n\n",argv[2]); - packet_size=atoi(argv[3]); - CHECK_NOT_NULL(packet_size); - printf("Packet size: %i\n",packet_size); - - // check for stereo - if(argv[4][strlen(argv[4])-1] == '*') { - // use stereo - usingStereo = true; - numChannels = 2; - argv[4][strlen(argv[4])-1] = '\0'; - } - - NetEQTest_GetCodec_and_PT(argv[4], &usedCodec, &payloadType, packet_size, &fs, &bitrate, &useRed); - - if(useRed) { - RTPheaderLen = 12 + 4 + 1; /* standard RTP = 12; 4 bytes per redundant payload, except last one which is 1 byte */ - } - - useVAD=atoi(argv[5]); -#if !(defined(CODEC_CNGCODEC8) || defined(CODEC_CNGCODEC16) || \ - defined(CODEC_CNGCODEC32) || defined(CODEC_CNGCODEC48)) - if (useVAD!=0) { - printf("Error: this simulation does not support VAD/DTX/CNG\n"); - } -#endif - - // check stereo type - if(usingStereo) - { - switch(usedCodec) - { - // sample based codecs - case webrtc::kDecoderPCMu: - case webrtc::kDecoderPCMa: - case webrtc::kDecoderG722: - { - // 1 octet per sample - stereoMode = STEREO_MODE_SAMPLE_1; - break; - } - case webrtc::kDecoderPCM16B: - case webrtc::kDecoderPCM16Bwb: - case webrtc::kDecoderPCM16Bswb32kHz: - case webrtc::kDecoderPCM16Bswb48kHz: - { - // 2 octets per sample - stereoMode = STEREO_MODE_SAMPLE_2; - break; - } - - // fixed-rate frame codecs (with internal VAD) - default: - { - printf("Cannot use codec %s as stereo codec\n", argv[4]); - exit(0); - } - } - } - - if ((usedCodec == webrtc::kDecoderISAC) || (usedCodec == webrtc::kDecoderISACswb)) - { - if (argc != 7) - { - if (usedCodec == webrtc::kDecoderISAC) - { - bitrate = 32000; - printf( - "Running iSAC at default bitrate of 32000 bps (to specify explicitly add the bps as last parameter)\n"); - } - else // (usedCodec==webrtc::kDecoderISACswb) - { - bitrate = 56000; - printf( - "Running iSAC at default bitrate of 56000 bps (to specify explicitly add the bps as last parameter)\n"); - } - } - else - { - bitrate = atoi(argv[6]); - if (usedCodec == webrtc::kDecoderISAC) - { - if ((bitrate < 10000) || (bitrate > 32000)) - { - printf( - "Error: iSAC bitrate must be between 10000 and 32000 bps (%i is invalid)\n", - bitrate); - exit(0); - } - printf("Running iSAC at bitrate of %i bps\n", bitrate); - } - else // (usedCodec==webrtc::kDecoderISACswb) - { - if ((bitrate < 32000) || (bitrate > 56000)) - { - printf( - "Error: iSAC SWB bitrate must be between 32000 and 56000 bps (%i is invalid)\n", - bitrate); - exit(0); - } - } - } - } - else - { - if (argc == 7) - { - printf( - "Error: Bitrate parameter can only be specified for iSAC, G.723, and G.729.1\n"); - exit(0); - } - } - - if(useRed) { - printf("Redundancy engaged. "); - } - printf("Used codec: %i\n",usedCodec); - printf("Payload type: %i\n",payloadType); - - NetEQTest_init_coders(usedCodec, packet_size, bitrate, fs, useVAD, numChannels); - - /* write file header */ - //fprintf(out_file, "#!RTPencode%s\n", "1.0"); - fprintf(out_file, "#!rtpplay%s \n", "1.0"); // this is the string that rtpplay needs - uint32_t dummy_variable = 0; // should be converted to network endian format, but does not matter when 0 - if (fwrite(&dummy_variable, 4, 1, out_file) != 1) { - return -1; - } - if (fwrite(&dummy_variable, 4, 1, out_file) != 1) { - return -1; - } - if (fwrite(&dummy_variable, 4, 1, out_file) != 1) { - return -1; - } - if (fwrite(&dummy_variable, 2, 1, out_file) != 1) { - return -1; - } - if (fwrite(&dummy_variable, 2, 1, out_file) != 1) { - return -1; - } - -#ifdef TIMESTAMP_WRAPAROUND - timestamp = 0xFFFFFFFF - fs*10; /* should give wrap-around in 10 seconds */ -#endif -#if defined(RANDOM_DATA) | defined(RANDOM_PAYLOAD_DATA) - srand(RANDOM_SEED); -#endif - - /* if redundancy is used, the first redundant payload is zero length */ - red_len[0] = 0; - - /* read first frame */ - len=fread(org_data,2,packet_size * numChannels,in_file) / numChannels; - - /* de-interleave if stereo */ - if ( usingStereo ) - { - stereoDeInterleave(org_data, len * numChannels); - } - - while (len==packet_size) { - -#ifdef INSERT_DTMF_PACKETS - dtmfSent = false; - - if ( sendtime >= NTone * DTMF_PACKET_INTERVAL ) { - if ( sendtime < NTone * DTMF_PACKET_INTERVAL + DTMF_DURATION ) { - // tone has not ended - if (DTMFfirst==1) { - DTMFtimestamp = timestamp; // save this timestamp - DTMFfirst=0; - } - makeRTPheader(rtp_data, NETEQ_CODEC_AVT_PT, seqNo,DTMFtimestamp, ssrc); - enc_len = makeDTMFpayload(&rtp_data[12], NTone % 12, 0, 4, (int) (sendtime - NTone * DTMF_PACKET_INTERVAL)*(fs/1000) + len); - } - else { - // tone has ended - makeRTPheader(rtp_data, NETEQ_CODEC_AVT_PT, seqNo,DTMFtimestamp, ssrc); - enc_len = makeDTMFpayload(&rtp_data[12], NTone % 12, 1, 4, DTMF_DURATION*(fs/1000)); - NTone++; - DTMFfirst=1; - } - - /* write RTP packet to file */ - length = htons(12 + enc_len + 8); - plen = htons(12 + enc_len); - offset = (uint32_t) sendtime; //(timestamp/(fs/1000)); - offset = htonl(offset); - if (fwrite(&length, 2, 1, out_file) != 1) { - return -1; - } - if (fwrite(&plen, 2, 1, out_file) != 1) { - return -1; - } - if (fwrite(&offset, 4, 1, out_file) != 1) { - return -1; - } - if (fwrite(rtp_data, 12 + enc_len, 1, out_file) != 1) { - return -1; - } - - dtmfSent = true; - } -#endif - -#ifdef NO_DTMF_OVERDUB - /* If DTMF is sent, we should not send any speech packets during the same time */ - if (dtmfSent) { - enc_len = 0; - } - else { -#endif - /* encode frame */ - enc_len=NetEQTest_encode(usedCodec, org_data, packet_size, &rtp_data[12] ,fs,&vad, useVAD, bitrate, numChannels); - if (enc_len==-1) { - printf("Error encoding frame\n"); - exit(0); - } - - if ( usingStereo && - stereoMode != STEREO_MODE_FRAME && - vad == 1 ) - { - // interleave the encoded payload for sample-based codecs (not for CNG) - stereoInterleave(&rtp_data[12], enc_len, stereoMode); - } -#ifdef NO_DTMF_OVERDUB - } -#endif - - if (enc_len > 0 && (sendtime <= STOPSENDTIME || sendtime > RESTARTSENDTIME)) { - if(useRed) { - if(red_len[0] > 0) { - memmove(&rtp_data[RTPheaderLen+red_len[0]], &rtp_data[12], enc_len); - memcpy(&rtp_data[RTPheaderLen], red_data, red_len[0]); - - red_len[1] = enc_len; - red_TS[1] = timestamp; - if(vad) - red_PT[1] = payloadType; - else - red_PT[1] = NETEQ_CODEC_CN_PT; - - makeRedundantHeader(rtp_data, red_PT, 2, red_TS, red_len, seqNo++, ssrc); - - - enc_len += red_len[0] + RTPheaderLen - 12; - } - else { // do not use redundancy payload for this packet, i.e., only last payload - memmove(&rtp_data[RTPheaderLen-4], &rtp_data[12], enc_len); - //memcpy(&rtp_data[RTPheaderLen], red_data, red_len[0]); - - red_len[1] = enc_len; - red_TS[1] = timestamp; - if(vad) - red_PT[1] = payloadType; - else - red_PT[1] = NETEQ_CODEC_CN_PT; - - makeRedundantHeader(rtp_data, red_PT, 2, red_TS, red_len, seqNo++, ssrc); - - - enc_len += red_len[0] + RTPheaderLen - 4 - 12; // 4 is length of redundancy header (not used) - } - } - else { - - /* make RTP header */ - if (vad) // regular speech data - makeRTPheader(rtp_data, payloadType, seqNo++,timestamp, ssrc); - else // CNG data - makeRTPheader(rtp_data, NETEQ_CODEC_CN_PT, seqNo++,timestamp, ssrc); - - } -#ifdef MULTIPLE_SAME_TIMESTAMP - int mult_pack=0; - do { -#endif //MULTIPLE_SAME_TIMESTAMP - /* write RTP packet to file */ - length = htons(12 + enc_len + 8); - plen = htons(12 + enc_len); - offset = (uint32_t) sendtime; - //(timestamp/(fs/1000)); - offset = htonl(offset); - if (fwrite(&length, 2, 1, out_file) != 1) { - return -1; - } - if (fwrite(&plen, 2, 1, out_file) != 1) { - return -1; - } - if (fwrite(&offset, 4, 1, out_file) != 1) { - return -1; - } -#ifdef RANDOM_DATA - for (int k=0; k<12+enc_len; k++) { - rtp_data[k] = rand() + rand(); - } -#endif -#ifdef RANDOM_PAYLOAD_DATA - for (int k=12; k<12+enc_len; k++) { - rtp_data[k] = rand() + rand(); - } -#endif - if (fwrite(rtp_data, 12 + enc_len, 1, out_file) != 1) { - return -1; - } -#ifdef MULTIPLE_SAME_TIMESTAMP - } while ( (seqNo%REPEAT_PACKET_DISTANCE == 0) && (mult_pack++ < REPEAT_PACKET_COUNT) ); -#endif //MULTIPLE_SAME_TIMESTAMP - -#ifdef INSERT_OLD_PACKETS - if (packet_age >= OLD_PACKET*fs) { - if (!first_old_packet) { - // send the old packet - if (fwrite(&old_length, 2, 1, - out_file) != 1) { - return -1; - } - if (fwrite(&old_plen, 2, 1, - out_file) != 1) { - return -1; - } - if (fwrite(&offset, 4, 1, - out_file) != 1) { - return -1; - } - if (fwrite(old_rtp_data, 12 + old_enc_len, - 1, out_file) != 1) { - return -1; - } - } - // store current packet as old - old_length=length; - old_plen=plen; - memcpy(old_rtp_data,rtp_data,12+enc_len); - old_enc_len=enc_len; - first_old_packet=0; - packet_age=0; - - } - packet_age += packet_size; -#endif - - if(useRed) { - /* move data to redundancy store */ -#ifdef CODEC_ISAC - if(usedCodec==webrtc::kDecoderISAC) - { - assert(!usingStereo); // Cannot handle stereo yet - red_len[0] = WebRtcIsac_GetRedPayload(ISAC_inst[0], (int16_t*)red_data); - } - else - { -#endif - memcpy(red_data, &rtp_data[RTPheaderLen+red_len[0]], enc_len); - red_len[0]=red_len[1]; -#ifdef CODEC_ISAC - } -#endif - red_TS[0]=red_TS[1]; - red_PT[0]=red_PT[1]; - } - - } - - /* read next frame */ - len=fread(org_data,2,packet_size * numChannels,in_file) / numChannels; - /* de-interleave if stereo */ - if ( usingStereo ) - { - stereoDeInterleave(org_data, len * numChannels); - } - - if (payloadType==NETEQ_CODEC_G722_PT) - timestamp+=len>>1; - else - timestamp+=len; - - sendtime += (double) len/(fs/1000); - } - - NetEQTest_free_coders(usedCodec, numChannels); - fclose(in_file); - fclose(out_file); - printf("Done!\n"); - - return(0); -} - - - - -/****************/ -/* Subfunctions */ -/****************/ - -void NetEQTest_GetCodec_and_PT(char * name, webrtc::NetEqDecoder *codec, int *PT, int frameLen, int *fs, int *bitrate, int *useRed) { - - *bitrate = 0; /* Default bitrate setting */ - *useRed = 0; /* Default no redundancy */ - - if(!strcmp(name,"pcmu")){ - *codec=webrtc::kDecoderPCMu; - *PT=NETEQ_CODEC_PCMU_PT; - *fs=8000; - } - else if(!strcmp(name,"pcma")){ - *codec=webrtc::kDecoderPCMa; - *PT=NETEQ_CODEC_PCMA_PT; - *fs=8000; - } - else if(!strcmp(name,"pcm16b")){ - *codec=webrtc::kDecoderPCM16B; - *PT=NETEQ_CODEC_PCM16B_PT; - *fs=8000; - } - else if(!strcmp(name,"pcm16b_wb")){ - *codec=webrtc::kDecoderPCM16Bwb; - *PT=NETEQ_CODEC_PCM16B_WB_PT; - *fs=16000; - } - else if(!strcmp(name,"pcm16b_swb32")){ - *codec=webrtc::kDecoderPCM16Bswb32kHz; - *PT=NETEQ_CODEC_PCM16B_SWB32KHZ_PT; - *fs=32000; - } - else if(!strcmp(name,"pcm16b_swb48")){ - *codec=webrtc::kDecoderPCM16Bswb48kHz; - *PT=NETEQ_CODEC_PCM16B_SWB48KHZ_PT; - *fs=48000; - } - else if(!strcmp(name,"g722")){ - *codec=webrtc::kDecoderG722; - *PT=NETEQ_CODEC_G722_PT; - *fs=16000; - } - else if((!strcmp(name,"ilbc"))&&((frameLen%240==0)||(frameLen%160==0))){ - *fs=8000; - *codec=webrtc::kDecoderILBC; - *PT=NETEQ_CODEC_ILBC_PT; - } - else if(!strcmp(name,"isac")){ - *fs=16000; - *codec=webrtc::kDecoderISAC; - *PT=NETEQ_CODEC_ISAC_PT; - } - else if(!strcmp(name,"isacswb")){ - *fs=32000; - *codec=webrtc::kDecoderISACswb; - *PT=NETEQ_CODEC_ISACSWB_PT; - } - else if(!strcmp(name,"celt32")){ - *fs=32000; - *codec=webrtc::kDecoderCELT_32; - *PT=NETEQ_CODEC_CELT32_PT; - } - else if(!strcmp(name,"red_pcm")){ - *codec=webrtc::kDecoderPCMa; - *PT=NETEQ_CODEC_PCMA_PT; /* this will be the PT for the sub-headers */ - *fs=8000; - *useRed = 1; - } else if(!strcmp(name,"red_isac")){ - *codec=webrtc::kDecoderISAC; - *PT=NETEQ_CODEC_ISAC_PT; /* this will be the PT for the sub-headers */ - *fs=16000; - *useRed = 1; - } else { - printf("Error: Not a supported codec (%s)\n", name); - exit(0); - } - -} - - - - -int NetEQTest_init_coders(webrtc::NetEqDecoder coder, int enc_frameSize, int bitrate, int sampfreq , int vad, int numChannels){ - - int ok=0; - - for (int k = 0; k < numChannels; k++) - { - ok=WebRtcVad_Create(&VAD_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for VAD instance\n"); - exit(0); - } - ok=WebRtcVad_Init(VAD_inst[k]); - if (ok==-1) { - printf("Error: Initialization of VAD struct failed\n"); - exit(0); - } - - -#if (defined(CODEC_CNGCODEC8) || defined(CODEC_CNGCODEC16) || \ - defined(CODEC_CNGCODEC32) || defined(CODEC_CNGCODEC48)) - ok=WebRtcCng_CreateEnc(&CNGenc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for CNG encoding instance\n"); - exit(0); - } - if(sampfreq <= 16000) { - ok=WebRtcCng_InitEnc(CNGenc_inst[k],sampfreq, 200, 5); - if (ok==-1) { - printf("Error: Initialization of CNG struct failed. Error code %d\n", - WebRtcCng_GetErrorCodeEnc(CNGenc_inst[k])); - exit(0); - } - } -#endif - - switch (coder) { -#ifdef CODEC_PCM16B - case webrtc::kDecoderPCM16B : -#endif -#ifdef CODEC_PCM16B_WB - case webrtc::kDecoderPCM16Bwb : -#endif -#ifdef CODEC_PCM16B_32KHZ - case webrtc::kDecoderPCM16Bswb32kHz : -#endif -#ifdef CODEC_PCM16B_48KHZ - case webrtc::kDecoderPCM16Bswb48kHz : -#endif -#ifdef CODEC_G711 - case webrtc::kDecoderPCMu : - case webrtc::kDecoderPCMa : -#endif - // do nothing - break; -#ifdef CODEC_G729 - case webrtc::kDecoderG729: - if (sampfreq==8000) { - if ((enc_frameSize==80)||(enc_frameSize==160)||(enc_frameSize==240)||(enc_frameSize==320)||(enc_frameSize==400)||(enc_frameSize==480)) { - ok=WebRtcG729_CreateEnc(&G729enc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for G729 encoding instance\n"); - exit(0); - } - } else { - printf("\nError: g729 only supports 10, 20, 30, 40, 50 or 60 ms!!\n\n"); - exit(0); - } - WebRtcG729_EncoderInit(G729enc_inst[k], vad); - if ((vad==1)&&(enc_frameSize!=80)) { - printf("\nError - This simulation only supports VAD for G729 at 10ms packets (not %dms)\n", (enc_frameSize>>3)); - } - } else { - printf("\nError - g729 is only developed for 8kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_G729_1 - case webrtc::kDecoderG729_1: - if (sampfreq==16000) { - if ((enc_frameSize==320)||(enc_frameSize==640)||(enc_frameSize==960) - ) { - ok=WebRtcG7291_Create(&G729_1_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for G.729.1 codec instance\n"); - exit(0); - } - } else { - printf("\nError: G.729.1 only supports 20, 40 or 60 ms!!\n\n"); - exit(0); - } - if (!(((bitrate >= 12000) && (bitrate <= 32000) && (bitrate%2000 == 0)) || (bitrate == 8000))) { - /* must be 8, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, or 32 kbps */ - printf("\nError: G.729.1 bitrate must be 8000 or 12000--32000 in steps of 2000 bps\n"); - exit(0); - } - WebRtcG7291_EncoderInit(G729_1_inst[k], bitrate, 0 /* flag8kHz*/, 0 /*flagG729mode*/); - } else { - printf("\nError - G.729.1 input is always 16 kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_SPEEX_8 - case webrtc::kDecoderSPEEX_8 : - if (sampfreq==8000) { - if ((enc_frameSize==160)||(enc_frameSize==320)||(enc_frameSize==480)) { - ok=WebRtcSpeex_CreateEnc(&SPEEX8enc_inst[k], sampfreq); - if (ok!=0) { - printf("Error: Couldn't allocate memory for Speex encoding instance\n"); - exit(0); - } - } else { - printf("\nError: Speex only supports 20, 40, and 60 ms!!\n\n"); - exit(0); - } - if ((vad==1)&&(enc_frameSize!=160)) { - printf("\nError - This simulation only supports VAD for Speex at 20ms packets (not %dms)\n", (enc_frameSize>>3)); - vad=0; - } - ok=WebRtcSpeex_EncoderInit(SPEEX8enc_inst[k], 0/*vbr*/, 3 /*complexity*/, vad); - if (ok!=0) exit(0); - } else { - printf("\nError - Speex8 called with sample frequency other than 8 kHz.\n\n"); - } - break; -#endif -#ifdef CODEC_SPEEX_16 - case webrtc::kDecoderSPEEX_16 : - if (sampfreq==16000) { - if ((enc_frameSize==320)||(enc_frameSize==640)||(enc_frameSize==960)) { - ok=WebRtcSpeex_CreateEnc(&SPEEX16enc_inst[k], sampfreq); - if (ok!=0) { - printf("Error: Couldn't allocate memory for Speex encoding instance\n"); - exit(0); - } - } else { - printf("\nError: Speex only supports 20, 40, and 60 ms!!\n\n"); - exit(0); - } - if ((vad==1)&&(enc_frameSize!=320)) { - printf("\nError - This simulation only supports VAD for Speex at 20ms packets (not %dms)\n", (enc_frameSize>>4)); - vad=0; - } - ok=WebRtcSpeex_EncoderInit(SPEEX16enc_inst[k], 0/*vbr*/, 3 /*complexity*/, vad); - if (ok!=0) exit(0); - } else { - printf("\nError - Speex16 called with sample frequency other than 16 kHz.\n\n"); - } - break; -#endif -#ifdef CODEC_CELT_32 - case webrtc::kDecoderCELT_32 : - if (sampfreq==32000) { - if (enc_frameSize==320) { - ok=WebRtcCelt_CreateEnc(&CELT32enc_inst[k], 1 /*mono*/); - if (ok!=0) { - printf("Error: Couldn't allocate memory for Celt encoding instance\n"); - exit(0); - } - } else { - printf("\nError: Celt only supports 10 ms!!\n\n"); - exit(0); - } - ok=WebRtcCelt_EncoderInit(CELT32enc_inst[k], 1 /*mono*/, 48000 /*bitrate*/); - if (ok!=0) exit(0); - } else { - printf("\nError - Celt32 called with sample frequency other than 32 kHz.\n\n"); - } - break; -#endif - -#ifdef CODEC_G722_1_16 - case webrtc::kDecoderG722_1_16 : - if (sampfreq==16000) { - ok=WebRtcG7221_CreateEnc16(&G722_1_16enc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for G.722.1 instance\n"); - exit(0); - } - if (enc_frameSize==320) { - } else { - printf("\nError: G722.1 only supports 20 ms!!\n\n"); - exit(0); - } - WebRtcG7221_EncoderInit16((G722_1_16_encinst_t*)G722_1_16enc_inst[k]); - } else { - printf("\nError - G722.1 is only developed for 16kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_G722_1_24 - case webrtc::kDecoderG722_1_24 : - if (sampfreq==16000) { - ok=WebRtcG7221_CreateEnc24(&G722_1_24enc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for G.722.1 instance\n"); - exit(0); - } - if (enc_frameSize==320) { - } else { - printf("\nError: G722.1 only supports 20 ms!!\n\n"); - exit(0); - } - WebRtcG7221_EncoderInit24((G722_1_24_encinst_t*)G722_1_24enc_inst[k]); - } else { - printf("\nError - G722.1 is only developed for 16kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_G722_1_32 - case webrtc::kDecoderG722_1_32 : - if (sampfreq==16000) { - ok=WebRtcG7221_CreateEnc32(&G722_1_32enc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for G.722.1 instance\n"); - exit(0); - } - if (enc_frameSize==320) { - } else { - printf("\nError: G722.1 only supports 20 ms!!\n\n"); - exit(0); - } - WebRtcG7221_EncoderInit32((G722_1_32_encinst_t*)G722_1_32enc_inst[k]); - } else { - printf("\nError - G722.1 is only developed for 16kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_G722_1C_24 - case webrtc::kDecoderG722_1C_24 : - if (sampfreq==32000) { - ok=WebRtcG7221C_CreateEnc24(&G722_1C_24enc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for G.722.1C instance\n"); - exit(0); - } - if (enc_frameSize==640) { - } else { - printf("\nError: G722.1 C only supports 20 ms!!\n\n"); - exit(0); - } - WebRtcG7221C_EncoderInit24((G722_1C_24_encinst_t*)G722_1C_24enc_inst[k]); - } else { - printf("\nError - G722.1 C is only developed for 32kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_G722_1C_32 - case webrtc::kDecoderG722_1C_32 : - if (sampfreq==32000) { - ok=WebRtcG7221C_CreateEnc32(&G722_1C_32enc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for G.722.1C instance\n"); - exit(0); - } - if (enc_frameSize==640) { - } else { - printf("\nError: G722.1 C only supports 20 ms!!\n\n"); - exit(0); - } - WebRtcG7221C_EncoderInit32((G722_1C_32_encinst_t*)G722_1C_32enc_inst[k]); - } else { - printf("\nError - G722.1 C is only developed for 32kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_G722_1C_48 - case webrtc::kDecoderG722_1C_48 : - if (sampfreq==32000) { - ok=WebRtcG7221C_CreateEnc48(&G722_1C_48enc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for G.722.1C instance\n"); - exit(0); - } - if (enc_frameSize==640) { - } else { - printf("\nError: G722.1 C only supports 20 ms!!\n\n"); - exit(0); - } - WebRtcG7221C_EncoderInit48((G722_1C_48_encinst_t*)G722_1C_48enc_inst[k]); - } else { - printf("\nError - G722.1 C is only developed for 32kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_G722 - case webrtc::kDecoderG722 : - if (sampfreq==16000) { - if (enc_frameSize%2==0) { - } else { - printf("\nError - g722 frames must have an even number of enc_frameSize\n"); - exit(0); - } - WebRtcG722_CreateEncoder(&g722EncState[k]); - WebRtcG722_EncoderInit(g722EncState[k]); - } else { - printf("\nError - g722 is only developed for 16kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_AMR - case webrtc::kDecoderAMR : - if (sampfreq==8000) { - ok=WebRtcAmr_CreateEnc(&AMRenc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for AMR encoding instance\n"); - exit(0); - }if ((enc_frameSize==160)||(enc_frameSize==320)||(enc_frameSize==480)) { - } else { - printf("\nError - AMR must have a multiple of 160 enc_frameSize\n"); - exit(0); - } - WebRtcAmr_EncoderInit(AMRenc_inst[k], vad); - WebRtcAmr_EncodeBitmode(AMRenc_inst[k], AMRBandwidthEfficient); - AMR_bitrate = bitrate; - } else { - printf("\nError - AMR is only developed for 8kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_AMRWB - case webrtc::kDecoderAMRWB : - if (sampfreq==16000) { - ok=WebRtcAmrWb_CreateEnc(&AMRWBenc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for AMRWB encoding instance\n"); - exit(0); - } - if (((enc_frameSize/320)<0)||((enc_frameSize/320)>3)||((enc_frameSize%320)!=0)) { - printf("\nError - AMRwb must have frameSize of 20, 40 or 60ms\n"); - exit(0); - } - WebRtcAmrWb_EncoderInit(AMRWBenc_inst[k], vad); - if (bitrate==7000) { - AMRWB_bitrate = AMRWB_MODE_7k; - } else if (bitrate==9000) { - AMRWB_bitrate = AMRWB_MODE_9k; - } else if (bitrate==12000) { - AMRWB_bitrate = AMRWB_MODE_12k; - } else if (bitrate==14000) { - AMRWB_bitrate = AMRWB_MODE_14k; - } else if (bitrate==16000) { - AMRWB_bitrate = AMRWB_MODE_16k; - } else if (bitrate==18000) { - AMRWB_bitrate = AMRWB_MODE_18k; - } else if (bitrate==20000) { - AMRWB_bitrate = AMRWB_MODE_20k; - } else if (bitrate==23000) { - AMRWB_bitrate = AMRWB_MODE_23k; - } else if (bitrate==24000) { - AMRWB_bitrate = AMRWB_MODE_24k; - } - WebRtcAmrWb_EncodeBitmode(AMRWBenc_inst[k], AMRBandwidthEfficient); - - } else { - printf("\nError - AMRwb is only developed for 16kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_ILBC - case webrtc::kDecoderILBC : - if (sampfreq==8000) { - ok=WebRtcIlbcfix_EncoderCreate(&iLBCenc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for iLBC encoding instance\n"); - exit(0); - } - if ((enc_frameSize==160)||(enc_frameSize==240)||(enc_frameSize==320)||(enc_frameSize==480)) { - } else { - printf("\nError - iLBC only supports 160, 240, 320 and 480 enc_frameSize (20, 30, 40 and 60 ms)\n"); - exit(0); - } - if ((enc_frameSize==160)||(enc_frameSize==320)) { - /* 20 ms version */ - WebRtcIlbcfix_EncoderInit(iLBCenc_inst[k], 20); - } else { - /* 30 ms version */ - WebRtcIlbcfix_EncoderInit(iLBCenc_inst[k], 30); - } - } else { - printf("\nError - iLBC is only developed for 8kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_ISAC - case webrtc::kDecoderISAC: - if (sampfreq==16000) { - ok=WebRtcIsac_Create(&ISAC_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for iSAC instance\n"); - exit(0); - }if ((enc_frameSize==480)||(enc_frameSize==960)) { - } else { - printf("\nError - iSAC only supports frameSize (30 and 60 ms)\n"); - exit(0); - } - WebRtcIsac_EncoderInit(ISAC_inst[k],1); - if ((bitrate<10000)||(bitrate>32000)) { - printf("\nError - iSAC bitrate has to be between 10000 and 32000 bps (not %i)\n", bitrate); - exit(0); - } - WebRtcIsac_Control(ISAC_inst[k], bitrate, enc_frameSize>>4); - } else { - printf("\nError - iSAC only supports 480 or 960 enc_frameSize (30 or 60 ms)\n"); - exit(0); - } - break; -#endif -#ifdef NETEQ_ISACFIX_CODEC - case webrtc::kDecoderISAC: - if (sampfreq==16000) { - ok=WebRtcIsacfix_Create(&ISAC_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for iSAC instance\n"); - exit(0); - }if ((enc_frameSize==480)||(enc_frameSize==960)) { - } else { - printf("\nError - iSAC only supports frameSize (30 and 60 ms)\n"); - exit(0); - } - WebRtcIsacfix_EncoderInit(ISAC_inst[k],1); - if ((bitrate<10000)||(bitrate>32000)) { - printf("\nError - iSAC bitrate has to be between 10000 and 32000 bps (not %i)\n", bitrate); - exit(0); - } - WebRtcIsacfix_Control(ISAC_inst[k], bitrate, enc_frameSize>>4); - } else { - printf("\nError - iSAC only supports 480 or 960 enc_frameSize (30 or 60 ms)\n"); - exit(0); - } - break; -#endif -#ifdef CODEC_ISAC_SWB - case webrtc::kDecoderISACswb: - if (sampfreq==32000) { - ok=WebRtcIsac_Create(&ISACSWB_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for iSAC SWB instance\n"); - exit(0); - }if (enc_frameSize==960) { - } else { - printf("\nError - iSAC SWB only supports frameSize 30 ms\n"); - exit(0); - } - ok = WebRtcIsac_SetEncSampRate(ISACSWB_inst[k], 32000); - if (ok!=0) { - printf("Error: Couldn't set sample rate for iSAC SWB instance\n"); - exit(0); - } - WebRtcIsac_EncoderInit(ISACSWB_inst[k],1); - if ((bitrate<32000)||(bitrate>56000)) { - printf("\nError - iSAC SWB bitrate has to be between 32000 and 56000 bps (not %i)\n", bitrate); - exit(0); - } - WebRtcIsac_Control(ISACSWB_inst[k], bitrate, enc_frameSize>>5); - } else { - printf("\nError - iSAC SWB only supports 960 enc_frameSize (30 ms)\n"); - exit(0); - } - break; -#endif -#ifdef CODEC_GSMFR - case webrtc::kDecoderGSMFR: - if (sampfreq==8000) { - ok=WebRtcGSMFR_CreateEnc(&GSMFRenc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for GSM FR encoding instance\n"); - exit(0); - } - if ((enc_frameSize==160)||(enc_frameSize==320)||(enc_frameSize==480)) { - } else { - printf("\nError - GSM FR must have a multiple of 160 enc_frameSize\n"); - exit(0); - } - WebRtcGSMFR_EncoderInit(GSMFRenc_inst[k], 0); - } else { - printf("\nError - GSM FR is only developed for 8kHz \n"); - exit(0); - } - break; -#endif - default : - printf("Error: unknown codec in call to NetEQTest_init_coders.\n"); - exit(0); - break; - } - - if (ok != 0) { - return(ok); - } - } // end for - - return(0); -} - - - - -int NetEQTest_free_coders(webrtc::NetEqDecoder coder, int numChannels) { - - for (int k = 0; k < numChannels; k++) - { - WebRtcVad_Free(VAD_inst[k]); -#if (defined(CODEC_CNGCODEC8) || defined(CODEC_CNGCODEC16) || \ - defined(CODEC_CNGCODEC32) || defined(CODEC_CNGCODEC48)) - WebRtcCng_FreeEnc(CNGenc_inst[k]); -#endif - - switch (coder) - { -#ifdef CODEC_PCM16B - case webrtc::kDecoderPCM16B : -#endif -#ifdef CODEC_PCM16B_WB - case webrtc::kDecoderPCM16Bwb : -#endif -#ifdef CODEC_PCM16B_32KHZ - case webrtc::kDecoderPCM16Bswb32kHz : -#endif -#ifdef CODEC_PCM16B_48KHZ - case webrtc::kDecoderPCM16Bswb48kHz : -#endif -#ifdef CODEC_G711 - case webrtc::kDecoderPCMu : - case webrtc::kDecoderPCMa : -#endif - // do nothing - break; -#ifdef CODEC_G729 - case webrtc::kDecoderG729: - WebRtcG729_FreeEnc(G729enc_inst[k]); - break; -#endif -#ifdef CODEC_G729_1 - case webrtc::kDecoderG729_1: - WebRtcG7291_Free(G729_1_inst[k]); - break; -#endif -#ifdef CODEC_SPEEX_8 - case webrtc::kDecoderSPEEX_8 : - WebRtcSpeex_FreeEnc(SPEEX8enc_inst[k]); - break; -#endif -#ifdef CODEC_SPEEX_16 - case webrtc::kDecoderSPEEX_16 : - WebRtcSpeex_FreeEnc(SPEEX16enc_inst[k]); - break; -#endif -#ifdef CODEC_CELT_32 - case webrtc::kDecoderCELT_32 : - WebRtcCelt_FreeEnc(CELT32enc_inst[k]); - break; -#endif - -#ifdef CODEC_G722_1_16 - case webrtc::kDecoderG722_1_16 : - WebRtcG7221_FreeEnc16(G722_1_16enc_inst[k]); - break; -#endif -#ifdef CODEC_G722_1_24 - case webrtc::kDecoderG722_1_24 : - WebRtcG7221_FreeEnc24(G722_1_24enc_inst[k]); - break; -#endif -#ifdef CODEC_G722_1_32 - case webrtc::kDecoderG722_1_32 : - WebRtcG7221_FreeEnc32(G722_1_32enc_inst[k]); - break; -#endif -#ifdef CODEC_G722_1C_24 - case webrtc::kDecoderG722_1C_24 : - WebRtcG7221C_FreeEnc24(G722_1C_24enc_inst[k]); - break; -#endif -#ifdef CODEC_G722_1C_32 - case webrtc::kDecoderG722_1C_32 : - WebRtcG7221C_FreeEnc32(G722_1C_32enc_inst[k]); - break; -#endif -#ifdef CODEC_G722_1C_48 - case webrtc::kDecoderG722_1C_48 : - WebRtcG7221C_FreeEnc48(G722_1C_48enc_inst[k]); - break; -#endif -#ifdef CODEC_G722 - case webrtc::kDecoderG722 : - WebRtcG722_FreeEncoder(g722EncState[k]); - break; -#endif -#ifdef CODEC_AMR - case webrtc::kDecoderAMR : - WebRtcAmr_FreeEnc(AMRenc_inst[k]); - break; -#endif -#ifdef CODEC_AMRWB - case webrtc::kDecoderAMRWB : - WebRtcAmrWb_FreeEnc(AMRWBenc_inst[k]); - break; -#endif -#ifdef CODEC_ILBC - case webrtc::kDecoderILBC : - WebRtcIlbcfix_EncoderFree(iLBCenc_inst[k]); - break; -#endif -#ifdef CODEC_ISAC - case webrtc::kDecoderISAC: - WebRtcIsac_Free(ISAC_inst[k]); - break; -#endif -#ifdef NETEQ_ISACFIX_CODEC - case webrtc::kDecoderISAC: - WebRtcIsacfix_Free(ISAC_inst[k]); - break; -#endif -#ifdef CODEC_ISAC_SWB - case webrtc::kDecoderISACswb: - WebRtcIsac_Free(ISACSWB_inst[k]); - break; -#endif -#ifdef CODEC_GSMFR - case webrtc::kDecoderGSMFR: - WebRtcGSMFR_FreeEnc(GSMFRenc_inst[k]); - break; -#endif - default : - printf("Error: unknown codec in call to NetEQTest_init_coders.\n"); - exit(0); - break; - } - } - - return(0); -} - - - - - - -int NetEQTest_encode(int coder, int16_t *indata, int frameLen, unsigned char * encoded,int sampleRate , - int * vad, int useVAD, int bitrate, int numChannels){ - - short cdlen = 0; - int16_t *tempdata; - static int first_cng=1; - int16_t tempLen; - - *vad =1; - - // check VAD first - if(useVAD) - { - *vad = 0; - - for (int k = 0; k < numChannels; k++) - { - tempLen = frameLen; - tempdata = &indata[k*frameLen]; - int localVad=0; - /* Partition the signal and test each chunk for VAD. - All chunks must be VAD=0 to produce a total VAD=0. */ - while (tempLen >= 10*sampleRate/1000) { - if ((tempLen % 30*sampleRate/1000) == 0) { // tempLen is multiple of 30ms - localVad |= WebRtcVad_Process(VAD_inst[k] ,sampleRate, tempdata, 30*sampleRate/1000); - tempdata += 30*sampleRate/1000; - tempLen -= 30*sampleRate/1000; - } - else if (tempLen >= 20*sampleRate/1000) { // tempLen >= 20ms - localVad |= WebRtcVad_Process(VAD_inst[k] ,sampleRate, tempdata, 20*sampleRate/1000); - tempdata += 20*sampleRate/1000; - tempLen -= 20*sampleRate/1000; - } - else { // use 10ms - localVad |= WebRtcVad_Process(VAD_inst[k] ,sampleRate, tempdata, 10*sampleRate/1000); - tempdata += 10*sampleRate/1000; - tempLen -= 10*sampleRate/1000; - } - } - - // aggregate all VAD decisions over all channels - *vad |= localVad; - } - - if(!*vad){ - // all channels are silent - cdlen = 0; - for (int k = 0; k < numChannels; k++) - { - WebRtcCng_Encode(CNGenc_inst[k],&indata[k*frameLen], (frameLen <= 640 ? frameLen : 640) /* max 640 */, - encoded,&tempLen,first_cng); - encoded += tempLen; - cdlen += tempLen; - } - *vad=0; - first_cng=0; - return(cdlen); - } - } - - - // loop over all channels - int totalLen = 0; - - for (int k = 0; k < numChannels; k++) - { - /* Encode with the selected coder type */ - if (coder==webrtc::kDecoderPCMu) { /*g711 u-law */ -#ifdef CODEC_G711 - cdlen = WebRtcG711_EncodeU(G711state[k], indata, frameLen, (int16_t*) encoded); -#endif - } - else if (coder==webrtc::kDecoderPCMa) { /*g711 A-law */ -#ifdef CODEC_G711 - cdlen = WebRtcG711_EncodeA(G711state[k], indata, frameLen, (int16_t*) encoded); - } -#endif -#ifdef CODEC_PCM16B - else if ((coder==webrtc::kDecoderPCM16B)||(coder==webrtc::kDecoderPCM16Bwb)|| - (coder==webrtc::kDecoderPCM16Bswb32kHz)||(coder==webrtc::kDecoderPCM16Bswb48kHz)) { /*pcm16b (8kHz, 16kHz, 32kHz or 48kHz) */ - cdlen = WebRtcPcm16b_EncodeW16(indata, frameLen, (int16_t*) encoded); - } -#endif -#ifdef CODEC_G722 - else if (coder==webrtc::kDecoderG722) { /*g722 */ - cdlen=WebRtcG722_Encode(g722EncState[k], indata, frameLen, (int16_t*)encoded); - assert(cdlen == frameLen>>1); - } -#endif -#ifdef CODEC_ILBC - else if (coder==webrtc::kDecoderILBC) { /*iLBC */ - cdlen=WebRtcIlbcfix_Encode(iLBCenc_inst[k], indata,frameLen,(int16_t*)encoded); - } -#endif -#if (defined(CODEC_ISAC) || defined(NETEQ_ISACFIX_CODEC)) // TODO(hlundin): remove all NETEQ_ISACFIX_CODEC - else if (coder==webrtc::kDecoderISAC) { /*iSAC */ - int noOfCalls=0; - cdlen=0; - while (cdlen<=0) { -#ifdef CODEC_ISAC /* floating point */ - cdlen=WebRtcIsac_Encode(ISAC_inst[k],&indata[noOfCalls*160],(int16_t*)encoded); -#else /* fixed point */ - cdlen=WebRtcIsacfix_Encode(ISAC_inst[k],&indata[noOfCalls*160],(int16_t*)encoded); -#endif - noOfCalls++; - } - } -#endif -#ifdef CODEC_ISAC_SWB - else if (coder==webrtc::kDecoderISACswb) { /* iSAC SWB */ - int noOfCalls=0; - cdlen=0; - while (cdlen<=0) { - cdlen=WebRtcIsac_Encode(ISACSWB_inst[k],&indata[noOfCalls*320],(int16_t*)encoded); - noOfCalls++; - } - } -#endif -#ifdef CODEC_CELT_32 - else if (coder==webrtc::kDecoderCELT_32) { /* Celt */ - int encodedLen = 0; - cdlen = 0; - while (cdlen <= 0) { - cdlen = WebRtcCelt_Encode(CELT32enc_inst[k], &indata[encodedLen], encoded); - encodedLen += 10*32; /* 10 ms */ - } - if( (encodedLen != frameLen) || cdlen < 0) { - printf("Error encoding Celt frame!\n"); - exit(0); - } - } -#endif - - indata += frameLen; - encoded += cdlen; - totalLen += cdlen; - - } // end for - - first_cng=1; - return(totalLen); -} - - - -void makeRTPheader(unsigned char* rtp_data, int payloadType, int seqNo, uint32_t timestamp, uint32_t ssrc){ - - rtp_data[0]=(unsigned char)0x80; - rtp_data[1]=(unsigned char)(payloadType & 0xFF); - rtp_data[2]=(unsigned char)((seqNo>>8)&0xFF); - rtp_data[3]=(unsigned char)((seqNo)&0xFF); - rtp_data[4]=(unsigned char)((timestamp>>24)&0xFF); - rtp_data[5]=(unsigned char)((timestamp>>16)&0xFF); - - rtp_data[6]=(unsigned char)((timestamp>>8)&0xFF); - rtp_data[7]=(unsigned char)(timestamp & 0xFF); - - rtp_data[8]=(unsigned char)((ssrc>>24)&0xFF); - rtp_data[9]=(unsigned char)((ssrc>>16)&0xFF); - - rtp_data[10]=(unsigned char)((ssrc>>8)&0xFF); - rtp_data[11]=(unsigned char)(ssrc & 0xFF); -} - - -int makeRedundantHeader(unsigned char* rtp_data, int *payloadType, int numPayloads, uint32_t *timestamp, uint16_t *blockLen, - int seqNo, uint32_t ssrc) -{ - - int i; - unsigned char *rtpPointer; - uint16_t offset; - - /* first create "standard" RTP header */ - makeRTPheader(rtp_data, NETEQ_CODEC_RED_PT, seqNo, timestamp[numPayloads-1], ssrc); - - rtpPointer = &rtp_data[12]; - - /* add one sub-header for each redundant payload (not the primary) */ - for(i=0; i 0) { - offset = (uint16_t) (timestamp[numPayloads-1] - timestamp[i]); - - rtpPointer[0] = (unsigned char) ( 0x80 | (0x7F & payloadType[i]) ); /* |F| block PT | */ - rtpPointer[1] = (unsigned char) ((offset >> 6) & 0xFF); /* | timestamp- | */ - rtpPointer[2] = (unsigned char) ( ((offset & 0x3F)<<2) | - ( (blockLen[i]>>8) & 0x03 ) ); /* | -offset |bl-| */ - rtpPointer[3] = (unsigned char) ( blockLen[i] & 0xFF ); /* | -ock length | */ - - rtpPointer += 4; - } - } - - /* last sub-header */ - rtpPointer[0]= (unsigned char) (0x00 | (0x7F&payloadType[numPayloads-1]));/* |F| block PT | */ - rtpPointer += 1; - - return(rtpPointer - rtp_data); /* length of header in bytes */ -} - - - -int makeDTMFpayload(unsigned char* payload_data, int Event, int End, int Volume, int Duration) { - unsigned char E,R,V; - R=0; - V=(unsigned char)Volume; - if (End==0) { - E = 0x00; - } else { - E = 0x80; - } - payload_data[0]=(unsigned char)Event; - payload_data[1]=(unsigned char)(E|R|V); - //Duration equals 8 times time_ms, default is 8000 Hz. - payload_data[2]=(unsigned char)((Duration>>8)&0xFF); - payload_data[3]=(unsigned char)(Duration&0xFF); - return(4); -} - -void stereoDeInterleave(int16_t* audioSamples, int numSamples) -{ - - int16_t *tempVec; - int16_t *readPtr, *writeL, *writeR; - - if (numSamples <= 0) - return; - - tempVec = (int16_t *) malloc(sizeof(int16_t) * numSamples); - if (tempVec == NULL) { - printf("Error allocating memory\n"); - exit(0); - } - - memcpy(tempVec, audioSamples, numSamples*sizeof(int16_t)); - - writeL = audioSamples; - writeR = &audioSamples[numSamples/2]; - readPtr = tempVec; - - for (int k = 0; k < numSamples; k += 2) - { - *writeL = *readPtr; - readPtr++; - *writeR = *readPtr; - readPtr++; - writeL++; - writeR++; - } - - free(tempVec); - -} - - -void stereoInterleave(unsigned char* data, int dataLen, int stride) -{ - - unsigned char *ptrL, *ptrR; - unsigned char temp[10]; - - if (stride > 10) - { - exit(0); - } - - if (dataLen%1 != 0) - { - // must be even number of samples - printf("Error: cannot interleave odd sample number\n"); - exit(0); - } - - ptrL = data + stride; - ptrR = &data[dataLen/2]; - - while (ptrL < ptrR) { - // copy from right pointer to temp - memcpy(temp, ptrR, stride); - - // shift data between pointers - memmove(ptrL + stride, ptrL, ptrR - ptrL); - - // copy from temp to left pointer - memcpy(ptrL, temp, stride); - - // advance pointers - ptrL += stride*2; - ptrR += stride; - } - -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPjitter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPjitter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPjitter.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPjitter.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,217 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -//TODO(hlundin): Reformat file to meet style guide. - -/* header includes */ -#include -#include -#include -#include -#ifdef WIN32 -#include -#include -#endif -#ifdef WEBRTC_LINUX -#include -#endif - -#include - -#include "gtest/gtest.h" -#include "webrtc/typedefs.h" - -/*********************/ -/* Misc. definitions */ -/*********************/ - -#define FIRSTLINELEN 40 -#define CHECK_NOT_NULL(a) if((a)==NULL){fprintf(stderr,"\n %s \n line: %d \nerror at %s\n",__FILE__,__LINE__,#a );return(-1);} - -struct arr_time { - float time; - uint32_t ix; -}; - -int filelen(FILE *fid) -{ - fpos_t cur_pos; - int len; - - if (!fid || fgetpos(fid, &cur_pos)) { - return(-1); - } - - fseek(fid, 0, SEEK_END); - len = ftell(fid); - - fsetpos(fid, &cur_pos); - - return (len); -} - -int compare_arr_time(const void *x, const void *y); - -int main(int argc, char* argv[]) -{ - unsigned int dat_len, rtp_len, Npack, k; - arr_time *time_vec; - char firstline[FIRSTLINELEN]; - unsigned char* rtp_vec = NULL; - unsigned char** packet_ptr = NULL; - unsigned char* temp_packet = NULL; - const unsigned int kRtpDumpHeaderSize = 4 + 4 + 4 + 2 + 2; - uint16_t len; - uint32_t *offset; - -/* check number of parameters */ - if (argc != 4) { - /* print help text and exit */ - printf("Apply jitter on RTP stream.\n"); - printf("The program reads an RTP stream and packet timing from two files.\n"); - printf("The RTP stream is modified to have the same jitter as described in the timing files.\n"); - printf("The format of the RTP stream file should be the same as for rtpplay,\n"); - printf("and can be obtained e.g., from Ethereal by using\n"); - printf("Statistics -> RTP -> Show All Streams -> [select a stream] -> Save As\n\n"); - printf("Usage:\n\n"); - printf("%s RTP_infile dat_file RTP_outfile\n", argv[0]); - printf("where:\n"); - - printf("RTP_infile : RTP stream input file\n\n"); - - printf("dat_file : file with packet arrival times in ms\n\n"); - - printf("RTP_outfile : RTP stream output file\n\n"); - - return(0); - } - - FILE* in_file=fopen(argv[1],"rb"); - CHECK_NOT_NULL(in_file); - printf("Input file: %s\n",argv[1]); - FILE* dat_file=fopen(argv[2],"rb"); - CHECK_NOT_NULL(dat_file); - printf("Dat-file: %s\n",argv[2]); - FILE* out_file=fopen(argv[3],"wb"); - CHECK_NOT_NULL(out_file); - printf("Output file: %s\n\n",argv[3]); - - time_vec = (arr_time *) malloc(sizeof(arr_time)*(filelen(dat_file)/sizeof(float)) + 1000); // add 1000 bytes to avoid (rare) strange error - if (time_vec==NULL) { - fprintf(stderr, "Error: could not allocate memory for reading dat file\n"); - goto closing; - } - - dat_len=0; - while(fread(&(time_vec[dat_len].time),sizeof(float),1,dat_file)>0) { - time_vec[dat_len].ix=dat_len; - dat_len++; - } - - if (dat_len == 0) { - fprintf(stderr, "Error: dat_file is empty, no arrival time is given.\n"); - goto closing; - } - - qsort(time_vec,dat_len,sizeof(arr_time),compare_arr_time); - - - rtp_vec = (unsigned char *) malloc(sizeof(unsigned char)*filelen(in_file)); - if (rtp_vec==NULL) { - fprintf(stderr,"Error: could not allocate memory for reading rtp file\n"); - goto closing; - } - - // read file header and write directly to output file - EXPECT_TRUE(fgets(firstline, FIRSTLINELEN, in_file) != NULL); - EXPECT_GT(fputs(firstline, out_file), 0); - EXPECT_EQ(kRtpDumpHeaderSize, fread(firstline, 1, kRtpDumpHeaderSize, - in_file)); - EXPECT_EQ(kRtpDumpHeaderSize, fwrite(firstline, 1, kRtpDumpHeaderSize, - out_file)); - - // read all RTP packets into vector - rtp_len=0; - Npack=0; - len=(uint16_t) fread(&rtp_vec[rtp_len], sizeof(unsigned char), 2, in_file); // read length of first packet - while(len==2) { - len = ntohs(*((uint16_t *)(rtp_vec + rtp_len))); - rtp_len += 2; - if(fread(&rtp_vec[rtp_len], sizeof(unsigned char), len-2, in_file)!=(unsigned) (len-2)) { - fprintf(stderr,"Error: currupt packet length\n"); - goto closing; - } - rtp_len += len-2; - Npack++; - len=(uint16_t) fread(&rtp_vec[rtp_len], sizeof(unsigned char), 2, in_file); // read length of next packet - } - - if (Npack == 0) { - fprintf(stderr, "Error: No RTP packet found.\n"); - goto closing; - } - - packet_ptr = (unsigned char **) malloc(Npack*sizeof(unsigned char*)); - - packet_ptr[0]=rtp_vec; - k=1; - while(k= 0 ) { - *offset = htonl((uint32_t) time_vec[k].time); - } - else { - *offset = htonl((uint32_t) 0); - fprintf(stderr, "Warning: negative receive time in dat file transformed to 0.\n"); - } - - // write packet to file - if (fwrite(temp_packet, sizeof(unsigned char), - ntohs(*((uint16_t*) temp_packet)), - out_file) != - ntohs(*((uint16_t*) temp_packet))) { - return -1; - } - } - } - - -closing: - free(time_vec); - free(rtp_vec); - if (packet_ptr != NULL) { - free(packet_ptr); - } - fclose(in_file); - fclose(dat_file); - fclose(out_file); - - return(0); -} - - - -int compare_arr_time(const void *xp, const void *yp) { - - if(((arr_time *)xp)->time == ((arr_time *)yp)->time) - return(0); - else if(((arr_time *)xp)->time > ((arr_time *)yp)->time) - return(1); - - return(-1); -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPtimeshift.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPtimeshift.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPtimeshift.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/RTPtimeshift.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include - -#include "NETEQTEST_RTPpacket.h" -#include "gtest/gtest.h" - -/*********************/ -/* Misc. definitions */ -/*********************/ - -#define FIRSTLINELEN 40 - - -int main(int argc, char* argv[]) -{ - if(argc < 4 || argc > 6) - { - printf("Usage: RTPtimeshift in.rtp out.rtp newStartTS [newStartSN [newStartArrTime]]\n"); - exit(1); - } - - FILE *inFile=fopen(argv[1],"rb"); - if (!inFile) - { - printf("Cannot open input file %s\n", argv[1]); - return(-1); - } - printf("Input RTP file: %s\n",argv[1]); - - FILE *outFile=fopen(argv[2],"wb"); - if (!outFile) - { - printf("Cannot open output file %s\n", argv[2]); - return(-1); - } - printf("Output RTP file: %s\n\n",argv[2]); - - // read file header and write directly to output file - const unsigned int kRtpDumpHeaderSize = 4 + 4 + 4 + 2 + 2; - char firstline[FIRSTLINELEN]; - EXPECT_TRUE(fgets(firstline, FIRSTLINELEN, inFile) != NULL); - EXPECT_GT(fputs(firstline, outFile), 0); - EXPECT_EQ(kRtpDumpHeaderSize, - fread(firstline, 1, kRtpDumpHeaderSize, inFile)); - EXPECT_EQ(kRtpDumpHeaderSize, - fwrite(firstline, 1, kRtpDumpHeaderSize, outFile)); - NETEQTEST_RTPpacket packet; - int packLen = packet.readFromFile(inFile); - if (packLen < 0) - { - exit(1); - } - - // get new start TS and start SeqNo from arguments - uint32_t TSdiff = atoi(argv[3]) - packet.timeStamp(); - uint16_t SNdiff = 0; - uint32_t ATdiff = 0; - if (argc > 4) - { - int startSN = atoi(argv[4]); - if (startSN >= 0) - SNdiff = startSN - packet.sequenceNumber(); - if (argc > 5) - { - int startTS = atoi(argv[5]); - if (startTS >= 0) - ATdiff = startTS - packet.time(); - } - } - - while (packLen >= 0) - { - - packet.setTimeStamp(packet.timeStamp() + TSdiff); - packet.setSequenceNumber(packet.sequenceNumber() + SNdiff); - packet.setTime(packet.time() + ATdiff); - - packet.writeToFile(outFile); - - packLen = packet.readFromFile(inFile); - - } - - fclose(inFile); - fclose(outFile); - - return 0; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/rtp_to_text.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/rtp_to_text.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/rtp_to_text.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/test/rtp_to_text.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Parses an rtpdump file and outputs a text table parsable by parseLog.m. - * The output file will have .txt appended to the specified base name. - * $ rtp_to_text [-d] - * - * -d RTP headers only - * - */ - -#include "data_log.h" -#include "NETEQTEST_DummyRTPpacket.h" -#include "NETEQTEST_RTPpacket.h" - -#include -#include - -#include -#include -#include - -/*********************/ -/* Misc. definitions */ -/*********************/ - -#define FIRSTLINELEN 40 - -using ::webrtc::DataLog; - -int main(int argc, char* argv[]) -{ - int arg_count = 1; - NETEQTEST_RTPpacket* packet; - - if (argc < 3) - { - printf("Usage: %s [-d] \n", argv[0]); - return -1; - } - - // Parse dummy option - if (argc >= 3 && strcmp(argv[arg_count], "-d") == 0) - { - packet = new NETEQTEST_DummyRTPpacket; - ++arg_count; - } - else - { - packet = new NETEQTEST_RTPpacket; - } - - std::string input_filename = argv[arg_count++]; - std::string table_name = argv[arg_count]; - - std::cout << "Input file: " << input_filename << std::endl; - std::cout << "Output file: " << table_name << ".txt" << std::endl; - - FILE *inFile=fopen(input_filename.c_str(),"rb"); - if (!inFile) - { - std::cout << "Cannot open input file " << input_filename << std::endl; - return -1; - } - - // Set up the DataLog and define the table - DataLog::CreateLog(); - if (DataLog::AddTable(table_name) < 0) - { - std::cout << "Error adding table " << table_name << ".txt" << std::endl; - return -1; - } - - DataLog::AddColumn(table_name, "seq", 1); - DataLog::AddColumn(table_name, "ssrc", 1); - DataLog::AddColumn(table_name, "payload type", 1); - DataLog::AddColumn(table_name, "length", 1); - DataLog::AddColumn(table_name, "timestamp", 1); - DataLog::AddColumn(table_name, "marker bit", 1); - DataLog::AddColumn(table_name, "arrival", 1); - - // read file header - char firstline[FIRSTLINELEN]; - if (fgets(firstline, FIRSTLINELEN, inFile) == NULL) - { - std::cout << "Error reading file " << input_filename << std::endl; - return -1; - } - - // start_sec + start_usec + source + port + padding - if (fread(firstline, 4+4+4+2+2, 1, inFile) != 1) - { - std::cout << "Error reading file " << input_filename << std::endl; - return -1; - } - - while (packet->readFromFile(inFile) >= 0) - { - // write packet headers to - DataLog::InsertCell(table_name, "seq", packet->sequenceNumber()); - DataLog::InsertCell(table_name, "ssrc", packet->SSRC()); - DataLog::InsertCell(table_name, "payload type", packet->payloadType()); - DataLog::InsertCell(table_name, "length", packet->dataLen()); - DataLog::InsertCell(table_name, "timestamp", packet->timeStamp()); - DataLog::InsertCell(table_name, "marker bit", packet->markerBit()); - DataLog::InsertCell(table_name, "arrival", packet->time()); - DataLog::NextRow(table_name); - return -1; - } - - DataLog::ReturnLog(); - - fclose(inFile); - - return 0; -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/timestamp_scaler.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/timestamp_scaler.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/timestamp_scaler.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/timestamp_scaler.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/timestamp_scaler.h" - -#include "webrtc/modules/audio_coding/neteq4/decoder_database.h" -#include "webrtc/modules/audio_coding/neteq4/defines.h" -#include "webrtc/system_wrappers/interface/logging.h" - -namespace webrtc { - -void TimestampScaler::ToInternal(Packet* packet) { - if (!packet) { - return; - } - packet->header.timestamp = ToInternal(packet->header.timestamp, - packet->header.payloadType); -} - -void TimestampScaler::ToInternal(PacketList* packet_list) { - PacketList::iterator it; - for (it = packet_list->begin(); it != packet_list->end(); ++it) { - ToInternal(*it); - } -} - -uint32_t TimestampScaler::ToInternal(uint32_t external_timestamp, - uint8_t rtp_payload_type) { - const DecoderDatabase::DecoderInfo* info = - decoder_database_.GetDecoderInfo(rtp_payload_type); - if (!info) { - // Payload type is unknown. Do not scale. - return external_timestamp; - } - switch (info->codec_type) { - case kDecoderG722: - case kDecoderG722_2ch: { - // Use timestamp scaling with factor 2 (two output samples per RTP - // timestamp). - numerator_ = 2; - denominator_ = 1; - break; - } - case kDecoderOpus: - case kDecoderOpus_2ch: - case kDecoderISACfb: - case kDecoderCNGswb48kHz: { - // Use timestamp scaling with factor 2/3 (32 kHz sample rate, but RTP - // timestamps run on 48 kHz). - // TODO(tlegrand): Remove scaling for kDecoderCNGswb48kHz once ACM has - // full 48 kHz support. - numerator_ = 2; - denominator_ = 3; - } - case kDecoderAVT: - case kDecoderCNGnb: - case kDecoderCNGwb: - case kDecoderCNGswb32kHz: { - // Do not change the timestamp scaling settings for DTMF or CNG. - break; - } - default: { - // Do not use timestamp scaling for any other codec. - numerator_ = 1; - denominator_ = 1; - break; - } - } - - if (!(numerator_ == 1 && denominator_ == 1)) { - // We have a scale factor != 1. - if (!first_packet_received_) { - external_ref_ = external_timestamp; - internal_ref_ = external_timestamp; - first_packet_received_ = true; - } - int32_t external_diff = external_timestamp - external_ref_; - assert(denominator_ > 0); // Should not be possible. - external_ref_ = external_timestamp; - internal_ref_ += (external_diff * numerator_) / denominator_; - LOG(LS_VERBOSE) << "Converting timestamp: " << external_timestamp << - " -> " << internal_ref_; - return internal_ref_; - } else { - // No scaling. - return external_timestamp; - } -} - - -uint32_t TimestampScaler::ToExternal(uint32_t internal_timestamp) const { - if (!first_packet_received_ || (numerator_ == 1 && denominator_ == 1)) { - // Not initialized, or scale factor is 1. - return internal_timestamp; - } else { - int32_t internal_diff = internal_timestamp - internal_ref_; - assert(numerator_ > 0); // Should not be possible. - // Do not update references in this method. - // Switch |denominator_| and |numerator_| to convert the other way. - return external_ref_ + (internal_diff * denominator_) / numerator_; - } -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/timestamp_scaler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/timestamp_scaler.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/timestamp_scaler.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/timestamp_scaler.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TIMESTAMP_SCALER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TIMESTAMP_SCALER_H_ - -#include "webrtc/modules/audio_coding/neteq4/packet.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Forward declaration. -class DecoderDatabase; - -// This class scales timestamps for codecs that need timestamp scaling. -// This is done for codecs where one RTP timestamp does not correspond to -// one sample. -class TimestampScaler { - public: - explicit TimestampScaler(const DecoderDatabase& decoder_database) - : first_packet_received_(false), - numerator_(1), - denominator_(1), - external_ref_(0), - internal_ref_(0), - decoder_database_(decoder_database) {} - - virtual ~TimestampScaler() {} - - // Start over. - virtual void Reset() { first_packet_received_ = false; } - - // Scale the timestamp in |packet| from external to internal. - virtual void ToInternal(Packet* packet); - - // Scale the timestamp for all packets in |packet_list| from external to - // internal. - virtual void ToInternal(PacketList* packet_list); - - // Returns the internal equivalent of |external_timestamp|, given the - // RTP payload type |rtp_payload_type|. - virtual uint32_t ToInternal(uint32_t external_timestamp, - uint8_t rtp_payload_type); - - // Scales back to external timestamp. This is the inverse of ToInternal(). - virtual uint32_t ToExternal(uint32_t internal_timestamp) const; - - private: - bool first_packet_received_; - int numerator_; - int denominator_; - uint32_t external_ref_; - uint32_t internal_ref_; - const DecoderDatabase& decoder_database_; - - DISALLOW_COPY_AND_ASSIGN(TimestampScaler); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TIMESTAMP_SCALER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/timestamp_scaler_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/timestamp_scaler_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/timestamp_scaler_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/timestamp_scaler_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,327 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/timestamp_scaler.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq4/mock/mock_decoder_database.h" -#include "webrtc/modules/audio_coding/neteq4/packet.h" - -using ::testing::Return; -using ::testing::ReturnNull; -using ::testing::_; - -namespace webrtc { - -TEST(TimestampScaler, TestNoScaling) { - MockDecoderDatabase db; - DecoderDatabase::DecoderInfo info; - info.codec_type = kDecoderPCMu; // Does not use scaled timestamps. - static const uint8_t kRtpPayloadType = 0; - EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) - .WillRepeatedly(Return(&info)); - - TimestampScaler scaler(db); - // Test both sides of the timestamp wrap-around. - for (uint32_t timestamp = 0xFFFFFFFF - 5; timestamp != 5; ++timestamp) { - // Scale to internal timestamp. - EXPECT_EQ(timestamp, scaler.ToInternal(timestamp, kRtpPayloadType)); - // Scale back. - EXPECT_EQ(timestamp, scaler.ToExternal(timestamp)); - } - - EXPECT_CALL(db, Die()); // Called when database object is deleted. -} - -TEST(TimestampScaler, TestNoScalingLargeStep) { - MockDecoderDatabase db; - DecoderDatabase::DecoderInfo info; - info.codec_type = kDecoderPCMu; // Does not use scaled timestamps. - static const uint8_t kRtpPayloadType = 0; - EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) - .WillRepeatedly(Return(&info)); - - TimestampScaler scaler(db); - // Test both sides of the timestamp wrap-around. - static const uint32_t kStep = 160; - uint32_t start_timestamp = 0; - // |external_timestamp| will be a large positive value. - start_timestamp = start_timestamp - 5 * kStep; - for (uint32_t timestamp = start_timestamp; timestamp != 5 * kStep; - timestamp += kStep) { - // Scale to internal timestamp. - EXPECT_EQ(timestamp, scaler.ToInternal(timestamp, kRtpPayloadType)); - // Scale back. - EXPECT_EQ(timestamp, scaler.ToExternal(timestamp)); - } - - EXPECT_CALL(db, Die()); // Called when database object is deleted. -} - -TEST(TimestampScaler, TestG722) { - MockDecoderDatabase db; - DecoderDatabase::DecoderInfo info; - info.codec_type = kDecoderG722; // Uses a factor 2 scaling. - static const uint8_t kRtpPayloadType = 17; - EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) - .WillRepeatedly(Return(&info)); - - TimestampScaler scaler(db); - // Test both sides of the timestamp wrap-around. - uint32_t external_timestamp = 0xFFFFFFFF - 5; - uint32_t internal_timestamp = external_timestamp; - for (; external_timestamp != 5; ++external_timestamp) { - // Scale to internal timestamp. - EXPECT_EQ(internal_timestamp, - scaler.ToInternal(external_timestamp, kRtpPayloadType)); - // Scale back. - EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); - internal_timestamp += 2; - } - - EXPECT_CALL(db, Die()); // Called when database object is deleted. -} - -TEST(TimestampScaler, TestG722LargeStep) { - MockDecoderDatabase db; - DecoderDatabase::DecoderInfo info; - info.codec_type = kDecoderG722; // Uses a factor 2 scaling. - static const uint8_t kRtpPayloadType = 17; - EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) - .WillRepeatedly(Return(&info)); - - TimestampScaler scaler(db); - // Test both sides of the timestamp wrap-around. - static const uint32_t kStep = 320; - uint32_t external_timestamp = 0; - // |external_timestamp| will be a large positive value. - external_timestamp = external_timestamp - 5 * kStep; - uint32_t internal_timestamp = external_timestamp; - for (; external_timestamp != 5 * kStep; external_timestamp += kStep) { - // Scale to internal timestamp. - EXPECT_EQ(internal_timestamp, - scaler.ToInternal(external_timestamp, kRtpPayloadType)); - // Scale back. - EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); - // Internal timestamp should be incremented with twice the step. - internal_timestamp += 2 * kStep; - } - - EXPECT_CALL(db, Die()); // Called when database object is deleted. -} - -TEST(TimestampScaler, TestG722WithCng) { - MockDecoderDatabase db; - DecoderDatabase::DecoderInfo info_g722, info_cng; - info_g722.codec_type = kDecoderG722; // Uses a factor 2 scaling. - info_cng.codec_type = kDecoderCNGwb; - static const uint8_t kRtpPayloadTypeG722 = 17; - static const uint8_t kRtpPayloadTypeCng = 13; - EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadTypeG722)) - .WillRepeatedly(Return(&info_g722)); - EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadTypeCng)) - .WillRepeatedly(Return(&info_cng)); - - TimestampScaler scaler(db); - // Test both sides of the timestamp wrap-around. - uint32_t external_timestamp = 0xFFFFFFFF - 5; - uint32_t internal_timestamp = external_timestamp; - bool next_is_cng = false; - for (; external_timestamp != 5; ++external_timestamp) { - // Alternate between G.722 and CNG every other packet. - if (next_is_cng) { - // Scale to internal timestamp. - EXPECT_EQ(internal_timestamp, - scaler.ToInternal(external_timestamp, kRtpPayloadTypeCng)); - next_is_cng = false; - } else { - // Scale to internal timestamp. - EXPECT_EQ(internal_timestamp, - scaler.ToInternal(external_timestamp, kRtpPayloadTypeG722)); - next_is_cng = true; - } - // Scale back. - EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); - internal_timestamp += 2; - } - - EXPECT_CALL(db, Die()); // Called when database object is deleted. -} - -// Make sure that the method ToInternal(Packet* packet) is wired up correctly. -// Since it is simply calling the other ToInternal method, we are not doing -// as many tests here. -TEST(TimestampScaler, TestG722Packet) { - MockDecoderDatabase db; - DecoderDatabase::DecoderInfo info; - info.codec_type = kDecoderG722; // Does uses a factor 2 scaling. - static const uint8_t kRtpPayloadType = 17; - EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) - .WillRepeatedly(Return(&info)); - - TimestampScaler scaler(db); - // Test both sides of the timestamp wrap-around. - uint32_t external_timestamp = 0xFFFFFFFF - 5; - uint32_t internal_timestamp = external_timestamp; - Packet packet; - packet.header.payloadType = kRtpPayloadType; - for (; external_timestamp != 5; ++external_timestamp) { - packet.header.timestamp = external_timestamp; - // Scale to internal timestamp. - scaler.ToInternal(&packet); - EXPECT_EQ(internal_timestamp, packet.header.timestamp); - internal_timestamp += 2; - } - - EXPECT_CALL(db, Die()); // Called when database object is deleted. -} - -// Make sure that the method ToInternal(PacketList* packet_list) is wired up -// correctly. Since it is simply calling the ToInternal(Packet* packet) method, -// we are not doing as many tests here. -TEST(TimestampScaler, TestG722PacketList) { - MockDecoderDatabase db; - DecoderDatabase::DecoderInfo info; - info.codec_type = kDecoderG722; // Uses a factor 2 scaling. - static const uint8_t kRtpPayloadType = 17; - EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) - .WillRepeatedly(Return(&info)); - - TimestampScaler scaler(db); - // Test both sides of the timestamp wrap-around. - uint32_t external_timestamp = 0xFFFFFFFF - 5; - uint32_t internal_timestamp = external_timestamp; - Packet packet1; - packet1.header.payloadType = kRtpPayloadType; - packet1.header.timestamp = external_timestamp; - Packet packet2; - packet2.header.payloadType = kRtpPayloadType; - packet2.header.timestamp = external_timestamp + 10; - PacketList packet_list; - packet_list.push_back(&packet1); - packet_list.push_back(&packet2); - - scaler.ToInternal(&packet_list); - EXPECT_EQ(internal_timestamp, packet1.header.timestamp); - EXPECT_EQ(internal_timestamp + 20, packet2.header.timestamp); - - EXPECT_CALL(db, Die()); // Called when database object is deleted. -} - -TEST(TimestampScaler, TestG722Reset) { - MockDecoderDatabase db; - DecoderDatabase::DecoderInfo info; - info.codec_type = kDecoderG722; // Uses a factor 2 scaling. - static const uint8_t kRtpPayloadType = 17; - EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) - .WillRepeatedly(Return(&info)); - - TimestampScaler scaler(db); - // Test both sides of the timestamp wrap-around. - uint32_t external_timestamp = 0xFFFFFFFF - 5; - uint32_t internal_timestamp = external_timestamp; - for (; external_timestamp != 5; ++external_timestamp) { - // Scale to internal timestamp. - EXPECT_EQ(internal_timestamp, - scaler.ToInternal(external_timestamp, kRtpPayloadType)); - // Scale back. - EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); - internal_timestamp += 2; - } - // Reset the scaler. After this, we expect the internal and external to start - // over at the same value again. - scaler.Reset(); - internal_timestamp = external_timestamp; - for (; external_timestamp != 15; ++external_timestamp) { - // Scale to internal timestamp. - EXPECT_EQ(internal_timestamp, - scaler.ToInternal(external_timestamp, kRtpPayloadType)); - // Scale back. - EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); - internal_timestamp += 2; - } - - EXPECT_CALL(db, Die()); // Called when database object is deleted. -} - -TEST(TimestampScaler, TestOpusLargeStep) { - MockDecoderDatabase db; - DecoderDatabase::DecoderInfo info; - info.codec_type = kDecoderOpus; // Uses a factor 2/3 scaling. - static const uint8_t kRtpPayloadType = 17; - EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) - .WillRepeatedly(Return(&info)); - - TimestampScaler scaler(db); - // Test both sides of the timestamp wrap-around. - static const uint32_t kStep = 960; - uint32_t external_timestamp = 0; - // |external_timestamp| will be a large positive value. - external_timestamp = external_timestamp - 5 * kStep; - uint32_t internal_timestamp = external_timestamp; - for (; external_timestamp != 5 * kStep; external_timestamp += kStep) { - // Scale to internal timestamp. - EXPECT_EQ(internal_timestamp, - scaler.ToInternal(external_timestamp, kRtpPayloadType)); - // Scale back. - EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); - // Internal timestamp should be incremented with twice the step. - internal_timestamp += 2 * kStep / 3; - } - - EXPECT_CALL(db, Die()); // Called when database object is deleted. -} - -TEST(TimestampScaler, TestIsacFbLargeStep) { - MockDecoderDatabase db; - DecoderDatabase::DecoderInfo info; - info.codec_type = kDecoderISACfb; // Uses a factor 2/3 scaling. - static const uint8_t kRtpPayloadType = 17; - EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) - .WillRepeatedly(Return(&info)); - - TimestampScaler scaler(db); - // Test both sides of the timestamp wrap-around. - static const uint32_t kStep = 960; - uint32_t external_timestamp = 0; - // |external_timestamp| will be a large positive value. - external_timestamp = external_timestamp - 5 * kStep; - uint32_t internal_timestamp = external_timestamp; - for (; external_timestamp != 5 * kStep; external_timestamp += kStep) { - // Scale to internal timestamp. - EXPECT_EQ(internal_timestamp, - scaler.ToInternal(external_timestamp, kRtpPayloadType)); - // Scale back. - EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); - // Internal timestamp should be incremented with twice the step. - internal_timestamp += 2 * kStep / 3; - } - - EXPECT_CALL(db, Die()); // Called when database object is deleted. -} - -TEST(TimestampScaler, Failures) { - static const uint8_t kRtpPayloadType = 17; - MockDecoderDatabase db; - EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) - .WillOnce(ReturnNull()); // Return NULL to indicate unknown payload type. - - TimestampScaler scaler(db); - uint32_t timestamp = 4711; // Some number. - EXPECT_EQ(timestamp, scaler.ToInternal(timestamp, kRtpPayloadType)); - - Packet* packet = NULL; - scaler.ToInternal(packet); // Should not crash. That's all we can test. - - EXPECT_CALL(db, Die()); // Called when database object is deleted. -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/time_stretch.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/time_stretch.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/time_stretch.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/time_stretch.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/time_stretch.h" - -#include // min, max - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_coding/neteq4/background_noise.h" -#include "webrtc/modules/audio_coding/neteq4/dsp_helper.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" - -namespace webrtc { - -TimeStretch::ReturnCodes TimeStretch::Process( - const int16_t* input, - size_t input_len, - AudioMultiVector* output, - int16_t* length_change_samples) { - - // Pre-calculate common multiplication with |fs_mult_|. - int fs_mult_120 = fs_mult_ * 120; // Corresponds to 15 ms. - - const int16_t* signal; - scoped_array signal_array; - size_t signal_len; - if (num_channels_ == 1) { - signal = input; - signal_len = input_len; - } else { - // We want |signal| to be only the first channel of |input|, which is - // interleaved. Thus, we take the first sample, skip forward |num_channels| - // samples, and continue like that. - signal_len = input_len / num_channels_; - signal_array.reset(new int16_t[signal_len]); - signal = signal_array.get(); - size_t j = master_channel_; - for (size_t i = 0; i < signal_len; ++i) { - signal_array[i] = input[j]; - j += num_channels_; - } - } - - // Find maximum absolute value of input signal. - max_input_value_ = WebRtcSpl_MaxAbsValueW16(signal, - static_cast(signal_len)); - - // Downsample to 4 kHz sample rate and calculate auto-correlation. - DspHelper::DownsampleTo4kHz(signal, signal_len, kDownsampledLen, - sample_rate_hz_, true /* compensate delay*/, - downsampled_input_); - AutoCorrelation(); - - // Find the strongest correlation peak. - static const int kNumPeaks = 1; - int peak_index; - int16_t peak_value; - DspHelper::PeakDetection(auto_correlation_, kCorrelationLen, kNumPeaks, - fs_mult_, &peak_index, &peak_value); - // Assert that |peak_index| stays within boundaries. - assert(peak_index >= 0); - assert(peak_index <= (2 * kCorrelationLen - 1) * fs_mult_); - - // Compensate peak_index for displaced starting position. The displacement - // happens in AutoCorrelation(). Here, |kMinLag| is in the down-sampled 4 kHz - // domain, while the |peak_index| is in the original sample rate; hence, the - // multiplication by fs_mult_ * 2. - peak_index += kMinLag * fs_mult_ * 2; - // Assert that |peak_index| stays within boundaries. - assert(peak_index >= 20 * fs_mult_); - assert(peak_index <= 20 * fs_mult_ + (2 * kCorrelationLen - 1) * fs_mult_); - - // Calculate scaling to ensure that |peak_index| samples can be square-summed - // without overflowing. - int scaling = 31 - WebRtcSpl_NormW32(max_input_value_ * max_input_value_) - - WebRtcSpl_NormW32(peak_index); - scaling = std::max(0, scaling); - - // |vec1| starts at 15 ms minus one pitch period. - const int16_t* vec1 = &signal[fs_mult_120 - peak_index]; - // |vec2| start at 15 ms. - const int16_t* vec2 = &signal[fs_mult_120]; - // Calculate energies for |vec1| and |vec2|, assuming they both contain - // |peak_index| samples. - int32_t vec1_energy = - WebRtcSpl_DotProductWithScale(vec1, vec1, peak_index, scaling); - int32_t vec2_energy = - WebRtcSpl_DotProductWithScale(vec2, vec2, peak_index, scaling); - - // Calculate cross-correlation between |vec1| and |vec2|. - int32_t cross_corr = - WebRtcSpl_DotProductWithScale(vec1, vec2, peak_index, scaling); - - // Check if the signal seems to be active speech or not (simple VAD). - bool active_speech = SpeechDetection(vec1_energy, vec2_energy, peak_index, - scaling); - - int16_t best_correlation; - if (!active_speech) { - SetParametersForPassiveSpeech(signal_len, &best_correlation, &peak_index); - } else { - // Calculate correlation: - // cross_corr / sqrt(vec1_energy * vec2_energy). - - // Start with calculating scale values. - int energy1_scale = std::max(0, 16 - WebRtcSpl_NormW32(vec1_energy)); - int energy2_scale = std::max(0, 16 - WebRtcSpl_NormW32(vec2_energy)); - - // Make sure total scaling is even (to simplify scale factor after sqrt). - if ((energy1_scale + energy2_scale) & 1) { - // The sum is odd. - energy1_scale += 1; - } - - // Scale energies to int16_t. - int16_t vec1_energy_int16 = - static_cast(vec1_energy >> energy1_scale); - int16_t vec2_energy_int16 = - static_cast(vec2_energy >> energy2_scale); - - // Calculate square-root of energy product. - int16_t sqrt_energy_prod = WebRtcSpl_SqrtFloor(vec1_energy_int16 * - vec2_energy_int16); - - // Calculate cross_corr / sqrt(en1*en2) in Q14. - int temp_scale = 14 - (energy1_scale + energy2_scale) / 2; - cross_corr = WEBRTC_SPL_SHIFT_W32(cross_corr, temp_scale); - cross_corr = std::max(0, cross_corr); // Don't use if negative. - best_correlation = WebRtcSpl_DivW32W16(cross_corr, sqrt_energy_prod); - // Make sure |best_correlation| is no larger than 1 in Q14. - best_correlation = std::min(static_cast(16384), best_correlation); - } - - - // Check accelerate criteria and stretch the signal. - ReturnCodes return_value = CheckCriteriaAndStretch( - input, input_len, peak_index, best_correlation, active_speech, output); - switch (return_value) { - case kSuccess: - *length_change_samples = peak_index; - break; - case kSuccessLowEnergy: - *length_change_samples = peak_index; - break; - case kNoStretch: - case kError: - *length_change_samples = 0; - break; - } - return return_value; -} - -void TimeStretch::AutoCorrelation() { - // Set scaling factor for cross correlation to protect against overflow. - int scaling = kLogCorrelationLen - WebRtcSpl_NormW32( - max_input_value_ * max_input_value_); - scaling = std::max(0, scaling); - - // Calculate correlation from lag kMinLag to lag kMaxLag in 4 kHz domain. - int32_t auto_corr[kCorrelationLen]; - WebRtcSpl_CrossCorrelation(auto_corr, &downsampled_input_[kMaxLag], - &downsampled_input_[kMaxLag - kMinLag], - kCorrelationLen, kMaxLag - kMinLag, scaling, -1); - - // Normalize correlation to 14 bits and write to |auto_correlation_|. - int32_t max_corr = WebRtcSpl_MaxAbsValueW32(auto_corr, kCorrelationLen); - scaling = std::max(0, 17 - WebRtcSpl_NormW32(max_corr)); - WebRtcSpl_VectorBitShiftW32ToW16(auto_correlation_, kCorrelationLen, - auto_corr, scaling); -} - -bool TimeStretch::SpeechDetection(int32_t vec1_energy, int32_t vec2_energy, - int peak_index, int scaling) const { - // Check if the signal seems to be active speech or not (simple VAD). - // If (vec1_energy + vec2_energy) / (2 * peak_index) <= - // 8 * background_noise_energy, then we say that the signal contains no - // active speech. - // Rewrite the inequality as: - // (vec1_energy + vec2_energy) / 16 <= peak_index * background_noise_energy. - // The two sides of the inequality will be denoted |left_side| and - // |right_side|. - int32_t left_side = (vec1_energy + vec2_energy) / 16; - int32_t right_side; - if (background_noise_.initialized()) { - right_side = background_noise_.Energy(master_channel_); - } else { - // If noise parameters have not been estimated, use a fixed threshold. - right_side = 75000; - } - int right_scale = 16 - WebRtcSpl_NormW32(right_side); - right_scale = std::max(0, right_scale); - left_side = left_side >> right_scale; - right_side = peak_index * (right_side >> right_scale); - - // Scale |left_side| properly before comparing with |right_side|. - // (|scaling| is the scale factor before energy calculation, thus the scale - // factor for the energy is 2 * scaling.) - if (WebRtcSpl_NormW32(left_side) < 2 * scaling) { - // Cannot scale only |left_side|, must scale |right_side| too. - int temp_scale = WebRtcSpl_NormW32(left_side); - left_side = left_side << temp_scale; - right_side = right_side >> (2 * scaling - temp_scale); - } else { - left_side = left_side << 2 * scaling; - } - return left_side > right_side; -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/time_stretch.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/time_stretch.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/time_stretch.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/time_stretch.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TIME_STRETCH_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TIME_STRETCH_H_ - -#include -#include // memset, size_t - -#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Forward declarations. -class BackgroundNoise; - -// This is the base class for Accelerate and PreemptiveExpand. This class -// cannot be instantiated, but must be used through either of the derived -// classes. -class TimeStretch { - public: - enum ReturnCodes { - kSuccess = 0, - kSuccessLowEnergy = 1, - kNoStretch = 2, - kError = -1 - }; - - TimeStretch(int sample_rate_hz, size_t num_channels, - const BackgroundNoise& background_noise) - : sample_rate_hz_(sample_rate_hz), - fs_mult_(sample_rate_hz / 8000), - num_channels_(static_cast(num_channels)), - master_channel_(0), // First channel is master. - background_noise_(background_noise), - max_input_value_(0) { - assert(sample_rate_hz_ == 8000 || - sample_rate_hz_ == 16000 || - sample_rate_hz_ == 32000 || - sample_rate_hz_ == 48000); - assert(num_channels_ > 0); - assert(static_cast(master_channel_) < num_channels_); - memset(auto_correlation_, 0, sizeof(auto_correlation_)); - } - - virtual ~TimeStretch() {} - - // This method performs the processing common to both Accelerate and - // PreemptiveExpand. - ReturnCodes Process(const int16_t* input, - size_t input_len, - AudioMultiVector* output, - int16_t* length_change_samples); - - protected: - // Sets the parameters |best_correlation| and |peak_index| to suitable - // values when the signal contains no active speech. This method must be - // implemented by the sub-classes. - virtual void SetParametersForPassiveSpeech(size_t input_length, - int16_t* best_correlation, - int* peak_index) const = 0; - - // Checks the criteria for performing the time-stretching operation and, - // if possible, performs the time-stretching. This method must be implemented - // by the sub-classes. - virtual ReturnCodes CheckCriteriaAndStretch( - const int16_t* input, size_t input_length, size_t peak_index, - int16_t best_correlation, bool active_speech, - AudioMultiVector* output) const = 0; - - static const int kCorrelationLen = 50; - static const int kLogCorrelationLen = 6; // >= log2(kCorrelationLen). - static const int kMinLag = 10; - static const int kMaxLag = 60; - static const int kDownsampledLen = kCorrelationLen + kMaxLag; - static const int kCorrelationThreshold = 14746; // 0.9 in Q14. - - const int sample_rate_hz_; - const int fs_mult_; // Sample rate multiplier = sample_rate_hz_ / 8000. - const int num_channels_; - const size_t master_channel_; - const BackgroundNoise& background_noise_; - int16_t max_input_value_; - int16_t downsampled_input_[kDownsampledLen]; - // Adding 1 to the size of |auto_correlation_| because of how it is used - // by the peak-detection algorithm. - int16_t auto_correlation_[kCorrelationLen + 1]; - - private: - // Calculates the auto-correlation of |downsampled_input_| and writes the - // result to |auto_correlation_|. - void AutoCorrelation(); - - // Performs a simple voice-activity detection based on the input parameters. - bool SpeechDetection(int32_t vec1_energy, int32_t vec2_energy, - int peak_index, int scaling) const; - - DISALLOW_COPY_AND_ASSIGN(TimeStretch); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TIME_STRETCH_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/time_stretch_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/time_stretch_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/time_stretch_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/time_stretch_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Unit tests for Accelerate and PreemptiveExpand classes. - -#include "webrtc/modules/audio_coding/neteq4/accelerate.h" -#include "webrtc/modules/audio_coding/neteq4/preemptive_expand.h" - -#include "gtest/gtest.h" -#include "webrtc/modules/audio_coding/neteq4/background_noise.h" - -namespace webrtc { - -TEST(TimeStretch, CreateAndDestroy) { - const int kSampleRate = 8000; - const size_t kNumChannels = 1; - BackgroundNoise bgn(kNumChannels); - Accelerate accelerate(kSampleRate, kNumChannels, bgn); - PreemptiveExpand preemptive_expand(kSampleRate, kNumChannels, bgn); -} - -TEST(TimeStretch, CreateUsingFactory) { - const int kSampleRate = 8000; - const size_t kNumChannels = 1; - BackgroundNoise bgn(kNumChannels); - - AccelerateFactory accelerate_factory; - Accelerate* accelerate = - accelerate_factory.Create(kSampleRate, kNumChannels, bgn); - EXPECT_TRUE(accelerate != NULL); - delete accelerate; - - PreemptiveExpandFactory preemptive_expand_factory; - PreemptiveExpand* preemptive_expand = - preemptive_expand_factory.Create(kSampleRate, kNumChannels, bgn); - EXPECT_TRUE(preemptive_expand != NULL); - delete preemptive_expand; -} - -// TODO(hlundin): Write more tests. - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/audio_loop.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/audio_loop.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/audio_loop.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/audio_loop.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/tools/audio_loop.h" - -#include -#include -#include - -namespace webrtc { -namespace test { - -bool AudioLoop::Init(const std::string file_name, - size_t max_loop_length_samples, - size_t block_length_samples) { - FILE* fp = fopen(file_name.c_str(), "rb"); - if (!fp) return false; - - audio_array_.reset(new int16_t[max_loop_length_samples + - block_length_samples]); - size_t samples_read = fread(audio_array_.get(), sizeof(int16_t), - max_loop_length_samples, fp); - fclose(fp); - - // Block length must be shorter than the loop length. - if (block_length_samples > samples_read) return false; - - // Add an extra block length of samples to the end of the array, starting - // over again from the beginning of the array. This is done to simplify - // the reading process when reading over the end of the loop. - memcpy(&audio_array_[samples_read], audio_array_.get(), - block_length_samples * sizeof(int16_t)); - - loop_length_samples_ = samples_read; - block_length_samples_ = block_length_samples; - return true; -} - -const int16_t* AudioLoop::GetNextBlock() { - // Check that the AudioLoop is initialized. - if (block_length_samples_ == 0) return NULL; - - const int16_t* output_ptr = &audio_array_[next_index_]; - next_index_ = (next_index_ + block_length_samples_) % loop_length_samples_; - return output_ptr; -} - - -} // namespace test -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/audio_loop.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/audio_loop.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/audio_loop.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/audio_loop.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_AUDIO_LOOP_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_AUDIO_LOOP_H_ - -#include - -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/typedefs.h" - -namespace webrtc { -namespace test { - -// Class serving as an infinite source of audio, realized by looping an audio -// clip. -class AudioLoop { - public: - AudioLoop() - : next_index_(0), - loop_length_samples_(0), - block_length_samples_(0), - audio_array_(NULL) { - } - - virtual ~AudioLoop() {} - - // Initializes the AudioLoop by reading from |file_name|. The loop will be no - // longer than |max_loop_length_samples|, if the length of the file is - // greater. Otherwise, the loop length is the same as the file length. - // The audio will be delivered in blocks of |block_length_samples|. - // Returns false if the initialization failed, otherwise true. - bool Init(const std::string file_name, size_t max_loop_length_samples, - size_t block_length_samples); - - // Returns a pointer to the next block of audio. The number given as - // |block_length_samples| to the Init() function determines how many samples - // that can be safely read from the pointer. - const int16_t* GetNextBlock(); - - private: - size_t next_index_; - size_t loop_length_samples_; - size_t block_length_samples_; - scoped_array audio_array_; - - DISALLOW_COPY_AND_ASSIGN(AudioLoop); -}; - -} // namespace test -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_AUDIO_LOOP_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/input_audio_file.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/input_audio_file.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/input_audio_file.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/input_audio_file.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/tools/input_audio_file.h" - -namespace webrtc { -namespace test { - -InputAudioFile::InputAudioFile(const std::string file_name) { - fp_ = fopen(file_name.c_str(), "rb"); -} - -InputAudioFile::~InputAudioFile() { fclose(fp_); } - -bool InputAudioFile::Read(size_t samples, int16_t* destination) { - if (!fp_) { - return false; - } - size_t samples_read = fread(destination, sizeof(int16_t), samples, fp_); - if (samples_read < samples) { - // Rewind and read the missing samples. - rewind(fp_); - size_t missing_samples = samples - samples_read; - if (fread(destination, sizeof(int16_t), missing_samples, fp_) < - missing_samples) { - // Could not read enough even after rewinding the file. - return false; - } - } - return true; -} - -void InputAudioFile::DuplicateInterleaved(const int16_t* source, size_t samples, - size_t channels, - int16_t* destination) { - for (size_t i = 0; i < samples; ++i) { - for (size_t j = 0; j < channels; ++j) { - destination[i * channels + j] = source[i]; - } - } -} - -} // namespace test -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/input_audio_file.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/input_audio_file.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/input_audio_file.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/input_audio_file.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_INPUT_AUDIO_FILE_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_INPUT_AUDIO_FILE_H_ - -#include - -#include - -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { -namespace test { - -// Class for handling a looping input audio file. -class InputAudioFile { - public: - explicit InputAudioFile(const std::string file_name); - - virtual ~InputAudioFile(); - - // Reads |samples| elements from source file to |destination|. Returns true - // if the read was successful, otherwise false. If the file end is reached, - // the file is rewound and reading continues from the beginning. - // The output |destination| must have the capacity to hold |samples| elements. - bool Read(size_t samples, int16_t* destination); - - // Creates a multi-channel signal from a mono signal. Each sample is repeated - // |channels| times to create an interleaved multi-channel signal where all - // channels are identical. The output |destination| must have the capacity to - // hold samples * channels elements. - static void DuplicateInterleaved(const int16_t* source, size_t samples, - size_t channels, int16_t* destination); - - private: - FILE* fp_; - DISALLOW_COPY_AND_ASSIGN(InputAudioFile); -}; - -} // namespace test -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_INPUT_AUDIO_FILE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/neteq_performance_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/neteq_performance_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/neteq_performance_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/neteq_performance_test.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/audio_coding/neteq4/tools/neteq_performance_test.h" - -#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" -#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" -#include "webrtc/modules/audio_coding/neteq4/tools/audio_loop.h" -#include "webrtc/modules/audio_coding/neteq4/tools/rtp_generator.h" -#include "webrtc/system_wrappers/interface/clock.h" -#include "webrtc/test/testsupport/fileutils.h" -#include "webrtc/typedefs.h" - -using webrtc::NetEq; -using webrtc::test::AudioLoop; -using webrtc::test::RtpGenerator; -using webrtc::WebRtcRTPHeader; - -namespace webrtc { -namespace test { - -int64_t NetEqPerformanceTest::Run(int runtime_ms, - int lossrate, - double drift_factor) { - const std::string kInputFileName = - webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); - const int kSampRateHz = 32000; - const webrtc::NetEqDecoder kDecoderType = webrtc::kDecoderPCM16Bswb32kHz; - const int kPayloadType = 95; - - // Initialize NetEq instance. - NetEq* neteq = NetEq::Create(kSampRateHz); - // Register decoder in |neteq|. - if (neteq->RegisterPayloadType(kDecoderType, kPayloadType) != 0) - return -1; - - // Set up AudioLoop object. - AudioLoop audio_loop; - const size_t kMaxLoopLengthSamples = kSampRateHz * 10; // 10 second loop. - const size_t kInputBlockSizeSamples = 60 * kSampRateHz / 1000; // 60 ms. - if (!audio_loop.Init(kInputFileName, kMaxLoopLengthSamples, - kInputBlockSizeSamples)) - return -1; - - int32_t time_now_ms = 0; - - // Get first input packet. - WebRtcRTPHeader rtp_header; - RtpGenerator rtp_gen(kSampRateHz / 1000); - // Start with positive drift first half of simulation. - rtp_gen.set_drift_factor(drift_factor); - bool drift_flipped = false; - int32_t packet_input_time_ms = - rtp_gen.GetRtpHeader(kPayloadType, kInputBlockSizeSamples, &rtp_header); - const int16_t* input_samples = audio_loop.GetNextBlock(); - if (!input_samples) exit(1); - uint8_t input_payload[kInputBlockSizeSamples * sizeof(int16_t)]; - int payload_len = WebRtcPcm16b_Encode(const_cast(input_samples), - kInputBlockSizeSamples, - input_payload); - assert(payload_len == kInputBlockSizeSamples * sizeof(int16_t)); - - // Main loop. - webrtc::Clock* clock = webrtc::Clock::GetRealTimeClock(); - int64_t start_time_ms = clock->TimeInMilliseconds(); - while (time_now_ms < runtime_ms) { - while (packet_input_time_ms <= time_now_ms) { - // Drop every N packets, where N = FLAGS_lossrate. - bool lost = false; - if (lossrate > 0) { - lost = ((rtp_header.header.sequenceNumber - 1) % lossrate) == 0; - } - if (!lost) { - // Insert packet. - int error = neteq->InsertPacket( - rtp_header, input_payload, payload_len, - packet_input_time_ms * kSampRateHz / 1000); - if (error != NetEq::kOK) - return -1; - } - - // Get next packet. - packet_input_time_ms = rtp_gen.GetRtpHeader(kPayloadType, - kInputBlockSizeSamples, - &rtp_header); - input_samples = audio_loop.GetNextBlock(); - if (!input_samples) return -1; - payload_len = WebRtcPcm16b_Encode(const_cast(input_samples), - kInputBlockSizeSamples, - input_payload); - assert(payload_len == kInputBlockSizeSamples * sizeof(int16_t)); - } - - // Get output audio, but don't do anything with it. - static const int kMaxChannels = 1; - static const int kMaxSamplesPerMs = 48000 / 1000; - static const int kOutputBlockSizeMs = 10; - static const int kOutDataLen = kOutputBlockSizeMs * kMaxSamplesPerMs * - kMaxChannels; - int16_t out_data[kOutDataLen]; - int num_channels; - int samples_per_channel; - int error = neteq->GetAudio(kOutDataLen, out_data, &samples_per_channel, - &num_channels, NULL); - if (error != NetEq::kOK) - return -1; - - assert(samples_per_channel == kSampRateHz * 10 / 1000); - - time_now_ms += kOutputBlockSizeMs; - if (time_now_ms >= runtime_ms / 2 && !drift_flipped) { - // Apply negative drift second half of simulation. - rtp_gen.set_drift_factor(-drift_factor); - drift_flipped = true; - } - } - int64_t end_time_ms = clock->TimeInMilliseconds(); - delete neteq; - return end_time_ms - start_time_ms; -} - -} // namespace test -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/neteq_performance_test.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/neteq_performance_test.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/neteq_performance_test.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/neteq_performance_test.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_NETEQ_PERFORMANCE_TEST_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_NETEQ_PERFORMANCE_TEST_H_ - -#include "webrtc/typedefs.h" - -namespace webrtc { -namespace test { - -class NetEqPerformanceTest { - public: - // Runs a performance test with parameters as follows: - // |runtime_ms|: the simulation time, i.e., the duration of the audio data. - // |lossrate|: drop one out of |lossrate| packets, e.g., one out of 10. - // |drift_factor|: clock drift in [0, 1]. - // Returns the runtime in ms. - static int64_t Run(int runtime_ms, int lossrate, double drift_factor); -}; - -} // namespace test -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_NETEQ_PERFORMANCE_TEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/neteq_rtpplay.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/neteq_rtpplay.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/neteq_rtpplay.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/neteq_rtpplay.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,626 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// TODO(hlundin): The functionality in this file should be moved into one or -// several classes. - -#include -#include - -#include -#include -#include - -#include "google/gflags.h" -#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" -#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" -#include "webrtc/modules/audio_coding/neteq4/test/NETEQTEST_RTPpacket.h" -#include "webrtc/modules/audio_coding/neteq4/test/NETEQTEST_DummyRTPpacket.h" -#include "webrtc/modules/audio_coding/neteq4/tools/input_audio_file.h" -#include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/system_wrappers/interface/trace.h" -#include "webrtc/test/testsupport/fileutils.h" -#include "webrtc/typedefs.h" - -using webrtc::NetEq; -using webrtc::WebRtcRTPHeader; - -// Flag validators. -static bool ValidatePayloadType(const char* flagname, int32_t value) { - if (value >= 0 && value <= 127) // Value is ok. - return true; - printf("Invalid value for --%s: %d\n", flagname, static_cast(value)); - return false; -} - -// Define command line flags. -DEFINE_int32(pcmu, 0, "RTP payload type for PCM-u"); -static const bool pcmu_dummy = - google::RegisterFlagValidator(&FLAGS_pcmu, &ValidatePayloadType); -DEFINE_int32(pcma, 8, "RTP payload type for PCM-a"); -static const bool pcma_dummy = - google::RegisterFlagValidator(&FLAGS_pcma, &ValidatePayloadType); -DEFINE_int32(ilbc, 102, "RTP payload type for iLBC"); -static const bool ilbc_dummy = - google::RegisterFlagValidator(&FLAGS_ilbc, &ValidatePayloadType); -DEFINE_int32(isac, 103, "RTP payload type for iSAC"); -static const bool isac_dummy = - google::RegisterFlagValidator(&FLAGS_isac, &ValidatePayloadType); -DEFINE_int32(isac_swb, 104, "RTP payload type for iSAC-swb (32 kHz)"); -static const bool isac_swb_dummy = - google::RegisterFlagValidator(&FLAGS_isac_swb, &ValidatePayloadType); -DEFINE_int32(pcm16b, 93, "RTP payload type for PCM16b-nb (8 kHz)"); -static const bool pcm16b_dummy = - google::RegisterFlagValidator(&FLAGS_pcm16b, &ValidatePayloadType); -DEFINE_int32(pcm16b_wb, 94, "RTP payload type for PCM16b-wb (16 kHz)"); -static const bool pcm16b_wb_dummy = - google::RegisterFlagValidator(&FLAGS_pcm16b_wb, &ValidatePayloadType); -DEFINE_int32(pcm16b_swb32, 95, "RTP payload type for PCM16b-swb32 (32 kHz)"); -static const bool pcm16b_swb32_dummy = - google::RegisterFlagValidator(&FLAGS_pcm16b_swb32, &ValidatePayloadType); -DEFINE_int32(pcm16b_swb48, 96, "RTP payload type for PCM16b-swb48 (48 kHz)"); -static const bool pcm16b_swb48_dummy = - google::RegisterFlagValidator(&FLAGS_pcm16b_swb48, &ValidatePayloadType); -DEFINE_int32(g722, 9, "RTP payload type for G.722"); -static const bool g722_dummy = - google::RegisterFlagValidator(&FLAGS_g722, &ValidatePayloadType); -DEFINE_int32(avt, 106, "RTP payload type for AVT/DTMF"); -static const bool avt_dummy = - google::RegisterFlagValidator(&FLAGS_avt, &ValidatePayloadType); -DEFINE_int32(red, 117, "RTP payload type for redundant audio (RED)"); -static const bool red_dummy = - google::RegisterFlagValidator(&FLAGS_red, &ValidatePayloadType); -DEFINE_int32(cn_nb, 13, "RTP payload type for comfort noise (8 kHz)"); -static const bool cn_nb_dummy = - google::RegisterFlagValidator(&FLAGS_cn_nb, &ValidatePayloadType); -DEFINE_int32(cn_wb, 98, "RTP payload type for comfort noise (16 kHz)"); -static const bool cn_wb_dummy = - google::RegisterFlagValidator(&FLAGS_cn_wb, &ValidatePayloadType); -DEFINE_int32(cn_swb32, 99, "RTP payload type for comfort noise (32 kHz)"); -static const bool cn_swb32_dummy = - google::RegisterFlagValidator(&FLAGS_cn_swb32, &ValidatePayloadType); -DEFINE_int32(cn_swb48, 100, "RTP payload type for comfort noise (48 kHz)"); -static const bool cn_swb48_dummy = - google::RegisterFlagValidator(&FLAGS_cn_swb48, &ValidatePayloadType); -DEFINE_bool(codec_map, false, "Prints the mapping between RTP payload type and " - "codec"); -DEFINE_bool(dummy_rtp, false, "The input file contains ""dummy"" RTP data, " - "i.e., only headers"); -DEFINE_string(replacement_audio_file, "", - "A PCM file that will be used to populate ""dummy"" RTP packets"); - -// Declaring helper functions (defined further down in this file). -std::string CodecName(webrtc::NetEqDecoder codec); -void RegisterPayloadTypes(NetEq* neteq); -void PrintCodecMapping(); -size_t ReplacePayload(webrtc::test::InputAudioFile* replacement_audio_file, - webrtc::scoped_ptr* replacement_audio, - webrtc::scoped_ptr* payload, - size_t* payload_mem_size_bytes, - size_t* frame_size_samples, - WebRtcRTPHeader* rtp_header, - NETEQTEST_RTPpacket* next_rtp); -int CodecSampleRate(uint8_t payload_type); -int CodecTimestampRate(uint8_t payload_type); -bool IsComfortNosie(uint8_t payload_type); - -int main(int argc, char* argv[]) { - static const int kMaxChannels = 5; - static const int kMaxSamplesPerMs = 48000 / 1000; - static const int kOutputBlockSizeMs = 10; - - std::string program_name = argv[0]; - std::string usage = "Tool for decoding an RTP dump file using NetEq.\n" - "Run " + program_name + " --helpshort for usage.\n" - "Example usage:\n" + program_name + - " input.rtp output.pcm\n"; - google::SetUsageMessage(usage); - google::ParseCommandLineFlags(&argc, &argv, true); - - if (FLAGS_codec_map) { - PrintCodecMapping(); - } - - if (argc != 3) { - if (FLAGS_codec_map) { - // We have already printed the codec map. Just end the program. - return 0; - } - // Print usage information. - std::cout << google::ProgramUsage(); - return 0; - } - - FILE* in_file = fopen(argv[1], "rb"); - if (!in_file) { - std::cerr << "Cannot open input file " << argv[1] << std::endl; - exit(1); - } - std::cout << "Input file: " << argv[1] << std::endl; - - FILE* out_file = fopen(argv[2], "wb"); - if (!in_file) { - std::cerr << "Cannot open output file " << argv[2] << std::endl; - exit(1); - } - std::cout << "Output file: " << argv[2] << std::endl; - - // Check if a replacement audio file was provided, and if so, open it. - bool replace_payload = false; - webrtc::scoped_ptr replacement_audio_file; - if (!FLAGS_replacement_audio_file.empty()) { - replacement_audio_file.reset( - new webrtc::test::InputAudioFile(FLAGS_replacement_audio_file)); - replace_payload = true; - } - - // Read RTP file header. - if (NETEQTEST_RTPpacket::skipFileHeader(in_file) != 0) { - std::cerr << "Wrong format in RTP file" << std::endl; - exit(1); - } - - // Enable tracing. - webrtc::Trace::CreateTrace(); - webrtc::Trace::SetTraceFile((webrtc::test::OutputPath() + - "neteq_trace.txt").c_str()); - webrtc::Trace::set_level_filter(webrtc::kTraceAll); - - // Initialize NetEq instance. - int sample_rate_hz = 16000; - NetEq* neteq = NetEq::Create(sample_rate_hz); - RegisterPayloadTypes(neteq); - - // Read first packet. - NETEQTEST_RTPpacket* rtp; - NETEQTEST_RTPpacket* next_rtp = NULL; - if (!FLAGS_dummy_rtp) { - rtp = new NETEQTEST_RTPpacket(); - if (replace_payload) { - next_rtp = new NETEQTEST_RTPpacket(); - } - } else { - rtp = new NETEQTEST_DummyRTPpacket(); - if (replace_payload) { - next_rtp = new NETEQTEST_DummyRTPpacket(); - } - } - rtp->readFromFile(in_file); - if (rtp->dataLen() < 0) { - std::cout << "Warning: RTP file is empty" << std::endl; - } - - // Set up variables for audio replacement if needed. - size_t input_frame_size_timestamps = 0; - webrtc::scoped_ptr replacement_audio; - webrtc::scoped_ptr payload; - size_t payload_mem_size_bytes = 0; - if (replace_payload) { - // Initially assume that the frame size is 30 ms at the initial sample rate. - // This value will be replaced with the correct one as soon as two - // consecutive packets are found. - input_frame_size_timestamps = 30 * sample_rate_hz / 1000; - replacement_audio.reset(new int16_t[input_frame_size_timestamps]); - payload_mem_size_bytes = 2 * input_frame_size_timestamps; - payload.reset(new uint8_t[payload_mem_size_bytes]); - assert(next_rtp); - next_rtp->readFromFile(in_file); - } - - // This is the main simulation loop. - int time_now_ms = rtp->time(); // Start immediately with the first packet. - int next_input_time_ms = rtp->time(); - int next_output_time_ms = time_now_ms; - if (time_now_ms % kOutputBlockSizeMs != 0) { - // Make sure that next_output_time_ms is rounded up to the next multiple - // of kOutputBlockSizeMs. (Legacy bit-exactness.) - next_output_time_ms += - kOutputBlockSizeMs - time_now_ms % kOutputBlockSizeMs; - } - while (rtp->dataLen() >= 0) { - // Check if it is time to insert packet. - while (time_now_ms >= next_input_time_ms && rtp->dataLen() >= 0) { - if (rtp->dataLen() > 0) { - // Parse RTP header. - WebRtcRTPHeader rtp_header; - rtp->parseHeader(&rtp_header); - uint8_t* payload_ptr = rtp->payload(); - size_t payload_len = rtp->payloadLen(); - if (replace_payload) { - payload_len = ReplacePayload(replacement_audio_file.get(), - &replacement_audio, - &payload, - &payload_mem_size_bytes, - &input_frame_size_timestamps, - &rtp_header, - next_rtp); - payload_ptr = payload.get(); - } - int error = neteq->InsertPacket(rtp_header, payload_ptr, - static_cast(payload_len), - rtp->time() * sample_rate_hz / 1000); - if (error != NetEq::kOK) { - std::cerr << "InsertPacket returned error code " << - neteq->LastError() << std::endl; - } - } - // Get next packet from file. - rtp->readFromFile(in_file); - if (replace_payload) { - // At this point |rtp| contains the packet *after* |next_rtp|. - // Swap RTP packet objects between |rtp| and |next_rtp|. - NETEQTEST_RTPpacket* temp_rtp = rtp; - rtp = next_rtp; - next_rtp = temp_rtp; - } - next_input_time_ms = rtp->time(); - } - - // Check if it is time to get output audio. - if (time_now_ms >= next_output_time_ms) { - static const int kOutDataLen = kOutputBlockSizeMs * kMaxSamplesPerMs * - kMaxChannels; - int16_t out_data[kOutDataLen]; - int num_channels; - int samples_per_channel; - int error = neteq->GetAudio(kOutDataLen, out_data, &samples_per_channel, - &num_channels, NULL); - if (error != NetEq::kOK) { - std::cerr << "GetAudio returned error code " << - neteq->LastError() << std::endl; - } else { - // Calculate sample rate from output size. - sample_rate_hz = 1000 * samples_per_channel / kOutputBlockSizeMs; - } - - // Write to file. - // TODO(hlundin): Make writing to file optional. - size_t write_len = samples_per_channel * num_channels; - if (fwrite(out_data, sizeof(out_data[0]), write_len, out_file) != - write_len) { - std::cerr << "Error while writing to file" << std::endl; - webrtc::Trace::ReturnTrace(); - exit(1); - } - next_output_time_ms += kOutputBlockSizeMs; - } - // Advance time to next event. - time_now_ms = std::min(next_input_time_ms, next_output_time_ms); - } - - std::cout << "Simulation done" << std::endl; - - fclose(in_file); - fclose(out_file); - delete rtp; - delete next_rtp; - delete neteq; - webrtc::Trace::ReturnTrace(); - return 0; -} - - -// Help functions. - -// Maps a codec type to a printable name string. -std::string CodecName(webrtc::NetEqDecoder codec) { - switch (codec) { - case webrtc::kDecoderPCMu: - return "PCM-u"; - case webrtc::kDecoderPCMa: - return "PCM-a"; - case webrtc::kDecoderILBC: - return "iLBC"; - case webrtc::kDecoderISAC: - return "iSAC"; - case webrtc::kDecoderISACswb: - return "iSAC-swb (32 kHz)"; - case webrtc::kDecoderPCM16B: - return "PCM16b-nb (8 kHz)"; - case webrtc::kDecoderPCM16Bwb: - return "PCM16b-wb (16 kHz)"; - case webrtc::kDecoderPCM16Bswb32kHz: - return "PCM16b-swb32 (32 kHz)"; - case webrtc::kDecoderPCM16Bswb48kHz: - return "PCM16b-swb48 (48 kHz)"; - case webrtc::kDecoderG722: - return "G.722"; - case webrtc::kDecoderRED: - return "redundant audio (RED)"; - case webrtc::kDecoderAVT: - return "AVT/DTMF"; - case webrtc::kDecoderCNGnb: - return "comfort noise (8 kHz)"; - case webrtc::kDecoderCNGwb: - return "comfort noise (16 kHz)"; - case webrtc::kDecoderCNGswb32kHz: - return "comfort noise (32 kHz)"; - case webrtc::kDecoderCNGswb48kHz: - return "comfort noise (48 kHz)"; - default: - assert(false); - return "undefined"; - } -} - -// Registers all decoders in |neteq|. -void RegisterPayloadTypes(NetEq* neteq) { - assert(neteq); - int error; - error = neteq->RegisterPayloadType(webrtc::kDecoderPCMu, FLAGS_pcmu); - if (error) { - std::cerr << "Cannot register payload type " << FLAGS_pcmu << - " as " << CodecName(webrtc::kDecoderPCMu).c_str() << std::endl; - exit(1); - } - error = neteq->RegisterPayloadType(webrtc::kDecoderPCMa, FLAGS_pcma); - if (error) { - std::cerr << "Cannot register payload type " << FLAGS_pcma << - " as " << CodecName(webrtc::kDecoderPCMa).c_str() << std::endl; - exit(1); - } - error = neteq->RegisterPayloadType(webrtc::kDecoderILBC, FLAGS_ilbc); - if (error) { - std::cerr << "Cannot register payload type " << FLAGS_ilbc << - " as " << CodecName(webrtc::kDecoderILBC).c_str() << std::endl; - exit(1); - } - error = neteq->RegisterPayloadType(webrtc::kDecoderISAC, FLAGS_isac); - if (error) { - std::cerr << "Cannot register payload type " << FLAGS_isac << - " as " << CodecName(webrtc::kDecoderISAC).c_str() << std::endl; - exit(1); - } - error = neteq->RegisterPayloadType(webrtc::kDecoderISACswb, FLAGS_isac_swb); - if (error) { - std::cerr << "Cannot register payload type " << FLAGS_isac_swb << - " as " << CodecName(webrtc::kDecoderISACswb).c_str() << std::endl; - exit(1); - } - error = neteq->RegisterPayloadType(webrtc::kDecoderPCM16B, FLAGS_pcm16b); - if (error) { - std::cerr << "Cannot register payload type " << FLAGS_pcm16b << - " as " << CodecName(webrtc::kDecoderPCM16B).c_str() << std::endl; - exit(1); - } - error = neteq->RegisterPayloadType(webrtc::kDecoderPCM16Bwb, - FLAGS_pcm16b_wb); - if (error) { - std::cerr << "Cannot register payload type " << FLAGS_pcm16b_wb << - " as " << CodecName(webrtc::kDecoderPCM16Bwb).c_str() << std::endl; - exit(1); - } - error = neteq->RegisterPayloadType(webrtc::kDecoderPCM16Bswb32kHz, - FLAGS_pcm16b_swb32); - if (error) { - std::cerr << "Cannot register payload type " << FLAGS_pcm16b_swb32 << - " as " << CodecName(webrtc::kDecoderPCM16Bswb32kHz).c_str() << - std::endl; - exit(1); - } - error = neteq->RegisterPayloadType(webrtc::kDecoderPCM16Bswb48kHz, - FLAGS_pcm16b_swb48); - if (error) { - std::cerr << "Cannot register payload type " << FLAGS_pcm16b_swb48 << - " as " << CodecName(webrtc::kDecoderPCM16Bswb48kHz).c_str() << - std::endl; - exit(1); - } - error = neteq->RegisterPayloadType(webrtc::kDecoderG722, FLAGS_g722); - if (error) { - std::cerr << "Cannot register payload type " << FLAGS_g722 << - " as " << CodecName(webrtc::kDecoderG722).c_str() << std::endl; - exit(1); - } - error = neteq->RegisterPayloadType(webrtc::kDecoderAVT, FLAGS_avt); - if (error) { - std::cerr << "Cannot register payload type " << FLAGS_avt << - " as " << CodecName(webrtc::kDecoderAVT).c_str() << std::endl; - exit(1); - } - error = neteq->RegisterPayloadType(webrtc::kDecoderRED, FLAGS_red); - if (error) { - std::cerr << "Cannot register payload type " << FLAGS_red << - " as " << CodecName(webrtc::kDecoderRED).c_str() << std::endl; - exit(1); - } - error = neteq->RegisterPayloadType(webrtc::kDecoderCNGnb, FLAGS_cn_nb); - if (error) { - std::cerr << "Cannot register payload type " << FLAGS_cn_nb << - " as " << CodecName(webrtc::kDecoderCNGnb).c_str() << std::endl; - exit(1); - } - error = neteq->RegisterPayloadType(webrtc::kDecoderCNGwb, FLAGS_cn_wb); - if (error) { - std::cerr << "Cannot register payload type " << FLAGS_cn_wb << - " as " << CodecName(webrtc::kDecoderCNGwb).c_str() << std::endl; - exit(1); - } - error = neteq->RegisterPayloadType(webrtc::kDecoderCNGswb32kHz, - FLAGS_cn_swb32); - if (error) { - std::cerr << "Cannot register payload type " << FLAGS_cn_swb32 << - " as " << CodecName(webrtc::kDecoderCNGswb32kHz).c_str() << std::endl; - exit(1); - } - error = neteq->RegisterPayloadType(webrtc::kDecoderCNGswb48kHz, - FLAGS_cn_swb48); - if (error) { - std::cerr << "Cannot register payload type " << FLAGS_cn_swb48 << - " as " << CodecName(webrtc::kDecoderCNGswb48kHz).c_str() << std::endl; - exit(1); - } -} - -void PrintCodecMapping() { - std::cout << CodecName(webrtc::kDecoderPCMu).c_str() << ": " << FLAGS_pcmu << - std::endl; - std::cout << CodecName(webrtc::kDecoderPCMa).c_str() << ": " << FLAGS_pcma << - std::endl; - std::cout << CodecName(webrtc::kDecoderILBC).c_str() << ": " << FLAGS_ilbc << - std::endl; - std::cout << CodecName(webrtc::kDecoderISAC).c_str() << ": " << FLAGS_isac << - std::endl; - std::cout << CodecName(webrtc::kDecoderISACswb).c_str() << ": " << - FLAGS_isac_swb << std::endl; - std::cout << CodecName(webrtc::kDecoderPCM16B).c_str() << ": " << - FLAGS_pcm16b << std::endl; - std::cout << CodecName(webrtc::kDecoderPCM16Bwb).c_str() << ": " << - FLAGS_pcm16b_wb << std::endl; - std::cout << CodecName(webrtc::kDecoderPCM16Bswb32kHz).c_str() << ": " << - FLAGS_pcm16b_swb32 << std::endl; - std::cout << CodecName(webrtc::kDecoderPCM16Bswb48kHz).c_str() << ": " << - FLAGS_pcm16b_swb48 << std::endl; - std::cout << CodecName(webrtc::kDecoderG722).c_str() << ": " << FLAGS_g722 << - std::endl; - std::cout << CodecName(webrtc::kDecoderAVT).c_str() << ": " << FLAGS_avt << - std::endl; - std::cout << CodecName(webrtc::kDecoderRED).c_str() << ": " << FLAGS_red << - std::endl; - std::cout << CodecName(webrtc::kDecoderCNGnb).c_str() << ": " << - FLAGS_cn_nb << std::endl; - std::cout << CodecName(webrtc::kDecoderCNGwb).c_str() << ": " << - FLAGS_cn_wb << std::endl; - std::cout << CodecName(webrtc::kDecoderCNGswb32kHz).c_str() << ": " << - FLAGS_cn_swb32 << std::endl; - std::cout << CodecName(webrtc::kDecoderCNGswb48kHz).c_str() << ": " << - FLAGS_cn_swb48 << std::endl; -} - -size_t ReplacePayload(webrtc::test::InputAudioFile* replacement_audio_file, - webrtc::scoped_ptr* replacement_audio, - webrtc::scoped_ptr* payload, - size_t* payload_mem_size_bytes, - size_t* frame_size_samples, - WebRtcRTPHeader* rtp_header, - NETEQTEST_RTPpacket* next_rtp) { - size_t payload_len = 0; - // Check for CNG. - if (IsComfortNosie(rtp_header->header.payloadType)) { - // If CNG, simply insert a zero-energy one-byte payload. - if (*payload_mem_size_bytes < 1) { - (*payload).reset(new uint8_t[1]); - *payload_mem_size_bytes = 1; - } - (*payload)[0] = 127; // Max attenuation of CNG. - payload_len = 1; - } else { - if (next_rtp->payloadLen() > 0) { - // Check if payload length has changed. - if (next_rtp->sequenceNumber() == rtp_header->header.sequenceNumber + 1) { - if (*frame_size_samples != - next_rtp->timeStamp() - rtp_header->header.timestamp) { - *frame_size_samples = - next_rtp->timeStamp() - rtp_header->header.timestamp; - (*replacement_audio).reset( - new int16_t[*frame_size_samples]); - *payload_mem_size_bytes = 2 * *frame_size_samples; - (*payload).reset(new uint8_t[*payload_mem_size_bytes]); - } - } - } - // Get new speech. - assert((*replacement_audio).get()); - if (CodecTimestampRate(rtp_header->header.payloadType) != - CodecSampleRate(rtp_header->header.payloadType) || - rtp_header->header.payloadType == FLAGS_red || - rtp_header->header.payloadType == FLAGS_avt) { - // Some codecs have different sample and timestamp rates. And neither - // RED nor DTMF is supported for replacement. - std::cerr << "Codec not supported for audio replacement." << - std::endl; - webrtc::Trace::ReturnTrace(); - exit(1); - } - assert(*frame_size_samples > 0); - if (!replacement_audio_file->Read(*frame_size_samples, - (*replacement_audio).get())) { - std::cerr << "Could no read replacement audio file." << std::endl; - webrtc::Trace::ReturnTrace(); - exit(1); - } - // Encode it as PCM16. - assert((*payload).get()); - payload_len = WebRtcPcm16b_Encode((*replacement_audio).get(), - static_cast(*frame_size_samples), - (*payload).get()); - assert(payload_len == 2 * *frame_size_samples); - // Change payload type to PCM16. - switch (CodecSampleRate(rtp_header->header.payloadType)) { - case 8000: - rtp_header->header.payloadType = FLAGS_pcm16b; - break; - case 16000: - rtp_header->header.payloadType = FLAGS_pcm16b_wb; - break; - case 32000: - rtp_header->header.payloadType = FLAGS_pcm16b_swb32; - break; - case 48000: - rtp_header->header.payloadType = FLAGS_pcm16b_swb48; - break; - default: - std::cerr << "Payload type " << - static_cast(rtp_header->header.payloadType) << - " not supported or unknown." << std::endl; - webrtc::Trace::ReturnTrace(); - exit(1); - assert(false); - } - } - return payload_len; -} - -int CodecSampleRate(uint8_t payload_type) { - if (payload_type == FLAGS_pcmu || - payload_type == FLAGS_pcma || - payload_type == FLAGS_ilbc || - payload_type == FLAGS_pcm16b || - payload_type == FLAGS_cn_nb) { - return 8000; - } else if (payload_type == FLAGS_isac || - payload_type == FLAGS_pcm16b_wb || - payload_type == FLAGS_g722 || - payload_type == FLAGS_cn_wb) { - return 16000; - } else if (payload_type == FLAGS_isac_swb || - payload_type == FLAGS_pcm16b_swb32 || - payload_type == FLAGS_cn_swb32) { - return 32000; - } else if (payload_type == FLAGS_pcm16b_swb48 || - payload_type == FLAGS_cn_swb48) { - return 48000; - } else if (payload_type == FLAGS_avt || - payload_type == FLAGS_red) { - return 0; - } else { - return -1; - } -} - -int CodecTimestampRate(uint8_t payload_type) { - if (payload_type == FLAGS_g722) { - return 8000; - } else { - return CodecSampleRate(payload_type); - } -} - -bool IsComfortNosie(uint8_t payload_type) { - if (payload_type == FLAGS_cn_nb || - payload_type == FLAGS_cn_wb || - payload_type == FLAGS_cn_swb32 || - payload_type == FLAGS_cn_swb48) { - return true; - } else { - return false; - } -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/rtp_generator.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/rtp_generator.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/rtp_generator.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/rtp_generator.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "webrtc/modules/audio_coding/neteq4/tools/rtp_generator.h" - -namespace webrtc { -namespace test { - -uint32_t RtpGenerator::GetRtpHeader(uint8_t payload_type, - size_t payload_length_samples, - WebRtcRTPHeader* rtp_header) { - assert(rtp_header); - if (!rtp_header) { - return 0; - } - rtp_header->header.sequenceNumber = seq_number_++; - rtp_header->header.timestamp = timestamp_; - timestamp_ += static_cast(payload_length_samples); - rtp_header->header.payloadType = payload_type; - rtp_header->header.markerBit = false; - rtp_header->header.ssrc = ssrc_; - rtp_header->header.numCSRCs = 0; - rtp_header->frameType = kAudioFrameSpeech; - - uint32_t this_send_time = next_send_time_ms_; - assert(samples_per_ms_ > 0); - next_send_time_ms_ += ((1.0 + drift_factor_) * payload_length_samples) / - samples_per_ms_; - return this_send_time; -} - -void RtpGenerator::set_drift_factor(double factor) { - if (factor > -1.0) { - drift_factor_ = factor; - } -} - -} // namespace test -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/rtp_generator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/rtp_generator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/rtp_generator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/neteq4/tools/rtp_generator.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_RTP_GENERATOR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_RTP_GENERATOR_H_ - -#include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" - -namespace webrtc { -namespace test { - -// Class for generating RTP headers. -class RtpGenerator { - public: - RtpGenerator(int samples_per_ms, - uint16_t start_seq_number = 0, - uint32_t start_timestamp = 0, - uint32_t start_send_time_ms = 0, - uint32_t ssrc = 0x12345678) - : seq_number_(start_seq_number), - timestamp_(start_timestamp), - next_send_time_ms_(start_send_time_ms), - ssrc_(ssrc), - samples_per_ms_(samples_per_ms), - drift_factor_(0.0) { - } - - // Writes the next RTP header to |rtp_header|, which will be of type - // |payload_type|. Returns the send time for this packet (in ms). The value of - // |payload_length_samples| determines the send time for the next packet. - uint32_t GetRtpHeader(uint8_t payload_type, size_t payload_length_samples, - WebRtcRTPHeader* rtp_header); - - void set_drift_factor(double factor); - - private: - uint16_t seq_number_; - uint32_t timestamp_; - uint32_t next_send_time_ms_; - const uint32_t ssrc_; - const int samples_per_ms_; - double drift_factor_; - DISALLOW_COPY_AND_ASSIGN(RtpGenerator); -}; - -} // namespace test -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_RTP_GENERATOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_coding/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1 @@ +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/BUILD.gn 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,52 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +config("audio_conference_mixer_config") { + visibility = [ ":*" ] # Only targets in this file can depend on this. + include_dirs = [ + "interface", + "../interface", + ] +} + +source_set("audio_conference_mixer") { + sources = [ + "interface/audio_conference_mixer.h", + "interface/audio_conference_mixer_defines.h", + "source/audio_conference_mixer_impl.cc", + "source/audio_conference_mixer_impl.h", + "source/audio_frame_manipulator.cc", + "source/audio_frame_manipulator.h", + "source/level_indicator.cc", + "source/level_indicator.h", + "source/memory_pool.h", + "source/memory_pool_posix.h", + "source/memory_pool_win.h", + "source/time_scheduler.cc", + "source/time_scheduler.h", + ] + + configs += [ "../..:common_config" ] + + public_configs = [ + "../..:common_inherited_config", + ":audio_conference_mixer_config", + ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ + "../../system_wrappers", + "../audio_processing", + "../utility", + ] +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/interface/audio_conference_mixer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/interface/audio_conference_mixer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/interface/audio_conference_mixer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/interface/audio_conference_mixer.h 2015-02-03 14:33:34.000000000 +0000 @@ -40,9 +40,9 @@ virtual ~AudioConferenceMixer() {} // Module functions - virtual int32_t ChangeUniqueId(const int32_t id) = 0; - virtual int32_t TimeUntilNextProcess() = 0 ; - virtual int32_t Process() = 0; + virtual int32_t ChangeUniqueId(const int32_t id) OVERRIDE = 0; + virtual int32_t TimeUntilNextProcess() OVERRIDE = 0; + virtual int32_t Process() OVERRIDE = 0; // Register/unregister a callback class for receiving the mixed audio. virtual int32_t RegisterMixedStreamCallback( diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -1,3 +1,4 @@ henrike@webrtc.org -pwestin@webrtc.org -andrew@webrtc.org \ No newline at end of file +andrew@webrtc.org + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../android-webrtc.mk - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_audio_conference_mixer -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := \ - audio_frame_manipulator.cc \ - level_indicator.cc \ - audio_conference_mixer_impl.cc \ - time_scheduler.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../interface \ - $(LOCAL_PATH)/../../audio_processing/include \ - $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../../../system_wrappers/interface - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/audio_conference_mixer_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/audio_conference_mixer_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/audio_conference_mixer_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/audio_conference_mixer_impl.cc 2015-02-03 14:33:34.000000000 +0000 @@ -32,10 +32,13 @@ // stereo at most. // // TODO(andrew): consider not modifying |frame| here. -void MixFrames(AudioFrame* mixed_frame, AudioFrame* frame) { +void MixFrames(AudioFrame* mixed_frame, AudioFrame* frame, bool use_limiter) { assert(mixed_frame->num_channels_ >= frame->num_channels_); - // Divide by two to avoid saturation in the mixing. - *frame >>= 1; + if (use_limiter) { + // Divide by two to avoid saturation in the mixing. + // This is only meaningful if the limiter will be used. + *frame >>= 1; + } if (mixed_frame->num_channels_ > frame->num_channels_) { // We only support mono-to-stereo. assert(mixed_frame->num_channels_ == 2 && @@ -131,6 +134,7 @@ _participantList(), _additionalParticipantList(), _numMixedParticipants(0), + use_limiter_(true), _timeStamp(0), _timeScheduler(kProcessPeriodicityInMs), _mixedAudioLevel(), @@ -308,6 +312,11 @@ _timeStamp += _sampleSize; + // We only use the limiter if it supports the output sample rate and + // we're actually mixing multiple streams. + use_limiter_ = _numMixedParticipants > 1 && + _outputFrequency <= kAudioProcMaxNativeSampleRateHz; + MixFromList(*mixedAudio, &mixList); MixAnonomouslyFromList(*mixedAudio, &additionalFramesList); MixAnonomouslyFromList(*mixedAudio, &rampOutList); @@ -651,6 +660,11 @@ _audioFramePool->PushMemory(audioFrame); continue; } + if (_participantList.size() != 1) { + // TODO(wu): Issue 3390, add support for multiple participants case. + audioFrame->ntp_time_ms_ = -1; + } + // TODO(henrike): this assert triggers in some test cases where SRTP is // used which prevents NetEQ from making a VAD. Temporarily disable this // assert until the problem is fixed on a higher level. @@ -941,13 +955,15 @@ if(audioFrameList->empty()) return 0; uint32_t position = 0; - if(_numMixedParticipants == 1) { - // No mixing required here; skip the saturation protection. - AudioFrame* audioFrame = audioFrameList->front(); - mixedAudio.CopyFrom(*audioFrame); - SetParticipantStatistics(&_scratchMixedParticipants[position], - *audioFrame); - return 0; + + if (_numMixedParticipants == 1) { + mixedAudio.timestamp_ = audioFrameList->front()->timestamp_; + mixedAudio.elapsed_time_ms_ = audioFrameList->front()->elapsed_time_ms_; + } else { + // TODO(wu): Issue 3390. + // Audio frame timestamp is only supported in one channel case. + mixedAudio.timestamp_ = 0; + mixedAudio.elapsed_time_ms_ = -1; } for (AudioFrameList::const_iterator iter = audioFrameList->begin(); @@ -964,7 +980,7 @@ assert(false); position = 0; } - MixFrames(&mixedAudio, (*iter)); + MixFrames(&mixedAudio, (*iter), use_limiter_); SetParticipantStatistics(&_scratchMixedParticipants[position], **iter); @@ -984,24 +1000,17 @@ if(audioFrameList->empty()) return 0; - if(_numMixedParticipants == 1) { - // No mixing required here; skip the saturation protection. - AudioFrame* audioFrame = audioFrameList->front(); - mixedAudio.CopyFrom(*audioFrame); - return 0; - } - for (AudioFrameList::const_iterator iter = audioFrameList->begin(); iter != audioFrameList->end(); ++iter) { - MixFrames(&mixedAudio, *iter); + MixFrames(&mixedAudio, *iter, use_limiter_); } return 0; } bool AudioConferenceMixerImpl::LimitMixedAudio(AudioFrame& mixedAudio) { - if(_numMixedParticipants == 1) { - return true; + if (!use_limiter_) { + return true; } // Smoothly limit the mixed frame. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/audio_conference_mixer_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/audio_conference_mixer_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/audio_conference_mixer_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/audio_conference_mixer_impl.h 2015-02-03 14:33:34.000000000 +0000 @@ -64,27 +64,27 @@ bool Init(); // Module functions - virtual int32_t ChangeUniqueId(const int32_t id); - virtual int32_t TimeUntilNextProcess(); - virtual int32_t Process(); + virtual int32_t ChangeUniqueId(const int32_t id) OVERRIDE; + virtual int32_t TimeUntilNextProcess() OVERRIDE; + virtual int32_t Process() OVERRIDE; // AudioConferenceMixer functions virtual int32_t RegisterMixedStreamCallback( - AudioMixerOutputReceiver& mixReceiver); - virtual int32_t UnRegisterMixedStreamCallback(); + AudioMixerOutputReceiver& mixReceiver) OVERRIDE; + virtual int32_t UnRegisterMixedStreamCallback() OVERRIDE; virtual int32_t RegisterMixerStatusCallback( AudioMixerStatusReceiver& mixerStatusCallback, - const uint32_t amountOf10MsBetweenCallbacks); - virtual int32_t UnRegisterMixerStatusCallback(); + const uint32_t amountOf10MsBetweenCallbacks) OVERRIDE; + virtual int32_t UnRegisterMixerStatusCallback() OVERRIDE; virtual int32_t SetMixabilityStatus(MixerParticipant& participant, - bool mixable); + bool mixable) OVERRIDE; virtual int32_t MixabilityStatus(MixerParticipant& participant, - bool& mixable); - virtual int32_t SetMinimumMixingFrequency(Frequency freq); + bool& mixable) OVERRIDE; + virtual int32_t SetMinimumMixingFrequency(Frequency freq) OVERRIDE; virtual int32_t SetAnonymousMixabilityStatus( - MixerParticipant& participant, const bool mixable); + MixerParticipant& participant, const bool mixable) OVERRIDE; virtual int32_t AnonymousMixabilityStatus( - MixerParticipant& participant, bool& mixable); + MixerParticipant& participant, bool& mixable) OVERRIDE; private: enum{DEFAULT_AUDIO_FRAME_POOLSIZE = 50}; @@ -192,6 +192,9 @@ MixerParticipantList _additionalParticipantList; size_t _numMixedParticipants; + // Determines if we will use a limiter for clipping protection during + // mixing. + bool use_limiter_; uint32_t _timeStamp; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/audio_frame_manipulator.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/audio_frame_manipulator.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/audio_frame_manipulator.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/audio_frame_manipulator.cc 2015-02-03 14:33:34.000000000 +0000 @@ -41,10 +41,6 @@ namespace webrtc { void CalculateEnergy(AudioFrame& audioFrame) { - if(audioFrame.energy_ != 0xffffffff) - { - return; - } audioFrame.energy_ = 0; for(int position = 0; position < audioFrame.samples_per_channel_; position++) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_conference_mixer/source/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_device_template.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_device_template.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_device_template.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_device_template.h 2015-02-03 14:33:34.000000000 +0000 @@ -175,11 +175,6 @@ return -1; } - int32_t SpeakerIsAvailable( - bool& available) { // NOLINT - return output_.SpeakerIsAvailable(available); - } - int32_t InitSpeaker() { return output_.InitSpeaker(); } @@ -188,11 +183,6 @@ return output_.SpeakerIsInitialized(); } - int32_t MicrophoneIsAvailable( - bool& available) { // NOLINT - return input_.MicrophoneIsAvailable(available); - } - int32_t InitMicrophone() { return input_.InitMicrophone(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_record_jni.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_record_jni.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_record_jni.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_record_jni.cc 2015-02-03 14:33:34.000000000 +0000 @@ -592,12 +592,6 @@ return 0; } -int32_t AudioRecordJni::MicrophoneIsAvailable(bool& available) { // NOLINT - // We always assume it's available - available = true; - return 0; -} - int32_t AudioRecordJni::InitMicrophone() { CriticalSectionScoped lock(&_critSect); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_record_jni.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_record_jni.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_record_jni.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_record_jni.h 2015-02-03 14:33:34.000000000 +0000 @@ -67,7 +67,6 @@ bool AGC() const { return _AGC; } // Audio mixer initialization - int32_t MicrophoneIsAvailable(bool& available); // NOLINT int32_t InitMicrophone(); bool MicrophoneIsInitialized() const { return _micIsInitialized; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_track_jni.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_track_jni.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_track_jni.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_track_jni.cc 2015-02-03 14:33:34.000000000 +0000 @@ -585,12 +585,6 @@ } -int32_t AudioTrackJni::SpeakerIsAvailable(bool& available) { // NOLINT - // We always assume it's available - available = true; - return 0; -} - int32_t AudioTrackJni::InitSpeaker() { CriticalSectionScoped lock(&_critSect); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_track_jni.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_track_jni.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_track_jni.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_track_jni.h 2015-02-03 14:33:34.000000000 +0000 @@ -63,7 +63,6 @@ bool Playing() const { return _playing; } // Audio mixer initialization - int32_t SpeakerIsAvailable(bool& available); // NOLINT int32_t InitSpeaker(); bool SpeakerIsInitialized() const { return _speakerIsInitialized; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/fine_audio_buffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/fine_audio_buffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/fine_audio_buffer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/fine_audio_buffer.h 2015-02-03 14:33:34.000000000 +0000 @@ -56,7 +56,7 @@ int bytes_per_10_ms_; // Storage for samples that are not yet asked for. - scoped_array cache_buffer_; + scoped_ptr cache_buffer_; int cached_buffer_start_; // Location of first unread sample. int cached_bytes_; // Number of bytes stored in cache. }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/fine_audio_buffer_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/fine_audio_buffer_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/fine_audio_buffer_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/fine_audio_buffer_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -80,7 +80,7 @@ FineAudioBuffer fine_buffer(&audio_device_buffer, kFrameSizeBytes, sample_rate); - scoped_array out_buffer; + scoped_ptr out_buffer; out_buffer.reset( new int8_t[fine_buffer.RequiredBufferSizeBytes()]); for (int i = 0; i < kNumberOfFrames; ++i) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/opensles_input.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/opensles_input.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/opensles_input.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/opensles_input.cc 2015-02-03 14:33:34.000000000 +0000 @@ -239,11 +239,6 @@ return 0; } -int32_t OpenSlesInput::MicrophoneIsAvailable(bool& available) { // NOLINT - available = true; - return 0; -} - int32_t OpenSlesInput::InitMicrophone() { assert(initialized_); assert(!recording_); @@ -365,7 +360,7 @@ fifo_.reset(new SingleRwFifo(num_fifo_buffers_needed_)); // Allocate the memory area to be used. - rec_buf_.reset(new scoped_array[TotalBuffersUsed()]); + rec_buf_.reset(new scoped_ptr[TotalBuffersUsed()]); for (int i = 0; i < TotalBuffersUsed(); ++i) { rec_buf_[i].reset(new int8_t[buffer_size_bytes()]); } @@ -424,23 +419,6 @@ return false; } -void OpenSlesInput::SetupVoiceMode() { - SLAndroidConfigurationItf configItf; - SLresult res = (*sles_recorder_)->GetInterface(sles_recorder_, SL_IID_ANDROIDCONFIGURATION_, - (void*)&configItf); - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, id_, "OpenSL GetInterface: %d", res); - - if (res == SL_RESULT_SUCCESS) { - SLuint32 voiceMode = SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; - SLuint32 voiceSize = sizeof(voiceMode); - - res = (*configItf)->SetConfiguration(configItf, - SL_ANDROID_KEY_RECORDING_PRESET, - &voiceMode, voiceSize); - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, id_, "OpenSL Set Voice mode res: %d", res); - } -} - void OpenSlesInput::SetupAECAndNS() { bool hasAec = CheckPlatformAEC(); WEBRTC_TRACE(kTraceError, kTraceAudioDevice, id_, "Platform has AEC: %d", hasAec); @@ -534,9 +512,23 @@ req), false); -#if defined(WEBRTC_GONK) && defined(WEBRTC_HARDWARE_AEC_NS) - SetupVoiceMode(); -#endif + SLAndroidConfigurationItf recorder_config; + OPENSL_RETURN_ON_FAILURE( + (*sles_recorder_)->GetInterface(sles_recorder_, + SL_IID_ANDROIDCONFIGURATION_, + &recorder_config), + false); + + // Set audio recorder configuration to + // SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION which ensures that we + // use the main microphone tuned for audio communications. + SLuint32 stream_type = SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; + OPENSL_RETURN_ON_FAILURE( + (*recorder_config)->SetConfiguration(recorder_config, + SL_ANDROID_KEY_RECORDING_PRESET, + &stream_type, + sizeof(SLint32)), + false); // Realize the recorder in synchronous mode. OPENSL_RETURN_ON_FAILURE((*sles_recorder_)->Realize(sles_recorder_, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/opensles_input.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/opensles_input.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/opensles_input.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/opensles_input.h 2015-02-03 14:33:34.000000000 +0000 @@ -15,6 +15,9 @@ #include #include +// Not defined in the android version we use to build with +#define SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION ((SLuint32) 0x00000004) + #if !defined(WEBRTC_GONK) #include "webrtc/modules/audio_device/android/audio_manager_jni.h" #else @@ -81,7 +84,6 @@ bool AGC() const { return agc_enabled_; } // Audio mixer initialization - int32_t MicrophoneIsAvailable(bool& available); // NOLINT int32_t InitMicrophone(); bool MicrophoneIsInitialized() const { return mic_initialized_; } @@ -217,7 +219,7 @@ // Audio buffers AudioDeviceBuffer* audio_buffer_; // Holds all allocated memory such that it is deallocated properly. - scoped_array > rec_buf_; + scoped_ptr[]> rec_buf_; // Index in |rec_buf_| pointing to the audio buffer that will be ready the // next time RecorderSimpleBufferQueueCallbackHandler is invoked. // Ready means buffer contains audio data from the device. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/opensles_output.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/opensles_output.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/opensles_output.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/opensles_output.cc 2015-02-03 14:33:34.000000000 +0000 @@ -234,11 +234,6 @@ return 0; } -int32_t OpenSlesOutput::SpeakerIsAvailable(bool& available) { // NOLINT - available = true; - return 0; -} - int32_t OpenSlesOutput::InitSpeaker() { assert(!playing_); speaker_initialized_ = true; @@ -395,7 +390,7 @@ fifo_.reset(new SingleRwFifo(num_fifo_buffers_needed_)); // Allocate the memory area to be used. - play_buf_.reset(new scoped_array[TotalBuffersUsed()]); + play_buf_.reset(new scoped_ptr[TotalBuffersUsed()]); int required_buffer_size = fine_buffer_->RequiredBufferSizeBytes(); for (int i = 0; i < TotalBuffersUsed(); ++i) { play_buf_[i].reset(new int8_t[required_buffer_size]); @@ -462,6 +457,24 @@ &audio_source, &audio_sink, kNumInterfaces, ids, req), false); + + SLAndroidConfigurationItf player_config; + OPENSL_RETURN_ON_FAILURE( + (*sles_player_)->GetInterface(sles_player_, + SL_IID_ANDROIDCONFIGURATION_, + &player_config), + false); + + // Set audio player configuration to SL_ANDROID_STREAM_VOICE which corresponds + // to android.media.AudioManager.STREAM_VOICE_CALL. + SLint32 stream_type = SL_ANDROID_STREAM_VOICE; + OPENSL_RETURN_ON_FAILURE( + (*player_config)->SetConfiguration(player_config, + SL_ANDROID_KEY_STREAM_TYPE, + &stream_type, + sizeof(SLint32)), + false); + // Realize the player in synchronous mode. OPENSL_RETURN_ON_FAILURE((*sles_player_)->Realize(sles_player_, SL_BOOLEAN_FALSE), diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/opensles_output.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/opensles_output.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/opensles_output.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/opensles_output.h 2015-02-03 14:33:34.000000000 +0000 @@ -79,7 +79,6 @@ bool Playing() const { return playing_; } // Audio mixer initialization - int32_t SpeakerIsAvailable(bool& available); // NOLINT int32_t InitSpeaker(); bool SpeakerIsInitialized() const { return speaker_initialized_; } @@ -231,7 +230,7 @@ // Audio buffers AudioDeviceBuffer* audio_buffer_; scoped_ptr fine_buffer_; - scoped_array > play_buf_; + scoped_ptr[]> play_buf_; // Index in |rec_buf_| pointing to the audio buffer that will be ready the // next time PlayerSimpleBufferQueueCallbackHandler is invoked. // Ready means buffer is ready to be played out to device. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -1,2 +1 @@ henrike@webrtc.org -fischman@webrtc.org \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/single_rw_fifo.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/single_rw_fifo.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/single_rw_fifo.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/single_rw_fifo.cc 2015-02-03 14:33:34.000000000 +0000 @@ -14,6 +14,8 @@ #include "webrtc/modules/audio_device/android/single_rw_fifo.h" +#include + static int UpdatePos(int pos, int capacity) { return (pos + 1) % capacity; } @@ -34,14 +36,16 @@ ::MemoryBarrier(); } +#elif defined(__aarch64__) +// From http://http://src.chromium.org/viewvc/chrome/trunk/src/base/atomicops_internals_arm64_gcc.h +inline void MemoryBarrier() { + __asm__ __volatile__ ("dmb ish" ::: "memory"); +} + #elif defined(__ARMEL__) // From http://src.chromium.org/viewvc/chrome/trunk/src/base/atomicops_internals_arm_gcc.h -// Note that it is only the MemoryBarrier function that makes this class arm -// specific. Borrowing other MemoryBarrier implementations, this class could -// be extended to more platforms. inline void MemoryBarrier() { - // Note: This is a function call, which is also an implicit compiler - // barrier. + // Note: This is a function call, which is also an implicit compiler barrier. typedef void (*KernelMemoryBarrierFunc)(); ((KernelMemoryBarrierFunc)0xffff0fa0)(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/single_rw_fifo.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/single_rw_fifo.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/single_rw_fifo.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/single_rw_fifo.h 2015-02-03 14:33:34.000000000 +0000 @@ -35,7 +35,7 @@ int capacity() const { return capacity_; } private: - scoped_array queue_; + scoped_ptr queue_; int capacity_; Atomic32 size_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/single_rw_fifo_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/single_rw_fifo_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/single_rw_fifo_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/android/single_rw_fifo_unittest.cc 2015-02-03 14:33:34.000000000 +0000 @@ -90,7 +90,7 @@ protected: SingleRwFifo fifo_; // Memory area for proper de-allocation. - scoped_array buffer_[kCapacity]; + scoped_ptr buffer_[kCapacity]; std::list memory_queue_; int pushed_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_audio_device -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := \ - audio_device_buffer.cc \ - audio_device_generic.cc \ - audio_device_utility.cc \ - audio_device_impl.cc \ - android/audio_device_android_opensles.cc \ - android/audio_device_utility_android.cc \ - dummy/audio_device_utility_dummy.cc \ - dummy/audio_device_dummy.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) \ - '-DWEBRTC_ANDROID_OPENSLES' - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH) \ - $(LOCAL_PATH)/android \ - $(LOCAL_PATH)/dummy \ - $(LOCAL_PATH)/linux \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../../../../common_audio/resampler/include \ - $(LOCAL_PATH)/../../../../common_audio/signal_processing/include \ - $(LOCAL_PATH)/../../../../system_wrappers/interface \ - system/media/wilhelm/include - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport \ - libOpenSLES - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_buffer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_buffer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_buffer.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_buffer.cc 2015-02-03 14:33:34.000000000 +0000 @@ -548,13 +548,16 @@ if (_ptrCbAudioTransport) { uint32_t res(0); - + int64_t elapsed_time_ms = -1; + int64_t ntp_time_ms = -1; res = _ptrCbAudioTransport->NeedMorePlayData(_playSamples, playBytesPerSample, playChannels, playSampleRate, &_playBuffer[0], - nSamplesOut); + nSamplesOut, + &elapsed_time_ms, + &ntp_time_ms); if (res != 0) { WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "NeedMorePlayData() failed"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_generic.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_generic.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_generic.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_generic.h 2015-02-03 14:33:34.000000000 +0000 @@ -76,10 +76,8 @@ uint16_t& volumeRight) const = 0; // Audio mixer initialization - virtual int32_t SpeakerIsAvailable(bool& available) = 0; virtual int32_t InitSpeaker() = 0; virtual bool SpeakerIsInitialized() const = 0; - virtual int32_t MicrophoneIsAvailable(bool& available) = 0; virtual int32_t InitMicrophone() = 0; virtual bool MicrophoneIsInitialized() const = 0; @@ -137,7 +135,7 @@ // CPU load virtual int32_t CPULoad(uint16_t& load) const = 0; - + // Native sample rate controls (samples/sec) virtual int32_t SetRecordingSampleRate( const uint32_t samplesPerSec); @@ -147,7 +145,7 @@ // Speaker audio routing (for mobile devices) virtual int32_t SetLoudspeakerStatus(bool enable); virtual int32_t GetLoudspeakerStatus(bool& enable) const; - + // Reset Audio Device (for mobile devices) virtual int32_t ResetAudioDevice(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device.gypi 2015-02-03 14:33:34.000000000 +0000 @@ -20,7 +20,7 @@ '.', '../interface', 'include', - 'dummy', # dummy audio device + 'dummy', # Contains dummy audio device implementations. ], 'direct_dependent_settings': { 'include_dirs': [ @@ -45,6 +45,8 @@ 'dummy/audio_device_dummy.h', 'dummy/audio_device_utility_dummy.cc', 'dummy/audio_device_utility_dummy.h', + 'dummy/file_audio_device.cc', + 'dummy/file_audio_device.h', ], 'conditions': [ ['build_with_mozilla==1', { @@ -105,13 +107,20 @@ 'WEBRTC_DUMMY_AUDIO_BUILD', ], }], + ['build_with_chromium==0', { + 'sources': [ + # Don't link these into Chrome since they contain static data. + 'dummy/file_audio_device_factory.cc', + 'dummy/file_audio_device_factory.h', + ], + }], ['include_internal_audio_device==1', { 'sources': [ 'linux/audio_device_utility_linux.cc', 'linux/audio_device_utility_linux.h', 'linux/latebindingsymboltable_linux.cc', 'linux/latebindingsymboltable_linux.h', - 'ios/audio_device_ios.cc', + 'ios/audio_device_ios.mm', 'ios/audio_device_ios.h', 'ios/audio_device_utility_ios.cc', 'ios/audio_device_utility_ios.h', @@ -221,7 +230,7 @@ 'linux/pulseaudiosymboltable_linux.h', ], }], - ['OS=="mac" or OS=="ios"', { + ['OS=="mac"', { 'link_settings': { 'libraries': [ '$(SDKROOT)/System/Library/Frameworks/AudioToolbox.framework', @@ -229,6 +238,19 @@ ], }, }], + ['OS=="ios"', { + 'xcode_settings': { + 'CLANG_ENABLE_OBJC_ARC': 'YES', + }, + 'link_settings': { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-framework AudioToolbox', + '-framework AVFoundation', + ], + }, + }, + }], ['OS=="win"', { 'link_settings': { 'libraries': [ @@ -293,7 +315,6 @@ ], 'includes': [ '../../build/isolate.gypi', - 'audio_device_tests.isolate', ], 'sources': [ 'audio_device_tests.isolate', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_impl.cc 2015-02-03 14:33:34.000000000 +0000 @@ -52,8 +52,14 @@ #include "audio_device_utility_mac.h" #include "audio_device_mac.h" #endif + +#if defined(WEBRTC_DUMMY_FILE_DEVICES) +#include "webrtc/modules/audio_device/dummy/file_audio_device_factory.h" +#endif + #include "webrtc/modules/audio_device/dummy/audio_device_dummy.h" #include "webrtc/modules/audio_device/dummy/audio_device_utility_dummy.h" +#include "webrtc/modules/audio_device/dummy/file_audio_device.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/trace.h" @@ -210,6 +216,14 @@ { ptrAudioDeviceUtility = new AudioDeviceUtilityDummy(Id()); } +#elif defined(WEBRTC_DUMMY_FILE_DEVICES) + ptrAudioDevice = FileAudioDeviceFactory::CreateFileAudioDevice(Id()); + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + "Will use file-playing dummy device."); + if (ptrAudioDevice != NULL) + { + ptrAudioDeviceUtility = new AudioDeviceUtilityDummy(Id()); + } #else const AudioLayer audioLayer(PlatformAudioLayer()); @@ -363,15 +377,15 @@ #if defined(WEBRTC_IOS) if (audioLayer == kPlatformDefaultAudio) { - // Create *iPhone Audio* implementation - ptrAudioDevice = new AudioDeviceIPhone(Id()); + // Create iOS Audio Device implementation. + ptrAudioDevice = new AudioDeviceIOS(Id()); WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "iPhone Audio APIs will be utilized"); } if (ptrAudioDevice != NULL) { - // Create the Mac implementation of the Device Utility. - ptrAudioDeviceUtility = new AudioDeviceUtilityIPhone(Id()); + // Create iOS Device Utility implementation. + ptrAudioDeviceUtility = new AudioDeviceUtilityIOS(Id()); } // END #if defined(WEBRTC_IOS) @@ -669,27 +683,6 @@ } // ---------------------------------------------------------------------------- -// SpeakerIsAvailable -// ---------------------------------------------------------------------------- - -int32_t AudioDeviceModuleImpl::SpeakerIsAvailable(bool* available) -{ - CHECK_INITIALIZED(); - - bool isAvailable(0); - - if (_ptrAudioDevice->SpeakerIsAvailable(isAvailable) == -1) - { - return -1; - } - - *available = isAvailable; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: available=%d", available); - return (0); -} - -// ---------------------------------------------------------------------------- // InitSpeaker // ---------------------------------------------------------------------------- @@ -700,27 +693,6 @@ } // ---------------------------------------------------------------------------- -// MicrophoneIsAvailable -// ---------------------------------------------------------------------------- - -int32_t AudioDeviceModuleImpl::MicrophoneIsAvailable(bool* available) -{ - CHECK_INITIALIZED(); - - bool isAvailable(0); - - if (_ptrAudioDevice->MicrophoneIsAvailable(isAvailable) == -1) - { - return -1; - } - - *available = isAvailable; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: available=%d", *available); - return (0); -} - -// ---------------------------------------------------------------------------- // InitMicrophone // ---------------------------------------------------------------------------- @@ -1778,8 +1750,6 @@ CHECK_INITIALIZED(); return (_audioDeviceBuffer.StopOutputFileRecording()); - - return 0; } // ---------------------------------------------------------------------------- diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_impl.h 2015-02-03 14:33:34.000000000 +0000 @@ -115,10 +115,8 @@ uint16_t* volumeRight) const OVERRIDE; // Audio mixer initialization - virtual int32_t SpeakerIsAvailable(bool* available) OVERRIDE; virtual int32_t InitSpeaker() OVERRIDE; virtual bool SpeakerIsInitialized() const OVERRIDE; - virtual int32_t MicrophoneIsAvailable(bool* available) OVERRIDE; virtual int32_t InitMicrophone() OVERRIDE; virtual bool MicrophoneIsInitialized() const OVERRIDE; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_tests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_tests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_tests.isolate 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_tests.isolate 2015-02-03 14:33:34.000000000 +0000 @@ -8,28 +8,23 @@ { 'conditions': [ ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. 'variables': { - 'isolate_dependency_untracked': [ - '../../../../data/', - '../../../../resources/', + 'files': [ + '<(DEPTH)/data/', + '<(DEPTH)/resources/', ], }, }], ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../../../testing/test_env.py', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/audio_device_tests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_tracked': [ - '../../../testing/test_env.py', + 'files': [ + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/audio_device_tests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '../../../tools/swarming_client/', - ], }, }], ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_utility.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_utility.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_utility.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_utility.cc 2015-02-03 14:33:34.000000000 +0000 @@ -82,9 +82,9 @@ // choose enter out of all available keys - if (getchar() == '\n') + if (getc(stdin) == '\n') { - getchar(); + getc(stdin); } tcsetattr( STDIN_FILENO, TCSANOW, &oldt ); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_utility.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_utility.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_utility.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_utility.h 2015-02-03 14:33:34.000000000 +0000 @@ -18,15 +18,15 @@ class AudioDeviceUtility { -public: - static uint32_t GetTimeInMS(); - static void WaitForKey(); - static bool StringCompare(const char* str1, - const char* str2, - const uint32_t length); - virtual int32_t Init() = 0; + public: + static uint32_t GetTimeInMS(); + static void WaitForKey(); + static bool StringCompare(const char* str1, + const char* str2, + const uint32_t length); + virtual int32_t Init() = 0; - virtual ~AudioDeviceUtility() {} + virtual ~AudioDeviceUtility() {} }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/BUILD.gn 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,186 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../../build/webrtc.gni") + +config("audio_device_config") { + include_dirs = [ + "../interface", + "include", + "dummy", # Contains dummy audio device implementations. + ] +} + +source_set("audio_device") { + sources = [ + "include/audio_device.h", + "include/audio_device_defines.h", + "audio_device_buffer.cc", + "audio_device_buffer.h", + "audio_device_generic.cc", + "audio_device_generic.h", + "audio_device_utility.cc", + "audio_device_utility.h", + "audio_device_impl.cc", + "audio_device_impl.h", + "audio_device_config.h", + "dummy/audio_device_dummy.cc", + "dummy/audio_device_dummy.h", + "dummy/audio_device_utility_dummy.cc", + "dummy/audio_device_utility_dummy.h", + "dummy/file_audio_device.cc", + "dummy/file_audio_device.h", + ] + + include_dirs = [] + if (is_linux) { + include_dirs += [ "linux" ] + } + if (is_ios) { + include_dirs += [ "ios" ] + } + if (is_mac) { + include_dirs += [ "mac" ] + } + if (is_win) { + include_dirs += [ "win" ] + } + if (is_android) { + include_dirs += [ "android" ] + } + if (rtc_include_internal_audio_device) { + sources += [ + "linux/alsasymboltable_linux.cc", + "linux/alsasymboltable_linux.h", + "linux/audio_device_alsa_linux.cc", + "linux/audio_device_alsa_linux.h", + "linux/audio_device_utility_linux.cc", + "linux/audio_device_utility_linux.h", + "linux/audio_mixer_manager_alsa_linux.cc", + "linux/audio_mixer_manager_alsa_linux.h", + "linux/latebindingsymboltable_linux.cc", + "linux/latebindingsymboltable_linux.h", + "ios/audio_device_ios.mm", + "ios/audio_device_ios.h", + "ios/audio_device_utility_ios.cc", + "ios/audio_device_utility_ios.h", + "mac/audio_device_mac.cc", + "mac/audio_device_mac.h", + "mac/audio_device_utility_mac.cc", + "mac/audio_device_utility_mac.h", + "mac/audio_mixer_manager_mac.cc", + "mac/audio_mixer_manager_mac.h", + "mac/portaudio/pa_memorybarrier.h", + "mac/portaudio/pa_ringbuffer.c", + "mac/portaudio/pa_ringbuffer.h", + "win/audio_device_core_win.cc", + "win/audio_device_core_win.h", + "win/audio_device_wave_win.cc", + "win/audio_device_wave_win.h", + "win/audio_device_utility_win.cc", + "win/audio_device_utility_win.h", + "win/audio_mixer_manager_win.cc", + "win/audio_mixer_manager_win.h", + "android/audio_device_template.h", + "android/audio_device_utility_android.cc", + "android/audio_device_utility_android.h", + "android/audio_manager_jni.cc", + "android/audio_manager_jni.h", + "android/audio_record_jni.cc", + "android/audio_record_jni.h", + "android/audio_track_jni.cc", + "android/audio_track_jni.h", + "android/fine_audio_buffer.cc", + "android/fine_audio_buffer.h", + "android/low_latency_event_posix.cc", + "android/low_latency_event.h", + "android/opensles_common.cc", + "android/opensles_common.h", + "android/opensles_input.cc", + "android/opensles_input.h", + "android/opensles_output.cc", + "android/opensles_output.h", + "android/single_rw_fifo.cc", + "android/single_rw_fifo.h", + ] + if (is_linux) { + defines += [ "LINUX_ALSA" ] + + libs = [ + "dl", + "X11", + ] + + if (rtc_include_pulse_audio) { + sources += [ + "linux/audio_device_pulse_linux.cc", + "linux/audio_device_pulse_linux.h", + "linux/audio_mixer_manager_pulse_linux.cc", + "linux/audio_mixer_manager_pulse_linux.h", + "linux/pulseaudiosymboltable_linux.cc", + "linux/pulseaudiosymboltable_linux.h", + ] + + defines += [ "LINUX_PULSE" ] + } + } + if (is_mac) { + libs = [ + "AudioToolbox.framework", + "CoreAudio.framework", + ] + } + if (is_ios) { + cflags += [ "-fobjc-arc" ] # CLANG_ENABLE_OBJC_ARC = YES. + + libs = [ + "AudioToolbox.framework", + "AVFoundation.framework", + ] + } + if (is_win) { + libs = [ + # Required for the built-in WASAPI AEC. + "dmoguids.lib", + "wmcodecdspuuid.lib", + "amstrmid.lib", + "msdmo.lib", + ] + } + } else { + defines = [ "WEBRTC_DUMMY_AUDIO_BUILD" ] + } + + if (!build_with_chromium) { + sources += [ + # Do not link these into Chrome since they contain static data. + "dummy/file_audio_device_factory.cc", + "dummy/file_audio_device_factory.h", + ] + } + + configs += [ "../..:common_config" ] + public_configs = [ + "../..:common_inherited_config", + ":audio_device_config", + ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ + "../../common_audio", + "../../system_wrappers", + "../utility", + ] +} + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/audio_device_dummy.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/audio_device_dummy.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/audio_device_dummy.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/audio_device_dummy.cc 2015-02-03 14:33:34.000000000 +0000 @@ -91,14 +91,10 @@ return -1; } -int32_t AudioDeviceDummy::SpeakerIsAvailable(bool& available) { return -1; } - int32_t AudioDeviceDummy::InitSpeaker() { return -1; } bool AudioDeviceDummy::SpeakerIsInitialized() const { return false; } -int32_t AudioDeviceDummy::MicrophoneIsAvailable(bool& available) { return -1; } - int32_t AudioDeviceDummy::InitMicrophone() { return -1; } bool AudioDeviceDummy::MicrophoneIsInitialized() const { return false; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/audio_device_dummy.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/audio_device_dummy.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/audio_device_dummy.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/audio_device_dummy.h 2015-02-03 14:33:35.000000000 +0000 @@ -76,10 +76,8 @@ uint16_t& volumeRight) const OVERRIDE; // Audio mixer initialization - virtual int32_t SpeakerIsAvailable(bool& available) OVERRIDE; virtual int32_t InitSpeaker() OVERRIDE; virtual bool SpeakerIsInitialized() const OVERRIDE; - virtual int32_t MicrophoneIsAvailable(bool& available) OVERRIDE; virtual int32_t InitMicrophone() OVERRIDE; virtual bool MicrophoneIsInitialized() const OVERRIDE; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/file_audio_device.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/file_audio_device.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/file_audio_device.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/file_audio_device.cc 2015-02-03 14:33:35.000000000 +0000 @@ -0,0 +1,587 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include +#include "webrtc/modules/audio_device/dummy/file_audio_device.h" +#include "webrtc/system_wrappers/interface/sleep.h" +#include "webrtc/system_wrappers/interface/thread_wrapper.h" + +namespace webrtc { + +int kRecordingFixedSampleRate = 48000; +int kRecordingNumChannels = 2; +int kPlayoutFixedSampleRate = 48000; +int kPlayoutNumChannels = 2; +int kPlayoutBufferSize = kPlayoutFixedSampleRate / 100 + * kPlayoutNumChannels * 2; +int kRecordingBufferSize = kRecordingFixedSampleRate / 100 + * kRecordingNumChannels * 2; + +FileAudioDevice::FileAudioDevice(const int32_t id, + const char* inputFilename, + const char* outputFile): + _ptrAudioBuffer(NULL), + _recordingBuffer(NULL), + _playoutBuffer(NULL), + _recordingFramesLeft(0), + _playoutFramesLeft(0), + _critSect(*CriticalSectionWrapper::CreateCriticalSection()), + _recordingBufferSizeIn10MS(0), + _recordingFramesIn10MS(0), + _playoutFramesIn10MS(0), + _ptrThreadRec(NULL), + _ptrThreadPlay(NULL), + _recThreadID(0), + _playThreadID(0), + _playing(false), + _recording(false), + _lastCallPlayoutMillis(0), + _lastCallRecordMillis(0), + _outputFile(*FileWrapper::Create()), + _inputFile(*FileWrapper::Create()), + _outputFilename(outputFile), + _inputFilename(inputFilename), + _clock(Clock::GetRealTimeClock()) { +} + +FileAudioDevice::~FileAudioDevice() { + _outputFile.Flush(); + _outputFile.CloseFile(); + delete &_outputFile; + _inputFile.Flush(); + _inputFile.CloseFile(); + delete &_inputFile; +} + +int32_t FileAudioDevice::ActiveAudioLayer( + AudioDeviceModule::AudioLayer& audioLayer) const { + return -1; +} + +int32_t FileAudioDevice::Init() { return 0; } + +int32_t FileAudioDevice::Terminate() { return 0; } + +bool FileAudioDevice::Initialized() const { return true; } + +int16_t FileAudioDevice::PlayoutDevices() { + return 1; +} + +int16_t FileAudioDevice::RecordingDevices() { + return 1; +} + +int32_t FileAudioDevice::PlayoutDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) { + const char* kName = "dummy_device"; + const char* kGuid = "dummy_device_unique_id"; + if (index < 1) { + memset(name, 0, kAdmMaxDeviceNameSize); + memset(guid, 0, kAdmMaxGuidSize); + memcpy(name, kName, strlen(kName)); + memcpy(guid, kGuid, strlen(guid)); + return 0; + } + return -1; +} + +int32_t FileAudioDevice::RecordingDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) { + const char* kName = "dummy_device"; + const char* kGuid = "dummy_device_unique_id"; + if (index < 1) { + memset(name, 0, kAdmMaxDeviceNameSize); + memset(guid, 0, kAdmMaxGuidSize); + memcpy(name, kName, strlen(kName)); + memcpy(guid, kGuid, strlen(guid)); + return 0; + } + return -1; +} + +int32_t FileAudioDevice::SetPlayoutDevice(uint16_t index) { + if (index == 0) { + _playout_index = index; + return 0; + } + return -1; +} + +int32_t FileAudioDevice::SetPlayoutDevice( + AudioDeviceModule::WindowsDeviceType device) { + return -1; +} + +int32_t FileAudioDevice::SetRecordingDevice(uint16_t index) { + if (index == 0) { + _record_index = index; + return _record_index; + } + return -1; +} + +int32_t FileAudioDevice::SetRecordingDevice( + AudioDeviceModule::WindowsDeviceType device) { + return -1; +} + +int32_t FileAudioDevice::PlayoutIsAvailable(bool& available) { + if (_playout_index == 0) { + available = true; + return _playout_index; + } + available = false; + return -1; +} + +int32_t FileAudioDevice::InitPlayout() { + if (_ptrAudioBuffer) + { + // Update webrtc audio buffer with the selected parameters + _ptrAudioBuffer->SetPlayoutSampleRate(kPlayoutFixedSampleRate); + _ptrAudioBuffer->SetPlayoutChannels(kPlayoutNumChannels); + } + return 0; +} + +bool FileAudioDevice::PlayoutIsInitialized() const { + return true; +} + +int32_t FileAudioDevice::RecordingIsAvailable(bool& available) { + if (_record_index == 0) { + available = true; + return _record_index; + } + available = false; + return -1; +} + +int32_t FileAudioDevice::InitRecording() { + CriticalSectionScoped lock(&_critSect); + + if (_recording) { + return -1; + } + + _recordingFramesIn10MS = kRecordingFixedSampleRate/100; + + if (_ptrAudioBuffer) { + _ptrAudioBuffer->SetRecordingSampleRate(kRecordingFixedSampleRate); + _ptrAudioBuffer->SetRecordingChannels(kRecordingNumChannels); + } + return 0; +} + +bool FileAudioDevice::RecordingIsInitialized() const { + return true; +} + +int32_t FileAudioDevice::StartPlayout() { + if (_playing) + { + return 0; + } + + _playing = true; + _playoutFramesLeft = 0; + _playoutFramesIn10MS = kPlayoutFixedSampleRate/100; + + if (!_playoutBuffer) + _playoutBuffer = new int8_t[2 * + kPlayoutNumChannels * + kPlayoutFixedSampleRate/100]; + if (!_playoutBuffer) + { + _playing = false; + return -1; + } + + // PLAYOUT + const char* threadName = "webrtc_audio_module_play_thread"; + _ptrThreadPlay = ThreadWrapper::CreateThread(PlayThreadFunc, + this, + kRealtimePriority, + threadName); + if (_ptrThreadPlay == NULL) + { + _playing = false; + delete [] _playoutBuffer; + _playoutBuffer = NULL; + return -1; + } + + if (_outputFile.OpenFile(_outputFilename.c_str(), + false, false, false) == -1) { + printf("Failed to open playout file %s!", _outputFilename.c_str()); + _playing = false; + delete [] _playoutBuffer; + _playoutBuffer = NULL; + return -1; + } + + unsigned int threadID(0); + if (!_ptrThreadPlay->Start(threadID)) + { + _playing = false; + delete _ptrThreadPlay; + _ptrThreadPlay = NULL; + delete [] _playoutBuffer; + _playoutBuffer = NULL; + return -1; + } + _playThreadID = threadID; + + return 0; +} + +int32_t FileAudioDevice::StopPlayout() { + { + CriticalSectionScoped lock(&_critSect); + _playing = false; + } + + // stop playout thread first + if (_ptrThreadPlay && !_ptrThreadPlay->Stop()) + { + return -1; + } + else { + delete _ptrThreadPlay; + _ptrThreadPlay = NULL; + } + + CriticalSectionScoped lock(&_critSect); + + _playoutFramesLeft = 0; + delete [] _playoutBuffer; + _playoutBuffer = NULL; + _outputFile.Flush(); + _outputFile.CloseFile(); + return 0; +} + +bool FileAudioDevice::Playing() const { + return true; +} + +int32_t FileAudioDevice::StartRecording() { + _recording = true; + + // Make sure we only create the buffer once. + _recordingBufferSizeIn10MS = _recordingFramesIn10MS * + kRecordingNumChannels * + 2; + if (!_recordingBuffer) { + _recordingBuffer = new int8_t[_recordingBufferSizeIn10MS]; + } + + if (_inputFile.OpenFile(_inputFilename.c_str(), true, + true, false) == -1) { + printf("Failed to open audio input file %s!\n", + _inputFilename.c_str()); + _recording = false; + delete[] _recordingBuffer; + _recordingBuffer = NULL; + return -1; + } + + const char* threadName = "webrtc_audio_module_capture_thread"; + _ptrThreadRec = ThreadWrapper::CreateThread(RecThreadFunc, + this, + kRealtimePriority, + threadName); + if (_ptrThreadRec == NULL) + { + _recording = false; + delete [] _recordingBuffer; + _recordingBuffer = NULL; + return -1; + } + + unsigned int threadID(0); + if (!_ptrThreadRec->Start(threadID)) + { + _recording = false; + delete _ptrThreadRec; + _ptrThreadRec = NULL; + delete [] _recordingBuffer; + _recordingBuffer = NULL; + return -1; + } + _recThreadID = threadID; + + return 0; +} + + +int32_t FileAudioDevice::StopRecording() { + { + CriticalSectionScoped lock(&_critSect); + _recording = false; + } + + if (_ptrThreadRec && !_ptrThreadRec->Stop()) + { + return -1; + } + else { + delete _ptrThreadRec; + _ptrThreadRec = NULL; + } + + CriticalSectionScoped lock(&_critSect); + _recordingFramesLeft = 0; + if (_recordingBuffer) + { + delete [] _recordingBuffer; + _recordingBuffer = NULL; + } + return 0; +} + +bool FileAudioDevice::Recording() const { + return _recording; +} + +int32_t FileAudioDevice::SetAGC(bool enable) { return -1; } + +bool FileAudioDevice::AGC() const { return false; } + +int32_t FileAudioDevice::SetWaveOutVolume(uint16_t volumeLeft, + uint16_t volumeRight) { + return -1; +} + +int32_t FileAudioDevice::WaveOutVolume(uint16_t& volumeLeft, + uint16_t& volumeRight) const { + return -1; +} + +int32_t FileAudioDevice::InitSpeaker() { return -1; } + +bool FileAudioDevice::SpeakerIsInitialized() const { return false; } + +int32_t FileAudioDevice::InitMicrophone() { return 0; } + +bool FileAudioDevice::MicrophoneIsInitialized() const { return true; } + +int32_t FileAudioDevice::SpeakerVolumeIsAvailable(bool& available) { + return -1; +} + +int32_t FileAudioDevice::SetSpeakerVolume(uint32_t volume) { return -1; } + +int32_t FileAudioDevice::SpeakerVolume(uint32_t& volume) const { return -1; } + +int32_t FileAudioDevice::MaxSpeakerVolume(uint32_t& maxVolume) const { + return -1; +} + +int32_t FileAudioDevice::MinSpeakerVolume(uint32_t& minVolume) const { + return -1; +} + +int32_t FileAudioDevice::SpeakerVolumeStepSize(uint16_t& stepSize) const { + return -1; +} + +int32_t FileAudioDevice::MicrophoneVolumeIsAvailable(bool& available) { + return -1; +} + +int32_t FileAudioDevice::SetMicrophoneVolume(uint32_t volume) { return -1; } + +int32_t FileAudioDevice::MicrophoneVolume(uint32_t& volume) const { + return -1; +} + +int32_t FileAudioDevice::MaxMicrophoneVolume(uint32_t& maxVolume) const { + return -1; +} + +int32_t FileAudioDevice::MinMicrophoneVolume(uint32_t& minVolume) const { + return -1; +} + +int32_t FileAudioDevice::MicrophoneVolumeStepSize(uint16_t& stepSize) const { + return -1; +} + +int32_t FileAudioDevice::SpeakerMuteIsAvailable(bool& available) { return -1; } + +int32_t FileAudioDevice::SetSpeakerMute(bool enable) { return -1; } + +int32_t FileAudioDevice::SpeakerMute(bool& enabled) const { return -1; } + +int32_t FileAudioDevice::MicrophoneMuteIsAvailable(bool& available) { + return -1; +} + +int32_t FileAudioDevice::SetMicrophoneMute(bool enable) { return -1; } + +int32_t FileAudioDevice::MicrophoneMute(bool& enabled) const { return -1; } + +int32_t FileAudioDevice::MicrophoneBoostIsAvailable(bool& available) { + return -1; +} + +int32_t FileAudioDevice::SetMicrophoneBoost(bool enable) { return -1; } + +int32_t FileAudioDevice::MicrophoneBoost(bool& enabled) const { return -1; } + +int32_t FileAudioDevice::StereoPlayoutIsAvailable(bool& available) { + available = true; + return 0; +} +int32_t FileAudioDevice::SetStereoPlayout(bool enable) { + return 0; +} + +int32_t FileAudioDevice::StereoPlayout(bool& enabled) const { + enabled = true; + return 0; +} + +int32_t FileAudioDevice::StereoRecordingIsAvailable(bool& available) { + available = true; + return 0; +} + +int32_t FileAudioDevice::SetStereoRecording(bool enable) { + return 0; +} + +int32_t FileAudioDevice::StereoRecording(bool& enabled) const { + enabled = true; + return 0; +} + +int32_t FileAudioDevice::SetPlayoutBuffer( + const AudioDeviceModule::BufferType type, + uint16_t sizeMS) { + return 0; +} + +int32_t FileAudioDevice::PlayoutBuffer(AudioDeviceModule::BufferType& type, + uint16_t& sizeMS) const { + type = _playBufType; + return 0; +} + +int32_t FileAudioDevice::PlayoutDelay(uint16_t& delayMS) const { + return 0; +} + +int32_t FileAudioDevice::RecordingDelay(uint16_t& delayMS) const { return -1; } + +int32_t FileAudioDevice::CPULoad(uint16_t& load) const { return -1; } + +bool FileAudioDevice::PlayoutWarning() const { return false; } + +bool FileAudioDevice::PlayoutError() const { return false; } + +bool FileAudioDevice::RecordingWarning() const { return false; } + +bool FileAudioDevice::RecordingError() const { return false; } + +void FileAudioDevice::ClearPlayoutWarning() {} + +void FileAudioDevice::ClearPlayoutError() {} + +void FileAudioDevice::ClearRecordingWarning() {} + +void FileAudioDevice::ClearRecordingError() {} + +void FileAudioDevice::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { + CriticalSectionScoped lock(&_critSect); + + _ptrAudioBuffer = audioBuffer; + + // Inform the AudioBuffer about default settings for this implementation. + // Set all values to zero here since the actual settings will be done by + // InitPlayout and InitRecording later. + _ptrAudioBuffer->SetRecordingSampleRate(0); + _ptrAudioBuffer->SetPlayoutSampleRate(0); + _ptrAudioBuffer->SetRecordingChannels(0); + _ptrAudioBuffer->SetPlayoutChannels(0); +} + +bool FileAudioDevice::PlayThreadFunc(void* pThis) +{ + return (static_cast(pThis)->PlayThreadProcess()); +} + +bool FileAudioDevice::RecThreadFunc(void* pThis) +{ + return (static_cast(pThis)->RecThreadProcess()); +} + +bool FileAudioDevice::PlayThreadProcess() +{ + if(!_playing) + return false; + + uint64_t currentTime = _clock->CurrentNtpInMilliseconds(); + _critSect.Enter(); + + if (_lastCallPlayoutMillis == 0 || + currentTime - _lastCallPlayoutMillis >= 10) + { + _critSect.Leave(); + _ptrAudioBuffer->RequestPlayoutData(_playoutFramesIn10MS); + _critSect.Enter(); + + _playoutFramesLeft = _ptrAudioBuffer->GetPlayoutData(_playoutBuffer); + assert(_playoutFramesLeft == _playoutFramesIn10MS); + if (_outputFile.Open()) { + _outputFile.Write(_playoutBuffer, kPlayoutBufferSize); + _outputFile.Flush(); + } + _lastCallPlayoutMillis = currentTime; + } + _playoutFramesLeft = 0; + _critSect.Leave(); + SleepMs(10 - (_clock->CurrentNtpInMilliseconds() - currentTime)); + return true; +} + +bool FileAudioDevice::RecThreadProcess() +{ + if (!_recording) + return false; + + uint64_t currentTime = _clock->CurrentNtpInMilliseconds(); + _critSect.Enter(); + + if (_lastCallRecordMillis == 0 || + currentTime - _lastCallRecordMillis >= 10) { + if (_inputFile.Open()) { + if (_inputFile.Read(_recordingBuffer, kRecordingBufferSize) > 0) { + _ptrAudioBuffer->SetRecordedBuffer(_recordingBuffer, + _recordingFramesIn10MS); + } else { + _inputFile.Rewind(); + } + _lastCallRecordMillis = currentTime; + _critSect.Leave(); + _ptrAudioBuffer->DeliverRecordedData(); + _critSect.Enter(); + } + } + + _critSect.Leave(); + SleepMs(10 - (_clock->CurrentNtpInMilliseconds() - currentTime)); + return true; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/file_audio_device_factory.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/file_audio_device_factory.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/file_audio_device_factory.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/file_audio_device_factory.cc 2015-02-03 14:33:35.000000000 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_device/dummy/file_audio_device_factory.h" + +#include + +#include "webrtc/modules/audio_device/dummy/file_audio_device.h" + +namespace webrtc { + +char FileAudioDeviceFactory::_inputAudioFilename[MAX_FILENAME_LEN] = ""; +char FileAudioDeviceFactory::_outputAudioFilename[MAX_FILENAME_LEN] = ""; + +FileAudioDevice* FileAudioDeviceFactory::CreateFileAudioDevice( + const int32_t id) { + // Bail out here if the files aren't set. + if (strlen(_inputAudioFilename) == 0 || strlen(_outputAudioFilename) == 0) { + printf("Was compiled with WEBRTC_DUMMY_AUDIO_PLAY_STATIC_FILE " + "but did not set input/output files to use. Bailing out.\n"); + exit(1); + } + return new FileAudioDevice(id, _inputAudioFilename, _outputAudioFilename); +} + +void FileAudioDeviceFactory::SetFilenamesToUse( + const char* inputAudioFilename, const char* outputAudioFilename) { +#ifdef WEBRTC_DUMMY_FILE_DEVICES + assert(strlen(inputAudioFilename) < MAX_FILENAME_LEN && + strlen(outputAudioFilename) < MAX_FILENAME_LEN); + + // Copy the strings since we don't know the lifetime of the input pointers. + strncpy(_inputAudioFilename, inputAudioFilename, MAX_FILENAME_LEN); + strncpy(_outputAudioFilename, outputAudioFilename, MAX_FILENAME_LEN); +#else + // Sanity: must be compiled with the right define to run this. + printf("Trying to use dummy file devices, but is not compiled " + "with WEBRTC_DUMMY_FILE_DEVICES. Bailing out.\n"); + exit(1); +#endif +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/file_audio_device_factory.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/file_audio_device_factory.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/file_audio_device_factory.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/file_audio_device_factory.h 2015-02-03 14:33:35.000000000 +0000 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_AUDIO_DEVICE_FILE_AUDIO_DEVICE_FACTORY_H +#define WEBRTC_AUDIO_DEVICE_FILE_AUDIO_DEVICE_FACTORY_H + +#include "webrtc/common_types.h" + +namespace webrtc { + +class FileAudioDevice; + +// This class is used by audio_device_impl.cc when WebRTC is compiled with +// WEBRTC_DUMMY_FILE_DEVICES. The application must include this file and set the +// filenames to use before the audio device module is initialized. This is +// intended for test tools which use the audio device module. +class FileAudioDeviceFactory { + public: + static FileAudioDevice* CreateFileAudioDevice(const int32_t id); + + // The input file must be a readable 48k stereo raw file. The output + // file must be writable. The strings will be copied. + static void SetFilenamesToUse(const char* inputAudioFilename, + const char* outputAudioFilename); + + private: + static const uint32_t MAX_FILENAME_LEN = 256; + static char _inputAudioFilename[MAX_FILENAME_LEN]; + static char _outputAudioFilename[MAX_FILENAME_LEN]; +}; + +} // namespace webrtc + +#endif // WEBRTC_AUDIO_DEVICE_FILE_AUDIO_DEVICE_FACTORY_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/file_audio_device.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/file_audio_device.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/file_audio_device.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/dummy/file_audio_device.h 2015-02-03 14:33:35.000000000 +0000 @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_AUDIO_DEVICE_FILE_AUDIO_DEVICE_H +#define WEBRTC_AUDIO_DEVICE_FILE_AUDIO_DEVICE_H + +#include + +#include + +#include "webrtc/modules/audio_device/audio_device_generic.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/file_wrapper.h" +#include "webrtc/system_wrappers/interface/clock.h" + +namespace webrtc { +class EventWrapper; +class ThreadWrapper; + +// This is a fake audio device which plays audio from a file as its microphone +// and plays out into a file. +class FileAudioDevice : public AudioDeviceGeneric { + public: + // Constructs a file audio device with |id|. It will read audio from + // |inputFilename| and record output audio to |outputFilename|. + // + // The input file should be a readable 48k stereo raw file, and the output + // file should point to a writable location. The output format will also be + // 48k stereo raw audio. + FileAudioDevice(const int32_t id, + const char* inputFilename, + const char* outputFilename); + virtual ~FileAudioDevice(); + + // Retrieve the currently utilized audio layer + virtual int32_t ActiveAudioLayer( + AudioDeviceModule::AudioLayer& audioLayer) const OVERRIDE; + + // Main initializaton and termination + virtual int32_t Init() OVERRIDE; + virtual int32_t Terminate() OVERRIDE; + virtual bool Initialized() const OVERRIDE; + + // Device enumeration + virtual int16_t PlayoutDevices() OVERRIDE; + virtual int16_t RecordingDevices() OVERRIDE; + virtual int32_t PlayoutDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) OVERRIDE; + virtual int32_t RecordingDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) OVERRIDE; + + // Device selection + virtual int32_t SetPlayoutDevice(uint16_t index) OVERRIDE; + virtual int32_t SetPlayoutDevice( + AudioDeviceModule::WindowsDeviceType device) OVERRIDE; + virtual int32_t SetRecordingDevice(uint16_t index) OVERRIDE; + virtual int32_t SetRecordingDevice( + AudioDeviceModule::WindowsDeviceType device) OVERRIDE; + + // Audio transport initialization + virtual int32_t PlayoutIsAvailable(bool& available) OVERRIDE; + virtual int32_t InitPlayout() OVERRIDE; + virtual bool PlayoutIsInitialized() const OVERRIDE; + virtual int32_t RecordingIsAvailable(bool& available) OVERRIDE; + virtual int32_t InitRecording() OVERRIDE; + virtual bool RecordingIsInitialized() const OVERRIDE; + + // Audio transport control + virtual int32_t StartPlayout() OVERRIDE; + virtual int32_t StopPlayout() OVERRIDE; + virtual bool Playing() const OVERRIDE; + virtual int32_t StartRecording() OVERRIDE; + virtual int32_t StopRecording() OVERRIDE; + virtual bool Recording() const OVERRIDE; + + // Microphone Automatic Gain Control (AGC) + virtual int32_t SetAGC(bool enable) OVERRIDE; + virtual bool AGC() const OVERRIDE; + + // Volume control based on the Windows Wave API (Windows only) + virtual int32_t SetWaveOutVolume(uint16_t volumeLeft, + uint16_t volumeRight) OVERRIDE; + virtual int32_t WaveOutVolume(uint16_t& volumeLeft, + uint16_t& volumeRight) const OVERRIDE; + + // Audio mixer initialization + virtual int32_t InitSpeaker() OVERRIDE; + virtual bool SpeakerIsInitialized() const OVERRIDE; + virtual int32_t InitMicrophone() OVERRIDE; + virtual bool MicrophoneIsInitialized() const OVERRIDE; + + // Speaker volume controls + virtual int32_t SpeakerVolumeIsAvailable(bool& available) OVERRIDE; + virtual int32_t SetSpeakerVolume(uint32_t volume) OVERRIDE; + virtual int32_t SpeakerVolume(uint32_t& volume) const OVERRIDE; + virtual int32_t MaxSpeakerVolume(uint32_t& maxVolume) const OVERRIDE; + virtual int32_t MinSpeakerVolume(uint32_t& minVolume) const OVERRIDE; + virtual int32_t SpeakerVolumeStepSize(uint16_t& stepSize) const OVERRIDE; + + // Microphone volume controls + virtual int32_t MicrophoneVolumeIsAvailable(bool& available) OVERRIDE; + virtual int32_t SetMicrophoneVolume(uint32_t volume) OVERRIDE; + virtual int32_t MicrophoneVolume(uint32_t& volume) const OVERRIDE; + virtual int32_t MaxMicrophoneVolume(uint32_t& maxVolume) const OVERRIDE; + virtual int32_t MinMicrophoneVolume(uint32_t& minVolume) const OVERRIDE; + virtual int32_t MicrophoneVolumeStepSize(uint16_t& stepSize) const OVERRIDE; + + // Speaker mute control + virtual int32_t SpeakerMuteIsAvailable(bool& available) OVERRIDE; + virtual int32_t SetSpeakerMute(bool enable) OVERRIDE; + virtual int32_t SpeakerMute(bool& enabled) const OVERRIDE; + + // Microphone mute control + virtual int32_t MicrophoneMuteIsAvailable(bool& available) OVERRIDE; + virtual int32_t SetMicrophoneMute(bool enable) OVERRIDE; + virtual int32_t MicrophoneMute(bool& enabled) const OVERRIDE; + + // Microphone boost control + virtual int32_t MicrophoneBoostIsAvailable(bool& available) OVERRIDE; + virtual int32_t SetMicrophoneBoost(bool enable) OVERRIDE; + virtual int32_t MicrophoneBoost(bool& enabled) const OVERRIDE; + + // Stereo support + virtual int32_t StereoPlayoutIsAvailable(bool& available) OVERRIDE; + virtual int32_t SetStereoPlayout(bool enable) OVERRIDE; + virtual int32_t StereoPlayout(bool& enabled) const OVERRIDE; + virtual int32_t StereoRecordingIsAvailable(bool& available) OVERRIDE; + virtual int32_t SetStereoRecording(bool enable) OVERRIDE; + virtual int32_t StereoRecording(bool& enabled) const OVERRIDE; + + // Delay information and control + virtual int32_t SetPlayoutBuffer(const AudioDeviceModule::BufferType type, + uint16_t sizeMS) OVERRIDE; + virtual int32_t PlayoutBuffer(AudioDeviceModule::BufferType& type, + uint16_t& sizeMS) const OVERRIDE; + virtual int32_t PlayoutDelay(uint16_t& delayMS) const OVERRIDE; + virtual int32_t RecordingDelay(uint16_t& delayMS) const OVERRIDE; + + // CPU load + virtual int32_t CPULoad(uint16_t& load) const OVERRIDE; + + virtual bool PlayoutWarning() const OVERRIDE; + virtual bool PlayoutError() const OVERRIDE; + virtual bool RecordingWarning() const OVERRIDE; + virtual bool RecordingError() const OVERRIDE; + virtual void ClearPlayoutWarning() OVERRIDE; + virtual void ClearPlayoutError() OVERRIDE; + virtual void ClearRecordingWarning() OVERRIDE; + virtual void ClearRecordingError() OVERRIDE; + + virtual void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) OVERRIDE; + + private: + static bool RecThreadFunc(void*); + static bool PlayThreadFunc(void*); + bool RecThreadProcess(); + bool PlayThreadProcess(); + + int32_t _playout_index; + int32_t _record_index; + AudioDeviceModule::BufferType _playBufType; + AudioDeviceBuffer* _ptrAudioBuffer; + int8_t* _recordingBuffer; // In bytes. + int8_t* _playoutBuffer; // In bytes. + uint32_t _recordingFramesLeft; + uint32_t _playoutFramesLeft; + CriticalSectionWrapper& _critSect; + + uint32_t _recordingBufferSizeIn10MS; + uint32_t _recordingFramesIn10MS; + uint32_t _playoutFramesIn10MS; + + ThreadWrapper* _ptrThreadRec; + ThreadWrapper* _ptrThreadPlay; + uint32_t _recThreadID; + uint32_t _playThreadID; + + bool _playing; + bool _recording; + uint64_t _lastCallPlayoutMillis; + uint64_t _lastCallRecordMillis; + + FileWrapper& _outputFile; + FileWrapper& _inputFile; + std::string _outputFilename; + std::string _inputFilename; + + Clock* _clock; +}; + +} // namespace webrtc + +#endif // WEBRTC_AUDIO_DEVICE_FILE_AUDIO_DEVICE_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/include/audio_device_defines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/include/audio_device_defines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/include/audio_device_defines.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/include/audio_device_defines.h 2015-02-03 14:33:35.000000000 +0000 @@ -63,14 +63,16 @@ const int32_t clockDrift, const uint32_t currentMicLevel, const bool keyPressed, - uint32_t& newMicLevel) = 0; + uint32_t& newMicLevel) = 0; virtual int32_t NeedMorePlayData(const uint32_t nSamples, const uint8_t nBytesPerSample, const uint8_t nChannels, const uint32_t samplesPerSec, void* audioSamples, - uint32_t& nSamplesOut) = 0; + uint32_t& nSamplesOut, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) = 0; // Method to pass captured data directly and unmixed to network channels. // |channel_ids| contains a list of VoE channels which are the @@ -101,13 +103,34 @@ // Method to pass the captured audio data to the specific VoE channel. // |voe_channel| is the id of the VoE channel which is the sink to the // capture data. - // TODO(xians): Make the interface pure virtual after libjingle - // has its implementation. + // TODO(xians): Remove this interface after Libjingle switches to + // PushCaptureData(). virtual void OnData(int voe_channel, const void* audio_data, int bits_per_sample, int sample_rate, int number_of_channels, int number_of_frames) {} + // Method to push the captured audio data to the specific VoE channel. + // The data will not undergo audio processing. + // |voe_channel| is the id of the VoE channel which is the sink to the + // capture data. + // TODO(xians): Make the interface pure virtual after Libjingle + // has its implementation. + virtual void PushCaptureData(int voe_channel, const void* audio_data, + int bits_per_sample, int sample_rate, + int number_of_channels, + int number_of_frames) {} + + // Method to pull mixed render audio data from all active VoE channels. + // The data will not be passed as reference for audio processing internally. + // TODO(xians): Support getting the unmixed render data from specific VoE + // channel. + virtual void PullRenderData(int bits_per_sample, int sample_rate, + int number_of_channels, int number_of_frames, + void* audio_data, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) {} + protected: virtual ~AudioTransport() {} }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/include/audio_device.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/include/audio_device.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/include/audio_device.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/include/audio_device.h 2015-02-03 14:33:35.000000000 +0000 @@ -107,10 +107,8 @@ uint16_t* volumeRight) const = 0; // Audio mixer initialization - virtual int32_t SpeakerIsAvailable(bool* available) = 0; virtual int32_t InitSpeaker() = 0; virtual bool SpeakerIsInitialized() const = 0; - virtual int32_t MicrophoneIsAvailable(bool* available) = 0; virtual int32_t InitMicrophone() = 0; virtual bool MicrophoneIsInitialized() const = 0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/include/fake_audio_device.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/include/fake_audio_device.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/include/fake_audio_device.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/include/fake_audio_device.h 2015-02-03 14:33:35.000000000 +0000 @@ -25,19 +25,11 @@ return 0; } virtual int32_t Init() { return 0; } - virtual int32_t SpeakerIsAvailable(bool* available) { - *available = true; - return 0; - } virtual int32_t InitSpeaker() { return 0; } virtual int32_t SetPlayoutDevice(uint16_t index) { return 0; } virtual int32_t SetPlayoutDevice(WindowsDeviceType device) { return 0; } virtual int32_t SetStereoPlayout(bool enable) { return 0; } virtual int32_t StopPlayout() { return 0; } - virtual int32_t MicrophoneIsAvailable(bool* available) { - *available = true; - return 0; - } virtual int32_t InitMicrophone() { return 0; } virtual int32_t SetRecordingDevice(uint16_t index) { return 0; } virtual int32_t SetRecordingDevice(WindowsDeviceType device) { return 0; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_ios.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_ios.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_ios.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_ios.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,1897 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include // AudioSession - -#include "webrtc/modules/audio_device/ios/audio_device_ios.h" - -#include "webrtc/system_wrappers/interface/thread_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" - -namespace webrtc { -AudioDeviceIPhone::AudioDeviceIPhone(const int32_t id) - : - _ptrAudioBuffer(NULL), - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _captureWorkerThread(NULL), - _captureWorkerThreadId(0), - _id(id), - _auVoiceProcessing(NULL), - _initialized(false), - _isShutDown(false), - _recording(false), - _playing(false), - _recIsInitialized(false), - _playIsInitialized(false), - _recordingDeviceIsSpecified(false), - _playoutDeviceIsSpecified(false), - _micIsInitialized(false), - _speakerIsInitialized(false), - _AGC(false), - _adbSampFreq(0), - _recordingDelay(0), - _playoutDelay(0), - _playoutDelayMeasurementCounter(9999), - _recordingDelayHWAndOS(0), - _recordingDelayMeasurementCounter(9999), - _playWarning(0), - _playError(0), - _recWarning(0), - _recError(0), - _playoutBufferUsed(0), - _recordingCurrentSeq(0), - _recordingBufferTotalSize(0) { - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, - "%s created", __FUNCTION__); - - memset(_playoutBuffer, 0, sizeof(_playoutBuffer)); - memset(_recordingBuffer, 0, sizeof(_recordingBuffer)); - memset(_recordingLength, 0, sizeof(_recordingLength)); - memset(_recordingSeqNumber, 0, sizeof(_recordingSeqNumber)); -} - -AudioDeviceIPhone::~AudioDeviceIPhone() { - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, - "%s destroyed", __FUNCTION__); - - Terminate(); - - delete &_critSect; -} - - -// ============================================================================ -// API -// ============================================================================ - -void AudioDeviceIPhone::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(&_critSect); - - _ptrAudioBuffer = audioBuffer; - - // inform the AudioBuffer about default settings for this implementation - _ptrAudioBuffer->SetRecordingSampleRate(ENGINE_REC_BUF_SIZE_IN_SAMPLES); - _ptrAudioBuffer->SetPlayoutSampleRate(ENGINE_PLAY_BUF_SIZE_IN_SAMPLES); - _ptrAudioBuffer->SetRecordingChannels(N_REC_CHANNELS); - _ptrAudioBuffer->SetPlayoutChannels(N_PLAY_CHANNELS); -} - -int32_t AudioDeviceIPhone::ActiveAudioLayer( - AudioDeviceModule::AudioLayer& audioLayer) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - audioLayer = AudioDeviceModule::kPlatformDefaultAudio; - return 0; -} - -int32_t AudioDeviceIPhone::Init() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(&_critSect); - - if (_initialized) { - return 0; - } - - _isShutDown = false; - - // Create and start capture thread - if (_captureWorkerThread == NULL) { - _captureWorkerThread - = ThreadWrapper::CreateThread(RunCapture, this, kRealtimePriority, - "CaptureWorkerThread"); - - if (_captureWorkerThread == NULL) { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, - _id, "CreateThread() error"); - return -1; - } - - unsigned int threadID(0); - bool res = _captureWorkerThread->Start(threadID); - _captureWorkerThreadId = static_cast(threadID); - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, - _id, "CaptureWorkerThread started (res=%d)", res); - } else { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, - _id, "Thread already created"); - } - _playWarning = 0; - _playError = 0; - _recWarning = 0; - _recError = 0; - - _initialized = true; - - return 0; -} - -int32_t AudioDeviceIPhone::Terminate() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (!_initialized) { - return 0; - } - - - // Stop capture thread - if (_captureWorkerThread != NULL) { - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, - _id, "Stopping CaptureWorkerThread"); - bool res = _captureWorkerThread->Stop(); - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, - _id, "CaptureWorkerThread stopped (res=%d)", res); - delete _captureWorkerThread; - _captureWorkerThread = NULL; - } - - // Shut down Audio Unit - ShutdownPlayOrRecord(); - - _isShutDown = true; - _initialized = false; - _speakerIsInitialized = false; - _micIsInitialized = false; - _playoutDeviceIsSpecified = false; - _recordingDeviceIsSpecified = false; - return 0; -} - -bool AudioDeviceIPhone::Initialized() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_initialized); -} - -int32_t AudioDeviceIPhone::SpeakerIsAvailable(bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - // speaker is always available in IOS - available = true; - return 0; -} - -int32_t AudioDeviceIPhone::InitSpeaker() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(&_critSect); - - if (!_initialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, - _id, " Not initialized"); - return -1; - } - - if (_playing) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, - _id, " Cannot init speaker when playing"); - return -1; - } - - if (!_playoutDeviceIsSpecified) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, - _id, " Playout device is not specified"); - return -1; - } - - // Do nothing - _speakerIsInitialized = true; - - return 0; -} - -int32_t AudioDeviceIPhone::MicrophoneIsAvailable(bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; - - OSStatus result = -1; - UInt32 channel = 0; - UInt32 size = sizeof(channel); - result = AudioSessionGetProperty(kAudioSessionProperty_AudioInputAvailable, - &size, &channel); - if (channel != 0) { - // API is not supported on this platform, we return available = true - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, - _id, " API call not supported on this version"); - available = true; - return 0; - } - - available = (channel == 0) ? false : true; - - return 0; -} - -int32_t AudioDeviceIPhone::InitMicrophone() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(&_critSect); - - if (!_initialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, - _id, " Not initialized"); - return -1; - } - - if (_recording) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, - _id, " Cannot init mic when recording"); - return -1; - } - - if (!_recordingDeviceIsSpecified) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, - _id, " Recording device is not specified"); - return -1; - } - - // Do nothing - - _micIsInitialized = true; - - return 0; -} - -bool AudioDeviceIPhone::SpeakerIsInitialized() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return _speakerIsInitialized; -} - -bool AudioDeviceIPhone::MicrophoneIsInitialized() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return _micIsInitialized; -} - -int32_t AudioDeviceIPhone::SpeakerVolumeIsAvailable(bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; // Speaker volume not supported on iOS - - return 0; -} - -int32_t AudioDeviceIPhone::SetSpeakerVolume(uint32_t volume) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceIPhone::SetSpeakerVolume(volume=%u)", volume); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -int32_t AudioDeviceIPhone::SpeakerVolume(uint32_t& volume) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -int32_t - AudioDeviceIPhone::SetWaveOutVolume(uint16_t volumeLeft, - uint16_t volumeRight) { - WEBRTC_TRACE( - kTraceModuleCall, - kTraceAudioDevice, - _id, - "AudioDeviceIPhone::SetWaveOutVolume(volumeLeft=%u, volumeRight=%u)", - volumeLeft, volumeRight); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - - return -1; -} - -int32_t -AudioDeviceIPhone::WaveOutVolume(uint16_t& /*volumeLeft*/, - uint16_t& /*volumeRight*/) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -int32_t - AudioDeviceIPhone::MaxSpeakerVolume(uint32_t& maxVolume) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -int32_t AudioDeviceIPhone::MinSpeakerVolume( - uint32_t& minVolume) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -int32_t - AudioDeviceIPhone::SpeakerVolumeStepSize(uint16_t& stepSize) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -int32_t AudioDeviceIPhone::SpeakerMuteIsAvailable(bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; // Speaker mute not supported on iOS - - return 0; -} - -int32_t AudioDeviceIPhone::SetSpeakerMute(bool enable) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -int32_t AudioDeviceIPhone::SpeakerMute(bool& enabled) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -int32_t AudioDeviceIPhone::MicrophoneMuteIsAvailable(bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; // Mic mute not supported on iOS - - return 0; -} - -int32_t AudioDeviceIPhone::SetMicrophoneMute(bool enable) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -int32_t AudioDeviceIPhone::MicrophoneMute(bool& enabled) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -int32_t AudioDeviceIPhone::MicrophoneBoostIsAvailable(bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; // Mic boost not supported on iOS - - return 0; -} - -int32_t AudioDeviceIPhone::SetMicrophoneBoost(bool enable) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceIPhone::SetMicrophoneBoost(enable=%u)", enable); - - if (!_micIsInitialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Microphone not initialized"); - return -1; - } - - if (enable) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " SetMicrophoneBoost cannot be enabled on this platform"); - return -1; - } - - return 0; -} - -int32_t AudioDeviceIPhone::MicrophoneBoost(bool& enabled) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - if (!_micIsInitialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Microphone not initialized"); - return -1; - } - - enabled = false; - - return 0; -} - -int32_t AudioDeviceIPhone::StereoRecordingIsAvailable(bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; // Stereo recording not supported on iOS - - return 0; -} - -int32_t AudioDeviceIPhone::SetStereoRecording(bool enable) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceIPhone::SetStereoRecording(enable=%u)", enable); - - if (enable) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Stereo recording is not supported on this platform"); - return -1; - } - return 0; -} - -int32_t AudioDeviceIPhone::StereoRecording(bool& enabled) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - enabled = false; - return 0; -} - -int32_t AudioDeviceIPhone::StereoPlayoutIsAvailable(bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; // Stereo playout not supported on iOS - - return 0; -} - -int32_t AudioDeviceIPhone::SetStereoPlayout(bool enable) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceIPhone::SetStereoPlayout(enable=%u)", enable); - - if (enable) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Stereo playout is not supported on this platform"); - return -1; - } - return 0; -} - -int32_t AudioDeviceIPhone::StereoPlayout(bool& enabled) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - enabled = false; - return 0; -} - -int32_t AudioDeviceIPhone::SetAGC(bool enable) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceIPhone::SetAGC(enable=%d)", enable); - - _AGC = enable; - - return 0; -} - -bool AudioDeviceIPhone::AGC() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - return _AGC; -} - -int32_t AudioDeviceIPhone::MicrophoneVolumeIsAvailable(bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; // Mic volume not supported on IOS - - return 0; -} - -int32_t AudioDeviceIPhone::SetMicrophoneVolume(uint32_t volume) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceIPhone::SetMicrophoneVolume(volume=%u)", volume); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -int32_t - AudioDeviceIPhone::MicrophoneVolume(uint32_t& volume) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -int32_t - AudioDeviceIPhone::MaxMicrophoneVolume(uint32_t& maxVolume) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -int32_t - AudioDeviceIPhone::MinMicrophoneVolume(uint32_t& minVolume) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -int32_t - AudioDeviceIPhone::MicrophoneVolumeStepSize( - uint16_t& stepSize) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -int16_t AudioDeviceIPhone::PlayoutDevices() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - return (int16_t)1; -} - -int32_t AudioDeviceIPhone::SetPlayoutDevice(uint16_t index) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceIPhone::SetPlayoutDevice(index=%u)", index); - - if (_playIsInitialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Playout already initialized"); - return -1; - } - - if (index !=0) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " SetPlayoutDevice invalid index"); - return -1; - } - _playoutDeviceIsSpecified = true; - - return 0; -} - -int32_t - AudioDeviceIPhone::SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "WindowsDeviceType not supported"); - return -1; -} - -int32_t - AudioDeviceIPhone::PlayoutDeviceName(uint16_t index, - char name[kAdmMaxDeviceNameSize], - char guid[kAdmMaxGuidSize]) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceIPhone::PlayoutDeviceName(index=%u)", index); - - if (index != 0) { - return -1; - } - // return empty strings - memset(name, 0, kAdmMaxDeviceNameSize); - if (guid != NULL) { - memset(guid, 0, kAdmMaxGuidSize); - } - - return 0; -} - -int32_t - AudioDeviceIPhone::RecordingDeviceName(uint16_t index, - char name[kAdmMaxDeviceNameSize], - char guid[kAdmMaxGuidSize]) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceIPhone::RecordingDeviceName(index=%u)", index); - - if (index != 0) { - return -1; - } - // return empty strings - memset(name, 0, kAdmMaxDeviceNameSize); - if (guid != NULL) { - memset(guid, 0, kAdmMaxGuidSize); - } - - return 0; -} - -int16_t AudioDeviceIPhone::RecordingDevices() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return (int16_t)1; -} - -int32_t AudioDeviceIPhone::SetRecordingDevice(uint16_t index) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceIPhone::SetRecordingDevice(index=%u)", index); - - if (_recIsInitialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Recording already initialized"); - return -1; - } - - if (index !=0) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " SetRecordingDevice invalid index"); - return -1; - } - - _recordingDeviceIsSpecified = true; - - return 0; -} - -int32_t - AudioDeviceIPhone::SetRecordingDevice( - AudioDeviceModule::WindowsDeviceType) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "WindowsDeviceType not supported"); - return -1; -} - -// ---------------------------------------------------------------------------- -// SetLoudspeakerStatus -// -// Overrides the receiver playout route to speaker instead. See -// kAudioSessionProperty_OverrideCategoryDefaultToSpeaker in CoreAudio -// documentation. -// ---------------------------------------------------------------------------- - -int32_t AudioDeviceIPhone::SetLoudspeakerStatus(bool enable) { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioDeviceIPhone::SetLoudspeakerStatus(enable=%d)", enable); - - UInt32 doChangeDefaultRoute = enable ? 1 : 0; - OSStatus err = AudioSessionSetProperty( - kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, - sizeof(doChangeDefaultRoute), &doChangeDefaultRoute); - - if (err != noErr) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Error changing default output route " \ - "(only available on iOS 3.1 or later)"); - return -1; - } - - return 0; -} - -int32_t AudioDeviceIPhone::GetLoudspeakerStatus(bool &enabled) const { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioDeviceIPhone::SetLoudspeakerStatus(enabled=?)"); - - UInt32 route(0); - UInt32 size = sizeof(route); - OSStatus err = AudioSessionGetProperty( - kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, - &size, &route); - if (err != noErr) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Error changing default output route " \ - "(only available on iOS 3.1 or later)"); - return -1; - } - - enabled = route == 1 ? true: false; - - return 0; -} - -int32_t AudioDeviceIPhone::PlayoutIsAvailable(bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = false; - - // Try to initialize the playout side - int32_t res = InitPlayout(); - - // Cancel effect of initialization - StopPlayout(); - - if (res != -1) { - available = true; - } - - return 0; -} - -int32_t AudioDeviceIPhone::RecordingIsAvailable(bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = false; - - // Try to initialize the recording side - int32_t res = InitRecording(); - - // Cancel effect of initialization - StopRecording(); - - if (res != -1) { - available = true; - } - - return 0; -} - -int32_t AudioDeviceIPhone::InitPlayout() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(&_critSect); - - if (!_initialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, " Not initialized"); - return -1; - } - - if (_playing) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Playout already started"); - return -1; - } - - if (_playIsInitialized) { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Playout already initialized"); - return 0; - } - - if (!_playoutDeviceIsSpecified) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Playout device is not specified"); - return -1; - } - - // Initialize the speaker - if (InitSpeaker() == -1) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " InitSpeaker() failed"); - } - - _playIsInitialized = true; - - if (!_recIsInitialized) { - // Audio init - if (InitPlayOrRecord() == -1) { - // todo: Handle error - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " InitPlayOrRecord() failed"); - } - } else { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Recording already initialized - InitPlayOrRecord() not called"); - } - - return 0; -} - -bool AudioDeviceIPhone::PlayoutIsInitialized() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_playIsInitialized); -} - -int32_t AudioDeviceIPhone::InitRecording() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(&_critSect); - - if (!_initialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Not initialized"); - return -1; - } - - if (_recording) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Recording already started"); - return -1; - } - - if (_recIsInitialized) { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Recording already initialized"); - return 0; - } - - if (!_recordingDeviceIsSpecified) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Recording device is not specified"); - return -1; - } - - // Initialize the microphone - if (InitMicrophone() == -1) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " InitMicrophone() failed"); - } - - _recIsInitialized = true; - - if (!_playIsInitialized) { - // Audio init - if (InitPlayOrRecord() == -1) { - // todo: Handle error - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " InitPlayOrRecord() failed"); - } - } else { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Playout already initialized - InitPlayOrRecord() " \ - "not called"); - } - - return 0; -} - -bool AudioDeviceIPhone::RecordingIsInitialized() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_recIsInitialized); -} - -int32_t AudioDeviceIPhone::StartRecording() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(&_critSect); - - if (!_recIsInitialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Recording not initialized"); - return -1; - } - - if (_recording) { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Recording already started"); - return 0; - } - - // Reset recording buffer - memset(_recordingBuffer, 0, sizeof(_recordingBuffer)); - memset(_recordingLength, 0, sizeof(_recordingLength)); - memset(_recordingSeqNumber, 0, sizeof(_recordingSeqNumber)); - _recordingCurrentSeq = 0; - _recordingBufferTotalSize = 0; - _recordingDelay = 0; - _recordingDelayHWAndOS = 0; - // Make sure first call to update delay function will update delay - _recordingDelayMeasurementCounter = 9999; - _recWarning = 0; - _recError = 0; - - if (!_playing) { - // Start Audio Unit - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " Starting Audio Unit"); - OSStatus result = AudioOutputUnitStart(_auVoiceProcessing); - if (0 != result) { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " Error starting Audio Unit (result=%d)", result); - return -1; - } - } - - _recording = true; - - return 0; -} - -int32_t AudioDeviceIPhone::StopRecording() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(&_critSect); - - if (!_recIsInitialized) { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Recording is not initialized"); - return 0; - } - - _recording = false; - - if (!_playing) { - // Both playout and recording has stopped, shutdown the device - ShutdownPlayOrRecord(); - } - - _recIsInitialized = false; - _micIsInitialized = false; - - return 0; -} - -bool AudioDeviceIPhone::Recording() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_recording); -} - -int32_t AudioDeviceIPhone::StartPlayout() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - // This lock is (among other things) needed to avoid concurrency issues - // with capture thread - // shutting down Audio Unit - CriticalSectionScoped lock(&_critSect); - - if (!_playIsInitialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Playout not initialized"); - return -1; - } - - if (_playing) { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Playing already started"); - return 0; - } - - // Reset playout buffer - memset(_playoutBuffer, 0, sizeof(_playoutBuffer)); - _playoutBufferUsed = 0; - _playoutDelay = 0; - // Make sure first call to update delay function will update delay - _playoutDelayMeasurementCounter = 9999; - _playWarning = 0; - _playError = 0; - - if (!_recording) { - // Start Audio Unit - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " Starting Audio Unit"); - OSStatus result = AudioOutputUnitStart(_auVoiceProcessing); - if (0 != result) { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " Error starting Audio Unit (result=%d)", result); - return -1; - } - } - - _playing = true; - - return 0; -} - -int32_t AudioDeviceIPhone::StopPlayout() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(&_critSect); - - if (!_playIsInitialized) { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Playout is not initialized"); - return 0; - } - - _playing = false; - - if (!_recording) { - // Both playout and recording has stopped, signal shutdown the device - ShutdownPlayOrRecord(); - } - - _playIsInitialized = false; - _speakerIsInitialized = false; - - return 0; -} - -bool AudioDeviceIPhone::Playing() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_playing); -} - -// ---------------------------------------------------------------------------- -// ResetAudioDevice -// -// Disable playout and recording, signal to capture thread to shutdown, -// and set enable states after shutdown to same as current. -// In capture thread audio device will be shutdown, then started again. -// ---------------------------------------------------------------------------- -int32_t AudioDeviceIPhone::ResetAudioDevice() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(&_critSect); - - if (!_playIsInitialized && !_recIsInitialized) { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Playout or recording not initialized, doing nothing"); - return 0; // Nothing to reset - } - - // Store the states we have before stopping to restart below - bool initPlay = _playIsInitialized; - bool play = _playing; - bool initRec = _recIsInitialized; - bool rec = _recording; - - int res(0); - - // Stop playout and recording - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " Stopping playout and recording"); - res += StopPlayout(); - res += StopRecording(); - - // Restart - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " Restarting playout and recording (%d, %d, %d, %d)", - initPlay, play, initRec, rec); - if (initPlay) res += InitPlayout(); - if (initRec) res += InitRecording(); - if (play) res += StartPlayout(); - if (rec) res += StartRecording(); - - if (0 != res) { - // Logging is done in init/start/stop calls above - return -1; - } - - return 0; -} - -int32_t AudioDeviceIPhone::PlayoutDelay(uint16_t& delayMS) const { - delayMS = _playoutDelay; - return 0; -} - -int32_t AudioDeviceIPhone::RecordingDelay(uint16_t& delayMS) const { - delayMS = _recordingDelay; - return 0; -} - -int32_t - AudioDeviceIPhone::SetPlayoutBuffer( - const AudioDeviceModule::BufferType type, - uint16_t sizeMS) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceIPhone::SetPlayoutBuffer(type=%u, sizeMS=%u)", - type, sizeMS); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -int32_t - AudioDeviceIPhone::PlayoutBuffer(AudioDeviceModule::BufferType& type, - uint16_t& sizeMS) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - type = AudioDeviceModule::kAdaptiveBufferSize; - - sizeMS = _playoutDelay; - - return 0; -} - -int32_t AudioDeviceIPhone::CPULoad(uint16_t& /*load*/) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -bool AudioDeviceIPhone::PlayoutWarning() const { - return (_playWarning > 0); -} - -bool AudioDeviceIPhone::PlayoutError() const { - return (_playError > 0); -} - -bool AudioDeviceIPhone::RecordingWarning() const { - return (_recWarning > 0); -} - -bool AudioDeviceIPhone::RecordingError() const { - return (_recError > 0); -} - -void AudioDeviceIPhone::ClearPlayoutWarning() { - _playWarning = 0; -} - -void AudioDeviceIPhone::ClearPlayoutError() { - _playError = 0; -} - -void AudioDeviceIPhone::ClearRecordingWarning() { - _recWarning = 0; -} - -void AudioDeviceIPhone::ClearRecordingError() { - _recError = 0; -} - -// ============================================================================ -// Private Methods -// ============================================================================ - -int32_t AudioDeviceIPhone::InitPlayOrRecord() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - OSStatus result = -1; - - // Check if already initialized - if (NULL != _auVoiceProcessing) { - // We already have initialized before and created any of the audio unit, - // check that all exist - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Already initialized"); - // todo: Call AudioUnitReset() here and empty all buffers? - return 0; - } - - // Create Voice Processing Audio Unit - AudioComponentDescription desc; - AudioComponent comp; - - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO; - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - comp = AudioComponentFindNext(NULL, &desc); - if (NULL == comp) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not find audio component for Audio Unit"); - return -1; - } - - result = AudioComponentInstanceNew(comp, &_auVoiceProcessing); - if (0 != result) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not create Audio Unit instance (result=%d)", - result); - return -1; - } - - // Set preferred hardware sample rate to 16 kHz - Float64 sampleRate(16000.0); - result = AudioSessionSetProperty( - kAudioSessionProperty_PreferredHardwareSampleRate, - sizeof(sampleRate), &sampleRate); - if (0 != result) { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Could not set preferred sample rate (result=%d)", result); - } - - uint32_t voiceChat = kAudioSessionMode_VoiceChat; - AudioSessionSetProperty(kAudioSessionProperty_Mode, - sizeof(voiceChat), &voiceChat); - - ////////////////////// - // Setup Voice Processing Audio Unit - - // Note: For Signal Processing AU element 0 is output bus, element 1 is - // input bus for global scope element is irrelevant (always use - // element 0) - - // Enable IO on both elements - - // todo: Below we just log and continue upon error. We might want - // to close AU and return error for some cases. - // todo: Log info about setup. - - UInt32 enableIO = 1; - result = AudioUnitSetProperty(_auVoiceProcessing, - kAudioOutputUnitProperty_EnableIO, - kAudioUnitScope_Input, - 1, // input bus - &enableIO, - sizeof(enableIO)); - if (0 != result) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not enable IO on input (result=%d)", result); - } - - result = AudioUnitSetProperty(_auVoiceProcessing, - kAudioOutputUnitProperty_EnableIO, - kAudioUnitScope_Output, - 0, // output bus - &enableIO, - sizeof(enableIO)); - if (0 != result) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not enable IO on output (result=%d)", result); - } - - // Disable AU buffer allocation for the recorder, we allocate our own - UInt32 flag = 0; - result = AudioUnitSetProperty( - _auVoiceProcessing, kAudioUnitProperty_ShouldAllocateBuffer, - kAudioUnitScope_Output, 1, &flag, sizeof(flag)); - if (0 != result) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Could not disable AU buffer allocation (result=%d)", - result); - // Should work anyway - } - - // Set recording callback - AURenderCallbackStruct auCbS; - memset(&auCbS, 0, sizeof(auCbS)); - auCbS.inputProc = RecordProcess; - auCbS.inputProcRefCon = this; - result = AudioUnitSetProperty(_auVoiceProcessing, - kAudioOutputUnitProperty_SetInputCallback, - kAudioUnitScope_Global, 1, - &auCbS, sizeof(auCbS)); - if (0 != result) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not set record callback for Audio Unit (result=%d)", - result); - } - - // Set playout callback - memset(&auCbS, 0, sizeof(auCbS)); - auCbS.inputProc = PlayoutProcess; - auCbS.inputProcRefCon = this; - result = AudioUnitSetProperty(_auVoiceProcessing, - kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Global, 0, - &auCbS, sizeof(auCbS)); - if (0 != result) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not set play callback for Audio Unit (result=%d)", - result); - } - - // Get stream format for out/0 - AudioStreamBasicDescription playoutDesc; - UInt32 size = sizeof(playoutDesc); - result = AudioUnitGetProperty(_auVoiceProcessing, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, 0, &playoutDesc, - &size); - if (0 != result) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not get stream format Audio Unit out/0 (result=%d)", - result); - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Audio Unit playout opened in sampling rate %f", - playoutDesc.mSampleRate); - - playoutDesc.mSampleRate = sampleRate; - - // Store the sampling frequency to use towards the Audio Device Buffer - // todo: Add 48 kHz (increase buffer sizes). Other fs? - if ((playoutDesc.mSampleRate > 44090.0) - && (playoutDesc.mSampleRate < 44110.0)) { - _adbSampFreq = 44100; - } else if ((playoutDesc.mSampleRate > 15990.0) - && (playoutDesc.mSampleRate < 16010.0)) { - _adbSampFreq = 16000; - } else if ((playoutDesc.mSampleRate > 7990.0) - && (playoutDesc.mSampleRate < 8010.0)) { - _adbSampFreq = 8000; - } else { - _adbSampFreq = 0; - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Audio Unit out/0 opened in unknown sampling rate (%f)", - playoutDesc.mSampleRate); - // todo: We should bail out here. - } - - // Set the audio device buffer sampling rate, - // we assume we get the same for play and record - if (_ptrAudioBuffer->SetRecordingSampleRate(_adbSampFreq) < 0) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not set audio device buffer recording sampling rate (%d)", - _adbSampFreq); - } - - if (_ptrAudioBuffer->SetPlayoutSampleRate(_adbSampFreq) < 0) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not set audio device buffer playout sampling rate (%d)", - _adbSampFreq); - } - - // Set stream format for in/0 (use same sampling frequency as for out/0) - playoutDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger - | kLinearPCMFormatFlagIsPacked - | kLinearPCMFormatFlagIsNonInterleaved; - playoutDesc.mBytesPerPacket = 2; - playoutDesc.mFramesPerPacket = 1; - playoutDesc.mBytesPerFrame = 2; - playoutDesc.mChannelsPerFrame = 1; - playoutDesc.mBitsPerChannel = 16; - result = AudioUnitSetProperty(_auVoiceProcessing, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, 0, &playoutDesc, size); - if (0 != result) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not set stream format Audio Unit in/0 (result=%d)", - result); - } - - // Get stream format for in/1 - AudioStreamBasicDescription recordingDesc; - size = sizeof(recordingDesc); - result = AudioUnitGetProperty(_auVoiceProcessing, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, 1, &recordingDesc, - &size); - if (0 != result) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not get stream format Audio Unit in/1 (result=%d)", - result); - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Audio Unit recording opened in sampling rate %f", - recordingDesc.mSampleRate); - - recordingDesc.mSampleRate = sampleRate; - - // Set stream format for out/1 (use same sampling frequency as for in/1) - recordingDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger - | kLinearPCMFormatFlagIsPacked - | kLinearPCMFormatFlagIsNonInterleaved; - - recordingDesc.mBytesPerPacket = 2; - recordingDesc.mFramesPerPacket = 1; - recordingDesc.mBytesPerFrame = 2; - recordingDesc.mChannelsPerFrame = 1; - recordingDesc.mBitsPerChannel = 16; - result = AudioUnitSetProperty(_auVoiceProcessing, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, 1, &recordingDesc, - size); - if (0 != result) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not set stream format Audio Unit out/1 (result=%d)", - result); - } - - // Initialize here already to be able to get/set stream properties. - result = AudioUnitInitialize(_auVoiceProcessing); - if (0 != result) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not init Audio Unit (result=%d)", result); - } - - // Get hardware sample rate for logging (see if we get what we asked for) - Float64 hardwareSampleRate = 0.0; - size = sizeof(hardwareSampleRate); - result = AudioSessionGetProperty( - kAudioSessionProperty_CurrentHardwareSampleRate, &size, - &hardwareSampleRate); - if (0 != result) { - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " Could not get current HW sample rate (result=%d)", result); - } - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " Current HW sample rate is %f, ADB sample rate is %d", - hardwareSampleRate, _adbSampFreq); - - return 0; -} - -int32_t AudioDeviceIPhone::ShutdownPlayOrRecord() { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - // Close and delete AU - OSStatus result = -1; - if (NULL != _auVoiceProcessing) { - result = AudioOutputUnitStop(_auVoiceProcessing); - if (0 != result) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Error stopping Audio Unit (result=%d)", result); - } - result = AudioComponentInstanceDispose(_auVoiceProcessing); - if (0 != result) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Error disposing Audio Unit (result=%d)", result); - } - _auVoiceProcessing = NULL; - } - - return 0; -} - -// ============================================================================ -// Thread Methods -// ============================================================================ - -OSStatus - AudioDeviceIPhone::RecordProcess(void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList *ioData) { - AudioDeviceIPhone* ptrThis = static_cast(inRefCon); - - return ptrThis->RecordProcessImpl(ioActionFlags, - inTimeStamp, - inBusNumber, - inNumberFrames); -} - - -OSStatus - AudioDeviceIPhone::RecordProcessImpl( - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - uint32_t inBusNumber, - uint32_t inNumberFrames) { - // Setup some basic stuff - // Use temp buffer not to lock up recording buffer more than necessary - // todo: Make dataTmp a member variable with static size that holds - // max possible frames? - int16_t* dataTmp = new int16_t[inNumberFrames]; - memset(dataTmp, 0, 2*inNumberFrames); - - AudioBufferList abList; - abList.mNumberBuffers = 1; - abList.mBuffers[0].mData = dataTmp; - abList.mBuffers[0].mDataByteSize = 2*inNumberFrames; // 2 bytes/sample - abList.mBuffers[0].mNumberChannels = 1; - - // Get data from mic - OSStatus res = AudioUnitRender(_auVoiceProcessing, - ioActionFlags, inTimeStamp, - inBusNumber, inNumberFrames, &abList); - if (res != 0) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Error getting rec data, error = %d", res); - - if (_recWarning > 0) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Pending rec warning exists"); - } - _recWarning = 1; - - delete [] dataTmp; - return 0; - } - - if (_recording) { - // Insert all data in temp buffer into recording buffers - // There is zero or one buffer partially full at any given time, - // all others are full or empty - // Full means filled with noSamp10ms samples. - - const unsigned int noSamp10ms = _adbSampFreq / 100; - unsigned int dataPos = 0; - uint16_t bufPos = 0; - int16_t insertPos = -1; - unsigned int nCopy = 0; // Number of samples to copy - - while (dataPos < inNumberFrames) { - // Loop over all recording buffers or - // until we find the partially full buffer - // First choice is to insert into partially full buffer, - // second choice is to insert into empty buffer - bufPos = 0; - insertPos = -1; - nCopy = 0; - while (bufPos < N_REC_BUFFERS) { - if ((_recordingLength[bufPos] > 0) - && (_recordingLength[bufPos] < noSamp10ms)) { - // Found the partially full buffer - insertPos = static_cast(bufPos); - // Don't need to search more, quit loop - bufPos = N_REC_BUFFERS; - } else if ((-1 == insertPos) - && (0 == _recordingLength[bufPos])) { - // Found an empty buffer - insertPos = static_cast(bufPos); - } - ++bufPos; - } - - // Insert data into buffer - if (insertPos > -1) { - // We found a non-full buffer, copy data to it - unsigned int dataToCopy = inNumberFrames - dataPos; - unsigned int currentRecLen = _recordingLength[insertPos]; - unsigned int roomInBuffer = noSamp10ms - currentRecLen; - nCopy = (dataToCopy < roomInBuffer ? dataToCopy : roomInBuffer); - - memcpy(&_recordingBuffer[insertPos][currentRecLen], - &dataTmp[dataPos], nCopy*sizeof(int16_t)); - if (0 == currentRecLen) { - _recordingSeqNumber[insertPos] = _recordingCurrentSeq; - ++_recordingCurrentSeq; - } - _recordingBufferTotalSize += nCopy; - // Has to be done last to avoid interrupt problems - // between threads - _recordingLength[insertPos] += nCopy; - dataPos += nCopy; - } else { - // Didn't find a non-full buffer - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Could not insert into recording buffer"); - if (_recWarning > 0) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Pending rec warning exists"); - } - _recWarning = 1; - dataPos = inNumberFrames; // Don't try to insert more - } - } - } - - delete [] dataTmp; - - return 0; -} - -OSStatus - AudioDeviceIPhone::PlayoutProcess(void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList *ioData) { - AudioDeviceIPhone* ptrThis = static_cast(inRefCon); - - return ptrThis->PlayoutProcessImpl(inNumberFrames, ioData); -} - -OSStatus - AudioDeviceIPhone::PlayoutProcessImpl(uint32_t inNumberFrames, - AudioBufferList *ioData) { - // Setup some basic stuff -// assert(sizeof(short) == 2); // Assumption for implementation - - int16_t* data = - static_cast(ioData->mBuffers[0].mData); - unsigned int dataSizeBytes = ioData->mBuffers[0].mDataByteSize; - unsigned int dataSize = dataSizeBytes/2; // Number of samples - if (dataSize != inNumberFrames) { // Should always be the same - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "dataSize (%u) != inNumberFrames (%u)", - dataSize, (unsigned int)inNumberFrames); - if (_playWarning > 0) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Pending play warning exists"); - } - _playWarning = 1; - } - memset(data, 0, dataSizeBytes); // Start with empty buffer - - - // Get playout data from Audio Device Buffer - - if (_playing) { - unsigned int noSamp10ms = _adbSampFreq / 100; - // todo: Member variable and allocate when samp freq is determined - int16_t* dataTmp = new int16_t[noSamp10ms]; - memset(dataTmp, 0, 2*noSamp10ms); - unsigned int dataPos = 0; - int noSamplesOut = 0; - unsigned int nCopy = 0; - - // First insert data from playout buffer if any - if (_playoutBufferUsed > 0) { - nCopy = (dataSize < _playoutBufferUsed) ? - dataSize : _playoutBufferUsed; - if (nCopy != _playoutBufferUsed) { - // todo: If dataSize < _playoutBufferUsed - // (should normally never be) - // we must move the remaining data - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "nCopy (%u) != _playoutBufferUsed (%u)", - nCopy, _playoutBufferUsed); - if (_playWarning > 0) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Pending play warning exists"); - } - _playWarning = 1; - } - memcpy(data, _playoutBuffer, 2*nCopy); - dataPos = nCopy; - memset(_playoutBuffer, 0, sizeof(_playoutBuffer)); - _playoutBufferUsed = 0; - } - - // Now get the rest from Audio Device Buffer - while (dataPos < dataSize) { - // Update playout delay - UpdatePlayoutDelay(); - - // Ask for new PCM data to be played out using the AudioDeviceBuffer - noSamplesOut = _ptrAudioBuffer->RequestPlayoutData(noSamp10ms); - - // Get data from Audio Device Buffer - noSamplesOut = - _ptrAudioBuffer->GetPlayoutData( - reinterpret_cast(dataTmp)); - // Cast OK since only equality comparison - if (noSamp10ms != (unsigned int)noSamplesOut) { - // Should never happen - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "noSamp10ms (%u) != noSamplesOut (%d)", - noSamp10ms, noSamplesOut); - - if (_playWarning > 0) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Pending play warning exists"); - } - _playWarning = 1; - } - - // Insert as much as fits in data buffer - nCopy = (dataSize-dataPos) > noSamp10ms ? - noSamp10ms : (dataSize-dataPos); - memcpy(&data[dataPos], dataTmp, 2*nCopy); - - // Save rest in playout buffer if any - if (nCopy < noSamp10ms) { - memcpy(_playoutBuffer, &dataTmp[nCopy], 2*(noSamp10ms-nCopy)); - _playoutBufferUsed = noSamp10ms - nCopy; - } - - // Update loop/index counter, if we copied less than noSamp10ms - // samples we shall quit loop anyway - dataPos += noSamp10ms; - } - - delete [] dataTmp; - } - - return 0; -} - -void AudioDeviceIPhone::UpdatePlayoutDelay() { - ++_playoutDelayMeasurementCounter; - - if (_playoutDelayMeasurementCounter >= 100) { - // Update HW and OS delay every second, unlikely to change - - _playoutDelay = 0; - - // HW output latency - Float32 f32(0); - UInt32 size = sizeof(f32); - OSStatus result = AudioSessionGetProperty( - kAudioSessionProperty_CurrentHardwareOutputLatency, &size, &f32); - if (0 != result) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "error HW latency (result=%d)", result); - } - _playoutDelay += static_cast(f32 * 1000000); - - // HW buffer duration - f32 = 0; - result = AudioSessionGetProperty( - kAudioSessionProperty_CurrentHardwareIOBufferDuration, &size, &f32); - if (0 != result) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "error HW buffer duration (result=%d)", result); - } - _playoutDelay += static_cast(f32 * 1000000); - - // AU latency - Float64 f64(0); - size = sizeof(f64); - result = AudioUnitGetProperty(_auVoiceProcessing, - kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, &f64, &size); - if (0 != result) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "error AU latency (result=%d)", result); - } - _playoutDelay += static_cast(f64 * 1000000); - - // To ms - _playoutDelay = (_playoutDelay - 500) / 1000; - - // Reset counter - _playoutDelayMeasurementCounter = 0; - } - - // todo: Add playout buffer? (Only used for 44.1 kHz) -} - -void AudioDeviceIPhone::UpdateRecordingDelay() { - ++_recordingDelayMeasurementCounter; - - if (_recordingDelayMeasurementCounter >= 100) { - // Update HW and OS delay every second, unlikely to change - - _recordingDelayHWAndOS = 0; - - // HW input latency - Float32 f32(0); - UInt32 size = sizeof(f32); - OSStatus result = AudioSessionGetProperty( - kAudioSessionProperty_CurrentHardwareInputLatency, &size, &f32); - if (0 != result) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "error HW latency (result=%d)", result); - } - _recordingDelayHWAndOS += static_cast(f32 * 1000000); - - // HW buffer duration - f32 = 0; - result = AudioSessionGetProperty( - kAudioSessionProperty_CurrentHardwareIOBufferDuration, &size, &f32); - if (0 != result) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "error HW buffer duration (result=%d)", result); - } - _recordingDelayHWAndOS += static_cast(f32 * 1000000); - - // AU latency - Float64 f64(0); - size = sizeof(f64); - result = AudioUnitGetProperty(_auVoiceProcessing, - kAudioUnitProperty_Latency, - kAudioUnitScope_Global, 0, &f64, &size); - if (0 != result) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "error AU latency (result=%d)", result); - } - _recordingDelayHWAndOS += static_cast(f64 * 1000000); - - // To ms - _recordingDelayHWAndOS = (_recordingDelayHWAndOS - 500) / 1000; - - // Reset counter - _recordingDelayMeasurementCounter = 0; - } - - _recordingDelay = _recordingDelayHWAndOS; - - // ADB recording buffer size, update every time - // Don't count the one next 10 ms to be sent, then convert samples => ms - const uint32_t noSamp10ms = _adbSampFreq / 100; - if (_recordingBufferTotalSize > noSamp10ms) { - _recordingDelay += - (_recordingBufferTotalSize - noSamp10ms) / (_adbSampFreq / 1000); - } -} - -bool AudioDeviceIPhone::RunCapture(void* ptrThis) { - return static_cast(ptrThis)->CaptureWorkerThread(); -} - -bool AudioDeviceIPhone::CaptureWorkerThread() { - if (_recording) { - int bufPos = 0; - unsigned int lowestSeq = 0; - int lowestSeqBufPos = 0; - bool foundBuf = true; - const unsigned int noSamp10ms = _adbSampFreq / 100; - - while (foundBuf) { - // Check if we have any buffer with data to insert - // into the Audio Device Buffer, - // and find the one with the lowest seq number - foundBuf = false; - for (bufPos = 0; bufPos < N_REC_BUFFERS; ++bufPos) { - if (noSamp10ms == _recordingLength[bufPos]) { - if (!foundBuf) { - lowestSeq = _recordingSeqNumber[bufPos]; - lowestSeqBufPos = bufPos; - foundBuf = true; - } else if (_recordingSeqNumber[bufPos] < lowestSeq) { - lowestSeq = _recordingSeqNumber[bufPos]; - lowestSeqBufPos = bufPos; - } - } - } // for - - // Insert data into the Audio Device Buffer if found any - if (foundBuf) { - // Update recording delay - UpdateRecordingDelay(); - - // Set the recorded buffer - _ptrAudioBuffer->SetRecordedBuffer( - reinterpret_cast( - _recordingBuffer[lowestSeqBufPos]), - _recordingLength[lowestSeqBufPos]); - - // Don't need to set the current mic level in ADB since we only - // support digital AGC, - // and besides we cannot get or set the IOS mic level anyway. - - // Set VQE info, use clockdrift == 0 - _ptrAudioBuffer->SetVQEData(_playoutDelay, _recordingDelay, 0); - - // Deliver recorded samples at specified sample rate, mic level - // etc. to the observer using callback - _ptrAudioBuffer->DeliverRecordedData(); - - // Make buffer available - _recordingSeqNumber[lowestSeqBufPos] = 0; - _recordingBufferTotalSize -= _recordingLength[lowestSeqBufPos]; - // Must be done last to avoid interrupt problems between threads - _recordingLength[lowestSeqBufPos] = 0; - } - } // while (foundBuf) - } // if (_recording) - - { - // Normal case - // Sleep thread (5ms) to let other threads get to work - // todo: Is 5 ms optimal? Sleep shorter if inserted into the Audio - // Device Buffer? - timespec t; - t.tv_sec = 0; - t.tv_nsec = 5*1000*1000; - nanosleep(&t, NULL); - } - - return true; -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_ios.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_ios.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_ios.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_ios.h 2015-02-03 14:33:35.000000000 +0000 @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_IPHONE_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_IPHONE_H +#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_IOS_H +#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_IOS_H #include @@ -32,246 +32,241 @@ // Number of 10 ms recording blocks in recording buffer const uint16_t N_REC_BUFFERS = 20; -class AudioDeviceIPhone : public AudioDeviceGeneric { -public: - AudioDeviceIPhone(const int32_t id); - ~AudioDeviceIPhone(); - - // Retrieve the currently utilized audio layer - virtual int32_t - ActiveAudioLayer(AudioDeviceModule::AudioLayer& audioLayer) const; - - // Main initializaton and termination - virtual int32_t Init(); - virtual int32_t Terminate(); - virtual bool Initialized() const; - - // Device enumeration - virtual int16_t PlayoutDevices(); - virtual int16_t RecordingDevices(); - virtual int32_t PlayoutDeviceName(uint16_t index, +class AudioDeviceIOS : public AudioDeviceGeneric { + public: + AudioDeviceIOS(const int32_t id); + ~AudioDeviceIOS(); + + // Retrieve the currently utilized audio layer + virtual int32_t ActiveAudioLayer( + AudioDeviceModule::AudioLayer& audioLayer) const; + + // Main initializaton and termination + virtual int32_t Init(); + virtual int32_t Terminate(); + virtual bool Initialized() const; + + // Device enumeration + virtual int16_t PlayoutDevices(); + virtual int16_t RecordingDevices(); + virtual int32_t PlayoutDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]); + virtual int32_t RecordingDeviceName(uint16_t index, char name[kAdmMaxDeviceNameSize], char guid[kAdmMaxGuidSize]); - virtual int32_t RecordingDeviceName(uint16_t index, - char name[kAdmMaxDeviceNameSize], - char guid[kAdmMaxGuidSize]); - - // Device selection - virtual int32_t SetPlayoutDevice(uint16_t index); - virtual int32_t - SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType device); - virtual int32_t SetRecordingDevice(uint16_t index); - virtual int32_t SetRecordingDevice( - AudioDeviceModule::WindowsDeviceType device); - - // Audio transport initialization - virtual int32_t PlayoutIsAvailable(bool& available); - virtual int32_t InitPlayout(); - virtual bool PlayoutIsInitialized() const; - virtual int32_t RecordingIsAvailable(bool& available); - virtual int32_t InitRecording(); - virtual bool RecordingIsInitialized() const; - - // Audio transport control - virtual int32_t StartPlayout(); - virtual int32_t StopPlayout(); - virtual bool Playing() const; - virtual int32_t StartRecording(); - virtual int32_t StopRecording(); - virtual bool Recording() const; - - // Microphone Automatic Gain Control (AGC) - virtual int32_t SetAGC(bool enable); - virtual bool AGC() const; - - // Volume control based on the Windows Wave API (Windows only) - virtual int32_t SetWaveOutVolume(uint16_t volumeLeft, uint16_t volumeRight); - virtual int32_t WaveOutVolume(uint16_t& volumeLeft, - uint16_t& volumeRight) const; - - // Audio mixer initialization - virtual int32_t SpeakerIsAvailable(bool& available); - virtual int32_t InitSpeaker(); - virtual bool SpeakerIsInitialized() const; - virtual int32_t MicrophoneIsAvailable(bool& available); - virtual int32_t InitMicrophone(); - virtual bool MicrophoneIsInitialized() const; - - // Speaker volume controls - virtual int32_t SpeakerVolumeIsAvailable(bool& available); - virtual int32_t SetSpeakerVolume(uint32_t volume); - virtual int32_t SpeakerVolume(uint32_t& volume) const; - virtual int32_t MaxSpeakerVolume(uint32_t& maxVolume) const; - virtual int32_t MinSpeakerVolume(uint32_t& minVolume) const; - virtual int32_t SpeakerVolumeStepSize(uint16_t& stepSize) const; - - // Microphone volume controls - virtual int32_t MicrophoneVolumeIsAvailable(bool& available); - virtual int32_t SetMicrophoneVolume(uint32_t volume); - virtual int32_t MicrophoneVolume(uint32_t& volume) const; - virtual int32_t MaxMicrophoneVolume(uint32_t& maxVolume) const; - virtual int32_t MinMicrophoneVolume(uint32_t& minVolume) const; - virtual int32_t - MicrophoneVolumeStepSize(uint16_t& stepSize) const; - - // Microphone mute control - virtual int32_t MicrophoneMuteIsAvailable(bool& available); - virtual int32_t SetMicrophoneMute(bool enable); - virtual int32_t MicrophoneMute(bool& enabled) const; - - // Speaker mute control - virtual int32_t SpeakerMuteIsAvailable(bool& available); - virtual int32_t SetSpeakerMute(bool enable); - virtual int32_t SpeakerMute(bool& enabled) const; - - // Microphone boost control - virtual int32_t MicrophoneBoostIsAvailable(bool& available); - virtual int32_t SetMicrophoneBoost(bool enable); - virtual int32_t MicrophoneBoost(bool& enabled) const; - - // Stereo support - virtual int32_t StereoPlayoutIsAvailable(bool& available); - virtual int32_t SetStereoPlayout(bool enable); - virtual int32_t StereoPlayout(bool& enabled) const; - virtual int32_t StereoRecordingIsAvailable(bool& available); - virtual int32_t SetStereoRecording(bool enable); - virtual int32_t StereoRecording(bool& enabled) const; - - // Delay information and control - virtual int32_t - SetPlayoutBuffer(const AudioDeviceModule::BufferType type, - uint16_t sizeMS); - virtual int32_t PlayoutBuffer(AudioDeviceModule::BufferType& type, - uint16_t& sizeMS) const; - virtual int32_t PlayoutDelay(uint16_t& delayMS) const; - virtual int32_t RecordingDelay(uint16_t& delayMS) const; - - // CPU load - virtual int32_t CPULoad(uint16_t& load) const; - -public: - virtual bool PlayoutWarning() const; - virtual bool PlayoutError() const; - virtual bool RecordingWarning() const; - virtual bool RecordingError() const; - virtual void ClearPlayoutWarning(); - virtual void ClearPlayoutError(); - virtual void ClearRecordingWarning(); - virtual void ClearRecordingError(); - -public: - virtual void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer); - - // Reset Audio Deivce (for mobile devices only) - virtual int32_t ResetAudioDevice(); - - // enable or disable loud speaker (for iphone only) - virtual int32_t SetLoudspeakerStatus(bool enable); - virtual int32_t GetLoudspeakerStatus(bool& enabled) const; - -private: - void Lock() { - _critSect.Enter(); - } - - void UnLock() { - _critSect.Leave(); - } - - int32_t Id() { - return _id; - } - - // Init and shutdown - int32_t InitPlayOrRecord(); - int32_t ShutdownPlayOrRecord(); - - void UpdateRecordingDelay(); - void UpdatePlayoutDelay(); - - static OSStatus RecordProcess(void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *timeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList *ioData); - - static OSStatus PlayoutProcess(void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *timeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList *ioData); - - OSStatus RecordProcessImpl(AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *timeStamp, - uint32_t inBusNumber, - uint32_t inNumberFrames); - OSStatus PlayoutProcessImpl(uint32_t inNumberFrames, + // Device selection + virtual int32_t SetPlayoutDevice(uint16_t index); + virtual int32_t SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType device); + virtual int32_t SetRecordingDevice(uint16_t index); + virtual int32_t SetRecordingDevice( + AudioDeviceModule::WindowsDeviceType device); + + // Audio transport initialization + virtual int32_t PlayoutIsAvailable(bool& available); + virtual int32_t InitPlayout(); + virtual bool PlayoutIsInitialized() const; + virtual int32_t RecordingIsAvailable(bool& available); + virtual int32_t InitRecording(); + virtual bool RecordingIsInitialized() const; + + // Audio transport control + virtual int32_t StartPlayout(); + virtual int32_t StopPlayout(); + virtual bool Playing() const; + virtual int32_t StartRecording(); + virtual int32_t StopRecording(); + virtual bool Recording() const; + + // Microphone Automatic Gain Control (AGC) + virtual int32_t SetAGC(bool enable); + virtual bool AGC() const; + + // Volume control based on the Windows Wave API (Windows only) + virtual int32_t SetWaveOutVolume(uint16_t volumeLeft, uint16_t volumeRight); + virtual int32_t WaveOutVolume(uint16_t& volumeLeft, + uint16_t& volumeRight) const; + + // Audio mixer initialization + virtual int32_t InitSpeaker(); + virtual bool SpeakerIsInitialized() const; + virtual int32_t InitMicrophone(); + virtual bool MicrophoneIsInitialized() const; + + // Speaker volume controls + virtual int32_t SpeakerVolumeIsAvailable(bool& available); + virtual int32_t SetSpeakerVolume(uint32_t volume); + virtual int32_t SpeakerVolume(uint32_t& volume) const; + virtual int32_t MaxSpeakerVolume(uint32_t& maxVolume) const; + virtual int32_t MinSpeakerVolume(uint32_t& minVolume) const; + virtual int32_t SpeakerVolumeStepSize(uint16_t& stepSize) const; + + // Microphone volume controls + virtual int32_t MicrophoneVolumeIsAvailable(bool& available); + virtual int32_t SetMicrophoneVolume(uint32_t volume); + virtual int32_t MicrophoneVolume(uint32_t& volume) const; + virtual int32_t MaxMicrophoneVolume(uint32_t& maxVolume) const; + virtual int32_t MinMicrophoneVolume(uint32_t& minVolume) const; + virtual int32_t MicrophoneVolumeStepSize(uint16_t& stepSize) const; + + // Microphone mute control + virtual int32_t MicrophoneMuteIsAvailable(bool& available); + virtual int32_t SetMicrophoneMute(bool enable); + virtual int32_t MicrophoneMute(bool& enabled) const; + + // Speaker mute control + virtual int32_t SpeakerMuteIsAvailable(bool& available); + virtual int32_t SetSpeakerMute(bool enable); + virtual int32_t SpeakerMute(bool& enabled) const; + + // Microphone boost control + virtual int32_t MicrophoneBoostIsAvailable(bool& available); + virtual int32_t SetMicrophoneBoost(bool enable); + virtual int32_t MicrophoneBoost(bool& enabled) const; + + // Stereo support + virtual int32_t StereoPlayoutIsAvailable(bool& available); + virtual int32_t SetStereoPlayout(bool enable); + virtual int32_t StereoPlayout(bool& enabled) const; + virtual int32_t StereoRecordingIsAvailable(bool& available); + virtual int32_t SetStereoRecording(bool enable); + virtual int32_t StereoRecording(bool& enabled) const; + + // Delay information and control + virtual int32_t SetPlayoutBuffer(const AudioDeviceModule::BufferType type, + uint16_t sizeMS); + virtual int32_t PlayoutBuffer(AudioDeviceModule::BufferType& type, + uint16_t& sizeMS) const; + virtual int32_t PlayoutDelay(uint16_t& delayMS) const; + virtual int32_t RecordingDelay(uint16_t& delayMS) const; + + // CPU load + virtual int32_t CPULoad(uint16_t& load) const; + + public: + virtual bool PlayoutWarning() const; + virtual bool PlayoutError() const; + virtual bool RecordingWarning() const; + virtual bool RecordingError() const; + virtual void ClearPlayoutWarning(); + virtual void ClearPlayoutError(); + virtual void ClearRecordingWarning(); + virtual void ClearRecordingError(); + + public: + virtual void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer); + + // Reset Audio Device (for mobile devices only) + virtual int32_t ResetAudioDevice(); + + // enable or disable loud speaker (for iphone only) + virtual int32_t SetLoudspeakerStatus(bool enable); + virtual int32_t GetLoudspeakerStatus(bool& enabled) const; + + private: + void Lock() { + _critSect.Enter(); + } + + void UnLock() { + _critSect.Leave(); + } + + int32_t Id() { + return _id; + } + + // Init and shutdown + int32_t InitPlayOrRecord(); + int32_t ShutdownPlayOrRecord(); + + void UpdateRecordingDelay(); + void UpdatePlayoutDelay(); + + static OSStatus RecordProcess(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *timeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, AudioBufferList *ioData); - static bool RunCapture(void* ptrThis); - bool CaptureWorkerThread(); + static OSStatus PlayoutProcess(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *timeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData); + + OSStatus RecordProcessImpl(AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *timeStamp, + uint32_t inBusNumber, + uint32_t inNumberFrames); + + OSStatus PlayoutProcessImpl(uint32_t inNumberFrames, + AudioBufferList *ioData); + + static bool RunCapture(void* ptrThis); + bool CaptureWorkerThread(); + + private: + AudioDeviceBuffer* _ptrAudioBuffer; + + CriticalSectionWrapper& _critSect; + + ThreadWrapper* _captureWorkerThread; + uint32_t _captureWorkerThreadId; + + int32_t _id; + + AudioUnit _auVoiceProcessing; + void* _audioInterruptionObserver; + + private: + bool _initialized; + bool _isShutDown; + bool _recording; + bool _playing; + bool _recIsInitialized; + bool _playIsInitialized; + + bool _recordingDeviceIsSpecified; + bool _playoutDeviceIsSpecified; + bool _micIsInitialized; + bool _speakerIsInitialized; + + bool _AGC; + + // The sampling rate to use with Audio Device Buffer + uint32_t _adbSampFreq; + + // Delay calculation + uint32_t _recordingDelay; + uint32_t _playoutDelay; + uint32_t _playoutDelayMeasurementCounter; + uint32_t _recordingDelayHWAndOS; + uint32_t _recordingDelayMeasurementCounter; + + // Errors and warnings count + uint16_t _playWarning; + uint16_t _playError; + uint16_t _recWarning; + uint16_t _recError; + + // Playout buffer, needed for 44.0 / 44.1 kHz mismatch + int16_t _playoutBuffer[ENGINE_PLAY_BUF_SIZE_IN_SAMPLES]; + uint32_t _playoutBufferUsed; // How much is filled + + // Recording buffers + int16_t _recordingBuffer[N_REC_BUFFERS][ENGINE_REC_BUF_SIZE_IN_SAMPLES]; + uint32_t _recordingLength[N_REC_BUFFERS]; + uint32_t _recordingSeqNumber[N_REC_BUFFERS]; + uint32_t _recordingCurrentSeq; -private: - AudioDeviceBuffer* _ptrAudioBuffer; - - CriticalSectionWrapper& _critSect; - - ThreadWrapper* _captureWorkerThread; - uint32_t _captureWorkerThreadId; - - int32_t _id; - - AudioUnit _auVoiceProcessing; - -private: - bool _initialized; - bool _isShutDown; - bool _recording; - bool _playing; - bool _recIsInitialized; - bool _playIsInitialized; - - bool _recordingDeviceIsSpecified; - bool _playoutDeviceIsSpecified; - bool _micIsInitialized; - bool _speakerIsInitialized; - - bool _AGC; - - // The sampling rate to use with Audio Device Buffer - uint32_t _adbSampFreq; - - // Delay calculation - uint32_t _recordingDelay; - uint32_t _playoutDelay; - uint32_t _playoutDelayMeasurementCounter; - uint32_t _recordingDelayHWAndOS; - uint32_t _recordingDelayMeasurementCounter; - - // Errors and warnings count - uint16_t _playWarning; - uint16_t _playError; - uint16_t _recWarning; - uint16_t _recError; - - // Playout buffer, needed for 44.0 / 44.1 kHz mismatch - int16_t _playoutBuffer[ENGINE_PLAY_BUF_SIZE_IN_SAMPLES]; - uint32_t _playoutBufferUsed; // How much is filled - - // Recording buffers - int16_t - _recordingBuffer[N_REC_BUFFERS][ENGINE_REC_BUF_SIZE_IN_SAMPLES]; - uint32_t _recordingLength[N_REC_BUFFERS]; - uint32_t _recordingSeqNumber[N_REC_BUFFERS]; - uint32_t _recordingCurrentSeq; - - // Current total size all data in buffers, used for delay estimate - uint32_t _recordingBufferTotalSize; + // Current total size all data in buffers, used for delay estimate + uint32_t _recordingBufferTotalSize; }; } // namespace webrtc -#endif // MODULES_AUDIO_DEVICE_MAIN_SOURCE_MAC_AUDIO_DEVICE_IPHONE_H_ +#endif // WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_IOS_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_ios.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_ios.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_ios.mm 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_ios.mm 2015-02-03 14:33:35.000000000 +0000 @@ -0,0 +1,1919 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import +#import + +#include "webrtc/modules/audio_device/ios/audio_device_ios.h" + +#include "webrtc/system_wrappers/interface/thread_wrapper.h" +#include "webrtc/system_wrappers/interface/trace.h" + +namespace webrtc { +AudioDeviceIOS::AudioDeviceIOS(const int32_t id) + : + _ptrAudioBuffer(NULL), + _critSect(*CriticalSectionWrapper::CreateCriticalSection()), + _captureWorkerThread(NULL), + _captureWorkerThreadId(0), + _id(id), + _auVoiceProcessing(NULL), + _audioInterruptionObserver(NULL), + _initialized(false), + _isShutDown(false), + _recording(false), + _playing(false), + _recIsInitialized(false), + _playIsInitialized(false), + _recordingDeviceIsSpecified(false), + _playoutDeviceIsSpecified(false), + _micIsInitialized(false), + _speakerIsInitialized(false), + _AGC(false), + _adbSampFreq(0), + _recordingDelay(0), + _playoutDelay(0), + _playoutDelayMeasurementCounter(9999), + _recordingDelayHWAndOS(0), + _recordingDelayMeasurementCounter(9999), + _playWarning(0), + _playError(0), + _recWarning(0), + _recError(0), + _playoutBufferUsed(0), + _recordingCurrentSeq(0), + _recordingBufferTotalSize(0) { + WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, + "%s created", __FUNCTION__); + + memset(_playoutBuffer, 0, sizeof(_playoutBuffer)); + memset(_recordingBuffer, 0, sizeof(_recordingBuffer)); + memset(_recordingLength, 0, sizeof(_recordingLength)); + memset(_recordingSeqNumber, 0, sizeof(_recordingSeqNumber)); +} + +AudioDeviceIOS::~AudioDeviceIOS() { + WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, + "%s destroyed", __FUNCTION__); + + Terminate(); + + delete &_critSect; +} + + +// ============================================================================ +// API +// ============================================================================ + +void AudioDeviceIOS::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + CriticalSectionScoped lock(&_critSect); + + _ptrAudioBuffer = audioBuffer; + + // inform the AudioBuffer about default settings for this implementation + _ptrAudioBuffer->SetRecordingSampleRate(ENGINE_REC_BUF_SIZE_IN_SAMPLES); + _ptrAudioBuffer->SetPlayoutSampleRate(ENGINE_PLAY_BUF_SIZE_IN_SAMPLES); + _ptrAudioBuffer->SetRecordingChannels(N_REC_CHANNELS); + _ptrAudioBuffer->SetPlayoutChannels(N_PLAY_CHANNELS); +} + +int32_t AudioDeviceIOS::ActiveAudioLayer( + AudioDeviceModule::AudioLayer& audioLayer) const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + audioLayer = AudioDeviceModule::kPlatformDefaultAudio; + return 0; +} + +int32_t AudioDeviceIOS::Init() { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + CriticalSectionScoped lock(&_critSect); + + if (_initialized) { + return 0; + } + + _isShutDown = false; + + // Create and start capture thread + if (_captureWorkerThread == NULL) { + _captureWorkerThread + = ThreadWrapper::CreateThread(RunCapture, this, kRealtimePriority, + "CaptureWorkerThread"); + + if (_captureWorkerThread == NULL) { + WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, + _id, "CreateThread() error"); + return -1; + } + + unsigned int threadID(0); + bool res = _captureWorkerThread->Start(threadID); + _captureWorkerThreadId = static_cast(threadID); + WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, + _id, "CaptureWorkerThread started (res=%d)", res); + } else { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, + _id, "Thread already created"); + } + _playWarning = 0; + _playError = 0; + _recWarning = 0; + _recError = 0; + + _initialized = true; + + return 0; +} + +int32_t AudioDeviceIOS::Terminate() { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + if (!_initialized) { + return 0; + } + + + // Stop capture thread + if (_captureWorkerThread != NULL) { + WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, + _id, "Stopping CaptureWorkerThread"); + bool res = _captureWorkerThread->Stop(); + WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, + _id, "CaptureWorkerThread stopped (res=%d)", res); + delete _captureWorkerThread; + _captureWorkerThread = NULL; + } + + // Shut down Audio Unit + ShutdownPlayOrRecord(); + + _isShutDown = true; + _initialized = false; + _speakerIsInitialized = false; + _micIsInitialized = false; + _playoutDeviceIsSpecified = false; + _recordingDeviceIsSpecified = false; + return 0; +} + +bool AudioDeviceIOS::Initialized() const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + return (_initialized); +} + +int32_t AudioDeviceIOS::InitSpeaker() { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + CriticalSectionScoped lock(&_critSect); + + if (!_initialized) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, + _id, " Not initialized"); + return -1; + } + + if (_playing) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, + _id, " Cannot init speaker when playing"); + return -1; + } + + if (!_playoutDeviceIsSpecified) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, + _id, " Playout device is not specified"); + return -1; + } + + // Do nothing + _speakerIsInitialized = true; + + return 0; +} + +int32_t AudioDeviceIOS::InitMicrophone() { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + CriticalSectionScoped lock(&_critSect); + + if (!_initialized) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, + _id, " Not initialized"); + return -1; + } + + if (_recording) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, + _id, " Cannot init mic when recording"); + return -1; + } + + if (!_recordingDeviceIsSpecified) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, + _id, " Recording device is not specified"); + return -1; + } + + // Do nothing + + _micIsInitialized = true; + + return 0; +} + +bool AudioDeviceIOS::SpeakerIsInitialized() const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + return _speakerIsInitialized; +} + +bool AudioDeviceIOS::MicrophoneIsInitialized() const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + return _micIsInitialized; +} + +int32_t AudioDeviceIOS::SpeakerVolumeIsAvailable(bool& available) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + available = false; // Speaker volume not supported on iOS + + return 0; +} + +int32_t AudioDeviceIOS::SetSpeakerVolume(uint32_t volume) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "AudioDeviceIOS::SetSpeakerVolume(volume=%u)", volume); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + return -1; +} + +int32_t AudioDeviceIOS::SpeakerVolume(uint32_t& volume) const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + return -1; +} + +int32_t + AudioDeviceIOS::SetWaveOutVolume(uint16_t volumeLeft, + uint16_t volumeRight) { + WEBRTC_TRACE( + kTraceModuleCall, + kTraceAudioDevice, + _id, + "AudioDeviceIOS::SetWaveOutVolume(volumeLeft=%u, volumeRight=%u)", + volumeLeft, volumeRight); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + + return -1; +} + +int32_t +AudioDeviceIOS::WaveOutVolume(uint16_t& /*volumeLeft*/, + uint16_t& /*volumeRight*/) const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + return -1; +} + +int32_t + AudioDeviceIOS::MaxSpeakerVolume(uint32_t& maxVolume) const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + return -1; +} + +int32_t AudioDeviceIOS::MinSpeakerVolume( + uint32_t& minVolume) const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + return -1; +} + +int32_t + AudioDeviceIOS::SpeakerVolumeStepSize(uint16_t& stepSize) const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + return -1; +} + +int32_t AudioDeviceIOS::SpeakerMuteIsAvailable(bool& available) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + available = false; // Speaker mute not supported on iOS + + return 0; +} + +int32_t AudioDeviceIOS::SetSpeakerMute(bool enable) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + return -1; +} + +int32_t AudioDeviceIOS::SpeakerMute(bool& enabled) const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + return -1; +} + +int32_t AudioDeviceIOS::MicrophoneMuteIsAvailable(bool& available) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + available = false; // Mic mute not supported on iOS + + return 0; +} + +int32_t AudioDeviceIOS::SetMicrophoneMute(bool enable) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + return -1; +} + +int32_t AudioDeviceIOS::MicrophoneMute(bool& enabled) const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + return -1; +} + +int32_t AudioDeviceIOS::MicrophoneBoostIsAvailable(bool& available) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + available = false; // Mic boost not supported on iOS + + return 0; +} + +int32_t AudioDeviceIOS::SetMicrophoneBoost(bool enable) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "AudioDeviceIOS::SetMicrophoneBoost(enable=%u)", enable); + + if (!_micIsInitialized) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Microphone not initialized"); + return -1; + } + + if (enable) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " SetMicrophoneBoost cannot be enabled on this platform"); + return -1; + } + + return 0; +} + +int32_t AudioDeviceIOS::MicrophoneBoost(bool& enabled) const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + if (!_micIsInitialized) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Microphone not initialized"); + return -1; + } + + enabled = false; + + return 0; +} + +int32_t AudioDeviceIOS::StereoRecordingIsAvailable(bool& available) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + available = false; // Stereo recording not supported on iOS + + return 0; +} + +int32_t AudioDeviceIOS::SetStereoRecording(bool enable) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "AudioDeviceIOS::SetStereoRecording(enable=%u)", enable); + + if (enable) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " Stereo recording is not supported on this platform"); + return -1; + } + return 0; +} + +int32_t AudioDeviceIOS::StereoRecording(bool& enabled) const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + enabled = false; + return 0; +} + +int32_t AudioDeviceIOS::StereoPlayoutIsAvailable(bool& available) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + available = false; // Stereo playout not supported on iOS + + return 0; +} + +int32_t AudioDeviceIOS::SetStereoPlayout(bool enable) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "AudioDeviceIOS::SetStereoPlayout(enable=%u)", enable); + + if (enable) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " Stereo playout is not supported on this platform"); + return -1; + } + return 0; +} + +int32_t AudioDeviceIOS::StereoPlayout(bool& enabled) const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + enabled = false; + return 0; +} + +int32_t AudioDeviceIOS::SetAGC(bool enable) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "AudioDeviceIOS::SetAGC(enable=%d)", enable); + + _AGC = enable; + + return 0; +} + +bool AudioDeviceIOS::AGC() const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + return _AGC; +} + +int32_t AudioDeviceIOS::MicrophoneVolumeIsAvailable(bool& available) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + available = false; // Mic volume not supported on IOS + + return 0; +} + +int32_t AudioDeviceIOS::SetMicrophoneVolume(uint32_t volume) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "AudioDeviceIOS::SetMicrophoneVolume(volume=%u)", volume); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + return -1; +} + +int32_t + AudioDeviceIOS::MicrophoneVolume(uint32_t& volume) const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + return -1; +} + +int32_t + AudioDeviceIOS::MaxMicrophoneVolume(uint32_t& maxVolume) const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + return -1; +} + +int32_t + AudioDeviceIOS::MinMicrophoneVolume(uint32_t& minVolume) const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + return -1; +} + +int32_t + AudioDeviceIOS::MicrophoneVolumeStepSize( + uint16_t& stepSize) const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + return -1; +} + +int16_t AudioDeviceIOS::PlayoutDevices() { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + + return (int16_t)1; +} + +int32_t AudioDeviceIOS::SetPlayoutDevice(uint16_t index) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "AudioDeviceIOS::SetPlayoutDevice(index=%u)", index); + + if (_playIsInitialized) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Playout already initialized"); + return -1; + } + + if (index !=0) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " SetPlayoutDevice invalid index"); + return -1; + } + _playoutDeviceIsSpecified = true; + + return 0; +} + +int32_t + AudioDeviceIOS::SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + "WindowsDeviceType not supported"); + return -1; +} + +int32_t + AudioDeviceIOS::PlayoutDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "AudioDeviceIOS::PlayoutDeviceName(index=%u)", index); + + if (index != 0) { + return -1; + } + // return empty strings + memset(name, 0, kAdmMaxDeviceNameSize); + if (guid != NULL) { + memset(guid, 0, kAdmMaxGuidSize); + } + + return 0; +} + +int32_t + AudioDeviceIOS::RecordingDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "AudioDeviceIOS::RecordingDeviceName(index=%u)", index); + + if (index != 0) { + return -1; + } + // return empty strings + memset(name, 0, kAdmMaxDeviceNameSize); + if (guid != NULL) { + memset(guid, 0, kAdmMaxGuidSize); + } + + return 0; +} + +int16_t AudioDeviceIOS::RecordingDevices() { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); + + return (int16_t)1; +} + +int32_t AudioDeviceIOS::SetRecordingDevice(uint16_t index) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "AudioDeviceIOS::SetRecordingDevice(index=%u)", index); + + if (_recIsInitialized) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Recording already initialized"); + return -1; + } + + if (index !=0) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " SetRecordingDevice invalid index"); + return -1; + } + + _recordingDeviceIsSpecified = true; + + return 0; +} + +int32_t + AudioDeviceIOS::SetRecordingDevice( + AudioDeviceModule::WindowsDeviceType) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + "WindowsDeviceType not supported"); + return -1; +} + +// ---------------------------------------------------------------------------- +// SetLoudspeakerStatus +// +// Change the default receiver playout route to speaker. +// +// ---------------------------------------------------------------------------- + +int32_t AudioDeviceIOS::SetLoudspeakerStatus(bool enable) { + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + "AudioDeviceIOS::SetLoudspeakerStatus(enable=%d)", enable); + + AVAudioSession* session = [AVAudioSession sharedInstance]; + NSString* category = session.category; + AVAudioSessionCategoryOptions options = session.categoryOptions; + // Respect old category options if category is + // AVAudioSessionCategoryPlayAndRecord. Otherwise reset it since old options + // might not be valid for this category. + if ([category isEqualToString:AVAudioSessionCategoryPlayAndRecord]) { + if (enable) { + options |= AVAudioSessionCategoryOptionDefaultToSpeaker; + } else { + options &= ~AVAudioSessionCategoryOptionDefaultToSpeaker; + } + } else { + options = AVAudioSessionCategoryOptionDefaultToSpeaker; + } + + NSError* error = nil; + [session setCategory:AVAudioSessionCategoryPlayAndRecord + withOptions:options + error:&error]; + if (error != nil) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + "Error changing default output route "); + return -1; + } + + return 0; +} + +int32_t AudioDeviceIOS::GetLoudspeakerStatus(bool &enabled) const { + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + "AudioDeviceIOS::SetLoudspeakerStatus(enabled=?)"); + + AVAudioSession* session = [AVAudioSession sharedInstance]; + AVAudioSessionCategoryOptions options = session.categoryOptions; + enabled = options & AVAudioSessionCategoryOptionDefaultToSpeaker; + + return 0; +} + +int32_t AudioDeviceIOS::PlayoutIsAvailable(bool& available) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); + + available = false; + + // Try to initialize the playout side + int32_t res = InitPlayout(); + + // Cancel effect of initialization + StopPlayout(); + + if (res != -1) { + available = true; + } + + return 0; +} + +int32_t AudioDeviceIOS::RecordingIsAvailable(bool& available) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); + + available = false; + + // Try to initialize the recording side + int32_t res = InitRecording(); + + // Cancel effect of initialization + StopRecording(); + + if (res != -1) { + available = true; + } + + return 0; +} + +int32_t AudioDeviceIOS::InitPlayout() { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); + + CriticalSectionScoped lock(&_critSect); + + if (!_initialized) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, " Not initialized"); + return -1; + } + + if (_playing) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " Playout already started"); + return -1; + } + + if (_playIsInitialized) { + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + " Playout already initialized"); + return 0; + } + + if (!_playoutDeviceIsSpecified) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Playout device is not specified"); + return -1; + } + + // Initialize the speaker + if (InitSpeaker() == -1) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " InitSpeaker() failed"); + } + + _playIsInitialized = true; + + if (!_recIsInitialized) { + // Audio init + if (InitPlayOrRecord() == -1) { + // todo: Handle error + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " InitPlayOrRecord() failed"); + } + } else { + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + " Recording already initialized - InitPlayOrRecord() not called"); + } + + return 0; +} + +bool AudioDeviceIOS::PlayoutIsInitialized() const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); + return (_playIsInitialized); +} + +int32_t AudioDeviceIOS::InitRecording() { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); + + CriticalSectionScoped lock(&_critSect); + + if (!_initialized) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Not initialized"); + return -1; + } + + if (_recording) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " Recording already started"); + return -1; + } + + if (_recIsInitialized) { + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + " Recording already initialized"); + return 0; + } + + if (!_recordingDeviceIsSpecified) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Recording device is not specified"); + return -1; + } + + // Initialize the microphone + if (InitMicrophone() == -1) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " InitMicrophone() failed"); + } + + _recIsInitialized = true; + + if (!_playIsInitialized) { + // Audio init + if (InitPlayOrRecord() == -1) { + // todo: Handle error + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " InitPlayOrRecord() failed"); + } + } else { + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + " Playout already initialized - InitPlayOrRecord() " \ + "not called"); + } + + return 0; +} + +bool AudioDeviceIOS::RecordingIsInitialized() const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); + return (_recIsInitialized); +} + +int32_t AudioDeviceIOS::StartRecording() { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); + + CriticalSectionScoped lock(&_critSect); + + if (!_recIsInitialized) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Recording not initialized"); + return -1; + } + + if (_recording) { + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + " Recording already started"); + return 0; + } + + // Reset recording buffer + memset(_recordingBuffer, 0, sizeof(_recordingBuffer)); + memset(_recordingLength, 0, sizeof(_recordingLength)); + memset(_recordingSeqNumber, 0, sizeof(_recordingSeqNumber)); + _recordingCurrentSeq = 0; + _recordingBufferTotalSize = 0; + _recordingDelay = 0; + _recordingDelayHWAndOS = 0; + // Make sure first call to update delay function will update delay + _recordingDelayMeasurementCounter = 9999; + _recWarning = 0; + _recError = 0; + + if (!_playing) { + // Start Audio Unit + WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, + " Starting Audio Unit"); + OSStatus result = AudioOutputUnitStart(_auVoiceProcessing); + if (0 != result) { + WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, + " Error starting Audio Unit (result=%d)", result); + return -1; + } + } + + _recording = true; + + return 0; +} + +int32_t AudioDeviceIOS::StopRecording() { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); + + CriticalSectionScoped lock(&_critSect); + + if (!_recIsInitialized) { + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + " Recording is not initialized"); + return 0; + } + + _recording = false; + + if (!_playing) { + // Both playout and recording has stopped, shutdown the device + ShutdownPlayOrRecord(); + } + + _recIsInitialized = false; + _micIsInitialized = false; + + return 0; +} + +bool AudioDeviceIOS::Recording() const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); + return (_recording); +} + +int32_t AudioDeviceIOS::StartPlayout() { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); + + // This lock is (among other things) needed to avoid concurrency issues + // with capture thread + // shutting down Audio Unit + CriticalSectionScoped lock(&_critSect); + + if (!_playIsInitialized) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Playout not initialized"); + return -1; + } + + if (_playing) { + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + " Playing already started"); + return 0; + } + + // Reset playout buffer + memset(_playoutBuffer, 0, sizeof(_playoutBuffer)); + _playoutBufferUsed = 0; + _playoutDelay = 0; + // Make sure first call to update delay function will update delay + _playoutDelayMeasurementCounter = 9999; + _playWarning = 0; + _playError = 0; + + if (!_recording) { + // Start Audio Unit + WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, + " Starting Audio Unit"); + OSStatus result = AudioOutputUnitStart(_auVoiceProcessing); + if (0 != result) { + WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, + " Error starting Audio Unit (result=%d)", result); + return -1; + } + } + + _playing = true; + + return 0; +} + +int32_t AudioDeviceIOS::StopPlayout() { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); + + CriticalSectionScoped lock(&_critSect); + + if (!_playIsInitialized) { + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + " Playout is not initialized"); + return 0; + } + + _playing = false; + + if (!_recording) { + // Both playout and recording has stopped, signal shutdown the device + ShutdownPlayOrRecord(); + } + + _playIsInitialized = false; + _speakerIsInitialized = false; + + return 0; +} + +bool AudioDeviceIOS::Playing() const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "%s", __FUNCTION__); + return (_playing); +} + +// ---------------------------------------------------------------------------- +// ResetAudioDevice +// +// Disable playout and recording, signal to capture thread to shutdown, +// and set enable states after shutdown to same as current. +// In capture thread audio device will be shutdown, then started again. +// ---------------------------------------------------------------------------- +int32_t AudioDeviceIOS::ResetAudioDevice() { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); + + CriticalSectionScoped lock(&_critSect); + + if (!_playIsInitialized && !_recIsInitialized) { + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + " Playout or recording not initialized, doing nothing"); + return 0; // Nothing to reset + } + + // Store the states we have before stopping to restart below + bool initPlay = _playIsInitialized; + bool play = _playing; + bool initRec = _recIsInitialized; + bool rec = _recording; + + int res(0); + + // Stop playout and recording + WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, + " Stopping playout and recording"); + res += StopPlayout(); + res += StopRecording(); + + // Restart + WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, + " Restarting playout and recording (%d, %d, %d, %d)", + initPlay, play, initRec, rec); + if (initPlay) res += InitPlayout(); + if (initRec) res += InitRecording(); + if (play) res += StartPlayout(); + if (rec) res += StartRecording(); + + if (0 != res) { + // Logging is done in init/start/stop calls above + return -1; + } + + return 0; +} + +int32_t AudioDeviceIOS::PlayoutDelay(uint16_t& delayMS) const { + delayMS = _playoutDelay; + return 0; +} + +int32_t AudioDeviceIOS::RecordingDelay(uint16_t& delayMS) const { + delayMS = _recordingDelay; + return 0; +} + +int32_t + AudioDeviceIOS::SetPlayoutBuffer(const AudioDeviceModule::BufferType type, + uint16_t sizeMS) { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, + "AudioDeviceIOS::SetPlayoutBuffer(type=%u, sizeMS=%u)", + type, sizeMS); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + return -1; +} + +int32_t + AudioDeviceIOS::PlayoutBuffer(AudioDeviceModule::BufferType& type, + uint16_t& sizeMS) const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); + + type = AudioDeviceModule::kAdaptiveBufferSize; + + sizeMS = _playoutDelay; + + return 0; +} + +int32_t AudioDeviceIOS::CPULoad(uint16_t& /*load*/) const { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); + + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " API call not supported on this platform"); + return -1; +} + +bool AudioDeviceIOS::PlayoutWarning() const { + return (_playWarning > 0); +} + +bool AudioDeviceIOS::PlayoutError() const { + return (_playError > 0); +} + +bool AudioDeviceIOS::RecordingWarning() const { + return (_recWarning > 0); +} + +bool AudioDeviceIOS::RecordingError() const { + return (_recError > 0); +} + +void AudioDeviceIOS::ClearPlayoutWarning() { + _playWarning = 0; +} + +void AudioDeviceIOS::ClearPlayoutError() { + _playError = 0; +} + +void AudioDeviceIOS::ClearRecordingWarning() { + _recWarning = 0; +} + +void AudioDeviceIOS::ClearRecordingError() { + _recError = 0; +} + +// ============================================================================ +// Private Methods +// ============================================================================ + +int32_t AudioDeviceIOS::InitPlayOrRecord() { + WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); + + OSStatus result = -1; + + // Check if already initialized + if (NULL != _auVoiceProcessing) { + // We already have initialized before and created any of the audio unit, + // check that all exist + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + " Already initialized"); + // todo: Call AudioUnitReset() here and empty all buffers? + return 0; + } + + // Create Voice Processing Audio Unit + AudioComponentDescription desc; + AudioComponent comp; + + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + comp = AudioComponentFindNext(NULL, &desc); + if (NULL == comp) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Could not find audio component for Audio Unit"); + return -1; + } + + result = AudioComponentInstanceNew(comp, &_auVoiceProcessing); + if (0 != result) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Could not create Audio Unit instance (result=%d)", + result); + return -1; + } + + // Set preferred hardware sample rate to 16 kHz + NSError* error = nil; + AVAudioSession* session = [AVAudioSession sharedInstance]; + Float64 preferredSampleRate(16000.0); + [session setPreferredSampleRate:preferredSampleRate + error:&error]; + if (error != nil) { + const char* errorString = [[error localizedDescription] UTF8String]; + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + "Could not set preferred sample rate: %s", errorString); + } + error = nil; + [session setMode:AVAudioSessionModeVoiceChat + error:&error]; + if (error != nil) { + const char* errorString = [[error localizedDescription] UTF8String]; + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + "Could not set mode: %s", errorString); + } + error = nil; + [session setCategory:AVAudioSessionCategoryPlayAndRecord + error:&error]; + if (error != nil) { + const char* errorString = [[error localizedDescription] UTF8String]; + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + "Could not set category: %s", errorString); + } + + ////////////////////// + // Setup Voice Processing Audio Unit + + // Note: For Signal Processing AU element 0 is output bus, element 1 is + // input bus for global scope element is irrelevant (always use + // element 0) + + // Enable IO on both elements + + // todo: Below we just log and continue upon error. We might want + // to close AU and return error for some cases. + // todo: Log info about setup. + + UInt32 enableIO = 1; + result = AudioUnitSetProperty(_auVoiceProcessing, + kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Input, + 1, // input bus + &enableIO, + sizeof(enableIO)); + if (0 != result) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Could not enable IO on input (result=%d)", result); + } + + result = AudioUnitSetProperty(_auVoiceProcessing, + kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Output, + 0, // output bus + &enableIO, + sizeof(enableIO)); + if (0 != result) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Could not enable IO on output (result=%d)", result); + } + + // Disable AU buffer allocation for the recorder, we allocate our own + UInt32 flag = 0; + result = AudioUnitSetProperty( + _auVoiceProcessing, kAudioUnitProperty_ShouldAllocateBuffer, + kAudioUnitScope_Output, 1, &flag, sizeof(flag)); + if (0 != result) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " Could not disable AU buffer allocation (result=%d)", + result); + // Should work anyway + } + + // Set recording callback + AURenderCallbackStruct auCbS; + memset(&auCbS, 0, sizeof(auCbS)); + auCbS.inputProc = RecordProcess; + auCbS.inputProcRefCon = this; + result = AudioUnitSetProperty(_auVoiceProcessing, + kAudioOutputUnitProperty_SetInputCallback, + kAudioUnitScope_Global, 1, + &auCbS, sizeof(auCbS)); + if (0 != result) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Could not set record callback for Audio Unit (result=%d)", + result); + } + + // Set playout callback + memset(&auCbS, 0, sizeof(auCbS)); + auCbS.inputProc = PlayoutProcess; + auCbS.inputProcRefCon = this; + result = AudioUnitSetProperty(_auVoiceProcessing, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Global, 0, + &auCbS, sizeof(auCbS)); + if (0 != result) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Could not set play callback for Audio Unit (result=%d)", + result); + } + + // Get stream format for out/0 + AudioStreamBasicDescription playoutDesc; + UInt32 size = sizeof(playoutDesc); + result = AudioUnitGetProperty(_auVoiceProcessing, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, 0, &playoutDesc, + &size); + if (0 != result) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Could not get stream format Audio Unit out/0 (result=%d)", + result); + } + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + " Audio Unit playout opened in sampling rate %f", + playoutDesc.mSampleRate); + + playoutDesc.mSampleRate = preferredSampleRate; + + // Store the sampling frequency to use towards the Audio Device Buffer + // todo: Add 48 kHz (increase buffer sizes). Other fs? + if ((playoutDesc.mSampleRate > 44090.0) + && (playoutDesc.mSampleRate < 44110.0)) { + _adbSampFreq = 44100; + } else if ((playoutDesc.mSampleRate > 15990.0) + && (playoutDesc.mSampleRate < 16010.0)) { + _adbSampFreq = 16000; + } else if ((playoutDesc.mSampleRate > 7990.0) + && (playoutDesc.mSampleRate < 8010.0)) { + _adbSampFreq = 8000; + } else { + _adbSampFreq = 0; + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Audio Unit out/0 opened in unknown sampling rate (%f)", + playoutDesc.mSampleRate); + // todo: We should bail out here. + } + + // Set the audio device buffer sampling rate, + // we assume we get the same for play and record + if (_ptrAudioBuffer->SetRecordingSampleRate(_adbSampFreq) < 0) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Could not set audio device buffer recording sampling rate (%d)", + _adbSampFreq); + } + + if (_ptrAudioBuffer->SetPlayoutSampleRate(_adbSampFreq) < 0) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Could not set audio device buffer playout sampling rate (%d)", + _adbSampFreq); + } + + // Set stream format for in/0 (use same sampling frequency as for out/0) + playoutDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger + | kLinearPCMFormatFlagIsPacked + | kLinearPCMFormatFlagIsNonInterleaved; + playoutDesc.mBytesPerPacket = 2; + playoutDesc.mFramesPerPacket = 1; + playoutDesc.mBytesPerFrame = 2; + playoutDesc.mChannelsPerFrame = 1; + playoutDesc.mBitsPerChannel = 16; + result = AudioUnitSetProperty(_auVoiceProcessing, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, 0, &playoutDesc, size); + if (0 != result) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Could not set stream format Audio Unit in/0 (result=%d)", + result); + } + + // Get stream format for in/1 + AudioStreamBasicDescription recordingDesc; + size = sizeof(recordingDesc); + result = AudioUnitGetProperty(_auVoiceProcessing, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, 1, &recordingDesc, + &size); + if (0 != result) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Could not get stream format Audio Unit in/1 (result=%d)", + result); + } + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, + " Audio Unit recording opened in sampling rate %f", + recordingDesc.mSampleRate); + + recordingDesc.mSampleRate = preferredSampleRate; + + // Set stream format for out/1 (use same sampling frequency as for in/1) + recordingDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger + | kLinearPCMFormatFlagIsPacked + | kLinearPCMFormatFlagIsNonInterleaved; + + recordingDesc.mBytesPerPacket = 2; + recordingDesc.mFramesPerPacket = 1; + recordingDesc.mBytesPerFrame = 2; + recordingDesc.mChannelsPerFrame = 1; + recordingDesc.mBitsPerChannel = 16; + result = AudioUnitSetProperty(_auVoiceProcessing, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, 1, &recordingDesc, + size); + if (0 != result) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Could not set stream format Audio Unit out/1 (result=%d)", + result); + } + + // Initialize here already to be able to get/set stream properties. + result = AudioUnitInitialize(_auVoiceProcessing); + if (0 != result) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + " Could not init Audio Unit (result=%d)", result); + } + + // Get hardware sample rate for logging (see if we get what we asked for) + double sampleRate = session.sampleRate; + WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, + " Current HW sample rate is %f, ADB sample rate is %d", + sampleRate, _adbSampFreq); + + // Listen to audio interruptions. + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + id observer = + [center addObserverForName:AVAudioSessionInterruptionNotification + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification* notification) { + NSNumber* typeNumber = + [notification userInfo][AVAudioSessionInterruptionTypeKey]; + AVAudioSessionInterruptionType type = + (AVAudioSessionInterruptionType)[typeNumber unsignedIntegerValue]; + switch (type) { + case AVAudioSessionInterruptionTypeBegan: + // At this point our audio session has been deactivated and the + // audio unit render callbacks no longer occur. Nothing to do. + break; + case AVAudioSessionInterruptionTypeEnded: { + NSError* error = nil; + AVAudioSession* session = [AVAudioSession sharedInstance]; + [session setActive:YES + error:&error]; + if (error != nil) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + "Error activating audio session"); + } + // Post interruption the audio unit render callbacks don't + // automatically continue, so we restart the unit manually here. + AudioOutputUnitStop(_auVoiceProcessing); + AudioOutputUnitStart(_auVoiceProcessing); + break; + } + } + }]; + // Increment refcount on observer using ARC bridge. Instance variable is a + // void* instead of an id because header is included in other pure C++ + // files. + _audioInterruptionObserver = (__bridge_retained void*)observer; + + // Activate audio session. + error = nil; + [session setActive:YES + error:&error]; + if (error != nil) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + "Error activating audio session"); + } + + return 0; +} + +int32_t AudioDeviceIOS::ShutdownPlayOrRecord() { + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); + + if (_audioInterruptionObserver != NULL) { + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + // Transfer ownership of observer back to ARC, which will dealloc the + // observer once it exits this scope. + id observer = (__bridge_transfer id)_audioInterruptionObserver; + [center removeObserver:observer]; + _audioInterruptionObserver = NULL; + } + + // Close and delete AU + OSStatus result = -1; + if (NULL != _auVoiceProcessing) { + result = AudioOutputUnitStop(_auVoiceProcessing); + if (0 != result) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " Error stopping Audio Unit (result=%d)", result); + } + result = AudioComponentInstanceDispose(_auVoiceProcessing); + if (0 != result) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " Error disposing Audio Unit (result=%d)", result); + } + _auVoiceProcessing = NULL; + } + + return 0; +} + +// ============================================================================ +// Thread Methods +// ============================================================================ + +OSStatus + AudioDeviceIOS::RecordProcess(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData) { + AudioDeviceIOS* ptrThis = static_cast(inRefCon); + + return ptrThis->RecordProcessImpl(ioActionFlags, + inTimeStamp, + inBusNumber, + inNumberFrames); +} + + +OSStatus + AudioDeviceIOS::RecordProcessImpl(AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + uint32_t inBusNumber, + uint32_t inNumberFrames) { + // Setup some basic stuff + // Use temp buffer not to lock up recording buffer more than necessary + // todo: Make dataTmp a member variable with static size that holds + // max possible frames? + int16_t* dataTmp = new int16_t[inNumberFrames]; + memset(dataTmp, 0, 2*inNumberFrames); + + AudioBufferList abList; + abList.mNumberBuffers = 1; + abList.mBuffers[0].mData = dataTmp; + abList.mBuffers[0].mDataByteSize = 2*inNumberFrames; // 2 bytes/sample + abList.mBuffers[0].mNumberChannels = 1; + + // Get data from mic + OSStatus res = AudioUnitRender(_auVoiceProcessing, + ioActionFlags, inTimeStamp, + inBusNumber, inNumberFrames, &abList); + if (res != 0) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " Error getting rec data, error = %d", res); + + if (_recWarning > 0) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " Pending rec warning exists"); + } + _recWarning = 1; + + delete [] dataTmp; + return 0; + } + + if (_recording) { + // Insert all data in temp buffer into recording buffers + // There is zero or one buffer partially full at any given time, + // all others are full or empty + // Full means filled with noSamp10ms samples. + + const unsigned int noSamp10ms = _adbSampFreq / 100; + unsigned int dataPos = 0; + uint16_t bufPos = 0; + int16_t insertPos = -1; + unsigned int nCopy = 0; // Number of samples to copy + + while (dataPos < inNumberFrames) { + // Loop over all recording buffers or + // until we find the partially full buffer + // First choice is to insert into partially full buffer, + // second choice is to insert into empty buffer + bufPos = 0; + insertPos = -1; + nCopy = 0; + while (bufPos < N_REC_BUFFERS) { + if ((_recordingLength[bufPos] > 0) + && (_recordingLength[bufPos] < noSamp10ms)) { + // Found the partially full buffer + insertPos = static_cast(bufPos); + // Don't need to search more, quit loop + bufPos = N_REC_BUFFERS; + } else if ((-1 == insertPos) + && (0 == _recordingLength[bufPos])) { + // Found an empty buffer + insertPos = static_cast(bufPos); + } + ++bufPos; + } + + // Insert data into buffer + if (insertPos > -1) { + // We found a non-full buffer, copy data to it + unsigned int dataToCopy = inNumberFrames - dataPos; + unsigned int currentRecLen = _recordingLength[insertPos]; + unsigned int roomInBuffer = noSamp10ms - currentRecLen; + nCopy = (dataToCopy < roomInBuffer ? dataToCopy : roomInBuffer); + + memcpy(&_recordingBuffer[insertPos][currentRecLen], + &dataTmp[dataPos], nCopy*sizeof(int16_t)); + if (0 == currentRecLen) { + _recordingSeqNumber[insertPos] = _recordingCurrentSeq; + ++_recordingCurrentSeq; + } + _recordingBufferTotalSize += nCopy; + // Has to be done last to avoid interrupt problems + // between threads + _recordingLength[insertPos] += nCopy; + dataPos += nCopy; + } else { + // Didn't find a non-full buffer + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " Could not insert into recording buffer"); + if (_recWarning > 0) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " Pending rec warning exists"); + } + _recWarning = 1; + dataPos = inNumberFrames; // Don't try to insert more + } + } + } + + delete [] dataTmp; + + return 0; +} + +OSStatus + AudioDeviceIOS::PlayoutProcess(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData) { + AudioDeviceIOS* ptrThis = static_cast(inRefCon); + + return ptrThis->PlayoutProcessImpl(inNumberFrames, ioData); +} + +OSStatus + AudioDeviceIOS::PlayoutProcessImpl(uint32_t inNumberFrames, + AudioBufferList *ioData) { + // Setup some basic stuff +// assert(sizeof(short) == 2); // Assumption for implementation + + int16_t* data = + static_cast(ioData->mBuffers[0].mData); + unsigned int dataSizeBytes = ioData->mBuffers[0].mDataByteSize; + unsigned int dataSize = dataSizeBytes/2; // Number of samples + if (dataSize != inNumberFrames) { // Should always be the same + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + "dataSize (%u) != inNumberFrames (%u)", + dataSize, (unsigned int)inNumberFrames); + if (_playWarning > 0) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " Pending play warning exists"); + } + _playWarning = 1; + } + memset(data, 0, dataSizeBytes); // Start with empty buffer + + + // Get playout data from Audio Device Buffer + + if (_playing) { + unsigned int noSamp10ms = _adbSampFreq / 100; + // todo: Member variable and allocate when samp freq is determined + int16_t* dataTmp = new int16_t[noSamp10ms]; + memset(dataTmp, 0, 2*noSamp10ms); + unsigned int dataPos = 0; + int noSamplesOut = 0; + unsigned int nCopy = 0; + + // First insert data from playout buffer if any + if (_playoutBufferUsed > 0) { + nCopy = (dataSize < _playoutBufferUsed) ? + dataSize : _playoutBufferUsed; + if (nCopy != _playoutBufferUsed) { + // todo: If dataSize < _playoutBufferUsed + // (should normally never be) + // we must move the remaining data + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + "nCopy (%u) != _playoutBufferUsed (%u)", + nCopy, _playoutBufferUsed); + if (_playWarning > 0) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " Pending play warning exists"); + } + _playWarning = 1; + } + memcpy(data, _playoutBuffer, 2*nCopy); + dataPos = nCopy; + memset(_playoutBuffer, 0, sizeof(_playoutBuffer)); + _playoutBufferUsed = 0; + } + + // Now get the rest from Audio Device Buffer + while (dataPos < dataSize) { + // Update playout delay + UpdatePlayoutDelay(); + + // Ask for new PCM data to be played out using the AudioDeviceBuffer + noSamplesOut = _ptrAudioBuffer->RequestPlayoutData(noSamp10ms); + + // Get data from Audio Device Buffer + noSamplesOut = + _ptrAudioBuffer->GetPlayoutData( + reinterpret_cast(dataTmp)); + // Cast OK since only equality comparison + if (noSamp10ms != (unsigned int)noSamplesOut) { + // Should never happen + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + "noSamp10ms (%u) != noSamplesOut (%d)", + noSamp10ms, noSamplesOut); + + if (_playWarning > 0) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, + " Pending play warning exists"); + } + _playWarning = 1; + } + + // Insert as much as fits in data buffer + nCopy = (dataSize-dataPos) > noSamp10ms ? + noSamp10ms : (dataSize-dataPos); + memcpy(&data[dataPos], dataTmp, 2*nCopy); + + // Save rest in playout buffer if any + if (nCopy < noSamp10ms) { + memcpy(_playoutBuffer, &dataTmp[nCopy], 2*(noSamp10ms-nCopy)); + _playoutBufferUsed = noSamp10ms - nCopy; + } + + // Update loop/index counter, if we copied less than noSamp10ms + // samples we shall quit loop anyway + dataPos += noSamp10ms; + } + + delete [] dataTmp; + } + + return 0; +} + +void AudioDeviceIOS::UpdatePlayoutDelay() { + ++_playoutDelayMeasurementCounter; + + if (_playoutDelayMeasurementCounter >= 100) { + // Update HW and OS delay every second, unlikely to change + + // Since this is eventually rounded to integral ms, add 0.5ms + // here to get round-to-nearest-int behavior instead of + // truncation. + double totalDelaySeconds = 0.0005; + + // HW output latency + AVAudioSession* session = [AVAudioSession sharedInstance]; + double latency = session.outputLatency; + assert(latency >= 0); + totalDelaySeconds += latency; + + // HW buffer duration + double ioBufferDuration = session.IOBufferDuration; + assert(ioBufferDuration >= 0); + totalDelaySeconds += ioBufferDuration; + + // AU latency + Float64 f64(0); + UInt32 size = sizeof(f64); + OSStatus result = AudioUnitGetProperty( + _auVoiceProcessing, kAudioUnitProperty_Latency, + kAudioUnitScope_Global, 0, &f64, &size); + if (0 != result) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + "error AU latency (result=%d)", result); + } + assert(f64 >= 0); + totalDelaySeconds += f64; + + // To ms + _playoutDelay = static_cast(totalDelaySeconds / 1000); + + // Reset counter + _playoutDelayMeasurementCounter = 0; + } + + // todo: Add playout buffer? +} + +void AudioDeviceIOS::UpdateRecordingDelay() { + ++_recordingDelayMeasurementCounter; + + if (_recordingDelayMeasurementCounter >= 100) { + // Update HW and OS delay every second, unlikely to change + + // Since this is eventually rounded to integral ms, add 0.5ms + // here to get round-to-nearest-int behavior instead of + // truncation. + double totalDelaySeconds = 0.0005; + + // HW input latency + AVAudioSession* session = [AVAudioSession sharedInstance]; + double latency = session.inputLatency; + assert(latency >= 0); + totalDelaySeconds += latency; + + // HW buffer duration + double ioBufferDuration = session.IOBufferDuration; + assert(ioBufferDuration >= 0); + totalDelaySeconds += ioBufferDuration; + + // AU latency + Float64 f64(0); + UInt32 size = sizeof(f64); + OSStatus result = AudioUnitGetProperty( + _auVoiceProcessing, kAudioUnitProperty_Latency, + kAudioUnitScope_Global, 0, &f64, &size); + if (0 != result) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + "error AU latency (result=%d)", result); + } + assert(f64 >= 0); + totalDelaySeconds += f64; + + // To ms + _recordingDelayHWAndOS = + static_cast(totalDelaySeconds / 1000); + + // Reset counter + _recordingDelayMeasurementCounter = 0; + } + + _recordingDelay = _recordingDelayHWAndOS; + + // ADB recording buffer size, update every time + // Don't count the one next 10 ms to be sent, then convert samples => ms + const uint32_t noSamp10ms = _adbSampFreq / 100; + if (_recordingBufferTotalSize > noSamp10ms) { + _recordingDelay += + (_recordingBufferTotalSize - noSamp10ms) / (_adbSampFreq / 1000); + } +} + +bool AudioDeviceIOS::RunCapture(void* ptrThis) { + return static_cast(ptrThis)->CaptureWorkerThread(); +} + +bool AudioDeviceIOS::CaptureWorkerThread() { + if (_recording) { + int bufPos = 0; + unsigned int lowestSeq = 0; + int lowestSeqBufPos = 0; + bool foundBuf = true; + const unsigned int noSamp10ms = _adbSampFreq / 100; + + while (foundBuf) { + // Check if we have any buffer with data to insert + // into the Audio Device Buffer, + // and find the one with the lowest seq number + foundBuf = false; + for (bufPos = 0; bufPos < N_REC_BUFFERS; ++bufPos) { + if (noSamp10ms == _recordingLength[bufPos]) { + if (!foundBuf) { + lowestSeq = _recordingSeqNumber[bufPos]; + lowestSeqBufPos = bufPos; + foundBuf = true; + } else if (_recordingSeqNumber[bufPos] < lowestSeq) { + lowestSeq = _recordingSeqNumber[bufPos]; + lowestSeqBufPos = bufPos; + } + } + } // for + + // Insert data into the Audio Device Buffer if found any + if (foundBuf) { + // Update recording delay + UpdateRecordingDelay(); + + // Set the recorded buffer + _ptrAudioBuffer->SetRecordedBuffer( + reinterpret_cast( + _recordingBuffer[lowestSeqBufPos]), + _recordingLength[lowestSeqBufPos]); + + // Don't need to set the current mic level in ADB since we only + // support digital AGC, + // and besides we cannot get or set the IOS mic level anyway. + + // Set VQE info, use clockdrift == 0 + _ptrAudioBuffer->SetVQEData(_playoutDelay, _recordingDelay, 0); + + // Deliver recorded samples at specified sample rate, mic level + // etc. to the observer using callback + _ptrAudioBuffer->DeliverRecordedData(); + + // Make buffer available + _recordingSeqNumber[lowestSeqBufPos] = 0; + _recordingBufferTotalSize -= _recordingLength[lowestSeqBufPos]; + // Must be done last to avoid interrupt problems between threads + _recordingLength[lowestSeqBufPos] = 0; + } + } // while (foundBuf) + } // if (_recording) + + { + // Normal case + // Sleep thread (5ms) to let other threads get to work + // todo: Is 5 ms optimal? Sleep shorter if inserted into the Audio + // Device Buffer? + timespec t; + t.tv_sec = 0; + t.tv_nsec = 5*1000*1000; + nanosleep(&t, NULL); + } + + return true; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_utility_ios.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_utility_ios.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_utility_ios.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_utility_ios.cc 2015-02-03 14:33:35.000000000 +0000 @@ -15,7 +15,7 @@ #include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { -AudioDeviceUtilityIPhone::AudioDeviceUtilityIPhone(const int32_t id) +AudioDeviceUtilityIOS::AudioDeviceUtilityIOS(const int32_t id) : _critSect(*CriticalSectionWrapper::CreateCriticalSection()), _id(id), @@ -24,15 +24,16 @@ "%s created", __FUNCTION__); } -AudioDeviceUtilityIPhone::~AudioDeviceUtilityIPhone() { +AudioDeviceUtilityIOS::~AudioDeviceUtilityIOS() { WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s destroyed", __FUNCTION__); - CriticalSectionScoped lock(&_critSect); - + { + CriticalSectionScoped lock(&_critSect); + } delete &_critSect; } -int32_t AudioDeviceUtilityIPhone::Init() { +int32_t AudioDeviceUtilityIOS::Init() { WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_utility_ios.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_utility_ios.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_utility_ios.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/ios/audio_device_utility_ios.h 2015-02-03 14:33:35.000000000 +0000 @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_IPHONE_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_IPHONE_H +#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_IOS_H +#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_IOS_H #include "webrtc/modules/audio_device/audio_device_utility.h" #include "webrtc/modules/audio_device/include/audio_device.h" @@ -17,15 +17,15 @@ namespace webrtc { class CriticalSectionWrapper; -class AudioDeviceUtilityIPhone: public AudioDeviceUtility { -public: - AudioDeviceUtilityIPhone(const int32_t id); - AudioDeviceUtilityIPhone(); - virtual ~AudioDeviceUtilityIPhone(); +class AudioDeviceUtilityIOS: public AudioDeviceUtility { + public: + AudioDeviceUtilityIOS(const int32_t id); + AudioDeviceUtilityIOS(); + virtual ~AudioDeviceUtilityIOS(); virtual int32_t Init(); -private: + private: CriticalSectionWrapper& _critSect; int32_t _id; AudioDeviceModule::ErrorCode _lastError; @@ -33,4 +33,4 @@ } // namespace webrtc -#endif // WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_IPHONE_H +#endif // WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_IOS_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_alsa_linux.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_alsa_linux.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_alsa_linux.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_alsa_linux.cc 2015-02-03 14:33:35.000000000 +0000 @@ -188,8 +188,7 @@ { return 0; } - -#ifdef USE_X11 +#if defined(USE_X11) //Get X display handle for typing detection _XDisplay = XOpenDisplay(NULL); if (!_XDisplay) @@ -198,7 +197,6 @@ " failed to open X display, typing detection will not work"); } #endif - _playWarning = 0; _playError = 0; _recWarning = 0; @@ -264,15 +262,13 @@ _critSect.Enter(); } - -#ifdef USE_X11 +#if defined(USE_X11) if (_XDisplay) { XCloseDisplay(_XDisplay); _XDisplay = NULL; } #endif - _initialized = false; _outputDeviceIsSpecified = false; _inputDeviceIsSpecified = false; @@ -285,34 +281,6 @@ return (_initialized); } -int32_t AudioDeviceLinuxALSA::SpeakerIsAvailable(bool& available) -{ - - bool wasInitialized = _mixerManager.SpeakerIsInitialized(); - - // Make an attempt to open up the - // output mixer corresponding to the currently selected output device. - // - if (!wasInitialized && InitSpeaker() == -1) - { - available = false; - return 0; - } - - // Given that InitSpeaker was successful, we know that a valid speaker - // exists - available = true; - - // Close the initialized output mixer - // - if (!wasInitialized) - { - _mixerManager.CloseSpeaker(); - } - - return 0; -} - int32_t AudioDeviceLinuxALSA::InitSpeaker() { @@ -328,34 +296,6 @@ return _mixerManager.OpenSpeaker(devName); } -int32_t AudioDeviceLinuxALSA::MicrophoneIsAvailable(bool& available) -{ - - bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); - - // Make an attempt to open up the - // input mixer corresponding to the currently selected output device. - // - if (!wasInitialized && InitMicrophone() == -1) - { - available = false; - return 0; - } - - // Given that InitMicrophone was successful, we know that a valid - // microphone exists - available = true; - - // Close the initialized input mixer - // - if (!wasInitialized) - { - _mixerManager.CloseMicrophone(); - } - - return 0; -} - int32_t AudioDeviceLinuxALSA::InitMicrophone() { @@ -2376,7 +2316,7 @@ bool AudioDeviceLinuxALSA::KeyPressed() const{ -#ifdef USE_X11 +#if defined(USE_X11) char szKey[32]; unsigned int i = 0; char state = 0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_alsa_linux.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_alsa_linux.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_alsa_linux.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_alsa_linux.h 2015-02-03 14:33:35.000000000 +0000 @@ -15,8 +15,7 @@ #include "webrtc/modules/audio_device/linux/audio_mixer_manager_alsa_linux.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" - -#ifdef USE_X11 +#if defined(USE_X11) #include #endif #include @@ -91,10 +90,8 @@ uint16_t& volumeRight) const OVERRIDE; // Audio mixer initialization - virtual int32_t SpeakerIsAvailable(bool& available) OVERRIDE; virtual int32_t InitSpeaker() OVERRIDE; virtual bool SpeakerIsInitialized() const OVERRIDE; - virtual int32_t MicrophoneIsAvailable(bool& available) OVERRIDE; virtual int32_t InitMicrophone() OVERRIDE; virtual bool MicrophoneIsInitialized() const OVERRIDE; @@ -137,7 +134,7 @@ virtual int32_t StereoRecordingIsAvailable(bool& available) OVERRIDE; virtual int32_t SetStereoRecording(bool enable) OVERRIDE; virtual int32_t StereoRecording(bool& enabled) const OVERRIDE; - + // Delay information and control virtual int32_t SetPlayoutBuffer( const AudioDeviceModule::BufferType type, @@ -192,7 +189,7 @@ private: AudioDeviceBuffer* _ptrAudioBuffer; - + CriticalSectionWrapper& _critSect; ThreadWrapper* _ptrThreadRec; @@ -255,7 +252,7 @@ uint16_t _playBufDelayFixed; // fixed playback delay char _oldKeyState[32]; -#ifdef USE_X11 +#if defined(USE_X11) Display* _XDisplay; #endif }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_pulse_linux.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_pulse_linux.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_pulse_linux.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_pulse_linux.cc 2015-02-03 14:33:35.000000000 +0000 @@ -345,34 +345,6 @@ return (_initialized); } -int32_t AudioDeviceLinuxPulse::SpeakerIsAvailable(bool& available) -{ - - bool wasInitialized = _mixerManager.SpeakerIsInitialized(); - - // Make an attempt to open up the - // output mixer corresponding to the currently selected output device. - // - if (!wasInitialized && InitSpeaker() == -1) - { - available = false; - return 0; - } - - // Given that InitSpeaker was successful, we know that a valid speaker exists - // - available = true; - - // Close the initialized output mixer - // - if (!wasInitialized) - { - _mixerManager.CloseSpeaker(); - } - - return 0; -} - int32_t AudioDeviceLinuxPulse::InitSpeaker() { @@ -417,34 +389,6 @@ return 0; } - -int32_t AudioDeviceLinuxPulse::MicrophoneIsAvailable(bool& available) -{ - - bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); - - // Make an attempt to open up the - // input mixer corresponding to the currently selected output device. - // - if (!wasInitialized && InitMicrophone() == -1) - { - available = false; - return 0; - } - - // Given that InitMicrophone was successful, we know that a valid microphone - // exists - available = true; - - // Close the initialized input mixer - // - if (!wasInitialized) - { - _mixerManager.CloseMicrophone(); - } - - return 0; -} int32_t AudioDeviceLinuxPulse::InitMicrophone() { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_pulse_linux.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_pulse_linux.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_pulse_linux.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_pulse_linux.h 2015-02-03 14:33:35.000000000 +0000 @@ -153,10 +153,8 @@ uint16_t& volumeRight) const OVERRIDE; // Audio mixer initialization - virtual int32_t SpeakerIsAvailable(bool& available) OVERRIDE; virtual int32_t InitSpeaker() OVERRIDE; virtual bool SpeakerIsInitialized() const OVERRIDE; - virtual int32_t MicrophoneIsAvailable(bool& available) OVERRIDE; virtual int32_t InitMicrophone() OVERRIDE; virtual bool MicrophoneIsInitialized() const OVERRIDE; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/latebindingsymboltable_linux.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/latebindingsymboltable_linux.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/latebindingsymboltable_linux.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/latebindingsymboltable_linux.cc 2015-02-03 14:33:35.000000000 +0000 @@ -1,28 +1,11 @@ /* - * libjingle - * Copyright 2004--2010, Google Inc. + * Copyright (c) 2010 The WebRTC project authors. All Rights Reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. 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. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. */ #include "webrtc/modules/audio_device/linux/latebindingsymboltable_linux.h" @@ -64,10 +47,22 @@ void InternalUnloadDll(DllHandle handle) { #if defined(WEBRTC_LINUX) || defined(WEBRTC_BSD) +// TODO(pbos): Remove this dlclose() exclusion when leaks and suppressions from +// here are gone (or AddressSanitizer can display them properly). +// +// Skip dlclose() on AddressSanitizer as leaks including this module in the +// stack trace gets displayed as instead of the actual library +// -> it can not be suppressed. +// https://code.google.com/p/address-sanitizer/issues/detail?id=89 +// +// Skip dlclose() on ThreadSanitizer since it's hitting an assert. +// https://code.google.com/p/webrtc/issues/detail?id=3895 +#if !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER) if (dlclose(handle) != 0) { WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, "%s", GetDllError()); } +#endif // !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER) #else #error Not implemented #endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/latebindingsymboltable_linux.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/latebindingsymboltable_linux.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/latebindingsymboltable_linux.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/linux/latebindingsymboltable_linux.h 2015-02-03 14:33:35.000000000 +0000 @@ -1,28 +1,11 @@ /* - * libjingle - * Copyright 2004--2010, Google Inc. + * Copyright (c) 2010 The WebRTC project authors. All Rights Reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. 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. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. */ #ifndef WEBRTC_AUDIO_DEVICE_LATEBINDINGSYMBOLTABLE_LINUX_H @@ -32,7 +15,7 @@ #include // for NULL #include -#include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/base/constructormagic.h" #include "webrtc/system_wrappers/interface/trace.h" // This file provides macros for creating "symbol table" classes to simplify the diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/mac/audio_device_mac.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/mac/audio_device_mac.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/mac/audio_device_mac.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/mac/audio_device_mac.cc 2015-02-03 14:33:35.000000000 +0000 @@ -578,7 +578,6 @@ return 0; } - int32_t AudioDeviceMac::InitMicrophone() { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/mac/audio_device_mac.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/mac/audio_device_mac.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/mac/audio_device_mac.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/mac/audio_device_mac.h 2015-02-03 14:33:35.000000000 +0000 @@ -108,10 +108,8 @@ uint16_t& volumeRight) const; // Audio mixer initialization - virtual int32_t SpeakerIsAvailable(bool& available); virtual int32_t InitSpeaker(); virtual bool SpeakerIsInitialized() const; - virtual int32_t MicrophoneIsAvailable(bool& available); virtual int32_t InitMicrophone(); virtual bool MicrophoneIsInitialized() const; @@ -167,7 +165,6 @@ // CPU load virtual int32_t CPULoad(uint16_t& load) const; -public: virtual bool PlayoutWarning() const; virtual bool PlayoutError() const; virtual bool RecordingWarning() const; @@ -177,10 +174,12 @@ virtual void ClearRecordingWarning(); virtual void ClearRecordingError(); -public: virtual void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer); private: + virtual int32_t MicrophoneIsAvailable(bool& available); + virtual int32_t SpeakerIsAvailable(bool& available); + void Lock() { _critSect.Enter(); @@ -236,7 +235,6 @@ int32_t HandleProcessorOverload(AudioObjectPropertyAddress propertyAddress); -private: static OSStatus deviceIOProc(AudioDeviceID device, const AudioTimeStamp *now, const AudioBufferList *inputData, @@ -284,10 +282,8 @@ bool CaptureWorkerThread(); bool RenderWorkerThread(); -private: bool KeyPressed(); -private: AudioDeviceBuffer* _ptrAudioBuffer; CriticalSectionWrapper& _critSect; @@ -325,7 +321,6 @@ AudioDeviceModule::BufferType _playBufType; -private: bool _initialized; bool _isShutDown; bool _recording; @@ -361,7 +356,6 @@ int32_t _renderDelayOffsetSamples; -private: uint16_t _playBufDelayFixed; // fixed playback delay uint16_t _playWarning; @@ -378,7 +372,6 @@ int _captureBufSizeSamples; int _renderBufSizeSamples; -private: // Typing detection // 0x5c is key "9", after that comes function keys. bool prev_key_state_[0x5d]; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/main/source/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/main/source/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/main/source/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/main/source/OWNERS 2015-02-03 14:33:35.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -1,4 +1,14 @@ henrikg@webrtc.org henrika@webrtc.org niklas.enbom@webrtc.org +tkchin@webrtc.org xians@webrtc.org + +per-file *.isolate=kjellander@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/test/audio_device_test_api.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/test/audio_device_test_api.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/test/audio_device_test_api.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/test/audio_device_test_api.cc 2015-02-03 14:33:35.000000000 +0000 @@ -116,7 +116,9 @@ const uint8_t nChannels, const uint32_t sampleRate, void* audioSamples, - uint32_t& nSamplesOut) { + uint32_t& nSamplesOut, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) { play_count_++; if (play_count_ % 100 == 0) { if (nChannels == 1) { @@ -142,10 +144,16 @@ return 0; } - virtual void OnData(int voe_channel, const void* audio_data, - int bits_per_sample, int sample_rate, - int number_of_channels, - int number_of_frames) {} + virtual void PushCaptureData(int voe_channel, const void* audio_data, + int bits_per_sample, int sample_rate, + int number_of_channels, + int number_of_frames) {} + + virtual void PullRenderData(int bits_per_sample, int sample_rate, + int number_of_channels, int number_of_frames, + void* audio_data, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) {} private: uint32_t rec_count_; uint32_t play_count_; @@ -378,6 +386,8 @@ EXPECT_GT(audio_device_->RecordingDevices(), 0); } +// TODO(henrika): uncomment when you have decided what to do with issue 3675. +#if 0 TEST_F(AudioDeviceAPITest, PlayoutDeviceName) { char name[kAdmMaxDeviceNameSize]; char guid[kAdmMaxGuidSize]; @@ -474,6 +484,7 @@ EXPECT_EQ(0, audio_device_->SetRecordingDevice(i)); } } +#endif // 0 TEST_F(AudioDeviceAPITest, PlayoutIsAvailable) { bool available; @@ -787,33 +798,6 @@ } #endif // defined(_WIN32) && !defined(WEBRTC_WINDOWS_CORE_AUDIO_BUILD) -TEST_F(AudioDeviceAPITest, SpeakerIsAvailable) { - bool available; - CheckInitialPlayoutStates(); - -#ifdef _WIN32 - // check the kDefaultCommunicationDevice - EXPECT_TRUE(audio_device_->SetPlayoutDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - EXPECT_EQ(0, audio_device_->SpeakerIsAvailable(&available)); - // check for availability should not lead to initialization - EXPECT_FALSE(audio_device_->SpeakerIsInitialized()); -#endif - - // check the kDefaultDevice - EXPECT_EQ(0, audio_device_->SetPlayoutDevice(MACRO_DEFAULT_DEVICE)); - EXPECT_EQ(0, audio_device_->SpeakerIsAvailable(&available)); - EXPECT_FALSE(audio_device_->SpeakerIsInitialized()); - - // check all availiable devices - int16_t no_devices = audio_device_->PlayoutDevices(); - for (int i = 0; i < no_devices; i++) { - EXPECT_EQ(0, audio_device_->SetPlayoutDevice(i)); - EXPECT_EQ(0, audio_device_->SpeakerIsAvailable(&available)); - EXPECT_FALSE(audio_device_->SpeakerIsInitialized()); - } -} - TEST_F(AudioDeviceAPITest, InitSpeaker) { // NOTE: By calling Terminate (in TearDown) followed by Init (in SetUp) we // ensure that any existing output mixer handle is set to NULL. @@ -824,13 +808,10 @@ // kDefaultCommunicationDevice EXPECT_EQ(0, audio_device_->SetPlayoutDevice( MACRO_DEFAULT_COMMUNICATION_DEVICE)); - bool available; - EXPECT_EQ(0, audio_device_->SpeakerIsAvailable(&available)); - if (available) { - EXPECT_EQ(0, audio_device_->InitSpeaker()); - } + EXPECT_EQ(0, audio_device_->InitSpeaker()); // fail tests + bool available; EXPECT_EQ(0, audio_device_->PlayoutIsAvailable(&available)); if (available) { EXPECT_EQ(0, audio_device_->InitPlayout()); @@ -841,45 +822,13 @@ // kDefaultDevice EXPECT_EQ(0, audio_device_->SetPlayoutDevice(MACRO_DEFAULT_DEVICE)); - EXPECT_EQ(0, audio_device_->SpeakerIsAvailable(&available)); - if (available) { - EXPECT_EQ(0, audio_device_->InitSpeaker()); - } + EXPECT_EQ(0, audio_device_->InitSpeaker()); // repeat test for all devices int16_t no_devices = audio_device_->PlayoutDevices(); for (int i = 0; i < no_devices; i++) { EXPECT_EQ(0, audio_device_->SetPlayoutDevice(i)); - EXPECT_EQ(0, audio_device_->SpeakerIsAvailable(&available)); - if (available) { - EXPECT_EQ(0, audio_device_->InitSpeaker()); - } - } -} - -TEST_F(AudioDeviceAPITest, MicrophoneIsAvailable) { - CheckInitialRecordingStates(); - bool available; -#ifdef _WIN32 - // check the kDefaultCommunicationDevice - EXPECT_TRUE(audio_device_->SetRecordingDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - EXPECT_EQ(0, audio_device_->MicrophoneIsAvailable(&available)); - // check for availability should not lead to initialization - EXPECT_FALSE(audio_device_->MicrophoneIsInitialized()); -#endif - - // check the kDefaultDevice - EXPECT_EQ(0, audio_device_->SetRecordingDevice(MACRO_DEFAULT_DEVICE)); - EXPECT_EQ(0, audio_device_->MicrophoneIsAvailable(&available)); - EXPECT_FALSE(audio_device_->MicrophoneIsInitialized()); - - // check all availiable devices - int16_t no_devices = audio_device_->RecordingDevices(); - for (int i = 0; i < no_devices; i++) { - EXPECT_EQ(0, audio_device_->SetRecordingDevice(i)); - EXPECT_EQ(0, audio_device_->MicrophoneIsAvailable(&available)); - EXPECT_FALSE(audio_device_->MicrophoneIsInitialized()); + EXPECT_EQ(0, audio_device_->InitSpeaker()); } } @@ -893,13 +842,10 @@ // kDefaultCommunicationDevice EXPECT_EQ(0, audio_device_->SetRecordingDevice(MACRO_DEFAULT_COMMUNICATION_DEVICE)); - bool available; - EXPECT_EQ(0, audio_device_->MicrophoneIsAvailable(&available)); - if (available) { - EXPECT_EQ(0, audio_device_->InitMicrophone()); - } + EXPECT_EQ(0, audio_device_->InitMicrophone()); // fail tests + bool available; EXPECT_EQ(0, audio_device_->RecordingIsAvailable(&available)); if (available) { EXPECT_EQ(0, audio_device_->InitRecording()); @@ -910,19 +856,13 @@ // kDefaultDevice EXPECT_EQ(0, audio_device_->SetRecordingDevice(MACRO_DEFAULT_DEVICE)); - EXPECT_EQ(0, audio_device_->MicrophoneIsAvailable(&available)); - if (available) { - EXPECT_EQ(0, audio_device_->InitMicrophone()); - } + EXPECT_EQ(0, audio_device_->InitMicrophone()); // repeat test for all devices int16_t no_devices = audio_device_->RecordingDevices(); for (int i = 0; i < no_devices; i++) { EXPECT_EQ(0, audio_device_->SetRecordingDevice(i)); - EXPECT_EQ(0, audio_device_->MicrophoneIsAvailable(&available)); - if (available) { - EXPECT_EQ(0, audio_device_->InitMicrophone()); - } + EXPECT_EQ(0, audio_device_->InitMicrophone()); } } @@ -1859,19 +1799,19 @@ #if defined(WEBRTC_IOS) // Not playing or recording, should just return a success EXPECT_EQ(0, audio_device_->SetLoudspeakerStatus(true)); - EXPECT_EQ(0, audio_device_->GetLoudspeakerStatus(loudspeakerOn)); + EXPECT_EQ(0, audio_device_->GetLoudspeakerStatus(&loudspeakerOn)); EXPECT_TRUE(loudspeakerOn); EXPECT_EQ(0, audio_device_->SetLoudspeakerStatus(false)); - EXPECT_EQ(0, audio_device_->GetLoudspeakerStatus(loudspeakerOn)); + EXPECT_EQ(0, audio_device_->GetLoudspeakerStatus(&loudspeakerOn)); EXPECT_FALSE(loudspeakerOn); EXPECT_EQ(0, audio_device_->InitPlayout()); EXPECT_EQ(0, audio_device_->StartPlayout()); EXPECT_EQ(0, audio_device_->SetLoudspeakerStatus(true)); - EXPECT_EQ(0, audio_device_->GetLoudspeakerStatus(loudspeakerOn)); + EXPECT_EQ(0, audio_device_->GetLoudspeakerStatus(&loudspeakerOn)); EXPECT_TRUE(loudspeakerOn); EXPECT_EQ(0, audio_device_->SetLoudspeakerStatus(false)); - EXPECT_EQ(0, audio_device_->GetLoudspeakerStatus(loudspeakerOn)); + EXPECT_EQ(0, audio_device_->GetLoudspeakerStatus(&loudspeakerOn)); EXPECT_FALSE(loudspeakerOn); #else diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/test/func_test_manager.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/test/func_test_manager.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/test/func_test_manager.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/test/func_test_manager.cc 2015-02-03 14:33:35.000000000 +0000 @@ -292,7 +292,9 @@ const uint8_t nChannels, const uint32_t samplesPerSec, void* audioSamples, - uint32_t& nSamplesOut) + uint32_t& nSamplesOut, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) { if (_fullDuplex) { @@ -536,11 +538,18 @@ return 0; } -void AudioTransportImpl::OnData(int voe_channel, - const void* audio_data, - int bits_per_sample, int sample_rate, - int number_of_channels, - int number_of_frames) {} +void AudioTransportImpl::PushCaptureData(int voe_channel, + const void* audio_data, + int bits_per_sample, int sample_rate, + int number_of_channels, + int number_of_frames) {} + +void AudioTransportImpl::PullRenderData(int bits_per_sample, int sample_rate, + int number_of_channels, + int number_of_frames, + void* audio_data, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) {} FuncTestManager::FuncTestManager() : _processThread(NULL), @@ -907,9 +916,12 @@ #ifdef _WIN32 // default (-1) + // TODO(henrika): fix below test. +#if 0 EXPECT_EQ(0, audioDevice->PlayoutDeviceName(-1, name, guid)); TEST_LOG("PlayoutDeviceName(%d): default name=%s \n \ default guid=%s\n", -1, name, guid); +#endif // 0 #else // should fail EXPECT_EQ(-1, audioDevice->PlayoutDeviceName(-1, name, guid)); @@ -929,9 +941,12 @@ #ifdef _WIN32 // default (-1) + // TODO(henrika): fix below test. +#if 0 EXPECT_EQ(0, audioDevice->RecordingDeviceName(-1, name, guid)); TEST_LOG("RecordingDeviceName(%d): default name=%s \n \ default guid=%s\n", -1, name, guid); +#endif #else // should fail EXPECT_EQ(-1, audioDevice->PlayoutDeviceName(-1, name, guid)); @@ -1007,8 +1022,6 @@ { PRINT_STR(Stereo Playout, false); } - EXPECT_EQ(0, audioDevice->SpeakerIsAvailable(&available)); - PRINT_STR(Speaker, available); EXPECT_EQ(0, audioDevice->SpeakerVolumeIsAvailable(&available)); PRINT_STR(Speaker Volume, available); EXPECT_EQ(0, audioDevice->SpeakerMuteIsAvailable(&available)); @@ -1027,8 +1040,6 @@ { PRINT_STR(Stereo Playout, false); } - EXPECT_EQ(0, audioDevice->SpeakerIsAvailable(&available)); - PRINT_STR(Speaker, available); EXPECT_EQ(0, audioDevice->SpeakerVolumeIsAvailable(&available)); PRINT_STR(Speaker Volume, available); EXPECT_EQ(0, audioDevice->SpeakerMuteIsAvailable(&available)); @@ -1054,8 +1065,6 @@ { PRINT_STR(Stereo Playout, false); } - EXPECT_EQ(0, audioDevice->SpeakerIsAvailable(&available)); - PRINT_STR(Speaker, available); EXPECT_EQ(0, audioDevice->SpeakerVolumeIsAvailable(&available)); PRINT_STR(Speaker Volume, available); EXPECT_EQ(0, audioDevice->SpeakerMuteIsAvailable(&available)); @@ -1085,8 +1094,6 @@ // special fix to ensure that we don't log 'available' when recording is not OK PRINT_STR(Stereo Recording, false); } - EXPECT_EQ(0, audioDevice->MicrophoneIsAvailable(&available)); - PRINT_STR(Microphone, available); EXPECT_EQ(0, audioDevice->MicrophoneVolumeIsAvailable(&available)); PRINT_STR(Microphone Volume, available); EXPECT_EQ(0, audioDevice->MicrophoneMuteIsAvailable(&available)); @@ -1108,8 +1115,6 @@ // special fix to ensure that we don't log 'available' when recording is not OK PRINT_STR(Stereo Recording, false); } - EXPECT_EQ(0, audioDevice->MicrophoneIsAvailable(&available)); - PRINT_STR(Microphone, available); EXPECT_EQ(0, audioDevice->MicrophoneVolumeIsAvailable(&available)); PRINT_STR(Microphone Volume, available); EXPECT_EQ(0, audioDevice->MicrophoneMuteIsAvailable(&available)); @@ -1139,8 +1144,6 @@ // is not OK PRINT_STR(Stereo Recording, false); } - EXPECT_EQ(0, audioDevice->MicrophoneIsAvailable(&available)); - PRINT_STR(Microphone, available); EXPECT_EQ(0, audioDevice->MicrophoneVolumeIsAvailable(&available)); PRINT_STR(Microphone Volume, available); EXPECT_EQ(0, audioDevice->MicrophoneMuteIsAvailable(&available)); @@ -2689,7 +2692,7 @@ " from the loudspeaker.\n\ > Press any key to stop...\n \n"); PAUSE(DEFAULT_PAUSE_TIME); - EXPECT_EQ(0, audioDevice->GetLoudspeakerStatus(loudspeakerOn)); + EXPECT_EQ(0, audioDevice->GetLoudspeakerStatus(&loudspeakerOn)); EXPECT_TRUE(loudspeakerOn); TEST_LOG("Set to not use speaker\n"); @@ -2698,7 +2701,7 @@ " from the loudspeaker.\n\ > Press any key to stop...\n \n"); PAUSE(DEFAULT_PAUSE_TIME); - EXPECT_EQ(0, audioDevice->GetLoudspeakerStatus(loudspeakerOn)); + EXPECT_EQ(0, audioDevice->GetLoudspeakerStatus(&loudspeakerOn)); EXPECT_FALSE(loudspeakerOn); #endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/test/func_test_manager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/test/func_test_manager.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/test/func_test_manager.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/test/func_test_manager.h 2015-02-03 14:33:35.000000000 +0000 @@ -118,7 +118,9 @@ const uint8_t nChannels, const uint32_t samplesPerSec, void* audioSamples, - uint32_t& nSamplesOut); + uint32_t& nSamplesOut, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms); virtual int OnDataAvailable(const int voe_channels[], int number_of_voe_channels, @@ -131,10 +133,16 @@ bool key_pressed, bool need_audio_processing); - virtual void OnData(int voe_channel, const void* audio_data, - int bits_per_sample, int sample_rate, - int number_of_channels, - int number_of_frames); + virtual void PushCaptureData(int voe_channel, const void* audio_data, + int bits_per_sample, int sample_rate, + int number_of_channels, + int number_of_frames); + + virtual void PullRenderData(int bits_per_sample, int sample_rate, + int number_of_channels, int number_of_frames, + void* audio_data, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms); AudioTransportImpl(AudioDeviceModule* audioDevice); ~AudioTransportImpl(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_core_win.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_core_win.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_core_win.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_core_win.cc 2015-02-03 14:33:35.000000000 +0000 @@ -20,12 +20,6 @@ #include "webrtc/modules/audio_device/audio_device_config.h" -#if defined(WEBRTC_WINDOWS_CORE_AUDIO_BUILD) -#pragma message(">> INFO: WEBRTC_WINDOWS_CORE_AUDIO_BUILD is defined") -#else -#pragma message(">> INFO: WEBRTC_WINDOWS_CORE_AUDIO_BUILD is *not* defined") -#endif - #ifdef WEBRTC_WINDOWS_CORE_AUDIO_BUILD #include "webrtc/modules/audio_device/win/audio_device_core_win.h" @@ -751,25 +745,6 @@ } // ---------------------------------------------------------------------------- -// SpeakerIsAvailable -// ---------------------------------------------------------------------------- - -int32_t AudioDeviceWindowsCore::SpeakerIsAvailable(bool& available) -{ - - CriticalSectionScoped lock(&_critSect); - - if (_ptrDeviceOut == NULL) - { - return -1; - } - - available = true; - - return 0; -} - -// ---------------------------------------------------------------------------- // InitSpeaker // ---------------------------------------------------------------------------- @@ -852,25 +827,6 @@ } // ---------------------------------------------------------------------------- -// MicrophoneIsAvailable -// ---------------------------------------------------------------------------- - -int32_t AudioDeviceWindowsCore::MicrophoneIsAvailable(bool& available) -{ - - CriticalSectionScoped lock(&_critSect); - - if (_ptrDeviceIn == NULL) - { - return -1; - } - - available = true; - - return 0; -} - -// ---------------------------------------------------------------------------- // InitMicrophone // ---------------------------------------------------------------------------- @@ -2239,7 +2195,7 @@ HRESULT hr = S_OK; WAVEFORMATEX* pWfxOut = NULL; - WAVEFORMATEX Wfx; + WAVEFORMATEX Wfx = WAVEFORMATEX(); WAVEFORMATEX* pWfxClosestMatch = NULL; // Create COM object with IAudioClient interface. @@ -2576,7 +2532,7 @@ HRESULT hr = S_OK; WAVEFORMATEX* pWfxIn = NULL; - WAVEFORMATEX Wfx; + WAVEFORMATEX Wfx = WAVEFORMATEX(); WAVEFORMATEX* pWfxClosestMatch = NULL; // Create COM object with IAudioClient interface. @@ -3373,7 +3329,7 @@ default: // unexpected error WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " unknown wait termination on get volume thread"); - return -1; + return 1; } } } @@ -3394,7 +3350,7 @@ default: // unexpected error WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " unknown wait termination on set volume thread"); - return -1; + return 1; } _Lock(); @@ -3430,10 +3386,10 @@ if (!comInit.succeeded()) { WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to initialize COM in render thread"); - return -1; + return 1; } - _SetThreadName(-1, "webrtc_core_audio_render_thread"); + _SetThreadName(0, "webrtc_core_audio_render_thread"); // Use Multimedia Class Scheduler Service (MMCSS) to boost the thread priority. // @@ -3710,7 +3666,7 @@ { _hMmTask = NULL; - _SetThreadName(-1, "webrtc_core_audio_capture_thread"); + _SetThreadName(0, "webrtc_core_audio_capture_thread"); // Use Multimedia Class Scheduler Service (MMCSS) to boost the thread // priority. @@ -3764,7 +3720,7 @@ if (!comInit.succeeded()) { WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to initialize COM in polling DMO thread"); - return -1; + return 1; } HRESULT hr = InitCaptureThreadPriority(); @@ -3922,7 +3878,7 @@ if (!comInit.succeeded()) { WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to initialize COM in capture thread"); - return -1; + return 1; } hr = InitCaptureThreadPriority(); @@ -3937,6 +3893,12 @@ // This value is fixed during the capturing session. // UINT32 bufferLength = 0; + if (_ptrClientIn == NULL) + { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, + "input state has been modified before capture loop starts."); + return 1; + } hr = _ptrClientIn->GetBufferSize(&bufferLength); EXIT_ON_ERROR(hr); WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[CAPT] size of buffer : %u", bufferLength); @@ -3949,7 +3911,7 @@ syncBuffer = new BYTE[syncBufferSize]; if (syncBuffer == NULL) { - return E_POINTER; + return (DWORD)E_POINTER; } WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[CAPT] size of sync buffer : %u [bytes]", syncBufferSize); @@ -4157,7 +4119,10 @@ // ---------------------------- THREAD LOOP ---------------------------- << - hr = _ptrClientIn->Stop(); + if (_ptrClientIn) + { + hr = _ptrClientIn->Stop(); + } Exit: if (FAILED(hr)) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_core_win.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_core_win.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_core_win.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_core_win.h 2015-02-03 14:33:35.000000000 +0000 @@ -138,10 +138,8 @@ virtual int32_t WaveOutVolume(uint16_t& volumeLeft, uint16_t& volumeRight) const; // Audio mixer initialization - virtual int32_t SpeakerIsAvailable(bool& available); virtual int32_t InitSpeaker(); virtual bool SpeakerIsInitialized() const; - virtual int32_t MicrophoneIsAvailable(bool& available); virtual int32_t InitMicrophone(); virtual bool MicrophoneIsInitialized() const; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_utility_win.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_utility_win.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_utility_win.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_utility_win.cc 2015-02-03 14:33:35.000000000 +0000 @@ -105,7 +105,8 @@ // Retrieve information about the current operating system // - if (!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osvi))) + bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osvi); + if (!bOsVersionInfoEx) return FALSE; // Parse our OS version string diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_wave_win.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_wave_win.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_wave_win.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_wave_win.cc 2015-02-03 14:33:35.000000000 +0000 @@ -428,7 +428,7 @@ default: // unexpected error WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " unknown wait termination on get volume thread"); - return -1; + return 1; } if (AGC()) @@ -464,7 +464,7 @@ default: // unexpected error WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " unknown wait termination on set volume thread"); - return -1; + return 1; } _critSect.Enter(); @@ -490,33 +490,6 @@ } // ---------------------------------------------------------------------------- -// SpeakerIsAvailable -// ---------------------------------------------------------------------------- - -int32_t AudioDeviceWindowsWave::SpeakerIsAvailable(bool& available) -{ - - // Enumerate all avaliable speakers and make an attempt to open up the - // output mixer corresponding to the currently selected output device. - // - if (InitSpeaker() == -1) - { - available = false; - return 0; - } - - // Given that InitSpeaker was successful, we know that a valid speaker exists - // - available = true; - - // Close the initialized output mixer - // - _mixerManager.CloseSpeaker(); - - return 0; -} - -// ---------------------------------------------------------------------------- // InitSpeaker // ---------------------------------------------------------------------------- @@ -555,33 +528,6 @@ } // ---------------------------------------------------------------------------- -// MicrophoneIsAvailable -// ---------------------------------------------------------------------------- - -int32_t AudioDeviceWindowsWave::MicrophoneIsAvailable(bool& available) -{ - - // Enumerate all avaliable microphones and make an attempt to open up the - // input mixer corresponding to the currently selected output device. - // - if (InitMicrophone() == -1) - { - available = false; - return 0; - } - - // Given that InitMicrophone was successful, we know that a valid microphone exists - // - available = true; - - // Close the initialized input mixer - // - _mixerManager.CloseMicrophone(); - - return 0; -} - -// ---------------------------------------------------------------------------- // InitMicrophone // ---------------------------------------------------------------------------- @@ -3364,7 +3310,7 @@ _sndCardPlayDelay = msecOnPlaySide; _sndCardRecDelay = msecOnRecordSide; - LARGE_INTEGER t1,t2; + LARGE_INTEGER t1={0},t2={0}; if (send) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_wave_win.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_wave_win.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_wave_win.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_device_wave_win.h 2015-02-03 14:33:35.000000000 +0000 @@ -94,10 +94,8 @@ virtual int32_t WaveOutVolume(uint16_t& volumeLeft, uint16_t& volumeRight) const; // Audio mixer initialization - virtual int32_t SpeakerIsAvailable(bool& available); virtual int32_t InitSpeaker(); virtual bool SpeakerIsInitialized() const; - virtual int32_t MicrophoneIsAvailable(bool& available); virtual int32_t InitMicrophone(); virtual bool MicrophoneIsInitialized() const; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_mixer_manager_win.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_mixer_manager_win.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_mixer_manager_win.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_device/win/audio_mixer_manager_win.cc 2015-02-03 14:33:35.000000000 +0000 @@ -586,7 +586,7 @@ _outputMixerHandle = NULL; } - MMRESULT res; + MMRESULT res = MMSYSERR_NOERROR; WAVEFORMATEX waveFormat; HWAVEOUT hWaveOut(NULL); @@ -808,7 +808,7 @@ _inputMixerHandle = NULL; } - MMRESULT res; + MMRESULT res = MMSYSERR_NOERROR; WAVEFORMATEX waveFormat; HWAVEIN hWaveIn(NULL); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_common.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_common.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_common.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_common.h 2015-02-03 14:33:35.000000000 +0000 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_COMMON_H_ +#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_COMMON_H_ + +#include "webrtc/typedefs.h" + +#ifdef _MSC_VER /* visual c++ */ +#define ALIGN16_BEG __declspec(align(16)) +#define ALIGN16_END +#else /* gcc or icc */ +#define ALIGN16_BEG +#define ALIGN16_END __attribute__((aligned(16))) +#endif + +extern ALIGN16_BEG const float ALIGN16_END WebRtcAec_sqrtHanning[65]; +extern ALIGN16_BEG const float ALIGN16_END WebRtcAec_weightCurve[65]; +extern ALIGN16_BEG const float ALIGN16_END WebRtcAec_overDriveCurve[65]; +extern const float WebRtcAec_kExtendedSmoothingCoefficients[2][2]; +extern const float WebRtcAec_kNormalSmoothingCoefficients[2][2]; +extern const float WebRtcAec_kMinFarendPSD; + +#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_COMMON_H_ + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.c 2015-02-03 14:33:35.000000000 +0000 @@ -14,6 +14,10 @@ #include "webrtc/modules/audio_processing/aec/aec_core.h" +#ifdef WEBRTC_AEC_DEBUG_DUMP +#include +#endif + #include #include #include // size_t @@ -21,6 +25,7 @@ #include #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/modules/audio_processing/aec/aec_common.h" #include "webrtc/modules/audio_processing/aec/aec_core_internal.h" #include "webrtc/modules/audio_processing/aec/aec_rdft.h" #include "webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h" @@ -51,7 +56,7 @@ // Matlab code to produce table: // win = sqrt(hanning(63)); win = [0 ; win(1:32)]; // fprintf(1, '\t%.14f, %.14f, %.14f,\n', win); -static const float sqrtHanning[65] = { +ALIGN16_BEG const float ALIGN16_END WebRtcAec_sqrtHanning[65] = { 0.00000000000000f, 0.02454122852291f, 0.04906767432742f, 0.07356456359967f, 0.09801714032956f, 0.12241067519922f, 0.14673047445536f, 0.17096188876030f, 0.19509032201613f, 0.21910124015687f, 0.24298017990326f, 0.26671275747490f, @@ -73,7 +78,7 @@ // Matlab code to produce table: // weightCurve = [0 ; 0.3 * sqrt(linspace(0,1,64))' + 0.1]; // fprintf(1, '\t%.4f, %.4f, %.4f, %.4f, %.4f, %.4f,\n', weightCurve); -const float WebRtcAec_weightCurve[65] = { +ALIGN16_BEG const float ALIGN16_END WebRtcAec_weightCurve[65] = { 0.0000f, 0.1000f, 0.1378f, 0.1535f, 0.1655f, 0.1756f, 0.1845f, 0.1926f, 0.2000f, 0.2069f, 0.2134f, 0.2195f, 0.2254f, 0.2309f, 0.2363f, 0.2414f, 0.2464f, 0.2512f, 0.2558f, 0.2604f, 0.2648f, 0.2690f, 0.2732f, 0.2773f, @@ -87,7 +92,7 @@ // Matlab code to produce table: // overDriveCurve = [sqrt(linspace(0,1,65))' + 1]; // fprintf(1, '\t%.4f, %.4f, %.4f, %.4f, %.4f, %.4f,\n', overDriveCurve); -const float WebRtcAec_overDriveCurve[65] = { +ALIGN16_BEG const float ALIGN16_END WebRtcAec_overDriveCurve[65] = { 1.0000f, 1.1250f, 1.1768f, 1.2165f, 1.2500f, 1.2795f, 1.3062f, 1.3307f, 1.3536f, 1.3750f, 1.3953f, 1.4146f, 1.4330f, 1.4507f, 1.4677f, 1.4841f, 1.5000f, 1.5154f, 1.5303f, 1.5449f, 1.5590f, 1.5728f, 1.5863f, 1.5995f, @@ -105,10 +110,10 @@ // Two sets of parameters, one for the extended filter mode. static const float kExtendedMinOverDrive[3] = {3.0f, 6.0f, 15.0f}; static const float kNormalMinOverDrive[3] = {1.0f, 2.0f, 5.0f}; -static const float kExtendedSmoothingCoefficients[2][2] = {{0.9f, 0.1f}, - {0.92f, 0.08f}}; -static const float kNormalSmoothingCoefficients[2][2] = {{0.9f, 0.1f}, - {0.93f, 0.07f}}; +const float WebRtcAec_kExtendedSmoothingCoefficients[2][2] = {{0.9f, 0.1f}, + {0.92f, 0.08f}}; +const float WebRtcAec_kNormalSmoothingCoefficients[2][2] = {{0.9f, 0.1f}, + {0.93f, 0.07f}}; // Number of partitions forming the NLP's "preferred" bands. enum { @@ -119,30 +124,12 @@ extern int webrtc_aec_instance_count; #endif -// "Private" function prototypes. -static void ProcessBlock(AecCore* aec); - -static void NonLinearProcessing(AecCore* aec, short* output, short* outputH); - -static void GetHighbandGain(const float* lambda, float* nlpGainHband); - -// Comfort_noise also computes noise for H band returned in comfortNoiseHband -static void ComfortNoise(AecCore* aec, - float efw[2][PART_LEN1], - complex_t* comfortNoiseHband, - const float* noisePow, - const float* lambda); - -static void InitLevel(PowerLevel* level); -static void InitStats(Stats* stats); -static void InitMetrics(AecCore* aec); -static void UpdateLevel(PowerLevel* level, float in[2][PART_LEN1]); -static void UpdateMetrics(AecCore* aec); -// Convert from time domain to frequency domain. Note that |time_data| are -// overwritten. -static void TimeToFrequency(float time_data[PART_LEN2], - float freq_data[2][PART_LEN1], - int window); +WebRtcAec_FilterFar_t WebRtcAec_FilterFar; +WebRtcAec_ScaleErrorSignal_t WebRtcAec_ScaleErrorSignal; +WebRtcAec_FilterAdaptation_t WebRtcAec_FilterAdaptation; +WebRtcAec_OverdriveAndSuppress_t WebRtcAec_OverdriveAndSuppress; +WebRtcAec_ComfortNoise_t WebRtcAec_ComfortNoise; +WebRtcAec_SubbandCoherence_t WebRtcAec_SubbandCoherence; __inline static float MulRe(float aRe, float aIm, float bRe, float bIm) { return aRe * bRe - aIm * bIm; @@ -159,116 +146,6 @@ return (*da > *db) - (*da < *db); } -int WebRtcAec_CreateAec(AecCore** aecInst) { - AecCore* aec = malloc(sizeof(AecCore)); - *aecInst = aec; - if (aec == NULL) { - return -1; - } - - aec->nearFrBuf = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t)); - if (!aec->nearFrBuf) { - WebRtcAec_FreeAec(aec); - aec = NULL; - return -1; - } - - aec->outFrBuf = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t)); - if (!aec->outFrBuf) { - WebRtcAec_FreeAec(aec); - aec = NULL; - return -1; - } - - aec->nearFrBufH = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t)); - if (!aec->nearFrBufH) { - WebRtcAec_FreeAec(aec); - aec = NULL; - return -1; - } - - aec->outFrBufH = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t)); - if (!aec->outFrBufH) { - WebRtcAec_FreeAec(aec); - aec = NULL; - return -1; - } - - // Create far-end buffers. - aec->far_buf = - WebRtc_CreateBuffer(kBufSizePartitions, sizeof(float) * 2 * PART_LEN1); - if (!aec->far_buf) { - WebRtcAec_FreeAec(aec); - aec = NULL; - return -1; - } - aec->far_buf_windowed = - WebRtc_CreateBuffer(kBufSizePartitions, sizeof(float) * 2 * PART_LEN1); - if (!aec->far_buf_windowed) { - WebRtcAec_FreeAec(aec); - aec = NULL; - return -1; - } -#ifdef WEBRTC_AEC_DEBUG_DUMP - aec->far_time_buf = - WebRtc_CreateBuffer(kBufSizePartitions, sizeof(int16_t) * PART_LEN); - if (!aec->far_time_buf) { - WebRtcAec_FreeAec(aec); - aec = NULL; - return -1; - } - aec->outLinearFile = aec->outFile = aec->nearFile = aec->farFile = NULL; - aec->debugWritten = 0; - OpenCoreDebugFiles(aec, &webrtc_aec_instance_count); -#endif - aec->delay_estimator_farend = - WebRtc_CreateDelayEstimatorFarend(PART_LEN1, kHistorySizeBlocks); - if (aec->delay_estimator_farend == NULL) { - WebRtcAec_FreeAec(aec); - aec = NULL; - return -1; - } - aec->delay_estimator = WebRtc_CreateDelayEstimator( - aec->delay_estimator_farend, kLookaheadBlocks); - if (aec->delay_estimator == NULL) { - WebRtcAec_FreeAec(aec); - aec = NULL; - return -1; - } - - return 0; -} - -int WebRtcAec_FreeAec(AecCore* aec) { - if (aec == NULL) { - return -1; - } - - WebRtc_FreeBuffer(aec->nearFrBuf); - WebRtc_FreeBuffer(aec->outFrBuf); - - WebRtc_FreeBuffer(aec->nearFrBufH); - WebRtc_FreeBuffer(aec->outFrBufH); - - WebRtc_FreeBuffer(aec->far_buf); - WebRtc_FreeBuffer(aec->far_buf_windowed); -#ifdef WEBRTC_AEC_DEBUG_DUMP - WebRtc_FreeBuffer(aec->far_time_buf); - if (aec->farFile) { - // we don't let one be open and not the others - fclose(aec->farFile); - fclose(aec->nearFile); - fclose(aec->outFile); - fclose(aec->outLinearFile); - } -#endif - WebRtc_FreeDelayEstimator(aec->delay_estimator); - WebRtc_FreeDelayEstimatorFarend(aec->delay_estimator_farend); - - free(aec); - return 0; -} - static void FilterFar(AecCore* aec, float yf[2][PART_LEN1]) { int i; for (i = 0; i < aec->num_partitions; i++) { @@ -416,620 +293,579 @@ } } -WebRtcAec_FilterFar_t WebRtcAec_FilterFar; -WebRtcAec_ScaleErrorSignal_t WebRtcAec_ScaleErrorSignal; -WebRtcAec_FilterAdaptation_t WebRtcAec_FilterAdaptation; -WebRtcAec_OverdriveAndSuppress_t WebRtcAec_OverdriveAndSuppress; - -int WebRtcAec_InitAec(AecCore* aec, int sampFreq) { +static int PartitionDelay(const AecCore* aec) { + // Measures the energy in each filter partition and returns the partition with + // highest energy. + // TODO(bjornv): Spread computational cost by computing one partition per + // block? + float wfEnMax = 0; int i; + int delay = 0; - aec->sampFreq = sampFreq; + for (i = 0; i < aec->num_partitions; i++) { + int j; + int pos = i * PART_LEN1; + float wfEn = 0; + for (j = 0; j < PART_LEN1; j++) { + wfEn += aec->wfBuf[0][pos + j] * aec->wfBuf[0][pos + j] + + aec->wfBuf[1][pos + j] * aec->wfBuf[1][pos + j]; + } - if (sampFreq == 8000) { - aec->normal_mu = 0.6f; - aec->normal_error_threshold = 2e-6f; - } else { - aec->normal_mu = 0.5f; - aec->normal_error_threshold = 1.5e-6f; + if (wfEn > wfEnMax) { + wfEnMax = wfEn; + delay = i; + } } + return delay; +} - if (WebRtc_InitBuffer(aec->nearFrBuf) == -1) { - return -1; - } +// Threshold to protect against the ill-effects of a zero far-end. +const float WebRtcAec_kMinFarendPSD = 15; - if (WebRtc_InitBuffer(aec->outFrBuf) == -1) { - return -1; - } +// Updates the following smoothed Power Spectral Densities (PSD): +// - sd : near-end +// - se : residual echo +// - sx : far-end +// - sde : cross-PSD of near-end and residual echo +// - sxd : cross-PSD of near-end and far-end +// +// In addition to updating the PSDs, also the filter diverge state is determined +// upon actions are taken. +static void SmoothedPSD(AecCore* aec, + float efw[2][PART_LEN1], + float dfw[2][PART_LEN1], + float xfw[2][PART_LEN1]) { + // Power estimate smoothing coefficients. + const float* ptrGCoh = aec->extended_filter_enabled + ? WebRtcAec_kExtendedSmoothingCoefficients[aec->mult - 1] + : WebRtcAec_kNormalSmoothingCoefficients[aec->mult - 1]; + int i; + float sdSum = 0, seSum = 0; - if (WebRtc_InitBuffer(aec->nearFrBufH) == -1) { - return -1; - } + for (i = 0; i < PART_LEN1; i++) { + aec->sd[i] = ptrGCoh[0] * aec->sd[i] + + ptrGCoh[1] * (dfw[0][i] * dfw[0][i] + dfw[1][i] * dfw[1][i]); + aec->se[i] = ptrGCoh[0] * aec->se[i] + + ptrGCoh[1] * (efw[0][i] * efw[0][i] + efw[1][i] * efw[1][i]); + // We threshold here to protect against the ill-effects of a zero farend. + // The threshold is not arbitrarily chosen, but balances protection and + // adverse interaction with the algorithm's tuning. + // TODO(bjornv): investigate further why this is so sensitive. + aec->sx[i] = + ptrGCoh[0] * aec->sx[i] + + ptrGCoh[1] * WEBRTC_SPL_MAX( + xfw[0][i] * xfw[0][i] + xfw[1][i] * xfw[1][i], + WebRtcAec_kMinFarendPSD); - if (WebRtc_InitBuffer(aec->outFrBufH) == -1) { - return -1; - } + aec->sde[i][0] = + ptrGCoh[0] * aec->sde[i][0] + + ptrGCoh[1] * (dfw[0][i] * efw[0][i] + dfw[1][i] * efw[1][i]); + aec->sde[i][1] = + ptrGCoh[0] * aec->sde[i][1] + + ptrGCoh[1] * (dfw[0][i] * efw[1][i] - dfw[1][i] * efw[0][i]); - // Initialize far-end buffers. - if (WebRtc_InitBuffer(aec->far_buf) == -1) { - return -1; - } - if (WebRtc_InitBuffer(aec->far_buf_windowed) == -1) { - return -1; - } -#ifdef WEBRTC_AEC_DEBUG_DUMP - if (WebRtc_InitBuffer(aec->far_time_buf) == -1) { - return -1; - } -#endif - aec->system_delay = 0; + aec->sxd[i][0] = + ptrGCoh[0] * aec->sxd[i][0] + + ptrGCoh[1] * (dfw[0][i] * xfw[0][i] + dfw[1][i] * xfw[1][i]); + aec->sxd[i][1] = + ptrGCoh[0] * aec->sxd[i][1] + + ptrGCoh[1] * (dfw[0][i] * xfw[1][i] - dfw[1][i] * xfw[0][i]); - if (WebRtc_InitDelayEstimatorFarend(aec->delay_estimator_farend) != 0) { - return -1; - } - if (WebRtc_InitDelayEstimator(aec->delay_estimator) != 0) { - return -1; + sdSum += aec->sd[i]; + seSum += aec->se[i]; } - aec->delay_logging_enabled = 0; - memset(aec->delay_histogram, 0, sizeof(aec->delay_histogram)); - aec->extended_filter_enabled = 0; - aec->num_partitions = kNormalNumPartitions; + // Divergent filter safeguard. + aec->divergeState = (aec->divergeState ? 1.05f : 1.0f) * seSum > sdSum; - // Update the delay estimator with filter length. We use half the - // |num_partitions| to take the echo path into account. In practice we say - // that the echo has a duration of maximum half |num_partitions|, which is not - // true, but serves as a crude measure. - WebRtc_set_allowed_offset(aec->delay_estimator, aec->num_partitions / 2); - // TODO(bjornv): I currently hard coded the enable. Once we've established - // that AECM has no performance regression, robust_validation will be enabled - // all the time and the APIs to turn it on/off will be removed. Hence, remove - // this line then. - WebRtc_enable_robust_validation(aec->delay_estimator, 1); + if (aec->divergeState) + memcpy(efw, dfw, sizeof(efw[0][0]) * 2 * PART_LEN1); - // Default target suppression mode. - aec->nlp_mode = 1; + // Reset if error is significantly larger than nearend (13 dB). + if (!aec->extended_filter_enabled && seSum > (19.95f * sdSum)) + memset(aec->wfBuf, 0, sizeof(aec->wfBuf)); +} - // Sampling frequency multiplier - // SWB is processed as 160 frame size - if (aec->sampFreq == 32000) { - aec->mult = (short)aec->sampFreq / 16000; - } else { - aec->mult = (short)aec->sampFreq / 8000; +// Window time domain data to be used by the fft. +__inline static void WindowData(float* x_windowed, const float* x) { + int i; + for (i = 0; i < PART_LEN; i++) { + x_windowed[i] = x[i] * WebRtcAec_sqrtHanning[i]; + x_windowed[PART_LEN + i] = + x[PART_LEN + i] * WebRtcAec_sqrtHanning[PART_LEN - i]; } +} - aec->farBufWritePos = 0; - aec->farBufReadPos = 0; +// Puts fft output data into a complex valued array. +__inline static void StoreAsComplex(const float* data, + float data_complex[2][PART_LEN1]) { + int i; + data_complex[0][0] = data[0]; + data_complex[1][0] = 0; + for (i = 1; i < PART_LEN; i++) { + data_complex[0][i] = data[2 * i]; + data_complex[1][i] = data[2 * i + 1]; + } + data_complex[0][PART_LEN] = data[1]; + data_complex[1][PART_LEN] = 0; +} - aec->inSamples = 0; - aec->outSamples = 0; - aec->knownDelay = 0; +static void SubbandCoherence(AecCore* aec, + float efw[2][PART_LEN1], + float xfw[2][PART_LEN1], + float* fft, + float* cohde, + float* cohxd) { + float dfw[2][PART_LEN1]; + int i; - // Initialize buffers - memset(aec->dBuf, 0, sizeof(aec->dBuf)); - memset(aec->eBuf, 0, sizeof(aec->eBuf)); - // For H band - memset(aec->dBufH, 0, sizeof(aec->dBufH)); + if (aec->delayEstCtr == 0) + aec->delayIdx = PartitionDelay(aec); - memset(aec->xPow, 0, sizeof(aec->xPow)); - memset(aec->dPow, 0, sizeof(aec->dPow)); - memset(aec->dInitMinPow, 0, sizeof(aec->dInitMinPow)); - aec->noisePow = aec->dInitMinPow; - aec->noiseEstCtr = 0; + // Use delayed far. + memcpy(xfw, + aec->xfwBuf + aec->delayIdx * PART_LEN1, + sizeof(xfw[0][0]) * 2 * PART_LEN1); - // Initial comfort noise power - for (i = 0; i < PART_LEN1; i++) { - aec->dMinPow[i] = 1.0e6f; - } + // Windowed near fft + WindowData(fft, aec->dBuf); + aec_rdft_forward_128(fft); + StoreAsComplex(fft, dfw); - // Holds the last block written to - aec->xfBufBlockPos = 0; - // TODO: Investigate need for these initializations. Deleting them doesn't - // change the output at all and yields 0.4% overall speedup. - memset(aec->xfBuf, 0, sizeof(complex_t) * kExtendedNumPartitions * PART_LEN1); - memset(aec->wfBuf, 0, sizeof(complex_t) * kExtendedNumPartitions * PART_LEN1); - memset(aec->sde, 0, sizeof(complex_t) * PART_LEN1); - memset(aec->sxd, 0, sizeof(complex_t) * PART_LEN1); - memset( - aec->xfwBuf, 0, sizeof(complex_t) * kExtendedNumPartitions * PART_LEN1); - memset(aec->se, 0, sizeof(float) * PART_LEN1); + // Windowed error fft + WindowData(fft, aec->eBuf); + aec_rdft_forward_128(fft); + StoreAsComplex(fft, efw); - // To prevent numerical instability in the first block. - for (i = 0; i < PART_LEN1; i++) { - aec->sd[i] = 1; - } + SmoothedPSD(aec, efw, dfw, xfw); + + // Subband coherence for (i = 0; i < PART_LEN1; i++) { - aec->sx[i] = 1; + cohde[i] = + (aec->sde[i][0] * aec->sde[i][0] + aec->sde[i][1] * aec->sde[i][1]) / + (aec->sd[i] * aec->se[i] + 1e-10f); + cohxd[i] = + (aec->sxd[i][0] * aec->sxd[i][0] + aec->sxd[i][1] * aec->sxd[i][1]) / + (aec->sx[i] * aec->sd[i] + 1e-10f); } +} - memset(aec->hNs, 0, sizeof(aec->hNs)); - memset(aec->outBuf, 0, sizeof(float) * PART_LEN); - - aec->hNlFbMin = 1; - aec->hNlFbLocalMin = 1; - aec->hNlXdAvgMin = 1; - aec->hNlNewMin = 0; - aec->hNlMinCtr = 0; - aec->overDrive = 2; - aec->overDriveSm = 2; - aec->delayIdx = 0; - aec->stNearState = 0; - aec->echoState = 0; - aec->divergeState = 0; +static void GetHighbandGain(const float* lambda, float* nlpGainHband) { + int i; - aec->seed = 777; - aec->delayEstCtr = 0; + nlpGainHband[0] = (float)0.0; + for (i = freqAvgIc; i < PART_LEN1 - 1; i++) { + nlpGainHband[0] += lambda[i]; + } + nlpGainHband[0] /= (float)(PART_LEN1 - 1 - freqAvgIc); +} - // Metrics disabled by default - aec->metricsMode = 0; - InitMetrics(aec); +static void ComfortNoise(AecCore* aec, + float efw[2][PART_LEN1], + complex_t* comfortNoiseHband, + const float* noisePow, + const float* lambda) { + int i, num; + float rand[PART_LEN]; + float noise, noiseAvg, tmp, tmpAvg; + int16_t randW16[PART_LEN]; + complex_t u[PART_LEN1]; - // Assembly optimization - WebRtcAec_FilterFar = FilterFar; - WebRtcAec_ScaleErrorSignal = ScaleErrorSignal; - WebRtcAec_FilterAdaptation = FilterAdaptation; - WebRtcAec_OverdriveAndSuppress = OverdriveAndSuppress; + const float pi2 = 6.28318530717959f; -#if defined(WEBRTC_ARCH_X86_FAMILY) - if (WebRtc_GetCPUInfo(kSSE2)) { - WebRtcAec_InitAec_SSE2(); + // Generate a uniform random array on [0 1] + WebRtcSpl_RandUArray(randW16, PART_LEN, &aec->seed); + for (i = 0; i < PART_LEN; i++) { + rand[i] = ((float)randW16[i]) / 32768; } -#endif - aec_rdft_init(); - - return 0; -} + // Reject LF noise + u[0][0] = 0; + u[0][1] = 0; + for (i = 1; i < PART_LEN1; i++) { + tmp = pi2 * rand[i - 1]; -void WebRtcAec_BufferFarendPartition(AecCore* aec, const float* farend) { - float fft[PART_LEN2]; - float xf[2][PART_LEN1]; + noise = sqrtf(noisePow[i]); + u[i][0] = noise * cosf(tmp); + u[i][1] = -noise * sinf(tmp); + } + u[PART_LEN][1] = 0; - // Check if the buffer is full, and in that case flush the oldest data. - if (WebRtc_available_write(aec->far_buf) < 1) { - WebRtcAec_MoveFarReadPtr(aec, 1); + for (i = 0; i < PART_LEN1; i++) { + // This is the proper weighting to match the background noise power + tmp = sqrtf(WEBRTC_SPL_MAX(1 - lambda[i] * lambda[i], 0)); + // tmp = 1 - lambda[i]; + efw[0][i] += tmp * u[i][0]; + efw[1][i] += tmp * u[i][1]; } - // Convert far-end partition to the frequency domain without windowing. - memcpy(fft, farend, sizeof(float) * PART_LEN2); - TimeToFrequency(fft, xf, 0); - WebRtc_WriteBuffer(aec->far_buf, &xf[0][0], 1); - // Convert far-end partition to the frequency domain with windowing. - memcpy(fft, farend, sizeof(float) * PART_LEN2); - TimeToFrequency(fft, xf, 1); - WebRtc_WriteBuffer(aec->far_buf_windowed, &xf[0][0], 1); -} + // For H band comfort noise + // TODO: don't compute noise and "tmp" twice. Use the previous results. + noiseAvg = 0.0; + tmpAvg = 0.0; + num = 0; + if (aec->sampFreq == 32000 && flagHbandCn == 1) { -int WebRtcAec_MoveFarReadPtr(AecCore* aec, int elements) { - int elements_moved = WebRtc_MoveReadPtr(aec->far_buf_windowed, elements); - WebRtc_MoveReadPtr(aec->far_buf, elements); -#ifdef WEBRTC_AEC_DEBUG_DUMP - WebRtc_MoveReadPtr(aec->far_time_buf, elements); -#endif - aec->system_delay -= elements_moved * PART_LEN; - return elements_moved; -} + // average noise scale + // average over second half of freq spectrum (i.e., 4->8khz) + // TODO: we shouldn't need num. We know how many elements we're summing. + for (i = PART_LEN1 >> 1; i < PART_LEN1; i++) { + num++; + noiseAvg += sqrtf(noisePow[i]); + } + noiseAvg /= (float)num; -void WebRtcAec_ProcessFrame(AecCore* aec, - const short* nearend, - const short* nearendH, - int knownDelay, - int16_t* out, - int16_t* outH) { - int out_elements = 0; + // average nlp scale + // average over second half of freq spectrum (i.e., 4->8khz) + // TODO: we shouldn't need num. We know how many elements we're summing. + num = 0; + for (i = PART_LEN1 >> 1; i < PART_LEN1; i++) { + num++; + tmpAvg += sqrtf(WEBRTC_SPL_MAX(1 - lambda[i] * lambda[i], 0)); + } + tmpAvg /= (float)num; - // For each frame the process is as follows: - // 1) If the system_delay indicates on being too small for processing a - // frame we stuff the buffer with enough data for 10 ms. - // 2) Adjust the buffer to the system delay, by moving the read pointer. - // 3) TODO(bjornv): Investigate if we need to add this: - // If we can't move read pointer due to buffer size limitations we - // flush/stuff the buffer. - // 4) Process as many partitions as possible. - // 5) Update the |system_delay| with respect to a full frame of FRAME_LEN - // samples. Even though we will have data left to process (we work with - // partitions) we consider updating a whole frame, since that's the - // amount of data we input and output in audio_processing. - // 6) Update the outputs. + // Use average noise for H band + // TODO: we should probably have a new random vector here. + // Reject LF noise + u[0][0] = 0; + u[0][1] = 0; + for (i = 1; i < PART_LEN1; i++) { + tmp = pi2 * rand[i - 1]; - // TODO(bjornv): Investigate how we should round the delay difference; right - // now we know that incoming |knownDelay| is underestimated when it's less - // than |aec->knownDelay|. We therefore, round (-32) in that direction. In - // the other direction, we don't have this situation, but might flush one - // partition too little. This can cause non-causality, which should be - // investigated. Maybe, allow for a non-symmetric rounding, like -16. - int move_elements = (aec->knownDelay - knownDelay - 32) / PART_LEN; - int moved_elements = 0; + // Use average noise for H band + u[i][0] = noiseAvg * (float)cos(tmp); + u[i][1] = -noiseAvg * (float)sin(tmp); + } + u[PART_LEN][1] = 0; - // TODO(bjornv): Change the near-end buffer handling to be the same as for - // far-end, that is, with a near_pre_buf. - // Buffer the near-end frame. - WebRtc_WriteBuffer(aec->nearFrBuf, nearend, FRAME_LEN); - // For H band - if (aec->sampFreq == 32000) { - WebRtc_WriteBuffer(aec->nearFrBufH, nearendH, FRAME_LEN); + for (i = 0; i < PART_LEN1; i++) { + // Use average NLP weight for H band + comfortNoiseHband[i][0] = tmpAvg * u[i][0]; + comfortNoiseHband[i][1] = tmpAvg * u[i][1]; + } } +} - // 1) At most we process |aec->mult|+1 partitions in 10 ms. Make sure we - // have enough far-end data for that by stuffing the buffer if the - // |system_delay| indicates others. - if (aec->system_delay < FRAME_LEN) { - // We don't have enough data so we rewind 10 ms. - WebRtcAec_MoveFarReadPtr(aec, -(aec->mult + 1)); - } +static void InitLevel(PowerLevel* level) { + const float kBigFloat = 1E17f; - // 2) Compensate for a possible change in the system delay. - WebRtc_MoveReadPtr(aec->far_buf_windowed, move_elements); - moved_elements = WebRtc_MoveReadPtr(aec->far_buf, move_elements); - aec->knownDelay -= moved_elements * PART_LEN; -#ifdef WEBRTC_AEC_DEBUG_DUMP - WebRtc_MoveReadPtr(aec->far_time_buf, move_elements); -#endif + level->averagelevel = 0; + level->framelevel = 0; + level->minlevel = kBigFloat; + level->frsum = 0; + level->sfrsum = 0; + level->frcounter = 0; + level->sfrcounter = 0; +} - // 4) Process as many blocks as possible. - while (WebRtc_available_read(aec->nearFrBuf) >= PART_LEN) { - ProcessBlock(aec); - } +static void InitStats(Stats* stats) { + stats->instant = kOffsetLevel; + stats->average = kOffsetLevel; + stats->max = kOffsetLevel; + stats->min = kOffsetLevel * (-1); + stats->sum = 0; + stats->hisum = 0; + stats->himean = kOffsetLevel; + stats->counter = 0; + stats->hicounter = 0; +} - // 5) Update system delay with respect to the entire frame. - aec->system_delay -= FRAME_LEN; +static void InitMetrics(AecCore* self) { + self->stateCounter = 0; + InitLevel(&self->farlevel); + InitLevel(&self->nearlevel); + InitLevel(&self->linoutlevel); + InitLevel(&self->nlpoutlevel); - // 6) Update output frame. - // Stuff the out buffer if we have less than a frame to output. - // This should only happen for the first frame. - out_elements = (int)WebRtc_available_read(aec->outFrBuf); - if (out_elements < FRAME_LEN) { - WebRtc_MoveReadPtr(aec->outFrBuf, out_elements - FRAME_LEN); - if (aec->sampFreq == 32000) { - WebRtc_MoveReadPtr(aec->outFrBufH, out_elements - FRAME_LEN); - } - } - // Obtain an output frame. - WebRtc_ReadBuffer(aec->outFrBuf, NULL, out, FRAME_LEN); - // For H band. - if (aec->sampFreq == 32000) { - WebRtc_ReadBuffer(aec->outFrBufH, NULL, outH, FRAME_LEN); - } + InitStats(&self->erl); + InitStats(&self->erle); + InitStats(&self->aNlp); + InitStats(&self->rerl); } -int WebRtcAec_GetDelayMetricsCore(AecCore* self, int* median, int* std) { - int i = 0; - int delay_values = 0; - int num_delay_values = 0; - int my_median = 0; - const int kMsPerBlock = PART_LEN / (self->mult * 8); - float l1_norm = 0; +static void UpdateLevel(PowerLevel* level, float in[2][PART_LEN1]) { + // Do the energy calculation in the frequency domain. The FFT is performed on + // a segment of PART_LEN2 samples due to overlap, but we only want the energy + // of half that data (the last PART_LEN samples). Parseval's relation states + // that the energy is preserved according to + // + // \sum_{n=0}^{N-1} |x(n)|^2 = 1/N * \sum_{n=0}^{N-1} |X(n)|^2 + // = ENERGY, + // + // where N = PART_LEN2. Since we are only interested in calculating the energy + // for the last PART_LEN samples we approximate by calculating ENERGY and + // divide by 2, + // + // \sum_{n=N/2}^{N-1} |x(n)|^2 ~= ENERGY / 2 + // + // Since we deal with real valued time domain signals we only store frequency + // bins [0, PART_LEN], which is what |in| consists of. To calculate ENERGY we + // need to add the contribution from the missing part in + // [PART_LEN+1, PART_LEN2-1]. These values are, up to a phase shift, identical + // with the values in [1, PART_LEN-1], hence multiply those values by 2. This + // is the values in the for loop below, but multiplication by 2 and division + // by 2 cancel. - assert(self != NULL); - assert(median != NULL); - assert(std != NULL); + // TODO(bjornv): Investigate reusing energy calculations performed at other + // places in the code. + int k = 1; + // Imaginary parts are zero at end points and left out of the calculation. + float energy = (in[0][0] * in[0][0]) / 2; + energy += (in[0][PART_LEN] * in[0][PART_LEN]) / 2; - if (self->delay_logging_enabled == 0) { - // Logging disabled. - return -1; + for (k = 1; k < PART_LEN; k++) { + energy += (in[0][k] * in[0][k] + in[1][k] * in[1][k]); } + energy /= PART_LEN2; - // Get number of delay values since last update. - for (i = 0; i < kHistorySizeBlocks; i++) { - num_delay_values += self->delay_histogram[i]; - } - if (num_delay_values == 0) { - // We have no new delay value data. Even though -1 is a valid estimate, it - // will practically never be used since multiples of |kMsPerBlock| will - // always be returned. - *median = -1; - *std = -1; - return 0; - } + level->sfrsum += energy; + level->sfrcounter++; - delay_values = num_delay_values >> 1; // Start value for median count down. - // Get median of delay values since last update. - for (i = 0; i < kHistorySizeBlocks; i++) { - delay_values -= self->delay_histogram[i]; - if (delay_values < 0) { - my_median = i; - break; + if (level->sfrcounter > subCountLen) { + level->framelevel = level->sfrsum / (subCountLen * PART_LEN); + level->sfrsum = 0; + level->sfrcounter = 0; + if (level->framelevel > 0) { + if (level->framelevel < level->minlevel) { + level->minlevel = level->framelevel; // New minimum. + } else { + level->minlevel *= (1 + 0.001f); // Small increase. + } + } + level->frcounter++; + level->frsum += level->framelevel; + if (level->frcounter > countLen) { + level->averagelevel = level->frsum / countLen; + level->frsum = 0; + level->frcounter = 0; } } - // Account for lookahead. - *median = (my_median - kLookaheadBlocks) * kMsPerBlock; +} - // Calculate the L1 norm, with median value as central moment. - for (i = 0; i < kHistorySizeBlocks; i++) { - l1_norm += (float)abs(i - my_median) * self->delay_histogram[i]; - } - *std = (int)(l1_norm / (float)num_delay_values + 0.5f) * kMsPerBlock; +static void UpdateMetrics(AecCore* aec) { + float dtmp, dtmp2; - // Reset histogram. - memset(self->delay_histogram, 0, sizeof(self->delay_histogram)); + const float actThresholdNoisy = 8.0f; + const float actThresholdClean = 40.0f; + const float safety = 0.99995f; + const float noisyPower = 300000.0f; - return 0; -} + float actThreshold; + float echo, suppressedEcho; -int WebRtcAec_echo_state(AecCore* self) { return self->echoState; } + if (aec->echoState) { // Check if echo is likely present + aec->stateCounter++; + } -void WebRtcAec_GetEchoStats(AecCore* self, - Stats* erl, - Stats* erle, - Stats* a_nlp) { - assert(erl != NULL); - assert(erle != NULL); - assert(a_nlp != NULL); - *erl = self->erl; - *erle = self->erle; - *a_nlp = self->aNlp; -} + if (aec->farlevel.frcounter == 0) { -#ifdef WEBRTC_AEC_DEBUG_DUMP -void* WebRtcAec_far_time_buf(AecCore* self) { return self->far_time_buf; } -#endif + if (aec->farlevel.minlevel < noisyPower) { + actThreshold = actThresholdClean; + } else { + actThreshold = actThresholdNoisy; + } -void WebRtcAec_SetConfigCore(AecCore* self, - int nlp_mode, - int metrics_mode, - int delay_logging) { - assert(nlp_mode >= 0 && nlp_mode < 3); - self->nlp_mode = nlp_mode; - self->metricsMode = metrics_mode; - if (self->metricsMode) { - InitMetrics(self); - } - self->delay_logging_enabled = delay_logging; - if (self->delay_logging_enabled) { - memset(self->delay_histogram, 0, sizeof(self->delay_histogram)); - } -} + if ((aec->stateCounter > (0.5f * countLen * subCountLen)) && + (aec->farlevel.sfrcounter == 0) -void WebRtcAec_enable_delay_correction(AecCore* self, int enable) { - self->extended_filter_enabled = enable; - self->num_partitions = enable ? kExtendedNumPartitions : kNormalNumPartitions; - // Update the delay estimator with filter length. See InitAEC() for details. - WebRtc_set_allowed_offset(self->delay_estimator, self->num_partitions / 2); -} + // Estimate in active far-end segments only + && + (aec->farlevel.averagelevel > + (actThreshold * aec->farlevel.minlevel))) { -int WebRtcAec_delay_correction_enabled(AecCore* self) { - return self->extended_filter_enabled; -} + // Subtract noise power + echo = aec->nearlevel.averagelevel - safety * aec->nearlevel.minlevel; -int WebRtcAec_system_delay(AecCore* self) { return self->system_delay; } + // ERL + dtmp = 10 * (float)log10(aec->farlevel.averagelevel / + aec->nearlevel.averagelevel + + 1e-10f); + dtmp2 = 10 * (float)log10(aec->farlevel.averagelevel / echo + 1e-10f); -void WebRtcAec_SetSystemDelay(AecCore* self, int delay) { - assert(delay >= 0); - self->system_delay = delay; -} + aec->erl.instant = dtmp; + if (dtmp > aec->erl.max) { + aec->erl.max = dtmp; + } -static void ProcessBlock(AecCore* aec) { - int i; - float d[PART_LEN], y[PART_LEN], e[PART_LEN], dH[PART_LEN]; - float scale; + if (dtmp < aec->erl.min) { + aec->erl.min = dtmp; + } - float fft[PART_LEN2]; - float xf[2][PART_LEN1], yf[2][PART_LEN1], ef[2][PART_LEN1]; - float df[2][PART_LEN1]; - float far_spectrum = 0.0f; - float near_spectrum = 0.0f; - float abs_far_spectrum[PART_LEN1]; - float abs_near_spectrum[PART_LEN1]; + aec->erl.counter++; + aec->erl.sum += dtmp; + aec->erl.average = aec->erl.sum / aec->erl.counter; - const float gPow[2] = {0.9f, 0.1f}; + // Upper mean + if (dtmp > aec->erl.average) { + aec->erl.hicounter++; + aec->erl.hisum += dtmp; + aec->erl.himean = aec->erl.hisum / aec->erl.hicounter; + } - // Noise estimate constants. - const int noiseInitBlocks = 500 * aec->mult; - const float step = 0.1f; - const float ramp = 1.0002f; - const float gInitNoise[2] = {0.999f, 0.001f}; + // A_NLP + dtmp = 10 * (float)log10(aec->nearlevel.averagelevel / + (2 * aec->linoutlevel.averagelevel) + + 1e-10f); - int16_t nearend[PART_LEN]; - int16_t* nearend_ptr = NULL; - int16_t output[PART_LEN]; - int16_t outputH[PART_LEN]; + // subtract noise power + suppressedEcho = 2 * (aec->linoutlevel.averagelevel - + safety * aec->linoutlevel.minlevel); - float* xf_ptr = NULL; + dtmp2 = 10 * (float)log10(echo / suppressedEcho + 1e-10f); - memset(dH, 0, sizeof(dH)); - if (aec->sampFreq == 32000) { - // Get the upper band first so we can reuse |nearend|. - WebRtc_ReadBuffer(aec->nearFrBufH, (void**)&nearend_ptr, nearend, PART_LEN); - for (i = 0; i < PART_LEN; i++) { - dH[i] = (float)(nearend_ptr[i]); - } - memcpy(aec->dBufH + PART_LEN, dH, sizeof(float) * PART_LEN); - } - WebRtc_ReadBuffer(aec->nearFrBuf, (void**)&nearend_ptr, nearend, PART_LEN); + aec->aNlp.instant = dtmp2; + if (dtmp > aec->aNlp.max) { + aec->aNlp.max = dtmp; + } - // ---------- Ooura fft ---------- - // Concatenate old and new nearend blocks. - for (i = 0; i < PART_LEN; i++) { - d[i] = (float)(nearend_ptr[i]); - } - memcpy(aec->dBuf + PART_LEN, d, sizeof(float) * PART_LEN); + if (dtmp < aec->aNlp.min) { + aec->aNlp.min = dtmp; + } -#ifdef WEBRTC_AEC_DEBUG_DUMP - { - int16_t farend[PART_LEN]; - int16_t* farend_ptr = NULL; - WebRtc_ReadBuffer(aec->far_time_buf, (void**)&farend_ptr, farend, 1); - OpenCoreDebugFiles(aec, &webrtc_aec_instance_count); - if (aec->farFile) { - (void)fwrite(farend_ptr, sizeof(int16_t), PART_LEN, aec->farFile); - (void)fwrite(nearend_ptr, sizeof(int16_t), PART_LEN, aec->nearFile); - aec->debugWritten += sizeof(int16_t) * PART_LEN; - if (aec->debugWritten >= AECDebugMaxSize()) { - AECDebugEnable(0); + aec->aNlp.counter++; + aec->aNlp.sum += dtmp; + aec->aNlp.average = aec->aNlp.sum / aec->aNlp.counter; + + // Upper mean + if (dtmp > aec->aNlp.average) { + aec->aNlp.hicounter++; + aec->aNlp.hisum += dtmp; + aec->aNlp.himean = aec->aNlp.hisum / aec->aNlp.hicounter; } - } - } -#endif - // We should always have at least one element stored in |far_buf|. - assert(WebRtc_available_read(aec->far_buf) > 0); - WebRtc_ReadBuffer(aec->far_buf, (void**)&xf_ptr, &xf[0][0], 1); + // ERLE - // Near fft - memcpy(fft, aec->dBuf, sizeof(float) * PART_LEN2); - TimeToFrequency(fft, df, 0); + // subtract noise power + suppressedEcho = 2 * (aec->nlpoutlevel.averagelevel - + safety * aec->nlpoutlevel.minlevel); - // Power smoothing - for (i = 0; i < PART_LEN1; i++) { - far_spectrum = (xf_ptr[i] * xf_ptr[i]) + - (xf_ptr[PART_LEN1 + i] * xf_ptr[PART_LEN1 + i]); - aec->xPow[i] = - gPow[0] * aec->xPow[i] + gPow[1] * aec->num_partitions * far_spectrum; - // Calculate absolute spectra - abs_far_spectrum[i] = sqrtf(far_spectrum); - - near_spectrum = df[0][i] * df[0][i] + df[1][i] * df[1][i]; - aec->dPow[i] = gPow[0] * aec->dPow[i] + gPow[1] * near_spectrum; - // Calculate absolute spectra - abs_near_spectrum[i] = sqrtf(near_spectrum); - } + dtmp = 10 * (float)log10(aec->nearlevel.averagelevel / + (2 * aec->nlpoutlevel.averagelevel) + + 1e-10f); + dtmp2 = 10 * (float)log10(echo / suppressedEcho + 1e-10f); - // Estimate noise power. Wait until dPow is more stable. - if (aec->noiseEstCtr > 50) { - for (i = 0; i < PART_LEN1; i++) { - if (aec->dPow[i] < aec->dMinPow[i]) { - aec->dMinPow[i] = - (aec->dPow[i] + step * (aec->dMinPow[i] - aec->dPow[i])) * ramp; - } else { - aec->dMinPow[i] *= ramp; + dtmp = dtmp2; + aec->erle.instant = dtmp; + if (dtmp > aec->erle.max) { + aec->erle.max = dtmp; } - } - } - // Smooth increasing noise power from zero at the start, - // to avoid a sudden burst of comfort noise. - if (aec->noiseEstCtr < noiseInitBlocks) { - aec->noiseEstCtr++; - for (i = 0; i < PART_LEN1; i++) { - if (aec->dMinPow[i] > aec->dInitMinPow[i]) { - aec->dInitMinPow[i] = gInitNoise[0] * aec->dInitMinPow[i] + - gInitNoise[1] * aec->dMinPow[i]; - } else { - aec->dInitMinPow[i] = aec->dMinPow[i]; + if (dtmp < aec->erle.min) { + aec->erle.min = dtmp; } - } - aec->noisePow = aec->dInitMinPow; - } else { - aec->noisePow = aec->dMinPow; - } - // Block wise delay estimation used for logging - if (aec->delay_logging_enabled) { - int delay_estimate = 0; - if (WebRtc_AddFarSpectrumFloat( - aec->delay_estimator_farend, abs_far_spectrum, PART_LEN1) == 0) { - delay_estimate = WebRtc_DelayEstimatorProcessFloat( - aec->delay_estimator, abs_near_spectrum, PART_LEN1); - if (delay_estimate >= 0) { - // Update delay estimate buffer. - aec->delay_histogram[delay_estimate]++; + aec->erle.counter++; + aec->erle.sum += dtmp; + aec->erle.average = aec->erle.sum / aec->erle.counter; + + // Upper mean + if (dtmp > aec->erle.average) { + aec->erle.hicounter++; + aec->erle.hisum += dtmp; + aec->erle.himean = aec->erle.hisum / aec->erle.hicounter; } } - } - - // Update the xfBuf block position. - aec->xfBufBlockPos--; - if (aec->xfBufBlockPos == -1) { - aec->xfBufBlockPos = aec->num_partitions - 1; - } - - // Buffer xf - memcpy(aec->xfBuf[0] + aec->xfBufBlockPos * PART_LEN1, - xf_ptr, - sizeof(float) * PART_LEN1); - memcpy(aec->xfBuf[1] + aec->xfBufBlockPos * PART_LEN1, - &xf_ptr[PART_LEN1], - sizeof(float) * PART_LEN1); - - memset(yf, 0, sizeof(yf)); - // Filter far - WebRtcAec_FilterFar(aec, yf); - - // Inverse fft to obtain echo estimate and error. - fft[0] = yf[0][0]; - fft[1] = yf[0][PART_LEN]; - for (i = 1; i < PART_LEN; i++) { - fft[2 * i] = yf[0][i]; - fft[2 * i + 1] = yf[1][i]; + aec->stateCounter = 0; } - aec_rdft_inverse_128(fft); +} - scale = 2.0f / PART_LEN2; - for (i = 0; i < PART_LEN; i++) { - y[i] = fft[PART_LEN + i] * scale; // fft scaling - } +static void TimeToFrequency(float time_data[PART_LEN2], + float freq_data[2][PART_LEN1], + int window) { + int i = 0; - for (i = 0; i < PART_LEN; i++) { - e[i] = d[i] - y[i]; + // TODO(bjornv): Should we have a different function/wrapper for windowed FFT? + if (window) { + for (i = 0; i < PART_LEN; i++) { + time_data[i] *= WebRtcAec_sqrtHanning[i]; + time_data[PART_LEN + i] *= WebRtcAec_sqrtHanning[PART_LEN - i]; + } } - // Error fft - memcpy(aec->eBuf + PART_LEN, e, sizeof(float) * PART_LEN); - memset(fft, 0, sizeof(float) * PART_LEN); - memcpy(fft + PART_LEN, e, sizeof(float) * PART_LEN); - // TODO(bjornv): Change to use TimeToFrequency(). - aec_rdft_forward_128(fft); - - ef[1][0] = 0; - ef[1][PART_LEN] = 0; - ef[0][0] = fft[0]; - ef[0][PART_LEN] = fft[1]; + aec_rdft_forward_128(time_data); + // Reorder. + freq_data[1][0] = 0; + freq_data[1][PART_LEN] = 0; + freq_data[0][0] = time_data[0]; + freq_data[0][PART_LEN] = time_data[1]; for (i = 1; i < PART_LEN; i++) { - ef[0][i] = fft[2 * i]; - ef[1][i] = fft[2 * i + 1]; - } - - if (aec->metricsMode == 1) { - // Note that the first PART_LEN samples in fft (before transformation) are - // zero. Hence, the scaling by two in UpdateLevel() should not be - // performed. That scaling is taken care of in UpdateMetrics() instead. - UpdateLevel(&aec->linoutlevel, ef); - } - - // Scale error signal inversely with far power. - WebRtcAec_ScaleErrorSignal(aec, ef); - WebRtcAec_FilterAdaptation(aec, fft, ef); - NonLinearProcessing(aec, output, outputH); - - if (aec->metricsMode == 1) { - // Update power levels and echo metrics - UpdateLevel(&aec->farlevel, (float(*)[PART_LEN1])xf_ptr); - UpdateLevel(&aec->nearlevel, df); - UpdateMetrics(aec); - } - - // Store the output block. - WebRtc_WriteBuffer(aec->outFrBuf, output, PART_LEN); - // For H band - if (aec->sampFreq == 32000) { - WebRtc_WriteBuffer(aec->outFrBufH, outputH, PART_LEN); + freq_data[0][i] = time_data[2 * i]; + freq_data[1][i] = time_data[2 * i + 1]; } +} #ifdef WEBRTC_AEC_DEBUG_DUMP - { - int16_t eInt16[PART_LEN]; - for (i = 0; i < PART_LEN; i++) { - eInt16[i] = (int16_t)WEBRTC_SPL_SAT( - WEBRTC_SPL_WORD16_MAX, e[i], WEBRTC_SPL_WORD16_MIN); +static void +OpenCoreDebugFiles(AecCore* aec, + int *instance_count) +{ + int error = 0; + // XXX If this impacts performance (opening files here), move file open + // to Trace::set_aec_debug(), and just grab them here + if (AECDebug() && !aec->farFile) { + if (!aec->farFile) { + char path[1024]; + char *filename; + path[0] = '\0'; + AECDebugFilenameBase(path, sizeof(path)); + filename = path + strlen(path); + if (&path[sizeof(path)] - filename < 128) { + return; // avoid a lot of snprintf's and checks lower + } + if (filename > path) { +#ifdef XP_WIN + if (*(filename-1) != '\\') { + *filename++ = '\\'; + } +#else + if (*(filename-1) != '/') { + *filename++ = '/'; + } +#endif + } + sprintf(filename, "aec_far%d.pcm", webrtc_aec_instance_count); + aec->farFile = fopen(path, "wb"); + sprintf(filename, "aec_near%d.pcm", webrtc_aec_instance_count); + aec->nearFile = fopen(path, "wb"); + sprintf(filename, "aec_out%d.pcm", webrtc_aec_instance_count); + aec->outFile = fopen(path, "wb"); + sprintf(filename, "aec_out_linear%d.pcm", webrtc_aec_instance_count); + aec->outLinearFile = fopen(path, "wb"); + aec->debugWritten = 0; + if (!aec->outLinearFile || !aec->outFile || !aec->nearFile || !aec->farFile) { + error = 1; + } + } + } + if (error || + (!AECDebug() && aec->farFile)) { + if (aec->farFile) { + fclose(aec->farFile); + } + if (aec->nearFile) { + fclose(aec->nearFile); + } + if (aec->outFile) { + fclose(aec->outFile); } - - OpenCoreDebugFiles(aec, &webrtc_aec_instance_count); if (aec->outLinearFile) { - (void)fwrite(eInt16, sizeof(int16_t), PART_LEN, aec->outLinearFile); - (void)fwrite(output, sizeof(int16_t), PART_LEN, aec->outFile); + fclose(aec->outLinearFile); } + aec->outLinearFile = aec->outFile = aec->nearFile = aec->farFile = NULL; + aec->debugWritten = 0; } -#endif } +#endif -static void NonLinearProcessing(AecCore* aec, short* output, short* outputH) { - float efw[2][PART_LEN1], dfw[2][PART_LEN1], xfw[2][PART_LEN1]; +static void NonLinearProcessing(AecCore* aec, float* output, float* outputH) { + float efw[2][PART_LEN1], xfw[2][PART_LEN1]; complex_t comfortNoiseHband[PART_LEN1]; float fft[PART_LEN2]; float scale, dtmp; float nlpGainHband; - int i, j, pos; + int i; // Coherence and non-linear filter float cohde[PART_LEN1], cohxd[PART_LEN1]; @@ -1040,20 +876,12 @@ const float prefBandQuant = 0.75f, prefBandQuantLow = 0.5f; const int prefBandSize = kPrefBandSize / aec->mult; const int minPrefBand = 4 / aec->mult; - - // Near and error power sums - float sdSum = 0, seSum = 0; - // Power estimate smoothing coefficients. - const float* ptrGCoh = aec->extended_filter_enabled - ? kExtendedSmoothingCoefficients[aec->mult - 1] - : kNormalSmoothingCoefficients[aec->mult - 1]; const float* min_overdrive = aec->extended_filter_enabled ? kExtendedMinOverDrive : kNormalMinOverDrive; // Filter energy - float wfEnMax = 0, wfEn = 0; const int delayEstInterval = 10 * aec->mult; float* xfw_ptr = NULL; @@ -1068,26 +896,6 @@ nlpGainHband = (float)0.0; dtmp = (float)0.0; - // Measure energy in each filter partition to determine delay. - // TODO: Spread by computing one partition per block? - if (aec->delayEstCtr == 0) { - wfEnMax = 0; - aec->delayIdx = 0; - for (i = 0; i < aec->num_partitions; i++) { - pos = i * PART_LEN1; - wfEn = 0; - for (j = 0; j < PART_LEN1; j++) { - wfEn += aec->wfBuf[0][pos + j] * aec->wfBuf[0][pos + j] + - aec->wfBuf[1][pos + j] * aec->wfBuf[1][pos + j]; - } - - if (wfEn > wfEnMax) { - wfEnMax = wfEn; - aec->delayIdx = i; - } - } - } - // We should always have at least one element stored in |far_buf|. assert(WebRtc_available_read(aec->far_buf_windowed) > 0); // NLP @@ -1098,126 +906,29 @@ // Buffer far. memcpy(aec->xfwBuf, xfw_ptr, sizeof(float) * 2 * PART_LEN1); - // Use delayed far. - memcpy(xfw, aec->xfwBuf + aec->delayIdx * PART_LEN1, sizeof(xfw)); + WebRtcAec_SubbandCoherence(aec, efw, xfw, fft, cohde, cohxd); - // Windowed near fft - for (i = 0; i < PART_LEN; i++) { - fft[i] = aec->dBuf[i] * sqrtHanning[i]; - fft[PART_LEN + i] = aec->dBuf[PART_LEN + i] * sqrtHanning[PART_LEN - i]; + hNlXdAvg = 0; + for (i = minPrefBand; i < prefBandSize + minPrefBand; i++) { + hNlXdAvg += cohxd[i]; } - aec_rdft_forward_128(fft); + hNlXdAvg /= prefBandSize; + hNlXdAvg = 1 - hNlXdAvg; - dfw[1][0] = 0; - dfw[1][PART_LEN] = 0; - dfw[0][0] = fft[0]; - dfw[0][PART_LEN] = fft[1]; - for (i = 1; i < PART_LEN; i++) { - dfw[0][i] = fft[2 * i]; - dfw[1][i] = fft[2 * i + 1]; + hNlDeAvg = 0; + for (i = minPrefBand; i < prefBandSize + minPrefBand; i++) { + hNlDeAvg += cohde[i]; } + hNlDeAvg /= prefBandSize; - // Windowed error fft - for (i = 0; i < PART_LEN; i++) { - fft[i] = aec->eBuf[i] * sqrtHanning[i]; - fft[PART_LEN + i] = aec->eBuf[PART_LEN + i] * sqrtHanning[PART_LEN - i]; + if (hNlXdAvg < 0.75f && hNlXdAvg < aec->hNlXdAvgMin) { + aec->hNlXdAvgMin = hNlXdAvg; } - aec_rdft_forward_128(fft); - efw[1][0] = 0; - efw[1][PART_LEN] = 0; - efw[0][0] = fft[0]; - efw[0][PART_LEN] = fft[1]; - for (i = 1; i < PART_LEN; i++) { - efw[0][i] = fft[2 * i]; - efw[1][i] = fft[2 * i + 1]; - } - - // Smoothed PSD - for (i = 0; i < PART_LEN1; i++) { - aec->sd[i] = ptrGCoh[0] * aec->sd[i] + - ptrGCoh[1] * (dfw[0][i] * dfw[0][i] + dfw[1][i] * dfw[1][i]); - aec->se[i] = ptrGCoh[0] * aec->se[i] + - ptrGCoh[1] * (efw[0][i] * efw[0][i] + efw[1][i] * efw[1][i]); - // We threshold here to protect against the ill-effects of a zero farend. - // The threshold is not arbitrarily chosen, but balances protection and - // adverse interaction with the algorithm's tuning. - // TODO: investigate further why this is so sensitive. - aec->sx[i] = - ptrGCoh[0] * aec->sx[i] + - ptrGCoh[1] * - WEBRTC_SPL_MAX(xfw[0][i] * xfw[0][i] + xfw[1][i] * xfw[1][i], 15); - - aec->sde[i][0] = - ptrGCoh[0] * aec->sde[i][0] + - ptrGCoh[1] * (dfw[0][i] * efw[0][i] + dfw[1][i] * efw[1][i]); - aec->sde[i][1] = - ptrGCoh[0] * aec->sde[i][1] + - ptrGCoh[1] * (dfw[0][i] * efw[1][i] - dfw[1][i] * efw[0][i]); - - aec->sxd[i][0] = - ptrGCoh[0] * aec->sxd[i][0] + - ptrGCoh[1] * (dfw[0][i] * xfw[0][i] + dfw[1][i] * xfw[1][i]); - aec->sxd[i][1] = - ptrGCoh[0] * aec->sxd[i][1] + - ptrGCoh[1] * (dfw[0][i] * xfw[1][i] - dfw[1][i] * xfw[0][i]); - - sdSum += aec->sd[i]; - seSum += aec->se[i]; - } - - // Divergent filter safeguard. - if (aec->divergeState == 0) { - if (seSum > sdSum) { - aec->divergeState = 1; - } - } else { - if (seSum * 1.05f < sdSum) { - aec->divergeState = 0; - } - } - - if (aec->divergeState == 1) { - memcpy(efw, dfw, sizeof(efw)); - } - - if (!aec->extended_filter_enabled) { - // Reset if error is significantly larger than nearend (13 dB). - if (seSum > (19.95f * sdSum)) { - memset(aec->wfBuf, 0, sizeof(aec->wfBuf)); - } - } - - // Subband coherence - for (i = 0; i < PART_LEN1; i++) { - cohde[i] = - (aec->sde[i][0] * aec->sde[i][0] + aec->sde[i][1] * aec->sde[i][1]) / - (aec->sd[i] * aec->se[i] + 1e-10f); - cohxd[i] = - (aec->sxd[i][0] * aec->sxd[i][0] + aec->sxd[i][1] * aec->sxd[i][1]) / - (aec->sx[i] * aec->sd[i] + 1e-10f); - } - - hNlXdAvg = 0; - for (i = minPrefBand; i < prefBandSize + minPrefBand; i++) { - hNlXdAvg += cohxd[i]; - } - hNlXdAvg /= prefBandSize; - hNlXdAvg = 1 - hNlXdAvg; - - hNlDeAvg = 0; - for (i = minPrefBand; i < prefBandSize + minPrefBand; i++) { - hNlDeAvg += cohde[i]; - } - hNlDeAvg /= prefBandSize; - - if (hNlXdAvg < 0.75f && hNlXdAvg < aec->hNlXdAvgMin) { - aec->hNlXdAvgMin = hNlXdAvg; - } - - if (hNlDeAvg > 0.98f && hNlXdAvg > 0.9f) { - aec->stNearState = 1; - } else if (hNlDeAvg < 0.95f || hNlXdAvg < 0.8f) { - aec->stNearState = 0; + + if (hNlDeAvg > 0.98f && hNlXdAvg > 0.9f) { + aec->stNearState = 1; + } else if (hNlDeAvg < 0.95f || hNlXdAvg < 0.8f) { + aec->stNearState = 0; } if (aec->hNlXdAvgMin == 1) { @@ -1290,7 +1001,7 @@ WebRtcAec_OverdriveAndSuppress(aec, hNl, hNlFb, efw); // Add comfort noise. - ComfortNoise(aec, efw, comfortNoiseHband, aec->noisePow, hNl); + WebRtcAec_ComfortNoise(aec, efw, comfortNoiseHband, aec->noisePow, hNl); // TODO(bjornv): Investigate how to take the windowing below into account if // needed. @@ -1315,14 +1026,14 @@ scale = 2.0f / PART_LEN2; for (i = 0; i < PART_LEN; i++) { fft[i] *= scale; // fft scaling - fft[i] = fft[i] * sqrtHanning[i] + aec->outBuf[i]; - - // Saturation protection - output[i] = (short)WEBRTC_SPL_SAT( - WEBRTC_SPL_WORD16_MAX, fft[i], WEBRTC_SPL_WORD16_MIN); + fft[i] = fft[i] * WebRtcAec_sqrtHanning[i] + aec->outBuf[i]; fft[PART_LEN + i] *= scale; // fft scaling - aec->outBuf[i] = fft[PART_LEN + i] * sqrtHanning[PART_LEN - i]; + aec->outBuf[i] = fft[PART_LEN + i] * WebRtcAec_sqrtHanning[PART_LEN - i]; + + // Saturate output to keep it in the allowed range. + output[i] = WEBRTC_SPL_SAT( + WEBRTC_SPL_WORD16_MAX, fft[i], WEBRTC_SPL_WORD16_MIN); } // For H band @@ -1347,8 +1058,8 @@ // compute gain factor for (i = 0; i < PART_LEN; i++) { - dtmp = (float)aec->dBufH[i]; - dtmp = (float)dtmp * nlpGainHband; // for variable gain + dtmp = aec->dBufH[i]; + dtmp = dtmp * nlpGainHband; // for variable gain // add some comfort noise where Hband is attenuated if (flagHbandCn == 1) { @@ -1356,8 +1067,8 @@ dtmp += cnScaleHband * fft[i]; } - // Saturation protection - outputH[i] = (short)WEBRTC_SPL_SAT( + // Saturate output to keep it in the allowed range. + outputH[i] = WEBRTC_SPL_SAT( WEBRTC_SPL_WORD16_MAX, dtmp, WEBRTC_SPL_WORD16_MIN); } } @@ -1376,411 +1087,755 @@ sizeof(aec->xfwBuf) - sizeof(complex_t) * PART_LEN1); } -static void GetHighbandGain(const float* lambda, float* nlpGainHband) { +static void ProcessBlock(AecCore* aec) { int i; + float y[PART_LEN], e[PART_LEN]; + float scale; - nlpGainHband[0] = (float)0.0; - for (i = freqAvgIc; i < PART_LEN1 - 1; i++) { - nlpGainHband[0] += lambda[i]; - } - nlpGainHband[0] /= (float)(PART_LEN1 - 1 - freqAvgIc); -} + float fft[PART_LEN2]; + float xf[2][PART_LEN1], yf[2][PART_LEN1], ef[2][PART_LEN1]; + float df[2][PART_LEN1]; + float far_spectrum = 0.0f; + float near_spectrum = 0.0f; + float abs_far_spectrum[PART_LEN1]; + float abs_near_spectrum[PART_LEN1]; -static void ComfortNoise(AecCore* aec, - float efw[2][PART_LEN1], - complex_t* comfortNoiseHband, - const float* noisePow, - const float* lambda) { - int i, num; - float rand[PART_LEN]; - float noise, noiseAvg, tmp, tmpAvg; - int16_t randW16[PART_LEN]; - complex_t u[PART_LEN1]; + const float gPow[2] = {0.9f, 0.1f}; - const float pi2 = 6.28318530717959f; + // Noise estimate constants. + const int noiseInitBlocks = 500 * aec->mult; + const float step = 0.1f; + const float ramp = 1.0002f; + const float gInitNoise[2] = {0.999f, 0.001f}; - // Generate a uniform random array on [0 1] - WebRtcSpl_RandUArray(randW16, PART_LEN, &aec->seed); - for (i = 0; i < PART_LEN; i++) { - rand[i] = ((float)randW16[i]) / 32768; - } + float nearend[PART_LEN]; + float* nearend_ptr = NULL; + float output[PART_LEN]; + float outputH[PART_LEN]; - // Reject LF noise - u[0][0] = 0; - u[0][1] = 0; - for (i = 1; i < PART_LEN1; i++) { - tmp = pi2 * rand[i - 1]; + float* xf_ptr = NULL; - noise = sqrtf(noisePow[i]); - u[i][0] = noise * cosf(tmp); - u[i][1] = -noise * sinf(tmp); + // Concatenate old and new nearend blocks. + if (aec->sampFreq == 32000) { + WebRtc_ReadBuffer(aec->nearFrBufH, (void**)&nearend_ptr, nearend, PART_LEN); + memcpy(aec->dBufH + PART_LEN, nearend_ptr, sizeof(nearend)); } - u[PART_LEN][1] = 0; + WebRtc_ReadBuffer(aec->nearFrBuf, (void**)&nearend_ptr, nearend, PART_LEN); + memcpy(aec->dBuf + PART_LEN, nearend_ptr, sizeof(nearend)); - for (i = 0; i < PART_LEN1; i++) { - // This is the proper weighting to match the background noise power - tmp = sqrtf(WEBRTC_SPL_MAX(1 - lambda[i] * lambda[i], 0)); - // tmp = 1 - lambda[i]; - efw[0][i] += tmp * u[i][0]; - efw[1][i] += tmp * u[i][1]; + // ---------- Ooura fft ---------- + +#ifdef WEBRTC_AEC_DEBUG_DUMP + { + float farend[PART_LEN]; + float* farend_ptr = NULL; + WebRtc_ReadBuffer(aec->far_time_buf, (void**)&farend_ptr, farend, 1); + OpenCoreDebugFiles(aec, &webrtc_aec_instance_count); + if (aec->farFile) { + rtc_WavWriteSamples(aec->farFile, farend_ptr, PART_LEN); + rtc_WavWriteSamples(aec->nearFile, nearend_ptr, PART_LEN); + aec->debugWritten += sizeof(int16_t) * PART_LEN; + if (aec->debugWritten >= AECDebugMaxSize()) { + AECDebugEnable(0); + } + } } +#endif - // For H band comfort noise - // TODO: don't compute noise and "tmp" twice. Use the previous results. - noiseAvg = 0.0; - tmpAvg = 0.0; - num = 0; - if (aec->sampFreq == 32000 && flagHbandCn == 1) { + // We should always have at least one element stored in |far_buf|. + assert(WebRtc_available_read(aec->far_buf) > 0); + WebRtc_ReadBuffer(aec->far_buf, (void**)&xf_ptr, &xf[0][0], 1); - // average noise scale - // average over second half of freq spectrum (i.e., 4->8khz) - // TODO: we shouldn't need num. We know how many elements we're summing. - for (i = PART_LEN1 >> 1; i < PART_LEN1; i++) { - num++; - noiseAvg += sqrtf(noisePow[i]); - } - noiseAvg /= (float)num; + // Near fft + memcpy(fft, aec->dBuf, sizeof(float) * PART_LEN2); + TimeToFrequency(fft, df, 0); - // average nlp scale - // average over second half of freq spectrum (i.e., 4->8khz) - // TODO: we shouldn't need num. We know how many elements we're summing. - num = 0; - for (i = PART_LEN1 >> 1; i < PART_LEN1; i++) { - num++; - tmpAvg += sqrtf(WEBRTC_SPL_MAX(1 - lambda[i] * lambda[i], 0)); - } - tmpAvg /= (float)num; + // Power smoothing + for (i = 0; i < PART_LEN1; i++) { + far_spectrum = (xf_ptr[i] * xf_ptr[i]) + + (xf_ptr[PART_LEN1 + i] * xf_ptr[PART_LEN1 + i]); + aec->xPow[i] = + gPow[0] * aec->xPow[i] + gPow[1] * aec->num_partitions * far_spectrum; + // Calculate absolute spectra + abs_far_spectrum[i] = sqrtf(far_spectrum); - // Use average noise for H band - // TODO: we should probably have a new random vector here. - // Reject LF noise - u[0][0] = 0; - u[0][1] = 0; - for (i = 1; i < PART_LEN1; i++) { - tmp = pi2 * rand[i - 1]; + near_spectrum = df[0][i] * df[0][i] + df[1][i] * df[1][i]; + aec->dPow[i] = gPow[0] * aec->dPow[i] + gPow[1] * near_spectrum; + // Calculate absolute spectra + abs_near_spectrum[i] = sqrtf(near_spectrum); + } - // Use average noise for H band - u[i][0] = noiseAvg * (float)cos(tmp); - u[i][1] = -noiseAvg * (float)sin(tmp); + // Estimate noise power. Wait until dPow is more stable. + if (aec->noiseEstCtr > 50) { + for (i = 0; i < PART_LEN1; i++) { + if (aec->dPow[i] < aec->dMinPow[i]) { + aec->dMinPow[i] = + (aec->dPow[i] + step * (aec->dMinPow[i] - aec->dPow[i])) * ramp; + } else { + aec->dMinPow[i] *= ramp; + } } - u[PART_LEN][1] = 0; + } + // Smooth increasing noise power from zero at the start, + // to avoid a sudden burst of comfort noise. + if (aec->noiseEstCtr < noiseInitBlocks) { + aec->noiseEstCtr++; for (i = 0; i < PART_LEN1; i++) { - // Use average NLP weight for H band - comfortNoiseHband[i][0] = tmpAvg * u[i][0]; - comfortNoiseHband[i][1] = tmpAvg * u[i][1]; + if (aec->dMinPow[i] > aec->dInitMinPow[i]) { + aec->dInitMinPow[i] = gInitNoise[0] * aec->dInitMinPow[i] + + gInitNoise[1] * aec->dMinPow[i]; + } else { + aec->dInitMinPow[i] = aec->dMinPow[i]; + } } + aec->noisePow = aec->dInitMinPow; + } else { + aec->noisePow = aec->dMinPow; } -} -static void InitLevel(PowerLevel* level) { - const float kBigFloat = 1E17f; + // Block wise delay estimation used for logging + if (aec->delay_logging_enabled) { + int delay_estimate = 0; + if (WebRtc_AddFarSpectrumFloat( + aec->delay_estimator_farend, abs_far_spectrum, PART_LEN1) == 0) { + delay_estimate = WebRtc_DelayEstimatorProcessFloat( + aec->delay_estimator, abs_near_spectrum, PART_LEN1); + if (delay_estimate >= 0) { + // Update delay estimate buffer. + aec->delay_histogram[delay_estimate]++; + } + } + } - level->averagelevel = 0; - level->framelevel = 0; - level->minlevel = kBigFloat; - level->frsum = 0; - level->sfrsum = 0; - level->frcounter = 0; - level->sfrcounter = 0; -} + // Update the xfBuf block position. + aec->xfBufBlockPos--; + if (aec->xfBufBlockPos == -1) { + aec->xfBufBlockPos = aec->num_partitions - 1; + } -static void InitStats(Stats* stats) { - stats->instant = kOffsetLevel; - stats->average = kOffsetLevel; - stats->max = kOffsetLevel; - stats->min = kOffsetLevel * (-1); - stats->sum = 0; - stats->hisum = 0; - stats->himean = kOffsetLevel; - stats->counter = 0; - stats->hicounter = 0; -} + // Buffer xf + memcpy(aec->xfBuf[0] + aec->xfBufBlockPos * PART_LEN1, + xf_ptr, + sizeof(float) * PART_LEN1); + memcpy(aec->xfBuf[1] + aec->xfBufBlockPos * PART_LEN1, + &xf_ptr[PART_LEN1], + sizeof(float) * PART_LEN1); -static void InitMetrics(AecCore* self) { - self->stateCounter = 0; - InitLevel(&self->farlevel); - InitLevel(&self->nearlevel); - InitLevel(&self->linoutlevel); - InitLevel(&self->nlpoutlevel); + memset(yf, 0, sizeof(yf)); - InitStats(&self->erl); - InitStats(&self->erle); - InitStats(&self->aNlp); - InitStats(&self->rerl); -} + // Filter far + WebRtcAec_FilterFar(aec, yf); -static void UpdateLevel(PowerLevel* level, float in[2][PART_LEN1]) { - // Do the energy calculation in the frequency domain. The FFT is performed on - // a segment of PART_LEN2 samples due to overlap, but we only want the energy - // of half that data (the last PART_LEN samples). Parseval's relation states - // that the energy is preserved according to - // - // \sum_{n=0}^{N-1} |x(n)|^2 = 1/N * \sum_{n=0}^{N-1} |X(n)|^2 - // = ENERGY, - // - // where N = PART_LEN2. Since we are only interested in calculating the energy - // for the last PART_LEN samples we approximate by calculating ENERGY and - // divide by 2, - // - // \sum_{n=N/2}^{N-1} |x(n)|^2 ~= ENERGY / 2 - // - // Since we deal with real valued time domain signals we only store frequency - // bins [0, PART_LEN], which is what |in| consists of. To calculate ENERGY we - // need to add the contribution from the missing part in - // [PART_LEN+1, PART_LEN2-1]. These values are, up to a phase shift, identical - // with the values in [1, PART_LEN-1], hence multiply those values by 2. This - // is the values in the for loop below, but multiplication by 2 and division - // by 2 cancel. + // Inverse fft to obtain echo estimate and error. + fft[0] = yf[0][0]; + fft[1] = yf[0][PART_LEN]; + for (i = 1; i < PART_LEN; i++) { + fft[2 * i] = yf[0][i]; + fft[2 * i + 1] = yf[1][i]; + } + aec_rdft_inverse_128(fft); - // TODO(bjornv): Investigate reusing energy calculations performed at other - // places in the code. - int k = 1; - // Imaginary parts are zero at end points and left out of the calculation. - float energy = (in[0][0] * in[0][0]) / 2; - energy += (in[0][PART_LEN] * in[0][PART_LEN]) / 2; + scale = 2.0f / PART_LEN2; + for (i = 0; i < PART_LEN; i++) { + y[i] = fft[PART_LEN + i] * scale; // fft scaling + } - for (k = 1; k < PART_LEN; k++) { - energy += (in[0][k] * in[0][k] + in[1][k] * in[1][k]); + for (i = 0; i < PART_LEN; i++) { + e[i] = nearend_ptr[i] - y[i]; } - energy /= PART_LEN2; - level->sfrsum += energy; - level->sfrcounter++; + // Error fft + memcpy(aec->eBuf + PART_LEN, e, sizeof(float) * PART_LEN); + memset(fft, 0, sizeof(float) * PART_LEN); + memcpy(fft + PART_LEN, e, sizeof(float) * PART_LEN); + // TODO(bjornv): Change to use TimeToFrequency(). + aec_rdft_forward_128(fft); - if (level->sfrcounter > subCountLen) { - level->framelevel = level->sfrsum / (subCountLen * PART_LEN); - level->sfrsum = 0; - level->sfrcounter = 0; - if (level->framelevel > 0) { - if (level->framelevel < level->minlevel) { - level->minlevel = level->framelevel; // New minimum. - } else { - level->minlevel *= (1 + 0.001f); // Small increase. - } - } - level->frcounter++; - level->frsum += level->framelevel; - if (level->frcounter > countLen) { - level->averagelevel = level->frsum / countLen; - level->frsum = 0; - level->frcounter = 0; - } + ef[1][0] = 0; + ef[1][PART_LEN] = 0; + ef[0][0] = fft[0]; + ef[0][PART_LEN] = fft[1]; + for (i = 1; i < PART_LEN; i++) { + ef[0][i] = fft[2 * i]; + ef[1][i] = fft[2 * i + 1]; } -} - -static void UpdateMetrics(AecCore* aec) { - float dtmp, dtmp2; - const float actThresholdNoisy = 8.0f; - const float actThresholdClean = 40.0f; - const float safety = 0.99995f; - const float noisyPower = 300000.0f; + if (aec->metricsMode == 1) { + // Note that the first PART_LEN samples in fft (before transformation) are + // zero. Hence, the scaling by two in UpdateLevel() should not be + // performed. That scaling is taken care of in UpdateMetrics() instead. + UpdateLevel(&aec->linoutlevel, ef); + } - float actThreshold; - float echo, suppressedEcho; + // Scale error signal inversely with far power. + WebRtcAec_ScaleErrorSignal(aec, ef); + WebRtcAec_FilterAdaptation(aec, fft, ef); + NonLinearProcessing(aec, output, outputH); - if (aec->echoState) { // Check if echo is likely present - aec->stateCounter++; + if (aec->metricsMode == 1) { + // Update power levels and echo metrics + UpdateLevel(&aec->farlevel, (float(*)[PART_LEN1])xf_ptr); + UpdateLevel(&aec->nearlevel, df); + UpdateMetrics(aec); } - if (aec->farlevel.frcounter == 0) { + // Store the output block. + WebRtc_WriteBuffer(aec->outFrBuf, output, PART_LEN); + // For H band + if (aec->sampFreq == 32000) { + WebRtc_WriteBuffer(aec->outFrBufH, outputH, PART_LEN); + } - if (aec->farlevel.minlevel < noisyPower) { - actThreshold = actThresholdClean; - } else { - actThreshold = actThresholdNoisy; +#ifdef WEBRTC_AEC_DEBUG_DUMP + OpenCoreDebugFiles(aec, &webrtc_aec_instance_count); + if (aec->outLinearFile) { + rtc_WavWriteSamples(aec->outLinearFile, e, PART_LEN); + rtc_WavWriteSamples(aec->outFile, output, PART_LEN); } +#endif +} - if ((aec->stateCounter > (0.5f * countLen * subCountLen)) && - (aec->farlevel.sfrcounter == 0) +int WebRtcAec_CreateAec(AecCore** aecInst) { + AecCore* aec = malloc(sizeof(AecCore)); + *aecInst = aec; + if (aec == NULL) { + return -1; + } - // Estimate in active far-end segments only - && - (aec->farlevel.averagelevel > - (actThreshold * aec->farlevel.minlevel))) { + aec->nearFrBuf = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(float)); + if (!aec->nearFrBuf) { + WebRtcAec_FreeAec(aec); + aec = NULL; + return -1; + } - // Subtract noise power - echo = aec->nearlevel.averagelevel - safety * aec->nearlevel.minlevel; + aec->outFrBuf = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(float)); + if (!aec->outFrBuf) { + WebRtcAec_FreeAec(aec); + aec = NULL; + return -1; + } - // ERL - dtmp = 10 * (float)log10(aec->farlevel.averagelevel / - aec->nearlevel.averagelevel + - 1e-10f); - dtmp2 = 10 * (float)log10(aec->farlevel.averagelevel / echo + 1e-10f); + aec->nearFrBufH = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(float)); + if (!aec->nearFrBufH) { + WebRtcAec_FreeAec(aec); + aec = NULL; + return -1; + } - aec->erl.instant = dtmp; - if (dtmp > aec->erl.max) { - aec->erl.max = dtmp; - } + aec->outFrBufH = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(float)); + if (!aec->outFrBufH) { + WebRtcAec_FreeAec(aec); + aec = NULL; + return -1; + } - if (dtmp < aec->erl.min) { - aec->erl.min = dtmp; - } + // Create far-end buffers. + aec->far_buf = + WebRtc_CreateBuffer(kBufSizePartitions, sizeof(float) * 2 * PART_LEN1); + if (!aec->far_buf) { + WebRtcAec_FreeAec(aec); + aec = NULL; + return -1; + } + aec->far_buf_windowed = + WebRtc_CreateBuffer(kBufSizePartitions, sizeof(float) * 2 * PART_LEN1); + if (!aec->far_buf_windowed) { + WebRtcAec_FreeAec(aec); + aec = NULL; + return -1; + } +#ifdef WEBRTC_AEC_DEBUG_DUMP + aec->instance_index = webrtc_aec_instance_count; + aec->far_time_buf = + WebRtc_CreateBuffer(kBufSizePartitions, sizeof(float) * PART_LEN); + if (!aec->far_time_buf) { + WebRtcAec_FreeAec(aec); + aec = NULL; + return -1; + } + aec->farFile = aec->nearFile = aec->outFile = aec->outLinearFile = NULL; + aec->debug_dump_count = 0; + aec->debugWritten = 0; + OpenCoreDebugFiles(aec, &webrtc_aec_instance_count); +#endif + aec->delay_estimator_farend = + WebRtc_CreateDelayEstimatorFarend(PART_LEN1, kHistorySizeBlocks); + if (aec->delay_estimator_farend == NULL) { + WebRtcAec_FreeAec(aec); + aec = NULL; + return -1; + } + aec->delay_estimator = WebRtc_CreateDelayEstimator( + aec->delay_estimator_farend, kLookaheadBlocks); + if (aec->delay_estimator == NULL) { + WebRtcAec_FreeAec(aec); + aec = NULL; + return -1; + } - aec->erl.counter++; - aec->erl.sum += dtmp; - aec->erl.average = aec->erl.sum / aec->erl.counter; + // Assembly optimization + WebRtcAec_FilterFar = FilterFar; + WebRtcAec_ScaleErrorSignal = ScaleErrorSignal; + WebRtcAec_FilterAdaptation = FilterAdaptation; + WebRtcAec_OverdriveAndSuppress = OverdriveAndSuppress; + WebRtcAec_ComfortNoise = ComfortNoise; + WebRtcAec_SubbandCoherence = SubbandCoherence; - // Upper mean - if (dtmp > aec->erl.average) { - aec->erl.hicounter++; - aec->erl.hisum += dtmp; - aec->erl.himean = aec->erl.hisum / aec->erl.hicounter; - } +#if defined(WEBRTC_ARCH_X86_FAMILY) + if (WebRtc_GetCPUInfo(kSSE2)) { + WebRtcAec_InitAec_SSE2(); + } +#endif - // A_NLP - dtmp = 10 * (float)log10(aec->nearlevel.averagelevel / - (2 * aec->linoutlevel.averagelevel) + - 1e-10f); +#if defined(MIPS_FPU_LE) + WebRtcAec_InitAec_mips(); +#endif - // subtract noise power - suppressedEcho = 2 * (aec->linoutlevel.averagelevel - - safety * aec->linoutlevel.minlevel); +#if defined(WEBRTC_DETECT_ARM_NEON) || defined(WEBRTC_ARCH_ARM_NEON) + WebRtcAec_InitAec_neon(); +#endif - dtmp2 = 10 * (float)log10(echo / suppressedEcho + 1e-10f); + aec_rdft_init(); - aec->aNlp.instant = dtmp2; - if (dtmp > aec->aNlp.max) { - aec->aNlp.max = dtmp; - } + return 0; +} - if (dtmp < aec->aNlp.min) { - aec->aNlp.min = dtmp; - } +int WebRtcAec_FreeAec(AecCore* aec) { + if (aec == NULL) { + return -1; + } - aec->aNlp.counter++; - aec->aNlp.sum += dtmp; - aec->aNlp.average = aec->aNlp.sum / aec->aNlp.counter; + WebRtc_FreeBuffer(aec->nearFrBuf); + WebRtc_FreeBuffer(aec->outFrBuf); - // Upper mean - if (dtmp > aec->aNlp.average) { - aec->aNlp.hicounter++; - aec->aNlp.hisum += dtmp; - aec->aNlp.himean = aec->aNlp.hisum / aec->aNlp.hicounter; - } + WebRtc_FreeBuffer(aec->nearFrBufH); + WebRtc_FreeBuffer(aec->outFrBufH); - // ERLE + WebRtc_FreeBuffer(aec->far_buf); + WebRtc_FreeBuffer(aec->far_buf_windowed); +#ifdef WEBRTC_AEC_DEBUG_DUMP + WebRtc_FreeBuffer(aec->far_time_buf); + if (aec->farFile) { + // we don't let one be open and not the others + rtc_WavClose(aec->farFile); + rtc_WavClose(aec->nearFile); + rtc_WavClose(aec->outFile); + rtc_WavClose(aec->outLinearFile); + } +#endif + WebRtc_FreeDelayEstimator(aec->delay_estimator); + WebRtc_FreeDelayEstimatorFarend(aec->delay_estimator_farend); - // subtract noise power - suppressedEcho = 2 * (aec->nlpoutlevel.averagelevel - - safety * aec->nlpoutlevel.minlevel); + free(aec); + return 0; +} - dtmp = 10 * (float)log10(aec->nearlevel.averagelevel / - (2 * aec->nlpoutlevel.averagelevel) + - 1e-10f); - dtmp2 = 10 * (float)log10(echo / suppressedEcho + 1e-10f); +#ifdef WEBRTC_AEC_DEBUG_DUMP +// Open a new Wav file for writing. If it was already open with a different +// sample frequency, close it first. +static void ReopenWav(rtc_WavWriter** wav_file, + const char* name, + int seq1, + int seq2, + int sample_rate) { + int written UNUSED; + char filename[64]; + if (*wav_file) { + if (rtc_WavSampleRate(*wav_file) == sample_rate) + return; + rtc_WavClose(*wav_file); + } + written = snprintf(filename, sizeof(filename), "%s%d-%d.wav", + name, seq1, seq2); + assert(written >= 0); // no output error + assert((size_t)written < sizeof(filename)); // buffer was large enough + *wav_file = rtc_WavOpen(filename, sample_rate, 1); +} +#endif // WEBRTC_AEC_DEBUG_DUMP - dtmp = dtmp2; - aec->erle.instant = dtmp; - if (dtmp > aec->erle.max) { - aec->erle.max = dtmp; - } +int WebRtcAec_InitAec(AecCore* aec, int sampFreq) { + int i; - if (dtmp < aec->erle.min) { - aec->erle.min = dtmp; - } + aec->sampFreq = sampFreq; - aec->erle.counter++; - aec->erle.sum += dtmp; - aec->erle.average = aec->erle.sum / aec->erle.counter; + if (sampFreq == 8000) { + aec->normal_mu = 0.6f; + aec->normal_error_threshold = 2e-6f; + } else { + aec->normal_mu = 0.5f; + aec->normal_error_threshold = 1.5e-6f; + } - // Upper mean - if (dtmp > aec->erle.average) { - aec->erle.hicounter++; - aec->erle.hisum += dtmp; - aec->erle.himean = aec->erle.hisum / aec->erle.hicounter; - } - } + if (WebRtc_InitBuffer(aec->nearFrBuf) == -1) { + return -1; + } - aec->stateCounter = 0; + if (WebRtc_InitBuffer(aec->outFrBuf) == -1) { + return -1; } -} -static void TimeToFrequency(float time_data[PART_LEN2], - float freq_data[2][PART_LEN1], - int window) { - int i = 0; + if (WebRtc_InitBuffer(aec->nearFrBufH) == -1) { + return -1; + } - // TODO(bjornv): Should we have a different function/wrapper for windowed FFT? - if (window) { - for (i = 0; i < PART_LEN; i++) { - time_data[i] *= sqrtHanning[i]; - time_data[PART_LEN + i] *= sqrtHanning[PART_LEN - i]; - } + if (WebRtc_InitBuffer(aec->outFrBufH) == -1) { + return -1; } - aec_rdft_forward_128(time_data); - // Reorder. - freq_data[1][0] = 0; - freq_data[1][PART_LEN] = 0; - freq_data[0][0] = time_data[0]; - freq_data[0][PART_LEN] = time_data[1]; - for (i = 1; i < PART_LEN; i++) { - freq_data[0][i] = time_data[2 * i]; - freq_data[1][i] = time_data[2 * i + 1]; + // Initialize far-end buffers. + if (WebRtc_InitBuffer(aec->far_buf) == -1) { + return -1; + } + if (WebRtc_InitBuffer(aec->far_buf_windowed) == -1) { + return -1; + } +#ifdef WEBRTC_AEC_DEBUG_DUMP + if (WebRtc_InitBuffer(aec->far_time_buf) == -1) { + return -1; } + { + int process_rate = sampFreq > 16000 ? 16000 : sampFreq; + ReopenWav(&aec->farFile, "aec_far", + aec->instance_index, aec->debug_dump_count, process_rate); + ReopenWav(&aec->nearFile, "aec_near", + aec->instance_index, aec->debug_dump_count, process_rate); + ReopenWav(&aec->outFile, "aec_out", + aec->instance_index, aec->debug_dump_count, process_rate); + ReopenWav(&aec->outLinearFile, "aec_out_linear", + aec->instance_index, aec->debug_dump_count, process_rate); + } + ++aec->debug_dump_count; +#endif + aec->system_delay = 0; + + if (WebRtc_InitDelayEstimatorFarend(aec->delay_estimator_farend) != 0) { + return -1; + } + if (WebRtc_InitDelayEstimator(aec->delay_estimator) != 0) { + return -1; + } + aec->delay_logging_enabled = 0; + memset(aec->delay_histogram, 0, sizeof(aec->delay_histogram)); + + aec->reported_delay_enabled = 1; + aec->extended_filter_enabled = 0; + aec->num_partitions = kNormalNumPartitions; + + // Update the delay estimator with filter length. We use half the + // |num_partitions| to take the echo path into account. In practice we say + // that the echo has a duration of maximum half |num_partitions|, which is not + // true, but serves as a crude measure. + WebRtc_set_allowed_offset(aec->delay_estimator, aec->num_partitions / 2); + // TODO(bjornv): I currently hard coded the enable. Once we've established + // that AECM has no performance regression, robust_validation will be enabled + // all the time and the APIs to turn it on/off will be removed. Hence, remove + // this line then. + WebRtc_enable_robust_validation(aec->delay_estimator, 1); + + // Default target suppression mode. + aec->nlp_mode = 1; + + // Sampling frequency multiplier + // SWB is processed as 160 frame size + if (aec->sampFreq == 32000) { + aec->mult = (short)aec->sampFreq / 16000; + } else { + aec->mult = (short)aec->sampFreq / 8000; + } + + aec->farBufWritePos = 0; + aec->farBufReadPos = 0; + + aec->inSamples = 0; + aec->outSamples = 0; + aec->knownDelay = 0; + + // Initialize buffers + memset(aec->dBuf, 0, sizeof(aec->dBuf)); + memset(aec->eBuf, 0, sizeof(aec->eBuf)); + // For H band + memset(aec->dBufH, 0, sizeof(aec->dBufH)); + + memset(aec->xPow, 0, sizeof(aec->xPow)); + memset(aec->dPow, 0, sizeof(aec->dPow)); + memset(aec->dInitMinPow, 0, sizeof(aec->dInitMinPow)); + aec->noisePow = aec->dInitMinPow; + aec->noiseEstCtr = 0; + + // Initial comfort noise power + for (i = 0; i < PART_LEN1; i++) { + aec->dMinPow[i] = 1.0e6f; + } + + // Holds the last block written to + aec->xfBufBlockPos = 0; + // TODO: Investigate need for these initializations. Deleting them doesn't + // change the output at all and yields 0.4% overall speedup. + memset(aec->xfBuf, 0, sizeof(complex_t) * kExtendedNumPartitions * PART_LEN1); + memset(aec->wfBuf, 0, sizeof(complex_t) * kExtendedNumPartitions * PART_LEN1); + memset(aec->sde, 0, sizeof(complex_t) * PART_LEN1); + memset(aec->sxd, 0, sizeof(complex_t) * PART_LEN1); + memset( + aec->xfwBuf, 0, sizeof(complex_t) * kExtendedNumPartitions * PART_LEN1); + memset(aec->se, 0, sizeof(float) * PART_LEN1); + + // To prevent numerical instability in the first block. + for (i = 0; i < PART_LEN1; i++) { + aec->sd[i] = 1; + } + for (i = 0; i < PART_LEN1; i++) { + aec->sx[i] = 1; + } + + memset(aec->hNs, 0, sizeof(aec->hNs)); + memset(aec->outBuf, 0, sizeof(float) * PART_LEN); + + aec->hNlFbMin = 1; + aec->hNlFbLocalMin = 1; + aec->hNlXdAvgMin = 1; + aec->hNlNewMin = 0; + aec->hNlMinCtr = 0; + aec->overDrive = 2; + aec->overDriveSm = 2; + aec->delayIdx = 0; + aec->stNearState = 0; + aec->echoState = 0; + aec->divergeState = 0; + + aec->seed = 777; + aec->delayEstCtr = 0; + + // Metrics disabled by default + aec->metricsMode = 0; + InitMetrics(aec); + + return 0; +} + +void WebRtcAec_BufferFarendPartition(AecCore* aec, const float* farend) { + float fft[PART_LEN2]; + float xf[2][PART_LEN1]; + + // Check if the buffer is full, and in that case flush the oldest data. + if (WebRtc_available_write(aec->far_buf) < 1) { + WebRtcAec_MoveFarReadPtr(aec, 1); + } + // Convert far-end partition to the frequency domain without windowing. + memcpy(fft, farend, sizeof(float) * PART_LEN2); + TimeToFrequency(fft, xf, 0); + WebRtc_WriteBuffer(aec->far_buf, &xf[0][0], 1); + + // Convert far-end partition to the frequency domain with windowing. + memcpy(fft, farend, sizeof(float) * PART_LEN2); + TimeToFrequency(fft, xf, 1); + WebRtc_WriteBuffer(aec->far_buf_windowed, &xf[0][0], 1); } +int WebRtcAec_MoveFarReadPtr(AecCore* aec, int elements) { + int elements_moved = WebRtc_MoveReadPtr(aec->far_buf_windowed, elements); + WebRtc_MoveReadPtr(aec->far_buf, elements); #ifdef WEBRTC_AEC_DEBUG_DUMP -static void -OpenCoreDebugFiles(AecCore* aec, - int *instance_count) -{ - int error = 0; - // XXX If this impacts performance (opening files here), move file open - // to Trace::set_aec_debug(), and just grab them here - if (AECDebug() && !aec->farFile) { - if (!aec->farFile) { - char path[1024]; - char *filename; - path[0] = '\0'; - AECDebugFilenameBase(path, sizeof(path)); - filename = path + strlen(path); - if (&path[sizeof(path)] - filename < 128) { - return; // avoid a lot of snprintf's and checks lower - } - if (filename > path) { -#ifdef XP_WIN - if (*(filename-1) != '\\') { - *filename++ = '\\'; - } -#else - if (*(filename-1) != '/') { - *filename++ = '/'; - } + WebRtc_MoveReadPtr(aec->far_time_buf, elements); #endif - } - sprintf(filename, "aec_far%d.pcm", webrtc_aec_instance_count); - aec->farFile = fopen(path, "wb"); - sprintf(filename, "aec_near%d.pcm", webrtc_aec_instance_count); - aec->nearFile = fopen(path, "wb"); - sprintf(filename, "aec_out%d.pcm", webrtc_aec_instance_count); - aec->outFile = fopen(path, "wb"); - sprintf(filename, "aec_out_linear%d.pcm", webrtc_aec_instance_count); - aec->outLinearFile = fopen(path, "wb"); - aec->debugWritten = 0; - if (!aec->outLinearFile || !aec->outFile || !aec->nearFile || !aec->farFile) { - error = 1; - } - } + aec->system_delay -= elements_moved * PART_LEN; + return elements_moved; +} + +void WebRtcAec_ProcessFrame(AecCore* aec, + const float* nearend, + const float* nearendH, + int knownDelay, + float* out, + float* outH) { + int out_elements = 0; + + // For each frame the process is as follows: + // 1) If the system_delay indicates on being too small for processing a + // frame we stuff the buffer with enough data for 10 ms. + // 2) Adjust the buffer to the system delay, by moving the read pointer. + // 3) TODO(bjornv): Investigate if we need to add this: + // If we can't move read pointer due to buffer size limitations we + // flush/stuff the buffer. + // 4) Process as many partitions as possible. + // 5) Update the |system_delay| with respect to a full frame of FRAME_LEN + // samples. Even though we will have data left to process (we work with + // partitions) we consider updating a whole frame, since that's the + // amount of data we input and output in audio_processing. + // 6) Update the outputs. + + // TODO(bjornv): Investigate how we should round the delay difference; right + // now we know that incoming |knownDelay| is underestimated when it's less + // than |aec->knownDelay|. We therefore, round (-32) in that direction. In + // the other direction, we don't have this situation, but might flush one + // partition too little. This can cause non-causality, which should be + // investigated. Maybe, allow for a non-symmetric rounding, like -16. + int move_elements = (aec->knownDelay - knownDelay - 32) / PART_LEN; + int moved_elements = 0; + + // TODO(bjornv): Change the near-end buffer handling to be the same as for + // far-end, that is, with a near_pre_buf. + // Buffer the near-end frame. + WebRtc_WriteBuffer(aec->nearFrBuf, nearend, FRAME_LEN); + // For H band + if (aec->sampFreq == 32000) { + WebRtc_WriteBuffer(aec->nearFrBufH, nearendH, FRAME_LEN); } - if (error || - (!AECDebug() && aec->farFile)) { - if (aec->farFile) { - fclose(aec->farFile); - } - if (aec->nearFile) { - fclose(aec->nearFile); - } - if (aec->outFile) { - fclose(aec->outFile); + + // 1) At most we process |aec->mult|+1 partitions in 10 ms. Make sure we + // have enough far-end data for that by stuffing the buffer if the + // |system_delay| indicates others. + if (aec->system_delay < FRAME_LEN) { + // We don't have enough data so we rewind 10 ms. + WebRtcAec_MoveFarReadPtr(aec, -(aec->mult + 1)); + } + + // 2) Compensate for a possible change in the system delay. + WebRtc_MoveReadPtr(aec->far_buf_windowed, move_elements); + moved_elements = WebRtc_MoveReadPtr(aec->far_buf, move_elements); + aec->knownDelay -= moved_elements * PART_LEN; +#ifdef WEBRTC_AEC_DEBUG_DUMP + WebRtc_MoveReadPtr(aec->far_time_buf, move_elements); +#endif + + // 4) Process as many blocks as possible. + while (WebRtc_available_read(aec->nearFrBuf) >= PART_LEN) { + ProcessBlock(aec); + } + + // 5) Update system delay with respect to the entire frame. + aec->system_delay -= FRAME_LEN; + + // 6) Update output frame. + // Stuff the out buffer if we have less than a frame to output. + // This should only happen for the first frame. + out_elements = (int)WebRtc_available_read(aec->outFrBuf); + if (out_elements < FRAME_LEN) { + WebRtc_MoveReadPtr(aec->outFrBuf, out_elements - FRAME_LEN); + if (aec->sampFreq == 32000) { + WebRtc_MoveReadPtr(aec->outFrBufH, out_elements - FRAME_LEN); } - if (aec->outLinearFile) { - fclose(aec->outLinearFile); + } + // Obtain an output frame. + WebRtc_ReadBuffer(aec->outFrBuf, NULL, out, FRAME_LEN); + // For H band. + if (aec->sampFreq == 32000) { + WebRtc_ReadBuffer(aec->outFrBufH, NULL, outH, FRAME_LEN); + } +} + +int WebRtcAec_GetDelayMetricsCore(AecCore* self, int* median, int* std) { + int i = 0; + int delay_values = 0; + int num_delay_values = 0; + int my_median = 0; + const int kMsPerBlock = PART_LEN / (self->mult * 8); + float l1_norm = 0; + + assert(self != NULL); + assert(median != NULL); + assert(std != NULL); + + if (self->delay_logging_enabled == 0) { + // Logging disabled. + return -1; + } + + // Get number of delay values since last update. + for (i = 0; i < kHistorySizeBlocks; i++) { + num_delay_values += self->delay_histogram[i]; + } + if (num_delay_values == 0) { + // We have no new delay value data. Even though -1 is a valid estimate, it + // will practically never be used since multiples of |kMsPerBlock| will + // always be returned. + *median = -1; + *std = -1; + return 0; + } + + delay_values = num_delay_values >> 1; // Start value for median count down. + // Get median of delay values since last update. + for (i = 0; i < kHistorySizeBlocks; i++) { + delay_values -= self->delay_histogram[i]; + if (delay_values < 0) { + my_median = i; + break; } - aec->outLinearFile = aec->outFile = aec->nearFile = aec->farFile = NULL; - aec->debugWritten = 0; } + // Account for lookahead. + *median = (my_median - kLookaheadBlocks) * kMsPerBlock; + + // Calculate the L1 norm, with median value as central moment. + for (i = 0; i < kHistorySizeBlocks; i++) { + l1_norm += (float)abs(i - my_median) * self->delay_histogram[i]; + } + *std = (int)(l1_norm / (float)num_delay_values + 0.5f) * kMsPerBlock; + + // Reset histogram. + memset(self->delay_histogram, 0, sizeof(self->delay_histogram)); + + return 0; } + +int WebRtcAec_echo_state(AecCore* self) { return self->echoState; } + +void WebRtcAec_GetEchoStats(AecCore* self, + Stats* erl, + Stats* erle, + Stats* a_nlp) { + assert(erl != NULL); + assert(erle != NULL); + assert(a_nlp != NULL); + *erl = self->erl; + *erle = self->erle; + *a_nlp = self->aNlp; +} + +#ifdef WEBRTC_AEC_DEBUG_DUMP +void* WebRtcAec_far_time_buf(AecCore* self) { return self->far_time_buf; } #endif + +void WebRtcAec_SetConfigCore(AecCore* self, + int nlp_mode, + int metrics_mode, + int delay_logging) { + assert(nlp_mode >= 0 && nlp_mode < 3); + self->nlp_mode = nlp_mode; + self->metricsMode = metrics_mode; + if (self->metricsMode) { + InitMetrics(self); + } + self->delay_logging_enabled = delay_logging; + if (self->delay_logging_enabled) { + memset(self->delay_histogram, 0, sizeof(self->delay_histogram)); + } +} + +void WebRtcAec_enable_reported_delay(AecCore* self, int enable) { + self->reported_delay_enabled = enable; +} + +int WebRtcAec_reported_delay_enabled(AecCore* self) { + return self->reported_delay_enabled; +} + +void WebRtcAec_enable_delay_correction(AecCore* self, int enable) { + self->extended_filter_enabled = enable; + self->num_partitions = enable ? kExtendedNumPartitions : kNormalNumPartitions; + // Update the delay estimator with filter length. See InitAEC() for details. + WebRtc_set_allowed_offset(self->delay_estimator, self->num_partitions / 2); +} + +int WebRtcAec_delay_correction_enabled(AecCore* self) { + return self->extended_filter_enabled; +} + +int WebRtcAec_system_delay(AecCore* self) { return self->system_delay; } + +void WebRtcAec_SetSystemDelay(AecCore* self, int delay) { + assert(delay >= 0); + self->system_delay = delay; +} + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.h 2015-02-03 14:33:35.000000000 +0000 @@ -22,17 +22,6 @@ #define PART_LEN1 (PART_LEN + 1) // Unique fft coefficients #define PART_LEN2 (PART_LEN * 2) // Length of partition * 2 -// Delay estimator constants, used for logging. -enum { - kMaxDelayBlocks = 60 -}; -enum { - kLookaheadBlocks = 15 -}; -enum { - kHistorySizeBlocks = kMaxDelayBlocks + kLookaheadBlocks -}; - typedef float complex_t[2]; // For performance reasons, some arrays of complex numbers are replaced by twice // as long arrays of float, all the real parts followed by all the imaginary @@ -65,14 +54,20 @@ int WebRtcAec_FreeAec(AecCore* aec); int WebRtcAec_InitAec(AecCore* aec, int sampFreq); void WebRtcAec_InitAec_SSE2(void); +#if defined(MIPS_FPU_LE) +void WebRtcAec_InitAec_mips(void); +#endif +#if defined(WEBRTC_DETECT_ARM_NEON) || defined(WEBRTC_ARCH_ARM_NEON) +void WebRtcAec_InitAec_neon(void); +#endif void WebRtcAec_BufferFarendPartition(AecCore* aec, const float* farend); void WebRtcAec_ProcessFrame(AecCore* aec, - const short* nearend, - const short* nearendH, + const float* nearend, + const float* nearendH, int knownDelay, - int16_t* out, - int16_t* outH); + float* out, + float* outH); // A helper function to call WebRtc_MoveReadPtr() for all far-end buffers. // Returns the number of elements moved, and adjusts |system_delay| by the @@ -101,6 +96,12 @@ int metrics_mode, int delay_logging); +// Non-zero enables, zero disables. +void WebRtcAec_enable_reported_delay(AecCore* self, int enable); + +// Returns non-zero if reported delay is enabled and zero if disabled. +int WebRtcAec_reported_delay_enabled(AecCore* self); + // We now interpret delay correction to mean an extended filter length feature. // We reuse the delay correction infrastructure to avoid changes through to // libjingle. See details along with |DelayCorrection| in diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_internal.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_internal.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_internal.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_internal.h 2015-02-03 14:33:35.000000000 +0000 @@ -11,10 +11,8 @@ #ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_INTERNAL_H_ #define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_INTERNAL_H_ -#ifdef WEBRTC_AEC_DEBUG_DUMP -#include -#endif - +#include "webrtc/common_audio/wav_file.h" +#include "webrtc/modules/audio_processing/aec/aec_common.h" #include "webrtc/modules/audio_processing/aec/aec_core.h" #include "webrtc/modules/audio_processing/utility/ring_buffer.h" #include "webrtc/typedefs.h" @@ -26,6 +24,17 @@ }; static const int kNormalNumPartitions = 12; +// Delay estimator constants, used for logging. +enum { + kMaxDelayBlocks = 60 +}; +enum { + kLookaheadBlocks = 15 +}; +enum { + kHistorySizeBlocks = kMaxDelayBlocks + kLookaheadBlocks +}; + // Extended filter adaptation parameters. // TODO(ajm): No narrowband tuning yet. static const float kExtendedMu = 0.4f; @@ -122,17 +131,26 @@ void* delay_estimator_farend; void* delay_estimator; + int reported_delay_enabled; // 0 = disabled, otherwise enabled. // 1 = extended filter mode enabled, 0 = disabled. int extended_filter_enabled; // Runtime selection of number of filter partitions. int num_partitions; #ifdef WEBRTC_AEC_DEBUG_DUMP + // Sequence number of this AEC instance, so that different instances can + // choose different dump file names. + int instance_index; + + // Number of times we've restarted dumping; used to pick new dump file names + // each time. + int debug_dump_count; + RingBuffer* far_time_buf; - FILE* farFile; - FILE* nearFile; - FILE* outFile; - FILE* outLinearFile; + rtc_WavWriter* farFile; + rtc_WavWriter* nearFile; + rtc_WavWriter* outFile; + rtc_WavWriter* outLinearFile; uint32_t debugWritten; #endif }; @@ -152,4 +170,19 @@ float efw[2][PART_LEN1]); extern WebRtcAec_OverdriveAndSuppress_t WebRtcAec_OverdriveAndSuppress; +typedef void (*WebRtcAec_ComfortNoise_t)(AecCore* aec, + float efw[2][PART_LEN1], + complex_t* comfortNoiseHband, + const float* noisePow, + const float* lambda); +extern WebRtcAec_ComfortNoise_t WebRtcAec_ComfortNoise; + +typedef void (*WebRtcAec_SubbandCoherence_t)(AecCore* aec, + float efw[2][PART_LEN1], + float xfw[2][PART_LEN1], + float* fft, + float* cohde, + float* cohxd); +extern WebRtcAec_SubbandCoherence_t WebRtcAec_SubbandCoherence; + #endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_INTERNAL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_mips.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_mips.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_mips.c 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_mips.c 2015-02-03 14:33:35.000000000 +0000 @@ -0,0 +1,774 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * The core AEC algorithm, which is presented with time-aligned signals. + */ + +#include "webrtc/modules/audio_processing/aec/aec_core.h" + +#include + +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/modules/audio_processing/aec/aec_core_internal.h" +#include "webrtc/modules/audio_processing/aec/aec_rdft.h" + +static const int flagHbandCn = 1; // flag for adding comfort noise in H band +extern const float WebRtcAec_weightCurve[65]; +extern const float WebRtcAec_overDriveCurve[65]; + +void WebRtcAec_ComfortNoise_mips(AecCore* aec, + float efw[2][PART_LEN1], + complex_t* comfortNoiseHband, + const float* noisePow, + const float* lambda) { + int i, num; + float rand[PART_LEN]; + float noise, noiseAvg, tmp, tmpAvg; + int16_t randW16[PART_LEN]; + complex_t u[PART_LEN1]; + + const float pi2 = 6.28318530717959f; + const float pi2t = pi2 / 32768; + + // Generate a uniform random array on [0 1] + WebRtcSpl_RandUArray(randW16, PART_LEN, &aec->seed); + + int16_t* randWptr = randW16; + float randTemp, randTemp2, randTemp3, randTemp4; + int32_t tmp1s, tmp2s, tmp3s, tmp4s; + + for (i = 0; i < PART_LEN; i+=4) { + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "lh %[tmp1s], 0(%[randWptr]) \n\t" + "lh %[tmp2s], 2(%[randWptr]) \n\t" + "lh %[tmp3s], 4(%[randWptr]) \n\t" + "lh %[tmp4s], 6(%[randWptr]) \n\t" + "mtc1 %[tmp1s], %[randTemp] \n\t" + "mtc1 %[tmp2s], %[randTemp2] \n\t" + "mtc1 %[tmp3s], %[randTemp3] \n\t" + "mtc1 %[tmp4s], %[randTemp4] \n\t" + "cvt.s.w %[randTemp], %[randTemp] \n\t" + "cvt.s.w %[randTemp2], %[randTemp2] \n\t" + "cvt.s.w %[randTemp3], %[randTemp3] \n\t" + "cvt.s.w %[randTemp4], %[randTemp4] \n\t" + "addiu %[randWptr], %[randWptr], 8 \n\t" + "mul.s %[randTemp], %[randTemp], %[pi2t] \n\t" + "mul.s %[randTemp2], %[randTemp2], %[pi2t] \n\t" + "mul.s %[randTemp3], %[randTemp3], %[pi2t] \n\t" + "mul.s %[randTemp4], %[randTemp4], %[pi2t] \n\t" + ".set pop \n\t" + : [randWptr] "+r" (randWptr), [randTemp] "=&f" (randTemp), + [randTemp2] "=&f" (randTemp2), [randTemp3] "=&f" (randTemp3), + [randTemp4] "=&f" (randTemp4), [tmp1s] "=&r" (tmp1s), + [tmp2s] "=&r" (tmp2s), [tmp3s] "=&r" (tmp3s), + [tmp4s] "=&r" (tmp4s) + : [pi2t] "f" (pi2t) + : "memory" + ); + + u[i+1][0] = cosf(randTemp); + u[i+1][1] = sinf(randTemp); + u[i+2][0] = cosf(randTemp2); + u[i+2][1] = sinf(randTemp2); + u[i+3][0] = cosf(randTemp3); + u[i+3][1] = sinf(randTemp3); + u[i+4][0] = cosf(randTemp4); + u[i+4][1] = sinf(randTemp4); + } + + // Reject LF noise + float* u_ptr = &u[1][0]; + float noise2, noise3, noise4; + float tmp1f, tmp2f, tmp3f, tmp4f, tmp5f, tmp6f, tmp7f, tmp8f; + + u[0][0] = 0; + u[0][1] = 0; + for (i = 1; i < PART_LEN1; i+=4) { + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "lwc1 %[noise], 4(%[noisePow]) \n\t" + "lwc1 %[noise2], 8(%[noisePow]) \n\t" + "lwc1 %[noise3], 12(%[noisePow]) \n\t" + "lwc1 %[noise4], 16(%[noisePow]) \n\t" + "sqrt.s %[noise], %[noise] \n\t" + "sqrt.s %[noise2], %[noise2] \n\t" + "sqrt.s %[noise3], %[noise3] \n\t" + "sqrt.s %[noise4], %[noise4] \n\t" + "lwc1 %[tmp1f], 0(%[u_ptr]) \n\t" + "lwc1 %[tmp2f], 4(%[u_ptr]) \n\t" + "lwc1 %[tmp3f], 8(%[u_ptr]) \n\t" + "lwc1 %[tmp4f], 12(%[u_ptr]) \n\t" + "lwc1 %[tmp5f], 16(%[u_ptr]) \n\t" + "lwc1 %[tmp6f], 20(%[u_ptr]) \n\t" + "lwc1 %[tmp7f], 24(%[u_ptr]) \n\t" + "lwc1 %[tmp8f], 28(%[u_ptr]) \n\t" + "addiu %[noisePow], %[noisePow], 16 \n\t" + "mul.s %[tmp1f], %[tmp1f], %[noise] \n\t" + "mul.s %[tmp2f], %[tmp2f], %[noise] \n\t" + "mul.s %[tmp3f], %[tmp3f], %[noise2] \n\t" + "mul.s %[tmp4f], %[tmp4f], %[noise2] \n\t" + "mul.s %[tmp5f], %[tmp5f], %[noise3] \n\t" + "mul.s %[tmp6f], %[tmp6f], %[noise3] \n\t" + "swc1 %[tmp1f], 0(%[u_ptr]) \n\t" + "swc1 %[tmp3f], 8(%[u_ptr]) \n\t" + "mul.s %[tmp8f], %[tmp8f], %[noise4] \n\t" + "mul.s %[tmp7f], %[tmp7f], %[noise4] \n\t" + "neg.s %[tmp2f] \n\t" + "neg.s %[tmp4f] \n\t" + "neg.s %[tmp6f] \n\t" + "neg.s %[tmp8f] \n\t" + "swc1 %[tmp5f], 16(%[u_ptr]) \n\t" + "swc1 %[tmp7f], 24(%[u_ptr]) \n\t" + "swc1 %[tmp2f], 4(%[u_ptr]) \n\t" + "swc1 %[tmp4f], 12(%[u_ptr]) \n\t" + "swc1 %[tmp6f], 20(%[u_ptr]) \n\t" + "swc1 %[tmp8f], 28(%[u_ptr]) \n\t" + "addiu %[u_ptr], %[u_ptr], 32 \n\t" + ".set pop \n\t" + : [u_ptr] "+r" (u_ptr), [noisePow] "+r" (noisePow), + [noise] "=&f" (noise), [noise2] "=&f" (noise2), + [noise3] "=&f" (noise3), [noise4] "=&f" (noise4), + [tmp1f] "=&f" (tmp1f), [tmp2f] "=&f" (tmp2f), + [tmp3f] "=&f" (tmp3f), [tmp4f] "=&f" (tmp4f), + [tmp5f] "=&f" (tmp5f), [tmp6f] "=&f" (tmp6f), + [tmp7f] "=&f" (tmp7f), [tmp8f] "=&f" (tmp8f) + : + : "memory" + ); + } + u[PART_LEN][1] = 0; + noisePow -= PART_LEN; + + u_ptr = &u[0][0]; + float* u_ptr_end = &u[PART_LEN][0]; + float* efw_ptr_0 = &efw[0][0]; + float* efw_ptr_1 = &efw[1][0]; + float tmp9f, tmp10f; + const float tmp1c = 1.0; + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "1: \n\t" + "lwc1 %[tmp1f], 0(%[lambda]) \n\t" + "lwc1 %[tmp6f], 4(%[lambda]) \n\t" + "addiu %[lambda], %[lambda], 8 \n\t" + "c.lt.s %[tmp1f], %[tmp1c] \n\t" + "bc1f 4f \n\t" + " nop \n\t" + "c.lt.s %[tmp6f], %[tmp1c] \n\t" + "bc1f 3f \n\t" + " nop \n\t" + "2: \n\t" + "mul.s %[tmp1f], %[tmp1f], %[tmp1f] \n\t" + "mul.s %[tmp6f], %[tmp6f], %[tmp6f] \n\t" + "sub.s %[tmp1f], %[tmp1c], %[tmp1f] \n\t" + "sub.s %[tmp6f], %[tmp1c], %[tmp6f] \n\t" + "sqrt.s %[tmp1f], %[tmp1f] \n\t" + "sqrt.s %[tmp6f], %[tmp6f] \n\t" + "lwc1 %[tmp2f], 0(%[efw_ptr_0]) \n\t" + "lwc1 %[tmp3f], 0(%[u_ptr]) \n\t" + "lwc1 %[tmp7f], 4(%[efw_ptr_0]) \n\t" + "lwc1 %[tmp8f], 8(%[u_ptr]) \n\t" + "lwc1 %[tmp4f], 0(%[efw_ptr_1]) \n\t" + "lwc1 %[tmp5f], 4(%[u_ptr]) \n\t" + "lwc1 %[tmp9f], 4(%[efw_ptr_1]) \n\t" + "lwc1 %[tmp10f], 12(%[u_ptr]) \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[tmp3f], %[tmp1f], %[tmp3f] \n\t" + "add.s %[tmp2f], %[tmp2f], %[tmp3f] \n\t" + "mul.s %[tmp3f], %[tmp1f], %[tmp5f] \n\t" + "add.s %[tmp4f], %[tmp4f], %[tmp3f] \n\t" + "mul.s %[tmp3f], %[tmp6f], %[tmp8f] \n\t" + "add.s %[tmp7f], %[tmp7f], %[tmp3f] \n\t" + "mul.s %[tmp3f], %[tmp6f], %[tmp10f] \n\t" + "add.s %[tmp9f], %[tmp9f], %[tmp3f] \n\t" +#else // #if !defined(MIPS32_R2_LE) + "madd.s %[tmp2f], %[tmp2f], %[tmp1f], %[tmp3f] \n\t" + "madd.s %[tmp4f], %[tmp4f], %[tmp1f], %[tmp5f] \n\t" + "madd.s %[tmp7f], %[tmp7f], %[tmp6f], %[tmp8f] \n\t" + "madd.s %[tmp9f], %[tmp9f], %[tmp6f], %[tmp10f] \n\t" +#endif // #if !defined(MIPS32_R2_LE) + "swc1 %[tmp2f], 0(%[efw_ptr_0]) \n\t" + "swc1 %[tmp4f], 0(%[efw_ptr_1]) \n\t" + "swc1 %[tmp7f], 4(%[efw_ptr_0]) \n\t" + "b 5f \n\t" + " swc1 %[tmp9f], 4(%[efw_ptr_1]) \n\t" + "3: \n\t" + "mul.s %[tmp1f], %[tmp1f], %[tmp1f] \n\t" + "sub.s %[tmp1f], %[tmp1c], %[tmp1f] \n\t" + "sqrt.s %[tmp1f], %[tmp1f] \n\t" + "lwc1 %[tmp2f], 0(%[efw_ptr_0]) \n\t" + "lwc1 %[tmp3f], 0(%[u_ptr]) \n\t" + "lwc1 %[tmp4f], 0(%[efw_ptr_1]) \n\t" + "lwc1 %[tmp5f], 4(%[u_ptr]) \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[tmp3f], %[tmp1f], %[tmp3f] \n\t" + "add.s %[tmp2f], %[tmp2f], %[tmp3f] \n\t" + "mul.s %[tmp3f], %[tmp1f], %[tmp5f] \n\t" + "add.s %[tmp4f], %[tmp4f], %[tmp3f] \n\t" +#else // #if !defined(MIPS32_R2_LE) + "madd.s %[tmp2f], %[tmp2f], %[tmp1f], %[tmp3f] \n\t" + "madd.s %[tmp4f], %[tmp4f], %[tmp1f], %[tmp5f] \n\t" +#endif // #if !defined(MIPS32_R2_LE) + "swc1 %[tmp2f], 0(%[efw_ptr_0]) \n\t" + "b 5f \n\t" + " swc1 %[tmp4f], 0(%[efw_ptr_1]) \n\t" + "4: \n\t" + "c.lt.s %[tmp6f], %[tmp1c] \n\t" + "bc1f 5f \n\t" + " nop \n\t" + "mul.s %[tmp6f], %[tmp6f], %[tmp6f] \n\t" + "sub.s %[tmp6f], %[tmp1c], %[tmp6f] \n\t" + "sqrt.s %[tmp6f], %[tmp6f] \n\t" + "lwc1 %[tmp7f], 4(%[efw_ptr_0]) \n\t" + "lwc1 %[tmp8f], 8(%[u_ptr]) \n\t" + "lwc1 %[tmp9f], 4(%[efw_ptr_1]) \n\t" + "lwc1 %[tmp10f], 12(%[u_ptr]) \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[tmp3f], %[tmp6f], %[tmp8f] \n\t" + "add.s %[tmp7f], %[tmp7f], %[tmp3f] \n\t" + "mul.s %[tmp3f], %[tmp6f], %[tmp10f] \n\t" + "add.s %[tmp9f], %[tmp9f], %[tmp3f] \n\t" +#else // #if !defined(MIPS32_R2_LE) + "madd.s %[tmp7f], %[tmp7f], %[tmp6f], %[tmp8f] \n\t" + "madd.s %[tmp9f], %[tmp9f], %[tmp6f], %[tmp10f] \n\t" +#endif // #if !defined(MIPS32_R2_LE) + "swc1 %[tmp7f], 4(%[efw_ptr_0]) \n\t" + "swc1 %[tmp9f], 4(%[efw_ptr_1]) \n\t" + "5: \n\t" + "addiu %[u_ptr], %[u_ptr], 16 \n\t" + "addiu %[efw_ptr_0], %[efw_ptr_0], 8 \n\t" + "bne %[u_ptr], %[u_ptr_end], 1b \n\t" + " addiu %[efw_ptr_1], %[efw_ptr_1], 8 \n\t" + ".set pop \n\t" + : [lambda] "+r" (lambda), [u_ptr] "+r" (u_ptr), + [efw_ptr_0] "+r" (efw_ptr_0), [efw_ptr_1] "+r" (efw_ptr_1), + [tmp1f] "=&f" (tmp1f), [tmp2f] "=&f" (tmp2f), [tmp3f] "=&f" (tmp3f), + [tmp4f] "=&f" (tmp4f), [tmp5f] "=&f" (tmp5f), + [tmp6f] "=&f" (tmp6f), [tmp7f] "=&f" (tmp7f), [tmp8f] "=&f" (tmp8f), + [tmp9f] "=&f" (tmp9f), [tmp10f] "=&f" (tmp10f) + : [tmp1c] "f" (tmp1c), [u_ptr_end] "r" (u_ptr_end) + : "memory" + ); + + lambda -= PART_LEN; + tmp = sqrtf(WEBRTC_SPL_MAX(1 - lambda[PART_LEN] * lambda[PART_LEN], 0)); + //tmp = 1 - lambda[i]; + efw[0][PART_LEN] += tmp * u[PART_LEN][0]; + efw[1][PART_LEN] += tmp * u[PART_LEN][1]; + + // For H band comfort noise + // TODO: don't compute noise and "tmp" twice. Use the previous results. + noiseAvg = 0.0; + tmpAvg = 0.0; + num = 0; + if (aec->sampFreq == 32000 && flagHbandCn == 1) { + for (i = 0; i < PART_LEN; i++) { + rand[i] = ((float)randW16[i]) / 32768; + } + + // average noise scale + // average over second half of freq spectrum (i.e., 4->8khz) + // TODO: we shouldn't need num. We know how many elements we're summing. + for (i = PART_LEN1 >> 1; i < PART_LEN1; i++) { + num++; + noiseAvg += sqrtf(noisePow[i]); + } + noiseAvg /= (float)num; + + // average nlp scale + // average over second half of freq spectrum (i.e., 4->8khz) + // TODO: we shouldn't need num. We know how many elements we're summing. + num = 0; + for (i = PART_LEN1 >> 1; i < PART_LEN1; i++) { + num++; + tmpAvg += sqrtf(WEBRTC_SPL_MAX(1 - lambda[i] * lambda[i], 0)); + } + tmpAvg /= (float)num; + + // Use average noise for H band + // TODO: we should probably have a new random vector here. + // Reject LF noise + u[0][0] = 0; + u[0][1] = 0; + for (i = 1; i < PART_LEN1; i++) { + tmp = pi2 * rand[i - 1]; + + // Use average noise for H band + u[i][0] = noiseAvg * (float)cos(tmp); + u[i][1] = -noiseAvg * (float)sin(tmp); + } + u[PART_LEN][1] = 0; + + for (i = 0; i < PART_LEN1; i++) { + // Use average NLP weight for H band + comfortNoiseHband[i][0] = tmpAvg * u[i][0]; + comfortNoiseHband[i][1] = tmpAvg * u[i][1]; + } + } +} + +void WebRtcAec_FilterFar_mips(AecCore* aec, float yf[2][PART_LEN1]) { + int i; + for (i = 0; i < aec->num_partitions; i++) { + int xPos = (i + aec->xfBufBlockPos) * PART_LEN1; + int pos = i * PART_LEN1; + // Check for wrap + if (i + aec->xfBufBlockPos >= aec->num_partitions) { + xPos -= aec->num_partitions * (PART_LEN1); + } + float* yf0 = yf[0]; + float* yf1 = yf[1]; + float* aRe = aec->xfBuf[0] + xPos; + float* aIm = aec->xfBuf[1] + xPos; + float* bRe = aec->wfBuf[0] + pos; + float* bIm = aec->wfBuf[1] + pos; + float f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13; + int len = PART_LEN1 >> 1; + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "1: \n\t" + "lwc1 %[f0], 0(%[aRe]) \n\t" + "lwc1 %[f1], 0(%[bRe]) \n\t" + "lwc1 %[f2], 0(%[bIm]) \n\t" + "lwc1 %[f3], 0(%[aIm]) \n\t" + "lwc1 %[f4], 4(%[aRe]) \n\t" + "lwc1 %[f5], 4(%[bRe]) \n\t" + "lwc1 %[f6], 4(%[bIm]) \n\t" + "mul.s %[f8], %[f0], %[f1] \n\t" + "mul.s %[f0], %[f0], %[f2] \n\t" + "mul.s %[f9], %[f4], %[f5] \n\t" + "mul.s %[f4], %[f4], %[f6] \n\t" + "lwc1 %[f7], 4(%[aIm]) \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[f12], %[f2], %[f3] \n\t" + "mul.s %[f1], %[f3], %[f1] \n\t" + "mul.s %[f11], %[f6], %[f7] \n\t" + "addiu %[aRe], %[aRe], 8 \n\t" + "addiu %[aIm], %[aIm], 8 \n\t" + "addiu %[len], %[len], -1 \n\t" + "sub.s %[f8], %[f8], %[f12] \n\t" + "mul.s %[f12], %[f7], %[f5] \n\t" + "lwc1 %[f2], 0(%[yf0]) \n\t" + "add.s %[f1], %[f0], %[f1] \n\t" + "lwc1 %[f3], 0(%[yf1]) \n\t" + "sub.s %[f9], %[f9], %[f11] \n\t" + "lwc1 %[f6], 4(%[yf0]) \n\t" + "add.s %[f4], %[f4], %[f12] \n\t" +#else // #if !defined(MIPS32_R2_LE) + "addiu %[aRe], %[aRe], 8 \n\t" + "addiu %[aIm], %[aIm], 8 \n\t" + "addiu %[len], %[len], -1 \n\t" + "nmsub.s %[f8], %[f8], %[f2], %[f3] \n\t" + "lwc1 %[f2], 0(%[yf0]) \n\t" + "madd.s %[f1], %[f0], %[f3], %[f1] \n\t" + "lwc1 %[f3], 0(%[yf1]) \n\t" + "nmsub.s %[f9], %[f9], %[f6], %[f7] \n\t" + "lwc1 %[f6], 4(%[yf0]) \n\t" + "madd.s %[f4], %[f4], %[f7], %[f5] \n\t" +#endif // #if !defined(MIPS32_R2_LE) + "lwc1 %[f5], 4(%[yf1]) \n\t" + "add.s %[f2], %[f2], %[f8] \n\t" + "addiu %[bRe], %[bRe], 8 \n\t" + "addiu %[bIm], %[bIm], 8 \n\t" + "add.s %[f3], %[f3], %[f1] \n\t" + "add.s %[f6], %[f6], %[f9] \n\t" + "add.s %[f5], %[f5], %[f4] \n\t" + "swc1 %[f2], 0(%[yf0]) \n\t" + "swc1 %[f3], 0(%[yf1]) \n\t" + "swc1 %[f6], 4(%[yf0]) \n\t" + "swc1 %[f5], 4(%[yf1]) \n\t" + "addiu %[yf0], %[yf0], 8 \n\t" + "bgtz %[len], 1b \n\t" + " addiu %[yf1], %[yf1], 8 \n\t" + "lwc1 %[f0], 0(%[aRe]) \n\t" + "lwc1 %[f1], 0(%[bRe]) \n\t" + "lwc1 %[f2], 0(%[bIm]) \n\t" + "lwc1 %[f3], 0(%[aIm]) \n\t" + "mul.s %[f8], %[f0], %[f1] \n\t" + "mul.s %[f0], %[f0], %[f2] \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[f12], %[f2], %[f3] \n\t" + "mul.s %[f1], %[f3], %[f1] \n\t" + "sub.s %[f8], %[f8], %[f12] \n\t" + "lwc1 %[f2], 0(%[yf0]) \n\t" + "add.s %[f1], %[f0], %[f1] \n\t" + "lwc1 %[f3], 0(%[yf1]) \n\t" +#else // #if !defined(MIPS32_R2_LE) + "nmsub.s %[f8], %[f8], %[f2], %[f3] \n\t" + "lwc1 %[f2], 0(%[yf0]) \n\t" + "madd.s %[f1], %[f0], %[f3], %[f1] \n\t" + "lwc1 %[f3], 0(%[yf1]) \n\t" +#endif // #if !defined(MIPS32_R2_LE) + "add.s %[f2], %[f2], %[f8] \n\t" + "add.s %[f3], %[f3], %[f1] \n\t" + "swc1 %[f2], 0(%[yf0]) \n\t" + "swc1 %[f3], 0(%[yf1]) \n\t" + ".set pop \n\t" + : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), + [f3] "=&f" (f3), [f4] "=&f" (f4), [f5] "=&f" (f5), + [f6] "=&f" (f6), [f7] "=&f" (f7), [f8] "=&f" (f8), + [f9] "=&f" (f9), [f10] "=&f" (f10), [f11] "=&f" (f11), + [f12] "=&f" (f12), [f13] "=&f" (f13), [aRe] "+r" (aRe), + [aIm] "+r" (aIm), [bRe] "+r" (bRe), [bIm] "+r" (bIm), + [yf0] "+r" (yf0), [yf1] "+r" (yf1), [len] "+r" (len) + : + : "memory" + ); + } +} + +void WebRtcAec_FilterAdaptation_mips(AecCore* aec, + float* fft, + float ef[2][PART_LEN1]) { + int i; + for (i = 0; i < aec->num_partitions; i++) { + int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1); + int pos; + // Check for wrap + if (i + aec->xfBufBlockPos >= aec->num_partitions) { + xPos -= aec->num_partitions * PART_LEN1; + } + + pos = i * PART_LEN1; + float* aRe = aec->xfBuf[0] + xPos; + float* aIm = aec->xfBuf[1] + xPos; + float* bRe = ef[0]; + float* bIm = ef[1]; + float* fft_tmp; + + float f0, f1, f2, f3, f4, f5, f6 ,f7, f8, f9, f10, f11, f12; + int len = PART_LEN >> 1; + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[fft_tmp], %[fft], 0 \n\t" + "1: \n\t" + "lwc1 %[f0], 0(%[aRe]) \n\t" + "lwc1 %[f1], 0(%[bRe]) \n\t" + "lwc1 %[f2], 0(%[bIm]) \n\t" + "lwc1 %[f4], 4(%[aRe]) \n\t" + "lwc1 %[f5], 4(%[bRe]) \n\t" + "lwc1 %[f6], 4(%[bIm]) \n\t" + "addiu %[aRe], %[aRe], 8 \n\t" + "addiu %[bRe], %[bRe], 8 \n\t" + "mul.s %[f8], %[f0], %[f1] \n\t" + "mul.s %[f0], %[f0], %[f2] \n\t" + "lwc1 %[f3], 0(%[aIm]) \n\t" + "mul.s %[f9], %[f4], %[f5] \n\t" + "lwc1 %[f7], 4(%[aIm]) \n\t" + "mul.s %[f4], %[f4], %[f6] \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[f10], %[f3], %[f2] \n\t" + "mul.s %[f1], %[f3], %[f1] \n\t" + "mul.s %[f11], %[f7], %[f6] \n\t" + "mul.s %[f5], %[f7], %[f5] \n\t" + "addiu %[aIm], %[aIm], 8 \n\t" + "addiu %[bIm], %[bIm], 8 \n\t" + "addiu %[len], %[len], -1 \n\t" + "add.s %[f8], %[f8], %[f10] \n\t" + "sub.s %[f1], %[f0], %[f1] \n\t" + "add.s %[f9], %[f9], %[f11] \n\t" + "sub.s %[f5], %[f4], %[f5] \n\t" +#else // #if !defined(MIPS32_R2_LE) + "addiu %[aIm], %[aIm], 8 \n\t" + "addiu %[bIm], %[bIm], 8 \n\t" + "addiu %[len], %[len], -1 \n\t" + "madd.s %[f8], %[f8], %[f3], %[f2] \n\t" + "nmsub.s %[f1], %[f0], %[f3], %[f1] \n\t" + "madd.s %[f9], %[f9], %[f7], %[f6] \n\t" + "nmsub.s %[f5], %[f4], %[f7], %[f5] \n\t" +#endif // #if !defined(MIPS32_R2_LE) + "swc1 %[f8], 0(%[fft_tmp]) \n\t" + "swc1 %[f1], 4(%[fft_tmp]) \n\t" + "swc1 %[f9], 8(%[fft_tmp]) \n\t" + "swc1 %[f5], 12(%[fft_tmp]) \n\t" + "bgtz %[len], 1b \n\t" + " addiu %[fft_tmp], %[fft_tmp], 16 \n\t" + "lwc1 %[f0], 0(%[aRe]) \n\t" + "lwc1 %[f1], 0(%[bRe]) \n\t" + "lwc1 %[f2], 0(%[bIm]) \n\t" + "lwc1 %[f3], 0(%[aIm]) \n\t" + "mul.s %[f8], %[f0], %[f1] \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[f10], %[f3], %[f2] \n\t" + "add.s %[f8], %[f8], %[f10] \n\t" +#else // #if !defined(MIPS32_R2_LE) + "madd.s %[f8], %[f8], %[f3], %[f2] \n\t" +#endif // #if !defined(MIPS32_R2_LE) + "swc1 %[f8], 4(%[fft]) \n\t" + ".set pop \n\t" + : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), + [f3] "=&f" (f3), [f4] "=&f" (f4), [f5] "=&f" (f5), + [f6] "=&f" (f6), [f7] "=&f" (f7), [f8] "=&f" (f8), + [f9] "=&f" (f9), [f10] "=&f" (f10), [f11] "=&f" (f11), + [f12] "=&f" (f12), [aRe] "+r" (aRe), [aIm] "+r" (aIm), + [bRe] "+r" (bRe), [bIm] "+r" (bIm), [fft_tmp] "=&r" (fft_tmp), + [len] "+r" (len) + : [fft] "r" (fft) + : "memory" + ); + + aec_rdft_inverse_128(fft); + memset(fft + PART_LEN, 0, sizeof(float) * PART_LEN); + + // fft scaling + { + float scale = 2.0f / PART_LEN2; + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[fft_tmp], %[fft], 0 \n\t" + "addiu %[len], $zero, 8 \n\t" + "1: \n\t" + "addiu %[len], %[len], -1 \n\t" + "lwc1 %[f0], 0(%[fft_tmp]) \n\t" + "lwc1 %[f1], 4(%[fft_tmp]) \n\t" + "lwc1 %[f2], 8(%[fft_tmp]) \n\t" + "lwc1 %[f3], 12(%[fft_tmp]) \n\t" + "mul.s %[f0], %[f0], %[scale] \n\t" + "mul.s %[f1], %[f1], %[scale] \n\t" + "mul.s %[f2], %[f2], %[scale] \n\t" + "mul.s %[f3], %[f3], %[scale] \n\t" + "lwc1 %[f4], 16(%[fft_tmp]) \n\t" + "lwc1 %[f5], 20(%[fft_tmp]) \n\t" + "lwc1 %[f6], 24(%[fft_tmp]) \n\t" + "lwc1 %[f7], 28(%[fft_tmp]) \n\t" + "mul.s %[f4], %[f4], %[scale] \n\t" + "mul.s %[f5], %[f5], %[scale] \n\t" + "mul.s %[f6], %[f6], %[scale] \n\t" + "mul.s %[f7], %[f7], %[scale] \n\t" + "swc1 %[f0], 0(%[fft_tmp]) \n\t" + "swc1 %[f1], 4(%[fft_tmp]) \n\t" + "swc1 %[f2], 8(%[fft_tmp]) \n\t" + "swc1 %[f3], 12(%[fft_tmp]) \n\t" + "swc1 %[f4], 16(%[fft_tmp]) \n\t" + "swc1 %[f5], 20(%[fft_tmp]) \n\t" + "swc1 %[f6], 24(%[fft_tmp]) \n\t" + "swc1 %[f7], 28(%[fft_tmp]) \n\t" + "bgtz %[len], 1b \n\t" + " addiu %[fft_tmp], %[fft_tmp], 32 \n\t" + ".set pop \n\t" + : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), + [f3] "=&f" (f3), [f4] "=&f" (f4), [f5] "=&f" (f5), + [f6] "=&f" (f6), [f7] "=&f" (f7), [len] "=&r" (len), + [fft_tmp] "=&r" (fft_tmp) + : [scale] "f" (scale), [fft] "r" (fft) + : "memory" + ); + } + aec_rdft_forward_128(fft); + aRe = aec->wfBuf[0] + pos; + aIm = aec->wfBuf[1] + pos; + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[fft_tmp], %[fft], 0 \n\t" + "addiu %[len], $zero, 31 \n\t" + "lwc1 %[f0], 0(%[aRe]) \n\t" + "lwc1 %[f1], 0(%[fft_tmp]) \n\t" + "lwc1 %[f2], 256(%[aRe]) \n\t" + "lwc1 %[f3], 4(%[fft_tmp]) \n\t" + "lwc1 %[f4], 4(%[aRe]) \n\t" + "lwc1 %[f5], 8(%[fft_tmp]) \n\t" + "lwc1 %[f6], 4(%[aIm]) \n\t" + "lwc1 %[f7], 12(%[fft_tmp]) \n\t" + "add.s %[f0], %[f0], %[f1] \n\t" + "add.s %[f2], %[f2], %[f3] \n\t" + "add.s %[f4], %[f4], %[f5] \n\t" + "add.s %[f6], %[f6], %[f7] \n\t" + "addiu %[fft_tmp], %[fft_tmp], 16 \n\t" + "swc1 %[f0], 0(%[aRe]) \n\t" + "swc1 %[f2], 256(%[aRe]) \n\t" + "swc1 %[f4], 4(%[aRe]) \n\t" + "addiu %[aRe], %[aRe], 8 \n\t" + "swc1 %[f6], 4(%[aIm]) \n\t" + "addiu %[aIm], %[aIm], 8 \n\t" + "1: \n\t" + "lwc1 %[f0], 0(%[aRe]) \n\t" + "lwc1 %[f1], 0(%[fft_tmp]) \n\t" + "lwc1 %[f2], 0(%[aIm]) \n\t" + "lwc1 %[f3], 4(%[fft_tmp]) \n\t" + "lwc1 %[f4], 4(%[aRe]) \n\t" + "lwc1 %[f5], 8(%[fft_tmp]) \n\t" + "lwc1 %[f6], 4(%[aIm]) \n\t" + "lwc1 %[f7], 12(%[fft_tmp]) \n\t" + "add.s %[f0], %[f0], %[f1] \n\t" + "add.s %[f2], %[f2], %[f3] \n\t" + "add.s %[f4], %[f4], %[f5] \n\t" + "add.s %[f6], %[f6], %[f7] \n\t" + "addiu %[len], %[len], -1 \n\t" + "addiu %[fft_tmp], %[fft_tmp], 16 \n\t" + "swc1 %[f0], 0(%[aRe]) \n\t" + "swc1 %[f2], 0(%[aIm]) \n\t" + "swc1 %[f4], 4(%[aRe]) \n\t" + "addiu %[aRe], %[aRe], 8 \n\t" + "swc1 %[f6], 4(%[aIm]) \n\t" + "bgtz %[len], 1b \n\t" + " addiu %[aIm], %[aIm], 8 \n\t" + ".set pop \n\t" + : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), + [f3] "=&f" (f3), [f4] "=&f" (f4), [f5] "=&f" (f5), + [f6] "=&f" (f6), [f7] "=&f" (f7), [len] "=&r" (len), + [fft_tmp] "=&r" (fft_tmp), [aRe] "+r" (aRe), [aIm] "+r" (aIm) + : [fft] "r" (fft) + : "memory" + ); + } +} + +void WebRtcAec_OverdriveAndSuppress_mips(AecCore* aec, + float hNl[PART_LEN1], + const float hNlFb, + float efw[2][PART_LEN1]) { + int i; + const float one = 1.0; + float* p_hNl; + float* p_efw0; + float* p_efw1; + float* p_WebRtcAec_wC; + float temp1, temp2, temp3, temp4; + + p_hNl = &hNl[0]; + p_efw0 = &efw[0][0]; + p_efw1 = &efw[1][0]; + p_WebRtcAec_wC = (float*)&WebRtcAec_weightCurve[0]; + + for (i = 0; i < PART_LEN1; i++) { + // Weight subbands + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "lwc1 %[temp1], 0(%[p_hNl]) \n\t" + "lwc1 %[temp2], 0(%[p_wC]) \n\t" + "c.lt.s %[hNlFb], %[temp1] \n\t" + "bc1f 1f \n\t" + " mul.s %[temp3], %[temp2], %[hNlFb] \n\t" + "sub.s %[temp4], %[one], %[temp2] \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[temp1], %[temp1], %[temp4] \n\t" + "add.s %[temp1], %[temp3], %[temp1] \n\t" +#else // #if !defined(MIPS32_R2_LE) + "madd.s %[temp1], %[temp3], %[temp1], %[temp4] \n\t" +#endif // #if !defined(MIPS32_R2_LE) + "swc1 %[temp1], 0(%[p_hNl]) \n\t" + "1: \n\t" + "addiu %[p_wC], %[p_wC], 4 \n\t" + ".set pop \n\t" + : [temp1] "=&f" (temp1), [temp2] "=&f" (temp2), [temp3] "=&f" (temp3), + [temp4] "=&f" (temp4), [p_wC] "+r" (p_WebRtcAec_wC) + : [hNlFb] "f" (hNlFb), [one] "f" (one), [p_hNl] "r" (p_hNl) + : "memory" + ); + + hNl[i] = powf(hNl[i], aec->overDriveSm * WebRtcAec_overDriveCurve[i]); + + __asm __volatile ( + "lwc1 %[temp1], 0(%[p_hNl]) \n\t" + "lwc1 %[temp3], 0(%[p_efw1]) \n\t" + "lwc1 %[temp2], 0(%[p_efw0]) \n\t" + "addiu %[p_hNl], %[p_hNl], 4 \n\t" + "mul.s %[temp3], %[temp3], %[temp1] \n\t" + "mul.s %[temp2], %[temp2], %[temp1] \n\t" + "addiu %[p_efw0], %[p_efw0], 4 \n\t" + "addiu %[p_efw1], %[p_efw1], 4 \n\t" + "neg.s %[temp4], %[temp3] \n\t" + "swc1 %[temp2], -4(%[p_efw0]) \n\t" + "swc1 %[temp4], -4(%[p_efw1]) \n\t" + : [temp1] "=&f" (temp1), [temp2] "=&f" (temp2), [temp3] "=&f" (temp3), + [temp4] "=&f" (temp4), [p_efw0] "+r" (p_efw0), [p_efw1] "+r" (p_efw1), + [p_hNl] "+r" (p_hNl) + : + : "memory" + ); + } +} + +void WebRtcAec_ScaleErrorSignal_mips(AecCore* aec, float ef[2][PART_LEN1]) { + const float mu = aec->extended_filter_enabled ? kExtendedMu : aec->normal_mu; + const float error_threshold = aec->extended_filter_enabled + ? kExtendedErrorThreshold + : aec->normal_error_threshold; + int len = (PART_LEN1); + float* ef0 = ef[0]; + float* ef1 = ef[1]; + float* xPow = aec->xPow; + float fac1 = 1e-10f; + float err_th2 = error_threshold * error_threshold; + float f0, f1, f2; +#if !defined(MIPS32_R2_LE) + float f3; +#endif + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "1: \n\t" + "lwc1 %[f0], 0(%[xPow]) \n\t" + "lwc1 %[f1], 0(%[ef0]) \n\t" + "lwc1 %[f2], 0(%[ef1]) \n\t" + "add.s %[f0], %[f0], %[fac1] \n\t" + "div.s %[f1], %[f1], %[f0] \n\t" + "div.s %[f2], %[f2], %[f0] \n\t" + "mul.s %[f0], %[f1], %[f1] \n\t" +#if defined(MIPS32_R2_LE) + "madd.s %[f0], %[f0], %[f2], %[f2] \n\t" +#else + "mul.s %[f3], %[f2], %[f2] \n\t" + "add.s %[f0], %[f0], %[f3] \n\t" +#endif + "c.le.s %[f0], %[err_th2] \n\t" + "nop \n\t" + "bc1t 2f \n\t" + " nop \n\t" + "sqrt.s %[f0], %[f0] \n\t" + "add.s %[f0], %[f0], %[fac1] \n\t" + "div.s %[f0], %[err_th], %[f0] \n\t" + "mul.s %[f1], %[f1], %[f0] \n\t" + "mul.s %[f2], %[f2], %[f0] \n\t" + "2: \n\t" + "mul.s %[f1], %[f1], %[mu] \n\t" + "mul.s %[f2], %[f2], %[mu] \n\t" + "swc1 %[f1], 0(%[ef0]) \n\t" + "swc1 %[f2], 0(%[ef1]) \n\t" + "addiu %[len], %[len], -1 \n\t" + "addiu %[xPow], %[xPow], 4 \n\t" + "addiu %[ef0], %[ef0], 4 \n\t" + "bgtz %[len], 1b \n\t" + " addiu %[ef1], %[ef1], 4 \n\t" + ".set pop \n\t" + : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), +#if !defined(MIPS32_R2_LE) + [f3] "=&f" (f3), +#endif + [xPow] "+r" (xPow), [ef0] "+r" (ef0), [ef1] "+r" (ef1), + [len] "+r" (len) + : [fac1] "f" (fac1), [err_th2] "f" (err_th2), [mu] "f" (mu), + [err_th] "f" (error_threshold) + : "memory" + ); +} + +void WebRtcAec_InitAec_mips(void) { + WebRtcAec_FilterFar = WebRtcAec_FilterFar_mips; + WebRtcAec_FilterAdaptation = WebRtcAec_FilterAdaptation_mips; + WebRtcAec_ScaleErrorSignal = WebRtcAec_ScaleErrorSignal_mips; + WebRtcAec_ComfortNoise = WebRtcAec_ComfortNoise_mips; + WebRtcAec_OverdriveAndSuppress = WebRtcAec_OverdriveAndSuppress_mips; +} + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_neon.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_neon.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_neon.c 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_neon.c 2015-02-03 14:33:35.000000000 +0000 @@ -0,0 +1,733 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * The core AEC algorithm, neon version of speed-critical functions. + * + * Based on aec_core_sse2.c. + */ + +#include +#include +#include // memset + +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/modules/audio_processing/aec/aec_common.h" +#include "webrtc/modules/audio_processing/aec/aec_core_internal.h" +#include "webrtc/modules/audio_processing/aec/aec_rdft.h" + +enum { kShiftExponentIntoTopMantissa = 8 }; +enum { kFloatExponentShift = 23 }; + +__inline static float MulRe(float aRe, float aIm, float bRe, float bIm) { + return aRe * bRe - aIm * bIm; +} + +__inline static float MulIm(float aRe, float aIm, float bRe, float bIm) { + return aRe * bIm + aIm * bRe; +} + +static void FilterFarNEON(AecCore* aec, float yf[2][PART_LEN1]) { + int i; + const int num_partitions = aec->num_partitions; + for (i = 0; i < num_partitions; i++) { + int j; + int xPos = (i + aec->xfBufBlockPos) * PART_LEN1; + int pos = i * PART_LEN1; + // Check for wrap + if (i + aec->xfBufBlockPos >= num_partitions) { + xPos -= num_partitions * PART_LEN1; + } + + // vectorized code (four at once) + for (j = 0; j + 3 < PART_LEN1; j += 4) { + const float32x4_t xfBuf_re = vld1q_f32(&aec->xfBuf[0][xPos + j]); + const float32x4_t xfBuf_im = vld1q_f32(&aec->xfBuf[1][xPos + j]); + const float32x4_t wfBuf_re = vld1q_f32(&aec->wfBuf[0][pos + j]); + const float32x4_t wfBuf_im = vld1q_f32(&aec->wfBuf[1][pos + j]); + const float32x4_t yf_re = vld1q_f32(&yf[0][j]); + const float32x4_t yf_im = vld1q_f32(&yf[1][j]); + const float32x4_t a = vmulq_f32(xfBuf_re, wfBuf_re); + const float32x4_t e = vmlsq_f32(a, xfBuf_im, wfBuf_im); + const float32x4_t c = vmulq_f32(xfBuf_re, wfBuf_im); + const float32x4_t f = vmlaq_f32(c, xfBuf_im, wfBuf_re); + const float32x4_t g = vaddq_f32(yf_re, e); + const float32x4_t h = vaddq_f32(yf_im, f); + vst1q_f32(&yf[0][j], g); + vst1q_f32(&yf[1][j], h); + } + // scalar code for the remaining items. + for (; j < PART_LEN1; j++) { + yf[0][j] += MulRe(aec->xfBuf[0][xPos + j], + aec->xfBuf[1][xPos + j], + aec->wfBuf[0][pos + j], + aec->wfBuf[1][pos + j]); + yf[1][j] += MulIm(aec->xfBuf[0][xPos + j], + aec->xfBuf[1][xPos + j], + aec->wfBuf[0][pos + j], + aec->wfBuf[1][pos + j]); + } + } +} + +static float32x4_t vdivq_f32(float32x4_t a, float32x4_t b) { + int i; + float32x4_t x = vrecpeq_f32(b); + // from arm documentation + // The Newton-Raphson iteration: + // x[n+1] = x[n] * (2 - d * x[n]) + // converges to (1/d) if x0 is the result of VRECPE applied to d. + // + // Note: The precision did not improve after 2 iterations. + for (i = 0; i < 2; i++) { + x = vmulq_f32(vrecpsq_f32(b, x), x); + } + // a/b = a*(1/b) + return vmulq_f32(a, x); +} + +static float32x4_t vsqrtq_f32(float32x4_t s) { + int i; + float32x4_t x = vrsqrteq_f32(s); + + // Code to handle sqrt(0). + // If the input to sqrtf() is zero, a zero will be returned. + // If the input to vrsqrteq_f32() is zero, positive infinity is returned. + const uint32x4_t vec_p_inf = vdupq_n_u32(0x7F800000); + // check for divide by zero + const uint32x4_t div_by_zero = vceqq_u32(vec_p_inf, vreinterpretq_u32_f32(x)); + // zero out the positive infinity results + x = vreinterpretq_f32_u32(vandq_u32(vmvnq_u32(div_by_zero), + vreinterpretq_u32_f32(x))); + // from arm documentation + // The Newton-Raphson iteration: + // x[n+1] = x[n] * (3 - d * (x[n] * x[n])) / 2) + // converges to (1/√d) if x0 is the result of VRSQRTE applied to d. + // + // Note: The precision did not improve after 2 iterations. + for (i = 0; i < 2; i++) { + x = vmulq_f32(vrsqrtsq_f32(vmulq_f32(x, x), s), x); + } + // sqrt(s) = s * 1/sqrt(s) + return vmulq_f32(s, x);; +} + +static void ScaleErrorSignalNEON(AecCore* aec, float ef[2][PART_LEN1]) { + const float mu = aec->extended_filter_enabled ? kExtendedMu : aec->normal_mu; + const float error_threshold = aec->extended_filter_enabled ? + kExtendedErrorThreshold : aec->normal_error_threshold; + const float32x4_t k1e_10f = vdupq_n_f32(1e-10f); + const float32x4_t kMu = vmovq_n_f32(mu); + const float32x4_t kThresh = vmovq_n_f32(error_threshold); + int i; + // vectorized code (four at once) + for (i = 0; i + 3 < PART_LEN1; i += 4) { + const float32x4_t xPow = vld1q_f32(&aec->xPow[i]); + const float32x4_t ef_re_base = vld1q_f32(&ef[0][i]); + const float32x4_t ef_im_base = vld1q_f32(&ef[1][i]); + const float32x4_t xPowPlus = vaddq_f32(xPow, k1e_10f); + float32x4_t ef_re = vdivq_f32(ef_re_base, xPowPlus); + float32x4_t ef_im = vdivq_f32(ef_im_base, xPowPlus); + const float32x4_t ef_re2 = vmulq_f32(ef_re, ef_re); + const float32x4_t ef_sum2 = vmlaq_f32(ef_re2, ef_im, ef_im); + const float32x4_t absEf = vsqrtq_f32(ef_sum2); + const uint32x4_t bigger = vcgtq_f32(absEf, kThresh); + const float32x4_t absEfPlus = vaddq_f32(absEf, k1e_10f); + const float32x4_t absEfInv = vdivq_f32(kThresh, absEfPlus); + uint32x4_t ef_re_if = vreinterpretq_u32_f32(vmulq_f32(ef_re, absEfInv)); + uint32x4_t ef_im_if = vreinterpretq_u32_f32(vmulq_f32(ef_im, absEfInv)); + uint32x4_t ef_re_u32 = vandq_u32(vmvnq_u32(bigger), + vreinterpretq_u32_f32(ef_re)); + uint32x4_t ef_im_u32 = vandq_u32(vmvnq_u32(bigger), + vreinterpretq_u32_f32(ef_im)); + ef_re_if = vandq_u32(bigger, ef_re_if); + ef_im_if = vandq_u32(bigger, ef_im_if); + ef_re_u32 = vorrq_u32(ef_re_u32, ef_re_if); + ef_im_u32 = vorrq_u32(ef_im_u32, ef_im_if); + ef_re = vmulq_f32(vreinterpretq_f32_u32(ef_re_u32), kMu); + ef_im = vmulq_f32(vreinterpretq_f32_u32(ef_im_u32), kMu); + vst1q_f32(&ef[0][i], ef_re); + vst1q_f32(&ef[1][i], ef_im); + } + // scalar code for the remaining items. + for (; i < PART_LEN1; i++) { + float abs_ef; + ef[0][i] /= (aec->xPow[i] + 1e-10f); + ef[1][i] /= (aec->xPow[i] + 1e-10f); + abs_ef = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]); + + if (abs_ef > error_threshold) { + abs_ef = error_threshold / (abs_ef + 1e-10f); + ef[0][i] *= abs_ef; + ef[1][i] *= abs_ef; + } + + // Stepsize factor + ef[0][i] *= mu; + ef[1][i] *= mu; + } +} + +static void FilterAdaptationNEON(AecCore* aec, + float* fft, + float ef[2][PART_LEN1]) { + int i; + const int num_partitions = aec->num_partitions; + for (i = 0; i < num_partitions; i++) { + int xPos = (i + aec->xfBufBlockPos) * PART_LEN1; + int pos = i * PART_LEN1; + int j; + // Check for wrap + if (i + aec->xfBufBlockPos >= num_partitions) { + xPos -= num_partitions * PART_LEN1; + } + + // Process the whole array... + for (j = 0; j < PART_LEN; j += 4) { + // Load xfBuf and ef. + const float32x4_t xfBuf_re = vld1q_f32(&aec->xfBuf[0][xPos + j]); + const float32x4_t xfBuf_im = vld1q_f32(&aec->xfBuf[1][xPos + j]); + const float32x4_t ef_re = vld1q_f32(&ef[0][j]); + const float32x4_t ef_im = vld1q_f32(&ef[1][j]); + // Calculate the product of conjugate(xfBuf) by ef. + // re(conjugate(a) * b) = aRe * bRe + aIm * bIm + // im(conjugate(a) * b)= aRe * bIm - aIm * bRe + const float32x4_t a = vmulq_f32(xfBuf_re, ef_re); + const float32x4_t e = vmlaq_f32(a, xfBuf_im, ef_im); + const float32x4_t c = vmulq_f32(xfBuf_re, ef_im); + const float32x4_t f = vmlsq_f32(c, xfBuf_im, ef_re); + // Interleave real and imaginary parts. + const float32x4x2_t g_n_h = vzipq_f32(e, f); + // Store + vst1q_f32(&fft[2 * j + 0], g_n_h.val[0]); + vst1q_f32(&fft[2 * j + 4], g_n_h.val[1]); + } + // ... and fixup the first imaginary entry. + fft[1] = MulRe(aec->xfBuf[0][xPos + PART_LEN], + -aec->xfBuf[1][xPos + PART_LEN], + ef[0][PART_LEN], + ef[1][PART_LEN]); + + aec_rdft_inverse_128(fft); + memset(fft + PART_LEN, 0, sizeof(float) * PART_LEN); + + // fft scaling + { + const float scale = 2.0f / PART_LEN2; + const float32x4_t scale_ps = vmovq_n_f32(scale); + for (j = 0; j < PART_LEN; j += 4) { + const float32x4_t fft_ps = vld1q_f32(&fft[j]); + const float32x4_t fft_scale = vmulq_f32(fft_ps, scale_ps); + vst1q_f32(&fft[j], fft_scale); + } + } + aec_rdft_forward_128(fft); + + { + const float wt1 = aec->wfBuf[1][pos]; + aec->wfBuf[0][pos + PART_LEN] += fft[1]; + for (j = 0; j < PART_LEN; j += 4) { + float32x4_t wtBuf_re = vld1q_f32(&aec->wfBuf[0][pos + j]); + float32x4_t wtBuf_im = vld1q_f32(&aec->wfBuf[1][pos + j]); + const float32x4_t fft0 = vld1q_f32(&fft[2 * j + 0]); + const float32x4_t fft4 = vld1q_f32(&fft[2 * j + 4]); + const float32x4x2_t fft_re_im = vuzpq_f32(fft0, fft4); + wtBuf_re = vaddq_f32(wtBuf_re, fft_re_im.val[0]); + wtBuf_im = vaddq_f32(wtBuf_im, fft_re_im.val[1]); + + vst1q_f32(&aec->wfBuf[0][pos + j], wtBuf_re); + vst1q_f32(&aec->wfBuf[1][pos + j], wtBuf_im); + } + aec->wfBuf[1][pos] = wt1; + } + } +} + +static float32x4_t vpowq_f32(float32x4_t a, float32x4_t b) { + // a^b = exp2(b * log2(a)) + // exp2(x) and log2(x) are calculated using polynomial approximations. + float32x4_t log2_a, b_log2_a, a_exp_b; + + // Calculate log2(x), x = a. + { + // To calculate log2(x), we decompose x like this: + // x = y * 2^n + // n is an integer + // y is in the [1.0, 2.0) range + // + // log2(x) = log2(y) + n + // n can be evaluated by playing with float representation. + // log2(y) in a small range can be approximated, this code uses an order + // five polynomial approximation. The coefficients have been + // estimated with the Remez algorithm and the resulting + // polynomial has a maximum relative error of 0.00086%. + + // Compute n. + // This is done by masking the exponent, shifting it into the top bit of + // the mantissa, putting eight into the biased exponent (to shift/ + // compensate the fact that the exponent has been shifted in the top/ + // fractional part and finally getting rid of the implicit leading one + // from the mantissa by substracting it out. + const uint32x4_t vec_float_exponent_mask = vdupq_n_u32(0x7F800000); + const uint32x4_t vec_eight_biased_exponent = vdupq_n_u32(0x43800000); + const uint32x4_t vec_implicit_leading_one = vdupq_n_u32(0x43BF8000); + const uint32x4_t two_n = vandq_u32(vreinterpretq_u32_f32(a), + vec_float_exponent_mask); + const uint32x4_t n_1 = vshrq_n_u32(two_n, kShiftExponentIntoTopMantissa); + const uint32x4_t n_0 = vorrq_u32(n_1, vec_eight_biased_exponent); + const float32x4_t n = + vsubq_f32(vreinterpretq_f32_u32(n_0), + vreinterpretq_f32_u32(vec_implicit_leading_one)); + // Compute y. + const uint32x4_t vec_mantissa_mask = vdupq_n_u32(0x007FFFFF); + const uint32x4_t vec_zero_biased_exponent_is_one = vdupq_n_u32(0x3F800000); + const uint32x4_t mantissa = vandq_u32(vreinterpretq_u32_f32(a), + vec_mantissa_mask); + const float32x4_t y = + vreinterpretq_f32_u32(vorrq_u32(mantissa, + vec_zero_biased_exponent_is_one)); + // Approximate log2(y) ~= (y - 1) * pol5(y). + // pol5(y) = C5 * y^5 + C4 * y^4 + C3 * y^3 + C2 * y^2 + C1 * y + C0 + const float32x4_t C5 = vdupq_n_f32(-3.4436006e-2f); + const float32x4_t C4 = vdupq_n_f32(3.1821337e-1f); + const float32x4_t C3 = vdupq_n_f32(-1.2315303f); + const float32x4_t C2 = vdupq_n_f32(2.5988452f); + const float32x4_t C1 = vdupq_n_f32(-3.3241990f); + const float32x4_t C0 = vdupq_n_f32(3.1157899f); + float32x4_t pol5_y = C5; + pol5_y = vmlaq_f32(C4, y, pol5_y); + pol5_y = vmlaq_f32(C3, y, pol5_y); + pol5_y = vmlaq_f32(C2, y, pol5_y); + pol5_y = vmlaq_f32(C1, y, pol5_y); + pol5_y = vmlaq_f32(C0, y, pol5_y); + const float32x4_t y_minus_one = + vsubq_f32(y, vreinterpretq_f32_u32(vec_zero_biased_exponent_is_one)); + const float32x4_t log2_y = vmulq_f32(y_minus_one, pol5_y); + + // Combine parts. + log2_a = vaddq_f32(n, log2_y); + } + + // b * log2(a) + b_log2_a = vmulq_f32(b, log2_a); + + // Calculate exp2(x), x = b * log2(a). + { + // To calculate 2^x, we decompose x like this: + // x = n + y + // n is an integer, the value of x - 0.5 rounded down, therefore + // y is in the [0.5, 1.5) range + // + // 2^x = 2^n * 2^y + // 2^n can be evaluated by playing with float representation. + // 2^y in a small range can be approximated, this code uses an order two + // polynomial approximation. The coefficients have been estimated + // with the Remez algorithm and the resulting polynomial has a + // maximum relative error of 0.17%. + // To avoid over/underflow, we reduce the range of input to ]-127, 129]. + const float32x4_t max_input = vdupq_n_f32(129.f); + const float32x4_t min_input = vdupq_n_f32(-126.99999f); + const float32x4_t x_min = vminq_f32(b_log2_a, max_input); + const float32x4_t x_max = vmaxq_f32(x_min, min_input); + // Compute n. + const float32x4_t half = vdupq_n_f32(0.5f); + const float32x4_t x_minus_half = vsubq_f32(x_max, half); + const int32x4_t x_minus_half_floor = vcvtq_s32_f32(x_minus_half); + + // Compute 2^n. + const int32x4_t float_exponent_bias = vdupq_n_s32(127); + const int32x4_t two_n_exponent = + vaddq_s32(x_minus_half_floor, float_exponent_bias); + const float32x4_t two_n = + vreinterpretq_f32_s32(vshlq_n_s32(two_n_exponent, kFloatExponentShift)); + // Compute y. + const float32x4_t y = vsubq_f32(x_max, vcvtq_f32_s32(x_minus_half_floor)); + + // Approximate 2^y ~= C2 * y^2 + C1 * y + C0. + const float32x4_t C2 = vdupq_n_f32(3.3718944e-1f); + const float32x4_t C1 = vdupq_n_f32(6.5763628e-1f); + const float32x4_t C0 = vdupq_n_f32(1.0017247f); + float32x4_t exp2_y = C2; + exp2_y = vmlaq_f32(C1, y, exp2_y); + exp2_y = vmlaq_f32(C0, y, exp2_y); + + // Combine parts. + a_exp_b = vmulq_f32(exp2_y, two_n); + } + + return a_exp_b; +} + +static void OverdriveAndSuppressNEON(AecCore* aec, + float hNl[PART_LEN1], + const float hNlFb, + float efw[2][PART_LEN1]) { + int i; + const float32x4_t vec_hNlFb = vmovq_n_f32(hNlFb); + const float32x4_t vec_one = vdupq_n_f32(1.0f); + const float32x4_t vec_minus_one = vdupq_n_f32(-1.0f); + const float32x4_t vec_overDriveSm = vmovq_n_f32(aec->overDriveSm); + + // vectorized code (four at once) + for (i = 0; i + 3 < PART_LEN1; i += 4) { + // Weight subbands + float32x4_t vec_hNl = vld1q_f32(&hNl[i]); + const float32x4_t vec_weightCurve = vld1q_f32(&WebRtcAec_weightCurve[i]); + const uint32x4_t bigger = vcgtq_f32(vec_hNl, vec_hNlFb); + const float32x4_t vec_weightCurve_hNlFb = vmulq_f32(vec_weightCurve, + vec_hNlFb); + const float32x4_t vec_one_weightCurve = vsubq_f32(vec_one, vec_weightCurve); + const float32x4_t vec_one_weightCurve_hNl = vmulq_f32(vec_one_weightCurve, + vec_hNl); + const uint32x4_t vec_if0 = vandq_u32(vmvnq_u32(bigger), + vreinterpretq_u32_f32(vec_hNl)); + const float32x4_t vec_one_weightCurve_add = + vaddq_f32(vec_weightCurve_hNlFb, vec_one_weightCurve_hNl); + const uint32x4_t vec_if1 = + vandq_u32(bigger, vreinterpretq_u32_f32(vec_one_weightCurve_add)); + + vec_hNl = vreinterpretq_f32_u32(vorrq_u32(vec_if0, vec_if1)); + + { + const float32x4_t vec_overDriveCurve = + vld1q_f32(&WebRtcAec_overDriveCurve[i]); + const float32x4_t vec_overDriveSm_overDriveCurve = + vmulq_f32(vec_overDriveSm, vec_overDriveCurve); + vec_hNl = vpowq_f32(vec_hNl, vec_overDriveSm_overDriveCurve); + vst1q_f32(&hNl[i], vec_hNl); + } + + // Suppress error signal + { + float32x4_t vec_efw_re = vld1q_f32(&efw[0][i]); + float32x4_t vec_efw_im = vld1q_f32(&efw[1][i]); + vec_efw_re = vmulq_f32(vec_efw_re, vec_hNl); + vec_efw_im = vmulq_f32(vec_efw_im, vec_hNl); + + // Ooura fft returns incorrect sign on imaginary component. It matters + // here because we are making an additive change with comfort noise. + vec_efw_im = vmulq_f32(vec_efw_im, vec_minus_one); + vst1q_f32(&efw[0][i], vec_efw_re); + vst1q_f32(&efw[1][i], vec_efw_im); + } + } + + // scalar code for the remaining items. + for (; i < PART_LEN1; i++) { + // Weight subbands + if (hNl[i] > hNlFb) { + hNl[i] = WebRtcAec_weightCurve[i] * hNlFb + + (1 - WebRtcAec_weightCurve[i]) * hNl[i]; + } + + hNl[i] = powf(hNl[i], aec->overDriveSm * WebRtcAec_overDriveCurve[i]); + + // Suppress error signal + efw[0][i] *= hNl[i]; + efw[1][i] *= hNl[i]; + + // Ooura fft returns incorrect sign on imaginary component. It matters + // here because we are making an additive change with comfort noise. + efw[1][i] *= -1; + } +} + +static int PartitionDelay(const AecCore* aec) { + // Measures the energy in each filter partition and returns the partition with + // highest energy. + // TODO(bjornv): Spread computational cost by computing one partition per + // block? + float wfEnMax = 0; + int i; + int delay = 0; + + for (i = 0; i < aec->num_partitions; i++) { + int j; + int pos = i * PART_LEN1; + float wfEn = 0; + float32x4_t vec_wfEn = vdupq_n_f32(0.0f); + // vectorized code (four at once) + for (j = 0; j + 3 < PART_LEN1; j += 4) { + const float32x4_t vec_wfBuf0 = vld1q_f32(&aec->wfBuf[0][pos + j]); + const float32x4_t vec_wfBuf1 = vld1q_f32(&aec->wfBuf[1][pos + j]); + vec_wfEn = vmlaq_f32(vec_wfEn, vec_wfBuf0, vec_wfBuf0); + vec_wfEn = vmlaq_f32(vec_wfEn, vec_wfBuf1, vec_wfBuf1); + } + { + float32x2_t vec_total; + // A B C D + vec_total = vpadd_f32(vget_low_f32(vec_wfEn), vget_high_f32(vec_wfEn)); + // A+B C+D + vec_total = vpadd_f32(vec_total, vec_total); + // A+B+C+D A+B+C+D + wfEn = vget_lane_f32(vec_total, 0); + } + + // scalar code for the remaining items. + for (; j < PART_LEN1; j++) { + wfEn += aec->wfBuf[0][pos + j] * aec->wfBuf[0][pos + j] + + aec->wfBuf[1][pos + j] * aec->wfBuf[1][pos + j]; + } + + if (wfEn > wfEnMax) { + wfEnMax = wfEn; + delay = i; + } + } + return delay; +} + +// Updates the following smoothed Power Spectral Densities (PSD): +// - sd : near-end +// - se : residual echo +// - sx : far-end +// - sde : cross-PSD of near-end and residual echo +// - sxd : cross-PSD of near-end and far-end +// +// In addition to updating the PSDs, also the filter diverge state is determined +// upon actions are taken. +static void SmoothedPSD(AecCore* aec, + float efw[2][PART_LEN1], + float dfw[2][PART_LEN1], + float xfw[2][PART_LEN1]) { + // Power estimate smoothing coefficients. + const float* ptrGCoh = aec->extended_filter_enabled + ? WebRtcAec_kExtendedSmoothingCoefficients[aec->mult - 1] + : WebRtcAec_kNormalSmoothingCoefficients[aec->mult - 1]; + int i; + float sdSum = 0, seSum = 0; + const float32x4_t vec_15 = vdupq_n_f32(WebRtcAec_kMinFarendPSD); + float32x4_t vec_sdSum = vdupq_n_f32(0.0f); + float32x4_t vec_seSum = vdupq_n_f32(0.0f); + + for (i = 0; i + 3 < PART_LEN1; i += 4) { + const float32x4_t vec_dfw0 = vld1q_f32(&dfw[0][i]); + const float32x4_t vec_dfw1 = vld1q_f32(&dfw[1][i]); + const float32x4_t vec_efw0 = vld1q_f32(&efw[0][i]); + const float32x4_t vec_efw1 = vld1q_f32(&efw[1][i]); + const float32x4_t vec_xfw0 = vld1q_f32(&xfw[0][i]); + const float32x4_t vec_xfw1 = vld1q_f32(&xfw[1][i]); + float32x4_t vec_sd = vmulq_n_f32(vld1q_f32(&aec->sd[i]), ptrGCoh[0]); + float32x4_t vec_se = vmulq_n_f32(vld1q_f32(&aec->se[i]), ptrGCoh[0]); + float32x4_t vec_sx = vmulq_n_f32(vld1q_f32(&aec->sx[i]), ptrGCoh[0]); + float32x4_t vec_dfw_sumsq = vmulq_f32(vec_dfw0, vec_dfw0); + float32x4_t vec_efw_sumsq = vmulq_f32(vec_efw0, vec_efw0); + float32x4_t vec_xfw_sumsq = vmulq_f32(vec_xfw0, vec_xfw0); + + vec_dfw_sumsq = vmlaq_f32(vec_dfw_sumsq, vec_dfw1, vec_dfw1); + vec_efw_sumsq = vmlaq_f32(vec_efw_sumsq, vec_efw1, vec_efw1); + vec_xfw_sumsq = vmlaq_f32(vec_xfw_sumsq, vec_xfw1, vec_xfw1); + vec_xfw_sumsq = vmaxq_f32(vec_xfw_sumsq, vec_15); + vec_sd = vmlaq_n_f32(vec_sd, vec_dfw_sumsq, ptrGCoh[1]); + vec_se = vmlaq_n_f32(vec_se, vec_efw_sumsq, ptrGCoh[1]); + vec_sx = vmlaq_n_f32(vec_sx, vec_xfw_sumsq, ptrGCoh[1]); + + vst1q_f32(&aec->sd[i], vec_sd); + vst1q_f32(&aec->se[i], vec_se); + vst1q_f32(&aec->sx[i], vec_sx); + + { + float32x4x2_t vec_sde = vld2q_f32(&aec->sde[i][0]); + float32x4_t vec_dfwefw0011 = vmulq_f32(vec_dfw0, vec_efw0); + float32x4_t vec_dfwefw0110 = vmulq_f32(vec_dfw0, vec_efw1); + vec_sde.val[0] = vmulq_n_f32(vec_sde.val[0], ptrGCoh[0]); + vec_sde.val[1] = vmulq_n_f32(vec_sde.val[1], ptrGCoh[0]); + vec_dfwefw0011 = vmlaq_f32(vec_dfwefw0011, vec_dfw1, vec_efw1); + vec_dfwefw0110 = vmlsq_f32(vec_dfwefw0110, vec_dfw1, vec_efw0); + vec_sde.val[0] = vmlaq_n_f32(vec_sde.val[0], vec_dfwefw0011, ptrGCoh[1]); + vec_sde.val[1] = vmlaq_n_f32(vec_sde.val[1], vec_dfwefw0110, ptrGCoh[1]); + vst2q_f32(&aec->sde[i][0], vec_sde); + } + + { + float32x4x2_t vec_sxd = vld2q_f32(&aec->sxd[i][0]); + float32x4_t vec_dfwxfw0011 = vmulq_f32(vec_dfw0, vec_xfw0); + float32x4_t vec_dfwxfw0110 = vmulq_f32(vec_dfw0, vec_xfw1); + vec_sxd.val[0] = vmulq_n_f32(vec_sxd.val[0], ptrGCoh[0]); + vec_sxd.val[1] = vmulq_n_f32(vec_sxd.val[1], ptrGCoh[0]); + vec_dfwxfw0011 = vmlaq_f32(vec_dfwxfw0011, vec_dfw1, vec_xfw1); + vec_dfwxfw0110 = vmlsq_f32(vec_dfwxfw0110, vec_dfw1, vec_xfw0); + vec_sxd.val[0] = vmlaq_n_f32(vec_sxd.val[0], vec_dfwxfw0011, ptrGCoh[1]); + vec_sxd.val[1] = vmlaq_n_f32(vec_sxd.val[1], vec_dfwxfw0110, ptrGCoh[1]); + vst2q_f32(&aec->sxd[i][0], vec_sxd); + } + + vec_sdSum = vaddq_f32(vec_sdSum, vec_sd); + vec_seSum = vaddq_f32(vec_seSum, vec_se); + } + { + float32x2_t vec_sdSum_total; + float32x2_t vec_seSum_total; + // A B C D + vec_sdSum_total = vpadd_f32(vget_low_f32(vec_sdSum), + vget_high_f32(vec_sdSum)); + vec_seSum_total = vpadd_f32(vget_low_f32(vec_seSum), + vget_high_f32(vec_seSum)); + // A+B C+D + vec_sdSum_total = vpadd_f32(vec_sdSum_total, vec_sdSum_total); + vec_seSum_total = vpadd_f32(vec_seSum_total, vec_seSum_total); + // A+B+C+D A+B+C+D + sdSum = vget_lane_f32(vec_sdSum_total, 0); + seSum = vget_lane_f32(vec_seSum_total, 0); + } + + // scalar code for the remaining items. + for (; i < PART_LEN1; i++) { + aec->sd[i] = ptrGCoh[0] * aec->sd[i] + + ptrGCoh[1] * (dfw[0][i] * dfw[0][i] + dfw[1][i] * dfw[1][i]); + aec->se[i] = ptrGCoh[0] * aec->se[i] + + ptrGCoh[1] * (efw[0][i] * efw[0][i] + efw[1][i] * efw[1][i]); + // We threshold here to protect against the ill-effects of a zero farend. + // The threshold is not arbitrarily chosen, but balances protection and + // adverse interaction with the algorithm's tuning. + // TODO(bjornv): investigate further why this is so sensitive. + aec->sx[i] = + ptrGCoh[0] * aec->sx[i] + + ptrGCoh[1] * WEBRTC_SPL_MAX( + xfw[0][i] * xfw[0][i] + xfw[1][i] * xfw[1][i], + WebRtcAec_kMinFarendPSD); + + aec->sde[i][0] = + ptrGCoh[0] * aec->sde[i][0] + + ptrGCoh[1] * (dfw[0][i] * efw[0][i] + dfw[1][i] * efw[1][i]); + aec->sde[i][1] = + ptrGCoh[0] * aec->sde[i][1] + + ptrGCoh[1] * (dfw[0][i] * efw[1][i] - dfw[1][i] * efw[0][i]); + + aec->sxd[i][0] = + ptrGCoh[0] * aec->sxd[i][0] + + ptrGCoh[1] * (dfw[0][i] * xfw[0][i] + dfw[1][i] * xfw[1][i]); + aec->sxd[i][1] = + ptrGCoh[0] * aec->sxd[i][1] + + ptrGCoh[1] * (dfw[0][i] * xfw[1][i] - dfw[1][i] * xfw[0][i]); + + sdSum += aec->sd[i]; + seSum += aec->se[i]; + } + + // Divergent filter safeguard. + aec->divergeState = (aec->divergeState ? 1.05f : 1.0f) * seSum > sdSum; + + if (aec->divergeState) + memcpy(efw, dfw, sizeof(efw[0][0]) * 2 * PART_LEN1); + + // Reset if error is significantly larger than nearend (13 dB). + if (!aec->extended_filter_enabled && seSum > (19.95f * sdSum)) + memset(aec->wfBuf, 0, sizeof(aec->wfBuf)); +} + +// Window time domain data to be used by the fft. +__inline static void WindowData(float* x_windowed, const float* x) { + int i; + for (i = 0; i < PART_LEN; i += 4) { + const float32x4_t vec_Buf1 = vld1q_f32(&x[i]); + const float32x4_t vec_Buf2 = vld1q_f32(&x[PART_LEN + i]); + const float32x4_t vec_sqrtHanning = vld1q_f32(&WebRtcAec_sqrtHanning[i]); + // A B C D + float32x4_t vec_sqrtHanning_rev = + vld1q_f32(&WebRtcAec_sqrtHanning[PART_LEN - i - 3]); + // B A D C + vec_sqrtHanning_rev = vrev64q_f32(vec_sqrtHanning_rev); + // D C B A + vec_sqrtHanning_rev = vcombine_f32(vget_high_f32(vec_sqrtHanning_rev), + vget_low_f32(vec_sqrtHanning_rev)); + vst1q_f32(&x_windowed[i], vmulq_f32(vec_Buf1, vec_sqrtHanning)); + vst1q_f32(&x_windowed[PART_LEN + i], + vmulq_f32(vec_Buf2, vec_sqrtHanning_rev)); + } +} + +// Puts fft output data into a complex valued array. +__inline static void StoreAsComplex(const float* data, + float data_complex[2][PART_LEN1]) { + int i; + for (i = 0; i < PART_LEN; i += 4) { + const float32x4x2_t vec_data = vld2q_f32(&data[2 * i]); + vst1q_f32(&data_complex[0][i], vec_data.val[0]); + vst1q_f32(&data_complex[1][i], vec_data.val[1]); + } + // fix beginning/end values + data_complex[1][0] = 0; + data_complex[1][PART_LEN] = 0; + data_complex[0][0] = data[0]; + data_complex[0][PART_LEN] = data[1]; +} + +static void SubbandCoherenceNEON(AecCore* aec, + float efw[2][PART_LEN1], + float xfw[2][PART_LEN1], + float* fft, + float* cohde, + float* cohxd) { + float dfw[2][PART_LEN1]; + int i; + + if (aec->delayEstCtr == 0) + aec->delayIdx = PartitionDelay(aec); + + // Use delayed far. + memcpy(xfw, + aec->xfwBuf + aec->delayIdx * PART_LEN1, + sizeof(xfw[0][0]) * 2 * PART_LEN1); + + // Windowed near fft + WindowData(fft, aec->dBuf); + aec_rdft_forward_128(fft); + StoreAsComplex(fft, dfw); + + // Windowed error fft + WindowData(fft, aec->eBuf); + aec_rdft_forward_128(fft); + StoreAsComplex(fft, efw); + + SmoothedPSD(aec, efw, dfw, xfw); + + { + const float32x4_t vec_1eminus10 = vdupq_n_f32(1e-10f); + + // Subband coherence + for (i = 0; i + 3 < PART_LEN1; i += 4) { + const float32x4_t vec_sd = vld1q_f32(&aec->sd[i]); + const float32x4_t vec_se = vld1q_f32(&aec->se[i]); + const float32x4_t vec_sx = vld1q_f32(&aec->sx[i]); + const float32x4_t vec_sdse = vmlaq_f32(vec_1eminus10, vec_sd, vec_se); + const float32x4_t vec_sdsx = vmlaq_f32(vec_1eminus10, vec_sd, vec_sx); + float32x4x2_t vec_sde = vld2q_f32(&aec->sde[i][0]); + float32x4x2_t vec_sxd = vld2q_f32(&aec->sxd[i][0]); + float32x4_t vec_cohde = vmulq_f32(vec_sde.val[0], vec_sde.val[0]); + float32x4_t vec_cohxd = vmulq_f32(vec_sxd.val[0], vec_sxd.val[0]); + vec_cohde = vmlaq_f32(vec_cohde, vec_sde.val[1], vec_sde.val[1]); + vec_cohde = vdivq_f32(vec_cohde, vec_sdse); + vec_cohxd = vmlaq_f32(vec_cohxd, vec_sxd.val[1], vec_sxd.val[1]); + vec_cohxd = vdivq_f32(vec_cohxd, vec_sdsx); + + vst1q_f32(&cohde[i], vec_cohde); + vst1q_f32(&cohxd[i], vec_cohxd); + } + } + // scalar code for the remaining items. + for (; i < PART_LEN1; i++) { + cohde[i] = + (aec->sde[i][0] * aec->sde[i][0] + aec->sde[i][1] * aec->sde[i][1]) / + (aec->sd[i] * aec->se[i] + 1e-10f); + cohxd[i] = + (aec->sxd[i][0] * aec->sxd[i][0] + aec->sxd[i][1] * aec->sxd[i][1]) / + (aec->sx[i] * aec->sd[i] + 1e-10f); + } +} + +void WebRtcAec_InitAec_neon(void) { + WebRtcAec_FilterFar = FilterFarNEON; + WebRtcAec_ScaleErrorSignal = ScaleErrorSignalNEON; + WebRtcAec_FilterAdaptation = FilterAdaptationNEON; + WebRtcAec_OverdriveAndSuppress = OverdriveAndSuppressNEON; + WebRtcAec_SubbandCoherence = SubbandCoherenceNEON; +} + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_sse2.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_sse2.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_sse2.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_sse2.c 2015-02-03 14:33:35.000000000 +0000 @@ -12,12 +12,12 @@ * The core AEC algorithm, SSE2 version of speed-critical functions. */ -#include "webrtc/modules/audio_processing/aec/aec_core.h" - #include #include #include // memset +#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/modules/audio_processing/aec/aec_common.h" #include "webrtc/modules/audio_processing/aec/aec_core_internal.h" #include "webrtc/modules/audio_processing/aec/aec_rdft.h" @@ -354,9 +354,6 @@ return a_exp_b; } -extern const float WebRtcAec_weightCurve[65]; -extern const float WebRtcAec_overDriveCurve[65]; - static void OverdriveAndSuppressSSE2(AecCore* aec, float hNl[PART_LEN1], const float hNlFb, @@ -423,10 +420,312 @@ } } +__inline static void _mm_add_ps_4x1(__m128 sum, float *dst) { + // A+B C+D + sum = _mm_add_ps(sum, _mm_shuffle_ps(sum, sum, _MM_SHUFFLE(0, 0, 3, 2))); + // A+B+C+D A+B+C+D + sum = _mm_add_ps(sum, _mm_shuffle_ps(sum, sum, _MM_SHUFFLE(1, 1, 1, 1))); + _mm_store_ss(dst, sum); +} +static int PartitionDelay(const AecCore* aec) { + // Measures the energy in each filter partition and returns the partition with + // highest energy. + // TODO(bjornv): Spread computational cost by computing one partition per + // block? + float wfEnMax = 0; + int i; + int delay = 0; + + for (i = 0; i < aec->num_partitions; i++) { + int j; + int pos = i * PART_LEN1; + float wfEn = 0; + __m128 vec_wfEn = _mm_set1_ps(0.0f); + // vectorized code (four at once) + for (j = 0; j + 3 < PART_LEN1; j += 4) { + const __m128 vec_wfBuf0 = _mm_loadu_ps(&aec->wfBuf[0][pos + j]); + const __m128 vec_wfBuf1 = _mm_loadu_ps(&aec->wfBuf[1][pos + j]); + vec_wfEn = _mm_add_ps(vec_wfEn, _mm_mul_ps(vec_wfBuf0, vec_wfBuf0)); + vec_wfEn = _mm_add_ps(vec_wfEn, _mm_mul_ps(vec_wfBuf1, vec_wfBuf1)); + } + _mm_add_ps_4x1(vec_wfEn, &wfEn); + + // scalar code for the remaining items. + for (; j < PART_LEN1; j++) { + wfEn += aec->wfBuf[0][pos + j] * aec->wfBuf[0][pos + j] + + aec->wfBuf[1][pos + j] * aec->wfBuf[1][pos + j]; + } + + if (wfEn > wfEnMax) { + wfEnMax = wfEn; + delay = i; + } + } + return delay; +} + +// Updates the following smoothed Power Spectral Densities (PSD): +// - sd : near-end +// - se : residual echo +// - sx : far-end +// - sde : cross-PSD of near-end and residual echo +// - sxd : cross-PSD of near-end and far-end +// +// In addition to updating the PSDs, also the filter diverge state is determined +// upon actions are taken. +static void SmoothedPSD(AecCore* aec, + float efw[2][PART_LEN1], + float dfw[2][PART_LEN1], + float xfw[2][PART_LEN1]) { + // Power estimate smoothing coefficients. + const float* ptrGCoh = aec->extended_filter_enabled + ? WebRtcAec_kExtendedSmoothingCoefficients[aec->mult - 1] + : WebRtcAec_kNormalSmoothingCoefficients[aec->mult - 1]; + int i; + float sdSum = 0, seSum = 0; + const __m128 vec_15 = _mm_set1_ps(WebRtcAec_kMinFarendPSD); + const __m128 vec_GCoh0 = _mm_set1_ps(ptrGCoh[0]); + const __m128 vec_GCoh1 = _mm_set1_ps(ptrGCoh[1]); + __m128 vec_sdSum = _mm_set1_ps(0.0f); + __m128 vec_seSum = _mm_set1_ps(0.0f); + + for (i = 0; i + 3 < PART_LEN1; i += 4) { + const __m128 vec_dfw0 = _mm_loadu_ps(&dfw[0][i]); + const __m128 vec_dfw1 = _mm_loadu_ps(&dfw[1][i]); + const __m128 vec_efw0 = _mm_loadu_ps(&efw[0][i]); + const __m128 vec_efw1 = _mm_loadu_ps(&efw[1][i]); + const __m128 vec_xfw0 = _mm_loadu_ps(&xfw[0][i]); + const __m128 vec_xfw1 = _mm_loadu_ps(&xfw[1][i]); + __m128 vec_sd = _mm_mul_ps(_mm_loadu_ps(&aec->sd[i]), vec_GCoh0); + __m128 vec_se = _mm_mul_ps(_mm_loadu_ps(&aec->se[i]), vec_GCoh0); + __m128 vec_sx = _mm_mul_ps(_mm_loadu_ps(&aec->sx[i]), vec_GCoh0); + __m128 vec_dfw_sumsq = _mm_mul_ps(vec_dfw0, vec_dfw0); + __m128 vec_efw_sumsq = _mm_mul_ps(vec_efw0, vec_efw0); + __m128 vec_xfw_sumsq = _mm_mul_ps(vec_xfw0, vec_xfw0); + vec_dfw_sumsq = _mm_add_ps(vec_dfw_sumsq, _mm_mul_ps(vec_dfw1, vec_dfw1)); + vec_efw_sumsq = _mm_add_ps(vec_efw_sumsq, _mm_mul_ps(vec_efw1, vec_efw1)); + vec_xfw_sumsq = _mm_add_ps(vec_xfw_sumsq, _mm_mul_ps(vec_xfw1, vec_xfw1)); + vec_xfw_sumsq = _mm_max_ps(vec_xfw_sumsq, vec_15); + vec_sd = _mm_add_ps(vec_sd, _mm_mul_ps(vec_dfw_sumsq, vec_GCoh1)); + vec_se = _mm_add_ps(vec_se, _mm_mul_ps(vec_efw_sumsq, vec_GCoh1)); + vec_sx = _mm_add_ps(vec_sx, _mm_mul_ps(vec_xfw_sumsq, vec_GCoh1)); + _mm_storeu_ps(&aec->sd[i], vec_sd); + _mm_storeu_ps(&aec->se[i], vec_se); + _mm_storeu_ps(&aec->sx[i], vec_sx); + + { + const __m128 vec_3210 = _mm_loadu_ps(&aec->sde[i][0]); + const __m128 vec_7654 = _mm_loadu_ps(&aec->sde[i + 2][0]); + __m128 vec_a = _mm_shuffle_ps(vec_3210, vec_7654, + _MM_SHUFFLE(2, 0, 2, 0)); + __m128 vec_b = _mm_shuffle_ps(vec_3210, vec_7654, + _MM_SHUFFLE(3, 1, 3, 1)); + __m128 vec_dfwefw0011 = _mm_mul_ps(vec_dfw0, vec_efw0); + __m128 vec_dfwefw0110 = _mm_mul_ps(vec_dfw0, vec_efw1); + vec_a = _mm_mul_ps(vec_a, vec_GCoh0); + vec_b = _mm_mul_ps(vec_b, vec_GCoh0); + vec_dfwefw0011 = _mm_add_ps(vec_dfwefw0011, + _mm_mul_ps(vec_dfw1, vec_efw1)); + vec_dfwefw0110 = _mm_sub_ps(vec_dfwefw0110, + _mm_mul_ps(vec_dfw1, vec_efw0)); + vec_a = _mm_add_ps(vec_a, _mm_mul_ps(vec_dfwefw0011, vec_GCoh1)); + vec_b = _mm_add_ps(vec_b, _mm_mul_ps(vec_dfwefw0110, vec_GCoh1)); + _mm_storeu_ps(&aec->sde[i][0], _mm_unpacklo_ps(vec_a, vec_b)); + _mm_storeu_ps(&aec->sde[i + 2][0], _mm_unpackhi_ps(vec_a, vec_b)); + } + + { + const __m128 vec_3210 = _mm_loadu_ps(&aec->sxd[i][0]); + const __m128 vec_7654 = _mm_loadu_ps(&aec->sxd[i + 2][0]); + __m128 vec_a = _mm_shuffle_ps(vec_3210, vec_7654, + _MM_SHUFFLE(2, 0, 2, 0)); + __m128 vec_b = _mm_shuffle_ps(vec_3210, vec_7654, + _MM_SHUFFLE(3, 1, 3, 1)); + __m128 vec_dfwxfw0011 = _mm_mul_ps(vec_dfw0, vec_xfw0); + __m128 vec_dfwxfw0110 = _mm_mul_ps(vec_dfw0, vec_xfw1); + vec_a = _mm_mul_ps(vec_a, vec_GCoh0); + vec_b = _mm_mul_ps(vec_b, vec_GCoh0); + vec_dfwxfw0011 = _mm_add_ps(vec_dfwxfw0011, + _mm_mul_ps(vec_dfw1, vec_xfw1)); + vec_dfwxfw0110 = _mm_sub_ps(vec_dfwxfw0110, + _mm_mul_ps(vec_dfw1, vec_xfw0)); + vec_a = _mm_add_ps(vec_a, _mm_mul_ps(vec_dfwxfw0011, vec_GCoh1)); + vec_b = _mm_add_ps(vec_b, _mm_mul_ps(vec_dfwxfw0110, vec_GCoh1)); + _mm_storeu_ps(&aec->sxd[i][0], _mm_unpacklo_ps(vec_a, vec_b)); + _mm_storeu_ps(&aec->sxd[i + 2][0], _mm_unpackhi_ps(vec_a, vec_b)); + } + + vec_sdSum = _mm_add_ps(vec_sdSum, vec_sd); + vec_seSum = _mm_add_ps(vec_seSum, vec_se); + } + + _mm_add_ps_4x1(vec_sdSum, &sdSum); + _mm_add_ps_4x1(vec_seSum, &seSum); + + for (; i < PART_LEN1; i++) { + aec->sd[i] = ptrGCoh[0] * aec->sd[i] + + ptrGCoh[1] * (dfw[0][i] * dfw[0][i] + dfw[1][i] * dfw[1][i]); + aec->se[i] = ptrGCoh[0] * aec->se[i] + + ptrGCoh[1] * (efw[0][i] * efw[0][i] + efw[1][i] * efw[1][i]); + // We threshold here to protect against the ill-effects of a zero farend. + // The threshold is not arbitrarily chosen, but balances protection and + // adverse interaction with the algorithm's tuning. + // TODO(bjornv): investigate further why this is so sensitive. + aec->sx[i] = + ptrGCoh[0] * aec->sx[i] + + ptrGCoh[1] * WEBRTC_SPL_MAX( + xfw[0][i] * xfw[0][i] + xfw[1][i] * xfw[1][i], + WebRtcAec_kMinFarendPSD); + + aec->sde[i][0] = + ptrGCoh[0] * aec->sde[i][0] + + ptrGCoh[1] * (dfw[0][i] * efw[0][i] + dfw[1][i] * efw[1][i]); + aec->sde[i][1] = + ptrGCoh[0] * aec->sde[i][1] + + ptrGCoh[1] * (dfw[0][i] * efw[1][i] - dfw[1][i] * efw[0][i]); + + aec->sxd[i][0] = + ptrGCoh[0] * aec->sxd[i][0] + + ptrGCoh[1] * (dfw[0][i] * xfw[0][i] + dfw[1][i] * xfw[1][i]); + aec->sxd[i][1] = + ptrGCoh[0] * aec->sxd[i][1] + + ptrGCoh[1] * (dfw[0][i] * xfw[1][i] - dfw[1][i] * xfw[0][i]); + + sdSum += aec->sd[i]; + seSum += aec->se[i]; + } + + // Divergent filter safeguard. + aec->divergeState = (aec->divergeState ? 1.05f : 1.0f) * seSum > sdSum; + + if (aec->divergeState) + memcpy(efw, dfw, sizeof(efw[0][0]) * 2 * PART_LEN1); + + // Reset if error is significantly larger than nearend (13 dB). + if (!aec->extended_filter_enabled && seSum > (19.95f * sdSum)) + memset(aec->wfBuf, 0, sizeof(aec->wfBuf)); +} + +// Window time domain data to be used by the fft. +__inline static void WindowData(float* x_windowed, const float* x) { + int i; + for (i = 0; i < PART_LEN; i += 4) { + const __m128 vec_Buf1 = _mm_loadu_ps(&x[i]); + const __m128 vec_Buf2 = _mm_loadu_ps(&x[PART_LEN + i]); + const __m128 vec_sqrtHanning = _mm_load_ps(&WebRtcAec_sqrtHanning[i]); + // A B C D + __m128 vec_sqrtHanning_rev = + _mm_loadu_ps(&WebRtcAec_sqrtHanning[PART_LEN - i - 3]); + // D C B A + vec_sqrtHanning_rev = + _mm_shuffle_ps(vec_sqrtHanning_rev, vec_sqrtHanning_rev, + _MM_SHUFFLE(0, 1, 2, 3)); + _mm_storeu_ps(&x_windowed[i], _mm_mul_ps(vec_Buf1, vec_sqrtHanning)); + _mm_storeu_ps(&x_windowed[PART_LEN + i], + _mm_mul_ps(vec_Buf2, vec_sqrtHanning_rev)); + } +} + +// Puts fft output data into a complex valued array. +__inline static void StoreAsComplex(const float* data, + float data_complex[2][PART_LEN1]) { + int i; + for (i = 0; i < PART_LEN; i += 4) { + const __m128 vec_fft0 = _mm_loadu_ps(&data[2 * i]); + const __m128 vec_fft4 = _mm_loadu_ps(&data[2 * i + 4]); + const __m128 vec_a = _mm_shuffle_ps(vec_fft0, vec_fft4, + _MM_SHUFFLE(2, 0, 2, 0)); + const __m128 vec_b = _mm_shuffle_ps(vec_fft0, vec_fft4, + _MM_SHUFFLE(3, 1, 3, 1)); + _mm_storeu_ps(&data_complex[0][i], vec_a); + _mm_storeu_ps(&data_complex[1][i], vec_b); + } + // fix beginning/end values + data_complex[1][0] = 0; + data_complex[1][PART_LEN] = 0; + data_complex[0][0] = data[0]; + data_complex[0][PART_LEN] = data[1]; +} + +static void SubbandCoherenceSSE2(AecCore* aec, + float efw[2][PART_LEN1], + float xfw[2][PART_LEN1], + float* fft, + float* cohde, + float* cohxd) { + float dfw[2][PART_LEN1]; + int i; + + if (aec->delayEstCtr == 0) + aec->delayIdx = PartitionDelay(aec); + + // Use delayed far. + memcpy(xfw, + aec->xfwBuf + aec->delayIdx * PART_LEN1, + sizeof(xfw[0][0]) * 2 * PART_LEN1); + + // Windowed near fft + WindowData(fft, aec->dBuf); + aec_rdft_forward_128(fft); + StoreAsComplex(fft, dfw); + + // Windowed error fft + WindowData(fft, aec->eBuf); + aec_rdft_forward_128(fft); + StoreAsComplex(fft, efw); + + SmoothedPSD(aec, efw, dfw, xfw); + + { + const __m128 vec_1eminus10 = _mm_set1_ps(1e-10f); + + // Subband coherence + for (i = 0; i + 3 < PART_LEN1; i += 4) { + const __m128 vec_sd = _mm_loadu_ps(&aec->sd[i]); + const __m128 vec_se = _mm_loadu_ps(&aec->se[i]); + const __m128 vec_sx = _mm_loadu_ps(&aec->sx[i]); + const __m128 vec_sdse = _mm_add_ps(vec_1eminus10, + _mm_mul_ps(vec_sd, vec_se)); + const __m128 vec_sdsx = _mm_add_ps(vec_1eminus10, + _mm_mul_ps(vec_sd, vec_sx)); + const __m128 vec_sde_3210 = _mm_loadu_ps(&aec->sde[i][0]); + const __m128 vec_sde_7654 = _mm_loadu_ps(&aec->sde[i + 2][0]); + const __m128 vec_sxd_3210 = _mm_loadu_ps(&aec->sxd[i][0]); + const __m128 vec_sxd_7654 = _mm_loadu_ps(&aec->sxd[i + 2][0]); + const __m128 vec_sde_0 = _mm_shuffle_ps(vec_sde_3210, vec_sde_7654, + _MM_SHUFFLE(2, 0, 2, 0)); + const __m128 vec_sde_1 = _mm_shuffle_ps(vec_sde_3210, vec_sde_7654, + _MM_SHUFFLE(3, 1, 3, 1)); + const __m128 vec_sxd_0 = _mm_shuffle_ps(vec_sxd_3210, vec_sxd_7654, + _MM_SHUFFLE(2, 0, 2, 0)); + const __m128 vec_sxd_1 = _mm_shuffle_ps(vec_sxd_3210, vec_sxd_7654, + _MM_SHUFFLE(3, 1, 3, 1)); + __m128 vec_cohde = _mm_mul_ps(vec_sde_0, vec_sde_0); + __m128 vec_cohxd = _mm_mul_ps(vec_sxd_0, vec_sxd_0); + vec_cohde = _mm_add_ps(vec_cohde, _mm_mul_ps(vec_sde_1, vec_sde_1)); + vec_cohde = _mm_div_ps(vec_cohde, vec_sdse); + vec_cohxd = _mm_add_ps(vec_cohxd, _mm_mul_ps(vec_sxd_1, vec_sxd_1)); + vec_cohxd = _mm_div_ps(vec_cohxd, vec_sdsx); + _mm_storeu_ps(&cohde[i], vec_cohde); + _mm_storeu_ps(&cohxd[i], vec_cohxd); + } + + // scalar code for the remaining items. + for (; i < PART_LEN1; i++) { + cohde[i] = + (aec->sde[i][0] * aec->sde[i][0] + aec->sde[i][1] * aec->sde[i][1]) / + (aec->sd[i] * aec->se[i] + 1e-10f); + cohxd[i] = + (aec->sxd[i][0] * aec->sxd[i][0] + aec->sxd[i][1] * aec->sxd[i][1]) / + (aec->sx[i] * aec->sd[i] + 1e-10f); + } + } +} + void WebRtcAec_InitAec_SSE2(void) { WebRtcAec_FilterFar = FilterFarSSE2; WebRtcAec_ScaleErrorSignal = ScaleErrorSignalSSE2; WebRtcAec_FilterAdaptation = FilterAdaptationSSE2; WebRtcAec_OverdriveAndSuppress = OverdriveAndSuppressSSE2; + WebRtcAec_SubbandCoherence = SubbandCoherenceSSE2; } - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_rdft.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_rdft.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_rdft.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_rdft.c 2015-02-03 14:33:35.000000000 +0000 @@ -26,97 +26,104 @@ #include "webrtc/system_wrappers/interface/cpu_features_wrapper.h" #include "webrtc/typedefs.h" -// constants shared by all paths (C, SSE2). -float rdft_w[64]; -// constants used by the C path. -float rdft_wk3ri_first[32]; -float rdft_wk3ri_second[32]; -// constants used by SSE2 but initialized in C path. -ALIGN16_BEG float ALIGN16_END rdft_wk1r[32]; -ALIGN16_BEG float ALIGN16_END rdft_wk2r[32]; -ALIGN16_BEG float ALIGN16_END rdft_wk3r[32]; -ALIGN16_BEG float ALIGN16_END rdft_wk1i[32]; -ALIGN16_BEG float ALIGN16_END rdft_wk2i[32]; -ALIGN16_BEG float ALIGN16_END rdft_wk3i[32]; -ALIGN16_BEG float ALIGN16_END cftmdl_wk1r[4]; - -static int ip[16]; - -static void bitrv2_32(int* ip, float* a) { - const int n = 32; - int j, j1, k, k1, m, m2; - float xr, xi, yr, yi; - - ip[0] = 0; - { - int l = n; - m = 1; - while ((m << 3) < l) { - l >>= 1; - for (j = 0; j < m; j++) { - ip[m + j] = ip[j] + l; - } - m <<= 1; - } - } - m2 = 2 * m; - for (k = 0; k < m; k++) { - for (j = 0; j < k; j++) { - j1 = 2 * j + ip[k]; - k1 = 2 * k + ip[j]; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 += 2 * m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 -= m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 += 2 * m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - } - j1 = 2 * k + m2 + ip[k]; - k1 = j1 + m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - } -} +// These tables used to be computed at run-time. For example, refer to: +// https://code.google.com/p/webrtc/source/browse/trunk/webrtc/modules/audio_processing/aec/aec_rdft.c?r=6564 +// to see the initialization code. +const float rdft_w[64] = { + 1.0000000000f, 0.0000000000f, 0.7071067691f, 0.7071067691f, + 0.9238795638f, 0.3826834559f, 0.3826834559f, 0.9238795638f, + 0.9807852507f, 0.1950903237f, 0.5555702448f, 0.8314695954f, + 0.8314695954f, 0.5555702448f, 0.1950903237f, 0.9807852507f, + 0.9951847196f, 0.0980171412f, 0.6343933344f, 0.7730104327f, + 0.8819212914f, 0.4713967443f, 0.2902846634f, 0.9569403529f, + 0.9569403529f, 0.2902846634f, 0.4713967443f, 0.8819212914f, + 0.7730104327f, 0.6343933344f, 0.0980171412f, 0.9951847196f, + 0.7071067691f, 0.4993977249f, 0.4975923598f, 0.4945882559f, + 0.4903926253f, 0.4850156307f, 0.4784701765f, 0.4707720280f, + 0.4619397819f, 0.4519946277f, 0.4409606457f, 0.4288643003f, + 0.4157347977f, 0.4016037583f, 0.3865052164f, 0.3704755902f, + 0.3535533845f, 0.3357794881f, 0.3171966672f, 0.2978496552f, + 0.2777851224f, 0.2570513785f, 0.2356983721f, 0.2137775421f, + 0.1913417280f, 0.1684449315f, 0.1451423317f, 0.1214900985f, + 0.0975451618f, 0.0733652338f, 0.0490085706f, 0.0245338380f, +}; +const float rdft_wk3ri_first[16] = { + 1.000000000f, 0.000000000f, 0.382683456f, 0.923879564f, + 0.831469536f, 0.555570245f, -0.195090353f, 0.980785251f, + 0.956940353f, 0.290284693f, 0.098017156f, 0.995184720f, + 0.634393334f, 0.773010492f, -0.471396863f, 0.881921172f, +}; +const float rdft_wk3ri_second[16] = { + -0.707106769f, 0.707106769f, -0.923879564f, -0.382683456f, + -0.980785251f, 0.195090353f, -0.555570245f, -0.831469536f, + -0.881921172f, 0.471396863f, -0.773010492f, -0.634393334f, + -0.995184720f, -0.098017156f, -0.290284693f, -0.956940353f, +}; +ALIGN16_BEG const float ALIGN16_END rdft_wk1r[32] = { + 1.000000000f, 1.000000000f, 0.707106769f, 0.707106769f, + 0.923879564f, 0.923879564f, 0.382683456f, 0.382683456f, + 0.980785251f, 0.980785251f, 0.555570245f, 0.555570245f, + 0.831469595f, 0.831469595f, 0.195090324f, 0.195090324f, + 0.995184720f, 0.995184720f, 0.634393334f, 0.634393334f, + 0.881921291f, 0.881921291f, 0.290284663f, 0.290284663f, + 0.956940353f, 0.956940353f, 0.471396744f, 0.471396744f, + 0.773010433f, 0.773010433f, 0.098017141f, 0.098017141f, +}; +ALIGN16_BEG const float ALIGN16_END rdft_wk2r[32] = { + 1.000000000f, 1.000000000f, -0.000000000f, -0.000000000f, + 0.707106769f, 0.707106769f, -0.707106769f, -0.707106769f, + 0.923879564f, 0.923879564f, -0.382683456f, -0.382683456f, + 0.382683456f, 0.382683456f, -0.923879564f, -0.923879564f, + 0.980785251f, 0.980785251f, -0.195090324f, -0.195090324f, + 0.555570245f, 0.555570245f, -0.831469595f, -0.831469595f, + 0.831469595f, 0.831469595f, -0.555570245f, -0.555570245f, + 0.195090324f, 0.195090324f, -0.980785251f, -0.980785251f, +}; +ALIGN16_BEG const float ALIGN16_END rdft_wk3r[32] = { + 1.000000000f, 1.000000000f, -0.707106769f, -0.707106769f, + 0.382683456f, 0.382683456f, -0.923879564f, -0.923879564f, + 0.831469536f, 0.831469536f, -0.980785251f, -0.980785251f, + -0.195090353f, -0.195090353f, -0.555570245f, -0.555570245f, + 0.956940353f, 0.956940353f, -0.881921172f, -0.881921172f, + 0.098017156f, 0.098017156f, -0.773010492f, -0.773010492f, + 0.634393334f, 0.634393334f, -0.995184720f, -0.995184720f, + -0.471396863f, -0.471396863f, -0.290284693f, -0.290284693f, +}; +ALIGN16_BEG const float ALIGN16_END rdft_wk1i[32] = { + -0.000000000f, 0.000000000f, -0.707106769f, 0.707106769f, + -0.382683456f, 0.382683456f, -0.923879564f, 0.923879564f, + -0.195090324f, 0.195090324f, -0.831469595f, 0.831469595f, + -0.555570245f, 0.555570245f, -0.980785251f, 0.980785251f, + -0.098017141f, 0.098017141f, -0.773010433f, 0.773010433f, + -0.471396744f, 0.471396744f, -0.956940353f, 0.956940353f, + -0.290284663f, 0.290284663f, -0.881921291f, 0.881921291f, + -0.634393334f, 0.634393334f, -0.995184720f, 0.995184720f, +}; +ALIGN16_BEG const float ALIGN16_END rdft_wk2i[32] = { + -0.000000000f, 0.000000000f, -1.000000000f, 1.000000000f, + -0.707106769f, 0.707106769f, -0.707106769f, 0.707106769f, + -0.382683456f, 0.382683456f, -0.923879564f, 0.923879564f, + -0.923879564f, 0.923879564f, -0.382683456f, 0.382683456f, + -0.195090324f, 0.195090324f, -0.980785251f, 0.980785251f, + -0.831469595f, 0.831469595f, -0.555570245f, 0.555570245f, + -0.555570245f, 0.555570245f, -0.831469595f, 0.831469595f, + -0.980785251f, 0.980785251f, -0.195090324f, 0.195090324f, +}; +ALIGN16_BEG const float ALIGN16_END rdft_wk3i[32] = { + -0.000000000f, 0.000000000f, -0.707106769f, 0.707106769f, + -0.923879564f, 0.923879564f, 0.382683456f, -0.382683456f, + -0.555570245f, 0.555570245f, -0.195090353f, 0.195090353f, + -0.980785251f, 0.980785251f, 0.831469536f, -0.831469536f, + -0.290284693f, 0.290284693f, -0.471396863f, 0.471396863f, + -0.995184720f, 0.995184720f, 0.634393334f, -0.634393334f, + -0.773010492f, 0.773010492f, 0.098017156f, -0.098017156f, + -0.881921172f, 0.881921172f, 0.956940353f, -0.956940353f, +}; +ALIGN16_BEG const float ALIGN16_END cftmdl_wk1r[4] = { + 0.707106769f, 0.707106769f, 0.707106769f, -0.707106769f, +}; -static void bitrv2_128(float* a) { +static void bitrv2_128_C(float* a) { /* Following things have been attempted but are no faster: (a) Storing the swap indexes in a LUT (index calculations are done @@ -190,103 +197,15 @@ } } -static void makewt_32(void) { - const int nw = 32; - int j, nwh; - float delta, x, y; - - ip[0] = nw; - ip[1] = 1; - nwh = nw >> 1; - delta = atanf(1.0f) / nwh; - rdft_w[0] = 1; - rdft_w[1] = 0; - rdft_w[nwh] = cosf(delta * nwh); - rdft_w[nwh + 1] = rdft_w[nwh]; - for (j = 2; j < nwh; j += 2) { - x = cosf(delta * j); - y = sinf(delta * j); - rdft_w[j] = x; - rdft_w[j + 1] = y; - rdft_w[nw - j] = y; - rdft_w[nw - j + 1] = x; - } - bitrv2_32(ip + 2, rdft_w); - - // pre-calculate constants used by cft1st_128 and cftmdl_128... - cftmdl_wk1r[0] = rdft_w[2]; - cftmdl_wk1r[1] = rdft_w[2]; - cftmdl_wk1r[2] = rdft_w[2]; - cftmdl_wk1r[3] = -rdft_w[2]; - { - int k1; - - for (k1 = 0, j = 0; j < 128; j += 16, k1 += 2) { - const int k2 = 2 * k1; - const float wk2r = rdft_w[k1 + 0]; - const float wk2i = rdft_w[k1 + 1]; - float wk1r, wk1i; - // ... scalar version. - wk1r = rdft_w[k2 + 0]; - wk1i = rdft_w[k2 + 1]; - rdft_wk3ri_first[k1 + 0] = wk1r - 2 * wk2i * wk1i; - rdft_wk3ri_first[k1 + 1] = 2 * wk2i * wk1r - wk1i; - wk1r = rdft_w[k2 + 2]; - wk1i = rdft_w[k2 + 3]; - rdft_wk3ri_second[k1 + 0] = wk1r - 2 * wk2r * wk1i; - rdft_wk3ri_second[k1 + 1] = 2 * wk2r * wk1r - wk1i; - // ... vector version. - rdft_wk1r[k2 + 0] = rdft_w[k2 + 0]; - rdft_wk1r[k2 + 1] = rdft_w[k2 + 0]; - rdft_wk1r[k2 + 2] = rdft_w[k2 + 2]; - rdft_wk1r[k2 + 3] = rdft_w[k2 + 2]; - rdft_wk2r[k2 + 0] = rdft_w[k1 + 0]; - rdft_wk2r[k2 + 1] = rdft_w[k1 + 0]; - rdft_wk2r[k2 + 2] = -rdft_w[k1 + 1]; - rdft_wk2r[k2 + 3] = -rdft_w[k1 + 1]; - rdft_wk3r[k2 + 0] = rdft_wk3ri_first[k1 + 0]; - rdft_wk3r[k2 + 1] = rdft_wk3ri_first[k1 + 0]; - rdft_wk3r[k2 + 2] = rdft_wk3ri_second[k1 + 0]; - rdft_wk3r[k2 + 3] = rdft_wk3ri_second[k1 + 0]; - rdft_wk1i[k2 + 0] = -rdft_w[k2 + 1]; - rdft_wk1i[k2 + 1] = rdft_w[k2 + 1]; - rdft_wk1i[k2 + 2] = -rdft_w[k2 + 3]; - rdft_wk1i[k2 + 3] = rdft_w[k2 + 3]; - rdft_wk2i[k2 + 0] = -rdft_w[k1 + 1]; - rdft_wk2i[k2 + 1] = rdft_w[k1 + 1]; - rdft_wk2i[k2 + 2] = -rdft_w[k1 + 0]; - rdft_wk2i[k2 + 3] = rdft_w[k1 + 0]; - rdft_wk3i[k2 + 0] = -rdft_wk3ri_first[k1 + 1]; - rdft_wk3i[k2 + 1] = rdft_wk3ri_first[k1 + 1]; - rdft_wk3i[k2 + 2] = -rdft_wk3ri_second[k1 + 1]; - rdft_wk3i[k2 + 3] = rdft_wk3ri_second[k1 + 1]; - } - } -} - -static void makect_32(void) { - float* c = rdft_w + 32; - const int nc = 32; - int j, nch; - float delta; - - ip[1] = nc; - nch = nc >> 1; - delta = atanf(1.0f) / nch; - c[0] = cosf(delta * nch); - c[nch] = 0.5f * c[0]; - for (j = 1; j < nch; j++) { - c[j] = 0.5f * cosf(delta * j); - c[nc - j] = 0.5f * sinf(delta * j); - } -} - static void cft1st_128_C(float* a) { const int n = 128; int j, k1, k2; float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + // The processing of the first set of elements was simplified in C to avoid + // some operations (multiplication by zero or one, addition of two elements + // multiplied by the same weight, ...). x0r = a[0] + a[2]; x0i = a[1] + a[3]; x1r = a[0] - a[2]; @@ -512,7 +431,7 @@ } } -static void cftfsub_128(float* a) { +static void cftfsub_128_C(float* a) { int j, j1, j2, j3, l; float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; @@ -542,7 +461,7 @@ } } -static void cftbsub_128(float* a) { +static void cftbsub_128_C(float* a) { int j, j1, j2, j3, l; float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; @@ -640,18 +559,27 @@ rft_sub_128_t cftmdl_128; rft_sub_128_t rftfsub_128; rft_sub_128_t rftbsub_128; +rft_sub_128_t cftfsub_128; +rft_sub_128_t cftbsub_128; +rft_sub_128_t bitrv2_128; void aec_rdft_init(void) { cft1st_128 = cft1st_128_C; cftmdl_128 = cftmdl_128_C; rftfsub_128 = rftfsub_128_C; rftbsub_128 = rftbsub_128_C; + cftfsub_128 = cftfsub_128_C; + cftbsub_128 = cftbsub_128_C; + bitrv2_128 = bitrv2_128_C; #if defined(WEBRTC_ARCH_X86_FAMILY) if (WebRtc_GetCPUInfo(kSSE2)) { aec_rdft_init_sse2(); } #endif - // init library constants. - makewt_32(); - makect_32(); +#if defined(MIPS_FPU_LE) + aec_rdft_init_mips(); +#endif +#if defined(WEBRTC_DETECT_ARM_NEON) || defined(WEBRTC_ARCH_ARM_NEON) + aec_rdft_init_neon(); +#endif } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_rdft.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_rdft.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_rdft.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_rdft.h 2015-02-03 14:33:35.000000000 +0000 @@ -11,6 +11,8 @@ #ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_RDFT_H_ #define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_RDFT_H_ +#include "webrtc/modules/audio_processing/aec/aec_common.h" + // These intrinsics were unavailable before VS 2008. // TODO(andrew): move to a common file. #if defined(_MSC_VER) && _MSC_VER < 1500 @@ -19,27 +21,19 @@ static __inline __m128i _mm_castps_si128(__m128 a) { return *(__m128i*)&a; } #endif -#ifdef _MSC_VER /* visual c++ */ -#define ALIGN16_BEG __declspec(align(16)) -#define ALIGN16_END -#else /* gcc or icc */ -#define ALIGN16_BEG -#define ALIGN16_END __attribute__((aligned(16))) -#endif - -// constants shared by all paths (C, SSE2). -extern float rdft_w[64]; -// constants used by the C path. -extern float rdft_wk3ri_first[32]; -extern float rdft_wk3ri_second[32]; -// constants used by SSE2 but initialized in C path. -extern ALIGN16_BEG float ALIGN16_END rdft_wk1r[32]; -extern ALIGN16_BEG float ALIGN16_END rdft_wk2r[32]; -extern ALIGN16_BEG float ALIGN16_END rdft_wk3r[32]; -extern ALIGN16_BEG float ALIGN16_END rdft_wk1i[32]; -extern ALIGN16_BEG float ALIGN16_END rdft_wk2i[32]; -extern ALIGN16_BEG float ALIGN16_END rdft_wk3i[32]; -extern ALIGN16_BEG float ALIGN16_END cftmdl_wk1r[4]; +// Constants shared by all paths (C, SSE2, NEON). +extern const float rdft_w[64]; +// Constants used by the C path. +extern const float rdft_wk3ri_first[16]; +extern const float rdft_wk3ri_second[16]; +// Constants used by SSE2 and NEON but initialized in the C path. +extern ALIGN16_BEG const float ALIGN16_END rdft_wk1r[32]; +extern ALIGN16_BEG const float ALIGN16_END rdft_wk2r[32]; +extern ALIGN16_BEG const float ALIGN16_END rdft_wk3r[32]; +extern ALIGN16_BEG const float ALIGN16_END rdft_wk1i[32]; +extern ALIGN16_BEG const float ALIGN16_END rdft_wk2i[32]; +extern ALIGN16_BEG const float ALIGN16_END rdft_wk3i[32]; +extern ALIGN16_BEG const float ALIGN16_END cftmdl_wk1r[4]; // code path selection function pointers typedef void (*rft_sub_128_t)(float* a); @@ -47,6 +41,9 @@ extern rft_sub_128_t rftbsub_128; extern rft_sub_128_t cft1st_128; extern rft_sub_128_t cftmdl_128; +extern rft_sub_128_t cftfsub_128; +extern rft_sub_128_t cftbsub_128; +extern rft_sub_128_t bitrv2_128; // entry points void aec_rdft_init(void); @@ -54,4 +51,11 @@ void aec_rdft_forward_128(float* a); void aec_rdft_inverse_128(float* a); +#if defined(MIPS_FPU_LE) +void aec_rdft_init_mips(void); +#endif +#if defined(WEBRTC_DETECT_ARM_NEON) || defined(WEBRTC_ARCH_ARM_NEON) +void aec_rdft_init_neon(void); +#endif + #endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_RDFT_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_rdft_mips.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_rdft_mips.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_rdft_mips.c 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_rdft_mips.c 2015-02-03 14:33:35.000000000 +0000 @@ -0,0 +1,1187 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_processing/aec/aec_rdft.h" +#include "webrtc/typedefs.h" + +static void bitrv2_128_mips(float* a) { + // n is 128 + float xr, xi, yr, yi; + + xr = a[8]; + xi = a[9]; + yr = a[16]; + yi = a[17]; + a[8] = yr; + a[9] = yi; + a[16] = xr; + a[17] = xi; + + xr = a[64]; + xi = a[65]; + yr = a[2]; + yi = a[3]; + a[64] = yr; + a[65] = yi; + a[2] = xr; + a[3] = xi; + + xr = a[72]; + xi = a[73]; + yr = a[18]; + yi = a[19]; + a[72] = yr; + a[73] = yi; + a[18] = xr; + a[19] = xi; + + xr = a[80]; + xi = a[81]; + yr = a[10]; + yi = a[11]; + a[80] = yr; + a[81] = yi; + a[10] = xr; + a[11] = xi; + + xr = a[88]; + xi = a[89]; + yr = a[26]; + yi = a[27]; + a[88] = yr; + a[89] = yi; + a[26] = xr; + a[27] = xi; + + xr = a[74]; + xi = a[75]; + yr = a[82]; + yi = a[83]; + a[74] = yr; + a[75] = yi; + a[82] = xr; + a[83] = xi; + + xr = a[32]; + xi = a[33]; + yr = a[4]; + yi = a[5]; + a[32] = yr; + a[33] = yi; + a[4] = xr; + a[5] = xi; + + xr = a[40]; + xi = a[41]; + yr = a[20]; + yi = a[21]; + a[40] = yr; + a[41] = yi; + a[20] = xr; + a[21] = xi; + + xr = a[48]; + xi = a[49]; + yr = a[12]; + yi = a[13]; + a[48] = yr; + a[49] = yi; + a[12] = xr; + a[13] = xi; + + xr = a[56]; + xi = a[57]; + yr = a[28]; + yi = a[29]; + a[56] = yr; + a[57] = yi; + a[28] = xr; + a[29] = xi; + + xr = a[34]; + xi = a[35]; + yr = a[68]; + yi = a[69]; + a[34] = yr; + a[35] = yi; + a[68] = xr; + a[69] = xi; + + xr = a[42]; + xi = a[43]; + yr = a[84]; + yi = a[85]; + a[42] = yr; + a[43] = yi; + a[84] = xr; + a[85] = xi; + + xr = a[50]; + xi = a[51]; + yr = a[76]; + yi = a[77]; + a[50] = yr; + a[51] = yi; + a[76] = xr; + a[77] = xi; + + xr = a[58]; + xi = a[59]; + yr = a[92]; + yi = a[93]; + a[58] = yr; + a[59] = yi; + a[92] = xr; + a[93] = xi; + + xr = a[44]; + xi = a[45]; + yr = a[52]; + yi = a[53]; + a[44] = yr; + a[45] = yi; + a[52] = xr; + a[53] = xi; + + xr = a[96]; + xi = a[97]; + yr = a[6]; + yi = a[7]; + a[96] = yr; + a[97] = yi; + a[6] = xr; + a[7] = xi; + + xr = a[104]; + xi = a[105]; + yr = a[22]; + yi = a[23]; + a[104] = yr; + a[105] = yi; + a[22] = xr; + a[23] = xi; + + xr = a[112]; + xi = a[113]; + yr = a[14]; + yi = a[15]; + a[112] = yr; + a[113] = yi; + a[14] = xr; + a[15] = xi; + + xr = a[120]; + xi = a[121]; + yr = a[30]; + yi = a[31]; + a[120] = yr; + a[121] = yi; + a[30] = xr; + a[31] = xi; + + xr = a[98]; + xi = a[99]; + yr = a[70]; + yi = a[71]; + a[98] = yr; + a[99] = yi; + a[70] = xr; + a[71] = xi; + + xr = a[106]; + xi = a[107]; + yr = a[86]; + yi = a[87]; + a[106] = yr; + a[107] = yi; + a[86] = xr; + a[87] = xi; + + xr = a[114]; + xi = a[115]; + yr = a[78]; + yi = a[79]; + a[114] = yr; + a[115] = yi; + a[78] = xr; + a[79] = xi; + + xr = a[122]; + xi = a[123]; + yr = a[94]; + yi = a[95]; + a[122] = yr; + a[123] = yi; + a[94] = xr; + a[95] = xi; + + xr = a[100]; + xi = a[101]; + yr = a[38]; + yi = a[39]; + a[100] = yr; + a[101] = yi; + a[38] = xr; + a[39] = xi; + + xr = a[108]; + xi = a[109]; + yr = a[54]; + yi = a[55]; + a[108] = yr; + a[109] = yi; + a[54] = xr; + a[55] = xi; + + xr = a[116]; + xi = a[117]; + yr = a[46]; + yi = a[47]; + a[116] = yr; + a[117] = yi; + a[46] = xr; + a[47] = xi; + + xr = a[124]; + xi = a[125]; + yr = a[62]; + yi = a[63]; + a[124] = yr; + a[125] = yi; + a[62] = xr; + a[63] = xi; + + xr = a[110]; + xi = a[111]; + yr = a[118]; + yi = a[119]; + a[110] = yr; + a[111] = yi; + a[118] = xr; + a[119] = xi; +} + +static void cft1st_128_mips(float* a) { + float f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14; + int a_ptr, p1_rdft, p2_rdft, count; + const float* first = rdft_wk3ri_first; + const float* second = rdft_wk3ri_second; + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + // first 8 + "lwc1 %[f0], 0(%[a]) \n\t" + "lwc1 %[f1], 4(%[a]) \n\t" + "lwc1 %[f2], 8(%[a]) \n\t" + "lwc1 %[f3], 12(%[a]) \n\t" + "lwc1 %[f4], 16(%[a]) \n\t" + "lwc1 %[f5], 20(%[a]) \n\t" + "lwc1 %[f6], 24(%[a]) \n\t" + "lwc1 %[f7], 28(%[a]) \n\t" + "add.s %[f8], %[f0], %[f2] \n\t" + "sub.s %[f0], %[f0], %[f2] \n\t" + "add.s %[f2], %[f4], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "add.s %[f6], %[f1], %[f3] \n\t" + "sub.s %[f1], %[f1], %[f3] \n\t" + "add.s %[f3], %[f5], %[f7] \n\t" + "sub.s %[f5], %[f5], %[f7] \n\t" + "add.s %[f7], %[f8], %[f2] \n\t" + "sub.s %[f8], %[f8], %[f2] \n\t" + "sub.s %[f2], %[f1], %[f4] \n\t" + "add.s %[f1], %[f1], %[f4] \n\t" + "add.s %[f4], %[f6], %[f3] \n\t" + "sub.s %[f6], %[f6], %[f3] \n\t" + "sub.s %[f3], %[f0], %[f5] \n\t" + "add.s %[f0], %[f0], %[f5] \n\t" + "swc1 %[f7], 0(%[a]) \n\t" + "swc1 %[f8], 16(%[a]) \n\t" + "swc1 %[f2], 28(%[a]) \n\t" + "swc1 %[f1], 12(%[a]) \n\t" + "swc1 %[f4], 4(%[a]) \n\t" + "swc1 %[f6], 20(%[a]) \n\t" + "swc1 %[f3], 8(%[a]) \n\t" + "swc1 %[f0], 24(%[a]) \n\t" + // second 8 + "lwc1 %[f0], 32(%[a]) \n\t" + "lwc1 %[f1], 36(%[a]) \n\t" + "lwc1 %[f2], 40(%[a]) \n\t" + "lwc1 %[f3], 44(%[a]) \n\t" + "lwc1 %[f4], 48(%[a]) \n\t" + "lwc1 %[f5], 52(%[a]) \n\t" + "lwc1 %[f6], 56(%[a]) \n\t" + "lwc1 %[f7], 60(%[a]) \n\t" + "add.s %[f8], %[f4], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "add.s %[f6], %[f1], %[f3] \n\t" + "sub.s %[f1], %[f1], %[f3] \n\t" + "add.s %[f3], %[f0], %[f2] \n\t" + "sub.s %[f0], %[f0], %[f2] \n\t" + "add.s %[f2], %[f5], %[f7] \n\t" + "sub.s %[f5], %[f5], %[f7] \n\t" + "add.s %[f7], %[f4], %[f1] \n\t" + "sub.s %[f4], %[f4], %[f1] \n\t" + "add.s %[f1], %[f3], %[f8] \n\t" + "sub.s %[f3], %[f3], %[f8] \n\t" + "sub.s %[f8], %[f0], %[f5] \n\t" + "add.s %[f0], %[f0], %[f5] \n\t" + "add.s %[f5], %[f6], %[f2] \n\t" + "sub.s %[f6], %[f2], %[f6] \n\t" + "lwc1 %[f9], 8(%[rdft_w]) \n\t" + "sub.s %[f2], %[f8], %[f7] \n\t" + "add.s %[f8], %[f8], %[f7] \n\t" + "sub.s %[f7], %[f4], %[f0] \n\t" + "add.s %[f4], %[f4], %[f0] \n\t" + // prepare for loop + "addiu %[a_ptr], %[a], 64 \n\t" + "addiu %[p1_rdft], %[rdft_w], 8 \n\t" + "addiu %[p2_rdft], %[rdft_w], 16 \n\t" + "addiu %[count], $zero, 7 \n\t" + // finish second 8 + "mul.s %[f2], %[f9], %[f2] \n\t" + "mul.s %[f8], %[f9], %[f8] \n\t" + "mul.s %[f7], %[f9], %[f7] \n\t" + "mul.s %[f4], %[f9], %[f4] \n\t" + "swc1 %[f1], 32(%[a]) \n\t" + "swc1 %[f3], 52(%[a]) \n\t" + "swc1 %[f5], 36(%[a]) \n\t" + "swc1 %[f6], 48(%[a]) \n\t" + "swc1 %[f2], 40(%[a]) \n\t" + "swc1 %[f8], 44(%[a]) \n\t" + "swc1 %[f7], 56(%[a]) \n\t" + "swc1 %[f4], 60(%[a]) \n\t" + // loop + "1: \n\t" + "lwc1 %[f0], 0(%[a_ptr]) \n\t" + "lwc1 %[f1], 4(%[a_ptr]) \n\t" + "lwc1 %[f2], 8(%[a_ptr]) \n\t" + "lwc1 %[f3], 12(%[a_ptr]) \n\t" + "lwc1 %[f4], 16(%[a_ptr]) \n\t" + "lwc1 %[f5], 20(%[a_ptr]) \n\t" + "lwc1 %[f6], 24(%[a_ptr]) \n\t" + "lwc1 %[f7], 28(%[a_ptr]) \n\t" + "add.s %[f8], %[f0], %[f2] \n\t" + "sub.s %[f0], %[f0], %[f2] \n\t" + "add.s %[f2], %[f4], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "add.s %[f6], %[f1], %[f3] \n\t" + "sub.s %[f1], %[f1], %[f3] \n\t" + "add.s %[f3], %[f5], %[f7] \n\t" + "sub.s %[f5], %[f5], %[f7] \n\t" + "lwc1 %[f10], 4(%[p1_rdft]) \n\t" + "lwc1 %[f11], 0(%[p2_rdft]) \n\t" + "lwc1 %[f12], 4(%[p2_rdft]) \n\t" + "lwc1 %[f13], 8(%[first]) \n\t" + "lwc1 %[f14], 12(%[first]) \n\t" + "add.s %[f7], %[f8], %[f2] \n\t" + "sub.s %[f8], %[f8], %[f2] \n\t" + "add.s %[f2], %[f6], %[f3] \n\t" + "sub.s %[f6], %[f6], %[f3] \n\t" + "add.s %[f3], %[f0], %[f5] \n\t" + "sub.s %[f0], %[f0], %[f5] \n\t" + "add.s %[f5], %[f1], %[f4] \n\t" + "sub.s %[f1], %[f1], %[f4] \n\t" + "swc1 %[f7], 0(%[a_ptr]) \n\t" + "swc1 %[f2], 4(%[a_ptr]) \n\t" + "mul.s %[f4], %[f9], %[f8] \n\t" +#if defined(MIPS32_R2_LE) + "mul.s %[f8], %[f10], %[f8] \n\t" + "mul.s %[f7], %[f11], %[f0] \n\t" + "mul.s %[f0], %[f12], %[f0] \n\t" + "mul.s %[f2], %[f13], %[f3] \n\t" + "mul.s %[f3], %[f14], %[f3] \n\t" + "nmsub.s %[f4], %[f4], %[f10], %[f6] \n\t" + "madd.s %[f8], %[f8], %[f9], %[f6] \n\t" + "nmsub.s %[f7], %[f7], %[f12], %[f5] \n\t" + "madd.s %[f0], %[f0], %[f11], %[f5] \n\t" + "nmsub.s %[f2], %[f2], %[f14], %[f1] \n\t" + "madd.s %[f3], %[f3], %[f13], %[f1] \n\t" +#else + "mul.s %[f7], %[f10], %[f6] \n\t" + "mul.s %[f6], %[f9], %[f6] \n\t" + "mul.s %[f8], %[f10], %[f8] \n\t" + "mul.s %[f2], %[f11], %[f0] \n\t" + "mul.s %[f11], %[f11], %[f5] \n\t" + "mul.s %[f5], %[f12], %[f5] \n\t" + "mul.s %[f0], %[f12], %[f0] \n\t" + "mul.s %[f12], %[f13], %[f3] \n\t" + "mul.s %[f13], %[f13], %[f1] \n\t" + "mul.s %[f1], %[f14], %[f1] \n\t" + "mul.s %[f3], %[f14], %[f3] \n\t" + "sub.s %[f4], %[f4], %[f7] \n\t" + "add.s %[f8], %[f6], %[f8] \n\t" + "sub.s %[f7], %[f2], %[f5] \n\t" + "add.s %[f0], %[f11], %[f0] \n\t" + "sub.s %[f2], %[f12], %[f1] \n\t" + "add.s %[f3], %[f13], %[f3] \n\t" +#endif + "swc1 %[f4], 16(%[a_ptr]) \n\t" + "swc1 %[f8], 20(%[a_ptr]) \n\t" + "swc1 %[f7], 8(%[a_ptr]) \n\t" + "swc1 %[f0], 12(%[a_ptr]) \n\t" + "swc1 %[f2], 24(%[a_ptr]) \n\t" + "swc1 %[f3], 28(%[a_ptr]) \n\t" + "lwc1 %[f0], 32(%[a_ptr]) \n\t" + "lwc1 %[f1], 36(%[a_ptr]) \n\t" + "lwc1 %[f2], 40(%[a_ptr]) \n\t" + "lwc1 %[f3], 44(%[a_ptr]) \n\t" + "lwc1 %[f4], 48(%[a_ptr]) \n\t" + "lwc1 %[f5], 52(%[a_ptr]) \n\t" + "lwc1 %[f6], 56(%[a_ptr]) \n\t" + "lwc1 %[f7], 60(%[a_ptr]) \n\t" + "add.s %[f8], %[f0], %[f2] \n\t" + "sub.s %[f0], %[f0], %[f2] \n\t" + "add.s %[f2], %[f4], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "add.s %[f6], %[f1], %[f3] \n\t" + "sub.s %[f1], %[f1], %[f3] \n\t" + "add.s %[f3], %[f5], %[f7] \n\t" + "sub.s %[f5], %[f5], %[f7] \n\t" + "lwc1 %[f11], 8(%[p2_rdft]) \n\t" + "lwc1 %[f12], 12(%[p2_rdft]) \n\t" + "lwc1 %[f13], 8(%[second]) \n\t" + "lwc1 %[f14], 12(%[second]) \n\t" + "add.s %[f7], %[f8], %[f2] \n\t" + "sub.s %[f8], %[f2], %[f8] \n\t" + "add.s %[f2], %[f6], %[f3] \n\t" + "sub.s %[f6], %[f3], %[f6] \n\t" + "add.s %[f3], %[f0], %[f5] \n\t" + "sub.s %[f0], %[f0], %[f5] \n\t" + "add.s %[f5], %[f1], %[f4] \n\t" + "sub.s %[f1], %[f1], %[f4] \n\t" + "swc1 %[f7], 32(%[a_ptr]) \n\t" + "swc1 %[f2], 36(%[a_ptr]) \n\t" + "mul.s %[f4], %[f10], %[f8] \n\t" +#if defined(MIPS32_R2_LE) + "mul.s %[f10], %[f10], %[f6] \n\t" + "mul.s %[f7], %[f11], %[f0] \n\t" + "mul.s %[f11], %[f11], %[f5] \n\t" + "mul.s %[f2], %[f13], %[f3] \n\t" + "mul.s %[f13], %[f13], %[f1] \n\t" + "madd.s %[f4], %[f4], %[f9], %[f6] \n\t" + "nmsub.s %[f10], %[f10], %[f9], %[f8] \n\t" + "nmsub.s %[f7], %[f7], %[f12], %[f5] \n\t" + "madd.s %[f11], %[f11], %[f12], %[f0] \n\t" + "nmsub.s %[f2], %[f2], %[f14], %[f1] \n\t" + "madd.s %[f13], %[f13], %[f14], %[f3] \n\t" +#else + "mul.s %[f2], %[f9], %[f6] \n\t" + "mul.s %[f10], %[f10], %[f6] \n\t" + "mul.s %[f9], %[f9], %[f8] \n\t" + "mul.s %[f7], %[f11], %[f0] \n\t" + "mul.s %[f8], %[f12], %[f5] \n\t" + "mul.s %[f11], %[f11], %[f5] \n\t" + "mul.s %[f12], %[f12], %[f0] \n\t" + "mul.s %[f5], %[f13], %[f3] \n\t" + "mul.s %[f0], %[f14], %[f1] \n\t" + "mul.s %[f13], %[f13], %[f1] \n\t" + "mul.s %[f14], %[f14], %[f3] \n\t" + "add.s %[f4], %[f4], %[f2] \n\t" + "sub.s %[f10], %[f10], %[f9] \n\t" + "sub.s %[f7], %[f7], %[f8] \n\t" + "add.s %[f11], %[f11], %[f12] \n\t" + "sub.s %[f2], %[f5], %[f0] \n\t" + "add.s %[f13], %[f13], %[f14] \n\t" +#endif + "swc1 %[f4], 48(%[a_ptr]) \n\t" + "swc1 %[f10], 52(%[a_ptr]) \n\t" + "swc1 %[f7], 40(%[a_ptr]) \n\t" + "swc1 %[f11], 44(%[a_ptr]) \n\t" + "swc1 %[f2], 56(%[a_ptr]) \n\t" + "swc1 %[f13], 60(%[a_ptr]) \n\t" + "addiu %[count], %[count], -1 \n\t" + "lwc1 %[f9], 8(%[p1_rdft]) \n\t" + "addiu %[a_ptr], %[a_ptr], 64 \n\t" + "addiu %[p1_rdft], %[p1_rdft], 8 \n\t" + "addiu %[p2_rdft], %[p2_rdft], 16 \n\t" + "addiu %[first], %[first], 8 \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[second], %[second], 8 \n\t" + ".set pop \n\t" + : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), + [f4] "=&f" (f4), [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), + [f8] "=&f" (f8), [f9] "=&f" (f9), [f10] "=&f" (f10), [f11] "=&f" (f11), + [f12] "=&f" (f12), [f13] "=&f" (f13), [f14] "=&f" (f14), + [a_ptr] "=&r" (a_ptr), [p1_rdft] "=&r" (p1_rdft), [first] "+r" (first), + [p2_rdft] "=&r" (p2_rdft), [count] "=&r" (count), [second] "+r" (second) + : [a] "r" (a), [rdft_w] "r" (rdft_w) + : "memory" + ); +} + +static void cftmdl_128_mips(float* a) { + float f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14; + int tmp_a, count; + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[tmp_a], %[a], 0 \n\t" + "addiu %[count], $zero, 4 \n\t" + "1: \n\t" + "addiu %[count], %[count], -1 \n\t" + "lwc1 %[f0], 0(%[tmp_a]) \n\t" + "lwc1 %[f2], 32(%[tmp_a]) \n\t" + "lwc1 %[f4], 64(%[tmp_a]) \n\t" + "lwc1 %[f6], 96(%[tmp_a]) \n\t" + "lwc1 %[f1], 4(%[tmp_a]) \n\t" + "lwc1 %[f3], 36(%[tmp_a]) \n\t" + "lwc1 %[f5], 68(%[tmp_a]) \n\t" + "lwc1 %[f7], 100(%[tmp_a]) \n\t" + "add.s %[f8], %[f0], %[f2] \n\t" + "sub.s %[f0], %[f0], %[f2] \n\t" + "add.s %[f2], %[f4], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "add.s %[f6], %[f1], %[f3] \n\t" + "sub.s %[f1], %[f1], %[f3] \n\t" + "add.s %[f3], %[f5], %[f7] \n\t" + "sub.s %[f5], %[f5], %[f7] \n\t" + "add.s %[f7], %[f8], %[f2] \n\t" + "sub.s %[f8], %[f8], %[f2] \n\t" + "add.s %[f2], %[f1], %[f4] \n\t" + "sub.s %[f1], %[f1], %[f4] \n\t" + "add.s %[f4], %[f6], %[f3] \n\t" + "sub.s %[f6], %[f6], %[f3] \n\t" + "sub.s %[f3], %[f0], %[f5] \n\t" + "add.s %[f0], %[f0], %[f5] \n\t" + "swc1 %[f7], 0(%[tmp_a]) \n\t" + "swc1 %[f8], 64(%[tmp_a]) \n\t" + "swc1 %[f2], 36(%[tmp_a]) \n\t" + "swc1 %[f1], 100(%[tmp_a]) \n\t" + "swc1 %[f4], 4(%[tmp_a]) \n\t" + "swc1 %[f6], 68(%[tmp_a]) \n\t" + "swc1 %[f3], 32(%[tmp_a]) \n\t" + "swc1 %[f0], 96(%[tmp_a]) \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[tmp_a], %[tmp_a], 8 \n\t" + ".set pop \n\t" + : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), + [f4] "=&f" (f4), [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), + [f8] "=&f" (f8), [tmp_a] "=&r" (tmp_a), [count] "=&r" (count) + : [a] "r" (a) + : "memory" + ); + f9 = rdft_w[2]; + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[tmp_a], %[a], 128 \n\t" + "addiu %[count], $zero, 4 \n\t" + "1: \n\t" + "addiu %[count], %[count], -1 \n\t" + "lwc1 %[f0], 0(%[tmp_a]) \n\t" + "lwc1 %[f2], 32(%[tmp_a]) \n\t" + "lwc1 %[f5], 68(%[tmp_a]) \n\t" + "lwc1 %[f7], 100(%[tmp_a]) \n\t" + "lwc1 %[f1], 4(%[tmp_a]) \n\t" + "lwc1 %[f3], 36(%[tmp_a]) \n\t" + "lwc1 %[f4], 64(%[tmp_a]) \n\t" + "lwc1 %[f6], 96(%[tmp_a]) \n\t" + "sub.s %[f8], %[f0], %[f2] \n\t" + "add.s %[f0], %[f0], %[f2] \n\t" + "sub.s %[f2], %[f5], %[f7] \n\t" + "add.s %[f5], %[f5], %[f7] \n\t" + "sub.s %[f7], %[f1], %[f3] \n\t" + "add.s %[f1], %[f1], %[f3] \n\t" + "sub.s %[f3], %[f4], %[f6] \n\t" + "add.s %[f4], %[f4], %[f6] \n\t" + "sub.s %[f6], %[f8], %[f2] \n\t" + "add.s %[f8], %[f8], %[f2] \n\t" + "add.s %[f2], %[f5], %[f1] \n\t" + "sub.s %[f5], %[f5], %[f1] \n\t" + "add.s %[f1], %[f3], %[f7] \n\t" + "sub.s %[f3], %[f3], %[f7] \n\t" + "add.s %[f7], %[f0], %[f4] \n\t" + "sub.s %[f0], %[f0], %[f4] \n\t" + "sub.s %[f4], %[f6], %[f1] \n\t" + "add.s %[f6], %[f6], %[f1] \n\t" + "sub.s %[f1], %[f3], %[f8] \n\t" + "add.s %[f3], %[f3], %[f8] \n\t" + "mul.s %[f4], %[f4], %[f9] \n\t" + "mul.s %[f6], %[f6], %[f9] \n\t" + "mul.s %[f1], %[f1], %[f9] \n\t" + "mul.s %[f3], %[f3], %[f9] \n\t" + "swc1 %[f7], 0(%[tmp_a]) \n\t" + "swc1 %[f2], 4(%[tmp_a]) \n\t" + "swc1 %[f5], 64(%[tmp_a]) \n\t" + "swc1 %[f0], 68(%[tmp_a]) \n\t" + "swc1 %[f4], 32(%[tmp_a]) \n\t" + "swc1 %[f6], 36(%[tmp_a]) \n\t" + "swc1 %[f1], 96(%[tmp_a]) \n\t" + "swc1 %[f3], 100(%[tmp_a]) \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[tmp_a], %[tmp_a], 8 \n\t" + ".set pop \n\t" + : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), + [f4] "=&f" (f4), [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), + [f8] "=&f" (f8), [tmp_a] "=&r" (tmp_a), [count] "=&r" (count) + : [a] "r" (a), [f9] "f" (f9) + : "memory" + ); + f10 = rdft_w[3]; + f11 = rdft_w[4]; + f12 = rdft_w[5]; + f13 = rdft_wk3ri_first[2]; + f14 = rdft_wk3ri_first[3]; + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[tmp_a], %[a], 256 \n\t" + "addiu %[count], $zero, 4 \n\t" + "1: \n\t" + "addiu %[count], %[count], -1 \n\t" + "lwc1 %[f0], 0(%[tmp_a]) \n\t" + "lwc1 %[f2], 32(%[tmp_a]) \n\t" + "lwc1 %[f4], 64(%[tmp_a]) \n\t" + "lwc1 %[f6], 96(%[tmp_a]) \n\t" + "lwc1 %[f1], 4(%[tmp_a]) \n\t" + "lwc1 %[f3], 36(%[tmp_a]) \n\t" + "lwc1 %[f5], 68(%[tmp_a]) \n\t" + "lwc1 %[f7], 100(%[tmp_a]) \n\t" + "add.s %[f8], %[f0], %[f2] \n\t" + "sub.s %[f0], %[f0], %[f2] \n\t" + "add.s %[f2], %[f4], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "add.s %[f6], %[f1], %[f3] \n\t" + "sub.s %[f1], %[f1], %[f3] \n\t" + "add.s %[f3], %[f5], %[f7] \n\t" + "sub.s %[f5], %[f5], %[f7] \n\t" + "sub.s %[f7], %[f8], %[f2] \n\t" + "add.s %[f8], %[f8], %[f2] \n\t" + "add.s %[f2], %[f1], %[f4] \n\t" + "sub.s %[f1], %[f1], %[f4] \n\t" + "sub.s %[f4], %[f6], %[f3] \n\t" + "add.s %[f6], %[f6], %[f3] \n\t" + "sub.s %[f3], %[f0], %[f5] \n\t" + "add.s %[f0], %[f0], %[f5] \n\t" + "swc1 %[f8], 0(%[tmp_a]) \n\t" + "swc1 %[f6], 4(%[tmp_a]) \n\t" + "mul.s %[f5], %[f9], %[f7] \n\t" +#if defined(MIPS32_R2_LE) + "mul.s %[f7], %[f10], %[f7] \n\t" + "mul.s %[f8], %[f11], %[f3] \n\t" + "mul.s %[f3], %[f12], %[f3] \n\t" + "mul.s %[f6], %[f13], %[f0] \n\t" + "mul.s %[f0], %[f14], %[f0] \n\t" + "nmsub.s %[f5], %[f5], %[f10], %[f4] \n\t" + "madd.s %[f7], %[f7], %[f9], %[f4] \n\t" + "nmsub.s %[f8], %[f8], %[f12], %[f2] \n\t" + "madd.s %[f3], %[f3], %[f11], %[f2] \n\t" + "nmsub.s %[f6], %[f6], %[f14], %[f1] \n\t" + "madd.s %[f0], %[f0], %[f13], %[f1] \n\t" + "swc1 %[f5], 64(%[tmp_a]) \n\t" + "swc1 %[f7], 68(%[tmp_a]) \n\t" +#else + "mul.s %[f8], %[f10], %[f4] \n\t" + "mul.s %[f4], %[f9], %[f4] \n\t" + "mul.s %[f7], %[f10], %[f7] \n\t" + "mul.s %[f6], %[f11], %[f3] \n\t" + "mul.s %[f3], %[f12], %[f3] \n\t" + "sub.s %[f5], %[f5], %[f8] \n\t" + "mul.s %[f8], %[f12], %[f2] \n\t" + "mul.s %[f2], %[f11], %[f2] \n\t" + "add.s %[f7], %[f4], %[f7] \n\t" + "mul.s %[f4], %[f13], %[f0] \n\t" + "mul.s %[f0], %[f14], %[f0] \n\t" + "sub.s %[f8], %[f6], %[f8] \n\t" + "mul.s %[f6], %[f14], %[f1] \n\t" + "mul.s %[f1], %[f13], %[f1] \n\t" + "add.s %[f3], %[f2], %[f3] \n\t" + "swc1 %[f5], 64(%[tmp_a]) \n\t" + "swc1 %[f7], 68(%[tmp_a]) \n\t" + "sub.s %[f6], %[f4], %[f6] \n\t" + "add.s %[f0], %[f1], %[f0] \n\t" +#endif + "swc1 %[f8], 32(%[tmp_a]) \n\t" + "swc1 %[f3], 36(%[tmp_a]) \n\t" + "swc1 %[f6], 96(%[tmp_a]) \n\t" + "swc1 %[f0], 100(%[tmp_a]) \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[tmp_a], %[tmp_a], 8 \n\t" + ".set pop \n\t" + : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), + [f4] "=&f" (f4), [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), + [f8] "=&f" (f8), [tmp_a] "=&r" (tmp_a), [count] "=&r" (count) + : [a] "r" (a), [f9] "f" (f9), [f10] "f" (f10), [f11] "f" (f11), + [f12] "f" (f12), [f13] "f" (f13), [f14] "f" (f14) + : "memory" + ); + f11 = rdft_w[6]; + f12 = rdft_w[7]; + f13 = rdft_wk3ri_second[2]; + f14 = rdft_wk3ri_second[3]; + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[tmp_a], %[a], 384 \n\t" + "addiu %[count], $zero, 4 \n\t" + "1: \n\t" + "addiu %[count], %[count], -1 \n\t" + "lwc1 %[f0], 0(%[tmp_a]) \n\t" + "lwc1 %[f1], 4(%[tmp_a]) \n\t" + "lwc1 %[f2], 32(%[tmp_a]) \n\t" + "lwc1 %[f3], 36(%[tmp_a]) \n\t" + "lwc1 %[f4], 64(%[tmp_a]) \n\t" + "lwc1 %[f5], 68(%[tmp_a]) \n\t" + "lwc1 %[f6], 96(%[tmp_a]) \n\t" + "lwc1 %[f7], 100(%[tmp_a]) \n\t" + "add.s %[f8], %[f0], %[f2] \n\t" + "sub.s %[f0], %[f0], %[f2] \n\t" + "add.s %[f2], %[f4], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "add.s %[f6], %[f1], %[f3] \n\t" + "sub.s %[f1], %[f1], %[f3] \n\t" + "add.s %[f3], %[f5], %[f7] \n\t" + "sub.s %[f5], %[f5], %[f7] \n\t" + "sub.s %[f7], %[f2], %[f8] \n\t" + "add.s %[f2], %[f2], %[f8] \n\t" + "add.s %[f8], %[f1], %[f4] \n\t" + "sub.s %[f1], %[f1], %[f4] \n\t" + "sub.s %[f4], %[f3], %[f6] \n\t" + "add.s %[f3], %[f3], %[f6] \n\t" + "sub.s %[f6], %[f0], %[f5] \n\t" + "add.s %[f0], %[f0], %[f5] \n\t" + "swc1 %[f2], 0(%[tmp_a]) \n\t" + "swc1 %[f3], 4(%[tmp_a]) \n\t" + "mul.s %[f5], %[f10], %[f7] \n\t" +#if defined(MIPS32_R2_LE) + "mul.s %[f7], %[f9], %[f7] \n\t" + "mul.s %[f2], %[f12], %[f8] \n\t" + "mul.s %[f8], %[f11], %[f8] \n\t" + "mul.s %[f3], %[f14], %[f1] \n\t" + "mul.s %[f1], %[f13], %[f1] \n\t" + "madd.s %[f5], %[f5], %[f9], %[f4] \n\t" + "msub.s %[f7], %[f7], %[f10], %[f4] \n\t" + "msub.s %[f2], %[f2], %[f11], %[f6] \n\t" + "madd.s %[f8], %[f8], %[f12], %[f6] \n\t" + "msub.s %[f3], %[f3], %[f13], %[f0] \n\t" + "madd.s %[f1], %[f1], %[f14], %[f0] \n\t" + "swc1 %[f5], 64(%[tmp_a]) \n\t" + "swc1 %[f7], 68(%[tmp_a]) \n\t" +#else + "mul.s %[f2], %[f9], %[f4] \n\t" + "mul.s %[f4], %[f10], %[f4] \n\t" + "mul.s %[f7], %[f9], %[f7] \n\t" + "mul.s %[f3], %[f11], %[f6] \n\t" + "mul.s %[f6], %[f12], %[f6] \n\t" + "add.s %[f5], %[f5], %[f2] \n\t" + "sub.s %[f7], %[f4], %[f7] \n\t" + "mul.s %[f2], %[f12], %[f8] \n\t" + "mul.s %[f8], %[f11], %[f8] \n\t" + "mul.s %[f4], %[f14], %[f1] \n\t" + "mul.s %[f1], %[f13], %[f1] \n\t" + "sub.s %[f2], %[f3], %[f2] \n\t" + "mul.s %[f3], %[f13], %[f0] \n\t" + "mul.s %[f0], %[f14], %[f0] \n\t" + "add.s %[f8], %[f8], %[f6] \n\t" + "swc1 %[f5], 64(%[tmp_a]) \n\t" + "swc1 %[f7], 68(%[tmp_a]) \n\t" + "sub.s %[f3], %[f3], %[f4] \n\t" + "add.s %[f1], %[f1], %[f0] \n\t" +#endif + "swc1 %[f2], 32(%[tmp_a]) \n\t" + "swc1 %[f8], 36(%[tmp_a]) \n\t" + "swc1 %[f3], 96(%[tmp_a]) \n\t" + "swc1 %[f1], 100(%[tmp_a]) \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[tmp_a], %[tmp_a], 8 \n\t" + ".set pop \n\t" + : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), + [f4] "=&f" (f4), [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), + [f8] "=&f" (f8), [tmp_a] "=&r" (tmp_a), [count] "=&r" (count) + : [a] "r" (a), [f9] "f" (f9), [f10] "f" (f10), [f11] "f" (f11), + [f12] "f" (f12), [f13] "f" (f13), [f14] "f" (f14) + : "memory" + ); +} + +static void cftfsub_128_mips(float* a) { + float f0, f1, f2, f3, f4, f5, f6, f7, f8; + int tmp_a, count; + + cft1st_128(a); + cftmdl_128(a); + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[tmp_a], %[a], 0 \n\t" + "addiu %[count], $zero, 16 \n\t" + "1: \n\t" + "addiu %[count], %[count], -1 \n\t" + "lwc1 %[f0], 0(%[tmp_a]) \n\t" + "lwc1 %[f2], 128(%[tmp_a]) \n\t" + "lwc1 %[f4], 256(%[tmp_a]) \n\t" + "lwc1 %[f6], 384(%[tmp_a]) \n\t" + "lwc1 %[f1], 4(%[tmp_a]) \n\t" + "lwc1 %[f3], 132(%[tmp_a]) \n\t" + "lwc1 %[f5], 260(%[tmp_a]) \n\t" + "lwc1 %[f7], 388(%[tmp_a]) \n\t" + "add.s %[f8], %[f0], %[f2] \n\t" + "sub.s %[f0], %[f0], %[f2] \n\t" + "add.s %[f2], %[f4], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "add.s %[f6], %[f1], %[f3] \n\t" + "sub.s %[f1], %[f1], %[f3] \n\t" + "add.s %[f3], %[f5], %[f7] \n\t" + "sub.s %[f5], %[f5], %[f7] \n\t" + "add.s %[f7], %[f8], %[f2] \n\t" + "sub.s %[f8], %[f8], %[f2] \n\t" + "add.s %[f2], %[f1], %[f4] \n\t" + "sub.s %[f1], %[f1], %[f4] \n\t" + "add.s %[f4], %[f6], %[f3] \n\t" + "sub.s %[f6], %[f6], %[f3] \n\t" + "sub.s %[f3], %[f0], %[f5] \n\t" + "add.s %[f0], %[f0], %[f5] \n\t" + "swc1 %[f7], 0(%[tmp_a]) \n\t" + "swc1 %[f8], 256(%[tmp_a]) \n\t" + "swc1 %[f2], 132(%[tmp_a]) \n\t" + "swc1 %[f1], 388(%[tmp_a]) \n\t" + "swc1 %[f4], 4(%[tmp_a]) \n\t" + "swc1 %[f6], 260(%[tmp_a]) \n\t" + "swc1 %[f3], 128(%[tmp_a]) \n\t" + "swc1 %[f0], 384(%[tmp_a]) \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[tmp_a], %[tmp_a], 8 \n\t" + ".set pop \n\t" + : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), + [f4] "=&f" (f4), [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), + [f8] "=&f" (f8), [tmp_a] "=&r" (tmp_a), + [count] "=&r" (count) + : [a] "r" (a) + : "memory" + ); +} + +static void cftbsub_128_mips(float* a) { + float f0, f1, f2, f3, f4, f5, f6, f7, f8; + int tmp_a, count; + + cft1st_128(a); + cftmdl_128(a); + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[tmp_a], %[a], 0 \n\t" + "addiu %[count], $zero, 16 \n\t" + "1: \n\t" + "addiu %[count], %[count], -1 \n\t" + "lwc1 %[f0], 0(%[tmp_a]) \n\t" + "lwc1 %[f2], 128(%[tmp_a]) \n\t" + "lwc1 %[f4], 256(%[tmp_a]) \n\t" + "lwc1 %[f6], 384(%[tmp_a]) \n\t" + "lwc1 %[f1], 4(%[tmp_a]) \n\t" + "lwc1 %[f3], 132(%[tmp_a]) \n\t" + "lwc1 %[f5], 260(%[tmp_a]) \n\t" + "lwc1 %[f7], 388(%[tmp_a]) \n\t" + "add.s %[f8], %[f0], %[f2] \n\t" + "sub.s %[f0], %[f0], %[f2] \n\t" + "add.s %[f2], %[f4], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "add.s %[f6], %[f1], %[f3] \n\t" + "sub.s %[f1], %[f3], %[f1] \n\t" + "add.s %[f3], %[f5], %[f7] \n\t" + "sub.s %[f5], %[f5], %[f7] \n\t" + "add.s %[f7], %[f8], %[f2] \n\t" + "sub.s %[f8], %[f8], %[f2] \n\t" + "sub.s %[f2], %[f1], %[f4] \n\t" + "add.s %[f1], %[f1], %[f4] \n\t" + "add.s %[f4], %[f3], %[f6] \n\t" + "sub.s %[f6], %[f3], %[f6] \n\t" + "sub.s %[f3], %[f0], %[f5] \n\t" + "add.s %[f0], %[f0], %[f5] \n\t" + "neg.s %[f4], %[f4] \n\t" + "swc1 %[f7], 0(%[tmp_a]) \n\t" + "swc1 %[f8], 256(%[tmp_a]) \n\t" + "swc1 %[f2], 132(%[tmp_a]) \n\t" + "swc1 %[f1], 388(%[tmp_a]) \n\t" + "swc1 %[f6], 260(%[tmp_a]) \n\t" + "swc1 %[f3], 128(%[tmp_a]) \n\t" + "swc1 %[f0], 384(%[tmp_a]) \n\t" + "swc1 %[f4], 4(%[tmp_a]) \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[tmp_a], %[tmp_a], 8 \n\t" + ".set pop \n\t" + : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), + [f4] "=&f" (f4), [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), + [f8] "=&f" (f8), [tmp_a] "=&r" (tmp_a), [count] "=&r" (count) + : [a] "r" (a) + : "memory" + ); +} + +static void rftfsub_128_mips(float* a) { + const float* c = rdft_w + 32; + const float f0 = 0.5f; + float* a1 = &a[2]; + float* a2 = &a[126]; + const float* c1 = &c[1]; + const float* c2 = &c[31]; + float f1, f2, f3 ,f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15; + int count; + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "lwc1 %[f6], 0(%[c2]) \n\t" + "lwc1 %[f1], 0(%[a1]) \n\t" + "lwc1 %[f2], 0(%[a2]) \n\t" + "lwc1 %[f3], 4(%[a1]) \n\t" + "lwc1 %[f4], 4(%[a2]) \n\t" + "lwc1 %[f5], 0(%[c1]) \n\t" + "sub.s %[f6], %[f0], %[f6] \n\t" + "sub.s %[f7], %[f1], %[f2] \n\t" + "add.s %[f8], %[f3], %[f4] \n\t" + "addiu %[count], $zero, 15 \n\t" + "mul.s %[f9], %[f6], %[f7] \n\t" + "mul.s %[f6], %[f6], %[f8] \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[f8], %[f5], %[f8] \n\t" + "mul.s %[f5], %[f5], %[f7] \n\t" + "sub.s %[f9], %[f9], %[f8] \n\t" + "add.s %[f6], %[f6], %[f5] \n\t" +#else + "nmsub.s %[f9], %[f9], %[f5], %[f8] \n\t" + "madd.s %[f6], %[f6], %[f5], %[f7] \n\t" +#endif + "sub.s %[f1], %[f1], %[f9] \n\t" + "add.s %[f2], %[f2], %[f9] \n\t" + "sub.s %[f3], %[f3], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "swc1 %[f1], 0(%[a1]) \n\t" + "swc1 %[f2], 0(%[a2]) \n\t" + "swc1 %[f3], 4(%[a1]) \n\t" + "swc1 %[f4], 4(%[a2]) \n\t" + "addiu %[a1], %[a1], 8 \n\t" + "addiu %[a2], %[a2], -8 \n\t" + "addiu %[c1], %[c1], 4 \n\t" + "addiu %[c2], %[c2], -4 \n\t" + "1: \n\t" + "lwc1 %[f6], 0(%[c2]) \n\t" + "lwc1 %[f1], 0(%[a1]) \n\t" + "lwc1 %[f2], 0(%[a2]) \n\t" + "lwc1 %[f3], 4(%[a1]) \n\t" + "lwc1 %[f4], 4(%[a2]) \n\t" + "lwc1 %[f5], 0(%[c1]) \n\t" + "sub.s %[f6], %[f0], %[f6] \n\t" + "sub.s %[f7], %[f1], %[f2] \n\t" + "add.s %[f8], %[f3], %[f4] \n\t" + "lwc1 %[f10], -4(%[c2]) \n\t" + "lwc1 %[f11], 8(%[a1]) \n\t" + "lwc1 %[f12], -8(%[a2]) \n\t" + "mul.s %[f9], %[f6], %[f7] \n\t" + "mul.s %[f6], %[f6], %[f8] \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[f8], %[f5], %[f8] \n\t" + "mul.s %[f5], %[f5], %[f7] \n\t" + "lwc1 %[f13], 12(%[a1]) \n\t" + "lwc1 %[f14], -4(%[a2]) \n\t" + "lwc1 %[f15], 4(%[c1]) \n\t" + "sub.s %[f9], %[f9], %[f8] \n\t" + "add.s %[f6], %[f6], %[f5] \n\t" +#else + "lwc1 %[f13], 12(%[a1]) \n\t" + "lwc1 %[f14], -4(%[a2]) \n\t" + "lwc1 %[f15], 4(%[c1]) \n\t" + "nmsub.s %[f9], %[f9], %[f5], %[f8] \n\t" + "madd.s %[f6], %[f6], %[f5], %[f7] \n\t" +#endif + "sub.s %[f10], %[f0], %[f10] \n\t" + "sub.s %[f5], %[f11], %[f12] \n\t" + "add.s %[f7], %[f13], %[f14] \n\t" + "sub.s %[f1], %[f1], %[f9] \n\t" + "add.s %[f2], %[f2], %[f9] \n\t" + "sub.s %[f3], %[f3], %[f6] \n\t" + "mul.s %[f8], %[f10], %[f5] \n\t" + "mul.s %[f10], %[f10], %[f7] \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[f9], %[f15], %[f7] \n\t" + "mul.s %[f15], %[f15], %[f5] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "swc1 %[f1], 0(%[a1]) \n\t" + "swc1 %[f2], 0(%[a2]) \n\t" + "sub.s %[f8], %[f8], %[f9] \n\t" + "add.s %[f10], %[f10], %[f15] \n\t" +#else + "swc1 %[f1], 0(%[a1]) \n\t" + "swc1 %[f2], 0(%[a2]) \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "nmsub.s %[f8], %[f8], %[f15], %[f7] \n\t" + "madd.s %[f10], %[f10], %[f15], %[f5] \n\t" +#endif + "swc1 %[f3], 4(%[a1]) \n\t" + "swc1 %[f4], 4(%[a2]) \n\t" + "sub.s %[f11], %[f11], %[f8] \n\t" + "add.s %[f12], %[f12], %[f8] \n\t" + "sub.s %[f13], %[f13], %[f10] \n\t" + "sub.s %[f14], %[f14], %[f10] \n\t" + "addiu %[c2], %[c2], -8 \n\t" + "addiu %[c1], %[c1], 8 \n\t" + "swc1 %[f11], 8(%[a1]) \n\t" + "swc1 %[f12], -8(%[a2]) \n\t" + "swc1 %[f13], 12(%[a1]) \n\t" + "swc1 %[f14], -4(%[a2]) \n\t" + "addiu %[a1], %[a1], 16 \n\t" + "addiu %[count], %[count], -1 \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[a2], %[a2], -16 \n\t" + ".set pop \n\t" + : [a1] "+r" (a1), [a2] "+r" (a2), [c1] "+r" (c1), [c2] "+r" (c2), + [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), [f4] "=&f" (f4), + [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), [f8] "=&f" (f8), + [f9] "=&f" (f9), [f10] "=&f" (f10), [f11] "=&f" (f11), [f12] "=&f" (f12), + [f13] "=&f" (f13), [f14] "=&f" (f14), [f15] "=&f" (f15), + [count] "=&r" (count) + : [f0] "f" (f0) + : "memory" + ); +} + +static void rftbsub_128_mips(float* a) { + const float *c = rdft_w + 32; + const float f0 = 0.5f; + float* a1 = &a[2]; + float* a2 = &a[126]; + const float* c1 = &c[1]; + const float* c2 = &c[31]; + float f1, f2, f3 ,f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15; + int count; + + a[1] = -a[1]; + a[65] = -a[65]; + + __asm __volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "lwc1 %[f6], 0(%[c2]) \n\t" + "lwc1 %[f1], 0(%[a1]) \n\t" + "lwc1 %[f2], 0(%[a2]) \n\t" + "lwc1 %[f3], 4(%[a1]) \n\t" + "lwc1 %[f4], 4(%[a2]) \n\t" + "lwc1 %[f5], 0(%[c1]) \n\t" + "sub.s %[f6], %[f0], %[f6] \n\t" + "sub.s %[f7], %[f1], %[f2] \n\t" + "add.s %[f8], %[f3], %[f4] \n\t" + "addiu %[count], $zero, 15 \n\t" + "mul.s %[f9], %[f6], %[f7] \n\t" + "mul.s %[f6], %[f6], %[f8] \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[f8], %[f5], %[f8] \n\t" + "mul.s %[f5], %[f5], %[f7] \n\t" + "add.s %[f9], %[f9], %[f8] \n\t" + "sub.s %[f6], %[f6], %[f5] \n\t" +#else + "madd.s %[f9], %[f9], %[f5], %[f8] \n\t" + "nmsub.s %[f6], %[f6], %[f5], %[f7] \n\t" +#endif + "sub.s %[f1], %[f1], %[f9] \n\t" + "add.s %[f2], %[f2], %[f9] \n\t" + "sub.s %[f3], %[f6], %[f3] \n\t" + "sub.s %[f4], %[f6], %[f4] \n\t" + "swc1 %[f1], 0(%[a1]) \n\t" + "swc1 %[f2], 0(%[a2]) \n\t" + "swc1 %[f3], 4(%[a1]) \n\t" + "swc1 %[f4], 4(%[a2]) \n\t" + "addiu %[a1], %[a1], 8 \n\t" + "addiu %[a2], %[a2], -8 \n\t" + "addiu %[c1], %[c1], 4 \n\t" + "addiu %[c2], %[c2], -4 \n\t" + "1: \n\t" + "lwc1 %[f6], 0(%[c2]) \n\t" + "lwc1 %[f1], 0(%[a1]) \n\t" + "lwc1 %[f2], 0(%[a2]) \n\t" + "lwc1 %[f3], 4(%[a1]) \n\t" + "lwc1 %[f4], 4(%[a2]) \n\t" + "lwc1 %[f5], 0(%[c1]) \n\t" + "sub.s %[f6], %[f0], %[f6] \n\t" + "sub.s %[f7], %[f1], %[f2] \n\t" + "add.s %[f8], %[f3], %[f4] \n\t" + "lwc1 %[f10], -4(%[c2]) \n\t" + "lwc1 %[f11], 8(%[a1]) \n\t" + "lwc1 %[f12], -8(%[a2]) \n\t" + "mul.s %[f9], %[f6], %[f7] \n\t" + "mul.s %[f6], %[f6], %[f8] \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[f8], %[f5], %[f8] \n\t" + "mul.s %[f5], %[f5], %[f7] \n\t" + "lwc1 %[f13], 12(%[a1]) \n\t" + "lwc1 %[f14], -4(%[a2]) \n\t" + "lwc1 %[f15], 4(%[c1]) \n\t" + "add.s %[f9], %[f9], %[f8] \n\t" + "sub.s %[f6], %[f6], %[f5] \n\t" +#else + "lwc1 %[f13], 12(%[a1]) \n\t" + "lwc1 %[f14], -4(%[a2]) \n\t" + "lwc1 %[f15], 4(%[c1]) \n\t" + "madd.s %[f9], %[f9], %[f5], %[f8] \n\t" + "nmsub.s %[f6], %[f6], %[f5], %[f7] \n\t" +#endif + "sub.s %[f10], %[f0], %[f10] \n\t" + "sub.s %[f5], %[f11], %[f12] \n\t" + "add.s %[f7], %[f13], %[f14] \n\t" + "sub.s %[f1], %[f1], %[f9] \n\t" + "add.s %[f2], %[f2], %[f9] \n\t" + "sub.s %[f3], %[f6], %[f3] \n\t" + "mul.s %[f8], %[f10], %[f5] \n\t" + "mul.s %[f10], %[f10], %[f7] \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[f9], %[f15], %[f7] \n\t" + "mul.s %[f15], %[f15], %[f5] \n\t" + "sub.s %[f4], %[f6], %[f4] \n\t" + "swc1 %[f1], 0(%[a1]) \n\t" + "swc1 %[f2], 0(%[a2]) \n\t" + "add.s %[f8], %[f8], %[f9] \n\t" + "sub.s %[f10], %[f10], %[f15] \n\t" +#else + "swc1 %[f1], 0(%[a1]) \n\t" + "swc1 %[f2], 0(%[a2]) \n\t" + "sub.s %[f4], %[f6], %[f4] \n\t" + "madd.s %[f8], %[f8], %[f15], %[f7] \n\t" + "nmsub.s %[f10], %[f10], %[f15], %[f5] \n\t" +#endif + "swc1 %[f3], 4(%[a1]) \n\t" + "swc1 %[f4], 4(%[a2]) \n\t" + "sub.s %[f11], %[f11], %[f8] \n\t" + "add.s %[f12], %[f12], %[f8] \n\t" + "sub.s %[f13], %[f10], %[f13] \n\t" + "sub.s %[f14], %[f10], %[f14] \n\t" + "addiu %[c2], %[c2], -8 \n\t" + "addiu %[c1], %[c1], 8 \n\t" + "swc1 %[f11], 8(%[a1]) \n\t" + "swc1 %[f12], -8(%[a2]) \n\t" + "swc1 %[f13], 12(%[a1]) \n\t" + "swc1 %[f14], -4(%[a2]) \n\t" + "addiu %[a1], %[a1], 16 \n\t" + "addiu %[count], %[count], -1 \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[a2], %[a2], -16 \n\t" + ".set pop \n\t" + : [a1] "+r" (a1), [a2] "+r" (a2), [c1] "+r" (c1), [c2] "+r" (c2), + [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), [f4] "=&f" (f4), + [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), [f8] "=&f" (f8), + [f9] "=&f" (f9), [f10] "=&f" (f10), [f11] "=&f" (f11), [f12] "=&f" (f12), + [f13] "=&f" (f13), [f14] "=&f" (f14), [f15] "=&f" (f15), + [count] "=&r" (count) + : [f0] "f" (f0) + : "memory" + ); +} + +void aec_rdft_init_mips(void) { + cft1st_128 = cft1st_128_mips; + cftmdl_128 = cftmdl_128_mips; + rftfsub_128 = rftfsub_128_mips; + rftbsub_128 = rftbsub_128_mips; + cftfsub_128 = cftfsub_128_mips; + cftbsub_128 = cftbsub_128_mips; + bitrv2_128 = bitrv2_128_mips; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_rdft_neon.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_rdft_neon.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_rdft_neon.c 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_rdft_neon.c 2015-02-03 14:33:35.000000000 +0000 @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * The rdft AEC algorithm, neon version of speed-critical functions. + * + * Based on the sse2 version. + */ + + +#include "webrtc/modules/audio_processing/aec/aec_rdft.h" + +#include + +static const ALIGN16_BEG float ALIGN16_END + k_swap_sign[4] = {-1.f, 1.f, -1.f, 1.f}; + +static void cft1st_128_neon(float* a) { + const float32x4_t vec_swap_sign = vld1q_f32((float32_t*)k_swap_sign); + int j, k2; + + for (k2 = 0, j = 0; j < 128; j += 16, k2 += 4) { + float32x4_t a00v = vld1q_f32(&a[j + 0]); + float32x4_t a04v = vld1q_f32(&a[j + 4]); + float32x4_t a08v = vld1q_f32(&a[j + 8]); + float32x4_t a12v = vld1q_f32(&a[j + 12]); + float32x4_t a01v = vcombine_f32(vget_low_f32(a00v), vget_low_f32(a08v)); + float32x4_t a23v = vcombine_f32(vget_high_f32(a00v), vget_high_f32(a08v)); + float32x4_t a45v = vcombine_f32(vget_low_f32(a04v), vget_low_f32(a12v)); + float32x4_t a67v = vcombine_f32(vget_high_f32(a04v), vget_high_f32(a12v)); + const float32x4_t wk1rv = vld1q_f32(&rdft_wk1r[k2]); + const float32x4_t wk1iv = vld1q_f32(&rdft_wk1i[k2]); + const float32x4_t wk2rv = vld1q_f32(&rdft_wk2r[k2]); + const float32x4_t wk2iv = vld1q_f32(&rdft_wk2i[k2]); + const float32x4_t wk3rv = vld1q_f32(&rdft_wk3r[k2]); + const float32x4_t wk3iv = vld1q_f32(&rdft_wk3i[k2]); + float32x4_t x0v = vaddq_f32(a01v, a23v); + const float32x4_t x1v = vsubq_f32(a01v, a23v); + const float32x4_t x2v = vaddq_f32(a45v, a67v); + const float32x4_t x3v = vsubq_f32(a45v, a67v); + const float32x4_t x3w = vrev64q_f32(x3v); + float32x4_t x0w; + a01v = vaddq_f32(x0v, x2v); + x0v = vsubq_f32(x0v, x2v); + x0w = vrev64q_f32(x0v); + a45v = vmulq_f32(wk2rv, x0v); + a45v = vmlaq_f32(a45v, wk2iv, x0w); + x0v = vmlaq_f32(x1v, x3w, vec_swap_sign); + x0w = vrev64q_f32(x0v); + a23v = vmulq_f32(wk1rv, x0v); + a23v = vmlaq_f32(a23v, wk1iv, x0w); + x0v = vmlsq_f32(x1v, x3w, vec_swap_sign); + x0w = vrev64q_f32(x0v); + a67v = vmulq_f32(wk3rv, x0v); + a67v = vmlaq_f32(a67v, wk3iv, x0w); + a00v = vcombine_f32(vget_low_f32(a01v), vget_low_f32(a23v)); + a04v = vcombine_f32(vget_low_f32(a45v), vget_low_f32(a67v)); + a08v = vcombine_f32(vget_high_f32(a01v), vget_high_f32(a23v)); + a12v = vcombine_f32(vget_high_f32(a45v), vget_high_f32(a67v)); + vst1q_f32(&a[j + 0], a00v); + vst1q_f32(&a[j + 4], a04v); + vst1q_f32(&a[j + 8], a08v); + vst1q_f32(&a[j + 12], a12v); + } +} + +static void cftmdl_128_neon(float* a) { + int j; + const int l = 8; + const float32x4_t vec_swap_sign = vld1q_f32((float32_t*)k_swap_sign); + float32x4_t wk1rv = vld1q_f32(cftmdl_wk1r); + + for (j = 0; j < l; j += 2) { + const float32x2_t a_00 = vld1_f32(&a[j + 0]); + const float32x2_t a_08 = vld1_f32(&a[j + 8]); + const float32x2_t a_32 = vld1_f32(&a[j + 32]); + const float32x2_t a_40 = vld1_f32(&a[j + 40]); + const float32x4_t a_00_32 = vcombine_f32(a_00, a_32); + const float32x4_t a_08_40 = vcombine_f32(a_08, a_40); + const float32x4_t x0r0_0i0_0r1_x0i1 = vaddq_f32(a_00_32, a_08_40); + const float32x4_t x1r0_1i0_1r1_x1i1 = vsubq_f32(a_00_32, a_08_40); + const float32x2_t a_16 = vld1_f32(&a[j + 16]); + const float32x2_t a_24 = vld1_f32(&a[j + 24]); + const float32x2_t a_48 = vld1_f32(&a[j + 48]); + const float32x2_t a_56 = vld1_f32(&a[j + 56]); + const float32x4_t a_16_48 = vcombine_f32(a_16, a_48); + const float32x4_t a_24_56 = vcombine_f32(a_24, a_56); + const float32x4_t x2r0_2i0_2r1_x2i1 = vaddq_f32(a_16_48, a_24_56); + const float32x4_t x3r0_3i0_3r1_x3i1 = vsubq_f32(a_16_48, a_24_56); + const float32x4_t xx0 = vaddq_f32(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); + const float32x4_t xx1 = vsubq_f32(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); + const float32x4_t x3i0_3r0_3i1_x3r1 = vrev64q_f32(x3r0_3i0_3r1_x3i1); + const float32x4_t x1_x3_add = + vmlaq_f32(x1r0_1i0_1r1_x1i1, vec_swap_sign, x3i0_3r0_3i1_x3r1); + const float32x4_t x1_x3_sub = + vmlsq_f32(x1r0_1i0_1r1_x1i1, vec_swap_sign, x3i0_3r0_3i1_x3r1); + const float32x2_t yy0_a = vdup_lane_f32(vget_high_f32(x1_x3_add), 0); + const float32x2_t yy0_s = vdup_lane_f32(vget_high_f32(x1_x3_sub), 0); + const float32x4_t yy0_as = vcombine_f32(yy0_a, yy0_s); + const float32x2_t yy1_a = vdup_lane_f32(vget_high_f32(x1_x3_add), 1); + const float32x2_t yy1_s = vdup_lane_f32(vget_high_f32(x1_x3_sub), 1); + const float32x4_t yy1_as = vcombine_f32(yy1_a, yy1_s); + const float32x4_t yy0 = vmlaq_f32(yy0_as, vec_swap_sign, yy1_as); + const float32x4_t yy4 = vmulq_f32(wk1rv, yy0); + const float32x4_t xx1_rev = vrev64q_f32(xx1); + const float32x4_t yy4_rev = vrev64q_f32(yy4); + + vst1_f32(&a[j + 0], vget_low_f32(xx0)); + vst1_f32(&a[j + 32], vget_high_f32(xx0)); + vst1_f32(&a[j + 16], vget_low_f32(xx1)); + vst1_f32(&a[j + 48], vget_high_f32(xx1_rev)); + + a[j + 48] = -a[j + 48]; + + vst1_f32(&a[j + 8], vget_low_f32(x1_x3_add)); + vst1_f32(&a[j + 24], vget_low_f32(x1_x3_sub)); + vst1_f32(&a[j + 40], vget_low_f32(yy4)); + vst1_f32(&a[j + 56], vget_high_f32(yy4_rev)); + } + + { + const int k = 64; + const int k1 = 2; + const int k2 = 2 * k1; + const float32x4_t wk2rv = vld1q_f32(&rdft_wk2r[k2 + 0]); + const float32x4_t wk2iv = vld1q_f32(&rdft_wk2i[k2 + 0]); + const float32x4_t wk1iv = vld1q_f32(&rdft_wk1i[k2 + 0]); + const float32x4_t wk3rv = vld1q_f32(&rdft_wk3r[k2 + 0]); + const float32x4_t wk3iv = vld1q_f32(&rdft_wk3i[k2 + 0]); + wk1rv = vld1q_f32(&rdft_wk1r[k2 + 0]); + for (j = k; j < l + k; j += 2) { + const float32x2_t a_00 = vld1_f32(&a[j + 0]); + const float32x2_t a_08 = vld1_f32(&a[j + 8]); + const float32x2_t a_32 = vld1_f32(&a[j + 32]); + const float32x2_t a_40 = vld1_f32(&a[j + 40]); + const float32x4_t a_00_32 = vcombine_f32(a_00, a_32); + const float32x4_t a_08_40 = vcombine_f32(a_08, a_40); + const float32x4_t x0r0_0i0_0r1_x0i1 = vaddq_f32(a_00_32, a_08_40); + const float32x4_t x1r0_1i0_1r1_x1i1 = vsubq_f32(a_00_32, a_08_40); + const float32x2_t a_16 = vld1_f32(&a[j + 16]); + const float32x2_t a_24 = vld1_f32(&a[j + 24]); + const float32x2_t a_48 = vld1_f32(&a[j + 48]); + const float32x2_t a_56 = vld1_f32(&a[j + 56]); + const float32x4_t a_16_48 = vcombine_f32(a_16, a_48); + const float32x4_t a_24_56 = vcombine_f32(a_24, a_56); + const float32x4_t x2r0_2i0_2r1_x2i1 = vaddq_f32(a_16_48, a_24_56); + const float32x4_t x3r0_3i0_3r1_x3i1 = vsubq_f32(a_16_48, a_24_56); + const float32x4_t xx = vaddq_f32(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); + const float32x4_t xx1 = vsubq_f32(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); + const float32x4_t x3i0_3r0_3i1_x3r1 = vrev64q_f32(x3r0_3i0_3r1_x3i1); + const float32x4_t x1_x3_add = + vmlaq_f32(x1r0_1i0_1r1_x1i1, vec_swap_sign, x3i0_3r0_3i1_x3r1); + const float32x4_t x1_x3_sub = + vmlsq_f32(x1r0_1i0_1r1_x1i1, vec_swap_sign, x3i0_3r0_3i1_x3r1); + float32x4_t xx4 = vmulq_f32(wk2rv, xx1); + float32x4_t xx12 = vmulq_f32(wk1rv, x1_x3_add); + float32x4_t xx22 = vmulq_f32(wk3rv, x1_x3_sub); + xx4 = vmlaq_f32(xx4, wk2iv, vrev64q_f32(xx1)); + xx12 = vmlaq_f32(xx12, wk1iv, vrev64q_f32(x1_x3_add)); + xx22 = vmlaq_f32(xx22, wk3iv, vrev64q_f32(x1_x3_sub)); + + vst1_f32(&a[j + 0], vget_low_f32(xx)); + vst1_f32(&a[j + 32], vget_high_f32(xx)); + vst1_f32(&a[j + 16], vget_low_f32(xx4)); + vst1_f32(&a[j + 48], vget_high_f32(xx4)); + vst1_f32(&a[j + 8], vget_low_f32(xx12)); + vst1_f32(&a[j + 40], vget_high_f32(xx12)); + vst1_f32(&a[j + 24], vget_low_f32(xx22)); + vst1_f32(&a[j + 56], vget_high_f32(xx22)); + } + } +} + +__inline static float32x4_t reverse_order_f32x4(float32x4_t in) { + // A B C D -> C D A B + const float32x4_t rev = vcombine_f32(vget_high_f32(in), vget_low_f32(in)); + // C D A B -> D C B A + return vrev64q_f32(rev); +} + +static void rftfsub_128_neon(float* a) { + const float* c = rdft_w + 32; + int j1, j2; + const float32x4_t mm_half = vdupq_n_f32(0.5f); + + // Vectorized code (four at once). + // Note: commented number are indexes for the first iteration of the loop. + for (j1 = 1, j2 = 2; j2 + 7 < 64; j1 += 4, j2 += 8) { + // Load 'wk'. + const float32x4_t c_j1 = vld1q_f32(&c[j1]); // 1, 2, 3, 4, + const float32x4_t c_k1 = vld1q_f32(&c[29 - j1]); // 28, 29, 30, 31, + const float32x4_t wkrt = vsubq_f32(mm_half, c_k1); // 28, 29, 30, 31, + const float32x4_t wkr_ = reverse_order_f32x4(wkrt); // 31, 30, 29, 28, + const float32x4_t wki_ = c_j1; // 1, 2, 3, 4, + // Load and shuffle 'a'. + // 2, 4, 6, 8, 3, 5, 7, 9 + float32x4x2_t a_j2_p = vld2q_f32(&a[0 + j2]); + // 120, 122, 124, 126, 121, 123, 125, 127, + const float32x4x2_t k2_0_4 = vld2q_f32(&a[122 - j2]); + // 126, 124, 122, 120 + const float32x4_t a_k2_p0 = reverse_order_f32x4(k2_0_4.val[0]); + // 127, 125, 123, 121 + const float32x4_t a_k2_p1 = reverse_order_f32x4(k2_0_4.val[1]); + // Calculate 'x'. + const float32x4_t xr_ = vsubq_f32(a_j2_p.val[0], a_k2_p0); + // 2-126, 4-124, 6-122, 8-120, + const float32x4_t xi_ = vaddq_f32(a_j2_p.val[1], a_k2_p1); + // 3-127, 5-125, 7-123, 9-121, + // Calculate product into 'y'. + // yr = wkr * xr - wki * xi; + // yi = wkr * xi + wki * xr; + const float32x4_t a_ = vmulq_f32(wkr_, xr_); + const float32x4_t b_ = vmulq_f32(wki_, xi_); + const float32x4_t c_ = vmulq_f32(wkr_, xi_); + const float32x4_t d_ = vmulq_f32(wki_, xr_); + const float32x4_t yr_ = vsubq_f32(a_, b_); // 2-126, 4-124, 6-122, 8-120, + const float32x4_t yi_ = vaddq_f32(c_, d_); // 3-127, 5-125, 7-123, 9-121, + // Update 'a'. + // a[j2 + 0] -= yr; + // a[j2 + 1] -= yi; + // a[k2 + 0] += yr; + // a[k2 + 1] -= yi; + // 126, 124, 122, 120, + const float32x4_t a_k2_p0n = vaddq_f32(a_k2_p0, yr_); + // 127, 125, 123, 121, + const float32x4_t a_k2_p1n = vsubq_f32(a_k2_p1, yi_); + // Shuffle in right order and store. + const float32x4_t a_k2_p0nr = vrev64q_f32(a_k2_p0n); + const float32x4_t a_k2_p1nr = vrev64q_f32(a_k2_p1n); + // 124, 125, 126, 127, 120, 121, 122, 123 + const float32x4x2_t a_k2_n = vzipq_f32(a_k2_p0nr, a_k2_p1nr); + // 2, 4, 6, 8, + a_j2_p.val[0] = vsubq_f32(a_j2_p.val[0], yr_); + // 3, 5, 7, 9, + a_j2_p.val[1] = vsubq_f32(a_j2_p.val[1], yi_); + // 2, 3, 4, 5, 6, 7, 8, 9, + vst2q_f32(&a[0 + j2], a_j2_p); + + vst1q_f32(&a[122 - j2], a_k2_n.val[1]); + vst1q_f32(&a[126 - j2], a_k2_n.val[0]); + } + + // Scalar code for the remaining items. + for (; j2 < 64; j1 += 1, j2 += 2) { + const int k2 = 128 - j2; + const int k1 = 32 - j1; + const float wkr = 0.5f - c[k1]; + const float wki = c[j1]; + const float xr = a[j2 + 0] - a[k2 + 0]; + const float xi = a[j2 + 1] + a[k2 + 1]; + const float yr = wkr * xr - wki * xi; + const float yi = wkr * xi + wki * xr; + a[j2 + 0] -= yr; + a[j2 + 1] -= yi; + a[k2 + 0] += yr; + a[k2 + 1] -= yi; + } +} + +static void rftbsub_128_neon(float* a) { + const float* c = rdft_w + 32; + int j1, j2; + const float32x4_t mm_half = vdupq_n_f32(0.5f); + + a[1] = -a[1]; + // Vectorized code (four at once). + // Note: commented number are indexes for the first iteration of the loop. + for (j1 = 1, j2 = 2; j2 + 7 < 64; j1 += 4, j2 += 8) { + // Load 'wk'. + const float32x4_t c_j1 = vld1q_f32(&c[j1]); // 1, 2, 3, 4, + const float32x4_t c_k1 = vld1q_f32(&c[29 - j1]); // 28, 29, 30, 31, + const float32x4_t wkrt = vsubq_f32(mm_half, c_k1); // 28, 29, 30, 31, + const float32x4_t wkr_ = reverse_order_f32x4(wkrt); // 31, 30, 29, 28, + const float32x4_t wki_ = c_j1; // 1, 2, 3, 4, + // Load and shuffle 'a'. + // 2, 4, 6, 8, 3, 5, 7, 9 + float32x4x2_t a_j2_p = vld2q_f32(&a[0 + j2]); + // 120, 122, 124, 126, 121, 123, 125, 127, + const float32x4x2_t k2_0_4 = vld2q_f32(&a[122 - j2]); + // 126, 124, 122, 120 + const float32x4_t a_k2_p0 = reverse_order_f32x4(k2_0_4.val[0]); + // 127, 125, 123, 121 + const float32x4_t a_k2_p1 = reverse_order_f32x4(k2_0_4.val[1]); + // Calculate 'x'. + const float32x4_t xr_ = vsubq_f32(a_j2_p.val[0], a_k2_p0); + // 2-126, 4-124, 6-122, 8-120, + const float32x4_t xi_ = vaddq_f32(a_j2_p.val[1], a_k2_p1); + // 3-127, 5-125, 7-123, 9-121, + // Calculate product into 'y'. + // yr = wkr * xr - wki * xi; + // yi = wkr * xi + wki * xr; + const float32x4_t a_ = vmulq_f32(wkr_, xr_); + const float32x4_t b_ = vmulq_f32(wki_, xi_); + const float32x4_t c_ = vmulq_f32(wkr_, xi_); + const float32x4_t d_ = vmulq_f32(wki_, xr_); + const float32x4_t yr_ = vaddq_f32(a_, b_); // 2-126, 4-124, 6-122, 8-120, + const float32x4_t yi_ = vsubq_f32(c_, d_); // 3-127, 5-125, 7-123, 9-121, + // Update 'a'. + // a[j2 + 0] -= yr; + // a[j2 + 1] -= yi; + // a[k2 + 0] += yr; + // a[k2 + 1] -= yi; + // 126, 124, 122, 120, + const float32x4_t a_k2_p0n = vaddq_f32(a_k2_p0, yr_); + // 127, 125, 123, 121, + const float32x4_t a_k2_p1n = vsubq_f32(yi_, a_k2_p1); + // Shuffle in right order and store. + // 2, 3, 4, 5, 6, 7, 8, 9, + const float32x4_t a_k2_p0nr = vrev64q_f32(a_k2_p0n); + const float32x4_t a_k2_p1nr = vrev64q_f32(a_k2_p1n); + // 124, 125, 126, 127, 120, 121, 122, 123 + const float32x4x2_t a_k2_n = vzipq_f32(a_k2_p0nr, a_k2_p1nr); + // 2, 4, 6, 8, + a_j2_p.val[0] = vsubq_f32(a_j2_p.val[0], yr_); + // 3, 5, 7, 9, + a_j2_p.val[1] = vsubq_f32(yi_, a_j2_p.val[1]); + // 2, 3, 4, 5, 6, 7, 8, 9, + vst2q_f32(&a[0 + j2], a_j2_p); + + vst1q_f32(&a[122 - j2], a_k2_n.val[1]); + vst1q_f32(&a[126 - j2], a_k2_n.val[0]); + } + + // Scalar code for the remaining items. + for (; j2 < 64; j1 += 1, j2 += 2) { + const int k2 = 128 - j2; + const int k1 = 32 - j1; + const float wkr = 0.5f - c[k1]; + const float wki = c[j1]; + const float xr = a[j2 + 0] - a[k2 + 0]; + const float xi = a[j2 + 1] + a[k2 + 1]; + const float yr = wkr * xr + wki * xi; + const float yi = wkr * xi - wki * xr; + a[j2 + 0] = a[j2 + 0] - yr; + a[j2 + 1] = yi - a[j2 + 1]; + a[k2 + 0] = yr + a[k2 + 0]; + a[k2 + 1] = yi - a[k2 + 1]; + } + a[65] = -a[65]; +} + +void aec_rdft_init_neon(void) { + cft1st_128 = cft1st_128_neon; + cftmdl_128 = cftmdl_128_neon; + rftfsub_128 = rftfsub_128_neon; + rftbsub_128 = rftbsub_128_neon; +} + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_resampler.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_resampler.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_resampler.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_resampler.c 2015-02-03 14:33:35.000000000 +0000 @@ -26,7 +26,7 @@ }; typedef struct { - short buffer[kResamplerBufferSize]; + float buffer[kResamplerBufferSize]; float position; int deviceSampleRateHz; @@ -71,15 +71,15 @@ } void WebRtcAec_ResampleLinear(void* resampInst, - const short* inspeech, + const float* inspeech, int size, float skew, - short* outspeech, + float* outspeech, int* size_out) { resampler_t* obj = (resampler_t*)resampInst; - short* y; - float be, tnew, interp; + float* y; + float be, tnew; int tn, mm; assert(!(size < 0 || size > 2 * FRAME_LEN)); @@ -91,7 +91,7 @@ // Add new frame data in lookahead memcpy(&obj->buffer[FRAME_LEN + kResamplingDelay], inspeech, - size * sizeof(short)); + size * sizeof(inspeech[0])); // Sample rate ratio be = 1 + skew; @@ -106,15 +106,7 @@ while (tn < size) { // Interpolation - interp = y[tn] + (tnew - tn) * (y[tn + 1] - y[tn]); - - if (interp > 32767) { - interp = 32767; - } else if (interp < -32768) { - interp = -32768; - } - - outspeech[mm] = (short)interp; + outspeech[mm] = y[tn] + (tnew - tn) * (y[tn + 1] - y[tn]); mm++; tnew = be * mm + obj->position; @@ -127,7 +119,7 @@ // Shift buffer memmove(obj->buffer, &obj->buffer[size], - (kResamplerBufferSize - size) * sizeof(short)); + (kResamplerBufferSize - size) * sizeof(obj->buffer[0])); } int WebRtcAec_GetSkew(void* resampInst, int rawSkew, float* skewEst) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_resampler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_resampler.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_resampler.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_resampler.h 2015-02-03 14:33:35.000000000 +0000 @@ -30,10 +30,10 @@ // Resamples input using linear interpolation. void WebRtcAec_ResampleLinear(void* resampInst, - const short* inspeech, + const float* inspeech, int size, float skew, - short* outspeech, + float* outspeech, int* size_out); #endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_RESAMPLER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../android-webrtc.mk - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_aec -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := \ - echo_cancellation.c \ - aec_resampler.c \ - aec_core.c \ - aec_rdft.c \ - -ifeq ($(TARGET_ARCH),x86) -LOCAL_SRC_FILES += \ - aec_core_sse2.c \ - aec_rdft_sse2.c -endif - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../utility \ - $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../../../common_audio/signal_processing/include \ - external/webrtc - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/echo_cancellation.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/echo_cancellation.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/echo_cancellation.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/echo_cancellation.c 2015-02-03 14:33:35.000000000 +0000 @@ -72,14 +72,14 @@ // GTP/Linux(ChromeOS): TBD, but for the moment we will trust the values. #if defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_MAC) #define WEBRTC_UNTRUSTED_DELAY +#endif -#if defined(WEBRTC_MAC) +#if defined(WEBRTC_UNTRUSTED_DELAY) && defined(WEBRTC_MAC) static const int kDelayDiffOffsetSamples = -160; #else // Not enabled for now. static const int kDelayDiffOffsetSamples = 0; #endif -#endif #if defined(WEBRTC_MAC) static const int kFixedDelayMs = 20; @@ -110,18 +110,18 @@ static void EstBufDelayNormal(aecpc_t* aecInst); static void EstBufDelayExtended(aecpc_t* aecInst); static int ProcessNormal(aecpc_t* self, - const int16_t* near, - const int16_t* near_high, - int16_t* out, - int16_t* out_high, + const float* near, + const float* near_high, + float* out, + float* out_high, int16_t num_samples, int16_t reported_delay_ms, int32_t skew); static void ProcessExtended(aecpc_t* self, - const int16_t* near, - const int16_t* near_high, - int16_t* out, - int16_t* out_high, + const float* near, + const float* near_high, + float* out, + float* out_high, int16_t num_samples, int16_t reported_delay_ms, int32_t skew); @@ -164,16 +164,8 @@ aecpc->lastError = 0; #ifdef WEBRTC_AEC_DEBUG_DUMP - aecpc->far_pre_buf_s16 = - WebRtc_CreateBuffer(PART_LEN2 + kResamplerBufferSize, sizeof(int16_t)); - if (!aecpc->far_pre_buf_s16) { - WebRtcAec_Free(aecpc); - aecpc = NULL; - return -1; - } aecpc->bufFile = aecpc->skewFile = aecpc->delayFile = NULL; OpenDebugFiles(aecpc, &webrtc_aec_instance_count); - #endif return 0; @@ -189,7 +181,6 @@ WebRtc_FreeBuffer(aecpc->far_pre_buf); #ifdef WEBRTC_AEC_DEBUG_DUMP - WebRtc_FreeBuffer(aecpc->far_pre_buf_s16); if (aecpc->bufFile) { // we don't let one be open and not the others fclose(aecpc->bufFile); @@ -256,7 +247,7 @@ aecpc->checkBuffSize = 1; aecpc->firstVal = 0; - aecpc->startup_phase = 1; + aecpc->startup_phase = WebRtcAec_reported_delay_enabled(aecpc->aec); aecpc->bufSizeStart = 0; aecpc->checkBufSizeCtr = 0; aecpc->msInSndCardBuf = 0; @@ -283,30 +274,17 @@ return -1; } -#ifdef WEBRTC_AEC_DEBUG_DUMP - if (WebRtc_InitBuffer(aecpc->far_pre_buf_s16) == -1) { - aecpc->lastError = AEC_UNSPECIFIED_ERROR; - return -1; - } - WebRtc_MoveReadPtr(aecpc->far_pre_buf_s16, -PART_LEN); // Start overlap. -#endif - return 0; } // only buffer L band for farend int32_t WebRtcAec_BufferFarend(void* aecInst, - const int16_t* farend, + const float* farend, int16_t nrOfSamples) { aecpc_t* aecpc = aecInst; - int32_t retVal = 0; int newNrOfSamples = (int)nrOfSamples; - short newFarend[MAX_RESAMP_LEN]; - const int16_t* farend_ptr = farend; - float tmp_farend[MAX_RESAMP_LEN]; - const float* farend_float = tmp_farend; - float skew; - int i = 0; + float new_farend[MAX_RESAMP_LEN]; + const float* farend_ptr = farend; if (farend == NULL) { aecpc->lastError = AEC_NULL_POINTER_ERROR; @@ -324,60 +302,50 @@ return -1; } - skew = aecpc->skew; - if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) { // Resample and get a new number of samples WebRtcAec_ResampleLinear(aecpc->resampler, farend, nrOfSamples, - skew, - newFarend, + aecpc->skew, + new_farend, &newNrOfSamples); - farend_ptr = (const int16_t*)newFarend; + farend_ptr = new_farend; } aecpc->farend_started = 1; WebRtcAec_SetSystemDelay(aecpc->aec, WebRtcAec_system_delay(aecpc->aec) + newNrOfSamples); -#ifdef WEBRTC_AEC_DEBUG_DUMP - WebRtc_WriteBuffer( - aecpc->far_pre_buf_s16, farend_ptr, (size_t)newNrOfSamples); -#endif - // Cast to float and write the time-domain data to |far_pre_buf|. - for (i = 0; i < newNrOfSamples; i++) { - tmp_farend[i] = (float)farend_ptr[i]; - } - WebRtc_WriteBuffer(aecpc->far_pre_buf, farend_float, (size_t)newNrOfSamples); + // Write the time-domain data to |far_pre_buf|. + WebRtc_WriteBuffer(aecpc->far_pre_buf, farend_ptr, (size_t)newNrOfSamples); // Transform to frequency domain if we have enough data. while (WebRtc_available_read(aecpc->far_pre_buf) >= PART_LEN2) { // We have enough data to pass to the FFT, hence read PART_LEN2 samples. - WebRtc_ReadBuffer( - aecpc->far_pre_buf, (void**)&farend_float, tmp_farend, PART_LEN2); - - WebRtcAec_BufferFarendPartition(aecpc->aec, farend_float); + { + float* ptmp; + float tmp[PART_LEN2]; + WebRtc_ReadBuffer(aecpc->far_pre_buf, (void**)&ptmp, tmp, PART_LEN2); + WebRtcAec_BufferFarendPartition(aecpc->aec, ptmp); +#ifdef WEBRTC_AEC_DEBUG_DUMP + WebRtc_WriteBuffer( + WebRtcAec_far_time_buf(aecpc->aec), &ptmp[PART_LEN], 1); +#endif + } // Rewind |far_pre_buf| PART_LEN samples for overlap before continuing. WebRtc_MoveReadPtr(aecpc->far_pre_buf, -PART_LEN); -#ifdef WEBRTC_AEC_DEBUG_DUMP - WebRtc_ReadBuffer( - aecpc->far_pre_buf_s16, (void**)&farend_ptr, newFarend, PART_LEN2); - WebRtc_WriteBuffer( - WebRtcAec_far_time_buf(aecpc->aec), &farend_ptr[PART_LEN], 1); - WebRtc_MoveReadPtr(aecpc->far_pre_buf_s16, -PART_LEN); -#endif } - return retVal; + return 0; } int32_t WebRtcAec_Process(void* aecInst, - const int16_t* nearend, - const int16_t* nearendH, - int16_t* out, - int16_t* outH, + const float* nearend, + const float* nearendH, + float* out, + float* outH, int16_t nrOfSamples, int16_t msInSndCardBuf, int32_t skew) { @@ -637,10 +605,10 @@ } static int ProcessNormal(aecpc_t* aecpc, - const int16_t* nearend, - const int16_t* nearendH, - int16_t* out, - int16_t* outH, + const float* nearend, + const float* nearendH, + float* out, + float* outH, int16_t nrOfSamples, int16_t msInSndCardBuf, int32_t skew) { @@ -697,10 +665,10 @@ if (aecpc->startup_phase) { // Only needed if they don't already point to the same place. if (nearend != out) { - memcpy(out, nearend, sizeof(short) * nrOfSamples); + memcpy(out, nearend, sizeof(*out) * nrOfSamples); } if (nearendH != outH) { - memcpy(outH, nearendH, sizeof(short) * nrOfSamples); + memcpy(outH, nearendH, sizeof(*outH) * nrOfSamples); } // The AEC is in the start up mode @@ -774,7 +742,9 @@ } } else { // AEC is enabled. - EstBufDelayNormal(aecpc); + if (WebRtcAec_reported_delay_enabled(aecpc->aec)) { + EstBufDelayNormal(aecpc); + } // Note that 1 frame is supported for NB and 2 frames for WB. for (i = 0; i < nFrames; i++) { @@ -795,21 +765,20 @@ } static void ProcessExtended(aecpc_t* self, - const int16_t* near, - const int16_t* near_high, - int16_t* out, - int16_t* out_high, + const float* near, + const float* near_high, + float* out, + float* out_high, int16_t num_samples, int16_t reported_delay_ms, int32_t skew) { int i; const int num_frames = num_samples / FRAME_LEN; -#if defined(WEBRTC_UNTRUSTED_DELAY) const int delay_diff_offset = kDelayDiffOffsetSamples; +#if defined(WEBRTC_UNTRUSTED_DELAY) reported_delay_ms = kFixedDelayMs; #else // This is the usual mode where we trust the reported system delay values. - const int delay_diff_offset = 0; // Due to the longer filter, we no longer add 10 ms to the reported delay // to reduce chance of non-causality. Instead we apply a minimum here to avoid // issues with the read pointer jumping around needlessly. @@ -829,10 +798,10 @@ if (!self->farend_started) { // Only needed if they don't already point to the same place. if (near != out) { - memcpy(out, near, sizeof(short) * num_samples); + memcpy(out, near, sizeof(*out) * num_samples); } if (near_high != out_high) { - memcpy(out_high, near_high, sizeof(short) * num_samples); + memcpy(out_high, near_high, sizeof(*out_high) * num_samples); } return; } @@ -850,7 +819,9 @@ self->startup_phase = 0; } - EstBufDelayExtended(self); + if (WebRtcAec_reported_delay_enabled(self->aec)) { + EstBufDelayExtended(self); + } { // |delay_diff_offset| gives us the option to manually rewind the delay on diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/echo_cancellation_internal.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/echo_cancellation_internal.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/echo_cancellation_internal.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/echo_cancellation_internal.h 2015-02-03 14:33:35.000000000 +0000 @@ -42,7 +42,6 @@ short lastDelayDiff; #ifdef WEBRTC_AEC_DEBUG_DUMP - RingBuffer* far_pre_buf_s16; // Time domain far-end pre-buffer in int16_t. FILE* bufFile; FILE* delayFile; FILE* skewFile; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/include/echo_cancellation.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/include/echo_cancellation.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/include/echo_cancellation.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/include/echo_cancellation.h 2015-02-03 14:33:35.000000000 +0000 @@ -68,7 +68,7 @@ * * Inputs Description * ------------------------------------------------------------------- - * void **aecInst Pointer to the AEC instance to be created + * void** aecInst Pointer to the AEC instance to be created * and initialized * * Outputs Description @@ -83,7 +83,7 @@ * * Inputs Description * ------------------------------------------------------------------- - * void *aecInst Pointer to the AEC instance + * void* aecInst Pointer to the AEC instance * * Outputs Description * ------------------------------------------------------------------- @@ -97,7 +97,7 @@ * * Inputs Description * ------------------------------------------------------------------- - * void *aecInst Pointer to the AEC instance + * void* aecInst Pointer to the AEC instance * int32_t sampFreq Sampling frequency of data * int32_t scSampFreq Soundcard sampling frequency * @@ -113,8 +113,8 @@ * * Inputs Description * ------------------------------------------------------------------- - * void *aecInst Pointer to the AEC instance - * int16_t *farend In buffer containing one frame of + * void* aecInst Pointer to the AEC instance + * const float* farend In buffer containing one frame of * farend signal for L band * int16_t nrOfSamples Number of samples in farend buffer * @@ -124,7 +124,7 @@ * -1: error */ int32_t WebRtcAec_BufferFarend(void* aecInst, - const int16_t* farend, + const float* farend, int16_t nrOfSamples); /* @@ -132,10 +132,10 @@ * * Inputs Description * ------------------------------------------------------------------- - * void *aecInst Pointer to the AEC instance - * int16_t *nearend In buffer containing one frame of + * void* aecInst Pointer to the AEC instance + * float* nearend In buffer containing one frame of * nearend+echo signal for L band - * int16_t *nearendH In buffer containing one frame of + * float* nearendH In buffer containing one frame of * nearend+echo signal for H band * int16_t nrOfSamples Number of samples in nearend buffer * int16_t msInSndCardBuf Delay estimate for sound card and @@ -146,18 +146,18 @@ * * Outputs Description * ------------------------------------------------------------------- - * int16_t *out Out buffer, one frame of processed nearend + * float* out Out buffer, one frame of processed nearend * for L band - * int16_t *outH Out buffer, one frame of processed nearend + * float* outH Out buffer, one frame of processed nearend * for H band * int32_t return 0: OK * -1: error */ int32_t WebRtcAec_Process(void* aecInst, - const int16_t* nearend, - const int16_t* nearendH, - int16_t* out, - int16_t* outH, + const float* nearend, + const float* nearendH, + float* out, + float* outH, int16_t nrOfSamples, int16_t msInSndCardBuf, int32_t skew); @@ -167,7 +167,7 @@ * * Inputs Description * ------------------------------------------------------------------- - * void *handle Pointer to the AEC instance + * void* handle Pointer to the AEC instance * AecConfig config Config instance that contains all * properties to be set * @@ -183,11 +183,11 @@ * * Inputs Description * ------------------------------------------------------------------- - * void *handle Pointer to the AEC instance + * void* handle Pointer to the AEC instance * * Outputs Description * ------------------------------------------------------------------- - * int *status 0: Almost certainly nearend single-talk + * int* status 0: Almost certainly nearend single-talk * 1: Might not be neared single-talk * int return 0: OK * -1: error @@ -199,11 +199,11 @@ * * Inputs Description * ------------------------------------------------------------------- - * void *handle Pointer to the AEC instance + * void* handle Pointer to the AEC instance * * Outputs Description * ------------------------------------------------------------------- - * AecMetrics *metrics Struct which will be filled out with the + * AecMetrics* metrics Struct which will be filled out with the * current echo metrics. * int return 0: OK * -1: error @@ -232,7 +232,7 @@ * * Inputs Description * ------------------------------------------------------------------- - * void *aecInst Pointer to the AEC instance + * void* aecInst Pointer to the AEC instance * * Outputs Description * ------------------------------------------------------------------- diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/system_delay_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/system_delay_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/system_delay_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aec/system_delay_unittest.cc 2015-02-03 14:33:35.000000000 +0000 @@ -9,12 +9,12 @@ */ #include "testing/gtest/include/gtest/gtest.h" - extern "C" { #include "webrtc/modules/audio_processing/aec/aec_core.h" } #include "webrtc/modules/audio_processing/aec/echo_cancellation_internal.h" #include "webrtc/modules/audio_processing/aec/include/echo_cancellation.h" +#include "webrtc/test/testsupport/gtest_disable.h" #include "webrtc/typedefs.h" namespace { @@ -46,16 +46,19 @@ aecpc_t* self_; int samples_per_frame_; // Dummy input/output speech data. - int16_t far_[160]; - int16_t near_[160]; - int16_t out_[160]; + static const int kSamplesPerChunk = 160; + float far_[kSamplesPerChunk]; + float near_[kSamplesPerChunk]; + float out_[kSamplesPerChunk]; }; SystemDelayTest::SystemDelayTest() : handle_(NULL), self_(NULL), samples_per_frame_(0) { // Dummy input data are set with more or less arbitrary non-zero values. - memset(far_, 1, sizeof(far_)); - memset(near_, 2, sizeof(near_)); + for (int i = 0; i < kSamplesPerChunk; i++) { + far_[i] = 257.0; + near_[i] = 514.0; + } memset(out_, 0, sizeof(out_)); } @@ -246,11 +249,15 @@ } } -TEST_F(SystemDelayTest, CorrectDelayAfterStableBufferBuildUp) { +TEST_F(SystemDelayTest, + DISABLED_ON_ANDROID(CorrectDelayAfterStableBufferBuildUp)) { // In this test we start by establishing the device buffer size during stable // conditions, but with an empty internal far-end buffer. Once that is done we // verify that the system delay is increased correctly until we have reach an // internal buffer size of 75% of what's been reported. + + // This test assumes the reported delays are used. + WebRtcAec_enable_reported_delay(WebRtcAec_aec_core(handle_), 1); for (size_t i = 0; i < kNumSampleRates; i++) { Init(kSampleRateHz[i]); @@ -327,11 +334,14 @@ } } -TEST_F(SystemDelayTest, CorrectDelayDuringDrift) { +TEST_F(SystemDelayTest, DISABLED_ON_ANDROID(CorrectDelayDuringDrift)) { // This drift test should verify that the system delay is never exceeding the // device buffer. The drift is simulated by decreasing the reported device // buffer size by 1 ms every 100 ms. If the device buffer size goes below 30 // ms we jump (add) 10 ms to give a repeated pattern. + + // This test assumes the reported delays are used. + WebRtcAec_enable_reported_delay(WebRtcAec_aec_core(handle_), 1); for (size_t i = 0; i < kNumSampleRates; i++) { Init(kSampleRateHz[i]); RunStableStartup(); @@ -358,13 +368,16 @@ } } -TEST_F(SystemDelayTest, ShouldRecoverAfterGlitch) { +TEST_F(SystemDelayTest, DISABLED_ON_ANDROID(ShouldRecoverAfterGlitch)) { // This glitch test should verify that the system delay recovers if there is // a glitch in data. The data glitch is constructed as 200 ms of buffering // after which the stable procedure continues. The glitch is never reported by // the device. // The system is said to be in a non-causal state if the difference between // the device buffer and system delay is less than a block (64 samples). + + // This test assumes the reported delays are used. + WebRtcAec_enable_reported_delay(WebRtcAec_aec_core(handle_), 1); for (size_t i = 0; i < kNumSampleRates; i++) { Init(kSampleRateHz[i]); RunStableStartup(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/aecm_core.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/aecm_core.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/aecm_core.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/aecm_core.c 2015-02-03 14:33:35.000000000 +0000 @@ -303,8 +303,7 @@ memcpy(aecm->channelAdapt16, echo_path, sizeof(int16_t) * PART_LEN1); for (i = 0; i < PART_LEN1; i++) { - aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32( - (int32_t)(aecm->channelAdapt16[i]), 16); + aecm->channelAdapt32[i] = (int32_t)aecm->channelAdapt16[i] << 16; } // Reset channel storing variables @@ -330,8 +329,7 @@ echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]); (*far_energy) += (uint32_t)(far_spectrum[i]); - (*echo_energy_adapt) += WEBRTC_SPL_UMUL_16_16(aecm->channelAdapt16[i], - far_spectrum[i]); + *echo_energy_adapt += aecm->channelAdapt16[i] * far_spectrum[i]; (*echo_energy_stored) += (uint32_t)echo_est[i]; } } @@ -371,16 +369,12 @@ // Restore the W32 channel for (i = 0; i < PART_LEN; i += 4) { - aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32( - (int32_t)aecm->channelStored[i], 16); - aecm->channelAdapt32[i + 1] = WEBRTC_SPL_LSHIFT_W32( - (int32_t)aecm->channelStored[i + 1], 16); - aecm->channelAdapt32[i + 2] = WEBRTC_SPL_LSHIFT_W32( - (int32_t)aecm->channelStored[i + 2], 16); - aecm->channelAdapt32[i + 3] = WEBRTC_SPL_LSHIFT_W32( - (int32_t)aecm->channelStored[i + 3], 16); + aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16; + aecm->channelAdapt32[i + 1] = (int32_t)aecm->channelStored[i + 1] << 16; + aecm->channelAdapt32[i + 2] = (int32_t)aecm->channelStored[i + 2] << 16; + aecm->channelAdapt32[i + 3] = (int32_t)aecm->channelStored[i + 3] << 16; } - aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32((int32_t)aecm->channelStored[i], 16); + aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16; } // Initialize function pointers for ARM Neon platform. @@ -697,15 +691,38 @@ retVal = filtOld; if (filtOld > inVal) { - retVal -= WEBRTC_SPL_RSHIFT_W16(filtOld - inVal, stepSizeNeg); + retVal -= (filtOld - inVal) >> stepSizeNeg; } else { - retVal += WEBRTC_SPL_RSHIFT_W16(inVal - filtOld, stepSizePos); + retVal += (inVal - filtOld) >> stepSizePos; } return retVal; } +// ExtractFractionPart(a, zeros) +// +// returns the fraction part of |a|, with |zeros| number of leading zeros, as an +// int16_t scaled to Q8. There is no sanity check of |a| in the sense that the +// number of zeros match. +static int16_t ExtractFractionPart(uint32_t a, int zeros) { + return (int16_t)(((a << zeros) & 0x7FFFFFFF) >> 23); +} + +// Calculates and returns the log of |energy| in Q8. The input |energy| is +// supposed to be in Q(|q_domain|). +static int16_t LogOfEnergyInQ8(uint32_t energy, int q_domain) { + static const int16_t kLogLowValue = PART_LEN_SHIFT << 7; + int16_t log_energy_q8 = kLogLowValue; + if (energy > 0) { + int zeros = WebRtcSpl_NormU32(energy); + int16_t frac = ExtractFractionPart(energy, zeros); + // log2 of |energy| in Q8. + log_energy_q8 += ((31 - zeros) << 8) + frac - (q_domain << 8); + } + return log_energy_q8; +} + // WebRtcAecm_CalcEnergies(...) // // This function calculates the log of energies for nearend, farend and estimated @@ -732,13 +749,11 @@ int i; - int16_t zeros, frac; int16_t tmp16; int16_t increase_max_shifts = 4; int16_t decrease_max_shifts = 11; int16_t increase_min_shifts = 11; int16_t decrease_min_shifts = 3; - int16_t kLogLowValue = WEBRTC_SPL_LSHIFT_W16(PART_LEN_SHIFT, 7); // Get log of near end energy and store in buffer @@ -747,19 +762,7 @@ sizeof(int16_t) * (MAX_BUF_LEN - 1)); // Logarithm of integrated magnitude spectrum (nearEner) - tmp16 = kLogLowValue; - if (nearEner) - { - zeros = WebRtcSpl_NormU32(nearEner); - frac = (int16_t)WEBRTC_SPL_RSHIFT_U32( - (WEBRTC_SPL_LSHIFT_U32(nearEner, zeros) & 0x7FFFFFFF), - 23); - // log2 in Q8 - tmp16 += WEBRTC_SPL_LSHIFT_W16((31 - zeros), 8) + frac; - tmp16 -= WEBRTC_SPL_LSHIFT_W16(aecm->dfaNoisyQDomain, 8); - } - aecm->nearLogEnergy[0] = tmp16; - // END: Get log of near end energy + aecm->nearLogEnergy[0] = LogOfEnergyInQ8(nearEner, aecm->dfaNoisyQDomain); WebRtcAecm_CalcLinearEnergies(aecm, far_spectrum, echoEst, &tmpFar, &tmpAdapt, &tmpStored); @@ -770,43 +773,15 @@ sizeof(int16_t) * (MAX_BUF_LEN - 1)); // Logarithm of delayed far end energy - tmp16 = kLogLowValue; - if (tmpFar) - { - zeros = WebRtcSpl_NormU32(tmpFar); - frac = (int16_t)WEBRTC_SPL_RSHIFT_U32((WEBRTC_SPL_LSHIFT_U32(tmpFar, zeros) - & 0x7FFFFFFF), 23); - // log2 in Q8 - tmp16 += WEBRTC_SPL_LSHIFT_W16((31 - zeros), 8) + frac; - tmp16 -= WEBRTC_SPL_LSHIFT_W16(far_q, 8); - } - aecm->farLogEnergy = tmp16; + aecm->farLogEnergy = LogOfEnergyInQ8(tmpFar, far_q); // Logarithm of estimated echo energy through adapted channel - tmp16 = kLogLowValue; - if (tmpAdapt) - { - zeros = WebRtcSpl_NormU32(tmpAdapt); - frac = (int16_t)WEBRTC_SPL_RSHIFT_U32((WEBRTC_SPL_LSHIFT_U32(tmpAdapt, zeros) - & 0x7FFFFFFF), 23); - //log2 in Q8 - tmp16 += WEBRTC_SPL_LSHIFT_W16((31 - zeros), 8) + frac; - tmp16 -= WEBRTC_SPL_LSHIFT_W16(RESOLUTION_CHANNEL16 + far_q, 8); - } - aecm->echoAdaptLogEnergy[0] = tmp16; + aecm->echoAdaptLogEnergy[0] = LogOfEnergyInQ8(tmpAdapt, + RESOLUTION_CHANNEL16 + far_q); // Logarithm of estimated echo energy through stored channel - tmp16 = kLogLowValue; - if (tmpStored) - { - zeros = WebRtcSpl_NormU32(tmpStored); - frac = (int16_t)WEBRTC_SPL_RSHIFT_U32((WEBRTC_SPL_LSHIFT_U32(tmpStored, zeros) - & 0x7FFFFFFF), 23); - //log2 in Q8 - tmp16 += WEBRTC_SPL_LSHIFT_W16((31 - zeros), 8) + frac; - tmp16 -= WEBRTC_SPL_LSHIFT_W16(RESOLUTION_CHANNEL16 + far_q, 8); - } - aecm->echoStoredLogEnergy[0] = tmp16; + aecm->echoStoredLogEnergy[0] = + LogOfEnergyInQ8(tmpStored, RESOLUTION_CHANNEL16 + far_q); // Update farend energy levels (min, max, vad, mse) if (aecm->farLogEnergy > FAR_ENERGY_MIN) @@ -843,10 +818,8 @@ { if (aecm->farEnergyVAD > aecm->farLogEnergy) { - aecm->farEnergyVAD += WEBRTC_SPL_RSHIFT_W16(aecm->farLogEnergy + - tmp16 - - aecm->farEnergyVAD, - 6); + aecm->farEnergyVAD += + (aecm->farLogEnergy + tmp16 - aecm->farEnergyVAD) >> 6; aecm->vadUpdateCount = 0; } else { @@ -985,9 +958,8 @@ { // We need to shift down before multiplication shiftChFar = 32 - zerosCh - zerosFar; - tmpU32no1 = WEBRTC_SPL_UMUL_32_16( - WEBRTC_SPL_RSHIFT_W32(aecm->channelAdapt32[i], shiftChFar), - far_spectrum[i]); + tmpU32no1 = (aecm->channelAdapt32[i] >> shiftChFar) * + far_spectrum[i]; } // Determine Q-domain of numerator zerosNum = WebRtcSpl_NormU32(tmpU32no1); @@ -1046,14 +1018,10 @@ shiftNum = 32 - (zerosNum + zerosFar); if (tmp32no1 > 0) { - tmp32no2 = (int32_t)WEBRTC_SPL_UMUL_32_16( - WEBRTC_SPL_RSHIFT_W32(tmp32no1, shiftNum), - far_spectrum[i]); + tmp32no2 = (tmp32no1 >> shiftNum) * far_spectrum[i]; } else { - tmp32no2 = -(int32_t)WEBRTC_SPL_UMUL_32_16( - WEBRTC_SPL_RSHIFT_W32(-tmp32no1, shiftNum), - far_spectrum[i]); + tmp32no2 = -((-tmp32no1 >> shiftNum) * far_spectrum[i]); } } // Normalize with respect to frequency bin @@ -1067,15 +1035,15 @@ { tmp32no2 = WEBRTC_SPL_SHIFT_W32(tmp32no2, shift2ResChan); } - aecm->channelAdapt32[i] = WEBRTC_SPL_ADD_SAT_W32(aecm->channelAdapt32[i], - tmp32no2); + aecm->channelAdapt32[i] = + WebRtcSpl_AddSatW32(aecm->channelAdapt32[i], tmp32no2); if (aecm->channelAdapt32[i] < 0) { // We can never have negative channel gain aecm->channelAdapt32[i] = 0; } - aecm->channelAdapt16[i] - = (int16_t)WEBRTC_SPL_RSHIFT_W32(aecm->channelAdapt32[i], 16); + aecm->channelAdapt16[i] = + (int16_t)(aecm->channelAdapt32[i] >> 16); } } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/aecm_core_c.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/aecm_core_c.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/aecm_core_c.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/aecm_core_c.c 2015-02-03 14:33:35.000000000 +0000 @@ -260,7 +260,7 @@ __asm __volatile( "smulbb %[tmp32no1], %[real], %[real]\n\t" "smlabb %[tmp32no2], %[imag], %[imag], %[tmp32no1]\n\t" - :[tmp32no1]"+r"(tmp32no1), + :[tmp32no1]"+&r"(tmp32no1), [tmp32no2]"=r"(tmp32no2) :[real]"r"(freq_signal[i].real), [imag]"r"(freq_signal[i].imag) @@ -270,7 +270,7 @@ tmp16no2 = WEBRTC_SPL_ABS_W16(freq_signal[i].imag); tmp32no1 = WEBRTC_SPL_MUL_16_16(tmp16no1, tmp16no1); tmp32no2 = WEBRTC_SPL_MUL_16_16(tmp16no2, tmp16no2); - tmp32no2 = WEBRTC_SPL_ADD_SAT_W32(tmp32no1, tmp32no2); + tmp32no2 = WebRtcSpl_AddSatW32(tmp32no1, tmp32no2); #endif // WEBRTC_ARCH_ARM_V7 tmp32no1 = WebRtcSpl_SqrtFloor(tmp32no2); @@ -328,7 +328,7 @@ int16_t zeros32, zeros16; int16_t zerosDBufNoisy, zerosDBufClean, zerosXBuf; int far_q; - int16_t resolutionDiff, qDomainDiff; + int16_t resolutionDiff, qDomainDiff, dfa_clean_q_domain_diff; const int kMinPrefBand = 4; const int kMaxPrefBand = 24; @@ -460,8 +460,7 @@ // Far end signal through channel estimate in Q8 // How much can we shift right to preserve resolution tmp32no1 = echoEst32[i] - aecm->echoFilt[i]; - aecm->echoFilt[i] += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_32_16(tmp32no1, - 50), 8); + aecm->echoFilt[i] += (tmp32no1 * 50) >> 8; zeros32 = WebRtcSpl_NormW32(aecm->echoFilt[i]) + 1; zeros16 = WebRtcSpl_NormW16(supGain) + 1; @@ -484,44 +483,37 @@ if (zeros32 > tmp16no1) { echoEst32Gained = WEBRTC_SPL_UMUL_32_16((uint32_t)aecm->echoFilt[i], - (uint16_t)WEBRTC_SPL_RSHIFT_W16( - supGain, - tmp16no1) - ); + supGain >> tmp16no1); } else { // Result in Q-(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN-16) - echoEst32Gained = WEBRTC_SPL_UMUL_32_16((uint32_t)WEBRTC_SPL_RSHIFT_W32( - aecm->echoFilt[i], - tmp16no1), - (uint16_t)supGain); + echoEst32Gained = (aecm->echoFilt[i] >> tmp16no1) * supGain; } } zeros16 = WebRtcSpl_NormW16(aecm->nearFilt[i]); - if ((zeros16 < (aecm->dfaCleanQDomain - aecm->dfaCleanQDomainOld)) - & (aecm->nearFilt[i])) - { - tmp16no1 = WEBRTC_SPL_SHIFT_W16(aecm->nearFilt[i], zeros16); - qDomainDiff = zeros16 - aecm->dfaCleanQDomain + aecm->dfaCleanQDomainOld; - } else - { - tmp16no1 = WEBRTC_SPL_SHIFT_W16(aecm->nearFilt[i], - aecm->dfaCleanQDomain - - aecm->dfaCleanQDomainOld); + assert(zeros16 >= 0); // |zeros16| is a norm, hence non-negative. + dfa_clean_q_domain_diff = aecm->dfaCleanQDomain - aecm->dfaCleanQDomainOld; + if (zeros16 < dfa_clean_q_domain_diff && aecm->nearFilt[i]) { + tmp16no1 = aecm->nearFilt[i] << zeros16; + qDomainDiff = zeros16 - dfa_clean_q_domain_diff; + tmp16no2 = ptrDfaClean[i] >> -qDomainDiff; + } else { + tmp16no1 = dfa_clean_q_domain_diff < 0 + ? aecm->nearFilt[i] >> -dfa_clean_q_domain_diff + : aecm->nearFilt[i] << dfa_clean_q_domain_diff; qDomainDiff = 0; + tmp16no2 = ptrDfaClean[i]; } - tmp16no2 = WEBRTC_SPL_SHIFT_W16(ptrDfaClean[i], qDomainDiff); tmp32no1 = (int32_t)(tmp16no2 - tmp16no1); - tmp16no2 = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32no1, 4); + tmp16no2 = (int16_t)(tmp32no1 >> 4); tmp16no2 += tmp16no1; zeros16 = WebRtcSpl_NormW16(tmp16no2); - if ((tmp16no2) & (-qDomainDiff > zeros16)) - { + if ((tmp16no2) & (-qDomainDiff > zeros16)) { aecm->nearFilt[i] = WEBRTC_SPL_WORD16_MAX; - } else - { - aecm->nearFilt[i] = WEBRTC_SPL_SHIFT_W16(tmp16no2, -qDomainDiff); + } else { + aecm->nearFilt[i] = qDomainDiff < 0 ? tmp16no2 << -qDomainDiff + : tmp16no2 >> qDomainDiff; } // Wiener filter coefficients, resulting hnl in Q14 @@ -689,7 +681,7 @@ { // Shift to the noise domain. tmp32 = (int32_t)dfa[i]; - outLShift32 = WEBRTC_SPL_LSHIFT_W32(tmp32, shiftFromNearToNoise); + outLShift32 = tmp32 << shiftFromNearToNoise; if (outLShift32 < aecm->noiseEst[i]) { @@ -748,11 +740,11 @@ for (i = 0; i < PART_LEN1; i++) { - tmp32 = WEBRTC_SPL_RSHIFT_W32(aecm->noiseEst[i], shiftFromNearToNoise); + tmp32 = aecm->noiseEst[i] >> shiftFromNearToNoise; if (tmp32 > 32767) { tmp32 = 32767; - aecm->noiseEst[i] = WEBRTC_SPL_LSHIFT_W32(tmp32, shiftFromNearToNoise); + aecm->noiseEst[i] = tmp32 << shiftFromNearToNoise; } noiseRShift16[i] = (int16_t)tmp32; @@ -785,8 +777,8 @@ for (i = 0; i < PART_LEN1; i++) { - out[i].real = WEBRTC_SPL_ADD_SAT_W16(out[i].real, uReal[i]); - out[i].imag = WEBRTC_SPL_ADD_SAT_W16(out[i].imag, uImag[i]); + out[i].real = WebRtcSpl_AddSatW16(out[i].real, uReal[i]); + out[i].imag = WebRtcSpl_AddSatW16(out[i].imag, uImag[i]); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/aecm_core_mips.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/aecm_core_mips.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/aecm_core_mips.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/aecm_core_mips.c 2015-02-03 14:33:35.000000000 +0000 @@ -512,8 +512,7 @@ echo_est[PART_LEN] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[PART_LEN], far_spectrum[PART_LEN]); par1 += (uint32_t)(far_spectrum[PART_LEN]); - par2 += WEBRTC_SPL_UMUL_16_16(aecm->channelAdapt16[PART_LEN], - far_spectrum[PART_LEN]); + par2 += aecm->channelAdapt16[PART_LEN] * far_spectrum[PART_LEN]; par3 += (uint32_t)echo_est[PART_LEN]; (*far_energy) = par1; @@ -607,8 +606,7 @@ ); } - aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32( - (int32_t)aecm->channelStored[i], 16); + aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16; } #endif // #if defined(MIPS_DSP_R1_LE) @@ -693,7 +691,7 @@ tmp16no2 = WEBRTC_SPL_ABS_W16(freq_signal[i].imag); tmp32no1 = WEBRTC_SPL_MUL_16_16(tmp16no1, tmp16no1); tmp32no2 = WEBRTC_SPL_MUL_16_16(tmp16no2, tmp16no2); - tmp32no2 = WEBRTC_SPL_ADD_SAT_W32(tmp32no1, tmp32no2); + tmp32no2 = WebRtcSpl_AddSatW32(tmp32no1, tmp32no2); tmp32no1 = WebRtcSpl_SqrtFloor(tmp32no2); freq_signal_abs[i] = (uint16_t)tmp32no1; @@ -836,7 +834,7 @@ int16_t zeros32, zeros16; int16_t zerosDBufNoisy, zerosDBufClean, zerosXBuf; int far_q; - int16_t resolutionDiff, qDomainDiff; + int16_t resolutionDiff, qDomainDiff, dfa_clean_q_domain_diff; const int kMinPrefBand = 4; const int kMaxPrefBand = 24; @@ -970,8 +968,7 @@ // Far end signal through channel estimate in Q8 // How much can we shift right to preserve resolution tmp32no1 = echoEst32[i] - aecm->echoFilt[i]; - aecm->echoFilt[i] += WEBRTC_SPL_RSHIFT_W32( - WEBRTC_SPL_MUL_32_16(tmp32no1, 50), 8); + aecm->echoFilt[i] += (tmp32no1 * 50) >> 8; zeros32 = WebRtcSpl_NormW32(aecm->echoFilt[i]) + 1; zeros16 = WebRtcSpl_NormW16(supGain) + 1; @@ -991,38 +988,37 @@ if (zeros32 > tmp16no1) { echoEst32Gained = WEBRTC_SPL_UMUL_32_16( (uint32_t)aecm->echoFilt[i], - (uint16_t)WEBRTC_SPL_RSHIFT_W16(supGain, tmp16no1)); + supGain >> tmp16no1); } else { // Result in Q-(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN-16) - echoEst32Gained = WEBRTC_SPL_UMUL_32_16( - (uint32_t)WEBRTC_SPL_RSHIFT_W32(aecm->echoFilt[i], - tmp16no1), - (uint16_t)supGain); + echoEst32Gained = (aecm->echoFilt[i] >> tmp16no1) * supGain; } } zeros16 = WebRtcSpl_NormW16(aecm->nearFilt[i]); - if ((zeros16 < (aecm->dfaCleanQDomain - aecm->dfaCleanQDomainOld)) - & (aecm->nearFilt[i])) { - tmp16no1 = WEBRTC_SPL_SHIFT_W16(aecm->nearFilt[i], zeros16); - qDomainDiff = zeros16 - aecm->dfaCleanQDomain + aecm->dfaCleanQDomainOld; - tmp16no2 = WEBRTC_SPL_SHIFT_W16(ptrDfaClean[i], qDomainDiff); + assert(zeros16 >= 0); // |zeros16| is a norm, hence non-negative. + dfa_clean_q_domain_diff = aecm->dfaCleanQDomain - aecm->dfaCleanQDomainOld; + if (zeros16 < dfa_clean_q_domain_diff && aecm->nearFilt[i]) { + tmp16no1 = aecm->nearFilt[i] << zeros16; + qDomainDiff = zeros16 - dfa_clean_q_domain_diff; + tmp16no2 = ptrDfaClean[i] >> -qDomainDiff; } else { - tmp16no1 = WEBRTC_SPL_SHIFT_W16(aecm->nearFilt[i], - aecm->dfaCleanQDomain - - aecm->dfaCleanQDomainOld); + tmp16no1 = dfa_clean_q_domain_diff < 0 + ? aecm->nearFilt[i] >> -dfa_clean_q_domain_diff + : aecm->nearFilt[i] << dfa_clean_q_domain_diff; qDomainDiff = 0; tmp16no2 = ptrDfaClean[i]; } tmp32no1 = (int32_t)(tmp16no2 - tmp16no1); - tmp16no2 = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32no1, 4); + tmp16no2 = (int16_t)(tmp32no1 >> 4); tmp16no2 += tmp16no1; zeros16 = WebRtcSpl_NormW16(tmp16no2); if ((tmp16no2) & (-qDomainDiff > zeros16)) { aecm->nearFilt[i] = WEBRTC_SPL_WORD16_MAX; } else { - aecm->nearFilt[i] = WEBRTC_SPL_SHIFT_W16(tmp16no2, -qDomainDiff); + aecm->nearFilt[i] = qDomainDiff < 0 ? tmp16no2 << -qDomainDiff + : tmp16no2 >> qDomainDiff; } // Wiener filter coefficients, resulting hnl in Q14 @@ -1471,11 +1467,11 @@ if (tmp32 > 32767) { tmp32 = 32767; - aecm->noiseEst[i] = WEBRTC_SPL_LSHIFT_W32(tmp32, shiftFromNearToNoise); + aecm->noiseEst[i] = tmp32 << shiftFromNearToNoise; } if (tmp321 > 32767) { tmp321 = 32767; - aecm->noiseEst[i+1] = WEBRTC_SPL_LSHIFT_W32(tmp321, shiftFromNearToNoise); + aecm->noiseEst[i+1] = tmp321 << shiftFromNearToNoise; } __asm __volatile ( @@ -1483,7 +1479,7 @@ "mul %[tmp321], %[tmp321], %[tmp161] \n\t" "sra %[nrsh1], %[tmp32], 14 \n\t" "sra %[nrsh2], %[tmp321], 14 \n\t" - : [nrsh1] "=r" (nrsh1), [nrsh2] "=r" (nrsh2) + : [nrsh1] "=&r" (nrsh1), [nrsh2] "=r" (nrsh2) : [tmp16] "r" (tmp16), [tmp161] "r" (tmp161), [tmp32] "r" (tmp32), [tmp321] "r" (tmp321) : "memory", "hi", "lo" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/aecm_core_neon.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/aecm_core_neon.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/aecm_core_neon.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/aecm_core_neon.c 2015-02-03 14:33:35.000000000 +0000 @@ -267,8 +267,7 @@ __asm __volatile("vadd.u32 q8, q10" : : : "q10", "q8"); __asm __volatile("vadd.u32 q8, q11" : : : "q11", "q8"); - // echo_energy_adapt += WEBRTC_SPL_UMUL_16_16( - // aecm->channelAdapt16[i], far_spectrum[i]); + // echo_energy_adapt += aecm->channelAdapt16[i] * far_spectrum[i]; __asm __volatile("vld1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelAdapt16[i]) : "q12"); __asm __volatile("vmull.u16 q10, d26, d24" : : : "q12", "q13", "q10"); __asm __volatile("vmull.u16 q11, d27, d25" : : : "q12", "q13", "q11"); @@ -292,8 +291,8 @@ echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]); *echo_energy_stored = echo_energy_stored_r + (uint32_t)echo_est[i]; *far_energy = far_energy_r + (uint32_t)(far_spectrum[i]); - *echo_energy_adapt = echo_energy_adapt_r + WEBRTC_SPL_UMUL_16_16( - aecm->channelAdapt16[i], far_spectrum[i]); + *echo_energy_adapt = echo_energy_adapt_r + + aecm->channelAdapt16[i] * far_spectrum[i]; } void WebRtcAecm_StoreAdaptiveChannelNeon(AecmCore_t* aecm, @@ -331,8 +330,7 @@ for (i = 0; i < PART_LEN - 7; i += 8) { // aecm->channelAdapt16[i] = aecm->channelStored[i]; - // aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32((int32_t) - // aecm->channelStored[i], 16); + // aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16; __asm __volatile("vld1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelStored[i]) : "q12"); __asm __volatile("vst1.16 {d24, d25}, [%0, :128]" : : @@ -343,6 +341,5 @@ "r"(&aecm->channelAdapt32[i]): "q10", "q11"); } aecm->channelAdapt16[i] = aecm->channelStored[i]; - aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32( - (int32_t)aecm->channelStored[i], 16); + aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,95 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -############################# -# Build the non-neon library. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_aecm -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := \ - echo_control_mobile.c \ - aecm_core.c - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../utility \ - $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../../../common_audio/signal_processing/include \ - $(LOCAL_PATH)/../../../system_wrappers/interface \ - external/webrtc - -LOCAL_STATIC_LIBRARIES += libwebrtc_system_wrappers - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) - -######################### -# Build the neon library. -ifeq ($(WEBRTC_BUILD_NEON_LIBS),true) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_aecm_neon -LOCAL_MODULE_TAGS := optional - -AECM_ASM_HEADER := $(intermediates)/aecm_core_neon_offsets.h -AECM_ASM_HEADER_DIR := $(intermediates) - -# Generate a header file aecm_core_neon_offsets.h which will be included in -# assembly file aecm_core_neon.S, from file aecm_core_neon_offsets.c. -$(AECM_ASM_HEADER): $(LOCAL_PATH)/../../../build/generate_asm_header.py \ - $(LOCAL_PATH)/aecm_core_neon_offsets.c - @python $^ --compiler=$(TARGET_CC) --options="$(addprefix -I, \ - $(LOCAL_INCLUDES)) $(addprefix -isystem , $(TARGET_C_INCLUDES)) -S" \ - --dir=$(AECM_ASM_HEADER_DIR) - -LOCAL_GENERATED_SOURCES := $(AECM_ASM_HEADER) -LOCAL_SRC_FILES := aecm_core_neon.S - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) \ - -mfpu=neon \ - -mfloat-abi=softfp \ - -flax-vector-conversions - -LOCAL_C_INCLUDES := \ - $(AECM_ASM_HEADER_DIR) \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../../../common_audio/signal_processing/include \ - external/webrtc - -LOCAL_INCLUDES := $(LOCAL_C_INCLUDES) - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) - -endif # ifeq ($(WEBRTC_BUILD_NEON_LIBS),true) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/echo_control_mobile.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/echo_control_mobile.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/echo_control_mobile.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/echo_control_mobile.c 2015-02-03 14:33:35.000000000 +0000 @@ -443,27 +443,14 @@ // Call the AECM /*WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearend[FRAME_LEN * i], &out[FRAME_LEN * i], aecm->knownDelay);*/ - if (nearendClean == NULL) - { - if (WebRtcAecm_ProcessFrame(aecm->aecmCore, - farend_ptr, - &nearendNoisy[FRAME_LEN * i], - NULL, - &out[FRAME_LEN * i]) == -1) - { - return -1; - } - } else - { - if (WebRtcAecm_ProcessFrame(aecm->aecmCore, - farend_ptr, - &nearendNoisy[FRAME_LEN * i], - &nearendClean[FRAME_LEN * i], - &out[FRAME_LEN * i]) == -1) - { - return -1; - } - } + if (WebRtcAecm_ProcessFrame(aecm->aecmCore, + farend_ptr, + &nearendNoisy[FRAME_LEN * i], + (nearendClean + ? &nearendClean[FRAME_LEN * i] + : NULL), + &out[FRAME_LEN * i]) == -1) + return -1; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h 2015-02-03 14:33:35.000000000 +0000 @@ -45,7 +45,7 @@ * * Inputs Description * ------------------------------------------------------------------- - * void **aecmInst Pointer to the AECM instance to be + * void** aecmInst Pointer to the AECM instance to be * created and initialized * * Outputs Description @@ -60,11 +60,11 @@ * * Inputs Description * ------------------------------------------------------------------- - * void *aecmInst Pointer to the AECM instance + * void* aecmInst Pointer to the AECM instance * * Outputs Description * ------------------------------------------------------------------- - * int32_t return 0: OK + * int32_t return 0: OK * -1: error */ int32_t WebRtcAecm_Free(void *aecmInst); @@ -74,7 +74,7 @@ * * Inputs Description * ------------------------------------------------------------------- - * void *aecmInst Pointer to the AECM instance + * void* aecmInst Pointer to the AECM instance * int32_t sampFreq Sampling frequency of data * * Outputs Description @@ -89,8 +89,8 @@ * * Inputs Description * ------------------------------------------------------------------- - * void *aecmInst Pointer to the AECM instance - * int16_t *farend In buffer containing one frame of + * void* aecmInst Pointer to the AECM instance + * int16_t* farend In buffer containing one frame of * farend signal * int16_t nrOfSamples Number of samples in farend buffer * @@ -106,14 +106,14 @@ /* * Runs the AECM on an 80 or 160 sample blocks of data. * - * Inputs Description + * Inputs Description * ------------------------------------------------------------------- - * void *aecmInst Pointer to the AECM instance - * int16_t *nearendNoisy In buffer containing one frame of + * void* aecmInst Pointer to the AECM instance + * int16_t* nearendNoisy In buffer containing one frame of * reference nearend+echo signal. If * noise reduction is active, provide * the noisy signal here. - * int16_t *nearendClean In buffer containing one frame of + * int16_t* nearendClean In buffer containing one frame of * nearend+echo signal. If noise * reduction is active, provide the * clean signal here. Otherwise pass a @@ -122,11 +122,11 @@ * int16_t msInSndCardBuf Delay estimate for sound card and * system buffers * - * Outputs Description + * Outputs Description * ------------------------------------------------------------------- - * int16_t *out Out buffer, one frame of processed nearend - * int32_t return 0: OK - * -1: error + * int16_t* out Out buffer, one frame of processed nearend + * int32_t return 0: OK + * -1: error */ int32_t WebRtcAecm_Process(void* aecmInst, const int16_t* nearendNoisy, @@ -140,8 +140,8 @@ * * Inputs Description * ------------------------------------------------------------------- - * void *aecmInst Pointer to the AECM instance - * AecmConfig config Config instance that contains all + * void* aecmInst Pointer to the AECM instance + * AecmConfig config Config instance that contains all * properties to be set * * Outputs Description @@ -156,11 +156,11 @@ * * Inputs Description * ------------------------------------------------------------------- - * void *aecmInst Pointer to the AECM instance + * void* aecmInst Pointer to the AECM instance * * Outputs Description * ------------------------------------------------------------------- - * AecmConfig *config Pointer to the config instance that + * AecmConfig* config Pointer to the config instance that * all properties will be written to * int32_t return 0: OK * -1: error @@ -178,7 +178,7 @@ * * Outputs Description * ------------------------------------------------------------------- - * int32_t return 0: OK + * int32_t return 0: OK * -1: error */ int32_t WebRtcAecm_InitEchoPath(void* aecmInst, @@ -197,7 +197,7 @@ * * Outputs Description * ------------------------------------------------------------------- - * int32_t return 0: OK + * int32_t return 0: OK * -1: error */ int32_t WebRtcAecm_GetEchoPath(void* aecmInst, @@ -209,7 +209,7 @@ * * Outputs Description * ------------------------------------------------------------------- - * size_t return : size in bytes + * size_t return Size in bytes */ size_t WebRtcAecm_echo_path_size_bytes(); @@ -218,7 +218,7 @@ * * Inputs Description * ------------------------------------------------------------------- - * void *aecmInst Pointer to the AECM instance + * void* aecmInst Pointer to the AECM instance * * Outputs Description * ------------------------------------------------------------------- diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/analog_agc.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/analog_agc.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/analog_agc.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/analog_agc.c 2015-02-03 14:33:35.000000000 +0000 @@ -19,7 +19,7 @@ #include #include -#ifdef AGC_DEBUG //test log +#ifdef WEBRTC_AGC_DEBUG_DUMP #include #endif #include "webrtc/modules/audio_processing/agc/analog_agc.h" @@ -139,10 +139,10 @@ L = 8; } else { -#ifdef AGC_DEBUG //test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, "AGC->add_mic, frame %d: Invalid number of samples\n\n", - (stt->fcount + 1)); + stt->fcount + 1); #endif return -1; } @@ -160,10 +160,10 @@ L = 16; } else { -#ifdef AGC_DEBUG //test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, "AGC->add_mic, frame %d: Invalid number of samples\n\n", - (stt->fcount + 1)); + stt->fcount + 1); #endif return -1; } @@ -177,10 +177,10 @@ L = 16; } else { -#ifdef AGC_DEBUG +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, "AGC->add_mic, frame %d: Invalid sample rate\n\n", - (stt->fcount + 1)); + stt->fcount + 1); #endif return -1; } @@ -208,7 +208,7 @@ tmp16 = (int16_t)(stt->micVol - stt->maxAnalog); tmp32 = WEBRTC_SPL_MUL_16_16(GAIN_TBL_LEN - 1, tmp16); tmp16 = (int16_t)(stt->maxLevel - stt->maxAnalog); - targetGainIdx = (uint16_t)WEBRTC_SPL_DIV(tmp32, tmp16); + targetGainIdx = tmp32 / tmp16; assert(targetGainIdx < GAIN_TBL_LEN); /* Increment through the table towards the target gain. @@ -228,8 +228,7 @@ for (i = 0; i < samples; i++) { // For lower band - tmp32 = WEBRTC_SPL_MUL_16_U16(in_mic[i], gain); - sample = WEBRTC_SPL_RSHIFT_W32(tmp32, 12); + sample = (in_mic[i] * gain) >> 12; if (sample > 32767) { in_mic[i] = 32767; @@ -244,8 +243,7 @@ // For higher band if (stt->fs == 32000) { - tmp32 = WEBRTC_SPL_MUL_16_U16(in_mic_H[i], gain); - sample = WEBRTC_SPL_RSHIFT_W32(tmp32, 12); + sample = (in_mic_H[i] * gain) >> 12; if (sample > 32767) { in_mic_H[i] = 32767; @@ -296,7 +294,7 @@ ptr = stt->Rxx16w32_array[0]; } - for (i = 0; i < WEBRTC_SPL_RSHIFT_W16(M, 1); i++) + for (i = 0; i < M / 2; i++) { if (stt->fs == 16000) { @@ -343,7 +341,7 @@ { if ((samples != 80) && (samples != 160)) { -#ifdef AGC_DEBUG //test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, "AGC->add_far_end, frame %d: Invalid number of samples\n\n", stt->fcount); @@ -355,7 +353,7 @@ { if ((samples != 160) && (samples != 320)) { -#ifdef AGC_DEBUG //test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, "AGC->add_far_end, frame %d: Invalid number of samples\n\n", stt->fcount); @@ -367,7 +365,7 @@ { if ((samples != 160) && (samples != 320)) { -#ifdef AGC_DEBUG //test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, "AGC->add_far_end, frame %d: Invalid number of samples\n\n", stt->fcount); @@ -377,7 +375,7 @@ subFrames = 160; } else { -#ifdef AGC_DEBUG //test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, "AGC->add_far_end, frame %d: Invalid sample rate\n\n", stt->fcount + 1); @@ -455,7 +453,7 @@ stt->lowLevelSignal = 0; } - micLevelTmp = WEBRTC_SPL_LSHIFT_W32(micLevelIn, stt->scale); + micLevelTmp = micLevelIn << stt->scale; /* Set desired level */ gainIdx = stt->micVol; if (stt->micVol > stt->maxAnalog) @@ -482,7 +480,7 @@ } for (ii = 0; ii < samples; ii++) { - tmpFlt = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_U16(in_near[ii], gain), 10); + tmpFlt = (in_near[ii] * gain) >> 10; if (tmpFlt > 32767) { tmpFlt = 32767; @@ -510,8 +508,7 @@ in_near[ii] = (int16_t)tmpFlt; if (stt->fs == 32000) { - tmpFlt = WEBRTC_SPL_MUL_16_U16(in_near_H[ii], gain); - tmpFlt = WEBRTC_SPL_RSHIFT_W32(tmpFlt, 10); + tmpFlt = (in_near_H[ii] * gain) >> 10; if (tmpFlt > 32767) { tmpFlt = 32767; @@ -526,7 +523,7 @@ /* Set the level we (finally) used */ stt->micGainIdx = gainIdx; // *micLevelOut = stt->micGainIdx; - *micLevelOut = WEBRTC_SPL_RSHIFT_W32(stt->micGainIdx, stt->scale); + *micLevelOut = stt->micGainIdx >> stt->scale; /* Add to Mic as if it was the output from a true microphone */ if (WebRtcAgc_AddMic(agcInst, in_near, in_near_H, samples) != 0) { @@ -546,7 +543,7 @@ { /* Lower the analog target level since we have reached its maximum */ zeros = WebRtcSpl_NormW32(stt->Rxx160_LPw32); - stt->targetIdxOffset = WEBRTC_SPL_RSHIFT_W16((3 * zeros) - stt->targetIdx - 2, 2); + stt->targetIdxOffset = (3 * zeros - stt->targetIdx - 2) / 4; } #endif @@ -594,7 +591,7 @@ /* Check if the signal is saturated */ for (i = 0; i < 10; i++) { - tmpW16 = (int16_t)WEBRTC_SPL_RSHIFT_W32(env[i], 20); + tmpW16 = (int16_t)(env[i] >> 20); if (tmpW16 > 875) { stt->envSum += tmpW16; @@ -645,22 +642,23 @@ stt->msZero = 0; /* Increase microphone level only if it's less than 50% */ - midVal = WEBRTC_SPL_RSHIFT_W32(stt->maxAnalog + stt->minLevel + 1, 1); + midVal = (stt->maxAnalog + stt->minLevel + 1) / 2; if (*inMicLevel < midVal) { /* *inMicLevel *= 1.1; */ - tmp32 = WEBRTC_SPL_MUL(1126, *inMicLevel); - *inMicLevel = WEBRTC_SPL_RSHIFT_W32(tmp32, 10); + *inMicLevel = (1126 * *inMicLevel) >> 10; /* Reduces risk of a muted mic repeatedly triggering excessive levels due * to zero signal detection. */ *inMicLevel = WEBRTC_SPL_MIN(*inMicLevel, stt->zeroCtrlMax); stt->micVol = *inMicLevel; } -#ifdef AGC_DEBUG //test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, - "\t\tAGC->zeroCntrl, frame %d: 500 ms under threshold, micVol:\n", - stt->fcount, stt->micVol); + "\t\tAGC->zeroCntrl, frame %d: 500 ms under threshold," + " micVol: %d\n", + stt->fcount, + stt->micVol); #endif stt->activeSpeech = 0; @@ -694,13 +692,13 @@ if (stt->vadMic.stdLongTerm < 4500) { /* Scale between min and max threshold */ - vadThresh += WEBRTC_SPL_RSHIFT_W16(4500 - stt->vadMic.stdLongTerm, 1); + vadThresh += (4500 - stt->vadMic.stdLongTerm) / 2; } /* stt->vadThreshold = (31 * stt->vadThreshold + vadThresh) / 32; */ tmp32 = (int32_t)vadThresh; tmp32 += WEBRTC_SPL_MUL_16_16((int16_t)31, stt->vadThreshold); - stt->vadThreshold = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 5); + stt->vadThreshold = (int16_t)(tmp32 >> 5); } } @@ -767,18 +765,22 @@ Agc_t *stt; stt = (Agc_t *)state; - inMicLevelTmp = WEBRTC_SPL_LSHIFT_W32(inMicLevel, stt->scale); + inMicLevelTmp = inMicLevel << stt->scale; if (inMicLevelTmp > stt->maxAnalog) { -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, "\tAGC->ProcessAnalog, frame %d: micLvl > maxAnalog\n", stt->fcount); +#ifdef WEBRTC_AGC_DEBUG_DUMP + fprintf(stt->fpt, + "\tAGC->ProcessAnalog, frame %d: micLvl > maxAnalog\n", + stt->fcount); #endif return -1; } else if (inMicLevelTmp < stt->minLevel) { -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, "\tAGC->ProcessAnalog, frame %d: micLvl < minLevel\n", stt->fcount); +#ifdef WEBRTC_AGC_DEBUG_DUMP + fprintf(stt->fpt, + "\tAGC->ProcessAnalog, frame %d: micLvl < minLevel\n", + stt->fcount); #endif return -1; } @@ -787,7 +789,7 @@ { int32_t tmpVol; stt->firstCall = 1; - tmp32 = WEBRTC_SPL_RSHIFT_W32((stt->maxLevel - stt->minLevel) * (int32_t)51, 9); + tmp32 = ((stt->maxLevel - stt->minLevel) * 51) >> 9; tmpVol = (stt->minLevel + tmp32); /* If the mic level is very low at start, increase it! */ @@ -807,25 +809,32 @@ /* If the mic level was manually changed to a very low value raise it! */ if ((inMicLevelTmp != stt->micVol) && (inMicLevelTmp < stt->minOutput)) { - tmp32 = WEBRTC_SPL_RSHIFT_W32((stt->maxLevel - stt->minLevel) * (int32_t)51, 9); + tmp32 = ((stt->maxLevel - stt->minLevel) * 51) >> 9; inMicLevelTmp = (stt->minLevel + tmp32); stt->micVol = inMicLevelTmp; #ifdef MIC_LEVEL_FEEDBACK //stt->numBlocksMicLvlSat = 0; #endif -#ifdef AGC_DEBUG //test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: micLvl < minLevel by manual decrease, raise vol\n", + "\tAGC->ProcessAnalog, frame %d: micLvl < minLevel by manual" + " decrease, raise vol\n", stt->fcount); #endif } if (inMicLevelTmp != stt->micVol) { - // Incoming level mismatch; update our level. - // This could be the case if the volume is changed manually, or if the - // sound device has a low volume resolution. - stt->micVol = inMicLevelTmp; + if (inMicLevel == stt->lastInMicLevel) { + // We requested a volume adjustment, but it didn't occur. This is + // probably due to a coarse quantization of the volume slider. + // Restore the requested value to prevent getting stuck. + inMicLevelTmp = stt->micVol; + } + else { + // As long as the value changed, update to match. + stt->micVol = inMicLevelTmp; + } } if (inMicLevelTmp > stt->maxLevel) @@ -835,6 +844,7 @@ } // Store last value here, after we've taken care of manual updates etc. + stt->lastInMicLevel = inMicLevel; lastMicVol = stt->micVol; /* Checks if the signal is saturated. Also a check if individual samples @@ -850,24 +860,25 @@ * Rxx160_LP is adjusted down because it is so slow it could * cause the AGC to make wrong decisions. */ /* stt->Rxx160_LPw32 *= 0.875; */ - stt->Rxx160_LPw32 = WEBRTC_SPL_MUL(WEBRTC_SPL_RSHIFT_W32(stt->Rxx160_LPw32, 3), 7); + stt->Rxx160_LPw32 = (stt->Rxx160_LPw32 / 8) * 7; stt->zeroCtrlMax = stt->micVol; /* stt->micVol *= 0.903; */ tmp32 = inMicLevelTmp - stt->minLevel; tmpU32 = WEBRTC_SPL_UMUL(29591, (uint32_t)(tmp32)); - stt->micVol = (int32_t)WEBRTC_SPL_RSHIFT_U32(tmpU32, 15) + stt->minLevel; + stt->micVol = (tmpU32 >> 15) + stt->minLevel; if (stt->micVol > lastMicVol - 2) { stt->micVol = lastMicVol - 2; } inMicLevelTmp = stt->micVol; -#ifdef AGC_DEBUG //test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, "\tAGC->ProcessAnalog, frame %d: saturated, micVol = %d\n", - stt->fcount, stt->micVol); + stt->fcount, + stt->micVol); #endif if (stt->micVol < stt->minOutput) @@ -920,7 +931,7 @@ Rxx16w32 = stt->Rxx16w32_array[0][i]; /* Rxx160w32 in Q(-7) */ - tmp32 = WEBRTC_SPL_RSHIFT_W32(Rxx16w32 - stt->Rxx16_vectorw32[stt->Rxx16pos], 3); + tmp32 = (Rxx16w32 - stt->Rxx16_vectorw32[stt->Rxx16pos]) >> 3; stt->Rxx160w32 = stt->Rxx160w32 + tmp32; stt->Rxx16_vectorw32[stt->Rxx16pos] = Rxx16w32; @@ -932,7 +943,7 @@ } /* Rxx16_LPw32 in Q(-4) */ - tmp32 = WEBRTC_SPL_RSHIFT_W32(Rxx16w32 - stt->Rxx16_LPw32, kAlphaShortTerm); + tmp32 = (Rxx16w32 - stt->Rxx16_LPw32) >> kAlphaShortTerm; stt->Rxx16_LPw32 = (stt->Rxx16_LPw32) + tmp32; if (vadLogRatio > stt->vadThreshold) @@ -954,11 +965,11 @@ } else if (stt->activeSpeech == 250) { stt->activeSpeech += 2; - tmp32 = WEBRTC_SPL_RSHIFT_W32(stt->Rxx16_LPw32Max, 3); - stt->Rxx160_LPw32 = WEBRTC_SPL_MUL(tmp32, RXX_BUFFER_LEN); + tmp32 = stt->Rxx16_LPw32Max >> 3; + stt->Rxx160_LPw32 = tmp32 * RXX_BUFFER_LEN; } - tmp32 = WEBRTC_SPL_RSHIFT_W32(stt->Rxx160w32 - stt->Rxx160_LPw32, kAlphaLongTerm); + tmp32 = (stt->Rxx160w32 - stt->Rxx160_LPw32) >> kAlphaLongTerm; stt->Rxx160_LPw32 = stt->Rxx160_LPw32 + tmp32; if (stt->Rxx160_LPw32 > stt->upperSecondaryLimit) @@ -973,15 +984,13 @@ /* Lower the recording level */ /* Multiply by 0.828125 which corresponds to decreasing ~0.8dB */ - tmp32 = WEBRTC_SPL_RSHIFT_W32(stt->Rxx160_LPw32, 6); - stt->Rxx160_LPw32 = WEBRTC_SPL_MUL(tmp32, 53); + tmp32 = stt->Rxx160_LPw32 >> 6; + stt->Rxx160_LPw32 = tmp32 * 53; /* Reduce the max gain to avoid excessive oscillation * (but never drop below the maximum analog level). - * stt->maxLevel = (15 * stt->maxLevel + stt->micVol) / 16; */ - tmp32 = (15 * stt->maxLevel) + stt->micVol; - stt->maxLevel = WEBRTC_SPL_RSHIFT_W32(tmp32, 4); + stt->maxLevel = (15 * stt->maxLevel + stt->micVol) / 16; stt->maxLevel = WEBRTC_SPL_MAX(stt->maxLevel, stt->maxAnalog); stt->zeroCtrlMax = stt->micVol; @@ -989,7 +998,7 @@ /* 0.95 in Q15 */ tmp32 = inMicLevelTmp - stt->minLevel; tmpU32 = WEBRTC_SPL_UMUL(31130, (uint32_t)(tmp32)); - stt->micVol = (int32_t)WEBRTC_SPL_RSHIFT_U32(tmpU32, 15) + stt->minLevel; + stt->micVol = (tmpU32 >> 15) + stt->minLevel; if (stt->micVol > lastMicVol - 1) { stt->micVol = lastMicVol - 1; @@ -1004,10 +1013,13 @@ #ifdef MIC_LEVEL_FEEDBACK //stt->numBlocksMicLvlSat = 0; #endif -#ifdef AGC_DEBUG //test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: measure > 2ndUpperLim, micVol = %d, maxLevel = %d\n", - stt->fcount, stt->micVol, stt->maxLevel); + "\tAGC->ProcessAnalog, frame %d: measure >" + " 2ndUpperLim, micVol = %d, maxLevel = %d\n", + stt->fcount, + stt->micVol, + stt->maxLevel); #endif } } else if (stt->Rxx160_LPw32 > stt->upperLimit) @@ -1021,15 +1033,12 @@ /* Lower the recording level */ stt->msTooHigh = 0; /* Multiply by 0.828125 which corresponds to decreasing ~0.8dB */ - tmp32 = WEBRTC_SPL_RSHIFT_W32(stt->Rxx160_LPw32, 6); - stt->Rxx160_LPw32 = WEBRTC_SPL_MUL(tmp32, 53); + stt->Rxx160_LPw32 = (stt->Rxx160_LPw32 / 64) * 53; /* Reduce the max gain to avoid excessive oscillation * (but never drop below the maximum analog level). - * stt->maxLevel = (15 * stt->maxLevel + stt->micVol) / 16; */ - tmp32 = (15 * stt->maxLevel) + stt->micVol; - stt->maxLevel = WEBRTC_SPL_RSHIFT_W32(tmp32, 4); + stt->maxLevel = (15 * stt->maxLevel + stt->micVol) / 16; stt->maxLevel = WEBRTC_SPL_MAX(stt->maxLevel, stt->maxAnalog); stt->zeroCtrlMax = stt->micVol; @@ -1037,7 +1046,7 @@ /* 0.965 in Q15 */ tmp32 = inMicLevelTmp - stt->minLevel; tmpU32 = WEBRTC_SPL_UMUL(31621, (uint32_t)(inMicLevelTmp - stt->minLevel)); - stt->micVol = (int32_t)WEBRTC_SPL_RSHIFT_U32(tmpU32, 15) + stt->minLevel; + stt->micVol = (tmpU32 >> 15) + stt->minLevel; if (stt->micVol > lastMicVol - 1) { stt->micVol = lastMicVol - 1; @@ -1047,10 +1056,13 @@ #ifdef MIC_LEVEL_FEEDBACK //stt->numBlocksMicLvlSat = 0; #endif -#ifdef AGC_DEBUG //test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: measure > UpperLim, micVol = %d, maxLevel = %d\n", - stt->fcount, stt->micVol, stt->maxLevel); + "\tAGC->ProcessAnalog, frame %d: measure >" + " UpperLim, micVol = %d, maxLevel = %d\n", + stt->fcount, + stt->micVol, + stt->maxLevel); #endif } } else if (stt->Rxx160_LPw32 < stt->lowerSecondaryLimit) @@ -1068,11 +1080,10 @@ stt->msTooLow = 0; /* Normalize the volume level */ - tmp32 = WEBRTC_SPL_LSHIFT_W32(inMicLevelTmp - stt->minLevel, 14); + tmp32 = (inMicLevelTmp - stt->minLevel) << 14; if (stt->maxInit != stt->minLevel) { - volNormFIX = (int16_t)WEBRTC_SPL_DIV(tmp32, - (stt->maxInit - stt->minLevel)); + volNormFIX = tmp32 / (stt->maxInit - stt->minLevel); } /* Find correct curve */ @@ -1084,12 +1095,11 @@ volNormFIX, 13); /* stt->Rxx160_LPw32 *= 1.047 [~0.2 dB]; */ - tmp32 = WEBRTC_SPL_RSHIFT_W32(stt->Rxx160_LPw32, 6); - stt->Rxx160_LPw32 = WEBRTC_SPL_MUL(tmp32, 67); + stt->Rxx160_LPw32 = (stt->Rxx160_LPw32 / 64) * 67; tmp32 = inMicLevelTmp - stt->minLevel; tmpU32 = ((uint32_t)weightFIX * (uint32_t)(inMicLevelTmp - stt->minLevel)); - stt->micVol = (int32_t)WEBRTC_SPL_RSHIFT_U32(tmpU32, 14) + stt->minLevel; + stt->micVol = (tmpU32 >> 14) + stt->minLevel; if (stt->micVol < lastMicVol + 2) { stt->micVol = lastMicVol + 2; @@ -1107,10 +1117,12 @@ fprintf(stderr, "Sat mic Level: %d\n", stt->numBlocksMicLvlSat); } #endif -#ifdef AGC_DEBUG //test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: measure < 2ndLowerLim, micVol = %d\n", - stt->fcount, stt->micVol); + "\tAGC->ProcessAnalog, frame %d: measure <" + " 2ndLowerLim, micVol = %d\n", + stt->fcount, + stt->micVol); #endif } } else if (stt->Rxx160_LPw32 < stt->lowerLimit) @@ -1128,11 +1140,10 @@ stt->msTooLow = 0; /* Normalize the volume level */ - tmp32 = WEBRTC_SPL_LSHIFT_W32(inMicLevelTmp - stt->minLevel, 14); + tmp32 = (inMicLevelTmp - stt->minLevel) << 14; if (stt->maxInit != stt->minLevel) { - volNormFIX = (int16_t)WEBRTC_SPL_DIV(tmp32, - (stt->maxInit - stt->minLevel)); + volNormFIX = tmp32 / (stt->maxInit - stt->minLevel); } /* Find correct curve */ @@ -1144,12 +1155,11 @@ volNormFIX, 13); /* stt->Rxx160_LPw32 *= 1.047 [~0.2 dB]; */ - tmp32 = WEBRTC_SPL_RSHIFT_W32(stt->Rxx160_LPw32, 6); - stt->Rxx160_LPw32 = WEBRTC_SPL_MUL(tmp32, 67); + stt->Rxx160_LPw32 = (stt->Rxx160_LPw32 / 64) * 67; tmp32 = inMicLevelTmp - stt->minLevel; tmpU32 = ((uint32_t)weightFIX * (uint32_t)(inMicLevelTmp - stt->minLevel)); - stt->micVol = (int32_t)WEBRTC_SPL_RSHIFT_U32(tmpU32, 14) + stt->minLevel; + stt->micVol = (tmpU32 >> 14) + stt->minLevel; if (stt->micVol < lastMicVol + 1) { stt->micVol = lastMicVol + 1; @@ -1167,10 +1177,11 @@ fprintf(stderr, "Sat mic Level: %d\n", stt->numBlocksMicLvlSat); } #endif -#ifdef AGC_DEBUG //test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, "\tAGC->ProcessAnalog, frame %d: measure < LowerLim, micVol = %d\n", - stt->fcount, stt->micVol); + stt->fcount, + stt->micVol); #endif } @@ -1233,11 +1244,7 @@ stt->micVol = stt->minOutput; } - *outMicLevel = WEBRTC_SPL_RSHIFT_W32(stt->micVol, stt->scale); - if (*outMicLevel > WEBRTC_SPL_RSHIFT_W32(stt->maxAnalog, stt->scale)) - { - *outMicLevel = WEBRTC_SPL_RSHIFT_W32(stt->maxAnalog, stt->scale); - } + *outMicLevel = WEBRTC_SPL_MIN(stt->micVol, stt->maxAnalog) >> stt->scale; return 0; } @@ -1267,9 +1274,10 @@ { if ((samples != 80) && (samples != 160)) { -#ifdef AGC_DEBUG //test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, - "AGC->Process, frame %d: Invalid number of samples\n\n", stt->fcount); + "AGC->Process, frame %d: Invalid number of samples\n\n", + stt->fcount); #endif return -1; } @@ -1278,9 +1286,10 @@ { if ((samples != 160) && (samples != 320)) { -#ifdef AGC_DEBUG //test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, - "AGC->Process, frame %d: Invalid number of samples\n\n", stt->fcount); + "AGC->Process, frame %d: Invalid number of samples\n\n", + stt->fcount); #endif return -1; } @@ -1289,18 +1298,20 @@ { if ((samples != 160) && (samples != 320)) { -#ifdef AGC_DEBUG //test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, - "AGC->Process, frame %d: Invalid number of samples\n\n", stt->fcount); + "AGC->Process, frame %d: Invalid number of samples\n\n", + stt->fcount); #endif return -1; } subFrames = 160; } else { -#ifdef AGC_DEBUG// test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, - "AGC->Process, frame %d: Invalid sample rate\n\n", stt->fcount); + "AGC->Process, frame %d: Invalid sample rate\n\n", + stt->fcount); #endif return -1; } @@ -1336,7 +1347,7 @@ } } -#ifdef AGC_DEBUG//test log +#ifdef WEBRTC_AGC_DEBUG_DUMP stt->fcount++; #endif @@ -1345,8 +1356,10 @@ if (WebRtcAgc_ProcessDigital(&stt->digitalAgc, &in_near[i], &in_near_H[i], &out[i], &out_H[i], stt->fs, stt->lowLevelSignal) == -1) { -#ifdef AGC_DEBUG//test log - fprintf(stt->fpt, "AGC->Process, frame %d: Error from DigAGC\n\n", stt->fcount); +#ifdef WEBRTC_AGC_DEBUG_DUMP + fprintf(stt->fpt, + "AGC->Process, frame %d: Error from DigAGC\n\n", + stt->fcount); #endif return -1; } @@ -1359,8 +1372,14 @@ return -1; } } -#ifdef AGC_DEBUG//test log - fprintf(stt->agcLog, "%5d\t%d\t%d\t%d\n", stt->fcount, inMicLevelTmp, *outMicLevel, stt->maxLevel, stt->micVol); +#ifdef WEBRTC_AGC_DEBUG_DUMP + fprintf(stt->agcLog, + "%5d\t%d\t%d\t%d\t%d\n", + stt->fcount, + inMicLevelTmp, + *outMicLevel, + stt->maxLevel, + stt->micVol); #endif /* update queue */ @@ -1436,8 +1455,10 @@ if (WebRtcAgc_CalculateGainTable(&(stt->digitalAgc.gainTable[0]), stt->compressionGaindB, stt->targetLevelDbfs, stt->limiterEnable, stt->analogTarget) == -1) { -#ifdef AGC_DEBUG//test log - fprintf(stt->fpt, "AGC->set_config, frame %d: Error from calcGainTable\n\n", stt->fcount); +#ifdef WEBRTC_AGC_DEBUG_DUMP + fprintf(stt->fpt, + "AGC->set_config, frame %d: Error from calcGainTable\n\n", + stt->fcount); #endif return -1; } @@ -1493,7 +1514,7 @@ return -1; } -#ifdef AGC_DEBUG +#ifdef WEBRTC_AGC_DEBUG_DUMP stt->fpt = fopen("./agc_test_log.txt", "wt"); stt->agcLog = fopen("./agc_debug_log.txt", "wt"); stt->digitalAgc.logFile = fopen("./agc_log.txt", "wt"); @@ -1510,7 +1531,7 @@ Agc_t *stt; stt = (Agc_t *)state; -#ifdef AGC_DEBUG +#ifdef WEBRTC_AGC_DEBUG_DUMP fclose(stt->fpt); fclose(stt->agcLog); fclose(stt->digitalAgc.logFile); @@ -1548,13 +1569,13 @@ * 2 - Digital Automatic Gain Control [-targetLevelDbfs (default -3 dBOv)] * 3 - Fixed Digital Gain [compressionGaindB (default 8 dB)] */ -#ifdef AGC_DEBUG//test log +#ifdef WEBRTC_AGC_DEBUG_DUMP stt->fcount = 0; fprintf(stt->fpt, "AGC->Init\n"); #endif if (agcMode < kAgcModeUnchanged || agcMode > kAgcModeFixedDigital) { -#ifdef AGC_DEBUG//test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, "AGC->Init: error, incorrect mode\n\n"); #endif return -1; @@ -1576,8 +1597,8 @@ // TODO(bjornv): Investigate if we really need to scale up a small range now when we have // a guard against zero-increments. For now, we do not support scale up (scale = 0). stt->scale = 0; - maxLevel = WEBRTC_SPL_LSHIFT_W32(maxLevel, stt->scale); - minLevel = WEBRTC_SPL_LSHIFT_W32(minLevel, stt->scale); + maxLevel <<= stt->scale; + minLevel <<= stt->scale; /* Make minLevel and maxLevel static in AdaptiveDigital */ if (stt->agcMode == kAgcModeAdaptiveDigital) @@ -1588,7 +1609,7 @@ } /* The maximum supplemental volume range is based on a vague idea * of how much lower the gain will be than the real analog gain. */ - max_add = WEBRTC_SPL_RSHIFT_W32(maxLevel - minLevel, 2); + max_add = (maxLevel - minLevel) / 4; /* Minimum/maximum volume level that can be set */ stt->minLevel = minLevel; @@ -1597,6 +1618,7 @@ stt->maxInit = stt->maxLevel; stt->zeroCtrlMax = stt->maxAnalog; + stt->lastInMicLevel = 0; /* Initialize micVol parameter */ stt->micVol = stt->maxAnalog; @@ -1610,14 +1632,16 @@ stt->numBlocksMicLvlSat = 0; stt->micLvlSat = 0; #endif -#ifdef AGC_DEBUG//test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, "AGC->Init: minLevel = %d, maxAnalog = %d, maxLevel = %d\n", - stt->minLevel, stt->maxAnalog, stt->maxLevel); + stt->minLevel, + stt->maxAnalog, + stt->maxLevel); #endif /* Minimum output volume is 4% higher than the available lowest volume level */ - tmp32 = WEBRTC_SPL_RSHIFT_W32((stt->maxLevel - stt->minLevel) * (int32_t)10, 8); + tmp32 = ((stt->maxLevel - stt->minLevel) * 10) >> 8; stt->minOutput = (stt->minLevel + tmp32); stt->msTooLow = 0; @@ -1681,13 +1705,13 @@ /* Only positive values are allowed that are not too large */ if ((minLevel >= maxLevel) || (maxLevel & 0xFC000000)) { -#ifdef AGC_DEBUG//test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, "minLevel, maxLevel value(s) are invalid\n\n"); #endif return -1; } else { -#ifdef AGC_DEBUG//test log +#ifdef WEBRTC_AGC_DEBUG_DUMP fprintf(stt->fpt, "\n"); #endif return 0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/analog_agc.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/analog_agc.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/analog_agc.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/analog_agc.h 2015-02-03 14:33:35.000000000 +0000 @@ -15,9 +15,8 @@ #include "webrtc/modules/audio_processing/agc/include/gain_control.h" #include "webrtc/typedefs.h" -//#define AGC_DEBUG //#define MIC_LEVEL_FEEDBACK -#ifdef AGC_DEBUG +#ifdef WEBRTC_AGC_DEBUG_DUMP #include #endif @@ -111,6 +110,7 @@ int32_t minLevel; // Minimum possible volume level int32_t minOutput; // Minimum output volume level int32_t zeroCtrlMax; // Remember max gain => don't amp low input + int32_t lastInMicLevel; int16_t scale; // Scale factor for internal volume levels #ifdef MIC_LEVEL_FEEDBACK @@ -121,10 +121,10 @@ AgcVad_t vadMic; DigitalAgc_t digitalAgc; -#ifdef AGC_DEBUG - FILE* fpt; - FILE* agcLog; - int32_t fcount; +#ifdef WEBRTC_AGC_DEBUG_DUMP + FILE* fpt; + FILE* agcLog; + int32_t fcount; #endif int16_t lowLevelSignal; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_agc -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := \ - analog_agc.c \ - digital_agc.c - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../../../common_audio/signal_processing/include \ - external/webrtc - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/digital_agc.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/digital_agc.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/digital_agc.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/digital_agc.c 2015-02-03 14:33:35.000000000 +0000 @@ -16,7 +16,7 @@ #include #include -#ifdef AGC_DEBUG +#ifdef WEBRTC_AGC_DEBUG_DUMP #include #endif @@ -116,9 +116,8 @@ // limiterLvlX = analogTarget - limiterOffset // limiterLvl = targetLevelDbfs + limiterOffset/compRatio limiterLvlX = analogTarget - limiterOffset; - limiterIdx = 2 - + WebRtcSpl_DivW32W16ResW16(WEBRTC_SPL_LSHIFT_W32((int32_t)limiterLvlX, 13), - WEBRTC_SPL_RSHIFT_U16(kLog10_2, 1)); + limiterIdx = + 2 + WebRtcSpl_DivW32W16ResW16((int32_t)limiterLvlX << 13, kLog10_2 / 2); tmp16no1 = WebRtcSpl_DivW32W16ResW16(limiterOffset + (kCompRatio >> 1), kCompRatio); limiterLvl = targetLevelDbfs + tmp16no1; @@ -144,18 +143,18 @@ inLevel = WebRtcSpl_DivW32W16(tmp32, kCompRatio); // Q14 // Calculate diffGain-inLevel, to map using the genFuncTable - inLevel = WEBRTC_SPL_LSHIFT_W32((int32_t)diffGain, 14) - inLevel; // Q14 + inLevel = ((int32_t)diffGain << 14) - inLevel; // Q14 // Make calculations on abs(inLevel) and compensate for the sign afterwards. absInLevel = (uint32_t)WEBRTC_SPL_ABS_W32(inLevel); // Q14 // LUT with interpolation - intPart = (uint16_t)WEBRTC_SPL_RSHIFT_U32(absInLevel, 14); + intPart = (uint16_t)(absInLevel >> 14); fracPart = (uint16_t)(absInLevel & 0x00003FFF); // extract the fractional part tmpU16 = kGenFuncTable[intPart + 1] - kGenFuncTable[intPart]; // Q8 - tmpU32no1 = WEBRTC_SPL_UMUL_16_16(tmpU16, fracPart); // Q22 - tmpU32no1 += WEBRTC_SPL_LSHIFT_U32((uint32_t)kGenFuncTable[intPart], 14); // Q22 - logApprox = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, 8); // Q14 + tmpU32no1 = tmpU16 * fracPart; // Q22 + tmpU32no1 += (uint32_t)kGenFuncTable[intPart] << 14; // Q22 + logApprox = tmpU32no1 >> 8; // Q14 // Compensate for negative exponent using the relation: // log2(1 + 2^-x) = log2(1 + 2^x) - x if (inLevel < 0) @@ -165,29 +164,29 @@ if (zeros < 15) { // Not enough space for multiplication - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(absInLevel, 15 - zeros); // Q(zeros-1) + tmpU32no2 = absInLevel >> (15 - zeros); // Q(zeros-1) tmpU32no2 = WEBRTC_SPL_UMUL_32_16(tmpU32no2, kLogE_1); // Q(zeros+13) if (zeros < 9) { - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, 9 - zeros); // Q(zeros+13) zerosScale = 9 - zeros; + tmpU32no1 >>= zerosScale; // Q(zeros+13) } else { - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(tmpU32no2, zeros - 9); // Q22 + tmpU32no2 >>= zeros - 9; // Q22 } } else { tmpU32no2 = WEBRTC_SPL_UMUL_32_16(absInLevel, kLogE_1); // Q28 - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(tmpU32no2, 6); // Q22 + tmpU32no2 >>= 6; // Q22 } logApprox = 0; if (tmpU32no2 < tmpU32no1) { - logApprox = WEBRTC_SPL_RSHIFT_U32(tmpU32no1 - tmpU32no2, 8 - zerosScale); //Q14 + logApprox = (tmpU32no1 - tmpU32no2) >> (8 - zerosScale); //Q14 } } - numFIX = WEBRTC_SPL_LSHIFT_W32(WEBRTC_SPL_MUL_16_U16(maxGain, constMaxGain), 6); // Q14 - numFIX -= WEBRTC_SPL_MUL_32_16((int32_t)logApprox, diffGain); // Q14 + numFIX = (maxGain * constMaxGain) << 6; // Q14 + numFIX -= (int32_t)logApprox * diffGain; // Q14 // Calculate ratio // Shift |numFIX| as much as possible. @@ -199,56 +198,55 @@ { zeros = WebRtcSpl_NormW32(den) + 8; } - numFIX = WEBRTC_SPL_LSHIFT_W32(numFIX, zeros); // Q(14+zeros) + numFIX <<= zeros; // Q(14+zeros) // Shift den so we end up in Qy1 tmp32no1 = WEBRTC_SPL_SHIFT_W32(den, zeros - 8); // Q(zeros) if (numFIX < 0) { - numFIX -= WEBRTC_SPL_RSHIFT_W32(tmp32no1, 1); + numFIX -= tmp32no1 / 2; } else { - numFIX += WEBRTC_SPL_RSHIFT_W32(tmp32no1, 1); + numFIX += tmp32no1 / 2; } - y32 = WEBRTC_SPL_DIV(numFIX, tmp32no1); // in Q14 + y32 = numFIX / tmp32no1; // in Q14 if (limiterEnable && (i < limiterIdx)) { tmp32 = WEBRTC_SPL_MUL_16_U16(i - 1, kLog10_2); // Q14 - tmp32 -= WEBRTC_SPL_LSHIFT_W32(limiterLvl, 14); // Q14 + tmp32 -= limiterLvl << 14; // Q14 y32 = WebRtcSpl_DivW32W16(tmp32 + 10, 20); } if (y32 > 39000) { - tmp32 = WEBRTC_SPL_MUL(y32 >> 1, kLog10) + 4096; // in Q27 - tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 13); // in Q14 + tmp32 = (y32 >> 1) * kLog10 + 4096; // in Q27 + tmp32 >>= 13; // In Q14. } else { - tmp32 = WEBRTC_SPL_MUL(y32, kLog10) + 8192; // in Q28 - tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 14); // in Q14 + tmp32 = y32 * kLog10 + 8192; // in Q28 + tmp32 >>= 14; // In Q14. } - tmp32 += WEBRTC_SPL_LSHIFT_W32(16, 14); // in Q14 (Make sure final output is in Q16) + tmp32 += 16 << 14; // in Q14 (Make sure final output is in Q16) // Calculate power if (tmp32 > 0) { - intPart = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 14); + intPart = (int16_t)(tmp32 >> 14); fracPart = (uint16_t)(tmp32 & 0x00003FFF); // in Q14 - if (WEBRTC_SPL_RSHIFT_W32(fracPart, 13)) + if ((fracPart >> 13) != 0) { - tmp16 = WEBRTC_SPL_LSHIFT_W16(2, 14) - constLinApprox; - tmp32no2 = WEBRTC_SPL_LSHIFT_W32(1, 14) - fracPart; - tmp32no2 = WEBRTC_SPL_MUL_32_16(tmp32no2, tmp16); - tmp32no2 = WEBRTC_SPL_RSHIFT_W32(tmp32no2, 13); - tmp32no2 = WEBRTC_SPL_LSHIFT_W32(1, 14) - tmp32no2; + tmp16 = (2 << 14) - constLinApprox; + tmp32no2 = (1 << 14) - fracPart; + tmp32no2 *= tmp16; + tmp32no2 >>= 13; + tmp32no2 = (1 << 14) - tmp32no2; } else { - tmp16 = constLinApprox - WEBRTC_SPL_LSHIFT_W16(1, 14); - tmp32no2 = WEBRTC_SPL_MUL_32_16(fracPart, tmp16); - tmp32no2 = WEBRTC_SPL_RSHIFT_W32(tmp32no2, 13); + tmp16 = constLinApprox - (1 << 14); + tmp32no2 = (fracPart * tmp16) >> 13; } fracPart = (uint16_t)tmp32no2; - gainTable[i] = WEBRTC_SPL_LSHIFT_W32(1, intPart) - + WEBRTC_SPL_SHIFT_W32(fracPart, intPart - 14); + gainTable[i] = + (1 << intPart) + WEBRTC_SPL_SHIFT_W32(fracPart, intPart - 14); } else { gainTable[i] = 0; @@ -274,7 +272,7 @@ stt->gain = 65536; stt->gatePrevious = 0; stt->agcMode = agcMode; -#ifdef AGC_DEBUG +#ifdef WEBRTC_AGC_DEBUG_DUMP stt->frameCounter = 0; #endif @@ -288,12 +286,7 @@ int32_t WebRtcAgc_AddFarendToDigital(DigitalAgc_t *stt, const int16_t *in_far, int16_t nrSamples) { - // Check for valid pointer - if (stt == NULL) - { - return -1; - } - + assert(stt != NULL); // VAD for far end WebRtcAgc_ProcessVad(&stt->vadFarend, in_far, nrSamples); @@ -315,7 +308,7 @@ int32_t gain32, delta; int16_t logratio; int16_t lower_thr, upper_thr; - int16_t zeros, zeros_fast, frac; + int16_t zeros = 0, zeros_fast, frac = 0; int16_t decay; int16_t gate, gain_adj; int16_t k, n; @@ -359,7 +352,7 @@ if (stt->vadFarend.counter > 10) { tmp32 = WEBRTC_SPL_MUL_16_16(3, logratio); - logratio = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 - stt->vadFarend.logRatio, 2); + logratio = (int16_t)((tmp32 - stt->vadFarend.logRatio) >> 2); } // Determine decay factor depending on VAD @@ -380,7 +373,7 @@ // * (2^27/(DecayTime*(upper_thr-lower_thr)))) >> 10); // SUBSTITUTED: 2^27/(DecayTime*(upper_thr-lower_thr)) -> 65 tmp32 = WEBRTC_SPL_MUL_16_16((lower_thr - logratio), 65); - decay = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 10); + decay = (int16_t)(tmp32 >> 10); } // adjust decay factor for long silence (detected as low standard deviation) @@ -394,7 +387,7 @@ { // decay = (int16_t)(((stt->vadNearend.stdLongTerm - 4000) * decay) >> 12); tmp32 = WEBRTC_SPL_MUL_16_16((stt->vadNearend.stdLongTerm - 4000), decay); - decay = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 12); + decay = (int16_t)(tmp32 >> 12); } if (lowlevelSignal != 0) @@ -402,9 +395,14 @@ decay = 0; } } -#ifdef AGC_DEBUG +#ifdef WEBRTC_AGC_DEBUG_DUMP stt->frameCounter++; - fprintf(stt->logFile, "%5.2f\t%d\t%d\t%d\t", (float)(stt->frameCounter) / 100, logratio, decay, stt->vadNearend.stdLongTerm); + fprintf(stt->logFile, + "%5.2f\t%d\t%d\t%d\t", + (float)(stt->frameCounter) / 100, + logratio, + decay, + stt->vadNearend.stdLongTerm); #endif // Find max amplitude per sub frame // iterate over sub frames @@ -462,29 +460,34 @@ { zeros = 31; } - tmp32 = (WEBRTC_SPL_LSHIFT_W32(cur_level, zeros) & 0x7FFFFFFF); - frac = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 19); // Q12 - tmp32 = WEBRTC_SPL_MUL((stt->gainTable[zeros-1] - stt->gainTable[zeros]), frac); - gains[k + 1] = stt->gainTable[zeros] + WEBRTC_SPL_RSHIFT_W32(tmp32, 12); -#ifdef AGC_DEBUG - if (k == 0) - { - fprintf(stt->logFile, "%d\t%d\t%d\t%d\t%d\n", env[0], cur_level, stt->capacitorFast, stt->capacitorSlow, zeros); + tmp32 = (cur_level << zeros) & 0x7FFFFFFF; + frac = (int16_t)(tmp32 >> 19); // Q12. + tmp32 = (stt->gainTable[zeros-1] - stt->gainTable[zeros]) * frac; + gains[k + 1] = stt->gainTable[zeros] + (tmp32 >> 12); +#ifdef WEBRTC_AGC_DEBUG_DUMP + if (k == 0) { + fprintf(stt->logFile, + "%d\t%d\t%d\t%d\t%d\n", + env[0], + cur_level, + stt->capacitorFast, + stt->capacitorSlow, + zeros); } #endif } // Gate processing (lower gain during absence of speech) - zeros = WEBRTC_SPL_LSHIFT_W16(zeros, 9) - WEBRTC_SPL_RSHIFT_W16(frac, 3); + zeros = (zeros << 9) - (frac >> 3); // find number of leading zeros zeros_fast = WebRtcSpl_NormU32((uint32_t)stt->capacitorFast); if (stt->capacitorFast == 0) { zeros_fast = 31; } - tmp32 = (WEBRTC_SPL_LSHIFT_W32(stt->capacitorFast, zeros_fast) & 0x7FFFFFFF); - zeros_fast = WEBRTC_SPL_LSHIFT_W16(zeros_fast, 9); - zeros_fast -= (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 22); + tmp32 = (stt->capacitorFast << zeros_fast) & 0x7FFFFFFF; + zeros_fast <<= 9; + zeros_fast -= (int16_t)(tmp32 >> 22); gate = 1000 + zeros_fast - zeros - stt->vadNearend.stdShortTerm; @@ -494,7 +497,7 @@ } else { tmp32 = WEBRTC_SPL_MUL_16_16(stt->gatePrevious, 7); - gate = (int16_t)WEBRTC_SPL_RSHIFT_W32((int32_t)gate + tmp32, 3); + gate = (int16_t)((gate + tmp32) >> 3); stt->gatePrevious = gate; } // gate < 0 -> no gate @@ -503,7 +506,7 @@ { if (gate < 2500) { - gain_adj = WEBRTC_SPL_RSHIFT_W16(2500 - gate, 5); + gain_adj = (2500 - gate) >> 5; } else { gain_adj = 0; @@ -513,12 +516,12 @@ if ((gains[k + 1] - stt->gainTable[0]) > 8388608) { // To prevent wraparound - tmp32 = WEBRTC_SPL_RSHIFT_W32((gains[k+1] - stt->gainTable[0]), 8); - tmp32 = WEBRTC_SPL_MUL(tmp32, (178 + gain_adj)); + tmp32 = (gains[k + 1] - stt->gainTable[0]) >> 8; + tmp32 *= 178 + gain_adj; } else { - tmp32 = WEBRTC_SPL_MUL((gains[k+1] - stt->gainTable[0]), (178 + gain_adj)); - tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 8); + tmp32 = (gains[k+1] - stt->gainTable[0]) * (178 + gain_adj); + tmp32 >>= 8; } gains[k + 1] = stt->gainTable[0] + tmp32; } @@ -533,23 +536,23 @@ { zeros = 16 - WebRtcSpl_NormW32(gains[k + 1]); } - gain32 = WEBRTC_SPL_RSHIFT_W32(gains[k+1], zeros) + 1; - gain32 = WEBRTC_SPL_MUL(gain32, gain32); + gain32 = (gains[k + 1] >> zeros) + 1; + gain32 *= gain32; // check for overflow - while (AGC_MUL32(WEBRTC_SPL_RSHIFT_W32(env[k], 12) + 1, gain32) + while (AGC_MUL32((env[k] >> 12) + 1, gain32) > WEBRTC_SPL_SHIFT_W32((int32_t)32767, 2 * (1 - zeros + 10))) { // multiply by 253/256 ==> -0.1 dB if (gains[k + 1] > 8388607) { // Prevent wrap around - gains[k + 1] = WEBRTC_SPL_MUL(WEBRTC_SPL_RSHIFT_W32(gains[k+1], 8), 253); + gains[k + 1] = (gains[k+1] / 256) * 253; } else { - gains[k + 1] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(gains[k+1], 253), 8); + gains[k + 1] = (gains[k+1] * 253) / 256; } - gain32 = WEBRTC_SPL_RSHIFT_W32(gains[k+1], zeros) + 1; - gain32 = WEBRTC_SPL_MUL(gain32, gain32); + gain32 = (gains[k + 1] >> zeros) + 1; + gain32 *= gain32; } } // gain reductions should be done 1 ms earlier than gain increases @@ -565,14 +568,14 @@ // Apply gain // handle first sub frame separately - delta = WEBRTC_SPL_LSHIFT_W32(gains[1] - gains[0], (4 - L2)); - gain32 = WEBRTC_SPL_LSHIFT_W32(gains[0], 4); + delta = (gains[1] - gains[0]) << (4 - L2); + gain32 = gains[0] << 4; // iterate over samples for (n = 0; n < L; n++) { // For lower band - tmp32 = WEBRTC_SPL_MUL((int32_t)out[n], WEBRTC_SPL_RSHIFT_W32(gain32 + 127, 7)); - out_tmp = WEBRTC_SPL_RSHIFT_W32(tmp32 , 16); + tmp32 = out[n] * ((gain32 + 127) >> 7); + out_tmp = tmp32 >> 16; if (out_tmp > 4095) { out[n] = (int16_t)32767; @@ -581,15 +584,14 @@ out[n] = (int16_t)-32768; } else { - tmp32 = WEBRTC_SPL_MUL((int32_t)out[n], WEBRTC_SPL_RSHIFT_W32(gain32, 4)); - out[n] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16); + tmp32 = out[n] * (gain32 >> 4); + out[n] = (int16_t)(tmp32 >> 16); } // For higher band if (FS == 32000) { - tmp32 = WEBRTC_SPL_MUL((int32_t)out_H[n], - WEBRTC_SPL_RSHIFT_W32(gain32 + 127, 7)); - out_tmp = WEBRTC_SPL_RSHIFT_W32(tmp32 , 16); + tmp32 = out_H[n] * ((gain32 + 127) >> 7); + out_tmp = tmp32 >> 16; if (out_tmp > 4095) { out_H[n] = (int16_t)32767; @@ -598,9 +600,8 @@ out_H[n] = (int16_t)-32768; } else { - tmp32 = WEBRTC_SPL_MUL((int32_t)out_H[n], - WEBRTC_SPL_RSHIFT_W32(gain32, 4)); - out_H[n] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16); + tmp32 = out_H[n] * (gain32 >> 4); + out_H[n] = (int16_t)(tmp32 >> 16); } } // @@ -610,21 +611,19 @@ // iterate over subframes for (k = 1; k < 10; k++) { - delta = WEBRTC_SPL_LSHIFT_W32(gains[k+1] - gains[k], (4 - L2)); - gain32 = WEBRTC_SPL_LSHIFT_W32(gains[k], 4); + delta = (gains[k+1] - gains[k]) << (4 - L2); + gain32 = gains[k] << 4; // iterate over samples for (n = 0; n < L; n++) { // For lower band - tmp32 = WEBRTC_SPL_MUL((int32_t)out[k * L + n], - WEBRTC_SPL_RSHIFT_W32(gain32, 4)); - out[k * L + n] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16); + tmp32 = out[k * L + n] * (gain32 >> 4); + out[k * L + n] = (int16_t)(tmp32 >> 16); // For higher band if (FS == 32000) { - tmp32 = WEBRTC_SPL_MUL((int32_t)out_H[k * L + n], - WEBRTC_SPL_RSHIFT_W32(gain32, 4)); - out_H[k * L + n] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16); + tmp32 = out_H[k * L + n] * (gain32 >> 4); + out_H[k * L + n] = (int16_t)(tmp32 >> 16); } gain32 += delta; } @@ -640,17 +639,17 @@ state->HPstate = 0; // state of high pass filter state->logRatio = 0; // log( P(active) / P(inactive) ) // average input level (Q10) - state->meanLongTerm = WEBRTC_SPL_LSHIFT_W16(15, 10); + state->meanLongTerm = 15 << 10; // variance of input level (Q8) - state->varianceLongTerm = WEBRTC_SPL_LSHIFT_W32(500, 8); + state->varianceLongTerm = 500 << 8; state->stdLongTerm = 0; // standard deviation of input level in dB // short-term average input level (Q10) - state->meanShortTerm = WEBRTC_SPL_LSHIFT_W16(15, 10); + state->meanShortTerm = 15 << 10; // short-term variance of input level (Q8) - state->varianceShortTerm = WEBRTC_SPL_LSHIFT_W32(500, 8); + state->varianceShortTerm = 500 << 8; state->stdShortTerm = 0; // short-term standard deviation of input level in dB state->counter = 3; // counts updates @@ -684,7 +683,7 @@ for (k = 0; k < 8; k++) { tmp32 = (int32_t)in[2 * k] + (int32_t)in[2 * k + 1]; - tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 1); + tmp32 >>= 1; buf1[k] = (int16_t)tmp32; } in += 16; @@ -700,10 +699,9 @@ for (k = 0; k < 4; k++) { out = buf2[k] + HPstate; - tmp32 = WEBRTC_SPL_MUL(600, out); - HPstate = (int16_t)(WEBRTC_SPL_RSHIFT_W32(tmp32, 10) - buf2[k]); - tmp32 = WEBRTC_SPL_MUL(out, out); - nrg += WEBRTC_SPL_RSHIFT_W32(tmp32, 6); + tmp32 = 600 * out; + HPstate = (int16_t)((tmp32 >> 10) - buf2[k]); + nrg += (out * out) >> 6; } } state->HPstate = HPstate; @@ -734,7 +732,7 @@ } // energy level (range {-32..30}) (Q10) - dB = WEBRTC_SPL_LSHIFT_W16(15 - zeros, 11); + dB = (15 - zeros) << 11; // Update statistics @@ -746,43 +744,43 @@ // update short-term estimate of mean energy level (Q10) tmp32 = (WEBRTC_SPL_MUL_16_16(state->meanShortTerm, 15) + (int32_t)dB); - state->meanShortTerm = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 4); + state->meanShortTerm = (int16_t)(tmp32 >> 4); // update short-term estimate of variance in energy level (Q8) - tmp32 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(dB, dB), 12); - tmp32 += WEBRTC_SPL_MUL(state->varianceShortTerm, 15); - state->varianceShortTerm = WEBRTC_SPL_RSHIFT_W32(tmp32, 4); + tmp32 = (dB * dB) >> 12; + tmp32 += state->varianceShortTerm * 15; + state->varianceShortTerm = tmp32 / 16; // update short-term estimate of standard deviation in energy level (Q10) tmp32 = WEBRTC_SPL_MUL_16_16(state->meanShortTerm, state->meanShortTerm); - tmp32 = WEBRTC_SPL_LSHIFT_W32(state->varianceShortTerm, 12) - tmp32; + tmp32 = (state->varianceShortTerm << 12) - tmp32; state->stdShortTerm = (int16_t)WebRtcSpl_Sqrt(tmp32); // update long-term estimate of mean energy level (Q10) tmp32 = WEBRTC_SPL_MUL_16_16(state->meanLongTerm, state->counter) + (int32_t)dB; - state->meanLongTerm = WebRtcSpl_DivW32W16ResW16(tmp32, - WEBRTC_SPL_ADD_SAT_W16(state->counter, 1)); + state->meanLongTerm = WebRtcSpl_DivW32W16ResW16( + tmp32, WebRtcSpl_AddSatW16(state->counter, 1)); // update long-term estimate of variance in energy level (Q8) - tmp32 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(dB, dB), 12); - tmp32 += WEBRTC_SPL_MUL(state->varianceLongTerm, state->counter); - state->varianceLongTerm = WebRtcSpl_DivW32W16(tmp32, - WEBRTC_SPL_ADD_SAT_W16(state->counter, 1)); + tmp32 = (dB * dB) >> 12; + tmp32 += state->varianceLongTerm * state->counter; + state->varianceLongTerm = WebRtcSpl_DivW32W16( + tmp32, WebRtcSpl_AddSatW16(state->counter, 1)); // update long-term estimate of standard deviation in energy level (Q10) tmp32 = WEBRTC_SPL_MUL_16_16(state->meanLongTerm, state->meanLongTerm); - tmp32 = WEBRTC_SPL_LSHIFT_W32(state->varianceLongTerm, 12) - tmp32; + tmp32 = (state->varianceLongTerm << 12) - tmp32; state->stdLongTerm = (int16_t)WebRtcSpl_Sqrt(tmp32); // update voice activity measure (Q10) - tmp16 = WEBRTC_SPL_LSHIFT_W16(3, 12); + tmp16 = 3 << 12; tmp32 = WEBRTC_SPL_MUL_16_16(tmp16, (dB - state->meanLongTerm)); tmp32 = WebRtcSpl_DivW32W16(tmp32, state->stdLongTerm); - tmpU16 = WEBRTC_SPL_LSHIFT_U16((uint16_t)13, 12); + tmpU16 = (13 << 12); tmp32b = WEBRTC_SPL_MUL_16_U16(state->logRatio, tmpU16); - tmp32 += WEBRTC_SPL_RSHIFT_W32(tmp32b, 10); + tmp32 += tmp32b >> 10; - state->logRatio = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 6); + state->logRatio = (int16_t)(tmp32 >> 6); // limit if (state->logRatio > 2048) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/digital_agc.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/digital_agc.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/digital_agc.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/agc/digital_agc.h 2015-02-03 14:33:35.000000000 +0000 @@ -11,7 +11,7 @@ #ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_SOURCE_DIGITAL_AGC_H_ #define WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_SOURCE_DIGITAL_AGC_H_ -#ifdef AGC_DEBUG +#ifdef WEBRTC_AGC_DEBUG_DUMP #include #endif #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" @@ -46,9 +46,9 @@ int16_t agcMode; AgcVad_t vadNearend; AgcVad_t vadFarend; -#ifdef AGC_DEBUG - FILE* logFile; - int frameCounter; +#ifdef WEBRTC_AGC_DEBUG_DUMP + FILE* logFile; + int frameCounter; #endif } DigitalAgc_t; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,153 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE := libwebrtc_apm -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := \ - $(call all-proto-files-under, .) \ - audio_buffer.cc \ - audio_processing_impl.cc \ - echo_cancellation_impl.cc \ - echo_control_mobile_impl.cc \ - gain_control_impl.cc \ - high_pass_filter_impl.cc \ - level_estimator_impl.cc \ - noise_suppression_impl.cc \ - splitting_filter.cc \ - processing_component.cc \ - voice_detection_impl.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) \ - '-DWEBRTC_NS_FIXED' \ - '-DWEBRTC_ANDROID_PLATFORM_BUILD' \ - '-DWEBRTC_AUDIOPROC_DEBUG_DUMP' -# floating point -# -DWEBRTC_NS_FLOAT' - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/aec/include \ - $(LOCAL_PATH)/aecm/include \ - $(LOCAL_PATH)/agc/include \ - $(LOCAL_PATH)/ns/include \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../.. \ - $(LOCAL_PATH)/../../common_audio/signal_processing/include \ - $(LOCAL_PATH)/../../common_audio/vad/include \ - $(LOCAL_PATH)/../../system_wrappers/interface \ - external/protobuf/src \ - external/webrtc - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) - -# apm process test app - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES:= \ - $(call all-proto-files-under, .) \ - test/process_test.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) \ - '-DWEBRTC_ANDROID_PLATFORM_BUILD' \ - '-DWEBRTC_AUDIOPROC_DEBUG_DUMP' - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../.. \ - $(LOCAL_PATH)/../../system_wrappers/interface \ - external/gtest/include \ - external/webrtc - -LOCAL_STATIC_LIBRARIES := \ - libgtest \ - libwebrtc_test_support \ - libprotobuf-cpp-2.3.0-lite - -LOCAL_SHARED_LIBRARIES := \ - libutils \ - libstlport \ - libwebrtc_audio_preprocessing - -LOCAL_MODULE:= webrtc_audioproc - -ifdef NDK_ROOT -include $(BUILD_EXECUTABLE) -else -include external/stlport/libstlport.mk -include $(BUILD_NATIVE_TEST) -endif - -# apm unit test app - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES:= \ - $(call all-proto-files-under, test) \ - test/audio_processing_unittest.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) \ - '-DWEBRTC_AUDIOPROC_FIXED_PROFILE' \ - '-DWEBRTC_ANDROID_PLATFORM_BUILD' \ - '-DWEBRTC_AUDIOPROC_DEBUG_DUMP' - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../.. \ - $(LOCAL_PATH)/../../../test \ - $(LOCAL_PATH)/../../system_wrappers/interface \ - $(LOCAL_PATH)/../../common_audio/signal_processing/include \ - external/gtest/include \ - external/protobuf/src \ - external/webrtc - -LOCAL_STATIC_LIBRARIES := \ - libgtest \ - libwebrtc_test_support \ - libprotobuf-cpp-2.3.0-lite - -LOCAL_SHARED_LIBRARIES := \ - libstlport \ - libwebrtc_audio_preprocessing - -LOCAL_MODULE:= webrtc_audioproc_unittest - -ifdef NDK_ROOT -include $(BUILD_EXECUTABLE) -else -include external/stlport/libstlport.mk -include $(BUILD_NATIVE_TEST) -endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_buffer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_buffer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_buffer.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_buffer.cc 2015-02-03 14:33:35.000000000 +0000 @@ -10,6 +10,8 @@ #include "webrtc/modules/audio_processing/audio_buffer.h" +#include "webrtc/common_audio/include/audio_util.h" +#include "webrtc/common_audio/resampler/push_sinc_resampler.h" #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" namespace webrtc { @@ -21,145 +23,379 @@ kSamplesPer32kHzChannel = 320 }; -void StereoToMono(const int16_t* left, const int16_t* right, - int16_t* out, int samples_per_channel) { - assert(left != NULL && right != NULL && out != NULL); - for (int i = 0; i < samples_per_channel; i++) { - int32_t data32 = (static_cast(left[i]) + - static_cast(right[i])) >> 1; - - out[i] = WebRtcSpl_SatW32ToW16(data32); - } +bool HasKeyboardChannel(AudioProcessing::ChannelLayout layout) { + switch (layout) { + case AudioProcessing::kMono: + case AudioProcessing::kStereo: + return false; + case AudioProcessing::kMonoAndKeyboard: + case AudioProcessing::kStereoAndKeyboard: + return true; + } + assert(false); + return false; +} + +int KeyboardChannelIndex(AudioProcessing::ChannelLayout layout) { + switch (layout) { + case AudioProcessing::kMono: + case AudioProcessing::kStereo: + assert(false); + return -1; + case AudioProcessing::kMonoAndKeyboard: + return 1; + case AudioProcessing::kStereoAndKeyboard: + return 2; + } + assert(false); + return -1; +} + +template +void StereoToMono(const T* left, const T* right, T* out, + int samples_per_channel) { + for (int i = 0; i < samples_per_channel; ++i) + out[i] = (left[i] + right[i]) / 2; } + } // namespace -struct AudioChannel { - AudioChannel() { - memset(data, 0, sizeof(data)); +// One int16_t and one float ChannelBuffer that are kept in sync. The sync is +// broken when someone requests write access to either ChannelBuffer, and +// reestablished when someone requests the outdated ChannelBuffer. It is +// therefore safe to use the return value of ibuf_const() and fbuf_const() +// until the next call to ibuf() or fbuf(), and the return value of ibuf() and +// fbuf() until the next call to any of the other functions. +class IFChannelBuffer { + public: + IFChannelBuffer(int samples_per_channel, int num_channels) + : ivalid_(true), + ibuf_(samples_per_channel, num_channels), + fvalid_(true), + fbuf_(samples_per_channel, num_channels) {} + + ChannelBuffer* ibuf() { return ibuf(false); } + ChannelBuffer* fbuf() { return fbuf(false); } + const ChannelBuffer* ibuf_const() { return ibuf(true); } + const ChannelBuffer* fbuf_const() { return fbuf(true); } + + private: + ChannelBuffer* ibuf(bool readonly) { + RefreshI(); + fvalid_ = readonly; + return &ibuf_; + } + + ChannelBuffer* fbuf(bool readonly) { + RefreshF(); + ivalid_ = readonly; + return &fbuf_; + } + + void RefreshF() { + if (!fvalid_) { + assert(ivalid_); + const int16_t* const int_data = ibuf_.data(); + float* const float_data = fbuf_.data(); + const int length = fbuf_.length(); + for (int i = 0; i < length; ++i) + float_data[i] = int_data[i]; + fvalid_ = true; + } } - int16_t data[kSamplesPer32kHzChannel]; -}; + void RefreshI() { + if (!ivalid_) { + assert(fvalid_); + FloatS16ToS16(fbuf_.data(), ibuf_.length(), ibuf_.data()); + ivalid_ = true; + } + } -struct SplitAudioChannel { - SplitAudioChannel() { - memset(low_pass_data, 0, sizeof(low_pass_data)); - memset(high_pass_data, 0, sizeof(high_pass_data)); - memset(analysis_filter_state1, 0, sizeof(analysis_filter_state1)); - memset(analysis_filter_state2, 0, sizeof(analysis_filter_state2)); - memset(synthesis_filter_state1, 0, sizeof(synthesis_filter_state1)); - memset(synthesis_filter_state2, 0, sizeof(synthesis_filter_state2)); - } - - int16_t low_pass_data[kSamplesPer16kHzChannel]; - int16_t high_pass_data[kSamplesPer16kHzChannel]; - - int32_t analysis_filter_state1[6]; - int32_t analysis_filter_state2[6]; - int32_t synthesis_filter_state1[6]; - int32_t synthesis_filter_state2[6]; + bool ivalid_; + ChannelBuffer ibuf_; + bool fvalid_; + ChannelBuffer fbuf_; }; -// TODO(andrew): check range of input parameters? -AudioBuffer::AudioBuffer(int max_num_channels, - int samples_per_channel) - : max_num_channels_(max_num_channels), - num_channels_(0), - num_mixed_channels_(0), - num_mixed_low_pass_channels_(0), - data_was_mixed_(false), - samples_per_channel_(samples_per_channel), - samples_per_split_channel_(samples_per_channel), +AudioBuffer::AudioBuffer(int input_samples_per_channel, + int num_input_channels, + int process_samples_per_channel, + int num_process_channels, + int output_samples_per_channel) + : input_samples_per_channel_(input_samples_per_channel), + num_input_channels_(num_input_channels), + proc_samples_per_channel_(process_samples_per_channel), + num_proc_channels_(num_process_channels), + output_samples_per_channel_(output_samples_per_channel), + samples_per_split_channel_(proc_samples_per_channel_), + mixed_low_pass_valid_(false), reference_copied_(false), activity_(AudioFrame::kVadUnknown), - is_muted_(false), - data_(NULL), - channels_(NULL), - split_channels_(NULL), - mixed_channels_(NULL), - mixed_low_pass_channels_(NULL), - low_pass_reference_channels_(NULL) { - if (max_num_channels_ > 1) { - channels_.reset(new AudioChannel[max_num_channels_]); - mixed_channels_.reset(new AudioChannel[max_num_channels_]); - mixed_low_pass_channels_.reset(new AudioChannel[max_num_channels_]); + keyboard_data_(NULL), + channels_(new IFChannelBuffer(proc_samples_per_channel_, + num_proc_channels_)) { + assert(input_samples_per_channel_ > 0); + assert(proc_samples_per_channel_ > 0); + assert(output_samples_per_channel_ > 0); + assert(num_input_channels_ > 0 && num_input_channels_ <= 2); + assert(num_proc_channels_ <= num_input_channels); + + if (num_input_channels_ == 2 && num_proc_channels_ == 1) { + input_buffer_.reset(new ChannelBuffer(input_samples_per_channel_, + num_proc_channels_)); + } + + if (input_samples_per_channel_ != proc_samples_per_channel_ || + output_samples_per_channel_ != proc_samples_per_channel_) { + // Create an intermediate buffer for resampling. + process_buffer_.reset(new ChannelBuffer(proc_samples_per_channel_, + num_proc_channels_)); + } + + if (input_samples_per_channel_ != proc_samples_per_channel_) { + input_resamplers_.reserve(num_proc_channels_); + for (int i = 0; i < num_proc_channels_; ++i) { + input_resamplers_.push_back( + new PushSincResampler(input_samples_per_channel_, + proc_samples_per_channel_)); + } + } + + if (output_samples_per_channel_ != proc_samples_per_channel_) { + output_resamplers_.reserve(num_proc_channels_); + for (int i = 0; i < num_proc_channels_; ++i) { + output_resamplers_.push_back( + new PushSincResampler(proc_samples_per_channel_, + output_samples_per_channel_)); + } } - low_pass_reference_channels_.reset(new AudioChannel[max_num_channels_]); - if (samples_per_channel_ == kSamplesPer32kHzChannel) { - split_channels_.reset(new SplitAudioChannel[max_num_channels_]); + if (proc_samples_per_channel_ == kSamplesPer32kHzChannel) { samples_per_split_channel_ = kSamplesPer16kHzChannel; + split_channels_low_.reset(new IFChannelBuffer(samples_per_split_channel_, + num_proc_channels_)); + split_channels_high_.reset(new IFChannelBuffer(samples_per_split_channel_, + num_proc_channels_)); + filter_states_.reset(new SplitFilterStates[num_proc_channels_]); } } AudioBuffer::~AudioBuffer() {} -int16_t* AudioBuffer::data(int channel) const { - assert(channel >= 0 && channel < num_channels_); - if (data_ != NULL) { - return data_; +void AudioBuffer::CopyFrom(const float* const* data, + int samples_per_channel, + AudioProcessing::ChannelLayout layout) { + assert(samples_per_channel == input_samples_per_channel_); + assert(ChannelsFromLayout(layout) == num_input_channels_); + InitForNewData(); + + if (HasKeyboardChannel(layout)) { + keyboard_data_ = data[KeyboardChannelIndex(layout)]; + } + + // Downmix. + const float* const* data_ptr = data; + if (num_input_channels_ == 2 && num_proc_channels_ == 1) { + StereoToMono(data[0], + data[1], + input_buffer_->channel(0), + input_samples_per_channel_); + data_ptr = input_buffer_->channels(); + } + + // Resample. + if (input_samples_per_channel_ != proc_samples_per_channel_) { + for (int i = 0; i < num_proc_channels_; ++i) { + input_resamplers_[i]->Resample(data_ptr[i], + input_samples_per_channel_, + process_buffer_->channel(i), + proc_samples_per_channel_); + } + data_ptr = process_buffer_->channels(); } - return channels_[channel].data; + // Convert to the S16 range. + for (int i = 0; i < num_proc_channels_; ++i) { + FloatToFloatS16(data_ptr[i], proc_samples_per_channel_, + channels_->fbuf()->channel(i)); + } } -int16_t* AudioBuffer::low_pass_split_data(int channel) const { - assert(channel >= 0 && channel < num_channels_); - if (split_channels_.get() == NULL) { - return data(channel); +void AudioBuffer::CopyTo(int samples_per_channel, + AudioProcessing::ChannelLayout layout, + float* const* data) { + assert(samples_per_channel == output_samples_per_channel_); + assert(ChannelsFromLayout(layout) == num_proc_channels_); + + // Convert to the float range. + float* const* data_ptr = data; + if (output_samples_per_channel_ != proc_samples_per_channel_) { + // Convert to an intermediate buffer for subsequent resampling. + data_ptr = process_buffer_->channels(); + } + for (int i = 0; i < num_proc_channels_; ++i) { + FloatS16ToFloat(channels_->fbuf()->channel(i), proc_samples_per_channel_, + data_ptr[i]); } - return split_channels_[channel].low_pass_data; + // Resample. + if (output_samples_per_channel_ != proc_samples_per_channel_) { + for (int i = 0; i < num_proc_channels_; ++i) { + output_resamplers_[i]->Resample(data_ptr[i], + proc_samples_per_channel_, + data[i], + output_samples_per_channel_); + } + } } -int16_t* AudioBuffer::high_pass_split_data(int channel) const { - assert(channel >= 0 && channel < num_channels_); - if (split_channels_.get() == NULL) { - return NULL; - } +void AudioBuffer::InitForNewData() { + keyboard_data_ = NULL; + mixed_low_pass_valid_ = false; + reference_copied_ = false; + activity_ = AudioFrame::kVadUnknown; +} - return split_channels_[channel].high_pass_data; +const int16_t* AudioBuffer::data(int channel) const { + return channels_->ibuf_const()->channel(channel); } -int16_t* AudioBuffer::mixed_data(int channel) const { - assert(channel >= 0 && channel < num_mixed_channels_); +int16_t* AudioBuffer::data(int channel) { + mixed_low_pass_valid_ = false; + return channels_->ibuf()->channel(channel); +} - return mixed_channels_[channel].data; +const float* AudioBuffer::data_f(int channel) const { + return channels_->fbuf_const()->channel(channel); } -int16_t* AudioBuffer::mixed_low_pass_data(int channel) const { - assert(channel >= 0 && channel < num_mixed_low_pass_channels_); +float* AudioBuffer::data_f(int channel) { + mixed_low_pass_valid_ = false; + return channels_->fbuf()->channel(channel); +} - return mixed_low_pass_channels_[channel].data; +const float* const* AudioBuffer::channels_f() const { + return channels_->fbuf_const()->channels(); } -int16_t* AudioBuffer::low_pass_reference(int channel) const { - assert(channel >= 0 && channel < num_channels_); - if (!reference_copied_) { - return NULL; - } +float* const* AudioBuffer::channels_f() { + mixed_low_pass_valid_ = false; + return channels_->fbuf()->channels(); +} + +const int16_t* AudioBuffer::low_pass_split_data(int channel) const { + return split_channels_low_.get() + ? split_channels_low_->ibuf_const()->channel(channel) + : data(channel); +} + +int16_t* AudioBuffer::low_pass_split_data(int channel) { + mixed_low_pass_valid_ = false; + return split_channels_low_.get() + ? split_channels_low_->ibuf()->channel(channel) + : data(channel); +} + +const float* AudioBuffer::low_pass_split_data_f(int channel) const { + return split_channels_low_.get() + ? split_channels_low_->fbuf_const()->channel(channel) + : data_f(channel); +} + +float* AudioBuffer::low_pass_split_data_f(int channel) { + mixed_low_pass_valid_ = false; + return split_channels_low_.get() + ? split_channels_low_->fbuf()->channel(channel) + : data_f(channel); +} + +const float* const* AudioBuffer::low_pass_split_channels_f() const { + return split_channels_low_.get() + ? split_channels_low_->fbuf_const()->channels() + : channels_f(); +} + +float* const* AudioBuffer::low_pass_split_channels_f() { + mixed_low_pass_valid_ = false; + return split_channels_low_.get() + ? split_channels_low_->fbuf()->channels() + : channels_f(); +} + +const int16_t* AudioBuffer::high_pass_split_data(int channel) const { + return split_channels_high_.get() + ? split_channels_high_->ibuf_const()->channel(channel) + : NULL; +} - return low_pass_reference_channels_[channel].data; +int16_t* AudioBuffer::high_pass_split_data(int channel) { + return split_channels_high_.get() + ? split_channels_high_->ibuf()->channel(channel) + : NULL; } -int32_t* AudioBuffer::analysis_filter_state1(int channel) const { - assert(channel >= 0 && channel < num_channels_); - return split_channels_[channel].analysis_filter_state1; +const float* AudioBuffer::high_pass_split_data_f(int channel) const { + return split_channels_high_.get() + ? split_channels_high_->fbuf_const()->channel(channel) + : NULL; } -int32_t* AudioBuffer::analysis_filter_state2(int channel) const { - assert(channel >= 0 && channel < num_channels_); - return split_channels_[channel].analysis_filter_state2; +float* AudioBuffer::high_pass_split_data_f(int channel) { + return split_channels_high_.get() + ? split_channels_high_->fbuf()->channel(channel) + : NULL; } -int32_t* AudioBuffer::synthesis_filter_state1(int channel) const { - assert(channel >= 0 && channel < num_channels_); - return split_channels_[channel].synthesis_filter_state1; +const float* const* AudioBuffer::high_pass_split_channels_f() const { + return split_channels_high_.get() + ? split_channels_high_->fbuf_const()->channels() + : NULL; } -int32_t* AudioBuffer::synthesis_filter_state2(int channel) const { - assert(channel >= 0 && channel < num_channels_); - return split_channels_[channel].synthesis_filter_state2; +float* const* AudioBuffer::high_pass_split_channels_f() { + return split_channels_high_.get() + ? split_channels_high_->fbuf()->channels() + : NULL; +} + +const int16_t* AudioBuffer::mixed_low_pass_data() { + // Currently only mixing stereo to mono is supported. + assert(num_proc_channels_ == 1 || num_proc_channels_ == 2); + + if (num_proc_channels_ == 1) { + return low_pass_split_data(0); + } + + if (!mixed_low_pass_valid_) { + if (!mixed_low_pass_channels_.get()) { + mixed_low_pass_channels_.reset( + new ChannelBuffer(samples_per_split_channel_, 1)); + } + StereoToMono(low_pass_split_data(0), + low_pass_split_data(1), + mixed_low_pass_channels_->data(), + samples_per_split_channel_); + mixed_low_pass_valid_ = true; + } + return mixed_low_pass_channels_->data(); +} + +const int16_t* AudioBuffer::low_pass_reference(int channel) const { + if (!reference_copied_) { + return NULL; + } + + return low_pass_reference_channels_->channel(channel); +} + +const float* AudioBuffer::keyboard_data() const { + return keyboard_data_; +} + +SplitFilterStates* AudioBuffer::filter_states(int channel) { + assert(channel >= 0 && channel < num_proc_channels_); + return &filter_states_[channel]; } void AudioBuffer::set_activity(AudioFrame::VADActivity activity) { @@ -170,137 +406,83 @@ return activity_; } -bool AudioBuffer::is_muted() const { - return is_muted_; -} - int AudioBuffer::num_channels() const { - return num_channels_; + return num_proc_channels_; } int AudioBuffer::samples_per_channel() const { - return samples_per_channel_; + return proc_samples_per_channel_; } int AudioBuffer::samples_per_split_channel() const { return samples_per_split_channel_; } +int AudioBuffer::samples_per_keyboard_channel() const { + // We don't resample the keyboard channel. + return input_samples_per_channel_; +} + // TODO(andrew): Do deinterleaving and mixing in one step? void AudioBuffer::DeinterleaveFrom(AudioFrame* frame) { - assert(frame->num_channels_ <= max_num_channels_); - assert(frame->samples_per_channel_ == samples_per_channel_); - - num_channels_ = frame->num_channels_; - data_was_mixed_ = false; - num_mixed_channels_ = 0; - num_mixed_low_pass_channels_ = 0; - reference_copied_ = false; + assert(proc_samples_per_channel_ == input_samples_per_channel_); + assert(frame->num_channels_ == num_input_channels_); + assert(frame->samples_per_channel_ == proc_samples_per_channel_); + InitForNewData(); activity_ = frame->vad_activity_; - is_muted_ = false; - if (frame->energy_ == 0) { - is_muted_ = true; - } - - if (num_channels_ == 1) { - // We can get away with a pointer assignment in this case. - data_ = frame->data_; - return; - } - int16_t* interleaved = frame->data_; - for (int i = 0; i < num_channels_; i++) { - int16_t* deinterleaved = channels_[i].data; - int interleaved_idx = i; - for (int j = 0; j < samples_per_channel_; j++) { - deinterleaved[j] = interleaved[interleaved_idx]; - interleaved_idx += num_channels_; + if (num_input_channels_ == 2 && num_proc_channels_ == 1) { + // Downmix directly; no explicit deinterleaving needed. + int16_t* downmixed = channels_->ibuf()->channel(0); + for (int i = 0; i < input_samples_per_channel_; ++i) { + downmixed[i] = (frame->data_[i * 2] + frame->data_[i * 2 + 1]) / 2; + } + } else { + assert(num_proc_channels_ == num_input_channels_); + int16_t* interleaved = frame->data_; + for (int i = 0; i < num_proc_channels_; ++i) { + int16_t* deinterleaved = channels_->ibuf()->channel(i); + int interleaved_idx = i; + for (int j = 0; j < proc_samples_per_channel_; ++j) { + deinterleaved[j] = interleaved[interleaved_idx]; + interleaved_idx += num_proc_channels_; + } } } } void AudioBuffer::InterleaveTo(AudioFrame* frame, bool data_changed) const { - assert(frame->num_channels_ == num_channels_); - assert(frame->samples_per_channel_ == samples_per_channel_); + assert(proc_samples_per_channel_ == output_samples_per_channel_); + assert(num_proc_channels_ == num_input_channels_); + assert(frame->num_channels_ == num_proc_channels_); + assert(frame->samples_per_channel_ == proc_samples_per_channel_); frame->vad_activity_ = activity_; if (!data_changed) { return; } - if (num_channels_ == 1) { - if (data_was_mixed_) { - memcpy(frame->data_, - channels_[0].data, - sizeof(int16_t) * samples_per_channel_); - } else { - // These should point to the same buffer in this case. - assert(data_ == frame->data_); - } - - return; - } - int16_t* interleaved = frame->data_; - for (int i = 0; i < num_channels_; i++) { - int16_t* deinterleaved = channels_[i].data; + for (int i = 0; i < num_proc_channels_; i++) { + int16_t* deinterleaved = channels_->ibuf()->channel(i); int interleaved_idx = i; - for (int j = 0; j < samples_per_channel_; j++) { + for (int j = 0; j < proc_samples_per_channel_; j++) { interleaved[interleaved_idx] = deinterleaved[j]; - interleaved_idx += num_channels_; + interleaved_idx += num_proc_channels_; } } } -// TODO(andrew): would be good to support the no-mix case with pointer -// assignment. -// TODO(andrew): handle mixing to multiple channels? -void AudioBuffer::Mix(int num_mixed_channels) { - // We currently only support the stereo to mono case. - assert(num_channels_ == 2); - assert(num_mixed_channels == 1); - - StereoToMono(channels_[0].data, - channels_[1].data, - channels_[0].data, - samples_per_channel_); - - num_channels_ = num_mixed_channels; - data_was_mixed_ = true; -} - -void AudioBuffer::CopyAndMix(int num_mixed_channels) { - // We currently only support the stereo to mono case. - assert(num_channels_ == 2); - assert(num_mixed_channels == 1); - - StereoToMono(channels_[0].data, - channels_[1].data, - mixed_channels_[0].data, - samples_per_channel_); - - num_mixed_channels_ = num_mixed_channels; -} - -void AudioBuffer::CopyAndMixLowPass(int num_mixed_channels) { - // We currently only support the stereo to mono case. - assert(num_channels_ == 2); - assert(num_mixed_channels == 1); - - StereoToMono(low_pass_split_data(0), - low_pass_split_data(1), - mixed_low_pass_channels_[0].data, - samples_per_split_channel_); - - num_mixed_low_pass_channels_ = num_mixed_channels; -} - void AudioBuffer::CopyLowPassToReference() { reference_copied_ = true; - for (int i = 0; i < num_channels_; i++) { - memcpy(low_pass_reference_channels_[i].data, - low_pass_split_data(i), - sizeof(int16_t) * samples_per_split_channel_); + if (!low_pass_reference_channels_.get()) { + low_pass_reference_channels_.reset( + new ChannelBuffer(samples_per_split_channel_, + num_proc_channels_)); + } + for (int i = 0; i < num_proc_channels_; i++) { + low_pass_reference_channels_->CopyFrom(low_pass_split_data(i), i); } } + } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_buffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_buffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_buffer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_buffer.h 2015-02-03 14:33:35.000000000 +0000 @@ -8,75 +8,134 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_BUFFER_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_BUFFER_H_ +#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AUDIO_BUFFER_H_ +#define WEBRTC_MODULES_AUDIO_PROCESSING_AUDIO_BUFFER_H_ +#include + +#include "webrtc/modules/audio_processing/common.h" +#include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/modules/interface/module_common_types.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/system_wrappers/interface/scoped_vector.h" #include "webrtc/typedefs.h" namespace webrtc { -struct AudioChannel; -struct SplitAudioChannel; +class PushSincResampler; +class IFChannelBuffer; + +struct SplitFilterStates { + SplitFilterStates() { + memset(analysis_filter_state1, 0, sizeof(analysis_filter_state1)); + memset(analysis_filter_state2, 0, sizeof(analysis_filter_state2)); + memset(synthesis_filter_state1, 0, sizeof(synthesis_filter_state1)); + memset(synthesis_filter_state2, 0, sizeof(synthesis_filter_state2)); + } + + static const int kStateSize = 6; + int analysis_filter_state1[kStateSize]; + int analysis_filter_state2[kStateSize]; + int synthesis_filter_state1[kStateSize]; + int synthesis_filter_state2[kStateSize]; +}; class AudioBuffer { public: - AudioBuffer(int max_num_channels, int samples_per_channel); + // TODO(ajm): Switch to take ChannelLayouts. + AudioBuffer(int input_samples_per_channel, + int num_input_channels, + int process_samples_per_channel, + int num_process_channels, + int output_samples_per_channel); virtual ~AudioBuffer(); int num_channels() const; int samples_per_channel() const; int samples_per_split_channel() const; + int samples_per_keyboard_channel() const; + + // Sample array accessors. Channels are guaranteed to be stored contiguously + // in memory. Prefer to use the const variants of each accessor when + // possible, since they incur less float<->int16 conversion overhead. + int16_t* data(int channel); + const int16_t* data(int channel) const; + int16_t* low_pass_split_data(int channel); + const int16_t* low_pass_split_data(int channel) const; + int16_t* high_pass_split_data(int channel); + const int16_t* high_pass_split_data(int channel) const;\ + // Returns a pointer to the low-pass data downmixed to mono. If this data + // isn't already available it re-calculates it. + const int16_t* mixed_low_pass_data(); + const int16_t* low_pass_reference(int channel) const; + + // Float versions of the accessors, with automatic conversion back and forth + // as necessary. The range of the numbers are the same as for int16_t. + float* data_f(int channel); + const float* data_f(int channel) const; + + float* const* channels_f(); + const float* const* channels_f() const; + + float* low_pass_split_data_f(int channel); + const float* low_pass_split_data_f(int channel) const; + float* high_pass_split_data_f(int channel); + const float* high_pass_split_data_f(int channel) const; + + float* const* low_pass_split_channels_f(); + const float* const* low_pass_split_channels_f() const; + float* const* high_pass_split_channels_f(); + const float* const* high_pass_split_channels_f() const; + + const float* keyboard_data() const; - int16_t* data(int channel) const; - int16_t* low_pass_split_data(int channel) const; - int16_t* high_pass_split_data(int channel) const; - int16_t* mixed_data(int channel) const; - int16_t* mixed_low_pass_data(int channel) const; - int16_t* low_pass_reference(int channel) const; - - int32_t* analysis_filter_state1(int channel) const; - int32_t* analysis_filter_state2(int channel) const; - int32_t* synthesis_filter_state1(int channel) const; - int32_t* synthesis_filter_state2(int channel) const; + SplitFilterStates* filter_states(int channel); void set_activity(AudioFrame::VADActivity activity); AudioFrame::VADActivity activity() const; - bool is_muted() const; - + // Use for int16 interleaved data. void DeinterleaveFrom(AudioFrame* audioFrame); - void InterleaveTo(AudioFrame* audioFrame) const; // If |data_changed| is false, only the non-audio data members will be copied // to |frame|. void InterleaveTo(AudioFrame* frame, bool data_changed) const; - void Mix(int num_mixed_channels); - void CopyAndMix(int num_mixed_channels); - void CopyAndMixLowPass(int num_mixed_channels); + + // Use for float deinterleaved data. + void CopyFrom(const float* const* data, + int samples_per_channel, + AudioProcessing::ChannelLayout layout); + void CopyTo(int samples_per_channel, + AudioProcessing::ChannelLayout layout, + float* const* data); void CopyLowPassToReference(); private: - const int max_num_channels_; - int num_channels_; - int num_mixed_channels_; - int num_mixed_low_pass_channels_; - // Whether the original data was replaced with mixed data. - bool data_was_mixed_; - const int samples_per_channel_; + // Called from DeinterleaveFrom() and CopyFrom(). + void InitForNewData(); + + const int input_samples_per_channel_; + const int num_input_channels_; + const int proc_samples_per_channel_; + const int num_proc_channels_; + const int output_samples_per_channel_; int samples_per_split_channel_; + bool mixed_low_pass_valid_; bool reference_copied_; AudioFrame::VADActivity activity_; - bool is_muted_; - int16_t* data_; - scoped_array channels_; - scoped_array split_channels_; - scoped_array mixed_channels_; - // TODO(andrew): improve this, we don't need the full 32 kHz space here. - scoped_array mixed_low_pass_channels_; - scoped_array low_pass_reference_channels_; + const float* keyboard_data_; + scoped_ptr channels_; + scoped_ptr split_channels_low_; + scoped_ptr split_channels_high_; + scoped_ptr filter_states_; + scoped_ptr > mixed_low_pass_channels_; + scoped_ptr > low_pass_reference_channels_; + scoped_ptr > input_buffer_; + scoped_ptr > process_buffer_; + ScopedVector input_resamplers_; + ScopedVector output_resamplers_; }; + } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_BUFFER_H_ +#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AUDIO_BUFFER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing.gypi 2015-02-03 14:33:35.000000000 +0000 @@ -9,6 +9,7 @@ { 'variables': { 'audio_processing_dependencies': [ + '<(webrtc_root)/base/base.gyp:rtc_base_approved', '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', ], @@ -21,6 +22,7 @@ 'variables': { # Outputs some low-level debug files. 'aec_debug_dump%': 0, + 'agc_debug_dump%': 0, # Disables the usual mode where we trust the reported system delay # values the AEC receives. The corresponding define is set appropriately @@ -54,9 +56,9 @@ 'audio_buffer.h', 'audio_processing_impl.cc', 'audio_processing_impl.h', + 'common.h', 'echo_cancellation_impl.cc', 'echo_cancellation_impl.h', - 'echo_cancellation_impl_wrapper.h', 'echo_control_mobile_impl.cc', 'echo_control_mobile_impl.h', 'gain_control_impl.cc', @@ -70,6 +72,8 @@ 'noise_suppression_impl.h', 'processing_component.cc', 'processing_component.h', + 'rms_level.cc', + 'rms_level.h', 'typing_detection.cc', 'typing_detection.h', 'utility/delay_estimator.c', @@ -91,6 +95,9 @@ ['aec_untrusted_delay_for_testing==1', { 'defines': ['WEBRTC_UNTRUSTED_DELAY',], }], + ['agc_debug_dump==1', { + 'defines': ['WEBRTC_AGC_DEBUG_DUMP',], + }], ['enable_protobuf==1', { 'dependencies': ['audioproc_debug_proto'], 'defines': ['WEBRTC_AUDIOPROC_DEBUG_DUMP'], @@ -105,7 +112,7 @@ 'ns/nsx_defines.h', ], 'conditions': [ - ['target_arch=="mipsel"', { + ['target_arch=="mipsel" and mips_arch_variant!="r6" and android_webview_build==0', { 'sources': [ 'ns/nsx_core_mips.c', ], @@ -132,10 +139,18 @@ ['(target_arch=="arm" and arm_version==7) or target_arch=="armv7"', { 'dependencies': ['audio_processing_neon',], }], - ['target_arch=="mipsel"', { + ['target_arch=="mipsel" and mips_arch_variant!="r6" and android_webview_build==0', { 'sources': [ 'aecm/aecm_core_mips.c', ], + 'conditions': [ + ['mips_fpu==1', { + 'sources': [ + 'aec/aec_core_mips.c', + 'aec/aec_rdft_mips.c', + ], + }], + ], }, { 'sources': [ 'aecm/aecm_core_c.c', @@ -194,6 +209,8 @@ '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', ], 'sources': [ + 'aec/aec_core_neon.c', + 'aec/aec_rdft_neon.c', 'aecm/aecm_core_neon.c', 'ns/nsx_core_neon.c', ], @@ -219,6 +236,13 @@ ], 'includes!': ['../../build/arm_neon.gypi',], }], + # Disable LTO in audio_processing_neon target due to compiler bug + ['use_lto==1', { + 'cflags!': [ + '-flto', + '-ffat-lto-objects', + ], + }], ], }], }], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing_impl.cc 2015-02-03 14:33:35.000000000 +0000 @@ -12,9 +12,12 @@ #include +#include "webrtc/base/platform_file.h" +#include "webrtc/common_audio/include/audio_util.h" #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" #include "webrtc/modules/audio_processing/audio_buffer.h" -#include "webrtc/modules/audio_processing/echo_cancellation_impl_wrapper.h" +#include "webrtc/modules/audio_processing/common.h" +#include "webrtc/modules/audio_processing/echo_cancellation_impl.h" #include "webrtc/modules/audio_processing/echo_control_mobile_impl.h" #include "webrtc/modules/audio_processing/gain_control_impl.h" #include "webrtc/modules/audio_processing/high_pass_filter_impl.h" @@ -37,8 +40,6 @@ #endif #endif // WEBRTC_AUDIOPROC_DEBUG_DUMP -static const int kChunkSizeMs = 10; - #define RETURN_ON_ERR(expr) \ do { \ int err = expr; \ @@ -80,42 +81,40 @@ noise_suppression_(NULL), voice_detection_(NULL), crit_(CriticalSectionWrapper::CreateCriticalSection()), - render_audio_(NULL), - capture_audio_(NULL), #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP debug_file_(FileWrapper::Create()), event_msg_(new audioproc::Event()), #endif - sample_rate_hz_(kSampleRate16kHz), - split_sample_rate_hz_(kSampleRate16kHz), - samples_per_channel_(kChunkSizeMs * sample_rate_hz_ / 1000), + fwd_in_format_(kSampleRate16kHz, 1), + fwd_proc_format_(kSampleRate16kHz, 1), + fwd_out_format_(kSampleRate16kHz), + rev_in_format_(kSampleRate16kHz, 1), + rev_proc_format_(kSampleRate16kHz, 1), + split_rate_(kSampleRate16kHz), stream_delay_ms_(0), delay_offset_ms_(0), was_stream_delay_set_(false), - num_reverse_channels_(1), - num_input_channels_(1), - num_output_channels_(1), output_will_be_muted_(false), key_pressed_(false) { - echo_cancellation_ = EchoCancellationImplWrapper::Create(this); + echo_cancellation_ = new EchoCancellationImpl(this, crit_); component_list_.push_back(echo_cancellation_); - echo_control_mobile_ = new EchoControlMobileImpl(this); + echo_control_mobile_ = new EchoControlMobileImpl(this, crit_); component_list_.push_back(echo_control_mobile_); - gain_control_ = new GainControlImpl(this); + gain_control_ = new GainControlImpl(this, crit_); component_list_.push_back(gain_control_); - high_pass_filter_ = new HighPassFilterImpl(this); + high_pass_filter_ = new HighPassFilterImpl(this, crit_); component_list_.push_back(high_pass_filter_); - level_estimator_ = new LevelEstimatorImpl(this); + level_estimator_ = new LevelEstimatorImpl(this, crit_); component_list_.push_back(level_estimator_); - noise_suppression_ = new NoiseSuppressionImpl(this); + noise_suppression_ = new NoiseSuppressionImpl(this, crit_); component_list_.push_back(noise_suppression_); - voice_detection_ = new VoiceDetectionImpl(this); + voice_detection_ = new VoiceDetectionImpl(this, crit_); component_list_.push_back(voice_detection_); SetExtraOptions(config); @@ -136,50 +135,52 @@ debug_file_->CloseFile(); } #endif - - if (render_audio_) { - delete render_audio_; - render_audio_ = NULL; - } - - if (capture_audio_) { - delete capture_audio_; - capture_audio_ = NULL; - } } - delete crit_; crit_ = NULL; } -CriticalSectionWrapper* AudioProcessingImpl::crit() const { - return crit_; -} - -int AudioProcessingImpl::split_sample_rate_hz() const { - return split_sample_rate_hz_; -} - int AudioProcessingImpl::Initialize() { CriticalSectionScoped crit_scoped(crit_); return InitializeLocked(); } -int AudioProcessingImpl::InitializeLocked() { - if (render_audio_ != NULL) { - delete render_audio_; - render_audio_ = NULL; - } - - if (capture_audio_ != NULL) { - delete capture_audio_; - capture_audio_ = NULL; - } +int AudioProcessingImpl::set_sample_rate_hz(int rate) { + CriticalSectionScoped crit_scoped(crit_); + return InitializeLocked(rate, + rate, + rev_in_format_.rate(), + fwd_in_format_.num_channels(), + fwd_proc_format_.num_channels(), + rev_in_format_.num_channels()); +} + +int AudioProcessingImpl::Initialize(int input_sample_rate_hz, + int output_sample_rate_hz, + int reverse_sample_rate_hz, + ChannelLayout input_layout, + ChannelLayout output_layout, + ChannelLayout reverse_layout) { + CriticalSectionScoped crit_scoped(crit_); + return InitializeLocked(input_sample_rate_hz, + output_sample_rate_hz, + reverse_sample_rate_hz, + ChannelsFromLayout(input_layout), + ChannelsFromLayout(output_layout), + ChannelsFromLayout(reverse_layout)); +} - render_audio_ = new AudioBuffer(num_reverse_channels_, - samples_per_channel_); - capture_audio_ = new AudioBuffer(num_input_channels_, - samples_per_channel_); +int AudioProcessingImpl::InitializeLocked() { + render_audio_.reset(new AudioBuffer(rev_in_format_.samples_per_channel(), + rev_in_format_.num_channels(), + rev_proc_format_.samples_per_channel(), + rev_proc_format_.num_channels(), + rev_proc_format_.samples_per_channel())); + capture_audio_.reset(new AudioBuffer(fwd_in_format_.samples_per_channel(), + fwd_in_format_.num_channels(), + fwd_proc_format_.samples_per_channel(), + fwd_proc_format_.num_channels(), + fwd_out_format_.samples_per_channel())); // Initialize all components. std::list::iterator it; @@ -202,97 +203,134 @@ return kNoError; } -void AudioProcessingImpl::SetExtraOptions(const Config& config) { - CriticalSectionScoped crit_scoped(crit_); - std::list::iterator it; - for (it = component_list_.begin(); it != component_list_.end(); ++it) - (*it)->SetExtraOptions(config); -} - -int AudioProcessingImpl::EnableExperimentalNs(bool enable) { - return kNoError; -} +int AudioProcessingImpl::InitializeLocked(int input_sample_rate_hz, + int output_sample_rate_hz, + int reverse_sample_rate_hz, + int num_input_channels, + int num_output_channels, + int num_reverse_channels) { + if (input_sample_rate_hz <= 0 || + output_sample_rate_hz <= 0 || + reverse_sample_rate_hz <= 0) { + return kBadSampleRateError; + } + if (num_output_channels > num_input_channels) { + return kBadNumberChannelsError; + } + // Only mono and stereo supported currently. + if (num_input_channels > 2 || num_input_channels < 1 || + num_output_channels > 2 || num_output_channels < 1 || + num_reverse_channels > 2 || num_reverse_channels < 1) { + return kBadNumberChannelsError; + } -int AudioProcessingImpl::set_sample_rate_hz(int rate) { - CriticalSectionScoped crit_scoped(crit_); - if (rate == sample_rate_hz_) { - return kNoError; + fwd_in_format_.set(input_sample_rate_hz, num_input_channels); + fwd_out_format_.set(output_sample_rate_hz); + rev_in_format_.set(reverse_sample_rate_hz, num_reverse_channels); + + // We process at the closest native rate >= min(input rate, output rate)... + int min_proc_rate = std::min(fwd_in_format_.rate(), fwd_out_format_.rate()); + int fwd_proc_rate; + if (min_proc_rate > kSampleRate16kHz) { + fwd_proc_rate = kSampleRate32kHz; + } else if (min_proc_rate > kSampleRate8kHz) { + fwd_proc_rate = kSampleRate16kHz; + } else { + fwd_proc_rate = kSampleRate8kHz; } - if (rate != kSampleRate8kHz && - rate != kSampleRate16kHz && - rate != kSampleRate32kHz) { - return kBadParameterError; + // ...with one exception. + if (echo_control_mobile_->is_enabled() && min_proc_rate > kSampleRate16kHz) { + fwd_proc_rate = kSampleRate16kHz; } - if (echo_control_mobile_->is_enabled() && rate > kSampleRate16kHz) { - LOG(LS_ERROR) << "AECM only supports 16 kHz or lower sample rates"; - return kUnsupportedComponentError; + + fwd_proc_format_.set(fwd_proc_rate, num_output_channels); + + // We normally process the reverse stream at 16 kHz. Unless... + int rev_proc_rate = kSampleRate16kHz; + if (fwd_proc_format_.rate() == kSampleRate8kHz) { + // ...the forward stream is at 8 kHz. + rev_proc_rate = kSampleRate8kHz; + } else { + if (rev_in_format_.rate() == kSampleRate32kHz) { + // ...or the input is at 32 kHz, in which case we use the splitting + // filter rather than the resampler. + rev_proc_rate = kSampleRate32kHz; + } } - sample_rate_hz_ = rate; - samples_per_channel_ = rate / 100; + // Always downmix the reverse stream to mono for analysis. This has been + // demonstrated to work well for AEC in most practical scenarios. + rev_proc_format_.set(rev_proc_rate, 1); - if (sample_rate_hz_ == kSampleRate32kHz) { - split_sample_rate_hz_ = kSampleRate16kHz; + if (fwd_proc_format_.rate() == kSampleRate32kHz) { + split_rate_ = kSampleRate16kHz; } else { - split_sample_rate_hz_ = sample_rate_hz_; + split_rate_ = fwd_proc_format_.rate(); } return InitializeLocked(); } -int AudioProcessingImpl::sample_rate_hz() const { - CriticalSectionScoped crit_scoped(crit_); - return sample_rate_hz_; -} - -int AudioProcessingImpl::set_num_reverse_channels(int channels) { - CriticalSectionScoped crit_scoped(crit_); - if (channels == num_reverse_channels_) { +// Calls InitializeLocked() if any of the audio parameters have changed from +// their current values. +int AudioProcessingImpl::MaybeInitializeLocked(int input_sample_rate_hz, + int output_sample_rate_hz, + int reverse_sample_rate_hz, + int num_input_channels, + int num_output_channels, + int num_reverse_channels) { + if (input_sample_rate_hz == fwd_in_format_.rate() && + output_sample_rate_hz == fwd_out_format_.rate() && + reverse_sample_rate_hz == rev_in_format_.rate() && + num_input_channels == fwd_in_format_.num_channels() && + num_output_channels == fwd_proc_format_.num_channels() && + num_reverse_channels == rev_in_format_.num_channels()) { return kNoError; } - // Only stereo supported currently. - if (channels > 2 || channels < 1) { - return kBadParameterError; - } - num_reverse_channels_ = channels; + return InitializeLocked(input_sample_rate_hz, + output_sample_rate_hz, + reverse_sample_rate_hz, + num_input_channels, + num_output_channels, + num_reverse_channels); +} - return InitializeLocked(); +void AudioProcessingImpl::SetExtraOptions(const Config& config) { + CriticalSectionScoped crit_scoped(crit_); + std::list::iterator it; + for (it = component_list_.begin(); it != component_list_.end(); ++it) + (*it)->SetExtraOptions(config); } -int AudioProcessingImpl::num_reverse_channels() const { - return num_reverse_channels_; +int AudioProcessingImpl::input_sample_rate_hz() const { + CriticalSectionScoped crit_scoped(crit_); + return fwd_in_format_.rate(); } -int AudioProcessingImpl::set_num_channels( - int input_channels, - int output_channels) { +int AudioProcessingImpl::sample_rate_hz() const { CriticalSectionScoped crit_scoped(crit_); - if (input_channels == num_input_channels_ && - output_channels == num_output_channels_) { - return kNoError; - } - if (output_channels > input_channels) { - return kBadParameterError; - } - // Only stereo supported currently. - if (input_channels > 2 || input_channels < 1 || - output_channels > 2 || output_channels < 1) { - return kBadParameterError; - } + return fwd_in_format_.rate(); +} - num_input_channels_ = input_channels; - num_output_channels_ = output_channels; +int AudioProcessingImpl::proc_sample_rate_hz() const { + return fwd_proc_format_.rate(); +} - return InitializeLocked(); +int AudioProcessingImpl::proc_split_sample_rate_hz() const { + return split_rate_; +} + +int AudioProcessingImpl::num_reverse_channels() const { + return rev_proc_format_.num_channels(); } int AudioProcessingImpl::num_input_channels() const { - return num_input_channels_; + return fwd_in_format_.num_channels(); } int AudioProcessingImpl::num_output_channels() const { - return num_output_channels_; + return fwd_proc_format_.num_channels(); } void AudioProcessingImpl::set_output_will_be_muted(bool muted) { @@ -303,61 +341,87 @@ return output_will_be_muted_; } -int AudioProcessingImpl::MaybeInitializeLocked(int sample_rate_hz, - int num_input_channels, int num_output_channels, int num_reverse_channels) { - if (sample_rate_hz == sample_rate_hz_ && - num_input_channels == num_input_channels_ && - num_output_channels == num_output_channels_ && - num_reverse_channels == num_reverse_channels_) { - return kNoError; +int AudioProcessingImpl::ProcessStream(const float* const* src, + int samples_per_channel, + int input_sample_rate_hz, + ChannelLayout input_layout, + int output_sample_rate_hz, + ChannelLayout output_layout, + float* const* dest) { + CriticalSectionScoped crit_scoped(crit_); + if (!src || !dest) { + return kNullPointerError; } - if (sample_rate_hz != kSampleRate8kHz && - sample_rate_hz != kSampleRate16kHz && - sample_rate_hz != kSampleRate32kHz) { - return kBadSampleRateError; - } - if (num_output_channels > num_input_channels) { - return kBadNumberChannelsError; - } - // Only mono and stereo supported currently. - if (num_input_channels > 2 || num_input_channels < 1 || - num_output_channels > 2 || num_output_channels < 1 || - num_reverse_channels > 2 || num_reverse_channels < 1) { - return kBadNumberChannelsError; + RETURN_ON_ERR(MaybeInitializeLocked(input_sample_rate_hz, + output_sample_rate_hz, + rev_in_format_.rate(), + ChannelsFromLayout(input_layout), + ChannelsFromLayout(output_layout), + rev_in_format_.num_channels())); + if (samples_per_channel != fwd_in_format_.samples_per_channel()) { + return kBadDataLengthError; } - if (echo_control_mobile_->is_enabled() && sample_rate_hz > kSampleRate16kHz) { - LOG(LS_ERROR) << "AECM only supports 16 or 8 kHz sample rates"; - return kUnsupportedComponentError; + +#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP + if (debug_file_->Open()) { + event_msg_->set_type(audioproc::Event::STREAM); + audioproc::Stream* msg = event_msg_->mutable_stream(); + const size_t channel_size = + sizeof(float) * fwd_in_format_.samples_per_channel(); + for (int i = 0; i < fwd_in_format_.num_channels(); ++i) + msg->add_input_channel(src[i], channel_size); } +#endif - sample_rate_hz_ = sample_rate_hz; - samples_per_channel_ = kChunkSizeMs * sample_rate_hz / 1000; - num_input_channels_ = num_input_channels; - num_output_channels_ = num_output_channels; - num_reverse_channels_ = num_reverse_channels; + capture_audio_->CopyFrom(src, samples_per_channel, input_layout); + RETURN_ON_ERR(ProcessStreamLocked()); + if (output_copy_needed(is_data_processed())) { + capture_audio_->CopyTo(fwd_out_format_.samples_per_channel(), + output_layout, + dest); + } - if (sample_rate_hz_ == kSampleRate32kHz) { - split_sample_rate_hz_ = kSampleRate16kHz; - } else { - split_sample_rate_hz_ = sample_rate_hz_; +#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP + if (debug_file_->Open()) { + audioproc::Stream* msg = event_msg_->mutable_stream(); + const size_t channel_size = + sizeof(float) * fwd_out_format_.samples_per_channel(); + for (int i = 0; i < fwd_proc_format_.num_channels(); ++i) + msg->add_output_channel(dest[i], channel_size); + RETURN_ON_ERR(WriteMessageToDebugFile()); } +#endif - return InitializeLocked(); + return kNoError; } int AudioProcessingImpl::ProcessStream(AudioFrame* frame) { CriticalSectionScoped crit_scoped(crit_); - int err = kNoError; - - if (frame == NULL) { + if (!frame) { return kNullPointerError; } - // TODO(ajm): We now always set the output channels equal to the input - // channels here. Remove the ability to downmix entirely. + // Must be a native rate. + if (frame->sample_rate_hz_ != kSampleRate8kHz && + frame->sample_rate_hz_ != kSampleRate16kHz && + frame->sample_rate_hz_ != kSampleRate32kHz) { + return kBadSampleRateError; + } + if (echo_control_mobile_->is_enabled() && + frame->sample_rate_hz_ > kSampleRate16kHz) { + LOG(LS_ERROR) << "AECM only supports 16 or 8 kHz sample rates"; + return kUnsupportedComponentError; + } + + // TODO(ajm): The input and output rates and channels are currently + // constrained to be identical in the int16 interface. RETURN_ON_ERR(MaybeInitializeLocked(frame->sample_rate_hz_, - frame->num_channels_, frame->num_channels_, num_reverse_channels_)); - if (frame->samples_per_channel_ != samples_per_channel_) { + frame->sample_rate_hz_, + rev_in_format_.rate(), + frame->num_channels_, + frame->num_channels_, + rev_in_format_.num_channels())); + if (frame->samples_per_channel_ != fwd_in_format_.samples_per_channel()) { return kBadDataLengthError; } @@ -369,127 +433,146 @@ frame->samples_per_channel_ * frame->num_channels_; msg->set_input_data(frame->data_, data_size); - msg->set_delay(stream_delay_ms_); - msg->set_drift(echo_cancellation_->stream_drift_samples()); - msg->set_level(gain_control_->stream_analog_level()); - msg->set_keypress(key_pressed_); } #endif capture_audio_->DeinterleaveFrom(frame); + RETURN_ON_ERR(ProcessStreamLocked()); + capture_audio_->InterleaveTo(frame, output_copy_needed(is_data_processed())); - // TODO(ajm): experiment with mixing and AEC placement. - if (num_output_channels_ < num_input_channels_) { - capture_audio_->Mix(num_output_channels_); - frame->num_channels_ = num_output_channels_; - } - - bool data_processed = is_data_processed(); - if (analysis_needed(data_processed)) { - for (int i = 0; i < num_output_channels_; i++) { - // Split into a low and high band. - WebRtcSpl_AnalysisQMF(capture_audio_->data(i), - capture_audio_->samples_per_channel(), - capture_audio_->low_pass_split_data(i), - capture_audio_->high_pass_split_data(i), - capture_audio_->analysis_filter_state1(i), - capture_audio_->analysis_filter_state2(i)); - } - } - - err = high_pass_filter_->ProcessCaptureAudio(capture_audio_); - if (err != kNoError) { - return err; - } - - err = gain_control_->AnalyzeCaptureAudio(capture_audio_); - if (err != kNoError) { - return err; - } - - err = echo_cancellation_->ProcessCaptureAudio(capture_audio_); - if (err != kNoError) { - return err; +#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP + if (debug_file_->Open()) { + audioproc::Stream* msg = event_msg_->mutable_stream(); + const size_t data_size = sizeof(int16_t) * + frame->samples_per_channel_ * + frame->num_channels_; + msg->set_output_data(frame->data_, data_size); + RETURN_ON_ERR(WriteMessageToDebugFile()); } +#endif - if (echo_control_mobile_->is_enabled() && - noise_suppression_->is_enabled()) { - capture_audio_->CopyLowPassToReference(); - } + return kNoError; +} - err = noise_suppression_->ProcessCaptureAudio(capture_audio_); - if (err != kNoError) { - return err; - } - err = echo_control_mobile_->ProcessCaptureAudio(capture_audio_); - if (err != kNoError) { - return err; +int AudioProcessingImpl::ProcessStreamLocked() { +#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP + if (debug_file_->Open()) { + audioproc::Stream* msg = event_msg_->mutable_stream(); + msg->set_delay(stream_delay_ms_); + msg->set_drift(echo_cancellation_->stream_drift_samples()); + msg->set_level(gain_control_->stream_analog_level()); + msg->set_keypress(key_pressed_); } +#endif - err = voice_detection_->ProcessCaptureAudio(capture_audio_); - if (err != kNoError) { - return err; + AudioBuffer* ca = capture_audio_.get(); // For brevity. + bool data_processed = is_data_processed(); + if (analysis_needed(data_processed)) { + for (int i = 0; i < fwd_proc_format_.num_channels(); i++) { + // Split into a low and high band. + WebRtcSpl_AnalysisQMF(ca->data(i), + ca->samples_per_channel(), + ca->low_pass_split_data(i), + ca->high_pass_split_data(i), + ca->filter_states(i)->analysis_filter_state1, + ca->filter_states(i)->analysis_filter_state2); + } } - err = gain_control_->ProcessCaptureAudio(capture_audio_); - if (err != kNoError) { - return err; - } + RETURN_ON_ERR(high_pass_filter_->ProcessCaptureAudio(ca)); + RETURN_ON_ERR(gain_control_->AnalyzeCaptureAudio(ca)); + RETURN_ON_ERR(noise_suppression_->AnalyzeCaptureAudio(ca)); + RETURN_ON_ERR(echo_cancellation_->ProcessCaptureAudio(ca)); + + if (echo_control_mobile_->is_enabled() && noise_suppression_->is_enabled()) { + ca->CopyLowPassToReference(); + } + RETURN_ON_ERR(noise_suppression_->ProcessCaptureAudio(ca)); + RETURN_ON_ERR(echo_control_mobile_->ProcessCaptureAudio(ca)); + RETURN_ON_ERR(voice_detection_->ProcessCaptureAudio(ca)); + RETURN_ON_ERR(gain_control_->ProcessCaptureAudio(ca)); if (synthesis_needed(data_processed)) { - for (int i = 0; i < num_output_channels_; i++) { + for (int i = 0; i < fwd_proc_format_.num_channels(); i++) { // Recombine low and high bands. - WebRtcSpl_SynthesisQMF(capture_audio_->low_pass_split_data(i), - capture_audio_->high_pass_split_data(i), - capture_audio_->samples_per_split_channel(), - capture_audio_->data(i), - capture_audio_->synthesis_filter_state1(i), - capture_audio_->synthesis_filter_state2(i)); + WebRtcSpl_SynthesisQMF(ca->low_pass_split_data(i), + ca->high_pass_split_data(i), + ca->samples_per_split_channel(), + ca->data(i), + ca->filter_states(i)->synthesis_filter_state1, + ca->filter_states(i)->synthesis_filter_state2); } } // The level estimator operates on the recombined data. - err = level_estimator_->ProcessStream(capture_audio_); - if (err != kNoError) { - return err; + RETURN_ON_ERR(level_estimator_->ProcessStream(ca)); + + was_stream_delay_set_ = false; + return kNoError; +} + +int AudioProcessingImpl::AnalyzeReverseStream(const float* const* data, + int samples_per_channel, + int sample_rate_hz, + ChannelLayout layout) { + CriticalSectionScoped crit_scoped(crit_); + if (data == NULL) { + return kNullPointerError; } - capture_audio_->InterleaveTo(frame, interleave_needed(data_processed)); + const int num_channels = ChannelsFromLayout(layout); + RETURN_ON_ERR(MaybeInitializeLocked(fwd_in_format_.rate(), + fwd_out_format_.rate(), + sample_rate_hz, + fwd_in_format_.num_channels(), + fwd_proc_format_.num_channels(), + num_channels)); + if (samples_per_channel != rev_in_format_.samples_per_channel()) { + return kBadDataLengthError; + } #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP if (debug_file_->Open()) { - audioproc::Stream* msg = event_msg_->mutable_stream(); - const size_t data_size = sizeof(int16_t) * - frame->samples_per_channel_ * - frame->num_channels_; - msg->set_output_data(frame->data_, data_size); - err = WriteMessageToDebugFile(); - if (err != kNoError) { - return err; - } + event_msg_->set_type(audioproc::Event::REVERSE_STREAM); + audioproc::ReverseStream* msg = event_msg_->mutable_reverse_stream(); + const size_t channel_size = + sizeof(float) * rev_in_format_.samples_per_channel(); + for (int i = 0; i < num_channels; ++i) + msg->add_channel(data[i], channel_size); + RETURN_ON_ERR(WriteMessageToDebugFile()); } #endif - was_stream_delay_set_ = false; - return kNoError; + render_audio_->CopyFrom(data, samples_per_channel, layout); + return AnalyzeReverseStreamLocked(); } -// TODO(ajm): Have AnalyzeReverseStream accept sample rates not matching the -// primary stream and convert ourselves rather than having the user manage it. -// We can be smarter and use the splitting filter when appropriate. Similarly, -// perform downmixing here. int AudioProcessingImpl::AnalyzeReverseStream(AudioFrame* frame) { CriticalSectionScoped crit_scoped(crit_); - int err = kNoError; if (frame == NULL) { return kNullPointerError; } - if (frame->sample_rate_hz_ != sample_rate_hz_) { + // Must be a native rate. + if (frame->sample_rate_hz_ != kSampleRate8kHz && + frame->sample_rate_hz_ != kSampleRate16kHz && + frame->sample_rate_hz_ != kSampleRate32kHz) { + return kBadSampleRateError; + } + // This interface does not tolerate different forward and reverse rates. + if (frame->sample_rate_hz_ != fwd_in_format_.rate()) { return kBadSampleRateError; } - RETURN_ON_ERR(MaybeInitializeLocked(sample_rate_hz_, num_input_channels_, - num_output_channels_, frame->num_channels_)); + + RETURN_ON_ERR(MaybeInitializeLocked(fwd_in_format_.rate(), + fwd_out_format_.rate(), + frame->sample_rate_hz_, + fwd_in_format_.num_channels(), + fwd_in_format_.num_channels(), + frame->num_channels_)); + if (frame->samples_per_channel_ != rev_in_format_.samples_per_channel()) { + return kBadDataLengthError; + } #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP if (debug_file_->Open()) { @@ -499,44 +582,33 @@ frame->samples_per_channel_ * frame->num_channels_; msg->set_data(frame->data_, data_size); - err = WriteMessageToDebugFile(); - if (err != kNoError) { - return err; - } + RETURN_ON_ERR(WriteMessageToDebugFile()); } #endif render_audio_->DeinterleaveFrom(frame); + return AnalyzeReverseStreamLocked(); +} - if (sample_rate_hz_ == kSampleRate32kHz) { - for (int i = 0; i < num_reverse_channels_; i++) { +int AudioProcessingImpl::AnalyzeReverseStreamLocked() { + AudioBuffer* ra = render_audio_.get(); // For brevity. + if (rev_proc_format_.rate() == kSampleRate32kHz) { + for (int i = 0; i < rev_proc_format_.num_channels(); i++) { // Split into low and high band. - WebRtcSpl_AnalysisQMF(render_audio_->data(i), - render_audio_->samples_per_channel(), - render_audio_->low_pass_split_data(i), - render_audio_->high_pass_split_data(i), - render_audio_->analysis_filter_state1(i), - render_audio_->analysis_filter_state2(i)); + WebRtcSpl_AnalysisQMF(ra->data(i), + ra->samples_per_channel(), + ra->low_pass_split_data(i), + ra->high_pass_split_data(i), + ra->filter_states(i)->analysis_filter_state1, + ra->filter_states(i)->analysis_filter_state2); } } - // TODO(ajm): warnings possible from components? - err = echo_cancellation_->ProcessRenderAudio(render_audio_); - if (err != kNoError) { - return err; - } - - err = echo_control_mobile_->ProcessRenderAudio(render_audio_); - if (err != kNoError) { - return err; - } - - err = gain_control_->ProcessRenderAudio(render_audio_); - if (err != kNoError) { - return err; - } + RETURN_ON_ERR(echo_cancellation_->ProcessRenderAudio(ra)); + RETURN_ON_ERR(echo_control_mobile_->ProcessRenderAudio(ra)); + RETURN_ON_ERR(gain_control_->ProcessRenderAudio(ra)); - return err; // TODO(ajm): this is for returning warnings; necessary? + return kNoError; } int AudioProcessingImpl::set_stream_delay_ms(int delay) { @@ -567,6 +639,14 @@ return was_stream_delay_set_; } +void AudioProcessingImpl::set_stream_key_pressed(bool key_pressed) { + key_pressed_ = key_pressed; +} + +bool AudioProcessingImpl::stream_key_pressed() const { + return key_pressed_; +} + void AudioProcessingImpl::set_delay_offset_ms(int offset) { CriticalSectionScoped crit_scoped(crit_); delay_offset_ms_ = offset; @@ -576,14 +656,6 @@ return delay_offset_ms_; } -void AudioProcessingImpl::set_stream_key_pressed(bool key_pressed) { - key_pressed_ = key_pressed; -} - -bool AudioProcessingImpl::stream_key_pressed() const { - return key_pressed_; -} - int AudioProcessingImpl::StartDebugRecording( const char filename[AudioProcessing::kMaxFilenameSize]) { CriticalSectionScoped crit_scoped(crit_); @@ -645,6 +717,12 @@ #endif // WEBRTC_AUDIOPROC_DEBUG_DUMP } +int AudioProcessingImpl::StartDebugRecordingForPlatformFile( + rtc::PlatformFile handle) { + FILE* stream = rtc::FdopenPlatformFileForWriting(handle); + return StartDebugRecording(stream); +} + int AudioProcessingImpl::StopDebugRecording() { CriticalSectionScoped crit_scoped(crit_); @@ -714,20 +792,21 @@ return true; } -bool AudioProcessingImpl::interleave_needed(bool is_data_processed) const { +bool AudioProcessingImpl::output_copy_needed(bool is_data_processed) const { // Check if we've upmixed or downmixed the audio. - return (num_output_channels_ != num_input_channels_ || is_data_processed); + return ((fwd_proc_format_.num_channels() != fwd_in_format_.num_channels()) || + is_data_processed); } bool AudioProcessingImpl::synthesis_needed(bool is_data_processed) const { - return (is_data_processed && sample_rate_hz_ == kSampleRate32kHz); + return (is_data_processed && fwd_proc_format_.rate() == kSampleRate32kHz); } bool AudioProcessingImpl::analysis_needed(bool is_data_processed) const { if (!is_data_processed && !voice_detection_->is_enabled()) { // Only level_estimator_ is enabled. return false; - } else if (sample_rate_hz_ == kSampleRate32kHz) { + } else if (fwd_proc_format_.rate() == kSampleRate32kHz) { // Something besides level_estimator_ is enabled, and we have super-wb. return true; } @@ -759,17 +838,18 @@ event_msg_->Clear(); - return 0; + return kNoError; } int AudioProcessingImpl::WriteInitMessage() { event_msg_->set_type(audioproc::Event::INIT); audioproc::Init* msg = event_msg_->mutable_init(); - msg->set_sample_rate(sample_rate_hz_); - msg->set_device_sample_rate(echo_cancellation_->device_sample_rate_hz()); - msg->set_num_input_channels(num_input_channels_); - msg->set_num_output_channels(num_output_channels_); - msg->set_num_reverse_channels(num_reverse_channels_); + msg->set_sample_rate(fwd_in_format_.rate()); + msg->set_num_input_channels(fwd_in_format_.num_channels()); + msg->set_num_output_channels(fwd_proc_format_.num_channels()); + msg->set_num_reverse_channels(rev_in_format_.num_channels()); + msg->set_reverse_sample_rate(rev_in_format_.rate()); + msg->set_output_sample_rate(fwd_out_format_.rate()); int err = WriteMessageToDebugFile(); if (err != kNoError) { @@ -779,4 +859,5 @@ return kNoError; } #endif // WEBRTC_AUDIOPROC_DEBUG_DUMP + } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing_impl.h 2015-02-03 14:33:35.000000000 +0000 @@ -19,9 +19,10 @@ #include "webrtc/system_wrappers/interface/scoped_ptr.h" namespace webrtc { + class AudioBuffer; class CriticalSectionWrapper; -class EchoCancellationImplWrapper; +class EchoCancellationImpl; class EchoControlMobileImpl; class FileWrapper; class GainControlImpl; @@ -39,43 +40,84 @@ } // namespace audioproc #endif -class AudioProcessingImpl : public AudioProcessing { +class AudioRate { public: - enum { - kSampleRate8kHz = 8000, - kSampleRate16kHz = 16000, - kSampleRate32kHz = 32000 - }; + explicit AudioRate(int sample_rate_hz) + : rate_(sample_rate_hz), + samples_per_channel_(AudioProcessing::kChunkSizeMs * rate_ / 1000) {} + virtual ~AudioRate() {} + + void set(int rate) { + rate_ = rate; + samples_per_channel_ = AudioProcessing::kChunkSizeMs * rate_ / 1000; + } - explicit AudioProcessingImpl(const Config& config); - virtual ~AudioProcessingImpl(); + int rate() const { return rate_; } + int samples_per_channel() const { return samples_per_channel_; } + + private: + int rate_; + int samples_per_channel_; +}; + +class AudioFormat : public AudioRate { + public: + AudioFormat(int sample_rate_hz, int num_channels) + : AudioRate(sample_rate_hz), + num_channels_(num_channels) {} + virtual ~AudioFormat() {} + + void set(int rate, int num_channels) { + AudioRate::set(rate); + num_channels_ = num_channels; + } - CriticalSectionWrapper* crit() const; + int num_channels() const { return num_channels_; } - int split_sample_rate_hz() const; - bool was_stream_delay_set() const; + private: + int num_channels_; +}; + +class AudioProcessingImpl : public AudioProcessing { + public: + explicit AudioProcessingImpl(const Config& config); + virtual ~AudioProcessingImpl(); // AudioProcessing methods. virtual int Initialize() OVERRIDE; + virtual int Initialize(int input_sample_rate_hz, + int output_sample_rate_hz, + int reverse_sample_rate_hz, + ChannelLayout input_layout, + ChannelLayout output_layout, + ChannelLayout reverse_layout) OVERRIDE; virtual void SetExtraOptions(const Config& config) OVERRIDE; - virtual int EnableExperimentalNs(bool enable) OVERRIDE; - virtual bool experimental_ns_enabled() const OVERRIDE { - return false; - } virtual int set_sample_rate_hz(int rate) OVERRIDE; + virtual int input_sample_rate_hz() const OVERRIDE; virtual int sample_rate_hz() const OVERRIDE; - virtual int set_num_channels(int input_channels, - int output_channels) OVERRIDE; + virtual int proc_sample_rate_hz() const OVERRIDE; + virtual int proc_split_sample_rate_hz() const OVERRIDE; virtual int num_input_channels() const OVERRIDE; virtual int num_output_channels() const OVERRIDE; - virtual int set_num_reverse_channels(int channels) OVERRIDE; virtual int num_reverse_channels() const OVERRIDE; virtual void set_output_will_be_muted(bool muted) OVERRIDE; virtual bool output_will_be_muted() const OVERRIDE; virtual int ProcessStream(AudioFrame* frame) OVERRIDE; + virtual int ProcessStream(const float* const* src, + int samples_per_channel, + int input_sample_rate_hz, + ChannelLayout input_layout, + int output_sample_rate_hz, + ChannelLayout output_layout, + float* const* dest) OVERRIDE; virtual int AnalyzeReverseStream(AudioFrame* frame) OVERRIDE; + virtual int AnalyzeReverseStream(const float* const* data, + int samples_per_channel, + int sample_rate_hz, + ChannelLayout layout) OVERRIDE; virtual int set_stream_delay_ms(int delay) OVERRIDE; virtual int stream_delay_ms() const OVERRIDE; + virtual bool was_stream_delay_set() const OVERRIDE; virtual void set_delay_offset_ms(int offset) OVERRIDE; virtual int delay_offset_ms() const OVERRIDE; virtual void set_stream_key_pressed(bool key_pressed) OVERRIDE; @@ -83,6 +125,8 @@ virtual int StartDebugRecording( const char filename[kMaxFilenameSize]) OVERRIDE; virtual int StartDebugRecording(FILE* handle) OVERRIDE; + virtual int StartDebugRecordingForPlatformFile( + rtc::PlatformFile handle) OVERRIDE; virtual int StopDebugRecording() OVERRIDE; virtual EchoCancellation* echo_cancellation() const OVERRIDE; virtual EchoControlMobile* echo_control_mobile() const OVERRIDE; @@ -93,17 +137,31 @@ virtual VoiceDetection* voice_detection() const OVERRIDE; protected: + // Overridden in a mock. virtual int InitializeLocked(); private: - int MaybeInitializeLocked(int sample_rate_hz, int num_input_channels, - int num_output_channels, int num_reverse_channels); + int InitializeLocked(int input_sample_rate_hz, + int output_sample_rate_hz, + int reverse_sample_rate_hz, + int num_input_channels, + int num_output_channels, + int num_reverse_channels); + int MaybeInitializeLocked(int input_sample_rate_hz, + int output_sample_rate_hz, + int reverse_sample_rate_hz, + int num_input_channels, + int num_output_channels, + int num_reverse_channels); + int ProcessStreamLocked(); + int AnalyzeReverseStreamLocked(); + bool is_data_processed() const; - bool interleave_needed(bool is_data_processed) const; + bool output_copy_needed(bool is_data_processed) const; bool synthesis_needed(bool is_data_processed) const; bool analysis_needed(bool is_data_processed) const; - EchoCancellationImplWrapper* echo_cancellation_; + EchoCancellationImpl* echo_cancellation_; EchoControlMobileImpl* echo_control_mobile_; GainControlImpl* gain_control_; HighPassFilterImpl* high_pass_filter_; @@ -113,8 +171,8 @@ std::list component_list_; CriticalSectionWrapper* crit_; - AudioBuffer* render_audio_; - AudioBuffer* capture_audio_; + scoped_ptr render_audio_; + scoped_ptr capture_audio_; #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP // TODO(andrew): make this more graceful. Ideally we would split this stuff // out into a separate class with an "enabled" and "disabled" implementation. @@ -125,20 +183,22 @@ std::string event_str_; // Memory for protobuf serialization. #endif - int sample_rate_hz_; - int split_sample_rate_hz_; - int samples_per_channel_; + AudioFormat fwd_in_format_; + AudioFormat fwd_proc_format_; + AudioRate fwd_out_format_; + AudioFormat rev_in_format_; + AudioFormat rev_proc_format_; + int split_rate_; + int stream_delay_ms_; int delay_offset_ms_; bool was_stream_delay_set_; - int num_reverse_channels_; - int num_input_channels_; - int num_output_channels_; bool output_will_be_muted_; bool key_pressed_; }; + } // namespace webrtc #endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_PROCESSING_IMPL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing_impl_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing_impl_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing_impl_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing_impl_unittest.cc 2015-02-03 14:33:35.000000000 +0000 @@ -12,6 +12,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/config.h" #include "webrtc/modules/audio_processing/test/test_utils.h" #include "webrtc/modules/interface/module_common_types.h" @@ -44,23 +45,23 @@ SetFrameSampleRate(&frame, 16000); EXPECT_CALL(mock, InitializeLocked()) .Times(0); - EXPECT_EQ(kNoErr, mock.ProcessStream(&frame)); - EXPECT_EQ(kNoErr, mock.AnalyzeReverseStream(&frame)); + EXPECT_NOERR(mock.ProcessStream(&frame)); + EXPECT_NOERR(mock.AnalyzeReverseStream(&frame)); // New sample rate. (Only impacts ProcessStream). SetFrameSampleRate(&frame, 32000); EXPECT_CALL(mock, InitializeLocked()) .Times(1); - EXPECT_EQ(kNoErr, mock.ProcessStream(&frame)); + EXPECT_NOERR(mock.ProcessStream(&frame)); // New number of channels. frame.num_channels_ = 2; EXPECT_CALL(mock, InitializeLocked()) .Times(2); - EXPECT_EQ(kNoErr, mock.ProcessStream(&frame)); + EXPECT_NOERR(mock.ProcessStream(&frame)); // ProcessStream sets num_channels_ == num_output_channels. frame.num_channels_ = 2; - EXPECT_EQ(kNoErr, mock.AnalyzeReverseStream(&frame)); + EXPECT_NOERR(mock.AnalyzeReverseStream(&frame)); // A new sample rate passed to AnalyzeReverseStream should be an error and // not cause an init. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing_tests.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing_tests.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing_tests.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing_tests.gypi 2015-02-03 14:33:35.000000000 +0000 @@ -7,25 +7,23 @@ # be found in the AUTHORS file in the root of the source tree. { - 'targets': [ - { - 'target_name': 'audioproc_unittest_proto', - 'type': 'static_library', - 'sources': [ 'test/unittest.proto', ], - 'variables': { - 'proto_in_dir': 'test', - # Workaround to protect against gyp's pathname relativization when this - # file is included by modules.gyp. - 'proto_out_protected': 'webrtc/audio_processing', - 'proto_out_dir': '<(proto_out_protected)', - }, - 'includes': [ '../../build/protoc.gypi', ], - }, - ], 'conditions': [ ['enable_protobuf==1', { 'targets': [ { + 'target_name': 'audioproc_unittest_proto', + 'type': 'static_library', + 'sources': [ 'test/unittest.proto', ], + 'variables': { + 'proto_in_dir': 'test', + # Workaround to protect against gyp's pathname relativization when + # this file is included by modules.gyp. + 'proto_out_protected': 'webrtc/audio_processing', + 'proto_out_dir': '<(proto_out_protected)', + }, + 'includes': [ '../../build/protoc.gypi', ], + }, + { 'target_name': 'audioproc', 'type': 'executable', 'dependencies': [ @@ -43,6 +41,7 @@ 'dependencies': [ 'audioproc_debug_proto', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', ], 'sources': [ 'test/unpack.cc', ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/BUILD.gn 2015-02-03 14:33:35.000000000 +0000 @@ -0,0 +1,235 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("//build/config/arm.gni") +import("//third_party/protobuf/proto_library.gni") +import("../../build/webrtc.gni") + +declare_args() { + # Outputs some low-level debug files. + aec_debug_dump = false + + # Disables the usual mode where we trust the reported system delay + # values the AEC receives. The corresponding define is set appropriately + # in the code, but it can be force-enabled here for testing. + aec_untrusted_delay_for_testing = false +} + +source_set("audio_processing") { + sources = [ + "aec/include/echo_cancellation.h", + "aec/echo_cancellation.c", + "aec/echo_cancellation_internal.h", + "aec/aec_core.h", + "aec/aec_core.c", + "aec/aec_core_internal.h", + "aec/aec_rdft.h", + "aec/aec_rdft.c", + "aec/aec_resampler.h", + "aec/aec_resampler.c", + "aecm/include/echo_control_mobile.h", + "aecm/echo_control_mobile.c", + "aecm/aecm_core.c", + "aecm/aecm_core.h", + "agc/include/gain_control.h", + "agc/analog_agc.c", + "agc/analog_agc.h", + "agc/digital_agc.c", + "agc/digital_agc.h", + "audio_buffer.cc", + "audio_buffer.h", + "audio_processing_impl.cc", + "audio_processing_impl.h", + "common.h", + "echo_cancellation_impl.cc", + "echo_cancellation_impl.h", + "echo_control_mobile_impl.cc", + "echo_control_mobile_impl.h", + "gain_control_impl.cc", + "gain_control_impl.h", + "high_pass_filter_impl.cc", + "high_pass_filter_impl.h", + "include/audio_processing.h", + "level_estimator_impl.cc", + "level_estimator_impl.h", + "noise_suppression_impl.cc", + "noise_suppression_impl.h", + "processing_component.cc", + "processing_component.h", + "rms_level.cc", + "rms_level.h", + "typing_detection.cc", + "typing_detection.h", + "utility/delay_estimator.c", + "utility/delay_estimator.h", + "utility/delay_estimator_internal.h", + "utility/delay_estimator_wrapper.c", + "utility/delay_estimator_wrapper.h", + "utility/fft4g.c", + "utility/fft4g.h", + "utility/ring_buffer.c", + "utility/ring_buffer.h", + "voice_detection_impl.cc", + "voice_detection_impl.h", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + defines = [] + deps = [] + + if (aec_debug_dump) { + defines += [ "WEBRTC_AEC_DEBUG_DUMP" ] + } + + if (aec_untrusted_delay_for_testing) { + defines += [ "WEBRTC_UNTRUSTED_DELAY" ] + } + + if (rtc_enable_protobuf) { + defines += [ "WEBRTC_AUDIOPROC_DEBUG_DUMP" ] + deps += [ ":audioproc_debug_proto" ] + } + + if (rtc_prefer_fixed_point) { + defines += [ "WEBRTC_NS_FIXED" ] + sources += [ + "ns/include/noise_suppression_x.h", + "ns/noise_suppression_x.c", + "ns/nsx_core.c", + "ns/nsx_core.h", + "ns/nsx_defines.h", + ] + if (cpu_arch == "mipsel") { + sources += [ "ns/nsx_core_mips.c" ] + } else { + sources += [ "ns/nsx_core_c.c" ] + } + } else { + defines += [ "WEBRTC_NS_FLOAT" ] + sources += [ + "ns/defines.h", + "ns/include/noise_suppression.h", + "ns/noise_suppression.c", + "ns/ns_core.c", + "ns/ns_core.h", + "ns/windows_private.h", + ] + } + + if (cpu_arch == "x86" || cpu_arch == "x64") { + deps += [ ":audio_processing_sse2" ] + } + + if (rtc_build_armv7_neon) { + deps += [ ":audio_processing_neon" ] + } + + if (cpu_arch == "mipsel") { + sources += [ "aecm/aecm_core_mips.c" ] + if (mips_fpu) { + sources += [ + "aec/aec_core_mips.c", + "aec/aec_rdft_mips.c", + ] + } + } else { + sources += [ "aecm/aecm_core_c.c" ] + } + + if (is_win) { + cflags = [ + # TODO(jschuh): Bug 1348: fix this warning. + "/wd4267", # size_t to int truncations + ] + } + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps += [ + "../../base:rtc_base_approved", + "../../common_audio", + "../../system_wrappers", + ] +} + +if (rtc_enable_protobuf) { + proto_library("audioproc_debug_proto") { + sources = [ "debug.proto" ] + + proto_out_dir = "webrtc/audio_processing" + } +} + +if (cpu_arch == "x86" || cpu_arch == "x64") { + source_set("audio_processing_sse2") { + sources = [ + "aec/aec_core_sse2.c", + "aec/aec_rdft_sse2.c", + ] + + cflags = [ "-msse2" ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + } +} + +if (rtc_build_armv7_neon) { + source_set("audio_processing_neon") { + sources = [ + "aec/aec_core_neon.c", + "aec/aec_rdft_neon.c", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + deps = [ "../../common_audio" ] + + if (is_android || is_ios) { + sources += [ + # TODO(andrew): Re-enable these once webrtc:3580 is resolved. + #"aecm/aecm_core_neon.S", + #"ns/nsx_core_neon.S", + ] + + include_dirs = [ target_out_dir ] + } else { + sources += [ + "aecm/aecm_core_neon.c", + "ns/nsx_core_neon.c", + ] + } + + # Enable compilation for the ARM v7 Neon instruction set. This is needed + # since //build/config/arm.gni only enables Neon for iOS, not Android. + # This provides the same functionality as webrtc/build/arm_neon.gypi. + # TODO(kjellander): Investigate if this can be moved into webrtc.gni or + # //build/config/arm.gni instead, to reduce code duplication. + # Remove the -mfpu=vfpv3-d16 cflag. + configs -= [ "//build/config/compiler:compiler_arm_fpu" ] + cflags = [ + "-flax-vector-conversions", + "-mfpu=neon", + ] + + # Disable LTO in audio_processing_neon target due to compiler bug. + if (rtc_use_lto) { + cflags -= [ + "-flto", + "-ffat-lto-objects", + ] + } + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/common.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/common.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/common.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/common.h 2015-02-03 14:33:35.000000000 +0000 @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_COMMON_H_ +#define WEBRTC_MODULES_AUDIO_PROCESSING_COMMON_H_ + +#include +#include + +#include "webrtc/base/checks.h" +#include "webrtc/modules/audio_processing/include/audio_processing.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +static inline int ChannelsFromLayout(AudioProcessing::ChannelLayout layout) { + switch (layout) { + case AudioProcessing::kMono: + case AudioProcessing::kMonoAndKeyboard: + return 1; + case AudioProcessing::kStereo: + case AudioProcessing::kStereoAndKeyboard: + return 2; + } + assert(false); + return -1; +} + +// Helper to encapsulate a contiguous data buffer with access to a pointer +// array of the deinterleaved channels. +template +class ChannelBuffer { + public: + ChannelBuffer(int samples_per_channel, int num_channels) + : data_(new T[samples_per_channel * num_channels]), + channels_(new T*[num_channels]), + samples_per_channel_(samples_per_channel), + num_channels_(num_channels) { + Initialize(); + } + + ChannelBuffer(const T* data, int samples_per_channel, int num_channels) + : data_(new T[samples_per_channel * num_channels]), + channels_(new T*[num_channels]), + samples_per_channel_(samples_per_channel), + num_channels_(num_channels) { + Initialize(); + memcpy(data_.get(), data, length() * sizeof(T)); + } + + ChannelBuffer(const T* const* channels, int samples_per_channel, + int num_channels) + : data_(new T[samples_per_channel * num_channels]), + channels_(new T*[num_channels]), + samples_per_channel_(samples_per_channel), + num_channels_(num_channels) { + Initialize(); + for (int i = 0; i < num_channels_; ++i) + CopyFrom(channels[i], i); + } + + ~ChannelBuffer() {} + + void CopyFrom(const void* channel_ptr, int i) { + DCHECK_LT(i, num_channels_); + memcpy(channels_[i], channel_ptr, samples_per_channel_ * sizeof(T)); + } + + T* data() { return data_.get(); } + const T* data() const { return data_.get(); } + + const T* channel(int i) const { + DCHECK_GE(i, 0); + DCHECK_LT(i, num_channels_); + return channels_[i]; + } + T* channel(int i) { + const ChannelBuffer* t = this; + return const_cast(t->channel(i)); + } + + T* const* channels() { return channels_.get(); } + const T* const* channels() const { return channels_.get(); } + + int samples_per_channel() const { return samples_per_channel_; } + int num_channels() const { return num_channels_; } + int length() const { return samples_per_channel_ * num_channels_; } + + private: + void Initialize() { + memset(data_.get(), 0, sizeof(T) * length()); + for (int i = 0; i < num_channels_; ++i) + channels_[i] = &data_[i * samples_per_channel_]; + } + + scoped_ptr data_; + scoped_ptr channels_; + const int samples_per_channel_; + const int num_channels_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_PROCESSING_COMMON_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/debug.proto thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/debug.proto --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/debug.proto 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/debug.proto 2015-02-03 14:33:35.000000000 +0000 @@ -4,23 +4,39 @@ message Init { optional int32 sample_rate = 1; - optional int32 device_sample_rate = 2; + optional int32 device_sample_rate = 2 [deprecated=true]; optional int32 num_input_channels = 3; optional int32 num_output_channels = 4; optional int32 num_reverse_channels = 5; + optional int32 reverse_sample_rate = 6; + optional int32 output_sample_rate = 7; } +// May contain interleaved or deinterleaved data, but don't store both formats. message ReverseStream { + // int16 interleaved data. optional bytes data = 1; + + // float deinterleaved data, where each repeated element points to a single + // channel buffer of data. + repeated bytes channel = 2; } +// May contain interleaved or deinterleaved data, but don't store both formats. message Stream { + // int16 interleaved data. optional bytes input_data = 1; optional bytes output_data = 2; + optional int32 delay = 3; optional sint32 drift = 4; optional int32 level = 5; optional bool keypress = 6; + + // float deinterleaved data, where each repeated element points to a single + // channel buffer of data. + repeated bytes input_channel = 7; + repeated bytes output_channel = 8; } message Event { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl.cc 2015-02-03 14:33:35.000000000 +0000 @@ -18,7 +18,6 @@ } #include "webrtc/modules/audio_processing/aec/include/echo_cancellation.h" #include "webrtc/modules/audio_processing/audio_buffer.h" -#include "webrtc/modules/audio_processing/audio_processing_impl.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" namespace webrtc { @@ -56,23 +55,20 @@ } } // namespace -EchoCancellationImplWrapper* EchoCancellationImplWrapper::Create( - const AudioProcessingImpl* audioproc) { - return new EchoCancellationImpl(audioproc); -} - -EchoCancellationImpl::EchoCancellationImpl(const AudioProcessingImpl* apm) - : ProcessingComponent(apm), +EchoCancellationImpl::EchoCancellationImpl(const AudioProcessing* apm, + CriticalSectionWrapper* crit) + : ProcessingComponent(), apm_(apm), + crit_(crit), drift_compensation_enabled_(false), metrics_enabled_(false), suppression_level_(kModerateSuppression), - device_sample_rate_hz_(48000), stream_drift_samples_(0), was_stream_drift_set_(false), stream_has_echo_(false), delay_logging_enabled_(false), - delay_correction_enabled_(true) {} // default to long AEC tail in Mozilla + delay_correction_enabled_(true), // default to long AEC tail in Mozilla + reported_delay_enabled_(true) {} EchoCancellationImpl::~EchoCancellationImpl() {} @@ -93,7 +89,7 @@ Handle* my_handle = static_cast(handle(handle_index)); err = WebRtcAec_BufferFarend( my_handle, - audio->low_pass_split_data(j), + audio->low_pass_split_data_f(j), static_cast(audio->samples_per_split_channel())); if (err != apm_->kNoError) { @@ -133,10 +129,10 @@ Handle* my_handle = handle(handle_index); err = WebRtcAec_Process( my_handle, - audio->low_pass_split_data(i), - audio->high_pass_split_data(i), - audio->low_pass_split_data(i), - audio->high_pass_split_data(i), + audio->low_pass_split_data_f(i), + audio->high_pass_split_data_f(i), + audio->low_pass_split_data_f(i), + audio->high_pass_split_data_f(i), static_cast(audio->samples_per_split_channel()), apm_->stream_delay_ms(), stream_drift_samples_); @@ -168,7 +164,7 @@ } int EchoCancellationImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); // Ensure AEC and AECM are not both enabled. if (enable && apm_->echo_control_mobile()->is_enabled()) { return apm_->kBadParameterError; @@ -182,7 +178,7 @@ } int EchoCancellationImpl::set_suppression_level(SuppressionLevel level) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); if (MapSetting(level) == -1) { return apm_->kBadParameterError; } @@ -197,7 +193,7 @@ } int EchoCancellationImpl::enable_drift_compensation(bool enable) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); drift_compensation_enabled_ = enable; return Configure(); } @@ -206,20 +202,6 @@ return drift_compensation_enabled_; } -int EchoCancellationImpl::set_device_sample_rate_hz(int rate) { - CriticalSectionScoped crit_scoped(apm_->crit()); - if (rate < 8000 || rate > 96000) { - return apm_->kBadParameterError; - } - - device_sample_rate_hz_ = rate; - return Initialize(); -} - -int EchoCancellationImpl::device_sample_rate_hz() const { - return device_sample_rate_hz_; -} - void EchoCancellationImpl::set_stream_drift_samples(int drift) { was_stream_drift_set_ = true; stream_drift_samples_ = drift; @@ -230,7 +212,7 @@ } int EchoCancellationImpl::enable_metrics(bool enable) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); metrics_enabled_ = enable; return Configure(); } @@ -242,7 +224,7 @@ // TODO(ajm): we currently just use the metrics from the first AEC. Think more // aboue the best way to extend this to multi-channel. int EchoCancellationImpl::GetMetrics(Metrics* metrics) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); if (metrics == NULL) { return apm_->kNullPointerError; } @@ -289,7 +271,7 @@ } int EchoCancellationImpl::enable_delay_logging(bool enable) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); delay_logging_enabled_ = enable; return Configure(); } @@ -300,7 +282,7 @@ // TODO(bjornv): How should we handle the multi-channel case? int EchoCancellationImpl::GetDelayMetrics(int* median, int* std) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); if (median == NULL) { return apm_->kNullPointerError; } @@ -322,7 +304,7 @@ } struct AecCore* EchoCancellationImpl::aec_core() const { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); if (!is_component_enabled()) { return NULL; } @@ -339,12 +321,13 @@ return apm_->kNoError; } -#if 0 void EchoCancellationImpl::SetExtraOptions(const Config& config) { +#if 0 delay_correction_enabled_ = config.Get().enabled; +#endif + reported_delay_enabled_ = config.Get().enabled; Configure(); } -#endif void* EchoCancellationImpl::CreateHandle() const { Handle* handle = NULL; @@ -357,16 +340,19 @@ return handle; } -int EchoCancellationImpl::DestroyHandle(void* handle) const { +void EchoCancellationImpl::DestroyHandle(void* handle) const { assert(handle != NULL); - return WebRtcAec_Free(static_cast(handle)); + WebRtcAec_Free(static_cast(handle)); } int EchoCancellationImpl::InitializeHandle(void* handle) const { assert(handle != NULL); + // TODO(ajm): Drift compensation is disabled in practice. If restored, it + // should be managed internally and not depend on the hardware sample rate. + // For now, just hardcode a 48 kHz value. return WebRtcAec_Init(static_cast(handle), - apm_->sample_rate_hz(), - device_sample_rate_hz_); + apm_->proc_sample_rate_hz(), + 48000); } int EchoCancellationImpl::ConfigureHandle(void* handle) const { @@ -379,6 +365,8 @@ WebRtcAec_enable_delay_correction(WebRtcAec_aec_core( static_cast(handle)), delay_correction_enabled_ ? 1 : 0); + WebRtcAec_enable_reported_delay(WebRtcAec_aec_core( + static_cast(handle)), reported_delay_enabled_ ? 1 : 0); return WebRtcAec_set_config(static_cast(handle), config); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl.h 2015-02-03 14:33:35.000000000 +0000 @@ -11,37 +11,37 @@ #ifndef WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_H_ #define WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_H_ -#include "webrtc/modules/audio_processing/echo_cancellation_impl_wrapper.h" +#include "webrtc/modules/audio_processing/include/audio_processing.h" +#include "webrtc/modules/audio_processing/processing_component.h" namespace webrtc { -class AudioProcessingImpl; class AudioBuffer; +class CriticalSectionWrapper; -class EchoCancellationImpl : public EchoCancellationImplWrapper { +class EchoCancellationImpl : public EchoCancellation, + public ProcessingComponent { public: - explicit EchoCancellationImpl(const AudioProcessingImpl* apm); + EchoCancellationImpl(const AudioProcessing* apm, + CriticalSectionWrapper* crit); virtual ~EchoCancellationImpl(); - // EchoCancellationImplWrapper implementation. - virtual int ProcessRenderAudio(const AudioBuffer* audio) OVERRIDE; - virtual int ProcessCaptureAudio(AudioBuffer* audio) OVERRIDE; + int ProcessRenderAudio(const AudioBuffer* audio); + int ProcessCaptureAudio(AudioBuffer* audio); // EchoCancellation implementation. virtual bool is_enabled() const OVERRIDE; - virtual int device_sample_rate_hz() const OVERRIDE; virtual int stream_drift_samples() const OVERRIDE; // ProcessingComponent implementation. virtual int Initialize() OVERRIDE; - // virtual void SetExtraOptions(const Config& config) OVERRIDE; + virtual void SetExtraOptions(const Config& config) OVERRIDE; private: // EchoCancellation implementation. virtual int Enable(bool enable) OVERRIDE; virtual int enable_drift_compensation(bool enable) OVERRIDE; virtual bool is_drift_compensation_enabled() const OVERRIDE; - virtual int set_device_sample_rate_hz(int rate) OVERRIDE; virtual void set_stream_drift_samples(int drift) OVERRIDE; virtual int set_suppression_level(SuppressionLevel level) OVERRIDE; virtual SuppressionLevel suppression_level() const OVERRIDE; @@ -58,20 +58,21 @@ virtual void* CreateHandle() const OVERRIDE; virtual int InitializeHandle(void* handle) const OVERRIDE; virtual int ConfigureHandle(void* handle) const OVERRIDE; - virtual int DestroyHandle(void* handle) const OVERRIDE; + virtual void DestroyHandle(void* handle) const OVERRIDE; virtual int num_handles_required() const OVERRIDE; virtual int GetHandleError(void* handle) const OVERRIDE; - const AudioProcessingImpl* apm_; + const AudioProcessing* apm_; + CriticalSectionWrapper* crit_; bool drift_compensation_enabled_; bool metrics_enabled_; SuppressionLevel suppression_level_; - int device_sample_rate_hz_; int stream_drift_samples_; bool was_stream_drift_set_; bool stream_has_echo_; bool delay_logging_enabled_; bool delay_correction_enabled_; + bool reported_delay_enabled_; }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl_unittest.cc 2015-02-03 14:33:35.000000000 +0000 @@ -14,6 +14,7 @@ } #include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/test/testsupport/gtest_disable.h" namespace webrtc { @@ -47,4 +48,34 @@ EXPECT_EQ(0, WebRtcAec_delay_correction_enabled(aec_core)); } +TEST(EchoCancellationInternalTest, ReportedDelay) { + scoped_ptr ap(AudioProcessing::Create(0)); + EXPECT_TRUE(ap->echo_cancellation()->aec_core() == NULL); + + EXPECT_EQ(ap->kNoError, ap->echo_cancellation()->Enable(true)); + EXPECT_TRUE(ap->echo_cancellation()->is_enabled()); + + AecCore* aec_core = ap->echo_cancellation()->aec_core(); + ASSERT_TRUE(aec_core != NULL); + // Enabled by default. + EXPECT_EQ(1, WebRtcAec_reported_delay_enabled(aec_core)); + + Config config; + config.Set(new ReportedDelay(false)); + ap->SetExtraOptions(config); + EXPECT_EQ(0, WebRtcAec_reported_delay_enabled(aec_core)); + + // Retains setting after initialization. + EXPECT_EQ(ap->kNoError, ap->Initialize()); + EXPECT_EQ(0, WebRtcAec_reported_delay_enabled(aec_core)); + + config.Set(new ReportedDelay(true)); + ap->SetExtraOptions(config); + EXPECT_EQ(1, WebRtcAec_reported_delay_enabled(aec_core)); + + // Retains setting after initialization. + EXPECT_EQ(ap->kNoError, ap->Initialize()); + EXPECT_EQ(1, WebRtcAec_reported_delay_enabled(aec_core)); +} + } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl_wrapper.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl_wrapper.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl_wrapper.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl_wrapper.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_WRAPPER_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_WRAPPER_H_ - -#include "webrtc/modules/audio_processing/include/audio_processing.h" -#include "webrtc/modules/audio_processing/processing_component.h" - -namespace webrtc { - -class AudioProcessingImpl; -class AudioBuffer; - -class EchoCancellationImplWrapper : public virtual EchoCancellation, - public virtual ProcessingComponent { - public: - static EchoCancellationImplWrapper* Create( - const AudioProcessingImpl* audioproc); - virtual ~EchoCancellationImplWrapper() {} - - virtual int ProcessRenderAudio(const AudioBuffer* audio) = 0; - virtual int ProcessCaptureAudio(AudioBuffer* audio) = 0; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_WRAPPER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_control_mobile_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_control_mobile_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_control_mobile_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_control_mobile_impl.cc 2015-02-03 14:33:35.000000000 +0000 @@ -15,7 +15,6 @@ #include "webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h" #include "webrtc/modules/audio_processing/audio_buffer.h" -#include "webrtc/modules/audio_processing/audio_processing_impl.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/logging.h" @@ -63,9 +62,11 @@ return WebRtcAecm_echo_path_size_bytes(); } -EchoControlMobileImpl::EchoControlMobileImpl(const AudioProcessingImpl* apm) - : ProcessingComponent(apm), +EchoControlMobileImpl::EchoControlMobileImpl(const AudioProcessing* apm, + CriticalSectionWrapper* crit) + : ProcessingComponent(), apm_(apm), + crit_(crit), routing_mode_(kSpeakerphone), comfort_noise_enabled_(true), external_echo_path_(NULL) {} @@ -127,7 +128,7 @@ for (int i = 0; i < audio->num_channels(); i++) { // TODO(ajm): improve how this works, possibly inside AECM. // This is kind of hacked up. - int16_t* noisy = audio->low_pass_reference(i); + const int16_t* noisy = audio->low_pass_reference(i); int16_t* clean = audio->low_pass_split_data(i); if (noisy == NULL) { noisy = clean; @@ -155,7 +156,7 @@ } int EchoControlMobileImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); // Ensure AEC and AECM are not both enabled. if (enable && apm_->echo_cancellation()->is_enabled()) { return apm_->kBadParameterError; @@ -169,7 +170,7 @@ } int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); if (MapSetting(mode) == -1) { return apm_->kBadParameterError; } @@ -184,7 +185,7 @@ } int EchoControlMobileImpl::enable_comfort_noise(bool enable) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); comfort_noise_enabled_ = enable; return Configure(); } @@ -195,7 +196,7 @@ int EchoControlMobileImpl::SetEchoPath(const void* echo_path, size_t size_bytes) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); if (echo_path == NULL) { return apm_->kNullPointerError; } @@ -214,7 +215,7 @@ int EchoControlMobileImpl::GetEchoPath(void* echo_path, size_t size_bytes) const { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); if (echo_path == NULL) { return apm_->kNullPointerError; } @@ -240,7 +241,7 @@ return apm_->kNoError; } - if (apm_->sample_rate_hz() == apm_->kSampleRate32kHz) { + if (apm_->proc_sample_rate_hz() > apm_->kSampleRate16kHz) { LOG(LS_ERROR) << "AECM only supports 16 kHz or lower sample rates"; return apm_->kBadSampleRateError; } @@ -259,14 +260,14 @@ return handle; } -int EchoControlMobileImpl::DestroyHandle(void* handle) const { - return WebRtcAecm_Free(static_cast(handle)); +void EchoControlMobileImpl::DestroyHandle(void* handle) const { + WebRtcAecm_Free(static_cast(handle)); } int EchoControlMobileImpl::InitializeHandle(void* handle) const { assert(handle != NULL); Handle* my_handle = static_cast(handle); - if (WebRtcAecm_Init(my_handle, apm_->sample_rate_hz()) != 0) { + if (WebRtcAecm_Init(my_handle, apm_->proc_sample_rate_hz()) != 0) { return GetHandleError(my_handle); } if (external_echo_path_ != NULL) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_control_mobile_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_control_mobile_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_control_mobile_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/echo_control_mobile_impl.h 2015-02-03 14:33:35.000000000 +0000 @@ -15,13 +15,15 @@ #include "webrtc/modules/audio_processing/processing_component.h" namespace webrtc { -class AudioProcessingImpl; + class AudioBuffer; +class CriticalSectionWrapper; class EchoControlMobileImpl : public EchoControlMobile, public ProcessingComponent { public: - explicit EchoControlMobileImpl(const AudioProcessingImpl* apm); + EchoControlMobileImpl(const AudioProcessing* apm, + CriticalSectionWrapper* crit); virtual ~EchoControlMobileImpl(); int ProcessRenderAudio(const AudioBuffer* audio); @@ -47,11 +49,12 @@ virtual void* CreateHandle() const OVERRIDE; virtual int InitializeHandle(void* handle) const OVERRIDE; virtual int ConfigureHandle(void* handle) const OVERRIDE; - virtual int DestroyHandle(void* handle) const OVERRIDE; + virtual void DestroyHandle(void* handle) const OVERRIDE; virtual int num_handles_required() const OVERRIDE; virtual int GetHandleError(void* handle) const OVERRIDE; - const AudioProcessingImpl* apm_; + const AudioProcessing* apm_; + CriticalSectionWrapper* crit_; RoutingMode routing_mode_; bool comfort_noise_enabled_; unsigned char* external_echo_path_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/gain_control_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/gain_control_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/gain_control_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/gain_control_impl.cc 2015-02-03 14:33:35.000000000 +0000 @@ -12,12 +12,10 @@ #include +#include "webrtc/modules/audio_processing/audio_buffer.h" #include "webrtc/modules/audio_processing/agc/include/gain_control.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/modules/audio_processing/audio_buffer.h" -#include "webrtc/modules/audio_processing/audio_processing_impl.h" - namespace webrtc { typedef void Handle; @@ -37,9 +35,11 @@ } } // namespace -GainControlImpl::GainControlImpl(const AudioProcessingImpl* apm) - : ProcessingComponent(apm), +GainControlImpl::GainControlImpl(const AudioProcessing* apm, + CriticalSectionWrapper* crit) + : ProcessingComponent(), apm_(apm), + crit_(crit), mode_(kAdaptiveAnalog), minimum_capture_level_(0), maximum_capture_level_(255), @@ -59,17 +59,11 @@ assert(audio->samples_per_split_channel() <= 160); - int16_t* mixed_data = audio->low_pass_split_data(0); - if (audio->num_channels() > 1) { - audio->CopyAndMixLowPass(1); - mixed_data = audio->mixed_low_pass_data(0); - } - for (int i = 0; i < num_handles(); i++) { Handle* my_handle = static_cast(handle(i)); int err = WebRtcAgc_AddFarend( my_handle, - mixed_data, + audio->mixed_low_pass_data(), static_cast(audio->samples_per_split_channel())); if (err != apm_->kNoError) { @@ -203,7 +197,7 @@ } int GainControlImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); return EnableComponent(enable); } @@ -212,7 +206,7 @@ } int GainControlImpl::set_mode(Mode mode) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); if (MapSetting(mode) == -1) { return apm_->kBadParameterError; } @@ -227,7 +221,7 @@ int GainControlImpl::set_analog_level_limits(int minimum, int maximum) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); if (minimum < 0) { return apm_->kBadParameterError; } @@ -259,7 +253,7 @@ } int GainControlImpl::set_target_level_dbfs(int level) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); if (level > 31 || level < 0) { return apm_->kBadParameterError; } @@ -273,7 +267,7 @@ } int GainControlImpl::set_compression_gain_db(int gain) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); if (gain < 0 || gain > 90) { return apm_->kBadParameterError; } @@ -287,7 +281,7 @@ } int GainControlImpl::enable_limiter(bool enable) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); limiter_enabled_ = enable; return Configure(); } @@ -317,8 +311,8 @@ return handle; } -int GainControlImpl::DestroyHandle(void* handle) const { - return WebRtcAgc_Free(static_cast(handle)); +void GainControlImpl::DestroyHandle(void* handle) const { + WebRtcAgc_Free(static_cast(handle)); } int GainControlImpl::InitializeHandle(void* handle) const { @@ -326,7 +320,7 @@ minimum_capture_level_, maximum_capture_level_, MapSetting(mode_), - apm_->sample_rate_hz()); + apm_->proc_sample_rate_hz()); } int GainControlImpl::ConfigureHandle(void* handle) const { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/gain_control_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/gain_control_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/gain_control_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/gain_control_impl.h 2015-02-03 14:33:35.000000000 +0000 @@ -17,13 +17,15 @@ #include "webrtc/modules/audio_processing/processing_component.h" namespace webrtc { -class AudioProcessingImpl; + class AudioBuffer; +class CriticalSectionWrapper; class GainControlImpl : public GainControl, public ProcessingComponent { public: - explicit GainControlImpl(const AudioProcessingImpl* apm); + GainControlImpl(const AudioProcessing* apm, + CriticalSectionWrapper* crit); virtual ~GainControlImpl(); int ProcessRenderAudio(AudioBuffer* audio); @@ -58,11 +60,12 @@ virtual void* CreateHandle() const OVERRIDE; virtual int InitializeHandle(void* handle) const OVERRIDE; virtual int ConfigureHandle(void* handle) const OVERRIDE; - virtual int DestroyHandle(void* handle) const OVERRIDE; + virtual void DestroyHandle(void* handle) const OVERRIDE; virtual int num_handles_required() const OVERRIDE; virtual int GetHandleError(void* handle) const OVERRIDE; - const AudioProcessingImpl* apm_; + const AudioProcessing* apm_; + CriticalSectionWrapper* crit_; Mode mode_; int minimum_capture_level_; int maximum_capture_level_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/high_pass_filter_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/high_pass_filter_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/high_pass_filter_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/high_pass_filter_impl.cc 2015-02-03 14:33:35.000000000 +0000 @@ -13,11 +13,10 @@ #include #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/modules/audio_processing/audio_buffer.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/typedefs.h" -#include "webrtc/modules/audio_processing/audio_buffer.h" -#include "webrtc/modules/audio_processing/audio_processing_impl.h" namespace webrtc { namespace { @@ -36,7 +35,7 @@ int InitializeFilter(FilterState* hpf, int sample_rate_hz) { assert(hpf != NULL); - if (sample_rate_hz == AudioProcessingImpl::kSampleRate8kHz) { + if (sample_rate_hz == AudioProcessing::kSampleRate8kHz) { hpf->ba = kFilterCoefficients8kHz; } else { hpf->ba = kFilterCoefficients; @@ -83,8 +82,8 @@ y[2] = y[0]; y[3] = y[1]; y[0] = static_cast(tmp_int32 >> 13); - y[1] = static_cast((tmp_int32 - - WEBRTC_SPL_LSHIFT_W32(static_cast(y[0]), 13)) << 2); + y[1] = static_cast( + (tmp_int32 - (static_cast(y[0]) << 13)) << 2); // Rounding in Q12, i.e. add 2^11 tmp_int32 += 2048; @@ -94,9 +93,8 @@ tmp_int32, static_cast(-134217728)); - // Convert back to Q0 and use rounding - data[i] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp_int32, 12); - + // Convert back to Q0 and use rounding. + data[i] = (int16_t)(tmp_int32 >> 12); } return AudioProcessing::kNoError; @@ -105,9 +103,11 @@ typedef FilterState Handle; -HighPassFilterImpl::HighPassFilterImpl(const AudioProcessingImpl* apm) - : ProcessingComponent(apm), - apm_(apm) {} +HighPassFilterImpl::HighPassFilterImpl(const AudioProcessing* apm, + CriticalSectionWrapper* crit) + : ProcessingComponent(), + apm_(apm), + crit_(crit) {} HighPassFilterImpl::~HighPassFilterImpl() {} @@ -135,7 +135,7 @@ } int HighPassFilterImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); return EnableComponent(enable); } @@ -147,14 +147,13 @@ return new FilterState; } -int HighPassFilterImpl::DestroyHandle(void* handle) const { +void HighPassFilterImpl::DestroyHandle(void* handle) const { delete static_cast(handle); - return apm_->kNoError; } int HighPassFilterImpl::InitializeHandle(void* handle) const { return InitializeFilter(static_cast(handle), - apm_->sample_rate_hz()); + apm_->proc_sample_rate_hz()); } int HighPassFilterImpl::ConfigureHandle(void* /*handle*/) const { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/high_pass_filter_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/high_pass_filter_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/high_pass_filter_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/high_pass_filter_impl.h 2015-02-03 14:33:35.000000000 +0000 @@ -15,13 +15,14 @@ #include "webrtc/modules/audio_processing/processing_component.h" namespace webrtc { -class AudioProcessingImpl; + class AudioBuffer; +class CriticalSectionWrapper; class HighPassFilterImpl : public HighPassFilter, public ProcessingComponent { public: - explicit HighPassFilterImpl(const AudioProcessingImpl* apm); + HighPassFilterImpl(const AudioProcessing* apm, CriticalSectionWrapper* crit); virtual ~HighPassFilterImpl(); int ProcessCaptureAudio(AudioBuffer* audio); @@ -37,11 +38,12 @@ virtual void* CreateHandle() const OVERRIDE; virtual int InitializeHandle(void* handle) const OVERRIDE; virtual int ConfigureHandle(void* handle) const OVERRIDE; - virtual int DestroyHandle(void* handle) const OVERRIDE; + virtual void DestroyHandle(void* handle) const OVERRIDE; virtual int num_handles_required() const OVERRIDE; virtual int GetHandleError(void* handle) const OVERRIDE; - const AudioProcessingImpl* apm_; + const AudioProcessing* apm_; + CriticalSectionWrapper* crit_; }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/include/audio_processing.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/include/audio_processing.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/include/audio_processing.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/include/audio_processing.h 2015-02-03 14:33:35.000000000 +0000 @@ -14,6 +14,7 @@ #include // size_t #include // FILE +#include "webrtc/base/platform_file.h" #include "webrtc/common.h" #include "webrtc/typedefs.h" @@ -53,6 +54,18 @@ bool enabled; }; +// Use to disable the reported system delays. By disabling the reported system +// delays the echo cancellation algorithm assumes the process and reverse +// streams to be aligned. This configuration only applies to EchoCancellation +// and not EchoControlMobile and is set with AudioProcessing::SetExtraOptions(). +// Note that by disabling reported system delays the EchoCancellation may +// regress in performance. +struct ReportedDelay { + ReportedDelay() : enabled(true) {} + explicit ReportedDelay(bool enabled) : enabled(enabled) {} + bool enabled; +}; + // Must be provided through AudioProcessing::Create(Confg&). It will have no // impact if used with AudioProcessing::SetExtraOptions(). struct ExperimentalAgc { @@ -61,6 +74,16 @@ bool enabled; }; +// Use to enable experimental noise suppression. It can be set in the +// constructor or using AudioProcessing::SetExtraOptions(). +struct ExperimentalNs { + ExperimentalNs() : enabled(false) {} + explicit ExperimentalNs(bool enabled) : enabled(enabled) {} + bool enabled; +}; + +static const int kAudioProcMaxNativeSampleRateHz = 32000; + // The Audio Processing Module (APM) provides a collection of voice processing // components designed for real-time communications software. // @@ -90,8 +113,9 @@ // 2. Parameter getters are never called concurrently with the corresponding // setter. // -// APM accepts only 16-bit linear PCM audio data in frames of 10 ms. Multiple -// channels should be interleaved. +// APM accepts only linear PCM audio data in chunks of 10 ms. The int16 +// interfaces use interleaved data, while the float interfaces use deinterleaved +// data. // // Usage example, omitting error checking: // AudioProcessing* apm = AudioProcessing::Create(0); @@ -135,6 +159,16 @@ // class AudioProcessing { public: + enum ChannelLayout { + kMono, + // Left, right. + kStereo, + // Mono, keyboard mic. + kMonoAndKeyboard, + // Left, right, keyboard mic. + kStereoAndKeyboard + }; + // Creates an APM instance. Use one instance for every primary audio stream // requiring processing. On the client-side, this would typically be one // instance for the near-end stream, and additional instances for each far-end @@ -150,39 +184,47 @@ // Initializes internal states, while retaining all user settings. This // should be called before beginning to process a new audio stream. However, // it is not necessary to call before processing the first stream after - // creation. It is also not necessary to call if the audio parameters (sample + // creation. + // + // It is also not necessary to call if the audio parameters (sample // rate and number of channels) have changed. Passing updated parameters // directly to |ProcessStream()| and |AnalyzeReverseStream()| is permissible. + // If the parameters are known at init-time though, they may be provided. virtual int Initialize() = 0; + // The int16 interfaces require: + // - only |NativeRate|s be used + // - that the input, output and reverse rates must match + // - that |output_layout| matches |input_layout| + // + // The float interfaces accept arbitrary rates and support differing input + // and output layouts, but the output may only remove channels, not add. + virtual int Initialize(int input_sample_rate_hz, + int output_sample_rate_hz, + int reverse_sample_rate_hz, + ChannelLayout input_layout, + ChannelLayout output_layout, + ChannelLayout reverse_layout) = 0; + // Pass down additional options which don't have explicit setters. This // ensures the options are applied immediately. virtual void SetExtraOptions(const Config& config) = 0; - virtual int EnableExperimentalNs(bool enable) = 0; - virtual bool experimental_ns_enabled() const = 0; - - // DEPRECATED: It is now possible to modify the sample rate directly in a call - // to |ProcessStream|. - // Sets the sample |rate| in Hz for both the primary and reverse audio - // streams. 8000, 16000 or 32000 Hz are permitted. + // DEPRECATED. + // TODO(ajm): Remove after Chromium has upgraded to using Initialize(). virtual int set_sample_rate_hz(int rate) = 0; + // TODO(ajm): Remove after voice engine no longer requires it to resample + // the reverse stream to the forward rate. + virtual int input_sample_rate_hz() const = 0; + // TODO(ajm): Remove after Chromium no longer depends on it. virtual int sample_rate_hz() const = 0; - // DEPRECATED: It is now possible to modify the number of channels directly in - // a call to |ProcessStream|. - // Sets the number of channels for the primary audio stream. Input frames must - // contain a number of channels given by |input_channels|, while output frames - // will be returned with number of channels given by |output_channels|. - virtual int set_num_channels(int input_channels, int output_channels) = 0; + // TODO(ajm): Only intended for internal use. Make private and friend the + // necessary classes? + virtual int proc_sample_rate_hz() const = 0; + virtual int proc_split_sample_rate_hz() const = 0; virtual int num_input_channels() const = 0; virtual int num_output_channels() const = 0; - - // DEPRECATED: It is now possible to modify the number of channels directly in - // a call to |AnalyzeReverseStream|. - // Sets the number of channels for the reverse audio stream. Input frames must - // contain a number of channels given by |channels|. - virtual int set_num_reverse_channels(int channels) = 0; virtual int num_reverse_channels() const = 0; // Set to true when the output of AudioProcessing will be muted or in some @@ -204,6 +246,21 @@ // method, it will trigger an initialization. virtual int ProcessStream(AudioFrame* frame) = 0; + // Accepts deinterleaved float audio with the range [-1, 1]. Each element + // of |src| points to a channel buffer, arranged according to + // |input_layout|. At output, the channels will be arranged according to + // |output_layout| at |output_sample_rate_hz| in |dest|. + // + // The output layout may only remove channels, not add. |src| and |dest| + // may use the same memory, if desired. + virtual int ProcessStream(const float* const* src, + int samples_per_channel, + int input_sample_rate_hz, + ChannelLayout input_layout, + int output_sample_rate_hz, + ChannelLayout output_layout, + float* const* dest) = 0; + // Analyzes a 10 ms |frame| of the reverse direction audio stream. The frame // will not be modified. On the client-side, this is the far-end (or to be // rendered) audio. @@ -216,11 +273,18 @@ // // The |sample_rate_hz_|, |num_channels_|, and |samples_per_channel_| // members of |frame| must be valid. |sample_rate_hz_| must correspond to - // |sample_rate_hz()| + // |input_sample_rate_hz()| // // TODO(ajm): add const to input; requires an implementation fix. virtual int AnalyzeReverseStream(AudioFrame* frame) = 0; + // Accepts deinterleaved float audio with the range [-1, 1]. Each element + // of |data| points to a channel buffer, arranged according to |layout|. + virtual int AnalyzeReverseStream(const float* const* data, + int samples_per_channel, + int sample_rate_hz, + ChannelLayout layout) = 0; + // This must be called if and only if echo processing is enabled. // // Sets the |delay| in ms between AnalyzeReverseStream() receiving a far-end @@ -236,6 +300,7 @@ // ProcessStream(). virtual int set_stream_delay_ms(int delay) = 0; virtual int stream_delay_ms() const = 0; + virtual bool was_stream_delay_set() const = 0; // Call to signal that a key press occurred (true) or did not occur (false) // with this chunk of audio. @@ -261,6 +326,13 @@ // of |handle| and closes it at StopDebugRecording(). virtual int StartDebugRecording(FILE* handle) = 0; + // Same as above but uses an existing PlatformFile handle. Takes ownership + // of |handle| and closes it at StopDebugRecording(). + // TODO(xians): Make this interface pure virtual. + virtual int StartDebugRecordingForPlatformFile(rtc::PlatformFile handle) { + return -1; + } + // Stops recording debugging information, and closes the file. Recording // cannot be resumed in the same file (without overwriting it). virtual int StopDebugRecording() = 0; @@ -304,6 +376,14 @@ // will continue, but the parameter may have been truncated. kBadStreamParameterWarning = -13 }; + + enum NativeRate { + kSampleRate8kHz = 8000, + kSampleRate16kHz = 16000, + kSampleRate32kHz = 32000 + }; + + static const int kChunkSizeMs = 10; }; // The acoustic echo cancellation (AEC) component provides better performance @@ -324,16 +404,10 @@ // render and capture devices are used, particularly with webcams. // // This enables a compensation mechanism, and requires that - // |set_device_sample_rate_hz()| and |set_stream_drift_samples()| be called. + // set_stream_drift_samples() be called. virtual int enable_drift_compensation(bool enable) = 0; virtual bool is_drift_compensation_enabled() const = 0; - // Provides the sampling rate of the audio devices. It is assumed the render - // and capture devices use the same nominal sample rate. Required if and only - // if drift compensation is enabled. - virtual int set_device_sample_rate_hz(int rate) = 0; - virtual int device_sample_rate_hz() const = 0; - // Sets the difference between the number of samples rendered and captured by // the audio devices since the last call to |ProcessStream()|. Must be called // if drift compensation is enabled, prior to |ProcessStream()|. @@ -573,8 +647,7 @@ // frames since the last call to RMS(). The returned value is positive but // should be interpreted as negative. It is constrained to [0, 127]. // - // The computation follows: - // http://tools.ietf.org/html/draft-ietf-avtext-client-to-mixer-audio-level-05 + // The computation follows: https://tools.ietf.org/html/rfc6465 // with the intent that it can provide the RTP audio level indication. // // Frames passed to ProcessStream() with an |_energy| of zero are considered diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/include/mock_audio_processing.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/include/mock_audio_processing.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/include/mock_audio_processing.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/include/mock_audio_processing.h 2015-02-03 14:33:35.000000000 +0000 @@ -26,10 +26,6 @@ int(bool enable)); MOCK_CONST_METHOD0(is_drift_compensation_enabled, bool()); - MOCK_METHOD1(set_device_sample_rate_hz, - int(int rate)); - MOCK_CONST_METHOD0(device_sample_rate_hz, - int()); MOCK_METHOD1(set_stream_drift_samples, void(int drift)); MOCK_CONST_METHOD0(stream_drift_samples, @@ -181,24 +177,29 @@ MOCK_METHOD0(Initialize, int()); + MOCK_METHOD6(Initialize, + int(int sample_rate_hz, + int output_sample_rate_hz, + int reverse_sample_rate_hz, + ChannelLayout input_layout, + ChannelLayout output_layout, + ChannelLayout reverse_layout)); MOCK_METHOD1(SetExtraOptions, void(const Config& config)); - MOCK_METHOD1(EnableExperimentalNs, - int(bool enable)); - MOCK_CONST_METHOD0(experimental_ns_enabled, - bool()); MOCK_METHOD1(set_sample_rate_hz, int(int rate)); + MOCK_CONST_METHOD0(input_sample_rate_hz, + int()); MOCK_CONST_METHOD0(sample_rate_hz, int()); - MOCK_METHOD2(set_num_channels, - int(int input_channels, int output_channels)); + MOCK_CONST_METHOD0(proc_sample_rate_hz, + int()); + MOCK_CONST_METHOD0(proc_split_sample_rate_hz, + int()); MOCK_CONST_METHOD0(num_input_channels, int()); MOCK_CONST_METHOD0(num_output_channels, int()); - MOCK_METHOD1(set_num_reverse_channels, - int(int channels)); MOCK_CONST_METHOD0(num_reverse_channels, int()); MOCK_METHOD1(set_output_will_be_muted, @@ -207,12 +208,25 @@ bool()); MOCK_METHOD1(ProcessStream, int(AudioFrame* frame)); + MOCK_METHOD7(ProcessStream, + int(const float* const* src, + int samples_per_channel, + int input_sample_rate_hz, + ChannelLayout input_layout, + int output_sample_rate_hz, + ChannelLayout output_layout, + float* const* dest)); MOCK_METHOD1(AnalyzeReverseStream, int(AudioFrame* frame)); + MOCK_METHOD4(AnalyzeReverseStream, + int(const float* const* data, int frames, int sample_rate_hz, + ChannelLayout input_layout)); MOCK_METHOD1(set_stream_delay_ms, int(int delay)); MOCK_CONST_METHOD0(stream_delay_ms, int()); + MOCK_CONST_METHOD0(was_stream_delay_set, + bool()); MOCK_METHOD1(set_stream_key_pressed, void(bool key_pressed)); MOCK_CONST_METHOD0(stream_key_pressed, @@ -238,20 +252,16 @@ } virtual MockHighPassFilter* high_pass_filter() const { return high_pass_filter_.get(); - }; + } virtual MockLevelEstimator* level_estimator() const { return level_estimator_.get(); - }; + } virtual MockNoiseSuppression* noise_suppression() const { return noise_suppression_.get(); - }; + } virtual MockVoiceDetection* voice_detection() const { return voice_detection_.get(); - }; - MOCK_METHOD0(TimeUntilNextProcess, - int32_t()); - MOCK_METHOD0(Process, - int32_t()); + } private: scoped_ptr echo_cancellation_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/level_estimator_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/level_estimator_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/level_estimator_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/level_estimator_impl.cc 2015-02-03 14:33:35.000000000 +0000 @@ -10,110 +10,35 @@ #include "webrtc/modules/audio_processing/level_estimator_impl.h" -#include -#include -#include - #include "webrtc/modules/audio_processing/audio_buffer.h" -#include "webrtc/modules/audio_processing/audio_processing_impl.h" +#include "webrtc/modules/audio_processing/include/audio_processing.h" +#include "webrtc/modules/audio_processing/rms_level.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" namespace webrtc { -namespace { - -const double kMaxSquaredLevel = 32768.0 * 32768.0; -class Level { - public: - static const int kMinLevel = 127; - - Level() - : sum_square_(0.0), - sample_count_(0) {} - ~Level() {} - - void Init() { - sum_square_ = 0.0; - sample_count_ = 0; - } - - void Process(int16_t* data, int length) { - assert(data != NULL); - assert(length > 0); - sum_square_ += SumSquare(data, length); - sample_count_ += length; - } - - void ProcessMuted(int length) { - assert(length > 0); - sample_count_ += length; - } - - int RMS() { - if (sample_count_ == 0 || sum_square_ == 0.0) { - Init(); - return kMinLevel; - } - - // Normalize by the max level. - double rms = sum_square_ / (sample_count_ * kMaxSquaredLevel); - // 20log_10(x^0.5) = 10log_10(x) - rms = 10 * log10(rms); - if (rms > 0) - rms = 0; - else if (rms < -kMinLevel) - rms = -kMinLevel; - - rms = -rms; - Init(); - return static_cast(rms + 0.5); - } - - private: - static double SumSquare(int16_t* data, int length) { - double sum_square = 0.0; - for (int i = 0; i < length; ++i) { - double data_d = static_cast(data[i]); - sum_square += data_d * data_d; - } - return sum_square; - } - - double sum_square_; - int sample_count_; -}; -} // namespace - -LevelEstimatorImpl::LevelEstimatorImpl(const AudioProcessingImpl* apm) - : ProcessingComponent(apm), - apm_(apm) {} +LevelEstimatorImpl::LevelEstimatorImpl(const AudioProcessing* apm, + CriticalSectionWrapper* crit) + : ProcessingComponent(), + crit_(crit) {} LevelEstimatorImpl::~LevelEstimatorImpl() {} int LevelEstimatorImpl::ProcessStream(AudioBuffer* audio) { if (!is_component_enabled()) { - return apm_->kNoError; - } - - Level* level = static_cast(handle(0)); - if (audio->is_muted()) { - level->ProcessMuted(audio->samples_per_channel()); - return apm_->kNoError; + return AudioProcessing::kNoError; } - int16_t* mixed_data = audio->data(0); - if (audio->num_channels() > 1) { - audio->CopyAndMix(1); - mixed_data = audio->mixed_data(0); + RMSLevel* rms_level = static_cast(handle(0)); + for (int i = 0; i < audio->num_channels(); ++i) { + rms_level->Process(audio->data(i), audio->samples_per_channel()); } - level->Process(mixed_data, audio->samples_per_channel()); - - return apm_->kNoError; + return AudioProcessing::kNoError; } int LevelEstimatorImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); return EnableComponent(enable); } @@ -123,43 +48,38 @@ int LevelEstimatorImpl::RMS() { if (!is_component_enabled()) { - return apm_->kNotEnabledError; + return AudioProcessing::kNotEnabledError; } - Level* level = static_cast(handle(0)); - return level->RMS(); + RMSLevel* rms_level = static_cast(handle(0)); + return rms_level->RMS(); } +// The ProcessingComponent implementation is pretty weird in this class since +// we have only a single instance of the trivial underlying component. void* LevelEstimatorImpl::CreateHandle() const { - return new Level; + return new RMSLevel; } -int LevelEstimatorImpl::DestroyHandle(void* handle) const { - assert(handle != NULL); - Level* level = static_cast(handle); - delete level; - return apm_->kNoError; +void LevelEstimatorImpl::DestroyHandle(void* handle) const { + delete static_cast(handle); } int LevelEstimatorImpl::InitializeHandle(void* handle) const { - assert(handle != NULL); - Level* level = static_cast(handle); - level->Init(); - - return apm_->kNoError; + static_cast(handle)->Reset(); + return AudioProcessing::kNoError; } int LevelEstimatorImpl::ConfigureHandle(void* /*handle*/) const { - return apm_->kNoError; + return AudioProcessing::kNoError; } int LevelEstimatorImpl::num_handles_required() const { return 1; } -int LevelEstimatorImpl::GetHandleError(void* handle) const { - // The component has no detailed errors. - assert(handle != NULL); - return apm_->kUnspecifiedError; +int LevelEstimatorImpl::GetHandleError(void* /*handle*/) const { + return AudioProcessing::kUnspecifiedError; } + } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/level_estimator_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/level_estimator_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/level_estimator_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/level_estimator_impl.h 2015-02-03 14:33:35.000000000 +0000 @@ -13,15 +13,18 @@ #include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/modules/audio_processing/processing_component.h" +#include "webrtc/modules/audio_processing/rms_level.h" namespace webrtc { -class AudioProcessingImpl; + class AudioBuffer; +class CriticalSectionWrapper; class LevelEstimatorImpl : public LevelEstimator, public ProcessingComponent { public: - explicit LevelEstimatorImpl(const AudioProcessingImpl* apm); + LevelEstimatorImpl(const AudioProcessing* apm, + CriticalSectionWrapper* crit); virtual ~LevelEstimatorImpl(); int ProcessStream(AudioBuffer* audio); @@ -38,12 +41,13 @@ virtual void* CreateHandle() const OVERRIDE; virtual int InitializeHandle(void* handle) const OVERRIDE; virtual int ConfigureHandle(void* handle) const OVERRIDE; - virtual int DestroyHandle(void* handle) const OVERRIDE; + virtual void DestroyHandle(void* handle) const OVERRIDE; virtual int num_handles_required() const OVERRIDE; virtual int GetHandleError(void* handle) const OVERRIDE; - const AudioProcessingImpl* apm_; + CriticalSectionWrapper* crit_; }; + } // namespace webrtc #endif // WEBRTC_MODULES_AUDIO_PROCESSING_LEVEL_ESTIMATOR_IMPL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/lib_core_neon_offsets.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/lib_core_neon_offsets.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/lib_core_neon_offsets.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/lib_core_neon_offsets.gypi 2015-02-03 14:33:35.000000000 +0000 @@ -14,7 +14,7 @@ 'lib_intermediate_name': '', 'conditions' : [ ['android_webview_build==1', { - 'lib_intermediate_name' : '<(android_src)/$(call intermediates-dir-for, STATIC_LIBRARIES, lib_core_neon_offsets)/lib_core_neon_offsets.a', + 'lib_intermediate_name' : '$(abspath $(call intermediates-dir-for,STATIC_LIBRARIES,lib_core_neon_offsets,,,$(gyp_var_prefix)))/lib_core_neon_offsets.a', }], ], }, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/noise_suppression_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/noise_suppression_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/noise_suppression_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/noise_suppression_impl.cc 2015-02-03 14:33:35.000000000 +0000 @@ -12,15 +12,14 @@ #include -#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/modules/audio_processing/audio_buffer.h" #if defined(WEBRTC_NS_FLOAT) #include "webrtc/modules/audio_processing/ns/include/noise_suppression.h" #elif defined(WEBRTC_NS_FIXED) #include "webrtc/modules/audio_processing/ns/include/noise_suppression_x.h" #endif +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/modules/audio_processing/audio_buffer.h" -#include "webrtc/modules/audio_processing/audio_processing_impl.h" namespace webrtc { @@ -47,13 +46,36 @@ } } // namespace -NoiseSuppressionImpl::NoiseSuppressionImpl(const AudioProcessingImpl* apm) - : ProcessingComponent(apm), +NoiseSuppressionImpl::NoiseSuppressionImpl(const AudioProcessing* apm, + CriticalSectionWrapper* crit) + : ProcessingComponent(), apm_(apm), + crit_(crit), level_(kModerate) {} NoiseSuppressionImpl::~NoiseSuppressionImpl() {} +int NoiseSuppressionImpl::AnalyzeCaptureAudio(AudioBuffer* audio) { +#if defined(WEBRTC_NS_FLOAT) + if (!is_component_enabled()) { + return apm_->kNoError; + } + assert(audio->samples_per_split_channel() <= 160); + assert(audio->num_channels() == num_handles()); + + for (int i = 0; i < num_handles(); ++i) { + Handle* my_handle = static_cast(handle(i)); + + int err = WebRtcNs_Analyze(my_handle, + audio->low_pass_split_data_f(i)); + if (err != apm_->kNoError) { + return GetHandleError(my_handle); + } + } +#endif + return apm_->kNoError; +} + int NoiseSuppressionImpl::ProcessCaptureAudio(AudioBuffer* audio) { int err = apm_->kNoError; @@ -63,16 +85,16 @@ assert(audio->samples_per_split_channel() <= 160); assert(audio->num_channels() == num_handles()); - for (int i = 0; i < num_handles(); i++) { + for (int i = 0; i < num_handles(); ++i) { Handle* my_handle = static_cast(handle(i)); #if defined(WEBRTC_NS_FLOAT) - err = WebRtcNs_Process(static_cast(handle(i)), - audio->low_pass_split_data(i), - audio->high_pass_split_data(i), - audio->low_pass_split_data(i), - audio->high_pass_split_data(i)); + err = WebRtcNs_Process(my_handle, + audio->low_pass_split_data_f(i), + audio->high_pass_split_data_f(i), + audio->low_pass_split_data_f(i), + audio->high_pass_split_data_f(i)); #elif defined(WEBRTC_NS_FIXED) - err = WebRtcNsx_Process(static_cast(handle(i)), + err = WebRtcNsx_Process(my_handle, audio->low_pass_split_data(i), audio->high_pass_split_data(i), audio->low_pass_split_data(i), @@ -88,7 +110,7 @@ } int NoiseSuppressionImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); return EnableComponent(enable); } @@ -97,7 +119,7 @@ } int NoiseSuppressionImpl::set_level(Level level) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); if (MapSetting(level) == -1) { return apm_->kBadParameterError; } @@ -140,19 +162,21 @@ return handle; } -int NoiseSuppressionImpl::DestroyHandle(void* handle) const { +void NoiseSuppressionImpl::DestroyHandle(void* handle) const { #if defined(WEBRTC_NS_FLOAT) - return WebRtcNs_Free(static_cast(handle)); + WebRtcNs_Free(static_cast(handle)); #elif defined(WEBRTC_NS_FIXED) - return WebRtcNsx_Free(static_cast(handle)); + WebRtcNsx_Free(static_cast(handle)); #endif } int NoiseSuppressionImpl::InitializeHandle(void* handle) const { #if defined(WEBRTC_NS_FLOAT) - return WebRtcNs_Init(static_cast(handle), apm_->sample_rate_hz()); + return WebRtcNs_Init(static_cast(handle), + apm_->proc_sample_rate_hz()); #elif defined(WEBRTC_NS_FIXED) - return WebRtcNsx_Init(static_cast(handle), apm_->sample_rate_hz()); + return WebRtcNsx_Init(static_cast(handle), + apm_->proc_sample_rate_hz()); #endif } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/noise_suppression_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/noise_suppression_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/noise_suppression_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/noise_suppression_impl.h 2015-02-03 14:33:35.000000000 +0000 @@ -15,15 +15,18 @@ #include "webrtc/modules/audio_processing/processing_component.h" namespace webrtc { -class AudioProcessingImpl; + class AudioBuffer; +class CriticalSectionWrapper; class NoiseSuppressionImpl : public NoiseSuppression, public ProcessingComponent { public: - explicit NoiseSuppressionImpl(const AudioProcessingImpl* apm); + NoiseSuppressionImpl(const AudioProcessing* apm, + CriticalSectionWrapper* crit); virtual ~NoiseSuppressionImpl(); + int AnalyzeCaptureAudio(AudioBuffer* audio); int ProcessCaptureAudio(AudioBuffer* audio); // NoiseSuppression implementation. @@ -40,13 +43,15 @@ virtual void* CreateHandle() const OVERRIDE; virtual int InitializeHandle(void* handle) const OVERRIDE; virtual int ConfigureHandle(void* handle) const OVERRIDE; - virtual int DestroyHandle(void* handle) const OVERRIDE; + virtual void DestroyHandle(void* handle) const OVERRIDE; virtual int num_handles_required() const OVERRIDE; virtual int GetHandleError(void* handle) const OVERRIDE; - const AudioProcessingImpl* apm_; + const AudioProcessing* apm_; + CriticalSectionWrapper* crit_; Level level_; }; + } // namespace webrtc #endif // WEBRTC_MODULES_AUDIO_PROCESSING_NOISE_SUPPRESSION_IMPL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,95 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -############################# -# Build the non-neon library. -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../android-webrtc.mk - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_ns -LOCAL_MODULE_TAGS := optional -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := \ - noise_suppression_x.c \ - nsx_core.c - -# Files for floating point. -# noise_suppression.c ns_core.c - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../utility \ - $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../../../common_audio/signal_processing/include \ - $(LOCAL_PATH)/../../../system_wrappers/interface \ - external/webrtc - -LOCAL_STATIC_LIBRARIES += libwebrtc_system_wrappers - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) - -############################# -# Build the neon library. -ifeq ($(WEBRTC_BUILD_NEON_LIBS),true) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_ns_neon -LOCAL_MODULE_TAGS := optional -NS_ASM_HEADER := $(intermediates)/ns_core_neon_offsets.h -NS_ASM_HEADER_DIR := $(intermediates) - -# Generate a header file nsx_core_neon_offsets.h which will be included in -# assembly file nsx_core_neon.S, from file nsx_core_neon_offsets.c. -$(NS_ASM_HEADER): $(LOCAL_PATH)/../../../build/generate_asm_header.py \ - $(LOCAL_PATH)/nsx_core_neon_offsets.c - @python $^ --compiler=$(TARGET_CC) --options="$(addprefix -I, \ - $(LOCAL_INCLUDES)) $(addprefix -isystem , $(TARGET_C_INCLUDES)) -S" \ - --dir=$(NS_ASM_HEADER_DIR) - -LOCAL_GENERATED_SOURCES := $(NS_ASM_HEADER) -LOCAL_SRC_FILES := nsx_core_neon.S - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) \ - -mfpu=neon \ - -mfloat-abi=softfp \ - -flax-vector-conversions - -LOCAL_C_INCLUDES := \ - $(NS_ASM_HEADER_DIR) \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../../../common_audio/signal_processing/include \ - external/webrtc - -LOCAL_INCLUDES := $(LOCAL_C_INCLUDES) - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) -endif # ifeq ($(WEBRTC_BUILD_NEON_LIBS),true) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/defines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/defines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/defines.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/defines.h 2015-02-03 14:33:35.000000000 +0000 @@ -11,10 +11,6 @@ #ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_DEFINES_H_ #define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_DEFINES_H_ -//#define PROCESS_FLOW_0 // Use the traditional method. -//#define PROCESS_FLOW_1 // Use traditional with DD estimate of prior SNR. -#define PROCESS_FLOW_2 // Use the new method of speech/noise classification. - #define BLOCKL_MAX 160 // max processing block length: 160 #define ANAL_BLOCKL_MAX 256 // max analysis block length: 256 #define HALF_ANAL_BLOCKL 129 // half max analysis block length + 1 @@ -27,7 +23,6 @@ #define FACTOR (float)40.0 #define WIDTH (float)0.01 -#define SMOOTH (float)0.75 // filter smoothing // Length of fft work arrays. #define IP_LENGTH (ANAL_BLOCKL_MAX >> 1) // must be at least ceil(2 + sqrt(ANAL_BLOCKL_MAX/2)) #define W_LENGTH (ANAL_BLOCKL_MAX >> 1) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/include/noise_suppression.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/include/noise_suppression.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/include/noise_suppression.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/include/noise_suppression.h 2015-02-03 14:33:35.000000000 +0000 @@ -79,6 +79,21 @@ */ int WebRtcNs_set_policy(NsHandle* NS_inst, int mode); +/* + * This functions estimates the background noise for the inserted speech frame. + * The input and output signals should always be 10ms (80 or 160 samples). + * + * Input + * - NS_inst : Noise suppression instance. + * - spframe : Pointer to speech frame buffer for L band + * + * Output: + * - NS_inst : Updated NS instance + * + * Return value : 0 - OK + * -1 - Error + */ +int WebRtcNs_Analyze(NsHandle* NS_inst, float* spframe); /* * This functions does Noise Suppression for the inserted speech frame. The @@ -99,10 +114,10 @@ * -1 - Error */ int WebRtcNs_Process(NsHandle* NS_inst, - short* spframe, - short* spframe_H, - short* outframe, - short* outframe_H); + float* spframe, + float* spframe_H, + float* outframe, + float* outframe_H); /* Returns the internally used prior speech probability of the current frame. * There is a frequency bin based one as well, with which this should not be diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/noise_suppression.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/noise_suppression.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/noise_suppression.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/noise_suppression.c 2015-02-03 14:33:35.000000000 +0000 @@ -42,9 +42,12 @@ return WebRtcNs_set_policy_core((NSinst_t*) NS_inst, mode); } +int WebRtcNs_Analyze(NsHandle* NS_inst, float* spframe) { + return WebRtcNs_AnalyzeCore((NSinst_t*) NS_inst, spframe); +} -int WebRtcNs_Process(NsHandle* NS_inst, short* spframe, short* spframe_H, - short* outframe, short* outframe_H) { +int WebRtcNs_Process(NsHandle* NS_inst, float* spframe, float* spframe_H, + float* outframe, float* outframe_H) { return WebRtcNs_ProcessCore( (NSinst_t*) NS_inst, spframe, spframe_H, outframe, outframe_H); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/ns_core.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/ns_core.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/ns_core.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/ns_core.c 2015-02-03 14:33:35.000000000 +0000 @@ -8,385 +8,375 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include #include #include -//#include #include + #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" #include "webrtc/modules/audio_processing/ns/include/noise_suppression.h" #include "webrtc/modules/audio_processing/ns/ns_core.h" #include "webrtc/modules/audio_processing/ns/windows_private.h" #include "webrtc/modules/audio_processing/utility/fft4g.h" -// Set Feature Extraction Parameters -void WebRtcNs_set_feature_extraction_parameters(NSinst_t* inst) { - //bin size of histogram - inst->featureExtractionParams.binSizeLrt = (float)0.1; - inst->featureExtractionParams.binSizeSpecFlat = (float)0.05; - inst->featureExtractionParams.binSizeSpecDiff = (float)0.1; - - //range of histogram over which lrt threshold is computed - inst->featureExtractionParams.rangeAvgHistLrt = (float)1.0; - - //scale parameters: multiply dominant peaks of the histograms by scale factor to obtain - // thresholds for prior model - inst->featureExtractionParams.factor1ModelPars = (float)1.20; //for lrt and spectral diff - inst->featureExtractionParams.factor2ModelPars = (float)0.9; //for spectral_flatness: - // used when noise is flatter than speech - - //peak limit for spectral flatness (varies between 0 and 1) - inst->featureExtractionParams.thresPosSpecFlat = (float)0.6; - - //limit on spacing of two highest peaks in histogram: spacing determined by bin size - inst->featureExtractionParams.limitPeakSpacingSpecFlat = - 2 * inst->featureExtractionParams.binSizeSpecFlat; - inst->featureExtractionParams.limitPeakSpacingSpecDiff = - 2 * inst->featureExtractionParams.binSizeSpecDiff; - - //limit on relevance of second peak: - inst->featureExtractionParams.limitPeakWeightsSpecFlat = (float)0.5; - inst->featureExtractionParams.limitPeakWeightsSpecDiff = (float)0.5; - - // fluctuation limit of lrt feature - inst->featureExtractionParams.thresFluctLrt = (float)0.05; - - //limit on the max and min values for the feature thresholds - inst->featureExtractionParams.maxLrt = (float)1.0; - inst->featureExtractionParams.minLrt = (float)0.20; - - inst->featureExtractionParams.maxSpecFlat = (float)0.95; - inst->featureExtractionParams.minSpecFlat = (float)0.10; - - inst->featureExtractionParams.maxSpecDiff = (float)1.0; - inst->featureExtractionParams.minSpecDiff = (float)0.16; - - //criteria of weight of histogram peak to accept/reject feature - inst->featureExtractionParams.thresWeightSpecFlat = (int)(0.3 - * (inst->modelUpdatePars[1])); //for spectral flatness - inst->featureExtractionParams.thresWeightSpecDiff = (int)(0.3 - * (inst->modelUpdatePars[1])); //for spectral difference +// Set Feature Extraction Parameters. +static void set_feature_extraction_parameters(NSinst_t* self) { + // Bin size of histogram. + self->featureExtractionParams.binSizeLrt = 0.1f; + self->featureExtractionParams.binSizeSpecFlat = 0.05f; + self->featureExtractionParams.binSizeSpecDiff = 0.1f; + + // Range of histogram over which LRT threshold is computed. + self->featureExtractionParams.rangeAvgHistLrt = 1.f; + + // Scale parameters: multiply dominant peaks of the histograms by scale factor + // to obtain thresholds for prior model. + // For LRT and spectral difference. + self->featureExtractionParams.factor1ModelPars = 1.2f; + // For spectral_flatness: used when noise is flatter than speech. + self->featureExtractionParams.factor2ModelPars = 0.9f; + + // Peak limit for spectral flatness (varies between 0 and 1). + self->featureExtractionParams.thresPosSpecFlat = 0.6f; + + // Limit on spacing of two highest peaks in histogram: spacing determined by + // bin size. + self->featureExtractionParams.limitPeakSpacingSpecFlat = + 2 * self->featureExtractionParams.binSizeSpecFlat; + self->featureExtractionParams.limitPeakSpacingSpecDiff = + 2 * self->featureExtractionParams.binSizeSpecDiff; + + // Limit on relevance of second peak. + self->featureExtractionParams.limitPeakWeightsSpecFlat = 0.5f; + self->featureExtractionParams.limitPeakWeightsSpecDiff = 0.5f; + + // Fluctuation limit of LRT feature. + self->featureExtractionParams.thresFluctLrt = 0.05f; + + // Limit on the max and min values for the feature thresholds. + self->featureExtractionParams.maxLrt = 1.f; + self->featureExtractionParams.minLrt = 0.2f; + + self->featureExtractionParams.maxSpecFlat = 0.95f; + self->featureExtractionParams.minSpecFlat = 0.1f; + + self->featureExtractionParams.maxSpecDiff = 1.f; + self->featureExtractionParams.minSpecDiff = 0.16f; + + // Criteria of weight of histogram peak to accept/reject feature. + self->featureExtractionParams.thresWeightSpecFlat = + (int)(0.3 * (self->modelUpdatePars[1])); // For spectral flatness. + self->featureExtractionParams.thresWeightSpecDiff = + (int)(0.3 * (self->modelUpdatePars[1])); // For spectral difference. } -// Initialize state -int WebRtcNs_InitCore(NSinst_t* inst, uint32_t fs) { +// Initialize state. +int WebRtcNs_InitCore(NSinst_t* self, uint32_t fs) { int i; - //We only support 10ms frames - - //check for valid pointer - if (inst == NULL) { + // Check for valid pointer. + if (self == NULL) { return -1; } - // Initialization of struct + // Initialization of struct. if (fs == 8000 || fs == 16000 || fs == 32000) { - inst->fs = fs; + self->fs = fs; } else { return -1; } - inst->windShift = 0; + self->windShift = 0; if (fs == 8000) { - // We only support 10ms frames - inst->blockLen = 80; - inst->blockLen10ms = 80; - inst->anaLen = 128; - inst->window = kBlocks80w128; - inst->outLen = 0; + // We only support 10ms frames. + self->blockLen = 80; + self->anaLen = 128; + self->window = kBlocks80w128; } else if (fs == 16000) { - // We only support 10ms frames - inst->blockLen = 160; - inst->blockLen10ms = 160; - inst->anaLen = 256; - inst->window = kBlocks160w256; - inst->outLen = 0; + // We only support 10ms frames. + self->blockLen = 160; + self->anaLen = 256; + self->window = kBlocks160w256; } else if (fs == 32000) { - // We only support 10ms frames - inst->blockLen = 160; - inst->blockLen10ms = 160; - inst->anaLen = 256; - inst->window = kBlocks160w256; - inst->outLen = 0; - } - inst->magnLen = inst->anaLen / 2 + 1; // Number of frequency bins - - // Initialize fft work arrays. - inst->ip[0] = 0; // Setting this triggers initialization. - memset(inst->dataBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX); - WebRtc_rdft(inst->anaLen, 1, inst->dataBuf, inst->ip, inst->wfft); - - memset(inst->dataBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX); - memset(inst->syntBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX); + // We only support 10ms frames. + self->blockLen = 160; + self->anaLen = 256; + self->window = kBlocks160w256; + } + self->magnLen = self->anaLen / 2 + 1; // Number of frequency bins. + + // Initialize FFT work arrays. + self->ip[0] = 0; // Setting this triggers initialization. + memset(self->dataBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX); + WebRtc_rdft(self->anaLen, 1, self->dataBuf, self->ip, self->wfft); + + memset(self->analyzeBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX); + memset(self->dataBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX); + memset(self->syntBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX); - //for HB processing - memset(inst->dataBufHB, 0, sizeof(float) * ANAL_BLOCKL_MAX); + // For HB processing. + memset(self->dataBufHB, 0, sizeof(float) * ANAL_BLOCKL_MAX); - //for quantile noise estimation - memset(inst->quantile, 0, sizeof(float) * HALF_ANAL_BLOCKL); + // For quantile noise estimation. + memset(self->quantile, 0, sizeof(float) * HALF_ANAL_BLOCKL); for (i = 0; i < SIMULT * HALF_ANAL_BLOCKL; i++) { - inst->lquantile[i] = (float)8.0; - inst->density[i] = (float)0.3; + self->lquantile[i] = 8.f; + self->density[i] = 0.3f; } for (i = 0; i < SIMULT; i++) { - inst->counter[i] = (int)floor((float)(END_STARTUP_LONG * (i + 1)) / (float)SIMULT); + self->counter[i] = + (int)floor((float)(END_STARTUP_LONG * (i + 1)) / (float)SIMULT); } - inst->updates = 0; + self->updates = 0; - // Wiener filter initialization + // Wiener filter initialization. for (i = 0; i < HALF_ANAL_BLOCKL; i++) { - inst->smooth[i] = (float)1.0; + self->smooth[i] = 1.f; } - // Set the aggressiveness: default - inst->aggrMode = 0; + // Set the aggressiveness: default. + self->aggrMode = 0; - //initialize variables for new method - inst->priorSpeechProb = (float)0.5; //prior prob for speech/noise + // Initialize variables for new method. + self->priorSpeechProb = 0.5f; // Prior prob for speech/noise. + // Previous analyze mag spectrum. + memset(self->magnPrevAnalyze, 0, sizeof(float) * HALF_ANAL_BLOCKL); + // Previous process mag spectrum. + memset(self->magnPrevProcess, 0, sizeof(float) * HALF_ANAL_BLOCKL); + // Current noise-spectrum. + memset(self->noise, 0, sizeof(float) * HALF_ANAL_BLOCKL); + // Previous noise-spectrum. + memset(self->noisePrev, 0, sizeof(float) * HALF_ANAL_BLOCKL); + // Conservative noise spectrum estimate. + memset(self->magnAvgPause, 0, sizeof(float) * HALF_ANAL_BLOCKL); + // For estimation of HB in second pass. + memset(self->speechProb, 0, sizeof(float) * HALF_ANAL_BLOCKL); + // Initial average magnitude spectrum. + memset(self->initMagnEst, 0, sizeof(float) * HALF_ANAL_BLOCKL); for (i = 0; i < HALF_ANAL_BLOCKL; i++) { - inst->magnPrev[i] = (float)0.0; //previous mag spectrum - inst->noisePrev[i] = (float)0.0; //previous noise-spectrum - inst->logLrtTimeAvg[i] = LRT_FEATURE_THR; //smooth LR ratio (same as threshold) - inst->magnAvgPause[i] = (float)0.0; //conservative noise spectrum estimate - inst->speechProbHB[i] = (float)0.0; //for estimation of HB in second pass - inst->initMagnEst[i] = (float)0.0; //initial average mag spectrum - } - - //feature quantities - inst->featureData[0] = SF_FEATURE_THR; //spectral flatness (start on threshold) - inst->featureData[1] = (float)0.0; //spectral entropy: not used in this version - inst->featureData[2] = (float)0.0; //spectral variance: not used in this version - inst->featureData[3] = LRT_FEATURE_THR; //average lrt factor (start on threshold) - inst->featureData[4] = SF_FEATURE_THR; //spectral template diff (start on threshold) - inst->featureData[5] = (float)0.0; //normalization for spectral-diff - inst->featureData[6] = (float)0.0; //window time-average of input magnitude spectrum - - //histogram quantities: used to estimate/update thresholds for features - for (i = 0; i < HIST_PAR_EST; i++) { - inst->histLrt[i] = 0; - inst->histSpecFlat[i] = 0; - inst->histSpecDiff[i] = 0; - } - - inst->blockInd = -1; //frame counter - inst->priorModelPars[0] = LRT_FEATURE_THR; //default threshold for lrt feature - inst->priorModelPars[1] = (float)0.5; //threshold for spectral flatness: - // determined on-line - inst->priorModelPars[2] = (float)1.0; //sgn_map par for spectral measure: - // 1 for flatness measure - inst->priorModelPars[3] = (float)0.5; //threshold for template-difference feature: - // determined on-line - inst->priorModelPars[4] = (float)1.0; //default weighting parameter for lrt feature - inst->priorModelPars[5] = (float)0.0; //default weighting parameter for - // spectral flatness feature - inst->priorModelPars[6] = (float)0.0; //default weighting parameter for - // spectral difference feature - - inst->modelUpdatePars[0] = 2; //update flag for parameters: - // 0 no update, 1=update once, 2=update every window - inst->modelUpdatePars[1] = 500; //window for update - inst->modelUpdatePars[2] = 0; //counter for update of conservative noise spectrum - //counter if the feature thresholds are updated during the sequence - inst->modelUpdatePars[3] = inst->modelUpdatePars[1]; - - inst->signalEnergy = 0.0; - inst->sumMagn = 0.0; - inst->whiteNoiseLevel = 0.0; - inst->pinkNoiseNumerator = 0.0; - inst->pinkNoiseExp = 0.0; - - WebRtcNs_set_feature_extraction_parameters(inst); // Set feature configuration - - //default mode - WebRtcNs_set_policy_core(inst, 0); - + // Smooth LR (same as threshold). + self->logLrtTimeAvg[i] = LRT_FEATURE_THR; + } - memset(inst->outBuf, 0, sizeof(float) * 3 * BLOCKL_MAX); + // Feature quantities. + // Spectral flatness (start on threshold). + self->featureData[0] = SF_FEATURE_THR; + self->featureData[1] = 0.f; // Spectral entropy: not used in this version. + self->featureData[2] = 0.f; // Spectral variance: not used in this version. + // Average LRT factor (start on threshold). + self->featureData[3] = LRT_FEATURE_THR; + // Spectral template diff (start on threshold). + self->featureData[4] = SF_FEATURE_THR; + self->featureData[5] = 0.f; // Normalization for spectral difference. + // Window time-average of input magnitude spectrum. + self->featureData[6] = 0.f; + + // Histogram quantities: used to estimate/update thresholds for features. + memset(self->histLrt, 0, sizeof(int) * HIST_PAR_EST); + memset(self->histSpecFlat, 0, sizeof(int) * HIST_PAR_EST); + memset(self->histSpecDiff, 0, sizeof(int) * HIST_PAR_EST); + + + self->blockInd = -1; // Frame counter. + // Default threshold for LRT feature. + self->priorModelPars[0] = LRT_FEATURE_THR; + // Threshold for spectral flatness: determined on-line. + self->priorModelPars[1] = 0.5f; + // sgn_map par for spectral measure: 1 for flatness measure. + self->priorModelPars[2] = 1.f; + // Threshold for template-difference feature: determined on-line. + self->priorModelPars[3] = 0.5f; + // Default weighting parameter for LRT feature. + self->priorModelPars[4] = 1.f; + // Default weighting parameter for spectral flatness feature. + self->priorModelPars[5] = 0.f; + // Default weighting parameter for spectral difference feature. + self->priorModelPars[6] = 0.f; + + // Update flag for parameters: + // 0 no update, 1 = update once, 2 = update every window. + self->modelUpdatePars[0] = 2; + self->modelUpdatePars[1] = 500; // Window for update. + // Counter for update of conservative noise spectrum. + self->modelUpdatePars[2] = 0; + // Counter if the feature thresholds are updated during the sequence. + self->modelUpdatePars[3] = self->modelUpdatePars[1]; + + self->signalEnergy = 0.0; + self->sumMagn = 0.0; + self->whiteNoiseLevel = 0.0; + self->pinkNoiseNumerator = 0.0; + self->pinkNoiseExp = 0.0; - inst->initFlag = 1; - return 0; -} + set_feature_extraction_parameters(self); -int WebRtcNs_set_policy_core(NSinst_t* inst, int mode) { - // allow for modes:0,1,2,3 - if (mode < 0 || mode > 3) { - return (-1); - } + // Default mode. + WebRtcNs_set_policy_core(self, 0); - inst->aggrMode = mode; - if (mode == 0) { - inst->overdrive = (float)1.0; - inst->denoiseBound = (float)0.5; - inst->gainmap = 0; - } else if (mode == 1) { - //inst->overdrive = (float)1.25; - inst->overdrive = (float)1.0; - inst->denoiseBound = (float)0.25; - inst->gainmap = 1; - } else if (mode == 2) { - //inst->overdrive = (float)1.25; - inst->overdrive = (float)1.1; - inst->denoiseBound = (float)0.125; - inst->gainmap = 1; - } else if (mode == 3) { - //inst->overdrive = (float)1.30; - inst->overdrive = (float)1.25; - inst->denoiseBound = (float)0.09; - inst->gainmap = 1; - } + self->initFlag = 1; return 0; } -// Estimate noise -void WebRtcNs_NoiseEstimation(NSinst_t* inst, float* magn, float* noise) { +// Estimate noise. +static void NoiseEstimation(NSinst_t* self, float* magn, float* noise) { int i, s, offset; float lmagn[HALF_ANAL_BLOCKL], delta; - if (inst->updates < END_STARTUP_LONG) { - inst->updates++; + if (self->updates < END_STARTUP_LONG) { + self->updates++; } - for (i = 0; i < inst->magnLen; i++) { + for (i = 0; i < self->magnLen; i++) { lmagn[i] = (float)log(magn[i]); } - // loop over simultaneous estimates + // Loop over simultaneous estimates. for (s = 0; s < SIMULT; s++) { - offset = s * inst->magnLen; + offset = s * self->magnLen; // newquantest(...) - for (i = 0; i < inst->magnLen; i++) { - // compute delta - if (inst->density[offset + i] > 1.0) { - delta = FACTOR * (float)1.0 / inst->density[offset + i]; + for (i = 0; i < self->magnLen; i++) { + // Compute delta. + if (self->density[offset + i] > 1.0) { + delta = FACTOR * 1.f / self->density[offset + i]; } else { delta = FACTOR; } - // update log quantile estimate - if (lmagn[i] > inst->lquantile[offset + i]) { - inst->lquantile[offset + i] += QUANTILE * delta - / (float)(inst->counter[s] + 1); + // Update log quantile estimate. + if (lmagn[i] > self->lquantile[offset + i]) { + self->lquantile[offset + i] += + QUANTILE * delta / (float)(self->counter[s] + 1); } else { - inst->lquantile[offset + i] -= ((float)1.0 - QUANTILE) * delta - / (float)(inst->counter[s] + 1); - } - - // update density estimate - if (fabs(lmagn[i] - inst->lquantile[offset + i]) < WIDTH) { - inst->density[offset + i] = ((float)inst->counter[s] * inst->density[offset - + i] + (float)1.0 / ((float)2.0 * WIDTH)) / (float)(inst->counter[s] + 1); + self->lquantile[offset + i] -= + (1.f - QUANTILE) * delta / (float)(self->counter[s] + 1); } - } // end loop over magnitude spectrum - if (inst->counter[s] >= END_STARTUP_LONG) { - inst->counter[s] = 0; - if (inst->updates >= END_STARTUP_LONG) { - for (i = 0; i < inst->magnLen; i++) { - inst->quantile[i] = (float)exp(inst->lquantile[offset + i]); + // Update density estimate. + if (fabs(lmagn[i] - self->lquantile[offset + i]) < WIDTH) { + self->density[offset + i] = + ((float)self->counter[s] * self->density[offset + i] + + 1.f / (2.f * WIDTH)) / + (float)(self->counter[s] + 1); + } + } // End loop over magnitude spectrum. + + if (self->counter[s] >= END_STARTUP_LONG) { + self->counter[s] = 0; + if (self->updates >= END_STARTUP_LONG) { + for (i = 0; i < self->magnLen; i++) { + self->quantile[i] = (float)exp(self->lquantile[offset + i]); } } } - inst->counter[s]++; - } // end loop over simultaneous estimates + self->counter[s]++; + } // End loop over simultaneous estimates. - // Sequentially update the noise during startup - if (inst->updates < END_STARTUP_LONG) { + // Sequentially update the noise during startup. + if (self->updates < END_STARTUP_LONG) { // Use the last "s" to get noise during startup that differ from zero. - for (i = 0; i < inst->magnLen; i++) { - inst->quantile[i] = (float)exp(inst->lquantile[offset + i]); + for (i = 0; i < self->magnLen; i++) { + self->quantile[i] = (float)exp(self->lquantile[offset + i]); } } - for (i = 0; i < inst->magnLen; i++) { - noise[i] = inst->quantile[i]; + for (i = 0; i < self->magnLen; i++) { + noise[i] = self->quantile[i]; } } -// Extract thresholds for feature parameters -// histograms are computed over some window_size (given by inst->modelUpdatePars[1]) -// thresholds and weights are extracted every window -// flag 0 means update histogram only, flag 1 means compute the thresholds/weights -// threshold and weights are returned in: inst->priorModelPars -void WebRtcNs_FeatureParameterExtraction(NSinst_t* inst, int flag) { +// Extract thresholds for feature parameters. +// Histograms are computed over some window size (given by +// self->modelUpdatePars[1]). +// Thresholds and weights are extracted every window. +// |flag| = 0 updates histogram only, |flag| = 1 computes the threshold/weights. +// Threshold and weights are returned in: self->priorModelPars. +static void FeatureParameterExtraction(NSinst_t* self, int flag) { int i, useFeatureSpecFlat, useFeatureSpecDiff, numHistLrt; int maxPeak1, maxPeak2; - int weightPeak1SpecFlat, weightPeak2SpecFlat, weightPeak1SpecDiff, weightPeak2SpecDiff; + int weightPeak1SpecFlat, weightPeak2SpecFlat, weightPeak1SpecDiff, + weightPeak2SpecDiff; float binMid, featureSum; float posPeak1SpecFlat, posPeak2SpecFlat, posPeak1SpecDiff, posPeak2SpecDiff; float fluctLrt, avgHistLrt, avgSquareHistLrt, avgHistLrtCompl; - //3 features: lrt, flatness, difference - //lrt_feature = inst->featureData[3]; - //flat_feature = inst->featureData[0]; - //diff_feature = inst->featureData[4]; + // 3 features: LRT, flatness, difference. + // lrt_feature = self->featureData[3]; + // flat_feature = self->featureData[0]; + // diff_feature = self->featureData[4]; - //update histograms + // Update histograms. if (flag == 0) { // LRT - if ((inst->featureData[3] < HIST_PAR_EST * inst->featureExtractionParams.binSizeLrt) - && (inst->featureData[3] >= 0.0)) { - i = (int)(inst->featureData[3] / inst->featureExtractionParams.binSizeLrt); - inst->histLrt[i]++; - } - // Spectral flatness - if ((inst->featureData[0] < HIST_PAR_EST - * inst->featureExtractionParams.binSizeSpecFlat) - && (inst->featureData[0] >= 0.0)) { - i = (int)(inst->featureData[0] / inst->featureExtractionParams.binSizeSpecFlat); - inst->histSpecFlat[i]++; - } - // Spectral difference - if ((inst->featureData[4] < HIST_PAR_EST - * inst->featureExtractionParams.binSizeSpecDiff) - && (inst->featureData[4] >= 0.0)) { - i = (int)(inst->featureData[4] / inst->featureExtractionParams.binSizeSpecDiff); - inst->histSpecDiff[i]++; + if ((self->featureData[3] < + HIST_PAR_EST * self->featureExtractionParams.binSizeLrt) && + (self->featureData[3] >= 0.0)) { + i = (int)(self->featureData[3] / + self->featureExtractionParams.binSizeLrt); + self->histLrt[i]++; + } + // Spectral flatness. + if ((self->featureData[0] < + HIST_PAR_EST * self->featureExtractionParams.binSizeSpecFlat) && + (self->featureData[0] >= 0.0)) { + i = (int)(self->featureData[0] / + self->featureExtractionParams.binSizeSpecFlat); + self->histSpecFlat[i]++; + } + // Spectral difference. + if ((self->featureData[4] < + HIST_PAR_EST * self->featureExtractionParams.binSizeSpecDiff) && + (self->featureData[4] >= 0.0)) { + i = (int)(self->featureData[4] / + self->featureExtractionParams.binSizeSpecDiff); + self->histSpecDiff[i]++; } } - // extract parameters for speech/noise probability + // Extract parameters for speech/noise probability. if (flag == 1) { - //lrt feature: compute the average over inst->featureExtractionParams.rangeAvgHistLrt + // LRT feature: compute the average over + // self->featureExtractionParams.rangeAvgHistLrt. avgHistLrt = 0.0; avgHistLrtCompl = 0.0; avgSquareHistLrt = 0.0; numHistLrt = 0; for (i = 0; i < HIST_PAR_EST; i++) { - binMid = ((float)i + (float)0.5) * inst->featureExtractionParams.binSizeLrt; - if (binMid <= inst->featureExtractionParams.rangeAvgHistLrt) { - avgHistLrt += inst->histLrt[i] * binMid; - numHistLrt += inst->histLrt[i]; + binMid = ((float)i + 0.5f) * self->featureExtractionParams.binSizeLrt; + if (binMid <= self->featureExtractionParams.rangeAvgHistLrt) { + avgHistLrt += self->histLrt[i] * binMid; + numHistLrt += self->histLrt[i]; } - avgSquareHistLrt += inst->histLrt[i] * binMid * binMid; - avgHistLrtCompl += inst->histLrt[i] * binMid; + avgSquareHistLrt += self->histLrt[i] * binMid * binMid; + avgHistLrtCompl += self->histLrt[i] * binMid; } if (numHistLrt > 0) { avgHistLrt = avgHistLrt / ((float)numHistLrt); } - avgHistLrtCompl = avgHistLrtCompl / ((float)inst->modelUpdatePars[1]); - avgSquareHistLrt = avgSquareHistLrt / ((float)inst->modelUpdatePars[1]); + avgHistLrtCompl = avgHistLrtCompl / ((float)self->modelUpdatePars[1]); + avgSquareHistLrt = avgSquareHistLrt / ((float)self->modelUpdatePars[1]); fluctLrt = avgSquareHistLrt - avgHistLrt * avgHistLrtCompl; - // get threshold for lrt feature: - if (fluctLrt < inst->featureExtractionParams.thresFluctLrt) { - //very low fluct, so likely noise - inst->priorModelPars[0] = inst->featureExtractionParams.maxLrt; + // Get threshold for LRT feature. + if (fluctLrt < self->featureExtractionParams.thresFluctLrt) { + // Very low fluctuation, so likely noise. + self->priorModelPars[0] = self->featureExtractionParams.maxLrt; } else { - inst->priorModelPars[0] = inst->featureExtractionParams.factor1ModelPars - * avgHistLrt; - // check if value is within min/max range - if (inst->priorModelPars[0] < inst->featureExtractionParams.minLrt) { - inst->priorModelPars[0] = inst->featureExtractionParams.minLrt; + self->priorModelPars[0] = + self->featureExtractionParams.factor1ModelPars * avgHistLrt; + // Check if value is within min/max range. + if (self->priorModelPars[0] < self->featureExtractionParams.minLrt) { + self->priorModelPars[0] = self->featureExtractionParams.minLrt; } - if (inst->priorModelPars[0] > inst->featureExtractionParams.maxLrt) { - inst->priorModelPars[0] = inst->featureExtractionParams.maxLrt; + if (self->priorModelPars[0] > self->featureExtractionParams.maxLrt) { + self->priorModelPars[0] = self->featureExtractionParams.maxLrt; } } - // done with lrt feature + // Done with LRT feature. - // - // for spectral flatness and spectral difference: compute the main peaks of histogram + // For spectral flatness and spectral difference: compute the main peaks of + // histogram. maxPeak1 = 0; maxPeak2 = 0; posPeak1SpecFlat = 0.0; @@ -394,221 +384,266 @@ weightPeak1SpecFlat = 0; weightPeak2SpecFlat = 0; - // peaks for flatness + // Peaks for flatness. for (i = 0; i < HIST_PAR_EST; i++) { - binMid = ((float)i + (float)0.5) * inst->featureExtractionParams.binSizeSpecFlat; - if (inst->histSpecFlat[i] > maxPeak1) { - // Found new "first" peak + binMid = + (i + 0.5f) * self->featureExtractionParams.binSizeSpecFlat; + if (self->histSpecFlat[i] > maxPeak1) { + // Found new "first" peak. maxPeak2 = maxPeak1; weightPeak2SpecFlat = weightPeak1SpecFlat; posPeak2SpecFlat = posPeak1SpecFlat; - maxPeak1 = inst->histSpecFlat[i]; - weightPeak1SpecFlat = inst->histSpecFlat[i]; + maxPeak1 = self->histSpecFlat[i]; + weightPeak1SpecFlat = self->histSpecFlat[i]; posPeak1SpecFlat = binMid; - } else if (inst->histSpecFlat[i] > maxPeak2) { - // Found new "second" peak - maxPeak2 = inst->histSpecFlat[i]; - weightPeak2SpecFlat = inst->histSpecFlat[i]; + } else if (self->histSpecFlat[i] > maxPeak2) { + // Found new "second" peak. + maxPeak2 = self->histSpecFlat[i]; + weightPeak2SpecFlat = self->histSpecFlat[i]; posPeak2SpecFlat = binMid; } } - //compute two peaks for spectral difference + // Compute two peaks for spectral difference. maxPeak1 = 0; maxPeak2 = 0; posPeak1SpecDiff = 0.0; posPeak2SpecDiff = 0.0; weightPeak1SpecDiff = 0; weightPeak2SpecDiff = 0; - // peaks for spectral difference + // Peaks for spectral difference. for (i = 0; i < HIST_PAR_EST; i++) { - binMid = ((float)i + (float)0.5) * inst->featureExtractionParams.binSizeSpecDiff; - if (inst->histSpecDiff[i] > maxPeak1) { - // Found new "first" peak + binMid = + ((float)i + 0.5f) * self->featureExtractionParams.binSizeSpecDiff; + if (self->histSpecDiff[i] > maxPeak1) { + // Found new "first" peak. maxPeak2 = maxPeak1; weightPeak2SpecDiff = weightPeak1SpecDiff; posPeak2SpecDiff = posPeak1SpecDiff; - maxPeak1 = inst->histSpecDiff[i]; - weightPeak1SpecDiff = inst->histSpecDiff[i]; + maxPeak1 = self->histSpecDiff[i]; + weightPeak1SpecDiff = self->histSpecDiff[i]; posPeak1SpecDiff = binMid; - } else if (inst->histSpecDiff[i] > maxPeak2) { - // Found new "second" peak - maxPeak2 = inst->histSpecDiff[i]; - weightPeak2SpecDiff = inst->histSpecDiff[i]; + } else if (self->histSpecDiff[i] > maxPeak2) { + // Found new "second" peak. + maxPeak2 = self->histSpecDiff[i]; + weightPeak2SpecDiff = self->histSpecDiff[i]; posPeak2SpecDiff = binMid; } } - // for spectrum flatness feature + // For spectrum flatness feature. useFeatureSpecFlat = 1; - // merge the two peaks if they are close - if ((fabs(posPeak2SpecFlat - posPeak1SpecFlat) - < inst->featureExtractionParams.limitPeakSpacingSpecFlat) - && (weightPeak2SpecFlat - > inst->featureExtractionParams.limitPeakWeightsSpecFlat - * weightPeak1SpecFlat)) { + // Merge the two peaks if they are close. + if ((fabs(posPeak2SpecFlat - posPeak1SpecFlat) < + self->featureExtractionParams.limitPeakSpacingSpecFlat) && + (weightPeak2SpecFlat > + self->featureExtractionParams.limitPeakWeightsSpecFlat * + weightPeak1SpecFlat)) { weightPeak1SpecFlat += weightPeak2SpecFlat; - posPeak1SpecFlat = (float)0.5 * (posPeak1SpecFlat + posPeak2SpecFlat); + posPeak1SpecFlat = 0.5f * (posPeak1SpecFlat + posPeak2SpecFlat); } - //reject if weight of peaks is not large enough, or peak value too small - if (weightPeak1SpecFlat < inst->featureExtractionParams.thresWeightSpecFlat - || posPeak1SpecFlat < inst->featureExtractionParams.thresPosSpecFlat) { + // Reject if weight of peaks is not large enough, or peak value too small. + if (weightPeak1SpecFlat < + self->featureExtractionParams.thresWeightSpecFlat || + posPeak1SpecFlat < self->featureExtractionParams.thresPosSpecFlat) { useFeatureSpecFlat = 0; } - // if selected, get the threshold + // If selected, get the threshold. if (useFeatureSpecFlat == 1) { - // compute the threshold - inst->priorModelPars[1] = inst->featureExtractionParams.factor2ModelPars - * posPeak1SpecFlat; - //check if value is within min/max range - if (inst->priorModelPars[1] < inst->featureExtractionParams.minSpecFlat) { - inst->priorModelPars[1] = inst->featureExtractionParams.minSpecFlat; + // Compute the threshold. + self->priorModelPars[1] = + self->featureExtractionParams.factor2ModelPars * posPeak1SpecFlat; + // Check if value is within min/max range. + if (self->priorModelPars[1] < self->featureExtractionParams.minSpecFlat) { + self->priorModelPars[1] = self->featureExtractionParams.minSpecFlat; } - if (inst->priorModelPars[1] > inst->featureExtractionParams.maxSpecFlat) { - inst->priorModelPars[1] = inst->featureExtractionParams.maxSpecFlat; + if (self->priorModelPars[1] > self->featureExtractionParams.maxSpecFlat) { + self->priorModelPars[1] = self->featureExtractionParams.maxSpecFlat; } } - // done with flatness feature + // Done with flatness feature. - // for template feature + // For template feature. useFeatureSpecDiff = 1; - // merge the two peaks if they are close - if ((fabs(posPeak2SpecDiff - posPeak1SpecDiff) - < inst->featureExtractionParams.limitPeakSpacingSpecDiff) - && (weightPeak2SpecDiff - > inst->featureExtractionParams.limitPeakWeightsSpecDiff - * weightPeak1SpecDiff)) { + // Merge the two peaks if they are close. + if ((fabs(posPeak2SpecDiff - posPeak1SpecDiff) < + self->featureExtractionParams.limitPeakSpacingSpecDiff) && + (weightPeak2SpecDiff > + self->featureExtractionParams.limitPeakWeightsSpecDiff * + weightPeak1SpecDiff)) { weightPeak1SpecDiff += weightPeak2SpecDiff; - posPeak1SpecDiff = (float)0.5 * (posPeak1SpecDiff + posPeak2SpecDiff); + posPeak1SpecDiff = 0.5f * (posPeak1SpecDiff + posPeak2SpecDiff); } - // get the threshold value - inst->priorModelPars[3] = inst->featureExtractionParams.factor1ModelPars - * posPeak1SpecDiff; - //reject if weight of peaks is not large enough - if (weightPeak1SpecDiff < inst->featureExtractionParams.thresWeightSpecDiff) { + // Get the threshold value. + self->priorModelPars[3] = + self->featureExtractionParams.factor1ModelPars * posPeak1SpecDiff; + // Reject if weight of peaks is not large enough. + if (weightPeak1SpecDiff < + self->featureExtractionParams.thresWeightSpecDiff) { useFeatureSpecDiff = 0; } - //check if value is within min/max range - if (inst->priorModelPars[3] < inst->featureExtractionParams.minSpecDiff) { - inst->priorModelPars[3] = inst->featureExtractionParams.minSpecDiff; - } - if (inst->priorModelPars[3] > inst->featureExtractionParams.maxSpecDiff) { - inst->priorModelPars[3] = inst->featureExtractionParams.maxSpecDiff; - } - // done with spectral difference feature - - // don't use template feature if fluctuation of lrt feature is very low: - // most likely just noise state - if (fluctLrt < inst->featureExtractionParams.thresFluctLrt) { + // Check if value is within min/max range. + if (self->priorModelPars[3] < self->featureExtractionParams.minSpecDiff) { + self->priorModelPars[3] = self->featureExtractionParams.minSpecDiff; + } + if (self->priorModelPars[3] > self->featureExtractionParams.maxSpecDiff) { + self->priorModelPars[3] = self->featureExtractionParams.maxSpecDiff; + } + // Done with spectral difference feature. + + // Don't use template feature if fluctuation of LRT feature is very low: + // most likely just noise state. + if (fluctLrt < self->featureExtractionParams.thresFluctLrt) { useFeatureSpecDiff = 0; } - // select the weights between the features - // inst->priorModelPars[4] is weight for lrt: always selected - // inst->priorModelPars[5] is weight for spectral flatness - // inst->priorModelPars[6] is weight for spectral difference + // Select the weights between the features. + // self->priorModelPars[4] is weight for LRT: always selected. + // self->priorModelPars[5] is weight for spectral flatness. + // self->priorModelPars[6] is weight for spectral difference. featureSum = (float)(1 + useFeatureSpecFlat + useFeatureSpecDiff); - inst->priorModelPars[4] = (float)1.0 / featureSum; - inst->priorModelPars[5] = ((float)useFeatureSpecFlat) / featureSum; - inst->priorModelPars[6] = ((float)useFeatureSpecDiff) / featureSum; + self->priorModelPars[4] = 1.f / featureSum; + self->priorModelPars[5] = ((float)useFeatureSpecFlat) / featureSum; + self->priorModelPars[6] = ((float)useFeatureSpecDiff) / featureSum; - // set hists to zero for next update - if (inst->modelUpdatePars[0] >= 1) { + // Set hists to zero for next update. + if (self->modelUpdatePars[0] >= 1) { for (i = 0; i < HIST_PAR_EST; i++) { - inst->histLrt[i] = 0; - inst->histSpecFlat[i] = 0; - inst->histSpecDiff[i] = 0; + self->histLrt[i] = 0; + self->histSpecFlat[i] = 0; + self->histSpecDiff[i] = 0; } } - } // end of flag == 1 + } // End of flag == 1. } -// Compute spectral flatness on input spectrum -// magnIn is the magnitude spectrum -// spectral flatness is returned in inst->featureData[0] -void WebRtcNs_ComputeSpectralFlatness(NSinst_t* inst, float* magnIn) { +// Compute spectral flatness on input spectrum. +// |magnIn| is the magnitude spectrum. +// Spectral flatness is returned in self->featureData[0]. +static void ComputeSpectralFlatness(NSinst_t* self, const float* magnIn) { int i; - int shiftLP = 1; //option to remove first bin(s) from spectral measures + int shiftLP = 1; // Option to remove first bin(s) from spectral measures. float avgSpectralFlatnessNum, avgSpectralFlatnessDen, spectralTmp; - // comute spectral measures - // for flatness + // Compute spectral measures. + // For flatness. avgSpectralFlatnessNum = 0.0; - avgSpectralFlatnessDen = inst->sumMagn; + avgSpectralFlatnessDen = self->sumMagn; for (i = 0; i < shiftLP; i++) { avgSpectralFlatnessDen -= magnIn[i]; } - // compute log of ratio of the geometric to arithmetic mean: check for log(0) case - for (i = shiftLP; i < inst->magnLen; i++) { + // Compute log of ratio of the geometric to arithmetic mean: check for log(0) + // case. + for (i = shiftLP; i < self->magnLen; i++) { if (magnIn[i] > 0.0) { avgSpectralFlatnessNum += (float)log(magnIn[i]); } else { - inst->featureData[0] -= SPECT_FL_TAVG * inst->featureData[0]; + self->featureData[0] -= SPECT_FL_TAVG * self->featureData[0]; return; } } - //normalize - avgSpectralFlatnessDen = avgSpectralFlatnessDen / inst->magnLen; - avgSpectralFlatnessNum = avgSpectralFlatnessNum / inst->magnLen; + // Normalize. + avgSpectralFlatnessDen = avgSpectralFlatnessDen / self->magnLen; + avgSpectralFlatnessNum = avgSpectralFlatnessNum / self->magnLen; - //ratio and inverse log: check for case of log(0) + // Ratio and inverse log: check for case of log(0). spectralTmp = (float)exp(avgSpectralFlatnessNum) / avgSpectralFlatnessDen; - //time-avg update of spectral flatness feature - inst->featureData[0] += SPECT_FL_TAVG * (spectralTmp - inst->featureData[0]); - // done with flatness feature + // Time-avg update of spectral flatness feature. + self->featureData[0] += SPECT_FL_TAVG * (spectralTmp - self->featureData[0]); + // Done with flatness feature. } -// Compute the difference measure between input spectrum and a template/learned noise spectrum -// magnIn is the input spectrum -// the reference/template spectrum is inst->magnAvgPause[i] -// returns (normalized) spectral difference in inst->featureData[4] -void WebRtcNs_ComputeSpectralDifference(NSinst_t* inst, float* magnIn) { - // avgDiffNormMagn = var(magnIn) - cov(magnIn, magnAvgPause)^2 / var(magnAvgPause) +// Compute prior and post SNR based on quantile noise estimation. +// Compute DD estimate of prior SNR. +// Inputs: +// * |magn| is the signal magnitude spectrum estimate. +// * |noise| is the magnitude noise spectrum estimate. +// Outputs: +// * |snrLocPrior| is the computed prior SNR. +// * |snrLocPost| is the computed post SNR. +static void ComputeSnr(const NSinst_t* self, + const float* magn, + const float* noise, + float* snrLocPrior, + float* snrLocPost) { + int i; + + for (i = 0; i < self->magnLen; i++) { + // Previous post SNR. + // Previous estimate: based on previous frame with gain filter. + float previousEstimateStsa = self->magnPrevAnalyze[i] / + (self->noisePrev[i] + 0.0001f) * self->smooth[i]; + // Post SNR. + snrLocPost[i] = 0.f; + if (magn[i] > noise[i]) { + snrLocPost[i] = magn[i] / (noise[i] + 0.0001f) - 1.f; + } + // DD estimate is sum of two terms: current estimate and previous estimate. + // Directed decision update of snrPrior. + snrLocPrior[i] = + DD_PR_SNR * previousEstimateStsa + (1.f - DD_PR_SNR) * snrLocPost[i]; + } // End of loop over frequencies. +} + +// Compute the difference measure between input spectrum and a template/learned +// noise spectrum. +// |magnIn| is the input spectrum. +// The reference/template spectrum is self->magnAvgPause[i]. +// Returns (normalized) spectral difference in self->featureData[4]. +static void ComputeSpectralDifference(NSinst_t* self, + const float* magnIn) { + // avgDiffNormMagn = var(magnIn) - cov(magnIn, magnAvgPause)^2 / + // var(magnAvgPause) int i; float avgPause, avgMagn, covMagnPause, varPause, varMagn, avgDiffNormMagn; avgPause = 0.0; - avgMagn = inst->sumMagn; - // compute average quantities - for (i = 0; i < inst->magnLen; i++) { - //conservative smooth noise spectrum from pause frames - avgPause += inst->magnAvgPause[i]; + avgMagn = self->sumMagn; + // Compute average quantities. + for (i = 0; i < self->magnLen; i++) { + // Conservative smooth noise spectrum from pause frames. + avgPause += self->magnAvgPause[i]; } - avgPause = avgPause / ((float)inst->magnLen); - avgMagn = avgMagn / ((float)inst->magnLen); + avgPause = avgPause / ((float)self->magnLen); + avgMagn = avgMagn / ((float)self->magnLen); covMagnPause = 0.0; varPause = 0.0; varMagn = 0.0; - // compute variance and covariance quantities - for (i = 0; i < inst->magnLen; i++) { - covMagnPause += (magnIn[i] - avgMagn) * (inst->magnAvgPause[i] - avgPause); - varPause += (inst->magnAvgPause[i] - avgPause) * (inst->magnAvgPause[i] - avgPause); + // Compute variance and covariance quantities. + for (i = 0; i < self->magnLen; i++) { + covMagnPause += (magnIn[i] - avgMagn) * (self->magnAvgPause[i] - avgPause); + varPause += + (self->magnAvgPause[i] - avgPause) * (self->magnAvgPause[i] - avgPause); varMagn += (magnIn[i] - avgMagn) * (magnIn[i] - avgMagn); } - covMagnPause = covMagnPause / ((float)inst->magnLen); - varPause = varPause / ((float)inst->magnLen); - varMagn = varMagn / ((float)inst->magnLen); - // update of average magnitude spectrum - inst->featureData[6] += inst->signalEnergy; - - avgDiffNormMagn = varMagn - (covMagnPause * covMagnPause) / (varPause + (float)0.0001); - // normalize and compute time-avg update of difference feature - avgDiffNormMagn = (float)(avgDiffNormMagn / (inst->featureData[5] + (float)0.0001)); - inst->featureData[4] += SPECT_DIFF_TAVG * (avgDiffNormMagn - inst->featureData[4]); + covMagnPause = covMagnPause / ((float)self->magnLen); + varPause = varPause / ((float)self->magnLen); + varMagn = varMagn / ((float)self->magnLen); + // Update of average magnitude spectrum. + self->featureData[6] += self->signalEnergy; + + avgDiffNormMagn = + varMagn - (covMagnPause * covMagnPause) / (varPause + 0.0001f); + // Normalize and compute time-avg update of difference feature. + avgDiffNormMagn = (float)(avgDiffNormMagn / (self->featureData[5] + 0.0001f)); + self->featureData[4] += + SPECT_DIFF_TAVG * (avgDiffNormMagn - self->featureData[4]); } -// Compute speech/noise probability -// speech/noise probability is returned in: probSpeechFinal -//magn is the input magnitude spectrum -//noise is the noise spectrum -//snrLocPrior is the prior snr for each freq. -//snr loc_post is the post snr for each freq. -void WebRtcNs_SpeechNoiseProb(NSinst_t* inst, float* probSpeechFinal, float* snrLocPrior, - float* snrLocPost) { +// Compute speech/noise probability. +// Speech/noise probability is returned in |probSpeechFinal|. +// |magn| is the input magnitude spectrum. +// |noise| is the noise spectrum. +// |snrLocPrior| is the prior SNR for each frequency. +// |snrLocPost| is the post SNR for each frequency. +static void SpeechNoiseProb(NSinst_t* self, + float* probSpeechFinal, + const float* snrLocPrior, + const float* snrLocPost) { int i, sgnMap; float invLrt, gainPrior, indPrior; float logLrtTimeAvgKsum, besselTmp; @@ -619,686 +654,755 @@ float widthPrior, widthPrior0, widthPrior1, widthPrior2; widthPrior0 = WIDTH_PR_MAP; - widthPrior1 = (float)2.0 * WIDTH_PR_MAP; //width for pause region: - // lower range, so increase width in tanh map - widthPrior2 = (float)2.0 * WIDTH_PR_MAP; //for spectral-difference measure - - //threshold parameters for features - threshPrior0 = inst->priorModelPars[0]; - threshPrior1 = inst->priorModelPars[1]; - threshPrior2 = inst->priorModelPars[3]; - - //sign for flatness feature - sgnMap = (int)(inst->priorModelPars[2]); - - //weight parameters for features - weightIndPrior0 = inst->priorModelPars[4]; - weightIndPrior1 = inst->priorModelPars[5]; - weightIndPrior2 = inst->priorModelPars[6]; + // Width for pause region: lower range, so increase width in tanh map. + widthPrior1 = 2.f * WIDTH_PR_MAP; + widthPrior2 = 2.f * WIDTH_PR_MAP; // For spectral-difference measure. + + // Threshold parameters for features. + threshPrior0 = self->priorModelPars[0]; + threshPrior1 = self->priorModelPars[1]; + threshPrior2 = self->priorModelPars[3]; + + // Sign for flatness feature. + sgnMap = (int)(self->priorModelPars[2]); + + // Weight parameters for features. + weightIndPrior0 = self->priorModelPars[4]; + weightIndPrior1 = self->priorModelPars[5]; + weightIndPrior2 = self->priorModelPars[6]; - // compute feature based on average LR factor - // this is the average over all frequencies of the smooth log lrt + // Compute feature based on average LR factor. + // This is the average over all frequencies of the smooth log LRT. logLrtTimeAvgKsum = 0.0; - for (i = 0; i < inst->magnLen; i++) { - tmpFloat1 = (float)1.0 + (float)2.0 * snrLocPrior[i]; - tmpFloat2 = (float)2.0 * snrLocPrior[i] / (tmpFloat1 + (float)0.0001); - besselTmp = (snrLocPost[i] + (float)1.0) * tmpFloat2; - inst->logLrtTimeAvg[i] += LRT_TAVG * (besselTmp - (float)log(tmpFloat1) - - inst->logLrtTimeAvg[i]); - logLrtTimeAvgKsum += inst->logLrtTimeAvg[i]; - } - logLrtTimeAvgKsum = (float)logLrtTimeAvgKsum / (inst->magnLen); - inst->featureData[3] = logLrtTimeAvgKsum; - // done with computation of LR factor - - // - //compute the indicator functions - // + for (i = 0; i < self->magnLen; i++) { + tmpFloat1 = 1.f + 2.f * snrLocPrior[i]; + tmpFloat2 = 2.f * snrLocPrior[i] / (tmpFloat1 + 0.0001f); + besselTmp = (snrLocPost[i] + 1.f) * tmpFloat2; + self->logLrtTimeAvg[i] += + LRT_TAVG * (besselTmp - (float)log(tmpFloat1) - self->logLrtTimeAvg[i]); + logLrtTimeAvgKsum += self->logLrtTimeAvg[i]; + } + logLrtTimeAvgKsum = (float)logLrtTimeAvgKsum / (self->magnLen); + self->featureData[3] = logLrtTimeAvgKsum; + // Done with computation of LR factor. - // average lrt feature + // Compute the indicator functions. + // Average LRT feature. widthPrior = widthPrior0; - //use larger width in tanh map for pause regions + // Use larger width in tanh map for pause regions. if (logLrtTimeAvgKsum < threshPrior0) { widthPrior = widthPrior1; } - // compute indicator function: sigmoid map - indicator0 = (float)0.5 * ((float)tanh(widthPrior * - (logLrtTimeAvgKsum - threshPrior0)) + (float)1.0); + // Compute indicator function: sigmoid map. + indicator0 = + 0.5f * + ((float)tanh(widthPrior * (logLrtTimeAvgKsum - threshPrior0)) + 1.f); - //spectral flatness feature - tmpFloat1 = inst->featureData[0]; + // Spectral flatness feature. + tmpFloat1 = self->featureData[0]; widthPrior = widthPrior0; - //use larger width in tanh map for pause regions + // Use larger width in tanh map for pause regions. if (sgnMap == 1 && (tmpFloat1 > threshPrior1)) { widthPrior = widthPrior1; } if (sgnMap == -1 && (tmpFloat1 < threshPrior1)) { widthPrior = widthPrior1; } - // compute indicator function: sigmoid map - indicator1 = (float)0.5 * ((float)tanh((float)sgnMap * - widthPrior * (threshPrior1 - tmpFloat1)) + (float)1.0); + // Compute indicator function: sigmoid map. + indicator1 = + 0.5f * + ((float)tanh((float)sgnMap * widthPrior * (threshPrior1 - tmpFloat1)) + + 1.f); - //for template spectrum-difference - tmpFloat1 = inst->featureData[4]; + // For template spectrum-difference. + tmpFloat1 = self->featureData[4]; widthPrior = widthPrior0; - //use larger width in tanh map for pause regions + // Use larger width in tanh map for pause regions. if (tmpFloat1 < threshPrior2) { widthPrior = widthPrior2; } - // compute indicator function: sigmoid map - indicator2 = (float)0.5 * ((float)tanh(widthPrior * (tmpFloat1 - threshPrior2)) - + (float)1.0); - - //combine the indicator function with the feature weights - indPrior = weightIndPrior0 * indicator0 + weightIndPrior1 * indicator1 + weightIndPrior2 - * indicator2; - // done with computing indicator function - - //compute the prior probability - inst->priorSpeechProb += PRIOR_UPDATE * (indPrior - inst->priorSpeechProb); - // make sure probabilities are within range: keep floor to 0.01 - if (inst->priorSpeechProb > 1.0) { - inst->priorSpeechProb = (float)1.0; - } - if (inst->priorSpeechProb < 0.01) { - inst->priorSpeechProb = (float)0.01; - } - - //final speech probability: combine prior model with LR factor: - gainPrior = ((float)1.0 - inst->priorSpeechProb) / (inst->priorSpeechProb + (float)0.0001); - for (i = 0; i < inst->magnLen; i++) { - invLrt = (float)exp(-inst->logLrtTimeAvg[i]); + // Compute indicator function: sigmoid map. + indicator2 = + 0.5f * ((float)tanh(widthPrior * (tmpFloat1 - threshPrior2)) + 1.f); + + // Combine the indicator function with the feature weights. + indPrior = weightIndPrior0 * indicator0 + weightIndPrior1 * indicator1 + + weightIndPrior2 * indicator2; + // Done with computing indicator function. + + // Compute the prior probability. + self->priorSpeechProb += PRIOR_UPDATE * (indPrior - self->priorSpeechProb); + // Make sure probabilities are within range: keep floor to 0.01. + if (self->priorSpeechProb > 1.f) { + self->priorSpeechProb = 1.f; + } + if (self->priorSpeechProb < 0.01f) { + self->priorSpeechProb = 0.01f; + } + + // Final speech probability: combine prior model with LR factor:. + gainPrior = (1.f - self->priorSpeechProb) / (self->priorSpeechProb + 0.0001f); + for (i = 0; i < self->magnLen; i++) { + invLrt = (float)exp(-self->logLrtTimeAvg[i]); invLrt = (float)gainPrior * invLrt; - probSpeechFinal[i] = (float)1.0 / ((float)1.0 + invLrt); + probSpeechFinal[i] = 1.f / (1.f + invLrt); + } +} + +// Update the noise features. +// Inputs: +// * |magn| is the signal magnitude spectrum estimate. +// * |updateParsFlag| is an update flag for parameters. +static void FeatureUpdate(NSinst_t* self, + const float* magn, + int updateParsFlag) { + // Compute spectral flatness on input spectrum. + ComputeSpectralFlatness(self, magn); + // Compute difference of input spectrum with learned/estimated noise spectrum. + ComputeSpectralDifference(self, magn); + // Compute histograms for parameter decisions (thresholds and weights for + // features). + // Parameters are extracted once every window time. + // (=self->modelUpdatePars[1]) + if (updateParsFlag >= 1) { + // Counter update. + self->modelUpdatePars[3]--; + // Update histogram. + if (self->modelUpdatePars[3] > 0) { + FeatureParameterExtraction(self, 0); + } + // Compute model parameters. + if (self->modelUpdatePars[3] == 0) { + FeatureParameterExtraction(self, 1); + self->modelUpdatePars[3] = self->modelUpdatePars[1]; + // If wish to update only once, set flag to zero. + if (updateParsFlag == 1) { + self->modelUpdatePars[0] = 0; + } else { + // Update every window: + // Get normalization for spectral difference for next window estimate. + self->featureData[6] = + self->featureData[6] / ((float)self->modelUpdatePars[1]); + self->featureData[5] = + 0.5f * (self->featureData[6] + self->featureData[5]); + self->featureData[6] = 0.f; + } + } + } +} + +// Update the noise estimate. +// Inputs: +// * |magn| is the signal magnitude spectrum estimate. +// * |snrLocPrior| is the prior SNR. +// * |snrLocPost| is the post SNR. +// Output: +// * |noise| is the updated noise magnitude spectrum estimate. +static void UpdateNoiseEstimate(NSinst_t* self, + const float* magn, + const float* snrLocPrior, + const float* snrLocPost, + float* noise) { + int i; + float probSpeech, probNonSpeech; + // Time-avg parameter for noise update. + float gammaNoiseTmp = NOISE_UPDATE; + float gammaNoiseOld; + float noiseUpdateTmp; + + for (i = 0; i < self->magnLen; i++) { + probSpeech = self->speechProb[i]; + probNonSpeech = 1.f - probSpeech; + // Temporary noise update: + // Use it for speech frames if update value is less than previous. + noiseUpdateTmp = gammaNoiseTmp * self->noisePrev[i] + + (1.f - gammaNoiseTmp) * (probNonSpeech * magn[i] + + probSpeech * self->noisePrev[i]); + // Time-constant based on speech/noise state. + gammaNoiseOld = gammaNoiseTmp; + gammaNoiseTmp = NOISE_UPDATE; + // Increase gamma (i.e., less noise update) for frame likely to be speech. + if (probSpeech > PROB_RANGE) { + gammaNoiseTmp = SPEECH_UPDATE; + } + // Conservative noise update. + if (probSpeech < PROB_RANGE) { + self->magnAvgPause[i] += GAMMA_PAUSE * (magn[i] - self->magnAvgPause[i]); + } + // Noise update. + if (gammaNoiseTmp == gammaNoiseOld) { + noise[i] = noiseUpdateTmp; + } else { + noise[i] = gammaNoiseTmp * self->noisePrev[i] + + (1.f - gammaNoiseTmp) * (probNonSpeech * magn[i] + + probSpeech * self->noisePrev[i]); + // Allow for noise update downwards: + // If noise update decreases the noise, it is safe, so allow it to + // happen. + if (noiseUpdateTmp < noise[i]) { + noise[i] = noiseUpdateTmp; + } + } + } // End of freq loop. +} + +// Updates |buffer| with a new |frame|. +// Inputs: +// * |frame| is a new speech frame or NULL for setting to zero. +// * |frame_length| is the length of the new frame. +// * |buffer_length| is the length of the buffer. +// Output: +// * |buffer| is the updated buffer. +static void UpdateBuffer(const float* frame, + int frame_length, + int buffer_length, + float* buffer) { + assert(buffer_length < 2 * frame_length); + + memcpy(buffer, + buffer + frame_length, + sizeof(*buffer) * (buffer_length - frame_length)); + if (frame) { + memcpy(buffer + buffer_length - frame_length, + frame, + sizeof(*buffer) * frame_length); + } else { + memset(buffer + buffer_length - frame_length, + 0, + sizeof(*buffer) * frame_length); + } +} + +// Transforms the signal from time to frequency domain. +// Inputs: +// * |time_data| is the signal in the time domain. +// * |time_data_length| is the length of the analysis buffer. +// * |magnitude_length| is the length of the spectrum magnitude, which equals +// the length of both |real| and |imag| (time_data_length / 2 + 1). +// Outputs: +// * |time_data| is the signal in the frequency domain. +// * |real| is the real part of the frequency domain. +// * |imag| is the imaginary part of the frequency domain. +// * |magn| is the calculated signal magnitude in the frequency domain. +static void FFT(NSinst_t* self, + float* time_data, + int time_data_length, + int magnitude_length, + float* real, + float* imag, + float* magn) { + int i; + + assert(magnitude_length == time_data_length / 2 + 1); + + WebRtc_rdft(time_data_length, 1, time_data, self->ip, self->wfft); + + imag[0] = 0; + real[0] = time_data[0]; + magn[0] = fabs(real[0]) + 1.f; + imag[magnitude_length - 1] = 0; + real[magnitude_length - 1] = time_data[1]; + magn[magnitude_length - 1] = fabs(real[magnitude_length - 1]) + 1.f; + for (i = 1; i < magnitude_length - 1; ++i) { + real[i] = time_data[2 * i]; + imag[i] = time_data[2 * i + 1]; + // Magnitude spectrum. + magn[i] = sqrtf(real[i] * real[i] + imag[i] * imag[i]) + 1.f; + } +} + +// Transforms the signal from frequency to time domain. +// Inputs: +// * |real| is the real part of the frequency domain. +// * |imag| is the imaginary part of the frequency domain. +// * |magnitude_length| is the length of the spectrum magnitude, which equals +// the length of both |real| and |imag|. +// * |time_data_length| is the length of the analysis buffer +// (2 * (magnitude_length - 1)). +// Output: +// * |time_data| is the signal in the time domain. +static void IFFT(NSinst_t* self, + const float* real, + const float* imag, + int magnitude_length, + int time_data_length, + float* time_data) { + int i; + + assert(time_data_length == 2 * (magnitude_length - 1)); + + time_data[0] = real[0]; + time_data[1] = real[magnitude_length - 1]; + for (i = 1; i < magnitude_length - 1; ++i) { + time_data[2 * i] = real[i]; + time_data[2 * i + 1] = imag[i]; + } + WebRtc_rdft(time_data_length, -1, time_data, self->ip, self->wfft); + + for (i = 0; i < time_data_length; ++i) { + time_data[i] *= 2.f / time_data_length; // FFT scaling. } } -int WebRtcNs_ProcessCore(NSinst_t* inst, - short* speechFrame, - short* speechFrameHB, - short* outFrame, - short* outFrameHB) { - // main routine for noise reduction - - int flagHB = 0; - int i; - const int kStartBand = 5; // Skip first frequency bins during estimation. - int updateParsFlag; - - float energy1, energy2, gain, factor, factor1, factor2; - float signalEnergy, sumMagn; - float snrPrior, currentEstimateStsa; - float tmpFloat1, tmpFloat2, tmpFloat3, probSpeech, probNonSpeech; - float gammaNoiseTmp, gammaNoiseOld; - float noiseUpdateTmp, fTmp, dTmp; - float fin[BLOCKL_MAX], fout[BLOCKL_MAX]; - float winData[ANAL_BLOCKL_MAX]; - float magn[HALF_ANAL_BLOCKL], noise[HALF_ANAL_BLOCKL]; - float theFilter[HALF_ANAL_BLOCKL], theFilterTmp[HALF_ANAL_BLOCKL]; - float snrLocPost[HALF_ANAL_BLOCKL], snrLocPrior[HALF_ANAL_BLOCKL]; - float probSpeechFinal[HALF_ANAL_BLOCKL] = { 0 }; - float previousEstimateStsa[HALF_ANAL_BLOCKL]; - float real[ANAL_BLOCKL_MAX], imag[HALF_ANAL_BLOCKL]; - // Variables during startup - float sum_log_i = 0.0; - float sum_log_i_square = 0.0; - float sum_log_magn = 0.0; - float sum_log_i_log_magn = 0.0; - float parametric_noise = 0.0; - float parametric_exp = 0.0; - float parametric_num = 0.0; - - // SWB variables - int deltaBweHB = 1; - int deltaGainHB = 1; - float decayBweHB = 1.0; - float gainMapParHB = 1.0; - float gainTimeDomainHB = 1.0; - float avgProbSpeechHB, avgProbSpeechHBTmp, avgFilterGainHB, gainModHB; +// Calculates the energy of a buffer. +// Inputs: +// * |buffer| is the buffer over which the energy is calculated. +// * |length| is the length of the buffer. +// Returns the calculated energy. +static float Energy(const float* buffer, int length) { + int i; + float energy = 0.f; - // Check that initiation has been done - if (inst->initFlag != 1) { + for (i = 0; i < length; ++i) { + energy += buffer[i] * buffer[i]; + } + + return energy; +} + +// Windows a buffer. +// Inputs: +// * |window| is the window by which to multiply. +// * |data| is the data without windowing. +// * |length| is the length of the window and data. +// Output: +// * |data_windowed| is the windowed data. +static void Windowing(const float* window, + const float* data, + int length, + float* data_windowed) { + int i; + + for (i = 0; i < length; ++i) { + data_windowed[i] = window[i] * data[i]; + } +} + +// Estimate prior SNR decision-directed and compute DD based Wiener Filter. +// Input: +// * |magn| is the signal magnitude spectrum estimate. +// Output: +// * |theFilter| is the frequency response of the computed Wiener filter. +static void ComputeDdBasedWienerFilter(const NSinst_t* self, + const float* magn, + float* theFilter) { + int i; + float snrPrior, previousEstimateStsa, currentEstimateStsa; + + for (i = 0; i < self->magnLen; i++) { + // Previous estimate: based on previous frame with gain filter. + previousEstimateStsa = self->magnPrevProcess[i] / + (self->noisePrev[i] + 0.0001f) * self->smooth[i]; + // Post and prior SNR. + currentEstimateStsa = 0.f; + if (magn[i] > self->noise[i]) { + currentEstimateStsa = magn[i] / (self->noise[i] + 0.0001f) - 1.f; + } + // DD estimate is sum of two terms: current estimate and previous estimate. + // Directed decision update of |snrPrior|. + snrPrior = DD_PR_SNR * previousEstimateStsa + + (1.f - DD_PR_SNR) * currentEstimateStsa; + // Gain filter. + theFilter[i] = snrPrior / (self->overdrive + snrPrior); + } // End of loop over frequencies. +} + +// Changes the aggressiveness of the noise suppression method. +// |mode| = 0 is mild (6dB), |mode| = 1 is medium (10dB) and |mode| = 2 is +// aggressive (15dB). +// Returns 0 on success and -1 otherwise. +int WebRtcNs_set_policy_core(NSinst_t* self, int mode) { + // Allow for modes: 0, 1, 2, 3. + if (mode < 0 || mode > 3) { return (-1); } - // Check for valid pointers based on sampling rate - if (inst->fs == 32000) { + + self->aggrMode = mode; + if (mode == 0) { + self->overdrive = 1.f; + self->denoiseBound = 0.5f; + self->gainmap = 0; + } else if (mode == 1) { + // self->overdrive = 1.25f; + self->overdrive = 1.f; + self->denoiseBound = 0.25f; + self->gainmap = 1; + } else if (mode == 2) { + // self->overdrive = 1.25f; + self->overdrive = 1.1f; + self->denoiseBound = 0.125f; + self->gainmap = 1; + } else if (mode == 3) { + // self->overdrive = 1.3f; + self->overdrive = 1.25f; + self->denoiseBound = 0.09f; + self->gainmap = 1; + } + return 0; +} + +int WebRtcNs_AnalyzeCore(NSinst_t* self, float* speechFrame) { + int i; + const int kStartBand = 5; // Skip first frequency bins during estimation. + int updateParsFlag; + float energy; + float signalEnergy = 0.f; + float sumMagn = 0.f; + float tmpFloat1, tmpFloat2, tmpFloat3; + float winData[ANAL_BLOCKL_MAX]; + float magn[HALF_ANAL_BLOCKL], noise[HALF_ANAL_BLOCKL]; + float snrLocPost[HALF_ANAL_BLOCKL], snrLocPrior[HALF_ANAL_BLOCKL]; + float real[ANAL_BLOCKL_MAX], imag[HALF_ANAL_BLOCKL]; + // Variables during startup. + float sum_log_i = 0.0; + float sum_log_i_square = 0.0; + float sum_log_magn = 0.0; + float sum_log_i_log_magn = 0.0; + float parametric_exp = 0.0; + float parametric_num = 0.0; + + // Check that initiation has been done. + if (self->initFlag != 1) { + return (-1); + } + updateParsFlag = self->modelUpdatePars[0]; + + // Update analysis buffer for L band. + UpdateBuffer(speechFrame, self->blockLen, self->anaLen, self->analyzeBuf); + + Windowing(self->window, self->analyzeBuf, self->anaLen, winData); + energy = Energy(winData, self->anaLen); + if (energy == 0.0) { + // We want to avoid updating statistics in this case: + // Updating feature statistics when we have zeros only will cause + // thresholds to move towards zero signal situations. This in turn has the + // effect that once the signal is "turned on" (non-zero values) everything + // will be treated as speech and there is no noise suppression effect. + // Depending on the duration of the inactive signal it takes a + // considerable amount of time for the system to learn what is noise and + // what is speech. + return 0; + } + + self->blockInd++; // Update the block index only when we process a block. + + FFT(self, winData, self->anaLen, self->magnLen, real, imag, magn); + + for (i = 0; i < self->magnLen; i++) { + signalEnergy += real[i] * real[i] + imag[i] * imag[i]; + sumMagn += magn[i]; + if (self->blockInd < END_STARTUP_SHORT) { + if (i >= kStartBand) { + tmpFloat2 = log((float)i); + sum_log_i += tmpFloat2; + sum_log_i_square += tmpFloat2 * tmpFloat2; + tmpFloat1 = log(magn[i]); + sum_log_magn += tmpFloat1; + sum_log_i_log_magn += tmpFloat2 * tmpFloat1; + } + } + } + signalEnergy = signalEnergy / ((float)self->magnLen); + self->signalEnergy = signalEnergy; + self->sumMagn = sumMagn; + + // Quantile noise estimate. + NoiseEstimation(self, magn, noise); + // Compute simplified noise model during startup. + if (self->blockInd < END_STARTUP_SHORT) { + // Estimate White noise. + self->whiteNoiseLevel += sumMagn / ((float)self->magnLen) * self->overdrive; + // Estimate Pink noise parameters. + tmpFloat1 = sum_log_i_square * ((float)(self->magnLen - kStartBand)); + tmpFloat1 -= (sum_log_i * sum_log_i); + tmpFloat2 = + (sum_log_i_square * sum_log_magn - sum_log_i * sum_log_i_log_magn); + tmpFloat3 = tmpFloat2 / tmpFloat1; + // Constrain the estimated spectrum to be positive. + if (tmpFloat3 < 0.f) { + tmpFloat3 = 0.f; + } + self->pinkNoiseNumerator += tmpFloat3; + tmpFloat2 = (sum_log_i * sum_log_magn); + tmpFloat2 -= ((float)(self->magnLen - kStartBand)) * sum_log_i_log_magn; + tmpFloat3 = tmpFloat2 / tmpFloat1; + // Constrain the pink noise power to be in the interval [0, 1]. + if (tmpFloat3 < 0.f) { + tmpFloat3 = 0.f; + } + if (tmpFloat3 > 1.f) { + tmpFloat3 = 1.f; + } + self->pinkNoiseExp += tmpFloat3; + + // Calculate frequency independent parts of parametric noise estimate. + if (self->pinkNoiseExp > 0.f) { + // Use pink noise estimate. + parametric_num = + exp(self->pinkNoiseNumerator / (float)(self->blockInd + 1)); + parametric_num *= (float)(self->blockInd + 1); + parametric_exp = self->pinkNoiseExp / (float)(self->blockInd + 1); + } + for (i = 0; i < self->magnLen; i++) { + // Estimate the background noise using the white and pink noise + // parameters. + if (self->pinkNoiseExp == 0.f) { + // Use white noise estimate. + self->parametricNoise[i] = self->whiteNoiseLevel; + } else { + // Use pink noise estimate. + float use_band = (float)(i < kStartBand ? kStartBand : i); + self->parametricNoise[i] = + parametric_num / pow(use_band, parametric_exp); + } + // Weight quantile noise with modeled noise. + noise[i] *= (self->blockInd); + tmpFloat2 = + self->parametricNoise[i] * (END_STARTUP_SHORT - self->blockInd); + noise[i] += (tmpFloat2 / (float)(self->blockInd + 1)); + noise[i] /= END_STARTUP_SHORT; + } + } + // Compute average signal during END_STARTUP_LONG time: + // used to normalize spectral difference measure. + if (self->blockInd < END_STARTUP_LONG) { + self->featureData[5] *= self->blockInd; + self->featureData[5] += signalEnergy; + self->featureData[5] /= (self->blockInd + 1); + } + + // Post and prior SNR needed for SpeechNoiseProb. + ComputeSnr(self, magn, noise, snrLocPrior, snrLocPost); + + FeatureUpdate(self, magn, updateParsFlag); + SpeechNoiseProb(self, self->speechProb, snrLocPrior, snrLocPost); + UpdateNoiseEstimate(self, magn, snrLocPrior, snrLocPost, noise); + + // Keep track of noise spectrum for next frame. + memcpy(self->noise, noise, sizeof(*noise) * self->magnLen); + memcpy(self->magnPrevAnalyze, magn, sizeof(*magn) * self->magnLen); + + return 0; +} + +int WebRtcNs_ProcessCore(NSinst_t* self, + float* speechFrame, + float* speechFrameHB, + float* outFrame, + float* outFrameHB) { + // Main routine for noise reduction. + int flagHB = 0; + int i; + + float energy1, energy2, gain, factor, factor1, factor2; + float fout[BLOCKL_MAX]; + float winData[ANAL_BLOCKL_MAX]; + float magn[HALF_ANAL_BLOCKL]; + float theFilter[HALF_ANAL_BLOCKL], theFilterTmp[HALF_ANAL_BLOCKL]; + float real[ANAL_BLOCKL_MAX], imag[HALF_ANAL_BLOCKL]; + + // SWB variables. + int deltaBweHB = 1; + int deltaGainHB = 1; + float decayBweHB = 1.0; + float gainMapParHB = 1.0; + float gainTimeDomainHB = 1.0; + float avgProbSpeechHB, avgProbSpeechHBTmp, avgFilterGainHB, gainModHB; + float sumMagnAnalyze, sumMagnProcess; + + // Check that initiation has been done. + if (self->initFlag != 1) { + return (-1); + } + // Check for valid pointers based on sampling rate. + if (self->fs == 32000) { if (speechFrameHB == NULL) { return -1; } flagHB = 1; - // range for averaging low band quantities for H band gain - deltaBweHB = (int)inst->magnLen / 4; + // Range for averaging low band quantities for H band gain. + deltaBweHB = (int)self->magnLen / 4; deltaGainHB = deltaBweHB; } - // - updateParsFlag = inst->modelUpdatePars[0]; - // - - //for LB do all processing - // convert to float - for (i = 0; i < inst->blockLen10ms; i++) { - fin[i] = (float)speechFrame[i]; - } - // update analysis buffer for L band - memcpy(inst->dataBuf, inst->dataBuf + inst->blockLen10ms, - sizeof(float) * (inst->anaLen - inst->blockLen10ms)); - memcpy(inst->dataBuf + inst->anaLen - inst->blockLen10ms, fin, - sizeof(float) * inst->blockLen10ms); + + // Update analysis buffer for L band. + UpdateBuffer(speechFrame, self->blockLen, self->anaLen, self->dataBuf); if (flagHB == 1) { - // convert to float - for (i = 0; i < inst->blockLen10ms; i++) { - fin[i] = (float)speechFrameHB[i]; - } - // update analysis buffer for H band - memcpy(inst->dataBufHB, inst->dataBufHB + inst->blockLen10ms, - sizeof(float) * (inst->anaLen - inst->blockLen10ms)); - memcpy(inst->dataBufHB + inst->anaLen - inst->blockLen10ms, fin, - sizeof(float) * inst->blockLen10ms); - } - - // check if processing needed - if (inst->outLen == 0) { - // windowing - energy1 = 0.0; - for (i = 0; i < inst->anaLen; i++) { - winData[i] = inst->window[i] * inst->dataBuf[i]; - energy1 += winData[i] * winData[i]; - } - if (energy1 == 0.0) { - // synthesize the special case of zero input - // we want to avoid updating statistics in this case: - // Updating feature statistics when we have zeros only will cause thresholds to - // move towards zero signal situations. This in turn has the effect that once the - // signal is "turned on" (non-zero values) everything will be treated as speech - // and there is no noise suppression effect. Depending on the duration of the - // inactive signal it takes a considerable amount of time for the system to learn - // what is noise and what is speech. - - // read out fully processed segment - for (i = inst->windShift; i < inst->blockLen + inst->windShift; i++) { - fout[i - inst->windShift] = inst->syntBuf[i]; - } - // update synthesis buffer - memcpy(inst->syntBuf, inst->syntBuf + inst->blockLen, - sizeof(float) * (inst->anaLen - inst->blockLen)); - memset(inst->syntBuf + inst->anaLen - inst->blockLen, 0, - sizeof(float) * inst->blockLen); - - // out buffer - inst->outLen = inst->blockLen - inst->blockLen10ms; - if (inst->blockLen > inst->blockLen10ms) { - for (i = 0; i < inst->outLen; i++) { - inst->outBuf[i] = fout[i + inst->blockLen10ms]; - } - } - // convert to short - for (i = 0; i < inst->blockLen10ms; i++) { - dTmp = fout[i]; - if (dTmp < WEBRTC_SPL_WORD16_MIN) { - dTmp = WEBRTC_SPL_WORD16_MIN; - } else if (dTmp > WEBRTC_SPL_WORD16_MAX) { - dTmp = WEBRTC_SPL_WORD16_MAX; - } - outFrame[i] = (short)dTmp; - } + // Update analysis buffer for H band. + UpdateBuffer(speechFrameHB, self->blockLen, self->anaLen, self->dataBufHB); + } - // for time-domain gain of HB - if (flagHB == 1) { - for (i = 0; i < inst->blockLen10ms; i++) { - dTmp = inst->dataBufHB[i]; - if (dTmp < WEBRTC_SPL_WORD16_MIN) { - dTmp = WEBRTC_SPL_WORD16_MIN; - } else if (dTmp > WEBRTC_SPL_WORD16_MAX) { - dTmp = WEBRTC_SPL_WORD16_MAX; - } - outFrameHB[i] = (short)dTmp; - } - } // end of H band gain computation - // - return 0; - } - - // - inst->blockInd++; // Update the block index only when we process a block. - // FFT - WebRtc_rdft(inst->anaLen, 1, winData, inst->ip, inst->wfft); - - imag[0] = 0; - real[0] = winData[0]; - magn[0] = (float)(fabs(real[0]) + 1.0f); - imag[inst->magnLen - 1] = 0; - real[inst->magnLen - 1] = winData[1]; - magn[inst->magnLen - 1] = (float)(fabs(real[inst->magnLen - 1]) + 1.0f); - signalEnergy = (float)(real[0] * real[0]) + - (float)(real[inst->magnLen - 1] * real[inst->magnLen - 1]); - sumMagn = magn[0] + magn[inst->magnLen - 1]; - if (inst->blockInd < END_STARTUP_SHORT) { - inst->initMagnEst[0] += magn[0]; - inst->initMagnEst[inst->magnLen - 1] += magn[inst->magnLen - 1]; - tmpFloat2 = log((float)(inst->magnLen - 1)); - sum_log_i = tmpFloat2; - sum_log_i_square = tmpFloat2 * tmpFloat2; - tmpFloat1 = log(magn[inst->magnLen - 1]); - sum_log_magn = tmpFloat1; - sum_log_i_log_magn = tmpFloat2 * tmpFloat1; - } - for (i = 1; i < inst->magnLen - 1; i++) { - real[i] = winData[2 * i]; - imag[i] = winData[2 * i + 1]; - // magnitude spectrum - fTmp = real[i] * real[i]; - fTmp += imag[i] * imag[i]; - signalEnergy += fTmp; - magn[i] = ((float)sqrt(fTmp)) + 1.0f; - sumMagn += magn[i]; - if (inst->blockInd < END_STARTUP_SHORT) { - inst->initMagnEst[i] += magn[i]; - if (i >= kStartBand) { - tmpFloat2 = log((float)i); - sum_log_i += tmpFloat2; - sum_log_i_square += tmpFloat2 * tmpFloat2; - tmpFloat1 = log(magn[i]); - sum_log_magn += tmpFloat1; - sum_log_i_log_magn += tmpFloat2 * tmpFloat1; - } - } + Windowing(self->window, self->dataBuf, self->anaLen, winData); + energy1 = Energy(winData, self->anaLen); + if (energy1 == 0.0) { + // Synthesize the special case of zero input. + // Read out fully processed segment. + for (i = self->windShift; i < self->blockLen + self->windShift; i++) { + fout[i - self->windShift] = self->syntBuf[i]; } - signalEnergy = signalEnergy / ((float)inst->magnLen); - inst->signalEnergy = signalEnergy; - inst->sumMagn = sumMagn; - - //compute spectral flatness on input spectrum - WebRtcNs_ComputeSpectralFlatness(inst, magn); - // quantile noise estimate - WebRtcNs_NoiseEstimation(inst, magn, noise); - //compute simplified noise model during startup - if (inst->blockInd < END_STARTUP_SHORT) { - // Estimate White noise - inst->whiteNoiseLevel += sumMagn / ((float)inst->magnLen) * inst->overdrive; - // Estimate Pink noise parameters - tmpFloat1 = sum_log_i_square * ((float)(inst->magnLen - kStartBand)); - tmpFloat1 -= (sum_log_i * sum_log_i); - tmpFloat2 = (sum_log_i_square * sum_log_magn - sum_log_i * sum_log_i_log_magn); - tmpFloat3 = tmpFloat2 / tmpFloat1; - // Constrain the estimated spectrum to be positive - if (tmpFloat3 < 0.0f) { - tmpFloat3 = 0.0f; - } - inst->pinkNoiseNumerator += tmpFloat3; - tmpFloat2 = (sum_log_i * sum_log_magn); - tmpFloat2 -= ((float)(inst->magnLen - kStartBand)) * sum_log_i_log_magn; - tmpFloat3 = tmpFloat2 / tmpFloat1; - // Constrain the pink noise power to be in the interval [0, 1]; - if (tmpFloat3 < 0.0f) { - tmpFloat3 = 0.0f; - } - if (tmpFloat3 > 1.0f) { - tmpFloat3 = 1.0f; - } - inst->pinkNoiseExp += tmpFloat3; + // Update synthesis buffer. + UpdateBuffer(NULL, self->blockLen, self->anaLen, self->syntBuf); - // Calculate frequency independent parts of parametric noise estimate. - if (inst->pinkNoiseExp == 0.0f) { - // Use white noise estimate - parametric_noise = inst->whiteNoiseLevel; - } else { - // Use pink noise estimate - parametric_num = exp(inst->pinkNoiseNumerator / (float)(inst->blockInd + 1)); - parametric_num *= (float)(inst->blockInd + 1); - parametric_exp = inst->pinkNoiseExp / (float)(inst->blockInd + 1); - parametric_noise = parametric_num / pow((float)kStartBand, parametric_exp); - } - for (i = 0; i < inst->magnLen; i++) { - // Estimate the background noise using the white and pink noise parameters - if ((inst->pinkNoiseExp > 0.0f) && (i >= kStartBand)) { - // Use pink noise estimate - parametric_noise = parametric_num / pow((float)i, parametric_exp); - } - theFilterTmp[i] = (inst->initMagnEst[i] - inst->overdrive * parametric_noise); - theFilterTmp[i] /= (inst->initMagnEst[i] + (float)0.0001); - // Weight quantile noise with modeled noise - noise[i] *= (inst->blockInd); - tmpFloat2 = parametric_noise * (END_STARTUP_SHORT - inst->blockInd); - noise[i] += (tmpFloat2 / (float)(inst->blockInd + 1)); - noise[i] /= END_STARTUP_SHORT; - } - } - //compute average signal during END_STARTUP_LONG time: - // used to normalize spectral difference measure - if (inst->blockInd < END_STARTUP_LONG) { - inst->featureData[5] *= inst->blockInd; - inst->featureData[5] += signalEnergy; - inst->featureData[5] /= (inst->blockInd + 1); + for (i = 0; i < self->blockLen; ++i) + outFrame[i] = + WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, fout[i], WEBRTC_SPL_WORD16_MIN); + + // For time-domain gain of HB. + if (flagHB == 1) + for (i = 0; i < self->blockLen; ++i) + outFrameHB[i] = WEBRTC_SPL_SAT( + WEBRTC_SPL_WORD16_MAX, self->dataBufHB[i], WEBRTC_SPL_WORD16_MIN); + + return 0; + } + + FFT(self, winData, self->anaLen, self->magnLen, real, imag, magn); + + if (self->blockInd < END_STARTUP_SHORT) { + for (i = 0; i < self->magnLen; i++) { + self->initMagnEst[i] += magn[i]; } + } -#ifdef PROCESS_FLOW_0 - if (inst->blockInd > END_STARTUP_LONG) { - //option: average the quantile noise: for check with AEC2 - for (i = 0; i < inst->magnLen; i++) { - noise[i] = (float)0.6 * inst->noisePrev[i] + (float)0.4 * noise[i]; - } - for (i = 0; i < inst->magnLen; i++) { - // Wiener with over sub-substraction: - theFilter[i] = (magn[i] - inst->overdrive * noise[i]) / (magn[i] + (float)0.0001); - } + ComputeDdBasedWienerFilter(self, magn, theFilter); + + for (i = 0; i < self->magnLen; i++) { + // Flooring bottom. + if (theFilter[i] < self->denoiseBound) { + theFilter[i] = self->denoiseBound; } -#else - //start processing at frames == converged+1 - // - // STEP 1: compute prior and post snr based on quantile noise est - // - - // compute DD estimate of prior SNR: needed for new method - for (i = 0; i < inst->magnLen; i++) { - // post snr - snrLocPost[i] = (float)0.0; - if (magn[i] > noise[i]) { - snrLocPost[i] = magn[i] / (noise[i] + (float)0.0001) - (float)1.0; - } - // previous post snr - // previous estimate: based on previous frame with gain filter - previousEstimateStsa[i] = inst->magnPrev[i] / (inst->noisePrev[i] + (float)0.0001) - * (inst->smooth[i]); - // DD estimate is sum of two terms: current estimate and previous estimate - // directed decision update of snrPrior - snrLocPrior[i] = DD_PR_SNR * previousEstimateStsa[i] + ((float)1.0 - DD_PR_SNR) - * snrLocPost[i]; - // post and prior snr needed for step 2 - } // end of loop over freqs -#ifdef PROCESS_FLOW_1 - for (i = 0; i < inst->magnLen; i++) { - // gain filter - tmpFloat1 = inst->overdrive + snrLocPrior[i]; - tmpFloat2 = (float)snrLocPrior[i] / tmpFloat1; - theFilter[i] = (float)tmpFloat2; - } // end of loop over freqs -#endif - // done with step 1: dd computation of prior and post snr - - // - //STEP 2: compute speech/noise likelihood - // -#ifdef PROCESS_FLOW_2 - // compute difference of input spectrum with learned/estimated noise spectrum - WebRtcNs_ComputeSpectralDifference(inst, magn); - // compute histograms for parameter decisions (thresholds and weights for features) - // parameters are extracted once every window time (=inst->modelUpdatePars[1]) - if (updateParsFlag >= 1) { - // counter update - inst->modelUpdatePars[3]--; - // update histogram - if (inst->modelUpdatePars[3] > 0) { - WebRtcNs_FeatureParameterExtraction(inst, 0); - } - // compute model parameters - if (inst->modelUpdatePars[3] == 0) { - WebRtcNs_FeatureParameterExtraction(inst, 1); - inst->modelUpdatePars[3] = inst->modelUpdatePars[1]; - // if wish to update only once, set flag to zero - if (updateParsFlag == 1) { - inst->modelUpdatePars[0] = 0; - } else { - // update every window: - // get normalization for spectral difference for next window estimate - inst->featureData[6] = inst->featureData[6] - / ((float)inst->modelUpdatePars[1]); - inst->featureData[5] = (float)0.5 * (inst->featureData[6] - + inst->featureData[5]); - inst->featureData[6] = (float)0.0; - } - } + // Flooring top. + if (theFilter[i] > 1.f) { + theFilter[i] = 1.f; } - // compute speech/noise probability - WebRtcNs_SpeechNoiseProb(inst, probSpeechFinal, snrLocPrior, snrLocPost); - // time-avg parameter for noise update - gammaNoiseTmp = NOISE_UPDATE; - for (i = 0; i < inst->magnLen; i++) { - probSpeech = probSpeechFinal[i]; - probNonSpeech = (float)1.0 - probSpeech; - // temporary noise update: - // use it for speech frames if update value is less than previous - noiseUpdateTmp = gammaNoiseTmp * inst->noisePrev[i] + ((float)1.0 - gammaNoiseTmp) - * (probNonSpeech * magn[i] + probSpeech * inst->noisePrev[i]); - // - // time-constant based on speech/noise state - gammaNoiseOld = gammaNoiseTmp; - gammaNoiseTmp = NOISE_UPDATE; - // increase gamma (i.e., less noise update) for frame likely to be speech - if (probSpeech > PROB_RANGE) { - gammaNoiseTmp = SPEECH_UPDATE; + if (self->blockInd < END_STARTUP_SHORT) { + theFilterTmp[i] = + (self->initMagnEst[i] - self->overdrive * self->parametricNoise[i]); + theFilterTmp[i] /= (self->initMagnEst[i] + 0.0001f); + // Flooring bottom. + if (theFilterTmp[i] < self->denoiseBound) { + theFilterTmp[i] = self->denoiseBound; } - // conservative noise update - if (probSpeech < PROB_RANGE) { - inst->magnAvgPause[i] += GAMMA_PAUSE * (magn[i] - inst->magnAvgPause[i]); + // Flooring top. + if (theFilterTmp[i] > 1.f) { + theFilterTmp[i] = 1.f; } - // noise update - if (gammaNoiseTmp == gammaNoiseOld) { - noise[i] = noiseUpdateTmp; - } else { - noise[i] = gammaNoiseTmp * inst->noisePrev[i] + ((float)1.0 - gammaNoiseTmp) - * (probNonSpeech * magn[i] + probSpeech * inst->noisePrev[i]); - // allow for noise update downwards: - // if noise update decreases the noise, it is safe, so allow it to happen - if (noiseUpdateTmp < noise[i]) { - noise[i] = noiseUpdateTmp; - } - } - } // end of freq loop - // done with step 2: noise update + // Weight the two suppression filters. + theFilter[i] *= (self->blockInd); + theFilterTmp[i] *= (END_STARTUP_SHORT - self->blockInd); + theFilter[i] += theFilterTmp[i]; + theFilter[i] /= (END_STARTUP_SHORT); + } - // - // STEP 3: compute dd update of prior snr and post snr based on new noise estimate - // - for (i = 0; i < inst->magnLen; i++) { - // post and prior snr - currentEstimateStsa = (float)0.0; - if (magn[i] > noise[i]) { - currentEstimateStsa = magn[i] / (noise[i] + (float)0.0001) - (float)1.0; - } - // DD estimate is sume of two terms: current estimate and previous estimate - // directed decision update of snrPrior - snrPrior = DD_PR_SNR * previousEstimateStsa[i] + ((float)1.0 - DD_PR_SNR) - * currentEstimateStsa; - // gain filter - tmpFloat1 = inst->overdrive + snrPrior; - tmpFloat2 = (float)snrPrior / tmpFloat1; - theFilter[i] = (float)tmpFloat2; - } // end of loop over freqs - // done with step3 -#endif -#endif - - for (i = 0; i < inst->magnLen; i++) { - // flooring bottom - if (theFilter[i] < inst->denoiseBound) { - theFilter[i] = inst->denoiseBound; - } - // flooring top - if (theFilter[i] > (float)1.0) { - theFilter[i] = 1.0; - } - if (inst->blockInd < END_STARTUP_SHORT) { - // flooring bottom - if (theFilterTmp[i] < inst->denoiseBound) { - theFilterTmp[i] = inst->denoiseBound; - } - // flooring top - if (theFilterTmp[i] > (float)1.0) { - theFilterTmp[i] = 1.0; - } - // Weight the two suppression filters - theFilter[i] *= (inst->blockInd); - theFilterTmp[i] *= (END_STARTUP_SHORT - inst->blockInd); - theFilter[i] += theFilterTmp[i]; - theFilter[i] /= (END_STARTUP_SHORT); - } - // smoothing -#ifdef PROCESS_FLOW_0 - inst->smooth[i] *= SMOOTH; // value set to 0.7 in define.h file - inst->smooth[i] += ((float)1.0 - SMOOTH) * theFilter[i]; -#else - inst->smooth[i] = theFilter[i]; -#endif - real[i] *= inst->smooth[i]; - imag[i] *= inst->smooth[i]; - } - // keep track of noise and magn spectrum for next frame - for (i = 0; i < inst->magnLen; i++) { - inst->noisePrev[i] = noise[i]; - inst->magnPrev[i] = magn[i]; - } - // back to time domain - winData[0] = real[0]; - winData[1] = real[inst->magnLen - 1]; - for (i = 1; i < inst->magnLen - 1; i++) { - winData[2 * i] = real[i]; - winData[2 * i + 1] = imag[i]; - } - WebRtc_rdft(inst->anaLen, -1, winData, inst->ip, inst->wfft); - - for (i = 0; i < inst->anaLen; i++) { - real[i] = 2.0f * winData[i] / inst->anaLen; // fft scaling - } - - //scale factor: only do it after END_STARTUP_LONG time - factor = (float)1.0; - if (inst->gainmap == 1 && inst->blockInd > END_STARTUP_LONG) { - factor1 = (float)1.0; - factor2 = (float)1.0; - - energy2 = 0.0; - for (i = 0; i < inst->anaLen; i++) { - energy2 += (float)real[i] * (float)real[i]; - } - gain = (float)sqrt(energy2 / (energy1 + (float)1.0)); + self->smooth[i] = theFilter[i]; + real[i] *= self->smooth[i]; + imag[i] *= self->smooth[i]; + } + // Keep track of |magn| spectrum for next frame. + memcpy(self->magnPrevProcess, magn, sizeof(*magn) * self->magnLen); + memcpy(self->noisePrev, self->noise, sizeof(self->noise[0]) * self->magnLen); + // Back to time domain. + IFFT(self, real, imag, self->magnLen, self->anaLen, winData); -#ifdef PROCESS_FLOW_2 - // scaling for new version - if (gain > B_LIM) { - factor1 = (float)1.0 + (float)1.3 * (gain - B_LIM); - if (gain * factor1 > (float)1.0) { - factor1 = (float)1.0 / gain; - } - } - if (gain < B_LIM) { - //don't reduce scale too much for pause regions: - // attenuation here should be controlled by flooring - if (gain <= inst->denoiseBound) { - gain = inst->denoiseBound; - } - factor2 = (float)1.0 - (float)0.3 * (B_LIM - gain); - } - //combine both scales with speech/noise prob: - // note prior (priorSpeechProb) is not frequency dependent - factor = inst->priorSpeechProb * factor1 + ((float)1.0 - inst->priorSpeechProb) - * factor2; -#else - if (gain > B_LIM) { - factor = (float)1.0 + (float)1.3 * (gain - B_LIM); - } else { - factor = (float)1.0 + (float)2.0 * (gain - B_LIM); - } - if (gain * factor > (float)1.0) { - factor = (float)1.0 / gain; - } -#endif - } // out of inst->gainmap==1 + // Scale factor: only do it after END_STARTUP_LONG time. + factor = 1.f; + if (self->gainmap == 1 && self->blockInd > END_STARTUP_LONG) { + factor1 = 1.f; + factor2 = 1.f; + + energy2 = Energy(winData, self->anaLen); + gain = (float)sqrt(energy2 / (energy1 + 1.f)); - // synthesis - for (i = 0; i < inst->anaLen; i++) { - inst->syntBuf[i] += factor * inst->window[i] * (float)real[i]; - } - // read out fully processed segment - for (i = inst->windShift; i < inst->blockLen + inst->windShift; i++) { - fout[i - inst->windShift] = inst->syntBuf[i]; - } - // update synthesis buffer - memcpy(inst->syntBuf, inst->syntBuf + inst->blockLen, - sizeof(float) * (inst->anaLen - inst->blockLen)); - memset(inst->syntBuf + inst->anaLen - inst->blockLen, 0, - sizeof(float) * inst->blockLen); - - // out buffer - inst->outLen = inst->blockLen - inst->blockLen10ms; - if (inst->blockLen > inst->blockLen10ms) { - for (i = 0; i < inst->outLen; i++) { - inst->outBuf[i] = fout[i + inst->blockLen10ms]; + // Scaling for new version. + if (gain > B_LIM) { + factor1 = 1.f + 1.3f * (gain - B_LIM); + if (gain * factor1 > 1.f) { + factor1 = 1.f / gain; } } - } // end of if out.len==0 - else { - for (i = 0; i < inst->blockLen10ms; i++) { - fout[i] = inst->outBuf[i]; - } - memcpy(inst->outBuf, inst->outBuf + inst->blockLen10ms, - sizeof(float) * (inst->outLen - inst->blockLen10ms)); - memset(inst->outBuf + inst->outLen - inst->blockLen10ms, 0, - sizeof(float) * inst->blockLen10ms); - inst->outLen -= inst->blockLen10ms; - } - - // convert to short - for (i = 0; i < inst->blockLen10ms; i++) { - dTmp = fout[i]; - if (dTmp < WEBRTC_SPL_WORD16_MIN) { - dTmp = WEBRTC_SPL_WORD16_MIN; - } else if (dTmp > WEBRTC_SPL_WORD16_MAX) { - dTmp = WEBRTC_SPL_WORD16_MAX; + if (gain < B_LIM) { + // Don't reduce scale too much for pause regions: + // attenuation here should be controlled by flooring. + if (gain <= self->denoiseBound) { + gain = self->denoiseBound; + } + factor2 = 1.f - 0.3f * (B_LIM - gain); } - outFrame[i] = (short)dTmp; + // Combine both scales with speech/noise prob: + // note prior (priorSpeechProb) is not frequency dependent. + factor = self->priorSpeechProb * factor1 + + (1.f - self->priorSpeechProb) * factor2; + } // Out of self->gainmap == 1. + + Windowing(self->window, winData, self->anaLen, winData); + + // Synthesis. + for (i = 0; i < self->anaLen; i++) { + self->syntBuf[i] += factor * winData[i]; } + // Read out fully processed segment. + for (i = self->windShift; i < self->blockLen + self->windShift; i++) { + fout[i - self->windShift] = self->syntBuf[i]; + } + // Update synthesis buffer. + UpdateBuffer(NULL, self->blockLen, self->anaLen, self->syntBuf); + + for (i = 0; i < self->blockLen; ++i) + outFrame[i] = + WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, fout[i], WEBRTC_SPL_WORD16_MIN); - // for time-domain gain of HB + // For time-domain gain of HB. if (flagHB == 1) { - for (i = 0; i < inst->magnLen; i++) { - inst->speechProbHB[i] = probSpeechFinal[i]; - } - // average speech prob from low band - // avg over second half (i.e., 4->8kHz) of freq. spectrum + // Average speech prob from low band. + // Average over second half (i.e., 4->8kHz) of frequencies spectrum. avgProbSpeechHB = 0.0; - for (i = inst->magnLen - deltaBweHB - 1; i < inst->magnLen - 1; i++) { - avgProbSpeechHB += inst->speechProbHB[i]; + for (i = self->magnLen - deltaBweHB - 1; i < self->magnLen - 1; i++) { + avgProbSpeechHB += self->speechProb[i]; } avgProbSpeechHB = avgProbSpeechHB / ((float)deltaBweHB); - // average filter gain from low band - // average over second half (i.e., 4->8kHz) of freq. spectrum + // If the speech was suppressed by a component between Analyze and + // Process, for example the AEC, then it should not be considered speech + // for high band suppression purposes. + sumMagnAnalyze = 0; + sumMagnProcess = 0; + for (i = 0; i < self->magnLen; ++i) { + sumMagnAnalyze += self->magnPrevAnalyze[i]; + sumMagnProcess += self->magnPrevProcess[i]; + } + avgProbSpeechHB *= sumMagnProcess / sumMagnAnalyze; + // Average filter gain from low band. + // Average over second half (i.e., 4->8kHz) of frequencies spectrum. avgFilterGainHB = 0.0; - for (i = inst->magnLen - deltaGainHB - 1; i < inst->magnLen - 1; i++) { - avgFilterGainHB += inst->smooth[i]; + for (i = self->magnLen - deltaGainHB - 1; i < self->magnLen - 1; i++) { + avgFilterGainHB += self->smooth[i]; } avgFilterGainHB = avgFilterGainHB / ((float)(deltaGainHB)); - avgProbSpeechHBTmp = (float)2.0 * avgProbSpeechHB - (float)1.0; - // gain based on speech prob: - gainModHB = (float)0.5 * ((float)1.0 + (float)tanh(gainMapParHB * avgProbSpeechHBTmp)); - //combine gain with low band gain - gainTimeDomainHB = (float)0.5 * gainModHB + (float)0.5 * avgFilterGainHB; - if (avgProbSpeechHB >= (float)0.5) { - gainTimeDomainHB = (float)0.25 * gainModHB + (float)0.75 * avgFilterGainHB; + avgProbSpeechHBTmp = 2.f * avgProbSpeechHB - 1.f; + // Gain based on speech probability. + gainModHB = 0.5f * (1.f + (float)tanh(gainMapParHB * avgProbSpeechHBTmp)); + // Combine gain with low band gain. + gainTimeDomainHB = 0.5f * gainModHB + 0.5f * avgFilterGainHB; + if (avgProbSpeechHB >= 0.5f) { + gainTimeDomainHB = 0.25f * gainModHB + 0.75f * avgFilterGainHB; } gainTimeDomainHB = gainTimeDomainHB * decayBweHB; - //make sure gain is within flooring range - // flooring bottom - if (gainTimeDomainHB < inst->denoiseBound) { - gainTimeDomainHB = inst->denoiseBound; - } - // flooring top - if (gainTimeDomainHB > (float)1.0) { - gainTimeDomainHB = 1.0; - } - //apply gain - for (i = 0; i < inst->blockLen10ms; i++) { - dTmp = gainTimeDomainHB * inst->dataBufHB[i]; - if (dTmp < WEBRTC_SPL_WORD16_MIN) { - dTmp = WEBRTC_SPL_WORD16_MIN; - } else if (dTmp > WEBRTC_SPL_WORD16_MAX) { - dTmp = WEBRTC_SPL_WORD16_MAX; - } - outFrameHB[i] = (short)dTmp; + // Make sure gain is within flooring range. + // Flooring bottom. + if (gainTimeDomainHB < self->denoiseBound) { + gainTimeDomainHB = self->denoiseBound; + } + // Flooring top. + if (gainTimeDomainHB > 1.f) { + gainTimeDomainHB = 1.f; + } + // Apply gain. + for (i = 0; i < self->blockLen; i++) { + float o = gainTimeDomainHB * self->dataBufHB[i]; + outFrameHB[i] = + WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, o, WEBRTC_SPL_WORD16_MIN); } - } // end of H band gain computation - // + } // End of H band gain computation. return 0; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/ns_core.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/ns_core.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/ns_core.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/ns_core.h 2015-02-03 14:33:35.000000000 +0000 @@ -8,106 +8,110 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NS_CORE_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NS_CORE_H_ +#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_NS_CORE_H_ +#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_NS_CORE_H_ #include "webrtc/modules/audio_processing/ns/defines.h" typedef struct NSParaExtract_t_ { - - //bin size of histogram + // Bin size of histogram. float binSizeLrt; float binSizeSpecFlat; float binSizeSpecDiff; - //range of histogram over which lrt threshold is computed + // Range of histogram over which LRT threshold is computed. float rangeAvgHistLrt; - //scale parameters: multiply dominant peaks of the histograms by scale factor to obtain - //thresholds for prior model - float factor1ModelPars; //for lrt and spectral difference - float factor2ModelPars; //for spectral_flatness: used when noise is flatter than speech - //peak limit for spectral flatness (varies between 0 and 1) + // Scale parameters: multiply dominant peaks of the histograms by scale factor + // to obtain thresholds for prior model. + float factor1ModelPars; // For LRT and spectral difference. + float factor2ModelPars; // For spectral_flatness: used when noise is flatter + // than speech. + // Peak limit for spectral flatness (varies between 0 and 1). float thresPosSpecFlat; - //limit on spacing of two highest peaks in histogram: spacing determined by bin size + // Limit on spacing of two highest peaks in histogram: spacing determined by + // bin size. float limitPeakSpacingSpecFlat; float limitPeakSpacingSpecDiff; - //limit on relevance of second peak: + // Limit on relevance of second peak. float limitPeakWeightsSpecFlat; float limitPeakWeightsSpecDiff; - //limit on fluctuation of lrt feature + // Limit on fluctuation of LRT feature. float thresFluctLrt; - //limit on the max and min values for the feature thresholds + // Limit on the max and min values for the feature thresholds. float maxLrt; float minLrt; float maxSpecFlat; float minSpecFlat; float maxSpecDiff; float minSpecDiff; - //criteria of weight of histogram peak to accept/reject feature + // Criteria of weight of histogram peak to accept/reject feature. int thresWeightSpecFlat; int thresWeightSpecDiff; } NSParaExtract_t; typedef struct NSinst_t_ { - - uint32_t fs; - int blockLen; - int blockLen10ms; - int windShift; - int outLen; - int anaLen; - int magnLen; - int aggrMode; - const float* window; - float dataBuf[ANAL_BLOCKL_MAX]; - float syntBuf[ANAL_BLOCKL_MAX]; - float outBuf[3 * BLOCKL_MAX]; - - int initFlag; - // parameters for quantile noise estimation - float density[SIMULT* HALF_ANAL_BLOCKL]; - float lquantile[SIMULT* HALF_ANAL_BLOCKL]; - float quantile[HALF_ANAL_BLOCKL]; - int counter[SIMULT]; - int updates; - // parameters for Wiener filter - float smooth[HALF_ANAL_BLOCKL]; - float overdrive; - float denoiseBound; - int gainmap; - // fft work arrays. - int ip[IP_LENGTH]; - float wfft[W_LENGTH]; - - // parameters for new method: some not needed, will reduce/cleanup later - int32_t blockInd; //frame index counter - int modelUpdatePars[4]; //parameters for updating or estimating - // thresholds/weights for prior model - float priorModelPars[7]; //parameters for prior model - float noisePrev[HALF_ANAL_BLOCKL]; //noise spectrum from previous frame - float magnPrev[HALF_ANAL_BLOCKL]; //magnitude spectrum of previous frame - float logLrtTimeAvg[HALF_ANAL_BLOCKL]; //log lrt factor with time-smoothing - float priorSpeechProb; //prior speech/noise probability - float featureData[7]; //data for features - float magnAvgPause[HALF_ANAL_BLOCKL]; //conservative noise spectrum estimate - float signalEnergy; //energy of magn - float sumMagn; //sum of magn - float whiteNoiseLevel; //initial noise estimate - float initMagnEst[HALF_ANAL_BLOCKL]; //initial magnitude spectrum estimate - float pinkNoiseNumerator; //pink noise parameter: numerator - float pinkNoiseExp; //pink noise parameter: power of freq - NSParaExtract_t featureExtractionParams; //parameters for feature extraction - //histograms for parameter estimation - int histLrt[HIST_PAR_EST]; - int histSpecFlat[HIST_PAR_EST]; - int histSpecDiff[HIST_PAR_EST]; - //quantities for high band estimate - float speechProbHB[HALF_ANAL_BLOCKL]; //final speech/noise prob: prior + LRT - float dataBufHB[ANAL_BLOCKL_MAX]; //buffering data for HB + uint32_t fs; + int blockLen; + int windShift; + int anaLen; + int magnLen; + int aggrMode; + const float* window; + float analyzeBuf[ANAL_BLOCKL_MAX]; + float dataBuf[ANAL_BLOCKL_MAX]; + float syntBuf[ANAL_BLOCKL_MAX]; + + int initFlag; + // Parameters for quantile noise estimation. + float density[SIMULT * HALF_ANAL_BLOCKL]; + float lquantile[SIMULT * HALF_ANAL_BLOCKL]; + float quantile[HALF_ANAL_BLOCKL]; + int counter[SIMULT]; + int updates; + // Parameters for Wiener filter. + float smooth[HALF_ANAL_BLOCKL]; + float overdrive; + float denoiseBound; + int gainmap; + // FFT work arrays. + int ip[IP_LENGTH]; + float wfft[W_LENGTH]; + + // Parameters for new method: some not needed, will reduce/cleanup later. + int32_t blockInd; // Frame index counter. + int modelUpdatePars[4]; // Parameters for updating or estimating. + // Thresholds/weights for prior model. + float priorModelPars[7]; // Parameters for prior model. + float noise[HALF_ANAL_BLOCKL]; // Noise spectrum from current frame. + float noisePrev[HALF_ANAL_BLOCKL]; // Noise spectrum from previous frame. + // Magnitude spectrum of previous analyze frame. + float magnPrevAnalyze[HALF_ANAL_BLOCKL]; + // Magnitude spectrum of previous process frame. + float magnPrevProcess[HALF_ANAL_BLOCKL]; + float logLrtTimeAvg[HALF_ANAL_BLOCKL]; // Log LRT factor with time-smoothing. + float priorSpeechProb; // Prior speech/noise probability. + float featureData[7]; + // Conservative noise spectrum estimate. + float magnAvgPause[HALF_ANAL_BLOCKL]; + float signalEnergy; // Energy of |magn|. + float sumMagn; + float whiteNoiseLevel; // Initial noise estimate. + float initMagnEst[HALF_ANAL_BLOCKL]; // Initial magnitude spectrum estimate. + float pinkNoiseNumerator; // Pink noise parameter: numerator. + float pinkNoiseExp; // Pink noise parameter: power of frequencies. + float parametricNoise[HALF_ANAL_BLOCKL]; + // Parameters for feature extraction. + NSParaExtract_t featureExtractionParams; + // Histograms for parameter estimation. + int histLrt[HIST_PAR_EST]; + int histSpecFlat[HIST_PAR_EST]; + int histSpecDiff[HIST_PAR_EST]; + // Quantities for high band estimate. + float speechProb[HALF_ANAL_BLOCKL]; // Final speech/noise prob: prior + LRT. + float dataBufHB[ANAL_BLOCKL_MAX]; // Buffering data for HB. } NSinst_t; - #ifdef __cplusplus extern "C" { #endif @@ -118,16 +122,16 @@ * This function initializes a noise suppression instance * * Input: - * - inst : Instance that should be initialized + * - self : Instance that should be initialized * - fs : Sampling frequency * * Output: - * - inst : Initialized instance + * - self : Initialized instance * * Return value : 0 - Ok * -1 - Error */ -int WebRtcNs_InitCore(NSinst_t* inst, uint32_t fs); +int WebRtcNs_InitCore(NSinst_t* self, uint32_t fs); /**************************************************************************** * WebRtcNs_set_policy_core(...) @@ -135,16 +139,33 @@ * This changes the aggressiveness of the noise suppression method. * * Input: - * - inst : Instance that should be initialized - * - mode : 0: Mild (6 dB), 1: Medium (10 dB), 2: Aggressive (15 dB) + * - self : Instance that should be initialized + * - mode : 0: Mild (6dB), 1: Medium (10dB), 2: Aggressive (15dB) * * Output: - * - NS_inst : Initialized instance + * - self : Initialized instance * * Return value : 0 - Ok * -1 - Error */ -int WebRtcNs_set_policy_core(NSinst_t* inst, int mode); +int WebRtcNs_set_policy_core(NSinst_t* self, int mode); + +/**************************************************************************** + * WebRtcNs_AnalyzeCore + * + * Estimate the background noise. + * + * Input: + * - self : Instance that should be initialized + * - speechFrame : Input speech frame for lower band + * + * Output: + * - self : Updated instance + * + * Return value : 0 - OK + * -1 - Error + */ +int WebRtcNs_AnalyzeCore(NSinst_t* self, float* speechFrame); /**************************************************************************** * WebRtcNs_ProcessCore @@ -152,28 +173,25 @@ * Do noise suppression. * * Input: - * - inst : Instance that should be initialized + * - self : Instance that should be initialized * - inFrameLow : Input speech frame for lower band * - inFrameHigh : Input speech frame for higher band * * Output: - * - inst : Updated instance + * - self : Updated instance * - outFrameLow : Output speech frame for lower band * - outFrameHigh : Output speech frame for higher band * * Return value : 0 - OK * -1 - Error */ - - -int WebRtcNs_ProcessCore(NSinst_t* inst, - short* inFrameLow, - short* inFrameHigh, - short* outFrameLow, - short* outFrameHigh); - +int WebRtcNs_ProcessCore(NSinst_t* self, + float* inFrameLow, + float* inFrameHigh, + float* outFrameLow, + float* outFrameHigh); #ifdef __cplusplus } #endif -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NS_CORE_H_ +#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_NS_CORE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core.c 2015-02-03 14:33:35.000000000 +0000 @@ -319,13 +319,13 @@ tmp32no2 = WEBRTC_SPL_MUL_16_16(kExp2Const, inst->noiseEstLogQuantile[offset + i]); tmp32no1 = (0x00200000 | (tmp32no2 & 0x001FFFFF)); // 2^21 + frac - tmp16 = (int16_t) WEBRTC_SPL_RSHIFT_W32(tmp32no2, 21); + tmp16 = (int16_t)(tmp32no2 >> 21); tmp16 -= 21;// shift 21 to get result in Q0 tmp16 += (int16_t) inst->qNoise; //shift to get result in Q(qNoise) if (tmp16 < 0) { - tmp32no1 = WEBRTC_SPL_RSHIFT_W32(tmp32no1, -tmp16); + tmp32no1 >>= -tmp16; } else { - tmp32no1 = WEBRTC_SPL_LSHIFT_W32(tmp32no1, tmp16); + tmp32no1 <<= tmp16; } inst->noiseEstQuantile[i] = WebRtcSpl_SatW32ToW16(tmp32no1); } @@ -407,13 +407,11 @@ // +=QUANTILE*delta/(inst->counter[s]+1) QUANTILE=0.25, =1 in Q2 // CounterDiv=1/(inst->counter[s]+1) in Q15 tmp16 += 2; - tmp16no1 = WEBRTC_SPL_RSHIFT_W16(tmp16, 2); - inst->noiseEstLogQuantile[offset + i] += tmp16no1; + inst->noiseEstLogQuantile[offset + i] += tmp16 / 4; } else { tmp16 += 1; - tmp16no1 = WEBRTC_SPL_RSHIFT_W16(tmp16, 1); // *(1-QUANTILE), in Q2 QUANTILE=0.25, 1-0.25=0.75=3 in Q2 - tmp16no2 = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(tmp16no1, 3, 1); + tmp16no2 = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(tmp16 / 2, 3, 1); inst->noiseEstLogQuantile[offset + i] -= tmp16no2; if (inst->noiseEstLogQuantile[offset + i] < logval) { // This is the smallest fixed point representation we can @@ -503,8 +501,8 @@ tmp32 = WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(tmp16a, gain_factor, 13); // Q0 // Down shift with rounding tmp16b = WebRtcSpl_SatW32ToW16(tmp32); // Q0 - inst->synthesisBuffer[i] = WEBRTC_SPL_ADD_SAT_W16(inst->synthesisBuffer[i], - tmp16b); // Q0 + inst->synthesisBuffer[i] = WebRtcSpl_AddSatW16(inst->synthesisBuffer[i], + tmp16b); // Q0 } // read out fully processed segment @@ -545,8 +543,9 @@ const int16_t* in, int16_t* out) { int i = 0; + assert(inst->normData >= 0); for (i = 0; i < inst->anaLen; ++i) { - out[i] = WEBRTC_SPL_LSHIFT_W16(in[i], inst->normData); // Q(normData) + out[i] = in[i] << inst->normData; // Q(normData) } } @@ -598,29 +597,29 @@ assert(freq_index >= 0); assert(freq_index < 129); tmp32no2 = WEBRTC_SPL_MUL_16_16(pink_noise_exp_avg, kLogIndex[freq_index]); // Q26 - tmp32no2 = WEBRTC_SPL_RSHIFT_W32(tmp32no2, 15); // Q11 + tmp32no2 >>= 15; // Q11 tmp32no1 = pink_noise_num_avg - tmp32no2; // Q11 // Calculate output: 2^tmp32no1 // Output in Q(minNorm-stages) - tmp32no1 += WEBRTC_SPL_LSHIFT_W32((int32_t)(inst->minNorm - inst->stages), 11); + tmp32no1 += (inst->minNorm - inst->stages) << 11; if (tmp32no1 > 0) { - int_part = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32no1, 11); + int_part = (int16_t)(tmp32no1 >> 11); frac_part = (int16_t)(tmp32no1 & 0x000007ff); // Q11 // Piecewise linear approximation of 'b' in // 2^(int_part+frac_part) = 2^int_part * (1 + b) // 'b' is given in Q11 and below stored in frac_part. - if (WEBRTC_SPL_RSHIFT_W16(frac_part, 10)) { + if (frac_part >> 10) { // Upper fractional part tmp32no2 = WEBRTC_SPL_MUL_16_16(2048 - frac_part, 1244); // Q21 - tmp32no2 = 2048 - WEBRTC_SPL_RSHIFT_W32(tmp32no2, 10); + tmp32no2 = 2048 - (tmp32no2 >> 10); } else { // Lower fractional part - tmp32no2 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(frac_part, 804), 10); + tmp32no2 = (frac_part * 804) >> 10; } // Shift fractional part to Q(minNorm-stages) tmp32no2 = WEBRTC_SPL_SHIFT_W32(tmp32no2, int_part - 11); - *noise_estimate_avg = WEBRTC_SPL_LSHIFT_U32(1, int_part) + (uint32_t)tmp32no2; + *noise_estimate_avg = (1 << int_part) + (uint32_t)tmp32no2; // Scale up to initMagnEst, which is not block averaged *noise_estimate = (*noise_estimate_avg) * (uint32_t)(inst->blockIndex + 1); } @@ -668,7 +667,7 @@ inst->maxLrt = 0x0080000; inst->minLrt = 104858; } - inst->anaLen2 = WEBRTC_SPL_RSHIFT_W16(inst->anaLen, 1); + inst->anaLen2 = inst->anaLen / 2; inst->magnLen = inst->anaLen2 + 1; if (inst->real_fft != NULL) { @@ -854,7 +853,7 @@ } // Spectral flatness // (inst->featureSpecFlat*20)>>10 = (inst->featureSpecFlat*5)>>8 - histIndex = WEBRTC_SPL_RSHIFT_U32(inst->featureSpecFlat * 5, 8); + histIndex = (inst->featureSpecFlat * 5) >> 8; if (histIndex < HIST_PAR_EST) { inst->histSpecFlat[histIndex]++; } @@ -864,8 +863,8 @@ // Guard against division by zero // If timeAvgMagnEnergy == 0 we have no normalizing statistics and // therefore can't update the histogram - histIndex = WEBRTC_SPL_UDIV((inst->featureSpecDiff * 5) >> inst->stages, - inst->timeAvgMagnEnergy); + histIndex = ((inst->featureSpecDiff * 5) >> inst->stages) / + inst->timeAvgMagnEnergy; } if (histIndex < HIST_PAR_EST) { inst->histSpecDiff[histIndex]++; @@ -885,17 +884,17 @@ tmp32 = WEBRTC_SPL_MUL_16_16(inst->histLrt[i], j); avgHistLrtFX += tmp32; numHistLrt += inst->histLrt[i]; - avgSquareHistLrtFX += WEBRTC_SPL_MUL_32_16(tmp32, j); + avgSquareHistLrtFX += tmp32 * j; } avgHistLrtComplFX = avgHistLrtFX; for (; i < HIST_PAR_EST; i++) { j = (2 * i + 1); tmp32 = WEBRTC_SPL_MUL_16_16(inst->histLrt[i], j); avgHistLrtComplFX += tmp32; - avgSquareHistLrtFX += WEBRTC_SPL_MUL_32_16(tmp32, j); + avgSquareHistLrtFX += tmp32 * j; } - fluctLrtFX = WEBRTC_SPL_MUL(avgSquareHistLrtFX, numHistLrt); - fluctLrtFX -= WEBRTC_SPL_MUL(avgHistLrtFX, avgHistLrtComplFX); + fluctLrtFX = avgSquareHistLrtFX * numHistLrt - + avgHistLrtFX * avgHistLrtComplFX; thresFluctLrtFX = THRES_FLUCT_LRT * numHistLrt; // get threshold for LRT feature: tmpU32 = (FACTOR_1_LRT_DIFF * (uint32_t)avgHistLrtFX); @@ -1057,7 +1056,7 @@ } else { //if at least one frequency component is zero, treat separately tmpU32 = WEBRTC_SPL_UMUL_32_16(inst->featureSpecFlat, SPECT_FLAT_TAVG_Q14); // Q24 - inst->featureSpecFlat -= WEBRTC_SPL_RSHIFT_U32(tmpU32, 14); // Q10 + inst->featureSpecFlat -= tmpU32 >> 14; // Q10 return; } } @@ -1070,22 +1069,20 @@ logCurSpectralFlatness = (int32_t)avgSpectralFlatnessNum; logCurSpectralFlatness += ((int32_t)(inst->stages - 1) << (inst->stages + 7)); // Q(8+stages-1) logCurSpectralFlatness -= (tmp32 << (inst->stages - 1)); - logCurSpectralFlatness = WEBRTC_SPL_LSHIFT_W32(logCurSpectralFlatness, 10 - inst->stages); // Q17 + logCurSpectralFlatness <<= (10 - inst->stages); // Q17 tmp32 = (int32_t)(0x00020000 | (WEBRTC_SPL_ABS_W32(logCurSpectralFlatness) & 0x0001FFFF)); //Q17 - intPart = -(int16_t)WEBRTC_SPL_RSHIFT_W32(logCurSpectralFlatness, 17); - intPart += 7; // Shift 7 to get the output in Q10 (from Q17 = -17+10) + intPart = 7 - (logCurSpectralFlatness >> 17); // Add 7 for output in Q10. if (intPart > 0) { - currentSpectralFlatness = WEBRTC_SPL_RSHIFT_W32(tmp32, intPart); + currentSpectralFlatness = tmp32 >> intPart; } else { - currentSpectralFlatness = WEBRTC_SPL_LSHIFT_W32(tmp32, -intPart); + currentSpectralFlatness = tmp32 << -intPart; } //time average update of spectral flatness feature tmp32 = currentSpectralFlatness - (int32_t)inst->featureSpecFlat; // Q10 - tmp32 = WEBRTC_SPL_MUL_32_16(SPECT_FLAT_TAVG_Q14, tmp32); // Q24 - inst->featureSpecFlat = (uint32_t)((int32_t)inst->featureSpecFlat - + WEBRTC_SPL_RSHIFT_W32(tmp32, 14)); // Q10 + tmp32 *= SPECT_FLAT_TAVG_Q14; // Q24 + inst->featureSpecFlat += tmp32 >> 14; // Q10 // done with flatness feature } @@ -1120,8 +1117,8 @@ minPause = WEBRTC_SPL_MIN(minPause, inst->avgMagnPause[i]); } // normalize by replacing div of "inst->magnLen" with "inst->stages-1" shifts - avgPauseFX = WEBRTC_SPL_RSHIFT_W32(avgPauseFX, inst->stages - 1); - avgMagnFX = (int32_t)WEBRTC_SPL_RSHIFT_U32(inst->sumMagn, inst->stages - 1); + avgPauseFX >>= inst->stages - 1; + avgMagnFX = inst->sumMagn >> (inst->stages - 1); // Largest possible deviation in magnPause for (co)var calculations tmp32no1 = WEBRTC_SPL_MAX(maxPause - avgPauseFX, avgPauseFX - minPause); // Get number of shifts to make sure we don't get wrap around in varPause @@ -1135,23 +1132,23 @@ tmp16no1 = (int16_t)((int32_t)magnIn[i] - avgMagnFX); tmp32no2 = inst->avgMagnPause[i] - avgPauseFX; varMagnUFX += (uint32_t)WEBRTC_SPL_MUL_16_16(tmp16no1, tmp16no1); // Q(2*qMagn) - tmp32no1 = WEBRTC_SPL_MUL_32_16(tmp32no2, tmp16no1); // Q(prevQMagn+qMagn) + tmp32no1 = tmp32no2 * tmp16no1; // Q(prevQMagn+qMagn) covMagnPauseFX += tmp32no1; // Q(prevQMagn+qMagn) - tmp32no1 = WEBRTC_SPL_RSHIFT_W32(tmp32no2, nShifts); // Q(prevQMagn-minPause) - varPauseUFX += (uint32_t)WEBRTC_SPL_MUL(tmp32no1, tmp32no1); // Q(2*(prevQMagn-minPause)) + tmp32no1 = tmp32no2 >> nShifts; // Q(prevQMagn-minPause). + varPauseUFX += tmp32no1 * tmp32no1; // Q(2*(prevQMagn-minPause)) } //update of average magnitude spectrum: Q(-2*stages) and averaging replaced by shifts - inst->curAvgMagnEnergy += WEBRTC_SPL_RSHIFT_U32(inst->magnEnergy, 2 * inst->normData - + inst->stages - 1); + inst->curAvgMagnEnergy += + inst->magnEnergy >> (2 * inst->normData + inst->stages - 1); avgDiffNormMagnUFX = varMagnUFX; // Q(2*qMagn) if ((varPauseUFX) && (covMagnPauseFX)) { tmpU32no1 = (uint32_t)WEBRTC_SPL_ABS_W32(covMagnPauseFX); // Q(prevQMagn+qMagn) norm32 = WebRtcSpl_NormU32(tmpU32no1) - 16; if (norm32 > 0) { - tmpU32no1 = WEBRTC_SPL_LSHIFT_U32(tmpU32no1, norm32); // Q(prevQMagn+qMagn+norm32) + tmpU32no1 <<= norm32; // Q(prevQMagn+qMagn+norm32) } else { - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, -norm32); // Q(prevQMagn+qMagn+norm32) + tmpU32no1 >>= -norm32; // Q(prevQMagn+qMagn+norm32) } tmpU32no2 = WEBRTC_SPL_UMUL(tmpU32no1, tmpU32no1); // Q(2*(prevQMagn+qMagn-norm32)) @@ -1163,8 +1160,8 @@ } if (varPauseUFX > 0) { // Q(2*(qMagn+norm32-16+minPause)) - tmpU32no1 = WEBRTC_SPL_UDIV(tmpU32no2, varPauseUFX); - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, nShifts); + tmpU32no1 = tmpU32no2 / varPauseUFX; + tmpU32no1 >>= nShifts; // Q(2*qMagn) avgDiffNormMagnUFX -= WEBRTC_SPL_MIN(avgDiffNormMagnUFX, tmpU32no1); @@ -1173,22 +1170,22 @@ } } //normalize and compute time average update of difference feature - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(avgDiffNormMagnUFX, 2 * inst->normData); + tmpU32no1 = avgDiffNormMagnUFX >> (2 * inst->normData); if (inst->featureSpecDiff > tmpU32no1) { tmpU32no2 = WEBRTC_SPL_UMUL_32_16(inst->featureSpecDiff - tmpU32no1, SPECT_DIFF_TAVG_Q8); // Q(8-2*stages) - inst->featureSpecDiff -= WEBRTC_SPL_RSHIFT_U32(tmpU32no2, 8); // Q(-2*stages) + inst->featureSpecDiff -= tmpU32no2 >> 8; // Q(-2*stages) } else { tmpU32no2 = WEBRTC_SPL_UMUL_32_16(tmpU32no1 - inst->featureSpecDiff, SPECT_DIFF_TAVG_Q8); // Q(8-2*stages) - inst->featureSpecDiff += WEBRTC_SPL_RSHIFT_U32(tmpU32no2, 8); // Q(-2*stages) + inst->featureSpecDiff += tmpU32no2 >> 8; // Q(-2*stages) } } // Transform input (speechFrame) to frequency domain magnitude (magnU16) void WebRtcNsx_DataAnalysis(NsxInst_t* inst, short* speechFrame, uint16_t* magnU16) { - uint32_t tmpU32no1, tmpU32no2; + uint32_t tmpU32no1; int32_t tmp_1_w32 = 0; int32_t tmp_2_w32 = 0; @@ -1281,21 +1278,13 @@ // // Switch initMagnEst to Q(minNorm-stages) - inst->initMagnEst[0] = WEBRTC_SPL_RSHIFT_U32(inst->initMagnEst[0], - right_shifts_in_initMagnEst); - inst->initMagnEst[inst->anaLen2] = - WEBRTC_SPL_RSHIFT_U32(inst->initMagnEst[inst->anaLen2], - right_shifts_in_initMagnEst); // Q(minNorm-stages) - - // Shift magnU16 to same domain as initMagnEst - tmpU32no1 = WEBRTC_SPL_RSHIFT_W32((uint32_t)magnU16[0], - right_shifts_in_magnU16); // Q(minNorm-stages) - tmpU32no2 = WEBRTC_SPL_RSHIFT_W32((uint32_t)magnU16[inst->anaLen2], - right_shifts_in_magnU16); // Q(minNorm-stages) - - // Update initMagnEst - inst->initMagnEst[0] += tmpU32no1; // Q(minNorm-stages) - inst->initMagnEst[inst->anaLen2] += tmpU32no2; // Q(minNorm-stages) + inst->initMagnEst[0] >>= right_shifts_in_initMagnEst; + inst->initMagnEst[inst->anaLen2] >>= right_shifts_in_initMagnEst; + + // Update initMagnEst with magnU16 in Q(minNorm-stages). + inst->initMagnEst[0] += magnU16[0] >> right_shifts_in_magnU16; + inst->initMagnEst[inst->anaLen2] += + magnU16[inst->anaLen2] >> right_shifts_in_magnU16; log2 = 0; if (magnU16[inst->anaLen2]) { @@ -1325,14 +1314,10 @@ inst->sumMagn += (uint32_t)magnU16[i]; // Q(normData-stages) // Switch initMagnEst to Q(minNorm-stages) - inst->initMagnEst[i] = WEBRTC_SPL_RSHIFT_U32(inst->initMagnEst[i], - right_shifts_in_initMagnEst); + inst->initMagnEst[i] >>= right_shifts_in_initMagnEst; - // Shift magnU16 to same domain as initMagnEst, i.e., Q(minNorm-stages) - tmpU32no1 = WEBRTC_SPL_RSHIFT_W32((uint32_t)magnU16[i], - right_shifts_in_magnU16); - // Update initMagnEst - inst->initMagnEst[i] += tmpU32no1; // Q(minNorm-stages) + // Update initMagnEst with magnU16 in Q(minNorm-stages). + inst->initMagnEst[i] += magnU16[i] >> right_shifts_in_magnU16; if (i >= kStartBand) { // For pink noise estimation. Collect data neglecting lower frequency band @@ -1359,16 +1344,15 @@ // Estimate White noise // Switch whiteNoiseLevel to Q(minNorm-stages) - inst->whiteNoiseLevel = WEBRTC_SPL_RSHIFT_U32(inst->whiteNoiseLevel, - right_shifts_in_initMagnEst); + inst->whiteNoiseLevel >>= right_shifts_in_initMagnEst; // Update the average magnitude spectrum, used as noise estimate. tmpU32no1 = WEBRTC_SPL_UMUL_32_16(inst->sumMagn, inst->overdrive); - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, inst->stages + 8); + tmpU32no1 >>= inst->stages + 8; // Replacing division above with 'stages' shifts // Shift to same Q-domain as whiteNoiseLevel - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, right_shifts_in_magnU16); + tmpU32no1 >>= right_shifts_in_magnU16; // This operation is safe from wrap around as long as END_STARTUP_SHORT < 128 assert(END_STARTUP_SHORT < 128); inst->whiteNoiseLevel += tmpU32no1; // Q(minNorm-stages) @@ -1386,7 +1370,7 @@ tmp_1_w32 = (int32_t)matrix_determinant; tmp_1_w32 += WEBRTC_SPL_MUL_16_16_RSFT(kSumLogIndex[65], sum_log_i, 9); tmp_1_w32 -= WEBRTC_SPL_MUL_16_16_RSFT(kSumLogIndex[65], kSumLogIndex[65], 10); - tmp_1_w32 -= WEBRTC_SPL_LSHIFT_W32((int32_t)sum_log_i_square, 4); + tmp_1_w32 -= (int32_t)sum_log_i_square << 4; tmp_1_w32 -= WEBRTC_SPL_MUL_16_16_RSFT((int16_t) (inst->magnLen - kStartBand), kSumSquareLogIndex[65], 2); matrix_determinant = (int16_t)tmp_1_w32; @@ -1399,24 +1383,24 @@ if (zeros < 0) { zeros = 0; } - tmp_1_w32 = WEBRTC_SPL_LSHIFT_W32(sum_log_magn, 1); // Q9 - sum_log_magn_u16 = (uint16_t)WEBRTC_SPL_RSHIFT_W32(tmp_1_w32, zeros);//Q(9-zeros) + tmp_1_w32 = sum_log_magn << 1; // Q9 + sum_log_magn_u16 = (uint16_t)(tmp_1_w32 >> zeros); // Q(9-zeros). // Calculate and update pinkNoiseNumerator. Result in Q11. tmp_2_w32 = WEBRTC_SPL_MUL_16_U16(sum_log_i_square, sum_log_magn_u16); // Q(11-zeros) - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32((uint32_t)sum_log_i_log_magn, 12); // Q5 + tmpU32no1 = sum_log_i_log_magn >> 12; // Q5 // Shift the largest value of sum_log_i and tmp32no3 before multiplication - tmp_u16 = WEBRTC_SPL_LSHIFT_U16((uint16_t)sum_log_i, 1); // Q6 + tmp_u16 = ((uint16_t)sum_log_i << 1); // Q6 if ((uint32_t)sum_log_i > tmpU32no1) { - tmp_u16 = WEBRTC_SPL_RSHIFT_U16(tmp_u16, zeros); + tmp_u16 >>= zeros; } else { - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, zeros); + tmpU32no1 >>= zeros; } tmp_2_w32 -= (int32_t)WEBRTC_SPL_UMUL_32_16(tmpU32no1, tmp_u16); // Q(11-zeros) - matrix_determinant = WEBRTC_SPL_RSHIFT_W16(matrix_determinant, zeros); // Q(-zeros) + matrix_determinant >>= zeros; // Q(-zeros) tmp_2_w32 = WebRtcSpl_DivW32W16(tmp_2_w32, matrix_determinant); // Q11 - tmp_2_w32 += WEBRTC_SPL_LSHIFT_W32((int32_t)net_norm, 11); // Q11 + tmp_2_w32 += (int32_t)net_norm << 11; // Q11 if (tmp_2_w32 < 0) { tmp_2_w32 = 0; } @@ -1424,9 +1408,8 @@ // Calculate and update pinkNoiseExp. Result in Q14. tmp_2_w32 = WEBRTC_SPL_MUL_16_U16(sum_log_i, sum_log_magn_u16); // Q(14-zeros) - tmp_1_w32 = WEBRTC_SPL_RSHIFT_W32(sum_log_i_log_magn, 3 + zeros); - tmp_1_w32 = WEBRTC_SPL_MUL((int32_t)(inst->magnLen - kStartBand), - tmp_1_w32); + tmp_1_w32 = sum_log_i_log_magn >> (3 + zeros); + tmp_1_w32 *= inst->magnLen - kStartBand; tmp_2_w32 -= tmp_1_w32; // Q(14-zeros) if (tmp_2_w32 > 0) { // If the exponential parameter is negative force it to zero, which means a @@ -1488,13 +1471,14 @@ energyOut = WEBRTC_SPL_SHIFT_W32(energyOut, 8 + scaleEnergyOut - inst->scaleEnergyIn); } else { - inst->energyIn = WEBRTC_SPL_RSHIFT_W32(inst->energyIn, 8 + scaleEnergyOut - - inst->scaleEnergyIn); // Q(-8-scaleEnergyOut) + // |energyIn| is currently in Q(|scaleEnergyIn|), but to later on end up + // with an |energyRatio| in Q8 we need to change the Q-domain to + // Q(-8-scaleEnergyOut). + inst->energyIn >>= 8 + scaleEnergyOut - inst->scaleEnergyIn; } assert(inst->energyIn > 0); - energyRatio = (int16_t)WEBRTC_SPL_DIV(energyOut - + WEBRTC_SPL_RSHIFT_W32(inst->energyIn, 1), inst->energyIn); // Q8 + energyRatio = (energyOut + inst->energyIn / 2) / inst->energyIn; // Q8 // Limit the ratio to [0, 1] in Q8, i.e., [0, 256] energyRatio = WEBRTC_SPL_SAT(256, energyRatio, 0); @@ -1616,7 +1600,7 @@ //noise estimate from previous frame for (i = 0; i < inst->magnLen; i++) { - prevNoiseU16[i] = (uint16_t)WEBRTC_SPL_RSHIFT_U32(inst->prevNoiseU32[i], 11); // Q(prevQNoise) + prevNoiseU16[i] = (uint16_t)(inst->prevNoiseU32[i] >> 11); // Q(prevQNoise) } if (inst->blockIndex < END_STARTUP_SHORT) { @@ -1661,7 +1645,7 @@ // numerator = (initMagnEst - noise_estimate * overdrive) // Result in Q(8+minNorm-stages) tmpU32no1 = WEBRTC_SPL_UMUL_32_16(noise_estimate, inst->overdrive); - numerator = WEBRTC_SPL_LSHIFT_U32(inst->initMagnEst[i], 8); + numerator = inst->initMagnEst[i] << 8; if (numerator > tmpU32no1) { // Suppression filter coefficient larger than zero, so calculate. numerator -= tmpU32no1; @@ -1672,16 +1656,16 @@ nShifts = WEBRTC_SPL_SAT(6, nShifts, 0); // Shift numerator to Q(nShifts+8+minNorm-stages) - numerator = WEBRTC_SPL_LSHIFT_U32(numerator, nShifts); + numerator <<= nShifts; // Shift denominator to Q(nShifts-6+minNorm-stages) - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(inst->initMagnEst[i], 6 - nShifts); + tmpU32no1 = inst->initMagnEst[i] >> (6 - nShifts); if (tmpU32no1 == 0) { // This is only possible if numerator = 0, in which case // we don't need any division. tmpU32no1 = 1; } - tmpU32no2 = WEBRTC_SPL_UDIV(numerator, tmpU32no1); // Q14 + tmpU32no2 = numerator / tmpU32no1; // Q14 noiseSupFilterTmp[i] = (uint16_t)WEBRTC_SPL_SAT(16384, tmpU32no2, (uint32_t)(inst->denoiseBound)); // Q14 } @@ -1694,16 +1678,16 @@ // may not. // Shift 'noiseU32' to 'q_domain_to_use' - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(noiseU32[i], (int)qNoise - q_domain_to_use); + tmpU32no1 = noiseU32[i] >> (qNoise - q_domain_to_use); // Shift 'noise_estimate_avg' to 'q_domain_to_use' - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(noise_estimate_avg, inst->minNorm - inst->stages - - q_domain_to_use); + tmpU32no2 = noise_estimate_avg >> + (inst->minNorm - inst->stages - q_domain_to_use); // Make a simple check to see if we have enough room for weighting 'tmpU32no1' // without wrap around nShifts = 0; if (tmpU32no1 & 0xfc000000) { - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, 6); - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(tmpU32no2, 6); + tmpU32no1 >>= 6; + tmpU32no2 >>= 6; nShifts = 6; } tmpU32no1 *= inst->blockIndex; @@ -1711,7 +1695,7 @@ // Add them together and divide by startup length noiseU32[i] = WebRtcSpl_DivU32U16(tmpU32no1 + tmpU32no2, END_STARTUP_SHORT); // Shift back if necessary - noiseU32[i] = WEBRTC_SPL_LSHIFT_U32(noiseU32[i], nShifts); + noiseU32[i] <<= nShifts; } // Update new Q-domain for 'noiseU32' qNoise = q_domain_to_use; @@ -1720,9 +1704,8 @@ // used to normalize spectral difference measure if (inst->blockIndex < END_STARTUP_LONG) { // substituting division with shift ending up in Q(-2*stages) - inst->timeAvgMagnEnergyTmp - += WEBRTC_SPL_RSHIFT_U32(inst->magnEnergy, - 2 * inst->normData + inst->stages - 1); + inst->timeAvgMagnEnergyTmp += + inst->magnEnergy >> (2 * inst->normData + inst->stages - 1); inst->timeAvgMagnEnergy = WebRtcSpl_DivU32U16(inst->timeAvgMagnEnergyTmp, inst->blockIndex + 1); } @@ -1754,17 +1737,17 @@ // calculate post SNR: output in Q11 postLocSnr[i] = 2048; // 1.0 in Q11 - tmpU32no1 = WEBRTC_SPL_LSHIFT_U32((uint32_t)magnU16[i], 6); // Q(6+qMagn) + tmpU32no1 = (uint32_t)magnU16[i] << 6; // Q(6+qMagn) if (postShifts < 0) { - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(noiseU32[i], -postShifts); // Q(6+qMagn) + tmpU32no2 = noiseU32[i] >> -postShifts; // Q(6+qMagn) } else { - tmpU32no2 = WEBRTC_SPL_LSHIFT_U32(noiseU32[i], postShifts); // Q(6+qMagn) + tmpU32no2 = noiseU32[i] << postShifts; // Q(6+qMagn) } if (tmpU32no1 > tmpU32no2) { // Current magnitude larger than noise - tmpU32no1 = WEBRTC_SPL_LSHIFT_U32(tmpU32no1, 11); // Q(17+qMagn) + tmpU32no1 <<= 11; // Q(17+qMagn) if (tmpU32no2 > 0) { - tmpU32no1 = WEBRTC_SPL_UDIV(tmpU32no1, tmpU32no2); // Q11 + tmpU32no1 /= tmpU32no2; // Q11 postLocSnr[i] = WEBRTC_SPL_MIN(satMax, tmpU32no1); // Q11 } else { postLocSnr[i] = satMax; @@ -1772,12 +1755,13 @@ } // calculate prevNearSnr[i] and save for later instead of recalculating it later - nearMagnEst = WEBRTC_SPL_UMUL_16_16(inst->prevMagnU16[i], inst->noiseSupFilter[i]); // Q(prevQMagn+14) - tmpU32no1 = WEBRTC_SPL_LSHIFT_U32(nearMagnEst, 3); // Q(prevQMagn+17) - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(inst->prevNoiseU32[i], nShifts); // Q(prevQMagn+6) + // |nearMagnEst| in Q(prevQMagn + 14) + nearMagnEst = inst->prevMagnU16[i] * inst->noiseSupFilter[i]; + tmpU32no1 = nearMagnEst << 3; // Q(prevQMagn+17) + tmpU32no2 = inst->prevNoiseU32[i] >> nShifts; // Q(prevQMagn+6) if (tmpU32no2 > 0) { - tmpU32no1 = WEBRTC_SPL_UDIV(tmpU32no1, tmpU32no2); // Q11 + tmpU32no1 /= tmpU32no2; // Q11 tmpU32no1 = WEBRTC_SPL_MIN(satMax, tmpU32no1); // Q11 } else { tmpU32no1 = satMax; // Q11 @@ -1789,7 +1773,7 @@ tmpU32no2 = WEBRTC_SPL_UMUL_32_16(postLocSnr[i] - 2048, ONE_MINUS_DD_PR_SNR_Q11); // Q22 priorSnr = tmpU32no1 + tmpU32no2 + 512; // Q22 (added 512 for rounding) // priorLocSnr = 1 + 2*priorSnr - priorLocSnr[i] = 2048 + WEBRTC_SPL_RSHIFT_U32(priorSnr, 10); // Q11 + priorLocSnr[i] = 2048 + (priorSnr >> 10); // Q11 } // end of loop over frequencies // done with step 1: DD computation of prior and post SNR @@ -1811,7 +1795,7 @@ // get normalization for spectral difference for next window estimate // Shift to Q(-2*stages) - inst->curAvgMagnEnergy = WEBRTC_SPL_RSHIFT_U32(inst->curAvgMagnEnergy, STAT_UPDATES); + inst->curAvgMagnEnergy >>= STAT_UPDATES; tmpU32no1 = (inst->curAvgMagnEnergy + inst->timeAvgMagnEnergy + 1) >> 1; //Q(-2*stages) // Update featureSpecDiff @@ -1829,12 +1813,12 @@ norm32no1++; } tmpU32no3 = WEBRTC_SPL_UMUL(tmpU32no3, tmpU32no2); - tmpU32no3 = WEBRTC_SPL_UDIV(tmpU32no3, inst->timeAvgMagnEnergy); + tmpU32no3 /= inst->timeAvgMagnEnergy; if (WebRtcSpl_NormU32(tmpU32no3) < norm32no1) { inst->featureSpecDiff = 0x007FFFFF; } else { inst->featureSpecDiff = WEBRTC_SPL_MIN(0x007FFFFF, - WEBRTC_SPL_LSHIFT_U32(tmpU32no3, norm32no1)); + tmpU32no3 << norm32no1); } } @@ -1857,9 +1841,9 @@ // noiseUpdate = noisePrev[i] + (1 - gammaNoise) * nonSpeechProb * (magn[i] - noisePrev[i]) if (postShifts < 0) { - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(magnU16[i], -postShifts); // Q(prevQNoise) + tmpU32no2 = magnU16[i] >> -postShifts; // Q(prevQNoise) } else { - tmpU32no2 = WEBRTC_SPL_LSHIFT_U32(magnU16[i], postShifts); // Q(prevQNoise) + tmpU32no2 = (uint32_t)magnU16[i] << postShifts; // Q(prevQNoise) } if (prevNoiseU16[i] > tmpU32no2) { sign = -1; @@ -1875,12 +1859,10 @@ tmpU32no3 = WEBRTC_SPL_UMUL_32_16(tmpU32no1, nonSpeechProbFinal[i]); // Q(prevQNoise+8) if (0x7c000000 & tmpU32no3) { // Shifting required before multiplication - tmpU32no2 - = WEBRTC_SPL_UMUL_32_16(WEBRTC_SPL_RSHIFT_U32(tmpU32no3, 5), gammaNoise); // Q(prevQNoise+11) + tmpU32no2 = (tmpU32no3 >> 5) * gammaNoise; // Q(prevQNoise+11) } else { // We can do shifting after multiplication - tmpU32no2 - = WEBRTC_SPL_RSHIFT_U32(WEBRTC_SPL_UMUL_32_16(tmpU32no3, gammaNoise), 5); // Q(prevQNoise+11) + tmpU32no2 = (tmpU32no3 * gammaNoise) >> 5; // Q(prevQNoise+11) } if (sign > 0) { noiseUpdateU32 += tmpU32no2; // Q(prevQNoise+11) @@ -1909,12 +1891,10 @@ if (0x7c000000 & tmpU32no3) { // Shifting required before multiplication - tmpU32no2 - = WEBRTC_SPL_UMUL_32_16(WEBRTC_SPL_RSHIFT_U32(tmpU32no3, 5), gammaNoise); // Q(prevQNoise+11) + tmpU32no2 = (tmpU32no3 >> 5) * gammaNoise; // Q(prevQNoise+11) } else { // We can do shifting after multiplication - tmpU32no2 - = WEBRTC_SPL_RSHIFT_U32(WEBRTC_SPL_UMUL_32_16(tmpU32no3, gammaNoise), 5); // Q(prevQNoise+11) + tmpU32no2 = (tmpU32no3 * gammaNoise) >> 5; // Q(prevQNoise+11) } if (sign > 0) { tmpU32no1 = inst->prevNoiseU32[i] + tmpU32no2; // Q(prevQNoise+11) @@ -1940,13 +1920,13 @@ if (nonSpeechProbFinal[i] > ONE_MINUS_PROB_RANGE_Q8) { if (nShifts < 0) { tmp32no1 = (int32_t)magnU16[i] - tmp32no2; // Q(qMagn) - tmp32no1 = WEBRTC_SPL_MUL_32_16(tmp32no1, ONE_MINUS_GAMMA_PAUSE_Q8); // Q(8+prevQMagn+nShifts) - tmp32no1 = WEBRTC_SPL_RSHIFT_W32(tmp32no1 + 128, 8); // Q(qMagn) + tmp32no1 *= ONE_MINUS_GAMMA_PAUSE_Q8; // Q(8+prevQMagn+nShifts) + tmp32no1 = (tmp32no1 + 128) >> 8; // Q(qMagn). } else { - tmp32no1 = WEBRTC_SPL_LSHIFT_W32((int32_t)magnU16[i], nShifts) - - inst->avgMagnPause[i]; // Q(qMagn+nShifts) - tmp32no1 = WEBRTC_SPL_MUL_32_16(tmp32no1, ONE_MINUS_GAMMA_PAUSE_Q8); // Q(8+prevQMagn+nShifts) - tmp32no1 = WEBRTC_SPL_RSHIFT_W32(tmp32no1 + (128 << nShifts), 8 + nShifts); // Q(qMagn) + // In Q(qMagn+nShifts) + tmp32no1 = ((int32_t)magnU16[i] << nShifts) - inst->avgMagnPause[i]; + tmp32no1 *= ONE_MINUS_GAMMA_PAUSE_Q8; // Q(8+prevQMagn+nShifts) + tmp32no1 = (tmp32no1 + (128 << nShifts)) >> (8 + nShifts); // Q(qMagn). } tmp32no2 += tmp32no1; // Q(qMagn) } @@ -1980,21 +1960,21 @@ if (nShifts < 0) { // This case is equivalent with magn < noise which implies curNearSnr = 0; tmpMagnU32 = (uint32_t)magnU16[i]; // Q(qMagn) - tmpNoiseU32 = WEBRTC_SPL_LSHIFT_U32(noiseU32[i], -nShifts); // Q(qMagn) + tmpNoiseU32 = noiseU32[i] << -nShifts; // Q(qMagn) } else if (nShifts > 17) { - tmpMagnU32 = WEBRTC_SPL_LSHIFT_U32(magnU16[i], 17); // Q(qMagn+17) - tmpNoiseU32 = WEBRTC_SPL_RSHIFT_U32(noiseU32[i], nShifts - 17); // Q(qMagn+17) + tmpMagnU32 = (uint32_t)magnU16[i] << 17; // Q(qMagn+17) + tmpNoiseU32 = noiseU32[i] >> (nShifts - 17); // Q(qMagn+17) } else { - tmpMagnU32 = WEBRTC_SPL_LSHIFT_U32((uint32_t)magnU16[i], nShifts); // Q(qNoise_prev+11) + tmpMagnU32 = (uint32_t)magnU16[i] << nShifts; // Q(qNoise_prev+11) tmpNoiseU32 = noiseU32[i]; // Q(qNoise_prev+11) } if (tmpMagnU32 > tmpNoiseU32) { tmpU32no1 = tmpMagnU32 - tmpNoiseU32; // Q(qCur) norm32no2 = WEBRTC_SPL_MIN(11, WebRtcSpl_NormU32(tmpU32no1)); - tmpU32no1 = WEBRTC_SPL_LSHIFT_U32(tmpU32no1, norm32no2); // Q(qCur+norm32no2) - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(tmpNoiseU32, 11 - norm32no2); // Q(qCur+norm32no2-11) + tmpU32no1 <<= norm32no2; // Q(qCur+norm32no2) + tmpU32no2 = tmpNoiseU32 >> (11 - norm32no2); // Q(qCur+norm32no2-11) if (tmpU32no2 > 0) { - tmpU32no1 = WEBRTC_SPL_UDIV(tmpU32no1, tmpU32no2); // Q11 + tmpU32no1 /= tmpU32no2; // Q11 } curNearSnr = WEBRTC_SPL_MIN(satMax, tmpU32no1); // Q11 } @@ -2008,20 +1988,17 @@ priorSnr = tmpU32no1 + tmpU32no2; // Q22 //gain filter - tmpU32no1 = (uint32_t)(inst->overdrive) - + WEBRTC_SPL_RSHIFT_U32(priorSnr + 8192, 14); // Q8 + tmpU32no1 = inst->overdrive + ((priorSnr + 8192) >> 14); // Q8 assert(inst->overdrive > 0); - tmpU16no1 = (uint16_t)WEBRTC_SPL_UDIV(priorSnr + (tmpU32no1 >> 1), tmpU32no1); // Q14 + tmpU16no1 = (priorSnr + tmpU32no1 / 2) / tmpU32no1; // Q14 inst->noiseSupFilter[i] = WEBRTC_SPL_SAT(16384, tmpU16no1, inst->denoiseBound); // 16384 = Q14(1.0) // Q14 // Weight in the parametric Wiener filter during startup if (inst->blockIndex < END_STARTUP_SHORT) { // Weight the two suppression filters - tmpU32no1 = WEBRTC_SPL_UMUL_16_16(inst->noiseSupFilter[i], - (uint16_t)inst->blockIndex); - tmpU32no2 = WEBRTC_SPL_UMUL_16_16(noiseSupFilterTmp[i], - (uint16_t)(END_STARTUP_SHORT - - inst->blockIndex)); + tmpU32no1 = inst->noiseSupFilter[i] * inst->blockIndex; + tmpU32no2 = noiseSupFilterTmp[i] * + (END_STARTUP_SHORT - inst->blockIndex); tmpU32no1 += tmpU32no2; inst->noiseSupFilter[i] = (uint16_t)WebRtcSpl_DivU32U16(tmpU32no1, END_STARTUP_SHORT); @@ -2034,12 +2011,12 @@ inst->prevQMagn = qMagn; if (norm32no1 > 5) { for (i = 0; i < inst->magnLen; i++) { - inst->prevNoiseU32[i] = WEBRTC_SPL_LSHIFT_U32(noiseU32[i], norm32no1 - 5); // Q(qNoise+11) + inst->prevNoiseU32[i] = noiseU32[i] << (norm32no1 - 5); // Q(qNoise+11) inst->prevMagnU16[i] = magnU16[i]; // Q(qMagn) } } else { for (i = 0; i < inst->magnLen; i++) { - inst->prevNoiseU32[i] = WEBRTC_SPL_RSHIFT_U32(noiseU32[i], 5 - norm32no1); // Q(qNoise+11) + inst->prevNoiseU32[i] = noiseU32[i] >> (5 - norm32no1); // Q(qNoise+11) inst->prevMagnU16[i] = magnU16[i]; // Q(qMagn) } } @@ -2071,10 +2048,9 @@ tmpU16no1 += nonSpeechProbFinal[i]; // Q8 tmpU32no1 += (uint32_t)(inst->noiseSupFilter[i]); // Q14 } - avgProbSpeechHB = (int16_t)(4096 - - WEBRTC_SPL_RSHIFT_U16(tmpU16no1, inst->stages - 7)); // Q12 - avgFilterGainHB = (int16_t)WEBRTC_SPL_RSHIFT_U32( - tmpU32no1, inst->stages - 3); // Q14 + assert(inst->stages >= 7); + avgProbSpeechHB = (4096 - (tmpU16no1 >> (inst->stages - 7))); // Q12 + avgFilterGainHB = (int16_t)(tmpU32no1 >> (inst->stages - 3)); // Q14 // // original FLOAT code // // gain based on speech probability: diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core_c.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core_c.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core_c.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core_c.c 2015-02-03 14:33:35.000000000 +0000 @@ -8,8 +8,11 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include + #include "webrtc/modules/audio_processing/ns/include/noise_suppression_x.h" #include "webrtc/modules/audio_processing/ns/nsx_core.h" +#include "webrtc/modules/audio_processing/ns/nsx_defines.h" static const int16_t kIndicatorTable[17] = { 0, 2017, 3809, 5227, 6258, 6963, 7424, 7718, @@ -39,16 +42,16 @@ for (i = 0; i < inst->magnLen; i++) { besselTmpFX32 = (int32_t)postLocSnr[i]; // Q11 normTmp = WebRtcSpl_NormU32(postLocSnr[i]); - num = WEBRTC_SPL_LSHIFT_U32(postLocSnr[i], normTmp); // Q(11+normTmp) + num = postLocSnr[i] << normTmp; // Q(11+normTmp) if (normTmp > 10) { - den = WEBRTC_SPL_LSHIFT_U32(priorLocSnr[i], normTmp - 11); // Q(normTmp) + den = priorLocSnr[i] << (normTmp - 11); // Q(normTmp) } else { - den = WEBRTC_SPL_RSHIFT_U32(priorLocSnr[i], 11 - normTmp); // Q(normTmp) + den = priorLocSnr[i] >> (11 - normTmp); // Q(normTmp) } if (den > 0) { - besselTmpFX32 -= WEBRTC_SPL_UDIV(num, den); // Q11 + besselTmpFX32 -= num / den; // Q11 } else { - besselTmpFX32 -= num; // Q11 + besselTmpFX32 = 0; } // inst->logLrtTimeAvg[i] += LRT_TAVG * (besselTmp - log(snrLocPrior) @@ -56,23 +59,21 @@ // Here, LRT_TAVG = 0.5 zeros = WebRtcSpl_NormU32(priorLocSnr[i]); frac32 = (int32_t)(((priorLocSnr[i] << zeros) & 0x7FFFFFFF) >> 19); - tmp32 = WEBRTC_SPL_MUL(frac32, frac32); - tmp32 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(tmp32, -43), 19); + tmp32 = (frac32 * frac32 * -43) >> 19; tmp32 += WEBRTC_SPL_MUL_16_16_RSFT((int16_t)frac32, 5412, 12); frac32 = tmp32 + 37; // tmp32 = log2(priorLocSnr[i]) tmp32 = (int32_t)(((31 - zeros) << 12) + frac32) - (11 << 12); // Q12 - logTmp = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_32_16(tmp32, 178), 8); - // log2(priorLocSnr[i])*log(2) - tmp32no1 = WEBRTC_SPL_RSHIFT_W32(logTmp + inst->logLrtTimeAvgW32[i], 1); - // Q12 + logTmp = (tmp32 * 178) >> 8; // log2(priorLocSnr[i])*log(2) + // tmp32no1 = LRT_TAVG * (log(snrLocPrior) + inst->logLrtTimeAvg[i]) in Q12. + tmp32no1 = (logTmp + inst->logLrtTimeAvgW32[i]) / 2; inst->logLrtTimeAvgW32[i] += (besselTmpFX32 - tmp32no1); // Q12 logLrtTimeAvgKsumFX += inst->logLrtTimeAvgW32[i]; // Q12 } - inst->featureLogLrt = WEBRTC_SPL_RSHIFT_W32(logLrtTimeAvgKsumFX * 5, - inst->stages + 10); - // 5 = BIN_SIZE_LRT / 2 + inst->featureLogLrt = (logLrtTimeAvgKsumFX * BIN_SIZE_LRT) >> + (inst->stages + 11); + // done with computation of LR factor // @@ -95,7 +96,7 @@ } tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1, nShifts); // Q14 // compute indicator function: sigmoid map - tableIndex = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32no1, 14); + tableIndex = (int16_t)(tmp32no1 >> 14); if ((tableIndex < 16) && (tableIndex >= 0)) { tmp16no2 = kIndicatorTable[tableIndex]; tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex]; @@ -122,16 +123,12 @@ //widthPrior = widthPrior * 2.0; nShifts++; } - tmp32no1 = (int32_t)WebRtcSpl_DivU32U16(WEBRTC_SPL_LSHIFT_U32(tmpU32no2, - nShifts), 25); - //Q14 - tmpU32no1 = WebRtcSpl_DivU32U16(WEBRTC_SPL_LSHIFT_U32(tmpU32no2, nShifts), - 25); //Q14 + tmpU32no1 = WebRtcSpl_DivU32U16(tmpU32no2 << nShifts, 25); // Q14 // compute indicator function: sigmoid map // FLOAT code // indicator1 = 0.5 * (tanh(sgnMap * widthPrior * // (threshPrior1 - tmpFloat1)) + 1.0); - tableIndex = (int16_t)WEBRTC_SPL_RSHIFT_U32(tmpU32no1, 14); + tableIndex = (int16_t)(tmpU32no1 >> 14); if (tableIndex < 16) { tmp16no2 = kIndicatorTable[tableIndex]; tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex]; @@ -152,20 +149,17 @@ if (inst->featureSpecDiff) { normTmp = WEBRTC_SPL_MIN(20 - inst->stages, WebRtcSpl_NormU32(inst->featureSpecDiff)); - tmpU32no1 = WEBRTC_SPL_LSHIFT_U32(inst->featureSpecDiff, normTmp); - // Q(normTmp-2*stages) - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(inst->timeAvgMagnEnergy, - 20 - inst->stages - normTmp); + assert(normTmp >= 0); + tmpU32no1 = inst->featureSpecDiff << normTmp; // Q(normTmp-2*stages) + tmpU32no2 = inst->timeAvgMagnEnergy >> (20 - inst->stages - normTmp); if (tmpU32no2 > 0) { // Q(20 - inst->stages) - tmpU32no1 = WEBRTC_SPL_UDIV(tmpU32no1, tmpU32no2); + tmpU32no1 /= tmpU32no2; } else { tmpU32no1 = (uint32_t)(0x7fffffff); } } - tmpU32no3 = WEBRTC_SPL_UDIV(WEBRTC_SPL_LSHIFT_U32(inst->thresholdSpecDiff, - 17), - 25); + tmpU32no3 = (inst->thresholdSpecDiff << 17) / 25; tmpU32no2 = tmpU32no1 - tmpU32no3; nShifts = 1; tmpIndFX = 16384; // Q14(1.0) @@ -176,12 +170,12 @@ //widthPrior = widthPrior * 2.0; nShifts--; } - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no2, nShifts); + tmpU32no1 = tmpU32no2 >> nShifts; // compute indicator function: sigmoid map /* FLOAT code indicator2 = 0.5 * (tanh(widthPrior * (tmpFloat1 - threshPrior2)) + 1.0); */ - tableIndex = (int16_t)WEBRTC_SPL_RSHIFT_U32(tmpU32no1, 14); + tableIndex = (int16_t)(tmpU32no1 >> 14); if (tableIndex < 16) { tmp16no2 = kIndicatorTable[tableIndex]; tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex]; @@ -227,44 +221,39 @@ // nonSpeechProbFinal[i] = inst->priorNonSpeechProb / // (inst->priorNonSpeechProb + invLrt); if (inst->logLrtTimeAvgW32[i] < 65300) { - tmp32no1 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL( - inst->logLrtTimeAvgW32[i], 23637), - 14); // Q12 - intPart = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32no1, 12); + tmp32no1 = (inst->logLrtTimeAvgW32[i] * 23637) >> 14; // Q12 + intPart = (int16_t)(tmp32no1 >> 12); if (intPart < -8) { intPart = -8; } frac = (int16_t)(tmp32no1 & 0x00000fff); // Q12 // Quadratic approximation of 2^frac - tmp32no2 = WEBRTC_SPL_RSHIFT_W32(frac * frac * 44, 19); // Q12 + tmp32no2 = (frac * frac * 44) >> 19; // Q12. tmp32no2 += WEBRTC_SPL_MUL_16_16_RSFT(frac, 84, 7); // Q12 - invLrtFX = WEBRTC_SPL_LSHIFT_W32(1, 8 + intPart) - + WEBRTC_SPL_SHIFT_W32(tmp32no2, intPart - 4); // Q8 + invLrtFX = (1 << (8 + intPart)) + + WEBRTC_SPL_SHIFT_W32(tmp32no2, intPart - 4); // Q8 normTmp = WebRtcSpl_NormW32(invLrtFX); normTmp2 = WebRtcSpl_NormW16((16384 - inst->priorNonSpeechProb)); if (normTmp + normTmp2 >= 7) { if (normTmp + normTmp2 < 15) { - invLrtFX = WEBRTC_SPL_RSHIFT_W32(invLrtFX, 15 - normTmp2 - normTmp); + invLrtFX >>= 15 - normTmp2 - normTmp; // Q(normTmp+normTmp2-7) - tmp32no1 = WEBRTC_SPL_MUL_32_16(invLrtFX, - (16384 - inst->priorNonSpeechProb)); + tmp32no1 = invLrtFX * (16384 - inst->priorNonSpeechProb); // Q(normTmp+normTmp2+7) invLrtFX = WEBRTC_SPL_SHIFT_W32(tmp32no1, 7 - normTmp - normTmp2); // Q14 } else { - tmp32no1 = WEBRTC_SPL_MUL_32_16(invLrtFX, - (16384 - inst->priorNonSpeechProb)); + tmp32no1 = invLrtFX * (16384 - inst->priorNonSpeechProb); // Q22 - invLrtFX = WEBRTC_SPL_RSHIFT_W32(tmp32no1, 8); // Q14 + invLrtFX = tmp32no1 >> 8; // Q14. } - tmp32no1 = WEBRTC_SPL_LSHIFT_W32((int32_t)inst->priorNonSpeechProb, - 8); // Q22 + tmp32no1 = (int32_t)inst->priorNonSpeechProb << 8; // Q22 - nonSpeechProbFinal[i] = (uint16_t)WEBRTC_SPL_DIV(tmp32no1, - (int32_t)inst->priorNonSpeechProb + invLrtFX); // Q8 + nonSpeechProbFinal[i] = tmp32no1 / + (inst->priorNonSpeechProb + invLrtFX); // Q8 } } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core_mips.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core_mips.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core_mips.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core_mips.c 2015-02-03 14:33:35.000000000 +0000 @@ -8,6 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include + #include "webrtc/modules/audio_processing/ns/include/noise_suppression_x.h" #include "webrtc/modules/audio_processing/ns/nsx_core.h" @@ -74,6 +76,7 @@ "sra %[r7], %[r7], 19 \n\t" "movz %[r3], %[r8], %[r6] \n\t" "subu %[r0], %[r0], %[r3] \n\t" + "movn %[r0], $0, %[r6] \n\t" "mul %[r1], %[r1], %[const_5412] \n\t" "sra %[r1], %[r1], 12 \n\t" "addu %[r7], %[r7], %[r1] \n\t" @@ -102,9 +105,9 @@ logLrtTimeAvgKsumFX += r2; } - inst->featureLogLrt = WEBRTC_SPL_RSHIFT_W32(logLrtTimeAvgKsumFX * 5, - inst->stages + 10); - // 5 = BIN_SIZE_LRT / 2 + inst->featureLogLrt = (logLrtTimeAvgKsumFX * BIN_SIZE_LRT) >> + (inst->stages + 11); + // done with computation of LR factor // @@ -127,7 +130,7 @@ } tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1, nShifts); // Q14 // compute indicator function: sigmoid map - tableIndex = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32no1, 14); + tableIndex = (int16_t)(tmp32no1 >> 14); if ((tableIndex < 16) && (tableIndex >= 0)) { tmp16no2 = kIndicatorTable[tableIndex]; tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex]; @@ -154,16 +157,12 @@ //widthPrior = widthPrior * 2.0; nShifts++; } - tmp32no1 = (int32_t)WebRtcSpl_DivU32U16(WEBRTC_SPL_LSHIFT_U32(tmpU32no2, - nShifts), 25); - //Q14 - tmpU32no1 = WebRtcSpl_DivU32U16(WEBRTC_SPL_LSHIFT_U32(tmpU32no2, nShifts), - 25); //Q14 + tmpU32no1 = WebRtcSpl_DivU32U16(tmpU32no2 << nShifts, 25); //Q14 // compute indicator function: sigmoid map // FLOAT code // indicator1 = 0.5 * (tanh(sgnMap * widthPrior * // (threshPrior1 - tmpFloat1)) + 1.0); - tableIndex = (int16_t)WEBRTC_SPL_RSHIFT_U32(tmpU32no1, 14); + tableIndex = (int16_t)(tmpU32no1 >> 14); if (tableIndex < 16) { tmp16no2 = kIndicatorTable[tableIndex]; tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex]; @@ -184,20 +183,17 @@ if (inst->featureSpecDiff) { normTmp = WEBRTC_SPL_MIN(20 - inst->stages, WebRtcSpl_NormU32(inst->featureSpecDiff)); - tmpU32no1 = WEBRTC_SPL_LSHIFT_U32(inst->featureSpecDiff, normTmp); - // Q(normTmp-2*stages) - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(inst->timeAvgMagnEnergy, - 20 - inst->stages - normTmp); + assert(normTmp >= 0); + tmpU32no1 = inst->featureSpecDiff << normTmp; // Q(normTmp-2*stages) + tmpU32no2 = inst->timeAvgMagnEnergy >> (20 - inst->stages - normTmp); if (tmpU32no2 > 0) { // Q(20 - inst->stages) - tmpU32no1 = WEBRTC_SPL_UDIV(tmpU32no1, tmpU32no2); + tmpU32no1 /= tmpU32no2; } else { tmpU32no1 = (uint32_t)(0x7fffffff); } } - tmpU32no3 = WEBRTC_SPL_UDIV(WEBRTC_SPL_LSHIFT_U32(inst->thresholdSpecDiff, - 17), - 25); + tmpU32no3 = (inst->thresholdSpecDiff << 17) / 25; tmpU32no2 = tmpU32no1 - tmpU32no3; nShifts = 1; tmpIndFX = 16384; // Q14(1.0) @@ -208,12 +204,12 @@ //widthPrior = widthPrior * 2.0; nShifts--; } - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no2, nShifts); + tmpU32no1 = tmpU32no2 >> nShifts; // compute indicator function: sigmoid map /* FLOAT code indicator2 = 0.5 * (tanh(widthPrior * (tmpFloat1 - threshPrior2)) + 1.0); */ - tableIndex = (int16_t)WEBRTC_SPL_RSHIFT_U32(tmpU32no1, 14); + tableIndex = (int16_t)(tmpU32no1 >> 14); if (tableIndex < 16) { tmp16no2 = kIndicatorTable[tableIndex]; tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex]; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core_neon.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core_neon.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core_neon.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core_neon.c 2015-02-03 14:33:35.000000000 +0000 @@ -89,7 +89,7 @@ int32x4_t v32x4A = vandq_s32(v32x4B, constA32x4); v32x4A = vorrq_s32(v32x4A, constB32x4); - // tmp16 = (int16_t) WEBRTC_SPL_RSHIFT_W32(tmp32no2, 21); + // tmp16 = (int16_t)(tmp32no2 >> 21); v32x4B = vshrq_n_s32(v32x4B, 21); // tmp16 -= 21;// shift 21 to get result in Q0 @@ -100,9 +100,9 @@ v32x4B = vaddq_s32(v32x4B, qNoise32x4); // if (tmp16 < 0) { - // tmp32no1 = WEBRTC_SPL_RSHIFT_W32(tmp32no1, -tmp16); + // tmp32no1 >>= -tmp16; // } else { - // tmp32no1 = WEBRTC_SPL_LSHIFT_W32(tmp32no1, tmp16); + // tmp32no1 <<= tmp16; // } v32x4B = vshlq_s32(v32x4A, v32x4B); @@ -121,13 +121,13 @@ *ptr_noiseEstLogQuantile); int32_t tmp32no1 = (0x00200000 | (tmp32no2 & 0x001FFFFF)); // 2^21 + frac - tmp16 = (int16_t) WEBRTC_SPL_RSHIFT_W32(tmp32no2, 21); + tmp16 = (int16_t)(tmp32no2 >> 21); tmp16 -= 21;// shift 21 to get result in Q0 tmp16 += (int16_t) inst->qNoise; //shift to get result in Q(qNoise) if (tmp16 < 0) { - tmp32no1 = WEBRTC_SPL_RSHIFT_W32(tmp32no1, -tmp16); + tmp32no1 >>= -tmp16; } else { - tmp32no1 = WEBRTC_SPL_LSHIFT_W32(tmp32no1, tmp16); + tmp32no1 <<= tmp16; } *ptr_noiseEstQuantile = WebRtcSpl_SatW32ToW16(tmp32no1); } @@ -313,13 +313,11 @@ // +=QUANTILE*delta/(inst->counter[s]+1) QUANTILE=0.25, =1 in Q2 // CounterDiv=1/(inst->counter[s]+1) in Q15 tmp16 += 2; - tmp16no1 = WEBRTC_SPL_RSHIFT_W16(tmp16, 2); - inst->noiseEstLogQuantile[offset + i] += tmp16no1; + inst->noiseEstLogQuantile[offset + i] += tmp16 / 4; } else { tmp16 += 1; - tmp16no1 = WEBRTC_SPL_RSHIFT_W16(tmp16, 1); // *(1-QUANTILE), in Q2 QUANTILE=0.25, 1-0.25=0.75=3 in Q2 - tmp16no2 = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(tmp16no1, 3, 1); + tmp16no2 = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(tmp16 / 2, 3, 1); inst->noiseEstLogQuantile[offset + i] -= tmp16no2; if (inst->noiseEstLogQuantile[offset + i] < logval) { // logval is the smallest fixed point representation we can have. @@ -363,6 +361,8 @@ // Filter the data in the frequency domain, and create spectrum. void WebRtcNsx_PrepareSpectrumNeon(NsxInst_t* inst, int16_t* freq_buf) { + assert(inst->magnLen % 8 == 1); + assert(inst->anaLen2 % 16 == 0); // (1) Filtering. @@ -374,49 +374,38 @@ // (int16_t)(inst->noiseSupFilter[i]), 14); // Q(normData-stages) // } - int16_t* ptr_real = &inst->real[0]; - int16_t* ptr_imag = &inst->imag[0]; - uint16_t* ptr_noiseSupFilter = &inst->noiseSupFilter[0]; - - // Filter the rest in the frequency domain. - for (; ptr_real < &inst->real[inst->magnLen - 1];) { - // Loop unrolled once. Both pointers are incremented by 4 twice. - __asm__ __volatile__( - "vld1.16 d20, [%[ptr_real]]\n\t" - "vld1.16 d22, [%[ptr_imag]]\n\t" - "vld1.16 d23, [%[ptr_noiseSupFilter]]!\n\t" - "vmull.s16 q10, d20, d23\n\t" - "vmull.s16 q11, d22, d23\n\t" - "vshrn.s32 d20, q10, #14\n\t" - "vshrn.s32 d22, q11, #14\n\t" - "vst1.16 d20, [%[ptr_real]]!\n\t" - "vst1.16 d22, [%[ptr_imag]]!\n\t" - - "vld1.16 d18, [%[ptr_real]]\n\t" - "vld1.16 d24, [%[ptr_imag]]\n\t" - "vld1.16 d25, [%[ptr_noiseSupFilter]]!\n\t" - "vmull.s16 q9, d18, d25\n\t" - "vmull.s16 q12, d24, d25\n\t" - "vshrn.s32 d18, q9, #14\n\t" - "vshrn.s32 d24, q12, #14\n\t" - "vst1.16 d18, [%[ptr_real]]!\n\t" - "vst1.16 d24, [%[ptr_imag]]!\n\t" - - // Specify constraints. - :[ptr_imag]"+r"(ptr_imag), - [ptr_real]"+r"(ptr_real), - [ptr_noiseSupFilter]"+r"(ptr_noiseSupFilter) - : - :"d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", - "q9", "q10", "q11", "q12" - ); - } - - // Filter the last pair of elements in the frequency domain. - *ptr_real = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(*ptr_real, - (int16_t)(*ptr_noiseSupFilter), 14); // Q(normData-stages) - *ptr_imag = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(*ptr_imag, - (int16_t)(*ptr_noiseSupFilter), 14); // Q(normData-stages) + int16_t* preal = &inst->real[0]; + int16_t* pimag = &inst->imag[0]; + int16_t* pns_filter = (int16_t*)&inst->noiseSupFilter[0]; + int16_t* pimag_end = pimag + inst->magnLen - 4; + + while (pimag < pimag_end) { + int16x8_t real = vld1q_s16(preal); + int16x8_t imag = vld1q_s16(pimag); + int16x8_t ns_filter = vld1q_s16(pns_filter); + + int32x4_t tmp_r_0 = vmull_s16(vget_low_s16(real), vget_low_s16(ns_filter)); + int32x4_t tmp_i_0 = vmull_s16(vget_low_s16(imag), vget_low_s16(ns_filter)); + int32x4_t tmp_r_1 = vmull_s16(vget_high_s16(real), + vget_high_s16(ns_filter)); + int32x4_t tmp_i_1 = vmull_s16(vget_high_s16(imag), + vget_high_s16(ns_filter)); + + int16x4_t result_r_0 = vshrn_n_s32(tmp_r_0, 14); + int16x4_t result_i_0 = vshrn_n_s32(tmp_i_0, 14); + int16x4_t result_r_1 = vshrn_n_s32(tmp_r_1, 14); + int16x4_t result_i_1 = vshrn_n_s32(tmp_i_1, 14); + + vst1q_s16(preal, vcombine_s16(result_r_0, result_r_1)); + vst1q_s16(pimag, vcombine_s16(result_i_0, result_i_1)); + preal += 8; + pimag += 8; + pns_filter += 8; + } + + // Filter the last element + *preal = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(*preal, *pns_filter, 14); + *pimag = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(*pimag, *pns_filter, 14); // (2) Create spectrum. @@ -424,74 +413,36 @@ // freq_buf[0] = inst->real[0]; // freq_buf[1] = -inst->imag[0]; // for (i = 1, j = 2; i < inst->anaLen2; i += 1, j += 2) { - // tmp16 = (inst->anaLen << 1) - j; // freq_buf[j] = inst->real[i]; // freq_buf[j + 1] = -inst->imag[i]; - // freq_buf[tmp16] = inst->real[i]; - // freq_buf[tmp16 + 1] = inst->imag[i]; // } // freq_buf[inst->anaLen] = inst->real[inst->anaLen2]; // freq_buf[inst->anaLen + 1] = -inst->imag[inst->anaLen2]; - freq_buf[0] = inst->real[0]; - freq_buf[1] = -inst->imag[0]; - - int offset = -16; - int16_t* ptr_realImag1 = &freq_buf[2]; - int16_t* ptr_realImag2 = ptr_realImag2 = &freq_buf[(inst->anaLen << 1) - 8]; - ptr_real = &inst->real[1]; - ptr_imag = &inst->imag[1]; - for (; ptr_real < &inst->real[inst->anaLen2 - 11];) { - // Loop unrolled once. All pointers are incremented twice. - __asm__ __volatile__( - "vld1.16 d22, [%[ptr_real]]!\n\t" - "vld1.16 d23, [%[ptr_imag]]!\n\t" - // Negate and interleave: - "vmov.s16 d20, d22\n\t" - "vneg.s16 d21, d23\n\t" - "vzip.16 d20, d21\n\t" - // Write 8 elements to &freq_buf[j] - "vst1.16 {d20, d21}, [%[ptr_realImag1]]!\n\t" - // Interleave and reverse elements: - "vzip.16 d22, d23\n\t" - "vrev64.32 d18, d23\n\t" - "vrev64.32 d19, d22\n\t" - // Write 8 elements to &freq_buf[tmp16] - "vst1.16 {d18, d19}, [%[ptr_realImag2]], %[offset]\n\t" - - "vld1.16 d22, [%[ptr_real]]!\n\t" - "vld1.16 d23, [%[ptr_imag]]!\n\t" - // Negate and interleave: - "vmov.s16 d20, d22\n\t" - "vneg.s16 d21, d23\n\t" - "vzip.16 d20, d21\n\t" - // Write 8 elements to &freq_buf[j] - "vst1.16 {d20, d21}, [%[ptr_realImag1]]!\n\t" - // Interleave and reverse elements: - "vzip.16 d22, d23\n\t" - "vrev64.32 d18, d23\n\t" - "vrev64.32 d19, d22\n\t" - // Write 8 elements to &freq_buf[tmp16] - "vst1.16 {d18, d19}, [%[ptr_realImag2]], %[offset]\n\t" - - // Specify constraints. - :[ptr_imag]"+r"(ptr_imag), - [ptr_real]"+r"(ptr_real), - [ptr_realImag1]"+r"(ptr_realImag1), - [ptr_realImag2]"+r"(ptr_realImag2) - :[offset]"r"(offset) - :"d18", "d19", "d20", "d21", "d22", "d23" - ); - } - for (ptr_realImag2 += 6; - ptr_real <= &inst->real[inst->anaLen2]; - ptr_real += 1, ptr_imag += 1, ptr_realImag1 += 2, ptr_realImag2 -= 2) { - *ptr_realImag1 = *ptr_real; - *(ptr_realImag1 + 1) = -(*ptr_imag); - *ptr_realImag2 = *ptr_real; - *(ptr_realImag2 + 1) = *ptr_imag; + preal = &inst->real[0]; + pimag = &inst->imag[0]; + pimag_end = pimag + inst->anaLen2; + int16_t * freq_buf_start = freq_buf; + while (pimag < pimag_end) { + // loop unroll + int16x8x2_t real_imag_0; + int16x8x2_t real_imag_1; + real_imag_0.val[1] = vld1q_s16(pimag); + real_imag_0.val[0] = vld1q_s16(preal); + preal += 8; + pimag += 8; + real_imag_1.val[1] = vld1q_s16(pimag); + real_imag_1.val[0] = vld1q_s16(preal); + preal += 8; + pimag += 8; + + real_imag_0.val[1] = vnegq_s16(real_imag_0.val[1]); + real_imag_1.val[1] = vnegq_s16(real_imag_1.val[1]); + vst2q_s16(freq_buf_start, real_imag_0); + freq_buf_start += 16; + vst2q_s16(freq_buf_start, real_imag_1); + freq_buf_start += 16; } - freq_buf[inst->anaLen] = inst->real[inst->anaLen2]; freq_buf[inst->anaLen + 1] = -inst->imag[inst->anaLen2]; } @@ -539,110 +490,91 @@ void WebRtcNsx_SynthesisUpdateNeon(NsxInst_t* inst, int16_t* out_frame, int16_t gain_factor) { - int16_t* ptr_real = &inst->real[0]; - int16_t* ptr_syn = &inst->synthesisBuffer[0]; - const int16_t* ptr_window = &inst->window[0]; + assert(inst->anaLen % 16 == 0); + assert(inst->blockLen10ms % 16 == 0); - // synthesis - __asm__ __volatile__("vdup.16 d24, %0" : : "r"(gain_factor) : "d24"); - // Loop unrolled once. All pointers are incremented in the assembly code. - for (; ptr_syn < &inst->synthesisBuffer[inst->anaLen];) { - __asm__ __volatile__( - // Load variables. - "vld1.16 d22, [%[ptr_real]]!\n\t" - "vld1.16 d23, [%[ptr_window]]!\n\t" - "vld1.16 d25, [%[ptr_syn]]\n\t" - // tmp16a = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( - // inst->window[i], inst->real[i], 14); // Q0, window in Q14 - "vmull.s16 q11, d22, d23\n\t" - "vrshrn.i32 d22, q11, #14\n\t" - // tmp32 = WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(tmp16a, gain_factor, 13); - "vmull.s16 q11, d24, d22\n\t" - // tmp16b = WebRtcSpl_SatW32ToW16(tmp32); // Q0 - "vqrshrn.s32 d22, q11, #13\n\t" - // inst->synthesisBuffer[i] = WEBRTC_SPL_ADD_SAT_W16( - // inst->synthesisBuffer[i], tmp16b); // Q0 - "vqadd.s16 d25, d22\n\t" - "vst1.16 d25, [%[ptr_syn]]!\n\t" - - // Load variables. - "vld1.16 d26, [%[ptr_real]]!\n\t" - "vld1.16 d27, [%[ptr_window]]!\n\t" - "vld1.16 d28, [%[ptr_syn]]\n\t" - // tmp16a = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( - // inst->window[i], inst->real[i], 14); // Q0, window in Q14 - "vmull.s16 q13, d26, d27\n\t" - "vrshrn.i32 d26, q13, #14\n\t" - // tmp32 = WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(tmp16a, gain_factor, 13); - "vmull.s16 q13, d24, d26\n\t" - // tmp16b = WebRtcSpl_SatW32ToW16(tmp32); // Q0 - "vqrshrn.s32 d26, q13, #13\n\t" - // inst->synthesisBuffer[i] = WEBRTC_SPL_ADD_SAT_W16( - // inst->synthesisBuffer[i], tmp16b); // Q0 - "vqadd.s16 d28, d26\n\t" - "vst1.16 d28, [%[ptr_syn]]!\n\t" - - // Specify constraints. - :[ptr_real]"+r"(ptr_real), - [ptr_window]"+r"(ptr_window), - [ptr_syn]"+r"(ptr_syn) - : - :"d22", "d23", "d24", "d25", "d26", "d27", "d28", "q11", "q12", "q13" - ); - } - - int16_t* ptr_out = &out_frame[0]; - ptr_syn = &inst->synthesisBuffer[0]; - // read out fully processed segment - for (; ptr_syn < &inst->synthesisBuffer[inst->blockLen10ms];) { - // Loop unrolled once. Both pointers are incremented in the assembly code. - __asm__ __volatile__( - // out_frame[i] = inst->synthesisBuffer[i]; // Q0 - "vld1.16 {d22, d23}, [%[ptr_syn]]!\n\t" - "vld1.16 {d24, d25}, [%[ptr_syn]]!\n\t" - "vst1.16 {d22, d23}, [%[ptr_out]]!\n\t" - "vst1.16 {d24, d25}, [%[ptr_out]]!\n\t" - :[ptr_syn]"+r"(ptr_syn), - [ptr_out]"+r"(ptr_out) - : - :"d22", "d23", "d24", "d25" - ); + int16_t* preal_start = inst->real; + const int16_t* pwindow = inst->window; + int16_t* preal_end = preal_start + inst->anaLen; + int16_t* psynthesis_buffer = inst->synthesisBuffer; + + while (preal_start < preal_end) { + // Loop unroll. + int16x8_t window_0 = vld1q_s16(pwindow); + int16x8_t real_0 = vld1q_s16(preal_start); + int16x8_t synthesis_buffer_0 = vld1q_s16(psynthesis_buffer); + + int16x8_t window_1 = vld1q_s16(pwindow + 8); + int16x8_t real_1 = vld1q_s16(preal_start + 8); + int16x8_t synthesis_buffer_1 = vld1q_s16(psynthesis_buffer + 8); + + int32x4_t tmp32a_0_low = vmull_s16(vget_low_s16(real_0), + vget_low_s16(window_0)); + int32x4_t tmp32a_0_high = vmull_s16(vget_high_s16(real_0), + vget_high_s16(window_0)); + + int32x4_t tmp32a_1_low = vmull_s16(vget_low_s16(real_1), + vget_low_s16(window_1)); + int32x4_t tmp32a_1_high = vmull_s16(vget_high_s16(real_1), + vget_high_s16(window_1)); + + int16x4_t tmp16a_0_low = vqrshrn_n_s32(tmp32a_0_low, 14); + int16x4_t tmp16a_0_high = vqrshrn_n_s32(tmp32a_0_high, 14); + + int16x4_t tmp16a_1_low = vqrshrn_n_s32(tmp32a_1_low, 14); + int16x4_t tmp16a_1_high = vqrshrn_n_s32(tmp32a_1_high, 14); + + int32x4_t tmp32b_0_low = vmull_n_s16(tmp16a_0_low, gain_factor); + int32x4_t tmp32b_0_high = vmull_n_s16(tmp16a_0_high, gain_factor); + + int32x4_t tmp32b_1_low = vmull_n_s16(tmp16a_1_low, gain_factor); + int32x4_t tmp32b_1_high = vmull_n_s16(tmp16a_1_high, gain_factor); + + int16x4_t tmp16b_0_low = vqrshrn_n_s32(tmp32b_0_low, 13); + int16x4_t tmp16b_0_high = vqrshrn_n_s32(tmp32b_0_high, 13); + + int16x4_t tmp16b_1_low = vqrshrn_n_s32(tmp32b_1_low, 13); + int16x4_t tmp16b_1_high = vqrshrn_n_s32(tmp32b_1_high, 13); + + synthesis_buffer_0 = vqaddq_s16(vcombine_s16(tmp16b_0_low, tmp16b_0_high), + synthesis_buffer_0); + synthesis_buffer_1 = vqaddq_s16(vcombine_s16(tmp16b_1_low, tmp16b_1_high), + synthesis_buffer_1); + vst1q_s16(psynthesis_buffer, synthesis_buffer_0); + vst1q_s16(psynthesis_buffer + 8, synthesis_buffer_1); + + pwindow += 16; + preal_start += 16; + psynthesis_buffer += 16; + } + + // Read out fully processed segment. + int16_t * p_start = inst->synthesisBuffer; + int16_t * p_end = inst->synthesisBuffer + inst->blockLen10ms; + int16_t * p_frame = out_frame; + while (p_start < p_end) { + int16x8_t frame_0 = vld1q_s16(p_start); + vst1q_s16(p_frame, frame_0); + p_start += 8; + p_frame += 8; } // Update synthesis buffer. - // C code: - // WEBRTC_SPL_MEMCPY_W16(inst->synthesisBuffer, - // inst->synthesisBuffer + inst->blockLen10ms, - // inst->anaLen - inst->blockLen10ms); - ptr_out = &inst->synthesisBuffer[0], - ptr_syn = &inst->synthesisBuffer[inst->blockLen10ms]; - for (; ptr_syn < &inst->synthesisBuffer[inst->anaLen];) { - // Loop unrolled once. Both pointers are incremented in the assembly code. - __asm__ __volatile__( - "vld1.16 {d22, d23}, [%[ptr_syn]]!\n\t" - "vld1.16 {d24, d25}, [%[ptr_syn]]!\n\t" - "vst1.16 {d22, d23}, [%[ptr_out]]!\n\t" - "vst1.16 {d24, d25}, [%[ptr_out]]!\n\t" - :[ptr_syn]"+r"(ptr_syn), - [ptr_out]"+r"(ptr_out) - : - :"d22", "d23", "d24", "d25" - ); + int16_t* p_start_src = inst->synthesisBuffer + inst->blockLen10ms; + int16_t* p_end_src = inst->synthesisBuffer + inst->anaLen; + int16_t* p_start_dst = inst->synthesisBuffer; + while (p_start_src < p_end_src) { + int16x8_t frame = vld1q_s16(p_start_src); + vst1q_s16(p_start_dst, frame); + p_start_src += 8; + p_start_dst += 8; } - // C code: - // WebRtcSpl_ZerosArrayW16(inst->synthesisBuffer - // + inst->anaLen - inst->blockLen10ms, inst->blockLen10ms); - __asm__ __volatile__("vdup.16 q10, %0" : : "r"(0) : "q10"); - for (; ptr_out < &inst->synthesisBuffer[inst->anaLen];) { - // Loop unrolled once. Pointer is incremented in the assembly code. - __asm__ __volatile__( - "vst1.16 {d20, d21}, [%[ptr_out]]!\n\t" - "vst1.16 {d20, d21}, [%[ptr_out]]!\n\t" - :[ptr_out]"+r"(ptr_out) - : - :"d20", "d21" - ); + p_start = inst->synthesisBuffer + inst->anaLen - inst->blockLen10ms; + p_end = p_start + inst->blockLen10ms; + int16x8_t zero = vdupq_n_s16(0); + for (;p_start < p_end; p_start += 8) { + vst1q_s16(p_start, zero); } } @@ -650,75 +582,64 @@ void WebRtcNsx_AnalysisUpdateNeon(NsxInst_t* inst, int16_t* out, int16_t* new_speech) { - - int16_t* ptr_ana = &inst->analysisBuffer[inst->blockLen10ms]; - int16_t* ptr_out = &inst->analysisBuffer[0]; + assert(inst->blockLen10ms % 16 == 0); + assert(inst->anaLen % 16 == 0); // For lower band update analysis buffer. // WEBRTC_SPL_MEMCPY_W16(inst->analysisBuffer, // inst->analysisBuffer + inst->blockLen10ms, // inst->anaLen - inst->blockLen10ms); - for (; ptr_out < &inst->analysisBuffer[inst->anaLen - inst->blockLen10ms];) { - // Loop unrolled once, so both pointers are incremented by 8 twice. - __asm__ __volatile__( - "vld1.16 {d20, d21}, [%[ptr_ana]]!\n\t" - "vst1.16 {d20, d21}, [%[ptr_out]]!\n\t" - "vld1.16 {d22, d23}, [%[ptr_ana]]!\n\t" - "vst1.16 {d22, d23}, [%[ptr_out]]!\n\t" - :[ptr_ana]"+r"(ptr_ana), - [ptr_out]"+r"(ptr_out) - : - :"d20", "d21", "d22", "d23" - ); + int16_t* p_start_src = inst->analysisBuffer + inst->blockLen10ms; + int16_t* p_end_src = inst->analysisBuffer + inst->anaLen; + int16_t* p_start_dst = inst->analysisBuffer; + while (p_start_src < p_end_src) { + int16x8_t frame = vld1q_s16(p_start_src); + vst1q_s16(p_start_dst, frame); + + p_start_src += 8; + p_start_dst += 8; } // WEBRTC_SPL_MEMCPY_W16(inst->analysisBuffer // + inst->anaLen - inst->blockLen10ms, new_speech, inst->blockLen10ms); - for (ptr_ana = new_speech; ptr_out < &inst->analysisBuffer[inst->anaLen];) { - // Loop unrolled once, so both pointers are incremented by 8 twice. - __asm__ __volatile__( - "vld1.16 {d20, d21}, [%[ptr_ana]]!\n\t" - "vst1.16 {d20, d21}, [%[ptr_out]]!\n\t" - "vld1.16 {d22, d23}, [%[ptr_ana]]!\n\t" - "vst1.16 {d22, d23}, [%[ptr_out]]!\n\t" - :[ptr_ana]"+r"(ptr_ana), - [ptr_out]"+r"(ptr_out) - : - :"d20", "d21", "d22", "d23" - ); - } - - // Window data before FFT - const int16_t* ptr_window = &inst->window[0]; - ptr_out = &out[0]; - ptr_ana = &inst->analysisBuffer[0]; - for (; ptr_out < &out[inst->anaLen];) { - - // Loop unrolled once, so all pointers are incremented by 4 twice. - __asm__ __volatile__( - "vld1.16 d20, [%[ptr_ana]]!\n\t" - "vld1.16 d21, [%[ptr_window]]!\n\t" - // out[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( - // inst->window[i], inst->analysisBuffer[i], 14); // Q0 - "vmull.s16 q10, d20, d21\n\t" - "vrshrn.i32 d20, q10, #14\n\t" - "vst1.16 d20, [%[ptr_out]]!\n\t" - - "vld1.16 d22, [%[ptr_ana]]!\n\t" - "vld1.16 d23, [%[ptr_window]]!\n\t" - // out[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( - // inst->window[i], inst->analysisBuffer[i], 14); // Q0 - "vmull.s16 q11, d22, d23\n\t" - "vrshrn.i32 d22, q11, #14\n\t" - "vst1.16 d22, [%[ptr_out]]!\n\t" - - // Specify constraints. - :[ptr_ana]"+r"(ptr_ana), - [ptr_window]"+r"(ptr_window), - [ptr_out]"+r"(ptr_out) - : - :"d20", "d21", "d22", "d23", "q10", "q11" - ); + p_start_src = new_speech; + p_end_src = new_speech + inst->blockLen10ms; + p_start_dst = inst->analysisBuffer + inst->anaLen - inst->blockLen10ms; + while (p_start_src < p_end_src) { + int16x8_t frame = vld1q_s16(p_start_src); + vst1q_s16(p_start_dst, frame); + + p_start_src += 8; + p_start_dst += 8; + } + + // Window data before FFT. + int16_t* p_start_window = (int16_t*) inst->window; + int16_t* p_start_buffer = inst->analysisBuffer; + int16_t* p_start_out = out; + const int16_t* p_end_out = out + inst->anaLen; + + // Load the first element to reduce pipeline bubble. + int16x8_t window = vld1q_s16(p_start_window); + int16x8_t buffer = vld1q_s16(p_start_buffer); + p_start_window += 8; + p_start_buffer += 8; + + while (p_start_out < p_end_out) { + // Unroll loop. + int32x4_t tmp32_low = vmull_s16(vget_low_s16(window), vget_low_s16(buffer)); + int32x4_t tmp32_high = vmull_s16(vget_high_s16(window), + vget_high_s16(buffer)); + window = vld1q_s16(p_start_window); + buffer = vld1q_s16(p_start_buffer); + + int16x4_t result_low = vrshrn_n_s32(tmp32_low, 14); + int16x4_t result_high = vrshrn_n_s32(tmp32_high, 14); + vst1q_s16(p_start_out, vcombine_s16(result_low, result_high)); + + p_start_buffer += 8; + p_start_window += 8; + p_start_out += 8; } } @@ -737,7 +658,7 @@ // Loop unrolled once, so ptr_in is incremented by 8 twice, // and ptr_out is incremented by 8 four times. __asm__ __volatile__( - // out[j] = WEBRTC_SPL_LSHIFT_W16(in[i], inst->normData); // Q(normData) + // out[j] = in[i] << inst->normData; // Q(normData) "vld1.16 {d22, d23}, [%[ptr_in]]!\n\t" "vshl.s16 q11, q10\n\t" "vmov d24, d23\n\t" @@ -747,7 +668,7 @@ "vst2.16 {d22, d23}, [%[ptr_out]]!\n\t" "vst2.16 {d24, d25}, [%[ptr_out]]!\n\t" - // out[j] = WEBRTC_SPL_LSHIFT_W16(in[i], inst->normData); // Q(normData) + // out[j] = in[i] << inst->normData; // Q(normData) "vld1.16 {d22, d23}, [%[ptr_in]]!\n\t" "vshl.s16 q11, q10\n\t" "vmov d24, d23\n\t" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core_neon.S thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core_neon.S --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core_neon.S 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/ns/nsx_core_neon.S 2015-02-03 14:33:35.000000000 +0000 @@ -12,6 +12,8 @@ @ This file contains some functions in NS, optimized for ARM Neon @ platforms. Reference C code is in file nsx_core.c. Bit-exact. +.syntax unified + #include "nsx_core_neon_offsets.h" #include "webrtc/modules/audio_processing/ns/nsx_defines.h" #include "webrtc/system_wrappers/interface/asm_defines.h" @@ -119,7 +121,7 @@ LOOP_SET_LMAGN: ldrh r2, [r1], #2 @ magn[i] cmp r2, #0 - streqh r3, [r12], #2 @ lmagn[i] = logval; + strheq r3, [r12], #2 @ lmagn[i] = logval; beq CHECK_LMAGN_COUNTER clz r6, r2 @@ -267,7 +269,7 @@ mov r0, r12, lsr #1 @ tmp16no1 mov r12, #3 smulbb r12, r0, r12 @ tmp16no2 - sub r2, r12, lsr #1 + sub r2, r2, r12, lsr #1 cmp r3, r2 ldrgt r2, [sp] ldrgt r3, [sp] @@ -275,7 +277,7 @@ UPDATE_LOG_QUANTILE_ESTIMATE_BIGGER_LMAGN: add r3, r12, #2 - add r2, r3, lsr #2 + add r2, r2, r3, lsr #2 UPDATE_LOG_QUANTILE_ESTIMATE_STORE: vmov.s16 r0, d25[0] @ countProd diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/OWNERS 2015-02-03 14:33:35.000000000 +0000 @@ -1,2 +1,10 @@ +aluebs@webrtc.org andrew@webrtc.org bjornv@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/processing_component.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/processing_component.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/processing_component.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/processing_component.cc 2015-02-03 14:33:35.000000000 +0000 @@ -12,15 +12,12 @@ #include -#include "webrtc/modules/audio_processing/audio_processing_impl.h" +#include "webrtc/modules/audio_processing/include/audio_processing.h" namespace webrtc { -ProcessingComponent::ProcessingComponent() {} - -ProcessingComponent::ProcessingComponent(const AudioProcessingImpl* apm) - : apm_(apm), - initialized_(false), +ProcessingComponent::ProcessingComponent() + : initialized_(false), enabled_(false), num_handles_(0) {} @@ -35,7 +32,7 @@ } initialized_ = false; - return apm_->kNoError; + return AudioProcessing::kNoError; } int ProcessingComponent::EnableComponent(bool enable) { @@ -43,7 +40,7 @@ enabled_ = enable; // Must be set before Initialize() is called. int err = Initialize(); - if (err != apm_->kNoError) { + if (err != AudioProcessing::kNoError) { enabled_ = false; return err; } @@ -51,7 +48,7 @@ enabled_ = enable; } - return apm_->kNoError; + return AudioProcessing::kNoError; } bool ProcessingComponent::is_component_enabled() const { @@ -69,7 +66,7 @@ int ProcessingComponent::Initialize() { if (!enabled_) { - return apm_->kNoError; + return AudioProcessing::kNoError; } num_handles_ = num_handles_required(); @@ -82,12 +79,12 @@ if (handles_[i] == NULL) { handles_[i] = CreateHandle(); if (handles_[i] == NULL) { - return apm_->kCreationFailedError; + return AudioProcessing::kCreationFailedError; } } int err = InitializeHandle(handles_[i]); - if (err != apm_->kNoError) { + if (err != AudioProcessing::kNoError) { return GetHandleError(handles_[i]); } } @@ -98,17 +95,17 @@ int ProcessingComponent::Configure() { if (!initialized_) { - return apm_->kNoError; + return AudioProcessing::kNoError; } assert(static_cast(handles_.size()) >= num_handles_); for (int i = 0; i < num_handles_; i++) { int err = ConfigureHandle(handles_[i]); - if (err != apm_->kNoError) { + if (err != AudioProcessing::kNoError) { return GetHandleError(handles_[i]); } } - return apm_->kNoError; + return AudioProcessing::kNoError; } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/processing_component.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/processing_component.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/processing_component.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/processing_component.h 2015-02-03 14:33:35.000000000 +0000 @@ -13,16 +13,13 @@ #include -#include "webrtc/modules/audio_processing/include/audio_processing.h" +#include "webrtc/common.h" namespace webrtc { -class AudioProcessingImpl; - class ProcessingComponent { public: ProcessingComponent(); - explicit ProcessingComponent(const AudioProcessingImpl* apm); virtual ~ProcessingComponent(); virtual int Initialize(); @@ -41,11 +38,10 @@ virtual void* CreateHandle() const = 0; virtual int InitializeHandle(void* handle) const = 0; virtual int ConfigureHandle(void* handle) const = 0; - virtual int DestroyHandle(void* handle) const = 0; + virtual void DestroyHandle(void* handle) const = 0; virtual int num_handles_required() const = 0; virtual int GetHandleError(void* handle) const = 0; - const AudioProcessingImpl* apm_; std::vector handles_; bool initialized_; bool enabled_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/rms_level.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/rms_level.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/rms_level.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/rms_level.cc 2015-02-03 14:33:35.000000000 +0000 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_processing/rms_level.h" + +#include +#include + +namespace webrtc { + +static const float kMaxSquaredLevel = 32768 * 32768; + +RMSLevel::RMSLevel() + : sum_square_(0), + sample_count_(0) {} + +RMSLevel::~RMSLevel() {} + +void RMSLevel::Reset() { + sum_square_ = 0; + sample_count_ = 0; +} + +void RMSLevel::Process(const int16_t* data, int length) { + for (int i = 0; i < length; ++i) { + sum_square_ += data[i] * data[i]; + } + sample_count_ += length; +} + +void RMSLevel::ProcessMuted(int length) { + sample_count_ += length; +} + +int RMSLevel::RMS() { + if (sample_count_ == 0 || sum_square_ == 0) { + Reset(); + return kMinLevel; + } + + // Normalize by the max level. + float rms = sum_square_ / (sample_count_ * kMaxSquaredLevel); + // 20log_10(x^0.5) = 10log_10(x) + rms = 10 * log10(rms); + assert(rms <= 0); + if (rms < -kMinLevel) + rms = -kMinLevel; + + rms = -rms; + Reset(); + return static_cast(rms + 0.5); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/rms_level.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/rms_level.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/rms_level.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/rms_level.h 2015-02-03 14:33:35.000000000 +0000 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_RMS_LEVEL_H_ +#define WEBRTC_MODULES_AUDIO_PROCESSING_RMS_LEVEL_H_ + +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Computes the root mean square (RMS) level in dBFs (decibels from digital +// full-scale) of audio data. The computation follows RFC 6465: +// https://tools.ietf.org/html/rfc6465 +// with the intent that it can provide the RTP audio level indication. +// +// The expected approach is to provide constant-sized chunks of audio to +// Process(). When enough chunks have been accumulated to form a packet, call +// RMS() to get the audio level indicator for the RTP header. +class RMSLevel { + public: + static const int kMinLevel = 127; + + RMSLevel(); + ~RMSLevel(); + + // Can be called to reset internal states, but is not required during normal + // operation. + void Reset(); + + // Pass each chunk of audio to Process() to accumulate the level. + void Process(const int16_t* data, int length); + + // If all samples with the given |length| have a magnitude of zero, this is + // a shortcut to avoid some computation. + void ProcessMuted(int length); + + // Computes the RMS level over all data passed to Process() since the last + // call to RMS(). The returned value is positive but should be interpreted as + // negative as per the RFC. It is constrained to [0, 127]. + int RMS(); + + private: + float sum_square_; + int sample_count_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_PROCESSING_RMS_LEVEL_H_ + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/android/apmtest/jni/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/android/apmtest/jni/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/android/apmtest/jni/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/android/apmtest/jni/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -# Copyright (C) 2010 The Android Open Source Project -# -# 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. -# -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := apmtest-activity -LOCAL_SRC_FILES := main.c -LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM -LOCAL_STATIC_LIBRARIES := android_native_app_glue - -include $(BUILD_SHARED_LIBRARY) - -$(call import-module,android/native_app_glue) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/audio_processing_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/audio_processing_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/audio_processing_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/audio_processing_unittest.cc 2015-02-03 14:33:35.000000000 +0000 @@ -8,11 +8,15 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include #include - #include +#include #include +#include "webrtc/common_audio/include/audio_util.h" +#include "webrtc/common_audio/resampler/include/push_resampler.h" +#include "webrtc/common_audio/resampler/push_sinc_resampler.h" #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" #include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/modules/audio_processing/test/test_utils.h" @@ -30,38 +34,18 @@ #include "webrtc/audio_processing/unittest.pb.h" #endif -#if (defined(WEBRTC_AUDIOPROC_FIXED_PROFILE)) || \ - (defined(WEBRTC_LINUX) && defined(WEBRTC_ARCH_X86_64) && !defined(NDEBUG)) -# define WEBRTC_AUDIOPROC_BIT_EXACT -#endif - -using webrtc::AudioProcessing; -using webrtc::AudioFrame; -using webrtc::Config; -using webrtc::ExperimentalAgc; -using webrtc::GainControl; -using webrtc::NoiseSuppression; -using webrtc::EchoCancellation; -using webrtc::EventWrapper; -using webrtc::scoped_array; -using webrtc::scoped_ptr; -using webrtc::Trace; -using webrtc::LevelEstimator; -using webrtc::EchoCancellation; -using webrtc::EchoControlMobile; -using webrtc::VoiceDetection; - +namespace webrtc { namespace { + // TODO(bjornv): This is not feasible until the functionality has been -// re-implemented; see comment at the bottom of this file. +// re-implemented; see comment at the bottom of this file. For now, the user has +// to hard code the |write_ref_data| value. // When false, this will compare the output data with the results stored to // file. This is the typical case. When the file should be updated, it can // be set to true with the command-line switch --write_ref_data. -#ifdef WEBRTC_AUDIOPROC_BIT_EXACT bool write_ref_data = false; const int kChannels[] = {1, 2}; const size_t kChannelsSize = sizeof(kChannels) / sizeof(*kChannels); -#endif const int kSampleRates[] = {8000, 16000, 32000}; const size_t kSampleRatesSize = sizeof(kSampleRates) / sizeof(*kSampleRates); @@ -75,19 +59,51 @@ const size_t kProcessSampleRatesSize = sizeof(kProcessSampleRates) / sizeof(*kProcessSampleRates); +void ConvertToFloat(const int16_t* int_data, ChannelBuffer* cb) { + ChannelBuffer cb_int(cb->samples_per_channel(), + cb->num_channels()); + Deinterleave(int_data, + cb->samples_per_channel(), + cb->num_channels(), + cb_int.channels()); + S16ToFloat(cb_int.data(), + cb->samples_per_channel() * cb->num_channels(), + cb->data()); +} + +void ConvertToFloat(const AudioFrame& frame, ChannelBuffer* cb) { + ConvertToFloat(frame.data_, cb); +} + +// Number of channels including the keyboard channel. +int TotalChannelsFromLayout(AudioProcessing::ChannelLayout layout) { + switch (layout) { + case AudioProcessing::kMono: + return 1; + case AudioProcessing::kMonoAndKeyboard: + case AudioProcessing::kStereo: + return 2; + case AudioProcessing::kStereoAndKeyboard: + return 3; + } + assert(false); + return -1; +} + int TruncateToMultipleOf10(int value) { return (value / 10) * 10; } -// TODO(andrew): Use the MonoToStereo routine from AudioFrameOperations. -void MixStereoToMono(const int16_t* stereo, - int16_t* mono, +void MixStereoToMono(const float* stereo, float* mono, int samples_per_channel) { - for (int i = 0; i < samples_per_channel; i++) { - int32_t mono_s32 = (static_cast(stereo[i * 2]) + - static_cast(stereo[i * 2 + 1])) >> 1; - mono[i] = static_cast(mono_s32); - } + for (int i = 0; i < samples_per_channel; ++i) + mono[i] = (stereo[i * 2] + stereo[i * 2 + 1]) / 2; +} + +void MixStereoToMono(const int16_t* stereo, int16_t* mono, + int samples_per_channel) { + for (int i = 0; i < samples_per_channel; ++i) + mono[i] = (stereo[i * 2] + stereo[i * 2 + 1]) >> 1; } void CopyLeftToRightChannel(int16_t* stereo, int samples_per_channel) { @@ -103,8 +119,7 @@ } void SetFrameTo(AudioFrame* frame, int16_t value) { - for (int i = 0; i < frame->samples_per_channel_ * frame->num_channels_; - ++i) { + for (int i = 0; i < frame->samples_per_channel_ * frame->num_channels_; ++i) { frame->data_[i] = value; } } @@ -117,25 +132,52 @@ } } +void ScaleFrame(AudioFrame* frame, float scale) { + for (int i = 0; i < frame->samples_per_channel_ * frame->num_channels_; ++i) { + frame->data_[i] = FloatS16ToS16(frame->data_[i] * scale); + } +} + bool FrameDataAreEqual(const AudioFrame& frame1, const AudioFrame& frame2) { - if (frame1.samples_per_channel_ != - frame2.samples_per_channel_) { + if (frame1.samples_per_channel_ != frame2.samples_per_channel_) { return false; } - if (frame1.num_channels_ != - frame2.num_channels_) { + if (frame1.num_channels_ != frame2.num_channels_) { return false; } if (memcmp(frame1.data_, frame2.data_, frame1.samples_per_channel_ * frame1.num_channels_ * - sizeof(int16_t))) { + sizeof(int16_t))) { return false; } return true; } -#ifdef WEBRTC_AUDIOPROC_BIT_EXACT -// These functions are only used by the bit-exact test. +void EnableAllAPComponents(AudioProcessing* ap) { +#if defined(WEBRTC_AUDIOPROC_FIXED_PROFILE) + EXPECT_NOERR(ap->echo_control_mobile()->Enable(true)); + + EXPECT_NOERR(ap->gain_control()->set_mode(GainControl::kAdaptiveDigital)); + EXPECT_NOERR(ap->gain_control()->Enable(true)); +#elif defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) + EXPECT_NOERR(ap->echo_cancellation()->enable_drift_compensation(true)); + EXPECT_NOERR(ap->echo_cancellation()->enable_metrics(true)); + EXPECT_NOERR(ap->echo_cancellation()->enable_delay_logging(true)); + EXPECT_NOERR(ap->echo_cancellation()->Enable(true)); + + EXPECT_NOERR(ap->gain_control()->set_mode(GainControl::kAdaptiveAnalog)); + EXPECT_NOERR(ap->gain_control()->set_analog_level_limits(0, 255)); + EXPECT_NOERR(ap->gain_control()->Enable(true)); +#endif + + EXPECT_NOERR(ap->high_pass_filter()->Enable(true)); + EXPECT_NOERR(ap->level_estimator()->Enable(true)); + EXPECT_NOERR(ap->noise_suppression()->Enable(true)); + + EXPECT_NOERR(ap->voice_detection()->Enable(true)); +} + +// These functions are only used by ApmTest.Process. template T AbsValue(T a) { return a > 0 ? a: -a; @@ -153,7 +195,7 @@ #if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) void TestStats(const AudioProcessing::Statistic& test, - const webrtc::audioproc::Test::Statistic& reference) { + const audioproc::Test::Statistic& reference) { EXPECT_EQ(reference.instant(), test.instant); EXPECT_EQ(reference.average(), test.average); EXPECT_EQ(reference.maximum(), test.maximum); @@ -161,50 +203,71 @@ } void WriteStatsMessage(const AudioProcessing::Statistic& output, - webrtc::audioproc::Test::Statistic* message) { - message->set_instant(output.instant); - message->set_average(output.average); - message->set_maximum(output.maximum); - message->set_minimum(output.minimum); + audioproc::Test::Statistic* msg) { + msg->set_instant(output.instant); + msg->set_average(output.average); + msg->set_maximum(output.maximum); + msg->set_minimum(output.minimum); } #endif -void WriteMessageLiteToFile(const std::string filename, - const ::google::protobuf::MessageLite& message) { +void OpenFileAndWriteMessage(const std::string filename, + const ::google::protobuf::MessageLite& msg) { +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) FILE* file = fopen(filename.c_str(), "wb"); - ASSERT_TRUE(file != NULL) << "Could not open " << filename; - int size = message.ByteSize(); + ASSERT_TRUE(file != NULL); + + int32_t size = msg.ByteSize(); ASSERT_GT(size, 0); - unsigned char* array = new unsigned char[size]; - ASSERT_TRUE(message.SerializeToArray(array, size)); + scoped_ptr array(new uint8_t[size]); + ASSERT_TRUE(msg.SerializeToArray(array.get(), size)); - ASSERT_EQ(1u, fwrite(&size, sizeof(int), 1, file)); + ASSERT_EQ(1u, fwrite(&size, sizeof(size), 1, file)); ASSERT_EQ(static_cast(size), - fwrite(array, sizeof(unsigned char), size, file)); - - delete [] array; + fwrite(array.get(), sizeof(array[0]), size, file)); fclose(file); +#else + std::cout << "Warning: Writing new reference is only allowed on Linux!" + << std::endl; +#endif } -void ReadMessageLiteFromFile(const std::string filename, - ::google::protobuf::MessageLite* message) { - assert(message != NULL); +std::string ResourceFilePath(std::string name, int sample_rate_hz) { + std::ostringstream ss; + // Resource files are all stereo. + ss << name << sample_rate_hz / 1000 << "_stereo"; + return test::ResourcePath(ss.str(), "pcm"); +} - FILE* file = fopen(filename.c_str(), "rb"); - ASSERT_TRUE(file != NULL) << "Could not open " << filename; - int size = 0; - ASSERT_EQ(1u, fread(&size, sizeof(int), 1, file)); - ASSERT_GT(size, 0); - unsigned char* array = new unsigned char[size]; - ASSERT_EQ(static_cast(size), - fread(array, sizeof(unsigned char), size, file)); +std::string OutputFilePath(std::string name, + int input_rate, + int output_rate, + int reverse_rate, + int num_input_channels, + int num_output_channels, + int num_reverse_channels) { + std::ostringstream ss; + ss << name << "_i" << num_input_channels << "_" << input_rate / 1000 + << "_r" << num_reverse_channels << "_" << reverse_rate / 1000 << "_"; + if (num_output_channels == 1) { + ss << "mono"; + } else if (num_output_channels == 2) { + ss << "stereo"; + } else { + assert(false); + } + ss << output_rate / 1000 << ".pcm"; - ASSERT_TRUE(message->ParseFromArray(array, size)); + return test::OutputPath() + ss.str(); +} - delete [] array; +void OpenFileAndReadMessage(const std::string filename, + ::google::protobuf::MessageLite* msg) { + FILE* file = fopen(filename.c_str(), "rb"); + ASSERT_TRUE(file != NULL); + ReadMessageFromFile(file, msg); fclose(file); } -#endif // WEBRTC_AUDIOPROC_BIT_EXACT class ApmTest : public ::testing::Test { protected: @@ -214,8 +277,7 @@ static void SetUpTestCase() { Trace::CreateTrace(); - std::string trace_filename = webrtc::test::OutputPath() + - "audioproc_trace.txt"; + std::string trace_filename = test::OutputPath() + "audioproc_trace.txt"; ASSERT_EQ(0, Trace::SetTraceFile(trace_filename.c_str())); } @@ -223,38 +285,59 @@ Trace::ReturnTrace(); } - void Init(int sample_rate_hz, int num_reverse_channels, - int num_input_channels, int num_output_channels, + // Used to select between int and float interface tests. + enum Format { + kIntFormat, + kFloatFormat + }; + + void Init(int sample_rate_hz, + int output_sample_rate_hz, + int reverse_sample_rate_hz, + int num_reverse_channels, + int num_input_channels, + int num_output_channels, bool open_output_file); - std::string ResourceFilePath(std::string name, int sample_rate_hz); - std::string OutputFilePath(std::string name, - int sample_rate_hz, - int num_reverse_channels, - int num_input_channels, - int num_output_channels); + void Init(AudioProcessing* ap); void EnableAllComponents(); bool ReadFrame(FILE* file, AudioFrame* frame); + bool ReadFrame(FILE* file, AudioFrame* frame, ChannelBuffer* cb); + void ReadFrameWithRewind(FILE* file, AudioFrame* frame); + void ReadFrameWithRewind(FILE* file, AudioFrame* frame, + ChannelBuffer* cb); void ProcessWithDefaultStreamParameters(AudioFrame* frame); void ProcessDelayVerificationTest(int delay_ms, int system_delay_ms, int delay_min, int delay_max); void TestChangingChannels(int num_channels, AudioProcessing::Error expected_return); + void RunQuantizedVolumeDoesNotGetStuckTest(int sample_rate); + void RunManualVolumeChangeIsPossibleTest(int sample_rate); + void StreamParametersTest(Format format); + int ProcessStreamChooser(Format format); + int AnalyzeReverseStreamChooser(Format format); + void ProcessDebugDump(const std::string& in_filename, + const std::string& out_filename, + Format format); + void VerifyDebugDumpTest(Format format); const std::string output_path_; const std::string ref_path_; const std::string ref_filename_; - scoped_ptr apm_; + scoped_ptr apm_; AudioFrame* frame_; AudioFrame* revframe_; + scoped_ptr > float_cb_; + scoped_ptr > revfloat_cb_; + int output_sample_rate_hz_; + int num_output_channels_; FILE* far_file_; FILE* near_file_; FILE* out_file_; }; ApmTest::ApmTest() - : output_path_(webrtc::test::OutputPath()), - ref_path_(webrtc::test::ProjectRootPath() + - "data/audio_processing/"), + : output_path_(test::OutputPath()), + ref_path_(test::ProjectRootPath() + "data/audio_processing/"), #if defined(WEBRTC_AUDIOPROC_FIXED_PROFILE) ref_filename_(ref_path_ + "output_data_fixed.pb"), #elif defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) @@ -262,6 +345,8 @@ #endif frame_(NULL), revframe_(NULL), + output_sample_rate_hz_(0), + num_output_channels_(0), far_file_(NULL), near_file_(NULL), out_file_(NULL) { @@ -277,9 +362,9 @@ revframe_ = new AudioFrame(); #if defined(WEBRTC_AUDIOPROC_FIXED_PROFILE) - Init(16000, 2, 2, 2, false); + Init(16000, 16000, 16000, 2, 2, 2, false); #else - Init(32000, 2, 2, 2, false); + Init(32000, 32000, 32000, 2, 2, 2, false); #endif } @@ -310,51 +395,30 @@ out_file_ = NULL; } -std::string ApmTest::ResourceFilePath(std::string name, int sample_rate_hz) { - std::ostringstream ss; - // Resource files are all stereo. - ss << name << sample_rate_hz / 1000 << "_stereo"; - return webrtc::test::ResourcePath(ss.str(), "pcm"); -} - -std::string ApmTest::OutputFilePath(std::string name, - int sample_rate_hz, - int num_reverse_channels, - int num_input_channels, - int num_output_channels) { - std::ostringstream ss; - ss << name << sample_rate_hz / 1000 << "_" << num_reverse_channels << "r" << - num_input_channels << "i" << "_"; - if (num_output_channels == 1) { - ss << "mono"; - } else if (num_output_channels == 2) { - ss << "stereo"; - } else { - assert(false); - return ""; - } - ss << ".pcm"; - - return output_path_ + ss.str(); -} - -void ApmTest::Init(int sample_rate_hz, int num_reverse_channels, - int num_input_channels, int num_output_channels, +void ApmTest::Init(AudioProcessing* ap) { + ASSERT_EQ(kNoErr, + ap->Initialize(frame_->sample_rate_hz_, + output_sample_rate_hz_, + revframe_->sample_rate_hz_, + LayoutFromChannels(frame_->num_channels_), + LayoutFromChannels(num_output_channels_), + LayoutFromChannels(revframe_->num_channels_))); +} + +void ApmTest::Init(int sample_rate_hz, + int output_sample_rate_hz, + int reverse_sample_rate_hz, + int num_input_channels, + int num_output_channels, + int num_reverse_channels, bool open_output_file) { - // We always use 10 ms frames. - const int samples_per_channel = kChunkSizeMs * sample_rate_hz / 1000; - frame_->samples_per_channel_ = samples_per_channel; - frame_->num_channels_ = num_input_channels; - frame_->sample_rate_hz_ = sample_rate_hz; - revframe_->samples_per_channel_ = samples_per_channel; - revframe_->num_channels_ = num_reverse_channels; - revframe_->sample_rate_hz_ = sample_rate_hz; - - // Make one process call to ensure the audio parameters are set. It might - // result in a stream error which we can safely ignore. - int err = apm_->ProcessStream(frame_); - ASSERT_TRUE(err == kNoErr || err == apm_->kStreamParameterNotSetError); - ASSERT_EQ(apm_->kNoError, apm_->Initialize()); + SetContainerFormat(sample_rate_hz, num_input_channels, frame_, &float_cb_); + output_sample_rate_hz_ = output_sample_rate_hz; + num_output_channels_ = num_output_channels; + + SetContainerFormat(reverse_sample_rate_hz, num_reverse_channels, revframe_, + &revfloat_cb_); + Init(apm_.get()); if (far_file_) { ASSERT_EQ(0, fclose(far_file_)); @@ -376,8 +440,13 @@ if (out_file_) { ASSERT_EQ(0, fclose(out_file_)); } - filename = OutputFilePath("out", sample_rate_hz, num_reverse_channels, - num_input_channels, num_output_channels); + filename = OutputFilePath("out", + sample_rate_hz, + output_sample_rate_hz, + reverse_sample_rate_hz, + num_input_channels, + num_output_channels, + num_reverse_channels); out_file_ = fopen(filename.c_str(), "wb"); ASSERT_TRUE(out_file_ != NULL) << "Could not open file " << filename << "\n"; @@ -385,42 +454,11 @@ } void ApmTest::EnableAllComponents() { -#if defined(WEBRTC_AUDIOPROC_FIXED_PROFILE) - EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(true)); - - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_mode(GainControl::kAdaptiveDigital)); - EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); -#elif defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) - EXPECT_EQ(apm_->kNoError, - apm_->echo_cancellation()->enable_drift_compensation(true)); - EXPECT_EQ(apm_->kNoError, - apm_->echo_cancellation()->enable_metrics(true)); - EXPECT_EQ(apm_->kNoError, - apm_->echo_cancellation()->enable_delay_logging(true)); - EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); - - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_mode(GainControl::kAdaptiveAnalog)); - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_analog_level_limits(0, 255)); - EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); -#endif - - EXPECT_EQ(apm_->kNoError, - apm_->high_pass_filter()->Enable(true)); - - EXPECT_EQ(apm_->kNoError, - apm_->level_estimator()->Enable(true)); - - EXPECT_EQ(apm_->kNoError, - apm_->noise_suppression()->Enable(true)); - - EXPECT_EQ(apm_->kNoError, - apm_->voice_detection()->Enable(true)); + EnableAllAPComponents(apm_.get()); } -bool ApmTest::ReadFrame(FILE* file, AudioFrame* frame) { +bool ApmTest::ReadFrame(FILE* file, AudioFrame* frame, + ChannelBuffer* cb) { // The files always contain stereo audio. size_t frame_size = frame->samples_per_channel_ * 2; size_t read_count = fread(frame->data_, @@ -438,9 +476,30 @@ frame->samples_per_channel_); } + if (cb) { + ConvertToFloat(*frame, cb); + } return true; } +bool ApmTest::ReadFrame(FILE* file, AudioFrame* frame) { + return ReadFrame(file, frame, NULL); +} + +// If the end of the file has been reached, rewind it and attempt to read the +// frame again. +void ApmTest::ReadFrameWithRewind(FILE* file, AudioFrame* frame, + ChannelBuffer* cb) { + if (!ReadFrame(near_file_, frame_, cb)) { + rewind(near_file_); + ASSERT_TRUE(ReadFrame(near_file_, frame_, cb)); + } +} + +void ApmTest::ReadFrameWithRewind(FILE* file, AudioFrame* frame) { + ReadFrameWithRewind(file, frame, NULL); +} + void ApmTest::ProcessWithDefaultStreamParameters(AudioFrame* frame) { EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0)); apm_->echo_cancellation()->set_stream_drift_samples(0); @@ -449,12 +508,36 @@ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame)); } +int ApmTest::ProcessStreamChooser(Format format) { + if (format == kIntFormat) { + return apm_->ProcessStream(frame_); + } + return apm_->ProcessStream(float_cb_->channels(), + frame_->samples_per_channel_, + frame_->sample_rate_hz_, + LayoutFromChannels(frame_->num_channels_), + output_sample_rate_hz_, + LayoutFromChannels(num_output_channels_), + float_cb_->channels()); +} + +int ApmTest::AnalyzeReverseStreamChooser(Format format) { + if (format == kIntFormat) { + return apm_->AnalyzeReverseStream(revframe_); + } + return apm_->AnalyzeReverseStream( + revfloat_cb_->channels(), + revframe_->samples_per_channel_, + revframe_->sample_rate_hz_, + LayoutFromChannels(revframe_->num_channels_)); +} + void ApmTest::ProcessDelayVerificationTest(int delay_ms, int system_delay_ms, int delay_min, int delay_max) { // The |revframe_| and |frame_| should include the proper frame information, // hence can be used for extracting information. - webrtc::AudioFrame tmp_frame; - std::queue frame_queue; + AudioFrame tmp_frame; + std::queue frame_queue; bool causal = true; tmp_frame.CopyFrom(*revframe_); @@ -464,14 +547,14 @@ // Initialize the |frame_queue| with empty frames. int frame_delay = delay_ms / 10; while (frame_delay < 0) { - webrtc::AudioFrame* frame = new AudioFrame(); + AudioFrame* frame = new AudioFrame(); frame->CopyFrom(tmp_frame); frame_queue.push(frame); frame_delay++; causal = false; } while (frame_delay > 0) { - webrtc::AudioFrame* frame = new AudioFrame(); + AudioFrame* frame = new AudioFrame(); frame->CopyFrom(tmp_frame); frame_queue.push(frame); frame_delay--; @@ -481,13 +564,13 @@ // possible to keep processing time down. 4.5 seconds seemed to be a good // compromise for this recording. for (int frame_count = 0; frame_count < 450; ++frame_count) { - webrtc::AudioFrame* frame = new AudioFrame(); + AudioFrame* frame = new AudioFrame(); frame->CopyFrom(tmp_frame); // Use the near end recording, since that has more speech in it. ASSERT_TRUE(ReadFrame(near_file_, frame)); frame_queue.push(frame); - webrtc::AudioFrame* reverse_frame = frame; - webrtc::AudioFrame* process_frame = frame_queue.front(); + AudioFrame* reverse_frame = frame; + AudioFrame* process_frame = frame_queue.front(); if (!causal) { reverse_frame = frame_queue.front(); // When we call ProcessStream() the frame is modified, so we can't use the @@ -514,7 +597,7 @@ rewind(near_file_); while (!frame_queue.empty()) { - webrtc::AudioFrame* frame = frame_queue.front(); + AudioFrame* frame = frame_queue.front(); frame_queue.pop(); delete frame; } @@ -536,20 +619,21 @@ EXPECT_LE(expected_median_low, median); } -TEST_F(ApmTest, StreamParameters) { +void ApmTest::StreamParametersTest(Format format) { // No errors when the components are disabled. - EXPECT_EQ(apm_->kNoError, - apm_->ProcessStream(frame_)); + EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(format)); // -- Missing AGC level -- EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); - EXPECT_EQ(apm_->kStreamParameterNotSetError, apm_->ProcessStream(frame_)); + EXPECT_EQ(apm_->kStreamParameterNotSetError, + ProcessStreamChooser(format)); // Resets after successful ProcessStream(). EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_stream_analog_level(127)); - EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); - EXPECT_EQ(apm_->kStreamParameterNotSetError, apm_->ProcessStream(frame_)); + EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(format)); + EXPECT_EQ(apm_->kStreamParameterNotSetError, + ProcessStreamChooser(format)); // Other stream parameters set correctly. EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); @@ -558,20 +642,22 @@ EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); apm_->echo_cancellation()->set_stream_drift_samples(0); EXPECT_EQ(apm_->kStreamParameterNotSetError, - apm_->ProcessStream(frame_)); + ProcessStreamChooser(format)); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(false)); EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->enable_drift_compensation(false)); // -- Missing delay -- EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); - EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); - EXPECT_EQ(apm_->kStreamParameterNotSetError, apm_->ProcessStream(frame_)); + EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(format)); + EXPECT_EQ(apm_->kStreamParameterNotSetError, + ProcessStreamChooser(format)); // Resets after successful ProcessStream(). EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); - EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); - EXPECT_EQ(apm_->kStreamParameterNotSetError, apm_->ProcessStream(frame_)); + EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(format)); + EXPECT_EQ(apm_->kStreamParameterNotSetError, + ProcessStreamChooser(format)); // Other stream parameters set correctly. EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); @@ -580,37 +666,49 @@ apm_->echo_cancellation()->set_stream_drift_samples(0); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_stream_analog_level(127)); - EXPECT_EQ(apm_->kStreamParameterNotSetError, apm_->ProcessStream(frame_)); + EXPECT_EQ(apm_->kStreamParameterNotSetError, + ProcessStreamChooser(format)); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(false)); // -- Missing drift -- - EXPECT_EQ(apm_->kStreamParameterNotSetError, apm_->ProcessStream(frame_)); + EXPECT_EQ(apm_->kStreamParameterNotSetError, + ProcessStreamChooser(format)); // Resets after successful ProcessStream(). EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); apm_->echo_cancellation()->set_stream_drift_samples(0); - EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); - EXPECT_EQ(apm_->kStreamParameterNotSetError, apm_->ProcessStream(frame_)); + EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(format)); + EXPECT_EQ(apm_->kStreamParameterNotSetError, + ProcessStreamChooser(format)); // Other stream parameters set correctly. EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_stream_analog_level(127)); - EXPECT_EQ(apm_->kStreamParameterNotSetError, apm_->ProcessStream(frame_)); + EXPECT_EQ(apm_->kStreamParameterNotSetError, + ProcessStreamChooser(format)); // -- No stream parameters -- EXPECT_EQ(apm_->kNoError, - apm_->AnalyzeReverseStream(revframe_)); + AnalyzeReverseStreamChooser(format)); EXPECT_EQ(apm_->kStreamParameterNotSetError, - apm_->ProcessStream(frame_)); + ProcessStreamChooser(format)); // -- All there -- EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); apm_->echo_cancellation()->set_stream_drift_samples(0); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_stream_analog_level(127)); - EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); + EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(format)); +} + +TEST_F(ApmTest, StreamParametersInt) { + StreamParametersTest(kIntFormat); +} + +TEST_F(ApmTest, StreamParametersFloat) { + StreamParametersTest(kFloatFormat); } TEST_F(ApmTest, DefaultDelayOffsetIsZero) { @@ -652,20 +750,21 @@ for (int i = 1; i < 3; i++) { TestChangingChannels(i, kNoErr); EXPECT_EQ(i, apm_->num_input_channels()); - EXPECT_EQ(i, apm_->num_reverse_channels()); + // We always force the number of reverse channels used for processing to 1. + EXPECT_EQ(1, apm_->num_reverse_channels()); } } -TEST_F(ApmTest, SampleRates) { +TEST_F(ApmTest, SampleRatesInt) { // Testing invalid sample rates - SetFrameSampleRate(frame_, 10000); - EXPECT_EQ(apm_->kBadSampleRateError, apm_->ProcessStream(frame_)); + SetContainerFormat(10000, 2, frame_, &float_cb_); + EXPECT_EQ(apm_->kBadSampleRateError, ProcessStreamChooser(kIntFormat)); // Testing valid sample rates int fs[] = {8000, 16000, 32000}; for (size_t i = 0; i < sizeof(fs) / sizeof(*fs); i++) { - SetFrameSampleRate(frame_, fs[i]); - EXPECT_EQ(kNoErr, apm_->ProcessStream(frame_)); - EXPECT_EQ(fs[i], apm_->sample_rate_hz()); + SetContainerFormat(fs[i], 2, frame_, &float_cb_); + EXPECT_NOERR(ProcessStreamChooser(kIntFormat)); + EXPECT_EQ(fs[i], apm_->input_sample_rate_hz()); } } @@ -677,19 +776,6 @@ apm_->echo_cancellation()->enable_drift_compensation(false)); EXPECT_FALSE(apm_->echo_cancellation()->is_drift_compensation_enabled()); - EXPECT_EQ(apm_->kBadParameterError, - apm_->echo_cancellation()->set_device_sample_rate_hz(4000)); - EXPECT_EQ(apm_->kBadParameterError, - apm_->echo_cancellation()->set_device_sample_rate_hz(100000)); - - int rate[] = {16000, 44100, 48000}; - for (size_t i = 0; i < sizeof(rate)/sizeof(*rate); i++) { - EXPECT_EQ(apm_->kNoError, - apm_->echo_cancellation()->set_device_sample_rate_hz(rate[i])); - EXPECT_EQ(rate[i], - apm_->echo_cancellation()->device_sample_rate_hz()); - } - EchoCancellation::SuppressionLevel level[] = { EchoCancellation::kLowSuppression, EchoCancellation::kModerateSuppression, @@ -738,7 +824,7 @@ EXPECT_FALSE(apm_->echo_cancellation()->aec_core() != NULL); } -TEST_F(ApmTest, EchoCancellationReportsCorrectDelays) { +TEST_F(ApmTest, DISABLED_EchoCancellationReportsCorrectDelays) { // Enable AEC only. EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->enable_drift_compensation(false)); @@ -747,6 +833,9 @@ EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->enable_delay_logging(true)); EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); + Config config; + config.Set(new ReportedDelay(true)); + apm_->SetExtraOptions(config); // Internally in the AEC the amount of lookahead the delay estimation can // handle is 15 blocks and the maximum delay is set to 60 blocks. @@ -767,7 +856,13 @@ // within a valid region (set to +-1.5 blocks). Note that these cases are // sampling frequency dependent. for (size_t i = 0; i < kProcessSampleRatesSize; i++) { - Init(kProcessSampleRates[i], 2, 2, 2, false); + Init(kProcessSampleRates[i], + kProcessSampleRates[i], + kProcessSampleRates[i], + 2, + 2, + 2, + false); // Sampling frequency dependent variables. const int num_ms_per_block = std::max(4, 640 / frame_->samples_per_channel_); @@ -809,18 +904,18 @@ TEST_F(ApmTest, EchoControlMobile) { // AECM won't use super-wideband. SetFrameSampleRate(frame_, 32000); - EXPECT_EQ(kNoErr, apm_->ProcessStream(frame_)); + EXPECT_NOERR(apm_->ProcessStream(frame_)); EXPECT_EQ(apm_->kBadSampleRateError, apm_->echo_control_mobile()->Enable(true)); SetFrameSampleRate(frame_, 16000); - EXPECT_EQ(kNoErr, apm_->ProcessStream(frame_)); + EXPECT_NOERR(apm_->ProcessStream(frame_)); EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(true)); SetFrameSampleRate(frame_, 32000); EXPECT_EQ(apm_->kUnsupportedComponentError, apm_->ProcessStream(frame_)); // Turn AECM on (and AEC off) - Init(16000, 2, 2, 2, false); + Init(16000, 16000, 16000, 2, 2, 2, false); EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(true)); EXPECT_TRUE(apm_->echo_control_mobile()->is_enabled()); @@ -848,8 +943,8 @@ // Set and get echo path const size_t echo_path_size = apm_->echo_control_mobile()->echo_path_size_bytes(); - scoped_array echo_path_in(new char[echo_path_size]); - scoped_array echo_path_out(new char[echo_path_size]); + scoped_ptr echo_path_in(new char[echo_path_size]); + scoped_ptr echo_path_out(new char[echo_path_size]); EXPECT_EQ(apm_->kNullPointerError, apm_->echo_control_mobile()->SetEchoPath(NULL, echo_path_size)); EXPECT_EQ(apm_->kNullPointerError, @@ -982,6 +1077,82 @@ EXPECT_FALSE(apm_->gain_control()->is_enabled()); } +void ApmTest::RunQuantizedVolumeDoesNotGetStuckTest(int sample_rate) { + Init(sample_rate, sample_rate, sample_rate, 2, 2, 2, false); + EXPECT_EQ(apm_->kNoError, + apm_->gain_control()->set_mode(GainControl::kAdaptiveAnalog)); + EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); + + int out_analog_level = 0; + for (int i = 0; i < 2000; ++i) { + ReadFrameWithRewind(near_file_, frame_); + // Ensure the audio is at a low level, so the AGC will try to increase it. + ScaleFrame(frame_, 0.25); + + // Always pass in the same volume. + EXPECT_EQ(apm_->kNoError, + apm_->gain_control()->set_stream_analog_level(100)); + EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); + out_analog_level = apm_->gain_control()->stream_analog_level(); + } + + // Ensure the AGC is still able to reach the maximum. + EXPECT_EQ(255, out_analog_level); +} + +// Verifies that despite volume slider quantization, the AGC can continue to +// increase its volume. +TEST_F(ApmTest, QuantizedVolumeDoesNotGetStuck) { + for (size_t i = 0; i < kSampleRatesSize; ++i) { + RunQuantizedVolumeDoesNotGetStuckTest(kSampleRates[i]); + } +} + +void ApmTest::RunManualVolumeChangeIsPossibleTest(int sample_rate) { + Init(sample_rate, sample_rate, sample_rate, 2, 2, 2, false); + EXPECT_EQ(apm_->kNoError, + apm_->gain_control()->set_mode(GainControl::kAdaptiveAnalog)); + EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); + + int out_analog_level = 100; + for (int i = 0; i < 1000; ++i) { + ReadFrameWithRewind(near_file_, frame_); + // Ensure the audio is at a low level, so the AGC will try to increase it. + ScaleFrame(frame_, 0.25); + + EXPECT_EQ(apm_->kNoError, + apm_->gain_control()->set_stream_analog_level(out_analog_level)); + EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); + out_analog_level = apm_->gain_control()->stream_analog_level(); + } + + // Ensure the volume was raised. + EXPECT_GT(out_analog_level, 100); + int highest_level_reached = out_analog_level; + // Simulate a user manual volume change. + out_analog_level = 100; + + for (int i = 0; i < 300; ++i) { + ReadFrameWithRewind(near_file_, frame_); + ScaleFrame(frame_, 0.25); + + EXPECT_EQ(apm_->kNoError, + apm_->gain_control()->set_stream_analog_level(out_analog_level)); + EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); + out_analog_level = apm_->gain_control()->stream_analog_level(); + // Check that AGC respected the manually adjusted volume. + EXPECT_LT(out_analog_level, highest_level_reached); + } + // Check that the volume was still raised. + EXPECT_GT(out_analog_level, 100); +} + +TEST_F(ApmTest, ManualVolumeChangeIsPossible) { + for (size_t i = 0; i < kSampleRatesSize; ++i) { + RunManualVolumeChangeIsPossibleTest(kSampleRates[i]); + } +} + TEST_F(ApmTest, NoiseSuppression) { // Test valid suppression levels. NoiseSuppression::Level level[] = { @@ -1058,15 +1229,6 @@ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); EXPECT_EQ(70, apm_->level_estimator()->RMS()); - // Min value if energy_ == 0. - SetFrameTo(frame_, 10000); - uint32_t energy = frame_->energy_; // Save default to restore below. - frame_->energy_ = 0; - EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); - EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); - EXPECT_EQ(127, apm_->level_estimator()->RMS()); - frame_->energy_ = energy; - // Verify reset after enable/disable. SetFrameTo(frame_, 32767); EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); @@ -1160,7 +1322,7 @@ TEST_F(ApmTest, NoProcessingWhenAllComponentsDisabled) { for (size_t i = 0; i < kSampleRatesSize; i++) { - Init(kSampleRates[i], 2, 2, 2, false); + Init(kSampleRates[i], kSampleRates[i], kSampleRates[i], 2, 2, 2, false); SetFrameTo(frame_, 1000, 2000); AudioFrame frame_copy; frame_copy.CopyFrom(*frame_); @@ -1175,25 +1337,29 @@ EnableAllComponents(); for (size_t i = 0; i < kProcessSampleRatesSize; i++) { - Init(kProcessSampleRates[i], 2, 2, 2, false); + Init(kProcessSampleRates[i], + kProcessSampleRates[i], + kProcessSampleRates[i], + 2, + 2, + 2, + false); int analog_level = 127; - EXPECT_EQ(0, feof(far_file_)); - EXPECT_EQ(0, feof(near_file_)); - while (1) { - if (!ReadFrame(far_file_, revframe_)) break; + ASSERT_EQ(0, feof(far_file_)); + ASSERT_EQ(0, feof(near_file_)); + while (ReadFrame(far_file_, revframe_) && ReadFrame(near_file_, frame_)) { CopyLeftToRightChannel(revframe_->data_, revframe_->samples_per_channel_); - EXPECT_EQ(apm_->kNoError, apm_->AnalyzeReverseStream(revframe_)); + ASSERT_EQ(kNoErr, apm_->AnalyzeReverseStream(revframe_)); - if (!ReadFrame(near_file_, frame_)) break; CopyLeftToRightChannel(frame_->data_, frame_->samples_per_channel_); frame_->vad_activity_ = AudioFrame::kVadUnknown; - EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0)); + ASSERT_EQ(kNoErr, apm_->set_stream_delay_ms(0)); apm_->echo_cancellation()->set_stream_drift_samples(0); - EXPECT_EQ(apm_->kNoError, + ASSERT_EQ(kNoErr, apm_->gain_control()->set_stream_analog_level(analog_level)); - EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); + ASSERT_EQ(kNoErr, apm_->ProcessStream(frame_)); analog_level = apm_->gain_control()->stream_analog_level(); VerifyChannelsAreEqual(frame_->data_, frame_->samples_per_channel_); @@ -1251,6 +1417,11 @@ // TODO(andrew): This test, and the one below, rely rather tenuously on the // behavior of the AEC. Think of something more robust. EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); + // Make sure we have extended filter enabled. This makes sure nothing is + // touched until we have a farend frame. + Config config; + config.Set(new DelayCorrection(true)); + apm_->SetExtraOptions(config); SetFrameTo(frame_, 1000); frame_copy.CopyFrom(*frame_); EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0)); @@ -1274,9 +1445,148 @@ EXPECT_FALSE(FrameDataAreEqual(*frame_, frame_copy)); } +#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP +void ApmTest::ProcessDebugDump(const std::string& in_filename, + const std::string& out_filename, + Format format) { + FILE* in_file = fopen(in_filename.c_str(), "rb"); + ASSERT_TRUE(in_file != NULL); + audioproc::Event event_msg; + bool first_init = true; + + while (ReadMessageFromFile(in_file, &event_msg)) { + if (event_msg.type() == audioproc::Event::INIT) { + const audioproc::Init msg = event_msg.init(); + int reverse_sample_rate = msg.sample_rate(); + if (msg.has_reverse_sample_rate()) { + reverse_sample_rate = msg.reverse_sample_rate(); + } + int output_sample_rate = msg.sample_rate(); + if (msg.has_output_sample_rate()) { + output_sample_rate = msg.output_sample_rate(); + } + + Init(msg.sample_rate(), + output_sample_rate, + reverse_sample_rate, + msg.num_input_channels(), + msg.num_output_channels(), + msg.num_reverse_channels(), + false); + if (first_init) { + // StartDebugRecording() writes an additional init message. Don't start + // recording until after the first init to avoid the extra message. + EXPECT_NOERR(apm_->StartDebugRecording(out_filename.c_str())); + first_init = false; + } + + } else if (event_msg.type() == audioproc::Event::REVERSE_STREAM) { + const audioproc::ReverseStream msg = event_msg.reverse_stream(); + + if (msg.channel_size() > 0) { + ASSERT_EQ(revframe_->num_channels_, msg.channel_size()); + for (int i = 0; i < msg.channel_size(); ++i) { + memcpy(revfloat_cb_->channel(i), msg.channel(i).data(), + msg.channel(i).size()); + } + } else { + memcpy(revframe_->data_, msg.data().data(), msg.data().size()); + if (format == kFloatFormat) { + // We're using an int16 input file; convert to float. + ConvertToFloat(*revframe_, revfloat_cb_.get()); + } + } + AnalyzeReverseStreamChooser(format); + + } else if (event_msg.type() == audioproc::Event::STREAM) { + const audioproc::Stream msg = event_msg.stream(); + // ProcessStream could have changed this for the output frame. + frame_->num_channels_ = apm_->num_input_channels(); + + EXPECT_NOERR(apm_->gain_control()->set_stream_analog_level(msg.level())); + EXPECT_NOERR(apm_->set_stream_delay_ms(msg.delay())); + apm_->echo_cancellation()->set_stream_drift_samples(msg.drift()); + if (msg.has_keypress()) { + apm_->set_stream_key_pressed(msg.keypress()); + } else { + apm_->set_stream_key_pressed(true); + } + + if (msg.input_channel_size() > 0) { + ASSERT_EQ(frame_->num_channels_, msg.input_channel_size()); + for (int i = 0; i < msg.input_channel_size(); ++i) { + memcpy(float_cb_->channel(i), msg.input_channel(i).data(), + msg.input_channel(i).size()); + } + } else { + memcpy(frame_->data_, msg.input_data().data(), msg.input_data().size()); + if (format == kFloatFormat) { + // We're using an int16 input file; convert to float. + ConvertToFloat(*frame_, float_cb_.get()); + } + } + ProcessStreamChooser(format); + } + } + EXPECT_NOERR(apm_->StopDebugRecording()); + fclose(in_file); +} + +void ApmTest::VerifyDebugDumpTest(Format format) { + const std::string in_filename = test::ResourcePath("ref03", "aecdump"); + std::string format_string; + switch (format) { + case kIntFormat: + format_string = "_int"; + break; + case kFloatFormat: + format_string = "_float"; + break; + } + const std::string ref_filename = + test::OutputPath() + "ref" + format_string + ".aecdump"; + const std::string out_filename = + test::OutputPath() + "out" + format_string + ".aecdump"; + EnableAllComponents(); + ProcessDebugDump(in_filename, ref_filename, format); + ProcessDebugDump(ref_filename, out_filename, format); + + FILE* ref_file = fopen(ref_filename.c_str(), "rb"); + FILE* out_file = fopen(out_filename.c_str(), "rb"); + ASSERT_TRUE(ref_file != NULL); + ASSERT_TRUE(out_file != NULL); + scoped_ptr ref_bytes; + scoped_ptr out_bytes; + + size_t ref_size = ReadMessageBytesFromFile(ref_file, &ref_bytes); + size_t out_size = ReadMessageBytesFromFile(out_file, &out_bytes); + size_t bytes_read = 0; + while (ref_size > 0 && out_size > 0) { + bytes_read += ref_size; + EXPECT_EQ(ref_size, out_size); + EXPECT_EQ(0, memcmp(ref_bytes.get(), out_bytes.get(), ref_size)); + ref_size = ReadMessageBytesFromFile(ref_file, &ref_bytes); + out_size = ReadMessageBytesFromFile(out_file, &out_bytes); + } + EXPECT_GT(bytes_read, 0u); + EXPECT_NE(0, feof(ref_file)); + EXPECT_NE(0, feof(out_file)); + ASSERT_EQ(0, fclose(ref_file)); + ASSERT_EQ(0, fclose(out_file)); +} + +TEST_F(ApmTest, VerifyDebugDumpInt) { + VerifyDebugDumpTest(kIntFormat); +} + +TEST_F(ApmTest, VerifyDebugDumpFloat) { + VerifyDebugDumpTest(kFloatFormat); +} +#endif + // TODO(andrew): expand test to verify output. TEST_F(ApmTest, DebugDump) { - const std::string filename = webrtc::test::OutputPath() + "debug.aec"; + const std::string filename = test::OutputPath() + "debug.aec"; EXPECT_EQ(apm_->kNullPointerError, apm_->StartDebugRecording(static_cast(NULL))); @@ -1310,7 +1620,7 @@ TEST_F(ApmTest, DebugDumpFromFileHandle) { FILE* fid = NULL; EXPECT_EQ(apm_->kNullPointerError, apm_->StartDebugRecording(fid)); - const std::string filename = webrtc::test::OutputPath() + "debug.aec"; + const std::string filename = test::OutputPath() + "debug.aec"; fid = fopen(filename.c_str(), "w"); ASSERT_TRUE(fid); @@ -1339,24 +1649,122 @@ #endif // WEBRTC_AUDIOPROC_DEBUG_DUMP } +TEST_F(ApmTest, FloatAndIntInterfacesGiveSimilarResults) { + audioproc::OutputData ref_data; + OpenFileAndReadMessage(ref_filename_, &ref_data); + + Config config; + config.Set(new ExperimentalAgc(false)); + scoped_ptr fapm(AudioProcessing::Create(config)); + EnableAllComponents(); + EnableAllAPComponents(fapm.get()); + for (int i = 0; i < ref_data.test_size(); i++) { + printf("Running test %d of %d...\n", i + 1, ref_data.test_size()); + + audioproc::Test* test = ref_data.mutable_test(i); + // TODO(ajm): Restore downmixing test cases. + if (test->num_input_channels() != test->num_output_channels()) + continue; + + const int num_render_channels = test->num_reverse_channels(); + const int num_input_channels = test->num_input_channels(); + const int num_output_channels = test->num_output_channels(); + const int samples_per_channel = test->sample_rate() * + AudioProcessing::kChunkSizeMs / 1000; + const int output_length = samples_per_channel * num_output_channels; + + Init(test->sample_rate(), test->sample_rate(), test->sample_rate(), + num_input_channels, num_output_channels, num_render_channels, true); + Init(fapm.get()); + + ChannelBuffer output_cb(samples_per_channel, num_input_channels); + ChannelBuffer output_int16(samples_per_channel, + num_input_channels); + + int analog_level = 127; + while (ReadFrame(far_file_, revframe_, revfloat_cb_.get()) && + ReadFrame(near_file_, frame_, float_cb_.get())) { + frame_->vad_activity_ = AudioFrame::kVadUnknown; + + EXPECT_NOERR(apm_->AnalyzeReverseStream(revframe_)); + EXPECT_NOERR(fapm->AnalyzeReverseStream( + revfloat_cb_->channels(), + samples_per_channel, + test->sample_rate(), + LayoutFromChannels(num_render_channels))); + + EXPECT_NOERR(apm_->set_stream_delay_ms(0)); + EXPECT_NOERR(fapm->set_stream_delay_ms(0)); + apm_->echo_cancellation()->set_stream_drift_samples(0); + fapm->echo_cancellation()->set_stream_drift_samples(0); + EXPECT_NOERR(apm_->gain_control()->set_stream_analog_level(analog_level)); + EXPECT_NOERR(fapm->gain_control()->set_stream_analog_level(analog_level)); + + EXPECT_NOERR(apm_->ProcessStream(frame_)); + Deinterleave(frame_->data_, samples_per_channel, num_output_channels, + output_int16.channels()); + + EXPECT_NOERR(fapm->ProcessStream( + float_cb_->channels(), + samples_per_channel, + test->sample_rate(), + LayoutFromChannels(num_input_channels), + test->sample_rate(), + LayoutFromChannels(num_output_channels), + float_cb_->channels())); + + FloatToS16(float_cb_->data(), output_length, output_cb.data()); + for (int j = 0; j < num_output_channels; ++j) { + float variance = 0; + float snr = ComputeSNR(output_int16.channel(j), output_cb.channel(j), + samples_per_channel, &variance); + #if defined(WEBRTC_AUDIOPROC_FIXED_PROFILE) + // There are a few chunks in the fixed-point profile that give low SNR. + // Listening confirmed the difference is acceptable. + const float kVarianceThreshold = 150; + const float kSNRThreshold = 10; + #else + const float kVarianceThreshold = 20; + const float kSNRThreshold = 20; + #endif + // Skip frames with low energy. + if (sqrt(variance) > kVarianceThreshold) { + EXPECT_LT(kSNRThreshold, snr); + } + } + + analog_level = fapm->gain_control()->stream_analog_level(); + EXPECT_EQ(apm_->gain_control()->stream_analog_level(), + fapm->gain_control()->stream_analog_level()); + EXPECT_EQ(apm_->echo_cancellation()->stream_has_echo(), + fapm->echo_cancellation()->stream_has_echo()); + EXPECT_NEAR(apm_->noise_suppression()->speech_probability(), + fapm->noise_suppression()->speech_probability(), + 0.0005); + + // Reset in case of downmixing. + frame_->num_channels_ = test->num_input_channels(); + } + rewind(far_file_); + rewind(near_file_); + } +} + // TODO(andrew): Add a test to process a few frames with different combinations // of enabled components. -// TODO(andrew): Make this test more robust such that it can be run on multiple -// platforms. It currently requires bit-exactness. -#ifdef WEBRTC_AUDIOPROC_BIT_EXACT -TEST_F(ApmTest, DISABLED_ON_ANDROID(Process)) { +TEST_F(ApmTest, Process) { GOOGLE_PROTOBUF_VERIFY_VERSION; - webrtc::audioproc::OutputData ref_data; + audioproc::OutputData ref_data; if (!write_ref_data) { - ReadMessageLiteFromFile(ref_filename_, &ref_data); + OpenFileAndReadMessage(ref_filename_, &ref_data); } else { // Write the desired tests to the protobuf reference file. for (size_t i = 0; i < kChannelsSize; i++) { for (size_t j = 0; j < kChannelsSize; j++) { for (size_t l = 0; l < kProcessSampleRatesSize; l++) { - webrtc::audioproc::Test* test = ref_data.add_test(); + audioproc::Test* test = ref_data.add_test(); test->set_num_reverse_channels(kChannels[i]); test->set_num_input_channels(kChannels[j]); test->set_num_output_channels(kChannels[j]); @@ -1371,14 +1779,19 @@ for (int i = 0; i < ref_data.test_size(); i++) { printf("Running test %d of %d...\n", i + 1, ref_data.test_size()); - webrtc::audioproc::Test* test = ref_data.mutable_test(i); + audioproc::Test* test = ref_data.mutable_test(i); // TODO(ajm): We no longer allow different input and output channels. Skip // these tests for now, but they should be removed from the set. if (test->num_input_channels() != test->num_output_channels()) continue; - Init(test->sample_rate(), test->num_reverse_channels(), - test->num_input_channels(), test->num_output_channels(), true); + Init(test->sample_rate(), + test->sample_rate(), + test->sample_rate(), + test->num_input_channels(), + test->num_output_channels(), + test->num_reverse_channels(), + true); int frame_count = 0; int has_echo_count = 0; @@ -1389,11 +1802,9 @@ int max_output_average = 0; float ns_speech_prob_average = 0.0f; - while (1) { - if (!ReadFrame(far_file_, revframe_)) break; + while (ReadFrame(far_file_, revframe_) && ReadFrame(near_file_, frame_)) { EXPECT_EQ(apm_->kNoError, apm_->AnalyzeReverseStream(revframe_)); - if (!ReadFrame(near_file_, frame_)) break; frame_->vad_activity_ = AudioFrame::kVadUnknown; EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0)); @@ -1402,6 +1813,7 @@ apm_->gain_control()->set_stream_analog_level(analog_level)); EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); + // Ensure the frame was downmixed properly. EXPECT_EQ(test->num_output_channels(), frame_->num_channels_); @@ -1455,16 +1867,37 @@ #endif if (!write_ref_data) { - EXPECT_EQ(test->has_echo_count(), has_echo_count); - EXPECT_EQ(test->has_voice_count(), has_voice_count); - EXPECT_EQ(test->is_saturated_count(), is_saturated_count); - - EXPECT_EQ(test->analog_level_average(), analog_level_average); - EXPECT_EQ(test->max_output_average(), max_output_average); + const int kIntNear = 1; + // When running the test on a N7 we get a {2, 6} difference of + // |has_voice_count| and |max_output_average| is up to 18 higher. + // All numbers being consistently higher on N7 compare to ref_data. + // TODO(bjornv): If we start getting more of these offsets on Android we + // should consider a different approach. Either using one slack for all, + // or generate a separate android reference. +#if defined(WEBRTC_ANDROID) + const int kHasVoiceCountOffset = 3; + const int kHasVoiceCountNear = 3; + const int kMaxOutputAverageOffset = 9; + const int kMaxOutputAverageNear = 9; +#else + const int kHasVoiceCountOffset = 0; + const int kHasVoiceCountNear = kIntNear; + const int kMaxOutputAverageOffset = 0; + const int kMaxOutputAverageNear = kIntNear; +#endif + EXPECT_NEAR(test->has_echo_count(), has_echo_count, kIntNear); + EXPECT_NEAR(test->has_voice_count(), + has_voice_count - kHasVoiceCountOffset, + kHasVoiceCountNear); + EXPECT_NEAR(test->is_saturated_count(), is_saturated_count, kIntNear); + + EXPECT_NEAR(test->analog_level_average(), analog_level_average, kIntNear); + EXPECT_NEAR(test->max_output_average(), + max_output_average - kMaxOutputAverageOffset, + kMaxOutputAverageNear); #if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) - webrtc::audioproc::Test::EchoMetrics reference = - test->echo_metrics(); + audioproc::Test::EchoMetrics reference = test->echo_metrics(); TestStats(echo_metrics.residual_echo_return_loss, reference.residual_echo_return_loss()); TestStats(echo_metrics.echo_return_loss, @@ -1474,15 +1907,16 @@ TestStats(echo_metrics.a_nlp, reference.a_nlp()); - webrtc::audioproc::Test::DelayMetrics reference_delay = - test->delay_metrics(); - EXPECT_EQ(reference_delay.median(), median); - EXPECT_EQ(reference_delay.std(), std); - - EXPECT_EQ(test->rms_level(), rms_level); - - EXPECT_FLOAT_EQ(test->ns_speech_probability_average(), - ns_speech_prob_average); + const double kFloatNear = 0.0005; + audioproc::Test::DelayMetrics reference_delay = test->delay_metrics(); + EXPECT_NEAR(reference_delay.median(), median, kIntNear); + EXPECT_NEAR(reference_delay.std(), std, kIntNear); + + EXPECT_NEAR(test->rms_level(), rms_level, kIntNear); + + EXPECT_NEAR(test->ns_speech_probability_average(), + ns_speech_prob_average, + kFloatNear); #endif } else { test->set_has_echo_count(has_echo_count); @@ -1493,8 +1927,7 @@ test->set_max_output_average(max_output_average); #if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) - webrtc::audioproc::Test::EchoMetrics* message = - test->mutable_echo_metrics(); + audioproc::Test::EchoMetrics* message = test->mutable_echo_metrics(); WriteStatsMessage(echo_metrics.residual_echo_return_loss, message->mutable_residual_echo_return_loss()); WriteStatsMessage(echo_metrics.echo_return_loss, @@ -1504,7 +1937,7 @@ WriteStatsMessage(echo_metrics.a_nlp, message->mutable_a_nlp()); - webrtc::audioproc::Test::DelayMetrics* message_delay = + audioproc::Test::DelayMetrics* message_delay = test->mutable_delay_metrics(); message_delay->set_median(median); message_delay->set_std(std); @@ -1522,13 +1955,502 @@ } if (write_ref_data) { - WriteMessageLiteToFile(ref_filename_, ref_data); + OpenFileAndWriteMessage(ref_filename_, ref_data); + } +} + +TEST_F(ApmTest, NoErrorsWithKeyboardChannel) { + struct ChannelFormat { + AudioProcessing::ChannelLayout in_layout; + AudioProcessing::ChannelLayout out_layout; + }; + ChannelFormat cf[] = { + {AudioProcessing::kMonoAndKeyboard, AudioProcessing::kMono}, + {AudioProcessing::kStereoAndKeyboard, AudioProcessing::kMono}, + {AudioProcessing::kStereoAndKeyboard, AudioProcessing::kStereo}, + }; + size_t channel_format_size = sizeof(cf) / sizeof(*cf); + + scoped_ptr ap(AudioProcessing::Create()); + // Enable one component just to ensure some processing takes place. + ap->noise_suppression()->Enable(true); + for (size_t i = 0; i < channel_format_size; ++i) { + const int in_rate = 44100; + const int out_rate = 48000; + ChannelBuffer in_cb(SamplesFromRate(in_rate), + TotalChannelsFromLayout(cf[i].in_layout)); + ChannelBuffer out_cb(SamplesFromRate(out_rate), + ChannelsFromLayout(cf[i].out_layout)); + + // Run over a few chunks. + for (int j = 0; j < 10; ++j) { + EXPECT_NOERR(ap->ProcessStream( + in_cb.channels(), + in_cb.samples_per_channel(), + in_rate, + cf[i].in_layout, + out_rate, + cf[i].out_layout, + out_cb.channels())); + } + } +} + +// Reads a 10 ms chunk of int16 interleaved audio from the given (assumed +// stereo) file, converts to deinterleaved float (optionally downmixing) and +// returns the result in |cb|. Returns false if the file ended (or on error) and +// true otherwise. +// +// |int_data| and |float_data| are just temporary space that must be +// sufficiently large to hold the 10 ms chunk. +bool ReadChunk(FILE* file, int16_t* int_data, float* float_data, + ChannelBuffer* cb) { + // The files always contain stereo audio. + size_t frame_size = cb->samples_per_channel() * 2; + size_t read_count = fread(int_data, sizeof(int16_t), frame_size, file); + if (read_count != frame_size) { + // Check that the file really ended. + assert(feof(file)); + return false; // This is expected. + } + + S16ToFloat(int_data, frame_size, float_data); + if (cb->num_channels() == 1) { + MixStereoToMono(float_data, cb->data(), cb->samples_per_channel()); + } else { + Deinterleave(float_data, cb->samples_per_channel(), 2, + cb->channels()); + } + + return true; +} + +// Compares the reference and test arrays over a region around the expected +// delay. Finds the highest SNR in that region and adds the variance and squared +// error results to the supplied accumulators. +void UpdateBestSNR(const float* ref, + const float* test, + int length, + int expected_delay, + double* variance_acc, + double* sq_error_acc) { + double best_snr = std::numeric_limits::min(); + double best_variance = 0; + double best_sq_error = 0; + // Search over a region of eight samples around the expected delay. + for (int delay = std::max(expected_delay - 4, 0); delay <= expected_delay + 4; + ++delay) { + double sq_error = 0; + double variance = 0; + for (int i = 0; i < length - delay; ++i) { + double error = test[i + delay] - ref[i]; + sq_error += error * error; + variance += ref[i] * ref[i]; + } + + if (sq_error == 0) { + *variance_acc += variance; + return; + } + double snr = variance / sq_error; + if (snr > best_snr) { + best_snr = snr; + best_variance = variance; + best_sq_error = sq_error; + } + } + + *variance_acc += best_variance; + *sq_error_acc += best_sq_error; +} + +// Used to test a multitude of sample rate and channel combinations. It works +// by first producing a set of reference files (in SetUpTestCase) that are +// assumed to be correct, as the used parameters are verified by other tests +// in this collection. Primarily the reference files are all produced at +// "native" rates which do not involve any resampling. + +// Each test pass produces an output file with a particular format. The output +// is matched against the reference file closest to its internal processing +// format. If necessary the output is resampled back to its process format. +// Due to the resampling distortion, we don't expect identical results, but +// enforce SNR thresholds which vary depending on the format. 0 is a special +// case SNR which corresponds to inf, or zero error. +typedef std::tr1::tuple AudioProcessingTestData; +class AudioProcessingTest + : public testing::TestWithParam { + public: + AudioProcessingTest() + : input_rate_(std::tr1::get<0>(GetParam())), + output_rate_(std::tr1::get<1>(GetParam())), + reverse_rate_(std::tr1::get<2>(GetParam())), + expected_snr_(std::tr1::get<3>(GetParam())) {} + + virtual ~AudioProcessingTest() {} + + static void SetUpTestCase() { + // Create all needed output reference files. + const int kNativeRates[] = {8000, 16000, 32000}; + const size_t kNativeRatesSize = + sizeof(kNativeRates) / sizeof(*kNativeRates); + const int kNumChannels[] = {1, 2}; + const size_t kNumChannelsSize = + sizeof(kNumChannels) / sizeof(*kNumChannels); + for (size_t i = 0; i < kNativeRatesSize; ++i) { + for (size_t j = 0; j < kNumChannelsSize; ++j) { + for (size_t k = 0; k < kNumChannelsSize; ++k) { + // The reference files always have matching input and output channels. + ProcessFormat(kNativeRates[i], + kNativeRates[i], + kNativeRates[i], + kNumChannels[j], + kNumChannels[j], + kNumChannels[k], + "ref"); + } + } + } + } + + // Runs a process pass on files with the given parameters and dumps the output + // to a file specified with |output_file_prefix|. + static void ProcessFormat(int input_rate, + int output_rate, + int reverse_rate, + int num_input_channels, + int num_output_channels, + int num_reverse_channels, + std::string output_file_prefix) { + Config config; + config.Set(new ExperimentalAgc(false)); + scoped_ptr ap(AudioProcessing::Create(config)); + EnableAllAPComponents(ap.get()); + ap->Initialize(input_rate, + output_rate, + reverse_rate, + LayoutFromChannels(num_input_channels), + LayoutFromChannels(num_output_channels), + LayoutFromChannels(num_reverse_channels)); + + FILE* far_file = fopen(ResourceFilePath("far", reverse_rate).c_str(), "rb"); + FILE* near_file = fopen(ResourceFilePath("near", input_rate).c_str(), "rb"); + FILE* out_file = fopen(OutputFilePath(output_file_prefix, + input_rate, + output_rate, + reverse_rate, + num_input_channels, + num_output_channels, + num_reverse_channels).c_str(), "wb"); + ASSERT_TRUE(far_file != NULL); + ASSERT_TRUE(near_file != NULL); + ASSERT_TRUE(out_file != NULL); + + ChannelBuffer fwd_cb(SamplesFromRate(input_rate), + num_input_channels); + ChannelBuffer rev_cb(SamplesFromRate(reverse_rate), + num_reverse_channels); + ChannelBuffer out_cb(SamplesFromRate(output_rate), + num_output_channels); + + // Temporary buffers. + const int max_length = + 2 * std::max(out_cb.samples_per_channel(), + std::max(fwd_cb.samples_per_channel(), + rev_cb.samples_per_channel())); + scoped_ptr float_data(new float[max_length]); + scoped_ptr int_data(new int16_t[max_length]); + + int analog_level = 127; + while (ReadChunk(far_file, int_data.get(), float_data.get(), &rev_cb) && + ReadChunk(near_file, int_data.get(), float_data.get(), &fwd_cb)) { + EXPECT_NOERR(ap->AnalyzeReverseStream( + rev_cb.channels(), + rev_cb.samples_per_channel(), + reverse_rate, + LayoutFromChannels(num_reverse_channels))); + + EXPECT_NOERR(ap->set_stream_delay_ms(0)); + ap->echo_cancellation()->set_stream_drift_samples(0); + EXPECT_NOERR(ap->gain_control()->set_stream_analog_level(analog_level)); + + EXPECT_NOERR(ap->ProcessStream( + fwd_cb.channels(), + fwd_cb.samples_per_channel(), + input_rate, + LayoutFromChannels(num_input_channels), + output_rate, + LayoutFromChannels(num_output_channels), + out_cb.channels())); + + Interleave(out_cb.channels(), + out_cb.samples_per_channel(), + out_cb.num_channels(), + float_data.get()); + // Dump output to file. + ASSERT_EQ(static_cast(out_cb.length()), + fwrite(float_data.get(), sizeof(float_data[0]), + out_cb.length(), out_file)); + + analog_level = ap->gain_control()->stream_analog_level(); + } + fclose(far_file); + fclose(near_file); + fclose(out_file); + } + + protected: + int input_rate_; + int output_rate_; + int reverse_rate_; + double expected_snr_; +}; + +TEST_P(AudioProcessingTest, Formats) { + struct ChannelFormat { + int num_input; + int num_output; + int num_reverse; + }; + ChannelFormat cf[] = { + {1, 1, 1}, + {1, 1, 2}, + {2, 1, 1}, + {2, 1, 2}, + {2, 2, 1}, + {2, 2, 2}, + }; + size_t channel_format_size = sizeof(cf) / sizeof(*cf); + + for (size_t i = 0; i < channel_format_size; ++i) { + ProcessFormat(input_rate_, + output_rate_, + reverse_rate_, + cf[i].num_input, + cf[i].num_output, + cf[i].num_reverse, + "out"); + int min_ref_rate = std::min(input_rate_, output_rate_); + int ref_rate; + if (min_ref_rate > 16000) { + ref_rate = 32000; + } else if (min_ref_rate > 8000) { + ref_rate = 16000; + } else { + ref_rate = 8000; + } +#ifdef WEBRTC_AUDIOPROC_FIXED_PROFILE + ref_rate = std::min(ref_rate, 16000); +#endif + + FILE* out_file = fopen(OutputFilePath("out", + input_rate_, + output_rate_, + reverse_rate_, + cf[i].num_input, + cf[i].num_output, + cf[i].num_reverse).c_str(), "rb"); + // The reference files always have matching input and output channels. + FILE* ref_file = fopen(OutputFilePath("ref", + ref_rate, + ref_rate, + ref_rate, + cf[i].num_output, + cf[i].num_output, + cf[i].num_reverse).c_str(), "rb"); + ASSERT_TRUE(out_file != NULL); + ASSERT_TRUE(ref_file != NULL); + + const int ref_length = SamplesFromRate(ref_rate) * cf[i].num_output; + const int out_length = SamplesFromRate(output_rate_) * cf[i].num_output; + // Data from the reference file. + scoped_ptr ref_data(new float[ref_length]); + // Data from the output file. + scoped_ptr out_data(new float[out_length]); + // Data from the resampled output, in case the reference and output rates + // don't match. + scoped_ptr cmp_data(new float[ref_length]); + + PushResampler resampler; + resampler.InitializeIfNeeded(output_rate_, ref_rate, cf[i].num_output); + + // Compute the resampling delay of the output relative to the reference, + // to find the region over which we should search for the best SNR. + float expected_delay_sec = 0; + if (input_rate_ != ref_rate) { + // Input resampling delay. + expected_delay_sec += + PushSincResampler::AlgorithmicDelaySeconds(input_rate_); + } + if (output_rate_ != ref_rate) { + // Output resampling delay. + expected_delay_sec += + PushSincResampler::AlgorithmicDelaySeconds(ref_rate); + // Delay of converting the output back to its processing rate for testing. + expected_delay_sec += + PushSincResampler::AlgorithmicDelaySeconds(output_rate_); + } + int expected_delay = floor(expected_delay_sec * ref_rate + 0.5f) * + cf[i].num_output; + + double variance = 0; + double sq_error = 0; + while (fread(out_data.get(), sizeof(out_data[0]), out_length, out_file) && + fread(ref_data.get(), sizeof(ref_data[0]), ref_length, ref_file)) { + float* out_ptr = out_data.get(); + if (output_rate_ != ref_rate) { + // Resample the output back to its internal processing rate if necssary. + ASSERT_EQ(ref_length, resampler.Resample(out_ptr, + out_length, + cmp_data.get(), + ref_length)); + out_ptr = cmp_data.get(); + } + + // Update the |sq_error| and |variance| accumulators with the highest SNR + // of reference vs output. + UpdateBestSNR(ref_data.get(), + out_ptr, + ref_length, + expected_delay, + &variance, + &sq_error); + } + + std::cout << "(" << input_rate_ << ", " + << output_rate_ << ", " + << reverse_rate_ << ", " + << cf[i].num_input << ", " + << cf[i].num_output << ", " + << cf[i].num_reverse << "): "; + if (sq_error > 0) { + double snr = 10 * log10(variance / sq_error); + EXPECT_GE(snr, expected_snr_); + EXPECT_NE(0, expected_snr_); + std::cout << "SNR=" << snr << " dB" << std::endl; + } else { + EXPECT_EQ(expected_snr_, 0); + std::cout << "SNR=" << "inf dB" << std::endl; + } + + fclose(out_file); + fclose(ref_file); } } -#endif // WEBRTC_AUDIOPROC_BIT_EXACT + +#if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) +INSTANTIATE_TEST_CASE_P( + CommonFormats, AudioProcessingTest, testing::Values( + std::tr1::make_tuple(48000, 48000, 48000, 20), + std::tr1::make_tuple(48000, 48000, 32000, 20), + std::tr1::make_tuple(48000, 48000, 16000, 20), + std::tr1::make_tuple(48000, 44100, 48000, 15), + std::tr1::make_tuple(48000, 44100, 32000, 15), + std::tr1::make_tuple(48000, 44100, 16000, 15), + std::tr1::make_tuple(48000, 32000, 48000, 20), + std::tr1::make_tuple(48000, 32000, 32000, 20), + std::tr1::make_tuple(48000, 32000, 16000, 20), + std::tr1::make_tuple(48000, 16000, 48000, 20), + std::tr1::make_tuple(48000, 16000, 32000, 20), + std::tr1::make_tuple(48000, 16000, 16000, 20), + + std::tr1::make_tuple(44100, 48000, 48000, 20), + std::tr1::make_tuple(44100, 48000, 32000, 20), + std::tr1::make_tuple(44100, 48000, 16000, 20), + std::tr1::make_tuple(44100, 44100, 48000, 15), + std::tr1::make_tuple(44100, 44100, 32000, 15), + std::tr1::make_tuple(44100, 44100, 16000, 15), + std::tr1::make_tuple(44100, 32000, 48000, 20), + std::tr1::make_tuple(44100, 32000, 32000, 20), + std::tr1::make_tuple(44100, 32000, 16000, 20), + std::tr1::make_tuple(44100, 16000, 48000, 20), + std::tr1::make_tuple(44100, 16000, 32000, 20), + std::tr1::make_tuple(44100, 16000, 16000, 20), + + std::tr1::make_tuple(32000, 48000, 48000, 25), + std::tr1::make_tuple(32000, 48000, 32000, 25), + std::tr1::make_tuple(32000, 48000, 16000, 25), + std::tr1::make_tuple(32000, 44100, 48000, 20), + std::tr1::make_tuple(32000, 44100, 32000, 20), + std::tr1::make_tuple(32000, 44100, 16000, 20), + std::tr1::make_tuple(32000, 32000, 48000, 30), + std::tr1::make_tuple(32000, 32000, 32000, 0), + std::tr1::make_tuple(32000, 32000, 16000, 30), + std::tr1::make_tuple(32000, 16000, 48000, 20), + std::tr1::make_tuple(32000, 16000, 32000, 20), + std::tr1::make_tuple(32000, 16000, 16000, 20), + + std::tr1::make_tuple(16000, 48000, 48000, 25), + std::tr1::make_tuple(16000, 48000, 32000, 25), + std::tr1::make_tuple(16000, 48000, 16000, 25), + std::tr1::make_tuple(16000, 44100, 48000, 15), + std::tr1::make_tuple(16000, 44100, 32000, 15), + std::tr1::make_tuple(16000, 44100, 16000, 15), + std::tr1::make_tuple(16000, 32000, 48000, 25), + std::tr1::make_tuple(16000, 32000, 32000, 25), + std::tr1::make_tuple(16000, 32000, 16000, 25), + std::tr1::make_tuple(16000, 16000, 48000, 30), + std::tr1::make_tuple(16000, 16000, 32000, 30), + std::tr1::make_tuple(16000, 16000, 16000, 0))); + +#elif defined(WEBRTC_AUDIOPROC_FIXED_PROFILE) +INSTANTIATE_TEST_CASE_P( + CommonFormats, AudioProcessingTest, testing::Values( + std::tr1::make_tuple(48000, 48000, 48000, 20), + std::tr1::make_tuple(48000, 48000, 32000, 20), + std::tr1::make_tuple(48000, 48000, 16000, 20), + std::tr1::make_tuple(48000, 44100, 48000, 15), + std::tr1::make_tuple(48000, 44100, 32000, 15), + std::tr1::make_tuple(48000, 44100, 16000, 15), + std::tr1::make_tuple(48000, 32000, 48000, 20), + std::tr1::make_tuple(48000, 32000, 32000, 20), + std::tr1::make_tuple(48000, 32000, 16000, 20), + std::tr1::make_tuple(48000, 16000, 48000, 20), + std::tr1::make_tuple(48000, 16000, 32000, 20), + std::tr1::make_tuple(48000, 16000, 16000, 20), + + std::tr1::make_tuple(44100, 48000, 48000, 19), + std::tr1::make_tuple(44100, 48000, 32000, 19), + std::tr1::make_tuple(44100, 48000, 16000, 19), + std::tr1::make_tuple(44100, 44100, 48000, 15), + std::tr1::make_tuple(44100, 44100, 32000, 15), + std::tr1::make_tuple(44100, 44100, 16000, 15), + std::tr1::make_tuple(44100, 32000, 48000, 19), + std::tr1::make_tuple(44100, 32000, 32000, 19), + std::tr1::make_tuple(44100, 32000, 16000, 19), + std::tr1::make_tuple(44100, 16000, 48000, 19), + std::tr1::make_tuple(44100, 16000, 32000, 19), + std::tr1::make_tuple(44100, 16000, 16000, 19), + + std::tr1::make_tuple(32000, 48000, 48000, 19), + std::tr1::make_tuple(32000, 48000, 32000, 19), + std::tr1::make_tuple(32000, 48000, 16000, 19), + std::tr1::make_tuple(32000, 44100, 48000, 15), + std::tr1::make_tuple(32000, 44100, 32000, 15), + std::tr1::make_tuple(32000, 44100, 16000, 15), + std::tr1::make_tuple(32000, 32000, 48000, 19), + std::tr1::make_tuple(32000, 32000, 32000, 19), + std::tr1::make_tuple(32000, 32000, 16000, 19), + std::tr1::make_tuple(32000, 16000, 48000, 19), + std::tr1::make_tuple(32000, 16000, 32000, 19), + std::tr1::make_tuple(32000, 16000, 16000, 19), + + std::tr1::make_tuple(16000, 48000, 48000, 25), + std::tr1::make_tuple(16000, 48000, 32000, 25), + std::tr1::make_tuple(16000, 48000, 16000, 25), + std::tr1::make_tuple(16000, 44100, 48000, 15), + std::tr1::make_tuple(16000, 44100, 32000, 15), + std::tr1::make_tuple(16000, 44100, 16000, 15), + std::tr1::make_tuple(16000, 32000, 48000, 25), + std::tr1::make_tuple(16000, 32000, 32000, 25), + std::tr1::make_tuple(16000, 32000, 16000, 25), + std::tr1::make_tuple(16000, 16000, 48000, 30), + std::tr1::make_tuple(16000, 16000, 32000, 30), + std::tr1::make_tuple(16000, 16000, 16000, 0))); +#endif // TODO(henrike): re-implement functionality lost when removing the old main // function. See // https://code.google.com/p/webrtc/issues/detail?id=1981 } // namespace +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/process_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/process_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/process_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/process_test.cc 2015-02-03 14:33:35.000000000 +0000 @@ -19,6 +19,7 @@ #include "webrtc/common.h" #include "webrtc/modules/audio_processing/include/audio_processing.h" +#include "webrtc/modules/audio_processing/test/test_utils.h" #include "webrtc/modules/interface/module_common_types.h" #include "webrtc/system_wrappers/interface/cpu_features_wrapper.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" @@ -33,18 +34,7 @@ #include "webrtc/audio_processing/debug.pb.h" #endif -using webrtc::AudioFrame; -using webrtc::AudioProcessing; -using webrtc::Config; -using webrtc::DelayCorrection; -using webrtc::EchoCancellation; -using webrtc::GainControl; -using webrtc::NoiseSuppression; -using webrtc::scoped_array; -using webrtc::scoped_ptr; -using webrtc::TickInterval; -using webrtc::TickTime; -using webrtc::VoiceDetection; +namespace webrtc { using webrtc::audioproc::Event; using webrtc::audioproc::Init; @@ -52,28 +42,6 @@ using webrtc::audioproc::Stream; namespace { -// Returns true on success, false on error or end-of-file. -bool ReadMessageFromFile(FILE* file, - ::google::protobuf::MessageLite* msg) { - // The "wire format" for the size is little-endian. - // Assume process_test is running on a little-endian machine. - int32_t size = 0; - if (fread(&size, sizeof(int32_t), 1, file) != 1) { - return false; - } - if (size <= 0) { - return false; - } - const size_t usize = static_cast(size); - - scoped_array array(new char[usize]); - if (fread(array.get(), sizeof(char), usize, file) != usize) { - return false; - } - - msg->Clear(); - return msg->ParseFromArray(array.get(), usize); -} void PrintStat(const AudioProcessing::Statistic& stat) { printf("%d, %d, %d\n", stat.average, @@ -87,11 +55,11 @@ " [-ir REVERSE_FILE] [-i PRIMARY_FILE] [-o OUT_FILE]\n"); printf( "process_test is a test application for AudioProcessing.\n\n" - "When a protobuf debug file is available, specify it with -pb.\n" - "Alternately, when -ir or -i is used, the specified files will be\n" - "processed directly in a simulation mode. Otherwise the full set of\n" - "legacy test files is expected to be present in the working directory.\n"); - printf("\n"); + "When a protobuf debug file is available, specify it with -pb. Alternately,\n" + "when -ir or -i is used, the specified files will be processed directly in\n" + "a simulation mode. Otherwise the full set of legacy test files is expected\n" + "to be present in the working directory. OUT_FILE should be specified\n" + "without extension to support both raw and wav output.\n\n"); printf("Options\n"); printf("General configuration (only used for the simulation mode):\n"); printf(" -fs SAMPLE_RATE_HZ\n"); @@ -110,6 +78,7 @@ printf(" --no_delay_logging\n"); printf(" --aec_suppression_level LEVEL [0 - 2]\n"); printf(" --extended_filter\n"); + printf(" --no_reported_delay\n"); printf("\n -aecm Echo control mobile\n"); printf(" --aecm_echo_path_in_file FILE\n"); printf(" --aecm_echo_path_out_file FILE\n"); @@ -132,6 +101,7 @@ printf(" --ns_prob_file FILE\n"); printf("\n -vad Voice activity detection\n"); printf(" --vad_out_file FILE\n"); + printf("\n -expns Experimental noise suppression\n"); printf("\n Level metrics (enabled by default)\n"); printf(" --no_level_metrics\n"); printf("\n"); @@ -142,6 +112,7 @@ printf(" --perf Measure performance.\n"); printf(" --quiet Suppress text output.\n"); printf(" --no_progress Suppress progress.\n"); + printf(" --raw_output Raw output instead of WAV file.\n"); printf(" --debug_file FILE Dump a debug recording.\n"); } @@ -173,20 +144,19 @@ printf("Try `process_test --help' for more information.\n\n"); } - scoped_ptr apm(AudioProcessing::Create(0)); + scoped_ptr apm(AudioProcessing::Create()); ASSERT_TRUE(apm.get() != NULL); const char* pb_filename = NULL; const char* far_filename = NULL; const char* near_filename = NULL; - const char* out_filename = NULL; + std::string out_filename; const char* vad_out_filename = NULL; const char* ns_prob_filename = NULL; const char* aecm_echo_path_in_filename = NULL; const char* aecm_echo_path_out_filename = NULL; int32_t sample_rate_hz = 16000; - int32_t device_sample_rate_hz = 16000; int num_capture_input_channels = 1; int num_capture_output_channels = 1; @@ -198,9 +168,9 @@ bool perf_testing = false; bool verbose = true; bool progress = true; + bool raw_output = false; int extra_delay_ms = 0; int override_delay_ms = 0; - //bool interleaved = true; ASSERT_EQ(apm->kNoError, apm->level_estimator()->Enable(true)); for (int i = 1; i < argc; i++) { @@ -223,7 +193,7 @@ } else if (strcmp(argv[i], "-o") == 0) { i++; - ASSERT_LT(i, argc) << "Specify filename after -o"; + ASSERT_LT(i, argc) << "Specify filename without extension after -o"; out_filename = argv[i]; } else if (strcmp(argv[i], "-fs") == 0) { @@ -290,6 +260,11 @@ config.Set(new DelayCorrection(true)); apm->SetExtraOptions(config); + } else if (strcmp(argv[i], "--no_reported_delay") == 0) { + Config config; + config.Set(new ReportedDelay(false)); + apm->SetExtraOptions(config); + } else if (strcmp(argv[i], "-aecm") == 0) { ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(true)); @@ -426,6 +401,11 @@ ASSERT_LT(i, argc) << "Specify filename after --vad_out_file"; vad_out_filename = argv[i]; + } else if (strcmp(argv[i], "-expns") == 0) { + Config config; + config.Set(new ExperimentalNs(true)); + apm->SetExtraOptions(config); + } else if (strcmp(argv[i], "--noasm") == 0) { WebRtc_GetCPUInfo = WebRtc_GetCPUInfoNoASM; // We need to reinitialize here if components have already been enabled. @@ -449,6 +429,9 @@ } else if (strcmp(argv[i], "--no_progress") == 0) { progress = false; + } else if (strcmp(argv[i], "--raw_output") == 0) { + raw_output = true; + } else if (strcmp(argv[i], "--debug_file") == 0) { i++; ASSERT_LT(i, argc) << "Specify filename after --debug_file"; @@ -472,7 +455,6 @@ const std::string out_path = webrtc::test::OutputPath(); const char far_file_default[] = "apm_far.pcm"; const char near_file_default[] = "apm_near.pcm"; - const std::string out_file_default = out_path + "out.pcm"; const char event_filename[] = "apm_event.dat"; const char delay_filename[] = "apm_delay.dat"; const char drift_filename[] = "apm_drift.dat"; @@ -484,8 +466,8 @@ near_filename = near_file_default; } - if (!out_filename) { - out_filename = out_file_default.c_str(); + if (out_filename.size() == 0) { + out_filename = out_path + "out"; } if (!vad_out_filename) { @@ -499,7 +481,6 @@ FILE* pb_file = NULL; FILE* far_file = NULL; FILE* near_file = NULL; - FILE* out_file = NULL; FILE* event_file = NULL; FILE* delay_file = NULL; FILE* drift_file = NULL; @@ -508,39 +489,24 @@ FILE* aecm_echo_path_in_file = NULL; FILE* aecm_echo_path_out_file = NULL; + scoped_ptr output_wav_file; + scoped_ptr output_raw_file; + if (pb_filename) { - pb_file = fopen(pb_filename, "rb"); - ASSERT_TRUE(NULL != pb_file) << "Unable to open protobuf file " - << pb_filename; + pb_file = OpenFile(pb_filename, "rb"); } else { if (far_filename) { - far_file = fopen(far_filename, "rb"); - ASSERT_TRUE(NULL != far_file) << "Unable to open far-end audio file " - << far_filename; + far_file = OpenFile(far_filename, "rb"); } - near_file = fopen(near_filename, "rb"); - ASSERT_TRUE(NULL != near_file) << "Unable to open near-end audio file " - << near_filename; + near_file = OpenFile(near_filename, "rb"); if (!simulating) { - event_file = fopen(event_filename, "rb"); - ASSERT_TRUE(NULL != event_file) << "Unable to open event file " - << event_filename; - - delay_file = fopen(delay_filename, "rb"); - ASSERT_TRUE(NULL != delay_file) << "Unable to open buffer file " - << delay_filename; - - drift_file = fopen(drift_filename, "rb"); - ASSERT_TRUE(NULL != drift_file) << "Unable to open drift file " - << drift_filename; + event_file = OpenFile(event_filename, "rb"); + delay_file = OpenFile(delay_filename, "rb"); + drift_file = OpenFile(drift_filename, "rb"); } } - out_file = fopen(out_filename, "wb"); - ASSERT_TRUE(NULL != out_file) << "Unable to open output audio file " - << out_filename; - int near_size_bytes = 0; if (pb_file) { struct stat st; @@ -554,25 +520,19 @@ } if (apm->voice_detection()->is_enabled()) { - vad_out_file = fopen(vad_out_filename, "wb"); - ASSERT_TRUE(NULL != vad_out_file) << "Unable to open VAD output file " - << vad_out_file; + vad_out_file = OpenFile(vad_out_filename, "wb"); } if (apm->noise_suppression()->is_enabled()) { - ns_prob_file = fopen(ns_prob_filename, "wb"); - ASSERT_TRUE(NULL != ns_prob_file) << "Unable to open NS output file " - << ns_prob_file; + ns_prob_file = OpenFile(ns_prob_filename, "wb"); } if (aecm_echo_path_in_filename != NULL) { - aecm_echo_path_in_file = fopen(aecm_echo_path_in_filename, "rb"); - ASSERT_TRUE(NULL != aecm_echo_path_in_file) << "Unable to open file " - << aecm_echo_path_in_filename; + aecm_echo_path_in_file = OpenFile(aecm_echo_path_in_filename, "rb"); const size_t path_size = apm->echo_control_mobile()->echo_path_size_bytes(); - scoped_array echo_path(new char[path_size]); + scoped_ptr echo_path(new char[path_size]); ASSERT_EQ(path_size, fread(echo_path.get(), sizeof(char), path_size, @@ -585,9 +545,7 @@ } if (aecm_echo_path_out_filename != NULL) { - aecm_echo_path_out_file = fopen(aecm_echo_path_out_filename, "wb"); - ASSERT_TRUE(NULL != aecm_echo_path_out_file) << "Unable to open file " - << aecm_echo_path_out_filename; + aecm_echo_path_out_file = OpenFile(aecm_echo_path_out_filename, "wb"); } size_t read_count = 0; @@ -616,6 +574,10 @@ // but for now we want to share the variables. if (pb_file) { Event event_msg; + scoped_ptr > reverse_cb; + scoped_ptr > primary_cb; + int output_sample_rate = 32000; + AudioProcessing::ChannelLayout output_layout = AudioProcessing::kMono; while (ReadMessageFromFile(pb_file, &event_msg)) { std::ostringstream trace_stream; trace_stream << "Processed frames: " << reverse_count << " (reverse), " @@ -627,52 +589,90 @@ const Init msg = event_msg.init(); ASSERT_TRUE(msg.has_sample_rate()); - // TODO(bjornv): Replace set_sample_rate_hz() when we have a smarter - // AnalyzeReverseStream(). - ASSERT_EQ(apm->kNoError, apm->set_sample_rate_hz(msg.sample_rate())); - ASSERT_TRUE(msg.has_device_sample_rate()); - ASSERT_EQ(apm->kNoError, - apm->echo_cancellation()->set_device_sample_rate_hz( - msg.device_sample_rate())); - ASSERT_TRUE(msg.has_num_input_channels()); ASSERT_TRUE(msg.has_num_output_channels()); ASSERT_TRUE(msg.has_num_reverse_channels()); + int reverse_sample_rate = msg.sample_rate(); + if (msg.has_reverse_sample_rate()) { + reverse_sample_rate = msg.reverse_sample_rate(); + } + output_sample_rate = msg.sample_rate(); + if (msg.has_output_sample_rate()) { + output_sample_rate = msg.output_sample_rate(); + } + output_layout = LayoutFromChannels(msg.num_output_channels()); + ASSERT_EQ(kNoErr, apm->Initialize( + msg.sample_rate(), + output_sample_rate, + reverse_sample_rate, + LayoutFromChannels(msg.num_input_channels()), + output_layout, + LayoutFromChannels(msg.num_reverse_channels()))); samples_per_channel = msg.sample_rate() / 100; far_frame.sample_rate_hz_ = msg.sample_rate(); - far_frame.samples_per_channel_ = samples_per_channel; + far_frame.samples_per_channel_ = reverse_sample_rate / 100; far_frame.num_channels_ = msg.num_reverse_channels(); near_frame.sample_rate_hz_ = msg.sample_rate(); near_frame.samples_per_channel_ = samples_per_channel; near_frame.num_channels_ = msg.num_input_channels(); + reverse_cb.reset(new ChannelBuffer( + far_frame.samples_per_channel_, + msg.num_reverse_channels())); + primary_cb.reset(new ChannelBuffer(samples_per_channel, + msg.num_input_channels())); if (verbose) { printf("Init at frame: %d (primary), %d (reverse)\n", primary_count, reverse_count); - printf(" Sample rate: %d Hz\n", msg.sample_rate()); + printf(" Primary rates: %d Hz (in), %d Hz (out)\n", + msg.sample_rate(), output_sample_rate); printf(" Primary channels: %d (in), %d (out)\n", msg.num_input_channels(), msg.num_output_channels()); - printf(" Reverse channels: %d \n", msg.num_reverse_channels()); + printf(" Reverse rate: %d\n", reverse_sample_rate); + printf(" Reverse channels: %d\n", msg.num_reverse_channels()); + } + + if (!raw_output) { + // The WAV file needs to be reset every time, because it cant change + // it's sample rate or number of channels. + output_wav_file.reset(new WavWriter(out_filename + ".wav", + output_sample_rate, + msg.num_output_channels())); } } else if (event_msg.type() == Event::REVERSE_STREAM) { ASSERT_TRUE(event_msg.has_reverse_stream()); - const ReverseStream msg = event_msg.reverse_stream(); + ReverseStream msg = event_msg.reverse_stream(); reverse_count++; - ASSERT_TRUE(msg.has_data()); - ASSERT_EQ(sizeof(int16_t) * samples_per_channel * - far_frame.num_channels_, msg.data().size()); - memcpy(far_frame.data_, msg.data().data(), msg.data().size()); + ASSERT_TRUE(msg.has_data() ^ (msg.channel_size() > 0)); + if (msg.has_data()) { + ASSERT_EQ(sizeof(int16_t) * far_frame.samples_per_channel_ * + far_frame.num_channels_, msg.data().size()); + memcpy(far_frame.data_, msg.data().data(), msg.data().size()); + } else { + for (int i = 0; i < msg.channel_size(); ++i) { + reverse_cb->CopyFrom(msg.channel(i).data(), i); + } + } if (perf_testing) { t0 = TickTime::Now(); } - ASSERT_EQ(apm->kNoError, - apm->AnalyzeReverseStream(&far_frame)); + if (msg.has_data()) { + ASSERT_EQ(apm->kNoError, + apm->AnalyzeReverseStream(&far_frame)); + } else { + ASSERT_EQ(apm->kNoError, + apm->AnalyzeReverseStream( + reverse_cb->channels(), + far_frame.samples_per_channel_, + far_frame.sample_rate_hz_, + LayoutFromChannels(far_frame.num_channels_))); + } if (perf_testing) { t1 = TickTime::Now(); @@ -694,15 +694,23 @@ // ProcessStream could have changed this for the output frame. near_frame.num_channels_ = apm->num_input_channels(); - ASSERT_TRUE(msg.has_input_data()); - ASSERT_EQ(sizeof(int16_t) * samples_per_channel * - near_frame.num_channels_, msg.input_data().size()); - memcpy(near_frame.data_, - msg.input_data().data(), - msg.input_data().size()); + ASSERT_TRUE(msg.has_input_data() ^ (msg.input_channel_size() > 0)); + if (msg.has_input_data()) { + ASSERT_EQ(sizeof(int16_t) * samples_per_channel * + near_frame.num_channels_, msg.input_data().size()); + memcpy(near_frame.data_, + msg.input_data().data(), + msg.input_data().size()); + near_read_bytes += msg.input_data().size(); + } else { + for (int i = 0; i < msg.input_channel_size(); ++i) { + primary_cb->CopyFrom(msg.input_channel(i).data(), i); + near_read_bytes += msg.input_channel(i).size(); + } + } - near_read_bytes += msg.input_data().size(); if (progress && primary_count % 100 == 0) { + near_read_bytes = std::min(near_read_bytes, near_size_bytes); printf("%.0f%% complete\r", (near_read_bytes * 100.0) / near_size_bytes); fflush(stdout); @@ -722,13 +730,32 @@ apm->set_stream_delay_ms(delay_ms)); apm->echo_cancellation()->set_stream_drift_samples(msg.drift()); - int err = apm->ProcessStream(&near_frame); + if (msg.has_keypress()) { + apm->set_stream_key_pressed(msg.keypress()); + } else { + apm->set_stream_key_pressed(true); + } + + int err = apm->kNoError; + if (msg.has_input_data()) { + err = apm->ProcessStream(&near_frame); + ASSERT_TRUE(near_frame.num_channels_ == apm->num_output_channels()); + } else { + err = apm->ProcessStream( + primary_cb->channels(), + near_frame.samples_per_channel_, + near_frame.sample_rate_hz_, + LayoutFromChannels(near_frame.num_channels_), + output_sample_rate, + output_layout, + primary_cb->channels()); + } + if (err == apm->kBadStreamParameterWarning) { printf("Bad parameter warning. %s\n", trace_stream.str().c_str()); } ASSERT_TRUE(err == apm->kNoError || err == apm->kBadStreamParameterWarning); - ASSERT_TRUE(near_frame.num_channels_ == apm->num_output_channels()); stream_has_voice = static_cast(apm->voice_detection()->stream_has_voice()); @@ -759,11 +786,25 @@ } } - size_t size = samples_per_channel * near_frame.num_channels_; - ASSERT_EQ(size, fwrite(near_frame.data_, - sizeof(int16_t), - size, - out_file)); + const size_t samples_per_channel = output_sample_rate / 100; + if (msg.has_input_data()) { + if (raw_output && !output_raw_file) { + output_raw_file.reset(new RawFile(out_filename + ".pcm")); + } + WriteIntData(near_frame.data_, + apm->num_output_channels() * samples_per_channel, + output_wav_file.get(), + output_raw_file.get()); + } else { + if (raw_output && !output_raw_file) { + output_raw_file.reset(new RawFile(out_filename + ".float")); + } + WriteFloatData(primary_cb->channels(), + samples_per_channel, + apm->num_output_channels(), + output_wav_file.get(), + output_raw_file.get()); + } } } @@ -811,19 +852,20 @@ fread(&sample_rate_hz, sizeof(sample_rate_hz), 1, event_file)); samples_per_channel = sample_rate_hz / 100; + int32_t unused_device_sample_rate_hz; ASSERT_EQ(1u, - fread(&device_sample_rate_hz, - sizeof(device_sample_rate_hz), + fread(&unused_device_sample_rate_hz, + sizeof(unused_device_sample_rate_hz), 1, event_file)); - // TODO(bjornv): Replace set_sample_rate_hz() when we have a smarter - // AnalyzeReverseStream(). - ASSERT_EQ(apm->kNoError, apm->set_sample_rate_hz(sample_rate_hz)); - - ASSERT_EQ(apm->kNoError, - apm->echo_cancellation()->set_device_sample_rate_hz( - device_sample_rate_hz)); + ASSERT_EQ(kNoErr, apm->Initialize( + sample_rate_hz, + sample_rate_hz, + sample_rate_hz, + LayoutFromChannels(num_capture_input_channels), + LayoutFromChannels(num_capture_output_channels), + LayoutFromChannels(num_render_channels))); far_frame.sample_rate_hz_ = sample_rate_hz; far_frame.samples_per_channel_ = samples_per_channel; @@ -831,6 +873,14 @@ near_frame.sample_rate_hz_ = sample_rate_hz; near_frame.samples_per_channel_ = samples_per_channel; + if (!raw_output) { + // The WAV file needs to be reset every time, because it can't change + // it's sample rate or number of channels. + output_wav_file.reset(new WavWriter(out_filename + ".wav", + sample_rate_hz, + num_capture_output_channels)); + } + if (verbose) { printf("Init at frame: %d (primary), %d (reverse)\n", primary_count, reverse_count); @@ -930,6 +980,8 @@ apm->set_stream_delay_ms(delay_ms)); apm->echo_cancellation()->set_stream_drift_samples(drift_samples); + apm->set_stream_key_pressed(true); + int err = apm->ProcessStream(&near_frame); if (err == apm->kBadStreamParameterWarning) { printf("Bad parameter warning. %s\n", trace_stream.str().c_str()); @@ -973,11 +1025,18 @@ } } - size = samples_per_channel * near_frame.num_channels_; - ASSERT_EQ(size, fwrite(near_frame.data_, - sizeof(int16_t), - size, - out_file)); + if (raw_output && !output_raw_file) { + output_raw_file.reset(new RawFile(out_filename + ".pcm")); + } + if (!raw_output && !output_wav_file) { + output_wav_file.reset(new WavWriter(out_filename + ".wav", + sample_rate_hz, + num_capture_output_channels)); + } + WriteIntData(near_frame.data_, + size, + output_wav_file.get(), + output_raw_file.get()); } else { FAIL() << "Event " << event << " is unrecognized"; @@ -989,7 +1048,7 @@ if (aecm_echo_path_out_file != NULL) { const size_t path_size = apm->echo_control_mobile()->echo_path_size_bytes(); - scoped_array echo_path(new char[path_size]); + scoped_ptr echo_path(new char[path_size]); apm->echo_control_mobile()->GetEchoPath(echo_path.get(), path_size); ASSERT_EQ(path_size, fwrite(echo_path.get(), sizeof(char), @@ -1067,11 +1126,13 @@ } } } + } // namespace +} // namespace webrtc int main(int argc, char* argv[]) { - void_main(argc, argv); + webrtc::void_main(argc, argv); // Optional, but removes memory leak noise from Valgrind. google::protobuf::ShutdownProtobufLibrary(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/test_utils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/test_utils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/test_utils.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/test_utils.h 2015-02-03 14:33:35.000000000 +0000 @@ -8,14 +8,172 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include +#include + +#include "webrtc/audio_processing/debug.pb.h" +#include "webrtc/common_audio/include/audio_util.h" +#include "webrtc/common_audio/wav_file.h" +#include "webrtc/modules/audio_processing/common.h" #include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +static const AudioProcessing::Error kNoErr = AudioProcessing::kNoError; +#define EXPECT_NOERR(expr) EXPECT_EQ(kNoErr, (expr)) + +class RawFile { + public: + RawFile(const std::string& filename) + : file_handle_(fopen(filename.c_str(), "wb")) {} + + ~RawFile() { + fclose(file_handle_); + } + + void WriteSamples(const int16_t* samples, size_t num_samples) { +#ifndef WEBRTC_ARCH_LITTLE_ENDIAN +#error "Need to convert samples to little-endian when writing to PCM file" +#endif + fwrite(samples, sizeof(*samples), num_samples, file_handle_); + } + + void WriteSamples(const float* samples, size_t num_samples) { + fwrite(samples, sizeof(*samples), num_samples, file_handle_); + } + + private: + FILE* file_handle_; +}; -static const int kChunkSizeMs = 10; -static const webrtc::AudioProcessing::Error kNoErr = - webrtc::AudioProcessing::kNoError; +static inline void WriteIntData(const int16_t* data, + size_t length, + WavWriter* wav_file, + RawFile* raw_file) { + if (wav_file) { + wav_file->WriteSamples(data, length); + } + if (raw_file) { + raw_file->WriteSamples(data, length); + } +} + +static inline void WriteFloatData(const float* const* data, + size_t samples_per_channel, + int num_channels, + WavWriter* wav_file, + RawFile* raw_file) { + size_t length = num_channels * samples_per_channel; + scoped_ptr buffer(new float[length]); + Interleave(data, samples_per_channel, num_channels, buffer.get()); + if (raw_file) { + raw_file->WriteSamples(buffer.get(), length); + } + // TODO(aluebs): Use ScaleToInt16Range() from audio_util + for (size_t i = 0; i < length; ++i) { + buffer[i] = buffer[i] > 0 ? + buffer[i] * std::numeric_limits::max() : + -buffer[i] * std::numeric_limits::min(); + } + if (wav_file) { + wav_file->WriteSamples(buffer.get(), length); + } +} + +// Exits on failure; do not use in unit tests. +static inline FILE* OpenFile(const std::string& filename, const char* mode) { + FILE* file = fopen(filename.c_str(), mode); + if (!file) { + printf("Unable to open file %s\n", filename.c_str()); + exit(1); + } + return file; +} + +static inline int SamplesFromRate(int rate) { + return AudioProcessing::kChunkSizeMs * rate / 1000; +} -static void SetFrameSampleRate(webrtc::AudioFrame* frame, int sample_rate_hz) { +static inline void SetFrameSampleRate(AudioFrame* frame, + int sample_rate_hz) { frame->sample_rate_hz_ = sample_rate_hz; - frame->samples_per_channel_ = kChunkSizeMs * sample_rate_hz / 1000; + frame->samples_per_channel_ = AudioProcessing::kChunkSizeMs * + sample_rate_hz / 1000; } + +template +void SetContainerFormat(int sample_rate_hz, + int num_channels, + AudioFrame* frame, + scoped_ptr >* cb) { + SetFrameSampleRate(frame, sample_rate_hz); + frame->num_channels_ = num_channels; + cb->reset(new ChannelBuffer(frame->samples_per_channel_, num_channels)); +} + +static inline AudioProcessing::ChannelLayout LayoutFromChannels( + int num_channels) { + switch (num_channels) { + case 1: + return AudioProcessing::kMono; + case 2: + return AudioProcessing::kStereo; + default: + assert(false); + return AudioProcessing::kMono; + } +} + +// Allocates new memory in the scoped_ptr to fit the raw message and returns the +// number of bytes read. +static inline size_t ReadMessageBytesFromFile(FILE* file, + scoped_ptr* bytes) { + // The "wire format" for the size is little-endian. Assume we're running on + // a little-endian machine. + int32_t size = 0; + if (fread(&size, sizeof(size), 1, file) != 1) + return 0; + if (size <= 0) + return 0; + + bytes->reset(new uint8_t[size]); + return fread(bytes->get(), sizeof((*bytes)[0]), size, file); +} + +// Returns true on success, false on error or end-of-file. +static inline bool ReadMessageFromFile(FILE* file, + ::google::protobuf::MessageLite* msg) { + scoped_ptr bytes; + size_t size = ReadMessageBytesFromFile(file, &bytes); + if (!size) + return false; + + msg->Clear(); + return msg->ParseFromArray(bytes.get(), size); +} + +template +float ComputeSNR(const T* ref, const T* test, int length, float* variance) { + float mse = 0; + float mean = 0; + *variance = 0; + for (int i = 0; i < length; ++i) { + T error = ref[i] - test[i]; + mse += error * error; + *variance += ref[i] * ref[i]; + mean += ref[i]; + } + mse /= length; + *variance /= length; + mean /= length; + *variance -= mean * mean; + + float snr = 100; // We assign 100 dB to the zero-error case. + if (mse > 0) + snr = 10 * log10(*variance / mse); + return snr; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/unpack.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/unpack.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/unpack.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/test/unpack.cc 2015-02-03 14:33:35.000000000 +0000 @@ -17,21 +17,15 @@ #include "gflags/gflags.h" #include "webrtc/audio_processing/debug.pb.h" +#include "webrtc/modules/audio_processing/test/test_utils.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/typedefs.h" -using webrtc::scoped_array; - -using webrtc::audioproc::Event; -using webrtc::audioproc::ReverseStream; -using webrtc::audioproc::Stream; -using webrtc::audioproc::Init; - // TODO(andrew): unpack more of the data. -DEFINE_string(input_file, "input.pcm", "The name of the input stream file."); -DEFINE_string(output_file, "ref_out.pcm", +DEFINE_string(input_file, "input", "The name of the input stream file."); +DEFINE_string(output_file, "ref_out", "The name of the reference output stream file."); -DEFINE_string(reverse_file, "reverse.pcm", +DEFINE_string(reverse_file, "reverse", "The name of the reverse input stream file."); DEFINE_string(delay_file, "delay.int32", "The name of the delay file."); DEFINE_string(drift_file, "drift.int32", "The name of the drift file."); @@ -40,32 +34,24 @@ DEFINE_string(settings_file, "settings.txt", "The name of the settings file."); DEFINE_bool(full, false, "Unpack the full set of files (normally not needed)."); +DEFINE_bool(raw, false, "Write raw data instead of a WAV file."); -// TODO(andrew): move this to a helper class to share with process_test.cc? -// Returns true on success, false on error or end-of-file. -bool ReadMessageFromFile(FILE* file, - ::google::protobuf::MessageLite* msg) { - // The "wire format" for the size is little-endian. - // Assume process_test is running on a little-endian machine. - int32_t size = 0; - if (fread(&size, sizeof(int32_t), 1, file) != 1) { - return false; - } - if (size <= 0) { - return false; - } - const size_t usize = static_cast(size); +namespace webrtc { - scoped_array array(new char[usize]); - if (fread(array.get(), sizeof(char), usize, file) != usize) { - return false; +using audioproc::Event; +using audioproc::ReverseStream; +using audioproc::Stream; +using audioproc::Init; + +void WriteData(const void* data, size_t size, FILE* file, + const std::string& filename) { + if (fwrite(data, size, 1, file) != 1) { + printf("Error when writing to %s\n", filename.c_str()); + exit(1); } - - msg->Clear(); - return msg->ParseFromArray(array.get(), usize); } -int main(int argc, char* argv[]) { +int do_main(int argc, char* argv[]) { std::string program_name = argv[0]; std::string usage = "Commandline tool to unpack audioproc debug files.\n" "Example usage:\n" + program_name + " debug_dump.pb\n"; @@ -77,155 +63,196 @@ return 1; } - FILE* debug_file = fopen(argv[1], "rb"); - if (debug_file == NULL) { - printf("Unable to open %s\n", argv[1]); - return 1; - } - FILE* input_file = fopen(FLAGS_input_file.c_str(), "wb"); - if (input_file == NULL) { - printf("Unable to open %s\n", FLAGS_input_file.c_str()); - return 1; - } - FILE* output_file = fopen(FLAGS_output_file.c_str(), "wb"); - if (output_file == NULL) { - printf("Unable to open %s\n", FLAGS_output_file.c_str()); - return 1; - } - FILE* reverse_file = fopen(FLAGS_reverse_file.c_str(), "wb"); - if (reverse_file == NULL) { - printf("Unable to open %s\n", FLAGS_reverse_file.c_str()); - return 1; - } - FILE* settings_file = fopen(FLAGS_settings_file.c_str(), "wb"); - if (settings_file == NULL) { - printf("Unable to open %s\n", FLAGS_settings_file.c_str()); - return 1; - } - - FILE* delay_file = NULL; - FILE* drift_file = NULL; - FILE* level_file = NULL; - FILE* keypress_file = NULL; - if (FLAGS_full) { - delay_file = fopen(FLAGS_delay_file.c_str(), "wb"); - if (delay_file == NULL) { - printf("Unable to open %s\n", FLAGS_delay_file.c_str()); - return 1; - } - drift_file = fopen(FLAGS_drift_file.c_str(), "wb"); - if (drift_file == NULL) { - printf("Unable to open %s\n", FLAGS_drift_file.c_str()); - return 1; - } - level_file = fopen(FLAGS_level_file.c_str(), "wb"); - if (level_file == NULL) { - printf("Unable to open %s\n", FLAGS_level_file.c_str()); - return 1; - } - keypress_file = fopen(FLAGS_keypress_file.c_str(), "wb"); - if (keypress_file == NULL) { - printf("Unable to open %s\n", FLAGS_keypress_file.c_str()); - return 1; - } - } + FILE* debug_file = OpenFile(argv[1], "rb"); Event event_msg; int frame_count = 0; + int reverse_samples_per_channel = 0; + int input_samples_per_channel = 0; + int output_samples_per_channel = 0; + int num_reverse_channels = 0; + int num_input_channels = 0; + int num_output_channels = 0; + scoped_ptr reverse_wav_file; + scoped_ptr input_wav_file; + scoped_ptr output_wav_file; + scoped_ptr reverse_raw_file; + scoped_ptr input_raw_file; + scoped_ptr output_raw_file; while (ReadMessageFromFile(debug_file, &event_msg)) { if (event_msg.type() == Event::REVERSE_STREAM) { if (!event_msg.has_reverse_stream()) { - printf("Corrupted input file: ReverseStream missing.\n"); + printf("Corrupt input file: ReverseStream missing.\n"); return 1; } const ReverseStream msg = event_msg.reverse_stream(); if (msg.has_data()) { - if (fwrite(msg.data().data(), msg.data().size(), 1, reverse_file) != - 1) { - printf("Error when writing to %s\n", FLAGS_reverse_file.c_str()); - return 1; + if (FLAGS_raw && !reverse_raw_file) { + reverse_raw_file.reset(new RawFile(FLAGS_reverse_file + ".pcm")); } + // TODO(aluebs): Replace "num_reverse_channels * + // reverse_samples_per_channel" with "msg.data().size() / + // sizeof(int16_t)" and so on when this fix in audio_processing has made + // it into stable: https://webrtc-codereview.appspot.com/15299004/ + WriteIntData(reinterpret_cast(msg.data().data()), + num_reverse_channels * reverse_samples_per_channel, + reverse_wav_file.get(), + reverse_raw_file.get()); + } else if (msg.channel_size() > 0) { + if (FLAGS_raw && !reverse_raw_file) { + reverse_raw_file.reset(new RawFile(FLAGS_reverse_file + ".float")); + } + scoped_ptr data(new const float*[num_reverse_channels]); + for (int i = 0; i < num_reverse_channels; ++i) { + data[i] = reinterpret_cast(msg.channel(i).data()); + } + WriteFloatData(data.get(), + reverse_samples_per_channel, + num_reverse_channels, + reverse_wav_file.get(), + reverse_raw_file.get()); } } else if (event_msg.type() == Event::STREAM) { frame_count++; if (!event_msg.has_stream()) { - printf("Corrupted input file: Stream missing.\n"); + printf("Corrupt input file: Stream missing.\n"); return 1; } const Stream msg = event_msg.stream(); if (msg.has_input_data()) { - if (fwrite(msg.input_data().data(), msg.input_data().size(), 1, - input_file) != 1) { - printf("Error when writing to %s\n", FLAGS_input_file.c_str()); - return 1; + if (FLAGS_raw && !input_raw_file) { + input_raw_file.reset(new RawFile(FLAGS_input_file + ".pcm")); } + WriteIntData(reinterpret_cast(msg.input_data().data()), + num_input_channels * input_samples_per_channel, + input_wav_file.get(), + input_raw_file.get()); + } else if (msg.input_channel_size() > 0) { + if (FLAGS_raw && !input_raw_file) { + input_raw_file.reset(new RawFile(FLAGS_input_file + ".float")); + } + scoped_ptr data(new const float*[num_input_channels]); + for (int i = 0; i < num_input_channels; ++i) { + data[i] = reinterpret_cast(msg.input_channel(i).data()); + } + WriteFloatData(data.get(), + input_samples_per_channel, + num_input_channels, + input_wav_file.get(), + input_raw_file.get()); } if (msg.has_output_data()) { - if (fwrite(msg.output_data().data(), msg.output_data().size(), 1, - output_file) != 1) { - printf("Error when writing to %s\n", FLAGS_output_file.c_str()); - return 1; + if (FLAGS_raw && !output_raw_file) { + output_raw_file.reset(new RawFile(FLAGS_output_file + ".pcm")); } + WriteIntData(reinterpret_cast(msg.output_data().data()), + num_output_channels * output_samples_per_channel, + output_wav_file.get(), + output_raw_file.get()); + } else if (msg.output_channel_size() > 0) { + if (FLAGS_raw && !output_raw_file) { + output_raw_file.reset(new RawFile(FLAGS_output_file + ".float")); + } + scoped_ptr data(new const float*[num_output_channels]); + for (int i = 0; i < num_output_channels; ++i) { + data[i] = + reinterpret_cast(msg.output_channel(i).data()); + } + WriteFloatData(data.get(), + output_samples_per_channel, + num_output_channels, + output_wav_file.get(), + output_raw_file.get()); } if (FLAGS_full) { if (msg.has_delay()) { + static FILE* delay_file = OpenFile(FLAGS_delay_file, "wb"); int32_t delay = msg.delay(); - if (fwrite(&delay, sizeof(int32_t), 1, delay_file) != 1) { - printf("Error when writing to %s\n", FLAGS_delay_file.c_str()); - return 1; - } + WriteData(&delay, sizeof(delay), delay_file, FLAGS_delay_file); } if (msg.has_drift()) { + static FILE* drift_file = OpenFile(FLAGS_drift_file, "wb"); int32_t drift = msg.drift(); - if (fwrite(&drift, sizeof(int32_t), 1, drift_file) != 1) { - printf("Error when writing to %s\n", FLAGS_drift_file.c_str()); - return 1; - } + WriteData(&drift, sizeof(drift), drift_file, FLAGS_drift_file); } if (msg.has_level()) { + static FILE* level_file = OpenFile(FLAGS_level_file, "wb"); int32_t level = msg.level(); - if (fwrite(&level, sizeof(int32_t), 1, level_file) != 1) { - printf("Error when writing to %s\n", FLAGS_level_file.c_str()); - return 1; - } + WriteData(&level, sizeof(level), level_file, FLAGS_level_file); } if (msg.has_keypress()) { + static FILE* keypress_file = OpenFile(FLAGS_keypress_file, "wb"); bool keypress = msg.keypress(); - if (fwrite(&keypress, sizeof(bool), 1, keypress_file) != 1) { - printf("Error when writing to %s\n", FLAGS_keypress_file.c_str()); - return 1; - } + WriteData(&keypress, sizeof(keypress), keypress_file, + FLAGS_keypress_file); } } } else if (event_msg.type() == Event::INIT) { if (!event_msg.has_init()) { - printf("Corrupted input file: Init missing.\n"); + printf("Corrupt input file: Init missing.\n"); return 1; } + static FILE* settings_file = OpenFile(FLAGS_settings_file, "wb"); const Init msg = event_msg.init(); // These should print out zeros if they're missing. fprintf(settings_file, "Init at frame: %d\n", frame_count); - fprintf(settings_file, " Sample rate: %d\n", msg.sample_rate()); - fprintf(settings_file, " Device sample rate: %d\n", - msg.device_sample_rate()); - fprintf(settings_file, " Input channels: %d\n", - msg.num_input_channels()); - fprintf(settings_file, " Output channels: %d\n", - msg.num_output_channels()); - fprintf(settings_file, " Reverse channels: %d\n", - msg.num_reverse_channels()); + int input_sample_rate = msg.sample_rate(); + fprintf(settings_file, " Input sample rate: %d\n", input_sample_rate); + int output_sample_rate = msg.output_sample_rate(); + fprintf(settings_file, " Output sample rate: %d\n", output_sample_rate); + int reverse_sample_rate = msg.reverse_sample_rate(); + fprintf(settings_file, + " Reverse sample rate: %d\n", + reverse_sample_rate); + num_input_channels = msg.num_input_channels(); + fprintf(settings_file, " Input channels: %d\n", num_input_channels); + num_output_channels = msg.num_output_channels(); + fprintf(settings_file, " Output channels: %d\n", num_output_channels); + num_reverse_channels = msg.num_reverse_channels(); + fprintf(settings_file, " Reverse channels: %d\n", num_reverse_channels); fprintf(settings_file, "\n"); + + if (reverse_sample_rate == 0) { + reverse_sample_rate = input_sample_rate; + } + if (output_sample_rate == 0) { + output_sample_rate = input_sample_rate; + } + + reverse_samples_per_channel = reverse_sample_rate / 100; + input_samples_per_channel = input_sample_rate / 100; + output_samples_per_channel = output_sample_rate / 100; + + if (!FLAGS_raw) { + // The WAV files need to be reset every time, because they cant change + // their sample rate or number of channels. + reverse_wav_file.reset(new WavWriter(FLAGS_reverse_file + ".wav", + reverse_sample_rate, + num_reverse_channels)); + input_wav_file.reset(new WavWriter(FLAGS_input_file + ".wav", + input_sample_rate, + num_input_channels)); + output_wav_file.reset(new WavWriter(FLAGS_output_file + ".wav", + output_sample_rate, + num_output_channels)); + } } } return 0; } + +} // namespace webrtc + +int main(int argc, char* argv[]) { + return webrtc::do_main(argc, argv); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_apm_utility -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := \ - fft4g.c \ - ring_buffer.c \ - delay_estimator.c \ - delay_estimator_wrapper.c - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH) \ - $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../../../common_audio/signal_processing/include \ - external/webrtc - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator.c 2015-02-03 14:33:35.000000000 +0000 @@ -98,6 +98,7 @@ kMaxHitsWhenPossiblyNonCausal : kMaxHitsWhenPossiblyCausal; int i = 0; + assert(self->history_size == self->farend->history_size); // Reset |candidate_hits| if we have a new candidate. if (candidate_delay != self->last_candidate_delay) { self->candidate_hits = 0; @@ -130,7 +131,7 @@ // 4. All other bins are decreased with |valley_depth|. // TODO(bjornv): Investigate how to make this loop more efficient. Split up // the loop? Remove parts that doesn't add too much. - for (i = 0; i < self->farend->history_size; ++i) { + for (i = 0; i < self->history_size; ++i) { int is_in_last_set = (i >= self->last_delay - 2) && (i <= self->last_delay + 1) && (i != candidate_delay); int is_in_candidate_set = (i >= candidate_delay - 2) && @@ -277,25 +278,45 @@ // Sanity conditions fulfilled. self = malloc(sizeof(BinaryDelayEstimatorFarend)); } - if (self != NULL) { - int malloc_fail = 0; - - self->history_size = history_size; - - // Allocate memory for history buffers. - self->binary_far_history = malloc(history_size * sizeof(uint32_t)); - malloc_fail |= (self->binary_far_history == NULL); + if (self == NULL) { + return NULL; + } - self->far_bit_counts = malloc(history_size * sizeof(int)); - malloc_fail |= (self->far_bit_counts == NULL); + self->history_size = 0; + self->binary_far_history = NULL; + self->far_bit_counts = NULL; + if (WebRtc_AllocateFarendBufferMemory(self, history_size) == 0) { + WebRtc_FreeBinaryDelayEstimatorFarend(self); + self = NULL; + } + return self; +} - if (malloc_fail) { - WebRtc_FreeBinaryDelayEstimatorFarend(self); - self = NULL; - } +int WebRtc_AllocateFarendBufferMemory(BinaryDelayEstimatorFarend* self, + int history_size) { + assert(self != NULL); + // (Re-)Allocate memory for history buffers. + self->binary_far_history = + realloc(self->binary_far_history, + history_size * sizeof(*self->binary_far_history)); + self->far_bit_counts = realloc(self->far_bit_counts, + history_size * sizeof(*self->far_bit_counts)); + if ((self->binary_far_history == NULL) || (self->far_bit_counts == NULL)) { + history_size = 0; + } + // Fill with zeros if we have expanded the buffers. + if (history_size > self->history_size) { + int size_diff = history_size - self->history_size; + memset(&self->binary_far_history[self->history_size], + 0, + sizeof(*self->binary_far_history) * size_diff); + memset(&self->far_bit_counts[self->history_size], + 0, + sizeof(*self->far_bit_counts) * size_diff); } + self->history_size = history_size; - return self; + return self->history_size; } void WebRtc_InitBinaryDelayEstimatorFarend(BinaryDelayEstimatorFarend* self) { @@ -304,6 +325,39 @@ memset(self->far_bit_counts, 0, sizeof(int) * self->history_size); } +void WebRtc_SoftResetBinaryDelayEstimatorFarend( + BinaryDelayEstimatorFarend* self, int delay_shift) { + int abs_shift = abs(delay_shift); + int shift_size = 0; + int dest_index = 0; + int src_index = 0; + int padding_index = 0; + + assert(self != NULL); + shift_size = self->history_size - abs_shift; + assert(shift_size > 0); + if (delay_shift == 0) { + return; + } else if (delay_shift > 0) { + dest_index = abs_shift; + } else if (delay_shift < 0) { + src_index = abs_shift; + padding_index = shift_size; + } + + // Shift and zero pad buffers. + memmove(&self->binary_far_history[dest_index], + &self->binary_far_history[src_index], + sizeof(*self->binary_far_history) * shift_size); + memset(&self->binary_far_history[padding_index], 0, + sizeof(*self->binary_far_history) * abs_shift); + memmove(&self->far_bit_counts[dest_index], + &self->far_bit_counts[src_index], + sizeof(*self->far_bit_counts) * shift_size); + memset(&self->far_bit_counts[padding_index], 0, + sizeof(*self->far_bit_counts) * abs_shift); +} + void WebRtc_AddBinaryFarSpectrum(BinaryDelayEstimatorFarend* handle, uint32_t binary_far_spectrum) { assert(handle != NULL); @@ -345,98 +399,151 @@ } BinaryDelayEstimator* WebRtc_CreateBinaryDelayEstimator( - BinaryDelayEstimatorFarend* farend, int lookahead) { + BinaryDelayEstimatorFarend* farend, int max_lookahead) { BinaryDelayEstimator* self = NULL; - if ((farend != NULL) && (lookahead >= 0)) { + if ((farend != NULL) && (max_lookahead >= 0)) { // Sanity conditions fulfilled. self = malloc(sizeof(BinaryDelayEstimator)); } + if (self == NULL) { + return NULL; + } - if (self != NULL) { - int malloc_fail = 0; + self->farend = farend; + self->near_history_size = max_lookahead + 1; + self->history_size = 0; + self->robust_validation_enabled = 0; // Disabled by default. + self->allowed_offset = 0; - self->farend = farend; - self->near_history_size = lookahead + 1; - self->robust_validation_enabled = 0; // Disabled by default. - self->allowed_offset = 0; - - // Allocate memory for spectrum buffers. The extra array element in - // |mean_bit_counts| and |histogram| is a dummy element only used while - // |last_delay| == -2, i.e., before we have a valid estimate. - self->mean_bit_counts = - malloc((farend->history_size + 1) * sizeof(int32_t)); - malloc_fail |= (self->mean_bit_counts == NULL); - - self->bit_counts = malloc(farend->history_size * sizeof(int32_t)); - malloc_fail |= (self->bit_counts == NULL); - - // Allocate memory for history buffers. - self->binary_near_history = malloc((lookahead + 1) * sizeof(uint32_t)); - malloc_fail |= (self->binary_near_history == NULL); - - self->histogram = malloc((farend->history_size + 1) * sizeof(float)); - malloc_fail |= (self->histogram == NULL); - - if (malloc_fail) { - WebRtc_FreeBinaryDelayEstimator(self); - self = NULL; - } + self->lookahead = max_lookahead; + + // Allocate memory for spectrum and history buffers. + self->mean_bit_counts = NULL; + self->bit_counts = NULL; + self->histogram = NULL; + self->binary_near_history = + malloc((max_lookahead + 1) * sizeof(*self->binary_near_history)); + if (self->binary_near_history == NULL || + WebRtc_AllocateHistoryBufferMemory(self, farend->history_size) == 0) { + WebRtc_FreeBinaryDelayEstimator(self); + self = NULL; } return self; } +int WebRtc_AllocateHistoryBufferMemory(BinaryDelayEstimator* self, + int history_size) { + BinaryDelayEstimatorFarend* far = self->farend; + // (Re-)Allocate memory for spectrum and history buffers. + if (history_size != far->history_size) { + // Only update far-end buffers if we need. + history_size = WebRtc_AllocateFarendBufferMemory(far, history_size); + } + // The extra array element in |mean_bit_counts| and |histogram| is a dummy + // element only used while |last_delay| == -2, i.e., before we have a valid + // estimate. + self->mean_bit_counts = + realloc(self->mean_bit_counts, + (history_size + 1) * sizeof(*self->mean_bit_counts)); + self->bit_counts = + realloc(self->bit_counts, history_size * sizeof(*self->bit_counts)); + self->histogram = + realloc(self->histogram, (history_size + 1) * sizeof(*self->histogram)); + + if ((self->mean_bit_counts == NULL) || + (self->bit_counts == NULL) || + (self->histogram == NULL)) { + history_size = 0; + } + // Fill with zeros if we have expanded the buffers. + if (history_size > self->history_size) { + int size_diff = history_size - self->history_size; + memset(&self->mean_bit_counts[self->history_size], + 0, + sizeof(*self->mean_bit_counts) * size_diff); + memset(&self->bit_counts[self->history_size], + 0, + sizeof(*self->bit_counts) * size_diff); + memset(&self->histogram[self->history_size], + 0, + sizeof(*self->histogram) * size_diff); + } + self->history_size = history_size; + + return self->history_size; +} + void WebRtc_InitBinaryDelayEstimator(BinaryDelayEstimator* self) { int i = 0; assert(self != NULL); - memset(self->bit_counts, 0, sizeof(int32_t) * self->farend->history_size); - memset(self->binary_near_history, 0, + memset(self->bit_counts, 0, sizeof(int32_t) * self->history_size); + memset(self->binary_near_history, + 0, sizeof(uint32_t) * self->near_history_size); - for (i = 0; i <= self->farend->history_size; ++i) { + for (i = 0; i <= self->history_size; ++i) { self->mean_bit_counts[i] = (20 << 9); // 20 in Q9. self->histogram[i] = 0.f; } - self->minimum_probability = (32 << 9); // 32 in Q9. - self->last_delay_probability = (32 << 9); // 32 in Q9. + self->minimum_probability = kMaxBitCountsQ9; // 32 in Q9. + self->last_delay_probability = (int) kMaxBitCountsQ9; // 32 in Q9. // Default return value if we're unable to estimate. -1 is used for errors. self->last_delay = -2; self->last_candidate_delay = -2; - self->compare_delay = self->farend->history_size; + self->compare_delay = self->history_size; self->candidate_hits = 0; self->last_delay_histogram = 0.f; } +int WebRtc_SoftResetBinaryDelayEstimator(BinaryDelayEstimator* self, + int delay_shift) { + int lookahead = 0; + assert(self != NULL); + lookahead = self->lookahead; + self->lookahead -= delay_shift; + if (self->lookahead < 0) { + self->lookahead = 0; + } + if (self->lookahead > self->near_history_size - 1) { + self->lookahead = self->near_history_size - 1; + } + return lookahead - self->lookahead; +} + int WebRtc_ProcessBinarySpectrum(BinaryDelayEstimator* self, uint32_t binary_near_spectrum) { int i = 0; int candidate_delay = -1; int valid_candidate = 0; - int32_t value_best_candidate = 32 << 9; // 32 in Q9, (max |mean_bit_counts|). + int32_t value_best_candidate = kMaxBitCountsQ9; int32_t value_worst_candidate = 0; int32_t valley_depth = 0; assert(self != NULL); + if (self->farend->history_size != self->history_size) { + // Non matching history sizes. + return -1; + } if (self->near_history_size > 1) { // If we apply lookahead, shift near-end binary spectrum history. Insert // current |binary_near_spectrum| and pull out the delayed one. memmove(&(self->binary_near_history[1]), &(self->binary_near_history[0]), (self->near_history_size - 1) * sizeof(uint32_t)); self->binary_near_history[0] = binary_near_spectrum; - binary_near_spectrum = - self->binary_near_history[self->near_history_size - 1]; + binary_near_spectrum = self->binary_near_history[self->lookahead]; } // Compare with delayed spectra and store the |bit_counts| for each delay. BitCountComparison(binary_near_spectrum, self->farend->binary_far_history, - self->farend->history_size, self->bit_counts); + self->history_size, self->bit_counts); // Update |mean_bit_counts|, which is the smoothed version of |bit_counts|. - for (i = 0; i < self->farend->history_size; i++) { + for (i = 0; i < self->history_size; i++) { // |bit_counts| is constrained to [0, 32], meaning we can smooth with a // factor up to 2^26. We use Q9. int32_t bit_count = (self->bit_counts[i] << 9); // Q9. @@ -454,7 +561,7 @@ // Find |candidate_delay|, |value_best_candidate| and |value_worst_candidate| // of |mean_bit_counts|. - for (i = 0; i < self->farend->history_size; i++) { + for (i = 0; i < self->history_size; i++) { if (self->mean_bit_counts[i] < value_best_candidate) { value_best_candidate = self->mean_bit_counts[i]; candidate_delay = i; @@ -544,21 +651,23 @@ return self->last_delay; } -int WebRtc_binary_last_delay_quality(BinaryDelayEstimator* self) { - int delay_quality = 0; +float WebRtc_binary_last_delay_quality(BinaryDelayEstimator* self) { + float quality = 0; assert(self != NULL); - // |last_delay_probability| is the opposite of quality and states how deep the - // minimum of the cost function is. The value states how many non-matching - // bits we have between the binary spectra for the corresponding delay - // estimate. The range is thus from 0 to 32, since we use 32 bits in the - // binary spectra. - - // Return the |delay_quality| = 1 - |last_delay_probability| / 32 (in Q14). - delay_quality = (32 << 9) - self->last_delay_probability; - if (delay_quality < 0) { - delay_quality = 0; + + if (self->robust_validation_enabled) { + // Simply a linear function of the histogram height at delay estimate. + quality = self->histogram[self->compare_delay] / kHistogramMax; + } else { + // Note that |last_delay_probability| states how deep the minimum of the + // cost function is, so it is rather an error probability. + quality = (float) (kMaxBitCountsQ9 - self->last_delay_probability) / + kMaxBitCountsQ9; + if (quality < 0) { + quality = 0; + } } - return delay_quality; + return quality; } void WebRtc_MeanEstimatorFix(int32_t new_value, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator.h 2015-02-03 14:33:35.000000000 +0000 @@ -16,6 +16,8 @@ #include "webrtc/typedefs.h" +static const int32_t kMaxBitCountsQ9 = (32 << 9); // 32 matching bits in Q9. + typedef struct { // Pointer to bit counts. int* far_bit_counts; @@ -34,6 +36,7 @@ // Binary history variables. uint32_t* binary_near_history; int near_history_size; + int history_size; // Delay estimation variables. int32_t minimum_probability; @@ -51,6 +54,9 @@ float* histogram; float last_delay_histogram; + // For dynamically changing the lookahead when using SoftReset...(). + int lookahead; + // Far-end binary spectrum history buffer etc. BinaryDelayEstimatorFarend* farend; } BinaryDelayEstimator; @@ -80,6 +86,19 @@ BinaryDelayEstimatorFarend* WebRtc_CreateBinaryDelayEstimatorFarend( int history_size); +// Re-allocates the buffers. +// +// Inputs: +// - self : Pointer to the binary estimation far-end instance +// which is the return value of +// WebRtc_CreateBinaryDelayEstimatorFarend(). +// - history_size : Size of the far-end binary spectrum history. +// +// Return value: +// - history_size : The history size allocated. +int WebRtc_AllocateFarendBufferMemory(BinaryDelayEstimatorFarend* self, + int history_size); + // Initializes the delay estimation far-end instance created with // WebRtc_CreateBinaryDelayEstimatorFarend(...). // @@ -91,6 +110,15 @@ // void WebRtc_InitBinaryDelayEstimatorFarend(BinaryDelayEstimatorFarend* self); +// Soft resets the delay estimation far-end instance created with +// WebRtc_CreateBinaryDelayEstimatorFarend(...). +// +// Input: +// - delay_shift : The amount of blocks to shift history buffers. +// +void WebRtc_SoftResetBinaryDelayEstimatorFarend( + BinaryDelayEstimatorFarend* self, int delay_shift); + // Adds the binary far-end spectrum to the internal far-end history buffer. This // spectrum is used as reference when calculating the delay using // WebRtc_ProcessBinarySpectrum(). @@ -122,38 +150,24 @@ // Allocates the memory needed by the binary delay estimation. The memory needs // to be initialized separately through WebRtc_InitBinaryDelayEstimator(...). // -// Inputs: -// - farend : Pointer to the far-end part of the Binary Delay -// Estimator. This memory has to be created separately -// prior to this call using -// WebRtc_CreateBinaryDelayEstimatorFarend(). -// -// Note that BinaryDelayEstimator does not take -// ownership of |farend|. -// -// - lookahead : Amount of non-causal lookahead to use. This can -// detect cases in which a near-end signal occurs before -// the corresponding far-end signal. It will delay the -// estimate for the current block by an equal amount, -// and the returned values will be offset by it. -// -// A value of zero is the typical no-lookahead case. -// This also represents the minimum delay which can be -// estimated. -// -// Note that the effective range of delay estimates is -// [-|lookahead|,... ,|history_size|-|lookahead|) -// where |history_size| was set upon creating the far-end -// history buffer size. +// See WebRtc_CreateDelayEstimator(..) in delay_estimator_wrapper.c for detailed +// description. +BinaryDelayEstimator* WebRtc_CreateBinaryDelayEstimator( + BinaryDelayEstimatorFarend* farend, int max_lookahead); + +// Re-allocates |history_size| dependent buffers. The far-end buffers will be +// updated at the same time if needed. // -// Return value: -// - BinaryDelayEstimator* -// : Created |handle|. If the memory can't be allocated -// or if any of the input parameters are invalid NULL -// is returned. +// Input: +// - self : Pointer to the binary estimation instance which is +// the return value of +// WebRtc_CreateBinaryDelayEstimator(). +// - history_size : Size of the history buffers. // -BinaryDelayEstimator* WebRtc_CreateBinaryDelayEstimator( - BinaryDelayEstimatorFarend* farend, int lookahead); +// Return value: +// - history_size : The history size allocated. +int WebRtc_AllocateHistoryBufferMemory(BinaryDelayEstimator* self, + int history_size); // Initializes the delay estimation instance created with // WebRtc_CreateBinaryDelayEstimator(...). @@ -166,6 +180,18 @@ // void WebRtc_InitBinaryDelayEstimator(BinaryDelayEstimator* self); +// Soft resets the delay estimation instance created with +// WebRtc_CreateBinaryDelayEstimator(...). +// +// Input: +// - delay_shift : The amount of blocks to shift history buffers. +// +// Return value: +// - actual_shifts : The actual number of shifts performed. +// +int WebRtc_SoftResetBinaryDelayEstimator(BinaryDelayEstimator* self, + int delay_shift); + // Estimates and returns the delay between the binary far-end and binary near- // end spectra. It is assumed the binary far-end spectrum has been added using // WebRtc_AddBinaryFarSpectrum() prior to this call. The value will be offset by @@ -200,17 +226,12 @@ // Returns the estimation quality of the last calculated delay updated by the // function WebRtc_ProcessBinarySpectrum(...). The estimation quality is a value -// in the interval [0, 1] in Q14. The higher the value, the better quality. -// -// Input: -// - self : Pointer to the delay estimation instance. +// in the interval [0, 1]. The higher the value, the better the quality. // // Return value: -// - delay_quality : >= 0 - Estimation quality (in Q14) of last -// calculated delay value. -// -2 - Insufficient data for estimation. -// -int WebRtc_binary_last_delay_quality(BinaryDelayEstimator* self); +// - delay_quality : >= 0 - Estimation quality of last calculated +// delay value. +float WebRtc_binary_last_delay_quality(BinaryDelayEstimator* self); // Updates the |mean_value| recursively with a step size of 2^-|factor|. This // function is used internally in the Binary Delay Estimator as well as the @@ -227,5 +248,4 @@ int factor, int32_t* mean_value); - #endif // WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator_unittest.cc 2015-02-03 14:33:35.000000000 +0000 @@ -23,9 +23,13 @@ // Delay history sizes. enum { kMaxDelay = 100 }; enum { kLookahead = 10 }; +enum { kHistorySize = kMaxDelay + kLookahead }; // Length of binary spectrum sequence. enum { kSequenceLength = 400 }; +const int kDifferentHistorySize = 3; +const int kDifferentLookahead = 1; + const int kEnable[] = { 0, 1 }; const size_t kSizeEnable = sizeof(kEnable) / sizeof(*kEnable); @@ -56,7 +60,7 @@ float near_f_[kSpectrumSize]; uint16_t far_u16_[kSpectrumSize]; uint16_t near_u16_[kSpectrumSize]; - uint32_t binary_spectrum_[kSequenceLength + kMaxDelay + kLookahead]; + uint32_t binary_spectrum_[kSequenceLength + kHistorySize]; }; DelayEstimatorTest::DelayEstimatorTest() @@ -76,21 +80,20 @@ // |kSequenceLength| has to be long enough for the delay estimation to leave // the initialized state. binary_spectrum_[0] = 1; - for (int i = 1; i < (kSequenceLength + kMaxDelay + kLookahead); i++) { + for (int i = 1; i < (kSequenceLength + kHistorySize); i++) { binary_spectrum_[i] = 3 * binary_spectrum_[i - 1]; } } void DelayEstimatorTest::SetUp() { farend_handle_ = WebRtc_CreateDelayEstimatorFarend(kSpectrumSize, - kMaxDelay + kLookahead); + kHistorySize); ASSERT_TRUE(farend_handle_ != NULL); farend_self_ = reinterpret_cast(farend_handle_); handle_ = WebRtc_CreateDelayEstimator(farend_handle_, kLookahead); ASSERT_TRUE(handle_ != NULL); self_ = reinterpret_cast(handle_); - binary_farend_ = WebRtc_CreateBinaryDelayEstimatorFarend(kMaxDelay + - kLookahead); + binary_farend_ = WebRtc_CreateBinaryDelayEstimatorFarend(kHistorySize); ASSERT_TRUE(binary_farend_ != NULL); binary_ = WebRtc_CreateBinaryDelayEstimator(binary_farend_, kLookahead); ASSERT_TRUE(binary_ != NULL); @@ -117,7 +120,7 @@ EXPECT_EQ(0, farend_self_->far_spectrum_initialized); EXPECT_EQ(0, self_->near_spectrum_initialized); EXPECT_EQ(-2, WebRtc_last_delay(handle_)); // Delay in initial state. - EXPECT_EQ(0, WebRtc_last_delay_quality(handle_)); // Zero quality. + EXPECT_FLOAT_EQ(0, WebRtc_last_delay_quality(handle_)); // Zero quality. } void DelayEstimatorTest::InitBinary() { @@ -190,9 +193,9 @@ } // Verify that we have left the initialized state. EXPECT_NE(-2, WebRtc_binary_last_delay(binary1)); - EXPECT_NE(0, WebRtc_binary_last_delay_quality(binary1)); + EXPECT_LT(0, WebRtc_binary_last_delay_quality(binary1)); EXPECT_NE(-2, WebRtc_binary_last_delay(binary2)); - EXPECT_NE(0, WebRtc_binary_last_delay_quality(binary2)); + EXPECT_LT(0, WebRtc_binary_last_delay_quality(binary2)); } void DelayEstimatorTest::RunBinarySpectraTest(int near_offset, @@ -226,16 +229,14 @@ // Make sure we have a non-NULL value at start, so we can detect NULL after // create failure. void* handle = farend_handle_; - handle = WebRtc_CreateDelayEstimatorFarend(33, kMaxDelay + kLookahead); + handle = WebRtc_CreateDelayEstimatorFarend(33, kHistorySize); EXPECT_TRUE(handle == NULL); - handle = farend_handle_; handle = WebRtc_CreateDelayEstimatorFarend(kSpectrumSize, 1); EXPECT_TRUE(handle == NULL); handle = handle_; handle = WebRtc_CreateDelayEstimator(NULL, kLookahead); EXPECT_TRUE(handle == NULL); - handle = handle_; handle = WebRtc_CreateDelayEstimator(farend_handle_, -1); EXPECT_TRUE(handle == NULL); @@ -268,6 +269,28 @@ EXPECT_EQ(-1, WebRtc_AddFarSpectrumFix(farend_handle_, far_u16_, spectrum_size_, 16)); + // WebRtc_set_history_size() should return -1 if: + // 1) |handle| is a NULL. + // 2) |history_size| <= 1. + EXPECT_EQ(-1, WebRtc_set_history_size(NULL, 1)); + EXPECT_EQ(-1, WebRtc_set_history_size(handle_, 1)); + // WebRtc_history_size() should return -1 if: + // 1) NULL pointer input. + EXPECT_EQ(-1, WebRtc_history_size(NULL)); + // 2) there is a mismatch between history size. + void* tmp_handle = WebRtc_CreateDelayEstimator(farend_handle_, kHistorySize); + EXPECT_EQ(0, WebRtc_InitDelayEstimator(tmp_handle)); + EXPECT_EQ(kDifferentHistorySize, + WebRtc_set_history_size(tmp_handle, kDifferentHistorySize)); + EXPECT_EQ(kDifferentHistorySize, WebRtc_history_size(tmp_handle)); + EXPECT_EQ(kHistorySize, WebRtc_set_history_size(handle_, kHistorySize)); + EXPECT_EQ(-1, WebRtc_history_size(tmp_handle)); + + // WebRtc_set_lookahead() should return -1 if we try a value outside the + /// buffer. + EXPECT_EQ(-1, WebRtc_set_lookahead(handle_, kLookahead + 1)); + EXPECT_EQ(-1, WebRtc_set_lookahead(handle_, -1)); + // WebRtc_set_allowed_offset() should return -1 if we have: // 1) NULL pointer as |handle|. // 2) |allowed_offset| < 0. @@ -291,6 +314,8 @@ // 1) NULL pointer as |handle|. // 2) NULL pointer as near-end spectrum. // 3) Incorrect spectrum size. + // 4) Non matching history sizes if multiple delay estimators using the same + // far-end reference. EXPECT_EQ(-1, WebRtc_DelayEstimatorProcessFloat(NULL, near_f_, spectrum_size_)); // Use |handle_| which is properly created at SetUp(). @@ -298,12 +323,18 @@ spectrum_size_)); EXPECT_EQ(-1, WebRtc_DelayEstimatorProcessFloat(handle_, near_f_, spectrum_size_ + 1)); + // |tmp_handle| is already in a non-matching state. + EXPECT_EQ(-1, WebRtc_DelayEstimatorProcessFloat(tmp_handle, + near_f_, + spectrum_size_)); // WebRtc_DelayEstimatorProcessFix() should return -1 if we have: // 1) NULL pointer as |handle|. - // 3) NULL pointer as near-end spectrum. - // 4) Incorrect spectrum size. - // 6) Too high precision in near-end spectrum (Q-domain > 15). + // 2) NULL pointer as near-end spectrum. + // 3) Incorrect spectrum size. + // 4) Too high precision in near-end spectrum (Q-domain > 15). + // 5) Non matching history sizes if multiple delay estimators using the same + // far-end reference. EXPECT_EQ(-1, WebRtc_DelayEstimatorProcessFix(NULL, near_u16_, spectrum_size_, 0)); EXPECT_EQ(-1, WebRtc_DelayEstimatorProcessFix(handle_, NULL, spectrum_size_, @@ -312,14 +343,16 @@ spectrum_size_ + 1, 0)); EXPECT_EQ(-1, WebRtc_DelayEstimatorProcessFix(handle_, near_u16_, spectrum_size_, 16)); + // |tmp_handle| is already in a non-matching state. + EXPECT_EQ(-1, WebRtc_DelayEstimatorProcessFix(tmp_handle, + near_u16_, + spectrum_size_, + 0)); + WebRtc_FreeDelayEstimator(tmp_handle); // WebRtc_last_delay() should return -1 if we have a NULL pointer as |handle|. EXPECT_EQ(-1, WebRtc_last_delay(NULL)); - // WebRtc_last_delay_quality() should return -1 if we have a NULL pointer as - // |handle|. - EXPECT_EQ(-1, WebRtc_last_delay_quality(NULL)); - // Free any local memory if needed. WebRtc_FreeDelayEstimator(handle); } @@ -350,14 +383,23 @@ TEST_F(DelayEstimatorTest, InitializedSpectrumAfterProcess) { // In this test we verify that the mean spectra are initialized after first - // time we call WebRtc_AddFarSpectrum() and Process() respectively. + // time we call WebRtc_AddFarSpectrum() and Process() respectively. The test + // also verifies the state is not left for zero spectra. + const float kZerosFloat[kSpectrumSize] = { 0.0 }; + const uint16_t kZerosU16[kSpectrumSize] = { 0 }; // For floating point operations, process one frame and verify initialization // flag. Init(); + EXPECT_EQ(0, WebRtc_AddFarSpectrumFloat(farend_handle_, kZerosFloat, + spectrum_size_)); + EXPECT_EQ(0, farend_self_->far_spectrum_initialized); EXPECT_EQ(0, WebRtc_AddFarSpectrumFloat(farend_handle_, far_f_, spectrum_size_)); EXPECT_EQ(1, farend_self_->far_spectrum_initialized); + EXPECT_EQ(-2, WebRtc_DelayEstimatorProcessFloat(handle_, kZerosFloat, + spectrum_size_)); + EXPECT_EQ(0, self_->near_spectrum_initialized); EXPECT_EQ(-2, WebRtc_DelayEstimatorProcessFloat(handle_, near_f_, spectrum_size_)); EXPECT_EQ(1, self_->near_spectrum_initialized); @@ -365,9 +407,15 @@ // For fixed point operations, process one frame and verify initialization // flag. Init(); + EXPECT_EQ(0, WebRtc_AddFarSpectrumFix(farend_handle_, kZerosU16, + spectrum_size_, 0)); + EXPECT_EQ(0, farend_self_->far_spectrum_initialized); EXPECT_EQ(0, WebRtc_AddFarSpectrumFix(farend_handle_, far_u16_, spectrum_size_, 0)); EXPECT_EQ(1, farend_self_->far_spectrum_initialized); + EXPECT_EQ(-2, WebRtc_DelayEstimatorProcessFix(handle_, kZerosU16, + spectrum_size_, 0)); + EXPECT_EQ(0, self_->near_spectrum_initialized); EXPECT_EQ(-2, WebRtc_DelayEstimatorProcessFix(handle_, near_u16_, spectrum_size_, 0)); EXPECT_EQ(1, self_->near_spectrum_initialized); @@ -379,6 +427,7 @@ // (|last_delay| = -2). Then we compare the Process() output with the // last_delay() call. + // TODO(bjornv): Update quality values for robust validation. int last_delay = 0; // Floating point operations. Init(); @@ -389,13 +438,16 @@ spectrum_size_); if (last_delay != -2) { EXPECT_EQ(last_delay, WebRtc_last_delay(handle_)); - EXPECT_EQ(7203, WebRtc_last_delay_quality(handle_)); + if (!WebRtc_is_robust_validation_enabled(handle_)) { + EXPECT_FLOAT_EQ(7203.f / kMaxBitCountsQ9, + WebRtc_last_delay_quality(handle_)); + } break; } } // Verify that we have left the initialized state. EXPECT_NE(-2, WebRtc_last_delay(handle_)); - EXPECT_NE(0, WebRtc_last_delay_quality(handle_)); + EXPECT_LT(0, WebRtc_last_delay_quality(handle_)); // Fixed point operations. Init(); @@ -406,13 +458,16 @@ spectrum_size_, 0); if (last_delay != -2) { EXPECT_EQ(last_delay, WebRtc_last_delay(handle_)); - EXPECT_EQ(7203, WebRtc_last_delay_quality(handle_)); + if (!WebRtc_is_robust_validation_enabled(handle_)) { + EXPECT_FLOAT_EQ(7203.f / kMaxBitCountsQ9, + WebRtc_last_delay_quality(handle_)); + } break; } } // Verify that we have left the initialized state. EXPECT_NE(-2, WebRtc_last_delay(handle_)); - EXPECT_NE(0, WebRtc_last_delay_quality(handle_)); + EXPECT_LT(0, WebRtc_last_delay_quality(handle_)); } TEST_F(DelayEstimatorTest, CorrectErrorReturnsOfBinaryEstimatorFarend) { @@ -441,7 +496,6 @@ // create failure. binary_handle = WebRtc_CreateBinaryDelayEstimator(NULL, kLookahead); EXPECT_TRUE(binary_handle == NULL); - binary_handle = binary_; binary_handle = WebRtc_CreateBinaryDelayEstimator(binary_farend_, -1); EXPECT_TRUE(binary_handle == NULL); } @@ -525,4 +579,43 @@ binary_->allowed_offset = 0; // Reset reference. } +TEST_F(DelayEstimatorTest, VerifyLookaheadAtCreate) { + void* farend_handle = WebRtc_CreateDelayEstimatorFarend(kSpectrumSize, + kMaxDelay); + ASSERT_TRUE(farend_handle != NULL); + void* handle = WebRtc_CreateDelayEstimator(farend_handle, kLookahead); + ASSERT_TRUE(handle != NULL); + EXPECT_EQ(kLookahead, WebRtc_lookahead(handle)); + WebRtc_FreeDelayEstimator(handle); + WebRtc_FreeDelayEstimatorFarend(farend_handle); +} + +TEST_F(DelayEstimatorTest, VerifyLookaheadIsSetAndKeptAfterInit) { + EXPECT_EQ(kLookahead, WebRtc_lookahead(handle_)); + EXPECT_EQ(kDifferentLookahead, + WebRtc_set_lookahead(handle_, kDifferentLookahead)); + EXPECT_EQ(kDifferentLookahead, WebRtc_lookahead(handle_)); + EXPECT_EQ(0, WebRtc_InitDelayEstimatorFarend(farend_handle_)); + EXPECT_EQ(kDifferentLookahead, WebRtc_lookahead(handle_)); + EXPECT_EQ(0, WebRtc_InitDelayEstimator(handle_)); + EXPECT_EQ(kDifferentLookahead, WebRtc_lookahead(handle_)); +} + +TEST_F(DelayEstimatorTest, VerifyHistorySizeAtCreate) { + EXPECT_EQ(kHistorySize, WebRtc_history_size(handle_)); +} + +TEST_F(DelayEstimatorTest, VerifyHistorySizeIsSetAndKeptAfterInit) { + EXPECT_EQ(kHistorySize, WebRtc_history_size(handle_)); + EXPECT_EQ(kDifferentHistorySize, + WebRtc_set_history_size(handle_, kDifferentHistorySize)); + EXPECT_EQ(kDifferentHistorySize, WebRtc_history_size(handle_)); + EXPECT_EQ(0, WebRtc_InitDelayEstimator(handle_)); + EXPECT_EQ(kDifferentHistorySize, WebRtc_history_size(handle_)); + EXPECT_EQ(0, WebRtc_InitDelayEstimatorFarend(farend_handle_)); + EXPECT_EQ(kDifferentHistorySize, WebRtc_history_size(handle_)); +} + +// TODO(bjornv): Add tests for SoftReset...(...). + } // namespace diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.c thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.c --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.c 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.c 2015-02-03 14:33:35.000000000 +0000 @@ -58,7 +58,7 @@ // Return: // - out : Binary spectrum. // -static uint32_t BinarySpectrumFix(uint16_t* spectrum, +static uint32_t BinarySpectrumFix(const uint16_t* spectrum, SpectrumType* threshold_spectrum, int q_domain, int* threshold_initialized) { @@ -93,7 +93,7 @@ return out; } -static uint32_t BinarySpectrumFloat(float* spectrum, +static uint32_t BinarySpectrumFloat(const float* spectrum, SpectrumType* threshold_spectrum, int* threshold_initialized) { int i = kBandFirst; @@ -147,7 +147,7 @@ COMPILE_ASSERT(kBandLast - kBandFirst < 32); if (spectrum_size >= kBandLast) { - self = malloc(sizeof(DelayEstimator)); + self = malloc(sizeof(DelayEstimatorFarend)); } if (self != NULL) { @@ -191,8 +191,16 @@ return 0; } -int WebRtc_AddFarSpectrumFix(void* handle, uint16_t* far_spectrum, - int spectrum_size, int far_q) { +void WebRtc_SoftResetDelayEstimatorFarend(void* handle, int delay_shift) { + DelayEstimatorFarend* self = (DelayEstimatorFarend*) handle; + assert(self != NULL); + WebRtc_SoftResetBinaryDelayEstimatorFarend(self->binary_farend, delay_shift); +} + +int WebRtc_AddFarSpectrumFix(void* handle, + const uint16_t* far_spectrum, + int spectrum_size, + int far_q) { DelayEstimatorFarend* self = (DelayEstimatorFarend*) handle; uint32_t binary_spectrum = 0; @@ -220,7 +228,8 @@ return 0; } -int WebRtc_AddFarSpectrumFloat(void* handle, float* far_spectrum, +int WebRtc_AddFarSpectrumFloat(void* handle, + const float* far_spectrum, int spectrum_size) { DelayEstimatorFarend* self = (DelayEstimatorFarend*) handle; uint32_t binary_spectrum = 0; @@ -261,7 +270,7 @@ free(self); } -void* WebRtc_CreateDelayEstimator(void* farend_handle, int lookahead) { +void* WebRtc_CreateDelayEstimator(void* farend_handle, int max_lookahead) { DelayEstimator* self = NULL; DelayEstimatorFarend* farend = (DelayEstimatorFarend*) farend_handle; @@ -274,7 +283,7 @@ // Allocate memory for the farend spectrum handling. self->binary_handle = - WebRtc_CreateBinaryDelayEstimator(farend->binary_farend, lookahead); + WebRtc_CreateBinaryDelayEstimator(farend->binary_farend, max_lookahead); memory_fail |= (self->binary_handle == NULL); // Allocate memory for spectrum buffers. @@ -312,6 +321,54 @@ return 0; } +int WebRtc_SoftResetDelayEstimator(void* handle, int delay_shift) { + DelayEstimator* self = (DelayEstimator*) handle; + assert(self != NULL); + return WebRtc_SoftResetBinaryDelayEstimator(self->binary_handle, delay_shift); +} + +int WebRtc_set_history_size(void* handle, int history_size) { + DelayEstimator* self = handle; + + if ((self == NULL) || (history_size <= 1)) { + return -1; + } + return WebRtc_AllocateHistoryBufferMemory(self->binary_handle, history_size); +} + +int WebRtc_history_size(const void* handle) { + const DelayEstimator* self = handle; + + if (self == NULL) { + return -1; + } + if (self->binary_handle->farend->history_size != + self->binary_handle->history_size) { + // Non matching history sizes. + return -1; + } + return self->binary_handle->history_size; +} + +int WebRtc_set_lookahead(void* handle, int lookahead) { + DelayEstimator* self = (DelayEstimator*) handle; + assert(self != NULL); + assert(self->binary_handle != NULL); + if ((lookahead > self->binary_handle->near_history_size - 1) || + (lookahead < 0)) { + return -1; + } + self->binary_handle->lookahead = lookahead; + return self->binary_handle->lookahead; +} + +int WebRtc_lookahead(void* handle) { + DelayEstimator* self = (DelayEstimator*) handle; + assert(self != NULL); + assert(self->binary_handle != NULL); + return self->binary_handle->lookahead; +} + int WebRtc_set_allowed_offset(void* handle, int allowed_offset) { DelayEstimator* self = (DelayEstimator*) handle; @@ -340,6 +397,7 @@ if ((enable < 0) || (enable > 1)) { return -1; } + assert(self->binary_handle != NULL); self->binary_handle->robust_validation_enabled = enable; return 0; } @@ -354,7 +412,7 @@ } int WebRtc_DelayEstimatorProcessFix(void* handle, - uint16_t* near_spectrum, + const uint16_t* near_spectrum, int spectrum_size, int near_q) { DelayEstimator* self = (DelayEstimator*) handle; @@ -386,7 +444,7 @@ } int WebRtc_DelayEstimatorProcessFloat(void* handle, - float* near_spectrum, + const float* near_spectrum, int spectrum_size) { DelayEstimator* self = (DelayEstimator*) handle; uint32_t binary_spectrum = 0; @@ -420,12 +478,8 @@ return WebRtc_binary_last_delay(self->binary_handle); } -int WebRtc_last_delay_quality(void* handle) { +float WebRtc_last_delay_quality(void* handle) { DelayEstimator* self = (DelayEstimator*) handle; - - if (self == NULL) { - return -1; - } - + assert(self != NULL); return WebRtc_binary_last_delay_quality(self->binary_handle); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h 2015-02-03 14:33:35.000000000 +0000 @@ -17,9 +17,6 @@ #include "webrtc/typedefs.h" // Releases the memory allocated by WebRtc_CreateDelayEstimatorFarend(...) -// Input: -// - handle : Pointer to the delay estimation far-end instance. -// void WebRtc_FreeDelayEstimatorFarend(void* handle); // Allocates the memory needed by the far-end part of the delay estimation. The @@ -27,37 +24,35 @@ // WebRtc_InitDelayEstimatorFarend(...). // // Inputs: -// - spectrum_size : Size of the spectrum used both in far-end and +// - spectrum_size : Size of the spectrum used both in far-end and // near-end. Used to allocate memory for spectrum // specific buffers. -// - history_size : The far-end history buffer size. Note that the maximum -// delay which can be estimated is controlled together -// with |lookahead| through -// WebRtc_CreateDelayEstimator(). +// - history_size : The far-end history buffer size. A change in buffer +// size can be forced with WebRtc_set_history_size(). +// Note that the maximum delay which can be estimated is +// determined together with WebRtc_set_lookahead(). // // Return value: -// - void* : Created |handle|. If the memory can't be allocated or +// - void* : Created |handle|. If the memory can't be allocated or // if any of the input parameters are invalid NULL is // returned. -// void* WebRtc_CreateDelayEstimatorFarend(int spectrum_size, int history_size); // Initializes the far-end part of the delay estimation instance returned by // WebRtc_CreateDelayEstimatorFarend(...) -// Input: -// - handle : Pointer to the delay estimation far-end instance. -// -// Output: -// - handle : Initialized instance. -// int WebRtc_InitDelayEstimatorFarend(void* handle); +// Soft resets the far-end part of the delay estimation instance returned by +// WebRtc_CreateDelayEstimatorFarend(...). +// Input: +// - delay_shift : The amount of blocks to shift history buffers. +void WebRtc_SoftResetDelayEstimatorFarend(void* handle, int delay_shift); + // Adds the far-end spectrum to the far-end history buffer. This spectrum is // used as reference when calculating the delay using // WebRtc_ProcessSpectrum(). // // Inputs: -// - handle : Pointer to the delay estimation far-end instance. // - far_spectrum : Far-end spectrum. // - spectrum_size : The size of the data arrays (same for both far- and // near-end). @@ -66,17 +61,17 @@ // Output: // - handle : Updated far-end instance. // -int WebRtc_AddFarSpectrumFix(void* handle, uint16_t* far_spectrum, - int spectrum_size, int far_q); +int WebRtc_AddFarSpectrumFix(void* handle, + const uint16_t* far_spectrum, + int spectrum_size, + int far_q); // See WebRtc_AddFarSpectrumFix() for description. -int WebRtc_AddFarSpectrumFloat(void* handle, float* far_spectrum, +int WebRtc_AddFarSpectrumFloat(void* handle, + const float* far_spectrum, int spectrum_size); // Releases the memory allocated by WebRtc_CreateDelayEstimator(...) -// Input: -// - handle : Pointer to the delay estimation instance. -// void WebRtc_FreeDelayEstimator(void* handle); // Allocates the memory needed by the delay estimation. The memory needs to be @@ -91,11 +86,18 @@ // ownership of |farend_handle|, which has to be torn // down properly after this instance. // -// - lookahead : Amount of non-causal lookahead to use. This can -// detect cases in which a near-end signal occurs before -// the corresponding far-end signal. It will delay the -// estimate for the current block by an equal amount, -// and the returned values will be offset by it. +// - max_lookahead : Maximum amount of non-causal lookahead allowed. The +// actual amount of lookahead used can be controlled by +// WebRtc_set_lookahead(...). The default |lookahead| is +// set to |max_lookahead| at create time. Use +// WebRtc_set_lookahead(...) before start if a different +// value is desired. +// +// Using lookahead can detect cases in which a near-end +// signal occurs before the corresponding far-end signal. +// It will delay the estimate for the current block by an +// equal amount, and the returned values will be offset +// by it. // // A value of zero is the typical no-lookahead case. // This also represents the minimum delay which can be @@ -103,25 +105,65 @@ // // Note that the effective range of delay estimates is // [-|lookahead|,... ,|history_size|-|lookahead|) -// where |history_size| was set upon creating the far-end -// history buffer size. +// where |history_size| is set through +// WebRtc_set_history_size(). // // Return value: // - void* : Created |handle|. If the memory can't be allocated or // if any of the input parameters are invalid NULL is // returned. -// -void* WebRtc_CreateDelayEstimator(void* farend_handle, int lookahead); +void* WebRtc_CreateDelayEstimator(void* farend_handle, int max_lookahead); // Initializes the delay estimation instance returned by // WebRtc_CreateDelayEstimator(...) +int WebRtc_InitDelayEstimator(void* handle); + +// Soft resets the delay estimation instance returned by +// WebRtc_CreateDelayEstimator(...) +// Input: +// - delay_shift : The amount of blocks to shift history buffers. +// +// Return value: +// - actual_shifts : The actual number of shifts performed. +int WebRtc_SoftResetDelayEstimator(void* handle, int delay_shift); + +// Sets the effective |history_size| used. Valid values from 2. We simply need +// at least two delays to compare to perform an estimate. If |history_size| is +// changed, buffers are reallocated filling in with zeros if necessary. +// Note that changing the |history_size| affects both buffers in far-end and +// near-end. Hence it is important to change all DelayEstimators that use the +// same reference far-end, to the same |history_size| value. +// Inputs: +// - handle : Pointer to the delay estimation instance. +// - history_size : Effective history size to be used. +// Return value: +// - new_history_size : The new history size used. If the memory was not able +// to be allocated 0 is returned. +int WebRtc_set_history_size(void* handle, int history_size); + +// Returns the history_size currently used. // Input: // - handle : Pointer to the delay estimation instance. +int WebRtc_history_size(const void* handle); + +// Sets the amount of |lookahead| to use. Valid values are [0, max_lookahead] +// where |max_lookahead| was set at create time through +// WebRtc_CreateDelayEstimator(...). // -// Output: -// - handle : Initialized instance. +// Input: +// - handle : Pointer to the delay estimation instance. +// - lookahead : The amount of lookahead to be used. // -int WebRtc_InitDelayEstimator(void* handle); +// Return value: +// - new_lookahead : The actual amount of lookahead set, unless |handle| is +// a NULL pointer or |lookahead| is invalid, for which an +// error is returned. +int WebRtc_set_lookahead(void* handle, int lookahead); + +// Returns the amount of lookahead we currently use. +// Input: +// - handle : Pointer to the delay estimation instance. +int WebRtc_lookahead(void* handle); // Sets the |allowed_offset| used in the robust validation scheme. If the // delay estimator is used in an echo control component, this parameter is @@ -139,8 +181,6 @@ // Returns the |allowed_offset| in number of partitions. int WebRtc_get_allowed_offset(const void* handle); -// TODO(bjornv): Implement this functionality. Currently, enabling it has no -// impact, hence this is an empty API. // Enables/Disables a robust validation functionality in the delay estimation. // This is by default set to disabled at create time. The state is preserved // over a reset. @@ -170,15 +210,14 @@ // - delay : >= 0 - Calculated delay value. // -1 - Error. // -2 - Insufficient data for estimation. -// int WebRtc_DelayEstimatorProcessFix(void* handle, - uint16_t* near_spectrum, + const uint16_t* near_spectrum, int spectrum_size, int near_q); // See WebRtc_DelayEstimatorProcessFix() for description. int WebRtc_DelayEstimatorProcessFloat(void* handle, - float* near_spectrum, + const float* near_spectrum, int spectrum_size); // Returns the last calculated delay updated by the function @@ -191,23 +230,15 @@ // - delay : >= 0 - Last calculated delay value. // -1 - Error. // -2 - Insufficient data for estimation. -// int WebRtc_last_delay(void* handle); // Returns the estimation quality/probability of the last calculated delay // updated by the function WebRtc_DelayEstimatorProcess(...). The estimation -// quality is a value in the interval [0, 1] in Q9. The higher the value, the -// better quality. -// -// Input: -// - handle : Pointer to the delay estimation instance. +// quality is a value in the interval [0, 1]. The higher the value, the better +// the quality. // // Return value: -// - delay_quality : >= 0 - Estimation quality (in Q9) of last calculated -// delay value. -// -1 - Error. -// -2 - Insufficient data for estimation. -// -int WebRtc_last_delay_quality(void* handle); +// - delay_quality : >= 0 - Estimation quality of last calculated delay. +float WebRtc_last_delay_quality(void* handle); #endif // WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_WRAPPER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/ring_buffer_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/ring_buffer_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/ring_buffer_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/utility/ring_buffer_unittest.cc 2015-02-03 14:33:35.000000000 +0000 @@ -22,7 +22,12 @@ namespace webrtc { -typedef scoped_ptr_malloc scoped_ring_buffer; +struct FreeBufferDeleter { + inline void operator()(void* ptr) const { + WebRtc_FreeBuffer(ptr); + } +}; +typedef scoped_ptr scoped_ring_buffer; static void AssertElementEq(int expected, int actual) { ASSERT_EQ(expected, actual); @@ -47,8 +52,8 @@ // We use ASSERTs in this test to avoid obscuring the seed in the case of a // failure. static void RandomStressTest(int** data_ptr) { - const int kNumTests = 100; - const int kNumOps = 10000; + const int kNumTests = 10; + const int kNumOps = 1000; const int kMaxBufferSize = 1000; unsigned int seed = time(NULL); @@ -56,8 +61,8 @@ srand(seed); for (int i = 0; i < kNumTests; i++) { const int buffer_size = std::max(rand() % kMaxBufferSize, 1); - scoped_array write_data(new int[buffer_size]); - scoped_array read_data(new int[buffer_size]); + scoped_ptr write_data(new int[buffer_size]); + scoped_ptr read_data(new int[buffer_size]); scoped_ring_buffer buffer(WebRtc_CreateBuffer(buffer_size, sizeof(int))); ASSERT_TRUE(buffer.get() != NULL); ASSERT_EQ(0, WebRtc_InitBuffer(buffer.get())); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/voice_detection_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/voice_detection_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/voice_detection_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/voice_detection_impl.cc 2015-02-03 14:33:35.000000000 +0000 @@ -13,10 +13,8 @@ #include #include "webrtc/common_audio/vad/include/webrtc_vad.h" -#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" - #include "webrtc/modules/audio_processing/audio_buffer.h" -#include "webrtc/modules/audio_processing/audio_processing_impl.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" namespace webrtc { @@ -39,9 +37,11 @@ } } // namespace -VoiceDetectionImpl::VoiceDetectionImpl(const AudioProcessingImpl* apm) - : ProcessingComponent(apm), +VoiceDetectionImpl::VoiceDetectionImpl(const AudioProcessing* apm, + CriticalSectionWrapper* crit) + : ProcessingComponent(), apm_(apm), + crit_(crit), stream_has_voice_(false), using_external_vad_(false), likelihood_(kLowLikelihood), @@ -61,17 +61,11 @@ } assert(audio->samples_per_split_channel() <= 160); - int16_t* mixed_data = audio->low_pass_split_data(0); - if (audio->num_channels() > 1) { - audio->CopyAndMixLowPass(1); - mixed_data = audio->mixed_low_pass_data(0); - } - // TODO(ajm): concatenate data in frame buffer here. int vad_ret = WebRtcVad_Process(static_cast(handle(0)), - apm_->split_sample_rate_hz(), - mixed_data, + apm_->proc_split_sample_rate_hz(), + audio->mixed_low_pass_data(), frame_size_samples_); if (vad_ret == 0) { stream_has_voice_ = false; @@ -87,7 +81,7 @@ } int VoiceDetectionImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); return EnableComponent(enable); } @@ -108,7 +102,7 @@ } int VoiceDetectionImpl::set_likelihood(VoiceDetection::Likelihood likelihood) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); if (MapSetting(likelihood) == -1) { return apm_->kBadParameterError; } @@ -122,7 +116,7 @@ } int VoiceDetectionImpl::set_frame_size_ms(int size) { - CriticalSectionScoped crit_scoped(apm_->crit()); + CriticalSectionScoped crit_scoped(crit_); assert(size == 10); // TODO(ajm): remove when supported. if (size != 10 && size != 20 && @@ -146,7 +140,8 @@ } using_external_vad_ = false; - frame_size_samples_ = frame_size_ms_ * (apm_->split_sample_rate_hz() / 1000); + frame_size_samples_ = frame_size_ms_ * + apm_->proc_split_sample_rate_hz() / 1000; // TODO(ajm): intialize frame buffer here. return apm_->kNoError; @@ -163,8 +158,8 @@ return handle; } -int VoiceDetectionImpl::DestroyHandle(void* handle) const { - return WebRtcVad_Free(static_cast(handle)); +void VoiceDetectionImpl::DestroyHandle(void* handle) const { + WebRtcVad_Free(static_cast(handle)); } int VoiceDetectionImpl::InitializeHandle(void* handle) const { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/voice_detection_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/voice_detection_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/voice_detection_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/audio_processing/voice_detection_impl.h 2015-02-03 14:33:35.000000000 +0000 @@ -15,13 +15,14 @@ #include "webrtc/modules/audio_processing/processing_component.h" namespace webrtc { -class AudioProcessingImpl; + class AudioBuffer; +class CriticalSectionWrapper; class VoiceDetectionImpl : public VoiceDetection, public ProcessingComponent { public: - explicit VoiceDetectionImpl(const AudioProcessingImpl* apm); + VoiceDetectionImpl(const AudioProcessing* apm, CriticalSectionWrapper* crit); virtual ~VoiceDetectionImpl(); int ProcessCaptureAudio(AudioBuffer* audio); @@ -46,11 +47,12 @@ virtual void* CreateHandle() const OVERRIDE; virtual int InitializeHandle(void* handle) const OVERRIDE; virtual int ConfigureHandle(void* handle) const OVERRIDE; - virtual int DestroyHandle(void* handle) const OVERRIDE; + virtual void DestroyHandle(void* handle) const OVERRIDE; virtual int num_handles_required() const OVERRIDE; virtual int GetHandleError(void* handle) const OVERRIDE; - const AudioProcessingImpl* apm_; + const AudioProcessing* apm_; + CriticalSectionWrapper* crit_; bool stream_has_voice_; bool using_external_vad_; Likelihood likelihood_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/bitrate_controller_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/bitrate_controller_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/bitrate_controller_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/bitrate_controller_impl.cc 2015-02-03 14:33:35.000000000 +0000 @@ -18,7 +18,8 @@ namespace webrtc { -class RtcpBandwidthObserverImpl : public RtcpBandwidthObserver { +class BitrateControllerImpl::RtcpBandwidthObserverImpl + : public RtcpBandwidthObserver { public: explicit RtcpBandwidthObserverImpl(BitrateControllerImpl* owner) : owner_(owner) { @@ -76,94 +77,29 @@ BitrateControllerImpl* owner_; }; -class LowRateStrategy { - public: - LowRateStrategy( - SendSideBandwidthEstimation* bandwidth_estimation, - BitrateControllerImpl::BitrateObserverConfList* bitrate_observers) - : bandwidth_estimation_(bandwidth_estimation), - bitrate_observers_(bitrate_observers) {} - - virtual ~LowRateStrategy() {} - - virtual void LowRateAllocation(uint32_t bitrate, - uint8_t fraction_loss, - uint32_t rtt, - uint32_t sum_min_bitrates) = 0; - - protected: - SendSideBandwidthEstimation* bandwidth_estimation_; - BitrateControllerImpl::BitrateObserverConfList* bitrate_observers_; -}; - -class EnforceMinRateStrategy : public LowRateStrategy { - public: - EnforceMinRateStrategy( - SendSideBandwidthEstimation* bandwidth_estimation, - BitrateControllerImpl::BitrateObserverConfList* bitrate_observers) - : LowRateStrategy(bandwidth_estimation, bitrate_observers) {} - - void LowRateAllocation(uint32_t bitrate, - uint8_t fraction_loss, - uint32_t rtt, - uint32_t sum_min_bitrates) { - // Min bitrate to all observers. - BitrateControllerImpl::BitrateObserverConfList::iterator it; - for (it = bitrate_observers_->begin(); it != bitrate_observers_->end(); - ++it) { - it->first->OnNetworkChanged(it->second->min_bitrate_, fraction_loss, - rtt); - } - // Set sum of min to current send bitrate. - bandwidth_estimation_->SetSendBitrate(sum_min_bitrates); - } -}; - -class NoEnforceMinRateStrategy : public LowRateStrategy { - public: - NoEnforceMinRateStrategy( - SendSideBandwidthEstimation* bandwidth_estimation, - BitrateControllerImpl::BitrateObserverConfList* bitrate_observers) - : LowRateStrategy(bandwidth_estimation, bitrate_observers) {} - - void LowRateAllocation(uint32_t bitrate, - uint8_t fraction_loss, - uint32_t rtt, - uint32_t sum_min_bitrates) { - // Allocate up to |min_bitrate_| to one observer at a time, until - // |bitrate| is depleted. - uint32_t remainder = bitrate; - BitrateControllerImpl::BitrateObserverConfList::iterator it; - for (it = bitrate_observers_->begin(); it != bitrate_observers_->end(); - ++it) { - uint32_t allocation = std::min(remainder, it->second->min_bitrate_); - it->first->OnNetworkChanged(allocation, fraction_loss, rtt); - remainder -= allocation; - } - // Set |bitrate| to current send bitrate. - bandwidth_estimation_->SetSendBitrate(bitrate); - } -}; - BitrateController* BitrateController::CreateBitrateController( + Clock* clock, bool enforce_min_bitrate) { - return new BitrateControllerImpl(enforce_min_bitrate); + return new BitrateControllerImpl(clock, enforce_min_bitrate); } -BitrateControllerImpl::BitrateControllerImpl(bool enforce_min_bitrate) - : critsect_(CriticalSectionWrapper::CreateCriticalSection()) { - if (enforce_min_bitrate) { - low_rate_strategy_.reset(new EnforceMinRateStrategy( - &bandwidth_estimation_, &bitrate_observers_)); - } else { - low_rate_strategy_.reset(new NoEnforceMinRateStrategy( - &bandwidth_estimation_, &bitrate_observers_)); - } -} +BitrateControllerImpl::BitrateControllerImpl(Clock* clock, bool enforce_min_bitrate) + : clock_(clock), + last_bitrate_update_ms_(clock_->TimeInMilliseconds()), + critsect_(CriticalSectionWrapper::CreateCriticalSection()), + bandwidth_estimation_(), + bitrate_observers_(), + enforce_min_bitrate_(enforce_min_bitrate), + reserved_bitrate_bps_(0), + last_bitrate_bps_(0), + last_fraction_loss_(0), + last_rtt_ms_(0), + last_enforce_min_bitrate_(!enforce_min_bitrate_), + bitrate_observers_modified_(false), + last_reserved_bitrate_bps_(0) {} BitrateControllerImpl::~BitrateControllerImpl() { - BitrateObserverConfList::iterator it = - bitrate_observers_.begin(); + BitrateObserverConfList::iterator it = bitrate_observers_.begin(); while (it != bitrate_observers_.end()) { delete it->second; bitrate_observers_.erase(it); @@ -203,26 +139,56 @@ it->second->start_bitrate_ = start_bitrate; it->second->min_bitrate_ = min_bitrate; it->second->max_bitrate_ = max_bitrate; + // Set the send-side bandwidth to the max of the sum of start bitrates and + // the current estimate, so that if the user wants to immediately use more + // bandwidth, that can be enforced. + uint32_t sum_start_bitrate = 0; + BitrateObserverConfList::iterator it; + for (it = bitrate_observers_.begin(); it != bitrate_observers_.end(); + ++it) { + sum_start_bitrate += it->second->start_bitrate_; + } + uint32_t current_estimate; + uint8_t loss; + uint32_t rtt; + bandwidth_estimation_.CurrentEstimate(¤t_estimate, &loss, &rtt); + bandwidth_estimation_.SetSendBitrate(std::max(sum_start_bitrate, + current_estimate)); } else { // Add new settings. bitrate_observers_.push_back(BitrateObserverConfiguration(observer, new BitrateConfiguration(start_bitrate, min_bitrate, max_bitrate))); + bitrate_observers_modified_ = true; + + // TODO(andresp): This is a ugly way to set start bitrate. + // + // Only change start bitrate if we have exactly one observer. By definition + // you can only have one start bitrate, once we have our first estimate we + // will adapt from there. + if (bitrate_observers_.size() == 1) { + bandwidth_estimation_.SetSendBitrate(start_bitrate); + } } - uint32_t sum_start_bitrate = 0; + + UpdateMinMaxBitrate(); +} + +void BitrateControllerImpl::UpdateMinMaxBitrate() { uint32_t sum_min_bitrate = 0; uint32_t sum_max_bitrate = 0; - - // Summarize all configurations. + BitrateObserverConfList::iterator it; for (it = bitrate_observers_.begin(); it != bitrate_observers_.end(); ++it) { - sum_start_bitrate += it->second->start_bitrate_; sum_min_bitrate += it->second->min_bitrate_; sum_max_bitrate += it->second->max_bitrate_; } - // Only change start bitrate if we have exactly one observer. By definition - // you can only have one start bitrate, once we have our first estimate we - // will adapt from there. - if (bitrate_observers_.size() == 1) { - bandwidth_estimation_.SetSendBitrate(sum_start_bitrate); + if (sum_max_bitrate == 0) { + // No max configured use 1Gbit/s. + sum_max_bitrate = 1000000000; + } + if (enforce_min_bitrate_ == false) { + // If not enforcing min bitrate, allow the bandwidth estimation to + // go as low as 10 kbps. + sum_min_bitrate = std::min(sum_min_bitrate, 10000u); } bandwidth_estimation_.SetMinMaxBitrate(sum_min_bitrate, sum_max_bitrate); @@ -235,31 +201,46 @@ if (it != bitrate_observers_.end()) { delete it->second; bitrate_observers_.erase(it); + bitrate_observers_modified_ = true; } } void BitrateControllerImpl::EnforceMinBitrate(bool enforce_min_bitrate) { CriticalSectionScoped cs(critsect_); - if (enforce_min_bitrate) { - low_rate_strategy_.reset(new EnforceMinRateStrategy( - &bandwidth_estimation_, &bitrate_observers_)); - } else { - low_rate_strategy_.reset(new NoEnforceMinRateStrategy( - &bandwidth_estimation_, &bitrate_observers_)); - } + enforce_min_bitrate_ = enforce_min_bitrate; + UpdateMinMaxBitrate(); +} + +void BitrateControllerImpl::SetReservedBitrate(uint32_t reserved_bitrate_bps) { + CriticalSectionScoped cs(critsect_); + reserved_bitrate_bps_ = reserved_bitrate_bps; + MaybeTriggerOnNetworkChanged(); } void BitrateControllerImpl::OnReceivedEstimatedBitrate(const uint32_t bitrate) { - uint32_t new_bitrate = 0; - uint8_t fraction_lost = 0; - uint16_t rtt = 0; CriticalSectionScoped cs(critsect_); - if (bandwidth_estimation_.UpdateBandwidthEstimate(bitrate, - &new_bitrate, - &fraction_lost, - &rtt)) { - OnNetworkChanged(new_bitrate, fraction_lost, rtt); + bandwidth_estimation_.UpdateReceiverEstimate(bitrate); + MaybeTriggerOnNetworkChanged(); +} + +int32_t BitrateControllerImpl::TimeUntilNextProcess() { + enum { kBitrateControllerUpdateIntervalMs = 25 }; + CriticalSectionScoped cs(critsect_); + int time_since_update_ms = + clock_->TimeInMilliseconds() - last_bitrate_update_ms_; + return std::max(0, kBitrateControllerUpdateIntervalMs - time_since_update_ms); +} + +int32_t BitrateControllerImpl::Process() { + if (TimeUntilNextProcess() > 0) + return 0; + { + CriticalSectionScoped cs(critsect_); + bandwidth_estimation_.UpdateEstimate(clock_->TimeInMilliseconds()); + MaybeTriggerOnNetworkChanged(); } + last_bitrate_update_ms_ = clock_->TimeInMilliseconds(); + return 0; } void BitrateControllerImpl::OnReceivedRtcpReceiverReport( @@ -267,37 +248,63 @@ const uint32_t rtt, const int number_of_packets, const uint32_t now_ms) { - uint32_t new_bitrate = 0; - uint8_t loss = fraction_loss; CriticalSectionScoped cs(critsect_); - if (bandwidth_estimation_.UpdatePacketLoss(number_of_packets, rtt, now_ms, - &loss, &new_bitrate)) { - OnNetworkChanged(new_bitrate, loss, rtt); + bandwidth_estimation_.UpdateReceiverBlock( + fraction_loss, rtt, number_of_packets, now_ms); + MaybeTriggerOnNetworkChanged(); +} + +void BitrateControllerImpl::MaybeTriggerOnNetworkChanged() { + uint32_t bitrate; + uint8_t fraction_loss; + uint32_t rtt; + bandwidth_estimation_.CurrentEstimate(&bitrate, &fraction_loss, &rtt); + bitrate -= std::min(bitrate, reserved_bitrate_bps_); + + if (bitrate_observers_modified_ || + bitrate != last_bitrate_bps_ || + fraction_loss != last_fraction_loss_ || + rtt != last_rtt_ms_ || + last_enforce_min_bitrate_ != enforce_min_bitrate_ || + last_reserved_bitrate_bps_ != reserved_bitrate_bps_) { + last_bitrate_bps_ = bitrate; + last_fraction_loss_ = fraction_loss; + last_rtt_ms_ = rtt; + last_enforce_min_bitrate_ = enforce_min_bitrate_; + last_reserved_bitrate_bps_ = reserved_bitrate_bps_; + bitrate_observers_modified_ = false; + OnNetworkChanged(bitrate, fraction_loss, rtt); } } -// We have the lock here. void BitrateControllerImpl::OnNetworkChanged(const uint32_t bitrate, const uint8_t fraction_loss, const uint32_t rtt) { // Sanity check. - uint32_t number_of_observers = bitrate_observers_.size(); - if (number_of_observers == 0) { + if (bitrate_observers_.empty()) return; - } + uint32_t sum_min_bitrates = 0; BitrateObserverConfList::iterator it; for (it = bitrate_observers_.begin(); it != bitrate_observers_.end(); ++it) { sum_min_bitrates += it->second->min_bitrate_; } - if (bitrate <= sum_min_bitrates) { - return low_rate_strategy_->LowRateAllocation(bitrate, fraction_loss, rtt, - sum_min_bitrates); - } + if (bitrate <= sum_min_bitrates) + return LowRateAllocation(bitrate, fraction_loss, rtt, sum_min_bitrates); + else + return NormalRateAllocation(bitrate, fraction_loss, rtt, sum_min_bitrates); +} + +void BitrateControllerImpl::NormalRateAllocation(uint32_t bitrate, + uint8_t fraction_loss, + uint32_t rtt, + uint32_t sum_min_bitrates) { + uint32_t number_of_observers = bitrate_observers_.size(); uint32_t bitrate_per_observer = (bitrate - sum_min_bitrates) / number_of_observers; // Use map to sort list based on max bitrate. ObserverSortingMap list_max_bitrates; + BitrateObserverConfList::iterator it; for (it = bitrate_observers_.begin(); it != bitrate_observers_.end(); ++it) { list_max_bitrates.insert(std::pair( it->second->max_bitrate_, @@ -328,8 +335,46 @@ } } +void BitrateControllerImpl::LowRateAllocation(uint32_t bitrate, + uint8_t fraction_loss, + uint32_t rtt, + uint32_t sum_min_bitrates) { + if (enforce_min_bitrate_) { + // Min bitrate to all observers. + BitrateControllerImpl::BitrateObserverConfList::iterator it; + for (it = bitrate_observers_.begin(); it != bitrate_observers_.end(); + ++it) { + it->first->OnNetworkChanged(it->second->min_bitrate_, fraction_loss, rtt); + } + // Set sum of min to current send bitrate. + bandwidth_estimation_.SetSendBitrate(sum_min_bitrates); + } else { + // Allocate up to |min_bitrate_| to one observer at a time, until + // |bitrate| is depleted. + uint32_t remainder = bitrate; + BitrateControllerImpl::BitrateObserverConfList::iterator it; + for (it = bitrate_observers_.begin(); it != bitrate_observers_.end(); + ++it) { + uint32_t allocation = std::min(remainder, it->second->min_bitrate_); + it->first->OnNetworkChanged(allocation, fraction_loss, rtt); + remainder -= allocation; + } + // Set |bitrate| to current send bitrate. + bandwidth_estimation_.SetSendBitrate(bitrate); + } +} + bool BitrateControllerImpl::AvailableBandwidth(uint32_t* bandwidth) const { - return bandwidth_estimation_.AvailableBandwidth(bandwidth); + CriticalSectionScoped cs(critsect_); + uint32_t bitrate; + uint8_t fraction_loss; + uint32_t rtt; + bandwidth_estimation_.CurrentEstimate(&bitrate, &fraction_loss, &rtt); + if (bitrate) { + *bandwidth = bitrate - std::min(bitrate, reserved_bitrate_bps_); + return true; + } + return false; } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/bitrate_controller_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/bitrate_controller_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/bitrate_controller_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/bitrate_controller_impl.h 2015-02-03 14:33:35.000000000 +0000 @@ -27,12 +27,30 @@ namespace webrtc { -class RtcpBandwidthObserverImpl; -class LowRateStrategy; - class BitrateControllerImpl : public BitrateController { public: - friend class RtcpBandwidthObserverImpl; + BitrateControllerImpl(Clock* clock, bool enforce_min_bitrate); + virtual ~BitrateControllerImpl(); + + virtual bool AvailableBandwidth(uint32_t* bandwidth) const OVERRIDE; + + virtual RtcpBandwidthObserver* CreateRtcpBandwidthObserver() OVERRIDE; + + virtual void SetBitrateObserver(BitrateObserver* observer, + const uint32_t start_bitrate, + const uint32_t min_bitrate, + const uint32_t max_bitrate) OVERRIDE; + + virtual void RemoveBitrateObserver(BitrateObserver* observer) OVERRIDE; + + virtual void EnforceMinBitrate(bool enforce_min_bitrate) OVERRIDE; + virtual void SetReservedBitrate(uint32_t reserved_bitrate_bps) OVERRIDE; + + virtual int32_t TimeUntilNextProcess() OVERRIDE; + virtual int32_t Process() OVERRIDE; + + private: + class RtcpBandwidthObserverImpl; struct BitrateConfiguration { BitrateConfiguration(uint32_t start_bitrate, @@ -59,23 +77,8 @@ BitrateObserverConfiguration; typedef std::list BitrateObserverConfList; - explicit BitrateControllerImpl(bool enforce_min_bitrate); - virtual ~BitrateControllerImpl(); - - virtual bool AvailableBandwidth(uint32_t* bandwidth) const OVERRIDE; - - virtual RtcpBandwidthObserver* CreateRtcpBandwidthObserver() OVERRIDE; - - virtual void SetBitrateObserver(BitrateObserver* observer, - const uint32_t start_bitrate, - const uint32_t min_bitrate, - const uint32_t max_bitrate) OVERRIDE; - - virtual void RemoveBitrateObserver(BitrateObserver* observer) OVERRIDE; + void UpdateMinMaxBitrate() EXCLUSIVE_LOCKS_REQUIRED(*critsect_); - virtual void EnforceMinBitrate(bool enforce_min_bitrate) OVERRIDE; - - private: // Called by BitrateObserver's direct from the RTCP module. void OnReceivedEstimatedBitrate(const uint32_t bitrate); @@ -84,18 +87,48 @@ const int number_of_packets, const uint32_t now_ms); - typedef std::multimap ObserverSortingMap; + void MaybeTriggerOnNetworkChanged() EXCLUSIVE_LOCKS_REQUIRED(*critsect_); - BitrateObserverConfList::iterator - FindObserverConfigurationPair(const BitrateObserver* observer); void OnNetworkChanged(const uint32_t bitrate, const uint8_t fraction_loss, // 0 - 255. - const uint32_t rtt); + const uint32_t rtt) + EXCLUSIVE_LOCKS_REQUIRED(*critsect_); + + void NormalRateAllocation(uint32_t bitrate, + uint8_t fraction_loss, + uint32_t rtt, + uint32_t sum_min_bitrates) + EXCLUSIVE_LOCKS_REQUIRED(*critsect_); + + void LowRateAllocation(uint32_t bitrate, + uint8_t fraction_loss, + uint32_t rtt, + uint32_t sum_min_bitrates) + EXCLUSIVE_LOCKS_REQUIRED(*critsect_); + + typedef std::multimap ObserverSortingMap; + + BitrateObserverConfList::iterator FindObserverConfigurationPair( + const BitrateObserver* observer) EXCLUSIVE_LOCKS_REQUIRED(*critsect_); + + // Used by process thread. + Clock* clock_; + uint32_t last_bitrate_update_ms_; CriticalSectionWrapper* critsect_; - SendSideBandwidthEstimation bandwidth_estimation_; - BitrateObserverConfList bitrate_observers_; - scoped_ptr low_rate_strategy_; + SendSideBandwidthEstimation bandwidth_estimation_ GUARDED_BY(*critsect_); + BitrateObserverConfList bitrate_observers_ GUARDED_BY(*critsect_); + bool enforce_min_bitrate_ GUARDED_BY(*critsect_); + uint32_t reserved_bitrate_bps_ GUARDED_BY(*critsect_); + + uint32_t last_bitrate_bps_ GUARDED_BY(*critsect_); + uint8_t last_fraction_loss_ GUARDED_BY(*critsect_); + uint32_t last_rtt_ms_ GUARDED_BY(*critsect_); + bool last_enforce_min_bitrate_ GUARDED_BY(*critsect_); + bool bitrate_observers_modified_ GUARDED_BY(*critsect_); + uint32_t last_reserved_bitrate_bps_ GUARDED_BY(*critsect_); + + DISALLOW_IMPLICIT_CONSTRUCTORS(BitrateControllerImpl); }; } // namespace webrtc #endif // WEBRTC_MODULES_BITRATE_CONTROLLER_BITRATE_CONTROLLER_IMPL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/bitrate_controller_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/bitrate_controller_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/bitrate_controller_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/bitrate_controller_unittest.cc 2015-02-03 14:33:35.000000000 +0000 @@ -57,12 +57,12 @@ class BitrateControllerTest : public ::testing::Test { protected: - BitrateControllerTest() : enforce_min_bitrate_(true) {} + BitrateControllerTest() : clock_(0), enforce_min_bitrate_(true) {} ~BitrateControllerTest() {} virtual void SetUp() { - controller_ = - BitrateController::CreateBitrateController(enforce_min_bitrate_); + controller_ = BitrateController::CreateBitrateController( + &clock_, enforce_min_bitrate_); bandwidth_observer_ = controller_->CreateRtcpBandwidthObserver(); } @@ -70,6 +70,8 @@ delete bandwidth_observer_; delete controller_; } + + webrtc::SimulatedClock clock_; bool enforce_min_bitrate_; BitrateController* controller_; RtcpBandwidthObserver* bandwidth_observer_; @@ -81,58 +83,109 @@ controller_->RemoveBitrateObserver(&bitrate_observer); } +TEST_F(BitrateControllerTest, InitialRemb) { + TestBitrateObserver bitrate_observer; + controller_->SetBitrateObserver(&bitrate_observer, 200000, 100000, 1500000); + const uint32_t kRemb = 1000000u; + const uint32_t kSecondRemb = kRemb + 500000u; + + // Initial REMB applies immediately. + bandwidth_observer_->OnReceivedEstimatedBitrate(kRemb); + webrtc::ReportBlockList report_blocks; + report_blocks.push_back(CreateReportBlock(1, 2, 0, 1)); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 1); + report_blocks.clear(); + EXPECT_EQ(kRemb, bitrate_observer.last_bitrate_); + + // Second REMB doesn't apply immediately. + bandwidth_observer_->OnReceivedEstimatedBitrate(kRemb + 500000); + report_blocks.push_back(CreateReportBlock(1, 2, 0, 21)); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 2001); + EXPECT_LT(bitrate_observer.last_bitrate_, kSecondRemb); +} + +TEST_F(BitrateControllerTest, UpdatingBitrateObserver) { + TestBitrateObserver bitrate_observer; + controller_->SetBitrateObserver(&bitrate_observer, 200000, 100000, 1500000); + clock_.AdvanceTimeMilliseconds(25); + controller_->Process(); + EXPECT_EQ(200000u, bitrate_observer.last_bitrate_); + + controller_->SetBitrateObserver(&bitrate_observer, 1500000, 100000, 1500000); + clock_.AdvanceTimeMilliseconds(25); + controller_->Process(); + EXPECT_EQ(1500000u, bitrate_observer.last_bitrate_); + + controller_->SetBitrateObserver(&bitrate_observer, 500000, 100000, 1500000); + clock_.AdvanceTimeMilliseconds(25); + controller_->Process(); + EXPECT_EQ(1500000u, bitrate_observer.last_bitrate_); +} + TEST_F(BitrateControllerTest, OneBitrateObserverOneRtcpObserver) { TestBitrateObserver bitrate_observer; controller_->SetBitrateObserver(&bitrate_observer, 200000, 100000, 300000); - // Receive a high remb, test bitrate inc. - bandwidth_observer_->OnReceivedEstimatedBitrate(400000); - - // Test start bitrate. + // First REMB applies immediately. + int64_t time_ms = 1001; webrtc::ReportBlockList report_blocks; report_blocks.push_back(CreateReportBlock(1, 2, 0, 1)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 1); - EXPECT_EQ(0u, bitrate_observer.last_bitrate_); + bandwidth_observer_->OnReceivedEstimatedBitrate(200000); + EXPECT_EQ(200000u, bitrate_observer.last_bitrate_); EXPECT_EQ(0, bitrate_observer.last_fraction_loss_); EXPECT_EQ(0u, bitrate_observer.last_rtt_); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); + report_blocks.clear(); + time_ms += 2000; + + // Receive a high remb, test bitrate inc. + bandwidth_observer_->OnReceivedEstimatedBitrate(400000); // Test bitrate increase 8% per second. - report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 21)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 1001); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); EXPECT_EQ(217000u, bitrate_observer.last_bitrate_); EXPECT_EQ(0, bitrate_observer.last_fraction_loss_); EXPECT_EQ(50u, bitrate_observer.last_rtt_); + time_ms += 1000; report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 41)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 2001); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); EXPECT_EQ(235360u, bitrate_observer.last_bitrate_); + EXPECT_EQ(0, bitrate_observer.last_fraction_loss_); + EXPECT_EQ(50u, bitrate_observer.last_rtt_); + time_ms += 1000; report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 61)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 3001); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); EXPECT_EQ(255189u, bitrate_observer.last_bitrate_); + time_ms += 1000; report_blocks.clear(); - report_blocks.push_back(CreateReportBlock(1, 2, 0, 801)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 4001); + report_blocks.push_back(CreateReportBlock(1, 2, 0, 81)); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); EXPECT_EQ(276604u, bitrate_observer.last_bitrate_); + time_ms += 1000; report_blocks.clear(); - report_blocks.push_back(CreateReportBlock(1, 2, 0, 101)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 5001); + report_blocks.push_back(CreateReportBlock(1, 2, 0, 801)); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); EXPECT_EQ(299732u, bitrate_observer.last_bitrate_); + time_ms += 1000; + // Reach max cap. report_blocks.clear(); - report_blocks.push_back(CreateReportBlock(1, 2, 0, 121)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 6001); - EXPECT_EQ(300000u, bitrate_observer.last_bitrate_); // Max cap. + report_blocks.push_back(CreateReportBlock(1, 2, 0, 101)); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); + EXPECT_EQ(300000u, bitrate_observer.last_bitrate_); + time_ms += 1000; report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 141)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 7001); - EXPECT_EQ(300000u, bitrate_observer.last_bitrate_); // Max cap. + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); + EXPECT_EQ(300000u, bitrate_observer.last_bitrate_); // Test that a low REMB trigger immediately. bandwidth_observer_->OnReceivedEstimatedBitrate(250000); @@ -149,81 +202,87 @@ TestBitrateObserver bitrate_observer; controller_->SetBitrateObserver(&bitrate_observer, 200000, 100000, 300000); + // REMBs during the first 2 seconds apply immediately. + int64_t time_ms = 1; + webrtc::ReportBlockList report_blocks; + report_blocks.push_back(CreateReportBlock(1, 2, 0, 1)); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); + report_blocks.clear(); + time_ms += 500; + RtcpBandwidthObserver* second_bandwidth_observer = controller_->CreateRtcpBandwidthObserver(); - // Receive a high remb, test bitrate inc. - bandwidth_observer_->OnReceivedEstimatedBitrate(400000); - // Test start bitrate. - webrtc::ReportBlockList report_blocks; - report_blocks.push_back(CreateReportBlock(1, 2, 0, 1)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 1); + report_blocks.push_back(CreateReportBlock(1, 2, 0, 21)); second_bandwidth_observer->OnReceivedRtcpReceiverReport( report_blocks, 100, 1); - EXPECT_EQ(0u, bitrate_observer.last_bitrate_); + EXPECT_EQ(217000u, bitrate_observer.last_bitrate_); EXPECT_EQ(0, bitrate_observer.last_fraction_loss_); - EXPECT_EQ(0u, bitrate_observer.last_rtt_); + EXPECT_EQ(100u, bitrate_observer.last_rtt_); + time_ms += 500; // Test bitrate increase 8% per second. report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 21)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 501); - second_bandwidth_observer->OnReceivedRtcpReceiverReport(report_blocks, 100, - 1001); - EXPECT_EQ(217000u, bitrate_observer.last_bitrate_); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); + time_ms += 500; + second_bandwidth_observer->OnReceivedRtcpReceiverReport( + report_blocks, 100, time_ms); + EXPECT_EQ(235360u, bitrate_observer.last_bitrate_); EXPECT_EQ(0, bitrate_observer.last_fraction_loss_); EXPECT_EQ(100u, bitrate_observer.last_rtt_); + time_ms += 500; // Extra report should not change estimate. report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 31)); - second_bandwidth_observer->OnReceivedRtcpReceiverReport(report_blocks, 100, - 1501); - EXPECT_EQ(217000u, bitrate_observer.last_bitrate_); + second_bandwidth_observer->OnReceivedRtcpReceiverReport( + report_blocks, 100, time_ms); + EXPECT_EQ(235360u, bitrate_observer.last_bitrate_); + time_ms += 500; report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 41)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 2001); - EXPECT_EQ(235360u, bitrate_observer.last_bitrate_); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); + EXPECT_EQ(255189u, bitrate_observer.last_bitrate_); // Second report should not change estimate. report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 41)); - second_bandwidth_observer->OnReceivedRtcpReceiverReport(report_blocks, 100, - 2001); - EXPECT_EQ(235360u, bitrate_observer.last_bitrate_); + second_bandwidth_observer->OnReceivedRtcpReceiverReport( + report_blocks, 100, time_ms); + EXPECT_EQ(255189u, bitrate_observer.last_bitrate_); + time_ms += 1000; // Reports from only one bandwidth observer is ok. report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 61)); - second_bandwidth_observer->OnReceivedRtcpReceiverReport(report_blocks, 50, - 3001); - EXPECT_EQ(255189u, bitrate_observer.last_bitrate_); - - report_blocks.clear(); - report_blocks.push_back(CreateReportBlock(1, 2, 0, 81)); - second_bandwidth_observer->OnReceivedRtcpReceiverReport(report_blocks, 50, - 4001); + second_bandwidth_observer->OnReceivedRtcpReceiverReport( + report_blocks, 50, time_ms); EXPECT_EQ(276604u, bitrate_observer.last_bitrate_); + time_ms += 1000; report_blocks.clear(); - report_blocks.push_back(CreateReportBlock(1, 2, 0, 101)); - second_bandwidth_observer->OnReceivedRtcpReceiverReport(report_blocks, 50, - 5001); + report_blocks.push_back(CreateReportBlock(1, 2, 0, 81)); + second_bandwidth_observer->OnReceivedRtcpReceiverReport( + report_blocks, 50, time_ms); EXPECT_EQ(299732u, bitrate_observer.last_bitrate_); + time_ms += 1000; + // Reach max cap. report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 121)); - second_bandwidth_observer->OnReceivedRtcpReceiverReport(report_blocks, 50, - 6001); - EXPECT_EQ(300000u, bitrate_observer.last_bitrate_); // Max cap. + second_bandwidth_observer->OnReceivedRtcpReceiverReport( + report_blocks, 50, time_ms); + EXPECT_EQ(300000u, bitrate_observer.last_bitrate_); + time_ms += 1000; report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 141)); - second_bandwidth_observer->OnReceivedRtcpReceiverReport(report_blocks, 50, - 7001); - EXPECT_EQ(300000u, bitrate_observer.last_bitrate_); // Max cap. + second_bandwidth_observer->OnReceivedRtcpReceiverReport( + report_blocks, 50, time_ms); + EXPECT_EQ(300000u, bitrate_observer.last_bitrate_); // Test that a low REMB trigger immediately. // We don't care which bandwidth observer that delivers the REMB. @@ -232,8 +291,9 @@ EXPECT_EQ(0, bitrate_observer.last_fraction_loss_); EXPECT_EQ(50u, bitrate_observer.last_rtt_); + // Min cap. bandwidth_observer_->OnReceivedEstimatedBitrate(1000); - EXPECT_EQ(100000u, bitrate_observer.last_bitrate_); // Min cap. + EXPECT_EQ(100000u, bitrate_observer.last_bitrate_); controller_->RemoveBitrateObserver(&bitrate_observer); delete second_bandwidth_observer; } @@ -247,11 +307,18 @@ controller_->SetBitrateObserver(&bitrate_observer, kStartBitrate, kMinBitrate, kMaxBitrate); + // REMBs during the first 2 seconds apply immediately. + int64_t time_ms = 1001; + webrtc::ReportBlockList report_blocks; + report_blocks.push_back(CreateReportBlock(1, 2, 0, sequence_number[0])); + bandwidth_observer_->OnReceivedEstimatedBitrate(kStartBitrate); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); + report_blocks.clear(); + time_ms += 2000; + // Receive a high REMB, test bitrate increase. bandwidth_observer_->OnReceivedEstimatedBitrate(400000); - webrtc::ReportBlockList report_blocks; - int64_t time_ms = 1001; uint32_t last_bitrate = 0; // Ramp up to max bitrate. for (int i = 0; i < 6; ++i) { @@ -316,87 +383,97 @@ controller_->SetBitrateObserver(&bitrate_observer_2, 200000, 200000, 300000); controller_->SetBitrateObserver(&bitrate_observer_1, 200000, 100000, 300000); - // Receive a high remb, test bitrate inc. - bandwidth_observer_->OnReceivedEstimatedBitrate(400000); - - // Test too low start bitrate, hence lower than sum of min. + // REMBs during the first 2 seconds apply immediately. + int64_t time_ms = 1001; webrtc::ReportBlockList report_blocks; report_blocks.push_back(CreateReportBlock(1, 2, 0, 1)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 1); + bandwidth_observer_->OnReceivedEstimatedBitrate(200000); + EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_); + EXPECT_EQ(0, bitrate_observer_1.last_fraction_loss_); + EXPECT_EQ(0u, bitrate_observer_1.last_rtt_); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); + report_blocks.clear(); + time_ms += 2000; + + // Receive a high remb, test bitrate inc. + // Test too low start bitrate, hence lower than sum of min. + bandwidth_observer_->OnReceivedEstimatedBitrate(400000); // Test bitrate increase 8% per second, distributed equally. - report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 21)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 1001); - EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); + EXPECT_EQ(112500u, bitrate_observer_1.last_bitrate_); EXPECT_EQ(0, bitrate_observer_1.last_fraction_loss_); EXPECT_EQ(50u, bitrate_observer_1.last_rtt_); + time_ms += 1000; - EXPECT_EQ(200000u, bitrate_observer_2.last_bitrate_); + EXPECT_EQ(212500u, bitrate_observer_2.last_bitrate_); EXPECT_EQ(0, bitrate_observer_2.last_fraction_loss_); EXPECT_EQ(50u, bitrate_observer_2.last_rtt_); report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 41)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 2001); - EXPECT_EQ(112500u, bitrate_observer_1.last_bitrate_); - EXPECT_EQ(212500u, bitrate_observer_2.last_bitrate_); - - report_blocks.clear(); - report_blocks.push_back(CreateReportBlock(1, 2, 0, 61)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 3001); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); EXPECT_EQ(126000u, bitrate_observer_1.last_bitrate_); EXPECT_EQ(226000u, bitrate_observer_2.last_bitrate_); + time_ms += 1000; report_blocks.clear(); - report_blocks.push_back(CreateReportBlock(1, 2, 0, 81)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 4001); + report_blocks.push_back(CreateReportBlock(1, 2, 0, 61)); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); EXPECT_EQ(140580u, bitrate_observer_1.last_bitrate_); EXPECT_EQ(240580u, bitrate_observer_2.last_bitrate_); + time_ms += 1000; // Check that the bitrate sum honor our REMB. report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 101)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 5001); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); EXPECT_EQ(150000u, bitrate_observer_1.last_bitrate_); EXPECT_EQ(250000u, bitrate_observer_2.last_bitrate_); + time_ms += 1000; // Remove REMB cap, higher than sum of max. bandwidth_observer_->OnReceivedEstimatedBitrate(700000); report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 121)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 6001); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); EXPECT_EQ(166500u, bitrate_observer_1.last_bitrate_); EXPECT_EQ(266500u, bitrate_observer_2.last_bitrate_); + time_ms += 1000; report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 141)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 7001); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); EXPECT_EQ(184320u, bitrate_observer_1.last_bitrate_); EXPECT_EQ(284320u, bitrate_observer_2.last_bitrate_); + time_ms += 1000; report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 161)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 8001); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); EXPECT_EQ(207130u, bitrate_observer_1.last_bitrate_); EXPECT_EQ(300000u, bitrate_observer_2.last_bitrate_); // Max cap. + time_ms += 1000; report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 181)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 9001); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); EXPECT_EQ(248700u, bitrate_observer_1.last_bitrate_); EXPECT_EQ(300000u, bitrate_observer_2.last_bitrate_); + time_ms += 1000; report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 201)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 10001); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); EXPECT_EQ(293596u, bitrate_observer_1.last_bitrate_); EXPECT_EQ(300000u, bitrate_observer_2.last_bitrate_); + time_ms += 1000; report_blocks.clear(); report_blocks.push_back(CreateReportBlock(1, 2, 0, 221)); - bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, 11001); + bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, 50, time_ms); EXPECT_EQ(300000u, bitrate_observer_1.last_bitrate_); // Max cap. EXPECT_EQ(300000u, bitrate_observer_2.last_bitrate_); @@ -416,6 +493,61 @@ controller_->RemoveBitrateObserver(&bitrate_observer_2); } +TEST_F(BitrateControllerTest, SetReservedBitrate) { + TestBitrateObserver bitrate_observer; + controller_->SetBitrateObserver(&bitrate_observer, 200000, 100000, 300000); + + // Receive successively lower REMBs, verify the reserved bitrate is deducted. + + controller_->SetReservedBitrate(0); + bandwidth_observer_->OnReceivedEstimatedBitrate(400000); + EXPECT_EQ(200000u, bitrate_observer.last_bitrate_); + controller_->SetReservedBitrate(50000); + bandwidth_observer_->OnReceivedEstimatedBitrate(400000); + EXPECT_EQ(150000u, bitrate_observer.last_bitrate_); + + controller_->SetReservedBitrate(0); + bandwidth_observer_->OnReceivedEstimatedBitrate(250000); + EXPECT_EQ(200000u, bitrate_observer.last_bitrate_); + controller_->SetReservedBitrate(50000); + bandwidth_observer_->OnReceivedEstimatedBitrate(250000); + EXPECT_EQ(150000u, bitrate_observer.last_bitrate_); + + controller_->SetReservedBitrate(0); + bandwidth_observer_->OnReceivedEstimatedBitrate(200000); + EXPECT_EQ(200000u, bitrate_observer.last_bitrate_); + controller_->SetReservedBitrate(30000); + bandwidth_observer_->OnReceivedEstimatedBitrate(200000); + EXPECT_EQ(170000u, bitrate_observer.last_bitrate_); + + controller_->SetReservedBitrate(0); + bandwidth_observer_->OnReceivedEstimatedBitrate(160000); + EXPECT_EQ(160000u, bitrate_observer.last_bitrate_); + controller_->SetReservedBitrate(30000); + bandwidth_observer_->OnReceivedEstimatedBitrate(160000); + EXPECT_EQ(130000u, bitrate_observer.last_bitrate_); + + controller_->SetReservedBitrate(0); + bandwidth_observer_->OnReceivedEstimatedBitrate(120000); + EXPECT_EQ(120000u, bitrate_observer.last_bitrate_); + controller_->SetReservedBitrate(10000); + bandwidth_observer_->OnReceivedEstimatedBitrate(120000); + EXPECT_EQ(110000u, bitrate_observer.last_bitrate_); + + controller_->SetReservedBitrate(0); + bandwidth_observer_->OnReceivedEstimatedBitrate(120000); + EXPECT_EQ(120000u, bitrate_observer.last_bitrate_); + controller_->SetReservedBitrate(50000); + bandwidth_observer_->OnReceivedEstimatedBitrate(120000); + EXPECT_EQ(100000u, bitrate_observer.last_bitrate_); + + controller_->SetReservedBitrate(10000); + bandwidth_observer_->OnReceivedEstimatedBitrate(0); + EXPECT_EQ(100000u, bitrate_observer.last_bitrate_); + + controller_->RemoveBitrateObserver(&bitrate_observer); +} + class BitrateControllerTestNoEnforceMin : public BitrateControllerTest { protected: BitrateControllerTestNoEnforceMin() : BitrateControllerTest() { @@ -434,8 +566,32 @@ EXPECT_EQ(150000u, bitrate_observer_1.last_bitrate_); // Low REMB. - bandwidth_observer_->OnReceivedEstimatedBitrate(1000); - EXPECT_EQ(1000u, bitrate_observer_1.last_bitrate_); + bandwidth_observer_->OnReceivedEstimatedBitrate(10000); + EXPECT_EQ(10000u, bitrate_observer_1.last_bitrate_); + + // Keeps at least 10 kbps. + bandwidth_observer_->OnReceivedEstimatedBitrate(9000); + EXPECT_EQ(10000u, bitrate_observer_1.last_bitrate_); + + controller_->RemoveBitrateObserver(&bitrate_observer_1); +} + +TEST_F(BitrateControllerTestNoEnforceMin, SetReservedBitrate) { + TestBitrateObserver bitrate_observer_1; + controller_->SetBitrateObserver(&bitrate_observer_1, 200000, 100000, 400000); + controller_->SetReservedBitrate(10000); + + // High REMB. + bandwidth_observer_->OnReceivedEstimatedBitrate(150000); + EXPECT_EQ(140000u, bitrate_observer_1.last_bitrate_); + + // Low REMB. + bandwidth_observer_->OnReceivedEstimatedBitrate(15000); + EXPECT_EQ(5000u, bitrate_observer_1.last_bitrate_); + + // Keeps at least 10 kbps. + bandwidth_observer_->OnReceivedEstimatedBitrate(9000); + EXPECT_EQ(0u, bitrate_observer_1.last_bitrate_); controller_->RemoveBitrateObserver(&bitrate_observer_1); } @@ -469,9 +625,15 @@ EXPECT_EQ(200000u, bitrate_observer_3.last_bitrate_); // Remainder. // Low REMB. - bandwidth_observer_->OnReceivedEstimatedBitrate(1000); + bandwidth_observer_->OnReceivedEstimatedBitrate(10000); // Verify that the first observer gets all the rate, and the rest get zero. - EXPECT_EQ(1000u, bitrate_observer_1.last_bitrate_); + EXPECT_EQ(10000u, bitrate_observer_1.last_bitrate_); + EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_); + EXPECT_EQ(0u, bitrate_observer_3.last_bitrate_); + + // Verify it keeps an estimate of at least 10kbps. + bandwidth_observer_->OnReceivedEstimatedBitrate(9000); + EXPECT_EQ(10000u, bitrate_observer_1.last_bitrate_); EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_); EXPECT_EQ(0u, bitrate_observer_3.last_bitrate_); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/BUILD.gn 2015-02-03 14:33:35.000000000 +0000 @@ -0,0 +1,37 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../../build/webrtc.gni") + +source_set("bitrate_controller") { + sources = [ + "bitrate_controller_impl.cc", + "bitrate_controller_impl.h", + "include/bitrate_controller.h", + "send_side_bandwidth_estimation.cc", + "send_side_bandwidth_estimation.h", + ] + + if (is_win) { + cflags = [ + # TODO(jschuh): Bug 1348: fix this warning. + "/wd4267" # size_t to int truncations + ] + } + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ "../../system_wrappers" ] +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/include/bitrate_controller.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/include/bitrate_controller.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/include/bitrate_controller.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/include/bitrate_controller.h 2015-02-03 14:33:36.000000000 +0000 @@ -15,6 +15,7 @@ #ifndef WEBRTC_MODULES_BITRATE_CONTROLLER_INCLUDE_BITRATE_CONTROLLER_H_ #define WEBRTC_MODULES_BITRATE_CONTROLLER_INCLUDE_BITRATE_CONTROLLER_H_ +#include "webrtc/modules/interface/module.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" namespace webrtc { @@ -35,7 +36,7 @@ virtual ~BitrateObserver() {} }; -class BitrateController { +class BitrateController : public Module { /* * This class collects feedback from all streams sent to a peer (via * RTCPBandwidthObservers). It does one aggregated send side bandwidth @@ -48,7 +49,8 @@ // When true, the bitrate will never be set lower than the minimum bitrate(s). // When false, the bitrate observers will be allocated rates up to their // respective minimum bitrate, satisfying one observer after the other. - static BitrateController* CreateBitrateController(bool enforce_min_bitrate); + static BitrateController* CreateBitrateController(Clock* clock, + bool enforce_min_bitrate); virtual ~BitrateController() {} virtual RtcpBandwidthObserver* CreateRtcpBandwidthObserver() = 0; @@ -73,6 +75,8 @@ // Changes the mode that was set in the constructor. virtual void EnforceMinBitrate(bool enforce_min_bitrate) = 0; + + virtual void SetReservedBitrate(uint32_t reserved_bitrate_bps) = 0; }; } // namespace webrtc #endif // WEBRTC_MODULES_BITRATE_CONTROLLER_INCLUDE_BITRATE_CONTROLLER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/OWNERS 2015-02-03 14:33:35.000000000 +0000 @@ -1,5 +1,11 @@ -pwestin@webrtc.org stefan@webrtc.org henrik.lundin@webrtc.org mflodman@webrtc.org asapersson@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/send_side_bandwidth_estimation.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/send_side_bandwidth_estimation.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/send_side_bandwidth_estimation.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/send_side_bandwidth_estimation.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,216 +10,259 @@ #include "webrtc/modules/bitrate_controller/send_side_bandwidth_estimation.h" -#include // sqrt() +#include -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/system_wrappers/interface/metrics.h" namespace webrtc { +namespace { +enum { kBweIncreaseIntervalMs = 1000 }; +enum { kBweDecreaseIntervalMs = 300 }; +enum { kLimitNumPackets = 20 }; +enum { kAvgPacketSizeBytes = 1000 }; +enum { kStartPhaseMs = 2000 }; +enum { kBweConverganceTimeMs = 20000 }; + +// Calculate the rate that TCP-Friendly Rate Control (TFRC) would apply. +// The formula in RFC 3448, Section 3.1, is used. +uint32_t CalcTfrcBps(uint16_t rtt, uint8_t loss) { + if (rtt == 0 || loss == 0) { + // Input variables out of range. + return 0; + } + double R = static_cast(rtt) / 1000; // RTT in seconds. + int b = 1; // Number of packets acknowledged by a single TCP acknowledgement: + // recommended = 1. + double t_RTO = 4.0 * R; // TCP retransmission timeout value in seconds + // recommended = 4*R. + double p = static_cast(loss) / 255; // Packet loss rate in [0, 1). + double s = static_cast(kAvgPacketSizeBytes); + + // Calculate send rate in bytes/second. + double X = + s / (R * std::sqrt(2 * b * p / 3) + + (t_RTO * (3 * std::sqrt(3 * b * p / 8) * p * (1 + 32 * p * p)))); + + // Convert to bits/second. + return (static_cast(X * 8)); +} +} SendSideBandwidthEstimation::SendSideBandwidthEstimation() - : critsect_(CriticalSectionWrapper::CreateCriticalSection()), - accumulate_lost_packets_Q8_(0), + : accumulate_lost_packets_Q8_(0), accumulate_expected_packets_(0), bitrate_(0), min_bitrate_configured_(0), max_bitrate_configured_(0), + time_last_receiver_block_ms_(0), last_fraction_loss_(0), - last_round_trip_time_(0), + last_round_trip_time_ms_(0), bwe_incoming_(0), - time_last_increase_(0), - time_last_decrease_(0) { + time_last_decrease_ms_(0), + first_report_time_ms_(-1), + initially_lost_packets_(0), + bitrate_at_2_seconds_kbps_(0), + uma_update_state_(kNoUpdate) { } -SendSideBandwidthEstimation::~SendSideBandwidthEstimation() { - delete critsect_; -} +SendSideBandwidthEstimation::~SendSideBandwidthEstimation() {} -void SendSideBandwidthEstimation::SetSendBitrate(const uint32_t bitrate) { - CriticalSectionScoped cs(critsect_); +void SendSideBandwidthEstimation::SetSendBitrate(uint32_t bitrate) { bitrate_ = bitrate; + + // Clear last sent bitrate history so the new value can be used directly + // and not capped. + min_bitrate_history_.clear(); } -void SendSideBandwidthEstimation::SetMinMaxBitrate(const uint32_t min_bitrate, - const uint32_t max_bitrate) { - CriticalSectionScoped cs(critsect_); +void SendSideBandwidthEstimation::SetMinMaxBitrate(uint32_t min_bitrate, + uint32_t max_bitrate) { + min_bitrate_configured_ = min_bitrate; + max_bitrate_configured_ = max_bitrate; +} + +void SendSideBandwidthEstimation::SetMinBitrate(uint32_t min_bitrate) { min_bitrate_configured_ = min_bitrate; - if (max_bitrate == 0) { - // no max configured use 1Gbit/s - max_bitrate_configured_ = 1000000000; - } else { - max_bitrate_configured_ = max_bitrate; - } } -bool SendSideBandwidthEstimation::UpdateBandwidthEstimate( - const uint32_t bandwidth, - uint32_t* new_bitrate, - uint8_t* fraction_lost, - uint16_t* rtt) { - *new_bitrate = 0; - CriticalSectionScoped cs(critsect_); +void SendSideBandwidthEstimation::CurrentEstimate(uint32_t* bitrate, + uint8_t* loss, + uint32_t* rtt) const { + *bitrate = bitrate_; + *loss = last_fraction_loss_; + *rtt = last_round_trip_time_ms_; +} +void SendSideBandwidthEstimation::UpdateReceiverEstimate(uint32_t bandwidth) { bwe_incoming_ = bandwidth; + bitrate_ = CapBitrateToThresholds(bitrate_); +} - if (bitrate_ == 0) { - // SendSideBandwidthEstimation off - return false; - } - if (bwe_incoming_ > 0 && bitrate_ > bwe_incoming_) { - bitrate_ = bwe_incoming_; - *new_bitrate = bitrate_; - *fraction_lost = last_fraction_loss_; - *rtt = last_round_trip_time_; - return true; - } - return false; -} - -bool SendSideBandwidthEstimation::UpdatePacketLoss( - const int number_of_packets, - const uint32_t rtt, - const uint32_t now_ms, - uint8_t* loss, - uint32_t* new_bitrate) { - CriticalSectionScoped cs(critsect_); - - if (bitrate_ == 0) { - // SendSideBandwidthEstimation off - return false; - } +void SendSideBandwidthEstimation::UpdateReceiverBlock(uint8_t fraction_loss, + uint32_t rtt, + int number_of_packets, + uint32_t now_ms) { // Update RTT. - last_round_trip_time_ = rtt; + last_round_trip_time_ms_ = rtt; // Check sequence number diff and weight loss report if (number_of_packets > 0) { // Calculate number of lost packets. - const int num_lost_packets_Q8 = *loss * number_of_packets; + const int num_lost_packets_Q8 = fraction_loss * number_of_packets; // Accumulate reports. accumulate_lost_packets_Q8_ += num_lost_packets_Q8; accumulate_expected_packets_ += number_of_packets; // Report loss if the total report is based on sufficiently many packets. if (accumulate_expected_packets_ >= kLimitNumPackets) { - *loss = accumulate_lost_packets_Q8_ / accumulate_expected_packets_; + last_fraction_loss_ = + accumulate_lost_packets_Q8_ / accumulate_expected_packets_; - // Reset accumulators + // Reset accumulators. accumulate_lost_packets_Q8_ = 0; accumulate_expected_packets_ = 0; } else { - // Report zero loss until we have enough data to estimate - // the loss rate. - return false; + // Early return without updating estimate. + return; } } - // Keep for next time. - last_fraction_loss_ = *loss; - uint32_t bitrate = 0; - if (!ShapeSimple(*loss, rtt, now_ms, &bitrate)) { - // No change. - return false; + time_last_receiver_block_ms_ = now_ms; + UpdateEstimate(now_ms); + + if (first_report_time_ms_ == -1) { + first_report_time_ms_ = now_ms; + } else { + UpdateUmaStats(now_ms, rtt, (fraction_loss * number_of_packets) >> 8); } - bitrate_ = bitrate; - *new_bitrate = bitrate; - return true; } -bool SendSideBandwidthEstimation::AvailableBandwidth( - uint32_t* bandwidth) const { - CriticalSectionScoped cs(critsect_); - if (bitrate_ == 0) { - return false; +void SendSideBandwidthEstimation::UpdateUmaStats(int64_t now_ms, + int rtt, + int lost_packets) { + if (IsInStartPhase(now_ms)) { + initially_lost_packets_ += lost_packets; + } else if (uma_update_state_ == kNoUpdate) { + uma_update_state_ = kFirstDone; + bitrate_at_2_seconds_kbps_ = (bitrate_ + 500) / 1000; + RTC_HISTOGRAM_COUNTS( + "WebRTC.BWE.InitiallyLostPackets", initially_lost_packets_, 0, 100, 50); + RTC_HISTOGRAM_COUNTS("WebRTC.BWE.InitialRtt", rtt, 0, 2000, 50); + RTC_HISTOGRAM_COUNTS("WebRTC.BWE.InitialBandwidthEstimate", + bitrate_at_2_seconds_kbps_, + 0, + 2000, + 50); + } else if (uma_update_state_ == kFirstDone && + now_ms - first_report_time_ms_ >= kBweConverganceTimeMs) { + uma_update_state_ = kDone; + int bitrate_diff_kbps = std::max( + bitrate_at_2_seconds_kbps_ - static_cast((bitrate_ + 500) / 1000), + 0); + RTC_HISTOGRAM_COUNTS( + "WebRTC.BWE.InitialVsConvergedDiff", bitrate_diff_kbps, 0, 2000, 50); } - *bandwidth = bitrate_; - return true; } -/* - * Calculate the rate that TCP-Friendly Rate Control (TFRC) would apply. - * The formula in RFC 3448, Section 3.1, is used. - */ -uint32_t SendSideBandwidthEstimation::CalcTFRCbps(uint16_t rtt, uint8_t loss) { - if (rtt == 0 || loss == 0) { - // input variables out of range - return 0; - } - double R = static_cast(rtt) / 1000; // RTT in seconds - int b = 1; // number of packets acknowledged by a single TCP acknowledgement; - // recommended = 1 - double t_RTO = 4.0 * R; // TCP retransmission timeout value in seconds - // recommended = 4*R - double p = static_cast(loss) / 255; // packet loss rate in [0, 1) - double s = static_cast(kAvgPacketSizeBytes); +void SendSideBandwidthEstimation::UpdateEstimate(uint32_t now_ms) { + // We trust the REMB during the first 2 seconds if we haven't had any + // packet loss reported, to allow startup bitrate probing. + if (last_fraction_loss_ == 0 && IsInStartPhase(now_ms) && + bwe_incoming_ > bitrate_) { + bitrate_ = CapBitrateToThresholds(bwe_incoming_); + min_bitrate_history_.clear(); + min_bitrate_history_.push_back(std::make_pair(now_ms, bitrate_)); + return; + } + UpdateMinHistory(now_ms); + // Only start updating bitrate when receiving receiver blocks. + if (time_last_receiver_block_ms_ != 0) { + if (last_fraction_loss_ <= 5) { + // Loss < 2%: Increase rate by 8% of the min bitrate in the last + // kBweIncreaseIntervalMs. + // Note that by remembering the bitrate over the last second one can + // rampup up one second faster than if only allowed to start ramping + // at 8% per second rate now. E.g.: + // If sending a constant 100kbps it can rampup immediatly to 108kbps + // whenever a receiver report is received with lower packet loss. + // If instead one would do: bitrate_ *= 1.08^(delta time), it would + // take over one second since the lower packet loss to achieve 108kbps. + bitrate_ = static_cast( + min_bitrate_history_.front().second * 1.08 + 0.5); + + // Add 1 kbps extra, just to make sure that we do not get stuck + // (gives a little extra increase at low rates, negligible at higher + // rates). + bitrate_ += 1000; - // calculate send rate in bytes/second - double X = s / (R * sqrt(2 * b * p / 3) + - (t_RTO * (3 * sqrt(3 * b * p / 8) * p * (1 + 32 * p * p)))); - - return (static_cast(X * 8)); // bits/second -} - -bool SendSideBandwidthEstimation::ShapeSimple(const uint8_t loss, - const uint32_t rtt, - const uint32_t now_ms, - uint32_t* bitrate) { - uint32_t new_bitrate = 0; - bool reducing = false; - - // Limit the rate increases to once a kBWEIncreaseIntervalMs. - if (loss <= 5) { - if ((now_ms - time_last_increase_) < kBWEIncreaseIntervalMs) { - return false; - } - time_last_increase_ = now_ms; - } - // Limit the rate decreases to once a kBWEDecreaseIntervalMs + rtt. - if (loss > 26) { - if ((now_ms - time_last_decrease_) < kBWEDecreaseIntervalMs + rtt) { - return false; + } else if (last_fraction_loss_ <= 26) { + // Loss between 2% - 10%: Do nothing. + + } else { + // Loss > 10%: Limit the rate decreases to once a kBweDecreaseIntervalMs + + // rtt. + if ((now_ms - time_last_decrease_ms_) >= + static_cast(kBweDecreaseIntervalMs + + last_round_trip_time_ms_)) { + time_last_decrease_ms_ = now_ms; + + // Reduce rate: + // newRate = rate * (1 - 0.5*lossRate); + // where packetLoss = 256*lossRate; + bitrate_ = static_cast( + (bitrate_ * static_cast(512 - last_fraction_loss_)) / + 512.0); + + // Calculate what rate TFRC would apply in this situation and to not + // reduce further than it. + bitrate_ = std::max( + bitrate_, + CalcTfrcBps(last_round_trip_time_ms_, last_fraction_loss_)); + } } - time_last_decrease_ = now_ms; } + bitrate_ = CapBitrateToThresholds(bitrate_); +} - if (loss > 5 && loss <= 26) { - // 2% - 10% - new_bitrate = bitrate_; - } else if (loss > 26) { - // 26/256 ~= 10% - // reduce rate: newRate = rate * (1 - 0.5*lossRate) - // packetLoss = 256*lossRate - new_bitrate = static_cast((bitrate_ * - static_cast(512 - loss)) / 512.0); - reducing = true; - } else { - // increase rate by 8% - new_bitrate = static_cast(bitrate_ * 1.08 + 0.5); +bool SendSideBandwidthEstimation::IsInStartPhase(int64_t now_ms) const { + return first_report_time_ms_ == -1 || + now_ms - first_report_time_ms_ < kStartPhaseMs; +} - // add 1 kbps extra, just to make sure that we do not get stuck - // (gives a little extra increase at low rates, negligible at higher rates) - new_bitrate += 1000; - } - if (reducing) { - // Calculate what rate TFRC would apply in this situation - // scale loss to Q0 (back to [0, 255]) - uint32_t tfrc_bitrate = CalcTFRCbps(rtt, loss); - if (tfrc_bitrate > new_bitrate) { - // do not reduce further if rate is below TFRC rate - new_bitrate = tfrc_bitrate; - } - } - if (bwe_incoming_ > 0 && new_bitrate > bwe_incoming_) { - new_bitrate = bwe_incoming_; +void SendSideBandwidthEstimation::UpdateMinHistory(uint32_t now_ms) { + // Remove old data points from history. + // Since history precision is in ms, add one so it is able to increase + // bitrate if it is off by as little as 0.5ms. + while (!min_bitrate_history_.empty() && + now_ms - min_bitrate_history_.front().first + 1 > + kBweIncreaseIntervalMs) { + min_bitrate_history_.pop_front(); + } + + // Typical minimum sliding-window algorithm: Pop values higher than current + // bitrate before pushing it. + while (!min_bitrate_history_.empty() && + bitrate_ <= min_bitrate_history_.back().second) { + min_bitrate_history_.pop_back(); } - if (new_bitrate > max_bitrate_configured_) { - new_bitrate = max_bitrate_configured_; - } - if (new_bitrate < min_bitrate_configured_) { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1, - "The configured min bitrate (%u kbps) is greater than the " - "estimated available bandwidth (%u kbps).\n", - min_bitrate_configured_ / 1000, new_bitrate / 1000); - new_bitrate = min_bitrate_configured_; + + min_bitrate_history_.push_back(std::make_pair(now_ms, bitrate_)); +} + +uint32_t SendSideBandwidthEstimation::CapBitrateToThresholds(uint32_t bitrate) { + if (bwe_incoming_ > 0 && bitrate > bwe_incoming_) { + bitrate = bwe_incoming_; + } + if (bitrate > max_bitrate_configured_) { + bitrate = max_bitrate_configured_; + } + if (bitrate < min_bitrate_configured_) { + LOG(LS_WARNING) << "Estimated available bandwidth " << bitrate / 1000 + << " kbps is below configured min bitrate " + << min_bitrate_configured_ / 1000 << " kbps."; + bitrate = min_bitrate_configured_; } - *bitrate = new_bitrate; - return true; + return bitrate; } + } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/send_side_bandwidth_estimation.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/send_side_bandwidth_estimation.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/send_side_bandwidth_estimation.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/bitrate_controller/send_side_bandwidth_estimation.h 2015-02-03 14:33:36.000000000 +0000 @@ -13,6 +13,8 @@ #ifndef WEBRTC_MODULES_BITRATE_CONTROLLER_SEND_SIDE_BANDWIDTH_ESTIMATION_H_ #define WEBRTC_MODULES_BITRATE_CONTROLLER_SEND_SIDE_BANDWIDTH_ESTIMATION_H_ +#include + #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" @@ -22,38 +24,41 @@ SendSideBandwidthEstimation(); virtual ~SendSideBandwidthEstimation(); - // Call when we receive a RTCP message with TMMBR or REMB - // Return true if new_bitrate is valid. - bool UpdateBandwidthEstimate(const uint32_t bandwidth, - uint32_t* new_bitrate, - uint8_t* fraction_lost, - uint16_t* rtt); - - // Call when we receive a RTCP message with a ReceiveBlock - // Return true if new_bitrate is valid. - bool UpdatePacketLoss(const int number_of_packets, - const uint32_t rtt, - const uint32_t now_ms, - uint8_t* loss, - uint32_t* new_bitrate); - - // Return false if no bandwidth estimate is available - bool AvailableBandwidth(uint32_t* bandwidth) const; - void SetSendBitrate(const uint32_t bitrate); - void SetMinMaxBitrate(const uint32_t min_bitrate, const uint32_t max_bitrate); + void CurrentEstimate(uint32_t* bitrate, uint8_t* loss, uint32_t* rtt) const; + + // Call periodically to update estimate. + void UpdateEstimate(uint32_t now_ms); + + // Call when we receive a RTCP message with TMMBR or REMB. + void UpdateReceiverEstimate(uint32_t bandwidth); + + // Call when we receive a RTCP message with a ReceiveBlock. + void UpdateReceiverBlock(uint8_t fraction_loss, + uint32_t rtt, + int number_of_packets, + uint32_t now_ms); + + void SetSendBitrate(uint32_t bitrate); + void SetMinMaxBitrate(uint32_t min_bitrate, uint32_t max_bitrate); + void SetMinBitrate(uint32_t min_bitrate); private: - bool ShapeSimple(const uint8_t loss, const uint32_t rtt, - const uint32_t now_ms, uint32_t* bitrate); + enum UmaState { kNoUpdate, kFirstDone, kDone }; + + bool IsInStartPhase(int64_t now_ms) const; + + void UpdateUmaStats(int64_t now_ms, int rtt, int lost_packets); - uint32_t CalcTFRCbps(uint16_t rtt, uint8_t loss); + // Returns the input bitrate capped to the thresholds defined by the max, + // min and incoming bandwidth. + uint32_t CapBitrateToThresholds(uint32_t bitrate); - enum { kBWEIncreaseIntervalMs = 1000 }; - enum { kBWEDecreaseIntervalMs = 300 }; - enum { kLimitNumPackets = 20 }; - enum { kAvgPacketSizeBytes = 1000 }; + // Updates history of min bitrates. + // After this method returns min_bitrate_history_.front().second contains the + // min bitrate used during last kBweIncreaseIntervalMs. + void UpdateMinHistory(uint32_t now_ms); - CriticalSectionWrapper* critsect_; + std::deque > min_bitrate_history_; // incoming filters int accumulate_lost_packets_Q8_; @@ -63,12 +68,16 @@ uint32_t min_bitrate_configured_; uint32_t max_bitrate_configured_; + uint32_t time_last_receiver_block_ms_; uint8_t last_fraction_loss_; - uint16_t last_round_trip_time_; + uint16_t last_round_trip_time_ms_; uint32_t bwe_incoming_; - uint32_t time_last_increase_; - uint32_t time_last_decrease_; + uint32_t time_last_decrease_ms_; + int64_t first_report_time_ms_; + int initially_lost_packets_; + int bitrate_at_2_seconds_kbps_; + UmaState uma_update_state_; }; } // namespace webrtc #endif // WEBRTC_MODULES_BITRATE_CONTROLLER_SEND_SIDE_BANDWIDTH_ESTIMATION_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer.h 2015-02-03 14:33:36.000000000 +0000 @@ -16,7 +16,6 @@ #include "webrtc/modules/desktop_capture/desktop_capture_types.h" #include "webrtc/modules/desktop_capture/desktop_capturer.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/typedefs.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/BUILD.gn 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,143 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("//build/config/ui.gni") +import("../../build/webrtc.gni") + +use_desktop_capture_differ_sse2 = + (!is_ios && (cpu_arch == "x86" || cpu_arch == "x64")) + +source_set("desktop_capture") { + sources = [ + "desktop_and_cursor_composer.cc", + "desktop_and_cursor_composer.h", + "desktop_capture_types.h", + "desktop_capturer.h", + "desktop_frame.cc", + "desktop_frame.h", + "desktop_frame_win.cc", + "desktop_frame_win.h", + "desktop_geometry.cc", + "desktop_geometry.h", + "desktop_capture_options.h", + "desktop_capture_options.cc", + "desktop_capturer.h", + "desktop_region.cc", + "desktop_region.h", + "differ.cc", + "differ.h", + "differ_block.cc", + "differ_block.h", + "mac/desktop_configuration.h", + "mac/desktop_configuration.mm", + "mac/desktop_configuration_monitor.h", + "mac/desktop_configuration_monitor.cc", + "mac/full_screen_chrome_window_detector.cc", + "mac/full_screen_chrome_window_detector.h", + "mac/scoped_pixel_buffer_object.cc", + "mac/scoped_pixel_buffer_object.h", + "mac/window_list_utils.cc", + "mac/window_list_utils.h", + "mouse_cursor.cc", + "mouse_cursor.h", + "mouse_cursor_monitor.h", + "mouse_cursor_monitor_mac.mm", + "mouse_cursor_monitor_win.cc", + "screen_capture_frame_queue.cc", + "screen_capture_frame_queue.h", + "screen_capturer.cc", + "screen_capturer.h", + "screen_capturer_helper.cc", + "screen_capturer_helper.h", + "screen_capturer_mac.mm", + "screen_capturer_win.cc", + "shared_desktop_frame.cc", + "shared_desktop_frame.h", + "shared_memory.cc", + "shared_memory.h", + "win/cursor.cc", + "win/cursor.h", + "win/desktop.cc", + "win/desktop.h", + "win/scoped_gdi_object.h", + "win/scoped_thread_desktop.cc", + "win/scoped_thread_desktop.h", + "win/screen_capturer_win_gdi.cc", + "win/screen_capturer_win_gdi.h", + "win/screen_capturer_win_magnifier.cc", + "win/screen_capturer_win_magnifier.h", + "win/screen_capture_utils.cc", + "win/screen_capture_utils.h", + "win/window_capture_utils.cc", + "win/window_capture_utils.h", + "window_capturer.cc", + "window_capturer.h", + "window_capturer_mac.mm", + "window_capturer_win.cc", + ] + + if (use_x11) { + sources += [ + "mouse_cursor_monitor_x11.cc", + "screen_capturer_x11.cc", + "window_capturer_x11.cc", + "x11/shared_x_display.h", + "x11/shared_x_display.cc", + "x11/x_error_trap.cc", + "x11/x_error_trap.h", + "x11/x_server_pixel_buffer.cc", + "x11/x_server_pixel_buffer.h", + ] + configs += ["//build/config/linux:x11"] + } + + if (!is_win && !is_mac && !use_x11) { + sources += [ + "mouse_cursor_monitor_null.cc", + "screen_capturer_null.cc", + "window_capturer_null.cc", + ] + } + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config"] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ + "../../system_wrappers", + "../../base:webrtc_base", + ] + + if (use_desktop_capture_differ_sse2) { + deps += [":desktop_capture_differ_sse2"] + } +} + +if (use_desktop_capture_differ_sse2) { + # Have to be compiled as a separate target because it needs to be compiled + # with SSE2 enabled. + source_set("desktop_capture_differ_sse2") { + visibility = [ ":*" ] + sources = [ + "differ_block_sse2.cc", + "differ_block_sse2.h", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_posix && !is_mac) { + cflags = ["-msse2"] + } + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer.cc 2015-02-03 14:33:36.000000000 +0000 @@ -142,6 +142,10 @@ desktop_capturer_->Capture(region); } +void DesktopAndCursorComposer::SetExcludedWindow(WindowId window) { + desktop_capturer_->SetExcludedWindow(window); +} + SharedMemory* DesktopAndCursorComposer::CreateSharedMemory(size_t size) { return callback_->CreateSharedMemory(size); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer.h 2015-02-03 14:33:36.000000000 +0000 @@ -34,6 +34,7 @@ // DesktopCapturer interface. virtual void Start(DesktopCapturer::Callback* callback) OVERRIDE; virtual void Capture(const DesktopRegion& region) OVERRIDE; + virtual void SetExcludedWindow(WindowId window) OVERRIDE; private: // DesktopCapturer::Callback interface. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,7 +10,7 @@ #include "webrtc/modules/desktop_capture/desktop_and_cursor_composer.h" -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/desktop_capture/desktop_capture_options.h" #include "webrtc/modules/desktop_capture/desktop_frame.h" #include "webrtc/modules/desktop_capture/mouse_cursor.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture.gypi 2015-02-03 14:33:36.000000000 +0000 @@ -16,6 +16,7 @@ 'type': 'static_library', 'dependencies': [ '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + '<(webrtc_root)/base/base.gyp:rtc_base_approved', ], 'sources': [ "desktop_and_cursor_composer.cc", @@ -35,10 +36,13 @@ "differ.h", "differ_block.cc", "differ_block.h", +# "mac/full_screen_chrome_window_detector.cc", +# "mac/full_screen_chrome_window_detector.h", +# "mac/window_list_utils.cc", +# "mac/window_list_utils.h", "mouse_cursor.cc", "mouse_cursor.h", "mouse_cursor_monitor.h", - "mouse_cursor_shape.h", "screen_capture_frame_queue.cc", "screen_capture_frame_queue.h", "screen_capturer.cc", @@ -49,6 +53,14 @@ "shared_desktop_frame.h", "shared_memory.cc", "shared_memory.h", +# "win/screen_capturer_win_gdi.cc", +# "win/screen_capturer_win_gdi.h", +# "win/screen_capturer_win_magnifier.cc", +# "win/screen_capturer_win_magnifier.h", +# "win/screen_capture_utils.cc", +# "win/screen_capture_utils.h", +# "win/window_capture_utils.cc", +# "win/window_capture_utils.h", "window_capturer.cc", "window_capturer.h", "desktop_device_info.h", @@ -119,8 +131,10 @@ "mac/desktop_configuration.mm", "mac/desktop_configuration_monitor.h", "mac/desktop_configuration_monitor.cc", - "mac/osx_version.h", - "mac/osx_version.cc", + "mac/full_screen_chrome_window_detector.cc", + "mac/full_screen_chrome_window_detector.h", + "mac/window_list_utils.cc", + "mac/window_list_utils.h", "mac/scoped_pixel_buffer_object.cc", "mac/scoped_pixel_buffer_object.h", "mac/desktop_device_info_mac.h", @@ -155,6 +169,14 @@ "win/win_shared.cc", "win/desktop_device_info_win.h", "win/desktop_device_info_win.cc", + "win/screen_capturer_win_gdi.cc", + "win/screen_capturer_win_gdi.h", + "win/screen_capturer_win_magnifier.cc", + "win/screen_capturer_win_magnifier.h", + "win/screen_capture_utils.cc", + "win/screen_capture_utils.h", + "win/window_capture_utils.cc", + "win/window_capture_utils.h", "window_capturer_win.cc", "app_capturer_win.cc", ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.cc 2015-02-03 14:33:36.000000000 +0000 @@ -19,6 +19,10 @@ // XDamage is often broken, so don't use it by default. use_update_notifications_ = false; #endif + +#if defined(WEBRTC_WIN) + allow_use_magnification_api_ = false; +#endif } DesktopCaptureOptions::~DesktopCaptureOptions() {} @@ -31,6 +35,8 @@ #endif #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) result.set_configuration_monitor(new DesktopConfigurationMonitor()); + result.set_full_screen_chrome_window_detector( + new FullScreenChromeWindowDetector()); #endif return result; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.h 2015-02-03 14:33:36.000000000 +0000 @@ -10,7 +10,7 @@ #ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_OPTIONS_H_ #define WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_OPTIONS_H_ -#include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/base/constructormagic.h" #include "webrtc/system_wrappers/interface/scoped_refptr.h" #if defined(USE_X11) @@ -19,6 +19,7 @@ #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) #include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h" +#include "webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.h" #endif namespace webrtc { @@ -50,6 +51,14 @@ void set_configuration_monitor(scoped_refptr m) { configuration_monitor_ = m; } + + FullScreenChromeWindowDetector* full_screen_chrome_window_detector() const { + return full_screen_window_detector_; + } + void set_full_screen_chrome_window_detector( + scoped_refptr detector) { + full_screen_window_detector_ = detector; + } #endif // Flag indicating that the capturer should use screen change notifications. @@ -66,6 +75,15 @@ disable_effects_ = disable_effects; } +#if defined(WEBRTC_WIN) + bool allow_use_magnification_api() const { + return allow_use_magnification_api_; + } + void set_allow_use_magnification_api(bool allow) { + allow_use_magnification_api_ = allow; + } +#endif + private: #if defined(USE_X11) scoped_refptr x_display_; @@ -73,6 +91,11 @@ #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) scoped_refptr configuration_monitor_; + scoped_refptr full_screen_window_detector_; +#endif + +#if defined(WEBRTC_WIN) + bool allow_use_magnification_api_; #endif bool use_update_notifications_; bool disable_effects_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.h 2015-02-03 14:33:36.000000000 +0000 @@ -13,6 +13,8 @@ #include +#include "webrtc/modules/desktop_capture/desktop_capture_types.h" + namespace webrtc { class DesktopFrame; @@ -52,6 +54,11 @@ // the top left corner of the capture target. Pending capture operations are // canceled when DesktopCapturer is deleted. virtual void Capture(const DesktopRegion& region) = 0; + + // Sets the window to be excluded from the captured image in the future + // Capture calls. Used to exclude the screenshare notification window for + // screen capturing. + virtual void SetExcludedWindow(WindowId window) {} }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_geometry.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_geometry.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_geometry.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_geometry.h 2015-02-03 14:33:36.000000000 +0000 @@ -11,8 +11,8 @@ #ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_GEOMETRY_H_ #define WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_GEOMETRY_H_ +#include "webrtc/base/constructormagic.h" #include "webrtc/typedefs.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" namespace webrtc { @@ -58,7 +58,7 @@ int32_t width() const { return width_; } int32_t height() const { return height_; } - bool is_empty() const { return width_ <= 0 && height_ <= 0; } + bool is_empty() const { return width_ <= 0 || height_ <= 0; } bool equals(const DesktopSize& other) const { return width_ == other.width_ && height_ == other.height_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_region.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_region.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_region.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_region.h 2015-02-03 14:33:36.000000000 +0000 @@ -14,8 +14,8 @@ #include #include +#include "webrtc/base/constructormagic.h" #include "webrtc/modules/desktop_capture/desktop_geometry.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/typedefs.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_region_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_region_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_region_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_region_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -12,7 +12,7 @@ #include -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/differ.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/differ.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/differ.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/differ.h 2015-02-03 14:33:36.000000000 +0000 @@ -76,7 +76,7 @@ int bytes_per_row_; // Diff information for each block in the image. - scoped_array diff_info_; + scoped_ptr diff_info_; // Dimensions and total size of diff info array. int diff_info_width_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/differ_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/differ_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/differ_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/differ_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -200,8 +200,8 @@ int buffer_size_; // Previous and current screen buffers. - scoped_array prev_; - scoped_array curr_; + scoped_ptr prev_; + scoped_ptr curr_; private: DISALLOW_COPY_AND_ASSIGN(DifferTest); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_configuration.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_configuration.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_configuration.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_configuration.h 2015-02-03 14:33:36.000000000 +0000 @@ -59,10 +59,11 @@ const MacDisplayConfiguration* FindDisplayConfigurationById( CGDirectDisplayID id); - // Bounds of the desktop in Density-Independent Pixels (DIPs). + // Bounds of the desktop excluding monitors with DPI settings different from + // the main monitor. In Density-Independent Pixels (DIPs). DesktopRect bounds; - // Bounds of the desktop in physical pixels. + // Same as bounds, but expressed in physical pixels. DesktopRect pixel_bounds; // Scale factor from DIPs to physical pixels. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_configuration.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_configuration.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_configuration.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_configuration.mm 2015-02-03 14:33:36.000000000 +0000 @@ -134,11 +134,15 @@ // Add the display to the configuration. desktop_config.displays.push_back(display_config); - // Update the desktop bounds to account for this display. - desktop_config.bounds = - JoinRects(desktop_config.bounds, display_config.bounds); - desktop_config.pixel_bounds = - JoinRects(desktop_config.pixel_bounds, display_config.pixel_bounds); + // Update the desktop bounds to account for this display, unless the current + // display uses different DPI settings. + if (display_config.dip_to_pixel_scale == + desktop_config.dip_to_pixel_scale) { + desktop_config.bounds = + JoinRects(desktop_config.bounds, display_config.bounds); + desktop_config.pixel_bounds = + JoinRects(desktop_config.pixel_bounds, display_config.pixel_bounds); + } } return desktop_config; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.h" + +#include +#include +#include + +#include "webrtc/base/macutils.h" +#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h" +#include "webrtc/modules/desktop_capture/mac/window_list_utils.h" +#include "webrtc/system_wrappers/interface/logging.h" + + +namespace webrtc { + +namespace { + +const int64_t kUpdateIntervalMs = 500; + +// Returns true if the window is minimized. +bool IsWindowMinimized(CGWindowID id) { + CFArrayRef window_id_array = + CFArrayCreate(NULL, reinterpret_cast(&id), 1, NULL); + CFArrayRef window_array = + CGWindowListCreateDescriptionFromArray(window_id_array); + bool minimized = false; + + if (window_array && CFArrayGetCount(window_array)) { + CFDictionaryRef window = reinterpret_cast( + CFArrayGetValueAtIndex(window_array, 0)); + CFBooleanRef on_screen = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowIsOnscreen)); + + minimized = !on_screen; + } + + CFRelease(window_id_array); + CFRelease(window_array); + + return minimized; +} + +// Returns true if the window is occupying a full screen. +bool IsWindowFullScreen(const MacDesktopConfiguration& desktop_config, + CFDictionaryRef window) { + bool fullscreen = false; + + CFDictionaryRef bounds_ref = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowBounds)); + + CGRect bounds; + if (bounds_ref && + CGRectMakeWithDictionaryRepresentation(bounds_ref, &bounds)) { + for (MacDisplayConfigurations::const_iterator it = + desktop_config.displays.begin(); + it != desktop_config.displays.end(); ++it) { + if (it->bounds.equals(DesktopRect::MakeXYWH(bounds.origin.x, + bounds.origin.y, + bounds.size.width, + bounds.size.height))) { + fullscreen = true; + break; + } + } + } + + return fullscreen; +} + +std::string GetWindowTitle(CGWindowID id) { + CFArrayRef window_id_array = + CFArrayCreate(NULL, reinterpret_cast(&id), 1, NULL); + CFArrayRef window_array = + CGWindowListCreateDescriptionFromArray(window_id_array); + std::string title; + + if (window_array && CFArrayGetCount(window_array)) { + CFDictionaryRef window = reinterpret_cast( + CFArrayGetValueAtIndex(window_array, 0)); + CFStringRef title_ref = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowName)); + + if (title_ref) + rtc::ToUtf8(title_ref, &title); + } + CFRelease(window_id_array); + CFRelease(window_array); + + return title; +} + +int GetWindowOwnerPid(CGWindowID id) { + CFArrayRef window_id_array = + CFArrayCreate(NULL, reinterpret_cast(&id), 1, NULL); + CFArrayRef window_array = + CGWindowListCreateDescriptionFromArray(window_id_array); + int pid = 0; + + if (window_array && CFArrayGetCount(window_array)) { + CFDictionaryRef window = reinterpret_cast( + CFArrayGetValueAtIndex(window_array, 0)); + CFNumberRef pid_ref = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowOwnerPID)); + + if (pid_ref) + CFNumberGetValue(pid_ref, kCFNumberIntType, &pid); + } + CFRelease(window_id_array); + CFRelease(window_array); + + return pid; +} + +// Returns the window that is full-screen and has the same title and owner pid +// as the input window. +CGWindowID FindFullScreenWindowWithSamePidAndTitle(CGWindowID id) { + int pid = GetWindowOwnerPid(id); + std::string title = GetWindowTitle(id); + + // Only get on screen, non-desktop windows. + CFArrayRef window_array = CGWindowListCopyWindowInfo( + kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, + kCGNullWindowID); + if (!window_array) + return kCGNullWindowID; + + CGWindowID full_screen_window = kCGNullWindowID; + + MacDesktopConfiguration desktop_config = MacDesktopConfiguration::GetCurrent( + MacDesktopConfiguration::TopLeftOrigin); + + // Check windows to make sure they have an id, title, and use window layer + // other than 0. + CFIndex count = CFArrayGetCount(window_array); + for (CFIndex i = 0; i < count; ++i) { + CFDictionaryRef window = reinterpret_cast( + CFArrayGetValueAtIndex(window_array, i)); + CFStringRef window_title_ref = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowName)); + CFNumberRef window_id_ref = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowNumber)); + CFNumberRef window_pid_ref = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowOwnerPID)); + + if (!window_title_ref || !window_id_ref || !window_pid_ref) + continue; + + int window_pid = 0; + CFNumberGetValue(window_pid_ref, kCFNumberIntType, &window_pid); + if (window_pid != pid) + continue; + + std::string window_title; + if (!rtc::ToUtf8(window_title_ref, &window_title) || + window_title != title) { + continue; + } + + CGWindowID window_id; + CFNumberGetValue(window_id_ref, kCFNumberIntType, &window_id); + if (IsWindowFullScreen(desktop_config, window)) { + full_screen_window = window_id; + break; + } + } + + CFRelease(window_array); + return full_screen_window; +} + +bool IsChromeWindow(CGWindowID id) { + int pid = GetWindowOwnerPid(id); + char buffer[PROC_PIDPATHINFO_MAXSIZE]; + int path_length = proc_pidpath(pid, buffer, sizeof(buffer)); + if (path_length <= 0) + return false; + + const char* last_slash = strrchr(buffer, '/'); + std::string name(last_slash ? last_slash + 1 : buffer); + return name.find("Google Chrome") == 0 || name == "Chromium"; +} + +} // namespace + +FullScreenChromeWindowDetector::FullScreenChromeWindowDetector() + : ref_count_(0) {} + +FullScreenChromeWindowDetector::~FullScreenChromeWindowDetector() {} + +CGWindowID FullScreenChromeWindowDetector::FindFullScreenWindow( + CGWindowID original_window) { + if (!IsChromeWindow(original_window) || !IsWindowMinimized(original_window)) + return kCGNullWindowID; + + CGWindowID full_screen_window_id = + FindFullScreenWindowWithSamePidAndTitle(original_window); + + if (full_screen_window_id == kCGNullWindowID) + return kCGNullWindowID; + + for (WindowCapturer::WindowList::iterator it = previous_window_list_.begin(); + it != previous_window_list_.end(); ++it) { + if (static_cast(it->id) != full_screen_window_id) + continue; + + int64_t time_interval = + (TickTime::Now() - last_udpate_time_).Milliseconds(); + LOG(LS_WARNING) << "The full-screen window exists in the list, " + << "which was updated " << time_interval << "ms ago."; + return kCGNullWindowID; + } + + return full_screen_window_id; +} + +void FullScreenChromeWindowDetector::UpdateWindowListIfNeeded( + CGWindowID original_window) { + if (IsChromeWindow(original_window) && + (TickTime::Now() - last_udpate_time_).Milliseconds() + > kUpdateIntervalMs) { + previous_window_list_.clear(); + previous_window_list_.swap(current_window_list_); + + // No need to update the window list when the window is minimized. + if (IsWindowMinimized(original_window)) { + previous_window_list_.clear(); + return; + } + + GetWindowList(¤t_window_list_); + last_udpate_time_ = TickTime::Now(); + } +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.h 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_FULL_SCREEN_CHROME_WINDOW_DETECTOR_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_FULL_SCREEN_CHROME_WINDOW_DETECTOR_H_ + +#include + +#include "webrtc/modules/desktop_capture/window_capturer.h" +#include "webrtc/system_wrappers/interface/atomic32.h" +#include "webrtc/system_wrappers/interface/tick_util.h" + +namespace webrtc { + +// This is a work around for the Chrome tab full-screen behavior: Chrome +// creates a new window in full-screen mode to show a tab full-screen and +// minimizes the old window. To continue capturing in this case, we try to +// find the new full-screen window using these criteria: +// 0. The original shared window is minimized. +// 1. The original shared window's owner application name is "Google Chrome". +// 2. The original window and the new window have the same title and owner +// pid. +// 3. The new window is full-screen. +// 4. The new window didn't exist at least 500 millisecond ago. + +class FullScreenChromeWindowDetector { + public: + FullScreenChromeWindowDetector(); + + void AddRef() { ++ref_count_; } + void Release() { + if (--ref_count_ == 0) + delete this; + } + + // Returns the full-screen window in place of the original window if all the + // criteria are met, or kCGNullWindowID if no such window found. + CGWindowID FindFullScreenWindow(CGWindowID original_window); + + // The caller should call this function periodically, no less than twice per + // second. + void UpdateWindowListIfNeeded(CGWindowID original_window); + + private: + ~FullScreenChromeWindowDetector(); + + Atomic32 ref_count_; + + // We cache the last two results of the window list, so + // |previous_window_list_| is taken at least 500ms before the next Capture() + // call. If we only save the last result, we may get false positive (i.e. + // full-screen window exists in the list) if Capture() is called too soon. + WindowCapturer::WindowList current_window_list_; + WindowCapturer::WindowList previous_window_list_; + TickTime last_udpate_time_; + + DISALLOW_COPY_AND_ASSIGN(FullScreenChromeWindowDetector); +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_FULL_SCREEN_CHROME_WINDOW_DETECTOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/osx_version.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/osx_version.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/osx_version.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/osx_version.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "webrtc/system_wrappers/interface/logging.h" - -namespace webrtc { - -namespace { - -int GetDarwinVersion() { - struct utsname uname_info; - if (uname(&uname_info) != 0) { - LOG(LS_ERROR) << "uname failed"; - return 0; - } - - if (strcmp(uname_info.sysname, "Darwin") != 0) - return 0; - - char* dot; - int result = strtol(uname_info.release, &dot, 10); - if (*dot != '.') { - LOG(LS_ERROR) << "Failed to parse version"; - return 0; - } - - return result; -} - -} // namespace - -bool IsOSLionOrLater() { - static int darwin_version = GetDarwinVersion(); - - // Verify that the version has been parsed correctly. - if (darwin_version < 6) { - LOG_F(LS_ERROR) << "Invalid Darwin version: " << darwin_version; - abort(); - } - - // Darwin major version 11 corresponds to OSX 10.7. - return darwin_version >= 11; -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/osx_version.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/osx_version.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/osx_version.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/osx_version.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -namespace webrtc { - -// Returns true if the OS version >= OSX 10.7. -bool IsOSLionOrLater(); - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/scoped_pixel_buffer_object.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/scoped_pixel_buffer_object.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/scoped_pixel_buffer_object.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/scoped_pixel_buffer_object.h 2015-02-03 14:33:36.000000000 +0000 @@ -14,7 +14,7 @@ #include #include -#include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/base/constructormagic.h" #include "webrtc/typedefs.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/window_list_utils.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/window_list_utils.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/window_list_utils.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/window_list_utils.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/mac/window_list_utils.h" + +#include + +#include "webrtc/base/macutils.h" + +namespace webrtc { + +bool GetWindowList(WindowCapturer::WindowList* windows) { + // Only get on screen, non-desktop windows. + CFArrayRef window_array = CGWindowListCopyWindowInfo( + kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, + kCGNullWindowID); + if (!window_array) + return false; + + // Check windows to make sure they have an id, title, and use window layer + // other than 0. + CFIndex count = CFArrayGetCount(window_array); + for (CFIndex i = 0; i < count; ++i) { + CFDictionaryRef window = reinterpret_cast( + CFArrayGetValueAtIndex(window_array, i)); + CFStringRef window_title = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowName)); + CFNumberRef window_id = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowNumber)); + CFNumberRef window_layer = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowLayer)); + if (window_title && window_id && window_layer) { + // Skip windows with layer=0 (menu, dock). + int layer; + CFNumberGetValue(window_layer, kCFNumberIntType, &layer); + if (layer != 0) + continue; + + int id; + CFNumberGetValue(window_id, kCFNumberIntType, &id); + WindowCapturer::Window window; + window.id = id; + if (!rtc::ToUtf8(window_title, &(window.title)) || + window.title.empty()) { + continue; + } + windows->push_back(window); + } + } + + CFRelease(window_array); + return true; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/window_list_utils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/window_list_utils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/window_list_utils.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/window_list_utils.h 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_LIST_UTILS_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_LIST_UTILS_H_ + +#include "webrtc/modules/desktop_capture/window_capturer.h" + +namespace webrtc { + +// A helper function to get the on-screen windows. +bool GetWindowList(WindowCapturer::WindowList* windows); + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_LIST_UTILS_H_ + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,6 +10,8 @@ #include "webrtc/modules/desktop_capture/mouse_cursor.h" +#include + #include "webrtc/modules/desktop_capture/desktop_frame.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor.h 2015-02-03 14:33:36.000000000 +0000 @@ -11,8 +11,8 @@ #ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_MOUSE_CURSOR_H_ #define WEBRTC_MODULES_DESKTOP_CAPTURE_MOUSE_CURSOR_H_ +#include "webrtc/base/constructormagic.h" #include "webrtc/modules/desktop_capture/desktop_geometry.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm 2015-02-03 14:33:36.000000000 +0000 @@ -15,11 +15,12 @@ #include #include +#include "webrtc/base/macutils.h" #include "webrtc/modules/desktop_capture/desktop_capture_options.h" #include "webrtc/modules/desktop_capture/desktop_frame.h" #include "webrtc/modules/desktop_capture/mac/desktop_configuration.h" #include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h" -#include "webrtc/modules/desktop_capture/mac/osx_version.h" +#include "webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.h" #include "webrtc/modules/desktop_capture/mouse_cursor.h" #include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" @@ -52,6 +53,8 @@ Callback* callback_; Mode mode_; scoped_ptr last_cursor_; + scoped_refptr + full_screen_chrome_window_detector_; }; MouseCursorMonitorMac::MouseCursorMonitorMac( @@ -62,9 +65,12 @@ window_id_(window_id), screen_id_(screen_id), callback_(NULL), - mode_(SHAPE_AND_POSITION) { + mode_(SHAPE_AND_POSITION), + full_screen_chrome_window_detector_( + options.full_screen_chrome_window_detector()) { assert(window_id == kCGNullWindowID || screen_id == kInvalidScreenId); - if (screen_id != kInvalidScreenId && !IsOSLionOrLater()) { + if (screen_id != kInvalidScreenId && + rtc::GetOSVersionName() < rtc::kMacOSLion) { // Single screen capture is not supported on pre OS X 10.7. screen_id_ = kFullDesktopScreenId; } @@ -115,14 +121,23 @@ // if the current mouse position is covered by another window and also adjust // |position| to make it relative to the window origin. if (window_id_ != kCGNullWindowID) { - // Get list of windows that may be covering parts of |window_id_|. + CGWindowID on_screen_window = window_id_; + if (full_screen_chrome_window_detector_) { + CGWindowID full_screen_window = + full_screen_chrome_window_detector_->FindFullScreenWindow(window_id_); + + if (full_screen_window != kCGNullWindowID) + on_screen_window = full_screen_window; + } + + // Get list of windows that may be covering parts of |on_screen_window|. // CGWindowListCopyWindowInfo() returns windows in order from front to back, - // so |window_id_| is expected to be the last in the list. + // so |on_screen_window| is expected to be the last in the list. CFArrayRef window_array = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListOptionOnScreenAboveWindow | kCGWindowListOptionIncludingWindow, - window_id_); + on_screen_window); bool found_window = false; if (window_array) { CFIndex count = CFArrayGetCount(window_array); @@ -158,7 +173,7 @@ if (!CFNumberGetValue(window_number, kCFNumberIntType, &window_id)) continue; - if (window_id == window_id_) { + if (window_id == on_screen_window) { found_window = true; if (!window_rect.Contains(position)) state = OUTSIDE; @@ -247,6 +262,7 @@ last_cursor_->hotspot().equals(hotspot) && memcmp(last_cursor_->image()->data(), src_data, last_cursor_->image()->stride() * size.height()) == 0) { + CFRelease(image_data_ref); return; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_null.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_null.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_null.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_null.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,7 +10,7 @@ #include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h" -#include +#include namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,7 +10,7 @@ #include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h" -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/desktop_capture/desktop_capture_options.h" #include "webrtc/modules/desktop_capture/desktop_frame.h" #include "webrtc/modules/desktop_capture/mouse_cursor.h" @@ -50,7 +50,12 @@ // tests. Figure out how to do that without breaking other tests in // modules_unittests and enable these tests on Mac. // https://code.google.com/p/webrtc/issues/detail?id=2532 -#if !defined(WEBRTC_MAC) +// +// Disabled on Windows due to flake, see: +// https://code.google.com/p/webrtc/issues/detail?id=3408 +// Disabled on Linux due to flake, see: +// https://code.google.com/p/webrtc/issues/detail?id=3245 +#if !defined(WEBRTC_MAC) && !defined(WEBRTC_WIN) && !defined(WEBRTC_LINUX) #define MAYBE(x) x #else #define MAYBE(x) DISABLED_##x diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,9 +10,12 @@ #include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h" +#include + #include "webrtc/modules/desktop_capture/desktop_frame.h" #include "webrtc/modules/desktop_capture/mouse_cursor.h" #include "webrtc/modules/desktop_capture/win/cursor.h" +#include "webrtc/modules/desktop_capture/win/window_capture_utils.h" #include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { @@ -27,6 +30,9 @@ virtual void Capture() OVERRIDE; private: + // Get the rect of the currently selected screen, relative to the primary + // display's top-left. If the screen is disabled or disconnected, or any error + // happens, an empty rect is returned. DesktopRect GetScreenRect(); HWND window_; @@ -102,8 +108,9 @@ bool inside = cursor_info.flags == CURSOR_SHOWING; if (window_) { - RECT rect; - if (!GetWindowRect(window_, &rect)) { + DesktopRect original_rect; + DesktopRect cropped_rect; + if (!GetCroppedWindowRect(window_, &cropped_rect, &original_rect)) { position.set(0, 0); inside = false; } else { @@ -112,7 +119,7 @@ inside = windowUnderCursor ? (window_ == GetAncestor(windowUnderCursor, GA_ROOT)) : false; } - position = position.subtract(DesktopVector(rect.left, rect.top)); + position = position.subtract(cropped_rect.top_left()); } } else { assert(screen_ != kInvalidScreenId); @@ -149,11 +156,10 @@ if (!result) return DesktopRect(); - return DesktopRect::MakeXYWH( - GetSystemMetrics(SM_XVIRTUALSCREEN) + device_mode.dmPosition.x, - GetSystemMetrics(SM_YVIRTUALSCREEN) + device_mode.dmPosition.y, - device_mode.dmPelsWidth, - device_mode.dmPelsHeight); + return DesktopRect::MakeXYWH(device_mode.dmPosition.x, + device_mode.dmPosition.y, + device_mode.dmPelsWidth, + device_mode.dmPelsHeight); } MouseCursorMonitor* MouseCursorMonitor::CreateForWindow( diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc 2015-02-03 14:33:36.000000000 +0000 @@ -184,9 +184,13 @@ void MouseCursorMonitorX11::CaptureCursor() { assert(have_xfixes_); - XFixesCursorImage* img = XFixesGetCursorImage(display()); - if (!img) - return; + XFixesCursorImage* img; + { + XErrorTrap error_trap(display()); + img = XFixesGetCursorImage(display()); + if (!img || error_trap.GetLastErrorAndDisable() != 0) + return; + } scoped_ptr image( new BasicDesktopFrame(DesktopSize(img->width, img->height))); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_shape.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_shape.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_shape.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_shape.h 2015-02-03 14:33:36.000000000 +0000 @@ -11,26 +11,7 @@ #ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_MOUSE_CURSOR_SHAPE_H_ #define WEBRTC_MODULES_DESKTOP_CAPTURE_MOUSE_CURSOR_SHAPE_H_ -#include - -#include "webrtc/modules/desktop_capture/desktop_geometry.h" - -namespace webrtc { - -// Type used to return mouse cursor shape from video capturers. -// -// TODO(sergeyu): Remove this type and use MouseCursor instead. -struct MouseCursorShape { - // Size of the cursor in screen pixels. - DesktopSize size; - - // Coordinates of the cursor hotspot relative to upper-left corner. - DesktopVector hotspot; - - // Cursor pixmap data in 32-bit BGRA format. - std::string data; -}; - -} // namespace webrtc +// This file is no longer needed, but some code in chromium still includes it. +// TODO(sergeyu): Cleanup dependencies in chromium and remove this file. #endif // WEBRTC_MODULES_DESKTOP_CAPTURE_MOUSE_CURSOR_SHAPE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -1,3 +1,11 @@ alexeypa@chromium.org +jiayl@webrtc.org sergeyu@chromium.org wez@chromium.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capture_frame_queue.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capture_frame_queue.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capture_frame_queue.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capture_frame_queue.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,6 +10,7 @@ #include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h" +#include #include #include "webrtc/modules/desktop_capture/desktop_frame.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer.h 2015-02-03 14:33:36.000000000 +0000 @@ -21,7 +21,6 @@ namespace webrtc { class DesktopCaptureOptions; -struct MouseCursorShape; // Class used to capture video frames asynchronously. // @@ -50,19 +49,9 @@ }; typedef std::vector ScreenList; - // Provides callbacks used by the capturer to pass captured video frames and - // mouse cursor shapes to the processing pipeline. - // - // TODO(sergeyu): Move cursor shape capturing to a separate class because it's - // unrelated. + // TODO(sergeyu): Remove this class once all dependencies are removed from + // chromium. class MouseShapeObserver { - public: - // Called when the cursor shape has changed. Must take ownership of - // |cursor_shape|. - virtual void OnCursorShapeChanged(MouseCursorShape* cursor_shape) = 0; - - protected: - virtual ~MouseShapeObserver() {} }; virtual ~ScreenCapturer() {} @@ -84,10 +73,10 @@ static ScreenCapturer* CreateWithDisableAero(bool disable_aero); #endif // defined(WEBRTC_WIN) - // Called at the beginning of a capturing session. |mouse_shape_observer| must - // remain valid until the capturer is destroyed. + // TODO(sergeyu): Remove this method once all dependencies are removed from + // chromium. virtual void SetMouseShapeObserver( - MouseShapeObserver* mouse_shape_observer) = 0; + MouseShapeObserver* mouse_shape_observer) {}; // Get the list of screens (not containing kFullDesktopScreenId). Returns // false in case of a failure. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_helper.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_helper.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_helper.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_helper.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,6 +10,7 @@ #include "webrtc/modules/desktop_capture/screen_capturer_helper.h" +#include #include #include "webrtc/system_wrappers/interface/logging.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_mac.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_mac.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_mac.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_mac.mm 2015-02-03 14:33:36.000000000 +0000 @@ -20,15 +20,14 @@ #include #include +#include "webrtc/base/macutils.h" #include "webrtc/modules/desktop_capture/desktop_capture_options.h" #include "webrtc/modules/desktop_capture/desktop_frame.h" #include "webrtc/modules/desktop_capture/desktop_geometry.h" #include "webrtc/modules/desktop_capture/desktop_region.h" #include "webrtc/modules/desktop_capture/mac/desktop_configuration.h" #include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h" -#include "webrtc/modules/desktop_capture/mac/osx_version.h" #include "webrtc/modules/desktop_capture/mac/scoped_pixel_buffer_object.h" -#include "webrtc/modules/desktop_capture/mouse_cursor_shape.h" #include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h" #include "webrtc/modules/desktop_capture/screen_capturer_helper.h" #include "webrtc/system_wrappers/interface/logging.h" @@ -63,7 +62,8 @@ static_cast(ceil((rect.origin.y + rect.size.height) * scale))); } -// Copy pixels in the |rect| from |src_place| to |dest_plane|. +// Copy pixels in the |rect| from |src_place| to |dest_plane|. |rect| should be +// relative to the origin of |src_plane| and |dest_plane|. void CopyRect(const uint8_t* src_plane, int src_plane_stride, uint8_t* dest_plane, @@ -87,6 +87,105 @@ } } +// Returns an array of CGWindowID for all the on-screen windows except +// |window_to_exclude|, or NULL if the window is not found or it fails. The +// caller should release the returned CFArrayRef. +CFArrayRef CreateWindowListWithExclusion(CGWindowID window_to_exclude) { + if (!window_to_exclude) + return NULL; + + CFArrayRef all_windows = CGWindowListCopyWindowInfo( + kCGWindowListOptionOnScreenOnly, kCGNullWindowID); + if (!all_windows) + return NULL; + + CFMutableArrayRef returned_array = CFArrayCreateMutable( + NULL, CFArrayGetCount(all_windows), NULL); + + bool found = false; + for (CFIndex i = 0; i < CFArrayGetCount(all_windows); ++i) { + CFDictionaryRef window = reinterpret_cast( + CFArrayGetValueAtIndex(all_windows, i)); + + CFNumberRef id_ref = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowNumber)); + + CGWindowID id; + CFNumberGetValue(id_ref, kCFNumberIntType, &id); + if (id == window_to_exclude) { + found = true; + continue; + } + CFArrayAppendValue(returned_array, reinterpret_cast(id)); + } + CFRelease(all_windows); + + if (!found) { + CFRelease(returned_array); + returned_array = NULL; + } + return returned_array; +} + +// Returns the bounds of |window| in physical pixels, enlarged by a small amount +// on four edges to take account of the border/shadow effects. +DesktopRect GetExcludedWindowPixelBounds(CGWindowID window, + float dip_to_pixel_scale) { + // The amount of pixels to add to the actual window bounds to take into + // account of the border/shadow effects. + static const int kBorderEffectSize = 20; + CGRect rect; + CGWindowID ids[1]; + ids[0] = window; + + CFArrayRef window_id_array = + CFArrayCreate(NULL, reinterpret_cast(&ids), 1, NULL); + CFArrayRef window_array = + CGWindowListCreateDescriptionFromArray(window_id_array); + + if (CFArrayGetCount(window_array) > 0) { + CFDictionaryRef window = reinterpret_cast( + CFArrayGetValueAtIndex(window_array, 0)); + CFDictionaryRef bounds_ref = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowBounds)); + CGRectMakeWithDictionaryRepresentation(bounds_ref, &rect); + } + + CFRelease(window_id_array); + CFRelease(window_array); + + rect.origin.x -= kBorderEffectSize; + rect.origin.y -= kBorderEffectSize; + rect.size.width += kBorderEffectSize * 2; + rect.size.height += kBorderEffectSize * 2; + // |rect| is in DIP, so convert to physical pixels. + return ScaleAndRoundCGRect(rect, dip_to_pixel_scale); +} + +// Create an image of the given region using the given |window_list|. +// |pixel_bounds| should be in the primary display's coordinate in physical +// pixels. The caller should release the returned CGImageRef and CFDataRef. +CGImageRef CreateExcludedWindowRegionImage(const DesktopRect& pixel_bounds, + float dip_to_pixel_scale, + CFArrayRef window_list, + CFDataRef* data_ref) { + CGRect window_bounds; + // The origin is in DIP while the size is in physical pixels. That's what + // CGWindowListCreateImageFromArray expects. + window_bounds.origin.x = pixel_bounds.left() / dip_to_pixel_scale; + window_bounds.origin.y = pixel_bounds.top() / dip_to_pixel_scale; + window_bounds.size.width = pixel_bounds.width(); + window_bounds.size.height = pixel_bounds.height(); + + CGImageRef excluded_image = CGWindowListCreateImageFromArray( + window_bounds, window_list, kCGWindowImageDefault); + + CGDataProviderRef provider = CGImageGetDataProvider(excluded_image); + *data_ref = CGDataProviderCopyData(provider); + assert(*data_ref); + return excluded_image; +} + // A class to perform video frame capturing for mac. class ScreenCapturerMac : public ScreenCapturer { public: @@ -99,14 +198,11 @@ // Overridden from ScreenCapturer: virtual void Start(Callback* callback) OVERRIDE; virtual void Capture(const DesktopRegion& region) OVERRIDE; - virtual void SetMouseShapeObserver( - MouseShapeObserver* mouse_shape_observer) OVERRIDE; + virtual void SetExcludedWindow(WindowId window) OVERRIDE; virtual bool GetScreenList(ScreenList* screens) OVERRIDE; virtual bool SelectScreen(ScreenId id) OVERRIDE; private: - void CaptureCursor(); - void GlBlitFast(const DesktopFrame& frame, const DesktopRegion& region); void GlBlitSlow(const DesktopFrame& frame); @@ -138,7 +234,6 @@ DesktopFrame* CreateFrame(); Callback* callback_; - MouseShapeObserver* mouse_shape_observer_; CGLContextObj cgl_context_; ScopedPixelBufferObject pixel_buffer_object_; @@ -163,9 +258,6 @@ // recently captured screen. ScreenCapturerHelper helper_; - // The last cursor that we sent to the client. - MouseCursorShape last_cursor_; - // Contains an invalid region from the previous capture. DesktopRegion last_invalid_region_; @@ -186,6 +278,8 @@ void* opengl_library_; CGLSetFullScreenFunc cgl_set_full_screen_; + CGWindowID excluded_window_; + DISALLOW_COPY_AND_ASSIGN(ScreenCapturerMac); }; @@ -215,7 +309,6 @@ ScreenCapturerMac::ScreenCapturerMac( scoped_refptr desktop_config_monitor) : callback_(NULL), - mouse_shape_observer_(NULL), cgl_context_(NULL), current_display_(0), dip_to_pixel_scale_(1.0f), @@ -227,7 +320,8 @@ cg_display_bytes_per_row_(NULL), cg_display_bits_per_pixel_(NULL), opengl_library_(NULL), - cgl_set_full_screen_(NULL) { + cgl_set_full_screen_(NULL), + excluded_window_(0) { } ScreenCapturerMac::~ScreenCapturerMac() { @@ -291,8 +385,7 @@ &power_assertion_id_user_); } -void ScreenCapturerMac::Capture( - const DesktopRegion& region_to_capture) { +void ScreenCapturerMac::Capture(const DesktopRegion& region_to_capture) { TickTime capture_start_time = TickTime::Now(); queue_.MoveToNextFrame(); @@ -322,10 +415,11 @@ DesktopFrame* current_frame = queue_.current_frame(); bool flip = false; // GL capturers need flipping. - if (IsOSLionOrLater()) { + if (rtc::GetOSVersionName() >= rtc::kMacOSLion) { // Lion requires us to use their new APIs for doing screen capture. These // APIS currently crash on 10.6.8 if there is no monitor attached. if (!CgBlitPostLion(*current_frame, region)) { + desktop_config_monitor_->Unlock(); callback_->OnCaptureCompleted(NULL); return; } @@ -354,24 +448,18 @@ // and accessing display structures. desktop_config_monitor_->Unlock(); - // Capture the current cursor shape and notify |callback_| if it has changed. - CaptureCursor(); - new_frame->set_capture_time_ms( (TickTime::Now() - capture_start_time).Milliseconds()); callback_->OnCaptureCompleted(new_frame); } -void ScreenCapturerMac::SetMouseShapeObserver( - MouseShapeObserver* mouse_shape_observer) { - assert(!mouse_shape_observer_); - assert(mouse_shape_observer); - mouse_shape_observer_ = mouse_shape_observer; +void ScreenCapturerMac::SetExcludedWindow(WindowId window) { + excluded_window_ = window; } bool ScreenCapturerMac::GetScreenList(ScreenList* screens) { assert(screens->size() == 0); - if (!IsOSLionOrLater()) { + if (rtc::GetOSVersionName() < rtc::kMacOSLion) { // Single monitor cast is not supported on pre OS X 10.7. Screen screen; screen.id = kFullDesktopScreenId; @@ -389,7 +477,7 @@ } bool ScreenCapturerMac::SelectScreen(ScreenId id) { - if (!IsOSLionOrLater()) { + if (rtc::GetOSVersionName() < rtc::kMacOSLion) { // Ignore the screen selection on unsupported OS. assert(!current_display_); return id == kFullDesktopScreenId; @@ -410,61 +498,6 @@ return true; } -void ScreenCapturerMac::CaptureCursor() { - if (!mouse_shape_observer_) - return; - - NSCursor* cursor = [NSCursor currentSystemCursor]; - if (cursor == nil) - return; - - NSImage* nsimage = [cursor image]; - NSPoint hotspot = [cursor hotSpot]; - NSSize size = [nsimage size]; - CGImageRef image = [nsimage CGImageForProposedRect:NULL - context:nil - hints:nil]; - if (image == nil) - return; - - if (CGImageGetBitsPerPixel(image) != 32 || - CGImageGetBytesPerRow(image) != (size.width * 4) || - CGImageGetBitsPerComponent(image) != 8) { - return; - } - - CGDataProviderRef provider = CGImageGetDataProvider(image); - CFDataRef image_data_ref = CGDataProviderCopyData(provider); - if (image_data_ref == NULL) - return; - - const char* cursor_src_data = - reinterpret_cast(CFDataGetBytePtr(image_data_ref)); - int data_size = CFDataGetLength(image_data_ref); - - // Create a MouseCursorShape that describes the cursor and pass it to - // the client. - scoped_ptr cursor_shape(new MouseCursorShape()); - cursor_shape->size.set(size.width, size.height); - cursor_shape->hotspot.set(hotspot.x, hotspot.y); - cursor_shape->data.assign(cursor_src_data, cursor_src_data + data_size); - - CFRelease(image_data_ref); - - // Compare the current cursor with the last one we sent to the client. If - // they're the same, then don't bother sending the cursor again. - if (last_cursor_.size.equals(cursor_shape->size) && - last_cursor_.hotspot.equals(cursor_shape->hotspot) && - last_cursor_.data == cursor_shape->data) { - return; - } - - // Record the last cursor image that we sent to the client. - last_cursor_ = *cursor_shape; - - mouse_shape_observer_->OnCursorShapeChanged(cursor_shape.release()); -} - void ScreenCapturerMac::GlBlitFast(const DesktopFrame& frame, const DesktopRegion& region) { // Clip to the size of our current screen. @@ -631,6 +664,9 @@ displays_to_capture = desktop_config_.displays; } + // Create the window list once for all displays. + CFArrayRef window_list = CreateWindowListWithExclusion(excluded_window_); + for (size_t i = 0; i < displays_to_capture.size(); ++i) { const MacDisplayConfiguration& display_config = displays_to_capture[i]; @@ -655,6 +691,26 @@ // Translate the region to be copied into display-relative coordinates. copy_region.Translate(-display_bounds.left(), -display_bounds.top()); + DesktopRect excluded_window_bounds; + CGImageRef excluded_image = NULL; + CFDataRef excluded_window_region_data = NULL; + if (excluded_window_ && window_list) { + // Get the region of the excluded window relative the primary display. + excluded_window_bounds = GetExcludedWindowPixelBounds( + excluded_window_, display_config.dip_to_pixel_scale); + excluded_window_bounds.IntersectWith(display_config.pixel_bounds); + + // Create the image under the excluded window first, because it's faster + // than captuing the whole display. + if (!excluded_window_bounds.is_empty()) { + excluded_image = CreateExcludedWindowRegionImage( + excluded_window_bounds, + display_config.dip_to_pixel_scale, + window_list, + &excluded_window_region_data); + } + } + // Create an image containing a snapshot of the display. CGImageRef image = CGDisplayCreateImage(display_config.id); if (image == NULL) @@ -684,9 +740,36 @@ i.rect()); } + // Copy the region of the excluded window to the frame. + if (excluded_image) { + assert(excluded_window_region_data); + display_base_address = CFDataGetBytePtr(excluded_window_region_data); + src_bytes_per_row = CGImageGetBytesPerRow(excluded_image); + + // Translate the bounds relative to the desktop, because |frame| data + // starts from the desktop top-left corner. + DesktopRect window_bounds_relative_to_desktop(excluded_window_bounds); + window_bounds_relative_to_desktop.Translate( + -screen_pixel_bounds_.left(), -screen_pixel_bounds_.top()); + out_ptr = frame.data() + + (window_bounds_relative_to_desktop.left() * src_bytes_per_pixel) + + (window_bounds_relative_to_desktop.top() * frame.stride()); + + CopyRect(display_base_address, + src_bytes_per_row, + out_ptr, + frame.stride(), + src_bytes_per_pixel, + DesktopRect::MakeSize(excluded_window_bounds.size())); + CFRelease(excluded_window_region_data); + CFRelease(excluded_image); + } + CFRelease(data); CFRelease(image); } + if (window_list) + CFRelease(window_list); return true; } @@ -717,7 +800,7 @@ // contents. Although the API exists in OS 10.6, it crashes the caller if // the machine has no monitor connected, so we fall back to depcreated APIs // when running on 10.6. - if (IsOSLionOrLater()) { + if (rtc::GetOSVersionName() >= rtc::kMacOSLion) { LOG(LS_INFO) << "Using CgBlitPostLion."; // No need for any OpenGL support on Lion return; @@ -765,10 +848,11 @@ LOG(LS_INFO) << "Using GlBlit"; CGLPixelFormatAttribute attributes[] = { - // This function does an early return if IsOSLionOrLater(), this code only - // runs on 10.6 and can be deleted once 10.6 support is dropped. So just - // keep using kCGLPFAFullScreen even though it was deprecated in 10.6 -- - // it's still functional there, and it's not used on newer OS X versions. + // This function does an early return if GetOSVersionName() >= kMacOSLion, + // this code only runs on 10.6 and can be deleted once 10.6 support is + // dropped. So just keep using kCGLPFAFullScreen even though it was + // deprecated in 10.6 -- it's still functional there, and it's not used on + // newer OS X versions. #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" kCGLPFAFullScreen, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_mock_objects.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_mock_objects.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_mock_objects.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_mock_objects.h 2015-02-03 14:33:36.000000000 +0000 @@ -12,7 +12,6 @@ #define WEBRTC_MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_MOCK_OBJECTS_H_ #include "testing/gmock/include/gmock/gmock.h" -#include "webrtc/modules/desktop_capture/mouse_cursor_shape.h" #include "webrtc/modules/desktop_capture/screen_capturer.h" namespace webrtc { @@ -24,8 +23,6 @@ MOCK_METHOD1(Start, void(Callback* callback)); MOCK_METHOD1(Capture, void(const DesktopRegion& region)); - MOCK_METHOD1(SetMouseShapeObserver, void( - MouseShapeObserver* mouse_shape_observer)); MOCK_METHOD1(GetScreenList, bool(ScreenList* screens)); MOCK_METHOD1(SelectScreen, bool(ScreenId id)); @@ -45,24 +42,6 @@ DISALLOW_COPY_AND_ASSIGN(MockScreenCapturerCallback); }; -class MockMouseShapeObserver : public ScreenCapturer::MouseShapeObserver { - public: - MockMouseShapeObserver() {} - virtual ~MockMouseShapeObserver() {} - - void OnCursorShapeChanged(MouseCursorShape* cursor_shape) OVERRIDE { - OnCursorShapeChangedPtr(cursor_shape); - delete cursor_shape; - } - - MOCK_METHOD1(OnCursorShapeChangedPtr, - void(MouseCursorShape* cursor_shape)); - - private: - DISALLOW_COPY_AND_ASSIGN(MockMouseShapeObserver); -}; - - } // namespace webrtc #endif // WEBRTC_MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_MOCK_OBJECTS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -37,7 +37,6 @@ protected: scoped_ptr capturer_; - MockMouseShapeObserver mouse_observer_; MockScreenCapturerCallback callback_; }; @@ -69,7 +68,6 @@ } TEST_F(ScreenCapturerTest, StartCapturer) { - capturer_->SetMouseShapeObserver(&mouse_observer_); capturer_->Start(&callback_); } @@ -78,8 +76,6 @@ DesktopFrame* frame = NULL; EXPECT_CALL(callback_, OnCaptureCompleted(_)) .WillOnce(SaveArg<0>(&frame)); - EXPECT_CALL(mouse_observer_, OnCursorShapeChangedPtr(_)) - .Times(AnyNumber()); EXPECT_CALL(callback_, CreateSharedMemory(_)) .Times(AnyNumber()) @@ -106,14 +102,12 @@ delete frame; } -#if defined(OS_WIN) +#if defined(WEBRTC_WIN) TEST_F(ScreenCapturerTest, UseSharedBuffers) { DesktopFrame* frame = NULL; EXPECT_CALL(callback_, OnCaptureCompleted(_)) .WillOnce(SaveArg<0>(&frame)); - EXPECT_CALL(mouse_observer_, OnCursorShapeChangedPtr(_)) - .Times(AnyNumber()); EXPECT_CALL(callback_, CreateSharedMemory(_)) .Times(AnyNumber()) @@ -129,6 +123,20 @@ delete frame; } -#endif // defined(OS_WIN) +TEST_F(ScreenCapturerTest, UseMagnifier) { + DesktopCaptureOptions options(DesktopCaptureOptions::CreateDefault()); + options.set_allow_use_magnification_api(true); + capturer_.reset(ScreenCapturer::Create(options)); + + DesktopFrame* frame = NULL; + EXPECT_CALL(callback_, OnCaptureCompleted(_)).WillOnce(SaveArg<0>(&frame)); + + capturer_->Start(&callback_); + capturer_->Capture(DesktopRegion()); + ASSERT_TRUE(frame); + delete frame; +} + +#endif // defined(WEBRTC_WIN) } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_win.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_win.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_win.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_win.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,481 +10,20 @@ #include "webrtc/modules/desktop_capture/screen_capturer.h" -#include - #include "webrtc/modules/desktop_capture/desktop_capture_options.h" -#include "webrtc/modules/desktop_capture/desktop_frame.h" -#include "webrtc/modules/desktop_capture/desktop_frame_win.h" -#include "webrtc/modules/desktop_capture/desktop_region.h" -#include "webrtc/modules/desktop_capture/differ.h" -#include "webrtc/modules/desktop_capture/mouse_cursor.h" -#include "webrtc/modules/desktop_capture/mouse_cursor_shape.h" -#include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h" -#include "webrtc/modules/desktop_capture/screen_capturer_helper.h" -#include "webrtc/modules/desktop_capture/win/cursor.h" -#include "webrtc/modules/desktop_capture/win/desktop.h" -#include "webrtc/modules/desktop_capture/win/scoped_thread_desktop.h" -#include "webrtc/system_wrappers/interface/logging.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/system_wrappers/interface/tick_util.h" +#include "webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h" +#include "webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h" namespace webrtc { -namespace { - -// Constants from dwmapi.h. -const UINT DWM_EC_DISABLECOMPOSITION = 0; -const UINT DWM_EC_ENABLECOMPOSITION = 1; - -typedef HRESULT (WINAPI * DwmEnableCompositionFunc)(UINT); -typedef HRESULT (WINAPI * DwmIsCompositionEnabledFunc)(BOOL*); - -const wchar_t kDwmapiLibraryName[] = L"dwmapi.dll"; - -// ScreenCapturerWin captures 32bit RGB using GDI. -// -// ScreenCapturerWin is double-buffered as required by ScreenCapturer. -class ScreenCapturerWin : public ScreenCapturer { - public: - ScreenCapturerWin(const DesktopCaptureOptions& options); - virtual ~ScreenCapturerWin(); - - // Overridden from ScreenCapturer: - virtual void Start(Callback* callback) OVERRIDE; - virtual void Capture(const DesktopRegion& region) OVERRIDE; - virtual void SetMouseShapeObserver( - MouseShapeObserver* mouse_shape_observer) OVERRIDE; - virtual bool GetScreenList(ScreenList* screens) OVERRIDE; - virtual bool SelectScreen(ScreenId id) OVERRIDE; - - private: - // Make sure that the device contexts match the screen configuration. - void PrepareCaptureResources(); - - // Captures the current screen contents into the current buffer. Returns true - // if succeeded. - bool CaptureImage(); - - // Capture the current cursor shape. - void CaptureCursor(); - - // Get the rect of the currently selected screen. If the screen is disabled - // or disconnected, or any error happens, an empty rect is returned. - DesktopRect GetScreenRect(); - - Callback* callback_; - MouseShapeObserver* mouse_shape_observer_; - ScreenId current_screen_id_; - std::wstring current_device_key_; - - // A thread-safe list of invalid rectangles, and the size of the most - // recently captured screen. - ScreenCapturerHelper helper_; - - // Snapshot of the last cursor bitmap we sent to the client. This is used - // to diff against the current cursor so we only send a cursor-change - // message when the shape has changed. - MouseCursorShape last_cursor_; - - ScopedThreadDesktop desktop_; - - // GDI resources used for screen capture. - HDC desktop_dc_; - HDC memory_dc_; - - // Queue of the frames buffers. - ScreenCaptureFrameQueue queue_; - - // Rectangle describing the bounds of the desktop device context. - DesktopRect desktop_dc_rect_; - - // Class to calculate the difference between two screen bitmaps. - scoped_ptr differ_; - - HMODULE dwmapi_library_; - DwmEnableCompositionFunc composition_func_; - DwmIsCompositionEnabledFunc composition_enabled_func_; - - bool disable_composition_; - - // Used to suppress duplicate logging of SetThreadExecutionState errors. - bool set_thread_execution_state_failed_; - - DISALLOW_COPY_AND_ASSIGN(ScreenCapturerWin); -}; - -ScreenCapturerWin::ScreenCapturerWin(const DesktopCaptureOptions& options) - : callback_(NULL), - mouse_shape_observer_(NULL), - current_screen_id_(kFullDesktopScreenId), - desktop_dc_(NULL), - memory_dc_(NULL), - dwmapi_library_(NULL), - composition_func_(NULL), - set_thread_execution_state_failed_(false) { - // Load dwmapi.dll dynamically since it is not available on XP. - if (!dwmapi_library_) - dwmapi_library_ = LoadLibrary(kDwmapiLibraryName); - - if (dwmapi_library_) { - composition_func_ = reinterpret_cast( - GetProcAddress(dwmapi_library_, "DwmEnableComposition")); - composition_enabled_func_ = reinterpret_cast - (GetProcAddress(dwmapi_library_, "DwmIsCompositionEnabled")); - } - - disable_composition_ = options.disable_effects(); -} - -ScreenCapturerWin::~ScreenCapturerWin() { - if (desktop_dc_) - ReleaseDC(NULL, desktop_dc_); - if (memory_dc_) - DeleteDC(memory_dc_); - - if (disable_composition_) { - // Restore Aero. - if (composition_func_) - (*composition_func_)(DWM_EC_ENABLECOMPOSITION); - } - - if (dwmapi_library_) - FreeLibrary(dwmapi_library_); -} - -void ScreenCapturerWin::Capture(const DesktopRegion& region) { - assert(IsGUIThread(false)); - TickTime capture_start_time = TickTime::Now(); - - queue_.MoveToNextFrame(); - - // Request that the system not power-down the system, or the display hardware. - if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) { - if (!set_thread_execution_state_failed_) { - set_thread_execution_state_failed_ = true; - LOG_F(LS_WARNING) << "Failed to make system & display power assertion: " - << GetLastError(); - } - } - - // Make sure the GDI capture resources are up-to-date. - PrepareCaptureResources(); - - // Copy screen bits to the current buffer. - if (!CaptureImage()) { - callback_->OnCaptureCompleted(NULL); - return; - } - - const DesktopFrame* current_frame = queue_.current_frame(); - const DesktopFrame* last_frame = queue_.previous_frame(); - if (last_frame && last_frame->size().equals(current_frame->size())) { - // Make sure the differencer is set up correctly for these previous and - // current screens. - if (!differ_.get() || - (differ_->width() != current_frame->size().width()) || - (differ_->height() != current_frame->size().height()) || - (differ_->bytes_per_row() != current_frame->stride())) { - differ_.reset(new Differ(current_frame->size().width(), - current_frame->size().height(), - DesktopFrame::kBytesPerPixel, - current_frame->stride())); - } - - // Calculate difference between the two last captured frames. - DesktopRegion region; - differ_->CalcDirtyRegion(last_frame->data(), current_frame->data(), - ®ion); - helper_.InvalidateRegion(region); - } else { - // No previous frame is available, or the screen is resized. Invalidate the - // whole screen. - helper_.InvalidateScreen(current_frame->size()); - } - - helper_.set_size_most_recent(current_frame->size()); - - // Emit the current frame. - DesktopFrame* frame = queue_.current_frame()->Share(); - frame->set_dpi(DesktopVector( - GetDeviceCaps(desktop_dc_, LOGPIXELSX), - GetDeviceCaps(desktop_dc_, LOGPIXELSY))); - frame->mutable_updated_region()->Clear(); - helper_.TakeInvalidRegion(frame->mutable_updated_region()); - frame->set_capture_time_ms( - (TickTime::Now() - capture_start_time).Milliseconds()); - callback_->OnCaptureCompleted(frame); - - // Check for cursor shape update. - CaptureCursor(); -} - -void ScreenCapturerWin::SetMouseShapeObserver( - MouseShapeObserver* mouse_shape_observer) { - assert(!mouse_shape_observer_); - assert(mouse_shape_observer); - - mouse_shape_observer_ = mouse_shape_observer; -} - -bool ScreenCapturerWin::GetScreenList(ScreenList* screens) { - assert(IsGUIThread(false)); - assert(screens->size() == 0); - BOOL enum_result = TRUE; - for (int device_index = 0; ; ++device_index) { - DISPLAY_DEVICE device; - device.cb = sizeof(device); - enum_result = EnumDisplayDevices(NULL, device_index, &device, 0); - // |enum_result| is 0 if we have enumerated all devices. - if (!enum_result) - break; - - // We only care about active displays. - if (!(device.StateFlags & DISPLAY_DEVICE_ACTIVE)) - continue; - Screen screen; - screen.id = device_index; - screens->push_back(screen); - } - return true; -} - -bool ScreenCapturerWin::SelectScreen(ScreenId id) { - assert(IsGUIThread(false)); - if (id == kFullDesktopScreenId) { - current_screen_id_ = id; - return true; - } - DISPLAY_DEVICE device; - device.cb = sizeof(device); - BOOL enum_result = EnumDisplayDevices(NULL, id, &device, 0); - if (!enum_result) - return false; - - current_device_key_ = device.DeviceKey; - current_screen_id_ = id; - return true; -} - -void ScreenCapturerWin::Start(Callback* callback) { - assert(!callback_); - assert(callback); - - callback_ = callback; - - if (disable_composition_) { - // Vote to disable Aero composited desktop effects while capturing. Windows - // will restore Aero automatically if the process exits. This has no effect - // under Windows 8 or higher. See crbug.com/124018. - if (composition_func_) - (*composition_func_)(DWM_EC_DISABLECOMPOSITION); - } -} - -void ScreenCapturerWin::PrepareCaptureResources() { - assert(IsGUIThread(false)); - // Switch to the desktop receiving user input if different from the current - // one. - scoped_ptr input_desktop(Desktop::GetInputDesktop()); - if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) { - // Release GDI resources otherwise SetThreadDesktop will fail. - if (desktop_dc_) { - ReleaseDC(NULL, desktop_dc_); - desktop_dc_ = NULL; - } - - if (memory_dc_) { - DeleteDC(memory_dc_); - memory_dc_ = NULL; - } - - // If SetThreadDesktop() fails, the thread is still assigned a desktop. - // So we can continue capture screen bits, just from the wrong desktop. - desktop_.SetThreadDesktop(input_desktop.release()); - - if (disable_composition_) { - // Re-assert our vote to disable Aero. - // See crbug.com/124018 and crbug.com/129906. - if (composition_func_ != NULL) { - (*composition_func_)(DWM_EC_DISABLECOMPOSITION); - } - } - } - - // If the display bounds have changed then recreate GDI resources. - // TODO(wez): Also check for pixel format changes. - DesktopRect screen_rect(DesktopRect::MakeXYWH( - GetSystemMetrics(SM_XVIRTUALSCREEN), - GetSystemMetrics(SM_YVIRTUALSCREEN), - GetSystemMetrics(SM_CXVIRTUALSCREEN), - GetSystemMetrics(SM_CYVIRTUALSCREEN))); - if (!screen_rect.equals(desktop_dc_rect_)) { - if (desktop_dc_) { - ReleaseDC(NULL, desktop_dc_); - desktop_dc_ = NULL; - } - if (memory_dc_) { - DeleteDC(memory_dc_); - memory_dc_ = NULL; - } - desktop_dc_rect_ = DesktopRect(); - } - - if (desktop_dc_ == NULL) { - assert(memory_dc_ == NULL); - - // Create GDI device contexts to capture from the desktop into memory. - desktop_dc_ = GetDC(NULL); - if (!desktop_dc_) - abort(); - memory_dc_ = CreateCompatibleDC(desktop_dc_); - if (!memory_dc_) - abort(); - desktop_dc_rect_ = screen_rect; - - // Make sure the frame buffers will be reallocated. - queue_.Reset(); - - helper_.ClearInvalidRegion(); - } -} - -bool ScreenCapturerWin::CaptureImage() { - assert(IsGUIThread(false)); - DesktopRect screen_rect = GetScreenRect(); - if (screen_rect.is_empty()) - return false; - DesktopSize size = screen_rect.size(); - // If the current buffer is from an older generation then allocate a new one. - // Note that we can't reallocate other buffers at this point, since the caller - // may still be reading from them. - if (!queue_.current_frame() || - !queue_.current_frame()->size().equals(size)) { - assert(desktop_dc_ != NULL); - assert(memory_dc_ != NULL); - - size_t buffer_size = size.width() * size.height() * - DesktopFrame::kBytesPerPixel; - SharedMemory* shared_memory = - callback_->CreateSharedMemory(buffer_size); - scoped_ptr buffer( - DesktopFrameWin::Create(size, shared_memory, desktop_dc_)); - queue_.ReplaceCurrentFrame(buffer.release()); - } - - // Select the target bitmap into the memory dc and copy the rect from desktop - // to memory. - DesktopFrameWin* current = static_cast( - queue_.current_frame()->GetUnderlyingFrame()); - HGDIOBJ previous_object = SelectObject(memory_dc_, current->bitmap()); - DWORD rop = SRCCOPY; - if (composition_enabled_func_) { - BOOL enabled; - (*composition_enabled_func_)(&enabled); - if (!enabled) { - // Vista or Windows 7, Aero disabled - rop |= CAPTUREBLT; - } - } else { - // Windows XP, required to get layered windows - rop |= CAPTUREBLT; - } - if (previous_object != NULL) { - BitBlt(memory_dc_, - 0, 0, screen_rect.width(), screen_rect.height(), - desktop_dc_, - screen_rect.left(), screen_rect.top(), - rop); - - // Select back the previously selected object to that the device contect - // could be destroyed independently of the bitmap if needed. - SelectObject(memory_dc_, previous_object); - } - return true; -} - -void ScreenCapturerWin::CaptureCursor() { - assert(IsGUIThread(false)); - CURSORINFO cursor_info; - cursor_info.cbSize = sizeof(CURSORINFO); - if (!GetCursorInfo(&cursor_info)) { - LOG_F(LS_ERROR) << "Unable to get cursor info. Error = " << GetLastError(); - return; - } - - // Note that |cursor_info.hCursor| does not need to be freed. - scoped_ptr cursor_image( - CreateMouseCursorFromHCursor(desktop_dc_, cursor_info.hCursor)); - if (!cursor_image.get()) - return; - - scoped_ptr cursor(new MouseCursorShape); - cursor->hotspot = cursor_image->hotspot(); - cursor->size = cursor_image->image()->size(); - uint8_t* current_row = cursor_image->image()->data(); - for (int y = 0; y < cursor_image->image()->size().height(); ++y) { - cursor->data.append(current_row, - current_row + cursor_image->image()->size().width() * - DesktopFrame::kBytesPerPixel); - current_row += cursor_image->image()->stride(); - } - - // Compare the current cursor with the last one we sent to the client. If - // they're the same, then don't bother sending the cursor again. - if (last_cursor_.size.equals(cursor->size) && - last_cursor_.hotspot.equals(cursor->hotspot) && - last_cursor_.data == cursor->data) { - return; - } - - LOG(LS_VERBOSE) << "Sending updated cursor: " << cursor->size.width() << "x" - << cursor->size.height(); - - // Record the last cursor image that we sent to the client. - last_cursor_ = *cursor; - - if (mouse_shape_observer_) - mouse_shape_observer_->OnCursorShapeChanged(cursor.release()); -} - -DesktopRect ScreenCapturerWin::GetScreenRect() { - assert(IsGUIThread(false)); - DesktopRect rect = desktop_dc_rect_; - if (current_screen_id_ == kFullDesktopScreenId) - return rect; - - DISPLAY_DEVICE device; - device.cb = sizeof(device); - BOOL result = EnumDisplayDevices(NULL, current_screen_id_, &device, 0); - if (!result) - return DesktopRect(); - - // Verifies the device index still maps to the same display device. DeviceKey - // is documented as reserved, but it actually contains the registry key for - // the device and is unique for each monitor, while DeviceID is not. - if (current_device_key_ != device.DeviceKey) - return DesktopRect(); - - DEVMODE device_mode; - device_mode.dmSize = sizeof(device_mode); - device_mode.dmDriverExtra = 0; - result = EnumDisplaySettingsEx( - device.DeviceName, ENUM_CURRENT_SETTINGS, &device_mode, 0); - if (!result) - return DesktopRect(); - - rect = DesktopRect::MakeXYWH( - rect.left() + device_mode.dmPosition.x, - rect.top() + device_mode.dmPosition.y, - device_mode.dmPelsWidth, - device_mode.dmPelsHeight); - return rect; -} -} // namespace - // static ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) { - return new ScreenCapturerWin(options); + scoped_ptr gdi_capturer(new ScreenCapturerWinGdi(options)); + + if (options.allow_use_magnification_api()) + return new ScreenCapturerWinMagnifier(gdi_capturer.Pass()); + + return gdi_capturer.release(); } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc 2015-02-03 14:33:36.000000000 +0000 @@ -21,7 +21,6 @@ #include "webrtc/modules/desktop_capture/desktop_capture_options.h" #include "webrtc/modules/desktop_capture/desktop_frame.h" #include "webrtc/modules/desktop_capture/differ.h" -#include "webrtc/modules/desktop_capture/mouse_cursor_shape.h" #include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h" #include "webrtc/modules/desktop_capture/screen_capturer_helper.h" #include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h" @@ -55,8 +54,6 @@ virtual void Capture(const DesktopRegion& region) OVERRIDE; // ScreenCapturer interface. - virtual void SetMouseShapeObserver( - MouseShapeObserver* mouse_shape_observer) OVERRIDE; virtual bool GetScreenList(ScreenList* screens) OVERRIDE; virtual bool SelectScreen(ScreenId id) OVERRIDE; @@ -68,9 +65,6 @@ void InitXDamage(); - // Capture the cursor image and notify the delegate if it was captured. - void CaptureCursor(); - // Capture screen pixels to the current buffer in the queue. In the DAMAGE // case, the ScreenCapturerHelper already holds the list of invalid rectangles // from HandleXEvent(). In the non-DAMAGE case, this captures the @@ -93,7 +87,6 @@ DesktopCaptureOptions options_; Callback* callback_; - MouseShapeObserver* mouse_shape_observer_; // X11 graphics context. GC gc_; @@ -133,7 +126,6 @@ ScreenCapturerLinux::ScreenCapturerLinux() : callback_(NULL), - mouse_shape_observer_(NULL), gc_(NULL), root_window_(BadValue), has_xfixes_(false), @@ -153,10 +145,6 @@ options_.x_display()->RemoveEventHandler( damage_event_base_ + XDamageNotify, this); } - if (has_xfixes_) { - options_.x_display()->RemoveEventHandler( - xfixes_event_base_ + XFixesCursorNotify, this); - } DeinitXlib(); } @@ -196,14 +184,6 @@ return false; } - if (has_xfixes_) { - // Register for changes to the cursor shape. - XFixesSelectCursorInput(display(), root_window_, - XFixesDisplayCursorNotifyMask); - options_.x_display()->AddEventHandler( - xfixes_event_base_ + XFixesCursorNotify, this); - } - if (options_.use_update_notifications()) { InitXDamage(); } @@ -304,14 +284,6 @@ callback_->OnCaptureCompleted(result); } -void ScreenCapturerLinux::SetMouseShapeObserver( - MouseShapeObserver* mouse_shape_observer) { - DCHECK(!mouse_shape_observer_); - DCHECK(mouse_shape_observer); - - mouse_shape_observer_ = mouse_shape_observer; -} - bool ScreenCapturerLinux::GetScreenList(ScreenList* screens) { DCHECK(screens->size() == 0); // TODO(jiayl): implement screen enumeration. @@ -337,50 +309,10 @@ } else if (event.type == ConfigureNotify) { ScreenConfigurationChanged(); return true; - } else if (has_xfixes_ && - event.type == xfixes_event_base_ + XFixesCursorNotify) { - const XFixesCursorNotifyEvent* cursor_event = - reinterpret_cast(&event); - if (cursor_event->window == root_window_ && - cursor_event->subtype == XFixesDisplayCursorNotify) { - CaptureCursor(); - } - // Always return false for cursor notifications, because there might be - // other listeners for these for the same window. - return false; } return false; } -void ScreenCapturerLinux::CaptureCursor() { - DCHECK(has_xfixes_); - - XFixesCursorImage* img = XFixesGetCursorImage(display()); - if (!img) { - return; - } - - scoped_ptr cursor(new MouseCursorShape()); - cursor->size = DesktopSize(img->width, img->height); - cursor->hotspot = DesktopVector(img->xhot, img->yhot); - - int total_bytes = cursor->size.width ()* cursor->size.height() * - DesktopFrame::kBytesPerPixel; - cursor->data.resize(total_bytes); - - // Xlib stores 32-bit data in longs, even if longs are 64-bits long. - unsigned long* src = img->pixels; - uint32_t* dst = reinterpret_cast(&*(cursor->data.begin())); - uint32_t* dst_end = dst + (img->width * img->height); - while (dst < dst_end) { - *dst++ = static_cast(*src++); - } - XFree(img); - - if (mouse_shape_observer_) - mouse_shape_observer_->OnCursorShapeChanged(cursor.release()); -} - DesktopFrame* ScreenCapturerLinux::CaptureScreen() { DesktopFrame* frame = queue_.current_frame()->Share(); assert(x_server_pixel_buffer_.window_size().equals(frame->size())); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/shared_memory.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/shared_memory.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/shared_memory.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/shared_memory.h 2015-02-03 14:33:36.000000000 +0000 @@ -17,8 +17,8 @@ #include #endif +#include "webrtc/base/constructormagic.h" #include "webrtc/typedefs.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/cursor.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/cursor.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/cursor.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/cursor.cc 2015-02-03 14:33:36.000000000 +0000 @@ -137,7 +137,7 @@ int width = bitmap_info.bmWidth; int height = bitmap_info.bmHeight; - scoped_array mask_data(new uint32_t[width * height]); + scoped_ptr mask_data(new uint32_t[width * height]); // Get pixel data from |scoped_mask| converting it to 32bpp along the way. // GetDIBits() sets the alpha component of every pixel to 0. @@ -197,7 +197,7 @@ // The XOR mask becomes the color bitmap. memcpy( - image->data(), mask_plane + (width * height), image->stride() * width); + image->data(), mask_plane + (width * height), image->stride() * height); } // Reconstruct transparency from the mask if the color image does not has diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/cursor_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/cursor_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/cursor_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/cursor_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -62,7 +62,7 @@ // Get the pixels from |scoped_color|. int size = width * height; - scoped_array data(new uint32_t[size]); + scoped_ptr data(new uint32_t[size]); EXPECT_TRUE(GetBitmapBits(scoped_color, size * sizeof(uint32_t), data.get())); // Compare the 32bpp image in |mouse_shape| with the one loaded from |right|. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/desktop.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/desktop.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/desktop.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/desktop.h 2015-02-03 14:33:36.000000000 +0000 @@ -11,10 +11,10 @@ #ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DESKTOP_H_ #define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DESKTOP_H_ -#include #include +#include -#include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/base/constructormagic.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/scoped_gdi_object.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/scoped_gdi_object.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/scoped_gdi_object.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/scoped_gdi_object.h 2015-02-03 14:33:36.000000000 +0000 @@ -13,7 +13,7 @@ #include -#include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/base/constructormagic.h" #include "webrtc/typedefs.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/scoped_thread_desktop.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/scoped_thread_desktop.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/scoped_thread_desktop.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/scoped_thread_desktop.h 2015-02-03 14:33:36.000000000 +0000 @@ -13,7 +13,7 @@ #include -#include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/base/constructormagic.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h" + +#include + +#include "webrtc/modules/desktop_capture/desktop_capture_options.h" +#include "webrtc/modules/desktop_capture/desktop_frame.h" +#include "webrtc/modules/desktop_capture/desktop_frame_win.h" +#include "webrtc/modules/desktop_capture/desktop_region.h" +#include "webrtc/modules/desktop_capture/differ.h" +#include "webrtc/modules/desktop_capture/mouse_cursor.h" +#include "webrtc/modules/desktop_capture/win/cursor.h" +#include "webrtc/modules/desktop_capture/win/desktop.h" +#include "webrtc/modules/desktop_capture/win/screen_capture_utils.h" +#include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/system_wrappers/interface/tick_util.h" + +namespace webrtc { + +namespace { + +// Constants from dwmapi.h. +const UINT DWM_EC_DISABLECOMPOSITION = 0; +const UINT DWM_EC_ENABLECOMPOSITION = 1; + +const wchar_t kDwmapiLibraryName[] = L"dwmapi.dll"; + +} // namespace + +ScreenCapturerWinGdi::ScreenCapturerWinGdi(const DesktopCaptureOptions& options) + : callback_(NULL), + current_screen_id_(kFullDesktopScreenId), + desktop_dc_(NULL), + memory_dc_(NULL), + dwmapi_library_(NULL), + composition_func_(NULL), + set_thread_execution_state_failed_(false) { + // Load dwmapi.dll dynamically since it is not available on XP. + if (!dwmapi_library_) + dwmapi_library_ = LoadLibrary(kDwmapiLibraryName); + + if (dwmapi_library_) { + composition_func_ = reinterpret_cast( + GetProcAddress(dwmapi_library_, "DwmEnableComposition")); + composition_enabled_func_ = reinterpret_cast + (GetProcAddress(dwmapi_library_, "DwmIsCompositionEnabled")); + } + + disable_composition_ = options.disable_effects(); +} + +ScreenCapturerWinGdi::~ScreenCapturerWinGdi() { + if (desktop_dc_) + ReleaseDC(NULL, desktop_dc_); + if (memory_dc_) + DeleteDC(memory_dc_); + + if (disable_composition_) { + // Restore Aero. + if (composition_func_) + (*composition_func_)(DWM_EC_ENABLECOMPOSITION); + } + + if (dwmapi_library_) + FreeLibrary(dwmapi_library_); +} + +void ScreenCapturerWinGdi::Capture(const DesktopRegion& region) { + assert(IsGUIThread(false)); + TickTime capture_start_time = TickTime::Now(); + + queue_.MoveToNextFrame(); + + // Request that the system not power-down the system, or the display hardware. + if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) { + if (!set_thread_execution_state_failed_) { + set_thread_execution_state_failed_ = true; + LOG_F(LS_WARNING) << "Failed to make system & display power assertion: " + << GetLastError(); + } + } + + // Make sure the GDI capture resources are up-to-date. + PrepareCaptureResources(); + + if (!CaptureImage()) { + callback_->OnCaptureCompleted(NULL); + return; + } + + const DesktopFrame* current_frame = queue_.current_frame(); + const DesktopFrame* last_frame = queue_.previous_frame(); + if (last_frame && last_frame->size().equals(current_frame->size())) { + // Make sure the differencer is set up correctly for these previous and + // current screens. + if (!differ_.get() || + (differ_->width() != current_frame->size().width()) || + (differ_->height() != current_frame->size().height()) || + (differ_->bytes_per_row() != current_frame->stride())) { + differ_.reset(new Differ(current_frame->size().width(), + current_frame->size().height(), + DesktopFrame::kBytesPerPixel, + current_frame->stride())); + } + + // Calculate difference between the two last captured frames. + DesktopRegion region; + differ_->CalcDirtyRegion(last_frame->data(), current_frame->data(), + ®ion); + helper_.InvalidateRegion(region); + } else { + // No previous frame is available, or the screen is resized. Invalidate the + // whole screen. + helper_.InvalidateScreen(current_frame->size()); + } + + helper_.set_size_most_recent(current_frame->size()); + + // Emit the current frame. + DesktopFrame* frame = queue_.current_frame()->Share(); + frame->set_dpi(DesktopVector( + GetDeviceCaps(desktop_dc_, LOGPIXELSX), + GetDeviceCaps(desktop_dc_, LOGPIXELSY))); + frame->mutable_updated_region()->Clear(); + helper_.TakeInvalidRegion(frame->mutable_updated_region()); + frame->set_capture_time_ms( + (TickTime::Now() - capture_start_time).Milliseconds()); + callback_->OnCaptureCompleted(frame); +} + +bool ScreenCapturerWinGdi::GetScreenList(ScreenList* screens) { + assert(IsGUIThread(false)); + return webrtc::GetScreenList(screens); +} + +bool ScreenCapturerWinGdi::SelectScreen(ScreenId id) { + assert(IsGUIThread(false)); + bool valid = IsScreenValid(id, ¤t_device_key_); + if (valid) + current_screen_id_ = id; + return valid; +} + +void ScreenCapturerWinGdi::Start(Callback* callback) { + assert(!callback_); + assert(callback); + + callback_ = callback; + + if (disable_composition_) { + // Vote to disable Aero composited desktop effects while capturing. Windows + // will restore Aero automatically if the process exits. This has no effect + // under Windows 8 or higher. See crbug.com/124018. + if (composition_func_) + (*composition_func_)(DWM_EC_DISABLECOMPOSITION); + } +} + +void ScreenCapturerWinGdi::PrepareCaptureResources() { + assert(IsGUIThread(false)); + // Switch to the desktop receiving user input if different from the current + // one. + scoped_ptr input_desktop(Desktop::GetInputDesktop()); + if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) { + // Release GDI resources otherwise SetThreadDesktop will fail. + if (desktop_dc_) { + ReleaseDC(NULL, desktop_dc_); + desktop_dc_ = NULL; + } + + if (memory_dc_) { + DeleteDC(memory_dc_); + memory_dc_ = NULL; + } + + // If SetThreadDesktop() fails, the thread is still assigned a desktop. + // So we can continue capture screen bits, just from the wrong desktop. + desktop_.SetThreadDesktop(input_desktop.release()); + + if (disable_composition_) { + // Re-assert our vote to disable Aero. + // See crbug.com/124018 and crbug.com/129906. + if (composition_func_ != NULL) { + (*composition_func_)(DWM_EC_DISABLECOMPOSITION); + } + } + } + + // If the display bounds have changed then recreate GDI resources. + // TODO(wez): Also check for pixel format changes. + DesktopRect screen_rect(DesktopRect::MakeXYWH( + GetSystemMetrics(SM_XVIRTUALSCREEN), + GetSystemMetrics(SM_YVIRTUALSCREEN), + GetSystemMetrics(SM_CXVIRTUALSCREEN), + GetSystemMetrics(SM_CYVIRTUALSCREEN))); + if (!screen_rect.equals(desktop_dc_rect_)) { + if (desktop_dc_) { + ReleaseDC(NULL, desktop_dc_); + desktop_dc_ = NULL; + } + if (memory_dc_) { + DeleteDC(memory_dc_); + memory_dc_ = NULL; + } + desktop_dc_rect_ = DesktopRect(); + } + + if (desktop_dc_ == NULL) { + assert(memory_dc_ == NULL); + + // Create GDI device contexts to capture from the desktop into memory. + desktop_dc_ = GetDC(NULL); + if (!desktop_dc_) + abort(); + memory_dc_ = CreateCompatibleDC(desktop_dc_); + if (!memory_dc_) + abort(); + + desktop_dc_rect_ = screen_rect; + + // Make sure the frame buffers will be reallocated. + queue_.Reset(); + + helper_.ClearInvalidRegion(); + } +} + +bool ScreenCapturerWinGdi::CaptureImage() { + assert(IsGUIThread(false)); + DesktopRect screen_rect = + GetScreenRect(current_screen_id_, current_device_key_); + if (screen_rect.is_empty()) + return false; + + DesktopSize size = screen_rect.size(); + // If the current buffer is from an older generation then allocate a new one. + // Note that we can't reallocate other buffers at this point, since the caller + // may still be reading from them. + if (!queue_.current_frame() || + !queue_.current_frame()->size().equals(screen_rect.size())) { + assert(desktop_dc_ != NULL); + assert(memory_dc_ != NULL); + + size_t buffer_size = size.width() * size.height() * + DesktopFrame::kBytesPerPixel; + SharedMemory* shared_memory = callback_->CreateSharedMemory(buffer_size); + + scoped_ptr buffer; + buffer.reset( + DesktopFrameWin::Create(size, shared_memory, desktop_dc_)); + queue_.ReplaceCurrentFrame(buffer.release()); + } + + // Select the target bitmap into the memory dc and copy the rect from desktop + // to memory. + DesktopFrameWin* current = static_cast( + queue_.current_frame()->GetUnderlyingFrame()); + HGDIOBJ previous_object = SelectObject(memory_dc_, current->bitmap()); + DWORD rop = SRCCOPY; + if (composition_enabled_func_) { + BOOL enabled; + (*composition_enabled_func_)(&enabled); + if (!enabled) { + // Vista or Windows 7, Aero disabled + rop |= CAPTUREBLT; + } + } else { + // Windows XP, required to get layered windows + rop |= CAPTUREBLT; + } + if (previous_object != NULL) { + BitBlt(memory_dc_, + 0, 0, screen_rect.width(), screen_rect.height(), + desktop_dc_, + screen_rect.left(), screen_rect.top(), + rop); + + // Select back the previously selected object to that the device contect + // could be destroyed independently of the bitmap if needed. + SelectObject(memory_dc_, previous_object); + } + return true; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_GDI_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_GDI_H_ + +#include "webrtc/modules/desktop_capture/screen_capturer.h" + +#include + +#include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h" +#include "webrtc/modules/desktop_capture/screen_capturer_helper.h" +#include "webrtc/modules/desktop_capture/win/scoped_thread_desktop.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class Differ; + +// ScreenCapturerWinGdi captures 32bit RGB using GDI. +// +// ScreenCapturerWinGdi is double-buffered as required by ScreenCapturer. +class ScreenCapturerWinGdi : public ScreenCapturer { + public: + explicit ScreenCapturerWinGdi(const DesktopCaptureOptions& options); + virtual ~ScreenCapturerWinGdi(); + + // Overridden from ScreenCapturer: + virtual void Start(Callback* callback) OVERRIDE; + virtual void Capture(const DesktopRegion& region) OVERRIDE; + virtual bool GetScreenList(ScreenList* screens) OVERRIDE; + virtual bool SelectScreen(ScreenId id) OVERRIDE; + + private: + typedef HRESULT (WINAPI * DwmEnableCompositionFunc)(UINT); + typedef HRESULT (WINAPI * DwmIsCompositionEnabledFunc)(BOOL*); + + // Make sure that the device contexts match the screen configuration. + void PrepareCaptureResources(); + + // Captures the current screen contents into the current buffer. Returns true + // if succeeded. + bool CaptureImage(); + + // Capture the current cursor shape. + void CaptureCursor(); + + Callback* callback_; + ScreenId current_screen_id_; + std::wstring current_device_key_; + + // A thread-safe list of invalid rectangles, and the size of the most + // recently captured screen. + ScreenCapturerHelper helper_; + + ScopedThreadDesktop desktop_; + + // GDI resources used for screen capture. + HDC desktop_dc_; + HDC memory_dc_; + + // Queue of the frames buffers. + ScreenCaptureFrameQueue queue_; + + // Rectangle describing the bounds of the desktop device context, relative to + // the primary display's top-left. + DesktopRect desktop_dc_rect_; + + // Class to calculate the difference between two screen bitmaps. + scoped_ptr differ_; + + HMODULE dwmapi_library_; + DwmEnableCompositionFunc composition_func_; + DwmIsCompositionEnabledFunc composition_enabled_func_; + + bool disable_composition_; + + // Used to suppress duplicate logging of SetThreadExecutionState errors. + bool set_thread_execution_state_failed_; + + DISALLOW_COPY_AND_ASSIGN(ScreenCapturerWinGdi); +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_GDI_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h" + +#include + +#include "webrtc/modules/desktop_capture/desktop_capture_options.h" +#include "webrtc/modules/desktop_capture/desktop_frame.h" +#include "webrtc/modules/desktop_capture/desktop_frame_win.h" +#include "webrtc/modules/desktop_capture/desktop_region.h" +#include "webrtc/modules/desktop_capture/differ.h" +#include "webrtc/modules/desktop_capture/mouse_cursor.h" +#include "webrtc/modules/desktop_capture/win/cursor.h" +#include "webrtc/modules/desktop_capture/win/desktop.h" +#include "webrtc/modules/desktop_capture/win/screen_capture_utils.h" +#include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/system_wrappers/interface/tick_util.h" + +namespace webrtc { + +// kMagnifierWindowClass has to be "Magnifier" according to the Magnification +// API. The other strings can be anything. +static LPCTSTR kMagnifierHostClass = L"ScreenCapturerWinMagnifierHost"; +static LPCTSTR kHostWindowName = L"MagnifierHost"; +static LPCTSTR kMagnifierWindowClass = L"Magnifier"; +static LPCTSTR kMagnifierWindowName = L"MagnifierWindow"; + +Atomic32 ScreenCapturerWinMagnifier::tls_index_(TLS_OUT_OF_INDEXES); + +ScreenCapturerWinMagnifier::ScreenCapturerWinMagnifier( + scoped_ptr fallback_capturer) + : fallback_capturer_(fallback_capturer.Pass()), + fallback_capturer_started_(false), + callback_(NULL), + current_screen_id_(kFullDesktopScreenId), + excluded_window_(NULL), + set_thread_execution_state_failed_(false), + desktop_dc_(NULL), + mag_lib_handle_(NULL), + mag_initialize_func_(NULL), + mag_uninitialize_func_(NULL), + set_window_source_func_(NULL), + set_window_filter_list_func_(NULL), + set_image_scaling_callback_func_(NULL), + host_window_(NULL), + magnifier_window_(NULL), + magnifier_initialized_(false), + magnifier_capture_succeeded_(true) { +} + +ScreenCapturerWinMagnifier::~ScreenCapturerWinMagnifier() { + // DestroyWindow must be called before MagUninitialize. magnifier_window_ is + // destroyed automatically when host_window_ is destroyed. + if (host_window_) + DestroyWindow(host_window_); + + if (magnifier_initialized_) + mag_uninitialize_func_(); + + if (mag_lib_handle_) + FreeLibrary(mag_lib_handle_); + + if (desktop_dc_) + ReleaseDC(NULL, desktop_dc_); +} + +void ScreenCapturerWinMagnifier::Start(Callback* callback) { + assert(!callback_); + assert(callback); + callback_ = callback; + + InitializeMagnifier(); +} + +void ScreenCapturerWinMagnifier::Capture(const DesktopRegion& region) { + TickTime capture_start_time = TickTime::Now(); + + queue_.MoveToNextFrame(); + + // Request that the system not power-down the system, or the display hardware. + if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) { + if (!set_thread_execution_state_failed_) { + set_thread_execution_state_failed_ = true; + LOG_F(LS_WARNING) << "Failed to make system & display power assertion: " + << GetLastError(); + } + } + // Switch to the desktop receiving user input if different from the current + // one. + scoped_ptr input_desktop(Desktop::GetInputDesktop()); + if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) { + // Release GDI resources otherwise SetThreadDesktop will fail. + if (desktop_dc_) { + ReleaseDC(NULL, desktop_dc_); + desktop_dc_ = NULL; + } + // If SetThreadDesktop() fails, the thread is still assigned a desktop. + // So we can continue capture screen bits, just from the wrong desktop. + desktop_.SetThreadDesktop(input_desktop.release()); + } + + bool succeeded = false; + + // Do not try to use the magnfiier if it's capturing non-primary screen, or it + // failed before. + if (magnifier_initialized_ && IsCapturingPrimaryScreenOnly() && + magnifier_capture_succeeded_) { + DesktopRect rect = GetScreenRect(current_screen_id_, current_device_key_); + CreateCurrentFrameIfNecessary(rect.size()); + + // CaptureImage may fail in some situations, e.g. windows8 metro mode. + succeeded = CaptureImage(rect); + } + + // Defer to the fallback capturer if magnifier capturer did not work. + if (!succeeded) { + LOG_F(LS_WARNING) << "Switching to the fallback screen capturer."; + StartFallbackCapturer(); + fallback_capturer_->Capture(region); + return; + } + + const DesktopFrame* current_frame = queue_.current_frame(); + const DesktopFrame* last_frame = queue_.previous_frame(); + if (last_frame && last_frame->size().equals(current_frame->size())) { + // Make sure the differencer is set up correctly for these previous and + // current screens. + if (!differ_.get() || (differ_->width() != current_frame->size().width()) || + (differ_->height() != current_frame->size().height()) || + (differ_->bytes_per_row() != current_frame->stride())) { + differ_.reset(new Differ(current_frame->size().width(), + current_frame->size().height(), + DesktopFrame::kBytesPerPixel, + current_frame->stride())); + } + + // Calculate difference between the two last captured frames. + DesktopRegion region; + differ_->CalcDirtyRegion( + last_frame->data(), current_frame->data(), ®ion); + helper_.InvalidateRegion(region); + } else { + // No previous frame is available, or the screen is resized. Invalidate the + // whole screen. + helper_.InvalidateScreen(current_frame->size()); + } + + helper_.set_size_most_recent(current_frame->size()); + + // Emit the current frame. + DesktopFrame* frame = queue_.current_frame()->Share(); + frame->set_dpi(DesktopVector(GetDeviceCaps(desktop_dc_, LOGPIXELSX), + GetDeviceCaps(desktop_dc_, LOGPIXELSY))); + frame->mutable_updated_region()->Clear(); + helper_.TakeInvalidRegion(frame->mutable_updated_region()); + frame->set_capture_time_ms( + (TickTime::Now() - capture_start_time).Milliseconds()); + callback_->OnCaptureCompleted(frame); +} + +bool ScreenCapturerWinMagnifier::GetScreenList(ScreenList* screens) { + return webrtc::GetScreenList(screens); +} + +bool ScreenCapturerWinMagnifier::SelectScreen(ScreenId id) { + bool valid = IsScreenValid(id, ¤t_device_key_); + + // Set current_screen_id_ even if the fallback capturer is being used, so we + // can switch back to the magnifier when possible. + if (valid) + current_screen_id_ = id; + + if (fallback_capturer_started_) + fallback_capturer_->SelectScreen(id); + + return valid; +} + +void ScreenCapturerWinMagnifier::SetExcludedWindow(WindowId excluded_window) { + excluded_window_ = (HWND)excluded_window; + if (excluded_window_ && magnifier_initialized_) { + set_window_filter_list_func_( + magnifier_window_, MW_FILTERMODE_EXCLUDE, 1, &excluded_window_); + } +} + +bool ScreenCapturerWinMagnifier::CaptureImage(const DesktopRect& rect) { + assert(magnifier_initialized_); + + // Set the magnifier control to cover the captured rect. The content of the + // magnifier control will be the captured image. + BOOL result = SetWindowPos(magnifier_window_, + NULL, + rect.left(), rect.top(), + rect.width(), rect.height(), + 0); + if (!result) { + LOG_F(LS_WARNING) << "Failed to call SetWindowPos: " << GetLastError() + << ". Rect = {" << rect.left() << ", " << rect.top() + << ", " << rect.right() << ", " << rect.bottom() << "}"; + return false; + } + + magnifier_capture_succeeded_ = false; + + RECT native_rect = {rect.left(), rect.top(), rect.right(), rect.bottom()}; + + // OnCaptured will be called via OnMagImageScalingCallback and fill in the + // frame before set_window_source_func_ returns. + result = set_window_source_func_(magnifier_window_, native_rect); + + if (!result) { + LOG_F(LS_WARNING) << "Failed to call MagSetWindowSource: " << GetLastError() + << ". Rect = {" << rect.left() << ", " << rect.top() + << ", " << rect.right() << ", " << rect.bottom() << "}"; + return false; + } + + return magnifier_capture_succeeded_; +} + +BOOL ScreenCapturerWinMagnifier::OnMagImageScalingCallback( + HWND hwnd, + void* srcdata, + MAGIMAGEHEADER srcheader, + void* destdata, + MAGIMAGEHEADER destheader, + RECT unclipped, + RECT clipped, + HRGN dirty) { + assert(tls_index_.Value() != TLS_OUT_OF_INDEXES); + + ScreenCapturerWinMagnifier* owner = + reinterpret_cast( + TlsGetValue(tls_index_.Value())); + + owner->OnCaptured(srcdata, srcheader); + + return TRUE; +} + +bool ScreenCapturerWinMagnifier::InitializeMagnifier() { + assert(!magnifier_initialized_); + + desktop_dc_ = GetDC(NULL); + + mag_lib_handle_ = LoadLibrary(L"Magnification.dll"); + if (!mag_lib_handle_) + return false; + + // Initialize Magnification API function pointers. + mag_initialize_func_ = reinterpret_cast( + GetProcAddress(mag_lib_handle_, "MagInitialize")); + mag_uninitialize_func_ = reinterpret_cast( + GetProcAddress(mag_lib_handle_, "MagUninitialize")); + set_window_source_func_ = reinterpret_cast( + GetProcAddress(mag_lib_handle_, "MagSetWindowSource")); + set_window_filter_list_func_ = reinterpret_cast( + GetProcAddress(mag_lib_handle_, "MagSetWindowFilterList")); + set_image_scaling_callback_func_ = + reinterpret_cast( + GetProcAddress(mag_lib_handle_, "MagSetImageScalingCallback")); + + if (!mag_initialize_func_ || !mag_uninitialize_func_ || + !set_window_source_func_ || !set_window_filter_list_func_ || + !set_image_scaling_callback_func_) { + LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: " + << "library functions missing."; + return false; + } + + BOOL result = mag_initialize_func_(); + if (!result) { + LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: " + << "error from MagInitialize " << GetLastError(); + return false; + } + + HMODULE hInstance = NULL; + result = GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast(&DefWindowProc), + &hInstance); + if (!result) { + mag_uninitialize_func_(); + LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: " + << "error from GetModulehandleExA " << GetLastError(); + return false; + } + + // Register the host window class. See the MSDN documentation of the + // Magnification API for more infomation. + WNDCLASSEX wcex = {}; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.lpfnWndProc = &DefWindowProc; + wcex.hInstance = hInstance; + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.lpszClassName = kMagnifierHostClass; + + // Ignore the error which may happen when the class is already registered. + RegisterClassEx(&wcex); + + // Create the host window. + host_window_ = CreateWindowEx(WS_EX_LAYERED, + kMagnifierHostClass, + kHostWindowName, + 0, + 0, 0, 0, 0, + NULL, + NULL, + hInstance, + NULL); + if (!host_window_) { + mag_uninitialize_func_(); + LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: " + << "error from creating host window " << GetLastError(); + return false; + } + + // Create the magnifier control. + magnifier_window_ = CreateWindow(kMagnifierWindowClass, + kMagnifierWindowName, + WS_CHILD | WS_VISIBLE, + 0, 0, 0, 0, + host_window_, + NULL, + hInstance, + NULL); + if (!magnifier_window_) { + mag_uninitialize_func_(); + LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: " + << "error from creating magnifier window " + << GetLastError(); + return false; + } + + // Hide the host window. + ShowWindow(host_window_, SW_HIDE); + + // Set the scaling callback to receive captured image. + result = set_image_scaling_callback_func_( + magnifier_window_, + &ScreenCapturerWinMagnifier::OnMagImageScalingCallback); + if (!result) { + mag_uninitialize_func_(); + LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: " + << "error from MagSetImageScalingCallback " + << GetLastError(); + return false; + } + + if (excluded_window_) { + result = set_window_filter_list_func_( + magnifier_window_, MW_FILTERMODE_EXCLUDE, 1, &excluded_window_); + if (!result) { + mag_uninitialize_func_(); + LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: " + << "error from MagSetWindowFilterList " + << GetLastError(); + return false; + } + } + + if (tls_index_.Value() == TLS_OUT_OF_INDEXES) { + // More than one threads may get here at the same time, but only one will + // write to tls_index_ using CompareExchange. + DWORD new_tls_index = TlsAlloc(); + if (!tls_index_.CompareExchange(new_tls_index, TLS_OUT_OF_INDEXES)) + TlsFree(new_tls_index); + } + + assert(tls_index_.Value() != TLS_OUT_OF_INDEXES); + TlsSetValue(tls_index_.Value(), this); + + magnifier_initialized_ = true; + return true; +} + +void ScreenCapturerWinMagnifier::OnCaptured(void* data, + const MAGIMAGEHEADER& header) { + DesktopFrame* current_frame = queue_.current_frame(); + + // Verify the format. + // TODO(jiayl): support capturing sources with pixel formats other than RGBA. + int captured_bytes_per_pixel = header.cbSize / header.width / header.height; + if (header.format != GUID_WICPixelFormat32bppRGBA || + header.width != static_cast(current_frame->size().width()) || + header.height != static_cast(current_frame->size().height()) || + header.stride != static_cast(current_frame->stride()) || + captured_bytes_per_pixel != DesktopFrame::kBytesPerPixel) { + LOG_F(LS_WARNING) << "Output format does not match the captured format: " + << "width = " << header.width << ", " + << "height = " << header.height << ", " + << "stride = " << header.stride << ", " + << "bpp = " << captured_bytes_per_pixel << ", " + << "pixel format RGBA ? " + << (header.format == GUID_WICPixelFormat32bppRGBA) << "."; + return; + } + + // Copy the data into the frame. + current_frame->CopyPixelsFrom( + reinterpret_cast(data), + header.stride, + DesktopRect::MakeXYWH(0, 0, header.width, header.height)); + + magnifier_capture_succeeded_ = true; +} + +void ScreenCapturerWinMagnifier::CreateCurrentFrameIfNecessary( + const DesktopSize& size) { + // If the current buffer is from an older generation then allocate a new one. + // Note that we can't reallocate other buffers at this point, since the caller + // may still be reading from them. + if (!queue_.current_frame() || !queue_.current_frame()->size().equals(size)) { + size_t buffer_size = + size.width() * size.height() * DesktopFrame::kBytesPerPixel; + SharedMemory* shared_memory = callback_->CreateSharedMemory(buffer_size); + + scoped_ptr buffer; + if (shared_memory) { + buffer.reset(new SharedMemoryDesktopFrame( + size, size.width() * DesktopFrame::kBytesPerPixel, shared_memory)); + } else { + buffer.reset(new BasicDesktopFrame(size)); + } + queue_.ReplaceCurrentFrame(buffer.release()); + } +} + +bool ScreenCapturerWinMagnifier::IsCapturingPrimaryScreenOnly() const { + if (current_screen_id_ != kFullDesktopScreenId) + return current_screen_id_ == 0; // the primary screen is always '0'. + + return GetSystemMetrics(SM_CMONITORS) == 1; +} + +void ScreenCapturerWinMagnifier::StartFallbackCapturer() { + assert(fallback_capturer_); + if (!fallback_capturer_started_) { + fallback_capturer_started_ = true; + + fallback_capturer_->Start(callback_); + fallback_capturer_->SelectScreen(current_screen_id_); + } +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_MAGNIFIER_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_MAGNIFIER_H_ + +#include +#include +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h" +#include "webrtc/modules/desktop_capture/screen_capturer.h" +#include "webrtc/modules/desktop_capture/screen_capturer_helper.h" +#include "webrtc/modules/desktop_capture/win/scoped_thread_desktop.h" +#include "webrtc/system_wrappers/interface/atomic32.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class DesktopFrame; +class DesktopRect; +class Differ; + +// Captures the screen using the Magnification API to support window exclusion. +// Each capturer must run on a dedicated thread because it uses thread local +// storage for redirecting the library callback. Also the thread must have a UI +// message loop to handle the window messages for the magnifier window. +class ScreenCapturerWinMagnifier : public ScreenCapturer { + public: + // |fallback_capturer| will be used to capture the screen if a non-primary + // screen is being captured, or the OS does not support Magnification API, or + // the magnifier capturer fails (e.g. in Windows8 Metro mode). + explicit ScreenCapturerWinMagnifier( + scoped_ptr fallback_capturer); + virtual ~ScreenCapturerWinMagnifier(); + + // Overridden from ScreenCapturer: + virtual void Start(Callback* callback) OVERRIDE; + virtual void Capture(const DesktopRegion& region) OVERRIDE; + virtual bool GetScreenList(ScreenList* screens) OVERRIDE; + virtual bool SelectScreen(ScreenId id) OVERRIDE; + virtual void SetExcludedWindow(WindowId window) OVERRIDE; + + private: + typedef BOOL(WINAPI* MagImageScalingCallback)(HWND hwnd, + void* srcdata, + MAGIMAGEHEADER srcheader, + void* destdata, + MAGIMAGEHEADER destheader, + RECT unclipped, + RECT clipped, + HRGN dirty); + typedef BOOL(WINAPI* MagInitializeFunc)(void); + typedef BOOL(WINAPI* MagUninitializeFunc)(void); + typedef BOOL(WINAPI* MagSetWindowSourceFunc)(HWND hwnd, RECT rect); + typedef BOOL(WINAPI* MagSetWindowFilterListFunc)(HWND hwnd, + DWORD dwFilterMode, + int count, + HWND* pHWND); + typedef BOOL(WINAPI* MagSetImageScalingCallbackFunc)( + HWND hwnd, + MagImageScalingCallback callback); + + static BOOL WINAPI OnMagImageScalingCallback(HWND hwnd, + void* srcdata, + MAGIMAGEHEADER srcheader, + void* destdata, + MAGIMAGEHEADER destheader, + RECT unclipped, + RECT clipped, + HRGN dirty); + + // Captures the screen within |rect| in the desktop coordinates. Returns true + // if succeeded. + // It can only capture the primary screen for now. The magnification library + // crashes under some screen configurations (e.g. secondary screen on top of + // primary screen) if it tries to capture a non-primary screen. The caller + // must make sure not calling it on non-primary screens. + bool CaptureImage(const DesktopRect& rect); + + // Helper method for setting up the magnifier control. Returns true if + // succeeded. + bool InitializeMagnifier(); + + // Called by OnMagImageScalingCallback to output captured data. + void OnCaptured(void* data, const MAGIMAGEHEADER& header); + + // Makes sure the current frame exists and matches |size|. + void CreateCurrentFrameIfNecessary(const DesktopSize& size); + + // Returns true if we are capturing the primary screen only. + bool IsCapturingPrimaryScreenOnly() const; + + // Start the fallback capturer and select the screen. + void StartFallbackCapturer(); + + static Atomic32 tls_index_; + + scoped_ptr fallback_capturer_; + bool fallback_capturer_started_; + Callback* callback_; + ScreenId current_screen_id_; + std::wstring current_device_key_; + HWND excluded_window_; + + // A thread-safe list of invalid rectangles, and the size of the most + // recently captured screen. + ScreenCapturerHelper helper_; + + // Queue of the frames buffers. + ScreenCaptureFrameQueue queue_; + + // Class to calculate the difference between two screen bitmaps. + scoped_ptr differ_; + + // Used to suppress duplicate logging of SetThreadExecutionState errors. + bool set_thread_execution_state_failed_; + + ScopedThreadDesktop desktop_; + + // Used for getting the screen dpi. + HDC desktop_dc_; + + HMODULE mag_lib_handle_; + MagInitializeFunc mag_initialize_func_; + MagUninitializeFunc mag_uninitialize_func_; + MagSetWindowSourceFunc set_window_source_func_; + MagSetWindowFilterListFunc set_window_filter_list_func_; + MagSetImageScalingCallbackFunc set_image_scaling_callback_func_; + + // The hidden window hosting the magnifier control. + HWND host_window_; + // The magnifier control that captures the screen. + HWND magnifier_window_; + + // True if the magnifier control has been successfully initialized. + bool magnifier_initialized_; + + // True if the last OnMagImageScalingCallback was called and handled + // successfully. Reset at the beginning of each CaptureImage call. + bool magnifier_capture_succeeded_; + + DISALLOW_COPY_AND_ASSIGN(ScreenCapturerWinMagnifier); +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_MAGNIFIER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capture_utils.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capture_utils.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capture_utils.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capture_utils.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/win/screen_capture_utils.h" + +#include +#include + +namespace webrtc { + +bool GetScreenList(ScreenCapturer::ScreenList* screens) { + assert(screens->size() == 0); + + BOOL enum_result = TRUE; + for (int device_index = 0;; ++device_index) { + DISPLAY_DEVICE device; + device.cb = sizeof(device); + enum_result = EnumDisplayDevices(NULL, device_index, &device, 0); + + // |enum_result| is 0 if we have enumerated all devices. + if (!enum_result) + break; + + // We only care about active displays. + if (!(device.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + + ScreenCapturer::Screen screen; + screen.id = device_index; + screens->push_back(screen); + } + return true; +} + +bool IsScreenValid(ScreenId screen, std::wstring* device_key) { + if (screen == kFullDesktopScreenId) { + *device_key = L""; + return true; + } + + DISPLAY_DEVICE device; + device.cb = sizeof(device); + BOOL enum_result = EnumDisplayDevices(NULL, screen, &device, 0); + if (enum_result) + *device_key = device.DeviceKey; + + return !!enum_result; +} + +DesktopRect GetScreenRect(ScreenId screen, const std::wstring& device_key) { + assert(IsGUIThread(false)); + if (screen == kFullDesktopScreenId) { + return DesktopRect::MakeXYWH(GetSystemMetrics(SM_XVIRTUALSCREEN), + GetSystemMetrics(SM_YVIRTUALSCREEN), + GetSystemMetrics(SM_CXVIRTUALSCREEN), + GetSystemMetrics(SM_CYVIRTUALSCREEN)); + } + + DISPLAY_DEVICE device; + device.cb = sizeof(device); + BOOL result = EnumDisplayDevices(NULL, screen, &device, 0); + if (!result) + return DesktopRect(); + + // Verifies the device index still maps to the same display device, to make + // sure we are capturing the same device when devices are added or removed. + // DeviceKey is documented as reserved, but it actually contains the registry + // key for the device and is unique for each monitor, while DeviceID is not. + if (device_key != device.DeviceKey) + return DesktopRect(); + + DEVMODE device_mode; + device_mode.dmSize = sizeof(device_mode); + device_mode.dmDriverExtra = 0; + result = EnumDisplaySettingsEx( + device.DeviceName, ENUM_CURRENT_SETTINGS, &device_mode, 0); + if (!result) + return DesktopRect(); + + return DesktopRect::MakeXYWH(device_mode.dmPosition.x, + device_mode.dmPosition.y, + device_mode.dmPelsWidth, + device_mode.dmPelsHeight); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capture_utils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capture_utils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capture_utils.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capture_utils.h 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURE_UTILS_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURE_UTILS_H_ + +#include "webrtc/modules/desktop_capture/screen_capturer.h" + +namespace webrtc { + +// Output the list of active screens into |screens|. Returns true if succeeded, +// or false if it fails to enumerate the display devices. +bool GetScreenList(ScreenCapturer::ScreenList* screens); + +// Returns true if |screen| is a valid screen. The screen device key is +// returned through |device_key| if the screen is valid. The device key can be +// used in GetScreenRect to verify the screen matches the previously obtained +// id. +bool IsScreenValid(ScreenId screen, std::wstring* device_key); + +// Get the rect of the screen identified by |screen|, relative to the primary +// display's top-left. If the screen device key does not match |device_key|, or +// the screen does not exist, or any error happens, an empty rect is returned. +DesktopRect GetScreenRect(ScreenId screen, const std::wstring& device_key); + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURE_UTILS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/window_capture_utils.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/window_capture_utils.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/window_capture_utils.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/window_capture_utils.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/win/window_capture_utils.h" + +namespace webrtc { + +bool +GetCroppedWindowRect(HWND window, + DesktopRect* cropped_rect, + DesktopRect* original_rect) { + RECT rect; + if (!GetWindowRect(window, &rect)) { + return false; + } + WINDOWPLACEMENT window_placement; + window_placement.length = sizeof(window_placement); + if (!GetWindowPlacement(window, &window_placement)) { + return false; + } + + *original_rect = DesktopRect::MakeLTRB( + rect.left, rect.top, rect.right, rect.bottom); + + if (window_placement.showCmd & SW_SHOWMAXIMIZED) { + DesktopSize border = DesktopSize(GetSystemMetrics(SM_CXSIZEFRAME), + GetSystemMetrics(SM_CYSIZEFRAME)); + *cropped_rect = DesktopRect::MakeLTRB( + rect.left + border.width(), + rect.top, + rect.right - border.width(), + rect.bottom - border.height()); + } else { + *cropped_rect = *original_rect; + } + return true; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/window_capture_utils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/window_capture_utils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/window_capture_utils.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/win/window_capture_utils.h 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/modules/desktop_capture/desktop_geometry.h" + +namespace webrtc { + +// Output the window rect, with the left/right/bottom frame border cropped if +// the window is maximized. |cropped_rect| is the cropped rect relative to the +// desktop. |original_rect| is the original rect returned from GetWindowRect. +// Returns true if all API calls succeeded. +bool GetCroppedWindowRect(HWND window, + DesktopRect* cropped_rect, + DesktopRect* original_rect); + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer.h 2015-02-03 14:33:36.000000000 +0000 @@ -11,12 +11,12 @@ #ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_CAPTURER_H_ #define WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_CAPTURER_H_ -#include #include +#include +#include "webrtc/base/constructormagic.h" #include "webrtc/modules/desktop_capture/desktop_capture_types.h" #include "webrtc/modules/desktop_capture/desktop_capturer.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/typedefs.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_mac.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_mac.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_mac.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_mac.mm 2015-02-03 14:33:36.000000000 +0000 @@ -15,36 +15,38 @@ #include #include +#include "webrtc/base/macutils.h" +#include "webrtc/modules/desktop_capture/desktop_capture_options.h" #include "webrtc/modules/desktop_capture/desktop_frame.h" +#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h" +#include "webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.h" +#include "webrtc/modules/desktop_capture/mac/window_list_utils.h" #include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/system_wrappers/interface/scoped_refptr.h" +#include "webrtc/system_wrappers/interface/tick_util.h" namespace webrtc { namespace { -bool CFStringRefToUtf8(const CFStringRef string, std::string* str_utf8) { - assert(string); - assert(str_utf8); - CFIndex length = CFStringGetLength(string); - size_t max_length_utf8 = - CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8); - str_utf8->resize(max_length_utf8); - CFIndex used_bytes; - int result = CFStringGetBytes( - string, CFRangeMake(0, length), kCFStringEncodingUTF8, 0, false, - reinterpret_cast(&*str_utf8->begin()), max_length_utf8, - &used_bytes); - if (result != length) { - str_utf8->clear(); - return false; - } - str_utf8->resize(used_bytes); - return true; +// Returns true if the window exists. +bool IsWindowValid(CGWindowID id) { + CFArrayRef window_id_array = + CFArrayCreate(NULL, reinterpret_cast(&id), 1, NULL); + CFArrayRef window_array = + CGWindowListCreateDescriptionFromArray(window_id_array); + bool valid = window_array && CFArrayGetCount(window_array); + CFRelease(window_id_array); + CFRelease(window_array); + + return valid; } class WindowCapturerMac : public WindowCapturer { public: - WindowCapturerMac(); + explicit WindowCapturerMac( + scoped_refptr + full_screen_chrome_window_detector); virtual ~WindowCapturerMac(); // WindowCapturer interface. @@ -58,14 +60,22 @@ private: Callback* callback_; + + // The window being captured. CGWindowID window_id_; + scoped_refptr + full_screen_chrome_window_detector_; + DISALLOW_COPY_AND_ASSIGN(WindowCapturerMac); }; -WindowCapturerMac::WindowCapturerMac() +WindowCapturerMac::WindowCapturerMac( + scoped_refptr + full_screen_chrome_window_detector) : callback_(NULL), - window_id_(0) { + window_id_(0), + full_screen_chrome_window_detector_(full_screen_chrome_window_detector) { } WindowCapturerMac::~WindowCapturerMac() { @@ -102,7 +112,7 @@ CFNumberGetValue(window_id, kCFNumberIntType, &id); WindowCapturer::Window window; window.id = id; - if (!CFStringRefToUtf8(window_title, &(window.title)) || + if (!rtc::ToUtf8(window_title, &(window.title)) || window.title.empty()) { continue; } @@ -115,22 +125,8 @@ } bool WindowCapturerMac::SelectWindow(WindowId id) { - // Request description for the specified window to make sure |id| is valid. - CGWindowID ids[1]; - ids[0] = id; - CFArrayRef window_id_array = - CFArrayCreate(NULL, reinterpret_cast(&ids), 1, NULL); - CFArrayRef window_array = - CGWindowListCreateDescriptionFromArray(window_id_array); - int results_count = window_array ? CFArrayGetCount(window_array) : 0; - CFRelease(window_id_array); - CFRelease(window_array); - - if (results_count == 0) { - // Could not find the window. It might have been closed. + if (!IsWindowValid(id)) return false; - } - window_id_ = id; return true; } @@ -180,12 +176,25 @@ } void WindowCapturerMac::Capture(const DesktopRegion& region) { + if (!IsWindowValid(window_id_)) { + callback_->OnCaptureCompleted(NULL); + return; + } + + CGWindowID on_screen_window = window_id_; + if (full_screen_chrome_window_detector_) { + CGWindowID full_screen_window = + full_screen_chrome_window_detector_->FindFullScreenWindow(window_id_); + + if (full_screen_window != kCGNullWindowID) + on_screen_window = full_screen_window; + } + CGImageRef window_image = CGWindowListCreateImage( CGRectNull, kCGWindowListOptionIncludingWindow, - window_id_, kCGWindowImageBoundsIgnoreFraming); + on_screen_window, kCGWindowImageBoundsIgnoreFraming); if (!window_image) { - CFRelease(window_image); callback_->OnCaptureCompleted(NULL); return; } @@ -215,14 +224,20 @@ CFRelease(cf_data); CFRelease(window_image); + frame->mutable_updated_region()->SetRect( + DesktopRect::MakeSize(frame->size())); + callback_->OnCaptureCompleted(frame); + + if (full_screen_chrome_window_detector_) + full_screen_chrome_window_detector_->UpdateWindowListIfNeeded(window_id_); } } // namespace // static WindowCapturer* WindowCapturer::Create(const DesktopCaptureOptions& options) { - return new WindowCapturerMac(); + return new WindowCapturerMac(options.full_screen_chrome_window_detector()); } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,7 +10,7 @@ #include "webrtc/modules/desktop_capture/window_capturer.h" -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/desktop_capture/desktop_capture_options.h" #include "webrtc/modules/desktop_capture/desktop_frame.h" #include "webrtc/modules/desktop_capture/desktop_region.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_win.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_win.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_win.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_win.cc 2015-02-03 14:33:36.000000000 +0000 @@ -11,9 +11,10 @@ #include "webrtc/modules/desktop_capture/window_capturer.h" #include -#include +#include "webrtc/base/win32.h" #include "webrtc/modules/desktop_capture/desktop_frame_win.h" +#include "webrtc/modules/desktop_capture/win/window_capture_utils.h" #include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" @@ -23,22 +24,6 @@ typedef HRESULT (WINAPI *DwmIsCompositionEnabledFunc)(BOOL* enabled); -// Coverts a zero-terminated UTF-16 string to UTF-8. Returns an empty string if -// error occurs. -std::string Utf16ToUtf8(const WCHAR* str) { - int len_utf8 = WideCharToMultiByte(CP_UTF8, 0, str, -1, - NULL, 0, NULL, NULL); - if (len_utf8 <= 0) - return std::string(); - std::string result(len_utf8, '\0'); - int rv = WideCharToMultiByte(CP_UTF8, 0, str, -1, - &*(result.begin()), len_utf8, NULL, NULL); - if (rv != len_utf8) - assert(false); - - return result; -} - BOOL CALLBACK WindowsEnumerationHandler(HWND hwnd, LPARAM param) { assert(IsGUIThread(false)); WindowCapturer::WindowList* list = @@ -71,7 +56,7 @@ WCHAR window_title[kTitleLength]; // Truncate the title if it's longer than kTitleLength. GetWindowText(hwnd, window_title, kTitleLength); - window.title = Utf16ToUtf8(window_title); + window.title = rtc::ToUtf8(window_title); // Skip windows when we failed to convert the title or it is empty. if (window.title.empty()) @@ -187,15 +172,27 @@ return; } - // Stop capturing if the window has been minimized or hidden. - if (IsIconic(window_) || !IsWindowVisible(window_)) { + // Stop capturing if the window has been closed or hidden. + if (!IsWindow(window_) || !IsWindowVisible(window_)) { callback_->OnCaptureCompleted(NULL); return; } - RECT rect; - if (!GetWindowRect(window_, &rect)) { - LOG(LS_WARNING) << "Failed to get window size: " << GetLastError(); + // Return a 1x1 black frame if the window is minimized, to match the behavior + // on Mac. + if (IsIconic(window_)) { + BasicDesktopFrame* frame = new BasicDesktopFrame(DesktopSize(1, 1)); + memset(frame->data(), 0, frame->stride() * frame->size().height()); + + previous_size_ = frame->size(); + callback_->OnCaptureCompleted(frame); + return; + } + + DesktopRect original_rect; + DesktopRect cropped_rect; + if (!GetCroppedWindowRect(window_, &cropped_rect, &original_rect)) { + LOG(LS_WARNING) << "Failed to get window info: " << GetLastError(); callback_->OnCaptureCompleted(NULL); return; } @@ -208,8 +205,7 @@ } scoped_ptr frame(DesktopFrameWin::Create( - DesktopSize(rect.right - rect.left, rect.bottom - rect.top), - NULL, window_dc)); + cropped_rect.size(), NULL, window_dc)); if (!frame.get()) { ReleaseDC(window_, window_dc); callback_->OnCaptureCompleted(NULL); @@ -244,7 +240,10 @@ // Aero is enabled or PrintWindow() failed, use BitBlt. if (!result) { result = BitBlt(mem_dc, 0, 0, frame->size().width(), frame->size().height(), - window_dc, 0, 0, SRCCOPY); + window_dc, + cropped_rect.left() - original_rect.left(), + cropped_rect.top() - original_rect.top(), + SRCCOPY); } SelectObject(mem_dc, previous_object); @@ -253,6 +252,9 @@ previous_size_ = frame->size(); + frame->mutable_updated_region()->SetRect( + DesktopRect::MakeSize(frame->size())); + if (!result) { LOG(LS_ERROR) << "Both PrintWindow() and BitBlt() failed."; frame.reset(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,6 +10,7 @@ #include "webrtc/modules/desktop_capture/window_capturer.h" +#include #include #include #include @@ -17,7 +18,6 @@ #include #include -#include #include "webrtc/modules/desktop_capture/desktop_capture_options.h" #include "webrtc/modules/desktop_capture/desktop_frame.h" @@ -228,6 +228,12 @@ void WindowCapturerLinux::Capture(const DesktopRegion& region) { x_display_->ProcessPendingXEvents(); + if (!x_server_pixel_buffer_.IsWindowValid()) { + LOG(LS_INFO) << "The window is no longer valid."; + callback_->OnCaptureCompleted(NULL); + return; + } + if (!has_composite_extension_) { // Without the Xcomposite extension we capture when the whole window is // visible on screen and not covered by any other window. This is not @@ -244,6 +250,9 @@ x_server_pixel_buffer_.CaptureRect(DesktopRect::MakeSize(frame->size()), frame); + frame->mutable_updated_region()->SetRect( + DesktopRect::MakeSize(frame->size())); + callback_->OnCaptureCompleted(frame); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/x_error_trap.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/x_error_trap.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/x_error_trap.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/x_error_trap.h 2015-02-03 14:33:36.000000000 +0000 @@ -15,7 +15,7 @@ #undef max // Xlibint.h defines this and it breaks std::max #undef min // Xlibint.h defines this and it breaks std::min -#include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/base/constructormagic.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc 2015-02-03 14:33:36.000000000 +0000 @@ -213,6 +213,18 @@ return true; } +bool XServerPixelBuffer::IsWindowValid() const { + XWindowAttributes attributes; + { + XErrorTrap error_trap(display_); + if (!XGetWindowAttributes(display_, window_, &attributes) || + error_trap.GetLastErrorAndDisable() != 0) { + return false; + } + } + return true; +} + void XServerPixelBuffer::Synchronize() { if (shm_segment_info_ && !shm_pixmap_) { // XShmGetImage can fail if the display is being reconfigured. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h 2015-02-03 14:33:36.000000000 +0000 @@ -40,6 +40,9 @@ // Returns the size of the window the buffer was initialized for. const DesktopSize& window_size() { return window_size_; } + // Returns true if the window can be found. + bool IsWindowValid() const; + // If shared memory is being used without pixmaps, synchronize this pixel // buffer with the root window contents (otherwise, this is a no-op). // This is to avoid doing a full-screen capture for each individual diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/interface/module_common_types.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/interface/module_common_types.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/interface/module_common_types.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/interface/module_common_types.h 2015-02-03 14:33:36.000000000 +0000 @@ -16,38 +16,12 @@ #include +#include "webrtc/base/constructormagic.h" #include "webrtc/common_types.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/typedefs.h" -#ifdef _WIN32 -// Remove warning "new behavior: elements of array will be default initialized". -#pragma warning(disable : 4351) -#endif - namespace webrtc { -struct RTPHeaderExtension { - bool hasTransmissionTimeOffset; - int32_t transmissionTimeOffset; - bool hasAbsoluteSendTime; - uint32_t absoluteSendTime; -}; - -struct RTPHeader { - bool markerBit; - uint8_t payloadType; - uint16_t sequenceNumber; - uint32_t timestamp; - uint32_t ssrc; - uint8_t numCSRCs; - uint32_t arrOfCSRCs[kRtpCsrcSize]; - uint8_t paddingLength; - uint16_t headerLength; - int payload_type_frequency; - RTPHeaderExtension extension; -}; - struct RTPAudioHeader { uint8_t numEnergy; // number of valid entries in arrOfEnergy uint8_t arrOfEnergy[kRtpCsrcSize]; // one energy byte (0-9) per channel @@ -55,21 +29,10 @@ uint8_t channel; // number of channels 2 = stereo }; -enum { - kNoPictureId = -1 -}; -enum { - kNoTl0PicIdx = -1 -}; -enum { - kNoTemporalIdx = -1 -}; -enum { - kNoKeyIdx = -1 -}; -enum { - kNoSimulcastIdx = 0 -}; +const int16_t kNoPictureId = -1; +const int16_t kNoTl0PicIdx = -1; +const uint8_t kNoTemporalIdx = 0xFF; +const int kNoKeyIdx = -1; struct RTPVideoHeaderVP8 { void InitRTPVideoHeaderVP8() { @@ -88,7 +51,7 @@ // kNoPictureId if PictureID does not exist. int16_t tl0PicIdx; // TL0PIC_IDX, 8 bits; // kNoTl0PicIdx means no value provided. - int8_t temporalIdx; // Temporal layer index, or kNoTemporalIdx. + uint8_t temporalIdx; // Temporal layer index, or kNoTemporalIdx. bool layerSync; // This frame is a layer sync frame. // Disabled if temporalIdx == kNoTemporalIdx. int keyIdx; // 5 bits; kNoKeyIdx means not used. @@ -98,8 +61,9 @@ }; struct RTPVideoHeaderH264 { - uint8_t nalu_header; - bool single_nalu; + uint8_t nalu_header; // us ********* REMOVE ME??? ********* + bool stap_a; // them *** or this? *** + bool single_nalu; }; union RTPVideoTypeHeader { @@ -132,6 +96,8 @@ RTPHeader header; FrameType frameType; RTPTypeHeader type; + // NTP time of the capture time in local timebase in milliseconds. + int64_t ntp_time_ms; }; class RTPFragmentationHeader { @@ -694,6 +660,10 @@ AudioFrame(); virtual ~AudioFrame() {} + // Resets all members to their default state (except does not modify the + // contents of |data_|). + void Reset(); + // |interleaved_| is not changed by this method. void UpdateFrame(int id, uint32_t timestamp, const int16_t* data, int samples_per_channel, int sample_rate_hz, @@ -711,13 +681,24 @@ AudioFrame& operator-=(const AudioFrame& rhs); int id_; + // RTP timestamp of the first sample in the AudioFrame. uint32_t timestamp_; + // Time since the first frame in milliseconds. + // -1 represents an uninitialized value. + int64_t elapsed_time_ms_; + // NTP time of the estimated capture time in local timebase in milliseconds. + // -1 represents an uninitialized value. + int64_t ntp_time_ms_; int16_t data_[kMaxDataSizeSamples]; int samples_per_channel_; int sample_rate_hz_; int num_channels_; SpeechType speech_type_; VADActivity vad_activity_; + // Note that there is no guarantee that |energy_| is correct. Any user of this + // member must verify that the value is correct. + // TODO(henrike) Remove |energy_|. + // See https://code.google.com/p/webrtc/issues/detail?id=3315. uint32_t energy_; bool interleaved_; @@ -726,16 +707,25 @@ }; inline AudioFrame::AudioFrame() - : id_(-1), - timestamp_(0), - data_(), - samples_per_channel_(0), - sample_rate_hz_(0), - num_channels_(1), - speech_type_(kUndefined), - vad_activity_(kVadUnknown), - energy_(0xffffffff), - interleaved_(true) {} + : data_() { + Reset(); +} + +inline void AudioFrame::Reset() { + id_ = -1; + // TODO(wu): Zero is a valid value for |timestamp_|. We should initialize + // to an invalid value, or add a new member to indicate invalidity. + timestamp_ = 0; + elapsed_time_ms_ = -1; + ntp_time_ms_ = -1; + samples_per_channel_ = 0; + sample_rate_hz_ = 0; + num_channels_ = 0; + speech_type_ = kUndefined; + vad_activity_ = kVadUnknown; + energy_ = 0xffffffff; + interleaved_ = true; +} inline void AudioFrame::UpdateFrame(int id, uint32_t timestamp, const int16_t* data, @@ -766,6 +756,8 @@ id_ = src.id_; timestamp_ = src.timestamp_; + elapsed_time_ms_ = src.elapsed_time_ms_; + ntp_time_ms_ = src.ntp_time_ms_; samples_per_channel_ = src.samples_per_channel_; sample_rate_hz_ = src.sample_rate_hz_; speech_type_ = src.speech_type_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/media_file/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/media_file/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/media_file/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/media_file/BUILD.gn 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,51 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../../build/webrtc.gni") + +config("media_file_config") { + visibility = [ ":*" ] # Only targets in this file can depend on this. + include_dirs = [ + "interface", + "../interface", + ] +} + +source_set("media_file") { + sources = [ + "interface/media_file.h", + "interface/media_file_defines.h", + "source/avi_file.cc", + "source/avi_file.h", + "source/media_file_impl.cc", + "source/media_file_impl.h", + "source/media_file_utility.cc", + "source/media_file_utility.h", + ] + + if (is_win) { + cflags = [ + # TODO(jschuh): Bug 1348: fix this warning. + "/wd4267", # size_t to int truncations + ] + } + + configs += [ "../..:common_config" ] + public_configs = [ + "../..:common_inherited_config", + ":media_file_config", + ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ "../../system_wrappers" ] +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/media_file/interface/media_file.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/media_file/interface/media_file.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/media_file/interface/media_file.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/media_file/interface/media_file.h 2015-02-03 14:33:36.000000000 +0000 @@ -27,7 +27,7 @@ static void DestroyMediaFile(MediaFile* module); // Set the MediaFile instance identifier. - virtual int32_t ChangeUniqueId(const int32_t id) = 0; + virtual int32_t ChangeUniqueId(const int32_t id) OVERRIDE = 0; // Put 10-60ms of audio data from file into the audioBuffer depending on // codec frame size. dataLengthInBytes is both an input and output diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/media_file/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/media_file/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/media_file/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/media_file/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -1,4 +1,5 @@ -pwestin@webrtc.org mflodman@webrtc.org perkj@webrtc.org -niklas.enbom@webrtc.org \ No newline at end of file +niklas.enbom@webrtc.org + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_media_file -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := \ - avi_file.cc \ - media_file_impl.cc \ - media_file_utility.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) \ - '-DWEBRTC_MODULE_UTILITY_VIDEO' - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../../interface \ - $(LOCAL_PATH)/../../utility/interface \ - $(LOCAL_PATH)/../../audio_coding/main/interface \ - $(LOCAL_PATH)/../../../system_wrappers/interface - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/media_file_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/media_file_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/media_file_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/media_file_impl.h 2015-02-03 14:33:36.000000000 +0000 @@ -26,16 +26,18 @@ MediaFileImpl(const int32_t id); ~MediaFileImpl(); - int32_t ChangeUniqueId(const int32_t id); - int32_t Process(); - int32_t TimeUntilNextProcess(); + virtual int32_t ChangeUniqueId(const int32_t id) OVERRIDE; + virtual int32_t Process() OVERRIDE; + virtual int32_t TimeUntilNextProcess() OVERRIDE; // MediaFile functions - int32_t PlayoutAudioData(int8_t* audioBuffer, uint32_t& dataLengthInBytes); - int32_t PlayoutAVIVideoData(int8_t* videoBuffer, - uint32_t& dataLengthInBytes); - int32_t PlayoutStereoData(int8_t* audioBufferLeft, int8_t* audioBufferRight, - uint32_t& dataLengthInBytes); + virtual int32_t PlayoutAudioData(int8_t* audioBuffer, + uint32_t& dataLengthInBytes) OVERRIDE; + virtual int32_t PlayoutAVIVideoData(int8_t* videoBuffer, + uint32_t& dataLengthInBytes) OVERRIDE; + virtual int32_t PlayoutStereoData(int8_t* audioBufferLeft, + int8_t* audioBufferRight, + uint32_t& dataLengthInBytes) OVERRIDE; virtual int32_t StartPlayingAudioFile( const char* fileName, const uint32_t notificationTimeMs = 0, @@ -43,51 +45,52 @@ const FileFormats format = kFileFormatPcm16kHzFile, const CodecInst* codecInst = NULL, const uint32_t startPointMs = 0, - const uint32_t stopPointMs = 0); - int32_t StartPlayingVideoFile(const char* fileName, const bool loop, - bool videoOnly, const FileFormats format); - int32_t StartPlayingAudioStream(InStream& stream, + const uint32_t stopPointMs = 0) OVERRIDE; + virtual int32_t StartPlayingVideoFile(const char* fileName, const bool loop, + bool videoOnly, + const FileFormats format) OVERRIDE; + virtual int32_t StartPlayingAudioStream(InStream& stream, const uint32_t notificationTimeMs = 0, const FileFormats format = kFileFormatPcm16kHzFile, const CodecInst* codecInst = NULL, const uint32_t startPointMs = 0, - const uint32_t stopPointMs = 0); - int32_t StopPlaying(); - bool IsPlaying(); - int32_t PlayoutPositionMs(uint32_t& positionMs) const; - int32_t IncomingAudioData(const int8_t* audioBuffer, - const uint32_t bufferLength); - int32_t IncomingAVIVideoData(const int8_t* audioBuffer, - const uint32_t bufferLength); - int32_t StartRecordingAudioFile( + const uint32_t stopPointMs = 0) OVERRIDE; + virtual int32_t StopPlaying() OVERRIDE; + virtual bool IsPlaying() OVERRIDE; + virtual int32_t PlayoutPositionMs(uint32_t& positionMs) const OVERRIDE; + virtual int32_t IncomingAudioData(const int8_t* audioBuffer, + const uint32_t bufferLength) OVERRIDE; + virtual int32_t IncomingAVIVideoData(const int8_t* audioBuffer, + const uint32_t bufferLength) OVERRIDE; + virtual int32_t StartRecordingAudioFile( const char* fileName, const FileFormats format, const CodecInst& codecInst, const uint32_t notificationTimeMs = 0, - const uint32_t maxSizeBytes = 0); - int32_t StartRecordingVideoFile( + const uint32_t maxSizeBytes = 0) OVERRIDE; + virtual int32_t StartRecordingVideoFile( const char* fileName, const FileFormats format, const CodecInst& codecInst, const VideoCodec& videoCodecInst, - bool videoOnly = false); - int32_t StartRecordingAudioStream( + bool videoOnly = false) OVERRIDE; + virtual int32_t StartRecordingAudioStream( OutStream& stream, const FileFormats format, const CodecInst& codecInst, - const uint32_t notificationTimeMs = 0); - int32_t StopRecording(); - bool IsRecording(); - int32_t RecordDurationMs(uint32_t& durationMs); - bool IsStereo(); - int32_t SetModuleFileCallback(FileCallback* callback); - int32_t FileDurationMs( + const uint32_t notificationTimeMs = 0) OVERRIDE; + virtual int32_t StopRecording() OVERRIDE; + virtual bool IsRecording() OVERRIDE; + virtual int32_t RecordDurationMs(uint32_t& durationMs) OVERRIDE; + virtual bool IsStereo() OVERRIDE; + virtual int32_t SetModuleFileCallback(FileCallback* callback) OVERRIDE; + virtual int32_t FileDurationMs( const char* fileName, uint32_t& durationMs, const FileFormats format, - const uint32_t freqInHz = 16000); - int32_t codec_info(CodecInst& codecInst) const; - int32_t VideoCodecInst(VideoCodec& codecInst) const; + const uint32_t freqInHz = 16000) OVERRIDE; + virtual int32_t codec_info(CodecInst& codecInst) const OVERRIDE; + virtual int32_t VideoCodecInst(VideoCodec& codecInst) const OVERRIDE; private: // Returns true if the combination of format and codecInst is valid. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/media_file_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/media_file_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/media_file_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/media_file_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,6 +10,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/media_file/interface/media_file.h" +#include "webrtc/system_wrappers/interface/compile_assert.h" #include "webrtc/system_wrappers/interface/sleep.h" #include "webrtc/test/testsupport/fileutils.h" #include "webrtc/test/testsupport/gtest_disable.h" @@ -45,3 +46,50 @@ ASSERT_EQ(0, media_file_->StopPlaying()); } + +TEST_F(MediaFileTest, WriteWavFile) { + // Write file. + static const int kHeaderSize = 44; + static const int kPayloadSize = 320; + webrtc::CodecInst codec = {0, "L16", 16000, kPayloadSize, 1}; + std::string outfile = webrtc::test::OutputPath() + "wavtest.wav"; + ASSERT_EQ(0, + media_file_->StartRecordingAudioFile( + outfile.c_str(), webrtc::kFileFormatWavFile, codec)); + static const int8_t kFakeData[kPayloadSize] = {0}; + ASSERT_EQ(0, media_file_->IncomingAudioData(kFakeData, kPayloadSize)); + ASSERT_EQ(0, media_file_->StopRecording()); + + // Check the file we just wrote. + static const uint8_t kExpectedHeader[] = { + 'R', 'I', 'F', 'F', + 0x64, 0x1, 0, 0, // size of whole file - 8: 320 + 44 - 8 + 'W', 'A', 'V', 'E', + 'f', 'm', 't', ' ', + 0x10, 0, 0, 0, // size of fmt block - 8: 24 - 8 + 0x1, 0, // format: PCM (1) + 0x1, 0, // channels: 1 + 0x80, 0x3e, 0, 0, // sample rate: 16000 + 0, 0x7d, 0, 0, // byte rate: 2 * 16000 + 0x2, 0, // block align: NumChannels * BytesPerSample + 0x10, 0, // bits per sample: 2 * 8 + 'd', 'a', 't', 'a', + 0x40, 0x1, 0, 0, // size of payload: 320 + }; + COMPILE_ASSERT(sizeof(kExpectedHeader) == kHeaderSize, header_size); + + EXPECT_EQ(size_t(kHeaderSize + kPayloadSize), + webrtc::test::GetFileSize(outfile)); + FILE* f = fopen(outfile.c_str(), "rb"); + ASSERT_TRUE(f); + + uint8_t header[kHeaderSize]; + ASSERT_EQ(1u, fread(header, kHeaderSize, 1, f)); + EXPECT_EQ(0, memcmp(kExpectedHeader, header, kHeaderSize)); + + uint8_t payload[kPayloadSize]; + ASSERT_EQ(1u, fread(payload, kPayloadSize, 1, f)); + EXPECT_EQ(0, memcmp(kFakeData, payload, kPayloadSize)); + + EXPECT_EQ(0, fclose(f)); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/media_file_utility.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/media_file_utility.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/media_file_utility.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/media_file_utility.cc 2015-02-03 14:33:36.000000000 +0000 @@ -14,6 +14,7 @@ #include #include +#include "webrtc/common_audio/wav_header.h" #include "webrtc/common_types.h" #include "webrtc/engine_configurations.h" #include "webrtc/modules/interface/module_common_types.h" @@ -25,12 +26,6 @@ #endif namespace { -enum WaveFormats -{ - kWaveFormatPcm = 0x0001, - kWaveFormatALaw = 0x0006, - kWaveFormatMuLaw = 0x0007 -}; // First 16 bytes the WAVE header. ckID should be "RIFF", wave_ckID should be // "WAVE" and ckSize is the chunk size (4 + n) @@ -183,7 +178,7 @@ waveFormatHeader.nSamplesPerSec = 8000; waveFormatHeader.wBitsPerSample = 8; waveFormatHeader.nBlockAlign = 1; - waveFormatHeader.wFormatTag = kWaveFormatMuLaw; + waveFormatHeader.wFormatTag = kWavFormatMuLaw; } else if (strncmp(audioCodecInst.plname, "PCMA", 4) == 0) { @@ -196,7 +191,7 @@ waveFormatHeader.nSamplesPerSec = 8000; waveFormatHeader.wBitsPerSample = 8; waveFormatHeader.nBlockAlign = 1; - waveFormatHeader.wFormatTag = kWaveFormatALaw; + waveFormatHeader.wFormatTag = kWavFormatALaw; } else if (strncmp(audioCodecInst.plname, "L16", 3) == 0) { @@ -210,7 +205,7 @@ waveFormatHeader.nSamplesPerSec = audioCodecInst.plfreq; waveFormatHeader.wBitsPerSample = 16; waveFormatHeader.nBlockAlign = 2; - waveFormatHeader.wFormatTag = kWaveFormatPcm; + waveFormatHeader.wFormatTag = kWavFormatPcm; } else { return -1; @@ -499,8 +494,7 @@ memcpy(tmpStr2, &_wavFormatObj.formatTag, 2); _wavFormatObj.formatTag = - (WaveFormats) ((uint32_t)tmpStr2[0] + - (((uint32_t)tmpStr2[1])<<8)); + (uint32_t)tmpStr2[0] + (((uint32_t)tmpStr2[1])<<8); memcpy(tmpStr2, &_wavFormatObj.nChannels, 2); _wavFormatObj.nChannels = (int16_t) ((uint32_t)tmpStr2[0] + @@ -575,9 +569,9 @@ // Either a proper format chunk has been read or a data chunk was come // across. - if( (_wavFormatObj.formatTag != kWaveFormatPcm) && - (_wavFormatObj.formatTag != kWaveFormatALaw) && - (_wavFormatObj.formatTag != kWaveFormatMuLaw)) + if( (_wavFormatObj.formatTag != kWavFormatPcm) && + (_wavFormatObj.formatTag != kWavFormatALaw) && + (_wavFormatObj.formatTag != kWavFormatMuLaw)) { WEBRTC_TRACE(kTraceError, kTraceFile, _id, "Coding formatTag value=%d not supported!", @@ -603,7 +597,7 @@ } // Calculate the number of bytes that 10 ms of audio data correspond to. - if(_wavFormatObj.formatTag == kWaveFormatPcm) + if(_wavFormatObj.formatTag == kWavFormatPcm) { // TODO (hellner): integer division for 22050 and 11025 would yield // the same result as the else statement. Remove those @@ -643,19 +637,19 @@ // Calculate the packet size for 10ms frames switch(formatTag) { - case kWaveFormatALaw: + case kWavFormatALaw: strcpy(codec_info_.plname, "PCMA"); _codecId = kCodecPcma; codec_info_.pltype = 8; codec_info_.pacsize = codec_info_.plfreq / 100; break; - case kWaveFormatMuLaw: + case kWavFormatMuLaw: strcpy(codec_info_.plname, "PCMU"); _codecId = kCodecPcmu; codec_info_.pltype = 0; codec_info_.pacsize = codec_info_.plfreq / 100; break; - case kWaveFormatPcm: + case kWavFormatPcm: codec_info_.pacsize = (bitsPerSample * (codec_info_.plfreq / 100)) / 8; if(samplesPerSec == 8000) { @@ -1054,14 +1048,14 @@ { _bytesPerSample = 1; if(WriteWavHeader(wav, 8000, _bytesPerSample, channels, - kWaveFormatMuLaw, 0) == -1) + kWavFormatMuLaw, 0) == -1) { return -1; } }else if(STR_CASE_CMP(codecInst.plname, "PCMA") == 0) { _bytesPerSample = 1; - if(WriteWavHeader(wav, 8000, _bytesPerSample, channels, kWaveFormatALaw, + if(WriteWavHeader(wav, 8000, _bytesPerSample, channels, kWavFormatALaw, 0) == -1) { return -1; @@ -1071,7 +1065,7 @@ { _bytesPerSample = 2; if(WriteWavHeader(wav, codecInst.plfreq, _bytesPerSample, channels, - kWaveFormatPcm, 0) == -1) + kWavFormatPcm, 0) == -1) { return -1; } @@ -1124,101 +1118,16 @@ const uint32_t format, const uint32_t lengthInBytes) { - // Frame size in bytes for 10 ms of audio. - int32_t frameSize = (freq / 100) * bytesPerSample * channels; + const int32_t frameSize = (freq / 100) * channels; // Calculate the number of full frames that the wave file contain. - const int32_t dataLengthInBytes = frameSize * - (lengthInBytes / frameSize); - - int8_t tmpStr[4]; - int8_t tmpChar; - uint32_t tmpLong; - - memcpy(tmpStr, "RIFF", 4); - wav.Write(tmpStr, 4); - - tmpLong = dataLengthInBytes + 36; - tmpChar = (int8_t)(tmpLong); - wav.Write(&tmpChar, 1); - tmpChar = (int8_t)(tmpLong >> 8); - wav.Write(&tmpChar, 1); - tmpChar = (int8_t)(tmpLong >> 16); - wav.Write(&tmpChar, 1); - tmpChar = (int8_t)(tmpLong >> 24); - wav.Write(&tmpChar, 1); - - memcpy(tmpStr, "WAVE", 4); - wav.Write(tmpStr, 4); - - memcpy(tmpStr, "fmt ", 4); - wav.Write(tmpStr, 4); - - tmpChar = 16; - wav.Write(&tmpChar, 1); - tmpChar = 0; - wav.Write(&tmpChar, 1); - tmpChar = 0; - wav.Write(&tmpChar, 1); - tmpChar = 0; - wav.Write(&tmpChar, 1); - - tmpChar = (int8_t)(format); - wav.Write(&tmpChar, 1); - tmpChar = 0; - wav.Write(&tmpChar, 1); - - tmpChar = (int8_t)(channels); - wav.Write(&tmpChar, 1); - tmpChar = 0; - wav.Write(&tmpChar, 1); - - tmpLong = freq; - tmpChar = (int8_t)(tmpLong); - wav.Write(&tmpChar, 1); - tmpChar = (int8_t)(tmpLong >> 8); - wav.Write(&tmpChar, 1); - tmpChar = (int8_t)(tmpLong >> 16); - wav.Write(&tmpChar, 1); - tmpChar = (int8_t)(tmpLong >> 24); - wav.Write(&tmpChar, 1); - - // nAverageBytesPerSec = Sample rate * Bytes per sample * Channels - tmpLong = bytesPerSample * freq * channels; - tmpChar = (int8_t)(tmpLong); - wav.Write(&tmpChar, 1); - tmpChar = (int8_t)(tmpLong >> 8); - wav.Write(&tmpChar, 1); - tmpChar = (int8_t)(tmpLong >> 16); - wav.Write(&tmpChar, 1); - tmpChar = (int8_t)(tmpLong >> 24); - wav.Write(&tmpChar, 1); - - // nBlockAlign = Bytes per sample * Channels - tmpChar = (int8_t)(bytesPerSample * channels); - wav.Write(&tmpChar, 1); - tmpChar = 0; - wav.Write(&tmpChar, 1); - - tmpChar = (int8_t)(bytesPerSample*8); - wav.Write(&tmpChar, 1); - tmpChar = 0; - wav.Write(&tmpChar, 1); - - memcpy(tmpStr, "data", 4); - wav.Write(tmpStr, 4); - - tmpLong = dataLengthInBytes; - tmpChar = (int8_t)(tmpLong); - wav.Write(&tmpChar, 1); - tmpChar = (int8_t)(tmpLong >> 8); - wav.Write(&tmpChar, 1); - tmpChar = (int8_t)(tmpLong >> 16); - wav.Write(&tmpChar, 1); - tmpChar = (int8_t)(tmpLong >> 24); - wav.Write(&tmpChar, 1); + const int32_t dataLengthInBytes = frameSize * (lengthInBytes / frameSize); + uint8_t buf[kWavHeaderSize]; + webrtc::WriteWavHeader(buf, channels, freq, static_cast(format), + bytesPerSample, dataLengthInBytes / bytesPerSample); + wav.Write(buf, kWavHeaderSize); return 0; } @@ -1235,12 +1144,12 @@ if(STR_CASE_CMP(codec_info_.plname, "L16") == 0) { res = WriteWavHeader(wav, codec_info_.plfreq, 2, channels, - kWaveFormatPcm, _bytesWritten); + kWavFormatPcm, _bytesWritten); } else if(STR_CASE_CMP(codec_info_.plname, "PCMU") == 0) { - res = WriteWavHeader(wav, 8000, 1, channels, kWaveFormatMuLaw, + res = WriteWavHeader(wav, 8000, 1, channels, kWavFormatMuLaw, _bytesWritten); } else if(STR_CASE_CMP(codec_info_.plname, "PCMA") == 0) { - res = WriteWavHeader(wav, 8000, 1, channels, kWaveFormatALaw, + res = WriteWavHeader(wav, 8000, 1, channels, kWavFormatALaw, _bytesWritten); } else { // Allow calling this API even if not writing to a WAVE file. @@ -2519,6 +2428,7 @@ break; } #endif + break; } case kFileFormatPreencodedFile: { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/media_file/source/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/modules.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/modules.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/modules.gyp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/modules.gyp 2015-02-03 14:33:36.000000000 +0000 @@ -12,9 +12,8 @@ 'audio_coding/codecs/cng/cng.gypi', 'audio_coding/codecs/g711/g711.gypi', 'audio_coding/codecs/pcm16b/pcm16b.gypi', - 'audio_coding/main/source/audio_coding_module.gypi', + 'audio_coding/main/acm2/audio_coding_module.gypi', 'audio_coding/neteq/neteq.gypi', - 'audio_coding/neteq4/neteq.gypi', 'audio_conference_mixer/source/audio_conference_mixer.gypi', 'audio_device/audio_device.gypi', 'audio_processing/audio_processing.gypi', @@ -47,6 +46,7 @@ }], ['include_tests==1', { 'includes': [ + 'audio_coding/codecs/tools/audio_codec_speed_tests.gypi', 'audio_processing/audio_processing_tests.gypi', 'rtp_rtcp/test/testFec/test_fec.gypi', 'video_coding/main/source/video_coding_test.gypi', @@ -78,17 +78,17 @@ '<@(audio_coding_defines)', ], 'dependencies': [ + 'acm_receive_test', + 'acm_send_test', 'audio_coding_module', 'audio_processing', - 'audioproc_unittest_proto', 'bitrate_controller', 'CNG', 'desktop_capture', 'iSACFix', 'media_file', - 'NetEq', - 'NetEq4', - 'NetEq4TestTools', + 'neteq', + 'neteq_test_tools', 'neteq_unittest_tools', 'paced_sender', 'PCM16B', # Needed by NetEq tests. @@ -105,17 +105,23 @@ '<(DEPTH)/testing/gtest.gyp:gtest', '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', + '<(webrtc_root)/modules/modules.gyp:video_capture_module_impl', '<(webrtc_root)/modules/video_coding/codecs/vp8/vp8.gyp:webrtc_vp8', + '<(webrtc_root)/modules/video_coding/codecs/vp9/vp9.gyp:webrtc_vp9', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', - '<(webrtc_root)/test/test.gyp:test_support_main', '<(webrtc_root)/test/test.gyp:frame_generator', + '<(webrtc_root)/test/test.gyp:rtp_test_utils', + '<(webrtc_root)/test/test.gyp:test_support_main', ], 'sources': [ + 'audio_coding/main/acm2/acm_opus_unittest.cc', 'audio_coding/main/acm2/acm_receiver_unittest.cc', + 'audio_coding/main/acm2/acm_receiver_unittest_oldapi.cc', + 'audio_coding/main/acm2/audio_coding_module_unittest.cc', + 'audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc', 'audio_coding/main/acm2/call_statistics_unittest.cc', 'audio_coding/main/acm2/initial_delay_manager_unittest.cc', 'audio_coding/main/acm2/nack_unittest.cc', - 'audio_coding/main/source/acm_neteq_unittest.cc', 'audio_coding/codecs/cng/cng_unittest.cc', 'audio_coding/codecs/isac/fix/source/filters_unittest.cc', 'audio_coding/codecs/isac/fix/source/filterbanks_unittest.cc', @@ -123,49 +129,50 @@ 'audio_coding/codecs/isac/fix/source/transform_unittest.cc', 'audio_coding/codecs/isac/main/source/isac_unittest.cc', 'audio_coding/codecs/opus/opus_unittest.cc', - 'audio_coding/neteq4/audio_multi_vector_unittest.cc', - 'audio_coding/neteq4/audio_vector_unittest.cc', - 'audio_coding/neteq4/background_noise_unittest.cc', - 'audio_coding/neteq4/buffer_level_filter_unittest.cc', - 'audio_coding/neteq4/comfort_noise_unittest.cc', - 'audio_coding/neteq4/decision_logic_unittest.cc', - 'audio_coding/neteq4/decoder_database_unittest.cc', - 'audio_coding/neteq4/delay_manager_unittest.cc', - 'audio_coding/neteq4/delay_peak_detector_unittest.cc', - 'audio_coding/neteq4/dsp_helper_unittest.cc', - 'audio_coding/neteq4/dtmf_buffer_unittest.cc', - 'audio_coding/neteq4/dtmf_tone_generator_unittest.cc', - 'audio_coding/neteq4/expand_unittest.cc', - 'audio_coding/neteq4/merge_unittest.cc', - 'audio_coding/neteq4/neteq_external_decoder_unittest.cc', - 'audio_coding/neteq4/neteq_impl_unittest.cc', - 'audio_coding/neteq4/neteq_stereo_unittest.cc', - 'audio_coding/neteq4/neteq_unittest.cc', - 'audio_coding/neteq4/normal_unittest.cc', - 'audio_coding/neteq4/packet_buffer_unittest.cc', - 'audio_coding/neteq4/payload_splitter_unittest.cc', - 'audio_coding/neteq4/post_decode_vad_unittest.cc', - 'audio_coding/neteq4/random_vector_unittest.cc', - 'audio_coding/neteq4/sync_buffer_unittest.cc', - 'audio_coding/neteq4/timestamp_scaler_unittest.cc', - 'audio_coding/neteq4/time_stretch_unittest.cc', - 'audio_coding/neteq4/mock/mock_audio_decoder.h', - 'audio_coding/neteq4/mock/mock_audio_vector.h', - 'audio_coding/neteq4/mock/mock_buffer_level_filter.h', - 'audio_coding/neteq4/mock/mock_decoder_database.h', - 'audio_coding/neteq4/mock/mock_delay_manager.h', - 'audio_coding/neteq4/mock/mock_delay_peak_detector.h', - 'audio_coding/neteq4/mock/mock_dtmf_buffer.h', - 'audio_coding/neteq4/mock/mock_dtmf_tone_generator.h', - 'audio_coding/neteq4/mock/mock_external_decoder_pcm16b.h', - 'audio_coding/neteq4/mock/mock_packet_buffer.h', - 'audio_coding/neteq4/mock/mock_payload_splitter.h', + 'audio_coding/neteq/audio_classifier_unittest.cc', + 'audio_coding/neteq/audio_multi_vector_unittest.cc', + 'audio_coding/neteq/audio_vector_unittest.cc', + 'audio_coding/neteq/background_noise_unittest.cc', + 'audio_coding/neteq/buffer_level_filter_unittest.cc', + 'audio_coding/neteq/comfort_noise_unittest.cc', + 'audio_coding/neteq/decision_logic_unittest.cc', + 'audio_coding/neteq/decoder_database_unittest.cc', + 'audio_coding/neteq/delay_manager_unittest.cc', + 'audio_coding/neteq/delay_peak_detector_unittest.cc', + 'audio_coding/neteq/dsp_helper_unittest.cc', + 'audio_coding/neteq/dtmf_buffer_unittest.cc', + 'audio_coding/neteq/dtmf_tone_generator_unittest.cc', + 'audio_coding/neteq/expand_unittest.cc', + 'audio_coding/neteq/merge_unittest.cc', + 'audio_coding/neteq/neteq_external_decoder_unittest.cc', + 'audio_coding/neteq/neteq_impl_unittest.cc', + 'audio_coding/neteq/neteq_stereo_unittest.cc', + 'audio_coding/neteq/neteq_unittest.cc', + 'audio_coding/neteq/normal_unittest.cc', + 'audio_coding/neteq/packet_buffer_unittest.cc', + 'audio_coding/neteq/payload_splitter_unittest.cc', + 'audio_coding/neteq/post_decode_vad_unittest.cc', + 'audio_coding/neteq/random_vector_unittest.cc', + 'audio_coding/neteq/sync_buffer_unittest.cc', + 'audio_coding/neteq/timestamp_scaler_unittest.cc', + 'audio_coding/neteq/time_stretch_unittest.cc', + 'audio_coding/neteq/mock/mock_audio_decoder.h', + 'audio_coding/neteq/mock/mock_audio_vector.h', + 'audio_coding/neteq/mock/mock_buffer_level_filter.h', + 'audio_coding/neteq/mock/mock_decoder_database.h', + 'audio_coding/neteq/mock/mock_delay_manager.h', + 'audio_coding/neteq/mock/mock_delay_peak_detector.h', + 'audio_coding/neteq/mock/mock_dtmf_buffer.h', + 'audio_coding/neteq/mock/mock_dtmf_tone_generator.h', + 'audio_coding/neteq/mock/mock_expand.h', + 'audio_coding/neteq/mock/mock_external_decoder_pcm16b.h', + 'audio_coding/neteq/mock/mock_packet_buffer.h', + 'audio_coding/neteq/mock/mock_payload_splitter.h', + 'audio_coding/neteq/tools/input_audio_file_unittest.cc', + 'audio_coding/neteq/tools/packet_unittest.cc', 'audio_processing/aec/system_delay_unittest.cc', 'audio_processing/aec/echo_cancellation_unittest.cc', - 'audio_processing/audio_processing_impl_unittest.cc', 'audio_processing/echo_cancellation_impl_unittest.cc', - 'audio_processing/test/audio_processing_unittest.cc', - 'audio_processing/test/test_utils.h', 'audio_processing/utility/delay_estimator_unittest.cc', 'audio_processing/utility/ring_buffer_unittest.cc', 'bitrate_controller/bitrate_controller_unittest.cc', @@ -184,6 +191,7 @@ 'desktop_capture/win/cursor_unittest_resources.rc', 'media_file/source/media_file_unittest.cc', 'module_common_types_unittest.cc', + 'pacing/bitrate_prober_unittest.cc', 'pacing/paced_sender_unittest.cc', 'remote_bitrate_estimator/bwe_simulations.cc', 'remote_bitrate_estimator/include/mock/mock_remote_bitrate_observer.h', @@ -192,7 +200,6 @@ 'remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc', 'remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h', 'remote_bitrate_estimator/remote_bitrate_estimators_test.cc', - 'remote_bitrate_estimator/rtp_to_ntp_unittest.cc', 'remote_bitrate_estimator/test/bwe_test_baselinefile.cc', 'remote_bitrate_estimator/test/bwe_test_baselinefile.h', 'remote_bitrate_estimator/test/bwe_test_fileutils.cc', @@ -212,17 +219,20 @@ 'rtp_rtcp/source/nack_rtx_unittest.cc', 'rtp_rtcp/source/producer_fec_unittest.cc', 'rtp_rtcp/source/receive_statistics_unittest.cc', + 'rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc', 'rtp_rtcp/source/rtcp_format_remb_unittest.cc', - 'rtp_rtcp/source/rtcp_sender_unittest.cc', + 'rtp_rtcp/source/rtcp_packet_unittest.cc', 'rtp_rtcp/source/rtcp_receiver_unittest.cc', + 'rtp_rtcp/source/rtcp_sender_unittest.cc', + 'rtp_rtcp/source/rtcp_utility_unittest.cc', 'rtp_rtcp/source/rtp_fec_unittest.cc', + 'rtp_rtcp/source/rtp_format_h264_unittest.cc', 'rtp_rtcp/source/rtp_format_vp8_unittest.cc', 'rtp_rtcp/source/rtp_format_vp8_test_helper.cc', 'rtp_rtcp/source/rtp_format_vp8_test_helper.h', 'rtp_rtcp/source/rtp_packet_history_unittest.cc', 'rtp_rtcp/source/rtp_payload_registry_unittest.cc', 'rtp_rtcp/source/rtp_rtcp_impl_unittest.cc', - 'rtp_rtcp/source/rtp_utility_unittest.cc', 'rtp_rtcp/source/rtp_header_extension_unittest.cc', 'rtp_rtcp/source/rtp_sender_unittest.cc', 'rtp_rtcp/source/vp8_partition_aggregator_unittest.cc', @@ -232,6 +242,7 @@ 'rtp_rtcp/test/testAPI/test_api_rtcp.cc', 'rtp_rtcp/test/testAPI/test_api_video.cc', 'utility/source/audio_frame_operations_unittest.cc', + 'utility/source/file_player_unittests.cc', 'video_coding/codecs/test/packet_manipulator_unittest.cc', 'video_coding/codecs/test/stats_unittest.cc', 'video_coding/codecs/test/videoprocessor_unittest.cc', @@ -240,6 +251,7 @@ 'video_coding/main/interface/mock/mock_vcm_callbacks.h', 'video_coding/main/source/decoding_state_unittest.cc', 'video_coding/main/source/jitter_buffer_unittest.cc', + 'video_coding/main/source/jitter_estimator_tests.cc', 'video_coding/main/source/media_optimization_unittest.cc', 'video_coding/main/source/receiver_unittest.cc', 'video_coding/main/source/session_info_unittest.cc', @@ -250,15 +262,11 @@ 'video_coding/main/source/qm_select_unittest.cc', 'video_coding/main/source/test/stream_generator.cc', 'video_coding/main/source/test/stream_generator.h', - 'video_coding/main/test/pcap_file_reader.cc', - 'video_coding/main/test/pcap_file_reader_unittest.cc', - 'video_coding/main/test/rtp_file_reader.cc', - 'video_coding/main/test/rtp_file_reader_unittest.cc', + 'video_coding/utility/quality_scaler_unittest.cc', 'video_processing/main/test/unit_test/brightness_detection_test.cc', 'video_processing/main/test/unit_test/color_enhancement_test.cc', 'video_processing/main/test/unit_test/content_metrics_test.cc', 'video_processing/main/test/unit_test/deflickering_test.cc', - 'video_processing/main/test/unit_test/denoising_test.cc', 'video_processing/main/test/unit_test/video_processing_unittest.cc', 'video_processing/main/test/unit_test/video_processing_unittest.h', ], @@ -291,15 +299,21 @@ }], ['enable_protobuf==1', { 'defines': [ 'WEBRTC_AUDIOPROC_DEBUG_DUMP' ], + 'dependencies': [ + 'audioproc_unittest_proto', + ], + 'sources': [ + 'audio_processing/audio_processing_impl_unittest.cc', + 'audio_processing/test/audio_processing_unittest.cc', + 'audio_processing/test/test_utils.h', + ], }], ['build_libvpx==1', { 'dependencies': [ '<(DEPTH)/third_party/libvpx/libvpx.gyp:libvpx', ], }], - # TODO(henrike): remove build_with_chromium==1 when the bots are - # using Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['OS=="android"', { 'dependencies': [ '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code', ], @@ -323,6 +337,7 @@ '<(DEPTH)/testing/gtest.gyp:gtest', '<(webrtc_root)/common_video/common_video.gyp:common_video', '<(webrtc_root)/modules/video_coding/codecs/vp8/vp8.gyp:webrtc_vp8', + '<(webrtc_root)/modules/video_coding/codecs/vp9/vp9.gyp:webrtc_vp9', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', '<(webrtc_root)/test/metrics.gyp:metrics', '<(webrtc_root)/test/test.gyp:test_support', @@ -332,20 +347,20 @@ '<@(audio_coding_defines)', ], 'sources': [ - 'audio_coding/main/test/ACMTest.cc', 'audio_coding/main/test/APITest.cc', 'audio_coding/main/test/Channel.cc', 'audio_coding/main/test/dual_stream_unittest.cc', 'audio_coding/main/test/EncodeDecodeTest.cc', 'audio_coding/main/test/iSACTest.cc', 'audio_coding/main/test/opus_test.cc', + 'audio_coding/main/test/PacketLossTest.cc', 'audio_coding/main/test/PCMFile.cc', 'audio_coding/main/test/RTPFile.cc', 'audio_coding/main/test/SpatialAudio.cc', 'audio_coding/main/test/TestAllCodecs.cc', 'audio_coding/main/test/target_delay_unittest.cc', 'audio_coding/main/test/Tester.cc', - 'audio_coding/main/test/TestFEC.cc', + 'audio_coding/main/test/TestRedFec.cc', 'audio_coding/main/test/TestStereo.cc', 'audio_coding/main/test/TestVADDTX.cc', 'audio_coding/main/test/TimedTrace.cc', @@ -357,9 +372,7 @@ 'video_coding/codecs/vp8/test/vp8_impl_unittest.cc', ], 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are - # using Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['OS=="android"', { 'dependencies': [ '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code', ], @@ -368,9 +381,7 @@ }, ], 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are using - # Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['OS=="android"', { 'targets': [ { 'target_name': 'modules_unittests_apk_target', @@ -398,7 +409,6 @@ ], 'includes': [ '../build/isolate.gypi', - 'modules_tests.isolate', ], 'sources': [ 'modules_tests.isolate', @@ -412,7 +422,6 @@ ], 'includes': [ '../build/isolate.gypi', - 'modules_unittests.isolate', ], 'sources': [ 'modules_unittests.isolate', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/modules_tests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/modules_tests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/modules_tests.isolate 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/modules_tests.isolate 2015-02-03 14:33:36.000000000 +0000 @@ -8,33 +8,28 @@ { 'conditions': [ ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. 'variables': { - 'isolate_dependency_untracked': [ - '../../../data/', - '../../../resources/', + 'files': [ + '<(DEPTH)/data/', + '<(DEPTH)/resources/', ], }, }], ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../../testing/test_env.py', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/modules_tests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_tracked': [ - '../../DEPS', - '../../resources/audio_coding/testfile32kHz.pcm', - '../../resources/audio_coding/teststereo32kHz.pcm', - '../../resources/foreman_cif.yuv', - '../../resources/paris_qcif.yuv', - '../../testing/test_env.py', + 'files': [ + '<(DEPTH)/DEPS', + '<(DEPTH)/resources/audio_coding/testfile32kHz.pcm', + '<(DEPTH)/resources/audio_coding/teststereo32kHz.pcm', + '<(DEPTH)/resources/foreman_cif.yuv', + '<(DEPTH)/resources/paris_qcif.yuv', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/modules_tests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '../../tools/swarming_client/', - ], }, }], ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/modules_unittests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/modules_unittests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/modules_unittests.isolate 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/modules_unittests.isolate 2015-02-03 14:33:36.000000000 +0000 @@ -8,89 +8,97 @@ { 'conditions': [ ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. 'variables': { - 'isolate_dependency_untracked': [ - '../../../data/', - '../../../resources/', + 'files': [ + '<(DEPTH)/data/', + '<(DEPTH)/resources/', ], }, }], ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../../testing/test_env.py', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/modules_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_tracked': [ - '../../DEPS', - '../../data/audio_processing/output_data_float.pb', - '../../data/voice_engine/audio_tiny48.wav', - '../../resources/att-downlink.rx', - '../../resources/att-uplink.rx', - '../../resources/audio_coding/neteq4_network_stats.dat', - '../../resources/audio_coding/neteq4_rtcp_stats.dat', - '../../resources/audio_coding/neteq4_universal_ref.pcm', - '../../resources/audio_coding/neteq_network_stats.dat', - '../../resources/audio_coding/neteq_rtcp_stats.dat', - '../../resources/audio_coding/neteq_universal_new.rtp', - '../../resources/audio_coding/neteq_universal_ref.pcm', - '../../resources/audio_coding/testfile32kHz.pcm', - '../../resources/deflicker_before_cif_short.yuv', - '../../resources/far16_stereo.pcm', - '../../resources/far32_stereo.pcm', - '../../resources/far8_stereo.pcm', - '../../resources/foremanColorEnhanced_cif_short.yuv', - '../../resources/foreman_cif.yuv', - '../../resources/foreman_cif_short.yuv', - '../../resources/near16_stereo.pcm', - '../../resources/near32_stereo.pcm', - '../../resources/near8_stereo.pcm', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke1_0_AST.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke1_0_TOF.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke1_1_AST.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke1_1_TOF.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke2_0_AST.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke2_0_TOF.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke2_1_AST.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke2_1_TOF.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingDelay1_0_AST.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingDelay1_0_TOF.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingLoss1_0_AST.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingLoss1_0_TOF.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_Multi1_1_AST.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_Multi1_1_TOF.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyChoke_0_AST.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyChoke_0_TOF.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyChoke_1_AST.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyChoke_1_TOF.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyDelay_0_AST.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyDelay_0_TOF.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyLoss_0_AST.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyLoss_0_TOF.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_UnlimitedSpeed_0_AST.bin', - '../../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_UnlimitedSpeed_0_TOF.bin', - '../../resources/sprint-downlink.rx', - '../../resources/sprint-uplink.rx', - '../../resources/synthetic-trace.rx', - '../../resources/tmobile-downlink.rx', - '../../resources/tmobile-uplink.rx', - '../../resources/verizon3g-downlink.rx', - '../../resources/verizon3g-uplink.rx', - '../../resources/verizon4g-downlink.rx', - '../../resources/verizon4g-uplink.rx', - '../../resources/video_coding/frame-ethernet-ii.pcap', - '../../resources/video_coding/frame-loopback.pcap', - '../../resources/video_coding/pltype103.rtp', - '../../resources/video_coding/ssrcs-2.pcap', - '../../resources/video_coding/ssrcs-3.pcap', - '../../testing/test_env.py', + 'files': [ + '<(DEPTH)/DEPS', + '<(DEPTH)/data/audio_processing/output_data_float.pb', + '<(DEPTH)/data/voice_engine/audio_tiny48.wav', + '<(DEPTH)/resources/att-downlink.rx', + '<(DEPTH)/resources/att-uplink.rx', + '<(DEPTH)/resources/audio_coding/neteq4_network_stats.dat', + '<(DEPTH)/resources/audio_coding/neteq4_rtcp_stats.dat', + '<(DEPTH)/resources/audio_coding/neteq4_universal_ref.pcm', + '<(DEPTH)/resources/audio_coding/neteq4_universal_ref_win_32.pcm', + '<(DEPTH)/resources/audio_coding/neteq4_universal_ref_win_64.pcm', + '<(DEPTH)/resources/audio_coding/neteq_network_stats.dat', + '<(DEPTH)/resources/audio_coding/neteq_rtcp_stats.dat', + '<(DEPTH)/resources/audio_coding/neteq_universal_new.rtp', + '<(DEPTH)/resources/audio_coding/neteq_universal_ref.pcm', + '<(DEPTH)/resources/audio_coding/testfile32kHz.pcm', + '<(DEPTH)/resources/deflicker_before_cif_short.yuv', + '<(DEPTH)/resources/far16_stereo.pcm', + '<(DEPTH)/resources/far32_stereo.pcm', + '<(DEPTH)/resources/far44_stereo.pcm', + '<(DEPTH)/resources/far48_stereo.pcm', + '<(DEPTH)/resources/far8_stereo.pcm', + '<(DEPTH)/resources/foremanColorEnhanced_cif_short.yuv', + '<(DEPTH)/resources/foreman_cif.yuv', + '<(DEPTH)/resources/foreman_cif_short.yuv', + '<(DEPTH)/resources/near16_stereo.pcm', + '<(DEPTH)/resources/near32_stereo.pcm', + '<(DEPTH)/resources/near44_stereo.pcm', + '<(DEPTH)/resources/near48_stereo.pcm', + '<(DEPTH)/resources/near8_stereo.pcm', + '<(DEPTH)/resources/ref03.aecdump', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke1_0_AST.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke1_0_TOF.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke1_1_AST.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke1_1_TOF.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke2_0_AST.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke2_0_TOF.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke2_1_AST.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke2_1_TOF.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingDelay1_0_AST.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingDelay1_0_TOF.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingLoss1_0_AST.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingLoss1_0_TOF.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_Multi1_1_AST.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_Multi1_1_TOF.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyChoke_0_AST.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyChoke_0_TOF.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyChoke_1_AST.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyChoke_1_TOF.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyDelay_0_AST.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyDelay_0_TOF.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyLoss_0_AST.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyLoss_0_TOF.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_UnlimitedSpeed_0_AST.bin', + '<(DEPTH)/resources/remote_bitrate_estimator/VideoSendersTest_BweTest_UnlimitedSpeed_0_TOF.bin', + '<(DEPTH)/resources/short_mixed_mono_48.dat', + '<(DEPTH)/resources/short_mixed_mono_48.pcm', + '<(DEPTH)/resources/short_mixed_stereo_48.dat', + '<(DEPTH)/resources/short_mixed_stereo_48.pcm', + '<(DEPTH)/resources/sprint-downlink.rx', + '<(DEPTH)/resources/sprint-uplink.rx', + '<(DEPTH)/resources/synthetic-trace.rx', + '<(DEPTH)/resources/tmobile-downlink.rx', + '<(DEPTH)/resources/tmobile-uplink.rx', + '<(DEPTH)/resources/utility/encapsulated_pcm16b_8khz.wav', + '<(DEPTH)/resources/utility/encapsulated_pcmu_8khz.wav', + '<(DEPTH)/resources/verizon3g-downlink.rx', + '<(DEPTH)/resources/verizon3g-uplink.rx', + '<(DEPTH)/resources/verizon4g-downlink.rx', + '<(DEPTH)/resources/verizon4g-uplink.rx', + '<(DEPTH)/resources/video_coding/frame-ethernet-ii.pcap', + '<(DEPTH)/resources/video_coding/frame-loopback.pcap', + '<(DEPTH)/resources/video_coding/pltype103.rtp', + '<(DEPTH)/resources/video_coding/ssrcs-2.pcap', + '<(DEPTH)/resources/video_coding/ssrcs-3.pcap', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/modules_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '../../tools/swarming_client/', - ], }, }], ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/OWNERS 2015-02-03 14:33:34.000000000 +0000 @@ -0,0 +1,6 @@ +per-file *.isolate=kjellander@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/bitrate_prober.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/bitrate_prober.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/bitrate_prober.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/bitrate_prober.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/pacing/bitrate_prober.h" + +#include +#include +#include + +#include "webrtc/system_wrappers/interface/logging.h" + +namespace webrtc { + +namespace { +int ComputeDeltaFromBitrate(size_t packet_size, int bitrate_bps) { + assert(bitrate_bps > 0); + // Compute the time delta needed to send packet_size bytes at bitrate_bps + // bps. Result is in milliseconds. + return static_cast(1000ll * static_cast(packet_size) * 8ll / + bitrate_bps); +} +} // namespace + +BitrateProber::BitrateProber() + : probing_state_(kDisabled), + packet_size_last_send_(0), + time_last_send_ms_(-1) { +} + +void BitrateProber::SetEnabled(bool enable) { + if (enable) { + if (probing_state_ == kDisabled) { + probing_state_ = kAllowedToProbe; + LOG(LS_INFO) << "Initial bandwidth probing enabled"; + } + } else { + probing_state_ = kDisabled; + LOG(LS_INFO) << "Initial bandwidth probing disabled"; + } +} + +bool BitrateProber::IsProbing() const { + return probing_state_ == kProbing; +} + +void BitrateProber::MaybeInitializeProbe(int bitrate_bps) { + if (probing_state_ != kAllowedToProbe) + return; + probe_bitrates_.clear(); + // Max number of packets used for probing. + const int kMaxNumProbes = 2; + const int kPacketsPerProbe = 5; + const float kProbeBitrateMultipliers[kMaxNumProbes] = {3, 6}; + int bitrates_bps[kMaxNumProbes]; + std::stringstream bitrate_log; + bitrate_log << "Start probing for bandwidth, bitrates:"; + for (int i = 0; i < kMaxNumProbes; ++i) { + bitrates_bps[i] = kProbeBitrateMultipliers[i] * bitrate_bps; + bitrate_log << " " << bitrates_bps[i]; + // We need one extra to get 5 deltas for the first probe. + if (i == 0) + probe_bitrates_.push_back(bitrates_bps[i]); + for (int j = 0; j < kPacketsPerProbe; ++j) + probe_bitrates_.push_back(bitrates_bps[i]); + } + bitrate_log << ", num packets: " << probe_bitrates_.size(); + LOG(LS_INFO) << bitrate_log.str().c_str(); + probing_state_ = kProbing; +} + +int BitrateProber::TimeUntilNextProbe(int64_t now_ms) { + if (probing_state_ != kDisabled && probe_bitrates_.empty()) { + probing_state_ = kWait; + } + if (probe_bitrates_.empty()) { + // No probe started, or waiting for next probe. + return std::numeric_limits::max(); + } + int64_t elapsed_time_ms = now_ms - time_last_send_ms_; + // We will send the first probe packet immediately if no packet has been + // sent before. + int time_until_probe_ms = 0; + if (packet_size_last_send_ > 0 && probing_state_ == kProbing) { + int next_delta_ms = ComputeDeltaFromBitrate(packet_size_last_send_, + probe_bitrates_.front()); + time_until_probe_ms = next_delta_ms - elapsed_time_ms; + // There is no point in trying to probe with less than 1 ms between packets + // as it essentially means trying to probe at infinite bandwidth. + const int kMinProbeDeltaMs = 1; + // If we have waited more than 3 ms for a new packet to probe with we will + // consider this probing session over. + const int kMaxProbeDelayMs = 3; + if (next_delta_ms < kMinProbeDeltaMs || + time_until_probe_ms < -kMaxProbeDelayMs) { + // We currently disable probing after the first probe, as we only want + // to probe at the beginning of a connection. We should set this to + // kWait if we later want to probe periodically. + probing_state_ = kWait; + LOG(LS_INFO) << "Next delta too small, stop probing."; + time_until_probe_ms = 0; + } + } + return time_until_probe_ms; +} + +void BitrateProber::PacketSent(int64_t now_ms, size_t packet_size) { + assert(packet_size > 0); + packet_size_last_send_ = packet_size; + time_last_send_ms_ = now_ms; + if (probing_state_ != kProbing) + return; + if (!probe_bitrates_.empty()) + probe_bitrates_.pop_front(); +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/bitrate_prober.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/bitrate_prober.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/bitrate_prober.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/bitrate_prober.h 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_PACING_BITRATE_PROBER_H_ +#define WEBRTC_MODULES_PACING_BITRATE_PROBER_H_ + +#include +#include + +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Note that this class isn't thread-safe by itself and therefore relies +// on being protected by the caller. +class BitrateProber { + public: + BitrateProber(); + + void SetEnabled(bool enable); + + // Returns true if the prober is in a probing session, i.e., it currently + // wants packets to be sent out according to the time returned by + // TimeUntilNextProbe(). + bool IsProbing() const; + + // Initializes a new probing session if the prober is allowed to probe. + void MaybeInitializeProbe(int bitrate_bps); + + // Returns the number of milliseconds until the next packet should be sent to + // get accurate probing. + int TimeUntilNextProbe(int64_t now_ms); + + // Called to report to the prober that a packet has been sent, which helps the + // prober know when to move to the next packet in a probe. + void PacketSent(int64_t now_ms, size_t packet_size); + + private: + enum ProbingState { kDisabled, kAllowedToProbe, kProbing, kWait }; + + ProbingState probing_state_; + // Probe bitrate per packet. These are used to compute the delta relative to + // the previous probe packet based on the size and time when that packet was + // sent. + std::list probe_bitrates_; + size_t packet_size_last_send_; + int64_t time_last_send_ms_; +}; +} // namespace webrtc +#endif // WEBRTC_MODULES_PACING_BITRATE_PROBER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/bitrate_prober_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/bitrate_prober_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/bitrate_prober_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/bitrate_prober_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/pacing/bitrate_prober.h" + +namespace webrtc { + +TEST(BitrateProberTest, VerifyStatesAndTimeBetweenProbes) { + BitrateProber prober; + EXPECT_FALSE(prober.IsProbing()); + int64_t now_ms = 0; + EXPECT_EQ(std::numeric_limits::max(), prober.TimeUntilNextProbe(now_ms)); + + prober.SetEnabled(true); + EXPECT_FALSE(prober.IsProbing()); + + prober.MaybeInitializeProbe(300000); + EXPECT_TRUE(prober.IsProbing()); + + EXPECT_EQ(0, prober.TimeUntilNextProbe(now_ms)); + prober.PacketSent(now_ms, 1000); + + for (int i = 0; i < 5; ++i) { + EXPECT_EQ(8, prober.TimeUntilNextProbe(now_ms)); + now_ms += 4; + EXPECT_EQ(4, prober.TimeUntilNextProbe(now_ms)); + now_ms += 4; + EXPECT_EQ(0, prober.TimeUntilNextProbe(now_ms)); + prober.PacketSent(now_ms, 1000); + } + for (int i = 0; i < 5; ++i) { + EXPECT_EQ(4, prober.TimeUntilNextProbe(now_ms)); + now_ms += 4; + EXPECT_EQ(0, prober.TimeUntilNextProbe(now_ms)); + prober.PacketSent(now_ms, 1000); + } + + EXPECT_EQ(std::numeric_limits::max(), prober.TimeUntilNextProbe(now_ms)); + EXPECT_FALSE(prober.IsProbing()); +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/BUILD.gn 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,27 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +source_set("pacing") { + sources = [ + "include/paced_sender.h", + "bitrate_prober.cc", + "bitrate_prober.h", + "paced_sender.cc", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ "../../system_wrappers" ] +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/include/mock/mock_paced_sender.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/include/mock/mock_paced_sender.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/include/mock/mock_paced_sender.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/include/mock/mock_paced_sender.h 2015-02-03 14:33:36.000000000 +0000 @@ -16,12 +16,13 @@ #include #include "webrtc/modules/pacing/include/paced_sender.h" +#include "webrtc/system_wrappers/interface/clock.h" namespace webrtc { class MockPacedSender : public PacedSender { public: - MockPacedSender() : PacedSender(NULL, 0, 0) {} + MockPacedSender() : PacedSender(Clock::GetRealTimeClock(), NULL, 0, 0, 0) {} MOCK_METHOD6(SendPacket, bool(Priority priority, uint32_t ssrc, uint16_t sequence_number, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/include/paced_sender.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/include/paced_sender.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/include/paced_sender.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/include/paced_sender.h 2015-02-03 14:33:36.000000000 +0000 @@ -8,23 +8,26 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_PACED_SENDER_H_ -#define WEBRTC_MODULES_PACED_SENDER_H_ +#ifndef WEBRTC_MODULES_PACING_INCLUDE_PACED_SENDER_H_ +#define WEBRTC_MODULES_PACING_INCLUDE_PACED_SENDER_H_ #include #include +#include "webrtc/base/thread_annotations.h" #include "webrtc/modules/interface/module.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/system_wrappers/interface/tick_util.h" #include "webrtc/typedefs.h" namespace webrtc { +class BitrateProber; +class Clock; class CriticalSectionWrapper; + namespace paced_sender { class IntervalBudget; struct Packet; -class PacketList; +class PacketQueue; } // namespace paced_sender class PacedSender : public Module { @@ -48,15 +51,28 @@ int64_t capture_time_ms, bool retransmission) = 0; // Called when it's a good time to send a padding data. + // Returns the number of bytes sent. virtual int TimeToSendPadding(int bytes) = 0; + protected: virtual ~Callback() {} }; static const int kDefaultMaxQueueLengthMs = 2000; - - PacedSender(Callback* callback, int target_bitrate_kbps, - float pace_multiplier); + // Pace in kbits/s until we receive first estimate. + static const int kDefaultInitialPaceKbps = 2000; + // Pacing-rate relative to our target send rate. + // Multiplicative factor that is applied to the target bitrate to calculate + // the number of bytes that can be transmitted per interval. + // Increasing this factor will result in lower delays in cases of bitrate + // overshoots from the encoder. + static const float kDefaultPaceMultiplier; + + PacedSender(Clock* clock, + Callback* callback, + int bitrate_kbps, + int max_bitrate_kbps, + int min_bitrate_kbps); virtual ~PacedSender(); @@ -71,13 +87,14 @@ // Resume sending packets. void Resume(); - // Set the pacing target bitrate and the bitrate up to which we are allowed to - // pad. We will send padding packets to increase the total bitrate until we - // reach |pad_up_to_bitrate_kbps|. If the media bitrate is above - // |pad_up_to_bitrate_kbps| no padding will be sent. - void UpdateBitrate(int target_bitrate_kbps, - int max_padding_bitrate_kbps, - int pad_up_to_bitrate_kbps); + // Set target bitrates for the pacer. + // We will pace out bursts of packets at a bitrate of |max_bitrate_kbps|. + // |bitrate_kbps| is our estimate of what we are allowed to send on average. + // Padding packets will be utilized to reach |min_bitrate| unless enough media + // packets are available. + void UpdateBitrate(int bitrate_kbps, + int max_bitrate_kbps, + int min_bitrate_kbps); // Returns true if we send the packet now, else it will add the packet // information to the queue and call TimeToSendPacket when it's time to send. @@ -88,13 +105,15 @@ int bytes, bool retransmission); - // Sets the max length of the pacer queue in milliseconds. - // A negative queue size is interpreted as infinite. - virtual void set_max_queue_length_ms(int max_queue_length_ms); - // Returns the time since the oldest queued packet was enqueued. virtual int QueueInMs() const; + virtual size_t QueueSizePackets() const; + + // Returns the number of milliseconds it will take to send the current + // packets in the queue, given the current size and bitrate, ignoring prio. + virtual int ExpectedQueueTimeMs() const; + // Returns the number of milliseconds until the module want a worker thread // to call Process. virtual int32_t TimeUntilNextProcess() OVERRIDE; @@ -102,47 +121,40 @@ // Process any pending packets in the queue(s). virtual int32_t Process() OVERRIDE; - private: - // Return true if next packet in line should be transmitted. - // Return packet list that contains the next packet. - bool ShouldSendNextPacket(paced_sender::PacketList** packet_list); - - // Local helper function to GetNextPacket. - paced_sender::Packet GetNextPacketFromList(paced_sender::PacketList* packets); - - bool SendPacketFromList(paced_sender::PacketList* packet_list); + protected: + virtual bool ProbingExperimentIsEnabled() const; + private: // Updates the number of bytes that can be sent for the next time interval. - void UpdateBytesPerInterval(uint32_t delta_time_in_ms); + void UpdateBytesPerInterval(uint32_t delta_time_in_ms) + EXCLUSIVE_LOCKS_REQUIRED(critsect_); + + bool SendPacket(const paced_sender::Packet& packet) + EXCLUSIVE_LOCKS_REQUIRED(critsect_); + void SendPadding(int padding_needed) EXCLUSIVE_LOCKS_REQUIRED(critsect_); - // Updates the buffers with the number of bytes that we sent. - void UpdateMediaBytesSent(int num_bytes); + Clock* const clock_; + Callback* const callback_; - Callback* callback_; - const float pace_multiplier_; - bool enabled_; - bool paused_; - int max_queue_length_ms_; scoped_ptr critsect_; + bool enabled_ GUARDED_BY(critsect_); + bool paused_ GUARDED_BY(critsect_); // This is the media budget, keeping track of how many bits of media // we can pace out during the current interval. - scoped_ptr media_budget_; + scoped_ptr media_budget_ GUARDED_BY(critsect_); // This is the padding budget, keeping track of how many bits of padding we're - // allowed to send out during the current interval. - scoped_ptr padding_budget_; - // Media and padding share this budget, therefore no padding will be sent if - // media uses all of this budget. This is used to avoid padding above a given - // bitrate. - scoped_ptr pad_up_to_bitrate_budget_; - - TickTime time_last_update_; - TickTime time_last_send_; - int64_t capture_time_ms_last_queued_; - int64_t capture_time_ms_last_sent_; - - scoped_ptr high_priority_packets_; - scoped_ptr normal_priority_packets_; - scoped_ptr low_priority_packets_; + // allowed to send out during the current interval. This budget will be + // utilized when there's no media to send. + scoped_ptr padding_budget_ + GUARDED_BY(critsect_); + + scoped_ptr prober_ GUARDED_BY(critsect_); + int bitrate_bps_ GUARDED_BY(critsect_); + + int64_t time_last_update_us_ GUARDED_BY(critsect_); + + scoped_ptr packets_ GUARDED_BY(critsect_); + uint64_t packet_counter_ GUARDED_BY(critsect_); }; } // namespace webrtc -#endif // WEBRTC_MODULES_PACED_SENDER_H_ +#endif // WEBRTC_MODULES_PACING_INCLUDE_PACED_SENDER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -1,4 +1,10 @@ -pwestin@webrtc.org stefan@webrtc.org mflodman@webrtc.org asapersson@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/paced_sender.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/paced_sender.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/paced_sender.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/paced_sender.cc 2015-02-03 14:33:36.000000000 +0000 @@ -12,8 +12,16 @@ #include +#include +#include +#include + #include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/modules/pacing/bitrate_prober.h" +#include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/field_trial.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/trace_event.h" namespace { @@ -24,66 +32,140 @@ // time. const int kMaxIntervalTimeMs = 30; -// Max time that the first packet in the queue can sit in the queue if no -// packets are sent, regardless of buffer state. In practice only in effect at -// low bitrates (less than 320 kbits/s). -const int kMaxQueueTimeWithoutSendingMs = 30; - } // namespace namespace webrtc { - namespace paced_sender { struct Packet { - Packet(uint32_t ssrc, uint16_t seq_number, int64_t capture_time_ms, - int64_t enqueue_time_ms, int length_in_bytes, bool retransmission) - : ssrc_(ssrc), - sequence_number_(seq_number), - capture_time_ms_(capture_time_ms), - enqueue_time_ms_(enqueue_time_ms), - bytes_(length_in_bytes), - retransmission_(retransmission) { - } - uint32_t ssrc_; - uint16_t sequence_number_; - int64_t capture_time_ms_; - int64_t enqueue_time_ms_; - int bytes_; - bool retransmission_; + Packet(PacedSender::Priority priority, + uint32_t ssrc, + uint16_t seq_number, + int64_t capture_time_ms, + int64_t enqueue_time_ms, + int length_in_bytes, + bool retransmission, + uint64_t enqueue_order) + : priority(priority), + ssrc(ssrc), + sequence_number(seq_number), + capture_time_ms(capture_time_ms), + enqueue_time_ms(enqueue_time_ms), + bytes(length_in_bytes), + retransmission(retransmission), + enqueue_order(enqueue_order) {} + + PacedSender::Priority priority; + uint32_t ssrc; + uint16_t sequence_number; + int64_t capture_time_ms; + int64_t enqueue_time_ms; + int bytes; + bool retransmission; + uint64_t enqueue_order; + std::list::iterator this_it; +}; + +// Used by priority queue to sort packets. +struct Comparator { + bool operator()(const Packet* first, const Packet* second) { + // Highest prio = 0. + if (first->priority != second->priority) + return first->priority > second->priority; + + // Retransmissions go first. + if (second->retransmission && !first->retransmission) + return true; + + // Older frames have higher prio. + if (first->capture_time_ms != second->capture_time_ms) + return first->capture_time_ms > second->capture_time_ms; + + return first->enqueue_order > second->enqueue_order; + } }; -// STL list style class which prevents duplicates in the list. -class PacketList { +// Class encapsulating a priority queue with some extensions. +class PacketQueue { public: - PacketList() {}; + PacketQueue() : bytes_(0) {} + virtual ~PacketQueue() {} + + void Push(const Packet& packet) { + if (!AddToDupeSet(packet)) + return; - bool empty() const { - return packet_list_.empty(); + // Store packet in list, use pointers in priority queue for cheaper moves. + // Packets have a handle to its own iterator in the list, for easy removal + // when popping from queue. + packet_list_.push_front(packet); + std::list::iterator it = packet_list_.begin(); + it->this_it = it; // Handle for direct removal from list. + prio_queue_.push(&(*it)); // Pointer into list. + bytes_ += packet.bytes; } - Packet front() const { - return packet_list_.front(); + const Packet& BeginPop() { + const Packet& packet = *prio_queue_.top(); + prio_queue_.pop(); + return packet; } - void pop_front() { - Packet& packet = packet_list_.front(); - uint16_t sequence_number = packet.sequence_number_; - packet_list_.pop_front(); - sequence_number_set_.erase(sequence_number); + void CancelPop(const Packet& packet) { prio_queue_.push(&(*packet.this_it)); } + + void FinalizePop(const Packet& packet) { + RemoveFromDupeSet(packet); + bytes_ -= packet.bytes; + packet_list_.erase(packet.this_it); } - void push_back(const Packet& packet) { - if (sequence_number_set_.find(packet.sequence_number_) == - sequence_number_set_.end()) { - // Don't insert duplicates. - packet_list_.push_back(packet); - sequence_number_set_.insert(packet.sequence_number_); - } + bool Empty() const { return prio_queue_.empty(); } + + size_t SizeInPackets() const { return prio_queue_.size(); } + + uint32_t SizeInBytes() const { return bytes_; } + + int64_t OldestEnqueueTime() const { + std::list::const_reverse_iterator it = packet_list_.rbegin(); + if (it == packet_list_.rend()) + return 0; + return it->enqueue_time_ms; } private: + // Try to add a packet to the set of ssrc/seqno identifiers currently in the + // queue. Return true if inserted, false if this is a duplicate. + bool AddToDupeSet(const Packet& packet) { + SsrcSeqNoMap::iterator it = dupe_map_.find(packet.ssrc); + if (it == dupe_map_.end()) { + // First for this ssrc, just insert. + dupe_map_[packet.ssrc].insert(packet.sequence_number); + return true; + } + + // Insert returns a pair, where second is a bool set to true if new element. + return it->second.insert(packet.sequence_number).second; + } + + void RemoveFromDupeSet(const Packet& packet) { + SsrcSeqNoMap::iterator it = dupe_map_.find(packet.ssrc); + assert(it != dupe_map_.end()); + it->second.erase(packet.sequence_number); + if (it->second.empty()) { + dupe_map_.erase(it); + } + } + + // List of packets, in the order the were enqueued. Since dequeueing may + // occur out of order, use list instead of vector. std::list packet_list_; - std::set sequence_number_set_; + // Priority queue of the packets, sorted according to Comparator. + // Use pointers into list, to avoid moving whole struct within heap. + std::priority_queue, Comparator> prio_queue_; + // Total number of bytes in the queue. + uint64_t bytes_; + // Map >, for checking duplicates. + typedef std::map > SsrcSeqNoMap; + SsrcSeqNoMap dupe_map_; }; class IntervalBudget { @@ -114,36 +196,37 @@ int bytes_remaining() const { return bytes_remaining_; } + int target_rate_kbps() const { return target_rate_kbps_; } + private: int target_rate_kbps_; int bytes_remaining_; }; } // namespace paced_sender -PacedSender::PacedSender(Callback* callback, int target_bitrate_kbps, - float pace_multiplier) - : callback_(callback), - pace_multiplier_(pace_multiplier), - enabled_(false), - paused_(false), - max_queue_length_ms_(kDefaultMaxQueueLengthMs), +const float PacedSender::kDefaultPaceMultiplier = 2.5f; + +PacedSender::PacedSender(Clock* clock, + Callback* callback, + int bitrate_kbps, + int max_bitrate_kbps, + int min_bitrate_kbps) + : clock_(clock), + callback_(callback), critsect_(CriticalSectionWrapper::CreateCriticalSection()), - media_budget_(new paced_sender::IntervalBudget( - pace_multiplier_ * target_bitrate_kbps)), - padding_budget_(new paced_sender::IntervalBudget(0)), - // No padding until UpdateBitrate is called. - pad_up_to_bitrate_budget_(new paced_sender::IntervalBudget(0)), - time_last_update_(TickTime::Now()), - capture_time_ms_last_queued_(0), - capture_time_ms_last_sent_(0), - high_priority_packets_(new paced_sender::PacketList), - normal_priority_packets_(new paced_sender::PacketList), - low_priority_packets_(new paced_sender::PacketList) { + enabled_(true), + paused_(false), + media_budget_(new paced_sender::IntervalBudget(max_bitrate_kbps)), + padding_budget_(new paced_sender::IntervalBudget(min_bitrate_kbps)), + prober_(new BitrateProber()), + bitrate_bps_(1000 * bitrate_kbps), + time_last_update_us_(clock->TimeInMicroseconds()), + packets_(new paced_sender::PacketQueue()), + packet_counter_(0) { UpdateBytesPerInterval(kMinPacketLimitMs); } -PacedSender::~PacedSender() { -} +PacedSender::~PacedSender() {} void PacedSender::Pause() { CriticalSectionScoped cs(critsect_.get()); @@ -165,13 +248,13 @@ return enabled_; } -void PacedSender::UpdateBitrate(int target_bitrate_kbps, - int max_padding_bitrate_kbps, - int pad_up_to_bitrate_kbps) { - CriticalSectionScoped cs(critsect_.get()); - media_budget_->set_target_rate_kbps(pace_multiplier_ * target_bitrate_kbps); - padding_budget_->set_target_rate_kbps(max_padding_bitrate_kbps); - pad_up_to_bitrate_budget_->set_target_rate_kbps(pad_up_to_bitrate_kbps); +void PacedSender::UpdateBitrate(int bitrate_kbps, + int max_bitrate_kbps, + int min_bitrate_kbps) { + CriticalSectionScoped cs(critsect_.get()); + media_budget_->set_target_rate_kbps(max_bitrate_kbps); + padding_budget_->set_target_rate_kbps(min_bitrate_kbps); + bitrate_bps_ = 1000 * bitrate_kbps; } bool PacedSender::SendPacket(Priority priority, uint32_t ssrc, @@ -182,69 +265,51 @@ if (!enabled_) { return true; // We can send now. } + // Enable probing if the probing experiment is enabled. + if (!prober_->IsProbing() && ProbingExperimentIsEnabled()) { + prober_->SetEnabled(true); + } + prober_->MaybeInitializeProbe(bitrate_bps_); + if (capture_time_ms < 0) { - capture_time_ms = TickTime::MillisecondTimestamp(); + capture_time_ms = clock_->TimeInMilliseconds(); } - if (priority != kHighPriority && - capture_time_ms > capture_time_ms_last_queued_) { - capture_time_ms_last_queued_ = capture_time_ms; - TRACE_EVENT_ASYNC_BEGIN1("webrtc_rtp", "PacedSend", capture_time_ms, - "capture_time_ms", capture_time_ms); - } - paced_sender::PacketList* packet_list = NULL; - switch (priority) { - case kHighPriority: - packet_list = high_priority_packets_.get(); - break; - case kNormalPriority: - packet_list = normal_priority_packets_.get(); - break; - case kLowPriority: - packet_list = low_priority_packets_.get(); - break; - } - packet_list->push_back(paced_sender::Packet(ssrc, - sequence_number, - capture_time_ms, - TickTime::MillisecondTimestamp(), - bytes, - retransmission)); + + packets_->Push(paced_sender::Packet( + priority, ssrc, sequence_number, capture_time_ms, + clock_->TimeInMilliseconds(), bytes, retransmission, packet_counter_++)); return false; } -void PacedSender::set_max_queue_length_ms(int max_queue_length_ms) { +int PacedSender::ExpectedQueueTimeMs() const { + CriticalSectionScoped cs(critsect_.get()); + int target_rate = media_budget_->target_rate_kbps(); + assert(target_rate > 0); + return packets_->SizeInBytes() * 8 / target_rate; +} + +size_t PacedSender::QueueSizePackets() const { CriticalSectionScoped cs(critsect_.get()); - max_queue_length_ms_ = max_queue_length_ms; + return packets_->SizeInPackets(); } int PacedSender::QueueInMs() const { CriticalSectionScoped cs(critsect_.get()); - int64_t now_ms = TickTime::MillisecondTimestamp(); - int64_t oldest_packet_enqueue_time = now_ms; - if (!high_priority_packets_->empty()) { - oldest_packet_enqueue_time = std::min( - oldest_packet_enqueue_time, - high_priority_packets_->front().enqueue_time_ms_); - } - if (!normal_priority_packets_->empty()) { - oldest_packet_enqueue_time = std::min( - oldest_packet_enqueue_time, - normal_priority_packets_->front().enqueue_time_ms_); - } - if (!low_priority_packets_->empty()) { - oldest_packet_enqueue_time = std::min( - oldest_packet_enqueue_time, - low_priority_packets_->front().enqueue_time_ms_); - } - return now_ms - oldest_packet_enqueue_time; + + int64_t oldest_packet = packets_->OldestEnqueueTime(); + if (oldest_packet == 0) + return 0; + + return clock_->TimeInMilliseconds() - oldest_packet; } int32_t PacedSender::TimeUntilNextProcess() { CriticalSectionScoped cs(critsect_.get()); - int64_t elapsed_time_ms = - (TickTime::Now() - time_last_update_).Milliseconds(); - if (elapsed_time_ms <= 0) { - return kMinPacketLimitMs; + int64_t elapsed_time_us = clock_->TimeInMicroseconds() - time_last_update_us_; + int elapsed_time_ms = static_cast((elapsed_time_us + 500) / 1000); + if (prober_->IsProbing()) { + int next_probe = prober_->TimeUntilNextProbe(clock_->TimeInMilliseconds()); + return next_probe; } if (elapsed_time_ms >= kMinPacketLimitMs) { return 0; @@ -253,10 +318,10 @@ } int32_t PacedSender::Process() { - TickTime now = TickTime::Now(); + int64_t now_us = clock_->TimeInMicroseconds(); CriticalSectionScoped cs(critsect_.get()); - int elapsed_time_ms = (now - time_last_update_).Milliseconds(); - time_last_update_ = now; + int elapsed_time_ms = (now_us - time_last_update_us_ + 500) / 1000; + time_last_update_us_ = now_us; if (!enabled_) { return 0; } @@ -265,130 +330,74 @@ uint32_t delta_time_ms = std::min(kMaxIntervalTimeMs, elapsed_time_ms); UpdateBytesPerInterval(delta_time_ms); } - paced_sender::PacketList* packet_list; - while (ShouldSendNextPacket(&packet_list)) { - if (!SendPacketFromList(packet_list)) + + while (!packets_->Empty()) { + if (media_budget_->bytes_remaining() <= 0 && !prober_->IsProbing()) + return 0; + + // Since we need to release the lock in order to send, we first pop the + // element from the priority queue but keep it in storage, so that we can + // reinsert it if send fails. + const paced_sender::Packet& packet = packets_->BeginPop(); + if (SendPacket(packet)) { + // Send succeeded, remove it from the queue. + packets_->FinalizePop(packet); + if (prober_->IsProbing()) + return 0; + } else { + // Send failed, put it back into the queue. + packets_->CancelPop(packet); return 0; + } } - if (high_priority_packets_->empty() && - normal_priority_packets_->empty() && - low_priority_packets_->empty() && - padding_budget_->bytes_remaining() > 0 && - pad_up_to_bitrate_budget_->bytes_remaining() > 0) { - int padding_needed = std::min( - padding_budget_->bytes_remaining(), - pad_up_to_bitrate_budget_->bytes_remaining()); - critsect_->Leave(); - int bytes_sent = callback_->TimeToSendPadding(padding_needed); - critsect_->Enter(); - media_budget_->UseBudget(bytes_sent); - padding_budget_->UseBudget(bytes_sent); - pad_up_to_bitrate_budget_->UseBudget(bytes_sent); + + int padding_needed = padding_budget_->bytes_remaining(); + if (padding_needed > 0) { + SendPadding(padding_needed); } } return 0; } -// MUST have critsect_ when calling. -bool PacedSender::SendPacketFromList(paced_sender::PacketList* packet_list) - EXCLUSIVE_LOCKS_REQUIRED(critsect_.get()) { - paced_sender::Packet packet = GetNextPacketFromList(packet_list); +bool PacedSender::SendPacket(const paced_sender::Packet& packet) { critsect_->Leave(); - - const bool success = callback_->TimeToSendPacket(packet.ssrc_, - packet.sequence_number_, - packet.capture_time_ms_, - packet.retransmission_); + const bool success = callback_->TimeToSendPacket(packet.ssrc, + packet.sequence_number, + packet.capture_time_ms, + packet.retransmission); critsect_->Enter(); - // If packet cannot be sent then keep it in packet list and exit early. - // There's no need to send more packets. - if (!success) { - return false; - } - packet_list->pop_front(); - const bool last_packet = packet_list->empty() || - packet_list->front().capture_time_ms_ > packet.capture_time_ms_; - if (packet_list != high_priority_packets_.get()) { - if (packet.capture_time_ms_ > capture_time_ms_last_sent_) { - capture_time_ms_last_sent_ = packet.capture_time_ms_; - } else if (packet.capture_time_ms_ == capture_time_ms_last_sent_ && - last_packet) { - TRACE_EVENT_ASYNC_END0("webrtc_rtp", "PacedSend", - packet.capture_time_ms_); - } + + if (success) { + // Update media bytes sent. + prober_->PacketSent(clock_->TimeInMilliseconds(), packet.bytes); + media_budget_->UseBudget(packet.bytes); + padding_budget_->UseBudget(packet.bytes); } - return true; + + return success; +} + +void PacedSender::SendPadding(int padding_needed) { + critsect_->Leave(); + int bytes_sent = callback_->TimeToSendPadding(padding_needed); + critsect_->Enter(); + + // Update padding bytes sent. + media_budget_->UseBudget(bytes_sent); + padding_budget_->UseBudget(bytes_sent); } -// MUST have critsect_ when calling. void PacedSender::UpdateBytesPerInterval(uint32_t delta_time_ms) { media_budget_->IncreaseBudget(delta_time_ms); padding_budget_->IncreaseBudget(delta_time_ms); - pad_up_to_bitrate_budget_->IncreaseBudget(delta_time_ms); } -// MUST have critsect_ when calling. -bool PacedSender::ShouldSendNextPacket(paced_sender::PacketList** packet_list) { - *packet_list = NULL; - if (media_budget_->bytes_remaining() <= 0) { - // All bytes consumed for this interval. - // Check if we have not sent in a too long time. - if ((TickTime::Now() - time_last_send_).Milliseconds() > - kMaxQueueTimeWithoutSendingMs) { - if (!high_priority_packets_->empty()) { - *packet_list = high_priority_packets_.get(); - return true; - } - if (!normal_priority_packets_->empty()) { - *packet_list = normal_priority_packets_.get(); - return true; - } - } - // Send any old packets to avoid queuing for too long. - if (max_queue_length_ms_ >= 0 && QueueInMs() > max_queue_length_ms_) { - int64_t high_priority_capture_time = -1; - if (!high_priority_packets_->empty()) { - high_priority_capture_time = - high_priority_packets_->front().capture_time_ms_; - *packet_list = high_priority_packets_.get(); - } - if (!normal_priority_packets_->empty() && - (high_priority_capture_time == -1 || high_priority_capture_time > - normal_priority_packets_->front().capture_time_ms_)) { - *packet_list = normal_priority_packets_.get(); - } - if (*packet_list) - return true; - } - return false; - } - if (!high_priority_packets_->empty()) { - *packet_list = high_priority_packets_.get(); - return true; - } - if (!normal_priority_packets_->empty()) { - *packet_list = normal_priority_packets_.get(); - return true; - } - if (!low_priority_packets_->empty()) { - *packet_list = low_priority_packets_.get(); - return true; - } +bool PacedSender::ProbingExperimentIsEnabled() const { +#ifndef WEBRTC_MOZILLA_BUILD + return webrtc::field_trial::FindFullName("WebRTC-BitrateProbing") == + "Enabled"; +#else return false; +#endif } - -paced_sender::Packet PacedSender::GetNextPacketFromList( - paced_sender::PacketList* packets) { - paced_sender::Packet packet = packets->front(); - UpdateMediaBytesSent(packet.bytes_); - return packet; -} - -// MUST have critsect_ when calling. -void PacedSender::UpdateMediaBytesSent(int num_bytes) { - time_last_send_ = TickTime::Now(); - media_budget_->UseBudget(num_bytes); - pad_up_to_bitrate_budget_->UseBudget(num_bytes); -} - } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/paced_sender_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/paced_sender_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/paced_sender_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/paced_sender_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -8,10 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include + #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" - #include "webrtc/modules/pacing/include/paced_sender.h" +#include "webrtc/system_wrappers/interface/clock.h" using testing::_; using testing::Return; @@ -25,8 +27,10 @@ class MockPacedSenderCallback : public PacedSender::Callback { public: MOCK_METHOD4(TimeToSendPacket, - bool(uint32_t ssrc, uint16_t sequence_number, int64_t capture_time_ms, - bool retransmission)); + bool(uint32_t ssrc, + uint16_t sequence_number, + int64_t capture_time_ms, + bool retransmission)); MOCK_METHOD1(TimeToSendPadding, int(int bytes)); }; @@ -35,8 +39,10 @@ public: PacedSenderPadding() : padding_sent_(0) {} - bool TimeToSendPacket(uint32_t ssrc, uint16_t sequence_number, - int64_t capture_time_ms, bool retransmission) { + bool TimeToSendPacket(uint32_t ssrc, + uint16_t sequence_number, + int64_t capture_time_ms, + bool retransmission) { return true; } @@ -53,29 +59,72 @@ int padding_sent_; }; +class PacedSenderProbing : public PacedSender::Callback { + public: + PacedSenderProbing(const std::list& expected_deltas, Clock* clock) + : prev_packet_time_ms_(-1), + expected_deltas_(expected_deltas), + packets_sent_(0), + clock_(clock) {} + + bool TimeToSendPacket(uint32_t ssrc, + uint16_t sequence_number, + int64_t capture_time_ms, + bool retransmission) { + ++packets_sent_; + EXPECT_FALSE(expected_deltas_.empty()); + if (expected_deltas_.empty()) + return false; + int64_t now_ms = clock_->TimeInMilliseconds(); + if (prev_packet_time_ms_ >= 0) { + EXPECT_EQ(expected_deltas_.front(), now_ms - prev_packet_time_ms_); + expected_deltas_.pop_front(); + } + prev_packet_time_ms_ = now_ms; + return true; + } + + int TimeToSendPadding(int bytes) { + EXPECT_TRUE(false); + return bytes; + } + + int packets_sent() const { return packets_sent_; } + + private: + int64_t prev_packet_time_ms_; + std::list expected_deltas_; + int packets_sent_; + Clock* clock_; +}; + class PacedSenderTest : public ::testing::Test { protected: - PacedSenderTest() { + PacedSenderTest() : clock_(123456) { srand(0); - TickTime::UseFakeClock(123456); // Need to initialize PacedSender after we initialize clock. - send_bucket_.reset(new PacedSender(&callback_, kTargetBitrate, - kPaceMultiplier)); - send_bucket_->SetStatus(true); + send_bucket_.reset(new PacedSender(&clock_, + &callback_, + kTargetBitrate, + kPaceMultiplier * kTargetBitrate, + 0)); } void SendAndExpectPacket(PacedSender::Priority priority, - uint32_t ssrc, uint16_t sequence_number, - int64_t capture_time_ms, int size, + uint32_t ssrc, + uint16_t sequence_number, + int64_t capture_time_ms, + int size, bool retransmission) { EXPECT_FALSE(send_bucket_->SendPacket(priority, ssrc, sequence_number, capture_time_ms, size, retransmission)); - EXPECT_CALL(callback_, TimeToSendPacket( - ssrc, sequence_number, capture_time_ms, false)) + EXPECT_CALL(callback_, + TimeToSendPacket(ssrc, sequence_number, capture_time_ms, false)) .Times(1) .WillRepeatedly(Return(true)); } + SimulatedClock clock_; MockPacedSenderCallback callback_; scoped_ptr send_bucket_; }; @@ -84,34 +133,55 @@ uint32_t ssrc = 12345; uint16_t sequence_number = 1234; // Due to the multiplicative factor we can send 3 packets not 2 packets. - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - TickTime::MillisecondTimestamp(), 250, false); - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - TickTime::MillisecondTimestamp(), 250, false); - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - TickTime::MillisecondTimestamp(), 250, false); - int64_t queued_packet_timestamp = TickTime::MillisecondTimestamp(); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + clock_.TimeInMilliseconds(), + 250, + false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + clock_.TimeInMilliseconds(), + 250, + false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + clock_.TimeInMilliseconds(), + 250, + false); + int64_t queued_packet_timestamp = clock_.TimeInMilliseconds(); EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc, sequence_number, queued_packet_timestamp, 250, false)); send_bucket_->Process(); EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess()); EXPECT_CALL(callback_, TimeToSendPadding(_)).Times(0); - TickTime::AdvanceFakeClock(4); + clock_.AdvanceTimeMilliseconds(4); EXPECT_EQ(1, send_bucket_->TimeUntilNextProcess()); - TickTime::AdvanceFakeClock(1); + clock_.AdvanceTimeMilliseconds(1); EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess()); - EXPECT_CALL(callback_, TimeToSendPacket( - ssrc, sequence_number++, queued_packet_timestamp, false)) + EXPECT_CALL( + callback_, + TimeToSendPacket(ssrc, sequence_number++, queued_packet_timestamp, false)) .Times(1) .WillRepeatedly(Return(true)); send_bucket_->Process(); sequence_number++; - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - TickTime::MillisecondTimestamp(), 250, false); - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - TickTime::MillisecondTimestamp(), 250, false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + clock_.TimeInMilliseconds(), + 250, + false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + clock_.TimeInMilliseconds(), + 250, + false); EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc, - sequence_number++, TickTime::MillisecondTimestamp(), 250, false)); + sequence_number++, clock_.TimeInMilliseconds(), 250, false)); send_bucket_->Process(); } @@ -121,37 +191,52 @@ // Due to the multiplicative factor we can send 3 packets not 2 packets. for (int i = 0; i < 3; ++i) { - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - TickTime::MillisecondTimestamp(), 250, false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + clock_.TimeInMilliseconds(), + 250, + false); } for (int j = 0; j < 30; ++j) { EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc, - sequence_number++, TickTime::MillisecondTimestamp(), 250, false)); + sequence_number++, clock_.TimeInMilliseconds(), 250, false)); } send_bucket_->Process(); EXPECT_CALL(callback_, TimeToSendPadding(_)).Times(0); for (int k = 0; k < 10; ++k) { EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess()); - TickTime::AdvanceFakeClock(5); - EXPECT_CALL(callback_, - TimeToSendPacket(ssrc, _, _, false)) + clock_.AdvanceTimeMilliseconds(5); + EXPECT_CALL(callback_, TimeToSendPacket(ssrc, _, _, false)) .Times(3) .WillRepeatedly(Return(true)); EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess()); EXPECT_EQ(0, send_bucket_->Process()); } EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess()); - TickTime::AdvanceFakeClock(5); + clock_.AdvanceTimeMilliseconds(5); EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess()); EXPECT_EQ(0, send_bucket_->Process()); - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - TickTime::MillisecondTimestamp(), 250, false); - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - TickTime::MillisecondTimestamp(), 250, false); - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - TickTime::MillisecondTimestamp(), 250, false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + clock_.TimeInMilliseconds(), + 250, + false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + clock_.TimeInMilliseconds(), + 250, + false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + clock_.TimeInMilliseconds(), + 250, + false); EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc, - sequence_number, TickTime::MillisecondTimestamp(), 250, false)); + sequence_number, clock_.TimeInMilliseconds(), 250, false)); send_bucket_->Process(); } @@ -162,28 +247,31 @@ // Due to the multiplicative factor we can send 3 packets not 2 packets. for (int i = 0; i < 3; ++i) { - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - TickTime::MillisecondTimestamp(), 250, false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + clock_.TimeInMilliseconds(), + 250, + false); } queued_sequence_number = sequence_number; for (int j = 0; j < 30; ++j) { // Send in duplicate packets. EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc, - sequence_number, TickTime::MillisecondTimestamp(), 250, false)); + sequence_number, clock_.TimeInMilliseconds(), 250, false)); EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc, - sequence_number++, TickTime::MillisecondTimestamp(), 250, false)); + sequence_number++, clock_.TimeInMilliseconds(), 250, false)); } EXPECT_CALL(callback_, TimeToSendPadding(_)).Times(0); send_bucket_->Process(); for (int k = 0; k < 10; ++k) { EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess()); - TickTime::AdvanceFakeClock(5); + clock_.AdvanceTimeMilliseconds(5); for (int i = 0; i < 3; ++i) { - EXPECT_CALL(callback_, TimeToSendPacket(ssrc, queued_sequence_number++, - _, - false)) + EXPECT_CALL(callback_, + TimeToSendPacket(ssrc, queued_sequence_number++, _, false)) .Times(1) .WillRepeatedly(Return(true)); } @@ -191,17 +279,52 @@ EXPECT_EQ(0, send_bucket_->Process()); } EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess()); - TickTime::AdvanceFakeClock(5); + clock_.AdvanceTimeMilliseconds(5); EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess()); EXPECT_EQ(0, send_bucket_->Process()); - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - TickTime::MillisecondTimestamp(), 250, false); - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - TickTime::MillisecondTimestamp(), 250, false); - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - TickTime::MillisecondTimestamp(), 250, false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + clock_.TimeInMilliseconds(), + 250, + false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + clock_.TimeInMilliseconds(), + 250, + false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + clock_.TimeInMilliseconds(), + 250, + false); EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc, - sequence_number++, TickTime::MillisecondTimestamp(), 250, false)); + sequence_number++, clock_.TimeInMilliseconds(), 250, false)); + send_bucket_->Process(); +} + +TEST_F(PacedSenderTest, CanQueuePacketsWithSameSequenceNumberOnDifferentSsrcs) { + uint32_t ssrc = 12345; + uint16_t sequence_number = 1234; + + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number, + clock_.TimeInMilliseconds(), + 250, + false); + + // Expect packet on second ssrc to be queued and sent as well. + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc + 1, + sequence_number, + clock_.TimeInMilliseconds(), + 250, + false); + + clock_.AdvanceTimeMilliseconds(1000); send_bucket_->Process(); } @@ -209,18 +332,31 @@ uint32_t ssrc = 12345; uint16_t sequence_number = 1234; - send_bucket_->UpdateBitrate(kTargetBitrate, kTargetBitrate, kTargetBitrate); + send_bucket_->UpdateBitrate( + kTargetBitrate, kPaceMultiplier * kTargetBitrate, kTargetBitrate); // Due to the multiplicative factor we can send 3 packets not 2 packets. - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - TickTime::MillisecondTimestamp(), 250, false); - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - TickTime::MillisecondTimestamp(), 250, false); - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - TickTime::MillisecondTimestamp(), 250, false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + clock_.TimeInMilliseconds(), + 250, + false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + clock_.TimeInMilliseconds(), + 250, + false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + clock_.TimeInMilliseconds(), + 250, + false); // No padding is expected since we have sent too much already. EXPECT_CALL(callback_, TimeToSendPadding(_)).Times(0); EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess()); - TickTime::AdvanceFakeClock(5); + clock_.AdvanceTimeMilliseconds(5); EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess()); EXPECT_EQ(0, send_bucket_->Process()); @@ -228,23 +364,24 @@ EXPECT_CALL(callback_, TimeToSendPadding(250)).Times(1). WillOnce(Return(250)); EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess()); - TickTime::AdvanceFakeClock(5); + clock_.AdvanceTimeMilliseconds(5); EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess()); EXPECT_EQ(0, send_bucket_->Process()); } TEST_F(PacedSenderTest, NoPaddingWhenDisabled) { send_bucket_->SetStatus(false); - send_bucket_->UpdateBitrate(kTargetBitrate, kTargetBitrate, kTargetBitrate); + send_bucket_->UpdateBitrate( + kTargetBitrate, kPaceMultiplier * kTargetBitrate, kTargetBitrate); // No padding is expected since the pacer is disabled. EXPECT_CALL(callback_, TimeToSendPadding(_)).Times(0); EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess()); - TickTime::AdvanceFakeClock(5); + clock_.AdvanceTimeMilliseconds(5); EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess()); EXPECT_EQ(0, send_bucket_->Process()); EXPECT_CALL(callback_, TimeToSendPadding(_)).Times(0); EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess()); - TickTime::AdvanceFakeClock(5); + clock_.AdvanceTimeMilliseconds(5); EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess()); EXPECT_EQ(0, send_bucket_->Process()); } @@ -255,39 +392,23 @@ int64_t capture_time_ms = 56789; const int kTimeStep = 5; const int64_t kBitrateWindow = 100; - send_bucket_->UpdateBitrate(kTargetBitrate, kTargetBitrate, kTargetBitrate); - int64_t start_time = TickTime::MillisecondTimestamp(); - while (TickTime::MillisecondTimestamp() - start_time < kBitrateWindow) { - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - capture_time_ms, 250, false); - TickTime::AdvanceFakeClock(kTimeStep); + send_bucket_->UpdateBitrate( + kTargetBitrate, kPaceMultiplier * kTargetBitrate, kTargetBitrate); + int64_t start_time = clock_.TimeInMilliseconds(); + while (clock_.TimeInMilliseconds() - start_time < kBitrateWindow) { + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + capture_time_ms, + 250, + false); + clock_.AdvanceTimeMilliseconds(kTimeStep); EXPECT_CALL(callback_, TimeToSendPadding(250)).Times(1). WillOnce(Return(250)); send_bucket_->Process(); } } -TEST_F(PacedSenderTest, VerifyMaxPaddingBitrate) { - uint32_t ssrc = 12345; - uint16_t sequence_number = 1234; - int64_t capture_time_ms = 56789; - const int kTimeStep = 5; - const int64_t kBitrateWindow = 100; - const int kTargetBitrate = 1500; - const int kMaxPaddingBitrate = 800; - send_bucket_->UpdateBitrate(kTargetBitrate, kMaxPaddingBitrate, - kTargetBitrate); - int64_t start_time = TickTime::MillisecondTimestamp(); - while (TickTime::MillisecondTimestamp() - start_time < kBitrateWindow) { - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - capture_time_ms, 250, false); - TickTime::AdvanceFakeClock(kTimeStep); - EXPECT_CALL(callback_, TimeToSendPadding(500)).Times(1). - WillOnce(Return(250)); - send_bucket_->Process(); - } -} - TEST_F(PacedSenderTest, VerifyAverageBitrateVaryingMediaPayload) { uint32_t ssrc = 12345; uint16_t sequence_number = 1234; @@ -295,19 +416,19 @@ const int kTimeStep = 5; const int64_t kBitrateWindow = 10000; PacedSenderPadding callback; - send_bucket_.reset(new PacedSender(&callback, kTargetBitrate, - kPaceMultiplier)); - send_bucket_->SetStatus(true); - send_bucket_->UpdateBitrate(kTargetBitrate, kTargetBitrate, kTargetBitrate); - int64_t start_time = TickTime::MillisecondTimestamp(); + send_bucket_.reset(new PacedSender( + &clock_, &callback, kTargetBitrate, kPaceMultiplier * kTargetBitrate, 0)); + send_bucket_->UpdateBitrate( + kTargetBitrate, kPaceMultiplier * kTargetBitrate, kTargetBitrate); + int64_t start_time = clock_.TimeInMilliseconds(); int media_bytes = 0; - while (TickTime::MillisecondTimestamp() - start_time < kBitrateWindow) { + while (clock_.TimeInMilliseconds() - start_time < kBitrateWindow) { int media_payload = rand() % 100 + 200; // [200, 300] bytes. EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, capture_time_ms, media_payload, false)); media_bytes += media_payload; - TickTime::AdvanceFakeClock(kTimeStep); + clock_.AdvanceTimeMilliseconds(kTimeStep); send_bucket_->Process(); } EXPECT_NEAR(kTargetBitrate, 8 * (media_bytes + callback.padding_sent()) / @@ -322,12 +443,24 @@ int64_t capture_time_ms_low_priority = 1234567; // Due to the multiplicative factor we can send 3 packets not 2 packets. - SendAndExpectPacket(PacedSender::kLowPriority, ssrc, sequence_number++, - capture_time_ms, 250, false); - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - capture_time_ms, 250, false); - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - capture_time_ms, 250, false); + SendAndExpectPacket(PacedSender::kLowPriority, + ssrc, + sequence_number++, + capture_time_ms, + 250, + false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + capture_time_ms, + 250, + false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + capture_time_ms, + 250, + false); send_bucket_->Process(); // Expect normal and low priority to be queued and high to pass through. @@ -348,17 +481,18 @@ .WillRepeatedly(Return(true)); EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess()); - TickTime::AdvanceFakeClock(5); + clock_.AdvanceTimeMilliseconds(5); EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess()); EXPECT_EQ(0, send_bucket_->Process()); - EXPECT_CALL(callback_, TimeToSendPacket( - ssrc_low_priority, _, capture_time_ms_low_priority, false)) + EXPECT_CALL(callback_, + TimeToSendPacket( + ssrc_low_priority, _, capture_time_ms_low_priority, false)) .Times(1) .WillRepeatedly(Return(true)); EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess()); - TickTime::AdvanceFakeClock(5); + clock_.AdvanceTimeMilliseconds(5); EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess()); EXPECT_EQ(0, send_bucket_->Process()); } @@ -367,17 +501,29 @@ uint32_t ssrc_low_priority = 12345; uint32_t ssrc = 12346; uint16_t sequence_number = 1234; - int64_t capture_time_ms = TickTime::MillisecondTimestamp(); + int64_t capture_time_ms = clock_.TimeInMilliseconds(); EXPECT_EQ(0, send_bucket_->QueueInMs()); // Due to the multiplicative factor we can send 3 packets not 2 packets. - SendAndExpectPacket(PacedSender::kLowPriority, ssrc, sequence_number++, - capture_time_ms, 250, false); - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - capture_time_ms, 250, false); - SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, - capture_time_ms, 250, false); + SendAndExpectPacket(PacedSender::kLowPriority, + ssrc, + sequence_number++, + capture_time_ms, + 250, + false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + capture_time_ms, + 250, + false); + SendAndExpectPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + capture_time_ms, + 250, + false); send_bucket_->Process(); send_bucket_->Pause(); @@ -389,15 +535,15 @@ EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kHighPriority, ssrc, sequence_number++, capture_time_ms, 250, false)); - TickTime::AdvanceFakeClock(10000); - int64_t second_capture_time_ms = TickTime::MillisecondTimestamp(); + clock_.AdvanceTimeMilliseconds(10000); + int64_t second_capture_time_ms = clock_.TimeInMilliseconds(); // Expect everything to be queued. EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kLowPriority, ssrc_low_priority, sequence_number++, second_capture_time_ms, 250, false)); - EXPECT_EQ(TickTime::MillisecondTimestamp() - capture_time_ms, + EXPECT_EQ(clock_.TimeInMilliseconds() - capture_time_ms, send_bucket_->QueueInMs()); // Expect no packet to come out while paused. @@ -405,7 +551,7 @@ EXPECT_CALL(callback_, TimeToSendPacket(_, _, _, _)).Times(0); for (int i = 0; i < 10; ++i) { - TickTime::AdvanceFakeClock(5); + clock_.AdvanceTimeMilliseconds(5); EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess()); EXPECT_EQ(0, send_bucket_->Process()); } @@ -417,7 +563,7 @@ send_bucket_->Resume(); EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess()); - TickTime::AdvanceFakeClock(5); + clock_.AdvanceTimeMilliseconds(5); EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess()); EXPECT_EQ(0, send_bucket_->Process()); @@ -425,7 +571,7 @@ .Times(1) .WillRepeatedly(Return(true)); EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess()); - TickTime::AdvanceFakeClock(5); + clock_.AdvanceTimeMilliseconds(5); EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess()); EXPECT_EQ(0, send_bucket_->Process()); EXPECT_EQ(0, send_bucket_->QueueInMs()); @@ -434,7 +580,7 @@ TEST_F(PacedSenderTest, ResendPacket) { uint32_t ssrc = 12346; uint16_t sequence_number = 1234; - int64_t capture_time_ms = TickTime::MillisecondTimestamp(); + int64_t capture_time_ms = clock_.TimeInMilliseconds(); EXPECT_EQ(0, send_bucket_->QueueInMs()); EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, @@ -443,81 +589,90 @@ capture_time_ms, 250, false)); - TickTime::AdvanceFakeClock(1); + clock_.AdvanceTimeMilliseconds(1); EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc, sequence_number + 1, capture_time_ms + 1, 250, false)); - TickTime::AdvanceFakeClock(9999); - EXPECT_EQ(TickTime::MillisecondTimestamp() - capture_time_ms, + clock_.AdvanceTimeMilliseconds(9999); + EXPECT_EQ(clock_.TimeInMilliseconds() - capture_time_ms, send_bucket_->QueueInMs()); // Fails to send first packet so only one call. - EXPECT_CALL(callback_, TimeToSendPacket( - ssrc, sequence_number, capture_time_ms, false)) + EXPECT_CALL(callback_, + TimeToSendPacket(ssrc, sequence_number, capture_time_ms, false)) .Times(1) .WillOnce(Return(false)); - TickTime::AdvanceFakeClock(10000); + clock_.AdvanceTimeMilliseconds(10000); send_bucket_->Process(); // Queue remains unchanged. - EXPECT_EQ(TickTime::MillisecondTimestamp() - capture_time_ms, + EXPECT_EQ(clock_.TimeInMilliseconds() - capture_time_ms, send_bucket_->QueueInMs()); // Fails to send second packet. - EXPECT_CALL(callback_, TimeToSendPacket( - ssrc, sequence_number, capture_time_ms, false)) + EXPECT_CALL(callback_, + TimeToSendPacket(ssrc, sequence_number, capture_time_ms, false)) .Times(1) .WillOnce(Return(true)); - EXPECT_CALL(callback_, TimeToSendPacket( - ssrc, sequence_number + 1, capture_time_ms + 1, false)) + EXPECT_CALL( + callback_, + TimeToSendPacket(ssrc, sequence_number + 1, capture_time_ms + 1, false)) .Times(1) .WillOnce(Return(false)); - TickTime::AdvanceFakeClock(10000); + clock_.AdvanceTimeMilliseconds(10000); send_bucket_->Process(); // Queue is reduced by 1 packet. - EXPECT_EQ(TickTime::MillisecondTimestamp() - capture_time_ms - 1, + EXPECT_EQ(clock_.TimeInMilliseconds() - capture_time_ms - 1, send_bucket_->QueueInMs()); // Send second packet and queue becomes empty. - EXPECT_CALL(callback_, TimeToSendPacket( - ssrc, sequence_number + 1, capture_time_ms + 1, false)) + EXPECT_CALL( + callback_, + TimeToSendPacket(ssrc, sequence_number + 1, capture_time_ms + 1, false)) .Times(1) .WillOnce(Return(true)); - TickTime::AdvanceFakeClock(10000); + clock_.AdvanceTimeMilliseconds(10000); send_bucket_->Process(); EXPECT_EQ(0, send_bucket_->QueueInMs()); } -TEST_F(PacedSenderTest, MaxQueueLength) { +TEST_F(PacedSenderTest, ExpectedQueueTimeMs) { uint32_t ssrc = 12346; uint16_t sequence_number = 1234; - EXPECT_EQ(0, send_bucket_->QueueInMs()); + const int32_t kNumPackets = 60; + const int32_t kPacketSize = 1200; + const int32_t kMaxBitrate = kPaceMultiplier * 30; + EXPECT_EQ(0, send_bucket_->ExpectedQueueTimeMs()); - send_bucket_->UpdateBitrate(30, 0, 0); - for (int i = 0; i < 30; ++i) { - SendAndExpectPacket(PacedSender::kNormalPriority, - ssrc, - sequence_number++, - TickTime::MillisecondTimestamp(), - 1200, - false); + send_bucket_->UpdateBitrate(30, kMaxBitrate, 0); + for (int i = 0; i < kNumPackets; ++i) { + SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, + clock_.TimeInMilliseconds(), kPacketSize, false); } - TickTime::AdvanceFakeClock(2001); - SendAndExpectPacket(PacedSender::kNormalPriority, - ssrc, - sequence_number++, - TickTime::MillisecondTimestamp(), - 1200, - false); - EXPECT_EQ(2001, send_bucket_->QueueInMs()); - send_bucket_->Process(); - EXPECT_EQ(0, send_bucket_->QueueInMs()); - TickTime::AdvanceFakeClock(31); - send_bucket_->Process(); + // Queue in ms = 1000 * (bytes in queue) / (kbit per second * 1000 / 8) + int32_t queue_in_ms = kNumPackets * kPacketSize * 8 / kMaxBitrate; + EXPECT_EQ(queue_in_ms, send_bucket_->ExpectedQueueTimeMs()); + + int64_t time_start = clock_.TimeInMilliseconds(); + while (send_bucket_->QueueSizePackets() > 0) { + int time_until_process = send_bucket_->TimeUntilNextProcess(); + if (time_until_process <= 0) { + send_bucket_->Process(); + } else { + clock_.AdvanceTimeMilliseconds(time_until_process); + } + } + int64_t duration = clock_.TimeInMilliseconds() - time_start; + + EXPECT_EQ(0, send_bucket_->ExpectedQueueTimeMs()); + + // Allow for aliasing, duration should be in [expected(n - 1), expected(n)]. + EXPECT_LE(duration, queue_in_ms); + EXPECT_GE(duration, queue_in_ms - (kPacketSize * 8 / kMaxBitrate)); } TEST_F(PacedSenderTest, QueueTimeGrowsOverTime) { @@ -525,18 +680,144 @@ uint16_t sequence_number = 1234; EXPECT_EQ(0, send_bucket_->QueueInMs()); - send_bucket_->UpdateBitrate(30, 0, 0); + send_bucket_->UpdateBitrate(30, kPaceMultiplier * 30, 0); SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number, - TickTime::MillisecondTimestamp(), + clock_.TimeInMilliseconds(), 1200, false); - TickTime::AdvanceFakeClock(500); + clock_.AdvanceTimeMilliseconds(500); EXPECT_EQ(500, send_bucket_->QueueInMs()); send_bucket_->Process(); EXPECT_EQ(0, send_bucket_->QueueInMs()); } + +class ProbingPacedSender : public PacedSender { + public: + ProbingPacedSender(Clock* clock, + Callback* callback, + int bitrate_kbps, + int max_bitrate_kbps, + int min_bitrate_kbps) + : PacedSender(clock, + callback, + bitrate_kbps, + max_bitrate_kbps, + min_bitrate_kbps) {} + + virtual bool ProbingExperimentIsEnabled() const OVERRIDE { return true; } +}; + +TEST_F(PacedSenderTest, ProbingWithInitialFrame) { + const int kNumPackets = 11; + const int kNumDeltas = kNumPackets - 1; + const int kPacketSize = 1200; + const int kInitialBitrateKbps = 300; + uint32_t ssrc = 12346; + uint16_t sequence_number = 1234; + const int expected_deltas[kNumDeltas] = { + 10, 10, 10, 10, 10, 5, 5, 5, 5, 5}; + std::list expected_deltas_list(expected_deltas, + expected_deltas + kNumPackets - 1); + PacedSenderProbing callback(expected_deltas_list, &clock_); + send_bucket_.reset( + new ProbingPacedSender(&clock_, + &callback, + kInitialBitrateKbps, + kPaceMultiplier * kInitialBitrateKbps, + 0)); + for (int i = 0; i < kNumPackets; ++i) { + EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, + ssrc, + sequence_number++, + clock_.TimeInMilliseconds(), + kPacketSize, + false)); + } + while (callback.packets_sent() < kNumPackets) { + int time_until_process = send_bucket_->TimeUntilNextProcess(); + if (time_until_process <= 0) { + send_bucket_->Process(); + } else { + clock_.AdvanceTimeMilliseconds(time_until_process); + } + } +} + +TEST_F(PacedSenderTest, PriorityInversion) { + uint32_t ssrc = 12346; + uint16_t sequence_number = 1234; + const int32_t kPacketSize = 1200; + + EXPECT_FALSE(send_bucket_->SendPacket( + PacedSender::kHighPriority, ssrc, sequence_number + 3, + clock_.TimeInMilliseconds() + 33, kPacketSize, true)); + + EXPECT_FALSE(send_bucket_->SendPacket( + PacedSender::kHighPriority, ssrc, sequence_number + 2, + clock_.TimeInMilliseconds() + 33, kPacketSize, true)); + + EXPECT_FALSE(send_bucket_->SendPacket( + PacedSender::kHighPriority, ssrc, sequence_number, + clock_.TimeInMilliseconds(), kPacketSize, true)); + + EXPECT_FALSE(send_bucket_->SendPacket( + PacedSender::kHighPriority, ssrc, sequence_number + 1, + clock_.TimeInMilliseconds(), kPacketSize, true)); + + // Packets from earlier frames should be sent first. + { + ::testing::InSequence sequence; + EXPECT_CALL(callback_, TimeToSendPacket(ssrc, sequence_number, + clock_.TimeInMilliseconds(), true)) + .WillOnce(Return(true)); + EXPECT_CALL(callback_, TimeToSendPacket(ssrc, sequence_number + 1, + clock_.TimeInMilliseconds(), true)) + .WillOnce(Return(true)); + EXPECT_CALL(callback_, TimeToSendPacket(ssrc, sequence_number + 3, + clock_.TimeInMilliseconds() + 33, + true)).WillOnce(Return(true)); + EXPECT_CALL(callback_, TimeToSendPacket(ssrc, sequence_number + 2, + clock_.TimeInMilliseconds() + 33, + true)).WillOnce(Return(true)); + + while (send_bucket_->QueueSizePackets() > 0) { + int time_until_process = send_bucket_->TimeUntilNextProcess(); + if (time_until_process <= 0) { + send_bucket_->Process(); + } else { + clock_.AdvanceTimeMilliseconds(time_until_process); + } + } + } +} + +TEST_F(PacedSenderTest, PaddingOveruse) { + uint32_t ssrc = 12346; + uint16_t sequence_number = 1234; + const int32_t kPacketSize = 1200; + + // Min bitrate 0 => no padding, padding budget will stay at 0. + send_bucket_->UpdateBitrate(60, 90, 0); + SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++, + clock_.TimeInMilliseconds(), kPacketSize, false); + send_bucket_->Process(); + + // Add 30kbit padding. When increasing budget, media budget will increase from + // negative (overuse) while padding budget will increase form 0. + clock_.AdvanceTimeMilliseconds(5); + send_bucket_->UpdateBitrate(60, 90, 30); + + EXPECT_FALSE(send_bucket_->SendPacket( + PacedSender::kHighPriority, ssrc, sequence_number++, + clock_.TimeInMilliseconds(), kPacketSize, false)); + + // Don't send padding if queue is non-empty, even if padding budget > 0. + EXPECT_CALL(callback_, TimeToSendPadding(_)).Times(0); + send_bucket_->Process(); +} + } // namespace test } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/pacing.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/pacing.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/pacing/pacing.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/pacing/pacing.gypi 2015-02-03 14:33:36.000000000 +0000 @@ -16,6 +16,8 @@ ], 'sources': [ 'include/paced_sender.h', + 'bitrate_prober.cc', + 'bitrate_prober.h', 'paced_sender.cc', ], }, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/BUILD.gn 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,42 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +source_set("remote_bitrate_estimator") { + sources = [ + "include/bwe_defines.h", + "include/remote_bitrate_estimator.h", + "rate_statistics.cc", + "rate_statistics.h", + ] + + configs += [ "../../:common_inherited_config"] + + deps = [ + ":rbe_components", + "../../system_wrappers", + ] +} + +source_set("rbe_components") { + sources = [ + "overuse_detector.cc", + "overuse_detector.h", + "remote_bitrate_estimator_single_stream.cc", + "remote_rate_control.cc", + "remote_rate_control.h", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/bwe_simulations.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/bwe_simulations.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/bwe_simulations.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/bwe_simulations.cc 2015-02-03 14:33:36.000000000 +0000 @@ -8,64 +8,63 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" #include "webrtc/modules/remote_bitrate_estimator/test/bwe_test.h" #include "webrtc/test/testsupport/fileutils.h" +using std::string; + namespace webrtc { namespace testing { namespace bwe { #if BWE_TEST_LOGGING_COMPILE_TIME_ENABLE -std::vector SingleEstimatorConfig() { - static const RemoteBitrateEstimatorFactory factory = +BweTestConfig::EstimatorConfig CreateEstimatorConfig( + int flow_id, bool plot_delay, bool plot_estimate) { + static const AbsoluteSendTimeRemoteBitrateEstimatorFactory factory = AbsoluteSendTimeRemoteBitrateEstimatorFactory(); - std::vector result; - result.push_back(BweTestConfig::EstimatorConfig("AST", &factory)); - return result; + return BweTestConfig::EstimatorConfig("AST", flow_id, &factory, kAimdControl, + plot_delay, plot_estimate); } -std::vector AdaptiveVideoSenderFactories( - uint32_t count) { - static const AdaptiveVideoPacketSenderFactory factories[] = { - AdaptiveVideoPacketSenderFactory(30.00f, 150, 0x1234, 0.13f), - AdaptiveVideoPacketSenderFactory(30.00f, 300, 0x3456, 0.26f), - AdaptiveVideoPacketSenderFactory(15.00f, 600, 0x4567, 0.39f), - }; - - assert(count <= sizeof(factories) / sizeof(factories[0])); - - std::vector result; - for (uint32_t i = 0; i < count; ++i) { - result.push_back(&factories[i]); - } +BweTestConfig MakeAdaptiveBweTestConfig() { + BweTestConfig result; + result.estimator_configs.push_back(CreateEstimatorConfig(0, true, true)); return result; } -BweTestConfig MakeAdaptiveBweTestConfig(uint32_t sender_count) { - BweTestConfig result = { - AdaptiveVideoSenderFactories(sender_count), SingleEstimatorConfig() - }; +BweTestConfig MakeMultiFlowBweTestConfig(int flow_count) { + BweTestConfig result; + for (int i = 0; i < flow_count; ++i) { + result.estimator_configs.push_back(CreateEstimatorConfig(i, false, true)); + } return result; } // This test fixture is used to instantiate tests running with adaptive video // senders. -class BweSimulation : public BweTest { +class BweSimulation : public BweTest, + public ::testing::TestWithParam { public: BweSimulation() : BweTest() {} virtual ~BweSimulation() {} + virtual void SetUp() { + const BweTestConfig& config = GetParam(); + SetupTestFromConfig(config); + } + private: DISALLOW_COPY_AND_ASSIGN(BweSimulation); }; INSTANTIATE_TEST_CASE_P(VideoSendersTest, BweSimulation, - ::testing::Values(MakeAdaptiveBweTestConfig(1), - MakeAdaptiveBweTestConfig(3))); + ::testing::Values(MakeAdaptiveBweTestConfig())); TEST_P(BweSimulation, SprintUplinkTest) { VerboseLogging(true); + AdaptiveVideoSender sender(0, this, 30, 300, 0, 0); RateCounterFilter counter1(this, "sender_output"); TraceBasedDeliveryFilter filter(this, "link_capacity"); RateCounterFilter counter2(this, "receiver_input"); @@ -75,12 +74,141 @@ TEST_P(BweSimulation, Verizon4gDownlinkTest) { VerboseLogging(true); + AdaptiveVideoSender sender(0, this, 30, 300, 0, 0); RateCounterFilter counter1(this, "sender_output"); TraceBasedDeliveryFilter filter(this, "link_capacity"); RateCounterFilter counter2(this, "receiver_input"); ASSERT_TRUE(filter.Init(test::ResourcePath("verizon4g-downlink", "rx"))); RunFor(22 * 60 * 1000); } + +TEST_P(BweSimulation, Choke1000kbps500kbps1000kbps) { + VerboseLogging(true); + AdaptiveVideoSender sender(0, this, 30, 300, 0, 0); + ChokeFilter filter(this); + RateCounterFilter counter(this, "receiver_input"); + filter.SetCapacity(1000); + filter.SetMaxDelay(500); + RunFor(60 * 1000); + filter.SetCapacity(500); + RunFor(60 * 1000); + filter.SetCapacity(1000); + RunFor(60 * 1000); +} + +TEST_P(BweSimulation, PacerChoke1000kbps500kbps1000kbps) { + VerboseLogging(true); + PeriodicKeyFrameSender source(0, NULL, 30, 300, 0, 0, 1000); + PacedVideoSender sender(this, 300, &source); + ChokeFilter filter(this); + RateCounterFilter counter(this, "receiver_input"); + filter.SetCapacity(1000); + filter.SetMaxDelay(500); + RunFor(60 * 1000); + filter.SetCapacity(500); + RunFor(60 * 1000); + filter.SetCapacity(1000); + RunFor(60 * 1000); +} + +TEST_P(BweSimulation, PacerChoke10000kbps) { + VerboseLogging(true); + PeriodicKeyFrameSender source(0, NULL, 30, 300, 0, 0, 0); + PacedVideoSender sender(this, 300, &source); + ChokeFilter filter(this); + RateCounterFilter counter(this, "receiver_input"); + filter.SetCapacity(10000); + filter.SetMaxDelay(500); + RunFor(60 * 1000); +} + +TEST_P(BweSimulation, PacerChoke200kbps30kbps200kbps) { + VerboseLogging(true); + PeriodicKeyFrameSender source(0, NULL, 30, 300, 0, 0, 1000); + PacedVideoSender sender(this, 300, &source); + ChokeFilter filter(this); + RateCounterFilter counter(this, "receiver_input"); + filter.SetCapacity(200); + filter.SetMaxDelay(500); + RunFor(60 * 1000); + filter.SetCapacity(30); + RunFor(60 * 1000); + filter.SetCapacity(200); + RunFor(60 * 1000); +} + +TEST_P(BweSimulation, Choke200kbps30kbps200kbps) { + VerboseLogging(true); + AdaptiveVideoSender sender(0, this, 30, 300, 0, 0); + ChokeFilter filter(this); + RateCounterFilter counter(this, "receiver_input"); + filter.SetCapacity(200); + filter.SetMaxDelay(500); + RunFor(60 * 1000); + filter.SetCapacity(30); + RunFor(60 * 1000); + filter.SetCapacity(200); + RunFor(60 * 1000); +} + +TEST_P(BweSimulation, GoogleWifiTrace3Mbps) { + VerboseLogging(true); + AdaptiveVideoSender sender(0, this, 30, 300, 0, 0); + RateCounterFilter counter1(this, "sender_output"); + TraceBasedDeliveryFilter filter(this, "link_capacity"); + filter.SetMaxDelay(500); + RateCounterFilter counter2(this, "receiver_input"); + ASSERT_TRUE(filter.Init(test::ResourcePath("google-wifi-3mbps", "rx"))); + RunFor(300 * 1000); +} + +TEST_P(BweSimulation, PacerGoogleWifiTrace3Mbps) { + VerboseLogging(true); + PeriodicKeyFrameSender source(0, NULL, 30, 300, 0, 0, 1000); + PacedVideoSender sender(this, 300, &source); + RateCounterFilter counter1(this, "sender_output"); + TraceBasedDeliveryFilter filter(this, "link_capacity"); + filter.SetMaxDelay(500); + RateCounterFilter counter2(this, "receiver_input"); + ASSERT_TRUE(filter.Init(test::ResourcePath("google-wifi-3mbps", "rx"))); + RunFor(300 * 1000); +} + +class MultiFlowBweSimulation : public BweSimulation { + public: + MultiFlowBweSimulation() : BweSimulation() {} + virtual ~MultiFlowBweSimulation() {} + + private: + DISALLOW_COPY_AND_ASSIGN(MultiFlowBweSimulation); +}; + +INSTANTIATE_TEST_CASE_P(VideoSendersTest, MultiFlowBweSimulation, + ::testing::Values(MakeMultiFlowBweTestConfig(3))); + +TEST_P(MultiFlowBweSimulation, SelfFairnessTest) { + VerboseLogging(true); + const int kAllFlowIds[] = {0, 1, 2}; + const size_t kNumFlows = sizeof(kAllFlowIds) / sizeof(kAllFlowIds[0]); + scoped_ptr senders[kNumFlows]; + for (size_t i = 0; i < kNumFlows; ++i) { + senders[i].reset(new AdaptiveVideoSender(kAllFlowIds[i], this, 30, 300, 0, + 0)); + } + // Second and third flow. + ChokeFilter choke(this, CreateFlowIds(&kAllFlowIds[1], 2)); + choke.SetCapacity(1500); + // First flow. + ChokeFilter choke2(this, CreateFlowIds(&kAllFlowIds[0], 1)); + choke2.SetCapacity(1000); + + scoped_ptr rate_counters[kNumFlows]; + for (size_t i = 0; i < kNumFlows; ++i) { + rate_counters[i].reset(new RateCounterFilter( + this, CreateFlowIds(&kAllFlowIds[i], 1), "receiver_input")); + } + RunFor(30 * 60 * 1000); +} #endif // BWE_TEST_LOGGING_COMPILE_TIME_ENABLE } // namespace bwe } // namespace testing diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h 2015-02-03 14:33:36.000000000 +0000 @@ -25,6 +25,11 @@ class Clock; +enum RateControlType { + kMimdControl, + kAimdControl +}; + // RemoteBitrateObserver is used to signal changes in bitrate estimates for // the incoming streams. class RemoteBitrateObserver { @@ -66,6 +71,7 @@ // estimate and the over-use detector. If an over-use is detected the // remote bitrate estimate will be updated. Note that |payload_size| is the // packet size excluding headers. + // Note that |arrival_time_ms| can be of an arbitrary time base. virtual void IncomingPacket(int64_t arrival_time_ms, int payload_size, const RTPHeader& header) = 0; @@ -94,6 +100,7 @@ virtual RemoteBitrateEstimator* Create( RemoteBitrateObserver* observer, Clock* clock, + RateControlType control_type, uint32_t min_bitrate_bps) const; }; @@ -105,6 +112,7 @@ virtual RemoteBitrateEstimator* Create( RemoteBitrateObserver* observer, Clock* clock, + RateControlType control_type, uint32_t min_bitrate_bps) const; }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/include/rtp_to_ntp.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/include/rtp_to_ntp.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/include/rtp_to_ntp.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/include/rtp_to_ntp.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_RTP_TO_NTP_H_ -#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_RTP_TO_NTP_H_ - -#include - -#include "webrtc/typedefs.h" - -namespace webrtc { - -namespace synchronization { - -struct RtcpMeasurement { - RtcpMeasurement(); - RtcpMeasurement(uint32_t ntp_secs, uint32_t ntp_frac, uint32_t timestamp); - uint32_t ntp_secs; - uint32_t ntp_frac; - uint32_t rtp_timestamp; -}; - -typedef std::list RtcpList; - -// Converts an RTP timestamp to the NTP domain in milliseconds using two -// (RTP timestamp, NTP timestamp) pairs. -bool RtpToNtpMs(int64_t rtp_timestamp, const RtcpList& rtcp, - int64_t* timestamp_in_ms); - -// Returns 1 there has been a forward wrap around, 0 if there has been no wrap -// around and -1 if there has been a backwards wrap around (i.e. reordering). -int CheckForWrapArounds(uint32_t rtp_timestamp, uint32_t rtcp_rtp_timestamp); -} // namespace synchronization -} // namespace webrtc - -#endif // WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_RTP_TO_NTP_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/overuse_detector.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/overuse_detector.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/overuse_detector.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/overuse_detector.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,19 +10,12 @@ #include #include // fabsf -#if _WIN32 -#include -#endif #include "webrtc/modules/remote_bitrate_estimator/overuse_detector.h" #include "webrtc/modules/remote_bitrate_estimator/remote_rate_control.h" #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" #include "webrtc/system_wrappers/interface/trace.h" -#ifdef WEBRTC_BWE_MATLAB -extern MatlabEngine eng; // global variable defined elsewhere -#endif - enum { kOverUsingTimeThreshold = 100 }; enum { kMinFramePeriodHistoryLength = 60 }; @@ -43,74 +36,20 @@ prev_offset_(0.0), time_over_using_(-1), over_use_counter_(0), - hypothesis_(kBwNormal), - time_of_last_received_packet_(-1) -#ifdef WEBRTC_BWE_MATLAB - , plots_() -#endif - { + hypothesis_(kBwNormal) { memcpy(E_, options_.initial_e, sizeof(E_)); memcpy(process_noise_, options_.initial_process_noise, sizeof(process_noise_)); } OveruseDetector::~OveruseDetector() { -#ifdef WEBRTC_BWE_MATLAB - if (plots_.plot1_) { - eng.DeletePlot(plots_.plot1_); - plots_.plot1_ = NULL; - } - if (plots_.plot2_) { - eng.DeletePlot(plots_.plot2_); - plots_.plot2_ = NULL; - } - if (plots_.plot3_) { - eng.DeletePlot(plots_.plot3_); - plots_.plot3_ = NULL; - } - if (plots_.plot4_) { - eng.DeletePlot(plots_.plot4_); - plots_.plot4_ = NULL; - } -#endif - ts_delta_hist_.clear(); } void OveruseDetector::Update(uint16_t packet_size, int64_t timestamp_ms, uint32_t timestamp, - const int64_t now_ms) { - time_of_last_received_packet_ = now_ms; -#ifdef WEBRTC_BWE_MATLAB - // Create plots - const int64_t startTimeMs = nowMS; - if (plots_.plot1_ == NULL) { - plots_.plot1_ = eng.NewPlot(new MatlabPlot()); - plots_.plot1_->AddLine(1000, "b.", "scatter"); - } - if (plots_.plot2_ == NULL) { - plots_.plot2_ = eng.NewPlot(new MatlabPlot()); - plots_.plot2_->AddTimeLine(30, "b", "offset", startTimeMs); - plots_.plot2_->AddTimeLine(30, "r--", "limitPos", startTimeMs); - plots_.plot2_->AddTimeLine(30, "k.", "trigger", startTimeMs); - plots_.plot2_->AddTimeLine(30, "ko", "detection", startTimeMs); - // plots_.plot2_->AddTimeLine(30, "g", "slowMean", startTimeMs); - } - if (plots_.plot3_ == NULL) { - plots_.plot3_ = eng.NewPlot(new MatlabPlot()); - plots_.plot3_->AddTimeLine(30, "b", "noiseVar", startTimeMs); - } - if (plots_.plot4_ == NULL) { - plots_.plot4_ = eng.NewPlot(new MatlabPlot()); - // plots_.plot4_->AddTimeLine(60, "b", "p11", startTimeMs); - // plots_.plot4_->AddTimeLine(60, "r", "p12", startTimeMs); - plots_.plot4_->AddTimeLine(60, "g", "p22", startTimeMs); - // plots_.plot4_->AddTimeLine(60, "g--", "p22_hat", startTimeMs); - // plots_.plot4_->AddTimeLine(30, "b.-", "deltaFs", startTimeMs); - } - -#endif + const int64_t arrival_time_ms) { bool new_timestamp = (timestamp != current_frame_.timestamp); if (timestamp_ms >= 0) { if (prev_frame_.timestamp_ms == -1 && current_frame_.timestamp_ms == -1) { @@ -127,8 +66,6 @@ return; } else if (new_timestamp) { // First packet of a later frame, the previous frame sample is ready. - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, "Frame complete at %I64i", - current_frame_.complete_time_ms); if (prev_frame_.complete_time_ms >= 0) { // This is our second frame. int64_t t_delta = 0; double ts_delta = 0; @@ -143,7 +80,7 @@ } // Accumulate the frame size current_frame_.size += packet_size; - current_frame_.complete_time_ms = now_ms; + current_frame_.complete_time_ms = arrival_time_ms; } BandwidthUsage OveruseDetector::State() const { @@ -168,10 +105,6 @@ } } -int64_t OveruseDetector::time_of_last_received_packet() const { - return time_of_last_received_packet_; -} - void OveruseDetector::SwitchTimeBase() { current_frame_.size = 0; current_frame_.complete_time_ms = -1; @@ -279,39 +212,11 @@ E_[0][0] * E_[1][1] - E_[0][1] * E_[1][0] >= 0 && E_[0][0] >= 0); -#ifdef WEBRTC_BWE_MATLAB - // plots_.plot4_->Append("p11",E_[0][0]); - // plots_.plot4_->Append("p12",E_[0][1]); - plots_.plot4_->Append("p22", E_[1][1]); - // plots_.plot4_->Append("p22_hat", 0.5*(process_noise_[1] + - // sqrt(process_noise_[1]*(process_noise_[1] + 4*var_noise_)))); - // plots_.plot4_->Append("deltaFs", fsDelta); - plots_.plot4_->Plot(); -#endif slope_ = slope_ + K[0] * residual; prev_offset_ = offset_; offset_ = offset_ + K[1] * residual; Detect(ts_delta); - -#ifdef WEBRTC_BWE_MATLAB - plots_.plot1_->Append("scatter", - static_cast(current_frame_.size) - prev_frame_.size, - static_cast(t_delta - ts_delta)); - plots_.plot1_->MakeTrend("scatter", "slope", slope_, offset_, "k-"); - plots_.plot1_->MakeTrend("scatter", "thresholdPos", - slope_, offset_ + 2 * sqrt(var_noise_), "r-"); - plots_.plot1_->MakeTrend("scatter", "thresholdNeg", - slope_, offset_ - 2 * sqrt(var_noise_), "r-"); - plots_.plot1_->Plot(); - - plots_.plot2_->Append("offset", offset_); - plots_.plot2_->Append("limitPos", threshold_/BWE_MIN(num_of_deltas_, 60)); - plots_.plot2_->Plot(); - - plots_.plot3_->Append("noiseVar", var_noise_); - plots_.plot3_->Plot(); -#endif } double OveruseDetector::UpdateMinFramePeriod(double ts_delta) { @@ -373,38 +278,17 @@ if (time_over_using_ > kOverUsingTimeThreshold && over_use_counter_ > 1) { if (offset_ >= prev_offset_) { -#ifdef _DEBUG - if (hypothesis_ != kBwOverusing) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, "BWE: kBwOverusing"); - } -#endif time_over_using_ = 0; over_use_counter_ = 0; hypothesis_ = kBwOverusing; -#ifdef WEBRTC_BWE_MATLAB - plots_.plot2_->Append("detection", offset_); // plot it later -#endif } } -#ifdef WEBRTC_BWE_MATLAB - plots_.plot2_->Append("trigger", offset_); // plot it later -#endif } else { -#ifdef _DEBUG - if (hypothesis_ != kBwUnderusing) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, "BWE: kBwUnderUsing"); - } -#endif time_over_using_ = -1; over_use_counter_ = 0; hypothesis_ = kBwUnderusing; } } else { -#ifdef _DEBUG - if (hypothesis_ != kBwNormal) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, "BWE: kBwNormal"); - } -#endif time_over_using_ = -1; over_use_counter_ = 0; hypothesis_ = kBwNormal; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/overuse_detector.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/overuse_detector.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/overuse_detector.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/overuse_detector.h 2015-02-03 14:33:36.000000000 +0000 @@ -16,10 +16,6 @@ #include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h" #include "webrtc/typedefs.h" -#ifdef WEBRTC_BWE_MATLAB -#include "webrtc/modules/rtp_rtcp/test/BWEStandAlone/MatlabPlot.h" -#endif - namespace webrtc { enum RateControlRegion; @@ -32,11 +28,10 @@ void Update(uint16_t packet_size, int64_t timestamp_ms, uint32_t rtp_timestamp, - int64_t now_ms); + int64_t arrival_time_ms); BandwidthUsage State() const; double NoiseVar() const; void SetRateControlRegion(RateControlRegion region); - int64_t time_of_last_received_packet() const; private: struct FrameSample { @@ -52,16 +47,6 @@ int64_t timestamp_ms; }; - struct DebugPlots { -#ifdef WEBRTC_BWE_MATLAB - DebugPlots() : plot1(NULL), plot2(NULL), plot3(NULL), plot4(NULL) {} - MatlabPlot* plot1; - MatlabPlot* plot2; - MatlabPlot* plot3; - MatlabPlot* plot4; -#endif - }; - // Returns true if |timestamp| represent a time which is later than // |prev_timestamp|. static bool InOrderTimestamp(uint32_t timestamp, uint32_t prev_timestamp); @@ -103,10 +88,6 @@ double time_over_using_; uint16_t over_use_counter_; BandwidthUsage hypothesis_; - int64_t time_of_last_received_packet_; -#ifdef WEBRTC_BWE_MATLAB - DebugPlots plots_; -#endif }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -1,5 +1,11 @@ -pwestin@webrtc.org stefan@webrtc.org henrik.lundin@webrtc.org mflodman@webrtc.org -asapersson@webrtc.org \ No newline at end of file +asapersson@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/rate_statistics.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/rate_statistics.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/rate_statistics.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/rate_statistics.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,6 +10,8 @@ #include "webrtc/modules/remote_bitrate_estimator/rate_statistics.h" +#include + namespace webrtc { RateStatistics::RateStatistics(uint32_t window_size_ms, float scale) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/rate_statistics.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/rate_statistics.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/rate_statistics.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/rate_statistics.h 2015-02-03 14:33:36.000000000 +0000 @@ -34,7 +34,7 @@ // Counters are kept in buckets (circular buffer), with one bucket // per millisecond. const int num_buckets_; - scoped_array buckets_; + scoped_ptr buckets_; // Total count recorded in buckets. uint32_t accumulated_count_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi 2015-02-03 14:33:36.000000000 +0000 @@ -21,10 +21,8 @@ 'sources': [ 'include/bwe_defines.h', 'include/remote_bitrate_estimator.h', - 'include/rtp_to_ntp.h', 'rate_statistics.cc', 'rate_statistics.h', - 'rtp_to_ntp.cc', ], # source }, { @@ -47,6 +45,7 @@ ], 'dependencies': [ '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers_default', 'bwe_tools_util', 'rtp_rtcp', ], @@ -57,8 +56,8 @@ }, 'sources': [ 'tools/rtp_to_text.cc', - '<(webrtc_root)/modules/video_coding/main/test/rtp_file_reader.cc', - '<(webrtc_root)/modules/video_coding/main/test/rtp_file_reader.h', + '<(webrtc_root)/test/rtp_file_reader.cc', + '<(webrtc_root)/test/rtp_file_reader.h', ], # source }, { @@ -69,6 +68,7 @@ ], 'dependencies': [ '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers_default', 'bwe_tools_util', 'rtp_rtcp', ], @@ -79,8 +79,8 @@ }, 'sources': [ 'tools/bwe_rtp_play.cc', - '<(webrtc_root)/modules/video_coding/main/test/rtp_file_reader.cc', - '<(webrtc_root)/modules/video_coding/main/test/rtp_file_reader.h', + '<(webrtc_root)/test/rtp_file_reader.cc', + '<(webrtc_root)/test/rtp_file_reader.h', ], # source }, ], # targets diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc 2015-02-03 14:33:36.000000000 +0000 @@ -16,8 +16,8 @@ #include "webrtc/modules/remote_bitrate_estimator/remote_rate_control.h" #include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/system_wrappers/interface/trace.h" #include "webrtc/typedefs.h" namespace webrtc { @@ -59,10 +59,27 @@ ReceiveBandwidthEstimatorStats* output) const OVERRIDE; private: - typedef std::map SsrcOveruseDetectorMap; + // Map from SSRC to over-use detector and last incoming packet time in + // milliseconds, taken from clock_. + typedef std::map > + SsrcOveruseDetectorMap; + + static OveruseDetector* GetDetector( + const SsrcOveruseDetectorMap::iterator it) { + return &it->second.first; + } + + static int64_t GetPacketTimeMs(const SsrcOveruseDetectorMap::iterator it) { + return it->second.second; + } + + static void SetPacketTimeMs(SsrcOveruseDetectorMap::iterator it, + int64_t time_ms) { + it->second.second = time_ms; + } // Triggers a new estimate calculation. - void UpdateEstimate(int64_t time_now); + void UpdateEstimate(int64_t now_ms); void GetSsrcs(std::vector* ssrcs) const; @@ -95,6 +112,7 @@ uint32_t ssrc = header.ssrc; uint32_t rtp_timestamp = header.timestamp + header.extension.transmissionTimeOffset; + int64_t now_ms = clock_->TimeInMilliseconds(); CriticalSectionScoped cs(crit_sect_.get()); SsrcOveruseDetectorMap::iterator it = overuse_detectors_.find(ssrc); if (it == overuse_detectors_.end()) { @@ -105,22 +123,23 @@ // automatically cleaned up when we have one RemoteBitrateEstimator per REMB // group. std::pair insert_result = - overuse_detectors_.insert(std::make_pair(ssrc, OveruseDetector( - OverUseDetectorOptions()))); + overuse_detectors_.insert(std::make_pair(ssrc, + std::make_pair(OveruseDetector(OverUseDetectorOptions()), now_ms))); it = insert_result.first; } - OveruseDetector* overuse_detector = &it->second; - incoming_bitrate_.Update(payload_size, arrival_time_ms); + SetPacketTimeMs(it, now_ms); + OveruseDetector* overuse_detector = GetDetector(it); + incoming_bitrate_.Update(payload_size, now_ms); const BandwidthUsage prior_state = overuse_detector->State(); overuse_detector->Update(payload_size, -1, rtp_timestamp, arrival_time_ms); if (overuse_detector->State() == kBwOverusing) { - unsigned int incoming_bitrate = incoming_bitrate_.Rate(arrival_time_ms); + unsigned int incoming_bitrate = incoming_bitrate_.Rate(now_ms); if (prior_state != kBwOverusing || - remote_rate_.TimeToReduceFurther(arrival_time_ms, incoming_bitrate)) { + remote_rate_.TimeToReduceFurther(now_ms, incoming_bitrate)) { // The first overuse should immediately trigger a new estimate. // We also have to update the estimate immediately if we are overusing // and the target bitrate is too high compared to what we are receiving. - UpdateEstimate(arrival_time_ms); + UpdateEstimate(now_ms); } } } @@ -129,8 +148,9 @@ if (TimeUntilNextProcess() > 0) { return 0; } - UpdateEstimate(clock_->TimeInMilliseconds()); - last_process_time_ = clock_->TimeInMilliseconds(); + int64_t now_ms = clock_->TimeInMilliseconds(); + UpdateEstimate(now_ms); + last_process_time_ = now_ms; return 0; } @@ -141,25 +161,24 @@ return last_process_time_ + kProcessIntervalMs - clock_->TimeInMilliseconds(); } -void RemoteBitrateEstimatorSingleStream::UpdateEstimate(int64_t time_now) { +void RemoteBitrateEstimatorSingleStream::UpdateEstimate(int64_t now_ms) { CriticalSectionScoped cs(crit_sect_.get()); BandwidthUsage bw_state = kBwNormal; double sum_noise_var = 0.0; SsrcOveruseDetectorMap::iterator it = overuse_detectors_.begin(); while (it != overuse_detectors_.end()) { - const int64_t time_of_last_received_packet = - it->second.time_of_last_received_packet(); - if (time_of_last_received_packet >= 0 && - time_now - time_of_last_received_packet > kStreamTimeOutMs) { + if (GetPacketTimeMs(it) >= 0 && + now_ms - GetPacketTimeMs(it) > kStreamTimeOutMs) { // This over-use detector hasn't received packets for |kStreamTimeOutMs| // milliseconds and is considered stale. overuse_detectors_.erase(it++); } else { - sum_noise_var += it->second.NoiseVar(); + OveruseDetector* overuse_detector = GetDetector(it); + sum_noise_var += overuse_detector->NoiseVar(); // Make sure that we trigger an over-use if any of the over-use detectors // is detecting over-use. - if (it->second.State() > bw_state) { - bw_state = it->second.State(); + if (overuse_detector->State() > bw_state) { + bw_state = overuse_detector->State(); } ++it; } @@ -172,17 +191,17 @@ double mean_noise_var = sum_noise_var / static_cast(overuse_detectors_.size()); const RateControlInput input(bw_state, - incoming_bitrate_.Rate(time_now), + incoming_bitrate_.Rate(now_ms), mean_noise_var); - const RateControlRegion region = remote_rate_.Update(&input, time_now); - unsigned int target_bitrate = remote_rate_.UpdateBandwidthEstimate(time_now); + const RateControlRegion region = remote_rate_.Update(&input, now_ms); + unsigned int target_bitrate = remote_rate_.UpdateBandwidthEstimate(now_ms); if (remote_rate_.ValidEstimate()) { std::vector ssrcs; GetSsrcs(&ssrcs); observer_->OnReceiveBitrateChanged(ssrcs, target_bitrate); } for (it = overuse_detectors_.begin(); it != overuse_detectors_.end(); ++it) { - it->second.SetRateControlRegion(region); + GetDetector(it)->SetRateControlRegion(region); } } @@ -234,9 +253,9 @@ RemoteBitrateEstimator* RemoteBitrateEstimatorFactory::Create( RemoteBitrateObserver* observer, Clock* clock, + RateControlType control_type, uint32_t min_bitrate_bps) const { - WEBRTC_TRACE(kTraceStateInfo, kTraceRemoteBitrateEstimator, -1, - "RemoteBitrateEstimatorFactory: Instantiating."); + LOG(LS_INFO) << "RemoteBitrateEstimatorFactory: Instantiating."; return new RemoteBitrateEstimatorSingleStream(observer, clock, min_bitrate_bps); } @@ -244,9 +263,10 @@ RemoteBitrateEstimator* AbsoluteSendTimeRemoteBitrateEstimatorFactory::Create( RemoteBitrateObserver* observer, Clock* clock, + RateControlType control_type, uint32_t min_bitrate_bps) const { - WEBRTC_TRACE(kTraceStateInfo, kTraceRemoteBitrateEstimator, -1, - "AbsoluteSendTimeRemoteBitrateEstimatorFactory: Instantiating."); + LOG(LS_INFO) << "AbsoluteSendTimeRemoteBitrateEstimatorFactory: " + "Instantiating."; return new RemoteBitrateEstimatorSingleStream(observer, clock, min_bitrate_bps); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,8 +10,8 @@ #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/constructormagic.h" #include "webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" namespace webrtc { @@ -24,6 +24,7 @@ bitrate_estimator_.reset(RemoteBitrateEstimatorFactory().Create( bitrate_observer_.get(), &clock_, + kMimdControl, kRemoteBitrateEstimatorMinBitrateBps)); } protected: @@ -35,49 +36,49 @@ } TEST_F(RemoteBitrateEstimatorSingleTest, RateIncreaseReordering) { - RateIncreaseReorderingTestHelper(); + RateIncreaseReorderingTestHelper(498136); } TEST_F(RemoteBitrateEstimatorSingleTest, RateIncreaseRtpTimestamps) { - RateIncreaseRtpTimestampsTestHelper(); + RateIncreaseRtpTimestampsTestHelper(1621); } // Verify that the time it takes for the estimator to reduce the bitrate when // the capacity is tightened stays the same. TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropOneStream) { - CapacityDropTestHelper(1, false, 956214, 367); + CapacityDropTestHelper(1, false, 367); } // Verify that the time it takes for the estimator to reduce the bitrate when // the capacity is tightened stays the same. This test also verifies that we // handle wrap-arounds in this scenario. TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropOneStreamWrap) { - CapacityDropTestHelper(1, true, 956214, 367); + CapacityDropTestHelper(1, true, 367); } // Verify that the time it takes for the estimator to reduce the bitrate when // the capacity is tightened stays the same. This test also verifies that we // handle wrap-arounds in this scenario. This is a multi-stream test. TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropTwoStreamsWrap) { - CapacityDropTestHelper(2, true, 927088, 267); + CapacityDropTestHelper(2, true, 267); } // Verify that the time it takes for the estimator to reduce the bitrate when // the capacity is tightened stays the same. This test also verifies that we // handle wrap-arounds in this scenario. This is a multi-stream test. TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropThreeStreamsWrap) { - CapacityDropTestHelper(3, true, 920944, 333); + CapacityDropTestHelper(3, true, 333); } TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropThirteenStreamsWrap) { - CapacityDropTestHelper(13, true, 938944, 300); + CapacityDropTestHelper(13, true, 300); } TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropNineteenStreamsWrap) { - CapacityDropTestHelper(19, true, 926718, 300); + CapacityDropTestHelper(19, true, 300); } TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropThirtyStreamsWrap) { - CapacityDropTestHelper(30, true, 927016, 300); + CapacityDropTestHelper(30, true, 300); } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimators_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimators_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimators_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimators_test.cc 2015-02-03 14:33:36.000000000 +0000 @@ -8,72 +8,95 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include + #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" #include "webrtc/modules/remote_bitrate_estimator/test/bwe_test.h" #include "webrtc/test/testsupport/fileutils.h" +#include "webrtc/test/testsupport/perf_test.h" + +using std::string; namespace webrtc { namespace testing { namespace bwe { -std::vector VideoSenderFactories(uint32_t count) { - static const VideoPacketSenderFactory factories[] = { - VideoPacketSenderFactory(30.00f, 150, 0x1234, 0.13f), - VideoPacketSenderFactory(15.00f, 500, 0x2345, 0.16f), - VideoPacketSenderFactory(30.00f, 1200, 0x3456, 0.26f), - VideoPacketSenderFactory(7.49f, 150, 0x4567, 0.05f), - VideoPacketSenderFactory(7.50f, 150, 0x5678, 0.15f), - VideoPacketSenderFactory(7.51f, 150, 0x6789, 0.25f), - VideoPacketSenderFactory(15.02f, 150, 0x7890, 0.27f), - VideoPacketSenderFactory(15.03f, 150, 0x8901, 0.38f), - VideoPacketSenderFactory(30.02f, 150, 0x9012, 0.39f), - VideoPacketSenderFactory(30.03f, 150, 0x0123, 0.52f) - }; - - assert(count <= sizeof(factories) / sizeof(factories[0])); - - std::vector result; - for (uint32_t i = 0; i < count; ++i) { - result.push_back(&factories[i]); - } - - return result; -} +enum Estimator { kAbsSendTime, kTransmissionOffset }; -std::vector EstimatorConfigs() { +BweTestConfig::EstimatorConfig EstimatorConfigs(Estimator estimator, + int flow_id) { static const RemoteBitrateEstimatorFactory factories[] = { RemoteBitrateEstimatorFactory(), AbsoluteSendTimeRemoteBitrateEstimatorFactory() }; - - std::vector result; - result.push_back(BweTestConfig::EstimatorConfig("TOF", &factories[0])); - result.push_back(BweTestConfig::EstimatorConfig("AST", &factories[1])); - return result; -} - -BweTestConfig MakeBweTestConfig(uint32_t sender_count) { - BweTestConfig result = { - VideoSenderFactories(sender_count), EstimatorConfigs() - }; + switch (estimator) { + case kTransmissionOffset: + return BweTestConfig::EstimatorConfig("TOF", flow_id, &factories[0], + kMimdControl, false, false); + case kAbsSendTime: + return BweTestConfig::EstimatorConfig("AST", flow_id, &factories[1], + kMimdControl, false, false); + } + assert(false); + return BweTestConfig::EstimatorConfig(); +} + +struct DefaultBweTestConfig { + BweTestConfig bwe_test_config; + size_t number_of_senders; +}; + +DefaultBweTestConfig MakeBweTestConfig(uint32_t sender_count, + Estimator estimator) { + DefaultBweTestConfig result; + result.bwe_test_config.estimator_configs.push_back( + EstimatorConfigs(estimator, 0)); + result.number_of_senders = sender_count; return result; } -INSTANTIATE_TEST_CASE_P(VideoSendersTest, BweTest, - ::testing::Values(MakeBweTestConfig(1), - MakeBweTestConfig(3))); +class DefaultBweTest : public BweTest, + public ::testing::TestWithParam { + public: + DefaultBweTest() : packet_senders_() {} + virtual ~DefaultBweTest() {} + + virtual void SetUp() { + const DefaultBweTestConfig& config = GetParam(); + SetupTestFromConfig(config.bwe_test_config); + for (size_t i = 0; i < config.number_of_senders; ++i) { + packet_senders_.push_back(new VideoSender(0, this, 30, 300, 0, 0)); + } + } + + virtual void TearDown() { + while (!packet_senders_.empty()) { + delete packet_senders_.front(); + packet_senders_.pop_front(); + } + } + + protected: + std::list packet_senders_; +}; + +INSTANTIATE_TEST_CASE_P(VideoSendersTest, DefaultBweTest, + ::testing::Values(MakeBweTestConfig(1, kAbsSendTime), + MakeBweTestConfig(3, kAbsSendTime), + MakeBweTestConfig(1, kTransmissionOffset), + MakeBweTestConfig(3, kTransmissionOffset))); -TEST_P(BweTest, UnlimitedSpeed) { +TEST_P(DefaultBweTest, UnlimitedSpeed) { VerboseLogging(false); RunFor(10 * 60 * 1000); } -TEST_P(BweTest, SteadyLoss) { +TEST_P(DefaultBweTest, DISABLED_SteadyLoss) { LossFilter loss(this); loss.SetLoss(20.0); RunFor(10 * 60 * 1000); } -TEST_P(BweTest, IncreasingLoss1) { +TEST_P(DefaultBweTest, IncreasingLoss1) { LossFilter loss(this); for (int i = 0; i < 76; ++i) { loss.SetLoss(i); @@ -81,13 +104,13 @@ } } -TEST_P(BweTest, SteadyDelay) { +TEST_P(DefaultBweTest, SteadyDelay) { DelayFilter delay(this); delay.SetDelay(1000); RunFor(10 * 60 * 1000); } -TEST_P(BweTest, IncreasingDelay1) { +TEST_P(DefaultBweTest, DISABLED_IncreasingDelay1) { DelayFilter delay(this); RunFor(10 * 60 * 1000); for (int i = 0; i < 30 * 2; ++i) { @@ -97,7 +120,7 @@ RunFor(10 * 60 * 1000); } -TEST_P(BweTest, IncreasingDelay2) { +TEST_P(DefaultBweTest, IncreasingDelay2) { DelayFilter delay(this); RateCounterFilter counter(this); RunFor(1 * 60 * 1000); @@ -109,7 +132,7 @@ RunFor(10 * 60 * 1000); } -TEST_P(BweTest, JumpyDelay1) { +TEST_P(DefaultBweTest, JumpyDelay1) { DelayFilter delay(this); RunFor(10 * 60 * 1000); for (int i = 1; i < 200; ++i) { @@ -122,14 +145,14 @@ RunFor(10 * 60 * 1000); } -TEST_P(BweTest, SteadyJitter) { +TEST_P(DefaultBweTest, SteadyJitter) { JitterFilter jitter(this); RateCounterFilter counter(this); jitter.SetJitter(20); RunFor(2 * 60 * 1000); } -TEST_P(BweTest, IncreasingJitter1) { +TEST_P(DefaultBweTest, IncreasingJitter1) { JitterFilter jitter(this); for (int i = 0; i < 2 * 60 * 2; ++i) { jitter.SetJitter(i); @@ -138,7 +161,7 @@ RunFor(10 * 60 * 1000); } -TEST_P(BweTest, IncreasingJitter2) { +TEST_P(DefaultBweTest, IncreasingJitter2) { JitterFilter jitter(this); RunFor(30 * 1000); for (int i = 1; i < 51; ++i) { @@ -149,13 +172,13 @@ RunFor(10 * 60 * 1000); } -TEST_P(BweTest, SteadyReorder) { +TEST_P(DefaultBweTest, SteadyReorder) { ReorderFilter reorder(this); reorder.SetReorder(20.0); RunFor(10 * 60 * 1000); } -TEST_P(BweTest, IncreasingReorder1) { +TEST_P(DefaultBweTest, IncreasingReorder1) { ReorderFilter reorder(this); for (int i = 0; i < 76; ++i) { reorder.SetReorder(i); @@ -163,13 +186,13 @@ } } -TEST_P(BweTest, SteadyChoke) { +TEST_P(DefaultBweTest, DISABLED_SteadyChoke) { ChokeFilter choke(this); choke.SetCapacity(140); RunFor(10 * 60 * 1000); } -TEST_P(BweTest, IncreasingChoke1) { +TEST_P(DefaultBweTest, DISABLED_IncreasingChoke1) { ChokeFilter choke(this); for (int i = 1200; i >= 100; i -= 100) { choke.SetCapacity(i); @@ -177,7 +200,7 @@ } } -TEST_P(BweTest, IncreasingChoke2) { +TEST_P(DefaultBweTest, DISABLED_IncreasingChoke2) { ChokeFilter choke(this); RunFor(60 * 1000); for (int i = 1200; i >= 100; i -= 20) { @@ -186,7 +209,7 @@ } } -TEST_P(BweTest, Multi1) { +TEST_P(DefaultBweTest, DISABLED_Multi1) { DelayFilter delay(this); ChokeFilter choke(this); RateCounterFilter counter(this); @@ -201,7 +224,7 @@ RunFor(5 * 60 * 1000); } -TEST_P(BweTest, Multi2) { +TEST_P(DefaultBweTest, Multi2) { ChokeFilter choke(this); JitterFilter jitter(this); RateCounterFilter counter(this); @@ -209,6 +232,108 @@ jitter.SetJitter(120); RunFor(5 * 60 * 1000); } + +// This test fixture is used to instantiate tests running with adaptive video +// senders. +class BweFeedbackTest : public BweTest, + public ::testing::TestWithParam { + public: + BweFeedbackTest() : BweTest() {} + virtual ~BweFeedbackTest() {} + + virtual void SetUp() { + BweTestConfig config; + config.estimator_configs.push_back(EstimatorConfigs(kAbsSendTime, 0)); + SetupTestFromConfig(config); + } + + void PrintResults(double max_throughput_kbps, Stats throughput_kbps, + Stats delay_ms) { + double utilization = throughput_kbps.GetMean() / max_throughput_kbps; + webrtc::test::PrintResult("BwePerformance", + GetTestName(), + "Utilization", + utilization * 100.0, + "%", + false); + std::stringstream ss; + ss << throughput_kbps.GetStdDev() / throughput_kbps.GetMean(); + webrtc::test::PrintResult("BwePerformance", + GetTestName(), + "Utilization var coeff", + ss.str(), + "", + false); + webrtc::test::PrintResult("BwePerformance", + GetTestName(), + "Average delay", + delay_ms.AsString(), + "ms", + false); + } + + private: + DISALLOW_COPY_AND_ASSIGN(BweFeedbackTest); +}; + +TEST_F(BweFeedbackTest, Choke1000kbps500kbps1000kbps) { + AdaptiveVideoSender sender(0, this, 30, 300, 0, 0); + ChokeFilter filter(this); + RateCounterFilter counter(this, "receiver_input"); + const int kHighCapacityKbps = 1000; + const int kLowCapacityKbps = 500; + filter.SetCapacity(kHighCapacityKbps); + filter.SetMaxDelay(500); + RunFor(60 * 1000); + filter.SetCapacity(kLowCapacityKbps); + RunFor(60 * 1000); + filter.SetCapacity(kHighCapacityKbps); + RunFor(60 * 1000); + PrintResults((2 * kHighCapacityKbps + kLowCapacityKbps) / 3.0, + counter.GetBitrateStats(), filter.GetDelayStats()); +} + +TEST_F(BweFeedbackTest, Choke200kbps30kbps200kbps) { + AdaptiveVideoSender sender(0, this, 30, 300, 0, 0); + ChokeFilter filter(this); + RateCounterFilter counter(this, "receiver_input"); + const int kHighCapacityKbps = 200; + const int kLowCapacityKbps = 30; + filter.SetCapacity(kHighCapacityKbps); + filter.SetMaxDelay(500); + RunFor(60 * 1000); + filter.SetCapacity(kLowCapacityKbps); + RunFor(60 * 1000); + filter.SetCapacity(kHighCapacityKbps); + RunFor(60 * 1000); + + PrintResults((2 * kHighCapacityKbps + kLowCapacityKbps) / 3.0, + counter.GetBitrateStats(), filter.GetDelayStats()); +} + +TEST_F(BweFeedbackTest, Verizon4gDownlinkTest) { + AdaptiveVideoSender sender(0, this, 30, 300, 0, 0); + RateCounterFilter counter1(this, "sender_output"); + TraceBasedDeliveryFilter filter(this, "link_capacity"); + RateCounterFilter counter2(this, "receiver_input"); + ASSERT_TRUE(filter.Init(test::ResourcePath("verizon4g-downlink", "rx"))); + RunFor(22 * 60 * 1000); + PrintResults(filter.GetBitrateStats().GetMean(), counter2.GetBitrateStats(), + filter.GetDelayStats()); +} + +// webrtc:3277 +TEST_F(BweFeedbackTest, DISABLED_GoogleWifiTrace3Mbps) { + AdaptiveVideoSender sender(0, this, 30, 300, 0, 0); + RateCounterFilter counter1(this, "sender_output"); + TraceBasedDeliveryFilter filter(this, "link_capacity"); + filter.SetMaxDelay(500); + RateCounterFilter counter2(this, "receiver_input"); + ASSERT_TRUE(filter.Init(test::ResourcePath("google-wifi-3mbps", "rx"))); + RunFor(300 * 1000); + PrintResults(filter.GetBitrateStats().GetMean(), counter2.GetBitrateStats(), + filter.GetDelayStats()); +} } // namespace bwe } // namespace testing } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc 2015-02-03 14:33:36.000000000 +0000 @@ -14,7 +14,7 @@ namespace webrtc { -enum { kMtu = 1200 }; +enum { kMtu = 1200, kAcceptedBitrateErrorBps = 50000u }; namespace testing { @@ -225,8 +225,10 @@ memset(&header, 0, sizeof(header)); header.ssrc = ssrc; header.timestamp = rtp_timestamp; + header.extension.hasAbsoluteSendTime = true; header.extension.absoluteSendTime = absolute_send_time; - bitrate_estimator_->IncomingPacket(arrival_time, payload_size, header); + bitrate_estimator_->IncomingPacket(arrival_time + kArrivalTimeClockOffsetMs, + payload_size, header); } // Generates a frame of packets belonging to a stream at a given bitrate and @@ -245,6 +247,10 @@ while (!packets.empty()) { testing::RtpStream::RtpPacket* packet = packets.front(); bitrate_observer_->Reset(); + // The simulated clock should match the time of packet->arrival_time + // since both are used in IncomingPacket(). + clock_.AdvanceTimeMicroseconds(packet->arrival_time - + clock_.TimeInMicroseconds()); IncomingPacket(packet->ssrc, packet->size, (packet->arrival_time + 500) / 1000, @@ -256,8 +262,6 @@ overuse = true; EXPECT_LE(bitrate_observer_->latest_bitrate(), bitrate_bps); } - clock_.AdvanceTimeMicroseconds(packet->arrival_time - - clock_.TimeInMicroseconds()); delete packet; packets.pop_front(); } @@ -337,13 +341,18 @@ EXPECT_TRUE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_bps)); ASSERT_EQ(1u, ssrcs.size()); EXPECT_EQ(kDefaultSsrc, ssrcs.front()); - EXPECT_EQ(expected_converge_bitrate, bitrate_bps); + EXPECT_NEAR(expected_converge_bitrate, bitrate_bps, kAcceptedBitrateErrorBps); EXPECT_TRUE(bitrate_observer_->updated()); bitrate_observer_->Reset(); EXPECT_EQ(bitrate_observer_->latest_bitrate(), bitrate_bps); + bitrate_estimator_->RemoveStream(kDefaultSsrc); + EXPECT_TRUE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_bps)); + ASSERT_EQ(0u, ssrcs.size()); + EXPECT_EQ(0u, bitrate_bps); } -void RemoteBitrateEstimatorTest::RateIncreaseReorderingTestHelper() { +void RemoteBitrateEstimatorTest::RateIncreaseReorderingTestHelper( + uint32_t expected_bitrate_bps) { const int kFramerate = 50; // 50 fps to avoid rounding errors. const int kFrameIntervalMs = 1000 / kFramerate; const uint32_t kFrameIntervalAbsSendTime = AbsSendTime(1, kFramerate); @@ -364,7 +373,9 @@ } bitrate_estimator_->Process(); EXPECT_TRUE(bitrate_observer_->updated()); - EXPECT_EQ(498136u, bitrate_observer_->latest_bitrate()); + EXPECT_NEAR(expected_bitrate_bps, + bitrate_observer_->latest_bitrate(), + kAcceptedBitrateErrorBps); for (int i = 0; i < 10; ++i) { clock_.AdvanceTimeMilliseconds(2 * kFrameIntervalMs); timestamp += 2 * 90 * kFrameIntervalMs; @@ -379,15 +390,17 @@ } bitrate_estimator_->Process(); EXPECT_TRUE(bitrate_observer_->updated()); - EXPECT_EQ(498136u, bitrate_observer_->latest_bitrate()); + EXPECT_NEAR(expected_bitrate_bps, + bitrate_observer_->latest_bitrate(), + kAcceptedBitrateErrorBps); } // Make sure we initially increase the bitrate as expected. -void RemoteBitrateEstimatorTest::RateIncreaseRtpTimestampsTestHelper() { +void RemoteBitrateEstimatorTest::RateIncreaseRtpTimestampsTestHelper( + int expected_iterations) { // This threshold corresponds approximately to increasing linearly with // bitrate(i) = 1.04 * bitrate(i-1) + 1000 // until bitrate(i) > 500000, with bitrate(1) ~= 30000. - const int kExpectedIterations = 1621; unsigned int bitrate_bps = 30000; int iterations = 0; AddDefaultStream(); @@ -404,15 +417,14 @@ bitrate_observer_->Reset(); } ++iterations; - ASSERT_LE(iterations, kExpectedIterations); + ASSERT_LE(iterations, expected_iterations); } - ASSERT_EQ(kExpectedIterations, iterations); + ASSERT_EQ(expected_iterations, iterations); } void RemoteBitrateEstimatorTest::CapacityDropTestHelper( int number_of_streams, bool wrap_time_stamp, - unsigned int expected_converge_bitrate, unsigned int expected_bitrate_drop_delta) { const int kFramerate = 30; const int kStartBitrate = 900e3; @@ -422,14 +434,11 @@ const unsigned int kReducedCapacityBps = 500e3; int steady_state_time = 0; - int expected_overuse_start_time = 0; if (number_of_streams <= 1) { steady_state_time = 10; - expected_overuse_start_time = 10000; AddDefaultStream(); } else { steady_state_time = 8 * number_of_streams; - expected_overuse_start_time = 8000; int bitrate_sum = 0; int kBitrateDenom = number_of_streams * (number_of_streams - 1); for (int i = 0; i < number_of_streams; i++) { @@ -463,13 +472,12 @@ kMinExpectedBitrate, kMaxExpectedBitrate, kInitialCapacityBps); - EXPECT_EQ(expected_converge_bitrate, bitrate_bps); + EXPECT_NEAR(kInitialCapacityBps, bitrate_bps, 100000u); bitrate_observer_->Reset(); // Reduce the capacity and verify the decrease time. stream_generator_->set_capacity_bps(kReducedCapacityBps); int64_t overuse_start_time = clock_.TimeInMilliseconds(); - EXPECT_EQ(expected_overuse_start_time, overuse_start_time); int64_t bitrate_drop_time = -1; for (int i = 0; i < 100 * number_of_streams; ++i) { GenerateAndProcessFrame(kDefaultSsrc, bitrate_bps); @@ -486,5 +494,21 @@ EXPECT_EQ(expected_bitrate_drop_delta, bitrate_drop_time - overuse_start_time); + + // Remove stream one by one. + unsigned int latest_bps = 0; + std::vector ssrcs; + for (int i = 0; i < number_of_streams; i++) { + EXPECT_TRUE(bitrate_estimator_->LatestEstimate(&ssrcs, &latest_bps)); + EXPECT_EQ(number_of_streams - i, static_cast(ssrcs.size())); + EXPECT_EQ(bitrate_bps, latest_bps); + for (int j = i; j < number_of_streams; j++) { + EXPECT_EQ(kDefaultSsrc + j, ssrcs[j - i]); + } + bitrate_estimator_->RemoveStream(kDefaultSsrc + i); + } + EXPECT_TRUE(bitrate_estimator_->LatestEstimate(&ssrcs, &latest_bps)); + EXPECT_EQ(0u, ssrcs.size()); + EXPECT_EQ(0u, latest_bps); } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h 2015-02-03 14:33:36.000000000 +0000 @@ -16,9 +16,9 @@ #include #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/constructormagic.h" #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" #include "webrtc/system_wrappers/interface/clock.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" namespace webrtc { @@ -190,14 +190,14 @@ unsigned int target_bitrate); void InitialBehaviorTestHelper(unsigned int expected_converge_bitrate); - void RateIncreaseReorderingTestHelper(); - void RateIncreaseRtpTimestampsTestHelper(); + void RateIncreaseReorderingTestHelper(unsigned int expected_bitrate); + void RateIncreaseRtpTimestampsTestHelper(int expected_iterations); void CapacityDropTestHelper(int number_of_streams, bool wrap_time_stamp, - unsigned int expected_converge_bitrate, unsigned int expected_bitrate_drop_delta); static const unsigned int kDefaultSsrc; + static const int kArrivalTimeClockOffsetMs = 60000; SimulatedClock clock_; // Time at the receiver. scoped_ptr bitrate_observer_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_rate_control.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_rate_control.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_rate_control.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_rate_control.cc 2015-02-03 14:33:36.000000000 +0000 @@ -122,8 +122,6 @@ } updated_ = true; current_input_ = *input; - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, "BWE: Incoming rate = %u kbps", - input->_incomingBitRate/1000); return rate_control_region_; } @@ -158,18 +156,11 @@ ChangeRegion(kRcAboveMax); } } - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "BWE: Response time: %f + %i + 10*33\n", - avg_change_period_, rtt_); const uint32_t response_time = static_cast(avg_change_period_ + 0.5f) + rtt_ + 300; double alpha = RateIncreaseFactor(now_ms, last_bit_rate_change_, response_time, noise_var); - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "BWE: avg_change_period_ = %f ms; RTT = %u ms", avg_change_period_, - rtt_); - current_bit_rate = static_cast(current_bit_rate * alpha) + 1000; if (max_hold_rate_ > 0 && beta_ * max_hold_rate_ > current_bit_rate) { current_bit_rate = static_cast(beta_ * max_hold_rate_); @@ -178,9 +169,6 @@ recovery = true; } max_hold_rate_ = 0; - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "BWE: Increase rate to current_bit_rate = %u kbps", - current_bit_rate / 1000); last_bit_rate_change_ = now_ms; break; } @@ -207,10 +195,6 @@ } UpdateMaxBitRateEstimate(incoming_bit_rate_kbps); - - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "BWE: Decrease rate to current_bit_rate = %u kbps", - current_bit_rate / 1000); } // Stay on hold until the pipes are cleared. ChangeState(kRcHold); @@ -251,8 +235,6 @@ alpha = 1.3; } - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, "BWE: alpha = %f", alpha); - if (last_ms > -1) { alpha = pow(alpha, (now_ms - last_ms) / 1000.0); } @@ -341,45 +323,5 @@ void RemoteRateControl::ChangeState(RateControlState new_state) { came_from_state_ = rate_control_state_; rate_control_state_ = new_state; - char state1[15]; - char state2[15]; - char state3[15]; - StateStr(came_from_state_, state1); - StateStr(rate_control_state_, state2); - StateStr(current_input_._bwState, state3); - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "\t%s => %s due to %s\n", state1, state2, state3); -} - -void RemoteRateControl::StateStr(RateControlState state, char* str) { - switch (state) { - case kRcDecrease: - strncpy(str, "DECREASE", 9); - break; - case kRcHold: - strncpy(str, "HOLD", 5); - break; - case kRcIncrease: - strncpy(str, "INCREASE", 9); - break; - default: - assert(false); - } -} - -void RemoteRateControl::StateStr(BandwidthUsage state, char* str) { - switch (state) { - case kBwNormal: - strncpy(str, "NORMAL", 7); - break; - case kBwOverusing: - strncpy(str, "OVER USING", 11); - break; - case kBwUnderusing: - strncpy(str, "UNDER USING", 12); - break; - default: - assert(false); - } } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_rate_control.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_rate_control.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_rate_control.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_rate_control.h 2015-02-03 14:33:36.000000000 +0000 @@ -53,8 +53,6 @@ void ChangeState(const RateControlInput& input, int64_t now_ms); void ChangeState(RateControlState new_state); void ChangeRegion(RateControlRegion region); - static void StateStr(RateControlState state, char* str); - static void StateStr(BandwidthUsage state, char* str); uint32_t min_configured_bit_rate_; uint32_t max_configured_bit_rate_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/rtp_to_ntp.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/rtp_to_ntp.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/rtp_to_ntp.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/rtp_to_ntp.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/remote_bitrate_estimator/include/rtp_to_ntp.h" - -#include "webrtc/system_wrappers/interface/clock.h" - -#include - -namespace webrtc { - -namespace synchronization { - -RtcpMeasurement::RtcpMeasurement() - : ntp_secs(0), ntp_frac(0), rtp_timestamp(0) {} - -RtcpMeasurement::RtcpMeasurement(uint32_t ntp_secs, uint32_t ntp_frac, - uint32_t timestamp) - : ntp_secs(ntp_secs), ntp_frac(ntp_frac), rtp_timestamp(timestamp) {} - -// Calculates the RTP timestamp frequency from two pairs of NTP and RTP -// timestamps. -bool CalculateFrequency( - int64_t rtcp_ntp_ms1, - uint32_t rtp_timestamp1, - int64_t rtcp_ntp_ms2, - uint32_t rtp_timestamp2, - double* frequency_khz) { - if (rtcp_ntp_ms1 <= rtcp_ntp_ms2) { - return false; - } - *frequency_khz = static_cast(rtp_timestamp1 - rtp_timestamp2) / - static_cast(rtcp_ntp_ms1 - rtcp_ntp_ms2); - return true; -} - -// Detects if there has been a wraparound between |old_timestamp| and -// |new_timestamp|, and compensates by adding 2^32 if that is the case. -bool CompensateForWrapAround(uint32_t new_timestamp, - uint32_t old_timestamp, - int64_t* compensated_timestamp) { - assert(compensated_timestamp); - int64_t wraps = synchronization::CheckForWrapArounds(new_timestamp, - old_timestamp); - if (wraps < 0) { - // Reordering, don't use this packet. - return false; - } - *compensated_timestamp = new_timestamp + (wraps << 32); - return true; -} - -// Converts |rtp_timestamp| to the NTP time base using the NTP and RTP timestamp -// pairs in |rtcp|. The converted timestamp is returned in -// |rtp_timestamp_in_ms|. This function compensates for wrap arounds in RTP -// timestamps and returns false if it can't do the conversion due to reordering. -bool RtpToNtpMs(int64_t rtp_timestamp, - const synchronization::RtcpList& rtcp, - int64_t* rtp_timestamp_in_ms) { - assert(rtcp.size() == 2); - int64_t rtcp_ntp_ms_new = Clock::NtpToMs(rtcp.front().ntp_secs, - rtcp.front().ntp_frac); - int64_t rtcp_ntp_ms_old = Clock::NtpToMs(rtcp.back().ntp_secs, - rtcp.back().ntp_frac); - int64_t rtcp_timestamp_new = rtcp.front().rtp_timestamp; - int64_t rtcp_timestamp_old = rtcp.back().rtp_timestamp; - if (!CompensateForWrapAround(rtcp_timestamp_new, - rtcp_timestamp_old, - &rtcp_timestamp_new)) { - return false; - } - double freq_khz; - if (!CalculateFrequency(rtcp_ntp_ms_new, - rtcp_timestamp_new, - rtcp_ntp_ms_old, - rtcp_timestamp_old, - &freq_khz)) { - return false; - } - double offset = rtcp_timestamp_new - freq_khz * rtcp_ntp_ms_new; - int64_t rtp_timestamp_unwrapped; - if (!CompensateForWrapAround(rtp_timestamp, rtcp_timestamp_old, - &rtp_timestamp_unwrapped)) { - return false; - } - double rtp_timestamp_ntp_ms = (static_cast(rtp_timestamp_unwrapped) - - offset) / freq_khz + 0.5f; - if (rtp_timestamp_ntp_ms < 0) { - return false; - } - *rtp_timestamp_in_ms = rtp_timestamp_ntp_ms; - return true; -} - -int CheckForWrapArounds(uint32_t new_timestamp, uint32_t old_timestamp) { - if (new_timestamp < old_timestamp) { - // This difference should be less than -2^31 if we have had a wrap around - // (e.g. |new_timestamp| = 1, |rtcp_rtp_timestamp| = 2^32 - 1). Since it is - // cast to a int32_t, it should be positive. - if (static_cast(new_timestamp - old_timestamp) > 0) { - // Forward wrap around. - return 1; - } - } else if (static_cast(old_timestamp - new_timestamp) > 0) { - // This difference should be less than -2^31 if we have had a backward wrap - // around. Since it is cast to a int32_t, it should be positive. - return -1; - } - return 0; -} -} // namespace synchronization -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/rtp_to_ntp_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/rtp_to_ntp_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/rtp_to_ntp_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/rtp_to_ntp_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "testing/gtest/include/gtest/gtest.h" -#include "webrtc/modules/remote_bitrate_estimator/include/rtp_to_ntp.h" - -namespace webrtc { - -TEST(WrapAroundTests, NoWrap) { - EXPECT_EQ(0, synchronization::CheckForWrapArounds(0xFFFFFFFF, 0xFFFFFFFE)); - EXPECT_EQ(0, synchronization::CheckForWrapArounds(1, 0)); - EXPECT_EQ(0, synchronization::CheckForWrapArounds(0x00010000, 0x0000FFFF)); -} - -TEST(WrapAroundTests, ForwardWrap) { - EXPECT_EQ(1, synchronization::CheckForWrapArounds(0, 0xFFFFFFFF)); - EXPECT_EQ(1, synchronization::CheckForWrapArounds(0, 0xFFFF0000)); - EXPECT_EQ(1, synchronization::CheckForWrapArounds(0x0000FFFF, 0xFFFFFFFF)); - EXPECT_EQ(1, synchronization::CheckForWrapArounds(0x0000FFFF, 0xFFFF0000)); -} - -TEST(WrapAroundTests, BackwardWrap) { - EXPECT_EQ(-1, synchronization::CheckForWrapArounds(0xFFFFFFFF, 0)); - EXPECT_EQ(-1, synchronization::CheckForWrapArounds(0xFFFF0000, 0)); - EXPECT_EQ(-1, synchronization::CheckForWrapArounds(0xFFFFFFFF, 0x0000FFFF)); - EXPECT_EQ(-1, synchronization::CheckForWrapArounds(0xFFFF0000, 0x0000FFFF)); -} - -TEST(WrapAroundTests, OldRtcpWrapped) { - synchronization::RtcpList rtcp; - uint32_t ntp_sec = 0; - uint32_t ntp_frac = 0; - uint32_t timestamp = 0; - const uint32_t kOneMsInNtpFrac = 4294967; - const uint32_t kTimestampTicksPerMs = 90; - rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, - timestamp)); - ntp_frac += kOneMsInNtpFrac; - timestamp -= kTimestampTicksPerMs; - rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, - timestamp)); - ntp_frac += kOneMsInNtpFrac; - timestamp -= kTimestampTicksPerMs; - int64_t timestamp_in_ms = -1; - // This expected to fail since it's highly unlikely that the older RTCP - // has a much smaller RTP timestamp than the newer. - EXPECT_FALSE(synchronization::RtpToNtpMs(timestamp, rtcp, ×tamp_in_ms)); -} - -TEST(WrapAroundTests, NewRtcpWrapped) { - synchronization::RtcpList rtcp; - uint32_t ntp_sec = 0; - uint32_t ntp_frac = 0; - uint32_t timestamp = 0xFFFFFFFF; - const uint32_t kOneMsInNtpFrac = 4294967; - const uint32_t kTimestampTicksPerMs = 90; - rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, - timestamp)); - ntp_frac += kOneMsInNtpFrac; - timestamp += kTimestampTicksPerMs; - rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, - timestamp)); - int64_t timestamp_in_ms = -1; - EXPECT_TRUE(synchronization::RtpToNtpMs(rtcp.back().rtp_timestamp, rtcp, - ×tamp_in_ms)); - // Since this RTP packet has the same timestamp as the RTCP packet constructed - // at time 0 it should be mapped to 0 as well. - EXPECT_EQ(0, timestamp_in_ms); -} - -TEST(WrapAroundTests, RtpWrapped) { - const uint32_t kOneMsInNtpFrac = 4294967; - const uint32_t kTimestampTicksPerMs = 90; - synchronization::RtcpList rtcp; - uint32_t ntp_sec = 0; - uint32_t ntp_frac = 0; - uint32_t timestamp = 0xFFFFFFFF - 2 * kTimestampTicksPerMs; - rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, - timestamp)); - ntp_frac += kOneMsInNtpFrac; - timestamp += kTimestampTicksPerMs; - rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, - timestamp)); - ntp_frac += kOneMsInNtpFrac; - timestamp += kTimestampTicksPerMs; - int64_t timestamp_in_ms = -1; - EXPECT_TRUE(synchronization::RtpToNtpMs(timestamp, rtcp, - ×tamp_in_ms)); - // Since this RTP packet has the same timestamp as the RTCP packet constructed - // at time 0 it should be mapped to 0 as well. - EXPECT_EQ(2, timestamp_in_ms); -} - -TEST(WrapAroundTests, OldRtp_RtcpsWrapped) { - const uint32_t kOneMsInNtpFrac = 4294967; - const uint32_t kTimestampTicksPerMs = 90; - synchronization::RtcpList rtcp; - uint32_t ntp_sec = 0; - uint32_t ntp_frac = 0; - uint32_t timestamp = 0; - rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, - timestamp)); - ntp_frac += kOneMsInNtpFrac; - timestamp += kTimestampTicksPerMs; - rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, - timestamp)); - ntp_frac += kOneMsInNtpFrac; - timestamp -= 2*kTimestampTicksPerMs; - int64_t timestamp_in_ms = -1; - EXPECT_FALSE(synchronization::RtpToNtpMs(timestamp, rtcp, - ×tamp_in_ms)); -} - -TEST(WrapAroundTests, OldRtp_NewRtcpWrapped) { - const uint32_t kOneMsInNtpFrac = 4294967; - const uint32_t kTimestampTicksPerMs = 90; - synchronization::RtcpList rtcp; - uint32_t ntp_sec = 0; - uint32_t ntp_frac = 0; - uint32_t timestamp = 0xFFFFFFFF; - rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, - timestamp)); - ntp_frac += kOneMsInNtpFrac; - timestamp += kTimestampTicksPerMs; - rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, - timestamp)); - ntp_frac += kOneMsInNtpFrac; - timestamp -= kTimestampTicksPerMs; - int64_t timestamp_in_ms = -1; - EXPECT_TRUE(synchronization::RtpToNtpMs(timestamp, rtcp, - ×tamp_in_ms)); - // Constructed at the same time as the first RTCP and should therefore be - // mapped to zero. - EXPECT_EQ(0, timestamp_in_ms); -} - -TEST(WrapAroundTests, OldRtp_OldRtcpWrapped) { - const uint32_t kOneMsInNtpFrac = 4294967; - const uint32_t kTimestampTicksPerMs = 90; - synchronization::RtcpList rtcp; - uint32_t ntp_sec = 0; - uint32_t ntp_frac = 0; - uint32_t timestamp = 0; - rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, - timestamp)); - ntp_frac += kOneMsInNtpFrac; - timestamp -= kTimestampTicksPerMs; - rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, - timestamp)); - ntp_frac += kOneMsInNtpFrac; - timestamp += 2*kTimestampTicksPerMs; - int64_t timestamp_in_ms = -1; - EXPECT_FALSE(synchronization::RtpToNtpMs(timestamp, rtcp, - ×tamp_in_ms)); -} -}; // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_baselinefile.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_baselinefile.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_baselinefile.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_baselinefile.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,13 +10,14 @@ #include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_baselinefile.h" +#include + #include -#include #include +#include "webrtc/base/constructormagic.h" #include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_fileutils.h" #include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/test/testsupport/fileutils.h" @@ -117,7 +118,7 @@ virtual bool VerifyOrWrite() { if (!verifier_->VerifyOrWrite()) { std::string dir_path = webrtc::test::OutputPath() + kResourceSubDir; - if (!webrtc::test::CreateDirectory(dir_path)) { + if (!webrtc::test::CreateDir(dir_path)) { printf("WARNING: Cannot create output dir: %s\n", dir_path.c_str()); return false; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test.cc 2015-02-03 14:33:36.000000000 +0000 @@ -23,43 +23,40 @@ namespace testing { namespace bwe { -namespace stl_helpers { -template void DeleteElements(T* container) { - if (!container) return; - for (typename T::iterator it = container->begin(); it != container->end(); - ++it) { - delete *it; - } - container->clear(); -} -} // namespace stl_helpers - -class BweTest::TestedEstimator : public RemoteBitrateObserver { +class TestedEstimator : public RemoteBitrateObserver { public: static const uint32_t kRemoteBitrateEstimatorMinBitrateBps = 30000; + static const int kDelayPlotIntervalMs = 100; TestedEstimator(const string& test_name, const BweTestConfig::EstimatorConfig& config) : debug_name_(config.debug_name), + delay_log_prefix_(), + estimate_log_prefix_(), + last_delay_plot_ms_(0), + plot_delay_(config.plot_delay), + plot_estimate_(config.plot_estimate), clock_(0), stats_(), - relative_estimator_stats_(), latest_estimate_bps_(-1), estimator_(config.estimator_factory->Create( - this, &clock_, kRemoteBitrateEstimatorMinBitrateBps)), - relative_estimator_(NULL), + this, &clock_, config.control_type, + kRemoteBitrateEstimatorMinBitrateBps)), baseline_(BaseLineFileInterface::Create(test_name + "_" + debug_name_, config.update_baseline)) { assert(estimator_.get()); assert(baseline_.get()); + // Setup the prefix strings used when logging. + std::stringstream ss; + ss << "Delay_" << config.flow_id << "#2"; + delay_log_prefix_ = ss.str(); + ss.str(""); + ss << "Estimate_" << config.flow_id << "#1"; + estimate_log_prefix_ = ss.str(); // Default RTT in RemoteRateControl is 200 ms ; 50 ms is more realistic. estimator_->OnRttUpdate(50); } - void SetRelativeEstimator(TestedEstimator* relative_estimator) { - relative_estimator_ = relative_estimator; - } - void EatPacket(const Packet& packet) { BWE_TEST_LOGGING_CONTEXT(debug_name_); @@ -69,15 +66,21 @@ // time once packet reaches the estimator. int64_t packet_time_ms = (packet.send_time_us() + 500) / 1000; BWE_TEST_LOGGING_TIME(packet_time_ms); - BWE_TEST_LOGGING_PLOT("Delay_#2", clock_.TimeInMilliseconds(), - packet_time_ms - - (packet.creation_time_us() + 500) / 1000); + if (plot_delay_) { + if (clock_.TimeInMilliseconds() - last_delay_plot_ms_ > + kDelayPlotIntervalMs) { + BWE_TEST_LOGGING_PLOT(delay_log_prefix_, clock_.TimeInMilliseconds(), + packet_time_ms - + (packet.creation_time_us() + 500) / 1000); + last_delay_plot_ms_ = clock_.TimeInMilliseconds(); + } + } - int64_t step_ms = estimator_->TimeUntilNextProcess(); + int64_t step_ms = std::max(estimator_->TimeUntilNextProcess(), 0); while ((clock_.TimeInMilliseconds() + step_ms) < packet_time_ms) { clock_.AdvanceTimeMilliseconds(step_ms); estimator_->Process(); - step_ms = estimator_->TimeUntilNextProcess(); + step_ms = std::max(estimator_->TimeUntilNextProcess(), 0); } estimator_->IncomingPacket(packet_time_ms, packet.payload_size(), packet.header()); @@ -96,14 +99,9 @@ double estimated_kbps = static_cast(estimated_bps) / 1000.0; stats_.Push(estimated_kbps); - BWE_TEST_LOGGING_PLOT("Estimate_#1", clock_.TimeInMilliseconds(), - estimated_kbps); - uint32_t relative_estimate_bps = 0; - if (relative_estimator_ && - relative_estimator_->LatestEstimate(&relative_estimate_bps)) { - double relative_estimate_kbps = - static_cast(relative_estimate_bps) / 1000.0; - relative_estimator_stats_.Push(estimated_kbps - relative_estimate_kbps); + if (plot_estimate_) { + BWE_TEST_LOGGING_PLOT(estimate_log_prefix_, clock_.TimeInMilliseconds(), + estimated_kbps); } return true; } @@ -114,10 +112,6 @@ BWE_TEST_LOGGING_CONTEXT(debug_name_); BWE_TEST_LOGGING_CONTEXT("Mean"); stats_.Log("kbps"); - if (relative_estimator_) { - BWE_TEST_LOGGING_CONTEXT("Diff"); - relative_estimator_stats_.Log("kbps"); - } } void VerifyOrWriteBaseline() { @@ -143,143 +137,217 @@ } string debug_name_; + string delay_log_prefix_; + string estimate_log_prefix_; + int64_t last_delay_plot_ms_; + bool plot_delay_; + bool plot_estimate_; SimulatedClock clock_; Stats stats_; - Stats relative_estimator_stats_; int64_t latest_estimate_bps_; scoped_ptr estimator_; - TestedEstimator* relative_estimator_; scoped_ptr baseline_; DISALLOW_IMPLICIT_CONSTRUCTORS(TestedEstimator); }; +class PacketProcessorRunner { + public: + explicit PacketProcessorRunner(PacketProcessor* processor) + : processor_(processor) {} + + bool HasProcessor(const PacketProcessor* processor) const { + return processor == processor_; + } + + void RunFor(int64_t time_ms, int64_t time_now_ms, Packets* in_out) { + Packets to_process; + FindPacketsToProcess(processor_->flow_ids(), in_out, &to_process); + processor_->RunFor(time_ms, &to_process); + QueuePackets(&to_process, time_now_ms * 1000); + if (!to_process.empty()) { + processor_->Plot((to_process.back().send_time_us() + 500) / 1000); + } + in_out->merge(to_process); + } + + private: + void FindPacketsToProcess(const FlowIds& flow_ids, Packets* in, + Packets* out) { + assert(out->empty()); + for (Packets::iterator it = in->begin(); it != in->end();) { + // TODO(holmer): Further optimize this by looking for consecutive flow ids + // in the packet list and only doing the binary search + splice once for a + // sequence. + if (std::binary_search(flow_ids.begin(), flow_ids.end(), it->flow_id())) { + Packets::iterator next = it; + ++next; + out->splice(out->end(), *in, it); + it = next; + } else { + ++it; + } + } + } + + void QueuePackets(Packets* batch, int64_t end_of_batch_time_us) { + queue_.merge(*batch); + if (queue_.empty()) { + return; + } + Packets::iterator it = queue_.begin(); + for (; it != queue_.end(); ++it) { + if (it->send_time_us() > end_of_batch_time_us) { + break; + } + } + Packets to_transfer; + to_transfer.splice(to_transfer.begin(), queue_, queue_.begin(), it); + batch->merge(to_transfer); + } + + PacketProcessor* processor_; + Packets queue_; +}; + BweTest::BweTest() : run_time_ms_(0), + time_now_ms_(-1), simulation_interval_ms_(-1), - previous_packets_(), - packet_senders_(), estimators_(), processors_() { } BweTest::~BweTest() { - stl_helpers::DeleteElements(&estimators_); - stl_helpers::DeleteElements(&packet_senders_); + BWE_TEST_LOGGING_GLOBAL_ENABLE(true); + for (EstimatorMap::iterator it = estimators_.begin(); it != estimators_.end(); + ++it) { + it->second->VerifyOrWriteBaseline(); + it->second->LogStats(); + } + BWE_TEST_LOGGING_GLOBAL_CONTEXT(""); + + for (EstimatorMap::iterator it = estimators_.begin(); + it != estimators_.end(); ++it) { + delete it->second; + } } -void BweTest::SetUp() { +void BweTest::SetupTestFromConfig(const BweTestConfig& config) { const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); string test_name = string(test_info->test_case_name()) + "_" + string(test_info->name()); BWE_TEST_LOGGING_GLOBAL_CONTEXT(test_name); - - const BweTestConfig& config = GetParam(); - - uint32_t total_capacity = 0; - for (vector::const_iterator it = - config.sender_factories.begin(); it != config.sender_factories.end(); - ++it) { - PacketSender* sender = (*it)->Create(); - assert(sender); - total_capacity += sender->GetCapacityKbps(); - packet_senders_.push_back(sender); - processors_.push_back(sender); + for (vector::const_iterator it = + config.estimator_configs.begin(); it != config.estimator_configs.end(); + ++it) { + estimators_.insert(std::make_pair(it->flow_id, new TestedEstimator( + test_name, *it))); } - BWE_TEST_LOGGING_LOG1("RequiredLinkCapacity", "%d kbps", total_capacity) - - // Set simulation interval from first packet sender. - if (packet_senders_.size() > 0) { - simulation_interval_ms_ = packet_senders_[0]->GetFeedbackIntervalMs(); - } - - for (vector:: const_iterator it = - config.estimator_configs.begin(); it != config.estimator_configs.end(); - ++it) { - estimators_.push_back(new TestedEstimator(test_name, *it)); - } - if (estimators_.size() > 1) { - // Set all estimators as relative to the first one. - for (uint32_t i = 1; i < estimators_.size(); ++i) { - estimators_[i]->SetRelativeEstimator(estimators_[0]); - } - } - BWE_TEST_LOGGING_GLOBAL_ENABLE(false); } -void BweTest::TearDown() { - BWE_TEST_LOGGING_GLOBAL_ENABLE(true); - - for (vector::iterator eit = estimators_.begin(); - eit != estimators_.end(); ++eit) { - (*eit)->VerifyOrWriteBaseline(); - (*eit)->LogStats(); - } - - BWE_TEST_LOGGING_GLOBAL_CONTEXT(""); -} - -void BweTest::AddPacketProcessor( - PacketProcessor* processor) { +void BweTest::AddPacketProcessor(PacketProcessor* processor, bool is_sender) { assert(processor); - processors_.push_back(processor); + processors_.push_back(PacketProcessorRunner(processor)); + if (is_sender) { + senders_.push_back(static_cast(processor)); + } + const FlowIds& flow_ids = processor->flow_ids(); + for (size_t i = 0; i < flow_ids.size(); ++i) { + assert(estimators_.count(flow_ids[i]) == 1); + } } void BweTest::RemovePacketProcessor( PacketProcessor* processor) { - vector::iterator it = - std::find(processors_.begin(), processors_.end(), processor); - assert(it != processors_.end()); - processors_.erase(it); + for (vector::iterator it = processors_.begin(); + it != processors_.end(); ++it) { + if (it->HasProcessor(processor)) { + processors_.erase(it); + return; + } + } + assert(false); } void BweTest::VerboseLogging(bool enable) { BWE_TEST_LOGGING_GLOBAL_ENABLE(enable); } +void BweTest::GiveFeedbackToAffectedSenders(int flow_id, + TestedEstimator* estimator) { + std::list affected_senders; + for (std::vector::iterator psit = + senders_.begin(); psit != senders_.end(); ++psit) { + const FlowIds& flow_ids = (*psit)->flow_ids(); + if (std::binary_search(flow_ids.begin(), flow_ids.end(), flow_id)) { + affected_senders.push_back(*psit); + } + } + PacketSender::Feedback feedback = {0}; + if (estimator->CheckEstimate(&feedback) && !affected_senders.empty()) { + // Allocate the bitrate evenly between the senders. + feedback.estimated_bps /= affected_senders.size(); + for (std::list::iterator psit = affected_senders.begin(); + psit != affected_senders.end(); ++psit) { + (*psit)->GiveFeedback(feedback); + } + } +} + void BweTest::RunFor(int64_t time_ms) { - for (run_time_ms_ += time_ms; run_time_ms_ >= simulation_interval_ms_; - run_time_ms_ -= simulation_interval_ms_) { + // Set simulation interval from first packet sender. + // TODO(holmer): Support different feedback intervals for different flows. + if (!senders_.empty()) { + simulation_interval_ms_ = senders_[0]->GetFeedbackIntervalMs(); + } + assert(simulation_interval_ms_ > 0); + if (time_now_ms_ == -1) { + time_now_ms_ = simulation_interval_ms_; + } + for (run_time_ms_ += time_ms; + time_now_ms_ <= run_time_ms_ - simulation_interval_ms_; + time_now_ms_ += simulation_interval_ms_) { Packets packets; - for (vector::const_iterator it = + for (vector::iterator it = processors_.begin(); it != processors_.end(); ++it) { - (*it)->RunFor(simulation_interval_ms_, &packets); - (*it)->Plot((packets.back().send_time_us() + 500) / 1000); + it->RunFor(simulation_interval_ms_, time_now_ms_, &packets); } // Verify packets are in order between batches. - if (!packets.empty() && !previous_packets_.empty()) { - packets.splice(packets.begin(), previous_packets_, - --previous_packets_.end()); - ASSERT_TRUE(IsTimeSorted(packets)); - packets.erase(packets.begin()); + if (!packets.empty()) { + if (!previous_packets_.empty()) { + packets.splice(packets.begin(), previous_packets_, + --previous_packets_.end()); + ASSERT_TRUE(IsTimeSorted(packets)); + packets.erase(packets.begin()); + } + ASSERT_LE(packets.front().send_time_us(), time_now_ms_ * 1000); + ASSERT_LE(packets.back().send_time_us(), time_now_ms_ * 1000); } else { ASSERT_TRUE(IsTimeSorted(packets)); } - for (PacketsConstIt pit = packets.begin(); pit != packets.end(); ++pit) { - for (vector::iterator eit = estimators_.begin(); - eit != estimators_.end(); ++eit) { - (*eit)->EatPacket(*pit); - } + for (PacketsConstIt it = packets.begin(); it != packets.end(); ++it) { + EstimatorMap::iterator est_it = estimators_.find(it->flow_id()); + ASSERT_TRUE(est_it != estimators_.end()); + est_it->second->EatPacket(*it); } - previous_packets_.swap(packets); - - for (vector::iterator eit = estimators_.begin(); - eit != estimators_.end(); ++eit) { - PacketSender::Feedback feedback = {0}; - if ((*eit)->CheckEstimate(&feedback)) { - for (vector::iterator psit = packet_senders_.begin(); - psit != packet_senders_.end(); ++psit) { - (*psit)->GiveFeedback(feedback); - } - } + for (EstimatorMap::iterator est_it = estimators_.begin(); + est_it != estimators_.end(); ++est_it) { + GiveFeedbackToAffectedSenders(est_it->first, est_it->second); } } } + +string BweTest::GetTestName() const { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + return string(test_info->name()); +} } // namespace bwe } // namespace testing } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_fileutils.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_fileutils.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_fileutils.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_fileutils.cc 2015-02-03 14:33:36.000000000 +0000 @@ -15,7 +15,7 @@ #else #include #endif -#include +#include #include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_fileutils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_fileutils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_fileutils.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_fileutils.h 2015-02-03 14:33:36.000000000 +0000 @@ -11,11 +11,12 @@ #ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_BWE_TEST_FILEUTILS_H_ #define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_BWE_TEST_FILEUTILS_H_ -#include +#include + #include +#include "webrtc/base/constructormagic.h" #include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" namespace webrtc { namespace testing { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,12 +10,45 @@ #include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h" -#include +#include + #include namespace webrtc { namespace testing { namespace bwe { +class DelayCapHelper { + public: + DelayCapHelper() : max_delay_us_(0), delay_stats_() {} + + void SetMaxDelay(int max_delay_ms) { + BWE_TEST_LOGGING_ENABLE(false); + BWE_TEST_LOGGING_LOG1("Max Delay", "%d ms", static_cast(max_delay_ms)); + assert(max_delay_ms >= 0); + max_delay_us_ = max_delay_ms * 1000; + } + + bool ShouldSendPacket(int64_t send_time_us, int64_t arrival_time_us) { + int64_t packet_delay_us = send_time_us - arrival_time_us; + delay_stats_.Push(std::min(packet_delay_us, max_delay_us_) / 1000); + return (max_delay_us_ == 0 || max_delay_us_ >= packet_delay_us); + } + + const Stats& delay_stats() const { + return delay_stats_; + } + + private: + int64_t max_delay_us_; + Stats delay_stats_; + + DISALLOW_COPY_AND_ASSIGN(DelayCapHelper); +}; + +const FlowIds CreateFlowIds(const int *flow_ids_array, size_t num_flow_ids) { + FlowIds flow_ids(&flow_ids_array[0], flow_ids_array + num_flow_ids); + return flow_ids; +} class RateCounter { public: @@ -83,30 +116,33 @@ a_ ^= b_; b_ += a_; return static_cast(mean + standard_deviation * - std::sqrt(-2 * std::log(u1)) * std::cos(2 * kPi * u2)); + sqrt(-2 * log(u1)) * cos(2 * kPi * u2)); } Packet::Packet() - : creation_time_us_(-1), + : flow_id_(0), + creation_time_us_(-1), send_time_us_(-1), payload_size_(0) { - memset(&header_, 0, sizeof(header_)); + memset(&header_, 0, sizeof(header_)); } -Packet::Packet(int64_t send_time_us, uint32_t payload_size, +Packet::Packet(int flow_id, int64_t send_time_us, uint32_t payload_size, const RTPHeader& header) - : creation_time_us_(send_time_us), - send_time_us_(send_time_us), - payload_size_(payload_size), - header_(header) { + : flow_id_(flow_id), + creation_time_us_(send_time_us), + send_time_us_(send_time_us), + payload_size_(payload_size), + header_(header) { } Packet::Packet(int64_t send_time_us, uint32_t sequence_number) - : creation_time_us_(send_time_us), + : flow_id_(0), + creation_time_us_(send_time_us), send_time_us_(send_time_us), payload_size_(0) { - memset(&header_, 0, sizeof(header_)); - header_.sequenceNumber = sequence_number; + memset(&header_, 0, sizeof(header_)); + header_.sequenceNumber = sequence_number; } bool Packet::operator<(const Packet& rhs) const { @@ -118,6 +154,12 @@ send_time_us_ = send_time_us; } +void Packet::SetAbsSendTimeMs(int64_t abs_send_time_ms) { + header_.extension.hasAbsoluteSendTime = true; + header_.extension.absoluteSendTime = ((static_cast(abs_send_time_ms * + (1 << 18)) + 500) / 1000) & 0x00fffffful; +} + bool IsTimeSorted(const Packets& packets) { PacketsConstIt last_it = packets.begin(); for (PacketsConstIt it = last_it; it != packets.end(); ++it) { @@ -129,10 +171,20 @@ return true; } -PacketProcessor::PacketProcessor(PacketProcessorListener* listener) - : listener_(listener) { +PacketProcessor::PacketProcessor(PacketProcessorListener* listener, + bool is_sender) + : listener_(listener), flow_ids_(1, 0) { if (listener_) { - listener_->AddPacketProcessor(this); + listener_->AddPacketProcessor(this, is_sender); + } +} + +PacketProcessor::PacketProcessor(PacketProcessorListener* listener, + const FlowIds& flow_ids, + bool is_sender) + : listener_(listener), flow_ids_(flow_ids) { + if (listener_) { + listener_->AddPacketProcessor(this, is_sender); } } @@ -143,20 +195,36 @@ } RateCounterFilter::RateCounterFilter(PacketProcessorListener* listener) - : PacketProcessor(listener), + : PacketProcessor(listener, false), rate_counter_(new RateCounter()), - pps_stats_(), + packets_per_second_stats_(), kbps_stats_(), name_("") {} RateCounterFilter::RateCounterFilter(PacketProcessorListener* listener, const std::string& name) - : PacketProcessor(listener), + : PacketProcessor(listener, false), rate_counter_(new RateCounter()), - pps_stats_(), + packets_per_second_stats_(), kbps_stats_(), name_(name) {} +RateCounterFilter::RateCounterFilter(PacketProcessorListener* listener, + const FlowIds& flow_ids, + const std::string& name) + : PacketProcessor(listener, flow_ids, false), + rate_counter_(new RateCounter()), + packets_per_second_stats_(), + kbps_stats_(), + name_(name) { + std::stringstream ss; + ss << name_ << "_"; + for (size_t i = 0; i < flow_ids.size(); ++i) { + ss << flow_ids[i] << ","; + } + name_ = ss.str(); +} + RateCounterFilter::~RateCounterFilter() { LogStats(); } @@ -171,10 +239,14 @@ void RateCounterFilter::LogStats() { BWE_TEST_LOGGING_CONTEXT("RateCounterFilter"); - pps_stats_.Log("pps"); + packets_per_second_stats_.Log("pps"); kbps_stats_.Log("kbps"); } +Stats RateCounterFilter::GetBitrateStats() const { + return kbps_stats_; +} + void RateCounterFilter::Plot(int64_t timestamp_ms) { BWE_TEST_LOGGING_CONTEXT(name_.c_str()); BWE_TEST_LOGGING_PLOT("Throughput_#1", timestamp_ms, @@ -186,12 +258,12 @@ for (PacketsConstIt it = in_out->begin(); it != in_out->end(); ++it) { rate_counter_->UpdateRates(it->send_time_us(), it->payload_size()); } - pps_stats_.Push(rate_counter_->packets_per_second()); + packets_per_second_stats_.Push(rate_counter_->packets_per_second()); kbps_stats_.Push(rate_counter_->bits_per_second() / 1000.0); } LossFilter::LossFilter(PacketProcessorListener* listener) - : PacketProcessor(listener), + : PacketProcessor(listener, false), random_(0x12345678), loss_fraction_(0.0f) { } @@ -216,7 +288,7 @@ } DelayFilter::DelayFilter(PacketProcessorListener* listener) - : PacketProcessor(listener), + : PacketProcessor(listener, false), delay_us_(0), last_send_time_us_(0) { } @@ -238,7 +310,7 @@ } JitterFilter::JitterFilter(PacketProcessorListener* listener) - : PacketProcessor(listener), + : PacketProcessor(listener, false), random_(0x89674523), stddev_jitter_us_(0), last_send_time_us_(0) { @@ -263,7 +335,7 @@ } ReorderFilter::ReorderFilter(PacketProcessorListener* listener) - : PacketProcessor(listener), + : PacketProcessor(listener, false), random_(0x27452389), reorder_fraction_(0.0f) { } @@ -295,25 +367,28 @@ } ChokeFilter::ChokeFilter(PacketProcessorListener* listener) - : PacketProcessor(listener), + : PacketProcessor(listener, false), kbps_(1200), - max_delay_us_(0), - last_send_time_us_(0) { + last_send_time_us_(0), + delay_cap_helper_(new DelayCapHelper()) { } +ChokeFilter::ChokeFilter(PacketProcessorListener* listener, + const FlowIds& flow_ids) + : PacketProcessor(listener, flow_ids, false), + kbps_(1200), + last_send_time_us_(0), + delay_cap_helper_(new DelayCapHelper()) { +} + +ChokeFilter::~ChokeFilter() {} + void ChokeFilter::SetCapacity(uint32_t kbps) { BWE_TEST_LOGGING_ENABLE(false); BWE_TEST_LOGGING_LOG1("BitrateChoke", "%d kbps", kbps); kbps_ = kbps; } -void ChokeFilter::SetMaxDelay(int64_t max_delay_ms) { - BWE_TEST_LOGGING_ENABLE(false); - BWE_TEST_LOGGING_LOG1("Max Delay", "%d ms", static_cast(max_delay_ms)); - assert(max_delay_ms >= 0); - max_delay_us_ = max_delay_ms * 1000; -} - void ChokeFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) { assert(in_out); for (PacketsIt it = in_out->begin(); it != in_out->end(); ) { @@ -321,8 +396,8 @@ (it->payload_size() * 8 * 1000 + kbps_ / 2) / kbps_; int64_t new_send_time_us = std::max(it->send_time_us(), earliest_send_time_us); - if (max_delay_us_ == 0 || - max_delay_us_ >= (new_send_time_us - it->send_time_us())) { + if (delay_cap_helper_->ShouldSendPacket(new_send_time_us, + it->send_time_us())) { it->set_send_time_us(new_send_time_us); last_send_time_us_ = new_send_time_us; ++it; @@ -332,24 +407,40 @@ } } +void ChokeFilter::SetMaxDelay(int max_delay_ms) { + delay_cap_helper_->SetMaxDelay(max_delay_ms); +} + +Stats ChokeFilter::GetDelayStats() const { + return delay_cap_helper_->delay_stats(); +} + TraceBasedDeliveryFilter::TraceBasedDeliveryFilter( PacketProcessorListener* listener) - : PacketProcessor(listener), + : PacketProcessor(listener, false), + current_offset_us_(0), delivery_times_us_(), next_delivery_it_(), local_time_us_(-1), rate_counter_(new RateCounter), - name_("") {} + name_(""), + delay_cap_helper_(new DelayCapHelper()), + packets_per_second_stats_(), + kbps_stats_() {} TraceBasedDeliveryFilter::TraceBasedDeliveryFilter( PacketProcessorListener* listener, const std::string& name) - : PacketProcessor(listener), + : PacketProcessor(listener, false), + current_offset_us_(0), delivery_times_us_(), next_delivery_it_(), local_time_us_(-1), rate_counter_(new RateCounter), - name_(name) {} + name_(name), + delay_cap_helper_(new DelayCapHelper()), + packets_per_second_stats_(), + kbps_stats_() {} TraceBasedDeliveryFilter::~TraceBasedDeliveryFilter() { } @@ -392,14 +483,38 @@ void TraceBasedDeliveryFilter::RunFor(int64_t time_ms, Packets* in_out) { assert(in_out); - for (PacketsIt it = in_out->begin(); it != in_out->end(); ++it) { - do { + for (PacketsIt it = in_out->begin(); it != in_out->end();) { + while (local_time_us_ < it->send_time_us()) { ProceedToNextSlot(); - const int kPayloadSize = 1240; - rate_counter_->UpdateRates(local_time_us_, kPayloadSize); - } while (local_time_us_ < it->send_time_us()); - it->set_send_time_us(local_time_us_); + } + // Drop any packets that have been queued for too long. + while (!delay_cap_helper_->ShouldSendPacket(local_time_us_, + it->send_time_us())) { + it = in_out->erase(it); + if (it == in_out->end()) { + return; + } + } + if (local_time_us_ >= it->send_time_us()) { + it->set_send_time_us(local_time_us_); + ProceedToNextSlot(); + } + ++it; } + packets_per_second_stats_.Push(rate_counter_->packets_per_second()); + kbps_stats_.Push(rate_counter_->bits_per_second() / 1000.0); +} + +void TraceBasedDeliveryFilter::SetMaxDelay(int max_delay_ms) { + delay_cap_helper_->SetMaxDelay(max_delay_ms); +} + +Stats TraceBasedDeliveryFilter::GetDelayStats() const { + return delay_cap_helper_->delay_stats(); +} + +Stats TraceBasedDeliveryFilter::GetBitrateStats() const { + return kbps_stats_; } void TraceBasedDeliveryFilter::ProceedToNextSlot() { @@ -409,22 +524,34 @@ // When the trace wraps we allow two packets to be sent back-to-back. for (TimeList::iterator it = delivery_times_us_.begin(); it != delivery_times_us_.end(); ++it) { - *it += local_time_us_; + *it += local_time_us_ - current_offset_us_; } + current_offset_us_ += local_time_us_ - current_offset_us_; next_delivery_it_ = delivery_times_us_.begin(); } } local_time_us_ = *next_delivery_it_; + const int kPayloadSize = 1200; + rate_counter_->UpdateRates(local_time_us_, kPayloadSize); } PacketSender::PacketSender(PacketProcessorListener* listener) - : PacketProcessor(listener) { + : PacketProcessor(listener, true) {} + +PacketSender::PacketSender(PacketProcessorListener* listener, + const FlowIds& flow_ids) + : PacketProcessor(listener, flow_ids, true) { + } -VideoSender::VideoSender(PacketProcessorListener* listener, float fps, - uint32_t kbps, uint32_t ssrc, float first_frame_offset) - : PacketSender(listener), - kMaxPayloadSizeBytes(1000), +VideoSender::VideoSender(int flow_id, + PacketProcessorListener* listener, + float fps, + uint32_t kbps, + uint32_t ssrc, + float first_frame_offset) + : PacketSender(listener, FlowIds(1, flow_id)), + kMaxPayloadSizeBytes(1200), kTimestampBase(0xff80ff00ul), frame_period_ms_(1000.0 / fps), bytes_per_second_((1000 * kbps) / 8), @@ -443,17 +570,22 @@ return (bytes_per_second_ * 8) / 1000; } +uint32_t VideoSender::NextFrameSize() { + return frame_size_bytes_; +} + +uint32_t VideoSender::NextPacketSize(uint32_t frame_size, + uint32_t remaining_payload) { + return std::min(kMaxPayloadSizeBytes, remaining_payload); +} + void VideoSender::RunFor(int64_t time_ms, Packets* in_out) { assert(in_out); now_ms_ += time_ms; - Packets newPackets; + Packets new_packets; while (now_ms_ >= next_frame_ms_) { - prototype_header_.sequenceNumber++; prototype_header_.timestamp = kTimestampBase + static_cast(next_frame_ms_ * 90.0); - prototype_header_.extension.absoluteSendTime = (kTimestampBase + - ((static_cast(next_frame_ms_ * (1 << 18)) + 500) / 1000)) & - 0x00fffffful; prototype_header_.extension.transmissionTimeOffset = 0; // Generate new packets for this frame, all with the same timestamp, @@ -461,29 +593,201 @@ // one packet, we will see a number of equally sized packets followed by // one smaller at the tail. int64_t send_time_us = next_frame_ms_ * 1000.0; - uint32_t payload_size = frame_size_bytes_; + uint32_t frame_size = NextFrameSize(); + uint32_t payload_size = frame_size; + while (payload_size > 0) { - uint32_t size = std::min(kMaxPayloadSizeBytes, payload_size); - newPackets.push_back(Packet(send_time_us, size, prototype_header_)); + ++prototype_header_.sequenceNumber; + uint32_t size = NextPacketSize(frame_size, payload_size); + new_packets.push_back(Packet(flow_ids()[0], send_time_us, size, + prototype_header_)); + new_packets.back().SetAbsSendTimeMs(next_frame_ms_); payload_size -= size; } next_frame_ms_ += frame_period_ms_; } - in_out->merge(newPackets); + in_out->merge(new_packets); } -AdaptiveVideoSender::AdaptiveVideoSender(PacketProcessorListener* listener, +AdaptiveVideoSender::AdaptiveVideoSender(int flow_id, + PacketProcessorListener* listener, float fps, uint32_t kbps, uint32_t ssrc, float first_frame_offset) - : VideoSender(listener, fps, kbps, ssrc, first_frame_offset) {} + : VideoSender(flow_id, listener, fps, kbps, ssrc, first_frame_offset) { +} void AdaptiveVideoSender::GiveFeedback(const PacketSender::Feedback& feedback) { - bytes_per_second_ = feedback.estimated_bps / 8; + bytes_per_second_ = std::min(feedback.estimated_bps / 8, 2500000u / 8); frame_size_bytes_ = (bytes_per_second_ * frame_period_ms_ + 500) / 1000; } + +PeriodicKeyFrameSender::PeriodicKeyFrameSender( + int flow_id, + PacketProcessorListener* listener, + float fps, + uint32_t kbps, + uint32_t ssrc, + float first_frame_offset, + int key_frame_interval) + : AdaptiveVideoSender(flow_id, + listener, + fps, + kbps, + ssrc, + first_frame_offset), + key_frame_interval_(key_frame_interval), + frame_counter_(0), + compensation_bytes_(0), + compensation_per_frame_(0) { +} + +uint32_t PeriodicKeyFrameSender::NextFrameSize() { + uint32_t payload_size = frame_size_bytes_; + if (frame_counter_ == 0) { + payload_size = kMaxPayloadSizeBytes * 12; + compensation_bytes_ = 4 * frame_size_bytes_; + compensation_per_frame_ = compensation_bytes_ / 30; + } else if (key_frame_interval_ > 0 && + (frame_counter_ % key_frame_interval_ == 0)) { + payload_size *= 5; + compensation_bytes_ = payload_size - frame_size_bytes_; + compensation_per_frame_ = compensation_bytes_ / 30; + } else if (compensation_bytes_ > 0) { + if (compensation_per_frame_ > static_cast(payload_size)) { + // Skip this frame. + compensation_bytes_ -= payload_size; + payload_size = 0; + } else { + payload_size -= compensation_per_frame_; + compensation_bytes_ -= compensation_per_frame_; + } + } + if (compensation_bytes_ < 0) + compensation_bytes_ = 0; + ++frame_counter_; + return payload_size; +} + +uint32_t PeriodicKeyFrameSender::NextPacketSize(uint32_t frame_size, + uint32_t remaining_payload) { + uint32_t fragments = + (frame_size + (kMaxPayloadSizeBytes - 1)) / kMaxPayloadSizeBytes; + uint32_t avg_size = (frame_size + fragments - 1) / fragments; + return std::min(avg_size, remaining_payload); +} + +PacedVideoSender::PacedVideoSender(PacketProcessorListener* listener, + uint32_t kbps, + AdaptiveVideoSender* source) + // It is important that the first_frame_offset and the initial time of + // clock_ are both zero, otherwise we can't have absolute time in this + // class. + : PacketSender(listener, source->flow_ids()), + clock_(0), + start_of_run_ms_(0), + pacer_(&clock_, this, kbps, PacedSender::kDefaultPaceMultiplier* kbps, 0), + source_(source) { +} + +void PacedVideoSender::RunFor(int64_t time_ms, Packets* in_out) { + start_of_run_ms_ = clock_.TimeInMilliseconds(); + Packets generated_packets; + source_->RunFor(time_ms, &generated_packets); + // Run process periodically to allow the packets to be paced out. + int64_t end_time_ms = clock_.TimeInMilliseconds() + time_ms; + Packets::iterator it = generated_packets.begin(); + while (clock_.TimeInMilliseconds() <= end_time_ms) { + int time_until_process_ms = pacer_.TimeUntilNextProcess(); + if (time_until_process_ms < 0) + time_until_process_ms = 0; + int time_until_packet_ms = time_ms; + if (it != generated_packets.end()) + time_until_packet_ms = + (it->send_time_us() + 500) / 1000 - clock_.TimeInMilliseconds(); + assert(time_until_packet_ms >= 0); + int time_until_next_event_ms = time_until_packet_ms; + if (time_until_process_ms < time_until_packet_ms && + pacer_.QueueSizePackets() > 0) + time_until_next_event_ms = time_until_process_ms; + + if (clock_.TimeInMilliseconds() + time_until_next_event_ms > end_time_ms) { + clock_.AdvanceTimeMilliseconds(end_time_ms - clock_.TimeInMilliseconds()); + break; + } + clock_.AdvanceTimeMilliseconds(time_until_next_event_ms); + if (time_until_process_ms < time_until_packet_ms) { + // Time to process. + pacer_.Process(); + } else { + // Time to send next packet to pacer. + pacer_.SendPacket(PacedSender::kNormalPriority, + it->header().ssrc, + it->header().sequenceNumber, + (it->send_time_us() + 500) / 1000, + it->payload_size(), + false); + pacer_queue_.push_back(*it); + const size_t kMaxPacerQueueSize = 10000; + if (pacer_queue_.size() > kMaxPacerQueueSize) { + pacer_queue_.pop_front(); + } + ++it; + } + } + QueuePackets(in_out, end_time_ms * 1000); +} + +void PacedVideoSender::QueuePackets(Packets* batch, + int64_t end_of_batch_time_us) { + queue_.merge(*batch); + if (queue_.empty()) { + return; + } + Packets::iterator it = queue_.begin(); + for (; it != queue_.end(); ++it) { + if (it->send_time_us() > end_of_batch_time_us) { + break; + } + } + Packets to_transfer; + to_transfer.splice(to_transfer.begin(), queue_, queue_.begin(), it); + batch->merge(to_transfer); +} + +void PacedVideoSender::GiveFeedback(const PacketSender::Feedback& feedback) { + source_->GiveFeedback(feedback); + pacer_.UpdateBitrate( + feedback.estimated_bps / 1000, + PacedSender::kDefaultPaceMultiplier * feedback.estimated_bps / 1000, + 0); +} + +bool PacedVideoSender::TimeToSendPacket(uint32_t ssrc, + uint16_t sequence_number, + int64_t capture_time_ms, + bool retransmission) { + for (Packets::iterator it = pacer_queue_.begin(); it != pacer_queue_.end(); + ++it) { + if (it->header().sequenceNumber == sequence_number) { + int64_t pace_out_time_ms = clock_.TimeInMilliseconds(); + // Make sure a packet is never paced out earlier than when it was put into + // the pacer. + assert(pace_out_time_ms >= (it->send_time_us() + 500) / 1000); + it->SetAbsSendTimeMs(pace_out_time_ms); + it->set_send_time_us(1000 * pace_out_time_ms); + queue_.push_back(*it); + return true; + } + } + return false; +} + +int PacedVideoSender::TimeToSendPadding(int bytes) { + return 0; +} } // namespace bwe } // namespace testing } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h 2015-02-03 14:33:36.000000000 +0000 @@ -11,24 +11,33 @@ #ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_BWE_TEST_FRAMEWORK_H_ #define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_BWE_TEST_FRAMEWORK_H_ +#include +#include + #include -#include -#include #include #include +#include #include #include #include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/modules/pacing/include/paced_sender.h" #include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h" +#include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" namespace webrtc { namespace testing { namespace bwe { +class DelayCapHelper; class RateCounter; + +typedef std::vector FlowIds; +const FlowIds CreateFlowIds(const int *flow_ids_array, size_t num_flow_ids); + template class Stats { public: Stats() @@ -71,7 +80,7 @@ return variance_; } T GetStdDev() { - return std::sqrt(static_cast(GetVariance())); + return sqrt(static_cast(GetVariance())); } T GetMin() { RefreshMinMax(); @@ -82,6 +91,13 @@ return max_; } + std::string AsString() { + std::stringstream ss; + ss << (GetMean() >= 0 ? GetMean() : -1) << ", " << + (GetStdDev() >= 0 ? GetStdDev() : -1); + return ss.str(); + } + void Log(const std::string& units) { BWE_TEST_LOGGING_LOG5("", "%f %s\t+/-%f\t[%f,%f]", GetMean(), units.c_str(), GetStdDev(), GetMin(), GetMax()); @@ -137,19 +153,22 @@ class Packet { public: Packet(); - Packet(int64_t send_time_us, uint32_t payload_size, + Packet(int flow_id, int64_t send_time_us, uint32_t payload_size, const RTPHeader& header); Packet(int64_t send_time_us, uint32_t sequence_number); bool operator<(const Packet& rhs) const; + int flow_id() const { return flow_id_; } int64_t creation_time_us() const { return creation_time_us_; } void set_send_time_us(int64_t send_time_us); int64_t send_time_us() const { return send_time_us_; } + void SetAbsSendTimeMs(int64_t abs_send_time_ms); uint32_t payload_size() const { return payload_size_; } const RTPHeader& header() const { return header_; } private: + int flow_id_; int64_t creation_time_us_; // Time when the packet was created. int64_t send_time_us_; // Time the packet left last processor touching it. uint32_t payload_size_; // Size of the (non-existent, simulated) payload. @@ -168,13 +187,16 @@ public: virtual ~PacketProcessorListener() {} - virtual void AddPacketProcessor(PacketProcessor* processor) = 0; + virtual void AddPacketProcessor(PacketProcessor* processor, + bool is_sender) = 0; virtual void RemovePacketProcessor(PacketProcessor* processor) = 0; }; class PacketProcessor { public: - explicit PacketProcessor(PacketProcessorListener* listener); + PacketProcessor(PacketProcessorListener* listener, bool is_sender); + PacketProcessor(PacketProcessorListener* listener, const FlowIds& flow_ids, + bool is_sender); virtual ~PacketProcessor(); // Called after each simulation batch to allow the processor to plot any @@ -186,8 +208,11 @@ // |send_time_us_|. The simulation time |time_ms| is optional to use. virtual void RunFor(int64_t time_ms, Packets* in_out) = 0; + const FlowIds& flow_ids() const { return flow_ids_; } + private: PacketProcessorListener* listener_; + FlowIds flow_ids_; DISALLOW_COPY_AND_ASSIGN(PacketProcessor); }; @@ -197,18 +222,22 @@ explicit RateCounterFilter(PacketProcessorListener* listener); RateCounterFilter(PacketProcessorListener* listener, const std::string& name); + RateCounterFilter(PacketProcessorListener* listener, + const FlowIds& flow_ids, + const std::string& name); virtual ~RateCounterFilter(); uint32_t packets_per_second() const; uint32_t bits_per_second() const; void LogStats(); + Stats GetBitrateStats() const; virtual void Plot(int64_t timestamp_ms); virtual void RunFor(int64_t time_ms, Packets* in_out); private: scoped_ptr rate_counter_; - Stats pps_stats_; + Stats packets_per_second_stats_; Stats kbps_stats_; std::string name_; @@ -280,16 +309,19 @@ class ChokeFilter : public PacketProcessor { public: explicit ChokeFilter(PacketProcessorListener* listener); - virtual ~ChokeFilter() {} + ChokeFilter(PacketProcessorListener* listener, const FlowIds& flow_ids); + virtual ~ChokeFilter(); void SetCapacity(uint32_t kbps); - void SetMaxDelay(int64_t max_delay_ms); + void SetMaxDelay(int max_delay_ms); virtual void RunFor(int64_t time_ms, Packets* in_out); + Stats GetDelayStats() const; + private: uint32_t kbps_; - int64_t max_delay_us_; int64_t last_send_time_us_; + scoped_ptr delay_cap_helper_; DISALLOW_IMPLICIT_CONSTRUCTORS(ChokeFilter); }; @@ -308,15 +340,23 @@ virtual void Plot(int64_t timestamp_ms); virtual void RunFor(int64_t time_ms, Packets* in_out); + void SetMaxDelay(int max_delay_ms); + Stats GetDelayStats() const; + Stats GetBitrateStats() const; + private: void ProceedToNextSlot(); typedef std::vector TimeList; + int64_t current_offset_us_; TimeList delivery_times_us_; TimeList::const_iterator next_delivery_it_; int64_t local_time_us_; scoped_ptr rate_counter_; std::string name_; + scoped_ptr delay_cap_helper_; + Stats packets_per_second_stats_; + Stats kbps_stats_; DISALLOW_COPY_AND_ASSIGN(TraceBasedDeliveryFilter); }; @@ -328,6 +368,7 @@ }; explicit PacketSender(PacketProcessorListener* listener); + PacketSender(PacketProcessorListener* listener, const FlowIds& flow_ids); virtual ~PacketSender() {} virtual uint32_t GetCapacityKbps() const { return 0; } @@ -337,33 +378,35 @@ // Note that changing the feedback interval affects the timing of when the // output of the estimators is sampled and therefore the baseline files may // have to be regenerated. - virtual int64_t GetFeedbackIntervalMs() const { return 1000; } + virtual int GetFeedbackIntervalMs() const { return 1000; } virtual void GiveFeedback(const Feedback& feedback) {} private: DISALLOW_COPY_AND_ASSIGN(PacketSender); }; -struct PacketSenderFactory { - PacketSenderFactory() {} - virtual ~PacketSenderFactory() {} - virtual PacketSender* Create() const = 0; -}; - class VideoSender : public PacketSender { public: - VideoSender(PacketProcessorListener* listener, float fps, uint32_t kbps, - uint32_t ssrc, float first_frame_offset); + VideoSender(int flow_id, + PacketProcessorListener* listener, + float fps, + uint32_t kbps, + uint32_t ssrc, + float first_frame_offset); virtual ~VideoSender() {} uint32_t max_payload_size_bytes() const { return kMaxPayloadSizeBytes; } uint32_t bytes_per_second() const { return bytes_per_second_; } - virtual uint32_t GetCapacityKbps() const; + virtual uint32_t GetCapacityKbps() const OVERRIDE; - virtual void RunFor(int64_t time_ms, Packets* in_out); + virtual void RunFor(int64_t time_ms, Packets* in_out) OVERRIDE; protected: + virtual uint32_t NextFrameSize(); + virtual uint32_t NextPacketSize(uint32_t frame_size, + uint32_t remaining_payload); + const uint32_t kMaxPayloadSizeBytes; const uint32_t kTimestampBase; const double frame_period_ms_; @@ -380,48 +423,88 @@ class AdaptiveVideoSender : public VideoSender { public: - AdaptiveVideoSender(PacketProcessorListener* listener, float fps, - uint32_t kbps, uint32_t ssrc, float first_frame_offset); + AdaptiveVideoSender(int flow_id, PacketProcessorListener* listener, + float fps, uint32_t kbps, uint32_t ssrc, + float first_frame_offset); virtual ~AdaptiveVideoSender() {} - virtual int64_t GetFeedbackIntervalMs() const { return 100; } - virtual void GiveFeedback(const Feedback& feedback); + virtual int GetFeedbackIntervalMs() const OVERRIDE { return 100; } + virtual void GiveFeedback(const Feedback& feedback) OVERRIDE; -private: + private: DISALLOW_IMPLICIT_CONSTRUCTORS(AdaptiveVideoSender); }; -class VideoPacketSenderFactory : public PacketSenderFactory { +class PeriodicKeyFrameSender : public AdaptiveVideoSender { public: - VideoPacketSenderFactory(float fps, uint32_t kbps, uint32_t ssrc, - float frame_offset) - : fps_(fps), - kbps_(kbps), - ssrc_(ssrc), - frame_offset_(frame_offset) { - } - virtual ~VideoPacketSenderFactory() {} - virtual PacketSender* Create() const { - return new VideoSender(NULL, fps_, kbps_, ssrc_, frame_offset_); - } + PeriodicKeyFrameSender(int flow_id, + PacketProcessorListener* listener, + float fps, + uint32_t kbps, + uint32_t ssrc, + float first_frame_offset, + int key_frame_interval); + virtual ~PeriodicKeyFrameSender() {} + protected: - float fps_; - uint32_t kbps_; - uint32_t ssrc_; - float frame_offset_; -}; + virtual uint32_t NextFrameSize() OVERRIDE; + virtual uint32_t NextPacketSize(uint32_t frame_size, + uint32_t remaining_payload) OVERRIDE; + + private: + int key_frame_interval_; + uint32_t frame_counter_; + int compensation_bytes_; + int compensation_per_frame_; + DISALLOW_IMPLICIT_CONSTRUCTORS(PeriodicKeyFrameSender); +}; + +class PacedVideoSender : public PacketSender, public PacedSender::Callback { + public: + PacedVideoSender(PacketProcessorListener* listener, + uint32_t kbps, AdaptiveVideoSender* source); + virtual ~PacedVideoSender() {} + + virtual int GetFeedbackIntervalMs() const OVERRIDE { return 100; } + virtual void GiveFeedback(const Feedback& feedback) OVERRIDE; + virtual void RunFor(int64_t time_ms, Packets* in_out) OVERRIDE; + + // Implements PacedSender::Callback. + virtual bool TimeToSendPacket(uint32_t ssrc, + uint16_t sequence_number, + int64_t capture_time_ms, + bool retransmission) OVERRIDE; + virtual int TimeToSendPadding(int bytes) OVERRIDE; + + private: + class ProbingPacedSender : public PacedSender { + public: + ProbingPacedSender(Clock* clock, + Callback* callback, + int bitrate_kbps, + int max_bitrate_kbps, + int min_bitrate_kbps) + : PacedSender(clock, + callback, + bitrate_kbps, + max_bitrate_kbps, + min_bitrate_kbps) {} -class AdaptiveVideoPacketSenderFactory : public VideoPacketSenderFactory { - public: - AdaptiveVideoPacketSenderFactory(float fps, uint32_t kbps, uint32_t ssrc, - float frame_offset) - : VideoPacketSenderFactory(fps, kbps, ssrc, frame_offset) {} - virtual ~AdaptiveVideoPacketSenderFactory() {} - virtual PacketSender* Create() const { - return new AdaptiveVideoSender(NULL, fps_, kbps_, ssrc_, frame_offset_); - } -}; + virtual bool ProbingExperimentIsEnabled() const OVERRIDE { return true; } + }; + void QueuePackets(Packets* batch, int64_t end_of_batch_time_us); + + static const int64_t kInitialTimeMs = 0; + SimulatedClock clock_; + int64_t start_of_run_ms_; + ProbingPacedSender pacer_; + Packets pacer_queue_; + Packets queue_; + AdaptiveVideoSender* source_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(PacedVideoSender); +}; } // namespace bwe } // namespace testing } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -12,8 +12,8 @@ #include -#include "gtest/gtest.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/constructormagic.h" #include "webrtc/test/testsupport/fileutils.h" using std::vector; @@ -41,12 +41,12 @@ } const double kPi = 3.14159265358979323846; - const double kScale = kN / (kStddev * std::sqrt(2.0 * kPi)); + const double kScale = kN / (kStddev * sqrt(2.0 * kPi)); const double kDiv = -2.0 * kStddev * kStddev; double self_corr = 0.0; double bucket_corr = 0.0; for (int n = 0; n < kBuckets; ++n) { - double normal_dist = kScale * std::exp((n - kMean) * (n - kMean) / kDiv); + double normal_dist = kScale * exp((n - kMean) * (n - kMean) / kDiv); self_corr += normal_dist * normal_dist; bucket_corr += normal_dist * buckets[n]; } @@ -181,10 +181,10 @@ void TestRateCounter(int64_t run_for_ms, uint32_t payload_bits, uint32_t expected_pps, uint32_t expected_bps) { Packets packets; - RTPHeader header = {0}; + RTPHeader header; // "Send" a packet every 10 ms. for (int64_t i = 0; i < run_for_ms; i += 10, now_ms_ += 10) { - packets.push_back(Packet(now_ms_ * 1000, payload_bits / 8, header)); + packets.push_back(Packet(0, now_ms_ * 1000, payload_bits / 8, header)); } filter_.RunFor(run_for_ms, &packets); ASSERT_TRUE(IsTimeSorted(packets)); @@ -500,7 +500,7 @@ TestJitterFilter(1031); } -static void TestReorderFilter(uint32_t reorder_percent, uint32_t near) { +static void TestReorderFilter(uint32_t reorder_percent, uint32_t near_value) { const uint32_t kPacketCount = 10000; // Generate packets with 10 ms interval. @@ -533,7 +533,8 @@ // Because reordering is random, we allow a threshold when comparing. The // maximum distance a packet can be moved is PacketCount - 1. - EXPECT_NEAR(((kPacketCount - 1) * reorder_percent) / 100, distance, near); + EXPECT_NEAR( + ((kPacketCount - 1) * reorder_percent) / 100, distance, near_value); } TEST(BweTestFramework_ReorderFilterTest, Reorder0) { @@ -582,12 +583,12 @@ uint32_t expected_kbit_transmitted) { // Generate a bunch of packets, apply choke, verify output is ordered. Packets packets; - RTPHeader header = {0}; + RTPHeader header; for (uint32_t i = 0; i < packets_to_generate; ++i) { int64_t send_time_ms = now_ms_ + (i * run_for_ms) / packets_to_generate; header.sequenceNumber = sequence_number_++; // Payload is 1000 bits. - packets.push_back(Packet(send_time_ms * 1000, 125, header)); + packets.push_back(Packet(0, send_time_ms * 1000, 125, header)); send_times_us_.push_back(send_time_ms * 1000); } ASSERT_TRUE(IsTimeSorted(packets)); @@ -709,12 +710,24 @@ TestChoke(&filter, 100, 100, 6); } -TEST_F(BweTestFramework_ChokeFilterTest, ShortTraceWrap) { - // According to the input file 10 packets should be transmitted within - // 140 milliseconds (at the wrapping point two packets are sent back to back). +TEST_F(BweTestFramework_ChokeFilterTest, ShortTraceTwoWraps) { + // According to the input file 19 packets should be transmitted within + // 280 milliseconds (at the wrapping point two packets are sent back to back). TraceBasedDeliveryFilter filter(NULL); ASSERT_TRUE(filter.Init(test::ResourcePath("synthetic-trace", "rx"))); - TestChoke(&filter, 140, 100, 10); + TestChoke(&filter, 280, 100, 19); +} + +TEST_F(BweTestFramework_ChokeFilterTest, ShortTraceMaxDelay) { + TraceBasedDeliveryFilter filter(NULL); + filter.SetMaxDelay(25); + ASSERT_TRUE(filter.Init(test::ResourcePath("synthetic-trace", "rx"))); + // Uses all slots up to 110 ms. Several packets are being dropped. + TestChoke(&filter, 110, 20, 9); + CheckMaxDelay(25); + // Simulate enough time for the next slot (at 135 ms) to be used. This makes + // sure that a slot isn't missed between runs. + TestChoke(&filter, 25, 1, 1); } void TestVideoSender(VideoSender* sender, int64_t run_for_ms, @@ -754,46 +767,46 @@ EXPECT_GE(1u, rtp_timestamp_wraps); } -TEST(BweTestFramework_VideoSenderTest, Fps1Kpbs80_1s) { +TEST(BweTestFramework_VideoSenderTest, Fps1Kbps80_1s) { // 1 fps, 80 kbps - VideoSender sender(NULL, 1.0f, 80, 0x1234, 0); + VideoSender sender(0, NULL, 1.0f, 80, 0x1234, 0); EXPECT_EQ(10000u, sender.bytes_per_second()); // We're at 1 fps, so all packets should be generated on first call, giving 10 // packets of each 1000 bytes, total 10000 bytes. - TestVideoSender(&sender, 1, 10, 1000, 10000); + TestVideoSender(&sender, 1, 9, 400, 10000); // 999ms, should see no output here. TestVideoSender(&sender, 998, 0, 0, 0); // 1999ms, should get data for one more frame. - TestVideoSender(&sender, 1000, 10, 1000, 10000); + TestVideoSender(&sender, 1000, 9, 400, 10000); // 2000ms, one more frame. - TestVideoSender(&sender, 1, 10, 1000, 10000); + TestVideoSender(&sender, 1, 9, 400, 10000); // 2999ms, should see nothing. TestVideoSender(&sender, 999, 0, 0, 0); } -TEST(BweTestFramework_VideoSenderTest, Fps1Kpbs80_1s_Offset) { +TEST(BweTestFramework_VideoSenderTest, Fps1Kbps80_1s_Offset) { // 1 fps, 80 kbps, offset 0.5 of a frame period, ==0.5s in this case. - VideoSender sender(NULL, 1.0f, 80, 0x1234, 0.5f); + VideoSender sender(0, NULL, 1.0f, 80, 0x1234, 0.5f); EXPECT_EQ(10000u, sender.bytes_per_second()); // 499ms, no output. TestVideoSender(&sender, 499, 0, 0, 0); // 500ms, first frame (this is the offset we set), 10 packets of 1000 bytes. - TestVideoSender(&sender, 1, 10, 1000, 10000); + TestVideoSender(&sender, 1, 9, 400, 10000); // 1499ms, nothing. TestVideoSender(&sender, 999, 0, 0, 0); // 1999ms, second frame. - TestVideoSender(&sender, 500, 10, 1000, 10000); + TestVideoSender(&sender, 500, 9, 400, 10000); // 2499ms, nothing. TestVideoSender(&sender, 500, 0, 0, 0); // 2500ms, third frame. - TestVideoSender(&sender, 1, 10, 1000, 10000); + TestVideoSender(&sender, 1, 9, 400, 10000); // 3499ms, nothing. TestVideoSender(&sender, 999, 0, 0, 0); } TEST(BweTestFramework_VideoSenderTest, Fps50Kpbs80_11s) { // 50 fps, 80 kbps. - VideoSender sender(NULL, 50.0f, 80, 0x1234, 0); + VideoSender sender(0, NULL, 50.0f, 80, 0x1234, 0); EXPECT_EQ(10000u, sender.bytes_per_second()); // 9998ms, should see 500 frames, 200 byte payloads, total 100000 bytes. TestVideoSender(&sender, 9998, 500, 200, 100000); @@ -809,7 +822,7 @@ TEST(BweTestFramework_VideoSenderTest, Fps10Kpbs120_1s) { // 20 fps, 120 kbps. - VideoSender sender(NULL, 20.0f, 120, 0x1234, 0); + VideoSender sender(0, NULL, 20.0f, 120, 0x1234, 0); EXPECT_EQ(15000u, sender.bytes_per_second()); // 498ms, 10 frames with 750 byte payloads, total 7500 bytes. TestVideoSender(&sender, 498, 10, 750, 7500); @@ -823,86 +836,86 @@ TestVideoSender(&sender, 1, 0, 0, 0); } -TEST(BweTestFramework_VideoSenderTest, Fps30Kpbs800_20s) { +TEST(BweTestFramework_VideoSenderTest, Fps30Kbps800_20s) { // 20 fps, 820 kbps. - VideoSender sender(NULL, 25.0f, 820, 0x1234, 0); + VideoSender sender(0, NULL, 25.0f, 820, 0x1234, 0); EXPECT_EQ(102500u, sender.bytes_per_second()); // 9998ms, 250 frames. 820 kbps = 102500 bytes/s, so total should be 1025000. // Each frame is 102500/25=4100 bytes, or 5 packets (4 @1000 bytes, 1 @100), // so packet count should be 5*250=1250 and last packet of each frame has // 100 bytes of payload. - TestVideoSender(&sender, 9998, 1250, 100, 1025000); + TestVideoSender(&sender, 9998, 1000, 500, 1025000); // 9999ms, nothing. TestVideoSender(&sender, 1, 0, 0, 0); // 19998ms, 250 more frames. - TestVideoSender(&sender, 9999, 1250, 100, 1025000); + TestVideoSender(&sender, 9999, 1000, 500, 1025000); // 19999ms, nothing. TestVideoSender(&sender, 1, 0, 0, 0); // 20038ms, one more frame, as described above (25fps == 40ms/frame). - TestVideoSender(&sender, 39, 5, 100, 4100); + TestVideoSender(&sender, 39, 4, 500, 4100); // 20039ms, nothing. TestVideoSender(&sender, 1, 0, 0, 0); } TEST(BweTestFramework_VideoSenderTest, TestAppendInOrder) { // 1 fps, 80 kbps, 250ms offset. - VideoSender sender1(NULL, 1.0f, 80, 0x1234, 0.25f); + VideoSender sender1(0, NULL, 1.0f, 80, 0x1234, 0.25f); EXPECT_EQ(10000u, sender1.bytes_per_second()); Packets packets; // Generate some packets, verify they are sorted. sender1.RunFor(999, &packets); ASSERT_TRUE(IsTimeSorted(packets)); ASSERT_TRUE(IsSequenceNumberSorted(packets)); - EXPECT_EQ(10u, packets.size()); + EXPECT_EQ(9u, packets.size()); // Generate some more packets and verify they are appended to end of list. sender1.RunFor(1000, &packets); ASSERT_TRUE(IsTimeSorted(packets)); ASSERT_TRUE(IsSequenceNumberSorted(packets)); - EXPECT_EQ(20u, packets.size()); + EXPECT_EQ(18u, packets.size()); // Another sender, 2 fps, 160 kpbs, 150ms offset - VideoSender sender2(NULL, 2.0f, 160, 0x2234, 0.30f); + VideoSender sender2(0, NULL, 2.0f, 160, 0x2234, 0.30f); EXPECT_EQ(20000u, sender2.bytes_per_second()); // Generate some packets, verify that they are merged with the packets already // on the list. sender2.RunFor(999, &packets); ASSERT_TRUE(IsTimeSorted(packets)); - EXPECT_EQ(40u, packets.size()); + EXPECT_EQ(36u, packets.size()); // Generate some more. sender2.RunFor(1000, &packets); ASSERT_TRUE(IsTimeSorted(packets)); - EXPECT_EQ(60u, packets.size()); + EXPECT_EQ(54u, packets.size()); } TEST(BweTestFramework_VideoSenderTest, FeedbackIneffective) { - VideoSender sender(NULL, 25.0f, 820, 0x1234, 0); + VideoSender sender(0, NULL, 25.0f, 820, 0x1234, 0); EXPECT_EQ(102500u, sender.bytes_per_second()); - TestVideoSender(&sender, 9998, 1250, 100, 1025000); + TestVideoSender(&sender, 9998, 1000, 500, 1025000); // Make sure feedback has no effect on a regular video sender. PacketSender::Feedback feedback = { 512000 }; sender.GiveFeedback(feedback); EXPECT_EQ(102500u, sender.bytes_per_second()); - TestVideoSender(&sender, 9998, 1250, 100, 1025000); + TestVideoSender(&sender, 9998, 1000, 500, 1025000); } TEST(BweTestFramework_AdaptiveVideoSenderTest, FeedbackChangesBitrate) { - AdaptiveVideoSender sender(NULL, 25.0f, 820, 0x1234, 0); + AdaptiveVideoSender sender(0, NULL, 25.0f, 820, 0x1234, 0); EXPECT_EQ(102500u, sender.bytes_per_second()); - TestVideoSender(&sender, 9998, 1250, 100, 1025000); + TestVideoSender(&sender, 9998, 1000, 500, 1025000); // Make sure we can reduce the bitrate. PacketSender::Feedback feedback = { 512000 }; sender.GiveFeedback(feedback); EXPECT_EQ(64000u, sender.bytes_per_second()); - TestVideoSender(&sender, 9998, 750, 560, 640000); + TestVideoSender(&sender, 9998, 750, 160, 640000); // Increase the bitrate to the initial bitrate and verify that the output is // the same. feedback.estimated_bps = 820000; sender.GiveFeedback(feedback); EXPECT_EQ(102500u, sender.bytes_per_second()); - TestVideoSender(&sender, 9998, 1250, 100, 1025000); + TestVideoSender(&sender, 9998, 1000, 500, 1025000); } } // namespace bwe } // namespace testing diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test.h 2015-02-03 14:33:36.000000000 +0000 @@ -8,11 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include #include #include -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" #include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" namespace webrtc { @@ -25,55 +27,96 @@ struct EstimatorConfig { EstimatorConfig() : debug_name(), + flow_id(0), estimator_factory(NULL), - update_baseline(false) { + control_type(kMimdControl), + update_baseline(false), + plot_delay(true), + plot_estimate(true) { } EstimatorConfig(std::string debug_name, - const RemoteBitrateEstimatorFactory* estimator_factory) + int flow_id, + const RemoteBitrateEstimatorFactory* estimator_factory, + bool plot_delay, + bool plot_estimate) + : debug_name(debug_name), + flow_id(flow_id), + estimator_factory(estimator_factory), + control_type(kMimdControl), + update_baseline(false), + plot_delay(plot_delay), + plot_estimate(plot_estimate) { + } + EstimatorConfig(std::string debug_name, + int flow_id, + const RemoteBitrateEstimatorFactory* estimator_factory, + RateControlType control_type, + bool plot_delay, + bool plot_estimate) : debug_name(debug_name), + flow_id(flow_id), estimator_factory(estimator_factory), - update_baseline(false) { + control_type(control_type), + update_baseline(false), + plot_delay(plot_delay), + plot_estimate(plot_estimate) { } EstimatorConfig(std::string debug_name, + int flow_id, const RemoteBitrateEstimatorFactory* estimator_factory, + RateControlType control_type, bool update_baseline) : debug_name(debug_name), + flow_id(flow_id), estimator_factory(estimator_factory), - update_baseline(update_baseline) { + control_type(control_type), + update_baseline(update_baseline), + plot_delay(false), + plot_estimate(false) { } std::string debug_name; + int flow_id; const RemoteBitrateEstimatorFactory* estimator_factory; + RateControlType control_type; bool update_baseline; + bool plot_delay; + bool plot_estimate; }; - std::vector sender_factories; std::vector estimator_configs; }; -class BweTest : public ::testing::TestWithParam, - public PacketProcessorListener { +class TestedEstimator; +class PacketProcessorRunner; + +class BweTest : public PacketProcessorListener { public: BweTest(); virtual ~BweTest(); - virtual void SetUp(); - virtual void TearDown(); - virtual void AddPacketProcessor(PacketProcessor* processor); + virtual void AddPacketProcessor(PacketProcessor* processor, bool is_sender); virtual void RemovePacketProcessor(PacketProcessor* processor); protected: + void SetupTestFromConfig(const BweTestConfig& config); void VerboseLogging(bool enable); void RunFor(int64_t time_ms); + std::string GetTestName() const; private: - class TestedEstimator; + typedef std::map EstimatorMap; + + void FindPacketsToProcess(const FlowIds& flow_ids, Packets* in, + Packets* out); + void GiveFeedbackToAffectedSenders(int flow_id, TestedEstimator* estimator); int64_t run_time_ms_; + int64_t time_now_ms_; int64_t simulation_interval_ms_; + EstimatorMap estimators_; Packets previous_packets_; - std::vector packet_senders_; - std::vector estimators_; - std::vector processors_; + std::vector senders_; + std::vector processors_; DISALLOW_COPY_AND_ASSIGN(BweTest); }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.cc 2015-02-03 14:33:36.000000000 +0000 @@ -12,9 +12,10 @@ #if BWE_TEST_LOGGING_COMPILE_TIME_ENABLE +#include +#include + #include -#include -#include #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/thread_wrapper.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h 2015-02-03 14:33:36.000000000 +0000 @@ -98,8 +98,8 @@ #include #include +#include "webrtc/base/constructormagic.h" #include "webrtc/common_types.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #define BWE_TEST_LOGGING_GLOBAL_CONTEXT(name) \ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.cc 2015-02-03 14:33:36.000000000 +0000 @@ -16,10 +16,7 @@ #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h" -#include "webrtc/modules/video_coding/main/test/rtp_file_reader.h" -#include "webrtc/modules/video_coding/main/test/rtp_player.h" - -using webrtc::rtpplayer::RtpPacketSourceInterface; +#include "webrtc/test/rtp_file_reader.h" const int kMinBitrateBps = 30000; @@ -27,23 +24,24 @@ char** argv, webrtc::Clock* clock, webrtc::RemoteBitrateObserver* observer, - RtpPacketSourceInterface** rtp_reader, + webrtc::test::RtpFileReader** rtp_reader, webrtc::RtpHeaderParser** parser, webrtc::RemoteBitrateEstimator** estimator, std::string* estimator_used) { - *rtp_reader = webrtc::rtpplayer::CreateRtpFileReader(argv[3]); + *rtp_reader = webrtc::test::RtpFileReader::Create( + webrtc::test::RtpFileReader::kRtpDump, argv[3]); if (!*rtp_reader) { - printf("Cannot open input file %s\n", argv[3]); + fprintf(stderr, "Cannot open input file %s\n", argv[3]); return false; } - printf("Input file: %s\n\n", argv[3]); + fprintf(stderr, "Input file: %s\n\n", argv[3]); webrtc::RTPExtensionType extension = webrtc::kRtpExtensionAbsoluteSendTime; if (strncmp("tsoffset", argv[1], 8) == 0) { extension = webrtc::kRtpExtensionTransmissionTimeOffset; - printf("Extension: toffset\n"); + fprintf(stderr, "Extension: toffset\n"); } else { - printf("Extension: abs\n"); + fprintf(stderr, "Extension: abs\n"); } int id = atoi(argv[2]); @@ -54,13 +52,15 @@ switch (extension) { case webrtc::kRtpExtensionAbsoluteSendTime: { webrtc::AbsoluteSendTimeRemoteBitrateEstimatorFactory factory; - *estimator = factory.Create(observer, clock, kMinBitrateBps); + *estimator = factory.Create(observer, clock, webrtc::kAimdControl, + kMinBitrateBps); *estimator_used = "AbsoluteSendTimeRemoteBitrateEstimator"; break; } case webrtc::kRtpExtensionTransmissionTimeOffset: { webrtc::RemoteBitrateEstimatorFactory factory; - *estimator = factory.Create(observer, clock, kMinBitrateBps); + *estimator = factory.Create(observer, clock, webrtc::kAimdControl, + kMinBitrateBps); *estimator_used = "RemoteBitrateEstimator"; break; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.h 2015-02-03 14:33:36.000000000 +0000 @@ -18,8 +18,8 @@ class RemoteBitrateEstimator; class RemoteBitrateObserver; class RtpHeaderParser; -namespace rtpplayer { -class RtpPacketSourceInterface; +namespace test { +class RtpFileReader; } } @@ -28,7 +28,7 @@ char** argv, webrtc::Clock* clock, webrtc::RemoteBitrateObserver* observer, - webrtc::rtpplayer::RtpPacketSourceInterface** rtp_reader, + webrtc::test::RtpFileReader** rtp_reader, webrtc::RtpHeaderParser** parser, webrtc::RemoteBitrateEstimator** estimator, std::string* estimator_used); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp_play.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp_play.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp_play.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp_play.cc 2015-02-03 14:33:36.000000000 +0000 @@ -14,11 +14,8 @@ #include "webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h" -#include "webrtc/modules/video_coding/main/test/rtp_file_reader.h" -#include "webrtc/modules/video_coding/main/test/rtp_player.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" - -using webrtc::rtpplayer::RtpPacketSourceInterface; +#include "webrtc/test/rtp_file_reader.h" class Observer : public webrtc::RemoteBitrateObserver { public: @@ -49,7 +46,7 @@ " is the id associated with the extension.\n"); return -1; } - RtpPacketSourceInterface* reader; + webrtc::test::RtpFileReader* reader; webrtc::RemoteBitrateEstimator* estimator; webrtc::RtpHeaderParser* parser; std::string estimator_used; @@ -59,7 +56,7 @@ &parser, &estimator, &estimator_used)) { return -1; } - webrtc::scoped_ptr rtp_reader(reader); + webrtc::scoped_ptr rtp_reader(reader); webrtc::scoped_ptr rtp_parser(parser); webrtc::scoped_ptr rbe(estimator); @@ -68,30 +65,25 @@ int64_t next_process_time_ms = 0; int64_t next_rtp_time_ms = 0; int64_t first_rtp_time_ms = -1; - const uint32_t kMaxPacketSize = 1500; - uint8_t packet_buffer[kMaxPacketSize]; - uint8_t* packet = packet_buffer; int non_zero_abs_send_time = 0; int non_zero_ts_offsets = 0; while (true) { - uint32_t next_rtp_time; if (next_rtp_time_ms <= clock.TimeInMilliseconds()) { - uint32_t packet_length = kMaxPacketSize; - if (rtp_reader->NextPacket(packet, &packet_length, - &next_rtp_time) == -1) { + webrtc::test::RtpFileReader::Packet packet; + if (!rtp_reader->NextPacket(&packet)) { break; } if (first_rtp_time_ms == -1) - first_rtp_time_ms = next_rtp_time; - next_rtp_time_ms = next_rtp_time - first_rtp_time_ms; + first_rtp_time_ms = packet.time_ms; + packet.time_ms = packet.time_ms - first_rtp_time_ms; webrtc::RTPHeader header; - parser->Parse(packet, packet_length, &header); + parser->Parse(packet.data, packet.length, &header); if (header.extension.absoluteSendTime != 0) ++non_zero_abs_send_time; if (header.extension.transmissionTimeOffset != 0) ++non_zero_ts_offsets; rbe->IncomingPacket(clock.TimeInMilliseconds(), - packet_length - header.headerLength, + static_cast(packet.length - header.headerLength), header); ++packet_counter; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/tools/rtp_to_text.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/tools/rtp_to_text.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/tools/rtp_to_text.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/tools/rtp_to_text.cc 2015-02-03 14:33:36.000000000 +0000 @@ -9,68 +9,70 @@ */ #include +#include #include "webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h" -#include "webrtc/modules/video_coding/main/test/rtp_file_reader.h" -#include "webrtc/modules/video_coding/main/test/rtp_player.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" - -using webrtc::rtpplayer::RtpPacketSourceInterface; +#include "webrtc/test/rtp_file_reader.h" int main(int argc, char** argv) { - if (argc < 5) { - printf("Usage: rtp_to_text " - " \n"); - printf(" can either be:\n" + if (argc < 4) { + fprintf(stderr, "Usage: rtp_to_text " + " [-t]\n"); + fprintf(stderr, " can either be:\n" " abs for absolute send time or\n" " tsoffset for timestamp offset.\n" - " is the id associated with the extension.\n"); + " is the id associated with the extension.\n" + " -t is an optional flag, if set only packet arrival time will be" + " output.\n"); return -1; } - RtpPacketSourceInterface* reader; + webrtc::test::RtpFileReader* reader; webrtc::RtpHeaderParser* parser; if (!ParseArgsAndSetupEstimator(argc, argv, NULL, NULL, &reader, &parser, NULL, NULL)) { return -1; } - webrtc::scoped_ptr rtp_reader(reader); + bool arrival_time_only = (argc >= 5 && strncmp(argv[4], "-t", 2) == 0); + webrtc::scoped_ptr rtp_reader(reader); webrtc::scoped_ptr rtp_parser(parser); - - FILE* out_file = fopen(argv[4], "wt"); - if (!out_file) - printf("Cannot open output file %s\n", argv[4]); - - printf("Output file: %s\n\n", argv[4]); - fprintf(out_file, "seqnum timestamp ts_offset abs_sendtime recvtime " + fprintf(stdout, "seqnum timestamp ts_offset abs_sendtime recvtime " "markerbit ssrc size\n"); int packet_counter = 0; - static const uint32_t kMaxPacketSize = 1500; - uint8_t packet_buffer[kMaxPacketSize]; - uint8_t* packet = packet_buffer; - uint32_t packet_length = kMaxPacketSize; - uint32_t time_ms = 0; int non_zero_abs_send_time = 0; int non_zero_ts_offsets = 0; - while (rtp_reader->NextPacket(packet, &packet_length, &time_ms) == 0) { - webrtc::RTPHeader header = {}; - parser->Parse(packet, packet_length, &header); + webrtc::test::RtpFileReader::Packet packet; + while (rtp_reader->NextPacket(&packet)) { + webrtc::RTPHeader header; + parser->Parse(packet.data, packet.length, &header); if (header.extension.absoluteSendTime != 0) ++non_zero_abs_send_time; if (header.extension.transmissionTimeOffset != 0) ++non_zero_ts_offsets; - fprintf(out_file, "%u %u %d %u %u %d %u %u\n", header.sequenceNumber, - header.timestamp, header.extension.transmissionTimeOffset, - header.extension.absoluteSendTime, time_ms, header.markerBit, - header.ssrc, packet_length); - packet_length = kMaxPacketSize; + if (arrival_time_only) { + std::stringstream ss; + ss << static_cast(packet.time_ms) * 1000000; + fprintf(stdout, "%s\n", ss.str().c_str()); + } else { + fprintf(stdout, + "%u %u %d %u %u %d %u %d\n", + header.sequenceNumber, + header.timestamp, + header.extension.transmissionTimeOffset, + header.extension.absoluteSendTime, + packet.time_ms, + header.markerBit, + header.ssrc, + static_cast(packet.length)); + } ++packet_counter; } - printf("Parsed %d packets\n", packet_counter); - printf("Packets with non-zero absolute send time: %d\n", + fprintf(stderr, "Parsed %d packets\n", packet_counter); + fprintf(stderr, "Packets with non-zero absolute send time: %d\n", non_zero_abs_send_time); - printf("Packets with non-zero timestamp offset: %d\n", + fprintf(stderr, "Packets with non-zero timestamp offset: %d\n", non_zero_ts_offsets); return 0; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/BUILD.gn 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,118 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../../build/webrtc.gni") + +source_set("rtp_rtcp") { + sources = [ + # Common + "interface/fec_receiver.h", + "interface/receive_statistics.h", + "interface/remote_ntp_time_estimator.h", + "interface/rtp_header_parser.h", + "interface/rtp_payload_registry.h", + "interface/rtp_receiver.h", + "interface/rtp_rtcp.h", + "interface/rtp_rtcp_defines.h", + "source/bitrate.cc", + "source/bitrate.h", + "source/byte_io.h", + "source/fec_receiver_impl.cc", + "source/fec_receiver_impl.h", + "source/receive_statistics_impl.cc", + "source/receive_statistics_impl.h", + "source/remote_ntp_time_estimator.cc", + "source/rtp_header_parser.cc", + "source/rtp_rtcp_config.h", + "source/rtp_rtcp_impl.cc", + "source/rtp_rtcp_impl.h", + "source/rtcp_packet.cc", + "source/rtcp_packet.h", + "source/rtcp_receiver.cc", + "source/rtcp_receiver.h", + "source/rtcp_receiver_help.cc", + "source/rtcp_receiver_help.h", + "source/rtcp_sender.cc", + "source/rtcp_sender.h", + "source/rtcp_utility.cc", + "source/rtcp_utility.h", + "source/rtp_header_extension.cc", + "source/rtp_header_extension.h", + "source/rtp_receiver_impl.cc", + "source/rtp_receiver_impl.h", + "source/rtp_sender.cc", + "source/rtp_sender.h", + "source/rtp_utility.cc", + "source/rtp_utility.h", + "source/ssrc_database.cc", + "source/ssrc_database.h", + "source/tmmbr_help.cc", + "source/tmmbr_help.h", + # Audio Files + "source/dtmf_queue.cc", + "source/dtmf_queue.h", + "source/rtp_receiver_audio.cc", + "source/rtp_receiver_audio.h", + "source/rtp_sender_audio.cc", + "source/rtp_sender_audio.h", + # Video Files + "source/fec_private_tables_random.h", + "source/fec_private_tables_bursty.h", + "source/forward_error_correction.cc", + "source/forward_error_correction.h", + "source/forward_error_correction_internal.cc", + "source/forward_error_correction_internal.h", + "source/producer_fec.cc", + "source/producer_fec.h", + "source/rtp_packet_history.cc", + "source/rtp_packet_history.h", + "source/rtp_payload_registry.cc", + "source/rtp_receiver_strategy.cc", + "source/rtp_receiver_strategy.h", + "source/rtp_receiver_video.cc", + "source/rtp_receiver_video.h", + "source/rtp_sender_video.cc", + "source/rtp_sender_video.h", + "source/video_codec_information.h", + "source/rtp_format.cc", + "source/rtp_format.h", + "source/rtp_format_h264.cc", + "source/rtp_format_h264.h", + "source/rtp_format_vp8.cc", + "source/rtp_format_vp8.h", + "source/rtp_format_video_generic.cc", + "source/rtp_format_video_generic.h", + "source/vp8_partition_aggregator.cc", + "source/vp8_partition_aggregator.h", + # Mocks + "mocks/mock_rtp_rtcp.h", + "source/mock/mock_rtp_payload_strategy.h", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ + "../../system_wrappers", + "../pacing", + "../remote_bitrate_estimator", + ] + + if (is_win) { + cflags = [ + # TODO(jschuh): Bug 1348: fix this warning. + "/wd4267", # size_t to int truncations + ] + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/fec_receiver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/fec_receiver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/fec_receiver.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/fec_receiver.h 2015-02-03 14:33:36.000000000 +0000 @@ -18,7 +18,7 @@ class FecReceiver { public: - static FecReceiver* Create(int32_t id, RtpData* callback); + static FecReceiver* Create(RtpData* callback); virtual ~FecReceiver() {} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/remote_ntp_time_estimator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/remote_ntp_time_estimator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/remote_ntp_time_estimator.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/remote_ntp_time_estimator.h 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_RTP_RTCP_INTERFACE_REMOTE_NTP_TIME_ESTIMATOR_H_ +#define WEBRTC_MODULES_RTP_RTCP_INTERFACE_REMOTE_NTP_TIME_ESTIMATOR_H_ + +#include "webrtc/system_wrappers/interface/rtp_to_ntp.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class Clock; +class TimestampExtrapolator; + +// RemoteNtpTimeEstimator can be used to estimate a given RTP timestamp's NTP +// time in local timebase. +// Note that it needs to be trained with at least 2 RTCP SR (by calling +// |UpdateRtcpTimestamp|) before it can be used. +class RemoteNtpTimeEstimator { + public: + explicit RemoteNtpTimeEstimator(Clock* clock); + + ~RemoteNtpTimeEstimator(); + + // Updates the estimator with round trip time |rtt|, NTP seconds |ntp_secs|, + // NTP fraction |ntp_frac| and RTP timestamp |rtcp_timestamp|. + bool UpdateRtcpTimestamp(uint16_t rtt, uint32_t ntp_secs, uint32_t ntp_frac, + uint32_t rtp_timestamp); + + // Estimates the NTP timestamp in local timebase from |rtp_timestamp|. + // Returns the NTP timestamp in ms when success. -1 if failed. + int64_t Estimate(uint32_t rtp_timestamp); + + private: + Clock* clock_; + scoped_ptr ts_extrapolator_; + RtcpList rtcp_list_; + int64_t last_timing_log_ms_; + DISALLOW_COPY_AND_ASSIGN(RemoteNtpTimeEstimator); +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_RTP_RTCP_INTERFACE_REMOTE_NTP_TIME_ESTIMATOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h 2015-02-03 14:33:36.000000000 +0000 @@ -23,13 +23,14 @@ virtual ~RtpHeaderParser() {} // Returns true if the packet is an RTCP packet, false otherwise. - static bool IsRtcp(const uint8_t* packet, int length); + static bool IsRtcp(const uint8_t* packet, size_t length); // Parses the packet and stores the parsed packet in |header|. Returns true on // success, false otherwise. // This method is thread-safe in the sense that it can parse multiple packets // at once. - virtual bool Parse(const uint8_t* packet, int length, + virtual bool Parse(const uint8_t* packet, + size_t length, RTPHeader* header) const = 0; // Registers an RTP header extension and binds it to |id|. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h 2015-02-03 14:33:36.000000000 +0000 @@ -25,17 +25,15 @@ virtual bool CodecsMustBeUnique() const = 0; - virtual bool PayloadIsCompatible( - const ModuleRTPUtility::Payload& payload, - const uint32_t frequency, - const uint8_t channels, - const uint32_t rate) const = 0; + virtual bool PayloadIsCompatible(const RtpUtility::Payload& payload, + const uint32_t frequency, + const uint8_t channels, + const uint32_t rate) const = 0; - virtual void UpdatePayloadRate( - ModuleRTPUtility::Payload* payload, - const uint32_t rate) const = 0; + virtual void UpdatePayloadRate(RtpUtility::Payload* payload, + const uint32_t rate) const = 0; - virtual ModuleRTPUtility::Payload* CreatePayloadType( + virtual RtpUtility::Payload* CreatePayloadType( const char payloadName[RTP_PAYLOAD_NAME_SIZE], const int8_t payloadType, const uint32_t frequency, @@ -43,7 +41,7 @@ const uint32_t rate) const = 0; virtual int GetPayloadTypeFrequency( - const ModuleRTPUtility::Payload& payload) const = 0; + const RtpUtility::Payload& payload) const = 0; static RTPPayloadStrategy* CreateStrategy(const bool handling_audio); @@ -54,8 +52,7 @@ class RTPPayloadRegistry { public: // The registry takes ownership of the strategy. - RTPPayloadRegistry(const int32_t id, - RTPPayloadStrategy* rtp_payload_strategy); + RTPPayloadRegistry(RTPPayloadStrategy* rtp_payload_strategy); ~RTPPayloadRegistry(); int32_t RegisterReceivePayload( @@ -76,10 +73,10 @@ const uint32_t rate, int8_t* payload_type) const; - void SetRtxStatus(bool enable, uint32_t ssrc); - bool RtxEnabled() const; + void SetRtxSsrc(uint32_t ssrc); + void SetRtxPayloadType(int payload_type); bool IsRtx(const RTPHeader& header) const; @@ -100,9 +97,8 @@ int GetPayloadTypeFrequency(uint8_t payload_type) const; - bool PayloadTypeToPayload( - const uint8_t payload_type, - ModuleRTPUtility::Payload*& payload) const; + bool PayloadTypeToPayload(const uint8_t payload_type, + RtpUtility::Payload*& payload) const; void ResetLastReceivedPayloadTypes() { CriticalSectionScoped cs(crit_sect_.get()); @@ -152,8 +148,7 @@ bool IsRtxInternal(const RTPHeader& header) const; scoped_ptr crit_sect_; - ModuleRTPUtility::PayloadTypeMap payload_type_map_; - int32_t id_; + RtpUtility::PayloadTypeMap payload_type_map_; scoped_ptr rtp_payload_strategy_; int8_t red_payload_type_; int8_t ulpfec_payload_type_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h 2015-02-03 14:33:36.000000000 +0000 @@ -63,6 +63,11 @@ kRtpAlive = 2 }; +enum ProtectionType { + kUnprotectedPacket, + kProtectedPacket +}; + enum StorageType { kDontStore, kDontRetransmit, @@ -192,6 +197,22 @@ typedef std::list ReportBlockList; +struct RtpState { + RtpState() + : sequence_number(0), + start_timestamp(0), + timestamp(0), + capture_time_ms(-1), + last_timestamp_time_ms(-1), + media_has_been_sent(false) {} + uint16_t sequence_number; + uint32_t start_timestamp; + uint32_t timestamp; + int64_t capture_time_ms; + int64_t last_timestamp_time_ms; + bool media_has_been_sent; +}; + class RtpData { public: @@ -339,7 +360,7 @@ } virtual bool OnRecoveredPacket(const uint8_t* packet, - int packet_length) { + int packet_length) OVERRIDE { return true; } }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h 2015-02-03 14:33:36.000000000 +0000 @@ -47,8 +47,8 @@ * intra_frame_callback - Called when the receiver request a intra frame. * bandwidth_callback - Called when we receive a changed estimate from * the receiver of out stream. - * audio_messages - Telehone events. May not be NULL; default callback - * will do nothing. + * audio_messages - Telephone events. May not be NULL; default + * callback will do nothing. * remote_bitrate_estimator - Estimates the bandwidth available for a set of * streams from the same client. * paced_sender - Spread any bursts of packets into smaller @@ -67,6 +67,9 @@ RtpAudioFeedback* audio_messages; RemoteBitrateEstimator* remote_bitrate_estimator; PacedSender* paced_sender; + BitrateStatisticsObserver* send_bitrate_observer; + FrameCountObserver* send_frame_count_observer; + SendSideDelayObserver* send_side_delay_observer; }; /* @@ -203,6 +206,10 @@ */ virtual int32_t SetSequenceNumber(const uint16_t seq) = 0; + virtual void SetRtpStateForSsrc(uint32_t ssrc, + const RtpState& rtp_state) = 0; + virtual bool GetRtpStateForSsrc(uint32_t ssrc, RtpState* rtp_state) = 0; + /* * Get SSRC */ @@ -213,7 +220,7 @@ * * return -1 on failure else 0 */ - virtual int32_t SetSSRC(const uint32_t ssrc) = 0; + virtual void SetSSRC(const uint32_t ssrc) = 0; /* * Get CSRC @@ -249,10 +256,14 @@ virtual int32_t SetCSRCStatus(const bool include) = 0; /* - * Turn on/off sending RTX (RFC 4588) on a specific SSRC. + * Turn on/off sending RTX (RFC 4588). The modes can be set as a combination + * of values of the enumerator RtxMode. */ - virtual int32_t SetRTXSendStatus(int modes, bool set_ssrc, - uint32_t ssrc) = 0; + virtual void SetRTXSendStatus(int modes) = 0; + + // Sets the SSRC to use when sending RTX packets. This doesn't enable RTX, + // only the SSRC is set. + virtual void SetRtxSsrc(uint32_t ssrc) = 0; // Sets the payload type to use when sending RTX packets. Note that this // doesn't enable RTX, only the payload type is set. @@ -261,8 +272,8 @@ /* * Get status of sending RTX (RFC 4588) on a specific SSRC. */ - virtual int32_t RTXSendStatus(int* modes, uint32_t* ssrc, - int* payloadType) const = 0; + virtual void RTXSendStatus(int* modes, uint32_t* ssrc, + int* payloadType) const = 0; /* * sends kRtcpByeCode when going from true to false @@ -301,13 +312,6 @@ uint32_t* nackRate) const = 0; /* - * Called on any new send bitrate estimate. - */ - virtual void RegisterVideoBitrateObserver( - BitrateStatisticsObserver* observer) = 0; - virtual BitrateStatisticsObserver* GetVideoBitrateObserver() const = 0; - - /* * Used by the codec module to deliver a video or audio frame for * packetization. * @@ -338,10 +342,6 @@ virtual int TimeToSendPadding(int bytes) = 0; - virtual void RegisterSendFrameCountObserver( - FrameCountObserver* observer) = 0; - virtual FrameCountObserver* GetSendFrameCountObserver() const = 0; - virtual bool GetSendSideDelay(int* avg_send_delay_ms, int* max_send_delay_ms) const = 0; @@ -379,13 +379,6 @@ virtual int32_t SetCNAME(const char cName[RTCP_CNAME_SIZE]) = 0; /* - * Get RTCP CName (i.e unique identifier) - * - * return -1 on failure else 0 - */ - virtual int32_t CNAME(char cName[RTCP_CNAME_SIZE]) = 0; - - /* * Get remote CName * * return -1 on failure else 0 @@ -519,6 +512,13 @@ virtual int32_t RemoveRTCPReportBlock(const uint32_t SSRC) = 0; /* + * Get number of sent and received RTCP packet types. + */ + virtual void GetRtcpPacketTypeCounters( + RtcpPacketTypeCounter* packets_sent, + RtcpPacketTypeCounter* packets_received) const = 0; + + /* * (APP) Application specific data * * return -1 on failure else 0 @@ -675,25 +675,6 @@ int8_t& payloadType) const = 0; /* - * Set status and ID for header-extension-for-audio-level-indication. - * See http://tools.ietf.org/html/rfc6464 for more details. - * - * return -1 on failure else 0 - */ - virtual int32_t SetRTPAudioLevelIndicationStatus( - const bool enable, - const uint8_t ID) = 0; - - /* - * Get status and ID for header-extension-for-audio-level-indication. - * - * return -1 on failure else 0 - */ - virtual int32_t GetRTPAudioLevelIndicationStatus( - bool& enable, - uint8_t& ID) const = 0; - - /* * Store the audio level in dBov for header-extension-for-audio-level- * indication. * This API shall be called before transmision of an RTP packet to ensure diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h 2015-02-03 14:33:36.000000000 +0000 @@ -19,6 +19,17 @@ namespace webrtc { +class MockRtpData : public RtpData { + public: + MOCK_METHOD3(OnReceivedPayloadData, + int32_t(const uint8_t* payloadData, + const uint16_t payloadSize, + const WebRtcRTPHeader* rtpHeader)); + + MOCK_METHOD2(OnRecoveredPacket, + bool(const uint8_t* packet, int packet_length)); +}; + class MockRtpRtcp : public RtpRtcp { public: MOCK_METHOD1(ChangeUniqueId, @@ -74,20 +85,25 @@ uint16_t()); MOCK_METHOD1(SetSequenceNumber, int32_t(const uint16_t seq)); + MOCK_METHOD2(SetRtpStateForSsrc, + void(uint32_t ssrc, const RtpState& rtp_state)); + MOCK_METHOD2(GetRtpStateForSsrc, bool(uint32_t ssrc, RtpState* rtp_state)); MOCK_CONST_METHOD0(SSRC, uint32_t()); MOCK_METHOD1(SetSSRC, - int32_t(const uint32_t ssrc)); + void(const uint32_t ssrc)); MOCK_CONST_METHOD1(CSRCs, int32_t(uint32_t arrOfCSRC[kRtpCsrcSize])); MOCK_METHOD2(SetCSRCs, int32_t(const uint32_t arrOfCSRC[kRtpCsrcSize], const uint8_t arrLength)); MOCK_METHOD1(SetCSRCStatus, int32_t(const bool include)); - MOCK_METHOD3(SetRTXSendStatus, - int32_t(int modes, bool setSSRC, uint32_t ssrc)); + MOCK_METHOD1(SetRTXSendStatus, + void(int modes)); MOCK_CONST_METHOD3(RTXSendStatus, - int32_t(int* modes, uint32_t* ssrc, int* payload_type)); + void(int* modes, uint32_t* ssrc, int* payload_type)); + MOCK_METHOD1(SetRtxSsrc, + void(uint32_t)); MOCK_METHOD1(SetRtxSendPayloadType, void(int)); MOCK_METHOD1(SetSendingStatus, @@ -130,8 +146,6 @@ int32_t(const RTCPMethod method)); MOCK_METHOD1(SetCNAME, int32_t(const char cName[RTCP_CNAME_SIZE])); - MOCK_METHOD1(CNAME, - int32_t(char cName[RTCP_CNAME_SIZE])); MOCK_CONST_METHOD2(RemoteCNAME, int32_t(const uint32_t remoteSSRC, char cName[RTCP_CNAME_SIZE])); @@ -168,6 +182,8 @@ int32_t(const uint32_t SSRC, const RTCPReportBlock* receiveBlock)); MOCK_METHOD1(RemoveRTCPReportBlock, int32_t(const uint32_t SSRC)); + MOCK_CONST_METHOD2(GetRtcpPacketTypeCounters, + void(RtcpPacketTypeCounter*, RtcpPacketTypeCounter*)); MOCK_METHOD4(SetRTCPApplicationSpecificData, int32_t(const uint8_t subType, const uint32_t name, const uint8_t* data, const uint16_t length)); MOCK_METHOD1(SetRTCPVoIPMetrics, @@ -221,9 +237,9 @@ int32_t(const int8_t payloadType)); MOCK_CONST_METHOD1(SendREDPayloadType, int32_t(int8_t& payloadType)); - MOCK_METHOD2(SetRTPAudioLevelIndicationStatus, + MOCK_METHOD2(SetAudioLevelIndicationStatus, int32_t(const bool enable, const uint8_t ID)); - MOCK_CONST_METHOD2(GetRTPAudioLevelIndicationStatus, + MOCK_CONST_METHOD2(GetAudioLevelIndicationStatus, int32_t(bool& enable, uint8_t& ID)); MOCK_METHOD1(SetAudioLevel, int32_t(const uint8_t level_dBov)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -1,5 +1,6 @@ -pwestin@webrtc.org stefan@webrtc.org henrik.lundin@webrtc.org mflodman@webrtc.org -asapersson@webrtc.org \ No newline at end of file +asapersson@webrtc.org + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../android-webrtc.mk - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_rtp_rtcp -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := \ - bitrate.cc \ - rtp_rtcp_impl.cc \ - rtcp_receiver.cc \ - rtcp_receiver_help.cc \ - rtcp_sender.cc \ - rtcp_utility.cc \ - rtp_receiver.cc \ - rtp_sender.cc \ - rtp_utility.cc \ - rtp_header_extension.cc \ - ssrc_database.cc \ - tmmbr_help.cc \ - dtmf_queue.cc \ - rtp_receiver_audio.cc \ - rtp_sender_audio.cc \ - bandwidth_management.cc \ - forward_error_correction.cc \ - forward_error_correction_internal.cc \ - overuse_detector.cc \ - remote_rate_control.cc \ - rtp_packet_history.cc \ - receiver_fec.cc \ - rtp_receiver_video.cc \ - rtp_sender_video.cc \ - rtp_format_vp8.cc \ - transmission_bucket.cc \ - vp8_partition_aggregator.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../../interface \ - $(LOCAL_PATH)/../../../system_wrappers/interface - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.cc 2015-02-03 14:33:36.000000000 +0000 @@ -16,20 +16,19 @@ #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" // RFC 5109 namespace webrtc { -FecReceiver* FecReceiver::Create(int32_t id, RtpData* callback) { - return new FecReceiverImpl(id, callback); +FecReceiver* FecReceiver::Create(RtpData* callback) { + return new FecReceiverImpl(callback); } -FecReceiverImpl::FecReceiverImpl(const int32_t id, RtpData* callback) - : id_(id), - crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), +FecReceiverImpl::FecReceiverImpl(RtpData* callback) + : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), recovered_packet_callback_(callback), - fec_(new ForwardErrorCorrection(id)) {} + fec_(new ForwardErrorCorrection()) {} FecReceiverImpl::~FecReceiverImpl() { while (!received_packet_list_.empty()) { @@ -103,8 +102,7 @@ if (timestamp_offset != 0) { // |timestampOffset| should be 0. However, it's possible this is the first // location a corrupt payload can be caught, so don't assert. - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, id_, - "Corrupt payload found in %s", __FUNCTION__); + LOG(LS_WARNING) << "Corrupt payload found."; delete received_packet; return -1; } @@ -173,7 +171,7 @@ payload_data_length - REDHeaderLength); received_packet->pkt->length = payload_data_length - REDHeaderLength; received_packet->ssrc = - ModuleRTPUtility::BufferToUWord32(&incoming_rtp_packet[8]); + RtpUtility::BufferToUWord32(&incoming_rtp_packet[8]); } else { // copy the RTP header diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h 2015-02-03 14:33:36.000000000 +0000 @@ -25,7 +25,7 @@ class FecReceiverImpl : public FecReceiver { public: - FecReceiverImpl(const int32_t id, RtpData* callback); + FecReceiverImpl(RtpData* callback); virtual ~FecReceiverImpl(); virtual int32_t AddReceivedRedPacket(const RTPHeader& rtp_header, @@ -36,7 +36,6 @@ virtual int32_t ProcessReceivedFec() OVERRIDE; private: - int id_; scoped_ptr crit_sect_; RtpData* recovered_packet_callback_; ForwardErrorCorrection* fec_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -15,6 +15,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/rtp_rtcp/interface/fec_receiver.h" +#include "webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h" #include "webrtc/modules/rtp_rtcp/source/fec_test_helper.h" #include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h" @@ -25,22 +26,11 @@ namespace webrtc { -class MockRtpData : public RtpData { - public: - MOCK_METHOD3(OnReceivedPayloadData, - int32_t(const uint8_t* payloadData, - const uint16_t payloadSize, - const WebRtcRTPHeader* rtpHeader)); - - MOCK_METHOD2(OnRecoveredPacket, - bool(const uint8_t* packet, int packet_length)); -}; - class ReceiverFecTest : public ::testing::Test { protected: virtual void SetUp() { - fec_ = new ForwardErrorCorrection(0); - receiver_fec_ = FecReceiver::Create(0, &rtp_data_callback_); + fec_ = new ForwardErrorCorrection(); + receiver_fec_ = FecReceiver::Create(&rtp_data_callback_); generator_ = new FrameGenerator(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/fec_test_helper.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/fec_test_helper.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/fec_test_helper.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/fec_test_helper.cc 2015-02-03 14:33:36.000000000 +0000 @@ -86,9 +86,9 @@ data[0] = 0x80; // Version 2. data[1] = header->payloadType; data[1] |= (header->markerBit ? kRtpMarkerBitMask : 0); - ModuleRTPUtility::AssignUWord16ToBuffer(data + 2, header->sequenceNumber); - ModuleRTPUtility::AssignUWord32ToBuffer(data + 4, header->timestamp); - ModuleRTPUtility::AssignUWord32ToBuffer(data + 8, header->ssrc); + RtpUtility::AssignUWord16ToBuffer(data + 2, header->sequenceNumber); + RtpUtility::AssignUWord32ToBuffer(data + 4, header->timestamp); + RtpUtility::AssignUWord32ToBuffer(data + 8, header->ssrc); } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc 2015-02-03 14:33:36.000000000 +0000 @@ -20,7 +20,7 @@ #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" #include "webrtc/modules/rtp_rtcp/source/forward_error_correction_internal.h" #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { @@ -82,9 +82,8 @@ ForwardErrorCorrection::RecoveredPacket::RecoveredPacket() {} ForwardErrorCorrection::RecoveredPacket::~RecoveredPacket() {} -ForwardErrorCorrection::ForwardErrorCorrection(int32_t id) - : id_(id), - generated_fec_packets_(kMaxMediaPackets), +ForwardErrorCorrection::ForwardErrorCorrection() + : generated_fec_packets_(kMaxMediaPackets), fec_packet_received_(false) {} ForwardErrorCorrection::~ForwardErrorCorrection() {} @@ -112,43 +111,23 @@ bool use_unequal_protection, FecMaskType fec_mask_type, PacketList* fec_packet_list) { - if (media_packet_list.empty()) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, - "%s media packet list is empty", __FUNCTION__); - return -1; - } - if (!fec_packet_list->empty()) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, - "%s FEC packet list is not empty", __FUNCTION__); - return -1; - } const uint16_t num_media_packets = media_packet_list.size(); - bool l_bit = (num_media_packets > 8 * kMaskSizeLBitClear); - int num_maskBytes = l_bit ? kMaskSizeLBitSet : kMaskSizeLBitClear; + + // Sanity check arguments. + assert(num_media_packets > 0); + assert(num_important_packets >= 0 && + num_important_packets <= num_media_packets); + assert(fec_packet_list->empty()); if (num_media_packets > kMaxMediaPackets) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, - "%s can only protect %d media packets per frame; %d requested", - __FUNCTION__, kMaxMediaPackets, num_media_packets); + LOG(LS_WARNING) << "Can't protect " << num_media_packets + << " media packets per frame. Max is " << kMaxMediaPackets; return -1; } - // Error checking on the number of important packets. - // Can't have more important packets than media packets. - if (num_important_packets > num_media_packets) { - WEBRTC_TRACE( - kTraceError, kTraceRtpRtcp, id_, - "Number of important packets (%d) greater than number of media " - "packets (%d)", - num_important_packets, num_media_packets); - return -1; - } - if (num_important_packets < 0) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, - "Number of important packets (%d) less than zero", - num_important_packets); - return -1; - } + bool l_bit = (num_media_packets > 8 * kMaskSizeLBitClear); + int num_maskBytes = l_bit ? kMaskSizeLBitSet : kMaskSizeLBitClear; + // Do some error checking on the media packets. PacketList::const_iterator media_list_it = media_packet_list.begin(); while (media_list_it != media_packet_list.end()) { @@ -156,20 +135,16 @@ assert(media_packet); if (media_packet->length < kRtpHeaderSize) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, - "%s media packet (%d bytes) is smaller than RTP header", - __FUNCTION__, media_packet->length); + LOG(LS_WARNING) << "Media packet " << media_packet->length << " bytes " + << "is smaller than RTP header."; return -1; } // Ensure our FEC packets will fit in a typical MTU. if (media_packet->length + PacketOverhead() + kTransportOverhead > IP_PACKET_SIZE) { - WEBRTC_TRACE( - kTraceError, kTraceRtpRtcp, id_, - "%s media packet (%d bytes) with overhead is larger than MTU(%d)", - __FUNCTION__, media_packet->length, IP_PACKET_SIZE); - return -1; + LOG(LS_WARNING) << "Media packet " << media_packet->length << " bytes " + << "with overhead is larger than " << IP_PACKET_SIZE; } media_list_it++; } @@ -255,7 +230,7 @@ Packet* media_packet = *media_list_it; // Assign network-ordered media payload length. - ModuleRTPUtility::AssignUWord16ToBuffer( + RtpUtility::AssignUWord16ToBuffer( media_payload_length, media_packet->length - kRtpHeaderSize); fec_packet_length = media_packet->length + fec_rtp_offset; @@ -457,7 +432,7 @@ // -- ULP header -- // Copy the payload size to the protection length field. // (We protect the entire packet.) - ModuleRTPUtility::AssignUWord16ToBuffer( + RtpUtility::AssignUWord16ToBuffer( &generated_fec_packets_[i].data[10], generated_fec_packets_[i].length - kFecHeaderSize - ulp_header_size); @@ -562,7 +537,7 @@ fec_packet->ssrc = rx_packet->ssrc; const uint16_t seq_num_base = - ModuleRTPUtility::BufferToUWord16(&fec_packet->pkt->data[2]); + RtpUtility::BufferToUWord16(&fec_packet->pkt->data[2]); const uint16_t maskSizeBytes = (fec_packet->pkt->data[0] & 0x40) ? kMaskSizeLBitSet : kMaskSizeLBitClear; // L bit set? @@ -582,9 +557,7 @@ } if (fec_packet->protected_pkt_list.empty()) { // All-zero packet mask; we can discard this FEC packet. - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, id_, - "FEC packet %u has an all-zero packet mask.", - fec_packet->seq_num, __FUNCTION__); + LOG(LS_WARNING) << "FEC packet has an all-zero packet mask."; delete fec_packet; } else { AssignRecoveredPackets(fec_packet, recovered_packet_list); @@ -630,6 +603,23 @@ while (!received_packet_list->empty()) { ReceivedPacket* rx_packet = received_packet_list->front(); + // Check for discarding oldest FEC packet, to avoid wrong FEC decoding from + // sequence number wrap-around. Detection of old FEC packet is based on + // sequence number difference of received packet and oldest packet in FEC + // packet list. + // TODO(marpan/holmer): We should be able to improve detection/discarding of + // old FEC packets based on timestamp information or better sequence number + // thresholding (e.g., to distinguish between wrap-around and reordering). + if (!fec_packet_list_.empty()) { + uint16_t seq_num_diff = abs( + static_cast(rx_packet->seq_num) - + static_cast(fec_packet_list_.front()->seq_num)); + if (seq_num_diff > 0x3fff) { + DiscardFECPacket(fec_packet_list_.front()); + fec_packet_list_.pop_front(); + } + } + if (rx_packet->is_fec) { InsertFECPacket(rx_packet, recovered_packet_list); } else { @@ -660,7 +650,7 @@ // Copy FEC payload, skipping the ULP header. memcpy(&recovered->pkt->data[kRtpHeaderSize], &fec_packet->pkt->data[kFecHeaderSize + ulp_header_size], - ModuleRTPUtility::BufferToUWord16(protection_length)); + RtpUtility::BufferToUWord16(protection_length)); // Copy the length recovery field. memcpy(recovered->length_recovery, &fec_packet->pkt->data[8], 2); // Copy the first 2 bytes of the FEC header. @@ -668,8 +658,7 @@ // Copy the 5th to 8th bytes of the FEC header. memcpy(&recovered->pkt->data[4], &fec_packet->pkt->data[4], 4); // Set the SSRC field. - ModuleRTPUtility::AssignUWord32ToBuffer(&recovered->pkt->data[8], - fec_packet->ssrc); + RtpUtility::AssignUWord32ToBuffer(&recovered->pkt->data[8], fec_packet->ssrc); } void ForwardErrorCorrection::FinishRecovery(RecoveredPacket* recovered) { @@ -678,12 +667,11 @@ recovered->pkt->data[0] &= 0xbf; // Clear the 2nd bit. // Set the SN field. - ModuleRTPUtility::AssignUWord16ToBuffer(&recovered->pkt->data[2], - recovered->seq_num); + RtpUtility::AssignUWord16ToBuffer(&recovered->pkt->data[2], + recovered->seq_num); // Recover the packet length. recovered->pkt->length = - ModuleRTPUtility::BufferToUWord16(recovered->length_recovery) + - kRtpHeaderSize; + RtpUtility::BufferToUWord16(recovered->length_recovery) + kRtpHeaderSize; } void ForwardErrorCorrection::XorPackets(const Packet* src_packet, @@ -698,8 +686,8 @@ } // XOR with the network-ordered payload size. uint8_t media_payload_length[2]; - ModuleRTPUtility::AssignUWord16ToBuffer(media_payload_length, - src_packet->length - kRtpHeaderSize); + RtpUtility::AssignUWord16ToBuffer(media_payload_length, + src_packet->length - kRtpHeaderSize); dst_packet->length_recovery[0] ^= media_payload_length[0]; dst_packet->length_recovery[1] ^= media_payload_length[1]; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/forward_error_correction.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/forward_error_correction.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/forward_error_correction.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/forward_error_correction.h 2015-02-03 14:33:36.000000000 +0000 @@ -117,8 +117,7 @@ typedef std::list ReceivedPacketList; typedef std::list RecoveredPacketList; - // \param[in] id Module ID - ForwardErrorCorrection(int32_t id); + ForwardErrorCorrection(); virtual ~ForwardErrorCorrection(); @@ -304,7 +303,6 @@ static void DiscardOldPackets(RecoveredPacketList* recovered_packet_list); static uint16_t ParseSequenceNumber(uint8_t* packet); - int32_t id_; std::vector generated_fec_packets_; FecPacketList fec_packet_list_; bool fec_packet_received_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/bitstream_builder.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/bitstream_builder.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/bitstream_builder.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/bitstream_builder.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,580 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "bitstream_builder.h" - -#include - -namespace webrtc { -BitstreamBuilder::BitstreamBuilder(uint8_t* data, const uint32_t dataSize) : - _data(data), - _dataSize(dataSize), - _byteOffset(0), - _bitOffset(0) -{ - memset(data, 0, dataSize); -} - -uint32_t -BitstreamBuilder::Length() const -{ - return _byteOffset+ (_bitOffset?1:0); -} - -int32_t -BitstreamBuilder::Add1Bit(const uint8_t bit) -{ - // sanity - if(_bitOffset + 1 > 8) - { - if(_dataSize < Length()+1) - { - // not enough space in buffer - return -1; - } - } - Add1BitWithoutSanity(bit); - return 0; -} - -void -BitstreamBuilder::Add1BitWithoutSanity(const uint8_t bit) -{ - if(bit & 0x1) - { - _data[_byteOffset] += (1 << (7-_bitOffset)); - } - - if(_bitOffset == 7) - { - // last bit in byte - _bitOffset = 0; - _byteOffset++; - } else - { - _bitOffset++; - } -} - -int32_t -BitstreamBuilder::Add2Bits(const uint8_t bits) -{ - // sanity - if(_bitOffset + 2 > 8) - { - if(_dataSize < Length()+1) - { - // not enough space in buffer - return -1; - } - } - Add1BitWithoutSanity(bits >> 1); - Add1BitWithoutSanity(bits); - return 0; -} - -int32_t -BitstreamBuilder::Add3Bits(const uint8_t bits) -{ - // sanity - if(_bitOffset + 3 > 8) - { - if(_dataSize < Length()+1) - { - // not enough space in buffer - return -1; - } - } - Add1BitWithoutSanity(bits >> 2); - Add1BitWithoutSanity(bits >> 1); - Add1BitWithoutSanity(bits); - return 0; -} - -int32_t -BitstreamBuilder::Add4Bits(const uint8_t bits) -{ - // sanity - if(_bitOffset + 4 > 8) - { - if(_dataSize < Length()+1) - { - // not enough space in buffer - return -1; - } - } - Add1BitWithoutSanity(bits >> 3); - Add1BitWithoutSanity(bits >> 2); - Add1BitWithoutSanity(bits >> 1); - Add1BitWithoutSanity(bits); - return 0; -} - -int32_t -BitstreamBuilder::Add5Bits(const uint8_t bits) -{ - // sanity - if(_bitOffset + 5 > 8) - { - if(_dataSize < Length()+1) - { - // not enough space in buffer - return -1; - } - } - Add1BitWithoutSanity(bits >> 4); - Add1BitWithoutSanity(bits >> 3); - Add1BitWithoutSanity(bits >> 2); - Add1BitWithoutSanity(bits >> 1); - Add1BitWithoutSanity(bits); - return 0; -} - -int32_t -BitstreamBuilder::Add6Bits(const uint8_t bits) -{ - // sanity - if(_bitOffset + 6 > 8) - { - if(_dataSize < Length()+1) - { - // not enough space in buffer - return -1; - } - } - Add1BitWithoutSanity(bits >> 5); - Add1BitWithoutSanity(bits >> 4); - Add1BitWithoutSanity(bits >> 3); - Add1BitWithoutSanity(bits >> 2); - Add1BitWithoutSanity(bits >> 1); - Add1BitWithoutSanity(bits); - return 0; -} - -int32_t -BitstreamBuilder::Add7Bits(const uint8_t bits) -{ - // sanity - if(_bitOffset + 7 > 8) - { - if(_dataSize < Length()+1) - { - // not enough space in buffer - return -1; - } - } - Add1BitWithoutSanity(bits >> 6); - Add1BitWithoutSanity(bits >> 5); - Add1BitWithoutSanity(bits >> 4); - Add1BitWithoutSanity(bits >> 3); - Add1BitWithoutSanity(bits >> 2); - Add1BitWithoutSanity(bits >> 1); - Add1BitWithoutSanity(bits); - return 0; -} - -int32_t -BitstreamBuilder::Add8Bits(const uint8_t bits) -{ - // sanity - if(_dataSize < Length()+1) - { - // not enough space in buffer - return -1; - } - if(_bitOffset == 0) - { - _data[_byteOffset] = bits; - } else - { - _data[_byteOffset] += (bits >> _bitOffset); - _data[_byteOffset+1] += (bits << (8-_bitOffset)); - } - _byteOffset++; - return 0; -} - -int32_t -BitstreamBuilder::Add16Bits(const uint16_t bits) -{ - // sanity - if(_dataSize < Length()+2) - { - // not enough space in buffer - return -1; - } - if(_bitOffset == 0) - { - _data[_byteOffset] = (uint8_t)(bits >> 8); - _data[_byteOffset+1] = (uint8_t)(bits); - } else - { - _data[_byteOffset] += (uint8_t)(bits >> (_bitOffset + 8)); - _data[_byteOffset+1] += (uint8_t)(bits >> _bitOffset); - _data[_byteOffset+2] += (uint8_t)(bits << (8-_bitOffset)); - } - _byteOffset += 2; - return 0; -} - -int32_t -BitstreamBuilder::Add24Bits(const uint32_t bits) -{ - // sanity - if(_dataSize < Length()+3) - { - // not enough space in buffer - return -1; - } - if(_bitOffset == 0) - { - _data[_byteOffset] = (uint8_t)(bits >> 16); - _data[_byteOffset+1] = (uint8_t)(bits >> 8); - _data[_byteOffset+2] = (uint8_t)(bits); - } else - { - _data[_byteOffset] += (uint8_t)(bits >> (_bitOffset+16)); - _data[_byteOffset+1] += (uint8_t)(bits >> (_bitOffset+8)); - _data[_byteOffset+2] += (uint8_t)(bits >> (_bitOffset)); - _data[_byteOffset+3] += (uint8_t)(bits << (8-_bitOffset)); - } - _byteOffset += 3; - return 0; -} - -int32_t -BitstreamBuilder::Add32Bits(const uint32_t bits) -{ - // sanity - if(_dataSize < Length()+4) - { - // not enough space in buffer - return -1; - } - if(_bitOffset == 0) - { - _data[_byteOffset] = (uint8_t)(bits >> 24); - _data[_byteOffset+1] = (uint8_t)(bits >> 16); - _data[_byteOffset+2] = (uint8_t)(bits >> 8); - _data[_byteOffset+3] = (uint8_t)(bits); - } else - { - _data[_byteOffset] += (uint8_t)(bits >> (_bitOffset+24)); - _data[_byteOffset+1] += (uint8_t)(bits >> (_bitOffset+16)); - _data[_byteOffset+2] += (uint8_t)(bits >> (_bitOffset+8)); - _data[_byteOffset+3] += (uint8_t)(bits >> (_bitOffset)); - _data[_byteOffset+4] += (uint8_t)(bits << (8-_bitOffset)); - } - _byteOffset += 4; - return 0; -} - -// Exp-Golomb codes -/* - with "prefix" and "suffix" bits and assignment to codeNum ranges (informative) - Bit string form Range of codeNum - 1 0 - 0 1 x0 1..2 2bits-1 - 0 0 1 x1 x0 3..6 3bits-1 - 0 0 0 1 x2 x1 x0 7..14 4bits-1 - 0 0 0 0 1 x3 x2 x1 x0 15..30 - 0 0 0 0 0 1 x4 x3 x2 x1 x0 31..62 -*/ -int32_t -BitstreamBuilder::AddUE(const uint32_t value) -{ - // un-rolled on 8 bit base to avoid too deep if else chain - if(value < 0x0000ffff) - { - if(value < 0x000000ff) - { - if(value == 0) - { - if(AddPrefix(0) != 0) - { - return -1; - } - } else if(value < 3) - { - if(AddPrefix(1) != 0) - { - return -1; - } - AddSuffix(1, value-1); - } else if(value < 7) - { - if(AddPrefix(2) != 0) - { - return -1; - } - AddSuffix(2, value-3); - } else if(value < 15) - { - if(AddPrefix(3) != 0) - { - return -1; - } - AddSuffix(3, value-7); - } else if(value < 31) - { - if(AddPrefix(4) != 0) - { - return -1; - } - AddSuffix(4, value-15); - } else if(value < 63) - { - if(AddPrefix(5) != 0) - { - return -1; - } - AddSuffix(5, value-31); - } else if(value < 127) - { - if(AddPrefix(6) != 0) - { - return -1; - } - AddSuffix(6, value-63); - } else - { - if(AddPrefix(7) != 0) - { - return -1; - } - AddSuffix(7, value-127); - } - }else - { - if(value < 0x000001ff) - { - if(AddPrefix(8) != 0) - { - return -1; - } - AddSuffix(8, value-0x000000ff); - } else if(value < 0x000003ff) - { - if(AddPrefix(9) != 0) - { - return -1; - } - AddSuffix(9, value-0x000001ff); - } else if(value < 0x000007ff) - { - if(AddPrefix(10) != 0) - { - return -1; - } - AddSuffix(10, value-0x000003ff); - } else if(value < 0x00000fff) - { - if(AddPrefix(11) != 0) - { - return -1; - } - AddSuffix(1, value-0x000007ff); - } else if(value < 0x00001fff) - { - if(AddPrefix(12) != 0) - { - return -1; - } - AddSuffix(12, value-0x00000fff); - } else if(value < 0x00003fff) - { - if(AddPrefix(13) != 0) - { - return -1; - } - AddSuffix(13, value-0x00001fff); - } else if(value < 0x00007fff) - { - if(AddPrefix(14) != 0) - { - return -1; - } - AddSuffix(14, value-0x00003fff); - } else - { - if(AddPrefix(15) != 0) - { - return -1; - } - AddSuffix(15, value-0x00007fff); - } - } - }else - { - if(value < 0x00ffffff) - { - if(value < 0x0001ffff) - { - if(AddPrefix(16) != 0) - { - return -1; - } - AddSuffix(16, value-0x0000ffff); - } else if(value < 0x0003ffff) - { - if(AddPrefix(17) != 0) - { - return -1; - } - AddSuffix(17, value-0x0001ffff); - } else if(value < 0x0007ffff) - { - if(AddPrefix(18) != 0) - { - return -1; - } - AddSuffix(18, value-0x0003ffff); - } else if(value < 0x000fffff) - { - if(AddPrefix(19) != 0) - { - return -1; - } - AddSuffix(19, value-0x0007ffff); - } else if(value < 0x001fffff) - { - if(AddPrefix(20) != 0) - { - return -1; - } - AddSuffix(20, value-0x000fffff); - } else if(value < 0x003fffff) - { - if(AddPrefix(21) != 0) - { - return -1; - } - AddSuffix(21, value-0x001fffff); - } else if(value < 0x007fffff) - { - if(AddPrefix(22) != 0) - { - return -1; - } - AddSuffix(22, value-0x003fffff); - } else - { - if(AddPrefix(23) != 0) - { - return -1; - } - AddSuffix(23, value-0x007fffff); - } - } else - { - if(value < 0x01ffffff) - { - if(AddPrefix(24) != 0) - { - return -1; - } - AddSuffix(24, value-0x00ffffff); - } else if(value < 0x03ffffff) - { - if(AddPrefix(25) != 0) - { - return -1; - } - AddSuffix(25, value-0x01ffffff); - } else if(value < 0x07ffffff) - { - if(AddPrefix(26) != 0) - { - return -1; - } - AddSuffix(26, value-0x03ffffff); - } else if(value < 0x0fffffff) - { - if(AddPrefix(27) != 0) - { - return -1; - } - AddSuffix(27, value-0x07ffffff); - } else if(value < 0x1fffffff) - { - if(AddPrefix(28) != 0) - { - return -1; - } - AddSuffix(28, value-0x0fffffff); - } else if(value < 0x3fffffff) - { - if(AddPrefix(29) != 0) - { - return -1; - } - AddSuffix(29, value-0x1fffffff); - } else if(value < 0x7fffffff) - { - if(AddPrefix(30) != 0) - { - return -1; - } - AddSuffix(30, value-0x3fffffff); - } else if(value < 0xffffffff) - { - if(AddPrefix(31) != 0) - { - return -1; - } - AddSuffix(31, value-0x7ffffff); - } else - { - if(AddPrefix(32) != 0) - { - return -1; - } - AddSuffix(32, 0); // exactly 0xffffffff - } - } - } - return 0; -} - -int32_t -BitstreamBuilder::AddPrefix(const uint8_t numZeros) -{ - // sanity for the sufix too - uint32_t numBitsToAdd = numZeros * 2 + 1; - if(((_dataSize - _byteOffset) *8 + 8-_bitOffset) < numBitsToAdd) - { - return -1; - } - - // add numZeros - for (uint32_t i = 0; i < numZeros; i++) - { - Add1Bit(0); - } - Add1Bit(1); - return 0; -} - -void -BitstreamBuilder::AddSuffix(const uint8_t numBits, const uint32_t rest) -{ - // most significant bit first - for(int32_t i = numBits - 1; i >= 0; i--) - { - if(( rest >> i) & 0x1) - { - Add1Bit(1); - }else - { - Add1Bit(0); - } - } -} -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/bitstream_builder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/bitstream_builder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/bitstream_builder.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/bitstream_builder.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_BITSTREAM_BUILDER_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_BITSTREAM_BUILDER_H_ - -#include "webrtc/typedefs.h" - -namespace webrtc { -class BitstreamBuilder -{ -public: - BitstreamBuilder(uint8_t* data, const uint32_t dataSize); - - uint32_t Length() const; - - int32_t Add1Bit(const uint8_t bit); - int32_t Add2Bits(const uint8_t bits); - int32_t Add3Bits(const uint8_t bits); - int32_t Add4Bits(const uint8_t bits); - int32_t Add5Bits(const uint8_t bits); - int32_t Add6Bits(const uint8_t bits); - int32_t Add7Bits(const uint8_t bits); - int32_t Add8Bits(const uint8_t bits); - int32_t Add16Bits(const uint16_t bits); - int32_t Add24Bits(const uint32_t bits); - int32_t Add32Bits(const uint32_t bits); - - // Exp-Golomb codes - int32_t AddUE(const uint32_t value); - -private: - int32_t AddPrefix(const uint8_t numZeros); - void AddSuffix(const uint8_t numBits, const uint32_t rest); - void Add1BitWithoutSanity(const uint8_t bit); - - uint8_t* _data; - uint32_t _dataSize; - - uint32_t _byteOffset; - uint8_t _bitOffset; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_BITSTREAM_BUILDER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/bitstream_parser.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/bitstream_parser.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/bitstream_parser.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/bitstream_parser.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,217 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "bitstream_parser.h" - -namespace webrtc { -BitstreamParser::BitstreamParser(const uint8_t* data, const uint32_t dataLength) : - _data(data), - _dataLength(dataLength), - _byteOffset(0), - _bitOffset(0) -{ -} - // todo should we have any error codes from this? - -uint8_t -BitstreamParser::Get1Bit() -{ - uint8_t retVal = 0x1 & (_data[_byteOffset] >> (7-_bitOffset++)); - - // prepare next byte - if(_bitOffset == 8) - { - _bitOffset = 0; - _byteOffset++; - } - return retVal; -} - -uint8_t -BitstreamParser::Get2Bits() -{ - uint8_t retVal = (Get1Bit() << 1); - retVal += Get1Bit(); - return retVal; -} - -uint8_t -BitstreamParser::Get3Bits() -{ - uint8_t retVal = (Get1Bit() << 2); - retVal += (Get1Bit() << 1); - retVal += Get1Bit(); - return retVal; -} - -uint8_t -BitstreamParser::Get4Bits() -{ - uint8_t retVal = (Get1Bit() << 3); - retVal += (Get1Bit() << 2); - retVal += (Get1Bit() << 1); - retVal += Get1Bit(); - return retVal; -} - -uint8_t -BitstreamParser::Get5Bits() -{ - uint8_t retVal = (Get1Bit() << 4); - retVal += (Get1Bit() << 3); - retVal += (Get1Bit() << 2); - retVal += (Get1Bit() << 1); - retVal += Get1Bit(); - return retVal; -} - -uint8_t -BitstreamParser::Get6Bits() -{ - uint8_t retVal = (Get1Bit() << 5); - retVal += (Get1Bit() << 4); - retVal += (Get1Bit() << 3); - retVal += (Get1Bit() << 2); - retVal += (Get1Bit() << 1); - retVal += Get1Bit(); - return retVal; -} - -uint8_t -BitstreamParser::Get7Bits() -{ - uint8_t retVal = (Get1Bit() << 6); - retVal += (Get1Bit() << 5); - retVal += (Get1Bit() << 4); - retVal += (Get1Bit() << 3); - retVal += (Get1Bit() << 2); - retVal += (Get1Bit() << 1); - retVal += Get1Bit(); - return retVal; -} - -uint8_t -BitstreamParser::Get8Bits() -{ - uint16_t retVal; - - if(_bitOffset != 0) - { - // read 16 bits - retVal = (_data[_byteOffset] << 8)+ (_data[_byteOffset+1]) ; - retVal = retVal >> (8-_bitOffset); - } else - { - retVal = _data[_byteOffset]; - } - _byteOffset++; - return (uint8_t)retVal; -} - -uint16_t -BitstreamParser::Get16Bits() -{ - uint32_t retVal; - - if(_bitOffset != 0) - { - // read 24 bits - retVal = (_data[_byteOffset] << 16) + (_data[_byteOffset+1] << 8) + (_data[_byteOffset+2]); - retVal = retVal >> (8-_bitOffset); - }else - { - // read 16 bits - retVal = (_data[_byteOffset] << 8) + (_data[_byteOffset+1]) ; - } - _byteOffset += 2; - return (uint16_t)retVal; -} - -uint32_t -BitstreamParser::Get24Bits() -{ - uint32_t retVal; - - if(_bitOffset != 0) - { - // read 32 bits - retVal = (_data[_byteOffset] << 24) + (_data[_byteOffset+1] << 16) + (_data[_byteOffset+2] << 8) + (_data[_byteOffset+3]); - retVal = retVal >> (8-_bitOffset); - }else - { - // read 24 bits - retVal = (_data[_byteOffset] << 16) + (_data[_byteOffset+1] << 8) + (_data[_byteOffset+2]) ; - } - _byteOffset += 3; - return retVal & 0x00ffffff; // we need to clean up the high 8 bits -} - -uint32_t -BitstreamParser::Get32Bits() -{ - uint32_t retVal; - - if(_bitOffset != 0) - { - // read 40 bits - uint64_t tempVal = _data[_byteOffset]; - tempVal <<= 8; - tempVal += _data[_byteOffset+1]; - tempVal <<= 8; - tempVal += _data[_byteOffset+2]; - tempVal <<= 8; - tempVal += _data[_byteOffset+3]; - tempVal <<= 8; - tempVal += _data[_byteOffset+4]; - tempVal >>= (8-_bitOffset); - - retVal = uint32_t(tempVal); - }else - { - // read 32 bits - retVal = (_data[_byteOffset]<< 24) + (_data[_byteOffset+1] << 16) + (_data[_byteOffset+2] << 8) + (_data[_byteOffset+3]) ; - } - _byteOffset += 4; - return retVal; -} - -// Exp-Golomb codes -/* - with "prefix" and "suffix" bits and assignment to codeNum ranges (informative) - Bit string form Range of codeNum - 1 0 - 0 1 x0 1..2 - 0 0 1 x1 x0 3..6 - 0 0 0 1 x2 x1 x0 7..14 - 0 0 0 0 1 x3 x2 x1 x0 15..30 - 0 0 0 0 0 1 x4 x3 x2 x1 x0 31..62 -*/ - -uint32_t -BitstreamParser::GetUE() -{ - uint32_t retVal = 0; - uint8_t numLeadingZeros = 0; - - while (Get1Bit() != 1) - { - numLeadingZeros++; - } - // prefix - retVal = (1 << numLeadingZeros) - 1; - - // suffix - while (numLeadingZeros) - { - retVal += (Get1Bit() << --numLeadingZeros); - } - return retVal; -} -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/bitstream_parser.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/bitstream_parser.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/bitstream_parser.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/bitstream_parser.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_BITSTREAM_PARSER_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_BITSTREAM_PARSER_H_ - -#include "webrtc/typedefs.h" - -namespace webrtc { -class BitstreamParser -{ -public: - BitstreamParser(const uint8_t* data, const uint32_t dataLength); - - uint8_t Get1Bit(); - uint8_t Get2Bits(); - uint8_t Get3Bits(); - uint8_t Get4Bits(); - uint8_t Get5Bits(); - uint8_t Get6Bits(); - uint8_t Get7Bits(); - uint8_t Get8Bits(); - uint16_t Get16Bits(); - uint32_t Get24Bits(); - uint32_t Get32Bits(); - - // Exp-Golomb codes - uint32_t GetUE(); - -private: - const uint8_t* _data; - const uint32_t _dataLength; - - uint32_t _byteOffset; - uint8_t _bitOffset; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_BITSTREAM_PARSER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/h264_information.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/h264_information.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/h264_information.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/h264_information.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,818 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include "h264_information.h" - -//#define DEBUG_SEI_MESSAGE 1 - -#ifdef DEBUG_SEI_MESSAGE - #include "bitstream_parser.h" - #include - #include - - uint32_t BitRateBPS(uint16_t x ) - { - return (x & 0x3fff) * uint32_t(pow(10.0f,(2 + (x >> 14)))); - } - -#endif - -namespace webrtc { -H264Information::H264Information(const bool SVC) - : _SVC(SVC) - -{ -} - -H264Information::~H264Information() -{ - -} - -void -H264Information::Reset() -{ - _parsedLength = 0; - _remLength = 0; - _length = 0; - _info.numNALUs = 0; - _info.numLayers = 0; - - memset(_info.startCodeSize, 0, sizeof(_info.startCodeSize)); - memset(_info.payloadSize, 0, sizeof(_info.payloadSize)); - memset(_info.NRI, 0, sizeof(_info.NRI)); - memset(_info.type, 0, sizeof(_info.type)); - memset(_info.accLayerSize, 0, sizeof(_info.accLayerSize)); - - for (int32_t i = 0; i < KMaxNumberOfNALUs; i++) - { - _info.SVCheader[i].idr = 0; - _info.SVCheader[i].priorityID = 0; - _info.SVCheader[i].interLayerPred = 0; - _info.SVCheader[i].dependencyID = 0; - _info.SVCheader[i].qualityID = 0; - _info.SVCheader[i].temporalID = 0; - _info.SVCheader[i].useRefBasePic = 0; - _info.SVCheader[i].discardable = 0; - _info.SVCheader[i].output = 0; - - _info.PACSI[i].X = 0; - _info.PACSI[i].Y = 0; -// _info.PACSI[i].T = 0; - _info.PACSI[i].A = 0; - _info.PACSI[i].P = 0; - _info.PACSI[i].C = 0; - _info.PACSI[i].S = 0; - _info.PACSI[i].E = 0; - _info.PACSI[i].TL0picIDx = 0; - _info.PACSI[i].IDRpicID = 0; - _info.PACSI[i].DONC = 0; - _info.PACSI[i].numSEINALUs = 0; - _info.PACSI[i].NALlength = 5; - } -} - -/******************************************************************************* - * int32_t GetInfo(const uint8_t* ptrEncodedBuffer, - * const uint32_t length, - * const H264Info*& ptrInfo); - * - * Gets information from an encoded stream. - * - * Input: - * - ptrEncodedBuffer : Pointer to encoded stream. - * - length : Length in bytes of encoded stream. - * - * Output: - * - ptrInfo : Pointer to struct with H.264 info. - * - * Return value: - * - 0 : ok - * - (-1) : Error - */ -int32_t -H264Information::GetInfo(const uint8_t* ptrEncodedBuffer, - const uint32_t length, - const H264Info*& ptrInfo) -{ - if (!ptrEncodedBuffer || length < 4) - { - return -1; - } - - if (!HasInfo(length)) - { - if (-1 == FindInfo(ptrEncodedBuffer, length)) - { - Reset(); - return -1; - } - } - ptrInfo = &_info; - return 0; -} - -RtpVideoCodecTypes -H264Information::Type() -{ - if(_SVC) - { - return RTP_H264_SVCVideo; - } - return RTP_H264Video; -} - - -/******************************************************************************* - * bool HasInfo(const uint32_t length); - * - * Checks if information has already been stored for this encoded stream. - * - * Input: - * - length : Length in bytes of encoded stream. - * - * Return value: - * - true (false) : Information has (not) been stored. - */ - -bool -H264Information::HasInfo(const uint32_t length) -{ - if (!_info.numNALUs) - { - return false; - } - - // has info, make sure current length matches info length - if (length != _length) - { - Reset(); - return false; - } - - return true; -} - -/******************************************************************************* - * int32_t FindInfo(const uint8_t* ptrEncodedBuffer, - * const uint32_t length); - * - * Parses the encoded stream. - * - * Input: - * - ptrEncodedBuffer : Pointer to encoded stream. - * - length : Length in bytes of encoded stream. - * - * Return value: - * - 0 : ok - * - (-1) : Error - */ -int32_t -H264Information::FindInfo(const uint8_t* ptrEncodedBuffer, const uint32_t length) -{ - _ptrData = ptrEncodedBuffer; - _length = length; - _parsedLength = 0; - _remLength = length; - - do - { - // Get start code length - if (FindNALUStartCodeSize() == -1) - { - Reset(); - return -1; - } - - // Get NAL unit payload size - int32_t foundLast = FindNALU(); - if (foundLast == -1) - { - Reset(); - return -1; - } - - // Validate parsed length - if (_parsedLength > _length) - { - Reset(); - return -1; - } - - // Get NRI - GetNRI(); - - // Get type - if (FindNALUType() == -1) - { - Reset(); - return -1; - } - - // Set layer start end bit - SetLayerSEBit(foundLast); - - - // Last NAL unit found? - if (foundLast == 1) - { - if (_parsedLength != _length) - { - Reset(); - return -1; - } - _info.numNALUs++; - return SetLayerLengths(); - } - - // Next NAL unit - _ptrData += (_info.startCodeSize[_info.numNALUs] + _info.payloadSize[_info.numNALUs]); - _remLength -= (_info.startCodeSize[_info.numNALUs] + _info.payloadSize[_info.numNALUs]); - _info.numNALUs++; - - // Validate memory allocation - if (_info.numNALUs >= KMaxNumberOfNALUs) - { - Reset(); - return -1; - } - } - while(true); - - return 0; -} - -/******************************************************************************* - * int32_t FindNALUStartCodeSize(); - * - * Finds the start code length of the current NAL unit. - * - * Output: - * - _info.startCodeSize[currentNALU] : Start code length in bytes of NAL unit. - * - * Return value: - * - 0 : ok - * - (-1) : Error - */ -int32_t -H264Information::FindNALUStartCodeSize() -{ - // NAL unit start code. Ex. {0,0,1} or {0,0,0,1} - for (uint32_t i = 2; i < _remLength; i++) - { - if (_ptrData[i] == 1 && _ptrData[i - 1] == 0 && _ptrData[i - 2] == 0) - { - _info.startCodeSize[_info.numNALUs] = uint8_t(i + 1); - return 0; - } - } - return -1; -} - -/******************************************************************************* - * int32_t FindNALU(); - * - * Finds the length of the current NAL unit. - * - * Output: - * - _info.payloadSize[currentNALU] : Payload length in bytes of NAL unit - * (start code length not included). - * - _parsedLength : Current parsed length in bytes. - * - * Return value: - * - 1 : ok. Last NAL unit found. - * - 0 : ok - * - (-1) : Error - */ -int32_t -H264Information::FindNALU() -{ - for (uint32_t i = _info.startCodeSize[_info.numNALUs]; i < _remLength - 2; i += 2) - { - if (_ptrData[i] == 0) - { - int32_t size = 0; - if ((_ptrData[i + 1] == 1 && _ptrData[i - 1] == 0) || - (_ptrData[i + 2] == 1 && _ptrData[i + 1] == 0)) - { - // Found a header - // Reduce size by preceding zeroes - while (_ptrData[i - 1] == 0) - { - i--; - } - size = i; - } - if (size > 0) - { - _info.payloadSize[_info.numNALUs] = size - _info.startCodeSize[_info.numNALUs]; - _parsedLength += _info.startCodeSize[_info.numNALUs] + _info.payloadSize[_info.numNALUs]; - return 0; - } - } - } - // Last NAL unit - _info.payloadSize[_info.numNALUs] = _remLength - _info.startCodeSize[_info.numNALUs]; - if (_info.payloadSize[_info.numNALUs] > 0) - { - _parsedLength += _info.startCodeSize[_info.numNALUs] + _info.payloadSize[_info.numNALUs]; - return 1; - } - return -1; -} - -/******************************************************************************* - * void GetNRI(); - * - * Finds the NRI of the current NAL unit. - * - * Output: - * - _info.NRI[currentNALU] : NRI of NAL unit. - * - * Return value: - * - 0 : ok - * - (-1) : Error - */ -void -H264Information::GetNRI() -{ - // NAL unit header (1 byte) - // --------------------------------- - // | start code |F|NRI| Type | - // --------------------------------- - - // NRI (2 bits) - nal_ref_idc. '00' - the NAL unit is not used to reconstruct reference pictures. - // >00 - the NAL unit is required to reconstruct reference pictures - // in the same layer, or contains a parameter set. - - - const uint8_t type = _ptrData[_info.startCodeSize[_info.numNALUs]] & 0x1f; - - // NALU type of 5, 7 and 8 shoud have NRI to b011 - if( type == 5 || - type == 7 || - type == 8) - { - _info.NRI[_info.numNALUs] = 0x60; - }else - { - _info.NRI[_info.numNALUs] = _ptrData[_info.startCodeSize[_info.numNALUs]] & 0x60; - } -} - - -/******************************************************************************* - * int32_t FindNALUType(); - * - * Finds the type of the current NAL unit. - * - * Output: - * - _info.type[currentNALU] : Type of NAL unit - * - * Return value: - * - 0 : ok - * - (-1) : Error - */ -int32_t -H264Information::FindNALUType() -{ - // NAL unit header (1 byte) - // --------------------------------- - // | start code |F|NRI| Type | - // --------------------------------- - - _info.type[_info.numNALUs] = _ptrData[_info.startCodeSize[_info.numNALUs]] & 0x1f; - - if (_info.type[_info.numNALUs] == 0) - { - return -1; - } - - // SVC NAL units, extended header - if (ParseSVCNALUHeader() == -1) - { - return -1; - } - - return 0; -} - -/******************************************************************************* - * int32_t ParseSVCNALUHeader(); - * - * Finds the extended header of the current NAL unit. Included for NAL unit types 14 and 20. - * - * Output: - * - _info.SVCheader[currentNALU] : SVC header of NAL unit. - * - * Return value: - * - 0 : ok - * - (-1) : Error - */ -int32_t -H264Information::ParseSVCNALUHeader() -{ - if (_info.type[_info.numNALUs] == 5) - { - _info.SVCheader[_info.numNALUs].idr = 1; - } - if (_info.type[_info.numNALUs] == 6) - { - uint32_t seiPayloadSize; - do - { - // SEI message - seiPayloadSize = 0; - - uint32_t curByte = _info.startCodeSize[_info.numNALUs] + 1; - const uint32_t seiStartOffset = curByte; - - uint32_t seiPayloadType = 0; - while(_ptrData[curByte] == 0xff) - { - seiPayloadType += 255; - curByte++; - } - seiPayloadType += _ptrData[curByte++]; - - while(_ptrData[curByte] == 0xff) - { - seiPayloadSize += 255; - curByte++; - } - seiPayloadSize += _ptrData[curByte++]; - - if(_info.payloadSize[_info.numNALUs] < _info.startCodeSize[_info.numNALUs] + seiPayloadSize) - { - // sanity of remaining buffer - // return 0 since no one "need" SEI messages - assert(false); - return 0; - } - - if(seiPayloadType == 24) - { - // we add this to NALU 0 to be signaled in the first PACSI packet - _info.PACSI[0].numSEINALUs = 1; // we allways add this to NALU 0 to send it in the first packet - if(_info.PACSI[0].seiMessageLength[0] != seiPayloadSize) - { - _info.PACSI[0].seiMessageLength[0] = seiPayloadSize; - delete [] _info.PACSI[0].seiMessageData[0]; - _info.PACSI[0].seiMessageData[0] = new uint8_t[seiPayloadSize]; - } - memcpy(_info.PACSI[0].seiMessageData[0], _ptrData+seiStartOffset, seiPayloadSize); - - _info.PACSI[0].NALlength += seiPayloadSize + 2; // additional 2 is the length - -#ifdef DEBUG_SEI_MESSAGE - const uint8_t numberOfLayers = 10; - uint16_t avgBitrate[numberOfLayers]= {0,0,0,0,0,0,0,0,0,0}; - uint16_t maxBitrateLayer[numberOfLayers]= {0,0,0,0,0,0,0,0,0,0}; - uint16_t maxBitrateLayerRepresentation[numberOfLayers] = {0,0,0,0,0,0,0,0,0,0}; - uint16_t maxBitrareCalcWindow[numberOfLayers] = {0,0,0,0,0,0,0,0,0,0}; - - BitstreamParser parserScalabilityInfo(_ptrData+curByte, seiPayloadSize); - - parserScalabilityInfo.Get1Bit(); // not used in futher parsing - const uint8_t priority_layer_info_present = parserScalabilityInfo.Get1Bit(); - const uint8_t priority_id_setting_flag = parserScalabilityInfo.Get1Bit(); - - uint32_t numberOfLayersMinusOne = parserScalabilityInfo.GetUE(); - for(uint32_t j = 0; j<= numberOfLayersMinusOne; j++) - { - printf("\nLayer ID:%d \n",parserScalabilityInfo.GetUE()); - printf("Priority ID:%d \n", parserScalabilityInfo.Get6Bits()); - printf("Discardable:%d \n", parserScalabilityInfo.Get1Bit()); - - printf("Dependency ID:%d \n", parserScalabilityInfo.Get3Bits()); - printf("Quality ID:%d \n", parserScalabilityInfo.Get4Bits()); - printf("Temporal ID:%d \n", parserScalabilityInfo.Get3Bits()); - - const uint8_t sub_pic_layer_flag = parserScalabilityInfo.Get1Bit(); - const uint8_t sub_region_layer_flag = parserScalabilityInfo.Get1Bit(); - const uint8_t iroi_division_info_present_flag = parserScalabilityInfo.Get1Bit(); - const uint8_t profile_level_info_present_flag = parserScalabilityInfo.Get1Bit(); - const uint8_t bitrate_info_present_flag = parserScalabilityInfo.Get1Bit(); - const uint8_t frm_rate_info_present_flag = parserScalabilityInfo.Get1Bit(); - const uint8_t frm_size_info_present_flag = parserScalabilityInfo.Get1Bit(); - const uint8_t layer_dependency_info_present_flag = parserScalabilityInfo.Get1Bit(); - const uint8_t parameter_sets_info_present_flag = parserScalabilityInfo.Get1Bit(); - const uint8_t bitstream_restriction_info_present_flag = parserScalabilityInfo.Get1Bit(); - const uint8_t exact_inter_layer_pred_flag = parserScalabilityInfo.Get1Bit(); // not used in futher parsing - - if(sub_pic_layer_flag || iroi_division_info_present_flag) - { - parserScalabilityInfo.Get1Bit(); - } - const uint8_t layer_conversion_flag = parserScalabilityInfo.Get1Bit(); - const uint8_t layer_output_flag = parserScalabilityInfo.Get1Bit(); // not used in futher parsing - - if(profile_level_info_present_flag) - { - parserScalabilityInfo.Get24Bits(); - } - if(bitrate_info_present_flag) - { - // this is what we want - avgBitrate[j] = parserScalabilityInfo.Get16Bits(); - maxBitrateLayer[j] = parserScalabilityInfo.Get16Bits(); - maxBitrateLayerRepresentation[j] = parserScalabilityInfo.Get16Bits(); - maxBitrareCalcWindow[j] = parserScalabilityInfo.Get16Bits(); - - printf("\tAvg:%d\n", BitRateBPS(avgBitrate[j])); - printf("\tmaxBitrate:%d\n", BitRateBPS(maxBitrateLayer[j])); - printf("\tmaxBitrate rep:%d\n", BitRateBPS(maxBitrateLayerRepresentation[j])); - printf("\tCalcWindow:%d\n", maxBitrareCalcWindow[j]); - } - if(frm_rate_info_present_flag) - { - printf("\tFrame rate constant:%d\n", parserScalabilityInfo.Get2Bits()); // 0 = not constant, 1 = constant, 2 = maybe... - printf("\tFrame rate avg:%d\n", parserScalabilityInfo.Get16Bits()/256); - } - if(frm_size_info_present_flag || iroi_division_info_present_flag) - { - printf("\tFrame Width:%d\n",(parserScalabilityInfo.GetUE()+1)*16); - printf("\tFrame Height:%d\n",(parserScalabilityInfo.GetUE()+1)*16); - } - if(sub_region_layer_flag) - { - parserScalabilityInfo.GetUE(); - if(parserScalabilityInfo.Get1Bit()) - { - parserScalabilityInfo.Get16Bits(); - parserScalabilityInfo.Get16Bits(); - parserScalabilityInfo.Get16Bits(); - parserScalabilityInfo.Get16Bits(); - } - } - if(sub_pic_layer_flag) - { - parserScalabilityInfo.GetUE(); - } - if(iroi_division_info_present_flag) - { - if(parserScalabilityInfo.Get1Bit()) - { - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - }else - { - const uint32_t numRoisMinusOne = parserScalabilityInfo.GetUE(); - for(uint32_t k = 0; k <= numRoisMinusOne; k++) - { - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - } - } - } - if(layer_dependency_info_present_flag) - { - const uint32_t numDirectlyDependentLayers = parserScalabilityInfo.GetUE(); - for(uint32_t k = 0; k < numDirectlyDependentLayers; k++) - { - parserScalabilityInfo.GetUE(); - } - } else - { - parserScalabilityInfo.GetUE(); - } - if(parameter_sets_info_present_flag) - { - const uint32_t numSeqParameterSetMinusOne = parserScalabilityInfo.GetUE(); - for(uint32_t k = 0; k <= numSeqParameterSetMinusOne; k++) - { - parserScalabilityInfo.GetUE(); - } - const uint32_t numSubsetSeqParameterSetMinusOne = parserScalabilityInfo.GetUE(); - for(uint32_t l = 0; l <= numSubsetSeqParameterSetMinusOne; l++) - { - parserScalabilityInfo.GetUE(); - } - const uint32_t numPicParameterSetMinusOne = parserScalabilityInfo.GetUE(); - for(uint32_t m = 0; m <= numPicParameterSetMinusOne; m++) - { - parserScalabilityInfo.GetUE(); - } - }else - { - parserScalabilityInfo.GetUE(); - } - if(bitstream_restriction_info_present_flag) - { - parserScalabilityInfo.Get1Bit(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - } - if(layer_conversion_flag) - { - parserScalabilityInfo.GetUE(); - for(uint32_t k = 0; k <2;k++) - { - if(parserScalabilityInfo.Get1Bit()) - { - parserScalabilityInfo.Get24Bits(); - parserScalabilityInfo.Get16Bits(); - parserScalabilityInfo.Get16Bits(); - } - } - } - } - if(priority_layer_info_present) - { - const uint32_t prNumDidMinusOne = parserScalabilityInfo.GetUE(); - for(uint32_t k = 0; k <= prNumDidMinusOne;k++) - { - parserScalabilityInfo.Get3Bits(); - const uint32_t prNumMinusOne = parserScalabilityInfo.GetUE(); - for(uint32_t l = 0; l <= prNumMinusOne; l++) - { - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.Get24Bits(); - parserScalabilityInfo.Get16Bits(); - parserScalabilityInfo.Get16Bits(); - } - } - } - if(priority_id_setting_flag) - { - uint8_t priorityIdSettingUri; - uint32_t priorityIdSettingUriIdx = 0; - do - { - priorityIdSettingUri = parserScalabilityInfo.Get8Bits(); - } while (priorityIdSettingUri != 0); - } -#endif - } else - { - // not seiPayloadType 24 ignore - } - //check if we have more SEI in NALU - } while (_info.payloadSize[_info.numNALUs] > _info.startCodeSize[_info.numNALUs] + seiPayloadSize); - } - - // Extended NAL unit header (3 bytes). - // +---------------+---------------+---------------+ - // |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7| - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // |R|I| PRID |N| DID | QID | TID |U|D|O| RR| - // +---------------+---------------+---------------+ - - // R - Reserved for future extensions (MUST be 1). Receivers SHOULD ignore the value of R. - // I - Is layer representation an IDR layer (1) or not (0). - // PRID - Priority identifier for the NAL unit. - // N - Specifies whether inter-layer prediction may be used for decoding the coded slice (1) or not (0). - // DID - Indicates the inter-layer coding dependency level of a layer representation. - // QID - Indicates the quality level of an MGS layer representation. - // TID - Indicates the temporal level of a layer representation. - // U - Use only reference base pictures during the inter prediction process (1) or not (0). - // D - Discardable flag. - // O - Output_flag. Affects the decoded picture output process as defined in Annex C of [H.264]. - // RR - Reserved_three_2bits (MUST be '11'). Receivers SHOULD ignore the value of RR. - - if (_info.type[_info.numNALUs] == 14 || - _info.type[_info.numNALUs] == 20) - { - uint32_t curByte = _info.startCodeSize[_info.numNALUs] + 1; - - if (_remLength < curByte + 3) - { - return -1; - } - - _info.SVCheader[_info.numNALUs].idr = (_ptrData[curByte] >> 6) & 0x01; - _info.SVCheader[_info.numNALUs].priorityID = (_ptrData[curByte++] & 0x3F); - - _info.SVCheader[_info.numNALUs].interLayerPred = (_ptrData[curByte] >> 7) & 0x01; - _info.SVCheader[_info.numNALUs].dependencyID = (_ptrData[curByte] >> 4) & 0x07; - _info.SVCheader[_info.numNALUs].qualityID = (_ptrData[curByte++] & 0x0F); - - _info.SVCheader[_info.numNALUs].temporalID = (_ptrData[curByte] >> 5) & 0x07; - _info.SVCheader[_info.numNALUs].useRefBasePic = (_ptrData[curByte] >> 4) & 0x01; - _info.SVCheader[_info.numNALUs].discardable = (_ptrData[curByte] >> 3) & 0x01; - _info.SVCheader[_info.numNALUs].output = (_ptrData[curByte] >> 2) & 0x01; - - if (_info.type[_info.numNALUs] == 14) - { - // inform the next NALU - memcpy(&(_info.SVCheader[_info.numNALUs+1]), &(_info.SVCheader[_info.numNALUs]), sizeof(_H264_SVC_NALUHeader)); - } - } - return 0; -} - - -/******************************************************************************* - * void SetLayerSEBit(); - * - * Sets start and end bits for the current NAL unit. - * - * Output: - * - _info.PACSI[currentNALU].S : First NAL unit in a layer (S = 1). - * - _info.PACSI[currentNALU].E : Last NAL unit in a layer (E = 1). - * - */ -void -H264Information::SetLayerSEBit(int32_t foundLast) -{ - if (_info.numNALUs == 0) - { - // First NAL unit - _info.PACSI[_info.numNALUs].S = 1; - } - - if (_info.numNALUs > 0) - { - if (_info.type[_info.numNALUs] != _info.type[_info.numNALUs-1] && - (_info.type[_info.numNALUs] == 20)) - { - // First layer in scalable extension - _info.PACSI[_info.numNALUs].S = 1; - _info.PACSI[_info.numNALUs-1].E = 1; - } - - if (_info.type[_info.numNALUs] == 20 && _info.type[_info.numNALUs-1] == 20) - { - if (_info.SVCheader[_info.numNALUs].temporalID != _info.SVCheader[_info.numNALUs-1].temporalID || - _info.SVCheader[_info.numNALUs].dependencyID != _info.SVCheader[_info.numNALUs-1].dependencyID || - _info.SVCheader[_info.numNALUs].qualityID != _info.SVCheader[_info.numNALUs-1].qualityID) - { - // New layer in scalable extension - _info.PACSI[_info.numNALUs].S = 1; - _info.PACSI[_info.numNALUs-1].E = 1; - } - } - } - - if (foundLast) - { - // Last NAL unit - _info.PACSI[_info.numNALUs].E = 1; - } - -} - -/******************************************************************************* - * int32_t SetLayerLengths(); - * - * Sets the accumulated layer length. - * - * Output: - * - _info.accLayerSize[currentLayer] : Size in bytes of layer: 0 - currentLayer. - * - * Return value: - * - 0 : ok - * - (-1) : Error - * - */ -int32_t -H264Information::SetLayerLengths() -{ - for (uint32_t curNALU = 0; curNALU < _info.numNALUs; curNALU++) - { - _info.accLayerSize[_info.numLayers] += _info.startCodeSize[curNALU] + _info.payloadSize[curNALU]; - - if (_info.PACSI[curNALU].E == 1) - { - _info.numLayers++; - if (curNALU == uint32_t(_info.numNALUs - 1)) - { - break; - } - if (_info.numLayers >= KMaxNumberOfLayers) - { - Reset(); - return -1; - } - _info.accLayerSize[_info.numLayers] += _info.accLayerSize[_info.numLayers - 1]; - } - } - - if (_info.numLayers < 1 && _info.numLayers > KMaxNumberOfLayers) - { - Reset(); - return -1; - } - - if (_info.accLayerSize[_info.numLayers - 1] != int32_t(_length)) - { - Reset(); - return -1; - } - - return 0; -} -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/h264_information.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/h264_information.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/h264_information.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/h264_information.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_H264_INFORMATION_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_H264_INFORMATION_H_ - -#include "VideoCodecInformation.h" -#include "webrtc/typedefs.h" - -namespace webrtc { -enum -{ - KMaxNumberOfNALUs = 128, - KMaxNumberOfSEINALUs = 2, - KMaxNumberOfLayers = 16 -}; - -struct H264_SVC_NALUHeader -{ - H264_SVC_NALUHeader() - : - r(1), - idr(0), - priorityID(0), - interLayerPred(0), - dependencyID(0), - qualityID(0), - temporalID(0), - useRefBasePic(0), - discardable(0), - output(0), - rr(3), - length(3) - { - } - const uint8_t r; - uint8_t idr; - uint8_t priorityID; - uint8_t interLayerPred; - uint8_t dependencyID; - uint8_t qualityID; - uint8_t temporalID; - uint8_t useRefBasePic; - uint8_t discardable; - uint8_t output; - const uint8_t rr; - const uint8_t length; -}; - -class H264_PACSI_NALU -{ -public: - H264_PACSI_NALU() : - NALlength(5), - type(30), - X(0), - Y(0), -// T(0), - A(0), - P(0), - C(0), - S(0), - E(0), - TL0picIDx(0), - IDRpicID(0), - DONC(0), - numSEINALUs(0) - { - memset(seiMessageLength, 0, sizeof(seiMessageLength)); - memset(seiMessageData, 0, sizeof(seiMessageData)); - } - ~H264_PACSI_NALU() - { - for(int i = 0; i> 8); - databuffer[curByte++] = (uint8_t)(pacsi.IDRpicID); - } - // Decoding order number - if (addDONC) // pacsi.T - { - databuffer[curByte++] = (uint8_t)(DONC >> 8); - databuffer[curByte++] = (uint8_t)(DONC); - } - - // SEI NALU - if(firstPacketInNALU) // IMPROVEMENT duplicate it to make sure it arrives... - { - // we only set this for NALU 0 to make sure we send it only once per frame - for (uint32_t i = 0; i < pacsi.numSEINALUs; i++) - { - // NALU size - databuffer[curByte++] = (uint8_t)(pacsi.seiMessageLength[i] >> 8); - databuffer[curByte++] = (uint8_t)(pacsi.seiMessageLength[i]); - - // NALU data - memcpy(databuffer + curByte, pacsi.seiMessageData[i], pacsi.seiMessageLength[i]); - curByte += pacsi.seiMessageLength[i]; - } - } - return curByte - startByte; -} - -int32_t -RTPSenderH264::SetH264RelaySequenceNumber(const uint16_t seqNum) -{ - _h264SVCRelaySequenceNumber = seqNum; - return 0; -} - -int32_t -RTPSenderH264::SetH264RelayCompleteLayer(const bool complete) -{ - _h264SVCRelayLayerComplete = complete; - return 0; -} - -/* - 12 Filler data - - The only restriction of filler data NAL units within an - access unit is that they shall not precede the first VCL - NAL unit with the same access unit. -*/ -int32_t -RTPSenderH264::SendH264FillerData(const WebRtcRTPHeader* rtpHeader, - const uint16_t bytesToSend, - const uint32_t ssrc) -{ - uint16_t fillerLength = bytesToSend - 12 - 1; - - if (fillerLength > WEBRTC_IP_PACKET_SIZE - 12 - 1) - { - return 0; - } - - if (fillerLength == 0) - { - // do not send an empty packet, will not reach JB - fillerLength = 1; - } - - // send codec valid data, H.264 has defined data which is binary 1111111 - uint8_t dataBuffer[WEBRTC_IP_PACKET_SIZE]; - - dataBuffer[0] = static_cast(0x80); // version 2 - dataBuffer[1] = rtpHeader->header.payloadType; - ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+2, _rtpSender.IncrementSequenceNumber()); // get the current SequenceNumber and add by 1 after returning - ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+4, rtpHeader->header.timestamp); - ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+8, rtpHeader->header.ssrc); - - // set filler NALU type - dataBuffer[12] = 12; // NRI field = 0, type 12 - - // fill with 0xff - memset(dataBuffer + 12 + 1, 0xff, fillerLength); - - return _rtpSender.SendToNetwork(dataBuffer, - fillerLength, - 12 + 1); -} - -int32_t -RTPSenderH264::SendH264FillerData(const uint32_t captureTimestamp, - const uint8_t payloadType, - const uint32_t bytes - ) -{ - - const uint16_t rtpHeaderLength = _rtpSender.RTPHeaderLength(); - uint16_t maxLength = _rtpSender.MaxPayloadLength() - FECPacketOverhead() - _rtpSender.RTPHeaderLength(); - - int32_t bytesToSend=bytes; - uint16_t fillerLength=0; - - uint8_t dataBuffer[WEBRTC_IP_PACKET_SIZE]; - - while(bytesToSend>0) - { - fillerLength=maxLength; - if(fillerLength WEBRTC_IP_PACKET_SIZE - 12 - 1) - { - return 0; - } - - if (fillerLength == 0) - { - // do not send an empty packet, will not reach JB - fillerLength = 1; - } - - // send paded data - // correct seq num, time stamp and payloadtype - _rtpSender.BuildRTPheader(dataBuffer, payloadType, false,captureTimestamp, true, true); - - // set filler NALU type - dataBuffer[12] = 12; // NRI field = 0, type 12 - - // send codec valid data, H.264 has defined data which is binary 1111111 - // fill with 0xff - memset(dataBuffer + 12 + 1, 0xff, fillerLength-1); - - if( _rtpSender.SendToNetwork(dataBuffer, - fillerLength, - 12)<0) - { - - return -1;; - } - } - return 0; -} - -int32_t -RTPSenderH264::SendH264SVCRelayPacket(const WebRtcRTPHeader* rtpHeader, - const uint8_t* incomingRTPPacket, - const uint16_t incomingRTPPacketSize, - const uint32_t ssrc, - const bool higestLayer) -{ - if (rtpHeader->header.sequenceNumber != (uint16_t)(_h264SVCRelaySequenceNumber + 1)) - { - // not continous, signal loss - _rtpSender.IncrementSequenceNumber(); - } - _h264SVCRelaySequenceNumber = rtpHeader->header.sequenceNumber; - - - if (rtpHeader->header.timestamp != _h264SVCRelayTimeStamp) - { - // new frame - _h264SVCRelayLayerComplete = false; - } - - if (rtpHeader->header.timestamp == _h264SVCRelayTimeStamp && - _h264SVCRelayLayerComplete) - { - // sanity, end of layer already sent - // Could happened for fragmented packet with missing PACSI info (PACSI packet reorded and received after packet it belongs to) - // fragmented packet has no layer info set (default info 0) - return 0; - } - _h264SVCRelayTimeStamp = rtpHeader->header.timestamp; - - // re-packetize H.264-SVC packets - // we keep the timestap unchanged - // make a copy and only change the SSRC and seqNum - - uint8_t dataBuffer[WEBRTC_IP_PACKET_SIZE]; - memcpy(dataBuffer, incomingRTPPacket, incomingRTPPacketSize); - - // _sequenceNumber initiated in Init() - // _ssrc initiated in constructor - - // re-write payload type - if(_h264SVCPayloadType != -1) - { - dataBuffer[1] &= kRtpMarkerBitMask; - dataBuffer[1] += _h264SVCPayloadType; - } - - // _sequenceNumber will not work for re-ordering by NACK from original sender - // engine responsible for this - ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+2, _rtpSender.IncrementSequenceNumber()); // get the current SequenceNumber and add by 1 after returning - //ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+8, ssrc); - - // how do we know it's the last relayed packet in a frame? - // 1) packets arrive in order, the engine manages that - // 2) highest layer that we relay - // 3) the end bit is set for the highest layer - - if(higestLayer && rtpHeader->type.Video.codecHeader.H264.relayE) - { - // set marker bit - dataBuffer[1] |= kRtpMarkerBitMask; - - // set relayed layer as complete - _h264SVCRelayLayerComplete = true; - } - return _rtpSender.SendToNetwork(dataBuffer, - incomingRTPPacketSize - rtpHeader->header.headerLength, - rtpHeader->header.headerLength); -} - -int32_t -RTPSenderH264::SendH264_STAP_A(const FrameType frameType, - const H264Info* ptrH264Info, - uint16_t &idxNALU, - const int8_t payloadType, - const uint32_t captureTimeStamp, - bool& switchToFUA, - int32_t &payloadBytesToSend, - const uint8_t*& data, - const uint16_t rtpHeaderLength) -{ - const int32_t H264_NALU_LENGTH = 2; - - uint16_t h264HeaderLength = 1; // normal header length - uint16_t maxPayloadLengthSTAP_A = _rtpSender.MaxPayloadLength() - - FECPacketOverhead() - rtpHeaderLength - - h264HeaderLength - H264_NALU_LENGTH; - - int32_t dataOffset = rtpHeaderLength + h264HeaderLength; - uint8_t NRI = 0; - uint16_t payloadBytesInPacket = 0; - uint8_t dataBuffer[WEBRTC_IP_PACKET_SIZE]; - - if (ptrH264Info->payloadSize[idxNALU] > maxPayloadLengthSTAP_A) - { - // we need to fragment NAL switch to mode FU-A - switchToFUA = true; - } else - { - // combine as many NAL units in every IP packet - do - { - if(!_h264SendPPS_SPS) - { - // don't send NALU of type 7 and 8 SPS and PPS - if(ptrH264Info->type[idxNALU] == 7 || ptrH264Info->type[idxNALU] == 8) - { - payloadBytesToSend -= ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - data += ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - idxNALU++; - continue; - } - } - if(ptrH264Info->payloadSize[idxNALU] + payloadBytesInPacket <= maxPayloadLengthSTAP_A) - { - if(ptrH264Info->NRI[idxNALU] > NRI) - { - NRI = ptrH264Info->NRI[idxNALU]; - } - // put NAL size into packet - dataBuffer[dataOffset] = (uint8_t)(ptrH264Info->payloadSize[idxNALU] >> 8); - dataOffset++; - dataBuffer[dataOffset] = (uint8_t)(ptrH264Info->payloadSize[idxNALU] & 0xff); - dataOffset++; - // Put payload in packet - memcpy(&dataBuffer[dataOffset], &data[ptrH264Info->startCodeSize[idxNALU]], ptrH264Info->payloadSize[idxNALU]); - dataOffset += ptrH264Info->payloadSize[idxNALU]; - data += ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - payloadBytesInPacket += (uint16_t)(ptrH264Info->payloadSize[idxNALU] + H264_NALU_LENGTH); - payloadBytesToSend -= ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - } else - { - // we don't fitt the next NALU in this packet - break; - } - idxNALU++; - }while(payloadBytesToSend); - } - - // sanity - // don't send empty packets - if (payloadBytesInPacket) - { - // add RTP header - _rtpSender.BuildRTPheader(dataBuffer, payloadType, (payloadBytesToSend==0)?true:false, captureTimeStamp); - dataBuffer[rtpHeaderLength] = 24 + NRI; // STAP-A == 24 - uint16_t payloadLength = payloadBytesInPacket + h264HeaderLength; - - if(-1 == SendVideoPacket(frameType, dataBuffer, payloadLength, rtpHeaderLength)) - { - return -1; - } - } - return 0; -} // end STAP-A - -// STAP-A for H.264 SVC -int32_t -RTPSenderH264::SendH264_STAP_A_PACSI(const FrameType frameType, - const H264Info* ptrH264Info, - uint16_t &idxNALU, - const int8_t payloadType, - const uint32_t captureTimeStamp, - bool& switchToFUA, - int32_t &payloadBytesToSend, - const uint8_t*& data, - const uint16_t rtpHeaderLength, - uint16_t& decodingOrderNumber) -{ - const int32_t H264_NALU_LENGTH = 2; - - uint16_t h264HeaderLength = 1; // normal header length - uint16_t maxPayloadLengthSTAP_A = _rtpSender.MaxPayloadLength() - FECPacketOverhead() - rtpHeaderLength - h264HeaderLength - H264_NALU_LENGTH; - int32_t dataOffset = rtpHeaderLength + h264HeaderLength; - uint8_t NRI = 0; - uint16_t payloadBytesInPacket = 0; - uint8_t dataBuffer[WEBRTC_IP_PACKET_SIZE]; - bool firstNALUNotIDR = true; //delta - - // Put PACSI NAL unit into packet - int32_t lengthPACSI = 0; - uint32_t PACSI_NALlength = ptrH264Info->PACSI[idxNALU].NALlength; - if (PACSI_NALlength > maxPayloadLengthSTAP_A) - { - return -1; - } - dataBuffer[dataOffset++] = (uint8_t)(PACSI_NALlength >> 8); - dataBuffer[dataOffset++] = (uint8_t)(PACSI_NALlength & 0xff); - - // end bit will be updated later, since another NALU in this packet might be the last - int32_t lengthPASCINALU = AddH264PACSINALU(true, - false, - ptrH264Info->PACSI[idxNALU], - ptrH264Info->SVCheader[idxNALU], - decodingOrderNumber, - dataBuffer, - dataOffset); - if (lengthPASCINALU <= 0) - { - return -1; - } - decodingOrderNumber++; - - lengthPACSI = H264_NALU_LENGTH + lengthPASCINALU; - maxPayloadLengthSTAP_A -= (uint16_t)lengthPACSI; - if (ptrH264Info->payloadSize[idxNALU] > maxPayloadLengthSTAP_A) - { - // we need to fragment NAL switch to mode FU-A - switchToFUA = true; - return 0; - } - if(!ptrH264Info->SVCheader[idxNALU].idr) - { - firstNALUNotIDR = true; - } - - uint32_t layer = (ptrH264Info->SVCheader[idxNALU].dependencyID << 16)+ - (ptrH264Info->SVCheader[idxNALU].qualityID << 8) + - ptrH264Info->SVCheader[idxNALU].temporalID; - - { - // combine as many NAL units in every IP packet, with the same priorityID - // Improvement we could allow several very small MGS NALU from different layers to be sent in one packet - - do - { - if(!_h264SendPPS_SPS) - { - // Don't send NALU of type 7 and 8 SPS and PPS, - // they could be signaled outofband - if(ptrH264Info->type[idxNALU] == 7 || ptrH264Info->type[idxNALU] == 8) - { - payloadBytesToSend -= ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - data += ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - idxNALU++; - continue; - } - } - // don't send NALU type 6 (SEI message) not allowed when we send it in PACSI - if(ptrH264Info->type[idxNALU] == 6) - { - // SEI NALU Don't send, not allowed when we send it in PACSI - payloadBytesToSend -= ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - data += ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - idxNALU++; - continue; - } - - const uint32_t layerNALU = (ptrH264Info->SVCheader[idxNALU].dependencyID << 16)+ - (ptrH264Info->SVCheader[idxNALU].qualityID << 8) + - ptrH264Info->SVCheader[idxNALU].temporalID; - - // we need to break on a new layer - if( ptrH264Info->payloadSize[idxNALU] + payloadBytesInPacket <= maxPayloadLengthSTAP_A && - layerNALU == layer) - { - if(ptrH264Info->NRI[idxNALU] > NRI) - { - NRI = ptrH264Info->NRI[idxNALU]; - } - // put NAL size into packet - dataBuffer[dataOffset] = (uint8_t)(ptrH264Info->payloadSize[idxNALU] >> 8); - dataOffset++; - dataBuffer[dataOffset] = (uint8_t)(ptrH264Info->payloadSize[idxNALU] & 0xff); - dataOffset++; - // Put payload in packet - memcpy(&dataBuffer[dataOffset], &data[ptrH264Info->startCodeSize[idxNALU]], ptrH264Info->payloadSize[idxNALU]); - dataOffset += ptrH264Info->payloadSize[idxNALU]; - data += ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - payloadBytesInPacket += (uint16_t)(ptrH264Info->payloadSize[idxNALU] + H264_NALU_LENGTH); - payloadBytesToSend -= ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - } else - { - // we don't fitt the next NALU in this packet or, - // it's the next layer - - // check if we should send this NALU - // based on the layer - - if(_useHighestSendLayer && layerNALU != layer) - { - // we don't send this NALU due to it's a new layer - // check if we should send the next or if this is the last - const uint8_t dependencyQualityID = (ptrH264Info->SVCheader[idxNALU].dependencyID << 4) + ptrH264Info->SVCheader[idxNALU].qualityID; - - bool highestLayer; - if(SendH264SVCLayer(frameType, - ptrH264Info->SVCheader[idxNALU].temporalID, - dependencyQualityID, - highestLayer) == false) - { - // will trigger markerbit and stop sending this frame - payloadBytesToSend = 0; - } - } - break; - } - idxNALU++; - - }while(payloadBytesToSend); - } - - // sanity, don't send empty packets - if (payloadBytesInPacket) - { - // add RTP header - _rtpSender.BuildRTPheader(dataBuffer, payloadType, (payloadBytesToSend==0)?true:false, captureTimeStamp); - - dataBuffer[rtpHeaderLength] = 24 + NRI; // STAP-A == 24 - - // NRI for PACSI - dataBuffer[rtpHeaderLength + H264_NALU_LENGTH + 1] &= 0x1f; // zero out NRI field - dataBuffer[rtpHeaderLength + H264_NALU_LENGTH + 1] |= NRI; - - if(ptrH264Info->PACSI[idxNALU-1].E) - { - // update end bit - dataBuffer[rtpHeaderLength + H264_NALU_LENGTH + 5] |= 0x01; - } - if(firstNALUNotIDR) - { - // we have to check if any of the NALU in this packet is an IDR NALU - bool setIBit = false; - for(int i = 0; i < idxNALU; i++) - { - if(ptrH264Info->SVCheader[i].idr) - { - setIBit = true; - break; - } - } - if(setIBit) - { - // update I bit - dataBuffer[rtpHeaderLength + H264_NALU_LENGTH + 2] |= 0x40; - } - } - const uint16_t payloadLength = payloadBytesInPacket + h264HeaderLength + (uint16_t)lengthPACSI; - if(-1 == SendVideoPacket(frameType, - dataBuffer, - payloadLength, - rtpHeaderLength, - layer==0)) - { - return -1; - } - } - return 0; -} // end STAP-A - -int32_t -RTPSenderH264::SendH264_FU_A(const FrameType frameType, - const H264Info* ptrH264Info, - uint16_t &idxNALU, - const int8_t payloadType, - const uint32_t captureTimeStamp, - int32_t &payloadBytesToSend, - const uint8_t*& data, - const uint16_t rtpHeaderLength, - uint16_t& decodingOrderNumber, - const bool sendSVCPACSI) -{ - - // FUA for the rest of the frame - uint16_t maxPayloadLength = _rtpSender.MaxPayloadLength() - FECPacketOverhead() - rtpHeaderLength; - uint8_t dataBuffer[WEBRTC_IP_PACKET_SIZE]; - uint32_t payloadBytesRemainingInNALU = ptrH264Info->payloadSize[idxNALU]; - - bool isBaseLayer=false; - - if(payloadBytesRemainingInNALU > maxPayloadLength) - { - // we need to fragment NALU - const uint16_t H264_FUA_LENGTH = 2; // FU-a H.264 header is 2 bytes - - if(sendSVCPACSI) - { - SendH264_SinglePACSI(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - true, - false); - - uint32_t layer = (ptrH264Info->SVCheader[idxNALU].dependencyID << 16)+ - (ptrH264Info->SVCheader[idxNALU].qualityID << 8) + - ptrH264Info->SVCheader[idxNALU].temporalID; - isBaseLayer=(layer==0); - } - - // First packet - _rtpSender.BuildRTPheader(dataBuffer,payloadType, false, captureTimeStamp); - - uint16_t maxPayloadLengthFU_A = maxPayloadLength - H264_FUA_LENGTH ; - uint8_t fuaIndc = 28 + ptrH264Info->NRI[idxNALU]; - dataBuffer[rtpHeaderLength] = fuaIndc; // FU-A indicator - dataBuffer[rtpHeaderLength+1] = (uint8_t)(ptrH264Info->type[idxNALU] + 0x80)/*start*/; // FU-A header - - memcpy(&dataBuffer[rtpHeaderLength + H264_FUA_LENGTH], &data[ptrH264Info->startCodeSize[idxNALU]+1], maxPayloadLengthFU_A); - uint16_t payloadLength = maxPayloadLengthFU_A + H264_FUA_LENGTH; - if(-1 == SendVideoPacket(frameType, dataBuffer, payloadLength, rtpHeaderLength, isBaseLayer)) - { - return -1; - } - - //+1 is from the type that is coded into the FU-a header - data += maxPayloadLengthFU_A + 1 + ptrH264Info->startCodeSize[idxNALU]; // inc data ptr - payloadBytesToSend -= maxPayloadLengthFU_A+1+ptrH264Info->startCodeSize[idxNALU]; - payloadBytesRemainingInNALU -= maxPayloadLengthFU_A+1; - - // all non first/last packets - while(payloadBytesRemainingInNALU > maxPayloadLengthFU_A) - { - if(sendSVCPACSI) - { - SendH264_SinglePACSI(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - false, - false); - } - - // prepare next header - _rtpSender.BuildRTPheader(dataBuffer, payloadType, false, captureTimeStamp); - - dataBuffer[rtpHeaderLength] = (uint8_t)fuaIndc; // FU-A indicator - dataBuffer[rtpHeaderLength+1] = ptrH264Info->type[idxNALU]; // FU-A header - - memcpy(&dataBuffer[rtpHeaderLength+H264_FUA_LENGTH], data, maxPayloadLengthFU_A); - payloadLength = maxPayloadLengthFU_A + H264_FUA_LENGTH; - - if(-1 == SendVideoPacket(frameType, dataBuffer, payloadLength, rtpHeaderLength,isBaseLayer)) - { - return -1; - } - data += maxPayloadLengthFU_A; // inc data ptr - payloadBytesToSend -= maxPayloadLengthFU_A; - payloadBytesRemainingInNALU -= maxPayloadLengthFU_A; - dataBuffer[rtpHeaderLength] = fuaIndc; // FU-A indicator - dataBuffer[rtpHeaderLength+1] = ptrH264Info->type[idxNALU]; // FU-A header - } - if(sendSVCPACSI) - { - SendH264_SinglePACSI(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - false, - true); // last packet in NALU - - if(_useHighestSendLayer && idxNALU+1 < ptrH264Info->numNALUs) - { - // not last NALU in frame - // check if it's the the next layer should not be sent - - // check if we should send the next or if this is the last - const uint8_t dependencyQualityID = (ptrH264Info->SVCheader[idxNALU+1].dependencyID << 4) + - ptrH264Info->SVCheader[idxNALU+1].qualityID; - - bool highestLayer; - if(SendH264SVCLayer(frameType, - ptrH264Info->SVCheader[idxNALU+1].temporalID, - dependencyQualityID, - highestLayer) == false) - { - // will trigger markerbit and stop sending this frame - payloadBytesToSend = payloadBytesRemainingInNALU; - } - } - } - // last packet in NALU - _rtpSender.BuildRTPheader(dataBuffer, payloadType,(payloadBytesToSend == (int32_t)payloadBytesRemainingInNALU)?true:false, captureTimeStamp); - dataBuffer[rtpHeaderLength+1] = ptrH264Info->type[idxNALU] + 0x40/*stop*/; // FU-A header - - memcpy(&dataBuffer[rtpHeaderLength+H264_FUA_LENGTH], data, payloadBytesRemainingInNALU); - payloadLength = (uint16_t)payloadBytesRemainingInNALU + H264_FUA_LENGTH; - payloadBytesToSend -= payloadBytesRemainingInNALU; - if(payloadBytesToSend != 0) - { - data += payloadBytesRemainingInNALU; // inc data ptr - } - idxNALU++; - if(-1 == SendVideoPacket(frameType, dataBuffer, payloadLength, rtpHeaderLength,isBaseLayer)) - { - return -1; - } - } else - { - // send NAL unit in singel mode - return SendH264_SingleMode(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - payloadBytesToSend, - data, - rtpHeaderLength, - sendSVCPACSI); - } - // end FU-a - return 0; -} - -int32_t -RTPSenderH264::SendH264_SingleMode(const FrameType frameType, - const H264Info* ptrH264Info, - uint16_t &idxNALU, - const int8_t payloadType, - const uint32_t captureTimeStamp, - int32_t &payloadBytesToSend, - const uint8_t*& data, - const uint16_t rtpHeaderLength, - uint16_t& decodingOrderNumber, - const bool sendSVCPACSI) -{ - // no H.264 header lenght in single mode - // we use WEBRTC_IP_PACKET_SIZE instead of the configured MTU since it's better to send fragmented UDP than not to send - const uint16_t maxPayloadLength = WEBRTC_IP_PACKET_SIZE - _rtpSender.PacketOverHead() - FECPacketOverhead() - rtpHeaderLength; - uint8_t dataBuffer[WEBRTC_IP_PACKET_SIZE]; - bool isBaseLayer=false; - - if(ptrH264Info->payloadSize[idxNALU] > maxPayloadLength) - { - return -3; - } - if(!_h264SendPPS_SPS) - { - // don't send NALU of type 7 and 8 SPS and PPS - if(ptrH264Info->type[idxNALU] == 7 || ptrH264Info->type[idxNALU] == 8) - { - payloadBytesToSend -= ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - data += ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - idxNALU++; - return 0; - } - } - if(sendSVCPACSI) - { - SendH264_SinglePACSI(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - true, - true); - - uint32_t layer = (ptrH264Info->SVCheader[idxNALU].dependencyID << 16)+ - (ptrH264Info->SVCheader[idxNALU].qualityID << 8) + - ptrH264Info->SVCheader[idxNALU].temporalID; - isBaseLayer=(layer==0); - } - - // Put payload in packet - memcpy(&dataBuffer[rtpHeaderLength], &data[ptrH264Info->startCodeSize[idxNALU]], ptrH264Info->payloadSize[idxNALU]); - - uint16_t payloadBytesInPacket = (uint16_t)ptrH264Info->payloadSize[idxNALU]; - payloadBytesToSend -= ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; // left to send - - // - _rtpSender.BuildRTPheader(dataBuffer,payloadType,(payloadBytesToSend ==0)?true:false, captureTimeStamp); - - dataBuffer[rtpHeaderLength] &= 0x1f; // zero out NRI field - dataBuffer[rtpHeaderLength] |= ptrH264Info->NRI[idxNALU]; // nri - if(payloadBytesToSend > 0) - { - data += ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - } - idxNALU++; - if(-1 == SendVideoPacket(frameType, dataBuffer, payloadBytesInPacket, rtpHeaderLength,isBaseLayer)) - { - return -1; - } - return 0; -} - -int32_t -RTPSenderH264::SendH264_SinglePACSI(const FrameType frameType, - const H264Info* ptrH264Info, - const uint16_t idxNALU, - const int8_t payloadType, - const uint32_t captureTimeStamp, - const bool firstPacketInNALU, - const bool lastPacketInNALU); -{ - // Send PACSI in single mode - uint8_t dataBuffer[WEBRTC_IP_PACKET_SIZE]; - uint16_t rtpHeaderLength = (uint16_t)_rtpSender.BuildRTPheader(dataBuffer, payloadType,false, captureTimeStamp); - int32_t dataOffset = rtpHeaderLength; - - int32_t lengthPASCINALU = AddH264PACSINALU(firstPacketInNALU, - lastPacketInNALU, - ptrH264Info->PACSI[idxNALU], - ptrH264Info->SVCheader[idxNALU], - decodingOrderNumber, - dataBuffer, - dataOffset); - - if (lengthPASCINALU <= 0) - { - return -1; - } - decodingOrderNumber++; - - uint16_t payloadBytesInPacket = (uint16_t)lengthPASCINALU; - - // Set payload header (first payload byte co-serves as the payload header) - dataBuffer[rtpHeaderLength] &= 0x1f; // zero out NRI field - dataBuffer[rtpHeaderLength] |= ptrH264Info->NRI[idxNALU]; // nri - - const uint32_t layer = (ptrH264Info->SVCheader[idxNALU].dependencyID << 16)+ - (ptrH264Info->SVCheader[idxNALU].qualityID << 8) + - ptrH264Info->SVCheader[idxNALU].temporalID; - - if (-1 == SendVideoPacket(frameType, dataBuffer, payloadBytesInPacket, rtpHeaderLength,layer==0)) - { - return -1; - } - return 0; -} - - - - -int32_t -RTPSenderH264::SendH264SVC(const FrameType frameType, - const int8_t payloadType, - const uint32_t captureTimeStamp, - const uint8_t* payloadData, - const uint32_t payloadSize, - H264Information& h264Information, - uint16_t& decodingOrderNumber) -{ - int32_t payloadBytesToSend = payloadSize; - const uint16_t rtpHeaderLength = _rtpSender.RTPHeaderLength(); - - const H264Info* ptrH264Info = NULL; - if (h264Information.GetInfo(payloadData,payloadSize, ptrH264Info) == -1) - { - return -1; - } - if(_useHighestSendLayer) - { - // we need to check if we should drop the frame - // it could be a temporal layer (aka a temporal frame) - const uint8_t dependencyQualityID = (ptrH264Info->SVCheader[0].dependencyID << 4) + ptrH264Info->SVCheader[0].qualityID; - - bool dummyHighestLayer; - if(SendH264SVCLayer(frameType, - ptrH264Info->SVCheader[0].temporalID, - dependencyQualityID, - dummyHighestLayer) == false) - { - // skip send this frame - return 0; - } - } - - uint16_t idxNALU = 0; - while (payloadBytesToSend > 0) - { - bool switchToFUA = false; - if (SendH264_STAP_A_PACSI(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - switchToFUA, - payloadBytesToSend, - payloadData, - rtpHeaderLength, - decodingOrderNumber) != 0) - { - return -1; - } - if(switchToFUA) - { - // FU_A for this NALU - if (SendH264_FU_A(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - payloadBytesToSend, - payloadData, - rtpHeaderLength, - true) != 0) - { - return -1; - } - } - } - return 0; -} - -int32_t -RTPSenderH264::SetH264PacketizationMode(const H264PacketizationMode mode) -{ - _h264Mode = mode; - return 0; -} - -int32_t -RTPSenderH264::SetH264SendModeNALU_PPS_SPS(const bool dontSend) -{ - _h264SendPPS_SPS = !dontSend; - return 0; -} - -bool -RTPSenderH264::SendH264SVCLayer(const FrameType frameType, - const uint8_t temporalID, - const uint8_t dependencyQualityID, - bool& higestLayer) -{ - uint8_t dependencyID = dependencyQualityID >> 4; - - // keyframe required to switch between dependency layers not quality and temporal - if( _highestDependencyLayer != _highestDependencyLayerOld) - { - // we want to switch dependency layer - if(frameType == kVideoFrameKey) - { - // key frame we can change layer if it's correct layer - if(_highestDependencyLayer > _highestDependencyLayerOld) - { - // we want to switch up - // does this packet belong to a new layer? - - if( dependencyID > _highestDependencyLayerOld && - dependencyID <= _highestDependencyLayer) - { - _highestDependencyLayerOld = dependencyID; - _highestDependencyQualityIDOld = _highestDependencyQualityID; - - if( dependencyID == _highestDependencyLayer && - dependencyQualityID == _highestDependencyQualityID) - { - higestLayer = true; - } - // relay - return true; - } - } - if(_highestDependencyLayer < _highestDependencyLayerOld) - { - // we want to switch down - // does this packet belong to a low layer? - if( dependencyID <= _highestDependencyLayer) - { - _highestDependencyLayerOld = dependencyID; - _highestDependencyQualityIDOld = _highestDependencyQualityID; - if( dependencyID == _highestDependencyLayer && - dependencyQualityID == _highestDependencyQualityID) - { - higestLayer = true; - } - // relay - return true; - } - } - } else - { - // Delta frame and we are waiting to switch dependency layer - if(_highestDependencyLayer > _highestDependencyLayerOld) - { - // we want to switch up to a higher dependency layer - // use old setting until we get a key-frame - - // filter based on old dependency - // we could have allowed to add a MGS layer lower than the dependency ID - // but then we can't know the highest layer relayed we assume that the user - // will add one layer at a time - if( _highestTemporalLayer < temporalID || - _highestDependencyLayerOld < dependencyID || - _highestDependencyQualityIDOld < dependencyQualityID) - { - // drop - return false; - } - // highest layer based on old - if( dependencyID == _highestDependencyLayerOld && - dependencyQualityID == _highestDependencyQualityIDOld) - { - higestLayer = true; - } - } else - { - // we want to switch down to a lower dependency layer, - // use old setting, done bellow - // drop all temporal layers while waiting for the key-frame - if(temporalID > 0) - { - // drop - return false; - } - // we can't drop a lower MGS layer since this might depend on it - // however we can drop MGS layers larger than dependecyQualityId - // with dependency from old and quality 0 - if( _highestDependencyLayerOld < dependencyID || - (_highestDependencyQualityIDOld & 0xf0) < dependencyQualityID) - { - // drop - return false; - } - if( dependencyID == _highestDependencyLayerOld && - dependencyQualityID == (_highestDependencyQualityIDOld & 0xf0)) - { - higestLayer = true; - } - } - } - } else - { - // filter based on current state - if( _highestTemporalLayer < temporalID || - _highestDependencyLayer < dependencyID || - _highestDependencyQualityID < dependencyQualityID) - { - // drop - return false; - } - if( dependencyID == _highestDependencyLayer && - dependencyQualityID == _highestDependencyQualityID) - { - higestLayer = true; - } - } - return true; -} - -int32_t -RTPSenderH264::SetHighestSendLayer(const uint8_t dependencyQualityLayer, - const uint8_t temporalLayer) -{ - const uint8_t dependencyLayer = (dependencyQualityLayer >> 4); - - if(_highestDependencyLayerOld != _highestDependencyLayer) - { - // we have not switched to the new dependency yet - } else - { - if(_highestDependencyLayer == dependencyLayer) - { - // no change of dependency - // switch now _highestDependencyQualityIDOld - _highestDependencyQualityIDOld = dependencyQualityLayer; - }else - { - // change of dependency, update _highestDependencyQualityIDOld store as old - _highestDependencyQualityIDOld = _highestDependencyQualityID; - } - } - _useHighestSendLayer = true; - _highestDependencyLayer = dependencyLayer; - _highestDependencyQualityID = dependencyQualityLayer; - _highestTemporalLayer = temporalLayer; - return 0; -} - -int32_t -RTPSenderH264::HighestSendLayer(uint8_t& dependencyQualityLayer, - uint8_t& temporalLayer) -{ - if (!_useHighestSendLayer) - { - // No information set - return -1; - } - dependencyQualityLayer = _highestDependencyQualityID; - temporalLayer = _highestTemporalLayer; - return 0; -} -/* -* H.264 -*/ -int32_t -RTPSenderH264::SendH264(const FrameType frameType, - const int8_t payloadType, - const uint32_t captureTimeStamp, - const uint8_t* payloadData, - const uint32_t payloadSize, - H264Information& h264Information) -{ - int32_t payloadBytesToSend = payloadSize; - const uint8_t* data = payloadData; - bool switchToFUA = false; - const uint16_t rtpHeaderLength = _rtpSender.RTPHeaderLength(); - - const H264Info* ptrH264Info = NULL; - if (h264Information.GetInfo(payloadData,payloadSize, ptrH264Info) == -1) - { - return -1; - } - uint16_t idxNALU = 0; - uint16_t DONCdummy = 0; - - while (payloadBytesToSend > 0) - { - switch(_h264Mode) - { - case H264_NON_INTERLEAVED_MODE: - - if(!switchToFUA) - { - if(SendH264_STAP_A(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - switchToFUA, - payloadBytesToSend, - data, - rtpHeaderLength) != 0) - { - return -1; - } - } - else - { - // FUA for the rest of the frame - if(SendH264_FU_A(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - payloadBytesToSend, - data, - rtpHeaderLength, - DONCdummy) != 0) - { - return -1; - } - // try to go back to STAP_A - switchToFUA = false; - } - break; - case H264_SINGLE_NAL_MODE: - { - // modeSingleU - if(SendH264_SingleMode(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - payloadBytesToSend, - data, - rtpHeaderLength, - DONCdummy) != 0) - { - return -1; - } - break; - } - case H264_INTERLEAVED_MODE: - // not supported - assert(false); - return -1; - } - } - return 0; -} -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/rtp_sender_h264.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/rtp_sender_h264.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/rtp_sender_h264.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/H264/rtp_sender_h264.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_RTP_SENDER_H264_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_RTP_SENDER_H264_H_ - -#include "webrtc/typedefs.h" -#include "ModuleRTPRTCPConfig.h" -#include "rtp_rtcp_defines.h" -#include "h264_information.h" - -#include "RTPSender.h" - -namespace webrtc { -class RTPSenderH264 -{ -public: - int32_t SendH264(const FrameType frameType, - const int8_t payloadType, - const uint32_t captureTimeStamp, - const uint8_t* payloadData, - const uint32_t payloadSize, - H264Information& h264Information); - - int32_t SendH264SVC(const FrameType frameType, - const int8_t payloadType, - const uint32_t captureTimeStamp, - const uint8_t* payloadData, - const uint32_t payloadSize, - H264Information& h264Information); - - // H.264 AVC - int32_t SetH264PacketizationMode(const H264PacketizationMode mode); - - int32_t SetH264SendModeNALU_PPS_SPS(const bool dontSend); - - // H.264 SVC - int32_t SetHighestSendLayer(const uint8_t dependencyQualityLayer, - const uint8_t temporalLayer); - - int32_t HighestSendLayer(uint8_t& dependencyQualityLayer, - uint8_t& temporalLayer); - -protected: - RTPSenderH264(RTPSenderInterface* rtpSender); - virtual ~RTPSenderH264(); - - int32_t Init(); - - virtual uint16_t FECPacketOverhead() const = 0; - virtual RtpVideoCodecTypes VideoCodecType() const = 0; - - virtual int32_t SendVideoPacket(const FrameType frameType, - const uint8_t* dataBuffer, - const uint16_t payloadLength, - const uint16_t rtpHeaderLength, - bool baseLayerVideoPacket=false) = 0; - - - bool SendH264SVCLayer(const FrameType frameType, - const uint8_t temporalID, - const uint8_t dependencyQualityID, - bool& higestLayer); - - // H.264 SVC - int32_t AddH264PACSINALU(const bool firstPacketInNALU, - const bool lastPacketInNALU, - const H264_PACSI_NALU& paci, - const H264_SVC_NALUHeader& svc, - const uint16_t DONC, - uint8_t* databuffer, - int32_t& curByte) const; - - int32_t SendH264FillerData(const WebRtcRTPHeader* rtpHeader, - const uint16_t bytesToSend, - const uint32_t ssrc); - - int32_t SendH264FillerData(const uint32_t captureTimestamp, - const uint8_t payloadType, - const uint32_t bytesToSend); - - int32_t SendH264SVCRelayPacket(const WebRtcRTPHeader* rtpHeader, - const uint8_t* incomingRTPPacket, - const uint16_t incomingRTPPacketSize, - const uint32_t ssrc, - const bool higestLayer); - - int32_t SetH264RelaySequenceNumber(const uint16_t seqNum); - - int32_t SetH264RelayCompleteLayer(const bool complete); - - // H.264 - H264PacketizationMode _h264Mode; - bool _h264SendPPS_SPS; - - // H.264-SVC - int8_t _h264SVCPayloadType; - uint16_t _h264SVCRelaySequenceNumber; - uint32_t _h264SVCRelayTimeStamp; - bool _h264SVCRelayLayerComplete; - - -private: - // H.264 - int32_t SendH264_SingleMode(const FrameType frameType, - const H264Info* ptrH264Info, - uint16_t &idxNALU, - const int8_t payloadType, - const uint32_t captureTimeStamp, - int32_t &payloadBytesToSend, - const uint8_t*& data, - const uint16_t rtpHeaderLength, - const bool sendSVCPACSI=false); - - int32_t SendH264_FU_A(const FrameType frameType, - const H264Info* ptrH264Info, - uint16_t &idxNALU, - const int8_t payloadType, - const uint32_t captureTimeStamp, - int32_t &payloadBytesToSend, - const uint8_t*& data, - const uint16_t rtpHeaderLength, - const bool sendSVCPACSI = false); - - int32_t SendH264_STAP_A(const FrameType frameType, - const H264Info* ptrH264Info, - uint16_t &idxNALU, - const int8_t payloadType, - const uint32_t captureTimeStamp, - bool& switchToFUA, - int32_t &payloadBytesToSend, - const uint8_t*& data, - const uint16_t rtpHeaderLength); - - int32_t SendH264_STAP_A_PACSI(const FrameType frameType, - const H264Info* ptrH264Info, - uint16_t &idxNALU, - const int8_t payloadType, - const uint32_t captureTimeStamp, - bool& switchToFUA, - int32_t &payloadBytesToSend, - const uint8_t*& data, - const uint16_t rtpHeaderLengh) - - int32_t SendH264_SinglePACSI(const FrameType frameType, - const H264Info* ptrH264Info, - const uint16_t idxNALU, - const int8_t payloadType, - const uint32_t captureTimeStamp, - const bool firstPacketInNALU, - const bool lastPacketInNALU); - - bool AddH264SVCNALUHeader(const H264_SVC_NALUHeader& svc, - uint8_t* databuffer, - int32_t& curByte) const; - - RTPSenderInterface& _rtpSender; - - // relay - bool _useHighestSendLayer; - uint8_t _highestDependencyLayerOld; - uint8_t _highestDependencyQualityIDOld; - uint8_t _highestDependencyLayer; - uint8_t _highestDependencyQualityID; - uint8_t _highestTemporalLayer; - - -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_RTP_SENDER_H264_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/mock/mock_rtp_payload_strategy.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/mock/mock_rtp_payload_strategy.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/mock/mock_rtp_payload_strategy.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/mock/mock_rtp_payload_strategy.h 2015-02-03 14:33:36.000000000 +0000 @@ -21,21 +21,21 @@ MOCK_CONST_METHOD0(CodecsMustBeUnique, bool()); MOCK_CONST_METHOD4(PayloadIsCompatible, - bool(const ModuleRTPUtility::Payload& payload, - const uint32_t frequency, - const uint8_t channels, - const uint32_t rate)); + bool(const RtpUtility::Payload& payload, + const uint32_t frequency, + const uint8_t channels, + const uint32_t rate)); MOCK_CONST_METHOD2(UpdatePayloadRate, - void(ModuleRTPUtility::Payload* payload, const uint32_t rate)); - MOCK_CONST_METHOD1(GetPayloadTypeFrequency, int( - const ModuleRTPUtility::Payload& payload)); - MOCK_CONST_METHOD5(CreatePayloadType, - ModuleRTPUtility::Payload*( - const char payloadName[RTP_PAYLOAD_NAME_SIZE], - const int8_t payloadType, - const uint32_t frequency, - const uint8_t channels, - const uint32_t rate)); + void(RtpUtility::Payload* payload, const uint32_t rate)); + MOCK_CONST_METHOD1(GetPayloadTypeFrequency, + int(const RtpUtility::Payload& payload)); + MOCK_CONST_METHOD5( + CreatePayloadType, + RtpUtility::Payload*(const char payloadName[RTP_PAYLOAD_NAME_SIZE], + const int8_t payloadType, + const uint32_t frequency, + const uint8_t channels, + const uint32_t rate)); }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -41,7 +41,7 @@ virtual int32_t OnReceivedPayloadData( const uint8_t* data, const uint16_t size, - const webrtc::WebRtcRTPHeader* rtp_header) { + const webrtc::WebRtcRTPHeader* rtp_header) OVERRIDE { if (!sequence_numbers_.empty()) EXPECT_EQ(kTestSsrc, rtp_header->header.ssrc); sequence_numbers_.push_back(rtp_header->header.sequenceNumber); @@ -56,7 +56,7 @@ virtual ~TestRtpFeedback() {} virtual void OnIncomingSSRCChanged(const int32_t id, - const uint32_t ssrc) { + const uint32_t ssrc) OVERRIDE { rtp_rtcp_->SetRemoteSSRC(ssrc); } @@ -95,7 +95,7 @@ packet_loss_ = 0; } - virtual int SendPacket(int channel, const void *data, int len) { + virtual int SendPacket(int channel, const void *data, int len) OVERRIDE { count_++; const unsigned char* ptr = static_cast(data); uint32_t ssrc = (ptr[8] << 24) + (ptr[9] << 16) + (ptr[10] << 8) + ptr[11]; @@ -112,7 +112,10 @@ return len; } int packet_length = len; - uint8_t restored_packet[1500]; + // TODO(pbos): Figure out why this needs to be initialized. Likely this + // is hiding a bug either in test setup or other code. + // https://code.google.com/p/webrtc/issues/detail?id=3183 + uint8_t restored_packet[1500] = {0}; uint8_t* restored_packet_ptr = restored_packet; RTPHeader header; scoped_ptr parser(RtpHeaderParser::Create()); @@ -143,7 +146,7 @@ return len; } - virtual int SendRTCPPacket(int channel, const void *data, int len) { + virtual int SendRTCPPacket(int channel, const void *data, int len) OVERRIDE { if (module_->IncomingRtcpPacket((const uint8_t*)data, len) == 0) { return len; } @@ -164,7 +167,7 @@ class RtpRtcpRtxNackTest : public ::testing::Test { protected: RtpRtcpRtxNackTest() - : rtp_payload_registry_(0, RTPPayloadStrategy::CreateStrategy(false)), + : rtp_payload_registry_(RTPPayloadStrategy::CreateStrategy(false)), rtp_rtcp_module_(NULL), transport_(kTestSsrc + 1), receiver_(), @@ -172,7 +175,7 @@ fake_clock(123456) {} ~RtpRtcpRtxNackTest() {} - virtual void SetUp() { + virtual void SetUp() OVERRIDE { RtpRtcp::Configuration configuration; configuration.id = kTestId; configuration.audio = false; @@ -188,7 +191,7 @@ kTestId, &fake_clock, &receiver_, rtp_feedback_.get(), &rtp_payload_registry_)); - EXPECT_EQ(0, rtp_rtcp_module_->SetSSRC(kTestSsrc)); + rtp_rtcp_module_->SetSSRC(kTestSsrc); EXPECT_EQ(0, rtp_rtcp_module_->SetRTCPStatus(kRtcpCompound)); rtp_receiver_->SetNACKStatus(kNackRtcp); EXPECT_EQ(0, rtp_rtcp_module_->SetStorePacketsStatus(true, 600)); @@ -253,9 +256,9 @@ } void RunRtxTest(RtxMode rtx_method, int loss) { - rtp_payload_registry_.SetRtxStatus(true, kTestSsrc + 1); - EXPECT_EQ(0, rtp_rtcp_module_->SetRTXSendStatus(rtx_method, true, - kTestSsrc + 1)); + rtp_payload_registry_.SetRtxSsrc(kTestSsrc + 1); + rtp_rtcp_module_->SetRTXSendStatus(rtx_method); + rtp_rtcp_module_->SetRtxSsrc(kTestSsrc + 1); transport_.DropEveryNthPacket(loss); uint32_t timestamp = 3000; uint16_t nack_list[kVideoNackListSize]; @@ -277,7 +280,7 @@ receiver_.sequence_numbers_.sort(); } - virtual void TearDown() { + virtual void TearDown() OVERRIDE { delete rtp_rtcp_module_; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/producer_fec.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/producer_fec.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/producer_fec.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/producer_fec.cc 2015-02-03 14:33:36.000000000 +0000 @@ -61,7 +61,7 @@ void RedPacket::SetSeqNum(int seq_num) { assert(seq_num >= 0 && seq_num < (1<<16)); - ModuleRTPUtility::AssignUWord16ToBuffer(&data_[2], seq_num); + RtpUtility::AssignUWord16ToBuffer(&data_[2], seq_num); } void RedPacket::AssignPayload(const uint8_t* payload, int length) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/producer_fec_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/producer_fec_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/producer_fec_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/producer_fec_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -39,7 +39,7 @@ class ProducerFecTest : public ::testing::Test { protected: virtual void SetUp() { - fec_ = new ForwardErrorCorrection(0); + fec_ = new ForwardErrorCorrection(); producer_ = new ProducerFec(fec_); generator_ = new FrameGenerator; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc 2015-02-03 14:33:36.000000000 +0000 @@ -133,11 +133,12 @@ void StreamStatisticianImpl::UpdateJitter(const RTPHeader& header, uint32_t receive_time_secs, uint32_t receive_time_frac) { - uint32_t receive_time_rtp = ModuleRTPUtility::ConvertNTPTimeToRTP( - receive_time_secs, receive_time_frac, header.payload_type_frequency); - uint32_t last_receive_time_rtp = ModuleRTPUtility::ConvertNTPTimeToRTP( - last_receive_time_secs_, last_receive_time_frac_, - header.payload_type_frequency); + uint32_t receive_time_rtp = RtpUtility::ConvertNTPTimeToRTP( + receive_time_secs, receive_time_frac, header.payload_type_frequency); + uint32_t last_receive_time_rtp = + RtpUtility::ConvertNTPTimeToRTP(last_receive_time_secs_, + last_receive_time_frac_, + header.payload_type_frequency); int32_t time_diff_samples = (receive_time_rtp - last_receive_time_rtp) - (header.timestamp - last_received_timestamp_); @@ -407,36 +408,31 @@ void ReceiveStatisticsImpl::IncomingPacket(const RTPHeader& header, size_t bytes, bool retransmitted) { - StatisticianImplMap::iterator it; + StreamStatisticianImpl* impl; { CriticalSectionScoped cs(receive_statistics_lock_.get()); - it = statisticians_.find(header.ssrc); - if (it == statisticians_.end()) { - std::pair insert_result = - statisticians_.insert(std::make_pair( - header.ssrc, new StreamStatisticianImpl(clock_, this, this))); - it = insert_result.first; + StatisticianImplMap::iterator it = statisticians_.find(header.ssrc); + if (it != statisticians_.end()) { + impl = it->second; + } else { + impl = new StreamStatisticianImpl(clock_, this, this); + statisticians_[header.ssrc] = impl; } } - it->second->IncomingPacket(header, bytes, retransmitted); + // StreamStatisticianImpl instance is created once and only destroyed when + // this whole ReceiveStatisticsImpl is destroyed. StreamStatisticianImpl has + // it's own locking so don't hold receive_statistics_lock_ (potential + // deadlock). + impl->IncomingPacket(header, bytes, retransmitted); } void ReceiveStatisticsImpl::FecPacketReceived(uint32_t ssrc) { CriticalSectionScoped cs(receive_statistics_lock_.get()); StatisticianImplMap::iterator it = statisticians_.find(ssrc); - assert(it != statisticians_.end()); - it->second->FecPacketReceived(); -} - -void ReceiveStatisticsImpl::ChangeSsrc(uint32_t from_ssrc, uint32_t to_ssrc) { - CriticalSectionScoped cs(receive_statistics_lock_.get()); - StatisticianImplMap::iterator from_it = statisticians_.find(from_ssrc); - if (from_it == statisticians_.end()) - return; - if (statisticians_.find(to_ssrc) != statisticians_.end()) - return; - statisticians_[to_ssrc] = from_it->second; - statisticians_.erase(from_it); + // Ignore FEC if it is the first packet. + if (it != statisticians_.end()) { + it->second->FecPacketReceived(); + } } StatisticianMap ReceiveStatisticsImpl::GetActiveStatisticians() const { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h 2015-02-03 14:33:36.000000000 +0000 @@ -114,8 +114,6 @@ virtual int32_t Process() OVERRIDE; virtual int32_t TimeUntilNextProcess() OVERRIDE; - void ChangeSsrc(uint32_t from_ssrc, uint32_t to_ssrc); - virtual void RegisterRtcpStatisticsCallback(RtcpStatisticsCallback* callback) OVERRIDE; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -219,41 +219,42 @@ EXPECT_EQ(1u, callback.num_calls_); } -TEST_F(ReceiveStatisticsTest, RtpCallbacks) { - class TestCallback : public StreamDataCountersCallback { - public: - TestCallback() - : StreamDataCountersCallback(), num_calls_(0), ssrc_(0), stats_() {} - virtual ~TestCallback() {} - - virtual void DataCountersUpdated(const StreamDataCounters& counters, - uint32_t ssrc) { - ssrc_ = ssrc; - stats_ = counters; - ++num_calls_; - } - - void ExpectMatches(uint32_t num_calls, - uint32_t ssrc, - uint32_t bytes, - uint32_t padding, - uint32_t packets, - uint32_t retransmits, - uint32_t fec) { - EXPECT_EQ(num_calls, num_calls_); - EXPECT_EQ(ssrc, ssrc_); - EXPECT_EQ(bytes, stats_.bytes); - EXPECT_EQ(padding, stats_.padding_bytes); - EXPECT_EQ(packets, stats_.packets); - EXPECT_EQ(retransmits, stats_.retransmitted_packets); - EXPECT_EQ(fec, stats_.fec_packets); - } - - uint32_t num_calls_; - uint32_t ssrc_; - StreamDataCounters stats_; - } callback; +class RtpTestCallback : public StreamDataCountersCallback { + public: + RtpTestCallback() + : StreamDataCountersCallback(), num_calls_(0), ssrc_(0), stats_() {} + virtual ~RtpTestCallback() {} + + virtual void DataCountersUpdated(const StreamDataCounters& counters, + uint32_t ssrc) { + ssrc_ = ssrc; + stats_ = counters; + ++num_calls_; + } + + void ExpectMatches(uint32_t num_calls, + uint32_t ssrc, + uint32_t bytes, + uint32_t padding, + uint32_t packets, + uint32_t retransmits, + uint32_t fec) { + EXPECT_EQ(num_calls, num_calls_); + EXPECT_EQ(ssrc, ssrc_); + EXPECT_EQ(bytes, stats_.bytes); + EXPECT_EQ(padding, stats_.padding_bytes); + EXPECT_EQ(packets, stats_.packets); + EXPECT_EQ(retransmits, stats_.retransmitted_packets); + EXPECT_EQ(fec, stats_.fec_packets); + } + + uint32_t num_calls_; + uint32_t ssrc_; + StreamDataCounters stats_; +}; +TEST_F(ReceiveStatisticsTest, RtpCallbacks) { + RtpTestCallback callback; receive_statistics_->RegisterRtpStatisticsCallback(&callback); const uint32_t kHeaderLength = 20; @@ -300,4 +301,23 @@ callback.ExpectMatches( 5, kSsrc1, 4 * kPacketSize1, kPaddingLength * 2, 4, 1, 1); } + +TEST_F(ReceiveStatisticsTest, RtpCallbacksFecFirst) { + RtpTestCallback callback; + receive_statistics_->RegisterRtpStatisticsCallback(&callback); + + const uint32_t kHeaderLength = 20; + + // If first packet is FEC, ignore it. + receive_statistics_->FecPacketReceived(kSsrc1); + EXPECT_EQ(0u, callback.num_calls_); + + header1_.headerLength = kHeaderLength; + receive_statistics_->IncomingPacket( + header1_, kPacketSize1 + kHeaderLength, false); + callback.ExpectMatches(1, kSsrc1, kPacketSize1, 0, 1, 0, 0); + + receive_statistics_->FecPacketReceived(kSsrc1); + callback.ExpectMatches(2, kSsrc1, kPacketSize1, 0, 1, 0, 1); +} } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/rtp_rtcp/interface/remote_ntp_time_estimator.h" + +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/system_wrappers/interface/timestamp_extrapolator.h" + +namespace webrtc { + +static const int kTimingLogIntervalMs = 10000; + +// TODO(wu): Refactor this class so that it can be shared with +// vie_sync_module.cc. +RemoteNtpTimeEstimator::RemoteNtpTimeEstimator(Clock* clock) + : clock_(clock), + ts_extrapolator_(new TimestampExtrapolator(clock_->TimeInMilliseconds())), + last_timing_log_ms_(-1) { +} + +RemoteNtpTimeEstimator::~RemoteNtpTimeEstimator() {} + +bool RemoteNtpTimeEstimator::UpdateRtcpTimestamp(uint16_t rtt, + uint32_t ntp_secs, + uint32_t ntp_frac, + uint32_t rtcp_timestamp) { + bool new_rtcp_sr = false; + if (!UpdateRtcpList( + ntp_secs, ntp_frac, rtcp_timestamp, &rtcp_list_, &new_rtcp_sr)) { + return false; + } + if (!new_rtcp_sr) { + // No new RTCP SR since last time this function was called. + return true; + } + // Update extrapolator with the new arrival time. + // The extrapolator assumes the TimeInMilliseconds time. + int64_t receiver_arrival_time_ms = clock_->TimeInMilliseconds(); + int64_t sender_send_time_ms = Clock::NtpToMs(ntp_secs, ntp_frac); + int64_t sender_arrival_time_90k = (sender_send_time_ms + rtt / 2) * 90; + ts_extrapolator_->Update(receiver_arrival_time_ms, sender_arrival_time_90k); + return true; +} + +int64_t RemoteNtpTimeEstimator::Estimate(uint32_t rtp_timestamp) { + if (rtcp_list_.size() < 2) { + // We need two RTCP SR reports to calculate NTP. + return -1; + } + int64_t sender_capture_ntp_ms = 0; + if (!RtpToNtpMs(rtp_timestamp, rtcp_list_, &sender_capture_ntp_ms)) { + return -1; + } + uint32_t timestamp = sender_capture_ntp_ms * 90; + int64_t receiver_capture_ms = + ts_extrapolator_->ExtrapolateLocalTime(timestamp); + int64_t ntp_offset = + clock_->CurrentNtpInMilliseconds() - clock_->TimeInMilliseconds(); + int64_t receiver_capture_ntp_ms = receiver_capture_ms + ntp_offset; + int64_t now_ms = clock_->TimeInMilliseconds(); + if (now_ms - last_timing_log_ms_ > kTimingLogIntervalMs) { + LOG(LS_INFO) << "RTP timestamp: " << rtp_timestamp + << " in NTP clock: " << sender_capture_ntp_ms + << " estimated time in receiver clock: " << receiver_capture_ms + << " converted to NTP clock: " << receiver_capture_ntp_ms; + last_timing_log_ms_ = now_ms; + } + return receiver_capture_ntp_ms; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,101 @@ +/* +* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +* +* Use of this source code is governed by a BSD-style license +* that can be found in the LICENSE file in the root of the source +* tree. An additional intellectual property rights grant can be found +* in the file PATENTS. All contributing project authors may +* be found in the AUTHORS file in the root of the source tree. +*/ + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/common_types.h" +#include "webrtc/modules/rtp_rtcp/interface/remote_ntp_time_estimator.h" +#include "webrtc/system_wrappers/interface/clock.h" + +using ::testing::_; +using ::testing::DoAll; +using ::testing::Return; +using ::testing::SetArgPointee; + +namespace webrtc { + +static const int kTestRtt = 10; +static const int64_t kLocalClockInitialTimeMs = 123; +static const int64_t kRemoteClockInitialTimeMs = 345; +static const uint32_t kTimestampOffset = 567; + +class RemoteNtpTimeEstimatorTest : public ::testing::Test { + protected: + RemoteNtpTimeEstimatorTest() + : local_clock_(kLocalClockInitialTimeMs * 1000), + remote_clock_(kRemoteClockInitialTimeMs * 1000), + estimator_(&local_clock_) {} + ~RemoteNtpTimeEstimatorTest() {} + + void AdvanceTimeMilliseconds(int64_t ms) { + local_clock_.AdvanceTimeMilliseconds(ms); + remote_clock_.AdvanceTimeMilliseconds(ms); + } + + uint32_t GetRemoteTimestamp() { + return static_cast(remote_clock_.TimeInMilliseconds()) * 90 + + kTimestampOffset; + } + + void SendRtcpSr() { + uint32_t rtcp_timestamp = GetRemoteTimestamp(); + uint32_t ntp_seconds; + uint32_t ntp_fractions; + remote_clock_.CurrentNtp(ntp_seconds, ntp_fractions); + + AdvanceTimeMilliseconds(kTestRtt / 2); + ReceiveRtcpSr(kTestRtt, rtcp_timestamp, ntp_seconds, ntp_fractions); + } + + void UpdateRtcpTimestamp(uint16_t rtt, uint32_t ntp_secs, uint32_t ntp_frac, + uint32_t rtp_timestamp, bool expected_result) { + EXPECT_EQ(expected_result, + estimator_.UpdateRtcpTimestamp(rtt, ntp_secs, ntp_frac, + rtp_timestamp)); + } + + void ReceiveRtcpSr(uint16_t rtt, + uint32_t rtcp_timestamp, + uint32_t ntp_seconds, + uint32_t ntp_fractions) { + UpdateRtcpTimestamp(rtt, ntp_seconds, ntp_fractions, rtcp_timestamp, true); + } + + SimulatedClock local_clock_; + SimulatedClock remote_clock_; + RemoteNtpTimeEstimator estimator_; +}; + +TEST_F(RemoteNtpTimeEstimatorTest, Estimate) { + // Failed without valid NTP. + UpdateRtcpTimestamp(kTestRtt, 0, 0, 0, false); + + AdvanceTimeMilliseconds(1000); + // Remote peer sends first RTCP SR. + SendRtcpSr(); + + // Remote sends a RTP packet. + AdvanceTimeMilliseconds(15); + uint32_t rtp_timestamp = GetRemoteTimestamp(); + int64_t capture_ntp_time_ms = local_clock_.CurrentNtpInMilliseconds(); + + // Local peer needs at least 2 RTCP SR to calculate the capture time. + const int64_t kNotEnoughRtcpSr = -1; + EXPECT_EQ(kNotEnoughRtcpSr, estimator_.Estimate(rtp_timestamp)); + + AdvanceTimeMilliseconds(800); + // Remote sends second RTCP SR. + SendRtcpSr(); + + // Local peer gets enough RTCP SR to calculate the capture time. + EXPECT_EQ(capture_ntp_time_ms, estimator_.Estimate(rtp_timestamp)); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_format_remb_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_format_remb_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_format_remb_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_format_remb_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -30,12 +30,14 @@ rtcp_receiver_(rtcp_receiver) { } - virtual int SendPacket(int /*channel*/, const void* /*data*/, int /*len*/) { + virtual int SendPacket(int /*channel*/, + const void* /*data*/, + int /*len*/) OVERRIDE { return -1; } virtual int SendRTCPPacket(int /*channel*/, const void *packet, - int packetLength) { + int packetLength) OVERRIDE { RTCPUtility::RTCPParserV2 rtcpParser((uint8_t*)packet, (int32_t)packetLength, true); // Allow non-compound RTCP @@ -69,9 +71,10 @@ RemoteBitrateEstimatorFactory().Create( &remote_bitrate_observer_, system_clock_, + kMimdControl, kRemoteBitrateEstimatorMinBitrateBps)) {} - virtual void SetUp(); - virtual void TearDown(); + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; OverUseDetectorOptions over_use_detector_options_; Clock* system_clock_; @@ -96,7 +99,6 @@ rtcp_receiver_ = new RTCPReceiver(0, system_clock_, dummy_rtp_rtcp_impl_); test_transport_ = new TestTransport(rtcp_receiver_); - EXPECT_EQ(0, rtcp_sender_->Init()); EXPECT_EQ(0, rtcp_sender_->RegisterSendTransport(test_transport_)); } @@ -121,7 +123,8 @@ uint32_t SSRC = 456789; EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpNonCompound)); EXPECT_EQ(0, rtcp_sender_->SetREMBData(1234, 1, &SSRC)); - RTCPSender::FeedbackState feedback_state(dummy_rtp_rtcp_impl_); + RTCPSender::FeedbackState feedback_state = + dummy_rtp_rtcp_impl_->GetFeedbackState(); EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpRemb)); } @@ -129,7 +132,8 @@ uint32_t SSRCs[2] = {456789, 98765}; EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound)); EXPECT_EQ(0, rtcp_sender_->SetREMBData(1234, 2, SSRCs)); - RTCPSender::FeedbackState feedback_state(dummy_rtp_rtcp_impl_); + RTCPSender::FeedbackState feedback_state = + dummy_rtp_rtcp_impl_->GetFeedbackState(); EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpRemb)); } } // namespace diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,1090 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h" + +#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" +#include "webrtc/system_wrappers/interface/logging.h" + +using webrtc::RTCPUtility::kBtDlrr; +using webrtc::RTCPUtility::kBtReceiverReferenceTime; +using webrtc::RTCPUtility::kBtVoipMetric; + +using webrtc::RTCPUtility::PT_APP; +using webrtc::RTCPUtility::PT_BYE; +using webrtc::RTCPUtility::PT_IJ; +using webrtc::RTCPUtility::PT_PSFB; +using webrtc::RTCPUtility::PT_RR; +using webrtc::RTCPUtility::PT_RTPFB; +using webrtc::RTCPUtility::PT_SDES; +using webrtc::RTCPUtility::PT_SR; +using webrtc::RTCPUtility::PT_XR; + +using webrtc::RTCPUtility::RTCPPacketAPP; +using webrtc::RTCPUtility::RTCPPacketBYE; +using webrtc::RTCPUtility::RTCPPacketPSFBAPP; +using webrtc::RTCPUtility::RTCPPacketPSFBFIR; +using webrtc::RTCPUtility::RTCPPacketPSFBFIRItem; +using webrtc::RTCPUtility::RTCPPacketPSFBPLI; +using webrtc::RTCPUtility::RTCPPacketPSFBREMBItem; +using webrtc::RTCPUtility::RTCPPacketPSFBRPSI; +using webrtc::RTCPUtility::RTCPPacketPSFBSLI; +using webrtc::RTCPUtility::RTCPPacketPSFBSLIItem; +using webrtc::RTCPUtility::RTCPPacketReportBlockItem; +using webrtc::RTCPUtility::RTCPPacketRR; +using webrtc::RTCPUtility::RTCPPacketRTPFBNACK; +using webrtc::RTCPUtility::RTCPPacketRTPFBNACKItem; +using webrtc::RTCPUtility::RTCPPacketRTPFBTMMBN; +using webrtc::RTCPUtility::RTCPPacketRTPFBTMMBNItem; +using webrtc::RTCPUtility::RTCPPacketRTPFBTMMBR; +using webrtc::RTCPUtility::RTCPPacketRTPFBTMMBRItem; +using webrtc::RTCPUtility::RTCPPacketSR; +using webrtc::RTCPUtility::RTCPPacketXRDLRRReportBlockItem; +using webrtc::RTCPUtility::RTCPPacketXRReceiverReferenceTimeItem; +using webrtc::RTCPUtility::RTCPPacketXR; +using webrtc::RTCPUtility::RTCPPacketXRVOIPMetricItem; + +namespace webrtc { +namespace rtcp { +namespace { +// Unused SSRC of media source, set to 0. +const uint32_t kUnusedMediaSourceSsrc0 = 0; + +void AssignUWord8(uint8_t* buffer, size_t* offset, uint8_t value) { + buffer[(*offset)++] = value; +} +void AssignUWord16(uint8_t* buffer, size_t* offset, uint16_t value) { + RtpUtility::AssignUWord16ToBuffer(buffer + *offset, value); + *offset += 2; +} +void AssignUWord24(uint8_t* buffer, size_t* offset, uint32_t value) { + RtpUtility::AssignUWord24ToBuffer(buffer + *offset, value); + *offset += 3; +} +void AssignUWord32(uint8_t* buffer, size_t* offset, uint32_t value) { + RtpUtility::AssignUWord32ToBuffer(buffer + *offset, value); + *offset += 4; +} + +void ComputeMantissaAnd6bitBase2Exponent(uint32_t input_base10, + uint8_t bits_mantissa, + uint32_t* mantissa, + uint8_t* exp) { + // input_base10 = mantissa * 2^exp + assert(bits_mantissa <= 32); + uint32_t mantissa_max = (1 << bits_mantissa) - 1; + uint8_t exponent = 0; + for (uint32_t i = 0; i < 64; ++i) { + if (input_base10 <= (mantissa_max << i)) { + exponent = i; + break; + } + } + *exp = exponent; + *mantissa = (input_base10 >> exponent); +} + +size_t BlockToHeaderLength(size_t length_in_bytes) { + // Length in 32-bit words minus 1. + assert(length_in_bytes > 0); + assert(length_in_bytes % 4 == 0); + return (length_in_bytes / 4) - 1; +} + +// From RFC 3550, RTP: A Transport Protocol for Real-Time Applications. +// +// RTP header format. +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| RC/FMT | PT | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateHeader(uint8_t count_or_format, // Depends on packet type. + uint8_t packet_type, + size_t length, + uint8_t* buffer, + size_t* pos) { + assert(length <= 0xffff); + const uint8_t kVersion = 2; + AssignUWord8(buffer, pos, (kVersion << 6) + count_or_format); + AssignUWord8(buffer, pos, packet_type); + AssignUWord16(buffer, pos, length); +} + +// Sender report (SR) (RFC 3550). +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| RC | PT=SR=200 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of sender | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | NTP timestamp, most significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NTP timestamp, least significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | RTP timestamp | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | sender's packet count | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | sender's octet count | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + +void CreateSenderReport(const RTCPPacketSR& sr, + size_t length, + uint8_t* buffer, + size_t* pos) { + CreateHeader(sr.NumberOfReportBlocks, PT_SR, length, buffer, pos); + AssignUWord32(buffer, pos, sr.SenderSSRC); + AssignUWord32(buffer, pos, sr.NTPMostSignificant); + AssignUWord32(buffer, pos, sr.NTPLeastSignificant); + AssignUWord32(buffer, pos, sr.RTPTimestamp); + AssignUWord32(buffer, pos, sr.SenderPacketCount); + AssignUWord32(buffer, pos, sr.SenderOctetCount); +} + +// Receiver report (RR), header (RFC 3550). +// +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| RC | PT=RR=201 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of packet sender | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + +void CreateReceiverReport(const RTCPPacketRR& rr, + size_t length, + uint8_t* buffer, + size_t* pos) { + CreateHeader(rr.NumberOfReportBlocks, PT_RR, length, buffer, pos); + AssignUWord32(buffer, pos, rr.SenderSSRC); +} + +// Report block (RFC 3550). +// +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | SSRC_1 (SSRC of first source) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | fraction lost | cumulative number of packets lost | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | extended highest sequence number received | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | interarrival jitter | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | last SR (LSR) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | delay since last SR (DLSR) | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + +void CreateReportBlocks(const std::vector& blocks, + uint8_t* buffer, + size_t* pos) { + for (std::vector::const_iterator + it = blocks.begin(); it != blocks.end(); ++it) { + AssignUWord32(buffer, pos, (*it).SSRC); + AssignUWord8(buffer, pos, (*it).FractionLost); + AssignUWord24(buffer, pos, (*it).CumulativeNumOfPacketsLost); + AssignUWord32(buffer, pos, (*it).ExtendedHighestSequenceNumber); + AssignUWord32(buffer, pos, (*it).Jitter); + AssignUWord32(buffer, pos, (*it).LastSR); + AssignUWord32(buffer, pos, (*it).DelayLastSR); + } +} + +// Transmission Time Offsets in RTP Streams (RFC 5450). +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// hdr |V=2|P| RC | PT=IJ=195 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | inter-arrival jitter | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// . . +// . . +// . . +// | inter-arrival jitter | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateIj(const std::vector& ij_items, + uint8_t* buffer, + size_t* pos) { + size_t length = ij_items.size(); + CreateHeader(length, PT_IJ, length, buffer, pos); + for (std::vector::const_iterator it = ij_items.begin(); + it != ij_items.end(); ++it) { + AssignUWord32(buffer, pos, *it); + } +} + +// Source Description (SDES) (RFC 3550). +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// header |V=2|P| SC | PT=SDES=202 | length | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// chunk | SSRC/CSRC_1 | +// 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SDES items | +// | ... | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// chunk | SSRC/CSRC_2 | +// 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SDES items | +// | ... | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// Canonical End-Point Identifier SDES Item (CNAME) +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | CNAME=1 | length | user and domain name ... +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateSdes(const std::vector& chunks, + size_t length, + uint8_t* buffer, + size_t* pos) { + CreateHeader(chunks.size(), PT_SDES, length, buffer, pos); + const uint8_t kSdesItemType = 1; + for (std::vector::const_iterator it = chunks.begin(); + it != chunks.end(); ++it) { + AssignUWord32(buffer, pos, (*it).ssrc); + AssignUWord8(buffer, pos, kSdesItemType); + AssignUWord8(buffer, pos, (*it).name.length()); + memcpy(buffer + *pos, (*it).name.data(), (*it).name.length()); + *pos += (*it).name.length(); + memset(buffer + *pos, 0, (*it).null_octets); + *pos += (*it).null_octets; + } +} + +// Bye packet (BYE) (RFC 3550). +// +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| SC | PT=BYE=203 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC/CSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : ... : +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// (opt) | length | reason for leaving ... +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateBye(const RTCPPacketBYE& bye, + const std::vector& csrcs, + size_t length, + uint8_t* buffer, + size_t* pos) { + CreateHeader(length, PT_BYE, length, buffer, pos); + AssignUWord32(buffer, pos, bye.SenderSSRC); + for (std::vector::const_iterator it = csrcs.begin(); + it != csrcs.end(); ++it) { + AssignUWord32(buffer, pos, *it); + } +} + +// Application-Defined packet (APP) (RFC 3550). +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| subtype | PT=APP=204 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC/CSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | name (ASCII) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | application-dependent data ... +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateApp(const RTCPPacketAPP& app, + uint32_t ssrc, + size_t length, + uint8_t* buffer, + size_t* pos) { + CreateHeader(app.SubType, PT_APP, length, buffer, pos); + AssignUWord32(buffer, pos, ssrc); + AssignUWord32(buffer, pos, app.Name); + memcpy(buffer + *pos, app.Data, app.Size); + *pos += app.Size; +} + +// RFC 4585: Feedback format. +// +// Common packet format: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| FMT | PT | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of packet sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of media source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : Feedback Control Information (FCI) : +// : +// + +// Picture loss indication (PLI) (RFC 4585). +// +// FCI: no feedback control information. + +void CreatePli(const RTCPPacketPSFBPLI& pli, + size_t length, + uint8_t* buffer, + size_t* pos) { + const uint8_t kFmt = 1; + CreateHeader(kFmt, PT_PSFB, length, buffer, pos); + AssignUWord32(buffer, pos, pli.SenderSSRC); + AssignUWord32(buffer, pos, pli.MediaSSRC); +} + +// Slice loss indication (SLI) (RFC 4585). +// +// FCI: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | First | Number | PictureID | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateSli(const RTCPPacketPSFBSLI& sli, + const RTCPPacketPSFBSLIItem& sli_item, + size_t length, + uint8_t* buffer, + size_t* pos) { + const uint8_t kFmt = 2; + CreateHeader(kFmt, PT_PSFB, length, buffer, pos); + AssignUWord32(buffer, pos, sli.SenderSSRC); + AssignUWord32(buffer, pos, sli.MediaSSRC); + + AssignUWord8(buffer, pos, sli_item.FirstMB >> 5); + AssignUWord8(buffer, pos, (sli_item.FirstMB << 3) + + ((sli_item.NumberOfMB >> 10) & 0x07)); + AssignUWord8(buffer, pos, sli_item.NumberOfMB >> 2); + AssignUWord8(buffer, pos, (sli_item.NumberOfMB << 6) + sli_item.PictureId); +} + +// Generic NACK (RFC 4585). +// +// FCI: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | PID | BLP | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateNack(const RTCPPacketRTPFBNACK& nack, + const std::vector& nack_fields, + size_t length, + uint8_t* buffer, + size_t* pos) { + const uint8_t kFmt = 1; + CreateHeader(kFmt, PT_RTPFB, length, buffer, pos); + AssignUWord32(buffer, pos, nack.SenderSSRC); + AssignUWord32(buffer, pos, nack.MediaSSRC); + for (std::vector::const_iterator + it = nack_fields.begin(); it != nack_fields.end(); ++it) { + AssignUWord16(buffer, pos, (*it).PacketID); + AssignUWord16(buffer, pos, (*it).BitMask); + } +} + +// Reference picture selection indication (RPSI) (RFC 4585). +// +// FCI: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | PB |0| Payload Type| Native RPSI bit string | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | defined per codec ... | Padding (0) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateRpsi(const RTCPPacketPSFBRPSI& rpsi, + uint8_t padding_bytes, + size_t length, + uint8_t* buffer, + size_t* pos) { + // Native bit string should be a multiple of 8 bits. + assert(rpsi.NumberOfValidBits % 8 == 0); + const uint8_t kFmt = 3; + CreateHeader(kFmt, PT_PSFB, length, buffer, pos); + AssignUWord32(buffer, pos, rpsi.SenderSSRC); + AssignUWord32(buffer, pos, rpsi.MediaSSRC); + AssignUWord8(buffer, pos, padding_bytes * 8); + AssignUWord8(buffer, pos, rpsi.PayloadType); + memcpy(buffer + *pos, rpsi.NativeBitString, rpsi.NumberOfValidBits / 8); + *pos += rpsi.NumberOfValidBits / 8; + memset(buffer + *pos, 0, padding_bytes); + *pos += padding_bytes; +} + +// Full intra request (FIR) (RFC 5104). +// +// FCI: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Seq nr. | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateFir(const RTCPPacketPSFBFIR& fir, + const RTCPPacketPSFBFIRItem& fir_item, + size_t length, + uint8_t* buffer, + size_t* pos) { + const uint8_t kFmt = 4; + CreateHeader(kFmt, PT_PSFB, length, buffer, pos); + AssignUWord32(buffer, pos, fir.SenderSSRC); + AssignUWord32(buffer, pos, kUnusedMediaSourceSsrc0); + AssignUWord32(buffer, pos, fir_item.SSRC); + AssignUWord8(buffer, pos, fir_item.CommandSequenceNumber); + AssignUWord24(buffer, pos, 0); +} + +void CreateTmmbrItem(const RTCPPacketRTPFBTMMBRItem& tmmbr_item, + uint8_t* buffer, + size_t* pos) { + uint32_t bitrate_bps = tmmbr_item.MaxTotalMediaBitRate * 1000; + uint32_t mantissa = 0; + uint8_t exp = 0; + ComputeMantissaAnd6bitBase2Exponent(bitrate_bps, 17, &mantissa, &exp); + + AssignUWord32(buffer, pos, tmmbr_item.SSRC); + AssignUWord8(buffer, pos, (exp << 2) + ((mantissa >> 15) & 0x03)); + AssignUWord8(buffer, pos, mantissa >> 7); + AssignUWord8(buffer, pos, (mantissa << 1) + + ((tmmbr_item.MeasuredOverhead >> 8) & 0x01)); + AssignUWord8(buffer, pos, tmmbr_item.MeasuredOverhead); +} + +// Temporary Maximum Media Stream Bit Rate Request (TMMBR) (RFC 5104). +// +// FCI: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | MxTBR Exp | MxTBR Mantissa |Measured Overhead| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateTmmbr(const RTCPPacketRTPFBTMMBR& tmmbr, + const RTCPPacketRTPFBTMMBRItem& tmmbr_item, + size_t length, + uint8_t* buffer, + size_t* pos) { + const uint8_t kFmt = 3; + CreateHeader(kFmt, PT_RTPFB, length, buffer, pos); + AssignUWord32(buffer, pos, tmmbr.SenderSSRC); + AssignUWord32(buffer, pos, kUnusedMediaSourceSsrc0); + CreateTmmbrItem(tmmbr_item, buffer, pos); +} + +// Temporary Maximum Media Stream Bit Rate Notification (TMMBN) (RFC 5104). +// +// FCI: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | MxTBR Exp | MxTBR Mantissa |Measured Overhead| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateTmmbn(const RTCPPacketRTPFBTMMBN& tmmbn, + const std::vector& tmmbn_items, + size_t length, + uint8_t* buffer, + size_t* pos) { + const uint8_t kFmt = 4; + CreateHeader(kFmt, PT_RTPFB, length, buffer, pos); + AssignUWord32(buffer, pos, tmmbn.SenderSSRC); + AssignUWord32(buffer, pos, kUnusedMediaSourceSsrc0); + for (uint8_t i = 0; i < tmmbn_items.size(); ++i) { + CreateTmmbrItem(tmmbn_items[i], buffer, pos); + } +} + +// Receiver Estimated Max Bitrate (REMB) (draft-alvestrand-rmcat-remb). +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| FMT=15 | PT=206 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of packet sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of media source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Unique identifier 'R' 'E' 'M' 'B' | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Num SSRC | BR Exp | BR Mantissa | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC feedback | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | ... | + +void CreateRemb(const RTCPPacketPSFBAPP& remb, + const RTCPPacketPSFBREMBItem& remb_item, + size_t length, + uint8_t* buffer, + size_t* pos) { + uint32_t mantissa = 0; + uint8_t exp = 0; + ComputeMantissaAnd6bitBase2Exponent(remb_item.BitRate, 18, &mantissa, &exp); + + const uint8_t kFmt = 15; + CreateHeader(kFmt, PT_PSFB, length, buffer, pos); + AssignUWord32(buffer, pos, remb.SenderSSRC); + AssignUWord32(buffer, pos, kUnusedMediaSourceSsrc0); + AssignUWord8(buffer, pos, 'R'); + AssignUWord8(buffer, pos, 'E'); + AssignUWord8(buffer, pos, 'M'); + AssignUWord8(buffer, pos, 'B'); + AssignUWord8(buffer, pos, remb_item.NumberOfSSRCs); + AssignUWord8(buffer, pos, (exp << 2) + ((mantissa >> 16) & 0x03)); + AssignUWord8(buffer, pos, mantissa >> 8); + AssignUWord8(buffer, pos, mantissa); + for (uint8_t i = 0; i < remb_item.NumberOfSSRCs; ++i) { + AssignUWord32(buffer, pos, remb_item.SSRCs[i]); + } +} + +// From RFC 3611: RTP Control Protocol Extended Reports (RTCP XR). +// +// Format for XR packets: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P|reserved | PT=XR=207 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : report blocks : +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateXrHeader(const RTCPPacketXR& header, + size_t length, + uint8_t* buffer, + size_t* pos) { + CreateHeader(0U, PT_XR, length, buffer, pos); + AssignUWord32(buffer, pos, header.OriginatorSSRC); +} + +void CreateXrBlockHeader(uint8_t block_type, + uint16_t block_length, + uint8_t* buffer, + size_t* pos) { + AssignUWord8(buffer, pos, block_type); + AssignUWord8(buffer, pos, 0); + AssignUWord16(buffer, pos, block_length); +} + +// Receiver Reference Time Report Block (RFC 3611). +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | BT=4 | reserved | block length = 2 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NTP timestamp, most significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NTP timestamp, least significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateRrtr(const std::vector& rrtrs, + uint8_t* buffer, + size_t* pos) { + const uint16_t kBlockLength = 2; + for (std::vector::const_iterator it = + rrtrs.begin(); it != rrtrs.end(); ++it) { + CreateXrBlockHeader(kBtReceiverReferenceTime, kBlockLength, buffer, pos); + AssignUWord32(buffer, pos, (*it).NTPMostSignificant); + AssignUWord32(buffer, pos, (*it).NTPLeastSignificant); + } +} + +// DLRR Report Block (RFC 3611). +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | BT=5 | reserved | block length | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | SSRC_1 (SSRC of first receiver) | sub- +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block +// | last RR (LRR) | 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | delay since last RR (DLRR) | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | SSRC_2 (SSRC of second receiver) | sub- +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block +// : ... : 2 + +void CreateDlrr(const std::vector& dlrrs, + uint8_t* buffer, + size_t* pos) { + for (std::vector::const_iterator it = dlrrs.begin(); + it != dlrrs.end(); ++it) { + if ((*it).empty()) { + continue; + } + uint16_t block_length = 3 * (*it).size(); + CreateXrBlockHeader(kBtDlrr, block_length, buffer, pos); + for (Xr::DlrrBlock::const_iterator it_block = (*it).begin(); + it_block != (*it).end(); ++it_block) { + AssignUWord32(buffer, pos, (*it_block).SSRC); + AssignUWord32(buffer, pos, (*it_block).LastRR); + AssignUWord32(buffer, pos, (*it_block).DelayLastRR); + } + } +} + +// VoIP Metrics Report Block (RFC 3611). +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | BT=7 | reserved | block length = 8 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | loss rate | discard rate | burst density | gap density | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | burst duration | gap duration | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | round trip delay | end system delay | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | signal level | noise level | RERL | Gmin | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | R factor | ext. R factor | MOS-LQ | MOS-CQ | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | RX config | reserved | JB nominal | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | JB maximum | JB abs max | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateVoipMetric(const std::vector& metrics, + uint8_t* buffer, + size_t* pos) { + const uint16_t kBlockLength = 8; + for (std::vector::const_iterator it = + metrics.begin(); it != metrics.end(); ++it) { + CreateXrBlockHeader(kBtVoipMetric, kBlockLength, buffer, pos); + AssignUWord32(buffer, pos, (*it).SSRC); + AssignUWord8(buffer, pos, (*it).lossRate); + AssignUWord8(buffer, pos, (*it).discardRate); + AssignUWord8(buffer, pos, (*it).burstDensity); + AssignUWord8(buffer, pos, (*it).gapDensity); + AssignUWord16(buffer, pos, (*it).burstDuration); + AssignUWord16(buffer, pos, (*it).gapDuration); + AssignUWord16(buffer, pos, (*it).roundTripDelay); + AssignUWord16(buffer, pos, (*it).endSystemDelay); + AssignUWord8(buffer, pos, (*it).signalLevel); + AssignUWord8(buffer, pos, (*it).noiseLevel); + AssignUWord8(buffer, pos, (*it).RERL); + AssignUWord8(buffer, pos, (*it).Gmin); + AssignUWord8(buffer, pos, (*it).Rfactor); + AssignUWord8(buffer, pos, (*it).extRfactor); + AssignUWord8(buffer, pos, (*it).MOSLQ); + AssignUWord8(buffer, pos, (*it).MOSCQ); + AssignUWord8(buffer, pos, (*it).RXconfig); + AssignUWord8(buffer, pos, 0); + AssignUWord16(buffer, pos, (*it).JBnominal); + AssignUWord16(buffer, pos, (*it).JBmax); + AssignUWord16(buffer, pos, (*it).JBabsMax); + } +} +} // namespace + +void RtcpPacket::Append(RtcpPacket* packet) { + assert(packet); + appended_packets_.push_back(packet); +} + +RawPacket RtcpPacket::Build() const { + size_t length = 0; + uint8_t packet[IP_PACKET_SIZE]; + CreateAndAddAppended(packet, &length, IP_PACKET_SIZE); + return RawPacket(packet, length); +} + +void RtcpPacket::Build(uint8_t* packet, + size_t* length, + size_t max_length) const { + *length = 0; + CreateAndAddAppended(packet, length, max_length); +} + +void RtcpPacket::CreateAndAddAppended(uint8_t* packet, + size_t* length, + size_t max_length) const { + Create(packet, length, max_length); + for (std::vector::const_iterator it = appended_packets_.begin(); + it != appended_packets_.end(); ++it) { + (*it)->CreateAndAddAppended(packet, length, max_length); + } +} + +void Empty::Create(uint8_t* packet, size_t* length, size_t max_length) const { +} + +void SenderReport::Create(uint8_t* packet, + size_t* length, + size_t max_length) const { + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateSenderReport(sr_, BlockToHeaderLength(BlockLength()), packet, length); + CreateReportBlocks(report_blocks_, packet, length); +} + +void SenderReport::WithReportBlock(ReportBlock* block) { + assert(block); + if (report_blocks_.size() >= kMaxNumberOfReportBlocks) { + LOG(LS_WARNING) << "Max report blocks reached."; + return; + } + report_blocks_.push_back(block->report_block_); + sr_.NumberOfReportBlocks = report_blocks_.size(); +} + +void ReceiverReport::Create(uint8_t* packet, + size_t* length, + size_t max_length) const { + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateReceiverReport(rr_, BlockToHeaderLength(BlockLength()), packet, length); + CreateReportBlocks(report_blocks_, packet, length); +} + +void ReceiverReport::WithReportBlock(ReportBlock* block) { + assert(block); + if (report_blocks_.size() >= kMaxNumberOfReportBlocks) { + LOG(LS_WARNING) << "Max report blocks reached."; + return; + } + report_blocks_.push_back(block->report_block_); + rr_.NumberOfReportBlocks = report_blocks_.size(); +} + +void Ij::Create(uint8_t* packet, size_t* length, size_t max_length) const { + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateIj(ij_items_, packet, length); +} + +void Ij::WithJitterItem(uint32_t jitter) { + if (ij_items_.size() >= kMaxNumberOfIjItems) { + LOG(LS_WARNING) << "Max inter-arrival jitter items reached."; + return; + } + ij_items_.push_back(jitter); +} + +void Sdes::Create(uint8_t* packet, size_t* length, size_t max_length) const { + assert(!chunks_.empty()); + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateSdes(chunks_, BlockToHeaderLength(BlockLength()), packet, length); +} + +void Sdes::WithCName(uint32_t ssrc, std::string cname) { + assert(cname.length() <= 0xff); + if (chunks_.size() >= kMaxNumberOfChunks) { + LOG(LS_WARNING) << "Max SDES chunks reached."; + return; + } + // In each chunk, the list of items must be terminated by one or more null + // octets. The next chunk must start on a 32-bit boundary. + // CNAME (1 byte) | length (1 byte) | name | padding. + int null_octets = 4 - ((2 + cname.length()) % 4); + Chunk chunk; + chunk.ssrc = ssrc; + chunk.name = cname; + chunk.null_octets = null_octets; + chunks_.push_back(chunk); +} + +size_t Sdes::BlockLength() const { + // Header (4 bytes). + // Chunk: + // SSRC/CSRC (4 bytes) | CNAME (1 byte) | length (1 byte) | name | padding. + size_t length = kHeaderLength; + for (std::vector::const_iterator it = chunks_.begin(); + it != chunks_.end(); ++it) { + length += 6 + (*it).name.length() + (*it).null_octets; + } + assert(length % 4 == 0); + return length; +} + +void Bye::Create(uint8_t* packet, size_t* length, size_t max_length) const { + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateBye(bye_, csrcs_, BlockToHeaderLength(BlockLength()), packet, length); +} + +void Bye::WithCsrc(uint32_t csrc) { + if (csrcs_.size() >= kMaxNumberOfCsrcs) { + LOG(LS_WARNING) << "Max CSRC size reached."; + return; + } + csrcs_.push_back(csrc); +} + +void App::Create(uint8_t* packet, size_t* length, size_t max_length) const { + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateApp(app_, ssrc_, BlockToHeaderLength(BlockLength()), packet, length); +} + +void Pli::Create(uint8_t* packet, size_t* length, size_t max_length) const { + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreatePli(pli_, BlockToHeaderLength(BlockLength()), packet, length); +} + +void Sli::Create(uint8_t* packet, size_t* length, size_t max_length) const { + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateSli(sli_, sli_item_, BlockToHeaderLength(BlockLength()), packet, + length); +} + +void Nack::Create(uint8_t* packet, size_t* length, size_t max_length) const { + assert(!nack_fields_.empty()); + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateNack(nack_, nack_fields_, BlockToHeaderLength(BlockLength()), packet, + length); +} + +void Nack::WithList(const uint16_t* nack_list, int length) { + assert(nack_list); + assert(nack_fields_.empty()); + int i = 0; + while (i < length) { + uint16_t pid = nack_list[i++]; + // Bitmask specifies losses in any of the 16 packets following the pid. + uint16_t bitmask = 0; + while (i < length) { + int shift = static_cast(nack_list[i] - pid) - 1; + if (shift >= 0 && shift <= 15) { + bitmask |= (1 << shift); + ++i; + } else { + break; + } + } + RTCPUtility::RTCPPacketRTPFBNACKItem item; + item.PacketID = pid; + item.BitMask = bitmask; + nack_fields_.push_back(item); + } +} + +void Rpsi::Create(uint8_t* packet, size_t* length, size_t max_length) const { + assert(rpsi_.NumberOfValidBits > 0); + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateRpsi(rpsi_, padding_bytes_, BlockToHeaderLength(BlockLength()), packet, + length); +} + +void Rpsi::WithPictureId(uint64_t picture_id) { + const uint32_t kPidBits = 7; + const uint64_t k7MsbZeroMask = 0x1ffffffffffffffULL; + uint8_t required_bytes = 0; + uint64_t shifted_pid = picture_id; + do { + ++required_bytes; + shifted_pid = (shifted_pid >> kPidBits) & k7MsbZeroMask; + } while (shifted_pid > 0); + + // Convert picture id to native bit string (natively defined by the video + // codec). + int pos = 0; + for (int i = required_bytes - 1; i > 0; i--) { + rpsi_.NativeBitString[pos++] = + 0x80 | static_cast(picture_id >> (i * kPidBits)); + } + rpsi_.NativeBitString[pos++] = static_cast(picture_id & 0x7f); + rpsi_.NumberOfValidBits = pos * 8; + + // Calculate padding bytes (to reach next 32-bit boundary, 1, 2 or 3 bytes). + padding_bytes_ = 4 - ((2 + required_bytes) % 4); + if (padding_bytes_ == 4) { + padding_bytes_ = 0; + } +} + +void Fir::Create(uint8_t* packet, size_t* length, size_t max_length) const { + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateFir(fir_, fir_item_, BlockToHeaderLength(BlockLength()), packet, + length); +} + +void Remb::Create(uint8_t* packet, size_t* length, size_t max_length) const { + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateRemb(remb_, remb_item_, BlockToHeaderLength(BlockLength()), packet, + length); +} + +void Remb::AppliesTo(uint32_t ssrc) { + if (remb_item_.NumberOfSSRCs >= kMaxNumberOfSsrcs) { + LOG(LS_WARNING) << "Max number of REMB feedback SSRCs reached."; + return; + } + remb_item_.SSRCs[remb_item_.NumberOfSSRCs++] = ssrc; +} + +void Tmmbr::Create(uint8_t* packet, size_t* length, size_t max_length) const { + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateTmmbr(tmmbr_, tmmbr_item_, BlockToHeaderLength(BlockLength()), packet, + length); +} + +void Tmmbn::WithTmmbr(uint32_t ssrc, uint32_t bitrate_kbps, uint16_t overhead) { + assert(overhead <= 0x1ff); + if (tmmbn_items_.size() >= kMaxNumberOfTmmbrs) { + LOG(LS_WARNING) << "Max TMMBN size reached."; + return; + } + RTCPPacketRTPFBTMMBRItem tmmbn_item; + tmmbn_item.SSRC = ssrc; + tmmbn_item.MaxTotalMediaBitRate = bitrate_kbps; + tmmbn_item.MeasuredOverhead = overhead; + tmmbn_items_.push_back(tmmbn_item); +} + +void Tmmbn::Create(uint8_t* packet, size_t* length, size_t max_length) const { + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateTmmbn(tmmbn_, tmmbn_items_, BlockToHeaderLength(BlockLength()), packet, + length); +} + +void Xr::Create(uint8_t* packet, size_t* length, size_t max_length) const { + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateXrHeader(xr_header_, BlockToHeaderLength(BlockLength()), packet, + length); + CreateRrtr(rrtr_blocks_, packet, length); + CreateDlrr(dlrr_blocks_, packet, length); + CreateVoipMetric(voip_metric_blocks_, packet, length); +} + +void Xr::WithRrtr(Rrtr* rrtr) { + assert(rrtr); + if (rrtr_blocks_.size() >= kMaxNumberOfRrtrBlocks) { + LOG(LS_WARNING) << "Max RRTR blocks reached."; + return; + } + rrtr_blocks_.push_back(rrtr->rrtr_block_); +} + +void Xr::WithDlrr(Dlrr* dlrr) { + assert(dlrr); + if (dlrr_blocks_.size() >= kMaxNumberOfDlrrBlocks) { + LOG(LS_WARNING) << "Max DLRR blocks reached."; + return; + } + dlrr_blocks_.push_back(dlrr->dlrr_block_); +} + +void Xr::WithVoipMetric(VoipMetric* voip_metric) { + assert(voip_metric); + if (voip_metric_blocks_.size() >= kMaxNumberOfVoipMetricBlocks) { + LOG(LS_WARNING) << "Max Voip Metric blocks reached."; + return; + } + voip_metric_blocks_.push_back(voip_metric->metric_); +} + +size_t Xr::DlrrLength() const { + const size_t kBlockHeaderLen = 4; + const size_t kSubBlockLen = 12; + size_t length = 0; + for (std::vector::const_iterator it = dlrr_blocks_.begin(); + it != dlrr_blocks_.end(); ++it) { + if (!(*it).empty()) { + length += kBlockHeaderLen + kSubBlockLen * (*it).size(); + } + } + return length; +} + +void Dlrr::WithDlrrItem(uint32_t ssrc, + uint32_t last_rr, + uint32_t delay_last_rr) { + if (dlrr_block_.size() >= kMaxNumberOfDlrrItems) { + LOG(LS_WARNING) << "Max DLRR items reached."; + return; + } + RTCPPacketXRDLRRReportBlockItem dlrr; + dlrr.SSRC = ssrc; + dlrr.LastRR = last_rr; + dlrr.DelayLastRR = delay_last_rr; + dlrr_block_.push_back(dlrr); +} + +} // namespace rtcp +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_packet.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_packet.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_packet.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_packet.h 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,1086 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#ifndef WEBRTC_MODULES_RTP_RTCP_RTCP_PACKET_H_ +#define WEBRTC_MODULES_RTP_RTCP_RTCP_PACKET_H_ + +#include +#include +#include + +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" +#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace rtcp { + +enum { kCommonFbFmtLength = 12 }; +enum { kReportBlockLength = 24 }; + +class Dlrr; +class RawPacket; +class Rrtr; +class VoipMetric; + +// Class for building RTCP packets. +// +// Example: +// ReportBlock report_block; +// report_block.To(234) +// report_block.FractionLost(10); +// +// ReceiverReport rr; +// rr.From(123); +// rr.WithReportBlock(&report_block) +// +// Fir fir; +// fir.From(123); +// fir.To(234) +// fir.WithCommandSeqNum(123); +// +// size_t length = 0; // Builds an intra frame request +// uint8_t packet[kPacketSize]; // with sequence number 123. +// fir.Build(packet, &length, kPacketSize); +// +// RawPacket packet = fir.Build(); // Returns a RawPacket holding +// // the built rtcp packet. +// +// rr.Append(&fir) // Builds a compound RTCP packet with +// RawPacket packet = rr.Build(); // a receiver report, report block +// // and fir message. + +class RtcpPacket { + public: + virtual ~RtcpPacket() {} + + void Append(RtcpPacket* packet); + + RawPacket Build() const; + + void Build(uint8_t* packet, size_t* length, size_t max_length) const; + + protected: + RtcpPacket() : kHeaderLength(4) {} + + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const = 0; + + const size_t kHeaderLength; + + private: + void CreateAndAddAppended( + uint8_t* packet, size_t* length, size_t max_length) const; + + std::vector appended_packets_; +}; + +class Empty : public RtcpPacket { + public: + Empty() : RtcpPacket() {} + + virtual ~Empty() {} + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(Empty); +}; + +// From RFC 3550, RTP: A Transport Protocol for Real-Time Applications. +// +// RTCP report block (RFC 3550). +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | SSRC_1 (SSRC of first source) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | fraction lost | cumulative number of packets lost | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | extended highest sequence number received | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | interarrival jitter | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | last SR (LSR) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | delay since last SR (DLSR) | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + +class ReportBlock { + public: + ReportBlock() { + // TODO(asapersson): Consider adding a constructor to struct. + memset(&report_block_, 0, sizeof(report_block_)); + } + + ~ReportBlock() {} + + void To(uint32_t ssrc) { + report_block_.SSRC = ssrc; + } + void WithFractionLost(uint8_t fraction_lost) { + report_block_.FractionLost = fraction_lost; + } + void WithCumulativeLost(uint32_t cumulative_lost) { + report_block_.CumulativeNumOfPacketsLost = cumulative_lost; + } + void WithExtHighestSeqNum(uint32_t ext_highest_seq_num) { + report_block_.ExtendedHighestSequenceNumber = ext_highest_seq_num; + } + void WithJitter(uint32_t jitter) { + report_block_.Jitter = jitter; + } + void WithLastSr(uint32_t last_sr) { + report_block_.LastSR = last_sr; + } + void WithDelayLastSr(uint32_t delay_last_sr) { + report_block_.DelayLastSR = delay_last_sr; + } + + private: + friend class SenderReport; + friend class ReceiverReport; + RTCPUtility::RTCPPacketReportBlockItem report_block_; +}; + +// RTCP sender report (RFC 3550). +// +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| RC | PT=SR=200 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of sender | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | NTP timestamp, most significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NTP timestamp, least significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | RTP timestamp | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | sender's packet count | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | sender's octet count | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | report block(s) | +// | .... | + +class SenderReport : public RtcpPacket { + public: + SenderReport() : RtcpPacket() { + memset(&sr_, 0, sizeof(sr_)); + } + + virtual ~SenderReport() {} + + void From(uint32_t ssrc) { + sr_.SenderSSRC = ssrc; + } + void WithNtpSec(uint32_t sec) { + sr_.NTPMostSignificant = sec; + } + void WithNtpFrac(uint32_t frac) { + sr_.NTPLeastSignificant = frac; + } + void WithRtpTimestamp(uint32_t rtp_timestamp) { + sr_.RTPTimestamp = rtp_timestamp; + } + void WithPacketCount(uint32_t packet_count) { + sr_.SenderPacketCount = packet_count; + } + void WithOctetCount(uint32_t octet_count) { + sr_.SenderOctetCount = octet_count; + } + void WithReportBlock(ReportBlock* block); + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + enum { kMaxNumberOfReportBlocks = 0x1f }; + + size_t BlockLength() const { + const size_t kSrHeaderLength = 8; + const size_t kSenderInfoLength = 20; + return kSrHeaderLength + kSenderInfoLength + + report_blocks_.size() * kReportBlockLength; + } + + RTCPUtility::RTCPPacketSR sr_; + std::vector report_blocks_; + + DISALLOW_COPY_AND_ASSIGN(SenderReport); +}; + +// +// RTCP receiver report (RFC 3550). +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| RC | PT=RR=201 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of packet sender | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | report block(s) | +// | .... | + +class ReceiverReport : public RtcpPacket { + public: + ReceiverReport() : RtcpPacket() { + memset(&rr_, 0, sizeof(rr_)); + } + + virtual ~ReceiverReport() {} + + void From(uint32_t ssrc) { + rr_.SenderSSRC = ssrc; + } + void WithReportBlock(ReportBlock* block); + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + enum { kMaxNumberOfReportBlocks = 0x1f }; + + size_t BlockLength() const { + const size_t kRrHeaderLength = 8; + return kRrHeaderLength + report_blocks_.size() * kReportBlockLength; + } + + RTCPUtility::RTCPPacketRR rr_; + std::vector report_blocks_; + + DISALLOW_COPY_AND_ASSIGN(ReceiverReport); +}; + +// Transmission Time Offsets in RTP Streams (RFC 5450). +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// hdr |V=2|P| RC | PT=IJ=195 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | inter-arrival jitter | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// . . +// . . +// . . +// | inter-arrival jitter | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// If present, this RTCP packet must be placed after a receiver report +// (inside a compound RTCP packet), and MUST have the same value for RC +// (reception report count) as the receiver report. + +class Ij : public RtcpPacket { + public: + Ij() : RtcpPacket() {} + + virtual ~Ij() {} + + void WithJitterItem(uint32_t jitter); + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + enum { kMaxNumberOfIjItems = 0x1f }; + + size_t BlockLength() const { + return kHeaderLength + 4 * ij_items_.size(); + } + + std::vector ij_items_; + + DISALLOW_COPY_AND_ASSIGN(Ij); +}; + +// Source Description (SDES) (RFC 3550). +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// header |V=2|P| SC | PT=SDES=202 | length | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// chunk | SSRC/CSRC_1 | +// 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SDES items | +// | ... | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// chunk | SSRC/CSRC_2 | +// 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SDES items | +// | ... | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// Canonical End-Point Identifier SDES Item (CNAME) +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | CNAME=1 | length | user and domain name ... +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Sdes : public RtcpPacket { + public: + Sdes() : RtcpPacket() {} + + virtual ~Sdes() {} + + void WithCName(uint32_t ssrc, std::string cname); + + struct Chunk { + uint32_t ssrc; + std::string name; + int null_octets; + }; + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + enum { kMaxNumberOfChunks = 0x1f }; + + size_t BlockLength() const; + + std::vector chunks_; + + DISALLOW_COPY_AND_ASSIGN(Sdes); +}; + +// +// Bye packet (BYE) (RFC 3550). +// +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| SC | PT=BYE=203 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC/CSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : ... : +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// (opt) | length | reason for leaving ... +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Bye : public RtcpPacket { + public: + Bye() : RtcpPacket() { + memset(&bye_, 0, sizeof(bye_)); + } + + virtual ~Bye() {} + + void From(uint32_t ssrc) { + bye_.SenderSSRC = ssrc; + } + void WithCsrc(uint32_t csrc); + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + enum { kMaxNumberOfCsrcs = 0x1f - 1 }; + + size_t BlockLength() const { + size_t source_count = 1 + csrcs_.size(); + return kHeaderLength + 4 * source_count; + } + + RTCPUtility::RTCPPacketBYE bye_; + std::vector csrcs_; + + DISALLOW_COPY_AND_ASSIGN(Bye); +}; + +// Application-Defined packet (APP) (RFC 3550). +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| subtype | PT=APP=204 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC/CSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | name (ASCII) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | application-dependent data ... +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class App : public RtcpPacket { + public: + App() + : RtcpPacket(), + ssrc_(0) { + memset(&app_, 0, sizeof(app_)); + } + + virtual ~App() {} + + void From(uint32_t ssrc) { + ssrc_ = ssrc; + } + void WithSubType(uint8_t subtype) { + assert(subtype <= 0x1f); + app_.SubType = subtype; + } + void WithName(uint32_t name) { + app_.Name = name; + } + void WithData(const uint8_t* data, uint16_t data_length) { + assert(data); + assert(data_length <= kRtcpAppCode_DATA_SIZE); + assert(data_length % 4 == 0); + memcpy(app_.Data, data, data_length); + app_.Size = data_length; + } + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + size_t BlockLength() const { + return 12 + app_.Size; + } + + uint32_t ssrc_; + RTCPUtility::RTCPPacketAPP app_; + + DISALLOW_COPY_AND_ASSIGN(App); +}; + +// RFC 4585: Feedback format. +// +// Common packet format: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| FMT | PT | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of packet sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of media source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : Feedback Control Information (FCI) : +// : + +// Picture loss indication (PLI) (RFC 4585). +// +// FCI: no feedback control information. + +class Pli : public RtcpPacket { + public: + Pli() : RtcpPacket() { + memset(&pli_, 0, sizeof(pli_)); + } + + virtual ~Pli() {} + + void From(uint32_t ssrc) { + pli_.SenderSSRC = ssrc; + } + void To(uint32_t ssrc) { + pli_.MediaSSRC = ssrc; + } + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + size_t BlockLength() const { + return kCommonFbFmtLength; + } + + RTCPUtility::RTCPPacketPSFBPLI pli_; + + DISALLOW_COPY_AND_ASSIGN(Pli); +}; + +// Slice loss indication (SLI) (RFC 4585). +// +// FCI: +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | First | Number | PictureID | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Sli : public RtcpPacket { + public: + Sli() : RtcpPacket() { + memset(&sli_, 0, sizeof(sli_)); + memset(&sli_item_, 0, sizeof(sli_item_)); + } + + virtual ~Sli() {} + + void From(uint32_t ssrc) { + sli_.SenderSSRC = ssrc; + } + void To(uint32_t ssrc) { + sli_.MediaSSRC = ssrc; + } + void WithFirstMb(uint16_t first_mb) { + assert(first_mb <= 0x1fff); + sli_item_.FirstMB = first_mb; + } + void WithNumberOfMb(uint16_t number_mb) { + assert(number_mb <= 0x1fff); + sli_item_.NumberOfMB = number_mb; + } + void WithPictureId(uint8_t picture_id) { + assert(picture_id <= 0x3f); + sli_item_.PictureId = picture_id; + } + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + size_t BlockLength() const { + const size_t kFciLength = 4; + return kCommonFbFmtLength + kFciLength; + } + + RTCPUtility::RTCPPacketPSFBSLI sli_; + RTCPUtility::RTCPPacketPSFBSLIItem sli_item_; + + DISALLOW_COPY_AND_ASSIGN(Sli); +}; + +// Generic NACK (RFC 4585). +// +// FCI: +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | PID | BLP | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Nack : public RtcpPacket { + public: + Nack() : RtcpPacket() { + memset(&nack_, 0, sizeof(nack_)); + } + + virtual ~Nack() {} + + void From(uint32_t ssrc) { + nack_.SenderSSRC = ssrc; + } + void To(uint32_t ssrc) { + nack_.MediaSSRC = ssrc; + } + void WithList(const uint16_t* nack_list, int length); + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + size_t BlockLength() const { + size_t fci_length = 4 * nack_fields_.size(); + return kCommonFbFmtLength + fci_length; + } + + RTCPUtility::RTCPPacketRTPFBNACK nack_; + std::vector nack_fields_; + + DISALLOW_COPY_AND_ASSIGN(Nack); +}; + +// Reference picture selection indication (RPSI) (RFC 4585). +// +// FCI: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | PB |0| Payload Type| Native RPSI bit string | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | defined per codec ... | Padding (0) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Rpsi : public RtcpPacket { + public: + Rpsi() + : RtcpPacket(), + padding_bytes_(0) { + memset(&rpsi_, 0, sizeof(rpsi_)); + } + + virtual ~Rpsi() {} + + void From(uint32_t ssrc) { + rpsi_.SenderSSRC = ssrc; + } + void To(uint32_t ssrc) { + rpsi_.MediaSSRC = ssrc; + } + void WithPayloadType(uint8_t payload) { + assert(payload <= 0x7f); + rpsi_.PayloadType = payload; + } + void WithPictureId(uint64_t picture_id); + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + size_t BlockLength() const { + size_t fci_length = 2 + (rpsi_.NumberOfValidBits / 8) + padding_bytes_; + return kCommonFbFmtLength + fci_length; + } + + uint8_t padding_bytes_; + RTCPUtility::RTCPPacketPSFBRPSI rpsi_; + + DISALLOW_COPY_AND_ASSIGN(Rpsi); +}; + +// Full intra request (FIR) (RFC 5104). +// +// FCI: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Seq nr. | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Fir : public RtcpPacket { + public: + Fir() : RtcpPacket() { + memset(&fir_, 0, sizeof(fir_)); + memset(&fir_item_, 0, sizeof(fir_item_)); + } + + virtual ~Fir() {} + + void From(uint32_t ssrc) { + fir_.SenderSSRC = ssrc; + } + void To(uint32_t ssrc) { + fir_item_.SSRC = ssrc; + } + void WithCommandSeqNum(uint8_t seq_num) { + fir_item_.CommandSequenceNumber = seq_num; + } + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + size_t BlockLength() const { + const size_t kFciLength = 8; + return kCommonFbFmtLength + kFciLength; + } + + RTCPUtility::RTCPPacketPSFBFIR fir_; + RTCPUtility::RTCPPacketPSFBFIRItem fir_item_; +}; + +// Temporary Maximum Media Stream Bit Rate Request (TMMBR) (RFC 5104). +// +// FCI: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | MxTBR Exp | MxTBR Mantissa |Measured Overhead| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Tmmbr : public RtcpPacket { + public: + Tmmbr() : RtcpPacket() { + memset(&tmmbr_, 0, sizeof(tmmbr_)); + memset(&tmmbr_item_, 0, sizeof(tmmbr_item_)); + } + + virtual ~Tmmbr() {} + + void From(uint32_t ssrc) { + tmmbr_.SenderSSRC = ssrc; + } + void To(uint32_t ssrc) { + tmmbr_item_.SSRC = ssrc; + } + void WithBitrateKbps(uint32_t bitrate_kbps) { + tmmbr_item_.MaxTotalMediaBitRate = bitrate_kbps; + } + void WithOverhead(uint16_t overhead) { + assert(overhead <= 0x1ff); + tmmbr_item_.MeasuredOverhead = overhead; + } + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + size_t BlockLength() const { + const size_t kFciLen = 8; + return kCommonFbFmtLength + kFciLen; + } + + RTCPUtility::RTCPPacketRTPFBTMMBR tmmbr_; + RTCPUtility::RTCPPacketRTPFBTMMBRItem tmmbr_item_; + + DISALLOW_COPY_AND_ASSIGN(Tmmbr); +}; + +// Temporary Maximum Media Stream Bit Rate Notification (TMMBN) (RFC 5104). +// +// FCI: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | MxTBR Exp | MxTBR Mantissa |Measured Overhead| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Tmmbn : public RtcpPacket { + public: + Tmmbn() : RtcpPacket() { + memset(&tmmbn_, 0, sizeof(tmmbn_)); + } + + virtual ~Tmmbn() {} + + void From(uint32_t ssrc) { + tmmbn_.SenderSSRC = ssrc; + } + void WithTmmbr(uint32_t ssrc, uint32_t bitrate_kbps, uint16_t overhead); + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + enum { kMaxNumberOfTmmbrs = 50 }; + + size_t BlockLength() const { + const size_t kFciLen = 8; + return kCommonFbFmtLength + kFciLen * tmmbn_items_.size(); + } + + RTCPUtility::RTCPPacketRTPFBTMMBN tmmbn_; + std::vector tmmbn_items_; + + DISALLOW_COPY_AND_ASSIGN(Tmmbn); +}; + +// Receiver Estimated Max Bitrate (REMB) (draft-alvestrand-rmcat-remb). +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| FMT=15 | PT=206 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of packet sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of media source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Unique identifier 'R' 'E' 'M' 'B' | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Num SSRC | BR Exp | BR Mantissa | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC feedback | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | ... + +class Remb : public RtcpPacket { + public: + Remb() : RtcpPacket() { + memset(&remb_, 0, sizeof(remb_)); + memset(&remb_item_, 0, sizeof(remb_item_)); + } + + virtual ~Remb() {} + + void From(uint32_t ssrc) { + remb_.SenderSSRC = ssrc; + } + void AppliesTo(uint32_t ssrc); + + void WithBitrateBps(uint32_t bitrate_bps) { + remb_item_.BitRate = bitrate_bps; + } + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + enum { kMaxNumberOfSsrcs = 0xff }; + + size_t BlockLength() const { + return (remb_item_.NumberOfSSRCs + 5) * 4; + } + + RTCPUtility::RTCPPacketPSFBAPP remb_; + RTCPUtility::RTCPPacketPSFBREMBItem remb_item_; + + DISALLOW_COPY_AND_ASSIGN(Remb); +}; + +// From RFC 3611: RTP Control Protocol Extended Reports (RTCP XR). +// +// Format for XR packets: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P|reserved | PT=XR=207 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : report blocks : +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Xr : public RtcpPacket { + public: + typedef std::vector DlrrBlock; + Xr() : RtcpPacket() { + memset(&xr_header_, 0, sizeof(xr_header_)); + } + + virtual ~Xr() {} + + void From(uint32_t ssrc) { + xr_header_.OriginatorSSRC = ssrc; + } + void WithRrtr(Rrtr* rrtr); + void WithDlrr(Dlrr* dlrr); + void WithVoipMetric(VoipMetric* voip_metric); + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + enum { kMaxNumberOfRrtrBlocks = 50 }; + enum { kMaxNumberOfDlrrBlocks = 50 }; + enum { kMaxNumberOfVoipMetricBlocks = 50 }; + + size_t BlockLength() const { + const size_t kXrHeaderLength = 8; + return kXrHeaderLength + RrtrLength() + DlrrLength() + VoipMetricLength(); + } + + size_t RrtrLength() const { + const size_t kRrtrBlockLength = 12; + return kRrtrBlockLength * rrtr_blocks_.size(); + } + + size_t DlrrLength() const; + + size_t VoipMetricLength() const { + const size_t kVoipMetricBlockLength = 36; + return kVoipMetricBlockLength * voip_metric_blocks_.size(); + } + + RTCPUtility::RTCPPacketXR xr_header_; + std::vector rrtr_blocks_; + std::vector dlrr_blocks_; + std::vector voip_metric_blocks_; + + DISALLOW_COPY_AND_ASSIGN(Xr); +}; + +// Receiver Reference Time Report Block (RFC 3611). +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | BT=4 | reserved | block length = 2 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NTP timestamp, most significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NTP timestamp, least significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Rrtr { + public: + Rrtr() { + memset(&rrtr_block_, 0, sizeof(rrtr_block_)); + } + ~Rrtr() {} + + void WithNtpSec(uint32_t sec) { + rrtr_block_.NTPMostSignificant = sec; + } + void WithNtpFrac(uint32_t frac) { + rrtr_block_.NTPLeastSignificant = frac; + } + + private: + friend class Xr; + RTCPUtility::RTCPPacketXRReceiverReferenceTimeItem rrtr_block_; + + DISALLOW_COPY_AND_ASSIGN(Rrtr); +}; + +// DLRR Report Block (RFC 3611). +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | BT=5 | reserved | block length | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | SSRC_1 (SSRC of first receiver) | sub- +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block +// | last RR (LRR) | 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | delay since last RR (DLRR) | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | SSRC_2 (SSRC of second receiver) | sub- +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block +// : ... : 2 + +class Dlrr { + public: + Dlrr() {} + ~Dlrr() {} + + void WithDlrrItem(uint32_t ssrc, uint32_t last_rr, uint32_t delay_last_rr); + + private: + friend class Xr; + enum { kMaxNumberOfDlrrItems = 100 }; + + std::vector dlrr_block_; + + DISALLOW_COPY_AND_ASSIGN(Dlrr); +}; + +// VoIP Metrics Report Block (RFC 3611). +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | BT=7 | reserved | block length = 8 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | loss rate | discard rate | burst density | gap density | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | burst duration | gap duration | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | round trip delay | end system delay | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | signal level | noise level | RERL | Gmin | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | R factor | ext. R factor | MOS-LQ | MOS-CQ | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | RX config | reserved | JB nominal | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | JB maximum | JB abs max | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class VoipMetric { + public: + VoipMetric() { + memset(&metric_, 0, sizeof(metric_)); + } + ~VoipMetric() {} + + void To(uint32_t ssrc) { metric_.SSRC = ssrc; } + void LossRate(uint8_t loss_rate) { metric_.lossRate = loss_rate; } + void DiscardRate(uint8_t discard_rate) { metric_.discardRate = discard_rate; } + void BurstDensity(uint8_t burst_density) { + metric_.burstDensity = burst_density; + } + void GapDensity(uint8_t gap_density) { metric_.gapDensity = gap_density; } + void BurstDuration(uint16_t burst_duration) { + metric_.burstDuration = burst_duration; + } + void GapDuration(uint16_t gap_duration) { + metric_.gapDuration = gap_duration; + } + void RoundTripDelay(uint16_t round_trip_delay) { + metric_.roundTripDelay = round_trip_delay; + } + void EndSystemDelay(uint16_t end_system_delay) { + metric_.endSystemDelay = end_system_delay; + } + void SignalLevel(uint8_t signal_level) { metric_.signalLevel = signal_level; } + void NoiseLevel(uint8_t noise_level) { metric_.noiseLevel = noise_level; } + void Rerl(uint8_t rerl) { metric_.RERL = rerl; } + void Gmin(uint8_t gmin) { metric_.Gmin = gmin; } + void Rfactor(uint8_t rfactor) { metric_.Rfactor = rfactor; } + void ExtRfactor(uint8_t extrfactor) { metric_.extRfactor = extrfactor; } + void MosLq(uint8_t moslq) { metric_.MOSLQ = moslq; } + void MosCq(uint8_t moscq) { metric_.MOSCQ = moscq; } + void RxConfig(uint8_t rxconfig) { metric_.RXconfig = rxconfig; } + void JbNominal(uint16_t jbnominal) { metric_.JBnominal = jbnominal; } + void JbMax(uint16_t jbmax) { metric_.JBmax = jbmax; } + void JbAbsMax(uint16_t jbabsmax) { metric_.JBabsMax = jbabsmax; } + + private: + friend class Xr; + RTCPUtility::RTCPPacketXRVOIPMetricItem metric_; + + DISALLOW_COPY_AND_ASSIGN(VoipMetric); +}; + +// Class holding a RTCP packet. +// +// Takes a built rtcp packet. +// RawPacket raw_packet(buffer, length); +// +// To access the raw packet: +// raw_packet.buffer(); - pointer to the raw packet +// raw_packet.buffer_length(); - the length of the raw packet + +class RawPacket { + public: + RawPacket(const uint8_t* packet, size_t length) { + assert(length <= IP_PACKET_SIZE); + memcpy(buffer_, packet, length); + buffer_length_ = length; + } + + const uint8_t* buffer() { + return buffer_; + } + size_t buffer_length() const { + return buffer_length_; + } + + private: + size_t buffer_length_; + uint8_t buffer_[IP_PACKET_SIZE]; +}; + +} // namespace rtcp +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_RTCP_PACKET_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,903 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + * This file includes unit tests for the RtcpPacket. + */ + +#include "testing/gtest/include/gtest/gtest.h" + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h" +#include "webrtc/test/rtcp_packet_parser.h" + +using webrtc::rtcp::App; +using webrtc::rtcp::Bye; +using webrtc::rtcp::Dlrr; +using webrtc::rtcp::Empty; +using webrtc::rtcp::Fir; +using webrtc::rtcp::Ij; +using webrtc::rtcp::Nack; +using webrtc::rtcp::Pli; +using webrtc::rtcp::Sdes; +using webrtc::rtcp::SenderReport; +using webrtc::rtcp::Sli; +using webrtc::rtcp::RawPacket; +using webrtc::rtcp::ReceiverReport; +using webrtc::rtcp::Remb; +using webrtc::rtcp::ReportBlock; +using webrtc::rtcp::Rpsi; +using webrtc::rtcp::Rrtr; +using webrtc::rtcp::SenderReport; +using webrtc::rtcp::Tmmbn; +using webrtc::rtcp::Tmmbr; +using webrtc::rtcp::VoipMetric; +using webrtc::rtcp::Xr; +using webrtc::test::RtcpPacketParser; + +namespace webrtc { + +const uint32_t kSenderSsrc = 0x12345678; +const uint32_t kRemoteSsrc = 0x23456789; + +TEST(RtcpPacketTest, Rr) { + ReceiverReport rr; + rr.From(kSenderSsrc); + + RawPacket packet = rr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.receiver_report()->Ssrc()); + EXPECT_EQ(0, parser.report_block()->num_packets()); +} + +TEST(RtcpPacketTest, RrWithOneReportBlock) { + ReportBlock rb; + rb.To(kRemoteSsrc); + rb.WithFractionLost(55); + rb.WithCumulativeLost(0x111111); + rb.WithExtHighestSeqNum(0x22222222); + rb.WithJitter(0x33333333); + rb.WithLastSr(0x44444444); + rb.WithDelayLastSr(0x55555555); + + ReceiverReport rr; + rr.From(kSenderSsrc); + rr.WithReportBlock(&rb); + + RawPacket packet = rr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.receiver_report()->Ssrc()); + EXPECT_EQ(1, parser.report_block()->num_packets()); + EXPECT_EQ(kRemoteSsrc, parser.report_block()->Ssrc()); + EXPECT_EQ(55U, parser.report_block()->FractionLost()); + EXPECT_EQ(0x111111U, parser.report_block()->CumPacketLost()); + EXPECT_EQ(0x22222222U, parser.report_block()->ExtHighestSeqNum()); + EXPECT_EQ(0x33333333U, parser.report_block()->Jitter()); + EXPECT_EQ(0x44444444U, parser.report_block()->LastSr()); + EXPECT_EQ(0x55555555U, parser.report_block()->DelayLastSr()); +} + +TEST(RtcpPacketTest, RrWithTwoReportBlocks) { + ReportBlock rb1; + rb1.To(kRemoteSsrc); + ReportBlock rb2; + rb2.To(kRemoteSsrc + 1); + + ReceiverReport rr; + rr.From(kSenderSsrc); + rr.WithReportBlock(&rb1); + rr.WithReportBlock(&rb2); + + RawPacket packet = rr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.receiver_report()->Ssrc()); + EXPECT_EQ(2, parser.report_block()->num_packets()); + EXPECT_EQ(1, parser.report_blocks_per_ssrc(kRemoteSsrc)); + EXPECT_EQ(1, parser.report_blocks_per_ssrc(kRemoteSsrc + 1)); +} + +TEST(RtcpPacketTest, Sr) { + SenderReport sr; + sr.From(kSenderSsrc); + sr.WithNtpSec(0x11111111); + sr.WithNtpFrac(0x22222222); + sr.WithRtpTimestamp(0x33333333); + sr.WithPacketCount(0x44444444); + sr.WithOctetCount(0x55555555); + + RawPacket packet = sr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + + EXPECT_EQ(1, parser.sender_report()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.sender_report()->Ssrc()); + EXPECT_EQ(0x11111111U, parser.sender_report()->NtpSec()); + EXPECT_EQ(0x22222222U, parser.sender_report()->NtpFrac()); + EXPECT_EQ(0x33333333U, parser.sender_report()->RtpTimestamp()); + EXPECT_EQ(0x44444444U, parser.sender_report()->PacketCount()); + EXPECT_EQ(0x55555555U, parser.sender_report()->OctetCount()); + EXPECT_EQ(0, parser.report_block()->num_packets()); +} + +TEST(RtcpPacketTest, SrWithOneReportBlock) { + ReportBlock rb; + rb.To(kRemoteSsrc); + + SenderReport sr; + sr.From(kSenderSsrc); + sr.WithReportBlock(&rb); + + RawPacket packet = sr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.sender_report()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.sender_report()->Ssrc()); + EXPECT_EQ(1, parser.report_block()->num_packets()); + EXPECT_EQ(kRemoteSsrc, parser.report_block()->Ssrc()); +} + +TEST(RtcpPacketTest, SrWithTwoReportBlocks) { + ReportBlock rb1; + rb1.To(kRemoteSsrc); + ReportBlock rb2; + rb2.To(kRemoteSsrc + 1); + + SenderReport sr; + sr.From(kSenderSsrc); + sr.WithReportBlock(&rb1); + sr.WithReportBlock(&rb2); + + RawPacket packet = sr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.sender_report()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.sender_report()->Ssrc()); + EXPECT_EQ(2, parser.report_block()->num_packets()); + EXPECT_EQ(1, parser.report_blocks_per_ssrc(kRemoteSsrc)); + EXPECT_EQ(1, parser.report_blocks_per_ssrc(kRemoteSsrc + 1)); +} + +TEST(RtcpPacketTest, IjNoItem) { + Ij ij; + + RawPacket packet = ij.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.ij()->num_packets()); + EXPECT_EQ(0, parser.ij_item()->num_packets()); +} + +TEST(RtcpPacketTest, IjOneItem) { + Ij ij; + ij.WithJitterItem(0x11111111); + + RawPacket packet = ij.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.ij()->num_packets()); + EXPECT_EQ(1, parser.ij_item()->num_packets()); + EXPECT_EQ(0x11111111U, parser.ij_item()->Jitter()); +} + +TEST(RtcpPacketTest, IjTwoItems) { + Ij ij; + ij.WithJitterItem(0x11111111); + ij.WithJitterItem(0x22222222); + + RawPacket packet = ij.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.ij()->num_packets()); + EXPECT_EQ(2, parser.ij_item()->num_packets()); + EXPECT_EQ(0x22222222U, parser.ij_item()->Jitter()); +} + +TEST(RtcpPacketTest, AppWithNoData) { + App app; + app.WithSubType(30); + uint32_t name = 'n' << 24; + name += 'a' << 16; + name += 'm' << 8; + name += 'e'; + app.WithName(name); + + RawPacket packet = app.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.app()->num_packets()); + EXPECT_EQ(30U, parser.app()->SubType()); + EXPECT_EQ(name, parser.app()->Name()); + EXPECT_EQ(0, parser.app_item()->num_packets()); +} + +TEST(RtcpPacketTest, App) { + App app; + app.From(kSenderSsrc); + app.WithSubType(30); + uint32_t name = 'n' << 24; + name += 'a' << 16; + name += 'm' << 8; + name += 'e'; + app.WithName(name); + const char kData[] = {'t', 'e', 's', 't', 'd', 'a', 't', 'a'}; + const size_t kDataLength = sizeof(kData) / sizeof(kData[0]); + app.WithData((const uint8_t*)kData, kDataLength); + + RawPacket packet = app.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.app()->num_packets()); + EXPECT_EQ(30U, parser.app()->SubType()); + EXPECT_EQ(name, parser.app()->Name()); + EXPECT_EQ(1, parser.app_item()->num_packets()); + EXPECT_EQ(kDataLength, parser.app_item()->DataLength()); + EXPECT_EQ(0, strncmp(kData, (const char*)parser.app_item()->Data(), + parser.app_item()->DataLength())); +} + +TEST(RtcpPacketTest, SdesWithOneChunk) { + Sdes sdes; + sdes.WithCName(kSenderSsrc, "alice@host"); + + RawPacket packet = sdes.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.sdes()->num_packets()); + EXPECT_EQ(1, parser.sdes_chunk()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.sdes_chunk()->Ssrc()); + EXPECT_EQ("alice@host", parser.sdes_chunk()->Cname()); +} + +TEST(RtcpPacketTest, SdesWithMultipleChunks) { + Sdes sdes; + sdes.WithCName(kSenderSsrc, "a"); + sdes.WithCName(kSenderSsrc + 1, "ab"); + sdes.WithCName(kSenderSsrc + 2, "abc"); + sdes.WithCName(kSenderSsrc + 3, "abcd"); + sdes.WithCName(kSenderSsrc + 4, "abcde"); + sdes.WithCName(kSenderSsrc + 5, "abcdef"); + + RawPacket packet = sdes.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.sdes()->num_packets()); + EXPECT_EQ(6, parser.sdes_chunk()->num_packets()); + EXPECT_EQ(kSenderSsrc + 5, parser.sdes_chunk()->Ssrc()); + EXPECT_EQ("abcdef", parser.sdes_chunk()->Cname()); +} + +TEST(RtcpPacketTest, CnameItemWithEmptyString) { + Sdes sdes; + sdes.WithCName(kSenderSsrc, ""); + + RawPacket packet = sdes.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.sdes()->num_packets()); + EXPECT_EQ(1, parser.sdes_chunk()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.sdes_chunk()->Ssrc()); + EXPECT_EQ("", parser.sdes_chunk()->Cname()); +} + +TEST(RtcpPacketTest, Pli) { + Pli pli; + pli.From(kSenderSsrc); + pli.To(kRemoteSsrc); + + RawPacket packet = pli.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.pli()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.pli()->Ssrc()); + EXPECT_EQ(kRemoteSsrc, parser.pli()->MediaSsrc()); +} + +TEST(RtcpPacketTest, Sli) { + const uint16_t kFirstMb = 7777; + const uint16_t kNumberOfMb = 6666; + const uint8_t kPictureId = 60; + Sli sli; + sli.From(kSenderSsrc); + sli.To(kRemoteSsrc); + sli.WithFirstMb(kFirstMb); + sli.WithNumberOfMb(kNumberOfMb); + sli.WithPictureId(kPictureId); + + RawPacket packet = sli.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.sli()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.sli()->Ssrc()); + EXPECT_EQ(kRemoteSsrc, parser.sli()->MediaSsrc()); + EXPECT_EQ(1, parser.sli_item()->num_packets()); + EXPECT_EQ(kFirstMb, parser.sli_item()->FirstMb()); + EXPECT_EQ(kNumberOfMb, parser.sli_item()->NumberOfMb()); + EXPECT_EQ(kPictureId, parser.sli_item()->PictureId()); +} + +TEST(RtcpPacketTest, Nack) { + Nack nack; + const uint16_t kList[] = {0, 1, 3, 8, 16}; + const uint16_t kListLength = sizeof(kList) / sizeof(kList[0]); + nack.From(kSenderSsrc); + nack.To(kRemoteSsrc); + nack.WithList(kList, kListLength); + RawPacket packet = nack.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.nack()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.nack()->Ssrc()); + EXPECT_EQ(kRemoteSsrc, parser.nack()->MediaSsrc()); + EXPECT_EQ(1, parser.nack_item()->num_packets()); + std::vector seqs = parser.nack_item()->last_nack_list(); + EXPECT_EQ(kListLength, seqs.size()); + for (size_t i = 0; i < kListLength; ++i) { + EXPECT_EQ(kList[i], seqs[i]); + } +} + +TEST(RtcpPacketTest, NackWithWrap) { + Nack nack; + const uint16_t kList[] = {65500, 65516, 65534, 65535, 0, 1, 3, 20, 100}; + const uint16_t kListLength = sizeof(kList) / sizeof(kList[0]); + nack.From(kSenderSsrc); + nack.To(kRemoteSsrc); + nack.WithList(kList, kListLength); + RawPacket packet = nack.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.nack()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.nack()->Ssrc()); + EXPECT_EQ(kRemoteSsrc, parser.nack()->MediaSsrc()); + EXPECT_EQ(4, parser.nack_item()->num_packets()); + std::vector seqs = parser.nack_item()->last_nack_list(); + EXPECT_EQ(kListLength, seqs.size()); + for (size_t i = 0; i < kListLength; ++i) { + EXPECT_EQ(kList[i], seqs[i]); + } +} + +TEST(RtcpPacketTest, Rpsi) { + Rpsi rpsi; + // 1000001 (7 bits = 1 byte in native string). + const uint64_t kPictureId = 0x41; + const uint16_t kNumberOfValidBytes = 1; + rpsi.WithPayloadType(100); + rpsi.WithPictureId(kPictureId); + + RawPacket packet = rpsi.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(100, parser.rpsi()->PayloadType()); + EXPECT_EQ(kNumberOfValidBytes * 8, parser.rpsi()->NumberOfValidBits()); + EXPECT_EQ(kPictureId, parser.rpsi()->PictureId()); +} + +TEST(RtcpPacketTest, RpsiWithTwoByteNativeString) { + Rpsi rpsi; + // |1 0000001 (7 bits = 1 byte in native string). + const uint64_t kPictureId = 0x81; + const uint16_t kNumberOfValidBytes = 2; + rpsi.WithPictureId(kPictureId); + + RawPacket packet = rpsi.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(kNumberOfValidBytes * 8, parser.rpsi()->NumberOfValidBits()); + EXPECT_EQ(kPictureId, parser.rpsi()->PictureId()); +} + +TEST(RtcpPacketTest, RpsiWithThreeByteNativeString) { + Rpsi rpsi; + // 10000|00 100000|0 1000000 (7 bits = 1 byte in native string). + const uint64_t kPictureId = 0x102040; + const uint16_t kNumberOfValidBytes = 3; + rpsi.WithPictureId(kPictureId); + + RawPacket packet = rpsi.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(kNumberOfValidBytes * 8, parser.rpsi()->NumberOfValidBits()); + EXPECT_EQ(kPictureId, parser.rpsi()->PictureId()); +} + +TEST(RtcpPacketTest, RpsiWithFourByteNativeString) { + Rpsi rpsi; + // 1000|001 00001|01 100001|1 1000010 (7 bits = 1 byte in native string). + const uint64_t kPictureId = 0x84161C2; + const uint16_t kNumberOfValidBytes = 4; + rpsi.WithPictureId(kPictureId); + + RawPacket packet = rpsi.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(kNumberOfValidBytes * 8, parser.rpsi()->NumberOfValidBits()); + EXPECT_EQ(kPictureId, parser.rpsi()->PictureId()); +} + +TEST(RtcpPacketTest, RpsiWithMaxPictureId) { + Rpsi rpsi; + // 1 1111111| 1111111 1|111111 11|11111 111|1111 1111|111 11111| + // 11 111111|1 1111111 (7 bits = 1 byte in native string). + const uint64_t kPictureId = 0xffffffffffffffff; + const uint16_t kNumberOfValidBytes = 10; + rpsi.WithPictureId(kPictureId); + + RawPacket packet = rpsi.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(kNumberOfValidBytes * 8, parser.rpsi()->NumberOfValidBits()); + EXPECT_EQ(kPictureId, parser.rpsi()->PictureId()); +} + +TEST(RtcpPacketTest, Fir) { + Fir fir; + fir.From(kSenderSsrc); + fir.To(kRemoteSsrc); + fir.WithCommandSeqNum(123); + + RawPacket packet = fir.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.fir()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.fir()->Ssrc()); + EXPECT_EQ(1, parser.fir_item()->num_packets()); + EXPECT_EQ(kRemoteSsrc, parser.fir_item()->Ssrc()); + EXPECT_EQ(123U, parser.fir_item()->SeqNum()); +} + +TEST(RtcpPacketTest, AppendPacket) { + Fir fir; + ReportBlock rb; + ReceiverReport rr; + rr.From(kSenderSsrc); + rr.WithReportBlock(&rb); + rr.Append(&fir); + + RawPacket packet = rr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.receiver_report()->Ssrc()); + EXPECT_EQ(1, parser.report_block()->num_packets()); + EXPECT_EQ(1, parser.fir()->num_packets()); +} + +TEST(RtcpPacketTest, AppendPacketOnEmpty) { + Empty empty; + ReceiverReport rr; + rr.From(kSenderSsrc); + empty.Append(&rr); + + RawPacket packet = empty.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(0, parser.report_block()->num_packets()); +} + +TEST(RtcpPacketTest, AppendPacketWithOwnAppendedPacket) { + Fir fir; + Bye bye; + ReportBlock rb; + + ReceiverReport rr; + rr.WithReportBlock(&rb); + rr.Append(&fir); + + SenderReport sr; + sr.Append(&bye); + sr.Append(&rr); + + RawPacket packet = sr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.sender_report()->num_packets()); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(1, parser.report_block()->num_packets()); + EXPECT_EQ(1, parser.bye()->num_packets()); + EXPECT_EQ(1, parser.fir()->num_packets()); +} + +TEST(RtcpPacketTest, Bye) { + Bye bye; + bye.From(kSenderSsrc); + + RawPacket packet = bye.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.bye()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.bye()->Ssrc()); +} + +TEST(RtcpPacketTest, ByeWithCsrcs) { + Fir fir; + Bye bye; + bye.From(kSenderSsrc); + bye.WithCsrc(0x22222222); + bye.WithCsrc(0x33333333); + bye.Append(&fir); + + RawPacket packet = bye.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.bye()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.bye()->Ssrc()); + EXPECT_EQ(1, parser.fir()->num_packets()); +} + +TEST(RtcpPacketTest, BuildWithInputBuffer) { + Fir fir; + ReportBlock rb; + ReceiverReport rr; + rr.From(kSenderSsrc); + rr.WithReportBlock(&rb); + rr.Append(&fir); + + const size_t kRrLength = 8; + const size_t kReportBlockLength = 24; + const size_t kFirLength = 20; + + size_t len = 0; + uint8_t packet[kRrLength + kReportBlockLength + kFirLength]; + rr.Build(packet, &len, kRrLength + kReportBlockLength + kFirLength); + + RtcpPacketParser parser; + parser.Parse(packet, len); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(1, parser.report_block()->num_packets()); + EXPECT_EQ(1, parser.fir()->num_packets()); +} + +TEST(RtcpPacketTest, BuildWithTooSmallBuffer) { + ReportBlock rb; + ReceiverReport rr; + rr.From(kSenderSsrc); + rr.WithReportBlock(&rb); + + const size_t kRrLength = 8; + const size_t kReportBlockLength = 24; + + // No packet. + size_t len = 0; + uint8_t packet[kRrLength + kReportBlockLength - 1]; + rr.Build(packet, &len, kRrLength + kReportBlockLength - 1); + RtcpPacketParser parser; + parser.Parse(packet, len); + EXPECT_EQ(0U, len); +} + +TEST(RtcpPacketTest, BuildWithTooSmallBuffer_LastBlockFits) { + Fir fir; + ReportBlock rb; + ReceiverReport rr; + rr.From(kSenderSsrc); + rr.WithReportBlock(&rb); + rr.Append(&fir); + + const size_t kRrLength = 8; + const size_t kReportBlockLength = 24; + + size_t len = 0; + uint8_t packet[kRrLength + kReportBlockLength - 1]; + rr.Build(packet, &len, kRrLength + kReportBlockLength - 1); + RtcpPacketParser parser; + parser.Parse(packet, len); + EXPECT_EQ(0, parser.receiver_report()->num_packets()); + EXPECT_EQ(0, parser.report_block()->num_packets()); + EXPECT_EQ(1, parser.fir()->num_packets()); +} + +TEST(RtcpPacketTest, Remb) { + Remb remb; + remb.From(kSenderSsrc); + remb.AppliesTo(kRemoteSsrc); + remb.AppliesTo(kRemoteSsrc + 1); + remb.AppliesTo(kRemoteSsrc + 2); + remb.WithBitrateBps(261011); + + RawPacket packet = remb.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.psfb_app()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.psfb_app()->Ssrc()); + EXPECT_EQ(1, parser.remb_item()->num_packets()); + EXPECT_EQ(261011, parser.remb_item()->last_bitrate_bps()); + std::vector ssrcs = parser.remb_item()->last_ssrc_list(); + EXPECT_EQ(kRemoteSsrc, ssrcs[0]); + EXPECT_EQ(kRemoteSsrc + 1, ssrcs[1]); + EXPECT_EQ(kRemoteSsrc + 2, ssrcs[2]); +} + +TEST(RtcpPacketTest, Tmmbr) { + Tmmbr tmmbr; + tmmbr.From(kSenderSsrc); + tmmbr.To(kRemoteSsrc); + tmmbr.WithBitrateKbps(312); + tmmbr.WithOverhead(60); + + RawPacket packet = tmmbr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.tmmbr()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.tmmbr()->Ssrc()); + EXPECT_EQ(1, parser.tmmbr_item()->num_packets()); + EXPECT_EQ(312U, parser.tmmbr_item()->BitrateKbps()); + EXPECT_EQ(60U, parser.tmmbr_item()->Overhead()); +} + +TEST(RtcpPacketTest, TmmbnWithNoItem) { + Tmmbn tmmbn; + tmmbn.From(kSenderSsrc); + + RawPacket packet = tmmbn.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.tmmbn()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.tmmbn()->Ssrc()); + EXPECT_EQ(0, parser.tmmbn_items()->num_packets()); +} + +TEST(RtcpPacketTest, TmmbnWithOneItem) { + Tmmbn tmmbn; + tmmbn.From(kSenderSsrc); + tmmbn.WithTmmbr(kRemoteSsrc, 312, 60); + + RawPacket packet = tmmbn.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.tmmbn()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.tmmbn()->Ssrc()); + EXPECT_EQ(1, parser.tmmbn_items()->num_packets()); + EXPECT_EQ(kRemoteSsrc, parser.tmmbn_items()->Ssrc(0)); + EXPECT_EQ(312U, parser.tmmbn_items()->BitrateKbps(0)); + EXPECT_EQ(60U, parser.tmmbn_items()->Overhead(0)); +} + +TEST(RtcpPacketTest, TmmbnWithTwoItems) { + Tmmbn tmmbn; + tmmbn.From(kSenderSsrc); + tmmbn.WithTmmbr(kRemoteSsrc, 312, 60); + tmmbn.WithTmmbr(kRemoteSsrc + 1, 1288, 40); + + RawPacket packet = tmmbn.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.tmmbn()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.tmmbn()->Ssrc()); + EXPECT_EQ(2, parser.tmmbn_items()->num_packets()); + EXPECT_EQ(kRemoteSsrc, parser.tmmbn_items()->Ssrc(0)); + EXPECT_EQ(312U, parser.tmmbn_items()->BitrateKbps(0)); + EXPECT_EQ(60U, parser.tmmbn_items()->Overhead(0)); + EXPECT_EQ(kRemoteSsrc + 1, parser.tmmbn_items()->Ssrc(1)); + EXPECT_EQ(1288U, parser.tmmbn_items()->BitrateKbps(1)); + EXPECT_EQ(40U, parser.tmmbn_items()->Overhead(1)); +} + +TEST(RtcpPacketTest, XrWithNoReportBlocks) { + Xr xr; + xr.From(kSenderSsrc); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); +} + +TEST(RtcpPacketTest, XrWithRrtr) { + Rrtr rrtr; + rrtr.WithNtpSec(0x11111111); + rrtr.WithNtpFrac(0x22222222); + Xr xr; + xr.From(kSenderSsrc); + xr.WithRrtr(&rrtr); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); + EXPECT_EQ(1, parser.rrtr()->num_packets()); + EXPECT_EQ(0x11111111U, parser.rrtr()->NtpSec()); + EXPECT_EQ(0x22222222U, parser.rrtr()->NtpFrac()); +} + +TEST(RtcpPacketTest, XrWithTwoRrtrBlocks) { + Rrtr rrtr1; + rrtr1.WithNtpSec(0x11111111); + rrtr1.WithNtpFrac(0x22222222); + Rrtr rrtr2; + rrtr2.WithNtpSec(0x33333333); + rrtr2.WithNtpFrac(0x44444444); + Xr xr; + xr.From(kSenderSsrc); + xr.WithRrtr(&rrtr1); + xr.WithRrtr(&rrtr2); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); + EXPECT_EQ(2, parser.rrtr()->num_packets()); + EXPECT_EQ(0x33333333U, parser.rrtr()->NtpSec()); + EXPECT_EQ(0x44444444U, parser.rrtr()->NtpFrac()); +} + +TEST(RtcpPacketTest, XrWithDlrrWithOneSubBlock) { + Dlrr dlrr; + dlrr.WithDlrrItem(0x11111111, 0x22222222, 0x33333333); + Xr xr; + xr.From(kSenderSsrc); + xr.WithDlrr(&dlrr); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); + EXPECT_EQ(1, parser.dlrr()->num_packets()); + EXPECT_EQ(1, parser.dlrr_items()->num_packets()); + EXPECT_EQ(0x11111111U, parser.dlrr_items()->Ssrc(0)); + EXPECT_EQ(0x22222222U, parser.dlrr_items()->LastRr(0)); + EXPECT_EQ(0x33333333U, parser.dlrr_items()->DelayLastRr(0)); +} + +TEST(RtcpPacketTest, XrWithDlrrWithTwoSubBlocks) { + Dlrr dlrr; + dlrr.WithDlrrItem(0x11111111, 0x22222222, 0x33333333); + dlrr.WithDlrrItem(0x44444444, 0x55555555, 0x66666666); + Xr xr; + xr.From(kSenderSsrc); + xr.WithDlrr(&dlrr); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); + EXPECT_EQ(1, parser.dlrr()->num_packets()); + EXPECT_EQ(2, parser.dlrr_items()->num_packets()); + EXPECT_EQ(0x11111111U, parser.dlrr_items()->Ssrc(0)); + EXPECT_EQ(0x22222222U, parser.dlrr_items()->LastRr(0)); + EXPECT_EQ(0x33333333U, parser.dlrr_items()->DelayLastRr(0)); + EXPECT_EQ(0x44444444U, parser.dlrr_items()->Ssrc(1)); + EXPECT_EQ(0x55555555U, parser.dlrr_items()->LastRr(1)); + EXPECT_EQ(0x66666666U, parser.dlrr_items()->DelayLastRr(1)); +} + +TEST(RtcpPacketTest, XrWithTwoDlrrBlocks) { + Dlrr dlrr1; + dlrr1.WithDlrrItem(0x11111111, 0x22222222, 0x33333333); + Dlrr dlrr2; + dlrr2.WithDlrrItem(0x44444444, 0x55555555, 0x66666666); + Xr xr; + xr.From(kSenderSsrc); + xr.WithDlrr(&dlrr1); + xr.WithDlrr(&dlrr2); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); + EXPECT_EQ(2, parser.dlrr()->num_packets()); + EXPECT_EQ(2, parser.dlrr_items()->num_packets()); + EXPECT_EQ(0x11111111U, parser.dlrr_items()->Ssrc(0)); + EXPECT_EQ(0x22222222U, parser.dlrr_items()->LastRr(0)); + EXPECT_EQ(0x33333333U, parser.dlrr_items()->DelayLastRr(0)); + EXPECT_EQ(0x44444444U, parser.dlrr_items()->Ssrc(1)); + EXPECT_EQ(0x55555555U, parser.dlrr_items()->LastRr(1)); + EXPECT_EQ(0x66666666U, parser.dlrr_items()->DelayLastRr(1)); +} + +TEST(RtcpPacketTest, XrWithVoipMetric) { + VoipMetric metric; + metric.To(kRemoteSsrc); + metric.LossRate(1); + metric.DiscardRate(2); + metric.BurstDensity(3); + metric.GapDensity(4); + metric.BurstDuration(0x1111); + metric.GapDuration(0x2222); + metric.RoundTripDelay(0x3333); + metric.EndSystemDelay(0x4444); + metric.SignalLevel(5); + metric.NoiseLevel(6); + metric.Rerl(7); + metric.Gmin(8); + metric.Rfactor(9); + metric.ExtRfactor(10); + metric.MosLq(11); + metric.MosCq(12); + metric.RxConfig(13); + metric.JbNominal(0x5555); + metric.JbMax(0x6666); + metric.JbAbsMax(0x7777); + + Xr xr; + xr.From(kSenderSsrc); + xr.WithVoipMetric(&metric); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); + EXPECT_EQ(1, parser.voip_metric()->num_packets()); + EXPECT_EQ(kRemoteSsrc, parser.voip_metric()->Ssrc()); + EXPECT_EQ(1, parser.voip_metric()->LossRate()); + EXPECT_EQ(2, parser.voip_metric()->DiscardRate()); + EXPECT_EQ(3, parser.voip_metric()->BurstDensity()); + EXPECT_EQ(4, parser.voip_metric()->GapDensity()); + EXPECT_EQ(0x1111, parser.voip_metric()->BurstDuration()); + EXPECT_EQ(0x2222, parser.voip_metric()->GapDuration()); + EXPECT_EQ(0x3333, parser.voip_metric()->RoundTripDelay()); + EXPECT_EQ(0x4444, parser.voip_metric()->EndSystemDelay()); + EXPECT_EQ(5, parser.voip_metric()->SignalLevel()); + EXPECT_EQ(6, parser.voip_metric()->NoiseLevel()); + EXPECT_EQ(7, parser.voip_metric()->Rerl()); + EXPECT_EQ(8, parser.voip_metric()->Gmin()); + EXPECT_EQ(9, parser.voip_metric()->Rfactor()); + EXPECT_EQ(10, parser.voip_metric()->ExtRfactor()); + EXPECT_EQ(11, parser.voip_metric()->MosLq()); + EXPECT_EQ(12, parser.voip_metric()->MosCq()); + EXPECT_EQ(13, parser.voip_metric()->RxConfig()); + EXPECT_EQ(0x5555, parser.voip_metric()->JbNominal()); + EXPECT_EQ(0x6666, parser.voip_metric()->JbMax()); + EXPECT_EQ(0x7777, parser.voip_metric()->JbAbsMax()); +} + +TEST(RtcpPacketTest, XrWithMultipleReportBlocks) { + Rrtr rrtr; + Dlrr dlrr; + dlrr.WithDlrrItem(1, 2, 3); + VoipMetric metric; + Xr xr; + xr.From(kSenderSsrc); + xr.WithRrtr(&rrtr); + xr.WithDlrr(&dlrr); + xr.WithVoipMetric(&metric); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); + EXPECT_EQ(1, parser.rrtr()->num_packets()); + EXPECT_EQ(1, parser.dlrr()->num_packets()); + EXPECT_EQ(1, parser.dlrr_items()->num_packets()); + EXPECT_EQ(1, parser.voip_metric()->num_packets()); +} + +TEST(RtcpPacketTest, DlrrWithoutItemNotIncludedInPacket) { + Rrtr rrtr; + Dlrr dlrr; + VoipMetric metric; + Xr xr; + xr.From(kSenderSsrc); + xr.WithRrtr(&rrtr); + xr.WithDlrr(&dlrr); + xr.WithVoipMetric(&metric); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); + EXPECT_EQ(1, parser.rrtr()->num_packets()); + EXPECT_EQ(0, parser.dlrr()->num_packets()); + EXPECT_EQ(1, parser.voip_metric()->num_packets()); +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc 2015-02-03 14:33:36.000000000 +0000 @@ -18,7 +18,7 @@ #include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" #include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/trace_event.h" namespace webrtc { @@ -57,7 +57,6 @@ _lastIncreasedSequenceNumberMs(0), stats_callback_(NULL) { memset(&_remoteSenderInfo, 0, sizeof(_remoteSenderInfo)); - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id, "%s created", __FUNCTION__); } RTCPReceiver::~RTCPReceiver() { @@ -82,8 +81,6 @@ delete first->second; _receivedCnameMap.erase(first); } - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, _id, - "%s deleted", __FUNCTION__); } void @@ -178,8 +175,7 @@ RTCPReportBlockInformation* reportBlock = GetReportBlockInformation(remoteSSRC); if (reportBlock == NULL) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "\tfailed to GetReportBlockInformation(%u)", remoteSSRC); + LOG(LS_WARNING) << "Failed to reset rtt for ssrc " << remoteSSRC; return -1; } reportBlock->RTT = 0; @@ -249,12 +245,12 @@ return 0; } -int32_t -RTCPReceiver::NTP(uint32_t *ReceivedNTPsecs, - uint32_t *ReceivedNTPfrac, - uint32_t *RTCPArrivalTimeSecs, - uint32_t *RTCPArrivalTimeFrac, - uint32_t *rtcp_timestamp) const +// TODO(pbos): Make this fail when we haven't received NTP. +bool RTCPReceiver::NTP(uint32_t* ReceivedNTPsecs, + uint32_t* ReceivedNTPfrac, + uint32_t* RTCPArrivalTimeSecs, + uint32_t* RTCPArrivalTimeFrac, + uint32_t* rtcp_timestamp) const { CriticalSectionScoped lock(_criticalSectionRTCPReceiver); if(ReceivedNTPsecs) @@ -276,7 +272,7 @@ if (rtcp_timestamp) { *rtcp_timestamp = _remoteSenderInfo.RTPtimeStamp; } - return 0; + return true; } bool RTCPReceiver::LastReceivedXrReferenceTimeInfo( @@ -303,22 +299,14 @@ return true; } -int32_t -RTCPReceiver::SenderInfoReceived(RTCPSenderInfo* senderInfo) const -{ - if(senderInfo == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - if(_lastReceivedSRNTPsecs == 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "%s No received SR", __FUNCTION__); - return -1; - } - memcpy(senderInfo, &(_remoteSenderInfo), sizeof(RTCPSenderInfo)); - return 0; +int32_t RTCPReceiver::SenderInfoReceived(RTCPSenderInfo* senderInfo) const { + assert(senderInfo); + CriticalSectionScoped lock(_criticalSectionRTCPReceiver); + if (_lastReceivedSRNTPsecs == 0) { + return -1; + } + memcpy(senderInfo, &(_remoteSenderInfo), sizeof(RTCPSenderInfo)); + return 0; } // statistics @@ -338,6 +326,12 @@ return 0; } +void RTCPReceiver::GetPacketTypeCounter( + RtcpPacketTypeCounter* packet_counter) const { + CriticalSectionScoped lock(_criticalSectionRTCPReceiver); + *packet_counter = packet_type_counter_; +} + int32_t RTCPReceiver::IncomingRTCPPacket(RTCPPacketInformation& rtcpPacketInformation, RTCPUtility::RTCPParserV2* rtcpParser) @@ -536,8 +530,8 @@ RTCPReportBlockInformation* reportBlock = CreateReportBlockInformation(remoteSSRC); if (reportBlock == NULL) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "\tfailed to CreateReportBlockInformation(%u)", remoteSSRC); + LOG(LS_WARNING) << "Failed to CreateReportBlockInformation(" + << remoteSSRC << ")"; return; } @@ -804,9 +798,6 @@ } RTCPReceiveInformation* receiveInfo = receiveInfoIt->second; if (receiveInfo == NULL) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s failed to get RTCPReceiveInformation", - __FUNCTION__); return -1; } if (receiveInfo->TmmbnBoundingSet.lengthOfSet() > 0) { @@ -870,6 +861,12 @@ HandleNACKItem(rtcpPacket, rtcpPacketInformation); pktType = rtcpParser.Iterate(); } + + if (rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpNack) { + ++packet_type_counter_.nack_packets; + packet_type_counter_.nack_requests = nack_stats_.requests(); + packet_type_counter_.unique_nack_requests = nack_stats_.unique_requests(); + } } // no need for critsect we have _criticalSectionRTCPReceiver @@ -878,6 +875,7 @@ RTCPPacketInformation& rtcpPacketInformation) { rtcpPacketInformation.AddNACKPacket(rtcpPacket.NACKItem.PacketID); + nack_stats_.ReportRequest(rtcpPacket.NACKItem.PacketID); uint16_t bitMask = rtcpPacket.NACKItem.BitMask; if(bitMask) @@ -887,6 +885,7 @@ if(bitMask & 0x01) { rtcpPacketInformation.AddNACKPacket(rtcpPacket.NACKItem.PacketID + i); + nack_stats_.ReportRequest(rtcpPacket.NACKItem.PacketID + i); } bitMask = bitMask >>1; } @@ -1059,6 +1058,7 @@ if (main_ssrc_ == rtcpPacket.PLI.MediaSSRC) { TRACE_EVENT_INSTANT0("webrtc_rtp", "PLI"); + ++packet_type_counter_.pli_packets; // Received a signal that we need to send a new key frame. rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpPli; } @@ -1301,6 +1301,9 @@ if (main_ssrc_ != rtcpPacket.FIRItem.SSRC) { return; } + + ++packet_type_counter_.fir_packets; + // rtcpPacket.FIR.MediaSSRC SHOULD be 0 but we ignore to check it // we don't know who this originate from if (receiveInfo) { @@ -1365,8 +1368,7 @@ TMMBRSet* boundingSet = NULL; numBoundingSet = FindTMMBRBoundingSet(boundingSet); if (numBoundingSet == -1) { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, - "Failed to find TMMBR bounding set."); + LOG(LS_WARNING) << "Failed to find TMMBR bounding set."; return -1; } // Set bounding set @@ -1386,8 +1388,6 @@ CriticalSectionScoped lock(_criticalSectionFeedbacks); if (_cbRtcpBandwidthObserver) { _cbRtcpBandwidthObserver->OnReceivedEstimatedBitrate(bitrate * 1000); - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, - "Set TMMBR request:%d kbps", bitrate); } } return 0; @@ -1396,8 +1396,6 @@ void RTCPReceiver::RegisterRtcpStatisticsCallback( RtcpStatisticsCallback* callback) { CriticalSectionScoped cs(_criticalSectionFeedbacks); - if (callback != NULL) - assert(stats_callback_ == NULL); stats_callback_ = callback; } @@ -1412,9 +1410,6 @@ // Process TMMBR and REMB first to avoid multiple callbacks // to OnNetworkChanged. if (rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpTmmbr) { - WEBRTC_TRACE(kTraceStateInfo, kTraceRtpRtcp, _id, - "SIG [RTCP] Incoming TMMBR to id:%d", _id); - // Might trigger a OnReceivedBandwidthEstimateUpdate. UpdateTMMBR(); } @@ -1429,9 +1424,8 @@ } if (rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpNack) { if (rtcpPacketInformation.nackSequenceNumbers.size() > 0) { - WEBRTC_TRACE(kTraceStateInfo, kTraceRtpRtcp, _id, - "SIG [RTCP] Incoming NACK length:%d", - rtcpPacketInformation.nackSequenceNumbers.size()); + LOG(LS_VERBOSE) << "Incoming NACK length: " + << rtcpPacketInformation.nackSequenceNumbers.size(); _rtpRtcp.OnReceivedNACK(rtcpPacketInformation.nackSequenceNumbers); } } @@ -1446,13 +1440,11 @@ if ((rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpPli) || (rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpFir)) { if (rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpPli) { - WEBRTC_TRACE(kTraceStateInfo, kTraceRtpRtcp, _id, - "SIG [RTCP] Incoming PLI from SSRC:0x%x", - rtcpPacketInformation.remoteSSRC); + LOG(LS_VERBOSE) << "Incoming PLI from SSRC " + << rtcpPacketInformation.remoteSSRC; } else { - WEBRTC_TRACE(kTraceStateInfo, kTraceRtpRtcp, _id, - "SIG [RTCP] Incoming FIR from SSRC:0x%x", - rtcpPacketInformation.remoteSSRC); + LOG(LS_VERBOSE) << "Incoming FIR from SSRC " + << rtcpPacketInformation.remoteSSRC; } _cbRtcpIntraFrameObserver->OnReceivedIntraFrameRequest(local_ssrc); } @@ -1467,9 +1459,8 @@ } if (_cbRtcpBandwidthObserver) { if (rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpRemb) { - WEBRTC_TRACE(kTraceStateInfo, kTraceRtpRtcp, _id, - "SIG [RTCP] Incoming REMB:%d", - rtcpPacketInformation.receiverEstimatedMaxBitrate); + LOG(LS_VERBOSE) << "Incoming REMB: " + << rtcpPacketInformation.receiverEstimatedMaxBitrate; _cbRtcpBandwidthObserver->OnReceivedEstimatedBitrate( rtcpPacketInformation.receiverEstimatedMaxBitrate); } @@ -1514,7 +1505,7 @@ stats.fraction_lost = it->fractionLost; stats.jitter = it->jitter; - stats_callback_->StatisticsUpdated(stats, local_ssrc); + stats_callback_->StatisticsUpdated(stats, it->sourceSSRC); } } } @@ -1565,9 +1556,6 @@ while (receiveInfoIt != _receivedInfoMap.end()) { RTCPReceiveInformation* receiveInfo = receiveInfoIt->second; if(receiveInfo == NULL) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s failed to get RTCPReceiveInformation", - __FUNCTION__); return -1; } num += receiveInfo->TmmbrSet.lengthOfSet(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver.h 2015-02-03 14:33:36.000000000 +0000 @@ -63,11 +63,11 @@ char cName[RTCP_CNAME_SIZE]) const; // get received NTP - int32_t NTP(uint32_t *ReceivedNTPsecs, - uint32_t *ReceivedNTPfrac, - uint32_t *RTCPArrivalTimeSecs, - uint32_t *RTCPArrivalTimeFrac, - uint32_t *rtcp_timestamp) const; + bool NTP(uint32_t* ReceivedNTPsecs, + uint32_t* ReceivedNTPfrac, + uint32_t* RTCPArrivalTimeSecs, + uint32_t* RTCPArrivalTimeFrac, + uint32_t* rtcp_timestamp) const; bool LastReceivedXrReferenceTimeInfo(RtcpReceiveTimeInfo* info) const; @@ -94,6 +94,8 @@ int32_t StatisticsReceived( std::vector* receiveBlocks) const; + void GetPacketTypeCounter(RtcpPacketTypeCounter* packet_counter) const; + // Returns true if we haven't received an RTCP RR for several RTCP // intervals, but only triggers true once. bool RtcpRrTimeout(int64_t rtcp_interval_ms); @@ -272,6 +274,10 @@ int64_t _lastIncreasedSequenceNumberMs; RtcpStatisticsCallback* stats_callback_; + + RtcpPacketTypeCounter packet_type_counter_; + + RTCPUtility::NackStats nack_stats_; }; } // namespace webrtc #endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_RECEIVER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver_help.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver_help.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver_help.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver_help.h 2015-02-03 14:33:36.000000000 +0000 @@ -12,10 +12,10 @@ #define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_RECEIVER_HELP_H_ +#include "webrtc/base/constructormagic.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" // RTCPReportBlock #include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" #include "webrtc/modules/rtp_rtcp/source/tmmbr_help.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/typedefs.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -19,6 +19,7 @@ #include "webrtc/common_types.h" #include "webrtc/modules/remote_bitrate_estimator/include/mock/mock_remote_bitrate_observer.h" #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_receiver.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_sender.h" #include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h" @@ -28,197 +29,6 @@ namespace { // Anonymous namespace; hide utility functions and classes. -// A very simple packet builder class for building RTCP packets. -class PacketBuilder { - public: - static const int kMaxPacketSize = 1024; - - struct ReportBlock { - ReportBlock(uint32_t ssrc, uint32_t extended_max, uint8_t fraction_loss, - uint32_t cumulative_loss, uint32_t jitter) - : ssrc(ssrc), - extended_max(extended_max), - fraction_loss(fraction_loss), - cumulative_loss(cumulative_loss), - jitter(jitter) {} - - uint32_t ssrc; - uint32_t extended_max; - uint8_t fraction_loss; - uint32_t cumulative_loss; - uint32_t jitter; - }; - - PacketBuilder() - : pos_(0), - pos_of_len_(0) { - } - - - void Add8(uint8_t byte) { - EXPECT_LT(pos_, kMaxPacketSize - 1); - buffer_[pos_] = byte; - ++pos_; - } - - void Add16(uint16_t word) { - Add8(word >> 8); - Add8(word & 0xFF); - } - - void Add32(uint32_t word) { - Add8(word >> 24); - Add8((word >> 16) & 0xFF); - Add8((word >> 8) & 0xFF); - Add8(word & 0xFF); - } - - void Add64(uint32_t upper_half, uint32_t lower_half) { - Add32(upper_half); - Add32(lower_half); - } - - // Set the 5-bit value in the 1st byte of the header - // and the payload type. Set aside room for the length field, - // and make provision for backpatching it. - // Note: No way to set the padding bit. - void AddRtcpHeader(int payload, int format_or_count) { - PatchLengthField(); - Add8(0x80 | (format_or_count & 0x1F)); - Add8(payload); - pos_of_len_ = pos_; - Add16(0xDEAD); // Initialize length to "clearly illegal". - } - - void AddTmmbrBandwidth(int mantissa, int exponent, int overhead) { - // 6 bits exponent, 17 bits mantissa, 9 bits overhead. - uint32_t word = 0; - word |= (exponent << 26); - word |= ((mantissa & 0x1FFFF) << 9); - word |= (overhead & 0x1FF); - Add32(word); - } - - void AddSrPacket(uint32_t sender_ssrc) { - AddRtcpHeader(200, 0); - Add32(sender_ssrc); - Add64(0x10203, 0x4050607); // NTP timestamp - Add32(0x10203); // RTP timestamp - Add32(0); // Sender's packet count - Add32(0); // Sender's octet count - } - - void AddRrPacket(uint32_t sender_ssrc, uint32_t rtp_ssrc, - uint32_t extended_max, uint8_t fraction_loss, - uint32_t cumulative_loss, uint32_t jitter) { - ReportBlock report_block(rtp_ssrc, extended_max, fraction_loss, - cumulative_loss, jitter); - std::list report_block_vector(&report_block, - &report_block + 1); - AddRrPacketMultipleReportBlocks(sender_ssrc, report_block_vector); - } - - void AddRrPacketMultipleReportBlocks( - uint32_t sender_ssrc, const std::list& report_blocks) { - AddRtcpHeader(201, report_blocks.size()); - Add32(sender_ssrc); - for (std::list::const_iterator it = report_blocks.begin(); - it != report_blocks.end(); ++it) { - AddReportBlock(it->ssrc, it->extended_max, it->fraction_loss, - it->cumulative_loss, it->jitter); - } - } - - void AddReportBlock(uint32_t rtp_ssrc, uint32_t extended_max, - uint8_t fraction_loss, uint32_t cumulative_loss, - uint32_t jitter) { - Add32(rtp_ssrc); - Add32((fraction_loss << 24) + cumulative_loss); - Add32(extended_max); - Add32(jitter); - Add32(0); // Last SR. - Add32(0); // Delay since last SR. - } - - void AddXrHeader(uint32_t sender_ssrc) { - AddRtcpHeader(207, 0); - Add32(sender_ssrc); - } - - void AddXrReceiverReferenceTimeBlock(uint32_t ntp_sec, uint32_t ntp_frac) { - Add8(4); // Block type. - Add8(0); // Reserved. - Add16(2); // Length. - Add64(ntp_sec, ntp_frac); // NTP timestamp. - } - - void AddXrDlrrBlock(std::vector& remote_ssrc) { - ASSERT_LT(pos_ + 4 + static_cast(remote_ssrc.size())*4, - kMaxPacketSize-1) << "Max buffer size reached."; - Add8(5); // Block type. - Add8(0); // Reserved. - Add16(remote_ssrc.size() * 3); // Length. - for (size_t i = 0; i < remote_ssrc.size(); ++i) { - Add32(remote_ssrc.at(i)); // Receiver SSRC. - Add32(0x10203); // Last RR. - Add32(0x40506); // Delay since last RR. - } - } - - void AddXrUnknownBlock() { - Add8(6); // Block type. - Add8(0); // Reserved. - Add16(9); // Length. - Add32(0); // Receiver SSRC. - Add64(0, 0); // Remaining fields (RFC 3611) are set to zero. - Add64(0, 0); - Add64(0, 0); - Add64(0, 0); - } - - void AddXrVoipBlock(uint32_t remote_ssrc, uint8_t loss) { - Add8(7); // Block type. - Add8(0); // Reserved. - Add16(8); // Length. - Add32(remote_ssrc); // Receiver SSRC. - Add8(loss); // Loss rate. - Add8(0); // Remaining statistics (RFC 3611) are set to zero. - Add16(0); - Add64(0, 0); - Add64(0, 0); - Add64(0, 0); - } - - const uint8_t* packet() { - PatchLengthField(); - return buffer_; - } - - unsigned int length() { - return pos_; - } - private: - void PatchLengthField() { - if (pos_of_len_ > 0) { - // Backpatch the packet length. The client must have taken - // care of proper padding to 32-bit words. - int this_packet_length = (pos_ - pos_of_len_ - 2); - ASSERT_EQ(0, this_packet_length % 4) - << "Packets must be a multiple of 32 bits long" - << " pos " << pos_ << " pos_of_len " << pos_of_len_; - buffer_[pos_of_len_] = this_packet_length >> 10; - buffer_[pos_of_len_+1] = (this_packet_length >> 2) & 0xFF; - pos_of_len_ = 0; - } - } - - int pos_; - // Where the length field of the current packet is. - // Note that 0 is not a legal value, so is used for "uninitialized". - int pos_of_len_; - uint8_t buffer_[kMaxPacketSize]; -}; - // This test transport verifies that no functions get called. class TestTransport : public Transport, public NullRtpData { @@ -229,20 +39,24 @@ void SetRTCPReceiver(RTCPReceiver* rtcp_receiver) { rtcp_receiver_ = rtcp_receiver; } - virtual int SendPacket(int /*ch*/, const void* /*data*/, int /*len*/) { + virtual int SendPacket(int /*ch*/, + const void* /*data*/, + int /*len*/) OVERRIDE { ADD_FAILURE(); // FAIL() gives a compile error. return -1; } // Injects an RTCP packet into the receiver. - virtual int SendRTCPPacket(int /* ch */, const void *packet, int packet_len) { + virtual int SendRTCPPacket(int /* ch */, + const void *packet, + int packet_len) OVERRIDE { ADD_FAILURE(); return 0; } virtual int OnReceivedPayloadData(const uint8_t* payloadData, const uint16_t payloadSize, - const WebRtcRTPHeader* rtpHeader) { + const WebRtcRTPHeader* rtpHeader) OVERRIDE { ADD_FAILURE(); return 0; } @@ -261,6 +75,7 @@ RemoteBitrateEstimatorFactory().Create( &remote_bitrate_observer_, &system_clock_, + kMimdControl, kRemoteBitrateEstimatorMinBitrateBps)) { test_transport_ = new TestTransport(); @@ -300,6 +115,8 @@ rtcp_packet_info_.applicationSubType = rtcpPacketInformation.applicationSubType; rtcp_packet_info_.applicationName = rtcpPacketInformation.applicationName; + rtcp_packet_info_.applicationLength = + rtcpPacketInformation.applicationLength; rtcp_packet_info_.report_blocks = rtcpPacketInformation.report_blocks; rtcp_packet_info_.rtt = rtcpPacketInformation.rtt; rtcp_packet_info_.interArrivalJitter = @@ -337,9 +154,10 @@ TEST_F(RtcpReceiverTest, InjectSrPacket) { const uint32_t kSenderSsrc = 0x10203; - PacketBuilder p; - p.AddSrPacket(kSenderSsrc); - EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length())); + rtcp::SenderReport sr; + sr.From(kSenderSsrc); + rtcp::RawPacket p = sr.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); // The parser will note the remote SSRC on a SR from other than his // expected peer, but will not flag that he's gotten a packet. EXPECT_EQ(kSenderSsrc, rtcp_packet_info_.remoteSSRC); @@ -347,10 +165,268 @@ kRtcpSr & rtcp_packet_info_.rtcpPacketTypeFlags); } +TEST_F(RtcpReceiverTest, InjectSrPacketFromExpectedPeer) { + const uint32_t kSenderSsrc = 0x10203; + rtcp_receiver_->SetRemoteSSRC(kSenderSsrc); + rtcp::SenderReport sr; + sr.From(kSenderSsrc); + rtcp::RawPacket p = sr.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); + EXPECT_EQ(kSenderSsrc, rtcp_packet_info_.remoteSSRC); + EXPECT_EQ(kRtcpSr, rtcp_packet_info_.rtcpPacketTypeFlags); +} + +TEST_F(RtcpReceiverTest, InjectRrPacket) { + const uint32_t kSenderSsrc = 0x10203; + rtcp::ReceiverReport rr; + rr.From(kSenderSsrc); + rtcp::RawPacket p = rr.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); + EXPECT_EQ(kSenderSsrc, rtcp_packet_info_.remoteSSRC); + EXPECT_EQ(kRtcpRr, rtcp_packet_info_.rtcpPacketTypeFlags); + ASSERT_EQ(0u, rtcp_packet_info_.report_blocks.size()); +} + +TEST_F(RtcpReceiverTest, InjectRrPacketWithReportBlockNotToUsIgnored) { + const uint32_t kSenderSsrc = 0x10203; + const uint32_t kSourceSsrc = 0x123456; + std::set ssrcs; + ssrcs.insert(kSourceSsrc); + rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs); + + rtcp::ReportBlock rb; + rb.To(kSourceSsrc + 1); + rtcp::ReceiverReport rr; + rr.From(kSenderSsrc); + rr.WithReportBlock(&rb); + rtcp::RawPacket p = rr.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); + EXPECT_EQ(kSenderSsrc, rtcp_packet_info_.remoteSSRC); + EXPECT_EQ(kRtcpRr, rtcp_packet_info_.rtcpPacketTypeFlags); + ASSERT_EQ(0u, rtcp_packet_info_.report_blocks.size()); +} + +TEST_F(RtcpReceiverTest, InjectRrPacketWithOneReportBlock) { + const uint32_t kSenderSsrc = 0x10203; + const uint32_t kSourceSsrc = 0x123456; + std::set ssrcs; + ssrcs.insert(kSourceSsrc); + rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs); + + rtcp::ReportBlock rb; + rb.To(kSourceSsrc); + rtcp::ReceiverReport rr; + rr.From(kSenderSsrc); + rr.WithReportBlock(&rb); + rtcp::RawPacket p = rr.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); + EXPECT_EQ(kSenderSsrc, rtcp_packet_info_.remoteSSRC); + EXPECT_EQ(kRtcpRr, rtcp_packet_info_.rtcpPacketTypeFlags); + ASSERT_EQ(1u, rtcp_packet_info_.report_blocks.size()); +} + +TEST_F(RtcpReceiverTest, InjectRrPacketWithTwoReportBlocks) { + const uint32_t kSenderSsrc = 0x10203; + const uint32_t kSourceSsrcs[] = {0x40506, 0x50607}; + const uint16_t kSequenceNumbers[] = {10, 12423}; + const int kNumSsrcs = sizeof(kSourceSsrcs) / sizeof(kSourceSsrcs[0]); + + std::set ssrcs(kSourceSsrcs, kSourceSsrcs + kNumSsrcs); + rtcp_receiver_->SetSsrcs(kSourceSsrcs[0], ssrcs); + + rtcp::ReportBlock rb1; + rb1.To(kSourceSsrcs[0]); + rb1.WithExtHighestSeqNum(kSequenceNumbers[0]); + rb1.WithFractionLost(10); + rb1.WithCumulativeLost(5); + + rtcp::ReportBlock rb2; + rb2.To(kSourceSsrcs[1]); + rb2.WithExtHighestSeqNum(kSequenceNumbers[1]); + + rtcp::ReceiverReport rr1; + rr1.From(kSenderSsrc); + rr1.WithReportBlock(&rb1); + rr1.WithReportBlock(&rb2); + + rtcp::RawPacket p1 = rr1.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p1.buffer(), p1.buffer_length())); + ASSERT_EQ(2u, rtcp_packet_info_.report_blocks.size()); + EXPECT_EQ(10, rtcp_packet_info_.report_blocks.front().fractionLost); + EXPECT_EQ(0, rtcp_packet_info_.report_blocks.back().fractionLost); + + rtcp::ReportBlock rb3; + rb3.To(kSourceSsrcs[0]); + rb3.WithExtHighestSeqNum(kSequenceNumbers[0]); + + rtcp::ReportBlock rb4; + rb4.To(kSourceSsrcs[1]); + rb4.WithExtHighestSeqNum(kSequenceNumbers[1]); + rb4.WithFractionLost(20); + rb4.WithCumulativeLost(10); + + rtcp::ReceiverReport rr2; + rr2.From(kSenderSsrc); + rr2.WithReportBlock(&rb3); + rr2.WithReportBlock(&rb4); + + rtcp::RawPacket p2 = rr2.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p2.buffer(), p2.buffer_length())); + ASSERT_EQ(2u, rtcp_packet_info_.report_blocks.size()); + EXPECT_EQ(0, rtcp_packet_info_.report_blocks.front().fractionLost); + EXPECT_EQ(20, rtcp_packet_info_.report_blocks.back().fractionLost); +} + +TEST_F(RtcpReceiverTest, InjectIjWithNoItem) { + rtcp::Ij ij; + rtcp::RawPacket p = ij.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); + EXPECT_EQ(0U, rtcp_packet_info_.rtcpPacketTypeFlags); +} + +TEST_F(RtcpReceiverTest, InjectIjWithOneItem) { + rtcp::Ij ij; + ij.WithJitterItem(0x11111111); + + rtcp::RawPacket p = ij.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); + EXPECT_EQ(kRtcpTransmissionTimeOffset, rtcp_packet_info_.rtcpPacketTypeFlags); + EXPECT_EQ(0x11111111U, rtcp_packet_info_.interArrivalJitter); +} + +TEST_F(RtcpReceiverTest, InjectAppWithNoData) { + rtcp::App app; + app.WithSubType(30); + uint32_t name = 'n' << 24; + name += 'a' << 16; + name += 'm' << 8; + name += 'e'; + app.WithName(name); + + rtcp::RawPacket p = app.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); + EXPECT_EQ(kRtcpApp, rtcp_packet_info_.rtcpPacketTypeFlags); + EXPECT_EQ(30, rtcp_packet_info_.applicationSubType); + EXPECT_EQ(name, rtcp_packet_info_.applicationName); + EXPECT_EQ(0, rtcp_packet_info_.applicationLength); +} + +TEST_F(RtcpReceiverTest, InjectAppWithData) { + rtcp::App app; + app.WithSubType(30); + uint32_t name = 'n' << 24; + name += 'a' << 16; + name += 'm' << 8; + name += 'e'; + app.WithName(name); + const char kData[] = {'t', 'e', 's', 't', 'd', 'a', 't', 'a'}; + const size_t kDataLength = sizeof(kData) / sizeof(kData[0]); + app.WithData((const uint8_t*)kData, kDataLength); + + rtcp::RawPacket p = app.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); + EXPECT_EQ(kRtcpApp, rtcp_packet_info_.rtcpPacketTypeFlags); + EXPECT_EQ(30, rtcp_packet_info_.applicationSubType); + EXPECT_EQ(name, rtcp_packet_info_.applicationName); + EXPECT_EQ(kDataLength, rtcp_packet_info_.applicationLength); +} + +TEST_F(RtcpReceiverTest, InjectSdesWithOneChunk) { + const uint32_t kSenderSsrc = 0x123456; + rtcp::Sdes sdes; + sdes.WithCName(kSenderSsrc, "alice@host"); + + rtcp::RawPacket p = sdes.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); + char cName[RTCP_CNAME_SIZE]; + EXPECT_EQ(0, rtcp_receiver_->CNAME(kSenderSsrc, cName)); + EXPECT_EQ(0, strncmp(cName, "alice@host", RTCP_CNAME_SIZE)); +} + +TEST_F(RtcpReceiverTest, InjectByePacket) { + const uint32_t kSenderSsrc = 0x123456; + rtcp::Sdes sdes; + sdes.WithCName(kSenderSsrc, "alice@host"); + + rtcp::RawPacket p = sdes.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); + char cName[RTCP_CNAME_SIZE]; + EXPECT_EQ(0, rtcp_receiver_->CNAME(kSenderSsrc, cName)); + + // Verify that BYE removes the CNAME. + rtcp::Bye bye; + bye.From(kSenderSsrc); + rtcp::RawPacket p2 = bye.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p2.buffer(), p2.buffer_length())); + EXPECT_EQ(-1, rtcp_receiver_->CNAME(kSenderSsrc, cName)); +} + +TEST_F(RtcpReceiverTest, InjectPliPacket) { + const uint32_t kSourceSsrc = 0x123456; + std::set ssrcs; + ssrcs.insert(kSourceSsrc); + rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs); + + rtcp::Pli pli; + pli.To(kSourceSsrc); + rtcp::RawPacket p = pli.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); + EXPECT_EQ(kRtcpPli, rtcp_packet_info_.rtcpPacketTypeFlags); +} + +TEST_F(RtcpReceiverTest, PliPacketNotToUsIgnored) { + const uint32_t kSourceSsrc = 0x123456; + std::set ssrcs; + ssrcs.insert(kSourceSsrc); + rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs); + + rtcp::Pli pli; + pli.To(kSourceSsrc + 1); + rtcp::RawPacket p = pli.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); + EXPECT_EQ(0U, rtcp_packet_info_.rtcpPacketTypeFlags); +} + +TEST_F(RtcpReceiverTest, InjectFirPacket) { + const uint32_t kSourceSsrc = 0x123456; + std::set ssrcs; + ssrcs.insert(kSourceSsrc); + rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs); + + rtcp::Fir fir; + fir.To(kSourceSsrc); + rtcp::RawPacket p = fir.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); + EXPECT_EQ(kRtcpFir, rtcp_packet_info_.rtcpPacketTypeFlags); +} + +TEST_F(RtcpReceiverTest, FirPacketNotToUsIgnored) { + const uint32_t kSourceSsrc = 0x123456; + std::set ssrcs; + ssrcs.insert(kSourceSsrc); + rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs); + + rtcp::Fir fir; + fir.To(kSourceSsrc + 1); + rtcp::RawPacket p = fir.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); + EXPECT_EQ(0U, rtcp_packet_info_.rtcpPacketTypeFlags); +} + +TEST_F(RtcpReceiverTest, InjectSliPacket) { + rtcp::Sli sli; + sli.WithPictureId(40); + rtcp::RawPacket p = sli.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); + EXPECT_EQ(kRtcpSli, rtcp_packet_info_.rtcpPacketTypeFlags); + EXPECT_EQ(40, rtcp_packet_info_.sliPictureId); +} + TEST_F(RtcpReceiverTest, XrPacketWithZeroReportBlocksIgnored) { - PacketBuilder p; - p.AddXrHeader(0x2345); - EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length())); + rtcp::Xr xr; + xr.From(0x2345); + rtcp::RawPacket p = xr.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); EXPECT_EQ(0U, rtcp_packet_info_.rtcpPacketTypeFlags); } @@ -361,37 +437,47 @@ rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs); const uint8_t kLossRate = 123; - PacketBuilder p; - p.AddXrHeader(0x2345); - p.AddXrVoipBlock(kSourceSsrc, kLossRate); - EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length())); + rtcp::VoipMetric voip_metric; + voip_metric.To(kSourceSsrc); + voip_metric.LossRate(kLossRate); + rtcp::Xr xr; + xr.From(0x2345); + xr.WithVoipMetric(&voip_metric); + rtcp::RawPacket p = xr.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); ASSERT_TRUE(rtcp_packet_info_.VoIPMetric != NULL); EXPECT_EQ(kLossRate, rtcp_packet_info_.VoIPMetric->lossRate); EXPECT_EQ(kRtcpXrVoipMetric, rtcp_packet_info_.rtcpPacketTypeFlags); } -TEST_F(RtcpReceiverTest, InjectXrReceiverReferenceTimePacket) { - PacketBuilder p; - p.AddXrHeader(0x2345); - p.AddXrReceiverReferenceTimeBlock(0x10203, 0x40506); - EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length())); - EXPECT_EQ(kRtcpXrReceiverReferenceTime, - rtcp_packet_info_.rtcpPacketTypeFlags); -} - -TEST_F(RtcpReceiverTest, InjectXrDlrrPacketWithNoSubBlock) { +TEST_F(RtcpReceiverTest, XrVoipPacketNotToUsIgnored) { const uint32_t kSourceSsrc = 0x123456; std::set ssrcs; ssrcs.insert(kSourceSsrc); rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs); - std::vector remote_ssrcs; - PacketBuilder p; - p.AddXrHeader(0x2345); - p.AddXrDlrrBlock(remote_ssrcs); - EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length())); + rtcp::VoipMetric voip_metric; + voip_metric.To(kSourceSsrc + 1); + rtcp::Xr xr; + xr.From(0x2345); + xr.WithVoipMetric(&voip_metric); + rtcp::RawPacket p = xr.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); EXPECT_EQ(0U, rtcp_packet_info_.rtcpPacketTypeFlags); - EXPECT_FALSE(rtcp_packet_info_.xr_dlrr_item); +} + +TEST_F(RtcpReceiverTest, InjectXrReceiverReferenceTimePacket) { + rtcp::Rrtr rrtr; + rrtr.WithNtpSec(0x10203); + rrtr.WithNtpFrac(0x40506); + rtcp::Xr xr; + xr.From(0x2345); + xr.WithRrtr(&rrtr); + + rtcp::RawPacket p = xr.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); + EXPECT_EQ(kRtcpXrReceiverReferenceTime, + rtcp_packet_info_.rtcpPacketTypeFlags); } TEST_F(RtcpReceiverTest, XrDlrrPacketNotToUsIgnored) { @@ -399,13 +485,14 @@ std::set ssrcs; ssrcs.insert(kSourceSsrc); rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs); - std::vector remote_ssrcs; - remote_ssrcs.push_back(kSourceSsrc+1); - PacketBuilder p; - p.AddXrHeader(0x2345); - p.AddXrDlrrBlock(remote_ssrcs); - EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length())); + rtcp::Dlrr dlrr; + dlrr.WithDlrrItem(kSourceSsrc + 1, 0x12345, 0x67890); + rtcp::Xr xr; + xr.From(0x2345); + xr.WithDlrr(&dlrr); + rtcp::RawPacket p = xr.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); EXPECT_EQ(0U, rtcp_packet_info_.rtcpPacketTypeFlags); EXPECT_FALSE(rtcp_packet_info_.xr_dlrr_item); } @@ -415,13 +502,14 @@ std::set ssrcs; ssrcs.insert(kSourceSsrc); rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs); - std::vector remote_ssrcs; - remote_ssrcs.push_back(kSourceSsrc); - PacketBuilder p; - p.AddXrHeader(0x2345); - p.AddXrDlrrBlock(remote_ssrcs); - EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length())); + rtcp::Dlrr dlrr; + dlrr.WithDlrrItem(kSourceSsrc, 0x12345, 0x67890); + rtcp::Xr xr; + xr.From(0x2345); + xr.WithDlrr(&dlrr); + rtcp::RawPacket p = xr.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); // The parser should note the DLRR report block item, but not flag the packet // since the RTT is not estimated. EXPECT_TRUE(rtcp_packet_info_.xr_dlrr_item); @@ -432,36 +520,39 @@ std::set ssrcs; ssrcs.insert(kSourceSsrc); rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs); - std::vector remote_ssrcs; - remote_ssrcs.push_back(kSourceSsrc+2); - remote_ssrcs.push_back(kSourceSsrc+1); - remote_ssrcs.push_back(kSourceSsrc); - PacketBuilder p; - p.AddXrHeader(0x2345); - p.AddXrDlrrBlock(remote_ssrcs); - EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length())); + rtcp::Dlrr dlrr; + dlrr.WithDlrrItem(kSourceSsrc + 1, 0x12345, 0x67890); + dlrr.WithDlrrItem(kSourceSsrc + 2, 0x12345, 0x67890); + dlrr.WithDlrrItem(kSourceSsrc, 0x12345, 0x67890); + rtcp::Xr xr; + xr.From(0x2345); + xr.WithDlrr(&dlrr); + rtcp::RawPacket p = xr.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); // The parser should note the DLRR report block item, but not flag the packet // since the RTT is not estimated. EXPECT_TRUE(rtcp_packet_info_.xr_dlrr_item); } TEST_F(RtcpReceiverTest, InjectXrPacketWithMultipleReportBlocks) { - const uint8_t kLossRate = 123; const uint32_t kSourceSsrc = 0x123456; std::set ssrcs; ssrcs.insert(kSourceSsrc); rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs); - std::vector remote_ssrcs; - remote_ssrcs.push_back(kSourceSsrc); - - PacketBuilder p; - p.AddXrHeader(0x2345); - p.AddXrDlrrBlock(remote_ssrcs); - p.AddXrVoipBlock(kSourceSsrc, kLossRate); - p.AddXrReceiverReferenceTimeBlock(0x10203, 0x40506); - EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length())); + rtcp::Rrtr rrtr; + rtcp::Dlrr dlrr; + dlrr.WithDlrrItem(kSourceSsrc, 0x12345, 0x67890); + rtcp::VoipMetric metric; + metric.To(kSourceSsrc); + rtcp::Xr xr; + xr.From(0x2345); + xr.WithRrtr(&rrtr); + xr.WithDlrr(&dlrr); + xr.WithVoipMetric(&metric); + rtcp::RawPacket p = xr.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); EXPECT_EQ(static_cast(kRtcpXrReceiverReferenceTime + kRtcpXrVoipMetric), rtcp_packet_info_.rtcpPacketTypeFlags); @@ -471,7 +562,6 @@ } TEST_F(RtcpReceiverTest, InjectXrPacketWithUnknownReportBlock) { - const uint8_t kLossRate = 123; const uint32_t kSourceSsrc = 0x123456; std::set ssrcs; ssrcs.insert(kSourceSsrc); @@ -479,23 +569,27 @@ std::vector remote_ssrcs; remote_ssrcs.push_back(kSourceSsrc); - PacketBuilder p; - p.AddXrHeader(0x2345); - p.AddXrVoipBlock(kSourceSsrc, kLossRate); - p.AddXrUnknownBlock(); - p.AddXrReceiverReferenceTimeBlock(0x10203, 0x40506); + rtcp::Rrtr rrtr; + rtcp::Dlrr dlrr; + dlrr.WithDlrrItem(kSourceSsrc, 0x12345, 0x67890); + rtcp::VoipMetric metric; + metric.To(kSourceSsrc); + rtcp::Xr xr; + xr.From(0x2345); + xr.WithRrtr(&rrtr); + xr.WithDlrr(&dlrr); + xr.WithVoipMetric(&metric); + rtcp::RawPacket p = xr.Build(); + // Modify the DLRR block to have an unsupported block type, from 5 to 6. + uint8_t* buffer = const_cast(p.buffer()); + EXPECT_EQ(5, buffer[20]); + buffer[20] = 6; - EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length())); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); EXPECT_EQ(static_cast(kRtcpXrReceiverReferenceTime + kRtcpXrVoipMetric), rtcp_packet_info_.rtcpPacketTypeFlags); -} - -TEST(RtcpUtilityTest, MidNtp) { - const uint32_t kNtpSec = 0x12345678; - const uint32_t kNtpFrac = 0x23456789; - const uint32_t kNtpMid = 0x56782345; - EXPECT_EQ(kNtpMid, RTCPUtility::MidNtp(kNtpSec, kNtpFrac)); + EXPECT_FALSE(rtcp_packet_info_.xr_dlrr_item); } TEST_F(RtcpReceiverTest, TestXrRrRttInitiallyFalse) { @@ -514,10 +608,14 @@ const uint32_t kNtpFrac = 0x40506; const uint32_t kNtpMid = RTCPUtility::MidNtp(kNtpSec, kNtpFrac); - PacketBuilder p; - p.AddXrHeader(kSenderSsrc); - p.AddXrReceiverReferenceTimeBlock(kNtpSec, kNtpFrac); - EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length())); + rtcp::Rrtr rrtr; + rrtr.WithNtpSec(kNtpSec); + rrtr.WithNtpFrac(kNtpFrac); + rtcp::Xr xr; + xr.From(kSenderSsrc); + xr.WithRrtr(&rrtr); + rtcp::RawPacket p = xr.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); EXPECT_EQ(kRtcpXrReceiverReferenceTime, rtcp_packet_info_.rtcpPacketTypeFlags); @@ -541,7 +639,7 @@ ssrcs.insert(kSourceSsrc); rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs); - uint32_t sequence_number = 1234; + const uint16_t kSequenceNumber = 1234; system_clock_.AdvanceTimeMilliseconds(3 * kRtcpIntervalMs); // No RR received, shouldn't trigger a timeout. @@ -549,18 +647,21 @@ EXPECT_FALSE(rtcp_receiver_->RtcpRrSequenceNumberTimeout(kRtcpIntervalMs)); // Add a RR and advance the clock just enough to not trigger a timeout. - PacketBuilder p1; - p1.AddRrPacket(kSenderSsrc, kSourceSsrc, sequence_number, 0, 0, 0); - EXPECT_EQ(0, InjectRtcpPacket(p1.packet(), p1.length())); + rtcp::ReportBlock rb1; + rb1.To(kSourceSsrc); + rb1.WithExtHighestSeqNum(kSequenceNumber); + rtcp::ReceiverReport rr1; + rr1.From(kSenderSsrc); + rr1.WithReportBlock(&rb1); + rtcp::RawPacket p1 = rr1.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p1.buffer(), p1.buffer_length())); system_clock_.AdvanceTimeMilliseconds(3 * kRtcpIntervalMs - 1); EXPECT_FALSE(rtcp_receiver_->RtcpRrTimeout(kRtcpIntervalMs)); EXPECT_FALSE(rtcp_receiver_->RtcpRrSequenceNumberTimeout(kRtcpIntervalMs)); // Add a RR with the same extended max as the previous RR to trigger a // sequence number timeout, but not a RR timeout. - PacketBuilder p2; - p2.AddRrPacket(kSenderSsrc, kSourceSsrc, sequence_number, 0, 0, 0); - EXPECT_EQ(0, InjectRtcpPacket(p2.packet(), p2.length())); + EXPECT_EQ(0, InjectRtcpPacket(p1.buffer(), p1.buffer_length())); system_clock_.AdvanceTimeMilliseconds(2); EXPECT_FALSE(rtcp_receiver_->RtcpRrTimeout(kRtcpIntervalMs)); EXPECT_TRUE(rtcp_receiver_->RtcpRrSequenceNumberTimeout(kRtcpIntervalMs)); @@ -575,18 +676,20 @@ EXPECT_FALSE(rtcp_receiver_->RtcpRrSequenceNumberTimeout(kRtcpIntervalMs)); // Add a new RR with increase sequence number to reset timers. - PacketBuilder p3; - sequence_number++; - p2.AddRrPacket(kSenderSsrc, kSourceSsrc, sequence_number, 0, 0, 0); - EXPECT_EQ(0, InjectRtcpPacket(p2.packet(), p2.length())); + rtcp::ReportBlock rb2; + rb2.To(kSourceSsrc); + rb2.WithExtHighestSeqNum(kSequenceNumber + 1); + rtcp::ReceiverReport rr2; + rr2.From(kSenderSsrc); + rr2.WithReportBlock(&rb2); + rtcp::RawPacket p2 = rr2.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p2.buffer(), p2.buffer_length())); EXPECT_FALSE(rtcp_receiver_->RtcpRrTimeout(kRtcpIntervalMs)); EXPECT_FALSE(rtcp_receiver_->RtcpRrSequenceNumberTimeout(kRtcpIntervalMs)); // Verify we can get a timeout again once we've received new RR. system_clock_.AdvanceTimeMilliseconds(2 * kRtcpIntervalMs); - PacketBuilder p4; - p4.AddRrPacket(kSenderSsrc, kSourceSsrc, sequence_number, 0, 0, 0); - EXPECT_EQ(0, InjectRtcpPacket(p4.packet(), p4.length())); + EXPECT_EQ(0, InjectRtcpPacket(p2.buffer(), p2.buffer_length())); system_clock_.AdvanceTimeMilliseconds(kRtcpIntervalMs + 1); EXPECT_FALSE(rtcp_receiver_->RtcpRrTimeout(kRtcpIntervalMs)); EXPECT_TRUE(rtcp_receiver_->RtcpRrSequenceNumberTimeout(kRtcpIntervalMs)); @@ -599,135 +702,98 @@ EXPECT_EQ(-1, rtcp_receiver_->TMMBRReceived(0, 0, NULL)); } -TEST_F(RtcpReceiverTest, TwoReportBlocks) { - const uint32_t kSenderSsrc = 0x10203; - const int kNumSsrcs = 2; - const uint32_t kSourceSsrcs[kNumSsrcs] = {0x40506, 0x50607}; - uint32_t sequence_numbers[kNumSsrcs] = {10, 12423}; - - std::set ssrcs(kSourceSsrcs, kSourceSsrcs + kNumSsrcs); - rtcp_receiver_->SetSsrcs(kSourceSsrcs[0], ssrcs); - - PacketBuilder packet; - std::list report_blocks; - report_blocks.push_back(PacketBuilder::ReportBlock( - kSourceSsrcs[0], sequence_numbers[0], 10, 5, 0)); - report_blocks.push_back(PacketBuilder::ReportBlock( - kSourceSsrcs[1], sequence_numbers[1], 0, 0, 0)); - packet.AddRrPacketMultipleReportBlocks(kSenderSsrc, report_blocks); - EXPECT_EQ(0, InjectRtcpPacket(packet.packet(), packet.length())); - ASSERT_EQ(2u, rtcp_packet_info_.report_blocks.size()); - EXPECT_EQ(10, rtcp_packet_info_.report_blocks.front().fractionLost); - EXPECT_EQ(0, rtcp_packet_info_.report_blocks.back().fractionLost); - - PacketBuilder packet2; - report_blocks.clear(); - report_blocks.push_back(PacketBuilder::ReportBlock( - kSourceSsrcs[0], sequence_numbers[0], 0, 0, 0)); - report_blocks.push_back(PacketBuilder::ReportBlock( - kSourceSsrcs[1], sequence_numbers[1], 20, 10, 0)); - packet2.AddRrPacketMultipleReportBlocks(kSenderSsrc, report_blocks); - EXPECT_EQ(0, InjectRtcpPacket(packet2.packet(), packet2.length())); - ASSERT_EQ(2u, rtcp_packet_info_.report_blocks.size()); - EXPECT_EQ(0, rtcp_packet_info_.report_blocks.front().fractionLost); - EXPECT_EQ(20, rtcp_packet_info_.report_blocks.back().fractionLost); -} - TEST_F(RtcpReceiverTest, TmmbrPacketAccepted) { const uint32_t kMediaFlowSsrc = 0x2040608; const uint32_t kSenderSsrc = 0x10203; - const uint32_t kMediaRecipientSsrc = 0x101; std::set ssrcs; ssrcs.insert(kMediaFlowSsrc); // Matches "media source" above. rtcp_receiver_->SetSsrcs(kMediaFlowSsrc, ssrcs); - PacketBuilder p; - p.AddSrPacket(kSenderSsrc); - // TMMBR packet. - p.AddRtcpHeader(205, 3); - p.Add32(kSenderSsrc); - p.Add32(kMediaRecipientSsrc); - p.Add32(kMediaFlowSsrc); - p.AddTmmbrBandwidth(30000, 0, 0); // 30 Kbits/sec bandwidth, no overhead. + rtcp::Tmmbr tmmbr; + tmmbr.From(kSenderSsrc); + tmmbr.To(kMediaFlowSsrc); + tmmbr.WithBitrateKbps(30); + + rtcp::SenderReport sr; + sr.From(kSenderSsrc); + sr.Append(&tmmbr); + rtcp::RawPacket p = sr.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); - EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length())); EXPECT_EQ(1, rtcp_receiver_->TMMBRReceived(0, 0, NULL)); TMMBRSet candidate_set; candidate_set.VerifyAndAllocateSet(1); EXPECT_EQ(1, rtcp_receiver_->TMMBRReceived(1, 0, &candidate_set)); EXPECT_LT(0U, candidate_set.Tmmbr(0)); - EXPECT_EQ(kMediaRecipientSsrc, candidate_set.Ssrc(0)); + EXPECT_EQ(kSenderSsrc, candidate_set.Ssrc(0)); } TEST_F(RtcpReceiverTest, TmmbrPacketNotForUsIgnored) { const uint32_t kMediaFlowSsrc = 0x2040608; const uint32_t kSenderSsrc = 0x10203; - const uint32_t kMediaRecipientSsrc = 0x101; - const uint32_t kOtherMediaFlowSsrc = 0x9999; - PacketBuilder p; - p.AddSrPacket(kSenderSsrc); - // TMMBR packet. - p.AddRtcpHeader(205, 3); - p.Add32(kSenderSsrc); - p.Add32(kMediaRecipientSsrc); - p.Add32(kOtherMediaFlowSsrc); // This SSRC is not what we're sending. - p.AddTmmbrBandwidth(30000, 0, 0); + rtcp::Tmmbr tmmbr; + tmmbr.From(kSenderSsrc); + tmmbr.To(kMediaFlowSsrc + 1); // This SSRC is not what we are sending. + tmmbr.WithBitrateKbps(30); + + rtcp::SenderReport sr; + sr.From(kSenderSsrc); + sr.Append(&tmmbr); + rtcp::RawPacket p = sr.Build(); std::set ssrcs; ssrcs.insert(kMediaFlowSsrc); rtcp_receiver_->SetSsrcs(kMediaFlowSsrc, ssrcs); - EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length())); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); EXPECT_EQ(0, rtcp_receiver_->TMMBRReceived(0, 0, NULL)); } TEST_F(RtcpReceiverTest, TmmbrPacketZeroRateIgnored) { const uint32_t kMediaFlowSsrc = 0x2040608; const uint32_t kSenderSsrc = 0x10203; - const uint32_t kMediaRecipientSsrc = 0x101; std::set ssrcs; ssrcs.insert(kMediaFlowSsrc); // Matches "media source" above. rtcp_receiver_->SetSsrcs(kMediaFlowSsrc, ssrcs); - PacketBuilder p; - p.AddSrPacket(kSenderSsrc); - // TMMBR packet. - p.AddRtcpHeader(205, 3); - p.Add32(kSenderSsrc); - p.Add32(kMediaRecipientSsrc); - p.Add32(kMediaFlowSsrc); - p.AddTmmbrBandwidth(0, 0, 0); // Rate zero. + rtcp::Tmmbr tmmbr; + tmmbr.From(kSenderSsrc); + tmmbr.To(kMediaFlowSsrc); + tmmbr.WithBitrateKbps(0); + + rtcp::SenderReport sr; + sr.From(kSenderSsrc); + sr.Append(&tmmbr); + rtcp::RawPacket p = sr.Build(); - EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length())); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); EXPECT_EQ(0, rtcp_receiver_->TMMBRReceived(0, 0, NULL)); } TEST_F(RtcpReceiverTest, TmmbrThreeConstraintsTimeOut) { const uint32_t kMediaFlowSsrc = 0x2040608; const uint32_t kSenderSsrc = 0x10203; - const uint32_t kMediaRecipientSsrc = 0x101; std::set ssrcs; ssrcs.insert(kMediaFlowSsrc); // Matches "media source" above. rtcp_receiver_->SetSsrcs(kMediaFlowSsrc, ssrcs); - // Inject 3 packets "from" kMediaRecipientSsrc, Ssrc+1, Ssrc+2. + // Inject 3 packets "from" kSenderSsrc, kSenderSsrc+1, kSenderSsrc+2. // The times of arrival are starttime + 0, starttime + 5 and starttime + 10. - for (uint32_t ssrc = kMediaRecipientSsrc; - ssrc < kMediaRecipientSsrc+3; ++ssrc) { - PacketBuilder p; - p.AddSrPacket(kSenderSsrc); - // TMMBR packet. - p.AddRtcpHeader(205, 3); - p.Add32(kSenderSsrc); - p.Add32(ssrc); - p.Add32(kMediaFlowSsrc); - p.AddTmmbrBandwidth(30000, 0, 0); // 30 Kbits/sec bandwidth, no overhead. - - EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length())); + for (uint32_t ssrc = kSenderSsrc; ssrc < kSenderSsrc + 3; ++ssrc) { + rtcp::Tmmbr tmmbr; + tmmbr.From(ssrc); + tmmbr.To(kMediaFlowSsrc); + tmmbr.WithBitrateKbps(30); + + rtcp::SenderReport sr; + sr.From(ssrc); + sr.Append(&tmmbr); + rtcp::RawPacket p = sr.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p.buffer(), p.buffer_length())); // 5 seconds between each packet. system_clock_.AdvanceTimeMilliseconds(5000); } - // It is now starttime+15. + // It is now starttime + 15. EXPECT_EQ(3, rtcp_receiver_->TMMBRReceived(0, 0, NULL)); TMMBRSet candidate_set; candidate_set.VerifyAndAllocateSet(3); @@ -738,9 +804,8 @@ system_clock_.AdvanceTimeMilliseconds(12000); // Odd behaviour: Just counting them does not trigger the timeout. EXPECT_EQ(3, rtcp_receiver_->TMMBRReceived(0, 0, NULL)); - // Odd behaviour: There's only one left after timeout, not 2. - EXPECT_EQ(1, rtcp_receiver_->TMMBRReceived(3, 0, &candidate_set)); - EXPECT_EQ(kMediaRecipientSsrc + 2, candidate_set.Ssrc(0)); + EXPECT_EQ(2, rtcp_receiver_->TMMBRReceived(3, 0, &candidate_set)); + EXPECT_EQ(kSenderSsrc + 1, candidate_set.Ssrc(0)); } TEST_F(RtcpReceiverTest, Callbacks) { @@ -750,7 +815,7 @@ virtual ~RtcpCallbackImpl() {} virtual void StatisticsUpdated(const RtcpStatistics& statistics, - uint32_t ssrc) { + uint32_t ssrc) OVERRIDE { stats_ = statistics; ssrc_ = ssrc; } @@ -772,31 +837,48 @@ const uint32_t kSenderSsrc = 0x10203; const uint32_t kSourceSsrc = 0x123456; - const uint8_t fraction_loss = 3; - const uint32_t cumulative_loss = 7; - const uint32_t jitter = 9; - uint32_t sequence_number = 1234; + const uint8_t kFractionLoss = 3; + const uint32_t kCumulativeLoss = 7; + const uint32_t kJitter = 9; + const uint16_t kSequenceNumber = 1234; std::set ssrcs; ssrcs.insert(kSourceSsrc); rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs); - // First packet, all numbers should just propagate - PacketBuilder p1; - p1.AddRrPacket(kSenderSsrc, kSourceSsrc, sequence_number, - fraction_loss, cumulative_loss, jitter); - EXPECT_EQ(0, InjectRtcpPacket(p1.packet(), p1.length())); - EXPECT_TRUE(callback.Matches(kSourceSsrc, sequence_number, fraction_loss, - cumulative_loss, jitter)); + // First packet, all numbers should just propagate. + rtcp::ReportBlock rb1; + rb1.To(kSourceSsrc); + rb1.WithExtHighestSeqNum(kSequenceNumber); + rb1.WithFractionLost(kFractionLoss); + rb1.WithCumulativeLost(kCumulativeLoss); + rb1.WithJitter(kJitter); + + rtcp::ReceiverReport rr1; + rr1.From(kSenderSsrc); + rr1.WithReportBlock(&rb1); + rtcp::RawPacket p1 = rr1.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p1.buffer(), p1.buffer_length())); + EXPECT_TRUE(callback.Matches(kSourceSsrc, kSequenceNumber, kFractionLoss, + kCumulativeLoss, kJitter)); rtcp_receiver_->RegisterRtcpStatisticsCallback(NULL); - // Add arbitrary numbers, callback should not be called (retain old values) - PacketBuilder p2; - p2.AddRrPacket(kSenderSsrc, kSourceSsrc, sequence_number + 1, 42, 137, 4711); - EXPECT_EQ(0, InjectRtcpPacket(p2.packet(), p2.length())); - EXPECT_TRUE(callback.Matches(kSourceSsrc, sequence_number, fraction_loss, - cumulative_loss, jitter)); + // Add arbitrary numbers, callback should not be called (retain old values). + rtcp::ReportBlock rb2; + rb2.To(kSourceSsrc); + rb2.WithExtHighestSeqNum(kSequenceNumber + 1); + rb2.WithFractionLost(42); + rb2.WithCumulativeLost(137); + rb2.WithJitter(4711); + + rtcp::ReceiverReport rr2; + rr2.From(kSenderSsrc); + rr2.WithReportBlock(&rb2); + rtcp::RawPacket p2 = rr2.Build(); + EXPECT_EQ(0, InjectRtcpPacket(p2.buffer(), p2.buffer_length())); + EXPECT_TRUE(callback.Matches(kSourceSsrc, kSequenceNumber, kFractionLoss, + kCumulativeLoss, kJitter)); } } // Anonymous namespace diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc 2015-02-03 14:33:36.000000000 +0000 @@ -19,7 +19,7 @@ #include "webrtc/common_types.h" #include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/trace_event.h" namespace webrtc { @@ -65,30 +65,11 @@ return _stream.str(); } -RTCPSender::FeedbackState::FeedbackState(ModuleRtpRtcpImpl* module) - : send_payload_type(module->SendPayloadType()), - frequency_hz(module->CurrentSendFrequencyHz()), - packet_count_sent(module->PacketCountSent()), - byte_count_sent(module->ByteCountSent()), - module(module) { - uint32_t last_ntp_secs = 0, last_ntp_frac = 0, last_remote_sr = 0; - module->LastReceivedNTP(last_ntp_secs, last_ntp_frac, last_remote_sr); - last_rr_ntp_secs = last_ntp_secs; - last_rr_ntp_frac = last_ntp_frac; - remote_sr = last_remote_sr; - - has_last_xr_rr = module->LastReceivedXrReferenceTimeInfo(&last_xr_rr); - - uint32_t send_bitrate = 0, tmp; - module->BitrateSent(&send_bitrate, &tmp, &tmp, &tmp); - this->send_bitrate = send_bitrate; -} - RTCPSender::FeedbackState::FeedbackState() : send_payload_type(0), frequency_hz(0), - packet_count_sent(0), - byte_count_sent(0), + packets_sent(0), + media_bytes_sent(0), send_bitrate(0), last_rr_ntp_secs(0), last_rr_ntp_frac(0), @@ -158,18 +139,13 @@ xrSendReceiverReferenceTimeEnabled_(false), _xrSendVoIPMetric(false), - _xrVoIPMetric(), - _nackCount(0), - _pliCount(0), - _fullIntraRequestCount(0) + _xrVoIPMetric() { memset(_CNAME, 0, sizeof(_CNAME)); memset(_lastSendReport, 0, sizeof(_lastSendReport)); memset(_lastRTCPTime, 0, sizeof(_lastRTCPTime)); memset(_lastSRPacketCount, 0, sizeof(_lastSRPacketCount)); memset(_lastSROctetCount, 0, sizeof(_lastSROctetCount)); - - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id, "%s created", __FUNCTION__); } RTCPSender::~RTCPSender() { @@ -194,68 +170,6 @@ } delete _criticalSectionTransport; delete _criticalSectionRTCPSender; - - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, _id, "%s deleted", __FUNCTION__); -} - -int32_t -RTCPSender::Init() -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - - _method = kRtcpOff; - _cbTransport = NULL; - _usingNack = false; - _sending = false; - _sendTMMBN = false; - _TMMBR = false; - _IJ = false; - _REMB = false; - _sendREMB = false; - last_rtp_timestamp_ = 0; - last_frame_capture_time_ms_ = -1; - start_timestamp_ = -1; - _SSRC = 0; - _remoteSSRC = 0; - _cameraDelayMS = 0; - _sequenceNumberFIR = 0; - _tmmbr_Send = 0; - _packetOH_Send = 0; - _nextTimeToSendRTCP = 0; - _CSRCs = 0; - _appSend = false; - _appSubType = 0; - - if(_appData) - { - delete [] _appData; - _appData = NULL; - } - _appLength = 0; - - xrSendReceiverReferenceTimeEnabled_ = false; - - _xrSendVoIPMetric = false; - - memset(&_xrVoIPMetric, 0, sizeof(_xrVoIPMetric)); - memset(_CNAME, 0, sizeof(_CNAME)); - memset(_lastSendReport, 0, sizeof(_lastSendReport)); - memset(_lastRTCPTime, 0, sizeof(_lastRTCPTime)); - memset(_lastSRPacketCount, 0, sizeof(_lastSRPacketCount)); - memset(_lastSROctetCount, 0, sizeof(_lastSROctetCount)); - last_xr_rr_.clear(); - - _nackCount = 0; - _pliCount = 0; - _fullIntraRequestCount = 0; - - return 0; -} - -void -RTCPSender::ChangeUniqueId(const int32_t id) -{ - _id = id; } int32_t @@ -346,20 +260,23 @@ { CriticalSectionScoped lock(_criticalSectionRTCPSender); _rembBitrate = bitrate; - + if(_sizeRembSSRC < numberOfSSRC) { delete [] _rembSSRC; _rembSSRC = new uint32_t[numberOfSSRC]; _sizeRembSSRC = numberOfSSRC; - } + } _lengthRembSSRC = numberOfSSRC; for (int i = 0; i < numberOfSSRC; i++) - { + { _rembSSRC[i] = SSRC[i]; } _sendREMB = true; + // Send a REMB immediately if we have a new REMB. The frequency of REMBs is + // throttled by the caller. + _nextTimeToSendRTCP = _clock->TimeInMilliseconds(); return 0; } @@ -394,6 +311,7 @@ } void RTCPSender::SetStartTimestamp(uint32_t start_timestamp) { + CriticalSectionScoped lock(_criticalSectionRTCPSender); start_timestamp_ = start_timestamp; } @@ -436,21 +354,14 @@ CriticalSectionScoped lock(_criticalSectionRTCPSender); if(delayMS > 1000 || delayMS < -1000) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument, delay can't be larger than 1 sec", __FUNCTION__); + LOG(LS_WARNING) << "Delay can't be larger than 1 second: " + << delayMS << " ms"; return -1; } _cameraDelayMS = delayMS; return 0; } -int32_t RTCPSender::CNAME(char cName[RTCP_CNAME_SIZE]) { - assert(cName); - CriticalSectionScoped lock(_criticalSectionRTCPSender); - cName[RTCP_CNAME_SIZE - 1] = 0; - strncpy(cName, _CNAME, RTCP_CNAME_SIZE - 1); - return 0; -} - int32_t RTCPSender::SetCNAME(const char cName[RTCP_CNAME_SIZE]) { if (!cName) return -1; @@ -495,14 +406,15 @@ For audio we use a fix 5 sec interval For video we use 1 sec interval fo a BW smaller than 360 kbit/s, - technicaly we break the max 5% RTCP BW for video below 10 kbit/s but that should be extreamly rare + technicaly we break the max 5% RTCP BW for video below 10 kbit/s but + that should be extremely rare From RFC 3550 MAX RTCP BW is 5% if the session BW A send report is approximately 65 bytes inc CNAME - A report report is approximately 28 bytes + A receiver report is approximately 28 bytes The RECOMMENDED value for the reduced minimum in seconds is 360 divided by the session bandwidth in kilobits/second. This minimum @@ -564,7 +476,7 @@ now += RTCP_SEND_BEFORE_KEY_FRAME_MS; } - if(now > _nextTimeToSendRTCP) + if(now >= _nextTimeToSendRTCP) { return true; @@ -628,6 +540,12 @@ return true; } +void RTCPSender::GetPacketTypeCounter( + RtcpPacketTypeCounter* packet_counter) const { + CriticalSectionScoped lock(_criticalSectionRTCPSender); + *packet_counter = packet_type_counter_; +} + int32_t RTCPSender::AddExternalReportBlock( uint32_t SSRC, const RTCPReportBlock* reportBlock) { @@ -639,15 +557,10 @@ uint32_t SSRC, std::map* report_blocks, const RTCPReportBlock* reportBlock) { - if (reportBlock == NULL) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s invalid argument", __FUNCTION__); - return -1; - } + assert(reportBlock); if (report_blocks->size() >= RTCP_MAX_REPORT_BLOCKS) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s invalid argument", __FUNCTION__); + LOG(LS_WARNING) << "Too many report blocks."; return -1; } std::map::iterator it = @@ -685,7 +598,7 @@ // sanity if(pos + 52 >= IP_PACKET_SIZE) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); + LOG(LS_WARNING) << "Failed to build Sender Report."; return -2; } uint32_t RTPtime; @@ -707,20 +620,16 @@ _lastRTCPTime[0] = Clock::NtpToMs(NTPsec, NTPfrac); _lastSendReport[0] = (NTPsec << 16) + (NTPfrac >> 16); - _lastSRPacketCount[0] = feedback_state.packet_count_sent; - _lastSROctetCount[0] = feedback_state.byte_count_sent; + _lastSRPacketCount[0] = feedback_state.packets_sent; + _lastSROctetCount[0] = feedback_state.media_bytes_sent; // The timestamp of this RTCP packet should be estimated as the timestamp of // the frame being captured at this moment. We are calculating that // timestamp as the last frame's timestamp + the time since the last frame // was captured. - { - // Needs protection since this method is called on the process thread. - CriticalSectionScoped lock(_criticalSectionRTCPSender); - RTPtime = start_timestamp_ + last_rtp_timestamp_ + ( - _clock->TimeInMilliseconds() - last_frame_capture_time_ms_) * - (feedback_state.frequency_hz / 1000); - } + RTPtime = start_timestamp_ + last_rtp_timestamp_ + + (_clock->TimeInMilliseconds() - last_frame_capture_time_ms_) * + (feedback_state.frequency_hz / 1000); // Add sender data // Save for our length field @@ -728,24 +637,24 @@ pos++; // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC); pos += 4; // NTP - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, NTPsec); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, NTPsec); pos += 4; - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, NTPfrac); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, NTPfrac); pos += 4; - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, RTPtime); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, RTPtime); pos += 4; //sender's packet count - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, - feedback_state.packet_count_sent); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, + feedback_state.packets_sent); pos += 4; //sender's octet count - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, - feedback_state.byte_count_sent); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, + feedback_state.media_bytes_sent); pos += 4; uint8_t numberOfReportBlocks = 0; @@ -761,7 +670,7 @@ rtcpbuffer[posNumberOfReportBlocks] += numberOfReportBlocks; uint16_t len = uint16_t((pos/4) -1); - ModuleRTPUtility::AssignUWord16ToBuffer(rtcpbuffer+2, len); + RtpUtility::AssignUWord16ToBuffer(rtcpbuffer + 2, len); return 0; } @@ -772,8 +681,7 @@ // sanity if(pos + 12 + lengthCname >= IP_PACKET_SIZE) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s invalid argument", __FUNCTION__); + LOG(LS_WARNING) << "Failed to build SDEC."; return -2; } // SDEC Source Description @@ -788,7 +696,7 @@ pos++; // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC); pos += 4; // CNAME = 1 @@ -823,7 +731,7 @@ uint32_t SSRC = it->first; // Add SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, SSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, SSRC); pos += 4; // CNAME = 1 @@ -854,8 +762,7 @@ } // in 32-bit words minus one and we don't count the header uint16_t buffer_length = (SDESLength / 4) - 1; - ModuleRTPUtility::AssignUWord16ToBuffer(rtcpbuffer + SDESLengthPos, - buffer_length); + RtpUtility::AssignUWord16ToBuffer(rtcpbuffer + SDESLengthPos, buffer_length); return 0; } @@ -880,7 +787,7 @@ pos++; // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC); pos += 4; uint8_t numberOfReportBlocks = 0; @@ -895,7 +802,7 @@ rtcpbuffer[posNumberOfReportBlocks] += numberOfReportBlocks; uint16_t len = uint16_t((pos)/4 -1); - ModuleRTPUtility::AssignUWord16ToBuffer(rtcpbuffer+2, len); + RtpUtility::AssignUWord16ToBuffer(rtcpbuffer + 2, len); return 0; } @@ -925,7 +832,9 @@ { if (external_report_blocks_.size() > 0) { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "Not implemented."); + // TODO(andresp): Remove external report blocks since they are not + // supported. + LOG(LS_ERROR) << "Handling of external report blocks not implemented."; return 0; } @@ -944,8 +853,8 @@ rtcpbuffer[pos++]=(uint8_t)(1); // Add inter-arrival jitter - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, - jitterTransmissionTimeOffset); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, + jitterTransmissionTimeOffset); pos += 4; return 0; } @@ -968,11 +877,11 @@ rtcpbuffer[pos++]=(uint8_t)(2); // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC); pos += 4; // Add the remote SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _remoteSSRC); pos += 4; return 0; } @@ -998,7 +907,7 @@ rtcpbuffer[pos++] = (uint8_t)(4); // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC); pos += 4; // RFC 5104 4.3.1.2. Semantics @@ -1009,7 +918,7 @@ rtcpbuffer[pos++] = (uint8_t)0; // Additional Feedback Control Information (FCI) - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _remoteSSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _remoteSSRC); pos += 4; rtcpbuffer[pos++] = (uint8_t)(_sequenceNumberFIR); @@ -1044,18 +953,18 @@ rtcpbuffer[pos++]=(uint8_t)(3); // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC); pos += 4; // Add the remote SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _remoteSSRC); pos += 4; // Add first, number & picture ID 6 bits // first = 0, 13 - bits // number = 0x1fff, 13 - bits only ones for now uint32_t sliField = (0x1fff << 6)+ (0x3f & pictureID); - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, sliField); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, sliField); pos += 4; return 0; } @@ -1109,11 +1018,11 @@ rtcpbuffer[pos++]=size; // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC); pos += 4; // Add the remote SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _remoteSSRC); pos += 4; // calc padding length @@ -1166,11 +1075,11 @@ rtcpbuffer[pos++]=_lengthRembSSRC + 4; // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC); pos += 4; // Remote SSRC must be 0 - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, 0); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, 0); pos += 4; rtcpbuffer[pos++]='R'; @@ -1195,9 +1104,9 @@ rtcpbuffer[pos++]=(uint8_t)(brMantissa >> 8); rtcpbuffer[pos++]=(uint8_t)(brMantissa); - for (int i = 0; i < _lengthRembSSRC; i++) - { - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _rembSSRC[i]); + for (int i = 0; i < _lengthRembSSRC; i++) + { + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _rembSSRC[i]); pos += 4; } return 0; @@ -1283,7 +1192,7 @@ rtcpbuffer[pos++]=(uint8_t)(4); // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC); pos += 4; // RFC 5104 4.2.1.2. Semantics @@ -1295,7 +1204,7 @@ rtcpbuffer[pos++]=(uint8_t)0; // Additional Feedback Control Information (FCI) - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _remoteSSRC); pos += 4; uint32_t bitRate = _tmmbr_Send*1000; @@ -1329,7 +1238,7 @@ // sanity if(pos + 12 + boundingSet->lengthOfSet()*8 >= IP_PACKET_SIZE) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); + LOG(LS_WARNING) << "Failed to build TMMBN."; return -2; } uint8_t FMT = 4; @@ -1343,7 +1252,7 @@ pos++; // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC); pos += 4; // RFC 5104 4.2.2.2. Semantics @@ -1361,7 +1270,7 @@ if (boundingSet->Tmmbr(n) > 0) { uint32_t tmmbrSSRC = boundingSet->Ssrc(n); - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, tmmbrSSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, tmmbrSSRC); pos += 4; uint32_t bitRate = boundingSet->Tmmbr(n) * 1000; @@ -1396,12 +1305,12 @@ // sanity if(_appData == NULL) { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "%s invalid state", __FUNCTION__); + LOG(LS_WARNING) << "Failed to build app specific."; return -1; } if(pos + 12 + _appLength >= IP_PACKET_SIZE) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); + LOG(LS_WARNING) << "Failed to build app specific."; return -2; } rtcpbuffer[pos++]=(uint8_t)0x80 + _appSubType; @@ -1414,11 +1323,11 @@ rtcpbuffer[pos++]=(uint8_t)(length); // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC); pos += 4; // Add our application name - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _appName); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _appName); pos += 4; // Add the data @@ -1437,7 +1346,7 @@ // sanity if(pos + 16 >= IP_PACKET_SIZE) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); + LOG(LS_WARNING) << "Failed to build NACK."; return -2; } @@ -1452,14 +1361,13 @@ rtcpbuffer[pos++]=(uint8_t)(3); //setting it to one kNACK signal as default // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC); pos += 4; // Add the remote SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _remoteSSRC); pos += 4; - NACKStringBuilder stringBuilder; // Build NACK bitmasks and write them to the RTCP message. // The nack list should be sorted and not contain duplicates if one // wants to build the smallest rtcp nack packet. @@ -1468,13 +1376,11 @@ (IP_PACKET_SIZE - pos) / 4); int i = 0; while (i < nackSize && numOfNackFields < maxNackFields) { - stringBuilder.PushNACK(nackList[i]); uint16_t nack = nackList[i++]; uint16_t bitmask = 0; while (i < nackSize) { int shift = static_cast(nackList[i] - nack) - 1; if (shift >= 0 && shift <= 15) { - stringBuilder.PushNACK(nackList[i]); bitmask |= (1 << shift); ++i; } else { @@ -1483,18 +1389,27 @@ } // Write the sequence number and the bitmask to the packet. assert(pos + 4 < IP_PACKET_SIZE); - ModuleRTPUtility::AssignUWord16ToBuffer(rtcpbuffer + pos, nack); + RtpUtility::AssignUWord16ToBuffer(rtcpbuffer + pos, nack); pos += 2; - ModuleRTPUtility::AssignUWord16ToBuffer(rtcpbuffer + pos, bitmask); + RtpUtility::AssignUWord16ToBuffer(rtcpbuffer + pos, bitmask); pos += 2; numOfNackFields++; } + rtcpbuffer[nackSizePos] = static_cast(2 + numOfNackFields); + if (i != nackSize) { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, - "Nack list to large for one packet."); + LOG(LS_WARNING) << "Nack list too large for one packet."; + } + + // Report stats. + NACKStringBuilder stringBuilder; + for (int idx = 0; idx < i; ++idx) { + stringBuilder.PushNACK(nackList[idx]); + nack_stats_.ReportRequest(nackList[idx]); } - rtcpbuffer[nackSizePos] = static_cast(2 + numOfNackFields); *nackString = stringBuilder.GetResult(); + packet_type_counter_.nack_requests = nack_stats_.requests(); + packet_type_counter_.unique_nack_requests = nack_stats_.unique_requests(); return 0; } @@ -1517,13 +1432,13 @@ rtcpbuffer[pos++]=(uint8_t)(1 + _CSRCs); // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC); pos += 4; // add CSRCs for(int i = 0; i < _CSRCs; i++) { - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _CSRC[i]); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _CSRC[i]); pos += 4; } } else @@ -1537,7 +1452,7 @@ rtcpbuffer[pos++]=(uint8_t)1; // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC); pos += 4; } return 0; @@ -1566,7 +1481,7 @@ buffer[pos++] = 4; // XR packet length. // Add our own SSRC. - ModuleRTPUtility::AssignUWord32ToBuffer(buffer + pos, _SSRC); + RtpUtility::AssignUWord32ToBuffer(buffer + pos, _SSRC); pos += 4; // 0 1 2 3 @@ -1586,9 +1501,9 @@ buffer[pos++] = 2; // Block length. // NTP timestamp. - ModuleRTPUtility::AssignUWord32ToBuffer(buffer + pos, ntp_sec); + RtpUtility::AssignUWord32ToBuffer(buffer + pos, ntp_sec); pos += 4; - ModuleRTPUtility::AssignUWord32ToBuffer(buffer + pos, ntp_frac); + RtpUtility::AssignUWord32ToBuffer(buffer + pos, ntp_frac); pos += 4; return 0; @@ -1609,7 +1524,7 @@ buffer[pos++] = 5; // XR packet length. // Add our own SSRC. - ModuleRTPUtility::AssignUWord32ToBuffer(buffer + pos, _SSRC); + RtpUtility::AssignUWord32ToBuffer(buffer + pos, _SSRC); pos += 4; // 0 1 2 3 @@ -1634,11 +1549,11 @@ buffer[pos++] = 3; // Block length. // NTP timestamp. - ModuleRTPUtility::AssignUWord32ToBuffer(buffer + pos, info.sourceSSRC); + RtpUtility::AssignUWord32ToBuffer(buffer + pos, info.sourceSSRC); pos += 4; - ModuleRTPUtility::AssignUWord32ToBuffer(buffer + pos, info.lastRR); + RtpUtility::AssignUWord32ToBuffer(buffer + pos, info.lastRR); pos += 4; - ModuleRTPUtility::AssignUWord32ToBuffer(buffer + pos, info.delaySinceLastRR); + RtpUtility::AssignUWord32ToBuffer(buffer + pos, info.delaySinceLastRR); pos += 4; return 0; @@ -1664,7 +1579,7 @@ pos++; // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _SSRC); pos += 4; // Add a VoIP metrics block @@ -1674,7 +1589,7 @@ rtcpbuffer[pos++]=8; // Add the remote SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + pos, _remoteSSRC); pos += 4; rtcpbuffer[pos++] = _xrVoIPMetric.lossRate; @@ -1727,8 +1642,7 @@ CriticalSectionScoped lock(_criticalSectionRTCPSender); if(_method == kRtcpOff) { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, - "%s invalid state", __FUNCTION__); + LOG(LS_WARNING) << "Can't send rtcp if it is disabled."; return -1; } } @@ -1935,8 +1849,9 @@ return position; } TRACE_EVENT_INSTANT0("webrtc_rtp", "RTCPSender::PLI"); - _pliCount++; - TRACE_COUNTER_ID1("webrtc_rtp", "RTCP_PLICount", _SSRC, _pliCount); + ++packet_type_counter_.pli_packets; + TRACE_COUNTER_ID1("webrtc_rtp", "RTCP_PLICount", _SSRC, + packet_type_counter_.pli_packets); } if(rtcpPacketTypeFlags & kRtcpFir) { @@ -1947,9 +1862,9 @@ return position; } TRACE_EVENT_INSTANT0("webrtc_rtp", "RTCPSender::FIR"); - _fullIntraRequestCount++; + ++packet_type_counter_.fir_packets; TRACE_COUNTER_ID1("webrtc_rtp", "RTCP_FIRCount", _SSRC, - _fullIntraRequestCount); + packet_type_counter_.fir_packets); } if(rtcpPacketTypeFlags & kRtcpSli) { @@ -2032,8 +1947,9 @@ } TRACE_EVENT_INSTANT1("webrtc_rtp", "RTCPSender::NACK", "nacks", TRACE_STR_COPY(nackString.c_str())); - _nackCount++; - TRACE_COUNTER_ID1("webrtc_rtp", "RTCP_NACKCount", _SSRC, _nackCount); + ++packet_type_counter_.nack_packets; + TRACE_COUNTER_ID1("webrtc_rtp", "RTCP_NACKCount", _SSRC, + packet_type_counter_.nack_packets); } if(rtcpPacketTypeFlags & kRtcpXrVoipMetric) { @@ -2130,6 +2046,7 @@ int32_t RTCPSender::SetCSRCStatus(const bool include) { + CriticalSectionScoped lock(_criticalSectionRTCPSender); _includeCSRCs = include; return 0; } @@ -2138,13 +2055,7 @@ RTCPSender::SetCSRCs(const uint32_t arrOfCSRC[kRtpCsrcSize], const uint8_t arrLength) { - if(arrLength > kRtpCsrcSize) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - assert(false); - return -1; - } - + assert(arrLength <= kRtpCsrcSize); CriticalSectionScoped lock(_criticalSectionRTCPSender); for(int i = 0; i < arrLength;i++) @@ -2163,7 +2074,7 @@ { if(length %4 != 0) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); + LOG(LS_ERROR) << "Failed to SetApplicationSpecificData."; return -1; } CriticalSectionScoped lock(_criticalSectionRTCPSender); @@ -2209,17 +2120,10 @@ uint8_t& numberOfReportBlocks, const uint32_t NTPsec, const uint32_t NTPfrac) { - // sanity one block - if(pos + 24 >= IP_PACKET_SIZE) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s invalid argument", __FUNCTION__); - return -1; - } numberOfReportBlocks = external_report_blocks_.size(); numberOfReportBlocks += internal_report_blocks_.size(); if ((pos + numberOfReportBlocks * 24) >= IP_PACKET_SIZE) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s invalid argument", __FUNCTION__); + LOG(LS_WARNING) << "Can't fit all report blocks."; return -1; } pos = WriteReportBlocksToBuffer(rtcpbuffer, pos, internal_report_blocks_); @@ -2242,33 +2146,33 @@ RTCPReportBlock* reportBlock = it->second; if (reportBlock) { // Remote SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+position, remoteSSRC); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + position, remoteSSRC); position += 4; // fraction lost rtcpbuffer[position++] = reportBlock->fractionLost; // cumulative loss - ModuleRTPUtility::AssignUWord24ToBuffer(rtcpbuffer+position, - reportBlock->cumulativeLost); + RtpUtility::AssignUWord24ToBuffer(rtcpbuffer + position, + reportBlock->cumulativeLost); position += 3; // extended highest seq_no, contain the highest sequence number received - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+position, - reportBlock->extendedHighSeqNum); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + position, + reportBlock->extendedHighSeqNum); position += 4; // Jitter - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+position, - reportBlock->jitter); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + position, + reportBlock->jitter); position += 4; - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+position, - reportBlock->lastSR); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + position, + reportBlock->lastSR); position += 4; - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+position, - reportBlock->delaySinceLastSR); + RtpUtility::AssignUWord32ToBuffer(rtcpbuffer + position, + reportBlock->delaySinceLastSR); position += 4; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_sender.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_sender.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_sender.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_sender.h 2015-02-03 14:33:36.000000000 +0000 @@ -15,6 +15,7 @@ #include #include +#include "webrtc/base/thread_annotations.h" #include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h" #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" #include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h" @@ -50,13 +51,12 @@ { public: struct FeedbackState { - explicit FeedbackState(ModuleRtpRtcpImpl* module); FeedbackState(); uint8_t send_payload_type; uint32_t frequency_hz; - uint32_t packet_count_sent; - uint32_t byte_count_sent; + uint32_t packets_sent; + uint32_t media_bytes_sent; uint32_t send_bitrate; uint32_t last_rr_ntp_secs; @@ -74,10 +74,6 @@ ReceiveStatistics* receive_statistics); virtual ~RTCPSender(); - void ChangeUniqueId(const int32_t id); - - int32_t Init(); - int32_t RegisterSendTransport(Transport* outgoingTransport); RTCPMethod Status() const; @@ -100,7 +96,6 @@ int32_t SetCameraDelay(const int32_t delayMS); - int32_t CNAME(char cName[RTCP_CNAME_SIZE]); int32_t SetCNAME(const char cName[RTCP_CNAME_SIZE]); int32_t AddMixedCNAME(const uint32_t SSRC, @@ -183,16 +178,17 @@ void SetTargetBitrate(unsigned int target_bitrate); + void GetPacketTypeCounter(RtcpPacketTypeCounter* packet_counter) const; + private: int32_t SendToNetwork(const uint8_t* dataBuffer, const uint16_t length); - void UpdatePacketRate(); - int32_t WriteAllReportBlocksToBuffer(uint8_t* rtcpbuffer, int pos, uint8_t& numberOfReportBlocks, const uint32_t NTPsec, - const uint32_t NTPfrac); + const uint32_t NTPfrac) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); int32_t WriteReportBlocksToBuffer( uint8_t* rtcpbuffer, @@ -213,12 +209,14 @@ uint8_t* rtcpbuffer, int& pos, uint32_t NTPsec, - uint32_t NTPfrac); + uint32_t NTPfrac) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); int32_t BuildRR(uint8_t* rtcpbuffer, int& pos, const uint32_t NTPsec, - const uint32_t NTPfrac); + const uint32_t NTPfrac) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); int PrepareRTCP( const FeedbackState& feedback_state, @@ -235,122 +233,142 @@ int32_t BuildExtendedJitterReport( uint8_t* rtcpbuffer, int& pos, - const uint32_t jitterTransmissionTimeOffset); + const uint32_t jitterTransmissionTimeOffset) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); - int32_t BuildSDEC(uint8_t* rtcpbuffer, int& pos); - int32_t BuildPLI(uint8_t* rtcpbuffer, int& pos); - int32_t BuildREMB(uint8_t* rtcpbuffer, int& pos); - int32_t BuildTMMBR(ModuleRtpRtcpImpl* module, - uint8_t* rtcpbuffer, - int& pos); - int32_t BuildTMMBN(uint8_t* rtcpbuffer, int& pos); - int32_t BuildAPP(uint8_t* rtcpbuffer, int& pos); - int32_t BuildVoIPMetric(uint8_t* rtcpbuffer, int& pos); - int32_t BuildBYE(uint8_t* rtcpbuffer, int& pos); - int32_t BuildFIR(uint8_t* rtcpbuffer, int& pos, bool repeat); - int32_t BuildSLI(uint8_t* rtcpbuffer, - int& pos, - const uint8_t pictureID); + int32_t BuildSDEC(uint8_t* rtcpbuffer, int& pos) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); + int32_t BuildPLI(uint8_t* rtcpbuffer, int& pos) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); + int32_t BuildREMB(uint8_t* rtcpbuffer, int& pos) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); + int32_t BuildTMMBR(ModuleRtpRtcpImpl* module, uint8_t* rtcpbuffer, int& pos) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); + int32_t BuildTMMBN(uint8_t* rtcpbuffer, int& pos) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); + int32_t BuildAPP(uint8_t* rtcpbuffer, int& pos) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); + int32_t BuildVoIPMetric(uint8_t* rtcpbuffer, int& pos) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); + int32_t BuildBYE(uint8_t* rtcpbuffer, int& pos) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); + int32_t BuildFIR(uint8_t* rtcpbuffer, int& pos, bool repeat) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); + int32_t BuildSLI(uint8_t* rtcpbuffer, int& pos, const uint8_t pictureID) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); int32_t BuildRPSI(uint8_t* rtcpbuffer, int& pos, const uint64_t pictureID, - const uint8_t payloadType); + const uint8_t payloadType) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); int32_t BuildNACK(uint8_t* rtcpbuffer, int& pos, const int32_t nackSize, const uint16_t* nackList, - std::string* nackString); - + std::string* nackString) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); int32_t BuildReceiverReferenceTime(uint8_t* buffer, int& pos, uint32_t ntp_sec, - uint32_t ntp_frac); + uint32_t ntp_frac) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); int32_t BuildDlrr(uint8_t* buffer, int& pos, - const RtcpReceiveTimeInfo& info); + const RtcpReceiveTimeInfo& info) + EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPSender); private: - int32_t _id; + const int32_t _id; const bool _audio; - Clock* _clock; - RTCPMethod _method; + Clock* const _clock; + RTCPMethod _method GUARDED_BY(_criticalSectionRTCPSender); CriticalSectionWrapper* _criticalSectionTransport; - Transport* _cbTransport; + Transport* _cbTransport GUARDED_BY(_criticalSectionTransport); CriticalSectionWrapper* _criticalSectionRTCPSender; - bool _usingNack; - bool _sending; - bool _sendTMMBN; - bool _REMB; - bool _sendREMB; - bool _TMMBR; - bool _IJ; - - int64_t _nextTimeToSendRTCP; - - uint32_t start_timestamp_; - uint32_t last_rtp_timestamp_; - int64_t last_frame_capture_time_ms_; - uint32_t _SSRC; - uint32_t _remoteSSRC; // SSRC that we receive on our RTP channel - char _CNAME[RTCP_CNAME_SIZE]; - - - ReceiveStatistics* receive_statistics_; - std::map internal_report_blocks_; - std::map external_report_blocks_; - std::map _csrcCNAMEs; + bool _usingNack GUARDED_BY(_criticalSectionRTCPSender); + bool _sending GUARDED_BY(_criticalSectionRTCPSender); + bool _sendTMMBN GUARDED_BY(_criticalSectionRTCPSender); + bool _REMB GUARDED_BY(_criticalSectionRTCPSender); + bool _sendREMB GUARDED_BY(_criticalSectionRTCPSender); + bool _TMMBR GUARDED_BY(_criticalSectionRTCPSender); + bool _IJ GUARDED_BY(_criticalSectionRTCPSender); + + int64_t _nextTimeToSendRTCP GUARDED_BY(_criticalSectionRTCPSender); + + uint32_t start_timestamp_ GUARDED_BY(_criticalSectionRTCPSender); + uint32_t last_rtp_timestamp_ GUARDED_BY(_criticalSectionRTCPSender); + int64_t last_frame_capture_time_ms_ GUARDED_BY(_criticalSectionRTCPSender); + uint32_t _SSRC GUARDED_BY(_criticalSectionRTCPSender); + // SSRC that we receive on our RTP channel + uint32_t _remoteSSRC GUARDED_BY(_criticalSectionRTCPSender); + char _CNAME[RTCP_CNAME_SIZE] GUARDED_BY(_criticalSectionRTCPSender); + + ReceiveStatistics* receive_statistics_ + GUARDED_BY(_criticalSectionRTCPSender); + std::map internal_report_blocks_ + GUARDED_BY(_criticalSectionRTCPSender); + std::map external_report_blocks_ + GUARDED_BY(_criticalSectionRTCPSender); + std::map _csrcCNAMEs + GUARDED_BY(_criticalSectionRTCPSender); - int32_t _cameraDelayMS; + int32_t _cameraDelayMS GUARDED_BY(_criticalSectionRTCPSender); // Sent - uint32_t _lastSendReport[RTCP_NUMBER_OF_SR]; // allow packet loss and RTT above 1 sec - uint32_t _lastRTCPTime[RTCP_NUMBER_OF_SR]; - uint32_t _lastSRPacketCount[RTCP_NUMBER_OF_SR]; - uint64_t _lastSROctetCount[RTCP_NUMBER_OF_SR]; + uint32_t _lastSendReport[RTCP_NUMBER_OF_SR] GUARDED_BY( + _criticalSectionRTCPSender); // allow packet loss and RTT above 1 sec + uint32_t _lastRTCPTime[RTCP_NUMBER_OF_SR] GUARDED_BY( + _criticalSectionRTCPSender); + uint32_t _lastSRPacketCount[RTCP_NUMBER_OF_SR] GUARDED_BY( + _criticalSectionRTCPSender); + uint64_t _lastSROctetCount[RTCP_NUMBER_OF_SR] GUARDED_BY( + _criticalSectionRTCPSender); // Sent XR receiver reference time report. // . - std::map last_xr_rr_; + std::map last_xr_rr_ + GUARDED_BY(_criticalSectionRTCPSender); // send CSRCs - uint8_t _CSRCs; - uint32_t _CSRC[kRtpCsrcSize]; - bool _includeCSRCs; + uint8_t _CSRCs GUARDED_BY(_criticalSectionRTCPSender); + uint32_t _CSRC[kRtpCsrcSize] GUARDED_BY(_criticalSectionRTCPSender); + bool _includeCSRCs GUARDED_BY(_criticalSectionRTCPSender); // Full intra request - uint8_t _sequenceNumberFIR; + uint8_t _sequenceNumberFIR GUARDED_BY(_criticalSectionRTCPSender); - // REMB - uint8_t _lengthRembSSRC; - uint8_t _sizeRembSSRC; - uint32_t* _rembSSRC; - uint32_t _rembBitrate; - - TMMBRHelp _tmmbrHelp; - uint32_t _tmmbr_Send; - uint32_t _packetOH_Send; + // REMB + uint8_t _lengthRembSSRC GUARDED_BY(_criticalSectionRTCPSender); + uint8_t _sizeRembSSRC GUARDED_BY(_criticalSectionRTCPSender); + uint32_t* _rembSSRC GUARDED_BY(_criticalSectionRTCPSender); + uint32_t _rembBitrate GUARDED_BY(_criticalSectionRTCPSender); + + TMMBRHelp _tmmbrHelp GUARDED_BY(_criticalSectionRTCPSender); + uint32_t _tmmbr_Send GUARDED_BY(_criticalSectionRTCPSender); + uint32_t _packetOH_Send GUARDED_BY(_criticalSectionRTCPSender); // APP - bool _appSend; - uint8_t _appSubType; - uint32_t _appName; - uint8_t* _appData; - uint16_t _appLength; + bool _appSend GUARDED_BY(_criticalSectionRTCPSender); + uint8_t _appSubType GUARDED_BY(_criticalSectionRTCPSender); + uint32_t _appName GUARDED_BY(_criticalSectionRTCPSender); + uint8_t* _appData GUARDED_BY(_criticalSectionRTCPSender); + uint16_t _appLength GUARDED_BY(_criticalSectionRTCPSender); // True if sending of XR Receiver reference time report is enabled. - bool xrSendReceiverReferenceTimeEnabled_; + bool xrSendReceiverReferenceTimeEnabled_ + GUARDED_BY(_criticalSectionRTCPSender); // XR VoIP metric - bool _xrSendVoIPMetric; - RTCPVoIPMetric _xrVoIPMetric; + bool _xrSendVoIPMetric GUARDED_BY(_criticalSectionRTCPSender); + RTCPVoIPMetric _xrVoIPMetric GUARDED_BY(_criticalSectionRTCPSender); + + RtcpPacketTypeCounter packet_type_counter_ + GUARDED_BY(_criticalSectionRTCPSender); - // Counters - uint32_t _nackCount; - uint32_t _pliCount; - uint32_t _fullIntraRequestCount; + RTCPUtility::NackStats nack_stats_ GUARDED_BY(_criticalSectionRTCPSender); }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_sender_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_sender_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_sender_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_sender_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -227,11 +227,15 @@ void SetRTCPReceiver(RTCPReceiver* rtcp_receiver) { rtcp_receiver_ = rtcp_receiver; } - virtual int SendPacket(int /*ch*/, const void* /*data*/, int /*len*/) { + virtual int SendPacket(int /*ch*/, + const void* /*data*/, + int /*len*/) OVERRIDE { return -1; } - virtual int SendRTCPPacket(int /*ch*/, const void *packet, int packet_len) { + virtual int SendRTCPPacket(int /*ch*/, + const void *packet, + int packet_len) OVERRIDE { RTCPUtility::RTCPParserV2 rtcpParser((uint8_t*)packet, (int32_t)packet_len, true); // Allow non-compound RTCP @@ -263,7 +267,7 @@ virtual int OnReceivedPayloadData(const uint8_t* payloadData, const uint16_t payloadSize, - const WebRtcRTPHeader* rtpHeader) { + const WebRtcRTPHeader* rtpHeader) OVERRIDE { return 0; } RTCPReceiver* rtcp_receiver_; @@ -278,12 +282,13 @@ : over_use_detector_options_(), clock_(1335900000), rtp_payload_registry_(new RTPPayloadRegistry( - 0, RTPPayloadStrategy::CreateStrategy(false))), + RTPPayloadStrategy::CreateStrategy(false))), remote_bitrate_observer_(), remote_bitrate_estimator_( RemoteBitrateEstimatorFactory().Create( &remote_bitrate_observer_, &clock_, + kMimdControl, kRemoteBitrateEstimatorMinBitrateBps)), receive_statistics_(ReceiveStatistics::Create(&clock_)) { test_transport_ = new TestTransport(); @@ -303,7 +308,6 @@ rtcp_receiver_ = new RTCPReceiver(0, &clock_, rtp_rtcp_impl_); test_transport_->SetRTCPReceiver(rtcp_receiver_); // Initialize - EXPECT_EQ(0, rtcp_sender_->Init()); EXPECT_EQ(0, rtcp_sender_->RegisterSendTransport(test_transport_)); } ~RtcpSenderTest() { @@ -337,7 +341,7 @@ TEST_F(RtcpSenderTest, RtcpOff) { EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpOff)); - RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); + RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_->GetFeedbackState(); EXPECT_EQ(-1, rtcp_sender_->SendRTCP(feedback_state, kRtcpSr)); } @@ -381,7 +385,7 @@ EXPECT_EQ(0, rtcp_sender_->SetIJStatus(true)); EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound)); - RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); + RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_->GetFeedbackState(); EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpRr)); // Transmission time offset packet should be received. @@ -392,7 +396,7 @@ TEST_F(RtcpSenderTest, TestCompound_NoRtpReceived) { EXPECT_EQ(0, rtcp_sender_->SetIJStatus(true)); EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound)); - RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); + RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_->GetFeedbackState(); EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpRr)); // Transmission time offset packet should not be received. @@ -402,7 +406,7 @@ TEST_F(RtcpSenderTest, TestXrReceiverReferenceTime) { EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound)); - RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); + RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_->GetFeedbackState(); EXPECT_EQ(0, rtcp_sender_->SetSendingStatus(feedback_state, false)); rtcp_sender_->SendRtcpXrReceiverReferenceTime(true); EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpReport)); @@ -413,7 +417,7 @@ TEST_F(RtcpSenderTest, TestNoXrReceiverReferenceTimeIfSending) { EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound)); - RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); + RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_->GetFeedbackState(); EXPECT_EQ(0, rtcp_sender_->SetSendingStatus(feedback_state, true)); rtcp_sender_->SendRtcpXrReceiverReferenceTime(true); EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpReport)); @@ -424,7 +428,7 @@ TEST_F(RtcpSenderTest, TestNoXrReceiverReferenceTimeIfNotEnabled) { EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound)); - RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); + RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_->GetFeedbackState(); EXPECT_EQ(0, rtcp_sender_->SetSendingStatus(feedback_state, false)); rtcp_sender_->SendRtcpXrReceiverReferenceTime(false); EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpReport)); @@ -435,7 +439,7 @@ TEST_F(RtcpSenderTest, TestSendTimeOfXrRrReport) { EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound)); - RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); + RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_->GetFeedbackState(); EXPECT_EQ(0, rtcp_sender_->SetSendingStatus(feedback_state, false)); rtcp_sender_->SendRtcpXrReceiverReferenceTime(true); uint32_t ntp_sec; @@ -475,7 +479,7 @@ TMMBRSet bounding_set; EXPECT_EQ(0, rtcp_sender_->SetTMMBN(&bounding_set, 3)); ASSERT_EQ(0U, test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags); - RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); + RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_->GetFeedbackState(); EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state,kRtcpSr)); // We now expect the packet to show up in the rtcp_packet_info_ of // test_transport_. @@ -498,7 +502,7 @@ EXPECT_EQ(0, rtcp_sender_->SetTMMBN(&bounding_set, 3)); ASSERT_EQ(0U, test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags); - RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); + RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_->GetFeedbackState(); EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpSr)); // We now expect the packet to show up in the rtcp_packet_info_ of // test_transport_. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_utility.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_utility.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_utility.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_utility.cc 2015-02-03 14:33:36.000000000 +0000 @@ -17,6 +17,23 @@ namespace webrtc { namespace RTCPUtility { + +NackStats::NackStats() + : max_sequence_number_(0), + requests_(0), + unique_requests_(0) {} + +NackStats::~NackStats() {} + +void NackStats::ReportRequest(uint16_t sequence_number) { + if (requests_ == 0 || + webrtc::IsNewerSequenceNumber(sequence_number, max_sequence_number_)) { + max_sequence_number_ = sequence_number; + ++unique_requests_; + } + ++requests_; +} + uint32_t MidNtp(uint32_t ntp_sec, uint32_t ntp_frac) { return (ntp_sec << 16) + (ntp_frac >> 16); } // end RTCPUtility @@ -1266,31 +1283,27 @@ } } -bool -RTCPUtility::RTCPParserV2::ParseRPSIItem() -{ - // RFC 4585 6.3.3. Reference Picture Selection Indication (RPSI) - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | PB |0| Payload Type| Native RPSI bit string | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | defined per codec ... | Padding (0) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ +bool RTCPUtility::RTCPParserV2::ParseRPSIItem() { + + // RFC 4585 6.3.3. Reference Picture Selection Indication (RPSI). + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | PB |0| Payload Type| Native RPSI bit string | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | defined per codec ... | Padding (0) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - if (length < 4) - { + if (length < 4) { _state = State_TopLevel; EndCurrentBlock(); return false; } - if(length > 2+RTCP_RPSI_DATA_SIZE) - { + if (length > 2 + RTCP_RPSI_DATA_SIZE) { _state = State_TopLevel; EndCurrentBlock(); @@ -1299,12 +1312,14 @@ _packetType = kRtcpPsfbRpsiCode; - uint8_t paddingBits = *_ptrRTCPData++; + uint8_t padding_bits = *_ptrRTCPData++; _packet.RPSI.PayloadType = *_ptrRTCPData++; - memcpy(_packet.RPSI.NativeBitString, _ptrRTCPData, length-2); + memcpy(_packet.RPSI.NativeBitString, _ptrRTCPData, length - 2); + _ptrRTCPData += length - 2; - _packet.RPSI.NumberOfValidBits = uint16_t(length-2)*8 - paddingBits; + _packet.RPSI.NumberOfValidBits = + static_cast(length - 2) * 8 - padding_bits; return true; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_utility.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_utility.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_utility.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_utility.h 2015-02-03 14:33:36.000000000 +0000 @@ -19,6 +19,29 @@ namespace webrtc { namespace RTCPUtility { + +class NackStats { + public: + NackStats(); + ~NackStats(); + + // Updates stats with requested sequence number. + // This function should be called for each NACK request to calculate the + // number of unique NACKed RTP packets. + void ReportRequest(uint16_t sequence_number); + + // Gets the number of NACKed RTP packets. + uint32_t requests() const { return requests_; } + + // Gets the number of unique NACKed RTP packets. + uint32_t unique_requests() const { return unique_requests_; } + + private: + uint16_t max_sequence_number_; + uint32_t requests_; + uint32_t unique_requests_; +}; + uint32_t MidNtp(uint32_t ntp_sec, uint32_t ntp_frac); // CNAME diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_utility_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_utility_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_utility_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_utility_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "testing/gtest/include/gtest/gtest.h" + +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" + +namespace webrtc { + +TEST(RtcpUtilityTest, MidNtp) { + const uint32_t kNtpSec = 0x12345678; + const uint32_t kNtpFrac = 0x23456789; + const uint32_t kNtpMid = 0x56782345; + EXPECT_EQ(kNtpMid, RTCPUtility::MidNtp(kNtpSec, kNtpFrac)); +} + +TEST(RtcpUtilityTest, NackRequests) { + RTCPUtility::NackStats stats; + EXPECT_EQ(0U, stats.unique_requests()); + EXPECT_EQ(0U, stats.requests()); + stats.ReportRequest(10); + EXPECT_EQ(1U, stats.unique_requests()); + EXPECT_EQ(1U, stats.requests()); + + stats.ReportRequest(10); + EXPECT_EQ(1U, stats.unique_requests()); + stats.ReportRequest(11); + EXPECT_EQ(2U, stats.unique_requests()); + + stats.ReportRequest(11); + EXPECT_EQ(2U, stats.unique_requests()); + stats.ReportRequest(13); + EXPECT_EQ(3U, stats.unique_requests()); + + stats.ReportRequest(11); + EXPECT_EQ(3U, stats.unique_requests()); + EXPECT_EQ(6U, stats.requests()); +} + +TEST(RtcpUtilityTest, NackRequestsWithWrap) { + RTCPUtility::NackStats stats; + stats.ReportRequest(65534); + EXPECT_EQ(1U, stats.unique_requests()); + + stats.ReportRequest(65534); + EXPECT_EQ(1U, stats.unique_requests()); + stats.ReportRequest(65535); + EXPECT_EQ(2U, stats.unique_requests()); + + stats.ReportRequest(65535); + EXPECT_EQ(2U, stats.unique_requests()); + stats.ReportRequest(0); + EXPECT_EQ(3U, stats.unique_requests()); + + stats.ReportRequest(65535); + EXPECT_EQ(3U, stats.unique_requests()); + stats.ReportRequest(0); + EXPECT_EQ(3U, stats.unique_requests()); + stats.ReportRequest(1); + EXPECT_EQ(4U, stats.unique_requests()); + EXPECT_EQ(8U, stats.requests()); +} + +} // namespace webrtc + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_fec_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_fec_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_fec_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_fec_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -41,7 +41,7 @@ class RtpFecTest : public ::testing::Test { protected: RtpFecTest() - : fec_(new ForwardErrorCorrection(0)), ssrc_(rand()), fec_seq_num_(0) {} + : fec_(new ForwardErrorCorrection()), ssrc_(rand()), fec_seq_num_(0) {} ForwardErrorCorrection* fec_; int ssrc_; @@ -86,43 +86,6 @@ void TearDown(); }; -// TODO(marpan): Consider adding table for input/output to simplify tests. - -TEST_F(RtpFecTest, HandleIncorrectInputs) { - int kNumImportantPackets = 0; - bool kUseUnequalProtection = false; - uint8_t kProtectionFactor = 60; - - // Media packet list is empty. - EXPECT_EQ(-1, fec_->GenerateFEC(media_packet_list_, kProtectionFactor, - kNumImportantPackets, kUseUnequalProtection, - webrtc::kFecMaskBursty, &fec_packet_list_)); - - int num_media_packets = 10; - ConstructMediaPackets(num_media_packets); - - kNumImportantPackets = -1; - // Number of important packets below 0. - EXPECT_EQ(-1, fec_->GenerateFEC(media_packet_list_, kProtectionFactor, - kNumImportantPackets, kUseUnequalProtection, - webrtc::kFecMaskBursty, &fec_packet_list_)); - - kNumImportantPackets = 12; - // Number of important packets greater than number of media packets. - EXPECT_EQ(-1, fec_->GenerateFEC(media_packet_list_, kProtectionFactor, - kNumImportantPackets, kUseUnequalProtection, - webrtc::kFecMaskBursty, &fec_packet_list_)); - - num_media_packets = kMaxNumberMediaPackets + 1; - ConstructMediaPackets(num_media_packets); - - kNumImportantPackets = 0; - // Number of media packet is above maximum allowed (kMaxNumberMediaPackets). - EXPECT_EQ(-1, fec_->GenerateFEC(media_packet_list_, kProtectionFactor, - kNumImportantPackets, kUseUnequalProtection, - webrtc::kFecMaskBursty, &fec_packet_list_)); -} - TEST_F(RtpFecTest, FecRecoveryNoLoss) { const int kNumImportantPackets = 0; const bool kUseUnequalProtection = false; @@ -192,6 +155,174 @@ EXPECT_FALSE(IsRecoveryComplete()); } +// Verify that we don't use an old FEC packet for FEC decoding. +TEST_F(RtpFecTest, FecRecoveryWithSeqNumGapTwoFrames) { + const int kNumImportantPackets = 0; + const bool kUseUnequalProtection = false; + uint8_t kProtectionFactor = 20; + + // Two frames: first frame (old) with two media packets and 1 FEC packet. + // Second frame (new) with 3 media packets, and no FEC packets. + // ---Frame 1---- ----Frame 2------ + // #0(media) #1(media) #2(FEC) #65535(media) #0(media) #1(media). + // If we lose either packet 0 or 1 of second frame, FEC decoding should not + // try to decode using "old" FEC packet #2. + + // Construct media packets for first frame, starting at sequence number 0. + fec_seq_num_ = ConstructMediaPacketsSeqNum(2, 0); + + EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, kProtectionFactor, + kNumImportantPackets, kUseUnequalProtection, + webrtc::kFecMaskBursty, &fec_packet_list_)); + // Expect 1 FEC packet. + EXPECT_EQ(1, static_cast(fec_packet_list_.size())); + // Add FEC packet (seq#2) of this first frame to received list (i.e., assume + // the two media packet were lost). + memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); + ReceivedPackets(fec_packet_list_, fec_loss_mask_, true); + + // Construct media packets for second frame, with sequence number wrap. + ClearList(&media_packet_list_); + fec_seq_num_ = ConstructMediaPacketsSeqNum(3, 65535); + + // Expect 3 media packets for this frame. + EXPECT_EQ(3, static_cast(media_packet_list_.size())); + + // Second media packet lost (seq#0). + memset(media_loss_mask_, 0, sizeof(media_loss_mask_)); + media_loss_mask_[1] = 1; + // Add packets #65535, and #1 to received list. + ReceivedPackets(media_packet_list_, media_loss_mask_, false); + + EXPECT_EQ(0, + fec_->DecodeFEC(&received_packet_list_, &recovered_packet_list_)); + + // Expect that no decoding is done to get missing packet (seq#0) of second + // frame, using old FEC packet (seq#2) from first (old) frame. So number of + // recovered packets is 2, and not equal to number of media packets (=3). + EXPECT_EQ(2, static_cast(recovered_packet_list_.size())); + EXPECT_TRUE(recovered_packet_list_.size() != media_packet_list_.size()); + FreeRecoveredPacketList(); +} + +// Verify we can still recovery frame if sequence number wrap occurs within +// the frame and FEC packet following wrap is received after media packets. +TEST_F(RtpFecTest, FecRecoveryWithSeqNumGapOneFrameRecovery) { + const int kNumImportantPackets = 0; + const bool kUseUnequalProtection = false; + uint8_t kProtectionFactor = 20; + + // One frame, with sequence number wrap in media packets. + // -----Frame 1---- + // #65534(media) #65535(media) #0(media) #1(FEC). + fec_seq_num_ = ConstructMediaPacketsSeqNum(3, 65534); + + EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, kProtectionFactor, + kNumImportantPackets, kUseUnequalProtection, + webrtc::kFecMaskBursty, &fec_packet_list_)); + + // Expect 1 FEC packet. + EXPECT_EQ(1, static_cast(fec_packet_list_.size())); + + // Lose one media packet (seq# 65535). + memset(media_loss_mask_, 0, sizeof(media_loss_mask_)); + memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); + media_loss_mask_[1] = 1; + ReceivedPackets(media_packet_list_, media_loss_mask_, false); + // Add FEC packet to received list following the media packets. + ReceivedPackets(fec_packet_list_, fec_loss_mask_, true); + + EXPECT_EQ(0, + fec_->DecodeFEC(&received_packet_list_, &recovered_packet_list_)); + + // Expect 3 media packets in recovered list, and complete recovery. + // Wrap-around won't remove FEC packet, as it follows the wrap. + EXPECT_EQ(3, static_cast(recovered_packet_list_.size())); + EXPECT_TRUE(IsRecoveryComplete()); + FreeRecoveredPacketList(); +} + +// Sequence number wrap occurs within the FEC packets for the frame. +// In this case we will discard FEC packet and full recovery is not expected. +// Same problem will occur if wrap is within media packets but FEC packet is +// received before the media packets. This may be improved if timing information +// is used to detect old FEC packets. +// TODO(marpan): Update test if wrap-around handling changes in FEC decoding. +TEST_F(RtpFecTest, FecRecoveryWithSeqNumGapOneFrameNoRecovery) { + const int kNumImportantPackets = 0; + const bool kUseUnequalProtection = false; + uint8_t kProtectionFactor = 200; + + // 1 frame: 3 media packets and 2 FEC packets. + // Sequence number wrap in FEC packets. + // -----Frame 1---- + // #65532(media) #65533(media) #65534(media) #65535(FEC) #0(FEC). + fec_seq_num_ = ConstructMediaPacketsSeqNum(3, 65532); + + EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, kProtectionFactor, + kNumImportantPackets, kUseUnequalProtection, + webrtc::kFecMaskBursty, &fec_packet_list_)); + + // Expect 2 FEC packets. + EXPECT_EQ(2, static_cast(fec_packet_list_.size())); + + // Lose the last two media packets (seq# 65533, 65534). + memset(media_loss_mask_, 0, sizeof(media_loss_mask_)); + memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); + media_loss_mask_[1] = 1; + media_loss_mask_[2] = 1; + ReceivedPackets(media_packet_list_, media_loss_mask_, false); + ReceivedPackets(fec_packet_list_, fec_loss_mask_, true); + + EXPECT_EQ(0, + fec_->DecodeFEC(&received_packet_list_, &recovered_packet_list_)); + + // The two FEC packets are received and should allow for complete recovery, + // but because of the wrap the second FEC packet will be discarded, and only + // one media packet is recoverable. So exepct 2 media packets on recovered + // list and no complete recovery. + EXPECT_EQ(2, static_cast(recovered_packet_list_.size())); + EXPECT_TRUE(recovered_packet_list_.size() != media_packet_list_.size()); + EXPECT_FALSE(IsRecoveryComplete()); + FreeRecoveredPacketList(); +} + +// Verify we can still recovery frame if FEC is received before media packets. +TEST_F(RtpFecTest, FecRecoveryWithFecOutOfOrder) { + const int kNumImportantPackets = 0; + const bool kUseUnequalProtection = false; + uint8_t kProtectionFactor = 20; + + // One frame: 3 media packets, 1 FEC packet. + // -----Frame 1---- + // #0(media) #1(media) #2(media) #3(FEC). + fec_seq_num_ = ConstructMediaPacketsSeqNum(3, 0); + + EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, kProtectionFactor, + kNumImportantPackets, kUseUnequalProtection, + webrtc::kFecMaskBursty, &fec_packet_list_)); + + // Expect 1 FEC packet. + EXPECT_EQ(1, static_cast(fec_packet_list_.size())); + + // Lose one media packet (seq# 1). + memset(media_loss_mask_, 0, sizeof(media_loss_mask_)); + memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); + media_loss_mask_[1] = 1; + // Add FEC packet to received list before the media packets. + ReceivedPackets(fec_packet_list_, fec_loss_mask_, true); + // Add media packets to received list. + ReceivedPackets(media_packet_list_, media_loss_mask_, false); + + EXPECT_EQ(0, + fec_->DecodeFEC(&received_packet_list_, &recovered_packet_list_)); + + // Expect 3 media packets in recovered list, and complete recovery. + EXPECT_EQ(3, static_cast(recovered_packet_list_.size())); + EXPECT_TRUE(IsRecoveryComplete()); + FreeRecoveredPacketList(); +} + // Test 50% protection with random mask type: Two cases are considered: // a 50% non-consecutive loss which can be fully recovered, and a 50% // consecutive loss which cannot be fully recovered. @@ -662,8 +793,6 @@ EXPECT_FALSE(IsRecoveryComplete()); } -// TODO(marpan): Add more test cases. - void RtpFecTest::TearDown() { fec_->ResetState(&recovered_packet_list_); delete fec_; @@ -737,7 +866,7 @@ // For media packets, the sequence number and marker bit is // obtained from RTP header. These were set in ConstructMediaPackets(). received_packet->seq_num = - webrtc::ModuleRTPUtility::BufferToUWord16(&packet->data[2]); + webrtc::RtpUtility::BufferToUWord16(&packet->data[2]); } else { // The sequence number, marker bit, and ssrc number are defined in the // RTP header of the FEC packet, which is not constructed in this test. @@ -792,12 +921,11 @@ // Only push one (fake) frame to the FEC. media_packet->data[1] &= 0x7f; - webrtc::ModuleRTPUtility::AssignUWord16ToBuffer(&media_packet->data[2], - sequence_number); - webrtc::ModuleRTPUtility::AssignUWord32ToBuffer(&media_packet->data[4], - time_stamp); - webrtc::ModuleRTPUtility::AssignUWord32ToBuffer(&media_packet->data[8], - ssrc_); + webrtc::RtpUtility::AssignUWord16ToBuffer(&media_packet->data[2], + sequence_number); + webrtc::RtpUtility::AssignUWord32ToBuffer(&media_packet->data[4], + time_stamp); + webrtc::RtpUtility::AssignUWord32ToBuffer(&media_packet->data[8], ssrc_); // Generate random values for payload. for (int j = 12; j < media_packet->length; ++j) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtp_format.h" + +#include "webrtc/modules/rtp_rtcp/source/rtp_format_h264.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h" + +namespace webrtc { +RtpPacketizer* RtpPacketizer::Create(RtpVideoCodecTypes type, + size_t max_payload_len, + const RTPVideoTypeHeader* rtp_type_header, + FrameType frame_type) { + switch (type) { + case kRtpVideoH264: + return new RtpPacketizerH264(frame_type, max_payload_len); + case kRtpVideoVp8: + assert(rtp_type_header != NULL); + return new RtpPacketizerVp8(rtp_type_header->VP8, max_payload_len); + case kRtpVideoGeneric: + return new RtpPacketizerGeneric(frame_type, max_payload_len); + case kRtpVideoNone: + assert(false); + } + return NULL; +} + +RtpDepacketizer* RtpDepacketizer::Create(RtpVideoCodecTypes type) { + switch (type) { + case kRtpVideoH264: + return new RtpDepacketizerH264(); + case kRtpVideoVp8: + return new RtpDepacketizerVp8(); + case kRtpVideoGeneric: + return new RtpDepacketizerGeneric(); + case kRtpVideoNone: + assert(false); + } + return NULL; +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format.h 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" + +namespace webrtc { + +class RtpPacketizer { + public: + static RtpPacketizer* Create(RtpVideoCodecTypes type, + size_t max_payload_len, + const RTPVideoTypeHeader* rtp_type_header, + FrameType frame_type); + + virtual ~RtpPacketizer() {} + + virtual void SetPayloadData(const uint8_t* payload_data, + size_t payload_size, + const RTPFragmentationHeader* fragmentation) = 0; + + // Get the next payload with payload header. + // buffer is a pointer to where the output will be written. + // bytes_to_send is an output variable that will contain number of bytes + // written to buffer. The parameter last_packet is true for the last packet of + // the frame, false otherwise (i.e., call the function again to get the + // next packet). + // Returns true on success or false if there was no payload to packetize. + virtual bool NextPacket(uint8_t* buffer, + size_t* bytes_to_send, + bool* last_packet) = 0; + + virtual ProtectionType GetProtectionType() = 0; + + virtual StorageType GetStorageType(uint32_t retransmission_settings) = 0; + + virtual std::string ToString() = 0; +}; + +class RtpDepacketizer { + public: + struct ParsedPayload { + const uint8_t* payload; + size_t payload_length; + FrameType frame_type; + RTPTypeHeader type; + }; + + static RtpDepacketizer* Create(RtpVideoCodecTypes type); + + virtual ~RtpDepacketizer() {} + + // Parses the RTP payload, parsed result will be saved in |parsed_payload|. + virtual bool Parse(ParsedPayload* parsed_payload, + const uint8_t* payload_data, + size_t payload_data_length) = 0; +}; +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc 2015-02-03 14:33:36.000000000 +0000 @@ -8,135 +8,312 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include // memcpy - -#ifdef WIN32 -#include -#else -#include -#endif +#include +#include "webrtc/modules/interface/module_common_types.h" #include "webrtc/modules/rtp_rtcp/source/rtp_format_h264.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" #include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { +namespace { + +enum Nalu { + kSlice = 1, + kIdr = 5, + kSei = 6, + kSps = 7, + kPps = 8, + kStapA = 24, + kFuA = 28 +}; + +static const size_t kNalHeaderSize = 1; +static const size_t kFuAHeaderSize = 2; +static const size_t kLengthFieldSize = 2; + +// Bit masks for FU (A and B) indicators. +enum NalDefs { kFBit = 0x80, kNriMask = 0x60, kTypeMask = 0x1F }; + +// Bit masks for FU (A and B) headers. +enum FuDefs { kSBit = 0x80, kEBit = 0x40, kRBit = 0x20 }; + +void ParseSingleNalu(RtpDepacketizer::ParsedPayload* parsed_payload, + const uint8_t* payload_data, + size_t payload_data_length) { + parsed_payload->type.Video.width = 0; + parsed_payload->type.Video.height = 0; + parsed_payload->type.Video.codec = kRtpVideoH264; + parsed_payload->type.Video.isFirstPacket = true; + RTPVideoHeaderH264* h264_header = + &parsed_payload->type.Video.codecHeader.H264; + h264_header->single_nalu = true; + h264_header->stap_a = false; + + uint8_t nal_type = payload_data[0] & kTypeMask; + if (nal_type == kStapA) { + nal_type = payload_data[3] & kTypeMask; + h264_header->stap_a = true; + } + + switch (nal_type) { + case kSps: + case kPps: + case kIdr: + parsed_payload->frame_type = kVideoFrameKey; + break; + default: + parsed_payload->frame_type = kVideoFrameDelta; + break; + } +} -RtpFormatH264::RtpFormatH264(const uint8_t* payload_data, - uint32_t payload_size, - int max_payload_len) - : payload_data_(payload_data), - payload_size_(static_cast(payload_size)), - max_payload_len_(static_cast(max_payload_len)), - fragments_(0), - fragment_size_(0), - next_fragment_(-1) { - if (payload_size_ <= max_payload_len_) { - fragments_ = 0; +void ParseFuaNalu(RtpDepacketizer::ParsedPayload* parsed_payload, + const uint8_t* payload_data, + size_t payload_data_length, + size_t* offset) { + uint8_t fnri = payload_data[0] & (kFBit | kNriMask); + uint8_t original_nal_type = payload_data[1] & kTypeMask; + bool first_fragment = (payload_data[1] & kSBit) > 0; + + uint8_t original_nal_header = fnri | original_nal_type; + if (first_fragment) { + *offset = kNalHeaderSize; + uint8_t* payload = const_cast(payload_data + *offset); + payload[0] = original_nal_header; } else { - fragment_size_ = max_payload_len_ - (kFuAHeaderOffset+kFuAHeaderSize); - fragments_ = ((payload_size_ - kNalHeaderSize) + (fragment_size_-1)) / - fragment_size_; - next_fragment_ = 0; + *offset = kFuAHeaderSize; } + + if (original_nal_type == kIdr) { + parsed_payload->frame_type = kVideoFrameKey; + } else { + parsed_payload->frame_type = kVideoFrameDelta; + } + parsed_payload->type.Video.width = 0; + parsed_payload->type.Video.height = 0; + parsed_payload->type.Video.codec = kRtpVideoH264; + parsed_payload->type.Video.isFirstPacket = first_fragment; + RTPVideoHeaderH264* h264_header = + &parsed_payload->type.Video.codecHeader.H264; + h264_header->single_nalu = false; + h264_header->stap_a = false; } +} // namespace -RtpFormatH264::~RtpFormatH264() { +RtpPacketizerH264::RtpPacketizerH264(FrameType frame_type, + size_t max_payload_len) + : payload_data_(NULL), + payload_size_(0), + max_payload_len_(max_payload_len), + frame_type_(frame_type) { } -int RtpFormatH264::NextPacket(uint8_t* buffer, - int* bytes_to_send, - bool* last_packet) { - if (next_fragment_ == fragments_) { - *bytes_to_send = 0; - *last_packet = true; - return -1; - } +RtpPacketizerH264::~RtpPacketizerH264() { +} - // TODO(jesup) This supports Mode 1 packetization only +void RtpPacketizerH264::SetPayloadData( + const uint8_t* payload_data, + size_t payload_size, + const RTPFragmentationHeader* fragmentation) { + assert(packets_.empty()); + assert(fragmentation); + payload_data_ = payload_data; + payload_size_ = payload_size; + fragmentation_.CopyFrom(*fragmentation); + GeneratePackets(); +} - // For mode 0, it's all single-NAL, and maybe deal with that by simply - // setting a large max_payload_len when constructing this (and tell the - // codec to keep generated NAL sizes less than one packet). If the codec - // goes over, a fragmented RTP packet would be sent (and may work or not). - uint8_t header = payload_data_[0]; - uint8_t type = header & kTypeMask; - if (payload_size_ <= max_payload_len_) { -//#define TEST_STAP_A -#ifdef TEST_STAP_A - static uint8_t sps_buffer[256]; - static uint32_t sps_size; - if (type == kSps) { - - sps_buffer[0] = kStapA; - *(reinterpret_cast(&sps_buffer[1])) = htons(payload_size_); // include NAL byte - memcpy(&sps_buffer[1 + sizeof(uint16_t)], payload_data_, payload_size_); - sps_size = 1 + sizeof(uint16_t) + payload_size_; - *bytes_to_send = 0; - return -1; - } else if (type == kPps && sps_size != 0) { - // Send a STAP-A of SPS/PPS - *(reinterpret_cast(&sps_buffer[sps_size])) = htons(payload_size_); - memcpy(&sps_buffer[sps_size + sizeof(uint16_t)], payload_data_, payload_size_); - memcpy(buffer, sps_buffer, sps_size + 2 + payload_size_); - *bytes_to_send = sps_size + 2 + payload_size_; - sps_size = 0; - *last_packet = false; - return 0; - } -#endif - // single NAL_UNIT - *bytes_to_send = payload_size_; - // TODO(jesup) - this doesn't work correctly for Mode 0. - // Unfortunately, we don't have a good signal to which NAL generated by - // the encoder is the last NAL of the frame. We need that to be passed - // through to this point, instead of trying to generate it from the packets - if (type == kSps || type == kPps || - type == kSei || type == kPrefix) { - *last_packet = false; +void RtpPacketizerH264::GeneratePackets() { + for (size_t i = 0; i < fragmentation_.fragmentationVectorSize;) { + size_t fragment_offset = fragmentation_.fragmentationOffset[i]; + size_t fragment_length = fragmentation_.fragmentationLength[i]; + if (fragment_length > max_payload_len_) { + PacketizeFuA(fragment_offset, fragment_length); + ++i; } else { - *last_packet = true; + i = PacketizeStapA(i, fragment_offset, fragment_length); } - memcpy(buffer, payload_data_, payload_size_); - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "RtpFormatH264(single NALU with type:%d, payload_size:%d", - type, payload_size_); - return 0; + } +} + +void RtpPacketizerH264::PacketizeFuA(size_t fragment_offset, + size_t fragment_length) { + // Fragment payload into packets (FU-A). + // Strip out the original header and leave room for the FU-A header. + fragment_length -= kNalHeaderSize; + size_t offset = fragment_offset + kNalHeaderSize; + size_t bytes_available = max_payload_len_ - kFuAHeaderSize; + size_t fragments = + (fragment_length + (bytes_available - 1)) / bytes_available; + size_t avg_size = (fragment_length + fragments - 1) / fragments; + while (fragment_length > 0) { + size_t packet_length = avg_size; + if (fragment_length < avg_size) + packet_length = fragment_length; + uint8_t header = payload_data_[fragment_offset]; + packets_.push(Packet(offset, + packet_length, + offset - kNalHeaderSize == fragment_offset, + fragment_length == packet_length, + false, + header)); + offset += packet_length; + fragment_length -= packet_length; + } +} + +int RtpPacketizerH264::PacketizeStapA(size_t fragment_index, + size_t fragment_offset, + size_t fragment_length) { + // Aggregate fragments into one packet (STAP-A). + size_t payload_size_left = max_payload_len_; + int aggregated_fragments = 0; + size_t fragment_headers_length = 0; + assert(payload_size_left >= fragment_length); + while (payload_size_left >= fragment_length + fragment_headers_length) { + assert(fragment_length > 0); + uint8_t header = payload_data_[fragment_offset]; + packets_.push(Packet(fragment_offset, + fragment_length, + aggregated_fragments == 0, + false, + true, + header)); + payload_size_left -= fragment_length; + payload_size_left -= fragment_headers_length; + + // Next fragment. + ++fragment_index; + if (fragment_index == fragmentation_.fragmentationVectorSize) + break; + fragment_offset = fragmentation_.fragmentationOffset[fragment_index]; + fragment_length = fragmentation_.fragmentationLength[fragment_index]; + + fragment_headers_length = kLengthFieldSize; + // If we are going to try to aggregate more fragments into this packet + // we need to add the STAP-A NALU header and a length field for the first + // NALU of this packet. + if (aggregated_fragments == 0) + fragment_headers_length += kNalHeaderSize + kLengthFieldSize; + ++aggregated_fragments; + } + packets_.back().last_fragment = true; + return fragment_index; +} + +bool RtpPacketizerH264::NextPacket(uint8_t* buffer, + size_t* bytes_to_send, + bool* last_packet) { + *bytes_to_send = 0; + if (packets_.empty()) { + *bytes_to_send = 0; + *last_packet = true; + return false; + } + + Packet packet = packets_.front(); + + if (packet.first_fragment && packet.last_fragment) { + // Single NAL unit packet. + *bytes_to_send = packet.size; + memcpy(buffer, &payload_data_[packet.offset], packet.size); + packets_.pop(); + assert(*bytes_to_send <= max_payload_len_); + } else if (packet.aggregated) { + NextAggregatePacket(buffer, bytes_to_send); + assert(*bytes_to_send <= max_payload_len_); } else { - uint8_t fu_indicator = (header & (kFBit | kNriMask)) | kFuA; - uint8_t fu_header = 0; - bool first_fragment = (next_fragment_ == 0); - bool last_fragment = (next_fragment_ == (fragments_ -1)); - - // S | E | R | 5 bit type. - fu_header |= (first_fragment ? kFragStartBit : 0); - fu_header |= (last_fragment ? kFragEndBit :0); - fu_header |= type; - buffer[0] = fu_indicator; - buffer[1] = fu_header; - - if (last_fragment) { - // last fragment - *bytes_to_send = payload_size_ - - kNalHeaderSize - - next_fragment_ * fragment_size_ + - kFuAHeaderOffset+kFuAHeaderSize; - *last_packet = true; - memcpy(buffer + kFuAHeaderOffset+kFuAHeaderSize, - payload_data_ + kNalHeaderSize + - next_fragment_ * fragment_size_, - *bytes_to_send - (kFuAHeaderOffset+kFuAHeaderSize)); - // We do not send original NALU header - } else { - *bytes_to_send = fragment_size_ + kFuAHeaderOffset+kFuAHeaderSize; - *last_packet = false; - memcpy(buffer + kFuAHeaderOffset+kFuAHeaderSize, - payload_data_ + kNalHeaderSize + - next_fragment_ * fragment_size_, - fragment_size_); // We do not send original NALU header - } - next_fragment_++; - return 1; + NextFragmentPacket(buffer, bytes_to_send); + assert(*bytes_to_send <= max_payload_len_); } + *last_packet = packets_.empty(); + return true; } +void RtpPacketizerH264::NextAggregatePacket(uint8_t* buffer, + size_t* bytes_to_send) { + Packet packet = packets_.front(); + assert(packet.first_fragment); + // STAP-A NALU header. + buffer[0] = (packet.header & (kFBit | kNriMask)) | kStapA; + int index = kNalHeaderSize; + *bytes_to_send += kNalHeaderSize; + while (packet.aggregated) { + // Add NAL unit length field. + RtpUtility::AssignUWord16ToBuffer(&buffer[index], packet.size); + index += kLengthFieldSize; + *bytes_to_send += kLengthFieldSize; + // Add NAL unit. + memcpy(&buffer[index], &payload_data_[packet.offset], packet.size); + index += packet.size; + *bytes_to_send += packet.size; + packets_.pop(); + if (packet.last_fragment) + break; + packet = packets_.front(); + } + assert(packet.last_fragment); +} + +void RtpPacketizerH264::NextFragmentPacket(uint8_t* buffer, + size_t* bytes_to_send) { + Packet packet = packets_.front(); + // NAL unit fragmented over multiple packets (FU-A). + // We do not send original NALU header, so it will be replaced by the + // FU indicator header of the first packet. + uint8_t fu_indicator = (packet.header & (kFBit | kNriMask)) | kFuA; + uint8_t fu_header = 0; + + // S | E | R | 5 bit type. + fu_header |= (packet.first_fragment ? kSBit : 0); + fu_header |= (packet.last_fragment ? kEBit : 0); + uint8_t type = packet.header & kTypeMask; + fu_header |= type; + buffer[0] = fu_indicator; + buffer[1] = fu_header; + + if (packet.last_fragment) { + *bytes_to_send = packet.size + kFuAHeaderSize; + memcpy(buffer + kFuAHeaderSize, &payload_data_[packet.offset], packet.size); + } else { + *bytes_to_send = packet.size + kFuAHeaderSize; + memcpy(buffer + kFuAHeaderSize, &payload_data_[packet.offset], packet.size); + } + packets_.pop(); +} + +ProtectionType RtpPacketizerH264::GetProtectionType() { + return (frame_type_ == kVideoFrameKey) ? kProtectedPacket + : kUnprotectedPacket; +} + +StorageType RtpPacketizerH264::GetStorageType( + uint32_t retransmission_settings) { + return kAllowRetransmission; +} + +std::string RtpPacketizerH264::ToString() { + return "RtpPacketizerH264"; +} + +bool RtpDepacketizerH264::Parse(ParsedPayload* parsed_payload, + const uint8_t* payload_data, + size_t payload_data_length) { + assert(parsed_payload != NULL); + uint8_t nal_type = payload_data[0] & kTypeMask; + size_t offset = 0; + if (nal_type == kFuA) { + // Fragmented NAL units (FU-A). + ParseFuaNalu(parsed_payload, payload_data, payload_data_length, &offset); + } else { + // We handle STAP-A and single NALU's the same way here. The jitter buffer + // will depacketize the STAP-A into NAL units later. + ParseSingleNalu(parsed_payload, payload_data, payload_data_length); + } + + parsed_payload->payload = payload_data + offset; + parsed_payload->payload_length = payload_data_length - offset; + return true; +} } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_h264.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_h264.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_h264.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_h264.h 2015-02-03 14:33:36.000000000 +0000 @@ -1,113 +1,103 @@ - /* - * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - - /* - * This file contains the declaration of the H264 packetizer class. - * A packetizer object is created for each encoded video frame. The - * constructor is called with the payload data and size, - * together with the fragmentation information and a packetizer mode - * of choice. Alternatively, if no fragmentation info is available, the - * second constructor can be used with only payload data and size; in that - * case the mode kEqualSize is used. - * - * After creating the packetizer, the method NextPacket is called - * repeatedly to get all packets for the frame. The method returns - * false as long as there are more packets left to fetch. - */ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ #ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_H264_H_ #define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_H264_H_ -#include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/typedefs.h" +#include +#include + +#include "webrtc/modules/rtp_rtcp/source/rtp_format.h" namespace webrtc { -// Packetizer for H264. -class RtpFormatH264 { +class RtpPacketizerH264 : public RtpPacketizer { public: - - // This supports H.264 RTP packetization modes 0/1 from RFC 6184 - // Single NALU: NAL Header (1 byte), Data... - // FU-A NALU: NAL Header, FU Header (1 byte), Data... - // STAP-A NALU: NAL Header, Length1 (2 bytes), Data1, Length2, Data2... - - enum NalHeader { // Network Abstraction Layer Unit Header - kNalHeaderOffset = 0, // start of every RTP payload - kNalHeaderSize = 1, // 1 byte: - kTypeMask = 0x1f, // bits 0-4: NAL Type - kNriMask = 0x60, // bits 5-6: Non-Ref Indicator - kFBit = 0x80, // bit 7: Forbidden (always 0) - }; - - enum NalType { // 0-23 from H.264, 24-31 from RFC 6184 - kIpb = 1, // I/P/B slice - kIdr = 5, // IDR slice - kSei = 6, // Supplementary Enhancement Info - kSeiRecPt = 6, // Recovery Point SEI Payload - kSps = 7, // Sequence Parameter Set - kPps = 8, // Picture Parameter Set - kPrefix = 14, // Prefix - kStapA = 24, // Single-Time Aggregation Packet Type A - kFuA = 28, // Fragmentation Unit Type A - }; - - enum FuAHeader { - kFuAHeaderOffset = 1, // follows NAL Header - kFuAHeaderSize = 1, // 1 byte: bits 0-4: Original NAL Type - kFragStartBit = 0x80, // bit 7: Start of Fragment - kFragEndBit = 0x40, // bit 6: End of Fragment - kReservedBit = 0x20 // bit 5: Reserved - }; - enum StapAHeader { - kStapAHeaderOffset = 1, // follows NAL Header - kAggUnitLengthSize = 2 // 2-byte length of next NALU including NAL header - }; - enum StartCodePrefix { // H.264 Annex B format {0,0,0,1} - kStartCodeSize = 4 // 4 byte prefix before each NAL header - }; - // Initialize with payload from encoder. // The payload_data must be exactly one encoded H264 frame. - RtpFormatH264(const uint8_t* payload_data, - uint32_t payload_size, - int max_payload_len); + RtpPacketizerH264(FrameType frame_type, size_t max_payload_len); + + virtual ~RtpPacketizerH264(); - ~RtpFormatH264(); + virtual void SetPayloadData( + const uint8_t* payload_data, + size_t payload_size, + const RTPFragmentationHeader* fragmentation) OVERRIDE; // Get the next payload with H264 payload header. - // max_payload_len limits the sum length of payload and H264 payload header. // buffer is a pointer to where the output will be written. // bytes_to_send is an output variable that will contain number of bytes - // written to buffer. Parameter last_packet is true for the last packet of + // written to buffer. The parameter last_packet is true for the last packet of // the frame, false otherwise (i.e., call the function again to get the // next packet). - // Returns 0 on success for single NAL_UNIT - // Returns 1 on success for fragmentation - // return -1 on error. - int NextPacket(uint8_t* buffer, - int* bytes_to_send, - bool* last_packet); + // Returns true on success or false if there was no payload to packetize. + virtual bool NextPacket(uint8_t* buffer, + size_t* bytes_to_send, + bool* last_packet) OVERRIDE; + + virtual ProtectionType GetProtectionType() OVERRIDE; + + virtual StorageType GetStorageType(uint32_t retransmission_settings) OVERRIDE; + + virtual std::string ToString() OVERRIDE; private: + struct Packet { + Packet(size_t offset, + size_t size, + bool first_fragment, + bool last_fragment, + bool aggregated, + uint8_t header) + : offset(offset), + size(size), + first_fragment(first_fragment), + last_fragment(last_fragment), + aggregated(aggregated), + header(header) {} + + size_t offset; + size_t size; + bool first_fragment; + bool last_fragment; + bool aggregated; + uint8_t header; + }; + typedef std::queue PacketQueue; + + void GeneratePackets(); + void PacketizeFuA(size_t fragment_offset, size_t fragment_length); + int PacketizeStapA(size_t fragment_index, + size_t fragment_offset, + size_t fragment_length); + void NextAggregatePacket(uint8_t* buffer, size_t* bytes_to_send); + void NextFragmentPacket(uint8_t* buffer, size_t* bytes_to_send); + const uint8_t* payload_data_; - const int payload_size_; - const int max_payload_len_; - int fragments_; - int fragment_size_; - int next_fragment_; + size_t payload_size_; + const size_t max_payload_len_; + RTPFragmentationHeader fragmentation_; + PacketQueue packets_; + FrameType frame_type_; - DISALLOW_COPY_AND_ASSIGN(RtpFormatH264); + DISALLOW_COPY_AND_ASSIGN(RtpPacketizerH264); }; -} // namespace webrtc +// Depacketizer for H264. +class RtpDepacketizerH264 : public RtpDepacketizer { + public: + virtual ~RtpDepacketizerH264() {} + virtual bool Parse(ParsedPayload* parsed_payload, + const uint8_t* payload_data, + size_t payload_data_length) OVERRIDE; +}; +} // namespace webrtc #endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_H264_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,499 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_format.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { +namespace { +const size_t kMaxPayloadSize = 1200; +const size_t kLengthFieldLength = 2; + +enum Nalu { + kSlice = 1, + kIdr = 5, + kSei = 6, + kSps = 7, + kPps = 8, + kStapA = 24, + kFuA = 28 +}; + +static const size_t kNalHeaderSize = 1; +static const size_t kFuAHeaderSize = 2; + +// Bit masks for FU (A and B) indicators. +enum NalDefs { kFBit = 0x80, kNriMask = 0x60, kTypeMask = 0x1F }; + +// Bit masks for FU (A and B) headers. +enum FuDefs { kSBit = 0x80, kEBit = 0x40, kRBit = 0x20 }; + +void VerifyFua(size_t fua_index, + const uint8_t* expected_payload, + int offset, + const uint8_t* packet, + size_t length, + const std::vector& expected_sizes) { + ASSERT_EQ(expected_sizes[fua_index] + kFuAHeaderSize, length) + << "FUA index: " << fua_index; + const uint8_t kFuIndicator = 0x1C; // F=0, NRI=0, Type=28. + EXPECT_EQ(kFuIndicator, packet[0]) << "FUA index: " << fua_index; + bool should_be_last_fua = (fua_index == expected_sizes.size() - 1); + uint8_t fu_header = 0; + if (fua_index == 0) + fu_header = 0x85; // S=1, E=0, R=0, Type=5. + else if (should_be_last_fua) + fu_header = 0x45; // S=0, E=1, R=0, Type=5. + else + fu_header = 0x05; // S=0, E=0, R=0, Type=5. + EXPECT_EQ(fu_header, packet[1]) << "FUA index: " << fua_index; + std::vector expected_packet_payload( + &expected_payload[offset], + &expected_payload[offset + expected_sizes[fua_index]]); + EXPECT_THAT( + expected_packet_payload, + ::testing::ElementsAreArray(&packet[2], expected_sizes[fua_index])) + << "FUA index: " << fua_index; +} + +void TestFua(size_t frame_size, + size_t max_payload_size, + const std::vector& expected_sizes) { + scoped_ptr frame; + frame.reset(new uint8_t[frame_size]); + frame[0] = 0x05; // F=0, NRI=0, Type=5. + for (size_t i = 0; i < frame_size - kNalHeaderSize; ++i) { + frame[i + kNalHeaderSize] = i; + } + RTPFragmentationHeader fragmentation; + fragmentation.VerifyAndAllocateFragmentationHeader(1); + fragmentation.fragmentationOffset[0] = 0; + fragmentation.fragmentationLength[0] = frame_size; + scoped_ptr packetizer(RtpPacketizer::Create( + kRtpVideoH264, max_payload_size, NULL, kFrameEmpty)); + packetizer->SetPayloadData(frame.get(), frame_size, &fragmentation); + + scoped_ptr packet(new uint8_t[max_payload_size]); + size_t length = 0; + bool last = false; + size_t offset = kNalHeaderSize; + for (size_t i = 0; i < expected_sizes.size(); ++i) { + ASSERT_TRUE(packetizer->NextPacket(packet.get(), &length, &last)); + VerifyFua(i, frame.get(), offset, packet.get(), length, expected_sizes); + EXPECT_EQ(i == expected_sizes.size() - 1, last) << "FUA index: " << i; + offset += expected_sizes[i]; + } + + EXPECT_FALSE(packetizer->NextPacket(packet.get(), &length, &last)); +} + +size_t GetExpectedNaluOffset(const RTPFragmentationHeader& fragmentation, + size_t start_index, + size_t nalu_index) { + assert(nalu_index < fragmentation.fragmentationVectorSize); + size_t expected_nalu_offset = kNalHeaderSize; // STAP-A header. + for (size_t i = start_index; i < nalu_index; ++i) { + expected_nalu_offset += + kLengthFieldLength + fragmentation.fragmentationLength[i]; + } + return expected_nalu_offset; +} + +void VerifyStapAPayload(const RTPFragmentationHeader& fragmentation, + size_t first_stapa_index, + size_t nalu_index, + const uint8_t* frame, + size_t frame_length, + const uint8_t* packet, + size_t packet_length) { + size_t expected_payload_offset = + GetExpectedNaluOffset(fragmentation, first_stapa_index, nalu_index) + + kLengthFieldLength; + size_t offset = fragmentation.fragmentationOffset[nalu_index]; + const uint8_t* expected_payload = &frame[offset]; + size_t expected_payload_length = + fragmentation.fragmentationLength[nalu_index]; + ASSERT_LE(offset + expected_payload_length, frame_length); + ASSERT_LE(expected_payload_offset + expected_payload_length, packet_length); + std::vector expected_payload_vector( + expected_payload, &expected_payload[expected_payload_length]); + EXPECT_THAT(expected_payload_vector, + ::testing::ElementsAreArray(&packet[expected_payload_offset], + expected_payload_length)); +} + +void VerifySingleNaluPayload(const RTPFragmentationHeader& fragmentation, + size_t nalu_index, + const uint8_t* frame, + size_t frame_length, + const uint8_t* packet, + size_t packet_length) { + std::vector expected_payload_vector( + &frame[fragmentation.fragmentationOffset[nalu_index]], + &frame[fragmentation.fragmentationOffset[nalu_index] + + fragmentation.fragmentationLength[nalu_index]]); + EXPECT_THAT(expected_payload_vector, + ::testing::ElementsAreArray(packet, packet_length)); +} +} // namespace + +TEST(RtpPacketizerH264Test, TestSingleNalu) { + const uint8_t frame[2] = {0x05, 0xFF}; // F=0, NRI=0, Type=5. + RTPFragmentationHeader fragmentation; + fragmentation.VerifyAndAllocateFragmentationHeader(1); + fragmentation.fragmentationOffset[0] = 0; + fragmentation.fragmentationLength[0] = sizeof(frame); + scoped_ptr packetizer( + RtpPacketizer::Create(kRtpVideoH264, kMaxPayloadSize, NULL, kFrameEmpty)); + packetizer->SetPayloadData(frame, sizeof(frame), &fragmentation); + uint8_t packet[kMaxPayloadSize] = {0}; + size_t length = 0; + bool last = false; + ASSERT_TRUE(packetizer->NextPacket(packet, &length, &last)); + EXPECT_EQ(2u, length); + EXPECT_TRUE(last); + VerifySingleNaluPayload( + fragmentation, 0, frame, sizeof(frame), packet, length); + EXPECT_FALSE(packetizer->NextPacket(packet, &length, &last)); +} + +TEST(RtpPacketizerH264Test, TestSingleNaluTwoPackets) { + const size_t kFrameSize = kMaxPayloadSize + 100; + uint8_t frame[kFrameSize] = {0}; + for (size_t i = 0; i < kFrameSize; ++i) + frame[i] = i; + RTPFragmentationHeader fragmentation; + fragmentation.VerifyAndAllocateFragmentationHeader(2); + fragmentation.fragmentationOffset[0] = 0; + fragmentation.fragmentationLength[0] = kMaxPayloadSize; + fragmentation.fragmentationOffset[1] = kMaxPayloadSize; + fragmentation.fragmentationLength[1] = 100; + // Set NAL headers. + frame[fragmentation.fragmentationOffset[0]] = 0x01; + frame[fragmentation.fragmentationOffset[1]] = 0x01; + + scoped_ptr packetizer( + RtpPacketizer::Create(kRtpVideoH264, kMaxPayloadSize, NULL, kFrameEmpty)); + packetizer->SetPayloadData(frame, kFrameSize, &fragmentation); + + uint8_t packet[kMaxPayloadSize] = {0}; + size_t length = 0; + bool last = false; + ASSERT_TRUE(packetizer->NextPacket(packet, &length, &last)); + ASSERT_EQ(fragmentation.fragmentationOffset[1], length); + VerifySingleNaluPayload(fragmentation, 0, frame, kFrameSize, packet, length); + + ASSERT_TRUE(packetizer->NextPacket(packet, &length, &last)); + ASSERT_EQ(fragmentation.fragmentationLength[1], length); + VerifySingleNaluPayload(fragmentation, 1, frame, kFrameSize, packet, length); + EXPECT_TRUE(last); + + EXPECT_FALSE(packetizer->NextPacket(packet, &length, &last)); +} + +TEST(RtpPacketizerH264Test, TestStapA) { + const size_t kFrameSize = + kMaxPayloadSize - 3 * kLengthFieldLength - kNalHeaderSize; + uint8_t frame[kFrameSize] = {0x07, 0xFF, // F=0, NRI=0, Type=7. + 0x08, 0xFF, // F=0, NRI=0, Type=8. + 0x05}; // F=0, NRI=0, Type=5. + const size_t kPayloadOffset = 5; + for (size_t i = 0; i < kFrameSize - kPayloadOffset; ++i) + frame[i + kPayloadOffset] = i; + RTPFragmentationHeader fragmentation; + fragmentation.VerifyAndAllocateFragmentationHeader(3); + fragmentation.fragmentationOffset[0] = 0; + fragmentation.fragmentationLength[0] = 2; + fragmentation.fragmentationOffset[1] = 2; + fragmentation.fragmentationLength[1] = 2; + fragmentation.fragmentationOffset[2] = 4; + fragmentation.fragmentationLength[2] = + kNalHeaderSize + kFrameSize - kPayloadOffset; + scoped_ptr packetizer( + RtpPacketizer::Create(kRtpVideoH264, kMaxPayloadSize, NULL, kFrameEmpty)); + packetizer->SetPayloadData(frame, kFrameSize, &fragmentation); + + uint8_t packet[kMaxPayloadSize] = {0}; + size_t length = 0; + bool last = false; + ASSERT_TRUE(packetizer->NextPacket(packet, &length, &last)); + size_t expected_packet_size = + kNalHeaderSize + 3 * kLengthFieldLength + kFrameSize; + ASSERT_EQ(expected_packet_size, length); + EXPECT_TRUE(last); + for (size_t i = 0; i < fragmentation.fragmentationVectorSize; ++i) + VerifyStapAPayload(fragmentation, 0, i, frame, kFrameSize, packet, length); + + EXPECT_FALSE(packetizer->NextPacket(packet, &length, &last)); +} + +TEST(RtpPacketizerH264Test, TestTooSmallForStapAHeaders) { + const size_t kFrameSize = kMaxPayloadSize - 1; + uint8_t frame[kFrameSize] = {0x07, 0xFF, // F=0, NRI=0, Type=7. + 0x08, 0xFF, // F=0, NRI=0, Type=8. + 0x05}; // F=0, NRI=0, Type=5. + const size_t kPayloadOffset = 5; + for (size_t i = 0; i < kFrameSize - kPayloadOffset; ++i) + frame[i + kPayloadOffset] = i; + RTPFragmentationHeader fragmentation; + fragmentation.VerifyAndAllocateFragmentationHeader(3); + fragmentation.fragmentationOffset[0] = 0; + fragmentation.fragmentationLength[0] = 2; + fragmentation.fragmentationOffset[1] = 2; + fragmentation.fragmentationLength[1] = 2; + fragmentation.fragmentationOffset[2] = 4; + fragmentation.fragmentationLength[2] = + kNalHeaderSize + kFrameSize - kPayloadOffset; + scoped_ptr packetizer( + RtpPacketizer::Create(kRtpVideoH264, kMaxPayloadSize, NULL, kFrameEmpty)); + packetizer->SetPayloadData(frame, kFrameSize, &fragmentation); + + uint8_t packet[kMaxPayloadSize] = {0}; + size_t length = 0; + bool last = false; + ASSERT_TRUE(packetizer->NextPacket(packet, &length, &last)); + size_t expected_packet_size = kNalHeaderSize; + for (size_t i = 0; i < 2; ++i) { + expected_packet_size += + kLengthFieldLength + fragmentation.fragmentationLength[i]; + } + ASSERT_EQ(expected_packet_size, length); + EXPECT_FALSE(last); + for (size_t i = 0; i < 2; ++i) + VerifyStapAPayload(fragmentation, 0, i, frame, kFrameSize, packet, length); + + ASSERT_TRUE(packetizer->NextPacket(packet, &length, &last)); + expected_packet_size = fragmentation.fragmentationLength[2]; + ASSERT_EQ(expected_packet_size, length); + EXPECT_TRUE(last); + VerifySingleNaluPayload(fragmentation, 2, frame, kFrameSize, packet, length); + + EXPECT_FALSE(packetizer->NextPacket(packet, &length, &last)); +} + +TEST(RtpPacketizerH264Test, TestMixedStapA_FUA) { + const size_t kFuaNaluSize = 2 * (kMaxPayloadSize - 100); + const size_t kStapANaluSize = 100; + RTPFragmentationHeader fragmentation; + fragmentation.VerifyAndAllocateFragmentationHeader(3); + fragmentation.fragmentationOffset[0] = 0; + fragmentation.fragmentationLength[0] = kFuaNaluSize; + fragmentation.fragmentationOffset[1] = kFuaNaluSize; + fragmentation.fragmentationLength[1] = kStapANaluSize; + fragmentation.fragmentationOffset[2] = kFuaNaluSize + kStapANaluSize; + fragmentation.fragmentationLength[2] = kStapANaluSize; + const size_t kFrameSize = kFuaNaluSize + 2 * kStapANaluSize; + uint8_t frame[kFrameSize]; + size_t nalu_offset = 0; + for (size_t i = 0; i < fragmentation.fragmentationVectorSize; ++i) { + nalu_offset = fragmentation.fragmentationOffset[i]; + frame[nalu_offset] = 0x05; // F=0, NRI=0, Type=5. + for (size_t j = 1; j < fragmentation.fragmentationLength[i]; ++j) { + frame[nalu_offset + j] = i + j; + } + } + scoped_ptr packetizer( + RtpPacketizer::Create(kRtpVideoH264, kMaxPayloadSize, NULL, kFrameEmpty)); + packetizer->SetPayloadData(frame, kFrameSize, &fragmentation); + + // First expecting two FU-A packets. + std::vector fua_sizes; + fua_sizes.push_back(1100); + fua_sizes.push_back(1099); + uint8_t packet[kMaxPayloadSize] = {0}; + size_t length = 0; + bool last = false; + int fua_offset = kNalHeaderSize; + for (size_t i = 0; i < 2; ++i) { + ASSERT_TRUE(packetizer->NextPacket(packet, &length, &last)); + VerifyFua(i, frame, fua_offset, packet, length, fua_sizes); + EXPECT_FALSE(last); + fua_offset += fua_sizes[i]; + } + // Then expecting one STAP-A packet with two nal units. + ASSERT_TRUE(packetizer->NextPacket(packet, &length, &last)); + size_t expected_packet_size = + kNalHeaderSize + 2 * kLengthFieldLength + 2 * kStapANaluSize; + ASSERT_EQ(expected_packet_size, length); + EXPECT_TRUE(last); + for (size_t i = 1; i < fragmentation.fragmentationVectorSize; ++i) + VerifyStapAPayload(fragmentation, 1, i, frame, kFrameSize, packet, length); + + EXPECT_FALSE(packetizer->NextPacket(packet, &length, &last)); +} + +TEST(RtpPacketizerH264Test, TestFUAOddSize) { + const size_t kExpectedPayloadSizes[2] = {600, 600}; + TestFua( + kMaxPayloadSize + 1, + kMaxPayloadSize, + std::vector(kExpectedPayloadSizes, + kExpectedPayloadSizes + + sizeof(kExpectedPayloadSizes) / sizeof(size_t))); +} + +TEST(RtpPacketizerH264Test, TestFUAEvenSize) { + const size_t kExpectedPayloadSizes[2] = {601, 600}; + TestFua( + kMaxPayloadSize + 2, + kMaxPayloadSize, + std::vector(kExpectedPayloadSizes, + kExpectedPayloadSizes + + sizeof(kExpectedPayloadSizes) / sizeof(size_t))); +} + +TEST(RtpPacketizerH264Test, TestFUARounding) { + const size_t kExpectedPayloadSizes[8] = {1266, 1266, 1266, 1266, + 1266, 1266, 1266, 1261}; + TestFua( + 10124, + 1448, + std::vector(kExpectedPayloadSizes, + kExpectedPayloadSizes + + sizeof(kExpectedPayloadSizes) / sizeof(size_t))); +} + +TEST(RtpPacketizerH264Test, TestFUABig) { + const size_t kExpectedPayloadSizes[10] = {1198, 1198, 1198, 1198, 1198, + 1198, 1198, 1198, 1198, 1198}; + // Generate 10 full sized packets, leave room for FU-A headers minus the NALU + // header. + TestFua( + 10 * (kMaxPayloadSize - kFuAHeaderSize) + kNalHeaderSize, + kMaxPayloadSize, + std::vector(kExpectedPayloadSizes, + kExpectedPayloadSizes + + sizeof(kExpectedPayloadSizes) / sizeof(size_t))); +} + +class RtpDepacketizerH264Test : public ::testing::Test { + protected: + RtpDepacketizerH264Test() + : depacketizer_(RtpDepacketizer::Create(kRtpVideoH264)) {} + + void ExpectPacket(RtpDepacketizer::ParsedPayload* parsed_payload, + const uint8_t* data, + size_t length) { + ASSERT_TRUE(parsed_payload != NULL); + EXPECT_THAT(std::vector( + parsed_payload->payload, + parsed_payload->payload + parsed_payload->payload_length), + ::testing::ElementsAreArray(data, length)); + } + + scoped_ptr depacketizer_; +}; + +TEST_F(RtpDepacketizerH264Test, TestSingleNalu) { + uint8_t packet[2] = {0x05, 0xFF}; // F=0, NRI=0, Type=5. + RtpDepacketizer::ParsedPayload payload; + + ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet))); + ExpectPacket(&payload, packet, sizeof(packet)); + EXPECT_EQ(kVideoFrameKey, payload.frame_type); + EXPECT_EQ(kRtpVideoH264, payload.type.Video.codec); + EXPECT_TRUE(payload.type.Video.isFirstPacket); + EXPECT_TRUE(payload.type.Video.codecHeader.H264.single_nalu); + EXPECT_FALSE(payload.type.Video.codecHeader.H264.stap_a); +} + +TEST_F(RtpDepacketizerH264Test, TestStapAKey) { + uint8_t packet[16] = {kStapA, // F=0, NRI=0, Type=24. + // Length, nal header, payload. + 0, 0x02, kIdr, 0xFF, 0, 0x03, kIdr, 0xFF, + 0x00, 0, 0x04, kIdr, 0xFF, 0x00, 0x11}; + RtpDepacketizer::ParsedPayload payload; + + ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet))); + ExpectPacket(&payload, packet, sizeof(packet)); + EXPECT_EQ(kVideoFrameKey, payload.frame_type); + EXPECT_EQ(kRtpVideoH264, payload.type.Video.codec); + EXPECT_TRUE(payload.type.Video.isFirstPacket); + EXPECT_TRUE(payload.type.Video.codecHeader.H264.single_nalu); + EXPECT_TRUE(payload.type.Video.codecHeader.H264.stap_a); +} + +TEST_F(RtpDepacketizerH264Test, TestStapADelta) { + uint8_t packet[16] = {kStapA, // F=0, NRI=0, Type=24. + // Length, nal header, payload. + 0, 0x02, kSlice, 0xFF, 0, 0x03, kSlice, 0xFF, + 0x00, 0, 0x04, kSlice, 0xFF, 0x00, 0x11}; + RtpDepacketizer::ParsedPayload payload; + + ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet))); + ExpectPacket(&payload, packet, sizeof(packet)); + EXPECT_EQ(kVideoFrameDelta, payload.frame_type); + EXPECT_EQ(kRtpVideoH264, payload.type.Video.codec); + EXPECT_TRUE(payload.type.Video.isFirstPacket); + EXPECT_TRUE(payload.type.Video.codecHeader.H264.single_nalu); + EXPECT_TRUE(payload.type.Video.codecHeader.H264.stap_a); +} + +TEST_F(RtpDepacketizerH264Test, TestFuA) { + uint8_t packet1[3] = { + kFuA, // F=0, NRI=0, Type=28. + kSBit | kIdr, // FU header. + 0x01 // Payload. + }; + const uint8_t kExpected1[2] = {kIdr, 0x01}; + + uint8_t packet2[3] = { + kFuA, // F=0, NRI=0, Type=28. + kIdr, // FU header. + 0x02 // Payload. + }; + const uint8_t kExpected2[1] = {0x02}; + + uint8_t packet3[3] = { + kFuA, // F=0, NRI=0, Type=28. + kEBit | kIdr, // FU header. + 0x03 // Payload. + }; + const uint8_t kExpected3[1] = {0x03}; + + RtpDepacketizer::ParsedPayload payload; + + // We expect that the first packet is one byte shorter since the FU-A header + // has been replaced by the original nal header. + ASSERT_TRUE(depacketizer_->Parse(&payload, packet1, sizeof(packet1))); + ExpectPacket(&payload, kExpected1, sizeof(kExpected1)); + EXPECT_EQ(kVideoFrameKey, payload.frame_type); + EXPECT_EQ(kRtpVideoH264, payload.type.Video.codec); + EXPECT_TRUE(payload.type.Video.isFirstPacket); + EXPECT_FALSE(payload.type.Video.codecHeader.H264.single_nalu); + EXPECT_FALSE(payload.type.Video.codecHeader.H264.stap_a); + + // Following packets will be 2 bytes shorter since they will only be appended + // onto the first packet. + payload = RtpDepacketizer::ParsedPayload(); + ASSERT_TRUE(depacketizer_->Parse(&payload, packet2, sizeof(packet2))); + ExpectPacket(&payload, kExpected2, sizeof(kExpected2)); + EXPECT_EQ(kVideoFrameKey, payload.frame_type); + EXPECT_EQ(kRtpVideoH264, payload.type.Video.codec); + EXPECT_FALSE(payload.type.Video.isFirstPacket); + EXPECT_FALSE(payload.type.Video.codecHeader.H264.single_nalu); + EXPECT_FALSE(payload.type.Video.codecHeader.H264.stap_a); + + payload = RtpDepacketizer::ParsedPayload(); + ASSERT_TRUE(depacketizer_->Parse(&payload, packet3, sizeof(packet3))); + ExpectPacket(&payload, kExpected3, sizeof(kExpected3)); + EXPECT_EQ(kVideoFrameKey, payload.frame_type); + EXPECT_EQ(kRtpVideoH264, payload.type.Video.codec); + EXPECT_FALSE(payload.type.Video.isFirstPacket); + EXPECT_FALSE(payload.type.Video.codecHeader.H264.single_nalu); + EXPECT_FALSE(payload.type.Video.codecHeader.H264.stap_a); +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h" + +namespace webrtc { + +static const size_t kGenericHeaderLength = 1; + +RtpPacketizerGeneric::RtpPacketizerGeneric(FrameType frame_type, + size_t max_payload_len) + : payload_data_(NULL), + payload_size_(0), + max_payload_len_(max_payload_len - kGenericHeaderLength), + frame_type_(frame_type) { +} + +RtpPacketizerGeneric::~RtpPacketizerGeneric() { +} + +void RtpPacketizerGeneric::SetPayloadData( + const uint8_t* payload_data, + size_t payload_size, + const RTPFragmentationHeader* fragmentation) { + payload_data_ = payload_data; + payload_size_ = payload_size; + + // Fragment packets more evenly by splitting the payload up evenly. + uint32_t num_packets = + (payload_size_ + max_payload_len_ - 1) / max_payload_len_; + payload_length_ = (payload_size_ + num_packets - 1) / num_packets; + assert(payload_length_ <= max_payload_len_); + + generic_header_ = RtpFormatVideoGeneric::kFirstPacketBit; +} + +bool RtpPacketizerGeneric::NextPacket(uint8_t* buffer, + size_t* bytes_to_send, + bool* last_packet) { + if (payload_size_ < payload_length_) { + payload_length_ = payload_size_; + } + + payload_size_ -= payload_length_; + *bytes_to_send = payload_length_ + kGenericHeaderLength; + assert(payload_length_ <= max_payload_len_); + + uint8_t* out_ptr = buffer; + // Put generic header in packet + if (frame_type_ == kVideoFrameKey) { + generic_header_ |= RtpFormatVideoGeneric::kKeyFrameBit; + } + *out_ptr++ = generic_header_; + // Remove first-packet bit, following packets are intermediate + generic_header_ &= ~RtpFormatVideoGeneric::kFirstPacketBit; + + // Put payload in packet + memcpy(out_ptr, payload_data_, payload_length_); + payload_data_ += payload_length_; + + *last_packet = payload_size_ <= 0; + + return true; +} + +ProtectionType RtpPacketizerGeneric::GetProtectionType() { + return kProtectedPacket; +} + +StorageType RtpPacketizerGeneric::GetStorageType( + uint32_t retransmission_settings) { + return kAllowRetransmission; +} + +std::string RtpPacketizerGeneric::ToString() { + return "RtpPacketizerGeneric"; +} + +bool RtpDepacketizerGeneric::Parse(ParsedPayload* parsed_payload, + const uint8_t* payload_data, + size_t payload_data_length) { + assert(parsed_payload != NULL); + + uint8_t generic_header = *payload_data++; + --payload_data_length; + + parsed_payload->frame_type = + ((generic_header & RtpFormatVideoGeneric::kKeyFrameBit) != 0) + ? kVideoFrameKey + : kVideoFrameDelta; + parsed_payload->type.Video.isFirstPacket = + (generic_header & RtpFormatVideoGeneric::kFirstPacketBit) != 0; + parsed_payload->type.Video.codec = kRtpVideoGeneric; + parsed_payload->type.Video.width = 0; + parsed_payload->type.Video.height = 0; + + parsed_payload->payload = payload_data; + parsed_payload->payload_length = payload_data_length; + return true; +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h 2015-02-03 14:33:36.000000000 +0000 @@ -10,13 +10,67 @@ #ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VIDEO_GENERIC_H_ #define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VIDEO_GENERIC_H_ +#include + +#include "webrtc/common_types.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_format.h" #include "webrtc/typedefs.h" namespace webrtc { namespace RtpFormatVideoGeneric { - static const uint8_t kKeyFrameBit = 0x01; - static const uint8_t kFirstPacketBit = 0x02; +static const uint8_t kKeyFrameBit = 0x01; +static const uint8_t kFirstPacketBit = 0x02; } // namespace RtpFormatVideoGeneric -} // namespace webrtc +class RtpPacketizerGeneric : public RtpPacketizer { + public: + // Initialize with payload from encoder. + // The payload_data must be exactly one encoded generic frame. + RtpPacketizerGeneric(FrameType frametype, size_t max_payload_len); + + virtual ~RtpPacketizerGeneric(); + + virtual void SetPayloadData( + const uint8_t* payload_data, + size_t payload_size, + const RTPFragmentationHeader* fragmentation) OVERRIDE; + + // Get the next payload with generic payload header. + // buffer is a pointer to where the output will be written. + // bytes_to_send is an output variable that will contain number of bytes + // written to buffer. The parameter last_packet is true for the last packet of + // the frame, false otherwise (i.e., call the function again to get the + // next packet). + // Returns true on success or false if there was no payload to packetize. + virtual bool NextPacket(uint8_t* buffer, + size_t* bytes_to_send, + bool* last_packet) OVERRIDE; + + virtual ProtectionType GetProtectionType() OVERRIDE; + + virtual StorageType GetStorageType(uint32_t retransmission_settings) OVERRIDE; + + virtual std::string ToString() OVERRIDE; + + private: + const uint8_t* payload_data_; + size_t payload_size_; + const size_t max_payload_len_; + FrameType frame_type_; + uint32_t payload_length_; + uint8_t generic_header_; + + DISALLOW_COPY_AND_ASSIGN(RtpPacketizerGeneric); +}; + +// Depacketizer for generic codec. +class RtpDepacketizerGeneric : public RtpDepacketizer { + public: + virtual ~RtpDepacketizerGeneric() {} + + virtual bool Parse(ParsedPayload* parsed_payload, + const uint8_t* payload_data, + size_t payload_data_length) OVERRIDE; +}; +} // namespace webrtc #endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VIDEO_GENERIC_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,68 +10,197 @@ #include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h" -#include // assert +#include // assert #include // memcpy #include #include "webrtc/modules/rtp_rtcp/source/vp8_partition_aggregator.h" +#include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { +namespace { +int ParseVP8PictureID(RTPVideoHeaderVP8* vp8, + const uint8_t** data, + int* data_length, + int* parsed_bytes) { + assert(vp8 != NULL); + if (*data_length <= 0) + return -1; + + vp8->pictureId = (**data & 0x7F); + if (**data & 0x80) { + (*data)++; + (*parsed_bytes)++; + if (--(*data_length) <= 0) + return -1; + // PictureId is 15 bits + vp8->pictureId = (vp8->pictureId << 8) + **data; + } + (*data)++; + (*parsed_bytes)++; + (*data_length)--; + return 0; +} + +int ParseVP8Tl0PicIdx(RTPVideoHeaderVP8* vp8, + const uint8_t** data, + int* data_length, + int* parsed_bytes) { + assert(vp8 != NULL); + if (*data_length <= 0) + return -1; + + vp8->tl0PicIdx = **data; + (*data)++; + (*parsed_bytes)++; + (*data_length)--; + return 0; +} + +int ParseVP8TIDAndKeyIdx(RTPVideoHeaderVP8* vp8, + const uint8_t** data, + int* data_length, + int* parsed_bytes, + bool has_tid, + bool has_key_idx) { + assert(vp8 != NULL); + if (*data_length <= 0) + return -1; + + if (has_tid) { + vp8->temporalIdx = ((**data >> 6) & 0x03); + vp8->layerSync = (**data & 0x20) ? true : false; // Y bit + } + if (has_key_idx) { + vp8->keyIdx = (**data & 0x1F); + } + (*data)++; + (*parsed_bytes)++; + (*data_length)--; + return 0; +} + +int ParseVP8Extension(RTPVideoHeaderVP8* vp8, + const uint8_t* data, + int data_length) { + assert(vp8 != NULL); + int parsed_bytes = 0; + if (data_length <= 0) + return -1; + // Optional X field is present. + bool has_picture_id = (*data & 0x80) ? true : false; // I bit + bool has_tl0_pic_idx = (*data & 0x40) ? true : false; // L bit + bool has_tid = (*data & 0x20) ? true : false; // T bit + bool has_key_idx = (*data & 0x10) ? true : false; // K bit + + // Advance data and decrease remaining payload size. + data++; + parsed_bytes++; + data_length--; + + if (has_picture_id) { + if (ParseVP8PictureID(vp8, &data, &data_length, &parsed_bytes) != 0) { + return -1; + } + } + + if (has_tl0_pic_idx) { + if (ParseVP8Tl0PicIdx(vp8, &data, &data_length, &parsed_bytes) != 0) { + return -1; + } + } + + if (has_tid || has_key_idx) { + if (ParseVP8TIDAndKeyIdx( + vp8, &data, &data_length, &parsed_bytes, has_tid, has_key_idx) != + 0) { + return -1; + } + } + return parsed_bytes; +} + +int ParseVP8FrameSize(RtpDepacketizer::ParsedPayload* parsed_payload, + const uint8_t* data, + int data_length) { + assert(parsed_payload != NULL); + if (parsed_payload->frame_type != kVideoFrameKey) { + // Included in payload header for I-frames. + return 0; + } + if (data_length < 10) { + // For an I-frame we should always have the uncompressed VP8 header + // in the beginning of the partition. + return -1; + } + parsed_payload->type.Video.width = ((data[7] << 8) + data[6]) & 0x3FFF; + parsed_payload->type.Video.height = ((data[9] << 8) + data[8]) & 0x3FFF; + return 0; +} +} // namespace // Define how the VP8PacketizerModes are implemented. // Modes are: kStrict, kAggregate, kEqualSize. -const RtpFormatVp8::AggregationMode RtpFormatVp8::aggr_modes_[kNumModes] = - { kAggrNone, kAggrPartitions, kAggrFragments }; -const bool RtpFormatVp8::balance_modes_[kNumModes] = - { true, true, true }; -const bool RtpFormatVp8::separate_first_modes_[kNumModes] = - { true, false, false }; - -RtpFormatVp8::RtpFormatVp8(const uint8_t* payload_data, - uint32_t payload_size, - const RTPVideoHeaderVP8& hdr_info, - int max_payload_len, - const RTPFragmentationHeader& fragmentation, - VP8PacketizerMode mode) - : payload_data_(payload_data), - payload_size_(static_cast(payload_size)), +const RtpPacketizerVp8::AggregationMode RtpPacketizerVp8::aggr_modes_ + [kNumModes] = {kAggrNone, kAggrPartitions, kAggrFragments}; +const bool RtpPacketizerVp8::balance_modes_[kNumModes] = {true, true, true}; +const bool RtpPacketizerVp8::separate_first_modes_[kNumModes] = {true, false, + false}; + +RtpPacketizerVp8::RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info, + int max_payload_len, + VP8PacketizerMode mode) + : payload_data_(NULL), + payload_size_(0), vp8_fixed_payload_descriptor_bytes_(1), aggr_mode_(aggr_modes_[mode]), balance_(balance_modes_[mode]), separate_first_(separate_first_modes_[mode]), hdr_info_(hdr_info), - num_partitions_(fragmentation.fragmentationVectorSize), + num_partitions_(0), max_payload_len_(max_payload_len), packets_calculated_(false) { - part_info_.CopyFrom(fragmentation); } -RtpFormatVp8::RtpFormatVp8(const uint8_t* payload_data, - uint32_t payload_size, - const RTPVideoHeaderVP8& hdr_info, - int max_payload_len) - : payload_data_(payload_data), - payload_size_(static_cast(payload_size)), +RtpPacketizerVp8::RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info, + int max_payload_len) + : payload_data_(NULL), + payload_size_(0), part_info_(), vp8_fixed_payload_descriptor_bytes_(1), aggr_mode_(aggr_modes_[kEqualSize]), balance_(balance_modes_[kEqualSize]), separate_first_(separate_first_modes_[kEqualSize]), hdr_info_(hdr_info), - num_partitions_(1), + num_partitions_(0), max_payload_len_(max_payload_len), packets_calculated_(false) { +} + +RtpPacketizerVp8::~RtpPacketizerVp8() { +} + +void RtpPacketizerVp8::SetPayloadData( + const uint8_t* payload_data, + size_t payload_size, + const RTPFragmentationHeader* fragmentation) { + payload_data_ = payload_data; + payload_size_ = payload_size; + if (fragmentation) { + part_info_.CopyFrom(*fragmentation); + num_partitions_ = fragmentation->fragmentationVectorSize; + } else { part_info_.VerifyAndAllocateFragmentationHeader(1); part_info_.fragmentationLength[0] = payload_size; part_info_.fragmentationOffset[0] = 0; + num_partitions_ = part_info_.fragmentationVectorSize; + } } -RtpFormatVp8::~RtpFormatVp8() {} - -int RtpFormatVp8::NextPacket(uint8_t* buffer, - int* bytes_to_send, - bool* last_packet) { +bool RtpPacketizerVp8::NextPacket(uint8_t* buffer, + size_t* bytes_to_send, + bool* last_packet) { if (!packets_calculated_) { int ret = 0; if (aggr_mode_ == kAggrPartitions && balance_) { @@ -80,26 +209,51 @@ ret = GeneratePackets(); } if (ret < 0) { - return ret; + return false; } } if (packets_.empty()) { - return -1; + return false; } InfoStruct packet_info = packets_.front(); packets_.pop(); - *bytes_to_send = WriteHeaderAndPayload(packet_info, buffer, max_payload_len_); - if (*bytes_to_send < 0) { - return -1; + int bytes = WriteHeaderAndPayload(packet_info, buffer, max_payload_len_); + if (bytes < 0) { + return false; } + *bytes_to_send = bytes; *last_packet = packets_.empty(); - return packet_info.first_partition_ix; + return true; +} + +ProtectionType RtpPacketizerVp8::GetProtectionType() { + bool protect = + hdr_info_.temporalIdx == 0 || hdr_info_.temporalIdx == kNoTemporalIdx; + return protect ? kProtectedPacket : kUnprotectedPacket; } -int RtpFormatVp8::CalcNextSize(int max_payload_len, int remaining_bytes, - bool split_payload) const { +StorageType RtpPacketizerVp8::GetStorageType(uint32_t retransmission_settings) { + StorageType storage = kAllowRetransmission; + if (hdr_info_.temporalIdx == 0 && + !(retransmission_settings & kRetransmitBaseLayer)) { + storage = kDontRetransmit; + } else if (hdr_info_.temporalIdx != kNoTemporalIdx && + hdr_info_.temporalIdx > 0 && + !(retransmission_settings & kRetransmitHigherLayers)) { + storage = kDontRetransmit; + } + return storage; +} + +std::string RtpPacketizerVp8::ToString() { + return "RtpPacketizerVp8"; +} + +int RtpPacketizerVp8::CalcNextSize(int max_payload_len, + int remaining_bytes, + bool split_payload) const { if (max_payload_len == 0 || remaining_bytes == 0) { return 0; } @@ -113,17 +267,17 @@ // Number of fragments for remaining_bytes: int num_frags = remaining_bytes / max_payload_len + 1; // Number of bytes in this fragment: - return static_cast(static_cast(remaining_bytes) - / num_frags + 0.5); + return static_cast(static_cast(remaining_bytes) / num_frags + + 0.5); } else { return max_payload_len >= remaining_bytes ? remaining_bytes - : max_payload_len; + : max_payload_len; } } -int RtpFormatVp8::GeneratePackets() { - if (max_payload_len_ < vp8_fixed_payload_descriptor_bytes_ - + PayloadDescriptorExtraLength() + 1) { +int RtpPacketizerVp8::GeneratePackets() { + if (max_payload_len_ < vp8_fixed_payload_descriptor_bytes_ + + PayloadDescriptorExtraLength() + 1) { // The provided payload length is not long enough for the payload // descriptor and one payload byte. Return an error. return -1; @@ -133,16 +287,18 @@ bool beginning = true; int part_ix = 0; while (total_bytes_processed < payload_size_) { - int packet_bytes = 0; // How much data to send in this packet. + int packet_bytes = 0; // How much data to send in this packet. bool split_payload = true; // Splitting of partitions is initially allowed. int remaining_in_partition = part_info_.fragmentationOffset[part_ix] - - total_bytes_processed + part_info_.fragmentationLength[part_ix]; - int rem_payload_len = max_payload_len_ - + total_bytes_processed + + part_info_.fragmentationLength[part_ix]; + int rem_payload_len = + max_payload_len_ - (vp8_fixed_payload_descriptor_bytes_ + PayloadDescriptorExtraLength()); int first_partition_in_packet = part_ix; - while (int next_size = CalcNextSize(rem_payload_len, remaining_in_partition, - split_payload)) { + while (int next_size = CalcNextSize( + rem_payload_len, remaining_in_partition, split_payload)) { packet_bytes += next_size; rem_payload_len -= next_size; remaining_in_partition -= next_size; @@ -155,7 +311,7 @@ // with an intact partition (indicated by first_fragment_ == true). if (part_ix + 1 < num_partitions_ && ((aggr_mode_ == kAggrFragments) || - (aggr_mode_ == kAggrPartitions && start_on_new_fragment))) { + (aggr_mode_ == kAggrPartitions && start_on_new_fragment))) { assert(part_ix < num_partitions_); remaining_in_partition = part_info_.fragmentationLength[++part_ix]; // Disallow splitting unless kAggrFragments. In kAggrPartitions, @@ -171,7 +327,9 @@ } assert(packet_bytes > 0); - QueuePacket(total_bytes_processed, packet_bytes, first_partition_in_packet, + QueuePacket(total_bytes_processed, + packet_bytes, + first_partition_in_packet, start_on_new_fragment); total_bytes_processed += packet_bytes; start_on_new_fragment = (remaining_in_partition == 0); @@ -182,16 +340,16 @@ return 0; } -int RtpFormatVp8::GeneratePacketsBalancedAggregates() { - if (max_payload_len_ < vp8_fixed_payload_descriptor_bytes_ - + PayloadDescriptorExtraLength() + 1) { +int RtpPacketizerVp8::GeneratePacketsBalancedAggregates() { + if (max_payload_len_ < vp8_fixed_payload_descriptor_bytes_ + + PayloadDescriptorExtraLength() + 1) { // The provided payload length is not long enough for the payload // descriptor and one payload byte. Return an error. return -1; } std::vector partition_decision; - const int overhead = vp8_fixed_payload_descriptor_bytes_ + - PayloadDescriptorExtraLength(); + const int overhead = + vp8_fixed_payload_descriptor_bytes_ + PayloadDescriptorExtraLength(); const uint32_t max_payload_len = max_payload_len_ - overhead; int min_size, max_size; AggregateSmallPartitions(&partition_decision, &min_size, &max_size); @@ -207,10 +365,11 @@ const int packet_bytes = (remaining_partition + num_fragments - 1) / num_fragments; for (int n = 0; n < num_fragments; ++n) { - const int this_packet_bytes = packet_bytes < remaining_partition ? - packet_bytes : remaining_partition; - QueuePacket(total_bytes_processed, this_packet_bytes, part_ix, - (n == 0)); + const int this_packet_bytes = packet_bytes < remaining_partition + ? packet_bytes + : remaining_partition; + QueuePacket( + total_bytes_processed, this_packet_bytes, part_ix, (n == 0)); remaining_partition -= this_packet_bytes; total_bytes_processed += this_packet_bytes; if (this_packet_bytes < min_size) { @@ -227,13 +386,15 @@ const int first_partition_in_packet = part_ix; const int aggregation_index = partition_decision[part_ix]; while (static_cast(part_ix) < partition_decision.size() && - partition_decision[part_ix] == aggregation_index) { + partition_decision[part_ix] == aggregation_index) { // Collect all partitions that were aggregated into the same packet. this_packet_bytes += part_info_.fragmentationLength[part_ix]; ++part_ix; } - QueuePacket(total_bytes_processed, this_packet_bytes, - first_partition_in_packet, true); + QueuePacket(total_bytes_processed, + this_packet_bytes, + first_partition_in_packet, + true); total_bytes_processed += this_packet_bytes; } } @@ -241,16 +402,16 @@ return 0; } -void RtpFormatVp8::AggregateSmallPartitions(std::vector* partition_vec, - int* min_size, - int* max_size) { +void RtpPacketizerVp8::AggregateSmallPartitions(std::vector* partition_vec, + int* min_size, + int* max_size) { assert(min_size && max_size); *min_size = -1; *max_size = -1; assert(partition_vec); partition_vec->assign(num_partitions_, -1); - const int overhead = vp8_fixed_payload_descriptor_bytes_ + - PayloadDescriptorExtraLength(); + const int overhead = + vp8_fixed_payload_descriptor_bytes_ + PayloadDescriptorExtraLength(); const uint32_t max_payload_len = max_payload_len_ - overhead; int first_in_set = 0; int last_in_set = 0; @@ -261,12 +422,12 @@ // Found start of a set. last_in_set = first_in_set; while (last_in_set + 1 < num_partitions_ && - part_info_.fragmentationLength[last_in_set + 1] < max_payload_len) { + part_info_.fragmentationLength[last_in_set + 1] < + max_payload_len) { ++last_in_set; } // Found end of a set. Run optimized aggregator. It is ok if start == end. - Vp8PartitionAggregator aggregator(part_info_, first_in_set, - last_in_set); + Vp8PartitionAggregator aggregator(part_info_, first_in_set, last_in_set); if (*min_size >= 0 && *max_size >= 0) { aggregator.SetPriorMinMax(*min_size, *max_size); } @@ -285,10 +446,10 @@ } } -void RtpFormatVp8::QueuePacket(int start_pos, - int packet_size, - int first_partition_in_packet, - bool start_on_new_fragment) { +void RtpPacketizerVp8::QueuePacket(int start_pos, + int packet_size, + int first_partition_in_packet, + bool start_on_new_fragment) { // Write info to packet info struct and store in packet info queue. InfoStruct packet_info; packet_info.payload_start_pos = start_pos; @@ -298,9 +459,9 @@ packets_.push(packet_info); } -int RtpFormatVp8::WriteHeaderAndPayload(const InfoStruct& packet_info, - uint8_t* buffer, - int buffer_length) const { +int RtpPacketizerVp8::WriteHeaderAndPayload(const InfoStruct& packet_info, + uint8_t* buffer, + int buffer_length) const { // Write the VP8 payload descriptor. // 0 // 0 1 2 3 4 5 6 7 8 @@ -318,43 +479,47 @@ assert(packet_info.size > 0); buffer[0] = 0; - if (XFieldPresent()) buffer[0] |= kXBit; - if (hdr_info_.nonReference) buffer[0] |= kNBit; - if (packet_info.first_fragment) buffer[0] |= kSBit; + if (XFieldPresent()) + buffer[0] |= kXBit; + if (hdr_info_.nonReference) + buffer[0] |= kNBit; + if (packet_info.first_fragment) + buffer[0] |= kSBit; buffer[0] |= (packet_info.first_partition_ix & kPartIdField); const int extension_length = WriteExtensionFields(buffer, buffer_length); memcpy(&buffer[vp8_fixed_payload_descriptor_bytes_ + extension_length], - &payload_data_[packet_info.payload_start_pos], packet_info.size); + &payload_data_[packet_info.payload_start_pos], + packet_info.size); // Return total length of written data. - return packet_info.size + vp8_fixed_payload_descriptor_bytes_ - + extension_length; + return packet_info.size + vp8_fixed_payload_descriptor_bytes_ + + extension_length; } -int RtpFormatVp8::WriteExtensionFields(uint8_t* buffer, - int buffer_length) const { +int RtpPacketizerVp8::WriteExtensionFields(uint8_t* buffer, + int buffer_length) const { int extension_length = 0; if (XFieldPresent()) { uint8_t* x_field = buffer + vp8_fixed_payload_descriptor_bytes_; *x_field = 0; extension_length = 1; // One octet for the X field. if (PictureIdPresent()) { - if (WritePictureIDFields(x_field, buffer, buffer_length, - &extension_length) < 0) { + if (WritePictureIDFields( + x_field, buffer, buffer_length, &extension_length) < 0) { return -1; } } if (TL0PicIdxFieldPresent()) { - if (WriteTl0PicIdxFields(x_field, buffer, buffer_length, - &extension_length) < 0) { + if (WriteTl0PicIdxFields( + x_field, buffer, buffer_length, &extension_length) < 0) { return -1; } } if (TIDFieldPresent() || KeyIdxFieldPresent()) { - if (WriteTIDAndKeyIdxFields(x_field, buffer, buffer_length, - &extension_length) < 0) { + if (WriteTIDAndKeyIdxFields( + x_field, buffer, buffer_length, &extension_length) < 0) { return -1; } } @@ -363,26 +528,25 @@ return extension_length; } -int RtpFormatVp8::WritePictureIDFields(uint8_t* x_field, - uint8_t* buffer, - int buffer_length, - int* extension_length) const { +int RtpPacketizerVp8::WritePictureIDFields(uint8_t* x_field, + uint8_t* buffer, + int buffer_length, + int* extension_length) const { *x_field |= kIBit; const int pic_id_length = WritePictureID( buffer + vp8_fixed_payload_descriptor_bytes_ + *extension_length, - buffer_length - vp8_fixed_payload_descriptor_bytes_ - - *extension_length); - if (pic_id_length < 0) return -1; + buffer_length - vp8_fixed_payload_descriptor_bytes_ - *extension_length); + if (pic_id_length < 0) + return -1; *extension_length += pic_id_length; return 0; } -int RtpFormatVp8::WritePictureID(uint8_t* buffer, - int buffer_length) const { - const uint16_t pic_id = - static_cast (hdr_info_.pictureId); +int RtpPacketizerVp8::WritePictureID(uint8_t* buffer, int buffer_length) const { + const uint16_t pic_id = static_cast(hdr_info_.pictureId); int picture_id_len = PictureIdLength(); - if (picture_id_len > buffer_length) return -1; + if (picture_id_len > buffer_length) + return -1; if (picture_id_len == 2) { buffer[0] = 0x80 | ((pic_id >> 8) & 0x7F); buffer[1] = pic_id & 0xFF; @@ -392,27 +556,27 @@ return picture_id_len; } -int RtpFormatVp8::WriteTl0PicIdxFields(uint8_t* x_field, - uint8_t* buffer, - int buffer_length, - int* extension_length) const { - if (buffer_length < vp8_fixed_payload_descriptor_bytes_ + *extension_length - + 1) { +int RtpPacketizerVp8::WriteTl0PicIdxFields(uint8_t* x_field, + uint8_t* buffer, + int buffer_length, + int* extension_length) const { + if (buffer_length < + vp8_fixed_payload_descriptor_bytes_ + *extension_length + 1) { return -1; } *x_field |= kLBit; - buffer[vp8_fixed_payload_descriptor_bytes_ - + *extension_length] = hdr_info_.tl0PicIdx; + buffer[vp8_fixed_payload_descriptor_bytes_ + *extension_length] = + hdr_info_.tl0PicIdx; ++*extension_length; return 0; } -int RtpFormatVp8::WriteTIDAndKeyIdxFields(uint8_t* x_field, - uint8_t* buffer, - int buffer_length, - int* extension_length) const { - if (buffer_length < vp8_fixed_payload_descriptor_bytes_ + *extension_length - + 1) { +int RtpPacketizerVp8::WriteTIDAndKeyIdxFields(uint8_t* x_field, + uint8_t* buffer, + int buffer_length, + int* extension_length) const { + if (buffer_length < + vp8_fixed_payload_descriptor_bytes_ + *extension_length + 1) { return -1; } uint8_t* data_field = @@ -420,7 +584,7 @@ *data_field = 0; if (TIDFieldPresent()) { *x_field |= kTBit; - assert(hdr_info_.temporalIdx >= 0 && hdr_info_.temporalIdx <= 3); + assert(hdr_info_.temporalIdx <= 3); *data_field |= hdr_info_.temporalIdx << 6; *data_field |= hdr_info_.layerSync ? kYBit : 0; } @@ -432,15 +596,18 @@ return 0; } -int RtpFormatVp8::PayloadDescriptorExtraLength() const { +int RtpPacketizerVp8::PayloadDescriptorExtraLength() const { int length_bytes = PictureIdLength(); - if (TL0PicIdxFieldPresent()) ++length_bytes; - if (TIDFieldPresent() || KeyIdxFieldPresent()) ++length_bytes; - if (length_bytes > 0) ++length_bytes; // Include the extension field. + if (TL0PicIdxFieldPresent()) + ++length_bytes; + if (TIDFieldPresent() || KeyIdxFieldPresent()) + ++length_bytes; + if (length_bytes > 0) + ++length_bytes; // Include the extension field. return length_bytes; } -int RtpFormatVp8::PictureIdLength() const { +int RtpPacketizerVp8::PictureIdLength() const { if (hdr_info_.pictureId == kNoPictureId) { return 0; } @@ -450,22 +617,115 @@ return 2; } -bool RtpFormatVp8::XFieldPresent() const { - return (TIDFieldPresent() || TL0PicIdxFieldPresent() || PictureIdPresent() - || KeyIdxFieldPresent()); +bool RtpPacketizerVp8::XFieldPresent() const { + return (TIDFieldPresent() || TL0PicIdxFieldPresent() || PictureIdPresent() || + KeyIdxFieldPresent()); } -bool RtpFormatVp8::TIDFieldPresent() const { +bool RtpPacketizerVp8::TIDFieldPresent() const { assert((hdr_info_.layerSync == false) || (hdr_info_.temporalIdx != kNoTemporalIdx)); return (hdr_info_.temporalIdx != kNoTemporalIdx); } -bool RtpFormatVp8::KeyIdxFieldPresent() const { +bool RtpPacketizerVp8::KeyIdxFieldPresent() const { return (hdr_info_.keyIdx != kNoKeyIdx); } -bool RtpFormatVp8::TL0PicIdxFieldPresent() const { +bool RtpPacketizerVp8::TL0PicIdxFieldPresent() const { return (hdr_info_.tl0PicIdx != kNoTl0PicIdx); } + +// +// VP8 format: +// +// Payload descriptor +// 0 1 2 3 4 5 6 7 +// +-+-+-+-+-+-+-+-+ +// |X|R|N|S|PartID | (REQUIRED) +// +-+-+-+-+-+-+-+-+ +// X: |I|L|T|K| RSV | (OPTIONAL) +// +-+-+-+-+-+-+-+-+ +// I: | PictureID | (OPTIONAL) +// +-+-+-+-+-+-+-+-+ +// L: | TL0PICIDX | (OPTIONAL) +// +-+-+-+-+-+-+-+-+ +// T/K: |TID:Y| KEYIDX | (OPTIONAL) +// +-+-+-+-+-+-+-+-+ +// +// Payload header (considered part of the actual payload, sent to decoder) +// 0 1 2 3 4 5 6 7 +// +-+-+-+-+-+-+-+-+ +// |Size0|H| VER |P| +// +-+-+-+-+-+-+-+-+ +// | ... | +// + + +bool RtpDepacketizerVp8::Parse(ParsedPayload* parsed_payload, + const uint8_t* payload_data, + size_t payload_data_length) { + assert(parsed_payload != NULL); + + // Parse mandatory first byte of payload descriptor. + bool extension = (*payload_data & 0x80) ? true : false; // X bit + bool beginning_of_partition = (*payload_data & 0x10) ? true : false; // S bit + int partition_id = (*payload_data & 0x0F); // PartID field + + parsed_payload->type.Video.width = 0; + parsed_payload->type.Video.height = 0; + parsed_payload->type.Video.isFirstPacket = + beginning_of_partition && (partition_id == 0); + parsed_payload->type.Video.codec = kRtpVideoVp8; + parsed_payload->type.Video.codecHeader.VP8.nonReference = + (*payload_data & 0x20) ? true : false; // N bit + parsed_payload->type.Video.codecHeader.VP8.partitionId = partition_id; + parsed_payload->type.Video.codecHeader.VP8.beginningOfPartition = + beginning_of_partition; + parsed_payload->type.Video.codecHeader.VP8.pictureId = kNoPictureId; + parsed_payload->type.Video.codecHeader.VP8.tl0PicIdx = kNoTl0PicIdx; + parsed_payload->type.Video.codecHeader.VP8.temporalIdx = kNoTemporalIdx; + parsed_payload->type.Video.codecHeader.VP8.layerSync = false; + parsed_payload->type.Video.codecHeader.VP8.keyIdx = kNoKeyIdx; + + if (partition_id > 8) { + // Weak check for corrupt payload_data: PartID MUST NOT be larger than 8. + return false; + } + + // Advance payload_data and decrease remaining payload size. + payload_data++; + payload_data_length--; + + if (extension) { + const int parsed_bytes = + ParseVP8Extension(&parsed_payload->type.Video.codecHeader.VP8, + payload_data, + payload_data_length); + if (parsed_bytes < 0) + return false; + payload_data += parsed_bytes; + payload_data_length -= parsed_bytes; + } + + if (payload_data_length <= 0) { + LOG(LS_ERROR) << "Error parsing VP8 payload descriptor!"; + return false; + } + + // Read P bit from payload header (only at beginning of first partition). + if (payload_data_length > 0 && beginning_of_partition && partition_id == 0) { + parsed_payload->frame_type = + (*payload_data & 0x01) ? kVideoFrameDelta : kVideoFrameKey; + } else { + parsed_payload->frame_type = kVideoFrameDelta; + } + + if (ParseVP8FrameSize(parsed_payload, payload_data, payload_data_length) != + 0) { + return false; + } + + parsed_payload->payload = payload_data; + parsed_payload->payload_length = payload_data_length; + return true; +} } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h 2015-02-03 14:33:36.000000000 +0000 @@ -26,10 +26,12 @@ #define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VP8_H_ #include +#include #include +#include "webrtc/base/constructormagic.h" #include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_format.h" #include "webrtc/typedefs.h" namespace webrtc { @@ -44,25 +46,24 @@ }; // Packetizer for VP8. -class RtpFormatVp8 { +class RtpPacketizerVp8 : public RtpPacketizer { public: // Initialize with payload from encoder and fragmentation info. // The payload_data must be exactly one encoded VP8 frame. - RtpFormatVp8(const uint8_t* payload_data, - uint32_t payload_size, - const RTPVideoHeaderVP8& hdr_info, - int max_payload_len, - const RTPFragmentationHeader& fragmentation, - VP8PacketizerMode mode); + RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info, + int max_payload_len, + VP8PacketizerMode mode); // Initialize without fragmentation info. Mode kEqualSize will be used. // The payload_data must be exactly one encoded VP8 frame. - RtpFormatVp8(const uint8_t* payload_data, - uint32_t payload_size, - const RTPVideoHeaderVP8& hdr_info, - int max_payload_len); + RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info, int max_payload_len); - ~RtpFormatVp8(); + virtual ~RtpPacketizerVp8(); + + virtual void SetPayloadData( + const uint8_t* payload_data, + size_t payload_size, + const RTPFragmentationHeader* fragmentation) OVERRIDE; // Get the next payload with VP8 payload header. // max_payload_len limits the sum length of payload and VP8 payload header. @@ -75,9 +76,15 @@ // the first payload byte in the packet is taken, with the first partition // having index 0; returns negative on error. // For the kEqualSize mode: returns 0 on success, return negative on error. - int NextPacket(uint8_t* buffer, - int* bytes_to_send, - bool* last_packet); + virtual bool NextPacket(uint8_t* buffer, + size_t* bytes_to_send, + bool* last_packet) OVERRIDE; + + virtual ProtectionType GetProtectionType() OVERRIDE; + + virtual StorageType GetStorageType(uint32_t retransmission_settings) OVERRIDE; + + virtual std::string ToString() OVERRIDE; private: typedef struct { @@ -96,19 +103,20 @@ static const AggregationMode aggr_modes_[kNumModes]; static const bool balance_modes_[kNumModes]; static const bool separate_first_modes_[kNumModes]; - static const int kXBit = 0x80; - static const int kNBit = 0x20; - static const int kSBit = 0x10; + static const int kXBit = 0x80; + static const int kNBit = 0x20; + static const int kSBit = 0x10; static const int kPartIdField = 0x0F; static const int kKeyIdxField = 0x1F; - static const int kIBit = 0x80; - static const int kLBit = 0x40; - static const int kTBit = 0x20; - static const int kKBit = 0x10; - static const int kYBit = 0x20; + static const int kIBit = 0x80; + static const int kLBit = 0x40; + static const int kTBit = 0x20; + static const int kKBit = 0x10; + static const int kYBit = 0x20; // Calculate size of next chunk to send. Returns 0 if none can be sent. - int CalcNextSize(int max_payload_len, int remaining_bytes, + int CalcNextSize(int max_payload_len, + int remaining_bytes, bool split_payload) const; // Calculate all packet sizes and load to packet info queue. @@ -144,7 +152,6 @@ uint8_t* buffer, int buffer_length) const; - // Write the X field and the appropriate extension fields to buffer. // The function returns the extension length (including X field), or -1 // on error. @@ -152,19 +159,25 @@ // Set the I bit in the x_field, and write PictureID to the appropriate // position in buffer. The function returns 0 on success, -1 otherwise. - int WritePictureIDFields(uint8_t* x_field, uint8_t* buffer, - int buffer_length, int* extension_length) const; + int WritePictureIDFields(uint8_t* x_field, + uint8_t* buffer, + int buffer_length, + int* extension_length) const; // Set the L bit in the x_field, and write Tl0PicIdx to the appropriate // position in buffer. The function returns 0 on success, -1 otherwise. - int WriteTl0PicIdxFields(uint8_t* x_field, uint8_t* buffer, - int buffer_length, int* extension_length) const; + int WriteTl0PicIdxFields(uint8_t* x_field, + uint8_t* buffer, + int buffer_length, + int* extension_length) const; // Set the T and K bits in the x_field, and write TID, Y and KeyIdx to the // appropriate position in buffer. The function returns 0 on success, // -1 otherwise. - int WriteTIDAndKeyIdxFields(uint8_t* x_field, uint8_t* buffer, - int buffer_length, int* extension_length) const; + int WriteTIDAndKeyIdxFields(uint8_t* x_field, + uint8_t* buffer, + int buffer_length, + int* extension_length) const; // Write the PictureID from codec_specific_info_ to buffer. One or two // bytes are written, depending on magnitude of PictureID. The function @@ -187,7 +200,7 @@ bool PictureIdPresent() const { return (PictureIdLength() > 0); } const uint8_t* payload_data_; - const int payload_size_; + int payload_size_; RTPFragmentationHeader part_info_; const int vp8_fixed_payload_descriptor_bytes_; // Length of VP8 payload // descriptors's fixed part. @@ -195,14 +208,22 @@ const bool balance_; const bool separate_first_; const RTPVideoHeaderVP8 hdr_info_; - const int num_partitions_; + int num_partitions_; const int max_payload_len_; InfoQueue packets_; bool packets_calculated_; - DISALLOW_COPY_AND_ASSIGN(RtpFormatVp8); + DISALLOW_COPY_AND_ASSIGN(RtpPacketizerVp8); }; -} // namespace +// Depacketizer for VP8. +class RtpDepacketizerVp8 : public RtpDepacketizer { + public: + virtual ~RtpDepacketizerVp8() {} + virtual bool Parse(ParsedPayload* parsed_payload, + const uint8_t* payload_data, + size_t payload_data_length) OVERRIDE; +}; +} // namespace webrtc #endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VP8_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.cc 2015-02-03 14:33:36.000000000 +0000 @@ -64,20 +64,19 @@ } void RtpFormatVp8TestHelper::GetAllPacketsAndCheck( - RtpFormatVp8* packetizer, + RtpPacketizerVp8* packetizer, const int* expected_sizes, const int* expected_part, const bool* expected_frag_start, int expected_num_packets) { ASSERT_TRUE(inited_); - int send_bytes = 0; + size_t send_bytes = 0; bool last = false; for (int i = 0; i < expected_num_packets; ++i) { std::ostringstream ss; ss << "Checking packet " << i; SCOPED_TRACE(ss.str()); - EXPECT_EQ(expected_part[i], - packetizer->NextPacket(buffer_, &send_bytes, &last)); + EXPECT_TRUE(packetizer->NextPacket(buffer_, &send_bytes, &last)); CheckPacket(send_bytes, expected_sizes[i], last, expected_frag_start[i]); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h 2015-02-03 14:33:36.000000000 +0000 @@ -18,9 +18,9 @@ #ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VP8_TEST_HELPER_H_ #define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VP8_TEST_HELPER_H_ +#include "webrtc/base/constructormagic.h" #include "webrtc/modules/interface/module_common_types.h" #include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/typedefs.h" namespace webrtc { @@ -32,7 +32,7 @@ explicit RtpFormatVp8TestHelper(const RTPVideoHeaderVP8* hdr); ~RtpFormatVp8TestHelper(); bool Init(const int* partition_sizes, int num_partitions); - void GetAllPacketsAndCheck(RtpFormatVp8* packetizer, + void GetAllPacketsAndCheck(RtpPacketizerVp8* packetizer, const int* expected_sizes, const int* expected_part, const bool* expected_frag_start, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -12,21 +12,73 @@ * This file includes unit tests for the VP8 packetizer. */ +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h" #include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h" #include "webrtc/system_wrappers/interface/compile_assert.h" #include "webrtc/typedefs.h" -#define CHECK_ARRAY_SIZE(expected_size, array) \ - COMPILE_ASSERT(expected_size == sizeof(array) / sizeof(array[0]), \ +#define CHECK_ARRAY_SIZE(expected_size, array) \ + COMPILE_ASSERT(expected_size == sizeof(array) / sizeof(array[0]), \ check_array_size); namespace webrtc { +namespace { +// Payload descriptor +// 0 1 2 3 4 5 6 7 +// +-+-+-+-+-+-+-+-+ +// |X|R|N|S|PartID | (REQUIRED) +// +-+-+-+-+-+-+-+-+ +// X: |I|L|T|K| RSV | (OPTIONAL) +// +-+-+-+-+-+-+-+-+ +// I: | PictureID | (OPTIONAL) +// +-+-+-+-+-+-+-+-+ +// L: | TL0PICIDX | (OPTIONAL) +// +-+-+-+-+-+-+-+-+ +// T/K: |TID:Y| KEYIDX | (OPTIONAL) +// +-+-+-+-+-+-+-+-+ +// +// Payload header +// 0 1 2 3 4 5 6 7 +// +-+-+-+-+-+-+-+-+ +// |Size0|H| VER |P| +// +-+-+-+-+-+-+-+-+ +// | Size1 | +// +-+-+-+-+-+-+-+-+ +// | Size2 | +// +-+-+-+-+-+-+-+-+ +// | Bytes 4..N of | +// | VP8 payload | +// : : +// +-+-+-+-+-+-+-+-+ +// | OPTIONAL RTP | +// | padding | +// : : +// +-+-+-+-+-+-+-+-+ +void VerifyBasicHeader(RTPTypeHeader* type, bool N, bool S, int part_id) { + ASSERT_TRUE(type != NULL); + EXPECT_EQ(N, type->Video.codecHeader.VP8.nonReference); + EXPECT_EQ(S, type->Video.codecHeader.VP8.beginningOfPartition); + EXPECT_EQ(part_id, type->Video.codecHeader.VP8.partitionId); +} + +void VerifyExtensions(RTPTypeHeader* type, + int16_t picture_id, /* I */ + int16_t tl0_pic_idx, /* L */ + uint8_t temporal_idx, /* T */ + int key_idx /* K */) { + ASSERT_TRUE(type != NULL); + EXPECT_EQ(picture_id, type->Video.codecHeader.VP8.pictureId); + EXPECT_EQ(tl0_pic_idx, type->Video.codecHeader.VP8.tl0PicIdx); + EXPECT_EQ(temporal_idx, type->Video.codecHeader.VP8.temporalIdx); + EXPECT_EQ(key_idx, type->Video.codecHeader.VP8.keyIdx); +} +} // namespace -class RtpFormatVp8Test : public ::testing::Test { +class RtpPacketizerVp8Test : public ::testing::Test { protected: - RtpFormatVp8Test() : helper_(NULL) {} + RtpPacketizerVp8Test() : helper_(NULL) {} virtual void TearDown() { delete helper_; } bool Init(const int* partition_sizes, int num_partitions) { hdr_info_.pictureId = kNoPictureId; @@ -35,7 +87,8 @@ hdr_info_.layerSync = false; hdr_info_.tl0PicIdx = kNoTl0PicIdx; hdr_info_.keyIdx = kNoKeyIdx; - if (helper_ != NULL) return false; + if (helper_ != NULL) + return false; helper_ = new test::RtpFormatVp8TestHelper(&hdr_info_); return helper_->Init(partition_sizes, num_partitions); } @@ -44,46 +97,45 @@ test::RtpFormatVp8TestHelper* helper_; }; -TEST_F(RtpFormatVp8Test, TestStrictMode) { +TEST_F(RtpPacketizerVp8Test, TestStrictMode) { const int kSizeVector[] = {10, 8, 27}; const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); hdr_info_.pictureId = 200; // > 0x7F should produce 2-byte PictureID. const int kMaxSize = 13; - RtpFormatVp8 packetizer(helper_->payload_data(), - helper_->payload_size(), - hdr_info_, - kMaxSize, - *(helper_->fragmentation()), - kStrict); + RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kStrict); + packetizer.SetPayloadData(helper_->payload_data(), + helper_->payload_size(), + helper_->fragmentation()); // The expected sizes are obtained by running a verified good implementation. const int kExpectedSizes[] = {9, 9, 12, 11, 11, 11, 10}; const int kExpectedPart[] = {0, 0, 1, 2, 2, 2, 2}; - const bool kExpectedFragStart[] = - {true, false, true, true, false, false, false}; + const bool kExpectedFragStart[] = {true, false, true, true, + false, false, false}; const int kExpectedNum = sizeof(kExpectedSizes) / sizeof(kExpectedSizes[0]); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedPart); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } -TEST_F(RtpFormatVp8Test, TestAggregateMode) { +TEST_F(RtpPacketizerVp8Test, TestAggregateMode) { const int kSizeVector[] = {60, 10, 10}; const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); hdr_info_.pictureId = 20; // <= 0x7F should produce 1-byte PictureID. const int kMaxSize = 25; - RtpFormatVp8 packetizer(helper_->payload_data(), - helper_->payload_size(), - hdr_info_, - kMaxSize, - *(helper_->fragmentation()), - kAggregate); + RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate); + packetizer.SetPayloadData(helper_->payload_data(), + helper_->payload_size(), + helper_->fragmentation()); // The expected sizes are obtained by running a verified good implementation. const int kExpectedSizes[] = {23, 23, 23, 23}; @@ -93,23 +145,24 @@ CHECK_ARRAY_SIZE(kExpectedNum, kExpectedPart); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } -TEST_F(RtpFormatVp8Test, TestAggregateModeManyPartitions1) { +TEST_F(RtpPacketizerVp8Test, TestAggregateModeManyPartitions1) { const int kSizeVector[] = {1600, 200, 200, 200, 200, 200, 200, 200, 200}; const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); hdr_info_.pictureId = 20; // <= 0x7F should produce 1-byte PictureID. const int kMaxSize = 1500; - RtpFormatVp8 packetizer(helper_->payload_data(), - helper_->payload_size(), - hdr_info_, - kMaxSize, - *(helper_->fragmentation()), - kAggregate); + RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate); + packetizer.SetPayloadData(helper_->payload_data(), + helper_->payload_size(), + helper_->fragmentation()); // The expected sizes are obtained by running a verified good implementation. const int kExpectedSizes[] = {803, 803, 803, 803}; @@ -119,23 +172,24 @@ CHECK_ARRAY_SIZE(kExpectedNum, kExpectedPart); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } -TEST_F(RtpFormatVp8Test, TestAggregateModeManyPartitions2) { +TEST_F(RtpPacketizerVp8Test, TestAggregateModeManyPartitions2) { const int kSizeVector[] = {1599, 200, 200, 200, 1600, 200, 200, 200, 200}; const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); hdr_info_.pictureId = 20; // <= 0x7F should produce 1-byte PictureID. const int kMaxSize = 1500; - RtpFormatVp8 packetizer(helper_->payload_data(), - helper_->payload_size(), - hdr_info_, - kMaxSize, - *(helper_->fragmentation()), - kAggregate); + RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate); + packetizer.SetPayloadData(helper_->payload_data(), + helper_->payload_size(), + helper_->fragmentation()); // The expected sizes are obtained by running a verified good implementation. const int kExpectedSizes[] = {803, 802, 603, 803, 803, 803}; @@ -145,23 +199,24 @@ CHECK_ARRAY_SIZE(kExpectedNum, kExpectedPart); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } -TEST_F(RtpFormatVp8Test, TestAggregateModeTwoLargePartitions) { +TEST_F(RtpPacketizerVp8Test, TestAggregateModeTwoLargePartitions) { const int kSizeVector[] = {1654, 2268}; const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); hdr_info_.pictureId = 20; // <= 0x7F should produce 1-byte PictureID. const int kMaxSize = 1460; - RtpFormatVp8 packetizer(helper_->payload_data(), - helper_->payload_size(), - hdr_info_, - kMaxSize, - *(helper_->fragmentation()), - kAggregate); + RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate); + packetizer.SetPayloadData(helper_->payload_data(), + helper_->payload_size(), + helper_->fragmentation()); // The expected sizes are obtained by running a verified good implementation. const int kExpectedSizes[] = {830, 830, 1137, 1137}; @@ -171,22 +226,24 @@ CHECK_ARRAY_SIZE(kExpectedNum, kExpectedPart); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } // Verify that EqualSize mode is forced if fragmentation info is missing. -TEST_F(RtpFormatVp8Test, TestEqualSizeModeFallback) { +TEST_F(RtpPacketizerVp8Test, TestEqualSizeModeFallback) { const int kSizeVector[] = {10, 10, 10}; const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); hdr_info_.pictureId = 200; // > 0x7F should produce 2-byte PictureID - const int kMaxSize = 12; // Small enough to produce 4 packets. - RtpFormatVp8 packetizer(helper_->payload_data(), - helper_->payload_size(), - hdr_info_, - kMaxSize); + const int kMaxSize = 12; // Small enough to produce 4 packets. + RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize); + packetizer.SetPayloadData( + helper_->payload_data(), helper_->payload_size(), NULL); // Expecting three full packets, and one with the remainder. const int kExpectedSizes[] = {12, 11, 12, 11}; @@ -198,22 +255,24 @@ CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); helper_->set_sloppy_partitioning(true); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } // Verify that non-reference bit is set. EqualSize mode fallback is expected. -TEST_F(RtpFormatVp8Test, TestNonReferenceBit) { +TEST_F(RtpPacketizerVp8Test, TestNonReferenceBit) { const int kSizeVector[] = {10, 10, 10}; const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); hdr_info_.nonReference = true; const int kMaxSize = 25; // Small enough to produce two packets. - RtpFormatVp8 packetizer(helper_->payload_data(), - helper_->payload_size(), - hdr_info_, - kMaxSize); + RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize); + packetizer.SetPayloadData( + helper_->payload_data(), helper_->payload_size(), NULL); // EqualSize mode => First packet full; other not. const int kExpectedSizes[] = {16, 16}; @@ -225,12 +284,15 @@ CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); helper_->set_sloppy_partitioning(true); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } // Verify Tl0PicIdx and TID fields, and layerSync bit. -TEST_F(RtpFormatVp8Test, TestTl0PicIdxAndTID) { +TEST_F(RtpPacketizerVp8Test, TestTl0PicIdxAndTID) { const int kSizeVector[] = {10, 10, 10}; const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); @@ -240,12 +302,10 @@ hdr_info_.layerSync = true; // kMaxSize is only limited by allocated buffer size. const int kMaxSize = helper_->buffer_size(); - RtpFormatVp8 packetizer(helper_->payload_data(), - helper_->payload_size(), - hdr_info_, - kMaxSize, - *(helper_->fragmentation()), - kAggregate); + RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate); + packetizer.SetPayloadData(helper_->payload_data(), + helper_->payload_size(), + helper_->fragmentation()); // Expect one single packet of payload_size() + 4 bytes header. const int kExpectedSizes[1] = {helper_->payload_size() + 4}; @@ -255,12 +315,15 @@ CHECK_ARRAY_SIZE(kExpectedNum, kExpectedPart); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } // Verify KeyIdx field. -TEST_F(RtpFormatVp8Test, TestKeyIdx) { +TEST_F(RtpPacketizerVp8Test, TestKeyIdx) { const int kSizeVector[] = {10, 10, 10}; const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); @@ -268,12 +331,10 @@ hdr_info_.keyIdx = 17; // kMaxSize is only limited by allocated buffer size. const int kMaxSize = helper_->buffer_size(); - RtpFormatVp8 packetizer(helper_->payload_data(), - helper_->payload_size(), - hdr_info_, - kMaxSize, - *(helper_->fragmentation()), - kAggregate); + RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate); + packetizer.SetPayloadData(helper_->payload_data(), + helper_->payload_size(), + helper_->fragmentation()); // Expect one single packet of payload_size() + 3 bytes header. const int kExpectedSizes[1] = {helper_->payload_size() + 3}; @@ -283,12 +344,15 @@ CHECK_ARRAY_SIZE(kExpectedNum, kExpectedPart); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } // Verify TID field and KeyIdx field in combination. -TEST_F(RtpFormatVp8Test, TestTIDAndKeyIdx) { +TEST_F(RtpPacketizerVp8Test, TestTIDAndKeyIdx) { const int kSizeVector[] = {10, 10, 10}; const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); @@ -297,12 +361,10 @@ hdr_info_.keyIdx = 5; // kMaxSize is only limited by allocated buffer size. const int kMaxSize = helper_->buffer_size(); - RtpFormatVp8 packetizer(helper_->payload_data(), - helper_->payload_size(), - hdr_info_, - kMaxSize, - *(helper_->fragmentation()), - kAggregate); + RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate); + packetizer.SetPayloadData(helper_->payload_data(), + helper_->payload_size(), + helper_->fragmentation()); // Expect one single packet of payload_size() + 3 bytes header. const int kExpectedSizes[1] = {helper_->payload_size() + 3}; @@ -312,8 +374,201 @@ CHECK_ARRAY_SIZE(kExpectedNum, kExpectedPart); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } -} // namespace +class RtpDepacketizerVp8Test : public ::testing::Test { + protected: + RtpDepacketizerVp8Test() + : depacketizer_(RtpDepacketizer::Create(kRtpVideoVp8)) {} + + void ExpectPacket(RtpDepacketizer::ParsedPayload* parsed_payload, + const uint8_t* data, + size_t length) { + ASSERT_TRUE(parsed_payload != NULL); + EXPECT_THAT(std::vector( + parsed_payload->payload, + parsed_payload->payload + parsed_payload->payload_length), + ::testing::ElementsAreArray(data, length)); + } + + scoped_ptr depacketizer_; +}; + +TEST_F(RtpDepacketizerVp8Test, BasicHeader) { + const uint8_t kHeaderLength = 1; + uint8_t packet[4] = {0}; + packet[0] = 0x14; // Binary 0001 0100; S = 1, PartID = 4. + packet[1] = 0x01; // P frame. + RtpDepacketizer::ParsedPayload payload; + + ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet))); + ExpectPacket( + &payload, packet + kHeaderLength, sizeof(packet) - kHeaderLength); + EXPECT_EQ(kVideoFrameDelta, payload.frame_type); + EXPECT_EQ(kRtpVideoVp8, payload.type.Video.codec); + VerifyBasicHeader(&payload.type, 0, 1, 4); + VerifyExtensions( + &payload.type, kNoPictureId, kNoTl0PicIdx, kNoTemporalIdx, kNoKeyIdx); +} + +TEST_F(RtpDepacketizerVp8Test, PictureID) { + const uint8_t kHeaderLength1 = 3; + const uint8_t kHeaderLength2 = 4; + const uint8_t kPictureId = 17; + uint8_t packet[10] = {0}; + packet[0] = 0xA0; + packet[1] = 0x80; + packet[2] = kPictureId; + RtpDepacketizer::ParsedPayload payload; + + ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet))); + ExpectPacket( + &payload, packet + kHeaderLength1, sizeof(packet) - kHeaderLength1); + EXPECT_EQ(kVideoFrameDelta, payload.frame_type); + EXPECT_EQ(kRtpVideoVp8, payload.type.Video.codec); + VerifyBasicHeader(&payload.type, 1, 0, 0); + VerifyExtensions( + &payload.type, kPictureId, kNoTl0PicIdx, kNoTemporalIdx, kNoKeyIdx); + + // Re-use packet, but change to long PictureID. + packet[2] = 0x80 | kPictureId; + packet[3] = kPictureId; + + payload = RtpDepacketizer::ParsedPayload(); + ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet))); + ExpectPacket( + &payload, packet + kHeaderLength2, sizeof(packet) - kHeaderLength2); + VerifyBasicHeader(&payload.type, 1, 0, 0); + VerifyExtensions(&payload.type, + (kPictureId << 8) + kPictureId, + kNoTl0PicIdx, + kNoTemporalIdx, + kNoKeyIdx); +} + +TEST_F(RtpDepacketizerVp8Test, Tl0PicIdx) { + const uint8_t kHeaderLength = 3; + const uint8_t kTl0PicIdx = 17; + uint8_t packet[13] = {0}; + packet[0] = 0x90; + packet[1] = 0x40; + packet[2] = kTl0PicIdx; + RtpDepacketizer::ParsedPayload payload; + + ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet))); + ExpectPacket( + &payload, packet + kHeaderLength, sizeof(packet) - kHeaderLength); + EXPECT_EQ(kVideoFrameKey, payload.frame_type); + EXPECT_EQ(kRtpVideoVp8, payload.type.Video.codec); + VerifyBasicHeader(&payload.type, 0, 1, 0); + VerifyExtensions( + &payload.type, kNoPictureId, kTl0PicIdx, kNoTemporalIdx, kNoKeyIdx); +} + +TEST_F(RtpDepacketizerVp8Test, TIDAndLayerSync) { + const uint8_t kHeaderLength = 3; + uint8_t packet[10] = {0}; + packet[0] = 0x88; + packet[1] = 0x20; + packet[2] = 0x80; // TID(2) + LayerSync(false) + RtpDepacketizer::ParsedPayload payload; + + ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet))); + ExpectPacket( + &payload, packet + kHeaderLength, sizeof(packet) - kHeaderLength); + EXPECT_EQ(kVideoFrameDelta, payload.frame_type); + EXPECT_EQ(kRtpVideoVp8, payload.type.Video.codec); + VerifyBasicHeader(&payload.type, 0, 0, 8); + VerifyExtensions(&payload.type, kNoPictureId, kNoTl0PicIdx, 2, kNoKeyIdx); + EXPECT_FALSE(payload.type.Video.codecHeader.VP8.layerSync); +} + +TEST_F(RtpDepacketizerVp8Test, KeyIdx) { + const uint8_t kHeaderLength = 3; + const uint8_t kKeyIdx = 17; + uint8_t packet[10] = {0}; + packet[0] = 0x88; + packet[1] = 0x10; // K = 1. + packet[2] = kKeyIdx; + RtpDepacketizer::ParsedPayload payload; + + ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet))); + ExpectPacket( + &payload, packet + kHeaderLength, sizeof(packet) - kHeaderLength); + EXPECT_EQ(kVideoFrameDelta, payload.frame_type); + EXPECT_EQ(kRtpVideoVp8, payload.type.Video.codec); + VerifyBasicHeader(&payload.type, 0, 0, 8); + VerifyExtensions( + &payload.type, kNoPictureId, kNoTl0PicIdx, kNoTemporalIdx, kKeyIdx); +} + +TEST_F(RtpDepacketizerVp8Test, MultipleExtensions) { + const uint8_t kHeaderLength = 6; + uint8_t packet[10] = {0}; + packet[0] = 0x88; + packet[1] = 0x80 | 0x40 | 0x20 | 0x10; + packet[2] = 0x80 | 17; // PictureID, high 7 bits. + packet[3] = 17; // PictureID, low 8 bits. + packet[4] = 42; // Tl0PicIdx. + packet[5] = 0x40 | 0x20 | 0x11; // TID(1) + LayerSync(true) + KEYIDX(17). + RtpDepacketizer::ParsedPayload payload; + + ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet))); + ExpectPacket( + &payload, packet + kHeaderLength, sizeof(packet) - kHeaderLength); + EXPECT_EQ(kVideoFrameDelta, payload.frame_type); + EXPECT_EQ(kRtpVideoVp8, payload.type.Video.codec); + VerifyBasicHeader(&payload.type, 0, 0, 8); + VerifyExtensions(&payload.type, (17 << 8) + 17, 42, 1, 17); +} + +TEST_F(RtpDepacketizerVp8Test, TooShortHeader) { + uint8_t packet[4] = {0}; + packet[0] = 0x88; + packet[1] = 0x80 | 0x40 | 0x20 | 0x10; // All extensions are enabled... + packet[2] = 0x80 | 17; // ... but only 2 bytes PictureID is provided. + packet[3] = 17; // PictureID, low 8 bits. + RtpDepacketizer::ParsedPayload payload; + + EXPECT_FALSE(depacketizer_->Parse(&payload, packet, sizeof(packet))); +} + +TEST_F(RtpDepacketizerVp8Test, TestWithPacketizer) { + const uint8_t kHeaderLength = 5; + uint8_t data[10] = {0}; + uint8_t packet[20] = {0}; + RTPVideoHeaderVP8 input_header; + input_header.nonReference = true; + input_header.pictureId = 300; + input_header.temporalIdx = 1; + input_header.layerSync = false; + input_header.tl0PicIdx = kNoTl0PicIdx; // Disable. + input_header.keyIdx = 31; + RtpPacketizerVp8 packetizer(input_header, 20); + packetizer.SetPayloadData(data, 10, NULL); + bool last; + size_t send_bytes; + ASSERT_TRUE(packetizer.NextPacket(packet, &send_bytes, &last)); + ASSERT_TRUE(last); + RtpDepacketizer::ParsedPayload payload; + + ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet))); + ExpectPacket( + &payload, packet + kHeaderLength, sizeof(packet) - kHeaderLength); + EXPECT_EQ(kVideoFrameKey, payload.frame_type); + EXPECT_EQ(kRtpVideoVp8, payload.type.Video.codec); + VerifyBasicHeader(&payload.type, 1, 1, 0); + VerifyExtensions(&payload.type, + input_header.pictureId, + input_header.tl0PicIdx, + input_header.temporalIdx, + input_header.keyIdx); + EXPECT_EQ(payload.type.Video.codecHeader.VP8.layerSync, + input_header.layerSync); +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extension.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extension.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extension.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extension.cc 2015-02-03 14:33:36.000000000 +0000 @@ -65,6 +65,16 @@ return 0; } +bool RtpHeaderExtensionMap::IsRegistered(RTPExtensionType type) const { + std::map::const_iterator it = + extensionMap_.begin(); + for (; it != extensionMap_.end(); ++it) { + if (it->second->type == type) + return true; + } + return false; +} + int32_t RtpHeaderExtensionMap::GetType(const uint8_t id, RTPExtensionType* type) const { assert(type); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h 2015-02-03 14:33:36.000000000 +0000 @@ -22,7 +22,7 @@ const size_t kRtpOneByteHeaderLength = 4; const size_t kTransmissionTimeOffsetLength = 4; -const size_t kAudioLevelLength = 2; +const size_t kAudioLevelLength = 4; const size_t kAbsoluteSendTimeLength = 4; struct HeaderExtension { @@ -37,11 +37,7 @@ length = kTransmissionTimeOffsetLength; break; case kRtpExtensionAudioLevel: - // TODO(solenberg): Because of how the audio level extension is handled - // in RTPSenderAudio::SendAudio(), we cannot set the actual length here - // but must leave it at zero. The consequence is that any other header - // extensions registered for an audio channel are effectively ignored. - // length = kAudioLevelLength; + length = kAudioLevelLength; break; case kRtpExtensionAbsoluteSendTime: length = kAbsoluteSendTimeLength; @@ -66,6 +62,8 @@ int32_t Deregister(const RTPExtensionType type); + bool IsRegistered(RTPExtensionType type) const; + int32_t GetType(const uint8_t id, RTPExtensionType* type) const; int32_t GetId(const RTPExtensionType type, uint8_t* id) const; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_parser.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_parser.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_parser.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_parser.cc 2015-02-03 14:33:36.000000000 +0000 @@ -13,7 +13,6 @@ #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { @@ -22,7 +21,8 @@ RtpHeaderParserImpl(); virtual ~RtpHeaderParserImpl() {} - virtual bool Parse(const uint8_t* packet, int length, + virtual bool Parse(const uint8_t* packet, + size_t length, RTPHeader* header) const OVERRIDE; virtual bool RegisterRtpHeaderExtension(RTPExtensionType type, @@ -32,7 +32,7 @@ private: scoped_ptr critical_section_; - RtpHeaderExtensionMap rtp_header_extension_map_; + RtpHeaderExtensionMap rtp_header_extension_map_ GUARDED_BY(critical_section_); }; RtpHeaderParser* RtpHeaderParser::Create() { @@ -42,14 +42,15 @@ RtpHeaderParserImpl::RtpHeaderParserImpl() : critical_section_(CriticalSectionWrapper::CreateCriticalSection()) {} -bool RtpHeaderParser::IsRtcp(const uint8_t* packet, int length) { - ModuleRTPUtility::RTPHeaderParser rtp_parser(packet, length); +bool RtpHeaderParser::IsRtcp(const uint8_t* packet, size_t length) { + RtpUtility::RtpHeaderParser rtp_parser(packet, length); return rtp_parser.RTCP(); } -bool RtpHeaderParserImpl::Parse(const uint8_t* packet, int length, - RTPHeader* header) const { - ModuleRTPUtility::RTPHeaderParser rtp_parser(packet, length); +bool RtpHeaderParserImpl::Parse(const uint8_t* packet, + size_t length, + RTPHeader* header) const { + RtpUtility::RtpHeaderParser rtp_parser(packet, length); memset(header, 0, sizeof(*header)); RtpHeaderExtensionMap map; @@ -60,8 +61,6 @@ const bool valid_rtpheader = rtp_parser.Parse(*header, &map); if (!valid_rtpheader) { - WEBRTC_TRACE(kTraceDebug, kTraceRtpRtcp, -1, - "IncomingPacket invalid RTP header"); return false; } return true; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet_history.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet_history.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet_history.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet_history.cc 2015-02-03 14:33:36.000000000 +0000 @@ -18,7 +18,7 @@ #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { @@ -33,13 +33,21 @@ } RTPPacketHistory::~RTPPacketHistory() { - Free(); + { + CriticalSectionScoped cs(critsect_); + Free(); + } delete critsect_; } -void RTPPacketHistory::SetStorePacketsStatus(bool enable, +void RTPPacketHistory::SetStorePacketsStatus(bool enable, uint16_t number_to_store) { + CriticalSectionScoped cs(critsect_); if (enable) { + if (store_) { + LOG(LS_WARNING) << "Purging packet history in order to re-set status."; + Free(); + } Allocate(number_to_store); } else { Free(); @@ -48,13 +56,7 @@ void RTPPacketHistory::Allocate(uint16_t number_to_store) { assert(number_to_store > 0); - CriticalSectionScoped cs(critsect_); - if (store_) { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1, - "SetStorePacketsStatus already set, number: %d", number_to_store); - return; - } - + assert(!store_); store_ = true; stored_packets_.resize(number_to_store); stored_seq_nums_.resize(number_to_store); @@ -65,13 +67,12 @@ } void RTPPacketHistory::Free() { - CriticalSectionScoped cs(critsect_); if (!store_) { return; } std::vector >::iterator it; - for (it = stored_packets_.begin(); it != stored_packets_.end(); ++it) { + for (it = stored_packets_.begin(); it != stored_packets_.end(); ++it) { it->clear(); } @@ -130,8 +131,8 @@ VerifyAndAllocatePacketLength(max_packet_length); if (packet_length > max_packet_length_) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, -1, - "Failed to store RTP packet, length: %d", packet_length); + LOG(LS_WARNING) << "Failed to store RTP packet with length: " + << packet_length; return -1; } @@ -156,46 +157,6 @@ return 0; } -int32_t RTPPacketHistory::ReplaceRTPHeader(const uint8_t* packet, - uint16_t sequence_number, - uint16_t rtp_header_length) { - CriticalSectionScoped cs(critsect_); - if (!store_) { - return 0; - } - - assert(packet); - assert(rtp_header_length > 3); - - if (rtp_header_length > max_packet_length_) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "Failed to replace RTP packet, length: %d", rtp_header_length); - return -1; - } - - int32_t index = 0; - bool found = FindSeqNum(sequence_number, &index); - if (!found) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "No match for getting seqNum %u", sequence_number); - return -1; - } - - uint16_t length = stored_lengths_.at(index); - if (length == 0 || length > max_packet_length_) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "No match for getting seqNum %u, len %d", sequence_number, length); - return -1; - } - assert(stored_seq_nums_[index] == sequence_number); - - // Update RTP header. - std::vector >::iterator it = - stored_packets_.begin() + index; - std::copy(packet, packet + rtp_header_length, it->begin()); - return 0; -} - bool RTPPacketHistory::HasRTPPacket(uint16_t sequence_number) const { CriticalSectionScoped cs(critsect_); if (!store_) { @@ -207,7 +168,7 @@ if (!found) { return false; } - + uint16_t length = stored_lengths_.at(index); if (length == 0 || length > max_packet_length_) { // Invalid length. @@ -222,6 +183,7 @@ uint8_t* packet, uint16_t* packet_length, int64_t* stored_time_ms) { + assert(*packet_length >= max_packet_length_); CriticalSectionScoped cs(critsect_); if (!store_) { return false; @@ -230,30 +192,22 @@ int32_t index = 0; bool found = FindSeqNum(sequence_number, &index); if (!found) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "No match for getting seqNum %u", sequence_number); + LOG(LS_WARNING) << "No match for getting seqNum " << sequence_number; return false; } uint16_t length = stored_lengths_.at(index); - if (length == 0 || length > max_packet_length_) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "No match for getting seqNum %u, len %d", sequence_number, length); - return false; - } - - if (length > *packet_length) { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1, - "Input buffer too short for packet %u", sequence_number); + assert(length <= max_packet_length_); + if (length == 0) { + LOG(LS_WARNING) << "No match for getting seqNum " << sequence_number + << ", len " << length; return false; } - // Verify elapsed time since last retrieve. + // Verify elapsed time since last retrieve. int64_t now = clock_->TimeInMilliseconds(); if (min_elapsed_time_ms > 0 && ((now - stored_send_times_.at(index)) < min_elapsed_time_ms)) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "Skip getting packet %u, packet recently resent.", sequence_number); return false; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet_history.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet_history.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet_history.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet_history.h 2015-02-03 14:33:36.000000000 +0000 @@ -15,6 +15,7 @@ #include +#include "webrtc/base/thread_annotations.h" #include "webrtc/modules/interface/module_common_types.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" #include "webrtc/typedefs.h" @@ -40,14 +41,6 @@ int64_t capture_time_ms, StorageType type); - // Replaces the stored RTP packet with matching sequence number with the - // RTP header of the provided packet. - // Note: Calling this function assumes that the RTP header length should not - // have changed since the packet was stored. - int32_t ReplaceRTPHeader(const uint8_t* packet, - uint16_t sequence_number, - uint16_t rtp_header_length); - // Gets stored RTP packet corresponding to the input sequence number. // The packet is copied to the buffer pointed to by ptr_rtp_packet. // The rtp_packet_length should show the available buffer size. @@ -74,8 +67,8 @@ private: void GetPacket(int index, uint8_t* packet, uint16_t* packet_length, int64_t* stored_time_ms) const; - void Allocate(uint16_t number_to_store); - void Free(); + void Allocate(uint16_t number_to_store) EXCLUSIVE_LOCKS_REQUIRED(*critsect_); + void Free() EXCLUSIVE_LOCKS_REQUIRED(*critsect_); void VerifyAndAllocatePacketLength(uint16_t packet_length); bool FindSeqNum(uint16_t sequence_number, int32_t* index) const; int FindBestFittingPacket(uint16_t size) const; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -103,19 +103,6 @@ kAllowRetransmission)); } -TEST_F(RtpPacketHistoryTest, GetRtpPacket_TooSmallBuffer) { - hist_->SetStorePacketsStatus(true, 10); - uint16_t len = 0; - int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); - CreateRtpPacket(kSeqNum, kSsrc, kPayload, kTimestamp, packet_, &len); - EXPECT_EQ(0, hist_->PutRTPPacket(packet_, len, kMaxPacketLength, - capture_time_ms, kAllowRetransmission)); - uint16_t len_out = len - 1; - int64_t time; - EXPECT_FALSE(hist_->GetPacketAndSetSendTime(kSeqNum, 0, false, packet_, - &len_out, &time)); -} - TEST_F(RtpPacketHistoryTest, GetRtpPacket_NotStored) { hist_->SetStorePacketsStatus(true, 10); uint16_t len = kMaxPacketLength; @@ -155,42 +142,6 @@ } } -TEST_F(RtpPacketHistoryTest, ReplaceRtpHeader) { - hist_->SetStorePacketsStatus(true, 10); - - uint16_t len = 0; - int64_t capture_time_ms = 1; - CreateRtpPacket(kSeqNum, kSsrc, kPayload, kTimestamp, packet_, &len); - // Replace should fail, packet is not stored. - EXPECT_EQ(-1, hist_->ReplaceRTPHeader(packet_, kSeqNum, len)); - EXPECT_EQ(0, hist_->PutRTPPacket(packet_, len, kMaxPacketLength, - capture_time_ms, kAllowRetransmission)); - - // Create modified packet and replace. - len = 0; - CreateRtpPacket(kSeqNum, kSsrc + 1, kPayload + 2, kTimestamp, packet_, &len); - EXPECT_EQ(0, hist_->ReplaceRTPHeader(packet_, kSeqNum, len)); - - uint16_t len_out = kMaxPacketLength; - int64_t time; - EXPECT_TRUE(hist_->GetPacketAndSetSendTime(kSeqNum, 0, false, packet_out_, - &len_out, &time)); - EXPECT_EQ(len, len_out); - EXPECT_EQ(capture_time_ms, time); - for (int i = 0; i < len; i++) { - EXPECT_EQ(packet_[i], packet_out_[i]); - } - - // Replace should fail, too large length. - EXPECT_EQ(-1, hist_->ReplaceRTPHeader(packet_, kSeqNum, - kMaxPacketLength + 1)); - - // Replace should fail, packet is not stored. - len = 0; - CreateRtpPacket(kSeqNum + 1, kSsrc, kPayload, kTimestamp, packet_, &len); - EXPECT_EQ(-1, hist_->ReplaceRTPHeader(packet_, kSeqNum + 1, len)); -} - TEST_F(RtpPacketHistoryTest, NoCaptureTime) { hist_->SetStorePacketsStatus(true, 10); uint16_t len = 0; @@ -236,10 +187,10 @@ capture_time_ms, kAllowRetransmission)); int64_t time; + len = kMaxPacketLength; EXPECT_TRUE(hist_->GetPacketAndSetSendTime(kSeqNum, 100, false, packet_, &len, &time)); fake_clock_.AdvanceTimeMilliseconds(100); - // Time has elapsed. len = kMaxPacketLength; EXPECT_TRUE(hist_->GetPacketAndSetSendTime(kSeqNum, 100, false, packet_, &len, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,15 +10,13 @@ #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { RTPPayloadRegistry::RTPPayloadRegistry( - const int32_t id, RTPPayloadStrategy* rtp_payload_strategy) : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), - id_(id), rtp_payload_strategy_(rtp_payload_strategy), red_payload_type_(-1), ulpfec_payload_type_(-1), @@ -31,7 +29,7 @@ RTPPayloadRegistry::~RTPPayloadRegistry() { while (!payload_type_map_.empty()) { - ModuleRTPUtility::PayloadTypeMap::iterator it = payload_type_map_.begin(); + RtpUtility::PayloadTypeMap::iterator it = payload_type_map_.begin(); delete it->second; payload_type_map_.erase(it); } @@ -60,9 +58,8 @@ case 77: // 205 Transport layer FB message. case 78: // 206 Payload-specific FB message. case 79: // 207 Extended report. - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, - "%s invalid payloadtype:%d", - __FUNCTION__, payload_type); + LOG(LS_ERROR) << "Can't register invalid receiver payload type: " + << payload_type; return -1; default: break; @@ -72,12 +69,12 @@ CriticalSectionScoped cs(crit_sect_.get()); - ModuleRTPUtility::PayloadTypeMap::iterator it = - payload_type_map_.find(payload_type); + RtpUtility::PayloadTypeMap::iterator it = + payload_type_map_.find(payload_type); if (it != payload_type_map_.end()) { // We already use this payload type. - ModuleRTPUtility::Payload* payload = it->second; + RtpUtility::Payload* payload = it->second; assert(payload); @@ -86,7 +83,7 @@ // Check if it's the same as we already have. // If same, ignore sending an error. if (payload_name_length == name_length && - ModuleRTPUtility::StringCompare( + RtpUtility::StringCompare( payload->name, payload_name, payload_name_length)) { if (rtp_payload_strategy_->PayloadIsCompatible(*payload, frequency, channels, rate)) { @@ -94,9 +91,7 @@ return 0; } } - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, - "%s invalid argument payload_type:%d already registered", - __FUNCTION__, payload_type); + LOG(LS_ERROR) << "Payload type already registered: " << payload_type; return -1; } @@ -105,18 +100,18 @@ payload_name, payload_name_length, frequency, channels, rate); } - ModuleRTPUtility::Payload* payload = NULL; + RtpUtility::Payload* payload = NULL; // Save the RED payload type. Used in both audio and video. - if (ModuleRTPUtility::StringCompare(payload_name, "red", 3)) { + if (RtpUtility::StringCompare(payload_name, "red", 3)) { red_payload_type_ = payload_type; - payload = new ModuleRTPUtility::Payload; + payload = new RtpUtility::Payload; memset(payload, 0, sizeof(*payload)); payload->audio = false; strncpy(payload->name, payload_name, RTP_PAYLOAD_NAME_SIZE - 1); - } else if (ModuleRTPUtility::StringCompare(payload_name, "ulpfec", 3)) { + } else if (RtpUtility::StringCompare(payload_name, "ulpfec", 3)) { ulpfec_payload_type_ = payload_type; - payload = new ModuleRTPUtility::Payload; + payload = new RtpUtility::Payload; memset(payload, 0, sizeof(*payload)); payload->audio = false; strncpy(payload->name, payload_name, RTP_PAYLOAD_NAME_SIZE - 1); @@ -137,15 +132,9 @@ int32_t RTPPayloadRegistry::DeRegisterReceivePayload( const int8_t payload_type) { CriticalSectionScoped cs(crit_sect_.get()); - ModuleRTPUtility::PayloadTypeMap::iterator it = - payload_type_map_.find(payload_type); - - if (it == payload_type_map_.end()) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, - "%s failed to find payload_type:%d", - __FUNCTION__, payload_type); - return -1; - } + RtpUtility::PayloadTypeMap::iterator it = + payload_type_map_.find(payload_type); + assert(it != payload_type_map_.end()); delete it->second; payload_type_map_.erase(it); return 0; @@ -160,15 +149,14 @@ const uint32_t frequency, const uint8_t channels, const uint32_t rate) { - ModuleRTPUtility::PayloadTypeMap::iterator iterator = - payload_type_map_.begin(); + RtpUtility::PayloadTypeMap::iterator iterator = payload_type_map_.begin(); for (; iterator != payload_type_map_.end(); ++iterator) { - ModuleRTPUtility::Payload* payload = iterator->second; + RtpUtility::Payload* payload = iterator->second; size_t name_length = strlen(payload->name); - if (payload_name_length == name_length - && ModuleRTPUtility::StringCompare(payload->name, payload_name, - payload_name_length)) { + if (payload_name_length == name_length && + RtpUtility::StringCompare( + payload->name, payload_name, payload_name_length)) { // We found the payload name in the list. // If audio, check frequency and rate. if (payload->audio) { @@ -179,7 +167,7 @@ payload_type_map_.erase(iterator); break; } - } else if (ModuleRTPUtility::StringCompare(payload_name, "red", 3)) { + } else if (RtpUtility::StringCompare(payload_name, "red", 3)) { delete payload; payload_type_map_.erase(iterator); break; @@ -194,25 +182,20 @@ const uint8_t channels, const uint32_t rate, int8_t* payload_type) const { - if (payload_type == NULL) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, - "%s invalid argument", __FUNCTION__); - return -1; - } + assert(payload_type); size_t payload_name_length = strlen(payload_name); CriticalSectionScoped cs(crit_sect_.get()); - ModuleRTPUtility::PayloadTypeMap::const_iterator it = - payload_type_map_.begin(); + RtpUtility::PayloadTypeMap::const_iterator it = payload_type_map_.begin(); for (; it != payload_type_map_.end(); ++it) { - ModuleRTPUtility::Payload* payload = it->second; + RtpUtility::Payload* payload = it->second; assert(payload); size_t name_length = strlen(payload->name); if (payload_name_length == name_length && - ModuleRTPUtility::StringCompare( + RtpUtility::StringCompare( payload->name, payload_name, payload_name_length)) { // Name matches. if (payload->audio) { @@ -243,12 +226,6 @@ return -1; } -void RTPPayloadRegistry::SetRtxStatus(bool enable, uint32_t ssrc) { - CriticalSectionScoped cs(crit_sect_.get()); - rtx_ = enable; - ssrc_rtx_ = ssrc; -} - bool RTPPayloadRegistry::RtxEnabled() const { CriticalSectionScoped cs(crit_sect_.get()); return rtx_; @@ -282,9 +259,9 @@ *packet_length -= kRtxHeaderSize; // Replace the SSRC and the sequence number with the originals. - ModuleRTPUtility::AssignUWord16ToBuffer(*restored_packet + 2, - original_sequence_number); - ModuleRTPUtility::AssignUWord32ToBuffer(*restored_packet + 8, original_ssrc); + RtpUtility::AssignUWord16ToBuffer(*restored_packet + 2, + original_sequence_number); + RtpUtility::AssignUWord32ToBuffer(*restored_packet + 8, original_ssrc); CriticalSectionScoped cs(crit_sect_.get()); @@ -296,17 +273,24 @@ (*restored_packet)[1] |= kRtpMarkerBitMask; // Marker bit is set. } } else { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, id_, - "Incorrect RTX configuration, dropping packet."); + LOG(LS_WARNING) << "Incorrect RTX configuration, dropping packet."; return false; } } return true; } +void RTPPayloadRegistry::SetRtxSsrc(uint32_t ssrc) { + CriticalSectionScoped cs(crit_sect_.get()); + ssrc_rtx_ = ssrc; + rtx_ = true; +} + void RTPPayloadRegistry::SetRtxPayloadType(int payload_type) { CriticalSectionScoped cs(crit_sect_.get()); + assert(payload_type >= 0); payload_type_rtx_ = payload_type; + rtx_ = true; } bool RTPPayloadRegistry::IsRed(const RTPHeader& header) const { @@ -321,8 +305,8 @@ bool RTPPayloadRegistry::GetPayloadSpecifics(uint8_t payload_type, PayloadUnion* payload) const { CriticalSectionScoped cs(crit_sect_.get()); - ModuleRTPUtility::PayloadTypeMap::const_iterator it = - payload_type_map_.find(payload_type); + RtpUtility::PayloadTypeMap::const_iterator it = + payload_type_map_.find(payload_type); // Check that this is a registered payload type. if (it == payload_type_map_.end()) { @@ -334,7 +318,7 @@ int RTPPayloadRegistry::GetPayloadTypeFrequency( uint8_t payload_type) const { - ModuleRTPUtility::Payload* payload; + RtpUtility::Payload* payload; if (!PayloadTypeToPayload(payload_type, payload)) { return -1; } @@ -343,12 +327,12 @@ } bool RTPPayloadRegistry::PayloadTypeToPayload( - const uint8_t payload_type, - ModuleRTPUtility::Payload*& payload) const { + const uint8_t payload_type, + RtpUtility::Payload*& payload) const { CriticalSectionScoped cs(crit_sect_.get()); - ModuleRTPUtility::PayloadTypeMap::const_iterator it = - payload_type_map_.find(payload_type); + RtpUtility::PayloadTypeMap::const_iterator it = + payload_type_map_.find(payload_type); // Check that this is a registered payload type. if (it == payload_type_map_.end()) { @@ -379,11 +363,10 @@ public: virtual bool CodecsMustBeUnique() const OVERRIDE { return true; } - virtual bool PayloadIsCompatible( - const ModuleRTPUtility::Payload& payload, - const uint32_t frequency, - const uint8_t channels, - const uint32_t rate) const OVERRIDE { + virtual bool PayloadIsCompatible(const RtpUtility::Payload& payload, + const uint32_t frequency, + const uint8_t channels, + const uint32_t rate) const OVERRIDE { return payload.audio && payload.typeSpecific.Audio.frequency == frequency && @@ -392,19 +375,18 @@ payload.typeSpecific.Audio.rate == 0 || rate == 0); } - virtual void UpdatePayloadRate( - ModuleRTPUtility::Payload* payload, - const uint32_t rate) const OVERRIDE { + virtual void UpdatePayloadRate(RtpUtility::Payload* payload, + const uint32_t rate) const OVERRIDE { payload->typeSpecific.Audio.rate = rate; } - virtual ModuleRTPUtility::Payload* CreatePayloadType( + virtual RtpUtility::Payload* CreatePayloadType( const char payloadName[RTP_PAYLOAD_NAME_SIZE], const int8_t payloadType, const uint32_t frequency, const uint8_t channels, const uint32_t rate) const OVERRIDE { - ModuleRTPUtility::Payload* payload = new ModuleRTPUtility::Payload; + RtpUtility::Payload* payload = new RtpUtility::Payload; payload->name[RTP_PAYLOAD_NAME_SIZE - 1] = 0; strncpy(payload->name, payloadName, RTP_PAYLOAD_NAME_SIZE - 1); assert(frequency >= 1000); @@ -415,8 +397,7 @@ return payload; } - int GetPayloadTypeFrequency( - const ModuleRTPUtility::Payload& payload) const { + int GetPayloadTypeFrequency(const RtpUtility::Payload& payload) const { return payload.typeSpecific.Audio.frequency; } }; @@ -425,39 +406,37 @@ public: virtual bool CodecsMustBeUnique() const OVERRIDE { return false; } - virtual bool PayloadIsCompatible( - const ModuleRTPUtility::Payload& payload, - const uint32_t frequency, - const uint8_t channels, - const uint32_t rate) const OVERRIDE { + virtual bool PayloadIsCompatible(const RtpUtility::Payload& payload, + const uint32_t frequency, + const uint8_t channels, + const uint32_t rate) const OVERRIDE { return !payload.audio; } - virtual void UpdatePayloadRate( - ModuleRTPUtility::Payload* payload, - const uint32_t rate) const OVERRIDE { + virtual void UpdatePayloadRate(RtpUtility::Payload* payload, + const uint32_t rate) const OVERRIDE { payload->typeSpecific.Video.maxRate = rate; } - virtual ModuleRTPUtility::Payload* CreatePayloadType( + virtual RtpUtility::Payload* CreatePayloadType( const char payloadName[RTP_PAYLOAD_NAME_SIZE], const int8_t payloadType, const uint32_t frequency, const uint8_t channels, const uint32_t rate) const OVERRIDE { RtpVideoCodecTypes videoType = kRtpVideoGeneric; - if (ModuleRTPUtility::StringCompare(payloadName, "VP8", 3)) { + if (RtpUtility::StringCompare(payloadName, "VP8", 3)) { videoType = kRtpVideoVp8; - } else if (ModuleRTPUtility::StringCompare(payloadName, "H264", 4)) { + } else if (RtpUtility::StringCompare(payloadName, "H264", 4)) { videoType = kRtpVideoH264; - } else if (ModuleRTPUtility::StringCompare(payloadName, "I420", 4)) { + } else if (RtpUtility::StringCompare(payloadName, "I420", 4)) { videoType = kRtpVideoGeneric; - } else if (ModuleRTPUtility::StringCompare(payloadName, "ULPFEC", 6)) { + } else if (RtpUtility::StringCompare(payloadName, "ULPFEC", 6)) { videoType = kRtpVideoNone; } else { videoType = kRtpVideoGeneric; } - ModuleRTPUtility::Payload* payload = new ModuleRTPUtility::Payload; + RtpUtility::Payload* payload = new RtpUtility::Payload; payload->name[RTP_PAYLOAD_NAME_SIZE - 1] = 0; strncpy(payload->name, payloadName, RTP_PAYLOAD_NAME_SIZE - 1); @@ -467,8 +446,7 @@ return payload; } - int GetPayloadTypeFrequency( - const ModuleRTPUtility::Payload& payload) const { + int GetPayloadTypeFrequency(const RtpUtility::Payload& payload) const { return kVideoPayloadTypeFrequency; } }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_payload_registry_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_payload_registry_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_payload_registry_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_payload_registry_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -32,23 +32,23 @@ void SetUp() { // Note: the payload registry takes ownership of the strategy. mock_payload_strategy_ = new testing::NiceMock(); - rtp_payload_registry_.reset( - new RTPPayloadRegistry(123, mock_payload_strategy_)); + rtp_payload_registry_.reset(new RTPPayloadRegistry(mock_payload_strategy_)); } protected: - ModuleRTPUtility::Payload* ExpectReturnOfTypicalAudioPayload( - uint8_t payload_type, uint32_t rate) { + RtpUtility::Payload* ExpectReturnOfTypicalAudioPayload(uint8_t payload_type, + uint32_t rate) { bool audio = true; - ModuleRTPUtility::Payload returned_payload = { "name", audio, { - // Initialize the audio struct in this case. - { kTypicalFrequency, kTypicalChannels, rate } - }}; + RtpUtility::Payload returned_payload = { + "name", + audio, + {// Initialize the audio struct in this case. + {kTypicalFrequency, kTypicalChannels, rate}}}; // Note: we return a new payload since the payload registry takes ownership // of the created object. - ModuleRTPUtility::Payload* returned_payload_on_heap = - new ModuleRTPUtility::Payload(returned_payload); + RtpUtility::Payload* returned_payload_on_heap = + new RtpUtility::Payload(returned_payload); EXPECT_CALL(*mock_payload_strategy_, CreatePayloadType(kTypicalPayloadName, payload_type, kTypicalFrequency, @@ -63,7 +63,7 @@ TEST_F(RtpPayloadRegistryTest, RegistersAndRemembersPayloadsUntilDeregistered) { uint8_t payload_type = 97; - ModuleRTPUtility::Payload* returned_payload_on_heap = + RtpUtility::Payload* returned_payload_on_heap = ExpectReturnOfTypicalAudioPayload(payload_type, kTypicalRate); bool new_payload_created = false; @@ -73,7 +73,7 @@ EXPECT_TRUE(new_payload_created) << "A new payload WAS created."; - ModuleRTPUtility::Payload* retrieved_payload = NULL; + RtpUtility::Payload* retrieved_payload = NULL; EXPECT_TRUE(rtp_payload_registry_->PayloadTypeToPayload(payload_type, retrieved_payload)); @@ -100,7 +100,7 @@ ASSERT_EQ(red_type_of_the_day, rtp_payload_registry_->red_payload_type()); - ModuleRTPUtility::Payload* retrieved_payload = NULL; + RtpUtility::Payload* retrieved_payload = NULL; EXPECT_TRUE(rtp_payload_registry_->PayloadTypeToPayload(red_type_of_the_day, retrieved_payload)); EXPECT_FALSE(retrieved_payload->audio); @@ -112,7 +112,7 @@ uint8_t payload_type = 97; bool ignored = false; - ModuleRTPUtility::Payload* first_payload_on_heap = + RtpUtility::Payload* first_payload_on_heap = ExpectReturnOfTypicalAudioPayload(payload_type, kTypicalRate); EXPECT_EQ(0, rtp_payload_registry_->RegisterReceivePayload( kTypicalPayloadName, payload_type, kTypicalFrequency, kTypicalChannels, @@ -122,7 +122,7 @@ kTypicalPayloadName, payload_type, kTypicalFrequency, kTypicalChannels, kTypicalRate, &ignored)) << "Adding same codec twice = bad."; - ModuleRTPUtility::Payload* second_payload_on_heap = + RtpUtility::Payload* second_payload_on_heap = ExpectReturnOfTypicalAudioPayload(payload_type - 1, kTypicalRate); EXPECT_EQ(0, rtp_payload_registry_->RegisterReceivePayload( kTypicalPayloadName, payload_type - 1, kTypicalFrequency, @@ -130,7 +130,7 @@ "With a different payload type is fine though."; // Ensure both payloads are preserved. - ModuleRTPUtility::Payload* retrieved_payload = NULL; + RtpUtility::Payload* retrieved_payload = NULL; EXPECT_TRUE(rtp_payload_registry_->PayloadTypeToPayload(payload_type, retrieved_payload)); EXPECT_EQ(first_payload_on_heap, retrieved_payload); @@ -169,7 +169,7 @@ kTypicalPayloadName, payload_type - 1, kTypicalFrequency, kTypicalChannels, kTypicalRate, &ignored)); - ModuleRTPUtility::Payload* retrieved_payload = NULL; + RtpUtility::Payload* retrieved_payload = NULL; EXPECT_FALSE(rtp_payload_registry_->PayloadTypeToPayload( payload_type, retrieved_payload)) << "The first payload should be " "deregistered because the only thing that differs is payload type."; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_audio.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_audio.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_audio.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_audio.cc 2015-02-03 14:33:36.000000000 +0000 @@ -15,7 +15,7 @@ #include // memcpy() #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/trace_event.h" namespace webrtc { @@ -159,10 +159,10 @@ uint32_t frequency) { CriticalSectionScoped lock(crit_sect_.get()); - if (ModuleRTPUtility::StringCompare(payload_name, "telephone-event", 15)) { + if (RtpUtility::StringCompare(payload_name, "telephone-event", 15)) { telephone_event_payload_type_ = payload_type; } - if (ModuleRTPUtility::StringCompare(payload_name, "cn", 2)) { + if (RtpUtility::StringCompare(payload_name, "cn", 2)) { // we can have three CNG on 8000Hz, 16000Hz and 32000Hz if (frequency == 8000) { cng_nb_payload_type_ = payload_type; @@ -277,11 +277,8 @@ specific_payload.Audio.frequency, specific_payload.Audio.channels, specific_payload.Audio.rate)) { - WEBRTC_TRACE(kTraceError, - kTraceRtpRtcp, - id, - "Failed to create video decoder for payload type:%d", - payload_type); + LOG(LS_ERROR) << "Failed to create decoder for payload type: " + << payload_name << "/" << payload_type; return -1; } return 0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_audio.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_audio.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_audio.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_audio.h 2015-02-03 14:33:36.000000000 +0000 @@ -83,7 +83,7 @@ // We do not allow codecs to have multiple payload types for audio, so we // need to override the default behavior (which is to do nothing). void PossiblyRemoveExistingPayloadType( - ModuleRTPUtility::PayloadTypeMap* payload_type_map, + RtpUtility::PayloadTypeMap* payload_type_map, const char payload_name[RTP_PAYLOAD_NAME_SIZE], size_t payload_name_length, uint32_t frequency, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.cc 2015-02-03 14:33:36.000000000 +0000 @@ -18,14 +18,13 @@ #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" #include "webrtc/modules/rtp_rtcp/source/rtp_receiver_strategy.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { -using ModuleRTPUtility::GetCurrentRTP; -using ModuleRTPUtility::Payload; -using ModuleRTPUtility::RTPPayloadParser; -using ModuleRTPUtility::StringCompare; +using RtpUtility::GetCurrentRTP; +using RtpUtility::Payload; +using RtpUtility::StringCompare; RtpReceiver* RtpReceiver::CreateVideoReceiver( int id, Clock* clock, @@ -39,7 +38,7 @@ return new RtpReceiverImpl( id, clock, NullObjectRtpAudioFeedback(), incoming_messages_callback, rtp_payload_registry, - RTPReceiverStrategy::CreateVideoStrategy(id, incoming_payload_callback)); + RTPReceiverStrategy::CreateVideoStrategy(incoming_payload_callback)); } RtpReceiver* RtpReceiver::CreateAudioReceiver( @@ -87,8 +86,6 @@ assert(incoming_messages_callback); memset(current_remote_csrc_, 0, sizeof(current_remote_csrc_)); - - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id, "%s created", __FUNCTION__); } RtpReceiverImpl::~RtpReceiverImpl() { @@ -96,17 +93,6 @@ cb_rtp_feedback_->OnIncomingCSRCChanged(id_, current_remote_csrc_[i], false); } - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id_, "%s deleted", __FUNCTION__); -} - -RTPReceiverStrategy* RtpReceiverImpl::GetMediaReceiver() const { - return rtp_media_receiver_.get(); -} - -RtpVideoCodecTypes RtpReceiverImpl::VideoCodecType() const { - PayloadUnion media_specific; - rtp_media_receiver_->GetLastMediaSpecificPayload(&media_specific); - return media_specific.Video.videoCodecType; } int32_t RtpReceiverImpl::RegisterReceivePayload( @@ -127,9 +113,8 @@ if (created_new_payload) { if (rtp_media_receiver_->OnNewPayloadTypeCreated(payload_name, payload_type, frequency) != 0) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, - "%s failed to register payload", - __FUNCTION__); + LOG(LS_ERROR) << "Failed to register payload: " << payload_name << "/" + << payload_type; return -1; } } @@ -182,19 +167,12 @@ PayloadUnion payload_specific, bool in_order) { // Sanity check. - if (payload_length < 0) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, - "%s invalid argument", - __FUNCTION__); - return false; - } - int8_t first_payload_byte = 0; - if (payload_length > 0) { - first_payload_byte = payload[0]; - } + assert(payload_length >= 0); + // Trigger our callbacks. CheckSSRCChanged(rtp_header); + int8_t first_payload_byte = payload_length > 0 ? payload[0] : 0; bool is_red = false; bool should_reset_statistics = false; @@ -205,14 +183,9 @@ &should_reset_statistics) == -1) { if (payload_length == 0) { // OK, keep-alive packet. - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_, - "%s received keepalive", - __FUNCTION__); return true; } - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, id_, - "%s received invalid payloadtype", - __FUNCTION__); + LOG(LS_WARNING) << "Receiving invalid payload type."; return false; } @@ -347,9 +320,8 @@ id_, rtp_header.payloadType, payload_name, rtp_header.payload_type_frequency, channels, rate)) { // New stream, same codec. - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, - "Failed to create decoder for payload type:%d", - rtp_header.payloadType); + LOG(LS_ERROR) << "Failed to create decoder for payload type: " + << rtp_header.payloadType; } } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.h 2015-02-03 14:33:36.000000000 +0000 @@ -34,53 +34,42 @@ virtual ~RtpReceiverImpl(); - RTPReceiverStrategy* GetMediaReceiver() const; - - int32_t RegisterReceivePayload( + virtual int32_t RegisterReceivePayload( const char payload_name[RTP_PAYLOAD_NAME_SIZE], const int8_t payload_type, const uint32_t frequency, const uint8_t channels, - const uint32_t rate); + const uint32_t rate) OVERRIDE; - int32_t DeRegisterReceivePayload(const int8_t payload_type); + virtual int32_t DeRegisterReceivePayload(const int8_t payload_type) OVERRIDE; - bool IncomingRtpPacket( + virtual bool IncomingRtpPacket( const RTPHeader& rtp_header, const uint8_t* payload, int payload_length, PayloadUnion payload_specific, - bool in_order); + bool in_order) OVERRIDE; - NACKMethod NACK() const; + virtual NACKMethod NACK() const OVERRIDE; // Turn negative acknowledgement requests on/off. - void SetNACKStatus(const NACKMethod method); + virtual void SetNACKStatus(const NACKMethod method) OVERRIDE; // Returns the last received timestamp. - bool Timestamp(uint32_t* timestamp) const; - bool LastReceivedTimeMs(int64_t* receive_time_ms) const; - - uint32_t SSRC() const; - - int32_t CSRCs(uint32_t array_of_csrc[kRtpCsrcSize]) const; + virtual bool Timestamp(uint32_t* timestamp) const OVERRIDE; + virtual bool LastReceivedTimeMs(int64_t* receive_time_ms) const OVERRIDE; - int32_t Energy(uint8_t array_of_energy[kRtpCsrcSize]) const; + virtual uint32_t SSRC() const OVERRIDE; - // RTX. - void SetRTXStatus(bool enable, uint32_t ssrc); + virtual int32_t CSRCs(uint32_t array_of_csrc[kRtpCsrcSize]) const OVERRIDE; - void RTXStatus(bool* enable, uint32_t* ssrc, int* payload_type) const; + virtual int32_t Energy(uint8_t array_of_energy[kRtpCsrcSize]) const OVERRIDE; - void SetRtxPayloadType(int payload_type); - - TelephoneEventHandler* GetTelephoneEventHandler(); + virtual TelephoneEventHandler* GetTelephoneEventHandler() OVERRIDE; private: bool HaveReceivedFrame() const; - RtpVideoCodecTypes VideoCodecType() const; - void CheckSSRCChanged(const RTPHeader& rtp_header); void CheckCSRC(const WebRtcRTPHeader& rtp_header); int32_t CheckPayloadChanged(const RTPHeader& rtp_header, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_strategy.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_strategy.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_strategy.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_strategy.h 2015-02-03 14:33:36.000000000 +0000 @@ -26,8 +26,7 @@ // This class is not thread-safe and must be protected by its caller. class RTPReceiverStrategy { public: - static RTPReceiverStrategy* CreateVideoStrategy(int32_t id, - RtpData* data_callback); + static RTPReceiverStrategy* CreateVideoStrategy(RtpData* data_callback); static RTPReceiverStrategy* CreateAudioStrategy( int32_t id, RtpData* data_callback, RtpAudioFeedback* incoming_messages_callback); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.cc 2015-02-03 14:33:36.000000000 +0000 @@ -20,29 +20,29 @@ #endif #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_format.h" #include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h" #include "webrtc/modules/rtp_rtcp/source/rtp_format_h264.h" #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/trace_event.h" namespace webrtc { RTPReceiverStrategy* RTPReceiverStrategy::CreateVideoStrategy( - int32_t id, RtpData* data_callback) { - return new RTPReceiverVideo(id, data_callback); + RtpData* data_callback) { + return new RTPReceiverVideo(data_callback); } -RTPReceiverVideo::RTPReceiverVideo(int32_t id, RtpData* data_callback) - : RTPReceiverStrategy(data_callback), - id_(id) {} +RTPReceiverVideo::RTPReceiverVideo(RtpData* data_callback) + : RTPReceiverStrategy(data_callback) { +} RTPReceiverVideo::~RTPReceiverVideo() { } -bool RTPReceiverVideo::ShouldReportCsrcChanges( - uint8_t payload_type) const { +bool RTPReceiverVideo::ShouldReportCsrcChanges(uint8_t payload_type) const { // Always do this for video packets. return true; } @@ -54,32 +54,49 @@ return 0; } -int32_t RTPReceiverVideo::ParseRtpPacket( - WebRtcRTPHeader* rtp_header, - const PayloadUnion& specific_payload, - bool is_red, - const uint8_t* payload, - uint16_t payload_length, - int64_t timestamp_ms, - bool is_first_packet) { - TRACE_EVENT2("webrtc_rtp", "Video::ParseRtp", - "seqnum", rtp_header->header.sequenceNumber, - "timestamp", rtp_header->header.timestamp); +int32_t RTPReceiverVideo::ParseRtpPacket(WebRtcRTPHeader* rtp_header, + const PayloadUnion& specific_payload, + bool is_red, + const uint8_t* payload, + uint16_t payload_length, + int64_t timestamp_ms, + bool is_first_packet) { + TRACE_EVENT2("webrtc_rtp", + "Video::ParseRtp", + "seqnum", + rtp_header->header.sequenceNumber, + "timestamp", + rtp_header->header.timestamp); rtp_header->type.Video.codec = specific_payload.Video.videoCodecType; const uint16_t payload_data_length = payload_length - rtp_header->header.paddingLength; - if (payload_data_length == 0) + if (payload == NULL || payload_data_length == 0) { return data_callback_->OnReceivedPayloadData(NULL, 0, rtp_header) == 0 ? 0 : -1; + } + + // We are not allowed to hold a critical section when calling below functions. + scoped_ptr depacketizer( + RtpDepacketizer::Create(rtp_header->type.Video.codec)); + if (depacketizer.get() == NULL) { + LOG(LS_ERROR) << "Failed to create depacketizer."; + return -1; + } - return ParseVideoCodecSpecific(rtp_header, - payload, - payload_data_length, - specific_payload.Video.videoCodecType, - timestamp_ms, - is_first_packet); + rtp_header->type.Video.isFirstPacket = is_first_packet; + RtpDepacketizer::ParsedPayload parsed_payload; + if (!depacketizer->Parse(&parsed_payload, payload, payload_data_length)) + return -1; + + rtp_header->frameType = parsed_payload.frame_type; + rtp_header->type = parsed_payload.type; + return data_callback_->OnReceivedPayloadData(parsed_payload.payload, + parsed_payload.payload_length, + rtp_header) == 0 + ? 0 + : -1; } int RTPReceiverVideo::GetPayloadTypeFrequency() const { @@ -98,61 +115,28 @@ const char payload_name[RTP_PAYLOAD_NAME_SIZE], const PayloadUnion& specific_payload) const { // For video we just go with default values. - if (-1 == callback->OnInitializeDecoder( - id, payload_type, payload_name, kVideoPayloadTypeFrequency, 1, 0)) { - WEBRTC_TRACE(kTraceError, - kTraceRtpRtcp, - id, - "Failed to create video decoder for payload type:%d", - payload_type); + if (-1 == + callback->OnInitializeDecoder( + id, payload_type, payload_name, kVideoPayloadTypeFrequency, 1, 0)) { + LOG(LS_ERROR) << "Failed to created decoder for payload type: " + << payload_type; return -1; } return 0; } -// We are not allowed to hold a critical section when calling this function. -int32_t RTPReceiverVideo::ParseVideoCodecSpecific( - WebRtcRTPHeader* rtp_header, - const uint8_t* payload_data, - uint16_t payload_data_length, - RtpVideoCodecTypes video_type, - int64_t now_ms, - bool is_first_packet) { - WEBRTC_TRACE(kTraceStream, - kTraceRtpRtcp, - id_, - "%s(timestamp:%u)", - __FUNCTION__, - rtp_header->header.timestamp); - - rtp_header->type.Video.isFirstPacket = is_first_packet; - switch (rtp_header->type.Video.codec) { - case kRtpVideoGeneric: - return ReceiveGenericCodec(rtp_header, payload_data, payload_data_length); - case kRtpVideoVp8: - return ReceiveVp8Codec(rtp_header, payload_data, payload_data_length); - case kRtpVideoH264: - return ReceiveH264Codec(rtp_header, payload_data, payload_data_length); - case kRtpVideoNone: - break; - } - return -1; -} - -int32_t RTPReceiverVideo::BuildRTPheader( - const WebRtcRTPHeader* rtp_header, - uint8_t* data_buffer) const { +int32_t RTPReceiverVideo::BuildRTPheader(const WebRtcRTPHeader* rtp_header, + uint8_t* data_buffer) const { data_buffer[0] = static_cast(0x80); // version 2 data_buffer[1] = static_cast(rtp_header->header.payloadType); if (rtp_header->header.markerBit) { data_buffer[1] |= kRtpMarkerBitMask; // MarkerBit is 1 } - ModuleRTPUtility::AssignUWord16ToBuffer(data_buffer + 2, - rtp_header->header.sequenceNumber); - ModuleRTPUtility::AssignUWord32ToBuffer(data_buffer + 4, - rtp_header->header.timestamp); - ModuleRTPUtility::AssignUWord32ToBuffer(data_buffer + 8, - rtp_header->header.ssrc); + RtpUtility::AssignUWord16ToBuffer(data_buffer + 2, + rtp_header->header.sequenceNumber); + RtpUtility::AssignUWord32ToBuffer(data_buffer + 4, + rtp_header->header.timestamp); + RtpUtility::AssignUWord32ToBuffer(data_buffer + 8, rtp_header->header.ssrc); int32_t rtp_header_length = 12; @@ -164,8 +148,7 @@ } uint8_t* ptr = &data_buffer[rtp_header_length]; for (uint32_t i = 0; i < rtp_header->header.numCSRCs; ++i) { - ModuleRTPUtility::AssignUWord32ToBuffer(ptr, - rtp_header->header.arrOfCSRCs[i]); + RtpUtility::AssignUWord32ToBuffer(ptr, rtp_header->header.arrOfCSRCs[i]); ptr += 4; } data_buffer[0] = (data_buffer[0] & 0xf0) | rtp_header->header.numCSRCs; @@ -175,157 +158,4 @@ return rtp_header_length; } -int32_t RTPReceiverVideo::ReceiveVp8Codec(WebRtcRTPHeader* rtp_header, - const uint8_t* payload_data, - uint16_t payload_data_length) { - ModuleRTPUtility::RTPPayload parsed_packet; - uint32_t id; - { - CriticalSectionScoped cs(crit_sect_.get()); - id = id_; - } - ModuleRTPUtility::RTPPayloadParser rtp_payload_parser( - kRtpVideoVp8, payload_data, payload_data_length, id); - - if (!rtp_payload_parser.Parse(parsed_packet)) - return -1; - - if (parsed_packet.info.VP8.dataLength == 0) - return 0; - - rtp_header->frameType = (parsed_packet.frameType == ModuleRTPUtility::kIFrame) - ? kVideoFrameKey : kVideoFrameDelta; - - RTPVideoHeaderVP8* to_header = &rtp_header->type.Video.codecHeader.VP8; - ModuleRTPUtility::RTPPayloadVP8* from_header = &parsed_packet.info.VP8; - - rtp_header->type.Video.isFirstPacket = - from_header->beginningOfPartition && (from_header->partitionID == 0); - to_header->nonReference = from_header->nonReferenceFrame; - to_header->pictureId = - from_header->hasPictureID ? from_header->pictureID : kNoPictureId; - to_header->tl0PicIdx = - from_header->hasTl0PicIdx ? from_header->tl0PicIdx : kNoTl0PicIdx; - if (from_header->hasTID) { - to_header->temporalIdx = from_header->tID; - to_header->layerSync = from_header->layerSync; - } else { - to_header->temporalIdx = kNoTemporalIdx; - to_header->layerSync = false; - } - to_header->keyIdx = from_header->hasKeyIdx ? from_header->keyIdx : kNoKeyIdx; - - rtp_header->type.Video.width = from_header->frameWidth; - rtp_header->type.Video.height = from_header->frameHeight; - - to_header->partitionId = from_header->partitionID; - to_header->beginningOfPartition = from_header->beginningOfPartition; - - if (data_callback_->OnReceivedPayloadData(parsed_packet.info.VP8.data, - parsed_packet.info.VP8.dataLength, - rtp_header) != 0) { - return -1; - } - return 0; -} - -int32_t RTPReceiverVideo::ReceiveH264Codec(WebRtcRTPHeader* rtp_header, - const uint8_t* payload_data, - uint16_t payload_data_length) { - size_t offset = RtpFormatH264::kNalHeaderOffset; - uint8_t nal_type = payload_data[offset] & RtpFormatH264::kTypeMask; - rtp_header->type.Video.codecHeader.H264.nalu_header = nal_type; - // For H.264, isFirstPacket means first in NAL unit, not first in the - // timestamp, which elsewhere is referred to as a 'frame' or 'session' - rtp_header->type.Video.isFirstPacket = true; - // get original NAL type if FU-A or STAP-A - switch (nal_type) { - case RtpFormatH264::kFuA: - offset = RtpFormatH264::kFuAHeaderOffset; - if (offset >= payload_data_length) { - return -1; // malformed - } - nal_type = payload_data[offset] & RtpFormatH264::kTypeMask; - if (!(payload_data[offset] & RtpFormatH264::kFragStartBit)) { - rtp_header->type.Video.isFirstPacket = false; - } - break; - case RtpFormatH264::kStapA: - offset = RtpFormatH264::kStapAHeaderOffset + - RtpFormatH264::kAggUnitLengthSize; - if (offset >= payload_data_length) { - return -1; // malformed - } - nal_type = payload_data[offset] & RtpFormatH264::kTypeMask; - break; - default: - break; - } - // key frames start with SPS, PPS, IDR, or Recovery Point SEI - // Recovery Point SEI's are used in AIR and GDR refreshes, which don't - // send large iframes, and instead use forms of incremental/continuous refresh. - rtp_header->frameType = kVideoFrameDelta; - switch (nal_type) { - case RtpFormatH264::kSei: // check if it is a Recovery Point SEI (aka GDR) - if (offset+1 >= payload_data_length) { - return -1; // malformed - } - if (payload_data[offset+1] != RtpFormatH264::kSeiRecPt) { - break; // some other form of SEI - not a keyframe - } - // else fall through since GDR is like IDR - case RtpFormatH264::kSps: - case RtpFormatH264::kPps: - case RtpFormatH264::kIdr: - rtp_header->frameType = kVideoFrameKey; - break; - default: - break; - } -#if 0 - // XXX This check (which is complex to implement) would verify if a NAL - // codes the first MB in the frame ("access unit"). See 7.4.1.2.4 in the - // 2003 draft H.264 spec for an algorithm for this, which requires - // considerable parsing of the stream. Even simpler variants are - // complex. Since mis-identification of complete KeyFrames doesn't have - // to be 100% correct for "fast-forward" in error recovery, we can let it - // consider *any* NAL that's not a non-start FU-A packet to be a possible - // first-in-frame/session/"access unit" with minimal impact. In some - // cases of leading loss, it might try to fast-forward to a "session" - // that's missing an SPS/PPS or missing the first NAL of a Mode 0 frame. - // This should be ok, even though the decode *might* fail. - - // Check if this NAL codes for pixels *and* is the first NAL of a frame - // Would fail for FMO/ASO, which aren't allowed in baseline - if (nal_type == RtpFormatH264::kIdr || nal_type == RtpFormatH264::kIpb) { - // TODO; see 7.4.1.2.4 Detection of the first VCL NAL unit of a primary coded picture - } -#endif - // receive payloads as-is, depacketize later when moving to frame buffer - if (data_callback_->OnReceivedPayloadData( - payload_data, payload_data_length, rtp_header) != 0) { - return -1; - } - return 0; -} - -int32_t RTPReceiverVideo::ReceiveGenericCodec( - WebRtcRTPHeader* rtp_header, - const uint8_t* payload_data, - uint16_t payload_data_length) { - uint8_t generic_header = *payload_data++; - --payload_data_length; - - rtp_header->frameType = - ((generic_header & RtpFormatVideoGeneric::kKeyFrameBit) != 0) ? - kVideoFrameKey : kVideoFrameDelta; - rtp_header->type.Video.isFirstPacket = - (generic_header & RtpFormatVideoGeneric::kFirstPacketBit) != 0; - - if (data_callback_->OnReceivedPayloadData( - payload_data, payload_data_length, rtp_header) != 0) { - return -1; - } - return 0; -} } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.h 2015-02-03 14:33:36.000000000 +0000 @@ -22,27 +22,24 @@ class RTPReceiverVideo : public RTPReceiverStrategy { public: - RTPReceiverVideo(const int32_t id, RtpData* data_callback); + explicit RTPReceiverVideo(RtpData* data_callback); virtual ~RTPReceiverVideo(); - virtual int32_t ParseRtpPacket( - WebRtcRTPHeader* rtp_header, - const PayloadUnion& specific_payload, - bool is_red, - const uint8_t* packet, - uint16_t packet_length, - int64_t timestamp, - bool is_first_packet) OVERRIDE; - - TelephoneEventHandler* GetTelephoneEventHandler() { - return NULL; - } + virtual int32_t ParseRtpPacket(WebRtcRTPHeader* rtp_header, + const PayloadUnion& specific_payload, + bool is_red, + const uint8_t* packet, + uint16_t packet_length, + int64_t timestamp, + bool is_first_packet) OVERRIDE; + + TelephoneEventHandler* GetTelephoneEventHandler() { return NULL; } int GetPayloadTypeFrequency() const OVERRIDE; - virtual RTPAliveType ProcessDeadOrAlive(uint16_t last_payload_length) const - OVERRIDE; + virtual RTPAliveType ProcessDeadOrAlive( + uint16_t last_payload_length) const OVERRIDE; virtual bool ShouldReportCsrcChanges(uint8_t payload_type) const OVERRIDE; @@ -60,32 +57,9 @@ void SetPacketOverHead(uint16_t packet_over_head); - protected: - int32_t ReceiveGenericCodec(WebRtcRTPHeader* rtp_header, - const uint8_t* payload_data, - uint16_t payload_data_length); - - int32_t ReceiveVp8Codec(WebRtcRTPHeader* rtp_header, - const uint8_t* payload_data, - uint16_t payload_data_length); - - int32_t ReceiveH264Codec(WebRtcRTPHeader* rtp_header, - const uint8_t* payload_data, - uint16_t payload_data_length); - + private: int32_t BuildRTPheader(const WebRtcRTPHeader* rtp_header, uint8_t* data_buffer) const; - - private: - int32_t ParseVideoCodecSpecific( - WebRtcRTPHeader* rtp_header, - const uint8_t* payload_data, - uint16_t payload_data_length, - RtpVideoCodecTypes video_type, - int64_t now_ms, - bool is_first_packet); - - int32_t id_; }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp.gypi 2015-02-03 14:33:36.000000000 +0000 @@ -20,6 +20,7 @@ # Common '../interface/fec_receiver.h', '../interface/receive_statistics.h', + '../interface/remote_ntp_time_estimator.h', '../interface/rtp_header_parser.h', '../interface/rtp_payload_registry.h', '../interface/rtp_receiver.h', @@ -32,10 +33,13 @@ 'fec_receiver_impl.h', 'receive_statistics_impl.cc', 'receive_statistics_impl.h', + 'remote_ntp_time_estimator.cc', 'rtp_header_parser.cc', 'rtp_rtcp_config.h', 'rtp_rtcp_impl.cc', 'rtp_rtcp_impl.h', + 'rtcp_packet.cc', + 'rtcp_packet.h', 'rtcp_receiver.cc', 'rtcp_receiver.h', 'rtcp_receiver_help.cc', @@ -82,10 +86,13 @@ 'rtp_sender_video.cc', 'rtp_sender_video.h', 'video_codec_information.h', + 'rtp_format.cc', + 'rtp_format.h', 'rtp_format_h264.cc', 'rtp_format_h264.h', 'rtp_format_vp8.cc', 'rtp_format_vp8.h', + 'rtp_format_video_generic.cc', 'rtp_format_video_generic.h', 'vp8_partition_aggregator.cc', 'vp8_partition_aggregator.h', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc 2015-02-03 14:33:36.000000000 +0000 @@ -17,11 +17,6 @@ #include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/trace.h" -#ifdef MATLAB -#include "webrtc/modules/rtp_rtcp/test/BWEStandAlone/MatlabPlot.h" -extern MatlabEngine eng; // Global variable defined elsewhere. -#endif - #ifdef _WIN32 // Disable warning C4355: 'this' : used in base member initializer list. #pragma warning(disable : 4355) @@ -42,20 +37,22 @@ rtt_stats(NULL), audio_messages(NullObjectRtpAudioFeedback()), remote_bitrate_estimator(NULL), - paced_sender(NULL) { + paced_sender(NULL), + send_bitrate_observer(NULL), + send_frame_count_observer(NULL), + send_side_delay_observer(NULL) { } RtpRtcp* RtpRtcp::CreateRtpRtcp(const RtpRtcp::Configuration& configuration) { if (configuration.clock) { return new ModuleRtpRtcpImpl(configuration); } else { + // No clock implementation provided, use default clock. RtpRtcp::Configuration configuration_copy; memcpy(&configuration_copy, &configuration, sizeof(RtpRtcp::Configuration)); configuration_copy.clock = Clock::GetRealTimeClock(); - ModuleRtpRtcpImpl* rtp_rtcp_instance = - new ModuleRtpRtcpImpl(configuration_copy); - return rtp_rtcp_instance; + return new ModuleRtpRtcpImpl(configuration_copy); } } @@ -65,8 +62,13 @@ configuration.clock, configuration.outgoing_transport, configuration.audio_messages, - configuration.paced_sender), - rtcp_sender_(configuration.id, configuration.audio, configuration.clock, + configuration.paced_sender, + configuration.send_bitrate_observer, + configuration.send_frame_count_observer, + configuration.send_side_delay_observer), + rtcp_sender_(configuration.id, + configuration.audio, + configuration.clock, configuration.receive_statistics), rtcp_receiver_(configuration.id, configuration.clock, this), clock_(configuration.clock), @@ -83,15 +85,13 @@ CriticalSectionWrapper::CreateCriticalSection()), default_module_( static_cast(configuration.default_module)), + padding_index_(static_cast(-1)), // Start padding at first child. nack_method_(kNackOff), nack_last_time_sent_full_(0), nack_last_seq_number_sent_(0), simulcast_(false), key_frame_req_method_(kKeyFrameReqFirRtp), remote_bitrate_(configuration.remote_bitrate_estimator), -#ifdef MATLAB - , plot1_(NULL), -#endif rtt_stats_(configuration.rtt_stats), critical_section_rtt_(CriticalSectionWrapper::CreateCriticalSection()), rtt_ms_(0) { @@ -110,13 +110,9 @@ uint32_t SSRC = rtp_sender_.SSRC(); rtcp_sender_.SetSSRC(SSRC); SetRtcpReceiverSsrcs(SSRC); - - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id_, "%s created", __FUNCTION__); } ModuleRtpRtcpImpl::~ModuleRtpRtcpImpl() { - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id_, "%s deleted", __FUNCTION__); - // All child modules MUST be deleted before deleting the default. assert(child_modules_.empty()); @@ -125,21 +121,9 @@ if (default_module_) { default_module_->DeRegisterChildModule(this); } -#ifdef MATLAB - if (plot1_) { - eng.DeletePlot(plot1_); - plot1_ = NULL; - } -#endif } void ModuleRtpRtcpImpl::RegisterChildModule(RtpRtcp* module) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "RegisterChildModule(module:0x%x)", - module); - CriticalSectionScoped lock( critical_section_module_ptrs_.get()); CriticalSectionScoped double_lock( @@ -153,17 +137,12 @@ } void ModuleRtpRtcpImpl::DeRegisterChildModule(RtpRtcp* remove_module) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "DeRegisterChildModule(module:0x%x)", remove_module); - CriticalSectionScoped lock( critical_section_module_ptrs_.get()); CriticalSectionScoped double_lock( critical_section_module_ptrs_feedback_.get()); - std::list::iterator it = child_modules_.begin(); + std::vector::iterator it = child_modules_.begin(); while (it != child_modules_.end()) { RtpRtcp* module = *it; if (module == remove_module) { @@ -251,8 +230,7 @@ } if (rtcp_sender_.TimeToSendRTCPReport()) { - RTCPSender::FeedbackState feedback_state(this); - rtcp_sender_.SendRTCP(feedback_state, kRtcpReport); + rtcp_sender_.SendRTCP(GetFeedbackState(), kRtcpReport); } } @@ -263,16 +241,18 @@ return 0; } -int32_t ModuleRtpRtcpImpl::SetRTXSendStatus(int mode, bool set_ssrc, - uint32_t ssrc) { - rtp_sender_.SetRTXStatus(mode, set_ssrc, ssrc); - return 0; +void ModuleRtpRtcpImpl::SetRTXSendStatus(int mode) { + rtp_sender_.SetRTXStatus(mode); } -int32_t ModuleRtpRtcpImpl::RTXSendStatus(int* mode, uint32_t* ssrc, - int* payload_type) const { +void ModuleRtpRtcpImpl::RTXSendStatus(int* mode, + uint32_t* ssrc, + int* payload_type) const { rtp_sender_.RTXStatus(mode, ssrc, payload_type); - return 0; +} + +void ModuleRtpRtcpImpl::SetRtxSsrc(uint32_t ssrc) { + rtp_sender_.SetRtxSsrc(ssrc); } void ModuleRtpRtcpImpl::SetRtxSendPayloadType(int payload_type) { @@ -282,29 +262,12 @@ int32_t ModuleRtpRtcpImpl::IncomingRtcpPacket( const uint8_t* rtcp_packet, const uint16_t length) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "IncomingRtcpPacket(packet_length:%u)", length); - // Minimum RTP is 12 bytes. - // Minimum RTCP is 8 bytes (RTCP BYE). - if (length == 8) { - WEBRTC_TRACE(kTraceDebug, kTraceRtpRtcp, -1, - "IncomingRtcpPacket invalid length"); - return false; - } - // Check RTP version. - const uint8_t version = rtcp_packet[0] >> 6; - if (version != 2) { - WEBRTC_TRACE(kTraceDebug, kTraceRtpRtcp, -1, - "IncomingRtcpPacket invalid RTP version"); - return false; - } // Allow receive of non-compound RTCP packets. RTCPUtility::RTCPParserV2 rtcp_parser(rtcp_packet, length, true); const bool valid_rtcpheader = rtcp_parser.IsValid(); if (!valid_rtcpheader) { - WEBRTC_TRACE(kTraceDebug, kTraceRtpRtcp, id_, - "IncomingRtcpPacket invalid RTCP packet"); + LOG(LS_WARNING) << "Incoming invalid RTCP packet"; return -1; } RTCPHelp::RTCPPacketInformation rtcp_packet_information; @@ -318,14 +281,6 @@ int32_t ModuleRtpRtcpImpl::RegisterSendPayload( const CodecInst& voice_codec) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "RegisterSendPayload(pl_name:%s pl_type:%d frequency:%u)", - voice_codec.plname, - voice_codec.pltype, - voice_codec.plfreq); - return rtp_sender_.RegisterPayload( voice_codec.plname, voice_codec.pltype, @@ -336,13 +291,6 @@ int32_t ModuleRtpRtcpImpl::RegisterSendPayload( const VideoCodec& video_codec) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "RegisterSendPayload(pl_name:%s pl_type:%d)", - video_codec.plName, - video_codec.plType); - send_video_codec_ = video_codec; { // simulcast_ is accessed when accessing child_modules_, so this write needs @@ -359,11 +307,6 @@ int32_t ModuleRtpRtcpImpl::DeRegisterSendPayload( const int8_t payload_type) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "DeRegisterSendPayload(%d)", payload_type); - return rtp_sender_.DeRegisterSendPayload(payload_type); } @@ -372,58 +315,73 @@ } uint32_t ModuleRtpRtcpImpl::StartTimestamp() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "StartTimestamp()"); - return rtp_sender_.StartTimestamp(); } // Configure start timestamp, default is a random number. int32_t ModuleRtpRtcpImpl::SetStartTimestamp( const uint32_t timestamp) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SetStartTimestamp(%d)", - timestamp); rtcp_sender_.SetStartTimestamp(timestamp); rtp_sender_.SetStartTimestamp(timestamp, true); return 0; // TODO(pwestin): change to void. } uint16_t ModuleRtpRtcpImpl::SequenceNumber() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "SequenceNumber()"); - return rtp_sender_.SequenceNumber(); } // Set SequenceNumber, default is a random number. int32_t ModuleRtpRtcpImpl::SetSequenceNumber( const uint16_t seq_num) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SetSequenceNumber(%d)", - seq_num); - rtp_sender_.SetSequenceNumber(seq_num); return 0; // TODO(pwestin): change to void. } -uint32_t ModuleRtpRtcpImpl::SSRC() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "SSRC()"); +void ModuleRtpRtcpImpl::SetRtpStateForSsrc(uint32_t ssrc, + const RtpState& rtp_state) { + if (rtp_sender_.SSRC() == ssrc) { + rtp_sender_.SetRtpState(rtp_state); + return; + } + if (rtp_sender_.RtxSsrc() == ssrc) { + rtp_sender_.SetRtxRtpState(rtp_state); + return; + } + + CriticalSectionScoped lock(critical_section_module_ptrs_.get()); + for (size_t i = 0; i < child_modules_.size(); ++i) { + child_modules_[i]->SetRtpStateForSsrc(ssrc, rtp_state); + } +} + +bool ModuleRtpRtcpImpl::GetRtpStateForSsrc(uint32_t ssrc, RtpState* rtp_state) { + if (rtp_sender_.SSRC() == ssrc) { + *rtp_state = rtp_sender_.GetRtpState(); + return true; + } + if (rtp_sender_.RtxSsrc() == ssrc) { + *rtp_state = rtp_sender_.GetRtxRtpState(); + return true; + } + + CriticalSectionScoped lock(critical_section_module_ptrs_.get()); + for (size_t i = 0; i < child_modules_.size(); ++i) { + if (child_modules_[i]->GetRtpStateForSsrc(ssrc, rtp_state)) + return true; + } + return false; +} + +uint32_t ModuleRtpRtcpImpl::SSRC() const { return rtp_sender_.SSRC(); } // Configure SSRC, default is a random number. -int32_t ModuleRtpRtcpImpl::SetSSRC(const uint32_t ssrc) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "SetSSRC(%d)", ssrc); - +void ModuleRtpRtcpImpl::SetSSRC(const uint32_t ssrc) { rtp_sender_.SetSSRC(ssrc); rtcp_sender_.SetSSRC(ssrc); SetRtcpReceiverSsrcs(ssrc); - - return 0; // TODO(pwestin): change to void. } int32_t ModuleRtpRtcpImpl::SetCSRCStatus(const bool include) { @@ -434,25 +392,17 @@ int32_t ModuleRtpRtcpImpl::CSRCs( uint32_t arr_of_csrc[kRtpCsrcSize]) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "CSRCs()"); - return rtp_sender_.CSRCs(arr_of_csrc); } int32_t ModuleRtpRtcpImpl::SetCSRCs( const uint32_t arr_of_csrc[kRtpCsrcSize], const uint8_t arr_length) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SetCSRCs(arr_length:%d)", - arr_length); - if (IsDefaultModule()) { // For default we need to update all child modules too. CriticalSectionScoped lock(critical_section_module_ptrs_.get()); - std::list::iterator it = child_modules_.begin(); + std::vector::iterator it = child_modules_.begin(); while (it != child_modules_.end()) { RtpRtcp* module = *it; if (module) { @@ -461,46 +411,46 @@ it++; } } else { - for (int i = 0; i < arr_length; ++i) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "\tidx:%d CSRC:%u", i, - arr_of_csrc[i]); - } rtcp_sender_.SetCSRCs(arr_of_csrc, arr_length); rtp_sender_.SetCSRCs(arr_of_csrc, arr_length); } return 0; // TODO(pwestin): change to void. } -uint32_t ModuleRtpRtcpImpl::PacketCountSent() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "PacketCountSent()"); - return rtp_sender_.Packets(); -} - -uint32_t ModuleRtpRtcpImpl::ByteCountSent() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "ByteCountSent()"); - return rtp_sender_.Bytes(); +// TODO(pbos): Handle media and RTX streams separately (separate RTCP +// feedbacks). +RTCPSender::FeedbackState ModuleRtpRtcpImpl::GetFeedbackState() { + StreamDataCounters rtp_stats; + StreamDataCounters rtx_stats; + rtp_sender_.GetDataCounters(&rtp_stats, &rtx_stats); + + RTCPSender::FeedbackState state; + state.send_payload_type = SendPayloadType(); + state.frequency_hz = CurrentSendFrequencyHz(); + state.packets_sent = rtp_stats.packets + rtx_stats.packets; + state.media_bytes_sent = rtp_stats.bytes + rtx_stats.bytes; + state.module = this; + + LastReceivedNTP(&state.last_rr_ntp_secs, + &state.last_rr_ntp_frac, + &state.remote_sr); + + state.has_last_xr_rr = LastReceivedXrReferenceTimeInfo(&state.last_xr_rr); + + uint32_t tmp; + BitrateSent(&state.send_bitrate, &tmp, &tmp, &tmp); + return state; } int ModuleRtpRtcpImpl::CurrentSendFrequencyHz() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "CurrentSendFrequencyHz()"); return rtp_sender_.SendPayloadFrequency(); } int32_t ModuleRtpRtcpImpl::SetSendingStatus(const bool sending) { - if (sending) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "SetSendingStatus(sending)"); - } else { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "SetSendingStatus(stopped)"); - } if (rtcp_sender_.Sending() != sending) { // Sends RTCP BYE when going from true to false - RTCPSender::FeedbackState feedback_state(this); - if (rtcp_sender_.SetSendingStatus(feedback_state, sending) != 0) { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, id_, - "Failed to send RTCP BYE"); + if (rtcp_sender_.SetSendingStatus(GetFeedbackState(), sending) != 0) { + LOG(LS_WARNING) << "Failed to send RTCP BYE"; } collision_detected_ = false; @@ -525,31 +475,21 @@ } bool ModuleRtpRtcpImpl::Sending() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "Sending()"); return rtcp_sender_.Sending(); } int32_t ModuleRtpRtcpImpl::SetSendingMediaStatus(const bool sending) { - if (sending) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "SetSendingMediaStatus(sending)"); - } else { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "SetSendingMediaStatus(stopped)"); - } rtp_sender_.SetSendingMediaStatus(sending); return 0; } bool ModuleRtpRtcpImpl::SendingMedia() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "Sending()"); - if (!IsDefaultModule()) { return rtp_sender_.SendingMedia(); } CriticalSectionScoped lock(critical_section_module_ptrs_.get()); - std::list::const_iterator it = child_modules_.begin(); + std::vector::const_iterator it = child_modules_.begin(); while (it != child_modules_.end()) { RTPSender& rtp_sender = (*it)->rtp_sender_; if (rtp_sender.SendingMedia()) { @@ -569,20 +509,12 @@ uint32_t payload_size, const RTPFragmentationHeader* fragmentation, const RTPVideoHeader* rtp_video_hdr) { - WEBRTC_TRACE( - kTraceStream, - kTraceRtpRtcp, - id_, - "SendOutgoingData(frame_type:%d payload_type:%d time_stamp:%u size:%u)", - frame_type, payload_type, time_stamp, payload_size); - rtcp_sender_.SetLastRtpTime(time_stamp, capture_time_ms); if (!IsDefaultModule()) { // Don't send RTCP from default module. if (rtcp_sender_.TimeToSendRTCPReport(kVideoFrameKey == frame_type)) { - RTCPSender::FeedbackState feedback_state(this); - rtcp_sender_.SendRTCP(feedback_state, kRtcpReport); + rtcp_sender_.SendRTCP(GetFeedbackState(), kRtcpReport); } return rtp_sender_.SendOutgoingData(frame_type, payload_type, @@ -601,7 +533,7 @@ return -1; } int idx = 0; - std::list::iterator it = child_modules_.begin(); + std::vector::iterator it = child_modules_.begin(); for (; idx < rtp_video_hdr->simulcastIdx; ++it) { if (it == child_modules_.end()) { return -1; @@ -619,11 +551,6 @@ if (it == child_modules_.end()) { return -1; } - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SendOutgoingData(SimulcastIdx:%u size:%u, ssrc:0x%x)", - idx, payload_size, (*it)->rtp_sender_.SSRC()); return (*it)->SendOutgoingData(frame_type, payload_type, time_stamp, @@ -633,7 +560,7 @@ fragmentation, rtp_video_hdr); } else { - std::list::iterator it = child_modules_.begin(); + std::vector::iterator it = child_modules_.begin(); // Send to all "child" modules while (it != child_modules_.end()) { if ((*it)->SendingMedia()) { @@ -656,13 +583,6 @@ uint16_t sequence_number, int64_t capture_time_ms, bool retransmission) { - WEBRTC_TRACE( - kTraceStream, - kTraceRtpRtcp, - id_, - "TimeToSendPacket(ssrc:0x%x sequence_number:%u capture_time_ms:%ll)", - ssrc, sequence_number, capture_time_ms); - if (!IsDefaultModule()) { // Don't send from default module. if (SendingMedia() && ssrc == rtp_sender_.SSRC()) { @@ -671,7 +591,7 @@ } } else { CriticalSectionScoped lock(critical_section_module_ptrs_.get()); - std::list::iterator it = child_modules_.begin(); + std::vector::iterator it = child_modules_.begin(); while (it != child_modules_.end()) { if ((*it)->SendingMedia() && ssrc == (*it)->rtp_sender_.SSRC()) { return (*it)->rtp_sender_.TimeToSendPacket(sequence_number, @@ -686,23 +606,16 @@ } int ModuleRtpRtcpImpl::TimeToSendPadding(int bytes) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_, "TimeToSendPadding(bytes: %d)", - bytes); - if (!IsDefaultModule()) { // Don't send from default module. - if (SendingMedia()) { - return rtp_sender_.TimeToSendPadding(bytes); - } + return rtp_sender_.TimeToSendPadding(bytes); } else { CriticalSectionScoped lock(critical_section_module_ptrs_.get()); - std::list::iterator it = child_modules_.begin(); - while (it != child_modules_.end()) { + for (size_t i = 0; i < child_modules_.size(); ++i) { // Send padding on one of the modules sending media. - if ((*it)->SendingMedia()) { - return (*it)->rtp_sender_.TimeToSendPadding(bytes); + if (child_modules_[i]->SendingMedia()) { + return child_modules_[i]->rtp_sender_.TimeToSendPadding(bytes); } - ++it; } } return 0; @@ -721,24 +634,17 @@ } uint16_t ModuleRtpRtcpImpl::MaxPayloadLength() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "MaxPayloadLength()"); return rtp_sender_.MaxPayloadLength(); } uint16_t ModuleRtpRtcpImpl::MaxDataPayloadLength() const { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "MaxDataPayloadLength()"); - // Assuming IP/UDP. uint16_t min_data_payload_length = IP_PACKET_SIZE - 28; if (IsDefaultModule()) { // For default we need to update all child modules too. CriticalSectionScoped lock(critical_section_module_ptrs_.get()); - std::list::const_iterator it = - child_modules_.begin(); + std::vector::const_iterator it = child_modules_.begin(); while (it != child_modules_.end()) { RtpRtcp* module = *it; if (module) { @@ -763,13 +669,6 @@ const bool tcp, const bool ipv6, const uint8_t authentication_overhead) { - WEBRTC_TRACE( - kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SetTransportOverhead(TCP:%d, IPV6:%d authentication_overhead:%u)", - tcp, ipv6, authentication_overhead); - uint16_t packet_overhead = 0; if (ipv6) { packet_overhead = 40; @@ -801,11 +700,8 @@ } int32_t ModuleRtpRtcpImpl::SetMaxTransferUnit(const uint16_t mtu) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "SetMaxTransferUnit(%u)", - mtu); if (mtu > IP_PACKET_SIZE) { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, id_, - "Invalid in argument to SetMaxTransferUnit(%u)", mtu); + LOG(LS_ERROR) << "Invalid mtu: " << mtu; return -1; } return rtp_sender_.SetMaxPayloadLength(mtu - packet_overhead_, @@ -813,7 +709,6 @@ } RTCPMethod ModuleRtpRtcpImpl::RTCP() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "RTCP()"); if (rtcp_sender_.Status() != kRtcpOff) { return rtcp_receiver_.Status(); } @@ -822,8 +717,6 @@ // Configure RTCP status i.e on/off. int32_t ModuleRtpRtcpImpl::SetRTCPStatus(const RTCPMethod method) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "SetRTCPStatus(%d)", - method); if (rtcp_sender_.SetRTCPStatus(method) == 0) { return rtcp_receiver_.SetRTCPStatus(method); } @@ -837,34 +730,22 @@ } int32_t ModuleRtpRtcpImpl::SetCNAME(const char c_name[RTCP_CNAME_SIZE]) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "SetCNAME(%s)", c_name); return rtcp_sender_.SetCNAME(c_name); } -int32_t ModuleRtpRtcpImpl::CNAME(char c_name[RTCP_CNAME_SIZE]) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "CNAME()"); - return rtcp_sender_.CNAME(c_name); -} - int32_t ModuleRtpRtcpImpl::AddMixedCNAME( const uint32_t ssrc, const char c_name[RTCP_CNAME_SIZE]) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "AddMixedCNAME(SSRC:%u)", ssrc); return rtcp_sender_.AddMixedCNAME(ssrc, c_name); } int32_t ModuleRtpRtcpImpl::RemoveMixedCNAME(const uint32_t ssrc) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "RemoveMixedCNAME(SSRC:%u)", ssrc); return rtcp_sender_.RemoveMixedCNAME(ssrc); } int32_t ModuleRtpRtcpImpl::RemoteCNAME( const uint32_t remote_ssrc, char c_name[RTCP_CNAME_SIZE]) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "RemoteCNAME(SSRC:%u)", remote_ssrc); return rtcp_receiver_.CNAME(remote_ssrc, c_name); } @@ -874,12 +755,13 @@ uint32_t* rtcp_arrival_time_secs, uint32_t* rtcp_arrival_time_frac, uint32_t* rtcp_timestamp) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "RemoteNTP()"); return rtcp_receiver_.NTP(received_ntpsecs, received_ntpfrac, rtcp_arrival_time_secs, rtcp_arrival_time_frac, - rtcp_timestamp); + rtcp_timestamp) + ? 0 + : -1; } // Get RoundTripTime. @@ -888,14 +770,16 @@ uint16_t* avg_rtt, uint16_t* min_rtt, uint16_t* max_rtt) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "RTT()"); - return rtcp_receiver_.RTT(remote_ssrc, rtt, avg_rtt, min_rtt, max_rtt); + int32_t ret = rtcp_receiver_.RTT(remote_ssrc, rtt, avg_rtt, min_rtt, max_rtt); + if (rtt && *rtt == 0) { + // Try to get RTT from RtcpRttStats class. + *rtt = static_cast(rtt_ms()); + } + return ret; } // Reset RoundTripTime statistics. int32_t ModuleRtpRtcpImpl::ResetRTT(const uint32_t remote_ssrc) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "ResetRTT(SSRC:%u)", - remote_ssrc); return rtcp_receiver_.ResetRTT(remote_ssrc); } @@ -914,8 +798,6 @@ // Reset RTP data counters for the sending side. int32_t ModuleRtpRtcpImpl::ResetSendDataCountersRTP() { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "ResetSendDataCountersRTP()"); rtp_sender_.ResetDataCounters(); return 0; // TODO(pwestin): change to void. } @@ -923,10 +805,7 @@ // Force a send of an RTCP packet. // Normal SR and RR are triggered via the process function. int32_t ModuleRtpRtcpImpl::SendRTCP(uint32_t rtcp_packet_type) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "SendRTCP(0x%x)", - rtcp_packet_type); - RTCPSender::FeedbackState feedback_state(this); - return rtcp_sender_.SendRTCP(feedback_state, rtcp_packet_type); + return rtcp_sender_.SendRTCP(GetFeedbackState(), rtcp_packet_type); } int32_t ModuleRtpRtcpImpl::SetRTCPApplicationSpecificData( @@ -934,23 +813,16 @@ const uint32_t name, const uint8_t* data, const uint16_t length) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "SetRTCPApplicationSpecificData(sub_type:%d name:0x%x)", - sub_type, name); return rtcp_sender_.SetApplicationSpecificData(sub_type, name, data, length); } // (XR) VOIP metric. int32_t ModuleRtpRtcpImpl::SetRTCPVoIPMetrics( const RTCPVoIPMetric* voip_metric) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "SetRTCPVoIPMetrics()"); - return rtcp_sender_.SetRTCPVoIPMetrics(voip_metric); } void ModuleRtpRtcpImpl::SetRtcpXrRrtrStatus(bool enable) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "SetRtcpXrRrtrStatus(%s)", enable ? "true" : "false"); return rtcp_sender_.SendRtcpXrReceiverReferenceTime(enable); } @@ -961,81 +833,70 @@ int32_t ModuleRtpRtcpImpl::DataCountersRTP( uint32_t* bytes_sent, uint32_t* packets_sent) const { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_, "DataCountersRTP()"); + StreamDataCounters rtp_stats; + StreamDataCounters rtx_stats; + rtp_sender_.GetDataCounters(&rtp_stats, &rtx_stats); + if (bytes_sent) { - *bytes_sent = rtp_sender_.Bytes(); + *bytes_sent = rtp_stats.bytes + rtp_stats.padding_bytes + + rtp_stats.header_bytes + rtx_stats.bytes + + rtx_stats.padding_bytes + rtx_stats.header_bytes; } if (packets_sent) { - *packets_sent = rtp_sender_.Packets(); + *packets_sent = rtp_stats.packets + rtx_stats.packets; } return 0; } int32_t ModuleRtpRtcpImpl::RemoteRTCPStat(RTCPSenderInfo* sender_info) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "RemoteRTCPStat()"); return rtcp_receiver_.SenderInfoReceived(sender_info); } // Received RTCP report. int32_t ModuleRtpRtcpImpl::RemoteRTCPStat( std::vector* receive_blocks) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "RemoteRTCPStat()"); return rtcp_receiver_.StatisticsReceived(receive_blocks); } int32_t ModuleRtpRtcpImpl::AddRTCPReportBlock( const uint32_t ssrc, const RTCPReportBlock* report_block) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "AddRTCPReportBlock()"); return rtcp_sender_.AddExternalReportBlock(ssrc, report_block); } int32_t ModuleRtpRtcpImpl::RemoveRTCPReportBlock( const uint32_t ssrc) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "RemoveRTCPReportBlock()"); return rtcp_sender_.RemoveExternalReportBlock(ssrc); } +void ModuleRtpRtcpImpl::GetRtcpPacketTypeCounters( + RtcpPacketTypeCounter* packets_sent, + RtcpPacketTypeCounter* packets_received) const { + rtcp_sender_.GetPacketTypeCounter(packets_sent); + rtcp_receiver_.GetPacketTypeCounter(packets_received); +} + // (REMB) Receiver Estimated Max Bitrate. bool ModuleRtpRtcpImpl::REMB() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "REMB()"); return rtcp_sender_.REMB(); } int32_t ModuleRtpRtcpImpl::SetREMBStatus(const bool enable) { - if (enable) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SetREMBStatus(enable)"); - } else { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SetREMBStatus(disable)"); - } return rtcp_sender_.SetREMBStatus(enable); } int32_t ModuleRtpRtcpImpl::SetREMBData(const uint32_t bitrate, const uint8_t number_of_ssrc, const uint32_t* ssrc) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "SetREMBData(bitrate:%d,?,?)", bitrate); return rtcp_sender_.SetREMBData(bitrate, number_of_ssrc, ssrc); } // (IJ) Extended jitter report. bool ModuleRtpRtcpImpl::IJ() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "IJ()"); return rtcp_sender_.IJ(); } int32_t ModuleRtpRtcpImpl::SetIJStatus(const bool enable) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SetIJStatus(%s)", enable ? "true" : "false"); return rtcp_sender_.SetIJStatus(enable); } @@ -1052,23 +913,14 @@ // (TMMBR) Temporary Max Media Bit Rate. bool ModuleRtpRtcpImpl::TMMBR() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "TMMBR()"); return rtcp_sender_.TMMBR(); } int32_t ModuleRtpRtcpImpl::SetTMMBRStatus(const bool enable) { - if (enable) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "SetTMMBRStatus(enable)"); - } else { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "SetTMMBRStatus(disable)"); - } return rtcp_sender_.SetTMMBRStatus(enable); } int32_t ModuleRtpRtcpImpl::SetTMMBN(const TMMBRSet* bounding_set) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "SetTMMBN()"); uint32_t max_bitrate_kbit = rtp_sender_.MaxConfiguredBitrateVideo() / 1000; return rtcp_sender_.SetTMMBN(bounding_set, max_bitrate_kbit); @@ -1076,32 +928,18 @@ // Returns the currently configured retransmission mode. int ModuleRtpRtcpImpl::SelectiveRetransmissions() const { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SelectiveRetransmissions()"); return rtp_sender_.SelectiveRetransmissions(); } // Enable or disable a retransmission mode, which decides which packets will // be retransmitted if NACKed. int ModuleRtpRtcpImpl::SetSelectiveRetransmissions(uint8_t settings) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SetSelectiveRetransmissions(%u)", - settings); return rtp_sender_.SetSelectiveRetransmissions(settings); } // Send a Negative acknowledgment packet. int32_t ModuleRtpRtcpImpl::SendNACK(const uint16_t* nack_list, const uint16_t size) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SendNACK(size:%u)", size); - // Use RTT from RtcpRttStats class if provided. uint16_t rtt = rtt_ms(); if (rtt == 0) { @@ -1145,9 +983,8 @@ } nack_last_seq_number_sent_ = nack_list[start_id + nackLength - 1]; - RTCPSender::FeedbackState feedback_state(this); return rtcp_sender_.SendRTCP( - feedback_state, kRtcpNack, nackLength, &nack_list[start_id]); + GetFeedbackState(), kRtcpNack, nackLength, &nack_list[start_id]); } // Store the sent packets, needed to answer to a Negative acknowledgment @@ -1155,14 +992,6 @@ int32_t ModuleRtpRtcpImpl::SetStorePacketsStatus( const bool enable, const uint16_t number_to_store) { - if (enable) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "SetStorePacketsStatus(enable, number_to_store:%d)", - number_to_store); - } else { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "SetStorePacketsStatus(disable)"); - } rtp_sender_.SetStorePacketsStatus(enable, number_to_store); return 0; // TODO(pwestin): change to void. } @@ -1186,19 +1015,11 @@ const uint8_t key, const uint16_t time_ms, const uint8_t level) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "SendTelephoneEventOutband(key:%u, time_ms:%u, level:%u)", key, - time_ms, level); return rtp_sender_.SendTelephoneEvent(key, time_ms, level); } bool ModuleRtpRtcpImpl::SendTelephoneEventActive( int8_t& telephone_event) const { - - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SendTelephoneEventActive()"); return rtp_sender_.SendTelephoneEventActive(&telephone_event); } @@ -1206,118 +1027,63 @@ // packet in silence (CNG). int32_t ModuleRtpRtcpImpl::SetAudioPacketSize( const uint16_t packet_size_samples) { - - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SetAudioPacketSize(%u)", - packet_size_samples); return rtp_sender_.SetAudioPacketSize(packet_size_samples); } -int32_t ModuleRtpRtcpImpl::SetRTPAudioLevelIndicationStatus( - const bool enable, - const uint8_t id) { - - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SetRTPAudioLevelIndicationStatus(enable=%d, ID=%u)", - enable, - id); - return rtp_sender_.SetAudioLevelIndicationStatus(enable, id); -} - -int32_t ModuleRtpRtcpImpl::GetRTPAudioLevelIndicationStatus( - bool& enable, - uint8_t& id) const { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "GetRTPAudioLevelIndicationStatus()"); - return rtp_sender_.AudioLevelIndicationStatus(&enable, &id); -} - int32_t ModuleRtpRtcpImpl::SetAudioLevel( const uint8_t level_d_bov) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SetAudioLevel(level_d_bov:%u)", - level_d_bov); return rtp_sender_.SetAudioLevel(level_d_bov); } // Set payload type for Redundant Audio Data RFC 2198. int32_t ModuleRtpRtcpImpl::SetSendREDPayloadType( const int8_t payload_type) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SetSendREDPayloadType(%d)", - payload_type); return rtp_sender_.SetRED(payload_type); } // Get payload type for Redundant Audio Data RFC 2198. int32_t ModuleRtpRtcpImpl::SendREDPayloadType( int8_t& payload_type) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "SendREDPayloadType()"); return rtp_sender_.RED(&payload_type); } -RtpVideoCodecTypes ModuleRtpRtcpImpl::SendVideoCodec() const { - return rtp_sender_.VideoCodecType(); -} - void ModuleRtpRtcpImpl::SetTargetSendBitrate( const std::vector& stream_bitrates) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, - "SetTargetSendBitrate: %ld streams", stream_bitrates.size()); if (IsDefaultModule()) { CriticalSectionScoped lock(critical_section_module_ptrs_.get()); if (simulcast_) { - std::list::iterator it = child_modules_.begin(); + std::vector::iterator it = child_modules_.begin(); for (size_t i = 0; it != child_modules_.end() && i < stream_bitrates.size(); ++it) { if ((*it)->SendingMedia()) { RTPSender& rtp_sender = (*it)->rtp_sender_; - rtp_sender.SetTargetSendBitrate(stream_bitrates[i]); + rtp_sender.SetTargetBitrate(stream_bitrates[i]); ++i; } } } else { if (stream_bitrates.size() > 1) return; - std::list::iterator it = child_modules_.begin(); + std::vector::iterator it = child_modules_.begin(); for (; it != child_modules_.end(); ++it) { RTPSender& rtp_sender = (*it)->rtp_sender_; - rtp_sender.SetTargetSendBitrate(stream_bitrates[0]); + rtp_sender.SetTargetBitrate(stream_bitrates[0]); } } } else { if (stream_bitrates.size() > 1) return; - rtp_sender_.SetTargetSendBitrate(stream_bitrates[0]); + rtp_sender_.SetTargetBitrate(stream_bitrates[0]); } } int32_t ModuleRtpRtcpImpl::SetKeyFrameRequestMethod( const KeyFrameRequestMethod method) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SetKeyFrameRequestMethod(method:%u)", - method); key_frame_req_method_ = method; return 0; } int32_t ModuleRtpRtcpImpl::RequestKeyFrame() { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "RequestKeyFrame"); switch (key_frame_req_method_) { case kKeyFrameReqFirRtp: return rtp_sender_.SendRTPIntraRequest(); @@ -1331,25 +1097,14 @@ int32_t ModuleRtpRtcpImpl::SendRTCPSliceLossIndication( const uint8_t picture_id) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SendRTCPSliceLossIndication (picture_id:%d)", - picture_id); - RTCPSender::FeedbackState feedback_state(this); return rtcp_sender_.SendRTCP( - feedback_state, kRtcpSli, 0, 0, false, picture_id); + GetFeedbackState(), kRtcpSli, 0, 0, false, picture_id); } int32_t ModuleRtpRtcpImpl::SetCameraDelay(const int32_t delay_ms) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SetCameraDelay(%d)", - delay_ms); if (IsDefaultModule()) { CriticalSectionScoped lock(critical_section_module_ptrs_.get()); - std::list::iterator it = child_modules_.begin(); + std::vector::iterator it = child_modules_.begin(); while (it != child_modules_.end()) { RtpRtcp* module = *it; if (module) { @@ -1366,18 +1121,6 @@ const bool enable, const uint8_t payload_type_red, const uint8_t payload_type_fec) { - if (enable) { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SetGenericFECStatus(enable, %u)", - payload_type_red); - } else { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SetGenericFECStatus(disable)"); - } return rtp_sender_.SetGenericFECStatus(enable, payload_type_red, payload_type_fec); @@ -1387,13 +1130,11 @@ bool& enable, uint8_t& payload_type_red, uint8_t& payload_type_fec) { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id_, "GenericFECStatus()"); - bool child_enabled = false; if (IsDefaultModule()) { // For default we need to check all child modules too. CriticalSectionScoped lock(critical_section_module_ptrs_.get()); - std::list::iterator it = child_modules_.begin(); + std::vector::iterator it = child_modules_.begin(); while (it != child_modules_.end()) { RtpRtcp* module = *it; if (module) { @@ -1427,7 +1168,7 @@ // For default we need to update all child modules too. CriticalSectionScoped lock(critical_section_module_ptrs_.get()); - std::list::iterator it = child_modules_.begin(); + std::vector::iterator it = child_modules_.begin(); while (it != child_modules_.end()) { RtpRtcp* module = *it; if (module) { @@ -1481,8 +1222,7 @@ if (nack_rate != NULL) *nack_rate = 0; - std::list::const_iterator it = - child_modules_.begin(); + std::vector::const_iterator it = child_modules_.begin(); while (it != child_modules_.end()) { RtpRtcp* module = *it; if (module) { @@ -1517,16 +1257,6 @@ *nack_rate = rtp_sender_.NackOverheadRate(); } -void ModuleRtpRtcpImpl::RegisterVideoBitrateObserver( - BitrateStatisticsObserver* observer) { - assert(!IsDefaultModule()); - rtp_sender_.RegisterBitrateObserver(observer); -} - -BitrateStatisticsObserver* ModuleRtpRtcpImpl::GetVideoBitrateObserver() const { - return rtp_sender_.GetBitrateObserver(); -} - void ModuleRtpRtcpImpl::OnRequestIntraFrame() { RequestKeyFrame(); } @@ -1537,9 +1267,8 @@ int32_t ModuleRtpRtcpImpl::SendRTCPReferencePictureSelection( const uint64_t picture_id) { - RTCPSender::FeedbackState feedback_state(this); return rtcp_sender_.SendRTCP( - feedback_state, kRtcpRpsi, 0, 0, false, picture_id); + GetFeedbackState(), kRtcpRpsi, 0, 0, false, picture_id); } bool ModuleRtpRtcpImpl::GetSendReportMetadata(const uint32_t send_report, @@ -1571,23 +1300,24 @@ rtp_sender_.OnReceivedNACK(nack_sequence_numbers, rtt); } -int32_t ModuleRtpRtcpImpl::LastReceivedNTP( - uint32_t& rtcp_arrival_time_secs, // When we got the last report. - uint32_t& rtcp_arrival_time_frac, - uint32_t& remote_sr) { +bool ModuleRtpRtcpImpl::LastReceivedNTP( + uint32_t* rtcp_arrival_time_secs, // When we got the last report. + uint32_t* rtcp_arrival_time_frac, + uint32_t* remote_sr) const { // Remote SR: NTP inside the last received (mid 16 bits from sec and frac). uint32_t ntp_secs = 0; uint32_t ntp_frac = 0; - if (-1 == rtcp_receiver_.NTP(&ntp_secs, - &ntp_frac, - &rtcp_arrival_time_secs, - &rtcp_arrival_time_frac, - NULL)) { - return -1; + if (!rtcp_receiver_.NTP(&ntp_secs, + &ntp_frac, + rtcp_arrival_time_secs, + rtcp_arrival_time_frac, + NULL)) { + return false; } - remote_sr = ((ntp_secs & 0x0000ffff) << 16) + ((ntp_frac & 0xffff0000) >> 16); - return 0; + *remote_sr = + ((ntp_secs & 0x0000ffff) << 16) + ((ntp_frac & 0xffff0000) >> 16); + return true; } bool ModuleRtpRtcpImpl::LastReceivedXrReferenceTimeInfo( @@ -1646,15 +1376,6 @@ return rtp_sender_.GetRtpStatisticsCallback(); } -void ModuleRtpRtcpImpl::RegisterSendFrameCountObserver( - FrameCountObserver* observer) { - rtp_sender_.RegisterFrameCountObserver(observer); -} - -FrameCountObserver* ModuleRtpRtcpImpl::GetSendFrameCountObserver() const { - return rtp_sender_.GetFrameCountObserver(); -} - bool ModuleRtpRtcpImpl::IsDefaultModule() const { CriticalSectionScoped cs(critical_section_module_ptrs_.get()); return !child_modules_.empty(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h 2015-02-03 14:33:36.000000000 +0000 @@ -21,10 +21,6 @@ #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/test/testsupport/gtest_prod_util.h" -#ifdef MATLAB -class MatlabPlot; -#endif - namespace webrtc { class ModuleRtpRtcpImpl : public RtpRtcp { @@ -46,7 +42,7 @@ virtual int32_t IncomingRtcpPacket(const uint8_t* incoming_packet, uint16_t incoming_packet_length) OVERRIDE; - virtual void SetRemoteSSRC(const uint32_t ssrc); + virtual void SetRemoteSSRC(const uint32_t ssrc) OVERRIDE; // Sender part. @@ -56,7 +52,7 @@ virtual int32_t DeRegisterSendPayload(const int8_t payload_type) OVERRIDE; - virtual int8_t SendPayloadType() const; + int8_t SendPayloadType() const; // Register RTP header extension. virtual int32_t RegisterSendRtpHeaderExtension( @@ -77,10 +73,14 @@ // Set SequenceNumber, default is a random number. virtual int32_t SetSequenceNumber(const uint16_t seq) OVERRIDE; + virtual void SetRtpStateForSsrc(uint32_t ssrc, + const RtpState& rtp_state) OVERRIDE; + virtual bool GetRtpStateForSsrc(uint32_t ssrc, RtpState* rtp_state) OVERRIDE; + virtual uint32_t SSRC() const OVERRIDE; // Configure SSRC, default is a random number. - virtual int32_t SetSSRC(const uint32_t ssrc) OVERRIDE; + virtual void SetSSRC(const uint32_t ssrc) OVERRIDE; virtual int32_t CSRCs(uint32_t arr_of_csrc[kRtpCsrcSize]) const OVERRIDE; @@ -89,19 +89,16 @@ virtual int32_t SetCSRCStatus(const bool include) OVERRIDE; - virtual uint32_t PacketCountSent() const; - - virtual int CurrentSendFrequencyHz() const; + RTCPSender::FeedbackState GetFeedbackState(); - virtual uint32_t ByteCountSent() const; + int CurrentSendFrequencyHz() const; - virtual int32_t SetRTXSendStatus(const int mode, - const bool set_ssrc, - const uint32_t ssrc) OVERRIDE; + virtual void SetRTXSendStatus(const int mode) OVERRIDE; - virtual int32_t RTXSendStatus(int* mode, uint32_t* ssrc, - int* payloadType) const OVERRIDE; + virtual void RTXSendStatus(int* mode, uint32_t* ssrc, + int* payloadType) const OVERRIDE; + virtual void SetRtxSsrc(uint32_t ssrc) OVERRIDE; virtual void SetRtxSendPayloadType(int payload_type) OVERRIDE; @@ -149,9 +146,6 @@ // Set RTCP CName. virtual int32_t SetCNAME(const char c_name[RTCP_CNAME_SIZE]) OVERRIDE; - // Get RTCP CName. - virtual int32_t CNAME(char c_name[RTCP_CNAME_SIZE]) OVERRIDE; - // Get remote CName. virtual int32_t RemoteCNAME(const uint32_t remote_ssrc, char c_name[RTCP_CNAME_SIZE]) const OVERRIDE; @@ -203,10 +197,14 @@ // Set received RTCP report block. virtual int32_t AddRTCPReportBlock( - const uint32_t ssrc, const RTCPReportBlock* receive_block) OVERRIDE; + const uint32_t ssrc, const RTCPReportBlock* receive_block) OVERRIDE; virtual int32_t RemoveRTCPReportBlock(const uint32_t ssrc) OVERRIDE; + virtual void GetRtcpPacketTypeCounters( + RtcpPacketTypeCounter* packets_sent, + RtcpPacketTypeCounter* packets_received) const OVERRIDE; + // (REMB) Receiver Estimated Max Bitrate. virtual bool REMB() const OVERRIDE; @@ -297,22 +295,12 @@ // Get payload type for Redundant Audio Data RFC 2198. virtual int32_t SendREDPayloadType(int8_t& payload_type) const OVERRIDE; - // Set status and id for header-extension-for-audio-level-indication. - virtual int32_t SetRTPAudioLevelIndicationStatus( - const bool enable, const uint8_t id) OVERRIDE; - - // Get status and id for header-extension-for-audio-level-indication. - virtual int32_t GetRTPAudioLevelIndicationStatus( - bool& enable, uint8_t& id) const OVERRIDE; - // Store the audio level in d_bov for header-extension-for-audio-level- // indication. virtual int32_t SetAudioLevel(const uint8_t level_d_bov) OVERRIDE; // Video part. - virtual RtpVideoCodecTypes SendVideoCodec() const; - virtual int32_t SendRTCPSliceLossIndication( const uint8_t picture_id) OVERRIDE; @@ -342,11 +330,11 @@ const FecProtectionParams* delta_params, const FecProtectionParams* key_params) OVERRIDE; - virtual int32_t LastReceivedNTP(uint32_t& NTPsecs, - uint32_t& NTPfrac, - uint32_t& remote_sr); + bool LastReceivedNTP(uint32_t* NTPsecs, + uint32_t* NTPfrac, + uint32_t* remote_sr) const; - virtual bool LastReceivedXrReferenceTimeInfo(RtcpReceiveTimeInfo* info) const; + bool LastReceivedXrReferenceTimeInfo(RtcpReceiveTimeInfo* info) const; virtual int32_t BoundingSet(bool& tmmbr_owner, TMMBRSet*& bounding_set_rec); @@ -355,26 +343,21 @@ uint32_t* fec_rate, uint32_t* nackRate) const OVERRIDE; - virtual void RegisterVideoBitrateObserver(BitrateStatisticsObserver* observer) - OVERRIDE; - - virtual BitrateStatisticsObserver* GetVideoBitrateObserver() const OVERRIDE; - - virtual bool GetSendReportMetadata(const uint32_t send_report, - uint32_t *time_of_send, - uint32_t *packet_count, - uint64_t *octet_count); + bool GetSendReportMetadata(const uint32_t send_report, + uint32_t *time_of_send, + uint32_t *packet_count, + uint64_t *octet_count); - virtual bool SendTimeOfXrRrReport(uint32_t mid_ntp, int64_t* time_ms) const; + bool SendTimeOfXrRrReport(uint32_t mid_ntp, int64_t* time_ms) const; // Good state of RTP receiver inform sender. virtual int32_t SendRTCPReferencePictureSelection( const uint64_t picture_id) OVERRIDE; virtual void RegisterSendChannelRtpStatisticsCallback( - StreamDataCountersCallback* callback); + StreamDataCountersCallback* callback) OVERRIDE; virtual StreamDataCountersCallback* - GetSendChannelRtpStatisticsCallback() const; + GetSendChannelRtpStatisticsCallback() const OVERRIDE; void OnReceivedTMMBR(); @@ -392,10 +375,6 @@ void OnRequestSendReport(); - virtual void RegisterSendFrameCountObserver( - FrameCountObserver* observer) OVERRIDE; - virtual FrameCountObserver* GetSendFrameCountObserver() const OVERRIDE; - protected: void RegisterChildModule(RtpRtcp* module); @@ -440,7 +419,8 @@ scoped_ptr critical_section_module_ptrs_; scoped_ptr critical_section_module_ptrs_feedback_; ModuleRtpRtcpImpl* default_module_; - std::list child_modules_; + std::vector child_modules_; + size_t padding_index_; // Send side NACKMethod nack_method_; @@ -453,10 +433,6 @@ RemoteBitrateEstimator* remote_bitrate_; -#ifdef MATLAB - MatlabPlot* plot1_; -#endif - RtcpRttStats* rtt_stats_; // The processed RTT from RtcpRttStats. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -12,24 +12,39 @@ #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/common_types.h" +#include "webrtc/modules/pacing/include/mock/mock_paced_sender.h" +#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h" #include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h" +#include "webrtc/system_wrappers/interface/scoped_vector.h" +#include "webrtc/test/rtcp_packet_parser.h" + +using ::testing::_; +using ::testing::ElementsAre; +using ::testing::NiceMock; +using ::testing::Return; +using ::testing::SaveArg; namespace webrtc { namespace { const uint32_t kSenderSsrc = 0x12345; const uint32_t kReceiverSsrc = 0x23456; +const uint32_t kSenderRtxSsrc = 0x32345; const uint32_t kOneWayNetworkDelayMs = 100; +const uint8_t kBaseLayerTid = 0; +const uint8_t kHigherLayerTid = 1; +const uint16_t kSequenceNumber = 100; class RtcpRttStatsTestImpl : public RtcpRttStats { public: RtcpRttStatsTestImpl() : rtt_ms_(0) {} virtual ~RtcpRttStatsTestImpl() {} - virtual void OnRttUpdate(uint32_t rtt_ms) { + virtual void OnRttUpdate(uint32_t rtt_ms) OVERRIDE { rtt_ms_ = rtt_ms; } - virtual uint32_t LastProcessedRtt() const { + virtual uint32_t LastProcessedRtt() const OVERRIDE { return rtt_ms_; } uint32_t rtt_ms_; @@ -38,7 +53,12 @@ class SendTransport : public Transport, public NullRtpData { public: - SendTransport() : receiver_(NULL), clock_(NULL), delay_ms_(0) {} + SendTransport() + : receiver_(NULL), + clock_(NULL), + delay_ms_(0), + rtp_packets_sent_(0) { + } void SetRtpRtcpModule(ModuleRtpRtcpImpl* receiver) { receiver_ = receiver; @@ -47,10 +67,21 @@ clock_ = clock; delay_ms_ = delay_ms; } - virtual int SendPacket(int /*ch*/, const void* /*data*/, int /*len*/) { - return -1; + virtual int SendPacket(int /*ch*/, const void* data, int len) OVERRIDE { + RTPHeader header; + scoped_ptr parser(RtpHeaderParser::Create()); + EXPECT_TRUE(parser->Parse(static_cast(data), + static_cast(len), + &header)); + ++rtp_packets_sent_; + last_rtp_header_ = header; + return len; } - virtual int SendRTCPPacket(int /*ch*/, const void *data, int len) { + virtual int SendRTCPPacket(int /*ch*/, const void *data, int len) OVERRIDE { + test::RtcpPacketParser parser; + parser.Parse(static_cast(data), len); + last_nack_list_ = parser.nack_item()->last_nack_list(); + if (clock_) { clock_->AdvanceTimeMilliseconds(delay_ms_); } @@ -62,6 +93,9 @@ ModuleRtpRtcpImpl* receiver_; SimulatedClock* clock_; uint32_t delay_ms_; + int rtp_packets_sent_; + RTPHeader last_rtp_header_; + std::vector last_nack_list_; }; class RtpRtcpModule { @@ -80,10 +114,31 @@ transport_.SimulateNetworkDelay(kOneWayNetworkDelayMs, clock); } + + RtcpPacketTypeCounter packets_sent_; + RtcpPacketTypeCounter packets_received_; scoped_ptr receive_statistics_; SendTransport transport_; RtcpRttStatsTestImpl rtt_stats_; scoped_ptr impl_; + + RtcpPacketTypeCounter RtcpSent() { + impl_->GetRtcpPacketTypeCounters(&packets_sent_, &packets_received_); + return packets_sent_; + } + RtcpPacketTypeCounter RtcpReceived() { + impl_->GetRtcpPacketTypeCounters(&packets_sent_, &packets_received_); + return packets_received_; + } + int RtpSent() { + return transport_.rtp_packets_sent_; + } + uint16_t LastRtpSequenceNumber() { + return transport_.last_rtp_header_.sequenceNumber; + } + std::vector LastNackListSent() { + return transport_.last_nack_list_; + } }; } // namespace @@ -95,11 +150,23 @@ receiver_(&clock_) { // Send module. EXPECT_EQ(0, sender_.impl_->SetSendingStatus(true)); - EXPECT_EQ(0, sender_.impl_->SetSSRC(kSenderSsrc)); + EXPECT_EQ(0, sender_.impl_->SetSendingMediaStatus(true)); + sender_.impl_->SetSSRC(kSenderSsrc); sender_.impl_->SetRemoteSSRC(kReceiverSsrc); + sender_.impl_->SetSequenceNumber(kSequenceNumber); + sender_.impl_->SetStorePacketsStatus(true, 100); + + memset(&codec_, 0, sizeof(VideoCodec)); + codec_.plType = 100; + strncpy(codec_.plName, "VP8", 3); + codec_.width = 320; + codec_.height = 180; + EXPECT_EQ(0, sender_.impl_->RegisterSendPayload(codec_)); + // Receive module. EXPECT_EQ(0, receiver_.impl_->SetSendingStatus(false)); - EXPECT_EQ(0, receiver_.impl_->SetSSRC(kReceiverSsrc)); + EXPECT_EQ(0, receiver_.impl_->SetSendingMediaStatus(false)); + receiver_.impl_->SetSSRC(kReceiverSsrc); receiver_.impl_->SetRemoteSSRC(kSenderSsrc); // Transport settings. sender_.transport_.SetRtpRtcpModule(receiver_.impl_.get()); @@ -108,10 +175,93 @@ SimulatedClock clock_; RtpRtcpModule sender_; RtpRtcpModule receiver_; + VideoCodec codec_; + + void SendFrame(const RtpRtcpModule* module, uint8_t tid) { + RTPVideoHeaderVP8 vp8_header = {}; + vp8_header.temporalIdx = tid; + RTPVideoHeader rtp_video_header = { + codec_.width, codec_.height, true, 0, kRtpVideoVp8, {vp8_header}}; + + const uint8_t payload[100] = {0}; + EXPECT_EQ(0, module->impl_->SendOutgoingData(kVideoFrameKey, + codec_.plType, + 0, + 0, + payload, + sizeof(payload), + NULL, + &rtp_video_header)); + } + + void IncomingRtcpNack(const RtpRtcpModule* module, uint16_t sequence_number) { + rtcp::Nack nack; + uint16_t list[1]; + list[0] = sequence_number; + const uint16_t kListLength = sizeof(list) / sizeof(list[0]); + nack.From(kReceiverSsrc); + nack.To(kSenderSsrc); + nack.WithList(list, kListLength); + rtcp::RawPacket packet = nack.Build(); + EXPECT_EQ(0, module->impl_->IncomingRtcpPacket(packet.buffer(), + packet.buffer_length())); + } }; +TEST_F(RtpRtcpImplTest, SetSelectiveRetransmissions_BaseLayer) { + sender_.impl_->SetSelectiveRetransmissions(kRetransmitBaseLayer); + EXPECT_EQ(kRetransmitBaseLayer, sender_.impl_->SelectiveRetransmissions()); + + // Send frames. + EXPECT_EQ(0, sender_.RtpSent()); + SendFrame(&sender_, kBaseLayerTid); // kSequenceNumber + SendFrame(&sender_, kHigherLayerTid); // kSequenceNumber + 1 + SendFrame(&sender_, kNoTemporalIdx); // kSequenceNumber + 2 + EXPECT_EQ(3, sender_.RtpSent()); + EXPECT_EQ(kSequenceNumber + 2, sender_.LastRtpSequenceNumber()); + + // Frame with kBaseLayerTid re-sent. + IncomingRtcpNack(&sender_, kSequenceNumber); + EXPECT_EQ(4, sender_.RtpSent()); + EXPECT_EQ(kSequenceNumber, sender_.LastRtpSequenceNumber()); + // Frame with kHigherLayerTid not re-sent. + IncomingRtcpNack(&sender_, kSequenceNumber + 1); + EXPECT_EQ(4, sender_.RtpSent()); + // Frame with kNoTemporalIdx re-sent. + IncomingRtcpNack(&sender_, kSequenceNumber + 2); + EXPECT_EQ(5, sender_.RtpSent()); + EXPECT_EQ(kSequenceNumber + 2, sender_.LastRtpSequenceNumber()); +} + +TEST_F(RtpRtcpImplTest, SetSelectiveRetransmissions_HigherLayers) { + const uint8_t kSetting = kRetransmitBaseLayer + kRetransmitHigherLayers; + sender_.impl_->SetSelectiveRetransmissions(kSetting); + EXPECT_EQ(kSetting, sender_.impl_->SelectiveRetransmissions()); + + // Send frames. + EXPECT_EQ(0, sender_.RtpSent()); + SendFrame(&sender_, kBaseLayerTid); // kSequenceNumber + SendFrame(&sender_, kHigherLayerTid); // kSequenceNumber + 1 + SendFrame(&sender_, kNoTemporalIdx); // kSequenceNumber + 2 + EXPECT_EQ(3, sender_.RtpSent()); + EXPECT_EQ(kSequenceNumber + 2, sender_.LastRtpSequenceNumber()); + + // Frame with kBaseLayerTid re-sent. + IncomingRtcpNack(&sender_, kSequenceNumber); + EXPECT_EQ(4, sender_.RtpSent()); + EXPECT_EQ(kSequenceNumber, sender_.LastRtpSequenceNumber()); + // Frame with kHigherLayerTid re-sent. + IncomingRtcpNack(&sender_, kSequenceNumber + 1); + EXPECT_EQ(5, sender_.RtpSent()); + EXPECT_EQ(kSequenceNumber + 1, sender_.LastRtpSequenceNumber()); + // Frame with kNoTemporalIdx re-sent. + IncomingRtcpNack(&sender_, kSequenceNumber + 2); + EXPECT_EQ(6, sender_.RtpSent()); + EXPECT_EQ(kSequenceNumber + 2, sender_.LastRtpSequenceNumber()); +} + TEST_F(RtpRtcpImplTest, Rtt) { - RTPHeader header = {}; + RTPHeader header; header.timestamp = 1; header.sequenceNumber = 123; header.ssrc = kSenderSsrc; @@ -172,4 +322,323 @@ EXPECT_EQ(2 * kOneWayNetworkDelayMs, receiver_.rtt_stats_.LastProcessedRtt()); EXPECT_EQ(2 * kOneWayNetworkDelayMs, receiver_.impl_->rtt_ms()); } + +TEST_F(RtpRtcpImplTest, RtcpPacketTypeCounter_Nack) { + EXPECT_EQ(0U, sender_.RtcpReceived().nack_packets); + EXPECT_EQ(0U, receiver_.RtcpSent().nack_packets); + // Receive module sends a NACK. + const uint16_t kNackLength = 1; + uint16_t nack_list[kNackLength] = {123}; + EXPECT_EQ(0, receiver_.impl_->SendNACK(nack_list, kNackLength)); + EXPECT_EQ(1U, receiver_.RtcpSent().nack_packets); + + // Send module receives the NACK. + EXPECT_EQ(1U, sender_.RtcpReceived().nack_packets); +} + +TEST_F(RtpRtcpImplTest, RtcpPacketTypeCounter_FirAndPli) { + EXPECT_EQ(0U, sender_.RtcpReceived().fir_packets); + EXPECT_EQ(0U, receiver_.RtcpSent().fir_packets); + // Receive module sends a FIR. + EXPECT_EQ(0, receiver_.impl_->SendRTCP(kRtcpFir)); + EXPECT_EQ(1U, receiver_.RtcpSent().fir_packets); + // Send module receives the FIR. + EXPECT_EQ(1U, sender_.RtcpReceived().fir_packets); + + // Receive module sends a FIR and PLI. + EXPECT_EQ(0, receiver_.impl_->SendRTCP(kRtcpFir | kRtcpPli)); + EXPECT_EQ(2U, receiver_.RtcpSent().fir_packets); + EXPECT_EQ(1U, receiver_.RtcpSent().pli_packets); + // Send module receives the FIR and PLI. + EXPECT_EQ(2U, sender_.RtcpReceived().fir_packets); + EXPECT_EQ(1U, sender_.RtcpReceived().pli_packets); +} + +TEST_F(RtpRtcpImplTest, UniqueNackRequests) { + receiver_.transport_.SimulateNetworkDelay(0, &clock_); + EXPECT_EQ(0U, receiver_.RtcpSent().nack_packets); + EXPECT_EQ(0U, receiver_.RtcpSent().nack_requests); + EXPECT_EQ(0U, receiver_.RtcpSent().unique_nack_requests); + EXPECT_EQ(0, receiver_.RtcpSent().UniqueNackRequestsInPercent()); + + // Receive module sends NACK request. + const uint16_t kNackLength = 4; + uint16_t nack_list[kNackLength] = {10, 11, 13, 18}; + EXPECT_EQ(0, receiver_.impl_->SendNACK(nack_list, kNackLength)); + EXPECT_EQ(1U, receiver_.RtcpSent().nack_packets); + EXPECT_EQ(4U, receiver_.RtcpSent().nack_requests); + EXPECT_EQ(4U, receiver_.RtcpSent().unique_nack_requests); + EXPECT_THAT(receiver_.LastNackListSent(), ElementsAre(10, 11, 13, 18)); + + // Send module receives the request. + EXPECT_EQ(1U, sender_.RtcpReceived().nack_packets); + EXPECT_EQ(4U, sender_.RtcpReceived().nack_requests); + EXPECT_EQ(4U, sender_.RtcpReceived().unique_nack_requests); + EXPECT_EQ(100, sender_.RtcpReceived().UniqueNackRequestsInPercent()); + + // Receive module sends new request with duplicated packets. + const int kStartupRttMs = 100; + clock_.AdvanceTimeMilliseconds(kStartupRttMs + 1); + const uint16_t kNackLength2 = 4; + uint16_t nack_list2[kNackLength2] = {11, 18, 20, 21}; + EXPECT_EQ(0, receiver_.impl_->SendNACK(nack_list2, kNackLength2)); + EXPECT_EQ(2U, receiver_.RtcpSent().nack_packets); + EXPECT_EQ(8U, receiver_.RtcpSent().nack_requests); + EXPECT_EQ(6U, receiver_.RtcpSent().unique_nack_requests); + EXPECT_THAT(receiver_.LastNackListSent(), ElementsAre(11, 18, 20, 21)); + + // Send module receives the request. + EXPECT_EQ(2U, sender_.RtcpReceived().nack_packets); + EXPECT_EQ(8U, sender_.RtcpReceived().nack_requests); + EXPECT_EQ(6U, sender_.RtcpReceived().unique_nack_requests); + EXPECT_EQ(75, sender_.RtcpReceived().UniqueNackRequestsInPercent()); +} + +class RtpSendingTestTransport : public Transport { + public: + void ResetCounters() { bytes_received_.clear(); } + + virtual int SendPacket(int channel, const void* data, int length) OVERRIDE { + RTPHeader header; + scoped_ptr parser(RtpHeaderParser::Create()); + EXPECT_TRUE(parser->Parse(static_cast(data), + static_cast(length), + &header)); + bytes_received_[header.ssrc] += length; + ++packets_received_[header.ssrc]; + return length; + } + + virtual int SendRTCPPacket(int channel, + const void* data, + int length) OVERRIDE { + return length; + } + + int GetPacketsReceived(uint32_t ssrc) const { + std::map::const_iterator it = packets_received_.find(ssrc); + if (it == packets_received_.end()) + return 0; + return it->second; + } + + int GetBytesReceived(uint32_t ssrc) const { + std::map::const_iterator it = bytes_received_.find(ssrc); + if (it == bytes_received_.end()) + return 0; + return it->second; + } + + int GetTotalBytesReceived() const { + int sum = 0; + for (std::map::const_iterator it = bytes_received_.begin(); + it != bytes_received_.end(); + ++it) { + sum += it->second; + } + return sum; + } + + private: + std::map bytes_received_; + std::map packets_received_; +}; + +class RtpSendingTest : public ::testing::Test { + protected: + // Map from SSRC to number of received packets and bytes. + typedef std::map > PaddingMap; + + RtpSendingTest() { + // Send module. + RtpRtcp::Configuration config; + config.audio = false; + config.clock = Clock::GetRealTimeClock(); + config.outgoing_transport = &transport_; + config.receive_statistics = receive_statistics_.get(); + config.rtt_stats = &rtt_stats_; + config.paced_sender = &pacer_; + memset(&codec_, 0, sizeof(VideoCodec)); + codec_.plType = 100; + strncpy(codec_.plName, "VP8", 3); + codec_.numberOfSimulcastStreams = 3; + codec_.simulcastStream[0].width = 320; + codec_.simulcastStream[0].height = 180; + codec_.simulcastStream[0].maxBitrate = 300; + codec_.simulcastStream[1].width = 640; + codec_.simulcastStream[1].height = 360; + codec_.simulcastStream[1].maxBitrate = 600; + codec_.simulcastStream[2].width = 1280; + codec_.simulcastStream[2].height = 720; + codec_.simulcastStream[2].maxBitrate = 1200; + // We need numberOfSimulcastStreams + 1 RTP modules since we need one + // default module. + for (int i = 0; i < codec_.numberOfSimulcastStreams + 1; ++i) { + RtpRtcp* sender = RtpRtcp::CreateRtpRtcp(config); + EXPECT_EQ(0, sender->RegisterSendPayload(codec_)); + EXPECT_EQ(0, sender->SetSendingStatus(true)); + EXPECT_EQ(0, sender->SetSendingMediaStatus(true)); + sender->SetSSRC(kSenderSsrc + i); + sender->SetRemoteSSRC(kReceiverSsrc + i); + senders_.push_back(sender); + config.default_module = senders_[0]; + } + std::vector bitrates; + bitrates.push_back(codec_.simulcastStream[0].maxBitrate); + bitrates.push_back(codec_.simulcastStream[1].maxBitrate); + bitrates.push_back(codec_.simulcastStream[2].maxBitrate); + senders_[0]->SetTargetSendBitrate(bitrates); + } + + ~RtpSendingTest() { + for (int i = senders_.size() - 1; i >= 0; --i) { + delete senders_[i]; + } + } + + void SendFrameOnSender(int sender_index, + const uint8_t* payload, + size_t length) { + RTPVideoHeader rtp_video_header = { + codec_.simulcastStream[sender_index].width, + codec_.simulcastStream[sender_index].height, + true, + 0, + kRtpVideoVp8, + {}}; + uint32_t seq_num = 0; + uint32_t ssrc = 0; + int64_t capture_time_ms = 0; + bool retransmission = false; + EXPECT_CALL(pacer_, SendPacket(_, _, _, _, _, _)) + .WillRepeatedly(DoAll(SaveArg<1>(&ssrc), + SaveArg<2>(&seq_num), + SaveArg<3>(&capture_time_ms), + SaveArg<5>(&retransmission), + Return(true))); + EXPECT_EQ(0, + senders_[sender_index]->SendOutgoingData(kVideoFrameKey, + codec_.plType, + 0, + 0, + payload, + length, + NULL, + &rtp_video_header)); + EXPECT_TRUE(senders_[sender_index]->TimeToSendPacket( + ssrc, seq_num, capture_time_ms, retransmission)); + } + + void ExpectPadding(const PaddingMap& expected_padding) { + int expected_total_bytes = 0; + for (PaddingMap::const_iterator it = expected_padding.begin(); + it != expected_padding.end(); + ++it) { + int packets_received = transport_.GetBytesReceived(it->first); + if (it->second.first > 0) { + EXPECT_GE(packets_received, it->second.first) + << "On SSRC: " << it->first; + } + int bytes_received = transport_.GetBytesReceived(it->first); + expected_total_bytes += bytes_received; + if (it->second.second > 0) { + EXPECT_GE(bytes_received, it->second.second) + << "On SSRC: " << it->first; + } else { + EXPECT_EQ(0, bytes_received) << "On SSRC: " << it->first; + } + } + EXPECT_EQ(expected_total_bytes, transport_.GetTotalBytesReceived()); + } + + scoped_ptr receive_statistics_; + RtcpRttStatsTestImpl rtt_stats_; + std::vector senders_; + RtpSendingTestTransport transport_; + NiceMock pacer_; + VideoCodec codec_; +}; + +TEST_F(RtpSendingTest, DISABLED_RoundRobinPadding) { + // We have to send on an SSRC to be allowed to pad, since a marker bit must + // be sent prior to padding packets. + const uint8_t payload[200] = {0}; + for (int i = 0; i < codec_.numberOfSimulcastStreams; ++i) { + SendFrameOnSender(i + 1, payload, sizeof(payload)); + } + transport_.ResetCounters(); + senders_[0]->TimeToSendPadding(500); + PaddingMap expected_padding; + expected_padding[kSenderSsrc + 1] = std::make_pair(2, 500); + expected_padding[kSenderSsrc + 2] = std::make_pair(0, 0); + expected_padding[kSenderSsrc + 3] = std::make_pair(0, 0); + ExpectPadding(expected_padding); + senders_[0]->TimeToSendPadding(1000); + expected_padding[kSenderSsrc + 2] = std::make_pair(4, 1000); + ExpectPadding(expected_padding); + senders_[0]->TimeToSendPadding(1500); + expected_padding[kSenderSsrc + 3] = std::make_pair(6, 1500); + ExpectPadding(expected_padding); +} + +TEST_F(RtpSendingTest, DISABLED_RoundRobinPaddingRtx) { + // Enable RTX to allow padding to be sent prior to media. + for (int i = 1; i < codec_.numberOfSimulcastStreams + 1; ++i) { + // Abs-send-time is needed to be allowed to send padding prior to media, + // as otherwise the timestmap used for BWE will be broken. + senders_[i]->RegisterSendRtpHeaderExtension(kRtpExtensionAbsoluteSendTime, + 1); + senders_[i]->SetRtxSendPayloadType(96); + senders_[i]->SetRtxSsrc(kSenderRtxSsrc + i); + senders_[i]->SetRTXSendStatus(kRtxRetransmitted); + } + transport_.ResetCounters(); + senders_[0]->TimeToSendPadding(500); + PaddingMap expected_padding; + expected_padding[kSenderSsrc + 1] = std::make_pair(0, 0); + expected_padding[kSenderSsrc + 2] = std::make_pair(0, 0); + expected_padding[kSenderSsrc + 3] = std::make_pair(0, 0); + expected_padding[kSenderRtxSsrc + 1] = std::make_pair(2, 500); + expected_padding[kSenderRtxSsrc + 2] = std::make_pair(0, 0); + expected_padding[kSenderRtxSsrc + 3] = std::make_pair(0, 0); + ExpectPadding(expected_padding); + senders_[0]->TimeToSendPadding(1000); + expected_padding[kSenderRtxSsrc + 2] = std::make_pair(4, 500); + ExpectPadding(expected_padding); + senders_[0]->TimeToSendPadding(1500); + + expected_padding[kSenderRtxSsrc + 3] = std::make_pair(6, 500); + ExpectPadding(expected_padding); +} + +TEST_F(RtpSendingTest, DISABLED_RoundRobinPaddingRtxRedundantPayloads) { + for (int i = 1; i < codec_.numberOfSimulcastStreams + 1; ++i) { + senders_[i]->SetRtxSendPayloadType(96); + senders_[i]->SetRtxSsrc(kSenderRtxSsrc + i); + senders_[i]->SetRTXSendStatus(kRtxRetransmitted | kRtxRedundantPayloads); + senders_[i]->SetStorePacketsStatus(true, 100); + } + // First send payloads so that we have something to retransmit. + const size_t kPayloadSize = 500; + const uint8_t payload[kPayloadSize] = {0}; + for (int i = 0; i < codec_.numberOfSimulcastStreams; ++i) { + SendFrameOnSender(i + 1, payload, sizeof(payload)); + } + transport_.ResetCounters(); + senders_[0]->TimeToSendPadding(500); + PaddingMap expected_padding; + expected_padding[kSenderSsrc + 1] = std::make_pair(0, 0); + expected_padding[kSenderSsrc + 2] = std::make_pair(0, 0); + expected_padding[kSenderSsrc + 3] = std::make_pair(0, 0); + expected_padding[kSenderRtxSsrc + 1] = std::make_pair(1, 500); + expected_padding[kSenderRtxSsrc + 2] = std::make_pair(0, 0); + expected_padding[kSenderRtxSsrc + 3] = std::make_pair(0, 0); + ExpectPadding(expected_padding); + senders_[0]->TimeToSendPadding(1000); + expected_padding[kSenderRtxSsrc + 2] = std::make_pair(2, 1000); + ExpectPadding(expected_padding); + senders_[0]->TimeToSendPadding(1500); + expected_padding[kSenderRtxSsrc + 3] = std::make_pair(3, 1500); + ExpectPadding(expected_padding); +} } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.cc 2015-02-03 14:33:36.000000000 +0000 @@ -17,7 +17,7 @@ namespace webrtc { RTPSenderAudio::RTPSenderAudio(const int32_t id, Clock* clock, - RTPSenderInterface* rtpSender) : + RTPSender* rtpSender) : _id(id), _clock(clock), _rtpSender(rtpSender), @@ -42,8 +42,6 @@ _cngSWBPayloadType(-1), _cngFBPayloadType(-1), _lastPayloadType(-1), - _includeAudioLevelIndication(false), // @TODO - reset at Init()? - _audioLevelIndicationID(0), _audioLevel_dBov(0) { }; @@ -91,10 +89,10 @@ const uint32_t frequency, const uint8_t channels, const uint32_t rate, - ModuleRTPUtility::Payload*& payload) { + RtpUtility::Payload*& payload) { CriticalSectionScoped cs(_sendAudioCritsect); - if (ModuleRTPUtility::StringCompare(payloadName, "cn", 2)) { + if (RtpUtility::StringCompare(payloadName, "cn", 2)) { // we can have multiple CNG payload types if (frequency == 8000) { _cngNBPayloadType = payloadType; @@ -112,14 +110,14 @@ return -1; } } - if (ModuleRTPUtility::StringCompare(payloadName, "telephone-event", 15)) { + if (RtpUtility::StringCompare(payloadName, "telephone-event", 15)) { // Don't add it to the list // we dont want to allow send with a DTMF payloadtype _dtmfPayloadType = payloadType; return 0; // The default timestamp rate is 8000 Hz, but other rates may be defined. } - payload = new ModuleRTPUtility::Payload; + payload = new RtpUtility::Payload; payload->typeSpecific.Audio.frequency = frequency; payload->typeSpecific.Audio.channels = channels; payload->typeSpecific.Audio.rate = rate; @@ -320,13 +318,15 @@ static_cast(dtmfDurationSamples), false); } else { - // set markerBit on the first packet in the burst + if (SendTelephoneEventPacket( + ended, + _dtmfTimestamp, + static_cast(dtmfDurationSamples), + !_dtmfEventFirstPacketSent) != 0) { + return -1; + } _dtmfEventFirstPacketSent = true; - return SendTelephoneEventPacket( - ended, - _dtmfTimestamp, - static_cast(dtmfDurationSamples), - !_dtmfEventFirstPacketSent); + return 0; } } return 0; @@ -365,52 +365,12 @@ if (rtpHeaderLength <= 0) { return -1; } + if (maxPayloadLength < (rtpHeaderLength + payloadSize)) { + // Too large payload buffer. + return -1; + } { CriticalSectionScoped cs(_sendAudioCritsect); - - // https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/ - if (_includeAudioLevelIndication) { - dataBuffer[0] |= 0x10; // set eXtension bit - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 0xBE | 0xDE | length=1 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | ID | len=0 |V| level | 0x00 | 0x00 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - // add our ID (0xBEDE) - ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+rtpHeaderLength, - RTP_AUDIO_LEVEL_UNIQUE_ID); - rtpHeaderLength += 2; - - // add the length (length=1) in number of word32 - const uint8_t length = 1; - ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+rtpHeaderLength, - length); - rtpHeaderLength += 2; - - // add ID (defined by the user) and len(=0) byte - const uint8_t id = _audioLevelIndicationID; - const uint8_t len = 0; - dataBuffer[rtpHeaderLength++] = (id << 4) + len; - - // add voice-activity flag (V) bit and the audio level (in dBov) - const uint8_t V = (frameType == kAudioFrameSpeech); - uint8_t level = _audioLevel_dBov; - dataBuffer[rtpHeaderLength++] = (V << 7) + level; - - // add two bytes zero padding - ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+rtpHeaderLength, 0); - rtpHeaderLength += 2; - } - - if(maxPayloadLength < rtpHeaderLength + payloadSize ) { - // too large payload buffer - return -1; - } - if (_REDPayloadType >= 0 && // Have we configured RED? fragmentation && fragmentation->fragmentationVectorSize > 1 && @@ -430,8 +390,8 @@ return -1; } uint32_t REDheader = (timestampOffset << 10) + blockLength; - ModuleRTPUtility::AssignUWord24ToBuffer(dataBuffer + rtpHeaderLength, - REDheader); + RtpUtility::AssignUWord24ToBuffer(dataBuffer + rtpHeaderLength, + REDheader); rtpHeaderLength += 3; dataBuffer[rtpHeaderLength++] = fragmentation->fragmentationPlType[0]; @@ -474,6 +434,17 @@ } } _lastPayloadType = payloadType; + + // Update audio level extension, if included. + { + uint16_t packetSize = payloadSize + rtpHeaderLength; + RtpUtility::RtpHeaderParser rtp_parser(dataBuffer, packetSize); + RTPHeader rtp_header; + rtp_parser.Parse(rtp_header); + _rtpSender->UpdateAudioLevel(dataBuffer, packetSize, rtp_header, + (frameType == kAudioFrameSpeech), + _audioLevel_dBov); + } } // end critical section TRACE_EVENT_ASYNC_END2("webrtc", "Audio", captureTimeStamp, "timestamp", _rtpSender->Timestamp(), @@ -486,32 +457,6 @@ PacedSender::kHighPriority); } -int32_t -RTPSenderAudio::SetAudioLevelIndicationStatus(const bool enable, - const uint8_t ID) -{ - if(enable && (ID < 1 || ID > 14)) - { - return -1; - } - CriticalSectionScoped cs(_sendAudioCritsect); - - _includeAudioLevelIndication = enable; - _audioLevelIndicationID = ID; - - return 0; -} - -int32_t -RTPSenderAudio::AudioLevelIndicationStatus(bool& enable, - uint8_t& ID) const -{ - CriticalSectionScoped cs(_sendAudioCritsect); - enable = _includeAudioLevelIndication; - ID = _audioLevelIndicationID; - return 0; -} - // Audio level magnitude and voice activity flag are set for each RTP packet int32_t RTPSenderAudio::SetAudioLevel(const uint8_t level_dBov) @@ -615,7 +560,7 @@ // First byte is Event number, equals key number dtmfbuffer[12] = _dtmfKey; dtmfbuffer[13] = E|R|volume; - ModuleRTPUtility::AssignUWord16ToBuffer(dtmfbuffer+14, duration); + RtpUtility::AssignUWord16ToBuffer(dtmfbuffer + 14, duration); _sendAudioCritsect->Leave(); TRACE_EVENT_INSTANT2("webrtc_rtp", diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h 2015-02-03 14:33:36.000000000 +0000 @@ -23,16 +23,15 @@ { public: RTPSenderAudio(const int32_t id, Clock* clock, - RTPSenderInterface* rtpSender); + RTPSender* rtpSender); virtual ~RTPSenderAudio(); - int32_t RegisterAudioPayload( - const char payloadName[RTP_PAYLOAD_NAME_SIZE], - const int8_t payloadType, - const uint32_t frequency, - const uint8_t channels, - const uint32_t rate, - ModuleRTPUtility::Payload*& payload); + int32_t RegisterAudioPayload(const char payloadName[RTP_PAYLOAD_NAME_SIZE], + const int8_t payloadType, + const uint32_t frequency, + const uint8_t channels, + const uint32_t rate, + RtpUtility::Payload*& payload); int32_t SendAudio(const FrameType frameType, const int8_t payloadType, @@ -44,13 +43,6 @@ // set audio packet size, used to determine when it's time to send a DTMF packet in silence (CNG) int32_t SetAudioPacketSize(const uint16_t packetSizeSamples); - // Set status and ID for header-extension-for-audio-level-indication. - // Valid ID range is [1,14]. - int32_t SetAudioLevelIndicationStatus(const bool enable, const uint8_t ID); - - // Get status and ID for header-extension-for-audio-level-indication. - int32_t AudioLevelIndicationStatus(bool& enable, uint8_t& ID) const; - // Store the audio level in dBov for header-extension-for-audio-level-indication. // Valid range is [0,100]. Actual value is negative. int32_t SetAudioLevel(const uint8_t level_dBov); @@ -86,7 +78,7 @@ private: int32_t _id; Clock* _clock; - RTPSenderInterface* _rtpSender; + RTPSender* _rtpSender; CriticalSectionWrapper* _audioFeedbackCritsect; RtpAudioFeedback* _audioFeedback; @@ -117,8 +109,6 @@ int8_t _lastPayloadType; // Audio level indication (https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/) - bool _includeAudioLevelIndication; - uint8_t _audioLevelIndicationID; uint8_t _audioLevel_dBov; }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender.cc 2015-02-03 14:33:36.000000000 +0000 @@ -15,7 +15,8 @@ #include "webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h" #include "webrtc/modules/rtp_rtcp/source/rtp_sender_video.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/system_wrappers/interface/tick_util.h" #include "webrtc/system_wrappers/interface/trace_event.h" namespace webrtc { @@ -39,24 +40,83 @@ } // namespace +class BitrateAggregator { + public: + explicit BitrateAggregator(BitrateStatisticsObserver* bitrate_callback) + : callback_(bitrate_callback), + total_bitrate_observer_(*this), + retransmit_bitrate_observer_(*this), + ssrc_(0) {} + + void OnStatsUpdated() const { + if (callback_) + callback_->Notify(total_bitrate_observer_.statistics(), + retransmit_bitrate_observer_.statistics(), + ssrc_); + } + + Bitrate::Observer* total_bitrate_observer() { + return &total_bitrate_observer_; + } + Bitrate::Observer* retransmit_bitrate_observer() { + return &retransmit_bitrate_observer_; + } + + void set_ssrc(uint32_t ssrc) { ssrc_ = ssrc; } + + private: + // We assume that these observers are called on the same thread, which is + // true for RtpSender as they are called on the Process thread. + class BitrateObserver : public Bitrate::Observer { + public: + explicit BitrateObserver(const BitrateAggregator& aggregator) + : aggregator_(aggregator) {} + + // Implements Bitrate::Observer. + virtual void BitrateUpdated(const BitrateStatistics& stats) OVERRIDE { + statistics_ = stats; + aggregator_.OnStatsUpdated(); + } + + BitrateStatistics statistics() const { return statistics_; } + + private: + BitrateStatistics statistics_; + const BitrateAggregator& aggregator_; + }; + + BitrateStatisticsObserver* const callback_; + BitrateObserver total_bitrate_observer_; + BitrateObserver retransmit_bitrate_observer_; + uint32_t ssrc_; +}; + RTPSender::RTPSender(const int32_t id, const bool audio, Clock* clock, Transport* transport, RtpAudioFeedback* audio_feedback, - PacedSender* paced_sender) + PacedSender* paced_sender, + BitrateStatisticsObserver* bitrate_callback, + FrameCountObserver* frame_count_observer, + SendSideDelayObserver* send_side_delay_observer) : clock_(clock), - bitrate_sent_(clock, this), + // TODO(holmer): Remove this conversion when we remove the use of + // TickTime. + clock_delta_ms_(clock_->TimeInMilliseconds() - + TickTime::MillisecondTimestamp()), + bitrates_(new BitrateAggregator(bitrate_callback)), + total_bitrate_sent_(clock, bitrates_->total_bitrate_observer()), id_(id), audio_configured_(audio), audio_(NULL), video_(NULL), paced_sender_(paced_sender), + last_capture_time_ms_sent_(0), send_critsect_(CriticalSectionWrapper::CreateCriticalSection()), transport_(transport), sending_media_(true), // Default to sending media. max_payload_length_(IP_PACKET_SIZE - 28), // Default is IP-v4/UDP. - target_send_bitrate_(0), packet_over_head_(28), payload_type_(-1), payload_type_map_(), @@ -66,16 +126,16 @@ // NACK. nack_byte_count_times_(), nack_byte_count_(), - nack_bitrate_(clock, NULL), + nack_bitrate_(clock, bitrates_->retransmit_bitrate_observer()), packet_history_(clock), // Statistics statistics_crit_(CriticalSectionWrapper::CreateCriticalSection()), - frame_count_observer_(NULL), rtp_stats_callback_(NULL), - bitrate_callback_(NULL), + frame_count_observer_(frame_count_observer), + send_side_delay_observer_(send_side_delay_observer), // RTP variables - start_time_stamp_forced_(false), - start_time_stamp_(0), + start_timestamp_forced_(false), + start_timestamp_(0), ssrc_db_(*SSRCDatabase::GetSSRCDatabase()), remote_ssrc_(0), sequence_number_forced_(false), @@ -83,12 +143,15 @@ timestamp_(0), capture_time_ms_(0), last_timestamp_time_ms_(0), + media_has_been_sent_(false), last_packet_marker_bit_(false), num_csrcs_(0), csrcs_(), include_csrcs_(true), rtx_(kRtxOff), - payload_type_rtx_(-1) { + payload_type_rtx_(-1), + target_bitrate_critsect_(CriticalSectionWrapper::CreateCriticalSection()), + target_bitrate_(0) { memset(nack_byte_count_times_, 0, sizeof(nack_byte_count_times_)); memset(nack_byte_count_, 0, sizeof(nack_byte_count_)); memset(csrcs_, 0, sizeof(csrcs_)); @@ -96,6 +159,7 @@ srand(static_cast(clock_->TimeInMilliseconds())); ssrc_ = ssrc_db_.CreateSSRC(); // Can't be 0. ssrc_rtx_ = ssrc_db_.CreateSSRC(); // Can't be 0. + bitrates_->set_ssrc(ssrc_); // Random start, 16 bits. Can't be 0. sequence_number_rtx_ = static_cast(rand() + 1) & 0x7FFF; sequence_number_ = static_cast(rand() + 1) & 0x7FFF; @@ -104,9 +168,8 @@ audio_ = new RTPSenderAudio(id, clock_, this); audio_->RegisterAudioCallback(audio_feedback); } else { - video_ = new RTPSenderVideo(id, clock_, this); + video_ = new RTPSenderVideo(clock_, this); } - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id, "%s created", __FUNCTION__); } RTPSender::~RTPSender() { @@ -118,23 +181,27 @@ SSRCDatabase::ReturnSSRCDatabase(); delete send_critsect_; while (!payload_type_map_.empty()) { - std::map::iterator it = + std::map::iterator it = payload_type_map_.begin(); delete it->second; payload_type_map_.erase(it); } delete audio_; delete video_; +} - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id_, "%s deleted", __FUNCTION__); +void RTPSender::SetTargetBitrate(uint32_t bitrate) { + CriticalSectionScoped cs(target_bitrate_critsect_.get()); + target_bitrate_ = bitrate; } -void RTPSender::SetTargetSendBitrate(const uint32_t bits) { - target_send_bitrate_ = static_cast(bits / 1000); +uint32_t RTPSender::GetTargetBitrate() { + CriticalSectionScoped cs(target_bitrate_critsect_.get()); + return target_bitrate_; } uint16_t RTPSender::ActualSendBitrateKbit() const { - return (uint16_t)(bitrate_sent_.BitrateNow() / 1000); + return (uint16_t)(total_bitrate_sent_.BitrateNow() / 1000); } uint32_t RTPSender::VideoBitrateSent() const { @@ -157,10 +224,10 @@ bool RTPSender::GetSendSideDelay(int* avg_send_delay_ms, int* max_send_delay_ms) const { - CriticalSectionScoped cs(statistics_crit_.get()); + CriticalSectionScoped lock(statistics_crit_.get()); SendDelayMap::const_iterator it = send_delays_.upper_bound( clock_->TimeInMilliseconds() - kSendSideDelayWindowMs); - if (!sending_media_ || it == send_delays_.end()) + if (it == send_delays_.end()) return false; int num_delays = 0; for (; it != send_delays_.end(); ++it) { @@ -217,17 +284,17 @@ assert(payload_name); CriticalSectionScoped cs(send_critsect_); - std::map::iterator it = + std::map::iterator it = payload_type_map_.find(payload_number); if (payload_type_map_.end() != it) { // We already use this payload type. - ModuleRTPUtility::Payload *payload = it->second; + RtpUtility::Payload* payload = it->second; assert(payload); // Check if it's the same as we already have. - if (ModuleRTPUtility::StringCompare(payload->name, payload_name, - RTP_PAYLOAD_NAME_SIZE - 1)) { + if (RtpUtility::StringCompare( + payload->name, payload_name, RTP_PAYLOAD_NAME_SIZE - 1)) { if (audio_configured_ && payload->audio && payload->typeSpecific.Audio.frequency == frequency && (payload->typeSpecific.Audio.rate == rate || @@ -243,7 +310,7 @@ return -1; } int32_t ret_val = -1; - ModuleRTPUtility::Payload *payload = NULL; + RtpUtility::Payload* payload = NULL; if (audio_configured_) { ret_val = audio_->RegisterAudioPayload(payload_name, payload_number, frequency, channels, rate, payload); @@ -261,19 +328,27 @@ const int8_t payload_type) { CriticalSectionScoped lock(send_critsect_); - std::map::iterator it = + std::map::iterator it = payload_type_map_.find(payload_type); if (payload_type_map_.end() == it) { return -1; } - ModuleRTPUtility::Payload *payload = it->second; + RtpUtility::Payload* payload = it->second; delete payload; payload_type_map_.erase(it); return 0; } -int8_t RTPSender::SendPayloadType() const { return payload_type_; } +void RTPSender::SetSendPayloadType(int8_t payload_type) { + CriticalSectionScoped cs(send_critsect_); + payload_type_ = payload_type; +} + +int8_t RTPSender::SendPayloadType() const { + CriticalSectionScoped cs(send_critsect_); + return payload_type_; +} int RTPSender::SendPayloadFrequency() const { return audio_ != NULL ? audio_->AudioFrequency() : kVideoPayloadTypeFrequency; @@ -284,26 +359,27 @@ const uint16_t packet_over_head) { // Sanity check. if (max_payload_length < 100 || max_payload_length > IP_PACKET_SIZE) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, "%s invalid argument", - __FUNCTION__); + LOG(LS_ERROR) << "Invalid max payload length: " << max_payload_length; return -1; } CriticalSectionScoped cs(send_critsect_); max_payload_length_ = max_payload_length; packet_over_head_ = packet_over_head; - - WEBRTC_TRACE(kTraceInfo, kTraceRtpRtcp, id_, "SetMaxPayloadLength to %d.", - max_payload_length); return 0; } uint16_t RTPSender::MaxDataPayloadLength() const { + int rtx; + { + CriticalSectionScoped rtx_lock(send_critsect_); + rtx = rtx_; + } if (audio_configured_) { return max_payload_length_ - RTPHeaderLength(); } else { return max_payload_length_ - RTPHeaderLength() // RTP overhead. - video_->FECPacketOverhead() // FEC/ULP/RED overhead. - - ((rtx_) ? 2 : 0); // RTX overhead. + - ((rtx) ? 2 : 0); // RTX overhead. } } @@ -313,16 +389,19 @@ uint16_t RTPSender::PacketOverHead() const { return packet_over_head_; } -void RTPSender::SetRTXStatus(int mode, bool set_ssrc, uint32_t ssrc) { +void RTPSender::SetRTXStatus(int mode) { CriticalSectionScoped cs(send_critsect_); rtx_ = mode; - if (rtx_ != kRtxOff) { - if (set_ssrc) { - ssrc_rtx_ = ssrc; - } else { - ssrc_rtx_ = ssrc_db_.CreateSSRC(); // Can't be 0. - } - } +} + +void RTPSender::SetRtxSsrc(uint32_t ssrc) { + CriticalSectionScoped cs(send_critsect_); + ssrc_rtx_ = ssrc; +} + +uint32_t RTPSender::RtxSsrc() const { + CriticalSectionScoped cs(send_critsect_); + return ssrc_rtx_; } void RTPSender::RTXStatus(int* mode, uint32_t* ssrc, @@ -333,7 +412,6 @@ *payload_type = payload_type_rtx_; } - void RTPSender::SetRtxPayloadType(int payload_type) { CriticalSectionScoped cs(send_critsect_); payload_type_rtx_ = payload_type; @@ -344,8 +422,7 @@ CriticalSectionScoped cs(send_critsect_); if (payload_type < 0) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, "\tinvalid payload_type (%d)", - payload_type); + LOG(LS_ERROR) << "Invalid payload_type " << payload_type; return -1; } if (audio_configured_) { @@ -364,15 +441,14 @@ } return 0; } - std::map::iterator it = + std::map::iterator it = payload_type_map_.find(payload_type); if (it == payload_type_map_.end()) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, - "\tpayloadType:%d not registered", payload_type); + LOG(LS_WARNING) << "Payload type " << payload_type << " not registered."; return -1; } - payload_type_ = payload_type; - ModuleRTPUtility::Payload *payload = it->second; + SetSendPayloadType(payload_type); + RtpUtility::Payload* payload = it->second; assert(payload); if (!payload->audio && !audio_configured_) { video_->SetVideoCodecType(payload->typeSpecific.Video.videoCodecType); @@ -388,18 +464,18 @@ const uint8_t *payload_data, const uint32_t payload_size, const RTPFragmentationHeader *fragmentation, VideoCodecInformation *codec_info, const RTPVideoTypeHeader *rtp_type_hdr) { + uint32_t ssrc; { // Drop this packet if we're not sending media packets. CriticalSectionScoped cs(send_critsect_); + ssrc = ssrc_; if (!sending_media_) { return 0; } } RtpVideoCodecTypes video_type = kRtpVideoGeneric; if (CheckPayloadType(payload_type, &video_type) != 0) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, - "%s invalid argument failed to find payload_type:%d", - __FUNCTION__, payload_type); + LOG(LS_ERROR) << "Don't send data with unknown payload type."; return -1; } @@ -417,14 +493,9 @@ "Send", "type", FrameTypeToString(frame_type)); assert(frame_type != kAudioFrameSpeech && frame_type != kAudioFrameCN); - if (frame_type == kFrameEmpty) { - if (paced_sender_->Enabled()) { - // Padding is driven by the pacer and not by the encoder. - return 0; - } - return SendPaddingAccordingToBitrate(payload_type, capture_timestamp, - capture_time_ms) ? 0 : -1; - } + if (frame_type == kFrameEmpty) + return 0; + ret_val = video_->SendVideo(video_type, frame_type, payload_type, capture_timestamp, capture_time_ms, payload_data, payload_size, @@ -436,17 +507,19 @@ CriticalSectionScoped cs(statistics_crit_.get()); uint32_t frame_count = ++frame_counts_[frame_type]; if (frame_count_observer_) { - frame_count_observer_->FrameCountUpdated(frame_type, - frame_count, - ssrc_); + frame_count_observer_->FrameCountUpdated(frame_type, frame_count, ssrc); } return ret_val; } -int RTPSender::SendRedundantPayloads(int payload_type, int bytes_to_send) { - if (!(rtx_ & kRtxRedundantPayloads)) - return 0; +int RTPSender::TrySendRedundantPayloads(int bytes_to_send) { + { + CriticalSectionScoped cs(send_critsect_); + if ((rtx_ & kRtxRedundantPayloads) == 0) + return 0; + } + uint8_t buffer[IP_PACKET_SIZE]; int bytes_left = bytes_to_send; while (bytes_left > 0) { @@ -456,9 +529,9 @@ &capture_time_ms)) { break; } - if (!PrepareAndSendPacket(buffer, length, capture_time_ms, true)) + if (!PrepareAndSendPacket(buffer, length, capture_time_ms, true, false)) return -1; - ModuleRTPUtility::RTPHeaderParser rtp_parser(buffer, length); + RtpUtility::RtpHeaderParser rtp_parser(buffer, length); RTPHeader rtp_header; rtp_parser.Parse(rtp_header); bytes_left -= length - rtp_header.headerLength; @@ -466,44 +539,6 @@ return bytes_to_send - bytes_left; } -bool RTPSender::SendPaddingAccordingToBitrate( - int8_t payload_type, uint32_t capture_timestamp, - int64_t capture_time_ms) { - // Current bitrate since last estimate(1 second) averaged with the - // estimate since then, to get the most up to date bitrate. - uint32_t current_bitrate = bitrate_sent_.BitrateNow(); - int bitrate_diff = target_send_bitrate_ * 1000 - current_bitrate; - if (bitrate_diff <= 0) { - return true; - } - int bytes = 0; - if (current_bitrate == 0) { - // Start up phase. Send one 33.3 ms batch to start with. - bytes = (bitrate_diff / 8) / 30; - } else { - bytes = (bitrate_diff / 8); - // Cap at 200 ms of target send data. - int bytes_cap = target_send_bitrate_ * 25; // 1000 / 8 / 5. - if (bytes > bytes_cap) { - bytes = bytes_cap; - } - } - uint32_t timestamp; - { - CriticalSectionScoped cs(send_critsect_); - // Add the random RTP timestamp offset and store the capture time for - // later calculation of the send time offset. - timestamp = start_time_stamp_ + capture_timestamp; - timestamp_ = timestamp; - capture_time_ms_ = capture_time_ms; - last_timestamp_time_ms_ = clock_->TimeInMilliseconds(); - } - int bytes_sent = SendPadData(payload_type, timestamp, capture_time_ms, - bytes, kDontRetransmit, false, false); - // We did not manage to send all bytes. Comparing with 31 due to modulus 32. - return bytes - bytes_sent < 31; -} - int RTPSender::BuildPaddingPacket(uint8_t* packet, int header_length, int32_t bytes) { int padding_bytes_in_packet = kMaxPaddingLength; @@ -523,64 +558,95 @@ return padding_bytes_in_packet; } -int RTPSender::SendPadData(int payload_type, uint32_t timestamp, - int64_t capture_time_ms, int32_t bytes, - StorageType store, bool force_full_size_packets, - bool only_pad_after_markerbit) { - // Drop this packet if we're not sending media packets. - if (!sending_media_) { - return bytes; +int RTPSender::TrySendPadData(int bytes) { + int64_t capture_time_ms; + uint32_t timestamp; + { + CriticalSectionScoped cs(send_critsect_); + timestamp = timestamp_; + capture_time_ms = capture_time_ms_; + if (last_timestamp_time_ms_ > 0) { + timestamp += + (clock_->TimeInMilliseconds() - last_timestamp_time_ms_) * 90; + capture_time_ms += + (clock_->TimeInMilliseconds() - last_timestamp_time_ms_); + } } + return SendPadData(timestamp, capture_time_ms, bytes); +} + +int RTPSender::SendPadData(uint32_t timestamp, + int64_t capture_time_ms, + int32_t bytes) { int padding_bytes_in_packet = 0; int bytes_sent = 0; for (; bytes > 0; bytes -= padding_bytes_in_packet) { // Always send full padding packets. - if (force_full_size_packets && bytes < kMaxPaddingLength) + if (bytes < kMaxPaddingLength) bytes = kMaxPaddingLength; - if (bytes < kMaxPaddingLength) { - if (force_full_size_packets) { - bytes = kMaxPaddingLength; - } else { - // Round to the nearest multiple of 32. - bytes = (bytes + 16) & 0xffe0; - } - } - if (bytes < 32) { - // Sanity don't send empty packets. - break; - } + uint32_t ssrc; uint16_t sequence_number; + int payload_type; + bool over_rtx; { CriticalSectionScoped cs(send_critsect_); // Only send padding packets following the last packet of a frame, // indicated by the marker bit. - if (only_pad_after_markerbit && !last_packet_marker_bit_) - return bytes_sent; if (rtx_ == kRtxOff) { + // Without RTX we can't send padding in the middle of frames. + if (!last_packet_marker_bit_) + return 0; ssrc = ssrc_; sequence_number = sequence_number_; ++sequence_number_; + payload_type = payload_type_; + over_rtx = false; } else { + // Without abs-send-time a media packet must be sent before padding so + // that the timestamps used for estimation are correct. + if (!media_has_been_sent_ && !rtp_header_extension_map_.IsRegistered( + kRtpExtensionAbsoluteSendTime)) + return 0; ssrc = ssrc_rtx_; sequence_number = sequence_number_rtx_; ++sequence_number_rtx_; + payload_type = ((rtx_ & kRtxRedundantPayloads) > 0) ? payload_type_rtx_ + : payload_type_; + over_rtx = true; } } + uint8_t padding_packet[IP_PACKET_SIZE]; - int header_length = CreateRTPHeader(padding_packet, payload_type, ssrc, - false, timestamp, sequence_number, NULL, + int header_length = CreateRTPHeader(padding_packet, + payload_type, + ssrc, + false, + timestamp, + sequence_number, + NULL, 0); - padding_bytes_in_packet = BuildPaddingPacket(padding_packet, header_length, - bytes); - if (0 > SendToNetwork(padding_packet, padding_bytes_in_packet, - header_length, capture_time_ms, store, - PacedSender::kLowPriority)) { - // Error sending the packet. - break; + padding_bytes_in_packet = + BuildPaddingPacket(padding_packet, header_length, bytes); + int length = padding_bytes_in_packet + header_length; + int64_t now_ms = clock_->TimeInMilliseconds(); + + RtpUtility::RtpHeaderParser rtp_parser(padding_packet, length); + RTPHeader rtp_header; + rtp_parser.Parse(rtp_header); + + if (capture_time_ms > 0) { + UpdateTransmissionTimeOffset( + padding_packet, length, rtp_header, now_ms - capture_time_ms); } + + UpdateAbsoluteSendTime(padding_packet, length, rtp_header, now_ms); + if (!SendPacketToNetwork(padding_packet, length)) + break; bytes_sent += padding_bytes_in_packet; + UpdateRtpStats(padding_packet, length, rtp_header, over_rtx, false); } + return bytes_sent; } @@ -596,7 +662,6 @@ int32_t RTPSender::ReSendPacket(uint16_t packet_id, uint32_t min_resend_time) { uint16_t length = IP_PACKET_SIZE; uint8_t data_buffer[IP_PACKET_SIZE]; - uint8_t *buffer_to_send_ptr = data_buffer; int64_t capture_time_ms; if (!packet_history_.GetPacketAndSetSendTime(packet_id, min_resend_time, true, data_buffer, &length, @@ -605,42 +670,32 @@ return 0; } - ModuleRTPUtility::RTPHeaderParser rtp_parser(data_buffer, length); - RTPHeader header; - if (!rtp_parser.Parse(header)) { - assert(false); - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, - "Failed to parse RTP header of packet to be retransmitted."); - return -1; - } - TRACE_EVENT_INSTANT2("webrtc_rtp", "RTPSender::ReSendPacket", - "timestamp", header.timestamp, - "seqnum", header.sequenceNumber); - if (paced_sender_) { - if (!paced_sender_->SendPacket(PacedSender::kHighPriority, - header.ssrc, - header.sequenceNumber, - capture_time_ms, - length - header.headerLength, - true)) { + RtpUtility::RtpHeaderParser rtp_parser(data_buffer, length); + RTPHeader header; + if (!rtp_parser.Parse(header)) { + assert(false); + return -1; + } + // Convert from TickTime to Clock since capture_time_ms is based on + // TickTime. + int64_t corrected_capture_tims_ms = capture_time_ms + clock_delta_ms_; + if (!paced_sender_->SendPacket( + PacedSender::kHighPriority, header.ssrc, header.sequenceNumber, + corrected_capture_tims_ms, length - header.headerLength, true)) { // We can't send the packet right now. // We will be called when it is time. return length; } } - - uint8_t data_buffer_rtx[IP_PACKET_SIZE]; - if ((rtx_ & kRtxRetransmitted) > 0) { - BuildRtxPacket(data_buffer, &length, data_buffer_rtx); - buffer_to_send_ptr = data_buffer_rtx; - } - - if (SendPacketToNetwork(buffer_to_send_ptr, length)) { - UpdateRtpStats(buffer_to_send_ptr, length, header, rtx_ != kRtxOff, true); - return length; + int rtx = kRtxOff; + { + CriticalSectionScoped lock(send_critsect_); + rtx = rtx_; } - return -1; + return PrepareAndSendPacket(data_buffer, length, capture_time_ms, + (rtx & kRtxRetransmitted) > 0, true) ? + length : -1; } bool RTPSender::SendPacketToNetwork(const uint8_t *packet, uint32_t size) { @@ -650,10 +705,9 @@ } TRACE_EVENT_INSTANT2("webrtc_rtp", "RTPSender::SendPacketToNetwork", "size", size, "sent", bytes_sent); - // TODO(pwesin): Add a separate bitrate for sent bitrate after pacer. + // TODO(pwestin): Add a separate bitrate for sent bitrate after pacer. if (bytes_sent <= 0) { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, id_, - "Transport failed to send packet"); + LOG(LS_WARNING) << "Transport failed to send packet"; return false; } return true; @@ -678,12 +732,12 @@ "num_seqnum", nack_sequence_numbers.size(), "avg_rtt", avg_rtt); const int64_t now = clock_->TimeInMilliseconds(); uint32_t bytes_re_sent = 0; + uint32_t target_bitrate = GetTargetBitrate(); // Enough bandwidth to send NACK? if (!ProcessNACKBitRate(now)) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_, - "NACK bitrate reached. Skip sending NACK response. Target %d", - target_send_bitrate_); + LOG(LS_INFO) << "NACK bitrate reached. Skip sending NACK response. Target " + << target_bitrate; return; } @@ -698,16 +752,15 @@ continue; } else if (bytes_sent < 0) { // Failed to send one Sequence number. Give up the rest in this nack. - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, id_, - "Failed resending RTP packet %d, Discard rest of packets", - *it); + LOG(LS_WARNING) << "Failed resending RTP packet " << *it + << ", Discard rest of packets"; break; } // Delay bandwidth estimate (RTT * BW). - if (target_send_bitrate_ != 0 && avg_rtt) { + if (target_bitrate != 0 && avg_rtt) { // kbits/s * ms = bits => bits/8 = bytes uint32_t target_bytes = - (static_cast(target_send_bitrate_) * avg_rtt) >> 3; + (static_cast(target_bitrate / 1000) * avg_rtt) >> 3; if (bytes_re_sent > target_bytes) { break; // Ignore the rest of the packets in the list. } @@ -722,32 +775,33 @@ bool RTPSender::ProcessNACKBitRate(const uint32_t now) { uint32_t num = 0; - int32_t byte_count = 0; - const uint32_t avg_interval = 1000; + int byte_count = 0; + const uint32_t kAvgIntervalMs = 1000; + uint32_t target_bitrate = GetTargetBitrate(); CriticalSectionScoped cs(send_critsect_); - if (target_send_bitrate_ == 0) { + if (target_bitrate == 0) { return true; } for (num = 0; num < NACK_BYTECOUNT_SIZE; ++num) { - if ((now - nack_byte_count_times_[num]) > avg_interval) { + if ((now - nack_byte_count_times_[num]) > kAvgIntervalMs) { // Don't use data older than 1sec. break; } else { byte_count += nack_byte_count_[num]; } } - int32_t time_interval = avg_interval; + uint32_t time_interval = kAvgIntervalMs; if (num == NACK_BYTECOUNT_SIZE) { // More than NACK_BYTECOUNT_SIZE nack messages has been received // during the last msg_interval. - time_interval = now - nack_byte_count_times_[num - 1]; - if (time_interval < 0) { - time_interval = avg_interval; + if (nack_byte_count_times_[num - 1] <= now) { + time_interval = now - nack_byte_count_times_[num - 1]; } } - return (byte_count * 8) < (target_send_bitrate_ * time_interval); + return (byte_count * 8) < + static_cast(target_bitrate / 1000 * time_interval); } void RTPSender::UpdateNACKBitRate(const uint32_t bytes, @@ -795,20 +849,33 @@ if (!retransmission && capture_time_ms > 0) { UpdateDelayStatistics(capture_time_ms, clock_->TimeInMilliseconds()); } - return PrepareAndSendPacket(data_buffer, length, capture_time_ms, - retransmission && (rtx_ & kRtxRetransmitted) > 0); + int rtx; + { + CriticalSectionScoped lock(send_critsect_); + rtx = rtx_; + } + return PrepareAndSendPacket(data_buffer, + length, + capture_time_ms, + retransmission && (rtx & kRtxRetransmitted) > 0, + retransmission); } bool RTPSender::PrepareAndSendPacket(uint8_t* buffer, uint16_t length, int64_t capture_time_ms, - bool send_over_rtx) { + bool send_over_rtx, + bool is_retransmit) { uint8_t *buffer_to_send_ptr = buffer; - ModuleRTPUtility::RTPHeaderParser rtp_parser(buffer, length); + RtpUtility::RtpHeaderParser rtp_parser(buffer, length); RTPHeader rtp_header; rtp_parser.Parse(rtp_header); - TRACE_EVENT_INSTANT2("webrtc_rtp", "RTPSender::TimeToSendPacket", + if (!is_retransmit && rtp_header.markerBit) { + TRACE_EVENT_ASYNC_END0("webrtc_rtp", "PacedSend", capture_time_ms); + } + + TRACE_EVENT_INSTANT2("webrtc_rtp", "PrepareAndSendPacket", "timestamp", rtp_header.timestamp, "seqnum", rtp_header.sequenceNumber); @@ -820,20 +887,16 @@ int64_t now_ms = clock_->TimeInMilliseconds(); int64_t diff_ms = now_ms - capture_time_ms; - bool updated_transmission_time_offset = - UpdateTransmissionTimeOffset(buffer_to_send_ptr, length, rtp_header, - diff_ms); - bool updated_abs_send_time = - UpdateAbsoluteSendTime(buffer_to_send_ptr, length, rtp_header, now_ms); - if (updated_transmission_time_offset || updated_abs_send_time) { - // Update stored packet in case of receiving a re-transmission request. - packet_history_.ReplaceRTPHeader(buffer_to_send_ptr, - rtp_header.sequenceNumber, - rtp_header.headerLength); - } - + UpdateTransmissionTimeOffset(buffer_to_send_ptr, length, rtp_header, + diff_ms); + UpdateAbsoluteSendTime(buffer_to_send_ptr, length, rtp_header, now_ms); bool ret = SendPacketToNetwork(buffer_to_send_ptr, length); - UpdateRtpStats(buffer_to_send_ptr, length, rtp_header, false, false); + if (ret) { + CriticalSectionScoped lock(send_critsect_); + media_has_been_sent_ = true; + } + UpdateRtpStats(buffer_to_send_ptr, length, rtp_header, send_over_rtx, + is_retransmit); return ret; } @@ -844,17 +907,16 @@ bool is_retransmit) { StreamDataCounters* counters; // Get ssrc before taking statistics_crit_ to avoid possible deadlock. - uint32_t ssrc = SSRC(); + uint32_t ssrc = is_rtx ? RtxSsrc() : SSRC(); CriticalSectionScoped lock(statistics_crit_.get()); if (is_rtx) { counters = &rtx_rtp_stats_; - ssrc = ssrc_rtx_; } else { counters = &rtp_stats_; } - bitrate_sent_.Update(size); + total_bitrate_sent_.Update(size); ++counters->packets; if (IsFecPacket(buffer, header)) { ++counters->fec_packets; @@ -888,42 +950,25 @@ } int RTPSender::TimeToSendPadding(int bytes) { - if (!sending_media_) { - return 0; - } - int payload_type; - int64_t capture_time_ms; - uint32_t timestamp; { CriticalSectionScoped cs(send_critsect_); - payload_type = ((rtx_ & kRtxRedundantPayloads) > 0) ? payload_type_rtx_ : - payload_type_; - timestamp = timestamp_; - capture_time_ms = capture_time_ms_; - if (last_timestamp_time_ms_ > 0) { - timestamp += - (clock_->TimeInMilliseconds() - last_timestamp_time_ms_) * 90; - capture_time_ms += - (clock_->TimeInMilliseconds() - last_timestamp_time_ms_); - } - } - int bytes_sent = SendRedundantPayloads(payload_type, bytes); - bytes -= bytes_sent; - if (bytes > 0) { - int padding_sent = SendPadData(payload_type, timestamp, capture_time_ms, - bytes, kDontStore, true, true); - bytes_sent += padding_sent; + if (!sending_media_) return 0; } - return bytes_sent; + int available_bytes = bytes; + if (available_bytes > 0) + available_bytes -= TrySendRedundantPayloads(available_bytes); + if (available_bytes > 0) + available_bytes -= TrySendPadData(available_bytes); + return bytes - available_bytes; } -// TODO(pwestin): send in the RTPHeaderParser to avoid parsing it again. +// TODO(pwestin): send in the RtpHeaderParser to avoid parsing it again. int32_t RTPSender::SendToNetwork( uint8_t *buffer, int payload_length, int rtp_header_length, int64_t capture_time_ms, StorageType storage, PacedSender::Priority priority) { - ModuleRTPUtility::RTPHeaderParser rtp_parser( - buffer, payload_length + rtp_header_length); + RtpUtility::RtpHeaderParser rtp_parser(buffer, + payload_length + rtp_header_length); RTPHeader rtp_header; rtp_parser.Parse(rtp_header); @@ -948,9 +993,18 @@ } if (paced_sender_ && storage != kDontStore) { + // Correct offset between implementations of millisecond time stamps in + // TickTime and Clock. + int64_t corrected_time_ms = capture_time_ms + clock_delta_ms_; if (!paced_sender_->SendPacket(priority, rtp_header.ssrc, - rtp_header.sequenceNumber, capture_time_ms, + rtp_header.sequenceNumber, corrected_time_ms, payload_length, false)) { + if (last_capture_time_ms_sent_ == 0 || + corrected_time_ms > last_capture_time_ms_sent_) { + last_capture_time_ms_sent_ = corrected_time_ms; + TRACE_EVENT_ASYNC_BEGIN1("webrtc_rtp", "PacedSend", corrected_time_ms, + "capture_time_ms", corrected_time_ms); + } // We can't send the packet right now. // We will be called when it is time. return 0; @@ -962,20 +1016,40 @@ uint32_t length = payload_length + rtp_header_length; if (!SendPacketToNetwork(buffer, length)) return -1; + { + CriticalSectionScoped lock(send_critsect_); + media_has_been_sent_ = true; + } UpdateRtpStats(buffer, length, rtp_header, false, false); return 0; } void RTPSender::UpdateDelayStatistics(int64_t capture_time_ms, int64_t now_ms) { - CriticalSectionScoped cs(statistics_crit_.get()); - send_delays_[now_ms] = now_ms - capture_time_ms; - send_delays_.erase(send_delays_.begin(), - send_delays_.lower_bound(now_ms - kSendSideDelayWindowMs)); + uint32_t ssrc; + int avg_delay_ms = 0; + int max_delay_ms = 0; + { + CriticalSectionScoped lock(send_critsect_); + ssrc = ssrc_; + } + { + CriticalSectionScoped cs(statistics_crit_.get()); + // TODO(holmer): Compute this iteratively instead. + send_delays_[now_ms] = now_ms - capture_time_ms; + send_delays_.erase(send_delays_.begin(), + send_delays_.lower_bound(now_ms - + kSendSideDelayWindowMs)); + } + if (send_side_delay_observer_ && + GetSendSideDelay(&avg_delay_ms, &max_delay_ms)) { + send_side_delay_observer_->SendSideDelayUpdated(avg_delay_ms, + max_delay_ms, ssrc); + } } void RTPSender::ProcessBitrate() { CriticalSectionScoped cs(send_critsect_); - bitrate_sent_.Process(); + total_bitrate_sent_.Process(); nack_bitrate_.Process(); if (audio_configured_) { return; @@ -984,6 +1058,7 @@ } uint16_t RTPSender::RTPHeaderLength() const { + CriticalSectionScoped lock(send_critsect_); uint16_t rtp_header_length = 12; if (include_csrcs_) { rtp_header_length += sizeof(uint32_t) * num_csrcs_; @@ -998,24 +1073,27 @@ } void RTPSender::ResetDataCounters() { + uint32_t ssrc; + uint32_t ssrc_rtx; + { + CriticalSectionScoped ssrc_lock(send_critsect_); + ssrc = ssrc_; + ssrc_rtx = ssrc_rtx_; + } CriticalSectionScoped lock(statistics_crit_.get()); rtp_stats_ = StreamDataCounters(); rtx_rtp_stats_ = StreamDataCounters(); if (rtp_stats_callback_) { - rtp_stats_callback_->DataCountersUpdated(rtp_stats_, ssrc_); - rtp_stats_callback_->DataCountersUpdated(rtx_rtp_stats_, ssrc_rtx_); + rtp_stats_callback_->DataCountersUpdated(rtp_stats_, ssrc); + rtp_stats_callback_->DataCountersUpdated(rtx_rtp_stats_, ssrc_rtx); } } -uint32_t RTPSender::Packets() const { - CriticalSectionScoped lock(statistics_crit_.get()); - return rtp_stats_.packets + rtx_rtp_stats_.packets; -} - -// Number of sent RTP bytes. -uint32_t RTPSender::Bytes() const { +void RTPSender::GetDataCounters(StreamDataCounters* rtp_stats, + StreamDataCounters* rtx_stats) const { CriticalSectionScoped lock(statistics_crit_.get()); - return rtp_stats_.bytes + rtx_rtp_stats_.bytes; + *rtp_stats = rtp_stats_; + *rtx_stats = rtx_rtp_stats_; } int RTPSender::CreateRTPHeader( @@ -1027,9 +1105,9 @@ if (marker_bit) { header[1] |= kRtpMarkerBitMask; // Marker bit is set. } - ModuleRTPUtility::AssignUWord16ToBuffer(header + 2, sequence_number); - ModuleRTPUtility::AssignUWord32ToBuffer(header + 4, timestamp); - ModuleRTPUtility::AssignUWord32ToBuffer(header + 8, ssrc); + RtpUtility::AssignUWord16ToBuffer(header + 2, sequence_number); + RtpUtility::AssignUWord32ToBuffer(header + 4, timestamp); + RtpUtility::AssignUWord32ToBuffer(header + 8, ssrc); int32_t rtp_header_length = 12; // Add the CSRCs if any. @@ -1041,7 +1119,7 @@ } uint8_t *ptr = &header[rtp_header_length]; for (int i = 0; i < num_csrcs; ++i) { - ModuleRTPUtility::AssignUWord32ToBuffer(ptr, csrcs[i]); + RtpUtility::AssignUWord32ToBuffer(ptr, csrcs[i]); ptr += 4; } header[0] = (header[0] & 0xf0) | num_csrcs; @@ -1058,16 +1136,18 @@ return rtp_header_length; } -int32_t RTPSender::BuildRTPheader( - uint8_t *data_buffer, const int8_t payload_type, - const bool marker_bit, const uint32_t capture_timestamp, - int64_t capture_time_ms, const bool time_stamp_provided, - const bool inc_sequence_number) { +int32_t RTPSender::BuildRTPheader(uint8_t* data_buffer, + const int8_t payload_type, + const bool marker_bit, + const uint32_t capture_timestamp, + int64_t capture_time_ms, + const bool timestamp_provided, + const bool inc_sequence_number) { assert(payload_type >= 0); CriticalSectionScoped cs(send_critsect_); - if (time_stamp_provided) { - timestamp_ = start_time_stamp_ + capture_timestamp; + if (timestamp_provided) { + timestamp_ = start_timestamp_ + capture_timestamp; } else { // Make a unique time stamp. // We can't inc by the actual time, since then we increase the risk of back @@ -1102,8 +1182,7 @@ const uint32_t kHeaderLength = kRtpOneByteHeaderLength; // Add extension ID (0xBEDE). - ModuleRTPUtility::AssignUWord16ToBuffer(data_buffer, - kRtpOneByteHeaderExtensionId); + RtpUtility::AssignUWord16ToBuffer(data_buffer, kRtpOneByteHeaderExtensionId); // Add extensions. uint16_t total_block_length = 0; @@ -1117,9 +1196,8 @@ data_buffer + kHeaderLength + total_block_length); break; case kRtpExtensionAudioLevel: - // Because AudioLevel is handled specially by RTPSenderAudio, we pretend - // we don't have to care about it here, which is true until we wan't to - // use it together with any of the other extensions we support. + block_length = BuildAudioLevelExtension( + data_buffer + kHeaderLength + total_block_length); break; case kRtpExtensionAbsoluteSendTime: block_length = BuildAbsoluteSendTimeExtension( @@ -1137,8 +1215,8 @@ } // Set header length (in number of Word32, header excluded). assert(total_block_length % 4 == 0); - ModuleRTPUtility::AssignUWord16ToBuffer(data_buffer + kPosLength, - total_block_length / 4); + RtpUtility::AssignUWord16ToBuffer(data_buffer + kPosLength, + total_block_length / 4); // Total added length. return kHeaderLength + total_block_length; } @@ -1172,15 +1250,49 @@ size_t pos = 0; const uint8_t len = 2; data_buffer[pos++] = (id << 4) + len; - ModuleRTPUtility::AssignUWord24ToBuffer(data_buffer + pos, - transmission_time_offset_); + RtpUtility::AssignUWord24ToBuffer(data_buffer + pos, + transmission_time_offset_); pos += 3; assert(pos == kTransmissionTimeOffsetLength); return kTransmissionTimeOffsetLength; } -uint8_t RTPSender::BuildAbsoluteSendTimeExtension( - uint8_t* data_buffer) const { +uint8_t RTPSender::BuildAudioLevelExtension(uint8_t* data_buffer) const { + // An RTP Header Extension for Client-to-Mixer Audio Level Indication + // + // https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/ + // + // The form of the audio level extension block: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | ID | len=0 |V| level | 0x00 | 0x00 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // Note that we always include 2 pad bytes, which will result in legal and + // correctly parsed RTP, but may be a bit wasteful if more short extensions + // are implemented. Right now the pad bytes would anyway be required at end + // of the extension block, so it makes no difference. + + // Get id defined by user. + uint8_t id; + if (rtp_header_extension_map_.GetId(kRtpExtensionAudioLevel, &id) != 0) { + // Not registered. + return 0; + } + size_t pos = 0; + const uint8_t len = 0; + data_buffer[pos++] = (id << 4) + len; + data_buffer[pos++] = (1 << 7) + 0; // Voice, 0 dBov. + data_buffer[pos++] = 0; // Padding. + data_buffer[pos++] = 0; // Padding. + // kAudioLevelLength is including pad bytes. + assert(pos == kAudioLevelLength); + return kAudioLevelLength; +} + +uint8_t RTPSender::BuildAbsoluteSendTimeExtension(uint8_t* data_buffer) const { // Absolute send time in RTP streams. // // The absolute send time is signaled to the receiver in-band using the @@ -1207,127 +1319,160 @@ size_t pos = 0; const uint8_t len = 2; data_buffer[pos++] = (id << 4) + len; - ModuleRTPUtility::AssignUWord24ToBuffer(data_buffer + pos, - absolute_send_time_); + RtpUtility::AssignUWord24ToBuffer(data_buffer + pos, absolute_send_time_); pos += 3; assert(pos == kAbsoluteSendTimeLength); return kAbsoluteSendTimeLength; } -bool RTPSender::UpdateTransmissionTimeOffset( +void RTPSender::UpdateTransmissionTimeOffset( uint8_t *rtp_packet, const uint16_t rtp_packet_length, const RTPHeader &rtp_header, const int64_t time_diff_ms) const { CriticalSectionScoped cs(send_critsect_); - + // Get id. + uint8_t id = 0; + if (rtp_header_extension_map_.GetId(kRtpExtensionTransmissionTimeOffset, + &id) != 0) { + // Not registered. + return; + } // Get length until start of header extension block. int extension_block_pos = rtp_header_extension_map_.GetLengthUntilBlockStartInBytes( kRtpExtensionTransmissionTimeOffset); if (extension_block_pos < 0) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_, - "Failed to update transmission time offset, not registered."); - return false; + LOG(LS_WARNING) + << "Failed to update transmission time offset, not registered."; + return; } int block_pos = 12 + rtp_header.numCSRCs + extension_block_pos; if (rtp_packet_length < block_pos + kTransmissionTimeOffsetLength || rtp_header.headerLength < block_pos + kTransmissionTimeOffsetLength) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_, - "Failed to update transmission time offset, invalid length."); - return false; + LOG(LS_WARNING) + << "Failed to update transmission time offset, invalid length."; + return; } // Verify that header contains extension. if (!((rtp_packet[12 + rtp_header.numCSRCs] == 0xBE) && (rtp_packet[12 + rtp_header.numCSRCs + 1] == 0xDE))) { - WEBRTC_TRACE( - kTraceStream, kTraceRtpRtcp, id_, - "Failed to update transmission time offset, hdr extension not found."); - return false; + LOG(LS_WARNING) << "Failed to update transmission time offset, hdr " + "extension not found."; + return; } + // Verify first byte in block. + const uint8_t first_block_byte = (id << 4) + 2; + if (rtp_packet[block_pos] != first_block_byte) { + LOG(LS_WARNING) << "Failed to update transmission time offset."; + return; + } + // Update transmission offset field (converting to a 90 kHz timestamp). + RtpUtility::AssignUWord24ToBuffer(rtp_packet + block_pos + 1, + time_diff_ms * 90); // RTP timestamp. +} + +bool RTPSender::UpdateAudioLevel(uint8_t *rtp_packet, + const uint16_t rtp_packet_length, + const RTPHeader &rtp_header, + const bool is_voiced, + const uint8_t dBov) const { + CriticalSectionScoped cs(send_critsect_); + // Get id. uint8_t id = 0; - if (rtp_header_extension_map_.GetId(kRtpExtensionTransmissionTimeOffset, - &id) != 0) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_, - "Failed to update transmission time offset, no id."); + if (rtp_header_extension_map_.GetId(kRtpExtensionAudioLevel, &id) != 0) { + // Not registered. + return false; + } + // Get length until start of header extension block. + int extension_block_pos = + rtp_header_extension_map_.GetLengthUntilBlockStartInBytes( + kRtpExtensionAudioLevel); + if (extension_block_pos < 0) { + // The feature is not enabled. + return false; + } + int block_pos = 12 + rtp_header.numCSRCs + extension_block_pos; + if (rtp_packet_length < block_pos + kAudioLevelLength || + rtp_header.headerLength < block_pos + kAudioLevelLength) { + LOG(LS_WARNING) << "Failed to update audio level, invalid length."; + return false; + } + // Verify that header contains extension. + if (!((rtp_packet[12 + rtp_header.numCSRCs] == 0xBE) && + (rtp_packet[12 + rtp_header.numCSRCs + 1] == 0xDE))) { + LOG(LS_WARNING) << "Failed to update audio level, hdr extension not found."; return false; } // Verify first byte in block. - const uint8_t first_block_byte = (id << 4) + 2; + const uint8_t first_block_byte = (id << 4) + 0; if (rtp_packet[block_pos] != first_block_byte) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_, - "Failed to update transmission time offset."); + LOG(LS_WARNING) << "Failed to update audio level."; return false; } - // Update transmission offset field (converting to a 90 kHz timestamp). - ModuleRTPUtility::AssignUWord24ToBuffer(rtp_packet + block_pos + 1, - time_diff_ms * 90); // RTP timestamp. + rtp_packet[block_pos + 1] = (is_voiced ? 0x80 : 0x00) + (dBov & 0x7f); return true; } -bool RTPSender::UpdateAbsoluteSendTime( +void RTPSender::UpdateAbsoluteSendTime( uint8_t *rtp_packet, const uint16_t rtp_packet_length, const RTPHeader &rtp_header, const int64_t now_ms) const { CriticalSectionScoped cs(send_critsect_); + // Get id. + uint8_t id = 0; + if (rtp_header_extension_map_.GetId(kRtpExtensionAbsoluteSendTime, + &id) != 0) { + // Not registered. + return; + } // Get length until start of header extension block. int extension_block_pos = rtp_header_extension_map_.GetLengthUntilBlockStartInBytes( kRtpExtensionAbsoluteSendTime); if (extension_block_pos < 0) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_, - "Failed to update absolute send time, not registered."); - return false; + // The feature is not enabled. + return; } int block_pos = 12 + rtp_header.numCSRCs + extension_block_pos; if (rtp_packet_length < block_pos + kAbsoluteSendTimeLength || rtp_header.headerLength < block_pos + kAbsoluteSendTimeLength) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_, - "Failed to update absolute send time, invalid length."); - return false; + LOG(LS_WARNING) << "Failed to update absolute send time, invalid length."; + return; } // Verify that header contains extension. if (!((rtp_packet[12 + rtp_header.numCSRCs] == 0xBE) && (rtp_packet[12 + rtp_header.numCSRCs + 1] == 0xDE))) { - WEBRTC_TRACE( - kTraceStream, kTraceRtpRtcp, id_, - "Failed to update absolute send time, hdr extension not found."); - return false; - } - // Get id. - uint8_t id = 0; - if (rtp_header_extension_map_.GetId(kRtpExtensionAbsoluteSendTime, - &id) != 0) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_, - "Failed to update absolute send time, no id."); - return false; + LOG(LS_WARNING) + << "Failed to update absolute send time, hdr extension not found."; + return; } // Verify first byte in block. const uint8_t first_block_byte = (id << 4) + 2; if (rtp_packet[block_pos] != first_block_byte) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_, - "Failed to update absolute send time."); - return false; + LOG(LS_WARNING) << "Failed to update absolute send time."; + return; } // Update absolute send time field (convert ms to 24-bit unsigned with 18 bit // fractional part). - ModuleRTPUtility::AssignUWord24ToBuffer(rtp_packet + block_pos + 1, - ((now_ms << 18) / 1000) & 0x00ffffff); - return true; + RtpUtility::AssignUWord24ToBuffer(rtp_packet + block_pos + 1, + ((now_ms << 18) / 1000) & 0x00ffffff); } void RTPSender::SetSendingStatus(bool enabled) { if (enabled) { uint32_t frequency_hz = SendPayloadFrequency(); - uint32_t RTPtime = ModuleRTPUtility::GetCurrentRTP(clock_, frequency_hz); + uint32_t RTPtime = RtpUtility::GetCurrentRTP(clock_, frequency_hz); // Will be ignored if it's already configured via API. SetStartTimestamp(RTPtime, false); } else { + CriticalSectionScoped lock(send_critsect_); if (!ssrc_forced_) { // Generate a new SSRC. ssrc_db_.ReturnSSRC(ssrc_); ssrc_ = ssrc_db_.CreateSSRC(); // Can't be 0. + bitrates_->set_ssrc(ssrc_); } // Don't initialize seq number if SSRC passed externally. if (!sequence_number_forced_ && !ssrc_forced_) { @@ -1356,18 +1501,18 @@ void RTPSender::SetStartTimestamp(uint32_t timestamp, bool force) { CriticalSectionScoped cs(send_critsect_); if (force) { - start_time_stamp_forced_ = force; - start_time_stamp_ = timestamp; + start_timestamp_forced_ = true; + start_timestamp_ = timestamp; } else { - if (!start_time_stamp_forced_) { - start_time_stamp_ = timestamp; + if (!start_timestamp_forced_) { + start_timestamp_ = timestamp; } } } uint32_t RTPSender::StartTimestamp() const { CriticalSectionScoped cs(send_critsect_); - return start_time_stamp_; + return start_timestamp_; } uint32_t RTPSender::GenerateNewSSRC() { @@ -1378,6 +1523,7 @@ return 0; } ssrc_ = ssrc_db_.CreateSSRC(); // Can't be 0. + bitrates_->set_ssrc(ssrc_); return ssrc_; } @@ -1392,6 +1538,7 @@ ssrc_db_.ReturnSSRC(ssrc_); ssrc_db_.RegisterSSRC(ssrc); ssrc_ = ssrc; + bitrates_->set_ssrc(ssrc_); if (!sequence_number_forced_) { sequence_number_ = rand() / (RAND_MAX / MAX_INIT_RTP_SEQ_NUMBER); // NOLINT @@ -1404,6 +1551,7 @@ } void RTPSender::SetCSRCStatus(const bool include) { + CriticalSectionScoped lock(send_critsect_); include_csrcs_ = include; } @@ -1463,19 +1611,6 @@ return audio_->SetAudioPacketSize(packet_size_samples); } -int32_t RTPSender::SetAudioLevelIndicationStatus(const bool enable, - const uint8_t ID) { - if (!audio_configured_) { - return -1; - } - return audio_->SetAudioLevelIndicationStatus(enable, ID); -} - -int32_t RTPSender::AudioLevelIndicationStatus(bool *enable, - uint8_t* id) const { - return audio_->AudioLevelIndicationStatus(*enable, *id); -} - int32_t RTPSender::SetAudioLevel(const uint8_t level_d_bov) { return audio_->SetAudioLevel(level_d_bov); } @@ -1555,8 +1690,8 @@ CriticalSectionScoped cs(send_critsect_); uint8_t* data_buffer_rtx = buffer_rtx; // Add RTX header. - ModuleRTPUtility::RTPHeaderParser rtp_parser( - reinterpret_cast(buffer), *length); + RtpUtility::RtpHeaderParser rtp_parser( + reinterpret_cast(buffer), *length); RTPHeader rtp_header; rtp_parser.Parse(rtp_header); @@ -1573,15 +1708,15 @@ // Replace sequence number. uint8_t *ptr = data_buffer_rtx + 2; - ModuleRTPUtility::AssignUWord16ToBuffer(ptr, sequence_number_rtx_++); + RtpUtility::AssignUWord16ToBuffer(ptr, sequence_number_rtx_++); // Replace SSRC. ptr += 6; - ModuleRTPUtility::AssignUWord32ToBuffer(ptr, ssrc_rtx_); + RtpUtility::AssignUWord32ToBuffer(ptr, ssrc_rtx_); // Add OSN (original sequence number). ptr = data_buffer_rtx + rtp_header.headerLength; - ModuleRTPUtility::AssignUWord16ToBuffer(ptr, rtp_header.sequenceNumber); + RtpUtility::AssignUWord16ToBuffer(ptr, rtp_header.sequenceNumber); ptr += 2; // Add original payload data. @@ -1590,23 +1725,9 @@ *length += 2; } -void RTPSender::RegisterFrameCountObserver(FrameCountObserver* observer) { - CriticalSectionScoped cs(statistics_crit_.get()); - if (observer != NULL) - assert(frame_count_observer_ == NULL); - frame_count_observer_ = observer; -} - -FrameCountObserver* RTPSender::GetFrameCountObserver() const { - CriticalSectionScoped cs(statistics_crit_.get()); - return frame_count_observer_; -} - void RTPSender::RegisterRtpStatisticsCallback( StreamDataCountersCallback* callback) { CriticalSectionScoped cs(statistics_crit_.get()); - if (callback != NULL) - assert(rtp_stats_callback_ == NULL); rtp_stats_callback_ = callback; } @@ -1615,24 +1736,48 @@ return rtp_stats_callback_; } -void RTPSender::RegisterBitrateObserver(BitrateStatisticsObserver* observer) { - CriticalSectionScoped cs(statistics_crit_.get()); - if (observer != NULL) - assert(bitrate_callback_ == NULL); - bitrate_callback_ = observer; +uint32_t RTPSender::BitrateSent() const { + return total_bitrate_sent_.BitrateLast(); } -BitrateStatisticsObserver* RTPSender::GetBitrateObserver() const { - CriticalSectionScoped cs(statistics_crit_.get()); - return bitrate_callback_; +void RTPSender::SetRtpState(const RtpState& rtp_state) { + SetStartTimestamp(rtp_state.start_timestamp, true); + CriticalSectionScoped lock(send_critsect_); + sequence_number_ = rtp_state.sequence_number; + sequence_number_forced_ = true; + timestamp_ = rtp_state.timestamp; + capture_time_ms_ = rtp_state.capture_time_ms; + last_timestamp_time_ms_ = rtp_state.last_timestamp_time_ms; + media_has_been_sent_ = rtp_state.media_has_been_sent; } -uint32_t RTPSender::BitrateSent() const { return bitrate_sent_.BitrateLast(); } +RtpState RTPSender::GetRtpState() const { + CriticalSectionScoped lock(send_critsect_); -void RTPSender::BitrateUpdated(const BitrateStatistics& stats) { - CriticalSectionScoped cs(statistics_crit_.get()); - if (bitrate_callback_) { - bitrate_callback_->Notify(stats, ssrc_); - } + RtpState state; + state.sequence_number = sequence_number_; + state.start_timestamp = start_timestamp_; + state.timestamp = timestamp_; + state.capture_time_ms = capture_time_ms_; + state.last_timestamp_time_ms = last_timestamp_time_ms_; + state.media_has_been_sent = media_has_been_sent_; + + return state; +} + +void RTPSender::SetRtxRtpState(const RtpState& rtp_state) { + CriticalSectionScoped lock(send_critsect_); + sequence_number_rtx_ = rtp_state.sequence_number; } + +RtpState RTPSender::GetRtxRtpState() const { + CriticalSectionScoped lock(send_critsect_); + + RtpState state; + state.sequence_number = sequence_number_rtx_; + state.start_timestamp = start_timestamp_; + + return state; +} + } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender.h 2015-02-03 14:33:36.000000000 +0000 @@ -16,6 +16,7 @@ #include +#include "webrtc/base/thread_annotations.h" #include "webrtc/common_types.h" #include "webrtc/modules/pacing/include/paced_sender.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" @@ -30,6 +31,7 @@ namespace webrtc { +class BitrateAggregator; class CriticalSectionWrapper; class RTPSenderAudio; class RTPSenderVideo; @@ -42,12 +44,13 @@ virtual uint32_t SSRC() const = 0; virtual uint32_t Timestamp() const = 0; - virtual int32_t BuildRTPheader( - uint8_t *data_buffer, const int8_t payload_type, - const bool marker_bit, const uint32_t capture_time_stamp, - int64_t capture_time_ms, - const bool time_stamp_provided = true, - const bool inc_sequence_number = true) = 0; + virtual int32_t BuildRTPheader(uint8_t* data_buffer, + const int8_t payload_type, + const bool marker_bit, + const uint32_t capture_timestamp, + int64_t capture_time_ms, + const bool timestamp_provided = true, + const bool inc_sequence_number = true) = 0; virtual uint16_t RTPHeaderLength() const = 0; virtual uint16_t IncrementSequenceNumber() = 0; @@ -63,11 +66,14 @@ PacedSender::Priority priority) = 0; }; -class RTPSender : public RTPSenderInterface, public Bitrate::Observer { +class RTPSender : public RTPSenderInterface { public: RTPSender(const int32_t id, const bool audio, Clock *clock, Transport *transport, RtpAudioFeedback *audio_feedback, - PacedSender *paced_sender); + PacedSender *paced_sender, + BitrateStatisticsObserver* bitrate_callback, + FrameCountObserver* frame_count_observer, + SendSideDelayObserver* send_side_delay_observer); virtual ~RTPSender(); void ProcessBitrate(); @@ -82,7 +88,8 @@ // was sent within the statistics window. bool GetSendSideDelay(int* avg_send_delay_ms, int* max_send_delay_ms) const; - void SetTargetSendBitrate(const uint32_t bits); + void SetTargetBitrate(uint32_t bitrate); + uint32_t GetTargetBitrate(); virtual uint16_t MaxDataPayloadLength() const OVERRIDE; // with RTP and FEC headers. @@ -94,6 +101,8 @@ int32_t DeRegisterSendPayload(const int8_t payload_type); + void SetSendPayloadType(int8_t payload_type); + int8_t SendPayloadType() const; int SendPayloadFrequency() const; @@ -103,11 +112,8 @@ void SetSendingMediaStatus(const bool enabled); bool SendingMedia() const; - // Number of sent RTP packets. - uint32_t Packets() const; - - // Number of sent RTP bytes. - uint32_t Bytes() const; + void GetDataCounters(StreamDataCounters* rtp_stats, + StreamDataCounters* rtx_stats) const; void ResetDataCounters(); @@ -130,13 +136,15 @@ int32_t SetMaxPayloadLength(const uint16_t length, const uint16_t packet_over_head); - int32_t SendOutgoingData( - const FrameType frame_type, const int8_t payload_type, - const uint32_t time_stamp, int64_t capture_time_ms, - const uint8_t *payload_data, const uint32_t payload_size, - const RTPFragmentationHeader *fragmentation, - VideoCodecInformation *codec_info = NULL, - const RTPVideoTypeHeader * rtp_type_hdr = NULL); + int32_t SendOutgoingData(const FrameType frame_type, + const int8_t payload_type, + const uint32_t timestamp, + int64_t capture_time_ms, + const uint8_t* payload_data, + const uint32_t payload_size, + const RTPFragmentationHeader* fragmentation, + VideoCodecInformation* codec_info = NULL, + const RTPVideoTypeHeader* rtp_type_hdr = NULL); // RTP header extension int32_t SetTransmissionTimeOffset( @@ -153,19 +161,15 @@ uint16_t BuildRTPHeaderExtension(uint8_t* data_buffer) const; - uint8_t BuildTransmissionTimeOffsetExtension( - uint8_t *data_buffer) const; - uint8_t BuildAbsoluteSendTimeExtension( - uint8_t* data_buffer) const; - - bool UpdateTransmissionTimeOffset(uint8_t *rtp_packet, - const uint16_t rtp_packet_length, - const RTPHeader &rtp_header, - const int64_t time_diff_ms) const; - bool UpdateAbsoluteSendTime(uint8_t *rtp_packet, - const uint16_t rtp_packet_length, - const RTPHeader &rtp_header, - const int64_t now_ms) const; + uint8_t BuildTransmissionTimeOffsetExtension(uint8_t *data_buffer) const; + uint8_t BuildAudioLevelExtension(uint8_t* data_buffer) const; + uint8_t BuildAbsoluteSendTimeExtension(uint8_t* data_buffer) const; + + bool UpdateAudioLevel(uint8_t *rtp_packet, + const uint16_t rtp_packet_length, + const RTPHeader &rtp_header, + const bool is_voiced, + const uint8_t dBov) const; bool TimeToSendPacket(uint16_t sequence_number, int64_t capture_time_ms, bool retransmission); @@ -187,18 +191,23 @@ bool ProcessNACKBitRate(const uint32_t now); // RTX. - void SetRTXStatus(int mode, bool set_ssrc, uint32_t ssrc); + void SetRTXStatus(int mode); void RTXStatus(int* mode, uint32_t* ssrc, int* payload_type) const; + uint32_t RtxSsrc() const; + void SetRtxSsrc(uint32_t ssrc); + void SetRtxPayloadType(int payloadType); // Functions wrapping RTPSenderInterface. virtual int32_t BuildRTPheader( - uint8_t *data_buffer, const int8_t payload_type, - const bool marker_bit, const uint32_t capture_time_stamp, + uint8_t* data_buffer, + const int8_t payload_type, + const bool marker_bit, + const uint32_t capture_timestamp, int64_t capture_time_ms, - const bool time_stamp_provided = true, + const bool timestamp_provided = true, const bool inc_sequence_number = true) OVERRIDE; virtual uint16_t RTPHeaderLength() const OVERRIDE; @@ -228,12 +237,6 @@ // packet in silence (CNG). int32_t SetAudioPacketSize(const uint16_t packet_size_samples); - // Set status and ID for header-extension-for-audio-level-indication. - int32_t SetAudioLevelIndicationStatus(const bool enable, const uint8_t ID); - - // Get status and ID for header-extension-for-audio-level-indication. - int32_t AudioLevelIndicationStatus(bool *enable, uint8_t *id) const; - // Store the audio level in d_bov for // header-extension-for-audio-level-indication. int32_t SetAudioLevel(const uint8_t level_d_bov); @@ -264,24 +267,20 @@ int32_t SetFecParameters(const FecProtectionParams *delta_params, const FecProtectionParams *key_params); - virtual void RegisterFrameCountObserver(FrameCountObserver* observer); - virtual FrameCountObserver* GetFrameCountObserver() const; - - int SendPadData(int payload_type, uint32_t timestamp, int64_t capture_time_ms, - int32_t bytes, StorageType store, - bool force_full_size_packets, bool only_pad_after_markerbit); + int SendPadData(uint32_t timestamp, + int64_t capture_time_ms, + int32_t bytes); // Called on update of RTP statistics. void RegisterRtpStatisticsCallback(StreamDataCountersCallback* callback); StreamDataCountersCallback* GetRtpStatisticsCallback() const; - // Called on new send bitrate estimate. - void RegisterBitrateObserver(BitrateStatisticsObserver* observer); - BitrateStatisticsObserver* GetBitrateObserver() const; - uint32_t BitrateSent() const; - virtual void BitrateUpdated(const BitrateStatistics& stats) OVERRIDE; + void SetRtpState(const RtpState& rtp_state); + RtpState GetRtpState() const; + void SetRtxRtpState(const RtpState& rtp_state); + RtpState GetRtxRtpState() const; protected: int32_t CheckPayloadType(const int8_t payload_type, @@ -303,13 +302,13 @@ bool PrepareAndSendPacket(uint8_t* buffer, uint16_t length, int64_t capture_time_ms, - bool send_over_rtx); + bool send_over_rtx, + bool is_retransmit); - int SendRedundantPayloads(int payload_type, int bytes); + // Return the number of bytes sent. + int TrySendRedundantPayloads(int bytes); + int TrySendPadData(int bytes); - bool SendPaddingAccordingToBitrate(int8_t payload_type, - uint32_t capture_timestamp, - int64_t capture_time_ms); int BuildPaddingPacket(uint8_t* packet, int header_length, int32_t bytes); void BuildRtxPacket(uint8_t* buffer, uint16_t* length, @@ -319,6 +318,15 @@ void UpdateDelayStatistics(int64_t capture_time_ms, int64_t now_ms); + void UpdateTransmissionTimeOffset(uint8_t *rtp_packet, + const uint16_t rtp_packet_length, + const RTPHeader &rtp_header, + const int64_t time_diff_ms) const; + void UpdateAbsoluteSendTime(uint8_t *rtp_packet, + const uint16_t rtp_packet_length, + const RTPHeader &rtp_header, + const int64_t now_ms) const; + void UpdateRtpStats(const uint8_t* buffer, uint32_t size, const RTPHeader& header, @@ -327,7 +335,10 @@ bool IsFecPacket(const uint8_t* buffer, const RTPHeader& header) const; Clock* clock_; - Bitrate bitrate_sent_; + int64_t clock_delta_ms_; + + scoped_ptr bitrates_; + Bitrate total_bitrate_sent_; int32_t id_; const bool audio_configured_; @@ -335,17 +346,17 @@ RTPSenderVideo *video_; PacedSender *paced_sender_; + int64_t last_capture_time_ms_sent_; CriticalSectionWrapper *send_critsect_; Transport *transport_; - bool sending_media_; + bool sending_media_ GUARDED_BY(send_critsect_); uint16_t max_payload_length_; - uint16_t target_send_bitrate_; uint16_t packet_over_head_; - int8_t payload_type_; - std::map payload_type_map_; + int8_t payload_type_ GUARDED_BY(send_critsect_); + std::map payload_type_map_; RtpHeaderExtensionMap rtp_header_extension_map_; int32_t transmission_time_offset_; @@ -360,34 +371,42 @@ // Statistics scoped_ptr statistics_crit_; - SendDelayMap send_delays_; - std::map frame_counts_; - FrameCountObserver* frame_count_observer_; - StreamDataCounters rtp_stats_; - StreamDataCounters rtx_rtp_stats_; - StreamDataCountersCallback* rtp_stats_callback_; - BitrateStatisticsObserver* bitrate_callback_; + SendDelayMap send_delays_ GUARDED_BY(statistics_crit_); + std::map frame_counts_ GUARDED_BY(statistics_crit_); + StreamDataCounters rtp_stats_ GUARDED_BY(statistics_crit_); + StreamDataCounters rtx_rtp_stats_ GUARDED_BY(statistics_crit_); + StreamDataCountersCallback* rtp_stats_callback_ GUARDED_BY(statistics_crit_); + FrameCountObserver* const frame_count_observer_; + SendSideDelayObserver* const send_side_delay_observer_; // RTP variables - bool start_time_stamp_forced_; - uint32_t start_time_stamp_; - SSRCDatabase &ssrc_db_; - uint32_t remote_ssrc_; - bool sequence_number_forced_; - uint16_t sequence_number_; - uint16_t sequence_number_rtx_; - bool ssrc_forced_; - uint32_t ssrc_; - uint32_t timestamp_; - int64_t capture_time_ms_; - int64_t last_timestamp_time_ms_; - bool last_packet_marker_bit_; - uint8_t num_csrcs_; - uint32_t csrcs_[kRtpCsrcSize]; - bool include_csrcs_; - int rtx_; - uint32_t ssrc_rtx_; - int payload_type_rtx_; + bool start_timestamp_forced_ GUARDED_BY(send_critsect_); + uint32_t start_timestamp_ GUARDED_BY(send_critsect_); + SSRCDatabase& ssrc_db_ GUARDED_BY(send_critsect_); + uint32_t remote_ssrc_ GUARDED_BY(send_critsect_); + bool sequence_number_forced_ GUARDED_BY(send_critsect_); + uint16_t sequence_number_ GUARDED_BY(send_critsect_); + uint16_t sequence_number_rtx_ GUARDED_BY(send_critsect_); + bool ssrc_forced_ GUARDED_BY(send_critsect_); + uint32_t ssrc_ GUARDED_BY(send_critsect_); + uint32_t timestamp_ GUARDED_BY(send_critsect_); + int64_t capture_time_ms_ GUARDED_BY(send_critsect_); + int64_t last_timestamp_time_ms_ GUARDED_BY(send_critsect_); + bool media_has_been_sent_ GUARDED_BY(send_critsect_); + bool last_packet_marker_bit_ GUARDED_BY(send_critsect_); + uint8_t num_csrcs_ GUARDED_BY(send_critsect_); + uint32_t csrcs_[kRtpCsrcSize] GUARDED_BY(send_critsect_); + bool include_csrcs_ GUARDED_BY(send_critsect_); + int rtx_ GUARDED_BY(send_critsect_); + uint32_t ssrc_rtx_ GUARDED_BY(send_critsect_); + int payload_type_rtx_ GUARDED_BY(send_critsect_); + + // Note: Don't access this variable directly, always go through + // SetTargetBitrateKbps or GetTargetBitrateKbps. Also remember + // that by the time the function returns there is no guarantee + // that the target bitrate is still valid. + scoped_ptr target_bitrate_critsect_; + uint32_t target_bitrate_ GUARDED_BY(target_bitrate_critsect_); }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -39,6 +39,7 @@ const uint8_t kAudioLevelExtensionId = 9; const int kAudioPayload = 103; const uint64_t kStartTime = 123456789; +const size_t kMaxPaddingSize = 224u; } // namespace using testing::_; @@ -62,20 +63,20 @@ class LoopbackTransportTest : public webrtc::Transport { public: LoopbackTransportTest() - : packets_sent_(0), - last_sent_packet_len_(0) { - } - virtual int SendPacket(int channel, const void *data, int len) { + : packets_sent_(0), last_sent_packet_len_(0), total_bytes_sent_(0) {} + virtual int SendPacket(int channel, const void *data, int len) OVERRIDE { packets_sent_++; memcpy(last_sent_packet_, data, len); last_sent_packet_len_ = len; + total_bytes_sent_ += static_cast(len); return len; } - virtual int SendRTCPPacket(int channel, const void *data, int len) { + virtual int SendRTCPPacket(int channel, const void *data, int len) OVERRIDE { return -1; } int packets_sent_; int last_sent_packet_len_; + size_t total_bytes_sent_; uint8_t last_sent_packet_[kMaxPacketLength]; }; @@ -92,9 +93,9 @@ SendPacket(_, _, _, _, _, _)).WillRepeatedly(testing::Return(true)); } - virtual void SetUp() { + virtual void SetUp() OVERRIDE { rtp_sender_.reset(new RTPSender(0, false, &fake_clock_, &transport_, NULL, - &mock_paced_sender_)); + &mock_paced_sender_, NULL, NULL, NULL)); rtp_sender_->SetSequenceNumber(kSeqNum); } @@ -160,11 +161,8 @@ EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength()); EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( kRtpExtensionAudioLevel, kAudioLevelExtensionId)); - // Accounted size for audio level is zero because it is currently specially - // treated by RTPSenderAudio. - EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength()); - // EXPECT_EQ(kRtpOneByteHeaderLength + kAudioLevelLength, - // rtp_sender_->RtpHeaderExtensionTotalLength()); + EXPECT_EQ(kRtpOneByteHeaderLength + kAudioLevelLength, + rtp_sender_->RtpHeaderExtensionTotalLength()); EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension( kRtpExtensionAudioLevel)); EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength()); @@ -183,14 +181,16 @@ EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( kRtpExtensionAudioLevel, kAudioLevelExtensionId)); EXPECT_EQ(kRtpOneByteHeaderLength + kTransmissionTimeOffsetLength + - kAbsoluteSendTimeLength, rtp_sender_->RtpHeaderExtensionTotalLength()); + kAbsoluteSendTimeLength + kAudioLevelLength, + rtp_sender_->RtpHeaderExtensionTotalLength()); EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension( kRtpExtensionTransmissionTimeOffset)); - EXPECT_EQ(kRtpOneByteHeaderLength + kAbsoluteSendTimeLength, - rtp_sender_->RtpHeaderExtensionTotalLength()); + EXPECT_EQ(kRtpOneByteHeaderLength + kAbsoluteSendTimeLength + + kAudioLevelLength, rtp_sender_->RtpHeaderExtensionTotalLength()); EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension( kRtpExtensionAbsoluteSendTime)); - EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength()); + EXPECT_EQ(kRtpOneByteHeaderLength + kAudioLevelLength, + rtp_sender_->RtpHeaderExtensionTotalLength()); EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension( kRtpExtensionAudioLevel)); EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength()); @@ -202,23 +202,24 @@ kMarkerBit, kTimestamp, 0); - EXPECT_EQ(12, length); + EXPECT_EQ(kRtpHeaderSize, length); // Verify - webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length); + webrtc::RtpUtility::RtpHeaderParser rtp_parser(packet_, length); webrtc::RTPHeader rtp_header; - RtpHeaderExtensionMap map; - map.Register(kRtpExtensionTransmissionTimeOffset, - kTransmissionTimeOffsetExtensionId); - const bool valid_rtp_header = rtp_parser.Parse(rtp_header, &map); + const bool valid_rtp_header = rtp_parser.Parse(rtp_header, NULL); ASSERT_TRUE(valid_rtp_header); ASSERT_FALSE(rtp_parser.RTCP()); VerifyRTPHeaderCommon(rtp_header); EXPECT_EQ(length, rtp_header.headerLength); + EXPECT_FALSE(rtp_header.extension.hasTransmissionTimeOffset); + EXPECT_FALSE(rtp_header.extension.hasAbsoluteSendTime); + EXPECT_FALSE(rtp_header.extension.hasAudioLevel); EXPECT_EQ(0, rtp_header.extension.transmissionTimeOffset); EXPECT_EQ(0u, rtp_header.extension.absoluteSendTime); + EXPECT_EQ(0u, rtp_header.extension.audioLevel); } TEST_F(RtpSenderTest, BuildRTPPacketWithTransmissionOffsetExtension) { @@ -231,10 +232,11 @@ kMarkerBit, kTimestamp, 0); - EXPECT_EQ(12 + rtp_sender_->RtpHeaderExtensionTotalLength(), length); + EXPECT_EQ(kRtpHeaderSize + rtp_sender_->RtpHeaderExtensionTotalLength(), + length); // Verify - webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length); + webrtc::RtpUtility::RtpHeaderParser rtp_parser(packet_, length); webrtc::RTPHeader rtp_header; RtpHeaderExtensionMap map; @@ -246,6 +248,7 @@ ASSERT_FALSE(rtp_parser.RTCP()); VerifyRTPHeaderCommon(rtp_header); EXPECT_EQ(length, rtp_header.headerLength); + EXPECT_TRUE(rtp_header.extension.hasTransmissionTimeOffset); EXPECT_EQ(kTimeOffset, rtp_header.extension.transmissionTimeOffset); // Parse without map extension @@ -255,6 +258,7 @@ ASSERT_TRUE(valid_rtp_header2); VerifyRTPHeaderCommon(rtp_header2); EXPECT_EQ(length, rtp_header2.headerLength); + EXPECT_FALSE(rtp_header2.extension.hasTransmissionTimeOffset); EXPECT_EQ(0, rtp_header2.extension.transmissionTimeOffset); } @@ -269,10 +273,11 @@ kMarkerBit, kTimestamp, 0); - EXPECT_EQ(12 + rtp_sender_->RtpHeaderExtensionTotalLength(), length); + EXPECT_EQ(kRtpHeaderSize + rtp_sender_->RtpHeaderExtensionTotalLength(), + length); // Verify - webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length); + webrtc::RtpUtility::RtpHeaderParser rtp_parser(packet_, length); webrtc::RTPHeader rtp_header; RtpHeaderExtensionMap map; @@ -284,6 +289,7 @@ ASSERT_FALSE(rtp_parser.RTCP()); VerifyRTPHeaderCommon(rtp_header); EXPECT_EQ(length, rtp_header.headerLength); + EXPECT_TRUE(rtp_header.extension.hasTransmissionTimeOffset); EXPECT_EQ(kNegTimeOffset, rtp_header.extension.transmissionTimeOffset); } @@ -297,10 +303,11 @@ kMarkerBit, kTimestamp, 0); - EXPECT_EQ(12 + rtp_sender_->RtpHeaderExtensionTotalLength(), length); + EXPECT_EQ(kRtpHeaderSize + rtp_sender_->RtpHeaderExtensionTotalLength(), + length); // Verify - webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length); + webrtc::RtpUtility::RtpHeaderParser rtp_parser(packet_, length); webrtc::RTPHeader rtp_header; RtpHeaderExtensionMap map; @@ -311,6 +318,7 @@ ASSERT_FALSE(rtp_parser.RTCP()); VerifyRTPHeaderCommon(rtp_header); EXPECT_EQ(length, rtp_header.headerLength); + EXPECT_TRUE(rtp_header.extension.hasAbsoluteSendTime); EXPECT_EQ(kAbsoluteSendTime, rtp_header.extension.absoluteSendTime); // Parse without map extension @@ -320,9 +328,54 @@ ASSERT_TRUE(valid_rtp_header2); VerifyRTPHeaderCommon(rtp_header2); EXPECT_EQ(length, rtp_header2.headerLength); + EXPECT_FALSE(rtp_header2.extension.hasAbsoluteSendTime); EXPECT_EQ(0u, rtp_header2.extension.absoluteSendTime); } +TEST_F(RtpSenderTest, BuildRTPPacketWithAudioLevelExtension) { + EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( + kRtpExtensionAudioLevel, kAudioLevelExtensionId)); + + int32_t length = rtp_sender_->BuildRTPheader(packet_, + kPayload, + kMarkerBit, + kTimestamp, + 0); + EXPECT_EQ(kRtpHeaderSize + rtp_sender_->RtpHeaderExtensionTotalLength(), + length); + + // Verify + webrtc::RtpUtility::RtpHeaderParser rtp_parser(packet_, length); + webrtc::RTPHeader rtp_header; + + // Updating audio level is done in RTPSenderAudio, so simulate it here. + rtp_parser.Parse(rtp_header); + rtp_sender_->UpdateAudioLevel(packet_, length, rtp_header, true, kAudioLevel); + + RtpHeaderExtensionMap map; + map.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId); + const bool valid_rtp_header = rtp_parser.Parse(rtp_header, &map); + + ASSERT_TRUE(valid_rtp_header); + ASSERT_FALSE(rtp_parser.RTCP()); + VerifyRTPHeaderCommon(rtp_header); + EXPECT_EQ(length, rtp_header.headerLength); + EXPECT_TRUE(rtp_header.extension.hasAudioLevel); + // Expect kAudioLevel + 0x80 because we set "voiced" to true in the call to + // UpdateAudioLevel(), above. + EXPECT_EQ(kAudioLevel + 0x80u, rtp_header.extension.audioLevel); + + // Parse without map extension + webrtc::RTPHeader rtp_header2; + const bool valid_rtp_header2 = rtp_parser.Parse(rtp_header2, NULL); + + ASSERT_TRUE(valid_rtp_header2); + VerifyRTPHeaderCommon(rtp_header2); + EXPECT_EQ(length, rtp_header2.headerLength); + EXPECT_FALSE(rtp_header2.extension.hasAudioLevel); + EXPECT_EQ(0u, rtp_header2.extension.audioLevel); +} + TEST_F(RtpSenderTest, BuildRTPPacketWithHeaderExtensions) { EXPECT_EQ(0, rtp_sender_->SetTransmissionTimeOffset(kTimeOffset)); EXPECT_EQ(0, rtp_sender_->SetAbsoluteSendTime(kAbsoluteSendTime)); @@ -330,30 +383,42 @@ kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId)); EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId)); + EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( + kRtpExtensionAudioLevel, kAudioLevelExtensionId)); int32_t length = rtp_sender_->BuildRTPheader(packet_, kPayload, kMarkerBit, kTimestamp, 0); - EXPECT_EQ(12 + rtp_sender_->RtpHeaderExtensionTotalLength(), length); + EXPECT_EQ(kRtpHeaderSize + rtp_sender_->RtpHeaderExtensionTotalLength(), + length); // Verify - webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length); + webrtc::RtpUtility::RtpHeaderParser rtp_parser(packet_, length); webrtc::RTPHeader rtp_header; + // Updating audio level is done in RTPSenderAudio, so simulate it here. + rtp_parser.Parse(rtp_header); + rtp_sender_->UpdateAudioLevel(packet_, length, rtp_header, true, kAudioLevel); + RtpHeaderExtensionMap map; map.Register(kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId); map.Register(kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId); + map.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId); const bool valid_rtp_header = rtp_parser.Parse(rtp_header, &map); ASSERT_TRUE(valid_rtp_header); ASSERT_FALSE(rtp_parser.RTCP()); VerifyRTPHeaderCommon(rtp_header); EXPECT_EQ(length, rtp_header.headerLength); + EXPECT_TRUE(rtp_header.extension.hasTransmissionTimeOffset); + EXPECT_TRUE(rtp_header.extension.hasAbsoluteSendTime); + EXPECT_TRUE(rtp_header.extension.hasAudioLevel); EXPECT_EQ(kTimeOffset, rtp_header.extension.transmissionTimeOffset); EXPECT_EQ(kAbsoluteSendTime, rtp_header.extension.absoluteSendTime); + EXPECT_EQ(kAudioLevel + 0x80u, rtp_header.extension.audioLevel); // Parse without map extension webrtc::RTPHeader rtp_header2; @@ -362,8 +427,12 @@ ASSERT_TRUE(valid_rtp_header2); VerifyRTPHeaderCommon(rtp_header2); EXPECT_EQ(length, rtp_header2.headerLength); + EXPECT_FALSE(rtp_header2.extension.hasTransmissionTimeOffset); + EXPECT_FALSE(rtp_header2.extension.hasAbsoluteSendTime); + EXPECT_FALSE(rtp_header2.extension.hasAudioLevel); EXPECT_EQ(0, rtp_header2.extension.transmissionTimeOffset); EXPECT_EQ(0u, rtp_header2.extension.absoluteSendTime); + EXPECT_EQ(0u, rtp_header2.extension.audioLevel); } TEST_F(RtpSenderTest, TrafficSmoothingWithExtensions) { @@ -376,7 +445,7 @@ kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId)); EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId)); - rtp_sender_->SetTargetSendBitrate(300000); + rtp_sender_->SetTargetBitrate(300000); int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); int32_t rtp_length = rtp_sender_->BuildRTPheader(packet_, kPayload, @@ -403,8 +472,8 @@ EXPECT_EQ(1, transport_.packets_sent_); EXPECT_EQ(rtp_length, transport_.last_sent_packet_len_); // Parse sent packet. - webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser( - transport_.last_sent_packet_, rtp_length); + webrtc::RtpUtility::RtpHeaderParser rtp_parser(transport_.last_sent_packet_, + rtp_length); webrtc::RTPHeader rtp_header; RtpHeaderExtensionMap map; map.Register(kRtpExtensionTransmissionTimeOffset, @@ -430,7 +499,7 @@ kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId)); EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId)); - rtp_sender_->SetTargetSendBitrate(300000); + rtp_sender_->SetTargetBitrate(300000); int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); int32_t rtp_length = rtp_sender_->BuildRTPheader(packet_, kPayload, @@ -465,8 +534,8 @@ EXPECT_EQ(rtp_length, transport_.last_sent_packet_len_); // Parse sent packet. - webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser( - transport_.last_sent_packet_, rtp_length); + webrtc::RtpUtility::RtpHeaderParser rtp_parser(transport_.last_sent_packet_, + rtp_length); webrtc::RTPHeader rtp_header; RtpHeaderExtensionMap map; map.Register(kRtpExtensionTransmissionTimeOffset, @@ -493,7 +562,7 @@ uint16_t seq_num = kSeqNum; uint32_t timestamp = kTimestamp; rtp_sender_->SetStorePacketsStatus(true, 10); - int rtp_header_len = 12; + int32_t rtp_header_len = kRtpHeaderSize; EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId)); rtp_header_len += 4; // 4 bytes extension. @@ -512,7 +581,7 @@ kAbsoluteSendTimeExtensionId); webrtc::RTPHeader rtp_header; - rtp_sender_->SetTargetSendBitrate(300000); + rtp_sender_->SetTargetBitrate(300000); int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); int32_t rtp_length = rtp_sender_->BuildRTPheader(packet_, kPayload, @@ -604,7 +673,7 @@ TEST_F(RtpSenderTest, SendRedundantPayloads) { MockTransport transport; rtp_sender_.reset(new RTPSender(0, false, &fake_clock_, &transport, NULL, - &mock_paced_sender_)); + &mock_paced_sender_, NULL, NULL, NULL)); rtp_sender_->SetSequenceNumber(kSeqNum); // Make all packets go through the pacer. EXPECT_CALL(mock_paced_sender_, @@ -613,14 +682,14 @@ uint16_t seq_num = kSeqNum; rtp_sender_->SetStorePacketsStatus(true, 10); - int rtp_header_len = 12; + int32_t rtp_header_len = kRtpHeaderSize; EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId)); rtp_header_len += 4; // 4 bytes extension. rtp_header_len += 4; // 4 extra bytes common to all extension headers. - rtp_sender_->SetRTXStatus(kRtxRetransmitted | kRtxRedundantPayloads, true, - 1234); + rtp_sender_->SetRTXStatus(kRtxRetransmitted | kRtxRedundantPayloads); + rtp_sender_->SetRtxSsrc(1234); // Create and set up parser. scoped_ptr rtp_parser( @@ -630,9 +699,9 @@ kTransmissionTimeOffsetExtensionId); rtp_parser->RegisterRtpHeaderExtension(kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId); - rtp_sender_->SetTargetSendBitrate(300000); + rtp_sender_->SetTargetBitrate(300000); const size_t kNumPayloadSizes = 10; - const int kPayloadSizes[kNumPayloadSizes] = {500, 550, 600, 650, 700, 750, + const size_t kPayloadSizes[kNumPayloadSizes] = {500, 550, 600, 650, 700, 750, 800, 850, 900, 950}; // Send 10 packets of increasing size. for (size_t i = 0; i < kNumPayloadSizes; ++i) { @@ -643,25 +712,27 @@ rtp_sender_->TimeToSendPacket(seq_num++, capture_time_ms, false); fake_clock_.AdvanceTimeMilliseconds(33); } - const int kPaddingPayloadSize = 224; // The amount of padding to send it too small to send a payload packet. - EXPECT_CALL(transport, SendPacket(_, _, kPaddingPayloadSize + rtp_header_len)) + EXPECT_CALL(transport, + SendPacket(_, _, kMaxPaddingSize + rtp_header_len)) .WillOnce(testing::ReturnArg<2>()); - EXPECT_EQ(kPaddingPayloadSize, rtp_sender_->TimeToSendPadding(49)); + EXPECT_EQ(kMaxPaddingSize, + static_cast(rtp_sender_->TimeToSendPadding(49))); const int kRtxHeaderSize = 2; EXPECT_CALL(transport, SendPacket(_, _, kPayloadSizes[0] + rtp_header_len + kRtxHeaderSize)) .WillOnce(testing::ReturnArg<2>()); - EXPECT_EQ(kPayloadSizes[0], rtp_sender_->TimeToSendPadding(500)); + EXPECT_EQ(kPayloadSizes[0], + static_cast(rtp_sender_->TimeToSendPadding(500))); EXPECT_CALL(transport, SendPacket(_, _, kPayloadSizes[kNumPayloadSizes - 1] + rtp_header_len + kRtxHeaderSize)) .WillOnce(testing::ReturnArg<2>()); - EXPECT_CALL(transport, SendPacket(_, _, kPaddingPayloadSize + rtp_header_len)) + EXPECT_CALL(transport, SendPacket(_, _, kMaxPaddingSize + rtp_header_len)) .WillOnce(testing::ReturnArg<2>()); - EXPECT_EQ(kPayloadSizes[kNumPayloadSizes - 1] + kPaddingPayloadSize, - rtp_sender_->TimeToSendPadding(999)); + EXPECT_EQ(kPayloadSizes[kNumPayloadSizes - 1] + kMaxPaddingSize, + static_cast(rtp_sender_->TimeToSendPadding(999))); } TEST_F(RtpSenderTest, SendGenericVideo) { @@ -676,8 +747,8 @@ 4321, payload, sizeof(payload), NULL)); - ModuleRTPUtility::RTPHeaderParser rtp_parser(transport_.last_sent_packet_, - transport_.last_sent_packet_len_); + RtpUtility::RtpHeaderParser rtp_parser(transport_.last_sent_packet_, + transport_.last_sent_packet_len_); webrtc::RTPHeader rtp_header; ASSERT_TRUE(rtp_parser.Parse(rtp_header)); @@ -702,8 +773,8 @@ 1234, 4321, payload, sizeof(payload), NULL)); - ModuleRTPUtility::RTPHeaderParser rtp_parser2(transport_.last_sent_packet_, - transport_.last_sent_packet_len_); + RtpUtility::RtpHeaderParser rtp_parser2(transport_.last_sent_packet_, + transport_.last_sent_packet_len_); ASSERT_TRUE(rtp_parser.Parse(rtp_header)); payload_data = GetPayloadData(rtp_header, transport_.last_sent_packet_); @@ -728,7 +799,7 @@ virtual void FrameCountUpdated(FrameType frame_type, uint32_t frame_count, - const unsigned int ssrc) { + const unsigned int ssrc) OVERRIDE { ++num_calls_; ssrc_ = ssrc; switch (frame_type) { @@ -749,6 +820,9 @@ uint32_t delta_frames_; } callback; + rtp_sender_.reset(new RTPSender(0, false, &fake_clock_, &transport_, NULL, + &mock_paced_sender_, NULL, &callback, NULL)); + char payload_name[RTP_PAYLOAD_NAME_SIZE] = "GENERIC"; const uint8_t payload_type = 127; ASSERT_EQ(0, rtp_sender_->RegisterPayload(payload_name, payload_type, 90000, @@ -757,8 +831,6 @@ rtp_sender_->SetStorePacketsStatus(true, 1); uint32_t ssrc = rtp_sender_->SSRC(); - rtp_sender_->RegisterFrameCountObserver(&callback); - ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kVideoFrameKey, payload_type, 1234, 4321, payload, sizeof(payload), NULL)); @@ -777,26 +849,31 @@ EXPECT_EQ(1U, callback.key_frames_); EXPECT_EQ(1U, callback.delta_frames_); - rtp_sender_->RegisterFrameCountObserver(NULL); + rtp_sender_.reset(); } TEST_F(RtpSenderTest, BitrateCallbacks) { class TestCallback : public BitrateStatisticsObserver { public: - TestCallback() - : BitrateStatisticsObserver(), num_calls_(0), ssrc_(0), bitrate_() {} + TestCallback() : BitrateStatisticsObserver(), num_calls_(0), ssrc_(0) {} virtual ~TestCallback() {} - virtual void Notify(const BitrateStatistics& stats, uint32_t ssrc) { + virtual void Notify(const BitrateStatistics& total_stats, + const BitrateStatistics& retransmit_stats, + uint32_t ssrc) OVERRIDE { ++num_calls_; ssrc_ = ssrc; - bitrate_ = stats; + total_stats_ = total_stats; + retransmit_stats_ = retransmit_stats; } uint32_t num_calls_; uint32_t ssrc_; - BitrateStatistics bitrate_; + BitrateStatistics total_stats_; + BitrateStatistics retransmit_stats_; } callback; + rtp_sender_.reset(new RTPSender(0, false, &fake_clock_, &transport_, NULL, + &mock_paced_sender_, &callback, NULL, NULL)); // Simulate kNumPackets sent with kPacketInterval ms intervals. const uint32_t kNumPackets = 15; @@ -813,8 +890,6 @@ rtp_sender_->SetStorePacketsStatus(true, 1); uint32_t ssrc = rtp_sender_->SSRC(); - rtp_sender_->RegisterBitrateObserver(&callback); - // Initial process call so we get a new time window. rtp_sender_->ProcessBitrate(); uint64_t start_time = fake_clock_.CurrentNtpInMilliseconds(); @@ -836,25 +911,27 @@ const uint32_t expected_packet_rate = 1000 / kPacketInterval; - EXPECT_EQ(1U, callback.num_calls_); + // We get one call for every stats updated, thus two calls since both the + // stream stats and the retransmit stats are updated once. + EXPECT_EQ(2u, callback.num_calls_); EXPECT_EQ(ssrc, callback.ssrc_); EXPECT_EQ(start_time + (kNumPackets * kPacketInterval), - callback.bitrate_.timestamp_ms); - EXPECT_EQ(expected_packet_rate, callback.bitrate_.packet_rate); + callback.total_stats_.timestamp_ms); + EXPECT_EQ(expected_packet_rate, callback.total_stats_.packet_rate); EXPECT_EQ((kPacketOverhead + sizeof(payload)) * 8 * expected_packet_rate, - callback.bitrate_.bitrate_bps); + callback.total_stats_.bitrate_bps); - rtp_sender_->RegisterBitrateObserver(NULL); + rtp_sender_.reset(); } class RtpSenderAudioTest : public RtpSenderTest { protected: RtpSenderAudioTest() {} - virtual void SetUp() { + virtual void SetUp() OVERRIDE { payload_ = kAudioPayload; rtp_sender_.reset(new RTPSender(0, true, &fake_clock_, &transport_, NULL, - &mock_paced_sender_)); + &mock_paced_sender_, NULL, NULL, NULL)); rtp_sender_->SetSequenceNumber(kSeqNum); } }; @@ -867,7 +944,7 @@ virtual ~TestCallback() {} virtual void DataCountersUpdated(const StreamDataCounters& counters, - uint32_t ssrc) { + uint32_t ssrc) OVERRIDE { ssrc_ = ssrc; counters_ = counters; } @@ -890,7 +967,6 @@ const uint8_t kRedPayloadType = 96; const uint8_t kUlpfecPayloadType = 97; - const uint32_t kMaxPaddingSize = 224; char payload_name[RTP_PAYLOAD_NAME_SIZE] = "GENERIC"; const uint8_t payload_type = 127; ASSERT_EQ(0, rtp_sender_->RegisterPayload(payload_name, payload_type, 90000, @@ -919,7 +995,7 @@ // Send padding. rtp_sender_->TimeToSendPadding(kMaxPaddingSize); // {bytes = 6, header = 24, padding = 224, packets = 3, retrans = 1, fec = 0} - EXPECT_TRUE(callback.Matches(ssrc, 6, 24, 224, 3, 1, 0)); + EXPECT_TRUE(callback.Matches(ssrc, 6, 24, kMaxPaddingSize, 3, 1, 0)); // Send FEC. rtp_sender_->SetGenericFECStatus(true, kRedPayloadType, kUlpfecPayloadType); @@ -934,53 +1010,11 @@ sizeof(payload), NULL)); // {bytes = 34, header = 48, padding = 224, packets = 5, retrans = 1, fec = 1} - EXPECT_TRUE(callback.Matches(ssrc, 34, 48, 224, 5, 1, 1)); + EXPECT_TRUE(callback.Matches(ssrc, 34, 48, kMaxPaddingSize, 5, 1, 1)); rtp_sender_->RegisterRtpStatisticsCallback(NULL); } -TEST_F(RtpSenderAudioTest, BuildRTPPacketWithAudioLevelExtension) { - EXPECT_EQ(0, rtp_sender_->SetAudioLevelIndicationStatus(true, - kAudioLevelExtensionId)); - EXPECT_EQ(0, rtp_sender_->SetAudioLevel(kAudioLevel)); - EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( - kRtpExtensionAudioLevel, kAudioLevelExtensionId)); - - int32_t length = rtp_sender_->BuildRTPheader(packet_, - kAudioPayload, - kMarkerBit, - kTimestamp, - 0); - EXPECT_EQ(12 + rtp_sender_->RtpHeaderExtensionTotalLength(), length); - - // Currently, no space is added by for header extension by BuildRTPHeader(). - EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength()); - - // Verify - webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length); - webrtc::RTPHeader rtp_header; - - RtpHeaderExtensionMap map; - map.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId); - const bool valid_rtp_header = rtp_parser.Parse(rtp_header, &map); - - ASSERT_TRUE(valid_rtp_header); - ASSERT_FALSE(rtp_parser.RTCP()); - VerifyRTPHeaderCommon(rtp_header); - EXPECT_EQ(length, rtp_header.headerLength); - // TODO(solenberg): Should verify that we got audio level in header extension. - - // Parse without map extension - webrtc::RTPHeader rtp_header2; - const bool valid_rtp_header2 = rtp_parser.Parse(rtp_header2, NULL); - - ASSERT_TRUE(valid_rtp_header2); - VerifyRTPHeaderCommon(rtp_header2); - EXPECT_EQ(length, rtp_header2.headerLength); - // TODO(solenberg): Should verify that we didn't get audio level. - EXPECT_EQ(0, rtp_sender_->SetAudioLevelIndicationStatus(false, 0)); -} - TEST_F(RtpSenderAudioTest, SendAudio) { char payload_name[RTP_PAYLOAD_NAME_SIZE] = "PAYLOAD_NAME"; const uint8_t payload_type = 127; @@ -992,8 +1026,8 @@ 4321, payload, sizeof(payload), NULL)); - ModuleRTPUtility::RTPHeaderParser rtp_parser(transport_.last_sent_packet_, - transport_.last_sent_packet_len_); + RtpUtility::RtpHeaderParser rtp_parser(transport_.last_sent_packet_, + transport_.last_sent_packet_len_); webrtc::RTPHeader rtp_header; ASSERT_TRUE(rtp_parser.Parse(rtp_header)); @@ -1007,8 +1041,6 @@ } TEST_F(RtpSenderAudioTest, SendAudioWithAudioLevelExtension) { - EXPECT_EQ(0, rtp_sender_->SetAudioLevelIndicationStatus(true, - kAudioLevelExtensionId)); EXPECT_EQ(0, rtp_sender_->SetAudioLevel(kAudioLevel)); EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( kRtpExtensionAudioLevel, kAudioLevelExtensionId)); @@ -1023,8 +1055,8 @@ 4321, payload, sizeof(payload), NULL)); - ModuleRTPUtility::RTPHeaderParser rtp_parser(transport_.last_sent_packet_, - transport_.last_sent_packet_len_); + RtpUtility::RtpHeaderParser rtp_parser(transport_.last_sent_packet_, + transport_.last_sent_packet_len_); webrtc::RTPHeader rtp_header; ASSERT_TRUE(rtp_parser.Parse(rtp_header)); @@ -1044,7 +1076,104 @@ EXPECT_EQ(0, memcmp(extension, payload_data - sizeof(extension), sizeof(extension))); - EXPECT_EQ(0, rtp_sender_->SetAudioLevelIndicationStatus(false, 0)); } +// As RFC4733, named telephone events are carried as part of the audio stream +// and must use the same sequence number and timestamp base as the regular +// audio channel. +// This test checks the marker bit for the first packet and the consequent +// packets of the same telephone event. Since it is specifically for DTMF +// events, ignoring audio packets and sending kFrameEmpty instead of those. +TEST_F(RtpSenderAudioTest, CheckMarkerBitForTelephoneEvents) { + char payload_name[RTP_PAYLOAD_NAME_SIZE] = "telephone-event"; + uint8_t payload_type = 126; + ASSERT_EQ(0, rtp_sender_->RegisterPayload(payload_name, payload_type, 0, + 0, 0)); + // For Telephone events, payload is not added to the registered payload list, + // it will register only the payload used for audio stream. + // Registering the payload again for audio stream with different payload name. + strcpy(payload_name, "payload_name"); + ASSERT_EQ(0, rtp_sender_->RegisterPayload(payload_name, payload_type, 8000, + 1, 0)); + int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); + // DTMF event key=9, duration=500 and attenuationdB=10 + rtp_sender_->SendTelephoneEvent(9, 500, 10); + // During start, it takes the starting timestamp as last sent timestamp. + // The duration is calculated as the difference of current and last sent + // timestamp. So for first call it will skip since the duration is zero. + ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kFrameEmpty, payload_type, + capture_time_ms, + 0, NULL, 0, + NULL)); + // DTMF Sample Length is (Frequency/1000) * Duration. + // So in this case, it is (8000/1000) * 500 = 4000. + // Sending it as two packets. + ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kFrameEmpty, payload_type, + capture_time_ms+2000, + 0, NULL, 0, + NULL)); + scoped_ptr rtp_parser( + webrtc::RtpHeaderParser::Create()); + ASSERT_TRUE(rtp_parser.get() != NULL); + webrtc::RTPHeader rtp_header; + ASSERT_TRUE(rtp_parser->Parse(transport_.last_sent_packet_, + transport_.last_sent_packet_len_, + &rtp_header)); + // Marker Bit should be set to 1 for first packet. + EXPECT_TRUE(rtp_header.markerBit); + + ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kFrameEmpty, payload_type, + capture_time_ms+4000, + 0, NULL, 0, + NULL)); + ASSERT_TRUE(rtp_parser->Parse(transport_.last_sent_packet_, + transport_.last_sent_packet_len_, + &rtp_header)); + // Marker Bit should be set to 0 for rest of the packets. + EXPECT_FALSE(rtp_header.markerBit); +} + +TEST_F(RtpSenderTest, BytesReportedCorrectly) { + const char* kPayloadName = "GENERIC"; + const uint8_t kPayloadType = 127; + rtp_sender_->SetSSRC(1234); + rtp_sender_->SetRtxSsrc(4321); + rtp_sender_->SetRtxPayloadType(kPayloadType - 1); + rtp_sender_->SetRTXStatus(kRtxRetransmitted | kRtxRedundantPayloads); + + ASSERT_EQ( + 0, + rtp_sender_->RegisterPayload(kPayloadName, kPayloadType, 90000, 0, 1500)); + uint8_t payload[] = {47, 11, 32, 93, 89}; + + ASSERT_EQ(0, + rtp_sender_->SendOutgoingData(kVideoFrameKey, + kPayloadType, + 1234, + 4321, + payload, + sizeof(payload), + 0)); + + // Will send 2 full-size padding packets. + rtp_sender_->TimeToSendPadding(1); + rtp_sender_->TimeToSendPadding(1); + + StreamDataCounters rtp_stats; + StreamDataCounters rtx_stats; + rtp_sender_->GetDataCounters(&rtp_stats, &rtx_stats); + + // Payload + 1-byte generic header. + EXPECT_EQ(rtp_stats.bytes, sizeof(payload) + 1); + EXPECT_EQ(rtp_stats.header_bytes, 12u); + EXPECT_EQ(rtp_stats.padding_bytes, 0u); + EXPECT_EQ(rtx_stats.bytes, 0u); + EXPECT_EQ(rtx_stats.header_bytes, 24u); + EXPECT_EQ(rtx_stats.padding_bytes, 2 * kMaxPaddingSize); + + EXPECT_EQ(transport_.total_bytes_sent_, + rtp_stats.bytes + rtp_stats.header_bytes + rtp_stats.padding_bytes + + rtx_stats.bytes + rtx_stats.header_bytes + + rtx_stats.padding_bytes); +} } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc 2015-02-03 14:33:36.000000000 +0000 @@ -17,11 +17,11 @@ #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" #include "webrtc/modules/rtp_rtcp/source/producer_fec.h" #include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h" -#include "webrtc/modules/rtp_rtcp/source/rtp_format_h264.h" #include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_format_h264.h" #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/trace_event.h" namespace webrtc { @@ -32,11 +32,8 @@ ForwardErrorCorrection::Packet* pkt; }; -RTPSenderVideo::RTPSenderVideo(const int32_t id, - Clock* clock, - RTPSenderInterface* rtpSender) - : _id(id), - _rtpSender(*rtpSender), +RTPSenderVideo::RTPSenderVideo(Clock* clock, RTPSenderInterface* rtpSender) + : _rtpSender(*rtpSender), _sendVideoCritsect(CriticalSectionWrapper::CreateCriticalSection()), _videoType(kRtpVideoGeneric), _videoCodecInformation(NULL), @@ -44,7 +41,7 @@ _retransmissionSettings(kRetransmitBaseLayer), // Generic FEC - _fec(id), + _fec(), _fecEnabled(false), _payloadTypeRED(-1), _payloadTypeFEC(-1), @@ -58,49 +55,43 @@ memset(&key_fec_params_, 0, sizeof(key_fec_params_)); delta_fec_params_.max_fec_frames = key_fec_params_.max_fec_frames = 1; delta_fec_params_.fec_mask_type = key_fec_params_.fec_mask_type = - kFecMaskRandom; + kFecMaskRandom; } -RTPSenderVideo::~RTPSenderVideo() -{ - if(_videoCodecInformation) - { - delete _videoCodecInformation; - } - delete _sendVideoCritsect; +RTPSenderVideo::~RTPSenderVideo() { + if (_videoCodecInformation) { + delete _videoCodecInformation; + } + delete _sendVideoCritsect; } -void -RTPSenderVideo::SetVideoCodecType(RtpVideoCodecTypes videoType) -{ - CriticalSectionScoped cs(_sendVideoCritsect); - _videoType = videoType; +void RTPSenderVideo::SetVideoCodecType(RtpVideoCodecTypes videoType) { + CriticalSectionScoped cs(_sendVideoCritsect); + _videoType = videoType; } -RtpVideoCodecTypes -RTPSenderVideo::VideoCodecType() const -{ - return _videoType; +RtpVideoCodecTypes RTPSenderVideo::VideoCodecType() const { + return _videoType; } int32_t RTPSenderVideo::RegisterVideoPayload( const char payloadName[RTP_PAYLOAD_NAME_SIZE], const int8_t payloadType, const uint32_t maxBitRate, - ModuleRTPUtility::Payload*& payload) { + RtpUtility::Payload*& payload) { CriticalSectionScoped cs(_sendVideoCritsect); RtpVideoCodecTypes videoType = kRtpVideoGeneric; - if (ModuleRTPUtility::StringCompare(payloadName, "VP8",3)) { + if (RtpUtility::StringCompare(payloadName, "VP8", 3)) { videoType = kRtpVideoVp8; - } else if (ModuleRTPUtility::StringCompare(payloadName, "H264", 4)) { + } else if (RtpUtility::StringCompare(payloadName, "H264", 4)) { videoType = kRtpVideoH264; - } else if (ModuleRTPUtility::StringCompare(payloadName, "I420", 4)) { + } else if (RtpUtility::StringCompare(payloadName, "I420", 4)) { videoType = kRtpVideoGeneric; } else { videoType = kRtpVideoGeneric; } - payload = new ModuleRTPUtility::Payload; + payload = new RtpUtility::Payload; payload->name[RTP_PAYLOAD_NAME_SIZE - 1] = 0; strncpy(payload->name, payloadName, RTP_PAYLOAD_NAME_SIZE - 1); payload->typeSpecific.Video.videoCodecType = videoType; @@ -109,34 +100,34 @@ return 0; } -int32_t -RTPSenderVideo::SendVideoPacket(uint8_t* data_buffer, - const uint16_t payload_length, - const uint16_t rtp_header_length, - const uint32_t capture_timestamp, - int64_t capture_time_ms, - StorageType storage, - bool protect) { - if(_fecEnabled) { +int32_t RTPSenderVideo::SendVideoPacket(uint8_t* data_buffer, + const uint16_t payload_length, + const uint16_t rtp_header_length, + const uint32_t capture_timestamp, + int64_t capture_time_ms, + StorageType storage, + bool protect) { + if (_fecEnabled) { int ret = 0; int fec_overhead_sent = 0; int video_sent = 0; - RedPacket* red_packet = producer_fec_.BuildRedPacket(data_buffer, - payload_length, - rtp_header_length, - _payloadTypeRED); - TRACE_EVENT_INSTANT2("webrtc_rtp", "Video::PacketRed", - "timestamp", capture_timestamp, - "seqnum", _rtpSender.SequenceNumber()); + RedPacket* red_packet = producer_fec_.BuildRedPacket( + data_buffer, payload_length, rtp_header_length, _payloadTypeRED); + TRACE_EVENT_INSTANT2("webrtc_rtp", + "Video::PacketRed", + "timestamp", + capture_timestamp, + "seqnum", + _rtpSender.SequenceNumber()); // Sending the media packet with RED header. - int packet_success = _rtpSender.SendToNetwork( - red_packet->data(), - red_packet->length() - rtp_header_length, - rtp_header_length, - capture_time_ms, - storage, - PacedSender::kNormalPriority); + int packet_success = + _rtpSender.SendToNetwork(red_packet->data(), + red_packet->length() - rtp_header_length, + rtp_header_length, + capture_time_ms, + storage, + PacedSender::kNormalPriority); ret |= packet_success; @@ -147,34 +138,36 @@ red_packet = NULL; if (protect) { - ret = producer_fec_.AddRtpPacketAndGenerateFec(data_buffer, - payload_length, - rtp_header_length); + ret = producer_fec_.AddRtpPacketAndGenerateFec( + data_buffer, payload_length, rtp_header_length); if (ret != 0) return ret; } while (producer_fec_.FecAvailable()) { - red_packet = producer_fec_.GetFecPacket( - _payloadTypeRED, - _payloadTypeFEC, - _rtpSender.IncrementSequenceNumber(), - rtp_header_length); + red_packet = + producer_fec_.GetFecPacket(_payloadTypeRED, + _payloadTypeFEC, + _rtpSender.IncrementSequenceNumber(), + rtp_header_length); StorageType storage = kDontRetransmit; if (_retransmissionSettings & kRetransmitFECPackets) { storage = kAllowRetransmission; } - TRACE_EVENT_INSTANT2("webrtc_rtp", "Video::PacketFec", - "timestamp", capture_timestamp, - "seqnum", _rtpSender.SequenceNumber()); + TRACE_EVENT_INSTANT2("webrtc_rtp", + "Video::PacketFec", + "timestamp", + capture_timestamp, + "seqnum", + _rtpSender.SequenceNumber()); // Sending FEC packet with RED header. - int packet_success = _rtpSender.SendToNetwork( - red_packet->data(), - red_packet->length() - rtp_header_length, - rtp_header_length, - capture_time_ms, - storage, - PacedSender::kNormalPriority); + int packet_success = + _rtpSender.SendToNetwork(red_packet->data(), + red_packet->length() - rtp_header_length, + rtp_header_length, + capture_time_ms, + storage, + PacedSender::kNormalPriority); ret |= packet_success; @@ -188,9 +181,12 @@ _fecOverheadRate.Update(fec_overhead_sent); return ret; } - TRACE_EVENT_INSTANT2("webrtc_rtp", "Video::PacketNormal", - "timestamp", capture_timestamp, - "seqnum", _rtpSender.SequenceNumber()); + TRACE_EVENT_INSTANT2("webrtc_rtp", + "Video::PacketNormal", + "timestamp", + capture_timestamp, + "seqnum", + _rtpSender.SequenceNumber()); int ret = _rtpSender.SendToNetwork(data_buffer, payload_length, rtp_header_length, @@ -203,69 +199,61 @@ return ret; } -int32_t -RTPSenderVideo::SendRTPIntraRequest() -{ - // RFC 2032 - // 5.2.1. Full intra-frame Request (FIR) packet - - uint16_t length = 8; - uint8_t data[8]; - data[0] = 0x80; - data[1] = 192; - data[2] = 0; - data[3] = 1; // length - - ModuleRTPUtility::AssignUWord32ToBuffer(data+4, _rtpSender.SSRC()); - - TRACE_EVENT_INSTANT1("webrtc_rtp", - "Video::IntraRequest", - "seqnum", _rtpSender.SequenceNumber()); - return _rtpSender.SendToNetwork(data, 0, length, -1, kDontStore, - PacedSender::kNormalPriority); -} - -int32_t -RTPSenderVideo::SetGenericFECStatus(const bool enable, - const uint8_t payloadTypeRED, - const uint8_t payloadTypeFEC) -{ - _fecEnabled = enable; - _payloadTypeRED = payloadTypeRED; - _payloadTypeFEC = payloadTypeFEC; - memset(&delta_fec_params_, 0, sizeof(delta_fec_params_)); - memset(&key_fec_params_, 0, sizeof(key_fec_params_)); - delta_fec_params_.max_fec_frames = key_fec_params_.max_fec_frames = 1; - delta_fec_params_.fec_mask_type = key_fec_params_.fec_mask_type = - kFecMaskRandom; - return 0; -} - -int32_t -RTPSenderVideo::GenericFECStatus(bool& enable, - uint8_t& payloadTypeRED, - uint8_t& payloadTypeFEC) const -{ - enable = _fecEnabled; - payloadTypeRED = _payloadTypeRED; - payloadTypeFEC = _payloadTypeFEC; - return 0; -} - -uint16_t -RTPSenderVideo::FECPacketOverhead() const -{ - if (_fecEnabled) - { - // Overhead is FEC headers plus RED for FEC header plus anything in RTP - // header beyond the 12 bytes base header (CSRC list, extensions...) - // This reason for the header extensions to be included here is that - // from an FEC viewpoint, they are part of the payload to be protected. - // (The base RTP header is already protected by the FEC header.) - return ForwardErrorCorrection::PacketOverhead() + REDForFECHeaderLength + - (_rtpSender.RTPHeaderLength() - kRtpHeaderSize); - } - return 0; +int32_t RTPSenderVideo::SendRTPIntraRequest() { + // RFC 2032 + // 5.2.1. Full intra-frame Request (FIR) packet + + uint16_t length = 8; + uint8_t data[8]; + data[0] = 0x80; + data[1] = 192; + data[2] = 0; + data[3] = 1; // length + + RtpUtility::AssignUWord32ToBuffer(data + 4, _rtpSender.SSRC()); + + TRACE_EVENT_INSTANT1("webrtc_rtp", + "Video::IntraRequest", + "seqnum", + _rtpSender.SequenceNumber()); + return _rtpSender.SendToNetwork( + data, 0, length, -1, kDontStore, PacedSender::kNormalPriority); +} + +int32_t RTPSenderVideo::SetGenericFECStatus(const bool enable, + const uint8_t payloadTypeRED, + const uint8_t payloadTypeFEC) { + _fecEnabled = enable; + _payloadTypeRED = payloadTypeRED; + _payloadTypeFEC = payloadTypeFEC; + memset(&delta_fec_params_, 0, sizeof(delta_fec_params_)); + memset(&key_fec_params_, 0, sizeof(key_fec_params_)); + delta_fec_params_.max_fec_frames = key_fec_params_.max_fec_frames = 1; + delta_fec_params_.fec_mask_type = key_fec_params_.fec_mask_type = + kFecMaskRandom; + return 0; +} + +int32_t RTPSenderVideo::GenericFECStatus(bool& enable, + uint8_t& payloadTypeRED, + uint8_t& payloadTypeFEC) const { + enable = _fecEnabled; + payloadTypeRED = _payloadTypeRED; + payloadTypeFEC = _payloadTypeFEC; + return 0; +} + +uint16_t RTPSenderVideo::FECPacketOverhead() const { + if (_fecEnabled) { + // Overhead is FEC headers plus RED for FEC header plus anything in RTP + // header beyond the 12 bytes base header (CSRC list, extensions...) + // This reason for the header extensions to be included here is that + // from an FEC viewpoint, they are part of the payload to be protected. + // (The base RTP header is already protected by the FEC header.) + return ForwardErrorCorrection::PacketOverhead() + REDForFECHeaderLength + + (_rtpSender.RTPHeaderLength() - kRtpHeaderSize); + } + return 0; } int32_t RTPSenderVideo::SetFecParameters( @@ -278,268 +266,109 @@ return 0; } -int32_t -RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType, - const FrameType frameType, - const int8_t payloadType, - const uint32_t captureTimeStamp, - int64_t capture_time_ms, - const uint8_t* payloadData, - const uint32_t payloadSize, - const RTPFragmentationHeader* fragmentation, - VideoCodecInformation* codecInfo, - const RTPVideoTypeHeader* rtpTypeHdr) { - if( payloadSize == 0) { - return -1; +int32_t RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType, + const FrameType frameType, + const int8_t payloadType, + const uint32_t captureTimeStamp, + int64_t capture_time_ms, + const uint8_t* payloadData, + const uint32_t payloadSize, + const RTPFragmentationHeader* fragmentation, + VideoCodecInformation* codecInfo, + const RTPVideoTypeHeader* rtpTypeHdr) { + if (payloadSize == 0) { + return -1; } if (frameType == kVideoFrameKey) { - producer_fec_.SetFecParameters(&key_fec_params_, - _numberFirstPartition); + producer_fec_.SetFecParameters(&key_fec_params_, _numberFirstPartition); } else { - producer_fec_.SetFecParameters(&delta_fec_params_, - _numberFirstPartition); + producer_fec_.SetFecParameters(&delta_fec_params_, _numberFirstPartition); } // Default setting for number of first partition packets: // Will be extracted in SendVP8 for VP8 codec; other codecs use 0 _numberFirstPartition = 0; - int32_t retVal = -1; - switch(videoType) { - case kRtpVideoGeneric: - retVal = SendGeneric(frameType, payloadType, captureTimeStamp, - capture_time_ms, payloadData, payloadSize); - break; - case kRtpVideoVp8: - retVal = SendVP8(frameType, - payloadType, - captureTimeStamp, - capture_time_ms, - payloadData, - payloadSize, - fragmentation, - rtpTypeHdr); - break; - case kRtpVideoH264: - retVal = SendH264(frameType, - payloadType, - captureTimeStamp, - capture_time_ms, - payloadData, - payloadSize, - fragmentation, - rtpTypeHdr); - break; - default: - assert(false); - break; - } - - if(retVal <= 0) { - return retVal; - } - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, "%s(timestamp:%u)", - __FUNCTION__, captureTimeStamp); - return 0; + return Send(videoType, + frameType, + payloadType, + captureTimeStamp, + capture_time_ms, + payloadData, + payloadSize, + fragmentation, + rtpTypeHdr) + ? 0 + : -1; } -int32_t RTPSenderVideo::SendGeneric(const FrameType frame_type, - const int8_t payload_type, - const uint32_t capture_timestamp, - int64_t capture_time_ms, - const uint8_t* payload, - uint32_t size) { - assert(frame_type == kVideoFrameKey || frame_type == kVideoFrameDelta); - uint16_t rtp_header_length = _rtpSender.RTPHeaderLength(); - uint16_t max_length = _rtpSender.MaxPayloadLength() - FECPacketOverhead() - - rtp_header_length - (1 /* generic header length */); - - // Fragment packets more evenly by splitting the payload up evenly. - uint32_t num_packets = (size + max_length - 1) / max_length; - uint32_t payload_length = (size + num_packets - 1) / num_packets; - assert(payload_length <= max_length); - - // Fragment packet into packets of max MaxPayloadLength bytes payload. - uint8_t buffer[IP_PACKET_SIZE]; - - uint8_t generic_header = RtpFormatVideoGeneric::kFirstPacketBit; - if (frame_type == kVideoFrameKey) { - generic_header |= RtpFormatVideoGeneric::kKeyFrameBit; - } - - while (size > 0) { - if (size < payload_length) { - payload_length = size; - } - size -= payload_length; - - // MarkerBit is 1 on final packet (bytes_to_send == 0) - if (_rtpSender.BuildRTPheader(buffer, payload_type, size == 0, - capture_timestamp, - capture_time_ms) != rtp_header_length) { - return -1; - } - - uint8_t* out_ptr = &buffer[rtp_header_length]; - - // Put generic header in packet - *out_ptr++ = generic_header; - // Remove first-packet bit, following packets are intermediate - generic_header &= ~RtpFormatVideoGeneric::kFirstPacketBit; - - // Put payload in packet - memcpy(out_ptr, payload, payload_length); - payload += payload_length; - - if (SendVideoPacket(buffer, payload_length + 1, rtp_header_length, - capture_timestamp, capture_time_ms, - kAllowRetransmission, true)) { - return -1; - } - } - return 0; +VideoCodecInformation* RTPSenderVideo::CodecInformationVideo() { + return _videoCodecInformation; } -VideoCodecInformation* -RTPSenderVideo::CodecInformationVideo() -{ - return _videoCodecInformation; -} - -void -RTPSenderVideo::SetMaxConfiguredBitrateVideo(const uint32_t maxBitrate) -{ - _maxBitrate = maxBitrate; -} - -uint32_t -RTPSenderVideo::MaxConfiguredBitrateVideo() const -{ - return _maxBitrate; -} - -int32_t -RTPSenderVideo::SendVP8(const FrameType frameType, - const int8_t payloadType, - const uint32_t captureTimeStamp, - int64_t capture_time_ms, - const uint8_t* payloadData, - const uint32_t payloadSize, - const RTPFragmentationHeader* fragmentation, - const RTPVideoTypeHeader* rtpTypeHdr) -{ - const uint16_t rtpHeaderLength = _rtpSender.RTPHeaderLength(); - - int32_t payloadBytesToSend = payloadSize; - const uint8_t* data = payloadData; - - uint16_t maxPayloadLengthVP8 = _rtpSender.MaxDataPayloadLength(); - - assert(rtpTypeHdr); - // Initialize disregarding partition boundaries: this will use kEqualSize - // packetization mode, which produces ~equal size packets for each frame. - RtpFormatVp8 packetizer(data, payloadBytesToSend, rtpTypeHdr->VP8, - maxPayloadLengthVP8); - - StorageType storage = kAllowRetransmission; - if (rtpTypeHdr->VP8.temporalIdx == 0 && - !(_retransmissionSettings & kRetransmitBaseLayer)) { - storage = kDontRetransmit; - } - if (rtpTypeHdr->VP8.temporalIdx > 0 && - !(_retransmissionSettings & kRetransmitHigherLayers)) { - storage = kDontRetransmit; - } +void RTPSenderVideo::SetMaxConfiguredBitrateVideo(const uint32_t maxBitrate) { + _maxBitrate = maxBitrate; +} - bool last = false; - _numberFirstPartition = 0; - // |rtpTypeHdr->VP8.temporalIdx| is zero for base layers, or -1 if the field - // isn't used. We currently only protect base layers. - bool protect = (rtpTypeHdr->VP8.temporalIdx < 1); - while (!last) - { - // Write VP8 Payload Descriptor and VP8 payload. - uint8_t dataBuffer[IP_PACKET_SIZE] = {0}; - int payloadBytesInPacket = 0; - int packetStartPartition = - packetizer.NextPacket(&dataBuffer[rtpHeaderLength], - &payloadBytesInPacket, &last); - // TODO(holmer): Temporarily disable first partition packet counting - // to avoid a bug in ProducerFec which doesn't properly handle - // important packets. - // if (packetStartPartition == 0) - // { - // ++_numberFirstPartition; - // } - // else - if (packetStartPartition < 0) - { - return -1; - } - - // Write RTP header. - // Set marker bit true if this is the last packet in frame. - _rtpSender.BuildRTPheader(dataBuffer, payloadType, last, - captureTimeStamp, capture_time_ms); - if (-1 == SendVideoPacket(dataBuffer, payloadBytesInPacket, - rtpHeaderLength, captureTimeStamp, - capture_time_ms, storage, protect)) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "RTPSenderVideo::SendVP8 failed to send packet number" - " %d", _rtpSender.SequenceNumber()); - } - } - TRACE_EVENT_ASYNC_END1("webrtc", "Video", capture_time_ms, - "timestamp", _rtpSender.Timestamp()); - return 0; +uint32_t RTPSenderVideo::MaxConfiguredBitrateVideo() const { + return _maxBitrate; } -int32_t RTPSenderVideo::SendH264(const FrameType frameType, - const int8_t payloadType, - const uint32_t captureTimeStamp, - int64_t capture_time_ms, - const uint8_t* payloadData, - const uint32_t payloadSize, - const RTPFragmentationHeader* fragmentation, - const RTPVideoTypeHeader* rtpTypeHdr) { - const uint16_t rtpHeaderLength = _rtpSender.RTPHeaderLength(); - int32_t payloadBytesToSend = payloadSize; +bool RTPSenderVideo::Send(const RtpVideoCodecTypes videoType, + const FrameType frameType, + const int8_t payloadType, + const uint32_t captureTimeStamp, + int64_t capture_time_ms, + const uint8_t* payloadData, + const uint32_t payloadSize, + const RTPFragmentationHeader* fragmentation, + const RTPVideoTypeHeader* rtpTypeHdr) { + uint16_t rtp_header_length = _rtpSender.RTPHeaderLength(); + int32_t payload_bytes_to_send = payloadSize; const uint8_t* data = payloadData; - uint16_t maxPayloadLengthH264 = _rtpSender.MaxDataPayloadLength(); + size_t max_payload_length = _rtpSender.MaxDataPayloadLength(); - RtpFormatH264 packetizer(data, payloadBytesToSend, maxPayloadLengthH264); + scoped_ptr packetizer(RtpPacketizer::Create( + videoType, max_payload_length, rtpTypeHdr, frameType)); - StorageType storage = kAllowRetransmission; - bool protect = (frameType == kVideoFrameKey); - bool last = false; + // TODO(changbin): we currently don't support to configure the codec to + // output multiple partitions for VP8. Should remove below check after the + // issue is fixed. + const RTPFragmentationHeader* frag = + (videoType == kRtpVideoVp8) ? NULL : fragmentation; + + packetizer->SetPayloadData(data, payload_bytes_to_send, frag); + bool last = false; while (!last) { - // Write H264 Payload uint8_t dataBuffer[IP_PACKET_SIZE] = {0}; - int payloadBytesInPacket = 0; - int ret_val = packetizer.NextPacket(&dataBuffer[rtpHeaderLength], - &payloadBytesInPacket, &last); - if (ret_val < 0) { - return -1; + size_t payload_bytes_in_packet = 0; + if (!packetizer->NextPacket( + &dataBuffer[rtp_header_length], &payload_bytes_in_packet, &last)) { + return false; } // Write RTP header. // Set marker bit true if this is the last packet in frame. - _rtpSender.BuildRTPheader(dataBuffer, payloadType, last, - captureTimeStamp, capture_time_ms); - if (-1 == SendVideoPacket(dataBuffer, payloadBytesInPacket, - rtpHeaderLength, captureTimeStamp, - capture_time_ms, storage, protect)) { - } - - if (ret_val == 0) { - // single NAL unit - last = true; + _rtpSender.BuildRTPheader( + dataBuffer, payloadType, last, captureTimeStamp, capture_time_ms); + if (SendVideoPacket(dataBuffer, + payload_bytes_in_packet, + rtp_header_length, + captureTimeStamp, + capture_time_ms, + packetizer->GetStorageType(_retransmissionSettings), + packetizer->GetProtectionType() == kProtectedPacket)) { + LOG(LS_WARNING) << packetizer->ToString() + << " failed to send packet number " + << _rtpSender.SequenceNumber(); } } - return 0; + + TRACE_EVENT_ASYNC_END1( + "webrtc", "Video", capture_time_ms, "timestamp", _rtpSender.Timestamp()); + return true; } void RTPSenderVideo::ProcessBitrate() { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_video.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_video.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_video.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_video.h 2015-02-03 14:33:36.000000000 +0000 @@ -28,124 +28,106 @@ class CriticalSectionWrapper; struct RtpPacket; -class RTPSenderVideo -{ -public: - RTPSenderVideo(const int32_t id, Clock* clock, - RTPSenderInterface* rtpSender); - virtual ~RTPSenderVideo(); - - virtual RtpVideoCodecTypes VideoCodecType() const; - - uint16_t FECPacketOverhead() const; - - int32_t RegisterVideoPayload( - const char payloadName[RTP_PAYLOAD_NAME_SIZE], - const int8_t payloadType, - const uint32_t maxBitRate, - ModuleRTPUtility::Payload*& payload); - - int32_t SendVideo(const RtpVideoCodecTypes videoType, - const FrameType frameType, - const int8_t payloadType, - const uint32_t captureTimeStamp, - int64_t capture_time_ms, - const uint8_t* payloadData, - const uint32_t payloadSize, - const RTPFragmentationHeader* fragmentation, - VideoCodecInformation* codecInfo, - const RTPVideoTypeHeader* rtpTypeHdr); - - int32_t SendRTPIntraRequest(); - - void SetVideoCodecType(RtpVideoCodecTypes type); - - VideoCodecInformation* CodecInformationVideo(); - - void SetMaxConfiguredBitrateVideo(const uint32_t maxBitrate); - - uint32_t MaxConfiguredBitrateVideo() const; - - // FEC - int32_t SetGenericFECStatus(const bool enable, - const uint8_t payloadTypeRED, - const uint8_t payloadTypeFEC); - - int32_t GenericFECStatus(bool& enable, - uint8_t& payloadTypeRED, - uint8_t& payloadTypeFEC) const; - - int32_t SetFecParameters(const FecProtectionParams* delta_params, - const FecProtectionParams* key_params); - - void ProcessBitrate(); - - uint32_t VideoBitrateSent() const; - uint32_t FecOverheadRate() const; - - int SelectiveRetransmissions() const; - int SetSelectiveRetransmissions(uint8_t settings); - -protected: - virtual int32_t SendVideoPacket(uint8_t* dataBuffer, - const uint16_t payloadLength, - const uint16_t rtpHeaderLength, - const uint32_t capture_timestamp, - int64_t capture_time_ms, - StorageType storage, - bool protect); - -private: - int32_t SendGeneric(const FrameType frame_type, - const int8_t payload_type, - const uint32_t capture_timestamp, - int64_t capture_time_ms, - const uint8_t* payload, const uint32_t size); +class RTPSenderVideo { + public: + RTPSenderVideo(Clock* clock, RTPSenderInterface* rtpSender); + virtual ~RTPSenderVideo(); + + virtual RtpVideoCodecTypes VideoCodecType() const; + + uint16_t FECPacketOverhead() const; + + int32_t RegisterVideoPayload(const char payloadName[RTP_PAYLOAD_NAME_SIZE], + const int8_t payloadType, + const uint32_t maxBitRate, + RtpUtility::Payload*& payload); - int32_t SendVP8(const FrameType frameType, + int32_t SendVideo(const RtpVideoCodecTypes videoType, + const FrameType frameType, const int8_t payloadType, const uint32_t captureTimeStamp, int64_t capture_time_ms, const uint8_t* payloadData, const uint32_t payloadSize, const RTPFragmentationHeader* fragmentation, + VideoCodecInformation* codecInfo, const RTPVideoTypeHeader* rtpTypeHdr); - int32_t SendH264(const FrameType frameType, - const int8_t payloadType, - const uint32_t captureTimeStamp, - int64_t capture_time_ms, - const uint8_t* payloadData, - const uint32_t payloadSize, - const RTPFragmentationHeader* fragmentation, - const RTPVideoTypeHeader* rtpTypeHdr); + int32_t SendRTPIntraRequest(); + + void SetVideoCodecType(RtpVideoCodecTypes type); + + VideoCodecInformation* CodecInformationVideo(); + + void SetMaxConfiguredBitrateVideo(const uint32_t maxBitrate); -private: - int32_t _id; - RTPSenderInterface& _rtpSender; - - CriticalSectionWrapper* _sendVideoCritsect; - RtpVideoCodecTypes _videoType; - VideoCodecInformation* _videoCodecInformation; - uint32_t _maxBitrate; - int32_t _retransmissionSettings; - - // FEC - ForwardErrorCorrection _fec; - bool _fecEnabled; - int8_t _payloadTypeRED; - int8_t _payloadTypeFEC; - unsigned int _numberFirstPartition; - FecProtectionParams delta_fec_params_; - FecProtectionParams key_fec_params_; - ProducerFec producer_fec_; - - // Bitrate used for FEC payload, RED headers, RTP headers for FEC packets - // and any padding overhead. - Bitrate _fecOverheadRate; - // Bitrate used for video payload and RTP headers - Bitrate _videoBitrate; + uint32_t MaxConfiguredBitrateVideo() const; + + // FEC + int32_t SetGenericFECStatus(const bool enable, + const uint8_t payloadTypeRED, + const uint8_t payloadTypeFEC); + + int32_t GenericFECStatus(bool& enable, + uint8_t& payloadTypeRED, + uint8_t& payloadTypeFEC) const; + + int32_t SetFecParameters(const FecProtectionParams* delta_params, + const FecProtectionParams* key_params); + + void ProcessBitrate(); + + uint32_t VideoBitrateSent() const; + uint32_t FecOverheadRate() const; + + int SelectiveRetransmissions() const; + int SetSelectiveRetransmissions(uint8_t settings); + + protected: + virtual int32_t SendVideoPacket(uint8_t* dataBuffer, + const uint16_t payloadLength, + const uint16_t rtpHeaderLength, + const uint32_t capture_timestamp, + int64_t capture_time_ms, + StorageType storage, + bool protect); + + private: + bool Send(const RtpVideoCodecTypes videoType, + const FrameType frameType, + const int8_t payloadType, + const uint32_t captureTimeStamp, + int64_t capture_time_ms, + const uint8_t* payloadData, + const uint32_t payloadSize, + const RTPFragmentationHeader* fragmentation, + const RTPVideoTypeHeader* rtpTypeHdr); + + private: + RTPSenderInterface& _rtpSender; + + CriticalSectionWrapper* _sendVideoCritsect; + RtpVideoCodecTypes _videoType; + VideoCodecInformation* _videoCodecInformation; + uint32_t _maxBitrate; + int32_t _retransmissionSettings; + + // FEC + ForwardErrorCorrection _fec; + bool _fecEnabled; + int8_t _payloadTypeRED; + int8_t _payloadTypeFEC; + unsigned int _numberFirstPartition; + FecProtectionParams delta_fec_params_; + FecProtectionParams key_fec_params_; + ProducerFec producer_fec_; + + // Bitrate used for FEC payload, RED headers, RTP headers for FEC packets + // and any padding overhead. + Bitrate _fecOverheadRate; + // Bitrate used for video payload and RTP headers + Bitrate _videoBitrate; }; } // namespace webrtc -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_SENDER_VIDEO_H_ +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_SENDER_VIDEO_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility.cc 2015-02-03 14:33:36.000000000 +0000 @@ -30,7 +30,7 @@ #endif #include "webrtc/system_wrappers/interface/tick_util.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #if (defined(_DEBUG) && defined(_WIN32) && (_MSC_VER >= 1400)) #define DEBUG_PRINT(...) \ @@ -66,7 +66,7 @@ return &null_receive_statistics; } -namespace ModuleRTPUtility { +namespace RtpUtility { enum { kRtcpExpectedVersion = 2, @@ -188,43 +188,16 @@ return 1 << exp; } -void RTPPayload::SetType(RtpVideoCodecTypes videoType) { - type = videoType; - - switch (type) { - case kRtpVideoGeneric: - break; - case kRtpVideoVp8: { - info.VP8.nonReferenceFrame = false; - info.VP8.beginningOfPartition = false; - info.VP8.partitionID = 0; - info.VP8.hasPictureID = false; - info.VP8.hasTl0PicIdx = false; - info.VP8.hasTID = false; - info.VP8.hasKeyIdx = false; - info.VP8.pictureID = -1; - info.VP8.tl0PicIdx = -1; - info.VP8.tID = -1; - info.VP8.layerSync = false; - info.VP8.frameWidth = 0; - info.VP8.frameHeight = 0; - break; - } - default: - break; - } -} - -RTPHeaderParser::RTPHeaderParser(const uint8_t* rtpData, - const uint32_t rtpDataLength) - : _ptrRTPDataBegin(rtpData), - _ptrRTPDataEnd(rtpData ? (rtpData + rtpDataLength) : NULL) { +RtpHeaderParser::RtpHeaderParser(const uint8_t* rtpData, + const size_t rtpDataLength) + : _ptrRTPDataBegin(rtpData), + _ptrRTPDataEnd(rtpData ? (rtpData + rtpDataLength) : NULL) { } -RTPHeaderParser::~RTPHeaderParser() { +RtpHeaderParser::~RtpHeaderParser() { } -bool RTPHeaderParser::RTCP() const { +bool RtpHeaderParser::RTCP() const { // 72 to 76 is reserved for RTP // 77 to 79 is not reserver but they are not assigned we will block them // for RTCP 200 SR == marker bit + 72 @@ -299,7 +272,7 @@ return RTCP; } -bool RTPHeaderParser::ParseRtcp(RTPHeader* header) const { +bool RtpHeaderParser::ParseRtcp(RTPHeader* header) const { assert(header != NULL); const ptrdiff_t length = _ptrRTPDataEnd - _ptrRTPDataBegin; @@ -328,7 +301,7 @@ return true; } -bool RTPHeaderParser::Parse(RTPHeader& header, +bool RtpHeaderParser::Parse(RTPHeader& header, RtpHeaderExtensionMap* ptrExtensionMap) const { const ptrdiff_t length = _ptrRTPDataEnd - _ptrRTPDataBegin; if (length < kRtpMinParseLength) { @@ -398,6 +371,10 @@ header.extension.hasAbsoluteSendTime = false; header.extension.absoluteSendTime = 0; + // May not be present in packet. + header.extension.hasAudioLevel = false; + header.extension.audioLevel = 0; + if (X) { /* RTP header extension, RFC 3550. 0 1 2 3 @@ -437,7 +414,7 @@ return true; } -void RTPHeaderParser::ParseOneByteExtensionHeader( +void RtpHeaderParser::ParseOneByteExtensionHeader( RTPHeader& header, const RtpHeaderExtensionMap* ptrExtensionMap, const uint8_t* ptrRTPDataExtensionEnd, @@ -453,98 +430,104 @@ // | ID | len | // +-+-+-+-+-+-+-+-+ + // Note that 'len' is the header extension element length, which is the + // number of bytes - 1. const uint8_t id = (*ptr & 0xf0) >> 4; const uint8_t len = (*ptr & 0x0f); ptr++; if (id == 15) { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1, - "Ext id: 15 encountered, parsing terminated."); + LOG(LS_WARNING) + << "RTP extension header 15 encountered. Terminate parsing."; return; } RTPExtensionType type; if (ptrExtensionMap->GetType(id, &type) != 0) { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "Failed to find extension id: %d", id); - return; - } - - switch (type) { - case kRtpExtensionTransmissionTimeOffset: { - if (len != 2) { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1, - "Incorrect transmission time offset len: %d", len); - return; + // If we encounter an unknown extension, just skip over it. + LOG(LS_WARNING) << "Failed to find extension id: " + << static_cast(id); + } else { + switch (type) { + case kRtpExtensionTransmissionTimeOffset: { + if (len != 2) { + LOG(LS_WARNING) << "Incorrect transmission time offset len: " + << len; + return; + } + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | ID | len=2 | transmission offset | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + int32_t transmissionTimeOffset = ptr[0] << 16; + transmissionTimeOffset += ptr[1] << 8; + transmissionTimeOffset += ptr[2]; + header.extension.transmissionTimeOffset = + transmissionTimeOffset; + if (transmissionTimeOffset & 0x800000) { + // Negative offset, correct sign for Word24 to Word32. + header.extension.transmissionTimeOffset |= 0xFF000000; + } + header.extension.hasTransmissionTimeOffset = true; + break; } - // 0 1 2 3 - // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | ID | len=2 | transmission offset | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - int32_t transmissionTimeOffset = *ptr++ << 16; - transmissionTimeOffset += *ptr++ << 8; - transmissionTimeOffset += *ptr++; - header.extension.transmissionTimeOffset = - transmissionTimeOffset; - if (transmissionTimeOffset & 0x800000) { - // Negative offset, correct sign for Word24 to Word32. - header.extension.transmissionTimeOffset |= 0xFF000000; + case kRtpExtensionAudioLevel: { + if (len != 0) { + LOG(LS_WARNING) << "Incorrect audio level len: " << len; + return; + } + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | ID | len=0 |V| level | 0x00 | 0x00 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // Parse out the fields but only use it for debugging for now. + // const uint8_t V = (*ptr & 0x80) >> 7; + // const uint8_t level = (*ptr & 0x7f); + // DEBUG_PRINT("RTP_AUDIO_LEVEL_UNIQUE_ID: ID=%u, len=%u, V=%u, + // level=%u", ID, len, V, level); + + header.extension.audioLevel = ptr[0]; + header.extension.hasAudioLevel = true; + break; } - header.extension.hasTransmissionTimeOffset = true; - break; - } - case kRtpExtensionAudioLevel: { - // --- Only used for debugging --- - // 0 1 2 3 - // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | ID | len=0 |V| level | 0x00 | 0x00 | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // - - // Parse out the fields but only use it for debugging for now. - // const uint8_t V = (*ptr & 0x80) >> 7; - // const uint8_t level = (*ptr & 0x7f); - // DEBUG_PRINT("RTP_AUDIO_LEVEL_UNIQUE_ID: ID=%u, len=%u, V=%u, - // level=%u", ID, len, V, level); - break; - } - case kRtpExtensionAbsoluteSendTime: { - if (len != 2) { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1, - "Incorrect absolute send time len: %d", len); + case kRtpExtensionAbsoluteSendTime: { + if (len != 2) { + LOG(LS_WARNING) << "Incorrect absolute send time len: " << len; + return; + } + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | ID | len=2 | absolute send time | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + uint32_t absoluteSendTime = ptr[0] << 16; + absoluteSendTime += ptr[1] << 8; + absoluteSendTime += ptr[2]; + header.extension.absoluteSendTime = absoluteSendTime; + header.extension.hasAbsoluteSendTime = true; + break; + } + default: { + LOG(LS_WARNING) << "Extension type not implemented: " << type; return; } - // 0 1 2 3 - // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | ID | len=2 | absolute send time | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - uint32_t absoluteSendTime = *ptr++ << 16; - absoluteSendTime += *ptr++ << 8; - absoluteSendTime += *ptr++; - header.extension.absoluteSendTime = absoluteSendTime; - header.extension.hasAbsoluteSendTime = true; - break; - } - default: { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "Extension type not implemented."); - return; } } + ptr += (len + 1); uint8_t num_bytes = ParsePaddingBytes(ptrRTPDataExtensionEnd, ptr); ptr += num_bytes; } } -uint8_t RTPHeaderParser::ParsePaddingBytes( - const uint8_t* ptrRTPDataExtensionEnd, - const uint8_t* ptr) const { - +uint8_t RtpHeaderParser::ParsePaddingBytes( + const uint8_t* ptrRTPDataExtensionEnd, + const uint8_t* ptr) const { uint8_t num_zero_bytes = 0; while (ptrRTPDataExtensionEnd - ptr > 0) { if (*ptr != 0) { @@ -555,214 +538,6 @@ } return num_zero_bytes; } - -// RTP payload parser -RTPPayloadParser::RTPPayloadParser(const RtpVideoCodecTypes videoType, - const uint8_t* payloadData, - uint16_t payloadDataLength, - int32_t id) - : - _id(id), - _dataPtr(payloadData), - _dataLength(payloadDataLength), - _videoType(videoType) { -} - -RTPPayloadParser::~RTPPayloadParser() { -} - -bool RTPPayloadParser::Parse(RTPPayload& parsedPacket) const { - parsedPacket.SetType(_videoType); - - switch (_videoType) { - case kRtpVideoGeneric: - return ParseGeneric(parsedPacket); - case kRtpVideoVp8: - return ParseVP8(parsedPacket); - default: - return false; - } -} - -bool RTPPayloadParser::ParseGeneric(RTPPayload& /*parsedPacket*/) const { - return false; -} - -// -// VP8 format: -// -// Payload descriptor -// 0 1 2 3 4 5 6 7 -// +-+-+-+-+-+-+-+-+ -// |X|R|N|S|PartID | (REQUIRED) -// +-+-+-+-+-+-+-+-+ -// X: |I|L|T|K| RSV | (OPTIONAL) -// +-+-+-+-+-+-+-+-+ -// I: | PictureID | (OPTIONAL) -// +-+-+-+-+-+-+-+-+ -// L: | TL0PICIDX | (OPTIONAL) -// +-+-+-+-+-+-+-+-+ -// T/K: |TID:Y| KEYIDX | (OPTIONAL) -// +-+-+-+-+-+-+-+-+ -// -// Payload header (considered part of the actual payload, sent to decoder) -// 0 1 2 3 4 5 6 7 -// +-+-+-+-+-+-+-+-+ -// |Size0|H| VER |P| -// +-+-+-+-+-+-+-+-+ -// | ... | -// + + - -bool RTPPayloadParser::ParseVP8(RTPPayload& parsedPacket) const { - RTPPayloadVP8* vp8 = &parsedPacket.info.VP8; - const uint8_t* dataPtr = _dataPtr; - int dataLength = _dataLength; - - // Parse mandatory first byte of payload descriptor - bool extension = (*dataPtr & 0x80) ? true : false; // X bit - vp8->nonReferenceFrame = (*dataPtr & 0x20) ? true : false; // N bit - vp8->beginningOfPartition = (*dataPtr & 0x10) ? true : false; // S bit - vp8->partitionID = (*dataPtr & 0x0F); // PartID field - - if (vp8->partitionID > 8) { - // Weak check for corrupt data: PartID MUST NOT be larger than 8. - return false; - } - - // Advance dataPtr and decrease remaining payload size - dataPtr++; - dataLength--; - - if (extension) { - const int parsedBytes = ParseVP8Extension(vp8, dataPtr, dataLength); - if (parsedBytes < 0) return false; - dataPtr += parsedBytes; - dataLength -= parsedBytes; - } - - if (dataLength <= 0) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "Error parsing VP8 payload descriptor; payload too short"); - return false; - } - - // Read P bit from payload header (only at beginning of first partition) - if (dataLength > 0 && vp8->beginningOfPartition && vp8->partitionID == 0) { - parsedPacket.frameType = (*dataPtr & 0x01) ? kPFrame : kIFrame; - } else { - parsedPacket.frameType = kPFrame; - } - if (0 != ParseVP8FrameSize(parsedPacket, dataPtr, dataLength)) { - return false; - } - parsedPacket.info.VP8.data = dataPtr; - parsedPacket.info.VP8.dataLength = dataLength; - return true; -} - -int RTPPayloadParser::ParseVP8FrameSize(RTPPayload& parsedPacket, - const uint8_t* dataPtr, - int dataLength) const { - if (parsedPacket.frameType != kIFrame) { - // Included in payload header for I-frames. - return 0; - } - if (dataLength < 10) { - // For an I-frame we should always have the uncompressed VP8 header - // in the beginning of the partition. - return -1; - } - RTPPayloadVP8* vp8 = &parsedPacket.info.VP8; - vp8->frameWidth = ((dataPtr[7] << 8) + dataPtr[6]) & 0x3FFF; - vp8->frameHeight = ((dataPtr[9] << 8) + dataPtr[8]) & 0x3FFF; - return 0; -} - -int RTPPayloadParser::ParseVP8Extension(RTPPayloadVP8* vp8, - const uint8_t* dataPtr, - int dataLength) const { - int parsedBytes = 0; - if (dataLength <= 0) return -1; - // Optional X field is present - vp8->hasPictureID = (*dataPtr & 0x80) ? true : false; // I bit - vp8->hasTl0PicIdx = (*dataPtr & 0x40) ? true : false; // L bit - vp8->hasTID = (*dataPtr & 0x20) ? true : false; // T bit - vp8->hasKeyIdx = (*dataPtr & 0x10) ? true : false; // K bit - - // Advance dataPtr and decrease remaining payload size - dataPtr++; - parsedBytes++; - dataLength--; - - if (vp8->hasPictureID) { - if (ParseVP8PictureID(vp8, &dataPtr, &dataLength, &parsedBytes) != 0) { - return -1; - } - } - - if (vp8->hasTl0PicIdx) { - if (ParseVP8Tl0PicIdx(vp8, &dataPtr, &dataLength, &parsedBytes) != 0) { - return -1; - } - } - - if (vp8->hasTID || vp8->hasKeyIdx) { - if (ParseVP8TIDAndKeyIdx(vp8, &dataPtr, &dataLength, &parsedBytes) != 0) { - return -1; - } - } - return parsedBytes; -} - -int RTPPayloadParser::ParseVP8PictureID(RTPPayloadVP8* vp8, - const uint8_t** dataPtr, - int* dataLength, - int* parsedBytes) const { - if (*dataLength <= 0) return -1; - vp8->pictureID = (**dataPtr & 0x7F); - if (**dataPtr & 0x80) { - (*dataPtr)++; - (*parsedBytes)++; - if (--(*dataLength) <= 0) return -1; - // PictureID is 15 bits - vp8->pictureID = (vp8->pictureID << 8) +** dataPtr; - } - (*dataPtr)++; - (*parsedBytes)++; - (*dataLength)--; - return 0; -} - -int RTPPayloadParser::ParseVP8Tl0PicIdx(RTPPayloadVP8* vp8, - const uint8_t** dataPtr, - int* dataLength, - int* parsedBytes) const { - if (*dataLength <= 0) return -1; - vp8->tl0PicIdx = **dataPtr; - (*dataPtr)++; - (*parsedBytes)++; - (*dataLength)--; - return 0; -} - -int RTPPayloadParser::ParseVP8TIDAndKeyIdx(RTPPayloadVP8* vp8, - const uint8_t** dataPtr, - int* dataLength, - int* parsedBytes) const { - if (*dataLength <= 0) return -1; - if (vp8->hasTID) { - vp8->tID = ((**dataPtr >> 6) & 0x03); - vp8->layerSync = (**dataPtr & 0x20) ? true : false; // Y bit - } - if (vp8->hasKeyIdx) { - vp8->keyIdx = (**dataPtr & 0x1F); - } - (*dataPtr)++; - (*parsedBytes)++; - (*dataLength)--; - return 0; -} - -} // namespace ModuleRTPUtility +} // namespace RtpUtility } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility.h 2015-02-03 14:33:36.000000000 +0000 @@ -28,8 +28,7 @@ RtpAudioFeedback* NullObjectRtpAudioFeedback(); ReceiveStatistics* NullObjectReceiveStatistics(); -namespace ModuleRTPUtility -{ +namespace RtpUtility { // January 1970, in NTP seconds. const uint32_t NTP_JAN_1970 = 2208988800UL; @@ -92,12 +91,10 @@ */ uint32_t BufferToUWord32(const uint8_t* dataBuffer); - class RTPHeaderParser - { + class RtpHeaderParser { public: - RTPHeaderParser(const uint8_t* rtpData, - const uint32_t rtpDataLength); - ~RTPHeaderParser(); + RtpHeaderParser(const uint8_t* rtpData, size_t rtpDataLength); + ~RtpHeaderParser(); bool RTCP() const; bool ParseRtcp(RTPHeader* header) const; @@ -118,98 +115,7 @@ const uint8_t* const _ptrRTPDataBegin; const uint8_t* const _ptrRTPDataEnd; }; - - enum FrameTypes - { - kIFrame, // key frame - kPFrame // Delta frame - }; - - struct RTPPayloadVP8 - { - bool nonReferenceFrame; - bool beginningOfPartition; - int partitionID; - bool hasPictureID; - bool hasTl0PicIdx; - bool hasTID; - bool hasKeyIdx; - int pictureID; - int tl0PicIdx; - int tID; - bool layerSync; - int keyIdx; - int frameWidth; - int frameHeight; - - const uint8_t* data; - uint16_t dataLength; - }; - - union RTPPayloadUnion - { - RTPPayloadVP8 VP8; - }; - - struct RTPPayload - { - void SetType(RtpVideoCodecTypes videoType); - - RtpVideoCodecTypes type; - FrameTypes frameType; - RTPPayloadUnion info; - }; - - // RTP payload parser - class RTPPayloadParser - { - public: - RTPPayloadParser(const RtpVideoCodecTypes payloadType, - const uint8_t* payloadData, - const uint16_t payloadDataLength, // Length w/o padding. - const int32_t id); - - ~RTPPayloadParser(); - - bool Parse(RTPPayload& parsedPacket) const; - - private: - bool ParseGeneric(RTPPayload& parsedPacket) const; - - bool ParseVP8(RTPPayload& parsedPacket) const; - - int ParseVP8Extension(RTPPayloadVP8 *vp8, - const uint8_t *dataPtr, - int dataLength) const; - - int ParseVP8PictureID(RTPPayloadVP8 *vp8, - const uint8_t **dataPtr, - int *dataLength, - int *parsedBytes) const; - - int ParseVP8Tl0PicIdx(RTPPayloadVP8 *vp8, - const uint8_t **dataPtr, - int *dataLength, - int *parsedBytes) const; - - int ParseVP8TIDAndKeyIdx(RTPPayloadVP8 *vp8, - const uint8_t **dataPtr, - int *dataLength, - int *parsedBytes) const; - - int ParseVP8FrameSize(RTPPayload& parsedPacket, - const uint8_t *dataPtr, - int dataLength) const; - - private: - int32_t _id; - const uint8_t* _dataPtr; - const uint16_t _dataLength; - const RtpVideoCodecTypes _videoType; - }; - -} // namespace ModuleRTPUtility - +} // namespace RtpUtility } // namespace webrtc #endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_UTILITY_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,288 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - - -/* - * This file conatins unit tests for the ModuleRTPUtility. - */ - -#include "testing/gtest/include/gtest/gtest.h" -#include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h" -#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -using ModuleRTPUtility::RTPPayloadParser; -using ModuleRTPUtility::RTPPayload; -using ModuleRTPUtility::RTPPayloadVP8; - -// Payload descriptor -// 0 1 2 3 4 5 6 7 -// +-+-+-+-+-+-+-+-+ -// |X|R|N|S|PartID | (REQUIRED) -// +-+-+-+-+-+-+-+-+ -// X: |I|L|T|K| RSV | (OPTIONAL) -// +-+-+-+-+-+-+-+-+ -// I: | PictureID | (OPTIONAL) -// +-+-+-+-+-+-+-+-+ -// L: | TL0PICIDX | (OPTIONAL) -// +-+-+-+-+-+-+-+-+ -// T/K: |TID:Y| KEYIDX | (OPTIONAL) -// +-+-+-+-+-+-+-+-+ -// -// Payload header -// 0 1 2 3 4 5 6 7 -// +-+-+-+-+-+-+-+-+ -// |Size0|H| VER |P| -// +-+-+-+-+-+-+-+-+ -// | Size1 | -// +-+-+-+-+-+-+-+-+ -// | Size2 | -// +-+-+-+-+-+-+-+-+ -// | Bytes 4..N of | -// | VP8 payload | -// : : -// +-+-+-+-+-+-+-+-+ -// | OPTIONAL RTP | -// | padding | -// : : -// +-+-+-+-+-+-+-+-+ - -void VerifyBasicHeader(const RTPPayloadVP8 &header, - bool N, bool S, int PartID) { - EXPECT_EQ(N, header.nonReferenceFrame); - EXPECT_EQ(S, header.beginningOfPartition); - EXPECT_EQ(PartID, header.partitionID); -} - -void VerifyExtensions(const RTPPayloadVP8 &header, - bool I, bool L, bool T, bool K) { - EXPECT_EQ(I, header.hasPictureID); - EXPECT_EQ(L, header.hasTl0PicIdx); - EXPECT_EQ(T, header.hasTID); - EXPECT_EQ(K, header.hasKeyIdx); -} - -TEST(ParseVP8Test, BasicHeader) { - uint8_t payload[4] = {0}; - payload[0] = 0x14; // Binary 0001 0100; S = 1, PartID = 4. - payload[1] = 0x01; // P frame. - - RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, payload, 4, 0); - - RTPPayload parsedPacket; - ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket)); - - EXPECT_EQ(ModuleRTPUtility::kPFrame, parsedPacket.frameType); - EXPECT_EQ(kRtpVideoVp8, parsedPacket.type); - - VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 1 /*S*/, 4 /*PartID*/); - VerifyExtensions(parsedPacket.info.VP8, 0 /*I*/, 0 /*L*/, 0 /*T*/, 0 /*K*/); - - EXPECT_EQ(payload + 1, parsedPacket.info.VP8.data); - EXPECT_EQ(4 - 1, parsedPacket.info.VP8.dataLength); -} - -TEST(ParseVP8Test, PictureID) { - uint8_t payload[10] = {0}; - payload[0] = 0xA0; - payload[1] = 0x80; - payload[2] = 17; - - RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, payload, 10, 0); - - RTPPayload parsedPacket; - ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket)); - - EXPECT_EQ(ModuleRTPUtility::kPFrame, parsedPacket.frameType); - EXPECT_EQ(kRtpVideoVp8, parsedPacket.type); - - VerifyBasicHeader(parsedPacket.info.VP8, 1 /*N*/, 0 /*S*/, 0 /*PartID*/); - VerifyExtensions(parsedPacket.info.VP8, 1 /*I*/, 0 /*L*/, 0 /*T*/, 0 /*K*/); - - EXPECT_EQ(17, parsedPacket.info.VP8.pictureID); - - EXPECT_EQ(payload + 3, parsedPacket.info.VP8.data); - EXPECT_EQ(10 - 3, parsedPacket.info.VP8.dataLength); - - - // Re-use payload, but change to long PictureID. - payload[2] = 0x80 | 17; - payload[3] = 17; - RTPPayloadParser rtpPayloadParser2(kRtpVideoVp8, payload, 10, 0); - - ASSERT_TRUE(rtpPayloadParser2.Parse(parsedPacket)); - - VerifyBasicHeader(parsedPacket.info.VP8, 1 /*N*/, 0 /*S*/, 0 /*PartID*/); - VerifyExtensions(parsedPacket.info.VP8, 1 /*I*/, 0 /*L*/, 0 /*T*/, 0 /*K*/); - - EXPECT_EQ((17<<8) + 17, parsedPacket.info.VP8.pictureID); - - EXPECT_EQ(payload + 4, parsedPacket.info.VP8.data); - EXPECT_EQ(10 - 4, parsedPacket.info.VP8.dataLength); -} - -TEST(ParseVP8Test, Tl0PicIdx) { - uint8_t payload[13] = {0}; - payload[0] = 0x90; - payload[1] = 0x40; - payload[2] = 17; - - RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, payload, 13, 0); - - RTPPayload parsedPacket; - ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket)); - - EXPECT_EQ(ModuleRTPUtility::kIFrame, parsedPacket.frameType); - EXPECT_EQ(kRtpVideoVp8, parsedPacket.type); - - VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 1 /*S*/, 0 /*PartID*/); - VerifyExtensions(parsedPacket.info.VP8, 0 /*I*/, 1 /*L*/, 0 /*T*/, 0 /*K*/); - - EXPECT_EQ(17, parsedPacket.info.VP8.tl0PicIdx); - - EXPECT_EQ(payload + 3, parsedPacket.info.VP8.data); - EXPECT_EQ(13 - 3, parsedPacket.info.VP8.dataLength); -} - -TEST(ParseVP8Test, TIDAndLayerSync) { - uint8_t payload[10] = {0}; - payload[0] = 0x88; - payload[1] = 0x20; - payload[2] = 0x80; // TID(2) + LayerSync(false) - - RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, payload, 10, 0); - - RTPPayload parsedPacket; - ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket)); - - EXPECT_EQ(ModuleRTPUtility::kPFrame, parsedPacket.frameType); - EXPECT_EQ(kRtpVideoVp8, parsedPacket.type); - - VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 0 /*S*/, 8 /*PartID*/); - VerifyExtensions(parsedPacket.info.VP8, 0 /*I*/, 0 /*L*/, 1 /*T*/, 0 /*K*/); - - EXPECT_EQ(2, parsedPacket.info.VP8.tID); - EXPECT_FALSE(parsedPacket.info.VP8.layerSync); - - EXPECT_EQ(payload + 3, parsedPacket.info.VP8.data); - EXPECT_EQ(10 - 3, parsedPacket.info.VP8.dataLength); -} - -TEST(ParseVP8Test, KeyIdx) { - uint8_t payload[10] = {0}; - payload[0] = 0x88; - payload[1] = 0x10; // K = 1. - payload[2] = 0x11; // KEYIDX = 17 decimal. - - RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, payload, 10, 0); - - RTPPayload parsedPacket; - ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket)); - - EXPECT_EQ(ModuleRTPUtility::kPFrame, parsedPacket.frameType); - EXPECT_EQ(kRtpVideoVp8, parsedPacket.type); - - VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 0 /*S*/, 8 /*PartID*/); - VerifyExtensions(parsedPacket.info.VP8, 0 /*I*/, 0 /*L*/, 0 /*T*/, 1 /*K*/); - - EXPECT_EQ(17, parsedPacket.info.VP8.keyIdx); - - EXPECT_EQ(payload + 3, parsedPacket.info.VP8.data); - EXPECT_EQ(10 - 3, parsedPacket.info.VP8.dataLength); -} - -TEST(ParseVP8Test, MultipleExtensions) { - uint8_t payload[10] = {0}; - payload[0] = 0x88; - payload[1] = 0x80 | 0x40 | 0x20 | 0x10; - payload[2] = 0x80 | 17; // PictureID, high 7 bits. - payload[3] = 17; // PictureID, low 8 bits. - payload[4] = 42; // Tl0PicIdx. - payload[5] = 0x40 | 0x20 | 0x11; // TID(1) + LayerSync(true) + KEYIDX(17). - - RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, payload, 10, 0); - - RTPPayload parsedPacket; - ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket)); - - EXPECT_EQ(ModuleRTPUtility::kPFrame, parsedPacket.frameType); - EXPECT_EQ(kRtpVideoVp8, parsedPacket.type); - - VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 0 /*S*/, 8 /*PartID*/); - VerifyExtensions(parsedPacket.info.VP8, 1 /*I*/, 1 /*L*/, 1 /*T*/, 1 /*K*/); - - EXPECT_EQ((17<<8) + 17, parsedPacket.info.VP8.pictureID); - EXPECT_EQ(42, parsedPacket.info.VP8.tl0PicIdx); - EXPECT_EQ(1, parsedPacket.info.VP8.tID); - EXPECT_EQ(17, parsedPacket.info.VP8.keyIdx); - - EXPECT_EQ(payload + 6, parsedPacket.info.VP8.data); - EXPECT_EQ(10 - 6, parsedPacket.info.VP8.dataLength); -} - -TEST(ParseVP8Test, TooShortHeader) { - uint8_t payload[4] = {0}; - payload[0] = 0x88; - payload[1] = 0x80 | 0x40 | 0x20 | 0x10; // All extensions are enabled... - payload[2] = 0x80 | 17; // ... but only 2 bytes PictureID is provided. - payload[3] = 17; // PictureID, low 8 bits. - - RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, payload, 4, 0); - - RTPPayload parsedPacket; - EXPECT_FALSE(rtpPayloadParser.Parse(parsedPacket)); -} - -TEST(ParseVP8Test, TestWithPacketizer) { - uint8_t payload[10] = {0}; - uint8_t packet[20] = {0}; - RTPVideoHeaderVP8 inputHeader; - inputHeader.nonReference = true; - inputHeader.pictureId = 300; - inputHeader.temporalIdx = 1; - inputHeader.layerSync = false; - inputHeader.tl0PicIdx = kNoTl0PicIdx; // Disable. - inputHeader.keyIdx = 31; - RtpFormatVp8 packetizer(payload, 10, inputHeader, 20); - bool last; - int send_bytes; - ASSERT_EQ(0, packetizer.NextPacket(packet, &send_bytes, &last)); - ASSERT_TRUE(last); - - RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, packet, send_bytes, 0); - - RTPPayload parsedPacket; - ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket)); - - EXPECT_EQ(ModuleRTPUtility::kIFrame, parsedPacket.frameType); - EXPECT_EQ(kRtpVideoVp8, parsedPacket.type); - - VerifyBasicHeader(parsedPacket.info.VP8, - inputHeader.nonReference /*N*/, - 1 /*S*/, - 0 /*PartID*/); - VerifyExtensions(parsedPacket.info.VP8, - 1 /*I*/, - 0 /*L*/, - 1 /*T*/, - 1 /*K*/); - - EXPECT_EQ(inputHeader.pictureId, parsedPacket.info.VP8.pictureID); - EXPECT_EQ(inputHeader.temporalIdx, parsedPacket.info.VP8.tID); - EXPECT_EQ(inputHeader.layerSync, parsedPacket.info.VP8.layerSync); - EXPECT_EQ(inputHeader.keyIdx, parsedPacket.info.VP8.keyIdx); - - EXPECT_EQ(packet + 5, parsedPacket.info.VP8.data); - EXPECT_EQ(send_bytes - 5, parsedPacket.info.VP8.dataLength); -} - -} // namespace diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/ssrc_database.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/ssrc_database.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/ssrc_database.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/ssrc_database.cc 2015-02-03 14:33:36.000000000 +0000 @@ -14,7 +14,6 @@ #include #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" #ifdef _WIN32 #include @@ -58,45 +57,12 @@ uint32_t ssrc = GenerateRandom(); -#ifndef WEBRTC_NO_STL - while(_ssrcMap.find(ssrc) != _ssrcMap.end()) { ssrc = GenerateRandom(); } _ssrcMap[ssrc] = 0; -#else - if(_sizeOfSSRC <= _numberOfSSRC) - { - // allocate more space - const int newSize = _sizeOfSSRC + 10; - uint32_t* tempSSRCVector = new uint32_t[newSize]; - memcpy(tempSSRCVector, _ssrcVector, _sizeOfSSRC*sizeof(uint32_t)); - delete [] _ssrcVector; - - _ssrcVector = tempSSRCVector; - _sizeOfSSRC = newSize; - } - - // check if in DB - if(_ssrcVector) - { - for (int i=0; i<_numberOfSSRC; i++) - { - if (_ssrcVector[i] == ssrc) - { - // we have a match - i = 0; // start over with a new ssrc - ssrc = GenerateRandom(); - } - - } - // add to database - _ssrcVector[_numberOfSSRC] = ssrc; - _numberOfSSRC++; - } -#endif return ssrc; } @@ -104,39 +70,7 @@ SSRCDatabase::RegisterSSRC(const uint32_t ssrc) { CriticalSectionScoped lock(_critSect); - -#ifndef WEBRTC_NO_STL - _ssrcMap[ssrc] = 0; - -#else - if(_sizeOfSSRC <= _numberOfSSRC) - { - // allocate more space - const int newSize = _sizeOfSSRC + 10; - uint32_t* tempSSRCVector = new uint32_t[newSize]; - memcpy(tempSSRCVector, _ssrcVector, _sizeOfSSRC*sizeof(uint32_t)); - delete [] _ssrcVector; - - _ssrcVector = tempSSRCVector; - _sizeOfSSRC = newSize; - } - // check if in DB - if(_ssrcVector) - { - for (int i=0; i<_numberOfSSRC; i++) - { - if (_ssrcVector[i] == ssrc) - { - // we have a match - return -1; - } - } - // add to database - _ssrcVector[_numberOfSSRC] = ssrc; - _numberOfSSRC++; - } -#endif return 0; } @@ -144,26 +78,7 @@ SSRCDatabase::ReturnSSRC(const uint32_t ssrc) { CriticalSectionScoped lock(_critSect); - -#ifndef WEBRTC_NO_STL _ssrcMap.erase(ssrc); - -#else - if(_ssrcVector) - { - for (int i=0; i<_numberOfSSRC; i++) - { - if (_ssrcVector[i] == ssrc) - { - // we have a match - // remove from database - _ssrcVector[i] = _ssrcVector[_numberOfSSRC-1]; - _numberOfSSRC--; - break; - } - } - } -#endif return 0; } @@ -179,26 +94,13 @@ srand(tv.tv_usec); #endif -#ifdef WEBRTC_NO_STL - _sizeOfSSRC = 10; - _numberOfSSRC = 0; - _ssrcVector = new uint32_t[10]; -#endif _critSect = CriticalSectionWrapper::CreateCriticalSection(); - - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, -1, "%s created", __FUNCTION__); } SSRCDatabase::~SSRCDatabase() { -#ifdef WEBRTC_NO_STL - delete [] _ssrcVector; -#else _ssrcMap.clear(); -#endif delete _critSect; - - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, -1, "%s deleted", __FUNCTION__); } uint32_t SSRCDatabase::GenerateRandom() diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/ssrc_database.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/ssrc_database.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/ssrc_database.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/ssrc_database.h 2015-02-03 14:33:36.000000000 +0000 @@ -11,9 +11,7 @@ #ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_SSRC_DATABASE_H_ #define WEBRTC_MODULES_RTP_RTCP_SOURCE_SSRC_DATABASE_H_ -#ifndef WEBRTC_NO_STL #include -#endif #include "webrtc/system_wrappers/interface/static_instance.h" #include "webrtc/typedefs.h" @@ -46,14 +44,7 @@ uint32_t GenerateRandom(); -#ifdef WEBRTC_NO_STL - int _numberOfSSRC; - int _sizeOfSSRC; - - uint32_t* _ssrcVector; -#else std::map _ssrcMap; -#endif CriticalSectionWrapper* _critSect; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/tmmbr_help.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/tmmbr_help.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/tmmbr_help.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/tmmbr_help.cc 2015-02-03 14:33:36.000000000 +0000 @@ -266,180 +266,177 @@ numBoundingSet++; } } - if (numBoundingSet != 1) - { - numBoundingSet = -1; - } - } else + return (numBoundingSet == 1) ? 1 : -1; + } + + // 1. Sort by increasing packetOH + for (int i = candidateSet.sizeOfSet() - 1; i >= 0; i--) { - // 1. Sort by increasing packetOH - for (int i = candidateSet.sizeOfSet() - 1; i >= 0; i--) + for (int j = 1; j <= i; j++) { - for (int j = 1; j <= i; j++) + if (candidateSet.PacketOH(j-1) > candidateSet.PacketOH(j)) { - if (candidateSet.PacketOH(j-1) > candidateSet.PacketOH(j)) - { - candidateSet.SwapEntries(j-1, j); - } + candidateSet.SwapEntries(j-1, j); } } - // 2. For tuples with same OH, keep the one w/ the lowest bitrate - for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++) + } + // 2. For tuples with same OH, keep the one w/ the lowest bitrate + for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++) + { + if (candidateSet.Tmmbr(i) > 0) { - if (candidateSet.Tmmbr(i) > 0) + // get min bitrate for packets w/ same OH + uint32_t currentPacketOH = candidateSet.PacketOH(i); + uint32_t currentMinTMMBR = candidateSet.Tmmbr(i); + uint32_t currentMinIndexTMMBR = i; + for (uint32_t j = i+1; j < candidateSet.sizeOfSet(); j++) { - // get min bitrate for packets w/ same OH - uint32_t currentPacketOH = candidateSet.PacketOH(i); - uint32_t currentMinTMMBR = candidateSet.Tmmbr(i); - uint32_t currentMinIndexTMMBR = i; - for (uint32_t j = i+1; j < candidateSet.sizeOfSet(); j++) + if(candidateSet.PacketOH(j) == currentPacketOH) { - if(candidateSet.PacketOH(j) == currentPacketOH) + if(candidateSet.Tmmbr(j) < currentMinTMMBR) { - if(candidateSet.Tmmbr(j) < currentMinTMMBR) - { - currentMinTMMBR = candidateSet.Tmmbr(j); - currentMinIndexTMMBR = j; - } + currentMinTMMBR = candidateSet.Tmmbr(j); + currentMinIndexTMMBR = j; } } - // keep lowest bitrate - for (uint32_t j = 0; j < candidateSet.sizeOfSet(); j++) + } + // keep lowest bitrate + for (uint32_t j = 0; j < candidateSet.sizeOfSet(); j++) + { + if(candidateSet.PacketOH(j) == currentPacketOH + && j != currentMinIndexTMMBR) { - if(candidateSet.PacketOH(j) == currentPacketOH - && j != currentMinIndexTMMBR) - { - candidateSet.ClearEntry(j); - } + candidateSet.ClearEntry(j); } } } - // 3. Select and remove tuple w/ lowest tmmbr. - // (If more than 1, choose the one w/ highest OH). - uint32_t minTMMBR = 0; - uint32_t minIndexTMMBR = 0; - for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++) + } + // 3. Select and remove tuple w/ lowest tmmbr. + // (If more than 1, choose the one w/ highest OH). + uint32_t minTMMBR = 0; + uint32_t minIndexTMMBR = 0; + for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++) + { + if (candidateSet.Tmmbr(i) > 0) { - if (candidateSet.Tmmbr(i) > 0) - { - minTMMBR = candidateSet.Tmmbr(i); - minIndexTMMBR = i; - break; - } + minTMMBR = candidateSet.Tmmbr(i); + minIndexTMMBR = i; + break; } + } - for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++) - { - if (candidateSet.Tmmbr(i) > 0 && candidateSet.Tmmbr(i) <= minTMMBR) - { - // get min bitrate - minTMMBR = candidateSet.Tmmbr(i); - minIndexTMMBR = i; - } - } - // first member of selected list - _boundingSet.SetEntry(numBoundingSet, - candidateSet.Tmmbr(minIndexTMMBR), - candidateSet.PacketOH(minIndexTMMBR), - candidateSet.Ssrc(minIndexTMMBR)); - - // set intersection value - _ptrIntersectionBoundingSet[numBoundingSet] = 0; - // calculate its maximum packet rate (where its line crosses x-axis) - _ptrMaxPRBoundingSet[numBoundingSet] - = _boundingSet.Tmmbr(numBoundingSet) * 1000 - / float(8 * _boundingSet.PacketOH(numBoundingSet)); - numBoundingSet++; - // remove from candidate list - candidateSet.ClearEntry(minIndexTMMBR); - numCandidates--; - - // 4. Discard from candidate list all tuple w/ lower OH - // (next tuple must be steeper) - for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++) + for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++) + { + if (candidateSet.Tmmbr(i) > 0 && candidateSet.Tmmbr(i) <= minTMMBR) { - if(candidateSet.Tmmbr(i) > 0 - && candidateSet.PacketOH(i) < _boundingSet.PacketOH(0)) - { - candidateSet.ClearEntry(i); - numCandidates--; - } + // get min bitrate + minTMMBR = candidateSet.Tmmbr(i); + minIndexTMMBR = i; } - - if (numCandidates == 0) + } + // first member of selected list + _boundingSet.SetEntry(numBoundingSet, + candidateSet.Tmmbr(minIndexTMMBR), + candidateSet.PacketOH(minIndexTMMBR), + candidateSet.Ssrc(minIndexTMMBR)); + + // set intersection value + _ptrIntersectionBoundingSet[numBoundingSet] = 0; + // calculate its maximum packet rate (where its line crosses x-axis) + _ptrMaxPRBoundingSet[numBoundingSet] + = _boundingSet.Tmmbr(numBoundingSet) * 1000 + / float(8 * _boundingSet.PacketOH(numBoundingSet)); + numBoundingSet++; + // remove from candidate list + candidateSet.ClearEntry(minIndexTMMBR); + numCandidates--; + + // 4. Discard from candidate list all tuple w/ lower OH + // (next tuple must be steeper) + for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++) + { + if(candidateSet.Tmmbr(i) > 0 + && candidateSet.PacketOH(i) < _boundingSet.PacketOH(0)) { - // Should be true already:_boundingSet.lengthOfSet = numBoundingSet; - assert(_boundingSet.lengthOfSet() == numBoundingSet); - return numBoundingSet; + candidateSet.ClearEntry(i); + numCandidates--; } + } - bool getNewCandidate = true; - int curCandidateTMMBR = 0; - int curCandidateIndex = 0; - int curCandidatePacketOH = 0; - int curCandidateSSRC = 0; - do + if (numCandidates == 0) + { + // Should be true already:_boundingSet.lengthOfSet = numBoundingSet; + assert(_boundingSet.lengthOfSet() == numBoundingSet); + return numBoundingSet; + } + + bool getNewCandidate = true; + int curCandidateTMMBR = 0; + int curCandidateIndex = 0; + int curCandidatePacketOH = 0; + int curCandidateSSRC = 0; + do + { + if (getNewCandidate) { - if (getNewCandidate) + // 5. Remove first remaining tuple from candidate list + for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++) { - // 5. Remove first remaining tuple from candidate list - for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++) + if (candidateSet.Tmmbr(i) > 0) { - if (candidateSet.Tmmbr(i) > 0) - { - curCandidateTMMBR = candidateSet.Tmmbr(i); - curCandidatePacketOH = candidateSet.PacketOH(i); - curCandidateSSRC = candidateSet.Ssrc(i); - curCandidateIndex = i; - candidateSet.ClearEntry(curCandidateIndex); - break; - } + curCandidateTMMBR = candidateSet.Tmmbr(i); + curCandidatePacketOH = candidateSet.PacketOH(i); + curCandidateSSRC = candidateSet.Ssrc(i); + curCandidateIndex = i; + candidateSet.ClearEntry(curCandidateIndex); + break; } } + } - // 6. Calculate packet rate and intersection of the current - // line with line of last tuple in selected list - float packetRate - = float(curCandidateTMMBR - - _boundingSet.Tmmbr(numBoundingSet-1))*1000 - / (8*(curCandidatePacketOH - - _boundingSet.PacketOH(numBoundingSet-1))); - - // 7. If the packet rate is equal or lower than intersection of - // last tuple in selected list, - // remove last tuple in selected list & go back to step 6 - if(packetRate <= _ptrIntersectionBoundingSet[numBoundingSet-1]) - { - // remove last tuple and goto step 6 - numBoundingSet--; - _boundingSet.ClearEntry(numBoundingSet); - _ptrIntersectionBoundingSet[numBoundingSet] = 0; - _ptrMaxPRBoundingSet[numBoundingSet] = 0; - getNewCandidate = false; - } else - { - // 8. If packet rate is lower than maximum packet rate of - // last tuple in selected list, add current tuple to selected - // list - if (packetRate < _ptrMaxPRBoundingSet[numBoundingSet-1]) - { - _boundingSet.SetEntry(numBoundingSet, - curCandidateTMMBR, - curCandidatePacketOH, - curCandidateSSRC); - _ptrIntersectionBoundingSet[numBoundingSet] = packetRate; - _ptrMaxPRBoundingSet[numBoundingSet] - = _boundingSet.Tmmbr(numBoundingSet)*1000 - / float(8*_boundingSet.PacketOH(numBoundingSet)); - numBoundingSet++; - } - numCandidates--; - getNewCandidate = true; + // 6. Calculate packet rate and intersection of the current + // line with line of last tuple in selected list + float packetRate + = float(curCandidateTMMBR + - _boundingSet.Tmmbr(numBoundingSet-1))*1000 + / (8*(curCandidatePacketOH + - _boundingSet.PacketOH(numBoundingSet-1))); + + // 7. If the packet rate is equal or lower than intersection of + // last tuple in selected list, + // remove last tuple in selected list & go back to step 6 + if(packetRate <= _ptrIntersectionBoundingSet[numBoundingSet-1]) + { + // remove last tuple and goto step 6 + numBoundingSet--; + _boundingSet.ClearEntry(numBoundingSet); + _ptrIntersectionBoundingSet[numBoundingSet] = 0; + _ptrMaxPRBoundingSet[numBoundingSet] = 0; + getNewCandidate = false; + } else + { + // 8. If packet rate is lower than maximum packet rate of + // last tuple in selected list, add current tuple to selected + // list + if (packetRate < _ptrMaxPRBoundingSet[numBoundingSet-1]) + { + _boundingSet.SetEntry(numBoundingSet, + curCandidateTMMBR, + curCandidatePacketOH, + curCandidateSSRC); + _ptrIntersectionBoundingSet[numBoundingSet] = packetRate; + _ptrMaxPRBoundingSet[numBoundingSet] + = _boundingSet.Tmmbr(numBoundingSet)*1000 + / float(8*_boundingSet.PacketOH(numBoundingSet)); + numBoundingSet++; } + numCandidates--; + getNewCandidate = true; + } + + // 9. Go back to step 5 if any tuple remains in candidate list + } while (numCandidates > 0); - // 9. Go back to step 5 if any tuple remains in candidate list - } while (numCandidates > 0); - } return numBoundingSet; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/vp8_partition_aggregator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/vp8_partition_aggregator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/vp8_partition_aggregator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/vp8_partition_aggregator.h 2015-02-03 14:33:36.000000000 +0000 @@ -13,8 +13,8 @@ #include +#include "webrtc/base/constructormagic.h" #include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/typedefs.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/BWEStandAlone/BWEStandAlone.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/BWEStandAlone/BWEStandAlone.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/BWEStandAlone/BWEStandAlone.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/BWEStandAlone/BWEStandAlone.cc 2015-02-03 14:33:36.000000000 +0000 @@ -33,14 +33,14 @@ protected: // Inherited from UdpTransportData virtual void IncomingRTPPacket(const int8_t* incomingRtpPacket, - const int32_t rtpPacketLength, - const int8_t* fromIP, - const uint16_t fromPort); + const int32_t rtpPacketLength, + const int8_t* fromIP, + const uint16_t fromPort) OVERRIDE; virtual void IncomingRTCPPacket(const int8_t* incomingRtcpPacket, - const int32_t rtcpPacketLength, - const int8_t* fromIP, - const uint16_t fromPort); + const int32_t rtcpPacketLength, + const int8_t* fromIP, + const uint16_t fromPort) OVERRIDE; private: RtpRtcp *_rtpMod; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/BWEStandAlone/TestSenderReceiver.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/BWEStandAlone/TestSenderReceiver.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/BWEStandAlone/TestSenderReceiver.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/BWEStandAlone/TestSenderReceiver.cc 2015-02-03 14:33:36.000000000 +0000 @@ -46,7 +46,6 @@ _loadGenerator(NULL), _isSender(false), _isReceiver(false), -_timeOut(false), _sendRecCB(NULL), _lastBytesReceived(0), _lastTime(-1) @@ -290,22 +289,6 @@ } -void TestSenderReceiver::OnPacketTimeout(const int32_t id) -{ - CriticalSectionScoped lock(_critSect); - - _timeOut = true; -} - - -void TestSenderReceiver::OnReceivedPacket(const int32_t id, - const RtpRtcpPacketType packetType) -{ - // do nothing - //printf("OnReceivedPacket\n"); - -} - int32_t TestSenderReceiver::OnReceivedPayloadData(const uint8_t* payloadData, const uint16_t payloadSize, const webrtc::WebRtcRTPHeader* rtpHeader) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/BWEStandAlone/TestSenderReceiver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/BWEStandAlone/TestSenderReceiver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/BWEStandAlone/TestSenderReceiver.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/BWEStandAlone/TestSenderReceiver.h 2015-02-03 14:33:36.000000000 +0000 @@ -68,49 +68,42 @@ int32_t SetPacketTimeout(const uint32_t timeoutMS); - bool timeOutTriggered () { return (_timeOut); }; - // Inherited from RtpFeedback - virtual int32_t OnInitializeDecoder(const int32_t id, - const int8_t payloadType, - const int8_t payloadName[RTP_PAYLOAD_NAME_SIZE], - const uint32_t frequency, - const uint8_t channels, - const uint32_t rate) { return(0);}; - - virtual void OnPacketTimeout(const int32_t id); - - virtual void OnReceivedPacket(const int32_t id, - const RtpRtcpPacketType packetType); - - virtual void OnPeriodicDeadOrAlive(const int32_t id, - const RTPAliveType alive) {}; - - virtual void OnIncomingSSRCChanged( const int32_t id, - const uint32_t SSRC) {}; - - virtual void OnIncomingCSRCChanged( const int32_t id, - const uint32_t CSRC, - const bool added) {}; + virtual int32_t OnInitializeDecoder( + const int32_t id, + const int8_t payloadType, + const int8_t payloadName[RTP_PAYLOAD_NAME_SIZE], + const uint32_t frequency, + const uint8_t channels, + const uint32_t rate) OVERRIDE { + return 0; + } + + virtual void OnIncomingSSRCChanged(const int32_t id, + const uint32_t SSRC) OVERRIDE {} + + virtual void OnIncomingCSRCChanged(const int32_t id, + const uint32_t CSRC, + const bool added) OVERRIDE {} // Inherited from RtpData - - virtual int32_t OnReceivedPayloadData(const uint8_t* payloadData, - const uint16_t payloadSize, - const webrtc::WebRtcRTPHeader* rtpHeader); + virtual int32_t OnReceivedPayloadData( + const uint8_t* payloadData, + const uint16_t payloadSize, + const webrtc::WebRtcRTPHeader* rtpHeader) OVERRIDE; // Inherited from UdpTransportData virtual void IncomingRTPPacket(const int8_t* incomingRtpPacket, - const int32_t rtpPacketLength, - const int8_t* fromIP, - const uint16_t fromPort); + const int32_t rtpPacketLength, + const int8_t* fromIP, + const uint16_t fromPort) OVERRIDE; virtual void IncomingRTCPPacket(const int8_t* incomingRtcpPacket, - const int32_t rtcpPacketLength, - const int8_t* fromIP, - const uint16_t fromPort); + const int32_t rtcpPacketLength, + const int8_t* fromIP, + const uint16_t fromPort) OVERRIDE; @@ -156,7 +149,6 @@ TestLoadGenerator* _loadGenerator; bool _isSender; bool _isReceiver; - bool _timeOut; SendRecCB * _sendRecCB; uint32_t _lastBytesReceived; int64_t _lastTime; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api_audio.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api_audio.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api_audio.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api_audio.cc 2015-02-03 14:33:36.000000000 +0000 @@ -28,7 +28,7 @@ virtual int32_t OnReceivedPayloadData( const uint8_t* payloadData, const uint16_t payloadSize, - const webrtc::WebRtcRTPHeader* rtpHeader) { + const webrtc::WebRtcRTPHeader* rtpHeader) OVERRIDE { if (rtpHeader->header.payloadType == 98 || rtpHeader->header.payloadType == 99) { EXPECT_EQ(4, payloadSize); @@ -67,7 +67,7 @@ const char payloadName[RTP_PAYLOAD_NAME_SIZE], const int frequency, const uint8_t channels, - const uint32_t rate) { + const uint32_t rate) OVERRIDE { if (payloadType == 96) { EXPECT_EQ(test_rate, rate) << "The rate should be 64K for this payloadType"; @@ -76,28 +76,6 @@ } }; -class AudioFeedback : public NullRtpAudioFeedback { - virtual void OnReceivedTelephoneEvent(const int32_t id, - const uint8_t event, - const bool end) { - static uint8_t expectedEvent = 0; - - if (end) { - uint8_t oldEvent = expectedEvent-1; - if (expectedEvent == 32) { - oldEvent = 15; - } - EXPECT_EQ(oldEvent, event); - } else { - EXPECT_EQ(expectedEvent, event); - expectedEvent++; - } - if (expectedEvent == 16) { - expectedEvent = 32; - } - } -}; - class RtpRtcpAudioTest : public ::testing::Test { protected: RtpRtcpAudioTest() : fake_clock(123456) { @@ -110,8 +88,8 @@ } ~RtpRtcpAudioTest() {} - virtual void SetUp() { - audioFeedback = new AudioFeedback(); + virtual void SetUp() OVERRIDE { + audioFeedback = new NullRtpAudioFeedback(); data_receiver1 = new VerifyingAudioReceiver(); data_receiver2 = new VerifyingAudioReceiver(); rtp_callback = new RTPCallback(); @@ -122,9 +100,9 @@ receive_statistics2_.reset(ReceiveStatistics::Create(&fake_clock)); rtp_payload_registry1_.reset(new RTPPayloadRegistry( - test_id, RTPPayloadStrategy::CreateStrategy(true))); + RTPPayloadStrategy::CreateStrategy(true))); rtp_payload_registry2_.reset(new RTPPayloadRegistry( - test_id, RTPPayloadStrategy::CreateStrategy(true))); + RTPPayloadStrategy::CreateStrategy(true))); RtpRtcp::Configuration configuration; configuration.id = test_id; @@ -155,7 +133,7 @@ rtp_receiver1_.get(), receive_statistics1_.get()); } - virtual void TearDown() { + virtual void TearDown() OVERRIDE { delete module1; delete module2; delete transport1; @@ -179,7 +157,7 @@ VerifyingAudioReceiver* data_receiver2; LoopBackTransport* transport1; LoopBackTransport* transport2; - AudioFeedback* audioFeedback; + NullRtpAudioFeedback* audioFeedback; RTPCallback* rtp_callback; uint32_t test_ssrc; uint32_t test_timestamp; @@ -189,7 +167,7 @@ }; TEST_F(RtpRtcpAudioTest, Basic) { - EXPECT_EQ(0, module1->SetSSRC(test_ssrc)); + module1->SetSSRC(test_ssrc); EXPECT_EQ(0, module1->SetStartTimestamp(test_timestamp)); // Test detection at the end of a DTMF tone. @@ -260,7 +238,7 @@ voice_codec.channels, (voice_codec.rate < 0) ? 0 : voice_codec.rate)); - EXPECT_EQ(0, module1->SetSSRC(test_ssrc)); + module1->SetSSRC(test_ssrc); EXPECT_EQ(0, module1->SetStartTimestamp(test_timestamp)); EXPECT_EQ(0, module1->SetSendingStatus(true)); @@ -333,7 +311,7 @@ voice_codec.channels, (voice_codec.rate < 0) ? 0 : voice_codec.rate)); - EXPECT_EQ(0, module1->SetSSRC(test_ssrc)); + module1->SetSSRC(test_ssrc); EXPECT_EQ(0, module1->SetStartTimestamp(test_timestamp)); EXPECT_EQ(0, module1->SetSendingStatus(true)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api.cc 2015-02-03 14:33:36.000000000 +0000 @@ -34,7 +34,7 @@ configuration.clock = &fake_clock; module = RtpRtcp::CreateRtpRtcp(configuration); rtp_payload_registry_.reset(new RTPPayloadRegistry( - test_id, RTPPayloadStrategy::CreateStrategy(true))); + RTPPayloadStrategy::CreateStrategy(true))); rtp_receiver_.reset(RtpReceiver::CreateAudioReceiver( test_id, &fake_clock, NULL, NULL, NULL, rtp_payload_registry_.get())); } @@ -80,7 +80,7 @@ } TEST_F(RtpRtcpAPITest, SSRC) { - EXPECT_EQ(0, module->SetSSRC(test_ssrc)); + module->SetSSRC(test_ssrc); EXPECT_EQ(test_ssrc, module->SSRC()); } @@ -99,10 +99,6 @@ EXPECT_EQ(0, module->SetCNAME("john.doe@test.test")); - char cName[RTCP_CNAME_SIZE]; - EXPECT_EQ(0, module->CNAME(cName)); - EXPECT_STRCASEEQ(cName, "john.doe@test.test"); - EXPECT_FALSE(module->TMMBR()); EXPECT_EQ(0, module->SetTMMBRStatus(true)); EXPECT_TRUE(module->TMMBR()); @@ -119,21 +115,22 @@ int rtx_mode = kRtxOff; const int kRtxPayloadType = 119; int payload_type = -1; - EXPECT_EQ(0, module->SetRTXSendStatus(kRtxRetransmitted, true, 1)); + module->SetRTXSendStatus(kRtxRetransmitted); module->SetRtxSendPayloadType(kRtxPayloadType); - EXPECT_EQ(0, module->RTXSendStatus(&rtx_mode, &ssrc, &payload_type)); + module->SetRtxSsrc(1); + module->RTXSendStatus(&rtx_mode, &ssrc, &payload_type); EXPECT_EQ(kRtxRetransmitted, rtx_mode); EXPECT_EQ(1u, ssrc); EXPECT_EQ(kRtxPayloadType, payload_type); rtx_mode = kRtxOff; - EXPECT_EQ(0, module->SetRTXSendStatus(kRtxOff, true, 0)); + module->SetRTXSendStatus(kRtxOff); payload_type = -1; module->SetRtxSendPayloadType(kRtxPayloadType); - EXPECT_EQ(0, module->RTXSendStatus(&rtx_mode, &ssrc, &payload_type)); + module->RTXSendStatus(&rtx_mode, &ssrc, &payload_type); EXPECT_EQ(kRtxOff, rtx_mode); - EXPECT_EQ(kRtxPayloadType ,payload_type); - EXPECT_EQ(0, module->SetRTXSendStatus(kRtxRetransmitted, false, 1)); - EXPECT_EQ(0, module->RTXSendStatus(&rtx_mode, &ssrc, &payload_type)); + EXPECT_EQ(kRtxPayloadType, payload_type); + module->SetRTXSendStatus(kRtxRetransmitted); + module->RTXSendStatus(&rtx_mode, &ssrc, &payload_type); EXPECT_EQ(kRtxRetransmitted, rtx_mode); EXPECT_EQ(kRtxPayloadType, payload_type); } @@ -141,7 +138,8 @@ TEST_F(RtpRtcpAPITest, RtxReceiver) { const uint32_t kRtxSsrc = 1; const int kRtxPayloadType = 119; - rtp_payload_registry_->SetRtxStatus(true, kRtxSsrc); + EXPECT_FALSE(rtp_payload_registry_->RtxEnabled()); + rtp_payload_registry_->SetRtxSsrc(kRtxSsrc); rtp_payload_registry_->SetRtxPayloadType(kRtxPayloadType); EXPECT_TRUE(rtp_payload_registry_->RtxEnabled()); RTPHeader rtx_header; @@ -150,8 +148,7 @@ EXPECT_TRUE(rtp_payload_registry_->IsRtx(rtx_header)); rtx_header.ssrc = 0; EXPECT_FALSE(rtp_payload_registry_->IsRtx(rtx_header)); - rtp_payload_registry_->SetRtxStatus(false, kRtxSsrc); - EXPECT_FALSE(rtp_payload_registry_->RtxEnabled()); rtx_header.ssrc = kRtxSsrc; - EXPECT_FALSE(rtp_payload_registry_->IsRtx(rtx_header)); + rtx_header.payloadType = 0; + EXPECT_TRUE(rtp_payload_registry_->IsRtx(rtx_header)); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api.h 2015-02-03 14:33:36.000000000 +0000 @@ -43,7 +43,7 @@ void DropEveryNthPacket(int n) { _packetLoss = n; } - virtual int SendPacket(int channel, const void *data, int len) { + virtual int SendPacket(int channel, const void *data, int len) OVERRIDE { _count++; if (_packetLoss > 0) { if ((_count % _packetLoss) == 0) { @@ -52,7 +52,9 @@ } RTPHeader header; scoped_ptr parser(RtpHeaderParser::Create()); - if (!parser->Parse(static_cast(data), len, &header)) { + if (!parser->Parse(static_cast(data), + static_cast(len), + &header)) { return -1; } PayloadUnion payload_specific; @@ -68,7 +70,7 @@ } return len; } - virtual int SendRTCPPacket(int channel, const void *data, int len) { + virtual int SendRTCPPacket(int channel, const void *data, int len) OVERRIDE { if (_rtpRtcpModule->IncomingRtcpPacket((const uint8_t*)data, len) < 0) { return -1; } @@ -89,7 +91,7 @@ virtual int32_t OnReceivedPayloadData( const uint8_t* payloadData, const uint16_t payloadSize, - const webrtc::WebRtcRTPHeader* rtpHeader) { + const webrtc::WebRtcRTPHeader* rtpHeader) OVERRIDE { EXPECT_LE(payloadSize, sizeof(_payloadData)); memcpy(_payloadData, payloadData, payloadSize); memcpy(&_rtpHeader, rtpHeader, sizeof(_rtpHeader)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api_rtcp.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api_rtcp.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api_rtcp.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api_rtcp.cc 2015-02-03 14:33:36.000000000 +0000 @@ -116,9 +116,9 @@ configuration.intra_frame_callback = myRTCPFeedback1; rtp_payload_registry1_.reset(new RTPPayloadRegistry( - test_id, RTPPayloadStrategy::CreateStrategy(true))); + RTPPayloadStrategy::CreateStrategy(true))); rtp_payload_registry2_.reset(new RTPPayloadRegistry( - test_id, RTPPayloadStrategy::CreateStrategy(true))); + RTPPayloadStrategy::CreateStrategy(true))); module1 = RtpRtcp::CreateRtpRtcp(configuration); @@ -152,8 +152,8 @@ EXPECT_EQ(0, module1->SetRTCPStatus(kRtcpCompound)); EXPECT_EQ(0, module2->SetRTCPStatus(kRtcpCompound)); - EXPECT_EQ(0, module2->SetSSRC(test_ssrc + 1)); - EXPECT_EQ(0, module1->SetSSRC(test_ssrc)); + module2->SetSSRC(test_ssrc + 1); + module1->SetSSRC(test_ssrc); EXPECT_EQ(0, module1->SetSequenceNumber(test_sequence_number)); EXPECT_EQ(0, module1->SetStartTimestamp(test_timestamp)); EXPECT_EQ(0, module1->SetCSRCs(test_CSRC, 2)); @@ -280,7 +280,6 @@ reportBlock.lastSR = 6; // Set report blocks. - EXPECT_EQ(-1, module1->AddRTCPReportBlock(test_CSRC[0], NULL)); EXPECT_EQ(0, module1->AddRTCPReportBlock(test_CSRC[0], &reportBlock)); reportBlock.lastSR= 7; @@ -319,7 +318,6 @@ // get all report blocks std::vector report_blocks; - EXPECT_EQ(-1, module1->RemoteRTCPStat(NULL)); EXPECT_EQ(0, module1->RemoteRTCPStat(&report_blocks)); ASSERT_EQ(1u, report_blocks.size()); const RTCPReportBlock& reportBlockReceived = report_blocks[0]; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api_video.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api_video.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api_video.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testAPI/test_api_video.cc 2015-02-03 14:33:36.000000000 +0000 @@ -28,7 +28,7 @@ protected: RtpRtcpVideoTest() : test_id_(123), - rtp_payload_registry_(0, RTPPayloadStrategy::CreateStrategy(false)), + rtp_payload_registry_(RTPPayloadStrategy::CreateStrategy(false)), test_ssrc_(3456), test_timestamp_(4567), test_sequence_number_(2345), @@ -51,7 +51,7 @@ test_id_, &fake_clock, receiver_, NULL, &rtp_payload_registry_)); EXPECT_EQ(0, video_module_->SetRTCPStatus(kRtcpCompound)); - EXPECT_EQ(0, video_module_->SetSSRC(test_ssrc_)); + video_module_->SetSSRC(test_ssrc_); rtp_receiver_->SetNACKStatus(kNackRtcp); EXPECT_EQ(0, video_module_->SetStorePacketsStatus(true, 600)); EXPECT_EQ(0, video_module_->SetSendingStatus(true)); @@ -83,11 +83,9 @@ uint32_t sequence_number) { dataBuffer[0] = static_cast(0x80); // version 2 dataBuffer[1] = static_cast(kPayloadType); - ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer + 2, - sequence_number); - ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer + 4, timestamp); - ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer + 8, - 0x1234); // SSRC. + RtpUtility::AssignUWord16ToBuffer(dataBuffer + 2, sequence_number); + RtpUtility::AssignUWord32ToBuffer(dataBuffer + 4, timestamp); + RtpUtility::AssignUWord32ToBuffer(dataBuffer + 8, 0x1234); // SSRC. int32_t rtpHeaderLength = 12; return rtpHeaderLength; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testFec/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testFec/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testFec/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testFec/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testFec/test_fec.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testFec/test_fec.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testFec/test_fec.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testFec/test_fec.cc 2015-02-03 14:33:36.000000000 +0000 @@ -105,9 +105,7 @@ ASSERT_EQ(12, kMaxMediaPackets[1]) << "Max media packets for bursty mode not " << "equal to 12."; - uint32_t id = 0; - ForwardErrorCorrection fec(id); - + ForwardErrorCorrection fec; ForwardErrorCorrection::PacketList mediaPacketList; ForwardErrorCorrection::PacketList fecPacketList; ForwardErrorCorrection::ReceivedPacketList toDecodeList; @@ -136,7 +134,7 @@ fclose(randomSeedFile); randomSeedFile = NULL; - uint16_t seqNum = static_cast(rand()); + uint16_t seqNum = 0; uint32_t timeStamp = static_cast(rand()); const uint32_t ssrc = static_cast(rand()); @@ -226,6 +224,11 @@ } // Construct media packets. + // Reset the sequence number here for each FEC code/mask tested + // below, to avoid sequence number wrap-around. In actual decoding, + // old FEC packets in list are dropped if sequence number wrap + // around is detected. This case is currently not handled below. + seqNum = 0; for (uint32_t i = 0; i < numMediaPackets; ++i) { mediaPacket = new ForwardErrorCorrection::Packet; mediaPacketList.push_back(mediaPacket); @@ -256,12 +259,10 @@ // Only push one (fake) frame to the FEC. mediaPacket->data[1] &= 0x7f; - ModuleRTPUtility::AssignUWord16ToBuffer(&mediaPacket->data[2], - seqNum); - ModuleRTPUtility::AssignUWord32ToBuffer(&mediaPacket->data[4], - timeStamp); - ModuleRTPUtility::AssignUWord32ToBuffer(&mediaPacket->data[8], - ssrc); + RtpUtility::AssignUWord16ToBuffer(&mediaPacket->data[2], seqNum); + RtpUtility::AssignUWord32ToBuffer(&mediaPacket->data[4], + timeStamp); + RtpUtility::AssignUWord32ToBuffer(&mediaPacket->data[8], ssrc); // Generate random values for payload for (int32_t j = 12; j < mediaPacket->length; ++j) { mediaPacket->data[j] = static_cast(rand() % 256); @@ -300,7 +301,7 @@ memcpy(receivedPacket->pkt->data, mediaPacket->data, mediaPacket->length); receivedPacket->seq_num = - ModuleRTPUtility::BufferToUWord16(&mediaPacket->data[2]); + RtpUtility::BufferToUWord16(&mediaPacket->data[2]); receivedPacket->is_fec = false; } mediaPacketIdx++; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testFec/test_packet_masks_metrics.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testFec/test_packet_masks_metrics.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testFec/test_packet_masks_metrics.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testFec/test_packet_masks_metrics.cc 2015-02-03 14:33:36.000000000 +0000 @@ -198,7 +198,7 @@ int RecoveredMediaPackets(int num_media_packets, int num_fec_packets, uint8_t* state) { - scoped_array state_tmp( + scoped_ptr state_tmp( new uint8_t[num_media_packets + num_fec_packets]); memcpy(state_tmp.get(), state, num_media_packets + num_fec_packets); int num_recovered_packets = 0; @@ -392,7 +392,7 @@ // (which containes the code size parameters/protection length). void ComputeMetricsForCode(CodeType code_type, int code_index) { - scoped_array prob_weight(new double[kNumLossModels]); + scoped_ptr prob_weight(new double[kNumLossModels]); memset(prob_weight.get() , 0, sizeof(double) * kNumLossModels); MetricsFecCode metrics_code; SetMetricsZero(&metrics_code); @@ -400,7 +400,7 @@ int num_media_packets = code_params_[code_index].num_media_packets; int num_fec_packets = code_params_[code_index].num_fec_packets; int tot_num_packets = num_media_packets + num_fec_packets; - scoped_array state(new uint8_t[tot_num_packets]); + scoped_ptr state(new uint8_t[tot_num_packets]); memset(state.get() , 0, tot_num_packets); int num_loss_configurations = static_cast(pow(2.0f, tot_num_packets)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/BUILD.gn 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,57 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../../build/webrtc.gni") + +source_set("utility") { + sources = [ + "interface/audio_frame_operations.h", + "interface/file_player.h", + "interface/file_recorder.h", + "interface/helpers_android.h", + "interface/process_thread.h", + "interface/rtp_dump.h", + "source/audio_frame_operations.cc", + "source/coder.cc", + "source/coder.h", + "source/file_player_impl.cc", + "source/file_player_impl.h", + "source/file_recorder_impl.cc", + "source/file_recorder_impl.h", + "source/helpers_android.cc", + "source/process_thread_impl.cc", + "source/process_thread_impl.h", + "source/rtp_dump_impl.cc", + "source/rtp_dump_impl.h", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ + "../../common_audio", + "../../system_wrappers", + "../audio_coding", + "../media_file", + ] + if (rtc_enable_video) { + sources += [ + "source/frame_scaler.cc", + "source/video_coder.cc", + "source/video_frames_queue.cc", + ] + + deps += [ "../video_coding" ] + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/interface/mock/mock_process_thread.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/interface/mock/mock_process_thread.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/interface/mock/mock_process_thread.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/interface/mock/mock_process_thread.h 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_UTILITY_INTERFACE_MOCK_PROCESS_THREAD_H_ +#define WEBRTC_MODULES_UTILITY_INTERFACE_MOCK_PROCESS_THREAD_H_ + +#include "webrtc/modules/utility/interface/process_thread.h" + +#include "testing/gmock/include/gmock/gmock.h" + +namespace webrtc { + +class MockProcessThread : public ProcessThread { + public: + MOCK_METHOD0(Start, int32_t()); + MOCK_METHOD0(Stop, int32_t()); + MOCK_METHOD1(RegisterModule, int32_t(Module* module)); + MOCK_METHOD1(DeRegisterModule, int32_t(const Module* module)); +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_UTILITY_INTERFACE_MOCK_PROCESS_THREAD_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -1,4 +1,5 @@ henrike@webrtc.org -pwestin@webrtc.org asapersson@webrtc.org -perkj@webrtc.org \ No newline at end of file +perkj@webrtc.org + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../android-webrtc.mk - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_utility -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := coder.cc \ - file_player_impl.cc \ - file_recorder_impl.cc \ - process_thread_impl.cc \ - rtp_dump_impl.cc \ - frame_scaler.cc \ - video_coder.cc \ - video_frames_queue.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) \ - '-DWEBRTC_MODULE_UTILITY_VIDEO' - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../interface \ - $(LOCAL_PATH)/../../audio_coding/main/interface \ - $(LOCAL_PATH)/../../media_file/interface \ - $(LOCAL_PATH)/../../video_coding/main/interface \ - $(LOCAL_PATH)/../../video_coding/codecs/interface \ - $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../../../common_video/vplib/main/interface \ - $(LOCAL_PATH)/../../../common_audio/resampler/include \ - $(LOCAL_PATH)/../../../system_wrappers/interface \ - external/webrtc - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) - - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/audio_frame_operations.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/audio_frame_operations.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/audio_frame_operations.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/audio_frame_operations.cc 2015-02-03 14:33:36.000000000 +0000 @@ -72,7 +72,6 @@ void AudioFrameOperations::Mute(AudioFrame& frame) { memset(frame.data_, 0, sizeof(int16_t) * frame.samples_per_channel_ * frame.num_channels_); - frame.energy_ = 0; } int AudioFrameOperations::Scale(float left, float right, AudioFrame& frame) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/audio_frame_operations_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/audio_frame_operations_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/audio_frame_operations_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/audio_frame_operations_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -142,17 +142,13 @@ TEST_F(AudioFrameOperationsTest, MuteSucceeds) { SetFrameData(&frame_, 1000, 1000); - frame_.energy_ = 1000 * 1000 * frame_.samples_per_channel_ * - frame_.num_channels_; AudioFrameOperations::Mute(frame_); AudioFrame muted_frame; muted_frame.samples_per_channel_ = 320; muted_frame.num_channels_ = 2; SetFrameData(&muted_frame, 0, 0); - muted_frame.energy_ = 0; VerifyFramesAreEqual(muted_frame, frame_); - EXPECT_EQ(muted_frame.energy_, frame_.energy_); } // TODO(andrew): should not allow negative scales. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/coder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/coder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/coder.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/coder.h 2015-02-03 14:33:36.000000000 +0000 @@ -27,11 +27,11 @@ int32_t SetEncodeCodec( const CodecInst& codecInst, - ACMAMRPackingFormat amrFormat = AMRBandwidthEfficient); + ACMAMRPackingFormat amrFormat = AMRBandwidthEfficient); int32_t SetDecodeCodec( const CodecInst& codecInst, - ACMAMRPackingFormat amrFormat = AMRBandwidthEfficient); + ACMAMRPackingFormat amrFormat = AMRBandwidthEfficient); int32_t Decode(AudioFrame& decodedAudio, uint32_t sampFreqHz, const int8_t* incomingPayload, int32_t payloadLength); @@ -42,12 +42,13 @@ uint32_t& encodedLengthInBytes); protected: - virtual int32_t SendData(FrameType frameType, - uint8_t payloadType, - uint32_t timeStamp, - const uint8_t* payloadData, - uint16_t payloadSize, - const RTPFragmentationHeader* fragmentation); + virtual int32_t SendData( + FrameType frameType, + uint8_t payloadType, + uint32_t timeStamp, + const uint8_t* payloadData, + uint16_t payloadSize, + const RTPFragmentationHeader* fragmentation) OVERRIDE; private: scoped_ptr _acm; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/file_player_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/file_player_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/file_player_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/file_player_impl.cc 2015-02-03 14:33:36.000000000 +0000 @@ -9,7 +9,7 @@ */ #include "webrtc/modules/utility/source/file_player_impl.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #ifdef WEBRTC_MODULE_UTILITY_VIDEO #include "frame_scaler.h" @@ -35,8 +35,6 @@ #ifdef WEBRTC_MODULE_UTILITY_VIDEO return new VideoFilePlayerImpl(instanceID, fileFormat); #else - WEBRTC_TRACE(kTraceError, kTraceFile, -1, - "Invalid file format: %d", kFileFormatAviFile); assert(false); return NULL; #endif @@ -114,10 +112,9 @@ { if(_codec.plfreq == 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVoice, _instanceID, - "FilePlayerImpl::Get10msAudioFromFile() playing not started!\ - codecFreq = %d, wantedFreq = %d", - _codec.plfreq, frequencyInHz); + LOG(LS_WARNING) << "Get10msAudioFromFile() playing not started!" + << " codec freq = " << _codec.plfreq + << ", wanted freq = " << frequencyInHz; return -1; } @@ -175,8 +172,7 @@ if(_resampler.ResetIfNeeded(unresampledAudioFrame.sample_rate_hz_, frequencyInHz, kResamplerSynchronous)) { - WEBRTC_TRACE(kTraceWarning, kTraceVoice, _instanceID, - "FilePlayerImpl::Get10msAudioFromFile() unexpected codec"); + LOG(LS_WARNING) << "Get10msAudioFromFile() unexpected codec."; // New sampling frequency. Update state. outLen = frequencyInHz / 100; @@ -214,8 +210,7 @@ _scaling = scaleFactor; return 0; } - WEBRTC_TRACE(kTraceWarning, kTraceVoice, _instanceID, - "FilePlayerImpl::SetAudioScaling() not allowed scale factor"); + LOG(LS_WARNING) << "SetAudioScaling() non-allowed scale factor."; return -1; } @@ -255,9 +250,8 @@ codecInstL16.pacsize = 160; } else { - WEBRTC_TRACE(kTraceError, kTraceVoice, _instanceID, - "FilePlayerImpl::StartPlayingFile() sample frequency\ - specifed not supported for PCM format."); + LOG(LS_ERROR) << "StartPlayingFile() sample frequency not " + << "supported for PCM format."; return -1; } @@ -266,12 +260,8 @@ startPosition, stopPosition) == -1) { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FilePlayerImpl::StartPlayingFile() failed to initialize file\ - %s playout.", fileName); + LOG(LS_WARNING) << "StartPlayingFile() failed to initialize " + << "pcm file " << fileName; return -1; } SetAudioScaling(volumeScaling); @@ -280,13 +270,8 @@ if (_fileModule.StartPlayingAudioFile(fileName, notification, loop, _fileFormat, codecInst) == -1) { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FilePlayerImpl::StartPlayingPreEncodedFile() failed to\ - initialize pre-encoded file %s playout.", - fileName); + LOG(LS_WARNING) << "StartPlayingFile() failed to initialize " + << "pre-encoded file " << fileName; return -1; } } else @@ -297,12 +282,8 @@ startPosition, stopPosition) == -1) { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FilePlayerImpl::StartPlayingFile() failed to initialize file\ - %s playout.", fileName); + LOG(LS_WARNING) << "StartPlayingFile() failed to initialize file " + << fileName; return -1; } SetAudioScaling(volumeScaling); @@ -350,12 +331,8 @@ codecInstL16.pacsize = 160; }else { - WEBRTC_TRACE( - kTraceError, - kTraceVoice, - _instanceID, - "FilePlayerImpl::StartPlayingFile() sample frequency specifed\ - not supported for PCM format."); + LOG(LS_ERROR) << "StartPlayingFile() sample frequency not " + << "supported for PCM format."; return -1; } if (_fileModule.StartPlayingAudioStream(sourceStream, notification, @@ -363,12 +340,8 @@ startPosition, stopPosition) == -1) { - WEBRTC_TRACE( - kTraceError, - kTraceVoice, - _instanceID, - "FilePlayerImpl::StartPlayingFile() failed to initialize stream\ - playout."); + LOG(LS_ERROR) << "StartPlayingFile() failed to initialize stream " + << "playout."; return -1; } @@ -377,12 +350,8 @@ if (_fileModule.StartPlayingAudioStream(sourceStream, notification, _fileFormat, codecInst) == -1) { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FilePlayerImpl::StartPlayingFile() failed to initialize stream\ - playout."); + LOG(LS_ERROR) << "StartPlayingFile() failed to initialize stream " + << "playout."; return -1; } } else { @@ -392,9 +361,8 @@ startPosition, stopPosition) == -1) { - WEBRTC_TRACE(kTraceError, kTraceVoice, _instanceID, - "FilePlayerImpl::StartPlayingFile() failed to initialize\ - stream playout."); + LOG(LS_ERROR) << "StartPlayingFile() failed to initialize stream " + << "playout."; return -1; } } @@ -430,23 +398,14 @@ { if ((_fileModule.codec_info(_codec) == -1)) { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FilePlayerImpl::StartPlayingFile() failed to retrieve Codec info\ - of file data."); + LOG(LS_WARNING) << "Failed to retrieve codec info of file data."; return -1; } if( STR_CASE_CMP(_codec.plname, "L16") != 0 && _audioDecoder.SetDecodeCodec(_codec,AMRFileStorage) == -1) { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FilePlayerImpl::StartPlayingFile() codec %s not supported", - _codec.plname); + LOG(LS_WARNING) << "SetUpAudioDecoder() codec " << _codec.plname + << " not supported."; return -1; } _numberOf10MsPerFrame = _codec.pacsize / (_codec.plfreq / 100); @@ -458,7 +417,7 @@ VideoFilePlayerImpl::VideoFilePlayerImpl(uint32_t instanceID, FileFormats fileFormat) : FilePlayerImpl(instanceID, fileFormat), - video_decoder_(new VideoCoder(instanceID)), + video_decoder_(new VideoCoder()), video_codec_info_(), _decodedVideoFrames(0), _encodedData(*new EncodedVideoData()), @@ -522,7 +481,7 @@ CriticalSectionScoped lock( _critSec); _decodedVideoFrames = 0; - video_decoder_.reset(new VideoCoder(_instanceID)); + video_decoder_.reset(new VideoCoder()); return FilePlayerImpl::StopPlayingFile(); } @@ -627,12 +586,7 @@ reinterpret_cast< int8_t*>(_encodedData.payloadData), encodedBufferLengthInBytes) != 0) { - WEBRTC_TRACE( - kTraceWarning, - kTraceVideo, - _instanceID, - "FilePlayerImpl::TimeUntilNextVideoFrame() error reading\ - video data"); + LOG(LS_WARNING) << "Error reading video data."; return -1; } _encodedData.payloadSize = encodedBufferLengthInBytes; @@ -685,23 +639,16 @@ { if (_fileModule.VideoCodecInst(video_codec_info_) != 0) { - WEBRTC_TRACE( - kTraceWarning, - kTraceVideo, - _instanceID, - "FilePlayerImpl::SetVideoDecoder() failed to retrieve Codec info of\ - file data."); + LOG(LS_WARNING) << "SetVideoDecoder() failed to retrieve codec info of " + << "file data."; return -1; } int32_t useNumberOfCores = 1; if (video_decoder_->SetDecodeCodec(video_codec_info_, useNumberOfCores) != 0) { - WEBRTC_TRACE(kTraceWarning, - kTraceVideo, - _instanceID, - "FilePlayerImpl::SetUpVideoDecoder() codec %s not supported", - video_codec_info_.plName); + LOG(LS_WARNING) << "SetUpVideoDecoder() codec " + << video_codec_info_.plName << " not supported."; return -1; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/file_player_unittests.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/file_player_unittests.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/file_player_unittests.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/file_player_unittests.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Unit tests for FilePlayer. + +#include "webrtc/modules/utility/interface/file_player.h" + +#include +#include + +#include "gflags/gflags.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/md5digest.h" +#include "webrtc/base/stringencode.h" +#include "webrtc/test/testsupport/fileutils.h" + +DEFINE_bool(file_player_output, false, "Generate reference files."); + +namespace webrtc { + +class FilePlayerTest : public ::testing::Test { + protected: + static const uint32_t kId = 0; + static const FileFormats kFileFormat = kFileFormatWavFile; + static const int kSampleRateHz = 8000; + + FilePlayerTest() + : player_(FilePlayer::CreateFilePlayer(kId, kFileFormat)), + output_file_(NULL) {} + + virtual void SetUp() OVERRIDE { + if (FLAGS_file_player_output) { + std::string output_file = + webrtc::test::OutputPath() + "file_player_unittest_out.pcm"; + output_file_ = fopen(output_file.c_str(), "wb"); + ASSERT_TRUE(output_file_ != NULL); + } + } + + virtual void TearDown() OVERRIDE { + if (output_file_) + fclose(output_file_); + } + + ~FilePlayerTest() { FilePlayer::DestroyFilePlayer(player_); } + + void PlayFileAndCheck(const std::string& input_file, + const std::string& ref_checksum, + int output_length_ms) { + const float kScaling = 1; + ASSERT_EQ(0, + player_->StartPlayingFile( + input_file.c_str(), false, 0, kScaling, 0, 0, NULL)); + rtc::Md5Digest checksum; + for (int i = 0; i < output_length_ms / 10; ++i) { + int16_t out[10 * kSampleRateHz / 1000] = {0}; + int num_samples; + EXPECT_EQ(0, + player_->Get10msAudioFromFile(out, num_samples, kSampleRateHz)); + checksum.Update(out, num_samples * sizeof(out[0])); + if (FLAGS_file_player_output) { + ASSERT_EQ(static_cast(num_samples), + fwrite(out, sizeof(out[0]), num_samples, output_file_)); + } + } + char checksum_result[rtc::Md5Digest::kSize]; + EXPECT_EQ(rtc::Md5Digest::kSize, + checksum.Finish(checksum_result, rtc::Md5Digest::kSize)); + EXPECT_EQ(ref_checksum, + rtc::hex_encode(checksum_result, sizeof(checksum_result))); + } + + FilePlayer* player_; + FILE* output_file_; +}; + +TEST_F(FilePlayerTest, PlayWavPcmuFile) { + const std::string kFileName = + test::ResourcePath("utility/encapsulated_pcmu_8khz", "wav"); + // The file is longer than this, but keeping the output shorter limits the + // runtime for the test. + const int kOutputLengthMs = 10000; + const std::string kRefChecksum = "c74e7fd432d439b1311e1c16815b3e9a"; + + PlayFileAndCheck(kFileName, kRefChecksum, kOutputLengthMs); +} + +TEST_F(FilePlayerTest, PlayWavPcm16File) { + const std::string kFileName = + test::ResourcePath("utility/encapsulated_pcm16b_8khz", "wav"); + // The file is longer than this, but keeping the output shorter limits the + // runtime for the test. + const int kOutputLengthMs = 10000; + const std::string kRefChecksum = "e41d7e1dac8aeae9f21e8e03cd7ecd71"; + + PlayFileAndCheck(kFileName, kRefChecksum, kOutputLengthMs); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/file_recorder_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/file_recorder_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/file_recorder_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/file_recorder_impl.cc 2015-02-03 14:33:36.000000000 +0000 @@ -12,7 +12,7 @@ #include "webrtc/engine_configurations.h" #include "webrtc/modules/media_file/interface/media_file.h" #include "webrtc/modules/utility/source/file_recorder_impl.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #ifdef WEBRTC_MODULE_UTILITY_VIDEO #include "critical_section_wrapper.h" @@ -38,8 +38,6 @@ #ifdef WEBRTC_MODULE_UTILITY_VIDEO return new AviRecorder(instanceID, fileFormat); #else - WEBRTC_TRACE(kTraceError, kTraceFile, -1, - "Invalid file format: %d", kFileFormatAviFile); assert(false); return NULL; #endif @@ -115,13 +113,8 @@ } if( retVal != 0) { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FileRecorder::StartRecording() failed to initialize file %s for\ - recording.", - fileName); + LOG(LS_WARNING) << "Failed to initialize file " << fileName + << " for recording."; if(IsRecording()) { @@ -152,12 +145,7 @@ } if( retVal != 0) { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FileRecorder::StartRecording() failed to initialize outStream for\ - recording."); + LOG(LS_WARNING) << "Failed to initialize outStream for recording."; if(IsRecording()) { @@ -184,12 +172,8 @@ { if (codec_info_.plfreq == 0) { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FileRecorder::RecordAudioToFile() recording audio is not turned\ - on"); + LOG(LS_WARNING) << "RecordAudioToFile() recording audio is not " + << "turned on."; return -1; } AudioFrame tempAudioFrame; @@ -250,13 +234,9 @@ if (_audioEncoder.Encode(*ptrAudioFrame, _audioBuffer, encodedLenInBytes) == -1) { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FileRecorder::RecordAudioToFile() codec %s not supported or\ - failed to encode stream", - codec_info_.plname); + LOG(LS_WARNING) << "RecordAudioToFile() codec " + << codec_info_.plname + << " not supported or failed to encode stream."; return -1; } } else { @@ -309,12 +289,8 @@ { if(_audioEncoder.SetEncodeCodec(codec_info_,_amrFormat) == -1) { - WEBRTC_TRACE( - kTraceError, - kTraceVoice, - _instanceID, - "FileRecorder::StartRecording() codec %s not supported", - codec_info_.plname); + LOG(LS_ERROR) << "SetUpAudioEncoder() codec " + << codec_info_.plname << " not supported."; return -1; } } @@ -352,7 +328,7 @@ _writtenAudioMS(0), _writtenVideoMS(0) { - _videoEncoder = new VideoCoder(instanceID); + _videoEncoder = new VideoCoder(); _frameScaler = new FrameScaler(); _videoFramesQueue = new VideoFramesQueue(); _thread = ThreadWrapper::CreateThread(Run, this, kNormalPriority, @@ -600,8 +576,8 @@ error = EncodeAndWriteVideoToFile( *frameToProcess); if( error != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, _instanceID, - "AviRecorder::Process() error writing to file."); + LOG(LS_ERROR) << "AviRecorder::Process() error writing to " + << "file."; break; } else { uint32_t frameLengthMS = 1000 / @@ -640,8 +616,7 @@ error = EncodeAndWriteVideoToFile( *frameToProcess); if(error != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, _instanceID, - "AviRecorder::Process() error writing to file."); + LOG(LS_ERROR) << "AviRecorder::Process() error writing to file."; } else { _writtenVideoMS += frameLengthMS; } @@ -692,17 +667,12 @@ (int8_t*)(_videoEncodedData.payloadData), _videoEncodedData.payloadSize)) { - WEBRTC_TRACE(kTraceError, kTraceVideo, _instanceID, - "Error writing AVI file"); + LOG(LS_ERROR) << "Error writing AVI file."; return -1; } } else { - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - _instanceID, - "FileRecorder::RecordVideoToFile() frame dropped by encoder bitrate\ - likely to low."); + LOG(LS_ERROR) << "FileRecorder::RecordVideoToFile() frame dropped by " + << "encoder, bitrate likely too low."; } return 0; } @@ -715,6 +685,8 @@ uint16_t millisecondsOfData, const TickTime* playoutTS) { + CriticalSectionScoped lock(_critSec); + if (!IsRecording()) { return -1; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/frame_scaler.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/frame_scaler.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/frame_scaler.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/frame_scaler.cc 2015-02-03 14:33:36.000000000 +0000 @@ -13,7 +13,6 @@ #ifdef WEBRTC_MODULE_UTILITY_VIDEO #include "webrtc/common_video/libyuv/include/scaler.h" -#include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/process_thread_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/process_thread_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/process_thread_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/process_thread_impl.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,7 +10,7 @@ #include "webrtc/modules/interface/module.h" #include "webrtc/modules/utility/source/process_thread_impl.h" -#include "webrtc/system_wrappers/interface/trace.h" + namespace webrtc { ProcessThread::~ProcessThread() @@ -32,14 +32,12 @@ _critSectModules(CriticalSectionWrapper::CreateCriticalSection()), _thread(NULL) { - WEBRTC_TRACE(kTraceMemory, kTraceUtility, -1, "%s created", __FUNCTION__); } ProcessThreadImpl::~ProcessThreadImpl() { delete _critSectModules; delete &_timeEvent; - WEBRTC_TRACE(kTraceMemory, kTraceUtility, -1, "%s deleted", __FUNCTION__); } int32_t ProcessThreadImpl::Start() @@ -101,9 +99,7 @@ } _modules.push_front(module); - WEBRTC_TRACE(kTraceInfo, kTraceUtility, -1, - "number of registered modules has increased to %d", - _modules.size()); + // Wake the thread calling ProcessThreadImpl::Process() to update the // waiting time. The waiting time for the just registered module may be // shorter than all other registered modules. @@ -119,9 +115,6 @@ if(module == *iter) { _modules.erase(iter); - WEBRTC_TRACE(kTraceInfo, kTraceUtility, -1, - "number of registered modules has decreased to %d", - _modules.size()); return 0; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/rtp_dump_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/rtp_dump_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/rtp_dump_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/rtp_dump_impl.cc 2015-02-03 14:33:36.000000000 +0000 @@ -14,7 +14,7 @@ #include #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #if defined(_WIN32) #include @@ -71,7 +71,6 @@ _file(*FileWrapper::Create()), _startTime(0) { - WEBRTC_TRACE(kTraceMemory, kTraceUtility, -1, "%s created", __FUNCTION__); } RtpDump::~RtpDump() @@ -84,7 +83,6 @@ _file.CloseFile(); delete &_file; delete _critSect; - WEBRTC_TRACE(kTraceMemory, kTraceUtility, -1, "%s deleted", __FUNCTION__); } int32_t RtpDumpImpl::Start(const char* fileNameUTF8) @@ -100,8 +98,7 @@ _file.CloseFile(); if (_file.OpenFile(fileNameUTF8, false, false, false) == -1) { - WEBRTC_TRACE(kTraceError, kTraceUtility, -1, - "failed to open the specified file"); + LOG(LS_ERROR) << "Failed to open file."; return -1; } @@ -113,8 +110,7 @@ sprintf(magic, "#!rtpplay%s \n", RTPFILE_VERSION); if (_file.WriteText(magic) == -1) { - WEBRTC_TRACE(kTraceError, kTraceUtility, -1, - "error writing to file"); + LOG(LS_ERROR) << "Error writing to file."; return -1; } @@ -129,8 +125,7 @@ memset(dummyHdr, 0, 16); if (!_file.Write(dummyHdr, sizeof(dummyHdr))) { - WEBRTC_TRACE(kTraceError, kTraceUtility, -1, - "error writing to file"); + LOG(LS_ERROR) << "Error writing to file."; return -1; } return 0; @@ -198,14 +193,12 @@ if (!_file.Write(&hdr, sizeof(hdr))) { - WEBRTC_TRACE(kTraceError, kTraceUtility, -1, - "error writing to file"); + LOG(LS_ERROR) << "Error writing to file."; return -1; } if (!_file.Write(packet, packetLength)) { - WEBRTC_TRACE(kTraceError, kTraceUtility, -1, - "error writing to file"); + LOG(LS_ERROR) << "Error writing to file."; return -1; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/rtp_dump_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/rtp_dump_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/rtp_dump_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/rtp_dump_impl.h 2015-02-03 14:33:36.000000000 +0000 @@ -22,10 +22,11 @@ RtpDumpImpl(); virtual ~RtpDumpImpl(); - virtual int32_t Start(const char* fileNameUTF8); - virtual int32_t Stop(); - virtual bool IsActive() const; - virtual int32_t DumpPacket(const uint8_t* packet, uint16_t packetLength); + virtual int32_t Start(const char* fileNameUTF8) OVERRIDE; + virtual int32_t Stop() OVERRIDE; + virtual bool IsActive() const OVERRIDE; + virtual int32_t DumpPacket(const uint8_t* packet, + uint16_t packetLength) OVERRIDE; private: // Return the system time in ms. inline uint32_t GetTimeInMS() const; @@ -35,7 +36,7 @@ inline uint16_t RtpDumpHtons(uint16_t x) const; // Return true if the packet starts with a valid RTCP header. - // Note: See ModuleRTPUtility::RTPHeaderParser::RTCP() for details on how + // Note: See RtpUtility::RtpHeaderParser::RTCP() for details on how // to determine if the packet is an RTCP packet. bool RTCP(const uint8_t* packet) const; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/video_coder.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/video_coder.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/video_coder.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/video_coder.cc 2015-02-03 14:33:36.000000000 +0000 @@ -13,10 +13,7 @@ #include "webrtc/modules/utility/source/video_coder.h" namespace webrtc { -VideoCoder::VideoCoder(uint32_t instanceID) - : _vcm(VideoCodingModule::Create(instanceID)), - _decodedVideo(0) -{ +VideoCoder::VideoCoder() : _vcm(VideoCodingModule::Create()), _decodedVideo(0) { _vcm->InitializeSender(); _vcm->InitializeReceiver(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/video_coder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/video_coder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/video_coder.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/video_coder.h 2015-02-03 14:33:36.000000000 +0000 @@ -20,7 +20,7 @@ class VideoCoder : public VCMPacketizationCallback, public VCMReceiveCallback { public: - VideoCoder(uint32_t instanceID); + VideoCoder(); ~VideoCoder(); int32_t SetEncodeCodec(VideoCodec& videoCodecInst, @@ -43,11 +43,11 @@ private: // VCMReceiveCallback function. // Note: called by VideoCodingModule when decoding finished. - int32_t FrameToRender(I420VideoFrame& videoFrame); + virtual int32_t FrameToRender(I420VideoFrame& videoFrame) OVERRIDE; // VCMPacketizationCallback function. // Note: called by VideoCodingModule when encoding finished. - int32_t SendData( + virtual int32_t SendData( FrameType /*frameType*/, uint8_t /*payloadType*/, uint32_t /*timeStamp*/, @@ -55,7 +55,7 @@ const uint8_t* payloadData, uint32_t payloadSize, const RTPFragmentationHeader& /* fragmentationHeader*/, - const RTPVideoHeader* rtpTypeHdr); + const RTPVideoHeader* rtpTypeHdr) OVERRIDE; VideoCodingModule* _vcm; I420VideoFrame* _decodedVideo; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/video_frames_queue.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/video_frames_queue.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/video_frames_queue.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/utility/source/video_frames_queue.cc 2015-02-03 14:33:36.000000000 +0000 @@ -16,8 +16,8 @@ #include "webrtc/common_video/interface/texture_video_frame.h" #include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/tick_util.h" -#include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { VideoFramesQueue::VideoFramesQueue() @@ -38,12 +38,7 @@ int32_t VideoFramesQueue::AddFrame(const I420VideoFrame& newFrame) { if (newFrame.native_handle() != NULL) { - _incomingFrames.push_back(new TextureVideoFrame( - static_cast(newFrame.native_handle()), - newFrame.width(), - newFrame.height(), - newFrame.timestamp(), - newFrame.render_time_ms())); + _incomingFrames.push_back(newFrame.CloneFrame()); return 0; } @@ -56,16 +51,9 @@ if (!ptrFrameToAdd) { if (_emptyFrames.size() + _incomingFrames.size() > KMaxNumberOfFrames) { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, -1, - "%s: too many frames, limit: %d", __FUNCTION__, - KMaxNumberOfFrames); + LOG(LS_WARNING) << "Too many frames, limit: " << KMaxNumberOfFrames; return -1; } - - WEBRTC_TRACE(kTraceMemory, kTraceVideoRenderer, -1, - "%s: allocating buffer %d", __FUNCTION__, - _emptyFrames.size() + _incomingFrames.size()); - ptrFrameToAdd = new I420VideoFrame(); } ptrFrameToAdd->CopyFrame(newFrame); @@ -87,8 +75,10 @@ // List is traversed beginning to end. If ptrRenderFrame is not // NULL it must be the first, and thus oldest, VideoFrame in the // queue. It can be recycled. - ReturnFrame(ptrRenderFrame); - iter = _incomingFrames.erase(iter); + if (ptrRenderFrame) { + ReturnFrame(ptrRenderFrame); + _incomingFrames.pop_front(); + } ptrRenderFrame = ptrOldestFrameInList; } else { // All VideoFrames following this one will be even newer. No match diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/device_info_android.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/device_info_android.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/device_info_android.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/device_info_android.cc 2015-02-03 14:33:36.000000000 +0000 @@ -22,36 +22,40 @@ #include "AndroidJNIWrapper.h" -namespace webrtc -{ +namespace webrtc { -namespace videocapturemodule -{ +namespace videocapturemodule { -static std::string ResolutionsToString( - const std::vector >& pairs) { +// Helper for storing lists of pairs of ints. Used e.g. for resolutions & FPS +// ranges. +typedef std::pair IntPair; +typedef std::vector IntPairs; + +static std::string IntPairsToString(const IntPairs& pairs, char separator) { std::stringstream stream; for (size_t i = 0; i < pairs.size(); ++i) { if (i > 0) stream << ", "; - stream << "(" << pairs[i].first << "x" << pairs[i].second << ")"; + stream << "(" << pairs[i].first << separator << pairs[i].second << ")"; } return stream.str(); } struct AndroidCameraInfo { std::string name; - int min_mfps, max_mfps; // FPS*1000. bool front_facing; int orientation; - std::vector > resolutions; // Pairs are: (width,height). + IntPairs resolutions; // Pairs are: (width,height). + // Pairs are (min,max) in units of FPS*1000 ("milli-frame-per-second"). + IntPairs mfpsRanges; std::string ToString() { std::stringstream stream; - stream << "Name: [" << name << "], mfps: [" << min_mfps << ":" << max_mfps + stream << "Name: [" << name << "], MFPS ranges: [" + << IntPairsToString(mfpsRanges, ':') << "], front_facing: " << front_facing << ", orientation: " << orientation << ", resolutions: [" - << ResolutionsToString(resolutions) << "]"; + << IntPairsToString(resolutions, 'x') << "]"; return stream.str(); } }; @@ -98,7 +102,6 @@ jclass j_cap_class = jsjni_GetGlobalClassRef("org/webrtc/videoengine/CaptureCapabilityAndroid"); assert(j_info_class); - assert(j_cap_class); jmethodID j_initialize = jni->GetStaticMethodID( j_info_class, "getDeviceInfo", "()[Lorg/webrtc/videoengine/CaptureCapabilityAndroid;"); @@ -139,10 +142,10 @@ info.name = std::string(camChars); jni->ReleaseStringUTFChars(camName, camChars); - info.min_mfps = jni->GetIntField(capabilityElement, minFpsField); - info.max_mfps = jni->GetIntField(capabilityElement, maxFpsField); info.orientation = jni->GetIntField(capabilityElement, orientationField); info.front_facing = jni->GetBooleanField(capabilityElement, frontFacingField); + jint min_mfps = jni->GetIntField(capabilityElement, minFpsField); + jint max_mfps = jni->GetIntField(capabilityElement, maxFpsField); jintArray widthResArray = static_cast(jni->GetObjectField(capabilityElement, widthField)); @@ -157,6 +160,8 @@ for (jsize j = 0; j < numRes; ++j) { info.resolutions.push_back(std::make_pair(widths[j], heights[j])); } + + info.mfpsRanges.push_back(std::make_pair(min_mfps, max_mfps)); g_camera_info->push_back(info); jni->ReleaseIntArrayElements(widthResArray, widths, JNI_ABORT); @@ -167,6 +172,13 @@ jni->DeleteGlobalRef(j_cap_class); } +void DeviceInfoAndroid::DeInitialize() { + if (g_camera_info) { + delete g_camera_info; + g_camera_info = NULL; + } +} + VideoCaptureModule::DeviceInfo* VideoCaptureImpl::CreateDeviceInfo( const int32_t id) { return new videocapturemodule::DeviceInfoAndroid(id); @@ -220,14 +232,17 @@ return -1; for (size_t i = 0; i < info->resolutions.size(); ++i) { - const std::pair& size = info->resolutions[i]; - VideoCaptureCapability cap; - cap.width = size.first; - cap.height = size.second; - cap.maxFPS = info->max_mfps / 1000; - cap.expectedCaptureDelay = kExpectedCaptureDelay; - cap.rawType = kVideoNV21; - _captureCapabilities.push_back(cap); + for (size_t j = 0; j < info->mfpsRanges.size(); ++j) { + const IntPair& size = info->resolutions[i]; + const IntPair& mfpsRange = info->mfpsRanges[j]; + VideoCaptureCapability cap; + cap.width = size.first; + cap.height = size.second; + cap.maxFPS = mfpsRange.second / 1000; + cap.expectedCaptureDelay = kExpectedCaptureDelay; + cap.rawType = kVideoNV21; + _captureCapabilities.push_back(cap); + } } return _captureCapabilities.size(); } @@ -237,19 +252,35 @@ VideoCaptureRotation& orientation) { const AndroidCameraInfo* info = FindCameraInfoByName(deviceUniqueIdUTF8); if (info == NULL || - !VideoCaptureImpl::RotationFromDegrees(info->orientation, &orientation)) { + VideoCaptureImpl::RotationFromDegrees(info->orientation, + &orientation) != 0) { return -1; } return 0; } -void DeviceInfoAndroid::GetFpsRange(const char* deviceUniqueIdUTF8, - int* min_mfps, int* max_mfps) { +void DeviceInfoAndroid::GetMFpsRange(const char* deviceUniqueIdUTF8, + int max_fps_to_match, + int* min_mfps, int* max_mfps) { const AndroidCameraInfo* info = FindCameraInfoByName(deviceUniqueIdUTF8); if (info == NULL) return; - *min_mfps = info->min_mfps; - *max_mfps = info->max_mfps; + int desired_mfps = max_fps_to_match * 1000; + int best_diff_mfps = 0; + LOG(LS_INFO) << "Search for best target mfps " << desired_mfps; + // Search for best fps range with preference shifted to constant fps modes. + for (size_t i = 0; i < info->mfpsRanges.size(); ++i) { + int diff_mfps = abs(info->mfpsRanges[i].first - desired_mfps) + + abs(info->mfpsRanges[i].second - desired_mfps) + + (info->mfpsRanges[i].second - info->mfpsRanges[i].first) / 2; + LOG(LS_INFO) << "Fps range " << info->mfpsRanges[i].first << ":" << + info->mfpsRanges[i].second << ". Distance: " << diff_mfps; + if (i == 0 || diff_mfps < best_diff_mfps) { + best_diff_mfps = diff_mfps; + *min_mfps = info->mfpsRanges[i].first; + *max_mfps = info->mfpsRanges[i].second; + } + } } } // namespace videocapturemodule diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/device_info_android.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/device_info_android.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/device_info_android.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/device_info_android.h 2015-02-03 14:33:36.000000000 +0000 @@ -27,6 +27,7 @@ class DeviceInfoAndroid : public DeviceInfoImpl { public: static void Initialize(JNIEnv* env); + static void DeInitialize(); DeviceInfoAndroid(int32_t id); virtual ~DeviceInfoAndroid(); @@ -56,10 +57,12 @@ virtual int32_t GetOrientation(const char* deviceUniqueIdUTF8, VideoCaptureRotation& orientation); - // Populate |min_mfps| and |max_mfps| with the supported range of the device. - void GetFpsRange(const char* deviceUniqueIdUTF8, - int* min_mfps, - int* max_mfps); + // Populate |min_mfps| and |max_mfps| with the closest supported range of the + // device to |max_fps_to_match|. + void GetMFpsRange(const char* deviceUniqueIdUTF8, + int max_fps_to_match, + int* min_mfps, + int* max_mfps); private: enum { kExpectedCaptureDelay = 190}; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java 2015-02-03 14:33:36.000000000 +0000 @@ -11,21 +11,26 @@ package org.webrtc.videoengine; import java.io.IOException; -import java.util.Locale; +import java.util.ArrayList; import java.util.List; -import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.Exchanger; +import android.content.Context; import android.graphics.ImageFormat; -import android.graphics.PixelFormat; -import android.graphics.Rect; import android.graphics.SurfaceTexture; -import android.graphics.YuvImage; -import android.hardware.Camera; +import android.hardware.Camera.Parameters; import android.hardware.Camera.PreviewCallback; +import android.hardware.Camera; +import android.opengl.GLES11Ext; +import android.opengl.GLES20; +import android.os.Handler; +import android.os.Looper; +import android.os.SystemClock; import android.util.Log; import android.view.Surface; -import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; +import android.view.SurfaceHolder; +import android.view.WindowManager; import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.GeckoAppShell.AppStateListener; @@ -37,17 +42,23 @@ // the entry points to this class are all synchronized. This shouldn't present // a performance bottleneck because only onPreviewFrame() is called more than // once (and is called serially on a single thread), so the lock should be -// uncontended. +// uncontended. Note that each of these synchronized methods must check +// |camera| for null to account for having possibly waited for stopCapture() to +// complete. public class VideoCaptureAndroid implements PreviewCallback, Callback, AppStateListener { private final static String TAG = "WEBRTC-JC"; + private static SurfaceHolder localPreview; // Only non-null while capturing, accessed exclusively from synchronized methods. Camera camera; private Camera.CameraInfo info; + private CameraThread cameraThread; + private Handler cameraThreadHandler; + private Context context; private final int id; private final long native_capturer; // |VideoCaptureAndroid*| in C++. - private SurfaceHolder localPreview; - private SurfaceTexture dummySurfaceTexture; + private SurfaceTexture cameraSurfaceTexture; + private int[] cameraGlTextures = null; // Arbitrary queue depth. Higher number means more memory allocated & held, // lower number means more sensitivity to processing time in the client (and @@ -64,10 +75,24 @@ // due to the application being backgrounded. boolean mResumeCapture; + private double averageDurationMs; + private long lastCaptureTimeMs; + private int frameCount; + private int frameDropRatio; + + // Requests future capturers to send their frames to |localPreview| directly. + public static void setLocalPreview(SurfaceHolder localPreview) { + // It is a gross hack that this is a class-static. Doing it right would + // mean plumbing this through the C++ API and using it from + // webrtc/examples/android/media_demo's MediaEngine class. + VideoCaptureAndroid.localPreview = localPreview; + } + @WebRTCJNITarget public VideoCaptureAndroid(int id, long native_capturer) { this.id = id; this.native_capturer = native_capturer; + this.context = GetContext(); if(android.os.Build.VERSION.SDK_INT>8) { this.info = new Camera.CameraInfo(); Camera.getCameraInfo(id, info); @@ -122,16 +147,54 @@ } } + // Return the global application context. + private static native Context GetContext(); + + private class CameraThread extends Thread { + private Exchanger handlerExchanger; + public CameraThread(Exchanger handlerExchanger) { + this.handlerExchanger = handlerExchanger; + } + + @Override public void run() { + Looper.prepare(); + exchange(handlerExchanger, new Handler()); + Looper.loop(); + } + } + // Called by native code. Returns true if capturer is started. // - // Note that this actually opens the camera, which can be a slow operation and - // thus might be done on a background thread, but ViE API needs a - // synchronous success return value so we can't do that. + // Note that this actually opens the camera, and Camera callbacks run on the + // thread that calls open(), so this is done on the CameraThread. Since ViE + // API needs a synchronous success return value we wait for the result. @WebRTCJNITarget private synchronized boolean startCapture( - int width, int height, int min_mfps, int max_mfps) { + final int width, final int height, + final int min_mfps, final int max_mfps) { Log.d(TAG, "startCapture: " + width + "x" + height + "@" + min_mfps + ":" + max_mfps); + if (cameraThread != null || cameraThreadHandler != null) { + throw new RuntimeException("Camera thread already started!"); + } + Exchanger handlerExchanger = new Exchanger(); + cameraThread = new CameraThread(handlerExchanger); + cameraThread.start(); + cameraThreadHandler = exchange(handlerExchanger, null); + + final Exchanger result = new Exchanger(); + cameraThreadHandler.post(new Runnable() { + @Override public void run() { + startCaptureOnCameraThread(width, height, min_mfps, max_mfps, result); + } + }); + boolean startResult = exchange(result, false); // |false| is a dummy value. + return startResult; + } + + private void startCaptureOnCameraThread( + int width, int height, int min_mfps, int max_mfps, + Exchanger result) { if (!mResumeCapture) { ViERenderer.CreateLocalRenderer(); } @@ -143,7 +206,6 @@ camera = Camera.open(); } - localPreview = ViERenderer.GetLocalRenderer(); if (localPreview != null) { localPreview.addCallback(this); if (localPreview.getSurface() != null && @@ -154,13 +216,28 @@ if(android.os.Build.VERSION.SDK_INT>10) { // No local renderer (we only care about onPreviewFrame() buffers, not a // directly-displayed UI element). Camera won't capture without - // setPreview{Texture,Display}, so we create a dummy SurfaceTexture and - // hand it over to Camera, but never listen for frame-ready callbacks, + // setPreview{Texture,Display}, so we create a SurfaceTexture and hand + // it over to Camera, but never listen for frame-ready callbacks, // and never call updateTexImage on it. try { - // "42" because http://goo.gl/KaEn8 - dummySurfaceTexture = new SurfaceTexture(42); - camera.setPreviewTexture(dummySurfaceTexture); + cameraGlTextures = new int[1]; + + // Generate one texture pointer and bind it as an external texture. + GLES20.glGenTextures(1, cameraGlTextures, 0); + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, + cameraGlTextures[0]); + GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, + GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, + GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, + GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, + GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + + cameraSurfaceTexture = new SurfaceTexture(cameraGlTextures[0]); + cameraSurfaceTexture.setOnFrameAvailableListener(null); + camera.setPreviewTexture(cameraSurfaceTexture); } catch (IOException e) { throw new RuntimeException(e); } @@ -169,6 +246,8 @@ } } + Log.d(TAG, "Camera orientation: " + info.orientation + + " .Device orientation: " + getDeviceOrientation()); Camera.Parameters parameters = camera.getParameters(); // This wasn't added until ICS MR1. if(android.os.Build.VERSION.SDK_INT>14) { @@ -185,12 +264,44 @@ parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); } } + parameters.setPictureSize(width, height); parameters.setPreviewSize(width, height); + + // Check if requested fps range is supported by camera, + // otherwise calculate frame drop ratio. + List supportedFpsRanges = parameters.getSupportedPreviewFpsRange(); + frameDropRatio = Integer.MAX_VALUE; + for (int i = 0; i < supportedFpsRanges.size(); i++) { + int[] range = supportedFpsRanges.get(i); + if (range[Parameters.PREVIEW_FPS_MIN_INDEX] == min_mfps && + range[Parameters.PREVIEW_FPS_MAX_INDEX] == max_mfps) { + frameDropRatio = 1; + break; + } + if (range[Parameters.PREVIEW_FPS_MIN_INDEX] % min_mfps == 0 && + range[Parameters.PREVIEW_FPS_MAX_INDEX] % max_mfps == 0) { + int dropRatio = range[Parameters.PREVIEW_FPS_MAX_INDEX] / max_mfps; + frameDropRatio = Math.min(dropRatio, frameDropRatio); + } + } + if (frameDropRatio == Integer.MAX_VALUE) { + Log.e(TAG, "Can not find camera fps range"); + error = new RuntimeException("Can not find camera fps range"); + exchange(result, false); + return; + } + if (frameDropRatio > 1) { + Log.d(TAG, "Frame dropper is enabled. Ratio: " + frameDropRatio); + } + min_mfps *= frameDropRatio; + max_mfps *= frameDropRatio; + Log.d(TAG, "Camera preview mfps range: " + min_mfps + " - " + max_mfps); if (android.os.Build.VERSION.SDK_INT>8) { parameters.setPreviewFpsRange(min_mfps, max_mfps); } else { parameters.setPreviewFrameRate(max_mfps / 1000); } + int format = ImageFormat.NV21; parameters.setPreviewFormat(format); camera.setParameters(parameters); @@ -199,6 +310,8 @@ camera.addCallbackBuffer(new byte[bufSize]); } camera.setPreviewCallbackWithBuffer(this); + frameCount = 0; + averageDurationMs = 1000000.0f / (max_mfps / frameDropRatio); camera.startPreview(); // Remember parameters we were started with. mCaptureWidth = width; @@ -209,7 +322,8 @@ if (!mResumeCapture) { GeckoAppShell.getGeckoInterface().addAppStateListener(this); } - return true; + exchange(result, true); + return; } catch (IOException e) { error = e; } catch (RuntimeException e) { @@ -217,21 +331,44 @@ } Log.e(TAG, "startCapture failed", error); if (camera != null) { - stopCapture(); + Exchanger resultDropper = new Exchanger(); + stopCaptureOnCameraThread(resultDropper); + exchange(resultDropper, false); } - return false; + exchange(result, false); + return; } // Called by native code. Returns true when camera is known to be stopped. @WebRTCJNITarget private synchronized boolean stopCapture() { Log.d(TAG, "stopCapture"); + final Exchanger result = new Exchanger(); + cameraThreadHandler.post(new Runnable() { + @Override public void run() { + stopCaptureOnCameraThread(result); + } + }); + boolean status = exchange(result, false); // |false| is a dummy value here. + try { + cameraThread.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + cameraThreadHandler = null; + cameraThread = null; + Log.d(TAG, "stopCapture done"); + return status; + } + + private void stopCaptureOnCameraThread( + Exchanger result) { if (camera == null) { if (mResumeCapture == true) { // We already got onPause, but now the native code wants us to stop. // Do not resume capturing when resuming the app. mResumeCapture = false; - return true; + return; } throw new RuntimeException("Camera is already stopped!"); } @@ -245,6 +382,11 @@ } else { if(android.os.Build.VERSION.SDK_INT>10) { camera.setPreviewTexture(null); + cameraSurfaceTexture = null; + if (cameraGlTextures != null) { + GLES20.glDeleteTextures(1, cameraGlTextures, 0); + cameraGlTextures = null; + } } } camera.release(); @@ -254,29 +396,94 @@ GeckoAppShell.getGeckoInterface().removeAppStateListener(this); ViERenderer.DestroyLocalRenderer(); } - return true; + exchange(result, true); + Looper.myLooper().quit(); + return; } catch (IOException e) { error = e; } catch (RuntimeException e) { error = e; } Log.e(TAG, "Failed to stop camera", error); - return false; + exchange(result, false); + Looper.myLooper().quit(); + return; + } + + @WebRTCJNITarget + private int getDeviceOrientation() { + int orientation = 0; + if (context != null) { + WindowManager wm = (WindowManager) context.getSystemService( + Context.WINDOW_SERVICE); + switch(wm.getDefaultDisplay().getRotation()) { + case Surface.ROTATION_90: + orientation = 90; + break; + case Surface.ROTATION_180: + orientation = 180; + break; + case Surface.ROTATION_270: + orientation = 270; + break; + case Surface.ROTATION_0: + default: + orientation = 0; + break; + } + } + return orientation; } @WebRTCJNITarget private native void ProvideCameraFrame( - byte[] data, int length, long captureObject, int rotation); + byte[] data, int length, int rotation, long timeStamp, long captureObject); + // Called on cameraThread so must not "synchronized". @WebRTCJNITarget - public synchronized void onPreviewFrame(byte[] data, Camera camera) { + @Override + public void onPreviewFrame(byte[] data, Camera callbackCamera) { + if (Thread.currentThread() != cameraThread) { + throw new RuntimeException("Camera callback not on camera thread?!?"); + } + if (camera == null) { + return; + } + if (camera != callbackCamera) { + throw new RuntimeException("Unexpected camera in callback!"); + } + frameCount++; + // Check if frame needs to be dropped. + if ((frameDropRatio > 1) && (frameCount % frameDropRatio) > 0) { + camera.addCallbackBuffer(data); + return; + } + long captureTimeMs = SystemClock.elapsedRealtime(); + if (frameCount > frameDropRatio) { + double durationMs = captureTimeMs - lastCaptureTimeMs; + averageDurationMs = 0.9 * averageDurationMs + 0.1 * durationMs; + if ((frameCount % 30) == 0) { + Log.d(TAG, "Camera TS " + captureTimeMs + + ". Duration: " + (int)durationMs + " ms. FPS: " + + (int) (1000 / averageDurationMs + 0.5)); + } + } + lastCaptureTimeMs = captureTimeMs; + + int rotation = getDeviceOrientation(); + if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { + rotation = 360 - rotation; + } + rotation = (info.orientation + rotation) % 360; + if (data != null) { - ProvideCameraFrame(data, data.length, native_capturer, mCaptureRotation); + ProvideCameraFrame(data, data.length, mCaptureRotation, lastCaptureTimeMs, native_capturer); camera.addCallbackBuffer(data); } } @WebRTCJNITarget + @Override public synchronized void surfaceChanged( SurfaceHolder holder, int format, int width, int height) { Log.d(TAG, "VideoCaptureAndroid::surfaceChanged ignored: " + @@ -284,25 +491,61 @@ } @WebRTCJNITarget - public synchronized void surfaceCreated(SurfaceHolder holder) { + @Override + public synchronized void surfaceCreated(final SurfaceHolder holder) { Log.d(TAG, "VideoCaptureAndroid::surfaceCreated"); - try { - if (camera != null) { - camera.setPreviewDisplay(holder); - } - } catch (IOException e) { + if (camera == null || cameraThreadHandler == null) { + return; + } + final Exchanger result = new Exchanger(); + cameraThreadHandler.post(new Runnable() { + @Override public void run() { + setPreviewDisplayOnCameraThread(holder, result); + } + }); + IOException e = exchange(result, null); // |null| is a dummy value here. + if (e != null) { throw new RuntimeException(e); } } @WebRTCJNITarget + @Override public synchronized void surfaceDestroyed(SurfaceHolder holder) { Log.d(TAG, "VideoCaptureAndroid::surfaceDestroyed"); + if (camera == null || cameraThreadHandler == null) { + return; + } + final Exchanger result = new Exchanger(); + cameraThreadHandler.post(new Runnable() { + @Override public void run() { + setPreviewDisplayOnCameraThread(null, result); + } + }); + IOException e = exchange(result, null); // |null| is a dummy value here. + if (e != null) { + throw new RuntimeException(e); + } + } + + private void setPreviewDisplayOnCameraThread( + SurfaceHolder holder, Exchanger result) { try { - if (camera != null) { - camera.setPreviewDisplay(null); - } + camera.setPreviewDisplay(holder); } catch (IOException e) { + exchange(result, e); + return; + } + exchange(result, null); + return; + } + + // Exchanges |value| with |exchanger|, converting InterruptedExceptions to + // RuntimeExceptions (since we expect never to see these). + private static T exchange(Exchanger exchanger, T value) { + try { + return exchanger.exchange(value); + } catch (InterruptedException e) { throw new RuntimeException(e); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java 2015-02-03 14:33:36.000000000 +0000 @@ -10,14 +10,9 @@ package org.webrtc.videoengine; -import java.io.File; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; -import java.util.Locale; -import android.content.Context; import android.hardware.Camera.CameraInfo; import android.hardware.Camera.Parameters; import android.hardware.Camera.Size; @@ -89,11 +84,11 @@ supportedFpsRanges.add(range); } } else { - Log.e(TAG, "Camera doesn't know its own framerate, guessing 25fps."); + Log.e(TAG, "Camera doesn't know its own framerate, guessing 30fps."); int[] range = new int[2]; // Your guess is as good as mine - range[0] = 25 * 1000; - range[1] = 25 * 1000; + range[0] = 30 * 1000; + range[1] = 30 * 1000; supportedFpsRanges.add(range); } } @@ -105,6 +100,29 @@ continue; } + boolean is30fpsRange = false; + boolean is15fpsRange = false; + // If there is constant 30 fps mode, but no 15 fps - add 15 fps + // mode to the list of supported ranges. Frame drop will be done + // in software. + for (int[] range : supportedFpsRanges) { + if (range[0] == 30000 && + range[1] == 30000) { + is30fpsRange = true; + } + if (range[0] == 15000 && + range[1] == 15000) { + is15fpsRange = true; + } + } + if (is30fpsRange && !is15fpsRange) { + Log.d(TAG, "Adding 15 fps support"); + int[] range = new int[2]; + range[0] = 15 * 1000; + range[1] = 15 * 1000; + supportedFpsRanges.add(range); + } + CaptureCapabilityAndroid device = new CaptureCapabilityAndroid(); int sizeLen = supportedSizes.size(); @@ -138,4 +156,5 @@ } return allDevices.toArray(new CaptureCapabilityAndroid[0]); } + } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/OWNERS 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -leozwang@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc 2015-02-03 14:33:36.000000000 +0000 @@ -11,37 +11,42 @@ #include "AndroidJNIWrapper.h" #include "webrtc/modules/video_capture/android/video_capture_android.h" +#include "webrtc/base/common.h" #include "webrtc/modules/utility/interface/helpers_android.h" #include "webrtc/modules/video_capture/android/device_info_android.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/logcat_trace_context.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/ref_count.h" #include "webrtc/system_wrappers/interface/trace.h" static JavaVM* g_jvm = NULL; static jclass g_java_capturer_class = NULL; // VideoCaptureAndroid.class. +static jobject g_context = NULL; // Owned android.content.Context. namespace webrtc { +// Called by Java to get the global application context. +jobject JNICALL GetContext(JNIEnv* env, jclass) { + assert(g_context); + return g_context; +} + // Called by Java when the camera has a new frame to deliver. void JNICALL ProvideCameraFrame( JNIEnv* env, jobject, jbyteArray javaCameraFrame, jint length, - jlong context, - jint rotation_deg) { + jint rotation, + jlong timeStamp, + jlong context) { webrtc::videocapturemodule::VideoCaptureAndroid* captureModule = reinterpret_cast( context); - VideoCaptureRotation rotation; - if (!videocapturemodule::VideoCaptureImpl::RotationFromDegrees( - static_cast(rotation_deg), &rotation)) { - captureModule->SetCaptureRotation(rotation); - } jbyte* cameraFrame = env->GetByteArrayElements(javaCameraFrame, NULL); captureModule->OnIncomingFrame( - reinterpret_cast(cameraFrame), length, 0); + reinterpret_cast(cameraFrame), length, rotation, 0); env->ReleaseByteArrayElements(javaCameraFrame, cameraFrame, JNI_ABORT); } @@ -49,21 +54,40 @@ if (g_java_capturer_class) return 0; - g_jvm = javaVM; - AttachThreadScoped ats(g_jvm); - - videocapturemodule::DeviceInfoAndroid::Initialize(ats.env()); - - g_java_capturer_class = - jsjni_GetGlobalClassRef("org/webrtc/videoengine/VideoCaptureAndroid"); - assert(g_java_capturer_class); - - JNINativeMethod native_method = { - "ProvideCameraFrame", "([BIJI)V", - reinterpret_cast(&ProvideCameraFrame) - }; - if (ats.env()->RegisterNatives(g_java_capturer_class, &native_method, 1) != 0) - assert(false); + if (javaVM) { + assert(!g_jvm); + g_jvm = javaVM; + AttachThreadScoped ats(g_jvm); + + g_context = jsjni_GetGlobalContextRef(); + + videocapturemodule::DeviceInfoAndroid::Initialize(ats.env()); + + g_java_capturer_class = + jsjni_GetGlobalClassRef("org/webrtc/videoengine/VideoCaptureAndroid"); + assert(g_java_capturer_class); + + JNINativeMethod native_methods[] = { + {"GetContext", + "()Landroid/content/Context;", + reinterpret_cast(&GetContext)}, + {"ProvideCameraFrame", + "([BIIJJ)V", + reinterpret_cast(&ProvideCameraFrame)}}; + if (ats.env()->RegisterNatives(g_java_capturer_class, + native_methods, 2) != 0) + assert(false); + } else { + if (g_jvm) { + AttachThreadScoped ats(g_jvm); + ats.env()->UnregisterNatives(g_java_capturer_class); + ats.env()->DeleteGlobalRef(g_java_capturer_class); + g_java_capturer_class = NULL; + g_context = NULL; + videocapturemodule::DeviceInfoAndroid::DeInitialize(); + g_jvm = NULL; + } + } return 0; } @@ -84,7 +108,23 @@ int32_t VideoCaptureAndroid::OnIncomingFrame(uint8_t* videoFrame, int32_t videoFrameLength, + int32_t degrees, int64_t captureTime) { + if (!_captureStarted) + return 0; + VideoCaptureRotation current_rotation = + (degrees <= 45 || degrees > 315) ? kCameraRotate0 : + (degrees > 45 && degrees <= 135) ? kCameraRotate90 : + (degrees > 135 && degrees <= 225) ? kCameraRotate180 : + (degrees > 225 && degrees <= 315) ? kCameraRotate270 : + kCameraRotate0; // Impossible. + if (_rotation != current_rotation) { + LOG(LS_INFO) << "New camera rotation: " << degrees; + _rotation = current_rotation; + int32_t status = VideoCaptureImpl::SetCaptureRotation(_rotation); + if (status != 0) + return status; + } return IncomingFrame( videoFrame, videoFrameLength, _captureCapability, captureTime); } @@ -103,21 +143,22 @@ return -1; // Store the device name + LOG(LS_INFO) << "VideoCaptureAndroid::Init: " << deviceUniqueIdUTF8; + size_t camera_id = 0; + if (!_deviceInfo.FindCameraIndex(deviceUniqueIdUTF8, &camera_id)) + return -1; _deviceUniqueId = new char[nameLength + 1]; memcpy(_deviceUniqueId, deviceUniqueIdUTF8, nameLength + 1); AttachThreadScoped ats(g_jvm); JNIEnv* env = ats.env(); - jmethodID ctor = env->GetMethodID(g_java_capturer_class, "", "(IJ)V"); assert(ctor); jlong j_this = reinterpret_cast(this); - size_t camera_id = 0; - if (!_deviceInfo.FindCameraIndex(deviceUniqueIdUTF8, &camera_id)) - return -1; _jCapturer = env->NewGlobalRef( env->NewObject(g_java_capturer_class, ctor, camera_id, j_this)); assert(_jCapturer); + _rotation = kCameraRotate0; return 0; } @@ -150,7 +191,8 @@ assert(j_start); int min_mfps = 0; int max_mfps = 0; - _deviceInfo.GetFpsRange(_deviceUniqueId, &min_mfps, &max_mfps); + _deviceInfo.GetMFpsRange(_deviceUniqueId, _captureCapability.maxFPS, + &min_mfps, &max_mfps); bool started = env->CallBooleanMethod(_jCapturer, j_start, _captureCapability.width, _captureCapability.height, @@ -163,13 +205,16 @@ } int32_t VideoCaptureAndroid::StopCapture() { - CriticalSectionScoped cs(&_apiCs); + _apiCs.Enter(); AttachThreadScoped ats(g_jvm); JNIEnv* env = ats.env(); memset(&_requestedCapability, 0, sizeof(_requestedCapability)); memset(&_captureCapability, 0, sizeof(_captureCapability)); _captureStarted = false; + // Exit critical section to avoid blocking camera thread inside + // onIncomingFrame() call. + _apiCs.Leave(); jmethodID j_stop = env->GetMethodID(g_java_capturer_class, "stopCapture", "()Z"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.h 2015-02-03 14:33:36.000000000 +0000 @@ -32,6 +32,7 @@ int32_t OnIncomingFrame(uint8_t* videoFrame, int32_t videoFrameLength, + int32_t degrees, int64_t captureTime = 0); protected: @@ -40,6 +41,7 @@ DeviceInfoAndroid _deviceInfo; jobject _jCapturer; // Global ref to Java VideoCaptureAndroid object. VideoCaptureCapability _captureCapability; + VideoCaptureRotation _rotation; bool _captureStarted; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_video_capture -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := \ - video_capture_impl.cc \ - device_info_impl.cc \ - video_capture_factory.cc \ - android/video_capture_android.cc \ - android/device_info_android.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/android \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../source \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../../../utility/interface \ - $(LOCAL_PATH)/../../../audio_coding/main/interface \ - $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/../../../../common_video/vplib/main/interface \ - $(LOCAL_PATH)/../../../../system_wrappers/interface - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/BUILD.gn 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,177 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../../build/webrtc.gni") + +# Note this target is missing an implementation for the video capture. +# Targets must link with either 'video_capture_impl' or +# 'video_capture_internal_impl' depending on whether they want to +# use the internal capturer. +source_set("video_capture") { + sources = [ + "device_info_impl.cc", + "device_info_impl.h", + "include/video_capture.h", + "include/video_capture_defines.h", + "include/video_capture_factory.h", + "video_capture_config.h", + "video_capture_delay.h", + "video_capture_factory.cc", + "video_capture_impl.cc", + "video_capture_impl.h", + ] + + deps = [ + "../../common_video", + "../../system_wrappers", + "../utility", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } +} + +source_set("video_capture_impl") { + sources = [ + "external/device_info_external.cc", + "external/video_capture_external.cc", + ] + + deps = [ + ":video_capture", + "../../system_wrappers", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } +} + +config("video_capture_internal_impl_config") { + if (is_ios) { + libs = [ + "AVFoundation.framework", + "CoreMedia.framework", + "CoreVideo.framework", + ] + } +} + +source_set("video_capture_internal_impl") { + deps = [ + ":video_capture", + "../../system_wrappers", + ] + + if (is_linux) { + sources = [ + "linux/device_info_linux.cc", + "linux/device_info_linux.h", + "linux/video_capture_linux.cc", + "linux/video_capture_linux.h", + ] + } + if (is_mac) { + sources = [ + "mac/qtkit/video_capture_qtkit.h", + "mac/qtkit/video_capture_qtkit.mm", + "mac/qtkit/video_capture_qtkit_info.h", + "mac/qtkit/video_capture_qtkit_info.mm", + "mac/qtkit/video_capture_qtkit_info_objc.h", + "mac/qtkit/video_capture_qtkit_info_objc.mm", + "mac/qtkit/video_capture_qtkit_objc.h", + "mac/qtkit/video_capture_qtkit_objc.mm", + "mac/qtkit/video_capture_qtkit_utility.h", + "mac/video_capture_mac.mm", + ] + + libs = [ + "CoreVideo.framework", + "QTKit.framework", + ] + } + # winsdk_samples isn't pulled into Chromium, so it is disabled for Chromium + # builds. This is not a problem since the internal video capture + # implementation should not be used in chrome - issue 3831. + if (is_win && !build_with_chromium) { + sources = [ + "windows/device_info_ds.cc", + "windows/device_info_ds.h", + "windows/device_info_mf.cc", + "windows/device_info_mf.h", + "windows/help_functions_ds.cc", + "windows/help_functions_ds.h", + "windows/sink_filter_ds.cc", + "windows/sink_filter_ds.h", + "windows/video_capture_ds.cc", + "windows/video_capture_ds.h", + "windows/video_capture_factory_windows.cc", + "windows/video_capture_mf.cc", + "windows/video_capture_mf.h", + ] + + libs = [ "Strmiids.lib" ] + + deps += [ "//third_party/winsdk_samples"] + } + if (is_android) { + sources = [ + "android/device_info_android.cc", + "android/device_info_android.h", + "android/video_capture_android.cc", + "android/video_capture_android.h", + ] + + deps += [ + "//third_party/icu:icuuc", + "//third_party/jsoncpp", + ] + } + if (is_ios) { + sources = [ + "ios/device_info_ios.h", + "ios/device_info_ios.mm", + "ios/device_info_ios_objc.h", + "ios/device_info_ios_objc.mm", + "ios/rtc_video_capture_ios_objc.h", + "ios/rtc_video_capture_ios_objc.mm", + "ios/video_capture_ios.h", + "ios/video_capture_ios.mm", + ] + + cflags = [ + "-fobjc-arc", # CLANG_ENABLE_OBJC_ARC = YES. + # To avoid warnings for deprecated videoMinFrameDuration and + # videoMaxFrameDuration properties in iOS 7.0. + # See webrtc:3705 for more details. + "-Wno-deprecated-declarations", + ] + } + + all_dependent_configs = [ ":video_capture_internal_impl_config" ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/device_info_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/device_info_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/device_info_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/device_info_impl.cc 2015-02-03 14:33:36.000000000 +0000 @@ -8,11 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include #include #include "webrtc/modules/video_capture/device_info_impl.h" #include "webrtc/modules/video_capture/video_capture_config.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #ifndef abs #define abs(a) (a>=0?a:-a) @@ -75,13 +76,8 @@ const uint32_t deviceCapabilityNumber, VideoCaptureCapability& capability) { + assert(deviceUniqueIdUTF8 != NULL); - if (!deviceUniqueIdUTF8) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "deviceUniqueIdUTF8 parameter not set in call to GetCapability"); - return -1; - } ReadLockScoped cs(_apiLock); if ((_lastUsedDeviceNameLength != strlen((char*) deviceUniqueIdUTF8)) @@ -111,9 +107,9 @@ // Make sure the number is valid if (deviceCapabilityNumber >= (unsigned int) _captureCapabilities.size()) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "deviceCapabilityNumber %d is invalid in call to GetCapability", - deviceCapabilityNumber); + LOG(LS_ERROR) << "Invalid deviceCapabilityNumber " + << deviceCapabilityNumber << ">= number of capabilities (" + << _captureCapabilities.size() << ")."; return -1; } @@ -266,9 +262,9 @@ }// else height not good }//end for - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "Best camera format: Width %d, Height %d, Frame rate %d, Color format %d", - bestWidth, bestHeight, bestFrameRate, bestRawType); + LOG(LS_VERBOSE) << "Best camera format: " << bestWidth << "x" << bestHeight + << "@" << bestFrameRate + << "fps, color format: " << bestRawType; // Copy the capability if (bestformatIndex < 0) @@ -343,11 +339,10 @@ } if (bestDelay > kMaxCaptureDelay) { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "Expected capture delay too high. %dms, will use %d", bestDelay, - kMaxCaptureDelay); + LOG(LS_WARNING) << "Expected capture delay (" << bestDelay + << " ms) too high, using " << kMaxCaptureDelay + << " ms."; bestDelay = kMaxCaptureDelay; - } return bestDelay; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ensure_initialized.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ensure_initialized.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ensure_initialized.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ensure_initialized.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Platform-specific initialization bits, if any, go here. + +#ifndef ANDROID + +namespace webrtc { +namespace videocapturemodule { +void EnsureInitialized() {} +} // namespace videocapturemodule +} // namespace webrtc + +#else + +#include + +#include "base/android/jni_android.h" +#include "webrtc/base/checks.h" +#include "webrtc/modules/video_capture/video_capture_internal.h" + +namespace webrtc { +namespace videocapturemodule { + +static pthread_once_t g_initialize_once = PTHREAD_ONCE_INIT; + +void EnsureInitializedOnce() { + JNIEnv* jni = ::base::android::AttachCurrentThread(); + jobject context = ::base::android::GetApplicationContext(); + JavaVM* jvm = NULL; + CHECK_EQ(0, jni->GetJavaVM(&jvm)); + CHECK_EQ(0, webrtc::SetCaptureAndroidVM(jvm, context)); +} + +void EnsureInitialized() { + CHECK_EQ(0, pthread_once(&g_initialize_once, &EnsureInitializedOnce)); +} + +} // namespace videocapturemodule +} // namespace webrtc + +#endif // !ANDROID diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ensure_initialized.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ensure_initialized.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ensure_initialized.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ensure_initialized.h 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +namespace webrtc { +namespace videocapturemodule { + +// Ensure any necessary initialization of webrtc::videocapturemodule has +// completed. +void EnsureInitialized(); + +} // namespace videocapturemodule. +} // namespace webrtc. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/include/mock/mock_video_capture.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/include/mock/mock_video_capture.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/include/mock/mock_video_capture.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/include/mock/mock_video_capture.h 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_INCLUDE_MOCK_MOCK_VIDEO_CAPTURE_H_ +#define WEBRTC_MODULES_VIDEO_CAPTURE_INCLUDE_MOCK_MOCK_VIDEO_CAPTURE_H_ + +#include "webrtc/modules/video_capture/include/video_capture.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace webrtc { + +class MockVideoCaptureModule : public VideoCaptureModule { + public: + // from Module + MOCK_METHOD0(TimeUntilNextProcess, int32_t()); + MOCK_METHOD0(Process, int32_t()); + + // from RefCountedModule + MOCK_METHOD0(AddRef, int32_t()); + MOCK_METHOD0(Release, int32_t()); + + // from VideoCaptureModule + MOCK_METHOD1(RegisterCaptureDataCallback, + void(VideoCaptureDataCallback& dataCallback)); + MOCK_METHOD0(DeRegisterCaptureDataCallback, void()); + MOCK_METHOD1(RegisterCaptureCallback, void(VideoCaptureFeedBack& callBack)); + MOCK_METHOD0(DeRegisterCaptureCallback, void()); + MOCK_METHOD1(StartCapture, int32_t(const VideoCaptureCapability& capability)); + MOCK_METHOD0(StopCapture, int32_t()); + MOCK_CONST_METHOD0(CurrentDeviceName, const char*()); + MOCK_METHOD0(CaptureStarted, bool()); + MOCK_METHOD1(CaptureSettings, int32_t(VideoCaptureCapability& settings)); + MOCK_METHOD1(SetCaptureDelay, void(int32_t delayMS)); + MOCK_METHOD0(CaptureDelay, int32_t()); + MOCK_METHOD1(SetCaptureRotation, int32_t(VideoCaptureRotation rotation)); + MOCK_METHOD1(GetEncodeInterface, + VideoCaptureEncodeInterface*(const VideoCodec& codec)); + MOCK_METHOD1(EnableFrameRateCallback, void(const bool enable)); + MOCK_METHOD1(EnableNoPictureAlarm, void(const bool enable)); +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_CAPTURE_INCLUDE_MOCK_MOCK_VIDEO_CAPTURE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/include/video_capture.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/include/video_capture.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/include/video_capture.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/include/video_capture.h 2015-02-03 14:33:36.000000000 +0000 @@ -21,7 +21,7 @@ namespace webrtc { #if defined(ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) && !defined(WEBRTC_GONK) -int32_t SetCaptureAndroidVM(JavaVM* javaVM); + int32_t SetCaptureAndroidVM(JavaVM* javaVM); #endif class VideoCaptureModule: public RefCountedModule { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/device_info_ios.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/device_info_ios.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/device_info_ios.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/device_info_ios.mm 2015-02-03 14:33:36.000000000 +0000 @@ -8,6 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + #include "webrtc/modules/video_capture/ios/device_info_ios.h" #include "webrtc/modules/video_capture/ios/device_info_ios_objc.h" #include "webrtc/modules/video_capture/video_capture_impl.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/device_info_ios_objc.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/device_info_ios_objc.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/device_info_ios_objc.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/device_info_ios_objc.mm 2015-02-03 14:33:36.000000000 +0000 @@ -8,6 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + #import #import "webrtc/modules/video_capture/ios/device_info_ios_objc.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/rtc_video_capture_ios_objc.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/rtc_video_capture_ios_objc.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/rtc_video_capture_ios_objc.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/rtc_video_capture_ios_objc.h 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_IOS_VIDEO_CAPTURE_IOS_OBJC_H_ +#define WEBRTC_MODULES_VIDEO_CAPTURE_IOS_VIDEO_CAPTURE_IOS_OBJC_H_ + +#import +#import + +#include "webrtc/modules/video_capture/ios/video_capture_ios.h" + +// The following class listens to a notification with name: +// 'StatusBarOrientationDidChange'. +// This notification must be posted in order for the capturer to reflect the +// orientation change in video w.r.t. the application orientation. +@interface RTCVideoCaptureIosObjC + : NSObject + +@property webrtc::VideoCaptureRotation frameRotation; + +// custom initializer. Instance of VideoCaptureIos is needed +// for callback purposes. +// default init methods have been overridden to return nil. +- (id)initWithOwner:(webrtc::videocapturemodule::VideoCaptureIos*)owner + captureId:(int)captureId; +- (BOOL)setCaptureDeviceByUniqueId:(NSString*)uniqueId; +- (BOOL)startCaptureWithCapability: + (const webrtc::VideoCaptureCapability&)capability; +- (BOOL)stopCapture; + +@end +#endif // WEBRTC_MODULES_VIDEO_CAPTURE_IOS_VIDEO_CAPTURE_IOS_OBJC_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/rtc_video_capture_ios_objc.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/rtc_video_capture_ios_objc.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/rtc_video_capture_ios_objc.mm 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/rtc_video_capture_ios_objc.mm 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import + +#import "webrtc/modules/video_capture/ios/device_info_ios_objc.h" +#import "webrtc/modules/video_capture/ios/rtc_video_capture_ios_objc.h" + +#include "webrtc/system_wrappers/interface/trace.h" + +using namespace webrtc; +using namespace webrtc::videocapturemodule; + +@interface RTCVideoCaptureIosObjC (hidden) +- (int)changeCaptureInputWithName:(NSString*)captureDeviceName; +@end + +@implementation RTCVideoCaptureIosObjC { + webrtc::videocapturemodule::VideoCaptureIos* _owner; + webrtc::VideoCaptureCapability _capability; + AVCaptureSession* _captureSession; + int _captureId; + AVCaptureConnection* _connection; + BOOL _captureChanging; // Guarded by _captureChangingCondition. + NSCondition* _captureChangingCondition; +} + +@synthesize frameRotation = _framRotation; + +- (id)initWithOwner:(VideoCaptureIos*)owner captureId:(int)captureId { + if (self == [super init]) { + _owner = owner; + _captureId = captureId; + _captureSession = [[AVCaptureSession alloc] init]; +#if defined(__IPHONE_7_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_0 + NSString* version = [[UIDevice currentDevice] systemVersion]; + if ([version integerValue] >= 7) { + _captureSession.usesApplicationAudioSession = NO; + } +#endif + _captureChanging = NO; + _captureChangingCondition = [[NSCondition alloc] init]; + + if (!_captureSession || !_captureChangingCondition) { + return nil; + } + + // create and configure a new output (using callbacks) + AVCaptureVideoDataOutput* captureOutput = + [[AVCaptureVideoDataOutput alloc] init]; + NSString* key = (NSString*)kCVPixelBufferPixelFormatTypeKey; + + NSNumber* val = [NSNumber + numberWithUnsignedInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange]; + NSDictionary* videoSettings = + [NSDictionary dictionaryWithObject:val forKey:key]; + captureOutput.videoSettings = videoSettings; + + // add new output + if ([_captureSession canAddOutput:captureOutput]) { + [_captureSession addOutput:captureOutput]; + } else { + WEBRTC_TRACE(kTraceError, + kTraceVideoCapture, + _captureId, + "%s:%s:%d Could not add output to AVCaptureSession ", + __FILE__, + __FUNCTION__, + __LINE__); + } + + NSNotificationCenter* notify = [NSNotificationCenter defaultCenter]; + [notify addObserver:self + selector:@selector(onVideoError:) + name:AVCaptureSessionRuntimeErrorNotification + object:_captureSession]; + [notify addObserver:self + selector:@selector(statusBarOrientationDidChange:) + name:@"StatusBarOrientationDidChange" + object:nil]; + } + + return self; +} + +- (void)directOutputToSelf { + [[self currentOutput] + setSampleBufferDelegate:self + queue:dispatch_get_global_queue( + DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; +} + +- (void)directOutputToNil { + [[self currentOutput] setSampleBufferDelegate:nil queue:NULL]; +} + +- (void)statusBarOrientationDidChange:(NSNotification*)notification { + [self setRelativeVideoOrientation]; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (BOOL)setCaptureDeviceByUniqueId:(NSString*)uniqueId { + [self waitForCaptureChangeToFinish]; + // check to see if the camera is already set + if (_captureSession) { + NSArray* currentInputs = [NSArray arrayWithArray:[_captureSession inputs]]; + if ([currentInputs count] > 0) { + AVCaptureDeviceInput* currentInput = [currentInputs objectAtIndex:0]; + if ([uniqueId isEqualToString:[currentInput.device localizedName]]) { + return YES; + } + } + } + + return [self changeCaptureInputByUniqueId:uniqueId]; +} + +- (BOOL)startCaptureWithCapability:(const VideoCaptureCapability&)capability { + [self waitForCaptureChangeToFinish]; + if (!_captureSession) { + return NO; + } + + // check limits of the resolution + if (capability.maxFPS < 0 || capability.maxFPS > 60) { + return NO; + } + + if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1920x1080]) { + if (capability.width > 1920 || capability.height > 1080) { + return NO; + } + } else if ([_captureSession + canSetSessionPreset:AVCaptureSessionPreset1280x720]) { + if (capability.width > 1280 || capability.height > 720) { + return NO; + } + } else if ([_captureSession + canSetSessionPreset:AVCaptureSessionPreset640x480]) { + if (capability.width > 640 || capability.height > 480) { + return NO; + } + } else if ([_captureSession + canSetSessionPreset:AVCaptureSessionPreset352x288]) { + if (capability.width > 352 || capability.height > 288) { + return NO; + } + } else if (capability.width < 0 || capability.height < 0) { + return NO; + } + + _capability = capability; + + AVCaptureVideoDataOutput* currentOutput = [self currentOutput]; + if (!currentOutput) + return NO; + + [self directOutputToSelf]; + + _captureChanging = YES; + dispatch_async( + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^(void) { [self startCaptureInBackgroundWithOutput:currentOutput]; }); + return YES; +} + +- (AVCaptureVideoDataOutput*)currentOutput { + return [[_captureSession outputs] firstObject]; +} + +- (void)startCaptureInBackgroundWithOutput: + (AVCaptureVideoDataOutput*)currentOutput { + NSString* captureQuality = + [NSString stringWithString:AVCaptureSessionPresetLow]; + if (_capability.width >= 1920 || _capability.height >= 1080) { + captureQuality = + [NSString stringWithString:AVCaptureSessionPreset1920x1080]; + } else if (_capability.width >= 1280 || _capability.height >= 720) { + captureQuality = [NSString stringWithString:AVCaptureSessionPreset1280x720]; + } else if (_capability.width >= 640 || _capability.height >= 480) { + captureQuality = [NSString stringWithString:AVCaptureSessionPreset640x480]; + } else if (_capability.width >= 352 || _capability.height >= 288) { + captureQuality = [NSString stringWithString:AVCaptureSessionPreset352x288]; + } + + // begin configuration for the AVCaptureSession + [_captureSession beginConfiguration]; + + // picture resolution + [_captureSession setSessionPreset:captureQuality]; + + // take care of capture framerate now + NSArray* sessionInputs = _captureSession.inputs; + AVCaptureDeviceInput* deviceInput = [sessionInputs count] > 0 ? + sessionInputs[0] : nil; + AVCaptureDevice* inputDevice = deviceInput.device; + if (inputDevice) { + AVCaptureDeviceFormat* activeFormat = inputDevice.activeFormat; + NSArray* supportedRanges = activeFormat.videoSupportedFrameRateRanges; + AVFrameRateRange* targetRange = [supportedRanges count] > 0 ? + supportedRanges[0] : nil; + // Find the largest supported framerate less than capability maxFPS. + for (AVFrameRateRange* range in supportedRanges) { + if (range.maxFrameRate <= _capability.maxFPS && + targetRange.maxFrameRate <= range.maxFrameRate) { + targetRange = range; + } + } + if (targetRange && [inputDevice lockForConfiguration:NULL]) { + inputDevice.activeVideoMinFrameDuration = targetRange.minFrameDuration; + inputDevice.activeVideoMaxFrameDuration = targetRange.minFrameDuration; + [inputDevice unlockForConfiguration]; + } + } + + _connection = [currentOutput connectionWithMediaType:AVMediaTypeVideo]; + [self setRelativeVideoOrientation]; + + // finished configuring, commit settings to AVCaptureSession. + [_captureSession commitConfiguration]; + + [_captureSession startRunning]; + [self signalCaptureChangeEnd]; +} + +- (void)setRelativeVideoOrientation { + if (!_connection.supportsVideoOrientation) + return; + switch ([UIApplication sharedApplication].statusBarOrientation) { + case UIInterfaceOrientationPortrait: +#if defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0 + case UIInterfaceOrientationUnknown: +#endif + _connection.videoOrientation = AVCaptureVideoOrientationPortrait; + break; + case UIInterfaceOrientationPortraitUpsideDown: + _connection.videoOrientation = + AVCaptureVideoOrientationPortraitUpsideDown; + break; + case UIInterfaceOrientationLandscapeLeft: + _connection.videoOrientation = AVCaptureVideoOrientationLandscapeLeft; + break; + case UIInterfaceOrientationLandscapeRight: + _connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; + break; + } +} + +- (void)onVideoError:(NSNotification*)notification { + NSLog(@"onVideoError: %@", notification); + // TODO(sjlee): make the specific error handling with this notification. + WEBRTC_TRACE(kTraceError, + kTraceVideoCapture, + _captureId, + "%s:%s:%d [AVCaptureSession startRunning] error.", + __FILE__, + __FUNCTION__, + __LINE__); +} + +- (BOOL)stopCapture { + [self waitForCaptureChangeToFinish]; + [self directOutputToNil]; + + if (!_captureSession) { + return NO; + } + + _captureChanging = YES; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^(void) { [self stopCaptureInBackground]; }); + return YES; +} + +- (void)stopCaptureInBackground { + [_captureSession stopRunning]; + [self signalCaptureChangeEnd]; +} + +- (BOOL)changeCaptureInputByUniqueId:(NSString*)uniqueId { + [self waitForCaptureChangeToFinish]; + NSArray* currentInputs = [_captureSession inputs]; + // remove current input + if ([currentInputs count] > 0) { + AVCaptureInput* currentInput = + (AVCaptureInput*)[currentInputs objectAtIndex:0]; + + [_captureSession removeInput:currentInput]; + } + + // Look for input device with the name requested (as our input param) + // get list of available capture devices + int captureDeviceCount = [DeviceInfoIosObjC captureDeviceCount]; + if (captureDeviceCount <= 0) { + return NO; + } + + AVCaptureDevice* captureDevice = + [DeviceInfoIosObjC captureDeviceForUniqueId:uniqueId]; + + if (!captureDevice) { + return NO; + } + + // now create capture session input out of AVCaptureDevice + NSError* deviceError = nil; + AVCaptureDeviceInput* newCaptureInput = + [AVCaptureDeviceInput deviceInputWithDevice:captureDevice + error:&deviceError]; + + if (!newCaptureInput) { + const char* errorMessage = [[deviceError localizedDescription] UTF8String]; + + WEBRTC_TRACE(kTraceError, + kTraceVideoCapture, + _captureId, + "%s:%s:%d deviceInputWithDevice error:%s", + __FILE__, + __FUNCTION__, + __LINE__, + errorMessage); + + return NO; + } + + // try to add our new capture device to the capture session + [_captureSession beginConfiguration]; + + BOOL addedCaptureInput = NO; + if ([_captureSession canAddInput:newCaptureInput]) { + [_captureSession addInput:newCaptureInput]; + addedCaptureInput = YES; + } else { + addedCaptureInput = NO; + } + + [_captureSession commitConfiguration]; + + return addedCaptureInput; +} + +- (void)captureOutput:(AVCaptureOutput*)captureOutput + didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer + fromConnection:(AVCaptureConnection*)connection { + const int kFlags = 0; + CVImageBufferRef videoFrame = CMSampleBufferGetImageBuffer(sampleBuffer); + + if (CVPixelBufferLockBaseAddress(videoFrame, kFlags) != kCVReturnSuccess) { + return; + } + + const int kYPlaneIndex = 0; + const int kUVPlaneIndex = 1; + + uint8_t* baseAddress = + (uint8_t*)CVPixelBufferGetBaseAddressOfPlane(videoFrame, kYPlaneIndex); + int yPlaneBytesPerRow = + CVPixelBufferGetBytesPerRowOfPlane(videoFrame, kYPlaneIndex); + int yPlaneHeight = CVPixelBufferGetHeightOfPlane(videoFrame, kYPlaneIndex); + int uvPlaneBytesPerRow = + CVPixelBufferGetBytesPerRowOfPlane(videoFrame, kUVPlaneIndex); + int uvPlaneHeight = CVPixelBufferGetHeightOfPlane(videoFrame, kUVPlaneIndex); + int frameSize = + yPlaneBytesPerRow * yPlaneHeight + uvPlaneBytesPerRow * uvPlaneHeight; + + VideoCaptureCapability tempCaptureCapability; + tempCaptureCapability.width = CVPixelBufferGetWidth(videoFrame); + tempCaptureCapability.height = CVPixelBufferGetHeight(videoFrame); + tempCaptureCapability.maxFPS = _capability.maxFPS; + tempCaptureCapability.rawType = kVideoNV12; + + _owner->IncomingFrame(baseAddress, frameSize, tempCaptureCapability, 0); + + CVPixelBufferUnlockBaseAddress(videoFrame, kFlags); +} + +- (void)signalCaptureChangeEnd { + [_captureChangingCondition lock]; + _captureChanging = NO; + [_captureChangingCondition signal]; + [_captureChangingCondition unlock]; +} + +- (void)waitForCaptureChangeToFinish { + [_captureChangingCondition lock]; + while (_captureChanging) { + [_captureChangingCondition wait]; + } + [_captureChangingCondition unlock]; +} +@end diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/video_capture_ios.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/video_capture_ios.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/video_capture_ios.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/video_capture_ios.h 2015-02-03 14:33:36.000000000 +0000 @@ -13,7 +13,7 @@ #include "webrtc/modules/video_capture/video_capture_impl.h" -@class VideoCaptureIosObjC; +@class RTCVideoCaptureIosObjC; namespace webrtc { namespace videocapturemodule { @@ -33,7 +33,7 @@ virtual int32_t CaptureSettings(VideoCaptureCapability& settings) OVERRIDE; private: - VideoCaptureIosObjC* capture_device_; + RTCVideoCaptureIosObjC* capture_device_; bool is_capturing_; int32_t id_; VideoCaptureCapability capability_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/video_capture_ios.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/video_capture_ios.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/video_capture_ios.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/video_capture_ios.mm 2015-02-03 14:33:36.000000000 +0000 @@ -8,8 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + #include "webrtc/modules/video_capture/ios/device_info_ios_objc.h" -#include "webrtc/modules/video_capture/ios/video_capture_ios_objc.h" +#include "webrtc/modules/video_capture/ios/rtc_video_capture_ios_objc.h" #include "webrtc/system_wrappers/interface/ref_count.h" #include "webrtc/system_wrappers/interface/scoped_refptr.h" #include "webrtc/system_wrappers/interface/trace.h" @@ -30,7 +34,7 @@ } VideoCaptureIos::~VideoCaptureIos() { - if (capture_device_) { + if (is_capturing_) { [capture_device_ stopCapture]; } } @@ -53,8 +57,8 @@ capture_module->_deviceUniqueId[name_length] = '\0'; capture_module->capture_device_ = - [[VideoCaptureIosObjC alloc] initWithOwner:capture_module - captureId:capture_module->id_]; + [[RTCVideoCaptureIosObjC alloc] initWithOwner:capture_module + captureId:capture_module->id_]; if (!capture_module->capture_device_) { return NULL; } @@ -86,7 +90,6 @@ } is_capturing_ = false; - return 0; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/video_capture_ios_objc.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/video_capture_ios_objc.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/video_capture_ios_objc.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/video_capture_ios_objc.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_IOS_VIDEO_CAPTURE_IOS_OBJC_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_IOS_VIDEO_CAPTURE_IOS_OBJC_H_ - -#import - -#include "webrtc/modules/video_capture/ios/video_capture_ios.h" - -@interface VideoCaptureIosObjC - : UIViewController { - @private - webrtc::videocapturemodule::VideoCaptureIos* _owner; - webrtc::VideoCaptureCapability _capability; - AVCaptureSession* _captureSession; - int _captureId; -} - -@property webrtc::VideoCaptureRotation frameRotation; - -// custom initializer. Instance of VideoCaptureIos is needed -// for callback purposes. -// default init methods have been overridden to return nil. -- (id)initWithOwner:(webrtc::videocapturemodule::VideoCaptureIos*)owner - captureId:(int)captureId; -- (BOOL)setCaptureDeviceByUniqueId:(NSString*)uniequeId; -- (BOOL)startCaptureWithCapability: - (const webrtc::VideoCaptureCapability&)capability; -- (BOOL)stopCapture; - -@end -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_IOS_VIDEO_CAPTURE_IOS_OBJC_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/video_capture_ios_objc.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/video_capture_ios_objc.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/video_capture_ios_objc.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/ios/video_capture_ios_objc.mm 1970-01-01 00:00:00.000000000 +0000 @@ -1,287 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#import "webrtc/modules/video_capture/ios/device_info_ios_objc.h" -#import "webrtc/modules/video_capture/ios/video_capture_ios_objc.h" - -#include "webrtc/system_wrappers/interface/trace.h" - -using namespace webrtc; -using namespace webrtc::videocapturemodule; - -@interface VideoCaptureIosObjC (hidden) -- (int)changeCaptureInputWithName:(NSString*)captureDeviceName; - -@end - -@implementation VideoCaptureIosObjC - -@synthesize frameRotation = _framRotation; - -- (id)initWithOwner:(VideoCaptureIos*)owner captureId:(int)captureId { - if (self == [super init]) { - _owner = owner; - _captureId = captureId; - _captureSession = [[AVCaptureSession alloc] init]; - - if (!_captureSession) { - return nil; - } - - // create and configure a new output (using callbacks) - AVCaptureVideoDataOutput* captureOutput = - [[AVCaptureVideoDataOutput alloc] init]; - [captureOutput setSampleBufferDelegate:self - queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; - NSString* key = (NSString*)kCVPixelBufferPixelFormatTypeKey; - - NSNumber* val = [NSNumber - numberWithUnsignedInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange]; - NSDictionary* videoSettings = - [NSDictionary dictionaryWithObject:val forKey:key]; - captureOutput.videoSettings = videoSettings; - - // add new output - if ([_captureSession canAddOutput:captureOutput]) { - [_captureSession addOutput:captureOutput]; - } else { - WEBRTC_TRACE(kTraceError, - kTraceVideoCapture, - _captureId, - "%s:%s:%d Could not add output to AVCaptureSession ", - __FILE__, - __FUNCTION__, - __LINE__); - } - - NSNotificationCenter* notify = [NSNotificationCenter defaultCenter]; - [notify addObserver:self - selector:@selector(onVideoError:) - name:AVCaptureSessionRuntimeErrorNotification - object:_captureSession]; - } - - return self; -} - -- (BOOL)setCaptureDeviceByUniqueId:(NSString*)uniqueId { - // check to see if the camera is already set - if (_captureSession) { - NSArray* currentInputs = [NSArray arrayWithArray:[_captureSession inputs]]; - if ([currentInputs count] > 0) { - AVCaptureDeviceInput* currentInput = [currentInputs objectAtIndex:0]; - if ([uniqueId isEqualToString:[currentInput.device localizedName]]) { - return YES; - } - } - } - - return [self changeCaptureInputByUniqueId:uniqueId]; -} - -- (BOOL)startCaptureWithCapability:(const VideoCaptureCapability&)capability { - if (!_captureSession) { - return NO; - } - - // check limits of the resolution - if (capability.maxFPS < 0 || capability.maxFPS > 60) { - return NO; - } - - if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1920x1080]) { - if (capability.width > 1920 || capability.height > 1080) { - return NO; - } - } else if ([_captureSession - canSetSessionPreset:AVCaptureSessionPreset1280x720]) { - if (capability.width > 1280 || capability.height > 720) { - return NO; - } - } else if ([_captureSession - canSetSessionPreset:AVCaptureSessionPreset640x480]) { - if (capability.width > 640 || capability.height > 480) { - return NO; - } - } else if ([_captureSession - canSetSessionPreset:AVCaptureSessionPreset352x288]) { - if (capability.width > 352 || capability.height > 288) { - return NO; - } - } else if (capability.width < 0 || capability.height < 0) { - return NO; - } - - _capability = capability; - - NSArray* currentOutputs = [_captureSession outputs]; - if ([currentOutputs count] == 0) { - return NO; - } - - NSString* captureQuality = - [NSString stringWithString:AVCaptureSessionPresetLow]; - if (_capability.width >= 1920 || _capability.height >= 1080) { - captureQuality = - [NSString stringWithString:AVCaptureSessionPreset1920x1080]; - } else if (_capability.width >= 1280 || _capability.height >= 720) { - captureQuality = [NSString stringWithString:AVCaptureSessionPreset1280x720]; - } else if (_capability.width >= 640 || _capability.height >= 480) { - captureQuality = [NSString stringWithString:AVCaptureSessionPreset640x480]; - } else if (_capability.width >= 352 || _capability.height >= 288) { - captureQuality = [NSString stringWithString:AVCaptureSessionPreset352x288]; - } - - AVCaptureVideoDataOutput* currentOutput = - (AVCaptureVideoDataOutput*)[currentOutputs objectAtIndex:0]; - - // begin configuration for the AVCaptureSession - [_captureSession beginConfiguration]; - - // picture resolution - [_captureSession setSessionPreset:captureQuality]; - - // take care of capture framerate now - AVCaptureConnection* connection = - [currentOutput connectionWithMediaType:AVMediaTypeVideo]; - - CMTime cm_time = {1, _capability.maxFPS, kCMTimeFlags_Valid, 0}; - - [connection setVideoMinFrameDuration:cm_time]; - [connection setVideoMaxFrameDuration:cm_time]; - - // finished configuring, commit settings to AVCaptureSession. - [_captureSession commitConfiguration]; - - [_captureSession startRunning]; - - [captureQuality release]; - - return YES; -} - -- (void)onVideoError { - // TODO(sjlee): make the specific error handling with this notification. - WEBRTC_TRACE(kTraceError, - kTraceVideoCapture, - _captureId, - "%s:%s:%d [AVCaptureSession startRunning] error.", - __FILE__, - __FUNCTION__, - __LINE__); -} - -- (BOOL)stopCapture { - if (!_captureSession) { - return NO; - } - - [_captureSession stopRunning]; - - return YES; -} - -- (BOOL)changeCaptureInputByUniqueId:(NSString*)uniqueId { - NSArray* currentInputs = [_captureSession inputs]; - // remove current input - if ([currentInputs count] > 0) { - AVCaptureInput* currentInput = - (AVCaptureInput*)[currentInputs objectAtIndex:0]; - - [_captureSession removeInput:currentInput]; - } - - // Look for input device with the name requested (as our input param) - // get list of available capture devices - int captureDeviceCount = [DeviceInfoIosObjC captureDeviceCount]; - if (captureDeviceCount <= 0) { - return NO; - } - - AVCaptureDevice* captureDevice = - [DeviceInfoIosObjC captureDeviceForUniqueId:uniqueId]; - - if (!captureDevice) { - return NO; - } - - // now create capture session input out of AVCaptureDevice - NSError* deviceError = nil; - AVCaptureDeviceInput* newCaptureInput = - [AVCaptureDeviceInput deviceInputWithDevice:captureDevice - error:&deviceError]; - - if (!newCaptureInput) { - const char* errorMessage = [[deviceError localizedDescription] UTF8String]; - - WEBRTC_TRACE(kTraceError, - kTraceVideoCapture, - _captureId, - "%s:%s:%d deviceInputWithDevice error:%s", - __FILE__, - __FUNCTION__, - __LINE__, - errorMessage); - - return NO; - } - - // try to add our new capture device to the capture session - [_captureSession beginConfiguration]; - - BOOL addedCaptureInput = NO; - if ([_captureSession canAddInput:newCaptureInput]) { - [_captureSession addInput:newCaptureInput]; - addedCaptureInput = YES; - } else { - addedCaptureInput = NO; - } - - [_captureSession commitConfiguration]; - - return addedCaptureInput; -} - -- (void)captureOutput:(AVCaptureOutput*)captureOutput - didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer - fromConnection:(AVCaptureConnection*)connection { - const int kFlags = 0; - CVImageBufferRef videoFrame = CMSampleBufferGetImageBuffer(sampleBuffer); - - if (CVPixelBufferLockBaseAddress(videoFrame, kFlags) != kCVReturnSuccess) { - return; - } - - const int kYPlaneIndex = 0; - const int kUVPlaneIndex = 1; - - uint8_t* baseAddress = - (uint8_t*)CVPixelBufferGetBaseAddressOfPlane(videoFrame, kYPlaneIndex); - int yPlaneBytesPerRow = - CVPixelBufferGetBytesPerRowOfPlane(videoFrame, kYPlaneIndex); - int yPlaneHeight = CVPixelBufferGetHeightOfPlane(videoFrame, kYPlaneIndex); - int uvPlaneBytesPerRow = - CVPixelBufferGetBytesPerRowOfPlane(videoFrame, kUVPlaneIndex); - int uvPlaneHeight = CVPixelBufferGetHeightOfPlane(videoFrame, kUVPlaneIndex); - int frameSize = - yPlaneBytesPerRow * yPlaneHeight + uvPlaneBytesPerRow * uvPlaneHeight; - - VideoCaptureCapability tempCaptureCapability; - tempCaptureCapability.width = CVPixelBufferGetWidth(videoFrame); - tempCaptureCapability.height = CVPixelBufferGetHeight(videoFrame); - tempCaptureCapability.maxFPS = _capability.maxFPS; - tempCaptureCapability.rawType = kVideoNV12; - - _owner->IncomingFrame(baseAddress, frameSize, tempCaptureCapability, 0); - - CVPixelBufferUnlockBaseAddress(videoFrame, kFlags); -} - -@end diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/linux/video_capture_linux.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/linux/video_capture_linux.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/linux/video_capture_linux.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/linux/video_capture_linux.cc 2015-02-03 14:33:36.000000000 +0000 @@ -167,18 +167,20 @@ // Supported video formats in preferred order. // If the requested resolution is larger than VGA, we prefer MJPEG. Go for // I420 otherwise. - const int nFormats = 4; + const int nFormats = 5; unsigned int fmts[nFormats]; if (capability.width > 640 || capability.height > 480) { fmts[0] = V4L2_PIX_FMT_MJPEG; fmts[1] = V4L2_PIX_FMT_YUV420; fmts[2] = V4L2_PIX_FMT_YUYV; - fmts[3] = V4L2_PIX_FMT_JPEG; + fmts[3] = V4L2_PIX_FMT_UYVY; + fmts[4] = V4L2_PIX_FMT_JPEG; } else { fmts[0] = V4L2_PIX_FMT_YUV420; fmts[1] = V4L2_PIX_FMT_YUYV; - fmts[2] = V4L2_PIX_FMT_MJPEG; - fmts[3] = V4L2_PIX_FMT_JPEG; + fmts[2] = V4L2_PIX_FMT_UYVY; + fmts[3] = V4L2_PIX_FMT_MJPEG; + fmts[4] = V4L2_PIX_FMT_JPEG; } // Enumerate image formats. @@ -228,6 +230,8 @@ _captureVideoType = kVideoYUY2; else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) _captureVideoType = kVideoI420; + else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) + _captureVideoType = kVideoUYVY; else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG || video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG) _captureVideoType = kVideoMJPEG; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/mac/qtkit/video_capture_qtkit_info_objc.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/mac/qtkit/video_capture_qtkit_info_objc.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/mac/qtkit/video_capture_qtkit_info_objc.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/mac/qtkit/video_capture_qtkit_info_objc.mm 2015-02-03 14:33:36.000000000 +0000 @@ -158,11 +158,14 @@ - (void)checkOSSupported { Class osSupportedTest = NSClassFromString(@"QTCaptureSession"); - _OSSupportedInfo = NO; if(nil == osSupportedTest) { + _OSSupportedInfo = NO; + } + else + { + _OSSupportedInfo = YES; } - _OSSupportedInfo = YES; } /// ***** Retrieves the number of capture devices currently available diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -1,5 +1,14 @@ -fischman@webrtc.org +glaznev@webrtc.org mallinath@webrtc.org mflodman@webrtc.org perkj@webrtc.org -wu@webrtc.org +tkchin@webrtc.org + +per-file *.isolate=kjellander@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/test/video_capture_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/test/video_capture_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/test/video_capture_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/test/video_capture_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,10 +10,14 @@ #include +#include +#include + #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/common_video/interface/i420_video_frame.h" #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" #include "webrtc/modules/utility/interface/process_thread.h" +#include "webrtc/modules/video_capture/ensure_initialized.h" #include "webrtc/modules/video_capture/include/video_capture.h" #include "webrtc/modules/video_capture/include/video_capture_factory.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" @@ -21,6 +25,7 @@ #include "webrtc/system_wrappers/interface/scoped_refptr.h" #include "webrtc/system_wrappers/interface/sleep.h" #include "webrtc/system_wrappers/interface/tick_util.h" +#include "webrtc/test/testsupport/gtest_disable.h" using webrtc::CriticalSectionWrapper; using webrtc::CriticalSectionScoped; @@ -103,9 +108,14 @@ virtual void OnIncomingCapturedFrame(const int32_t id, webrtc::I420VideoFrame& videoFrame) { CriticalSectionScoped cs(capture_cs_.get()); - int height = videoFrame.height(); int width = videoFrame.width(); +#if ANDROID + // Android camera frames may be rotated depending on test device + // orientation. + EXPECT_TRUE(height == capability_.height || height == capability_.width); + EXPECT_TRUE(width == capability_.width || width == capability_.height); +#else if (rotate_frame_ == webrtc::kCameraRotate90 || rotate_frame_ == webrtc::kCameraRotate270) { EXPECT_EQ(width, capability_.height); @@ -114,6 +124,7 @@ EXPECT_EQ(height, capability_.height); EXPECT_EQ(width, capability_.width); } +#endif // RenderTimstamp should be the time now. EXPECT_TRUE( videoFrame.render_time_ms() >= TickTime::MillisecondTimestamp()-30 && @@ -231,7 +242,9 @@ VideoCaptureTest() : number_of_devices_(0) {} void SetUp() { - device_info_.reset(VideoCaptureFactory::CreateDeviceInfo(5)); + webrtc::videocapturemodule::EnsureInitialized(); + device_info_.reset(VideoCaptureFactory::CreateDeviceInfo(0)); + assert(device_info_.get()); number_of_devices_ = device_info_->NumberOfDevices(); ASSERT_GT(number_of_devices_, 0u); } @@ -258,7 +271,7 @@ void StartCapture(VideoCaptureModule* capture_module, VideoCaptureCapability capability) { - EXPECT_EQ(0, capture_module->StartCapture(capability)); + ASSERT_EQ(0, capture_module->StartCapture(capability)); EXPECT_TRUE(capture_module->CaptureStarted()); VideoCaptureCapability resulting_capability; @@ -289,7 +302,7 @@ capability.rawType = webrtc::kVideoUnknown; #endif capture_observer.SetExpectedCapability(capability); - StartCapture(module.get(), capability); + ASSERT_NO_FATAL_FAILURE(StartCapture(module.get(), capability)); // Less than 4s to start the camera. EXPECT_LE(TickTime::MillisecondTimestamp() - start_time, 4000); @@ -323,17 +336,50 @@ int number_of_capabilities = device_info_->NumberOfCapabilities( module->CurrentDeviceName()); EXPECT_GT(number_of_capabilities, 0); + // Key is x, value is vector of maxFPS values at that + // resolution. + typedef std::map > FrameRatesByResolution; + FrameRatesByResolution frame_rates_by_resolution; for (int i = 0; i < number_of_capabilities; ++i) { VideoCaptureCapability capability; EXPECT_EQ(0, device_info_->GetCapability(module->CurrentDeviceName(), i, capability)); + std::ostringstream resolutionStream; + resolutionStream << capability.width << "x" << capability.height; + resolutionStream.flush(); + std::string resolution = resolutionStream.str(); + frame_rates_by_resolution[resolution].push_back(capability.maxFPS); + + // Since Android presents so many resolution/FPS combinations and the test + // runner imposes a timeout, we only actually start the capture and test + // that a frame was captured for 2 frame-rates at each resolution. + if (frame_rates_by_resolution[resolution].size() > 2) + continue; + capture_observer.SetExpectedCapability(capability); - StartCapture(module.get(), capability); - // Make sure 5 frames are captured. - EXPECT_TRUE_WAIT(capture_observer.incoming_frames() >= 5, kTimeOut); + ASSERT_NO_FATAL_FAILURE(StartCapture(module.get(), capability)); + // Make sure at least one frame is captured. + EXPECT_TRUE_WAIT(capture_observer.incoming_frames() >= 1, kTimeOut); EXPECT_EQ(0, module->StopCapture()); } + +#if ANDROID + // There's no reason for this to _necessarily_ be true, but in practice all + // Android devices this test runs on in fact do support multiple capture + // resolutions and multiple frame-rates per captured resolution, so we assert + // this fact here as a regression-test against the time that we only noticed a + // single frame-rate per resolution (bug 2974). If this test starts being run + // on devices for which this is untrue (e.g. Nexus4) then the following should + // probably be wrapped in a base::android::BuildInfo::model()/device() check. + EXPECT_GT(frame_rates_by_resolution.size(), 1U); + for (FrameRatesByResolution::const_iterator it = + frame_rates_by_resolution.begin(); + it != frame_rates_by_resolution.end(); + ++it) { + EXPECT_GT(it->second.size(), 1U) << it->first; + } +#endif // ANDROID } // NOTE: flaky, crashes sometimes. @@ -376,10 +422,12 @@ #endif capture_observer2.SetExpectedCapability(capability2); - StartCapture(module1.get(), capability1); - StartCapture(module2.get(), capability2); + ASSERT_NO_FATAL_FAILURE(StartCapture(module1.get(), capability1)); + ASSERT_NO_FATAL_FAILURE(StartCapture(module2.get(), capability2)); EXPECT_TRUE_WAIT(capture_observer1.incoming_frames() >= 5, kTimeOut); EXPECT_TRUE_WAIT(capture_observer2.incoming_frames() >= 5, kTimeOut); + EXPECT_EQ(0, module2->StopCapture()); + EXPECT_EQ(0, module1->StopCapture()); } // Test class for testing external capture and capture feedback information @@ -432,7 +480,7 @@ unsigned int length = webrtc::CalcBufferSize(webrtc::kI420, test_frame_.width(), test_frame_.height()); - webrtc::scoped_array test_buffer(new uint8_t[length]); + webrtc::scoped_ptr test_buffer(new uint8_t[length]); webrtc::ExtractBuffer(test_frame_, length, test_buffer.get()); EXPECT_EQ(0, capture_input_interface_->IncomingFrame(test_buffer.get(), length, capture_callback_.capability(), 0)); @@ -509,7 +557,8 @@ } // Test frame rate and no picture alarm. -TEST_F(VideoCaptureExternalTest , FrameRate) { +// Flaky on Win32, see webrtc:3270. +TEST_F(VideoCaptureExternalTest, DISABLED_ON_WIN(FrameRate)) { int64_t testTime = 3; TickTime startTime = TickTime::Now(); @@ -517,7 +566,7 @@ unsigned int length = webrtc::CalcBufferSize(webrtc::kI420, test_frame_.width(), test_frame_.height()); - webrtc::scoped_array test_buffer(new uint8_t[length]); + webrtc::scoped_ptr test_buffer(new uint8_t[length]); webrtc::ExtractBuffer(test_frame_, length, test_buffer.get()); EXPECT_EQ(0, capture_input_interface_->IncomingFrame(test_buffer.get(), length, capture_callback_.capability(), 0)); @@ -533,7 +582,7 @@ unsigned int length = webrtc::CalcBufferSize(webrtc::kI420, test_frame_.width(), test_frame_.height()); - webrtc::scoped_array test_buffer(new uint8_t[length]); + webrtc::scoped_ptr test_buffer(new uint8_t[length]); webrtc::ExtractBuffer(test_frame_, length, test_buffer.get()); EXPECT_EQ(0, capture_input_interface_->IncomingFrame(test_buffer.get(), length, capture_callback_.capability(), 0)); @@ -551,7 +600,7 @@ unsigned int length = webrtc::CalcBufferSize(webrtc::kI420, test_frame_.width(), test_frame_.height()); - webrtc::scoped_array test_buffer(new uint8_t[length]); + webrtc::scoped_ptr test_buffer(new uint8_t[length]); webrtc::ExtractBuffer(test_frame_, length, test_buffer.get()); EXPECT_EQ(0, capture_input_interface_->IncomingFrame(test_buffer.get(), length, capture_callback_.capability(), 0)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/video_capture.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/video_capture.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/video_capture.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/video_capture.gypi 2015-02-03 14:33:36.000000000 +0000 @@ -9,6 +9,10 @@ { 'targets': [ { + # Note this library is missing an implementation for the video capture. + # Targets must link with either 'video_capture_module_impl' or + # 'video_capture_module_internal_impl' depending on whether they want to + # use the internal capturer. 'target_name': 'video_capture_module', 'type': 'static_library', 'dependencies': [ @@ -31,124 +35,153 @@ 'video_capture_impl.cc', 'video_capture_impl.h', ], + }, + { + # Default video capture module implementation that only supports external + # capture. + 'target_name': 'video_capture_module_impl', + 'type': 'static_library', + 'dependencies': [ + 'video_capture_module', + ], + 'cflags_mozilla': [ + '$(NSPR_CFLAGS)', + ], + 'sources': [ + 'external/device_info_external.cc', + 'external/video_capture_external.cc', + ], + }, + { + 'target_name': 'video_capture_module_internal_impl', + 'type': 'static_library', + 'dependencies': [ + 'video_capture_module', + ], + 'cflags_mozilla': [ + '$(NSPR_CFLAGS)', + ], 'conditions': [ - ['include_internal_video_capture==0', { + ['include_v4l2_video_capture==1', { 'sources': [ - 'external/device_info_external.cc', - 'external/video_capture_external.cc', + 'linux/device_info_linux.cc', + 'linux/device_info_linux.h', + 'linux/video_capture_linux.cc', + 'linux/video_capture_linux.h', ], - }, { # include_internal_video_capture == 1 + }], # linux + ['OS=="mac"', { + 'sources': [ + 'mac/qtkit/video_capture_qtkit.h', + 'mac/qtkit/video_capture_qtkit.mm', + 'mac/qtkit/video_capture_qtkit_info.h', + 'mac/qtkit/video_capture_qtkit_info.mm', + 'mac/qtkit/video_capture_qtkit_info_objc.h', + 'mac/qtkit/video_capture_qtkit_info_objc.mm', + 'mac/qtkit/video_capture_qtkit_objc.h', + 'mac/qtkit/video_capture_qtkit_objc.mm', + 'mac/qtkit/video_capture_qtkit_utility.h', + 'mac/video_capture_mac.mm', + ], + 'link_settings': { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-framework Cocoa', + '-framework CoreVideo', + '-framework QTKit', + ], + }, + }, + }], # mac + # Note that since winsdk_samples isn't pulled into chromium gyp will + # fail to parse this rule and try to resolve the dependencies. This + # is not a problem since the internal video capture implementation + # should not be used in chrome - issue 3831. + ['OS=="win" and build_with_chromium==0', { 'conditions': [ - ['include_v4l2_video_capture==1', { - 'sources': [ - 'linux/device_info_linux.cc', - 'linux/device_info_linux.h', - 'linux/video_capture_linux.cc', - 'linux/video_capture_linux.h', - ], - }], # linux - ['OS=="mac"', { - 'sources': [ - 'mac/qtkit/video_capture_qtkit.h', - 'mac/qtkit/video_capture_qtkit.mm', - 'mac/qtkit/video_capture_qtkit_info.h', - 'mac/qtkit/video_capture_qtkit_info.mm', - 'mac/qtkit/video_capture_qtkit_info_objc.h', - 'mac/qtkit/video_capture_qtkit_info_objc.mm', - 'mac/qtkit/video_capture_qtkit_objc.h', - 'mac/qtkit/video_capture_qtkit_objc.mm', - 'mac/qtkit/video_capture_qtkit_utility.h', - 'mac/video_capture_mac.mm', - ], - 'link_settings': { - 'xcode_settings': { - 'OTHER_LDFLAGS': [ - '-framework QTKit', - ], - }, - }, - }], # mac - ['OS=="win"', { - 'conditions': [ - ['build_with_mozilla==0', { - 'dependencies': [ - '<(DEPTH)/third_party/winsdk_samples/winsdk_samples.gyp:directshow_baseclasses', - ], - }], - ], - 'sources': [ - 'windows/device_info_ds.cc', - 'windows/device_info_ds.h', - 'windows/device_info_mf.cc', - 'windows/device_info_mf.h', - 'windows/help_functions_ds.cc', - 'windows/help_functions_ds.h', - 'windows/sink_filter_ds.cc', - 'windows/sink_filter_ds.h', - 'windows/video_capture_ds.cc', - 'windows/video_capture_ds.h', - 'windows/video_capture_factory_windows.cc', - 'windows/video_capture_mf.cc', - 'windows/video_capture_mf.h', - 'windows/BasePin.cpp', - 'windows/BaseFilter.cpp', - 'windows/BaseInputPin.cpp', - 'windows/MediaType.cpp', - ], - 'link_settings': { - 'libraries': [ - '-lStrmiids.lib', - ], - }, - }], # win - ['OS=="android"', { - 'sources': [ - 'android/device_info_android.cc', - 'android/device_info_android.h', - 'android/video_capture_android.cc', - 'android/video_capture_android.h', + ['build_with_mozilla==0', { + 'dependencies': [ + '<(DEPTH)/third_party/winsdk_samples/winsdk_samples.gyp:directshow_baseclasses', ], - }], # android - ['OS=="ios"', { - 'sources': [ - 'ios/device_info_ios.h', - 'ios/device_info_ios.mm', - 'ios/device_info_ios_objc.h', - 'ios/device_info_ios_objc.mm', - 'ios/video_capture_ios.h', - 'ios/video_capture_ios.mm', - 'ios/video_capture_ios_objc.h', - 'ios/video_capture_ios_objc.mm', - ], - 'all_dependent_settings': { - 'xcode_settings': { - 'OTHER_LDFLAGS': [ - '-framework AVFoundation', - '-framework CoreMedia', - '-framework CoreVideo', - '-framework UIKit', - ], - }, - }, - }], # ios - ], # conditions - }], # include_internal_video_capture + }], + ], + 'sources': [ + 'windows/device_info_ds.cc', + 'windows/device_info_ds.h', + 'windows/device_info_mf.cc', + 'windows/device_info_mf.h', + 'windows/help_functions_ds.cc', + 'windows/help_functions_ds.h', + 'windows/sink_filter_ds.cc', + 'windows/sink_filter_ds.h', + 'windows/video_capture_ds.cc', + 'windows/video_capture_ds.h', + 'windows/video_capture_factory_windows.cc', + 'windows/video_capture_mf.cc', + 'windows/video_capture_mf.h', + 'windows/BasePin.cpp', + 'windows/BaseFilter.cpp', + 'windows/BaseInputPin.cpp', + 'windows/MediaType.cpp', + ], + 'link_settings': { + 'libraries': [ + '-lStrmiids.lib', + ], + }, + }], # win + ['OS=="android"', { + 'sources': [ + 'android/device_info_android.cc', + 'android/device_info_android.h', + 'android/video_capture_android.cc', + 'android/video_capture_android.h', + ], + }], # android + ['OS=="ios"', { + 'sources': [ + 'ios/device_info_ios.h', + 'ios/device_info_ios.mm', + 'ios/device_info_ios_objc.h', + 'ios/device_info_ios_objc.mm', + 'ios/rtc_video_capture_ios_objc.h', + 'ios/rtc_video_capture_ios_objc.mm', + 'ios/video_capture_ios.h', + 'ios/video_capture_ios.mm', + ], + 'xcode_settings': { + 'CLANG_ENABLE_OBJC_ARC': 'YES', + }, + 'all_dependent_settings': { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-framework AVFoundation', + '-framework CoreMedia', + '-framework CoreVideo', + '-framework UIKit', + ], + }, + }, + }], # ios ], # conditions - }, + } ], 'conditions': [ ['include_tests==1', { 'targets': [ { 'target_name': 'video_capture_tests', - 'type': 'executable', + 'type': '<(gtest_target_type)', 'dependencies': [ 'video_capture_module', + 'video_capture_module_internal_impl', 'webrtc_utility', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', '<(DEPTH)/testing/gtest.gyp:gtest', ], 'sources': [ + 'ensure_initialized.cc', + 'ensure_initialized.h', 'test/video_capture_unittest.cc', 'test/video_capture_main_mac.mm', ], @@ -172,6 +205,18 @@ '-lrt', ], }], + ['OS=="android"', { + 'dependencies': [ + '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code', + ], + # Need to disable error due to the line in + # base/android/jni_android.h triggering it: + # const BASE_EXPORT jobject GetApplicationContext() + # error: type qualifiers ignored on function return type + 'cflags': [ + '-Wno-ignored-qualifiers', + ], + }], ['OS=="mac"', { 'dependencies': [ # Link with a special main for mac so we can use the webcam. @@ -194,6 +239,17 @@ }, ], # targets 'conditions': [ + ['OS=="android"', { + 'targets': [ + { + 'target_name': 'video_capture_tests_apk_target', + 'type': 'none', + 'dependencies': [ + '<(apk_tests_path):video_capture_tests_apk', + ], + }, + ], + }], ['test_isolation_mode != "noop"', { 'targets': [ { @@ -204,7 +260,6 @@ ], 'includes': [ '../../build/isolate.gypi', - 'video_capture_tests.isolate', ], 'sources': [ 'video_capture_tests.isolate', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_impl.cc 2015-02-03 14:33:36.000000000 +0000 @@ -17,9 +17,9 @@ #include "webrtc/modules/video_capture/video_capture_config.h" #include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/ref_count.h" #include "webrtc/system_wrappers/interface/tick_util.h" -#include "webrtc/system_wrappers/interface/trace.h" #include "webrtc/system_wrappers/interface/trace_event.h" namespace webrtc @@ -260,11 +260,8 @@ const VideoCaptureCapability& frameInfo, int64_t captureTime/*=0*/) { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideoCapture, _id, - "IncomingFrame width %d, height %d", (int) frameInfo.width, - (int) frameInfo.height); - - CriticalSectionScoped cs(&_callBackCs); + CriticalSectionScoped cs(&_apiCs); + CriticalSectionScoped cs2(&_callBackCs); const int32_t width = frameInfo.width; const int32_t height = frameInfo.height; @@ -281,8 +278,7 @@ CalcBufferSize(commonVideoType, width, abs(height)) != videoFrameLength) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Wrong incoming frame length."); + LOG(LS_ERROR) << "Wrong incoming frame length."; return -1; } @@ -306,8 +302,8 @@ stride_uv, stride_uv); if (ret < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to allocate I420 frame."); + LOG(LS_ERROR) << "Failed to create empty frame, this should only " + "happen due to bad parameters."; return -1; } const int conversionResult = ConvertToI420(commonVideoType, @@ -319,9 +315,8 @@ &_captureFrame); if (conversionResult < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to convert capture frame from type %d to I420", - frameInfo.rawType); + LOG(LS_ERROR) << "Failed to convert capture frame from type " + << frameInfo.rawType << "to I420."; return -1; } DeliverCapturedFrame(_captureFrame, captureTime); @@ -338,7 +333,8 @@ int32_t VideoCaptureImpl::IncomingI420VideoFrame(I420VideoFrame* video_frame, int64_t captureTime) { - CriticalSectionScoped cs(&_callBackCs); + CriticalSectionScoped cs(&_apiCs); + CriticalSectionScoped cs2(&_callBackCs); DeliverCapturedFrame(*video_frame, captureTime); return 0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_internal.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_internal.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_internal.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_internal.h 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_VIDEO_CAPTURE_INTERNAL_H_ +#define WEBRTC_MODULES_VIDEO_CAPTURE_VIDEO_CAPTURE_INTERNAL_H_ + +#ifdef ANDROID +#include + +namespace webrtc { + +// In order to be able to use the internal webrtc video capture +// for android, the jvm objects must be set via this method. +int32_t SetCaptureAndroidVM(JavaVM* javaVM, jobject context); + +} // namespace webrtc + +#endif // ANDROID + +#endif // WEBRTC_MODULES_VIDEO_CAPTURE_VIDEO_CAPTURE_INTERNAL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_tests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_tests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_tests.isolate 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_tests.isolate 2015-02-03 14:33:36.000000000 +0000 @@ -8,28 +8,23 @@ { 'conditions': [ ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. 'variables': { - 'isolate_dependency_untracked': [ - '../../../../data/', - '../../../../resources/', + 'files': [ + '<(DEPTH)/data/', + '<(DEPTH)/resources/', ], }, }], ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../../../testing/test_env.py', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/video_capture_tests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_tracked': [ - '../../../testing/test_env.py', + 'files': [ + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/video_capture_tests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '../../../tools/swarming_client/', - ], }, }], ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/windows/sink_filter_ds.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/windows/sink_filter_ds.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/windows/sink_filter_ds.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/windows/sink_filter_ds.cc 2015-02-03 14:33:36.000000000 +0000 @@ -442,6 +442,7 @@ STDMETHODIMP CaptureSinkFilter::Pause() { + LockReceive(); LockFilter(); if (mState == State_Stopped) { @@ -461,6 +462,7 @@ mState = State_Paused; } UnlockFilter(); + UnlockReceive(); return S_OK; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/windows/video_capture_ds.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/windows/video_capture_ds.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/windows/video_capture_ds.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_capture/windows/video_capture_ds.cc 2015-02-03 14:33:36.000000000 +0000 @@ -45,13 +45,14 @@ if (_dvFilter) _graphBuilder->RemoveFilter(_dvFilter); } + RELEASE_AND_CLEAR(_inputSendPin); + RELEASE_AND_CLEAR(_outputCapturePin); + RELEASE_AND_CLEAR(_captureFilter); // release the capture device RELEASE_AND_CLEAR(_sinkFilter); RELEASE_AND_CLEAR(_dvFilter); RELEASE_AND_CLEAR(_mediaControl); - RELEASE_AND_CLEAR(_inputSendPin); - RELEASE_AND_CLEAR(_outputCapturePin); RELEASE_AND_CLEAR(_inputDvPin); RELEASE_AND_CLEAR(_outputDvPin); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/BUILD.gn 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,194 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../../build/webrtc.gni") + +source_set("video_coding") { + sources = [ + "main/interface/video_coding.h", + "main/interface/video_coding_defines.h", + "main/source/codec_database.cc", + "main/source/codec_database.h", + "main/source/codec_timer.cc", + "main/source/codec_timer.h", + "main/source/content_metrics_processing.cc", + "main/source/content_metrics_processing.h", + "main/source/decoding_state.cc", + "main/source/decoding_state.h", + "main/source/encoded_frame.cc", + "main/source/encoded_frame.h", + "main/source/er_tables_xor.h", + "main/source/fec_tables_xor.h", + "main/source/frame_buffer.cc", + "main/source/frame_buffer.h", + "main/source/generic_decoder.cc", + "main/source/generic_decoder.h", + "main/source/generic_encoder.cc", + "main/source/generic_encoder.h", + "main/source/inter_frame_delay.cc", + "main/source/inter_frame_delay.h", + "main/source/internal_defines.h", + "main/source/jitter_buffer.cc", + "main/source/jitter_buffer.h", + "main/source/jitter_buffer_common.h", + "main/source/jitter_estimator.cc", + "main/source/jitter_estimator.h", + "main/source/media_opt_util.cc", + "main/source/media_opt_util.h", + "main/source/media_optimization.cc", + "main/source/media_optimization.h", + "main/source/nack_fec_tables.h", + "main/source/packet.cc", + "main/source/packet.h", + "main/source/qm_select_data.h", + "main/source/qm_select.cc", + "main/source/qm_select.h", + "main/source/receiver.cc", + "main/source/receiver.h", + "main/source/rtt_filter.cc", + "main/source/rtt_filter.h", + "main/source/session_info.cc", + "main/source/session_info.h", + "main/source/timestamp_map.cc", + "main/source/timestamp_map.h", + "main/source/timing.cc", + "main/source/timing.h", + "main/source/video_coding_impl.cc", + "main/source/video_coding_impl.h", + "main/source/video_receiver.cc", + "main/source/video_sender.cc", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ + ":video_coding_utility", + ":webrtc_i420", + ":webrtc_vp8", + ":webrtc_vp9", + "../../common_video", + "../../system_wrappers", + ] +} + +source_set("video_coding_utility") { + sources = [ + "utility/include/frame_dropper.h", + "utility/frame_dropper.cc", + "utility/quality_scaler.cc", + "utility/quality_scaler.h", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ "../../system_wrappers" ] +} + +source_set("webrtc_i420") { + sources = [ + "codecs/i420/main/source/i420.cc", + "codecs/i420/main/interface/i420.h", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ "../../system_wrappers" ] +} + +source_set("webrtc_vp8") { + sources = [ + "codecs/vp8/default_temporal_layers.cc", + "codecs/vp8/default_temporal_layers.h", + "codecs/vp8/realtime_temporal_layers.cc", + "codecs/vp8/reference_picture_selection.cc", + "codecs/vp8/reference_picture_selection.h", + "codecs/vp8/include/vp8.h", + "codecs/vp8/include/vp8_common_types.h", + "codecs/vp8/temporal_layers.h", + "codecs/vp8/vp8_factory.cc", + "codecs/vp8/vp8_impl.cc", + "codecs/vp8/vp8_impl.h", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + # TODO(kjellander): Remove once libvpx has changed it's libvpx_config to be + # in direct_dependent_configs. + configs += [ "//third_party/libvpx:libvpx_config" ] + + deps = [ + ":video_coding_utility", + "../../common_video", + "../../system_wrappers", + ] + if (rtc_build_libvpx) { + deps += [ + "//third_party/libvpx", + ] + } +} + +source_set("webrtc_vp9") { + sources = [ + "codecs/vp9/include/vp9.h", + "codecs/vp9/vp9_impl.cc", + "codecs/vp9/vp9_impl.h", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + # TODO(kjellander): Remove once libvpx has changed it's libvpx_config to be + # in direct_dependent_configs. + configs += [ "//third_party/libvpx:libvpx_config" ] + + deps = [ + ":video_coding_utility", + "../../common_video", + "../../system_wrappers", + ] + if (rtc_build_libvpx) { + deps += [ + "//third_party/libvpx", + ] + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/i420/main/source/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/i420/main/source/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/i420/main/source/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/i420/main/source/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../../../android-webrtc.mk - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_i420 -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := i420.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../../../../../.. \ - $(LOCAL_PATH)/../../../../../../common_video/interface \ - $(LOCAL_PATH)/../../../../../../system_wrappers/interface - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/i420/main/source/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/i420/main/source/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/i420/main/source/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/i420/main/source/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/interface/video_codec_interface.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/interface/video_codec_interface.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/interface/video_codec_interface.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/interface/video_codec_interface.h 2015-02-03 14:33:36.000000000 +0000 @@ -15,11 +15,11 @@ #include "webrtc/common_types.h" #include "webrtc/common_video/interface/i420_video_frame.h" -#include "webrtc/common_video/interface/video_image.h" #include "webrtc/modules/interface/module_common_types.h" #include "webrtc/modules/video_coding/codecs/interface/video_error_codes.h" - #include "webrtc/typedefs.h" +#include "webrtc/video_decoder.h" +#include "webrtc/video_encoder.h" namespace webrtc { @@ -28,19 +28,31 @@ // Note: if any pointers are added to this struct, it must be fitted // with a copy-constructor. See below. -struct CodecSpecificInfoVP8 -{ - bool hasReceivedSLI; - uint8_t pictureIdSLI; - bool hasReceivedRPSI; - uint64_t pictureIdRPSI; - int16_t pictureId; // negative value to skip pictureId - bool nonReference; - uint8_t simulcastIdx; - uint8_t temporalIdx; - bool layerSync; - int tl0PicIdx; // Negative value to skip tl0PicIdx - int8_t keyIdx; // negative value to skip keyIdx +struct CodecSpecificInfoVP8 { + bool hasReceivedSLI; + uint8_t pictureIdSLI; + bool hasReceivedRPSI; + uint64_t pictureIdRPSI; + int16_t pictureId; // Negative value to skip pictureId. + bool nonReference; + uint8_t simulcastIdx; + uint8_t temporalIdx; + bool layerSync; + int tl0PicIdx; // Negative value to skip tl0PicIdx. + int8_t keyIdx; // Negative value to skip keyIdx. +}; + +struct CodecSpecificInfoVP9 { + bool hasReceivedSLI; + uint8_t pictureIdSLI; + bool hasReceivedRPSI; + uint64_t pictureIdRPSI; + int16_t pictureId; // Negative value to skip pictureId. + bool nonReference; + uint8_t temporalIdx; + bool layerSync; + int tl0PicIdx; // Negative value to skip tl0PicIdx. + int8_t keyIdx; // Negative value to skip keyIdx. }; struct CodecSpecificInfoGeneric { @@ -54,9 +66,10 @@ }; union CodecSpecificInfoUnion { - CodecSpecificInfoGeneric generic; - CodecSpecificInfoVP8 VP8; - CodecSpecificInfoH264 H264; + CodecSpecificInfoGeneric generic; + CodecSpecificInfoVP8 VP8; + CodecSpecificInfoVP9 VP9; + CodecSpecificInfoH264 H264; }; // Note: if any pointers are added to this struct or its sub-structs, it @@ -68,196 +81,6 @@ CodecSpecificInfoUnion codecSpecific; }; -class EncodedImageCallback -{ -public: - virtual ~EncodedImageCallback() {}; - - // Callback function which is called when an image has been encoded. - // - // Input: - // - encodedImage : The encoded image - // - // Return value : > 0, signals to the caller that one or more future frames - // should be dropped to keep bit rate or frame rate. - // = 0, if OK. - // < 0, on error. - virtual int32_t - Encoded(EncodedImage& encodedImage, - const CodecSpecificInfo* codecSpecificInfo = NULL, - const RTPFragmentationHeader* fragmentation = NULL) = 0; -}; - -class VideoEncoder -{ -public: - virtual ~VideoEncoder() {}; - - // Initialize the encoder with the information from the VideoCodec. - // - // Input: - // - codecSettings : Codec settings - // - numberOfCores : Number of cores available for the encoder - // - maxPayloadSize : The maximum size each payload is allowed - // to have. Usually MTU - overhead. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual int32_t InitEncode(const VideoCodec* codecSettings, int32_t numberOfCores, uint32_t maxPayloadSize) = 0; - - // Encode an I420 image (as a part of a video stream). The encoded image - // will be returned to the user through the encode complete callback. - // - // Input: - // - inputImage : Image to be encoded - // - codecSpecificInfo : Pointer to codec specific data - // - frame_types : The frame type to encode - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 - // otherwise. - virtual int32_t Encode( - const I420VideoFrame& inputImage, - const CodecSpecificInfo* codecSpecificInfo, - const std::vector* frame_types) = 0; - - // Register an encode complete callback object. - // - // Input: - // - callback : Callback object which handles encoded images. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual int32_t RegisterEncodeCompleteCallback(EncodedImageCallback* callback) = 0; - - // Free encoder memory. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual int32_t Release() = 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 - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual int32_t SetChannelParameters(uint32_t packetLoss, int rtt) = 0; - - // Inform the encoder about the new target bit rate. - // - // - newBitRate : New target bit rate - // - frameRate : The target frame rate - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual int32_t SetRates(uint32_t newBitRate, uint32_t frameRate) = 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 - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual int32_t SetPeriodicKeyFrames(bool enable) { return WEBRTC_VIDEO_CODEC_ERROR; } - - // Codec configuration data to send out-of-band, i.e. in SIP call setup - // - // - buffer : Buffer pointer to where the configuration data - // should be stored - // - size : The size of the buffer in bytes - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual int32_t CodecConfigParameters(uint8_t* /*buffer*/, int32_t /*size*/) { return WEBRTC_VIDEO_CODEC_ERROR; } -}; - -class DecodedImageCallback -{ -public: - virtual ~DecodedImageCallback() {}; - - // Callback function which is called when an image has been decoded. - // - // Input: - // - decodedImage : The decoded image. - // - // Return value : 0 if OK, < 0 otherwise. - virtual int32_t Decoded(I420VideoFrame& decodedImage) = 0; - - virtual int32_t ReceivedDecodedReferenceFrame(const uint64_t pictureId) {return -1;} - - virtual int32_t ReceivedDecodedFrame(const uint64_t pictureId) {return -1;} -}; - -class VideoDecoder -{ -public: - virtual ~VideoDecoder() {}; - - // Initialize the decoder with the information from the VideoCodec. - // - // Input: - // - inst : Codec settings - // - numberOfCores : Number of cores available for the decoder - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual int32_t InitDecode(const VideoCodec* codecSettings, int32_t numberOfCores) = 0; - - // Decode encoded image (as a part of a video stream). The decoded image - // will be returned to the user through the decode complete callback. - // - // Input: - // - inputImage : Encoded image to be decoded - // - 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 : Pointer to codec specific data - // - renderTimeMs : System time to render in milliseconds. Only - // used by decoders with internal rendering. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual int32_t - Decode(const EncodedImage& inputImage, - bool missingFrames, - const RTPFragmentationHeader* fragmentation, - const CodecSpecificInfo* codecSpecificInfo = NULL, - int64_t renderTimeMs = -1) = 0; - - // Register an decode complete callback object. - // - // Input: - // - callback : Callback object which handles decoded images. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual int32_t RegisterDecodeCompleteCallback(DecodedImageCallback* callback) = 0; - - // Free decoder memory. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual int32_t Release() = 0; - - // Reset decoder state and prepare for a new call. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual int32_t Reset() = 0; - - // Codec configuration data sent out-of-band, i.e. in SIP call setup - // - // Input/Output: - // - buffer : Buffer pointer to the configuration data - // - size : The size of the configuration data in - // bytes - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual int32_t SetCodecConfigParameters(const uint8_t* /*buffer*/, int32_t /*size*/) { return WEBRTC_VIDEO_CODEC_ERROR; } - - // Create a copy of the codec and its internal state. - // - // Return value : A copy of the instance if OK, NULL otherwise. - virtual VideoDecoder* Copy() { return NULL; } -}; - } // namespace webrtc #endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_INTERFACE_VIDEO_CODEC_INTERFACE_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -1,4 +1,3 @@ stefan@webrtc.org -mikhal@webrtc.org marpan@webrtc.org henrik.lundin@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/mock/mock_packet_manipulator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/mock/mock_packet_manipulator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/mock/mock_packet_manipulator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/mock/mock_packet_manipulator.h 2015-02-03 14:33:36.000000000 +0000 @@ -16,8 +16,8 @@ #include #include "testing/gmock/include/gmock/gmock.h" -#include "webrtc/common_video/interface/video_image.h" #include "webrtc/typedefs.h" +#include "webrtc/video_frame.h" namespace webrtc { namespace test { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/packet_manipulator.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/packet_manipulator.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/packet_manipulator.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/packet_manipulator.cc 2015-02-03 14:33:36.000000000 +0000 @@ -90,7 +90,7 @@ // get the same behavior as long as we're using a fixed initial seed. critsect_->Enter(); srand(random_seed_); - random_seed_ = std::rand(); + random_seed_ = rand(); critsect_->Leave(); return (random_seed_ + 1.0)/(RAND_MAX + 1.0); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/videoprocessor.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/videoprocessor.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/videoprocessor.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/videoprocessor.cc 2015-02-03 14:33:36.000000000 +0000 @@ -331,7 +331,7 @@ } // TODO(mikhal): Extracting the buffer for now - need to update test. int length = CalcBufferSize(kI420, up_image.width(), up_image.height()); - scoped_array image_buffer(new uint8_t[length]); + scoped_ptr image_buffer(new uint8_t[length]); length = ExtractBuffer(up_image, length, image_buffer.get()); // Update our copy of the last successful frame: memcpy(last_successful_frame_buffer_, image_buffer.get(), length); @@ -344,7 +344,7 @@ // Update our copy of the last successful frame: // TODO(mikhal): Add as a member function, so won't be allocated per frame. int length = CalcBufferSize(kI420, image.width(), image.height()); - scoped_array image_buffer(new uint8_t[length]); + scoped_ptr image_buffer(new uint8_t[length]); length = ExtractBuffer(image, length, image_buffer.get()); assert(length > 0); memcpy(last_successful_frame_buffer_, image_buffer.get(), length); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -16,6 +16,7 @@ #include "webrtc/modules/video_coding/codecs/test/packet_manipulator.h" #include "webrtc/modules/video_coding/codecs/test/videoprocessor.h" #include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h" +#include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h" #include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h" #include "webrtc/modules/video_coding/main/interface/video_coding.h" #include "webrtc/test/testsupport/fileutils.h" @@ -37,6 +38,7 @@ // Codec and network settings. struct CodecConfigPars { + VideoCodecType codec_type; float packet_loss; int num_temporal_layers; int key_frame_interval; @@ -136,6 +138,7 @@ float start_bitrate_; // Codec and network settings. + VideoCodecType codec_type_; float packet_loss_; int num_temporal_layers_; int key_frame_interval_; @@ -149,14 +152,24 @@ virtual ~VideoProcessorIntegrationTest() {} void SetUpCodecConfig() { - encoder_ = VP8Encoder::Create(); - decoder_ = VP8Decoder::Create(); + if (codec_type_ == kVideoCodecVP8) { + encoder_ = VP8Encoder::Create(); + decoder_ = VP8Decoder::Create(); + VideoCodingModule::Codec(kVideoCodecVP8, &codec_settings_); + } else if (codec_type_ == kVideoCodecVP9) { + encoder_ = VP9Encoder::Create(); + decoder_ = VP9Decoder::Create(); + VideoCodingModule::Codec(kVideoCodecVP9, &codec_settings_); + } // CIF is currently used for all tests below. // Setup the TestConfig struct for processing of a clip in CIF resolution. config_.input_filename = webrtc::test::ResourcePath("foreman_cif", "yuv"); - config_.output_filename = tmpnam(NULL); + + // Generate an output filename in a safe way. + config_.output_filename = webrtc::test::TempFilename( + webrtc::test::OutputPath(), "videoprocessor_integrationtest"); config_.frame_length_in_bytes = CalcBufferSize(kI420, kCIFWidth, kCIFHeight); config_.verbose = false; @@ -166,26 +179,42 @@ config_.keyframe_interval = key_frame_interval_; config_.networking_config.packet_loss_probability = packet_loss_; - // Get a codec configuration struct and configure it. - VideoCodingModule::Codec(kVideoCodecVP8, &codec_settings_); + // Configure codec settings. config_.codec_settings = &codec_settings_; config_.codec_settings->startBitrate = start_bitrate_; config_.codec_settings->width = kCIFWidth; config_.codec_settings->height = kCIFHeight; - // These features may be set depending on the test. - config_.codec_settings->codecSpecific.VP8.errorConcealmentOn = - error_concealment_on_; - config_.codec_settings->codecSpecific.VP8.denoisingOn = - denoising_on_; - config_.codec_settings->codecSpecific.VP8.numberOfTemporalLayers = - num_temporal_layers_; - config_.codec_settings->codecSpecific.VP8.frameDroppingOn = - frame_dropper_on_; - config_.codec_settings->codecSpecific.VP8.automaticResizeOn = - spatial_resize_on_; - config_.codec_settings->codecSpecific.VP8.keyFrameInterval = - kBaseKeyFrameInterval; + // These features may be set depending on the test. + switch (config_.codec_settings->codecType) { + case kVideoCodecVP8: + config_.codec_settings->codecSpecific.VP8.errorConcealmentOn = + error_concealment_on_; + config_.codec_settings->codecSpecific.VP8.denoisingOn = + denoising_on_; + config_.codec_settings->codecSpecific.VP8.numberOfTemporalLayers = + num_temporal_layers_; + config_.codec_settings->codecSpecific.VP8.frameDroppingOn = + frame_dropper_on_; + config_.codec_settings->codecSpecific.VP8.automaticResizeOn = + spatial_resize_on_; + config_.codec_settings->codecSpecific.VP8.keyFrameInterval = + kBaseKeyFrameInterval; + break; + case kVideoCodecVP9: + config_.codec_settings->codecSpecific.VP9.denoisingOn = + denoising_on_; + config_.codec_settings->codecSpecific.VP9.numberOfTemporalLayers = + num_temporal_layers_; + config_.codec_settings->codecSpecific.VP9.frameDroppingOn = + frame_dropper_on_; + config_.codec_settings->codecSpecific.VP9.keyFrameInterval = + kBaseKeyFrameInterval; + break; + default: + assert(false); + break; + } frame_reader_ = new webrtc::test::FrameReaderImpl(config_.input_filename, config_.frame_length_in_bytes); @@ -402,6 +431,7 @@ CodecConfigPars process, RateControlMetrics* rc_metrics) { // Codec/config settings. + codec_type_ = process.codec_type; start_bitrate_ = rate_profile.target_bit_rate[0]; packet_loss_ = process.packet_loss; key_frame_interval_ = process.key_frame_interval; @@ -511,6 +541,7 @@ } void SetCodecParameters(CodecConfigPars* process_settings, + VideoCodecType codec_type, float packet_loss, int key_frame_interval, int num_temporal_layers, @@ -518,6 +549,7 @@ bool denoising_on, bool frame_dropper_on, bool spatial_resize_on) { + process_settings->codec_type = codec_type; process_settings->packet_loss = packet_loss; process_settings->key_frame_interval = key_frame_interval; process_settings->num_temporal_layers = num_temporal_layers, @@ -557,11 +589,34 @@ rc_metrics[update_index].num_spatial_resizes = num_spatial_resizes; } -// Run with no packet loss and fixed bitrate. Quality should be very high. +// VP9: Run with no packet loss and fixed bitrate. Quality should be very high. // One key frame (first frame only) in sequence. Setting |key_frame_interval| // to -1 below means no periodic key frames in test. -TEST_F(VideoProcessorIntegrationTest, - DISABLED_ON_ANDROID(ProcessZeroPacketLoss)) { +TEST_F(VideoProcessorIntegrationTest, Process0PercentPacketLossVP9) { + // Bitrate and frame rate profile. + RateProfile rate_profile; + SetRateProfilePars(&rate_profile, 0, 500, 30, 0); + rate_profile.frame_index_rate_update[1] = kNbrFramesShort + 1; + rate_profile.num_frames = kNbrFramesShort; + // Codec/network settings. + CodecConfigPars process_settings; + SetCodecParameters(&process_settings, kVideoCodecVP9, 0.0f, -1, 1, false, + false, true, false); + // Metrics for expected quality. + QualityMetrics quality_metrics; + SetQualityMetrics(&quality_metrics, 37.0, 36.0, 0.93, 0.92); + // Metrics for rate control. + RateControlMetrics rc_metrics[1]; + SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 15, 0); + ProcessFramesAndVerify(quality_metrics, + rate_profile, + process_settings, + rc_metrics); +} + +// VP9: Run with 5% packet loss and fixed bitrate. Quality should be a bit +// lower. One key frame (first frame only) in sequence. +TEST_F(VideoProcessorIntegrationTest, Process5PercentPacketLossVP9) { // Bitrate and frame rate profile. RateProfile rate_profile; SetRateProfilePars(&rate_profile, 0, 500, 30, 0); @@ -569,10 +624,11 @@ rate_profile.num_frames = kNbrFramesShort; // Codec/network settings. CodecConfigPars process_settings; - SetCodecParameters(&process_settings, 0.0f, -1, 1, false, true, true, false); + SetCodecParameters(&process_settings, kVideoCodecVP9, 0.05f, -1, 1, false, + false, true, false); // Metrics for expected quality. QualityMetrics quality_metrics; - SetQualityMetrics(&quality_metrics, 36.95, 33.0, 0.90, 0.90); + SetQualityMetrics(&quality_metrics, 17.0, 15.0, 0.45, 0.38); // Metrics for rate control. RateControlMetrics rc_metrics[1]; SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 15, 0); @@ -582,10 +638,80 @@ rc_metrics); } -// Run with 5% packet loss and fixed bitrate. Quality should be a bit lower. + +// VP9: Run with no packet loss, with varying bitrate (3 rate updates): +// low to high to medium. Check that quality and encoder response to the new +// target rate/per-frame bandwidth (for each rate update) is within limits. // One key frame (first frame only) in sequence. +TEST_F(VideoProcessorIntegrationTest, ProcessNoLossChangeBitRateVP9) { + // Bitrate and frame rate profile. + RateProfile rate_profile; + SetRateProfilePars(&rate_profile, 0, 200, 30, 0); + SetRateProfilePars(&rate_profile, 1, 800, 30, 100); + SetRateProfilePars(&rate_profile, 2, 500, 30, 200); + rate_profile.frame_index_rate_update[3] = kNbrFramesLong + 1; + rate_profile.num_frames = kNbrFramesLong; + // Codec/network settings. + CodecConfigPars process_settings; + SetCodecParameters(&process_settings, kVideoCodecVP9, 0.0f, -1, 1, false, + false, true, false); + // Metrics for expected quality. + QualityMetrics quality_metrics; + SetQualityMetrics(&quality_metrics, 36.0, 31.8, 0.90, 0.85); + // Metrics for rate control. + RateControlMetrics rc_metrics[3]; + SetRateControlMetrics(rc_metrics, 0, 0, 30, 20, 20, 20, 0); + SetRateControlMetrics(rc_metrics, 1, 2, 0, 20, 20, 60, 0); + SetRateControlMetrics(rc_metrics, 2, 0, 0, 20, 20, 40, 0); + ProcessFramesAndVerify(quality_metrics, + rate_profile, + process_settings, + rc_metrics); +} + +// VP9: Run with no packet loss, with an update (decrease) in frame rate. +// Lower frame rate means higher per-frame-bandwidth, so easier to encode. +// At the low bitrate in this test, this means better rate control after the +// update(s) to lower frame rate. So expect less frame drops, and max values +// for the rate control metrics can be lower. One key frame (first frame only). +// Note: quality after update should be higher but we currently compute quality +// metrics averaged over whole sequence run. TEST_F(VideoProcessorIntegrationTest, - DISABLED_ON_ANDROID(Process5PercentPacketLoss)) { + ProcessNoLossChangeFrameRateFrameDropVP9) { + config_.networking_config.packet_loss_probability = 0; + // Bitrate and frame rate profile. + RateProfile rate_profile; + SetRateProfilePars(&rate_profile, 0, 50, 24, 0); + SetRateProfilePars(&rate_profile, 1, 50, 15, 100); + SetRateProfilePars(&rate_profile, 2, 50, 10, 200); + rate_profile.frame_index_rate_update[3] = kNbrFramesLong + 1; + rate_profile.num_frames = kNbrFramesLong; + // Codec/network settings. + CodecConfigPars process_settings; + SetCodecParameters(&process_settings, kVideoCodecVP9, 0.0f, -1, 1, false, + false, true, false); + // Metrics for expected quality. + QualityMetrics quality_metrics; + SetQualityMetrics(&quality_metrics, 29.0, 17.0, 0.80, 0.40); + // Metrics for rate control. + RateControlMetrics rc_metrics[3]; + SetRateControlMetrics(rc_metrics, 0, 50, 60, 100, 15, 45, 0); + SetRateControlMetrics(rc_metrics, 1, 30, 0, 65, 10, 35, 0); + SetRateControlMetrics(rc_metrics, 2, 5, 0, 38, 10, 30, 0); + ProcessFramesAndVerify(quality_metrics, + rate_profile, + process_settings, + rc_metrics); +} + + +// TODO(marpan): Add temporal layer test for VP9, once changes are in +// vp9 wrapper for this. + +// VP8: Run with no packet loss and fixed bitrate. Quality should be very high. +// One key frame (first frame only) in sequence. Setting |key_frame_interval| +// to -1 below means no periodic key frames in test. +TEST_F(VideoProcessorIntegrationTest, ProcessZeroPacketLoss) { // Bitrate and frame rate profile. RateProfile rate_profile; SetRateProfilePars(&rate_profile, 0, 500, 30, 0); @@ -593,7 +719,32 @@ rate_profile.num_frames = kNbrFramesShort; // Codec/network settings. CodecConfigPars process_settings; - SetCodecParameters(&process_settings, 0.05f, -1, 1, false, true, true, false); + SetCodecParameters(&process_settings, kVideoCodecVP8, 0.0f, -1, 1, false, + true, true, false); + // Metrics for expected quality. + QualityMetrics quality_metrics; + SetQualityMetrics(&quality_metrics, 34.95, 33.0, 0.90, 0.89); + // Metrics for rate control. + RateControlMetrics rc_metrics[1]; + SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 15, 0); + ProcessFramesAndVerify(quality_metrics, + rate_profile, + process_settings, + rc_metrics); +} + +// VP8: Run with 5% packet loss and fixed bitrate. Quality should be a bit +// lower. One key frame (first frame only) in sequence. +TEST_F(VideoProcessorIntegrationTest, Process5PercentPacketLoss) { + // Bitrate and frame rate profile. + RateProfile rate_profile; + SetRateProfilePars(&rate_profile, 0, 500, 30, 0); + rate_profile.frame_index_rate_update[1] = kNbrFramesShort + 1; + rate_profile.num_frames = kNbrFramesShort; + // Codec/network settings. + CodecConfigPars process_settings; + SetCodecParameters(&process_settings, kVideoCodecVP8, 0.05f, -1, 1, false, + true, true, false); // Metrics for expected quality. QualityMetrics quality_metrics; SetQualityMetrics(&quality_metrics, 20.0, 16.0, 0.60, 0.40); @@ -606,10 +757,9 @@ rc_metrics); } -// Run with 10% packet loss and fixed bitrate. Quality should be even lower. +// VP8: Run with 10% packet loss and fixed bitrate. Quality should be lower. // One key frame (first frame only) in sequence. -TEST_F(VideoProcessorIntegrationTest, - DISABLED_ON_ANDROID(Process10PercentPacketLoss)) { +TEST_F(VideoProcessorIntegrationTest, Process10PercentPacketLoss) { // Bitrate and frame rate profile. RateProfile rate_profile; SetRateProfilePars(&rate_profile, 0, 500, 30, 0); @@ -617,7 +767,8 @@ rate_profile.num_frames = kNbrFramesShort; // Codec/network settings. CodecConfigPars process_settings; - SetCodecParameters(&process_settings, 0.1f, -1, 1, false, true, true, false); + SetCodecParameters(&process_settings, kVideoCodecVP8, 0.1f, -1, 1, false, + true, true, false); // Metrics for expected quality. QualityMetrics quality_metrics; SetQualityMetrics(&quality_metrics, 19.0, 16.0, 0.50, 0.35); @@ -630,12 +781,21 @@ rc_metrics); } -// Run with no packet loss, with varying bitrate (3 rate updates): +// The tests below are currently disabled for Android. For ARM, the encoder +// uses |cpu_speed| = 12, as opposed to default |cpu_speed| <= 6 for x86, +// which leads to significantly different quality. The quality and rate control +// settings in the tests below are defined for encoder speed setting +// |cpu_speed| <= ~6. A number of settings would need to be significantly +// modified for the |cpu_speed| = 12 case. For now, keep the tests below +// disabled on Android. Some quality parameter in the above test has been +// adjusted to also pass for |cpu_speed| <= 12. + +// VP8: Run with no packet loss, with varying bitrate (3 rate updates): // low to high to medium. Check that quality and encoder response to the new // target rate/per-frame bandwidth (for each rate update) is within limits. // One key frame (first frame only) in sequence. TEST_F(VideoProcessorIntegrationTest, - DISABLED_ON_ANDROID(ProcessNoLossChangeBitRate)) { + DISABLED_ON_ANDROID(ProcessNoLossChangeBitRateVP8)) { // Bitrate and frame rate profile. RateProfile rate_profile; SetRateProfilePars(&rate_profile, 0, 200, 30, 0); @@ -645,7 +805,8 @@ rate_profile.num_frames = kNbrFramesLong; // Codec/network settings. CodecConfigPars process_settings; - SetCodecParameters(&process_settings, 0.0f, -1, 1, false, true, true, false); + SetCodecParameters(&process_settings, kVideoCodecVP8, 0.0f, -1, 1, false, + true, true, false); // Metrics for expected quality. QualityMetrics quality_metrics; SetQualityMetrics(&quality_metrics, 34.0, 32.0, 0.85, 0.80); @@ -660,15 +821,15 @@ rc_metrics); } -// Run with no packet loss, with an update (decrease) in frame rate. +// VP8: Run with no packet loss, with an update (decrease) in frame rate. // Lower frame rate means higher per-frame-bandwidth, so easier to encode. // At the bitrate in this test, this means better rate control after the // update(s) to lower frame rate. So expect less frame drops, and max values // for the rate control metrics can be lower. One key frame (first frame only). // Note: quality after update should be higher but we currently compute quality -// metrics avergaed over whole sequence run. +// metrics averaged over whole sequence run. TEST_F(VideoProcessorIntegrationTest, - DISABLED_ON_ANDROID(ProcessNoLossChangeFrameRateFrameDrop)) { + DISABLED_ON_ANDROID(ProcessNoLossChangeFrameRateFrameDropVP8)) { config_.networking_config.packet_loss_probability = 0; // Bitrate and frame rate profile. RateProfile rate_profile; @@ -679,7 +840,8 @@ rate_profile.num_frames = kNbrFramesLong; // Codec/network settings. CodecConfigPars process_settings; - SetCodecParameters(&process_settings, 0.0f, -1, 1, false, true, true, false); + SetCodecParameters(&process_settings, kVideoCodecVP8, 0.0f, -1, 1, false, + true, true, false); // Metrics for expected quality. QualityMetrics quality_metrics; SetQualityMetrics(&quality_metrics, 31.0, 22.0, 0.80, 0.65); @@ -694,47 +856,39 @@ rc_metrics); } -// Run with no packet loss, at low bitrate, then increase rate somewhat. -// Key frame is thrown in every 120 frames. Can expect some frame drops after -// key frame, even at high rate. The internal spatial resizer is on, so expect -// spatial resize down at first key frame, and back up at second key frame. -// Error_concealment is off in this test since there is a memory leak with -// resizing and error concealment. +// Run with no packet loss, at low bitrate. During this time we should've +// resized once. TEST_F(VideoProcessorIntegrationTest, - DISABLED_ON_ANDROID(ProcessNoLossSpatialResizeFrameDrop)) { + DISABLED_ON_ANDROID(ProcessNoLossSpatialResizeFrameDropVP8)) { config_.networking_config.packet_loss_probability = 0; // Bitrate and frame rate profile. RateProfile rate_profile; - SetRateProfilePars(&rate_profile, 0, 100, 30, 0); - SetRateProfilePars(&rate_profile, 1, 200, 30, 120); - SetRateProfilePars(&rate_profile, 2, 200, 30, 240); - rate_profile.frame_index_rate_update[3] = kNbrFramesLong + 1; + SetRateProfilePars(&rate_profile, 0, 50, 30, 0); + rate_profile.frame_index_rate_update[1] = kNbrFramesLong + 1; rate_profile.num_frames = kNbrFramesLong; // Codec/network settings. CodecConfigPars process_settings; - SetCodecParameters(&process_settings, 0.0f, 120, 1, false, true, true, true); - // Metrics for expected quality.: lower quality on average from up-sampling - // the down-sampled portion of the run, in case resizer is on. + SetCodecParameters(&process_settings, kVideoCodecVP8, 0.0f, kNbrFramesLong, + 1, false, true, true, true); + // Metrics for expected quality. QualityMetrics quality_metrics; - SetQualityMetrics(&quality_metrics, 29.0, 20.0, 0.75, 0.60); + SetQualityMetrics(&quality_metrics, 25.0, 15.0, 0.70, 0.40); // Metrics for rate control. - RateControlMetrics rc_metrics[3]; - SetRateControlMetrics(rc_metrics, 0, 45, 30, 75, 20, 70, 0); - SetRateControlMetrics(rc_metrics, 1, 20, 35, 30, 20, 15, 1); - SetRateControlMetrics(rc_metrics, 2, 0, 30, 30, 15, 25, 1); + RateControlMetrics rc_metrics[1]; + SetRateControlMetrics(rc_metrics, 0, 160, 60, 120, 20, 70, 1); ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings, rc_metrics); } -// Run with no packet loss, with 3 temporal layers, with a rate update in the -// middle of the sequence. The max values for the frame size mismatch and +// VP8: Run with no packet loss, with 3 temporal layers, with a rate update in +// the middle of the sequence. The max values for the frame size mismatch and // encoding rate mismatch are applied to each layer. // No dropped frames in this test, and internal spatial resizer is off. // One key frame (first frame only) in sequence, so no spatial resizing. TEST_F(VideoProcessorIntegrationTest, - DISABLED_ON_ANDROID(ProcessNoLossTemporalLayers)) { + DISABLED_ON_ANDROID(ProcessNoLossTemporalLayersVP8)) { config_.networking_config.packet_loss_probability = 0; // Bitrate and frame rate profile. RateProfile rate_profile; @@ -744,7 +898,8 @@ rate_profile.num_frames = kNbrFramesLong; // Codec/network settings. CodecConfigPars process_settings; - SetCodecParameters(&process_settings, 0.0f, -1, 3, false, true, true, false); + SetCodecParameters(&process_settings, kVideoCodecVP8, 0.0f, -1, 3, false, + true, true, false); // Metrics for expected quality. QualityMetrics quality_metrics; SetQualityMetrics(&quality_metrics, 32.5, 30.0, 0.85, 0.80); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test_framework/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test_framework/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test_framework/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test_framework/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test_framework/test.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test_framework/test.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test_framework/test.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test_framework/test.h 2015-02-03 14:33:36.000000000 +0000 @@ -41,7 +41,7 @@ virtual void Teardown(); double ActualBitRate(int nFrames); virtual bool PacketLoss(double lossRate, int /*thrown*/); - static double RandUniform() { return (std::rand() + 1.0)/(RAND_MAX + 1.0); } + static double RandUniform() { return (rand() + 1.0)/(RAND_MAX + 1.0); } static void VideoEncodedBufferToEncodedImage( webrtc::VideoFrame& videoBuffer, webrtc::EncodedImage &image); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test_framework/unit_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test_framework/unit_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test_framework/unit_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/test_framework/unit_test.cc 2015-02-03 14:33:36.000000000 +0000 @@ -393,7 +393,7 @@ _inst.maxFramerate = 30; // Bad bitrate. - _inst.startBitrate = -1; + _inst.startBitrate = static_cast(-1); EXPECT_TRUE(_encoder->InitEncode(&_inst, 1, 1440) == WEBRTC_VIDEO_CODEC_ERR_PARAMETER); _inst.maxBitrate = _inst.startBitrate - 1; @@ -565,7 +565,7 @@ frameLength = WaitForDecodedFrame(); } unsigned int length = CalcBufferSize(kI420, width, height); - scoped_array decoded_buffer(new uint8_t[length]); + scoped_ptr decoded_buffer(new uint8_t[length]); ExtractBuffer(_decodedVideoBuffer, _lengthSourceFrame, decoded_buffer.get()); EXPECT_TRUE(CheckIfBitExact(decoded_buffer.get(), frameLength, _refDecFrame, @@ -645,7 +645,7 @@ // check that decoded frame matches with reference unsigned int length = CalcBufferSize(kI420, width, height); - scoped_array decoded_buffer(new uint8_t[length]); + scoped_ptr decoded_buffer(new uint8_t[length]); ExtractBuffer(_decodedVideoBuffer, length, decoded_buffer.get()); EXPECT_TRUE(CheckIfBitExact(decoded_buffer.get(), length, _refDecFrame, _lengthSourceFrame) == true); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/tools/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/tools/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/tools/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/tools/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/tools/video_codecs_tools.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/tools/video_codecs_tools.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/tools/video_codecs_tools.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/tools/video_codecs_tools.gypi 2015-02-03 14:33:36.000000000 +0000 @@ -17,6 +17,7 @@ 'video_codecs_test_framework', 'webrtc_video_coding', '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers_default', '<(webrtc_root)/test/metrics.gyp:metrics', '<(webrtc_vp8_dir)/vp8.gyp:webrtc_vp8', ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../../../android-webrtc.mk - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_vp8 -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := \ - reference_picture_selection.cc \ - vp8_impl.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) -# TODO(leozwang) Enable WEBRTC_LIBVPX_VERSION after libvpx is updateed -# to a new version and also add temporal_layers.cc - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../../../../../.. \ - $(LOCAL_PATH)/../../../../../../common_video/interface \ - $(LOCAL_PATH)/../../../../../../common_video/vplib/main/interface \ - $(LOCAL_PATH)/../../../../../../modules/interface \ - $(LOCAL_PATH)/../../../../../../system_wrappers/interface \ - external/libvpx - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -22,6 +22,8 @@ enum { kMaxWaitEncTimeMs = 100 }; enum { kMaxWaitDecTimeMs = 25 }; +static const uint32_t kTestTimestamp = 123; +static const int64_t kTestNtpTimeMs = 456; // TODO(mikhal): Replace these with mocks. class Vp8UnitTestEncodeCompleteCallback : public webrtc::EncodedImageCallback { @@ -128,6 +130,7 @@ input_frame_.CreateEmptyFrame(codec_inst_.width, codec_inst_.height, stride_y, stride_uv, stride_uv); + input_frame_.set_timestamp(kTestTimestamp); // Using ConvertToI420 to add stride to the image. EXPECT_EQ(0, ConvertToI420(kI420, source_buffer_.get(), 0, 0, codec_inst_.width, codec_inst_.height, @@ -178,7 +181,7 @@ scoped_ptr encode_complete_callback_; scoped_ptr decode_complete_callback_; - scoped_array source_buffer_; + scoped_ptr source_buffer_; FILE* source_file_; I420VideoFrame input_frame_; scoped_ptr encoder_; @@ -189,7 +192,13 @@ VideoCodec codec_inst_; }; +// Disabled on MemorySanitizer as it's breaking on generic libvpx. +// https://code.google.com/p/webrtc/issues/detail?id=3904 +#if defined(MEMORY_SANITIZER) +TEST_F(TestVp8Impl, DISABLED_BaseUnitTest) { +#else TEST_F(TestVp8Impl, DISABLED_ON_ANDROID(BaseUnitTest)) { +#endif // TODO(mikhal): Remove dependency. Move all test code here. EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Release()); UnitTest unittest; @@ -235,10 +244,13 @@ VideoFrameToEncodedImage(encoded_video_frame_, encodedImage); // First frame should be a key frame. encodedImage._frameType = kKeyFrame; + encodedImage.ntp_time_ms_ = kTestNtpTimeMs; EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Decode(encodedImage, false, NULL)); EXPECT_GT(WaitForDecodedFrame(), 0); // Compute PSNR on all planes (faster than SSIM). EXPECT_GT(I420PSNR(&input_frame_, &decoded_video_frame_), 36); + EXPECT_EQ(kTestTimestamp, decoded_video_frame_.timestamp()); + EXPECT_EQ(kTestNtpTimeMs, decoded_video_frame_.ntp_time_ms()); } TEST_F(TestVp8Impl, DISABLED_ON_ANDROID(DecodeWithACompleteKeyFrame)) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8_factory.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8_factory.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8_factory.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8_factory.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#include "webrtc/modules/video_coding/codecs/vp8/vp8_impl.h" + +namespace webrtc { + +VP8Encoder* VP8Encoder::Create() { + return new VP8EncoderImpl(); +} + +VP8Decoder* VP8Decoder::Create() { + return new VP8DecoderImpl(); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8.gyp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8.gyp 2015-02-03 14:33:36.000000000 +0000 @@ -37,6 +37,7 @@ 'reference_picture_selection.cc', 'include/vp8.h', 'include/vp8_common_types.h', + 'vp8_factory.cc', 'vp8_impl.cc', 'default_temporal_layers.cc', 'default_temporal_layers.h', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc 2015-02-03 14:33:36.000000000 +0000 @@ -35,10 +35,6 @@ namespace webrtc { -VP8Encoder* VP8Encoder::Create() { - return new VP8EncoderImpl(); -} - VP8EncoderImpl::VP8EncoderImpl() : encoded_image_(), encoded_complete_callback_(NULL), @@ -109,6 +105,7 @@ temporal_layers_->ConfigureBitrates(new_bitrate_kbit, codec_.maxBitrate, new_framerate, config_); codec_.maxFramerate = new_framerate; + quality_scaler_.ReportFramerate(new_framerate); // update encoder context if (vpx_codec_enc_config_set(encoder_, config_)) { @@ -220,7 +217,10 @@ } config_->g_lag_in_frames = 0; // 0- no frame lagging - if (codec_.width * codec_.height > 1280 * 960 && number_of_cores >= 6) { + if (codec_.width * codec_.height >= 1920 * 1080 && number_of_cores > 8) { + config_->g_threads = 8; // 8 threads for 1080p on high perf machines. + } else if (codec_.width * codec_.height > 1280 * 960 && + number_of_cores >= 6) { config_->g_threads = 3; // 3 threads for 1080p. } else if (codec_.width * codec_.height > 640 * 480 && number_of_cores >= 3) { config_->g_threads = 2; // 2 threads for qHD/HD. @@ -233,8 +233,8 @@ 30 : 0; config_->rc_end_usage = VPX_CBR; config_->g_pass = VPX_RC_ONE_PASS; - config_->rc_resize_allowed = inst->codecSpecific.VP8.automaticResizeOn ? - 1 : 0; + // Handle resizing outside of libvpx. + config_->rc_resize_allowed = 0; config_->rc_min_quantizer = 2; config_->rc_max_quantizer = inst->qpMax; config_->rc_undershoot_pct = 100; @@ -275,6 +275,8 @@ cpu_speed_ = -12; #endif rps_->Init(); + quality_scaler_.Init(codec_.qpMax); + quality_scaler_.ReportFramerate(codec_.maxFramerate); return InitAndSetControlSettings(inst); } @@ -299,6 +301,7 @@ vpx_codec_control(encoder_, VP8E_SET_MAX_INTRA_BITRATE_PCT, rc_max_intra_target_); inited_ = true; + return WEBRTC_VIDEO_CODEC_OK; } @@ -318,15 +321,15 @@ return (targetPct < minIntraTh) ? minIntraTh: targetPct; } -int VP8EncoderImpl::Encode(const I420VideoFrame& input_image, +int VP8EncoderImpl::Encode(const I420VideoFrame& input_frame, const CodecSpecificInfo* codec_specific_info, const std::vector* frame_types) { - TRACE_EVENT1("webrtc", "VP8::Encode", "timestamp", input_image.timestamp()); + TRACE_EVENT1("webrtc", "VP8::Encode", "timestamp", input_frame.timestamp()); if (!inited_) { return WEBRTC_VIDEO_CODEC_UNINITIALIZED; } - if (input_image.IsZeroSize()) { + if (input_frame.IsZeroSize()) { return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; } if (encoded_complete_callback_ == NULL) { @@ -339,10 +342,16 @@ frame_type = (*frame_types)[0]; } + const I420VideoFrame& frame = + config_->rc_dropframe_thresh > 0 && + codec_.codecSpecific.VP8.automaticResizeOn + ? quality_scaler_.GetScaledFrame(input_frame) + : input_frame; + // Check for change in frame size. - if (input_image.width() != codec_.width || - input_image.height() != codec_.height) { - int ret = UpdateCodecFrameSize(input_image); + if (frame.width() != codec_.width || + frame.height() != codec_.height) { + int ret = UpdateCodecFrameSize(frame); if (ret < 0) { return ret; } @@ -350,18 +359,17 @@ frame_type = kKeyFrame; #endif } - // Image in vpx_image_t format. - // Input image is const. VP8's raw image is not defined as const. - raw_->planes[VPX_PLANE_Y] = const_cast(input_image.buffer(kYPlane)); - raw_->planes[VPX_PLANE_U] = const_cast(input_image.buffer(kUPlane)); - raw_->planes[VPX_PLANE_V] = const_cast(input_image.buffer(kVPlane)); + // Input frame is const. VP8's raw frame is not defined as const. + raw_->planes[VPX_PLANE_Y] = const_cast(frame.buffer(kYPlane)); + raw_->planes[VPX_PLANE_U] = const_cast(frame.buffer(kUPlane)); + raw_->planes[VPX_PLANE_V] = const_cast(frame.buffer(kVPlane)); // TODO(mikhal): Stride should be set in initialization. - raw_->stride[VPX_PLANE_Y] = input_image.stride(kYPlane); - raw_->stride[VPX_PLANE_U] = input_image.stride(kUPlane); - raw_->stride[VPX_PLANE_V] = input_image.stride(kVPlane); + raw_->stride[VPX_PLANE_Y] = frame.stride(kYPlane); + raw_->stride[VPX_PLANE_U] = frame.stride(kUPlane); + raw_->stride[VPX_PLANE_V] = frame.stride(kVPlane); - int flags = temporal_layers_->EncodeFlags(input_image.timestamp()); + int flags = temporal_layers_->EncodeFlags(frame.timestamp()); bool send_keyframe = (frame_type == kKeyFrame); if (send_keyframe) { @@ -377,11 +385,11 @@ codec_specific_info->codecSpecific.VP8.pictureIdRPSI); } if (codec_specific_info->codecSpecific.VP8.hasReceivedSLI) { - sendRefresh = rps_->ReceivedSLI(input_image.timestamp()); + sendRefresh = rps_->ReceivedSLI(frame.timestamp()); } } flags = rps_->EncodeFlags(picture_id_, sendRefresh, - input_image.timestamp()); + frame.timestamp()); } // TODO(holmer): Ideally the duration should be the timestamp diff of this @@ -397,7 +405,7 @@ } timestamp_ += duration; - return GetEncodedPartitions(input_image); + return GetEncodedPartitions(frame); } int VP8EncoderImpl::UpdateCodecFrameSize(const I420VideoFrame& input_image) { @@ -491,10 +499,15 @@ TRACE_COUNTER1("webrtc", "EncodedFrameSize", encoded_image_._length); encoded_image_._timeStamp = input_image.timestamp(); encoded_image_.capture_time_ms_ = input_image.render_time_ms(); - encoded_image_._encodedHeight = raw_->h; - encoded_image_._encodedWidth = raw_->w; + encoded_image_._encodedHeight = codec_.height; + encoded_image_._encodedWidth = codec_.width; encoded_complete_callback_->Encoded(encoded_image_, &codec_specific, &frag_info); + int qp; + vpx_codec_control(encoder_, VP8E_GET_LAST_QUANTIZER_64, &qp); + quality_scaler_.ReportEncodedFrame(qp); + } else { + quality_scaler_.ReportDroppedFrame(); } return WEBRTC_VIDEO_CODEC_OK; } @@ -510,10 +523,6 @@ return WEBRTC_VIDEO_CODEC_OK; } -VP8Decoder* VP8Decoder::Create() { - return new VP8DecoderImpl(); -} - VP8DecoderImpl::VP8DecoderImpl() : decode_complete_callback_(NULL), inited_(false), @@ -732,7 +741,7 @@ } img = vpx_codec_get_frame(decoder_, &iter); - ret = ReturnFrame(img, input_image._timeStamp); + ret = ReturnFrame(img, input_image._timeStamp, input_image.ntp_time_ms_); if (ret != 0) { // Reset to avoid requesting key frames too often. if (ret < 0 && propagation_cnt_ > 0) @@ -812,7 +821,9 @@ return WEBRTC_VIDEO_CODEC_OK; } -int VP8DecoderImpl::ReturnFrame(const vpx_image_t* img, uint32_t timestamp) { +int VP8DecoderImpl::ReturnFrame(const vpx_image_t* img, + uint32_t timestamp, + int64_t ntp_time_ms) { if (img == NULL) { // Decoder OK and NULL image => No show frame return WEBRTC_VIDEO_CODEC_NO_OUTPUT; @@ -830,6 +841,7 @@ img->stride[VPX_PLANE_U], img->stride[VPX_PLANE_V]); decoded_image_.set_timestamp(timestamp); + decoded_image_.set_ntp_time_ms(ntp_time_ms); int ret = decode_complete_callback_->Decoded(decoded_image_); if (ret != 0) return ret; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8_impl.h 2015-02-03 14:33:36.000000000 +0000 @@ -14,6 +14,7 @@ #define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_IMPL_H_ #include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h" +#include "webrtc/modules/video_coding/utility/quality_scaler.h" // VPX forward declaration typedef struct vpx_codec_ctx vpx_codec_ctx_t; @@ -34,73 +35,20 @@ virtual ~VP8EncoderImpl(); - // Free encoder memory. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. virtual int Release(); - // Initialize the encoder with the information from the codecSettings - // - // Input: - // - codec_settings : Codec settings - // - number_of_cores : Number of cores available for the encoder - // - max_payload_size : The maximum size each payload is allowed - // to have. Usually MTU - overhead. - // - // Return value : Set bit rate if OK - // <0 - Errors: - // WEBRTC_VIDEO_CODEC_ERR_PARAMETER - // WEBRTC_VIDEO_CODEC_ERR_SIZE - // WEBRTC_VIDEO_CODEC_LEVEL_EXCEEDED - // WEBRTC_VIDEO_CODEC_MEMORY - // WEBRTC_VIDEO_CODEC_ERROR virtual int InitEncode(const VideoCodec* codec_settings, int number_of_cores, uint32_t max_payload_size); - // Encode an I420 image (as a part of a video stream). The encoded image - // will be returned to the user through the encode complete callback. - // - // Input: - // - input_image : Image to be encoded - // - frame_types : Frame type to be generated by the encoder. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK - // <0 - Errors: - // WEBRTC_VIDEO_CODEC_ERR_PARAMETER - // WEBRTC_VIDEO_CODEC_MEMORY - // WEBRTC_VIDEO_CODEC_ERROR - // WEBRTC_VIDEO_CODEC_TIMEOUT - virtual int Encode(const I420VideoFrame& input_image, const CodecSpecificInfo* codec_specific_info, const std::vector* frame_types); - // Register an encode complete callback object. - // - // Input: - // - callback : Callback object which handles encoded images. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. virtual int RegisterEncodeCompleteCallback(EncodedImageCallback* callback); - // Inform the encoder of the new packet loss rate and the round-trip time of - // the network. - // - // - packet_loss : Fraction lost - // (loss rate in percent = 100 * packetLoss / 255) - // - rtt : Round-trip time in milliseconds - // Return value : WEBRTC_VIDEO_CODEC_OK if OK - // <0 - Errors: WEBRTC_VIDEO_CODEC_ERROR - // virtual int SetChannelParameters(uint32_t packet_loss, int rtt); - // Inform the encoder about the new target bit rate. - // - // - new_bitrate_kbit : New target bit rate - // - frame_rate : The target frame rate - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. virtual int SetRates(uint32_t new_bitrate_kbit, uint32_t frame_rate); private: @@ -139,6 +87,7 @@ vpx_codec_ctx_t* encoder_; vpx_codec_enc_cfg_t* config_; vpx_image_t* raw_; + QualityScaler quality_scaler_; }; // end of VP8Encoder class @@ -148,61 +97,20 @@ virtual ~VP8DecoderImpl(); - // Initialize the decoder. - // - // Return value : WEBRTC_VIDEO_CODEC_OK. - // <0 - Errors: - // WEBRTC_VIDEO_CODEC_ERROR virtual int InitDecode(const VideoCodec* inst, int number_of_cores); - // Decode encoded image (as a part of a video stream). The decoded image - // will be returned to the user through the decode complete callback. - // - // Input: - // - input_image : Encoded image to be decoded - // - missing_frames : True if one or more frames have been lost - // since the previous decode call. - // - fragmentation : Specifies the start and length of each VP8 - // partition. - // - codec_specific_info : pointer to specific codec data - // - render_time_ms : Render time in Ms - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK - // <0 - Errors: - // WEBRTC_VIDEO_CODEC_ERROR - // WEBRTC_VIDEO_CODEC_ERR_PARAMETER virtual int Decode(const EncodedImage& input_image, bool missing_frames, const RTPFragmentationHeader* fragmentation, const CodecSpecificInfo* codec_specific_info, int64_t /*render_time_ms*/); - // Register a decode complete callback object. - // - // Input: - // - callback : Callback object which handles decoded images. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. virtual int RegisterDecodeCompleteCallback(DecodedImageCallback* callback); - // Free decoder memory. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK - // <0 - Errors: - // WEBRTC_VIDEO_CODEC_ERROR virtual int Release(); - // Reset decoder state and prepare for a new call. - // - // Return value : WEBRTC_VIDEO_CODEC_OK. - // <0 - Errors: - // WEBRTC_VIDEO_CODEC_UNINITIALIZED - // WEBRTC_VIDEO_CODEC_ERROR virtual int Reset(); - // Create a copy of the codec and its internal state. - // - // Return value : A copy of the instance if OK, NULL otherwise. virtual VideoDecoder* Copy(); private: @@ -214,7 +122,9 @@ int DecodePartitions(const EncodedImage& input_image, const RTPFragmentationHeader* fragmentation); - int ReturnFrame(const vpx_image_t* img, uint32_t timeStamp); + int ReturnFrame(const vpx_image_t* img, + uint32_t timeStamp, + int64_t ntp_time_ms); I420VideoFrame decoded_image_; DecodedImageCallback* decode_complete_callback_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8_sequence_coder.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8_sequence_coder.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8_sequence_coder.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8_sequence_coder.cc 2015-02-03 14:33:36.000000000 +0000 @@ -142,7 +142,7 @@ EXPECT_EQ(0, decoder->InitDecode(&inst, 1)); webrtc::I420VideoFrame input_frame; unsigned int length = webrtc::CalcBufferSize(webrtc::kI420, width, height); - webrtc::scoped_array frame_buffer(new uint8_t[length]); + webrtc::scoped_ptr frame_buffer(new uint8_t[length]); int half_width = (width + 1) / 2; // Set and register callbacks. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/include/vp9.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/include/vp9.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/include/vp9.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/include/vp9.h 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_H_ +#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_H_ + +#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h" + +namespace webrtc { + +class VP9Encoder : public VideoEncoder { + public: + static VP9Encoder* Create(); + + virtual ~VP9Encoder() {} +}; + + +class VP9Decoder : public VideoDecoder { + public: + static VP9Decoder* Create(); + + virtual ~VP9Decoder() {} +}; +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9.gyp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9.gyp 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,36 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +{ + 'includes': [ + '../../../../build/common.gypi', + ], + 'targets': [ + { + 'target_name': 'webrtc_vp9', + 'type': 'static_library', + 'dependencies': [ + '<(webrtc_root)/common_video/common_video.gyp:common_video', + '<(webrtc_root)/modules/video_coding/utility/video_coding_utility.gyp:video_coding_utility', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + ], + 'conditions': [ + ['build_libvpx==1', { + 'dependencies': [ + '<(DEPTH)/third_party/libvpx/libvpx.gyp:libvpx', + ], + }], + ], + 'sources': [ + 'include/vp9.h', + 'vp9_impl.cc', + 'vp9_impl.h', + ], + }, + ], +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#include "webrtc/modules/video_coding/codecs/vp9/vp9_impl.h" + +#include +#include +#include +#include + +#include "vpx/vpx_encoder.h" +#include "vpx/vpx_decoder.h" +#include "vpx/vp8cx.h" +#include "vpx/vp8dx.h" + +#include "webrtc/common.h" +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/system_wrappers/interface/tick_util.h" +#include "webrtc/system_wrappers/interface/trace_event.h" + +namespace webrtc { + +VP9Encoder* VP9Encoder::Create() { + return new VP9EncoderImpl(); +} + +VP9EncoderImpl::VP9EncoderImpl() + : encoded_image_(), + encoded_complete_callback_(NULL), + inited_(false), + timestamp_(0), + picture_id_(0), + cpu_speed_(3), + rc_max_intra_target_(0), + encoder_(NULL), + config_(NULL), + raw_(NULL) { + memset(&codec_, 0, sizeof(codec_)); + uint32_t seed = static_cast(TickTime::MillisecondTimestamp()); + srand(seed); +} + +VP9EncoderImpl::~VP9EncoderImpl() { + Release(); +} + +int VP9EncoderImpl::Release() { + if (encoded_image_._buffer != NULL) { + delete [] encoded_image_._buffer; + encoded_image_._buffer = NULL; + } + if (encoder_ != NULL) { + if (vpx_codec_destroy(encoder_)) { + return WEBRTC_VIDEO_CODEC_MEMORY; + } + delete encoder_; + encoder_ = NULL; + } + if (config_ != NULL) { + delete config_; + config_ = NULL; + } + if (raw_ != NULL) { + vpx_img_free(raw_); + raw_ = NULL; + } + inited_ = false; + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9EncoderImpl::SetRates(uint32_t new_bitrate_kbit, + uint32_t new_framerate) { + if (!inited_) { + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + if (encoder_->err) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + if (new_framerate < 1) { + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + // Update bit rate + if (codec_.maxBitrate > 0 && new_bitrate_kbit > codec_.maxBitrate) { + new_bitrate_kbit = codec_.maxBitrate; + } + config_->rc_target_bitrate = new_bitrate_kbit; + codec_.maxFramerate = new_framerate; + // Update encoder context + if (vpx_codec_enc_config_set(encoder_, config_)) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9EncoderImpl::InitEncode(const VideoCodec* inst, + int number_of_cores, + uint32_t /*max_payload_size*/) { + if (inst == NULL) { + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + if (inst->maxFramerate < 1) { + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + // Allow zero to represent an unspecified maxBitRate + if (inst->maxBitrate > 0 && inst->startBitrate > inst->maxBitrate) { + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + if (inst->width < 1 || inst->height < 1) { + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + if (number_of_cores < 1) { + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + int retVal = Release(); + if (retVal < 0) { + return retVal; + } + if (encoder_ == NULL) { + encoder_ = new vpx_codec_ctx_t; + } + if (config_ == NULL) { + config_ = new vpx_codec_enc_cfg_t; + } + timestamp_ = 0; + if (&codec_ != inst) { + codec_ = *inst; + } + // Random start 16 bits is enough. + picture_id_ = static_cast(rand()) & 0x7FFF; + // Allocate memory for encoded image + if (encoded_image_._buffer != NULL) { + delete [] encoded_image_._buffer; + } + encoded_image_._size = CalcBufferSize(kI420, codec_.width, codec_.height); + encoded_image_._buffer = new uint8_t[encoded_image_._size]; + encoded_image_._completeFrame = true; + // Creating a wrapper to the image - setting image data to NULL. Actual + // pointer will be set in encode. Setting align to 1, as it is meaningless + // (actual memory is not allocated). + raw_ = vpx_img_wrap(NULL, VPX_IMG_FMT_I420, codec_.width, codec_.height, + 1, NULL); + // Populate encoder configuration with default values. + if (vpx_codec_enc_config_default(vpx_codec_vp9_cx(), config_, 0)) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + config_->g_w = codec_.width; + config_->g_h = codec_.height; + config_->rc_target_bitrate = inst->startBitrate; // in kbit/s + config_->g_error_resilient = 1; + // Setting the time base of the codec. + config_->g_timebase.num = 1; + config_->g_timebase.den = 90000; + config_->g_lag_in_frames = 0; // 0- no frame lagging + config_->g_threads = 1; + // Rate control settings. + config_->rc_dropframe_thresh = inst->codecSpecific.VP9.frameDroppingOn ? + 30 : 0; + config_->rc_end_usage = VPX_CBR; + config_->g_pass = VPX_RC_ONE_PASS; + config_->rc_min_quantizer = 2; + config_->rc_max_quantizer = 56; + config_->rc_undershoot_pct = 50; + config_->rc_overshoot_pct = 50; + config_->rc_buf_initial_sz = 500; + config_->rc_buf_optimal_sz = 600; + config_->rc_buf_sz = 1000; + // Set the maximum target size of any key-frame. + rc_max_intra_target_ = MaxIntraTarget(config_->rc_buf_optimal_sz); + if (inst->codecSpecific.VP9.keyFrameInterval > 0) { + config_->kf_mode = VPX_KF_AUTO; + config_->kf_max_dist = inst->codecSpecific.VP9.keyFrameInterval; + } else { + config_->kf_mode = VPX_KF_DISABLED; + } + return InitAndSetControlSettings(inst); +} + +int VP9EncoderImpl::InitAndSetControlSettings(const VideoCodec* inst) { + if (vpx_codec_enc_init(encoder_, vpx_codec_vp9_cx(), config_, 0)) { + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + // Only positive speeds, currently: 0 - 7. + // O means slowest/best quality, 7 means fastest/lower quality. + cpu_speed_ = 6; + // Note: some of these codec controls still use "VP8" in the control name. + // TODO(marpan): Update this in the next/future libvpx version. + vpx_codec_control(encoder_, VP8E_SET_CPUUSED, cpu_speed_); + vpx_codec_control(encoder_, VP8E_SET_MAX_INTRA_BITRATE_PCT, + rc_max_intra_target_); + vpx_codec_control(encoder_, VP9E_SET_AQ_MODE, + inst->codecSpecific.VP9.adaptiveQpMode ? 3 : 0); + // TODO(marpan): Enable in future libvpx roll: waiting for SSE2 optimization. +// #if !defined(WEBRTC_ARCH_ARM) + // vpx_codec_control(encoder_, VP9E_SET_NOISE_SENSITIVITY, + // inst->codecSpecific.VP9.denoisingOn ? 1 : 0); +// #endif + inited_ = true; + return WEBRTC_VIDEO_CODEC_OK; +} + +uint32_t VP9EncoderImpl::MaxIntraTarget(uint32_t optimal_buffer_size) { + // Set max to the optimal buffer level (normalized by target BR), + // and scaled by a scale_par. + // Max target size = scale_par * optimal_buffer_size * targetBR[Kbps]. + // This value is presented in percentage of perFrameBw: + // perFrameBw = targetBR[Kbps] * 1000 / framerate. + // The target in % is as follows: + float scale_par = 0.5; + uint32_t target_pct = + optimal_buffer_size * scale_par * codec_.maxFramerate / 10; + // Don't go below 3 times the per frame bandwidth. + const uint32_t min_intra_size = 300; + return (target_pct < min_intra_size) ? min_intra_size: target_pct; +} + +int VP9EncoderImpl::Encode(const I420VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector* frame_types) { + if (!inited_) { + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + if (input_image.IsZeroSize()) { + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + if (encoded_complete_callback_ == NULL) { + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + VideoFrameType frame_type = kDeltaFrame; + // We only support one stream at the moment. + if (frame_types && frame_types->size() > 0) { + frame_type = (*frame_types)[0]; + } + // Image in vpx_image_t format. + // Input image is const. VPX's raw image is not defined as const. + raw_->planes[VPX_PLANE_Y] = const_cast(input_image.buffer(kYPlane)); + raw_->planes[VPX_PLANE_U] = const_cast(input_image.buffer(kUPlane)); + raw_->planes[VPX_PLANE_V] = const_cast(input_image.buffer(kVPlane)); + raw_->stride[VPX_PLANE_Y] = input_image.stride(kYPlane); + raw_->stride[VPX_PLANE_U] = input_image.stride(kUPlane); + raw_->stride[VPX_PLANE_V] = input_image.stride(kVPlane); + + int flags = 0; + bool send_keyframe = (frame_type == kKeyFrame); + if (send_keyframe) { + // Key frame request from caller. + flags = VPX_EFLAG_FORCE_KF; + } + assert(codec_.maxFramerate > 0); + uint32_t duration = 90000 / codec_.maxFramerate; + if (vpx_codec_encode(encoder_, raw_, timestamp_, duration, flags, + VPX_DL_REALTIME)) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + timestamp_ += duration; + return GetEncodedPartitions(input_image); +} + +void VP9EncoderImpl::PopulateCodecSpecific(CodecSpecificInfo* codec_specific, + const vpx_codec_cx_pkt& pkt, + uint32_t timestamp) { + assert(codec_specific != NULL); + codec_specific->codecType = kVideoCodecVP9; + CodecSpecificInfoVP9 *vp9_info = &(codec_specific->codecSpecific.VP9); + vp9_info->pictureId = picture_id_; + vp9_info->keyIdx = kNoKeyIdx; + vp9_info->nonReference = (pkt.data.frame.flags & VPX_FRAME_IS_DROPPABLE) != 0; + // TODO(marpan): Temporal layers are supported in the current VP9 version, + // but for now use 1 temporal layer encoding. Will update this when temporal + // layer support for VP9 is added in webrtc. + vp9_info->temporalIdx = kNoTemporalIdx; + vp9_info->layerSync = false; + vp9_info->tl0PicIdx = kNoTl0PicIdx; + picture_id_ = (picture_id_ + 1) & 0x7FFF; +} + +int VP9EncoderImpl::GetEncodedPartitions(const I420VideoFrame& input_image) { + vpx_codec_iter_t iter = NULL; + encoded_image_._length = 0; + encoded_image_._frameType = kDeltaFrame; + RTPFragmentationHeader frag_info; + // Note: no data partitioning in VP9, so 1 partition only. We keep this + // fragmentation data for now, until VP9 packetizer is implemented. + frag_info.VerifyAndAllocateFragmentationHeader(1); + int part_idx = 0; + CodecSpecificInfo codec_specific; + const vpx_codec_cx_pkt_t *pkt = NULL; + while ((pkt = vpx_codec_get_cx_data(encoder_, &iter)) != NULL) { + switch (pkt->kind) { + case VPX_CODEC_CX_FRAME_PKT: { + memcpy(&encoded_image_._buffer[encoded_image_._length], + pkt->data.frame.buf, + pkt->data.frame.sz); + frag_info.fragmentationOffset[part_idx] = encoded_image_._length; + frag_info.fragmentationLength[part_idx] = + static_cast(pkt->data.frame.sz); + frag_info.fragmentationPlType[part_idx] = 0; + frag_info.fragmentationTimeDiff[part_idx] = 0; + encoded_image_._length += static_cast(pkt->data.frame.sz); + assert(encoded_image_._length <= encoded_image_._size); + break; + } + default: { + break; + } + } + // End of frame. + if ((pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0) { + // Check if encoded frame is a key frame. + if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) { + encoded_image_._frameType = kKeyFrame; + } + PopulateCodecSpecific(&codec_specific, *pkt, input_image.timestamp()); + break; + } + } + if (encoded_image_._length > 0) { + TRACE_COUNTER1("webrtc", "EncodedFrameSize", encoded_image_._length); + encoded_image_._timeStamp = input_image.timestamp(); + encoded_image_.capture_time_ms_ = input_image.render_time_ms(); + encoded_image_._encodedHeight = raw_->d_h; + encoded_image_._encodedWidth = raw_->d_w; + encoded_complete_callback_->Encoded(encoded_image_, &codec_specific, + &frag_info); + } + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9EncoderImpl::SetChannelParameters(uint32_t packet_loss, int rtt) { + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9EncoderImpl::RegisterEncodeCompleteCallback( + EncodedImageCallback* callback) { + encoded_complete_callback_ = callback; + return WEBRTC_VIDEO_CODEC_OK; +} + +VP9Decoder* VP9Decoder::Create() { + return new VP9DecoderImpl(); +} + +VP9DecoderImpl::VP9DecoderImpl() + : decode_complete_callback_(NULL), + inited_(false), + decoder_(NULL), + key_frame_required_(true) { + memset(&codec_, 0, sizeof(codec_)); +} + +VP9DecoderImpl::~VP9DecoderImpl() { + inited_ = true; // in order to do the actual release + Release(); +} + +int VP9DecoderImpl::Reset() { + if (!inited_) { + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + InitDecode(&codec_, 1); + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9DecoderImpl::InitDecode(const VideoCodec* inst, int number_of_cores) { + if (inst == NULL) { + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + int ret_val = Release(); + if (ret_val < 0) { + return ret_val; + } + if (decoder_ == NULL) { + decoder_ = new vpx_dec_ctx_t; + } + vpx_codec_dec_cfg_t cfg; + // Setting number of threads to a constant value (1) + cfg.threads = 1; + cfg.h = cfg.w = 0; // set after decode + vpx_codec_flags_t flags = 0; + if (vpx_codec_dec_init(decoder_, vpx_codec_vp9_dx(), &cfg, flags)) { + return WEBRTC_VIDEO_CODEC_MEMORY; + } + if (&codec_ != inst) { + // Save VideoCodec instance for later; mainly for duplicating the decoder. + codec_ = *inst; + } + inited_ = true; + // Always start with a complete key frame. + key_frame_required_ = true; + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9DecoderImpl::Decode(const EncodedImage& input_image, + bool missing_frames, + const RTPFragmentationHeader* fragmentation, + const CodecSpecificInfo* codec_specific_info, + int64_t /*render_time_ms*/) { + if (!inited_) { + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + if (decode_complete_callback_ == NULL) { + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + // Always start with a complete key frame. + if (key_frame_required_) { + if (input_image._frameType != kKeyFrame) + return WEBRTC_VIDEO_CODEC_ERROR; + // We have a key frame - is it complete? + if (input_image._completeFrame) { + key_frame_required_ = false; + } else { + return WEBRTC_VIDEO_CODEC_ERROR; + } + } + vpx_codec_iter_t iter = NULL; + vpx_image_t* img; + uint8_t* buffer = input_image._buffer; + if (input_image._length == 0) { + buffer = NULL; // Triggers full frame concealment. + } + if (vpx_codec_decode(decoder_, + buffer, + input_image._length, + 0, + VPX_DL_REALTIME)) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + img = vpx_codec_get_frame(decoder_, &iter); + int ret = ReturnFrame(img, input_image._timeStamp); + if (ret != 0) { + return ret; + } + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9DecoderImpl::ReturnFrame(const vpx_image_t* img, uint32_t timestamp) { + if (img == NULL) { + // Decoder OK and NULL image => No show frame. + return WEBRTC_VIDEO_CODEC_NO_OUTPUT; + } + int half_height = (img->d_h + 1) / 2; + int size_y = img->stride[VPX_PLANE_Y] * img->d_h; + int size_u = img->stride[VPX_PLANE_U] * half_height; + int size_v = img->stride[VPX_PLANE_V] * half_height; + decoded_image_.CreateFrame(size_y, img->planes[VPX_PLANE_Y], + size_u, img->planes[VPX_PLANE_U], + size_v, img->planes[VPX_PLANE_V], + img->d_w, img->d_h, + img->stride[VPX_PLANE_Y], + img->stride[VPX_PLANE_U], + img->stride[VPX_PLANE_V]); + decoded_image_.set_timestamp(timestamp); + int ret = decode_complete_callback_->Decoded(decoded_image_); + if (ret != 0) + return ret; + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9DecoderImpl::RegisterDecodeCompleteCallback( + DecodedImageCallback* callback) { + decode_complete_callback_ = callback; + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9DecoderImpl::Release() { + if (decoder_ != NULL) { + if (vpx_codec_destroy(decoder_)) { + return WEBRTC_VIDEO_CODEC_MEMORY; + } + delete decoder_; + decoder_ = NULL; + } + inited_ = false; + return WEBRTC_VIDEO_CODEC_OK; +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.h 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_IMPL_H_ +#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_IMPL_H_ + +#include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h" + +// VPX forward declaration +typedef struct vpx_codec_ctx vpx_codec_ctx_t; +typedef struct vpx_codec_ctx vpx_dec_ctx_t; +typedef struct vpx_codec_enc_cfg vpx_codec_enc_cfg_t; +typedef struct vpx_image vpx_image_t; +typedef struct vpx_ref_frame vpx_ref_frame_t; +struct vpx_codec_cx_pkt; + +namespace webrtc { + +class VP9EncoderImpl : public VP9Encoder { + public: + VP9EncoderImpl(); + + virtual ~VP9EncoderImpl(); + + virtual int Release() OVERRIDE; + + virtual int InitEncode(const VideoCodec* codec_settings, + int number_of_cores, + uint32_t max_payload_size) OVERRIDE; + + virtual int Encode(const I420VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector* frame_types) OVERRIDE; + + virtual int RegisterEncodeCompleteCallback(EncodedImageCallback* callback) + OVERRIDE; + + virtual int SetChannelParameters(uint32_t packet_loss, int rtt) OVERRIDE; + + virtual int SetRates(uint32_t new_bitrate_kbit, uint32_t frame_rate) OVERRIDE; + + private: + // Call encoder initialize function and set control settings. + int InitAndSetControlSettings(const VideoCodec* inst); + + void PopulateCodecSpecific(CodecSpecificInfo* codec_specific, + const vpx_codec_cx_pkt& pkt, + uint32_t timestamp); + + int GetEncodedPartitions(const I420VideoFrame& input_image); + + // Determine maximum target for Intra frames + // + // Input: + // - optimal_buffer_size : Optimal buffer size + // Return Value : Max target size for Intra frames represented as + // percentage of the per frame bandwidth + uint32_t MaxIntraTarget(uint32_t optimal_buffer_size); + + EncodedImage encoded_image_; + EncodedImageCallback* encoded_complete_callback_; + VideoCodec codec_; + bool inited_; + int64_t timestamp_; + uint16_t picture_id_; + int cpu_speed_; + uint32_t rc_max_intra_target_; + vpx_codec_ctx_t* encoder_; + vpx_codec_enc_cfg_t* config_; + vpx_image_t* raw_; +}; + + +class VP9DecoderImpl : public VP9Decoder { + public: + VP9DecoderImpl(); + + virtual ~VP9DecoderImpl(); + + virtual int InitDecode(const VideoCodec* inst, int number_of_cores) OVERRIDE; + + virtual int Decode(const EncodedImage& input_image, + bool missing_frames, + const RTPFragmentationHeader* fragmentation, + const CodecSpecificInfo* codec_specific_info, + int64_t /*render_time_ms*/) OVERRIDE; + + virtual int RegisterDecodeCompleteCallback(DecodedImageCallback* callback) + OVERRIDE; + + virtual int Release() OVERRIDE; + + virtual int Reset() OVERRIDE; + + private: + int ReturnFrame(const vpx_image_t* img, uint32_t timeStamp); + + I420VideoFrame decoded_image_; + DecodedImageCallback* decode_complete_callback_; + bool inited_; + vpx_dec_ctx_t* decoder_; + VideoCodec codec_; + bool key_frame_required_; +}; +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_IMPL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/interface/video_coding_defines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/interface/video_coding_defines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/interface/video_coding_defines.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/interface/video_coding_defines.h 2015-02-03 14:33:36.000000000 +0000 @@ -39,7 +39,11 @@ #define VCM_RED_PAYLOAD_TYPE 96 #define VCM_ULPFEC_PAYLOAD_TYPE 97 #define VCM_VP8_PAYLOAD_TYPE 100 +#define VCM_VP9_PAYLOAD_TYPE 101 #define VCM_I420_PAYLOAD_TYPE 124 +#define VCM_H264_PAYLOAD_TYPE 127 + +enum { kDefaultStartBitrateKbps = 300 }; enum VCMVideoProtection { kProtectionNack, // Both send-side and receive-side diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/interface/video_coding.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/interface/video_coding.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/interface/video_coding.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/interface/video_coding.h 2015-02-03 14:33:36.000000000 +0000 @@ -72,11 +72,9 @@ kReferenceSelection }; - static VideoCodingModule* Create(const int32_t id); + static VideoCodingModule* Create(); - static VideoCodingModule* Create(const int32_t id, - Clock* clock, - EventFactory* event_factory); + static VideoCodingModule* Create(Clock* clock, EventFactory* event_factory); static void Destroy(VideoCodingModule* module); @@ -240,7 +238,7 @@ // frame rate/dimensions need to be updated for video quality optimization // // Input: - // - videoQMSettings : The callback object to register. + // - videoQMSettings : The callback object to register. // // Return value : VCM_OK, on success. // < 0, on error diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,69 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_video_coding -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := \ - codec_database.cc \ - codec_timer.cc \ - content_metrics_processing.cc \ - decoding_state.cc \ - encoded_frame.cc \ - frame_buffer.cc \ - generic_decoder.cc \ - generic_encoder.cc \ - inter_frame_delay.cc \ - jitter_buffer.cc \ - jitter_buffer_common.cc \ - jitter_estimator.cc \ - media_opt_util.cc \ - media_optimization.cc \ - packet.cc \ - qm_select.cc \ - receiver.cc \ - rtt_filter.cc \ - session_info.cc \ - timestamp_extrapolator.cc \ - timestamp_map.cc \ - timing.cc \ - video_coding_impl.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../codecs/interface \ - $(LOCAL_PATH)/../../codecs/i420/main/interface \ - $(LOCAL_PATH)/../../codecs/vp8/main/interface \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/../../../../common_video/vplib/main/interface \ - $(LOCAL_PATH)/../../../../common_video/interface \ - $(LOCAL_PATH)/../../utility/include \ - $(LOCAL_PATH)/../../../../system_wrappers/interface - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/codec_database.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/codec_database.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/codec_database.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/codec_database.cc 2015-02-03 14:33:36.000000000 +0000 @@ -19,11 +19,58 @@ #ifdef VIDEOCODEC_VP8 #include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h" #endif +#ifdef VIDEOCODEC_VP9 +#include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h" +#endif #include "webrtc/modules/video_coding/main/source/internal_defines.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { +VideoCodecVP8 VideoEncoder::GetDefaultVp8Settings() { + VideoCodecVP8 vp8_settings; + memset(&vp8_settings, 0, sizeof(vp8_settings)); + + vp8_settings.resilience = kResilientStream; + vp8_settings.numberOfTemporalLayers = 1; + vp8_settings.denoisingOn = true; + vp8_settings.errorConcealmentOn = false; + vp8_settings.automaticResizeOn = false; + vp8_settings.frameDroppingOn = true; + vp8_settings.keyFrameInterval = 3000; + + return vp8_settings; +} + +VideoCodecVP9 VideoEncoder::GetDefaultVp9Settings() { + VideoCodecVP9 vp9_settings; + memset(&vp9_settings, 0, sizeof(vp9_settings)); + + vp9_settings.resilience = 1; + vp9_settings.numberOfTemporalLayers = 1; + vp9_settings.denoisingOn = false; + vp9_settings.frameDroppingOn = true; + vp9_settings.keyFrameInterval = 3000; + vp9_settings.adaptiveQpMode = true; + + return vp9_settings; +} + +VideoCodecH264 VideoEncoder::GetDefaultH264Settings() { + VideoCodecH264 h264_settings; + memset(&h264_settings, 0, sizeof(h264_settings)); + + h264_settings.profile = kProfileBase; + h264_settings.frameDroppingOn = true; + h264_settings.keyFrameInterval = 3000; + h264_settings.spsData = NULL; + h264_settings.spsLen = 0; + h264_settings.ppsData = NULL; + h264_settings.ppsLen = 0; + + return h264_settings; +} + VCMDecoderMapItem::VCMDecoderMapItem(VideoCodec* settings, int number_of_cores, bool require_key_frame) @@ -42,9 +89,8 @@ internal_render_timing(internal_render_timing) { } -VCMCodecDataBase::VCMCodecDataBase(int id) - : id_(id), - number_of_cores_(0), +VCMCodecDataBase::VCMCodecDataBase() + : number_of_cores_(0), max_payload_size_(kDefaultPayloadSize), periodic_key_frames_(false), pending_encoder_reset_(true), @@ -58,8 +104,7 @@ ptr_decoder_(NULL), current_dec_is_external_(false), dec_map_(), - dec_external_map_() { -} + dec_external_map_() {} VCMCodecDataBase::~VCMCodecDataBase() { ResetSender(); @@ -86,6 +131,26 @@ settings->codecType = kVideoCodecVP8; // 96 to 127 dynamic payload types for video codecs. settings->plType = VCM_VP8_PAYLOAD_TYPE; + settings->startBitrate = kDefaultStartBitrateKbps; + settings->minBitrate = VCM_MIN_BITRATE; + settings->maxBitrate = 0; + settings->maxFramerate = VCM_DEFAULT_FRAME_RATE; + settings->width = VCM_DEFAULT_CODEC_WIDTH; + settings->height = VCM_DEFAULT_CODEC_HEIGHT; + // consider using 2 to avoid deal with 'odd' downscales + settings->resolution_divisor = 1; // may not actually be needed + settings->numberOfSimulcastStreams = 0; + settings->qpMax = 56; + settings->codecSpecific.VP8 = VideoEncoder::GetDefaultVp8Settings(); + return true; + } +#endif +#ifdef VIDEOCODEC_VP9 + case VCM_VP9_IDX: { + strncpy(settings->plName, "VP9", 4); + settings->codecType = kVideoCodecVP9; + // 96 to 127 dynamic payload types for video codecs. + settings->plType = VCM_VP9_PAYLOAD_TYPE; settings->startBitrate = 100; settings->minBitrate = VCM_MIN_BITRATE; settings->maxBitrate = 0; @@ -96,13 +161,27 @@ settings->resolution_divisor = 1; // may not actually be needed settings->numberOfSimulcastStreams = 0; settings->qpMax = 56; - settings->codecSpecific.VP8.resilience = kResilientStream; - settings->codecSpecific.VP8.numberOfTemporalLayers = 1; - settings->codecSpecific.VP8.denoisingOn = true; - settings->codecSpecific.VP8.errorConcealmentOn = false; - settings->codecSpecific.VP8.automaticResizeOn = false; - settings->codecSpecific.VP8.frameDroppingOn = true; - settings->codecSpecific.VP8.keyFrameInterval = 3000; + settings->codecSpecific.VP9 = VideoEncoder::GetDefaultVp9Settings(); + return true; + } +#endif +#ifdef VIDEOCODEC_H264 + case VCM_H264_IDX: { + strncpy(settings->plName, "H264", 5); + settings->codecType = kVideoCodecH264; + // 96 to 127 dynamic payload types for video codecs. + settings->plType = VCM_H264_PAYLOAD_TYPE; + settings->startBitrate = kDefaultStartBitrateKbps; + settings->minBitrate = VCM_MIN_BITRATE; + settings->maxBitrate = 0; + settings->maxFramerate = VCM_DEFAULT_FRAME_RATE; + settings->width = VCM_DEFAULT_CODEC_WIDTH; + settings->height = VCM_DEFAULT_CODEC_HEIGHT; + // consider using 2 to avoid deal with 'odd' downscales + settings->resolution_divisor = 1; // may not actually be needed + settings->numberOfSimulcastStreams = 0; + settings->qpMax = 56; + settings->codecSpecific.H264 = VideoEncoder::GetDefaultH264Settings(); return true; } #endif @@ -123,6 +202,8 @@ settings->resolution_divisor = 1; settings->minBitrate = VCM_MIN_BITRATE; settings->numberOfSimulcastStreams = 0; + // consider using 2 to avoid deal with 'odd' downscales + settings->resolution_divisor = 1; // may not actually be needed return true; } #endif @@ -163,7 +244,7 @@ if (max_payload_size <= 0) { max_payload_size = kDefaultPayloadSize; } - if (number_of_cores <= 0 || number_of_cores > 32) { + if (number_of_cores <= 0) { return false; } if (send_codec->plType <= 0) { @@ -224,24 +305,14 @@ } else { ptr_encoder_ = CreateEncoder(send_codec->codecType); current_enc_is_external_ = false; + if (!ptr_encoder_) { + return false; + } } encoded_frame_callback->SetPayloadType(send_codec->plType); - if (!ptr_encoder_) { - WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceVideoCoding, - VCMId(id_), - "Failed to create encoder: %s.", - send_codec->plName); - return false; - } if (ptr_encoder_->InitEncode(send_codec, number_of_cores_, max_payload_size_) < 0) { - WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceVideoCoding, - VCMId(id_), - "Failed to initialize encoder: %s.", - send_codec->plName); DeleteEncoder(); return false; } else if (ptr_encoder_->RegisterEncodeCallback(encoded_frame_callback) < 0) { @@ -260,8 +331,6 @@ } bool VCMCodecDataBase::SendCodec(VideoCodec* current_send_codec) const { - WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, VCMId(id_), - "SendCodec"); if (!ptr_encoder_) { return false; } @@ -270,8 +339,6 @@ } VideoCodecType VCMCodecDataBase::SendCodec() const { - WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, VCMId(id_), - "SendCodec type"); if (!ptr_encoder_) { return kVideoCodecUnknown; } @@ -336,8 +403,21 @@ case kVideoCodecVP8: if (memcmp(&new_send_codec.codecSpecific.VP8, &send_codec_.codecSpecific.VP8, - sizeof(new_send_codec.codecSpecific.VP8)) != - 0) { + sizeof(new_send_codec.codecSpecific.VP8)) != 0) { + return true; + } + break; + case kVideoCodecVP9: + if (memcmp(&new_send_codec.codecSpecific.VP9, + &send_codec_.codecSpecific.VP9, + sizeof(new_send_codec.codecSpecific.VP9)) != 0) { + return true; + } + break; + case kVideoCodecH264: + if (memcmp(&new_send_codec.codecSpecific.H264, + &send_codec_.codecSpecific.H264, + sizeof(new_send_codec.codecSpecific.H264)) != 0) { return true; } break; @@ -347,8 +427,6 @@ case kVideoCodecI420: case kVideoCodecRED: case kVideoCodecULPFEC: - case kVideoCodecH264: - // TODO(jesup): analyze codec config for H264 break; // Unknown codec type, reset just to be sure. case kVideoCodecUnknown: @@ -404,7 +482,11 @@ // Not found return false; } - if (receive_codec_.plType == payload_type) { + // We can't use payload_type to check if the decoder is currently in use, + // because payload type may be out of date (e.g. before we decode the first + // frame after RegisterReceiveCodec) + if (ptr_decoder_ != NULL && + &ptr_decoder_->_decoder == (*it).second->external_decoder_instance) { // Release it if it was registered and in use. ReleaseDecoder(ptr_decoder_); ptr_decoder_ = NULL; @@ -443,12 +525,6 @@ if (number_of_cores < 0) { return false; } - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCoding, VCMId(id_), - "Codec: %s, Payload type %d, Height %d, Width %d, Bitrate %d," - "Framerate %d.", - receive_codec->plName, receive_codec->plType, - receive_codec->height, receive_codec->width, - receive_codec->startBitrate, receive_codec->maxFramerate); // Check if payload value already exists, if so - erase old and insert new. DeregisterReceiveCodec(receive_codec->plType); if (receive_codec->codecType == kVideoCodecUnknown) { @@ -530,7 +606,7 @@ if (!decoder_copy) { return NULL; } - return new VCMGenericDecoder(*decoder_copy, id_, ptr_decoder_->External()); + return new VCMGenericDecoder(*decoder_copy, ptr_decoder_->External()); } void VCMCodecDataBase::ReleaseDecoder(VCMGenericDecoder* decoder) const { @@ -549,8 +625,7 @@ if (decoder_copy) { VCMDecodedFrameCallback* cb = ptr_decoder_->_callback; ReleaseDecoder(ptr_decoder_); - ptr_decoder_ = new VCMGenericDecoder(*decoder_copy, id_, - decoder.External()); + ptr_decoder_ = new VCMGenericDecoder(*decoder_copy, decoder.External()); if (cb && ptr_decoder_->RegisterDecodeCompleteCallback(cb)) { assert(false); } @@ -563,8 +638,7 @@ const VCMExtDecoderMapItem* ext_item = FindExternalDecoderItem( receive_codec_.plType); if (!ext_item) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(id_), - "Unknown payload type: %u", receive_codec_.plType); + LOG(LS_ERROR) << "Unknown payload type: " << receive_codec_.plType; return false; } render_timing = ext_item->internal_render_timing; @@ -580,8 +654,8 @@ assert(new_codec); const VCMDecoderMapItem* decoder_item = FindDecoderItem(payload_type); if (!decoder_item) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(id_), - "Unknown payload type: %u", payload_type); + LOG(LS_ERROR) << "Can't find a decoder associated with payload type: " + << payload_type; return NULL; } VCMGenericDecoder* ptr_decoder = NULL; @@ -590,7 +664,7 @@ if (external_dec_item) { // External codec. ptr_decoder = new VCMGenericDecoder( - *external_dec_item->external_decoder_instance, id_, true); + *external_dec_item->external_decoder_instance, true); *external = true; } else { // Create decoder. @@ -617,11 +691,16 @@ case kVideoCodecVP8: return new VCMGenericEncoder(*(VP8Encoder::Create())); #endif +#ifdef VIDEOCODEC_VP9 + case kVideoCodecVP9: + return new VCMGenericEncoder(*(VP9Encoder::Create())); +#endif #ifdef VIDEOCODEC_I420 case kVideoCodecI420: return new VCMGenericEncoder(*(new I420Encoder)); #endif default: + LOG(LS_WARNING) << "No internal encoder of this type exists."; return NULL; } } @@ -641,13 +720,18 @@ switch (type) { #ifdef VIDEOCODEC_VP8 case kVideoCodecVP8: - return new VCMGenericDecoder(*(VP8Decoder::Create()), id_); + return new VCMGenericDecoder(*(VP8Decoder::Create())); +#endif +#ifdef VIDEOCODEC_VP9 + case kVideoCodecVP9: + return new VCMGenericDecoder(*(VP9Decoder::Create())); #endif #ifdef VIDEOCODEC_I420 case kVideoCodecI420: - return new VCMGenericDecoder(*(new I420Decoder), id_); + return new VCMGenericDecoder(*(new I420Decoder)); #endif default: + LOG(LS_WARNING) << "No internal decoder of this type exists."; return NULL; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/codec_database.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/codec_database.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/codec_database.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/codec_database.h 2015-02-03 14:33:36.000000000 +0000 @@ -50,7 +50,7 @@ class VCMCodecDataBase { public: - explicit VCMCodecDataBase(int id); + VCMCodecDataBase(); ~VCMCodecDataBase(); // Sender Side @@ -174,7 +174,6 @@ const VCMExtDecoderMapItem* FindExternalDecoderItem( uint8_t payload_type) const; - int id_; int number_of_cores_; int max_payload_size_; bool periodic_key_frames_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/encoded_frame.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/encoded_frame.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/encoded_frame.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/encoded_frame.cc 2015-02-03 14:33:36.000000000 +0000 @@ -100,34 +100,34 @@ void VCMEncodedFrame::CopyCodecSpecific(const RTPVideoHeader* header) { - if (header) { - switch (header->codec) { - case kRtpVideoVp8: { - if (_codecSpecificInfo.codecType != kVideoCodecVP8) { - // This is the first packet for this frame. - _codecSpecificInfo.codecSpecific.VP8.pictureId = -1; - _codecSpecificInfo.codecSpecific.VP8.temporalIdx = 0; - _codecSpecificInfo.codecSpecific.VP8.layerSync = false; - _codecSpecificInfo.codecSpecific.VP8.keyIdx = -1; - _codecSpecificInfo.codecType = kVideoCodecVP8; - } - _codecSpecificInfo.codecSpecific.VP8.nonReference = - header->codecHeader.VP8.nonReference; - if (header->codecHeader.VP8.pictureId != kNoPictureId) { - _codecSpecificInfo.codecSpecific.VP8.pictureId = - header->codecHeader.VP8.pictureId; - } - if (header->codecHeader.VP8.temporalIdx != kNoTemporalIdx) { - _codecSpecificInfo.codecSpecific.VP8.temporalIdx = - header->codecHeader.VP8.temporalIdx; - _codecSpecificInfo.codecSpecific.VP8.layerSync = - header->codecHeader.VP8.layerSync; - } - if (header->codecHeader.VP8.keyIdx != kNoKeyIdx) { - _codecSpecificInfo.codecSpecific.VP8.keyIdx = - header->codecHeader.VP8.keyIdx; - } - break; + if (header) { + switch (header->codec) { + case kRtpVideoVp8: { + if (_codecSpecificInfo.codecType != kVideoCodecVP8) { + // This is the first packet for this frame. + _codecSpecificInfo.codecSpecific.VP8.pictureId = -1; + _codecSpecificInfo.codecSpecific.VP8.temporalIdx = 0; + _codecSpecificInfo.codecSpecific.VP8.layerSync = false; + _codecSpecificInfo.codecSpecific.VP8.keyIdx = -1; + _codecSpecificInfo.codecType = kVideoCodecVP8; + } + _codecSpecificInfo.codecSpecific.VP8.nonReference = + header->codecHeader.VP8.nonReference; + if (header->codecHeader.VP8.pictureId != kNoPictureId) { + _codecSpecificInfo.codecSpecific.VP8.pictureId = + header->codecHeader.VP8.pictureId; + } + if (header->codecHeader.VP8.temporalIdx != kNoTemporalIdx) { + _codecSpecificInfo.codecSpecific.VP8.temporalIdx = + header->codecHeader.VP8.temporalIdx; + _codecSpecificInfo.codecSpecific.VP8.layerSync = + header->codecHeader.VP8.layerSync; + } + if (header->codecHeader.VP8.keyIdx != kNoKeyIdx) { + _codecSpecificInfo.codecSpecific.VP8.keyIdx = + header->codecHeader.VP8.keyIdx; + } + break; } case kRtpVideoH264: { _codecSpecificInfo.codecSpecific.H264.nalu_header = @@ -149,17 +149,12 @@ return &_fragmentation; } -int32_t -VCMEncodedFrame::VerifyAndAllocate(const uint32_t minimumSize) +void VCMEncodedFrame::VerifyAndAllocate(const uint32_t minimumSize) { if(minimumSize > _size) { // create buffer of sufficient size uint8_t* newBuffer = new uint8_t[minimumSize]; - if (newBuffer == NULL) - { - return -1; - } if(_buffer) { // copy old data @@ -169,7 +164,6 @@ _buffer = newBuffer; _size = minimumSize; } - return 0; } webrtc::FrameType VCMEncodedFrame::ConvertFrameType(VideoFrameType frameType) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/encoded_frame.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/encoded_frame.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/encoded_frame.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/encoded_frame.h 2015-02-03 14:33:36.000000000 +0000 @@ -104,7 +104,7 @@ * is copied to the new buffer. * Buffer size is updated to minimumSize. */ - int32_t VerifyAndAllocate(const uint32_t minimumSize); + void VerifyAndAllocate(const uint32_t minimumSize); void Reset(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/frame_buffer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/frame_buffer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/frame_buffer.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/frame_buffer.cc 2015-02-03 14:33:36.000000000 +0000 @@ -14,6 +14,7 @@ #include #include "webrtc/modules/video_coding/main/source/packet.h" +#include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { @@ -86,20 +87,7 @@ int64_t timeInMs, VCMDecodeErrorMode decode_error_mode, const FrameData& frame_data) { - // Is this packet part of this frame? - if (TimeStamp() && (TimeStamp() != packet.timestamp)) { - return kTimeStampError; - } - - // sanity checks - if (_size + packet.sizeBytes + - (packet.insertStartCode ? kH264StartCodeLengthBytes : 0 ) - > kMaxJBFrameSizeBytes) { - return kSizeError; - } - if (NULL == packet.dataPtr && packet.sizeBytes > 0) { - return kSizeError; - } + assert(!(NULL == packet.dataPtr && packet.sizeBytes > 0)); if (packet.dataPtr != NULL) { _payloadType = packet.payloadType; } @@ -108,6 +96,8 @@ // First packet (empty and/or media) inserted into this frame. // store some info and set some initial values. _timeStamp = packet.timestamp; + // We only take the ntp timestamp of the first packet of a frame. + ntp_time_ms_ = packet.ntp_time_ms_; _codec = packet.codec; if (packet.frameType != kFrameEmpty) { // first media packet @@ -126,11 +116,11 @@ const uint32_t newSize = _size + increments * kBufferIncStepSizeBytes; if (newSize > kMaxJBFrameSizeBytes) { + LOG(LS_ERROR) << "Failed to insert packet due to frame being too " + "big."; return kSizeError; } - if (VerifyAndAllocate(newSize) == -1) { - return kSizeError; - } + VerifyAndAllocate(newSize); _sessionInfo.UpdateDataPointers(prevBuffer, _buffer); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/generic_decoder.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/generic_decoder.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/generic_decoder.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/generic_decoder.cc 2015-02-03 14:33:36.000000000 +0000 @@ -12,8 +12,7 @@ #include "webrtc/modules/video_coding/main/source/generic_decoder.h" #include "webrtc/modules/video_coding/main/source/internal_defines.h" #include "webrtc/system_wrappers/interface/clock.h" -#include "webrtc/system_wrappers/interface/trace.h" -#include "webrtc/system_wrappers/interface/trace_event.h" +#include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { @@ -59,10 +58,11 @@ _timestampMap.Pop(decodedImage.timestamp())); callback = _receiveCallback; } - if (frameInfo == NULL) - { - // The map should never be empty or full if this callback is called. - return WEBRTC_VIDEO_CODEC_ERROR; + + if (frameInfo == NULL) { + LOG(LS_WARNING) << "Too many frames backed up in the decoder, dropping " + "this one."; + return WEBRTC_VIDEO_CODEC_OK; } _timing.StopDecodeTimer( @@ -73,14 +73,7 @@ if (callback != NULL) { decodedImage.set_render_time_ms(frameInfo->renderTimeMs); - int32_t callbackReturn = callback->FrameToRender(decodedImage); - if (callbackReturn < 0) - { - WEBRTC_TRACE(webrtc::kTraceDebug, - webrtc::kTraceVideoCoding, - -1, - "Render callback returned error: %d", callbackReturn); - } + callback->FrameToRender(decodedImage); } return WEBRTC_VIDEO_CODEC_OK; } @@ -125,15 +118,15 @@ return VCM_OK; } -VCMGenericDecoder::VCMGenericDecoder(VideoDecoder& decoder, int32_t id, bool isExternal) +VCMGenericDecoder::VCMGenericDecoder(VideoDecoder& decoder, bool isExternal) : -_id(id), _callback(NULL), _frameInfos(), _nextFrameInfoIdx(0), _decoder(decoder), _codecType(kVideoCodecUnknown), -_isExternal(isExternal) +_isExternal(isExternal), +_keyFrameDecoded(false) { } @@ -156,11 +149,6 @@ _frameInfos[_nextFrameInfoIdx].renderTimeMs = frame.RenderTimeMs(); _callback->Map(frame.TimeStamp(), &_frameInfos[_nextFrameInfoIdx]); - WEBRTC_TRACE(webrtc::kTraceDebug, - webrtc::kTraceVideoCoding, - VCMId(_id), - "Decoding timestamp %u", frame.TimeStamp()); - _nextFrameInfoIdx = (_nextFrameInfoIdx + 1) % kDecoderFrameMemoryLength; int32_t ret = _decoder.Decode(frame.EncodedImage(), frame.MissingFrame(), @@ -170,7 +158,8 @@ if (ret < WEBRTC_VIDEO_CODEC_OK) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id), "Decoder error: %d\n", ret); + LOG(LS_WARNING) << "Failed to decode frame with timestamp " + << frame.TimeStamp() << ", error code: " << ret; _callback->Pop(frame.TimeStamp()); return ret; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/generic_decoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/generic_decoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/generic_decoder.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/generic_decoder.h 2015-02-03 14:33:36.000000000 +0000 @@ -63,7 +63,7 @@ { friend class VCMCodecDataBase; public: - VCMGenericDecoder(VideoDecoder& decoder, int32_t id = 0, bool isExternal = false); + VCMGenericDecoder(VideoDecoder& decoder, bool isExternal = false); ~VCMGenericDecoder(); /** @@ -105,17 +105,14 @@ bool External() const; -protected: - - int32_t _id; +private: VCMDecodedFrameCallback* _callback; VCMFrameInformation _frameInfos[kDecoderFrameMemoryLength]; - uint32_t _nextFrameInfoIdx; + uint32_t _nextFrameInfoIdx; VideoDecoder& _decoder; VideoCodecType _codecType; bool _isExternal; bool _keyFrameDecoded; - }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/generic_encoder.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/generic_encoder.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/generic_encoder.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/generic_encoder.cc 2015-02-03 14:33:36.000000000 +0000 @@ -13,6 +13,7 @@ #include "webrtc/modules/video_coding/main/source/generic_encoder.h" #include "webrtc/modules/video_coding/main/source/media_optimization.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { namespace { @@ -87,7 +88,12 @@ _bitRate = settings->startBitrate * 1000; _frameRate = settings->maxFramerate; _codecType = settings->codecType; - return _encoder.InitEncode(settings, numberOfCores, maxPayloadSize); + if (_encoder.InitEncode(settings, numberOfCores, maxPayloadSize) != 0) { + LOG(LS_ERROR) << "Failed to initialize the encoder associated with " + "payload name: " << settings->plName; + return -1; + } + return 0; } int32_t diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/internal_defines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/internal_defines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/internal_defines.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/internal_defines.h 2015-02-03 14:33:36.000000000 +0000 @@ -35,16 +35,26 @@ // Helper macros for creating the static codec list #define VCM_NO_CODEC_IDX -1 #ifdef VIDEOCODEC_VP8 - #define VCM_VP8_IDX VCM_NO_CODEC_IDX + 1 + #define VCM_VP8_IDX (VCM_NO_CODEC_IDX + 1) #else #define VCM_VP8_IDX VCM_NO_CODEC_IDX #endif +#ifdef VIDEOCODEC_VP9 + #define VCM_VP9_IDX (VCM_VP8_IDX + 1) +#else + #define VCM_VP9_IDX VCM_VP8_IDX +#endif +#ifdef VIDEOCODEC_H264 + #define VCM_H264_IDX (VCM_VP9_IDX + 1) +#else + #define VCM_H264_IDX VCM_VP9_IDX +#endif #ifdef VIDEOCODEC_I420 - #define VCM_I420_IDX VCM_VP8_IDX + 1 + #define VCM_I420_IDX (VCM_H264_IDX + 1) #else - #define VCM_I420_IDX VCM_VP8_IDX + #define VCM_I420_IDX VCM_H264_IDX #endif -#define VCM_NUM_VIDEO_CODECS_AVAILABLE VCM_I420_IDX + 1 +#define VCM_NUM_VIDEO_CODECS_AVAILABLE (VCM_I420_IDX + 1) #define VCM_NO_RECEIVER_ID 0 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer.cc 2015-02-03 14:33:36.000000000 +0000 @@ -25,7 +25,7 @@ #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/event_wrapper.h" #include "webrtc/system_wrappers/interface/logging.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/metrics.h" #include "webrtc/system_wrappers/interface/trace_event.h" namespace webrtc { @@ -114,10 +114,6 @@ FrameList::iterator it = begin(); while (!empty()) { // Throw at least one frame. - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, -1, - "Recycling: type=%s, low seqnum=%u", - it->second->FrameType() == kVideoFrameKey ? - "key" : "delta", it->second->GetLowSeqNum()); it->second->Reset(); free_frames->push_back(it->second); erase(it++); @@ -164,17 +160,10 @@ } } -VCMJitterBuffer::VCMJitterBuffer(Clock* clock, - EventFactory* event_factory, - int vcm_id, - int receiver_id, - bool master) - : vcm_id_(vcm_id), - receiver_id_(receiver_id), - clock_(clock), +VCMJitterBuffer::VCMJitterBuffer(Clock* clock, EventFactory* event_factory) + : clock_(clock), running_(false), crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), - master_(master), frame_event_(event_factory->CreateEvent()), packet_event_(event_factory->CreateEvent()), max_number_of_frames_(kStartNumberOfFrames), @@ -192,8 +181,10 @@ drop_count_(0), num_consecutive_old_frames_(0), num_consecutive_old_packets_(0), + num_packets_(0), + num_duplicated_packets_(0), num_discarded_packets_(0), - jitter_estimate_(vcm_id, receiver_id), + jitter_estimate_(clock), inter_frame_delay_(clock_->TimeInMilliseconds()), rtt_ms_(kDefaultRtt), nack_mode_(kNoNack), @@ -229,10 +220,7 @@ if (this != &rhs) { crit_sect_->Enter(); rhs.crit_sect_->Enter(); - vcm_id_ = rhs.vcm_id_; - receiver_id_ = rhs.receiver_id_; running_ = rhs.running_; - master_ = !rhs.master_; max_number_of_frames_ = rhs.max_number_of_frames_; incoming_frame_rate_ = rhs.incoming_frame_rate_; incoming_frame_count_ = rhs.incoming_frame_count_; @@ -242,6 +230,8 @@ drop_count_ = rhs.drop_count_; num_consecutive_old_frames_ = rhs.num_consecutive_old_frames_; num_consecutive_old_packets_ = rhs.num_consecutive_old_packets_; + num_packets_ = rhs.num_packets_; + num_duplicated_packets_ = rhs.num_duplicated_packets_; num_discarded_packets_ = rhs.num_discarded_packets_; jitter_estimate_ = rhs.jitter_estimate_; inter_frame_delay_ = rhs.inter_frame_delay_; @@ -290,6 +280,15 @@ } } +void VCMJitterBuffer::UpdateHistograms() { + if (num_packets_ > 0) { + RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.DiscardedPacketsInPercent", + num_discarded_packets_ * 100 / num_packets_); + RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.DuplicatedPacketsInPercent", + num_duplicated_packets_ * 100 / num_packets_); + } +} + void VCMJitterBuffer::Start() { CriticalSectionScoped cs(crit_sect_); running_ = true; @@ -302,6 +301,8 @@ num_consecutive_old_frames_ = 0; num_consecutive_old_packets_ = 0; + num_packets_ = 0; + num_duplicated_packets_ = 0; num_discarded_packets_ = 0; // Start in a non-signaled state. @@ -313,14 +314,11 @@ first_packet_since_reset_ = true; rtt_ms_ = kDefaultRtt; last_decoded_state_.Reset(); - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, receiver_id_), "JB(0x%x): Jitter buffer: start", - this); } void VCMJitterBuffer::Stop() { crit_sect_->Enter(); + UpdateHistograms(); running_ = false; last_decoded_state_.Reset(); free_frames_.clear(); @@ -337,9 +335,6 @@ // Make sure we wake up any threads waiting on these events. frame_event_->Set(); packet_event_->Set(); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, receiver_id_), "JB(0x%x): Jitter buffer: stop", - this); } bool VCMJitterBuffer::Running() const { @@ -364,9 +359,6 @@ waiting_for_completion_.latest_packet_time = -1; first_packet_since_reset_ = true; missing_sequence_numbers_.clear(); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, receiver_id_), "JB(0x%x): Jitter buffer: flush", - this); } // Get received key and delta frames @@ -375,6 +367,16 @@ return receive_statistics_; } +int VCMJitterBuffer::num_packets() const { + CriticalSectionScoped cs(crit_sect_); + return num_packets_; +} + +int VCMJitterBuffer::num_duplicated_packets() const { + CriticalSectionScoped cs(crit_sect_); + return num_duplicated_packets_; +} + int VCMJitterBuffer::num_discarded_packets() const { CriticalSectionScoped cs(crit_sect_); return num_discarded_packets_; @@ -543,7 +545,6 @@ VCMEncodedFrame* VCMJitterBuffer::ExtractAndSetDecode(uint32_t timestamp) { CriticalSectionScoped cs(crit_sect_); - if (!running_) { return NULL; } @@ -611,6 +612,7 @@ // Gets frame to use for this timestamp. If no match, get empty frame. VCMFrameBufferEnum VCMJitterBuffer::GetFrame(const VCMPacket& packet, VCMFrameBuffer** frame) { + ++num_packets_; // Does this packet belong to an old frame? if (last_decoded_state_.IsOldPacket(&packet)) { // Account only for media packets. @@ -625,6 +627,8 @@ DropPacketsFromNackList(last_decoded_state_.sequence_num()); if (num_consecutive_old_packets_ > kMaxConsecutiveOldPackets) { + LOG(LS_WARNING) << num_consecutive_old_packets_ << " consecutive old " + "packets received. Flushing the jitter buffer."; Flush(); return kFlushIndicator; } @@ -653,13 +657,13 @@ VCMFrameBufferEnum ret = kNoError; if (!*frame) { // No free frame! Try to reclaim some... - LOG_F(LS_INFO) << "Unable to get empty frame; Recycling."; + LOG(LS_WARNING) << "Unable to get empty frame; Recycling."; bool found_key_frame = RecycleFramesUntilKeyFrame(); *frame = GetEmptyFrame(); - if (!*frame) - return kGeneralError; - else if (!found_key_frame) + assert(*frame); + if (!found_key_frame) { ret = kFlushIndicator; + } } (*frame)->Reset(); return ret; @@ -684,7 +688,6 @@ if (error != kNoError && frame == NULL) { return error; } - int64_t now_ms = clock_->TimeInMilliseconds(); // We are keeping track of the first and latest seq numbers, and // the number of wraps to be able to calculate how many packets we expect. @@ -701,6 +704,8 @@ // Flush if this happens consistently. num_consecutive_old_frames_++; if (num_consecutive_old_frames_ > kMaxConsecutiveOldFrames) { + LOG(LS_WARNING) << num_consecutive_old_packets_ << " consecutive old " + "frames received. Flushing the jitter buffer."; Flush(); return kFlushIndicator; } @@ -753,8 +758,8 @@ if (IsPacketRetransmitted(packet)) { frame->IncrementNackCount(); } - if (!UpdateNackList(packet.seqNum)) { - LOG_F(LS_INFO) << "Requesting key frame due to flushed NACK list."; + if (!UpdateNackList(packet.seqNum) && + packet.frameType != kVideoFrameKey) { buffer_return = kFlushIndicator; } latest_received_sequence_number_ = LatestSequenceNumber( @@ -776,15 +781,6 @@ } case kCompleteSession: { if (update_decodable_list) { - if (master_) { - // Only trace the primary jitter buffer to make it possible to parse - // and plot the trace file. - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, receiver_id_), - "JB(0x%x) FB(0x%x): Complete frame added to jitter" - "buffer, size:%d type %d", - this, frame, frame->Length(), frame->FrameType()); - } CountFrame(*frame); frame->SetCountedFrame(true); if (continuous) { @@ -830,6 +826,7 @@ case kNoError: case kOutOfBoundsPacket: case kDuplicatePacket: { + ++num_duplicated_packets_; break; } case kFlushIndicator: @@ -1011,8 +1008,6 @@ incomplete_frames_.begin(), incomplete_frames_.end(), HasNonEmptyState); } - if (have_non_empty_frame) - LOG_F(LS_INFO) << "First frame is not key; Recycling."; bool found_key_frame = RecycleFramesUntilKeyFrame(); if (!found_key_frame) { *request_key_frame = have_non_empty_frame; @@ -1028,9 +1023,9 @@ int non_continuous_incomplete_duration = NonContinuousOrIncompleteDuration(); if (non_continuous_incomplete_duration > 90 * max_incomplete_time_ms_) { - LOG_F(LS_INFO) << "Too long non-decodable duration: " << - non_continuous_incomplete_duration << " > " << - 90 * max_incomplete_time_ms_; + LOG_F(LS_WARNING) << "Too long non-decodable duration: " + << non_continuous_incomplete_duration << " > " + << 90 * max_incomplete_time_ms_; FrameList::reverse_iterator rit = find_if(incomplete_frames_.rbegin(), incomplete_frames_.rend(), IsKeyFrame); if (rit == incomplete_frames_.rend()) { @@ -1089,10 +1084,12 @@ TRACE_EVENT_INSTANT1("webrtc", "AddNack", "seqnum", i); } if (TooLargeNackList() && !HandleTooLargeNackList()) { + LOG(LS_WARNING) << "Requesting key frame due to too large NACK list."; return false; } if (MissingTooOldPacket(sequence_number) && !HandleTooOldPackets(sequence_number)) { + LOG(LS_WARNING) << "Requesting key frame due to missing too old packets"; return false; } } else { @@ -1109,8 +1106,9 @@ bool VCMJitterBuffer::HandleTooLargeNackList() { // Recycle frames until the NACK list is small enough. It is likely cheaper to // request a key frame than to retransmit this many missing packets. - LOG_F(LS_INFO) << "NACK list has grown too large: " << - missing_sequence_numbers_.size() << " > " << max_nack_list_size_; + LOG_F(LS_WARNING) << "NACK list has grown too large: " + << missing_sequence_numbers_.size() << " > " + << max_nack_list_size_; bool key_frame_found = false; while (TooLargeNackList()) { key_frame_found = RecycleFramesUntilKeyFrame(); @@ -1134,8 +1132,9 @@ bool key_frame_found = false; const uint16_t age_of_oldest_missing_packet = latest_sequence_number - *missing_sequence_numbers_.begin(); - LOG_F(LS_INFO) << "NACK list contains too old sequence numbers: " << - age_of_oldest_missing_packet << " > " << max_packet_age_to_nack_; + LOG_F(LS_WARNING) << "NACK list contains too old sequence numbers: " + << age_of_oldest_missing_packet << " > " + << max_packet_age_to_nack_; while (MissingTooOldPacket(latest_sequence_number)) { key_frame_found = RecycleFramesUntilKeyFrame(); } @@ -1187,10 +1186,6 @@ frame_buffers_[max_number_of_frames_] = new_frame; free_frames_.push_back(new_frame); ++max_number_of_frames_; - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, receiver_id_), - "JB(0x%x) FB(0x%x): Jitter buffer increased to:%d frames", - this, new_frame, max_number_of_frames_); TRACE_COUNTER1("webrtc", "JBMaxFrames", max_number_of_frames_); return true; } @@ -1212,13 +1207,9 @@ key_frame_found = key_frame_it != decodable_frames_.end(); } drop_count_ += dropped_frames; - if (dropped_frames) { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, receiver_id_), - "Jitter buffer drop count:%u", drop_count_); - } TRACE_EVENT_INSTANT0("webrtc", "JB::RecycleFramesUntilKeyFrame"); if (key_frame_found) { + LOG(LS_INFO) << "Found key frame while dropping frames."; // Reset last decoded state to make sure the next frame decoded is a key // frame, and start NACKing from here. last_decoded_state_.Reset(); @@ -1297,19 +1288,6 @@ if (sample.latest_packet_time == -1) { return; } - if (incomplete_frame) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, receiver_id_), "Received incomplete frame " - "timestamp %u frame size %u at time %u", - sample.timestamp, sample.frame_size, - MaskWord64ToUWord32(sample.latest_packet_time)); - } else { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, receiver_id_), "Received complete frame " - "timestamp %u frame size %u at time %u", - sample.timestamp, sample.frame_size, - MaskWord64ToUWord32(sample.latest_packet_time)); - } UpdateJitterEstimate(sample.latest_packet_time, sample.timestamp, sample.frame_size, incomplete_frame); } @@ -1324,23 +1302,6 @@ } // No retransmitted frames should be a part of the jitter // estimate. - if (incomplete_frame) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, receiver_id_), - "Received incomplete frame timestamp %u frame type %d " - "frame size %u at time %u, jitter estimate was %u", - frame.TimeStamp(), frame.FrameType(), frame.Length(), - MaskWord64ToUWord32(frame.LatestPacketTimeMs()), - EstimatedJitterMs()); - } else { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, receiver_id_), "Received complete frame " - "timestamp %u frame type %d frame size %u at time %u, " - "jitter estimate was %u", - frame.TimeStamp(), frame.FrameType(), frame.Length(), - MaskWord64ToUWord32(frame.LatestPacketTimeMs()), - EstimatedJitterMs()); - } UpdateJitterEstimate(frame.LatestPacketTimeMs(), frame.TimeStamp(), frame.Length(), incomplete_frame); } @@ -1357,12 +1318,6 @@ return; } int64_t frame_delay; - // Calculate the delay estimate - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, receiver_id_), - "Packet received and sent to jitter estimate with: " - "timestamp=%u wall_clock=%u", timestamp, - MaskWord64ToUWord32(latest_packet_time_ms)); bool not_reordered = inter_frame_delay_.CalculateDelay(timestamp, &frame_delay, latest_packet_time_ms); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer.h 2015-02-03 14:33:36.000000000 +0000 @@ -16,6 +16,7 @@ #include #include +#include "webrtc/base/constructormagic.h" #include "webrtc/modules/interface/module_common_types.h" #include "webrtc/modules/video_coding/main/interface/video_coding.h" #include "webrtc/modules/video_coding/main/interface/video_coding_defines.h" @@ -23,7 +24,6 @@ #include "webrtc/modules/video_coding/main/source/inter_frame_delay.h" #include "webrtc/modules/video_coding/main/source/jitter_buffer_common.h" #include "webrtc/modules/video_coding/main/source/jitter_estimator.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/typedefs.h" @@ -77,10 +77,7 @@ class VCMJitterBuffer { public: VCMJitterBuffer(Clock* clock, - EventFactory* event_factory, - int vcm_id, - int receiver_id, - bool master); + EventFactory* event_factory); virtual ~VCMJitterBuffer(); // Makes |this| a deep copy of |rhs|. @@ -106,6 +103,12 @@ // won't be able to decode them. int num_not_decodable_packets() const; + // Gets number of packets received. + int num_packets() const; + + // Gets number of duplicated packets received. + int num_duplicated_packets() const; + // Gets number of packets discarded by the jitter buffer. int num_discarded_packets() const; @@ -274,13 +277,12 @@ uint16_t EstimatedLowSequenceNumber(const VCMFrameBuffer& frame) const; - int vcm_id_; - int receiver_id_; + void UpdateHistograms(); + Clock* clock_; // If we are running (have started) or not. bool running_; CriticalSectionWrapper* crit_sect_; - bool master_; // Event to signal when we have a frame ready for decoder. scoped_ptr frame_event_; // Event to signal when we have received a packet. @@ -309,6 +311,10 @@ int num_consecutive_old_frames_; // Number of packets in a row that have been too old. int num_consecutive_old_packets_; + // Number of packets received. + int num_packets_; + // Number of duplicated packets received. + int num_duplicated_packets_; // Number of packets discarded by the jitter buffer. int num_discarded_packets_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -27,8 +27,8 @@ protected: virtual void SetUp() { clock_.reset(new SimulatedClock(0)); - jitter_buffer_.reset(new VCMJitterBuffer(clock_.get(), - &event_factory_, -1, -1, true)); + jitter_buffer_.reset( + new VCMJitterBuffer(clock_.get(), &event_factory_)); jitter_buffer_->Start(); seq_num_ = 1234; timestamp_ = 0; @@ -126,8 +126,7 @@ clock_.reset(new SimulatedClock(0)); max_nack_list_size_ = 150; oldest_packet_to_nack_ = 250; - jitter_buffer_ = new VCMJitterBuffer(clock_.get(), &event_factory_, -1, -1, - true); + jitter_buffer_ = new VCMJitterBuffer(clock_.get(), &event_factory_); stream_generator_ = new StreamGenerator(0, 0, clock_->TimeInMilliseconds()); jitter_buffer_->Start(); jitter_buffer_->SetNackSettings(max_nack_list_size_, @@ -513,6 +512,8 @@ packet_->markerBit = false; packet_->seqNum = seq_num_; packet_->timestamp = timestamp_; + EXPECT_EQ(0, jitter_buffer_->num_packets()); + EXPECT_EQ(0, jitter_buffer_->num_duplicated_packets()); bool retransmitted = false; EXPECT_EQ(kIncomplete, jitter_buffer_->InsertPacket(*packet_, @@ -521,6 +522,8 @@ VCMEncodedFrame* frame_out = DecodeCompleteFrame(); EXPECT_TRUE(frame_out == NULL); + EXPECT_EQ(1, jitter_buffer_->num_packets()); + EXPECT_EQ(0, jitter_buffer_->num_duplicated_packets()); packet_->isFirstPacket = false; packet_->markerBit = true; @@ -528,6 +531,8 @@ // Insert a packet into a frame. EXPECT_EQ(kDuplicatePacket, jitter_buffer_->InsertPacket(*packet_, &retransmitted)); + EXPECT_EQ(2, jitter_buffer_->num_packets()); + EXPECT_EQ(1, jitter_buffer_->num_duplicated_packets()); seq_num_++; packet_->seqNum = seq_num_; @@ -540,6 +545,8 @@ CheckOutFrame(frame_out, 2 * size_, false); EXPECT_EQ(kVideoFrameKey, frame_out->FrameType()); + EXPECT_EQ(3, jitter_buffer_->num_packets()); + EXPECT_EQ(1, jitter_buffer_->num_duplicated_packets()); } TEST_F(TestBasicJitterBuffer, H264InsertStartCode) { @@ -2034,4 +2041,31 @@ EXPECT_EQ(65535, list[0]); } +TEST_F(TestJitterBufferNack, ResetByFutureKeyFrameDoesntError) { + stream_generator_->Init(0, 0, clock_->TimeInMilliseconds()); + InsertFrame(kVideoFrameKey); + EXPECT_TRUE(DecodeCompleteFrame()); + uint16_t nack_list_size = 0; + bool extended = false; + jitter_buffer_->GetNackList(&nack_list_size, &extended); + EXPECT_EQ(0, nack_list_size); + + // Far-into-the-future video frame, could be caused by resetting the encoder + // or otherwise restarting. This should not fail when error when the packet is + // a keyframe, even if all of the nack list needs to be flushed. + stream_generator_->Init(10000, 0, clock_->TimeInMilliseconds()); + clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs); + InsertFrame(kVideoFrameKey); + EXPECT_TRUE(DecodeCompleteFrame()); + jitter_buffer_->GetNackList(&nack_list_size, &extended); + EXPECT_EQ(0, nack_list_size); + + // Stream should be decodable from this point. + clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs); + InsertFrame(kVideoFrameDelta); + EXPECT_TRUE(DecodeCompleteFrame()); + jitter_buffer_->GetNackList(&nack_list_size, &extended); + EXPECT_EQ(0, nack_list_size); +} + } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_estimator.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_estimator.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_estimator.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_estimator.cc 2015-02-03 14:33:36.000000000 +0000 @@ -11,7 +11,8 @@ #include "webrtc/modules/video_coding/main/source/internal_defines.h" #include "webrtc/modules/video_coding/main/source/jitter_estimator.h" #include "webrtc/modules/video_coding/main/source/rtt_filter.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/field_trial.h" #include #include @@ -20,21 +21,34 @@ namespace webrtc { -VCMJitterEstimator::VCMJitterEstimator(int32_t vcmId, int32_t receiverId) : -_vcmId(vcmId), -_receiverId(receiverId), -_phi(0.97), -_psi(0.9999), -_alphaCountMax(400), -_thetaLow(0.000001), -_nackLimit(3), -_numStdDevDelayOutlier(15), -_numStdDevFrameSizeOutlier(3), -_noiseStdDevs(2.33), // ~Less than 1% chance - // (look up in normal distribution table)... -_noiseStdDevOffset(30.0), // ...of getting 30 ms freezes -_rttFilter(vcmId, receiverId) { - Reset(); +enum { kStartupDelaySamples = 30 }; +enum { kFsAccuStartupSamples = 5 }; +enum { kMaxFramerateEstimate = 200 }; + +VCMJitterEstimator::VCMJitterEstimator(const Clock* clock, + int32_t vcmId, + int32_t receiverId) + : _vcmId(vcmId), + _receiverId(receiverId), + _phi(0.97), + _psi(0.9999), + _alphaCountMax(400), + _thetaLow(0.000001), + _nackLimit(3), + _numStdDevDelayOutlier(15), + _numStdDevFrameSizeOutlier(3), + _noiseStdDevs(2.33), // ~Less than 1% chance + // (look up in normal distribution table)... + _noiseStdDevOffset(30.0), // ...of getting 30 ms freezes + _rttFilter(), + fps_counter_(30), // TODO(sprang): Use an estimator with limit based on + // time, rather than number of samples. + low_rate_experiment_(kInit), + clock_(clock) { + Reset(); +} + +VCMJitterEstimator::~VCMJitterEstimator() { } VCMJitterEstimator& @@ -95,6 +109,7 @@ _fsCount = 0; _startupCount = 0; _rttFilter.Reset(); + fps_counter_.Reset(); } void @@ -108,10 +123,6 @@ VCMJitterEstimator::UpdateEstimate(int64_t frameDelayMS, uint32_t frameSizeBytes, bool incompleteFrame /* = false */) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, - VCMId(_vcmId, _receiverId), - "Jitter estimate updated with: frameSize=%d frameDelayMS=%d", - frameSizeBytes, frameDelayMS); if (frameSizeBytes == 0) { return; @@ -195,16 +206,6 @@ { _startupCount++; } - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Framesize statistics: max=%f average=%f", _maxFrameSize, _avgFrameSize); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "The estimated slope is: theta=(%f, %f)", _theta[0], _theta[1]); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Random jitter: mean=%f variance=%f", _avgNoise, _varNoise); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Current jitter estimate: %f", _filterJitterEstimate); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Current max RTT: %u", _rttFilter.RttMs()); } // Updates the nack/packet ratio @@ -312,35 +313,54 @@ // Estimates the random jitter by calculating the variance of the // sample distance from the line given by theta. -void -VCMJitterEstimator::EstimateRandomJitter(double d_dT, bool incompleteFrame) -{ - double alpha; - if (_alphaCount == 0) - { - assert(_alphaCount > 0); - return; - } - alpha = static_cast(_alphaCount - 1) / static_cast(_alphaCount); - _alphaCount++; - if (_alphaCount > _alphaCountMax) - { - _alphaCount = _alphaCountMax; - } - double avgNoise = alpha * _avgNoise + (1 - alpha) * d_dT; - double varNoise = alpha * _varNoise + - (1 - alpha) * (d_dT - _avgNoise) * (d_dT - _avgNoise); - if (!incompleteFrame || varNoise > _varNoise) - { - _avgNoise = avgNoise; - _varNoise = varNoise; - } - if (_varNoise < 1.0) - { - // The variance should never be zero, since we might get - // stuck and consider all samples as outliers. - _varNoise = 1.0; - } +void VCMJitterEstimator::EstimateRandomJitter(double d_dT, + bool incompleteFrame) { + uint64_t now = clock_->TimeInMicroseconds(); + if (_lastUpdateT != -1) { + fps_counter_.AddSample(now - _lastUpdateT); + } + _lastUpdateT = now; + + if (_alphaCount == 0) { + assert(false); + return; + } + double alpha = + static_cast(_alphaCount - 1) / static_cast(_alphaCount); + _alphaCount++; + if (_alphaCount > _alphaCountMax) + _alphaCount = _alphaCountMax; + + if (LowRateExperimentEnabled()) { + // In order to avoid a low frame rate stream to react slower to changes, + // scale the alpha weight relative a 30 fps stream. + double fps = GetFrameRate(); + if (fps > 0.0) { + double rate_scale = 30.0 / fps; + // At startup, there can be a lot of noise in the fps estimate. + // Interpolate rate_scale linearly, from 1.0 at sample #1, to 30.0 / fps + // at sample #kStartupDelaySamples. + if (_alphaCount < kStartupDelaySamples) { + rate_scale = + (_alphaCount * rate_scale + (kStartupDelaySamples - _alphaCount)) / + kStartupDelaySamples; + } + alpha = pow(alpha, rate_scale); + } + } + + double avgNoise = alpha * _avgNoise + (1 - alpha) * d_dT; + double varNoise = + alpha * _varNoise + (1 - alpha) * (d_dT - _avgNoise) * (d_dT - _avgNoise); + if (!incompleteFrame || varNoise > _varNoise) { + _avgNoise = avgNoise; + _varNoise = varNoise; + } + if (_varNoise < 1.0) { + // The variance should never be zero, since we might get + // stuck and consider all samples as outliers. + _varNoise = 1.0; + } } double @@ -402,19 +422,63 @@ // Returns the current filtered estimate if available, // otherwise tries to calculate an estimate. -int -VCMJitterEstimator::GetJitterEstimate(double rttMultiplier) -{ - double jitterMS = CalculateEstimate() + OPERATING_SYSTEM_JITTER; - if (_filterJitterEstimate > jitterMS) - { - jitterMS = _filterJitterEstimate; - } - if (_nackCount >= _nackLimit) - { - jitterMS += _rttFilter.RttMs() * rttMultiplier; - } - return static_cast(jitterMS + 0.5); +int VCMJitterEstimator::GetJitterEstimate(double rttMultiplier) { + double jitterMS = CalculateEstimate() + OPERATING_SYSTEM_JITTER; + if (_filterJitterEstimate > jitterMS) + jitterMS = _filterJitterEstimate; + if (_nackCount >= _nackLimit) + jitterMS += _rttFilter.RttMs() * rttMultiplier; + + if (LowRateExperimentEnabled()) { + static const double kJitterScaleLowThreshold = 5.0; + static const double kJitterScaleHighThreshold = 10.0; + double fps = GetFrameRate(); + // Ignore jitter for very low fps streams. + if (fps < kJitterScaleLowThreshold) { + if (fps == 0.0) { + return jitterMS; + } + return 0; + } + + // Semi-low frame rate; scale by factor linearly interpolated from 0.0 at + // kJitterScaleLowThreshold to 1.0 at kJitterScaleHighThreshold. + if (fps < kJitterScaleHighThreshold) { + jitterMS = + (1.0 / (kJitterScaleHighThreshold - kJitterScaleLowThreshold)) * + (fps - kJitterScaleLowThreshold) * jitterMS; + } + } + + return static_cast(jitterMS + 0.5); +} + +bool VCMJitterEstimator::LowRateExperimentEnabled() { +#ifndef WEBRTC_MOZILLA_BUILD + if (low_rate_experiment_ == kInit) { + std::string group = + webrtc::field_trial::FindFullName("WebRTC-ReducedJitterDelay"); + if (group == "Disabled") { + low_rate_experiment_ = kDisabled; + } else { + low_rate_experiment_ = kEnabled; + } + } +#endif + return low_rate_experiment_ == kEnabled ? true : false; +} + +double VCMJitterEstimator::GetFrameRate() const { + if (fps_counter_.count() == 0) + return 0; + + double fps = 1000000.0 / fps_counter_.ComputeMean(); + // Sanity check. + assert(fps >= 0.0); + if (fps > kMaxFramerateEstimate) { + fps = kMaxFramerateEstimate; + } + return fps; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_estimator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_estimator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_estimator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_estimator.h 2015-02-03 14:33:36.000000000 +0000 @@ -11,17 +11,22 @@ #ifndef WEBRTC_MODULES_VIDEO_CODING_JITTER_ESTIMATOR_H_ #define WEBRTC_MODULES_VIDEO_CODING_JITTER_ESTIMATOR_H_ +#include "webrtc/base/rollingaccumulator.h" #include "webrtc/modules/video_coding/main/source/rtt_filter.h" #include "webrtc/typedefs.h" namespace webrtc { +class Clock; + class VCMJitterEstimator { public: - VCMJitterEstimator(int32_t vcmId = 0, int32_t receiverId = 0); - + VCMJitterEstimator(const Clock* clock, + int32_t vcmId = 0, + int32_t receiverId = 0); + virtual ~VCMJitterEstimator(); VCMJitterEstimator& operator=(const VCMJitterEstimator& rhs); // Resets the estimate to the initial state @@ -68,6 +73,8 @@ double _theta[2]; // Estimated line parameters (slope, offset) double _varNoise; // Variance of the time-deviation from the line + virtual bool LowRateExperimentEnabled(); + private: // Updates the Kalman filter for the line describing // the frame size dependent jitter. @@ -109,6 +116,8 @@ double DeviationFromExpectedDelay(int64_t frameDelayMS, int32_t deltaFSBytes) const; + double GetFrameRate() const; + // Constants, filter parameters int32_t _vcmId; int32_t _receiverId; @@ -145,8 +154,10 @@ // but never goes above _nackLimit VCMRttFilter _rttFilter; - enum { kStartupDelaySamples = 30 }; - enum { kFsAccuStartupSamples = 5 }; + rtc::RollingAccumulator fps_counter_; + enum ExperimentFlag { kInit, kEnabled, kDisabled }; + ExperimentFlag low_rate_experiment_; + const Clock* clock_; }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_estimator_tests.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_estimator_tests.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_estimator_tests.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_estimator_tests.cc 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,160 @@ +/* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/video_coding/main/source/jitter_estimator.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/system_wrappers/interface/clock.h" + +namespace webrtc { + +class TestEstimator : public VCMJitterEstimator { + public: + explicit TestEstimator(bool exp_enabled) + : VCMJitterEstimator(&fake_clock_, 0, 0), + fake_clock_(0), + exp_enabled_(exp_enabled) {} + + virtual bool LowRateExperimentEnabled() { return exp_enabled_; } + + void AdvanceClock(int64_t microseconds) { + fake_clock_.AdvanceTimeMicroseconds(microseconds); + } + + private: + SimulatedClock fake_clock_; + const bool exp_enabled_; +}; + +class TestVCMJitterEstimator : public ::testing::Test { + protected: + TestVCMJitterEstimator() + : regular_estimator_(false), low_rate_estimator_(true) {} + + virtual void SetUp() { regular_estimator_.Reset(); } + + TestEstimator regular_estimator_; + TestEstimator low_rate_estimator_; +}; + +// Generates some simple test data in the form of a sawtooth wave. +class ValueGenerator { + public: + ValueGenerator(int32_t amplitude) : amplitude_(amplitude), counter_(0) {} + virtual ~ValueGenerator() {} + + int64_t Delay() { return ((counter_ % 11) - 5) * amplitude_; } + + uint32_t FrameSize() { return 1000 + Delay(); } + + void Advance() { ++counter_; } + + private: + const int32_t amplitude_; + int64_t counter_; +}; + +// 5 fps, disable jitter delay altogether. +TEST_F(TestVCMJitterEstimator, TestLowRate) { + ValueGenerator gen(10); + uint64_t time_delta = 1000000 / 5; + for (int i = 0; i < 60; ++i) { + regular_estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize()); + regular_estimator_.AdvanceClock(time_delta); + low_rate_estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize()); + low_rate_estimator_.AdvanceClock(time_delta); + EXPECT_GT(regular_estimator_.GetJitterEstimate(0), 0); + if (i > 2) + EXPECT_EQ(low_rate_estimator_.GetJitterEstimate(0), 0); + gen.Advance(); + } +} + +// 8 fps, steady state estimate should be in interpolated interval between 0 +// and value of previous method. +TEST_F(TestVCMJitterEstimator, TestMidRate) { + ValueGenerator gen(10); + uint64_t time_delta = 1000000 / 8; + for (int i = 0; i < 60; ++i) { + regular_estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize()); + regular_estimator_.AdvanceClock(time_delta); + low_rate_estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize()); + low_rate_estimator_.AdvanceClock(time_delta); + EXPECT_GT(regular_estimator_.GetJitterEstimate(0), 0); + EXPECT_GT(low_rate_estimator_.GetJitterEstimate(0), 0); + EXPECT_GE(regular_estimator_.GetJitterEstimate(0), + low_rate_estimator_.GetJitterEstimate(0)); + gen.Advance(); + } +} + +// 30 fps, steady state estimate should be same as previous method. +TEST_F(TestVCMJitterEstimator, TestHighRate) { + ValueGenerator gen(10); + uint64_t time_delta = 1000000 / 30; + for (int i = 0; i < 60; ++i) { + regular_estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize()); + regular_estimator_.AdvanceClock(time_delta); + low_rate_estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize()); + low_rate_estimator_.AdvanceClock(time_delta); + EXPECT_EQ(regular_estimator_.GetJitterEstimate(0), + low_rate_estimator_.GetJitterEstimate(0)); + gen.Advance(); + } +} + +// 10 fps, high jitter then low jitter. Low rate estimator should converge +// faster to low noise estimate. +TEST_F(TestVCMJitterEstimator, TestConvergence) { + // Reach a steady state with high noise. + ValueGenerator gen(50); + uint64_t time_delta = 1000000 / 10; + for (int i = 0; i < 100; ++i) { + regular_estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize()); + regular_estimator_.AdvanceClock(time_delta * 2); + low_rate_estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize()); + low_rate_estimator_.AdvanceClock(time_delta * 2); + gen.Advance(); + } + + int threshold = regular_estimator_.GetJitterEstimate(0) / 2; + + // New generator with zero noise. + ValueGenerator low_gen(0); + int regular_iterations = 0; + int low_rate_iterations = 0; + for (int i = 0; i < 500; ++i) { + if (regular_iterations == 0) { + regular_estimator_.UpdateEstimate(low_gen.Delay(), low_gen.FrameSize()); + regular_estimator_.AdvanceClock(time_delta); + if (regular_estimator_.GetJitterEstimate(0) < threshold) { + regular_iterations = i; + } + } + + if (low_rate_iterations == 0) { + low_rate_estimator_.UpdateEstimate(low_gen.Delay(), low_gen.FrameSize()); + low_rate_estimator_.AdvanceClock(time_delta); + if (low_rate_estimator_.GetJitterEstimate(0) < threshold) { + low_rate_iterations = i; + } + } + + if (regular_iterations != 0 && low_rate_iterations != 0) { + break; + } + + gen.Advance(); + } + + EXPECT_NE(regular_iterations, 0); + EXPECT_NE(low_rate_iterations, 0); + EXPECT_LE(low_rate_iterations, regular_iterations); +} +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_optimization.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_optimization.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_optimization.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_optimization.cc 2015-02-03 14:33:36.000000000 +0000 @@ -14,6 +14,7 @@ #include "webrtc/modules/video_coding/main/source/qm_select.h" #include "webrtc/modules/video_coding/utility/include/frame_dropper.h" #include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { namespace media_optimization { @@ -73,8 +74,8 @@ int64_t time_complete_ms; }; -MediaOptimization::MediaOptimization(int32_t id, Clock* clock) - : id_(id), +MediaOptimization::MediaOptimization(Clock* clock) + : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), clock_(clock), max_bit_rate_(0), send_codec_type_(kVideoCodecUnknown), @@ -114,7 +115,9 @@ } void MediaOptimization::Reset() { - SetEncodingData(kVideoCodecUnknown, 0, 0, 0, 0, 0, 1, 0, max_payload_size_); + CriticalSectionScoped lock(crit_sect_.get()); + SetEncodingData( + kVideoCodecUnknown, 0, 0, 0, 0, 0, 1, 0, max_payload_size_); memset(incoming_frame_times_, -1, sizeof(incoming_frame_times_)); incoming_frame_rate_ = 0.0; frame_dropper_->Reset(); @@ -160,6 +163,27 @@ uint8_t divisor, int num_layers, int32_t mtu) { + CriticalSectionScoped lock(crit_sect_.get()); + SetEncodingDataInternal(send_codec_type, + max_bit_rate, + frame_rate, + target_bitrate, + width, + height, + divisor, + num_layers, + mtu); +} + +void MediaOptimization::SetEncodingDataInternal(VideoCodecType send_codec_type, + int32_t max_bit_rate, // in bits/s + uint32_t frame_rate, // in fps*1000 + uint32_t target_bitrate, // in bits/s + uint16_t width, + uint16_t height, + uint8_t divisor, + int num_layers, + int32_t mtu) { // Everything codec specific should be reset here since this means the codec // has changed. If native dimension values have changed, then either user // initiated change, or QM initiated change. Will be able to determine only @@ -199,12 +223,10 @@ uint32_t round_trip_time_ms, VCMProtectionCallback* protection_callback, VCMQMSettingsCallback* qmsettings_callback) { - WEBRTC_TRACE(webrtc::kTraceDebug, - webrtc::kTraceVideoCoding, - id_, - "SetTargetRates: %u bps %u%% loss %dms RTT", - target_bitrate, fraction_lost, round_trip_time_ms); + LOG(LS_INFO) << "SetTargetRates: " << target_bitrate << " bps " << fraction_lost + << "% loss " << round_trip_time_ms << "ms RTT"; + CriticalSectionScoped lock(crit_sect_.get()); // TODO(holmer): Consider putting this threshold only on the video bitrate, // and not on protection. if (max_bit_rate_ > 0 && @@ -218,7 +240,7 @@ loss_prot_logic_->UpdateResidualPacketLoss(static_cast(fraction_lost)); // Get frame rate for encoder: this is the actual/sent frame rate. - float actual_frame_rate = SentFrameRate(); + float actual_frame_rate = SentFrameRateInternal(); // Sanity check. if (actual_frame_rate < 1.0) { @@ -300,14 +322,9 @@ frame_dropper_->SetRates(target_video_bitrate_kbps, incoming_frame_rate_); if (enable_qm_ && qmsettings_callback) { - WEBRTC_TRACE(webrtc::kTraceDebug, - webrtc::kTraceVideoCoding, - id_, - "SetTargetRates/enable_qm: %f bps %f kbps %f fps %d loss", - target_video_bitrate_kbps, - sent_video_rate_kbps, - incoming_frame_rate_, - fraction_lost_); + LOG(LS_INFO) << "SetTargetRates/enable_qm: " << target_video_bitrate_kbps + << " bps, " << sent_video_rate_kbps << " kbps, " << incoming_frame_rate_ + << " fps, " << fraction_lost << " loss"; // Update QM with rates. qm_resolution_->UpdateRates(target_video_bitrate_kbps, @@ -330,6 +347,7 @@ void MediaOptimization::EnableProtectionMethod(bool enable, VCMProtectionMethodEnum method) { + CriticalSectionScoped lock(crit_sect_.get()); bool updated = false; if (enable) { updated = loss_prot_logic_->SetMethod(method); @@ -342,17 +360,28 @@ } uint32_t MediaOptimization::InputFrameRate() { + CriticalSectionScoped lock(crit_sect_.get()); + return InputFrameRateInternal(); +} + +uint32_t MediaOptimization::InputFrameRateInternal() { ProcessIncomingFrameRate(clock_->TimeInMilliseconds()); return uint32_t(incoming_frame_rate_ + 0.5f); } uint32_t MediaOptimization::SentFrameRate() { + CriticalSectionScoped lock(crit_sect_.get()); + return SentFrameRateInternal(); +} + +uint32_t MediaOptimization::SentFrameRateInternal() { PurgeOldFrameSamples(clock_->TimeInMilliseconds()); UpdateSentFramerate(); return avg_sent_framerate_; } uint32_t MediaOptimization::SentBitRate() { + CriticalSectionScoped lock(crit_sect_.get()); const int64_t now_ms = clock_->TimeInMilliseconds(); PurgeOldFrameSamples(now_ms); UpdateSentBitrate(now_ms); @@ -360,6 +389,7 @@ } VCMFrameCount MediaOptimization::SentFrameCount() { + CriticalSectionScoped lock(crit_sect_.get()); VCMFrameCount count; count.numDeltaFrames = delta_frame_cnt_; count.numKeyFrames = key_frame_cnt_; @@ -369,6 +399,7 @@ int32_t MediaOptimization::UpdateWithEncodedData(int encoded_length, uint32_t timestamp, FrameType encoded_frame_type) { + CriticalSectionScoped lock(crit_sect_.get()); const int64_t now_ms = clock_->TimeInMilliseconds(); bool same_frame; PurgeOldFrameSamples(now_ms); @@ -426,22 +457,55 @@ return VCM_OK; } -void MediaOptimization::EnableQM(bool enable) { enable_qm_ = enable; } +void MediaOptimization::EnableQM(bool enable) { + CriticalSectionScoped lock(crit_sect_.get()); + enable_qm_ = enable; +} void MediaOptimization::EnableFrameDropper(bool enable) { + CriticalSectionScoped lock(crit_sect_.get()); frame_dropper_->Enable(enable); } +void MediaOptimization::SuspendBelowMinBitrate(int threshold_bps, + int window_bps) { + CriticalSectionScoped lock(crit_sect_.get()); + assert(threshold_bps > 0 && window_bps >= 0); + suspension_threshold_bps_ = threshold_bps; + suspension_window_bps_ = window_bps; + suspension_enabled_ = true; + video_suspended_ = false; +} + +bool MediaOptimization::IsVideoSuspended() const { + CriticalSectionScoped lock(crit_sect_.get()); + return video_suspended_; +} + bool MediaOptimization::DropFrame() { + CriticalSectionScoped lock(crit_sect_.get()); UpdateIncomingFrameRate(); // Leak appropriate number of bytes. - frame_dropper_->Leak((uint32_t)(InputFrameRate() + 0.5f)); + frame_dropper_->Leak((uint32_t)(InputFrameRateInternal() + 0.5f)); if (video_suspended_) { return true; // Drop all frames when muted. } return frame_dropper_->DropFrame(); } +void MediaOptimization::UpdateContentData( + const VideoContentMetrics* content_metrics) { + CriticalSectionScoped lock(crit_sect_.get()); + // Updating content metrics. + if (content_metrics == NULL) { + // Disable QM if metrics are NULL. + enable_qm_ = false; + qm_resolution_->Reset(); + } else { + content_->UpdateContentData(content_metrics); + } +} + void MediaOptimization::UpdateIncomingFrameRate() { int64_t now = clock_->TimeInMilliseconds(); if (incoming_frame_times_[0] == 0) { @@ -456,18 +520,6 @@ ProcessIncomingFrameRate(now); } -void MediaOptimization::UpdateContentData( - const VideoContentMetrics* content_metrics) { - // Updating content metrics. - if (content_metrics == NULL) { - // Disable QM if metrics are NULL. - enable_qm_ = false; - qm_resolution_->Reset(); - } else { - content_->UpdateContentData(content_metrics); - } -} - int32_t MediaOptimization::SelectQuality( VCMQMSettingsCallback* video_qmsettings_callback) { // Reset quantities for QM select. @@ -501,17 +553,6 @@ return VCM_OK; } -void MediaOptimization::SuspendBelowMinBitrate(int threshold_bps, - int window_bps) { - assert(threshold_bps > 0 && window_bps >= 0); - suspension_threshold_bps_ = threshold_bps; - suspension_window_bps_ = window_bps; - suspension_enabled_ = true; - video_suspended_ = false; -} - -bool MediaOptimization::IsVideoSuspended() const { return video_suspended_; } - void MediaOptimization::PurgeOldFrameSamples(int64_t now_ms) { while (!encoded_frame_samples_.empty()) { if (now_ms - encoded_frame_samples_.front().time_complete_ms > @@ -538,7 +579,7 @@ now_ms - encoded_frame_samples_.front().time_complete_ms); if (denom >= 1.0f) { avg_sent_bit_rate_bps_ = - static_cast(framesize_sum * 8 * 1000 / denom + 0.5f); + static_cast(framesize_sum * 8.0f * 1000.0f / denom + 0.5f); } else { avg_sent_bit_rate_bps_ = framesize_sum * 8; } @@ -589,17 +630,14 @@ // to avoid confusion later qm->codec_width = codec_width_; - qm->codec_height = codec_height_; + qm->codec_height = codec_height_; } - WEBRTC_TRACE(webrtc::kTraceDebug, - webrtc::kTraceVideoCoding, - id_, - "Resolution change from QM select: W = %d (%d), H = %d (%d), FR = %f", - qm->codec_width, codec_width_, - qm->codec_height, codec_height_, - qm->frame_rate); + LOG(LS_INFO) << "Media optimizer requests the video resolution to be changed " + "to " << qm->codec_width << " (" << codec_width_ << ") x " + << qm->codec_height << " (" << codec_height_ << ") @ " + << qm->frame_rate; // Update VPM with new target frame rate and frame size. // Note: use |qm->frame_rate| instead of |_incoming_frame_rate| for updating diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_optimization.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_optimization.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_optimization.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_optimization.h 2015-02-03 14:33:36.000000000 +0000 @@ -17,8 +17,8 @@ #include "webrtc/modules/video_coding/main/interface/video_coding.h" #include "webrtc/modules/video_coding/main/source/media_opt_util.h" #include "webrtc/modules/video_coding/main/source/qm_select.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { @@ -29,10 +29,9 @@ namespace media_optimization { -// TODO(andresp): Make thread safe. class MediaOptimization { public: - MediaOptimization(int32_t id, Clock* clock); + explicit MediaOptimization(Clock* clock); ~MediaOptimization(); // TODO(andresp): Can Reset and SetEncodingData be done at construction time @@ -105,63 +104,84 @@ struct EncodedFrameSample; typedef std::list FrameSampleList; - void UpdateIncomingFrameRate(); - void PurgeOldFrameSamples(int64_t now_ms); - void UpdateSentBitrate(int64_t now_ms); - void UpdateSentFramerate(); + void UpdateIncomingFrameRate() EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + void PurgeOldFrameSamples(int64_t now_ms) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + void UpdateSentBitrate(int64_t now_ms) EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + void UpdateSentFramerate() EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); // Computes new Quality Mode. - int32_t SelectQuality(VCMQMSettingsCallback* qmsettings_callback); + int32_t SelectQuality(VCMQMSettingsCallback* qmsettings_callback) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); // Verifies if QM settings differ from default, i.e. if an update is required. // Computes actual values, as will be sent to the encoder. bool QMUpdate(VCMResolutionScale* qm, - VCMQMSettingsCallback* qmsettings_callback); + VCMQMSettingsCallback* qmsettings_callback) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); // Checks if we should make a QM change. Return true if yes, false otherwise. - bool CheckStatusForQMchange(); + bool CheckStatusForQMchange() EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); - void ProcessIncomingFrameRate(int64_t now); + void ProcessIncomingFrameRate(int64_t now) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); // Checks conditions for suspending the video. The method compares // |target_bit_rate_| with the threshold values for suspension, and changes // the state of |video_suspended_| accordingly. - void CheckSuspendConditions(); + void CheckSuspendConditions() EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); - int32_t id_; - Clock* clock_; - int32_t max_bit_rate_; - VideoCodecType send_codec_type_; - uint16_t codec_width_; - uint16_t codec_height_; - uint16_t min_width_; - uint16_t min_height_; - float user_frame_rate_; - scoped_ptr frame_dropper_; - scoped_ptr loss_prot_logic_; - uint8_t fraction_lost_; - uint32_t send_statistics_[4]; - uint32_t send_statistics_zero_encode_; - int32_t max_payload_size_; - int target_bit_rate_; - float incoming_frame_rate_; - int64_t incoming_frame_times_[kFrameCountHistorySize]; - bool enable_qm_; - std::list encoded_frame_samples_; - uint32_t avg_sent_bit_rate_bps_; - uint32_t avg_sent_framerate_; - uint32_t key_frame_cnt_; - uint32_t delta_frame_cnt_; - scoped_ptr content_; - scoped_ptr qm_resolution_; - int64_t last_qm_update_time_; - int64_t last_change_time_; // Content/user triggered. - int num_layers_; - bool suspension_enabled_; - bool video_suspended_; - int suspension_threshold_bps_; - int suspension_window_bps_; - CPULoadState loadstate_; + void SetEncodingDataInternal(VideoCodecType send_codec_type, + int32_t max_bit_rate, + uint32_t frame_rate, + uint32_t bit_rate, + uint16_t width, + uint16_t height, + uint8_t divisor, + int num_temporal_layers, + int32_t mtu) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + uint32_t InputFrameRateInternal() EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + uint32_t SentFrameRateInternal() EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + // Protect all members. + scoped_ptr crit_sect_; + + Clock* clock_ GUARDED_BY(crit_sect_); + int32_t max_bit_rate_ GUARDED_BY(crit_sect_); + VideoCodecType send_codec_type_ GUARDED_BY(crit_sect_); + uint16_t codec_width_ GUARDED_BY(crit_sect_); + uint16_t codec_height_ GUARDED_BY(crit_sect_); + uint16_t min_width_ GUARDED_BY(crit_sect_); + uint16_t min_height_ GUARDED_BY(crit_sect_); + float user_frame_rate_ GUARDED_BY(crit_sect_); + scoped_ptr frame_dropper_ GUARDED_BY(crit_sect_); + scoped_ptr loss_prot_logic_ GUARDED_BY(crit_sect_); + uint8_t fraction_lost_ GUARDED_BY(crit_sect_); + uint32_t send_statistics_[4] GUARDED_BY(crit_sect_); + uint32_t send_statistics_zero_encode_ GUARDED_BY(crit_sect_); + int32_t max_payload_size_ GUARDED_BY(crit_sect_); + int target_bit_rate_ GUARDED_BY(crit_sect_); + float incoming_frame_rate_ GUARDED_BY(crit_sect_); + int64_t incoming_frame_times_[kFrameCountHistorySize] GUARDED_BY(crit_sect_); + bool enable_qm_ GUARDED_BY(crit_sect_); + std::list encoded_frame_samples_ GUARDED_BY(crit_sect_); + uint32_t avg_sent_bit_rate_bps_ GUARDED_BY(crit_sect_); + uint32_t avg_sent_framerate_ GUARDED_BY(crit_sect_); + uint32_t key_frame_cnt_ GUARDED_BY(crit_sect_); + uint32_t delta_frame_cnt_ GUARDED_BY(crit_sect_); + scoped_ptr content_ GUARDED_BY(crit_sect_); + scoped_ptr qm_resolution_ GUARDED_BY(crit_sect_); + int64_t last_qm_update_time_ GUARDED_BY(crit_sect_); + int64_t last_change_time_ GUARDED_BY(crit_sect_); // Content/user triggered. + int num_layers_ GUARDED_BY(crit_sect_); + bool suspension_enabled_ GUARDED_BY(crit_sect_); + bool video_suspended_ GUARDED_BY(crit_sect_); + int suspension_threshold_bps_ GUARDED_BY(crit_sect_); + int suspension_window_bps_ GUARDED_BY(crit_sect_); + CPULoadState loadstate_ GUARDED_BY(crit_sect_); }; } // namespace media_optimization } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_optimization_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_optimization_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_optimization_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_optimization_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -18,9 +18,6 @@ class TestMediaOptimization : public ::testing::Test { protected: enum { - kId = 4711 // Id number for the MediaOptimization class. - }; - enum { kSampleRate = 90000 // RTP timestamps per second. }; @@ -28,7 +25,7 @@ // a special case (e.g. frame rate in media optimization). TestMediaOptimization() : clock_(1000), - media_opt_(kId, &clock_), + media_opt_(&clock_), frame_time_ms_(33), next_timestamp_(0) {} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_opt_util.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_opt_util.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_opt_util.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_opt_util.cc 2015-02-03 14:33:36.000000000 +0000 @@ -837,7 +837,7 @@ case kNoFilter: break; case kAvgFilter: - filtered_loss = static_cast (_lossPr255.Value() + 0.5); + filtered_loss = static_cast(_lossPr255.filtered() + 0.5); break; case kMaxFilter: filtered_loss = MaxFilteredLossPr(nowMs); @@ -907,8 +907,8 @@ _currentParameters.keyFrameSize = _keyFrameSize; _currentParameters.fecRateDelta = _fecRateDelta; _currentParameters.fecRateKey = _fecRateKey; - _currentParameters.packetsPerFrame = _packetsPerFrame.Value(); - _currentParameters.packetsPerFrameKey = _packetsPerFrameKey.Value(); + _currentParameters.packetsPerFrame = _packetsPerFrame.filtered(); + _currentParameters.packetsPerFrameKey = _packetsPerFrameKey.filtered(); _currentParameters.residualPacketLossFec = _residualPacketLossFec; _currentParameters.codecWidth = _codecWidth; _currentParameters.codecHeight = _codecHeight; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_opt_util.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_opt_util.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_opt_util.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/media_opt_util.h 2015-02-03 14:33:36.000000000 +0000 @@ -14,9 +14,9 @@ #include #include +#include "webrtc/base/exp_filter.h" #include "webrtc/modules/video_coding/main/source/internal_defines.h" #include "webrtc/modules/video_coding/main/source/qm_select.h" -#include "webrtc/modules/video_coding/utility/include/exp_filter.h" #include "webrtc/system_wrappers/interface/trace.h" #include "webrtc/typedefs.h" @@ -367,27 +367,27 @@ // Sets the available loss protection methods. void UpdateMaxLossHistory(uint8_t lossPr255, int64_t now); uint8_t MaxFilteredLossPr(int64_t nowMs) const; - VCMProtectionMethod* _selectedMethod; - VCMProtectionParameters _currentParameters; - uint32_t _rtt; - float _lossPr; - float _bitRate; - float _frameRate; - float _keyFrameSize; - uint8_t _fecRateKey; - uint8_t _fecRateDelta; - int64_t _lastPrUpdateT; - int64_t _lastPacketPerFrameUpdateT; - int64_t _lastPacketPerFrameUpdateTKey; - VCMExpFilter _lossPr255; - VCMLossProbabilitySample _lossPrHistory[kLossPrHistorySize]; - uint8_t _shortMaxLossPr255; - VCMExpFilter _packetsPerFrame; - VCMExpFilter _packetsPerFrameKey; - float _residualPacketLossFec; - uint16_t _codecWidth; - uint16_t _codecHeight; - int _numLayers; + VCMProtectionMethod* _selectedMethod; + VCMProtectionParameters _currentParameters; + uint32_t _rtt; + float _lossPr; + float _bitRate; + float _frameRate; + float _keyFrameSize; + uint8_t _fecRateKey; + uint8_t _fecRateDelta; + int64_t _lastPrUpdateT; + int64_t _lastPacketPerFrameUpdateT; + int64_t _lastPacketPerFrameUpdateTKey; + rtc::ExpFilter _lossPr255; + VCMLossProbabilitySample _lossPrHistory[kLossPrHistorySize]; + uint8_t _shortMaxLossPr255; + rtc::ExpFilter _packetsPerFrame; + rtc::ExpFilter _packetsPerFrameKey; + float _residualPacketLossFec; + uint16_t _codecWidth; + uint16_t _codecHeight; + int _numLayers; }; } // namespace media_optimization diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/packet.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/packet.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/packet.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/packet.cc 2015-02-03 14:33:36.000000000 +0000 @@ -20,6 +20,7 @@ : payloadType(0), timestamp(0), + ntp_time_ms_(0), seqNum(0), dataPtr(NULL), sizeBytes(0), @@ -39,6 +40,7 @@ const WebRtcRTPHeader& rtpHeader) : payloadType(rtpHeader.header.payloadType), timestamp(rtpHeader.header.timestamp), + ntp_time_ms_(rtpHeader.ntp_time_ms), seqNum(rtpHeader.header.sequenceNumber), dataPtr(ptr), sizeBytes(size), @@ -59,6 +61,7 @@ VCMPacket::VCMPacket(const uint8_t* ptr, uint32_t size, uint16_t seq, uint32_t ts, bool mBit) : payloadType(0), timestamp(ts), + ntp_time_ms_(0), seqNum(seq), dataPtr(ptr), sizeBytes(size), @@ -77,6 +80,7 @@ void VCMPacket::Reset() { payloadType = 0; timestamp = 0; + ntp_time_ms_ = 0; seqNum = 0; dataPtr = NULL; sizeBytes = 0; @@ -93,42 +97,41 @@ void VCMPacket::CopyCodecSpecifics(const RTPVideoHeader& videoHeader) { switch (videoHeader.codec) { - case kRtpVideoVp8: { + case kRtpVideoVp8: // Handle all packets within a frame as depending on the previous packet // TODO(holmer): This should be changed to make fragments independent // when the VP8 RTP receiver supports fragments. if (isFirstPacket && markerBit) - completeNALU = kNaluComplete; + completeNALU = kNaluComplete; else if (isFirstPacket) - completeNALU = kNaluStart; + completeNALU = kNaluStart; else if (markerBit) - completeNALU = kNaluEnd; + completeNALU = kNaluEnd; else - completeNALU = kNaluIncomplete; + completeNALU = kNaluIncomplete; codec = kVideoCodecVP8; - break; - } - case kRtpVideoH264: { + return; + case kRtpVideoH264: isFirstPacket = videoHeader.isFirstPacket; if (isFirstPacket) { insertStartCode = true; } if (videoHeader.codecHeader.H264.single_nalu) { - completeNALU = kNaluComplete; - } else if (isFirstPacket) - completeNALU = kNaluStart; - else if (markerBit) - completeNALU = kNaluEnd; - else - completeNALU = kNaluIncomplete; + completeNALU = kNaluComplete; + } else if (isFirstPacket) { + completeNALU = kNaluStart; + } else if (markerBit) { + completeNALU = kNaluEnd; + } else { + completeNALU = kNaluIncomplete; + } codec = kVideoCodecH264; - break; - } - default: { + return; + case kRtpVideoGeneric: + case kRtpVideoNone: codec = kVideoCodecUnknown; - break; - } + return; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/packet.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/packet.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/packet.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/packet.h 2015-02-03 14:33:36.000000000 +0000 @@ -33,6 +33,8 @@ uint8_t payloadType; uint32_t timestamp; + // NTP time of the capture time in local timebase in milliseconds. + int64_t ntp_time_ms_; uint16_t seqNum; const uint8_t* dataPtr; uint32_t sizeBytes; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver.cc 2015-02-03 14:33:36.000000000 +0000 @@ -12,11 +12,13 @@ #include +#include + #include "webrtc/modules/video_coding/main/source/encoded_frame.h" #include "webrtc/modules/video_coding/main/source/internal_defines.h" #include "webrtc/modules/video_coding/main/source/media_opt_util.h" #include "webrtc/system_wrappers/interface/clock.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/trace_event.h" namespace webrtc { @@ -26,15 +28,11 @@ VCMReceiver::VCMReceiver(VCMTiming* timing, Clock* clock, EventFactory* event_factory, - int32_t vcm_id, - int32_t receiver_id, bool master) : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), - vcm_id_(vcm_id), clock_(clock), - receiver_id_(receiver_id), master_(master), - jitter_buffer_(clock_, event_factory, vcm_id, receiver_id, master), + jitter_buffer_(clock_, event_factory), timing_(timing), render_wait_event_(event_factory->CreateEvent()), state_(kPassive), @@ -79,13 +77,6 @@ int32_t VCMReceiver::InsertPacket(const VCMPacket& packet, uint16_t frame_width, uint16_t frame_height) { - if (packet.frameType == kVideoFrameKey) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, receiver_id_), - "Inserting key frame packet seqnum=%u, timestamp=%u", - packet.seqNum, packet.timestamp); - } - // Insert the packet into the jitter buffer. The packet can either be empty or // contain media at this point. bool retransmitted = false; @@ -96,10 +87,6 @@ } else if (ret == kFlushIndicator) { return VCM_FLUSH_INDICATOR; } else if (ret < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, receiver_id_), - "Error inserting packet seqnum=%u, timestamp=%u", - packet.seqNum, packet.timestamp); return VCM_JITTER_BUFFER_ERROR; } if (ret == kCompleteSession && !retransmitted) { @@ -108,15 +95,6 @@ // delay within the jitter estimate. timing_->IncomingTimestamp(packet.timestamp, clock_->TimeInMilliseconds()); } - if (master_) { - // Only trace the primary receiver to make it possible to parse and plot - // the trace file. - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, receiver_id_), - "Packet seqnum=%u timestamp=%u inserted at %u", - packet.seqNum, packet.timestamp, - MaskWord64ToUWord32(clock_->TimeInMilliseconds())); - } return VCM_OK; } @@ -161,19 +139,16 @@ timing_error = true; } else if (std::abs(static_cast(next_render_time_ms - now_ms)) > max_video_delay_ms_) { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, receiver_id_), - "This frame is out of our delay bounds, resetting jitter " - "buffer: %d > %d", - std::abs(static_cast(next_render_time_ms - now_ms)), - max_video_delay_ms_); + int frame_delay = std::abs(static_cast(next_render_time_ms - now_ms)); + LOG(LS_WARNING) << "A frame about to be decoded is out of the configured " + << "delay bounds (" << frame_delay << " > " + << max_video_delay_ms_ + << "). Resetting the video jitter buffer."; timing_error = true; } else if (static_cast(timing_->TargetVideoDelay()) > max_video_delay_ms_) { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, receiver_id_), - "More than %u ms target delay. Flushing jitter buffer and" - "resetting timing.", max_video_delay_ms_); + LOG(LS_WARNING) << "The video target delay has grown larger than " + << max_video_delay_ms_ << " ms. Resetting jitter buffer."; timing_error = true; } @@ -284,10 +259,7 @@ bool request_key_frame = false; uint16_t* internal_nack_list = jitter_buffer_.GetNackList( nack_list_length, &request_key_frame); - if (*nack_list_length > size) { - *nack_list_length = 0; - return kNackNeedMoreMemory; - } + assert(*nack_list_length <= size); if (internal_nack_list != NULL && *nack_list_length > 0) { memcpy(nack_list, internal_nack_list, *nack_list_length * sizeof(uint16_t)); } @@ -378,10 +350,7 @@ void VCMReceiver::UpdateState(VCMReceiverState new_state) { CriticalSectionScoped cs(crit_sect_); assert(!(state_ == kPassive && new_state == kWaitForPrimaryDecode)); - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, receiver_id_), - "Receiver changing state: %d to %d", - state_, new_state); + LOG(LS_INFO) << "Receiver changing state: " << state_ << " to " << new_state; state_ = new_state; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver.h 2015-02-03 14:33:36.000000000 +0000 @@ -25,7 +25,6 @@ enum VCMNackStatus { kNackOk, - kNackNeedMoreMemory, kNackKeyFrameRequest }; @@ -40,8 +39,6 @@ VCMReceiver(VCMTiming* timing, Clock* clock, EventFactory* event_factory, - int32_t vcm_id, - int32_t receiver_id, bool master); ~VCMReceiver(); @@ -97,9 +94,7 @@ static int32_t GenerateReceiverId(); CriticalSectionWrapper* crit_sect_; - int32_t vcm_id_; Clock* clock_; - int32_t receiver_id_; bool master_; VCMJitterBuffer jitter_buffer_; VCMTiming* timing_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -31,7 +31,7 @@ TestVCMReceiver() : clock_(new SimulatedClock(0)), timing_(clock_.get()), - receiver_(&timing_, clock_.get(), &event_factory_, 1, 1, true) { + receiver_(&timing_, clock_.get(), &event_factory_, true) { stream_generator_.reset(new StreamGenerator(0, 0, clock_->TimeInMilliseconds())); memset(data_buffer_, 0, kDataBufferSize); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/rtt_filter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/rtt_filter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/rtt_filter.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/rtt_filter.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,7 +10,6 @@ #include "webrtc/modules/video_coding/main/source/internal_defines.h" #include "webrtc/modules/video_coding/main/source/rtt_filter.h" -#include "webrtc/system_wrappers/interface/trace.h" #include #include @@ -18,15 +17,11 @@ namespace webrtc { -VCMRttFilter::VCMRttFilter(int32_t vcmId, int32_t receiverId) -: -_vcmId(vcmId), -_receiverId(receiverId), -_filtFactMax(35), -_jumpStdDevs(2.5), -_driftStdDevs(3.5), -_detectThreshold(kMaxDriftJumpCount) -{ +VCMRttFilter::VCMRttFilter() + : _filtFactMax(35), + _jumpStdDevs(2.5), + _driftStdDevs(3.5), + _detectThreshold(kMaxDriftJumpCount) { Reset(); } @@ -105,9 +100,6 @@ _avgRtt = oldAvg; _varRtt = oldVar; } - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "RttFilter Update: sample=%u avgRtt=%f varRtt=%f maxRtt=%u", - rttMs, _avgRtt, _varRtt, _maxRtt); } bool @@ -141,8 +133,6 @@ ShortRttFilter(_jumpBuf, abs(_jumpCount)); _filtFactCount = _detectThreshold + 1; _jumpCount = 0; - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Detected an RTT jump"); } else { @@ -174,8 +164,6 @@ ShortRttFilter(_driftBuf, _driftCount); _filtFactCount = _detectThreshold + 1; _driftCount = 0; - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Detected an RTT drift"); } } else diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/rtt_filter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/rtt_filter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/rtt_filter.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/rtt_filter.h 2015-02-03 14:33:36.000000000 +0000 @@ -19,7 +19,7 @@ class VCMRttFilter { public: - VCMRttFilter(int32_t vcmId = 0, int32_t receiverId = 0); + VCMRttFilter(); VCMRttFilter& operator=(const VCMRttFilter& rhs); @@ -48,8 +48,6 @@ // Computes the short time average and maximum of the vector buf. void ShortRttFilter(uint32_t* buf, uint32_t length); - int32_t _vcmId; - int32_t _receiverId; bool _gotNonZeroUpdate; double _avgRtt; double _varRtt; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/session_info.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/session_info.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/session_info.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/session_info.cc 2015-02-03 14:33:36.000000000 +0000 @@ -9,11 +9,12 @@ */ #include "webrtc/modules/video_coding/main/source/session_info.h" + #include "webrtc/modules/video_coding/main/source/packet.h" -#include "webrtc/modules/rtp_rtcp/source/rtp_format_h264.h" +#include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { - +namespace { // Used in determining whether a frame is decodable. enum {kRttThreshold = 100}; // Not decodable if Rtt is lower than this. @@ -22,6 +23,11 @@ static const float kLowPacketPercentageThreshold = 0.2f; static const float kHighPacketPercentageThreshold = 0.8f; +uint16_t BufferToUWord16(const uint8_t* dataBuffer) { + return (dataBuffer[0] << 8) | dataBuffer[1]; +} +} // namespace + VCMSessionInfo::VCMSessionInfo() : session_nack_(false), complete_(false), @@ -115,106 +121,78 @@ return packets_.size(); } -void VCMSessionInfo::CopyPacket(uint8_t* dst, const uint8_t* src, size_t len) { - memcpy(dst, src, len); -} - -void VCMSessionInfo::CopyWithStartCode(uint8_t* dst, const uint8_t* src, size_t len) { - // H.264 Start Code is 2 or more bytes of 0 followed by 1 byte of 1. - memset(dst, 0, RtpFormatH264::kStartCodeSize-1); - dst[RtpFormatH264::kStartCodeSize-1] = 1; - CopyPacket(dst + RtpFormatH264::kStartCodeSize, src, len); -} - int VCMSessionInfo::InsertBuffer(uint8_t* frame_buffer, PacketIterator packet_it) { VCMPacket& packet = *packet_it; + PacketIterator it; - // Advance to the offset into the frame buffer for this packet. - for (PacketIterator it = packets_.begin(); it != packet_it; ++it) - frame_buffer += (*it).sizeBytes; - - if (packet.codec == kVideoCodecH264) { - // Calculate extra packet size needed for adding start codes, - // and removing fragmentation and aggregation unit headers. - size_t nalu_size; - size_t all_nalu_size = 0; - const uint8_t* nalu_ptr = packet.dataPtr; - uint8_t nal_header = *nalu_ptr; - uint8_t fu_header; - switch (nal_header & RtpFormatH264::kTypeMask) { - case RtpFormatH264::kFuA: - fu_header = nalu_ptr[RtpFormatH264::kFuAHeaderOffset]; - if (fu_header & RtpFormatH264::kFragStartBit) { - nal_header &= ~RtpFormatH264::kTypeMask; // Keep F/NRI bits - nal_header |= fu_header & RtpFormatH264::kTypeMask; // Keep NAL type - packet.sizeBytes -= RtpFormatH264::kFuAHeaderOffset; - packet.dataPtr += RtpFormatH264::kFuAHeaderOffset; - ShiftSubsequentPackets(packet_it, packet.sizeBytes + - RtpFormatH264::kStartCodeSize); - CopyWithStartCode(frame_buffer, packet.dataPtr, packet.sizeBytes); - frame_buffer[RtpFormatH264::kStartCodeSize] = nal_header; - packet.sizeBytes += RtpFormatH264::kStartCodeSize; - packet.dataPtr = frame_buffer; - packet.completeNALU = kNaluStart; - } else { - packet.sizeBytes -= RtpFormatH264::kFuAHeaderOffset + - RtpFormatH264::kFuAHeaderSize; - packet.dataPtr += RtpFormatH264::kFuAHeaderOffset + - RtpFormatH264::kFuAHeaderSize; - ShiftSubsequentPackets(packet_it, packet.sizeBytes); - CopyPacket(frame_buffer, packet.dataPtr, packet.sizeBytes); - packet.dataPtr = frame_buffer; - if (fu_header & RtpFormatH264::kFragEndBit) { - packet.completeNALU = kNaluEnd; - } else { - packet.completeNALU = kNaluIncomplete; - } - } - break; - case RtpFormatH264::kStapA: - packet.sizeBytes -= RtpFormatH264::kStapAHeaderOffset; - packet.dataPtr += RtpFormatH264::kStapAHeaderOffset; - for (nalu_ptr = packet.dataPtr; - nalu_ptr < packet.dataPtr + packet.sizeBytes; - nalu_ptr += nalu_size + RtpFormatH264::kAggUnitLengthSize) { - nalu_size = (nalu_ptr[0] << 8) + nalu_ptr[1]; - all_nalu_size += nalu_size + RtpFormatH264::kStartCodeSize; - } - if (nalu_ptr > packet.dataPtr + packet.sizeBytes) { - // malformed packet - packet.completeNALU = kNaluIncomplete; - return -1; - } - ShiftSubsequentPackets(packet_it, all_nalu_size); - for (nalu_ptr = packet.dataPtr; - nalu_ptr < packet.dataPtr + packet.sizeBytes; - nalu_ptr += nalu_size + RtpFormatH264::kAggUnitLengthSize) { - nalu_size = (nalu_ptr[0] << 8) + nalu_ptr[1]; - CopyWithStartCode(frame_buffer, nalu_ptr+2, nalu_size); - frame_buffer += nalu_size + RtpFormatH264::kStartCodeSize; - } - packet.sizeBytes = all_nalu_size; - packet.dataPtr = frame_buffer - all_nalu_size; - packet.completeNALU = kNaluComplete; - break; - default: - ShiftSubsequentPackets(packet_it, packet.sizeBytes + - RtpFormatH264::kStartCodeSize); - CopyWithStartCode(frame_buffer, packet.dataPtr, packet.sizeBytes); - packet.sizeBytes += RtpFormatH264::kStartCodeSize; - packet.dataPtr = frame_buffer; - packet.completeNALU = kNaluComplete; - break; - } // switch nal_type - } else { // not H.264 - ShiftSubsequentPackets(packet_it, packet.sizeBytes); - CopyPacket(frame_buffer, packet.dataPtr, packet.sizeBytes); - packet.dataPtr = frame_buffer; - } + // Calculate the offset into the frame buffer for this packet. + int offset = 0; + for (it = packets_.begin(); it != packet_it; ++it) + offset += (*it).sizeBytes; + + // Set the data pointer to pointing to the start of this packet in the + // frame buffer. + const uint8_t* packet_buffer = packet.dataPtr; + packet.dataPtr = frame_buffer + offset; + + // We handle H.264 STAP-A packets in a special way as we need to remove the + // two length bytes between each NAL unit, and potentially add start codes. + const size_t kH264NALHeaderLengthInBytes = 1; + const size_t kLengthFieldLength = 2; + if (packet.codecSpecificHeader.codec == kRtpVideoH264 && + packet.codecSpecificHeader.codecHeader.H264.stap_a) { + size_t required_length = 0; + const uint8_t* nalu_ptr = packet_buffer + kH264NALHeaderLengthInBytes; + while (nalu_ptr < packet_buffer + packet.sizeBytes) { + uint32_t length = BufferToUWord16(nalu_ptr); + required_length += + length + (packet.insertStartCode ? kH264StartCodeLengthBytes : 0); + nalu_ptr += kLengthFieldLength + length; + } + ShiftSubsequentPackets(packet_it, required_length); + nalu_ptr = packet_buffer + kH264NALHeaderLengthInBytes; + uint8_t* frame_buffer_ptr = frame_buffer + offset; + while (nalu_ptr < packet_buffer + packet.sizeBytes) { + uint32_t length = BufferToUWord16(nalu_ptr); + nalu_ptr += kLengthFieldLength; + frame_buffer_ptr += Insert(nalu_ptr, + length, + packet.insertStartCode, + const_cast(frame_buffer_ptr)); + nalu_ptr += length; + } + packet.sizeBytes = required_length; + return packet.sizeBytes; + } + ShiftSubsequentPackets( + packet_it, + packet.sizeBytes + + (packet.insertStartCode ? kH264StartCodeLengthBytes : 0)); + + packet.sizeBytes = Insert(packet_buffer, + packet.sizeBytes, + packet.insertStartCode, + const_cast(packet.dataPtr)); return packet.sizeBytes; } +size_t VCMSessionInfo::Insert(const uint8_t* buffer, + size_t length, + bool insert_start_code, + uint8_t* frame_buffer) { + if (insert_start_code) { + const unsigned char startCode[] = {0, 0, 0, 1}; + memcpy(frame_buffer, startCode, kH264StartCodeLengthBytes); + } + memcpy(frame_buffer + (insert_start_code ? kH264StartCodeLengthBytes : 0), + buffer, + length); + length += (insert_start_code ? kH264StartCodeLengthBytes : 0); + + return length; +} + void VCMSessionInfo::ShiftSubsequentPackets(PacketIterator it, int steps_to_shift) { ++it; @@ -469,6 +447,7 @@ } if (packets_.size() == kMaxPacketsInSession) { + LOG(LS_ERROR) << "Max number of packets per frame has been reached."; return -1; } @@ -476,43 +455,29 @@ // order and insert it. Loop over the list in reverse order. ReversePacketIterator rit = packets_.rbegin(); for (; rit != packets_.rend(); ++rit) - if (LatestSequenceNumber(packet.seqNum, (*rit).seqNum) == packet.seqNum) { + if (LatestSequenceNumber(packet.seqNum, (*rit).seqNum) == packet.seqNum) break; - } // Check for duplicate packets. if (rit != packets_.rend() && - (*rit).seqNum == packet.seqNum && (*rit).sizeBytes > 0) { + (*rit).seqNum == packet.seqNum && (*rit).sizeBytes > 0) return -2; - } - - PacketIterator packet_list_it; if (packet.codec == kVideoCodecH264) { - // H.264 can have leading or trailing non-VCL (Video Coding Layer) - // NALUs, such as SPS/PPS/SEI and others. Also, the RTP marker bit is - // not reliable for the last packet of a frame (RFC 6184 5.1 - "Decoders - // [] MUST NOT rely on this property"), so allow out-of-order packets to - // update the first and last seq# range. Also mark as a key frame if - // any packet is of that type. - if (frame_type_ != kVideoFrameKey) { - frame_type_ = packet.frameType; - } - if ((!HaveFirstPacket() || - IsNewerSequenceNumber(first_packet_seq_num_, packet.seqNum)) && - packet.isFirstPacket) { + frame_type_ = packet.frameType; + if (packet.isFirstPacket && + (first_packet_seq_num_ == -1 || + IsNewerSequenceNumber(first_packet_seq_num_, packet.seqNum))) { first_packet_seq_num_ = packet.seqNum; } - // Note: the code does *not* currently handle the Marker bit being totally - // absent from a frame. It does not, however, depend on it being on the last - // packet of the 'frame'/'session'. - if ((!HaveLastPacket() && packet.markerBit) || - (HaveLastPacket() && - IsNewerSequenceNumber(packet.seqNum, last_packet_seq_num_))) { + if (packet.markerBit && + (last_packet_seq_num_ == -1 || + IsNewerSequenceNumber(packet.seqNum, last_packet_seq_num_))) { last_packet_seq_num_ = packet.seqNum; } } else { - // Only insert media packets between first and last packets (when available). + // Only insert media packets between first and last packets (when + // available). // Placing check here, as to properly account for duplicate packets. // Check if this is first packet (only valid for some codecs) // Should only be set for one packet per session. @@ -523,8 +488,8 @@ first_packet_seq_num_ = static_cast(packet.seqNum); } else if (first_packet_seq_num_ != -1 && !IsNewerSequenceNumber(packet.seqNum, first_packet_seq_num_)) { - //LOG(LS_WARNING) << "Received packet with a sequence number which is out " - // "of frame boundaries"; + LOG(LS_WARNING) << "Received packet with a sequence number which is out " + "of frame boundaries"; return -3; } else if (frame_type_ == kFrameEmpty && packet.frameType != kFrameEmpty) { // Update the frame type with the type of the first media packet. @@ -537,20 +502,17 @@ last_packet_seq_num_ = static_cast(packet.seqNum); } else if (last_packet_seq_num_ != -1 && IsNewerSequenceNumber(packet.seqNum, last_packet_seq_num_)) { - //LOG(LS_WARNING) << "Received packet with a sequence number which is out " - // "of frame boundaries"; + LOG(LS_WARNING) << "Received packet with a sequence number which is out " + "of frame boundaries"; return -3; } } // The insert operation invalidates the iterator |rit|. - packet_list_it = packets_.insert(rit.base(), packet); + PacketIterator packet_list_it = packets_.insert(rit.base(), packet); int returnLength = InsertBuffer(frame_buffer, packet_list_it); UpdateCompleteSession(); - // We call MakeDecodable() before decoding, which removes packets after a loss - // (and which means h.264 mode 1 frames with a loss in the first packet will be - // totally removed) if (decode_error_mode == kWithErrors) decodable_ = true; else if (decode_error_mode == kSelectiveErrors) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/session_info.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/session_info.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/session_info.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/session_info.h 2015-02-03 14:33:36.000000000 +0000 @@ -114,10 +114,12 @@ PacketIterator FindPartitionEnd(PacketIterator it) const; static bool InSequence(const PacketIterator& it, const PacketIterator& prev_it); - void CopyPacket(uint8_t* dst, const uint8_t* src, size_t len); - void CopyWithStartCode(uint8_t* dst, const uint8_t* src, size_t len); int InsertBuffer(uint8_t* frame_buffer, PacketIterator packetIterator); + size_t Insert(const uint8_t* buffer, + size_t length, + bool insert_start_code, + uint8_t* frame_buffer); void ShiftSubsequentPackets(PacketIterator it, int steps_to_shift); PacketIterator FindNaluEnd(PacketIterator packet_iter) const; // Deletes the data of all packets between |start| and |end|, inclusively. @@ -149,7 +151,6 @@ bool complete_; bool decodable_; webrtc::FrameType frame_type_; - bool previous_frame_loss_; // Packets in this frame. PacketList packets_; int empty_seq_num_low_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/timestamp_extrapolator.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/timestamp_extrapolator.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/timestamp_extrapolator.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/timestamp_extrapolator.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,248 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/video_coding/main/source/internal_defines.h" -#include "webrtc/modules/video_coding/main/source/timestamp_extrapolator.h" -#include "webrtc/system_wrappers/interface/clock.h" -#include "webrtc/system_wrappers/interface/trace.h" - -namespace webrtc { - -VCMTimestampExtrapolator::VCMTimestampExtrapolator(Clock* clock, - int32_t vcmId, - int32_t id) -: -_rwLock(RWLockWrapper::CreateRWLock()), -_vcmId(vcmId), -_id(id), -_clock(clock), -_startMs(0), -_firstTimestamp(0), -_wrapArounds(0), -_prevUnwrappedTimestamp(-1), -_prevWrapTimestamp(-1), -_lambda(1), -_firstAfterReset(true), -_packetCount(0), -_startUpFilterDelayInPackets(2), -_detectorAccumulatorPos(0), -_detectorAccumulatorNeg(0), -_alarmThreshold(60e3), -_accDrift(6600), // in timestamp ticks, i.e. 15 ms -_accMaxError(7000), -_P11(1e10) -{ - Reset(); -} - -VCMTimestampExtrapolator::~VCMTimestampExtrapolator() -{ - delete _rwLock; -} - -void -VCMTimestampExtrapolator::Reset() -{ - WriteLockScoped wl(*_rwLock); - _startMs = _clock->TimeInMilliseconds(); - _prevMs = _startMs; - _firstTimestamp = 0; - _w[0] = 90.0; - _w[1] = 0; - _pp[0][0] = 1; - _pp[1][1] = _P11; - _pp[0][1] = _pp[1][0] = 0; - _firstAfterReset = true; - _prevUnwrappedTimestamp = -1; - _prevWrapTimestamp = -1; - _wrapArounds = 0; - _packetCount = 0; - _detectorAccumulatorPos = 0; - _detectorAccumulatorNeg = 0; -} - -void -VCMTimestampExtrapolator::Update(int64_t tMs, uint32_t ts90khz, bool trace) -{ - - _rwLock->AcquireLockExclusive(); - if (tMs - _prevMs > 10e3) - { - // Ten seconds without a complete frame. - // Reset the extrapolator - _rwLock->ReleaseLockExclusive(); - Reset(); - _rwLock->AcquireLockExclusive(); - } - else - { - _prevMs = tMs; - } - - // Remove offset to prevent badly scaled matrices - tMs -= _startMs; - - CheckForWrapArounds(ts90khz); - - int64_t unwrapped_ts90khz = static_cast(ts90khz) + - _wrapArounds * ((static_cast(1) << 32) - 1); - - if (_prevUnwrappedTimestamp >= 0 && - unwrapped_ts90khz < _prevUnwrappedTimestamp) - { - // Drop reordered frames. - _rwLock->ReleaseLockExclusive(); - return; - } - - if (_firstAfterReset) - { - // Make an initial guess of the offset, - // should be almost correct since tMs - _startMs - // should about zero at this time. - _w[1] = -_w[0] * tMs; - _firstTimestamp = unwrapped_ts90khz; - _firstAfterReset = false; - } - - double residual = - (static_cast(unwrapped_ts90khz) - _firstTimestamp) - - static_cast(tMs) * _w[0] - _w[1]; - if (DelayChangeDetection(residual, trace) && - _packetCount >= _startUpFilterDelayInPackets) - { - // A sudden change of average network delay has been detected. - // Force the filter to adjust its offset parameter by changing - // the offset uncertainty. Don't do this during startup. - _pp[1][1] = _P11; - } - //T = [t(k) 1]'; - //that = T'*w; - //K = P*T/(lambda + T'*P*T); - double K[2]; - K[0] = _pp[0][0] * tMs + _pp[0][1]; - K[1] = _pp[1][0] * tMs + _pp[1][1]; - double TPT = _lambda + tMs * K[0] + K[1]; - K[0] /= TPT; - K[1] /= TPT; - //w = w + K*(ts(k) - that); - _w[0] = _w[0] + K[0] * residual; - _w[1] = _w[1] + K[1] * residual; - //P = 1/lambda*(P - K*T'*P); - double p00 = 1 / _lambda * (_pp[0][0] - (K[0] * tMs * _pp[0][0] + K[0] * _pp[1][0])); - double p01 = 1 / _lambda * (_pp[0][1] - (K[0] * tMs * _pp[0][1] + K[0] * _pp[1][1])); - _pp[1][0] = 1 / _lambda * (_pp[1][0] - (K[1] * tMs * _pp[0][0] + K[1] * _pp[1][0])); - _pp[1][1] = 1 / _lambda * (_pp[1][1] - (K[1] * tMs * _pp[0][1] + K[1] * _pp[1][1])); - _pp[0][0] = p00; - _pp[0][1] = p01; - _prevUnwrappedTimestamp = unwrapped_ts90khz; - if (_packetCount < _startUpFilterDelayInPackets) - { - _packetCount++; - } - if (trace) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _id), "w[0]=%f w[1]=%f ts=%u tMs=%u", _w[0], _w[1], ts90khz, tMs); - } - _rwLock->ReleaseLockExclusive(); -} - -int64_t -VCMTimestampExtrapolator::ExtrapolateLocalTime(uint32_t timestamp90khz) -{ - ReadLockScoped rl(*_rwLock); - int64_t localTimeMs = 0; - CheckForWrapArounds(timestamp90khz); - double unwrapped_ts90khz = static_cast(timestamp90khz) + - _wrapArounds * ((static_cast(1) << 32) - 1); - if (_packetCount == 0) - { - localTimeMs = -1; - } - else if (_packetCount < _startUpFilterDelayInPackets) - { - localTimeMs = _prevMs + static_cast( - static_cast(unwrapped_ts90khz - _prevUnwrappedTimestamp) / - 90.0 + 0.5); - } - else - { - if (_w[0] < 1e-3) - { - localTimeMs = _startMs; - } - else - { - double timestampDiff = unwrapped_ts90khz - - static_cast(_firstTimestamp); - localTimeMs = static_cast( - static_cast(_startMs) + (timestampDiff - _w[1]) / - _w[0] + 0.5); - } - } - return localTimeMs; -} - -// Investigates if the timestamp clock has overflowed since the last timestamp and -// keeps track of the number of wrap arounds since reset. -void -VCMTimestampExtrapolator::CheckForWrapArounds(uint32_t ts90khz) -{ - if (_prevWrapTimestamp == -1) - { - _prevWrapTimestamp = ts90khz; - return; - } - if (ts90khz < _prevWrapTimestamp) - { - // This difference will probably be less than -2^31 if we have had a wrap around - // (e.g. timestamp = 1, _previousTimestamp = 2^32 - 1). Since it is casted to a Word32, - // it should be positive. - if (static_cast(ts90khz - _prevWrapTimestamp) > 0) - { - // Forward wrap around - _wrapArounds++; - } - } - // This difference will probably be less than -2^31 if we have had a backward wrap around. - // Since it is casted to a Word32, it should be positive. - else if (static_cast(_prevWrapTimestamp - ts90khz) > 0) - { - // Backward wrap around - _wrapArounds--; - } - _prevWrapTimestamp = ts90khz; -} - -bool -VCMTimestampExtrapolator::DelayChangeDetection(double error, bool trace) -{ - // CUSUM detection of sudden delay changes - error = (error > 0) ? VCM_MIN(error, _accMaxError) : VCM_MAX(error, -_accMaxError); - _detectorAccumulatorPos = VCM_MAX(_detectorAccumulatorPos + error - _accDrift, (double)0); - _detectorAccumulatorNeg = VCM_MIN(_detectorAccumulatorNeg + error + _accDrift, (double)0); - if (_detectorAccumulatorPos > _alarmThreshold || _detectorAccumulatorNeg < -_alarmThreshold) - { - // Alarm - if (trace) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _id), "g1=%f g2=%f alarm=1", _detectorAccumulatorPos, _detectorAccumulatorNeg); - } - _detectorAccumulatorPos = _detectorAccumulatorNeg = 0; - return true; - } - if (trace) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _id), "g1=%f g2=%f alarm=0", _detectorAccumulatorPos, _detectorAccumulatorNeg); - } - return false; -} - -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/timestamp_extrapolator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/timestamp_extrapolator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/timestamp_extrapolator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/timestamp_extrapolator.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_TIMESTAMP_EXTRAPOLATOR_H_ -#define WEBRTC_MODULES_VIDEO_CODING_TIMESTAMP_EXTRAPOLATOR_H_ - -#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" -#include "webrtc/typedefs.h" - -namespace webrtc -{ - -class Clock; - -class VCMTimestampExtrapolator -{ -public: - VCMTimestampExtrapolator(Clock* clock, - int32_t vcmId = 0, - int32_t receiverId = 0); - ~VCMTimestampExtrapolator(); - void Update(int64_t tMs, uint32_t ts90khz, bool trace = true); - int64_t ExtrapolateLocalTime(uint32_t timestamp90khz); - void Reset(); - -private: - void CheckForWrapArounds(uint32_t ts90khz); - bool DelayChangeDetection(double error, bool trace = true); - RWLockWrapper* _rwLock; - int32_t _vcmId; - int32_t _id; - Clock* _clock; - double _w[2]; - double _pp[2][2]; - int64_t _startMs; - int64_t _prevMs; - uint32_t _firstTimestamp; - int32_t _wrapArounds; - int64_t _prevUnwrappedTimestamp; - int64_t _prevWrapTimestamp; - const double _lambda; - bool _firstAfterReset; - uint32_t _packetCount; - const uint32_t _startUpFilterDelayInPackets; - - double _detectorAccumulatorPos; - double _detectorAccumulatorNeg; - const double _alarmThreshold; - const double _accDrift; - const double _accMaxError; - const double _P11; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_TIMESTAMP_EXTRAPOLATOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/timing.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/timing.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/timing.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/timing.cc 2015-02-03 14:33:36.000000000 +0000 @@ -10,25 +10,18 @@ #include "webrtc/modules/video_coding/main/source/timing.h" - #include "webrtc/modules/video_coding/main/source/internal_defines.h" #include "webrtc/modules/video_coding/main/source/jitter_buffer_common.h" -#include "webrtc/modules/video_coding/main/source/timestamp_extrapolator.h" #include "webrtc/system_wrappers/interface/clock.h" -#include "webrtc/system_wrappers/interface/trace.h" - +#include "webrtc/system_wrappers/interface/timestamp_extrapolator.h" namespace webrtc { VCMTiming::VCMTiming(Clock* clock, - int32_t vcm_id, - int32_t timing_id, VCMTiming* master_timing) : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), - vcm_id_(vcm_id), clock_(clock), - timing_id_(timing_id), master_(false), ts_extrapolator_(), codec_timer_(), @@ -40,7 +33,7 @@ prev_frame_timestamp_(0) { if (master_timing == NULL) { master_ = true; - ts_extrapolator_ = new VCMTimestampExtrapolator(clock_, vcm_id, timing_id); + ts_extrapolator_ = new TimestampExtrapolator(clock_->TimeInMilliseconds()); } else { ts_extrapolator_ = master_timing->ts_extrapolator_; } @@ -55,7 +48,7 @@ void VCMTiming::Reset() { CriticalSectionScoped cs(crit_sect_); - ts_extrapolator_->Reset(); + ts_extrapolator_->Reset(clock_->TimeInMilliseconds()); codec_timer_.Reset(); render_delay_ms_ = kDefaultRenderDelayMs; min_playout_delay_ms_ = 0; @@ -65,6 +58,7 @@ } void VCMTiming::ResetDecodeTime() { + CriticalSectionScoped lock(crit_sect_); codec_timer_.Reset(); } @@ -81,11 +75,6 @@ void VCMTiming::SetJitterDelay(uint32_t jitter_delay_ms) { CriticalSectionScoped cs(crit_sect_); if (jitter_delay_ms != jitter_delay_ms_) { - if (master_) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, timing_id_), - "Desired jitter buffer level: %u ms", jitter_delay_ms); - } jitter_delay_ms_ = jitter_delay_ms; // When in initial state, set current delay to minimum delay. if (current_delay_ms_ == 0) { @@ -152,39 +141,21 @@ int64_t start_time_ms, int64_t now_ms) { CriticalSectionScoped cs(crit_sect_); - const int32_t max_dec_time = MaxDecodeTimeMs(); int32_t time_diff_ms = codec_timer_.StopTimer(start_time_ms, now_ms); - if (time_diff_ms < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(vcm_id_, - timing_id_), "Codec timer error: %d", time_diff_ms); - assert(false); - } + assert(time_diff_ms >= 0); last_decode_ms_ = time_diff_ms; - if (master_) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(vcm_id_, - timing_id_), - "Frame decoded: time_stamp=%u dec_time=%d max_dec_time=%u, at %u", - time_stamp, time_diff_ms, max_dec_time, MaskWord64ToUWord32(now_ms)); - } return 0; } void VCMTiming::IncomingTimestamp(uint32_t time_stamp, int64_t now_ms) { CriticalSectionScoped cs(crit_sect_); - ts_extrapolator_->Update(now_ms, time_stamp, master_); + ts_extrapolator_->Update(now_ms, time_stamp); } int64_t VCMTiming::RenderTimeMs(uint32_t frame_timestamp, int64_t now_ms) const { CriticalSectionScoped cs(crit_sect_); const int64_t render_time_ms = RenderTimeMsInternal(frame_timestamp, now_ms); - if (master_) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(vcm_id_, - timing_id_), "Render frame %u at %u. Render delay %u", - "jitter delay %u, max decode time %u, playout delay %u", - frame_timestamp, MaskWord64ToUWord32(render_time_ms), render_delay_ms_, - jitter_delay_ms_, MaxDecodeTimeMs(), min_playout_delay_ms_); - } return render_time_ms; } @@ -192,11 +163,6 @@ int64_t now_ms) const { int64_t estimated_complete_time_ms = ts_extrapolator_->ExtrapolateLocalTime(frame_timestamp); - if (master_) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, timing_id_), "ExtrapolateLocalTime(%u)=%u ms", - frame_timestamp, MaskWord64ToUWord32(estimated_complete_time_ms)); - } if (estimated_complete_time_ms == -1) { estimated_complete_time_ms = now_ms; } @@ -210,11 +176,7 @@ int32_t VCMTiming::MaxDecodeTimeMs(FrameType frame_type /*= kVideoFrameDelta*/) const { const int32_t decode_time_ms = codec_timer_.RequiredDecodeTimeMs(frame_type); - if (decode_time_ms < 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(vcm_id_, - timing_id_), "Negative maximum decode time: %d", decode_time_ms); - return -1; - } + assert(decode_time_ms >= 0); return decode_time_ms; } @@ -254,11 +216,6 @@ } uint32_t VCMTiming::TargetDelayInternal() const { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, - VCMId(vcm_id_, timing_id_), - "Delay: min_playout=%u jitter=%u max_decode=%u render=%u", - min_playout_delay_ms_, jitter_delay_ms_, MaxDecodeTimeMs(), - render_delay_ms_); return std::max(min_playout_delay_ms_, jitter_delay_ms_ + MaxDecodeTimeMs() + render_delay_ms_); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/timing.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/timing.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/timing.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/timing.h 2015-02-03 14:33:36.000000000 +0000 @@ -11,6 +11,7 @@ #ifndef WEBRTC_MODULES_VIDEO_CODING_MAIN_SOURCE_TIMING_H_ #define WEBRTC_MODULES_VIDEO_CODING_MAIN_SOURCE_TIMING_H_ +#include "webrtc/base/thread_annotations.h" #include "webrtc/modules/video_coding/main/source/codec_timer.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/typedefs.h" @@ -18,15 +19,13 @@ namespace webrtc { class Clock; -class VCMTimestampExtrapolator; +class TimestampExtrapolator; class VCMTiming { public: // The primary timing component should be passed // if this is the dual timing component. VCMTiming(Clock* clock, - int32_t vcm_id = 0, - int32_t timing_id = 0, VCMTiming* master_timing = NULL); ~VCMTiming(); @@ -95,24 +94,24 @@ enum { kDelayMaxChangeMsPerS = 100 }; protected: - int32_t MaxDecodeTimeMs(FrameType frame_type = kVideoFrameDelta) const; - int64_t RenderTimeMsInternal(uint32_t frame_timestamp, int64_t now_ms) const; - uint32_t TargetDelayInternal() const; + int32_t MaxDecodeTimeMs(FrameType frame_type = kVideoFrameDelta) const + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + int64_t RenderTimeMsInternal(uint32_t frame_timestamp, int64_t now_ms) const + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + uint32_t TargetDelayInternal() const EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); private: CriticalSectionWrapper* crit_sect_; - int32_t vcm_id_; - Clock* clock_; - int32_t timing_id_; - bool master_; - VCMTimestampExtrapolator* ts_extrapolator_; - VCMCodecTimer codec_timer_; - uint32_t render_delay_ms_; - uint32_t min_playout_delay_ms_; - uint32_t jitter_delay_ms_; - uint32_t current_delay_ms_; - int last_decode_ms_; - uint32_t prev_frame_timestamp_; + Clock* const clock_; + bool master_ GUARDED_BY(crit_sect_); + TimestampExtrapolator* ts_extrapolator_ GUARDED_BY(crit_sect_); + VCMCodecTimer codec_timer_ GUARDED_BY(crit_sect_); + uint32_t render_delay_ms_ GUARDED_BY(crit_sect_); + uint32_t min_playout_delay_ms_ GUARDED_BY(crit_sect_); + uint32_t jitter_delay_ms_ GUARDED_BY(crit_sect_); + uint32_t current_delay_ms_ GUARDED_BY(crit_sect_); + int last_decode_ms_ GUARDED_BY(crit_sect_); + uint32_t prev_frame_timestamp_ GUARDED_BY(crit_sect_); }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding.gypi 2015-02-03 14:33:36.000000000 +0000 @@ -17,6 +17,7 @@ '<(webrtc_root)/modules/video_coding/utility/video_coding_utility.gyp:video_coding_utility', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', '<(webrtc_vp8_dir)/vp8.gyp:webrtc_vp8', + '<(webrtc_vp9_dir)/vp9.gyp:webrtc_vp9', ], 'sources': [ # interfaces @@ -48,7 +49,6 @@ 'receiver.h', 'rtt_filter.h', 'session_info.h', - 'timestamp_extrapolator.h', 'timestamp_map.h', 'timing.h', 'video_coding_impl.h', @@ -72,7 +72,6 @@ 'receiver.cc', 'rtt_filter.cc', 'session_info.cc', - 'timestamp_extrapolator.cc', 'timestamp_map.cc', 'timing.cc', 'video_coding_impl.cc', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_impl.cc 2015-02-03 14:33:36.000000000 +0000 @@ -16,7 +16,6 @@ #include "webrtc/modules/video_coding/main/source/packet.h" #include "webrtc/modules/video_coding/main/source/video_coding_impl.h" #include "webrtc/system_wrappers/interface/clock.h" -#include "webrtc/system_wrappers/interface/trace.h" #include "webrtc/system_wrappers/interface/trace_event.h" namespace webrtc { @@ -77,13 +76,12 @@ class VideoCodingModuleImpl : public VideoCodingModule { public: - VideoCodingModuleImpl(const int32_t id, - Clock* clock, + VideoCodingModuleImpl(Clock* clock, EventFactory* event_factory, bool owns_event_factory) : VideoCodingModule(), - sender_(new vcm::VideoSender(id, clock, &post_encode_callback_)), - receiver_(new vcm::VideoReceiver(id, clock, event_factory)), + sender_(new vcm::VideoSender(clock, &post_encode_callback_)), + receiver_(new vcm::VideoReceiver(clock, event_factory)), own_event_factory_(owns_event_factory ? event_factory : NULL) {} virtual ~VideoCodingModuleImpl() { @@ -133,8 +131,8 @@ externalEncoder, payloadType, internalSource); } - virtual int32_t CodecConfigParameters(uint8_t* buffer, int32_t size) - OVERRIDE { + virtual int32_t CodecConfigParameters(uint8_t* buffer, + int32_t size) OVERRIDE { return sender_->CodecConfigParameters(buffer, size); } @@ -152,8 +150,8 @@ return sender_->SetChannelParameters(target_bitrate, lossRate, rtt); } - virtual int32_t RegisterTransportCallback(VCMPacketizationCallback* transport) - OVERRIDE { + virtual int32_t RegisterTransportCallback( + VCMPacketizationCallback* transport) OVERRIDE { return sender_->RegisterTransportCallback(transport); } @@ -167,8 +165,8 @@ return sender_->RegisterVideoQMCallback(videoQMSettings); } - virtual int32_t RegisterProtectionCallback(VCMProtectionCallback* protection) - OVERRIDE { + virtual int32_t RegisterProtectionCallback( + VCMProtectionCallback* protection) OVERRIDE { return sender_->RegisterProtectionCallback(protection); } @@ -183,10 +181,10 @@ return sender_return; } - virtual int32_t AddVideoFrame(const I420VideoFrame& videoFrame, - const VideoContentMetrics* contentMetrics, - const CodecSpecificInfo* codecSpecificInfo) - OVERRIDE { + virtual int32_t AddVideoFrame( + const I420VideoFrame& videoFrame, + const VideoContentMetrics* contentMetrics, + const CodecSpecificInfo* codecSpecificInfo) OVERRIDE { return sender_->AddVideoFrame( videoFrame, contentMetrics, codecSpecificInfo); } @@ -228,11 +226,11 @@ return VCM_OK; } - virtual void SuspendBelowMinBitrate() { + virtual void SuspendBelowMinBitrate() OVERRIDE { return sender_->SuspendBelowMinBitrate(); } - virtual bool VideoSuspended() const { + virtual bool VideoSuspended() const OVERRIDE { return sender_->VideoSuspended(); } @@ -254,8 +252,8 @@ externalDecoder, payloadType, internalRenderTiming); } - virtual int32_t RegisterReceiveCallback(VCMReceiveCallback* receiveCallback) - OVERRIDE { + virtual int32_t RegisterReceiveCallback( + VCMReceiveCallback* receiveCallback) OVERRIDE { return receiver_->RegisterReceiveCallback(receiveCallback); } @@ -299,7 +297,7 @@ virtual int32_t ResetDecoder() OVERRIDE { return receiver_->ResetDecoder(); } - virtual int32_t ReceiveCodec(VideoCodec* currentReceiveCodec) const { + virtual int32_t ReceiveCodec(VideoCodec* currentReceiveCodec) const OVERRIDE { return receiver_->ReceiveCodec(currentReceiveCodec); } @@ -395,17 +393,16 @@ return VCMCodecDataBase::Codec(codecType, codec) ? 0 : -1; } -VideoCodingModule* VideoCodingModule::Create(const int32_t id) { +VideoCodingModule* VideoCodingModule::Create() { return new VideoCodingModuleImpl( - id, Clock::GetRealTimeClock(), new EventFactoryImpl, true); + Clock::GetRealTimeClock(), new EventFactoryImpl, true); } -VideoCodingModule* VideoCodingModule::Create(const int32_t id, - Clock* clock, +VideoCodingModule* VideoCodingModule::Create(Clock* clock, EventFactory* event_factory) { assert(clock); assert(event_factory); - return new VideoCodingModuleImpl(id, clock, event_factory, false); + return new VideoCodingModuleImpl(clock, event_factory, false); } void VideoCodingModule::Destroy(VideoCodingModule* module) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_impl.h 2015-02-03 14:33:36.000000000 +0000 @@ -15,6 +15,7 @@ #include +#include "webrtc/base/thread_annotations.h" #include "webrtc/modules/video_coding/main/source/codec_database.h" #include "webrtc/modules/video_coding/main/source/frame_buffer.h" #include "webrtc/modules/video_coding/main/source/generic_decoder.h" @@ -54,9 +55,7 @@ public: typedef VideoCodingModule::SenderNackMode SenderNackMode; - VideoSender(const int32_t id, - Clock* clock, - EncodedImageCallback* post_encode_callback); + VideoSender(Clock* clock, EncodedImageCallback* post_encode_callback); ~VideoSender(); @@ -112,7 +111,6 @@ int32_t Process(); private: - int32_t _id; Clock* clock_; scoped_ptr recorder_; @@ -136,7 +134,7 @@ public: typedef VideoCodingModule::ReceiverRobustness ReceiverRobustness; - VideoReceiver(const int32_t id, Clock* clock, EventFactory* event_factory); + VideoReceiver(Clock* clock, EventFactory* event_factory); ~VideoReceiver(); int32_t InitializeReceiver(); @@ -192,7 +190,8 @@ void RegisterPreDecodeImageCallback(EncodedImageCallback* observer); protected: - int32_t Decode(const webrtc::VCMEncodedFrame& frame); + int32_t Decode(const webrtc::VCMEncodedFrame& frame) + EXCLUSIVE_LOCKS_REQUIRED(_receiveCritSect); int32_t RequestKeyFrame(); int32_t RequestSliceLossIndication(const uint64_t pictureID) const; int32_t NackList(uint16_t* nackList, uint16_t* size); @@ -206,11 +205,10 @@ // in any frame }; - int32_t _id; - Clock* clock_; + Clock* const clock_; scoped_ptr process_crit_sect_; CriticalSectionWrapper* _receiveCritSect; - bool _receiverInited; + bool _receiverInited GUARDED_BY(_receiveCritSect); VideoReceiveState _receiveState; VCMTiming _timing; VCMTiming _dualTiming; @@ -218,12 +216,17 @@ VCMReceiver _dualReceiver; VCMDecodedFrameCallback _decodedFrameCallback; VCMDecodedFrameCallback _dualDecodedFrameCallback; - VCMFrameTypeCallback* _frameTypeCallback; - VCMReceiveStatisticsCallback* _receiveStatsCallback; - VCMDecoderTimingCallback* _decoderTimingCallback; - VCMPacketRequestCallback* _packetRequestCallback; - VCMReceiveStateCallback* _receiveStateCallback; - VCMRenderBufferSizeCallback* render_buffer_callback_; + VCMFrameTypeCallback* _frameTypeCallback GUARDED_BY(process_crit_sect_); + VCMReceiveStatisticsCallback* _receiveStatsCallback + GUARDED_BY(process_crit_sect_); + VCMDecoderTimingCallback* _decoderTimingCallback + GUARDED_BY(process_crit_sect_); + VCMPacketRequestCallback* _packetRequestCallback + GUARDED_BY(process_crit_sect_); + VCMReceiveStateCallback* _receiveStateCallback + GUARDED_BY(process_crit_sect_); + VCMRenderBufferSizeCallback* render_buffer_callback_ + GUARDED_BY(process_crit_sect_); VCMGenericDecoder* _decoder; VCMGenericDecoder* _dualDecoder; #ifdef DEBUG_DECODER_BIT_STREAM @@ -231,11 +234,11 @@ #endif VCMFrameBuffer _frameFromFile; VCMKeyRequestMode _keyRequestMode; - bool _scheduleKeyRequest; - size_t max_nack_list_size_; - EncodedImageCallback* pre_decode_image_callback_; + bool _scheduleKeyRequest GUARDED_BY(process_crit_sect_); + size_t max_nack_list_size_ GUARDED_BY(process_crit_sect_); + EncodedImageCallback* pre_decode_image_callback_ GUARDED_BY(_receiveCritSect); - VCMCodecDataBase _codecDataBase; + VCMCodecDataBase _codecDataBase GUARDED_BY(_receiveCritSect); VCMProcessTimer _receiveStatsTimer; VCMProcessTimer _retransmissionTimer; VCMProcessTimer _keyRequestTimer; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_robustness_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_robustness_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_robustness_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_robustness_unittest.cc 2015-02-03 14:33:36.000000000 +0000 @@ -35,7 +35,7 @@ virtual void SetUp() { clock_.reset(new SimulatedClock(0)); ASSERT_TRUE(clock_.get() != NULL); - vcm_ = VideoCodingModule::Create(0, clock_.get(), &event_factory_); + vcm_ = VideoCodingModule::Create(clock_.get(), &event_factory_); ASSERT_TRUE(vcm_ != NULL); ASSERT_EQ(0, vcm_->InitializeReceiver()); const size_t kMaxNackListSize = 250; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_test.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_test.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_test.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_test.gypi 2015-02-03 14:33:36.000000000 +0000 @@ -20,6 +20,8 @@ '<(webrtc_root)/test/test.gyp:test_support', '<(webrtc_root)/test/metrics.gyp:metrics', '<(webrtc_root)/common_video/common_video.gyp:common_video', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers_default', + '<(webrtc_root)/test/webrtc_test_common.gyp:webrtc_test_common', ], 'sources': [ # headers @@ -29,11 +31,9 @@ '../test/media_opt_test.h', '../test/mt_test_common.h', '../test/normal_test.h', - '../test/pcap_file_reader.h', '../test/quality_modes_test.h', '../test/receiver_tests.h', '../test/release_test.h', - '../test/rtp_file_reader.h', '../test/rtp_player.h', '../test/test_callbacks.h', '../test/test_util.h', @@ -44,19 +44,17 @@ '../test/codec_database_test.cc', '../test/generic_codec_test.cc', '../test/media_opt_test.cc', - '../test/mt_test_common.cc', '../test/mt_rx_tx_test.cc', + '../test/mt_test_common.cc', '../test/normal_test.cc', - '../test/pcap_file_reader.cc', '../test/quality_modes_test.cc', - '../test/rtp_file_reader.cc', '../test/rtp_player.cc', '../test/test_callbacks.cc', '../test/test_util.cc', '../test/tester_main.cc', '../test/vcm_payload_sink_factory.cc', - '../test/video_rtp_play_mt.cc', '../test/video_rtp_play.cc', + '../test/video_rtp_play_mt.cc', '../test/video_source.cc', ], # sources }, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_receiver.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_receiver.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_receiver.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_receiver.cc 2015-02-03 14:33:36.000000000 +0000 @@ -16,7 +16,7 @@ #include "webrtc/modules/video_coding/main/source/packet.h" #include "webrtc/modules/video_coding/main/source/video_coding_impl.h" #include "webrtc/system_wrappers/interface/clock.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/trace_event.h" // #define DEBUG_DECODER_BIT_STREAM @@ -24,19 +24,16 @@ namespace webrtc { namespace vcm { -VideoReceiver::VideoReceiver(const int32_t id, - Clock* clock, - EventFactory* event_factory) - : _id(id), - clock_(clock), +VideoReceiver::VideoReceiver(Clock* clock, EventFactory* event_factory) + : clock_(clock), process_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), _receiveCritSect(CriticalSectionWrapper::CreateCriticalSection()), _receiverInited(false), _receiveState(kReceiveStateInitial), - _timing(clock_, id, 1), - _dualTiming(clock_, id, 2, &_timing), - _receiver(&_timing, clock_, event_factory, id, 1, true), - _dualReceiver(&_dualTiming, clock_, event_factory, id, 2, false), + _timing(clock_), + _dualTiming(clock_, &_timing), + _receiver(&_timing, clock_, event_factory, true), + _dualReceiver(&_dualTiming, clock_, event_factory, false), _decodedFrameCallback(_timing, clock_), _dualDecodedFrameCallback(_dualTiming, clock_), _frameTypeCallback(NULL), @@ -55,7 +52,7 @@ _scheduleKeyRequest(false), max_nack_list_size_(0), pre_decode_image_callback_(NULL), - _codecDataBase(id), + _codecDataBase(), _receiveStatsTimer(1000, clock_), _retransmissionTimer(10, clock_), _keyRequestTimer(500, clock_) { @@ -295,8 +292,6 @@ // Initialize receiver, resets codec database etc int32_t VideoReceiver::InitializeReceiver() { - CriticalSectionScoped receive_cs(_receiveCritSect); - CriticalSectionScoped process_cs(process_crit_sect_.get()); int32_t ret = _receiver.Initialize(); if (ret < 0) { return ret; @@ -306,19 +301,26 @@ if (ret < 0) { return ret; } - _codecDataBase.ResetReceiver(); - _timing.Reset(); - _decoder = NULL; - _decodedFrameCallback.SetUserReceiveCallback(NULL); - _receiverInited = true; - _frameTypeCallback = NULL; - _receiveStatsCallback = NULL; - _decoderTimingCallback = NULL; - _packetRequestCallback = NULL; - _receiveStateCallback = NULL; - _keyRequestMode = kKeyOnError; - _scheduleKeyRequest = false; + { + CriticalSectionScoped receive_cs(_receiveCritSect); + _codecDataBase.ResetReceiver(); + _timing.Reset(); + _receiverInited = true; + } + + { + CriticalSectionScoped process_cs(process_crit_sect_.get()); + _decoder = NULL; + _decodedFrameCallback.SetUserReceiveCallback(NULL); + _frameTypeCallback = NULL; + _receiveStatsCallback = NULL; + _decoderTimingCallback = NULL; + _packetRequestCallback = NULL; + _receiveStateCallback = NULL; + _keyRequestMode = kKeyOnError; + _scheduleKeyRequest = false; + } return VCM_OK; } @@ -396,6 +398,7 @@ // Should be called as often as possible to get the most out of the decoder. int32_t VideoReceiver::Decode(uint16_t maxWaitTimeMs) { int64_t nextRenderTimeMs; + bool supports_render_scheduling; { CriticalSectionScoped cs(_receiveCritSect); if (!_receiverInited) { @@ -404,6 +407,7 @@ if (!_codecDataBase.DecoderRegistered()) { return VCM_NO_CODEC_REGISTERED; } + supports_render_scheduling = _codecDataBase.SupportsRenderScheduling(); } const bool dualReceiverEnabledNotReceiving = ( @@ -412,7 +416,7 @@ VCMEncodedFrame* frame = _receiver.FrameForDecoding(maxWaitTimeMs, nextRenderTimeMs, - _codecDataBase.SupportsRenderScheduling(), + supports_render_scheduling, &_dualReceiver); if (dualReceiverEnabledNotReceiving && _dualReceiver.State() == kReceiving) { @@ -480,17 +484,9 @@ const int32_t ret = _frameTypeCallback->SliceLossIndicationRequest(pictureID); if (ret < 0) { - WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceVideoCoding, - VCMId(_id), - "Failed to request key frame"); return ret; } } else { - WEBRTC_TRACE(webrtc::kTraceWarning, - webrtc::kTraceVideoCoding, - VCMId(_id), - "No frame type request callback registered"); return VCM_MISSING_CALLBACK; } return VCM_OK; @@ -502,18 +498,10 @@ if (_frameTypeCallback != NULL) { const int32_t ret = _frameTypeCallback->RequestKeyFrame(); if (ret < 0) { - WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceVideoCoding, - VCMId(_id), - "Failed to request key frame"); return ret; } _scheduleKeyRequest = false; } else { - WEBRTC_TRACE(webrtc::kTraceWarning, - webrtc::kTraceVideoCoding, - VCMId(_id), - "No frame type request callback registered"); return VCM_MISSING_CALLBACK; } return VCM_OK; @@ -536,29 +524,18 @@ VCMEncodedFrame* dualFrame = _dualReceiver.FrameForDecoding(maxWaitTimeMs, dummyRenderTime); if (dualFrame != NULL && _dualDecoder != NULL) { - WEBRTC_TRACE(webrtc::kTraceStream, - webrtc::kTraceVideoCoding, - VCMId(_id), - "Decoding frame %u with dual decoder", - dualFrame->TimeStamp()); // Decode dualFrame and try to catch up int32_t ret = _dualDecoder->Decode(*dualFrame, clock_->TimeInMilliseconds()); if (ret != WEBRTC_VIDEO_CODEC_OK) { - WEBRTC_TRACE(webrtc::kTraceWarning, - webrtc::kTraceVideoCoding, - VCMId(_id), - "Failed to decode frame with dual decoder"); + LOG(LS_ERROR) << "Failed to decode frame with dual decoder. Error code: " + << ret; _dualReceiver.ReleaseFrame(dualFrame); return VCM_CODEC_ERROR; } if (_receiver.DualDecoderCaughtUp(dualFrame, _dualReceiver)) { // Copy the complete decoder state of the dual decoder // to the primary decoder. - WEBRTC_TRACE(webrtc::kTraceStream, - webrtc::kTraceVideoCoding, - VCMId(_id), - "Dual decoder caught up"); _codecDataBase.CopyDecoder(*_dualDecoder); _codecDataBase.ReleaseDecoder(_dualDecoder); _dualDecoder = NULL; @@ -599,11 +576,6 @@ return RequestSliceLossIndication( _decodedFrameCallback.LastReceivedPictureID() + 1); } else { - WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceVideoCoding, - VCMId(_id), - "Failed to decode frame %u, requesting key frame", - frame.TimeStamp()); request_key_frame = true; } } else if (ret == VCM_REQUEST_SLI) { @@ -766,25 +738,9 @@ nackStatus = _dualReceiver.NackList(nackList, *size, &nack_list_length); } *size = nack_list_length; - - switch (nackStatus) { - case kNackNeedMoreMemory: { - WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceVideoCoding, - VCMId(_id), - "Out of memory"); - return VCM_MEMORY; - } - case kNackKeyFrameRequest: { - WEBRTC_TRACE(webrtc::kTraceWarning, - webrtc::kTraceVideoCoding, - VCMId(_id), - "Failed to get NACK list, requesting key frame"); + if (nackStatus == kNackKeyFrameRequest) { SetReceiveState(kReceiveStateWaitingKey); return RequestKeyFrame(); - } - default: - break; } if (*size != 0) { // Note: not a valid transition from WaitingKey or DecodingWithErrors; @@ -824,14 +780,17 @@ _keyRequestMode = kKeyOnError; // TODO(hlundin): On long NACK list? break; case VideoCodingModule::kSoftNack: +#if 1 assert(false); // TODO(hlundin): Not completed. return VCM_NOT_IMPLEMENTED; +#else // Enable hybrid NACK/FEC. Always wait for retransmissions and don't add // extra delay when RTT is above kLowRttNackMs. _receiver.SetNackMode(kNack, media_optimization::kLowRttNackMs, -1); _dualReceiver.SetNackMode(kNoNack, -1, -1); _keyRequestMode = kKeyOnError; break; +#endif case VideoCodingModule::kDualDecoder: if (decode_error_mode == kNoErrors) { return VCM_PARAMETER_ERROR; @@ -844,14 +803,17 @@ _keyRequestMode = kKeyOnError; break; case VideoCodingModule::kReferenceSelection: +#if 1 assert(false); // TODO(hlundin): Not completed. return VCM_NOT_IMPLEMENTED; +#else if (decode_error_mode == kNoErrors) { return VCM_PARAMETER_ERROR; } _receiver.SetNackMode(kNoNack, -1, -1); _dualReceiver.SetNackMode(kNoNack, -1, -1); break; +#endif } _receiver.SetDecodeErrorMode(decode_error_mode); // The dual decoder should never decode with errors. @@ -868,7 +830,6 @@ int max_packet_age_to_nack, int max_incomplete_time_ms) { if (max_nack_list_size != 0) { - CriticalSectionScoped receive_cs(_receiveCritSect); CriticalSectionScoped process_cs(process_crit_sect_.get()); max_nack_list_size_ = max_nack_list_size; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_receiver_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_receiver_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_receiver_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_receiver_unittest.cc 2015-02-03 14:33:37.000000000 +0000 @@ -33,7 +33,7 @@ TestVideoReceiver() : clock_(0) {} virtual void SetUp() { - receiver_.reset(new VideoReceiver(0, &clock_, &event_factory_)); + receiver_.reset(new VideoReceiver(&clock_, &event_factory_)); EXPECT_EQ(0, receiver_->InitializeReceiver()); EXPECT_EQ(0, receiver_->RegisterExternalDecoder( diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_sender.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_sender.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_sender.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_sender.cc 2015-02-03 14:33:37.000000000 +0000 @@ -17,6 +17,7 @@ #include "webrtc/modules/video_coding/main/source/encoded_frame.h" #include "webrtc/modules/video_coding/main/source/video_coding_impl.h" #include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { namespace vcm { @@ -57,20 +58,18 @@ FILE* file_ GUARDED_BY(cs_); }; -VideoSender::VideoSender(const int32_t id, - Clock* clock, +VideoSender::VideoSender(Clock* clock, EncodedImageCallback* post_encode_callback) - : _id(id), - clock_(clock), + : clock_(clock), recorder_(new DebugRecorder()), process_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), _sendCritSect(CriticalSectionWrapper::CreateCriticalSection()), _encoder(), _encodedFrameCallback(post_encode_callback), _nextFrameTypes(1, kVideoFrameDelta), - _mediaOpt(id, clock_), + _mediaOpt(clock_), _sendStatsCallback(NULL), - _codecDataBase(id), + _codecDataBase(), frame_dropper_enabled_(true), _sendStatsTimer(1000, clock_), qm_settings_callback_(NULL), @@ -133,10 +132,8 @@ _encoder = _codecDataBase.GetEncoder(); if (!ret) { - WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceVideoCoding, - VCMId(_id), - "Failed to initialize encoder"); + LOG(LS_ERROR) << "Failed to initialize the encoder with payload name " + << sendCodec->plName << ". Error code: " << ret; return VCM_CODEC_ERROR; } @@ -365,26 +362,18 @@ return VCM_OK; } if (_mediaOpt.DropFrame()) { - WEBRTC_TRACE(webrtc::kTraceStream, - webrtc::kTraceVideoCoding, - VCMId(_id), - "Drop frame due to bitrate"); - } else { - _mediaOpt.UpdateContentData(contentMetrics); - int32_t ret = - _encoder->Encode(videoFrame, codecSpecificInfo, _nextFrameTypes); - recorder_->Add(videoFrame); - if (ret < 0) { - WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceVideoCoding, - VCMId(_id), - "Encode error: %d", - ret); - return ret; - } - for (size_t i = 0; i < _nextFrameTypes.size(); ++i) { - _nextFrameTypes[i] = kVideoFrameDelta; // Default frame type. - } + return VCM_OK; + } + _mediaOpt.UpdateContentData(contentMetrics); + int32_t ret = + _encoder->Encode(videoFrame, codecSpecificInfo, _nextFrameTypes); + recorder_->Add(videoFrame); + if (ret < 0) { + LOG(LS_ERROR) << "Failed to encode frame. Error code: " << ret; + return ret; + } + for (size_t i = 0; i < _nextFrameTypes.size(); ++i) { + _nextFrameTypes[i] = kVideoFrameDelta; // Default frame type. } return VCM_OK; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_sender_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_sender_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_sender_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_sender_unittest.cc 2015-02-03 14:33:37.000000000 +0000 @@ -70,7 +70,10 @@ class EmptyFrameGenerator : public FrameGenerator { public: - I420VideoFrame* NextFrame() OVERRIDE { frame_.ResetSize(); return &frame_; } + virtual I420VideoFrame* NextFrame() OVERRIDE { + frame_.ResetSize(); + return &frame_; + } private: I420VideoFrame frame_; @@ -90,7 +93,7 @@ const uint8_t* payload_data, uint32_t payload_size, const RTPFragmentationHeader& fragmentation_header, - const RTPVideoHeader* rtp_video_header) { + const RTPVideoHeader* rtp_video_header) OVERRIDE { assert(rtp_video_header); frame_data_.push_back(FrameData(payload_size, *rtp_video_header)); return 0; @@ -141,10 +144,10 @@ int frames = 0; for (size_t i = 0; i < frame_data_.size(); ++i) { EXPECT_EQ(kRtpVideoVp8, frame_data_[i].rtp_video_header.codec); - if (frame_data_[i].rtp_video_header.codecHeader.VP8.temporalIdx <= - temporal_layer) { + const uint8_t temporal_idx = + frame_data_[i].rtp_video_header.codecHeader.VP8.temporalIdx; + if (temporal_idx <= temporal_layer || temporal_idx == kNoTemporalIdx) frames++; - } } return frames; } @@ -153,10 +156,10 @@ int payload_size = 0; for (size_t i = 0; i < frame_data_.size(); ++i) { EXPECT_EQ(kRtpVideoVp8, frame_data_[i].rtp_video_header.codec); - if (frame_data_[i].rtp_video_header.codecHeader.VP8.temporalIdx <= - temporal_layer) { + const uint8_t temporal_idx = + frame_data_[i].rtp_video_header.codecHeader.VP8.temporalIdx; + if (temporal_idx <= temporal_layer || temporal_idx == kNoTemporalIdx) payload_size += frame_data_[i].payload_size; - } } return payload_size; } @@ -172,8 +175,8 @@ // a special case (e.g. frame rate in media optimization). TestVideoSender() : clock_(1000), packetization_callback_(&clock_) {} - virtual void SetUp() { - sender_.reset(new VideoSender(0, &clock_, &post_encode_callback_)); + virtual void SetUp() OVERRIDE { + sender_.reset(new VideoSender(&clock_, &post_encode_callback_)); EXPECT_EQ(0, sender_->InitializeSender()); EXPECT_EQ(0, sender_->RegisterTransportCallback(&packetization_callback_)); } @@ -198,7 +201,7 @@ static const int kNumberOfLayers = 3; static const int kUnusedPayloadType = 10; - virtual void SetUp() { + virtual void SetUp() OVERRIDE { TestVideoSender::SetUp(); generator_.reset(new EmptyFrameGenerator()); EXPECT_EQ( @@ -221,7 +224,7 @@ EXPECT_EQ(0, sender_->RegisterSendCodec(&settings_, 1, 1200)); } - virtual void TearDown() { sender_.reset(); } + virtual void TearDown() OVERRIDE { sender_.reset(); } void ExpectIntraRequest(int stream) { if (stream == -1) { @@ -314,7 +317,7 @@ TestVideoSenderWithVp8() : codec_bitrate_kbps_(300), available_bitrate_kbps_(1000) {} - virtual void SetUp() { + virtual void SetUp() OVERRIDE { TestVideoSender::SetUp(); const char* input_video = "foreman_cif"; @@ -345,6 +348,8 @@ void InsertFrames(float framerate, float seconds) { for (int i = 0; i < seconds * framerate; ++i) { clock_.AdvanceTimeMilliseconds(1000.0f / framerate); + EXPECT_CALL(post_encode_callback_, Encoded(_, NULL, NULL)) + .WillOnce(Return(0)); AddFrame(); // SetChannelParameters needs to be called frequently to propagate diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/codec_database_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/codec_database_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/codec_database_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/codec_database_test.cc 2015-02-03 14:33:37.000000000 +0000 @@ -29,7 +29,7 @@ int CodecDataBaseTest::RunTest(CmdArgs& args) { - VideoCodingModule* vcm = VideoCodingModule::Create(1); + VideoCodingModule* vcm = VideoCodingModule::Create(); CodecDataBaseTest* cdbt = new CodecDataBaseTest(vcm); cdbt->Perform(args); VideoCodingModule::Destroy(vcm); @@ -129,8 +129,7 @@ sourceFrame.set_timestamp(_timeStamp); // Encoder registration TEST (VideoCodingModule::NumberOfCodecs() > 0); - TEST(VideoCodingModule::Codec(-1, &sendCodec) < 0); - TEST(VideoCodingModule::Codec(VideoCodingModule::NumberOfCodecs() + 1, + TEST(VideoCodingModule::Codec(VideoCodingModule::NumberOfCodecs() + 1u, &sendCodec) < 0); VideoCodingModule::Codec(1, &sendCodec); sendCodec.plType = 0; // random value diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/generic_codec_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/generic_codec_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/generic_codec_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/generic_codec_test.cc 2015-02-03 14:33:37.000000000 +0000 @@ -28,8 +28,7 @@ { SimulatedClock clock(0); NullEventFactory event_factory; - VideoCodingModule* vcm = VideoCodingModule::Create(1, &clock, - &event_factory); + VideoCodingModule* vcm = VideoCodingModule::Create(&clock, &event_factory); GenericCodecTest* get = new GenericCodecTest(vcm, &clock); Trace::CreateTrace(); Trace::SetTraceFile( @@ -127,7 +126,6 @@ I420VideoFrame sourceFrame; _vcm->InitializeSender(); TEST(_vcm->Codec(kVideoCodecVP8, &sendCodec) == 0); - TEST(_vcm->RegisterSendCodec(&sendCodec, -1, 1440) < 0); // bad number of cores sendCodec.maxBitrate = 8000; _vcm->RegisterSendCodec(&sendCodec, 1, 1440); _vcm->InitializeSender(); @@ -135,8 +133,6 @@ sendCodec.height = 0; TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) < 0); // bad height _vcm->Codec(kVideoCodecVP8, &sendCodec); - sendCodec.startBitrate = -2; - TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) < 0); // bad bit rate _vcm->Codec(kVideoCodecVP8, &sendCodec); _vcm->InitializeSender(); // Setting rate when encoder uninitialized. @@ -283,7 +279,7 @@ const float nBitrates = sizeof(bitRate)/sizeof(*bitRate); float _bitRate = 0; int _frameCnt = 0; - float totalBytesOneSec;//, totalBytesTenSec; + float totalBytesOneSec = 0;//, totalBytesTenSec; float totalBytes, actualBitrate; VCMFrameCount frameCount; // testing frame type counters // start test diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/generic_codec_test.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/generic_codec_test.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/generic_codec_test.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/generic_codec_test.h 2015-02-03 14:33:37.000000000 +0000 @@ -75,8 +75,8 @@ public: // constructor input: (receive side) rtp module to send encoded data to RTPSendCallback_SizeTest() : _maxPayloadSize(0), _payloadSizeSum(0), _nPackets(0) {} - virtual int SendPacket(int channel, const void *data, int len); - virtual int SendRTCPPacket(int channel, const void *data, int len) {return 0;} + virtual int SendPacket(int channel, const void *data, int len) OVERRIDE; + virtual int SendRTCPPacket(int channel, const void *data, int len) OVERRIDE {return 0;} void SetMaxPayloadSize(uint32_t maxPayloadSize); void Reset(); float AveragePayloadSize() const; @@ -90,15 +90,15 @@ { public: VCMEncComplete_KeyReqTest(webrtc::VideoCodingModule &vcm) : _vcm(vcm), _seqNo(0), _timeStamp(0) {} - int32_t SendData( - const webrtc::FrameType frameType, - const uint8_t payloadType, - uint32_t timeStamp, - int64_t capture_time_ms, - const uint8_t* payloadData, - const uint32_t payloadSize, - const webrtc::RTPFragmentationHeader& fragmentationHeader, - const webrtc::RTPVideoHeader* videoHdr); + virtual int32_t SendData( + const webrtc::FrameType frameType, + const uint8_t payloadType, + uint32_t timeStamp, + int64_t capture_time_ms, + const uint8_t* payloadData, + const uint32_t payloadSize, + const webrtc::RTPFragmentationHeader& fragmentationHeader, + const webrtc::RTPVideoHeader* videoHdr) OVERRIDE; private: webrtc::VideoCodingModule& _vcm; uint16_t _seqNo; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/jitter_estimate_test.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/jitter_estimate_test.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/jitter_estimate_test.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/jitter_estimate_test.h 2015-02-03 14:33:37.000000000 +0000 @@ -17,7 +17,7 @@ #include #include -double const pi = 4*std::atan(1.0); +double const pi = 4 * atan(1.0); class GaussDist { @@ -26,9 +26,10 @@ double RandValue() // returns a single normally distributed number { - double r1 = (std::rand() + 1.0)/(RAND_MAX + 1.0); // gives equal distribution in (0, 1] - double r2 = (std::rand() + 1.0)/(RAND_MAX + 1.0); - return _mu + _sigma * sqrt(-2*std::log(r1))*std::cos(2*pi*r2); + double r1 = (rand() + 1.0) / + (RAND_MAX + 1.0); // gives equal distribution in (0, 1] + double r2 = (rand() + 1.0) / (RAND_MAX + 1.0); + return _mu + _sigma * sqrt(-2 * log(r1)) * cos(2 * pi * r2); } double GetAverage() @@ -86,7 +87,7 @@ void SetLossRate(double rate); private: - double RandUniform() { return (std::rand() + 1.0)/(RAND_MAX + 1.0); } + double RandUniform() { return (rand() + 1.0)/(RAND_MAX + 1.0); } unsigned int _frameRate; unsigned int _capacity; unsigned int _rate; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/media_opt_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/media_opt_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/media_opt_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/media_opt_test.cc 2015-02-03 14:33:37.000000000 +0000 @@ -32,7 +32,7 @@ Trace::CreateTrace(); Trace::SetTraceFile((test::OutputPath() + "mediaOptTestTrace.txt").c_str()); Trace::set_level_filter(webrtc::kTraceAll); - VideoCodingModule* vcm = VideoCodingModule::Create(1); + VideoCodingModule* vcm = VideoCodingModule::Create(); Clock* clock = Clock::GetRealTimeClock(); MediaOptTest* mot = new MediaOptTest(vcm, clock); if (testNum == 0) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/mt_rx_tx_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/mt_rx_tx_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/mt_rx_tx_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/mt_rx_tx_test.cc 2015-02-03 14:33:37.000000000 +0000 @@ -145,7 +145,7 @@ printf("Cannot read file %s.\n", outname.c_str()); return -1; } - VideoCodingModule* vcm = VideoCodingModule::Create(1); + VideoCodingModule* vcm = VideoCodingModule::Create(); RtpDataCallback dataCallback(vcm); RTPSendCompleteCallback* outgoingTransport = @@ -157,7 +157,7 @@ configuration.outgoing_transport = outgoingTransport; RtpRtcp* rtp = RtpRtcp::CreateRtpRtcp(configuration); scoped_ptr registry(new RTPPayloadRegistry( - -1, RTPPayloadStrategy::CreateStrategy(false))); + RTPPayloadStrategy::CreateStrategy(false))); scoped_ptr rtp_receiver( RtpReceiver::CreateVideoReceiver(-1, Clock::GetRealTimeClock(), &dataCallback, NULL, registry.get())); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/mt_test_common.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/mt_test_common.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/mt_test_common.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/mt_test_common.h 2015-02-03 14:33:37.000000000 +0000 @@ -52,7 +52,7 @@ // Add packets to list // Incorporate network conditions - delay and packet loss // Actual transmission will occur on a separate thread - int SendPacket(int channel, const void *data, int len); + virtual int SendPacket(int channel, const void *data, int len) OVERRIDE; // Send to the receiver packets which are ready to be submitted int TransportPackets(); }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/normal_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/normal_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/normal_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/normal_test.cc 2015-02-03 14:33:37.000000000 +0000 @@ -37,8 +37,7 @@ Trace::SetTraceFile( (test::OutputPath() + "VCMNormalTestTrace.txt").c_str()); Trace::set_level_filter(webrtc::kTraceAll); - VideoCodingModule* vcm = VideoCodingModule::Create(1, clock, - &event_factory); + VideoCodingModule* vcm = VideoCodingModule::Create(clock, &event_factory); NormalTest VCMNTest(vcm, clock); VCMNTest.Perform(args); VideoCodingModule::Destroy(vcm); @@ -102,6 +101,9 @@ rtpInfo.type.Video.codecHeader.VP8.pictureId = videoHdr->codecHeader.VP8.pictureId; break; + case kVideoCodecVP9: + // Leave for now, until we add kRtpVideoVp9 to RTP. + break; default: assert(false); return -1; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/normal_test.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/normal_test.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/normal_test.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/normal_test.h 2015-02-03 14:33:37.000000000 +0000 @@ -32,15 +32,15 @@ void RegisterTransportCallback(webrtc::VCMPacketizationCallback* transport); // process encoded data received from the encoder, // pass stream to the VCMReceiver module - int32_t - SendData(const webrtc::FrameType frameType, - const uint8_t payloadType, - const uint32_t timeStamp, - int64_t capture_time_ms, - const uint8_t* payloadData, - const uint32_t payloadSize, - const webrtc::RTPFragmentationHeader& fragmentationHeader, - const webrtc::RTPVideoHeader* videoHdr); + virtual int32_t SendData( + const webrtc::FrameType frameType, + const uint8_t payloadType, + const uint32_t timeStamp, + int64_t capture_time_ms, + const uint8_t* payloadData, + const uint32_t payloadSize, + const webrtc::RTPFragmentationHeader& fragmentationHeader, + const webrtc::RTPVideoHeader* videoHdr) OVERRIDE; // Register exisitng VCM. // Currently - encode and decode with the same vcm module. @@ -73,8 +73,10 @@ _currentHeight(0) {} virtual ~VCMNTDecodeCompleCallback(); void SetUserReceiveCallback(webrtc::VCMReceiveCallback* receiveCallback); + // will write decoded frame into file - int32_t FrameToRender(webrtc::I420VideoFrame& videoFrame); + virtual int32_t FrameToRender(webrtc::I420VideoFrame& videoFrame) OVERRIDE; + int32_t DecodedBytes(); private: FILE* _decodedFile; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/pcap_file_reader.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/pcap_file_reader.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/pcap_file_reader.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/pcap_file_reader.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,465 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/video_coding/main/test/pcap_file_reader.h" - -#ifdef WIN32 -#include -#include -#else -#include -#endif -#include -#include - -#include -#include -#include - -#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" -#include "webrtc/modules/video_coding/main/test/rtp_player.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" - -namespace webrtc { -namespace rtpplayer { - -enum { - kResultFail = -1, - kResultSuccess = 0, - kResultSkip = 1, - - kPcapVersionMajor = 2, - kPcapVersionMinor = 4, - kLinktypeNull = 0, - kLinktypeEthernet = 1, - kBsdNullLoopback1 = 0x00000002, - kBsdNullLoopback2 = 0x02000000, - kEthernetIIHeaderMacSkip = 12, - kEthertypeIp = 0x0800, - kIpVersion4 = 4, - kMinIpHeaderLength = 20, - kFragmentOffsetClear = 0x0000, - kFragmentOffsetDoNotFragment = 0x4000, - kProtocolTcp = 0x06, - kProtocolUdp = 0x11, - kUdpHeaderLength = 8, - kMaxReadBufferSize = 4096 -}; - -const uint32_t kPcapBOMSwapOrder = 0xd4c3b2a1UL; -const uint32_t kPcapBOMNoSwapOrder = 0xa1b2c3d4UL; - -#if 1 -# define DEBUG_LOG(text) -# define DEBUG_LOG1(text, arg) -#else -# define DEBUG_LOG(text) (printf(text "\n")) -# define DEBUG_LOG1(text, arg) (printf(text "\n", arg)) -#endif - -#define TRY(expr) \ - do { \ - int r = (expr); \ - if (r == kResultFail) { \ - DEBUG_LOG1("FAIL at " __FILE__ ":%d", __LINE__); \ - return kResultFail; \ - } else if (r == kResultSkip) { \ - return kResultSkip; \ - } \ - } while (0) - -// Read RTP packets from file in tcpdump/libpcap format, as documented at: -// http://wiki.wireshark.org/Development/LibpcapFileFormat -class PcapFileReaderImpl : public RtpPacketSourceInterface { - public: - PcapFileReaderImpl() - : file_(NULL), - swap_pcap_byte_order_(false), - swap_network_byte_order_(false), - read_buffer_(), - packets_by_ssrc_(), - packets_(), - next_packet_it_() { - int16_t test = 0x7f00; - if (test != htons(test)) { - swap_network_byte_order_ = true; - } - } - - virtual ~PcapFileReaderImpl() { - if (file_ != NULL) { - fclose(file_); - file_ = NULL; - } - } - - int Initialize(const std::string& filename) { - file_ = fopen(filename.c_str(), "rb"); - if (file_ == NULL) { - printf("ERROR: Can't open file: %s\n", filename.c_str()); - return kResultFail; - } - - if (ReadGlobalHeader() < 0) { - return kResultFail; - } - - int total_packet_count = 0; - uint32_t stream_start_ms = 0; - int32_t next_packet_pos = ftell(file_); - for (;;) { - TRY(fseek(file_, next_packet_pos, SEEK_SET)); - int result = ReadPacket(&next_packet_pos, stream_start_ms, - ++total_packet_count); - if (result == kResultFail) { - break; - } else if (result == kResultSuccess && packets_.size() == 1) { - assert(stream_start_ms == 0); - PacketIterator it = packets_.begin(); - stream_start_ms = it->time_offset_ms; - it->time_offset_ms = 0; - } - } - - if (feof(file_) == 0) { - printf("Failed reading file!\n"); - return kResultFail; - } - - printf("Total packets in file: %d\n", total_packet_count); - printf("Total RTP/RTCP packets: %d\n", static_cast(packets_.size())); - - for (SsrcMapIterator mit = packets_by_ssrc_.begin(); - mit != packets_by_ssrc_.end(); ++mit) { - uint32_t ssrc = mit->first; - const std::vector& packet_numbers = mit->second; - uint8_t pt = packets_[packet_numbers[0]].rtp_header.payloadType; - printf("SSRC: %08x, %d packets, pt=%d\n", ssrc, - static_cast(packet_numbers.size()), pt); - } - - // TODO(solenberg): Better validation of identified SSRC streams. - // - // Since we're dealing with raw network data here, we will wrongly identify - // some packets as RTP. When these packets are consumed by RtpPlayer, they - // are unlikely to cause issues as they will ultimately be filtered out by - // the RtpRtcp module. However, we should really do better filtering here, - // which we can accomplish in a number of ways, e.g.: - // - // - Verify that the time stamps and sequence numbers for RTP packets are - // both increasing/decreasing. If they move in different directions, the - // SSRC is likely bogus and can be dropped. (Normally they should be inc- - // reasing but we must allow packet reordering). - // - If RTP sequence number is not changing, drop the stream. - // - Can also use srcip:port->dstip:port pairs, assuming few SSRC collisions - // for up/down streams. - - next_packet_it_ = packets_.begin(); - return kResultSuccess; - } - - virtual int NextPacket(uint8_t* data, uint32_t* length, uint32_t* time_ms) { - assert(data); - assert(length); - assert(time_ms); - - if (next_packet_it_ == packets_.end()) { - return -1; - } - if (*length < next_packet_it_->payload_length) { - return -1; - } - TRY(fseek(file_, next_packet_it_->pos_in_file, SEEK_SET)); - TRY(Read(data, next_packet_it_->payload_length)); - *length = next_packet_it_->payload_length; - *time_ms = next_packet_it_->time_offset_ms; - next_packet_it_++; - - return 0; - } - - private: - // A marker of an RTP packet within the file. - struct RtpPacketMarker { - uint32_t packet_number; // One-based index (like in WireShark) - uint32_t time_offset_ms; - uint32_t source_ip; - uint32_t dest_ip; - uint16_t source_port; - uint16_t dest_port; - RTPHeader rtp_header; - int32_t pos_in_file; // Byte offset of payload from start of file. - uint32_t payload_length; - }; - - typedef std::vector::iterator PacketIterator; - typedef std::map > SsrcMap; - typedef std::map >::iterator SsrcMapIterator; - - int ReadGlobalHeader() { - uint32_t magic; - TRY(Read(&magic, false)); - if (magic == kPcapBOMSwapOrder) { - swap_pcap_byte_order_ = true; - } else if (magic == kPcapBOMNoSwapOrder) { - swap_pcap_byte_order_ = false; - } else { - return kResultFail; - } - - uint16_t version_major; - uint16_t version_minor; - TRY(Read(&version_major, false)); - TRY(Read(&version_minor, false)); - if (version_major != kPcapVersionMajor || - version_minor != kPcapVersionMinor) { - return kResultFail; - } - - int32_t this_zone; // GMT to local correction. - uint32_t sigfigs; // Accuracy of timestamps. - uint32_t snaplen; // Max length of captured packets, in octets. - uint32_t network; // Data link type. - TRY(Read(&this_zone, false)); - TRY(Read(&sigfigs, false)); - TRY(Read(&snaplen, false)); - TRY(Read(&network, false)); - - // Accept only LINKTYPE_NULL and LINKTYPE_ETHERNET. - // See: http://www.tcpdump.org/linktypes.html - if (network != kLinktypeNull && network != kLinktypeEthernet) { - return kResultFail; - } - - return kResultSuccess; - } - - int ReadPacket(int32_t* next_packet_pos, uint32_t stream_start_ms, - uint32_t number) { - assert(next_packet_pos); - - uint32_t ts_sec; // Timestamp seconds. - uint32_t ts_usec; // Timestamp microseconds. - uint32_t incl_len; // Number of octets of packet saved in file. - uint32_t orig_len; // Actual length of packet. - TRY(Read(&ts_sec, false)); - TRY(Read(&ts_usec, false)); - TRY(Read(&incl_len, false)); - TRY(Read(&orig_len, false)); - - *next_packet_pos = ftell(file_) + incl_len; - - RtpPacketMarker marker = {0}; - marker.packet_number = number; - marker.time_offset_ms = CalcTimeDelta(ts_sec, ts_usec, stream_start_ms); - TRY(ReadPacketHeader(&marker)); - marker.pos_in_file = ftell(file_); - - if (marker.payload_length > sizeof(read_buffer_)) { - printf("Packet too large!\n"); - return kResultFail; - } - TRY(Read(read_buffer_, marker.payload_length)); - - ModuleRTPUtility::RTPHeaderParser rtp_parser(read_buffer_, - marker.payload_length); - if (rtp_parser.RTCP()) { - rtp_parser.ParseRtcp(&marker.rtp_header); - packets_.push_back(marker); - } else { - if (!rtp_parser.Parse(marker.rtp_header, NULL)) { - DEBUG_LOG("Not recognized as RTP/RTCP"); - return kResultSkip; - } - - uint32_t ssrc = marker.rtp_header.ssrc; - packets_by_ssrc_[ssrc].push_back(marker.packet_number); - packets_.push_back(marker); - } - - return kResultSuccess; - } - - int ReadPacketHeader(RtpPacketMarker* marker) { - int32_t file_pos = ftell(file_); - - // Check for BSD null/loopback frame header. The header is just 4 bytes in - // native byte order, so we check for both versions as we don't care about - // the header as such and will likely fail reading the IP header if this is - // something else than null/loopback. - uint32_t protocol; - TRY(Read(&protocol, true)); - if (protocol == kBsdNullLoopback1 || protocol == kBsdNullLoopback2) { - int result = ReadXxpIpHeader(marker); - DEBUG_LOG("Recognized loopback frame"); - if (result != kResultSkip) { - return result; - } - } - - TRY(fseek(file_, file_pos, SEEK_SET)); - - // Check for Ethernet II, IP frame header. - uint16_t type; - TRY(Skip(kEthernetIIHeaderMacSkip)); // Source+destination MAC. - TRY(Read(&type, true)); - if (type == kEthertypeIp) { - int result = ReadXxpIpHeader(marker); - DEBUG_LOG("Recognized ethernet 2 frame"); - if (result != kResultSkip) { - return result; - } - } - - return kResultSkip; - } - - uint32_t CalcTimeDelta(uint32_t ts_sec, uint32_t ts_usec, uint32_t start_ms) { - // Round to nearest ms. - uint64_t t2_ms = ((static_cast(ts_sec) * 1000000) + ts_usec + - 500) / 1000; - uint64_t t1_ms = static_cast(start_ms); - if (t2_ms < t1_ms) { - return 0; - } else { - return t2_ms - t1_ms; - } - } - - int ReadXxpIpHeader(RtpPacketMarker* marker) { - assert(marker); - - uint16_t version; - uint16_t length; - uint16_t id; - uint16_t fragment; - uint16_t protocol; - uint16_t checksum; - TRY(Read(&version, true)); - TRY(Read(&length, true)); - TRY(Read(&id, true)); - TRY(Read(&fragment, true)); - TRY(Read(&protocol, true)); - TRY(Read(&checksum, true)); - TRY(Read(&marker->source_ip, true)); - TRY(Read(&marker->dest_ip, true)); - - if (((version >> 12) & 0x000f) != kIpVersion4) { - DEBUG_LOG("IP header is not IPv4"); - return kResultSkip; - } - - if (fragment != kFragmentOffsetClear && - fragment != kFragmentOffsetDoNotFragment) { - DEBUG_LOG("IP fragments cannot be handled"); - return kResultSkip; - } - - // Skip remaining fields of IP header. - uint16_t header_length = (version & 0x0f00) >> (8 - 2); - assert(header_length >= kMinIpHeaderLength); - TRY(Skip(header_length - kMinIpHeaderLength)); - - protocol = protocol & 0x00ff; - if (protocol == kProtocolTcp) { - DEBUG_LOG("TCP packets are not handled"); - return kResultSkip; - } else if (protocol == kProtocolUdp) { - uint16_t length; - uint16_t checksum; - TRY(Read(&marker->source_port, true)); - TRY(Read(&marker->dest_port, true)); - TRY(Read(&length, true)); - TRY(Read(&checksum, true)); - marker->payload_length = length - kUdpHeaderLength; - } else { - DEBUG_LOG("Unknown transport (expected UDP or TCP)"); - return kResultSkip; - } - - return kResultSuccess; - } - - int Read(uint32_t* out, bool expect_network_order) { - uint32_t tmp = 0; - if (fread(&tmp, 1, sizeof(uint32_t), file_) != sizeof(uint32_t)) { - return kResultFail; - } - if ((!expect_network_order && swap_pcap_byte_order_) || - (expect_network_order && swap_network_byte_order_)) { - tmp = ((tmp >> 24) & 0x000000ff) | (tmp << 24) | - ((tmp >> 8) & 0x0000ff00) | ((tmp << 8) & 0x00ff0000); - } - *out = tmp; - return kResultSuccess; - } - - int Read(uint16_t* out, bool expect_network_order) { - uint16_t tmp = 0; - if (fread(&tmp, 1, sizeof(uint16_t), file_) != sizeof(uint16_t)) { - return kResultFail; - } - if ((!expect_network_order && swap_pcap_byte_order_) || - (expect_network_order && swap_network_byte_order_)) { - tmp = ((tmp >> 8) & 0x00ff) | (tmp << 8); - } - *out = tmp; - return kResultSuccess; - } - - int Read(uint8_t* out, uint32_t count) { - if (fread(out, 1, count, file_) != count) { - return kResultFail; - } - return kResultSuccess; - } - - int Read(int32_t* out, bool expect_network_order) { - int32_t tmp = 0; - if (fread(&tmp, 1, sizeof(uint32_t), file_) != sizeof(uint32_t)) { - return kResultFail; - } - if ((!expect_network_order && swap_pcap_byte_order_) || - (expect_network_order && swap_network_byte_order_)) { - tmp = ((tmp >> 24) & 0x000000ff) | (tmp << 24) | - ((tmp >> 8) & 0x0000ff00) | ((tmp << 8) & 0x00ff0000); - } - *out = tmp; - return kResultSuccess; - } - - int Skip(uint32_t length) { - if (fseek(file_, length, SEEK_CUR) != 0) { - return kResultFail; - } - return kResultSuccess; - } - - FILE* file_; - bool swap_pcap_byte_order_; - bool swap_network_byte_order_; - uint8_t read_buffer_[kMaxReadBufferSize]; - - SsrcMap packets_by_ssrc_; - std::vector packets_; - PacketIterator next_packet_it_; - - DISALLOW_COPY_AND_ASSIGN(PcapFileReaderImpl); -}; - -RtpPacketSourceInterface* CreatePcapFileReader(const std::string& filename) { - scoped_ptr impl(new PcapFileReaderImpl()); - if (impl->Initialize(filename) != 0) { - return NULL; - } - return impl.release(); -} -} // namespace rtpplayer -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/pcap_file_reader.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/pcap_file_reader.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/pcap_file_reader.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/pcap_file_reader.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_PCAP_FILE_READER_H_ -#define WEBRTC_MODULES_VIDEO_CODING_TEST_PCAP_FILE_READER_H_ - -#include - -namespace webrtc { -namespace rtpplayer { - -class RtpPacketSourceInterface; - -RtpPacketSourceInterface* CreatePcapFileReader(const std::string& filename); - -} // namespace rtpplayer -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_PCAP_FILE_READER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/pcap_file_reader_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/pcap_file_reader_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/pcap_file_reader_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/pcap_file_reader_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "testing/gtest/include/gtest/gtest.h" -#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" -#include "webrtc/modules/video_coding/main/test/pcap_file_reader.h" -#include "webrtc/modules/video_coding/main/test/rtp_player.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/test/testsupport/fileutils.h" - -namespace webrtc { -namespace rtpplayer { - -typedef std::map PacketsPerSsrc; - -class TestPcapFileReader : public ::testing::Test { - public: - void Init(const std::string& filename) { - std::string filepath = - test::ResourcePath("video_coding/" + filename, "pcap"); - rtp_packet_source_.reset(CreatePcapFileReader(filepath)); - ASSERT_TRUE(rtp_packet_source_.get() != NULL); - } - - int CountRtpPackets() { - const uint32_t kBufferSize = 4096; - uint8_t data[kBufferSize]; - uint32_t length = kBufferSize; - uint32_t dummy_time_ms = 0; - int c = 0; - while (rtp_packet_source_->NextPacket(data, &length, &dummy_time_ms) == 0) { - EXPECT_GE(kBufferSize, length); - length = kBufferSize; - c++; - } - return c; - } - - PacketsPerSsrc CountRtpPacketsPerSsrc() { - const uint32_t kBufferSize = 4096; - uint8_t data[kBufferSize]; - uint32_t length = kBufferSize; - uint32_t dummy_time_ms = 0; - PacketsPerSsrc pps; - while (rtp_packet_source_->NextPacket(data, &length, &dummy_time_ms) == 0) { - EXPECT_GE(kBufferSize, length); - length = kBufferSize; - - ModuleRTPUtility::RTPHeaderParser rtp_header_parser(data, length); - webrtc::RTPHeader header; - if (!rtp_header_parser.RTCP() && rtp_header_parser.Parse(header, NULL)) { - pps[header.ssrc]++; - } - } - return pps; - } - - private: - scoped_ptr rtp_packet_source_; -}; - -TEST_F(TestPcapFileReader, TestEthernetIIFrame) { - Init("frame-ethernet-ii"); - EXPECT_EQ(368, CountRtpPackets()); -} - -TEST_F(TestPcapFileReader, TestLoopbackFrame) { - Init("frame-loopback"); - EXPECT_EQ(491, CountRtpPackets()); -} - -TEST_F(TestPcapFileReader, TestTwoSsrc) { - Init("ssrcs-2"); - PacketsPerSsrc pps = CountRtpPacketsPerSsrc(); - EXPECT_EQ(2UL, pps.size()); - EXPECT_EQ(370, pps[0x78d48f61]); - EXPECT_EQ(60, pps[0xae94130b]); -} - -TEST_F(TestPcapFileReader, TestThreeSsrc) { - Init("ssrcs-3"); - PacketsPerSsrc pps = CountRtpPacketsPerSsrc(); - EXPECT_EQ(3UL, pps.size()); - EXPECT_EQ(162, pps[0x938c5eaa]); - EXPECT_EQ(113, pps[0x59fe6ef0]); - EXPECT_EQ(61, pps[0xed2bd2ac]); -} - -} // namespace rtpplayer -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/quality_modes_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/quality_modes_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/quality_modes_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/quality_modes_test.cc 2015-02-03 14:33:37.000000000 +0000 @@ -32,7 +32,7 @@ { SimulatedClock clock(0); NullEventFactory event_factory; - VideoCodingModule* vcm = VideoCodingModule::Create(1, &clock, &event_factory); + VideoCodingModule* vcm = VideoCodingModule::Create(&clock, &event_factory); QualityModesTest QMTest(vcm, &clock); QMTest.Perform(args); VideoCodingModule::Destroy(vcm); @@ -249,7 +249,6 @@ VideoContentMetrics* contentMetrics = NULL; // setting user frame rate - _vpm->SetMaxFramerate((uint32_t)(_nativeFrameRate+ 0.5f)); // for starters: keeping native values: _vpm->SetTargetResolution(_width, _height, (uint32_t)(_frameRate+ 0.5f)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/receiver_tests.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/receiver_tests.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/receiver_tests.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/receiver_tests.h 2015-02-03 14:33:37.000000000 +0000 @@ -30,7 +30,7 @@ virtual int32_t OnReceivedPayloadData( const uint8_t* payload_data, const uint16_t payload_size, - const webrtc::WebRtcRTPHeader* rtp_header) { + const webrtc::WebRtcRTPHeader* rtp_header) OVERRIDE { return vcm_->IncomingPacket(payload_data, payload_size, *rtp_header); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_file_reader.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_file_reader.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_file_reader.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_file_reader.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/video_coding/main/test/rtp_file_reader.h" - -#ifdef WIN32 -#include -#include -#else -#include -#endif -#include -#include - -#include "webrtc/modules/video_coding/main/test/rtp_player.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" - -namespace webrtc { -namespace rtpplayer { - -enum { - kResultFail = -1, - kResultSuccess = 0, - - kFirstLineLength = 40, // More than needed to read the ID line. - kPacketHeaderSize = 8 // Rtpplay packet header size in bytes. -}; - -#if 1 -# define DEBUG_LOG(text) -# define DEBUG_LOG1(text, arg) -#else -# define DEBUG_LOG(text) (printf(text "\n")) -# define DEBUG_LOG1(text, arg) (printf(text "\n", arg)) -#endif - -#define TRY(expr) \ - do { \ - if ((expr) < 0) { \ - DEBUG_LOG1("FAIL at " __FILE__ ":%d", __LINE__); \ - return kResultFail; \ - } \ - } while (0) - -// Read RTP packets from file in rtpdump format, as documented at: -// http://www.cs.columbia.edu/irt/software/rtptools/ -class RtpFileReaderImpl : public RtpPacketSourceInterface { - public: - RtpFileReaderImpl() : file_(NULL) {} - virtual ~RtpFileReaderImpl() { - if (file_ != NULL) { - fclose(file_); - file_ = NULL; - } - } - - int Initialize(const std::string& filename) { - file_ = fopen(filename.c_str(), "rb"); - if (file_ == NULL) { - printf("ERROR: Can't open file: %s\n", filename.c_str()); - return kResultFail; - } - - char firstline[kFirstLineLength + 1] = {0}; - if (fgets(firstline, kFirstLineLength, file_) == NULL) { - DEBUG_LOG("ERROR: Can't read from file\n"); - return kResultFail; - } - if (strncmp(firstline, "#!rtpplay", 9) == 0) { - if (strncmp(firstline, "#!rtpplay1.0", 12) != 0) { - DEBUG_LOG("ERROR: wrong rtpplay version, must be 1.0\n"); - return kResultFail; - } - } else if (strncmp(firstline, "#!RTPencode", 11) == 0) { - if (strncmp(firstline, "#!RTPencode1.0", 14) != 0) { - DEBUG_LOG("ERROR: wrong RTPencode version, must be 1.0\n"); - return kResultFail; - } - } else { - DEBUG_LOG("ERROR: wrong file format of input file\n"); - return kResultFail; - } - - uint32_t start_sec; - uint32_t start_usec; - uint32_t source; - uint16_t port; - uint16_t padding; - TRY(Read(&start_sec)); - TRY(Read(&start_usec)); - TRY(Read(&source)); - TRY(Read(&port)); - TRY(Read(&padding)); - - return kResultSuccess; - } - - virtual int NextPacket(uint8_t* rtp_data, uint32_t* length, - uint32_t* time_ms) { - assert(rtp_data); - assert(length); - assert(time_ms); - - uint16_t len; - uint16_t plen; - uint32_t offset; - TRY(Read(&len)); - TRY(Read(&plen)); - TRY(Read(&offset)); - - // Use 'len' here because a 'plen' of 0 specifies rtcp. - len -= kPacketHeaderSize; - if (*length < len) { - return kResultFail; - } - if (fread(rtp_data, 1, len, file_) != len) { - return kResultFail; - } - - *length = len; - *time_ms = offset; - return kResultSuccess; - } - - private: - int Read(uint32_t* out) { - assert(out); - uint32_t tmp = 0; - if (fread(&tmp, 1, sizeof(uint32_t), file_) != sizeof(uint32_t)) { - return kResultFail; - } - *out = ntohl(tmp); - return kResultSuccess; - } - - int Read(uint16_t* out) { - assert(out); - uint16_t tmp = 0; - if (fread(&tmp, 1, sizeof(uint16_t), file_) != sizeof(uint16_t)) { - return kResultFail; - } - *out = ntohs(tmp); - return kResultSuccess; - } - - FILE* file_; - - DISALLOW_COPY_AND_ASSIGN(RtpFileReaderImpl); -}; - -RtpPacketSourceInterface* CreateRtpFileReader(const std::string& filename) { - scoped_ptr impl(new RtpFileReaderImpl()); - if (impl->Initialize(filename) != 0) { - return NULL; - } - return impl.release(); -} -} // namespace rtpplayer -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_file_reader.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_file_reader.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_file_reader.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_file_reader.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_MAIN_TEST_RTP_FILE_READER_H_ -#define WEBRTC_MODULES_VIDEO_CODING_MAIN_TEST_RTP_FILE_READER_H_ - -#include - -namespace webrtc { -namespace rtpplayer { - -class RtpPacketSourceInterface; - -RtpPacketSourceInterface* CreateRtpFileReader(const std::string& filename); - -} // namespace rtpplayer -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_MAIN_TEST_RTP_FILE_READER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_file_reader_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_file_reader_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_file_reader_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_file_reader_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "testing/gtest/include/gtest/gtest.h" -#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" -#include "webrtc/modules/video_coding/main/test/rtp_file_reader.h" -#include "webrtc/modules/video_coding/main/test/rtp_player.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/test/testsupport/fileutils.h" - -namespace webrtc { -namespace rtpplayer { - -class TestRtpFileReader : public ::testing::Test { - public: - void Init(const std::string& filename) { - std::string filepath = - test::ResourcePath("video_coding/" + filename, "rtp"); - rtp_packet_source_.reset(CreateRtpFileReader(filepath)); - ASSERT_TRUE(rtp_packet_source_.get() != NULL); - } - - int CountRtpPackets() { - const uint32_t kBufferSize = 4096; - uint8_t data[kBufferSize]; - uint32_t length = kBufferSize; - uint32_t dummy_time_ms = 0; - int c = 0; - while (rtp_packet_source_->NextPacket(data, &length, &dummy_time_ms) == 0) { - EXPECT_GE(kBufferSize, length); - length = kBufferSize; - c++; - } - return c; - } - - private: - scoped_ptr rtp_packet_source_; -}; - -TEST_F(TestRtpFileReader, Test60Packets) { - Init("pltype103"); - EXPECT_EQ(60, CountRtpPackets()); -} - -} // namespace rtpplayer -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_player.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_player.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_player.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_player.cc 2015-02-03 14:33:37.000000000 +0000 @@ -19,12 +19,11 @@ #include "webrtc/modules/rtp_rtcp/interface/rtp_receiver.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" #include "webrtc/modules/video_coding/main/source/internal_defines.h" -#include "webrtc/modules/video_coding/main/test/pcap_file_reader.h" -#include "webrtc/modules/video_coding/main/test/rtp_file_reader.h" #include "webrtc/modules/video_coding/main/test/test_util.h" #include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/test/rtp_file_reader.h" #if 1 # define DEBUG_LOG1(text, arg) @@ -61,7 +60,7 @@ uint16_t seq_num() const { return seq_num_; } private: - scoped_array data_; + scoped_ptr data_; uint32_t length_; int64_t resend_time_ms_; uint32_t ssrc_; @@ -273,7 +272,7 @@ LostPackets* lost_packets) : rtp_header_parser_(RtpHeaderParser::Create()), rtp_payload_registry_(new RTPPayloadRegistry( - 0, RTPPayloadStrategy::CreateStrategy(false))), + RTPPayloadStrategy::CreateStrategy(false))), rtp_module_(), payload_sink_(), ssrc_(ssrc), @@ -323,7 +322,7 @@ public: RtpPlayerImpl(PayloadSinkFactoryInterface* payload_sink_factory, const PayloadTypes& payload_types, Clock* clock, - scoped_ptr* packet_source, + scoped_ptr* packet_source, float loss_rate, uint32_t rtt_ms, bool reordering) : ssrc_handlers_(payload_sink_factory, payload_types), clock_(clock), @@ -337,9 +336,7 @@ no_loss_startup_(100), end_of_file_(false), reordering_(false), - reorder_buffer_(), - next_packet_(), - next_packet_length_(0) { + reorder_buffer_() { assert(clock); assert(packet_source); assert(packet_source->get()); @@ -351,8 +348,9 @@ virtual int NextPacket(int64_t time_now) { // Send any packets ready to be resent. - RawRtpPacket* packet; - while ((packet = lost_packets_.NextPacketToResend(time_now))) { + for (RawRtpPacket* packet = lost_packets_.NextPacketToResend(time_now); + packet != NULL; + packet = lost_packets_.NextPacketToResend(time_now)) { int ret = SendPacket(packet->data(), packet->length()); if (ret > 0) { printf("Resend: %08x:%u\n", packet->ssrc(), packet->seq_num()); @@ -368,22 +366,23 @@ // Send any packets from packet source. if (!end_of_file_ && (TimeUntilNextPacket() == 0 || first_packet_)) { if (first_packet_) { - next_packet_length_ = sizeof(next_packet_); - if (packet_source_->NextPacket(next_packet_, &next_packet_length_, - &next_rtp_time_) != 0) { + if (!packet_source_->NextPacket(&next_packet_)) return 0; - } - first_packet_rtp_time_ = next_rtp_time_; + first_packet_rtp_time_ = next_packet_.time_ms; first_packet_time_ms_ = clock_->TimeInMilliseconds(); first_packet_ = false; } if (reordering_ && reorder_buffer_.get() == NULL) { - reorder_buffer_.reset(new RawRtpPacket(next_packet_, - next_packet_length_, 0, 0)); + reorder_buffer_.reset( + new RawRtpPacket(next_packet_.data, + static_cast(next_packet_.length), + 0, + 0)); return 0; } - int ret = SendPacket(next_packet_, next_packet_length_); + int ret = SendPacket(next_packet_.data, + static_cast(next_packet_.length)); if (reorder_buffer_.get()) { SendPacket(reorder_buffer_->data(), reorder_buffer_->length()); reorder_buffer_.reset(NULL); @@ -392,13 +391,11 @@ return ret; } - next_packet_length_ = sizeof(next_packet_); - if (packet_source_->NextPacket(next_packet_, &next_packet_length_, - &next_rtp_time_) != 0) { + if (!packet_source_->NextPacket(&next_packet_)) { end_of_file_ = true; return 0; } - else if (next_packet_length_ == 0) { + else if (next_packet_.length == 0) { return 0; } } @@ -456,7 +453,8 @@ SsrcHandlers ssrc_handlers_; Clock* clock_; - scoped_ptr packet_source_; + scoped_ptr packet_source_; + test::RtpFileReader::Packet next_packet_; uint32_t next_rtp_time_; bool first_packet_; int64_t first_packet_rtp_time_; @@ -468,8 +466,6 @@ bool end_of_file_; bool reordering_; scoped_ptr reorder_buffer_; - uint8_t next_packet_[kMaxPacketBufferSize]; - uint32_t next_packet_length_; DISALLOW_IMPLICIT_CONSTRUCTORS(RtpPlayerImpl); }; @@ -478,10 +474,11 @@ PayloadSinkFactoryInterface* payload_sink_factory, Clock* clock, const PayloadTypes& payload_types, float loss_rate, uint32_t rtt_ms, bool reordering) { - scoped_ptr packet_source( - CreateRtpFileReader(input_filename)); + scoped_ptr packet_source(test::RtpFileReader::Create( + test::RtpFileReader::kRtpDump, input_filename)); if (packet_source.get() == NULL) { - packet_source.reset(CreatePcapFileReader(input_filename)); + packet_source.reset(test::RtpFileReader::Create(test::RtpFileReader::kPcap, + input_filename)); if (packet_source.get() == NULL) { return NULL; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_player.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_player.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_player.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/rtp_player.h 2015-02-03 14:33:37.000000000 +0000 @@ -44,20 +44,6 @@ typedef std::vector PayloadTypes; typedef std::vector::const_iterator PayloadTypesIterator; -// Implemented by something that can provide RTP packets, for instance a file -// format parser such as the rtp_file_reader or the pcap_file_reader. -class RtpPacketSourceInterface { - public: - virtual ~RtpPacketSourceInterface() {} - - // Read next RTP packet into buffer pointed to by rtp_data. On call, 'length' - // field must be filled in with the size of the buffer. The actual size of - // the packet is available in 'length' upon returning. Time in milliseconds - // from start of stream is returned in 'time_ms'. - virtual int NextPacket(uint8_t* rtp_data, uint32_t* length, - uint32_t* time_ms) = 0; -}; - // Implemented by RtpPlayer and given to client as a means to retrieve // information about a specific RTP stream. class RtpStreamInterface { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/test_callbacks.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/test_callbacks.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/test_callbacks.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/test_callbacks.cc 2015-02-03 14:33:37.000000000 +0000 @@ -82,6 +82,9 @@ rtpInfo.type.Video.codecHeader.VP8.pictureId = videoHdr->codecHeader.VP8.pictureId; break; + case webrtc::kRtpVideoGeneric: + // Leave for now, until we add kRtpVideoVp9 to RTP. + break; default: assert(false); return -1; @@ -406,7 +409,7 @@ bool RTPSendCompleteCallback::UnifomLoss(double lossPct) { - double randVal = (std::rand() + 1.0)/(RAND_MAX + 1.0); + double randVal = (rand() + 1.0) / (RAND_MAX + 1.0); return randVal < lossPct/100; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/test_callbacks.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/test_callbacks.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/test_callbacks.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/test_callbacks.h 2015-02-03 14:33:37.000000000 +0000 @@ -44,14 +44,14 @@ void RegisterTransportCallback(VCMPacketizationCallback* transport); // Process encoded data received from the encoder, pass stream to the // VCMReceiver module - int32_t SendData(const FrameType frameType, - const uint8_t payloadType, - const uint32_t timeStamp, - int64_t capture_time_ms, - const uint8_t* payloadData, - const uint32_t payloadSize, - const RTPFragmentationHeader& fragmentationHeader, - const RTPVideoHeader* videoHdr); + virtual int32_t SendData(const FrameType frameType, + const uint8_t payloadType, + const uint32_t timeStamp, + int64_t capture_time_ms, + const uint8_t* payloadData, + const uint32_t payloadSize, + const RTPFragmentationHeader& fragmentationHeader, + const RTPVideoHeader* videoHdr) OVERRIDE; // Register exisitng VCM. Currently - encode and decode under same module. void RegisterReceiverVCM(VideoCodingModule *vcm) {_VCMReceiver = vcm;} // Return size of last encoded frame data (all frames in the sequence) @@ -101,14 +101,14 @@ virtual ~VCMRTPEncodeCompleteCallback() {} // Process encoded data received from the encoder, pass stream to the // RTP module - int32_t SendData(const FrameType frameType, - const uint8_t payloadType, - const uint32_t timeStamp, - int64_t capture_time_ms, - const uint8_t* payloadData, - const uint32_t payloadSize, - const RTPFragmentationHeader& fragmentationHeader, - const RTPVideoHeader* videoHdr); + virtual int32_t SendData(const FrameType frameType, + const uint8_t payloadType, + const uint32_t timeStamp, + int64_t capture_time_ms, + const uint8_t* payloadData, + const uint32_t payloadSize, + const RTPFragmentationHeader& fragmentationHeader, + const RTPVideoHeader* videoHdr) OVERRIDE; // Return size of last encoded frame. Value good for one call // (resets to zero after call to inform test of frame drop) float EncodedBytes(); @@ -144,7 +144,7 @@ _decodedFile(decodedFile), _decodedBytes(0) {} virtual ~VCMDecodeCompleteCallback() {} // Write decoded frame into file - int32_t FrameToRender(webrtc::I420VideoFrame& videoFrame); + virtual int32_t FrameToRender(webrtc::I420VideoFrame& videoFrame) OVERRIDE; int32_t DecodedBytes(); private: FILE* _decodedFile; @@ -165,9 +165,9 @@ void SetRtpModule(RtpRtcp* rtp_module) { _rtp = rtp_module; } // Send Packet to receive side RTP module - virtual int SendPacket(int channel, const void *data, int len); + virtual int SendPacket(int channel, const void *data, int len) OVERRIDE; // Send RTCP Packet to receive side RTP module - virtual int SendRTCPPacket(int channel, const void *data, int len); + virtual int SendRTCPPacket(int channel, const void *data, int len) OVERRIDE; // Set percentage of channel loss in the network void SetLossPct(double lossPct); // Set average size of burst loss @@ -209,8 +209,8 @@ public: PacketRequester(RtpRtcp& rtp) : _rtp(rtp) {} - int32_t ResendPackets(const uint16_t* sequenceNumbers, - uint16_t length); + virtual int32_t ResendPackets(const uint16_t* sequenceNumbers, + uint16_t length) OVERRIDE; private: webrtc::RtpRtcp& _rtp; }; @@ -219,7 +219,7 @@ class KeyFrameReqTest: public VCMFrameTypeCallback { public: - int32_t RequestKeyFrame(); + virtual int32_t RequestKeyFrame() OVERRIDE; }; @@ -228,8 +228,8 @@ { public: SendStatsTest() : _framerate(15), _bitrate(500) {} - int32_t SendStatistics(const uint32_t bitRate, - const uint32_t frameRate); + virtual int32_t SendStatistics(const uint32_t bitRate, + const uint32_t frameRate) OVERRIDE; void set_framerate(uint32_t frameRate) {_framerate = frameRate;} void set_bitrate(uint32_t bitrate) {_bitrate = bitrate;} private: @@ -245,12 +245,12 @@ VideoProtectionCallback(); virtual ~VideoProtectionCallback(); void RegisterRtpModule(RtpRtcp* rtp) {_rtp = rtp;} - int32_t ProtectionRequest( + virtual int32_t ProtectionRequest( const FecProtectionParams* delta_fec_params, const FecProtectionParams* key_fec_params, uint32_t* sent_video_rate_bps, uint32_t* sent_nack_rate_bps, - uint32_t* sent_fec_rate_bps); + uint32_t* sent_fec_rate_bps) OVERRIDE; FecProtectionParams DeltaFecParameters() const; FecProtectionParams KeyFecParameters() const; private: diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/tester_main.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/tester_main.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/tester_main.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/tester_main.cc 2015-02-03 14:33:37.000000000 +0000 @@ -63,6 +63,8 @@ args.codecName = FLAGS_codec; if (args.codecName == "VP8") { args.codecType = kVideoCodecVP8; + } else if (args.codecName == "VP9") { + args.codecType = kVideoCodecVP9; } else if (args.codecName == "I420") { args.codecType = kVideoCodecI420; } else { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/test_util.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/test_util.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/test_util.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/test_util.cc 2015-02-03 14:33:37.000000000 +0000 @@ -151,6 +151,7 @@ if (strncmp(plname,"VP8" , 3) == 0) { return webrtc::kRtpVideoVp8; } else { - return webrtc::kRtpVideoNone; // Default value + // Default value. + return webrtc::kRtpVideoGeneric; } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/test_util.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/test_util.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/test_util.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/test_util.h 2015-02-03 14:33:37.000000000 +0000 @@ -17,9 +17,9 @@ #include +#include "webrtc/base/constructormagic.h" #include "webrtc/modules/interface/module_common_types.h" #include "webrtc/modules/video_coding/main/interface/video_coding.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/system_wrappers/interface/event_wrapper.h" enum { kMaxNackListSize = 250 }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.cc 2015-02-03 14:33:37.000000000 +0000 @@ -54,19 +54,19 @@ virtual int32_t OnReceivedPayloadData( const uint8_t* payload_data, const uint16_t payload_size, - const WebRtcRTPHeader* rtp_header) { + const WebRtcRTPHeader* rtp_header) OVERRIDE { return vcm_->IncomingPacket(payload_data, payload_size, *rtp_header); } virtual bool OnRecoveredPacket(const uint8_t* packet, - int packet_length) { + int packet_length) OVERRIDE { // We currently don't handle FEC. return true; } // VCMPacketRequestCallback virtual int32_t ResendPackets(const uint16_t* sequence_numbers, - uint16_t length) { + uint16_t length) OVERRIDE { stream_->ResendPackets(sequence_numbers, length); return 0; } @@ -108,9 +108,13 @@ }; VcmPayloadSinkFactory::VcmPayloadSinkFactory( - const std::string& base_out_filename, Clock* clock, bool protection_enabled, - VCMVideoProtection protection_method, uint32_t rtt_ms, - uint32_t render_delay_ms, uint32_t min_playout_delay_ms) + const std::string& base_out_filename, + Clock* clock, + bool protection_enabled, + VCMVideoProtection protection_method, + uint32_t rtt_ms, + uint32_t render_delay_ms, + uint32_t min_playout_delay_ms) : base_out_filename_(base_out_filename), clock_(clock), protection_enabled_(protection_enabled), @@ -120,8 +124,7 @@ min_playout_delay_ms_(min_playout_delay_ms), null_event_factory_(new NullEventFactory()), crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), - sinks_(), - next_id_(1) { + sinks_() { assert(clock); assert(crit_sect_.get()); } @@ -136,7 +139,7 @@ CriticalSectionScoped cs(crit_sect_.get()); scoped_ptr vcm( - VideoCodingModule::Create(next_id_++, clock_, null_event_factory_.get())); + VideoCodingModule::Create(clock_, null_event_factory_.get())); if (vcm.get() == NULL) { return NULL; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.h 2015-02-03 14:33:37.000000000 +0000 @@ -11,9 +11,9 @@ #include #include +#include "webrtc/base/constructormagic.h" #include "webrtc/modules/video_coding/main/interface/video_coding_defines.h" #include "webrtc/modules/video_coding/main/test/rtp_player.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" class NullEventFactory; @@ -56,7 +56,6 @@ scoped_ptr null_event_factory_; scoped_ptr crit_sect_; Sinks sinks_; - int next_id_; DISALLOW_IMPLICIT_CONSTRUCTORS(VcmPayloadSinkFactory); }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/OWNERS 2015-02-03 14:33:36.000000000 +0000 @@ -1,4 +1,5 @@ stefan@webrtc.org -mikhal@webrtc.org marpan@webrtc.org henrik.lundin@webrtc.org + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -# Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libvideo_coding_utility -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := \ - exp_filter.cc \ - frame_dropper.cc \ - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../../../../system_wrappers/interface - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/exp_filter.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/exp_filter.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/exp_filter.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/exp_filter.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/video_coding/utility/include/exp_filter.h" - -#include - -namespace webrtc { - -void -VCMExpFilter::Reset(float alpha) -{ - _alpha = alpha; - _filtered = -1.0; -} - -float -VCMExpFilter::Apply(float exp, float sample) -{ - if (_filtered == -1.0) - { - // Initialize filtered bit rates - _filtered = sample; - } - else if (exp == 1.0) - { - _filtered = _alpha * _filtered + (1 - _alpha) * sample; - } - else - { - float alpha = pow(_alpha, exp); - _filtered = alpha * _filtered + (1 - alpha) * sample; - } - if (_max != -1 && _filtered > _max) - { - _filtered = _max; - } - return _filtered; -} - -void -VCMExpFilter::UpdateBase(float alpha) -{ - _alpha = alpha; -} - -float -VCMExpFilter::Value() const -{ - return _filtered; -} - -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/frame_dropper.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/frame_dropper.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/frame_dropper.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/frame_dropper.cc 2015-02-03 14:33:37.000000000 +0000 @@ -86,25 +86,27 @@ { _keyFrameSizeAvgKbits.Apply(1, frameSizeKbits); _keyFrameRatio.Apply(1.0, 1.0); - if (frameSizeKbits > _keyFrameSizeAvgKbits.Value()) + if (frameSizeKbits > _keyFrameSizeAvgKbits.filtered()) { // Remove the average key frame size since we // compensate for key frames when adding delta // frames. - frameSizeKbits -= _keyFrameSizeAvgKbits.Value(); + frameSizeKbits -= _keyFrameSizeAvgKbits.filtered(); } else { // Shouldn't be negative, so zero is the lower bound. frameSizeKbits = 0; } - if (_keyFrameRatio.Value() > 1e-5 && 1 / _keyFrameRatio.Value() < _keyFrameSpreadFrames) + if (_keyFrameRatio.filtered() > 1e-5 && + 1 / _keyFrameRatio.filtered() < _keyFrameSpreadFrames) { // We are sending key frames more often than our upper bound for // how much we allow the key frame compensation to be spread // out in time. Therefor we must use the key frame ratio rather // than keyFrameSpreadFrames. - _keyFrameCount = static_cast(1 / _keyFrameRatio.Value() + 0.5); + _keyFrameCount = + static_cast(1 / _keyFrameRatio.filtered() + 0.5); } else { @@ -145,13 +147,14 @@ if (_keyFrameCount > 0) { // Perform the key frame compensation - if (_keyFrameRatio.Value() > 0 && 1 / _keyFrameRatio.Value() < _keyFrameSpreadFrames) + if (_keyFrameRatio.filtered() > 0 && + 1 / _keyFrameRatio.filtered() < _keyFrameSpreadFrames) { - T -= _keyFrameSizeAvgKbits.Value() * _keyFrameRatio.Value(); + T -= _keyFrameSizeAvgKbits.filtered() * _keyFrameRatio.filtered(); } else { - T -= _keyFrameSizeAvgKbits.Value() / _keyFrameSpreadFrames; + T -= _keyFrameSizeAvgKbits.filtered() / _keyFrameSpreadFrames; } _keyFrameCount--; } @@ -232,11 +235,11 @@ _dropCount = 0; } - if (_dropRatio.Value() >= 0.5f) // Drops per keep + if (_dropRatio.filtered() >= 0.5f) // Drops per keep { // limit is the number of frames we should drop between each kept frame // to keep our drop ratio. limit is positive in this case. - float denom = 1.0f - _dropRatio.Value(); + float denom = 1.0f - _dropRatio.filtered(); if (denom < 1e-5) { denom = (float)1e-5; @@ -252,7 +255,7 @@ if (_dropCount < 0) { // Reset the _dropCount since it was negative and should be positive. - if (_dropRatio.Value() > 0.4f) + if (_dropRatio.filtered() > 0.4f) { _dropCount = -_dropCount; } @@ -274,12 +277,13 @@ return false; } } - else if (_dropRatio.Value() > 0.0f && _dropRatio.Value() < 0.5f) // Keeps per drop + else if (_dropRatio.filtered() > 0.0f && + _dropRatio.filtered() < 0.5f) // Keeps per drop { // limit is the number of frames we should keep between each drop // in order to keep the drop ratio. limit is negative in this case, // and the _dropCount is also negative. - float denom = _dropRatio.Value(); + float denom = _dropRatio.filtered(); if (denom < 1e-5) { denom = (float)1e-5; @@ -289,7 +293,7 @@ { // Reset the _dropCount since we have a positive // _dropCount, and it should be negative. - if (_dropRatio.Value() < 0.6f) + if (_dropRatio.filtered() < 0.6f) { _dropCount = -_dropCount; } @@ -350,7 +354,7 @@ { return static_cast(inputFrameRate); } - return inputFrameRate * (1.0f - _dropRatio.Value()); + return inputFrameRate * (1.0f - _dropRatio.filtered()); } // Put a cap on the accumulator, i.e., don't let it grow beyond some level. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/include/exp_filter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/include/exp_filter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/include/exp_filter.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/include/exp_filter.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_EXP_FILTER_H_ -#define WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_EXP_FILTER_H_ - -namespace webrtc -{ - -/**********************/ -/* ExpFilter class */ -/**********************/ - -class VCMExpFilter -{ -public: - VCMExpFilter(float alpha, float max = -1.0) : _alpha(alpha), _filtered(-1.0), _max(max) {} - - // Resets the filter to its initial state, and resets alpha to the given value - // - // Input: - // - alpha : the new value of the filter factor base. - void Reset(float alpha); - - // Applies the filter with the given exponent on the provided sample - // - // Input: - // - exp : Exponent T in y(k) = alpha^T * y(k-1) + (1 - alpha^T) * x(k) - // - sample : x(k) in the above filter equation - float Apply(float exp, float sample); - - // Return current filtered value: y(k) - // - // Return value : The current filter output - float Value() const; - - // Change the filter factor base - // - // Input: - // - alpha : The new filter factor base. - void UpdateBase(float alpha); - -private: - float _alpha; // Filter factor base - float _filtered; // Current filter output - const float _max; -}; // end of ExpFilter class - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_EXP_FILTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/include/frame_dropper.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/include/frame_dropper.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/include/frame_dropper.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/include/frame_dropper.h 2015-02-03 14:33:37.000000000 +0000 @@ -11,7 +11,7 @@ #ifndef WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_FRAME_DROPPER_H_ #define WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_FRAME_DROPPER_H_ -#include "webrtc/modules/video_coding/utility/include/exp_filter.h" +#include "webrtc/base/exp_filter.h" #include "webrtc/typedefs.h" namespace webrtc @@ -72,23 +72,23 @@ void UpdateRatio(); void CapAccumulator(); - VCMExpFilter _keyFrameSizeAvgKbits; - VCMExpFilter _keyFrameRatio; - float _keyFrameSpreadFrames; - int32_t _keyFrameCount; - float _accumulator; - float _accumulatorMax; - float _targetBitRate; - bool _dropNext; - VCMExpFilter _dropRatio; - int32_t _dropCount; - float _windowSize; - float _incoming_frame_rate; - bool _wasBelowMax; - bool _enabled; - bool _fastMode; - float _cap_buffer_size; - float _max_time_drops; + rtc::ExpFilter _keyFrameSizeAvgKbits; + rtc::ExpFilter _keyFrameRatio; + float _keyFrameSpreadFrames; + int32_t _keyFrameCount; + float _accumulator; + float _accumulatorMax; + float _targetBitRate; + bool _dropNext; + rtc::ExpFilter _dropRatio; + int32_t _dropCount; + float _windowSize; + float _incoming_frame_rate; + bool _wasBelowMax; + bool _enabled; + bool _fastMode; + float _cap_buffer_size; + float _max_time_drops; }; // end of VCMFrameDropper class } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/OWNERS 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/quality_scaler.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/quality_scaler.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/quality_scaler.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/quality_scaler.cc 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/video_coding/utility/quality_scaler.h" + +namespace webrtc { + +static const int kMinFps = 10; +static const int kMeasureSeconds = 5; +static const int kFramedropPercentThreshold = 60; +static const int kLowQpThresholdDenominator = 3; + +QualityScaler::QualityScaler() + : num_samples_(0), low_qp_threshold_(-1), downscale_shift_(0) { +} + +void QualityScaler::Init(int max_qp) { + ClearSamples(); + downscale_shift_ = 0; + low_qp_threshold_ = max_qp / kLowQpThresholdDenominator ; +} + +void QualityScaler::ReportFramerate(int framerate) { + num_samples_ = static_cast( + kMeasureSeconds * (framerate < kMinFps ? kMinFps : framerate)); +} + +void QualityScaler::ReportEncodedFrame(int qp) { + average_qp_.AddSample(qp); + framedrop_percent_.AddSample(0); +} + +void QualityScaler::ReportDroppedFrame() { + framedrop_percent_.AddSample(100); +} + +QualityScaler::Resolution QualityScaler::GetScaledResolution( + const I420VideoFrame& frame) { + // Both of these should be set through InitEncode -> Should be set by now. + assert(low_qp_threshold_ >= 0); + assert(num_samples_ > 0); + // Update scale factor. + int avg; + if (framedrop_percent_.GetAverage(num_samples_, &avg) && + avg >= kFramedropPercentThreshold) { + AdjustScale(false); + } else if (average_qp_.GetAverage(num_samples_, &avg) && + avg <= low_qp_threshold_) { + AdjustScale(true); + } + + Resolution res; + res.width = frame.width(); + res.height = frame.height(); + + assert(downscale_shift_ >= 0); + for (int shift = downscale_shift_; + shift > 0 && res.width > 1 && res.height > 1; + --shift) { + res.width >>= 1; + res.height >>= 1; + } + + return res; +} + +const I420VideoFrame& QualityScaler::GetScaledFrame( + const I420VideoFrame& frame) { + Resolution res = GetScaledResolution(frame); + if (res.width == frame.width()) + return frame; + + scaler_.Set(frame.width(), + frame.height(), + res.width, + res.height, + kI420, + kI420, + kScaleBox); + if (scaler_.Scale(frame, &scaled_frame_) != 0) + return frame; + + scaled_frame_.set_ntp_time_ms(frame.ntp_time_ms()); + scaled_frame_.set_timestamp(frame.timestamp()); + scaled_frame_.set_render_time_ms(frame.render_time_ms()); + + return scaled_frame_; +} + +QualityScaler::MovingAverage::MovingAverage() : sum_(0) { +} + +void QualityScaler::MovingAverage::AddSample(int sample) { + samples_.push_back(sample); + sum_ += sample; +} + +bool QualityScaler::MovingAverage::GetAverage(size_t num_samples, int* avg) { + assert(num_samples > 0); + if (num_samples > samples_.size()) + return false; + + // Remove old samples. + while (num_samples < samples_.size()) { + sum_ -= samples_.front(); + samples_.pop_front(); + } + + *avg = sum_ / static_cast(num_samples); + return true; +} + +void QualityScaler::MovingAverage::Reset() { + sum_ = 0; + samples_.clear(); +} + +void QualityScaler::ClearSamples() { + average_qp_.Reset(); + framedrop_percent_.Reset(); +} + +void QualityScaler::AdjustScale(bool up) { + downscale_shift_ += up ? -1 : 1; + if (downscale_shift_ < 0) + downscale_shift_ = 0; + ClearSamples(); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/quality_scaler.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/quality_scaler.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/quality_scaler.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/quality_scaler.h 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_CODING_UTILITY_QUALITY_SCALER_H_ +#define WEBRTC_MODULES_VIDEO_CODING_UTILITY_QUALITY_SCALER_H_ + +#include + +#include "webrtc/common_video/libyuv/include/scaler.h" + +namespace webrtc { +class QualityScaler { + public: + struct Resolution { + int width; + int height; + }; + + QualityScaler(); + void Init(int max_qp); + + void ReportFramerate(int framerate); + void ReportEncodedFrame(int qp); + void ReportDroppedFrame(); + + Resolution GetScaledResolution(const I420VideoFrame& frame); + const I420VideoFrame& GetScaledFrame(const I420VideoFrame& frame); + + private: + class MovingAverage { + public: + MovingAverage(); + void AddSample(int sample); + bool GetAverage(size_t num_samples, int* average); + void Reset(); + + private: + int sum_; + std::list samples_; + }; + + void AdjustScale(bool up); + void ClearSamples(); + + Scaler scaler_; + I420VideoFrame scaled_frame_; + + size_t num_samples_; + int low_qp_threshold_; + MovingAverage average_qp_; + MovingAverage framedrop_percent_; + + int downscale_shift_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_CODING_UTILITY_QUALITY_SCALER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/video_coding/utility/quality_scaler.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace webrtc { +namespace { +static const int kNumSeconds = 10; +static const int kWidth = 1920; +static const int kHalfWidth = kWidth / 2; +static const int kHeight = 1080; +static const int kFramerate = 30; +static const int kLowQp = 15; +static const int kNormalQp = 30; +static const int kMaxQp = 56; +} // namespace + +class QualityScalerTest : public ::testing::Test { + protected: + enum ScaleDirection { kScaleDown, kScaleUp }; + + QualityScalerTest() { + input_frame_.CreateEmptyFrame( + kWidth, kHeight, kWidth, kHalfWidth, kHalfWidth); + qs_.Init(kMaxQp); + qs_.ReportFramerate(kFramerate); + } + + void TriggerScale(ScaleDirection scale_direction) { + int initial_width = qs_.GetScaledResolution(input_frame_).width; + for (int i = 0; i < kFramerate * kNumSeconds; ++i) { + switch (scale_direction) { + case kScaleUp: + qs_.ReportEncodedFrame(kLowQp); + break; + case kScaleDown: + qs_.ReportDroppedFrame(); + break; + } + + if (qs_.GetScaledResolution(input_frame_).width != initial_width) + return; + } + + FAIL() << "No downscale within " << kNumSeconds << " seconds."; + } + + void ExpectOriginalFrame() { + EXPECT_EQ(&input_frame_, &qs_.GetScaledFrame(input_frame_)) + << "Using scaled frame instead of original input."; + } + + void ExpectScaleUsingReportedResolution() { + QualityScaler::Resolution res = qs_.GetScaledResolution(input_frame_); + const I420VideoFrame& scaled_frame = qs_.GetScaledFrame(input_frame_); + EXPECT_EQ(res.width, scaled_frame.width()); + EXPECT_EQ(res.height, scaled_frame.height()); + } + + void ContinuouslyDownscalesByHalfDimensionsAndBackUp(); + + void DoesNotDownscaleFrameDimensions(int width, int height); + + QualityScaler qs_; + I420VideoFrame input_frame_; +}; + +TEST_F(QualityScalerTest, UsesOriginalFrameInitially) { + ExpectOriginalFrame(); +} + +TEST_F(QualityScalerTest, ReportsOriginalResolutionInitially) { + QualityScaler::Resolution res = qs_.GetScaledResolution(input_frame_); + EXPECT_EQ(input_frame_.width(), res.width); + EXPECT_EQ(input_frame_.height(), res.height); +} + +TEST_F(QualityScalerTest, DownscalesAfterContinuousFramedrop) { + TriggerScale(kScaleDown); + QualityScaler::Resolution res = qs_.GetScaledResolution(input_frame_); + EXPECT_LT(res.width, input_frame_.width()); + EXPECT_LT(res.height, input_frame_.height()); +} + +TEST_F(QualityScalerTest, DownscalesAfterTwoThirdsFramedrop) { + for (int i = 0; i < kFramerate * kNumSeconds / 3; ++i) { + qs_.ReportEncodedFrame(kNormalQp); + qs_.ReportDroppedFrame(); + qs_.ReportDroppedFrame(); + if (qs_.GetScaledResolution(input_frame_).width < input_frame_.width()) + return; + } + + FAIL() << "No downscale within " << kNumSeconds << " seconds."; +} + +TEST_F(QualityScalerTest, DoesNotDownscaleOnNormalQp) { + for (int i = 0; i < kFramerate * kNumSeconds; ++i) { + qs_.ReportEncodedFrame(kNormalQp); + ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution(input_frame_).width) + << "Unexpected scale on half framedrop."; + } +} + +TEST_F(QualityScalerTest, DoesNotDownscaleAfterHalfFramedrop) { + for (int i = 0; i < kFramerate * kNumSeconds / 2; ++i) { + qs_.ReportEncodedFrame(kNormalQp); + ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution(input_frame_).width) + << "Unexpected scale on half framedrop."; + + qs_.ReportDroppedFrame(); + ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution(input_frame_).width) + << "Unexpected scale on half framedrop."; + } +} + +void QualityScalerTest::ContinuouslyDownscalesByHalfDimensionsAndBackUp() { + const int initial_min_dimension = input_frame_.width() < input_frame_.height() + ? input_frame_.width() + : input_frame_.height(); + int min_dimension = initial_min_dimension; + int current_shift = 0; + // Drop all frames to force-trigger downscaling. + while (min_dimension > 16) { + TriggerScale(kScaleDown); + QualityScaler::Resolution res = qs_.GetScaledResolution(input_frame_); + min_dimension = res.width < res.height ? res.width : res.height; + ++current_shift; + ASSERT_EQ(input_frame_.width() >> current_shift, res.width); + ASSERT_EQ(input_frame_.height() >> current_shift, res.height); + ExpectScaleUsingReportedResolution(); + } + + // Make sure we can scale back with good-quality frames. + while (min_dimension < initial_min_dimension) { + TriggerScale(kScaleUp); + QualityScaler::Resolution res = qs_.GetScaledResolution(input_frame_); + min_dimension = res.width < res.height ? res.width : res.height; + --current_shift; + ASSERT_EQ(input_frame_.width() >> current_shift, res.width); + ASSERT_EQ(input_frame_.height() >> current_shift, res.height); + ExpectScaleUsingReportedResolution(); + } + + // Verify we don't start upscaling after further low use. + for (int i = 0; i < kFramerate * kNumSeconds; ++i) { + qs_.ReportEncodedFrame(kLowQp); + ExpectOriginalFrame(); + } +} + +TEST_F(QualityScalerTest, ContinuouslyDownscalesByHalfDimensionsAndBackUp) { + ContinuouslyDownscalesByHalfDimensionsAndBackUp(); +} + +TEST_F(QualityScalerTest, + ContinuouslyDownscalesOddResolutionsByHalfDimensionsAndBackUp) { + const int kOddWidth = 517; + const int kHalfOddWidth = (kOddWidth + 1) / 2; + const int kOddHeight = 1239; + input_frame_.CreateEmptyFrame( + kOddWidth, kOddHeight, kOddWidth, kHalfOddWidth, kHalfOddWidth); + ContinuouslyDownscalesByHalfDimensionsAndBackUp(); +} + +void QualityScalerTest::DoesNotDownscaleFrameDimensions(int width, int height) { + input_frame_.CreateEmptyFrame( + width, height, width, (width + 1) / 2, (width + 1) / 2); + + for (int i = 0; i < kFramerate * kNumSeconds; ++i) { + qs_.ReportDroppedFrame(); + ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution(input_frame_).width) + << "Unexpected scale of minimal-size frame."; + } +} + +TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxWidth) { + DoesNotDownscaleFrameDimensions(1, kHeight); +} + +TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxHeight) { + DoesNotDownscaleFrameDimensions(kWidth, 1); +} + +TEST_F(QualityScalerTest, DoesNotDownscaleFrom1Px) { + DoesNotDownscaleFrameDimensions(1, 1); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/video_coding_utility.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/video_coding_utility.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/video_coding_utility.gyp 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_coding/utility/video_coding_utility.gyp 2015-02-03 14:33:37.000000000 +0000 @@ -18,10 +18,10 @@ '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', ], 'sources': [ - 'include/exp_filter.h', - 'include/frame_dropper.h', - 'exp_filter.cc', 'frame_dropper.cc', + 'include/frame_dropper.h', + 'quality_scaler.cc', + 'quality_scaler.h', ], }, ], # targets diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/BUILD.gn 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,75 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../../build/webrtc.gni") + +build_video_processing_sse2 = cpu_arch == "x86" || cpu_arch == "x64" + +source_set("video_processing") { + sources = [ + "main/interface/video_processing.h", + "main/interface/video_processing_defines.h", + "main/source/brighten.cc", + "main/source/brighten.h", + "main/source/brightness_detection.cc", + "main/source/brightness_detection.h", + "main/source/color_enhancement.cc", + "main/source/color_enhancement.h", + "main/source/color_enhancement_private.h", + "main/source/content_analysis.cc", + "main/source/content_analysis.h", + "main/source/deflickering.cc", + "main/source/deflickering.h", + "main/source/frame_preprocessor.cc", + "main/source/frame_preprocessor.h", + "main/source/spatial_resampler.cc", + "main/source/spatial_resampler.h", + "main/source/video_decimator.cc", + "main/source/video_decimator.h", + "main/source/video_processing_impl.cc", + "main/source/video_processing_impl.h", + ] + + deps = [ + "../../common_audio", + "../../common_video", + "../../modules/utility", + "../../system_wrappers", + ] + if (build_video_processing_sse2) { + deps += [ ":video_processing_sse2" ] + } + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } +} + +if (build_video_processing_sse2) { + source_set("video_processing_sse2") { + sources = [ "main/source/content_analysis_sse2.cc" ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + if (is_posix) { + cflags = [ "-msse2" ] + } + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/interface/video_processing.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/interface/video_processing.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/interface/video_processing.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/interface/video_processing.h 2015-02-03 14:33:37.000000000 +0000 @@ -89,12 +89,12 @@ /** Not supported. */ - virtual int32_t TimeUntilNextProcess() { return -1; } + virtual int32_t TimeUntilNextProcess() OVERRIDE { return -1; } /** Not supported. */ - virtual int32_t Process() { return -1; } + virtual int32_t Process() OVERRIDE { return -1; } /** Resets all processing components to their initial states. This should be @@ -177,17 +177,6 @@ virtual int32_t Deflickering(I420VideoFrame* frame, FrameStats* stats) = 0; /** - Denoises a video frame. Every frame from the stream should be passed in. - Has a fixed-point implementation. - - \param[in,out] frame - Pointer to the video frame. - - \return The number of modified pixels on success, -1 on failure. - */ - virtual int32_t Denoising(I420VideoFrame* frame) = 0; - - /** Detects if a video frame is excessively bright or dark. Returns a warning if this is the case. Multiple frames should be passed in before expecting a warning. Has a floating-point implementation. @@ -236,14 +225,6 @@ uint32_t frame_rate) = 0; /** - Set max frame rate - \param[in] max_frame_rate: maximum frame rate (limited to native frame rate) - - \return VPM_OK on success, a negative value on error (see error codes) - */ - virtual int32_t SetMaxFramerate(uint32_t max_frame_rate) = 0; - - /** Get decimated(target) frame rate */ virtual uint32_t Decimatedframe_rate() = 0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/OWNERS 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -stefan@webrtc.org -mikhal@webrtc.org -marpan@webrtc.org -henrik.lundin@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,59 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_video_processing -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := \ - brighten.cc \ - brightness_detection.cc \ - color_enhancement.cc \ - content_analysis.cc \ - deflickering.cc \ - denoising.cc \ - frame_preprocessor.cc \ - spatial_resampler.cc \ - video_decimator.cc \ - video_processing_impl.cc - -ifeq ($(TARGET_ARCH),x86) -LOCAL_SRC_FILES += \ - content_analysis_sse2.cc -endif - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../../../audio_coding/main/interface \ - $(LOCAL_PATH)/../../../utility/interface \ - $(LOCAL_PATH)/../../../../common_audio/signal_processing/include \ - $(LOCAL_PATH)/../../../../common_video/vplib/main/interface \ - $(LOCAL_PATH)/../../../../system_wrappers/interface - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/brighten.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/brighten.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/brighten.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/brighten.cc 2015-02-03 14:33:37.000000000 +0000 @@ -12,22 +12,15 @@ #include -#include "webrtc/system_wrappers/interface/trace.h" - namespace webrtc { namespace VideoProcessing { int32_t Brighten(I420VideoFrame* frame, int delta) { assert(frame); if (frame->IsZeroSize()) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, -1, - "zero size frame"); return VPM_PARAMETER_ERROR; } - if (frame->width() <= 0 || frame->height() <= 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, -1, - "Invalid frame size"); return VPM_PARAMETER_ERROR; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/brightness_detection.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/brightness_detection.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/brightness_detection.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/brightness_detection.cc 2015-02-03 14:33:37.000000000 +0000 @@ -10,7 +10,6 @@ #include "webrtc/modules/video_processing/main/interface/video_processing.h" #include "webrtc/modules/video_processing/main/source/brightness_detection.h" -#include "webrtc/system_wrappers/interface/trace.h" #include @@ -37,16 +36,12 @@ const I420VideoFrame& frame, const VideoProcessingModule::FrameStats& stats) { if (frame.IsZeroSize()) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_, - "Null frame pointer"); return VPM_PARAMETER_ERROR; } int width = frame.width(); int height = frame.height(); if (!VideoProcessingModule::ValidFrameStats(stats)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_, - "Invalid frame stats"); return VPM_PARAMETER_ERROR; } @@ -58,7 +53,7 @@ for (uint32_t i = 0; i < low_th; i++) { prop_low += stats.hist[i]; } -prop_low /= stats.num_pixels; + prop_low /= stats.num_pixels; // Get proportion in highest bins. unsigned char high_th = 230; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/color_enhancement.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/color_enhancement.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/color_enhancement.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/color_enhancement.cc 2015-02-03 14:33:37.000000000 +0000 @@ -12,44 +12,38 @@ #include "webrtc/modules/video_processing/main/source/color_enhancement.h" #include "webrtc/modules/video_processing/main/source/color_enhancement_private.h" -#include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { namespace VideoProcessing { int32_t ColorEnhancement(I420VideoFrame* frame) { -assert(frame); -// Pointers to U and V color pixels. -uint8_t* ptr_u; -uint8_t* ptr_v; -uint8_t temp_chroma; -if (frame->IsZeroSize()) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, - -1, "Null frame pointer"); - return VPM_GENERAL_ERROR; -} - -if (frame->width() == 0 || frame->height() == 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, - -1, "Invalid frame size"); - return VPM_GENERAL_ERROR; -} - -// Set pointers to first U and V pixels (skip luminance). -ptr_u = frame->buffer(kUPlane); -ptr_v = frame->buffer(kVPlane); -int size_uv = ((frame->width() + 1) / 2) * ((frame->height() + 1) / 2); - -// Loop through all chrominance pixels and modify color. -for (int ix = 0; ix < size_uv; ix++) { - temp_chroma = colorTable[*ptr_u][*ptr_v]; - *ptr_v = colorTable[*ptr_v][*ptr_u]; - *ptr_u = temp_chroma; - - ptr_u++; - ptr_v++; -} -return VPM_OK; + assert(frame); + // Pointers to U and V color pixels. + uint8_t* ptr_u; + uint8_t* ptr_v; + uint8_t temp_chroma; + if (frame->IsZeroSize()) { + return VPM_GENERAL_ERROR; + } + if (frame->width() == 0 || frame->height() == 0) { + return VPM_GENERAL_ERROR; + } + + // Set pointers to first U and V pixels (skip luminance). + ptr_u = frame->buffer(kUPlane); + ptr_v = frame->buffer(kVPlane); + int size_uv = ((frame->width() + 1) / 2) * ((frame->height() + 1) / 2); + + // Loop through all chrominance pixels and modify color. + for (int ix = 0; ix < size_uv; ix++) { + temp_chroma = colorTable[*ptr_u][*ptr_v]; + *ptr_v = colorTable[*ptr_v][*ptr_u]; + *ptr_u = temp_chroma; + + ptr_u++; + ptr_v++; + } + return VPM_OK; } } // namespace VideoProcessing diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/deflickering.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/deflickering.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/deflickering.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/deflickering.cc 2015-02-03 14:33:37.000000000 +0000 @@ -14,8 +14,8 @@ #include #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/sort.h" -#include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { @@ -75,8 +75,9 @@ quant_hist_uw8_[0][0] = 0; quant_hist_uw8_[0][kNumQuants - 1] = 255; for (int32_t i = 0; i < kNumProbs; i++) { - quant_hist_uw8_[0][i + 1] = static_cast((WEBRTC_SPL_UMUL_16_16( - prob_uw16_[i], 255) + (1 << 10)) >> 11); // Unsigned round. + // Unsigned round. + quant_hist_uw8_[0][i + 1] = static_cast( + (prob_uw16_[i] * 255 + (1 << 10)) >> 11); } for (int32_t i = 1; i < kFrameHistory_size; i++) { @@ -102,21 +103,16 @@ int height = frame->height(); if (frame->IsZeroSize()) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_, - "Null frame pointer"); return VPM_GENERAL_ERROR; } // Stricter height check due to subsampling size calculation below. if (height < 2) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_, - "Invalid frame size"); + LOG(LS_ERROR) << "Invalid frame size."; return VPM_GENERAL_ERROR; } if (!VideoProcessingModule::ValidFrameStats(*stats)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_, - "Invalid frame stats"); return VPM_GENERAL_ERROR; } @@ -152,8 +148,7 @@ // Ensure we won't get an overflow below. // In practice, the number of subsampled pixels will not become this large. if (y_sub_size > (1 << 21) - 1) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_, - "Subsampled number of pixels too large"); + LOG(LS_ERROR) << "Subsampled number of pixels too large."; return -1; } @@ -197,9 +192,12 @@ // Get target quantiles. for (int32_t i = 0; i < kNumQuants - kMaxOnlyLength; i++) { - target_quant_uw16[i] = static_cast((WEBRTC_SPL_UMUL_16_16( - weight_uw16_[i], maxquant_uw8[i]) + WEBRTC_SPL_UMUL_16_16((1 << 15) - - weight_uw16_[i], minquant_uw8[i])) >> 8); // + // target = w * maxquant_uw8 + (1 - w) * minquant_uw8 + // Weights w = |weight_uw16_| are in Q15, hence the final output has to be + // right shifted by 8 to end up in Q7. + target_quant_uw16[i] = static_cast(( + weight_uw16_[i] * maxquant_uw8[i] + + ((1 << 15) - weight_uw16_[i]) * minquant_uw8[i]) >> 8); // } for (int32_t i = kNumQuants - kMaxOnlyLength; i < kNumQuants; i++) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/denoising.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/denoising.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/denoising.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/denoising.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/modules/video_processing/main/source/denoising.h" -#include "webrtc/system_wrappers/interface/trace.h" - -#include - -namespace webrtc { -// Down-sampling in time (unit: number of frames) -enum { kSubsamplingTime = 0 }; -// Sub-sampling in width (unit: power of 2. -enum { kSubsamplingWidth = 0 }; -// Sub-sampling in height (unit: power of 2) -enum { kSubsamplingHeight = 0 }; -// (Q8) De-noising filter parameter -enum { kDenoiseFiltParam = 179 }; -// (Q8) 1 - filter parameter -enum { kDenoiseFiltParamRec = 77 }; -// (Q8) De-noising threshold level -enum { kDenoiseThreshold = 19200 }; - -VPMDenoising::VPMDenoising() - : id_(0), - moment1_(NULL), - moment2_(NULL) { - Reset(); -} - -VPMDenoising::~VPMDenoising() { - if (moment1_) { - delete [] moment1_; - moment1_ = NULL; -} - - if (moment2_) { - delete [] moment2_; - moment2_ = NULL; - } -} - -int32_t VPMDenoising::ChangeUniqueId(const int32_t id) { - id_ = id; - return VPM_OK; -} - -void VPMDenoising::Reset() { - frame_size_ = 0; - denoise_frame_cnt_ = 0; - - if (moment1_) { - delete [] moment1_; - moment1_ = NULL; - } - - if (moment2_) { - delete [] moment2_; - moment2_ = NULL; - } -} - -int32_t VPMDenoising::ProcessFrame(I420VideoFrame* frame) { - assert(frame); - int32_t thevar; - int k; - int jsub, ksub; - int32_t diff0; - uint32_t tmp_moment1; - uint32_t tmp_moment2; - uint32_t tmp; - int32_t num_pixels_changed = 0; - - if (frame->IsZeroSize()) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_, - "zero size frame"); - return VPM_GENERAL_ERROR; - } - - int width = frame->width(); - int height = frame->height(); - - /* Size of luminance component */ - const uint32_t y_size = height * width; - - /* Initialization */ - if (y_size != frame_size_) { - delete [] moment1_; - moment1_ = NULL; - - delete [] moment2_; - moment2_ = NULL; - } - frame_size_ = y_size; - - if (!moment1_) { - moment1_ = new uint32_t[y_size]; - memset(moment1_, 0, sizeof(uint32_t)*y_size); - } - - if (!moment2_) { - moment2_ = new uint32_t[y_size]; - memset(moment2_, 0, sizeof(uint32_t)*y_size); - } - - /* Apply de-noising on each pixel, but update variance sub-sampled */ - uint8_t* buffer = frame->buffer(kYPlane); - for (int i = 0; i < height; i++) { // Collect over height - k = i * width; - ksub = ((i >> kSubsamplingHeight) << kSubsamplingHeight) * width; - for (int j = 0; j < width; j++) { // Collect over width - jsub = ((j >> kSubsamplingWidth) << kSubsamplingWidth); - /* Update mean value for every pixel and every frame */ - tmp_moment1 = moment1_[k + j]; - tmp_moment1 *= kDenoiseFiltParam; // Q16 - tmp_moment1 += ((kDenoiseFiltParamRec * ((uint32_t)buffer[k + j])) << 8); - tmp_moment1 >>= 8; // Q8 - moment1_[k + j] = tmp_moment1; - - tmp_moment2 = moment2_[ksub + jsub]; - if ((ksub == k) && (jsub == j) && (denoise_frame_cnt_ == 0)) { - tmp = ((uint32_t)buffer[k + j] * - (uint32_t)buffer[k + j]); - tmp_moment2 *= kDenoiseFiltParam; // Q16 - tmp_moment2 += ((kDenoiseFiltParamRec * tmp) << 8); - tmp_moment2 >>= 8; // Q8 - } - moment2_[k + j] = tmp_moment2; - /* Current event = deviation from mean value */ - diff0 = ((int32_t)buffer[k + j] << 8) - moment1_[k + j]; - /* Recent events = variance (variations over time) */ - thevar = moment2_[k + j]; - thevar -= ((moment1_[k + j] * moment1_[k + j]) >> 8); - // De-noising criteria, i.e., when should we replace a pixel by its mean. - // 1) recent events are minor. - // 2) current events are minor. - if ((thevar < kDenoiseThreshold) - && ((diff0 * diff0 >> 8) < kDenoiseThreshold)) { - // Replace with mean. - buffer[k + j] = (uint8_t)(moment1_[k + j] >> 8); - num_pixels_changed++; - } - } - } - - denoise_frame_cnt_++; - if (denoise_frame_cnt_ > kSubsamplingTime) - denoise_frame_cnt_ = 0; - - return num_pixels_changed; -} - -} // namespace diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/denoising.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/denoising.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/denoising.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/denoising.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_DENOISING_H_ -#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_DENOISING_H_ - -#include "webrtc/modules/video_processing/main/interface/video_processing.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -class VPMDenoising { - public: - VPMDenoising(); - ~VPMDenoising(); - - int32_t ChangeUniqueId(int32_t id); - - void Reset(); - - int32_t ProcessFrame(I420VideoFrame* frame); - - private: - int32_t id_; - - uint32_t* moment1_; // (Q8) First order moment (mean). - uint32_t* moment2_; // (Q8) Second order moment. - uint32_t frame_size_; // Size (# of pixels) of frame. - int denoise_frame_cnt_; // Counter for subsampling in time. -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_DENOISING_H_ - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/frame_preprocessor.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/frame_preprocessor.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/frame_preprocessor.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/frame_preprocessor.cc 2015-02-03 14:33:37.000000000 +0000 @@ -9,14 +9,12 @@ */ #include "webrtc/modules/video_processing/main/source/frame_preprocessor.h" -#include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { VPMFramePreprocessor::VPMFramePreprocessor() : id_(0), content_metrics_(NULL), - max_frame_rate_(0), resampled_frame_(), enable_ca_(false), frame_cnt_(0) { @@ -60,14 +58,6 @@ spatial_resampler_->SetInputFrameResampleMode(resampling_mode); } -int32_t VPMFramePreprocessor::SetMaxFramerate(uint32_t max_frame_rate) { - if (max_frame_rate == 0) return VPM_PARAMETER_ERROR; - - // Max allowed frame_rate. - max_frame_rate_ = max_frame_rate; - return vd_->SetMaxFramerate(max_frame_rate); -} - int32_t VPMFramePreprocessor::SetTargetResolution( uint32_t width, uint32_t height, uint32_t frame_rate) { if ( (width == 0) || (height == 0) || (frame_rate == 0)) { @@ -78,7 +68,7 @@ if (ret_val < 0) return ret_val; - ret_val = vd_->SetTargetframe_rate(frame_rate); + ret_val = vd_->SetTargetFramerate(frame_rate); if (ret_val < 0) return ret_val; return VPM_OK; @@ -112,8 +102,6 @@ vd_->UpdateIncomingframe_rate(); if (vd_->DropFrame()) { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo, id_, - "Drop frame due to frame rate"); return 1; // drop 1 frame } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/frame_preprocessor.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/frame_preprocessor.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/frame_preprocessor.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/frame_preprocessor.h 2015-02-03 14:33:37.000000000 +0000 @@ -39,9 +39,6 @@ // Enable content analysis. void EnableContentAnalysis(bool enable); - // Set max frame rate. - int32_t SetMaxFramerate(uint32_t max_frame_rate); - // Set target resolution: frame rate and dimension. int32_t SetTargetResolution(uint32_t width, uint32_t height, uint32_t frame_rate); @@ -68,7 +65,6 @@ int32_t id_; VideoContentMetrics* content_metrics_; - uint32_t max_frame_rate_; I420VideoFrame resampled_frame_; VPMSpatialResampler* spatial_resampler_; VPMContentAnalysis* ca_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/OWNERS 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_decimator.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_decimator.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_decimator.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_decimator.cc 2015-02-03 14:33:37.000000000 +0000 @@ -16,15 +16,7 @@ namespace webrtc { -VPMVideoDecimator::VPMVideoDecimator() - : overshoot_modifier_(0), - drop_count_(0), - keep_count_(0), - target_frame_rate_(30), - incoming_frame_rate_(0.0f), - max_frame_rate_(30), - incoming_frame_times_(), - enable_temporal_decimation_(true) { +VPMVideoDecimator::VPMVideoDecimator() { Reset(); } @@ -36,7 +28,6 @@ keep_count_ = 0; target_frame_rate_ = 30; incoming_frame_rate_ = 0.0f; - max_frame_rate_ = 30; memset(incoming_frame_times_, 0, sizeof(incoming_frame_times_)); enable_temporal_decimation_ = true; } @@ -45,26 +36,10 @@ enable_temporal_decimation_ = enable; } -int32_t VPMVideoDecimator::SetMaxFramerate(uint32_t max_frame_rate) { - if (max_frame_rate == 0) return VPM_PARAMETER_ERROR; - - max_frame_rate_ = max_frame_rate; - - if (target_frame_rate_ > max_frame_rate_) - target_frame_rate_ = max_frame_rate_; - - return VPM_OK; -} - -int32_t VPMVideoDecimator::SetTargetframe_rate(uint32_t frame_rate) { +int32_t VPMVideoDecimator::SetTargetFramerate(uint32_t frame_rate) { if (frame_rate == 0) return VPM_PARAMETER_ERROR; - if (frame_rate > max_frame_rate_) { - // Override. - target_frame_rate_ = max_frame_rate_; - } else { - target_frame_rate_ = frame_rate; - } + target_frame_rate_ = frame_rate; return VPM_OK; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_decimator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_decimator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_decimator.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_decimator.h 2015-02-03 14:33:37.000000000 +0000 @@ -25,8 +25,7 @@ void EnableTemporalDecimation(bool enable); - int32_t SetMaxFramerate(uint32_t max_frame_rate); - int32_t SetTargetframe_rate(uint32_t frame_rate); + int32_t SetTargetFramerate(uint32_t frame_rate); bool DropFrame(); @@ -50,7 +49,6 @@ uint32_t keep_count_; uint32_t target_frame_rate_; float incoming_frame_rate_; - uint32_t max_frame_rate_; int64_t incoming_frame_times_[kFrameCountHistory_size]; bool enable_temporal_decimation_; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_processing.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_processing.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_processing.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_processing.gypi 2015-02-03 14:33:37.000000000 +0000 @@ -31,8 +31,6 @@ 'content_analysis.h', 'deflickering.cc', 'deflickering.h', - 'denoising.cc', - 'denoising.h', 'frame_preprocessor.cc', 'frame_preprocessor.h', 'spatial_resampler.cc', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_processing_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_processing_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_processing_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_processing_impl.cc 2015-02-03 14:33:37.000000000 +0000 @@ -11,7 +11,7 @@ #include "webrtc/modules/video_processing/main/source/video_processing_impl.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include @@ -51,7 +51,6 @@ id_ = id; brightness_detection_.ChangeUniqueId(id); deflickering_.ChangeUniqueId(id); - denoising_.ChangeUniqueId(id); frame_pre_processor_.ChangeUniqueId(id); return VPM_OK; } @@ -66,22 +65,16 @@ mutex_(*CriticalSectionWrapper::CreateCriticalSection()) { brightness_detection_.ChangeUniqueId(id); deflickering_.ChangeUniqueId(id); - denoising_.ChangeUniqueId(id); frame_pre_processor_.ChangeUniqueId(id); - WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideoPreocessing, id_, - "Created"); } VideoProcessingModuleImpl::~VideoProcessingModuleImpl() { - WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideoPreocessing, id_, - "Destroyed"); delete &mutex_; } void VideoProcessingModuleImpl::Reset() { CriticalSectionScoped mutex(&mutex_); deflickering_.Reset(); - denoising_.Reset(); brightness_detection_.Reset(); frame_pre_processor_.Reset(); } @@ -89,8 +82,7 @@ int32_t VideoProcessingModule::GetFrameStats(FrameStats* stats, const I420VideoFrame& frame) { if (frame.IsZeroSize()) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, -1, - "zero size frame"); + LOG(LS_ERROR) << "Zero size frame."; return VPM_PARAMETER_ERROR; } @@ -121,7 +113,10 @@ } bool VideoProcessingModule::ValidFrameStats(const FrameStats& stats) { - if (stats.num_pixels == 0) return false; + if (stats.num_pixels == 0) { + LOG(LS_WARNING) << "Invalid frame stats."; + return false; + } return true; } @@ -148,11 +143,6 @@ return deflickering_.ProcessFrame(frame, stats); } -int32_t VideoProcessingModuleImpl::Denoising(I420VideoFrame* frame) { - CriticalSectionScoped mutex(&mutex_); - return denoising_.ProcessFrame(frame); -} - int32_t VideoProcessingModuleImpl::BrightnessDetection( const I420VideoFrame& frame, const FrameStats& stats) { @@ -173,11 +163,6 @@ frame_pre_processor_.SetInputFrameResampleMode(resampling_mode); } -int32_t VideoProcessingModuleImpl::SetMaxFramerate(uint32_t max_frame_rate) { - CriticalSectionScoped cs(&mutex_); - return frame_pre_processor_.SetMaxFramerate(max_frame_rate); -} - int32_t VideoProcessingModuleImpl::SetTargetResolution(uint32_t width, uint32_t height, uint32_t frame_rate) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_processing_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_processing_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_processing_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/source/video_processing_impl.h 2015-02-03 14:33:37.000000000 +0000 @@ -16,7 +16,6 @@ #include "webrtc/modules/video_processing/main/source/brightness_detection.h" #include "webrtc/modules/video_processing/main/source/color_enhancement.h" #include "webrtc/modules/video_processing/main/source/deflickering.h" -#include "webrtc/modules/video_processing/main/source/denoising.h" #include "webrtc/modules/video_processing/main/source/frame_preprocessor.h" namespace webrtc { @@ -30,54 +29,50 @@ int32_t Id() const; - virtual int32_t ChangeUniqueId(const int32_t id); + virtual int32_t ChangeUniqueId(const int32_t id) OVERRIDE; - virtual void Reset(); + virtual void Reset() OVERRIDE; - virtual int32_t Deflickering(I420VideoFrame* frame, FrameStats* stats); - - virtual int32_t Denoising(I420VideoFrame* frame); + virtual int32_t Deflickering(I420VideoFrame* frame, + FrameStats* stats) OVERRIDE; virtual int32_t BrightnessDetection(const I420VideoFrame& frame, - const FrameStats& stats); + const FrameStats& stats) OVERRIDE; // Frame pre-processor functions // Enable temporal decimation - virtual void EnableTemporalDecimation(bool enable); + virtual void EnableTemporalDecimation(bool enable) OVERRIDE; - virtual void SetInputFrameResampleMode(VideoFrameResampling resampling_mode); + virtual void SetInputFrameResampleMode( + VideoFrameResampling resampling_mode) OVERRIDE; // Enable content analysis - virtual void EnableContentAnalysis(bool enable); - - // Set max frame rate - virtual int32_t SetMaxFramerate(uint32_t max_frame_rate); + virtual void EnableContentAnalysis(bool enable) OVERRIDE; // Set Target Resolution: frame rate and dimension virtual int32_t SetTargetResolution(uint32_t width, uint32_t height, - uint32_t frame_rate); + uint32_t frame_rate) OVERRIDE; // Get decimated values: frame rate/dimension - virtual uint32_t Decimatedframe_rate(); - virtual uint32_t DecimatedWidth() const; - virtual uint32_t DecimatedHeight() const; + virtual uint32_t Decimatedframe_rate() OVERRIDE; + virtual uint32_t DecimatedWidth() const OVERRIDE; + virtual uint32_t DecimatedHeight() const OVERRIDE; // Preprocess: // Pre-process incoming frame: Sample when needed and compute content // metrics when enabled. // If no resampling takes place - processed_frame is set to NULL. virtual int32_t PreprocessFrame(const I420VideoFrame& frame, - I420VideoFrame** processed_frame); - virtual VideoContentMetrics* ContentMetrics() const; + I420VideoFrame** processed_frame) OVERRIDE; + virtual VideoContentMetrics* ContentMetrics() const OVERRIDE; private: int32_t id_; CriticalSectionWrapper& mutex_; VPMDeflickering deflickering_; - VPMDenoising denoising_; VPMBrightnessDetection brightness_detection_; VPMFramePreprocessor frame_pre_processor_; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/brightness_detection_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/brightness_detection_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/brightness_detection_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/brightness_detection_test.cc 2015-02-03 14:33:37.000000000 +0000 @@ -19,7 +19,7 @@ uint32_t frameNum = 0; int32_t brightnessWarning = 0; uint32_t warningCount = 0; - scoped_array video_buffer(new uint8_t[frame_length_]); + scoped_ptr video_buffer(new uint8_t[frame_length_]); while (fread(video_buffer.get(), 1, frame_length_, source_file_) == frame_length_) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/color_enhancement_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/color_enhancement_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/color_enhancement_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/color_enhancement_test.cc 2015-02-03 14:33:37.000000000 +0000 @@ -39,7 +39,7 @@ ASSERT_TRUE(modFile != NULL) << "Could not open output file.\n"; uint32_t frameNum = 0; - scoped_array video_buffer(new uint8_t[frame_length_]); + scoped_ptr video_buffer(new uint8_t[frame_length_]); while (fread(video_buffer.get(), 1, frame_length_, source_file_) == frame_length_) { @@ -86,7 +86,7 @@ width_, half_width_, half_width_); // Compare frame-by-frame. - scoped_array ref_buffer(new uint8_t[frame_length_]); + scoped_ptr ref_buffer(new uint8_t[frame_length_]); while (fread(video_buffer.get(), 1, frame_length_, modFile) == frame_length_) { @@ -114,7 +114,7 @@ // Verify that all color pixels are enhanced, and no luminance values are // altered. - scoped_array testFrame(new uint8_t[frame_length_]); + scoped_ptr testFrame(new uint8_t[frame_length_]); // Use value 128 as probe value, since we know that this will be changed // in the enhancement. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/content_metrics_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/content_metrics_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/content_metrics_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/content_metrics_test.cc 2015-02-03 14:33:37.000000000 +0000 @@ -23,7 +23,7 @@ ca__c.Initialize(width_,height_); ca__sse.Initialize(width_,height_); - scoped_array video_buffer(new uint8_t[frame_length_]); + scoped_ptr video_buffer(new uint8_t[frame_length_]); while (fread(video_buffer.get(), 1, frame_length_, source_file_) == frame_length_) { // Using ConvertToI420 to add stride to the image. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/deflickering_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/deflickering_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/deflickering_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/deflickering_test.cc 2015-02-03 14:33:37.000000000 +0000 @@ -43,7 +43,7 @@ "Could not open output file: " << output_file << "\n"; printf("\nRun time [us / frame]:\n"); - scoped_array video_buffer(new uint8_t[frame_length_]); + scoped_ptr video_buffer(new uint8_t[frame_length_]); for (uint32_t run_idx = 0; run_idx < NumRuns; run_idx++) { TickTime t0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/denoising_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/denoising_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/denoising_test.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/denoising_test.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include - -#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" -#include "webrtc/modules/video_processing/main/interface/video_processing.h" -#include "webrtc/modules/video_processing/main/test/unit_test/video_processing_unittest.h" -#include "webrtc/system_wrappers/interface/tick_util.h" -#include "webrtc/test/testsupport/fileutils.h" -#include "webrtc/test/testsupport/gtest_disable.h" - -namespace webrtc { - -TEST_F(VideoProcessingModuleTest, DISABLED_ON_ANDROID(Denoising)) -{ - enum { NumRuns = 10 }; - uint32_t frameNum = 0; - - int64_t min_runtime = 0; - int64_t avg_runtime = 0; - - const std::string denoise_filename = - webrtc::test::OutputPath() + "denoise_testfile.yuv"; - FILE* denoiseFile = fopen(denoise_filename.c_str(), "wb"); - ASSERT_TRUE(denoiseFile != NULL) << - "Could not open output file: " << denoise_filename << "\n"; - - const std::string noise_filename = - webrtc::test::OutputPath() + "noise_testfile.yuv"; - FILE* noiseFile = fopen(noise_filename.c_str(), "wb"); - ASSERT_TRUE(noiseFile != NULL) << - "Could not open noisy file: " << noise_filename << "\n"; - - printf("\nRun time [us / frame]:\n"); - for (uint32_t run_idx = 0; run_idx < NumRuns; run_idx++) - { - TickTime t0; - TickTime t1; - TickInterval acc_ticks; - int32_t modifiedPixels = 0; - - frameNum = 0; - scoped_array video_buffer(new uint8_t[frame_length_]); - while (fread(video_buffer.get(), 1, frame_length_, source_file_) == - frame_length_) - { - EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0, - width_, height_, - 0, kRotateNone, &video_frame_)); - frameNum++; - uint8_t* sourceBuffer = video_frame_.buffer(kYPlane); - - // Add noise to a part in video stream - // Random noise - // TODO: investigate the effectiveness of this test. - - for (int ir = 0; ir < height_; ir++) - { - uint32_t ik = ir * width_; - for (int ic = 0; ic < width_; ic++) - { - uint8_t r = rand() % 16; - r -= 8; - if (ir < height_ / 4) - r = 0; - if (ir >= 3 * height_ / 4) - r = 0; - if (ic < width_ / 4) - r = 0; - if (ic >= 3 * width_ / 4) - r = 0; - - /*uint8_t pixelValue = 0; - if (ir >= height_ / 2) - { // Region 3 or 4 - pixelValue = 170; - } - if (ic >= width_ / 2) - { // Region 2 or 4 - pixelValue += 85; - } - pixelValue += r; - sourceBuffer[ik + ic] = pixelValue; - */ - sourceBuffer[ik + ic] += r; - } - } - - if (run_idx == 0) - { - if (PrintI420VideoFrame(video_frame_, noiseFile) < 0) { - return; - } - } - - t0 = TickTime::Now(); - ASSERT_GE(modifiedPixels = vpm_->Denoising(&video_frame_), 0); - t1 = TickTime::Now(); - acc_ticks += (t1 - t0); - - if (run_idx == 0) - { - if (PrintI420VideoFrame(video_frame_, noiseFile) < 0) { - return; - } - } - } - ASSERT_NE(0, feof(source_file_)) << "Error reading source file"; - - printf("%u\n", static_cast(acc_ticks.Microseconds() / frameNum)); - if (acc_ticks.Microseconds() < min_runtime || run_idx == 0) - { - min_runtime = acc_ticks.Microseconds(); - } - avg_runtime += acc_ticks.Microseconds(); - - rewind(source_file_); - } - ASSERT_EQ(0, fclose(denoiseFile)); - ASSERT_EQ(0, fclose(noiseFile)); - printf("\nAverage run time = %d us / frame\n", - static_cast(avg_runtime / frameNum / NumRuns)); - printf("Min run time = %d us / frame\n\n", - static_cast(min_runtime / frameNum)); -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/video_processing_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/video_processing_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/video_processing_unittest.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/main/test/unit_test/video_processing_unittest.cc 2015-02-03 14:33:37.000000000 +0000 @@ -82,14 +82,12 @@ EXPECT_EQ(-1, vpm_->Deflickering(&videoFrame, &stats)); - EXPECT_EQ(-1, vpm_->Denoising(&videoFrame)); - EXPECT_EQ(-3, vpm_->BrightnessDetection(videoFrame, stats)); } TEST_F(VideoProcessingModuleTest, HandleBadStats) { VideoProcessingModule::FrameStats stats; - scoped_array video_buffer(new uint8_t[frame_length_]); + scoped_ptr video_buffer(new uint8_t[frame_length_]); ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_, source_file_)); EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0, @@ -113,12 +111,9 @@ EXPECT_EQ(-1, vpm_->Deflickering(&video_frame_, &stats)); - EXPECT_EQ(-1, vpm_->Denoising(&video_frame_)); - EXPECT_EQ(-3, vpm_->BrightnessDetection(video_frame_, stats)); EXPECT_EQ(VPM_PARAMETER_ERROR, vpm_->SetTargetResolution(0,0,0)); - EXPECT_EQ(VPM_PARAMETER_ERROR, vpm_->SetMaxFramerate(0)); I420VideoFrame *out_frame = NULL; EXPECT_EQ(VPM_PARAMETER_ERROR, vpm_->PreprocessFrame(video_frame_, @@ -129,7 +124,7 @@ I420VideoFrame video_frame2; VideoProcessingModule::FrameStats stats; // Only testing non-static functions here. - scoped_array video_buffer(new uint8_t[frame_length_]); + scoped_ptr video_buffer(new uint8_t[frame_length_]); ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_, source_file_)); EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0, @@ -146,19 +141,6 @@ ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_, source_file_)); - // Using ConvertToI420 to add stride to the image. - EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0, - width_, height_, - 0, kRotateNone, &video_frame_)); - video_frame2.CopyFrame(video_frame_); - EXPECT_TRUE(CompareFrames(video_frame_, video_frame2)); - ASSERT_GE(vpm_->Denoising(&video_frame_), 0); - vpm_->Reset(); - ASSERT_GE(vpm_->Denoising(&video_frame2), 0); - EXPECT_TRUE(CompareFrames(video_frame_, video_frame2)); - - ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_, - source_file_)); EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0, width_, height_, 0, kRotateNone, &video_frame_)); @@ -172,7 +154,7 @@ TEST_F(VideoProcessingModuleTest, FrameStats) { VideoProcessingModule::FrameStats stats; - scoped_array video_buffer(new uint8_t[frame_length_]); + scoped_ptr video_buffer(new uint8_t[frame_length_]); ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_, source_file_)); EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0, @@ -200,7 +182,6 @@ // Disable temporal sampling (frame dropping). vpm_->EnableTemporalDecimation(false); int resolution = 100; - EXPECT_EQ(VPM_OK, vpm_->SetMaxFramerate(30)); EXPECT_EQ(VPM_OK, vpm_->SetTargetResolution(resolution, resolution, 15)); EXPECT_EQ(VPM_OK, vpm_->SetTargetResolution(resolution, resolution, 30)); // Disable spatial sampling. @@ -242,7 +223,7 @@ vpm_->EnableTemporalDecimation(false); // Reading test frame - scoped_array video_buffer(new uint8_t[frame_length_]); + scoped_ptr video_buffer(new uint8_t[frame_length_]); ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_, source_file_)); // Using ConvertToI420 to add stride to the image. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_processing/OWNERS 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,5 @@ +stefan@webrtc.org +marpan@webrtc.org +henrik.lundin@webrtc.org + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/android/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/android/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/android/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/android/OWNERS 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -leozwang@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/android/video_render_android_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/android/video_render_android_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/android/video_render_android_impl.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/android/video_render_android_impl.cc 2015-02-03 14:33:37.000000000 +0000 @@ -10,6 +10,7 @@ #include "webrtc/modules/video_render/android/video_render_android_impl.h" +#include "webrtc/modules/video_render/video_render_internal.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/event_wrapper.h" #include "webrtc/system_wrappers/interface/thread_wrapper.h" @@ -29,13 +30,11 @@ JavaVM* VideoRenderAndroid::g_jvm = NULL; -#if defined(WEBRTC_ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) -int32_t SetRenderAndroidVM(void* javaVM) { +int32_t SetRenderAndroidVM(JavaVM* javaVM) { WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, -1, "%s", __FUNCTION__); - VideoRenderAndroid::g_jvm = (JavaVM*)javaVM; + VideoRenderAndroid::g_jvm = javaVM; return 0; } -#endif VideoRenderAndroid::VideoRenderAndroid( const int32_t id, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/android/video_render_android_native_opengl2.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/android/video_render_android_native_opengl2.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/android/video_render_android_native_opengl2.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/android/video_render_android_native_opengl2.cc 2015-02-03 14:33:37.000000000 +0000 @@ -245,7 +245,6 @@ AndroidNativeOpenGl2Channel::~AndroidNativeOpenGl2Channel() { WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "AndroidNativeOpenGl2Channel dtor"); - delete &_renderCritSect; if (_jvm) { // get the JNI env for this thread bool isAttached = false; @@ -277,6 +276,8 @@ } } } + + delete &_renderCritSect; } int32_t AndroidNativeOpenGl2Channel::Init(int32_t zOrder, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/android/video_render_android_native_opengl2.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/android/video_render_android_native_opengl2.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/android/video_render_android_native_opengl2.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/android/video_render_android_native_opengl2.h 2015-02-03 14:33:37.000000000 +0000 @@ -41,7 +41,7 @@ virtual void DeliverFrame(JNIEnv* jniEnv); private: - static jint CreateOpenGLNativeStatic( + static jint JNICALL CreateOpenGLNativeStatic( JNIEnv * env, jobject, jlong context, @@ -49,7 +49,7 @@ jint height); jint CreateOpenGLNative(int width, int height); - static void DrawNativeStatic(JNIEnv * env,jobject, jlong context); + static void JNICALL DrawNativeStatic(JNIEnv * env,jobject, jlong context); void DrawNative(); uint32_t _id; CriticalSectionWrapper& _renderCritSect; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/Android.mk 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../android-webrtc.mk - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_video_render -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := \ - incoming_video_stream.cc \ - video_render_frames.cc \ - video_render_impl.cc \ - external/video_render_external_impl.cc \ - android/video_render_android_impl.cc \ - android/video_render_android_native_opengl2.cc \ - android/video_render_android_surface_view.cc \ - android/video_render_opengles20.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) \ - '-DWEBRTC_INCLUDE_INTERNAL_VIDEO_RENDER' - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH) \ - $(LOCAL_PATH)/android \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/../../../audio_coding/main/interface \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../../../utility/interface \ - $(LOCAL_PATH)/../../../../common_video/vplib/main/interface \ - $(LOCAL_PATH)/../../../../system_wrappers/interface - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/BUILD.gn 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,174 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../../build/webrtc.gni") + +source_set("video_render") { + sources = [ + "external/video_render_external_impl.cc", + "external/video_render_external_impl.h", + "i_video_render.h", + "include/video_render.h", + "include/video_render_defines.h", + "incoming_video_stream.cc", + "incoming_video_stream.h", + "video_render_frames.cc", + "video_render_frames.h", + "video_render_impl.h", + ] + + deps = [ + "../../common_video", + "../../system_wrappers", + "../utility", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } +} + +source_set("video_render_impl") { + sources = [ + "video_render_impl.cc", + ] + deps = [ + ":video_render", + "../../system_wrappers", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } +} + +config("video_render_internal_impl_config") { + if (is_ios) { + libs = [ + "OpenGLES.framework", + "QuartzCore.framework", + ] + } +} + +source_set("video_render_internal_impl") { + libs = [] + sources = [ + "video_render_internal_impl.cc", + ] + deps = [ + ":video_render", + "../../system_wrappers", + ] + + if (is_linux) { + sources += [ + "linux/video_render_linux_impl.cc", + "linux/video_render_linux_impl.h", + "linux/video_x11_channel.cc", + "linux/video_x11_channel.h", + "linux/video_x11_render.cc", + "linux/video_x11_render.h", + ] + + libs += [ "Xext" ] + } + if (is_mac) { + sources += [ + "mac/cocoa_full_screen_window.h", + "mac/cocoa_full_screen_window.mm", + "mac/cocoa_render_view.h", + "mac/cocoa_render_view.mm", + "mac/video_render_agl.cc", + "mac/video_render_agl.h", + "mac/video_render_mac_carbon_impl.cc", + "mac/video_render_mac_carbon_impl.h", + "mac/video_render_mac_cocoa_impl.h", + "mac/video_render_mac_cocoa_impl.mm", + "mac/video_render_nsopengl.h", + "mac/video_render_nsopengl.mm", + ] + + libs += [ + "CoreVideo.framework", + "QTKit.framework", + ] + } + if (is_win) { + sources += [ + "windows/i_video_render_win.h", + "windows/video_render_direct3d9.cc", + "windows/video_render_direct3d9.h", + "windows/video_render_windows_impl.cc", + "windows/video_render_windows_impl.h", + ] + + directxsdk_exists = + (exec_script("//build/dir_exists.py", + [ rebase_path("//third_party/directxsdk/files", + root_build_dir) ], + "trim string") == "True") + if (directxsdk_exists) { + directxsdk_path = "//third_party/directxsdk/files" + } else { + directxsdk_path = getenv("DXSDK_DIR") + } + include_dirs = [ directxsdk_path + "/Include" ] + } + if (is_android) { + sources += [ + "android/video_render_android_impl.cc", + "android/video_render_android_impl.h", + "android/video_render_android_native_opengl2.cc", + "android/video_render_android_native_opengl2.h", + "android/video_render_android_surface_view.cc", + "android/video_render_android_surface_view.h", + "android/video_render_opengles20.cc", + "android/video_render_opengles20.h", + ] + + libs += [ "GLESv2" ] + } + if (is_ios) { + sources += [ + "ios/open_gles20.h", + "ios/open_gles20.mm", + "ios/video_render_ios_channel.h", + "ios/video_render_ios_channel.mm", + "ios/video_render_ios_gles20.h", + "ios/video_render_ios_gles20.mm", + "ios/video_render_ios_impl.h", + "ios/video_render_ios_impl.mm", + "ios/video_render_ios_view.h", + "ios/video_render_ios_view.mm", + ] + + cflags += [ "-fobjc-arc" ] # CLANG_ENABLE_OBJC_ARC = YES. + } + + all_dependent_configs = [ ":video_render_internal_impl_config"] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/include/video_render.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/include/video_render.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/include/video_render.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/include/video_render.h 2015-02-03 14:33:37.000000000 +0000 @@ -25,10 +25,6 @@ namespace webrtc { -#if defined(WEBRTC_ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) -int32_t SetRenderAndroidVM(void* javaVM); -#endif - // Class definitions class VideoRender: public Module { @@ -61,10 +57,10 @@ * * id - new unique identifier of this video render module object */ - virtual int32_t ChangeUniqueId(const int32_t id) = 0; + virtual int32_t ChangeUniqueId(const int32_t id) OVERRIDE = 0; - virtual int32_t TimeUntilNextProcess() = 0; - virtual int32_t Process() = 0; + virtual int32_t TimeUntilNextProcess() OVERRIDE = 0; + virtual int32_t Process() OVERRIDE = 0; /************************************************************************** * diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/incoming_video_stream.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/incoming_video_stream.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/incoming_video_stream.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/incoming_video_stream.cc 2015-02-03 14:33:37.000000000 +0000 @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/video_render//incoming_video_stream.h" +#include "webrtc/modules/video_render/incoming_video_stream.h" #include @@ -22,7 +22,7 @@ #endif #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" -#include "webrtc/modules/video_render//video_render_frames.h" +#include "webrtc/modules/video_render/video_render_frames.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/event_wrapper.h" #include "webrtc/system_wrappers/interface/thread_wrapper.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/open_gles20.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/open_gles20.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/open_gles20.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/open_gles20.mm 2015-02-03 14:33:37.000000000 +0000 @@ -8,6 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + // This files is mostly copied from // webrtc/modules/video_render/android/video_render_opengles20.h diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_channel.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_channel.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_channel.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_channel.mm 2015-02-03 14:33:37.000000000 +0000 @@ -8,6 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + #include "webrtc/modules/video_render/ios/video_render_ios_channel.h" using namespace webrtc; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_gles20.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_gles20.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_gles20.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_gles20.mm 2015-02-03 14:33:37.000000000 +0000 @@ -8,6 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + #include "webrtc/modules/video_render/ios/video_render_ios_gles20.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/event_wrapper.h" @@ -246,7 +250,7 @@ int VideoRenderIosGles20::ChangeWindow(void* new_window) { CriticalSectionScoped cs(gles_crit_sec_.get()); - view_ = (VideoRenderIosView*)new_window; + view_ = (__bridge VideoRenderIosView*)new_window; return 0; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_impl.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_impl.h 2015-02-03 14:33:37.000000000 +0000 @@ -99,7 +99,7 @@ bool full_screen_; CriticalSectionWrapper* crit_sec_; - VideoRenderIosGles20* ptr_ios_render_; + webrtc::scoped_ptr ptr_ios_render_; }; } // namespace webrtc #endif // WEBRTC_MODULES_VIDEO_RENDER_IOS_VIDEO_RENDER_IOS_IMPL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_impl.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_impl.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_impl.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_impl.mm 2015-02-03 14:33:37.000000000 +0000 @@ -8,6 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + #include "webrtc/modules/video_render/ios/video_render_ios_impl.h" #include "webrtc/modules/video_render/ios/video_render_ios_gles20.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" @@ -33,18 +37,13 @@ VideoRenderIosImpl::~VideoRenderIosImpl() { delete crit_sec_; - - if (ptr_ios_render_) { - delete ptr_ios_render_; - ptr_ios_render_ = NULL; - } } int32_t VideoRenderIosImpl::Init() { CriticalSectionScoped cs(crit_sec_); - ptr_ios_render_ = new VideoRenderIosGles20( - (VideoRenderIosView*)ptr_window_, full_screen_, id_); + ptr_ios_render_.reset(new VideoRenderIosGles20( + (__bridge VideoRenderIosView*)ptr_window_, full_screen_, id_)); return ptr_ios_render_->Init(); ; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_view.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_view.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_view.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_view.h 2015-02-03 14:33:37.000000000 +0000 @@ -16,15 +16,7 @@ #include "webrtc/modules/video_render/ios/open_gles20.h" -@interface VideoRenderIosView : UIView { - @private // NOLINT - EAGLContext* context_; - webrtc::OpenGles20* gles_renderer20_; - int _frameBufferWidth; - int _frameBufferHeight; - unsigned int _defaultFrameBuffer; - unsigned int _colorRenderBuffer; -} +@interface VideoRenderIosView : UIView - (BOOL)createContext; - (BOOL)presentFramebuffer; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_view.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_view.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_view.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/ios/video_render_ios_view.mm 2015-02-03 14:33:37.000000000 +0000 @@ -8,12 +8,23 @@ * be found in the AUTHORS file in the root of the source tree. */ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + #include "webrtc/modules/video_render/ios/video_render_ios_view.h" #include "webrtc/system_wrappers/interface/trace.h" using namespace webrtc; -@implementation VideoRenderIosView +@implementation VideoRenderIosView { + EAGLContext* _context; + webrtc::scoped_ptr _gles_renderer20; + int _frameBufferWidth; + int _frameBufferHeight; + unsigned int _defaultFrameBuffer; + unsigned int _colorRenderBuffer; +} @synthesize context = context_; @@ -25,7 +36,7 @@ // init super class self = [super initWithCoder:coder]; if (self) { - gles_renderer20_ = new OpenGles20(); + _gles_renderer20.reset(new OpenGles20()); } return self; } @@ -34,7 +45,7 @@ // init super class self = [super init]; if (self) { - gles_renderer20_ = new OpenGles20(); + _gles_renderer20.reset(new OpenGles20()); } return self; } @@ -43,7 +54,7 @@ // init super class self = [super initWithFrame:frame]; if (self) { - gles_renderer20_ = new OpenGles20(); + _gles_renderer20.reset(new OpenGles20()); } return self; } @@ -59,13 +70,7 @@ _colorRenderBuffer = 0; } - context_ = nil; - - if (gles_renderer20_) { - delete gles_renderer20_; - } - - [super dealloc]; + [EAGLContext setCurrentContext:nil]; } - (NSString*)description { @@ -84,14 +89,13 @@ kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; - context_ = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; - if (!context_) { + if (!_context) { return NO; } - // set current EAGLContext to self context_ - if (![EAGLContext setCurrentContext:context_]) { + if (![EAGLContext setCurrentContext:_context]) { return NO; } @@ -102,7 +106,7 @@ // Create color render buffer and allocate backing store. glGenRenderbuffers(1, &_colorRenderBuffer); glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer); - [context_ renderbufferStorage:GL_RENDERBUFFER + [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer]; glGetRenderbufferParameteriv( GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_frameBufferWidth); @@ -121,12 +125,12 @@ glBindFramebuffer(GL_FRAMEBUFFER, _defaultFrameBuffer); glViewport(0, 0, self.frame.size.width, self.frame.size.height); - return gles_renderer20_->Setup([self bounds].size.width, + return _gles_renderer20->Setup([self bounds].size.width, [self bounds].size.height); } - (BOOL)presentFramebuffer { - if (![context_ presentRenderbuffer:GL_RENDERBUFFER]) { + if (![_context presentRenderbuffer:GL_RENDERBUFFER]) { WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, 0, @@ -135,21 +139,15 @@ __FUNCTION__, __LINE__); } - - // update UI stuff on the main thread - [self performSelectorOnMainThread:@selector(setNeedsDisplay) - withObject:nil - waitUntilDone:NO]; - return YES; } - (BOOL)renderFrame:(I420VideoFrame*)frameToRender { - if (![EAGLContext setCurrentContext:context_]) { + if (![EAGLContext setCurrentContext:_context]) { return NO; } - return gles_renderer20_->Render(*frameToRender); + return _gles_renderer20->Render(*frameToRender); } - (BOOL)setCoordinatesForZOrder:(const float)zOrder @@ -157,7 +155,7 @@ Top:(const float)top Right:(const float)right Bottom:(const float)bottom { - return gles_renderer20_->SetCoordinates(zOrder, left, top, right, bottom); + return _gles_renderer20->SetCoordinates(zOrder, left, top, right, bottom); } @end diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/mac/cocoa_full_screen_window.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/mac/cocoa_full_screen_window.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/mac/cocoa_full_screen_window.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/mac/cocoa_full_screen_window.mm 2015-02-03 14:33:37.000000000 +0000 @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#import "cocoa_full_screen_window.h" -#include "trace.h" +#include "webrtc/modules/video_render/mac/cocoa_full_screen_window.h" +#include "webrtc/system_wrappers/interface/trace.h" using namespace webrtc; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/mac/cocoa_render_view.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/mac/cocoa_render_view.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/mac/cocoa_render_view.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/mac/cocoa_render_view.mm 2015-02-03 14:33:37.000000000 +0000 @@ -10,8 +10,9 @@ #import #import -#import "cocoa_render_view.h" -#include "trace.h" + +#include "webrtc/modules/video_render/mac/cocoa_render_view.h" +#include "webrtc/system_wrappers/interface/trace.h" using namespace webrtc; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/mac/video_render_mac_cocoa_impl.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/mac/video_render_mac_cocoa_impl.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/mac/video_render_mac_cocoa_impl.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/mac/video_render_mac_cocoa_impl.mm 2015-02-03 14:33:37.000000000 +0000 @@ -11,12 +11,11 @@ #include "webrtc/engine_configurations.h" #if defined(COCOA_RENDERING) -#import "cocoa_render_view.h" - -#include "video_render_mac_cocoa_impl.h" -#include "critical_section_wrapper.h" -#include "video_render_nsopengl.h" -#include "trace.h" +#include "webrtc/modules/video_render/mac/cocoa_render_view.h" +#include "webrtc/modules/video_render/mac/video_render_mac_cocoa_impl.h" +#include "webrtc/modules/video_render/mac/video_render_nsopengl.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/mac/video_render_nsopengl.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/mac/video_render_nsopengl.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/mac/video_render_nsopengl.mm 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/mac/video_render_nsopengl.mm 2015-02-03 14:33:37.000000000 +0000 @@ -11,12 +11,12 @@ #include "webrtc/engine_configurations.h" #if defined(COCOA_RENDERING) -#include "video_render_nsopengl.h" -#include "critical_section_wrapper.h" -#include "event_wrapper.h" -#include "trace.h" -#include "thread_wrapper.h" #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/modules/video_render/mac/video_render_nsopengl.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/event_wrapper.h" +#include "webrtc/system_wrappers/interface/thread_wrapper.h" +#include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/OWNERS 2015-02-03 14:33:37.000000000 +0000 @@ -1,5 +1,13 @@ -fischman@webrtc.org +mallinath@webrtc.org mflodman@webrtc.org perkj@webrtc.org -wu@webrtc.org -mallinath@webrtc.org +tkchin@webrtc.org + +per-file *.isolate=kjellander@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/test/testAPI/testAPI.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/test/testAPI/testAPI.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/test/testAPI/testAPI.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/test/testAPI/testAPI.cc 2015-02-03 14:33:37.000000000 +0000 @@ -294,11 +294,6 @@ VideoRenderCallback* renderCallback0 = renderModule->AddIncomingRenderStream(streamId0, 0, 0.0f, 0.0f, 1.0f, 1.0f); assert(renderCallback0 != NULL); -#ifndef WEBRTC_INCLUDE_INTERNAL_VIDEO_RENDER - MyRenderCallback externalRender; - renderModule->AddExternalRenderCallback(streamId0, &externalRender); -#endif - printf("Start render\n"); error = renderModule->StartRender(streamId0); if (error != 0) { @@ -423,6 +418,8 @@ } int TestMultipleStreams(VideoRender* renderModule) { + int error = 0; + // Add settings for a stream to render printf("Add stream 0\n"); const int streamId0 = 0; @@ -444,10 +441,19 @@ VideoRenderCallback* renderCallback3 = renderModule->AddIncomingRenderStream(streamId3, 0, 0.55f, 0.55f, 1.0f, 1.0f); assert(renderCallback3 != NULL); - assert(renderModule->StartRender(streamId0) == 0); - assert(renderModule->StartRender(streamId1) == 0); - assert(renderModule->StartRender(streamId2) == 0); - assert(renderModule->StartRender(streamId3) == 0); + error = renderModule->StartRender(streamId0); + if (error != 0) { + // TODO(phoglund): This test will not work if compiled in release mode. + // This rather silly construct here is to avoid compilation errors when + // compiling in release. Release => no asserts => unused 'error' variable. + assert(false); + } + error = renderModule->StartRender(streamId1); + assert(error == 0); + error = renderModule->StartRender(streamId2); + assert(error == 0); + error = renderModule->StartRender(streamId3); + assert(error == 0); // Loop through an I420 file and render each frame const int width = 352; @@ -493,19 +499,28 @@ // Shut down printf("Closing...\n"); - assert(renderModule->StopRender(streamId0) == 0); - assert(renderModule->DeleteIncomingRenderStream(streamId0) == 0); - assert(renderModule->StopRender(streamId1) == 0); - assert(renderModule->DeleteIncomingRenderStream(streamId1) == 0); - assert(renderModule->StopRender(streamId2) == 0); - assert(renderModule->DeleteIncomingRenderStream(streamId2) == 0); - assert(renderModule->StopRender(streamId3) == 0); - assert(renderModule->DeleteIncomingRenderStream(streamId3) == 0); + error = renderModule->StopRender(streamId0); + assert(error == 0); + error = renderModule->DeleteIncomingRenderStream(streamId0); + assert(error == 0); + error = renderModule->StopRender(streamId1); + assert(error == 0); + error = renderModule->DeleteIncomingRenderStream(streamId1); + assert(error == 0); + error = renderModule->StopRender(streamId2); + assert(error == 0); + error = renderModule->DeleteIncomingRenderStream(streamId2); + assert(error == 0); + error = renderModule->StopRender(streamId3); + assert(error == 0); + error = renderModule->DeleteIncomingRenderStream(streamId3); + assert(error == 0); return 0; } int TestExternalRender(VideoRender* renderModule) { + int error = 0; MyRenderCallback *externalRender = new MyRenderCallback(); const int streamId0 = 0; @@ -513,10 +528,16 @@ renderModule->AddIncomingRenderStream(streamId0, 0, 0.0f, 0.0f, 1.0f, 1.0f); assert(renderCallback0 != NULL); - assert(renderModule->AddExternalRenderCallback(streamId0, - externalRender) == 0); + error = renderModule->AddExternalRenderCallback(streamId0, externalRender); + if (error != 0) { + // TODO(phoglund): This test will not work if compiled in release mode. + // This rather silly construct here is to avoid compilation errors when + // compiling in release. Release => no asserts => unused 'error' variable. + assert(false); + } - assert(renderModule->StartRender(streamId0) == 0); + error = renderModule->StartRender(streamId0); + assert(error == 0); const int width = 352; const int half_width = (width + 1) / 2; @@ -536,8 +557,12 @@ // Sleep and let all frames be rendered before closing SleepMs(2*renderDelayMs); - assert(renderModule->StopRender(streamId0) == 0); - assert(renderModule->DeleteIncomingRenderStream(streamId0) == 0); + // Shut down + printf("Closing...\n"); + error = renderModule->StopRender(streamId0); + assert(error == 0); + error = renderModule->DeleteIncomingRenderStream(streamId0); + assert(error == 0); assert(frameCount == externalRender->_cnt); delete externalRender; @@ -547,10 +572,6 @@ } void RunVideoRenderTests(void* window, VideoRenderType windowType) { -#ifndef WEBRTC_INCLUDE_INTERNAL_VIDEO_RENDER - windowType = kRenderExternal; -#endif - int myId = 12345; // Create the render module @@ -562,7 +583,6 @@ windowType); assert(renderModule != NULL); - // ##### Test single stream rendering #### printf("#### TestSingleStream ####\n"); if (TestSingleStream(renderModule) != 0) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render_frames.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render_frames.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render_frames.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render_frames.cc 2015-02-03 14:33:37.000000000 +0000 @@ -55,12 +55,7 @@ } if (new_frame->native_handle() != NULL) { - incoming_frames_.push_back(new TextureVideoFrame( - static_cast(new_frame->native_handle()), - new_frame->width(), - new_frame->height(), - new_frame->timestamp(), - new_frame->render_time_ms())); + incoming_frames_.push_back(new_frame->CloneFrame()); return static_cast(incoming_frames_.size()); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render.gypi 2015-02-03 14:33:37.000000000 +0000 @@ -9,6 +9,10 @@ { 'targets': [ { + # Note this library is missing an implementation for the video render. + # For that targets must link with 'video_render_module_impl' or + # 'video_render_module_internal_impl' if they want to compile and use + # the internal render as the default renderer. 'target_name': 'video_render_module', 'type': 'static_library', 'dependencies': [ @@ -17,14 +21,6 @@ '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', ], 'sources': [ - 'android/video_render_android_impl.cc', - 'android/video_render_android_impl.h', - 'android/video_render_android_native_opengl2.cc', - 'android/video_render_android_native_opengl2.h', - 'android/video_render_android_surface_view.cc', - 'android/video_render_android_surface_view.h', - 'android/video_render_opengles20.cc', - 'android/video_render_opengles20.h', 'external/video_render_external_impl.cc', 'external/video_render_external_impl.h', 'i_video_render.h', @@ -32,52 +28,39 @@ 'include/video_render_defines.h', 'incoming_video_stream.cc', 'incoming_video_stream.h', - 'ios/open_gles20.h', - 'ios/open_gles20.mm', - 'ios/video_render_ios_channel.h', - 'ios/video_render_ios_channel.mm', - 'ios/video_render_ios_gles20.h', - 'ios/video_render_ios_gles20.mm', - 'ios/video_render_ios_impl.h', - 'ios/video_render_ios_impl.mm', - 'ios/video_render_ios_view.h', - 'ios/video_render_ios_view.mm', - 'linux/video_render_linux_impl.cc', - 'linux/video_render_linux_impl.h', - 'linux/video_x11_channel.cc', - 'linux/video_x11_channel.h', - 'linux/video_x11_render.cc', - 'linux/video_x11_render.h', - 'mac/cocoa_full_screen_window.mm', - 'mac/cocoa_full_screen_window.h', - 'mac/cocoa_render_view.mm', - 'mac/cocoa_render_view.h', - 'mac/video_render_agl.cc', - 'mac/video_render_agl.h', - 'mac/video_render_mac_carbon_impl.cc', - 'mac/video_render_mac_carbon_impl.h', - 'mac/video_render_mac_cocoa_impl.h', - 'mac/video_render_mac_cocoa_impl.mm', - 'mac/video_render_nsopengl.h', - 'mac/video_render_nsopengl.mm', 'video_render_frames.cc', 'video_render_frames.h', - 'video_render_impl.cc', 'video_render_impl.h', - 'windows/i_video_render_win.h', - 'windows/video_render_direct3d9.cc', - 'windows/video_render_direct3d9.h', - 'windows/video_render_windows_impl.cc', - 'windows/video_render_windows_impl.h', ], - # TODO(andrew): with the proper suffix, these files will be excluded - # automatically. + }, + { + # Default video_render_module implementation that only supports external + # renders. + 'target_name': 'video_render_module_impl', + 'type': 'static_library', + 'dependencies': [ + 'video_render_module', + ], + 'sources': [ + 'video_render_impl.cc', + ], + }, + { + # video_render_module implementation that supports the internal + # video_render implementation. + 'target_name': 'video_render_module_internal_impl', + 'type': 'static_library', + 'dependencies': [ + 'video_render_module', + ], + 'sources': [ + 'video_render_internal_impl.cc', + ], + # TODO(andrew): with the proper suffix, these files will be excluded + # automatically. 'conditions': [ - ['include_internal_video_render==1', { - 'defines': ['WEBRTC_INCLUDE_INTERNAL_VIDEO_RENDER',], - }], - ['OS!="android" or include_internal_video_render==0', { - 'sources!': [ + ['OS=="android"', { + 'sources': [ 'android/video_render_android_impl.h', 'android/video_render_android_native_opengl2.h', 'android/video_render_android_surface_view.h', @@ -87,9 +70,14 @@ 'android/video_render_android_surface_view.cc', 'android/video_render_opengles20.cc', ], + 'link_settings': { + 'libraries': [ + '-lGLESv2', + ], + }, }], - ['OS!="ios" or include_internal_video_render==0', { - 'sources!': [ + ['OS=="ios"', { + 'sources': [ # iOS 'ios/open_gles20.h', 'ios/open_gles20.mm', @@ -102,9 +90,21 @@ 'ios/video_render_ios_view.h', 'ios/video_render_ios_view.mm', ], + 'xcode_settings': { + 'CLANG_ENABLE_OBJC_ARC': 'YES', + }, + 'all_dependent_settings': { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-framework OpenGLES', + '-framework QuartzCore', + '-framework UIKit', + ], + }, + }, }], - ['OS!="linux" or include_internal_video_render==0', { - 'sources!': [ + ['OS=="linux"', { + 'sources': [ 'linux/video_render_linux_impl.h', 'linux/video_x11_channel.h', 'linux/video_x11_render.h', @@ -112,9 +112,14 @@ 'linux/video_x11_channel.cc', 'linux/video_x11_render.cc', ], + 'link_settings': { + 'libraries': [ + '-lXext', + ], + }, }], - ['OS!="mac" or include_internal_video_render==0', { - 'sources!': [ + ['OS=="mac"', { + 'sources': [ 'mac/cocoa_full_screen_window.h', 'mac/cocoa_render_view.h', 'mac/video_render_agl.h', @@ -129,62 +134,30 @@ 'mac/cocoa_full_screen_window.mm', ], }], - ['OS=="ios"', { - 'all_dependent_settings': { - 'xcode_settings': { - 'OTHER_LDFLAGS': [ - '-framework OpenGLES', - '-framework QuartzCore', - '-framework UIKit', - ], - }, - }, - }], - ['OS=="win" and include_internal_video_render==1', { - 'variables': { - # 'directx_sdk_path' will be overridden in the condition block - # below, but it must not be declared as empty here since gyp - # will check if the first character is '/' for some reason. - # If it's empty, we'll get an out-of-bounds error. - 'directx_sdk_path': 'will_be_overridden', - 'directx_sdk_default_path': '<(DEPTH)/third_party/directxsdk/files', - 'conditions': [ - ['" #include "webrtc/engine_configurations.h" -#include "webrtc/modules/video_render/i_video_render.h" +#include "webrtc/modules/video_render/external/video_render_external_impl.h" #include "webrtc/modules/video_render/include/video_render_defines.h" #include "webrtc/modules/video_render/incoming_video_stream.h" +#include "webrtc/modules/video_render/i_video_render.h" #include "webrtc/modules/video_render/video_render_impl.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/trace.h" -#ifdef WEBRTC_INCLUDE_INTERNAL_VIDEO_RENDER - -#if defined (_WIN32) -#include "webrtc/modules/video_render/windows/video_render_windows_impl.h" -#define STANDARD_RENDERING kRenderWindows - -// WEBRTC_IOS should go before WEBRTC_MAC because WEBRTC_MAC -// gets defined if WEBRTC_IOS is defined -#elif defined(WEBRTC_IOS) -#define STANDARD_RENDERING kRenderiOS -#include "ios/video_render_ios_impl.h" -#elif defined(WEBRTC_MAC) -#if defined(COCOA_RENDERING) -#define STANDARD_RENDERING kRenderCocoa -#include "webrtc/modules/video_render/mac/video_render_mac_cocoa_impl.h" -#elif defined(CARBON_RENDERING) -#define STANDARD_RENDERING kRenderCarbon -#include "webrtc/modules/video_render/mac/video_render_mac_carbon_impl.h" -#endif - -#elif defined(WEBRTC_ANDROID) -#include "webrtc/modules/video_render/android/video_render_android_impl.h" -#include "webrtc/modules/video_render/android/video_render_android_native_opengl2.h" -#include "webrtc/modules/video_render/android/video_render_android_surface_view.h" -#define STANDARD_RENDERING kRenderAndroid - -#elif defined(WEBRTC_LINUX) -#include "webrtc/modules/video_render/linux/video_render_linux_impl.h" -#define STANDARD_RENDERING kRenderX11 - -#else -//Other platforms -#endif - -#endif // WEBRTC_INCLUDE_INTERNAL_VIDEO_RENDER - -// For external rendering -#include "webrtc/modules/video_render/external/video_render_external_impl.h" -#ifndef STANDARD_RENDERING -#define STANDARD_RENDERING kRenderExternal -#endif // STANDARD_RENDERING - namespace webrtc { VideoRender* @@ -71,7 +30,7 @@ VideoRenderType resultVideoRenderType = videoRenderType; if (videoRenderType == kRenderDefault) { - resultVideoRenderType = STANDARD_RENDERING; + resultVideoRenderType = kRenderExternal; } return new ModuleVideoRenderImpl(id, resultVideoRenderType, window, fullscreen); @@ -98,97 +57,6 @@ // Create platform specific renderer switch (videoRenderType) { -#ifdef WEBRTC_INCLUDE_INTERNAL_VIDEO_RENDER - -#if defined(_WIN32) - case kRenderWindows: - { - VideoRenderWindowsImpl* ptrRenderer; - ptrRenderer = new VideoRenderWindowsImpl(_id, videoRenderType, window, _fullScreen); - if (ptrRenderer) - { - _ptrRenderer = reinterpret_cast(ptrRenderer); - } - } - break; - -#elif defined(WEBRTC_IOS) - case kRenderiOS: - { - VideoRenderIosImpl* ptrRenderer = new VideoRenderIosImpl(_id, window, _fullScreen); - if(ptrRenderer) - { - _ptrRenderer = reinterpret_cast(ptrRenderer); - } - } - break; - -#elif defined(WEBRTC_MAC) - -#if defined(COCOA_RENDERING) - case kRenderCocoa: - { - VideoRenderMacCocoaImpl* ptrRenderer = new VideoRenderMacCocoaImpl(_id, videoRenderType, window, _fullScreen); - if(ptrRenderer) - { - _ptrRenderer = reinterpret_cast(ptrRenderer); - } - } - - break; -#elif defined(CARBON_RENDERING) - case kRenderCarbon: - { - VideoRenderMacCarbonImpl* ptrRenderer = new VideoRenderMacCarbonImpl(_id, videoRenderType, window, _fullScreen); - if(ptrRenderer) - { - _ptrRenderer = reinterpret_cast(ptrRenderer); - } - } - break; -#endif - -#elif defined(WEBRTC_ANDROID) - case kRenderAndroid: - { - if(AndroidNativeOpenGl2Renderer::UseOpenGL2(window)) - { - AndroidNativeOpenGl2Renderer* ptrRenderer = NULL; - ptrRenderer = new AndroidNativeOpenGl2Renderer(_id, videoRenderType, window, _fullScreen); - if (ptrRenderer) - { - _ptrRenderer = reinterpret_cast (ptrRenderer); - } - } - else - { - AndroidSurfaceViewRenderer* ptrRenderer = NULL; - ptrRenderer = new AndroidSurfaceViewRenderer(_id, videoRenderType, window, _fullScreen); - if (ptrRenderer) - { - _ptrRenderer = reinterpret_cast (ptrRenderer); - } - } - - } - break; -#elif defined(WEBRTC_LINUX) - case kRenderX11: - { - VideoRenderLinuxImpl* ptrRenderer = NULL; - ptrRenderer = new VideoRenderLinuxImpl(_id, videoRenderType, window, _fullScreen); - if ( ptrRenderer ) - { - _ptrRenderer = reinterpret_cast (ptrRenderer); - } - } - break; - -#else - // Other platforms -#endif - -#endif // WEBRTC_INCLUDE_INTERNAL_VIDEO_RENDER case kRenderExternal: { VideoRenderExternalImpl* ptrRenderer(NULL); @@ -238,66 +106,6 @@ delete ptrRenderer; } break; -#ifdef WEBRTC_INCLUDE_INTERNAL_VIDEO_RENDER - -#if defined(_WIN32) - case kRenderWindows: - { - VideoRenderWindowsImpl* ptrRenderer = reinterpret_cast(_ptrRenderer); - _ptrRenderer = NULL; - delete ptrRenderer; - } - break; -#elif defined(WEBRTC_IOS) - case kRenderiOS: - { - VideoRenderIosImpl* ptrRenderer = reinterpret_cast (_ptrRenderer); - _ptrRenderer = NULL; - delete ptrRenderer; - } - break; -#elif defined(WEBRTC_MAC) - -#if defined(COCOA_RENDERING) - case kRenderCocoa: - { - VideoRenderMacCocoaImpl* ptrRenderer = reinterpret_cast (_ptrRenderer); - _ptrRenderer = NULL; - delete ptrRenderer; - } - break; -#elif defined(CARBON_RENDERING) - case kRenderCarbon: - { - VideoRenderMacCarbonImpl* ptrRenderer = reinterpret_cast (_ptrRenderer); - _ptrRenderer = NULL; - delete ptrRenderer; - } - break; -#endif - -#elif defined(WEBRTC_ANDROID) - case kRenderAndroid: - { - VideoRenderAndroid* ptrRenderer = reinterpret_cast (_ptrRenderer); - _ptrRenderer = NULL; - delete ptrRenderer; - } - break; - -#elif defined(WEBRTC_LINUX) - case kRenderX11: - { - VideoRenderLinuxImpl* ptrRenderer = reinterpret_cast (_ptrRenderer); - _ptrRenderer = NULL; - delete ptrRenderer; - } - break; -#else - //other platforms -#endif - -#endif // WEBRTC_INCLUDE_INTERNAL_VIDEO_RENDER default: // Error... @@ -341,56 +149,7 @@ int32_t ModuleVideoRenderImpl::ChangeWindow(void* window) { - - CriticalSectionScoped cs(&_moduleCrit); - -#ifdef WEBRTC_INCLUDE_INTERNAL_VIDEO_RENDER - -#if defined(WEBRTC_IOS) // WEBRTC_IOS must go before WEBRTC_MAC - _ptrRenderer = NULL; - delete _ptrRenderer; - - VideoRenderIosImpl* ptrRenderer; - ptrRenderer = new VideoRenderIosImpl(_id, window, _fullScreen); - if (!ptrRenderer) - { - return -1; - } - _ptrRenderer = reinterpret_cast(ptrRenderer); - return _ptrRenderer->ChangeWindow(window); -#elif defined(WEBRTC_MAC) - - _ptrRenderer = NULL; - delete _ptrRenderer; - -#if defined(COCOA_RENDERING) - VideoRenderMacCocoaImpl* ptrRenderer; - ptrRenderer = new VideoRenderMacCocoaImpl(_id, kRenderCocoa, window, _fullScreen); -#elif defined(CARBON_RENDERING) - VideoRenderMacCarbonImpl* ptrRenderer; - ptrRenderer = new VideoRenderMacCarbonImpl(_id, kRenderCarbon, window, _fullScreen); -#endif - if (!ptrRenderer) - { - return -1; - } - _ptrRenderer = reinterpret_cast(ptrRenderer); - return _ptrRenderer->ChangeWindow(window); - -#else - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return -1; - } - return _ptrRenderer->ChangeWindow(window); - -#endif - -#else // WEBRTC_INCLUDE_INTERNAL_VIDEO_RENDER return -1; -#endif } int32_t ModuleVideoRenderImpl::Id() diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render_internal.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render_internal.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render_internal.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render_internal.h 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_VIDEO_RENDER_INTERNAL_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_VIDEO_RENDER_INTERNAL_H_ + +#ifdef ANDROID +#include + +namespace webrtc { + +// In order to be able to use the internal webrtc video render +// for android, the jvm objects must be set via this method. +int32_t SetRenderAndroidVM(JavaVM* javaVM); + +} // namespace webrtc + +#endif // ANDROID + +#endif // WEBRTC_MODULES_VIDEO_RENDER_VIDEO_RENDER_INTERNAL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render_internal_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render_internal_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render_internal_impl.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render_internal_impl.cc 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,917 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/engine_configurations.h" +#include "webrtc/modules/video_render/i_video_render.h" +#include "webrtc/modules/video_render/include/video_render_defines.h" +#include "webrtc/modules/video_render/incoming_video_stream.h" +#include "webrtc/modules/video_render/video_render_impl.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/trace.h" + +#if defined (_WIN32) +#include "webrtc/modules/video_render/windows/video_render_windows_impl.h" +#define STANDARD_RENDERING kRenderWindows + +// WEBRTC_IOS should go before WEBRTC_MAC because WEBRTC_MAC +// gets defined if WEBRTC_IOS is defined +#elif defined(WEBRTC_IOS) +#define STANDARD_RENDERING kRenderiOS +#include "webrtc/modules/video_render/ios/video_render_ios_impl.h" +#elif defined(WEBRTC_MAC) +#if defined(COCOA_RENDERING) +#define STANDARD_RENDERING kRenderCocoa +#include "webrtc/modules/video_render/mac/video_render_mac_cocoa_impl.h" +#elif defined(CARBON_RENDERING) +#define STANDARD_RENDERING kRenderCarbon +#include "webrtc/modules/video_render/mac/video_render_mac_carbon_impl.h" +#endif + +#elif defined(WEBRTC_ANDROID) +#include "webrtc/modules/video_render/android/video_render_android_impl.h" +#include "webrtc/modules/video_render/android/video_render_android_native_opengl2.h" +#include "webrtc/modules/video_render/android/video_render_android_surface_view.h" +#define STANDARD_RENDERING kRenderAndroid + +#elif defined(WEBRTC_LINUX) +#include "webrtc/modules/video_render/linux/video_render_linux_impl.h" +#define STANDARD_RENDERING kRenderX11 + +#else +//Other platforms +#endif + +// For external rendering +#include "webrtc/modules/video_render/external/video_render_external_impl.h" +#ifndef STANDARD_RENDERING +#define STANDARD_RENDERING kRenderExternal +#endif // STANDARD_RENDERING + +namespace webrtc { + +VideoRender* +VideoRender::CreateVideoRender(const int32_t id, + void* window, + const bool fullscreen, + const VideoRenderType videoRenderType/*=kRenderDefault*/) +{ + VideoRenderType resultVideoRenderType = videoRenderType; + if (videoRenderType == kRenderDefault) + { + resultVideoRenderType = STANDARD_RENDERING; + } + return new ModuleVideoRenderImpl(id, resultVideoRenderType, window, + fullscreen); +} + +void VideoRender::DestroyVideoRender( + VideoRender* module) +{ + if (module) + { + delete module; + } +} + +ModuleVideoRenderImpl::ModuleVideoRenderImpl( + const int32_t id, + const VideoRenderType videoRenderType, + void* window, + const bool fullscreen) : + _id(id), _moduleCrit(*CriticalSectionWrapper::CreateCriticalSection()), + _ptrWindow(window), _fullScreen(fullscreen), _ptrRenderer(NULL) +{ + + // Create platform specific renderer + switch (videoRenderType) + { +#if defined(_WIN32) + case kRenderWindows: + { + VideoRenderWindowsImpl* ptrRenderer; + ptrRenderer = new VideoRenderWindowsImpl(_id, videoRenderType, window, _fullScreen); + if (ptrRenderer) + { + _ptrRenderer = reinterpret_cast(ptrRenderer); + } + } + break; + +#elif defined(WEBRTC_IOS) + case kRenderiOS: + { + VideoRenderIosImpl* ptrRenderer = new VideoRenderIosImpl(_id, window, _fullScreen); + if(ptrRenderer) + { + _ptrRenderer = reinterpret_cast(ptrRenderer); + } + } + break; + +#elif defined(WEBRTC_MAC) + +#if defined(COCOA_RENDERING) + case kRenderCocoa: + { + VideoRenderMacCocoaImpl* ptrRenderer = new VideoRenderMacCocoaImpl(_id, videoRenderType, window, _fullScreen); + if(ptrRenderer) + { + _ptrRenderer = reinterpret_cast(ptrRenderer); + } + } + + break; +#elif defined(CARBON_RENDERING) + case kRenderCarbon: + { + VideoRenderMacCarbonImpl* ptrRenderer = new VideoRenderMacCarbonImpl(_id, videoRenderType, window, _fullScreen); + if(ptrRenderer) + { + _ptrRenderer = reinterpret_cast(ptrRenderer); + } + } + break; +#endif + +#elif defined(WEBRTC_ANDROID) + case kRenderAndroid: + { + if(AndroidNativeOpenGl2Renderer::UseOpenGL2(window)) + { + AndroidNativeOpenGl2Renderer* ptrRenderer = NULL; + ptrRenderer = new AndroidNativeOpenGl2Renderer(_id, videoRenderType, window, _fullScreen); + if (ptrRenderer) + { + _ptrRenderer = reinterpret_cast (ptrRenderer); + } + } + else + { + AndroidSurfaceViewRenderer* ptrRenderer = NULL; + ptrRenderer = new AndroidSurfaceViewRenderer(_id, videoRenderType, window, _fullScreen); + if (ptrRenderer) + { + _ptrRenderer = reinterpret_cast (ptrRenderer); + } + } + + } + break; +#elif defined(WEBRTC_LINUX) + case kRenderX11: + { + VideoRenderLinuxImpl* ptrRenderer = NULL; + ptrRenderer = new VideoRenderLinuxImpl(_id, videoRenderType, window, _fullScreen); + if ( ptrRenderer ) + { + _ptrRenderer = reinterpret_cast (ptrRenderer); + } + } + break; + +#else + // Other platforms +#endif + case kRenderExternal: + { + VideoRenderExternalImpl* ptrRenderer(NULL); + ptrRenderer = new VideoRenderExternalImpl(_id, videoRenderType, + window, _fullScreen); + if (ptrRenderer) + { + _ptrRenderer = reinterpret_cast (ptrRenderer); + } + } + break; + default: + // Error... + break; + } + if (_ptrRenderer) + { + if (_ptrRenderer->Init() == -1) + { + } + } +} + +ModuleVideoRenderImpl::~ModuleVideoRenderImpl() +{ + delete &_moduleCrit; + + for (IncomingVideoStreamMap::iterator it = _streamRenderMap.begin(); + it != _streamRenderMap.end(); + ++it) { + delete it->second; + } + + // Delete platform specific renderer + if (_ptrRenderer) + { + VideoRenderType videoRenderType = _ptrRenderer->RenderType(); + + switch (videoRenderType) + { + case kRenderExternal: + { + VideoRenderExternalImpl + * ptrRenderer = + reinterpret_cast (_ptrRenderer); + _ptrRenderer = NULL; + delete ptrRenderer; + } + break; +#if defined(_WIN32) + case kRenderWindows: + { + VideoRenderWindowsImpl* ptrRenderer = reinterpret_cast(_ptrRenderer); + _ptrRenderer = NULL; + delete ptrRenderer; + } + break; +#elif defined(WEBRTC_IOS) + case kRenderiOS: + { + VideoRenderIosImpl* ptrRenderer = reinterpret_cast (_ptrRenderer); + _ptrRenderer = NULL; + delete ptrRenderer; + } + break; +#elif defined(WEBRTC_MAC) + +#if defined(COCOA_RENDERING) + case kRenderCocoa: + { + VideoRenderMacCocoaImpl* ptrRenderer = reinterpret_cast (_ptrRenderer); + _ptrRenderer = NULL; + delete ptrRenderer; + } + break; +#elif defined(CARBON_RENDERING) + case kRenderCarbon: + { + VideoRenderMacCarbonImpl* ptrRenderer = reinterpret_cast (_ptrRenderer); + _ptrRenderer = NULL; + delete ptrRenderer; + } + break; +#endif + +#elif defined(WEBRTC_ANDROID) + case kRenderAndroid: + { + VideoRenderAndroid* ptrRenderer = reinterpret_cast (_ptrRenderer); + _ptrRenderer = NULL; + delete ptrRenderer; + } + break; + +#elif defined(WEBRTC_LINUX) + case kRenderX11: + { + VideoRenderLinuxImpl* ptrRenderer = reinterpret_cast (_ptrRenderer); + _ptrRenderer = NULL; + delete ptrRenderer; + } + break; +#else + //other platforms +#endif + + default: + // Error... + break; + } + } +} + +int32_t ModuleVideoRenderImpl::ChangeUniqueId(const int32_t id) +{ + + CriticalSectionScoped cs(&_moduleCrit); + + _id = id; + + if (_ptrRenderer) + { + _ptrRenderer->ChangeUniqueId(_id); + } + + return 0; +} + +int32_t ModuleVideoRenderImpl::TimeUntilNextProcess() +{ + // Not used + return 50; +} +int32_t ModuleVideoRenderImpl::Process() +{ + // Not used + return 0; +} + +void* +ModuleVideoRenderImpl::Window() +{ + CriticalSectionScoped cs(&_moduleCrit); + return _ptrWindow; +} + +int32_t ModuleVideoRenderImpl::ChangeWindow(void* window) +{ + + CriticalSectionScoped cs(&_moduleCrit); + +#if defined(WEBRTC_IOS) // WEBRTC_IOS must go before WEBRTC_MAC + _ptrRenderer = NULL; + delete _ptrRenderer; + + VideoRenderIosImpl* ptrRenderer; + ptrRenderer = new VideoRenderIosImpl(_id, window, _fullScreen); + if (!ptrRenderer) + { + return -1; + } + _ptrRenderer = reinterpret_cast(ptrRenderer); + return _ptrRenderer->ChangeWindow(window); +#elif defined(WEBRTC_MAC) + + _ptrRenderer = NULL; + delete _ptrRenderer; + +#if defined(COCOA_RENDERING) + VideoRenderMacCocoaImpl* ptrRenderer; + ptrRenderer = new VideoRenderMacCocoaImpl(_id, kRenderCocoa, window, _fullScreen); +#elif defined(CARBON_RENDERING) + VideoRenderMacCarbonImpl* ptrRenderer; + ptrRenderer = new VideoRenderMacCarbonImpl(_id, kRenderCarbon, window, _fullScreen); +#endif + if (!ptrRenderer) + { + return -1; + } + _ptrRenderer = reinterpret_cast(ptrRenderer); + return _ptrRenderer->ChangeWindow(window); + +#else + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + return _ptrRenderer->ChangeWindow(window); + +#endif +} + +int32_t ModuleVideoRenderImpl::Id() +{ + CriticalSectionScoped cs(&_moduleCrit); + return _id; +} + +uint32_t ModuleVideoRenderImpl::GetIncomingFrameRate(const uint32_t streamId) { + CriticalSectionScoped cs(&_moduleCrit); + + IncomingVideoStreamMap::iterator it = _streamRenderMap.find(streamId); + + if (it == _streamRenderMap.end()) { + // This stream doesn't exist + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "%s: stream doesn't exist", + __FUNCTION__); + return 0; + } + assert(it->second != NULL); + return it->second->IncomingRate(); +} + +VideoRenderCallback* +ModuleVideoRenderImpl::AddIncomingRenderStream(const uint32_t streamId, + const uint32_t zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return NULL; + } + + if (_streamRenderMap.find(streamId) != _streamRenderMap.end()) { + // The stream already exists... + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: stream already exists", __FUNCTION__); + return NULL; + } + + VideoRenderCallback* ptrRenderCallback = + _ptrRenderer->AddIncomingRenderStream(streamId, zOrder, left, top, + right, bottom); + if (ptrRenderCallback == NULL) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Can't create incoming stream in renderer", + __FUNCTION__); + return NULL; + } + + // Create platform independant code + IncomingVideoStream* ptrIncomingStream = new IncomingVideoStream(_id, + streamId); + if (ptrIncomingStream == NULL) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Can't create incoming stream", __FUNCTION__); + return NULL; + } + + + if (ptrIncomingStream->SetRenderCallback(ptrRenderCallback) == -1) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Can't set render callback", __FUNCTION__); + delete ptrIncomingStream; + _ptrRenderer->DeleteIncomingRenderStream(streamId); + return NULL; + } + + VideoRenderCallback* moduleCallback = + ptrIncomingStream->ModuleCallback(); + + // Store the stream + _streamRenderMap[streamId] = ptrIncomingStream; + + return moduleCallback; +} + +int32_t ModuleVideoRenderImpl::DeleteIncomingRenderStream( + const uint32_t streamId) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + + IncomingVideoStreamMap::iterator item = _streamRenderMap.find(streamId); + if (item == _streamRenderMap.end()) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: stream doesn't exist", __FUNCTION__); + return -1; + } + + delete item->second; + + _ptrRenderer->DeleteIncomingRenderStream(streamId); + + _streamRenderMap.erase(item); + + return 0; +} + +int32_t ModuleVideoRenderImpl::AddExternalRenderCallback( + const uint32_t streamId, + VideoRenderCallback* renderObject) { + CriticalSectionScoped cs(&_moduleCrit); + + IncomingVideoStreamMap::iterator item = _streamRenderMap.find(streamId); + + if (item == _streamRenderMap.end()) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: stream doesn't exist", __FUNCTION__); + return -1; + } + + if (item->second == NULL) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: could not get stream", __FUNCTION__); + return -1; + } + return item->second->SetExternalCallback(renderObject); +} + +int32_t ModuleVideoRenderImpl::GetIncomingRenderStreamProperties( + const uint32_t streamId, + uint32_t& zOrder, + float& left, + float& top, + float& right, + float& bottom) const { + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + + return _ptrRenderer->GetIncomingRenderStreamProperties(streamId, zOrder, + left, top, right, + bottom); +} + +uint32_t ModuleVideoRenderImpl::GetNumIncomingRenderStreams() const +{ + CriticalSectionScoped cs(&_moduleCrit); + + return static_cast(_streamRenderMap.size()); +} + +bool ModuleVideoRenderImpl::HasIncomingRenderStream( + const uint32_t streamId) const { + CriticalSectionScoped cs(&_moduleCrit); + + return _streamRenderMap.find(streamId) != _streamRenderMap.end(); +} + +int32_t ModuleVideoRenderImpl::RegisterRawFrameCallback( + const uint32_t streamId, + VideoRenderCallback* callbackObj) { + return -1; +} + +int32_t ModuleVideoRenderImpl::StartRender(const uint32_t streamId) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + + // Start the stream + IncomingVideoStreamMap::iterator item = _streamRenderMap.find(streamId); + + if (item == _streamRenderMap.end()) + { + return -1; + } + + if (item->second->Start() == -1) + { + return -1; + } + + // Start the HW renderer + if (_ptrRenderer->StartRender() == -1) + { + return -1; + } + return 0; +} + +int32_t ModuleVideoRenderImpl::StopRender(const uint32_t streamId) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s(%d): No renderer", __FUNCTION__, streamId); + return -1; + } + + // Stop the incoming stream + IncomingVideoStreamMap::iterator item = _streamRenderMap.find(streamId); + + if (item == _streamRenderMap.end()) + { + return -1; + } + + if (item->second->Stop() == -1) + { + return -1; + } + + return 0; +} + +int32_t ModuleVideoRenderImpl::ResetRender() +{ + CriticalSectionScoped cs(&_moduleCrit); + + int32_t ret = 0; + // Loop through all incoming streams and reset them + for (IncomingVideoStreamMap::iterator it = _streamRenderMap.begin(); + it != _streamRenderMap.end(); + ++it) { + if (it->second->Reset() == -1) + ret = -1; + } + return ret; +} + +RawVideoType ModuleVideoRenderImpl::PreferredVideoType() const +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (_ptrRenderer == NULL) + { + return kVideoI420; + } + + return _ptrRenderer->PerferedVideoType(); +} + +bool ModuleVideoRenderImpl::IsFullScreen() +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->FullScreen(); +} + +int32_t ModuleVideoRenderImpl::GetScreenResolution( + uint32_t& screenWidth, + uint32_t& screenHeight) const +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->GetScreenResolution(screenWidth, screenHeight); +} + +uint32_t ModuleVideoRenderImpl::RenderFrameRate( + const uint32_t streamId) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->RenderFrameRate(streamId); +} + +int32_t ModuleVideoRenderImpl::SetStreamCropping( + const uint32_t streamId, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->SetStreamCropping(streamId, left, top, right, bottom); +} + +int32_t ModuleVideoRenderImpl::SetTransparentBackground(const bool enable) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->SetTransparentBackground(enable); +} + +int32_t ModuleVideoRenderImpl::FullScreenRender(void* window, const bool enable) +{ + return -1; +} + +int32_t ModuleVideoRenderImpl::SetText( + const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + return _ptrRenderer->SetText(textId, text, textLength, textColorRef, + backgroundColorRef, left, top, right, bottom); +} + +int32_t ModuleVideoRenderImpl::SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + return _ptrRenderer->SetBitmap(bitMap, pictureId, colorKey, left, top, + right, bottom); +} + +int32_t ModuleVideoRenderImpl::GetLastRenderedFrame( + const uint32_t streamId, + I420VideoFrame &frame) const +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + + IncomingVideoStreamMap::const_iterator item = + _streamRenderMap.find(streamId); + if (item == _streamRenderMap.end()) + { + // This stream doesn't exist + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: stream doesn't exist", __FUNCTION__); + return 0; + } + + assert(item->second != NULL); + return item->second->GetLastRenderedFrame(frame); +} + +int32_t ModuleVideoRenderImpl::SetExpectedRenderDelay( + uint32_t stream_id, int32_t delay_ms) { + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + + IncomingVideoStreamMap::const_iterator item = + _streamRenderMap.find(stream_id); + if (item == _streamRenderMap.end()) { + // This stream doesn't exist + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s(%u, %d): stream doesn't exist", __FUNCTION__, stream_id, + delay_ms); + return -1; + } + + assert(item->second != NULL); + return item->second->SetExpectedRenderDelay(delay_ms); +} + +int32_t ModuleVideoRenderImpl::ConfigureRenderer( + const uint32_t streamId, + const unsigned int zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->ConfigureRenderer(streamId, zOrder, left, top, right, + bottom); +} + +int32_t ModuleVideoRenderImpl::SetStartImage( + const uint32_t streamId, + const I420VideoFrame& videoFrame) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + + IncomingVideoStreamMap::const_iterator item = + _streamRenderMap.find(streamId); + if (item == _streamRenderMap.end()) + { + // This stream doesn't exist + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: stream doesn't exist", __FUNCTION__); + return -1; + } + assert (item->second != NULL); + return item->second->SetStartImage(videoFrame); + +} + +int32_t ModuleVideoRenderImpl::SetTimeoutImage( + const uint32_t streamId, + const I420VideoFrame& videoFrame, + const uint32_t timeout) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + + IncomingVideoStreamMap::const_iterator item = + _streamRenderMap.find(streamId); + if (item == _streamRenderMap.end()) + { + // This stream doesn't exist + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: stream doesn't exist", __FUNCTION__); + return -1; + } + assert(item->second != NULL); + return item->second->SetTimeoutImage(videoFrame, timeout); +} + +int32_t ModuleVideoRenderImpl::MirrorRenderStream(const int renderId, + const bool enable, + const bool mirrorXAxis, + const bool mirrorYAxis) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + + IncomingVideoStreamMap::const_iterator item = + _streamRenderMap.find(renderId); + if (item == _streamRenderMap.end()) + { + // This stream doesn't exist + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: stream doesn't exist", __FUNCTION__); + return 0; + } + assert(item->second != NULL); + + return item->second->EnableMirroring(enable, mirrorXAxis, mirrorYAxis); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render_tests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render_tests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render_tests.isolate 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/video_render_tests.isolate 2015-02-03 14:33:37.000000000 +0000 @@ -8,28 +8,23 @@ { 'conditions': [ ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. 'variables': { - 'isolate_dependency_untracked': [ - '../../../../data/', - '../../../../resources/', + 'files': [ + '<(DEPTH)/data/', + '<(DEPTH)/resources/', ], }, }], ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../../../testing/test_env.py', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/video_render_tests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_tracked': [ - '../../../testing/test_env.py', + 'files': [ + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/video_render_tests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '../../../tools/swarming_client/', - ], }, }], ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/windows/video_render_direct3d9.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/windows/video_render_direct3d9.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/modules/video_render/windows/video_render_direct3d9.cc 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/modules/video_render/windows/video_render_direct3d9.cc 2015-02-03 14:33:37.000000000 +0000 @@ -294,8 +294,8 @@ _logoRight(0), _logoBottom(0), _pd3dSurface(NULL), - _totalMemory(-1), - _availableMemory(-1) + _totalMemory(0), + _availableMemory(0) { _screenUpdateThread = ThreadWrapper::CreateThread(ScreenUpdateThreadProc, this, kRealtimePriority); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/overrides/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/overrides/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/overrides/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/overrides/OWNERS 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,13 @@ +henrika@webrtc.org +henrike@webrtc.org +henrikg@webrtc.org +hta@webrtc.org +jiayl@webrtc.org +juberti@webrtc.org +mflodman@webrtc.org +perkj@webrtc.org +pthatcher@webrtc.org +sergeyu@chromium.org +tommi@webrtc.org + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/basictypes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/basictypes.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/basictypes.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/basictypes.h 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,108 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file overrides the inclusion of webrtc/base/basictypes.h to remove +// collisions with Chromium's base/basictypes.h. We then add back a few +// items that Chromium's version doesn't provide, but libjingle expects. + +#ifndef OVERRIDES_WEBRTC_BASE_BASICTYPES_H__ +#define OVERRIDES_WEBRTC_BASE_BASICTYPES_H__ + +#include "base/basictypes.h" +#include "build/build_config.h" + +#ifndef INT_TYPES_DEFINED +#define INT_TYPES_DEFINED + +#ifdef COMPILER_MSVC +#if _MSC_VER >= 1600 +#include +#else +typedef unsigned __int64 uint64; +typedef __int64 int64; +#endif +#ifndef INT64_C +#define INT64_C(x) x ## I64 +#endif +#ifndef UINT64_C +#define UINT64_C(x) x ## UI64 +#endif +#define INT64_F "I64" +#else // COMPILER_MSVC +#ifndef INT64_C +#define INT64_C(x) x ## LL +#endif +#ifndef UINT64_C +#define UINT64_C(x) x ## ULL +#endif +#ifndef INT64_F +#define INT64_F "ll" +#endif +#endif // COMPILER_MSVC +#endif // INT_TYPES_DEFINED + +// Detect compiler is for x86 or x64. +#if defined(__x86_64__) || defined(_M_X64) || \ + defined(__i386__) || defined(_M_IX86) +#define CPU_X86 1 +#endif +// Detect compiler is for arm. +#if defined(__arm__) || defined(_M_ARM) +#define CPU_ARM 1 +#endif +#if defined(CPU_X86) && defined(CPU_ARM) +#error CPU_X86 and CPU_ARM both defined. +#endif +#if !defined(ARCH_CPU_BIG_ENDIAN) && !defined(ARCH_CPU_LITTLE_ENDIAN) +// x86, arm or GCC provided __BYTE_ORDER__ macros +#if CPU_X86 || CPU_ARM || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +#define ARCH_CPU_LITTLE_ENDIAN +#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define ARCH_CPU_BIG_ENDIAN +#else +#error ARCH_CPU_BIG_ENDIAN or ARCH_CPU_LITTLE_ENDIAN should be defined. +#endif +#endif +#if defined(ARCH_CPU_BIG_ENDIAN) && defined(ARCH_CPU_LITTLE_ENDIAN) +#error ARCH_CPU_BIG_ENDIAN and ARCH_CPU_LITTLE_ENDIAN both defined. +#endif + +#if defined(WEBRTC_WIN) +typedef int socklen_t; +#endif + +namespace rtc { +template inline T _min(T a, T b) { return (a > b) ? b : a; } +template inline T _max(T a, T b) { return (a < b) ? b : a; } + +// For wait functions that take a number of milliseconds, kForever indicates +// unlimited time. +const int kForever = -1; +} + +#if defined(WEBRTC_WIN) +#if _MSC_VER < 1700 + #define alignof(t) __alignof(t) +#endif +#else // !WEBRTC_WIN +#define alignof(t) __alignof__(t) +#endif // !WEBRTC_WIN +#define RTC_IS_ALIGNED(p, a) (0==(reinterpret_cast(p) & ((a)-1))) +#define ALIGNP(p, t) \ + (reinterpret_cast(((reinterpret_cast(p) + \ + ((t)-1)) & ~((t)-1)))) + +// LIBJINGLE_DEFINE_STATIC_LOCAL() is a libjingle's copy +// of CR_DEFINE_STATIC_LOCAL(). +#define LIBJINGLE_DEFINE_STATIC_LOCAL(type, name, arguments) \ + CR_DEFINE_STATIC_LOCAL(type, name, arguments) + +#endif // OVERRIDES_WEBRTC_BASE_BASICTYPES_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/constructormagic.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/constructormagic.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/constructormagic.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/constructormagic.h 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file overrides the inclusion of webrtc/base/constructormagic.h +// We do this because constructor magic defines DISALLOW_EVIL_CONSTRUCTORS, +// but we want to use the version from Chromium. + +#ifndef OVERRIDES_WEBRTC_BASE_CONSTRUCTORMAGIC_H__ +#define OVERRIDES_WEBRTC_BASE_CONSTRUCTORMAGIC_H__ + +#include "base/macros.h" + +#endif // OVERRIDES_WEBRTC_BASE_CONSTRUCTORMAGIC_H__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/logging.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/logging.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/logging.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/logging.cc 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,323 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "third_party/webrtc/overrides/webrtc/base/logging.h" + +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +#include +#endif // OS_MACOSX + +#include + +#include "base/atomicops.h" +#include "base/strings/string_util.h" +#include "base/threading/platform_thread.h" +#include "third_party/webrtc/base/ipaddress.h" +#include "third_party/webrtc/base/stream.h" +#include "third_party/webrtc/base/stringencode.h" +#include "third_party/webrtc/base/stringutils.h" +#include "third_party/webrtc/base/timeutils.h" + +// From this file we can't use VLOG since it expands into usage of the __FILE__ +// macro (for correct filtering). The actual logging call from DIAGNOSTIC_LOG in +// ~DiagnosticLogMessage. Note that the second parameter to the LAZY_STREAM +// macro is true since the filter check has already been done for +// DIAGNOSTIC_LOG. +#define LOG_LAZY_STREAM_DIRECT(file_name, line_number, sev) \ + LAZY_STREAM(logging::LogMessage(file_name, line_number, \ + -sev).stream(), true) + +namespace rtc { + +void (*g_logging_delegate_function)(const std::string&) = NULL; +void (*g_extra_logging_init_function)( + void (*logging_delegate_function)(const std::string&)) = NULL; +#ifndef NDEBUG +COMPILE_ASSERT(sizeof(base::subtle::Atomic32) == sizeof(base::PlatformThreadId), + atomic32_not_same_size_as_platformthreadid); +base::subtle::Atomic32 g_init_logging_delegate_thread_id = 0; +#endif + +///////////////////////////////////////////////////////////////////////////// +// Constant Labels +///////////////////////////////////////////////////////////////////////////// + +const char* FindLabel(int value, const ConstantLabel entries[]) { + for (int i = 0; entries[i].label; ++i) { + if (value == entries[i].value) return entries[i].label; + } + return 0; +} + +std::string ErrorName(int err, const ConstantLabel* err_table) { + if (err == 0) + return "No error"; + + if (err_table != 0) { + if (const char * value = FindLabel(err, err_table)) + return value; + } + + char buffer[16]; + base::snprintf(buffer, sizeof(buffer), "0x%08x", err); + return buffer; +} + +///////////////////////////////////////////////////////////////////////////// +// Log helper functions +///////////////////////////////////////////////////////////////////////////// + +// Generates extra information for LOG_E. +static std::string GenerateExtra(LogErrorContext err_ctx, + int err, + const char* module) { + if (err_ctx != ERRCTX_NONE) { + std::ostringstream tmp; + tmp << ": "; + tmp << "[0x" << std::setfill('0') << std::hex << std::setw(8) << err << "]"; + switch (err_ctx) { + case ERRCTX_ERRNO: + tmp << " " << strerror(err); + break; +#if defined(WEBRTC_WIN) + case ERRCTX_HRESULT: { + char msgbuf[256]; + DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM; + HMODULE hmod = GetModuleHandleA(module); + if (hmod) + flags |= FORMAT_MESSAGE_FROM_HMODULE; + if (DWORD len = FormatMessageA( + flags, hmod, err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + msgbuf, sizeof(msgbuf) / sizeof(msgbuf[0]), NULL)) { + while ((len > 0) && + isspace(static_cast(msgbuf[len-1]))) { + msgbuf[--len] = 0; + } + tmp << " " << msgbuf; + } + break; + } +#endif // OS_WIN +#if defined(WEBRTC_IOS) + case ERRCTX_OSSTATUS: + tmp << " " << "Unknown LibJingle error: " << err; + break; +#elif defined(WEBRTC_MAC) + case ERRCTX_OSSTATUS: { + tmp << " " << nonnull(GetMacOSStatusErrorString(err), "Unknown error"); + if (const char* desc = GetMacOSStatusCommentString(err)) { + tmp << ": " << desc; + } + break; + } +#endif // OS_MACOSX + default: + break; + } + return tmp.str(); + } + return ""; +} + +DiagnosticLogMessage::DiagnosticLogMessage(const char* file, + int line, + LoggingSeverity severity, + bool log_to_chrome, + LogErrorContext err_ctx, + int err) + : file_name_(file), + line_(line), + severity_(severity), + log_to_chrome_(log_to_chrome) { + extra_ = GenerateExtra(err_ctx, err, NULL); +} + +DiagnosticLogMessage::DiagnosticLogMessage(const char* file, + int line, + LoggingSeverity severity, + bool log_to_chrome, + LogErrorContext err_ctx, + int err, + const char* module) + : file_name_(file), + line_(line), + severity_(severity), + log_to_chrome_(log_to_chrome) { + extra_ = GenerateExtra(err_ctx, err, module); +} + +DiagnosticLogMessage::~DiagnosticLogMessage() { + print_stream_ << extra_; + const std::string& str = print_stream_.str(); + if (log_to_chrome_) + LOG_LAZY_STREAM_DIRECT(file_name_, line_, severity_) << str; + if (g_logging_delegate_function && severity_ <= LS_INFO) { + g_logging_delegate_function(str); + } +} + +// static +void LogMessage::LogToDebug(int min_sev) { + logging::SetMinLogLevel(min_sev); +} + +// Note: this function is a copy from the overriden libjingle implementation. +void LogMultiline(LoggingSeverity level, const char* label, bool input, + const void* data, size_t len, bool hex_mode, + LogMultilineState* state) { + if (!LOG_CHECK_LEVEL_V(level)) + return; + + const char * direction = (input ? " << " : " >> "); + + // NULL data means to flush our count of unprintable characters. + if (!data) { + if (state && state->unprintable_count_[input]) { + LOG_V(level) << label << direction << "## " + << state->unprintable_count_[input] + << " consecutive unprintable ##"; + state->unprintable_count_[input] = 0; + } + return; + } + + // The ctype classification functions want unsigned chars. + const unsigned char* udata = static_cast(data); + + if (hex_mode) { + const size_t LINE_SIZE = 24; + char hex_line[LINE_SIZE * 9 / 4 + 2], asc_line[LINE_SIZE + 1]; + while (len > 0) { + memset(asc_line, ' ', sizeof(asc_line)); + memset(hex_line, ' ', sizeof(hex_line)); + size_t line_len = _min(len, LINE_SIZE); + for (size_t i = 0; i < line_len; ++i) { + unsigned char ch = udata[i]; + asc_line[i] = isprint(ch) ? ch : '.'; + hex_line[i*2 + i/4] = hex_encode(ch >> 4); + hex_line[i*2 + i/4 + 1] = hex_encode(ch & 0xf); + } + asc_line[sizeof(asc_line)-1] = 0; + hex_line[sizeof(hex_line)-1] = 0; + LOG_V(level) << label << direction + << asc_line << " " << hex_line << " "; + udata += line_len; + len -= line_len; + } + return; + } + + size_t consecutive_unprintable = state ? state->unprintable_count_[input] : 0; + + const unsigned char* end = udata + len; + while (udata < end) { + const unsigned char* line = udata; + const unsigned char* end_of_line = strchrn(udata, + end - udata, + '\n'); + if (!end_of_line) { + udata = end_of_line = end; + } else { + udata = end_of_line + 1; + } + + bool is_printable = true; + + // If we are in unprintable mode, we need to see a line of at least + // kMinPrintableLine characters before we'll switch back. + const ptrdiff_t kMinPrintableLine = 4; + if (consecutive_unprintable && ((end_of_line - line) < kMinPrintableLine)) { + is_printable = false; + } else { + // Determine if the line contains only whitespace and printable + // characters. + bool is_entirely_whitespace = true; + for (const unsigned char* pos = line; pos < end_of_line; ++pos) { + if (isspace(*pos)) + continue; + is_entirely_whitespace = false; + if (!isprint(*pos)) { + is_printable = false; + break; + } + } + // Treat an empty line following unprintable data as unprintable. + if (consecutive_unprintable && is_entirely_whitespace) { + is_printable = false; + } + } + if (!is_printable) { + consecutive_unprintable += (udata - line); + continue; + } + // Print out the current line, but prefix with a count of prior unprintable + // characters. + if (consecutive_unprintable) { + LOG_V(level) << label << direction << "## " << consecutive_unprintable + << " consecutive unprintable ##"; + consecutive_unprintable = 0; + } + // Strip off trailing whitespace. + while ((end_of_line > line) && isspace(*(end_of_line-1))) { + --end_of_line; + } + // Filter out any private data + std::string substr(reinterpret_cast(line), end_of_line - line); + std::string::size_type pos_private = substr.find("Email"); + if (pos_private == std::string::npos) { + pos_private = substr.find("Passwd"); + } + if (pos_private == std::string::npos) { + LOG_V(level) << label << direction << substr; + } else { + LOG_V(level) << label << direction << "## omitted for privacy ##"; + } + } + + if (state) { + state->unprintable_count_[input] = consecutive_unprintable; + } +} + +void InitDiagnosticLoggingDelegateFunction( + void (*delegate)(const std::string&)) { +#ifndef NDEBUG + // Ensure that this function is always called from the same thread. + base::subtle::NoBarrier_CompareAndSwap(&g_init_logging_delegate_thread_id, 0, + static_cast(base::PlatformThread::CurrentId())); + DCHECK_EQ( + g_init_logging_delegate_thread_id, + static_cast(base::PlatformThread::CurrentId())); +#endif + CHECK(delegate); + // This function may be called with the same argument several times if the + // page is reloaded or there are several PeerConnections on one page with + // logging enabled. This is OK, we simply don't have to do anything. + if (delegate == g_logging_delegate_function) + return; + CHECK(!g_logging_delegate_function); +#ifdef NDEBUG + IPAddress::set_strip_sensitive(true); +#endif + g_logging_delegate_function = delegate; + + if (g_extra_logging_init_function) + g_extra_logging_init_function(delegate); +} + +void SetExtraLoggingInit( + void (*function)(void (*delegate)(const std::string&))) { + CHECK(function); + CHECK(!g_extra_logging_init_function); + g_extra_logging_init_function = function; +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/logging.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/logging.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/logging.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/logging.h 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,226 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file overrides the logging macros in libjingle (webrtc/base/logging.h). +// Instead of using libjingle's logging implementation, the libjingle macros are +// mapped to the corresponding base/logging.h macro (chromium's VLOG). +// If this file is included outside of libjingle (e.g. in wrapper code) it +// should be included after base/logging.h (if any) or compiler error or +// unexpected behavior may occur (macros that have the same name in libjingle as +// in chromium will use the libjingle definition if this file is included +// first). + +// Setting the LoggingSeverity (and lower) that should be written to file should +// be done via command line by specifying the flags: +// --vmodule or --v please see base/logging.h for details on how to use them. +// Specifying what file to write to is done using InitLogging also in +// base/logging.h. + +// The macros and classes declared in here are not described as they are +// NOT TO BE USED outside of libjingle. + +#ifndef THIRD_PARTY_LIBJINGLE_OVERRIDES_WEBRTC_BASE_LOGGING_H_ +#define THIRD_PARTY_LIBJINGLE_OVERRIDES_WEBRTC_BASE_LOGGING_H_ + +#include +#include + +#include "base/logging.h" +#include "third_party/webrtc/base/scoped_ref_ptr.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// ConstantLabel can be used to easily generate string names from constant +// values. This can be useful for logging descriptive names of error messages. +// Usage: +// const ConstantLabel LIBRARY_ERRORS[] = { +// KLABEL(SOME_ERROR), +// KLABEL(SOME_OTHER_ERROR), +// ... +// LASTLABEL +// } +// +// int err = LibraryFunc(); +// LOG(LS_ERROR) << "LibraryFunc returned: " +// << ErrorName(err, LIBRARY_ERRORS); + +struct ConstantLabel { + int value; + const char* label; +}; +#define KLABEL(x) { x, #x } +#define LASTLABEL { 0, 0 } + +const char* FindLabel(int value, const ConstantLabel entries[]); +std::string ErrorName(int err, const ConstantLabel* err_table); + +////////////////////////////////////////////////////////////////////// +// Note that the non-standard LoggingSeverity aliases exist because they are +// still in broad use. The meanings of the levels are: +// LS_SENSITIVE: Information which should only be logged with the consent +// of the user, due to privacy concerns. +// LS_VERBOSE: This level is for data which we do not want to appear in the +// normal debug log, but should appear in diagnostic logs. +// LS_INFO: Chatty level used in debugging for all sorts of things, the default +// in debug builds. +// LS_WARNING: Something that may warrant investigation. +// LS_ERROR: Something that should not have occurred. +// Note that LoggingSeverity is mapped over to chromiums verbosity levels where +// anything lower than or equal to the current verbosity level is written to +// file which is the opposite of logging severity in libjingle where higher +// severity numbers than or equal to the current severity level are written to +// file. Also, note that the values are explicitly defined here for convenience +// since the command line flag must be set using numerical values. +enum LoggingSeverity { LS_ERROR = 1, + LS_WARNING = 2, + LS_INFO = 3, + LS_VERBOSE = 4, + LS_SENSITIVE = 5, + INFO = LS_INFO, + WARNING = LS_WARNING, + LERROR = LS_ERROR }; + +// LogErrorContext assists in interpreting the meaning of an error value. +enum LogErrorContext { + ERRCTX_NONE, + ERRCTX_ERRNO, // System-local errno + ERRCTX_HRESULT, // Windows HRESULT + ERRCTX_OSSTATUS, // MacOS OSStatus + + // Abbreviations for LOG_E macro + ERRCTX_EN = ERRCTX_ERRNO, // LOG_E(sev, EN, x) + ERRCTX_HR = ERRCTX_HRESULT, // LOG_E(sev, HR, x) + ERRCTX_OS = ERRCTX_OSSTATUS, // LOG_E(sev, OS, x) +}; + +// Class that writes a log message to the logging delegate ("WebRTC logging +// stream" in Chrome) and to Chrome's logging stream. +class DiagnosticLogMessage { + public: + DiagnosticLogMessage(const char* file, int line, LoggingSeverity severity, + bool log_to_chrome, LogErrorContext err_ctx, int err); + DiagnosticLogMessage(const char* file, int line, LoggingSeverity severity, + bool log_to_chrome, LogErrorContext err_ctx, int err, + const char* module); + ~DiagnosticLogMessage(); + + void CreateTimestamp(); + + std::ostream& stream() { return print_stream_; } + + private: + const char* file_name_; + const int line_; + const LoggingSeverity severity_; + const bool log_to_chrome_; + + std::string extra_; + + std::ostringstream print_stream_; +}; + +// This class is used to explicitly ignore values in the conditional +// logging macros. This avoids compiler warnings like "value computed +// is not used" and "statement has no effect". +class LogMessageVoidify { + public: + LogMessageVoidify() { } + // This has to be an operator with a precedence lower than << but + // higher than ?: + void operator&(std::ostream&) { } +}; + +////////////////////////////////////////////////////////////////////// +// Logging Helpers +////////////////////////////////////////////////////////////////////// + +class LogMultilineState { + public: + size_t unprintable_count_[2]; + LogMultilineState() { + unprintable_count_[0] = unprintable_count_[1] = 0; + } +}; + +class LogMessage { + public: + static void LogToDebug(int min_sev); +}; + +// When possible, pass optional state variable to track various data across +// multiple calls to LogMultiline. Otherwise, pass NULL. +void LogMultiline(LoggingSeverity level, const char* label, bool input, + const void* data, size_t len, bool hex_mode, + LogMultilineState* state); + +// TODO(grunell): Change name to InitDiagnosticLoggingDelegate or +// InitDiagnosticLogging. Change also in init_webrtc.h/cc. +// TODO(grunell): typedef the delegate function. +void InitDiagnosticLoggingDelegateFunction( + void (*delegate)(const std::string&)); + +void SetExtraLoggingInit( + void (*function)(void (*delegate)(const std::string&))); +} // namespace rtc + +////////////////////////////////////////////////////////////////////// +// Libjingle macros which are mapped over to their VLOG equivalent in +// base/logging.h +////////////////////////////////////////////////////////////////////// + +#if defined(LOGGING_INSIDE_WEBRTC) + +#define DIAGNOSTIC_LOG(sev, ctx, err, ...) \ + rtc::DiagnosticLogMessage( \ + __FILE__, __LINE__, sev, VLOG_IS_ON(sev), \ + rtc::ERRCTX_ ## ctx, err, ##__VA_ARGS__).stream() + +#define LOG_CHECK_LEVEL(sev) VLOG_IS_ON(rtc::sev) +#define LOG_CHECK_LEVEL_V(sev) VLOG_IS_ON(sev) + +#define LOG_V(sev) DIAGNOSTIC_LOG(sev, NONE, 0) +#undef LOG +#define LOG(sev) DIAGNOSTIC_LOG(rtc::sev, NONE, 0) + +// The _F version prefixes the message with the current function name. +#if defined(__GNUC__) && defined(_DEBUG) +#define LOG_F(sev) LOG(sev) << __PRETTY_FUNCTION__ << ": " +#else +#define LOG_F(sev) LOG(sev) << __FUNCTION__ << ": " +#endif + +#define LOG_E(sev, ctx, err, ...) \ + DIAGNOSTIC_LOG(rtc::sev, ctx, err, ##__VA_ARGS__) + +#undef LOG_ERRNO_EX +#define LOG_ERRNO_EX(sev, err) LOG_E(sev, ERRNO, err) +#undef LOG_ERRNO +#define LOG_ERRNO(sev) LOG_ERRNO_EX(sev, errno) + +#if defined(WEBRTC_WIN) +#define LOG_GLE_EX(sev, err) LOG_E(sev, HRESULT, err) +#define LOG_GLE(sev) LOG_GLE_EX(sev, GetLastError()) +#define LOG_GLEM(sev, mod) LOG_E(sev, HRESULT, GetLastError(), mod) +#define LOG_ERR_EX(sev, err) LOG_GLE_EX(sev, err) +#define LOG_ERR(sev) LOG_GLE(sev) +#define LAST_SYSTEM_ERROR (::GetLastError()) +#else +#define LOG_ERR_EX(sev, err) LOG_ERRNO_EX(sev, err) +#define LOG_ERR(sev) LOG_ERRNO(sev) +#define LAST_SYSTEM_ERROR (errno) +#endif // OS_WIN + +#undef PLOG +#define PLOG(sev, err) LOG_ERR_EX(sev, err) + +#endif // LOGGING_INSIDE_WEBRTC + +#endif // THIRD_PARTY_LIBJINGLE_OVERRIDES_WEBRTC_BASE_LOGGING_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/win32socketinit.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/win32socketinit.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/win32socketinit.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/overrides/webrtc/base/win32socketinit.cc 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,28 @@ +/* + * Copyright 2006 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Redirect Libjingle's winsock initialization activity into Chromium's +// singleton object that managest precisely that for the browser. + +#include "webrtc/base/win32socketinit.h" + +#include "net/base/winsock_init.h" + +#if !defined(WEBRTC_WIN) +#error "Only compile this on Windows" +#endif + +namespace rtc { + +void EnsureWinsockInit() { + net::EnsureWinsockInit(); +} + +} // namespace rtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/OWNERS 2015-02-03 14:33:32.000000000 +0000 @@ -0,0 +1,8 @@ +per-file *.isolate=kjellander@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/PRESUBMIT.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/PRESUBMIT.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/PRESUBMIT.py 2015-01-25 22:24:30.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/PRESUBMIT.py 2015-02-03 14:33:32.000000000 +0000 @@ -8,13 +8,13 @@ def _LicenseHeader(input_api): """Returns the license header regexp.""" - # Accept any year number from 2011 to the current year + # Accept any year number from 2003 to the current year current_year = int(input_api.time.strftime('%Y')) - allowed_years = (str(s) for s in reversed(xrange(2011, current_year + 1))) + allowed_years = (str(s) for s in reversed(xrange(2003, current_year + 1))) years_re = '(' + '|'.join(allowed_years) + ')' license_header = ( - r'.*? Copyright \(c\) %(year)s The WebRTC project authors\. ' - r'All Rights Reserved\.\n' + r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. ' + r'All [Rr]ights [Rr]eserved\.\n' r'.*?\n' r'.*? Use of this source code is governed by a BSD-style license\n' r'.*? that can be found in the LICENSE file in the root of the source\n' diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/rtc_unittests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/rtc_unittests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/rtc_unittests.isolate 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/rtc_unittests.isolate 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,21 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. +{ + 'conditions': [ + ['OS=="linux" or OS=="mac" or OS=="win"', { + 'variables': { + 'command': [ + '<(PRODUCT_DIR)/rtc_unittests<(EXECUTABLE_SUFFIX)', + ], + 'files': [ + '<(PRODUCT_DIR)/rtc_unittests<(EXECUTABLE_SUFFIX)', + ], + }, + }], + ], +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/supplement.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/supplement.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/supplement.gypi 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/supplement.gypi 2015-02-03 14:33:37.000000000 +0000 @@ -1,5 +1,26 @@ { 'variables': { + 'variables': { + 'webrtc_root%': '<(DEPTH)/webrtc', + }, + 'webrtc_root%': '<(webrtc_root)', 'build_with_chromium': 0, - } + }, + 'target_defaults': { + 'target_conditions': [ + ['_target_name=="sanitizer_options"', { + 'conditions': [ + ['tsan==1', { + # Replace Chromium's TSan v2 suppressions with our own for WebRTC. + 'sources/': [ + ['exclude', 'tsan_suppressions.cc'], + ], + 'sources': [ + '<(webrtc_root)/build/tsan_suppressions_webrtc.cc', + ], + }], + ], + }], + ], + }, } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/BUILD.gn 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,271 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("//build/config/android/config.gni") +import("../build/webrtc.gni") + +config("system_wrappers_inherited_config") { + include_dirs = [ + "interface", + ] +} + +static_library("system_wrappers") { + sources = [ + "interface/aligned_array.h", + "interface/aligned_malloc.h", + "interface/atomic32.h", + "interface/clock.h", + "interface/compile_assert.h", + "interface/condition_variable_wrapper.h", + "interface/cpu_info.h", + "interface/cpu_features_wrapper.h", + "interface/critical_section_wrapper.h", + "interface/data_log.h", + "interface/data_log_c.h", + "interface/data_log_impl.h", + "interface/event_tracer.h", + "interface/event_wrapper.h", + "interface/field_trial.h", + "interface/file_wrapper.h", + "interface/fix_interlocked_exchange_pointer_win.h", + "interface/logging.h", + "interface/metrics.h", + "interface/ref_count.h", + "interface/rtp_to_ntp.h", + "interface/rw_lock_wrapper.h", + "interface/scoped_ptr.h", + "interface/scoped_refptr.h", + "interface/scoped_vector.h", + "interface/sleep.h", + "interface/sort.h", + "interface/static_instance.h", + "interface/stl_util.h", + "interface/stringize_macros.h", + "interface/thread_wrapper.h", + "interface/tick_util.h", + "interface/timestamp_extrapolator.h", + "interface/trace.h", + "interface/trace_event.h", + "interface/utf_util_win.h", + "source/aligned_malloc.cc", + "source/atomic32_mac.cc", + "source/atomic32_win.cc", + "source/clock.cc", + "source/condition_variable.cc", + "source/condition_variable_posix.cc", + "source/condition_variable_posix.h", + "source/condition_variable_event_win.cc", + "source/condition_variable_event_win.h", + "source/condition_variable_native_win.cc", + "source/condition_variable_native_win.h", + "source/cpu_info.cc", + "source/cpu_features.cc", + "source/critical_section.cc", + "source/critical_section_posix.cc", + "source/critical_section_posix.h", + "source/critical_section_win.cc", + "source/critical_section_win.h", + "source/data_log_c.cc", + "source/event.cc", + "source/event_posix.cc", + "source/event_posix.h", + "source/event_tracer.cc", + "source/event_win.cc", + "source/event_win.h", + "source/file_impl.cc", + "source/file_impl.h", + "source/logging.cc", + "source/rtp_to_ntp.cc", + "source/rw_lock.cc", + "source/rw_lock_generic.cc", + "source/rw_lock_generic.h", + "source/rw_lock_posix.cc", + "source/rw_lock_posix.h", + "source/rw_lock_win.cc", + "source/rw_lock_win.h", + "source/set_thread_name_win.h", + "source/sleep.cc", + "source/sort.cc", + "source/tick_util.cc", + "source/thread.cc", + "source/thread_posix.cc", + "source/thread_posix.h", + "source/thread_win.cc", + "source/thread_win.h", + "source/timestamp_extrapolator.cc", + "source/trace_impl.cc", + "source/trace_impl.h", + "source/trace_posix.cc", + "source/trace_posix.h", + "source/trace_win.cc", + "source/trace_win.h", + ] + + configs += [ "..:common_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + public_configs = [ + "..:common_inherited_config", + ":system_wrappers_inherited_config", + ] + + if (rtc_enable_data_logging) { + sources += [ "source/data_log.cc" ] + } else { + sources += [ "source/data_log_no_op.cc" ] + } + + defines = [] + libs = [] + deps = [] + + if (is_android) { + sources += [ + "interface/logcat_trace_context.h", + "source/logcat_trace_context.cc", + ] + + defines += [ + "WEBRTC_THREAD_RR", + # TODO(leozwang): Investigate CLOCK_REALTIME and CLOCK_MONOTONIC + # support on Android. Keep WEBRTC_CLOCK_TYPE_REALTIME for now, + # remove it after I verify that CLOCK_MONOTONIC is fully functional + # with condition and event functions in system_wrappers. + "WEBRTC_CLOCK_TYPE_REALTIME", + ] + + deps += [ ":cpu_features_android" ] + + libs += [ "log" ] + } + + if (is_linux) { + defines += [ + "WEBRTC_THREAD_RR", + # TODO(andrew): can we select this automatically? + # Define this if the Linux system does not support CLOCK_MONOTONIC. + #"WEBRTC_CLOCK_TYPE_REALTIME", + ] + + libs += [ "rt" ] + } + + if (!is_mac && !is_ios) { + sources += [ + "source/atomic32_posix.cc", + ] + } + + if (is_ios || is_mac) { + defines += [ + "WEBRTC_THREAD_RR", + "WEBRTC_CLOCK_TYPE_REALTIME", + ] + } + + if (is_ios) { + sources += [ + "source/atomic32_mac.cc", + ] + } + + if (is_win) { + libs += [ "winmm.lib" ] + + cflags = [ + "/wd4267", # size_t to int truncation. + "/wd4334", # Ignore warning on shift operator promotion. + ] + } + + include_dirs = [ + "source/spreadsortlib", + ] + + deps += [ + "../base:rtc_base_approved", + ] +} + +source_set("field_trial_default") { + sources = [ + "source/field_trial_default.cc", + ] + + configs += [ "..:common_config" ] + public_configs = [ "..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ + ":system_wrappers", + ] +} + +source_set("metrics_default") { + sources = [ + "source/metrics_default.cc", + ] + + configs += [ "..:common_config" ] + public_configs = [ "..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ + ":system_wrappers", + ] +} + +source_set("system_wrappers_default") { + + configs += [ "..:common_config" ] + public_configs = [ "..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ + ":field_trial_default", + ":metrics_default", + ] +} + +if (is_android) { + source_set("cpu_features_android") { + sources = [ + "source/cpu_features_android.c", + ] + + configs += [ "..:common_config" ] + public_configs = [ "..:common_inherited_config" ] + + if (is_android_webview_build) { + libs += [ "cpufeatures.a" ] + } else { + deps = [ "//third_party/android_tools:cpu_features" ] + } + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/aligned_array.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/aligned_array.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/aligned_array.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/aligned_array.h 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_ALIGNED_ARRAY_ +#define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_ALIGNED_ARRAY_ + +#include "webrtc/base/checks.h" +#include "webrtc/system_wrappers/interface/aligned_malloc.h" + +namespace webrtc { + +// Wrapper class for aligned arrays. Every row (and the first dimension) are +// aligned to the given byte alignment. +template class AlignedArray { + public: + AlignedArray(int rows, int cols, int alignment) + : rows_(rows), + cols_(cols), + alignment_(alignment) { + CHECK_GT(alignment_, 0); + head_row_ = static_cast(AlignedMalloc(rows_ * sizeof(*head_row_), + alignment_)); + for (int i = 0; i < rows_; ++i) { + head_row_[i] = static_cast(AlignedMalloc(cols_ * sizeof(**head_row_), + alignment_)); + } + } + + ~AlignedArray() { + for (int i = 0; i < rows_; ++i) { + AlignedFree(head_row_[i]); + } + AlignedFree(head_row_); + } + + T* const* Array() { + return head_row_; + } + + const T* const* Array() const { + return head_row_; + } + + T* Row(int row) { + CHECK_LE(row, rows_); + return head_row_[row]; + } + + const T* Row(int row) const { + CHECK_LE(row, rows_); + return head_row_[row]; + } + + T& At(int row, int col) { + CHECK_LE(col, cols_); + return Row(row)[col]; + } + + const T& At(int row, int col) const { + CHECK_LE(col, cols_); + return Row(row)[col]; + } + + int rows() const { + return rows_; + } + + int cols() const { + return cols_; + } + + private: + int rows_; + int cols_; + int alignment_; + T** head_row_; +}; + +} // namespace webrtc + +#endif // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_ALIGNED_ARRAY_ + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/aligned_malloc.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/aligned_malloc.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/aligned_malloc.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/aligned_malloc.h 2015-02-03 14:33:37.000000000 +0000 @@ -19,8 +19,6 @@ #include -#include "webrtc/system_wrappers/interface/scoped_ptr.h" - namespace webrtc { // Returns a pointer to the first boundry of |alignment| bytes following the @@ -48,10 +46,12 @@ return reinterpret_cast(AlignedMalloc(size, alignment)); } -// Scoped pointer to AlignedMalloc-memory. -template -struct Allocator { - typedef scoped_ptr_malloc scoped_ptr_aligned; +// Deleter for use with scoped_ptr. E.g., use as +// scoped_ptr foo; +struct AlignedFreeDeleter { + inline void operator()(void* ptr) const { + AlignedFree(ptr); + } }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/atomic32.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/atomic32.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/atomic32.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/atomic32.h 2015-02-03 14:33:37.000000000 +0000 @@ -17,8 +17,8 @@ #include +#include "webrtc/base/constructormagic.h" #include "webrtc/common_types.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/clock.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/clock.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/clock.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/clock.h 2015-02-03 14:33:37.000000000 +0000 @@ -11,6 +11,8 @@ #ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_CLOCK_H_ #define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_CLOCK_H_ +#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/typedefs.h" namespace webrtc { @@ -28,17 +30,17 @@ // Return a timestamp in milliseconds relative to some arbitrary source; the // source is fixed for this clock. - virtual int64_t TimeInMilliseconds() = 0; + virtual int64_t TimeInMilliseconds() const = 0; // Return a timestamp in microseconds relative to some arbitrary source; the // source is fixed for this clock. - virtual int64_t TimeInMicroseconds() = 0; + virtual int64_t TimeInMicroseconds() const = 0; // Retrieve an NTP absolute timestamp in seconds and fractions of a second. - virtual void CurrentNtp(uint32_t& seconds, uint32_t& fractions) = 0; + virtual void CurrentNtp(uint32_t& seconds, uint32_t& fractions) const = 0; // Retrieve an NTP absolute timestamp in milliseconds. - virtual int64_t CurrentNtpInMilliseconds() = 0; + virtual int64_t CurrentNtpInMilliseconds() const = 0; // Converts an NTP timestamp to a millisecond timestamp. static int64_t NtpToMs(uint32_t seconds, uint32_t fractions); @@ -51,21 +53,22 @@ public: explicit SimulatedClock(int64_t initial_time_us); - virtual ~SimulatedClock() {} + virtual ~SimulatedClock(); // Return a timestamp in milliseconds relative to some arbitrary source; the // source is fixed for this clock. - virtual int64_t TimeInMilliseconds() OVERRIDE; + virtual int64_t TimeInMilliseconds() const OVERRIDE; // Return a timestamp in microseconds relative to some arbitrary source; the // source is fixed for this clock. - virtual int64_t TimeInMicroseconds() OVERRIDE; + virtual int64_t TimeInMicroseconds() const OVERRIDE; // Retrieve an NTP absolute timestamp in milliseconds. - virtual void CurrentNtp(uint32_t& seconds, uint32_t& fractions) OVERRIDE; + virtual void CurrentNtp(uint32_t& seconds, + uint32_t& fractions) const OVERRIDE; // Converts an NTP timestamp to a millisecond timestamp. - virtual int64_t CurrentNtpInMilliseconds() OVERRIDE; + virtual int64_t CurrentNtpInMilliseconds() const OVERRIDE; // Advance the simulated clock with a given number of milliseconds or // microseconds. @@ -74,6 +77,7 @@ private: int64_t time_us_; + scoped_ptr lock_; }; }; // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/compile_assert.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/compile_assert.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/compile_assert.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/compile_assert.h 2015-02-03 14:33:37.000000000 +0000 @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -// Borrowed from Chromium's src/base/basictypes.h. +// Borrowed from Chromium's src/base/macros.h. #ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_COMPILE_ASSERT_H_ #define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_COMPILE_ASSERT_H_ @@ -31,13 +31,20 @@ // TODO(ajm): Hack to avoid multiple definitions until the base/ of webrtc and // libjingle are merged. #if !defined(COMPILE_ASSERT) +#if __cplusplus >= 201103L +// Under C++11, just use static_assert. +#define COMPILE_ASSERT(expr, msg) static_assert(expr, #msg) + +#else template struct CompileAssert { }; #define COMPILE_ASSERT(expr, msg) \ typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] -#endif // COMPILE_ASSERT + +#endif // __cplusplus >= 201103L +#endif // !defined(COMPILE_ASSERT) // Implementation details of COMPILE_ASSERT: // diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/constructor_magic.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/constructor_magic.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/constructor_magic.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/constructor_magic.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * WebRtc - * Copy from third_party/libjingle/source/talk/base/constructormagic.h - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_CONSTRUCTOR_MAGIC_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_CONSTRUCTOR_MAGIC_H_ - -#ifndef DISALLOW_ASSIGN -#define DISALLOW_ASSIGN(TypeName) \ - void operator=(const TypeName&) -#endif - -#ifndef DISALLOW_COPY_AND_ASSIGN -// A macro to disallow the evil copy constructor and operator= functions -// This should be used in the private: declarations for a class -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - DISALLOW_ASSIGN(TypeName) -#endif - -#ifndef DISALLOW_EVIL_CONSTRUCTORS -// Alternative, less-accurate legacy name. -#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \ - DISALLOW_COPY_AND_ASSIGN(TypeName) -#endif - -#ifndef DISALLOW_IMPLICIT_CONSTRUCTORS -// A macro to disallow all the implicit constructors, namely the -// default constructor, copy constructor and operator= functions. -// -// This should be used in the private: declarations for a class -// that wants to prevent anyone from instantiating it. This is -// especially useful for classes containing only static methods. -#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ - TypeName(); \ - DISALLOW_EVIL_CONSTRUCTORS(TypeName) -#endif - -#endif // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_CONSTRUCTOR_MAGIC_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/critical_section_wrapper.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/critical_section_wrapper.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/critical_section_wrapper.h 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/critical_section_wrapper.h 2015-02-03 14:33:37.000000000 +0000 @@ -14,8 +14,8 @@ // If the critical section is heavily contended it may be beneficial to use // read/write locks instead. +#include "webrtc/base/thread_annotations.h" #include "webrtc/common_types.h" -#include "webrtc/system_wrappers/interface/thread_annotations.h" namespace webrtc { class LOCKABLE CriticalSectionWrapper { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/field_trial.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/field_trial.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/field_trial.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/field_trial.h 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,70 @@ +// +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// + +#ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_FIELD_TRIAL_H_ +#define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_FIELD_TRIAL_H_ + +#include + +#include "webrtc/common_types.h" + +// Field trials allow webrtc clients (such as Chrome) to turn on feature code +// in binaries out in the field and gather information with that. +// +// WebRTC clients MUST provide an implementation of: +// +// std::string webrtc::field_trial::FindFullName(const std::string& trial). +// +// Or link with a default one provided in: +// +// system_wrappers/source/system_wrappers.gyp:field_trial_default +// +// +// They are designed to wire up directly to chrome field trials and to speed up +// developers by reducing the need to wire APIs to control whether a feature is +// on/off. E.g. to experiment with a new method that could lead to a different +// trade-off between CPU/bandwidth: +// +// 1 - Develop the feature with default behaviour off: +// +// if (FieldTrial::FindFullName("WebRTCExperimenMethod2") == "Enabled") +// method2(); +// else +// method1(); +// +// 2 - Once the changes are rolled to chrome, the new code path can be +// controlled as normal chrome field trials. +// +// 3 - Evaluate the new feature and clean the code paths. +// +// Notes: +// - NOT every feature is a candidate to be controlled by this mechanism as +// it may require negotation between involved parties (e.g. SDP). +// +// TODO(andresp): since chrome --force-fieldtrials does not marks the trial +// as active it does not gets propaged to renderer process. For now one +// needs to push a config with start_active:true or run a local finch +// server. +// +// TODO(andresp): find out how to get bots to run tests with trials enabled. + +namespace webrtc { +namespace field_trial { + +// Returns the group name chosen for the named trial, or the empty string +// if the trial does not exists. +// +// Note: To keep things tidy append all the trial names with WebRTC. +std::string FindFullName(const std::string& name); + +} // namespace field_trial +} // namespace webrtc + +#endif // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_FIELD_TRIAL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/metrics.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/metrics.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/metrics.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/metrics.h 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,130 @@ +// +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// + +#ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_METRICS_H_ +#define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_METRICS_H_ + +#include + +#include "webrtc/common_types.h" + +// Macros for allowing WebRTC clients (e.g. Chrome) to gather and aggregate +// statistics. +// +// Histogram for counters. +// RTC_HISTOGRAM_COUNTS(name, sample, min, max, bucket_count); +// +// Histogram for enumerators. +// The boundary should be above the max enumerator sample. +// RTC_HISTOGRAM_ENUMERATION(name, sample, boundary); +// +// +// The macros use the methods HistogramFactoryGetCounts, +// HistogramFactoryGetEnumeration and HistogramAdd. +// +// Therefore, WebRTC clients must either: +// +// - provide implementations of +// Histogram* webrtc::metrics::HistogramFactoryGetCounts( +// const std::string& name, int sample, int min, int max, +// int bucket_count); +// Histogram* webrtc::metrics::HistogramFactoryGetEnumeration( +// const std::string& name, int sample, int boundary); +// void webrtc::metrics::HistogramAdd( +// Histogram* histogram_pointer, const std::string& name, int sample); +// +// - or link with the default implementations (i.e. +// system_wrappers/source/system_wrappers.gyp:metrics_default). +// +// +// Example usage: +// +// RTC_HISTOGRAM_COUNTS("WebRTC.Video.NacksSent", nacks_sent, 1, 100000, 100); +// +// enum Types { +// kTypeX, +// kTypeY, +// kBoundary, +// }; +// +// RTC_HISTOGRAM_ENUMERATION("WebRTC.Types", kTypeX, kBoundary); + + +// Macros for adding samples to a named histogram. +// +// NOTE: this is a temporary solution. +// The aim is to mimic the behaviour in Chromium's src/base/metrics/histograms.h +// However as atomics are not supported in webrtc, this is for now a modified +// and temporary solution. Note that the histogram is constructed/found for +// each call. Therefore, for now only use this implementation for metrics +// that do not need to be updated frequently. +// TODO(asapersson): Change implementation when atomics are supported. +// Also consider changing string to const char* when switching to atomics. + +// Histogram for counters. +#define RTC_HISTOGRAM_COUNTS_100(name, sample) RTC_HISTOGRAM_COUNTS( \ + name, sample, 1, 100, 50) + +#define RTC_HISTOGRAM_COUNTS_1000(name, sample) RTC_HISTOGRAM_COUNTS( \ + name, sample, 1, 1000, 50) + +#define RTC_HISTOGRAM_COUNTS_10000(name, sample) RTC_HISTOGRAM_COUNTS( \ + name, sample, 1, 10000, 50) + +#define RTC_HISTOGRAM_COUNTS(name, sample, min, max, bucket_count) \ + RTC_HISTOGRAM_COMMON_BLOCK(name, sample, \ + webrtc::metrics::HistogramFactoryGetCounts( \ + name, min, max, bucket_count)) + +// Histogram for percentage. +#define RTC_HISTOGRAM_PERCENTAGE(name, sample) \ + RTC_HISTOGRAM_ENUMERATION(name, sample, 101) + +// Histogram for enumerators. +// |boundary| should be above the max enumerator sample. +#define RTC_HISTOGRAM_ENUMERATION(name, sample, boundary) \ + RTC_HISTOGRAM_COMMON_BLOCK(name, sample, \ + webrtc::metrics::HistogramFactoryGetEnumeration(name, boundary)) + +#define RTC_HISTOGRAM_COMMON_BLOCK(constant_name, sample, \ + factory_get_invocation) \ + do { \ + webrtc::metrics::Histogram* histogram_pointer = factory_get_invocation; \ + webrtc::metrics::HistogramAdd(histogram_pointer, constant_name, sample); \ + } while (0) + + +namespace webrtc { +namespace metrics { + +class Histogram; + +// Functions for getting pointer to histogram (constructs or finds the named +// histogram). + +// Get histogram for counters. +Histogram* HistogramFactoryGetCounts( + const std::string& name, int min, int max, int bucket_count); + +// Get histogram for enumerators. +// |boundary| should be above the max enumerator sample. +Histogram* HistogramFactoryGetEnumeration( + const std::string& name, int boundary); + +// Function for adding a |sample| to a histogram. +// |name| can be used to verify that it matches the histogram name. +void HistogramAdd( + Histogram* histogram_pointer, const std::string& name, int sample); + +} // namespace metrics +} // namespace webrtc + +#endif // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_METRICS_H_ + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/rtp_to_ntp.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/rtp_to_ntp.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/rtp_to_ntp.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/rtp_to_ntp.h 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SYSTEM_WRAPPERS_INTERFACE_RTP_TO_NTP_H_ +#define SYSTEM_WRAPPERS_INTERFACE_RTP_TO_NTP_H_ + +#include + +#include "webrtc/typedefs.h" + +namespace webrtc { + +struct RtcpMeasurement { + RtcpMeasurement(); + RtcpMeasurement(uint32_t ntp_secs, uint32_t ntp_frac, uint32_t timestamp); + uint32_t ntp_secs; + uint32_t ntp_frac; + uint32_t rtp_timestamp; +}; + +typedef std::list RtcpList; + +// Updates |rtcp_list| with timestamps from the latest RTCP SR. +// |new_rtcp_sr| will be set to true if these are the timestamps which have +// never be added to |rtcp_list|. +bool UpdateRtcpList(uint32_t ntp_secs, + uint32_t ntp_frac, + uint32_t rtp_timestamp, + RtcpList* rtcp_list, + bool* new_rtcp_sr); + +// Converts an RTP timestamp to the NTP domain in milliseconds using two +// (RTP timestamp, NTP timestamp) pairs. +bool RtpToNtpMs(int64_t rtp_timestamp, const RtcpList& rtcp, + int64_t* timestamp_in_ms); + +// Returns 1 there has been a forward wrap around, 0 if there has been no wrap +// around and -1 if there has been a backwards wrap around (i.e. reordering). +int CheckForWrapArounds(uint32_t rtp_timestamp, uint32_t rtcp_rtp_timestamp); + +} // namespace webrtc + +#endif // SYSTEM_WRAPPERS_INTERFACE_RTP_TO_NTP_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/rw_lock_wrapper.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/rw_lock_wrapper.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/rw_lock_wrapper.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/rw_lock_wrapper.h 2015-02-03 14:33:37.000000000 +0000 @@ -11,7 +11,7 @@ #ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_RW_LOCK_WRAPPER_H_ #define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_RW_LOCK_WRAPPER_H_ -#include "webrtc/system_wrappers/interface/thread_annotations.h" +#include "webrtc/base/thread_annotations.h" // Note, Windows pre-Vista version of RW locks are not supported natively. For // these OSs regular critical sections have been used to approximate RW lock diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/scoped_ptr.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/scoped_ptr.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/scoped_ptr.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/scoped_ptr.h 2015-02-03 14:33:37.000000000 +0000 @@ -10,10 +10,10 @@ // Borrowed from Chromium's src/base/memory/scoped_ptr.h. -// Scopers help you manage ownership of a pointer, helping you easily manage the -// a pointer within a scope, and automatically destroying the pointer at the -// end of a scope. There are two main classes you will use, which correspond -// to the operators new/delete and new[]/delete[]. +// Scopers help you manage ownership of a pointer, helping you easily manage a +// pointer within a scope, and automatically destroying the pointer at the end +// of a scope. There are two main classes you will use, which correspond to the +// operators new/delete and new[]/delete[]. // // Example usage (scoped_ptr): // { @@ -66,7 +66,7 @@ // TakesOwnership(ptr.Pass()); // ptr no longer owns Foo("yay"). // scoped_ptr ptr2 = CreateFoo(); // ptr2 owns the return Foo. // scoped_ptr ptr3 = // ptr3 now owns what was in ptr2. -// PassThru(ptr2.Pass()); // ptr2 is correspondingly NULL. +// PassThru(ptr2.Pass()); // ptr2 is correspondingly nullptr. // } // // Notice that if you do not call Pass() when returning from PassThru(), or @@ -96,7 +96,7 @@ #define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_SCOPED_PTR_H_ // This is an implementation designed to match the anticipated future TR2 -// implementation of the scoped_ptr class and scoped_ptr_malloc (deprecated). +// implementation of the scoped_ptr class. #include #include @@ -104,8 +104,8 @@ #include // For std::swap(). +#include "webrtc/base/constructormagic.h" #include "webrtc/system_wrappers/interface/compile_assert.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/system_wrappers/interface/template_util.h" #include "webrtc/system_wrappers/source/move.h" #include "webrtc/typedefs.h" @@ -193,12 +193,23 @@ namespace internal { +template +struct ShouldAbortOnSelfReset { + template + static NoType Test(const typename U::AllowSelfReset*); + + template + static YesType Test(...); + + static const bool value = sizeof(Test(0)) == sizeof(YesType); +}; + // Minimal implementation of the core logic of scoped_ptr, suitable for // reuse in both scoped_ptr and its specializations. template class scoped_ptr_impl { public: - explicit scoped_ptr_impl(T* p) : data_(p) { } + explicit scoped_ptr_impl(T* p) : data_(p) {} // Initializer for deleters that have data parameters. scoped_ptr_impl(T* p, const D& d) : data_(p, d) {} @@ -223,7 +234,7 @@ } ~scoped_ptr_impl() { - if (data_.ptr != NULL) { + if (data_.ptr != nullptr) { // Not using get_deleter() saves one function call in non-optimized // builds. static_cast(data_)(data_.ptr); @@ -231,12 +242,12 @@ } void reset(T* p) { - // This is a self-reset, which is no longer allowed: http://crbug.com/162971 - if (p != NULL && p == data_.ptr) - abort(); + // This is a self-reset, which is no longer allowed for default deleters: + // https://crbug.com/162971 + assert(!ShouldAbortOnSelfReset::value || p == nullptr || p != data_.ptr); // Note that running data_.ptr = p can lead to undefined behavior if - // get_deleter()(get()) deletes this. In order to pevent this, reset() + // get_deleter()(get()) deletes this. In order to prevent this, reset() // should update the stored pointer before deleting its old value. // // However, changing reset() to use that behavior may cause current code to @@ -245,13 +256,13 @@ // then it will incorrectly dispatch calls to |p| rather than the original // value of |data_.ptr|. // - // During the transition period, set the stored pointer to NULL while + // During the transition period, set the stored pointer to nullptr while // deleting the object. Eventually, this safety check will be removed to - // prevent the scenario initially described from occuring and + // prevent the scenario initially described from occurring and // http://crbug.com/176091 can be closed. T* old = data_.ptr; - data_.ptr = NULL; - if (old != NULL) + data_.ptr = nullptr; + if (old != nullptr) static_cast(data_)(old); data_.ptr = p; } @@ -272,7 +283,7 @@ T* release() { T* old_ptr = data_.ptr; - data_.ptr = NULL; + data_.ptr = nullptr; return old_ptr; } @@ -300,8 +311,8 @@ // A scoped_ptr is like a T*, except that the destructor of scoped_ptr // automatically deletes the pointer it holds (if any). // That is, scoped_ptr owns the T object that it points to. -// Like a T*, a scoped_ptr may hold either NULL or a pointer to a T object. -// Also like T*, scoped_ptr is thread-compatible, and once you +// Like a T*, a scoped_ptr may hold either nullptr or a pointer to a T +// object. Also like T*, scoped_ptr is thread-compatible, and once you // dereference it, you get the thread safety guarantees of T. // // The size of scoped_ptr is small. On most compilers, when using the @@ -311,25 +322,33 @@ // // Current implementation targets having a strict subset of C++11's // unique_ptr<> features. Known deficiencies include not supporting move-only -// deleteres, function pointers as deleters, and deleters with reference +// deleters, function pointers as deleters, and deleters with reference // types. template > class scoped_ptr { - WEBRTC_MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue) + RTC_MOVE_ONLY_TYPE_WITH_MOVE_CONSTRUCTOR_FOR_CPP_03(scoped_ptr) + + // TODO(ajm): If we ever import RefCountedBase, this check needs to be + // enabled. + //COMPILE_ASSERT(webrtc::internal::IsNotRefCounted::value, + // T_is_refcounted_type_and_needs_scoped_refptr); public: // The element and deleter types. typedef T element_type; typedef D deleter_type; - // Constructor. Defaults to initializing with NULL. - scoped_ptr() : impl_(NULL) { } + // Constructor. Defaults to initializing with nullptr. + scoped_ptr() : impl_(nullptr) {} // Constructor. Takes ownership of p. - explicit scoped_ptr(element_type* p) : impl_(p) { } + explicit scoped_ptr(element_type* p) : impl_(p) {} // Constructor. Allows initialization of a stateful deleter. - scoped_ptr(element_type* p, const D& d) : impl_(p, d) { } + scoped_ptr(element_type* p, const D& d) : impl_(p, d) {} + + // Constructor. Allows construction from a nullptr. + scoped_ptr(decltype(nullptr)) : impl_(nullptr) {} // Constructor. Allows construction from a scoped_ptr rvalue for a // convertible type and deleter. @@ -342,13 +361,11 @@ // use of SFINAE. You only need to care about this if you modify the // implementation of scoped_ptr. template - scoped_ptr(scoped_ptr other) : impl_(&other.impl_) { + scoped_ptr(scoped_ptr&& other) + : impl_(&other.impl_) { COMPILE_ASSERT(!webrtc::is_array::value, U_cannot_be_an_array); } - // Constructor. Move constructor for C++03 move emulation of this type. - scoped_ptr(RValue rvalue) : impl_(&rvalue.object->impl_) { } - // operator=. Allows assignment from a scoped_ptr rvalue for a convertible // type and deleter. // @@ -360,24 +377,31 @@ // You only need to care about this if you modify the implementation of // scoped_ptr. template - scoped_ptr& operator=(scoped_ptr rhs) { + scoped_ptr& operator=(scoped_ptr&& rhs) { COMPILE_ASSERT(!webrtc::is_array::value, U_cannot_be_an_array); impl_.TakeState(&rhs.impl_); return *this; } + // operator=. Allows assignment from a nullptr. Deletes the currently owned + // object, if any. + scoped_ptr& operator=(decltype(nullptr)) { + reset(); + return *this; + } + // Reset. Deletes the currently owned object, if any. // Then takes ownership of a new object, if given. - void reset(element_type* p = NULL) { impl_.reset(p); } + void reset(element_type* p = nullptr) { impl_.reset(p); } // Accessors to get the owned object. // operator* and operator-> will assert() if there is no current object. element_type& operator*() const { - assert(impl_.get() != NULL); + assert(impl_.get() != nullptr); return *impl_.get(); } element_type* operator->() const { - assert(impl_.get() != NULL); + assert(impl_.get() != nullptr); return impl_.get(); } element_type* get() const { return impl_.get(); } @@ -398,7 +422,9 @@ scoped_ptr::*Testable; public: - operator Testable() const { return impl_.get() ? &scoped_ptr::impl_ : NULL; } + operator Testable() const { + return impl_.get() ? &scoped_ptr::impl_ : nullptr; + } // Comparison operators. // These return whether two scoped_ptr refer to the same object, not just to @@ -412,25 +438,13 @@ } // Release a pointer. - // The return value is the current pointer held by this object. - // If this object holds a NULL pointer, the return value is NULL. - // After this operation, this object will hold a NULL pointer, - // and will not own the object any more. + // The return value is the current pointer held by this object. If this object + // holds a nullptr, the return value is nullptr. After this operation, this + // object will hold a nullptr, and will not own the object any more. element_type* release() WARN_UNUSED_RESULT { return impl_.release(); } - // C++98 doesn't support functions templates with default parameters which - // makes it hard to write a PassAs() that understands converting the deleter - // while preserving simple calling semantics. - // - // Until there is a use case for PassAs() with custom deleters, just ignore - // the custom deleter. - template - scoped_ptr PassAs() { - return scoped_ptr(Pass()); - } - private: // Needed to reach into |impl_| in the constructor. template friend class scoped_ptr; @@ -449,15 +463,15 @@ template class scoped_ptr { - WEBRTC_MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue) + RTC_MOVE_ONLY_TYPE_WITH_MOVE_CONSTRUCTOR_FOR_CPP_03(scoped_ptr) public: // The element and deleter types. typedef T element_type; typedef D deleter_type; - // Constructor. Defaults to initializing with NULL. - scoped_ptr() : impl_(NULL) { } + // Constructor. Defaults to initializing with nullptr. + scoped_ptr() : impl_(nullptr) {} // Constructor. Stores the given array. Note that the argument's type // must exactly match T*. In particular: @@ -467,32 +481,39 @@ // T and the derived types had different sizes access would be // incorrectly calculated). Deletion is also always undefined // (C++98 [expr.delete]p3). If you're doing this, fix your code. - // - it cannot be NULL, because NULL is an integral expression, not a - // pointer to T. Use the no-argument version instead of explicitly - // passing NULL. // - it cannot be const-qualified differently from T per unique_ptr spec // (http://cplusplus.github.com/LWG/lwg-active.html#2118). Users wanting // to work around this may use implicit_cast(). // However, because of the first bullet in this comment, users MUST // NOT use implicit_cast() to upcast the static type of the array. - explicit scoped_ptr(element_type* array) : impl_(array) { } + explicit scoped_ptr(element_type* array) : impl_(array) {} - // Constructor. Move constructor for C++03 move emulation of this type. - scoped_ptr(RValue rvalue) : impl_(&rvalue.object->impl_) { } + // Constructor. Allows construction from a nullptr. + scoped_ptr(decltype(nullptr)) : impl_(nullptr) {} - // operator=. Move operator= for C++03 move emulation of this type. - scoped_ptr& operator=(RValue rhs) { - impl_.TakeState(&rhs.object->impl_); + // Constructor. Allows construction from a scoped_ptr rvalue. + scoped_ptr(scoped_ptr&& other) : impl_(&other.impl_) {} + + // operator=. Allows assignment from a scoped_ptr rvalue. + scoped_ptr& operator=(scoped_ptr&& rhs) { + impl_.TakeState(&rhs.impl_); + return *this; + } + + // operator=. Allows assignment from a nullptr. Deletes the currently owned + // array, if any. + scoped_ptr& operator=(decltype(nullptr)) { + reset(); return *this; } // Reset. Deletes the currently owned array, if any. // Then takes ownership of a new object, if given. - void reset(element_type* array = NULL) { impl_.reset(array); } + void reset(element_type* array = nullptr) { impl_.reset(array); } // Accessors to get the owned array. element_type& operator[](size_t i) const { - assert(impl_.get() != NULL); + assert(impl_.get() != nullptr); return impl_.get()[i]; } element_type* get() const { return impl_.get(); } @@ -508,7 +529,9 @@ scoped_ptr::*Testable; public: - operator Testable() const { return impl_.get() ? &scoped_ptr::impl_ : NULL; } + operator Testable() const { + return impl_.get() ? &scoped_ptr::impl_ : nullptr; + } // Comparison operators. // These return whether two scoped_ptr refer to the same object, not just to @@ -522,10 +545,9 @@ } // Release a pointer. - // The return value is the current pointer held by this object. - // If this object holds a NULL pointer, the return value is NULL. - // After this operation, this object will hold a NULL pointer, - // and will not own the object any more. + // The return value is the current pointer held by this object. If this object + // holds a nullptr, the return value is nullptr. After this operation, this + // object will hold a nullptr, and will not own the object any more. element_type* release() WARN_UNUSED_RESULT { return impl_.release(); } @@ -560,7 +582,6 @@ } // namespace webrtc -// Free functions template void swap(webrtc::scoped_ptr& p1, webrtc::scoped_ptr& p2) { p1.swap(p2); @@ -576,155 +597,14 @@ return p1 != p2.get(); } -namespace webrtc { - -// DEPRECATED: Use scoped_ptr instead. -// TODO(ajm): Remove scoped_array. -// -// scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to -// is guaranteed, either on destruction of the scoped_array or via an explicit -// reset(). Use shared_array or std::vector if your needs are more complex. - -template -class scoped_array { - private: - - T* ptr; - - scoped_array(scoped_array const &); - scoped_array & operator=(scoped_array const &); - - public: - - typedef T element_type; - - explicit scoped_array(T* p = NULL) : ptr(p) {} - - ~scoped_array() { - typedef char type_must_be_complete[sizeof(T)]; - delete[] ptr; - } - - void reset(T* p = NULL) { - typedef char type_must_be_complete[sizeof(T)]; - - if (ptr != p) { - T* arr = ptr; - ptr = p; - // Delete last, in case arr destructor indirectly results in ~scoped_array - delete [] arr; - } - } - - T& operator[](ptrdiff_t i) const { - assert(ptr != NULL); - assert(i >= 0); - return ptr[i]; - } - - T* get() const { - return ptr; - } - - void swap(scoped_array & b) { - T* tmp = b.ptr; - b.ptr = ptr; - ptr = tmp; - } - - T* release() { - T* tmp = ptr; - ptr = NULL; - return tmp; - } - - T** accept() { - if (ptr) { - delete [] ptr; - ptr = NULL; - } - return &ptr; - } -}; - -template inline -void swap(scoped_array& a, scoped_array& b) { - a.swap(b); -} - -// DEPRECATED: Use scoped_ptr instead. -// TODO(ajm): Remove scoped_ptr_malloc. -// -// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a -// second template argument, the function used to free the object. - -template class scoped_ptr_malloc { - private: - - T* ptr; - - scoped_ptr_malloc(scoped_ptr_malloc const &); - scoped_ptr_malloc & operator=(scoped_ptr_malloc const &); - - public: - - typedef T element_type; - - explicit scoped_ptr_malloc(T* p = 0): ptr(p) {} - - ~scoped_ptr_malloc() { - FF(static_cast(ptr)); - } - - void reset(T* p = 0) { - if (ptr != p) { - FF(static_cast(ptr)); - ptr = p; - } - } - - T& operator*() const { - assert(ptr != 0); - return *ptr; - } - - T* operator->() const { - assert(ptr != 0); - return ptr; - } - - T* get() const { - return ptr; - } - - void swap(scoped_ptr_malloc & b) { - T* tmp = b.ptr; - b.ptr = ptr; - ptr = tmp; - } - - T* release() { - T* tmp = ptr; - ptr = 0; - return tmp; - } - - T** accept() { - if (ptr) { - FF(static_cast(ptr)); - ptr = 0; - } - return &ptr; - } -}; - -template inline -void swap(scoped_ptr_malloc& a, scoped_ptr_malloc& b) { - a.swap(b); +// A function to convert T* into scoped_ptr +// Doing e.g. make_scoped_ptr(new FooBarBaz(arg)) is a shorter notation +// for scoped_ptr >(new FooBarBaz(arg)) +template +webrtc::scoped_ptr rtc_make_scoped_ptr(T* ptr) { + return webrtc::scoped_ptr(ptr); } -} // namespace webrtc - // Pop off 'ignored "-Wunused-local-typedefs"': #if defined(__GNUC__) #if !defined(__clang__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/scoped_vector.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/scoped_vector.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/scoped_vector.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/scoped_vector.h 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Borrowed from Chromium's src/base/memory/scoped_vector.h. + +#ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_SCOPED_VECTOR_H_ +#define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_SCOPED_VECTOR_H_ + +#include + +#include "webrtc/base/checks.h" +#include "webrtc/system_wrappers/interface/stl_util.h" +#include "webrtc/system_wrappers/source/move.h" + +namespace webrtc { + +// ScopedVector wraps a vector deleting the elements from its +// destructor. +template +class ScopedVector { + RTC_MOVE_ONLY_TYPE_FOR_CPP_03(ScopedVector, RValue) + + public: + typedef typename std::vector::allocator_type allocator_type; + typedef typename std::vector::size_type size_type; + typedef typename std::vector::difference_type difference_type; + typedef typename std::vector::pointer pointer; + typedef typename std::vector::const_pointer const_pointer; + typedef typename std::vector::reference reference; + typedef typename std::vector::const_reference const_reference; + typedef typename std::vector::value_type value_type; + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; + typedef typename std::vector::reverse_iterator reverse_iterator; + typedef typename std::vector::const_reverse_iterator + const_reverse_iterator; + + ScopedVector() {} + ~ScopedVector() { clear(); } + ScopedVector(RValue other) { swap(*other.object); } + + ScopedVector& operator=(RValue rhs) { + swap(*rhs.object); + return *this; + } + + reference operator[](size_t index) { return v_[index]; } + const_reference operator[](size_t index) const { return v_[index]; } + + bool empty() const { return v_.empty(); } + size_t size() const { return v_.size(); } + + reverse_iterator rbegin() { return v_.rbegin(); } + const_reverse_iterator rbegin() const { return v_.rbegin(); } + reverse_iterator rend() { return v_.rend(); } + const_reverse_iterator rend() const { return v_.rend(); } + + iterator begin() { return v_.begin(); } + const_iterator begin() const { return v_.begin(); } + iterator end() { return v_.end(); } + const_iterator end() const { return v_.end(); } + + const_reference front() const { return v_.front(); } + reference front() { return v_.front(); } + const_reference back() const { return v_.back(); } + reference back() { return v_.back(); } + + void push_back(T* elem) { v_.push_back(elem); } + + void pop_back() { + DCHECK(!empty()); + delete v_.back(); + v_.pop_back(); + } + + std::vector& get() { return v_; } + const std::vector& get() const { return v_; } + void swap(std::vector& other) { v_.swap(other); } + void swap(ScopedVector& other) { v_.swap(other.v_); } + void release(std::vector* out) { + out->swap(v_); + v_.clear(); + } + + void reserve(size_t capacity) { v_.reserve(capacity); } + + // Resize, deleting elements in the disappearing range if we are shrinking. + void resize(size_t new_size) { + if (v_.size() > new_size) + STLDeleteContainerPointers(v_.begin() + new_size, v_.end()); + v_.resize(new_size); + } + + template + void assign(InputIterator begin, InputIterator end) { + v_.assign(begin, end); + } + + void clear() { STLDeleteElements(&v_); } + + // Like |clear()|, but doesn't delete any elements. + void weak_clear() { v_.clear(); } + + // Lets the ScopedVector take ownership of |x|. + iterator insert(iterator position, T* x) { + return v_.insert(position, x); + } + + // Lets the ScopedVector take ownership of elements in [first,last). + template + void insert(iterator position, InputIterator first, InputIterator last) { + v_.insert(position, first, last); + } + + iterator erase(iterator position) { + delete *position; + return v_.erase(position); + } + + iterator erase(iterator first, iterator last) { + STLDeleteContainerPointers(first, last); + return v_.erase(first, last); + } + + // Like |erase()|, but doesn't delete the element at |position|. + iterator weak_erase(iterator position) { + return v_.erase(position); + } + + // Like |erase()|, but doesn't delete the elements in [first, last). + iterator weak_erase(iterator first, iterator last) { + return v_.erase(first, last); + } + + private: + std::vector v_; +}; + +} // namespace webrtc + +#endif // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_SCOPED_VECTOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/stl_util.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/stl_util.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/stl_util.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/stl_util.h 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Borrowed from Chromium's src/base/stl_util.h. + +#ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_STL_UTIL_H_ +#define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_STL_UTIL_H_ + +#include +#include +#include +#include +#include +#include + +namespace webrtc { + +// Clears internal memory of an STL object. +// STL clear()/reserve(0) does not always free internal memory allocated +// This function uses swap/destructor to ensure the internal memory is freed. +template +void STLClearObject(T* obj) { + T tmp; + tmp.swap(*obj); + // Sometimes "T tmp" allocates objects with memory (arena implementation?). + // Hence using additional reserve(0) even if it doesn't always work. + obj->reserve(0); +} + +// For a range within a container of pointers, calls delete (non-array version) +// on these pointers. +// NOTE: for these three functions, we could just implement a DeleteObject +// functor and then call for_each() on the range and functor, but this +// requires us to pull in all of algorithm.h, which seems expensive. +// For hash_[multi]set, it is important that this deletes behind the iterator +// because the hash_set may call the hash function on the iterator when it is +// advanced, which could result in the hash function trying to deference a +// stale pointer. +template +void STLDeleteContainerPointers(ForwardIterator begin, ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete *temp; + } +} + +// For a range within a container of pairs, calls delete (non-array version) on +// BOTH items in the pairs. +// NOTE: Like STLDeleteContainerPointers, it is important that this deletes +// behind the iterator because if both the key and value are deleted, the +// container may call the hash function on the iterator when it is advanced, +// which could result in the hash function trying to dereference a stale +// pointer. +template +void STLDeleteContainerPairPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete temp->first; + delete temp->second; + } +} + +// For a range within a container of pairs, calls delete (non-array version) on +// the FIRST item in the pairs. +// NOTE: Like STLDeleteContainerPointers, deleting behind the iterator. +template +void STLDeleteContainerPairFirstPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete temp->first; + } +} + +// For a range within a container of pairs, calls delete. +// NOTE: Like STLDeleteContainerPointers, deleting behind the iterator. +// Deleting the value does not always invalidate the iterator, but it may +// do so if the key is a pointer into the value object. +template +void STLDeleteContainerPairSecondPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete temp->second; + } +} + +// To treat a possibly-empty vector as an array, use these functions. +// If you know the array will never be empty, you can use &*v.begin() +// directly, but that is undefined behaviour if |v| is empty. +template +inline T* vector_as_array(std::vector* v) { + return v->empty() ? NULL : &*v->begin(); +} + +template +inline const T* vector_as_array(const std::vector* v) { + return v->empty() ? NULL : &*v->begin(); +} + +// Return a mutable char* pointing to a string's internal buffer, +// which may not be null-terminated. Writing through this pointer will +// modify the string. +// +// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the +// next call to a string method that invalidates iterators. +// +// As of 2006-04, there is no standard-blessed way of getting a +// mutable reference to a string's internal buffer. However, issue 530 +// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-active.html#530) +// proposes this as the method. According to Matt Austern, this should +// already work on all current implementations. +inline char* string_as_array(std::string* str) { + // DO NOT USE const_cast(str->data()) + return str->empty() ? NULL : &*str->begin(); +} + +// The following functions are useful for cleaning up STL containers whose +// elements point to allocated memory. + +// STLDeleteElements() deletes all the elements in an STL container and clears +// the container. This function is suitable for use with a vector, set, +// hash_set, or any other STL container which defines sensible begin(), end(), +// and clear() methods. +// +// If container is NULL, this function is a no-op. +// +// As an alternative to calling STLDeleteElements() directly, consider +// STLElementDeleter (defined below), which ensures that your container's +// elements are deleted when the STLElementDeleter goes out of scope. +template +void STLDeleteElements(T* container) { + if (!container) + return; + STLDeleteContainerPointers(container->begin(), container->end()); + container->clear(); +} + +// Given an STL container consisting of (key, value) pairs, STLDeleteValues +// deletes all the "value" components and clears the container. Does nothing +// in the case it's given a NULL pointer. +template +void STLDeleteValues(T* container) { + if (!container) + return; + for (typename T::iterator i(container->begin()); i != container->end(); ++i) + delete i->second; + container->clear(); +} + + +// The following classes provide a convenient way to delete all elements or +// values from STL containers when they goes out of scope. This greatly +// simplifies code that creates temporary objects and has multiple return +// statements. Example: +// +// vector tmp_proto; +// STLElementDeleter > d(&tmp_proto); +// if (...) return false; +// ... +// return success; + +// Given a pointer to an STL container this class will delete all the element +// pointers when it goes out of scope. +template +class STLElementDeleter { + public: + STLElementDeleter(T* container) : container_(container) {} + ~STLElementDeleter() { STLDeleteElements(container_); } + + private: + T* container_; +}; + +// Given a pointer to an STL container this class will delete all the value +// pointers when it goes out of scope. +template +class STLValueDeleter { + public: + STLValueDeleter(T* container) : container_(container) {} + ~STLValueDeleter() { STLDeleteValues(container_); } + + private: + T* container_; +}; + +// Test to see if a set, map, hash_set or hash_map contains a particular key. +// Returns true if the key is in the collection. +template +bool ContainsKey(const Collection& collection, const Key& key) { + return collection.find(key) != collection.end(); +} + +// Returns true if the container is sorted. +template +bool STLIsSorted(const Container& cont) { + // Note: Use reverse iterator on container to ensure we only require + // value_type to implement operator<. + return std::adjacent_find(cont.rbegin(), cont.rend(), + std::less()) + == cont.rend(); +} + +// Returns a new ResultType containing the difference of two sorted containers. +template +ResultType STLSetDifference(const Arg1& a1, const Arg2& a2) { + assert(STLIsSorted(a1)); + assert(STLIsSorted(a2)); + ResultType difference; + std::set_difference(a1.begin(), a1.end(), + a2.begin(), a2.end(), + std::inserter(difference, difference.end())); + return difference; +} + +// Returns a new ResultType containing the union of two sorted containers. +template +ResultType STLSetUnion(const Arg1& a1, const Arg2& a2) { + assert(STLIsSorted(a1)); + assert(STLIsSorted(a2)); + ResultType result; + std::set_union(a1.begin(), a1.end(), + a2.begin(), a2.end(), + std::inserter(result, result.end())); + return result; +} + +// Returns a new ResultType containing the intersection of two sorted +// containers. +template +ResultType STLSetIntersection(const Arg1& a1, const Arg2& a2) { + assert(STLIsSorted(a1)); + assert(STLIsSorted(a2)); + ResultType result; + std::set_intersection(a1.begin(), a1.end(), + a2.begin(), a2.end(), + std::inserter(result, result.end())); + return result; +} + +// Returns true if the sorted container |a1| contains all elements of the sorted +// container |a2|. +template +bool STLIncludes(const Arg1& a1, const Arg2& a2) { + assert(STLIsSorted(a1)); + assert(STLIsSorted(a2)); + return std::includes(a1.begin(), a1.end(), + a2.begin(), a2.end()); +} + +} // namespace webrtc + +#endif // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_STL_UTIL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/template_util.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/template_util.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/template_util.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/template_util.h 2015-02-03 14:33:37.000000000 +0000 @@ -13,7 +13,7 @@ #ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_TEMPLATE_UTIL_H_ #define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_TEMPLATE_UTIL_H_ -#include // For size_t. +#include // For size_t. namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/thread_annotations.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/thread_annotations.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/thread_annotations.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/thread_annotations.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,99 +0,0 @@ -// -// Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the LICENSE file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// -// Borrowed from -// https://code.google.com/p/gperftools/source/browse/src/base/thread_annotations.h -// but adapted for clang attributes instead of the gcc. -// -// This header file contains the macro definitions for thread safety -// annotations that allow the developers to document the locking policies -// of their multi-threaded code. The annotations can also help program -// analysis tools to identify potential thread safety issues. - -#ifndef BASE_THREAD_ANNOTATIONS_H_ -#define BASE_THREAD_ANNOTATIONS_H_ - -#if defined(__clang__) && (!defined(SWIG)) -#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) -#else -#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op -#endif - -// Document if a shared variable/field needs to be protected by a lock. -// GUARDED_BY allows the user to specify a particular lock that should be -// held when accessing the annotated variable, while GUARDED_VAR only -// indicates a shared variable should be guarded (by any lock). GUARDED_VAR -// is primarily used when the client cannot express the name of the lock. -#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) -#define GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(guarded) - -// Document if the memory location pointed to by a pointer should be guarded -// by a lock when dereferencing the pointer. Similar to GUARDED_VAR, -// PT_GUARDED_VAR is primarily used when the client cannot express the name -// of the lock. Note that a pointer variable to a shared memory location -// could itself be a shared variable. For example, if a shared global pointer -// q, which is guarded by mu1, points to a shared memory location that is -// guarded by mu2, q should be annotated as follows: -// int *q GUARDED_BY(mu1) PT_GUARDED_BY(mu2); -#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(point_to_guarded_by(x)) -#define PT_GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(point_to_guarded) - -// Document the acquisition order between locks that can be held -// simultaneously by a thread. For any two locks that need to be annotated -// to establish an acquisition order, only one of them needs the annotation. -// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER -// and ACQUIRED_BEFORE.) -#define ACQUIRED_AFTER(x) THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(x)) -#define ACQUIRED_BEFORE(x) THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(x)) - -// The following three annotations document the lock requirements for -// functions/methods. - -// Document if a function expects certain locks to be held before it is called -#define EXCLUSIVE_LOCKS_REQUIRED(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) - -#define SHARED_LOCKS_REQUIRED(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) - -// Document the locks acquired in the body of the function. These locks -// cannot be held when calling this function (as google3's Mutex locks are -// non-reentrant). -#define LOCKS_EXCLUDED(x) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(x)) - -// Document the lock the annotated function returns without acquiring it. -#define LOCK_RETURNED(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) - -// Document if a class/type is a lockable type (such as the Mutex class). -#define LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(lockable) - -// Document if a class is a scoped lockable type (such as the MutexLock class). -#define SCOPED_LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) - -// The following annotations specify lock and unlock primitives. -#define EXCLUSIVE_LOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) - -#define SHARED_LOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) - -#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) - -#define SHARED_TRYLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) - -#define UNLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) - -// An escape hatch for thread safety analysis to ignore the annotated function. -#define NO_THREAD_SAFETY_ANALYSIS \ - THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) - -#endif // BASE_THREAD_ANNOTATIONS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/timestamp_extrapolator.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/timestamp_extrapolator.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/timestamp_extrapolator.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/timestamp_extrapolator.h 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SYSTEM_WRAPPERS_INTERFACE_TIMESTAMP_EXTRAPOLATOR_H_ +#define SYSTEM_WRAPPERS_INTERFACE_TIMESTAMP_EXTRAPOLATOR_H_ + +#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" +#include "webrtc/typedefs.h" + +namespace webrtc +{ + +class TimestampExtrapolator +{ +public: + explicit TimestampExtrapolator(int64_t start_ms); + ~TimestampExtrapolator(); + void Update(int64_t tMs, uint32_t ts90khz); + int64_t ExtrapolateLocalTime(uint32_t timestamp90khz); + void Reset(int64_t start_ms); + +private: + void CheckForWrapArounds(uint32_t ts90khz); + bool DelayChangeDetection(double error); + RWLockWrapper* _rwLock; + double _w[2]; + double _pP[2][2]; + int64_t _startMs; + int64_t _prevMs; + uint32_t _firstTimestamp; + int32_t _wrapArounds; + int64_t _prevUnwrappedTimestamp; + int64_t _prevWrapTimestamp; + const double _lambda; + bool _firstAfterReset; + uint32_t _packetCount; + const uint32_t _startUpFilterDelayInPackets; + + double _detectorAccumulatorPos; + double _detectorAccumulatorNeg; + const double _alarmThreshold; + const double _accDrift; + const double _accMaxError; + const double _pP11; +}; + +} // namespace webrtc + +#endif // SYSTEM_WRAPPERS_INTERFACE_TIMESTAMP_EXTRAPOLATOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/utf_util_win.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/utf_util_win.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/utf_util_win.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/interface/utf_util_win.h 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Conversion functions for UTF-8 and UTF-16 strings on Windows. +// Duplicated from talk/base/win32.h. +#ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_UTF_UTIL_H_ +#define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_UTF_UTIL_H_ + +#ifdef WIN32 +#include +#include + +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +inline std::wstring ToUtf16(const char* utf8, size_t len) { + int len16 = ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast(len), + NULL, 0); + scoped_ptr ws(new wchar_t[len16]); + ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast(len), ws.get(), + len16); + return std::wstring(ws.get(), len16); +} + +inline std::wstring ToUtf16(const std::string& str) { + return ToUtf16(str.data(), str.length()); +} + +inline std::string ToUtf8(const wchar_t* wide, size_t len) { + int len8 = ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast(len), + NULL, 0, NULL, NULL); + scoped_ptr ns(new char[len8]); + ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast(len), ns.get(), len8, + NULL, NULL); + return std::string(ns.get(), len8); +} + +inline std::string ToUtf8(const wchar_t* wide) { + return ToUtf8(wide, wcslen(wide)); +} + +inline std::string ToUtf8(const std::wstring& wstr) { + return ToUtf8(wstr.data(), wstr.length()); +} + +} // namespace webrtc + +#endif // WIN32 +#endif // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_UTF_UTIL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/OWNERS 2015-01-25 22:24:31.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/OWNERS 2015-02-03 14:33:37.000000000 +0000 @@ -1,7 +1,8 @@ henrike@webrtc.org -pwestin@webrtc.org perkj@webrtc.org henrika@webrtc.org henrikg@webrtc.org mflodman@webrtc.org -niklas.enbom@webrtc.org \ No newline at end of file +niklas.enbom@webrtc.org + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/aligned_array_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/aligned_array_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/aligned_array_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/aligned_array_unittest.cc 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/system_wrappers/interface/aligned_array.h" + +#include + +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +bool IsAligned(const void* ptr, int alignment) { + return reinterpret_cast(ptr) % alignment == 0; +} + +} // namespace + +namespace webrtc { + +TEST(AlignedArrayTest, CheckAlignment) { + AlignedArray arr(10, 7, 128); + ASSERT_TRUE(IsAligned(arr.Array(), 128)); + for (int i = 0; i < 10; ++i) { + ASSERT_TRUE(IsAligned(arr.Row(i), 128)); + ASSERT_EQ(arr.Row(i), arr.Array()[i]); + } +} + +TEST(AlignedArrayTest, CheckOverlap) { + AlignedArray arr(10, 7, 128); + + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 7; ++j) { + arr.At(i, j) = 20 * i + j; + } + } + + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 7; ++j) { + ASSERT_EQ(arr.At(i, j), 20 * i + j); + ASSERT_EQ(arr.Row(i)[j], 20 * i + j); + ASSERT_EQ(arr.Array()[i][j], 20 * i + j); + } + } +} + +} // namespace webrtc + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/aligned_malloc_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/aligned_malloc_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/aligned_malloc_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/aligned_malloc_unittest.cc 2015-02-03 14:33:37.000000000 +0000 @@ -16,14 +16,16 @@ #include #endif +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/typedefs.h" -#include "testing/gtest/include/gtest/gtest.h" +namespace webrtc { // Returns true if |size| and |alignment| are valid combinations. bool CorrectUsage(size_t size, size_t alignment) { - webrtc::Allocator::scoped_ptr_aligned scoped( - webrtc::AlignedMalloc(size, alignment)); + scoped_ptr scoped( + static_cast(AlignedMalloc(size, alignment))); if (scoped.get() == NULL) { return false; } @@ -34,16 +36,15 @@ TEST(AlignedMalloc, GetRightAlign) { const size_t size = 100; const size_t alignment = 32; - const size_t left_missalignment = 8; - webrtc::Allocator::scoped_ptr_aligned scoped( - webrtc::AlignedMalloc(size, alignment)); + const size_t left_misalignment = 1; + scoped_ptr scoped( + static_cast(AlignedMalloc(size, alignment))); EXPECT_TRUE(scoped.get() != NULL); const uintptr_t aligned_address = reinterpret_cast (scoped.get()); - const uintptr_t missaligned_address = aligned_address - left_missalignment; - const char* missaligned_ptr = reinterpret_cast( - missaligned_address); - const char* realigned_ptr = webrtc::GetRightAlign( - missaligned_ptr, alignment); + const uintptr_t misaligned_address = aligned_address - left_misalignment; + const char* misaligned_ptr = reinterpret_cast( + misaligned_address); + const char* realigned_ptr = GetRightAlign(misaligned_ptr, alignment); EXPECT_EQ(scoped.get(), realigned_ptr); } @@ -76,3 +77,6 @@ size_t alignment = 128; EXPECT_TRUE(CorrectUsage(size, alignment)); } + +} // namespace webrtc + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/Android.mk 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE := libwebrtc_system_wrappers -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := \ - android/cpu-features.c \ - cpu_features_android.c \ - sort.cc \ - aligned_malloc.cc \ - atomic32_posix.cc \ - condition_variable.cc \ - cpu_features.cc \ - cpu_info.cc \ - critical_section.cc \ - event.cc \ - event_tracer.cc \ - file_impl.cc \ - list_no_stl.cc \ - rw_lock.cc \ - thread.cc \ - trace_impl.cc \ - condition_variable_posix.cc \ - critical_section_posix.cc \ - event_posix.cc \ - sleep.cc \ - thread_posix.cc \ - tick_util.cc \ - trace_posix.cc \ - rw_lock_posix.cc - -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/spreadsortlib \ - external/webrtc - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/clock.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/clock.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/clock.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/clock.cc 2015-02-03 14:33:37.000000000 +0000 @@ -20,6 +20,7 @@ #include #endif +#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" #include "webrtc/system_wrappers/interface/tick_util.h" namespace webrtc { @@ -128,18 +129,19 @@ class RealTimeClock : public Clock { // Return a timestamp in milliseconds relative to some arbitrary source; the // source is fixed for this clock. - virtual int64_t TimeInMilliseconds() OVERRIDE { + virtual int64_t TimeInMilliseconds() const OVERRIDE { return TickTime::MillisecondTimestamp(); } // Return a timestamp in microseconds relative to some arbitrary source; the // source is fixed for this clock. - virtual int64_t TimeInMicroseconds() OVERRIDE { + virtual int64_t TimeInMicroseconds() const OVERRIDE { return TickTime::MicrosecondTimestamp(); } // Retrieve an NTP absolute timestamp in seconds and fractions of a second. - virtual void CurrentNtp(uint32_t& seconds, uint32_t& fractions) OVERRIDE { + virtual void CurrentNtp(uint32_t& seconds, + uint32_t& fractions) const OVERRIDE { timeval tv = CurrentTimeVal(); double microseconds_in_seconds; Adjust(tv, &seconds, µseconds_in_seconds); @@ -148,7 +150,7 @@ } // Retrieve an NTP absolute timestamp in milliseconds. - virtual int64_t CurrentNtpInMilliseconds() OVERRIDE { + virtual int64_t CurrentNtpInMilliseconds() const OVERRIDE { timeval tv = CurrentTimeVal(); uint32_t seconds; double microseconds_in_seconds; @@ -236,12 +238,12 @@ // // Note that on Windows, GetSystemTimeAsFileTime has poorer (up to 15 ms) // resolution than the media timers, hence the WindowsHelpTimer context -// object and Synchronize API. +// object and Synchronize API to sync the two. // // We only sync up once, which means that on Windows, our realtime clock // wont respond to system time/date changes without a program restart. -// TODO: We could attempt to detect 1+ minute jumps and resync for parity -// with other platforms. +// TODO(henrike): We should probably call sync more often to catch +// drift and time changes for parity with other platforms. static WindowsHelpTimer *SyncGlobalHelpTimer() { static WindowsHelpTimer global_help_timer = {0, 0, {{ 0, 0}, 0}, 0}; @@ -263,23 +265,30 @@ } SimulatedClock::SimulatedClock(int64_t initial_time_us) - : time_us_(initial_time_us) {} + : time_us_(initial_time_us), lock_(RWLockWrapper::CreateRWLock()) { +} + +SimulatedClock::~SimulatedClock() { +} -int64_t SimulatedClock::TimeInMilliseconds() { +int64_t SimulatedClock::TimeInMilliseconds() const { + ReadLockScoped synchronize(*lock_); return (time_us_ + 500) / 1000; } -int64_t SimulatedClock::TimeInMicroseconds() { +int64_t SimulatedClock::TimeInMicroseconds() const { + ReadLockScoped synchronize(*lock_); return time_us_; } -void SimulatedClock::CurrentNtp(uint32_t& seconds, uint32_t& fractions) { - seconds = (TimeInMilliseconds() / 1000) + kNtpJan1970; - fractions = (uint32_t)((TimeInMilliseconds() % 1000) * - kMagicNtpFractionalUnit / 1000); +void SimulatedClock::CurrentNtp(uint32_t& seconds, uint32_t& fractions) const { + int64_t now_ms = TimeInMilliseconds(); + seconds = (now_ms / 1000) + kNtpJan1970; + fractions = + static_cast((now_ms % 1000) * kMagicNtpFractionalUnit / 1000); } -int64_t SimulatedClock::CurrentNtpInMilliseconds() { +int64_t SimulatedClock::CurrentNtpInMilliseconds() const { return TimeInMilliseconds() + 1000 * static_cast(kNtpJan1970); } @@ -288,6 +297,7 @@ } void SimulatedClock::AdvanceTimeMicroseconds(int64_t microseconds) { + WriteLockScoped synchronize(*lock_); time_us_ += microseconds; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/clock_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/clock_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/clock_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/clock_unittest.cc 2015-02-03 14:33:37.000000000 +0000 @@ -20,6 +20,7 @@ uint32_t fractions; clock->CurrentNtp(seconds, fractions); int64_t milliseconds = clock->CurrentNtpInMilliseconds(); + EXPECT_GT(milliseconds / 1000, kNtpJan1970); EXPECT_GE(milliseconds, Clock::NtpToMs(seconds, fractions)); EXPECT_NEAR(milliseconds, Clock::NtpToMs(seconds, fractions), 5); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/condition_variable_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/condition_variable_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/condition_variable_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/condition_variable_unittest.cc 2015-02-03 14:33:37.000000000 +0000 @@ -14,13 +14,11 @@ #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/thread_wrapper.h" #include "webrtc/system_wrappers/interface/trace.h" -#include "webrtc/system_wrappers/source/unittest_utilities.h" namespace webrtc { namespace { -const int kLogTrace = false; // Set to true to enable debug logging to stdout. const int kLongWaitMs = 100 * 1000; // A long time in testing terms const int kShortWaitMs = 2 * 1000; // Long enough for process switches to happen @@ -143,9 +141,7 @@ class CondVarTest : public ::testing::Test { public: - CondVarTest() - : trace_(kLogTrace) { - } + CondVarTest() {} virtual void SetUp() { thread_ = ThreadWrapper::CreateThread(&WaitingRunFunction, @@ -171,7 +167,6 @@ Baton baton_; private: - ScopedTracing trace_; ThreadWrapper* thread_; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/critical_section_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/critical_section_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/critical_section_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/critical_section_unittest.cc 2015-02-03 14:33:37.000000000 +0000 @@ -14,14 +14,11 @@ #include "webrtc/system_wrappers/interface/sleep.h" #include "webrtc/system_wrappers/interface/thread_wrapper.h" #include "webrtc/system_wrappers/interface/trace.h" -#include "webrtc/system_wrappers/source/unittest_utilities.h" namespace webrtc { namespace { -const bool kLogTrace = false; // Set to true to enable debug logging to stdout. - // Cause a process switch. Needed to avoid depending on // busy-wait in tests. static void SwitchProcess() { @@ -54,8 +51,7 @@ class CritSectTest : public ::testing::Test { public: - CritSectTest() : trace_(kLogTrace) { - } + CritSectTest() {} // Waits a number of cycles for the count to reach a given value. // Returns true if the target is reached or passed. @@ -70,9 +66,6 @@ } return (count->Count() >= target); } - -private: - ScopedTracing trace_; }; bool LockUnlockThenStopRunFunction(void* obj) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/field_trial_default.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/field_trial_default.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/field_trial_default.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/field_trial_default.cc 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,22 @@ +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// + +#include "webrtc/system_wrappers/interface/field_trial.h" + +// Clients of webrtc that do not want to configure field trials can link with +// this instead of providing their own implementation. +namespace webrtc { +namespace field_trial { + +std::string FindFullName(const std::string& name) { + return std::string(); +} + +} // namespace field_trial +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/logging.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/logging.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/logging.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/logging.cc 2015-02-03 14:33:37.000000000 +0000 @@ -55,7 +55,7 @@ LogMessage::~LogMessage() { const std::string& str = print_stream_.str(); - Trace::Add(WebRtcSeverity(severity_), kTraceUndefined, 0, str.c_str()); + Trace::Add(WebRtcSeverity(severity_), kTraceUndefined, 0, "%s", str.c_str()); } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/logging_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/logging_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/logging_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/logging_unittest.cc 2015-02-03 14:33:37.000000000 +0000 @@ -46,22 +46,19 @@ void SetUp() { Trace::CreateTrace(); Trace::SetTraceCallback(this); - // Reduce the chance that spurious traces will ruin the test. - Trace::set_level_filter(kTraceWarning | kTraceError); } void TearDown() { - CriticalSectionScoped cs(crit_.get()); Trace::SetTraceCallback(NULL); Trace::ReturnTrace(); + CriticalSectionScoped cs(crit_.get()); ASSERT_EQ(kTraceNone, level_) << "Print() was not called"; } scoped_ptr crit_; scoped_ptr cv_; - TraceLevel level_; - int length_; - std::ostringstream expected_log_; + TraceLevel level_ GUARDED_BY(crit_); + std::ostringstream expected_log_ GUARDED_BY(crit_); }; TEST_F(LoggingTest, LogStream) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/metrics_default.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/metrics_default.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/metrics_default.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/metrics_default.cc 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,29 @@ +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// + +#include "webrtc/system_wrappers/interface/metrics.h" + +// Default implementation of histogram methods for WebRTC clients that do not +// want to provide their own implementation. + +namespace webrtc { +namespace metrics { + +Histogram* HistogramFactoryGetCounts(const std::string& name, int min, int max, + int bucket_count) { return NULL; } + +Histogram* HistogramFactoryGetEnumeration(const std::string& name, + int boundary) { return NULL; } + +void HistogramAdd( + Histogram* histogram_pointer, const std::string& name, int sample) {} + +} // namespace metrics +} // namespace webrtc + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/move.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/move.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/move.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/move.h 2015-02-03 14:33:37.000000000 +0000 @@ -10,8 +10,10 @@ // Borrowed from Chromium's src/base/move.h. -#ifndef WEBRTC_SYSTEM_WRAPPERS_INTEFACE_MOVE_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INTEFACE_MOVE_H_ +#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_MOVE_H_ +#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_MOVE_H_ + +#include "webrtc/typedefs.h" // Macro with the boilerplate that makes a type move-only in C++03. // @@ -144,6 +146,16 @@ // choose the one that adheres to the standard. // // +// WHY HAVE typedef void MoveOnlyTypeForCPP03 +// +// Callback<>/Bind() needs to understand movable-but-not-copyable semantics +// to call .Pass() appropriately when it is expected to transfer the value. +// The cryptic typedef MoveOnlyTypeForCPP03 is added to make this check +// easy and automatic in helper templates for Callback<>/Bind(). +// See IsMoveOnlyType template and its usage in base/callback_internal.h +// for more details. +// +// // COMPARED TO C++11 // // In C++11, you would implement this functionality using an r-value reference @@ -199,7 +211,7 @@ // // The workaround is to explicitly declare your copy constructor. // -#define WEBRTC_MOVE_ONLY_TYPE_FOR_CPP_03(type, rvalue_type) \ +#define RTC_MOVE_ONLY_TYPE_FOR_CPP_03(type, rvalue_type) \ private: \ struct rvalue_type { \ explicit rvalue_type(type* object) : object(object) {} \ @@ -209,7 +221,17 @@ void operator=(type&); \ public: \ operator rvalue_type() { return rvalue_type(this); } \ - type Pass() { return type(rvalue_type(this)); } \ + type Pass() WARN_UNUSED_RESULT { return type(rvalue_type(this)); } \ + typedef void MoveOnlyTypeForCPP03; \ + private: + +#define RTC_MOVE_ONLY_TYPE_WITH_MOVE_CONSTRUCTOR_FOR_CPP_03(type) \ + private: \ + type(type&); \ + void operator=(type&); \ + public: \ + type&& Pass() WARN_UNUSED_RESULT { return static_cast(*this); } \ + typedef void MoveOnlyTypeForCPP03; \ private: -#endif // WEBRTC_SYSTEM_WRAPPERS_INTEFACE_MOVE_H_ +#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_MOVE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/OWNERS 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,6 @@ +per-file *.isolate=kjellander@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/rtp_to_ntp.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/rtp_to_ntp.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/rtp_to_ntp.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/rtp_to_ntp.cc 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/system_wrappers/interface/rtp_to_ntp.h" + +#include "webrtc/system_wrappers/interface/clock.h" + +#include + +namespace webrtc { + +RtcpMeasurement::RtcpMeasurement() + : ntp_secs(0), ntp_frac(0), rtp_timestamp(0) {} + +RtcpMeasurement::RtcpMeasurement(uint32_t ntp_secs, uint32_t ntp_frac, + uint32_t timestamp) + : ntp_secs(ntp_secs), ntp_frac(ntp_frac), rtp_timestamp(timestamp) {} + +// Calculates the RTP timestamp frequency from two pairs of NTP and RTP +// timestamps. +bool CalculateFrequency( + int64_t rtcp_ntp_ms1, + uint32_t rtp_timestamp1, + int64_t rtcp_ntp_ms2, + uint32_t rtp_timestamp2, + double* frequency_khz) { + if (rtcp_ntp_ms1 <= rtcp_ntp_ms2) { + return false; + } + *frequency_khz = static_cast(rtp_timestamp1 - rtp_timestamp2) / + static_cast(rtcp_ntp_ms1 - rtcp_ntp_ms2); + return true; +} + +// Detects if there has been a wraparound between |old_timestamp| and +// |new_timestamp|, and compensates by adding 2^32 if that is the case. +bool CompensateForWrapAround(uint32_t new_timestamp, + uint32_t old_timestamp, + int64_t* compensated_timestamp) { + assert(compensated_timestamp); + int64_t wraps = CheckForWrapArounds(new_timestamp, old_timestamp); + if (wraps < 0) { + // Reordering, don't use this packet. + return false; + } + *compensated_timestamp = new_timestamp + (wraps << 32); + return true; +} + +bool UpdateRtcpList(uint32_t ntp_secs, + uint32_t ntp_frac, + uint32_t rtp_timestamp, + RtcpList* rtcp_list, + bool* new_rtcp_sr) { + *new_rtcp_sr = false; + if (ntp_secs == 0 && ntp_frac == 0) { + return false; + } + + RtcpMeasurement measurement; + measurement.ntp_secs = ntp_secs; + measurement.ntp_frac = ntp_frac; + measurement.rtp_timestamp = rtp_timestamp; + + for (RtcpList::iterator it = rtcp_list->begin(); + it != rtcp_list->end(); ++it) { + if (measurement.ntp_secs == (*it).ntp_secs && + measurement.ntp_frac == (*it).ntp_frac) { + // This RTCP has already been added to the list. + return true; + } + } + + // We need two RTCP SR reports to map between RTP and NTP. More than two will + // not improve the mapping. + if (rtcp_list->size() == 2) { + rtcp_list->pop_back(); + } + rtcp_list->push_front(measurement); + *new_rtcp_sr = true; + return true; +} + +// Converts |rtp_timestamp| to the NTP time base using the NTP and RTP timestamp +// pairs in |rtcp|. The converted timestamp is returned in +// |rtp_timestamp_in_ms|. This function compensates for wrap arounds in RTP +// timestamps and returns false if it can't do the conversion due to reordering. +bool RtpToNtpMs(int64_t rtp_timestamp, + const RtcpList& rtcp, + int64_t* rtp_timestamp_in_ms) { + assert(rtcp.size() == 2); + int64_t rtcp_ntp_ms_new = Clock::NtpToMs(rtcp.front().ntp_secs, + rtcp.front().ntp_frac); + int64_t rtcp_ntp_ms_old = Clock::NtpToMs(rtcp.back().ntp_secs, + rtcp.back().ntp_frac); + int64_t rtcp_timestamp_new = rtcp.front().rtp_timestamp; + int64_t rtcp_timestamp_old = rtcp.back().rtp_timestamp; + if (!CompensateForWrapAround(rtcp_timestamp_new, + rtcp_timestamp_old, + &rtcp_timestamp_new)) { + return false; + } + double freq_khz; + if (!CalculateFrequency(rtcp_ntp_ms_new, + rtcp_timestamp_new, + rtcp_ntp_ms_old, + rtcp_timestamp_old, + &freq_khz)) { + return false; + } + double offset = rtcp_timestamp_new - freq_khz * rtcp_ntp_ms_new; + int64_t rtp_timestamp_unwrapped; + if (!CompensateForWrapAround(rtp_timestamp, rtcp_timestamp_old, + &rtp_timestamp_unwrapped)) { + return false; + } + double rtp_timestamp_ntp_ms = (static_cast(rtp_timestamp_unwrapped) - + offset) / freq_khz + 0.5f; + if (rtp_timestamp_ntp_ms < 0) { + return false; + } + *rtp_timestamp_in_ms = rtp_timestamp_ntp_ms; + return true; +} + +int CheckForWrapArounds(uint32_t new_timestamp, uint32_t old_timestamp) { + if (new_timestamp < old_timestamp) { + // This difference should be less than -2^31 if we have had a wrap around + // (e.g. |new_timestamp| = 1, |rtcp_rtp_timestamp| = 2^32 - 1). Since it is + // cast to a int32_t, it should be positive. + if (static_cast(new_timestamp - old_timestamp) > 0) { + // Forward wrap around. + return 1; + } + } else if (static_cast(old_timestamp - new_timestamp) > 0) { + // This difference should be less than -2^31 if we have had a backward wrap + // around. Since it is cast to a int32_t, it should be positive. + return -1; + } + return 0; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/rtp_to_ntp_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/rtp_to_ntp_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/rtp_to_ntp_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/rtp_to_ntp_unittest.cc 2015-02-03 14:33:37.000000000 +0000 @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/system_wrappers/interface/rtp_to_ntp.h" + +namespace webrtc { + +TEST(WrapAroundTests, NoWrap) { + EXPECT_EQ(0, CheckForWrapArounds(0xFFFFFFFF, 0xFFFFFFFE)); + EXPECT_EQ(0, CheckForWrapArounds(1, 0)); + EXPECT_EQ(0, CheckForWrapArounds(0x00010000, 0x0000FFFF)); +} + +TEST(WrapAroundTests, ForwardWrap) { + EXPECT_EQ(1, CheckForWrapArounds(0, 0xFFFFFFFF)); + EXPECT_EQ(1, CheckForWrapArounds(0, 0xFFFF0000)); + EXPECT_EQ(1, CheckForWrapArounds(0x0000FFFF, 0xFFFFFFFF)); + EXPECT_EQ(1, CheckForWrapArounds(0x0000FFFF, 0xFFFF0000)); +} + +TEST(WrapAroundTests, BackwardWrap) { + EXPECT_EQ(-1, CheckForWrapArounds(0xFFFFFFFF, 0)); + EXPECT_EQ(-1, CheckForWrapArounds(0xFFFF0000, 0)); + EXPECT_EQ(-1, CheckForWrapArounds(0xFFFFFFFF, 0x0000FFFF)); + EXPECT_EQ(-1, CheckForWrapArounds(0xFFFF0000, 0x0000FFFF)); +} + +TEST(WrapAroundTests, OldRtcpWrapped) { + RtcpList rtcp; + uint32_t ntp_sec = 0; + uint32_t ntp_frac = 0; + uint32_t timestamp = 0; + const uint32_t kOneMsInNtpFrac = 4294967; + const uint32_t kTimestampTicksPerMs = 90; + rtcp.push_front(RtcpMeasurement(ntp_sec, ntp_frac, timestamp)); + ntp_frac += kOneMsInNtpFrac; + timestamp -= kTimestampTicksPerMs; + rtcp.push_front(RtcpMeasurement(ntp_sec, ntp_frac, timestamp)); + ntp_frac += kOneMsInNtpFrac; + timestamp -= kTimestampTicksPerMs; + int64_t timestamp_in_ms = -1; + // This expected to fail since it's highly unlikely that the older RTCP + // has a much smaller RTP timestamp than the newer. + EXPECT_FALSE(RtpToNtpMs(timestamp, rtcp, ×tamp_in_ms)); +} + +TEST(WrapAroundTests, NewRtcpWrapped) { + RtcpList rtcp; + uint32_t ntp_sec = 0; + uint32_t ntp_frac = 0; + uint32_t timestamp = 0xFFFFFFFF; + const uint32_t kOneMsInNtpFrac = 4294967; + const uint32_t kTimestampTicksPerMs = 90; + rtcp.push_front(RtcpMeasurement(ntp_sec, ntp_frac, timestamp)); + ntp_frac += kOneMsInNtpFrac; + timestamp += kTimestampTicksPerMs; + rtcp.push_front(RtcpMeasurement(ntp_sec, ntp_frac, timestamp)); + int64_t timestamp_in_ms = -1; + EXPECT_TRUE(RtpToNtpMs(rtcp.back().rtp_timestamp, rtcp, ×tamp_in_ms)); + // Since this RTP packet has the same timestamp as the RTCP packet constructed + // at time 0 it should be mapped to 0 as well. + EXPECT_EQ(0, timestamp_in_ms); +} + +TEST(WrapAroundTests, RtpWrapped) { + const uint32_t kOneMsInNtpFrac = 4294967; + const uint32_t kTimestampTicksPerMs = 90; + RtcpList rtcp; + uint32_t ntp_sec = 0; + uint32_t ntp_frac = 0; + uint32_t timestamp = 0xFFFFFFFF - 2 * kTimestampTicksPerMs; + rtcp.push_front(RtcpMeasurement(ntp_sec, ntp_frac, timestamp)); + ntp_frac += kOneMsInNtpFrac; + timestamp += kTimestampTicksPerMs; + rtcp.push_front(RtcpMeasurement(ntp_sec, ntp_frac, timestamp)); + ntp_frac += kOneMsInNtpFrac; + timestamp += kTimestampTicksPerMs; + int64_t timestamp_in_ms = -1; + EXPECT_TRUE(RtpToNtpMs(timestamp, rtcp, ×tamp_in_ms)); + // Since this RTP packet has the same timestamp as the RTCP packet constructed + // at time 0 it should be mapped to 0 as well. + EXPECT_EQ(2, timestamp_in_ms); +} + +TEST(WrapAroundTests, OldRtp_RtcpsWrapped) { + const uint32_t kOneMsInNtpFrac = 4294967; + const uint32_t kTimestampTicksPerMs = 90; + RtcpList rtcp; + uint32_t ntp_sec = 0; + uint32_t ntp_frac = 0; + uint32_t timestamp = 0; + rtcp.push_front(RtcpMeasurement(ntp_sec, ntp_frac, timestamp)); + ntp_frac += kOneMsInNtpFrac; + timestamp += kTimestampTicksPerMs; + rtcp.push_front(RtcpMeasurement(ntp_sec, ntp_frac, timestamp)); + ntp_frac += kOneMsInNtpFrac; + timestamp -= 2*kTimestampTicksPerMs; + int64_t timestamp_in_ms = -1; + EXPECT_FALSE(RtpToNtpMs(timestamp, rtcp, ×tamp_in_ms)); +} + +TEST(WrapAroundTests, OldRtp_NewRtcpWrapped) { + const uint32_t kOneMsInNtpFrac = 4294967; + const uint32_t kTimestampTicksPerMs = 90; + RtcpList rtcp; + uint32_t ntp_sec = 0; + uint32_t ntp_frac = 0; + uint32_t timestamp = 0xFFFFFFFF; + rtcp.push_front(RtcpMeasurement(ntp_sec, ntp_frac, timestamp)); + ntp_frac += kOneMsInNtpFrac; + timestamp += kTimestampTicksPerMs; + rtcp.push_front(RtcpMeasurement(ntp_sec, ntp_frac, timestamp)); + ntp_frac += kOneMsInNtpFrac; + timestamp -= kTimestampTicksPerMs; + int64_t timestamp_in_ms = -1; + EXPECT_TRUE(RtpToNtpMs(timestamp, rtcp, ×tamp_in_ms)); + // Constructed at the same time as the first RTCP and should therefore be + // mapped to zero. + EXPECT_EQ(0, timestamp_in_ms); +} + +TEST(WrapAroundTests, OldRtp_OldRtcpWrapped) { + const uint32_t kOneMsInNtpFrac = 4294967; + const uint32_t kTimestampTicksPerMs = 90; + RtcpList rtcp; + uint32_t ntp_sec = 0; + uint32_t ntp_frac = 0; + uint32_t timestamp = 0; + rtcp.push_front(RtcpMeasurement(ntp_sec, ntp_frac, timestamp)); + ntp_frac += kOneMsInNtpFrac; + timestamp -= kTimestampTicksPerMs; + rtcp.push_front(RtcpMeasurement(ntp_sec, ntp_frac, timestamp)); + ntp_frac += kOneMsInNtpFrac; + timestamp += 2*kTimestampTicksPerMs; + int64_t timestamp_in_ms = -1; + EXPECT_FALSE(RtpToNtpMs(timestamp, rtcp, ×tamp_in_ms)); +} +}; // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/scoped_vector_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/scoped_vector_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/scoped_vector_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/scoped_vector_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Borrowed from Chromium's src/base/memory/scoped_vector_unittest.cc + +#include "webrtc/system_wrappers/interface/scoped_vector.h" + +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace webrtc { +namespace { + +// The LifeCycleObject notifies its Observer upon construction & destruction. +class LifeCycleObject { + public: + class Observer { + public: + virtual void OnLifeCycleConstruct(LifeCycleObject* o) = 0; + virtual void OnLifeCycleDestroy(LifeCycleObject* o) = 0; + + protected: + virtual ~Observer() {} + }; + + ~LifeCycleObject() { + observer_->OnLifeCycleDestroy(this); + } + + private: + friend class LifeCycleWatcher; + + explicit LifeCycleObject(Observer* observer) + : observer_(observer) { + observer_->OnLifeCycleConstruct(this); + } + + Observer* observer_; + + DISALLOW_COPY_AND_ASSIGN(LifeCycleObject); +}; + +// The life cycle states we care about for the purposes of testing ScopedVector +// against objects. +enum LifeCycleState { + LC_INITIAL, + LC_CONSTRUCTED, + LC_DESTROYED, +}; + +// Because we wish to watch the life cycle of an object being constructed and +// destroyed, and further wish to test expectations against the state of that +// object, we cannot save state in that object itself. Instead, we use this +// pairing of the watcher, which observes the object and notifies of +// construction & destruction. Since we also may be testing assumptions about +// things not getting freed, this class also acts like a scoping object and +// deletes the |constructed_life_cycle_object_|, if any when the +// LifeCycleWatcher is destroyed. To keep this simple, the only expected state +// changes are: +// INITIAL -> CONSTRUCTED -> DESTROYED. +// Anything more complicated than that should start another test. +class LifeCycleWatcher : public LifeCycleObject::Observer { + public: + LifeCycleWatcher() : life_cycle_state_(LC_INITIAL) {} + virtual ~LifeCycleWatcher() {} + + // Assert INITIAL -> CONSTRUCTED and no LifeCycleObject associated with this + // LifeCycleWatcher. + virtual void OnLifeCycleConstruct(LifeCycleObject* object) OVERRIDE { + ASSERT_EQ(LC_INITIAL, life_cycle_state_); + ASSERT_EQ(NULL, constructed_life_cycle_object_.get()); + life_cycle_state_ = LC_CONSTRUCTED; + constructed_life_cycle_object_.reset(object); + } + + // Assert CONSTRUCTED -> DESTROYED and the |object| being destroyed is the + // same one we saw constructed. + virtual void OnLifeCycleDestroy(LifeCycleObject* object) OVERRIDE { + ASSERT_EQ(LC_CONSTRUCTED, life_cycle_state_); + LifeCycleObject* constructed_life_cycle_object = + constructed_life_cycle_object_.release(); + ASSERT_EQ(constructed_life_cycle_object, object); + life_cycle_state_ = LC_DESTROYED; + } + + LifeCycleState life_cycle_state() const { return life_cycle_state_; } + + // Factory method for creating a new LifeCycleObject tied to this + // LifeCycleWatcher. + LifeCycleObject* NewLifeCycleObject() { + return new LifeCycleObject(this); + } + + // Returns true iff |object| is the same object that this watcher is tracking. + bool IsWatching(LifeCycleObject* object) const { + return object == constructed_life_cycle_object_.get(); + } + + private: + LifeCycleState life_cycle_state_; + scoped_ptr constructed_life_cycle_object_; + + DISALLOW_COPY_AND_ASSIGN(LifeCycleWatcher); +}; + +TEST(ScopedVectorTest, LifeCycleWatcher) { + LifeCycleWatcher watcher; + EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); + LifeCycleObject* object = watcher.NewLifeCycleObject(); + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + delete object; + EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); +} + +TEST(ScopedVectorTest, PopBack) { + LifeCycleWatcher watcher; + EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); + ScopedVector scoped_vector; + scoped_vector.push_back(watcher.NewLifeCycleObject()); + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); + scoped_vector.pop_back(); + EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); + EXPECT_TRUE(scoped_vector.empty()); +} + +TEST(ScopedVectorTest, Clear) { + LifeCycleWatcher watcher; + EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); + ScopedVector scoped_vector; + scoped_vector.push_back(watcher.NewLifeCycleObject()); + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); + scoped_vector.clear(); + EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); + EXPECT_TRUE(scoped_vector.empty()); +} + +TEST(ScopedVectorTest, WeakClear) { + LifeCycleWatcher watcher; + EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); + ScopedVector scoped_vector; + scoped_vector.push_back(watcher.NewLifeCycleObject()); + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); + scoped_vector.weak_clear(); + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + EXPECT_TRUE(scoped_vector.empty()); +} + +TEST(ScopedVectorTest, ResizeShrink) { + LifeCycleWatcher first_watcher; + EXPECT_EQ(LC_INITIAL, first_watcher.life_cycle_state()); + LifeCycleWatcher second_watcher; + EXPECT_EQ(LC_INITIAL, second_watcher.life_cycle_state()); + ScopedVector scoped_vector; + + scoped_vector.push_back(first_watcher.NewLifeCycleObject()); + EXPECT_EQ(LC_CONSTRUCTED, first_watcher.life_cycle_state()); + EXPECT_EQ(LC_INITIAL, second_watcher.life_cycle_state()); + EXPECT_TRUE(first_watcher.IsWatching(scoped_vector[0])); + EXPECT_FALSE(second_watcher.IsWatching(scoped_vector[0])); + + scoped_vector.push_back(second_watcher.NewLifeCycleObject()); + EXPECT_EQ(LC_CONSTRUCTED, first_watcher.life_cycle_state()); + EXPECT_EQ(LC_CONSTRUCTED, second_watcher.life_cycle_state()); + EXPECT_FALSE(first_watcher.IsWatching(scoped_vector[1])); + EXPECT_TRUE(second_watcher.IsWatching(scoped_vector[1])); + + // Test that shrinking a vector deletes elements in the disappearing range. + scoped_vector.resize(1); + EXPECT_EQ(LC_CONSTRUCTED, first_watcher.life_cycle_state()); + EXPECT_EQ(LC_DESTROYED, second_watcher.life_cycle_state()); + EXPECT_EQ(1u, scoped_vector.size()); + EXPECT_TRUE(first_watcher.IsWatching(scoped_vector[0])); +} + +TEST(ScopedVectorTest, ResizeGrow) { + LifeCycleWatcher watcher; + EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); + ScopedVector scoped_vector; + scoped_vector.push_back(watcher.NewLifeCycleObject()); + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); + + scoped_vector.resize(5); + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + ASSERT_EQ(5u, scoped_vector.size()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector[0])); + EXPECT_FALSE(watcher.IsWatching(scoped_vector[1])); + EXPECT_FALSE(watcher.IsWatching(scoped_vector[2])); + EXPECT_FALSE(watcher.IsWatching(scoped_vector[3])); + EXPECT_FALSE(watcher.IsWatching(scoped_vector[4])); +} + +TEST(ScopedVectorTest, Scope) { + LifeCycleWatcher watcher; + EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); + { + ScopedVector scoped_vector; + scoped_vector.push_back(watcher.NewLifeCycleObject()); + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); + } + EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); +} + +TEST(ScopedVectorTest, MoveConstruct) { + LifeCycleWatcher watcher; + EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); + { + ScopedVector scoped_vector; + scoped_vector.push_back(watcher.NewLifeCycleObject()); + EXPECT_FALSE(scoped_vector.empty()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); + + ScopedVector scoped_vector_copy(scoped_vector.Pass()); + EXPECT_TRUE(scoped_vector.empty()); + EXPECT_FALSE(scoped_vector_copy.empty()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector_copy.back())); + + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + } + EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); +} + +TEST(ScopedVectorTest, MoveAssign) { + LifeCycleWatcher watcher; + EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); + { + ScopedVector scoped_vector; + scoped_vector.push_back(watcher.NewLifeCycleObject()); + ScopedVector scoped_vector_assign; + EXPECT_FALSE(scoped_vector.empty()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); + + scoped_vector_assign = scoped_vector.Pass(); + EXPECT_TRUE(scoped_vector.empty()); + EXPECT_FALSE(scoped_vector_assign.empty()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector_assign.back())); + + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + } + EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); +} + +class DeleteCounter { + public: + explicit DeleteCounter(int* deletes) + : deletes_(deletes) { + } + + ~DeleteCounter() { + (*deletes_)++; + } + + void VoidMethod0() {} + + private: + int* const deletes_; + + DISALLOW_COPY_AND_ASSIGN(DeleteCounter); +}; + +// This class is used in place of Chromium's base::Callback. +template +class PassThru { + public: + explicit PassThru(ScopedVector scoper) : scoper_(scoper.Pass()) {} + + ScopedVector Run() { + return scoper_.Pass(); + } + + private: + ScopedVector scoper_; +}; + +TEST(ScopedVectorTest, Passed) { + int deletes = 0; + ScopedVector deleter_vector; + deleter_vector.push_back(new DeleteCounter(&deletes)); + EXPECT_EQ(0, deletes); + PassThru pass_thru(deleter_vector.Pass()); + EXPECT_EQ(0, deletes); + ScopedVector result = pass_thru.Run(); + EXPECT_EQ(0, deletes); + result.clear(); + EXPECT_EQ(1, deletes); +}; + +TEST(ScopedVectorTest, InsertRange) { + LifeCycleWatcher watchers[5]; + size_t watchers_size = sizeof(watchers) / sizeof(*watchers); + + std::vector vec; + for (LifeCycleWatcher* it = watchers; it != watchers + watchers_size; + ++it) { + EXPECT_EQ(LC_INITIAL, it->life_cycle_state()); + vec.push_back(it->NewLifeCycleObject()); + EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state()); + } + // Start scope for ScopedVector. + { + ScopedVector scoped_vector; + scoped_vector.insert(scoped_vector.end(), vec.begin() + 1, vec.begin() + 3); + for (LifeCycleWatcher* it = watchers; it != watchers + watchers_size; + ++it) + EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state()); + } + for (LifeCycleWatcher* it = watchers; it != watchers + 1; ++it) + EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state()); + for (LifeCycleWatcher* it = watchers + 1; it != watchers + 3; ++it) + EXPECT_EQ(LC_DESTROYED, it->life_cycle_state()); + for (LifeCycleWatcher* it = watchers + 3; it != watchers + watchers_size; + ++it) + EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state()); +} + +} // namespace +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/stl_util_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/stl_util_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/stl_util_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/stl_util_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Borrowed from Chromium's src/base/stl_util_unittest.cc +#include "webrtc/system_wrappers/interface/stl_util.h" + +#include + +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// Used as test case to ensure the various base::STLXxx functions don't require +// more than operators "<" and "==" on values stored in containers. +class ComparableValue { + public: + explicit ComparableValue(int value) : value_(value) {} + + bool operator==(const ComparableValue& rhs) const { + return value_ == rhs.value_; + } + + bool operator<(const ComparableValue& rhs) const { + return value_ < rhs.value_; + } + + private: + int value_; +}; + +} // namespace + +namespace webrtc { +namespace { + +TEST(STLUtilTest, STLIsSorted) { + { + std::set set; + set.insert(24); + set.insert(1); + set.insert(12); + EXPECT_TRUE(STLIsSorted(set)); + } + + { + std::set set; + set.insert(ComparableValue(24)); + set.insert(ComparableValue(1)); + set.insert(ComparableValue(12)); + EXPECT_TRUE(STLIsSorted(set)); + } + + { + std::vector vector; + vector.push_back(1); + vector.push_back(1); + vector.push_back(4); + vector.push_back(64); + vector.push_back(12432); + EXPECT_TRUE(STLIsSorted(vector)); + vector.back() = 1; + EXPECT_FALSE(STLIsSorted(vector)); + } +} + +TEST(STLUtilTest, STLSetDifference) { + std::set a1; + a1.insert(1); + a1.insert(2); + a1.insert(3); + a1.insert(4); + + std::set a2; + a2.insert(3); + a2.insert(4); + a2.insert(5); + a2.insert(6); + a2.insert(7); + + { + std::set difference; + difference.insert(1); + difference.insert(2); + EXPECT_EQ(difference, STLSetDifference >(a1, a2)); + } + + { + std::set difference; + difference.insert(5); + difference.insert(6); + difference.insert(7); + EXPECT_EQ(difference, STLSetDifference >(a2, a1)); + } + + { + std::vector difference; + difference.push_back(1); + difference.push_back(2); + EXPECT_EQ(difference, STLSetDifference >(a1, a2)); + } + + { + std::vector difference; + difference.push_back(5); + difference.push_back(6); + difference.push_back(7); + EXPECT_EQ(difference, STLSetDifference >(a2, a1)); + } +} + +TEST(STLUtilTest, STLSetUnion) { + std::set a1; + a1.insert(1); + a1.insert(2); + a1.insert(3); + a1.insert(4); + + std::set a2; + a2.insert(3); + a2.insert(4); + a2.insert(5); + a2.insert(6); + a2.insert(7); + + { + std::set result; + result.insert(1); + result.insert(2); + result.insert(3); + result.insert(4); + result.insert(5); + result.insert(6); + result.insert(7); + EXPECT_EQ(result, STLSetUnion >(a1, a2)); + } + + { + std::set result; + result.insert(1); + result.insert(2); + result.insert(3); + result.insert(4); + result.insert(5); + result.insert(6); + result.insert(7); + EXPECT_EQ(result, STLSetUnion >(a2, a1)); + } + + { + std::vector result; + result.push_back(1); + result.push_back(2); + result.push_back(3); + result.push_back(4); + result.push_back(5); + result.push_back(6); + result.push_back(7); + EXPECT_EQ(result, STLSetUnion >(a1, a2)); + } + + { + std::vector result; + result.push_back(1); + result.push_back(2); + result.push_back(3); + result.push_back(4); + result.push_back(5); + result.push_back(6); + result.push_back(7); + EXPECT_EQ(result, STLSetUnion >(a2, a1)); + } +} + +TEST(STLUtilTest, STLSetIntersection) { + std::set a1; + a1.insert(1); + a1.insert(2); + a1.insert(3); + a1.insert(4); + + std::set a2; + a2.insert(3); + a2.insert(4); + a2.insert(5); + a2.insert(6); + a2.insert(7); + + { + std::set result; + result.insert(3); + result.insert(4); + EXPECT_EQ(result, STLSetIntersection >(a1, a2)); + } + + { + std::set result; + result.insert(3); + result.insert(4); + EXPECT_EQ(result, STLSetIntersection >(a2, a1)); + } + + { + std::vector result; + result.push_back(3); + result.push_back(4); + EXPECT_EQ(result, STLSetIntersection >(a1, a2)); + } + + { + std::vector result; + result.push_back(3); + result.push_back(4); + EXPECT_EQ(result, STLSetIntersection >(a2, a1)); + } +} + +TEST(STLUtilTest, STLIncludes) { + std::set a1; + a1.insert(1); + a1.insert(2); + a1.insert(3); + a1.insert(4); + + std::set a2; + a2.insert(3); + a2.insert(4); + + std::set a3; + a3.insert(3); + a3.insert(4); + a3.insert(5); + + EXPECT_TRUE(STLIncludes >(a1, a2)); + EXPECT_FALSE(STLIncludes >(a1, a3)); + EXPECT_FALSE(STLIncludes >(a2, a1)); + EXPECT_FALSE(STLIncludes >(a2, a3)); + EXPECT_FALSE(STLIncludes >(a3, a1)); + EXPECT_TRUE(STLIncludes >(a3, a2)); +} + +} // namespace +} // namespace webrtc + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/system_wrappers.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/system_wrappers.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/system_wrappers.gyp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/system_wrappers.gyp 2015-02-03 14:33:38.000000000 +0000 @@ -16,12 +16,16 @@ 'spreadsortlib', '../interface', ], + 'dependencies': [ + '../../base/base.gyp:rtc_base_approved', + ], 'direct_dependent_settings': { 'include_dirs': [ '../interface', ], }, 'sources': [ + '../interface/aligned_array.h', '../interface/aligned_malloc.h', '../interface/atomic32.h', '../interface/clock.h', @@ -35,23 +39,29 @@ '../interface/data_log_impl.h', '../interface/event_tracer.h', '../interface/event_wrapper.h', + '../interface/field_trial.h', '../interface/file_wrapper.h', '../interface/fix_interlocked_exchange_pointer_win.h', '../interface/logcat_trace_context.h', '../interface/logging.h', + '../interface/metrics.h', '../interface/ref_count.h', + '../interface/rtp_to_ntp.h', '../interface/rw_lock_wrapper.h', '../interface/scoped_ptr.h', '../interface/scoped_refptr.h', + '../interface/scoped_vector.h', '../interface/sleep.h', '../interface/sort.h', '../interface/static_instance.h', + '../interface/stl_util.h', '../interface/stringize_macros.h', - '../interface/thread_annotations.h', '../interface/thread_wrapper.h', '../interface/tick_util.h', + '../interface/timestamp_extrapolator.h', '../interface/trace.h', '../interface/trace_event.h', + '../interface/utf_util_win.h', 'aligned_malloc.cc', 'atomic32_mac.cc', 'atomic32_posix.cc', @@ -84,6 +94,7 @@ 'file_impl.h', 'logcat_trace_context.cc', 'logging.cc', + 'rtp_to_ntp.cc', 'rw_lock.cc', 'rw_lock_generic.cc', 'rw_lock_generic.h', @@ -100,6 +111,7 @@ 'thread_posix.h', 'thread_win.cc', 'thread_win.h', + 'timestamp_extrapolator.cc', 'trace_impl.cc', 'trace_impl.h', 'trace_posix.cc', @@ -118,6 +130,11 @@ 'WEBRTC_LAZY_TRACE_ALLOC', ], }], + ['build_with_mozilla', { + 'sources': [ + 'metrics_default.cc', + ], + }], ['OS=="android" or moz_widget_toolkit_gonk==1', { 'defines': [ 'WEBRTC_THREAD_RR', @@ -194,6 +211,31 @@ 4267, # size_t to int truncation. 4334, # Ignore warning on shift operator promotion. ], + }, { + 'target_name': 'field_trial_default', + 'type': 'static_library', + 'sources': [ + 'field_trial_default.cc', + ], + 'dependencies': [ + 'system_wrappers', + ] + }, { + 'target_name': 'metrics_default', + 'type': 'static_library', + 'sources': [ + 'metrics_default.cc', + ], + 'dependencies': [ + 'system_wrappers', + ] + }, { + 'target_name': 'system_wrappers_default', + 'type': 'static_library', + 'dependencies': [ + 'field_trial_default', + 'metrics_default', + ] }, ], # targets 'conditions': [ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/system_wrappers_tests.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/system_wrappers_tests.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/system_wrappers_tests.gyp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/system_wrappers_tests.gyp 2015-02-03 14:33:38.000000000 +0000 @@ -18,6 +18,7 @@ '<(webrtc_root)/test/test.gyp:test_support_main', ], 'sources': [ + 'aligned_array_unittest.cc', 'aligned_malloc_unittest.cc', 'clock_unittest.cc', 'condition_variable_unittest.cc', @@ -29,10 +30,12 @@ 'data_log_helpers_unittest.cc', 'data_log_c_helpers_unittest.c', 'data_log_c_helpers_unittest.h', + 'rtp_to_ntp_unittest.cc', + 'scoped_vector_unittest.cc', 'stringize_macros_unittest.cc', + 'stl_util_unittest.cc', 'thread_unittest.cc', 'thread_posix_unittest.cc', - 'unittest_utilities_unittest.cc', ], 'conditions': [ ['enable_data_logging==1', { @@ -43,9 +46,7 @@ ['os_posix==0', { 'sources!': [ 'thread_posix_unittest.cc', ], }], - # TODO(henrike): remove build_with_chromium==1 when the bots are - # using Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['OS=="android"', { 'dependencies': [ '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code', ], @@ -58,9 +59,7 @@ }, ], 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are using - # Chromium's buildbots. - ['include_tests==1 and build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['include_tests==1 and OS=="android"', { 'targets': [ { 'target_name': 'system_wrappers_unittests_apk_target', @@ -81,7 +80,6 @@ ], 'includes': [ '../../build/isolate.gypi', - 'system_wrappers_unittests.isolate', ], 'sources': [ 'system_wrappers_unittests.isolate', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/system_wrappers_unittests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/system_wrappers_unittests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/system_wrappers_unittests.isolate 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/system_wrappers_unittests.isolate 2015-02-03 14:33:38.000000000 +0000 @@ -9,28 +9,23 @@ { 'conditions': [ ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. 'variables': { - 'isolate_dependency_untracked': [ - '../../../../data/', - '../../../../resources/', + 'files': [ + '<(DEPTH)/data/', + '<(DEPTH)/resources/', ], }, }], ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../../../testing/test_env.py', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/system_wrappers_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_tracked': [ - '../../../testing/test_env.py', + 'files': [ + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/system_wrappers_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '../../../tools/swarming_client/', - ], }, }], ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/thread_posix.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/thread_posix.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/thread_posix.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/thread_posix.cc 2015-02-03 14:33:38.000000000 +0000 @@ -203,21 +203,6 @@ int result = pthread_attr_setdetachstate(&attr_, PTHREAD_CREATE_DETACHED); // Set the stack stack size to 1M. result |= pthread_attr_setstacksize(&attr_, 1024 * 1024); -#if 0 -// Temporarily remove the attempt to set this to real-time scheduling. -// -// See: https://code.google.com/p/webrtc/issues/detail?id=1956 -// -// To be removed when upstream is fixed. -#ifdef WEBRTC_THREAD_RR - const int policy = SCHED_RR; -#else - const int policy = SCHED_FIFO; -#endif -#else - const int policy = SCHED_OTHER; -#endif - event_->Reset(); // If pthread_create was successful, a thread was created and is running. // Don't return false if it was successful since if there are any other @@ -245,26 +230,6 @@ #if HAS_THREAD_ID thread_id = static_cast(thread_); #endif - sched_param param; - - const int min_prio = sched_get_priority_min(policy); - const int max_prio = sched_get_priority_max(policy); - - if ((min_prio == EINVAL) || (max_prio == EINVAL)) { - WEBRTC_TRACE(kTraceError, kTraceUtility, -1, - "unable to retreive min or max priority for threads"); - return true; - } - if (max_prio - min_prio <= 2) { - // There is no room for setting priorities with any granularity. - return true; - } - param.sched_priority = ConvertToSystemPriority(prio_, min_prio, max_prio); - result = pthread_setschedparam(thread_, policy, ¶m); - if (result == EINVAL) { - WEBRTC_TRACE(kTraceError, kTraceUtility, -1, - "unable to set thread priority"); - } return true; } @@ -373,6 +338,27 @@ WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1, "Thread without name started"); } + +#ifdef WEBRTC_THREAD_RR + const int policy = SCHED_RR; +#else + const int policy = SCHED_FIFO; +#endif + const int min_prio = sched_get_priority_min(policy); + const int max_prio = sched_get_priority_max(policy); + if ((min_prio == -1) || (max_prio == -1)) { + WEBRTC_TRACE(kTraceError, kTraceUtility, -1, + "unable to retreive min or max priority for threads"); + } + if (max_prio - min_prio > 2) { + sched_param param; + param.sched_priority = ConvertToSystemPriority(prio_, min_prio, max_prio); + if (pthread_setschedparam(pthread_self(), policy, ¶m) != 0) { + WEBRTC_TRACE( + kTraceError, kTraceUtility, -1, "unable to set thread priority"); + } + } + bool alive = true; bool run = true; while (alive) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/thread_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/thread_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/thread_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/thread_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -12,11 +12,13 @@ #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/system_wrappers/interface/sleep.h" namespace webrtc { // Function that does nothing, and reports success. bool NullRunFunction(void* obj) { + SleepMs(0); // Hand over timeslice, prevents busy looping. return true; } @@ -32,6 +34,7 @@ bool SetFlagRunFunction(void* obj) { bool* obj_as_bool = static_cast(obj); *obj_as_bool = true; + SleepMs(0); // Hand over timeslice, prevents busy looping. return true; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/thread_win.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/thread_win.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/thread_win.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/thread_win.cc 2015-02-03 14:33:38.000000000 +0000 @@ -162,7 +162,7 @@ if (set_thread_name_) { WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, id_, "Thread with name:%s started ", name_); - SetThreadName(-1, name_); // -1, set thread name for the calling thread. + SetThreadName(static_cast(-1), name_); // -1 == caller thread. } else { WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, id_, "Thread without name started"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/timestamp_extrapolator.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/timestamp_extrapolator.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/timestamp_extrapolator.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/timestamp_extrapolator.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/system_wrappers/interface/timestamp_extrapolator.h" + +#include + +namespace webrtc { + +TimestampExtrapolator::TimestampExtrapolator(int64_t start_ms) + : _rwLock(RWLockWrapper::CreateRWLock()), + _startMs(0), + _firstTimestamp(0), + _wrapArounds(0), + _prevUnwrappedTimestamp(-1), + _prevWrapTimestamp(-1), + _lambda(1), + _firstAfterReset(true), + _packetCount(0), + _startUpFilterDelayInPackets(2), + _detectorAccumulatorPos(0), + _detectorAccumulatorNeg(0), + _alarmThreshold(60e3), + _accDrift(6600), // in timestamp ticks, i.e. 15 ms + _accMaxError(7000), + _pP11(1e10) { + Reset(start_ms); +} + +TimestampExtrapolator::~TimestampExtrapolator() +{ + delete _rwLock; +} + +void TimestampExtrapolator::Reset(int64_t start_ms) +{ + WriteLockScoped wl(*_rwLock); + _startMs = start_ms; + _prevMs = _startMs; + _firstTimestamp = 0; + _w[0] = 90.0; + _w[1] = 0; + _pP[0][0] = 1; + _pP[1][1] = _pP11; + _pP[0][1] = _pP[1][0] = 0; + _firstAfterReset = true; + _prevUnwrappedTimestamp = -1; + _prevWrapTimestamp = -1; + _wrapArounds = 0; + _packetCount = 0; + _detectorAccumulatorPos = 0; + _detectorAccumulatorNeg = 0; +} + +void +TimestampExtrapolator::Update(int64_t tMs, uint32_t ts90khz) +{ + + _rwLock->AcquireLockExclusive(); + if (tMs - _prevMs > 10e3) + { + // Ten seconds without a complete frame. + // Reset the extrapolator + _rwLock->ReleaseLockExclusive(); + Reset(tMs); + _rwLock->AcquireLockExclusive(); + } + else + { + _prevMs = tMs; + } + + // Remove offset to prevent badly scaled matrices + tMs -= _startMs; + + CheckForWrapArounds(ts90khz); + + int64_t unwrapped_ts90khz = static_cast(ts90khz) + + _wrapArounds * ((static_cast(1) << 32) - 1); + + if (_prevUnwrappedTimestamp >= 0 && + unwrapped_ts90khz < _prevUnwrappedTimestamp) + { + // Drop reordered frames. + _rwLock->ReleaseLockExclusive(); + return; + } + + if (_firstAfterReset) + { + // Make an initial guess of the offset, + // should be almost correct since tMs - _startMs + // should about zero at this time. + _w[1] = -_w[0] * tMs; + _firstTimestamp = unwrapped_ts90khz; + _firstAfterReset = false; + } + + double residual = + (static_cast(unwrapped_ts90khz) - _firstTimestamp) - + static_cast(tMs) * _w[0] - _w[1]; + if (DelayChangeDetection(residual) && + _packetCount >= _startUpFilterDelayInPackets) + { + // A sudden change of average network delay has been detected. + // Force the filter to adjust its offset parameter by changing + // the offset uncertainty. Don't do this during startup. + _pP[1][1] = _pP11; + } + //T = [t(k) 1]'; + //that = T'*w; + //K = P*T/(lambda + T'*P*T); + double K[2]; + K[0] = _pP[0][0] * tMs + _pP[0][1]; + K[1] = _pP[1][0] * tMs + _pP[1][1]; + double TPT = _lambda + tMs * K[0] + K[1]; + K[0] /= TPT; + K[1] /= TPT; + //w = w + K*(ts(k) - that); + _w[0] = _w[0] + K[0] * residual; + _w[1] = _w[1] + K[1] * residual; + //P = 1/lambda*(P - K*T'*P); + double p00 = 1 / _lambda * + (_pP[0][0] - (K[0] * tMs * _pP[0][0] + K[0] * _pP[1][0])); + double p01 = 1 / _lambda * + (_pP[0][1] - (K[0] * tMs * _pP[0][1] + K[0] * _pP[1][1])); + _pP[1][0] = 1 / _lambda * + (_pP[1][0] - (K[1] * tMs * _pP[0][0] + K[1] * _pP[1][0])); + _pP[1][1] = 1 / _lambda * + (_pP[1][1] - (K[1] * tMs * _pP[0][1] + K[1] * _pP[1][1])); + _pP[0][0] = p00; + _pP[0][1] = p01; + _prevUnwrappedTimestamp = unwrapped_ts90khz; + if (_packetCount < _startUpFilterDelayInPackets) + { + _packetCount++; + } + _rwLock->ReleaseLockExclusive(); +} + +int64_t +TimestampExtrapolator::ExtrapolateLocalTime(uint32_t timestamp90khz) +{ + ReadLockScoped rl(*_rwLock); + int64_t localTimeMs = 0; + CheckForWrapArounds(timestamp90khz); + double unwrapped_ts90khz = static_cast(timestamp90khz) + + _wrapArounds * ((static_cast(1) << 32) - 1); + if (_packetCount == 0) + { + localTimeMs = -1; + } + else if (_packetCount < _startUpFilterDelayInPackets) + { + localTimeMs = _prevMs + static_cast( + static_cast(unwrapped_ts90khz - _prevUnwrappedTimestamp) / + 90.0 + 0.5); + } + else + { + if (_w[0] < 1e-3) + { + localTimeMs = _startMs; + } + else + { + double timestampDiff = unwrapped_ts90khz - + static_cast(_firstTimestamp); + localTimeMs = static_cast( + static_cast(_startMs) + (timestampDiff - _w[1]) / + _w[0] + 0.5); + } + } + return localTimeMs; +} + +// Investigates if the timestamp clock has overflowed since the last timestamp and +// keeps track of the number of wrap arounds since reset. +void +TimestampExtrapolator::CheckForWrapArounds(uint32_t ts90khz) +{ + if (_prevWrapTimestamp == -1) + { + _prevWrapTimestamp = ts90khz; + return; + } + if (ts90khz < _prevWrapTimestamp) + { + // This difference will probably be less than -2^31 if we have had a wrap around + // (e.g. timestamp = 1, _previousTimestamp = 2^32 - 1). Since it is casted to a Word32, + // it should be positive. + if (static_cast(ts90khz - _prevWrapTimestamp) > 0) + { + // Forward wrap around + _wrapArounds++; + } + } + // This difference will probably be less than -2^31 if we have had a backward wrap around. + // Since it is casted to a Word32, it should be positive. + else if (static_cast(_prevWrapTimestamp - ts90khz) > 0) + { + // Backward wrap around + _wrapArounds--; + } + _prevWrapTimestamp = ts90khz; +} + +bool +TimestampExtrapolator::DelayChangeDetection(double error) +{ + // CUSUM detection of sudden delay changes + error = (error > 0) ? std::min(error, _accMaxError) : + std::max(error, -_accMaxError); + _detectorAccumulatorPos = + std::max(_detectorAccumulatorPos + error - _accDrift, (double)0); + _detectorAccumulatorNeg = + std::min(_detectorAccumulatorNeg + error + _accDrift, (double)0); + if (_detectorAccumulatorPos > _alarmThreshold || _detectorAccumulatorNeg < -_alarmThreshold) + { + // Alarm + _detectorAccumulatorPos = _detectorAccumulatorNeg = 0; + return true; + } + return false; +} + +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -288,10 +288,6 @@ sprintf(trace_message, "VIDEO CAPTUR:%5ld %5ld;", id_engine, id_channel); break; - case kTraceVideoPreocessing: - sprintf(trace_message, " VIDEO PROC:%5ld %5ld;", id_engine, - id_channel); - break; case kTraceRemoteBitrateEstimator: sprintf(trace_message, " BWE RBE:%5ld %5ld;", id_engine, id_channel); @@ -352,9 +348,6 @@ case kTraceVideoCapture: sprintf(trace_message, "VIDEO CAPTUR:%11ld;", idl); break; - case kTraceVideoPreocessing: - sprintf(trace_message, " VIDEO PROC:%11ld;", idl); - break; case kTraceRemoteBitrateEstimator: sprintf(trace_message, " BWE RBE:%11ld;", idl); break; @@ -617,15 +610,6 @@ trace_file_.Write(message, length); row_count_text_++; } - length = AddBuildInfo(message); - if (length != -1) { - message[length + 1] = 0; - message[length] = '\n'; - message[length - 1] = '\n'; - trace_file_.Write(message, length + 1); - row_count_text_++; - row_count_text_++; - } } uint16_t length = length_[local_queue_active][idx]; message_queue_[local_queue_active][idx][length] = 0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_impl.h 2015-02-03 14:33:38.000000000 +0000 @@ -30,7 +30,7 @@ #define WEBRTC_TRACE_MAX_QUEUE 16000 #endif #define WEBRTC_TRACE_NUM_ARRAY 2 -#define WEBRTC_TRACE_MAX_MESSAGE_SIZE 256 +#define WEBRTC_TRACE_MAX_MESSAGE_SIZE 1024 // Total buffer size is WEBRTC_TRACE_NUM_ARRAY (number of buffer partitions) * // WEBRTC_TRACE_MAX_QUEUE (number of lines per buffer partition) * // WEBRTC_TRACE_MAX_MESSAGE_SIZE (number of 1 byte charachters per line) = @@ -73,7 +73,6 @@ virtual int32_t AddTime(char* trace_message, const TraceLevel level) const = 0; - virtual int32_t AddBuildInfo(char* trace_message) const = 0; virtual int32_t AddDateTimeInfo(char* trace_message) const = 0; static bool Run(void* obj); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_posix.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_posix.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_posix.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_posix.cc 2015-02-03 14:33:38.000000000 +0000 @@ -17,20 +17,6 @@ #include #include -#if defined(_DEBUG) -#define BUILDMODE "d" -#elif defined(DEBUG) -#define BUILDMODE "d" -#elif defined(NDEBUG) -#define BUILDMODE "r" -#else -#define BUILDMODE "?" -#endif -#define BUILDTIME __TIME__ -#define BUILDDATE __DATE__ -// example: "Oct 10 2002 12:05:30 r" -#define BUILDINFO BUILDDATE " " BUILDTIME " " BUILDMODE - namespace webrtc { TracePosix::TracePosix() @@ -86,12 +72,6 @@ return 22; } -int32_t TracePosix::AddBuildInfo(char* trace_message) const { - sprintf(trace_message, "Build info: %s", BUILDINFO); - // Include NULL termination (hence + 1). - return strlen(trace_message) + 1; -} - int32_t TracePosix::AddDateTimeInfo(char* trace_message) const { time_t t; time(&t); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_posix.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_posix.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_posix.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_posix.h 2015-02-03 14:33:38.000000000 +0000 @@ -26,7 +26,6 @@ virtual int32_t AddTime(char* trace_message, const TraceLevel level) const OVERRIDE; - virtual int32_t AddBuildInfo(char* trace_message) const OVERRIDE; virtual int32_t AddDateTimeInfo(char* trace_message) const OVERRIDE; private: diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_win.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_win.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_win.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_win.cc 2015-02-03 14:33:38.000000000 +0000 @@ -15,20 +15,6 @@ #include "Mmsystem.h" -#if defined(_DEBUG) -#define BUILDMODE "d" -#elif defined(DEBUG) -#define BUILDMODE "d" -#elif defined(NDEBUG) -#define BUILDMODE "r" -#else -#define BUILDMODE "?" -#endif -#define BUILDTIME __TIME__ -#define BUILDDATE __DATE__ -// Example: "Oct 10 2002 12:05:30 r" -#define BUILDINFO BUILDDATE " " BUILDTIME " " BUILDMODE - namespace webrtc { TraceWindows::TraceWindows() : prev_api_tick_count_(0), @@ -60,7 +46,7 @@ dw_delta_time = 99999; } - sprintf(trace_message, "(%2u:%2u:%2u:%3u |%5lu) ", system_time.wHour, + sprintf(trace_message, "(%2u:%2u:%2u:%3u |%5u) ", system_time.wHour, system_time.wMinute, system_time.wSecond, system_time.wMilliseconds, dw_delta_time); } else { @@ -77,20 +63,13 @@ if (dw_delta_time > 99999) { dw_delta_time = 99999; } - sprintf(trace_message, "(%2u:%2u:%2u:%3u |%5lu) ", system_time.wHour, + sprintf(trace_message, "(%2u:%2u:%2u:%3u |%5u) ", system_time.wHour, system_time.wMinute, system_time.wSecond, system_time.wMilliseconds, dw_delta_time); } return 22; } -int32_t TraceWindows::AddBuildInfo(char* trace_message) const { - // write data and time to text file - sprintf(trace_message, "Build info: %s", BUILDINFO); - // Include NULL termination (hence + 1). - return static_cast(strlen(trace_message) + 1); -} - int32_t TraceWindows::AddDateTimeInfo(char* trace_message) const { prev_api_tick_count_ = timeGetTime(); prev_tick_count_ = prev_api_tick_count_; @@ -109,7 +88,7 @@ GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, &sys_time, TEXT("HH':'mm':'ss"), sz_time_str, 20); - sprintf(trace_message, "Local Date: %s Local Time: %s", sz_date_str, + sprintf(trace_message, "Local Date: %ls Local Time: %ls", sz_date_str, sz_time_str); // Include NULL termination (hence + 1). diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_win.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_win.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_win.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/trace_win.h 2015-02-03 14:33:38.000000000 +0000 @@ -25,7 +25,6 @@ virtual int32_t AddTime(char* trace_message, const TraceLevel level) const; - virtual int32_t AddBuildInfo(char* trace_message) const; virtual int32_t AddDateTimeInfo(char* trace_message) const; private: volatile mutable uint32_t prev_api_tick_count_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/unittest_utilities.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/unittest_utilities.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/unittest_utilities.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/unittest_utilities.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_UNITTEST_UTILITIES_H_ -#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_UNITTEST_UTILITIES_H_ - -// This file contains utilities that make it simpler to write unittests -// that are appropriate for the system_wrappers classes. - -#include -#include - -#include "webrtc/system_wrappers/interface/trace.h" - -namespace webrtc { - -class TestTraceCallback : public TraceCallback { - public: - virtual void Print(TraceLevel level, const char* msg, int length) { - if (msg) { - char* cmd_print = new char[length+1]; - memcpy(cmd_print, msg, length); - cmd_print[length] = '\0'; - printf("%s\n", cmd_print); - fflush(stdout); - delete[] cmd_print; - } - } -}; - -// A class that turns on tracing to stdout at the beginning of the test, -// and turns it off once the test is finished. -// Intended usage: -// class SomeTest : public ::testing::Test { -// protected: -// SomeTest() -// : trace_(false) {} // Change to true to turn on tracing. -// private: -// ScopedTracing trace_; -// } -class ScopedTracing { - public: - explicit ScopedTracing(bool logOn) { - logging_ = logOn; - StartTrace(); - } - - ~ScopedTracing() { - StopTrace(); - } - - private: - void StartTrace() { - if (logging_) { - Trace::CreateTrace(); - Trace::set_level_filter(webrtc::kTraceAll); - Trace::SetTraceCallback(&trace_); - } - } - - void StopTrace() { - if (logging_) { - Trace::SetTraceCallback(NULL); - Trace::ReturnTrace(); - } - } - - private: - bool logging_; - TestTraceCallback trace_; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_UNITTEST_UTILITIES_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/unittest_utilities_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/unittest_utilities_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/unittest_utilities_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/system_wrappers/source/unittest_utilities_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/system_wrappers/source/unittest_utilities.h" - -#include "testing/gtest/include/gtest/gtest.h" -#include "webrtc/system_wrappers/interface/trace.h" - -namespace webrtc { - -// These tests merely check that the code compiles and that no -// fatal accidents happen when logging. -TEST(UnittestUtilities, TraceOn) { - ScopedTracing trace(true); - WEBRTC_TRACE(kTraceInfo, kTraceUtility, 0, "Log line that should appear"); - // TODO(hta): Verify that output appears. - // Note - output is written on another thread, so can take time to appear. -} - -TEST(UnittestUtilities, TraceOff) { - ScopedTracing trace(false); - WEBRTC_TRACE(kTraceInfo, kTraceUtility, 0, - "Log line that should not appear"); - // TODO(hta): Verify that no output appears. -} - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/Android.mk 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ - -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -include $(LOCAL_PATH)/../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE:= libwebrtc_test_support -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES:= \ - testsupport/android/root_path_android.cc \ - testsupport/fileutils.cc \ - testsupport/perf_test.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - external/gtest/include \ - external/webrtc \ - external/webrtc/webrtc - -LOCAL_STATIC_LIBRARIES := \ - libgtest - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/buildbot_tests.py thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/buildbot_tests.py --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/buildbot_tests.py 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/buildbot_tests.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,141 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - - -"""Script to run tests with pre-configured command line arguments. - -NOTICE: This test is designed to be run from the build output folder! It is -copied automatically during build. - -With this script, it's easier for anyone to enable/disable or extend a test that -runs on the buildbots. It is also easier for developers to run the tests in the -same way as they run on the bots. -""" - -import optparse -import os -import subprocess -import sys - -_CURRENT_DIR = os.path.abspath(os.path.dirname(__file__)) -_HOME = os.environ.get('HOME', '') - -_VIE_AUTO_TEST_CMD_LIST = [ - 'vie_auto_test', - '--automated', - '--gtest_filter=-ViERtpFuzzTest*', - '--capture_test_ensure_resolution_alignment_in_capture_device=false'] -_WIN_TESTS = { - 'vie_auto_test': _VIE_AUTO_TEST_CMD_LIST, - 'voe_auto_test': ['voe_auto_test', - '--automated'], -} -_MAC_TESTS = { - 'libjingle_peerconnection_objc_test': [ - ('libjingle_peerconnection_objc_test.app/Contents/MacOS/' - 'libjingle_peerconnection_objc_test')], - 'vie_auto_test': _VIE_AUTO_TEST_CMD_LIST, - 'voe_auto_test': ['voe_auto_test', - '--automated', - ('--gtest_filter=' - '-VolumeTest.SetVolumeBeforePlayoutWorks' # bug 527 - )], -} -_LINUX_TESTS = { - 'vie_auto_test': _VIE_AUTO_TEST_CMD_LIST, - 'voe_auto_test': ['voe_auto_test', - '--automated', - '--gtest_filter=-RtpFuzzTest.*'], - 'audio_e2e_test': ['python', - 'run_audio_test.py', - '--input=../../resources/e2e_audio_in.pcm', - '--output=/tmp/e2e_audio_out.pcm', - '--codec=L16', - '--harness=%s/audio_e2e_harness' % _CURRENT_DIR, - '--compare=%s/bin/compare-audio +16000 +wb' % _HOME, - '--regexp=(\d\.\d{3})'], - 'audioproc_perf': ['audioproc', - '-aecm', '-ns', '-agc', '--fixed_digital', '--perf', - '-pb', '../../resources/audioproc.aecdump'], - 'isac_fixed_perf': ['iSACFixtest', - '32000', '../../resources/speech_and_misc_wb.pcm', - 'isac_speech_and_misc_wb.pcm'], - 'libjingle_peerconnection_java_unittest': [ - 'libjingle_peerconnection_java_unittest'], -} - -_CUSTOM_ENV = { - 'libjingle_peerconnection_java_unittest': - {'LD_PRELOAD': '/usr/lib/x86_64-linux-gnu/libpulse.so.0'}, -} - -def main(): - parser = optparse.OptionParser('usage: %prog -t [-t ...]\n' - 'If no test is specified, all tests are run.') - parser.add_option('-l', '--list', action='store_true', default=False, - help='Lists all currently supported tests.') - parser.add_option('-t', '--test', action='append', default=[], - help='Which test to run. May be specified multiple times.') - options, _ = parser.parse_args() - - if sys.platform.startswith('win'): - test_dict = _WIN_TESTS - elif sys.platform.startswith('linux'): - test_dict = _LINUX_TESTS - elif sys.platform.startswith('darwin'): - test_dict = _MAC_TESTS - else: - parser.error('Unsupported platform: %s' % sys.platform) - - if options.list: - print 'Available tests:' - print 'Test name Command line' - print '========= ============' - for test, cmd_line in test_dict.items(): - print '%-20s %s' % (test, ' '.join(cmd_line)) - return - - if not options.test: - options.test = test_dict.keys() - for test in options.test: - if test not in test_dict: - parser.error('Test "%s" is not supported (use --list to view supported ' - 'tests).') - - # Change current working directory to the script's dir to make the relative - # paths always work. - os.chdir(_CURRENT_DIR) - print 'Changed working directory to: %s' % _CURRENT_DIR - - print 'Running WebRTC Buildbot tests: %s' % options.test - for test in options.test: - cmd_line = test_dict[test] - env = os.environ.copy() - if test in _CUSTOM_ENV: - env.update(_CUSTOM_ENV[test]) - - # Create absolute paths to test executables for non-Python tests. - if cmd_line[0] != 'python': - cmd_line[0] = os.path.join(_CURRENT_DIR, cmd_line[0]) - - print 'Running: %s' % ' '.join(cmd_line) - try: - subprocess.check_call(cmd_line, env=env) - except subprocess.CalledProcessError as e: - print >> sys.stderr, ('An error occurred during test execution: return ' - 'code: %d' % e.returncode) - return -1 - - print 'Testing completed successfully.' - return 0 - - -if __name__ == '__main__': - sys.exit(main()) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/BUILD.gn 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,109 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +# TODO(kjellander): Convert the rest of the test.gyp targets and put here. + +source_set("test") { + testonly = true + + deps = [ + ":field_trial", + ":test_support", + ":test_support_main", + ] +} + +source_set("field_trial") { + sources = [ + "field_trial.cc", + "field_trial.h", + ] + + deps = [ + "../system_wrappers", + ] + + configs += [ "..:common_config" ] + public_configs = [ "..:common_inherited_config"] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } +} + +source_set("test_support") { + testonly = true + + sources = [ + "testsupport/fileutils.cc", + "testsupport/fileutils.h", + "testsupport/frame_reader.cc", + "testsupport/frame_reader.h", + "testsupport/frame_writer.cc", + "testsupport/frame_writer.h", + "testsupport/gtest_prod_util.h", + "testsupport/gtest_disable.h", + "testsupport/mock/mock_frame_reader.h", + "testsupport/mock/mock_frame_writer.h", + "testsupport/packet_reader.cc", + "testsupport/packet_reader.h", + "testsupport/perf_test.cc", + "testsupport/perf_test.h", + "testsupport/trace_to_stderr.cc", + "testsupport/trace_to_stderr.h", + ] + + deps = [ + "//testing/gmock", + "//testing/gtest", + "../system_wrappers", + ] + + if (is_android) { + deps += [ "//base:base" ] + } + + configs += [ "..:common_config" ] + public_configs = [ "..:common_inherited_config"] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } +} + +source_set("test_support_main") { + testonly = true + + sources = [ + "run_all_unittests.cc", + "test_suite.cc", + "test_suite.h", + ] + + deps = [ + ":field_trial", + ":test_support", + "../system_wrappers:metrics_default", + "//testing/gmock", + "//testing/gtest", + "//third_party/gflags", + ] + + configs += [ "..:common_config" ] + public_configs = [ "..:common_inherited_config"] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/call_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/call_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/call_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/call_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "webrtc/test/call_test.h" + +#include "webrtc/test/encoder_settings.h" + +namespace webrtc { +namespace test { + +CallTest::CallTest() + : clock_(Clock::GetRealTimeClock()), + send_stream_(NULL), + fake_encoder_(clock_) { +} + +CallTest::~CallTest() { +} + +void CallTest::RunBaseTest(BaseTest* test) { + CreateSenderCall(test->GetSenderCallConfig()); + if (test->ShouldCreateReceivers()) + CreateReceiverCall(test->GetReceiverCallConfig()); + test->OnCallsCreated(sender_call_.get(), receiver_call_.get()); + + if (test->ShouldCreateReceivers()) { + test->SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver()); + } else { + // Sender-only call delivers to itself. + test->SetReceivers(sender_call_->Receiver(), NULL); + } + + CreateSendConfig(test->GetNumStreams()); + if (test->ShouldCreateReceivers()) { + CreateMatchingReceiveConfigs(); + } + test->ModifyConfigs(&send_config_, &receive_configs_, &encoder_config_); + CreateStreams(); + test->OnStreamsCreated(send_stream_, receive_streams_); + + CreateFrameGeneratorCapturer(); + test->OnFrameGeneratorCapturerCreated(frame_generator_capturer_.get()); + + Start(); + test->PerformTest(); + test->StopSending(); + Stop(); + + DestroyStreams(); +} + +void CallTest::Start() { + send_stream_->Start(); + for (size_t i = 0; i < receive_streams_.size(); ++i) + receive_streams_[i]->Start(); + if (frame_generator_capturer_.get() != NULL) + frame_generator_capturer_->Start(); +} + +void CallTest::Stop() { + if (frame_generator_capturer_.get() != NULL) + frame_generator_capturer_->Stop(); + for (size_t i = 0; i < receive_streams_.size(); ++i) + receive_streams_[i]->Stop(); + send_stream_->Stop(); +} + +void CallTest::CreateCalls(const Call::Config& sender_config, + const Call::Config& receiver_config) { + CreateSenderCall(sender_config); + CreateReceiverCall(receiver_config); +} + +void CallTest::CreateSenderCall(const Call::Config& config) { + sender_call_.reset(Call::Create(config)); +} + +void CallTest::CreateReceiverCall(const Call::Config& config) { + receiver_call_.reset(Call::Create(config)); +} + +void CallTest::CreateSendConfig(size_t num_streams) { + assert(num_streams <= kNumSsrcs); + send_config_ = VideoSendStream::Config(); + send_config_.encoder_settings.encoder = &fake_encoder_; + send_config_.encoder_settings.payload_name = "FAKE"; + send_config_.encoder_settings.payload_type = kFakeSendPayloadType; + encoder_config_.streams = test::CreateVideoStreams(num_streams); + for (size_t i = 0; i < num_streams; ++i) + send_config_.rtp.ssrcs.push_back(kSendSsrcs[i]); +} + +void CallTest::CreateMatchingReceiveConfigs() { + assert(!send_config_.rtp.ssrcs.empty()); + assert(receive_configs_.empty()); + assert(allocated_decoders_.empty()); + VideoReceiveStream::Config config; + config.rtp.local_ssrc = kReceiverLocalSsrc; + for (size_t i = 0; i < send_config_.rtp.ssrcs.size(); ++i) { + VideoReceiveStream::Decoder decoder = + test::CreateMatchingDecoder(send_config_.encoder_settings); + allocated_decoders_.push_back(decoder.decoder); + config.decoders.clear(); + config.decoders.push_back(decoder); + config.rtp.remote_ssrc = send_config_.rtp.ssrcs[i]; + receive_configs_.push_back(config); + } +} + +void CallTest::CreateFrameGeneratorCapturer() { + VideoStream stream = encoder_config_.streams.back(); + frame_generator_capturer_.reset( + test::FrameGeneratorCapturer::Create(send_stream_->Input(), + stream.width, + stream.height, + stream.max_framerate, + clock_)); +} +void CallTest::CreateStreams() { + assert(send_stream_ == NULL); + assert(receive_streams_.empty()); + + send_stream_ = + sender_call_->CreateVideoSendStream(send_config_, encoder_config_); + + for (size_t i = 0; i < receive_configs_.size(); ++i) { + receive_streams_.push_back( + receiver_call_->CreateVideoReceiveStream(receive_configs_[i])); + } +} + +void CallTest::DestroyStreams() { + if (send_stream_ != NULL) + sender_call_->DestroyVideoSendStream(send_stream_); + send_stream_ = NULL; + for (size_t i = 0; i < receive_streams_.size(); ++i) + receiver_call_->DestroyVideoReceiveStream(receive_streams_[i]); + receive_streams_.clear(); + allocated_decoders_.clear(); +} + +const unsigned int CallTest::kDefaultTimeoutMs = 30 * 1000; +const unsigned int CallTest::kLongTimeoutMs = 120 * 1000; +const uint8_t CallTest::kSendPayloadType = 100; +const uint8_t CallTest::kFakeSendPayloadType = 125; +const uint8_t CallTest::kSendRtxPayloadType = 98; +const uint8_t CallTest::kRedPayloadType = 118; +const uint8_t CallTest::kUlpfecPayloadType = 119; +const uint32_t CallTest::kSendRtxSsrcs[kNumSsrcs] = {0xBADCAFD, 0xBADCAFE, + 0xBADCAFF}; +const uint32_t CallTest::kSendSsrcs[kNumSsrcs] = {0xC0FFED, 0xC0FFEE, 0xC0FFEF}; +const uint32_t CallTest::kReceiverLocalSsrc = 0x123456; +const int CallTest::kNackRtpHistoryMs = 1000; + +BaseTest::BaseTest(unsigned int timeout_ms) : RtpRtcpObserver(timeout_ms) { +} + +BaseTest::BaseTest(unsigned int timeout_ms, + const FakeNetworkPipe::Config& config) + : RtpRtcpObserver(timeout_ms, config) { +} + +BaseTest::~BaseTest() { +} + +Call::Config BaseTest::GetSenderCallConfig() { + return Call::Config(SendTransport()); +} + +Call::Config BaseTest::GetReceiverCallConfig() { + return Call::Config(ReceiveTransport()); +} + +void BaseTest::OnCallsCreated(Call* sender_call, Call* receiver_call) { +} + +size_t BaseTest::GetNumStreams() const { + return 1; +} + +void BaseTest::ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) { +} + +void BaseTest::OnStreamsCreated( + VideoSendStream* send_stream, + const std::vector& receive_streams) { +} + +void BaseTest::OnFrameGeneratorCapturerCreated( + FrameGeneratorCapturer* frame_generator_capturer) { +} + +SendTest::SendTest(unsigned int timeout_ms) : BaseTest(timeout_ms) { +} + +SendTest::SendTest(unsigned int timeout_ms, + const FakeNetworkPipe::Config& config) + : BaseTest(timeout_ms, config) { +} + +bool SendTest::ShouldCreateReceivers() const { + return false; +} + +EndToEndTest::EndToEndTest(unsigned int timeout_ms) : BaseTest(timeout_ms) { +} + +EndToEndTest::EndToEndTest(unsigned int timeout_ms, + const FakeNetworkPipe::Config& config) + : BaseTest(timeout_ms, config) { +} + +bool EndToEndTest::ShouldCreateReceivers() const { + return true; +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/call_test.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/call_test.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/call_test.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/call_test.h 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef WEBRTC_TEST_COMMON_CALL_TEST_H_ +#define WEBRTC_TEST_COMMON_CALL_TEST_H_ + +#include + +#include "webrtc/call.h" +#include "webrtc/system_wrappers/interface/scoped_vector.h" +#include "webrtc/test/fake_decoder.h" +#include "webrtc/test/fake_encoder.h" +#include "webrtc/test/frame_generator_capturer.h" +#include "webrtc/test/rtp_rtcp_observer.h" + +namespace webrtc { +namespace test { + +class BaseTest; + +class CallTest : public ::testing::Test { + public: + CallTest(); + ~CallTest(); + + static const size_t kNumSsrcs = 3; + + static const unsigned int kDefaultTimeoutMs; + static const unsigned int kLongTimeoutMs; + static const uint8_t kSendPayloadType; + static const uint8_t kSendRtxPayloadType; + static const uint8_t kFakeSendPayloadType; + static const uint8_t kRedPayloadType; + static const uint8_t kUlpfecPayloadType; + static const uint32_t kSendRtxSsrcs[kNumSsrcs]; + static const uint32_t kSendSsrcs[kNumSsrcs]; + static const uint32_t kReceiverLocalSsrc; + static const int kNackRtpHistoryMs; + + protected: + void RunBaseTest(BaseTest* test); + + void CreateCalls(const Call::Config& sender_config, + const Call::Config& receiver_config); + void CreateSenderCall(const Call::Config& config); + void CreateReceiverCall(const Call::Config& config); + + void CreateSendConfig(size_t num_streams); + void CreateMatchingReceiveConfigs(); + + void CreateFrameGeneratorCapturer(); + + void CreateStreams(); + void Start(); + void Stop(); + void DestroyStreams(); + + Clock* const clock_; + + scoped_ptr sender_call_; + VideoSendStream::Config send_config_; + VideoEncoderConfig encoder_config_; + VideoSendStream* send_stream_; + + scoped_ptr receiver_call_; + std::vector receive_configs_; + std::vector receive_streams_; + + scoped_ptr frame_generator_capturer_; + test::FakeEncoder fake_encoder_; + ScopedVector allocated_decoders_; +}; + +class BaseTest : public RtpRtcpObserver { + public: + explicit BaseTest(unsigned int timeout_ms); + BaseTest(unsigned int timeout_ms, const FakeNetworkPipe::Config& config); + virtual ~BaseTest(); + + virtual void PerformTest() = 0; + virtual bool ShouldCreateReceivers() const = 0; + + virtual size_t GetNumStreams() const; + + virtual Call::Config GetSenderCallConfig(); + virtual Call::Config GetReceiverCallConfig(); + virtual void OnCallsCreated(Call* sender_call, Call* receiver_call); + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config); + virtual void OnStreamsCreated( + VideoSendStream* send_stream, + const std::vector& receive_streams); + + virtual void OnFrameGeneratorCapturerCreated( + FrameGeneratorCapturer* frame_generator_capturer); +}; + +class SendTest : public BaseTest { + public: + explicit SendTest(unsigned int timeout_ms); + SendTest(unsigned int timeout_ms, const FakeNetworkPipe::Config& config); + + virtual bool ShouldCreateReceivers() const OVERRIDE; +}; + +class EndToEndTest : public BaseTest { + public: + explicit EndToEndTest(unsigned int timeout_ms); + EndToEndTest(unsigned int timeout_ms, const FakeNetworkPipe::Config& config); + + virtual bool ShouldCreateReceivers() const OVERRIDE; +}; + +} // namespace test +} // namespace webrtc + +#endif // WEBRTC_TEST_COMMON_CALL_TEST_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/channel_transport.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/channel_transport.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/channel_transport.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/channel_transport.cc 2015-02-03 14:33:38.000000000 +0000 @@ -53,7 +53,8 @@ const int32_t packet_length, const char* /*from_ip*/, const uint16_t /*from_port*/) { - voe_network_->ReceivedRTPPacket(channel_, incoming_rtp_packet, packet_length); + voe_network_->ReceivedRTPPacket( + channel_, incoming_rtp_packet, packet_length, PacketTime()); } void VoiceChannelTransport::IncomingRTCPPacket( diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/OWNERS 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/OWNERS 2015-02-03 14:33:38.000000000 +0000 @@ -1,4 +1,3 @@ -pwestin@webrtc.org henrikg@webrtc.org mallinath@webrtc.org tomasl@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_socket2_win.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_socket2_win.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_socket2_win.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_socket2_win.cc 2015-02-03 14:33:38.000000000 +0000 @@ -426,7 +426,8 @@ { return len; } - if((error = _mgr->PushIoContext(pIoContext))) + error = _mgr->PushIoContext(pIoContext); + if(error) { WEBRTC_TRACE( kTraceError, @@ -493,8 +494,8 @@ { assert(false); } - int32_t err = 0; - if((err = _mgr->PushIoContext(pIOContext))) + int32_t err = _mgr->PushIoContext(pIOContext); + if(err) { WEBRTC_TRACE( kTraceError, @@ -648,8 +649,8 @@ { assert(false); } - int32_t error = 0; - if((error = _mgr->PushIoContext(pIoContext))) + int32_t error = _mgr->PushIoContext(pIoContext); + if(error) { WEBRTC_TRACE( kTraceError, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_socket2_win.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_socket2_win.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_socket2_win.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_socket2_win.h 2015-02-03 14:33:38.000000000 +0000 @@ -42,36 +42,37 @@ bool ipV6Enable = false, bool disableGQOS = false); virtual ~UdpSocket2Windows(); - virtual int32_t ChangeUniqueId(const int32_t id); + virtual int32_t ChangeUniqueId(const int32_t id) OVERRIDE; - virtual bool ValidHandle(); + virtual bool ValidHandle() OVERRIDE; - virtual bool SetCallback(CallbackObj, IncomingSocketCallback); + virtual bool SetCallback(CallbackObj, IncomingSocketCallback) OVERRIDE; - virtual bool Bind(const SocketAddress& name); + virtual bool Bind(const SocketAddress& name) OVERRIDE; virtual bool SetSockopt(int32_t level, int32_t optname, - const int8_t* optval, int32_t optlen); + const int8_t* optval, int32_t optlen) OVERRIDE; - virtual bool StartReceiving(const uint32_t receiveBuffers); - virtual inline bool StartReceiving() {return StartReceiving(8);} - virtual bool StopReceiving(); + virtual bool StartReceiving(const uint32_t receiveBuffers) OVERRIDE; + virtual inline bool StartReceiving() OVERRIDE {return StartReceiving(8);} + virtual bool StopReceiving() OVERRIDE; virtual int32_t SendTo(const int8_t* buf, int32_t len, - const SocketAddress& to); + const SocketAddress& to) OVERRIDE; - virtual void CloseBlocking(); + virtual void CloseBlocking() OVERRIDE; + + SOCKET GetFd() { return _socket;} - virtual SOCKET GetFd() { return _socket;} virtual bool SetQos(int32_t serviceType, int32_t tokenRate, int32_t bucketSize, int32_t peekBandwith, int32_t minPolicedSize, int32_t maxSduSize, const SocketAddress &stRemName, - int32_t overrideDSCP = 0); + int32_t overrideDSCP = 0) OVERRIDE; - virtual int32_t SetTOS(const int32_t serviceType); - virtual int32_t SetPCP(const int32_t pcp); + virtual int32_t SetTOS(const int32_t serviceType) OVERRIDE; + virtual int32_t SetPCP(const int32_t pcp) OVERRIDE; - virtual uint32_t ReceiveBuffers(){return _receiveBuffers.Value();} + virtual uint32_t ReceiveBuffers() OVERRIDE {return _receiveBuffers.Value();} protected: void IOCompleted(PerIoContext* pIOContext, uint32_t ioSize, uint32_t error); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_socket_posix.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_socket_posix.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_socket_posix.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_socket_posix.cc 2015-02-03 14:33:38.000000000 +0000 @@ -33,7 +33,6 @@ "UdpSocketPosix::UdpSocketPosix()"); _wantsIncoming = false; - _error = 0; _mgr = mgr; _id = id; @@ -129,9 +128,8 @@ return true; } - _error = errno; WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocketPosix::SetSockopt(), error:%d", _error); + "UdpSocketPosix::SetSockopt(), error:%d", errno); return false; } @@ -151,9 +149,8 @@ { return true; } - _error = errno; WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocketPosix::Bind() error: %d",_error); + "UdpSocketPosix::Bind() error: %d", errno); return false; } @@ -165,16 +162,14 @@ reinterpret_cast(&to), size); if(retVal == SOCKET_ERROR) { - _error = errno; WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocketPosix::SendTo() error: %d", _error); + "UdpSocketPosix::SendTo() error: %d", errno); } return retVal; } SOCKET UdpSocketPosix::GetFd() { return _socket; } -int32_t UdpSocketPosix::GetError() { return _error; } bool UdpSocketPosix::ValidHandle() { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_socket_posix.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_socket_posix.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_socket_posix.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_socket_posix.h 2015-02-03 14:33:38.000000000 +0000 @@ -52,8 +52,7 @@ // TODO (hellner): make destructor protected. virtual void CloseBlocking() OVERRIDE; - virtual SOCKET GetFd(); - virtual int32_t GetError(); + SOCKET GetFd(); virtual bool ValidHandle() OVERRIDE; @@ -76,7 +75,6 @@ int32_t _id; IncomingSocketCallback _incomingCb; CallbackObj _obj; - int32_t _error; SOCKET _socket; UdpSocketManager* _mgr; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_transport_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_transport_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_transport_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/channel_transport/udp_transport_impl.h 2015-02-03 14:33:38.000000000 +0000 @@ -60,8 +60,8 @@ virtual int32_t InitializeSourcePorts( const uint16_t rtpPort, const uint16_t rtcpPort = 0) OVERRIDE; - virtual int32_t SourcePorts(uint16_t& rtpPort, uint16_t& rtcpPort) const - OVERRIDE; + virtual int32_t SourcePorts(uint16_t& rtpPort, + uint16_t& rtcpPort) const OVERRIDE; virtual int32_t ReceiveSocketInformation( char ipAddr[kIpAddressVersion6Length], uint16_t& rtpPort, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/configurable_frame_size_encoder.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/configurable_frame_size_encoder.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/configurable_frame_size_encoder.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/configurable_frame_size_encoder.cc 2015-02-03 14:33:38.000000000 +0000 @@ -12,9 +12,11 @@ #include -#include "webrtc/common_video/interface/video_image.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/common_video/interface/video_image.h" +#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h" + namespace webrtc { namespace test { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/configurable_frame_size_encoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/configurable_frame_size_encoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/configurable_frame_size_encoder.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/configurable_frame_size_encoder.h 2015-02-03 14:33:38.000000000 +0000 @@ -14,7 +14,7 @@ #include #include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h" +#include "webrtc/video_encoder.h" namespace webrtc { namespace test { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/direct_transport.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/direct_transport.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/direct_transport.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/direct_transport.cc 2015-02-03 14:33:38.000000000 +0000 @@ -42,6 +42,10 @@ DirectTransport::~DirectTransport() { StopSending(); } +void DirectTransport::SetConfig(const FakeNetworkPipe::Config& config) { + fake_network_.SetConfig(config); +} + void DirectTransport::StopSending() { { CriticalSectionScoped crit_(lock_.get()); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/direct_transport.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/direct_transport.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/direct_transport.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/direct_transport.h 2015-02-03 14:33:38.000000000 +0000 @@ -34,6 +34,8 @@ explicit DirectTransport(const FakeNetworkPipe::Config& config); ~DirectTransport(); + void SetConfig(const FakeNetworkPipe::Config& config); + virtual void StopSending(); virtual void SetReceiver(PacketReceiver* receiver); @@ -47,7 +49,7 @@ scoped_ptr lock_; scoped_ptr packet_event_; scoped_ptr thread_; - Clock* clock_; + Clock* const clock_; bool shutting_down_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/encoder_settings.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/encoder_settings.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/encoder_settings.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/encoder_settings.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "webrtc/test/encoder_settings.h" + +#include +#include + +#include "webrtc/test/fake_decoder.h" +#include "webrtc/video_decoder.h" + +namespace webrtc { +namespace test { +std::vector CreateVideoStreams(size_t num_streams) { + assert(num_streams > 0); + + // Add more streams to the settings above with reasonable values if required. + static const size_t kNumSettings = 3; + assert(num_streams <= kNumSettings); + + std::vector stream_settings(kNumSettings); + + stream_settings[0].width = 320; + stream_settings[0].height = 180; + stream_settings[0].max_framerate = 30; + stream_settings[0].min_bitrate_bps = 50000; + stream_settings[0].target_bitrate_bps = stream_settings[0].max_bitrate_bps = + 150000; + stream_settings[0].max_qp = 56; + + stream_settings[1].width = 640; + stream_settings[1].height = 360; + stream_settings[1].max_framerate = 30; + stream_settings[1].min_bitrate_bps = 200000; + stream_settings[1].target_bitrate_bps = stream_settings[1].max_bitrate_bps = + 450000; + stream_settings[1].max_qp = 56; + + stream_settings[2].width = 1280; + stream_settings[2].height = 720; + stream_settings[2].max_framerate = 30; + stream_settings[2].min_bitrate_bps = 700000; + stream_settings[2].target_bitrate_bps = stream_settings[2].max_bitrate_bps = + 1500000; + stream_settings[2].max_qp = 56; + stream_settings.resize(num_streams); + return stream_settings; +} + +VideoReceiveStream::Decoder CreateMatchingDecoder( + const VideoSendStream::Config::EncoderSettings& encoder_settings) { + VideoReceiveStream::Decoder decoder; + decoder.payload_type = encoder_settings.payload_type; + decoder.payload_name = encoder_settings.payload_name; + if (encoder_settings.payload_name == "VP8") { + decoder.decoder = VideoDecoder::Create(VideoDecoder::kVp8); + } else if (encoder_settings.payload_name == "VP9") { + decoder.decoder = VideoDecoder::Create(VideoDecoder::kVp9); + } else { + decoder.decoder = new FakeDecoder(); + } + return decoder; +} +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/encoder_settings.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/encoder_settings.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/encoder_settings.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/encoder_settings.h 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef WEBRTC_TEST_ENCODER_SETTINGS_H_ +#define WEBRTC_TEST_ENCODER_SETTINGS_H_ + +#include "webrtc/video_receive_stream.h" +#include "webrtc/video_send_stream.h" + +namespace webrtc { +namespace test { +std::vector CreateVideoStreams(size_t num_streams); + +VideoReceiveStream::Decoder CreateMatchingDecoder( + const VideoSendStream::Config::EncoderSettings& encoder_settings); +} // namespace test +} // namespace webrtc + +#endif // WEBRTC_TEST_ENCODER_SETTINGS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/fake_audio_device.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/fake_audio_device.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/fake_audio_device.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/fake_audio_device.cc 2015-02-03 14:33:38.000000000 +0000 @@ -121,13 +121,17 @@ samples_needed = std::min(kFrequencyHz / time_since_last_playout_ms, kBufferSizeBytes / 2); uint32_t samples_out = 0; + int64_t elapsed_time_ms = -1; + int64_t ntp_time_ms = -1; EXPECT_EQ(0, audio_callback_->NeedMorePlayData(samples_needed, 2, 1, kFrequencyHz, playout_buffer_, - samples_out)); + samples_out, + &elapsed_time_ms, + &ntp_time_ms)); } } tick_->Wait(WEBRTC_EVENT_INFINITE); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/fake_decoder.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/fake_decoder.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/fake_decoder.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/fake_decoder.cc 2015-02-03 14:33:38.000000000 +0000 @@ -36,6 +36,7 @@ const CodecSpecificInfo* codec_specific_info, int64_t render_time_ms) { frame_.set_timestamp(input._timeStamp); + frame_.set_ntp_time_ms(input.ntp_time_ms_); frame_.set_render_time_ms(render_time_ms); callback_->Decoded(frame_); @@ -56,5 +57,31 @@ return WEBRTC_VIDEO_CODEC_OK; } +int32_t FakeH264Decoder::Decode(const EncodedImage& input, + bool missing_frames, + const RTPFragmentationHeader* fragmentation, + const CodecSpecificInfo* codec_specific_info, + int64_t render_time_ms) { + uint8_t value = 0; + for (size_t i = 0; i < input._length; ++i) { + uint8_t kStartCode[] = {0, 0, 0, 1}; + if (i < input._length - sizeof(kStartCode) && + !memcmp(&input._buffer[i], kStartCode, sizeof(kStartCode))) { + i += sizeof(kStartCode) + 1; // Skip start code and NAL header. + } + if (input._buffer[i] != value) { + EXPECT_EQ(value, input._buffer[i]) + << "Bitstream mismatch between sender and receiver."; + return -1; + } + ++value; + } + return FakeDecoder::Decode(input, + missing_frames, + fragmentation, + codec_specific_info, + render_time_ms); +} + } // namespace test } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/fake_decoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/fake_decoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/fake_decoder.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/fake_decoder.h 2015-02-03 14:33:38.000000000 +0000 @@ -22,6 +22,7 @@ class FakeDecoder : public VideoDecoder { public: FakeDecoder(); + virtual ~FakeDecoder() {} virtual int32_t InitDecode(const VideoCodec* config, int32_t number_of_cores) OVERRIDE; @@ -43,6 +44,17 @@ I420VideoFrame frame_; DecodedImageCallback* callback_; }; + +class FakeH264Decoder : public FakeDecoder { + public: + virtual ~FakeH264Decoder() {} + + virtual int32_t Decode(const EncodedImage& input, + bool missing_frames, + const RTPFragmentationHeader* fragmentation, + const CodecSpecificInfo* codec_specific_info, + int64_t render_time_ms) OVERRIDE; +}; } // namespace test } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/fake_encoder.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/fake_encoder.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/fake_encoder.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/fake_encoder.cc 2015-02-03 14:33:38.000000000 +0000 @@ -12,6 +12,8 @@ #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h" + namespace webrtc { namespace test { @@ -19,6 +21,7 @@ : clock_(clock), callback_(NULL), target_bitrate_kbps_(0), + max_target_bitrate_kbps_(-1), last_encode_time_ms_(0) { // Generate some arbitrary not-all-zero data for (size_t i = 0; i < sizeof(encoded_buffer_); ++i) { @@ -28,38 +31,9 @@ FakeEncoder::~FakeEncoder() {} -void FakeEncoder::SetCodecSettings(VideoCodec* codec, - size_t num_streams) { - assert(num_streams > 0); - assert(num_streams <= kMaxSimulcastStreams); - - static const SimulcastStream stream_settings[] = { - {320, 180, 0, 150, 150, 50, codec->qpMax}, - {640, 360, 0, 500, 500, 150, codec->qpMax}, - {1280, 720, 0, 1200, 1200, 600, codec->qpMax}}; - // Add more streams to the settings above with reasonable values if required. - assert(num_streams <= sizeof(stream_settings) / sizeof(stream_settings[0])); - - codec->numberOfSimulcastStreams = static_cast(num_streams); - - unsigned int sum_of_max_bitrates = 0; - for (size_t i = 0; i < num_streams; ++i) { - codec->simulcastStream[i] = stream_settings[i]; - sum_of_max_bitrates += stream_settings[i].maxBitrate; - } - - size_t last_stream = num_streams - 1; - codec->width = stream_settings[last_stream].width; - codec->height = stream_settings[last_stream].height; - // Start with the average for the middle stream's max/min settings. - codec->startBitrate = (stream_settings[last_stream / 2].maxBitrate + - stream_settings[last_stream / 2].minBitrate) / - 2; - codec->minBitrate = stream_settings[0].minBitrate; - codec->maxBitrate = sum_of_max_bitrates; - - codec->codecType = kVideoCodecGeneric; - strcpy(codec->plName, "FAKE"); +void FakeEncoder::SetMaxBitrate(int max_kbps) { + assert(max_kbps >= -1); // max_kbps == -1 disables it. + max_target_bitrate_kbps_ = max_kbps; } int32_t FakeEncoder::InitEncode(const VideoCodec* config, @@ -75,33 +49,44 @@ const CodecSpecificInfo* codec_specific_info, const std::vector* frame_types) { assert(config_.maxFramerate > 0); - int delta_since_last_encode = 1000 / config_.maxFramerate; + int time_since_last_encode_ms = 1000 / config_.maxFramerate; int64_t time_now_ms = clock_->TimeInMilliseconds(); - if (last_encode_time_ms_ > 0) { + const bool first_encode = last_encode_time_ms_ == 0; + if (!first_encode) { // For all frames but the first we can estimate the display time by looking // at the display time of the previous frame. - delta_since_last_encode = time_now_ms - last_encode_time_ms_; + time_since_last_encode_ms = time_now_ms - last_encode_time_ms_; } - int bits_available = target_bitrate_kbps_ * delta_since_last_encode; + int bits_available = target_bitrate_kbps_ * time_since_last_encode_ms; int min_bits = - config_.simulcastStream[0].minBitrate * delta_since_last_encode; + config_.simulcastStream[0].minBitrate * time_since_last_encode_ms; if (bits_available < min_bits) bits_available = min_bits; + int max_bits = max_target_bitrate_kbps_ * time_since_last_encode_ms; + if (max_bits > 0 && max_bits < bits_available) + bits_available = max_bits; last_encode_time_ms_ = time_now_ms; + assert(config_.numberOfSimulcastStreams > 0); for (int i = 0; i < config_.numberOfSimulcastStreams; ++i) { CodecSpecificInfo specifics; memset(&specifics, 0, sizeof(specifics)); specifics.codecType = kVideoCodecGeneric; specifics.codecSpecific.generic.simulcast_idx = i; - int min_stream_bits = config_.simulcastStream[i].minBitrate * - delta_since_last_encode; - int max_stream_bits = config_.simulcastStream[i].maxBitrate * - delta_since_last_encode; + int min_stream_bits = + config_.simulcastStream[i].minBitrate * time_since_last_encode_ms; + int max_stream_bits = + config_.simulcastStream[i].maxBitrate * time_since_last_encode_ms; int stream_bits = (bits_available > max_stream_bits) ? max_stream_bits : bits_available; int stream_bytes = (stream_bits + 7) / 8; + if (first_encode) { + // The first frame is a key frame and should be larger. + // TODO(holmer): The FakeEncoder should store the bits_available between + // encodes so that it can compensate for oversized frames. + stream_bytes *= 10; + } if (static_cast(stream_bytes) > sizeof(encoded_buffer_)) stream_bytes = sizeof(encoded_buffer_); @@ -110,13 +95,14 @@ encoded._timeStamp = input_image.timestamp(); encoded.capture_time_ms_ = input_image.render_time_ms(); encoded._frameType = (*frame_types)[i]; - if (min_stream_bits > bits_available) { + // Always encode something on the first frame. + if (min_stream_bits > bits_available && i > 0) { encoded._length = 0; encoded._frameType = kSkipFrame; } + assert(callback_ != NULL); if (callback_->Encoded(encoded, &specifics, NULL) != 0) return -1; - bits_available -= encoded._length * 8; } return 0; @@ -138,5 +124,61 @@ target_bitrate_kbps_ = new_target_bitrate; return 0; } + +FakeH264Encoder::FakeH264Encoder(Clock* clock) + : FakeEncoder(clock), callback_(NULL), idr_counter_(0) { + FakeEncoder::RegisterEncodeCompleteCallback(this); +} + +int32_t FakeH264Encoder::RegisterEncodeCompleteCallback( + EncodedImageCallback* callback) { + callback_ = callback; + return 0; +} + +int32_t FakeH264Encoder::Encoded(EncodedImage& encoded_image, + const CodecSpecificInfo* codec_specific_info, + const RTPFragmentationHeader* fragments) { + const size_t kSpsSize = 8; + const size_t kPpsSize = 11; + const int kIdrFrequency = 10; + RTPFragmentationHeader fragmentation; + if (idr_counter_++ % kIdrFrequency == 0 && + encoded_image._length > kSpsSize + kPpsSize + 1) { + const size_t kNumSlices = 3; + fragmentation.VerifyAndAllocateFragmentationHeader(kNumSlices); + fragmentation.fragmentationOffset[0] = 0; + fragmentation.fragmentationLength[0] = kSpsSize; + fragmentation.fragmentationOffset[1] = kSpsSize; + fragmentation.fragmentationLength[1] = kPpsSize; + fragmentation.fragmentationOffset[2] = kSpsSize + kPpsSize; + fragmentation.fragmentationLength[2] = + encoded_image._length - (kSpsSize + kPpsSize); + const uint8_t kSpsNalHeader = 0x37; + const uint8_t kPpsNalHeader = 0x38; + const uint8_t kIdrNalHeader = 0x15; + encoded_image._buffer[fragmentation.fragmentationOffset[0]] = kSpsNalHeader; + encoded_image._buffer[fragmentation.fragmentationOffset[1]] = kPpsNalHeader; + encoded_image._buffer[fragmentation.fragmentationOffset[2]] = kIdrNalHeader; + } else { + const size_t kNumSlices = 1; + fragmentation.VerifyAndAllocateFragmentationHeader(kNumSlices); + fragmentation.fragmentationOffset[0] = 0; + fragmentation.fragmentationLength[0] = encoded_image._length; + const uint8_t kNalHeader = 0x11; + encoded_image._buffer[fragmentation.fragmentationOffset[0]] = kNalHeader; + } + uint8_t value = 0; + int fragment_counter = 0; + for (size_t i = 0; i < encoded_image._length; ++i) { + if (fragment_counter == fragmentation.fragmentationVectorSize || + i != fragmentation.fragmentationOffset[fragment_counter]) { + encoded_image._buffer[i] = value++; + } else { + ++fragment_counter; + } + } + return callback_->Encoded(encoded_image, NULL, &fragmentation); +} } // namespace test } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/fake_encoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/fake_encoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/fake_encoder.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/fake_encoder.h 2015-02-03 14:33:38.000000000 +0000 @@ -13,8 +13,9 @@ #include -#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h" +#include "webrtc/common_types.h" #include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/video_encoder.h" namespace webrtc { namespace test { @@ -24,35 +25,50 @@ explicit FakeEncoder(Clock* clock); virtual ~FakeEncoder(); - static void SetCodecSettings(VideoCodec* codec, size_t num_streams); + // Sets max bitrate. Not thread-safe, call before registering the encoder. + void SetMaxBitrate(int max_kbps); virtual int32_t InitEncode(const VideoCodec* config, int32_t number_of_cores, uint32_t max_payload_size) OVERRIDE; - virtual int32_t Encode( const I420VideoFrame& input_image, const CodecSpecificInfo* codec_specific_info, const std::vector* frame_types) OVERRIDE; - virtual int32_t RegisterEncodeCompleteCallback( EncodedImageCallback* callback) OVERRIDE; - virtual int32_t Release() OVERRIDE; - virtual int32_t SetChannelParameters(uint32_t packet_loss, int rtt) OVERRIDE; - virtual int32_t SetRates(uint32_t new_target_bitrate, uint32_t framerate) OVERRIDE; private: - Clock* clock_; + Clock* const clock_; VideoCodec config_; EncodedImageCallback* callback_; int target_bitrate_kbps_; + int max_target_bitrate_kbps_; int64_t last_encode_time_ms_; uint8_t encoded_buffer_[100000]; }; + +class FakeH264Encoder : public FakeEncoder, public EncodedImageCallback { + public: + explicit FakeH264Encoder(Clock* clock); + virtual ~FakeH264Encoder() {} + + virtual int32_t RegisterEncodeCompleteCallback( + EncodedImageCallback* callback) OVERRIDE; + + virtual int32_t Encoded( + EncodedImage& encodedImage, + const CodecSpecificInfo* codecSpecificInfo = NULL, + const RTPFragmentationHeader* fragments = NULL) OVERRIDE; + + private: + EncodedImageCallback* callback_; + int idr_counter_; +}; } // namespace test } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/fake_network_pipe.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/fake_network_pipe.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/fake_network_pipe.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/fake_network_pipe.cc 2015-02-03 14:33:38.000000000 +0000 @@ -33,6 +33,11 @@ sqrt(-2 * log(uniform1)) * cos(2 * kPi * uniform2)); } +static bool UniformLoss(int loss_percent) { + int outcome = rand() % 100; + return outcome < loss_percent; +} + class NetworkPacket { public: NetworkPacket(const uint8_t* data, size_t length, int64_t send_time, @@ -93,14 +98,19 @@ packet_receiver_ = receiver; } +void FakeNetworkPipe::SetConfig(const FakeNetworkPipe::Config& config) { + CriticalSectionScoped crit(lock_.get()); + config_ = config; // Shallow copy of the struct. +} + void FakeNetworkPipe::SendPacket(const uint8_t* data, size_t data_length) { // A NULL packet_receiver_ means that this pipe will terminate the flow of // packets. if (packet_receiver_ == NULL) return; CriticalSectionScoped crit(lock_.get()); - if (config_.queue_length > 0 && - capacity_link_.size() >= config_.queue_length) { + if (config_.queue_length_packets > 0 && + capacity_link_.size() >= config_.queue_length_packets) { // Too many packet on the link, drop this one. ++dropped_packets_; return; @@ -154,6 +164,12 @@ NetworkPacket* packet = capacity_link_.front(); capacity_link_.pop(); + // Packets are randomly dropped after being affected by the bottleneck. + if (UniformLoss(config_.loss_percent)) { + delete packet; + continue; + } + // Add extra delay and jitter, but make sure the arrival time is not // earlier than the last packet in the queue. int extra_delay = GaussianRandom(config_.queue_delay_ms, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/fake_network_pipe.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/fake_network_pipe.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/fake_network_pipe.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/fake_network_pipe.h 2015-02-03 14:33:38.000000000 +0000 @@ -13,7 +13,7 @@ #include -#include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/base/constructormagic.h" #include "webrtc/system_wrappers/interface/event_wrapper.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/typedefs.h" @@ -33,21 +33,21 @@ public: struct Config { Config() - : queue_length(0), + : queue_length_packets(0), queue_delay_ms(0), delay_standard_deviation_ms(0), link_capacity_kbps(0), loss_percent(0) { } // Queue length in number of packets. - size_t queue_length; + size_t queue_length_packets; // Delay in addition to capacity induced delay. int queue_delay_ms; // Standard deviation of the extra delay. int delay_standard_deviation_ms; // Link capacity in kbps. int link_capacity_kbps; - // Random packet loss. Not implemented. + // Random packet loss. int loss_percent; }; @@ -57,6 +57,9 @@ // Must not be called in parallel with SendPacket or Process. void SetReceiver(PacketReceiver* receiver); + // Sets a new configuration. This won't affect packets already in the pipe. + void SetConfig(const FakeNetworkPipe::Config& config); + // Sends a new packet to the link. void SendPacket(const uint8_t* packet, size_t packet_length); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/fake_network_pipe_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/fake_network_pipe_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/fake_network_pipe_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/fake_network_pipe_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -33,7 +33,7 @@ delete [] data; } - MOCK_METHOD2(DeliverPacket, bool(const uint8_t*, size_t)); + MOCK_METHOD2(DeliverPacket, DeliveryStatus(const uint8_t*, size_t)); }; class FakeNetworkPipeTest : public ::testing::Test { @@ -41,19 +41,21 @@ virtual void SetUp() { TickTime::UseFakeClock(12345); receiver_.reset(new MockReceiver()); + ON_CALL(*receiver_, DeliverPacket(_, _)) + .WillByDefault(Return(PacketReceiver::DELIVERY_OK)); } virtual void TearDown() { } void SendPackets(FakeNetworkPipe* pipe, int number_packets, int kPacketSize) { - scoped_array packet(new uint8_t[kPacketSize]); + scoped_ptr packet(new uint8_t[kPacketSize]); for (int i = 0; i < number_packets; ++i) { pipe->SendPacket(packet.get(), kPacketSize); } } - int PacketTimeMs(int capacity_kbps, int kPacketSize) { + int PacketTimeMs(int capacity_kbps, int kPacketSize) const { return 8 * kPacketSize / capacity_kbps; } @@ -65,7 +67,7 @@ // Test the capacity link and verify we get as many packets as we expect. TEST_F(FakeNetworkPipeTest, CapacityTest) { FakeNetworkPipe::Config config; - config.queue_length = 20; + config.queue_length_packets = 20; config.link_capacity_kbps = 80; scoped_ptr pipe(new FakeNetworkPipe(config)); pipe->SetReceiver(receiver_.get()); @@ -107,7 +109,7 @@ // Test the extra network delay. TEST_F(FakeNetworkPipeTest, ExtraDelayTest) { FakeNetworkPipe::Config config; - config.queue_length = 20; + config.queue_length_packets = 20; config.queue_delay_ms = 100; config.link_capacity_kbps = 80; scoped_ptr pipe(new FakeNetworkPipe(config)); @@ -144,7 +146,7 @@ // packets too quickly. TEST_F(FakeNetworkPipeTest, QueueLengthTest) { FakeNetworkPipe::Config config; - config.queue_length = 2; + config.queue_length_packets = 2; config.link_capacity_kbps = 80; scoped_ptr pipe(new FakeNetworkPipe(config)); pipe->SetReceiver(receiver_.get()); @@ -167,7 +169,7 @@ // Test we get statistics as expected. TEST_F(FakeNetworkPipeTest, StatisticsTest) { FakeNetworkPipe::Config config; - config.queue_length = 2; + config.queue_length_packets = 2; config.queue_delay_ms = 20; config.link_capacity_kbps = 80; scoped_ptr pipe(new FakeNetworkPipe(config)); @@ -193,4 +195,114 @@ EXPECT_EQ(pipe->PercentageLoss(), 1/3.f); } +// Change the link capacity half-way through the test and verify that the +// delivery times change accordingly. +TEST_F(FakeNetworkPipeTest, ChangingCapacityWithEmptyPipeTest) { + FakeNetworkPipe::Config config; + config.queue_length_packets = 20; + config.link_capacity_kbps = 80; + scoped_ptr pipe(new FakeNetworkPipe(config)); + pipe->SetReceiver(receiver_.get()); + + // Add 10 packets of 1000 bytes, = 80 kb, and verify it takes one second to + // get through the pipe. + const int kNumPackets = 10; + const int kPacketSize = 1000; + SendPackets(pipe.get(), kNumPackets, kPacketSize); + + // Time to get one packet through the link. + int packet_time_ms = PacketTimeMs(config.link_capacity_kbps, kPacketSize); + + // Time hasn't increased yet, so we souldn't get any packets. + EXPECT_CALL(*receiver_, DeliverPacket(_, _)).Times(0); + pipe->Process(); + + // Advance time in steps to release one packet at a time. + for (int i = 0; i < kNumPackets; ++i) { + TickTime::AdvanceFakeClock(packet_time_ms); + EXPECT_CALL(*receiver_, DeliverPacket(_, _)).Times(1); + pipe->Process(); + } + + // Change the capacity. + config.link_capacity_kbps /= 2; // Reduce to 50%. + pipe->SetConfig(config); + + // Add another 10 packets of 1000 bytes, = 80 kb, and verify it takes two + // seconds to get them through the pipe. + SendPackets(pipe.get(), kNumPackets, kPacketSize); + + // Time to get one packet through the link. + packet_time_ms = PacketTimeMs(config.link_capacity_kbps, kPacketSize); + + // Time hasn't increased yet, so we souldn't get any packets. + EXPECT_CALL(*receiver_, DeliverPacket(_, _)).Times(0); + pipe->Process(); + + // Advance time in steps to release one packet at a time. + for (int i = 0; i < kNumPackets; ++i) { + TickTime::AdvanceFakeClock(packet_time_ms); + EXPECT_CALL(*receiver_, DeliverPacket(_, _)).Times(1); + pipe->Process(); + } + + // Check that all the packets were sent. + EXPECT_EQ(static_cast(2 * kNumPackets), pipe->sent_packets()); + TickTime::AdvanceFakeClock(pipe->TimeUntilNextProcess()); + EXPECT_CALL(*receiver_, DeliverPacket(_, _)).Times(0); + pipe->Process(); +} + +// Change the link capacity half-way through the test and verify that the +// delivery times change accordingly. +TEST_F(FakeNetworkPipeTest, ChangingCapacityWithPacketsInPipeTest) { + FakeNetworkPipe::Config config; + config.queue_length_packets = 20; + config.link_capacity_kbps = 80; + scoped_ptr pipe(new FakeNetworkPipe(config)); + pipe->SetReceiver(receiver_.get()); + + // Add 10 packets of 1000 bytes, = 80 kb. + const int kNumPackets = 10; + const int kPacketSize = 1000; + SendPackets(pipe.get(), kNumPackets, kPacketSize); + + // Time to get one packet through the link at the initial speed. + int packet_time_1_ms = PacketTimeMs(config.link_capacity_kbps, kPacketSize); + + // Change the capacity. + config.link_capacity_kbps *= 2; // Double the capacity. + pipe->SetConfig(config); + + // Add another 10 packets of 1000 bytes, = 80 kb, and verify it takes two + // seconds to get them through the pipe. + SendPackets(pipe.get(), kNumPackets, kPacketSize); + + // Time to get one packet through the link at the new capacity. + int packet_time_2_ms = PacketTimeMs(config.link_capacity_kbps, kPacketSize); + + // Time hasn't increased yet, so we souldn't get any packets. + EXPECT_CALL(*receiver_, DeliverPacket(_, _)).Times(0); + pipe->Process(); + + // Advance time in steps to release one packet at a time. + for (int i = 0; i < kNumPackets; ++i) { + TickTime::AdvanceFakeClock(packet_time_1_ms); + EXPECT_CALL(*receiver_, DeliverPacket(_, _)).Times(1); + pipe->Process(); + } + + // Advance time in steps to release one packet at a time. + for (int i = 0; i < kNumPackets; ++i) { + TickTime::AdvanceFakeClock(packet_time_2_ms); + EXPECT_CALL(*receiver_, DeliverPacket(_, _)).Times(1); + pipe->Process(); + } + + // Check that all the packets were sent. + EXPECT_EQ(static_cast(2 * kNumPackets), pipe->sent_packets()); + TickTime::AdvanceFakeClock(pipe->TimeUntilNextProcess()); + EXPECT_CALL(*receiver_, DeliverPacket(_, _)).Times(0); + pipe->Process(); +} } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/field_trial.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/field_trial.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/field_trial.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/field_trial.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/test/field_trial.h" + +#include +#include +#include +#include +#include +#include + +#include "webrtc/system_wrappers/interface/field_trial.h" + +namespace webrtc { +namespace { +// Clients of this library have show a clear intent to setup field trials by +// linking with it. As so try to crash if they forget to call +// InitFieldTrialsFromString before webrtc tries to access a field trial. +bool field_trials_initiated_ = false; +std::map field_trials_; +} // namespace + +namespace field_trial { +std::string FindFullName(const std::string& trial_name) { + assert(field_trials_initiated_); + std::map::const_iterator it = + field_trials_.find(trial_name); + if (it == field_trials_.end()) + return std::string(); + return it->second; +} +} // namespace field_trial + +namespace test { +// Note: this code is copied from src/base/metrics/field_trial.cc since the aim +// is to mimic chromium --force-fieldtrials. +void InitFieldTrialsFromString(const std::string& trials_string) { + static const char kPersistentStringSeparator = '/'; + + // Catch an error if this is called more than once. + assert(field_trials_initiated_ == false); + field_trials_initiated_ = true; + + if (trials_string.empty()) return; + + size_t next_item = 0; + while (next_item < trials_string.length()) { + size_t name_end = trials_string.find(kPersistentStringSeparator, next_item); + if (name_end == trials_string.npos || next_item == name_end) + break; + size_t group_name_end = trials_string.find(kPersistentStringSeparator, + name_end + 1); + if (group_name_end == trials_string.npos || name_end + 1 == group_name_end) + break; + std::string name(trials_string, next_item, name_end - next_item); + std::string group_name(trials_string, name_end + 1, + group_name_end - name_end - 1); + next_item = group_name_end + 1; + + // Fail if duplicate with different group name. + if (field_trials_.find(name) != field_trials_.end() && + field_trials_.find(name)->second != group_name) + break; + + field_trials_[name] = group_name; + + // Successfully parsed all field trials from the string. + if (next_item == trials_string.length()) + return; + } + // LOG does not prints when this is called early on main. + fprintf(stderr, "Invalid field trials string.\n"); + + // Using abort so it crashs both in debug and release mode. + abort(); +} +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/field_trial.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/field_trial.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/field_trial.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/field_trial.h 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_TEST_FIELD_TRIAL_H_ +#define WEBRTC_TEST_FIELD_TRIAL_H_ + +#include + +namespace webrtc { +namespace test { + +// Parses enabled field trials from a string config, such as the one passed +// to chrome's argument --force-fieldtrials and initializes webrtc::field_trial +// with such a config. +// E.g.: +// "WebRTC-experimentFoo/Enabled/WebRTC-experimentBar/Enabled100kbps/" +// Assigns the process to group "Enabled" on WebRTCExperimentFoo trial +// and to group "Enabled100kbps" on WebRTCExperimentBar. +// +// E.g. invalid config: +// "WebRTC-experiment1/Enabled" (note missing / separator at the end). +// +// Note: This method crashes with an error message if an invalid config is +// passed to it. That can be used to find out if a binary is parsing the flags. +void InitFieldTrialsFromString(const std::string& config); + +} // namespace test +} // namespace webrtc + +#endif // WEBRTC_TEST_FIELD_TRIAL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/flags.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/flags.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/flags.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/flags.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/test/flags.h" - -#include "gflags/gflags.h" - -namespace webrtc { -namespace test { -namespace flags { - -void Init(int* argc, char*** argv) { - // AllowCommandLineParsing allows us to ignore flags passed on to us by - // Chromium build bots without having to explicitly disable them. - google::AllowCommandLineReparsing(); - google::ParseCommandLineFlags(argc, argv, true); -} - -DEFINE_int32(width, 640, "Video width."); -size_t Width() { return static_cast(FLAGS_width); } - -DEFINE_int32(height, 480, "Video height."); -size_t Height() { return static_cast(FLAGS_height); } - -DEFINE_int32(fps, 30, "Frames per second."); -int Fps() { return static_cast(FLAGS_fps); } - -DEFINE_int32(min_bitrate, 50, "Minimum video bitrate."); -size_t MinBitrate() { return static_cast(FLAGS_min_bitrate); } - -DEFINE_int32(start_bitrate, 300, "Video starting bitrate."); -size_t StartBitrate() { return static_cast(FLAGS_start_bitrate); } - -DEFINE_int32(max_bitrate, 800, "Maximum video bitrate."); -size_t MaxBitrate() { return static_cast(FLAGS_max_bitrate); } -} // flags -} // test -} // webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/flags.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/flags.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/flags.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/flags.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_VIDEO_ENGINE_TEST_COMMON_FLAGS_H_ -#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_FLAGS_H_ - -#include - -namespace webrtc { -namespace test { -namespace flags { - -void Init(int* argc, char ***argv); - -size_t Width(); -size_t Height(); -int Fps(); -size_t MinBitrate(); -size_t StartBitrate(); -size_t MaxBitrate(); -} // flags -} // test -} // webrtc - -#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_FLAGS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/frame_generator_capturer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/frame_generator_capturer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/frame_generator_capturer.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/frame_generator_capturer.cc 2015-02-03 14:33:38.000000000 +0000 @@ -67,7 +67,8 @@ tick_(EventWrapper::Create()), lock_(CriticalSectionWrapper::CreateCriticalSection()), frame_generator_(frame_generator), - target_fps_(target_fps) { + target_fps_(target_fps), + first_frame_capture_time_(-1) { assert(input != NULL); assert(frame_generator != NULL); assert(target_fps > 0); @@ -113,6 +114,9 @@ if (sending_) { I420VideoFrame* frame = frame_generator_->NextFrame(); frame->set_render_time_ms(clock_->CurrentNtpInMilliseconds()); + if (first_frame_capture_time_ == -1) { + first_frame_capture_time_ = frame->render_time_ms(); + } input_->SwapFrame(frame); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/frame_generator_capturer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/frame_generator_capturer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/frame_generator_capturer.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/frame_generator_capturer.h 2015-02-03 14:33:38.000000000 +0000 @@ -43,6 +43,8 @@ virtual void Start() OVERRIDE; virtual void Stop() OVERRIDE; + int64_t first_frame_capture_time() const { return first_frame_capture_time_; } + private: FrameGeneratorCapturer(Clock* clock, VideoSendStreamInput* input, @@ -52,7 +54,7 @@ void InsertFrame(); static bool Run(void* obj); - Clock* clock_; + Clock* const clock_; bool sending_; scoped_ptr tick_; @@ -61,6 +63,8 @@ scoped_ptr frame_generator_; int target_fps_; + + int64_t first_frame_capture_time_; }; } // test } // webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/libtest/helpers/bit_flip_encryption.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/libtest/helpers/bit_flip_encryption.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/libtest/helpers/bit_flip_encryption.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/libtest/helpers/bit_flip_encryption.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/test/libtest/include/bit_flip_encryption.h" - -#include - -float NormalizedRand() { - return static_cast(rand()) / - static_cast(RAND_MAX); -} - -BitFlipEncryption::BitFlipEncryption(unsigned int rand_seed, - float flip_probability) - : flip_probability_(flip_probability), - flip_count_(0) { - srand(rand_seed); -} - -void BitFlipEncryption::FlipSomeBitsInData(const unsigned char* in_data, - unsigned char* out_data, - int bytes_in, int* bytes_out) { - for (int i = 0; i < bytes_in; i++) { - out_data[i] = in_data[i]; - - if (NormalizedRand() < flip_probability_) { - int bit_to_flip = rand() % 8; - out_data[i] ^= 1 << bit_to_flip; - flip_count_++; - } - } - *bytes_out = bytes_in; -} - -void BitFlipEncryption::encrypt(int channel_no, - unsigned char* in_data, - unsigned char* out_data, - int bytes_in, - int* bytes_out) { - FlipSomeBitsInData(in_data, out_data, bytes_in, bytes_out); -} - -void BitFlipEncryption::decrypt(int channel_no, - unsigned char* in_data, - unsigned char* out_data, - int bytes_in, - int* bytes_out) { - FlipSomeBitsInData(in_data, out_data, bytes_in, bytes_out); -} - -void BitFlipEncryption::encrypt_rtcp(int channel_no, - unsigned char* in_data, - unsigned char* out_data, - int bytes_in, - int* bytes_out) { - FlipSomeBitsInData(in_data, out_data, bytes_in, bytes_out); -} - -void BitFlipEncryption::decrypt_rtcp(int channel_no, - unsigned char* in_data, - unsigned char* out_data, - int bytes_in, - int* bytes_out) { - FlipSomeBitsInData(in_data, out_data, bytes_in, bytes_out); -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/libtest/helpers/random_encryption.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/libtest/helpers/random_encryption.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/libtest/helpers/random_encryption.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/libtest/helpers/random_encryption.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/test/libtest/include/random_encryption.h" - -#include -#include - -#include - -#include "webrtc/video_engine/vie_defines.h" - -static int Saturate(int value, int min, int max) { - return std::min(std::max(value, min), max); -} - -RandomEncryption::RandomEncryption(unsigned int rand_seed) { - srand(rand_seed); -} - -// Generates some completely random data with roughly the right length. -void RandomEncryption::GenerateRandomData(unsigned char* out_data, int bytes_in, - int* bytes_out) { - int out_length = MakeUpSimilarLength(bytes_in); - for (int i = 0; i < out_length; i++) { - // The modulo will skew the random distribution a bit, but I think it - // will be random enough. - out_data[i] = static_cast(rand() % 256); - } - *bytes_out = out_length; -} - -// Makes up a length within +- 50 of the original length, without -// overstepping the contract for encrypt / decrypt. -int RandomEncryption::MakeUpSimilarLength(int original_length) { - int sign = rand() - RAND_MAX / 2; - int length = original_length + sign * rand() % 50; - - return Saturate(length, 0, static_cast(webrtc::kViEMaxMtu)); -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/libtest/include/bit_flip_encryption.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/libtest/include/bit_flip_encryption.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/libtest/include/bit_flip_encryption.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/libtest/include/bit_flip_encryption.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef SRC_VIDEO_ENGINE_TEST_AUTO_TEST_HELPERS_BIT_FLIP_ENCRYPTION_H_ -#define SRC_VIDEO_ENGINE_TEST_AUTO_TEST_HELPERS_BIT_FLIP_ENCRYPTION_H_ - -#include "webrtc/common_types.h" - -// This encryption scheme will randomly flip bits every now and then in the -// input data. -class BitFlipEncryption : public webrtc::Encryption { - public: - // Args: - // rand_seed: the seed to initialize the test's random generator with. - // flip_probability: A number [0, 1] which is the percentage chance a bit - // gets flipped in a particular byte. - BitFlipEncryption(unsigned int rand_seed, float flip_probability); - - virtual void encrypt(int channel_no, - unsigned char* in_data, - unsigned char* out_data, - int bytes_in, - int* bytes_out) OVERRIDE; - - virtual void decrypt(int channel_no, - unsigned char* in_data, - unsigned char* out_data, - int bytes_in, - int* bytes_out) OVERRIDE; - - virtual void encrypt_rtcp(int channel_no, - unsigned char* in_data, - unsigned char* out_data, - int bytes_in, - int* bytes_out) OVERRIDE; - - virtual void decrypt_rtcp(int channel_no, - unsigned char* in_data, - unsigned char* out_data, - int bytes_in, - int* bytes_out) OVERRIDE; - - int64_t flip_count() const { return flip_count_; } - - private: - // The flip probability ([0, 1]). - float flip_probability_; - // The number of bits we've flipped so far. - int64_t flip_count_; - - // Flips some bits in the data at random. - void FlipSomeBitsInData(const unsigned char *in_data, unsigned char* out_data, - int bytes_in, int* bytes_out); -}; - -#endif // SRC_VIDEO_ENGINE_TEST_AUTO_TEST_HELPERS_BIT_FLIP_ENCRYPTION_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/libtest/include/random_encryption.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/libtest/include/random_encryption.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/libtest/include/random_encryption.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/libtest/include/random_encryption.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef SRC_VIDEO_ENGINE_TEST_AUTO_TEST_HELPERS_RANDOM_ENCRYPTION_H_ -#define SRC_VIDEO_ENGINE_TEST_AUTO_TEST_HELPERS_RANDOM_ENCRYPTION_H_ - -#include "webrtc/common_types.h" - -// These algorithms attempt to create an uncrackable encryption -// scheme by completely disregarding the input data. -class RandomEncryption : public webrtc::Encryption { - public: - explicit RandomEncryption(unsigned int rand_seed); - - virtual void encrypt(int channel_no, - unsigned char* in_data, - unsigned char* out_data, - int bytes_in, - int* bytes_out) OVERRIDE { - GenerateRandomData(out_data, bytes_in, bytes_out); - } - - virtual void decrypt(int channel_no, - unsigned char* in_data, - unsigned char* out_data, - int bytes_in, - int* bytes_out) OVERRIDE { - GenerateRandomData(out_data, bytes_in, bytes_out); - } - - virtual void encrypt_rtcp(int channel_no, - unsigned char* in_data, - unsigned char* out_data, - int bytes_in, - int* bytes_out) OVERRIDE { - GenerateRandomData(out_data, bytes_in, bytes_out); - } - - virtual void decrypt_rtcp(int channel_no, - unsigned char* in_data, - unsigned char* out_data, - int bytes_in, - int* bytes_out) OVERRIDE { - GenerateRandomData(out_data, bytes_in, bytes_out); - } - - private: - // Generates some completely random data with roughly the right length. - void GenerateRandomData(unsigned char* out_data, - int bytes_in, - int* bytes_out); - - // Makes up a length within +- 50 of the original length, without - // overstepping the contract for encrypt / decrypt. - int MakeUpSimilarLength(int original_length); -}; - -#endif // SRC_VIDEO_ENGINE_TEST_AUTO_TEST_HELPERS_RANDOM_ENCRYPTION_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/libtest/libtest.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/libtest/libtest.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/libtest/libtest.gyp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/libtest/libtest.gyp 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. -{ - 'includes': [ - '../../build/common.gypi' - ], - 'targets': [ - { - 'target_name': 'libtest', - 'type': 'static_library', - 'sources': [ - # Helper classes - 'include/bit_flip_encryption.h', - 'include/random_encryption.h', - - 'helpers/bit_flip_encryption.cc', - 'helpers/random_encryption.cc', - ], - }, - ], -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/mac/run_test.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/mac/run_test.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/mac/run_test.mm 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/mac/run_test.mm 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import + +#include "webrtc/test/run_test.h" + +// Converting a C++ function pointer to an Objective-C block. +typedef void(^TestBlock)(); +TestBlock functionToBlock(void(*function)()) { + return [^(void) { function(); } copy]; +} + +// Class calling the test function on the platform specific thread. +@interface TestRunner : NSObject { + BOOL running_; +} +- (void)runAllTests:(TestBlock)ignored; +- (BOOL)running; +@end + +@implementation TestRunner +- (id)init { + self = [super init]; + if (self) { + running_ = YES; + } + return self; +} + +- (void)runAllTests:(TestBlock)testBlock { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + testBlock(); + running_ = NO; + [pool release]; +} + +- (BOOL)running { + return running_; +} +@end + +namespace webrtc { +namespace test { + +void RunTest(void(*test)()) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [NSApplication sharedApplication]; + + // Convert the function pointer to an Objective-C block and call on a + // separate thread, to avoid blocking the main thread. + TestRunner *testRunner = [[TestRunner alloc] init]; + TestBlock testBlock = functionToBlock(test); + [NSThread detachNewThreadSelector:@selector(runAllTests:) + toTarget:testRunner + withObject:testBlock]; + + NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; + while ([testRunner running] && + [runLoop runMode:NSDefaultRunLoopMode + beforeDate:[NSDate distantFuture]]); + + [testRunner release]; + [pool release]; +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/mac/run_tests.mm thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/mac/run_tests.mm --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/mac/run_tests.mm 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/mac/run_tests.mm 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#import - -#include "testing/gtest/include/gtest/gtest.h" - -@interface TestRunner : NSObject { - BOOL running_; - int testResult_; -} -- (void)runAllTests:(NSObject *)ignored; -- (BOOL)running; -- (int)result; -@end - -@implementation TestRunner -- (id)init { - self = [super init]; - if (self) { - running_ = YES; - } - return self; -} - -- (void)runAllTests:(NSObject *)ignored { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - testResult_ = RUN_ALL_TESTS(); - running_ = NO; - [pool release]; -} - -- (BOOL)running { - return running_; -} - -- (int)result { - return testResult_; -} -@end - -namespace webrtc { -namespace test { - -int RunAllTests() { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - [NSApplication sharedApplication]; - - TestRunner *testRunner = [[TestRunner alloc] init]; - [NSThread detachNewThreadSelector:@selector(runAllTests:) - toTarget:testRunner - withObject:nil]; - - NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; - while ([testRunner running] && - [runLoop runMode:NSDefaultRunLoopMode - beforeDate:[NSDate distantFuture]]); - - int result = [testRunner result]; - [testRunner release]; - [pool release]; - return result; -} - -} // namespace test -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/mac/video_renderer_mac.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/mac/video_renderer_mac.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/mac/video_renderer_mac.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/mac/video_renderer_mac.h 2015-02-03 14:33:38.000000000 +0000 @@ -11,8 +11,8 @@ #ifndef WEBRTC_VIDEO_ENGINE_TEST_COMMON_MAC_VIDEO_RENDERER_MAC_H_ #define WEBRTC_VIDEO_ENGINE_TEST_COMMON_MAC_VIDEO_RENDERER_MAC_H_ +#include "webrtc/base/constructormagic.h" #include "webrtc/test/gl/gl_renderer.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" @class CocoaWindow; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/OWNERS 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/OWNERS 2015-02-03 14:33:38.000000000 +0000 @@ -1,4 +1,10 @@ -phoglund@webrtc.org -kjellander@webrtc.org -ivinnichenko@webrtc.org - +kjellander@webrtc.org +pbos@webrtc.org +phoglund@webrtc.org + +per-file *.isolate=kjellander@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/rtcp_packet_parser.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/rtcp_packet_parser.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/rtcp_packet_parser.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/rtcp_packet_parser.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/test/rtcp_packet_parser.h" + +namespace webrtc { +namespace test { + +RtcpPacketParser::RtcpPacketParser() {} + +RtcpPacketParser::~RtcpPacketParser() {} + +void RtcpPacketParser::Parse(const void *data, int len) { + const uint8_t* packet = static_cast(data); + RTCPUtility::RTCPParserV2 parser(packet, len, true); + for (RTCPUtility::RTCPPacketTypes type = parser.Begin(); + type != RTCPUtility::kRtcpNotValidCode; + type = parser.Iterate()) { + switch (type) { + case RTCPUtility::kRtcpSrCode: + sender_report_.Set(parser.Packet().SR); + break; + case RTCPUtility::kRtcpRrCode: + receiver_report_.Set(parser.Packet().RR); + break; + case RTCPUtility::kRtcpReportBlockItemCode: + report_block_.Set(parser.Packet().ReportBlockItem); + ++report_blocks_per_ssrc_[parser.Packet().ReportBlockItem.SSRC]; + break; + case RTCPUtility::kRtcpSdesCode: + sdes_.Set(); + break; + case RTCPUtility::kRtcpSdesChunkCode: + sdes_chunk_.Set(parser.Packet().CName); + break; + case RTCPUtility::kRtcpByeCode: + bye_.Set(parser.Packet().BYE); + break; + case RTCPUtility::kRtcpAppCode: + app_.Set(parser.Packet().APP); + break; + case RTCPUtility::kRtcpAppItemCode: + app_item_.Set(parser.Packet().APP); + break; + case RTCPUtility::kRtcpExtendedIjCode: + ij_.Set(); + break; + case RTCPUtility::kRtcpExtendedIjItemCode: + ij_item_.Set(parser.Packet().ExtendedJitterReportItem); + break; + case RTCPUtility::kRtcpPsfbPliCode: + pli_.Set(parser.Packet().PLI); + break; + case RTCPUtility::kRtcpPsfbSliCode: + sli_.Set(parser.Packet().SLI); + break; + case RTCPUtility::kRtcpPsfbSliItemCode: + sli_item_.Set(parser.Packet().SLIItem); + break; + case RTCPUtility::kRtcpPsfbRpsiCode: + rpsi_.Set(parser.Packet().RPSI); + break; + case RTCPUtility::kRtcpPsfbFirCode: + fir_.Set(parser.Packet().FIR); + break; + case RTCPUtility::kRtcpPsfbFirItemCode: + fir_item_.Set(parser.Packet().FIRItem); + break; + case RTCPUtility::kRtcpRtpfbNackCode: + nack_.Set(parser.Packet().NACK); + nack_item_.Clear(); + break; + case RTCPUtility::kRtcpRtpfbNackItemCode: + nack_item_.Set(parser.Packet().NACKItem); + break; + case RTCPUtility::kRtcpPsfbAppCode: + psfb_app_.Set(parser.Packet().PSFBAPP); + break; + case RTCPUtility::kRtcpPsfbRembItemCode: + remb_item_.Set(parser.Packet().REMBItem); + break; + case RTCPUtility::kRtcpRtpfbTmmbrCode: + tmmbr_.Set(parser.Packet().TMMBR); + break; + case RTCPUtility::kRtcpRtpfbTmmbrItemCode: + tmmbr_item_.Set(parser.Packet().TMMBRItem); + break; + case RTCPUtility::kRtcpRtpfbTmmbnCode: + tmmbn_.Set(parser.Packet().TMMBN); + tmmbn_items_.Clear(); + break; + case RTCPUtility::kRtcpRtpfbTmmbnItemCode: + tmmbn_items_.Set(parser.Packet().TMMBNItem); + break; + case RTCPUtility::kRtcpXrHeaderCode: + xr_header_.Set(parser.Packet().XR); + dlrr_items_.Clear(); + break; + case RTCPUtility::kRtcpXrReceiverReferenceTimeCode: + rrtr_.Set(parser.Packet().XRReceiverReferenceTimeItem); + break; + case RTCPUtility::kRtcpXrDlrrReportBlockCode: + dlrr_.Set(); + break; + case RTCPUtility::kRtcpXrDlrrReportBlockItemCode: + dlrr_items_.Set(parser.Packet().XRDLRRReportBlockItem); + break; + case RTCPUtility::kRtcpXrVoipMetricCode: + voip_metric_.Set(parser.Packet().XRVOIPMetricItem); + break; + default: + break; + } + } +} + +uint64_t Rpsi::PictureId() const { + assert(num_packets_ > 0); + uint16_t num_bytes = rpsi_.NumberOfValidBits / 8; + assert(num_bytes > 0); + uint64_t picture_id = 0; + for (uint16_t i = 0; i < num_bytes - 1; ++i) { + picture_id += (rpsi_.NativeBitString[i] & 0x7f); + picture_id <<= 7; + } + picture_id += (rpsi_.NativeBitString[num_bytes - 1] & 0x7f); + return picture_id; +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/rtcp_packet_parser.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/rtcp_packet_parser.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/rtcp_packet_parser.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/rtcp_packet_parser.h 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,705 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#ifndef WEBRTC_TEST_RTCP_PACKET_PARSER_H_ +#define WEBRTC_TEST_RTCP_PACKET_PARSER_H_ + +#include +#include +#include + +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace test { + +class RtcpPacketParser; + +class PacketType { + public: + virtual ~PacketType() {} + + int num_packets() const { return num_packets_; } + + protected: + PacketType() : num_packets_(0) {} + + int num_packets_; +}; + +class SenderReport : public PacketType { + public: + SenderReport() {} + virtual ~SenderReport() {} + + uint32_t Ssrc() const { return sr_.SenderSSRC; } + uint32_t NtpSec() const { return sr_.NTPMostSignificant; } + uint32_t NtpFrac() const { return sr_.NTPLeastSignificant; } + uint32_t RtpTimestamp() const { return sr_.RTPTimestamp; } + uint32_t PacketCount() const { return sr_.SenderPacketCount; } + uint32_t OctetCount() const { return sr_.SenderOctetCount; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketSR& sr) { + sr_ = sr; + ++num_packets_; + } + + RTCPUtility::RTCPPacketSR sr_; +}; + +class ReceiverReport : public PacketType { + public: + ReceiverReport() {} + virtual ~ReceiverReport() {} + + uint32_t Ssrc() const { return rr_.SenderSSRC; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketRR& rr) { + rr_ = rr; + ++num_packets_; + } + + RTCPUtility::RTCPPacketRR rr_; +}; + +class ReportBlock : public PacketType { + public: + ReportBlock() {} + virtual ~ReportBlock() {} + + uint32_t Ssrc() const { return rb_.SSRC; } + uint8_t FractionLost() const { return rb_.FractionLost; } + uint32_t CumPacketLost() const { return rb_.CumulativeNumOfPacketsLost; } + uint32_t ExtHighestSeqNum() const { return rb_.ExtendedHighestSequenceNumber;} + uint32_t Jitter() const { return rb_.Jitter; } + uint32_t LastSr() const { return rb_.LastSR; } + uint32_t DelayLastSr()const { return rb_.DelayLastSR; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketReportBlockItem& rb) { + rb_ = rb; + ++num_packets_; + } + + RTCPUtility::RTCPPacketReportBlockItem rb_; +}; + +class Ij : public PacketType { + public: + Ij() {} + virtual ~Ij() {} + + private: + friend class RtcpPacketParser; + + void Set() { ++num_packets_; } +}; + +class IjItem : public PacketType { + public: + IjItem() {} + virtual ~IjItem() {} + + uint32_t Jitter() const { return ij_item_.Jitter; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketExtendedJitterReportItem& ij_item) { + ij_item_ = ij_item; + ++num_packets_; + } + + RTCPUtility::RTCPPacketExtendedJitterReportItem ij_item_; +}; + +class Sdes : public PacketType { + public: + Sdes() {} + virtual ~Sdes() {} + + private: + friend class RtcpPacketParser; + + void Set() { ++num_packets_; } +}; + +class SdesChunk : public PacketType { + public: + SdesChunk() {} + virtual ~SdesChunk() {} + + uint32_t Ssrc() const { return cname_.SenderSSRC; } + std::string Cname() const { return cname_.CName; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketSDESCName& cname) { + cname_ = cname; + ++num_packets_; + } + + RTCPUtility::RTCPPacketSDESCName cname_; +}; + +class Bye : public PacketType { + public: + Bye() {} + virtual ~Bye() {} + + uint32_t Ssrc() const { return bye_.SenderSSRC; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketBYE& bye) { + bye_ = bye; + ++num_packets_; + } + + RTCPUtility::RTCPPacketBYE bye_; +}; + +class Rpsi : public PacketType { + public: + Rpsi() {} + virtual ~Rpsi() {} + + uint32_t Ssrc() const { return rpsi_.SenderSSRC; } + uint32_t MediaSsrc() const { return rpsi_.MediaSSRC; } + uint8_t PayloadType() const { return rpsi_.PayloadType; } + uint16_t NumberOfValidBits() const { return rpsi_.NumberOfValidBits; } + uint64_t PictureId() const; + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketPSFBRPSI& rpsi) { + rpsi_ = rpsi; + ++num_packets_; + } + + RTCPUtility::RTCPPacketPSFBRPSI rpsi_; +}; + +class App : public PacketType { + public: + App() {} + virtual ~App() {} + + uint8_t SubType() const { return app_.SubType; } + uint32_t Name() const { return app_.Name; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketAPP& app) { + app_ = app; + ++num_packets_; + } + + RTCPUtility::RTCPPacketAPP app_; +}; + +class AppItem : public PacketType { + public: + AppItem() {} + virtual ~AppItem() {} + + uint8_t* Data() { return app_item_.Data; } + uint16_t DataLength() const { return app_item_.Size; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketAPP& app) { + app_item_ = app; + ++num_packets_; + } + + RTCPUtility::RTCPPacketAPP app_item_; +}; + +class Pli : public PacketType { + public: + Pli() {} + virtual ~Pli() {} + + uint32_t Ssrc() const { return pli_.SenderSSRC; } + uint32_t MediaSsrc() const { return pli_.MediaSSRC; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketPSFBPLI& pli) { + pli_ = pli; + ++num_packets_; + } + + RTCPUtility::RTCPPacketPSFBPLI pli_; +}; + +class Sli : public PacketType { + public: + Sli() {} + virtual ~Sli() {} + + uint32_t Ssrc() const { return sli_.SenderSSRC; } + uint32_t MediaSsrc() const { return sli_.MediaSSRC; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketPSFBSLI& sli) { + sli_ = sli; + ++num_packets_; + } + + RTCPUtility::RTCPPacketPSFBSLI sli_; +}; + +class SliItem : public PacketType { + public: + SliItem() {} + virtual ~SliItem() {} + + uint16_t FirstMb() const { return sli_item_.FirstMB; } + uint16_t NumberOfMb() const { return sli_item_.NumberOfMB; } + uint8_t PictureId() const { return sli_item_.PictureId; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketPSFBSLIItem& sli_item) { + sli_item_ = sli_item; + ++num_packets_; + } + + RTCPUtility::RTCPPacketPSFBSLIItem sli_item_; +}; + +class Fir : public PacketType { + public: + Fir() {} + virtual ~Fir() {} + + uint32_t Ssrc() const { return fir_.SenderSSRC; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketPSFBFIR& fir) { + fir_ = fir; + ++num_packets_; + } + + RTCPUtility::RTCPPacketPSFBFIR fir_; +}; + +class FirItem : public PacketType { + public: + FirItem() {} + virtual ~FirItem() {} + + uint32_t Ssrc() const { return fir_item_.SSRC; } + uint8_t SeqNum() const { return fir_item_.CommandSequenceNumber; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketPSFBFIRItem& fir_item) { + fir_item_ = fir_item; + ++num_packets_; + } + + RTCPUtility::RTCPPacketPSFBFIRItem fir_item_; +}; + +class Nack : public PacketType { + public: + Nack() {} + virtual ~Nack() {} + + uint32_t Ssrc() const { return nack_.SenderSSRC; } + uint32_t MediaSsrc() const { return nack_.MediaSSRC; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketRTPFBNACK& nack) { + nack_ = nack; + ++num_packets_; + } + + RTCPUtility::RTCPPacketRTPFBNACK nack_; +}; + +class NackItem : public PacketType { + public: + NackItem() {} + virtual ~NackItem() {} + + std::vector last_nack_list() const { + return last_nack_list_; + } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketRTPFBNACKItem& nack_item) { + last_nack_list_.push_back(nack_item.PacketID); + for (int i = 0; i < 16; ++i) { + if (nack_item.BitMask & (1 << i)) { + last_nack_list_.push_back(nack_item.PacketID + i + 1); + } + } + ++num_packets_; + } + void Clear() { last_nack_list_.clear(); } + + std::vector last_nack_list_; +}; + +class PsfbApp : public PacketType { + public: + PsfbApp() {} + virtual ~PsfbApp() {} + + uint32_t Ssrc() const { return psfb_app_.SenderSSRC; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketPSFBAPP& psfb_app) { + psfb_app_ = psfb_app; + ++num_packets_; + } + + RTCPUtility::RTCPPacketPSFBAPP psfb_app_; +}; + +class RembItem : public PacketType { + public: + RembItem() : last_bitrate_bps_(0) {} + virtual ~RembItem() {} + + int last_bitrate_bps() const { return last_bitrate_bps_; } + std::vector last_ssrc_list() { + return last_ssrc_list_; + } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketPSFBREMBItem& remb_item) { + last_bitrate_bps_ = remb_item.BitRate; + last_ssrc_list_.clear(); + last_ssrc_list_.insert( + last_ssrc_list_.end(), + remb_item.SSRCs, + remb_item.SSRCs + remb_item.NumberOfSSRCs); + ++num_packets_; + } + + uint32_t last_bitrate_bps_; + std::vector last_ssrc_list_; +}; + +class Tmmbr : public PacketType { + public: + Tmmbr() {} + virtual ~Tmmbr() {} + + uint32_t Ssrc() const { return tmmbr_.SenderSSRC; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketRTPFBTMMBR& tmmbr) { + tmmbr_ = tmmbr; + ++num_packets_; + } + + RTCPUtility::RTCPPacketRTPFBTMMBR tmmbr_; +}; + +class TmmbrItem : public PacketType { + public: + TmmbrItem() {} + virtual ~TmmbrItem() {} + + uint32_t Ssrc() const { return tmmbr_item_.SSRC; } + uint32_t BitrateKbps() const { return tmmbr_item_.MaxTotalMediaBitRate; } + uint32_t Overhead() const { return tmmbr_item_.MeasuredOverhead; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketRTPFBTMMBRItem& tmmbr_item) { + tmmbr_item_ = tmmbr_item; + ++num_packets_; + } + + RTCPUtility::RTCPPacketRTPFBTMMBRItem tmmbr_item_; +}; + + +class Tmmbn : public PacketType { + public: + Tmmbn() {} + virtual ~Tmmbn() {} + + uint32_t Ssrc() const { return tmmbn_.SenderSSRC; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketRTPFBTMMBN& tmmbn) { + tmmbn_ = tmmbn; + ++num_packets_; + } + + RTCPUtility::RTCPPacketRTPFBTMMBN tmmbn_; +}; + +class TmmbnItems : public PacketType { + public: + TmmbnItems() {} + virtual ~TmmbnItems() {} + + uint32_t Ssrc(uint8_t num) const { + assert(num < tmmbns_.size()); + return tmmbns_[num].SSRC; + } + uint32_t BitrateKbps(uint8_t num) const { + assert(num < tmmbns_.size()); + return tmmbns_[num].MaxTotalMediaBitRate; + } + uint32_t Overhead(uint8_t num) const { + assert(num < tmmbns_.size()); + return tmmbns_[num].MeasuredOverhead; + } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketRTPFBTMMBNItem& tmmbn_item) { + tmmbns_.push_back(tmmbn_item); + ++num_packets_; + } + void Clear() { tmmbns_.clear(); } + + std::vector tmmbns_; +}; + +class XrHeader : public PacketType { + public: + XrHeader() {} + virtual ~XrHeader() {} + + uint32_t Ssrc() const { return xr_header_.OriginatorSSRC; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketXR& xr_header) { + xr_header_ = xr_header; + ++num_packets_; + } + + RTCPUtility::RTCPPacketXR xr_header_; +}; + +class Rrtr : public PacketType { + public: + Rrtr() {} + virtual ~Rrtr() {} + + uint32_t NtpSec() const { return rrtr_.NTPMostSignificant; } + uint32_t NtpFrac() const { return rrtr_.NTPLeastSignificant; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketXRReceiverReferenceTimeItem& rrtr) { + rrtr_ = rrtr; + ++num_packets_; + } + + RTCPUtility::RTCPPacketXRReceiverReferenceTimeItem rrtr_; +}; + +class Dlrr : public PacketType { + public: + Dlrr() {} + virtual ~Dlrr() {} + + private: + friend class RtcpPacketParser; + + void Set() { ++num_packets_; } +}; + +class DlrrItems : public PacketType { + public: + DlrrItems() {} + virtual ~DlrrItems() {} + + uint32_t Ssrc(uint8_t num) const { + assert(num < dlrrs_.size()); + return dlrrs_[num].SSRC; + } + uint32_t LastRr(uint8_t num) const { + assert(num < dlrrs_.size()); + return dlrrs_[num].LastRR; + } + uint32_t DelayLastRr(uint8_t num) const { + assert(num < dlrrs_.size()); + return dlrrs_[num].DelayLastRR; + } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketXRDLRRReportBlockItem& dlrr) { + dlrrs_.push_back(dlrr); + ++num_packets_; + } + void Clear() { dlrrs_.clear(); } + + std::vector dlrrs_; +}; + +class VoipMetric : public PacketType { + public: + VoipMetric() {} + virtual ~VoipMetric() {} + + uint32_t Ssrc() const { return voip_metric_.SSRC; } + uint8_t LossRate() { return voip_metric_.lossRate; } + uint8_t DiscardRate() { return voip_metric_.discardRate; } + uint8_t BurstDensity() { return voip_metric_.burstDensity; } + uint8_t GapDensity() { return voip_metric_.gapDensity; } + uint16_t BurstDuration() { return voip_metric_.burstDuration; } + uint16_t GapDuration() { return voip_metric_.gapDuration; } + uint16_t RoundTripDelay() { return voip_metric_.roundTripDelay; } + uint16_t EndSystemDelay() { return voip_metric_.endSystemDelay; } + uint8_t SignalLevel() { return voip_metric_.signalLevel; } + uint8_t NoiseLevel() { return voip_metric_.noiseLevel; } + uint8_t Rerl() { return voip_metric_.RERL; } + uint8_t Gmin() { return voip_metric_.Gmin; } + uint8_t Rfactor() { return voip_metric_.Rfactor; } + uint8_t ExtRfactor() { return voip_metric_.extRfactor; } + uint8_t MosLq() { return voip_metric_.MOSLQ; } + uint8_t MosCq() { return voip_metric_.MOSCQ; } + uint8_t RxConfig() { return voip_metric_.RXconfig; } + uint16_t JbNominal() { return voip_metric_.JBnominal; } + uint16_t JbMax() { return voip_metric_.JBmax; } + uint16_t JbAbsMax() { return voip_metric_.JBabsMax; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketXRVOIPMetricItem& voip_metric) { + voip_metric_ = voip_metric; + ++num_packets_; + } + + RTCPUtility::RTCPPacketXRVOIPMetricItem voip_metric_; +}; + +class RtcpPacketParser { + public: + RtcpPacketParser(); + ~RtcpPacketParser(); + + void Parse(const void *packet, int packet_len); + + SenderReport* sender_report() { return &sender_report_; } + ReceiverReport* receiver_report() { return &receiver_report_; } + ReportBlock* report_block() { return &report_block_; } + Sdes* sdes() { return &sdes_; } + SdesChunk* sdes_chunk() { return &sdes_chunk_; } + Bye* bye() { return &bye_; } + App* app() { return &app_; } + AppItem* app_item() { return &app_item_; } + Ij* ij() { return &ij_; } + IjItem* ij_item() { return &ij_item_; } + Pli* pli() { return &pli_; } + Sli* sli() { return &sli_; } + SliItem* sli_item() { return &sli_item_; } + Rpsi* rpsi() { return &rpsi_; } + Fir* fir() { return &fir_; } + FirItem* fir_item() { return &fir_item_; } + Nack* nack() { return &nack_; } + NackItem* nack_item() { return &nack_item_; } + PsfbApp* psfb_app() { return &psfb_app_; } + RembItem* remb_item() { return &remb_item_; } + Tmmbr* tmmbr() { return &tmmbr_; } + TmmbrItem* tmmbr_item() { return &tmmbr_item_; } + Tmmbn* tmmbn() { return &tmmbn_; } + TmmbnItems* tmmbn_items() { return &tmmbn_items_; } + XrHeader* xr_header() { return &xr_header_; } + Rrtr* rrtr() { return &rrtr_; } + Dlrr* dlrr() { return &dlrr_; } + DlrrItems* dlrr_items() { return &dlrr_items_; } + VoipMetric* voip_metric() { return &voip_metric_; } + + int report_blocks_per_ssrc(uint32_t ssrc) { + return report_blocks_per_ssrc_[ssrc]; + } + + private: + SenderReport sender_report_; + ReceiverReport receiver_report_; + ReportBlock report_block_; + Sdes sdes_; + SdesChunk sdes_chunk_; + Bye bye_; + App app_; + AppItem app_item_; + Ij ij_; + IjItem ij_item_; + Pli pli_; + Sli sli_; + SliItem sli_item_; + Rpsi rpsi_; + Fir fir_; + FirItem fir_item_; + Nack nack_; + NackItem nack_item_; + PsfbApp psfb_app_; + RembItem remb_item_; + Tmmbr tmmbr_; + TmmbrItem tmmbr_item_; + Tmmbn tmmbn_; + TmmbnItems tmmbn_items_; + XrHeader xr_header_; + Rrtr rrtr_; + Dlrr dlrr_; + DlrrItems dlrr_items_; + VoipMetric voip_metric_; + + std::map report_blocks_per_ssrc_; +}; +} // namespace test +} // namespace webrtc +#endif // WEBRTC_TEST_RTCP_PACKET_PARSER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/rtp_file_reader.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/rtp_file_reader.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/rtp_file_reader.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/rtp_file_reader.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,609 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/test/rtp_file_reader.h" + +#include + +#include +#include +#include + +#include "webrtc/base/checks.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { +namespace test { + +static const size_t kFirstLineLength = 40; +static uint16_t kPacketHeaderSize = 8; + +#if 1 +# define DEBUG_LOG(text) +# define DEBUG_LOG1(text, arg) +#else +# define DEBUG_LOG(text) (printf(text "\n")) +# define DEBUG_LOG1(text, arg) (printf(text "\n", arg)) +#endif + +#define TRY(expr) \ + do { \ + if (!(expr)) { \ + DEBUG_LOG1("FAIL at " __FILE__ ":%d", __LINE__); \ + return false; \ + } \ + } while (0) + +class RtpFileReaderImpl : public RtpFileReader { + public: + virtual bool Init(const std::string& filename) = 0; +}; + +// Read RTP packets from file in rtpdump format, as documented at: +// http://www.cs.columbia.edu/irt/software/rtptools/ +class RtpDumpReader : public RtpFileReaderImpl { + public: + RtpDumpReader() : file_(NULL) {} + virtual ~RtpDumpReader() { + if (file_ != NULL) { + fclose(file_); + file_ = NULL; + } + } + + bool Init(const std::string& filename) { + file_ = fopen(filename.c_str(), "rb"); + if (file_ == NULL) { + printf("ERROR: Can't open file: %s\n", filename.c_str()); + return false; + } + + char firstline[kFirstLineLength + 1] = {0}; + if (fgets(firstline, kFirstLineLength, file_) == NULL) { + DEBUG_LOG("ERROR: Can't read from file\n"); + return false; + } + if (strncmp(firstline, "#!rtpplay", 9) == 0) { + if (strncmp(firstline, "#!rtpplay1.0", 12) != 0) { + DEBUG_LOG("ERROR: wrong rtpplay version, must be 1.0\n"); + return false; + } + } else if (strncmp(firstline, "#!RTPencode", 11) == 0) { + if (strncmp(firstline, "#!RTPencode1.0", 14) != 0) { + DEBUG_LOG("ERROR: wrong RTPencode version, must be 1.0\n"); + return false; + } + } else { + DEBUG_LOG("ERROR: wrong file format of input file\n"); + return false; + } + + uint32_t start_sec; + uint32_t start_usec; + uint32_t source; + uint16_t port; + uint16_t padding; + TRY(Read(&start_sec)); + TRY(Read(&start_usec)); + TRY(Read(&source)); + TRY(Read(&port)); + TRY(Read(&padding)); + + return true; + } + + virtual bool NextPacket(Packet* packet) OVERRIDE { + uint8_t* rtp_data = packet->data; + packet->length = Packet::kMaxPacketBufferSize; + + uint16_t len; + uint16_t plen; + uint32_t offset; + TRY(Read(&len)); + TRY(Read(&plen)); + TRY(Read(&offset)); + + // Use 'len' here because a 'plen' of 0 specifies rtcp. + len -= kPacketHeaderSize; + if (packet->length < len) { + FATAL() << "Packet is too large to fit: " << len << " bytes vs " + << packet->length + << " bytes allocated. Consider increasing the buffer " + "size"; + } + if (fread(rtp_data, 1, len, file_) != len) { + return false; + } + + packet->length = len; + packet->original_length = plen; + packet->time_ms = offset; + return true; + } + + private: + bool Read(uint32_t* out) { + *out = 0; + for (size_t i = 0; i < 4; ++i) { + *out <<= 8; + uint8_t tmp; + if (fread(&tmp, 1, sizeof(uint8_t), file_) != sizeof(uint8_t)) + return false; + *out |= tmp; + } + return true; + } + + bool Read(uint16_t* out) { + *out = 0; + for (size_t i = 0; i < 2; ++i) { + *out <<= 8; + uint8_t tmp; + if (fread(&tmp, 1, sizeof(uint8_t), file_) != sizeof(uint8_t)) + return false; + *out |= tmp; + } + return true; + } + + FILE* file_; + + DISALLOW_COPY_AND_ASSIGN(RtpDumpReader); +}; + +enum { + kResultFail = -1, + kResultSuccess = 0, + kResultSkip = 1, + + kPcapVersionMajor = 2, + kPcapVersionMinor = 4, + kLinktypeNull = 0, + kLinktypeEthernet = 1, + kBsdNullLoopback1 = 0x00000002, + kBsdNullLoopback2 = 0x02000000, + kEthernetIIHeaderMacSkip = 12, + kEthertypeIp = 0x0800, + kIpVersion4 = 4, + kMinIpHeaderLength = 20, + kFragmentOffsetClear = 0x0000, + kFragmentOffsetDoNotFragment = 0x4000, + kProtocolTcp = 0x06, + kProtocolUdp = 0x11, + kUdpHeaderLength = 8, + kMaxReadBufferSize = 4096 +}; + +const uint32_t kPcapBOMSwapOrder = 0xd4c3b2a1UL; +const uint32_t kPcapBOMNoSwapOrder = 0xa1b2c3d4UL; + +#define TRY_PCAP(expr) \ + do { \ + int r = (expr); \ + if (r == kResultFail) { \ + DEBUG_LOG1("FAIL at " __FILE__ ":%d", __LINE__); \ + return kResultFail; \ + } else if (r == kResultSkip) { \ + return kResultSkip; \ + } \ + } while (0) + +// Read RTP packets from file in tcpdump/libpcap format, as documented at: +// http://wiki.wireshark.org/Development/LibpcapFileFormat +class PcapReader : public RtpFileReaderImpl { + public: + PcapReader() + : file_(NULL), + swap_pcap_byte_order_(false), +#ifdef WEBRTC_ARCH_BIG_ENDIAN + swap_network_byte_order_(false), +#else + swap_network_byte_order_(true), +#endif + read_buffer_(), + packets_by_ssrc_(), + packets_(), + next_packet_it_() { + } + + virtual ~PcapReader() { + if (file_ != NULL) { + fclose(file_); + file_ = NULL; + } + } + + bool Init(const std::string& filename) OVERRIDE { + return Initialize(filename) == kResultSuccess; + } + + int Initialize(const std::string& filename) { + file_ = fopen(filename.c_str(), "rb"); + if (file_ == NULL) { + printf("ERROR: Can't open file: %s\n", filename.c_str()); + return kResultFail; + } + + if (ReadGlobalHeader() < 0) { + return kResultFail; + } + + int total_packet_count = 0; + uint32_t stream_start_ms = 0; + int32_t next_packet_pos = ftell(file_); + for (;;) { + TRY_PCAP(fseek(file_, next_packet_pos, SEEK_SET)); + int result = ReadPacket(&next_packet_pos, stream_start_ms, + ++total_packet_count); + if (result == kResultFail) { + break; + } else if (result == kResultSuccess && packets_.size() == 1) { + assert(stream_start_ms == 0); + PacketIterator it = packets_.begin(); + stream_start_ms = it->time_offset_ms; + it->time_offset_ms = 0; + } + } + + if (feof(file_) == 0) { + printf("Failed reading file!\n"); + return kResultFail; + } + + printf("Total packets in file: %d\n", total_packet_count); + printf("Total RTP/RTCP packets: %d\n", static_cast(packets_.size())); + + for (SsrcMapIterator mit = packets_by_ssrc_.begin(); + mit != packets_by_ssrc_.end(); ++mit) { + uint32_t ssrc = mit->first; + const std::vector& packet_numbers = mit->second; + uint8_t pt = packets_[packet_numbers[0]].rtp_header.payloadType; + printf("SSRC: %08x, %d packets, pt=%d\n", ssrc, + static_cast(packet_numbers.size()), pt); + } + + // TODO(solenberg): Better validation of identified SSRC streams. + // + // Since we're dealing with raw network data here, we will wrongly identify + // some packets as RTP. When these packets are consumed by RtpPlayer, they + // are unlikely to cause issues as they will ultimately be filtered out by + // the RtpRtcp module. However, we should really do better filtering here, + // which we can accomplish in a number of ways, e.g.: + // + // - Verify that the time stamps and sequence numbers for RTP packets are + // both increasing/decreasing. If they move in different directions, the + // SSRC is likely bogus and can be dropped. (Normally they should be inc- + // reasing but we must allow packet reordering). + // - If RTP sequence number is not changing, drop the stream. + // - Can also use srcip:port->dstip:port pairs, assuming few SSRC collisions + // for up/down streams. + + next_packet_it_ = packets_.begin(); + return kResultSuccess; + } + + virtual bool NextPacket(Packet* packet) OVERRIDE { + uint32_t length = Packet::kMaxPacketBufferSize; + if (NextPcap(packet->data, &length, &packet->time_ms) != kResultSuccess) + return false; + packet->length = static_cast(length); + packet->original_length = packet->length; + return true; + } + + virtual int NextPcap(uint8_t* data, uint32_t* length, uint32_t* time_ms) { + assert(data); + assert(length); + assert(time_ms); + + if (next_packet_it_ == packets_.end()) { + return -1; + } + if (*length < next_packet_it_->payload_length) { + return -1; + } + TRY_PCAP(fseek(file_, next_packet_it_->pos_in_file, SEEK_SET)); + TRY_PCAP(Read(data, next_packet_it_->payload_length)); + *length = next_packet_it_->payload_length; + *time_ms = next_packet_it_->time_offset_ms; + next_packet_it_++; + + return 0; + } + + private: + // A marker of an RTP packet within the file. + struct RtpPacketMarker { + uint32_t packet_number; // One-based index (like in WireShark) + uint32_t time_offset_ms; + uint32_t source_ip; + uint32_t dest_ip; + uint16_t source_port; + uint16_t dest_port; + RTPHeader rtp_header; + int32_t pos_in_file; // Byte offset of payload from start of file. + uint32_t payload_length; + }; + + typedef std::vector::iterator PacketIterator; + typedef std::map > SsrcMap; + typedef std::map >::iterator SsrcMapIterator; + + int ReadGlobalHeader() { + uint32_t magic; + TRY_PCAP(Read(&magic, false)); + if (magic == kPcapBOMSwapOrder) { + swap_pcap_byte_order_ = true; + } else if (magic == kPcapBOMNoSwapOrder) { + swap_pcap_byte_order_ = false; + } else { + return kResultFail; + } + + uint16_t version_major; + uint16_t version_minor; + TRY_PCAP(Read(&version_major, false)); + TRY_PCAP(Read(&version_minor, false)); + if (version_major != kPcapVersionMajor || + version_minor != kPcapVersionMinor) { + return kResultFail; + } + + int32_t this_zone; // GMT to local correction. + uint32_t sigfigs; // Accuracy of timestamps. + uint32_t snaplen; // Max length of captured packets, in octets. + uint32_t network; // Data link type. + TRY_PCAP(Read(&this_zone, false)); + TRY_PCAP(Read(&sigfigs, false)); + TRY_PCAP(Read(&snaplen, false)); + TRY_PCAP(Read(&network, false)); + + // Accept only LINKTYPE_NULL and LINKTYPE_ETHERNET. + // See: http://www.tcpdump.org/linktypes.html + if (network != kLinktypeNull && network != kLinktypeEthernet) { + return kResultFail; + } + + return kResultSuccess; + } + + int ReadPacket(int32_t* next_packet_pos, uint32_t stream_start_ms, + uint32_t number) { + assert(next_packet_pos); + + uint32_t ts_sec; // Timestamp seconds. + uint32_t ts_usec; // Timestamp microseconds. + uint32_t incl_len; // Number of octets of packet saved in file. + uint32_t orig_len; // Actual length of packet. + TRY_PCAP(Read(&ts_sec, false)); + TRY_PCAP(Read(&ts_usec, false)); + TRY_PCAP(Read(&incl_len, false)); + TRY_PCAP(Read(&orig_len, false)); + + *next_packet_pos = ftell(file_) + incl_len; + + RtpPacketMarker marker = {0}; + marker.packet_number = number; + marker.time_offset_ms = CalcTimeDelta(ts_sec, ts_usec, stream_start_ms); + TRY_PCAP(ReadPacketHeader(&marker)); + marker.pos_in_file = ftell(file_); + + if (marker.payload_length > sizeof(read_buffer_)) { + printf("Packet too large!\n"); + return kResultFail; + } + TRY_PCAP(Read(read_buffer_, marker.payload_length)); + + RtpUtility::RtpHeaderParser rtp_parser(read_buffer_, marker.payload_length); + if (rtp_parser.RTCP()) { + rtp_parser.ParseRtcp(&marker.rtp_header); + packets_.push_back(marker); + } else { + if (!rtp_parser.Parse(marker.rtp_header, NULL)) { + DEBUG_LOG("Not recognized as RTP/RTCP"); + return kResultSkip; + } + + uint32_t ssrc = marker.rtp_header.ssrc; + packets_by_ssrc_[ssrc].push_back(marker.packet_number); + packets_.push_back(marker); + } + + return kResultSuccess; + } + + int ReadPacketHeader(RtpPacketMarker* marker) { + int32_t file_pos = ftell(file_); + + // Check for BSD null/loopback frame header. The header is just 4 bytes in + // native byte order, so we check for both versions as we don't care about + // the header as such and will likely fail reading the IP header if this is + // something else than null/loopback. + uint32_t protocol; + TRY_PCAP(Read(&protocol, true)); + if (protocol == kBsdNullLoopback1 || protocol == kBsdNullLoopback2) { + int result = ReadXxpIpHeader(marker); + DEBUG_LOG("Recognized loopback frame"); + if (result != kResultSkip) { + return result; + } + } + + TRY_PCAP(fseek(file_, file_pos, SEEK_SET)); + + // Check for Ethernet II, IP frame header. + uint16_t type; + TRY_PCAP(Skip(kEthernetIIHeaderMacSkip)); // Source+destination MAC. + TRY_PCAP(Read(&type, true)); + if (type == kEthertypeIp) { + int result = ReadXxpIpHeader(marker); + DEBUG_LOG("Recognized ethernet 2 frame"); + if (result != kResultSkip) { + return result; + } + } + + return kResultSkip; + } + + uint32_t CalcTimeDelta(uint32_t ts_sec, uint32_t ts_usec, uint32_t start_ms) { + // Round to nearest ms. + uint64_t t2_ms = ((static_cast(ts_sec) * 1000000) + ts_usec + + 500) / 1000; + uint64_t t1_ms = static_cast(start_ms); + if (t2_ms < t1_ms) { + return 0; + } else { + return t2_ms - t1_ms; + } + } + + int ReadXxpIpHeader(RtpPacketMarker* marker) { + assert(marker); + + uint16_t version; + uint16_t length; + uint16_t id; + uint16_t fragment; + uint16_t protocol; + uint16_t checksum; + TRY_PCAP(Read(&version, true)); + TRY_PCAP(Read(&length, true)); + TRY_PCAP(Read(&id, true)); + TRY_PCAP(Read(&fragment, true)); + TRY_PCAP(Read(&protocol, true)); + TRY_PCAP(Read(&checksum, true)); + TRY_PCAP(Read(&marker->source_ip, true)); + TRY_PCAP(Read(&marker->dest_ip, true)); + + if (((version >> 12) & 0x000f) != kIpVersion4) { + DEBUG_LOG("IP header is not IPv4"); + return kResultSkip; + } + + if (fragment != kFragmentOffsetClear && + fragment != kFragmentOffsetDoNotFragment) { + DEBUG_LOG("IP fragments cannot be handled"); + return kResultSkip; + } + + // Skip remaining fields of IP header. + uint16_t header_length = (version & 0x0f00) >> (8 - 2); + assert(header_length >= kMinIpHeaderLength); + TRY_PCAP(Skip(header_length - kMinIpHeaderLength)); + + protocol = protocol & 0x00ff; + if (protocol == kProtocolTcp) { + DEBUG_LOG("TCP packets are not handled"); + return kResultSkip; + } else if (protocol == kProtocolUdp) { + uint16_t length; + uint16_t checksum; + TRY_PCAP(Read(&marker->source_port, true)); + TRY_PCAP(Read(&marker->dest_port, true)); + TRY_PCAP(Read(&length, true)); + TRY_PCAP(Read(&checksum, true)); + marker->payload_length = length - kUdpHeaderLength; + } else { + DEBUG_LOG("Unknown transport (expected UDP or TCP)"); + return kResultSkip; + } + + return kResultSuccess; + } + + int Read(uint32_t* out, bool expect_network_order) { + uint32_t tmp = 0; + if (fread(&tmp, 1, sizeof(uint32_t), file_) != sizeof(uint32_t)) { + return kResultFail; + } + if ((!expect_network_order && swap_pcap_byte_order_) || + (expect_network_order && swap_network_byte_order_)) { + tmp = ((tmp >> 24) & 0x000000ff) | (tmp << 24) | + ((tmp >> 8) & 0x0000ff00) | ((tmp << 8) & 0x00ff0000); + } + *out = tmp; + return kResultSuccess; + } + + int Read(uint16_t* out, bool expect_network_order) { + uint16_t tmp = 0; + if (fread(&tmp, 1, sizeof(uint16_t), file_) != sizeof(uint16_t)) { + return kResultFail; + } + if ((!expect_network_order && swap_pcap_byte_order_) || + (expect_network_order && swap_network_byte_order_)) { + tmp = ((tmp >> 8) & 0x00ff) | (tmp << 8); + } + *out = tmp; + return kResultSuccess; + } + + int Read(uint8_t* out, uint32_t count) { + if (fread(out, 1, count, file_) != count) { + return kResultFail; + } + return kResultSuccess; + } + + int Read(int32_t* out, bool expect_network_order) { + int32_t tmp = 0; + if (fread(&tmp, 1, sizeof(uint32_t), file_) != sizeof(uint32_t)) { + return kResultFail; + } + if ((!expect_network_order && swap_pcap_byte_order_) || + (expect_network_order && swap_network_byte_order_)) { + tmp = ((tmp >> 24) & 0x000000ff) | (tmp << 24) | + ((tmp >> 8) & 0x0000ff00) | ((tmp << 8) & 0x00ff0000); + } + *out = tmp; + return kResultSuccess; + } + + int Skip(uint32_t length) { + if (fseek(file_, length, SEEK_CUR) != 0) { + return kResultFail; + } + return kResultSuccess; + } + + FILE* file_; + bool swap_pcap_byte_order_; + const bool swap_network_byte_order_; + uint8_t read_buffer_[kMaxReadBufferSize]; + + SsrcMap packets_by_ssrc_; + std::vector packets_; + PacketIterator next_packet_it_; + + DISALLOW_COPY_AND_ASSIGN(PcapReader); +}; + +RtpFileReader* RtpFileReader::Create(FileFormat format, + const std::string& filename) { + RtpFileReaderImpl* reader = NULL; + switch (format) { + case kPcap: + reader = new PcapReader(); + break; + case kRtpDump: + reader = new RtpDumpReader(); + break; + } + if (!reader->Init(filename)) { + delete reader; + return NULL; + } + return reader; +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/rtp_file_reader.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/rtp_file_reader.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/rtp_file_reader.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/rtp_file_reader.h 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef WEBRTC_TEST_RTP_FILE_READER_H_ +#define WEBRTC_TEST_RTP_FILE_READER_H_ + +#include + +#include "webrtc/common_types.h" + +namespace webrtc { +namespace test { +class RtpFileReader { + public: + enum FileFormat { + kPcap, + kRtpDump, + }; + + struct Packet { + // Accommodate for 50 ms packets of 32 kHz PCM16 samples (3200 bytes) plus + // some overhead. + static const size_t kMaxPacketBufferSize = 3500; + uint8_t data[kMaxPacketBufferSize]; + size_t length; + // The length the packet had on wire. Will be different from |length| when + // reading a header-only RTP dump. + size_t original_length; + + uint32_t time_ms; + }; + + virtual ~RtpFileReader() {} + static RtpFileReader* Create(FileFormat format, + const std::string& filename); + + virtual bool NextPacket(Packet* packet) = 0; +}; +} // namespace test +} // namespace webrtc +#endif // WEBRTC_TEST_RTP_FILE_READER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/rtp_file_reader_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/rtp_file_reader_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/rtp_file_reader_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/rtp_file_reader_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/test/rtp_file_reader.h" +#include "webrtc/test/testsupport/fileutils.h" + +namespace webrtc { + +class TestRtpFileReader : public ::testing::Test { + public: + void Init(const std::string& filename, bool headers_only_file) { + std::string filepath = + test::ResourcePath("video_coding/" + filename, "rtp"); + rtp_packet_source_.reset( + test::RtpFileReader::Create(test::RtpFileReader::kRtpDump, filepath)); + ASSERT_TRUE(rtp_packet_source_.get() != NULL); + headers_only_file_ = headers_only_file; + } + + int CountRtpPackets() { + test::RtpFileReader::Packet packet; + int c = 0; + while (rtp_packet_source_->NextPacket(&packet)) { + if (headers_only_file_) + EXPECT_LT(packet.length, packet.original_length); + else + EXPECT_EQ(packet.length, packet.original_length); + c++; + } + return c; + } + + private: + scoped_ptr rtp_packet_source_; + bool headers_only_file_; +}; + +TEST_F(TestRtpFileReader, Test60Packets) { + Init("pltype103", false); + EXPECT_EQ(60, CountRtpPackets()); +} + +TEST_F(TestRtpFileReader, Test60PacketsHeaderOnly) { + Init("pltype103_header_only", true); + EXPECT_EQ(60, CountRtpPackets()); +} + +typedef std::map PacketsPerSsrc; + +class TestPcapFileReader : public ::testing::Test { + public: + void Init(const std::string& filename) { + std::string filepath = + test::ResourcePath("video_coding/" + filename, "pcap"); + rtp_packet_source_.reset( + test::RtpFileReader::Create(test::RtpFileReader::kPcap, filepath)); + ASSERT_TRUE(rtp_packet_source_.get() != NULL); + } + + int CountRtpPackets() { + int c = 0; + test::RtpFileReader::Packet packet; + while (rtp_packet_source_->NextPacket(&packet)) { + EXPECT_EQ(packet.length, packet.original_length); + c++; + } + return c; + } + + PacketsPerSsrc CountRtpPacketsPerSsrc() { + PacketsPerSsrc pps; + test::RtpFileReader::Packet packet; + while (rtp_packet_source_->NextPacket(&packet)) { + RtpUtility::RtpHeaderParser rtp_header_parser(packet.data, packet.length); + webrtc::RTPHeader header; + if (!rtp_header_parser.RTCP() && rtp_header_parser.Parse(header, NULL)) { + pps[header.ssrc]++; + } + } + return pps; + } + + private: + scoped_ptr rtp_packet_source_; +}; + +TEST_F(TestPcapFileReader, TestEthernetIIFrame) { + Init("frame-ethernet-ii"); + EXPECT_EQ(368, CountRtpPackets()); +} + +TEST_F(TestPcapFileReader, TestLoopbackFrame) { + Init("frame-loopback"); + EXPECT_EQ(491, CountRtpPackets()); +} + +TEST_F(TestPcapFileReader, TestTwoSsrc) { + Init("ssrcs-2"); + PacketsPerSsrc pps = CountRtpPacketsPerSsrc(); + EXPECT_EQ(2UL, pps.size()); + EXPECT_EQ(370, pps[0x78d48f61]); + EXPECT_EQ(60, pps[0xae94130b]); +} + +TEST_F(TestPcapFileReader, TestThreeSsrc) { + Init("ssrcs-3"); + PacketsPerSsrc pps = CountRtpPacketsPerSsrc(); + EXPECT_EQ(3UL, pps.size()); + EXPECT_EQ(162, pps[0x938c5eaa]); + EXPECT_EQ(113, pps[0x59fe6ef0]); + EXPECT_EQ(61, pps[0xed2bd2ac]); +} +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/rtp_rtcp_observer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/rtp_rtcp_observer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/rtp_rtcp_observer.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/rtp_rtcp_observer.h 2015-02-03 14:33:38.000000000 +0000 @@ -16,6 +16,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" +#include "webrtc/test/direct_transport.h" #include "webrtc/typedefs.h" #include "webrtc/video_send_stream.h" @@ -33,8 +34,8 @@ return &receive_transport_; } - void SetReceivers(PacketReceiver* send_transport_receiver, - PacketReceiver* receive_transport_receiver) { + virtual void SetReceivers(PacketReceiver* send_transport_receiver, + PacketReceiver* receive_transport_receiver) { send_transport_.SetReceiver(send_transport_receiver); receive_transport_.SetReceiver(receive_transport_receiver); } @@ -53,15 +54,15 @@ protected: RtpRtcpObserver(unsigned int event_timeout_ms, const FakeNetworkPipe::Config& configuration) - : lock_(CriticalSectionWrapper::CreateCriticalSection()), + : crit_(CriticalSectionWrapper::CreateCriticalSection()), observation_complete_(EventWrapper::Create()), parser_(RtpHeaderParser::Create()), - send_transport_(lock_.get(), + send_transport_(crit_.get(), this, &RtpRtcpObserver::OnSendRtp, &RtpRtcpObserver::OnSendRtcp, configuration), - receive_transport_(lock_.get(), + receive_transport_(crit_.get(), this, &RtpRtcpObserver::OnReceiveRtp, &RtpRtcpObserver::OnReceiveRtcp, @@ -69,15 +70,15 @@ timeout_ms_(event_timeout_ms) {} explicit RtpRtcpObserver(unsigned int event_timeout_ms) - : lock_(CriticalSectionWrapper::CreateCriticalSection()), + : crit_(CriticalSectionWrapper::CreateCriticalSection()), observation_complete_(EventWrapper::Create()), parser_(RtpHeaderParser::Create()), - send_transport_(lock_.get(), + send_transport_(crit_.get(), this, &RtpRtcpObserver::OnSendRtp, &RtpRtcpObserver::OnSendRtcp, FakeNetworkPipe::Config()), - receive_transport_(lock_.get(), + receive_transport_(crit_.get(), this, &RtpRtcpObserver::OnReceiveRtp, &RtpRtcpObserver::OnReceiveRtcp, @@ -89,23 +90,26 @@ DROP_PACKET, }; - virtual Action OnSendRtp(const uint8_t* packet, size_t length) { + virtual Action OnSendRtp(const uint8_t* packet, size_t length) + EXCLUSIVE_LOCKS_REQUIRED(crit_) { return SEND_PACKET; } - virtual Action OnSendRtcp(const uint8_t* packet, size_t length) { + virtual Action OnSendRtcp(const uint8_t* packet, size_t length) + EXCLUSIVE_LOCKS_REQUIRED(crit_) { return SEND_PACKET; } - virtual Action OnReceiveRtp(const uint8_t* packet, size_t length) { + virtual Action OnReceiveRtp(const uint8_t* packet, size_t length) + EXCLUSIVE_LOCKS_REQUIRED(crit_) { return SEND_PACKET; } - virtual Action OnReceiveRtcp(const uint8_t* packet, size_t length) { + virtual Action OnReceiveRtcp(const uint8_t* packet, size_t length) + EXCLUSIVE_LOCKS_REQUIRED(crit_) { return SEND_PACKET; } - private: class PacketTransport : public test::DirectTransport { public: @@ -118,17 +122,17 @@ PacketTransportAction on_rtcp, const FakeNetworkPipe::Config& configuration) : test::DirectTransport(configuration), - lock_(lock), + crit_(lock), observer_(observer), on_rtp_(on_rtp), on_rtcp_(on_rtcp) {} private: virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE { - EXPECT_FALSE(RtpHeaderParser::IsRtcp(packet, static_cast(length))); + EXPECT_FALSE(RtpHeaderParser::IsRtcp(packet, length)); Action action; { - CriticalSectionScoped crit_(lock_); + CriticalSectionScoped lock(crit_); action = (observer_->*on_rtp_)(packet, length); } switch (action) { @@ -142,10 +146,10 @@ } virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE { - EXPECT_TRUE(RtpHeaderParser::IsRtcp(packet, static_cast(length))); + EXPECT_TRUE(RtpHeaderParser::IsRtcp(packet, length)); Action action; { - CriticalSectionScoped crit_(lock_); + CriticalSectionScoped lock(crit_); action = (observer_->*on_rtcp_)(packet, length); } switch (action) { @@ -159,16 +163,16 @@ } // Pointer to shared lock instance protecting on_rtp_/on_rtcp_ calls. - CriticalSectionWrapper* lock_; + CriticalSectionWrapper* const crit_; - RtpRtcpObserver* observer_; - PacketTransportAction on_rtp_, on_rtcp_; + RtpRtcpObserver* const observer_; + const PacketTransportAction on_rtp_, on_rtcp_; }; protected: - scoped_ptr lock_; - scoped_ptr observation_complete_; - scoped_ptr parser_; + const scoped_ptr crit_; + const scoped_ptr observation_complete_; + const scoped_ptr parser_; private: PacketTransport send_transport_, receive_transport_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/run_loop.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/run_loop.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/run_loop.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/run_loop.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "webrtc/test/run_loop.h" + +#include + +namespace webrtc { +namespace test { + +void PressEnterToContinue() { + puts(">> Press ENTER to continue..."); + while (getc(stdin) != '\n' && !feof(stdin)); +} +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/run_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/run_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/run_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/run_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/test/run_test.h" + +#include + +namespace webrtc { +namespace test { + +void RunTest(void(*test)()) { + (*test)(); +} + +} // namespace test +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/run_test.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/run_test.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/run_test.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/run_test.h 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef WEBRTC_TEST_RUN_TEST_H +#define WEBRTC_TEST_RUN_TEST_H + +namespace webrtc { +namespace test { + +// Running a test function on a separate thread, if required by the OS. +void RunTest(void(*test)()); + +} // namespace test +} // namespace webrtc + +#endif // WEBRTC_TEST_RUN_TEST_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/run_tests.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/run_tests.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/run_tests.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/run_tests.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#ifndef WEBRTC_VIDEO_ENGINE_TEST_COMMON_TEST_RUNNER_H_ -#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_TEST_RUNNER_H_ - -namespace webrtc { -namespace test { - -// Blocks until the user presses enter. -void PressEnterToContinue(); - -// Performs platform-dependent initializations and calls gtest's -// RUN_ALL_TESTS(). -int RunAllTests(); -} // namespace test -} // namespace webrtc - -#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_TEST_RUNNER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/test.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/test.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/test.gyp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/test.gyp 2015-02-03 14:33:38.000000000 +0000 @@ -53,19 +53,51 @@ ], }, { + 'target_name': 'rtp_test_utils', + 'type': 'static_library', + 'sources': [ + 'rtcp_packet_parser.cc', + 'rtcp_packet_parser.h', + 'rtp_file_reader.cc', + 'rtp_file_reader.h', + ], + 'dependencies': [ + '<(webrtc_root)/modules/modules.gyp:rtp_rtcp', + ], + }, + { + 'target_name': 'field_trial', + 'type': 'static_library', + 'sources': [ + 'field_trial.cc', + 'field_trial.h', + ], + 'dependencies': [ + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + ], + }, + { + 'target_name': 'test_main', + 'type': 'static_library', + 'sources': [ + 'test_main.cc', + ], + 'dependencies': [ + 'field_trial', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:metrics_default', + ], + }, + { 'target_name': 'test_support', 'type': 'static_library', 'dependencies': [ '<(DEPTH)/testing/gtest.gyp:gtest', '<(DEPTH)/testing/gmock.gyp:gmock', - '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', ], 'sources': [ - 'test_suite.cc', - 'test_suite.h', - 'testsupport/android/root_path_android.cc', - 'testsupport/android/root_path_android_chromium.cc', 'testsupport/fileutils.cc', 'testsupport/fileutils.h', 'testsupport/frame_reader.cc', @@ -83,22 +115,6 @@ 'testsupport/trace_to_stderr.cc', 'testsupport/trace_to_stderr.h', ], - 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are using - # Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { - 'dependencies': [ - '<(DEPTH)/base/base.gyp:base', - ], - 'sources!': [ - 'testsupport/android/root_path_android.cc', - ], - }, { - 'sources!': [ - 'testsupport/android/root_path_android_chromium.cc', - ], - }], - ], }, { # Depend on this target when you want to have test_support but also the @@ -106,10 +122,17 @@ 'target_name': 'test_support_main', 'type': 'static_library', 'dependencies': [ + 'field_trial', 'test_support', + '<(DEPTH)/testing/gmock.gyp:gmock', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:metrics_default', ], 'sources': [ 'run_all_unittests.cc', + 'test_suite.cc', + 'test_suite.h', ], }, { @@ -142,6 +165,7 @@ 'channel_transport/udp_transport_unittest.cc', 'channel_transport/udp_socket_manager_unittest.cc', 'channel_transport/udp_socket_wrapper_unittest.cc', + 'testsupport/always_passing_unittest.cc', 'testsupport/unittest_utils.h', 'testsupport/fileutils_unittest.cc', 'testsupport/frame_reader_unittest.cc', @@ -154,9 +178,7 @@ 4267, # size_t to int truncation. ], 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are - # using Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['OS=="android"', { 'dependencies': [ '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code', ], @@ -165,33 +187,7 @@ }, ], 'conditions': [ - ['build_with_chromium==0', { - 'targets': [ - { - 'target_name': 'buildbot_tests_scripts', - 'type': 'none', - 'copies': [ - { - 'destination': '<(PRODUCT_DIR)', - 'files': [ - 'buildbot_tests.py', - '<(webrtc_root)/tools/e2e_quality/audio/run_audio_test.py', - ], - }, - { - 'destination': '<(PRODUCT_DIR)/perf', - 'files': [ - '<(DEPTH)/tools/perf/__init__.py', - '<(DEPTH)/tools/perf/perf_utils.py', - ], - }, - ], - }, # target buildbot_tests_scripts - ], - }], - # TODO(henrike): remove build_with_chromium==1 when the bots are using - # Chromium's buildbots. - ['include_tests==1 and build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['include_tests==1 and OS=="android"', { 'targets': [ { 'target_name': 'test_support_unittests_apk_target', @@ -212,7 +208,6 @@ ], 'includes': [ '../build/isolate.gypi', - 'test_support_unittests.isolate', ], 'sources': [ 'test_support_unittests.isolate', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/test_main.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/test_main.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/test_main.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/test_main.cc 2015-02-03 14:33:38.000000000 +0000 @@ -8,16 +8,25 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include "gflags/gflags.h" #include "testing/gtest/include/gtest/gtest.h" - -#include "webrtc/test/flags.h" -#include "webrtc/test/run_tests.h" +#include "webrtc/test/field_trial.h" #include "webrtc/test/testsupport/fileutils.h" +DEFINE_string(force_fieldtrials, "", + "Field trials control experimental feature code which can be forced. " + "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enable/" + " will assign the group Enable to field trial WebRTC-FooFeature."); + int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); - webrtc::test::flags::Init(&argc, &argv); - webrtc::test::SetExecutablePath(argv[0]); - return webrtc::test::RunAllTests(); + // AllowCommandLineParsing allows us to ignore flags passed on to us by + // Chromium build bots without having to explicitly disable them. + google::AllowCommandLineReparsing(); + google::ParseCommandLineFlags(&argc, &argv, false); + + webrtc::test::SetExecutablePath(argv[0]); + webrtc::test::InitFieldTrialsFromString(FLAGS_force_fieldtrials); + return RUN_ALL_TESTS(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/test_suite.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/test_suite.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/test_suite.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/test_suite.cc 2015-02-03 14:33:38.000000000 +0000 @@ -15,9 +15,15 @@ #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/test/testsupport/fileutils.h" #include "webrtc/test/testsupport/trace_to_stderr.h" +#include "webrtc/test/field_trial.h" DEFINE_bool(logs, false, "print logs to stderr"); +DEFINE_string(force_fieldtrials, "", + "Field trials control experimental feature code which can be forced. " + "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enable/" + " will assign the group Enable to field trial WebRTC-FooFeature."); + namespace webrtc { namespace test { @@ -28,6 +34,8 @@ // Chromium build bots without having to explicitly disable them. google::AllowCommandLineReparsing(); google::ParseCommandLineFlags(&argc, &argv, true); + + webrtc::test::InitFieldTrialsFromString(FLAGS_force_fieldtrials); } TestSuite::~TestSuite() { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/test_suite.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/test_suite.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/test_suite.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/test_suite.h 2015-02-03 14:33:38.000000000 +0000 @@ -17,7 +17,7 @@ // instantiate this class in your main function and call its Run method to run // any gtest based tests that are linked into your executable. -#include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/base/constructormagic.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/always_passing_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/always_passing_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/always_passing_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/always_passing_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "testing/gtest/include/gtest/gtest.h" + +namespace webrtc { + +// A test that always passes. Useful when all tests in a executable are +// disabled, since a gtest returns exit code 1 if no tests have executed. +TEST(AlwaysPassingTest, AlwaysPassingTest) {} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/android/root_path_android.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/android/root_path_android.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/android/root_path_android.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/android/root_path_android.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -namespace webrtc { -namespace test { - -static const char* kRootDirName = "/sdcard/"; -std::string ProjectRootPathAndroid() { - return kRootDirName; -} - -std::string OutputPathAndroid() { - return kRootDirName; -} - -} // namespace test -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/android/root_path_android_chromium.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/android/root_path_android_chromium.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/android/root_path_android_chromium.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/android/root_path_android_chromium.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "base/android/path_utils.h" -#include "base/files/file_path.h" - -namespace webrtc { -namespace test { - -std::string OutputPathImpl(); - -// This file is only compiled when running WebRTC tests in a Chromium workspace. -// The Android testing framework will push files relative to the root path of -// the Chromium workspace. The root path for webrtc is one directory up from -// trunk/webrtc (in standalone) or src/third_party/webrtc (in Chromium). -std::string ProjectRootPathAndroid() { - base::FilePath root_path; - base::android::GetExternalStorageDirectory(&root_path); - return root_path.value() + "/"; -} - -std::string OutputPathAndroid() { - return OutputPathImpl(); -} - -} // namespace test -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/fileutils.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/fileutils.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/fileutils.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/fileutils.cc 2015-02-03 14:33:38.000000000 +0000 @@ -10,12 +10,20 @@ #include "webrtc/test/testsupport/fileutils.h" +#include + #ifdef WIN32 #include +#include +#include #include + +#include "webrtc/system_wrappers/interface/utf_util_win.h" #define GET_CURRENT_DIR _getcwd #else #include + +#include "webrtc/system_wrappers/interface/scoped_ptr.h" #define GET_CURRENT_DIR getcwd #endif @@ -25,6 +33,7 @@ #endif #include +#include #include #include "webrtc/typedefs.h" // For architecture defines @@ -41,15 +50,15 @@ #endif #ifdef WEBRTC_ANDROID -const char* kResourcesDirName = "resources"; +const char* kRootDirName = "/sdcard/"; #else // The file we're looking for to identify the project root dir. const char* kProjectRootFileName = "DEPS"; -const char* kResourcesDirName = "resources"; +const char* kOutputDirName = "out"; +const char* kFallbackPath = "./"; #endif +const char* kResourcesDirName = "resources"; -const char* kFallbackPath = "./"; -const char* kOutputDirName = "out"; char relative_dir_path[FILENAME_MAX]; bool relative_dir_path_set = false; @@ -57,9 +66,6 @@ const char* kCannotFindProjectRootDir = "ERROR_CANNOT_FIND_PROJECT_ROOT_DIR"; -std::string OutputPathAndroid(); -std::string ProjectRootPathAndroid(); - void SetExecutablePath(const std::string& path) { std::string working_dir = WorkingDir(); std::string temp_path = path; @@ -86,30 +92,18 @@ return stat(file_name.c_str(), &file_info) == 0; } -std::string OutputPathImpl() { - std::string path = ProjectRootPath(); - if (path == kCannotFindProjectRootDir) { - return kFallbackPath; - } - path += kOutputDirName; - if (!CreateDirectory(path)) { - return kFallbackPath; - } - return path + kPathDelimiter; -} - #ifdef WEBRTC_ANDROID std::string ProjectRootPath() { - return ProjectRootPathAndroid(); + return kRootDirName; } std::string OutputPath() { - return OutputPathAndroid(); + return kRootDirName; } std::string WorkingDir() { - return ProjectRootPath(); + return kRootDirName; } #else // WEBRTC_ANDROID @@ -139,7 +133,15 @@ } std::string OutputPath() { - return OutputPathImpl(); + std::string path = ProjectRootPath(); + if (path == kCannotFindProjectRootDir) { + return kFallbackPath; + } + path += kOutputDirName; + if (!CreateDir(path)) { + return kFallbackPath; + } + return path + kPathDelimiter; } std::string WorkingDir() { @@ -154,7 +156,35 @@ #endif // !WEBRTC_ANDROID -bool CreateDirectory(std::string directory_name) { +// Generate a temporary filename in a safe way. +// Largely copied from talk/base/{unixfilesystem,win32filesystem}.cc. +std::string TempFilename(const std::string &dir, const std::string &prefix) { +#ifdef WIN32 + wchar_t filename[MAX_PATH]; + if (::GetTempFileName(ToUtf16(dir).c_str(), + ToUtf16(prefix).c_str(), 0, filename) != 0) + return ToUtf8(filename); + assert(false); + return ""; +#else + int len = dir.size() + prefix.size() + 2 + 6; + scoped_ptr tempname(new char[len]); + + snprintf(tempname.get(), len, "%s/%sXXXXXX", dir.c_str(), + prefix.c_str()); + int fd = ::mkstemp(tempname.get()); + if (fd == -1) { + assert(false); + return ""; + } else { + ::close(fd); + } + std::string ret(tempname.get()); + return ret; +#endif +} + +bool CreateDir(std::string directory_name) { struct stat path_info = {0}; // Check if the path exists already: if (stat(directory_name.c_str(), &path_info) == 0) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/fileutils.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/fileutils.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/fileutils.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/fileutils.h 2015-02-03 14:33:38.000000000 +0000 @@ -103,6 +103,10 @@ // found, the current working directory ("./") is returned as a fallback. std::string OutputPath(); +// Generates an empty file with a unique name in the specified directory and +// returns the file name and path. +std::string TempFilename(const std::string &dir, const std::string &prefix); + // Returns a path to a resource file for the currently executing platform. // Adapts to what filenames are currently present in the // [project-root]/resources/ dir. @@ -132,7 +136,10 @@ // Creates a directory if it not already exists. // Returns true if successful. Will print an error message to stderr and return // false if a file with the same name already exists. -bool CreateDirectory(std::string directory_name); +bool CreateDir(std::string directory_name); + +// Checks if a file exists. +bool FileExists(std::string& file_name); // File size of the supplied file in bytes. Will return 0 if the file is // empty or if the file does not exist/is readable. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/fileutils_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/fileutils_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/fileutils_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/fileutils_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -29,8 +29,6 @@ static const std::string kTestName = "fileutils_unittest"; static const std::string kExtension = "tmp"; -typedef std::list FileList; - namespace webrtc { // Test fixture to restore the working directory between each test, since some @@ -44,38 +42,6 @@ // Runs before the first test static void SetUpTestCase() { original_working_dir_ = webrtc::test::WorkingDir(); - std::string resources_path = original_working_dir_ + kPathDelimiter + - kResourcesDir + kPathDelimiter; - webrtc::test::CreateDirectory(resources_path); - - files_.push_back(resources_path + kTestName + "." + kExtension); - files_.push_back(resources_path + kTestName + "_32." + kExtension); - files_.push_back(resources_path + kTestName + "_64." + kExtension); - files_.push_back(resources_path + kTestName + "_linux." + kExtension); - files_.push_back(resources_path + kTestName + "_mac." + kExtension); - files_.push_back(resources_path + kTestName + "_win." + kExtension); - files_.push_back(resources_path + kTestName + "_linux_32." + kExtension); - files_.push_back(resources_path + kTestName + "_mac_32." + kExtension); - files_.push_back(resources_path + kTestName + "_win_32." + kExtension); - files_.push_back(resources_path + kTestName + "_linux_64." + kExtension); - files_.push_back(resources_path + kTestName + "_mac_64." + kExtension); - files_.push_back(resources_path + kTestName + "_win_64." + kExtension); - - // Now that the resources dir exists, write some empty test files into it. - for (FileList::iterator file_it = files_.begin(); - file_it != files_.end(); ++file_it) { - FILE* file = fopen(file_it->c_str(), "wb"); - ASSERT_TRUE(file != NULL) << "Failed to write file: " << file_it->c_str(); - ASSERT_GT(fprintf(file, "%s", "Dummy data"), 0); - fclose(file); - } - } - static void TearDownTestCase() { - // Clean up all resource files written - for (FileList::iterator file_it = files_.begin(); - file_it != files_.end(); ++file_it) { - remove(file_it->c_str()); - } } void SetUp() { ASSERT_EQ(chdir(original_working_dir_.c_str()), 0); @@ -83,13 +49,10 @@ void TearDown() { ASSERT_EQ(chdir(original_working_dir_.c_str()), 0); } - protected: - static FileList files_; private: static std::string original_working_dir_; }; -FileList FileUtilsTest::files_; std::string FileUtilsTest::original_working_dir_ = ""; // Tests that the project root path is returned for the default working @@ -103,7 +66,7 @@ } // Similar to the above test, but for the output dir -TEST_F(FileUtilsTest, OutputPathFromUnchangedWorkingDir) { +TEST_F(FileUtilsTest, DISABLED_ON_ANDROID(OutputPathFromUnchangedWorkingDir)) { std::string path = webrtc::test::OutputPath(); std::string expected_end = "out"; expected_end = kPathDelimiter + expected_end + kPathDelimiter; @@ -117,12 +80,20 @@ ASSERT_EQ("./", webrtc::test::OutputPath()); } +TEST_F(FileUtilsTest, TempFilename) { + std::string temp_filename = webrtc::test::TempFilename( + webrtc::test::OutputPath(), "TempFilenameTest"); + ASSERT_TRUE(webrtc::test::FileExists(temp_filename)) + << "Couldn't find file: " << temp_filename; + remove(temp_filename.c_str()); +} + // Only tests that the code executes -TEST_F(FileUtilsTest, CreateDirectory) { +TEST_F(FileUtilsTest, CreateDir) { std::string directory = "fileutils-unittest-empty-dir"; // Make sure it's removed if a previous test has failed: remove(directory.c_str()); - ASSERT_TRUE(webrtc::test::CreateDirectory(directory)); + ASSERT_TRUE(webrtc::test::CreateDir(directory)); remove(directory.c_str()); } @@ -152,7 +123,16 @@ } TEST_F(FileUtilsTest, GetFileSizeExistingFile) { - ASSERT_GT(webrtc::test::GetFileSize(files_.front()), 0u); + // Create a file with some dummy data in. + std::string temp_filename = webrtc::test::TempFilename( + webrtc::test::OutputPath(), "fileutils_unittest"); + FILE* file = fopen(temp_filename.c_str(), "wb"); + ASSERT_TRUE(file != NULL) << "Failed to open file: " << temp_filename; + ASSERT_GT(fprintf(file, "%s", "Dummy data"), 0) << + "Failed to write to file: " << temp_filename; + fclose(file); + ASSERT_GT(webrtc::test::GetFileSize(std::string(temp_filename.c_str())), 0u); + remove(temp_filename.c_str()); } TEST_F(FileUtilsTest, GetFileSizeNonExistingFile) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/frame_reader_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/frame_reader_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/frame_reader_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/frame_reader_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -16,7 +16,6 @@ namespace webrtc { namespace test { -const std::string kInputFilename = "temp_inputfile.tmp"; const std::string kInputFileContents = "baz"; // Setting the kFrameLength value to a value much larger than the // file to test causes the ReadFrame test to fail on Windows. @@ -27,27 +26,27 @@ FrameReaderTest() {} virtual ~FrameReaderTest() {} void SetUp() { - // Cleanup any previous dummy input file. - remove(kInputFilename.c_str()); - // Create a dummy input file. - FILE* dummy = fopen(kInputFilename.c_str(), "wb"); + temp_filename_ = webrtc::test::TempFilename(webrtc::test::OutputPath(), + "frame_reader_unittest"); + FILE* dummy = fopen(temp_filename_.c_str(), "wb"); fprintf(dummy, "%s", kInputFileContents.c_str()); fclose(dummy); - frame_reader_ = new FrameReaderImpl(kInputFilename, kFrameLength); + frame_reader_ = new FrameReaderImpl(temp_filename_, kFrameLength); ASSERT_TRUE(frame_reader_->Init()); } void TearDown() { delete frame_reader_; // Cleanup the dummy input file. - remove(kInputFilename.c_str()); + remove(temp_filename_.c_str()); } FrameReader* frame_reader_; + std::string temp_filename_; }; TEST_F(FrameReaderTest, InitSuccess) { - FrameReaderImpl frame_reader(kInputFilename, kFrameLength); + FrameReaderImpl frame_reader(temp_filename_, kFrameLength); ASSERT_TRUE(frame_reader.Init()); ASSERT_EQ(kFrameLength, frame_reader.FrameLength()); ASSERT_EQ(0, frame_reader.NumberOfFrames()); @@ -64,7 +63,7 @@ TEST_F(FrameReaderTest, ReadFrameUninitialized) { uint8_t buffer[3]; - FrameReaderImpl file_reader(kInputFilename, kFrameLength); + FrameReaderImpl file_reader(temp_filename_, kFrameLength); ASSERT_FALSE(file_reader.ReadFrame(buffer)); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/frame_writer_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/frame_writer_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/frame_writer_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/frame_writer_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -16,7 +16,6 @@ namespace webrtc { namespace test { -const std::string kOutputFilename = "temp_outputfile.tmp"; const size_t kFrameLength = 1000; class FrameWriterTest: public testing::Test { @@ -24,21 +23,22 @@ FrameWriterTest() {} virtual ~FrameWriterTest() {} void SetUp() { - // Cleanup any previous output file. - remove(kOutputFilename.c_str()); - frame_writer_ = new FrameWriterImpl(kOutputFilename, kFrameLength); + temp_filename_ = webrtc::test::TempFilename(webrtc::test::OutputPath(), + "frame_writer_unittest"); + frame_writer_ = new FrameWriterImpl(temp_filename_, kFrameLength); ASSERT_TRUE(frame_writer_->Init()); } void TearDown() { delete frame_writer_; // Cleanup the temporary file. - remove(kOutputFilename.c_str()); + remove(temp_filename_.c_str()); } FrameWriter* frame_writer_; + std::string temp_filename_; }; TEST_F(FrameWriterTest, InitSuccess) { - FrameWriterImpl frame_writer(kOutputFilename, kFrameLength); + FrameWriterImpl frame_writer(temp_filename_, kFrameLength); ASSERT_TRUE(frame_writer.Init()); ASSERT_EQ(kFrameLength, frame_writer.FrameLength()); } @@ -50,12 +50,12 @@ ASSERT_TRUE(result); // success // Close the file and verify the size. frame_writer_->Close(); - ASSERT_EQ(kFrameLength, GetFileSize(kOutputFilename)); + ASSERT_EQ(kFrameLength, GetFileSize(temp_filename_)); } TEST_F(FrameWriterTest, WriteFrameUninitialized) { uint8_t buffer[3]; - FrameWriterImpl frame_writer(kOutputFilename, kFrameLength); + FrameWriterImpl frame_writer(temp_filename_, kFrameLength); ASSERT_FALSE(frame_writer.WriteFrame(buffer)); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/metrics/video_metrics.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/metrics/video_metrics.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/metrics/video_metrics.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/metrics/video_metrics.cc 2015-02-03 14:33:38.000000000 +0000 @@ -111,8 +111,8 @@ const size_t frame_length = 3 * width * height >> 1; I420VideoFrame ref_frame; I420VideoFrame test_frame; - scoped_array ref_buffer(new uint8_t[frame_length]); - scoped_array test_buffer(new uint8_t[frame_length]); + scoped_ptr ref_buffer(new uint8_t[frame_length]); + scoped_ptr test_buffer(new uint8_t[frame_length]); // Set decoded image parameters. int half_width = (width + 1) / 2; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/metrics/video_metrics_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/metrics/video_metrics_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/testsupport/metrics/video_metrics_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/testsupport/metrics/video_metrics_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -15,7 +15,6 @@ namespace webrtc { -static const char* kNonExistingFileName = "video_metrics_unittest_non_existing"; static const int kWidth = 352; static const int kHeight = 288; @@ -28,13 +27,17 @@ class VideoMetricsTest: public testing::Test { protected: VideoMetricsTest() { - empty_file_ = webrtc::test::OutputPath() + - "video_metrics_unittest_empty_file.tmp"; video_file_ = webrtc::test::ResourcePath("foreman_cif_short", "yuv"); } virtual ~VideoMetricsTest() {} void SetUp() { + non_existing_file_ = webrtc::test::OutputPath() + + "video_metrics_unittest_non_existing"; + remove(non_existing_file_.c_str()); // To be sure it doesn't exist. + // Create an empty file: + empty_file_ = webrtc::test::TempFilename( + webrtc::test::OutputPath(), "video_metrics_unittest_empty_file"); FILE* dummy = fopen(empty_file_.c_str(), "wb"); fclose(dummy); } @@ -43,6 +46,7 @@ } webrtc::test::QualityMetricsResult psnr_result_; webrtc::test::QualityMetricsResult ssim_result_; + std::string non_existing_file_; std::string empty_file_; std::string video_file_; }; @@ -71,40 +75,42 @@ // Tests that the right return code is given when the reference file is missing. TEST_F(VideoMetricsTest, MissingReferenceFilePSNR) { EXPECT_EQ(kMissingReferenceFileReturnCode, - I420PSNRFromFiles(kNonExistingFileName, video_file_.c_str(), - kWidth, kHeight, &ssim_result_)); + I420PSNRFromFiles(non_existing_file_.c_str(), + video_file_.c_str(), kWidth, kHeight, + &ssim_result_)); } TEST_F(VideoMetricsTest, MissingReferenceFileSSIM) { EXPECT_EQ(kMissingReferenceFileReturnCode, - I420SSIMFromFiles(kNonExistingFileName, video_file_.c_str(), - kWidth, kHeight, &ssim_result_)); + I420SSIMFromFiles(non_existing_file_.c_str(), + video_file_.c_str(), kWidth, kHeight, + &ssim_result_)); } TEST_F(VideoMetricsTest, MissingReferenceFileBothMetrics) { EXPECT_EQ(kMissingReferenceFileReturnCode, - I420MetricsFromFiles(kNonExistingFileName, video_file_.c_str(), - kWidth, kHeight, + I420MetricsFromFiles(non_existing_file_.c_str(), + video_file_.c_str(), kWidth, kHeight, &psnr_result_, &ssim_result_)); } // Tests that the right return code is given when the test file is missing. TEST_F(VideoMetricsTest, MissingTestFilePSNR) { EXPECT_EQ(kMissingTestFileReturnCode, - I420PSNRFromFiles(video_file_.c_str(), kNonExistingFileName, + I420PSNRFromFiles(video_file_.c_str(), non_existing_file_.c_str(), kWidth, kHeight, &ssim_result_)); } TEST_F(VideoMetricsTest, MissingTestFileSSIM) { EXPECT_EQ(kMissingTestFileReturnCode, - I420SSIMFromFiles(video_file_.c_str(), kNonExistingFileName, + I420SSIMFromFiles(video_file_.c_str(), non_existing_file_.c_str(), kWidth, kHeight, &ssim_result_)); } TEST_F(VideoMetricsTest, MissingTestFileBothMetrics) { EXPECT_EQ(kMissingTestFileReturnCode, - I420MetricsFromFiles(video_file_.c_str(), kNonExistingFileName, - kWidth, kHeight, + I420MetricsFromFiles(video_file_.c_str(), + non_existing_file_.c_str(), kWidth, kHeight, &psnr_result_, &ssim_result_)); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/test_support_unittests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/test_support_unittests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/test_support_unittests.isolate 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/test_support_unittests.isolate 2015-02-03 14:33:38.000000000 +0000 @@ -8,29 +8,24 @@ { 'conditions': [ ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. 'variables': { - 'isolate_dependency_untracked': [ - '../../../data/', - '../../../resources/', + 'files': [ + '<(DEPTH)/data/', + '<(DEPTH)/resources/', ], }, }], ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../../testing/test_env.py', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/test_support_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_tracked': [ - '../../DEPS', - '../../testing/test_env.py', + 'files': [ + '<(DEPTH)/DEPS', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/test_support_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '../../tools/swarming_client/', - ], }, }], ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/w3c/getusermedia_conformance_test.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/w3c/getusermedia_conformance_test.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/w3c/getusermedia_conformance_test.html 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/w3c/getusermedia_conformance_test.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ - - - - - getUserMedia Browser Conformance Test - - - - - - - -

    Conformance test for the Media Capture and Streams API

    -

    This page contains a foundation of conformance tests that can be expanded - to cover most things in the W3C specification of the Media Capture and Streams - API.

    -

    VERSION: These tests are based on the W3C Editor's Draft of August 24th, - 2013 - (http://dev.w3.org/2011/webrtc/editor/archives/20130824/getusermedia.html) -

    STATUS: In its current state, it only performs simple checks on the various - attributes and methods of the objects exposed by the API. There's not much - functionality tested so far. The spec doesn't define if an attribute shall be - owned by the object itself (assert_own_propety) or if it shall be - inherited (assert_inherits). Since testharness.js doesn't offer - any generic function that covers both, the method for verification is - currently chosen according to the current Chrome implementation.

    -

    PREFIXES: These tests currently utilizes the adapter.js - script, which handle the prefixes used by different browsers.

    -

    HOW TO RUN: The easiest way is to tell your browser to: -

      -
    • Provide a fake webcam (--use-fake-ui-for-media-stream in - Chrome)
    • -
    • Automatically allow access to the webcam - (--use-fake-device-for-media-stream in Chrome)
    • -
    • Allow loading HTML files from disk - (--allow-file-access-from-files in Chrome)
    • -
    - Then just load this HTML file to execute the tests.

    - -
    - - - - - - - - - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/w3c/getusermedia_conformance_test.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/w3c/getusermedia_conformance_test.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/w3c/getusermedia_conformance_test.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/w3c/getusermedia_conformance_test.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,451 +0,0 @@ -// Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the LICENSE file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. - -setup({timeout:10000}); - -// Helper functions to minimize code duplication. -function failedCallback(test) { - return test.step_func(function (error) { - assert_unreached('Should not get an error callback'); - }); -} -function invokeGetUserMedia(test, okCallback) { - getUserMedia({ video: true, audio: true }, okCallback, - failedCallback(test)); -} - -// 4.2 MediaStream. -var mediaStreamTest = async_test('4.2 MediaStream'); - -function verifyMediaStream(stream) { - // TODO(kjellander): Add checks for default values where applicable. - test(function () { - assert_own_property(stream, 'id'); - assert_true(typeof stream.id === 'string'); - assert_readonly(stream, 'id'); - }, '[MediaStream] id attribute'); - - test(function () { - assert_inherits(stream, 'getAudioTracks'); - assert_true(typeof stream.getAudioTracks === 'function'); - }, '[MediaStream] getAudioTracks function'); - - test(function () { - assert_inherits(stream, 'getVideoTracks'); - assert_true(typeof stream.getVideoTracks === 'function'); - }, '[MediaStream] getVideoTracks function'); - - test(function () { - assert_inherits(stream, 'getTrackById'); - assert_true(typeof stream.getTrackById === 'function'); - }, '[MediaStream] getTrackById function'); - - test(function () { - assert_inherits(stream, 'addTrack'); - assert_true(typeof stream.addTrack === 'function'); - }, '[MediaStream] addTrack function'); - - test(function () { - assert_inherits(stream, 'removeTrack'); - assert_true(typeof stream.removeTrack === 'function'); - }, '[MediaStream] removeTrack function'); - - test(function () { - // Missing in Chrome. - assert_inherits(stream, 'clone'); - assert_true(typeof stream.clone === 'function'); - }, '[MediaStream] clone function'); - - test(function () { - assert_own_property(stream, 'ended'); - assert_true(typeof stream.ended === 'boolean'); - assert_readonly(stream, 'ended'); - }, '[MediaStream] ended attribute'); - - test(function () { - assert_own_property(stream, 'onended'); - assert_true(stream.onended === null); - }, '[MediaStream] onended EventHandler'); - - test(function () { - assert_own_property(stream, 'onaddtrack'); - assert_true(stream.onaddtrack === null); - }, '[MediaStream] onaddtrack EventHandler'); - - test(function () { - assert_own_property(stream, 'onremovetrack'); - assert_true(stream.onremovetrack === null); - }, '[MediaStream] onremovetrack EventHandler'); -} - -mediaStreamTest.step(function() { - var okCallback = mediaStreamTest.step_func(function (stream) { - verifyMediaStream(stream); - - var videoTracks = stream.getVideoTracks(); - assert_true(videoTracks.length > 0); - - // Verify event handlers are working. - stream.onaddtrack = onAddTrackCallback - stream.onremovetrack = onRemoveTrackCallback - stream.removeTrack(videoTracks[0]); - stream.addTrack(videoTracks[0]); - mediaStreamTest.done(); - }); - var onAddTrackCallback = mediaStreamTest.step_func(function () { - // TODO(kjellander): verify number of tracks. - mediaStreamTest.done(); - }); - var onRemoveTrackCallback = mediaStreamTest.step_func(function () { - // TODO(kjellander): verify number of tracks. - mediaStreamTest.done(); - }); - invokeGetUserMedia(mediaStreamTest, okCallback);; -}); - -// 4.3 MediaStreamTrack. -var mediaStreamTrackTest = async_test('4.3 MediaStreamTrack'); - -function verifyTrack(type, track) { - test(function () { - assert_own_property(track, 'kind'); - assert_readonly(track, 'kind'); - assert_true(typeof track.kind === 'string', - 'kind is an object (DOMString)'); - }, '[MediaStreamTrack (' + type + ')] kind attribute'); - - test(function () { - assert_own_property(track, 'id'); - assert_readonly(track, 'id'); - assert_true(typeof track.id === 'string', - 'id is an object (DOMString)'); - }, '[MediaStreamTrack (' + type + ')] id attribute'); - - test(function () { - assert_own_property(track, 'label'); - assert_readonly(track, 'label'); - assert_true(typeof track.label === 'string', - 'label is an object (DOMString)'); - }, '[MediaStreamTrack (' + type + ')] label attribute'); - - test(function () { - assert_own_property(track, 'enabled'); - assert_true(typeof track.enabled === 'boolean'); - assert_true(track.enabled, 'enabled property must be true initially'); - }, '[MediaStreamTrack (' + type + ')] enabled attribute'); - - test(function () { - // Missing in Chrome. - assert_own_property(track, 'muted'); - assert_readonly(track, 'muted'); - assert_true(typeof track.muted === 'boolean'); - assert_false(track.muted, 'muted property must be false initially'); - }, '[MediaStreamTrack (' + type + ')] muted attribute'); - - test(function () { - assert_own_property(track, 'onmute'); - assert_true(track.onmute === null); - }, '[MediaStreamTrack (' + type + ')] onmute EventHandler'); - - test(function () { - assert_own_property(track, 'onunmute'); - assert_true(track.onunmute === null); - }, '[MediaStreamTrack (' + type + ')] onunmute EventHandler'); - - test(function () { - // Missing in Chrome. - assert_own_property(track, '_readonly'); - assert_readonly(track, '_readonly'); - assert_true(typeof track._readonly === 'boolean'); - }, '[MediaStreamTrack (' + type + ')] _readonly attribute'); - - test(function () { - // Missing in Chrome. - assert_own_property(track, 'remote'); - assert_readonly(track, 'remote'); - assert_true(typeof track.remote === 'boolean'); - }, '[MediaStreamTrack (' + type + ')] remote attribute'); - - test(function () { - assert_own_property(track, 'readyState'); - assert_readonly(track, 'readyState'); - assert_true(typeof track.readyState === 'string'); - // TODO(kjellander): verify the initial state. - }, '[MediaStreamTrack (' + type + ')] readyState attribute'); - - test(function () { - // Missing in Chrome. - assert_own_property(track, 'onstarted'); - assert_true(track.onstarted === null); - }, '[MediaStreamTrack (' + type + ')] onstarted EventHandler'); - - test(function () { - assert_own_property(track, 'onended'); - assert_true(track.onended === null); - }, '[MediaStreamTrack (' + type + ')] onended EventHandler'); - - test(function () { - // Missing in Chrome. - assert_inherits(track, 'getSourceInfos'); - assert_true(typeof track.getSourceInfos === 'function'); - }, '[MediaStreamTrack (' + type + ')]: getSourceInfos function'); - - test(function () { - // Missing in Chrome. - assert_inherits(track, 'constraints'); - assert_true(typeof track.constraints === 'function'); - }, '[MediaStreamTrack (' + type + ')]: constraints function'); - - test(function () { - // Missing in Chrome. - assert_inherits(track, 'states'); - assert_true(typeof track.states === 'function'); - }, '[MediaStreamTrack (' + type + ')]: states function'); - - test(function () { - // Missing in Chrome. - assert_inherits(track, 'capabilities'); - assert_true(typeof track.capabilities === 'function'); - }, '[MediaStreamTrack (' + type + ')]: capabilities function'); - - test(function () { - // Missing in Chrome. - assert_inherits(track, 'applyConstraints'); - assert_true(typeof track.applyConstraints === 'function'); - }, '[MediaStreamTrack (' + type + ')]: applyConstraints function'); - - test(function () { - // Missing in Chrome. - assert_own_property(track, 'onoverconstrained'); - assert_true(track.onoverconstrained === null); - }, '[MediaStreamTrack (' + type + ')] onoverconstrained EventHandler'); - - test(function () { - // Missing in Chrome. - assert_inherits(track, 'clone'); - assert_true(typeof track.clone === 'function'); - }, '[MediaStreamTrack (' + type + ')] clone function'); - - test(function () { - // Missing in Chrome. - assert_inherits(track, 'stop'); - assert_true(typeof track.stop === 'function'); - }, '[MediaStreamTrack (' + type + ')] stop function'); -}; -mediaStreamTrackTest.step(function() { - var okCallback = mediaStreamTrackTest.step_func(function (stream) { - verifyTrack('audio', stream.getAudioTracks()[0]); - verifyTrack('video', stream.getVideoTracks()[0]); - mediaStreamTrackTest.done(); - }); - invokeGetUserMedia(mediaStreamTrackTest, okCallback); -}); - -mediaStreamTrackTest.step(function() { - var okCallback = mediaStreamTrackTest.step_func(function (stream) { - // Verify event handlers are working. - var track = stream.getVideoTracks()[0]; - track.onended = onendedCallback - track.stop(); - mediaStreamTrackTest.done(); - }); - var onendedCallback = mediaStreamTrackTest.step_func(function () { - assert_true(track.ended); - mediaStreamTrackTest.done(); - }); - invokeGetUserMedia(mediaStreamTrackTest, okCallback); -}); - -// 4.4 MediaStreamTrackEvent tests. -var mediaStreamTrackEventTest = async_test('4.4 MediaStreamTrackEvent'); -mediaStreamTrackEventTest.step(function() { - var okCallback = mediaStreamTrackEventTest.step_func(function (stream) { - // TODO(kjellander): verify attributes - mediaStreamTrackEventTest.done(); - }); - invokeGetUserMedia(mediaStreamTrackEventTest, okCallback); -}); - -// 4.5 Video and Audio Tracks tests. -var avTracksTest = async_test('4.5 Video and Audio Tracks'); -avTracksTest.step(function() { - var okCallback = avTracksTest.step_func(function (stream) { - // TODO(kjellander): verify attributes - avTracksTest.done(); - }); - invokeGetUserMedia(avTracksTest, okCallback); -}); - -// 5. The model: sources, sinks, constraints, and states - -// 6. Source states -// 6.1 Dictionary MediaSourceStates Members - -// 7. Source capabilities -// 7.1 Dictionary CapabilityRange Members -// 7.2 CapabilityList array -// 7.3 Dictionary AllVideoCapabilities Members -// 7.4 Dictionary AllAudioCapabilities Members - -// 8. URL tests. -var createObjectURLTest = async_test('8.1 URL createObjectURL method'); -createObjectURLTest.step(function() { - var okCallback = createObjectURLTest.step_func(function (stream) { - var url = webkitURL.createObjectURL(stream); - assert_true(typeof url === 'string'); - createObjectURLTest.done(); - }); - invokeGetUserMedia(createObjectURLTest, okCallback); -}); - -// 9. MediaStreams as Media Elements. -var mediaElementsTest = async_test('9. MediaStreams as Media Elements'); - -function verifyVideoTagWithStream(videoTag) { - test(function () { - assert_equals(videoTag.buffered.length, 0); - }, '[Video tag] buffered attribute'); - - test(function () { - // Attempts to alter currentTime shall be ignored. - assert_true(videoTag.currentTime >= 0); - assert_throws('InvalidStateError', - function () { videoTag.currentTime = 1234; }, - 'Attempts to modify currentTime shall throw ' + - 'InvalidStateError'); - }, '[Video tag] currentTime attribute'); - - test(function () { - assert_equals(videoTag.duration, Infinity, 'videoTag.duration'); - }, '[Video tag] duration attribute'); - - test(function () { - assert_false(videoTag.seeking, 'videoTag.seeking'); - }, '[Video tag] seeking attribute'); - - test(function () { - assert_equals(videoTag.defaultPlaybackRate, 1.0); - assert_throws('DOMException', - function () { videoTag.defaultPlaybackRate = 2.0; }, - 'Attempts to alter videoTag.defaultPlaybackRate MUST fail'); - }, '[Video tag] defaultPlaybackRate attribute'); - - test(function () { - assert_equals(videoTag.playbackRate, 1.0); - assert_throws('DOMException', - function () { videoTag.playbackRate = 2.0; }, - 'Attempts to alter videoTag.playbackRate MUST fail'); - }, '[Video tag] playbackRate attribute'); - - test(function () { - assert_equals(videoTag.played.length, 1, 'videoTag.played.length'); - assert_equals(videoTag.played.start(0), 0); - assert_true(videoTag.played.end(0) >= videoTag.currentTime); - }, '[Video tag] played attribute'); - - test(function () { - assert_equals(videoTag.seekable.length, 0); - assert_equals(videoTag.seekable.start(), videoTag.currentTime); - assert_equals(videoTag.seekable.end(), videoTag.currentTime); - assert_equals(videoTag.startDate, NaN, 'videoTag.startDate'); - }, '[Video tag] seekable attribute'); - - test(function () { - assert_false(videoTag.loop); - }, '[Video tag] loop attribute'); -}; - -mediaElementsTest.step(function() { - var okCallback = mediaElementsTest.step_func(function (stream) { - var videoTag = document.getElementById('local-view'); - // Call the polyfill wrapper to attach the media stream to this element. - attachMediaStream(videoTag, stream); - verifyVideoTagWithStream(videoTag); - mediaElementsTest.done(); - }); - invokeGetUserMedia(mediaElementsTest, okCallback); -}); - -// 11. Obtaining local multimedia content. - -// 11.1 NavigatorUserMedia. -var getUserMediaTest = async_test('11.1 NavigatorUserMedia'); -getUserMediaTest.step(function() { - var okCallback = getUserMediaTest.step_func(function (stream) { - assert_true(stream !== null); - getUserMediaTest.done(); - }); - - // boolean parameters, without failure callback: - getUserMedia({ video: true, audio: true }, okCallback); - getUserMedia({ video: true, audio: false }, okCallback); - getUserMedia({ video: false, audio: true }, okCallback); - - // boolean parameters, with failure callback: - getUserMedia({ video: true, audio: true }, okCallback, - failedCallback(getUserMediaTest)); - getUserMedia({ video: true, audio: false }, okCallback, - failedCallback(getUserMediaTest)); - getUserMedia({ video: false, audio: true }, okCallback, - failedCallback(getUserMediaTest)); -}); - -// 11.2 MediaStreamConstraints. -var constraintsTest = async_test('11.2 MediaStreamConstraints'); -constraintsTest.step(function() { - var okCallback = constraintsTest.step_func(function (stream) { - assert_true(stream !== null); - constraintsTest.done(); - }); - - // Constraints on video. - // See http://webrtc.googlecode.com/svn/trunk/samples/js/demos/html/constraints-and-stats.html - // for more examples of constraints. - var constraints = {}; - constraints.audio = true; - constraints.video = { mandatory: {}, optional: [] }; - constraints.video.mandatory.minWidth = 640; - constraints.video.mandatory.minHeight = 480; - constraints.video.mandatory.minFrameRate = 15; - - getUserMedia(constraints, okCallback, failedCallback(constraintsTest)); -}); - -// 11.3 NavigatorUserMediaSuccessCallback. -var successCallbackTest = - async_test('11.3 NavigatorUserMediaSuccessCallback'); -successCallbackTest.step(function() { - var okCallback = successCallbackTest.step_func(function (stream) { - assert_true(stream !== null); - successCallbackTest.done(); - }); - invokeGetUserMedia(successCallbackTest, okCallback); -}); - -// 11.4 NavigatorUserMediaError and NavigatorUserMediaErrorCallback. -var errorCallbackTest = async_test('11.4 NavigatorUserMediaError and ' + - 'NavigatorUserMediaErrorCallback'); -errorCallbackTest.step(function() { - var okCallback = errorCallbackTest.step_func(function (stream) { - assert_unreached('Should not get a success callback'); - }); - var errorCallback = errorCallbackTest.step_func(function (error) { - assert_own_property(error, 'name'); - assert_readonly(error.name); - assert_true(typeof error.name === 'string'); - assert_equals(error.name, 'ConstraintNotSatisfiedError', 'error.name'); - errorCallbackTest.done(); - }); - // Setting both audio and video to false triggers an error callback. - // TODO(kjellander): Figure out if there's a way in the spec to trigger an - // error callback. - - // TODO(kjellander): Investigate why the error callback is not called when - // false/false is provided in Chrome. - getUserMedia({ video: false, audio: false }, okCallback, errorCallback); -}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/w3c/peerconnection_conformance_test.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/w3c/peerconnection_conformance_test.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/w3c/peerconnection_conformance_test.html 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/w3c/peerconnection_conformance_test.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,119 +0,0 @@ - - - - - - - - PeerConnection Connection Test - - - - - - - - -
    - - -
    -
    - - \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/webrtc_test_common.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/webrtc_test_common.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/webrtc_test_common.gyp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/webrtc_test_common.gyp 2015-02-03 14:33:38.000000000 +0000 @@ -14,10 +14,14 @@ 'target_name': 'webrtc_test_common', 'type': 'static_library', 'sources': [ + 'call_test.cc', + 'call_test.h', 'configurable_frame_size_encoder.cc', 'configurable_frame_size_encoder.h', 'direct_transport.cc', 'direct_transport.h', + 'encoder_settings.cc', + 'encoder_settings.h', 'fake_audio_device.cc', 'fake_audio_device.h', 'fake_decoder.cc', @@ -26,25 +30,12 @@ 'fake_encoder.h', 'fake_network_pipe.cc', 'fake_network_pipe.h', - 'flags.cc', - 'flags.h', 'frame_generator_capturer.cc', 'frame_generator_capturer.h', - 'gl/gl_renderer.cc', - 'gl/gl_renderer.h', - 'linux/glx_renderer.cc', - 'linux/glx_renderer.h', - 'linux/video_renderer_linux.cc', - 'mac/run_tests.mm', - 'mac/video_renderer_mac.h', - 'mac/video_renderer_mac.mm', 'mock_transport.h', - 'null_platform_renderer.cc', 'null_transport.cc', 'null_transport.h', 'rtp_rtcp_observer.h', - 'run_tests.cc', - 'run_tests.h', 'run_loop.cc', 'run_loop.h', 'statistics.cc', @@ -53,11 +44,43 @@ 'vcm_capturer.h', 'video_capturer.cc', 'video_capturer.h', + 'win/run_loop_win.cc', + ], + 'conditions': [ + ['OS=="win"', { + 'sources!': [ + 'run_loop.cc', + ], + }], + ], + 'dependencies': [ + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + '<(webrtc_root)/base/base.gyp:rtc_base', + '<(webrtc_root)/modules/modules.gyp:media_file', + '<(webrtc_root)/modules/modules.gyp:video_render_module_impl', + '<(webrtc_root)/test/test.gyp:frame_generator', + '<(webrtc_root)/test/test.gyp:test_support', + '<(webrtc_root)/test/test.gyp:rtp_test_utils', + '<(webrtc_root)/webrtc.gyp:webrtc', + ], + }, + { + 'target_name': 'webrtc_test_renderer', + 'type': 'static_library', + 'sources': [ + 'gl/gl_renderer.cc', + 'gl/gl_renderer.h', + 'linux/glx_renderer.cc', + 'linux/glx_renderer.h', + 'linux/video_renderer_linux.cc', + 'mac/video_renderer_mac.h', + 'mac/video_renderer_mac.mm', + 'null_platform_renderer.cc', 'video_renderer.cc', 'video_renderer.h', 'win/d3d_renderer.cc', 'win/d3d_renderer.h', - 'win/run_loop_win.cc', ], 'conditions': [ ['OS=="linux"', { @@ -68,7 +91,6 @@ ['OS=="mac"', { 'sources!': [ 'null_platform_renderer.cc', - 'run_tests.cc', ], }], ['OS!="linux" and OS!="mac"', { @@ -80,10 +102,18 @@ ['OS=="win"', { 'sources!': [ 'null_platform_renderer.cc', - 'run_loop.cc', + ], + 'include_dirs': [ + '<(directx_sdk_path)/Include', ], }], ], + 'dependencies': [ + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(webrtc_root)/modules/modules.gyp:media_file', + '<(webrtc_root)/test/test.gyp:frame_generator', + '<(webrtc_root)/test/test.gyp:test_support', + ], 'direct_dependent_settings': { 'conditions': [ ['OS=="linux"', { @@ -93,9 +123,6 @@ '-lGL', ], }], - #TODO(pbos) : These dependencies should not have to be here, they - # aren't used by test code directly, only by components - # used by the tests. ['OS=="android"', { 'libraries' : [ '-lGLESv2', '-llog', @@ -104,26 +131,14 @@ ['OS=="mac"', { 'xcode_settings' : { 'OTHER_LDFLAGS' : [ - '-framework Foundation', - '-framework AppKit', '-framework Cocoa', '-framework OpenGL', '-framework CoreVideo', - '-framework CoreAudio', - '-framework AudioToolbox', ], }, }], ], }, - 'dependencies': [ - '<(DEPTH)/testing/gtest.gyp:gtest', - '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', - '<(webrtc_root)/modules/modules.gyp:video_capture_module', - '<(webrtc_root)/modules/modules.gyp:media_file', - '<(webrtc_root)/test/test.gyp:frame_generator', - '<(webrtc_root)/test/test.gyp:test_support', - ], }, ], 'conditions': [ @@ -136,10 +151,12 @@ 'webrtc_test_common', '<(DEPTH)/testing/gtest.gyp:gtest', '<(DEPTH)/testing/gmock.gyp:gmock', + '<(webrtc_root)/modules/modules.gyp:video_capture_module_impl', '<(webrtc_root)/test/test.gyp:test_support_main', ], 'sources': [ 'fake_network_pipe_unittest.cc', + 'rtp_file_reader_unittest.cc', ], }, ], #targets diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/win/d3d_renderer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/win/d3d_renderer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/test/win/d3d_renderer.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/test/win/d3d_renderer.h 2015-02-03 14:33:38.000000000 +0000 @@ -12,6 +12,7 @@ #include #include +#pragma comment(lib, "d3d9.lib") // located in DirectX SDK #include "webrtc/system_wrappers/interface/scoped_refptr.h" #include "webrtc/test/video_renderer.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/BUILD.gn 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,46 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../build/webrtc.gni") + +source_set("tools") { + deps = [ + ":command_line_parser", + ] +} + +source_set("command_line_parser") { + sources = [ + "simple_command_line_parser.h", + "simple_command_line_parser.cc", + ] + + configs += [ "..:common_config" ] + public_configs = [ "..:common_inherited_config"] +} + +# TODO(kjellander): Convert all of tools.gyp into GN here. + +if (!build_with_chromium) { + executable("tools_unittests") { + testonly = true + + sources = [ + "simple_command_line_parser_unittest.cc", + ] + + configs += [ "..:common_config" ] + public_configs = [ "..:common_inherited_config"] + + deps = [ + ":command_line_parser", + "../test:test_support_main", + "//testing/gtest", + ] + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/e2e_quality/audio/audio_e2e_harness.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/e2e_quality/audio/audio_e2e_harness.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/e2e_quality/audio/audio_e2e_harness.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/e2e_quality/audio/audio_e2e_harness.cc 2015-02-03 14:33:38.000000000 +0000 @@ -13,7 +13,7 @@ // flags. #include "gflags/gflags.h" -#include "gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/test/channel_transport/include/channel_transport.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/frame_analyzer/video_quality_analysis.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/frame_analyzer/video_quality_analysis.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/frame_analyzer/video_quality_analysis.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/frame_analyzer/video_quality_analysis.cc 2015-02-03 14:33:38.000000000 +0000 @@ -17,6 +17,9 @@ #include #define STATS_LINE_LENGTH 32 +#define Y4M_FILE_HEADER_MAX_SIZE 200 +#define Y4M_FRAME_DELIMITER "FRAME" +#define Y4M_FRAME_HEADER_SIZE 6 namespace webrtc { namespace test { @@ -84,46 +87,81 @@ return true; } -bool GetNextI420Frame(FILE* input_file, int width, int height, - uint8* result_frame) { +bool ExtractFrameFromYuvFile(const char* i420_file_name, int width, int height, + int frame_number, uint8* result_frame) { int frame_size = GetI420FrameSize(width, height); + int offset = frame_number * frame_size; // Calculate offset for the frame. bool errors = false; + FILE* input_file = fopen(i420_file_name, "rb"); + if (input_file == NULL) { + fprintf(stderr, "Couldn't open input file for reading: %s\n", + i420_file_name); + return false; + } + + // Change stream pointer to new offset. + fseek(input_file, offset, SEEK_SET); + size_t bytes_read = fread(result_frame, 1, frame_size, input_file); - if (bytes_read != static_cast(frame_size)) { - // If end-of-file is reached, don't print an error. - if (feof(input_file)) { - return false; - } - fprintf(stdout, "Error while reading frame from file\n"); + if (bytes_read != static_cast(frame_size) && + ferror(input_file)) { + fprintf(stdout, "Error while reading frame no %d from file %s\n", + frame_number, i420_file_name); errors = true; } + fclose(input_file); return !errors; } -bool ExtractFrameFromI420(const char* i420_file_name, int width, int height, - int frame_number, uint8* result_frame) { +bool ExtractFrameFromY4mFile(const char* y4m_file_name, int width, int height, + int frame_number, uint8* result_frame) { int frame_size = GetI420FrameSize(width, height); - int offset = frame_number * frame_size; // Calculate offset for the frame. + int frame_offset = frame_number * frame_size; bool errors = false; - FILE* input_file = fopen(i420_file_name, "rb"); + FILE* input_file = fopen(y4m_file_name, "rb"); if (input_file == NULL) { fprintf(stderr, "Couldn't open input file for reading: %s\n", - i420_file_name); + y4m_file_name); return false; } - // Change stream pointer to new offset. - fseek(input_file, offset, SEEK_SET); + // YUV4MPEG2, a.k.a. Y4M File format has a file header and a frame header. The + // file header has the aspect: "YUV4MPEG2 C420 W640 H360 Ip F30:1 A1:1". + // Skip the header if this is the first frame of the file. + if (frame_number == 0) { + char frame_header[Y4M_FILE_HEADER_MAX_SIZE]; + size_t bytes_read = + fread(frame_header, 1, Y4M_FILE_HEADER_MAX_SIZE, input_file); + if (bytes_read != static_cast(frame_size) && ferror(input_file)) { + fprintf(stdout, "Error while reading first frame from file %s\n", + y4m_file_name); + fclose(input_file); + return false; + } + std::string header_contents(frame_header); + std::size_t found = header_contents.find(Y4M_FRAME_DELIMITER); + if (found == std::string::npos) { + fprintf(stdout, "Corrupted Y4M header, could not find \"FRAME\" in %s\n", + header_contents.c_str()); + fclose(input_file); + return false; + } + frame_offset = static_cast(found); + } + + // Change stream pointer to new offset, skipping the frame header as well. + fseek(input_file, frame_offset + Y4M_FRAME_HEADER_SIZE, SEEK_SET); size_t bytes_read = fread(result_frame, 1, frame_size, input_file); if (bytes_read != static_cast(frame_size) && ferror(input_file)) { fprintf(stdout, "Error while reading frame no %d from file %s\n", - frame_number, i420_file_name); + frame_number, y4m_file_name); errors = true; } + fclose(input_file); return !errors; } @@ -176,6 +214,12 @@ void RunAnalysis(const char* reference_file_name, const char* test_file_name, const char* stats_file_name, int width, int height, ResultsContainer* results) { + // Check if the reference_file_name ends with "y4m". + bool y4m_mode = false; + if (std::string(reference_file_name).find("y4m") != std::string::npos){ + y4m_mode = true; + } + int size = GetI420FrameSize(width, height); FILE* stats_file = fopen(stats_file_name, "r"); @@ -202,10 +246,15 @@ assert(extracted_test_frame != -1); assert(decoded_frame_number != -1); - ExtractFrameFromI420(test_file_name, width, height, extracted_test_frame, - test_frame); - ExtractFrameFromI420(reference_file_name, width, height, - decoded_frame_number, reference_frame); + ExtractFrameFromYuvFile(test_file_name, width, height, extracted_test_frame, + test_frame); + if (y4m_mode) { + ExtractFrameFromY4mFile(reference_file_name, width, height, + decoded_frame_number, reference_frame); + } else { + ExtractFrameFromYuvFile(reference_file_name, width, height, + decoded_frame_number, reference_frame); + } // Calculate the PSNR and SSIM. double result_psnr = CalculateMetrics(kPSNR, reference_frame, test_frame, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/frame_analyzer/video_quality_analysis.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/frame_analyzer/video_quality_analysis.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/frame_analyzer/video_quality_analysis.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/frame_analyzer/video_quality_analysis.h 2015-02-03 14:33:38.000000000 +0000 @@ -97,13 +97,14 @@ // frame_0023 0284, we will get 284. int ExtractDecodedFrameNumber(std::string line); -// Gets the next frame from an open I420 file. -bool GetNextI420Frame(FILE* input_file, int width, int height, - uint8* result_frame); +// Extracts an I420 frame at position frame_number from the raw YUV file. +bool ExtractFrameFromYuvFile(const char* i420_file_name, int width, int height, + int frame_number, uint8* result_frame); -// Extracts an I420 frame at position frame_number from the file. -bool ExtractFrameFromI420(const char* i420_file_name, int width, int height, - int frame_number, uint8* result_frame); +// Extracts an I420 frame at position frame_number from the Y4M file. The first +// frame has corresponded |frame_number| 0. +bool ExtractFrameFromY4mFile(const char* i420_file_name, int width, int height, + int frame_number, uint8* result_frame); } // namespace test diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/frame_editing/frame_editing_lib.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/frame_editing/frame_editing_lib.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/frame_editing/frame_editing_lib.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/frame_editing/frame_editing_lib.cc 2015-02-03 14:33:38.000000000 +0000 @@ -38,7 +38,7 @@ // Frame size of I420. int frame_length = CalcBufferSize(kI420, width, height); - webrtc::scoped_array temp_buffer(new uint8_t[frame_length]); + webrtc::scoped_ptr temp_buffer(new uint8_t[frame_length]); FILE* out_fid = fopen(out_path.c_str(), "wb"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/frame_editing/frame_editing_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/frame_editing/frame_editing_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/frame_editing/frame_editing_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/frame_editing/frame_editing_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -30,7 +30,8 @@ protected: virtual void SetUp() { reference_video_ = ResourcePath("foreman_cif", "yuv"); - test_video_ = OutputPath() + "testvideo.yuv"; + test_video_ = webrtc::test::TempFilename(webrtc::test::OutputPath(), + "frame_editing_unittest.yuv"); original_fid_ = fopen(reference_video_.c_str(), "rb"); ASSERT_TRUE(original_fid_ != NULL); @@ -50,11 +51,12 @@ virtual void TearDown() { fclose(original_fid_); fclose(edited_fid_); + remove(test_video_.c_str()); } // Compares the frames in both streams to the end of one of the streams. void CompareToTheEnd(FILE* test_video_fid, FILE* ref_video_fid, - scoped_array* ref_buffer, - scoped_array* test_buffer) { + scoped_ptr* ref_buffer, + scoped_ptr* test_buffer) { while (!feof(test_video_fid) && !feof(ref_video_fid)) { num_bytes_read_ = fread(ref_buffer->get(), 1, kFrameSize, ref_video_fid); if (!feof(ref_video_fid)) { @@ -78,8 +80,8 @@ FILE* original_fid_; FILE* edited_fid_; int num_bytes_read_; - scoped_array original_buffer_; - scoped_array edited_buffer_; + scoped_ptr original_buffer_; + scoped_ptr edited_buffer_; int num_frames_read_; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/adapter.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/adapter.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/adapter.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/adapter.js 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,211 @@ +/** + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file is cloned from samples/js/base/adapter.js +// Modify the original and do new copy instead of doing changes here. + +var RTCPeerConnection = null; +var getUserMedia = null; +var attachMediaStream = null; +var reattachMediaStream = null; +var webrtcDetectedBrowser = null; +var webrtcDetectedVersion = null; + +function trace(text) { + // This function is used for logging. + if (text[text.length - 1] == '\n') { + text = text.substring(0, text.length - 1); + } + console.log((performance.now() / 1000).toFixed(3) + ": " + text); +} +function maybeFixConfiguration(pcConfig) { + if (pcConfig == null) { + return; + } + for (var i = 0; i < pcConfig.iceServers.length; i++) { + if (pcConfig.iceServers[i].hasOwnProperty('urls')){ + pcConfig.iceServers[i]['url'] = pcConfig.iceServers[i]['urls']; + delete pcConfig.iceServers[i]['urls']; + } + } +} + +if (navigator.mozGetUserMedia) { + console.log("This appears to be Firefox"); + + webrtcDetectedBrowser = "firefox"; + + webrtcDetectedVersion = + parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10); + + // The RTCPeerConnection object. + var RTCPeerConnection = function(pcConfig, pcConstraints) { + // .urls is not supported in FF yet. + maybeFixConfiguration(pcConfig); + return new mozRTCPeerConnection(pcConfig, pcConstraints); + } + + // The RTCSessionDescription object. + RTCSessionDescription = mozRTCSessionDescription; + + // The RTCIceCandidate object. + RTCIceCandidate = mozRTCIceCandidate; + + // Get UserMedia (only difference is the prefix). + // Code from Adam Barth. + getUserMedia = navigator.mozGetUserMedia.bind(navigator); + navigator.getUserMedia = getUserMedia; + + // Creates iceServer from the url for FF. + createIceServer = function(url, username, password) { + var iceServer = null; + var url_parts = url.split(':'); + if (url_parts[0].indexOf('stun') === 0) { + // Create iceServer with stun url. + iceServer = { 'url': url }; + } else if (url_parts[0].indexOf('turn') === 0) { + if (webrtcDetectedVersion < 27) { + // Create iceServer with turn url. + // Ignore the transport parameter from TURN url for FF version <=27. + var turn_url_parts = url.split("?"); + // Return null for createIceServer if transport=tcp. + if (turn_url_parts.length === 1 || + turn_url_parts[1].indexOf('transport=udp') === 0) { + iceServer = {'url': turn_url_parts[0], + 'credential': password, + 'username': username}; + } + } else { + // FF 27 and above supports transport parameters in TURN url, + // So passing in the full url to create iceServer. + iceServer = {'url': url, + 'credential': password, + 'username': username}; + } + } + return iceServer; + }; + + createIceServers = function(urls, username, password) { + var iceServers = []; + // Use .url for FireFox. + for (i = 0; i < urls.length; i++) { + var iceServer = createIceServer(urls[i], + username, + password); + if (iceServer !== null) { + iceServers.push(iceServer); + } + } + return iceServers; + } + + // Attach a media stream to an element. + attachMediaStream = function(element, stream) { + console.log("Attaching media stream"); + element.mozSrcObject = stream; + element.play(); + }; + + reattachMediaStream = function(to, from) { + console.log("Reattaching media stream"); + to.mozSrcObject = from.mozSrcObject; + to.play(); + }; + + // Fake get{Video,Audio}Tracks + if (!MediaStream.prototype.getVideoTracks) { + MediaStream.prototype.getVideoTracks = function() { + return []; + }; + } + + if (!MediaStream.prototype.getAudioTracks) { + MediaStream.prototype.getAudioTracks = function() { + return []; + }; + } +} else if (navigator.webkitGetUserMedia) { + console.log("This appears to be Chrome"); + + webrtcDetectedBrowser = "chrome"; + webrtcDetectedVersion = + parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10); + + // Creates iceServer from the url for Chrome M33 and earlier. + createIceServer = function(url, username, password) { + var iceServer = null; + var url_parts = url.split(':'); + if (url_parts[0].indexOf('stun') === 0) { + // Create iceServer with stun url. + iceServer = { 'url': url }; + } else if (url_parts[0].indexOf('turn') === 0) { + // Chrome M28 & above uses below TURN format. + iceServer = {'url': url, + 'credential': password, + 'username': username}; + } + return iceServer; + }; + + // Creates iceServers from the urls for Chrome M34 and above. + createIceServers = function(urls, username, password) { + var iceServers = []; + if (webrtcDetectedVersion >= 34) { + // .urls is supported since Chrome M34. + iceServers = {'urls': urls, + 'credential': password, + 'username': username }; + } else { + for (i = 0; i < urls.length; i++) { + var iceServer = createIceServer(urls[i], + username, + password); + if (iceServer !== null) { + iceServers.push(iceServer); + } + } + } + return iceServers; + }; + + // The RTCPeerConnection object. + var RTCPeerConnection = function(pcConfig, pcConstraints) { + // .urls is supported since Chrome M34. + if (webrtcDetectedVersion < 34) { + maybeFixConfiguration(pcConfig); + } + return new webkitRTCPeerConnection(pcConfig, pcConstraints); + } + + // Get UserMedia (only difference is the prefix). + // Code from Adam Barth. + getUserMedia = navigator.webkitGetUserMedia.bind(navigator); + navigator.getUserMedia = getUserMedia; + + // Attach a media stream to an element. + attachMediaStream = function(element, stream) { + if (typeof element.srcObject !== 'undefined') { + element.srcObject = stream; + } else if (typeof element.mozSrcObject !== 'undefined') { + element.mozSrcObject = stream; + } else if (typeof element.src !== 'undefined') { + element.src = URL.createObjectURL(stream); + } else { + console.log('Error attaching stream to element.'); + } + }; + + reattachMediaStream = function(to, from) { + to.src = from.src; + }; +} else { + console.log("Browser does not appear to be WebRTC-capable"); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/loopback_test.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/loopback_test.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/loopback_test.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/loopback_test.html 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,227 @@ + + + + +Loopback test + + + + + + + + + + + + + + + + +
    +

    Duration (s):

    +

    Max video bitrate (kbps):

    +

    Peer connection constraints:

    +

    Force TURN:

    +

    +

    +
    +
    +
    +
    +
    +
    + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/loopback_test.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/loopback_test.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/loopback_test.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/loopback_test.js 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,240 @@ +/** + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// LoopbackTest establish a one way loopback call between 2 peer connections +// while continuously monitoring bandwidth stats. The idea is to use this as +// a base for other future tests and to keep track of more than just bandwidth +// stats. +// +// Usage: +// var test = new LoopbackTest(stream, callDurationMs, +// forceTurn, pcConstraints, +// maxVideoBitrateKbps); +// test.run(onDone); +// function onDone() { +// test.getResults(); // return stats recorded during the loopback test. +// } +// +function LoopbackTest( + stream, + callDurationMs, + forceTurn, + pcConstraints, + maxVideoBitrateKbps) { + + var pc1StatTracker; + var pc2StatTracker; + + // In order to study effect of network (e.g. wifi) on peer connection one can + // establish a loopback call and force it to go via a turn server. This way + // the call won't switch to local addresses. That is achieved by filtering out + // all non-relay ice candidades on both peers. + function constrainTurnCandidates(pc) { + var origAddIceCandidate = pc.addIceCandidate; + pc.addIceCandidate = function (candidate, successCallback, + failureCallback) { + if (forceTurn && candidate.candidate.indexOf("typ relay ") == -1) { + trace("Dropping non-turn candidate: " + candidate.candidate); + successCallback(); + return; + } else { + origAddIceCandidate.call(this, candidate, successCallback, + failureCallback); + } + } + } + + // FEC makes it hard to study bwe estimation since there seems to be a spike + // when it is enabled and disabled. Disable it for now. FEC issue tracked on: + // https://code.google.com/p/webrtc/issues/detail?id=3050 + function constrainOfferToRemoveFec(pc) { + var origCreateOffer = pc.createOffer; + pc.createOffer = function (successCallback, failureCallback, options) { + function filteredSuccessCallback(desc) { + desc.sdp = desc.sdp.replace(/(m=video 1 [^\r]+)(116 117)(\r\n)/g, + '$1\r\n'); + desc.sdp = desc.sdp.replace(/a=rtpmap:116 red\/90000\r\n/g, ''); + desc.sdp = desc.sdp.replace(/a=rtpmap:117 ulpfec\/90000\r\n/g, ''); + successCallback(desc); + } + origCreateOffer.call(this, filteredSuccessCallback, failureCallback, + options); + } + } + + // Constraint max video bitrate by modifying the SDP when creating an answer. + function constrainBitrateAnswer(pc) { + var origCreateAnswer = pc.createAnswer; + pc.createAnswer = function (successCallback, failureCallback, options) { + function filteredSuccessCallback(desc) { + if (maxVideoBitrateKbps) { + desc.sdp = desc.sdp.replace( + /a=mid:video\r\n/g, + 'a=mid:video\r\nb=AS:' + maxVideoBitrateKbps + '\r\n'); + } + successCallback(desc); + } + origCreateAnswer.call(this, filteredSuccessCallback, failureCallback, + options); + } + } + + // Run the actual LoopbackTest. + this.run = function(doneCallback) { + if (forceTurn) requestTurn(start, fail); + else start(); + + function start(turnServer) { + var pcConfig = forceTurn ? { iceServers: [turnServer] } : null; + console.log(pcConfig); + var pc1 = new RTCPeerConnection(pcConfig, pcConstraints); + constrainTurnCandidates(pc1); + constrainOfferToRemoveFec(pc1); + pc1StatTracker = new StatTracker(pc1, 50); + pc1StatTracker.recordStat("EstimatedSendBitrate", + "bweforvideo", "googAvailableSendBandwidth"); + pc1StatTracker.recordStat("TransmitBitrate", + "bweforvideo", "googTransmitBitrate"); + pc1StatTracker.recordStat("TargetEncodeBitrate", + "bweforvideo", "googTargetEncBitrate"); + pc1StatTracker.recordStat("ActualEncodedBitrate", + "bweforvideo", "googActualEncBitrate"); + + var pc2 = new RTCPeerConnection(pcConfig, pcConstraints); + constrainTurnCandidates(pc2); + constrainBitrateAnswer(pc2); + pc2StatTracker = new StatTracker(pc2, 50); + pc2StatTracker.recordStat("REMB", + "bweforvideo", "googAvailableReceiveBandwidth"); + + pc1.addStream(stream); + var call = new Call(pc1, pc2); + + call.start(); + setTimeout(function () { + call.stop(); + pc1StatTracker.stop(); + pc2StatTracker.stop(); + success(); + }, callDurationMs); + } + + function success() { + trace("Success"); + doneCallback(); + } + + function fail(msg) { + trace("Fail: " + msg); + doneCallback(); + } + } + + // Returns a google visualization datatable with the recorded samples during + // the loopback test. + this.getResults = function () { + return mergeDataTable(pc1StatTracker.dataTable(), + pc2StatTracker.dataTable()); + } + + // Helper class to establish and manage a call between 2 peer connections. + // Usage: + // var c = new Call(pc1, pc2); + // c.start(); + // c.stop(); + // + function Call(pc1, pc2) { + pc1.onicecandidate = applyIceCandidate.bind(pc2); + pc2.onicecandidate = applyIceCandidate.bind(pc1); + + function applyIceCandidate(e) { + if (e.candidate) { + this.addIceCandidate(new RTCIceCandidate(e.candidate), + onAddIceCandidateSuccess, + onAddIceCandidateError); + } + } + + function onAddIceCandidateSuccess() {} + function onAddIceCandidateError(error) { + trace("Failed to add Ice Candidate: " + error.toString()); + } + + this.start = function() { + pc1.createOffer(gotDescription1, onCreateSessionDescriptionError); + + function onCreateSessionDescriptionError(error) { + trace('Failed to create session description: ' + error.toString()); + } + + function gotDescription1(desc){ + trace("Offer: " + desc.sdp); + pc1.setLocalDescription(desc); + pc2.setRemoteDescription(desc); + // Since the "remote" side has no media stream we need + // to pass in the right constraints in order for it to + // accept the incoming offer of audio and video. + pc2.createAnswer(gotDescription2, onCreateSessionDescriptionError); + } + + function gotDescription2(desc){ + trace("Answer: " + desc.sdp); + pc2.setLocalDescription(desc); + pc1.setRemoteDescription(desc); + } + } + + this.stop = function() { + pc1.close(); + pc2.close(); + } + } + + // Request a turn server. This uses the same servers as apprtc. + function requestTurn(successCallback, failureCallback) { + var currentDomain = document.domain; + if (currentDomain.search('localhost') === -1 && + currentDomain.search('webrtc.googlecode.com') === -1) { + failureCallback("Domain not authorized for turn server: " + + currentDomain); + return; + } + + // Get a turn server from computeengineondemand.appspot.com. + var turnUrl = 'https://computeengineondemand.appspot.com/' + + 'turn?username=156547625762562&key=4080218913'; + var xmlhttp = new XMLHttpRequest(); + xmlhttp.onreadystatechange = onTurnResult; + xmlhttp.open('GET', turnUrl, true); + xmlhttp.send(); + + function onTurnResult() { + if (this.readyState !== 4) { + return; + } + + if (this.status === 200) { + var turnServer = JSON.parse(xmlhttp.responseText); + // Create turnUris using the polyfill (adapter.js). + turnServer.uris = turnServer.uris.filter( + function (e) { return e.search('transport=udp') != -1; } + ); + var iceServers = createIceServers(turnServer.uris, + turnServer.username, + turnServer.password); + if (iceServers !== null) { + successCallback(iceServers); + return; + } + } + failureCallback("Failed to get a turn server."); + } + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/OWNERS 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1 @@ +andresp@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/README thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/README --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/README 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/README 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,12 @@ +Loopback test + +This is a simple html test framework to run a loopback test which can go via +turn. For now the test is used to analyse bandwidth estimation and get records +for bad scenarios. + +How to run: + ./run-server.sh (to start python serving the tests) + Access http://localhost:8080/loopback_test.html to run the test + +How to record: + You can use record-test.sh to get a tcpdump of a test run. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/record-test.sh thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/record-test.sh --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/record-test.sh 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/record-test.sh 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,60 @@ +#!/bin/sh +# +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. +# +# This script is used to record a tcp dump of running a loop back test. +# Example use case: +# +# $ ./run-server.sh & # spawns a server to serve the html pages +# # on localhost:8080 +# +# (recording 3 tests with 5mins and bitrates 1mbps, 2mbps and 3mbps) +# $ sudo -v # Caches sudo credentials needed +# # for tcpdump +# $ export INTERFACE=eth1 # Defines interface to record packets +# $ export CHROME_UNDER_TESTING=./chrome # Define which chrome to run on tests +# $ export TEST="http://localhost:8080/loopback_test.html?auto-mode=true" +# $ record-test.sh ./record1.pcap "$TEST&duration=300&max-video-bitrate=1000" +# $ record-test.sh ./record2.pcap "$TEST&duration=300&max-video-bitrate=2000" +# $ record-test.sh ./record3.pcap "$TEST&duration=300&max-video-bitrate=3000" + +# Indicate an error and exit with a nonzero status if any of the required +# environment variables is Null or Unset. +: ${INTERFACE:?"Need to set INTERFACE env variable"} +: ${CHROME_UNDER_TESTING:?"Need to set CHROME_UNDER_TESTING env variable"} + +if [ ! -x "$CHROME_UNDER_TESTING" ]; then + echo "CHROME_UNDER_TESTING=$CHROME_UNDER_TESTING does not seem to exist." + exit 1 +fi + +if [ "$#" -ne 2 ]; then + echo "Usage: $0 " + exit 1 +fi +TEST_URL=$1 +OUTPUT_RECORDING=$2 + +sudo -nv > /dev/null 2>&1 +if [ $? != 0 ]; then + echo "Run \"sudo -v\" to cache your credentials." \ + "They are needed to run tcpdump." + exit +fi + +echo "Recording $INTERFACE into ${OUTPUT_RECORDING}" +sudo -n tcpdump -i "$INTERFACE" -w - > "${OUTPUT_RECORDING}" & +TCPDUMP_PID=$! + +echo "Starting ${CHROME_UNDER_TESTING} with ${TEST_URL}." +# Using real camera instead of --use-fake-device-for-media-stream as it +# does not produces images complex enough to reach 3mbps. +# Flag --use-fake-ui-for-media-stream automatically allows getUserMedia calls. +$CHROME_UNDER_TESTING --use-fake-ui-for-media-stream "${TEST_URL}" +kill ${TCPDUMP_PID} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/run-server.sh thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/run-server.sh --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/run-server.sh 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/run-server.sh 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,15 @@ +#!/bin/sh +# +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. +# +# This script is used to launch a simple http server for files in the same +# location as the script itself. +cd "`dirname \"$0\"`" +echo "Starting http server in port 8080." +exec python -m SimpleHTTPServer 8080 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/stat_tracker.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/stat_tracker.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/stat_tracker.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/loopback_test/stat_tracker.js 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// StatTracker is a helper class to keep track of stats on a RTCPeerConnection +// object. It uses google visualization datatables to keep the recorded samples +// and simplify plugging them into graphs later. +// +// Usage example: +// var tracker = new StatTracker(pc, pollInterval); +// tracker.recordStat("EstimatedSendBitrate", +// "bweforvideo", "googAvailableSendBandwidth"); +// ... +// tracker.stop(); +// tracker.dataTable(); // returns the recorded values. In this case +// a table with 2 columns { Time, EstimatedSendBitrate } and a row for each +// sample taken until stop() was called. +// +function StatTracker(pc, pollInterval) { + pollInterval = pollInterval || 250; + + var dataTable = new google.visualization.DataTable(); + var timeColumnIndex = dataTable.addColumn('datetime', 'Time'); + var recording = true; + + // Set of sampling functions. Functions registered here are called + // once per getStats with the given report and a rowIndex for the + // sample period so they can extract and record the tracked variables. + var samplingFunctions = {}; + + // Accessor to the current recorded stats. + this.dataTable = function() { return dataTable; } + + // recordStat(varName, recordName, statName) adds a samplingFunction that + // records namedItem(recordName).stat(statName) from RTCStatsReport for each + // sample into a column named varName in the dataTable. + this.recordStat = function (varName, recordName, statName) { + var columnIndex = dataTable.addColumn('number', varName); + samplingFunctions[varName] = function (report, rowIndex) { + var sample; + var record = report.namedItem(recordName); + if (record) sample = record.stat(statName); + dataTable.setCell(rowIndex, columnIndex, sample); + } + } + + // Stops the polling of stats from the peer connection. + this.stop = function() { + recording = false; + } + + // RTCPeerConnection.getStats is asynchronous. In order to avoid having + // too many pending getStats requests going, this code only queues the + // next getStats with setTimeout after the previous one returns, instead + // of using setInterval. + function poll() { + pc.getStats(function (report) { + if (!recording) return; + setTimeout(poll, pollInterval); + var result = report.result(); + if (result.length < 1) return; + + var rowIndex = dataTable.addRow(); + dataTable.setCell(rowIndex, timeColumnIndex, result[0].timestamp); + for (var v in samplingFunctions) + samplingFunctions[v](report, rowIndex); + }); + } + setTimeout(poll, pollInterval); +} + +/** + * Utility method to perform a full join between data tables from StatTracker. + */ +function mergeDataTable(dataTable1, dataTable2) { + function allColumns(cols) { + var a = []; + for (var i = 1; i < cols; ++i) a.push(i); + return a; + } + return google.visualization.data.join( + dataTable1, + dataTable2, + 'full', + [[0, 0]], + allColumns(dataTable1.getNumberOfColumns()), + allColumns(dataTable2.getNumberOfColumns())); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/OWNERS 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/OWNERS 2015-02-03 14:33:38.000000000 +0000 @@ -1,2 +1,9 @@ phoglund@webrtc.org -kjellander@webrtc.org \ No newline at end of file +kjellander@webrtc.org + +per-file *.isolate=kjellander@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/psnr_ssim_analyzer/psnr_ssim_analyzer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/psnr_ssim_analyzer/psnr_ssim_analyzer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/psnr_ssim_analyzer/psnr_ssim_analyzer.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/psnr_ssim_analyzer/psnr_ssim_analyzer.cc 2015-02-03 14:33:38.000000000 +0000 @@ -8,6 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include #include #include @@ -18,10 +19,16 @@ #include "webrtc/tools/frame_analyzer/video_quality_analysis.h" #include "webrtc/tools/simple_command_line_parser.h" +#define MAX_NUM_FRAMES_PER_FILE INT_MAX + void CompareFiles(const char* reference_file_name, const char* test_file_name, const char* results_file_name, int width, int height) { - FILE* ref_file = fopen(reference_file_name, "rb"); - FILE* test_file = fopen(test_file_name, "rb"); + // Check if the reference_file_name ends with "y4m". + bool y4m_mode = false; + if (std::string(reference_file_name).find("y4m") != std::string::npos){ + y4m_mode = true; + } + FILE* results_file = fopen(results_file_name, "w"); int size = webrtc::test::GetI420FrameSize(width, height); @@ -30,10 +37,19 @@ uint8* test_frame = new uint8[size]; uint8* ref_frame = new uint8[size]; - int frame_counter = 0; + bool read_result = true; + for(int frame_counter = 0; frame_counter < MAX_NUM_FRAMES_PER_FILE; + ++frame_counter){ + read_result &= (y4m_mode) ? webrtc::test::ExtractFrameFromY4mFile( + reference_file_name, width, height, frame_counter, ref_frame): + webrtc::test::ExtractFrameFromYuvFile(reference_file_name, width, + height, frame_counter, ref_frame); + read_result &= webrtc::test::ExtractFrameFromYuvFile(test_file_name, width, + height, frame_counter, test_frame); + + if (!read_result) + break; - while (webrtc::test::GetNextI420Frame(ref_file, width, height, ref_frame) && - webrtc::test::GetNextI420Frame(test_file, width, height, test_frame)) { // Calculate the PSNR and SSIM. double result_psnr = webrtc::test::CalculateMetrics( webrtc::test::kPSNR, ref_frame, test_frame, width, height); @@ -41,13 +57,10 @@ webrtc::test::kSSIM, ref_frame, test_frame, width, height); fprintf(results_file, "Frame: %d, PSNR: %f, SSIM: %f\n", frame_counter, result_psnr, result_ssim); - ++frame_counter; } delete[] test_frame; delete[] ref_frame; - fclose(ref_file); - fclose(test_file); fclose(results_file); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/bot/api.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/bot/api.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/bot/api.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/bot/api.js 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,37 @@ +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// +// This file exposes the api for the bot to connect to the host script +// waiting a websocket connection and using dnode for javascript rpc. +// +// This file is served to the browser via browserify to resolve the +// dnode requires. +var WebSocketStream = require('websocket-stream'); +var Dnode = require('dnode'); + +function connectToServer(api) { + var stream = new WebSocketStream("wss://localhost:8080/"); + var dnode = new Dnode(api); + dnode.on('error', function (error) { console.log(error); }); + dnode.pipe(stream).pipe(dnode); +} + +// Dnode loses certain method calls when exposing native browser objects such as +// peer connections. This methods helps work around that by allowing one to +// redefine a non-native method in a target "obj" from "src" that applies a list +// of casts to the arguments (types are lost in dnode). +function expose(obj, src, method, casts) { + obj[method] = function () { + for (index in casts) + arguments[index] = new (casts[index])(arguments[index]); + src[method].apply(src, arguments); + } +} + +window.expose = expose; +window.connectToServer = connectToServer; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/bot/browser/bot.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/bot/browser/bot.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/bot/browser/bot.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/bot/browser/bot.js 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,140 @@ +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// +var localStreams = []; +var remoteStreams = []; + +function ping(callback) { + callback("pong"); +} + +function getUserMedia(constraints, onSuccessCallback, onFailCallback){ + console.log("Getting user media."); + navigator.webkitGetUserMedia(constraints, + onSuccessCallbackWraper, onFailCallback); + + function onSuccessCallbackWraper(stream) { + console.log("GetUserMedia success."); + localStreams[stream.id] = stream; + onSuccessCallback(stream); + } +} + +function createPeerConnection(config, doneCallback, failCallback) { + console.log("Creating peer connection"); + var obj = {}; + var pc = new webkitRTCPeerConnection(config); + + expose(obj, pc, "close"); + expose(obj, pc, "createOffer"); + expose(obj, pc, "createAnswer"); + expose(obj, pc, "addEventListener"); + expose(obj, pc, "addIceCandidate", { 0: RTCIceCandidate}); + expose(obj, pc, "setRemoteDescription", { 0: RTCSessionDescription }); + expose(obj, pc, "setLocalDescription", { 0: RTCSessionDescription }); + + obj.addStream = function(stream) { + console.log("Adding local stream."); + var tempStream = localStreams[stream.id]; + if (!tempStream) { + console.log("Undefined stream!"); + return; + } + pc.addStream(tempStream); + }; + + // Return an array of Objects, each Object is a copy of RTCStateReport + // and has the following attributes (id, type, names, and stats). + // names: array originaly returned by calling RTCStateReport.names(). + // stats: dictionary of stat name as key and stat value as dictionary + // value. + obj.getStats = function(callback, mediaTrack) { + pc.getStats(onStatsReady, mediaTrack); + + function onStatsReady(stateResponse) { + var outputReports = []; + var reports = stateResponse.result(); + for (index in reports) { + var report = {}; + report.id = reports[index].id; + report.type = reports[index].type; + report.names = reports[index].names(); + report.stats = []; + populateStats(reports[index], report.stats); + + outputReports.push(report); + } + + callback(outputReports); + } + + function populateStats(report, stats) { + var names = report.names(); + for (index in names) { + stats.push({ + name: names[index], + stat: report.stat(names[index]), + }); + } + + } + }; + + pc.addEventListener('addstream', function(event) { + remoteStreams[event.stream.id] = event.stream; + }); + + doneCallback(obj); +}; + +function showStream(streamId, autoplay, muted) { + var stream = getStreamFromIdentifier_(streamId); + var video = document.createElement('video'); + video.autoplay = autoplay; + video.muted = muted; + document.body.appendChild(video); + video.src = URL.createObjectURL(stream); + console.log("Stream " + stream.id + " attached to video element"); +}; + +function getStreamFromIdentifier_(id) { + var tempStream = localStreams[id]; + if (tempStream) + return tempStream; + tempStream = remoteStreams[id]; + if (tempStream) + return tempStream; + console.log(id + " is not id for stream."); + return null; +}; + +function downloadFile(path, onSuccess, onError) { + var xhr = new XMLHttpRequest(); + function onResult() { + if (xhr.readyState != 4) + return; + + if (xhr.status != 200) { + onError("Download request failed!"); + return; + } + onSuccess(xhr.responseText); + } + + xhr.onreadystatechange = onResult; + xhr.open('GET', path, true); + xhr.send(); +}; + +connectToServer({ + ping: ping, + getUserMedia: getUserMedia, + createPeerConnection: createPeerConnection, + showStream: showStream, + downloadFile: downloadFile, +}); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/bot/browser/index.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/bot/browser/index.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/bot/browser/index.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/bot/browser/index.html 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,11 @@ + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/botmanager.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/botmanager.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/botmanager.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/botmanager.js 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,216 @@ +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// +// botmanager.js module allows a test to spawn bots that expose an RPC API +// to be controlled by tests. +var https = require('https'); +var fs = require('fs'); +var child = require('child_process'); +var Browserify = require('browserify'); +var Dnode = require('dnode'); +var Express = require('express'); +var WebSocketServer = require('ws').Server; +var WebSocketStream = require('websocket-stream'); + +// BotManager runs a HttpsServer that serves bots assets and and WebSocketServer +// that listens to incoming connections. Once a connection is available it +// connects it to bots pending endpoints. +// +// TODO(andresp): There should be a way to control which bot was spawned +// and what bot instance it gets connected to. +BotManager = function () { + this.webSocketServer_ = null; + this.bots_ = []; + this.pendingConnections_ = []; + this.androidDeviceManager_ = new AndroidDeviceManager(); +} + +BotManager.BotTypes = { + CHROME : 'chrome', + ANDROID_CHROME : 'android-chrome', +}; + +BotManager.prototype = { + createBot_: function (name, botType, callback) { + switch(botType) { + case BotManager.BotTypes.CHROME: + return new BrowserBot(name, callback); + case BotManager.BotTypes.ANDROID_CHROME: + return new AndroidChromeBot(name, this.androidDeviceManager_, + callback); + default: + console.log('Error: Type ' + botType + ' not supported by rtc-Bot!'); + process.exit(1); + } + }, + + spawnNewBot: function (name, botType, callback) { + this.startWebSocketServer_(); + var bot = this.createBot_(name, botType, callback); + this.bots_.push(bot); + this.pendingConnections_.push(bot.onBotConnected.bind(bot)); + }, + + startWebSocketServer_: function () { + if (this.webSocketServer_) return; + + this.app_ = new Express(); + + this.app_.use('/bot/api.js', + this.serveBrowserifyFile_.bind(this, + __dirname + '/bot/api.js')); + + this.app_.use('/bot/', Express.static(__dirname + '/bot')); + + var options = options = { + key: fs.readFileSync('configurations/priv.pem', 'utf8'), + cert: fs.readFileSync('configurations/cert.crt', 'utf8') + }; + this.server_ = https.createServer(options, this.app_); + + this.webSocketServer_ = new WebSocketServer({ server: this.server_ }); + this.webSocketServer_.on('connection', this.onConnection_.bind(this)); + + this.server_.listen(8080); + }, + + onConnection_: function (ws) { + var callback = this.pendingConnections_.shift(); + callback(new WebSocketStream(ws)); + }, + + serveBrowserifyFile_: function (file, request, result) { + // TODO(andresp): Cache browserify result for future serves. + var browserify = new Browserify(); + browserify.add(file); + browserify.bundle().pipe(result); + } +} + +// A basic bot waits for onBotConnected to be called with a stream to the actual +// endpoint with the bot. Once that stream is available it establishes a dnode +// connection and calls the callback with the other endpoint interface so the +// test can interact with it. +Bot = function (name, callback) { + this.name_ = name; + this.onbotready_ = callback; +} + +Bot.prototype = { + log: function (msg) { + console.log("bot:" + this.name_ + " > " + msg); + }, + + name: function () { return this.name_; }, + + onBotConnected: function (stream) { + this.log('Connected'); + this.stream_ = stream; + this.dnode_ = new Dnode(); + this.dnode_.on('remote', this.onRemoteFromDnode_.bind(this)); + this.dnode_.pipe(this.stream_).pipe(this.dnode_); + }, + + onRemoteFromDnode_: function (remote) { + this.onbotready_(remote); + } +} + +// BrowserBot spawns a process to open "https://localhost:8080/bot/browser". +// +// That page once loaded, connects to the websocket server run by BotManager +// and exposes the bot api. +BrowserBot = function (name, callback) { + Bot.call(this, name, callback); + this.spawnBotProcess_(); +} + +BrowserBot.prototype = { + spawnBotProcess_: function () { + this.log('Spawning browser'); + child.exec('google-chrome "https://localhost:8080/bot/browser/"'); + }, + + __proto__: Bot.prototype +} + +// AndroidChromeBot spawns a process to open +// "https://localhost:8080/bot/browser/" on chrome for Android. +AndroidChromeBot = function (name, androidDeviceManager, callback) { + Bot.call(this, name, callback); + androidDeviceManager.getNewDevice(function (serialNumber) { + this.serialNumber_ = serialNumber; + this.spawnBotProcess_(); + }.bind(this)); +} + +AndroidChromeBot.prototype = { + spawnBotProcess_: function () { + this.log('Spawning Android device with serial ' + this.serialNumber_); + var runChrome = 'adb -s ' + this.serialNumber_ + ' shell am start ' + + '-n com.android.chrome/com.google.android.apps.chrome.Main ' + + '-d https://localhost:8080/bot/browser/'; + child.exec(runChrome, function (error, stdout, stderr) { + if (error) { + this.log(error); + process.exit(1); + } + this.log('Opening Chrome for Android...'); + this.log(stdout); + }.bind(this)); + }, + + __proto__: Bot.prototype +} + +AndroidDeviceManager = function () { + this.connectedDevices_ = []; +} + +AndroidDeviceManager.prototype = { + getNewDevice: function (callback) { + this.listDevices_(function (devices) { + for (var i = 0; i < devices.length; i++) { + if (!this.connectedDevices_[devices[i]]) { + this.connectedDevices_[devices[i]] = devices[i]; + callback(this.connectedDevices_[devices[i]]); + return; + } + } + if (devices.length == 0) { + console.log('Error: No connected devices!'); + } else { + console.log('Error: There is no enough connected devices.'); + } + process.exit(1); + }.bind(this)); + }, + + listDevices_: function (callback) { + child.exec('adb devices' , function (error, stdout, stderr) { + var devices = []; + if (error || stderr) { + console.log(error || stderr); + } + if (stdout) { + // The first line is "List of devices attached" + // and the following lines: + // + var tempList = stdout.split("\n").slice(1); + for (var i = 0; i < tempList.length; i++) { + if (tempList[i] == "") { + continue; + } + devices.push(tempList[i].split("\t")[0]); + } + } + callback(devices); + }); + }, +} +module.exports = BotManager; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/main.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/main.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/main.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/main.js 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,102 @@ +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// +// This script loads all the test/* files into a very small context that +// only exposes a minimal set of functions that allows to register tests. +// +// Once all files are loaded it runs the specific test on the command line. +// If no arguments are given it lists all the registered tests. +// +// Note: the small context where the scripts are loaded is intended to keep +// nodejs-isms away from the test code and isolate implementation details away +// from them. +var fs = require('fs'); +var vm = require('vm'); +var Test = require('./test.js'); + +var testSuites = {}; + +function registerTest(name, func) { + testSuites[name] = func; +} + +function registerBotTest(name, func, bots) { + registerTest(name, bootstrap); + + function bootstrap(test) { + var callbacks = []; + for (var i = 0; i != bots.length; ++i) + callbacks.push(test.spawnBot.bind(test, "", bots[i])); + + test.wait(callbacks, func.bind(test, test)); + } +} + +function loadTestFile(filename, doneCallback) { + var loadTestContext = { + setTimeout: setTimeout, + registerTest: registerTest, + registerBotTest: registerBotTest + }; + var script = vm.createScript(fs.readFileSync(filename), filename); + script.runInNewContext(loadTestContext); + doneCallback(); +} + +function iterateOverTestFiles(foreachCallback, doneCallback) { + fs.readdir('test', function (error, list) { + function iterateNextFile() { + if (list.length === 0) { + doneCallback(); + } else { + var filename = list.pop(); + if (filename[0] === '.' || filename.slice(-3) !== '.js') { + // Skip hidden and non .js files on that directory. + iterateNextFile(); + } else { + foreachCallback('test/' + filename, iterateNextFile); + } + } + } + + if (error !== null) { + throw error; + } + iterateNextFile(); + }); +} + +function runTest(testname) { + if (testname in testSuites) { + console.log("Running test: " + testname); + var test = new Test(); + testSuites[testname](test); + } else { + console.log("Unknown test: " + testname); + } +} + +function printUsage() { + console.log('Run as:\n $ ' + + process.argv[0] + ' ' + process.argv[1] + + ' '); + console.log('These are the existent ones:'); + for (var testname in testSuites) + console.log(' ' + testname); +} + +function main() { + // TODO(andresp): support multiple tests. + var testList = process.argv.slice(2); + if (testList.length === 1) + runTest(testList[0]); + else + printUsage(); +} + +iterateOverTestFiles(loadTestFile, main); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/OWNERS 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,2 @@ +andresp@webrtc.org +houssainy@google.com diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/README thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/README --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/README 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/README 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,59 @@ +=== RTCBot === +RTCBot is a framework to write tests that need to spawn multiple webrtc +endpoints. + +== Description == +RTCBot is a framework that allows to write tests where logic runs on a single +host that controls multiple endpoints ("bots"). It allows creating complex +scenarios that would otherwise require non-trival signalling between multiple +parties. + +The host runs in node.js, but the test code is run in an isolated context with +no access to node.js specifics other than the exposed api via a test variable. + +Part of the exposed api (test.spawnBot) allows a test to spawn a bot and +access its exposed API. Details are in botmanager.js. + +== How to run the test == + $ cd trunk/webrtc/tool/rtcbot + $ npm install express browserify ws websocket-stream dnode + $ mkdir configurations + $ cd configurations + $ openssl genrsa -out priv.pem 1024 + $ openssl req -x509 -new -key priv.pem -days 3650 -out cert.crt + $ cd trunk/webrtc/tool/rtcbot + $ node main.js "" + +* Note: + In first time you will use rtcBot you will receive a warning telling + you that your connection is not private. Just avoid this warning and + click Proceed to localhost (unsafe). + +== How can I see the list of available tests? == + $ node main.js + +== Example on how to install nodejs == + $ cd /work/tools/ + $ git clone https://github.com/creationix/nvm.git + $ export NVM_DIR=/work/tools/nvm; source $NVM_DIR/nvm.sh + $ nvm install 0.10 + $ nvm use 0.10 + +== Why generating the private key and self signed certificate? == + - Private key and certificate are used for creating HTTPs server in + rtcBot for loading the required files on the different types of the bots. + +== Supported Bot Types == + - "chrome": chrome on host machine. + - "android-chrome": chrome on android device. Details in "Android" Section. + + * Bot type is specified directly by the test. + +== Android == +Before running test with Android one MUST forward the device port 8080 to the +host machine. That is easy to achieve with chrome port forwarding tools. + - Visit chrome://inspect/devices on the host machine. + - Configure and enable port forwarding 8080 -> localhost:8080 + - Open chrome on you Android device before running test, and leave it + running until the end of test. + - Run your test. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/rtcBotReportVisualizer/index.html thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/rtcBotReportVisualizer/index.html --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/rtcBotReportVisualizer/index.html 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/rtcBotReportVisualizer/index.html 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/rtcBotReportVisualizer/main.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/rtcBotReportVisualizer/main.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/rtcBotReportVisualizer/main.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/rtcBotReportVisualizer/main.js 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,191 @@ +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// +google.load("visualization", "1", {packages:["corechart"]}); + +function openFiles(event) { + var files = event.target.files; + readAndAnalyzeFiles(files) +} + +function readAndAnalyzeFiles(files) { + if(!files) { + alert("No files have been selected!"); + return; + } + + var reports = []; + var filesNames = []; + missingFiles = files.length; + + for(var i = 0; i < files.length; i++) { + var reader = new FileReader(); + reader.onload = onReaderLoad.bind(reader, files[i].name); + reader.readAsText(files[i]); + } + + function onReaderLoad(fileName) { + reports.push(JSON.parse(this.result)); + filesNames.push(fileName); + + missingFiles--; + if(missingFiles == 0) { + analyzeReports_(reports, filesNames); + } + } +} + +// TODO(houssainy) take the input stats from the select list or +// drop down menu in html. +function analyzeReports_(reports, filesNames) { + filesNames.unshift(""); // ned + + // Rtt + analyzeRttData(reports, filesNames, "bot1"); + analyzeRttData(reports, filesNames, "bot2"); + + // Send Packets Lost + analyzePacketsLostData(reports, filesNames, "bot1"); + analyzePacketsLostData(reports, filesNames, "bot2"); + + // Send bandwidth + analyzeData(reports, filesNames, "Available Send Bandwidth-bot1", "bot1", + "bweforvideo", "googAvailableSendBandwidth"); + analyzeData(reports, filesNames, "Available Send Bandwidth-bot2", "bot2", + "bweforvideo", "googAvailableSendBandwidth"); + + // Receive bandwidth + analyzeData(reports, filesNames, "Available Receive Bandwidth-bot1", "bot1", + "bweforvideo", "googAvailableReceiveBandwidth"); + analyzeData(reports, filesNames, "Available Receive Bandwidth-bot2", "bot2", + "bweforvideo", "googAvailableReceiveBandwidth"); + + drawSeparatorLine(); +} + +function analyzeRttData(reports, filesNames, botName) { + var outPut = []; + outPut.push(filesNames); + + var avergaData = ['Average Rtt x10']; + var maxData = ['Max Rtt']; + + var average; + var max; + for(var index in reports) { + average = getStateAverage(reports[index], botName, "Conn-audio-1-0", + "googRtt"); + avergaData.push(average*10); + + max = getStateMax(reports[index], botName, "Conn-audio-1-0", + "googRtt"); + maxData.push(max); + } + outPut.push(avergaData); + outPut.push(maxData); + + drawChart("Rtt-" + botName, outPut); +} + +function analyzePacketsLostData(reports, filesNames, botName) { + var outPut = []; + outPut.push(filesNames); + + var maxData = ['Max Send PacketsLost']; + var max; + for(var index in reports) { + max = getStateMax(reports[index], botName, "ssrc_[0-9]+_send", + "packetsLost"); + maxData.push(max); + } + outPut.push(maxData); + + drawChart("Send PacketsLost-" + botName, outPut); +} + +function analyzeData(reports, filesNames, chartName, botName, reportId, + statName) { + var outPut = []; + outPut.push(filesNames); + + var avergaData = ['Average ' + statName]; + var maxData = ['Max ' + statName]; + + var average; + var max; + for(var index in reports) { + average = getStateAverage(reports[index], botName, reportId, statName); + avergaData.push(average); + + max = getStateMax(reports[index], botName, reportId, statName); + maxData.push(max); + } + outPut.push(avergaData); + outPut.push(maxData); + + drawChart(chartName, outPut); +} + +function getStateAverage(reports, botName, reportId, statName) { + var sum = 0; + var count = 0; + + for (var index in reports) { + var data = reports[index].data; + if(index == 0 || !data.hasOwnProperty(botName)) + continue; + + var stats = data[botName]; + for (var key in stats) { + if(key.search(reportId) != -1) { + var value = parseInt(stats[key][statName]); + sum += value; + count++; + } + } + } + return Math.round(sum/count); +} + +function getStateMax(reports, botName, reportId, statName) { + var max = -1; + + for (var index in reports) { + var data = reports[index].data; + if(index == 0 || !data.hasOwnProperty(botName)) + continue; + + var stats = data[botName]; + for (var key in stats) { + if(key.search(reportId) != -1) { + var value = parseInt(stats[key][statName]); + max = Math.max(value, max); + } + } + } + return max; +} + +function drawChart(title, data) { + var dataTable = google.visualization.arrayToDataTable(data); + + var options = { + title: title, + }; + + var div = document.createElement('div'); + document.body.appendChild(div); + + var chart = new google.visualization.ColumnChart(div); + chart.draw(dataTable, options); +} + +function drawSeparatorLine() { + var hr = document.createElement('hr'); + document.body.appendChild(hr); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/oneWayVideoStreamingWithDownloadingFile.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/oneWayVideoStreamingWithDownloadingFile.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/oneWayVideoStreamingWithDownloadingFile.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/oneWayVideoStreamingWithDownloadingFile.js 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,122 @@ +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// +// A unidirectional video and audio flowing test from bot 1 to bot 2, +// and download a file from a server after 2 seconds of establishing +// the call. +// +// The test succeeds after collecting stats for 10 seconds from both bots +// and then write these stats to a file. +// +// Note: the source of the video and audio stream is getUserMedia(). +// +function testOneWayVideoWithDownloading(test, bot1, bot2) { + var report = test.createStatisticsReport("testOneWayVideoWithDownloading"); + + test.wait([ + createPeerConnection.bind(bot1), + createPeerConnection.bind(bot2) ], + onPeerConnectionCreated); + + function createPeerConnection(done) { + test.createTurnConfig(onTurnConfig.bind(this), test.fail); + + function onTurnConfig(config) { + this.createPeerConnection(config, done, test.fail); + }; + } + + function onPeerConnectionCreated(pc1, pc2) { + test.log("RTC Peers created."); + pc1.addEventListener('addstream', test.fail); + pc2.addEventListener('addstream', onAddStream); + pc1.addEventListener('icecandidate', onIceCandidate.bind(pc2)); + pc2.addEventListener('icecandidate', onIceCandidate.bind(pc1)); + + bot1.getUserMedia({video:true, audio:true}, onUserMediaSuccess, test.fail); + + function onUserMediaSuccess(stream) { + test.log("User has granted access to local media."); + pc1.addStream(stream); + bot1.showStream(stream.id, true, true); + + createOfferAndAnswer(pc1, pc2); + } + } + + function onAddStream(event) { + test.log("On Add stream."); + bot2.showStream(event.stream.id, true, false); + } + + function onIceCandidate(event) { + if(event.candidate) { + test.log(event.candidate.candidate); + this.addIceCandidate(event.candidate, + onAddIceCandidateSuccess, test.fail); + } + + function onAddIceCandidateSuccess() { + test.log("Candidate added successfully"); + } + } + + function createOfferAndAnswer(pc1, pc2) { + test.log("Creating offer."); + pc1.createOffer(gotOffer, test.fail); + + function gotOffer(offer) { + test.log("Got offer"); + pc1.setLocalDescription(offer, onSetSessionDescriptionSuccess, test.fail); + pc2.setRemoteDescription(offer, onSetSessionDescriptionSuccess, + test.fail); + test.log("Creating answer"); + pc2.createAnswer(gotAnswer, test.fail); + } + + function gotAnswer(answer) { + test.log("Got answer"); + pc2.setLocalDescription(answer, onSetSessionDescriptionSuccess, + test.fail); + pc1.setRemoteDescription(answer, onSetSessionDescriptionSuccess, + test.fail); + collectStats(); + + setTimeout(function() { + downloadFile(bot1, "bot1"); + downloadFile(bot2, "bot2"); + }, 2000); + } + + function onSetSessionDescriptionSuccess() { + test.log("Set session description success."); + } + + function collectStats() { + report.collectStatsFromPeerConnection("bot1", pc1); + report.collectStatsFromPeerConnection("bot2", pc2); + + setTimeout(function() { + report.finish(test.done); + }, 10000); + } + + function downloadFile(bot, name) { + bot.downloadFile("https://test.webrtc.org/test-download-file/9000KB.data", + onDownloadSuccess.bind(null, name), test.fail); + + function onDownloadSuccess(name, data) { + test.log( name + " downloaded " + + Math.round(data.length/(1024*1024)) + "MB."); + } + } + } +} + +registerBotTest('testOneWayVideoWithDownloading/chrome-chrome', + testOneWayVideoWithDownloading, ['chrome', 'chrome']); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/ping_pong.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/ping_pong.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/ping_pong.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/ping_pong.js 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,20 @@ +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// +function testPingPong(test, bot) { + test.assert(typeof bot.ping === 'function', 'Bot does not exposes ping.'); + + bot.ping(gotAnswer); + + function gotAnswer(answer) { + test.log('bot > ' + answer); + test.done(); + } +} + +registerBotTest('testPingPong/chrome', testPingPong, ['chrome']); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/simple_offer_answer.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/simple_offer_answer.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/simple_offer_answer.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/simple_offer_answer.js 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,48 @@ +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// +// Test that offer/answer between 2 peers completes successfully. +// +// Note: This test does not performs ice candidate exchange and +// does not verifies that media can flow between the peers. +function testOfferAnswer(test, bot1, bot2) { + test.wait( [ bot1.createPeerConnection.bind(bot1, null), + bot2.createPeerConnection.bind(bot2, null) ], + run); + + function run(pc1, pc2) { + test.log("Establishing call."); + pc1.createOffer(gotOffer); + + function gotOffer(offer) { + test.log("Got offer"); + expectedCall(); + pc1.setLocalDescription(offer, expectedCall, test.fail); + pc2.setRemoteDescription(offer, expectedCall, test.fail); + pc2.createAnswer(gotAnswer, test.fail); + } + + function gotAnswer(answer) { + test.log("Got answer"); + expectedCall(); + pc2.setLocalDescription(answer, expectedCall, test.fail); + pc1.setRemoteDescription(answer, expectedCall, test.fail); + } + + // TODO(andresp): Implement utilities in test to write expectations + // that certain methods must be called. + var expectedCalls = 0; + function expectedCall() { + if (++expectedCalls == 6) + test.done(); + } + } +} + +registerBotTest('testOfferAnswer/chrome-chrome', + testOfferAnswer, ['chrome', 'chrome']); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/three_bots_video_conference.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/three_bots_video_conference.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/three_bots_video_conference.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/three_bots_video_conference.js 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,135 @@ +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// +// A video conference between 3 bots streaming video and audio between +// each other. +// The test succeeds after establishing the call between the three +// devices. +// +// Note: the source of the video and audio stream is getUserMedia(). +function testTwoWayVideoStreaming(test, bot1, bot2, bot3) { + var answersCount = 0; + var statsCollector; + + test.wait([ + createBotPeerConnectionsWithLocalStream.bind(bot1), + createBotPeerConnectionsWithLocalStream.bind(bot2), + createBotPeerConnectionsWithLocalStream.bind(bot3)], + onPeerConnectionCreated); + + // done() callback is called with list of peers as argument. + function createBotPeerConnectionsWithLocalStream(done) { + var peerConnections = []; + + this.getUserMedia({video:true, audio:true}, + onUserMediaSuccess.bind(this), test.fail); + + function onUserMediaSuccess(stream) { + test.log("User has granted access to local media."); + this.showStream(stream.id, true, true); + + test.createTurnConfig(onTurnConfig.bind(this), test.fail); + + function onTurnConfig(config) { + this.createPeerConnection(config, addStream.bind(this), + test.fail); + this.createPeerConnection(config, addStream.bind(this), + test.fail); + } + + function addStream(pc) { + pc.addStream(stream); + pc.addEventListener('addstream', onAddStream.bind(this)); + + peerConnections.push(pc); + if(peerConnections.length == 2) + done(peerConnections); + } + } + } + + function onPeerConnectionCreated(peerConnections1, + peerConnections2, peerConnections3) { + test.log("RTC Peers created."); + + // Bot1 and Bot2 + establichCall(peerConnections1[0], peerConnections2[1]); + // Bot2 and Bot3 + establichCall(peerConnections2[0], peerConnections3[1]); + // Bot3 and Bot1 + establichCall(peerConnections3[0], peerConnections1[1]); + } + + function establichCall(pc1, pc2) { + pc1.addEventListener('icecandidate', onIceCandidate.bind(pc2)); + pc2.addEventListener('icecandidate', onIceCandidate.bind(pc1)); + + createOfferAndAnswer(pc1, pc2); + } + + function onAddStream(event) { + test.log("On Add stream."); + this.showStream(event.stream.id, true, false); + } + + function onIceCandidate(event) { + if(event.candidate) { + this.addIceCandidate(event.candidate, + onAddIceCandidateSuccess, test.fail); + }; + + function onAddIceCandidateSuccess() { + test.log("Candidate added successfully"); + }; + } + + function createOfferAndAnswer(pc1, pc2) { + test.log("Creating offer."); + pc1.createOffer(gotOffer, test.fail); + + function gotOffer(offer) { + test.log("Got offer"); + pc1.setLocalDescription(offer, onSetSessionDescriptionSuccess, test.fail); + pc2.setRemoteDescription(offer, onSetSessionDescriptionSuccess, + test.fail); + test.log("Creating answer"); + pc2.createAnswer(gotAnswer, test.fail); + } + + function gotAnswer(answer) { + test.log("Got answer"); + pc2.setLocalDescription(answer, onSetSessionDescriptionSuccess, + test.fail); + pc1.setRemoteDescription(answer, onSetSessionDescriptionSuccess, + test.fail); + + answersCount++; + if(answersCount == 3) { + // SetTimeout used because creating the three answers will very fast + // and test will success and the vm will be closed before establishing + // the calls. + setTimeout(function() { + test.done(); + }, 5000); + } + } + + function onSetSessionDescriptionSuccess() { + test.log("Set session description success."); + } + } +} + +registerBotTest('threeBotsVideoConference/android+android+chrome', + testTwoWayVideoStreaming, ['android-chrome', 'android-chrome', + 'chrome']); +registerBotTest('threeBotsVideoConference/chrome-chrome-chrome', + testTwoWayVideoStreaming, ['chrome', 'chrome', 'chrome']); +registerBotTest('threeBotsVideoConference/android-android-android', + testTwoWayVideoStreaming, ['android-chrome', 'android-chrome', + 'android-chrome']); \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/two_way_video_streaming.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/two_way_video_streaming.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/two_way_video_streaming.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/two_way_video_streaming.js 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,112 @@ +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// +// A two way video and audio flowing test between bot 1 and bot 2. +// The test succeeds after collecting stats for 10 seconds from both bots +// and then write these stats to a file. +// +// Note: the source of the video and audio stream is getUserMedia(). +function testTwoWayVideoStreaming(test, bot1, bot2) { + var report = test.createStatisticsReport("two_way_video_streaming"); + var statsCollector; + + test.wait([ + createPeerConnectionWithLocalStream.bind(bot1), + createPeerConnectionWithLocalStream.bind(bot2)], + onPeerConnectionCreated); + + function createPeerConnectionWithLocalStream(done) { + this.getUserMedia({video:true, audio:true}, + onUserMediaSuccess.bind(this), test.fail); + + function onUserMediaSuccess(stream) { + test.log("User has granted access to local media."); + test.createTurnConfig(onTurnConfig.bind(this), test.fail); + + function onTurnConfig(config) { + this.createPeerConnection(config, addAndShowStream.bind(this), + test.fail); + }; + + function addAndShowStream(pc) { + pc.addStream(stream); + this.showStream(stream.id, true, true); + + done(pc); + } + } + } + + function onPeerConnectionCreated(pc1, pc2) { + test.log("RTC Peers created."); + pc1.addEventListener('addstream', onAddStream.bind(bot1)); + pc2.addEventListener('addstream', onAddStream.bind(bot2)); + pc1.addEventListener('icecandidate', onIceCandidate.bind(pc2)); + pc2.addEventListener('icecandidate', onIceCandidate.bind(pc1)); + + createOfferAndAnswer(pc1, pc2); + } + + function onAddStream(event) { + test.log("On Add stream."); + this.showStream(event.stream.id, true, false); + } + + function onIceCandidate(event) { + if(event.candidate) { + test.log(event.candidate.candidate); + this.addIceCandidate(event.candidate, + onAddIceCandidateSuccess, test.fail); + }; + + function onAddIceCandidateSuccess() { + test.log("Candidate added successfully"); + }; + } + + function createOfferAndAnswer(pc1, pc2) { + test.log("Creating offer."); + pc1.createOffer(gotOffer, test.fail); + + function gotOffer(offer) { + test.log("Got offer"); + pc1.setLocalDescription(offer, onSetSessionDescriptionSuccess, test.fail); + pc2.setRemoteDescription(offer, onSetSessionDescriptionSuccess, + test.fail); + test.log("Creating answer"); + pc2.createAnswer(gotAnswer, test.fail); + } + + function gotAnswer(answer) { + test.log("Got answer"); + pc2.setLocalDescription(answer, onSetSessionDescriptionSuccess, + test.fail); + pc1.setRemoteDescription(answer, onSetSessionDescriptionSuccess, + test.fail); + collectStats(); + } + + function onSetSessionDescriptionSuccess() { + test.log("Set session description success."); + } + + function collectStats() { + report.collectStatsFromPeerConnection("bot1", pc1); + report.collectStatsFromPeerConnection("bot2", pc2); + + setTimeout(function() { + report.finish(test.done); + }, 10000); + } + } +} + +registerBotTest('testTwoWayVideo/android-android', + testTwoWayVideoStreaming, ['android-chrome', 'android-chrome']); +registerBotTest('testTwoWayVideo/chrome-chrome', + testTwoWayVideoStreaming, ['chrome', 'chrome']); \ No newline at end of file diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/webrtc_video_streaming.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/webrtc_video_streaming.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/webrtc_video_streaming.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test/webrtc_video_streaming.js 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,103 @@ +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// +// A unidirectional video and audio flowing test from bot 1 to bot 2. +// The test succeeds after collecting stats for 10 seconds from both bots +// and then write these stats to a file. +// +// Note: the source of the video and audio stream is getUserMedia(). +function testOneWayVideo(test, bot1, bot2) { + var report = test.createStatisticsReport("webrtc_video_streaming"); + + test.wait([ + createPeerConnection.bind(bot1), + createPeerConnection.bind(bot2) ], + onPeerConnectionCreated); + + function createPeerConnection(done) { + test.createTurnConfig(onTurnConfig.bind(this), test.fail); + + function onTurnConfig(config) { + this.createPeerConnection(config, done, test.fail); + }; + } + + function onPeerConnectionCreated(pc1, pc2) { + test.log("RTC Peers created."); + pc1.addEventListener('addstream', test.fail); + pc2.addEventListener('addstream', onAddStream); + pc1.addEventListener('icecandidate', onIceCandidate.bind(pc2)); + pc2.addEventListener('icecandidate', onIceCandidate.bind(pc1)); + + bot1.getUserMedia({video:true, audio:true}, onUserMediaSuccess, test.fail); + + function onUserMediaSuccess(stream) { + test.log("User has granted access to local media."); + pc1.addStream(stream); + bot1.showStream(stream.id, true, true); + + createOfferAndAnswer(pc1, pc2); + } + } + + function onAddStream(event) { + test.log("On Add stream."); + bot2.showStream(event.stream.id, true, false); + } + + function onIceCandidate(event) { + if(event.candidate) { + test.log(event.candidate.candidate); + this.addIceCandidate(event.candidate, + onAddIceCandidateSuccess, test.fail); + } + + function onAddIceCandidateSuccess() { + test.log("Candidate added successfully"); + } + } + + function createOfferAndAnswer(pc1, pc2) { + test.log("Creating offer."); + pc1.createOffer(gotOffer, test.fail); + + function gotOffer(offer) { + test.log("Got offer"); + pc1.setLocalDescription(offer, onSetSessionDescriptionSuccess, test.fail); + pc2.setRemoteDescription(offer, onSetSessionDescriptionSuccess, + test.fail); + test.log("Creating answer"); + pc2.createAnswer(gotAnswer, test.fail); + } + + function gotAnswer(answer) { + test.log("Got answer"); + pc2.setLocalDescription(answer, onSetSessionDescriptionSuccess, + test.fail); + pc1.setRemoteDescription(answer, onSetSessionDescriptionSuccess, + test.fail); + collectStats(); + } + + function onSetSessionDescriptionSuccess() { + test.log("Set session description success."); + } + + function collectStats() { + report.collectStatsFromPeerConnection("bot1", pc1); + report.collectStatsFromPeerConnection("bot2", pc2); + + setTimeout(function() { + report.finish(test.done); + }, 10000); + } + } +} + +registerBotTest('testOneWayVideo/chrome-chrome', + testOneWayVideo, ['chrome', 'chrome']); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/rtcbot/test.js 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,155 @@ +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// +// Provides a Test class that exposes api to the tests. +// Read test.prototype to see what methods are exposed. +var fs = require('fs'); +var request = require('request'); +var BotManager = require('./botmanager.js'); + +function Test() { + this.timeout_ = setTimeout( + this.fail.bind(this, "Test timeout!"), + 100000); +} + +Test.prototype = { + log: function () { + console.log.apply(console.log, arguments); + }, + + abort: function (error) { + var error = new Error(error || "Test aborted"); + console.log(error.stack); + process.exit(1); + }, + + assert: function (value, message) { + if (value !== true) { + this.abort(message || "Assert failed."); + } + }, + + fail: function () { + this.assert(false, "Test failed."); + }, + + done: function () { + clearTimeout(this.timeout_); + console.log("Test succeeded"); + process.exit(0); + }, + + // Utility method to wait for multiple callbacks to be executed. + // functions - array of functions to call with a callback. + // doneCallback - called when all callbacks on the array have completed. + wait: function (functions, doneCallback) { + var result = new Array(functions.length); + var missingResult = functions.length; + for (var i = 0; i != functions.length; ++i) + functions[i](complete.bind(this, i)); + + function complete(index, value) { + missingResult--; + result[index] = value; + if (missingResult == 0) + doneCallback.apply(null, result); + } + }, + + spawnBot: function (name, botType, doneCallback) { + // Lazy initialization of botmanager. + if (!this.botManager_) + this.botManager_ = new BotManager(); + this.botManager_.spawnNewBot(name, botType, doneCallback); + }, + + createStatisticsReport: function (outputFileName) { + return new StatisticsReport(outputFileName); + }, + + // Ask computeengineondemand to give us TURN server credentials and URIs. + createTurnConfig: function (onSuccess, onError) { + request('https://computeengineondemand.appspot.com/turn?username=1234&key=5678', + function (error, response, body) { + if (error || response.statusCode != 200) { + onError('TURN request failed'); + return; + } + + var response = JSON.parse(body); + var iceServer = { + 'username': response.username, + 'credential': response.password, + 'urls': response.uris + }; + onSuccess({ 'iceServers': [ iceServer ] }); + } + ); + }, +} + +StatisticsReport = function (outputFileName) { + this.output_ = []; + this.output_.push("Version: 1"); + this.outputFileName_ = outputFileName; +} + +StatisticsReport.prototype = { + collectStatsFromPeerConnection: function (prefix, pc) { + setInterval(this.addPeerConnectionStats.bind(this, prefix, pc), 100); + }, + + addPeerConnectionStats: function (prefix, pc) { + pc.getStats(onStatsReady.bind(this)); + + function onStatsReady(reports) { + for (index in reports) { + var stats = {}; + stats[reports[index].id] = collectStats(reports[index].stats); + + var data = {}; + data[prefix] = stats; + + this.output_.push({ + type: "UpdateCounters", + startTime: (new Date()).getTime(), + data: data, + }); + } + }; + + function collectStats(stats) { + var outputStats = {}; + for (index in stats) { + var statValue = parseFloat(stats[index].stat); + outputStats[stats[index].name] = isNaN(statValue)? + stats[index].stat : statValue; + } + return outputStats; + }; + }, + + finish: function (doneCallback) { + fs.exists("test/reports/", function (exists) { + if(exists) { + writeFile.bind(this)(); + } else { + fs.mkdir("test/reports/", 0777, writeFile.bind(this)); + } + }.bind(this)); + + function writeFile () { + fs.writeFile("test/reports/" + this.outputFileName_ + "_" + + (new Date()).getTime() +".json", JSON.stringify(this.output_), + doneCallback); + } + }, +}; + +module.exports = Test; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/simple_command_line_parser.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/simple_command_line_parser.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/simple_command_line_parser.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/simple_command_line_parser.h 2015-02-03 14:33:38.000000000 +0000 @@ -15,7 +15,7 @@ #include #include -#include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/base/constructormagic.h" #include "webrtc/test/testsupport/gtest_prod_util.h" // This is a very basic command line parsing class. We pass the command line diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/tools.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/tools.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/tools.gyp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/tools.gyp 2015-02-03 14:33:38.000000000 +0000 @@ -91,6 +91,7 @@ 'type': 'executable', 'dependencies': [ '<(webrtc_root)/voice_engine/voice_engine.gyp:voice_engine', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers_default', ], 'sources': [ 'force_mic_volume_max/force_mic_volume_max.cc', @@ -106,6 +107,7 @@ 'dependencies': [ '<(webrtc_root)/test/test.gyp:channel_transport', '<(webrtc_root)/voice_engine/voice_engine.gyp:voice_engine', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers_default', '<(DEPTH)/testing/gtest.gyp:gtest', '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', ], @@ -133,9 +135,7 @@ 4267, # size_t to int truncation. ], 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are - # using Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['OS=="android"', { 'dependencies': [ '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code', ], @@ -143,10 +143,8 @@ ], }, # tools_unittests ], # targets - # TODO(henrike): remove build_with_chromium==1 when the bots are using - # Chromium's buildbots. 'conditions': [ - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['OS=="android"', { 'targets': [ { 'target_name': 'tools_unittests_apk_target', @@ -167,7 +165,6 @@ ], 'includes': [ '../build/isolate.gypi', - 'tools_unittests.isolate', ], 'sources': [ 'tools_unittests.isolate', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/tools_unittests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/tools_unittests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/tools/tools_unittests.isolate 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/tools/tools_unittests.isolate 2015-02-03 14:33:38.000000000 +0000 @@ -8,30 +8,25 @@ { 'conditions': [ ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. 'variables': { - 'isolate_dependency_untracked': [ - '../../../data/', - '../../../resources/', + 'files': [ + '<(DEPTH)/data/', + '<(DEPTH)/resources/', ], }, }], ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../../testing/test_env.py', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/tools_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_tracked': [ - '../../DEPS', - '../../resources/foreman_cif.yuv', - '../../testing/test_env.py', + 'files': [ + '<(DEPTH)/DEPS', + '<(DEPTH)/resources/foreman_cif.yuv', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/tools_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '../../tools/swarming_client/', - ], }, }], ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/typedefs.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/typedefs.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/typedefs.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/typedefs.h 2015-02-03 14:33:38.000000000 +0000 @@ -14,13 +14,6 @@ #ifndef WEBRTC_TYPEDEFS_H_ #define WEBRTC_TYPEDEFS_H_ -// For access to standard POSIXish features, use WEBRTC_POSIX instead of a -// more specific macro. -#if defined(WEBRTC_MAC) || defined(WEBRTC_LINUX) || \ - defined(WEBRTC_ANDROID) || defined(WEBRTC_BSD) -#define WEBRTC_POSIX -#endif - // Processor architecture detection. For more info on what's defined, see: // http://msdn.microsoft.com/en-us/library/b0084kay.aspx // http://www.agner.org/optimize/calling_conventions.pdf @@ -30,6 +23,9 @@ #define WEBRTC_ARCH_X86_64 #define WEBRTC_ARCH_64_BITS #define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(__aarch64__) +#define WEBRTC_ARCH_64_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN #elif defined(_M_IX86) || defined(__i386__) #define WEBRTC_ARCH_X86_FAMILY #define WEBRTC_ARCH_X86 @@ -129,6 +125,9 @@ #define WEBRTC_ARCH_32_BITS 1 #define WEBRTC_ARCH_BIG_ENDIAN #define WEBRTC_BIG_ENDIAN +#elif defined(__pnacl__) +#define WEBRTC_ARCH_32_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN #else #error Please add support for your architecture in typedefs.h #endif @@ -137,14 +136,15 @@ #error Define either WEBRTC_ARCH_LITTLE_ENDIAN or WEBRTC_ARCH_BIG_ENDIAN #endif -#if defined(__SSE2__) || defined(_MSC_VER) -#define WEBRTC_USE_SSE2 +#if (defined(WEBRTC_ARCH_X86_FAMILY) && !defined(__SSE2__)) || \ + (defined(WEBRTC_ARCH_ARM_V7) && !defined(WEBRTC_ARCH_ARM_NEON)) +#define WEBRTC_CPU_DETECTION #endif #if !defined(_MSC_VER) #include #else -// Define C99 equivalent types, since MSVC doesn't provide stdint.h. +// Define C99 equivalent types, since pre-2010 MSVC doesn't provide stdint.h. typedef signed char int8_t; typedef signed short int16_t; typedef signed int int32_t; @@ -190,4 +190,24 @@ #endif #endif // WARN_UNUSED_RESULT +// Put after a variable that might not be used, to prevent compiler warnings: +// int result UNUSED = DoSomething(); +// assert(result == 17); +#ifndef UNUSED +#ifdef __GNUC__ +#define UNUSED __attribute__((unused)) +#else +#define UNUSED +#endif +#endif + +// Annotate a function that will not return control flow to the caller. +#if defined(_MSC_VER) +#define NO_RETURN __declspec(noreturn) +#elif defined(__GNUC__) +#define NO_RETURN __attribute__((noreturn)) +#else +#define NO_RETURN +#endif + #endif // WEBRTC_TYPEDEFS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/bitrate_estimator_tests.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/bitrate_estimator_tests.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/bitrate_estimator_tests.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/bitrate_estimator_tests.cc 2015-02-03 14:33:38.000000000 +0000 @@ -13,26 +13,110 @@ #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/thread_annotations.h" #include "webrtc/call.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/event_wrapper.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/test/call_test.h" #include "webrtc/test/direct_transport.h" +#include "webrtc/test/encoder_settings.h" #include "webrtc/test/fake_decoder.h" #include "webrtc/test/fake_encoder.h" #include "webrtc/test/frame_generator_capturer.h" namespace webrtc { +namespace { +// Note: consider to write tests that don't depend on the trace system instead +// of re-using this class. +class TraceObserver { + public: + TraceObserver() { + Trace::set_level_filter(kTraceTerseInfo); + + Trace::CreateTrace(); + Trace::SetTraceCallback(&callback_); + + // Call webrtc trace to initialize the tracer that would otherwise trigger a + // data-race if left to be initialized by multiple threads (i.e. threads + // spawned by test::DirectTransport members in BitrateEstimatorTest). + WEBRTC_TRACE(kTraceStateInfo, + kTraceUtility, + -1, + "Instantiate without data races."); + } + + ~TraceObserver() { + Trace::SetTraceCallback(NULL); + Trace::ReturnTrace(); + } + + void PushExpectedLogLine(const std::string& expected_log_line) { + callback_.PushExpectedLogLine(expected_log_line); + } + + EventTypeWrapper Wait() { + return callback_.Wait(); + } + + private: + class Callback : public TraceCallback { + public: + Callback() + : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + done_(EventWrapper::Create()) {} + + virtual void Print(TraceLevel level, + const char* message, + int length) OVERRIDE { + CriticalSectionScoped lock(crit_sect_.get()); + std::string msg(message); + if (msg.find("BitrateEstimator") != std::string::npos) { + received_log_lines_.push_back(msg); + } + int num_popped = 0; + while (!received_log_lines_.empty() && !expected_log_lines_.empty()) { + std::string a = received_log_lines_.front(); + std::string b = expected_log_lines_.front(); + received_log_lines_.pop_front(); + expected_log_lines_.pop_front(); + num_popped++; + EXPECT_TRUE(a.find(b) != std::string::npos); + } + if (expected_log_lines_.size() <= 0) { + if (num_popped > 0) { + done_->Set(); + } + return; + } + } + + EventTypeWrapper Wait() { + return done_->Wait(test::CallTest::kDefaultTimeoutMs); + } + + void PushExpectedLogLine(const std::string& expected_log_line) { + CriticalSectionScoped lock(crit_sect_.get()); + expected_log_lines_.push_back(expected_log_line); + } + + private: + typedef std::list Strings; + const scoped_ptr crit_sect_; + Strings received_log_lines_ GUARDED_BY(crit_sect_); + Strings expected_log_lines_ GUARDED_BY(crit_sect_); + scoped_ptr done_; + }; + + Callback callback_; +}; +} // namespace static const int kTOFExtensionId = 4; static const int kASTExtensionId = 5; -static unsigned int kDefaultTimeoutMs = 30 * 1000; -static const uint32_t kSendSsrc = 0x654321; -static const uint32_t kReceiverLocalSsrc = 0x123456; -static const uint8_t kSendPayloadType = 125; - -class BitrateEstimatorTest : public ::testing::Test { +class BitrateEstimatorTest : public test::CallTest { public: BitrateEstimatorTest() : receiver_trace_(), @@ -40,7 +124,6 @@ receive_transport_(), sender_call_(), receiver_call_(), - send_config_(), receive_config_(), streams_() { } @@ -50,10 +133,7 @@ } virtual void SetUp() { - // Create receiver call first so that we are guaranteed to have a trace - // callback when sender call is created. Call::Config receiver_call_config(&receive_transport_); - receiver_call_config.trace_callback = &receiver_trace_; receiver_call_.reset(Call::Create(receiver_call_config)); Call::Config sender_call_config(&send_transport_); @@ -62,17 +142,16 @@ send_transport_.SetReceiver(receiver_call_->Receiver()); receive_transport_.SetReceiver(sender_call_->Receiver()); - send_config_ = sender_call_->GetDefaultSendConfig(); - send_config_.rtp.ssrcs.push_back(kSendSsrc); - // send_config_.encoder will be set by every stream separately. - send_config_.internal_source = false; - test::FakeEncoder::SetCodecSettings(&send_config_.codec, 1); - send_config_.codec.plType = kSendPayloadType; - - receive_config_ = receiver_call_->GetDefaultReceiveConfig(); - receive_config_.codecs.clear(); - receive_config_.codecs.push_back(send_config_.codec); - // receive_config_.external_decoders will be set by every stream separately. + send_config_ = VideoSendStream::Config(); + send_config_.rtp.ssrcs.push_back(kSendSsrcs[0]); + // Encoders will be set separately per stream. + send_config_.encoder_settings.encoder = NULL; + send_config_.encoder_settings.payload_name = "FAKE"; + send_config_.encoder_settings.payload_type = kFakeSendPayloadType; + encoder_config_.streams = test::CreateVideoStreams(1); + + receive_config_ = VideoReceiveStream::Config(); + // receive_config_.decoders will be set by every stream separately. receive_config_.rtp.remote_ssrc = send_config_.rtp.ssrcs[0]; receive_config_.rtp.local_ssrc = kReceiverLocalSsrc; receive_config_.rtp.extensions.push_back( @@ -93,65 +172,12 @@ streams_.pop_back(); } - // The TraceCallback instance MUST outlive Calls, destroy Calls explicitly. receiver_call_.reset(); } protected: friend class Stream; - class TraceObserver : public TraceCallback { - public: - TraceObserver() - : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), - received_log_lines_(), - expected_log_lines_(), - done_(EventWrapper::Create()) { - } - - void PushExpectedLogLine(const std::string& expected_log_line) { - CriticalSectionScoped cs(crit_sect_.get()); - expected_log_lines_.push_back(expected_log_line); - } - - virtual void Print(TraceLevel level, - const char* message, - int length) OVERRIDE { - CriticalSectionScoped cs(crit_sect_.get()); - if (!(level & kTraceStateInfo)) { - return; - } - std::string msg(message); - if (msg.find("BitrateEstimator") != std::string::npos) { - received_log_lines_.push_back(msg); - } - int num_popped = 0; - while (!received_log_lines_.empty() && !expected_log_lines_.empty()) { - std::string a = received_log_lines_.front(); - std::string b = expected_log_lines_.front(); - received_log_lines_.pop_front(); - expected_log_lines_.pop_front(); - num_popped++; - EXPECT_TRUE(a.find(b) != std::string::npos); - } - if (expected_log_lines_.size() <= 0) { - if (num_popped > 0) { - done_->Set(); - } - return; - } - } - - EventTypeWrapper Wait() { return done_->Wait(kDefaultTimeoutMs); } - - private: - typedef std::list Strings; - scoped_ptr crit_sect_; - Strings received_log_lines_; - Strings expected_log_lines_; - scoped_ptr done_; - }; - class Stream { public: explicit Stream(BitrateEstimatorTest* test) @@ -163,27 +189,29 @@ fake_encoder_(Clock::GetRealTimeClock()), fake_decoder_() { test_->send_config_.rtp.ssrcs[0]++; - test_->send_config_.encoder = &fake_encoder_; - send_stream_ = - test_->sender_call_->CreateVideoSendStream(test_->send_config_); - frame_generator_capturer_.reset( - test::FrameGeneratorCapturer::Create(send_stream_->Input(), - test_->send_config_.codec.width, - test_->send_config_.codec.height, - 30, - Clock::GetRealTimeClock())); - send_stream_->StartSending(); + test_->send_config_.encoder_settings.encoder = &fake_encoder_; + send_stream_ = test_->sender_call_->CreateVideoSendStream( + test_->send_config_, test_->encoder_config_); + assert(test_->encoder_config_.streams.size() == 1); + frame_generator_capturer_.reset(test::FrameGeneratorCapturer::Create( + send_stream_->Input(), + test_->encoder_config_.streams[0].width, + test_->encoder_config_.streams[0].height, + 30, + Clock::GetRealTimeClock())); + send_stream_->Start(); frame_generator_capturer_->Start(); - ExternalVideoDecoder decoder; + VideoReceiveStream::Decoder decoder; decoder.decoder = &fake_decoder_; - decoder.payload_type = test_->send_config_.codec.plType; + decoder.payload_type = test_->send_config_.encoder_settings.payload_type; + decoder.payload_name = test_->send_config_.encoder_settings.payload_name; + test_->receive_config_.decoders.push_back(decoder); test_->receive_config_.rtp.remote_ssrc = test_->send_config_.rtp.ssrcs[0]; test_->receive_config_.rtp.local_ssrc++; - test_->receive_config_.external_decoders.push_back(decoder); receive_stream_ = test_->receiver_call_->CreateVideoReceiveStream( test_->receive_config_); - receive_stream_->StartReceiving(); + receive_stream_->Start(); is_sending_receiving_ = true; } @@ -199,8 +227,8 @@ void StopSending() { if (is_sending_receiving_) { frame_generator_capturer_->Stop(); - send_stream_->StopSending(); - receive_stream_->StopReceiving(); + send_stream_->Stop(); + receive_stream_->Stop(); is_sending_receiving_ = false; } } @@ -220,7 +248,6 @@ test::DirectTransport receive_transport_; scoped_ptr sender_call_; scoped_ptr receiver_call_; - VideoSendStream::Config send_config_; VideoReceiveStream::Config receive_config_; std::vector streams_; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/BUILD.gn 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,39 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../build/webrtc.gni") + +source_set("video") { + sources = [ + "call.cc", + "encoded_frame_callback_adapter.cc", + "encoded_frame_callback_adapter.h", + "receive_statistics_proxy.cc", + "receive_statistics_proxy.h", + "send_statistics_proxy.cc", + "send_statistics_proxy.h", + "transport_adapter.cc", + "transport_adapter.h", + "video_receive_stream.cc", + "video_receive_stream.h", + "video_send_stream.cc", + "video_send_stream.h", + ] + + configs += [ "..:common_config" ] + public_configs = [ "..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ "../video_engine:video_engine_core" ] +} + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/call.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/call.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/call.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/call.cc 2015-02-03 14:33:38.000000000 +0000 @@ -14,10 +14,13 @@ #include #include +#include "webrtc/base/thread_annotations.h" #include "webrtc/call.h" #include "webrtc/common.h" #include "webrtc/config.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" +#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h" +#include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" @@ -27,16 +30,48 @@ #include "webrtc/video_engine/include/vie_base.h" #include "webrtc/video_engine/include/vie_codec.h" #include "webrtc/video_engine/include/vie_rtp_rtcp.h" +#include "webrtc/video_engine/include/vie_network.h" +#include "webrtc/video_engine/include/vie_rtp_rtcp.h" namespace webrtc { const char* RtpExtension::kTOffset = "urn:ietf:params:rtp-hdrext:toffset"; const char* RtpExtension::kAbsSendTime = "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"; + +bool RtpExtension::IsSupported(const std::string& name) { + return name == webrtc::RtpExtension::kTOffset || + name == webrtc::RtpExtension::kAbsSendTime; +} + +VideoEncoder* VideoEncoder::Create(VideoEncoder::EncoderType codec_type) { + switch (codec_type) { + case kVp8: + return VP8Encoder::Create(); + case kVp9: + return VP9Encoder::Create(); + } + assert(false); + return NULL; +} + +VideoDecoder* VideoDecoder::Create(VideoDecoder::DecoderType codec_type) { + switch (codec_type) { + case kVp8: + return VP8Decoder::Create(); + case kVp9: + return VP9Decoder::Create(); + } + assert(false); + return NULL; +} + +const int Call::Config::kDefaultStartBitrateBps = 300000; + namespace internal { class CpuOveruseObserverProxy : public webrtc::CpuOveruseObserver { public: - CpuOveruseObserverProxy(OveruseCallback* overuse_callback) + explicit CpuOveruseObserverProxy(LoadObserver* overuse_callback) : crit_(CriticalSectionWrapper::CreateCriticalSection()), overuse_callback_(overuse_callback) { assert(overuse_callback != NULL); @@ -45,18 +80,18 @@ virtual ~CpuOveruseObserverProxy() {} virtual void OveruseDetected() OVERRIDE { - CriticalSectionScoped cs(crit_.get()); - overuse_callback_->OnOveruse(); + CriticalSectionScoped lock(crit_.get()); + overuse_callback_->OnLoadUpdate(LoadObserver::kOveruse); } virtual void NormalUsage() OVERRIDE { - CriticalSectionScoped cs(crit_.get()); - overuse_callback_->OnNormalUse(); + CriticalSectionScoped lock(crit_.get()); + overuse_callback_->OnLoadUpdate(LoadObserver::kUnderuse); } private: - scoped_ptr crit_; - OveruseCallback* overuse_callback_; + const scoped_ptr crit_; + LoadObserver* overuse_callback_ GUARDED_BY(crit_); }; class Call : public webrtc::Call, public PacketReceiver { @@ -65,47 +100,50 @@ virtual ~Call(); virtual PacketReceiver* Receiver() OVERRIDE; - virtual std::vector GetVideoCodecs() OVERRIDE; - - virtual VideoSendStream::Config GetDefaultSendConfig() OVERRIDE; virtual VideoSendStream* CreateVideoSendStream( - const VideoSendStream::Config& config) OVERRIDE; + const VideoSendStream::Config& config, + const VideoEncoderConfig& encoder_config) OVERRIDE; virtual void DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) OVERRIDE; - virtual VideoReceiveStream::Config GetDefaultReceiveConfig() OVERRIDE; - virtual VideoReceiveStream* CreateVideoReceiveStream( const VideoReceiveStream::Config& config) OVERRIDE; virtual void DestroyVideoReceiveStream( webrtc::VideoReceiveStream* receive_stream) OVERRIDE; - virtual uint32_t SendBitrateEstimate() OVERRIDE; - virtual uint32_t ReceiveBitrateEstimate() OVERRIDE; + virtual Stats GetStats() const OVERRIDE; + + virtual DeliveryStatus DeliverPacket(const uint8_t* packet, + size_t length) OVERRIDE; - virtual bool DeliverPacket(const uint8_t* packet, size_t length) OVERRIDE; + virtual void SignalNetworkState(NetworkState state) OVERRIDE; private: - bool DeliverRtcp(const uint8_t* packet, size_t length); - bool DeliverRtp(const RTPHeader& header, - const uint8_t* packet, - size_t length); + DeliveryStatus DeliverRtcp(const uint8_t* packet, size_t length); + DeliveryStatus DeliverRtp(const uint8_t* packet, size_t length); Call::Config config_; - std::map receive_ssrcs_; - scoped_ptr receive_lock_; + // Needs to be held while write-locking |receive_crit_| or |send_crit_|. This + // ensures that we have a consistent network state signalled to all senders + // and receivers. + scoped_ptr network_enabled_crit_; + bool network_enabled_ GUARDED_BY(network_enabled_crit_); + + scoped_ptr receive_crit_; + std::map receive_ssrcs_ + GUARDED_BY(receive_crit_); - std::map send_ssrcs_; - scoped_ptr send_lock_; - - scoped_ptr rtp_header_parser_; + scoped_ptr send_crit_; + std::map send_ssrcs_ GUARDED_BY(send_crit_); scoped_ptr overuse_observer_proxy_; + VideoSendStream::RtpStateMap suspended_send_ssrcs_; + VideoEngine* video_engine_; ViERTP_RTCP* rtp_rtcp_; ViECodec* codec_; @@ -116,83 +154,7 @@ }; } // namespace internal -class TraceDispatcher : public TraceCallback { - public: - TraceDispatcher() - : lock_(CriticalSectionWrapper::CreateCriticalSection()), - filter_(kTraceNone) { - Trace::CreateTrace(); - VideoEngine::SetTraceCallback(this); - VideoEngine::SetTraceFilter(kTraceNone); - } - - ~TraceDispatcher() { - Trace::ReturnTrace(); - VideoEngine::SetTraceCallback(NULL); - } - - virtual void Print(TraceLevel level, - const char* message, - int length) OVERRIDE { - CriticalSectionScoped crit(lock_.get()); - for (std::map::iterator it = callbacks_.begin(); - it != callbacks_.end(); - ++it) { - if ((level & it->second->trace_filter) != kTraceNone) - it->second->trace_callback->Print(level, message, length); - } - } - - void RegisterCallback(Call* call, Call::Config* config) { - if (config->trace_callback == NULL) - return; - - CriticalSectionScoped crit(lock_.get()); - callbacks_[call] = config; - - filter_ |= config->trace_filter; - VideoEngine::SetTraceFilter(filter_); - } - - void DeregisterCallback(Call* call) { - CriticalSectionScoped crit(lock_.get()); - callbacks_.erase(call); - - filter_ = kTraceNone; - for (std::map::iterator it = callbacks_.begin(); - it != callbacks_.end(); - ++it) { - filter_ |= it->second->trace_filter; - } - - VideoEngine::SetTraceFilter(filter_); - } - - private: - scoped_ptr lock_; - unsigned int filter_; - std::map callbacks_; -}; - -namespace internal { -TraceDispatcher* global_trace_dispatcher = NULL; -} // internal - -void CreateTraceDispatcher() { - if (internal::global_trace_dispatcher == NULL) { - TraceDispatcher* dispatcher = new TraceDispatcher(); - // TODO(pbos): Atomic compare and exchange. - if (internal::global_trace_dispatcher == NULL) { - internal::global_trace_dispatcher = dispatcher; - } else { - delete dispatcher; - } - } -} - Call* Call::Create(const Call::Config& config) { - CreateTraceDispatcher(); - VideoEngine* video_engine = config.webrtc_config != NULL ? VideoEngine::Create(*config.webrtc_config) : VideoEngine::Create(); @@ -205,9 +167,10 @@ Call::Call(webrtc::VideoEngine* video_engine, const Call::Config& config) : config_(config), - receive_lock_(RWLockWrapper::CreateRWLock()), - send_lock_(RWLockWrapper::CreateRWLock()), - rtp_header_parser_(RtpHeaderParser::Create()), + network_enabled_crit_(CriticalSectionWrapper::CreateCriticalSection()), + network_enabled_(true), + receive_crit_(RWLockWrapper::CreateRWLock()), + send_crit_(RWLockWrapper::CreateRWLock()), video_engine_(video_engine), base_channel_id_(-1) { assert(video_engine != NULL); @@ -218,8 +181,6 @@ new CpuOveruseObserverProxy(config.overuse_callback)); } - global_trace_dispatcher->RegisterCallback(this, &config_); - rtp_rtcp_ = ViERTP_RTCP::GetInterface(video_engine_); assert(rtp_rtcp_ != NULL); @@ -236,7 +197,6 @@ } Call::~Call() { - global_trace_dispatcher->DeregisterCallback(this); base_->DeleteChannel(base_channel_id_); base_->Release(); codec_->Release(); @@ -246,72 +206,67 @@ PacketReceiver* Call::Receiver() { return this; } -std::vector Call::GetVideoCodecs() { - std::vector codecs; - - VideoCodec codec; - for (size_t i = 0; i < static_cast(codec_->NumberOfCodecs()); ++i) { - if (codec_->GetCodec(static_cast(i), codec) == 0) { - codecs.push_back(codec); - } - } - return codecs; -} - -VideoSendStream::Config Call::GetDefaultSendConfig() { - VideoSendStream::Config config; - codec_->GetCodec(0, config.codec); - return config; -} - VideoSendStream* Call::CreateVideoSendStream( - const VideoSendStream::Config& config) { + const VideoSendStream::Config& config, + const VideoEncoderConfig& encoder_config) { assert(config.rtp.ssrcs.size() > 0); - assert(config.rtp.ssrcs.size() >= config.codec.numberOfSimulcastStreams); - - VideoSendStream* send_stream = new VideoSendStream( - config_.send_transport, - overuse_observer_proxy_.get(), - video_engine_, - config, - base_channel_id_); - WriteLockScoped write_lock(*send_lock_); + // TODO(mflodman): Base the start bitrate on a current bandwidth estimate, if + // the call has already started. + VideoSendStream* send_stream = + new VideoSendStream(config_.send_transport, + overuse_observer_proxy_.get(), + video_engine_, + config, + encoder_config, + suspended_send_ssrcs_, + base_channel_id_, + config_.stream_start_bitrate_bps); + + // This needs to be taken before send_crit_ as both locks need to be held + // while changing network state. + CriticalSectionScoped lock(network_enabled_crit_.get()); + WriteLockScoped write_lock(*send_crit_); for (size_t i = 0; i < config.rtp.ssrcs.size(); ++i) { assert(send_ssrcs_.find(config.rtp.ssrcs[i]) == send_ssrcs_.end()); send_ssrcs_[config.rtp.ssrcs[i]] = send_stream; } + if (!network_enabled_) + send_stream->SignalNetworkState(kNetworkDown); return send_stream; } void Call::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) { assert(send_stream != NULL); + send_stream->Stop(); + VideoSendStream* send_stream_impl = NULL; { - WriteLockScoped write_lock(*send_lock_); - for (std::map::iterator it = - send_ssrcs_.begin(); - it != send_ssrcs_.end(); - ++it) { + WriteLockScoped write_lock(*send_crit_); + std::map::iterator it = send_ssrcs_.begin(); + while (it != send_ssrcs_.end()) { if (it->second == static_cast(send_stream)) { send_stream_impl = it->second; - send_ssrcs_.erase(it); - break; + send_ssrcs_.erase(it++); + } else { + ++it; } } } + VideoSendStream::RtpStateMap rtp_state = send_stream_impl->GetRtpStates(); + + for (VideoSendStream::RtpStateMap::iterator it = rtp_state.begin(); + it != rtp_state.end(); + ++it) { + suspended_send_ssrcs_[it->first] = it->second; + } + assert(send_stream_impl != NULL); delete send_stream_impl; } -VideoReceiveStream::Config Call::GetDefaultReceiveConfig() { - VideoReceiveStream::Config config; - config.rtp.remb = true; - return config; -} - VideoReceiveStream* Call::CreateVideoReceiveStream( const VideoReceiveStream::Config& config) { VideoReceiveStream* receive_stream = @@ -321,7 +276,10 @@ config_.voice_engine, base_channel_id_); - WriteLockScoped write_lock(*receive_lock_); + // This needs to be taken before receive_crit_ as both locks need to be held + // while changing network state. + CriticalSectionScoped lock(network_enabled_crit_.get()); + WriteLockScoped write_lock(*receive_crit_); assert(receive_ssrcs_.find(config.rtp.remote_ssrc) == receive_ssrcs_.end()); receive_ssrcs_[config.rtp.remote_ssrc] = receive_stream; // TODO(pbos): Configure different RTX payloads per receive payload. @@ -330,6 +288,8 @@ if (it != config.rtp.rtx.end()) receive_ssrcs_[it->second.ssrc] = receive_stream; + if (!network_enabled_) + receive_stream->SignalNetworkState(kNetworkDown); return receive_stream; } @@ -339,7 +299,7 @@ VideoReceiveStream* receive_stream_impl = NULL; { - WriteLockScoped write_lock(*receive_lock_); + WriteLockScoped write_lock(*receive_crit_); // Remove all ssrcs pointing to a receive stream. As RTX retransmits on a // separate SSRC there can be either one or two. std::map::iterator it = @@ -360,22 +320,62 @@ delete receive_stream_impl; } -uint32_t Call::SendBitrateEstimate() { - // TODO(pbos): Return send-bitrate estimate - return 0; +Call::Stats Call::GetStats() const { + Stats stats; + // Ignoring return values. + uint32_t send_bandwidth = 0; + rtp_rtcp_->GetEstimatedSendBandwidth(base_channel_id_, &send_bandwidth); + stats.send_bandwidth_bps = send_bandwidth; + uint32_t recv_bandwidth = 0; + rtp_rtcp_->GetEstimatedReceiveBandwidth(base_channel_id_, &recv_bandwidth); + stats.recv_bandwidth_bps = recv_bandwidth; + { + ReadLockScoped read_lock(*send_crit_); + for (std::map::const_iterator it = + send_ssrcs_.begin(); + it != send_ssrcs_.end(); + ++it) { + stats.pacer_delay_ms = + std::max(it->second->GetPacerQueuingDelayMs(), stats.pacer_delay_ms); + } + } + return stats; } -uint32_t Call::ReceiveBitrateEstimate() { - // TODO(pbos): Return receive-bitrate estimate - return 0; +void Call::SignalNetworkState(NetworkState state) { + // Take crit for entire function, it needs to be held while updating streams + // to guarantee a consistent state across streams. + CriticalSectionScoped lock(network_enabled_crit_.get()); + network_enabled_ = state == kNetworkUp; + { + ReadLockScoped write_lock(*send_crit_); + for (std::map::iterator it = + send_ssrcs_.begin(); + it != send_ssrcs_.end(); + ++it) { + it->second->SignalNetworkState(state); + } + } + { + ReadLockScoped write_lock(*receive_crit_); + for (std::map::iterator it = + receive_ssrcs_.begin(); + it != receive_ssrcs_.end(); + ++it) { + it->second->SignalNetworkState(state); + } + } } -bool Call::DeliverRtcp(const uint8_t* packet, size_t length) { +PacketReceiver::DeliveryStatus Call::DeliverRtcp(const uint8_t* packet, + size_t length) { // TODO(pbos): Figure out what channel needs it actually. // Do NOT broadcast! Also make sure it's a valid packet. + // Return DELIVERY_UNKNOWN_SSRC if it can be determined that + // there's no receiver of the packet. bool rtcp_delivered = false; { - ReadLockScoped read_lock(*receive_lock_); + ReadLockScoped read_lock(*receive_crit_); for (std::map::iterator it = receive_ssrcs_.begin(); it != receive_ssrcs_.end(); @@ -386,7 +386,7 @@ } { - ReadLockScoped read_lock(*send_lock_); + ReadLockScoped read_lock(*send_crit_); for (std::map::iterator it = send_ssrcs_.begin(); it != send_ssrcs_.end(); @@ -395,32 +395,35 @@ rtcp_delivered = true; } } - return rtcp_delivered; + return rtcp_delivered ? DELIVERY_OK : DELIVERY_PACKET_ERROR; } -bool Call::DeliverRtp(const RTPHeader& header, - const uint8_t* packet, - size_t length) { - ReadLockScoped read_lock(*receive_lock_); +PacketReceiver::DeliveryStatus Call::DeliverRtp(const uint8_t* packet, + size_t length) { + // Minimum RTP header size. + if (length < 12) + return DELIVERY_PACKET_ERROR; + + const uint8_t* ptr = &packet[8]; + uint32_t ssrc = ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; + + ReadLockScoped read_lock(*receive_crit_); std::map::iterator it = - receive_ssrcs_.find(header.ssrc); - if (it == receive_ssrcs_.end()) { - // TODO(pbos): Log some warning, SSRC without receiver. - return false; - } - return it->second->DeliverRtp(static_cast(packet), length); + receive_ssrcs_.find(ssrc); + + if (it == receive_ssrcs_.end()) + return DELIVERY_UNKNOWN_SSRC; + + return it->second->DeliverRtp(packet, length) ? DELIVERY_OK + : DELIVERY_PACKET_ERROR; } -bool Call::DeliverPacket(const uint8_t* packet, size_t length) { - // TODO(pbos): ExtensionMap if there are extensions. - if (RtpHeaderParser::IsRtcp(packet, static_cast(length))) +PacketReceiver::DeliveryStatus Call::DeliverPacket(const uint8_t* packet, + size_t length) { + if (RtpHeaderParser::IsRtcp(packet, length)) return DeliverRtcp(packet, length); - RTPHeader rtp_header; - if (!rtp_header_parser_->Parse(packet, static_cast(length), &rtp_header)) - return false; - - return DeliverRtp(rtp_header, packet, length); + return DeliverRtp(packet, length); } } // namespace internal diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/call_perf_tests.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/call_perf_tests.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/call_perf_tests.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/call_perf_tests.cc 2015-02-03 14:33:38.000000000 +0000 @@ -15,13 +15,17 @@ #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/thread_annotations.h" #include "webrtc/call.h" -#include "webrtc/modules/remote_bitrate_estimator/include/rtp_to_ntp.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/rtp_to_ntp.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/test/call_test.h" #include "webrtc/test/direct_transport.h" +#include "webrtc/test/encoder_settings.h" #include "webrtc/test/fake_audio_device.h" #include "webrtc/test/fake_decoder.h" #include "webrtc/test/fake_encoder.h" @@ -39,52 +43,23 @@ namespace webrtc { -static unsigned int kLongTimeoutMs = 120 * 1000; -static const uint32_t kSendSsrc = 0x654321; -static const uint32_t kReceiverLocalSsrc = 0x123456; -static const uint8_t kSendPayloadType = 125; - -class CallPerfTest : public ::testing::Test { - public: - CallPerfTest() - : send_stream_(NULL), fake_encoder_(Clock::GetRealTimeClock()) {} +class CallPerfTest : public test::CallTest { protected: - VideoSendStream::Config GetSendTestConfig(Call* call) { - VideoSendStream::Config config = call->GetDefaultSendConfig(); - config.encoder = &fake_encoder_; - config.internal_source = false; - config.rtp.ssrcs.push_back(kSendSsrc); - test::FakeEncoder::SetCodecSettings(&config.codec, 1); - config.codec.plType = kSendPayloadType; - return config; - } - void RunVideoSendTest(Call* call, - const VideoSendStream::Config& config, - test::RtpRtcpObserver* observer) { - send_stream_ = call->CreateVideoSendStream(config); - scoped_ptr frame_generator_capturer( - test::FrameGeneratorCapturer::Create( - send_stream_->Input(), 320, 240, 30, Clock::GetRealTimeClock())); - send_stream_->StartSending(); - frame_generator_capturer->Start(); - - EXPECT_EQ(kEventSignaled, observer->Wait()); - - observer->StopSending(); - frame_generator_capturer->Stop(); - send_stream_->StopSending(); - call->DestroyVideoSendStream(send_stream_); - } + void TestAudioVideoSync(bool fec); + + void TestMinTransmitBitrate(bool pad_to_min_bitrate); - VideoSendStream* send_stream_; - test::FakeEncoder fake_encoder_; + void TestCaptureNtpTime(const FakeNetworkPipe::Config& net_config, + int threshold_ms, + int start_time_ms, + int run_time_ms); }; class SyncRtcpObserver : public test::RtpRtcpObserver { public: explicit SyncRtcpObserver(const FakeNetworkPipe::Config& config) - : test::RtpRtcpObserver(kLongTimeoutMs, config), - critical_section_(CriticalSectionWrapper::CreateCriticalSection()) {} + : test::RtpRtcpObserver(CallPerfTest::kLongTimeoutMs, config), + crit_(CriticalSectionWrapper::CreateCriticalSection()) {} virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE { RTCPUtility::RTCPParserV2 parser(packet, length, true); @@ -95,7 +70,7 @@ packet_type = parser.Iterate()) { if (packet_type == RTCPUtility::kRtcpSrCode) { const RTCPUtility::RTCPPacket& packet = parser.Packet(); - synchronization::RtcpMeasurement ntp_rtp_pair( + RtcpMeasurement ntp_rtp_pair( packet.SR.NTPMostSignificant, packet.SR.NTPLeastSignificant, packet.SR.RTPTimestamp); @@ -106,22 +81,22 @@ } int64_t RtpTimestampToNtp(uint32_t timestamp) const { - CriticalSectionScoped cs(critical_section_.get()); + CriticalSectionScoped lock(crit_.get()); int64_t timestamp_in_ms = -1; if (ntp_rtp_pairs_.size() == 2) { // TODO(stefan): We can't EXPECT_TRUE on this call due to a bug in the // RTCP sender where it sends RTCP SR before any RTP packets, which leads // to a bogus NTP/RTP mapping. - synchronization::RtpToNtpMs(timestamp, ntp_rtp_pairs_, ×tamp_in_ms); + RtpToNtpMs(timestamp, ntp_rtp_pairs_, ×tamp_in_ms); return timestamp_in_ms; } return -1; } private: - void StoreNtpRtpPair(synchronization::RtcpMeasurement ntp_rtp_pair) { - CriticalSectionScoped cs(critical_section_.get()); - for (synchronization::RtcpList::iterator it = ntp_rtp_pairs_.begin(); + void StoreNtpRtpPair(RtcpMeasurement ntp_rtp_pair) { + CriticalSectionScoped lock(crit_.get()); + for (RtcpList::iterator it = ntp_rtp_pairs_.begin(); it != ntp_rtp_pairs_.end(); ++it) { if (ntp_rtp_pair.ntp_secs == it->ntp_secs && @@ -138,8 +113,8 @@ ntp_rtp_pairs_.push_front(ntp_rtp_pair); } - scoped_ptr critical_section_; - synchronization::RtcpList ntp_rtp_pairs_; + const scoped_ptr crit_; + RtcpList ntp_rtp_pairs_ GUARDED_BY(crit_); }; class VideoRtcpAndSyncObserver : public SyncRtcpObserver, public VideoRenderer { @@ -177,8 +152,12 @@ int64_t stream_offset = latest_audio_ntp - latest_video_ntp; std::stringstream ss; ss << stream_offset; - webrtc::test::PrintResult( - "stream_offset", "", "synchronization", ss.str(), "ms", false); + webrtc::test::PrintResult("stream_offset", + "", + "synchronization", + ss.str(), + "ms", + false); int64_t time_since_creation = now_ms - creation_time_ms_; // During the first couple of seconds audio and video can falsely be // estimated as being synchronized. We don't want to trigger on those. @@ -200,7 +179,7 @@ } private: - Clock* clock_; + Clock* const clock_; int voe_channel_; VoEVideoSync* voe_sync_; SyncRtcpObserver* audio_observer_; @@ -208,7 +187,32 @@ int64_t first_time_in_sync_; }; -TEST_F(CallPerfTest, PlaysOutAudioAndVideoInSync) { +void CallPerfTest::TestAudioVideoSync(bool fec) { + class AudioPacketReceiver : public PacketReceiver { + public: + AudioPacketReceiver(int channel, VoENetwork* voe_network) + : channel_(channel), + voe_network_(voe_network), + parser_(RtpHeaderParser::Create()) {} + virtual DeliveryStatus DeliverPacket(const uint8_t* packet, + size_t length) OVERRIDE { + int ret; + if (parser_->IsRtcp(packet, static_cast(length))) { + ret = voe_network_->ReceivedRTCPPacket( + channel_, packet, static_cast(length)); + } else { + ret = voe_network_->ReceivedRTPPacket( + channel_, packet, static_cast(length), PacketTime()); + } + return ret == 0 ? DELIVERY_OK : DELIVERY_PACKET_ERROR; + } + + private: + int channel_; + VoENetwork* voe_network_; + scoped_ptr parser_; + }; + VoiceEngine* voice_engine = VoiceEngine::Create(); VoEBase* voe_base = VoEBase::GetInterface(voice_engine); VoECodec* voe_codec = VoECodec::GetInterface(voice_engine); @@ -224,42 +228,21 @@ FakeNetworkPipe::Config net_config; net_config.queue_delay_ms = 500; + net_config.loss_percent = 5; SyncRtcpObserver audio_observer(net_config); - VideoRtcpAndSyncObserver observer( - Clock::GetRealTimeClock(), channel, voe_sync, &audio_observer); + VideoRtcpAndSyncObserver observer(Clock::GetRealTimeClock(), + channel, + voe_sync, + &audio_observer); Call::Config receiver_config(observer.ReceiveTransport()); receiver_config.voice_engine = voice_engine; - scoped_ptr sender_call( - Call::Create(Call::Config(observer.SendTransport()))); - scoped_ptr receiver_call(Call::Create(receiver_config)); + CreateCalls(Call::Config(observer.SendTransport()), receiver_config); + CodecInst isac = {103, "ISAC", 16000, 480, 1, 32000}; EXPECT_EQ(0, voe_codec->SetSendCodec(channel, isac)); - class VoicePacketReceiver : public PacketReceiver { - public: - VoicePacketReceiver(int channel, VoENetwork* voe_network) - : channel_(channel), - voe_network_(voe_network), - parser_(RtpHeaderParser::Create()) {} - virtual bool DeliverPacket(const uint8_t* packet, size_t length) { - int ret; - if (parser_->IsRtcp(packet, static_cast(length))) { - ret = voe_network_->ReceivedRTCPPacket( - channel_, packet, static_cast(length)); - } else { - ret = voe_network_->ReceivedRTPPacket( - channel_, packet, static_cast(length)); - } - return ret == 0; - } - - private: - int channel_; - VoENetwork* voe_network_; - scoped_ptr parser_; - } voe_packet_receiver(channel, voe_network); - + AudioPacketReceiver voe_packet_receiver(channel, voe_network); audio_observer.SetReceivers(&voe_packet_receiver, &voe_packet_receiver); internal::TransportAdapter transport_adapter(audio_observer.SendTransport()); @@ -267,38 +250,29 @@ EXPECT_EQ(0, voe_network->RegisterExternalTransport(channel, transport_adapter)); - observer.SetReceivers(receiver_call->Receiver(), sender_call->Receiver()); + observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver()); test::FakeDecoder fake_decoder; - VideoSendStream::Config send_config = GetSendTestConfig(sender_call.get()); + CreateSendConfig(1); + CreateMatchingReceiveConfigs(); + + send_config_.rtp.nack.rtp_history_ms = kNackRtpHistoryMs; + if (fec) { + send_config_.rtp.fec.red_payload_type = kRedPayloadType; + send_config_.rtp.fec.ulpfec_payload_type = kUlpfecPayloadType; + receive_configs_[0].rtp.fec.red_payload_type = kRedPayloadType; + receive_configs_[0].rtp.fec.ulpfec_payload_type = kUlpfecPayloadType; + } + receive_configs_[0].rtp.nack.rtp_history_ms = 1000; + receive_configs_[0].renderer = &observer; + receive_configs_[0].audio_channel_id = channel; + + CreateStreams(); + + CreateFrameGeneratorCapturer(); - VideoReceiveStream::Config receive_config = - receiver_call->GetDefaultReceiveConfig(); - receive_config.codecs.clear(); - receive_config.codecs.push_back(send_config.codec); - ExternalVideoDecoder decoder; - decoder.decoder = &fake_decoder; - decoder.payload_type = send_config.codec.plType; - receive_config.external_decoders.push_back(decoder); - receive_config.rtp.remote_ssrc = send_config.rtp.ssrcs[0]; - receive_config.rtp.local_ssrc = kReceiverLocalSsrc; - receive_config.renderer = &observer; - receive_config.audio_channel_id = channel; - - VideoSendStream* send_stream = - sender_call->CreateVideoSendStream(send_config); - VideoReceiveStream* receive_stream = - receiver_call->CreateVideoReceiveStream(receive_config); - scoped_ptr capturer( - test::FrameGeneratorCapturer::Create(send_stream->Input(), - send_config.codec.width, - send_config.codec.height, - 30, - Clock::GetRealTimeClock())); - receive_stream->StartReceiving(); - send_stream->StartSending(); - capturer->Start(); + Start(); fake_audio_device.Start(); EXPECT_EQ(0, voe_base->StartPlayout(channel)); @@ -313,9 +287,7 @@ EXPECT_EQ(0, voe_base->StopPlayout(channel)); fake_audio_device.Stop(); - capturer->Stop(); - send_stream->StopSending(); - receive_stream->StopReceiving(); + Stop(); observer.StopSending(); audio_observer.StopSending(); @@ -324,32 +296,367 @@ voe_codec->Release(); voe_network->Release(); voe_sync->Release(); - sender_call->DestroyVideoSendStream(send_stream); - receiver_call->DestroyVideoReceiveStream(receive_stream); + + DestroyStreams(); + VoiceEngine::Delete(voice_engine); } +TEST_F(CallPerfTest, PlaysOutAudioAndVideoInSync) { + TestAudioVideoSync(false); +} + +TEST_F(CallPerfTest, PlaysOutAudioAndVideoInSyncWithFec) { + TestAudioVideoSync(true); +} + +void CallPerfTest::TestCaptureNtpTime(const FakeNetworkPipe::Config& net_config, + int threshold_ms, + int start_time_ms, + int run_time_ms) { + class CaptureNtpTimeObserver : public test::EndToEndTest, + public VideoRenderer { + public: + CaptureNtpTimeObserver(const FakeNetworkPipe::Config& config, + int threshold_ms, + int start_time_ms, + int run_time_ms) + : EndToEndTest(kLongTimeoutMs, config), + clock_(Clock::GetRealTimeClock()), + threshold_ms_(threshold_ms), + start_time_ms_(start_time_ms), + run_time_ms_(run_time_ms), + creation_time_ms_(clock_->TimeInMilliseconds()), + capturer_(NULL), + rtp_start_timestamp_set_(false), + rtp_start_timestamp_(0) {} + + private: + virtual void RenderFrame(const I420VideoFrame& video_frame, + int time_to_render_ms) OVERRIDE { + if (video_frame.ntp_time_ms() <= 0) { + // Haven't got enough RTCP SR in order to calculate the capture ntp + // time. + return; + } + + int64_t now_ms = clock_->TimeInMilliseconds(); + int64_t time_since_creation = now_ms - creation_time_ms_; + if (time_since_creation < start_time_ms_) { + // Wait for |start_time_ms_| before start measuring. + return; + } + + if (time_since_creation > run_time_ms_) { + observation_complete_->Set(); + } + + FrameCaptureTimeList::iterator iter = + capture_time_list_.find(video_frame.timestamp()); + EXPECT_TRUE(iter != capture_time_list_.end()); + + // The real capture time has been wrapped to uint32_t before converted + // to rtp timestamp in the sender side. So here we convert the estimated + // capture time to a uint32_t 90k timestamp also for comparing. + uint32_t estimated_capture_timestamp = + 90 * static_cast(video_frame.ntp_time_ms()); + uint32_t real_capture_timestamp = iter->second; + int time_offset_ms = real_capture_timestamp - estimated_capture_timestamp; + time_offset_ms = time_offset_ms / 90; + std::stringstream ss; + ss << time_offset_ms; + + webrtc::test::PrintResult( + "capture_ntp_time", "", "real - estimated", ss.str(), "ms", true); + EXPECT_TRUE(std::abs(time_offset_ms) < threshold_ms_); + } + + virtual Action OnSendRtp(const uint8_t* packet, size_t length) { + RTPHeader header; + EXPECT_TRUE(parser_->Parse(packet, length, &header)); + + if (!rtp_start_timestamp_set_) { + // Calculate the rtp timestamp offset in order to calculate the real + // capture time. + uint32_t first_capture_timestamp = + 90 * static_cast(capturer_->first_frame_capture_time()); + rtp_start_timestamp_ = header.timestamp - first_capture_timestamp; + rtp_start_timestamp_set_ = true; + } + + uint32_t capture_timestamp = header.timestamp - rtp_start_timestamp_; + capture_time_list_.insert( + capture_time_list_.end(), + std::make_pair(header.timestamp, capture_timestamp)); + return SEND_PACKET; + } + + virtual void OnFrameGeneratorCapturerCreated( + test::FrameGeneratorCapturer* frame_generator_capturer) OVERRIDE { + capturer_ = frame_generator_capturer; + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + (*receive_configs)[0].renderer = this; + // Enable the receiver side rtt calculation. + (*receive_configs)[0].rtp.rtcp_xr.receiver_reference_time_report = true; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) << "Timed out while waiting for " + "estimated capture NTP time to be " + "within bounds."; + } + + Clock* clock_; + int threshold_ms_; + int start_time_ms_; + int run_time_ms_; + int64_t creation_time_ms_; + test::FrameGeneratorCapturer* capturer_; + bool rtp_start_timestamp_set_; + uint32_t rtp_start_timestamp_; + typedef std::map FrameCaptureTimeList; + FrameCaptureTimeList capture_time_list_; + } test(net_config, threshold_ms, start_time_ms, run_time_ms); + + RunBaseTest(&test); +} + +TEST_F(CallPerfTest, CaptureNtpTimeWithNetworkDelay) { + FakeNetworkPipe::Config net_config; + net_config.queue_delay_ms = 100; + // TODO(wu): lower the threshold as the calculation/estimatation becomes more + // accurate. + const int kThresholdMs = 100; + const int kStartTimeMs = 10000; + const int kRunTimeMs = 20000; + TestCaptureNtpTime(net_config, kThresholdMs, kStartTimeMs, kRunTimeMs); +} + +TEST_F(CallPerfTest, CaptureNtpTimeWithNetworkJitter) { + FakeNetworkPipe::Config net_config; + net_config.queue_delay_ms = 100; + net_config.delay_standard_deviation_ms = 10; + // TODO(wu): lower the threshold as the calculation/estimatation becomes more + // accurate. + const int kThresholdMs = 100; + const int kStartTimeMs = 10000; + const int kRunTimeMs = 20000; + TestCaptureNtpTime(net_config, kThresholdMs, kStartTimeMs, kRunTimeMs); +} + TEST_F(CallPerfTest, RegisterCpuOveruseObserver) { // Verifies that either a normal or overuse callback is triggered. - class OveruseCallbackObserver : public test::RtpRtcpObserver, - public webrtc::OveruseCallback { + class LoadObserver : public test::SendTest, public webrtc::LoadObserver { public: - OveruseCallbackObserver() : RtpRtcpObserver(kLongTimeoutMs) {} + LoadObserver() : SendTest(kLongTimeoutMs) {} - virtual void OnOveruse() OVERRIDE { + virtual void OnLoadUpdate(Load load) OVERRIDE { observation_complete_->Set(); } - virtual void OnNormalUse() OVERRIDE { - observation_complete_->Set(); + + virtual Call::Config GetSenderCallConfig() OVERRIDE { + Call::Config config(SendTransport()); + config.overuse_callback = this; + return config; } - }; - OveruseCallbackObserver observer; - Call::Config call_config(observer.SendTransport()); - call_config.overuse_callback = &observer; - scoped_ptr call(Call::Create(call_config)); + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out before receiving an overuse callback."; + } + } test; - VideoSendStream::Config send_config = GetSendTestConfig(call.get()); - RunVideoSendTest(call.get(), send_config, &observer); + RunBaseTest(&test); } + +void CallPerfTest::TestMinTransmitBitrate(bool pad_to_min_bitrate) { + static const int kMaxEncodeBitrateKbps = 30; + static const int kMinTransmitBitrateBps = 150000; + static const int kMinAcceptableTransmitBitrate = 130; + static const int kMaxAcceptableTransmitBitrate = 170; + static const int kNumBitrateObservationsInRange = 100; + class BitrateObserver : public test::EndToEndTest, public PacketReceiver { + public: + explicit BitrateObserver(bool using_min_transmit_bitrate) + : EndToEndTest(kLongTimeoutMs), + send_stream_(NULL), + send_transport_receiver_(NULL), + pad_to_min_bitrate_(using_min_transmit_bitrate), + num_bitrate_observations_in_range_(0) {} + + private: + virtual void SetReceivers(PacketReceiver* send_transport_receiver, + PacketReceiver* receive_transport_receiver) + OVERRIDE { + send_transport_receiver_ = send_transport_receiver; + test::RtpRtcpObserver::SetReceivers(this, receive_transport_receiver); + } + + virtual DeliveryStatus DeliverPacket(const uint8_t* packet, + size_t length) OVERRIDE { + VideoSendStream::Stats stats = send_stream_->GetStats(); + if (stats.substreams.size() > 0) { + assert(stats.substreams.size() == 1); + int bitrate_kbps = + stats.substreams.begin()->second.total_bitrate_bps / 1000; + if (bitrate_kbps > 0) { + test::PrintResult( + "bitrate_stats_", + (pad_to_min_bitrate_ ? "min_transmit_bitrate" + : "without_min_transmit_bitrate"), + "bitrate_kbps", + static_cast(bitrate_kbps), + "kbps", + false); + if (pad_to_min_bitrate_) { + if (bitrate_kbps > kMinAcceptableTransmitBitrate && + bitrate_kbps < kMaxAcceptableTransmitBitrate) { + ++num_bitrate_observations_in_range_; + } + } else { + // Expect bitrate stats to roughly match the max encode bitrate. + if (bitrate_kbps > kMaxEncodeBitrateKbps - 5 && + bitrate_kbps < kMaxEncodeBitrateKbps + 5) { + ++num_bitrate_observations_in_range_; + } + } + if (num_bitrate_observations_in_range_ == + kNumBitrateObservationsInRange) + observation_complete_->Set(); + } + } + return send_transport_receiver_->DeliverPacket(packet, length); + } + + virtual void OnStreamsCreated( + VideoSendStream* send_stream, + const std::vector& receive_streams) OVERRIDE { + send_stream_ = send_stream; + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + if (pad_to_min_bitrate_) { + encoder_config->min_transmit_bitrate_bps = kMinTransmitBitrateBps; + } else { + assert(encoder_config->min_transmit_bitrate_bps == 0); + } + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timeout while waiting for send-bitrate stats."; + } + + VideoSendStream* send_stream_; + PacketReceiver* send_transport_receiver_; + const bool pad_to_min_bitrate_; + int num_bitrate_observations_in_range_; + } test(pad_to_min_bitrate); + + fake_encoder_.SetMaxBitrate(kMaxEncodeBitrateKbps); + RunBaseTest(&test); +} + +TEST_F(CallPerfTest, PadsToMinTransmitBitrate) { TestMinTransmitBitrate(true); } + +TEST_F(CallPerfTest, NoPadWithoutMinTransmitBitrate) { + TestMinTransmitBitrate(false); +} + +TEST_F(CallPerfTest, KeepsHighBitrateWhenReconfiguringSender) { + static const uint32_t kInitialBitrateKbps = 400; + static const uint32_t kReconfigureThresholdKbps = 600; + static const uint32_t kPermittedReconfiguredBitrateDiffKbps = 100; + + class BitrateObserver : public test::EndToEndTest, public test::FakeEncoder { + public: + BitrateObserver() + : EndToEndTest(kDefaultTimeoutMs), + FakeEncoder(Clock::GetRealTimeClock()), + time_to_reconfigure_(webrtc::EventWrapper::Create()), + encoder_inits_(0) {} + + virtual int32_t InitEncode(const VideoCodec* config, + int32_t number_of_cores, + uint32_t max_payload_size) OVERRIDE { + if (encoder_inits_ == 0) { + EXPECT_EQ(kInitialBitrateKbps, config->startBitrate) + << "Encoder not initialized at expected bitrate."; + } + ++encoder_inits_; + if (encoder_inits_ == 2) { + EXPECT_GE(last_set_bitrate_, kReconfigureThresholdKbps); + EXPECT_NEAR(config->startBitrate, + last_set_bitrate_, + kPermittedReconfiguredBitrateDiffKbps) + << "Encoder reconfigured with bitrate too far away from last set."; + observation_complete_->Set(); + } + return FakeEncoder::InitEncode(config, number_of_cores, max_payload_size); + } + + virtual int32_t SetRates(uint32_t new_target_bitrate_kbps, + uint32_t framerate) OVERRIDE { + last_set_bitrate_ = new_target_bitrate_kbps; + if (encoder_inits_ == 1 && + new_target_bitrate_kbps > kReconfigureThresholdKbps) { + time_to_reconfigure_->Set(); + } + return FakeEncoder::SetRates(new_target_bitrate_kbps, framerate); + } + + Call::Config GetSenderCallConfig() OVERRIDE { + Call::Config config = EndToEndTest::GetSenderCallConfig(); + config.stream_start_bitrate_bps = kInitialBitrateKbps * 1000; + return config; + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->encoder_settings.encoder = this; + encoder_config->streams[0].min_bitrate_bps = 50000; + encoder_config->streams[0].target_bitrate_bps = + encoder_config->streams[0].max_bitrate_bps = 2000000; + + encoder_config_ = *encoder_config; + } + + virtual void OnStreamsCreated( + VideoSendStream* send_stream, + const std::vector& receive_streams) OVERRIDE { + send_stream_ = send_stream; + } + + virtual void PerformTest() OVERRIDE { + ASSERT_EQ(kEventSignaled, time_to_reconfigure_->Wait(kDefaultTimeoutMs)) + << "Timed out before receiving an initial high bitrate."; + encoder_config_.streams[0].width *= 2; + encoder_config_.streams[0].height *= 2; + EXPECT_TRUE(send_stream_->ReconfigureVideoEncoder(encoder_config_)); + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for a couple of high bitrate estimates " + "after reconfiguring the send stream."; + } + + private: + scoped_ptr time_to_reconfigure_; + int encoder_inits_; + uint32_t last_set_bitrate_; + VideoSendStream* send_stream_; + VideoEncoderConfig encoder_config_; + } test; + + RunBaseTest(&test); +} + } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/call_tests.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/call_tests.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/call_tests.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/call_tests.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,1478 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#include - -#include -#include -#include -#include - -#include "testing/gtest/include/gtest/gtest.h" - -#include "webrtc/call.h" -#include "webrtc/frame_callback.h" -#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" -#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/event_wrapper.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/system_wrappers/interface/sleep.h" -#include "webrtc/test/direct_transport.h" -#include "webrtc/test/fake_audio_device.h" -#include "webrtc/test/fake_decoder.h" -#include "webrtc/test/fake_encoder.h" -#include "webrtc/test/frame_generator.h" -#include "webrtc/test/frame_generator_capturer.h" -#include "webrtc/test/null_transport.h" -#include "webrtc/test/rtp_rtcp_observer.h" -#include "webrtc/test/testsupport/fileutils.h" -#include "webrtc/test/testsupport/perf_test.h" -#include "webrtc/video/transport_adapter.h" - -namespace webrtc { - -static unsigned int kDefaultTimeoutMs = 30 * 1000; -static unsigned int kLongTimeoutMs = 120 * 1000; -static const uint32_t kSendSsrc = 0x654321; -static const uint32_t kSendRtxSsrc = 0x424242; -static const uint32_t kReceiverLocalSsrc = 0x123456; -static const uint8_t kSendPayloadType = 125; -static const uint8_t kSendRtxPayloadType = 126; - -class CallTest : public ::testing::Test { - public: - CallTest() - : send_stream_(NULL), - receive_stream_(NULL), - fake_encoder_(Clock::GetRealTimeClock()) {} - - virtual ~CallTest() { - EXPECT_EQ(NULL, send_stream_); - EXPECT_EQ(NULL, receive_stream_); - } - - protected: - void CreateCalls(const Call::Config& sender_config, - const Call::Config& receiver_config) { - sender_call_.reset(Call::Create(sender_config)); - receiver_call_.reset(Call::Create(receiver_config)); - } - - void CreateTestConfigs() { - send_config_ = sender_call_->GetDefaultSendConfig(); - receive_config_ = receiver_call_->GetDefaultReceiveConfig(); - - send_config_.rtp.ssrcs.push_back(kSendSsrc); - send_config_.encoder = &fake_encoder_; - send_config_.internal_source = false; - test::FakeEncoder::SetCodecSettings(&send_config_.codec, 1); - send_config_.codec.plType = kSendPayloadType; - - receive_config_.codecs.clear(); - receive_config_.codecs.push_back(send_config_.codec); - ExternalVideoDecoder decoder; - decoder.decoder = &fake_decoder_; - decoder.payload_type = send_config_.codec.plType; - receive_config_.external_decoders.push_back(decoder); - receive_config_.rtp.remote_ssrc = send_config_.rtp.ssrcs[0]; - receive_config_.rtp.local_ssrc = kReceiverLocalSsrc; - } - - void CreateStreams() { - assert(send_stream_ == NULL); - assert(receive_stream_ == NULL); - - send_stream_ = sender_call_->CreateVideoSendStream(send_config_); - receive_stream_ = receiver_call_->CreateVideoReceiveStream(receive_config_); - } - - void CreateFrameGenerator() { - frame_generator_capturer_.reset( - test::FrameGeneratorCapturer::Create(send_stream_->Input(), - send_config_.codec.width, - send_config_.codec.height, - 30, - Clock::GetRealTimeClock())); - } - - void StartSending() { - receive_stream_->StartReceiving(); - send_stream_->StartSending(); - if (frame_generator_capturer_.get() != NULL) - frame_generator_capturer_->Start(); - } - - void StopSending() { - if (frame_generator_capturer_.get() != NULL) - frame_generator_capturer_->Stop(); - if (send_stream_ != NULL) - send_stream_->StopSending(); - if (receive_stream_ != NULL) - receive_stream_->StopReceiving(); - } - - void DestroyStreams() { - if (send_stream_ != NULL) - sender_call_->DestroyVideoSendStream(send_stream_); - if (receive_stream_ != NULL) - receiver_call_->DestroyVideoReceiveStream(receive_stream_); - send_stream_ = NULL; - receive_stream_ = NULL; - } - - void DecodesRetransmittedFrame(bool retransmit_over_rtx); - void ReceivesPliAndRecovers(int rtp_history_ms); - void RespectsRtcpMode(newapi::RtcpMode rtcp_mode); - void TestXrReceiverReferenceTimeReport(bool enable_rrtr); - - scoped_ptr sender_call_; - scoped_ptr receiver_call_; - - VideoSendStream::Config send_config_; - VideoReceiveStream::Config receive_config_; - - VideoSendStream* send_stream_; - VideoReceiveStream* receive_stream_; - - scoped_ptr frame_generator_capturer_; - - test::FakeEncoder fake_encoder_; - test::FakeDecoder fake_decoder_; -}; - -class NackObserver : public test::RtpRtcpObserver { - static const int kNumberOfNacksToObserve = 2; - static const int kLossBurstSize = 2; - static const int kPacketsBetweenLossBursts = 9; - - public: - NackObserver() - : test::RtpRtcpObserver(kLongTimeoutMs), - rtp_parser_(RtpHeaderParser::Create()), - sent_rtp_packets_(0), - packets_left_to_drop_(0), - nacks_left_(kNumberOfNacksToObserve) {} - - private: - virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { - RTPHeader header; - EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast(length), &header)); - - // Never drop retransmitted packets. - if (dropped_packets_.find(header.sequenceNumber) != - dropped_packets_.end()) { - retransmitted_packets_.insert(header.sequenceNumber); - if (nacks_left_ == 0 && - retransmitted_packets_.size() == dropped_packets_.size()) { - observation_complete_->Set(); - } - return SEND_PACKET; - } - - ++sent_rtp_packets_; - - // Enough NACKs received, stop dropping packets. - if (nacks_left_ == 0) - return SEND_PACKET; - - // Check if it's time for a new loss burst. - if (sent_rtp_packets_ % kPacketsBetweenLossBursts == 0) - packets_left_to_drop_ = kLossBurstSize; - - if (packets_left_to_drop_ > 0) { - --packets_left_to_drop_; - dropped_packets_.insert(header.sequenceNumber); - return DROP_PACKET; - } - - return SEND_PACKET; - } - - virtual Action OnReceiveRtcp(const uint8_t* packet, size_t length) OVERRIDE { - RTCPUtility::RTCPParserV2 parser(packet, length, true); - EXPECT_TRUE(parser.IsValid()); - - RTCPUtility::RTCPPacketTypes packet_type = parser.Begin(); - while (packet_type != RTCPUtility::kRtcpNotValidCode) { - if (packet_type == RTCPUtility::kRtcpRtpfbNackCode) { - --nacks_left_; - break; - } - packet_type = parser.Iterate(); - } - return SEND_PACKET; - } - - private: - scoped_ptr rtp_parser_; - std::set dropped_packets_; - std::set retransmitted_packets_; - uint64_t sent_rtp_packets_; - int packets_left_to_drop_; - int nacks_left_; -}; - -TEST_F(CallTest, UsesTraceCallback) { - const unsigned int kSenderTraceFilter = kTraceDebug; - const unsigned int kReceiverTraceFilter = kTraceDefault & (~kTraceDebug); - class TraceObserver : public TraceCallback { - public: - explicit TraceObserver(unsigned int filter) - : filter_(filter), messages_left_(50), done_(EventWrapper::Create()) {} - - virtual void Print(TraceLevel level, - const char* message, - int length) OVERRIDE { - EXPECT_EQ(0u, level & (~filter_)); - if (--messages_left_ == 0) - done_->Set(); - } - - EventTypeWrapper Wait() { return done_->Wait(kDefaultTimeoutMs); } - - private: - unsigned int filter_; - unsigned int messages_left_; - scoped_ptr done_; - } sender_trace(kSenderTraceFilter), receiver_trace(kReceiverTraceFilter); - - test::DirectTransport send_transport, receive_transport; - Call::Config sender_call_config(&send_transport); - sender_call_config.trace_callback = &sender_trace; - sender_call_config.trace_filter = kSenderTraceFilter; - Call::Config receiver_call_config(&receive_transport); - receiver_call_config.trace_callback = &receiver_trace; - receiver_call_config.trace_filter = kReceiverTraceFilter; - CreateCalls(sender_call_config, receiver_call_config); - send_transport.SetReceiver(receiver_call_->Receiver()); - receive_transport.SetReceiver(sender_call_->Receiver()); - - CreateTestConfigs(); - - CreateStreams(); - CreateFrameGenerator(); - StartSending(); - - // Wait() waits for a couple of trace callbacks to occur. - EXPECT_EQ(kEventSignaled, sender_trace.Wait()); - EXPECT_EQ(kEventSignaled, receiver_trace.Wait()); - - StopSending(); - send_transport.StopSending(); - receive_transport.StopSending(); - DestroyStreams(); - - // The TraceCallback instance MUST outlive Calls, destroy Calls explicitly. - sender_call_.reset(); - receiver_call_.reset(); -} - -TEST_F(CallTest, ReceiverCanBeStartedTwice) { - test::NullTransport transport; - CreateCalls(Call::Config(&transport), Call::Config(&transport)); - - CreateTestConfigs(); - CreateStreams(); - - receive_stream_->StartReceiving(); - receive_stream_->StartReceiving(); - - DestroyStreams(); -} - -TEST_F(CallTest, ReceiverCanBeStoppedTwice) { - test::NullTransport transport; - CreateCalls(Call::Config(&transport), Call::Config(&transport)); - - CreateTestConfigs(); - CreateStreams(); - - receive_stream_->StopReceiving(); - receive_stream_->StopReceiving(); - - DestroyStreams(); -} - -TEST_F(CallTest, RendersSingleDelayedFrame) { - static const int kWidth = 320; - static const int kHeight = 240; - // This constant is chosen to be higher than the timeout in the video_render - // module. This makes sure that frames aren't dropped if there are no other - // frames in the queue. - static const int kDelayRenderCallbackMs = 1000; - - class Renderer : public VideoRenderer { - public: - Renderer() : event_(EventWrapper::Create()) {} - - virtual void RenderFrame(const I420VideoFrame& video_frame, - int /*time_to_render_ms*/) OVERRIDE { - event_->Set(); - } - - EventTypeWrapper Wait() { return event_->Wait(kDefaultTimeoutMs); } - - scoped_ptr event_; - } renderer; - - class TestFrameCallback : public I420FrameCallback { - public: - TestFrameCallback() : event_(EventWrapper::Create()) {} - - EventTypeWrapper Wait() { return event_->Wait(kDefaultTimeoutMs); } - - private: - virtual void FrameCallback(I420VideoFrame* frame) OVERRIDE { - SleepMs(kDelayRenderCallbackMs); - event_->Set(); - } - - scoped_ptr event_; - }; - - test::DirectTransport sender_transport, receiver_transport; - - CreateCalls(Call::Config(&sender_transport), - Call::Config(&receiver_transport)); - - sender_transport.SetReceiver(receiver_call_->Receiver()); - receiver_transport.SetReceiver(sender_call_->Receiver()); - - CreateTestConfigs(); - - TestFrameCallback pre_render_callback; - receive_config_.pre_render_callback = &pre_render_callback; - receive_config_.renderer = &renderer; - - CreateStreams(); - StartSending(); - - // Create frames that are smaller than the send width/height, this is done to - // check that the callbacks are done after processing video. - scoped_ptr frame_generator( - test::FrameGenerator::Create(kWidth, kHeight)); - send_stream_->Input()->SwapFrame(frame_generator->NextFrame()); - EXPECT_EQ(kEventSignaled, pre_render_callback.Wait()) - << "Timed out while waiting for pre-render callback."; - EXPECT_EQ(kEventSignaled, renderer.Wait()) - << "Timed out while waiting for the frame to render."; - - StopSending(); - - sender_transport.StopSending(); - receiver_transport.StopSending(); - - DestroyStreams(); -} - -TEST_F(CallTest, TransmitsFirstFrame) { - class Renderer : public VideoRenderer { - public: - Renderer() : event_(EventWrapper::Create()) {} - - virtual void RenderFrame(const I420VideoFrame& video_frame, - int /*time_to_render_ms*/) OVERRIDE { - event_->Set(); - } - - EventTypeWrapper Wait() { return event_->Wait(kDefaultTimeoutMs); } - - scoped_ptr event_; - } renderer; - - test::DirectTransport sender_transport, receiver_transport; - - CreateCalls(Call::Config(&sender_transport), - Call::Config(&receiver_transport)); - - sender_transport.SetReceiver(receiver_call_->Receiver()); - receiver_transport.SetReceiver(sender_call_->Receiver()); - - CreateTestConfigs(); - receive_config_.renderer = &renderer; - - CreateStreams(); - StartSending(); - - scoped_ptr frame_generator(test::FrameGenerator::Create( - send_config_.codec.width, send_config_.codec.height)); - send_stream_->Input()->SwapFrame(frame_generator->NextFrame()); - - EXPECT_EQ(kEventSignaled, renderer.Wait()) - << "Timed out while waiting for the frame to render."; - - StopSending(); - - sender_transport.StopSending(); - receiver_transport.StopSending(); - - DestroyStreams(); -} - -TEST_F(CallTest, ReceiverUsesLocalSsrc) { - class SyncRtcpObserver : public test::RtpRtcpObserver { - public: - SyncRtcpObserver() : test::RtpRtcpObserver(kDefaultTimeoutMs) {} - - virtual Action OnReceiveRtcp(const uint8_t* packet, - size_t length) OVERRIDE { - RTCPUtility::RTCPParserV2 parser(packet, length, true); - EXPECT_TRUE(parser.IsValid()); - uint32_t ssrc = 0; - ssrc |= static_cast(packet[4]) << 24; - ssrc |= static_cast(packet[5]) << 16; - ssrc |= static_cast(packet[6]) << 8; - ssrc |= static_cast(packet[7]) << 0; - EXPECT_EQ(kReceiverLocalSsrc, ssrc); - observation_complete_->Set(); - - return SEND_PACKET; - } - } observer; - - CreateCalls(Call::Config(observer.SendTransport()), - Call::Config(observer.ReceiveTransport())); - - observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver()); - - CreateTestConfigs(); - - CreateStreams(); - CreateFrameGenerator(); - StartSending(); - - EXPECT_EQ(kEventSignaled, observer.Wait()) - << "Timed out while waiting for a receiver RTCP packet to be sent."; - - StopSending(); - - observer.StopSending(); - - DestroyStreams(); -} - -TEST_F(CallTest, ReceivesAndRetransmitsNack) { - NackObserver observer; - - CreateCalls(Call::Config(observer.SendTransport()), - Call::Config(observer.ReceiveTransport())); - - observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver()); - - CreateTestConfigs(); - int rtp_history_ms = 1000; - send_config_.rtp.nack.rtp_history_ms = rtp_history_ms; - receive_config_.rtp.nack.rtp_history_ms = rtp_history_ms; - - CreateStreams(); - CreateFrameGenerator(); - StartSending(); - - // Wait() waits for an event triggered when NACKs have been received, NACKed - // packets retransmitted and frames rendered again. - EXPECT_EQ(kEventSignaled, observer.Wait()); - - StopSending(); - - observer.StopSending(); - - DestroyStreams(); -} - -// This test drops second RTP packet with a marker bit set, makes sure it's -// retransmitted and renders. Retransmission SSRCs are also checked. -void CallTest::DecodesRetransmittedFrame(bool retransmit_over_rtx) { - static const int kDroppedFrameNumber = 2; - class RetransmissionObserver : public test::RtpRtcpObserver, - public I420FrameCallback { - public: - RetransmissionObserver(bool expect_rtx) - : RtpRtcpObserver(kDefaultTimeoutMs), - retransmission_ssrc_(expect_rtx ? kSendRtxSsrc : kSendSsrc), - retransmission_payload_type_(expect_rtx ? kSendRtxPayloadType - : kSendPayloadType), - marker_bits_observed_(0), - retransmitted_timestamp_(0), - frame_retransmitted_(false) {} - - private: - virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { - RTPHeader header; - EXPECT_TRUE(parser_->Parse(packet, static_cast(length), &header)); - - if (header.timestamp == retransmitted_timestamp_) { - EXPECT_EQ(retransmission_ssrc_, header.ssrc); - EXPECT_EQ(retransmission_payload_type_, header.payloadType); - frame_retransmitted_ = true; - return SEND_PACKET; - } - - EXPECT_EQ(kSendSsrc, header.ssrc); - EXPECT_EQ(kSendPayloadType, header.payloadType); - - // Found the second frame's final packet, drop this and expect a - // retransmission. - if (header.markerBit && ++marker_bits_observed_ == kDroppedFrameNumber) { - retransmitted_timestamp_ = header.timestamp; - return DROP_PACKET; - } - - return SEND_PACKET; - } - - virtual void FrameCallback(I420VideoFrame* frame) OVERRIDE { - CriticalSectionScoped crit_(lock_.get()); - if (frame->timestamp() == retransmitted_timestamp_) { - EXPECT_TRUE(frame_retransmitted_); - observation_complete_->Set(); - } - } - - const uint32_t retransmission_ssrc_; - const int retransmission_payload_type_; - int marker_bits_observed_; - uint32_t retransmitted_timestamp_; - bool frame_retransmitted_; - } observer(retransmit_over_rtx); - - CreateCalls(Call::Config(observer.SendTransport()), - Call::Config(observer.ReceiveTransport())); - - observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver()); - - CreateTestConfigs(); - send_config_.rtp.nack.rtp_history_ms = - receive_config_.rtp.nack.rtp_history_ms = 1000; - if (retransmit_over_rtx) { - send_config_.rtp.rtx.ssrcs.push_back(kSendRtxSsrc); - send_config_.rtp.rtx.payload_type = kSendRtxPayloadType; - int payload_type = send_config_.codec.plType; - receive_config_.rtp.rtx[payload_type].ssrc = kSendRtxSsrc; - receive_config_.rtp.rtx[payload_type].payload_type = kSendRtxPayloadType; - } - receive_config_.pre_render_callback = &observer; - - CreateStreams(); - CreateFrameGenerator(); - StartSending(); - - EXPECT_EQ(kEventSignaled, observer.Wait()) - << "Timed out while waiting for retransmission to render."; - - StopSending(); - observer.StopSending(); - DestroyStreams(); -} - -TEST_F(CallTest, DecodesRetransmittedFrame) { - DecodesRetransmittedFrame(false); -} - -TEST_F(CallTest, DecodesRetransmittedFrameOverRtx) { - DecodesRetransmittedFrame(true); -} - -TEST_F(CallTest, UsesFrameCallbacks) { - static const int kWidth = 320; - static const int kHeight = 240; - - class Renderer : public VideoRenderer { - public: - Renderer() : event_(EventWrapper::Create()) {} - - virtual void RenderFrame(const I420VideoFrame& video_frame, - int /*time_to_render_ms*/) OVERRIDE { - EXPECT_EQ(0, *video_frame.buffer(kYPlane)) - << "Rendered frame should have zero luma which is applied by the " - "pre-render callback."; - event_->Set(); - } - - EventTypeWrapper Wait() { return event_->Wait(kDefaultTimeoutMs); } - scoped_ptr event_; - } renderer; - - class TestFrameCallback : public I420FrameCallback { - public: - TestFrameCallback(int expected_luma_byte, int next_luma_byte) - : event_(EventWrapper::Create()), - expected_luma_byte_(expected_luma_byte), - next_luma_byte_(next_luma_byte) {} - - EventTypeWrapper Wait() { return event_->Wait(kDefaultTimeoutMs); } - - private: - virtual void FrameCallback(I420VideoFrame* frame) { - EXPECT_EQ(kWidth, frame->width()) - << "Width not as expected, callback done before resize?"; - EXPECT_EQ(kHeight, frame->height()) - << "Height not as expected, callback done before resize?"; - - // Previous luma specified, observed luma should be fairly close. - if (expected_luma_byte_ != -1) { - EXPECT_NEAR(expected_luma_byte_, *frame->buffer(kYPlane), 10); - } - - memset(frame->buffer(kYPlane), - next_luma_byte_, - frame->allocated_size(kYPlane)); - - event_->Set(); - } - - scoped_ptr event_; - int expected_luma_byte_; - int next_luma_byte_; - }; - - TestFrameCallback pre_encode_callback(-1, 255); // Changes luma to 255. - TestFrameCallback pre_render_callback(255, 0); // Changes luma from 255 to 0. - - test::DirectTransport sender_transport, receiver_transport; - - CreateCalls(Call::Config(&sender_transport), - Call::Config(&receiver_transport)); - - sender_transport.SetReceiver(receiver_call_->Receiver()); - receiver_transport.SetReceiver(sender_call_->Receiver()); - - CreateTestConfigs(); - send_config_.encoder = NULL; - send_config_.codec = sender_call_->GetVideoCodecs()[0]; - send_config_.codec.width = kWidth; - send_config_.codec.height = kHeight; - send_config_.pre_encode_callback = &pre_encode_callback; - receive_config_.pre_render_callback = &pre_render_callback; - receive_config_.renderer = &renderer; - - CreateStreams(); - StartSending(); - - // Create frames that are smaller than the send width/height, this is done to - // check that the callbacks are done after processing video. - scoped_ptr frame_generator( - test::FrameGenerator::Create(kWidth / 2, kHeight / 2)); - send_stream_->Input()->SwapFrame(frame_generator->NextFrame()); - - EXPECT_EQ(kEventSignaled, pre_encode_callback.Wait()) - << "Timed out while waiting for pre-encode callback."; - EXPECT_EQ(kEventSignaled, pre_render_callback.Wait()) - << "Timed out while waiting for pre-render callback."; - EXPECT_EQ(kEventSignaled, renderer.Wait()) - << "Timed out while waiting for the frame to render."; - - StopSending(); - - sender_transport.StopSending(); - receiver_transport.StopSending(); - - DestroyStreams(); -} - -class PliObserver : public test::RtpRtcpObserver, public VideoRenderer { - static const int kInverseDropProbability = 16; - - public: - explicit PliObserver(bool nack_enabled) - : test::RtpRtcpObserver(kLongTimeoutMs), - nack_enabled_(nack_enabled), - highest_dropped_timestamp_(0), - frames_to_drop_(0), - received_pli_(false) {} - - virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { - RTPHeader header; - EXPECT_TRUE(parser_->Parse(packet, static_cast(length), &header)); - - // Drop all retransmitted packets to force a PLI. - if (header.timestamp <= highest_dropped_timestamp_) - return DROP_PACKET; - - if (frames_to_drop_ > 0) { - highest_dropped_timestamp_ = header.timestamp; - --frames_to_drop_; - return DROP_PACKET; - } - - return SEND_PACKET; - } - - virtual Action OnReceiveRtcp(const uint8_t* packet, size_t length) OVERRIDE { - RTCPUtility::RTCPParserV2 parser(packet, length, true); - EXPECT_TRUE(parser.IsValid()); - - for (RTCPUtility::RTCPPacketTypes packet_type = parser.Begin(); - packet_type != RTCPUtility::kRtcpNotValidCode; - packet_type = parser.Iterate()) { - if (!nack_enabled_) - EXPECT_NE(packet_type, RTCPUtility::kRtcpRtpfbNackCode); - - if (packet_type == RTCPUtility::kRtcpPsfbPliCode) { - received_pli_ = true; - break; - } - } - return SEND_PACKET; - } - - virtual void RenderFrame(const I420VideoFrame& video_frame, - int time_to_render_ms) OVERRIDE { - CriticalSectionScoped crit_(lock_.get()); - if (received_pli_ && video_frame.timestamp() > highest_dropped_timestamp_) { - observation_complete_->Set(); - } - if (!received_pli_) - frames_to_drop_ = kPacketsToDrop; - } - - private: - static const int kPacketsToDrop = 1; - - bool nack_enabled_; - uint32_t highest_dropped_timestamp_; - int frames_to_drop_; - bool received_pli_; -}; - -void CallTest::ReceivesPliAndRecovers(int rtp_history_ms) { - PliObserver observer(rtp_history_ms > 0); - - CreateCalls(Call::Config(observer.SendTransport()), - Call::Config(observer.ReceiveTransport())); - - observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver()); - - CreateTestConfigs(); - send_config_.rtp.nack.rtp_history_ms = rtp_history_ms; - receive_config_.rtp.nack.rtp_history_ms = rtp_history_ms; - receive_config_.renderer = &observer; - - CreateStreams(); - CreateFrameGenerator(); - StartSending(); - - // Wait() waits for an event triggered when Pli has been received and frames - // have been rendered afterwards. - EXPECT_EQ(kEventSignaled, observer.Wait()); - - StopSending(); - - observer.StopSending(); - - DestroyStreams(); -} - -TEST_F(CallTest, ReceivesPliAndRecoversWithNack) { - ReceivesPliAndRecovers(1000); -} - -// TODO(pbos): Enable this when 2250 is resolved. -TEST_F(CallTest, DISABLED_ReceivesPliAndRecoversWithoutNack) { - ReceivesPliAndRecovers(0); -} - -TEST_F(CallTest, SurvivesIncomingRtpPacketsToDestroyedReceiveStream) { - class PacketInputObserver : public PacketReceiver { - public: - explicit PacketInputObserver(PacketReceiver* receiver) - : receiver_(receiver), delivered_packet_(EventWrapper::Create()) {} - - EventTypeWrapper Wait() { - return delivered_packet_->Wait(kDefaultTimeoutMs); - } - - private: - virtual bool DeliverPacket(const uint8_t* packet, size_t length) { - if (RtpHeaderParser::IsRtcp(packet, static_cast(length))) { - return receiver_->DeliverPacket(packet, length); - } else { - EXPECT_FALSE(receiver_->DeliverPacket(packet, length)); - delivered_packet_->Set(); - return false; - } - } - - PacketReceiver* receiver_; - scoped_ptr delivered_packet_; - }; - - test::DirectTransport send_transport, receive_transport; - - CreateCalls(Call::Config(&send_transport), Call::Config(&receive_transport)); - PacketInputObserver input_observer(receiver_call_->Receiver()); - - send_transport.SetReceiver(&input_observer); - receive_transport.SetReceiver(sender_call_->Receiver()); - - CreateTestConfigs(); - - CreateStreams(); - CreateFrameGenerator(); - StartSending(); - - receiver_call_->DestroyVideoReceiveStream(receive_stream_); - receive_stream_ = NULL; - - // Wait() waits for a received packet. - EXPECT_EQ(kEventSignaled, input_observer.Wait()); - - StopSending(); - - DestroyStreams(); - - send_transport.StopSending(); - receive_transport.StopSending(); -} - -void CallTest::RespectsRtcpMode(newapi::RtcpMode rtcp_mode) { - static const int kRtpHistoryMs = 1000; - static const int kNumCompoundRtcpPacketsToObserve = 10; - class RtcpModeObserver : public test::RtpRtcpObserver { - public: - explicit RtcpModeObserver(newapi::RtcpMode rtcp_mode) - : test::RtpRtcpObserver(kDefaultTimeoutMs), - rtcp_mode_(rtcp_mode), - sent_rtp_(0), - sent_rtcp_(0) {} - - private: - virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { - if (++sent_rtp_ % 3 == 0) - return DROP_PACKET; - - return SEND_PACKET; - } - - virtual Action OnReceiveRtcp(const uint8_t* packet, - size_t length) OVERRIDE { - ++sent_rtcp_; - RTCPUtility::RTCPParserV2 parser(packet, length, true); - EXPECT_TRUE(parser.IsValid()); - - RTCPUtility::RTCPPacketTypes packet_type = parser.Begin(); - bool has_report_block = false; - while (packet_type != RTCPUtility::kRtcpNotValidCode) { - EXPECT_NE(RTCPUtility::kRtcpSrCode, packet_type); - if (packet_type == RTCPUtility::kRtcpRrCode) { - has_report_block = true; - break; - } - packet_type = parser.Iterate(); - } - - switch (rtcp_mode_) { - case newapi::kRtcpCompound: - if (!has_report_block) { - ADD_FAILURE() << "Received RTCP packet without receiver report for " - "kRtcpCompound."; - observation_complete_->Set(); - } - - if (sent_rtcp_ >= kNumCompoundRtcpPacketsToObserve) - observation_complete_->Set(); - - break; - case newapi::kRtcpReducedSize: - if (!has_report_block) - observation_complete_->Set(); - break; - } - - return SEND_PACKET; - } - - newapi::RtcpMode rtcp_mode_; - int sent_rtp_; - int sent_rtcp_; - } observer(rtcp_mode); - - CreateCalls(Call::Config(observer.SendTransport()), - Call::Config(observer.ReceiveTransport())); - - observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver()); - - CreateTestConfigs(); - send_config_.rtp.nack.rtp_history_ms = kRtpHistoryMs; - receive_config_.rtp.nack.rtp_history_ms = kRtpHistoryMs; - receive_config_.rtp.rtcp_mode = rtcp_mode; - - CreateStreams(); - CreateFrameGenerator(); - StartSending(); - - EXPECT_EQ(kEventSignaled, observer.Wait()) - << (rtcp_mode == newapi::kRtcpCompound - ? "Timed out before observing enough compound packets." - : "Timed out before receiving a non-compound RTCP packet."); - - StopSending(); - observer.StopSending(); - DestroyStreams(); -} - -TEST_F(CallTest, UsesRtcpCompoundMode) { - RespectsRtcpMode(newapi::kRtcpCompound); -} - -TEST_F(CallTest, UsesRtcpReducedSizeMode) { - RespectsRtcpMode(newapi::kRtcpReducedSize); -} - -// Test sets up a Call multiple senders with different resolutions and SSRCs. -// Another is set up to receive all three of these with different renderers. -// Each renderer verifies that it receives the expected resolution, and as soon -// as every renderer has received a frame, the test finishes. -TEST_F(CallTest, SendsAndReceivesMultipleStreams) { - static const size_t kNumStreams = 3; - - class VideoOutputObserver : public VideoRenderer { - public: - VideoOutputObserver(test::FrameGeneratorCapturer** capturer, - int width, - int height) - : capturer_(capturer), - width_(width), - height_(height), - done_(EventWrapper::Create()) {} - - virtual void RenderFrame(const I420VideoFrame& video_frame, - int time_to_render_ms) OVERRIDE { - EXPECT_EQ(width_, video_frame.width()); - EXPECT_EQ(height_, video_frame.height()); - (*capturer_)->Stop(); - done_->Set(); - } - - void Wait() { done_->Wait(kDefaultTimeoutMs); } - - private: - test::FrameGeneratorCapturer** capturer_; - int width_; - int height_; - scoped_ptr done_; - }; - - struct { - uint32_t ssrc; - int width; - int height; - } codec_settings[kNumStreams] = {{1, 640, 480}, {2, 320, 240}, {3, 240, 160}}; - - test::DirectTransport sender_transport, receiver_transport; - scoped_ptr sender_call(Call::Create(Call::Config(&sender_transport))); - scoped_ptr receiver_call( - Call::Create(Call::Config(&receiver_transport))); - sender_transport.SetReceiver(receiver_call->Receiver()); - receiver_transport.SetReceiver(sender_call->Receiver()); - - VideoSendStream* send_streams[kNumStreams]; - VideoReceiveStream* receive_streams[kNumStreams]; - - VideoOutputObserver* observers[kNumStreams]; - test::FrameGeneratorCapturer* frame_generators[kNumStreams]; - - for (size_t i = 0; i < kNumStreams; ++i) { - uint32_t ssrc = codec_settings[i].ssrc; - int width = codec_settings[i].width; - int height = codec_settings[i].height; - observers[i] = new VideoOutputObserver(&frame_generators[i], width, height); - - VideoReceiveStream::Config receive_config = - receiver_call->GetDefaultReceiveConfig(); - receive_config.renderer = observers[i]; - receive_config.rtp.remote_ssrc = ssrc; - receive_config.rtp.local_ssrc = kReceiverLocalSsrc; - receive_streams[i] = - receiver_call->CreateVideoReceiveStream(receive_config); - receive_streams[i]->StartReceiving(); - - VideoSendStream::Config send_config = sender_call->GetDefaultSendConfig(); - send_config.rtp.ssrcs.push_back(ssrc); - send_config.codec.width = width; - send_config.codec.height = height; - send_streams[i] = sender_call->CreateVideoSendStream(send_config); - send_streams[i]->StartSending(); - - frame_generators[i] = test::FrameGeneratorCapturer::Create( - send_streams[i]->Input(), width, height, 30, Clock::GetRealTimeClock()); - frame_generators[i]->Start(); - } - - for (size_t i = 0; i < kNumStreams; ++i) { - observers[i]->Wait(); - } - - for (size_t i = 0; i < kNumStreams; ++i) { - frame_generators[i]->Stop(); - sender_call->DestroyVideoSendStream(send_streams[i]); - receiver_call->DestroyVideoReceiveStream(receive_streams[i]); - delete frame_generators[i]; - delete observers[i]; - } - - sender_transport.StopSending(); - receiver_transport.StopSending(); -}; - -TEST_F(CallTest, ObserversEncodedFrames) { - class EncodedFrameTestObserver : public EncodedFrameObserver { - public: - EncodedFrameTestObserver() - : length_(0), - frame_type_(kFrameEmpty), - called_(EventWrapper::Create()) {} - virtual ~EncodedFrameTestObserver() {} - - virtual void EncodedFrameCallback(const EncodedFrame& encoded_frame) { - frame_type_ = encoded_frame.frame_type_; - length_ = encoded_frame.length_; - buffer_.reset(new uint8_t[length_]); - memcpy(buffer_.get(), encoded_frame.data_, length_); - called_->Set(); - } - - EventTypeWrapper Wait() { return called_->Wait(kDefaultTimeoutMs); } - - void ExpectEqualFrames(const EncodedFrameTestObserver& observer) { - ASSERT_EQ(length_, observer.length_) - << "Observed frames are of different lengths."; - EXPECT_EQ(frame_type_, observer.frame_type_) - << "Observed frames have different frame types."; - EXPECT_EQ(0, memcmp(buffer_.get(), observer.buffer_.get(), length_)) - << "Observed encoded frames have different content."; - } - - private: - scoped_ptr buffer_; - size_t length_; - FrameType frame_type_; - scoped_ptr called_; - }; - - EncodedFrameTestObserver post_encode_observer; - EncodedFrameTestObserver pre_decode_observer; - - test::DirectTransport sender_transport, receiver_transport; - - CreateCalls(Call::Config(&sender_transport), - Call::Config(&receiver_transport)); - - sender_transport.SetReceiver(receiver_call_->Receiver()); - receiver_transport.SetReceiver(sender_call_->Receiver()); - - CreateTestConfigs(); - send_config_.post_encode_callback = &post_encode_observer; - receive_config_.pre_decode_callback = &pre_decode_observer; - - CreateStreams(); - StartSending(); - - scoped_ptr frame_generator(test::FrameGenerator::Create( - send_config_.codec.width, send_config_.codec.height)); - send_stream_->Input()->SwapFrame(frame_generator->NextFrame()); - - EXPECT_EQ(kEventSignaled, post_encode_observer.Wait()) - << "Timed out while waiting for send-side encoded-frame callback."; - - EXPECT_EQ(kEventSignaled, pre_decode_observer.Wait()) - << "Timed out while waiting for pre-decode encoded-frame callback."; - - post_encode_observer.ExpectEqualFrames(pre_decode_observer); - - StopSending(); - - sender_transport.StopSending(); - receiver_transport.StopSending(); - - DestroyStreams(); -} - -TEST_F(CallTest, ReceiveStreamSendsRemb) { - class RembObserver : public test::RtpRtcpObserver { - public: - RembObserver() : test::RtpRtcpObserver(kDefaultTimeoutMs) {} - - virtual Action OnReceiveRtcp(const uint8_t* packet, - size_t length) OVERRIDE { - RTCPUtility::RTCPParserV2 parser(packet, length, true); - EXPECT_TRUE(parser.IsValid()); - - bool received_psfb = false; - bool received_remb = false; - RTCPUtility::RTCPPacketTypes packet_type = parser.Begin(); - while (packet_type != RTCPUtility::kRtcpNotValidCode) { - if (packet_type == RTCPUtility::kRtcpPsfbRembCode) { - const RTCPUtility::RTCPPacket& packet = parser.Packet(); - EXPECT_EQ(packet.PSFBAPP.SenderSSRC, kReceiverLocalSsrc); - received_psfb = true; - } else if (packet_type == RTCPUtility::kRtcpPsfbRembItemCode) { - const RTCPUtility::RTCPPacket& packet = parser.Packet(); - EXPECT_GT(packet.REMBItem.BitRate, 0u); - EXPECT_EQ(packet.REMBItem.NumberOfSSRCs, 1u); - EXPECT_EQ(packet.REMBItem.SSRCs[0], kSendSsrc); - received_remb = true; - } - packet_type = parser.Iterate(); - } - if (received_psfb && received_remb) - observation_complete_->Set(); - return SEND_PACKET; - } - } observer; - - CreateCalls(Call::Config(observer.SendTransport()), - Call::Config(observer.ReceiveTransport())); - observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver()); - CreateTestConfigs(); - CreateStreams(); - CreateFrameGenerator(); - StartSending(); - - EXPECT_EQ(kEventSignaled, observer.Wait()) - << "Timed out while waiting for a receiver RTCP REMB packet to be sent."; - - StopSending(); - observer.StopSending(); - DestroyStreams(); -} - -void CallTest::TestXrReceiverReferenceTimeReport(bool enable_rrtr) { - static const int kNumRtcpReportPacketsToObserve = 5; - class RtcpXrObserver : public test::RtpRtcpObserver { - public: - explicit RtcpXrObserver(bool enable_rrtr) - : test::RtpRtcpObserver(kDefaultTimeoutMs), - enable_rrtr_(enable_rrtr), - sent_rtcp_sr_(0), - sent_rtcp_rr_(0), - sent_rtcp_rrtr_(0), - sent_rtcp_dlrr_(0) {} - - private: - // Receive stream should send RR packets (and RRTR packets if enabled). - virtual Action OnReceiveRtcp(const uint8_t* packet, - size_t length) OVERRIDE { - RTCPUtility::RTCPParserV2 parser(packet, length, true); - EXPECT_TRUE(parser.IsValid()); - - RTCPUtility::RTCPPacketTypes packet_type = parser.Begin(); - while (packet_type != RTCPUtility::kRtcpNotValidCode) { - if (packet_type == RTCPUtility::kRtcpRrCode) { - ++sent_rtcp_rr_; - } else if (packet_type == - RTCPUtility::kRtcpXrReceiverReferenceTimeCode) { - ++sent_rtcp_rrtr_; - } - EXPECT_NE(packet_type, RTCPUtility::kRtcpSrCode); - EXPECT_NE(packet_type, RTCPUtility::kRtcpXrDlrrReportBlockItemCode); - packet_type = parser.Iterate(); - } - return SEND_PACKET; - } - // Send stream should send SR packets (and DLRR packets if enabled). - virtual Action OnSendRtcp(const uint8_t* packet, size_t length) { - RTCPUtility::RTCPParserV2 parser(packet, length, true); - EXPECT_TRUE(parser.IsValid()); - - RTCPUtility::RTCPPacketTypes packet_type = parser.Begin(); - while (packet_type != RTCPUtility::kRtcpNotValidCode) { - if (packet_type == RTCPUtility::kRtcpSrCode) { - ++sent_rtcp_sr_; - } else if (packet_type == RTCPUtility::kRtcpXrDlrrReportBlockItemCode) { - ++sent_rtcp_dlrr_; - } - EXPECT_NE(packet_type, RTCPUtility::kRtcpXrReceiverReferenceTimeCode); - packet_type = parser.Iterate(); - } - if (sent_rtcp_sr_ > kNumRtcpReportPacketsToObserve && - sent_rtcp_rr_ > kNumRtcpReportPacketsToObserve) { - if (enable_rrtr_) { - EXPECT_GT(sent_rtcp_rrtr_, 0); - EXPECT_GT(sent_rtcp_dlrr_, 0); - } else { - EXPECT_EQ(0, sent_rtcp_rrtr_); - EXPECT_EQ(0, sent_rtcp_dlrr_); - } - observation_complete_->Set(); - } - return SEND_PACKET; - } - bool enable_rrtr_; - int sent_rtcp_sr_; - int sent_rtcp_rr_; - int sent_rtcp_rrtr_; - int sent_rtcp_dlrr_; - } observer(enable_rrtr); - - CreateCalls(Call::Config(observer.SendTransport()), - Call::Config(observer.ReceiveTransport())); - observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver()); - - CreateTestConfigs(); - receive_config_.rtp.rtcp_mode = newapi::kRtcpReducedSize; - receive_config_.rtp.rtcp_xr.receiver_reference_time_report = enable_rrtr; - - CreateStreams(); - CreateFrameGenerator(); - StartSending(); - - EXPECT_EQ(kEventSignaled, observer.Wait()) - << "Timed out while waiting for RTCP SR/RR packets to be sent."; - - StopSending(); - observer.StopSending(); - DestroyStreams(); -} - -class StatsObserver : public test::RtpRtcpObserver, public I420FrameCallback { - public: - StatsObserver() - : test::RtpRtcpObserver(kLongTimeoutMs), - receive_stream_(NULL), - send_stream_(NULL), - expected_receive_ssrc_(), - expected_send_ssrcs_(), - check_stats_event_(EventWrapper::Create()) {} - - void SetExpectedReceiveSsrc(uint32_t ssrc) { expected_receive_ssrc_ = ssrc; } - - void SetExpectedSendSsrcs(const std::vector& ssrcs) { - for (std::vector::const_iterator it = ssrcs.begin(); - it != ssrcs.end(); - ++it) { - expected_send_ssrcs_.insert(*it); - } - } - - void SetExpectedCName(std::string cname) { expected_cname_ = cname; } - - void SetReceiveStream(VideoReceiveStream* stream) { - receive_stream_ = stream; - } - - void SetSendStream(VideoSendStream* stream) { send_stream_ = stream; } - - void WaitForFilledStats() { - Clock* clock = Clock::GetRealTimeClock(); - int64_t now = clock->TimeInMilliseconds(); - int64_t stop_time = now + kLongTimeoutMs; - bool receive_ok = false; - bool send_ok = false; - - while (now < stop_time) { - if (!receive_ok) - receive_ok = CheckReceiveStats(); - if (!send_ok) - send_ok = CheckSendStats(); - - if (receive_ok && send_ok) - return; - - int64_t time_until_timout_ = stop_time - now; - if (time_until_timout_ > 0) - check_stats_event_->Wait(time_until_timout_); - now = clock->TimeInMilliseconds(); - } - - ADD_FAILURE() << "Timed out waiting for filled stats."; - for (std::map::const_iterator it = - receive_stats_filled_.begin(); - it != receive_stats_filled_.end(); - ++it) { - if (!it->second) { - ADD_FAILURE() << "Missing receive stats: " << it->first; - } - } - - for (std::map::const_iterator it = - send_stats_filled_.begin(); - it != send_stats_filled_.end(); - ++it) { - if (!it->second) { - ADD_FAILURE() << "Missing send stats: " << it->first; - } - } - } - - private: - virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { - check_stats_event_->Set(); - return SEND_PACKET; - } - - virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE { - check_stats_event_->Set(); - return SEND_PACKET; - } - - virtual Action OnReceiveRtp(const uint8_t* packet, size_t length) OVERRIDE { - check_stats_event_->Set(); - return SEND_PACKET; - } - - virtual Action OnReceiveRtcp(const uint8_t* packet, size_t length) OVERRIDE { - check_stats_event_->Set(); - return SEND_PACKET; - } - - virtual void FrameCallback(I420VideoFrame* video_frame) OVERRIDE { - // Ensure that we have at least 5ms send side delay. - int64_t render_time = video_frame->render_time_ms(); - if (render_time > 0) - video_frame->set_render_time_ms(render_time - 5); - } - - bool CheckReceiveStats() { - assert(receive_stream_ != NULL); - VideoReceiveStream::Stats stats = receive_stream_->GetStats(); - EXPECT_EQ(expected_receive_ssrc_, stats.ssrc); - - // Make sure all fields have been populated. - - receive_stats_filled_["IncomingRate"] |= - stats.network_frame_rate != 0 || stats.bitrate_bps != 0; - - receive_stats_filled_["FrameCallback"] |= stats.decode_frame_rate != 0; - - receive_stats_filled_["FrameRendered"] |= stats.render_frame_rate != 0; - - receive_stats_filled_["StatisticsUpdated"] |= - stats.rtcp_stats.cumulative_lost != 0 || - stats.rtcp_stats.extended_max_sequence_number != 0 || - stats.rtcp_stats.fraction_lost != 0 || stats.rtcp_stats.jitter != 0; - - receive_stats_filled_["DataCountersUpdated"] |= - stats.rtp_stats.bytes != 0 || stats.rtp_stats.fec_packets != 0 || - stats.rtp_stats.header_bytes != 0 || stats.rtp_stats.packets != 0 || - stats.rtp_stats.padding_bytes != 0 || - stats.rtp_stats.retransmitted_packets != 0; - - receive_stats_filled_["CodecStats"] |= - stats.avg_delay_ms != 0 || stats.discarded_packets != 0 || - stats.key_frames != 0 || stats.delta_frames != 0; - - receive_stats_filled_["CName"] |= stats.c_name == expected_cname_; - - return AllStatsFilled(receive_stats_filled_); - } - - bool CheckSendStats() { - assert(send_stream_ != NULL); - VideoSendStream::Stats stats = send_stream_->GetStats(); - - send_stats_filled_["NumStreams"] |= - stats.substreams.size() == expected_send_ssrcs_.size(); - - send_stats_filled_["Delay"] |= - stats.avg_delay_ms != 0 || stats.max_delay_ms != 0; - - receive_stats_filled_["CName"] |= stats.c_name == expected_cname_; - - for (std::map::const_iterator it = - stats.substreams.begin(); - it != stats.substreams.end(); - ++it) { - EXPECT_TRUE(expected_send_ssrcs_.find(it->first) != - expected_send_ssrcs_.end()); - - send_stats_filled_[CompoundKey("IncomingRate", it->first)] |= - stats.input_frame_rate != 0; - - const StreamStats& stream_stats = it->second; - - send_stats_filled_[CompoundKey("StatisticsUpdated", it->first)] |= - stream_stats.rtcp_stats.cumulative_lost != 0 || - stream_stats.rtcp_stats.extended_max_sequence_number != 0 || - stream_stats.rtcp_stats.fraction_lost != 0; - - send_stats_filled_[CompoundKey("DataCountersUpdated", it->first)] |= - stream_stats.rtp_stats.fec_packets != 0 || - stream_stats.rtp_stats.padding_bytes != 0 || - stream_stats.rtp_stats.retransmitted_packets != 0 || - stream_stats.rtp_stats.packets != 0; - - send_stats_filled_[CompoundKey("BitrateStatisticsObserver", it->first)] |= - stream_stats.bitrate_bps != 0; - - send_stats_filled_[CompoundKey("FrameCountObserver", it->first)] |= - stream_stats.delta_frames != 0 || stream_stats.key_frames != 0; - - send_stats_filled_[CompoundKey("OutgoingRate", it->first)] |= - stats.encode_frame_rate != 0; - } - - return AllStatsFilled(send_stats_filled_); - } - - std::string CompoundKey(const char* name, uint32_t ssrc) { - std::ostringstream oss; - oss << name << "_" << ssrc; - return oss.str(); - } - - bool AllStatsFilled(const std::map& stats_map) { - for (std::map::const_iterator it = stats_map.begin(); - it != stats_map.end(); - ++it) { - if (!it->second) - return false; - } - return true; - } - - VideoReceiveStream* receive_stream_; - std::map receive_stats_filled_; - - VideoSendStream* send_stream_; - std::map send_stats_filled_; - - uint32_t expected_receive_ssrc_; - std::set expected_send_ssrcs_; - std::string expected_cname_; - - scoped_ptr check_stats_event_; -}; - -TEST_F(CallTest, GetStats) { - StatsObserver observer; - - CreateCalls(Call::Config(observer.SendTransport()), - Call::Config(observer.ReceiveTransport())); - - observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver()); - - CreateTestConfigs(); - send_config_.pre_encode_callback = &observer; // Used to inject delay. - send_config_.rtp.c_name = "SomeCName"; - - observer.SetExpectedReceiveSsrc(receive_config_.rtp.local_ssrc); - observer.SetExpectedSendSsrcs(send_config_.rtp.ssrcs); - observer.SetExpectedCName(send_config_.rtp.c_name); - - CreateStreams(); - observer.SetReceiveStream(receive_stream_); - observer.SetSendStream(send_stream_); - CreateFrameGenerator(); - StartSending(); - - observer.WaitForFilledStats(); - - StopSending(); - observer.StopSending(); - DestroyStreams(); -} - -TEST_F(CallTest, ReceiverReferenceTimeReportEnabled) { - TestXrReceiverReferenceTimeReport(true); -} - -TEST_F(CallTest, ReceiverReferenceTimeReportDisabled) { - TestXrReceiverReferenceTimeReport(false); -} -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/end_to_end_tests.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/end_to_end_tests.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/end_to_end_tests.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/end_to_end_tests.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,2224 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include + +#include +#include +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" + +#include "webrtc/call.h" +#include "webrtc/frame_callback.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" +#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h" +#include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h" +#include "webrtc/modules/video_coding/main/interface/video_coding_defines.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/event_wrapper.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/system_wrappers/interface/sleep.h" +#include "webrtc/test/call_test.h" +#include "webrtc/test/direct_transport.h" +#include "webrtc/test/encoder_settings.h" +#include "webrtc/test/fake_audio_device.h" +#include "webrtc/test/fake_decoder.h" +#include "webrtc/test/fake_encoder.h" +#include "webrtc/test/frame_generator.h" +#include "webrtc/test/frame_generator_capturer.h" +#include "webrtc/test/null_transport.h" +#include "webrtc/test/rtp_rtcp_observer.h" +#include "webrtc/test/testsupport/fileutils.h" +#include "webrtc/test/testsupport/gtest_disable.h" +#include "webrtc/test/testsupport/perf_test.h" +#include "webrtc/video/transport_adapter.h" +#include "webrtc/video_encoder.h" + +namespace webrtc { + +static const unsigned long kSilenceTimeoutMs = 2000; + +class EndToEndTest : public test::CallTest { + public: + EndToEndTest() {} + + virtual ~EndToEndTest() { + EXPECT_EQ(NULL, send_stream_); + EXPECT_TRUE(receive_streams_.empty()); + } + + protected: + class UnusedTransport : public newapi::Transport { + private: + virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE { + ADD_FAILURE() << "Unexpected RTP sent."; + return false; + } + + virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE { + ADD_FAILURE() << "Unexpected RTCP sent."; + return false; + } + }; + + void DecodesRetransmittedFrame(bool retransmit_over_rtx); + void ReceivesPliAndRecovers(int rtp_history_ms); + void RespectsRtcpMode(newapi::RtcpMode rtcp_mode); + void TestXrReceiverReferenceTimeReport(bool enable_rrtr); + void TestSendsSetSsrcs(size_t num_ssrcs, bool send_single_ssrc_first); + void TestRtpStatePreservation(bool use_rtx); +}; + +TEST_F(EndToEndTest, ReceiverCanBeStartedTwice) { + test::NullTransport transport; + CreateCalls(Call::Config(&transport), Call::Config(&transport)); + + CreateSendConfig(1); + CreateMatchingReceiveConfigs(); + + CreateStreams(); + + receive_streams_[0]->Start(); + receive_streams_[0]->Start(); + + DestroyStreams(); +} + +TEST_F(EndToEndTest, ReceiverCanBeStoppedTwice) { + test::NullTransport transport; + CreateCalls(Call::Config(&transport), Call::Config(&transport)); + + CreateSendConfig(1); + CreateMatchingReceiveConfigs(); + + CreateStreams(); + + receive_streams_[0]->Stop(); + receive_streams_[0]->Stop(); + + DestroyStreams(); +} + +TEST_F(EndToEndTest, RendersSingleDelayedFrame) { + static const int kWidth = 320; + static const int kHeight = 240; + // This constant is chosen to be higher than the timeout in the video_render + // module. This makes sure that frames aren't dropped if there are no other + // frames in the queue. + static const int kDelayRenderCallbackMs = 1000; + + class Renderer : public VideoRenderer { + public: + Renderer() : event_(EventWrapper::Create()) {} + + virtual void RenderFrame(const I420VideoFrame& video_frame, + int /*time_to_render_ms*/) OVERRIDE { + event_->Set(); + } + + EventTypeWrapper Wait() { return event_->Wait(kDefaultTimeoutMs); } + + scoped_ptr event_; + } renderer; + + class TestFrameCallback : public I420FrameCallback { + public: + TestFrameCallback() : event_(EventWrapper::Create()) {} + + EventTypeWrapper Wait() { return event_->Wait(kDefaultTimeoutMs); } + + private: + virtual void FrameCallback(I420VideoFrame* frame) OVERRIDE { + SleepMs(kDelayRenderCallbackMs); + event_->Set(); + } + + scoped_ptr event_; + }; + + test::DirectTransport sender_transport, receiver_transport; + + CreateCalls(Call::Config(&sender_transport), + Call::Config(&receiver_transport)); + + sender_transport.SetReceiver(receiver_call_->Receiver()); + receiver_transport.SetReceiver(sender_call_->Receiver()); + + CreateSendConfig(1); + CreateMatchingReceiveConfigs(); + + TestFrameCallback pre_render_callback; + receive_configs_[0].pre_render_callback = &pre_render_callback; + receive_configs_[0].renderer = &renderer; + + CreateStreams(); + Start(); + + // Create frames that are smaller than the send width/height, this is done to + // check that the callbacks are done after processing video. + scoped_ptr frame_generator( + test::FrameGenerator::Create(kWidth, kHeight)); + send_stream_->Input()->SwapFrame(frame_generator->NextFrame()); + EXPECT_EQ(kEventSignaled, pre_render_callback.Wait()) + << "Timed out while waiting for pre-render callback."; + EXPECT_EQ(kEventSignaled, renderer.Wait()) + << "Timed out while waiting for the frame to render."; + + Stop(); + + sender_transport.StopSending(); + receiver_transport.StopSending(); + + DestroyStreams(); +} + +TEST_F(EndToEndTest, TransmitsFirstFrame) { + class Renderer : public VideoRenderer { + public: + Renderer() : event_(EventWrapper::Create()) {} + + virtual void RenderFrame(const I420VideoFrame& video_frame, + int /*time_to_render_ms*/) OVERRIDE { + event_->Set(); + } + + EventTypeWrapper Wait() { return event_->Wait(kDefaultTimeoutMs); } + + scoped_ptr event_; + } renderer; + + test::DirectTransport sender_transport, receiver_transport; + + CreateCalls(Call::Config(&sender_transport), + Call::Config(&receiver_transport)); + + sender_transport.SetReceiver(receiver_call_->Receiver()); + receiver_transport.SetReceiver(sender_call_->Receiver()); + + CreateSendConfig(1); + CreateMatchingReceiveConfigs(); + receive_configs_[0].renderer = &renderer; + + CreateStreams(); + Start(); + + scoped_ptr frame_generator(test::FrameGenerator::Create( + encoder_config_.streams[0].width, encoder_config_.streams[0].height)); + send_stream_->Input()->SwapFrame(frame_generator->NextFrame()); + + EXPECT_EQ(kEventSignaled, renderer.Wait()) + << "Timed out while waiting for the frame to render."; + + Stop(); + + sender_transport.StopSending(); + receiver_transport.StopSending(); + + DestroyStreams(); +} + +TEST_F(EndToEndTest, SendsAndReceivesVP9) { + class VP9Observer : public test::EndToEndTest, public VideoRenderer { + public: + VP9Observer() + : EndToEndTest(2 * kDefaultTimeoutMs), + encoder_(VideoEncoder::Create(VideoEncoder::kVp9)), + decoder_(VP9Decoder::Create()), + frame_counter_(0) {} + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for enough frames to be decoded."; + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->encoder_settings.encoder = encoder_.get(); + send_config->encoder_settings.payload_name = "VP9"; + send_config->encoder_settings.payload_type = VCM_VP9_PAYLOAD_TYPE; + encoder_config->streams[0].min_bitrate_bps = 50000; + encoder_config->streams[0].target_bitrate_bps = + encoder_config->streams[0].max_bitrate_bps = 2000000; + + (*receive_configs)[0].renderer = this; + (*receive_configs)[0].decoders.resize(1); + (*receive_configs)[0].decoders[0].payload_type = + send_config->encoder_settings.payload_type; + (*receive_configs)[0].decoders[0].payload_name = + send_config->encoder_settings.payload_name; + (*receive_configs)[0].decoders[0].decoder = decoder_.get(); + } + + virtual void RenderFrame(const I420VideoFrame& video_frame, + int time_to_render_ms) OVERRIDE { + const int kRequiredFrames = 500; + if (++frame_counter_ == kRequiredFrames) + observation_complete_->Set(); + } + + private: + scoped_ptr encoder_; + scoped_ptr decoder_; + int frame_counter_; + } test; + + RunBaseTest(&test); +} + +TEST_F(EndToEndTest, SendsAndReceivesH264) { + class H264Observer : public test::EndToEndTest, public VideoRenderer { + public: + H264Observer() + : EndToEndTest(2 * kDefaultTimeoutMs), + fake_encoder_(Clock::GetRealTimeClock()), + frame_counter_(0) {} + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for enough frames to be decoded."; + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->encoder_settings.encoder = &fake_encoder_; + send_config->encoder_settings.payload_name = "H264"; + send_config->encoder_settings.payload_type = kFakeSendPayloadType; + encoder_config->streams[0].min_bitrate_bps = 50000; + encoder_config->streams[0].target_bitrate_bps = + encoder_config->streams[0].max_bitrate_bps = 2000000; + + (*receive_configs)[0].renderer = this; + (*receive_configs)[0].decoders.resize(1); + (*receive_configs)[0].decoders[0].payload_type = + send_config->encoder_settings.payload_type; + (*receive_configs)[0].decoders[0].payload_name = + send_config->encoder_settings.payload_name; + (*receive_configs)[0].decoders[0].decoder = &fake_decoder_; + } + + virtual void RenderFrame(const I420VideoFrame& video_frame, + int time_to_render_ms) OVERRIDE { + const int kRequiredFrames = 500; + if (++frame_counter_ == kRequiredFrames) + observation_complete_->Set(); + } + + private: + test::FakeH264Decoder fake_decoder_; + test::FakeH264Encoder fake_encoder_; + int frame_counter_; + } test; + + RunBaseTest(&test); +} + +TEST_F(EndToEndTest, ReceiverUsesLocalSsrc) { + class SyncRtcpObserver : public test::EndToEndTest { + public: + SyncRtcpObserver() : EndToEndTest(kDefaultTimeoutMs) {} + + virtual Action OnReceiveRtcp(const uint8_t* packet, + size_t length) OVERRIDE { + RTCPUtility::RTCPParserV2 parser(packet, length, true); + EXPECT_TRUE(parser.IsValid()); + uint32_t ssrc = 0; + ssrc |= static_cast(packet[4]) << 24; + ssrc |= static_cast(packet[5]) << 16; + ssrc |= static_cast(packet[6]) << 8; + ssrc |= static_cast(packet[7]) << 0; + EXPECT_EQ(kReceiverLocalSsrc, ssrc); + observation_complete_->Set(); + + return SEND_PACKET; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for a receiver RTCP packet to be sent."; + } + } test; + + RunBaseTest(&test); +} + +TEST_F(EndToEndTest, ReceivesAndRetransmitsNack) { + static const int kNumberOfNacksToObserve = 2; + static const int kLossBurstSize = 2; + static const int kPacketsBetweenLossBursts = 9; + class NackObserver : public test::EndToEndTest { + public: + NackObserver() + : EndToEndTest(kLongTimeoutMs), + rtp_parser_(RtpHeaderParser::Create()), + sent_rtp_packets_(0), + packets_left_to_drop_(0), + nacks_left_(kNumberOfNacksToObserve) {} + + private: + virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { + RTPHeader header; + EXPECT_TRUE(rtp_parser_->Parse(packet, length, &header)); + + // Never drop retransmitted packets. + if (dropped_packets_.find(header.sequenceNumber) != + dropped_packets_.end()) { + retransmitted_packets_.insert(header.sequenceNumber); + if (nacks_left_ == 0 && + retransmitted_packets_.size() == dropped_packets_.size()) { + observation_complete_->Set(); + } + return SEND_PACKET; + } + + ++sent_rtp_packets_; + + // Enough NACKs received, stop dropping packets. + if (nacks_left_ == 0) + return SEND_PACKET; + + // Check if it's time for a new loss burst. + if (sent_rtp_packets_ % kPacketsBetweenLossBursts == 0) + packets_left_to_drop_ = kLossBurstSize; + + if (packets_left_to_drop_ > 0) { + --packets_left_to_drop_; + dropped_packets_.insert(header.sequenceNumber); + return DROP_PACKET; + } + + return SEND_PACKET; + } + + virtual Action OnReceiveRtcp(const uint8_t* packet, + size_t length) OVERRIDE { + RTCPUtility::RTCPParserV2 parser(packet, length, true); + EXPECT_TRUE(parser.IsValid()); + + RTCPUtility::RTCPPacketTypes packet_type = parser.Begin(); + while (packet_type != RTCPUtility::kRtcpNotValidCode) { + if (packet_type == RTCPUtility::kRtcpRtpfbNackCode) { + --nacks_left_; + break; + } + packet_type = parser.Iterate(); + } + return SEND_PACKET; + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs; + (*receive_configs)[0].rtp.nack.rtp_history_ms = kNackRtpHistoryMs; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out waiting for packets to be NACKed, retransmitted and " + "rendered."; + } + + scoped_ptr rtp_parser_; + std::set dropped_packets_; + std::set retransmitted_packets_; + uint64_t sent_rtp_packets_; + int packets_left_to_drop_; + int nacks_left_; + } test; + + RunBaseTest(&test); +} + +// TODO(pbos): Flaky, webrtc:3269 +TEST_F(EndToEndTest, DISABLED_CanReceiveFec) { + class FecRenderObserver : public test::EndToEndTest, public VideoRenderer { + public: + FecRenderObserver() + : EndToEndTest(kDefaultTimeoutMs), + state_(kFirstPacket), + protected_sequence_number_(0), + protected_frame_timestamp_(0) {} + + private: + virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE + EXCLUSIVE_LOCKS_REQUIRED(crit_) { + RTPHeader header; + EXPECT_TRUE(parser_->Parse(packet, length, &header)); + + EXPECT_EQ(kRedPayloadType, header.payloadType); + int encapsulated_payload_type = + static_cast(packet[header.headerLength]); + if (encapsulated_payload_type != kFakeSendPayloadType) + EXPECT_EQ(kUlpfecPayloadType, encapsulated_payload_type); + + switch (state_) { + case kFirstPacket: + state_ = kDropEveryOtherPacketUntilFec; + break; + case kDropEveryOtherPacketUntilFec: + if (encapsulated_payload_type == kUlpfecPayloadType) { + state_ = kDropNextMediaPacket; + return SEND_PACKET; + } + if (header.sequenceNumber % 2 == 0) + return DROP_PACKET; + break; + case kDropNextMediaPacket: + if (encapsulated_payload_type == kFakeSendPayloadType) { + protected_sequence_number_ = header.sequenceNumber; + protected_frame_timestamp_ = header.timestamp; + state_ = kProtectedPacketDropped; + return DROP_PACKET; + } + break; + case kProtectedPacketDropped: + EXPECT_NE(header.sequenceNumber, protected_sequence_number_) + << "Protected packet retransmitted. Should not happen with FEC."; + break; + } + + return SEND_PACKET; + } + + virtual void RenderFrame(const I420VideoFrame& video_frame, + int time_to_render_ms) OVERRIDE { + CriticalSectionScoped lock(crit_.get()); + // Rendering frame with timestamp associated with dropped packet -> FEC + // protection worked. + if (state_ == kProtectedPacketDropped && + video_frame.timestamp() == protected_frame_timestamp_) { + observation_complete_->Set(); + } + } + + enum { + kFirstPacket, + kDropEveryOtherPacketUntilFec, + kDropNextMediaPacket, + kProtectedPacketDropped, + } state_; + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + // TODO(pbos): Run this test with combined NACK/FEC enabled as well. + // int rtp_history_ms = 1000; + // (*receive_configs)[0].rtp.nack.rtp_history_ms = rtp_history_ms; + // send_config->rtp.nack.rtp_history_ms = rtp_history_ms; + send_config->rtp.fec.red_payload_type = kRedPayloadType; + send_config->rtp.fec.ulpfec_payload_type = kUlpfecPayloadType; + + (*receive_configs)[0].rtp.fec.red_payload_type = kRedPayloadType; + (*receive_configs)[0].rtp.fec.ulpfec_payload_type = kUlpfecPayloadType; + (*receive_configs)[0].renderer = this; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for retransmitted NACKed frames to be " + "rendered again."; + } + + uint32_t protected_sequence_number_ GUARDED_BY(crit_); + uint32_t protected_frame_timestamp_ GUARDED_BY(crit_); + } test; + + RunBaseTest(&test); +} + +// This test drops second RTP packet with a marker bit set, makes sure it's +// retransmitted and renders. Retransmission SSRCs are also checked. +void EndToEndTest::DecodesRetransmittedFrame(bool retransmit_over_rtx) { + static const int kDroppedFrameNumber = 2; + class RetransmissionObserver : public test::EndToEndTest, + public I420FrameCallback { + public: + explicit RetransmissionObserver(bool expect_rtx) + : EndToEndTest(kDefaultTimeoutMs), + retransmission_ssrc_(expect_rtx ? kSendRtxSsrcs[0] : kSendSsrcs[0]), + retransmission_payload_type_(expect_rtx ? kSendRtxPayloadType + : kFakeSendPayloadType), + marker_bits_observed_(0), + retransmitted_timestamp_(0), + frame_retransmitted_(false) {} + + private: + virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { + RTPHeader header; + EXPECT_TRUE(parser_->Parse(packet, length, &header)); + + if (header.timestamp == retransmitted_timestamp_) { + EXPECT_EQ(retransmission_ssrc_, header.ssrc); + EXPECT_EQ(retransmission_payload_type_, header.payloadType); + frame_retransmitted_ = true; + return SEND_PACKET; + } + + EXPECT_EQ(kSendSsrcs[0], header.ssrc); + EXPECT_EQ(kFakeSendPayloadType, header.payloadType); + + // Found the second frame's final packet, drop this and expect a + // retransmission. + if (header.markerBit && ++marker_bits_observed_ == kDroppedFrameNumber) { + retransmitted_timestamp_ = header.timestamp; + return DROP_PACKET; + } + + return SEND_PACKET; + } + + virtual void FrameCallback(I420VideoFrame* frame) OVERRIDE { + CriticalSectionScoped lock(crit_.get()); + if (frame->timestamp() == retransmitted_timestamp_) { + EXPECT_TRUE(frame_retransmitted_); + observation_complete_->Set(); + } + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs; + (*receive_configs)[0].pre_render_callback = this; + (*receive_configs)[0].rtp.nack.rtp_history_ms = kNackRtpHistoryMs; + if (retransmission_ssrc_ == kSendRtxSsrcs[0]) { + send_config->rtp.rtx.ssrcs.push_back(kSendRtxSsrcs[0]); + send_config->rtp.rtx.payload_type = kSendRtxPayloadType; + (*receive_configs)[0].rtp.rtx[kSendRtxPayloadType].ssrc = + kSendRtxSsrcs[0]; + (*receive_configs)[0].rtp.rtx[kSendRtxPayloadType].payload_type = + kSendRtxPayloadType; + } + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for retransmission to render."; + } + + const uint32_t retransmission_ssrc_; + const int retransmission_payload_type_; + int marker_bits_observed_; + uint32_t retransmitted_timestamp_; + bool frame_retransmitted_; + } test(retransmit_over_rtx); + + RunBaseTest(&test); +} + +TEST_F(EndToEndTest, DecodesRetransmittedFrame) { + DecodesRetransmittedFrame(false); +} + +TEST_F(EndToEndTest, DecodesRetransmittedFrameOverRtx) { + DecodesRetransmittedFrame(true); +} + +TEST_F(EndToEndTest, UsesFrameCallbacks) { + static const int kWidth = 320; + static const int kHeight = 240; + + class Renderer : public VideoRenderer { + public: + Renderer() : event_(EventWrapper::Create()) {} + + virtual void RenderFrame(const I420VideoFrame& video_frame, + int /*time_to_render_ms*/) OVERRIDE { + EXPECT_EQ(0, *video_frame.buffer(kYPlane)) + << "Rendered frame should have zero luma which is applied by the " + "pre-render callback."; + event_->Set(); + } + + EventTypeWrapper Wait() { return event_->Wait(kDefaultTimeoutMs); } + scoped_ptr event_; + } renderer; + + class TestFrameCallback : public I420FrameCallback { + public: + TestFrameCallback(int expected_luma_byte, int next_luma_byte) + : event_(EventWrapper::Create()), + expected_luma_byte_(expected_luma_byte), + next_luma_byte_(next_luma_byte) {} + + EventTypeWrapper Wait() { return event_->Wait(kDefaultTimeoutMs); } + + private: + virtual void FrameCallback(I420VideoFrame* frame) { + EXPECT_EQ(kWidth, frame->width()) + << "Width not as expected, callback done before resize?"; + EXPECT_EQ(kHeight, frame->height()) + << "Height not as expected, callback done before resize?"; + + // Previous luma specified, observed luma should be fairly close. + if (expected_luma_byte_ != -1) { + EXPECT_NEAR(expected_luma_byte_, *frame->buffer(kYPlane), 10); + } + + memset(frame->buffer(kYPlane), + next_luma_byte_, + frame->allocated_size(kYPlane)); + + event_->Set(); + } + + scoped_ptr event_; + int expected_luma_byte_; + int next_luma_byte_; + }; + + TestFrameCallback pre_encode_callback(-1, 255); // Changes luma to 255. + TestFrameCallback pre_render_callback(255, 0); // Changes luma from 255 to 0. + + test::DirectTransport sender_transport, receiver_transport; + + CreateCalls(Call::Config(&sender_transport), + Call::Config(&receiver_transport)); + + sender_transport.SetReceiver(receiver_call_->Receiver()); + receiver_transport.SetReceiver(sender_call_->Receiver()); + + CreateSendConfig(1); + scoped_ptr encoder( + VideoEncoder::Create(VideoEncoder::kVp8)); + send_config_.encoder_settings.encoder = encoder.get(); + send_config_.encoder_settings.payload_name = "VP8"; + ASSERT_EQ(1u, encoder_config_.streams.size()) << "Test setup error."; + encoder_config_.streams[0].width = kWidth; + encoder_config_.streams[0].height = kHeight; + send_config_.pre_encode_callback = &pre_encode_callback; + + CreateMatchingReceiveConfigs(); + receive_configs_[0].pre_render_callback = &pre_render_callback; + receive_configs_[0].renderer = &renderer; + + CreateStreams(); + Start(); + + // Create frames that are smaller than the send width/height, this is done to + // check that the callbacks are done after processing video. + scoped_ptr frame_generator( + test::FrameGenerator::Create(kWidth / 2, kHeight / 2)); + send_stream_->Input()->SwapFrame(frame_generator->NextFrame()); + + EXPECT_EQ(kEventSignaled, pre_encode_callback.Wait()) + << "Timed out while waiting for pre-encode callback."; + EXPECT_EQ(kEventSignaled, pre_render_callback.Wait()) + << "Timed out while waiting for pre-render callback."; + EXPECT_EQ(kEventSignaled, renderer.Wait()) + << "Timed out while waiting for the frame to render."; + + Stop(); + + sender_transport.StopSending(); + receiver_transport.StopSending(); + + DestroyStreams(); +} + +void EndToEndTest::ReceivesPliAndRecovers(int rtp_history_ms) { + static const int kPacketsToDrop = 1; + + class PliObserver : public test::EndToEndTest, public VideoRenderer { + public: + explicit PliObserver(int rtp_history_ms) + : EndToEndTest(kLongTimeoutMs), + rtp_history_ms_(rtp_history_ms), + nack_enabled_(rtp_history_ms > 0), + highest_dropped_timestamp_(0), + frames_to_drop_(0), + received_pli_(false) {} + + private: + virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { + RTPHeader header; + EXPECT_TRUE(parser_->Parse(packet, length, &header)); + + // Drop all retransmitted packets to force a PLI. + if (header.timestamp <= highest_dropped_timestamp_) + return DROP_PACKET; + + if (frames_to_drop_ > 0) { + highest_dropped_timestamp_ = header.timestamp; + --frames_to_drop_; + return DROP_PACKET; + } + + return SEND_PACKET; + } + + virtual Action OnReceiveRtcp(const uint8_t* packet, + size_t length) OVERRIDE { + RTCPUtility::RTCPParserV2 parser(packet, length, true); + EXPECT_TRUE(parser.IsValid()); + + for (RTCPUtility::RTCPPacketTypes packet_type = parser.Begin(); + packet_type != RTCPUtility::kRtcpNotValidCode; + packet_type = parser.Iterate()) { + if (!nack_enabled_) + EXPECT_NE(packet_type, RTCPUtility::kRtcpRtpfbNackCode); + + if (packet_type == RTCPUtility::kRtcpPsfbPliCode) { + received_pli_ = true; + break; + } + } + return SEND_PACKET; + } + + virtual void RenderFrame(const I420VideoFrame& video_frame, + int time_to_render_ms) OVERRIDE { + CriticalSectionScoped lock(crit_.get()); + if (received_pli_ && + video_frame.timestamp() > highest_dropped_timestamp_) { + observation_complete_->Set(); + } + if (!received_pli_) + frames_to_drop_ = kPacketsToDrop; + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->rtp.nack.rtp_history_ms = rtp_history_ms_; + (*receive_configs)[0].rtp.nack.rtp_history_ms = rtp_history_ms_; + (*receive_configs)[0].renderer = this; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) << "Timed out waiting for PLI to be " + "received and a frame to be " + "rendered afterwards."; + } + + int rtp_history_ms_; + bool nack_enabled_; + uint32_t highest_dropped_timestamp_; + int frames_to_drop_; + bool received_pli_; + } test(rtp_history_ms); + + RunBaseTest(&test); +} + +TEST_F(EndToEndTest, ReceivesPliAndRecoversWithNack) { + ReceivesPliAndRecovers(1000); +} + +// TODO(pbos): Enable this when 2250 is resolved. +TEST_F(EndToEndTest, DISABLED_ReceivesPliAndRecoversWithoutNack) { + ReceivesPliAndRecovers(0); +} + +TEST_F(EndToEndTest, UnknownRtpPacketGivesUnknownSsrcReturnCode) { + class PacketInputObserver : public PacketReceiver { + public: + explicit PacketInputObserver(PacketReceiver* receiver) + : receiver_(receiver), delivered_packet_(EventWrapper::Create()) {} + + EventTypeWrapper Wait() { + return delivered_packet_->Wait(kDefaultTimeoutMs); + } + + private: + virtual DeliveryStatus DeliverPacket(const uint8_t* packet, + size_t length) OVERRIDE { + if (RtpHeaderParser::IsRtcp(packet, length)) { + return receiver_->DeliverPacket(packet, length); + } else { + DeliveryStatus delivery_status = + receiver_->DeliverPacket(packet, length); + EXPECT_EQ(DELIVERY_UNKNOWN_SSRC, delivery_status); + delivered_packet_->Set(); + return delivery_status; + } + } + + PacketReceiver* receiver_; + scoped_ptr delivered_packet_; + }; + + test::DirectTransport send_transport, receive_transport; + + CreateCalls(Call::Config(&send_transport), Call::Config(&receive_transport)); + PacketInputObserver input_observer(receiver_call_->Receiver()); + + send_transport.SetReceiver(&input_observer); + receive_transport.SetReceiver(sender_call_->Receiver()); + + CreateSendConfig(1); + CreateMatchingReceiveConfigs(); + + CreateStreams(); + CreateFrameGeneratorCapturer(); + Start(); + + receiver_call_->DestroyVideoReceiveStream(receive_streams_[0]); + receive_streams_.clear(); + + // Wait() waits for a received packet. + EXPECT_EQ(kEventSignaled, input_observer.Wait()); + + Stop(); + + DestroyStreams(); + + send_transport.StopSending(); + receive_transport.StopSending(); +} + +void EndToEndTest::RespectsRtcpMode(newapi::RtcpMode rtcp_mode) { + static const int kNumCompoundRtcpPacketsToObserve = 10; + class RtcpModeObserver : public test::EndToEndTest { + public: + explicit RtcpModeObserver(newapi::RtcpMode rtcp_mode) + : EndToEndTest(kDefaultTimeoutMs), + rtcp_mode_(rtcp_mode), + sent_rtp_(0), + sent_rtcp_(0) {} + + private: + virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { + if (++sent_rtp_ % 3 == 0) + return DROP_PACKET; + + return SEND_PACKET; + } + + virtual Action OnReceiveRtcp(const uint8_t* packet, + size_t length) OVERRIDE { + ++sent_rtcp_; + RTCPUtility::RTCPParserV2 parser(packet, length, true); + EXPECT_TRUE(parser.IsValid()); + + RTCPUtility::RTCPPacketTypes packet_type = parser.Begin(); + bool has_report_block = false; + while (packet_type != RTCPUtility::kRtcpNotValidCode) { + EXPECT_NE(RTCPUtility::kRtcpSrCode, packet_type); + if (packet_type == RTCPUtility::kRtcpRrCode) { + has_report_block = true; + break; + } + packet_type = parser.Iterate(); + } + + switch (rtcp_mode_) { + case newapi::kRtcpCompound: + if (!has_report_block) { + ADD_FAILURE() << "Received RTCP packet without receiver report for " + "kRtcpCompound."; + observation_complete_->Set(); + } + + if (sent_rtcp_ >= kNumCompoundRtcpPacketsToObserve) + observation_complete_->Set(); + + break; + case newapi::kRtcpReducedSize: + if (!has_report_block) + observation_complete_->Set(); + break; + } + + return SEND_PACKET; + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs; + (*receive_configs)[0].rtp.nack.rtp_history_ms = kNackRtpHistoryMs; + (*receive_configs)[0].rtp.rtcp_mode = rtcp_mode_; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << (rtcp_mode_ == newapi::kRtcpCompound + ? "Timed out before observing enough compound packets." + : "Timed out before receiving a non-compound RTCP packet."); + } + + newapi::RtcpMode rtcp_mode_; + int sent_rtp_; + int sent_rtcp_; + } test(rtcp_mode); + + RunBaseTest(&test); +} + +TEST_F(EndToEndTest, UsesRtcpCompoundMode) { + RespectsRtcpMode(newapi::kRtcpCompound); +} + +TEST_F(EndToEndTest, UsesRtcpReducedSizeMode) { + RespectsRtcpMode(newapi::kRtcpReducedSize); +} + +// Test sets up a Call multiple senders with different resolutions and SSRCs. +// Another is set up to receive all three of these with different renderers. +// Each renderer verifies that it receives the expected resolution, and as soon +// as every renderer has received a frame, the test finishes. +TEST_F(EndToEndTest, SendsAndReceivesMultipleStreams) { + static const size_t kNumStreams = 3; + + class VideoOutputObserver : public VideoRenderer { + public: + VideoOutputObserver(test::FrameGeneratorCapturer** capturer, + int width, + int height) + : capturer_(capturer), + width_(width), + height_(height), + done_(EventWrapper::Create()) {} + + virtual void RenderFrame(const I420VideoFrame& video_frame, + int time_to_render_ms) OVERRIDE { + EXPECT_EQ(width_, video_frame.width()); + EXPECT_EQ(height_, video_frame.height()); + (*capturer_)->Stop(); + done_->Set(); + } + + EventTypeWrapper Wait() { return done_->Wait(kDefaultTimeoutMs); } + + private: + test::FrameGeneratorCapturer** capturer_; + int width_; + int height_; + scoped_ptr done_; + }; + + struct { + uint32_t ssrc; + int width; + int height; + } codec_settings[kNumStreams] = {{1, 640, 480}, {2, 320, 240}, {3, 240, 160}}; + + test::DirectTransport sender_transport, receiver_transport; + scoped_ptr sender_call(Call::Create(Call::Config(&sender_transport))); + scoped_ptr receiver_call( + Call::Create(Call::Config(&receiver_transport))); + sender_transport.SetReceiver(receiver_call->Receiver()); + receiver_transport.SetReceiver(sender_call->Receiver()); + + VideoSendStream* send_streams[kNumStreams]; + VideoReceiveStream* receive_streams[kNumStreams]; + + VideoOutputObserver* observers[kNumStreams]; + test::FrameGeneratorCapturer* frame_generators[kNumStreams]; + + scoped_ptr encoders[kNumStreams]; + for (size_t i = 0; i < kNumStreams; ++i) + encoders[i].reset(VideoEncoder::Create(VideoEncoder::kVp8)); + + ScopedVector allocated_decoders; + for (size_t i = 0; i < kNumStreams; ++i) { + uint32_t ssrc = codec_settings[i].ssrc; + int width = codec_settings[i].width; + int height = codec_settings[i].height; + observers[i] = new VideoOutputObserver(&frame_generators[i], width, height); + + VideoSendStream::Config send_config; + send_config.rtp.ssrcs.push_back(ssrc); + send_config.encoder_settings.encoder = encoders[i].get(); + send_config.encoder_settings.payload_name = "VP8"; + send_config.encoder_settings.payload_type = 124; + VideoEncoderConfig encoder_config; + encoder_config.streams = test::CreateVideoStreams(1); + VideoStream* stream = &encoder_config.streams[0]; + stream->width = width; + stream->height = height; + stream->max_framerate = 5; + stream->min_bitrate_bps = stream->target_bitrate_bps = + stream->max_bitrate_bps = 100000; + send_streams[i] = + sender_call->CreateVideoSendStream(send_config, encoder_config); + send_streams[i]->Start(); + + VideoReceiveStream::Config receive_config; + receive_config.renderer = observers[i]; + receive_config.rtp.remote_ssrc = ssrc; + receive_config.rtp.local_ssrc = kReceiverLocalSsrc; + VideoReceiveStream::Decoder decoder = + test::CreateMatchingDecoder(send_config.encoder_settings); + allocated_decoders.push_back(decoder.decoder); + receive_config.decoders.push_back(decoder); + receive_streams[i] = + receiver_call->CreateVideoReceiveStream(receive_config); + receive_streams[i]->Start(); + + frame_generators[i] = test::FrameGeneratorCapturer::Create( + send_streams[i]->Input(), width, height, 30, Clock::GetRealTimeClock()); + frame_generators[i]->Start(); + } + + for (size_t i = 0; i < kNumStreams; ++i) { + EXPECT_EQ(kEventSignaled, observers[i]->Wait()) + << "Timed out while waiting for observer " << i << " to render."; + } + + for (size_t i = 0; i < kNumStreams; ++i) { + frame_generators[i]->Stop(); + sender_call->DestroyVideoSendStream(send_streams[i]); + receiver_call->DestroyVideoReceiveStream(receive_streams[i]); + delete frame_generators[i]; + delete observers[i]; + } + + sender_transport.StopSending(); + receiver_transport.StopSending(); +} + +TEST_F(EndToEndTest, ObserversEncodedFrames) { + class EncodedFrameTestObserver : public EncodedFrameObserver { + public: + EncodedFrameTestObserver() + : length_(0), + frame_type_(kFrameEmpty), + called_(EventWrapper::Create()) {} + virtual ~EncodedFrameTestObserver() {} + + virtual void EncodedFrameCallback(const EncodedFrame& encoded_frame) { + frame_type_ = encoded_frame.frame_type_; + length_ = encoded_frame.length_; + buffer_.reset(new uint8_t[length_]); + memcpy(buffer_.get(), encoded_frame.data_, length_); + called_->Set(); + } + + EventTypeWrapper Wait() { return called_->Wait(kDefaultTimeoutMs); } + + void ExpectEqualFrames(const EncodedFrameTestObserver& observer) { + ASSERT_EQ(length_, observer.length_) + << "Observed frames are of different lengths."; + EXPECT_EQ(frame_type_, observer.frame_type_) + << "Observed frames have different frame types."; + EXPECT_EQ(0, memcmp(buffer_.get(), observer.buffer_.get(), length_)) + << "Observed encoded frames have different content."; + } + + private: + scoped_ptr buffer_; + size_t length_; + FrameType frame_type_; + scoped_ptr called_; + }; + + EncodedFrameTestObserver post_encode_observer; + EncodedFrameTestObserver pre_decode_observer; + + test::DirectTransport sender_transport, receiver_transport; + + CreateCalls(Call::Config(&sender_transport), + Call::Config(&receiver_transport)); + + sender_transport.SetReceiver(receiver_call_->Receiver()); + receiver_transport.SetReceiver(sender_call_->Receiver()); + + CreateSendConfig(1); + CreateMatchingReceiveConfigs(); + send_config_.post_encode_callback = &post_encode_observer; + receive_configs_[0].pre_decode_callback = &pre_decode_observer; + + CreateStreams(); + Start(); + + scoped_ptr frame_generator(test::FrameGenerator::Create( + encoder_config_.streams[0].width, encoder_config_.streams[0].height)); + send_stream_->Input()->SwapFrame(frame_generator->NextFrame()); + + EXPECT_EQ(kEventSignaled, post_encode_observer.Wait()) + << "Timed out while waiting for send-side encoded-frame callback."; + + EXPECT_EQ(kEventSignaled, pre_decode_observer.Wait()) + << "Timed out while waiting for pre-decode encoded-frame callback."; + + post_encode_observer.ExpectEqualFrames(pre_decode_observer); + + Stop(); + + sender_transport.StopSending(); + receiver_transport.StopSending(); + + DestroyStreams(); +} + +TEST_F(EndToEndTest, ReceiveStreamSendsRemb) { + class RembObserver : public test::EndToEndTest { + public: + RembObserver() : EndToEndTest(kDefaultTimeoutMs) {} + + virtual Action OnReceiveRtcp(const uint8_t* packet, + size_t length) OVERRIDE { + RTCPUtility::RTCPParserV2 parser(packet, length, true); + EXPECT_TRUE(parser.IsValid()); + + bool received_psfb = false; + bool received_remb = false; + RTCPUtility::RTCPPacketTypes packet_type = parser.Begin(); + while (packet_type != RTCPUtility::kRtcpNotValidCode) { + if (packet_type == RTCPUtility::kRtcpPsfbRembCode) { + const RTCPUtility::RTCPPacket& packet = parser.Packet(); + EXPECT_EQ(packet.PSFBAPP.SenderSSRC, kReceiverLocalSsrc); + received_psfb = true; + } else if (packet_type == RTCPUtility::kRtcpPsfbRembItemCode) { + const RTCPUtility::RTCPPacket& packet = parser.Packet(); + EXPECT_GT(packet.REMBItem.BitRate, 0u); + EXPECT_EQ(packet.REMBItem.NumberOfSSRCs, 1u); + EXPECT_EQ(packet.REMBItem.SSRCs[0], kSendSsrcs[0]); + received_remb = true; + } + packet_type = parser.Iterate(); + } + if (received_psfb && received_remb) + observation_complete_->Set(); + return SEND_PACKET; + } + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) << "Timed out while waiting for a " + "receiver RTCP REMB packet to be " + "sent."; + } + } test; + + RunBaseTest(&test); +} + +TEST_F(EndToEndTest, VerifyBandwidthStats) { + class RtcpObserver : public test::EndToEndTest, public PacketReceiver { + public: + RtcpObserver() + : EndToEndTest(kDefaultTimeoutMs), + sender_call_(NULL), + receiver_call_(NULL), + has_seen_pacer_delay_(false) {} + + virtual DeliveryStatus DeliverPacket(const uint8_t* packet, + size_t length) OVERRIDE { + Call::Stats sender_stats = sender_call_->GetStats(); + Call::Stats receiver_stats = receiver_call_->GetStats(); + if (!has_seen_pacer_delay_) + has_seen_pacer_delay_ = sender_stats.pacer_delay_ms > 0; + if (sender_stats.send_bandwidth_bps > 0 && + receiver_stats.recv_bandwidth_bps > 0 && has_seen_pacer_delay_) + observation_complete_->Set(); + return receiver_call_->Receiver()->DeliverPacket(packet, length); + } + + virtual void OnCallsCreated(Call* sender_call, + Call* receiver_call) OVERRIDE { + sender_call_ = sender_call; + receiver_call_ = receiver_call; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) << "Timed out while waiting for " + "non-zero bandwidth stats."; + } + + virtual void SetReceivers( + PacketReceiver* send_transport_receiver, + PacketReceiver* receive_transport_receiver) OVERRIDE { + test::RtpRtcpObserver::SetReceivers(this, receive_transport_receiver); + } + + private: + Call* sender_call_; + Call* receiver_call_; + bool has_seen_pacer_delay_; + } test; + + RunBaseTest(&test); +} + +void EndToEndTest::TestXrReceiverReferenceTimeReport(bool enable_rrtr) { + static const int kNumRtcpReportPacketsToObserve = 5; + class RtcpXrObserver : public test::EndToEndTest { + public: + explicit RtcpXrObserver(bool enable_rrtr) + : EndToEndTest(kDefaultTimeoutMs), + enable_rrtr_(enable_rrtr), + sent_rtcp_sr_(0), + sent_rtcp_rr_(0), + sent_rtcp_rrtr_(0), + sent_rtcp_dlrr_(0) {} + + private: + // Receive stream should send RR packets (and RRTR packets if enabled). + virtual Action OnReceiveRtcp(const uint8_t* packet, + size_t length) OVERRIDE { + RTCPUtility::RTCPParserV2 parser(packet, length, true); + EXPECT_TRUE(parser.IsValid()); + + RTCPUtility::RTCPPacketTypes packet_type = parser.Begin(); + while (packet_type != RTCPUtility::kRtcpNotValidCode) { + if (packet_type == RTCPUtility::kRtcpRrCode) { + ++sent_rtcp_rr_; + } else if (packet_type == + RTCPUtility::kRtcpXrReceiverReferenceTimeCode) { + ++sent_rtcp_rrtr_; + } + EXPECT_NE(packet_type, RTCPUtility::kRtcpSrCode); + EXPECT_NE(packet_type, RTCPUtility::kRtcpXrDlrrReportBlockItemCode); + packet_type = parser.Iterate(); + } + return SEND_PACKET; + } + // Send stream should send SR packets (and DLRR packets if enabled). + virtual Action OnSendRtcp(const uint8_t* packet, size_t length) { + RTCPUtility::RTCPParserV2 parser(packet, length, true); + EXPECT_TRUE(parser.IsValid()); + + RTCPUtility::RTCPPacketTypes packet_type = parser.Begin(); + while (packet_type != RTCPUtility::kRtcpNotValidCode) { + if (packet_type == RTCPUtility::kRtcpSrCode) { + ++sent_rtcp_sr_; + } else if (packet_type == RTCPUtility::kRtcpXrDlrrReportBlockItemCode) { + ++sent_rtcp_dlrr_; + } + EXPECT_NE(packet_type, RTCPUtility::kRtcpXrReceiverReferenceTimeCode); + packet_type = parser.Iterate(); + } + if (sent_rtcp_sr_ > kNumRtcpReportPacketsToObserve && + sent_rtcp_rr_ > kNumRtcpReportPacketsToObserve) { + if (enable_rrtr_) { + EXPECT_GT(sent_rtcp_rrtr_, 0); + EXPECT_GT(sent_rtcp_dlrr_, 0); + } else { + EXPECT_EQ(0, sent_rtcp_rrtr_); + EXPECT_EQ(0, sent_rtcp_dlrr_); + } + observation_complete_->Set(); + } + return SEND_PACKET; + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + (*receive_configs)[0].rtp.rtcp_mode = newapi::kRtcpReducedSize; + (*receive_configs)[0].rtp.rtcp_xr.receiver_reference_time_report = + enable_rrtr_; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for RTCP SR/RR packets to be sent."; + } + + bool enable_rrtr_; + int sent_rtcp_sr_; + int sent_rtcp_rr_; + int sent_rtcp_rrtr_; + int sent_rtcp_dlrr_; + } test(enable_rrtr); + + RunBaseTest(&test); +} + +void EndToEndTest::TestSendsSetSsrcs(size_t num_ssrcs, + bool send_single_ssrc_first) { + class SendsSetSsrcs : public test::EndToEndTest { + public: + SendsSetSsrcs(const uint32_t* ssrcs, + size_t num_ssrcs, + bool send_single_ssrc_first) + : EndToEndTest(kDefaultTimeoutMs), + num_ssrcs_(num_ssrcs), + send_single_ssrc_first_(send_single_ssrc_first), + ssrcs_to_observe_(num_ssrcs), + expect_single_ssrc_(send_single_ssrc_first) { + for (size_t i = 0; i < num_ssrcs; ++i) + valid_ssrcs_[ssrcs[i]] = true; + } + + private: + virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { + RTPHeader header; + EXPECT_TRUE(parser_->Parse(packet, length, &header)); + + EXPECT_TRUE(valid_ssrcs_[header.ssrc]) + << "Received unknown SSRC: " << header.ssrc; + + if (!valid_ssrcs_[header.ssrc]) + observation_complete_->Set(); + + if (!is_observed_[header.ssrc]) { + is_observed_[header.ssrc] = true; + --ssrcs_to_observe_; + if (expect_single_ssrc_) { + expect_single_ssrc_ = false; + observation_complete_->Set(); + } + } + + if (ssrcs_to_observe_ == 0) + observation_complete_->Set(); + + return SEND_PACKET; + } + + virtual size_t GetNumStreams() const OVERRIDE { return num_ssrcs_; } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + if (num_ssrcs_ > 1) { + // Set low simulcast bitrates to not have to wait for bandwidth ramp-up. + for (size_t i = 0; i < encoder_config->streams.size(); ++i) { + encoder_config->streams[i].min_bitrate_bps = 10000; + encoder_config->streams[i].target_bitrate_bps = 15000; + encoder_config->streams[i].max_bitrate_bps = 20000; + } + } + + encoder_config_all_streams_ = *encoder_config; + if (send_single_ssrc_first_) + encoder_config->streams.resize(1); + } + + virtual void OnStreamsCreated( + VideoSendStream* send_stream, + const std::vector& receive_streams) OVERRIDE { + send_stream_ = send_stream; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for " + << (send_single_ssrc_first_ ? "first SSRC." : "SSRCs."); + + if (send_single_ssrc_first_) { + // Set full simulcast and continue with the rest of the SSRCs. + send_stream_->ReconfigureVideoEncoder(encoder_config_all_streams_); + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting on additional SSRCs."; + } + } + + private: + std::map valid_ssrcs_; + std::map is_observed_; + + const size_t num_ssrcs_; + const bool send_single_ssrc_first_; + + size_t ssrcs_to_observe_; + bool expect_single_ssrc_; + + VideoSendStream* send_stream_; + VideoEncoderConfig encoder_config_all_streams_; + } test(kSendSsrcs, num_ssrcs, send_single_ssrc_first); + + RunBaseTest(&test); +} + +TEST_F(EndToEndTest, GetStats) { + class StatsObserver : public test::EndToEndTest, public I420FrameCallback { + public: + StatsObserver() + : EndToEndTest(kLongTimeoutMs), + receive_stream_(NULL), + send_stream_(NULL), + expected_receive_ssrc_(), + expected_send_ssrcs_(), + check_stats_event_(EventWrapper::Create()) {} + + private: + virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { + check_stats_event_->Set(); + return SEND_PACKET; + } + + virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE { + check_stats_event_->Set(); + return SEND_PACKET; + } + + virtual Action OnReceiveRtp(const uint8_t* packet, size_t length) OVERRIDE { + check_stats_event_->Set(); + return SEND_PACKET; + } + + virtual Action OnReceiveRtcp(const uint8_t* packet, + size_t length) OVERRIDE { + check_stats_event_->Set(); + return SEND_PACKET; + } + + virtual void FrameCallback(I420VideoFrame* video_frame) OVERRIDE { + // Ensure that we have at least 5ms send side delay. + int64_t render_time = video_frame->render_time_ms(); + if (render_time > 0) + video_frame->set_render_time_ms(render_time - 5); + } + + bool CheckReceiveStats() { + assert(receive_stream_ != NULL); + VideoReceiveStream::Stats stats = receive_stream_->GetStats(); + EXPECT_EQ(expected_receive_ssrc_, stats.ssrc); + + // Make sure all fields have been populated. + + receive_stats_filled_["IncomingRate"] |= + stats.network_frame_rate != 0 || stats.total_bitrate_bps != 0; + + receive_stats_filled_["FrameCallback"] |= stats.decode_frame_rate != 0; + + receive_stats_filled_["FrameRendered"] |= stats.render_frame_rate != 0; + + receive_stats_filled_["StatisticsUpdated"] |= + stats.rtcp_stats.cumulative_lost != 0 || + stats.rtcp_stats.extended_max_sequence_number != 0 || + stats.rtcp_stats.fraction_lost != 0 || stats.rtcp_stats.jitter != 0; + + receive_stats_filled_["DataCountersUpdated"] |= + stats.rtp_stats.bytes != 0 || stats.rtp_stats.fec_packets != 0 || + stats.rtp_stats.header_bytes != 0 || stats.rtp_stats.packets != 0 || + stats.rtp_stats.padding_bytes != 0 || + stats.rtp_stats.retransmitted_packets != 0; + + receive_stats_filled_["CodecStats"] |= + stats.avg_delay_ms != 0 || stats.discarded_packets != 0 || + stats.key_frames != 0 || stats.delta_frames != 0; + + return AllStatsFilled(receive_stats_filled_); + } + + bool CheckSendStats() { + assert(send_stream_ != NULL); + VideoSendStream::Stats stats = send_stream_->GetStats(); + + send_stats_filled_["NumStreams"] |= + stats.substreams.size() == expected_send_ssrcs_.size(); + + for (std::map::const_iterator it = + stats.substreams.begin(); + it != stats.substreams.end(); + ++it) { + EXPECT_TRUE(expected_send_ssrcs_.find(it->first) != + expected_send_ssrcs_.end()); + + send_stats_filled_[CompoundKey("IncomingRate", it->first)] |= + stats.input_frame_rate != 0; + + const SsrcStats& stream_stats = it->second; + + send_stats_filled_[CompoundKey("StatisticsUpdated", it->first)] |= + stream_stats.rtcp_stats.cumulative_lost != 0 || + stream_stats.rtcp_stats.extended_max_sequence_number != 0 || + stream_stats.rtcp_stats.fraction_lost != 0; + + send_stats_filled_[CompoundKey("DataCountersUpdated", it->first)] |= + stream_stats.rtp_stats.fec_packets != 0 || + stream_stats.rtp_stats.padding_bytes != 0 || + stream_stats.rtp_stats.retransmitted_packets != 0 || + stream_stats.rtp_stats.packets != 0; + + send_stats_filled_[CompoundKey("BitrateStatisticsObserver", + it->first)] |= + stream_stats.total_bitrate_bps != 0; + + send_stats_filled_[CompoundKey("FrameCountObserver", it->first)] |= + stream_stats.delta_frames != 0 || stream_stats.key_frames != 0; + + send_stats_filled_[CompoundKey("OutgoingRate", it->first)] |= + stats.encode_frame_rate != 0; + + send_stats_filled_[CompoundKey("Delay", it->first)] |= + stream_stats.avg_delay_ms != 0 || stream_stats.max_delay_ms != 0; + } + + return AllStatsFilled(send_stats_filled_); + } + + std::string CompoundKey(const char* name, uint32_t ssrc) { + std::ostringstream oss; + oss << name << "_" << ssrc; + return oss.str(); + } + + bool AllStatsFilled(const std::map& stats_map) { + for (std::map::const_iterator it = stats_map.begin(); + it != stats_map.end(); + ++it) { + if (!it->second) + return false; + } + return true; + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->pre_encode_callback = this; // Used to inject delay. + send_config->rtp.c_name = "SomeCName"; + + expected_receive_ssrc_ = (*receive_configs)[0].rtp.local_ssrc; + const std::vector& ssrcs = send_config->rtp.ssrcs; + for (size_t i = 0; i < ssrcs.size(); ++i) + expected_send_ssrcs_.insert(ssrcs[i]); + + expected_cname_ = send_config->rtp.c_name; + } + + virtual void OnStreamsCreated( + VideoSendStream* send_stream, + const std::vector& receive_streams) OVERRIDE { + send_stream_ = send_stream; + receive_stream_ = receive_streams[0]; + } + + virtual void PerformTest() OVERRIDE { + Clock* clock = Clock::GetRealTimeClock(); + int64_t now = clock->TimeInMilliseconds(); + int64_t stop_time = now + test::CallTest::kLongTimeoutMs; + bool receive_ok = false; + bool send_ok = false; + + while (now < stop_time) { + if (!receive_ok) + receive_ok = CheckReceiveStats(); + if (!send_ok) + send_ok = CheckSendStats(); + + if (receive_ok && send_ok) + return; + + int64_t time_until_timout_ = stop_time - now; + if (time_until_timout_ > 0) + check_stats_event_->Wait(time_until_timout_); + now = clock->TimeInMilliseconds(); + } + + ADD_FAILURE() << "Timed out waiting for filled stats."; + for (std::map::const_iterator it = + receive_stats_filled_.begin(); + it != receive_stats_filled_.end(); + ++it) { + if (!it->second) { + ADD_FAILURE() << "Missing receive stats: " << it->first; + } + } + + for (std::map::const_iterator it = + send_stats_filled_.begin(); + it != send_stats_filled_.end(); + ++it) { + if (!it->second) { + ADD_FAILURE() << "Missing send stats: " << it->first; + } + } + } + + VideoReceiveStream* receive_stream_; + std::map receive_stats_filled_; + + VideoSendStream* send_stream_; + std::map send_stats_filled_; + + uint32_t expected_receive_ssrc_; + std::set expected_send_ssrcs_; + std::string expected_cname_; + + scoped_ptr check_stats_event_; + } test; + + RunBaseTest(&test); +} + +TEST_F(EndToEndTest, ReceiverReferenceTimeReportEnabled) { + TestXrReceiverReferenceTimeReport(true); +} + +TEST_F(EndToEndTest, ReceiverReferenceTimeReportDisabled) { + TestXrReceiverReferenceTimeReport(false); +} + +TEST_F(EndToEndTest, TestReceivedRtpPacketStats) { + static const size_t kNumRtpPacketsToSend = 5; + class ReceivedRtpStatsObserver : public test::EndToEndTest { + public: + ReceivedRtpStatsObserver() + : EndToEndTest(kDefaultTimeoutMs), + receive_stream_(NULL), + sent_rtp_(0) {} + + private: + virtual void OnStreamsCreated( + VideoSendStream* send_stream, + const std::vector& receive_streams) OVERRIDE { + receive_stream_ = receive_streams[0]; + } + + virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { + if (sent_rtp_ >= kNumRtpPacketsToSend) { + VideoReceiveStream::Stats stats = receive_stream_->GetStats(); + if (kNumRtpPacketsToSend == stats.rtp_stats.packets) { + observation_complete_->Set(); + } + return DROP_PACKET; + } + ++sent_rtp_; + return SEND_PACKET; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while verifying number of received RTP packets."; + } + + VideoReceiveStream* receive_stream_; + uint32_t sent_rtp_; + } test; + + RunBaseTest(&test); +} + +TEST_F(EndToEndTest, SendsSetSsrc) { TestSendsSetSsrcs(1, false); } + +TEST_F(EndToEndTest, SendsSetSimulcastSsrcs) { + TestSendsSetSsrcs(kNumSsrcs, false); +} + +TEST_F(EndToEndTest, CanSwitchToUseAllSsrcs) { + TestSendsSetSsrcs(kNumSsrcs, true); +} + +TEST_F(EndToEndTest, DISABLED_RedundantPayloadsTransmittedOnAllSsrcs) { + class ObserveRedundantPayloads: public test::EndToEndTest { + public: + ObserveRedundantPayloads() + : EndToEndTest(kDefaultTimeoutMs), ssrcs_to_observe_(kNumSsrcs) { + for (size_t i = 0; i < kNumSsrcs; ++i) { + registered_rtx_ssrc_[kSendRtxSsrcs[i]] = true; + } + } + + private: + virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { + RTPHeader header; + EXPECT_TRUE(parser_->Parse(packet, length, &header)); + + if (!registered_rtx_ssrc_[header.ssrc]) + return SEND_PACKET; + + EXPECT_LE(static_cast(header.headerLength + header.paddingLength), + length); + const bool packet_is_redundant_payload = + static_cast(header.headerLength + header.paddingLength) < + length; + + if (!packet_is_redundant_payload) + return SEND_PACKET; + + if (!observed_redundant_retransmission_[header.ssrc]) { + observed_redundant_retransmission_[header.ssrc] = true; + if (--ssrcs_to_observe_ == 0) + observation_complete_->Set(); + } + + return SEND_PACKET; + } + + virtual size_t GetNumStreams() const OVERRIDE { return kNumSsrcs; } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + // Set low simulcast bitrates to not have to wait for bandwidth ramp-up. + for (size_t i = 0; i < encoder_config->streams.size(); ++i) { + encoder_config->streams[i].min_bitrate_bps = 10000; + encoder_config->streams[i].target_bitrate_bps = 15000; + encoder_config->streams[i].max_bitrate_bps = 20000; + } + + send_config->rtp.rtx.payload_type = kSendRtxPayloadType; + send_config->rtp.rtx.pad_with_redundant_payloads = true; + + for (size_t i = 0; i < kNumSsrcs; ++i) + send_config->rtp.rtx.ssrcs.push_back(kSendRtxSsrcs[i]); + + // Significantly higher than max bitrates for all video streams -> forcing + // padding to trigger redundant padding on all RTX SSRCs. + encoder_config->min_transmit_bitrate_bps = 100000; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for redundant payloads on all SSRCs."; + } + + private: + size_t ssrcs_to_observe_; + std::map observed_redundant_retransmission_; + std::map registered_rtx_ssrc_; + } test; + + RunBaseTest(&test); +} + +void EndToEndTest::TestRtpStatePreservation(bool use_rtx) { + static const uint32_t kMaxSequenceNumberGap = 100; + static const uint64_t kMaxTimestampGap = kDefaultTimeoutMs * 90; + class RtpSequenceObserver : public test::RtpRtcpObserver { + public: + explicit RtpSequenceObserver(bool use_rtx) + : test::RtpRtcpObserver(kDefaultTimeoutMs), + crit_(CriticalSectionWrapper::CreateCriticalSection()), + ssrcs_to_observe_(kNumSsrcs) { + for (size_t i = 0; i < kNumSsrcs; ++i) { + configured_ssrcs_[kSendSsrcs[i]] = true; + if (use_rtx) + configured_ssrcs_[kSendRtxSsrcs[i]] = true; + } + } + + void ResetExpectedSsrcs(size_t num_expected_ssrcs) { + CriticalSectionScoped lock(crit_.get()); + ssrc_observed_.clear(); + ssrcs_to_observe_ = num_expected_ssrcs; + } + + private: + virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { + RTPHeader header; + EXPECT_TRUE(parser_->Parse(packet, length, &header)); + const uint32_t ssrc = header.ssrc; + const uint16_t sequence_number = header.sequenceNumber; + const uint32_t timestamp = header.timestamp; + const bool only_padding = + static_cast(header.headerLength + header.paddingLength) == + length; + + EXPECT_TRUE(configured_ssrcs_[ssrc]) + << "Received SSRC that wasn't configured: " << ssrc; + + std::map::iterator it = + last_observed_sequence_number_.find(header.ssrc); + if (it == last_observed_sequence_number_.end()) { + last_observed_sequence_number_[ssrc] = sequence_number; + last_observed_timestamp_[ssrc] = timestamp; + } else { + // Verify sequence numbers are reasonably close. + uint32_t extended_sequence_number = sequence_number; + // Check for roll-over. + if (sequence_number < last_observed_sequence_number_[ssrc]) + extended_sequence_number += 0xFFFFu + 1; + EXPECT_LE( + extended_sequence_number - last_observed_sequence_number_[ssrc], + kMaxSequenceNumberGap) + << "Gap in sequence numbers (" + << last_observed_sequence_number_[ssrc] << " -> " << sequence_number + << ") too large for SSRC: " << ssrc << "."; + last_observed_sequence_number_[ssrc] = sequence_number; + + // TODO(pbos): Remove this check if we ever have monotonically + // increasing timestamps. Right now padding packets add a delta which + // can cause reordering between padding packets and regular packets, + // hence we drop padding-only packets to not flake. + if (only_padding) { + // Verify that timestamps are reasonably close. + uint64_t extended_timestamp = timestamp; + // Check for roll-over. + if (timestamp < last_observed_timestamp_[ssrc]) + extended_timestamp += static_cast(0xFFFFFFFFu) + 1; + EXPECT_LE(extended_timestamp - last_observed_timestamp_[ssrc], + kMaxTimestampGap) + << "Gap in timestamps (" << last_observed_timestamp_[ssrc] + << " -> " << timestamp << ") too large for SSRC: " << ssrc << "."; + } + last_observed_timestamp_[ssrc] = timestamp; + } + + CriticalSectionScoped lock(crit_.get()); + // Wait for media packets on all ssrcs. + if (!ssrc_observed_[ssrc] && !only_padding) { + ssrc_observed_[ssrc] = true; + if (--ssrcs_to_observe_ == 0) + observation_complete_->Set(); + } + + return SEND_PACKET; + } + + std::map last_observed_sequence_number_; + std::map last_observed_timestamp_; + std::map configured_ssrcs_; + + scoped_ptr crit_; + size_t ssrcs_to_observe_ GUARDED_BY(crit_); + std::map ssrc_observed_ GUARDED_BY(crit_); + } observer(use_rtx); + + CreateCalls(Call::Config(observer.SendTransport()), + Call::Config(observer.ReceiveTransport())); + observer.SetReceivers(sender_call_->Receiver(), NULL); + + CreateSendConfig(kNumSsrcs); + + if (use_rtx) { + for (size_t i = 0; i < kNumSsrcs; ++i) { + send_config_.rtp.rtx.ssrcs.push_back(kSendRtxSsrcs[i]); + } + send_config_.rtp.rtx.payload_type = kSendRtxPayloadType; + } + + // Lower bitrates so that all streams send initially. + for (size_t i = 0; i < encoder_config_.streams.size(); ++i) { + encoder_config_.streams[i].min_bitrate_bps = 10000; + encoder_config_.streams[i].target_bitrate_bps = 15000; + encoder_config_.streams[i].max_bitrate_bps = 20000; + } + + // Use the same total bitrates when sending a single stream to avoid lowering + // the bitrate estimate and requiring a subsequent rampup. + VideoEncoderConfig one_stream = encoder_config_; + one_stream.streams.resize(1); + for (size_t i = 1; i < encoder_config_.streams.size(); ++i) { + one_stream.streams.front().min_bitrate_bps += + encoder_config_.streams[i].min_bitrate_bps; + one_stream.streams.front().target_bitrate_bps += + encoder_config_.streams[i].target_bitrate_bps; + one_stream.streams.front().max_bitrate_bps += + encoder_config_.streams[i].max_bitrate_bps; + } + + CreateMatchingReceiveConfigs(); + + CreateStreams(); + CreateFrameGeneratorCapturer(); + + Start(); + EXPECT_EQ(kEventSignaled, observer.Wait()) + << "Timed out waiting for all SSRCs to send packets."; + + // Test stream resetting more than once to make sure that the state doesn't + // get set once (this could be due to using std::map::insert for instance). + for (size_t i = 0; i < 3; ++i) { + frame_generator_capturer_->Stop(); + sender_call_->DestroyVideoSendStream(send_stream_); + + // Re-create VideoSendStream with only one stream. + send_stream_ = + sender_call_->CreateVideoSendStream(send_config_, one_stream); + send_stream_->Start(); + CreateFrameGeneratorCapturer(); + frame_generator_capturer_->Start(); + + observer.ResetExpectedSsrcs(1); + EXPECT_EQ(kEventSignaled, observer.Wait()) + << "Timed out waiting for single RTP packet."; + + // Reconfigure back to use all streams. + send_stream_->ReconfigureVideoEncoder(encoder_config_); + observer.ResetExpectedSsrcs(kNumSsrcs); + EXPECT_EQ(kEventSignaled, observer.Wait()) + << "Timed out waiting for all SSRCs to send packets."; + + // Reconfigure down to one stream. + send_stream_->ReconfigureVideoEncoder(one_stream); + observer.ResetExpectedSsrcs(1); + EXPECT_EQ(kEventSignaled, observer.Wait()) + << "Timed out waiting for single RTP packet."; + + // Reconfigure back to use all streams. + send_stream_->ReconfigureVideoEncoder(encoder_config_); + observer.ResetExpectedSsrcs(kNumSsrcs); + EXPECT_EQ(kEventSignaled, observer.Wait()) + << "Timed out waiting for all SSRCs to send packets."; + } + + observer.StopSending(); + + Stop(); + DestroyStreams(); +} + +TEST_F(EndToEndTest, DISABLED_RestartingSendStreamPreservesRtpState) { + TestRtpStatePreservation(false); +} + +TEST_F(EndToEndTest, RestartingSendStreamPreservesRtpStatesWithRtx) { + TestRtpStatePreservation(true); +} + +TEST_F(EndToEndTest, RespectsNetworkState) { + // TODO(pbos): Remove accepted downtime packets etc. when signaling network + // down blocks until no more packets will be sent. + + // Pacer will send from its packet list and then send required padding before + // checking paused_ again. This should be enough for one round of pacing, + // otherwise increase. + static const int kNumAcceptedDowntimeRtp = 5; + // A single RTCP may be in the pipeline. + static const int kNumAcceptedDowntimeRtcp = 1; + class NetworkStateTest : public test::EndToEndTest, public test::FakeEncoder { + public: + NetworkStateTest() + : EndToEndTest(kDefaultTimeoutMs), + FakeEncoder(Clock::GetRealTimeClock()), + test_crit_(CriticalSectionWrapper::CreateCriticalSection()), + encoded_frames_(EventWrapper::Create()), + sender_packets_(EventWrapper::Create()), + receiver_packets_(EventWrapper::Create()), + sender_state_(Call::kNetworkUp), + down_sender_rtp_(0), + down_sender_rtcp_(0), + receiver_state_(Call::kNetworkUp), + down_receiver_rtcp_(0), + down_frames_(0) {} + + virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { + CriticalSectionScoped lock(test_crit_.get()); + if (sender_state_ == Call::kNetworkDown) { + ++down_sender_rtp_; + EXPECT_LE(down_sender_rtp_, kNumAcceptedDowntimeRtp) + << "RTP sent during sender-side downtime."; + if (down_sender_rtp_> kNumAcceptedDowntimeRtp) + sender_packets_->Set(); + } else { + sender_packets_->Set(); + } + return SEND_PACKET; + } + + virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE { + CriticalSectionScoped lock(test_crit_.get()); + if (sender_state_ == Call::kNetworkDown) { + ++down_sender_rtcp_; + EXPECT_LE(down_sender_rtcp_, kNumAcceptedDowntimeRtcp) + << "RTCP sent during sender-side downtime."; + if (down_sender_rtcp_ > kNumAcceptedDowntimeRtcp) + sender_packets_->Set(); + } else { + sender_packets_->Set(); + } + return SEND_PACKET; + } + + virtual Action OnReceiveRtp(const uint8_t* packet, size_t length) OVERRIDE { + ADD_FAILURE() << "Unexpected receiver RTP, should not be sending."; + return SEND_PACKET; + } + + virtual Action OnReceiveRtcp(const uint8_t* packet, + size_t length) OVERRIDE { + CriticalSectionScoped lock(test_crit_.get()); + if (receiver_state_ == Call::kNetworkDown) { + ++down_receiver_rtcp_; + EXPECT_LE(down_receiver_rtcp_, kNumAcceptedDowntimeRtcp) + << "RTCP sent during receiver-side downtime."; + if (down_receiver_rtcp_ > kNumAcceptedDowntimeRtcp) + receiver_packets_->Set(); + } else { + receiver_packets_->Set(); + } + return SEND_PACKET; + } + + virtual void OnCallsCreated(Call* sender_call, + Call* receiver_call) OVERRIDE { + sender_call_ = sender_call; + receiver_call_ = receiver_call; + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->encoder_settings.encoder = this; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, encoded_frames_->Wait(kDefaultTimeoutMs)) + << "No frames received by the encoder."; + EXPECT_EQ(kEventSignaled, sender_packets_->Wait(kDefaultTimeoutMs)) + << "Timed out waiting for send-side packets."; + EXPECT_EQ(kEventSignaled, receiver_packets_->Wait(kDefaultTimeoutMs)) + << "Timed out waiting for receiver-side packets."; + + // Sender-side network down. + sender_call_->SignalNetworkState(Call::kNetworkDown); + { + CriticalSectionScoped lock(test_crit_.get()); + sender_packets_->Reset(); // Earlier packets should not count. + sender_state_ = Call::kNetworkDown; + } + EXPECT_EQ(kEventTimeout, sender_packets_->Wait(kSilenceTimeoutMs)) + << "Packets sent during sender-network downtime."; + EXPECT_EQ(kEventSignaled, receiver_packets_->Wait(kDefaultTimeoutMs)) + << "Timed out waiting for receiver-side packets."; + // Receiver-side network down. + receiver_call_->SignalNetworkState(Call::kNetworkDown); + { + CriticalSectionScoped lock(test_crit_.get()); + receiver_packets_->Reset(); // Earlier packets should not count. + receiver_state_ = Call::kNetworkDown; + } + EXPECT_EQ(kEventTimeout, receiver_packets_->Wait(kSilenceTimeoutMs)) + << "Packets sent during receiver-network downtime."; + + // Network back up again for both. + { + CriticalSectionScoped lock(test_crit_.get()); + sender_packets_->Reset(); // Earlier packets should not count. + receiver_packets_->Reset(); // Earlier packets should not count. + sender_state_ = receiver_state_ = Call::kNetworkUp; + } + sender_call_->SignalNetworkState(Call::kNetworkUp); + receiver_call_->SignalNetworkState(Call::kNetworkUp); + EXPECT_EQ(kEventSignaled, sender_packets_->Wait(kDefaultTimeoutMs)) + << "Timed out waiting for send-side packets."; + EXPECT_EQ(kEventSignaled, receiver_packets_->Wait(kDefaultTimeoutMs)) + << "Timed out waiting for receiver-side packets."; + } + + virtual int32_t Encode(const I420VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector* frame_types) + OVERRIDE { + { + CriticalSectionScoped lock(test_crit_.get()); + if (sender_state_ == Call::kNetworkDown) { + ++down_frames_; + EXPECT_LE(down_frames_, 1) + << "Encoding more than one frame while network is down."; + if (down_frames_ > 1) + encoded_frames_->Set(); + } else { + encoded_frames_->Set(); + } + } + return test::FakeEncoder::Encode( + input_image, codec_specific_info, frame_types); + } + + private: + const scoped_ptr test_crit_; + scoped_ptr encoded_frames_; + scoped_ptr sender_packets_; + scoped_ptr receiver_packets_; + Call* sender_call_; + Call* receiver_call_; + Call::NetworkState sender_state_ GUARDED_BY(test_crit_); + int down_sender_rtp_ GUARDED_BY(test_crit_); + int down_sender_rtcp_ GUARDED_BY(test_crit_); + Call::NetworkState receiver_state_ GUARDED_BY(test_crit_); + int down_receiver_rtcp_ GUARDED_BY(test_crit_); + int down_frames_ GUARDED_BY(test_crit_); + } test; + + RunBaseTest(&test); +} + +TEST_F(EndToEndTest, NewSendStreamsRespectNetworkDown) { + class UnusedEncoder : public test::FakeEncoder { + public: + UnusedEncoder() : FakeEncoder(Clock::GetRealTimeClock()) {} + virtual int32_t Encode(const I420VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector* frame_types) + OVERRIDE { + ADD_FAILURE() << "Unexpected frame encode."; + return test::FakeEncoder::Encode( + input_image, codec_specific_info, frame_types); + } + }; + + UnusedTransport transport; + CreateSenderCall(Call::Config(&transport)); + sender_call_->SignalNetworkState(Call::kNetworkDown); + + CreateSendConfig(1); + UnusedEncoder unused_encoder; + send_config_.encoder_settings.encoder = &unused_encoder; + CreateStreams(); + CreateFrameGeneratorCapturer(); + + Start(); + SleepMs(kSilenceTimeoutMs); + Stop(); + + DestroyStreams(); +} + +TEST_F(EndToEndTest, NewReceiveStreamsRespectNetworkDown) { + test::DirectTransport sender_transport; + CreateSenderCall(Call::Config(&sender_transport)); + UnusedTransport transport; + CreateReceiverCall(Call::Config(&transport)); + sender_transport.SetReceiver(receiver_call_->Receiver()); + + receiver_call_->SignalNetworkState(Call::kNetworkDown); + + CreateSendConfig(1); + CreateMatchingReceiveConfigs(); + CreateStreams(); + CreateFrameGeneratorCapturer(); + + Start(); + SleepMs(kSilenceTimeoutMs); + Stop(); + + sender_transport.StopSending(); + + DestroyStreams(); +} + +// TODO(pbos): Remove this regression test when VideoEngine is no longer used as +// a backend. This is to test that we hand channels back properly. +TEST_F(EndToEndTest, CanCreateAndDestroyManyVideoStreams) { + test::NullTransport transport; + scoped_ptr call(Call::Create(Call::Config(&transport))); + test::FakeDecoder fake_decoder; + test::FakeEncoder fake_encoder(Clock::GetRealTimeClock()); + for (size_t i = 0; i < 100; ++i) { + VideoSendStream::Config send_config; + send_config.encoder_settings.encoder = &fake_encoder; + send_config.encoder_settings.payload_name = "FAKE"; + send_config.encoder_settings.payload_type = 123; + + VideoEncoderConfig encoder_config; + encoder_config.streams = test::CreateVideoStreams(1); + send_config.rtp.ssrcs.push_back(1); + VideoSendStream* send_stream = + call->CreateVideoSendStream(send_config, encoder_config); + call->DestroyVideoSendStream(send_stream); + + VideoReceiveStream::Config receive_config; + receive_config.rtp.remote_ssrc = 1; + receive_config.rtp.local_ssrc = kReceiverLocalSsrc; + VideoReceiveStream::Decoder decoder; + decoder.decoder = &fake_decoder; + decoder.payload_type = 123; + decoder.payload_name = "FAKE"; + receive_config.decoders.push_back(decoder); + VideoReceiveStream* receive_stream = + call->CreateVideoReceiveStream(receive_config); + call->DestroyVideoReceiveStream(receive_stream); + } +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/full_stack.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/full_stack.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/full_stack.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/full_stack.cc 2015-02-03 14:33:38.000000000 +0000 @@ -12,9 +12,9 @@ #include #include -#include "gflags/gflags.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/thread_annotations.h" #include "webrtc/call.h" #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" @@ -23,18 +23,18 @@ #include "webrtc/system_wrappers/interface/event_wrapper.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/system_wrappers/interface/sleep.h" -#include "webrtc/test/testsupport/fileutils.h" +#include "webrtc/test/call_test.h" #include "webrtc/test/direct_transport.h" +#include "webrtc/test/encoder_settings.h" +#include "webrtc/test/fake_encoder.h" #include "webrtc/test/frame_generator_capturer.h" #include "webrtc/test/statistics.h" -#include "webrtc/test/video_renderer.h" +#include "webrtc/test/testsupport/fileutils.h" #include "webrtc/typedefs.h" -DEFINE_int32(seconds, 10, "Seconds to run each clip."); - namespace webrtc { -static const uint32_t kSendSsrc = 0x654321; +static const int kFullStackTestDurationSecs = 60; struct FullStackTestParams { const char* test_label; @@ -43,25 +43,17 @@ size_t width, height; int fps; } clip; - unsigned int bitrate; + int min_bitrate_bps; + int target_bitrate_bps; + int max_bitrate_bps; double avg_psnr_threshold; double avg_ssim_threshold; + FakeNetworkPipe::Config link; }; -FullStackTestParams paris_qcif = { - "net_delay_0_0_plr_0", {"paris_qcif", 176, 144, 30}, 300, 36.0, 0.96}; - -// TODO(pbos): Decide on psnr/ssim thresholds for foreman_cif. -FullStackTestParams foreman_cif = { - "foreman_cif_net_delay_0_0_plr_0", - {"foreman_cif", 352, 288, 30}, - 700, - 0.0, - 0.0}; - -class FullStackTest : public ::testing::TestWithParam { +class FullStackTest : public test::CallTest { protected: - std::map reserved_ssrcs_; + void RunTest(const FullStackTestParams& params); }; class VideoAnalyzer : public PacketReceiver, @@ -79,18 +71,18 @@ transport_(transport), receiver_(NULL), test_label_(test_label), + frames_left_(duration_frames), dropped_frames_(0), + last_render_time_(0), rtp_timestamp_delta_(0), + crit_(CriticalSectionWrapper::CreateCriticalSection()), first_send_frame_(NULL), - last_render_time_(0), avg_psnr_threshold_(avg_psnr_threshold), avg_ssim_threshold_(avg_ssim_threshold), - frames_left_(duration_frames), - crit_(CriticalSectionWrapper::CreateCriticalSection()), comparison_lock_(CriticalSectionWrapper::CreateCriticalSection()), comparison_thread_(ThreadWrapper::CreateThread(&FrameComparisonThread, this)), - trigger_(EventWrapper::Create()) { + done_(EventWrapper::Create()) { unsigned int id; EXPECT_TRUE(comparison_thread_->Start(id)); } @@ -110,12 +102,13 @@ virtual void SetReceiver(PacketReceiver* receiver) { receiver_ = receiver; } - virtual bool DeliverPacket(const uint8_t* packet, size_t length) OVERRIDE { + virtual DeliveryStatus DeliverPacket(const uint8_t* packet, + size_t length) OVERRIDE { scoped_ptr parser(RtpHeaderParser::Create()); RTPHeader header; - parser->Parse(packet, static_cast(length), &header); + parser->Parse(packet, length, &header); { - CriticalSectionScoped cs(crit_.get()); + CriticalSectionScoped lock(crit_.get()); recv_times_[header.timestamp - rtp_timestamp_delta_] = Clock::GetRealTimeClock()->CurrentNtpInMilliseconds(); } @@ -123,14 +116,10 @@ return receiver_->DeliverPacket(packet, length); } - virtual void PutFrame(const I420VideoFrame& video_frame) OVERRIDE { - ADD_FAILURE() << "PutFrame() should not have been called in this test."; - } - virtual void SwapFrame(I420VideoFrame* video_frame) OVERRIDE { I420VideoFrame* copy = NULL; { - CriticalSectionScoped cs(crit_.get()); + CriticalSectionScoped lock(crit_.get()); if (frame_pool_.size() > 0) { copy = frame_pool_.front(); frame_pool_.pop_front(); @@ -143,7 +132,7 @@ copy->set_timestamp(copy->render_time_ms() * 90); { - CriticalSectionScoped cs(crit_.get()); + CriticalSectionScoped lock(crit_.get()); if (first_send_frame_ == NULL && rtp_timestamp_delta_ == 0) first_send_frame_ = copy; @@ -156,10 +145,10 @@ virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE { scoped_ptr parser(RtpHeaderParser::Create()); RTPHeader header; - parser->Parse(packet, static_cast(length), &header); + parser->Parse(packet, length, &header); { - CriticalSectionScoped cs(crit_.get()); + CriticalSectionScoped lock(crit_.get()); if (rtp_timestamp_delta_ == 0) { rtp_timestamp_delta_ = header.timestamp - first_send_frame_->timestamp(); @@ -182,29 +171,29 @@ Clock::GetRealTimeClock()->CurrentNtpInMilliseconds(); uint32_t send_timestamp = video_frame.timestamp() - rtp_timestamp_delta_; - { - CriticalSectionScoped cs(crit_.get()); - while (frames_.front()->timestamp() < send_timestamp) { - AddFrameComparison( - frames_.front(), &last_rendered_frame_, true, render_time_ms); - frame_pool_.push_back(frames_.front()); - frames_.pop_front(); - } - - I420VideoFrame* reference_frame = frames_.front(); + CriticalSectionScoped lock(crit_.get()); + while (frames_.front()->timestamp() < send_timestamp) { + AddFrameComparison( + frames_.front(), &last_rendered_frame_, true, render_time_ms); + frame_pool_.push_back(frames_.front()); frames_.pop_front(); - assert(reference_frame != NULL); - EXPECT_EQ(reference_frame->timestamp(), send_timestamp); - assert(reference_frame->timestamp() == send_timestamp); - - AddFrameComparison(reference_frame, &video_frame, false, render_time_ms); - frame_pool_.push_back(reference_frame); } + I420VideoFrame* reference_frame = frames_.front(); + frames_.pop_front(); + assert(reference_frame != NULL); + EXPECT_EQ(reference_frame->timestamp(), send_timestamp); + assert(reference_frame->timestamp() == send_timestamp); + + AddFrameComparison(reference_frame, &video_frame, false, render_time_ms); + frame_pool_.push_back(reference_frame); + last_rendered_frame_.CopyFrame(video_frame); } - void Wait() { trigger_->Wait(120 * 1000); } + void Wait() { + EXPECT_EQ(kEventSignaled, done_->Wait(FullStackTest::kLongTimeoutMs)); + } VideoSendStreamInput* input_; Transport* transport_; @@ -248,7 +237,8 @@ void AddFrameComparison(const I420VideoFrame* reference, const I420VideoFrame* render, bool dropped, - int64_t render_time_ms) { + int64_t render_time_ms) + EXCLUSIVE_LOCKS_REQUIRED(crit_) { int64_t send_time_ms = send_times_[reference->timestamp()]; send_times_.erase(reference->timestamp()); int64_t recv_time_ms = recv_times_[reference->timestamp()]; @@ -311,7 +301,7 @@ PrintResult("time_between_rendered_frames", rendered_delta_, " ms"); EXPECT_GT(psnr_.Mean(), avg_psnr_threshold_); EXPECT_GT(ssim_.Mean(), avg_ssim_threshold_); - trigger_->Set(); + done_->Set(); return false; } @@ -351,66 +341,75 @@ unit); } - const char* test_label_; + const char* const test_label_; test::Statistics sender_time_; test::Statistics receiver_time_; test::Statistics psnr_; test::Statistics ssim_; test::Statistics end_to_end_; test::Statistics rendered_delta_; - + int frames_left_; int dropped_frames_; - std::deque frames_; - std::deque frame_pool_; - I420VideoFrame last_rendered_frame_; - std::map send_times_; - std::map recv_times_; - uint32_t rtp_timestamp_delta_; - I420VideoFrame* first_send_frame_; int64_t last_render_time_; - double avg_psnr_threshold_; - double avg_ssim_threshold_; - int frames_left_; - scoped_ptr crit_; - scoped_ptr comparison_lock_; - scoped_ptr comparison_thread_; - std::deque comparisons_; - scoped_ptr trigger_; -}; + uint32_t rtp_timestamp_delta_; -TEST_P(FullStackTest, NoPacketLoss) { - static const uint32_t kReceiverLocalSsrc = 0x123456; - FullStackTestParams params = GetParam(); + const scoped_ptr crit_; + std::deque frames_ GUARDED_BY(crit_); + std::deque frame_pool_ GUARDED_BY(crit_); + I420VideoFrame last_rendered_frame_ GUARDED_BY(crit_); + std::map send_times_ GUARDED_BY(crit_); + std::map recv_times_ GUARDED_BY(crit_); + I420VideoFrame* first_send_frame_ GUARDED_BY(crit_); + const double avg_psnr_threshold_; + const double avg_ssim_threshold_; + + const scoped_ptr comparison_lock_; + const scoped_ptr comparison_thread_; + std::deque comparisons_ GUARDED_BY(comparison_lock_); + const scoped_ptr done_; +}; - test::DirectTransport transport; +void FullStackTest::RunTest(const FullStackTestParams& params) { + test::DirectTransport send_transport(params.link); + test::DirectTransport recv_transport(params.link); VideoAnalyzer analyzer(NULL, - &transport, + &send_transport, params.test_label, params.avg_psnr_threshold, params.avg_ssim_threshold, - FLAGS_seconds * params.clip.fps); - - Call::Config call_config(&analyzer); + kFullStackTestDurationSecs * params.clip.fps); - scoped_ptr call(Call::Create(call_config)); - analyzer.SetReceiver(call->Receiver()); - transport.SetReceiver(&analyzer); + CreateCalls(Call::Config(&analyzer), Call::Config(&recv_transport)); - VideoSendStream::Config send_config = call->GetDefaultSendConfig(); - send_config.rtp.ssrcs.push_back(kSendSsrc); + analyzer.SetReceiver(receiver_call_->Receiver()); + send_transport.SetReceiver(&analyzer); + recv_transport.SetReceiver(sender_call_->Receiver()); + + CreateSendConfig(1); + + scoped_ptr encoder( + VideoEncoder::Create(VideoEncoder::kVp8)); + send_config_.encoder_settings.encoder = encoder.get(); + send_config_.encoder_settings.payload_name = "VP8"; + send_config_.encoder_settings.payload_type = 124; + send_config_.rtp.nack.rtp_history_ms = kNackRtpHistoryMs; + + VideoStream* stream = &encoder_config_.streams[0]; + stream->width = params.clip.width; + stream->height = params.clip.height; + stream->min_bitrate_bps = params.min_bitrate_bps; + stream->target_bitrate_bps = params.target_bitrate_bps; + stream->max_bitrate_bps = params.max_bitrate_bps; + stream->max_framerate = params.clip.fps; + + CreateMatchingReceiveConfigs(); + receive_configs_[0].renderer = &analyzer; + receive_configs_[0].rtp.nack.rtp_history_ms = kNackRtpHistoryMs; - // TODO(pbos): static_cast shouldn't be required after mflodman refactors the - // VideoCodec struct. - send_config.codec.width = static_cast(params.clip.width); - send_config.codec.height = static_cast(params.clip.height); - send_config.codec.minBitrate = params.bitrate; - send_config.codec.startBitrate = params.bitrate; - send_config.codec.maxBitrate = params.bitrate; + CreateStreams(); + analyzer.input_ = send_stream_->Input(); - VideoSendStream* send_stream = call->CreateVideoSendStream(send_config); - analyzer.input_ = send_stream->Input(); - - scoped_ptr file_capturer( + frame_generator_capturer_.reset( test::FrameGeneratorCapturer::CreateFromYuvFile( &analyzer, test::ResourcePath(params.clip.name, "yuv").c_str(), @@ -418,37 +417,134 @@ params.clip.height, params.clip.fps, Clock::GetRealTimeClock())); - ASSERT_TRUE(file_capturer.get() != NULL) + + ASSERT_TRUE(frame_generator_capturer_.get() != NULL) << "Could not create capturer for " << params.clip.name << ".yuv. Is this resource file present?"; - VideoReceiveStream::Config receive_config = call->GetDefaultReceiveConfig(); - receive_config.rtp.remote_ssrc = send_config.rtp.ssrcs[0]; - receive_config.rtp.local_ssrc = kReceiverLocalSsrc; - receive_config.renderer = &analyzer; + Start(); - VideoReceiveStream* receive_stream = - call->CreateVideoReceiveStream(receive_config); + analyzer.Wait(); - receive_stream->StartReceiving(); - send_stream->StartSending(); + send_transport.StopSending(); + recv_transport.StopSending(); - file_capturer->Start(); + Stop(); - analyzer.Wait(); + DestroyStreams(); +} - file_capturer->Stop(); - send_stream->StopSending(); - receive_stream->StopReceiving(); +TEST_F(FullStackTest, ParisQcifWithoutPacketLoss) { + FullStackTestParams paris_qcif = {"net_delay_0_0_plr_0", + {"paris_qcif", 176, 144, 30}, + 300000, + 300000, + 300000, + 36.0, + 0.96 + }; + RunTest(paris_qcif); +} + +TEST_F(FullStackTest, ForemanCifWithoutPacketLoss) { + // TODO(pbos): Decide on psnr/ssim thresholds for foreman_cif. + FullStackTestParams foreman_cif = {"foreman_cif_net_delay_0_0_plr_0", + {"foreman_cif", 352, 288, 30}, + 700000, + 700000, + 700000, + 0.0, + 0.0 + }; + RunTest(foreman_cif); +} + +TEST_F(FullStackTest, ForemanCifPlr5) { + FullStackTestParams foreman_cif = {"foreman_cif_delay_50_0_plr_5", + {"foreman_cif", 352, 288, 30}, + 30000, + 500000, + 2000000, + 0.0, + 0.0 + }; + foreman_cif.link.loss_percent = 5; + foreman_cif.link.queue_delay_ms = 50; + RunTest(foreman_cif); +} - call->DestroyVideoReceiveStream(receive_stream); - call->DestroyVideoSendStream(send_stream); +TEST_F(FullStackTest, ForemanCif500kbps) { + FullStackTestParams foreman_cif = {"foreman_cif_500kbps", + {"foreman_cif", 352, 288, 30}, + 30000, + 500000, + 2000000, + 0.0, + 0.0 + }; + foreman_cif.link.queue_length_packets = 0; + foreman_cif.link.queue_delay_ms = 0; + foreman_cif.link.link_capacity_kbps = 500; + RunTest(foreman_cif); +} - transport.StopSending(); +TEST_F(FullStackTest, ForemanCif500kbpsLimitedQueue) { + FullStackTestParams foreman_cif = {"foreman_cif_500kbps_32pkts_queue", + {"foreman_cif", 352, 288, 30}, + 30000, + 500000, + 2000000, + 0.0, + 0.0 + }; + foreman_cif.link.queue_length_packets = 32; + foreman_cif.link.queue_delay_ms = 0; + foreman_cif.link.link_capacity_kbps = 500; + RunTest(foreman_cif); } -INSTANTIATE_TEST_CASE_P(FullStack, - FullStackTest, - ::testing::Values(paris_qcif, foreman_cif)); +TEST_F(FullStackTest, ForemanCif500kbps100ms) { + FullStackTestParams foreman_cif = {"foreman_cif_500kbps_100ms", + {"foreman_cif", 352, 288, 30}, + 30000, + 500000, + 2000000, + 0.0, + 0.0 + }; + foreman_cif.link.queue_length_packets = 0; + foreman_cif.link.queue_delay_ms = 100; + foreman_cif.link.link_capacity_kbps = 500; + RunTest(foreman_cif); +} +TEST_F(FullStackTest, ForemanCif500kbps100msLimitedQueue) { + FullStackTestParams foreman_cif = {"foreman_cif_500kbps_100ms_32pkts_queue", + {"foreman_cif", 352, 288, 30}, + 30000, + 500000, + 2000000, + 0.0, + 0.0 + }; + foreman_cif.link.queue_length_packets = 32; + foreman_cif.link.queue_delay_ms = 100; + foreman_cif.link.link_capacity_kbps = 500; + RunTest(foreman_cif); +} + +TEST_F(FullStackTest, ForemanCif1000kbps100msLimitedQueue) { + FullStackTestParams foreman_cif = {"foreman_cif_1000kbps_100ms_32pkts_queue", + {"foreman_cif", 352, 288, 30}, + 30000, + 2000000, + 2000000, + 0.0, + 0.0 + }; + foreman_cif.link.queue_length_packets = 32; + foreman_cif.link.queue_delay_ms = 100; + foreman_cif.link.link_capacity_kbps = 1000; + RunTest(foreman_cif); +} } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/loopback.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/loopback.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/loopback.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/loopback.cc 2015-02-03 14:33:38.000000000 +0000 @@ -12,90 +12,209 @@ #include +#include "gflags/gflags.h" #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/call.h" +#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h" #include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/test/direct_transport.h" -#include "webrtc/test/flags.h" +#include "webrtc/test/encoder_settings.h" +#include "webrtc/test/fake_encoder.h" +#include "webrtc/test/field_trial.h" #include "webrtc/test/run_loop.h" -#include "webrtc/test/run_tests.h" +#include "webrtc/test/run_test.h" +#include "webrtc/test/testsupport/trace_to_stderr.h" #include "webrtc/test/video_capturer.h" #include "webrtc/test/video_renderer.h" #include "webrtc/typedefs.h" namespace webrtc { -class LoopbackTest : public ::testing::Test { - protected: - std::map reserved_ssrcs_; -}; +static const int kAbsSendTimeExtensionId = 7; + +namespace flags { + +DEFINE_int32(width, 640, "Video width."); +size_t Width() { return static_cast(FLAGS_width); } + +DEFINE_int32(height, 480, "Video height."); +size_t Height() { return static_cast(FLAGS_height); } + +DEFINE_int32(fps, 30, "Frames per second."); +int Fps() { return static_cast(FLAGS_fps); } + +DEFINE_int32(min_bitrate, 50, "Minimum video bitrate."); +size_t MinBitrate() { return static_cast(FLAGS_min_bitrate); } + +DEFINE_int32(start_bitrate, 300, "Video starting bitrate."); +size_t StartBitrate() { return static_cast(FLAGS_start_bitrate); } + +DEFINE_int32(max_bitrate, 800, "Maximum video bitrate."); +size_t MaxBitrate() { return static_cast(FLAGS_max_bitrate); } + +DEFINE_string(codec, "VP8", "Video codec to use."); +std::string Codec() { return static_cast(FLAGS_codec); } + +DEFINE_int32(loss_percent, 0, "Percentage of packets randomly lost."); +int LossPercent() { + return static_cast(FLAGS_loss_percent); +} + +DEFINE_int32(link_capacity, + 0, + "Capacity (kbps) of the fake link. 0 means infinite."); +int LinkCapacity() { + return static_cast(FLAGS_link_capacity); +} + +DEFINE_int32(queue_size, 0, "Size of the bottleneck link queue in packets."); +int QueueSize() { + return static_cast(FLAGS_queue_size); +} + +DEFINE_int32(avg_propagation_delay_ms, + 0, + "Average link propagation delay in ms."); +int AvgPropagationDelayMs() { + return static_cast(FLAGS_avg_propagation_delay_ms); +} + +DEFINE_int32(std_propagation_delay_ms, + 0, + "Link propagation delay standard deviation in ms."); +int StdPropagationDelayMs() { + return static_cast(FLAGS_std_propagation_delay_ms); +} + +DEFINE_bool(logs, false, "print logs to stderr"); + +DEFINE_string( + force_fieldtrials, + "", + "Field trials control experimental feature code which can be forced. " + "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enable/" + " will assign the group Enable to field trial WebRTC-FooFeature. Multiple " + "trials are separated by \"/\""); +} // namespace flags static const uint32_t kSendSsrc = 0x654321; +static const uint32_t kSendRtxSsrc = 0x654322; static const uint32_t kReceiverLocalSsrc = 0x123456; -TEST_F(LoopbackTest, Test) { +static const uint8_t kRtxPayloadType = 96; + +void Loopback() { + scoped_ptr trace_to_stderr_; + if (webrtc::flags::FLAGS_logs) + trace_to_stderr_.reset(new test::TraceToStderr); + scoped_ptr local_preview(test::VideoRenderer::Create( - "Local Preview", test::flags::Width(), test::flags::Height())); + "Local Preview", flags::Width(), flags::Height())); scoped_ptr loopback_video(test::VideoRenderer::Create( - "Loopback Video", test::flags::Width(), test::flags::Height())); + "Loopback Video", flags::Width(), flags::Height())); - test::DirectTransport transport; + FakeNetworkPipe::Config pipe_config; + pipe_config.loss_percent = flags::LossPercent(); + pipe_config.link_capacity_kbps = flags::LinkCapacity(); + pipe_config.queue_length_packets = flags::QueueSize(); + pipe_config.queue_delay_ms = flags::AvgPropagationDelayMs(); + pipe_config.delay_standard_deviation_ms = flags::StdPropagationDelayMs(); + test::DirectTransport transport(pipe_config); Call::Config call_config(&transport); + call_config.stream_start_bitrate_bps = + static_cast(flags::StartBitrate()) * 1000; scoped_ptr call(Call::Create(call_config)); // Loopback, call sends to itself. transport.SetReceiver(call->Receiver()); - VideoSendStream::Config send_config = call->GetDefaultSendConfig(); + VideoSendStream::Config send_config; send_config.rtp.ssrcs.push_back(kSendSsrc); + send_config.rtp.rtx.ssrcs.push_back(kSendRtxSsrc); + send_config.rtp.rtx.payload_type = kRtxPayloadType; + send_config.rtp.nack.rtp_history_ms = 1000; + send_config.rtp.extensions.push_back( + RtpExtension(RtpExtension::kAbsSendTime, kAbsSendTimeExtensionId)); send_config.local_renderer = local_preview.get(); + scoped_ptr encoder; + if (flags::Codec() == "VP8") { + encoder.reset(VideoEncoder::Create(VideoEncoder::kVp8)); + } else if (flags::Codec() == "VP9") { + encoder.reset(VideoEncoder::Create(VideoEncoder::kVp9)); + } else { + // Codec not supported. + assert(false && "Codec not supported!"); + return; + } + send_config.encoder_settings.encoder = encoder.get(); + send_config.encoder_settings.payload_name = flags::Codec(); + send_config.encoder_settings.payload_type = 124; + VideoEncoderConfig encoder_config; + encoder_config.streams = test::CreateVideoStreams(1); + VideoStream* stream = &encoder_config.streams[0]; + stream->width = flags::Width(); + stream->height = flags::Height(); + stream->min_bitrate_bps = static_cast(flags::MinBitrate()) * 1000; + stream->target_bitrate_bps = static_cast(flags::MaxBitrate()) * 1000; + stream->max_bitrate_bps = static_cast(flags::MaxBitrate()) * 1000; + stream->max_framerate = 30; + stream->max_qp = 56; - // TODO(pbos): static_cast shouldn't be required after mflodman refactors the - // VideoCodec struct. - send_config.codec.width = static_cast(test::flags::Width()); - send_config.codec.height = static_cast(test::flags::Height()); - send_config.codec.minBitrate = - static_cast(test::flags::MinBitrate()); - send_config.codec.startBitrate = - static_cast(test::flags::StartBitrate()); - send_config.codec.maxBitrate = - static_cast(test::flags::MaxBitrate()); - - VideoSendStream* send_stream = call->CreateVideoSendStream(send_config); + VideoSendStream* send_stream = + call->CreateVideoSendStream(send_config, encoder_config); Clock* test_clock = Clock::GetRealTimeClock(); scoped_ptr camera( test::VideoCapturer::Create(send_stream->Input(), - test::flags::Width(), - test::flags::Height(), - test::flags::Fps(), + flags::Width(), + flags::Height(), + flags::Fps(), test_clock)); - VideoReceiveStream::Config receive_config = call->GetDefaultReceiveConfig(); + VideoReceiveStream::Config receive_config; receive_config.rtp.remote_ssrc = send_config.rtp.ssrcs[0]; receive_config.rtp.local_ssrc = kReceiverLocalSsrc; + receive_config.rtp.nack.rtp_history_ms = 1000; + receive_config.rtp.rtx[kRtxPayloadType].ssrc = kSendRtxSsrc; + receive_config.rtp.rtx[kRtxPayloadType].payload_type = kRtxPayloadType; + receive_config.rtp.extensions.push_back( + RtpExtension(RtpExtension::kAbsSendTime, kAbsSendTimeExtensionId)); receive_config.renderer = loopback_video.get(); + VideoReceiveStream::Decoder decoder = + test::CreateMatchingDecoder(send_config.encoder_settings); + receive_config.decoders.push_back(decoder); VideoReceiveStream* receive_stream = call->CreateVideoReceiveStream(receive_config); - receive_stream->StartReceiving(); - send_stream->StartSending(); + receive_stream->Start(); + send_stream->Start(); camera->Start(); test::PressEnterToContinue(); camera->Stop(); - send_stream->StopSending(); - receive_stream->StopReceiving(); + send_stream->Stop(); + receive_stream->Stop(); call->DestroyVideoReceiveStream(receive_stream); call->DestroyVideoSendStream(send_stream); + delete decoder.decoder; + transport.StopSending(); } } // namespace webrtc + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + google::ParseCommandLineFlags(&argc, &argv, true); + webrtc::test::InitFieldTrialsFromString( + webrtc::flags::FLAGS_force_fieldtrials); + webrtc::test::RunTest(webrtc::Loopback); + return 0; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/OWNERS 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/OWNERS 2015-02-03 14:33:38.000000000 +0000 @@ -1,4 +1,10 @@ mflodman@webrtc.org stefan@webrtc.org -wu@webrtc.org -mallinath@webrtc.org +pbos@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/rampup_tests.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/rampup_tests.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/rampup_tests.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/rampup_tests.cc 2015-02-03 14:33:38.000000000 +0000 @@ -7,271 +7,505 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ -#include - -#include -#include #include "testing/gtest/include/gtest/gtest.h" - -#include "webrtc/call.h" -#include "webrtc/common.h" -#include "webrtc/experiments.h" -#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" #include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/event_wrapper.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/test/direct_transport.h" -#include "webrtc/test/fake_decoder.h" -#include "webrtc/test/fake_encoder.h" -#include "webrtc/test/frame_generator_capturer.h" #include "webrtc/test/testsupport/perf_test.h" -#include "webrtc/video/transport_adapter.h" +#include "webrtc/video/rampup_tests.h" namespace webrtc { - namespace { -static const int kAbsoluteSendTimeExtensionId = 7; + static const int kMaxPacketSize = 1500; + +std::vector GenerateSsrcs(size_t num_streams, + uint32_t ssrc_offset) { + std::vector ssrcs; + for (size_t i = 0; i != num_streams; ++i) + ssrcs.push_back(static_cast(ssrc_offset + i)); + return ssrcs; } +} // namespace -class StreamObserver : public newapi::Transport, public RemoteBitrateObserver { - public: - typedef std::map BytesSentMap; - typedef std::map SsrcMap; - StreamObserver(int num_expected_ssrcs, - const SsrcMap& rtx_media_ssrcs, - newapi::Transport* feedback_transport, - Clock* clock) - : critical_section_(CriticalSectionWrapper::CreateCriticalSection()), - all_ssrcs_sent_(EventWrapper::Create()), - rtp_parser_(RtpHeaderParser::Create()), - feedback_transport_(feedback_transport), - receive_stats_(ReceiveStatistics::Create(clock)), - payload_registry_( - new RTPPayloadRegistry(-1, - RTPPayloadStrategy::CreateStrategy(false))), - clock_(clock), - num_expected_ssrcs_(num_expected_ssrcs), - rtx_media_ssrcs_(rtx_media_ssrcs), - total_sent_(0), - padding_sent_(0), - rtx_media_sent_(0), - total_packets_sent_(0), - padding_packets_sent_(0), - rtx_media_packets_sent_(0) { - // Ideally we would only have to instantiate an RtcpSender, an - // RtpHeaderParser and a RemoteBitrateEstimator here, but due to the current - // state of the RTP module we need a full module and receive statistics to - // be able to produce an RTCP with REMB. - RtpRtcp::Configuration config; - config.receive_statistics = receive_stats_.get(); - feedback_transport_.Enable(); - config.outgoing_transport = &feedback_transport_; - rtp_rtcp_.reset(RtpRtcp::CreateRtpRtcp(config)); - rtp_rtcp_->SetREMBStatus(true); - rtp_rtcp_->SetRTCPStatus(kRtcpNonCompound); - rtp_parser_->RegisterRtpHeaderExtension(kRtpExtensionAbsoluteSendTime, - kAbsoluteSendTimeExtensionId); - AbsoluteSendTimeRemoteBitrateEstimatorFactory rbe_factory; - const uint32_t kRemoteBitrateEstimatorMinBitrateBps = 30000; - remote_bitrate_estimator_.reset( - rbe_factory.Create(this, clock, kRemoteBitrateEstimatorMinBitrateBps)); - } - - virtual void OnReceiveBitrateChanged(const std::vector& ssrcs, - unsigned int bitrate) { - CriticalSectionScoped lock(critical_section_.get()); - if (ssrcs.size() == num_expected_ssrcs_ && bitrate >= kExpectedBitrateBps) { - if (rtx_media_ssrcs_.empty() || rtx_media_sent_ > 0) { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - webrtc::test::PrintResult( - "total-sent", "", test_info->name(), total_sent_, "bytes", false); - webrtc::test::PrintResult("padding-sent", - "", - test_info->name(), - padding_sent_, - "bytes", - false); - webrtc::test::PrintResult("rtx-media-sent", - "", - test_info->name(), - rtx_media_sent_, - "bytes", +StreamObserver::StreamObserver(const SsrcMap& rtx_media_ssrcs, + newapi::Transport* feedback_transport, + Clock* clock, + RemoteBitrateEstimatorFactory* rbe_factory, + RateControlType control_type) + : clock_(clock), + test_done_(EventWrapper::Create()), + rtp_parser_(RtpHeaderParser::Create()), + feedback_transport_(feedback_transport), + receive_stats_(ReceiveStatistics::Create(clock)), + payload_registry_( + new RTPPayloadRegistry(RTPPayloadStrategy::CreateStrategy(false))), + crit_(CriticalSectionWrapper::CreateCriticalSection()), + expected_bitrate_bps_(0), + start_bitrate_bps_(0), + rtx_media_ssrcs_(rtx_media_ssrcs), + total_sent_(0), + padding_sent_(0), + rtx_media_sent_(0), + total_packets_sent_(0), + padding_packets_sent_(0), + rtx_media_packets_sent_(0), + test_start_ms_(clock_->TimeInMilliseconds()), + ramp_up_finished_ms_(0) { + // Ideally we would only have to instantiate an RtcpSender, an + // RtpHeaderParser and a RemoteBitrateEstimator here, but due to the current + // state of the RTP module we need a full module and receive statistics to + // be able to produce an RTCP with REMB. + RtpRtcp::Configuration config; + config.receive_statistics = receive_stats_.get(); + feedback_transport_.Enable(); + config.outgoing_transport = &feedback_transport_; + rtp_rtcp_.reset(RtpRtcp::CreateRtpRtcp(config)); + rtp_rtcp_->SetREMBStatus(true); + rtp_rtcp_->SetRTCPStatus(kRtcpNonCompound); + rtp_parser_->RegisterRtpHeaderExtension(kRtpExtensionAbsoluteSendTime, + kAbsSendTimeExtensionId); + rtp_parser_->RegisterRtpHeaderExtension(kRtpExtensionTransmissionTimeOffset, + kTransmissionTimeOffsetExtensionId); + const uint32_t kRemoteBitrateEstimatorMinBitrateBps = 30000; + remote_bitrate_estimator_.reset( + rbe_factory->Create(this, clock, control_type, + kRemoteBitrateEstimatorMinBitrateBps)); +} + +void StreamObserver::set_expected_bitrate_bps( + unsigned int expected_bitrate_bps) { + CriticalSectionScoped lock(crit_.get()); + expected_bitrate_bps_ = expected_bitrate_bps; +} + +void StreamObserver::set_start_bitrate_bps(unsigned int start_bitrate_bps) { + CriticalSectionScoped lock(crit_.get()); + start_bitrate_bps_ = start_bitrate_bps; +} + +void StreamObserver::OnReceiveBitrateChanged( + const std::vector& ssrcs, unsigned int bitrate) { + CriticalSectionScoped lock(crit_.get()); + assert(expected_bitrate_bps_ > 0); + if (start_bitrate_bps_ != 0) { + // For tests with an explicitly set start bitrate, verify the first + // bitrate estimate is close to the start bitrate and lower than the + // test target bitrate. This is to verify a call respects the configured + // start bitrate, but due to the BWE implementation we can't guarantee the + // first estimate really is as high as the start bitrate. + EXPECT_GT(bitrate, 0.9 * start_bitrate_bps_); + start_bitrate_bps_ = 0; + } + if (bitrate >= expected_bitrate_bps_) { + ramp_up_finished_ms_ = clock_->TimeInMilliseconds(); + // Just trigger if there was any rtx padding packet. + if (rtx_media_ssrcs_.empty() || rtx_media_sent_ > 0) { + TriggerTestDone(); + } + } + rtp_rtcp_->SetREMBData( + bitrate, static_cast(ssrcs.size()), &ssrcs[0]); + rtp_rtcp_->Process(); +} + +bool StreamObserver::SendRtp(const uint8_t* packet, size_t length) { + CriticalSectionScoped lock(crit_.get()); + RTPHeader header; + EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast(length), &header)); + receive_stats_->IncomingPacket(header, length, false); + payload_registry_->SetIncomingPayloadType(header); + remote_bitrate_estimator_->IncomingPacket( + clock_->TimeInMilliseconds(), static_cast(length - 12), header); + if (remote_bitrate_estimator_->TimeUntilNextProcess() <= 0) { + remote_bitrate_estimator_->Process(); + } + total_sent_ += length; + padding_sent_ += header.paddingLength; + ++total_packets_sent_; + if (header.paddingLength > 0) + ++padding_packets_sent_; + if (rtx_media_ssrcs_.find(header.ssrc) != rtx_media_ssrcs_.end()) { + rtx_media_sent_ += length - header.headerLength - header.paddingLength; + if (header.paddingLength == 0) + ++rtx_media_packets_sent_; + uint8_t restored_packet[kMaxPacketSize]; + uint8_t* restored_packet_ptr = restored_packet; + int restored_length = static_cast(length); + payload_registry_->RestoreOriginalPacket(&restored_packet_ptr, + packet, + &restored_length, + rtx_media_ssrcs_[header.ssrc], + header); + length = restored_length; + EXPECT_TRUE(rtp_parser_->Parse( + restored_packet, static_cast(length), &header)); + } else { + rtp_rtcp_->SetRemoteSSRC(header.ssrc); + } + return true; +} + +bool StreamObserver::SendRtcp(const uint8_t* packet, size_t length) { + return true; +} + +EventTypeWrapper StreamObserver::Wait() { + return test_done_->Wait(test::CallTest::kLongTimeoutMs); +} + +void StreamObserver::ReportResult(const std::string& measurement, + size_t value, + const std::string& units) { + webrtc::test::PrintResult( + measurement, "", + ::testing::UnitTest::GetInstance()->current_test_info()->name(), + value, units, false); +} + +void StreamObserver::TriggerTestDone() EXCLUSIVE_LOCKS_REQUIRED(crit_) { + ReportResult("ramp-up-total-sent", total_sent_, "bytes"); + ReportResult("ramp-up-padding-sent", padding_sent_, "bytes"); + ReportResult("ramp-up-rtx-media-sent", rtx_media_sent_, "bytes"); + ReportResult("ramp-up-total-packets-sent", total_packets_sent_, "packets"); + ReportResult("ramp-up-padding-packets-sent", + padding_packets_sent_, + "packets"); + ReportResult("ramp-up-rtx-packets-sent", + rtx_media_packets_sent_, + "packets"); + ReportResult("ramp-up-time", + ramp_up_finished_ms_ - test_start_ms_, + "milliseconds"); + test_done_->Set(); +} + +LowRateStreamObserver::LowRateStreamObserver( + newapi::Transport* feedback_transport, + Clock* clock, + size_t number_of_streams, + bool rtx_used) + : clock_(clock), + number_of_streams_(number_of_streams), + rtx_used_(rtx_used), + test_done_(EventWrapper::Create()), + rtp_parser_(RtpHeaderParser::Create()), + feedback_transport_(feedback_transport), + receive_stats_(ReceiveStatistics::Create(clock)), + crit_(CriticalSectionWrapper::CreateCriticalSection()), + send_stream_(NULL), + test_state_(kFirstRampup), + state_start_ms_(clock_->TimeInMilliseconds()), + interval_start_ms_(state_start_ms_), + last_remb_bps_(0), + sent_bytes_(0), + total_overuse_bytes_(0), + suspended_in_stats_(false) { + RtpRtcp::Configuration config; + config.receive_statistics = receive_stats_.get(); + feedback_transport_.Enable(); + config.outgoing_transport = &feedback_transport_; + rtp_rtcp_.reset(RtpRtcp::CreateRtpRtcp(config)); + rtp_rtcp_->SetREMBStatus(true); + rtp_rtcp_->SetRTCPStatus(kRtcpNonCompound); + rtp_parser_->RegisterRtpHeaderExtension(kRtpExtensionTransmissionTimeOffset, + kTransmissionTimeOffsetExtensionId); + AbsoluteSendTimeRemoteBitrateEstimatorFactory rbe_factory; + const uint32_t kRemoteBitrateEstimatorMinBitrateBps = 10000; + remote_bitrate_estimator_.reset( + rbe_factory.Create(this, clock, kMimdControl, + kRemoteBitrateEstimatorMinBitrateBps)); + forward_transport_config_.link_capacity_kbps = + kHighBandwidthLimitBps / 1000; + forward_transport_config_.queue_length_packets = 100; // Something large. + test::DirectTransport::SetConfig(forward_transport_config_); + test::DirectTransport::SetReceiver(this); +} + +void LowRateStreamObserver::SetSendStream(const VideoSendStream* send_stream) { + CriticalSectionScoped lock(crit_.get()); + send_stream_ = send_stream; +} + +void LowRateStreamObserver::OnReceiveBitrateChanged( + const std::vector& ssrcs, + unsigned int bitrate) { + CriticalSectionScoped lock(crit_.get()); + rtp_rtcp_->SetREMBData( + bitrate, static_cast(ssrcs.size()), &ssrcs[0]); + rtp_rtcp_->Process(); + last_remb_bps_ = bitrate; +} + +bool LowRateStreamObserver::SendRtp(const uint8_t* data, size_t length) { + CriticalSectionScoped lock(crit_.get()); + sent_bytes_ += length; + int64_t now_ms = clock_->TimeInMilliseconds(); + if (now_ms > interval_start_ms_ + 1000) { // Let at least 1 second pass. + // Verify that the send rate was about right. + unsigned int average_rate_bps = static_cast(sent_bytes_) * + 8 * 1000 / (now_ms - interval_start_ms_); + // TODO(holmer): Why is this failing? + // EXPECT_LT(average_rate_bps, last_remb_bps_ * 1.1); + if (average_rate_bps > last_remb_bps_ * 1.1) { + total_overuse_bytes_ += + sent_bytes_ - + last_remb_bps_ / 8 * (now_ms - interval_start_ms_) / 1000; + } + EvolveTestState(average_rate_bps); + interval_start_ms_ = now_ms; + sent_bytes_ = 0; + } + return test::DirectTransport::SendRtp(data, length); +} + +PacketReceiver::DeliveryStatus LowRateStreamObserver::DeliverPacket( + const uint8_t* packet, size_t length) { + CriticalSectionScoped lock(crit_.get()); + RTPHeader header; + EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast(length), &header)); + receive_stats_->IncomingPacket(header, length, false); + remote_bitrate_estimator_->IncomingPacket( + clock_->TimeInMilliseconds(), static_cast(length - 12), header); + if (remote_bitrate_estimator_->TimeUntilNextProcess() <= 0) { + remote_bitrate_estimator_->Process(); + } + suspended_in_stats_ = send_stream_->GetStats().suspended; + return DELIVERY_OK; +} + +bool LowRateStreamObserver::SendRtcp(const uint8_t* packet, size_t length) { + return true; +} + +std::string LowRateStreamObserver::GetModifierString() { + std::string str("_"); + char temp_str[5]; + sprintf(temp_str, "%i", + static_cast(number_of_streams_)); + str += std::string(temp_str); + str += "stream"; + str += (number_of_streams_ > 1 ? "s" : ""); + str += "_"; + str += (rtx_used_ ? "" : "no"); + str += "rtx"; + return str; +} + +void LowRateStreamObserver::EvolveTestState(unsigned int bitrate_bps) { + int64_t now = clock_->TimeInMilliseconds(); + CriticalSectionScoped lock(crit_.get()); + assert(send_stream_ != NULL); + switch (test_state_) { + case kFirstRampup: { + EXPECT_FALSE(suspended_in_stats_); + if (bitrate_bps > kExpectedHighBitrateBps) { + // The first ramp-up has reached the target bitrate. Change the + // channel limit, and move to the next test state. + forward_transport_config_.link_capacity_kbps = + kLowBandwidthLimitBps / 1000; + test::DirectTransport::SetConfig(forward_transport_config_); + test_state_ = kLowRate; + webrtc::test::PrintResult("ramp_up_down_up", + GetModifierString(), + "first_rampup", + now - state_start_ms_, + "ms", false); - webrtc::test::PrintResult("total-packets-sent", - "", - test_info->name(), - total_packets_sent_, - "packets", + state_start_ms_ = now; + interval_start_ms_ = now; + sent_bytes_ = 0; + } + break; + } + case kLowRate: { + if (bitrate_bps < kExpectedLowBitrateBps && suspended_in_stats_) { + // The ramp-down was successful. Change the channel limit back to a + // high value, and move to the next test state. + forward_transport_config_.link_capacity_kbps = + kHighBandwidthLimitBps / 1000; + test::DirectTransport::SetConfig(forward_transport_config_); + test_state_ = kSecondRampup; + webrtc::test::PrintResult("ramp_up_down_up", + GetModifierString(), + "rampdown", + now - state_start_ms_, + "ms", false); - webrtc::test::PrintResult("padding-packets-sent", - "", - test_info->name(), - padding_packets_sent_, - "packets", + state_start_ms_ = now; + interval_start_ms_ = now; + sent_bytes_ = 0; + } + break; + } + case kSecondRampup: { + if (bitrate_bps > kExpectedHighBitrateBps && !suspended_in_stats_) { + webrtc::test::PrintResult("ramp_up_down_up", + GetModifierString(), + "second_rampup", + now - state_start_ms_, + "ms", false); - webrtc::test::PrintResult("rtx-packets-sent", - "", - test_info->name(), - rtx_media_packets_sent_, - "packets", + webrtc::test::PrintResult("ramp_up_down_up", + GetModifierString(), + "total_overuse", + total_overuse_bytes_, + "bytes", false); - all_ssrcs_sent_->Set(); + test_done_->Set(); } + break; } - rtp_rtcp_->SetREMBData( - bitrate, static_cast(ssrcs.size()), &ssrcs[0]); - rtp_rtcp_->Process(); } +} - virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE { - CriticalSectionScoped lock(critical_section_.get()); - RTPHeader header; - EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast(length), &header)); - receive_stats_->IncomingPacket(header, length, false); - payload_registry_->SetIncomingPayloadType(header); - remote_bitrate_estimator_->IncomingPacket( - clock_->TimeInMilliseconds(), static_cast(length - 12), header); - if (remote_bitrate_estimator_->TimeUntilNextProcess() <= 0) { - remote_bitrate_estimator_->Process(); - } - total_sent_ += length; - padding_sent_ += header.paddingLength; - ++total_packets_sent_; - if (header.paddingLength > 0) - ++padding_packets_sent_; - if (rtx_media_ssrcs_.find(header.ssrc) != rtx_media_ssrcs_.end()) { - rtx_media_sent_ += length - header.headerLength - header.paddingLength; - if (header.paddingLength == 0) - ++rtx_media_packets_sent_; - uint8_t restored_packet[kMaxPacketSize]; - uint8_t* restored_packet_ptr = restored_packet; - int restored_length = static_cast(length); - payload_registry_->RestoreOriginalPacket(&restored_packet_ptr, - packet, - &restored_length, - rtx_media_ssrcs_[header.ssrc], - header); - length = restored_length; - EXPECT_TRUE(rtp_parser_->Parse( - restored_packet, static_cast(length), &header)); - } else { - rtp_rtcp_->SetRemoteSSRC(header.ssrc); - } - return true; +EventTypeWrapper LowRateStreamObserver::Wait() { + return test_done_->Wait(test::CallTest::kLongTimeoutMs); +} + +void RampUpTest::RunRampUpTest(bool rtx, + size_t num_streams, + unsigned int start_bitrate_bps, + const std::string& extension_type) { + std::vector ssrcs(GenerateSsrcs(num_streams, 100)); + std::vector rtx_ssrcs(GenerateSsrcs(num_streams, 200)); + StreamObserver::SsrcMap rtx_ssrc_map; + if (rtx) { + for (size_t i = 0; i < ssrcs.size(); ++i) + rtx_ssrc_map[rtx_ssrcs[i]] = ssrcs[i]; + } + + CreateSendConfig(num_streams); + + scoped_ptr rbe_factory; + RateControlType control_type; + if (extension_type == RtpExtension::kAbsSendTime) { + control_type = kAimdControl; + rbe_factory.reset(new AbsoluteSendTimeRemoteBitrateEstimatorFactory); + send_config_.rtp.extensions.push_back(RtpExtension( + extension_type.c_str(), kAbsSendTimeExtensionId)); + } else { + control_type = kMimdControl; + rbe_factory.reset(new RemoteBitrateEstimatorFactory); + send_config_.rtp.extensions.push_back(RtpExtension( + extension_type.c_str(), kTransmissionTimeOffsetExtensionId)); } - virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE { - return true; + test::DirectTransport receiver_transport; + StreamObserver stream_observer(rtx_ssrc_map, + &receiver_transport, + Clock::GetRealTimeClock(), + rbe_factory.get(), + control_type); + + Call::Config call_config(&stream_observer); + if (start_bitrate_bps != 0) { + call_config.stream_start_bitrate_bps = start_bitrate_bps; + stream_observer.set_start_bitrate_bps(start_bitrate_bps); } - EventTypeWrapper Wait() { return all_ssrcs_sent_->Wait(120 * 1000); } + CreateSenderCall(call_config); - private: - static const unsigned int kExpectedBitrateBps = 1200000; - - scoped_ptr critical_section_; - scoped_ptr all_ssrcs_sent_; - scoped_ptr rtp_parser_; - scoped_ptr rtp_rtcp_; - internal::TransportAdapter feedback_transport_; - scoped_ptr receive_stats_; - scoped_ptr payload_registry_; - scoped_ptr remote_bitrate_estimator_; - Clock* clock_; - const size_t num_expected_ssrcs_; - SsrcMap rtx_media_ssrcs_; - size_t total_sent_; - size_t padding_sent_; - size_t rtx_media_sent_; - int total_packets_sent_; - int padding_packets_sent_; - int rtx_media_packets_sent_; -}; - -class RampUpTest : public ::testing::TestWithParam { - public: - virtual void SetUp() { reserved_ssrcs_.clear(); } - - protected: - void RunRampUpTest(bool pacing, bool rtx) { - const size_t kNumberOfStreams = 3; - std::vector ssrcs; - for (size_t i = 0; i < kNumberOfStreams; ++i) - ssrcs.push_back(static_cast(i + 1)); - uint32_t kRtxSsrcs[kNumberOfStreams] = {111, 112, 113}; - StreamObserver::SsrcMap rtx_ssrc_map; - if (rtx) { - for (size_t i = 0; i < ssrcs.size(); ++i) - rtx_ssrc_map[kRtxSsrcs[i]] = ssrcs[i]; - } - test::DirectTransport receiver_transport; - int num_expected_ssrcs = kNumberOfStreams + (rtx ? 1 : 0); - StreamObserver stream_observer(num_expected_ssrcs, - rtx_ssrc_map, - &receiver_transport, - Clock::GetRealTimeClock()); - - Call::Config call_config(&stream_observer); - webrtc::Config webrtc_config; - call_config.webrtc_config = &webrtc_config; - webrtc_config.Set(new PaddingStrategy(rtx)); - scoped_ptr call(Call::Create(call_config)); - VideoSendStream::Config send_config = call->GetDefaultSendConfig(); - - receiver_transport.SetReceiver(call->Receiver()); - - test::FakeEncoder encoder(Clock::GetRealTimeClock()); - send_config.encoder = &encoder; - send_config.internal_source = false; - test::FakeEncoder::SetCodecSettings(&send_config.codec, kNumberOfStreams); - send_config.codec.plType = 125; - send_config.pacing = pacing; - send_config.rtp.nack.rtp_history_ms = 1000; - send_config.rtp.ssrcs.insert( - send_config.rtp.ssrcs.begin(), ssrcs.begin(), ssrcs.end()); - if (rtx) { - send_config.rtp.rtx.payload_type = 96; - send_config.rtp.rtx.ssrcs.insert(send_config.rtp.rtx.ssrcs.begin(), - kRtxSsrcs, - kRtxSsrcs + kNumberOfStreams); - } - send_config.rtp.extensions.push_back( - RtpExtension(RtpExtension::kAbsSendTime, kAbsoluteSendTimeExtensionId)); + receiver_transport.SetReceiver(sender_call_->Receiver()); + + if (num_streams == 1) { + encoder_config_.streams[0].target_bitrate_bps = 2000000; + encoder_config_.streams[0].max_bitrate_bps = 2000000; + } + + send_config_.rtp.nack.rtp_history_ms = kNackRtpHistoryMs; + send_config_.rtp.ssrcs = ssrcs; + if (rtx) { + send_config_.rtp.rtx.payload_type = kSendRtxPayloadType; + send_config_.rtp.rtx.ssrcs = rtx_ssrcs; + send_config_.rtp.rtx.pad_with_redundant_payloads = true; + } - VideoSendStream* send_stream = call->CreateVideoSendStream(send_config); + if (num_streams == 1) { + // For single stream rampup until 1mbps + stream_observer.set_expected_bitrate_bps(kSingleStreamTargetBps); + } else { + // For multi stream rampup until all streams are being sent. That means + // enough birate to send all the target streams plus the min bitrate of + // the last one. + int expected_bitrate_bps = encoder_config_.streams.back().min_bitrate_bps; + for (size_t i = 0; i < encoder_config_.streams.size() - 1; ++i) { + expected_bitrate_bps += encoder_config_.streams[i].target_bitrate_bps; + } + stream_observer.set_expected_bitrate_bps(expected_bitrate_bps); + } - scoped_ptr frame_generator_capturer( - test::FrameGeneratorCapturer::Create(send_stream->Input(), - send_config.codec.width, - send_config.codec.height, - 30, - Clock::GetRealTimeClock())); + CreateStreams(); + CreateFrameGeneratorCapturer(); - send_stream->StartSending(); - frame_generator_capturer->Start(); + Start(); - EXPECT_EQ(kEventSignaled, stream_observer.Wait()); + EXPECT_EQ(kEventSignaled, stream_observer.Wait()); - frame_generator_capturer->Stop(); - send_stream->StopSending(); + Stop(); + DestroyStreams(); +} - call->DestroyVideoSendStream(send_stream); +void RampUpTest::RunRampUpDownUpTest(size_t number_of_streams, bool rtx) { + test::DirectTransport receiver_transport; + LowRateStreamObserver stream_observer( + &receiver_transport, Clock::GetRealTimeClock(), number_of_streams, rtx); + + Call::Config call_config(&stream_observer); + CreateSenderCall(call_config); + receiver_transport.SetReceiver(sender_call_->Receiver()); + + CreateSendConfig(number_of_streams); + + send_config_.rtp.nack.rtp_history_ms = kNackRtpHistoryMs; + send_config_.rtp.extensions.push_back(RtpExtension( + RtpExtension::kTOffset, kTransmissionTimeOffsetExtensionId)); + send_config_.suspend_below_min_bitrate = true; + if (rtx) { + send_config_.rtp.rtx.payload_type = kSendRtxPayloadType; + send_config_.rtp.rtx.ssrcs = GenerateSsrcs(number_of_streams, 200); + send_config_.rtp.rtx.pad_with_redundant_payloads = true; } - std::map reserved_ssrcs_; -}; -TEST_F(RampUpTest, WithoutPacing) { RunRampUpTest(false, false); } + CreateStreams(); + stream_observer.SetSendStream(send_stream_); + + CreateFrameGeneratorCapturer(); + + Start(); + + EXPECT_EQ(kEventSignaled, stream_observer.Wait()); + + Stop(); + DestroyStreams(); +} + +TEST_F(RampUpTest, SingleStream) { + RunRampUpTest(false, 1, 0, RtpExtension::kTOffset); +} + +TEST_F(RampUpTest, Simulcast) { + RunRampUpTest(false, 3, 0, RtpExtension::kTOffset); +} + +TEST_F(RampUpTest, SimulcastWithRtx) { + RunRampUpTest(true, 3, 0, RtpExtension::kTOffset); +} + +TEST_F(RampUpTest, SingleStreamWithHighStartBitrate) { + RunRampUpTest(false, 1, 0.9 * kSingleStreamTargetBps, RtpExtension::kTOffset); +} + +TEST_F(RampUpTest, UpDownUpOneStream) { RunRampUpDownUpTest(1, false); } + +TEST_F(RampUpTest, UpDownUpThreeStreams) { RunRampUpDownUpTest(3, false); } -TEST_F(RampUpTest, WithPacing) { RunRampUpTest(true, false); } +TEST_F(RampUpTest, UpDownUpOneStreamRtx) { RunRampUpDownUpTest(1, true); } -TEST_F(RampUpTest, WithPacingAndRtx) { RunRampUpTest(true, true); } +TEST_F(RampUpTest, UpDownUpThreeStreamsRtx) { RunRampUpDownUpTest(3, true); } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/rampup_tests.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/rampup_tests.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/rampup_tests.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/rampup_tests.h 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_VIDEO_RAMPUP_TESTS_H_ +#define WEBRTC_VIDEO_RAMPUP_TESTS_H_ + +#include +#include +#include + +#include "webrtc/call.h" +#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" +#include "webrtc/system_wrappers/interface/event_wrapper.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/test/call_test.h" +#include "webrtc/video/transport_adapter.h" + +namespace webrtc { + +static const int kTransmissionTimeOffsetExtensionId = 6; +static const int kAbsSendTimeExtensionId = 7; +static const unsigned int kSingleStreamTargetBps = 1000000; + +class Clock; +class CriticalSectionWrapper; +class ReceiveStatistics; +class RtpHeaderParser; +class RTPPayloadRegistry; +class RtpRtcp; + +class StreamObserver : public newapi::Transport, public RemoteBitrateObserver { + public: + typedef std::map BytesSentMap; + typedef std::map SsrcMap; + StreamObserver(const SsrcMap& rtx_media_ssrcs, + newapi::Transport* feedback_transport, + Clock* clock, + RemoteBitrateEstimatorFactory* rbe_factory, + RateControlType control_type); + + void set_expected_bitrate_bps(unsigned int expected_bitrate_bps); + + void set_start_bitrate_bps(unsigned int start_bitrate_bps); + + virtual void OnReceiveBitrateChanged(const std::vector& ssrcs, + unsigned int bitrate) OVERRIDE; + + virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE; + + virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE; + + EventTypeWrapper Wait(); + + private: + void ReportResult(const std::string& measurement, + size_t value, + const std::string& units); + void TriggerTestDone() EXCLUSIVE_LOCKS_REQUIRED(crit_); + + Clock* const clock_; + const scoped_ptr test_done_; + const scoped_ptr rtp_parser_; + scoped_ptr rtp_rtcp_; + internal::TransportAdapter feedback_transport_; + const scoped_ptr receive_stats_; + const scoped_ptr payload_registry_; + scoped_ptr remote_bitrate_estimator_; + + const scoped_ptr crit_; + unsigned int expected_bitrate_bps_ GUARDED_BY(crit_); + unsigned int start_bitrate_bps_ GUARDED_BY(crit_); + SsrcMap rtx_media_ssrcs_ GUARDED_BY(crit_); + size_t total_sent_ GUARDED_BY(crit_); + size_t padding_sent_ GUARDED_BY(crit_); + size_t rtx_media_sent_ GUARDED_BY(crit_); + int total_packets_sent_ GUARDED_BY(crit_); + int padding_packets_sent_ GUARDED_BY(crit_); + int rtx_media_packets_sent_ GUARDED_BY(crit_); + int64_t test_start_ms_ GUARDED_BY(crit_); + int64_t ramp_up_finished_ms_ GUARDED_BY(crit_); +}; + +class LowRateStreamObserver : public test::DirectTransport, + public RemoteBitrateObserver, + public PacketReceiver { + public: + LowRateStreamObserver(newapi::Transport* feedback_transport, + Clock* clock, + size_t number_of_streams, + bool rtx_used); + + virtual void SetSendStream(const VideoSendStream* send_stream); + + virtual void OnReceiveBitrateChanged(const std::vector& ssrcs, + unsigned int bitrate); + + virtual bool SendRtp(const uint8_t* data, size_t length) OVERRIDE; + + virtual DeliveryStatus DeliverPacket(const uint8_t* packet, + size_t length) OVERRIDE; + + virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE; + + // Produces a string similar to "1stream_nortx", depending on the values of + // number_of_streams_ and rtx_used_; + std::string GetModifierString(); + + // This method defines the state machine for the ramp up-down-up test. + void EvolveTestState(unsigned int bitrate_bps); + + EventTypeWrapper Wait(); + + private: + static const unsigned int kHighBandwidthLimitBps = 80000; + static const unsigned int kExpectedHighBitrateBps = 60000; + static const unsigned int kLowBandwidthLimitBps = 20000; + static const unsigned int kExpectedLowBitrateBps = 20000; + enum TestStates { kFirstRampup, kLowRate, kSecondRampup }; + + Clock* const clock_; + const size_t number_of_streams_; + const bool rtx_used_; + const scoped_ptr test_done_; + const scoped_ptr rtp_parser_; + scoped_ptr rtp_rtcp_; + internal::TransportAdapter feedback_transport_; + const scoped_ptr receive_stats_; + scoped_ptr remote_bitrate_estimator_; + + scoped_ptr crit_; + const VideoSendStream* send_stream_ GUARDED_BY(crit_); + FakeNetworkPipe::Config forward_transport_config_ GUARDED_BY(crit_); + TestStates test_state_ GUARDED_BY(crit_); + int64_t state_start_ms_ GUARDED_BY(crit_); + int64_t interval_start_ms_ GUARDED_BY(crit_); + unsigned int last_remb_bps_ GUARDED_BY(crit_); + size_t sent_bytes_ GUARDED_BY(crit_); + size_t total_overuse_bytes_ GUARDED_BY(crit_); + bool suspended_in_stats_ GUARDED_BY(crit_); +}; + +class RampUpTest : public test::CallTest { + protected: + void RunRampUpTest(bool rtx, + size_t num_streams, + unsigned int start_bitrate_bps, + const std::string& extension_type); + + void RunRampUpDownUpTest(size_t number_of_streams, bool rtx); +}; +} // namespace webrtc +#endif // WEBRTC_VIDEO_RAMPUP_TESTS_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/receive_statistics_proxy.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/receive_statistics_proxy.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/receive_statistics_proxy.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/receive_statistics_proxy.cc 2015-02-03 14:33:38.000000000 +0000 @@ -22,14 +22,14 @@ ViECodec* codec, int channel) : channel_(channel), - lock_(CriticalSectionWrapper::CreateCriticalSection()), clock_(clock), + codec_(codec), + rtp_rtcp_(rtp_rtcp), + crit_(CriticalSectionWrapper::CreateCriticalSection()), // 1000ms window, scale 1000 for ms to s. decode_fps_estimator_(1000, 1000), renders_fps_estimator_(1000, 1000), - receive_state_(kReceiveStateInitial), - codec_(codec), - rtp_rtcp_(rtp_rtcp) { + receive_state_(kReceiveStateInitial) { stats_.ssrc = ssrc; } @@ -38,12 +38,12 @@ VideoReceiveStream::Stats ReceiveStatisticsProxy::GetStats() const { VideoReceiveStream::Stats stats; { - CriticalSectionScoped cs(lock_.get()); + CriticalSectionScoped lock(crit_.get()); stats = stats_; } stats.c_name = GetCName(); codec_->GetReceiveSideDelay(channel_, &stats.avg_delay_ms); - stats.discarded_packets = codec_->GetDiscardedPackets(channel_); + stats.discarded_packets = codec_->GetNumDiscardedPackets(channel_); codec_->GetReceiveCodecStatistics( channel_, stats.key_frames, stats.delta_frames); @@ -59,10 +59,10 @@ void ReceiveStatisticsProxy::IncomingRate(const int video_channel, const unsigned int framerate, - const unsigned int bitrate) { - CriticalSectionScoped cs(lock_.get()); + const unsigned int bitrate_bps) { + CriticalSectionScoped lock(crit_.get()); stats_.network_frame_rate = framerate; - stats_.bitrate_bps = bitrate; + stats_.total_bitrate_bps = bitrate_bps; } void ReceiveStatisticsProxy::ReceiveStateChange(const int video_channel, @@ -74,7 +74,7 @@ void ReceiveStatisticsProxy::StatisticsUpdated( const webrtc::RtcpStatistics& statistics, uint32_t ssrc) { - CriticalSectionScoped cs(lock_.get()); + CriticalSectionScoped lock(crit_.get()); stats_.rtcp_stats = statistics; } @@ -82,7 +82,7 @@ void ReceiveStatisticsProxy::DataCountersUpdated( const webrtc::StreamDataCounters& counters, uint32_t ssrc) { - CriticalSectionScoped cs(lock_.get()); + CriticalSectionScoped lock(crit_.get()); stats_.rtp_stats = counters; } @@ -90,7 +90,7 @@ void ReceiveStatisticsProxy::OnDecodedFrame() { uint64_t now = clock_->TimeInMilliseconds(); - CriticalSectionScoped cs(lock_.get()); + CriticalSectionScoped lock(crit_.get()); decode_fps_estimator_.Update(1, now); stats_.decode_frame_rate = decode_fps_estimator_.Rate(now); } @@ -98,7 +98,7 @@ void ReceiveStatisticsProxy::OnRenderedFrame() { uint64_t now = clock_->TimeInMilliseconds(); - CriticalSectionScoped cs(lock_.get()); + CriticalSectionScoped lock(crit_.get()); renders_fps_estimator_.Update(1, now); stats_.render_frame_rate = renders_fps_estimator_.Rate(now); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/receive_statistics_proxy.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/receive_statistics_proxy.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/receive_statistics_proxy.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/receive_statistics_proxy.h 2015-02-03 14:33:38.000000000 +0000 @@ -13,6 +13,7 @@ #include +#include "webrtc/base/thread_annotations.h" #include "webrtc/common_types.h" #include "webrtc/frame_callback.h" #include "webrtc/modules/remote_bitrate_estimator/rate_statistics.h" @@ -51,7 +52,7 @@ const VideoCodec& video_codec) OVERRIDE {} virtual void IncomingRate(const int video_channel, const unsigned int framerate, - const unsigned int bitrate) OVERRIDE; + const unsigned int bitrate_bps) OVERRIDE; virtual void DecoderTiming(int decode_ms, int max_decode_ms, int current_delay_ms, @@ -74,14 +75,15 @@ std::string GetCName() const; const int channel_; - scoped_ptr lock_; - Clock* clock_; - VideoReceiveStream::Stats stats_; - RateStatistics decode_fps_estimator_; - RateStatistics renders_fps_estimator_; - VideoReceiveState receive_state_; - ViECodec* codec_; - ViERTP_RTCP* rtp_rtcp_; + Clock* const clock_; + ViECodec* const codec_; + ViERTP_RTCP* const rtp_rtcp_; + + scoped_ptr crit_; + VideoReceiveStream::Stats stats_ GUARDED_BY(crit_); + RateStatistics decode_fps_estimator_ GUARDED_BY(crit_); + RateStatistics renders_fps_estimator_ GUARDED_BY(crit_); + VideoReceiveState receive_state_ GUARDED_BY(crit_); }; } // namespace internal diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/replay.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/replay.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/replay.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/replay.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include +#include + +#include "gflags/gflags.h" +#include "testing/gtest/include/gtest/gtest.h" + +#include "webrtc/call.h" +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/system_wrappers/interface/sleep.h" +#include "webrtc/test/encoder_settings.h" +#include "webrtc/test/null_transport.h" +#include "webrtc/test/rtp_file_reader.h" +#include "webrtc/test/run_loop.h" +#include "webrtc/test/run_test.h" +#include "webrtc/test/video_capturer.h" +#include "webrtc/test/video_renderer.h" +#include "webrtc/typedefs.h" +#include "webrtc/video_decoder.h" + +namespace webrtc { +namespace flags { + +// TODO(pbos): Multiple receivers. + +// Flag for payload type. +static bool ValidatePayloadType(const char* flagname, int32_t payload_type) { + return payload_type > 0 && payload_type <= 127; +} +DEFINE_int32(payload_type, 0, "Payload type"); +static int PayloadType() { return static_cast(FLAGS_payload_type); } +static const bool payload_dummy = + google::RegisterFlagValidator(&FLAGS_payload_type, &ValidatePayloadType); + +// Flag for SSRC. +static bool ValidateSsrc(const char* flagname, uint64_t ssrc) { + return ssrc > 0 && ssrc <= 0xFFFFFFFFu; +} + +DEFINE_uint64(ssrc, 0, "Incoming SSRC"); +static uint32_t Ssrc() { return static_cast(FLAGS_ssrc); } +static const bool ssrc_dummy = + google::RegisterFlagValidator(&FLAGS_ssrc, &ValidateSsrc); + +static bool ValidateOptionalPayloadType(const char* flagname, + int32_t payload_type) { + return payload_type == -1 || ValidatePayloadType(flagname, payload_type); +} + +// Flag for RED payload type. +DEFINE_int32(red_payload_type, -1, "RED payload type"); +static int RedPayloadType() { + return static_cast(FLAGS_red_payload_type); +} +static const bool red_dummy = + google::RegisterFlagValidator(&FLAGS_red_payload_type, + &ValidateOptionalPayloadType); + +// Flag for ULPFEC payload type. +DEFINE_int32(fec_payload_type, -1, "ULPFEC payload type"); +static int FecPayloadType() { + return static_cast(FLAGS_fec_payload_type); +} +static const bool fec_dummy = + google::RegisterFlagValidator(&FLAGS_fec_payload_type, + &ValidateOptionalPayloadType); + +// Flag for abs-send-time id. +static bool ValidateRtpHeaderExtensionId(const char* flagname, + int32_t extension_id) { + return extension_id >= -1 || extension_id < 15; +} +DEFINE_int32(abs_send_time_id, -1, "RTP extension ID for abs-send-time"); +static int AbsSendTimeId() { return static_cast(FLAGS_abs_send_time_id); } +static const bool abs_send_time_dummy = + google::RegisterFlagValidator(&FLAGS_abs_send_time_id, + &ValidateRtpHeaderExtensionId); + +// Flag for transmission-offset id. +DEFINE_int32(transmission_offset_id, + -1, + "RTP extension ID for transmission-offset"); +static int TransmissionOffsetId() { + return static_cast(FLAGS_transmission_offset_id); +} +static const bool timestamp_offset_dummy = + google::RegisterFlagValidator(&FLAGS_transmission_offset_id, + &ValidateRtpHeaderExtensionId); + +// Flag for rtpdump input file. +bool ValidateInputFilenameNotEmpty(const char* flagname, + const std::string& string) { + return string != ""; +} +DEFINE_string(input_file, "", "input file"); +static std::string InputFile() { + return static_cast(FLAGS_input_file); +} +static const bool input_file_dummy = + google::RegisterFlagValidator(&FLAGS_input_file, + &ValidateInputFilenameNotEmpty); + +// Flag for raw output files. +DEFINE_string(out_base, "", "Basename (excluding .yuv) for raw output"); +static std::string OutBase() { + return static_cast(FLAGS_out_base); +} + +// Flag for video codec. +DEFINE_string(codec, "VP8", "Video codec"); +static std::string Codec() { return static_cast(FLAGS_codec); } + +} // namespace flags + +static const uint32_t kReceiverLocalSsrc = 0x123456; + +class FileRenderPassthrough : public VideoRenderer { + public: + FileRenderPassthrough(const std::string& basename, VideoRenderer* renderer) + : basename_(basename), + renderer_(renderer), + file_(NULL), + count_(0), + last_width_(0), + last_height_(0) {} + + ~FileRenderPassthrough() { + if (file_ != NULL) + fclose(file_); + } + + private: + virtual void RenderFrame(const I420VideoFrame& video_frame, + int time_to_render_ms) OVERRIDE { + if (renderer_ != NULL) + renderer_->RenderFrame(video_frame, time_to_render_ms); + if (basename_ == "") + return; + if (last_width_ != video_frame.width() || + last_height_ != video_frame.height()) { + if (file_ != NULL) + fclose(file_); + std::stringstream filename; + filename << basename_; + if (++count_ > 1) + filename << '-' << count_; + filename << '_' << video_frame.width() << 'x' << video_frame.height() + << ".yuv"; + file_ = fopen(filename.str().c_str(), "wb"); + if (file_ == NULL) { + fprintf(stderr, + "Couldn't open file for writing: %s\n", + filename.str().c_str()); + } + } + last_width_ = video_frame.width(); + last_height_ = video_frame.height(); + if (file_ == NULL) + return; + PrintI420VideoFrame(video_frame, file_); + } + + const std::string basename_; + VideoRenderer* const renderer_; + FILE* file_; + size_t count_; + int last_width_; + int last_height_; +}; + +void RtpReplay() { + scoped_ptr playback_video(test::VideoRenderer::Create( + "Playback Video", 640, 480)); + FileRenderPassthrough file_passthrough(flags::OutBase(), + playback_video.get()); + + // TODO(pbos): Might be good to have a transport that prints keyframe requests + // etc. + test::NullTransport transport; + Call::Config call_config(&transport); + scoped_ptr call(Call::Create(call_config)); + + VideoReceiveStream::Config receive_config; + receive_config.rtp.remote_ssrc = flags::Ssrc(); + receive_config.rtp.local_ssrc = kReceiverLocalSsrc; + receive_config.rtp.fec.ulpfec_payload_type = flags::FecPayloadType(); + receive_config.rtp.fec.red_payload_type = flags::RedPayloadType(); + receive_config.rtp.nack.rtp_history_ms = 1000; + if (flags::TransmissionOffsetId() != -1) { + receive_config.rtp.extensions.push_back( + RtpExtension(RtpExtension::kTOffset, flags::TransmissionOffsetId())); + } + if (flags::AbsSendTimeId() != -1) { + receive_config.rtp.extensions.push_back( + RtpExtension(RtpExtension::kAbsSendTime, flags::AbsSendTimeId())); + } + receive_config.renderer = &file_passthrough; + + VideoSendStream::Config::EncoderSettings encoder_settings; + encoder_settings.payload_name = flags::Codec(); + encoder_settings.payload_type = flags::PayloadType(); + VideoReceiveStream::Decoder decoder = + test::CreateMatchingDecoder(encoder_settings); + receive_config.decoders.push_back(decoder); + + VideoReceiveStream* receive_stream = + call->CreateVideoReceiveStream(receive_config); + + scoped_ptr rtp_reader(test::RtpFileReader::Create( + test::RtpFileReader::kRtpDump, flags::InputFile())); + if (rtp_reader.get() == NULL) { + rtp_reader.reset(test::RtpFileReader::Create(test::RtpFileReader::kPcap, + flags::InputFile())); + if (rtp_reader.get() == NULL) { + fprintf(stderr, + "Couldn't open input file as either a rtpdump or .pcap. Note " + "that .pcapng is not supported.\n"); + return; + } + } + receive_stream->Start(); + + uint32_t last_time_ms = 0; + int num_packets = 0; + std::map unknown_packets; + while (true) { + test::RtpFileReader::Packet packet; + if (!rtp_reader->NextPacket(&packet)) + break; + ++num_packets; + switch (call->Receiver()->DeliverPacket(packet.data, packet.length)) { + case PacketReceiver::DELIVERY_OK: + break; + case PacketReceiver::DELIVERY_UNKNOWN_SSRC: { + RTPHeader header; + scoped_ptr parser(RtpHeaderParser::Create()); + parser->Parse(packet.data, packet.length, &header); + if (unknown_packets[header.ssrc] == 0) + fprintf(stderr, "Unknown SSRC: %u!\n", header.ssrc); + ++unknown_packets[header.ssrc]; + break; + } + case PacketReceiver::DELIVERY_PACKET_ERROR: + fprintf(stderr, "Packet error, corrupt packets or incorrect setup?\n"); + break; + } + if (last_time_ms != 0 && last_time_ms != packet.time_ms) { + SleepMs(packet.time_ms - last_time_ms); + } + last_time_ms = packet.time_ms; + } + fprintf(stderr, "num_packets: %d\n", num_packets); + + for (std::map::const_iterator it = unknown_packets.begin(); + it != unknown_packets.end(); + ++it) { + fprintf( + stderr, "Packets for unknown ssrc '%u': %d\n", it->first, it->second); + } + + call->DestroyVideoReceiveStream(receive_stream); + + delete decoder.decoder; +} +} // namespace webrtc + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + google::ParseCommandLineFlags(&argc, &argv, true); + + webrtc::test::RunTest(webrtc::RtpReplay); + return 0; +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/send_statistics_proxy.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/send_statistics_proxy.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/send_statistics_proxy.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/send_statistics_proxy.cc 2015-02-03 14:33:38.000000000 +0000 @@ -17,54 +17,57 @@ namespace webrtc { SendStatisticsProxy::SendStatisticsProxy( - const VideoSendStream::Config& config, - SendStatisticsProxy::StatsProvider* stats_provider) + const VideoSendStream::Config& config) : config_(config), - lock_(CriticalSectionWrapper::CreateCriticalSection()), - stats_provider_(stats_provider) {} + crit_(CriticalSectionWrapper::CreateCriticalSection()) { +} SendStatisticsProxy::~SendStatisticsProxy() {} void SendStatisticsProxy::OutgoingRate(const int video_channel, const unsigned int framerate, const unsigned int bitrate) { - CriticalSectionScoped cs(lock_.get()); + CriticalSectionScoped lock(crit_.get()); stats_.encode_frame_rate = framerate; + stats_.media_bitrate_bps = bitrate; +} + +void SendStatisticsProxy::SuspendChange(int video_channel, bool is_suspended) { + CriticalSectionScoped lock(crit_.get()); + stats_.suspended = is_suspended; } void SendStatisticsProxy::CapturedFrameRate(const int capture_id, const unsigned char frame_rate) { - CriticalSectionScoped cs(lock_.get()); + CriticalSectionScoped lock(crit_.get()); stats_.input_frame_rate = frame_rate; } VideoSendStream::Stats SendStatisticsProxy::GetStats() const { - VideoSendStream::Stats stats; - { - CriticalSectionScoped cs(lock_.get()); - stats = stats_; - } - stats_provider_->GetSendSideDelay(&stats); - stats.c_name = stats_provider_->GetCName(); - return stats; + CriticalSectionScoped lock(crit_.get()); + return stats_; } -StreamStats* SendStatisticsProxy::GetStatsEntry(uint32_t ssrc) { - std::map::iterator it = stats_.substreams.find(ssrc); +SsrcStats* SendStatisticsProxy::GetStatsEntry(uint32_t ssrc) { + std::map::iterator it = stats_.substreams.find(ssrc); if (it != stats_.substreams.end()) return &it->second; if (std::find(config_.rtp.ssrcs.begin(), config_.rtp.ssrcs.end(), ssrc) == - config_.rtp.ssrcs.end()) + config_.rtp.ssrcs.end() && + std::find(config_.rtp.rtx.ssrcs.begin(), + config_.rtp.rtx.ssrcs.end(), + ssrc) == config_.rtp.rtx.ssrcs.end()) { return NULL; + } return &stats_.substreams[ssrc]; // Insert new entry and return ptr. } void SendStatisticsProxy::StatisticsUpdated(const RtcpStatistics& statistics, uint32_t ssrc) { - CriticalSectionScoped cs(lock_.get()); - StreamStats* stats = GetStatsEntry(ssrc); + CriticalSectionScoped lock(crit_.get()); + SsrcStats* stats = GetStatsEntry(ssrc); if (stats == NULL) return; @@ -74,29 +77,31 @@ void SendStatisticsProxy::DataCountersUpdated( const StreamDataCounters& counters, uint32_t ssrc) { - CriticalSectionScoped cs(lock_.get()); - StreamStats* stats = GetStatsEntry(ssrc); + CriticalSectionScoped lock(crit_.get()); + SsrcStats* stats = GetStatsEntry(ssrc); if (stats == NULL) return; stats->rtp_stats = counters; } -void SendStatisticsProxy::Notify(const BitrateStatistics& bitrate, +void SendStatisticsProxy::Notify(const BitrateStatistics& total_stats, + const BitrateStatistics& retransmit_stats, uint32_t ssrc) { - CriticalSectionScoped cs(lock_.get()); - StreamStats* stats = GetStatsEntry(ssrc); + CriticalSectionScoped lock(crit_.get()); + SsrcStats* stats = GetStatsEntry(ssrc); if (stats == NULL) return; - stats->bitrate_bps = bitrate.bitrate_bps; + stats->total_bitrate_bps = total_stats.bitrate_bps; + stats->retransmit_bitrate_bps = retransmit_stats.bitrate_bps; } void SendStatisticsProxy::FrameCountUpdated(FrameType frame_type, uint32_t frame_count, const unsigned int ssrc) { - CriticalSectionScoped cs(lock_.get()); - StreamStats* stats = GetStatsEntry(ssrc); + CriticalSectionScoped lock(crit_.get()); + SsrcStats* stats = GetStatsEntry(ssrc); if (stats == NULL) return; @@ -114,4 +119,15 @@ } } +void SendStatisticsProxy::SendSideDelayUpdated(int avg_delay_ms, + int max_delay_ms, + uint32_t ssrc) { + CriticalSectionScoped lock(crit_.get()); + SsrcStats* stats = GetStatsEntry(ssrc); + if (stats == NULL) + return; + stats->avg_delay_ms = avg_delay_ms; + stats->max_delay_ms = max_delay_ms; +} + } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/send_statistics_proxy.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/send_statistics_proxy.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/send_statistics_proxy.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/send_statistics_proxy.h 2015-02-03 14:33:38.000000000 +0000 @@ -13,6 +13,7 @@ #include +#include "webrtc/base/thread_annotations.h" #include "webrtc/common_types.h" #include "webrtc/video_engine/include/vie_codec.h" #include "webrtc/video_engine/include/vie_capture.h" @@ -28,20 +29,10 @@ public BitrateStatisticsObserver, public FrameCountObserver, public ViEEncoderObserver, - public ViECaptureObserver { + public ViECaptureObserver, + public SendSideDelayObserver { public: - class StatsProvider { - protected: - StatsProvider() {} - virtual ~StatsProvider() {} - - public: - virtual bool GetSendSideDelay(VideoSendStream::Stats* stats) = 0; - virtual std::string GetCName() = 0; - }; - - SendStatisticsProxy(const VideoSendStream::Config& config, - StatsProvider* stats_provider); + explicit SendStatisticsProxy(const VideoSendStream::Config& config); virtual ~SendStatisticsProxy(); VideoSendStream::Stats GetStats() const; @@ -55,7 +46,9 @@ uint32_t ssrc) OVERRIDE; // From BitrateStatisticsObserver. - virtual void Notify(const BitrateStatistics& stats, uint32_t ssrc) OVERRIDE; + virtual void Notify(const BitrateStatistics& total_stats, + const BitrateStatistics& retransmit_stats, + uint32_t ssrc) OVERRIDE; // From FrameCountObserver. virtual void FrameCountUpdated(FrameType frame_type, @@ -67,7 +60,7 @@ const unsigned int framerate, const unsigned int bitrate) OVERRIDE; - virtual void SuspendChange(int video_channel, bool is_suspended) OVERRIDE {} + virtual void SuspendChange(int video_channel, bool is_suspended) OVERRIDE; // From ViECaptureObserver. virtual void BrightnessAlarm(const int capture_id, @@ -79,13 +72,16 @@ virtual void NoPictureAlarm(const int capture_id, const CaptureAlarm alarm) OVERRIDE {} + virtual void SendSideDelayUpdated(int avg_delay_ms, + int max_delay_ms, + uint32_t ssrc) OVERRIDE; + private: - StreamStats* GetStatsEntry(uint32_t ssrc); + SsrcStats* GetStatsEntry(uint32_t ssrc) EXCLUSIVE_LOCKS_REQUIRED(crit_); const VideoSendStream::Config config_; - scoped_ptr lock_; - VideoSendStream::Stats stats_; - StatsProvider* stats_provider_; + scoped_ptr crit_; + VideoSendStream::Stats stats_ GUARDED_BY(crit_); }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/send_statistics_proxy_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/send_statistics_proxy_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/send_statistics_proxy_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/send_statistics_proxy_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -19,8 +19,7 @@ namespace webrtc { -class SendStatisticsProxyTest : public ::testing::Test, - protected SendStatisticsProxy::StatsProvider { +class SendStatisticsProxyTest : public ::testing::Test { public: SendStatisticsProxyTest() : avg_delay_ms_(0), max_delay_ms_(0) {} virtual ~SendStatisticsProxyTest() {} @@ -28,7 +27,7 @@ protected: virtual void SetUp() { statistics_proxy_.reset( - new SendStatisticsProxy(GetTestConfig(), this)); + new SendStatisticsProxy(GetTestConfig())); config_ = GetTestConfig(); expected_ = VideoSendStream::Stats(); } @@ -37,39 +36,33 @@ VideoSendStream::Config config; config.rtp.ssrcs.push_back(17); config.rtp.ssrcs.push_back(42); + config.rtp.rtx.ssrcs.push_back(18); + config.rtp.rtx.ssrcs.push_back(43); return config; } - virtual bool GetSendSideDelay(VideoSendStream::Stats* stats) OVERRIDE { - stats->avg_delay_ms = avg_delay_ms_; - stats->max_delay_ms = max_delay_ms_; - return true; - } - - virtual std::string GetCName() { return cname_; } - void ExpectEqual(VideoSendStream::Stats one, VideoSendStream::Stats other) { - EXPECT_EQ(one.avg_delay_ms, other.avg_delay_ms); EXPECT_EQ(one.input_frame_rate, other.input_frame_rate); EXPECT_EQ(one.encode_frame_rate, other.encode_frame_rate); - EXPECT_EQ(one.avg_delay_ms, other.avg_delay_ms); - EXPECT_EQ(one.max_delay_ms, other.max_delay_ms); - EXPECT_EQ(one.c_name, other.c_name); + EXPECT_EQ(one.media_bitrate_bps, other.media_bitrate_bps); + EXPECT_EQ(one.suspended, other.suspended); EXPECT_EQ(one.substreams.size(), other.substreams.size()); - for (std::map::const_iterator it = + for (std::map::const_iterator it = one.substreams.begin(); it != one.substreams.end(); ++it) { - std::map::const_iterator corresponding_it = + std::map::const_iterator corresponding_it = other.substreams.find(it->first); ASSERT_TRUE(corresponding_it != other.substreams.end()); - const StreamStats& a = it->second; - const StreamStats& b = corresponding_it->second; + const SsrcStats& a = it->second; + const SsrcStats& b = corresponding_it->second; EXPECT_EQ(a.key_frames, b.key_frames); EXPECT_EQ(a.delta_frames, b.delta_frames); - EXPECT_EQ(a.bitrate_bps, b.bitrate_bps); + EXPECT_EQ(a.total_bitrate_bps, b.total_bitrate_bps); + EXPECT_EQ(a.avg_delay_ms, b.avg_delay_ms); + EXPECT_EQ(a.max_delay_ms, b.max_delay_ms); EXPECT_EQ(a.rtp_stats.bytes, b.rtp_stats.bytes); EXPECT_EQ(a.rtp_stats.header_bytes, b.rtp_stats.header_bytes); @@ -91,9 +84,8 @@ VideoSendStream::Config config_; int avg_delay_ms_; int max_delay_ms_; - std::string cname_; VideoSendStream::Stats expected_; - typedef std::map::const_iterator StreamIterator; + typedef std::map::const_iterator StreamIterator; }; TEST_F(SendStatisticsProxyTest, RtcpStatistics) { @@ -102,7 +94,7 @@ it != config_.rtp.ssrcs.end(); ++it) { const uint32_t ssrc = *it; - StreamStats& ssrc_stats = expected_.substreams[ssrc]; + SsrcStats& ssrc_stats = expected_.substreams[ssrc]; // Add statistics with some arbitrary, but unique, numbers. uint32_t offset = ssrc * sizeof(RtcpStatistics); @@ -112,25 +104,60 @@ ssrc_stats.rtcp_stats.jitter = offset + 3; callback->StatisticsUpdated(ssrc_stats.rtcp_stats, ssrc); } + for (std::vector::const_iterator it = config_.rtp.rtx.ssrcs.begin(); + it != config_.rtp.rtx.ssrcs.end(); + ++it) { + const uint32_t ssrc = *it; + SsrcStats& ssrc_stats = expected_.substreams[ssrc]; + // Add statistics with some arbitrary, but unique, numbers. + uint32_t offset = ssrc * sizeof(RtcpStatistics); + ssrc_stats.rtcp_stats.cumulative_lost = offset; + ssrc_stats.rtcp_stats.extended_max_sequence_number = offset + 1; + ssrc_stats.rtcp_stats.fraction_lost = offset + 2; + ssrc_stats.rtcp_stats.jitter = offset + 3; + callback->StatisticsUpdated(ssrc_stats.rtcp_stats, ssrc); + } VideoSendStream::Stats stats = statistics_proxy_->GetStats(); ExpectEqual(expected_, stats); } -TEST_F(SendStatisticsProxyTest, FrameRates) { +TEST_F(SendStatisticsProxyTest, CaptureFramerate) { const int capture_fps = 31; - const int encode_fps = 29; ViECaptureObserver* capture_observer = statistics_proxy_.get(); capture_observer->CapturedFrameRate(0, capture_fps); - ViEEncoderObserver* encoder_observer = statistics_proxy_.get(); - encoder_observer->OutgoingRate(0, encode_fps, 0); VideoSendStream::Stats stats = statistics_proxy_->GetStats(); EXPECT_EQ(capture_fps, stats.input_frame_rate); +} + +TEST_F(SendStatisticsProxyTest, EncodedBitrateAndFramerate) { + const int media_bitrate_bps = 500; + const int encode_fps = 29; + + ViEEncoderObserver* encoder_observer = statistics_proxy_.get(); + encoder_observer->OutgoingRate(0, encode_fps, media_bitrate_bps); + + VideoSendStream::Stats stats = statistics_proxy_->GetStats(); + EXPECT_EQ(media_bitrate_bps, stats.media_bitrate_bps); EXPECT_EQ(encode_fps, stats.encode_frame_rate); } +TEST_F(SendStatisticsProxyTest, Suspended) { + // Verify that the value is false by default. + EXPECT_FALSE(statistics_proxy_->GetStats().suspended); + + // Verify that we can set it to true. + ViEEncoderObserver* encoder_observer = statistics_proxy_.get(); + encoder_observer->SuspendChange(0, true); + EXPECT_TRUE(statistics_proxy_->GetStats().suspended); + + // Verify that we can set it back to false again. + encoder_observer->SuspendChange(0, false); + EXPECT_FALSE(statistics_proxy_->GetStats().suspended); +} + TEST_F(SendStatisticsProxyTest, FrameCounts) { FrameCountObserver* observer = statistics_proxy_.get(); for (std::vector::const_iterator it = config_.rtp.ssrcs.begin(); @@ -138,8 +165,20 @@ ++it) { const uint32_t ssrc = *it; // Add statistics with some arbitrary, but unique, numbers. - StreamStats& stats = expected_.substreams[ssrc]; - uint32_t offset = ssrc * sizeof(StreamStats); + SsrcStats& stats = expected_.substreams[ssrc]; + uint32_t offset = ssrc * sizeof(SsrcStats); + stats.key_frames = offset; + stats.delta_frames = offset + 1; + observer->FrameCountUpdated(kVideoFrameKey, stats.key_frames, ssrc); + observer->FrameCountUpdated(kVideoFrameDelta, stats.delta_frames, ssrc); + } + for (std::vector::const_iterator it = config_.rtp.rtx.ssrcs.begin(); + it != config_.rtp.rtx.ssrcs.end(); + ++it) { + const uint32_t ssrc = *it; + // Add statistics with some arbitrary, but unique, numbers. + SsrcStats& stats = expected_.substreams[ssrc]; + uint32_t offset = ssrc * sizeof(SsrcStats); stats.key_frames = offset; stats.delta_frames = offset + 1; observer->FrameCountUpdated(kVideoFrameKey, stats.key_frames, ssrc); @@ -167,6 +206,21 @@ counters.packets = offset + 5; callback->DataCountersUpdated(counters, ssrc); } + for (std::vector::const_iterator it = config_.rtp.rtx.ssrcs.begin(); + it != config_.rtp.rtx.ssrcs.end(); + ++it) { + const uint32_t ssrc = *it; + StreamDataCounters& counters = expected_.substreams[ssrc].rtp_stats; + // Add statistics with some arbitrary, but unique, numbers. + uint32_t offset = ssrc * sizeof(StreamDataCounters); + counters.bytes = offset; + counters.header_bytes = offset + 1; + counters.fec_packets = offset + 2; + counters.padding_bytes = offset + 3; + counters.retransmitted_packets = offset + 4; + counters.packets = offset + 5; + callback->DataCountersUpdated(counters, ssrc); + } VideoSendStream::Stats stats = statistics_proxy_->GetStats(); ExpectEqual(expected_, stats); @@ -178,31 +232,70 @@ it != config_.rtp.ssrcs.end(); ++it) { const uint32_t ssrc = *it; - BitrateStatistics bitrate; - bitrate.bitrate_bps = ssrc; - observer->Notify(bitrate, ssrc); - expected_.substreams[ssrc].bitrate_bps = ssrc; + BitrateStatistics total; + BitrateStatistics retransmit; + // Use ssrc as bitrate_bps to get a unique value for each stream. + total.bitrate_bps = ssrc; + retransmit.bitrate_bps = ssrc + 1; + observer->Notify(total, retransmit, ssrc); + expected_.substreams[ssrc].total_bitrate_bps = total.bitrate_bps; + expected_.substreams[ssrc].retransmit_bitrate_bps = retransmit.bitrate_bps; + } + for (std::vector::const_iterator it = config_.rtp.rtx.ssrcs.begin(); + it != config_.rtp.rtx.ssrcs.end(); + ++it) { + const uint32_t ssrc = *it; + BitrateStatistics total; + BitrateStatistics retransmit; + // Use ssrc as bitrate_bps to get a unique value for each stream. + total.bitrate_bps = ssrc; + retransmit.bitrate_bps = ssrc + 1; + observer->Notify(total, retransmit, ssrc); + expected_.substreams[ssrc].total_bitrate_bps = total.bitrate_bps; + expected_.substreams[ssrc].retransmit_bitrate_bps = retransmit.bitrate_bps; } VideoSendStream::Stats stats = statistics_proxy_->GetStats(); ExpectEqual(expected_, stats); } -TEST_F(SendStatisticsProxyTest, StreamStats) { - avg_delay_ms_ = 1; - max_delay_ms_ = 2; - cname_ = "qwertyuiop"; - +TEST_F(SendStatisticsProxyTest, SendSideDelay) { + SendSideDelayObserver* observer = statistics_proxy_.get(); + for (std::vector::const_iterator it = config_.rtp.ssrcs.begin(); + it != config_.rtp.ssrcs.end(); + ++it) { + const uint32_t ssrc = *it; + // Use ssrc as avg_delay_ms and max_delay_ms to get a unique value for each + // stream. + int avg_delay_ms = ssrc; + int max_delay_ms = ssrc + 1; + observer->SendSideDelayUpdated(avg_delay_ms, max_delay_ms, ssrc); + expected_.substreams[ssrc].avg_delay_ms = avg_delay_ms; + expected_.substreams[ssrc].max_delay_ms = max_delay_ms; + } + for (std::vector::const_iterator it = config_.rtp.rtx.ssrcs.begin(); + it != config_.rtp.rtx.ssrcs.end(); + ++it) { + const uint32_t ssrc = *it; + // Use ssrc as avg_delay_ms and max_delay_ms to get a unique value for each + // stream. + int avg_delay_ms = ssrc; + int max_delay_ms = ssrc + 1; + observer->SendSideDelayUpdated(avg_delay_ms, max_delay_ms, ssrc); + expected_.substreams[ssrc].avg_delay_ms = avg_delay_ms; + expected_.substreams[ssrc].max_delay_ms = max_delay_ms; + } VideoSendStream::Stats stats = statistics_proxy_->GetStats(); - - EXPECT_EQ(avg_delay_ms_, stats.avg_delay_ms); - EXPECT_EQ(max_delay_ms_, stats.max_delay_ms); - EXPECT_EQ(cname_, stats.c_name); + ExpectEqual(expected_, stats); } TEST_F(SendStatisticsProxyTest, NoSubstreams) { uint32_t exluded_ssrc = - *std::max_element(config_.rtp.ssrcs.begin(), config_.rtp.ssrcs.end()) + 1; + std::max( + *std::max_element(config_.rtp.ssrcs.begin(), config_.rtp.ssrcs.end()), + *std::max_element(config_.rtp.rtx.ssrcs.begin(), + config_.rtp.rtx.ssrcs.end())) + + 1; // From RtcpStatisticsCallback. RtcpStatistics rtcp_stats; RtcpStatisticsCallback* rtcp_callback = statistics_proxy_.get(); @@ -214,9 +307,10 @@ rtp_callback->DataCountersUpdated(rtp_stats, exluded_ssrc); // From BitrateStatisticsObserver. - BitrateStatistics bitrate; + BitrateStatistics total; + BitrateStatistics retransmit; BitrateStatisticsObserver* bitrate_observer = statistics_proxy_.get(); - bitrate_observer->Notify(bitrate, exluded_ssrc); + bitrate_observer->Notify(total, retransmit, exluded_ssrc); // From FrameCountObserver. FrameCountObserver* fps_observer = statistics_proxy_.get(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/transport_adapter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/transport_adapter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/transport_adapter.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/transport_adapter.h 2015-02-03 14:33:38.000000000 +0000 @@ -7,8 +7,8 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_VIDEO_ENGINE_INTERNAL_TRANSPORT_ADAPTER_H_ -#define WEBRTC_VIDEO_ENGINE_INTERNAL_TRANSPORT_ADAPTER_H_ +#ifndef WEBRTC_VIDEO_TRANSPORT_ADAPTER_H_ +#define WEBRTC_VIDEO_TRANSPORT_ADAPTER_H_ #include "webrtc/common_types.h" #include "webrtc/system_wrappers/interface/atomic32.h" @@ -21,10 +21,12 @@ public: explicit TransportAdapter(newapi::Transport* transport); - virtual int SendPacket(int /*channel*/, const void* packet, int length) - OVERRIDE; - virtual int SendRTCPPacket(int /*channel*/, const void* packet, int length) - OVERRIDE; + virtual int SendPacket(int /*channel*/, + const void* packet, + int length) OVERRIDE; + virtual int SendRTCPPacket(int /*channel*/, + const void* packet, + int length) OVERRIDE; void Enable(); void Disable(); @@ -36,4 +38,4 @@ } // namespace internal } // namespace webrtc -#endif // WEBRTC_VIDEO_ENGINE_INTERNAL_TRANSPORT_ADAPTER_H_ +#endif // WEBRTC_VIDEO_TRANSPORT_ADAPTER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/video_receive_stream.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/video_receive_stream.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/video_receive_stream.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/video_receive_stream.cc 2015-02-03 14:33:38.000000000 +0000 @@ -17,7 +17,9 @@ #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" #include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/video/receive_statistics_proxy.h" +#include "webrtc/video_encoder.h" #include "webrtc/video_engine/include/vie_base.h" #include "webrtc/video_engine/include/vie_capture.h" #include "webrtc/video_engine/include/vie_codec.h" @@ -30,6 +32,35 @@ namespace webrtc { namespace internal { +namespace { +VideoCodec CreateDecoderVideoCodec(const VideoReceiveStream::Decoder& decoder) { + VideoCodec codec; + memset(&codec, 0, sizeof(codec)); + + codec.plType = decoder.payload_type; + strcpy(codec.plName, decoder.payload_name.c_str()); + if (decoder.payload_name == "VP8") { + codec.codecType = kVideoCodecVP8; + } else if (decoder.payload_name == "H264") { + codec.codecType = kVideoCodecH264; + } else { + codec.codecType = kVideoCodecGeneric; + } + + if (codec.codecType == kVideoCodecVP8) { + codec.codecSpecific.VP8 = VideoEncoder::GetDefaultVp8Settings(); + } else if (codec.codecType == kVideoCodecH264) { + codec.codecSpecific.H264 = VideoEncoder::GetDefaultH264Settings(); + } + + codec.width = 320; + codec.height = 180; + codec.startBitrate = codec.minBitrate = codec.maxBitrate = + Call::Config::kDefaultStartBitrateBps / 1000; + + return codec; +} +} // namespace VideoReceiveStream::VideoReceiveStream(webrtc::VideoEngine* video_engine, const VideoReceiveStream::Config& config, @@ -51,14 +82,7 @@ // TODO(pbos): This is not fine grained enough... rtp_rtcp_->SetNACKStatus(channel_, config_.rtp.nack.rtp_history_ms > 0); rtp_rtcp_->SetKeyFrameRequestMethod(channel_, kViEKeyFrameRequestPliRtcp); - switch (config_.rtp.rtcp_mode) { - case newapi::kRtcpCompound: - rtp_rtcp_->SetRTCPStatus(channel_, kRtcpCompound_RFC4585); - break; - case newapi::kRtcpReducedSize: - rtp_rtcp_->SetRTCPStatus(channel_, kRtcpNonCompound_RFC5506); - break; - } + SetRtcpMode(config_.rtp.rtcp_mode); assert(config_.rtp.remote_ssrc != 0); // TODO(pbos): What's an appropriate local_ssrc for receive-only streams? @@ -99,10 +123,27 @@ codec_ = ViECodec::GetInterface(video_engine); - for (size_t i = 0; i < config_.codecs.size(); ++i) { - if (codec_->SetReceiveCodec(channel_, config_.codecs[i]) != 0) { - // TODO(pbos): Abort gracefully, this can be a runtime error. - // Factor out to an Init() method. + if (config_.rtp.fec.ulpfec_payload_type != -1) { + // ULPFEC without RED doesn't make sense. + assert(config_.rtp.fec.red_payload_type != -1); + VideoCodec codec; + memset(&codec, 0, sizeof(codec)); + codec.codecType = kVideoCodecULPFEC; + strcpy(codec.plName, "ulpfec"); + codec.plType = config_.rtp.fec.ulpfec_payload_type; + if (codec_->SetReceiveCodec(channel_, codec) != 0) { + LOG(LS_ERROR) << "Could not set ULPFEC codec. This shouldn't happen."; + abort(); + } + } + if (config_.rtp.fec.red_payload_type != -1) { + VideoCodec codec; + memset(&codec, 0, sizeof(codec)); + codec.codecType = kVideoCodecRED; + strcpy(codec.plName, "red"); + codec.plType = config_.rtp.fec.red_payload_type; + if (codec_->SetReceiveCodec(channel_, codec) != 0) { + LOG(LS_ERROR) << "Could not set RED codec. This shouldn't happen."; abort(); } } @@ -122,17 +163,26 @@ abort(); external_codec_ = ViEExternalCodec::GetInterface(video_engine); - for (size_t i = 0; i < config_.external_decoders.size(); ++i) { - ExternalVideoDecoder* decoder = &config_.external_decoders[i]; + assert(!config_.decoders.empty()); + for (size_t i = 0; i < config_.decoders.size(); ++i) { + const Decoder& decoder = config_.decoders[i]; if (external_codec_->RegisterExternalReceiveCodec( channel_, - decoder->payload_type, - decoder->decoder, - decoder->renderer, - decoder->expected_delay_ms) != 0) { + decoder.payload_type, + decoder.decoder, + decoder.renderer, + decoder.expected_delay_ms) != 0) { // TODO(pbos): Abort gracefully? Can this be a runtime error? abort(); } + + VideoCodec codec = CreateDecoderVideoCodec(decoder); + + if (codec_->SetReceiveCodec(channel_, codec) != 0) { + // TODO(pbos): Abort gracefully, this can be a runtime error. + // Factor out to an Init() method. + abort(); + } } render_ = ViERender::GetInterface(video_engine); @@ -140,7 +190,7 @@ render_->AddRenderCallback(channel_, this); - if (voice_engine) { + if (voice_engine && config_.audio_channel_id != -1) { video_engine_base_->SetVoiceEngine(voice_engine); video_engine_base_->ConnectAudioChannel(channel_, config_.audio_channel_id); } @@ -163,16 +213,15 @@ render_->RemoveRenderer(channel_); - for (size_t i = 0; i < config_.external_decoders.size(); ++i) { + for (size_t i = 0; i < config_.decoders.size(); ++i) { external_codec_->DeRegisterExternalReceiveCodec( - channel_, config_.external_decoders[i].payload_type); + channel_, config_.decoders[i].payload_type); } network_->DeregisterSendTransport(channel_); video_engine_base_->SetVoiceEngine(NULL); image_process_->Release(); - video_engine_base_->Release(); external_codec_->Release(); codec_->DeregisterDecoderObserver(channel_); rtp_rtcp_->DeregisterReceiveChannelRtpStatisticsCallback(channel_, @@ -183,9 +232,11 @@ network_->Release(); render_->Release(); rtp_rtcp_->Release(); + video_engine_base_->DeleteChannel(channel_); + video_engine_base_->Release(); } -void VideoReceiveStream::StartReceiving() { +void VideoReceiveStream::Start() { transport_adapter_.Enable(); if (render_->StartRender(channel_) != 0) abort(); @@ -193,7 +244,7 @@ abort(); } -void VideoReceiveStream::StopReceiving() { +void VideoReceiveStream::Stop() { if (render_->StopRender(channel_) != 0) abort(); if (video_engine_base_->StopReceive(channel_) != 0) @@ -205,10 +256,6 @@ return stats_proxy_->GetStats(); } -void VideoReceiveStream::GetCurrentReceiveCodec(VideoCodec* receive_codec) { - // TODO(pbos): Implement -} - bool VideoReceiveStream::DeliverRtcp(const uint8_t* packet, size_t length) { return network_->ReceivedRTCPPacket( channel_, packet, static_cast(length)) == 0; @@ -237,5 +284,24 @@ return 0; } + +void VideoReceiveStream::SignalNetworkState(Call::NetworkState state) { + if (state == Call::kNetworkUp) + SetRtcpMode(config_.rtp.rtcp_mode); + network_->SetNetworkTransmissionState(channel_, state == Call::kNetworkUp); + if (state == Call::kNetworkDown) + rtp_rtcp_->SetRTCPStatus(channel_, kRtcpNone); +} + +void VideoReceiveStream::SetRtcpMode(newapi::RtcpMode mode) { + switch (mode) { + case newapi::kRtcpCompound: + rtp_rtcp_->SetRTCPStatus(channel_, kRtcpCompound_RFC4585); + break; + case newapi::kRtcpReducedSize: + rtp_rtcp_->SetRTCPStatus(channel_, kRtcpNonCompound_RFC5506); + break; + } +} } // namespace internal } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/video_receive_stream.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/video_receive_stream.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/video_receive_stream.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/video_receive_stream.h 2015-02-03 14:33:38.000000000 +0000 @@ -13,6 +13,7 @@ #include +#include "webrtc/call.h" #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" #include "webrtc/modules/video_render/include/video_render_defines.h" #include "webrtc/system_wrappers/interface/clock.h" @@ -40,7 +41,6 @@ class VideoReceiveStream : public webrtc::VideoReceiveStream, public I420FrameCallback, public VideoRenderCallback { - public: VideoReceiveStream(webrtc::VideoEngine* video_engine, const VideoReceiveStream::Config& config, @@ -49,12 +49,10 @@ int base_channel); virtual ~VideoReceiveStream(); - virtual void StartReceiving() OVERRIDE; - virtual void StopReceiving() OVERRIDE; + virtual void Start() OVERRIDE; + virtual void Stop() OVERRIDE; virtual Stats GetStats() const OVERRIDE; - virtual void GetCurrentReceiveCodec(VideoCodec* receive_codec) OVERRIDE; - // Overrides I420FrameCallback. virtual void FrameCallback(I420VideoFrame* video_frame) OVERRIDE; @@ -62,15 +60,18 @@ virtual int32_t RenderFrame(const uint32_t stream_id, I420VideoFrame& video_frame) OVERRIDE; - public: + void SignalNetworkState(Call::NetworkState state); + virtual bool DeliverRtcp(const uint8_t* packet, size_t length); virtual bool DeliverRtp(const uint8_t* packet, size_t length); private: + void SetRtcpMode(newapi::RtcpMode mode); + TransportAdapter transport_adapter_; EncodedFrameCallbackAdapter encoded_frame_proxy_; - VideoReceiveStream::Config config_; - Clock* clock_; + const VideoReceiveStream::Config config_; + Clock* const clock_; ViEBase* video_engine_base_; ViECodec* codec_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/video_send_stream.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/video_send_stream.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/video_send_stream.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/video_send_stream.cc 2015-02-03 14:33:38.000000000 +0000 @@ -10,10 +10,13 @@ #include "webrtc/video/video_send_stream.h" +#include +#include #include #include #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/video_engine/include/vie_base.h" #include "webrtc/video_engine/include/vie_capture.h" #include "webrtc/video_engine/include/vie_codec.h" @@ -21,33 +24,116 @@ #include "webrtc/video_engine/include/vie_image_process.h" #include "webrtc/video_engine/include/vie_network.h" #include "webrtc/video_engine/include/vie_rtp_rtcp.h" +#include "webrtc/video_engine/vie_defines.h" #include "webrtc/video_send_stream.h" namespace webrtc { -namespace internal { +std::string +VideoSendStream::Config::EncoderSettings::ToString() const { + std::stringstream ss; + ss << "{payload_name: " << payload_name; + ss << ", payload_type: " << payload_type; + if (encoder != NULL) + ss << ", encoder: " << (encoder != NULL ? "(encoder)" : "NULL"); + ss << '}'; + return ss.str(); +} + +std::string VideoSendStream::Config::Rtp::Rtx::ToString() + const { + std::stringstream ss; + ss << "{ssrcs: {"; + for (size_t i = 0; i < ssrcs.size(); ++i) { + ss << ssrcs[i]; + if (i != ssrcs.size() - 1) + ss << "}, {"; + } + ss << '}'; + + ss << ", payload_type: " << payload_type; + ss << '}'; + return ss.str(); +} + +std::string VideoSendStream::Config::Rtp::ToString() const { + std::stringstream ss; + ss << "{ssrcs: {"; + for (size_t i = 0; i < ssrcs.size(); ++i) { + ss << ssrcs[i]; + if (i != ssrcs.size() - 1) + ss << "}, {"; + } + ss << '}'; + + ss << ", max_packet_size: " << max_packet_size; -VideoSendStream::VideoSendStream(newapi::Transport* transport, - CpuOveruseObserver* overuse_observer, - webrtc::VideoEngine* video_engine, - const VideoSendStream::Config& config, - int base_channel) + ss << ", extensions: {"; + for (size_t i = 0; i < extensions.size(); ++i) { + ss << extensions[i].ToString(); + if (i != extensions.size() - 1) + ss << "}, {"; + } + ss << '}'; + + if (nack.rtp_history_ms != 0) + ss << ", nack.rtp_history_ms: " << nack.rtp_history_ms; + if (fec.ulpfec_payload_type != -1 || fec.red_payload_type != -1) + ss << ", fec: " << fec.ToString(); + if (rtx.payload_type != 0 || !rtx.ssrcs.empty()) + ss << ", rtx: " << rtx.ToString(); + if (c_name != "") + ss << ", c_name: " << c_name; + ss << '}'; + return ss.str(); +} + +std::string VideoSendStream::Config::ToString() const { + std::stringstream ss; + ss << "{encoder_settings: " << encoder_settings.ToString(); + ss << ", rtp: " << rtp.ToString(); + if (pre_encode_callback != NULL) + ss << ", (pre_encode_callback)"; + if (post_encode_callback != NULL) + ss << ", (post_encode_callback)"; + if (local_renderer != NULL) { + ss << ", (local_renderer, render_delay_ms: " << render_delay_ms << ")"; + } + if (target_delay_ms > 0) + ss << ", target_delay_ms: " << target_delay_ms; + if (suspend_below_min_bitrate) + ss << ", suspend_below_min_bitrate: on"; + ss << '}'; + return ss.str(); +} + +namespace internal { +VideoSendStream::VideoSendStream( + newapi::Transport* transport, + CpuOveruseObserver* overuse_observer, + webrtc::VideoEngine* video_engine, + const VideoSendStream::Config& config, + const VideoEncoderConfig& encoder_config, + const std::map& suspended_ssrcs, + int base_channel, + int start_bitrate_bps) : transport_adapter_(transport), encoded_frame_proxy_(config.post_encode_callback), - codec_lock_(CriticalSectionWrapper::CreateCriticalSection()), config_(config), + start_bitrate_bps_(start_bitrate_bps), + suspended_ssrcs_(suspended_ssrcs), external_codec_(NULL), - channel_(-1) { + channel_(-1), + use_default_bitrate_(true), + stats_proxy_(config) { video_engine_base_ = ViEBase::GetInterface(video_engine); video_engine_base_->CreateChannel(channel_, base_channel); assert(channel_ != -1); + assert(start_bitrate_bps_ > 0); rtp_rtcp_ = ViERTP_RTCP::GetInterface(video_engine); assert(rtp_rtcp_ != NULL); assert(config_.rtp.ssrcs.size() > 0); - if (config_.suspend_below_min_bitrate) - config_.pacing = true; - rtp_rtcp_->SetTransmissionSmoothingStatus(channel_, config_.pacing); for (size_t i = 0; i < config_.rtp.extensions.size(); ++i) { const std::string& extension = config_.rtp.extensions[i].name; @@ -85,6 +171,8 @@ rtp_rtcp_->SetNACKStatus(channel_, config_.rtp.nack.rtp_history_ms > 0); } + ConfigureSsrcs(); + char rtcp_cname[ViERTP_RTCP::KMaxRTCPCNameLength]; assert(config_.rtp.c_name.length() < ViERTP_RTCP::KMaxRTCPCNameLength); strncpy(rtcp_cname, config_.rtp.c_name.c_str(), sizeof(rtcp_cname) - 1); @@ -104,24 +192,26 @@ network_->SetMTU(channel_, static_cast(config_.rtp.max_packet_size + 28)); - if (config.encoder) { - external_codec_ = ViEExternalCodec::GetInterface(video_engine); - if (external_codec_->RegisterExternalSendCodec(channel_, - config.codec.plType, - config.encoder, - config.internal_source) != - 0) { - abort(); - } + assert(config.encoder_settings.encoder != NULL); + assert(config.encoder_settings.payload_type >= 0); + assert(config.encoder_settings.payload_type <= 127); + external_codec_ = ViEExternalCodec::GetInterface(video_engine); + if (external_codec_->RegisterExternalSendCodec( + channel_, + config.encoder_settings.payload_type, + config.encoder_settings.encoder, + false) != 0) { + abort(); } codec_ = ViECodec::GetInterface(video_engine); - if (!SetCodec(config_.codec)) + if (!ReconfigureVideoEncoder(encoder_config)) abort(); - if (overuse_observer) { + if (overuse_observer) video_engine_base_->RegisterCpuOveruseObserver(channel_, overuse_observer); - } + + video_engine_base_->RegisterSendSideDelayObserver(channel_, &stats_proxy_); image_process_ = ViEImageProcess::GetInterface(video_engine); image_process_->RegisterPreEncodeCallback(channel_, @@ -131,33 +221,30 @@ &encoded_frame_proxy_); } - if (config.suspend_below_min_bitrate) { + if (config_.suspend_below_min_bitrate) codec_->SuspendBelowMinBitrate(channel_); - } - - stats_proxy_.reset(new SendStatisticsProxy(config, this)); rtp_rtcp_->RegisterSendChannelRtcpStatisticsCallback(channel_, - stats_proxy_.get()); + &stats_proxy_); rtp_rtcp_->RegisterSendChannelRtpStatisticsCallback(channel_, - stats_proxy_.get()); - rtp_rtcp_->RegisterSendBitrateObserver(channel_, stats_proxy_.get()); - rtp_rtcp_->RegisterSendFrameCountObserver(channel_, stats_proxy_.get()); + &stats_proxy_); + rtp_rtcp_->RegisterSendBitrateObserver(channel_, &stats_proxy_); + rtp_rtcp_->RegisterSendFrameCountObserver(channel_, &stats_proxy_); - codec_->RegisterEncoderObserver(channel_, *stats_proxy_); - capture_->RegisterObserver(capture_id_, *stats_proxy_); + codec_->RegisterEncoderObserver(channel_, stats_proxy_); + capture_->RegisterObserver(capture_id_, stats_proxy_); } VideoSendStream::~VideoSendStream() { capture_->DeregisterObserver(capture_id_); codec_->DeregisterEncoderObserver(channel_); - rtp_rtcp_->DeregisterSendFrameCountObserver(channel_, stats_proxy_.get()); - rtp_rtcp_->DeregisterSendBitrateObserver(channel_, stats_proxy_.get()); + rtp_rtcp_->DeregisterSendFrameCountObserver(channel_, &stats_proxy_); + rtp_rtcp_->DeregisterSendBitrateObserver(channel_, &stats_proxy_); rtp_rtcp_->DeregisterSendChannelRtpStatisticsCallback(channel_, - stats_proxy_.get()); + &stats_proxy_); rtp_rtcp_->DeregisterSendChannelRtcpStatisticsCallback(channel_, - stats_proxy_.get()); + &stats_proxy_); image_process_->DeRegisterPreEncodeCallback(channel_); @@ -166,10 +253,8 @@ capture_->DisconnectCaptureDevice(channel_); capture_->ReleaseCaptureDevice(capture_id_); - if (external_codec_) { - external_codec_->DeRegisterExternalSendCodec(channel_, - config_.codec.plType); - } + external_codec_->DeRegisterExternalSendCodec( + channel_, config_.encoder_settings.payload_type); video_engine_base_->DeleteChannel(channel_); @@ -183,78 +268,157 @@ rtp_rtcp_->Release(); } -void VideoSendStream::PutFrame(const I420VideoFrame& frame) { - input_frame_.CopyFrame(frame); - SwapFrame(&input_frame_); -} - void VideoSendStream::SwapFrame(I420VideoFrame* frame) { - // TODO(pbos): Warn if frame is "too far" into the future, or too old. This - // would help detect if frame's being used without NTP. - // TO REVIEWER: Is there any good check for this? Should it be - // skipped? - if (frame != &input_frame_) - input_frame_.SwapFrame(frame); - // TODO(pbos): Local rendering should not be done on the capture thread. if (config_.local_renderer != NULL) - config_.local_renderer->RenderFrame(input_frame_, 0); + config_.local_renderer->RenderFrame(*frame, 0); - external_capture_->SwapFrame(&input_frame_); + external_capture_->SwapFrame(frame); } VideoSendStreamInput* VideoSendStream::Input() { return this; } -void VideoSendStream::StartSending() { +void VideoSendStream::Start() { transport_adapter_.Enable(); video_engine_base_->StartSend(channel_); video_engine_base_->StartReceive(channel_); } -void VideoSendStream::StopSending() { +void VideoSendStream::Stop() { video_engine_base_->StopSend(channel_); video_engine_base_->StopReceive(channel_); transport_adapter_.Disable(); } -bool VideoSendStream::SetCodec(const VideoCodec& codec) { - assert(config_.rtp.ssrcs.size() >= codec.numberOfSimulcastStreams); - - CriticalSectionScoped crit(codec_lock_.get()); - if (codec_->SetSendCodec(channel_, codec) != 0) - return false; +bool VideoSendStream::ReconfigureVideoEncoder( + const VideoEncoderConfig& config) { + LOG(LS_INFO) << "(Re)configureVideoEncoder: " << config.ToString(); + const std::vector& streams = config.streams; + assert(!streams.empty()); + assert(config_.rtp.ssrcs.size() >= streams.size()); + + VideoCodec video_codec; + memset(&video_codec, 0, sizeof(video_codec)); + if (config_.encoder_settings.payload_name == "VP8") { + video_codec.codecType = kVideoCodecVP8; + } else if (config_.encoder_settings.payload_name == "VP9") { + video_codec.codecType = kVideoCodecVP9; + } else if (config_.encoder_settings.payload_name == "H264") { + video_codec.codecType = kVideoCodecH264; + } else { + video_codec.codecType = kVideoCodecGeneric; + } - for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) { - rtp_rtcp_->SetLocalSSRC(channel_, - config_.rtp.ssrcs[i], - kViEStreamTypeNormal, - static_cast(i)); + switch (config.content_type) { + case VideoEncoderConfig::kRealtimeVideo: + video_codec.mode = kRealtimeVideo; + break; + case VideoEncoderConfig::kScreenshare: + video_codec.mode = kScreensharing; + if (config.streams.size() == 1 && + config.streams[0].temporal_layer_thresholds_bps.size() == 1) { + video_codec.targetBitrate = + config.streams[0].temporal_layer_thresholds_bps[0] / 1000; + } + break; } - if (&config_.codec != &codec) - config_.codec = codec; + if (video_codec.codecType == kVideoCodecVP8) { + video_codec.codecSpecific.VP8 = VideoEncoder::GetDefaultVp8Settings(); + } else if (video_codec.codecType == kVideoCodecVP9) { + video_codec.codecSpecific.VP9 = VideoEncoder::GetDefaultVp9Settings(); + } else if (video_codec.codecType == kVideoCodecH264) { + video_codec.codecSpecific.H264 = VideoEncoder::GetDefaultH264Settings(); + } - if (config_.rtp.rtx.ssrcs.empty()) - return true; + if (video_codec.codecType == kVideoCodecVP8) { + if (config.encoder_specific_settings != NULL) { + video_codec.codecSpecific.VP8 = *reinterpret_cast( + config.encoder_specific_settings); + } + video_codec.codecSpecific.VP8.numberOfTemporalLayers = + static_cast( + streams.back().temporal_layer_thresholds_bps.size() + 1); + } else { + // TODO(pbos): Support encoder_settings codec-agnostically. + assert(config.encoder_specific_settings == NULL); + } - // Set up RTX. - assert(config_.rtp.rtx.ssrcs.size() == config_.rtp.ssrcs.size()); - for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) { - rtp_rtcp_->SetLocalSSRC(channel_, - config_.rtp.rtx.ssrcs[i], - kViEStreamTypeRtx, - static_cast(i)); + strncpy(video_codec.plName, + config_.encoder_settings.payload_name.c_str(), + kPayloadNameSize - 1); + video_codec.plName[kPayloadNameSize - 1] = '\0'; + video_codec.plType = config_.encoder_settings.payload_type; + video_codec.numberOfSimulcastStreams = + static_cast(streams.size()); + video_codec.minBitrate = streams[0].min_bitrate_bps / 1000; + assert(streams.size() <= kMaxSimulcastStreams); + for (size_t i = 0; i < streams.size(); ++i) { + SimulcastStream* sim_stream = &video_codec.simulcastStream[i]; + assert(streams[i].width > 0); + assert(streams[i].height > 0); + assert(streams[i].max_framerate > 0); + // Different framerates not supported per stream at the moment. + assert(streams[i].max_framerate == streams[0].max_framerate); + assert(streams[i].min_bitrate_bps >= 0); + assert(streams[i].target_bitrate_bps >= streams[i].min_bitrate_bps); + assert(streams[i].max_bitrate_bps >= streams[i].target_bitrate_bps); + assert(streams[i].max_qp >= 0); + + sim_stream->width = static_cast(streams[i].width); + sim_stream->height = static_cast(streams[i].height); + sim_stream->minBitrate = streams[i].min_bitrate_bps / 1000; + sim_stream->targetBitrate = streams[i].target_bitrate_bps / 1000; + sim_stream->maxBitrate = streams[i].max_bitrate_bps / 1000; + sim_stream->qpMax = streams[i].max_qp; + sim_stream->numberOfTemporalLayers = static_cast( + streams[i].temporal_layer_thresholds_bps.size() + 1); + + video_codec.width = std::max(video_codec.width, + static_cast(streams[i].width)); + video_codec.height = std::max( + video_codec.height, static_cast(streams[i].height)); + video_codec.minBitrate = + std::min(video_codec.minBitrate, + static_cast(streams[i].min_bitrate_bps / 1000)); + video_codec.maxBitrate += streams[i].max_bitrate_bps / 1000; + video_codec.qpMax = std::max(video_codec.qpMax, + static_cast(streams[i].max_qp)); + } + unsigned int start_bitrate_bps; + if (codec_->GetCodecTargetBitrate(channel_, &start_bitrate_bps) != 0 || + use_default_bitrate_) { + start_bitrate_bps = start_bitrate_bps_; } + video_codec.startBitrate = + static_cast(start_bitrate_bps) / 1000; - if (config_.rtp.rtx.payload_type != 0) - rtp_rtcp_->SetRtxSendPayloadType(channel_, config_.rtp.rtx.payload_type); + if (video_codec.minBitrate < kViEMinCodecBitrate) + video_codec.minBitrate = kViEMinCodecBitrate; + if (video_codec.maxBitrate < kViEMinCodecBitrate) + video_codec.maxBitrate = kViEMinCodecBitrate; + if (video_codec.startBitrate < video_codec.minBitrate) + video_codec.startBitrate = video_codec.minBitrate; + if (video_codec.startBitrate > video_codec.maxBitrate) + video_codec.startBitrate = video_codec.maxBitrate; + + if (video_codec.startBitrate < video_codec.minBitrate) + video_codec.startBitrate = video_codec.minBitrate; + if (video_codec.startBitrate > video_codec.maxBitrate) + video_codec.startBitrate = video_codec.maxBitrate; - return true; -} + assert(streams[0].max_framerate > 0); + video_codec.maxFramerate = streams[0].max_framerate; -VideoCodec VideoSendStream::GetCodec() { - CriticalSectionScoped crit(codec_lock_.get()); - return config_.codec; + if (codec_->SetSendCodec(channel_, video_codec) != 0) + return false; + + assert(config.min_transmit_bitrate_bps >= 0); + rtp_rtcp_->SetMinTransmitBitrate(channel_, + config.min_transmit_bitrate_bps / 1000); + + use_default_bitrate_ = false; + return true; } bool VideoSendStream::DeliverRtcp(const uint8_t* packet, size_t length) { @@ -263,19 +427,77 @@ } VideoSendStream::Stats VideoSendStream::GetStats() const { - return stats_proxy_->GetStats(); + return stats_proxy_.GetStats(); } -bool VideoSendStream::GetSendSideDelay(VideoSendStream::Stats* stats) { - return codec_->GetSendSideDelay( - channel_, &stats->avg_delay_ms, &stats->max_delay_ms); +void VideoSendStream::ConfigureSsrcs() { + for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) { + uint32_t ssrc = config_.rtp.ssrcs[i]; + rtp_rtcp_->SetLocalSSRC( + channel_, ssrc, kViEStreamTypeNormal, static_cast(i)); + RtpStateMap::iterator it = suspended_ssrcs_.find(ssrc); + if (it != suspended_ssrcs_.end()) + rtp_rtcp_->SetRtpStateForSsrc(channel_, ssrc, it->second); + } + + if (config_.rtp.rtx.ssrcs.empty()) { + assert(!config_.rtp.rtx.pad_with_redundant_payloads); + return; + } + + // Set up RTX. + assert(config_.rtp.rtx.ssrcs.size() == config_.rtp.ssrcs.size()); + for (size_t i = 0; i < config_.rtp.rtx.ssrcs.size(); ++i) { + uint32_t ssrc = config_.rtp.rtx.ssrcs[i]; + rtp_rtcp_->SetLocalSSRC(channel_, + config_.rtp.rtx.ssrcs[i], + kViEStreamTypeRtx, + static_cast(i)); + RtpStateMap::iterator it = suspended_ssrcs_.find(ssrc); + if (it != suspended_ssrcs_.end()) + rtp_rtcp_->SetRtpStateForSsrc(channel_, ssrc, it->second); + } + + if (config_.rtp.rtx.pad_with_redundant_payloads) { + rtp_rtcp_->SetPadWithRedundantPayloads(channel_, true); + } + + assert(config_.rtp.rtx.payload_type >= 0); + rtp_rtcp_->SetRtxSendPayloadType(channel_, config_.rtp.rtx.payload_type); } -std::string VideoSendStream::GetCName() { - char rtcp_cname[ViERTP_RTCP::KMaxRTCPCNameLength]; - rtp_rtcp_->GetRTCPCName(channel_, rtcp_cname); - return rtcp_cname; +std::map VideoSendStream::GetRtpStates() const { + std::map rtp_states; + for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) { + uint32_t ssrc = config_.rtp.ssrcs[i]; + rtp_states[ssrc] = rtp_rtcp_->GetRtpStateForSsrc(channel_, ssrc); + } + + for (size_t i = 0; i < config_.rtp.rtx.ssrcs.size(); ++i) { + uint32_t ssrc = config_.rtp.rtx.ssrcs[i]; + rtp_states[ssrc] = rtp_rtcp_->GetRtpStateForSsrc(channel_, ssrc); + } + + return rtp_states; } +void VideoSendStream::SignalNetworkState(Call::NetworkState state) { + // When network goes up, enable RTCP status before setting transmission state. + // When it goes down, disable RTCP afterwards. This ensures that any packets + // sent due to the network state changed will not be dropped. + if (state == Call::kNetworkUp) + rtp_rtcp_->SetRTCPStatus(channel_, kRtcpCompound_RFC4585); + network_->SetNetworkTransmissionState(channel_, state == Call::kNetworkUp); + if (state == Call::kNetworkDown) + rtp_rtcp_->SetRTCPStatus(channel_, kRtcpNone); +} + +int VideoSendStream::GetPacerQueuingDelayMs() const { + int pacer_delay_ms = 0; + if (rtp_rtcp_->GetPacerQueuingDelayMs(channel_, &pacer_delay_ms) != 0) { + return 0; + } + return pacer_delay_ms; +} } // namespace internal } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/video_send_stream.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/video_send_stream.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/video_send_stream.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/video_send_stream.h 2015-02-03 14:33:38.000000000 +0000 @@ -11,7 +11,12 @@ #ifndef WEBRTC_VIDEO_VIDEO_SEND_STREAM_H_ #define WEBRTC_VIDEO_VIDEO_SEND_STREAM_H_ +#include +#include + +#include "webrtc/call.h" #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" #include "webrtc/video/encoded_frame_callback_adapter.h" #include "webrtc/video/send_statistics_proxy.h" #include "webrtc/video/transport_adapter.h" @@ -35,46 +40,49 @@ namespace internal { class VideoSendStream : public webrtc::VideoSendStream, - public VideoSendStreamInput, - public SendStatisticsProxy::StatsProvider { + public VideoSendStreamInput { public: VideoSendStream(newapi::Transport* transport, CpuOveruseObserver* overuse_observer, webrtc::VideoEngine* video_engine, const VideoSendStream::Config& config, - int base_channel); + const VideoEncoderConfig& encoder_config, + const std::map& suspended_ssrcs, + int base_channel, + int start_bitrate); virtual ~VideoSendStream(); - virtual void StartSending() OVERRIDE; - - virtual void StopSending() OVERRIDE; + virtual void Start() OVERRIDE; + virtual void Stop() OVERRIDE; - virtual bool SetCodec(const VideoCodec& codec) OVERRIDE; - virtual VideoCodec GetCodec() OVERRIDE; + virtual bool ReconfigureVideoEncoder( + const VideoEncoderConfig& config) OVERRIDE; virtual Stats GetStats() const OVERRIDE; bool DeliverRtcp(const uint8_t* packet, size_t length); // From VideoSendStreamInput. - virtual void PutFrame(const I420VideoFrame& frame) OVERRIDE; virtual void SwapFrame(I420VideoFrame* frame) OVERRIDE; // From webrtc::VideoSendStream. virtual VideoSendStreamInput* Input() OVERRIDE; - protected: - // From SendStatisticsProxy::StreamStatsProvider. - virtual bool GetSendSideDelay(VideoSendStream::Stats* stats) OVERRIDE; - virtual std::string GetCName() OVERRIDE; + typedef std::map RtpStateMap; + RtpStateMap GetRtpStates() const; + + void SignalNetworkState(Call::NetworkState state); + + int GetPacerQueuingDelayMs() const; private: - I420VideoFrame input_frame_; + void ConfigureSsrcs(); TransportAdapter transport_adapter_; EncodedFrameCallbackAdapter encoded_frame_proxy_; - scoped_ptr codec_lock_; - VideoSendStream::Config config_; + const VideoSendStream::Config config_; + const int start_bitrate_bps_; + std::map suspended_ssrcs_; ViEBase* video_engine_base_; ViECapture* capture_; @@ -88,7 +96,12 @@ int channel_; int capture_id_; - scoped_ptr stats_proxy_; + // Used as a workaround to indicate that we should be using the configured + // start bitrate initially, instead of the one reported by VideoEngine (which + // defaults to too high). + bool use_default_bitrate_; + + SendStatisticsProxy stats_proxy_; }; } // namespace internal } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/video_send_stream_tests.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/video_send_stream_tests.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video/video_send_stream_tests.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video/video_send_stream_tests.cc 2015-02-03 14:33:38.000000000 +0000 @@ -13,21 +13,25 @@ #include "webrtc/call.h" #include "webrtc/common_video/interface/i420_video_frame.h" +#include "webrtc/common_video/interface/native_handle.h" +#include "webrtc/common_video/interface/texture_video_frame.h" #include "webrtc/frame_callback.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" +#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_sender.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/event_wrapper.h" +#include "webrtc/system_wrappers/interface/ref_count.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/system_wrappers/interface/scoped_vector.h" #include "webrtc/system_wrappers/interface/sleep.h" #include "webrtc/system_wrappers/interface/thread_wrapper.h" -#include "webrtc/test/direct_transport.h" -#include "webrtc/test/fake_encoder.h" +#include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/test/call_test.h" #include "webrtc/test/configurable_frame_size_encoder.h" -#include "webrtc/test/frame_generator_capturer.h" #include "webrtc/test/null_transport.h" -#include "webrtc/test/rtp_rtcp_observer.h" +#include "webrtc/test/testsupport/perf_test.h" #include "webrtc/video/transport_adapter.h" #include "webrtc/video_send_stream.h" @@ -35,208 +39,61 @@ enum VideoFormat { kGeneric, kVP8, }; -class VideoSendStreamTest : public ::testing::Test { +void ExpectEqualFrames(const I420VideoFrame& frame1, + const I420VideoFrame& frame2); +void ExpectEqualTextureFrames(const I420VideoFrame& frame1, + const I420VideoFrame& frame2); +void ExpectEqualBufferFrames(const I420VideoFrame& frame1, + const I420VideoFrame& frame2); +void ExpectEqualFramesVector(const std::vector& frames1, + const std::vector& frames2); +I420VideoFrame* CreateI420VideoFrame(int width, int height, uint8_t data); + +class FakeNativeHandle : public NativeHandle { public: - VideoSendStreamTest() - : send_stream_(NULL), fake_encoder_(Clock::GetRealTimeClock()) {} + FakeNativeHandle() {} + virtual ~FakeNativeHandle() {} + virtual void* GetHandle() { return NULL; } +}; +class VideoSendStreamTest : public test::CallTest { protected: - void RunSendTest(Call* call, - const VideoSendStream::Config& config, - test::RtpRtcpObserver* observer) { - send_stream_ = call->CreateVideoSendStream(config); - scoped_ptr frame_generator_capturer( - test::FrameGeneratorCapturer::Create( - send_stream_->Input(), 320, 240, 30, Clock::GetRealTimeClock())); - send_stream_->StartSending(); - frame_generator_capturer->Start(); - - EXPECT_EQ(kEventSignaled, observer->Wait()); - - observer->StopSending(); - frame_generator_capturer->Stop(); - send_stream_->StopSending(); - call->DestroyVideoSendStream(send_stream_); - } - - VideoSendStream::Config GetSendTestConfig(Call* call, - size_t number_of_streams) { - assert(number_of_streams <= kNumSendSsrcs); - VideoSendStream::Config config = call->GetDefaultSendConfig(); - config.encoder = &fake_encoder_; - config.internal_source = false; - for (size_t i = 0; i < number_of_streams; ++i) - config.rtp.ssrcs.push_back(kSendSsrcs[i]); - config.pacing = true; - test::FakeEncoder::SetCodecSettings(&config.codec, number_of_streams); - config.codec.plType = kFakeSendPayloadType; - return config; - } - void TestNackRetransmission(uint32_t retransmit_ssrc, - uint8_t retransmit_payload_type, - bool enable_pacing); - + uint8_t retransmit_payload_type); void TestPacketFragmentationSize(VideoFormat format, bool with_fec); - - void SendsSetSsrcs(size_t num_ssrcs, bool send_single_ssrc_first); - - enum { kNumSendSsrcs = 3 }; - static const uint8_t kSendPayloadType; - static const uint8_t kSendRtxPayloadType; - static const uint8_t kFakeSendPayloadType; - static const uint32_t kSendSsrc; - static const uint32_t kSendRtxSsrc; - static const uint32_t kSendSsrcs[kNumSendSsrcs]; - - VideoSendStream* send_stream_; - test::FakeEncoder fake_encoder_; }; -const uint8_t VideoSendStreamTest::kSendPayloadType = 100; -const uint8_t VideoSendStreamTest::kFakeSendPayloadType = 125; -const uint8_t VideoSendStreamTest::kSendRtxPayloadType = 98; -const uint32_t VideoSendStreamTest::kSendRtxSsrc = 0xBADCAFE; -const uint32_t VideoSendStreamTest::kSendSsrcs[kNumSendSsrcs] = { - 0xC0FFED, 0xC0FFEE, 0xC0FFEF}; -const uint32_t VideoSendStreamTest::kSendSsrc = - VideoSendStreamTest::kSendSsrcs[0]; - -void VideoSendStreamTest::SendsSetSsrcs(size_t num_ssrcs, - bool send_single_ssrc_first) { - class SendSsrcObserver : public test::RtpRtcpObserver { - public: - SendSsrcObserver(const uint32_t* ssrcs, - size_t num_ssrcs, - bool send_single_ssrc_first) - : RtpRtcpObserver(30 * 1000), - ssrcs_to_observe_(num_ssrcs), - expect_single_ssrc_(send_single_ssrc_first) { - for (size_t i = 0; i < num_ssrcs; ++i) - valid_ssrcs_[ssrcs[i]] = true; - } - - virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { - RTPHeader header; - EXPECT_TRUE(parser_->Parse(packet, static_cast(length), &header)); - - // TODO(pbos): Reenable this part of the test when #1695 is resolved and - // all SSRCs are allocated on startup. This test was observed - // to fail on TSan as the codec gets set before the SSRCs are - // set up and some frames are sent on a random-generated SSRC - // before the correct SSRC gets set. - // EXPECT_TRUE(valid_ssrcs_[header.ssrc]) - // << "Received unknown SSRC: " << header.ssrc; - // - // if (!valid_ssrcs_[header.ssrc]) - // observation_complete_->Set(); - - if (!is_observed_[header.ssrc]) { - is_observed_[header.ssrc] = true; - --ssrcs_to_observe_; - if (expect_single_ssrc_) { - expect_single_ssrc_ = false; - observation_complete_->Set(); - } - } - - if (ssrcs_to_observe_ == 0) - observation_complete_->Set(); - - return SEND_PACKET; - } - - private: - std::map valid_ssrcs_; - std::map is_observed_; - size_t ssrcs_to_observe_; - bool expect_single_ssrc_; - } observer(kSendSsrcs, num_ssrcs, send_single_ssrc_first); - - Call::Config call_config(observer.SendTransport()); - scoped_ptr call(Call::Create(call_config)); - - VideoSendStream::Config send_config = - GetSendTestConfig(call.get(), num_ssrcs); - - if (num_ssrcs > 1) { - // Set low simulcast bitrates to not have to wait for bandwidth ramp-up. - for (size_t i = 0; i < num_ssrcs; ++i) { - send_config.codec.simulcastStream[i].minBitrate = 10; - send_config.codec.simulcastStream[i].targetBitrate = 10; - send_config.codec.simulcastStream[i].maxBitrate = 10; - } - } - - if (send_single_ssrc_first) - send_config.codec.numberOfSimulcastStreams = 1; - - send_stream_ = call->CreateVideoSendStream(send_config); - scoped_ptr frame_generator_capturer( - test::FrameGeneratorCapturer::Create( - send_stream_->Input(), 320, 240, 30, Clock::GetRealTimeClock())); - send_stream_->StartSending(); - frame_generator_capturer->Start(); - - EXPECT_EQ(kEventSignaled, observer.Wait()) - << "Timed out while waiting for " - << (send_single_ssrc_first ? "first SSRC." : "SSRCs."); - - if (send_single_ssrc_first) { - // Set full simulcast and continue with the rest of the SSRCs. - send_config.codec.numberOfSimulcastStreams = - static_cast(num_ssrcs); - send_stream_->SetCodec(send_config.codec); - EXPECT_EQ(kEventSignaled, observer.Wait()) - << "Timed out while waiting on additional SSRCs."; - } - - observer.StopSending(); - frame_generator_capturer->Stop(); - send_stream_->StopSending(); - call->DestroyVideoSendStream(send_stream_); -} - TEST_F(VideoSendStreamTest, CanStartStartedStream) { test::NullTransport transport; Call::Config call_config(&transport); - scoped_ptr call(Call::Create(call_config)); + CreateSenderCall(call_config); - VideoSendStream::Config config = GetSendTestConfig(call.get(), 1); - VideoSendStream* stream = call->CreateVideoSendStream(config); - stream->StartSending(); - stream->StartSending(); - call->DestroyVideoSendStream(stream); + CreateSendConfig(1); + CreateStreams(); + send_stream_->Start(); + send_stream_->Start(); + DestroyStreams(); } TEST_F(VideoSendStreamTest, CanStopStoppedStream) { test::NullTransport transport; Call::Config call_config(&transport); - scoped_ptr call(Call::Create(call_config)); - - VideoSendStream::Config config = GetSendTestConfig(call.get(), 1); - VideoSendStream* stream = call->CreateVideoSendStream(config); - stream->StopSending(); - stream->StopSending(); - call->DestroyVideoSendStream(stream); -} + CreateSenderCall(call_config); -TEST_F(VideoSendStreamTest, SendsSetSsrc) { SendsSetSsrcs(1, false); } - -TEST_F(VideoSendStreamTest, SendsSetSimulcastSsrcs) { - SendsSetSsrcs(kNumSendSsrcs, false); -} - -TEST_F(VideoSendStreamTest, CanSwitchToUseAllSsrcs) { - SendsSetSsrcs(kNumSendSsrcs, true); + CreateSendConfig(1); + CreateStreams(); + send_stream_->Stop(); + send_stream_->Stop(); + DestroyStreams(); } TEST_F(VideoSendStreamTest, SupportsCName) { static std::string kCName = "PjQatC14dGfbVwGPUOA9IH7RlsFDbWl4AhXEiDsBizo="; - class CNameObserver : public test::RtpRtcpObserver { + class CNameObserver : public test::SendTest { public: - CNameObserver() : RtpRtcpObserver(30 * 1000) {} + CNameObserver() : SendTest(kDefaultTimeoutMs) {} + private: virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE { RTCPUtility::RTCPParserV2 parser(packet, length, true); EXPECT_TRUE(parser.IsValid()); @@ -253,29 +110,35 @@ return SEND_PACKET; } - } observer; - Call::Config call_config(observer.SendTransport()); - scoped_ptr call(Call::Create(call_config)); + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->rtp.c_name = kCName; + } - VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1); - send_config.rtp.c_name = kCName; + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for RTCP with CNAME."; + } + } test; - RunSendTest(call.get(), send_config, &observer); + RunBaseTest(&test); } TEST_F(VideoSendStreamTest, SupportsAbsoluteSendTime) { static const uint8_t kAbsSendTimeExtensionId = 13; - class AbsoluteSendTimeObserver : public test::RtpRtcpObserver { + class AbsoluteSendTimeObserver : public test::SendTest { public: - AbsoluteSendTimeObserver() : RtpRtcpObserver(30 * 1000) { + AbsoluteSendTimeObserver() : SendTest(kDefaultTimeoutMs) { EXPECT_TRUE(parser_->RegisterRtpHeaderExtension( kRtpExtensionAbsoluteSendTime, kAbsSendTimeExtensionId)); } virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { RTPHeader header; - EXPECT_TRUE(parser_->Parse(packet, static_cast(length), &header)); + EXPECT_TRUE(parser_->Parse(packet, length, &header)); EXPECT_FALSE(header.extension.hasTransmissionTimeOffset); EXPECT_TRUE(header.extension.hasAbsoluteSendTime); @@ -285,44 +148,38 @@ return SEND_PACKET; } - } observer; - Call::Config call_config(observer.SendTransport()); - scoped_ptr call(Call::Create(call_config)); + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->rtp.extensions.push_back( + RtpExtension(RtpExtension::kAbsSendTime, kAbsSendTimeExtensionId)); + } - VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1); - send_config.rtp.extensions.push_back( - RtpExtension(RtpExtension::kAbsSendTime, kAbsSendTimeExtensionId)); + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for single RTP packet."; + } + } test; - RunSendTest(call.get(), send_config, &observer); + RunBaseTest(&test); } TEST_F(VideoSendStreamTest, SupportsTransmissionTimeOffset) { static const uint8_t kTOffsetExtensionId = 13; - class DelayedEncoder : public test::FakeEncoder { + class TransmissionTimeOffsetObserver : public test::SendTest { public: - explicit DelayedEncoder(Clock* clock) : test::FakeEncoder(clock) {} - virtual int32_t Encode(const I420VideoFrame& input_image, - const CodecSpecificInfo* codec_specific_info, - const std::vector* frame_types) - OVERRIDE { - // A delay needs to be introduced to assure that we get a timestamp - // offset. - SleepMs(5); - return FakeEncoder::Encode(input_image, codec_specific_info, frame_types); - } - } encoder(Clock::GetRealTimeClock()); - - class TransmissionTimeOffsetObserver : public test::RtpRtcpObserver { - public: - TransmissionTimeOffsetObserver() : RtpRtcpObserver(30 * 1000) { + TransmissionTimeOffsetObserver() + : SendTest(kDefaultTimeoutMs), encoder_(Clock::GetRealTimeClock()) { EXPECT_TRUE(parser_->RegisterRtpHeaderExtension( kRtpExtensionTransmissionTimeOffset, kTOffsetExtensionId)); } + private: virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { RTPHeader header; - EXPECT_TRUE(parser_->Parse(packet, static_cast(length), &header)); + EXPECT_TRUE(parser_->Parse(packet, length, &header)); EXPECT_TRUE(header.extension.hasTransmissionTimeOffset); EXPECT_FALSE(header.extension.hasAbsoluteSendTime); @@ -332,17 +189,40 @@ return SEND_PACKET; } - } observer; - Call::Config call_config(observer.SendTransport()); - scoped_ptr call(Call::Create(call_config)); + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->encoder_settings.encoder = &encoder_; + send_config->rtp.extensions.push_back( + RtpExtension(RtpExtension::kTOffset, kTOffsetExtensionId)); + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for a single RTP packet."; + } + + class DelayedEncoder : public test::FakeEncoder { + public: + explicit DelayedEncoder(Clock* clock) : test::FakeEncoder(clock) {} + virtual int32_t Encode( + const I420VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector* frame_types) OVERRIDE { + // A delay needs to be introduced to assure that we get a timestamp + // offset. + SleepMs(5); + return FakeEncoder::Encode( + input_image, codec_specific_info, frame_types); + } + }; - VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1); - send_config.encoder = &encoder; - send_config.rtp.extensions.push_back( - RtpExtension(RtpExtension::kTOffset, kTOffsetExtensionId)); + DelayedEncoder encoder_; + } test; - RunSendTest(call.get(), send_config, &observer); + RunBaseTest(&test); } class FakeReceiveStatistics : public NullReceiveStatistics { @@ -409,31 +289,29 @@ test::NullTransport transport; Call::Config call_config(&transport); - scoped_ptr call(Call::Create(call_config)); + CreateSenderCall(call_config); - VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1); - VideoSendStream* video_send_stream = call->CreateVideoSendStream(send_config); - video_send_stream->StartSending(); + CreateSendConfig(1); + CreateStreams(); + send_stream_->Start(); I420VideoFrame frame; frame.CreateEmptyFrame( kWidth, kHeight, kWidth, (kWidth + 1) / 2, (kWidth + 1) / 2); uint8_t* old_y_buffer = frame.buffer(kYPlane); - video_send_stream->Input()->SwapFrame(&frame); + send_stream_->Input()->SwapFrame(&frame); EXPECT_NE(frame.buffer(kYPlane), old_y_buffer); - call->DestroyVideoSendStream(video_send_stream); + DestroyStreams(); } TEST_F(VideoSendStreamTest, SupportsFec) { - static const int kRedPayloadType = 118; - static const int kUlpfecPayloadType = 119; - class FecObserver : public test::RtpRtcpObserver { + class FecObserver : public test::SendTest { public: FecObserver() - : RtpRtcpObserver(30 * 1000), + : SendTest(kDefaultTimeoutMs), transport_adapter_(SendTransport()), send_count_(0), received_media_(false), @@ -441,21 +319,22 @@ transport_adapter_.Enable(); } + private: virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { RTPHeader header; - EXPECT_TRUE(parser_->Parse(packet, static_cast(length), &header)); + EXPECT_TRUE(parser_->Parse(packet, length, &header)); // Send lossy receive reports to trigger FEC enabling. if (send_count_++ % 2 != 0) { // Receive statistics reporting having lost 50% of the packets. FakeReceiveStatistics lossy_receive_stats( - kSendSsrc, header.sequenceNumber, send_count_ / 2, 127); + kSendSsrcs[0], header.sequenceNumber, send_count_ / 2, 127); RTCPSender rtcp_sender( 0, false, Clock::GetRealTimeClock(), &lossy_receive_stats); EXPECT_EQ(0, rtcp_sender.RegisterSendTransport(&transport_adapter_)); rtcp_sender.SetRTCPStatus(kRtcpNonCompound); - rtcp_sender.SetRemoteSSRC(kSendSsrc); + rtcp_sender.SetRemoteSSRC(kSendSsrcs[0]); RTCPSender::FeedbackState feedback_state; @@ -478,34 +357,35 @@ return SEND_PACKET; } - private: + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->rtp.fec.red_payload_type = kRedPayloadType; + send_config->rtp.fec.ulpfec_payload_type = kUlpfecPayloadType; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_TRUE(Wait()) << "Timed out waiting for FEC and media packets."; + } + internal::TransportAdapter transport_adapter_; int send_count_; bool received_media_; bool received_fec_; - } observer; + } test; - Call::Config call_config(observer.SendTransport()); - scoped_ptr call(Call::Create(call_config)); - - observer.SetReceivers(call->Receiver(), NULL); - - VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1); - send_config.rtp.fec.red_payload_type = kRedPayloadType; - send_config.rtp.fec.ulpfec_payload_type = kUlpfecPayloadType; - - RunSendTest(call.get(), send_config, &observer); + RunBaseTest(&test); } void VideoSendStreamTest::TestNackRetransmission( uint32_t retransmit_ssrc, - uint8_t retransmit_payload_type, - bool enable_pacing) { - class NackObserver : public test::RtpRtcpObserver { + uint8_t retransmit_payload_type) { + class NackObserver : public test::SendTest { public: explicit NackObserver(uint32_t retransmit_ssrc, uint8_t retransmit_payload_type) - : RtpRtcpObserver(30 * 1000), + : SendTest(kDefaultTimeoutMs), transport_adapter_(SendTransport()), send_count_(0), retransmit_ssrc_(retransmit_ssrc), @@ -514,9 +394,10 @@ transport_adapter_.Enable(); } + private: virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { RTPHeader header; - EXPECT_TRUE(parser_->Parse(packet, static_cast(length), &header)); + EXPECT_TRUE(parser_->Parse(packet, length, &header)); // Nack second packet after receiving the third one. if (++send_count_ == 3) { @@ -528,7 +409,7 @@ EXPECT_EQ(0, rtcp_sender.RegisterSendTransport(&transport_adapter_)); rtcp_sender.SetRTCPStatus(kRtcpNonCompound); - rtcp_sender.SetRemoteSSRC(kSendSsrc); + rtcp_sender.SetRemoteSSRC(kSendSsrcs[0]); RTCPSender::FeedbackState feedback_state; @@ -539,8 +420,10 @@ uint16_t sequence_number = header.sequenceNumber; - if (header.ssrc == retransmit_ssrc_ && retransmit_ssrc_ != kSendSsrc) { - // Not kSendSsrc, assume correct RTX packet. Extract sequence number. + if (header.ssrc == retransmit_ssrc_ && + retransmit_ssrc_ != kSendSsrcs[0]) { + // Not kSendSsrcs[0], assume correct RTX packet. Extract sequence + // number. const uint8_t* rtx_header = packet + header.headerLength; sequence_number = (rtx_header[0] << 8) + rtx_header[1]; } @@ -554,61 +437,63 @@ return SEND_PACKET; } - private: + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs; + send_config->rtp.rtx.payload_type = retransmit_payload_type_; + if (retransmit_ssrc_ != kSendSsrcs[0]) + send_config->rtp.rtx.ssrcs.push_back(retransmit_ssrc_); + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for NACK retransmission."; + } + internal::TransportAdapter transport_adapter_; int send_count_; uint32_t retransmit_ssrc_; uint8_t retransmit_payload_type_; int nacked_sequence_number_; - } observer(retransmit_ssrc, retransmit_payload_type); - - Call::Config call_config(observer.SendTransport()); - scoped_ptr call(Call::Create(call_config)); - observer.SetReceivers(call->Receiver(), NULL); - - VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1); - send_config.rtp.nack.rtp_history_ms = 1000; - send_config.rtp.rtx.payload_type = retransmit_payload_type; - send_config.pacing = enable_pacing; - if (retransmit_ssrc != kSendSsrc) - send_config.rtp.rtx.ssrcs.push_back(retransmit_ssrc); + } test(retransmit_ssrc, retransmit_payload_type); - RunSendTest(call.get(), send_config, &observer); + RunBaseTest(&test); } TEST_F(VideoSendStreamTest, RetransmitsNack) { // Normal NACKs should use the send SSRC. - TestNackRetransmission(kSendSsrc, kFakeSendPayloadType, false); + TestNackRetransmission(kSendSsrcs[0], kFakeSendPayloadType); } TEST_F(VideoSendStreamTest, RetransmitsNackOverRtx) { // NACKs over RTX should use a separate SSRC. - TestNackRetransmission(kSendRtxSsrc, kSendRtxPayloadType, false); -} - -TEST_F(VideoSendStreamTest, RetransmitsNackOverRtxWithPacing) { - // NACKs over RTX should use a separate SSRC. - TestNackRetransmission(kSendRtxSsrc, kSendRtxPayloadType, true); + TestNackRetransmission(kSendRtxSsrcs[0], kSendRtxPayloadType); } void VideoSendStreamTest::TestPacketFragmentationSize(VideoFormat format, bool with_fec) { - static const int kRedPayloadType = 118; - static const int kUlpfecPayloadType = 119; + // Use a fake encoder to output a frame of every size in the range [90, 290], + // for each size making sure that the exact number of payload bytes received + // is correct and that packets are fragmented to respect max packet size. + static const uint32_t kMaxPacketSize = 128; + static const uint32_t start = 90; + static const uint32_t stop = 290; + // Observer that verifies that the expected number of packets and bytes // arrive for each frame size, from start_size to stop_size. - class FrameFragmentationObserver : public test::RtpRtcpObserver, - public EncodedFrameObserver { + class FrameFragmentationTest : public test::SendTest, + public EncodedFrameObserver { public: - FrameFragmentationObserver(uint32_t max_packet_size, - uint32_t start_size, - uint32_t stop_size, - test::ConfigurableFrameSizeEncoder* encoder, - bool test_generic_packetization, - bool use_fec) - : RtpRtcpObserver(120 * 1000), // Timeout after two minutes. + FrameFragmentationTest(uint32_t max_packet_size, + uint32_t start_size, + uint32_t stop_size, + bool test_generic_packetization, + bool use_fec) + : SendTest(kLongTimeoutMs), transport_adapter_(SendTransport()), - encoder_(encoder), + encoder_(stop), max_packet_size_(max_packet_size), stop_size_(stop_size), test_generic_packetization_(test_generic_packetization), @@ -620,10 +505,12 @@ current_size_rtp_(start_size), current_size_frame_(start_size) { // Fragmentation required, this test doesn't make sense without it. + encoder_.SetFrameSize(start); assert(stop_size > max_packet_size); transport_adapter_.Enable(); } + private: virtual Action OnSendRtp(const uint8_t* packet, size_t size) OVERRIDE { uint32_t length = static_cast(size); RTPHeader header; @@ -699,13 +586,13 @@ if (packet_count_++ % 2 != 0) { // Receive statistics reporting having lost 50% of the packets. FakeReceiveStatistics lossy_receive_stats( - kSendSsrc, header.sequenceNumber, packet_count_ / 2, 127); + kSendSsrcs[0], header.sequenceNumber, packet_count_ / 2, 127); RTCPSender rtcp_sender( 0, false, Clock::GetRealTimeClock(), &lossy_receive_stats); EXPECT_EQ(0, rtcp_sender.RegisterSendTransport(&transport_adapter_)); rtcp_sender.SetRTCPStatus(kRtcpNonCompound); - rtcp_sender.SetRemoteSSRC(kSendSsrc); + rtcp_sender.SetRemoteSSRC(kSendSsrcs[0]); RTCPSender::FeedbackState feedback_state; @@ -720,12 +607,39 @@ current_size_frame_.Value() < static_cast(stop_size_)) { ++current_size_frame_; } - encoder_->SetFrameSize(current_size_frame_.Value()); + encoder_.SetFrameSize(current_size_frame_.Value()); + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + if (use_fec_) { + send_config->rtp.fec.red_payload_type = kRedPayloadType; + send_config->rtp.fec.ulpfec_payload_type = kUlpfecPayloadType; + } + + if (!test_generic_packetization_) + send_config->encoder_settings.payload_name = "VP8"; + + send_config->encoder_settings.encoder = &encoder_; + send_config->rtp.max_packet_size = kMaxPacketSize; + send_config->post_encode_callback = this; + + // Add an extension header, to make the RTP header larger than the base + // length of 12 bytes. + static const uint8_t kAbsSendTimeExtensionId = 13; + send_config->rtp.extensions.push_back( + RtpExtension(RtpExtension::kAbsSendTime, kAbsSendTimeExtensionId)); + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while observing incoming RTP packets."; } - private: internal::TransportAdapter transport_adapter_; - test::ConfigurableFrameSizeEncoder* const encoder_; + test::ConfigurableFrameSizeEncoder encoder_; const uint32_t max_packet_size_; const uint32_t stop_size_; @@ -741,47 +655,12 @@ Atomic32 current_size_frame_; }; - // Use a fake encoder to output a frame of every size in the range [90, 290], - // for each size making sure that the exact number of payload bytes received - // is correct and that packets are fragmented to respect max packet size. - static const uint32_t kMaxPacketSize = 128; - static const uint32_t start = 90; - static const uint32_t stop = 290; - // Don't auto increment if FEC is used; continue sending frame size until // a FEC packet has been received. - test::ConfigurableFrameSizeEncoder encoder(stop); - encoder.SetFrameSize(start); - - FrameFragmentationObserver observer( - kMaxPacketSize, start, stop, &encoder, format == kGeneric, with_fec); - Call::Config call_config(observer.SendTransport()); - scoped_ptr call(Call::Create(call_config)); - - observer.SetReceivers(call->Receiver(), NULL); - - VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1); - if (with_fec) { - send_config.rtp.fec.red_payload_type = kRedPayloadType; - send_config.rtp.fec.ulpfec_payload_type = kUlpfecPayloadType; - } - - if (format == kVP8) { - strcpy(send_config.codec.plName, "VP8"); - send_config.codec.codecType = kVideoCodecVP8; - } - send_config.pacing = false; - send_config.encoder = &encoder; - send_config.rtp.max_packet_size = kMaxPacketSize; - send_config.post_encode_callback = &observer; + FrameFragmentationTest test( + kMaxPacketSize, start, stop, format == kGeneric, with_fec); - // Add an extension header, to make the RTP header larger than the base - // length of 12 bytes. - static const uint8_t kAbsSendTimeExtensionId = 13; - send_config.rtp.extensions.push_back( - RtpExtension(RtpExtension::kAbsSendTime, kAbsSendTimeExtensionId)); - - RunSendTest(call.get(), send_config, &observer); + RunBaseTest(&test); } // TODO(sprang): Is there any way of speeding up these tests? @@ -801,68 +680,6 @@ TestPacketFragmentationSize(kVP8, true); } -TEST_F(VideoSendStreamTest, CanChangeSendCodec) { - static const uint8_t kFirstPayloadType = 121; - static const uint8_t kSecondPayloadType = 122; - - class CodecChangeObserver : public test::RtpRtcpObserver { - public: - CodecChangeObserver(VideoSendStream** send_stream_ptr) - : RtpRtcpObserver(30 * 1000), - received_first_payload_(EventWrapper::Create()), - send_stream_ptr_(send_stream_ptr) {} - - virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { - RTPHeader header; - EXPECT_TRUE(parser_->Parse(packet, static_cast(length), &header)); - - if (header.payloadType == kFirstPayloadType) { - received_first_payload_->Set(); - } else if (header.payloadType == kSecondPayloadType) { - observation_complete_->Set(); - } - - return SEND_PACKET; - } - - virtual EventTypeWrapper Wait() OVERRIDE { - EXPECT_EQ(kEventSignaled, received_first_payload_->Wait(30 * 1000)) - << "Timed out while waiting for first payload."; - - EXPECT_TRUE((*send_stream_ptr_)->SetCodec(second_codec_)); - - EXPECT_EQ(kEventSignaled, RtpRtcpObserver::Wait()) - << "Timed out while waiting for second payload type."; - - // Return OK regardless, prevents double error reporting. - return kEventSignaled; - } - - void SetSecondCodec(const VideoCodec& codec) { second_codec_ = codec; } - - private: - scoped_ptr received_first_payload_; - VideoSendStream** send_stream_ptr_; - VideoCodec second_codec_; - } observer(&send_stream_); - - Call::Config call_config(observer.SendTransport()); - scoped_ptr call(Call::Create(call_config)); - - std::vector codecs = call->GetVideoCodecs(); - ASSERT_GE(codecs.size(), 2u) - << "Test needs at least 2 separate codecs to work."; - codecs[0].plType = kFirstPayloadType; - codecs[1].plType = kSecondPayloadType; - observer.SetSecondCodec(codecs[1]); - - VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1); - send_config.codec = codecs[0]; - send_config.encoder = NULL; - - RunSendTest(call.get(), send_config, &observer); -} - // The test will go through a number of phases. // 1. Start sending packets. // 2. As soon as the RTP stream has been detected, signal a low REMB value to @@ -870,43 +687,41 @@ // 3. Wait until |kSuspendTimeFrames| have been captured without seeing any RTP // packets. // 4. Signal a high REMB and then wait for the RTP stream to start again. -// When the stream is detected again, the test ends. +// When the stream is detected again, and the stats show that the stream +// is no longer suspended, the test ends. TEST_F(VideoSendStreamTest, SuspendBelowMinBitrate) { static const int kSuspendTimeFrames = 60; // Suspend for 2 seconds @ 30 fps. - class RembObserver : public test::RtpRtcpObserver, public I420FrameCallback { + class RembObserver : public test::SendTest, public I420FrameCallback { public: RembObserver() - : RtpRtcpObserver(30 * 1000), // Timeout after 30 seconds. + : SendTest(kDefaultTimeoutMs), transport_adapter_(&transport_), clock_(Clock::GetRealTimeClock()), + crit_(CriticalSectionWrapper::CreateCriticalSection()), test_state_(kBeforeSuspend), rtp_count_(0), last_sequence_number_(0), suspended_frame_count_(0), low_remb_bps_(0), - high_remb_bps_(0), - crit_sect_(CriticalSectionWrapper::CreateCriticalSection()) { + high_remb_bps_(0) { transport_adapter_.Enable(); } - void SetReceiver(PacketReceiver* receiver) { - transport_.SetReceiver(receiver); - } - + private: virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE { // Receive statistics reporting having lost 0% of the packets. // This is needed for the send-side bitrate controller to work properly. - CriticalSectionScoped lock(crit_sect_.get()); + CriticalSectionScoped lock(crit_.get()); SendRtcpFeedback(0); // REMB is only sent if value is > 0. return SEND_PACKET; } virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { - CriticalSectionScoped lock(crit_sect_.get()); + CriticalSectionScoped lock(crit_.get()); ++rtp_count_; RTPHeader header; - EXPECT_TRUE(parser_->Parse(packet, static_cast(length), &header)); + EXPECT_TRUE(parser_->Parse(packet, length, &header)); last_sequence_number_ = header.sequenceNumber; if (test_state_ == kBeforeSuspend) { @@ -917,15 +732,18 @@ if (header.paddingLength == 0) { // Received non-padding packet during suspension period. Reset the // counter. - // TODO(hlundin): We should probably make this test more advanced in - // the future, so that it verifies that the bitrate can go below the - // min_bitrate. This requires that the fake encoder sees the - // min_bitrate, and never goes below it. See WebRTC Issue 2655. suspended_frame_count_ = 0; } } else if (test_state_ == kWaitingForPacket) { if (header.paddingLength == 0) { - // Non-padding packet observed. Test is complete. + // Non-padding packet observed. Test is almost complete. Will just + // have to wait for the stats to change. + test_state_ = kWaitingForStats; + } + } else if (test_state_ == kWaitingForStats) { + VideoSendStream::Stats stats = stream_->GetStats(); + if (stats.suspended == false) { + // Stats flipped to false. Test is complete. observation_complete_->Set(); } } @@ -935,36 +753,75 @@ // This method implements the I420FrameCallback. void FrameCallback(I420VideoFrame* video_frame) OVERRIDE { - CriticalSectionScoped lock(crit_sect_.get()); + CriticalSectionScoped lock(crit_.get()); if (test_state_ == kDuringSuspend && ++suspended_frame_count_ > kSuspendTimeFrames) { + VideoSendStream::Stats stats = stream_->GetStats(); + EXPECT_TRUE(stats.suspended); SendRtcpFeedback(high_remb_bps_); test_state_ = kWaitingForPacket; } } - void set_low_remb_bps(int value) { low_remb_bps_ = value; } - - void set_high_remb_bps(int value) { high_remb_bps_ = value; } - - void Stop() { transport_.StopSending(); } + void set_low_remb_bps(int value) { + CriticalSectionScoped lock(crit_.get()); + low_remb_bps_ = value; + } + + void set_high_remb_bps(int value) { + CriticalSectionScoped lock(crit_.get()); + high_remb_bps_ = value; + } + + virtual void SetReceivers( + PacketReceiver* send_transport_receiver, + PacketReceiver* receive_transport_receiver) OVERRIDE { + transport_.SetReceiver(send_transport_receiver); + } + + virtual void OnStreamsCreated( + VideoSendStream* send_stream, + const std::vector& receive_streams) OVERRIDE { + stream_ = send_stream; + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs; + send_config->pre_encode_callback = this; + send_config->suspend_below_min_bitrate = true; + int min_bitrate_bps = encoder_config->streams[0].min_bitrate_bps; + set_low_remb_bps(min_bitrate_bps - 10000); + int threshold_window = std::max(min_bitrate_bps / 10, 10000); + ASSERT_GT(encoder_config->streams[0].max_bitrate_bps, + min_bitrate_bps + threshold_window + 5000); + set_high_remb_bps(min_bitrate_bps + threshold_window + 5000); + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out during suspend-below-min-bitrate test."; + transport_.StopSending(); + } - private: enum TestState { kBeforeSuspend, kDuringSuspend, kWaitingForPacket, - kAfterSuspend + kWaitingForStats }; - virtual void SendRtcpFeedback(int remb_value) { + virtual void SendRtcpFeedback(int remb_value) + EXCLUSIVE_LOCKS_REQUIRED(crit_) { FakeReceiveStatistics receive_stats( - kSendSsrc, last_sequence_number_, rtp_count_, 0); + kSendSsrcs[0], last_sequence_number_, rtp_count_, 0); RTCPSender rtcp_sender(0, false, clock_, &receive_stats); EXPECT_EQ(0, rtcp_sender.RegisterSendTransport(&transport_adapter_)); rtcp_sender.SetRTCPStatus(kRtcpNonCompound); - rtcp_sender.SetRemoteSSRC(kSendSsrc); + rtcp_sender.SetRemoteSSRC(kSendSsrcs[0]); if (remb_value > 0) { rtcp_sender.SetREMBStatus(true); rtcp_sender.SetREMBData(remb_value, 0, NULL); @@ -975,62 +832,44 @@ internal::TransportAdapter transport_adapter_; test::DirectTransport transport_; - Clock* clock_; - TestState test_state_; - int rtp_count_; - int last_sequence_number_; - int suspended_frame_count_; - int low_remb_bps_; - int high_remb_bps_; - scoped_ptr crit_sect_; - } observer; - - Call::Config call_config(observer.SendTransport()); - scoped_ptr call(Call::Create(call_config)); - observer.SetReceiver(call->Receiver()); - - VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1); - send_config.rtp.nack.rtp_history_ms = 1000; - send_config.pre_encode_callback = &observer; - send_config.suspend_below_min_bitrate = true; - unsigned int min_bitrate_bps = - send_config.codec.simulcastStream[0].minBitrate * 1000; - observer.set_low_remb_bps(min_bitrate_bps - 10000); - unsigned int threshold_window = std::max(min_bitrate_bps / 10, 10000u); - ASSERT_GT(send_config.codec.simulcastStream[0].maxBitrate * 1000, - min_bitrate_bps + threshold_window + 5000); - observer.set_high_remb_bps(min_bitrate_bps + threshold_window + 5000); + Clock* const clock_; + VideoSendStream* stream_; + + const scoped_ptr crit_; + TestState test_state_ GUARDED_BY(crit_); + int rtp_count_ GUARDED_BY(crit_); + int last_sequence_number_ GUARDED_BY(crit_); + int suspended_frame_count_ GUARDED_BY(crit_); + int low_remb_bps_ GUARDED_BY(crit_); + int high_remb_bps_ GUARDED_BY(crit_); + } test; - RunSendTest(call.get(), send_config, &observer); - observer.Stop(); + RunBaseTest(&test); } TEST_F(VideoSendStreamTest, NoPaddingWhenVideoIsMuted) { - class PacketObserver : public test::RtpRtcpObserver { + class NoPaddingWhenVideoIsMuted : public test::SendTest { public: - PacketObserver() - : RtpRtcpObserver(30 * 1000), // Timeout after 30 seconds. + NoPaddingWhenVideoIsMuted() + : SendTest(kDefaultTimeoutMs), clock_(Clock::GetRealTimeClock()), - last_packet_time_ms_(-1), transport_adapter_(ReceiveTransport()), - capturer_(NULL), - crit_sect_(CriticalSectionWrapper::CreateCriticalSection()) { + crit_(CriticalSectionWrapper::CreateCriticalSection()), + last_packet_time_ms_(-1), + capturer_(NULL) { transport_adapter_.Enable(); } - void SetCapturer(test::FrameGeneratorCapturer* capturer) { - capturer_ = capturer; - } - + private: virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { - CriticalSectionScoped lock(crit_sect_.get()); + CriticalSectionScoped lock(crit_.get()); last_packet_time_ms_ = clock_->TimeInMilliseconds(); capturer_->Stop(); return SEND_PACKET; } virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE { - CriticalSectionScoped lock(crit_sect_.get()); + CriticalSectionScoped lock(crit_.get()); const int kVideoMutedThresholdMs = 10000; if (last_packet_time_ms_ > 0 && clock_->TimeInMilliseconds() - last_packet_time_ms_ > @@ -1051,49 +890,71 @@ return SEND_PACKET; } - private: - Clock* clock_; - int64_t last_packet_time_ms_; + virtual void SetReceivers( + PacketReceiver* send_transport_receiver, + PacketReceiver* receive_transport_receiver) OVERRIDE { + RtpRtcpObserver::SetReceivers(send_transport_receiver, + send_transport_receiver); + } + + virtual size_t GetNumStreams() const OVERRIDE { return 3; } + + virtual void OnFrameGeneratorCapturerCreated( + test::FrameGeneratorCapturer* frame_generator_capturer) { + CriticalSectionScoped lock(crit_.get()); + capturer_ = frame_generator_capturer; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for RTP packets to stop being sent."; + } + + Clock* const clock_; internal::TransportAdapter transport_adapter_; - test::FrameGeneratorCapturer* capturer_; - scoped_ptr crit_sect_; - } observer; - - Call::Config call_config(observer.SendTransport()); - scoped_ptr call(Call::Create(call_config)); - observer.SetReceivers(call->Receiver(), call->Receiver()); - - VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 3); - - send_stream_ = call->CreateVideoSendStream(send_config); - scoped_ptr frame_generator_capturer( - test::FrameGeneratorCapturer::Create( - send_stream_->Input(), 320, 240, 30, Clock::GetRealTimeClock())); - observer.SetCapturer(frame_generator_capturer.get()); - send_stream_->StartSending(); - frame_generator_capturer->Start(); - - EXPECT_EQ(kEventSignaled, observer.Wait()) - << "Timed out while waiting for RTP packets to stop being sent."; - - observer.StopSending(); - frame_generator_capturer->Stop(); - send_stream_->StopSending(); - call->DestroyVideoSendStream(send_stream_); + const scoped_ptr crit_; + int64_t last_packet_time_ms_ GUARDED_BY(crit_); + test::FrameGeneratorCapturer* capturer_ GUARDED_BY(crit_); + } test; + + RunBaseTest(&test); } TEST_F(VideoSendStreamTest, ProducesStats) { - static std::string kCName = "PjQatC14dGfbVwGPUOA9IH7RlsFDbWl4AhXEiDsBizo="; - class StatsObserver : public test::RtpRtcpObserver { + class ProducesStats : public test::SendTest { public: - StatsObserver() : RtpRtcpObserver(30 * 1000), stream_(NULL) {} + ProducesStats() + : SendTest(kDefaultTimeoutMs), + stream_(NULL), + event_(EventWrapper::Create()) {} virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE { + event_->Set(); + + return SEND_PACKET; + } + + private: + bool WaitForFilledStats() { + Clock* clock = Clock::GetRealTimeClock(); + int64_t now = clock->TimeInMilliseconds(); + int64_t stop_time = now + kDefaultTimeoutMs; + while (now < stop_time) { + int64_t time_left = stop_time - now; + if (time_left > 0 && event_->Wait(time_left) == kEventSignaled && + CheckStats()) { + return true; + } + now = clock->TimeInMilliseconds(); + } + return false; + } + + bool CheckStats() { VideoSendStream::Stats stats = stream_->GetStats(); // Check that all applicable data sources have been used. - if (stats.input_frame_rate > 0 && stats.encode_frame_rate > 0 && - stats.avg_delay_ms > 0 && stats.c_name == kCName && - !stats.substreams.empty()) { + if (stats.input_frame_rate > 0 && stats.encode_frame_rate > 0 + && !stats.substreams.empty()) { uint32_t ssrc = stats.substreams.begin()->first; EXPECT_NE( config_.rtp.ssrcs.end(), @@ -1101,45 +962,632 @@ config_.rtp.ssrcs.begin(), config_.rtp.ssrcs.end(), ssrc)); // Check for data populated by various sources. RTCP excluded as this // data is received from remote side. Tested in call tests instead. - StreamStats& entry = stats.substreams[ssrc]; - if (entry.key_frames > 0u && entry.bitrate_bps > 0 && - entry.rtp_stats.packets > 0u) { - observation_complete_->Set(); + const SsrcStats& entry = stats.substreams[ssrc]; + if (entry.key_frames > 0u && entry.total_bitrate_bps > 0 && + entry.rtp_stats.packets > 0u && entry.avg_delay_ms > 0 && + entry.max_delay_ms > 0) { + return true; } } - - return SEND_PACKET; + return false; } void SetConfig(const VideoSendStream::Config& config) { config_ = config; } - void SetSendStream(VideoSendStream* stream) { stream_ = stream; } + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + SetConfig(*send_config); + } + + virtual void OnStreamsCreated( + VideoSendStream* send_stream, + const std::vector& receive_streams) OVERRIDE { + stream_ = send_stream; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_TRUE(WaitForFilledStats()) + << "Timed out waiting for filled statistics."; + } VideoSendStream* stream_; VideoSendStream::Config config_; - } observer; + scoped_ptr event_; + } test; + + RunBaseTest(&test); +} + +// This test first observes "high" bitrate use at which point it sends a REMB to +// indicate that it should be lowered significantly. The test then observes that +// the bitrate observed is sinking well below the min-transmit-bitrate threshold +// to verify that the min-transmit bitrate respects incoming REMB. +// +// Note that the test starts at "high" bitrate and does not ramp up to "higher" +// bitrate since no receiver block or remb is sent in the initial phase. +TEST_F(VideoSendStreamTest, MinTransmitBitrateRespectsRemb) { + static const int kMinTransmitBitrateBps = 400000; + static const int kHighBitrateBps = 150000; + static const int kRembBitrateBps = 80000; + static const int kRembRespectedBitrateBps = 100000; + class BitrateObserver : public test::SendTest, public PacketReceiver { + public: + BitrateObserver() + : SendTest(kDefaultTimeoutMs), + feedback_transport_(ReceiveTransport()), + bitrate_capped_(false) { + RtpRtcp::Configuration config; + feedback_transport_.Enable(); + config.outgoing_transport = &feedback_transport_; + rtp_rtcp_.reset(RtpRtcp::CreateRtpRtcp(config)); + rtp_rtcp_->SetREMBStatus(true); + rtp_rtcp_->SetRTCPStatus(kRtcpNonCompound); + } + + virtual void OnStreamsCreated( + VideoSendStream* send_stream, + const std::vector& receive_streams) OVERRIDE { + stream_ = send_stream; + } + + private: + virtual DeliveryStatus DeliverPacket(const uint8_t* packet, + size_t length) OVERRIDE { + if (RtpHeaderParser::IsRtcp(packet, length)) + return DELIVERY_OK; + + RTPHeader header; + if (!parser_->Parse(packet, length, &header)) + return DELIVERY_PACKET_ERROR; + assert(stream_ != NULL); + VideoSendStream::Stats stats = stream_->GetStats(); + if (!stats.substreams.empty()) { + EXPECT_EQ(1u, stats.substreams.size()); + int total_bitrate_bps = + stats.substreams.begin()->second.total_bitrate_bps; + test::PrintResult("bitrate_stats_", + "min_transmit_bitrate_low_remb", + "bitrate_bps", + static_cast(total_bitrate_bps), + "bps", + false); + if (total_bitrate_bps > kHighBitrateBps) { + rtp_rtcp_->SetREMBData(kRembBitrateBps, 1, &header.ssrc); + rtp_rtcp_->Process(); + bitrate_capped_ = true; + } else if (bitrate_capped_ && + total_bitrate_bps < kRembRespectedBitrateBps) { + observation_complete_->Set(); + } + } + return DELIVERY_OK; + } + + virtual void SetReceivers( + PacketReceiver* send_transport_receiver, + PacketReceiver* receive_transport_receiver) OVERRIDE { + RtpRtcpObserver::SetReceivers(this, send_transport_receiver); + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + encoder_config->min_transmit_bitrate_bps = kMinTransmitBitrateBps; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timeout while waiting for low bitrate stats after REMB."; + } + + scoped_ptr rtp_rtcp_; + internal::TransportAdapter feedback_transport_; + VideoSendStream* stream_; + bool bitrate_capped_; + } test; + + RunBaseTest(&test); +} + +TEST_F(VideoSendStreamTest, CapturesTextureAndI420VideoFrames) { + class FrameObserver : public I420FrameCallback { + public: + FrameObserver() : output_frame_event_(EventWrapper::Create()) {} + + void FrameCallback(I420VideoFrame* video_frame) OVERRIDE { + // Clone the frame because the caller owns it. + output_frames_.push_back(video_frame->CloneFrame()); + output_frame_event_->Set(); + } + + void WaitOutputFrame() { + const unsigned long kWaitFrameTimeoutMs = 3000; + EXPECT_EQ(kEventSignaled, output_frame_event_->Wait(kWaitFrameTimeoutMs)) + << "Timeout while waiting for output frames."; + } + + const std::vector& output_frames() const { + return output_frames_.get(); + } + + private: + // Delivered output frames. + ScopedVector output_frames_; + + // Indicate an output frame has arrived. + scoped_ptr output_frame_event_; + }; + + // Initialize send stream. + test::NullTransport transport; + CreateSenderCall(Call::Config(&transport)); + + CreateSendConfig(1); + FrameObserver observer; + send_config_.pre_encode_callback = &observer; + CreateStreams(); + + // Prepare five input frames. Send I420VideoFrame and TextureVideoFrame + // alternatively. + ScopedVector input_frames; + int width = static_cast(encoder_config_.streams[0].width); + int height = static_cast(encoder_config_.streams[0].height); + webrtc::RefCountImpl* handle1 = + new webrtc::RefCountImpl(); + webrtc::RefCountImpl* handle2 = + new webrtc::RefCountImpl(); + webrtc::RefCountImpl* handle3 = + new webrtc::RefCountImpl(); + input_frames.push_back(new TextureVideoFrame(handle1, width, height, 1, 1)); + input_frames.push_back(new TextureVideoFrame(handle2, width, height, 2, 2)); + input_frames.push_back(CreateI420VideoFrame(width, height, 1)); + input_frames.push_back(CreateI420VideoFrame(width, height, 2)); + input_frames.push_back(new TextureVideoFrame(handle3, width, height, 3, 3)); + + send_stream_->Start(); + for (size_t i = 0; i < input_frames.size(); i++) { + // Make a copy of the input frame because the buffer will be swapped. + scoped_ptr frame(input_frames[i]->CloneFrame()); + send_stream_->Input()->SwapFrame(frame.get()); + // Do not send the next frame too fast, so the frame dropper won't drop it. + if (i < input_frames.size() - 1) + SleepMs(1000 / encoder_config_.streams[0].max_framerate); + // Wait until the output frame is received before sending the next input + // frame. Or the previous input frame may be replaced without delivering. + observer.WaitOutputFrame(); + } + send_stream_->Stop(); + + // Test if the input and output frames are the same. render_time_ms and + // timestamp are not compared because capturer sets those values. + ExpectEqualFramesVector(input_frames.get(), observer.output_frames()); + + DestroyStreams(); +} + +void ExpectEqualFrames(const I420VideoFrame& frame1, + const I420VideoFrame& frame2) { + if (frame1.native_handle() != NULL || frame2.native_handle() != NULL) + ExpectEqualTextureFrames(frame1, frame2); + else + ExpectEqualBufferFrames(frame1, frame2); +} + +void ExpectEqualTextureFrames(const I420VideoFrame& frame1, + const I420VideoFrame& frame2) { + EXPECT_EQ(frame1.native_handle(), frame2.native_handle()); + EXPECT_EQ(frame1.width(), frame2.width()); + EXPECT_EQ(frame1.height(), frame2.height()); +} + +void ExpectEqualBufferFrames(const I420VideoFrame& frame1, + const I420VideoFrame& frame2) { + EXPECT_EQ(frame1.width(), frame2.width()); + EXPECT_EQ(frame1.height(), frame2.height()); + EXPECT_EQ(frame1.stride(kYPlane), frame2.stride(kYPlane)); + EXPECT_EQ(frame1.stride(kUPlane), frame2.stride(kUPlane)); + EXPECT_EQ(frame1.stride(kVPlane), frame2.stride(kVPlane)); + EXPECT_EQ(frame1.ntp_time_ms(), frame2.ntp_time_ms()); + ASSERT_EQ(frame1.allocated_size(kYPlane), frame2.allocated_size(kYPlane)); + EXPECT_EQ(0, + memcmp(frame1.buffer(kYPlane), + frame2.buffer(kYPlane), + frame1.allocated_size(kYPlane))); + ASSERT_EQ(frame1.allocated_size(kUPlane), frame2.allocated_size(kUPlane)); + EXPECT_EQ(0, + memcmp(frame1.buffer(kUPlane), + frame2.buffer(kUPlane), + frame1.allocated_size(kUPlane))); + ASSERT_EQ(frame1.allocated_size(kVPlane), frame2.allocated_size(kVPlane)); + EXPECT_EQ(0, + memcmp(frame1.buffer(kVPlane), + frame2.buffer(kVPlane), + frame1.allocated_size(kVPlane))); +} + +void ExpectEqualFramesVector(const std::vector& frames1, + const std::vector& frames2) { + EXPECT_EQ(frames1.size(), frames2.size()); + for (size_t i = 0; i < std::min(frames1.size(), frames2.size()); ++i) + ExpectEqualFrames(*frames1[i], *frames2[i]); +} + +I420VideoFrame* CreateI420VideoFrame(int width, int height, uint8_t data) { + I420VideoFrame* frame = new I420VideoFrame(); + const int kSizeY = width * height * 2; + const int kSizeUV = width * height; + scoped_ptr buffer(new uint8_t[kSizeY]); + memset(buffer.get(), data, kSizeY); + frame->CreateFrame(kSizeY, + buffer.get(), + kSizeUV, + buffer.get(), + kSizeUV, + buffer.get(), + width, + height, + width, + width / 2, + width / 2); + frame->set_timestamp(data); + frame->set_ntp_time_ms(data); + frame->set_render_time_ms(data); + return frame; +} + +TEST_F(VideoSendStreamTest, EncoderIsProperlyInitializedAndDestroyed) { + class EncoderStateObserver : public test::SendTest, public VideoEncoder { + public: + EncoderStateObserver() + : SendTest(kDefaultTimeoutMs), + crit_(CriticalSectionWrapper::CreateCriticalSection()), + initialized_(false), + callback_registered_(false), + num_releases_(0), + released_(false) {} + + bool IsReleased() { + CriticalSectionScoped lock(crit_.get()); + return released_; + } + + bool IsReadyForEncode() { + CriticalSectionScoped lock(crit_.get()); + return initialized_ && callback_registered_; + } + + size_t num_releases() { + CriticalSectionScoped lock(crit_.get()); + return num_releases_; + } + + private: + virtual int32_t InitEncode(const VideoCodec* codecSettings, + int32_t numberOfCores, + uint32_t maxPayloadSize) OVERRIDE { + CriticalSectionScoped lock(crit_.get()); + EXPECT_FALSE(initialized_); + initialized_ = true; + released_ = false; + return 0; + } + + virtual int32_t Encode( + const I420VideoFrame& inputImage, + const CodecSpecificInfo* codecSpecificInfo, + const std::vector* frame_types) OVERRIDE { + EXPECT_TRUE(IsReadyForEncode()); + + observation_complete_->Set(); + return 0; + } + + virtual int32_t RegisterEncodeCompleteCallback( + EncodedImageCallback* callback) OVERRIDE { + CriticalSectionScoped lock(crit_.get()); + EXPECT_TRUE(initialized_); + callback_registered_ = true; + return 0; + } + + virtual int32_t Release() OVERRIDE { + CriticalSectionScoped lock(crit_.get()); + EXPECT_TRUE(IsReadyForEncode()); + EXPECT_FALSE(released_); + initialized_ = false; + callback_registered_ = false; + released_ = true; + ++num_releases_; + return 0; + } + + virtual int32_t SetChannelParameters(uint32_t packetLoss, + int rtt) OVERRIDE { + EXPECT_TRUE(IsReadyForEncode()); + return 0; + } + + virtual int32_t SetRates(uint32_t newBitRate, uint32_t frameRate) OVERRIDE { + EXPECT_TRUE(IsReadyForEncode()); + return 0; + } + + virtual void OnStreamsCreated( + VideoSendStream* send_stream, + const std::vector& receive_streams) OVERRIDE { + // Encoder initialization should be done in stream construction before + // starting. + EXPECT_TRUE(IsReadyForEncode()); + stream_ = send_stream; + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->encoder_settings.encoder = this; + encoder_config_ = *encoder_config; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for Encode."; + EXPECT_EQ(0u, num_releases()); + stream_->ReconfigureVideoEncoder(encoder_config_); + EXPECT_EQ(0u, num_releases()); + stream_->Stop(); + // Encoder should not be released before destroying the VideoSendStream. + EXPECT_FALSE(IsReleased()); + EXPECT_TRUE(IsReadyForEncode()); + stream_->Start(); + // Sanity check, make sure we still encode frames with this encoder. + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for Encode."; + } + + scoped_ptr crit_; + VideoSendStream* stream_; + bool initialized_ GUARDED_BY(crit_); + bool callback_registered_ GUARDED_BY(crit_); + size_t num_releases_ GUARDED_BY(crit_); + bool released_ GUARDED_BY(crit_); + VideoEncoderConfig encoder_config_; + } test_encoder; + + RunBaseTest(&test_encoder); + + EXPECT_TRUE(test_encoder.IsReleased()); + EXPECT_EQ(1u, test_encoder.num_releases()); +} + +TEST_F(VideoSendStreamTest, EncoderSetupPropagatesCommonEncoderConfigValues) { + class VideoCodecConfigObserver : public test::SendTest, + public test::FakeEncoder { + public: + VideoCodecConfigObserver() + : SendTest(kDefaultTimeoutMs), + FakeEncoder(Clock::GetRealTimeClock()), + num_initializations_(0) {} + + private: + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->encoder_settings.encoder = this; + encoder_config_ = *encoder_config; + } + + virtual void OnStreamsCreated( + VideoSendStream* send_stream, + const std::vector& receive_streams) OVERRIDE { + stream_ = send_stream; + } + + virtual int32_t InitEncode(const VideoCodec* config, + int32_t number_of_cores, + uint32_t max_payload_size) OVERRIDE { + if (num_initializations_ == 0) { + // Verify default values. + EXPECT_EQ(kRealtimeVideo, config->mode); + } else { + // Verify that changed values are propagated. + EXPECT_EQ(kScreensharing, config->mode); + } + ++num_initializations_; + return FakeEncoder::InitEncode(config, number_of_cores, max_payload_size); + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(1u, num_initializations_) << "VideoEncoder not initialized."; + + encoder_config_.content_type = VideoEncoderConfig::kScreenshare; + stream_->ReconfigureVideoEncoder(encoder_config_); + EXPECT_EQ(2u, num_initializations_) + << "ReconfigureVideoEncoder did not reinitialize the encoder with " + "new encoder settings."; + } + + size_t num_initializations_; + VideoSendStream* stream_; + VideoEncoderConfig encoder_config_; + } test; + + RunBaseTest(&test); +} + +TEST_F(VideoSendStreamTest, EncoderSetupPropagatesVp8Config) { + static const size_t kNumberOfTemporalLayers = 4; + class VideoCodecConfigObserver : public test::SendTest, + public test::FakeEncoder { + public: + VideoCodecConfigObserver() + : SendTest(kDefaultTimeoutMs), + FakeEncoder(Clock::GetRealTimeClock()), + num_initializations_(0) { + memset(&vp8_settings_, 0, sizeof(vp8_settings_)); + } + + private: + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->encoder_settings.encoder = this; + send_config->encoder_settings.payload_name = "VP8"; + + for (size_t i = 0; i < encoder_config->streams.size(); ++i) { + encoder_config->streams[i].temporal_layer_thresholds_bps.resize( + kNumberOfTemporalLayers - 1); + } + + encoder_config->encoder_specific_settings = &vp8_settings_; + encoder_config_ = *encoder_config; + } + + virtual void OnStreamsCreated( + VideoSendStream* send_stream, + const std::vector& receive_streams) OVERRIDE { + stream_ = send_stream; + } + + virtual int32_t InitEncode(const VideoCodec* config, + int32_t number_of_cores, + uint32_t max_payload_size) OVERRIDE { + EXPECT_EQ(kVideoCodecVP8, config->codecType); + + // Check that the number of temporal layers has propagated properly to + // VideoCodec. + EXPECT_EQ(kNumberOfTemporalLayers, + config->codecSpecific.VP8.numberOfTemporalLayers); + + for (unsigned char i = 0; i < config->numberOfSimulcastStreams; ++i) { + EXPECT_EQ(kNumberOfTemporalLayers, + config->simulcastStream[i].numberOfTemporalLayers); + } + + // Set expected temporal layers as they should have been set when + // reconfiguring the encoder and not match the set config. + vp8_settings_.numberOfTemporalLayers = kNumberOfTemporalLayers; + EXPECT_EQ(0, + memcmp(&config->codecSpecific.VP8, + &vp8_settings_, + sizeof(vp8_settings_))); + ++num_initializations_; + return FakeEncoder::InitEncode(config, number_of_cores, max_payload_size); + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(1u, num_initializations_) << "VideoEncoder not initialized."; + + vp8_settings_.denoisingOn = true; + stream_->ReconfigureVideoEncoder(encoder_config_); + EXPECT_EQ(2u, num_initializations_) + << "ReconfigureVideoEncoder did not reinitialize the encoder with " + "new encoder settings."; + } + + int32_t Encode(const I420VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector* frame_types) OVERRIDE { + // Silently skip the encode, FakeEncoder::Encode doesn't produce VP8. + return 0; + } + + VideoCodecVP8 vp8_settings_; + size_t num_initializations_; + VideoSendStream* stream_; + VideoEncoderConfig encoder_config_; + } test; + + RunBaseTest(&test); +} + +TEST_F(VideoSendStreamTest, RtcpSenderReportContainsMediaBytesSent) { + class RtcpByeTest : public test::SendTest { + public: + RtcpByeTest() : SendTest(kDefaultTimeoutMs), media_bytes_sent_(0) {} + + private: + virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { + RTPHeader header; + EXPECT_TRUE(parser_->Parse(packet, length, &header)); + media_bytes_sent_ += length - header.headerLength - header.paddingLength; + return SEND_PACKET; + } + + virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE { + RTCPUtility::RTCPParserV2 parser(packet, length, true); + EXPECT_TRUE(parser.IsValid()); + + RTCPUtility::RTCPPacketTypes packet_type = parser.Begin(); + uint32_t sender_octet_count = 0; + while (packet_type != RTCPUtility::kRtcpNotValidCode) { + if (packet_type == RTCPUtility::kRtcpSrCode) { + sender_octet_count = parser.Packet().SR.SenderOctetCount; + EXPECT_EQ(sender_octet_count, media_bytes_sent_); + if (sender_octet_count > 0) + observation_complete_->Set(); + } + + packet_type = parser.Iterate(); + } + + return SEND_PACKET; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for RTCP sender report."; + } - Call::Config call_config(observer.SendTransport()); - scoped_ptr call(Call::Create(call_config)); + size_t media_bytes_sent_; + } test; - VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1); - send_config.rtp.c_name = kCName; - observer.SetConfig(send_config); - - send_stream_ = call->CreateVideoSendStream(send_config); - observer.SetSendStream(send_stream_); - scoped_ptr frame_generator_capturer( - test::FrameGeneratorCapturer::Create( - send_stream_->Input(), 320, 240, 30, Clock::GetRealTimeClock())); - send_stream_->StartSending(); - frame_generator_capturer->Start(); - - EXPECT_EQ(kEventSignaled, observer.Wait()); - - observer.StopSending(); - frame_generator_capturer->Stop(); - send_stream_->StopSending(); - call->DestroyVideoSendStream(send_stream_); + RunBaseTest(&test); } +TEST_F(VideoSendStreamTest, TranslatesTwoLayerScreencastToTargetBitrate) { + static const int kScreencastTargetBitrateKbps = 200; + class ScreencastTargetBitrateTest : public test::SendTest, + public test::FakeEncoder { + public: + ScreencastTargetBitrateTest() + : SendTest(kDefaultTimeoutMs), + test::FakeEncoder(Clock::GetRealTimeClock()) {} + + private: + virtual int32_t InitEncode(const VideoCodec* config, + int32_t number_of_cores, + uint32_t max_payload_size) { + EXPECT_EQ(static_cast(kScreencastTargetBitrateKbps), + config->targetBitrate); + observation_complete_->Set(); + return test::FakeEncoder::InitEncode( + config, number_of_cores, max_payload_size); + } + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->encoder_settings.encoder = this; + EXPECT_EQ(1u, encoder_config->streams.size()); + EXPECT_TRUE( + encoder_config->streams[0].temporal_layer_thresholds_bps.empty()); + encoder_config->streams[0].temporal_layer_thresholds_bps.push_back( + kScreencastTargetBitrateKbps * 1000); + encoder_config->content_type = VideoEncoderConfig::kScreenshare; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for the encoder to be initialized."; + } + } test; + + RunBaseTest(&test); +} } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_decoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_decoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_decoder.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_decoder.h 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_VIDEO_DECODER_H_ +#define WEBRTC_VIDEO_DECODER_H_ + +#include + +#include "webrtc/common_types.h" +#include "webrtc/typedefs.h" +#include "webrtc/video_frame.h" + +namespace webrtc { + +class RTPFragmentationHeader; +// TODO(pbos): Expose these through a public (root) header or change these APIs. +struct CodecSpecificInfo; +struct VideoCodec; + +class DecodedImageCallback { + public: + virtual ~DecodedImageCallback() {} + + virtual int32_t Decoded(I420VideoFrame& decodedImage) = 0; + virtual int32_t ReceivedDecodedReferenceFrame(const uint64_t pictureId) { + return -1; + } + + virtual int32_t ReceivedDecodedFrame(const uint64_t pictureId) { return -1; } +}; + +class VideoDecoder { + public: + enum DecoderType { + kVp8, + kVp9 + }; + + static VideoDecoder* Create(DecoderType codec_type); + + virtual ~VideoDecoder() {} + + virtual int32_t InitDecode(const VideoCodec* codecSettings, + int32_t numberOfCores) = 0; + + virtual int32_t Decode(const EncodedImage& inputImage, + bool missingFrames, + const RTPFragmentationHeader* fragmentation, + const CodecSpecificInfo* codecSpecificInfo = NULL, + int64_t renderTimeMs = -1) = 0; + + virtual int32_t RegisterDecodeCompleteCallback( + DecodedImageCallback* callback) = 0; + + virtual int32_t Release() = 0; + virtual int32_t Reset() = 0; + + virtual int32_t SetCodecConfigParameters(const uint8_t* /*buffer*/, + int32_t /*size*/) { + return -1; + } + + virtual VideoDecoder* Copy() { return NULL; } +}; + +} // namespace webrtc + +#endif // WEBRTC_VIDEO_DECODER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_encoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_encoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_encoder.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_encoder.h 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_VIDEO_ENCODER_H_ +#define WEBRTC_VIDEO_ENCODER_H_ + +#include + +#include "webrtc/common_types.h" +#include "webrtc/typedefs.h" +#include "webrtc/video_frame.h" + +namespace webrtc { + +class RTPFragmentationHeader; +// TODO(pbos): Expose these through a public (root) header or change these APIs. +struct CodecSpecificInfo; +struct VideoCodec; + +class EncodedImageCallback { + public: + virtual ~EncodedImageCallback() {} + + // Callback function which is called when an image has been encoded. + // TODO(pbos): Make encoded_image const or pointer. Remove default arguments. + virtual int32_t Encoded( + EncodedImage& encoded_image, + const CodecSpecificInfo* codec_specific_info = NULL, + const RTPFragmentationHeader* fragmentation = NULL) = 0; +}; + +class VideoEncoder { + public: + enum EncoderType { + kVp8, + kVp9, + }; + + static VideoEncoder* Create(EncoderType codec_type); + + static VideoCodecVP8 GetDefaultVp8Settings(); + static VideoCodecVP9 GetDefaultVp9Settings(); + static VideoCodecH264 GetDefaultH264Settings(); + + virtual ~VideoEncoder() {} + + // Initialize the encoder with the information from the codecSettings + // + // Input: + // - codec_settings : Codec settings + // - number_of_cores : Number of cores available for the encoder + // - max_payload_size : The maximum size each payload is allowed + // to have. Usually MTU - overhead. + // + // Return value : Set bit rate if OK + // <0 - Errors: + // WEBRTC_VIDEO_CODEC_ERR_PARAMETER + // WEBRTC_VIDEO_CODEC_ERR_SIZE + // WEBRTC_VIDEO_CODEC_LEVEL_EXCEEDED + // WEBRTC_VIDEO_CODEC_MEMORY + // WEBRTC_VIDEO_CODEC_ERROR + virtual int32_t InitEncode(const VideoCodec* codec_settings, + int32_t number_of_cores, + uint32_t max_payload_size) = 0; + + // Register an encode complete callback object. + // + // Input: + // - callback : Callback object which handles encoded images. + // + // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. + virtual int32_t RegisterEncodeCompleteCallback( + EncodedImageCallback* callback) = 0; + + // Free encoder memory. + // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. + virtual int32_t Release() = 0; + + // Encode an I420 image (as a part of a video stream). The encoded image + // will be returned to the user through the encode complete callback. + // + // Input: + // - frame : Image to be encoded + // - frame_types : Frame type to be generated by the encoder. + // + // Return value : WEBRTC_VIDEO_CODEC_OK if OK + // <0 - Errors: + // WEBRTC_VIDEO_CODEC_ERR_PARAMETER + // WEBRTC_VIDEO_CODEC_MEMORY + // WEBRTC_VIDEO_CODEC_ERROR + // WEBRTC_VIDEO_CODEC_TIMEOUT + virtual int32_t Encode(const I420VideoFrame& frame, + const CodecSpecificInfo* codec_specific_info, + const std::vector* frame_types) = 0; + + // Inform the encoder of the new packet loss rate and the round-trip time of + // the network. + // + // Input: + // - packet_loss : Fraction lost + // (loss rate in percent = 100 * packetLoss / 255) + // - rtt : Round-trip time in milliseconds + // Return value : WEBRTC_VIDEO_CODEC_OK if OK + // <0 - Errors: WEBRTC_VIDEO_CODEC_ERROR + virtual int32_t SetChannelParameters(uint32_t packet_loss, int rtt) = 0; + + // Inform the encoder about the new target bit rate. + // + // Input: + // - bitrate : New target bit rate + // - framerate : The target frame rate + // + // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. + virtual int32_t SetRates(uint32_t bitrate, uint32_t framerate) = 0; + + virtual int32_t SetPeriodicKeyFrames(bool enable) { return -1; } + virtual int32_t CodecConfigParameters(uint8_t* /*buffer*/, int32_t /*size*/) { + return -1; + } +}; + +} // namespace webrtc +#endif // WEBRTC_VIDEO_ENCODER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/Android.mk 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE := libwebrtc_vie_core -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := \ - vie_base_impl.cc \ - vie_capture_impl.cc \ - vie_codec_impl.cc \ - vie_external_codec_impl.cc \ - vie_file_impl.cc \ - vie_image_process_impl.cc \ - vie_impl.cc \ - vie_network_impl.cc \ - vie_ref_count.cc \ - vie_render_impl.cc \ - vie_rtp_rtcp_impl.cc \ - vie_shared_data.cc \ - vie_capturer.cc \ - vie_channel.cc \ - vie_channel_group.cc \ - vie_channel_manager.cc \ - vie_encoder.cc \ - vie_file_image.cc \ - vie_file_player.cc \ - vie_file_recorder.cc \ - vie_frame_provider_base.cc \ - vie_input_manager.cc \ - vie_manager_base.cc \ - vie_performance_monitor.cc \ - vie_receiver.cc \ - vie_remb.cc \ - vie_renderer.cc \ - vie_render_manager.cc \ - vie_sender.cc \ - vie_sync_module.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include\ - $(LOCAL_PATH)/.. \ - $(LOCAL_PATH)/../common_video/interface \ - $(LOCAL_PATH)/../common_video/jpeg/main/interface \ - $(LOCAL_PATH)/../common_video/vplib/main/interface \ - $(LOCAL_PATH)/../modules/interface \ - $(LOCAL_PATH)/../modules/audio_coding/main/interface \ - $(LOCAL_PATH)/../modules/rtp_rtcp/interface \ - $(LOCAL_PATH)/../modules/udp_transport/interface \ - $(LOCAL_PATH)/../modules/utility/interface \ - $(LOCAL_PATH)/../modules/video_capture/main/interface \ - $(LOCAL_PATH)/../modules/video_capture/main/source \ - $(LOCAL_PATH)/../modules/video_capture/main/source/Android \ - $(LOCAL_PATH)/../modules/video_coding/codecs/interface \ - $(LOCAL_PATH)/../modules/video_coding/main/interface \ - $(LOCAL_PATH)/../modules/video_mixer/main/interface \ - $(LOCAL_PATH)/../modules/video_processing/main/interface \ - $(LOCAL_PATH)/../modules/video_render/main/interface \ - $(LOCAL_PATH)/../system_wrappers/interface \ - $(LOCAL_PATH)/../voice_engine/include - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) -LOCAL_LDLIBS += -ldl -lpthread -endif - -ifneq ($(TARGET_SIMULATOR),true) -LOCAL_SHARED_LIBRARIES += libdl -endif - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/BUILD.gn 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,117 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../build/webrtc.gni") + +source_set("video_engine") { + deps = [ ":video_engine_core" ] +} + +source_set("video_engine_core") { + sources = [ + "include/vie_base.h", + "include/vie_capture.h", + "include/vie_codec.h", + "include/vie_errors.h", + "include/vie_external_codec.h", + "include/vie_image_process.h", + "include/vie_network.h", + "include/vie_render.h", + "include/vie_rtp_rtcp.h", + "call_stats.cc", + "call_stats.h", + "encoder_state_feedback.cc", + "encoder_state_feedback.h", + "overuse_frame_detector.cc", + "overuse_frame_detector.h", + "stream_synchronization.cc", + "stream_synchronization.h", + "vie_base_impl.cc", + "vie_base_impl.h", + "vie_capture_impl.cc", + "vie_capture_impl.h", + "vie_capturer.cc", + "vie_capturer.h", + "vie_channel.cc", + "vie_channel_group.cc", + "vie_channel_group.h", + "vie_channel.h", + "vie_channel_manager.cc", + "vie_channel_manager.h", + "vie_codec_impl.cc", + "vie_codec_impl.h", + "vie_defines.h", + "vie_encoder.cc", + "vie_encoder.h", + "vie_external_codec_impl.cc", + "vie_external_codec_impl.h", + "vie_file_image.cc", + "vie_file_image.h", + "vie_frame_provider_base.cc", + "vie_frame_provider_base.h", + "vie_image_process_impl.cc", + "vie_image_process_impl.h", + "vie_impl.cc", + "vie_impl.h", + "vie_input_manager.cc", + "vie_input_manager.h", + "vie_manager_base.cc", + "vie_manager_base.h", + "vie_network_impl.cc", + "vie_network_impl.h", + "vie_receiver.cc", + "vie_receiver.h", + "vie_ref_count.cc", + "vie_ref_count.h", + "vie_remb.cc", + "vie_remb.h", + "vie_renderer.cc", + "vie_renderer.h", + "vie_render_impl.cc", + "vie_render_impl.h", + "vie_render_manager.cc", + "vie_render_manager.h", + "vie_rtp_rtcp_impl.cc", + "vie_rtp_rtcp_impl.h", + "vie_sender.cc", + "vie_sender.h", + "vie_shared_data.cc", + "vie_shared_data.h", + "vie_sync_module.cc", + "vie_sync_module.h", + ] + + configs += [ "..:common_config" ] + public_configs = [ "..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + if (is_win) { + cflags = [ + # TODO(jschuh): Bug 1348: fix size_t to int truncations. + "/wd4267", # size_t to int truncation. + ] + } + + deps = [ + "../common_video", + "../modules/bitrate_controller", + "../modules/rtp_rtcp", + "../modules/utility", + "../modules/video_capture", + "../modules/video_coding", + "../modules/video_processing", + "../modules/video_render", + "../voice_engine", + "../system_wrappers", + ] +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/call_stats.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/call_stats.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/call_stats.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/call_stats.cc 2015-02-03 14:33:38.000000000 +0000 @@ -15,7 +15,6 @@ #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/tick_util.h" -#include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/call_stats.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/call_stats.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/call_stats.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/call_stats.h 2015-02-03 14:33:38.000000000 +0000 @@ -13,8 +13,8 @@ #include +#include "webrtc/base/constructormagic.h" #include "webrtc/modules/interface/module.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" namespace webrtc { @@ -32,8 +32,8 @@ ~CallStats(); // Implements Module, to use the process thread. - virtual int32_t TimeUntilNextProcess(); - virtual int32_t Process(); + virtual int32_t TimeUntilNextProcess() OVERRIDE; + virtual int32_t Process() OVERRIDE; // Returns a RtcpRttStats to register at a statistics provider. The object // has the same lifetime as the CallStats instance. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -824,9 +824,5 @@ #endif } -void DesktopCaptureImpl::OnCursorShapeChanged(MouseCursorShape* cursor_shape) { - // do nothing, DesktopAndCursorComposer does it all -} - } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.h 2015-02-03 14:33:38.000000000 +0000 @@ -211,9 +211,6 @@ virtual SharedMemory* CreateSharedMemory(size_t size) OVERRIDE; virtual void OnCaptureCompleted(DesktopFrame* frame) OVERRIDE; - //ScreenCapturer::MouseShapeObserver - virtual void OnCursorShapeChanged(MouseCursorShape* cursor_shape) OVERRIDE; - protected: DesktopCaptureImpl(const int32_t id); virtual ~DesktopCaptureImpl(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/encoder_state_feedback.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/encoder_state_feedback.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/encoder_state_feedback.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/encoder_state_feedback.h 2015-02-03 14:33:38.000000000 +0000 @@ -16,7 +16,7 @@ #include -#include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/base/constructormagic.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/typedefs.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/encoder_state_feedback_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/encoder_state_feedback_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/encoder_state_feedback_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/encoder_state_feedback_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -17,26 +17,17 @@ #include "webrtc/common.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" -#include "webrtc/modules/utility/interface/process_thread.h" +#include "webrtc/modules/utility/interface/mock/mock_process_thread.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/video_engine/vie_encoder.h" -namespace webrtc { +using ::testing::NiceMock; -// TODO(mflodman) Create a common mock in module utility. -class TestProcessThread : public ProcessThread { - public: - TestProcessThread() {} - ~TestProcessThread() {} - virtual int32_t Start() { return 0; } - virtual int32_t Stop() { return 0; } - virtual int32_t RegisterModule(Module* module) { return 0; } - virtual int32_t DeRegisterModule(const Module* module) { return 0; } -}; +namespace webrtc { class MockVieEncoder : public ViEEncoder { public: - explicit MockVieEncoder(TestProcessThread* process_thread) + explicit MockVieEncoder(ProcessThread* process_thread) : ViEEncoder(1, 1, 1, config_, *process_thread, NULL) {} ~MockVieEncoder() {} @@ -55,10 +46,10 @@ class VieKeyRequestTest : public ::testing::Test { protected: virtual void SetUp() { - process_thread_.reset(new TestProcessThread()); + process_thread_.reset(new NiceMock); encoder_state_feedback_.reset(new EncoderStateFeedback()); } - scoped_ptr process_thread_; + scoped_ptr process_thread_; scoped_ptr encoder_state_feedback_; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_base.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_base.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_base.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_base.h 2015-02-03 14:33:38.000000000 +0000 @@ -43,6 +43,93 @@ virtual ~CpuOveruseObserver() {} }; +struct CpuOveruseOptions { + CpuOveruseOptions() + : enable_capture_jitter_method(true), + low_capture_jitter_threshold_ms(20.0f), + high_capture_jitter_threshold_ms(30.0f), + enable_encode_usage_method(false), + low_encode_usage_threshold_percent(60), + high_encode_usage_threshold_percent(90), + low_encode_time_rsd_threshold(-1), + high_encode_time_rsd_threshold(-1), + enable_extended_processing_usage(true), + frame_timeout_interval_ms(1500), + min_frame_samples(120), + min_process_count(3), + high_threshold_consecutive_count(2) {} + + // Method based on inter-arrival jitter of captured frames. + bool enable_capture_jitter_method; + float low_capture_jitter_threshold_ms; // Threshold for triggering underuse. + float high_capture_jitter_threshold_ms; // Threshold for triggering overuse. + // Method based on encode time of frames. + bool enable_encode_usage_method; + int low_encode_usage_threshold_percent; // Threshold for triggering underuse. + int high_encode_usage_threshold_percent; // Threshold for triggering overuse. + // TODO(asapersson): Remove options, not used. + int low_encode_time_rsd_threshold; // Additional threshold for triggering + // underuse (used in addition to + // threshold above if configured). + int high_encode_time_rsd_threshold; // Additional threshold for triggering + // overuse (used in addition to + // threshold above if configured). + bool enable_extended_processing_usage; // Include a larger time span (in + // addition to encode time) for + // measuring the processing time of a + // frame. + // General settings. + int frame_timeout_interval_ms; // The maximum allowed interval between two + // frames before resetting estimations. + int min_frame_samples; // The minimum number of frames required. + int min_process_count; // The number of initial process times required before + // triggering an overuse/underuse. + int high_threshold_consecutive_count; // The number of consecutive checks + // above the high threshold before + // triggering an overuse. + + bool Equals(const CpuOveruseOptions& o) const { + return enable_capture_jitter_method == o.enable_capture_jitter_method && + low_capture_jitter_threshold_ms == o.low_capture_jitter_threshold_ms && + high_capture_jitter_threshold_ms == + o.high_capture_jitter_threshold_ms && + enable_encode_usage_method == o.enable_encode_usage_method && + low_encode_usage_threshold_percent == + o.low_encode_usage_threshold_percent && + high_encode_usage_threshold_percent == + o.high_encode_usage_threshold_percent && + low_encode_time_rsd_threshold == o.low_encode_time_rsd_threshold && + high_encode_time_rsd_threshold == o.high_encode_time_rsd_threshold && + enable_extended_processing_usage == + o.enable_extended_processing_usage && + frame_timeout_interval_ms == o.frame_timeout_interval_ms && + min_frame_samples == o.min_frame_samples && + min_process_count == o.min_process_count && + high_threshold_consecutive_count == o.high_threshold_consecutive_count; + } +}; + +struct CpuOveruseMetrics { + CpuOveruseMetrics() + : capture_jitter_ms(-1), + avg_encode_time_ms(-1), + encode_usage_percent(-1), + encode_rsd(-1), + capture_queue_delay_ms_per_s(-1) {} + + int capture_jitter_ms; // The current estimated jitter in ms based on + // incoming captured frames. + int avg_encode_time_ms; // The average encode time in ms. + int encode_usage_percent; // The average encode time divided by the average + // time difference between incoming captured frames. + // TODO(asapersson): Remove metric, not used. + int encode_rsd; // The relative std dev of encode time of frames. + int capture_queue_delay_ms_per_s; // The current time delay between an + // incoming captured frame until the frame + // is being processed. The delay is + // expressed in ms delay per second. +}; + class WEBRTC_DLLEXPORT VideoEngine { public: // Creates a VideoEngine object, which can then be used to acquire sub‐APIs. @@ -119,24 +206,19 @@ virtual int RegisterCpuOveruseObserver(int channel, CpuOveruseObserver* observer) = 0; + // Sets options for cpu overuse detector. + virtual int SetCpuOveruseOptions(int channel, + const CpuOveruseOptions& options) = 0; + // Gets cpu overuse measures. - // capture_jitter_ms: The current estimated jitter in ms based on incoming - // captured frames. - // avg_encode_time_ms: The average encode time in ms. - // encode_usage_percent: The average encode time divided by the average time - // difference between incoming captured frames. - // capture_queue_delay_ms_per_s: The current time delay between an incoming - // captured frame until the frame is being - // processed. The delay is expressed in ms - // delay per second. - // TODO(asapersson): Remove default implementation. - virtual int CpuOveruseMeasures(int channel, - int* capture_jitter_ms, - int* avg_encode_time_ms, - int* encode_usage_percent, - int* capture_queue_delay_ms_per_s) { - return -1; - } + virtual int GetCpuOveruseMetrics(int channel, CpuOveruseMetrics* metrics) = 0; + + // Registers a callback which is called when send-side delay statistics has + // been updated. + // TODO(holmer): Remove the default implementation when fakevideoengine.h has + // been updated. + virtual void RegisterSendSideDelayObserver( + int channel, SendSideDelayObserver* observer) {} // Changing the current state of the host CPU. Encoding engines // can adapt their behavior if needed. (Optional) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_codec.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_codec.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_codec.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_codec.h 2015-02-03 14:33:38.000000000 +0000 @@ -153,7 +153,13 @@ // Gets the number of packets discarded by the jitter buffer because they // arrived too late. - virtual unsigned int GetDiscardedPackets(const int video_channel) const = 0; + // TODO(asapersson): Remove default implementation. + virtual int GetNumDiscardedPackets(int video_channel) const { return -1; } + + // TODO(asapersson): Remove once the api has been removed from + // fakewebrtcvideoengine.h. + virtual unsigned int GetDiscardedPackets( + const int video_channel) const { return 0; } // Enables key frame request callback in ViEDecoderObserver. virtual int SetKeyFrameRequestCallbackStatus(const int video_channel, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_errors.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_errors.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_errors.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_errors.h 2015-02-03 14:33:38.000000000 +0000 @@ -103,8 +103,8 @@ kViEImageProcessInvalidCaptureId, // No capture device exist with the provided capture id. kViEImageProcessFilterExists, // RegisterCaptureEffectFilter,RegisterSendEffectFilter,RegisterRenderEffectFilter - Effect filter already registered. kViEImageProcessFilterDoesNotExist, // DeRegisterCaptureEffectFilter,DeRegisterSendEffectFilter,DeRegisterRenderEffectFilter - Effect filter not registered. - kViEImageProcessAlreadyEnabled, // EnableDeflickering,EnableDenoising,EnableColorEnhancement- Function already enabled. - kViEImageProcessAlreadyDisabled, // EnableDeflickering,EnableDenoising,EnableColorEnhancement- Function already disabled. + kViEImageProcessAlreadyEnabled, // EnableDeflickering,EnableColorEnhancement- Function already enabled. + kViEImageProcessAlreadyDisabled, // EnableDeflickering,EnableColorEnhancement- Function already disabled. kViEImageProcessUnknownError // An unknown error has occurred. Check the log file. }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_image_process.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_image_process.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_image_process.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_image_process.h 2015-02-03 14:33:38.000000000 +0000 @@ -11,7 +11,6 @@ // This sub-API supports the following functionalities: // - Effect filters // - Deflickering -// - Denoising // - Color enhancement #ifndef WEBRTC_VIDEO_ENGINE_INCLUDE_VIE_IMAGE_PROCESS_H_ @@ -33,8 +32,11 @@ public: // This method is called with an I420 video frame allowing the user to // modify the video frame. - virtual int Transform(int size, unsigned char* frameBuffer, - unsigned int timeStamp90KHz, unsigned int width, + virtual int Transform(int size, + unsigned char* frame_buffer, + int64_t ntp_time_ms, + unsigned int timestamp, + unsigned int width, unsigned int height) = 0; protected: ViEEffectFilter() {} @@ -82,9 +84,10 @@ // not all of them succeed. Enabling this function will remove the flicker. virtual int EnableDeflickering(const int capture_id, const bool enable) = 0; - // Some cameras produce very noisy captured images, especially in low‐light - // conditions. This functionality will reduce the camera noise. - virtual int EnableDenoising(const int capture_id, const bool enable) = 0; + // TODO(pbos): Remove this function when removed from fakewebrtcvideoengine.h. + virtual int EnableDenoising(const int capture_id, const bool enable) { + return -1; + } // This function enhances the colors on the decoded video stream, enabled by // default. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_network.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_network.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_network.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_network.h 2015-02-03 14:33:38.000000000 +0000 @@ -79,6 +79,20 @@ // over the network. virtual int SetMTU(int video_channel, unsigned int mtu) = 0; + // Forward (audio) packet to bandwidth estimator for the given video channel, + // for aggregated audio+video BWE. + virtual int ReceivedBWEPacket(const int video_channel, + int64_t arrival_time_ms, int payload_size, const RTPHeader& header) { + return 0; + } + + // TODO(holmer): Remove the default implementation when this has been fixed + // in fakewebrtcvideoengine.cc. + virtual bool SetBandwidthEstimationConfig(int video_channel, + const webrtc::Config& config) { + return false; + } + protected: ViENetwork() {} virtual ~ViENetwork() {} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_render.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_render.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_render.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_render.h 2015-02-03 14:33:38.000000000 +0000 @@ -38,10 +38,13 @@ virtual int DeliverFrame(unsigned char* buffer, int buffer_size, // RTP timestamp in 90kHz. - uint32_t time_stamp, - // Wallclock render time in miliseconds - int64_t render_time, - // Handle of the underlying video frame, + uint32_t timestamp, + // NTP time of the capture time in local timebase + // in milliseconds. + int64_t ntp_time_ms, + // Wallclock render time in milliseconds. + int64_t render_time_ms, + // Handle of the underlying video frame. void* handle) = 0; // Returns true if the renderer supports textures. DeliverFrame can be called diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_rtp_rtcp.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_rtp_rtcp.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_rtp_rtcp.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/include/vie_rtp_rtcp.h 2015-02-03 14:33:38.000000000 +0000 @@ -23,6 +23,7 @@ #define WEBRTC_VIDEO_ENGINE_INCLUDE_VIE_RTP_RTCP_H_ #include "webrtc/common_types.h" +#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" namespace webrtc { @@ -49,11 +50,6 @@ kViEStreamTypeRtx = 1 // Retransmission media stream }; -enum BandwidthEstimationMode { - kViEMultiStreamEstimation, - kViESingleStreamEstimation -}; - // This class declares an abstract interface for a user defined observer. It is // up to the VideoEngine user to implement a derived class which implements the // observer class. The observer is registered using RegisterRTPObserver() and @@ -141,6 +137,15 @@ virtual int SetRtxSendPayloadType(const int video_channel, const uint8_t payload_type) = 0; + // This enables sending redundant payloads when padding up the bitrate instead + // of sending dummy padding packets. This feature is off by default and will + // only have an effect if RTX is also enabled. + // TODO(holmer): Remove default implementation once this has been implemented + // in libjingle. + virtual int SetPadWithRedundantPayloads(int video_channel, bool enable) { + return 0; + } + virtual int SetRtxReceivePayloadType(const int video_channel, const uint8_t payload_type) = 0; @@ -149,6 +154,17 @@ virtual int SetStartSequenceNumber(const int video_channel, unsigned short sequence_number) = 0; + // TODO(pbos): Remove default implementation once this has been implemented + // in libjingle. + virtual void SetRtpStateForSsrc(int video_channel, + uint32_t ssrc, + const RtpState& rtp_state) {} + // TODO(pbos): Remove default implementation once this has been implemented + // in libjingle. + virtual RtpState GetRtpStateForSsrc(int video_channel, uint32_t ssrc) { + return RtpState(); + } + // This function sets the RTCP status for the specified channel. // Default mode is kRtcpCompound_RFC4585. virtual int SetRTCPStatus(const int video_channel, @@ -163,10 +179,12 @@ virtual int SetRTCPCName(const int video_channel, const char rtcp_cname[KMaxRTCPCNameLength]) = 0; - // This function gets the RTCP canonical name (CNAME) for the RTCP reports - // sent the specified channel. + // TODO(holmer): Remove this API once it has been removed from + // fakewebrtcvideoengine.h. virtual int GetRTCPCName(const int video_channel, - char rtcp_cname[KMaxRTCPCNameLength]) const = 0; + char rtcp_cname[KMaxRTCPCNameLength]) const { + return -1; + } // This function gets the RTCP canonical name (CNAME) for the RTCP reports // received on the specified channel. @@ -269,8 +287,7 @@ // Enables/disables RTCP Receiver Reference Time Report Block extension/ // DLRR Report Block extension (RFC 3611). - // TODO(asapersson): Remove default implementation. - virtual int SetRtcpXrRrtrStatus(int video_channel, bool enable) { return -1; } + virtual int SetRtcpXrRrtrStatus(int video_channel, bool enable) = 0; // Enables transmission smoothening, i.e. packets belonging to the same frame // will be sent over a longer period of time instead of sending them @@ -278,14 +295,33 @@ virtual int SetTransmissionSmoothingStatus(int video_channel, bool enable) = 0; + // Sets a minimal bitrate which will be padded to when the encoder doesn't + // produce enough bitrate. + // TODO(pbos): Remove default implementation when libjingle's + // FakeWebRtcVideoEngine is updated. + virtual int SetMinTransmitBitrate(int video_channel, + int min_transmit_bitrate_kbps) { + return -1; + }; + + // Set a constant amount to deduct from received bitrate estimates before + // using it to allocate capacity among outgoing video streams. + virtual int SetReservedTransmitBitrate( + int video_channel, unsigned int reserved_transmit_bitrate_bps) { + return 0; + } + // This function returns our locally created statistics of the received RTP // stream. virtual int GetReceiveChannelRtcpStatistics(const int video_channel, RtcpStatistics& basic_stats, int& rtt_ms) const = 0; - // This function returns statistics reported by the remote client in a RTCP - // packet. + // This function returns statistics reported by the remote client in RTCP + // report blocks. If several streams are reported, the statistics will be + // aggregated. + // If statistics are aggregated, extended_max_sequence_number is not reported, + // and will always be set to 0. virtual int GetSendChannelRtcpStatistics(const int video_channel, RtcpStatistics& basic_stats, int& rtt_ms) const = 0; @@ -372,6 +408,14 @@ virtual int DeregisterReceiveChannelRtpStatisticsCallback( int video_channel, StreamDataCountersCallback* callback) = 0; + + // Gets sent and received RTCP packet types. + // TODO(asapersson): Remove default implementation. + virtual int GetRtcpPacketTypeCounters( + int video_channel, + RtcpPacketTypeCounter* packets_sent, + RtcpPacketTypeCounter* packets_received) const { return -1; } + // Gets the sender info part of the last received RTCP Sender Report (SR) virtual int GetRemoteRTCPSenderInfo(const int video_channel, SenderInfo* sender_info) const = 0; @@ -413,6 +457,13 @@ const int video_channel, ReceiveBandwidthEstimatorStats* output) const { return -1; } + // This function gets the PacedSender queuing delay for the last sent frame. + // TODO(jiayl): remove the default impl when libjingle is updated. + virtual int GetPacerQueuingDelayMs( + const int video_channel, int* delay_ms) const { + return -1; + } + // This function enables capturing of RTP packets to a binary file on a // specific channel and for a given direction. The file can later be // replayed using e.g. RTP Tools rtpplay since the binary file format is diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/mock/mock_vie_frame_provider_base.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/mock/mock_vie_frame_provider_base.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/mock/mock_vie_frame_provider_base.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/mock/mock_vie_frame_provider_base.h 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef WEBRTC_VIDEO_ENGINE_MOCK_MOCK_VIE_FRAME_PROVIDER_BASE_H_ +#define WEBRTC_VIDEO_ENGINE_MOCK_MOCK_VIE_FRAME_PROVIDER_BASE_H_ + +#include "webrtc/video_engine/vie_frame_provider_base.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace webrtc { + +class MockViEFrameCallback : public ViEFrameCallback { + public: + MOCK_METHOD4(DeliverFrame, + void(int id, + I420VideoFrame* video_frame, + int num_csrcs, + const uint32_t CSRC[kRtpCsrcSize])); + MOCK_METHOD2(DelayChanged, void(int id, int frame_delay)); + MOCK_METHOD3(GetPreferedFrameSettings, + int(int* width, int* height, int* frame_rate)); + MOCK_METHOD1(ProviderDestroyed, void(int id)); +}; + +} // namespace webrtc + +#endif // WEBRTC_VIDEO_ENGINE_MOCK_MOCK_VIE_FRAME_PROVIDER_BASE_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/overuse_frame_detector.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/overuse_frame_detector.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/overuse_frame_detector.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/overuse_frame_detector.cc 2015-02-03 14:33:38.000000000 +0000 @@ -15,12 +15,12 @@ #include #include +#include -#include "webrtc/modules/video_coding/utility/include/exp_filter.h" +#include "webrtc/base/exp_filter.h" #include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" -#include "webrtc/video_engine/include/vie_base.h" +#include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { @@ -29,33 +29,21 @@ namespace { const int64_t kProcessIntervalMs = 5000; -// Number of initial process times before reporting. -const int64_t kMinProcessCountBeforeReporting = 3; - -const int64_t kFrameTimeoutIntervalMs = 1500; - -// Consecutive checks above threshold to trigger overuse. -const int kConsecutiveChecksAboveThreshold = 2; - -// Minimum samples required to perform a check. -const size_t kMinFrameSampleCount = 120; - // Weight factor to apply to the standard deviation. const float kWeightFactor = 0.997f; - // Weight factor to apply to the average. const float kWeightFactorMean = 0.98f; // Delay between consecutive rampups. (Used for quick recovery.) const int kQuickRampUpDelayMs = 10 * 1000; // Delay between rampup attempts. Initially uses standard, scales up to max. -const int kStandardRampUpDelayMs = 30 * 1000; -const int kMaxRampUpDelayMs = 120 * 1000; +const int kStandardRampUpDelayMs = 40 * 1000; +const int kMaxRampUpDelayMs = 240 * 1000; // Expontential back-off factor, to prevent annoying up-down behaviour. const double kRampUpBackoffFactor = 2.0; -// The initial average encode time (set to a fairly small value). -const float kInitialAvgEncodeTimeMs = 5.0f; +// Max number of overuses detected before always applying the rampup delay. +const int kMaxOverusesBeforeApplyRampupDelay = 4; // The maximum exponent to use in VCMExpFilter. const float kSampleDiffMs = 33.0f; @@ -63,36 +51,42 @@ } // namespace +// TODO(asapersson): Remove this class. Not used. Statistics::Statistics() : sum_(0.0), count_(0), - filtered_samples_(new VCMExpFilter(kWeightFactorMean)), - filtered_variance_(new VCMExpFilter(kWeightFactor)) { + filtered_samples_(new rtc::ExpFilter(kWeightFactorMean)), + filtered_variance_(new rtc::ExpFilter(kWeightFactor)) { + Reset(); +} + +void Statistics::SetOptions(const CpuOveruseOptions& options) { + options_ = options; } void Statistics::Reset() { sum_ = 0.0; count_ = 0; + filtered_variance_->Reset(kWeightFactor); + filtered_variance_->Apply(1.0f, InitialVariance()); } void Statistics::AddSample(float sample_ms) { sum_ += sample_ms; ++count_; - if (count_ < kMinFrameSampleCount) { + if (count_ < static_cast(options_.min_frame_samples)) { // Initialize filtered samples. filtered_samples_->Reset(kWeightFactorMean); filtered_samples_->Apply(1.0f, InitialMean()); - filtered_variance_->Reset(kWeightFactor); - filtered_variance_->Apply(1.0f, InitialVariance()); return; } float exp = sample_ms / kSampleDiffMs; exp = std::min(exp, kMaxExp); filtered_samples_->Apply(exp, sample_ms); - filtered_variance_->Apply(exp, (sample_ms - filtered_samples_->Value()) * - (sample_ms - filtered_samples_->Value())); + filtered_variance_->Apply(exp, (sample_ms - filtered_samples_->filtered()) * + (sample_ms - filtered_samples_->filtered())); } float Statistics::InitialMean() const { @@ -103,14 +97,15 @@ float Statistics::InitialVariance() const { // Start in between the underuse and overuse threshold. - float average_stddev = (kNormalUseStdDevMs + kOveruseStdDevMs)/2.0f; + float average_stddev = (options_.low_capture_jitter_threshold_ms + + options_.high_capture_jitter_threshold_ms) / 2.0f; return average_stddev * average_stddev; } -float Statistics::Mean() const { return filtered_samples_->Value(); } +float Statistics::Mean() const { return filtered_samples_->filtered(); } float Statistics::StdDev() const { - return sqrt(std::max(filtered_variance_->Value(), 0.0f)); + return sqrt(std::max(filtered_variance_->filtered(), 0.0f)); } uint64_t Statistics::Count() const { return count_; } @@ -121,72 +116,157 @@ public: EncodeTimeAvg() : kWeightFactor(0.5f), - filtered_encode_time_ms_(new VCMExpFilter(kWeightFactor)) { + kInitialAvgEncodeTimeMs(5.0f), + filtered_encode_time_ms_(new rtc::ExpFilter(kWeightFactor)) { filtered_encode_time_ms_->Apply(1.0f, kInitialAvgEncodeTimeMs); } ~EncodeTimeAvg() {} - void AddEncodeSample(float encode_time_ms, int64_t diff_last_sample_ms) { + void AddSample(float encode_time_ms, int64_t diff_last_sample_ms) { float exp = diff_last_sample_ms / kSampleDiffMs; exp = std::min(exp, kMaxExp); filtered_encode_time_ms_->Apply(exp, encode_time_ms); } - int filtered_encode_time_ms() const { - return static_cast(filtered_encode_time_ms_->Value() + 0.5); + int Value() const { + return static_cast(filtered_encode_time_ms_->filtered() + 0.5); } private: const float kWeightFactor; - scoped_ptr filtered_encode_time_ms_; + const float kInitialAvgEncodeTimeMs; + scoped_ptr filtered_encode_time_ms_; }; -// Class for calculating the encode usage. -class OveruseFrameDetector::EncodeUsage { +// Class for calculating the processing usage on the send-side (the average +// processing time of a frame divided by the average time difference between +// captured frames). +class OveruseFrameDetector::SendProcessingUsage { public: - EncodeUsage() + SendProcessingUsage() : kWeightFactorFrameDiff(0.998f), - kWeightFactorEncodeTime(0.995f), - filtered_encode_time_ms_(new VCMExpFilter(kWeightFactorEncodeTime)), - filtered_frame_diff_ms_(new VCMExpFilter(kWeightFactorFrameDiff)) { - filtered_encode_time_ms_->Apply(1.0f, kInitialAvgEncodeTimeMs); - filtered_frame_diff_ms_->Apply(1.0f, kSampleDiffMs); + kWeightFactorProcessing(0.995f), + kInitialSampleDiffMs(40.0f), + kMaxSampleDiffMs(45.0f), + count_(0), + filtered_processing_ms_(new rtc::ExpFilter(kWeightFactorProcessing)), + filtered_frame_diff_ms_(new rtc::ExpFilter(kWeightFactorFrameDiff)) { + Reset(); } - ~EncodeUsage() {} + ~SendProcessingUsage() {} - void AddSample(float sample_ms) { + void SetOptions(const CpuOveruseOptions& options) { + options_ = options; + } + + void Reset() { + count_ = 0; + filtered_frame_diff_ms_->Reset(kWeightFactorFrameDiff); + filtered_frame_diff_ms_->Apply(1.0f, kInitialSampleDiffMs); + filtered_processing_ms_->Reset(kWeightFactorProcessing); + filtered_processing_ms_->Apply(1.0f, InitialProcessingMs()); + } + + void AddCaptureSample(float sample_ms) { float exp = sample_ms / kSampleDiffMs; exp = std::min(exp, kMaxExp); filtered_frame_diff_ms_->Apply(exp, sample_ms); } - void AddEncodeSample(float encode_time_ms, int64_t diff_last_sample_ms) { + void AddSample(float processing_ms, int64_t diff_last_sample_ms) { + ++count_; float exp = diff_last_sample_ms / kSampleDiffMs; exp = std::min(exp, kMaxExp); - filtered_encode_time_ms_->Apply(exp, encode_time_ms); + filtered_processing_ms_->Apply(exp, processing_ms); } - int UsageInPercent() const { - float frame_diff_ms = std::max(filtered_frame_diff_ms_->Value(), 1.0f); + int Value() const { + if (count_ < static_cast(options_.min_frame_samples)) { + return static_cast(InitialUsageInPercent() + 0.5f); + } + float frame_diff_ms = std::max(filtered_frame_diff_ms_->filtered(), 1.0f); + frame_diff_ms = std::min(frame_diff_ms, kMaxSampleDiffMs); float encode_usage_percent = - 100.0f * filtered_encode_time_ms_->Value() / frame_diff_ms; + 100.0f * filtered_processing_ms_->filtered() / frame_diff_ms; return static_cast(encode_usage_percent + 0.5); } private: + float InitialUsageInPercent() const { + // Start in between the underuse and overuse threshold. + return (options_.low_encode_usage_threshold_percent + + options_.high_encode_usage_threshold_percent) / 2.0f; + } + + float InitialProcessingMs() const { + return InitialUsageInPercent() * kInitialSampleDiffMs / 100; + } + const float kWeightFactorFrameDiff; - const float kWeightFactorEncodeTime; - scoped_ptr filtered_encode_time_ms_; - scoped_ptr filtered_frame_diff_ms_; + const float kWeightFactorProcessing; + const float kInitialSampleDiffMs; + const float kMaxSampleDiffMs; + uint64_t count_; + CpuOveruseOptions options_; + scoped_ptr filtered_processing_ms_; + scoped_ptr filtered_frame_diff_ms_; +}; + +// Class for calculating the processing time of frames. +class OveruseFrameDetector::FrameQueue { + public: + FrameQueue() : last_processing_time_ms_(-1) {} + ~FrameQueue() {} + + // Called when a frame is captured. + // Starts the measuring of the processing time of the frame. + void Start(int64_t capture_time, int64_t now) { + const size_t kMaxSize = 90; // Allows for processing time of 1.5s at 60fps. + if (frame_times_.size() > kMaxSize) { + LOG(LS_WARNING) << "Max size reached, removed oldest frame."; + frame_times_.erase(frame_times_.begin()); + } + if (frame_times_.find(capture_time) != frame_times_.end()) { + // Frame should not exist. + assert(false); + return; + } + frame_times_[capture_time] = now; + } + + // Called when the processing of a frame has finished. + // Returns the processing time of the frame. + int End(int64_t capture_time, int64_t now) { + std::map::iterator it = frame_times_.find(capture_time); + if (it == frame_times_.end()) { + return -1; + } + // Remove any old frames up to current. + // Old frames have been skipped by the capture process thread. + // TODO(asapersson): Consider measuring time from first frame in list. + last_processing_time_ms_ = now - (*it).second; + frame_times_.erase(frame_times_.begin(), ++it); + return last_processing_time_ms_; + } + + void Reset() { frame_times_.clear(); } + int NumFrames() const { return frame_times_.size(); } + int last_processing_time_ms() const { return last_processing_time_ms_; } + + private: + // Captured frames mapped by the capture time. + std::map frame_times_; + int last_processing_time_ms_; }; +// TODO(asapersson): Remove this class. Not used. // Class for calculating the capture queue delay change. class OveruseFrameDetector::CaptureQueueDelay { public: CaptureQueueDelay() : kWeightFactor(0.5f), delay_ms_(0), - filtered_delay_ms_per_s_(new VCMExpFilter(kWeightFactor)) { + filtered_delay_ms_per_s_(new rtc::ExpFilter(kWeightFactor)) { filtered_delay_ms_per_s_->Apply(1.0f, 0.0f); } ~CaptureQueueDelay() {} @@ -226,24 +306,19 @@ return delay_ms_; } - int filtered_delay_ms_per_s() const { - return static_cast(filtered_delay_ms_per_s_->Value() + 0.5); + int Value() const { + return static_cast(filtered_delay_ms_per_s_->filtered() + 0.5); } private: const float kWeightFactor; std::list frames_; int delay_ms_; - scoped_ptr filtered_delay_ms_per_s_; + scoped_ptr filtered_delay_ms_per_s_; }; -OveruseFrameDetector::OveruseFrameDetector(Clock* clock, - float normaluse_stddev_ms, - float overuse_stddev_ms) +OveruseFrameDetector::OveruseFrameDetector(Clock* clock) : crit_(CriticalSectionWrapper::CreateCriticalSection()), - normaluse_stddev_ms_(normaluse_stddev_ms), - overuse_stddev_ms_(overuse_stddev_ms), - min_process_count_before_reporting_(kMinProcessCountBeforeReporting), observer_(NULL), clock_(clock), next_process_time_(clock_->TimeInMilliseconds()), @@ -251,14 +326,16 @@ last_capture_time_(0), last_overuse_time_(0), checks_above_threshold_(0), + num_overuse_detections_(0), last_rampup_time_(0), in_quick_rampup_(false), current_rampup_delay_ms_(kStandardRampUpDelayMs), num_pixels_(0), - last_capture_jitter_ms_(-1), last_encode_sample_ms_(0), encode_time_(new EncodeTimeAvg()), - encode_usage_(new EncodeUsage()), + usage_(new SendProcessingUsage()), + frame_queue_(new FrameQueue()), + last_sample_time_ms_(0), capture_queue_delay_(new CaptureQueueDelay()) { } @@ -270,24 +347,41 @@ observer_ = observer; } -int OveruseFrameDetector::AvgEncodeTimeMs() const { +void OveruseFrameDetector::SetOptions(const CpuOveruseOptions& options) { + assert(options.min_frame_samples > 0); CriticalSectionScoped cs(crit_.get()); - return encode_time_->filtered_encode_time_ms(); + if (options_.Equals(options)) { + return; + } + options_ = options; + capture_deltas_.SetOptions(options); + usage_->SetOptions(options); + ResetAll(num_pixels_); } -int OveruseFrameDetector::EncodeUsagePercent() const { +int OveruseFrameDetector::CaptureQueueDelayMsPerS() const { CriticalSectionScoped cs(crit_.get()); - return encode_usage_->UsageInPercent(); + return capture_queue_delay_->delay_ms(); } -int OveruseFrameDetector::AvgCaptureQueueDelayMsPerS() const { +int OveruseFrameDetector::LastProcessingTimeMs() const { CriticalSectionScoped cs(crit_.get()); - return capture_queue_delay_->filtered_delay_ms_per_s(); + return frame_queue_->last_processing_time_ms(); } -int OveruseFrameDetector::CaptureQueueDelayMsPerS() const { +int OveruseFrameDetector::FramesInQueue() const { CriticalSectionScoped cs(crit_.get()); - return capture_queue_delay_->delay_ms(); + return frame_queue_->NumFrames(); +} + +void OveruseFrameDetector::GetCpuOveruseMetrics( + CpuOveruseMetrics* metrics) const { + CriticalSectionScoped cs(crit_.get()); + metrics->capture_jitter_ms = static_cast(capture_deltas_.StdDev() + 0.5); + metrics->avg_encode_time_ms = encode_time_->Value(); + metrics->encode_rsd = 0; + metrics->encode_usage_percent = usage_->Value(); + metrics->capture_queue_delay_ms_per_s = capture_queue_delay_->Value(); } int32_t OveruseFrameDetector::TimeUntilNextProcess() { @@ -295,34 +389,51 @@ return next_process_time_ - clock_->TimeInMilliseconds(); } -bool OveruseFrameDetector::DetectFrameTimeout(int64_t now) const { +bool OveruseFrameDetector::FrameSizeChanged(int num_pixels) const { + if (num_pixels != num_pixels_) { + return true; + } + return false; +} + +bool OveruseFrameDetector::FrameTimeoutDetected(int64_t now) const { if (last_capture_time_ == 0) { return false; } - return (now - last_capture_time_) > kFrameTimeoutIntervalMs; + return (now - last_capture_time_) > options_.frame_timeout_interval_ms; } -void OveruseFrameDetector::FrameCaptured(int width, int height) { +void OveruseFrameDetector::ResetAll(int num_pixels) { + num_pixels_ = num_pixels; + capture_deltas_.Reset(); + usage_->Reset(); + frame_queue_->Reset(); + capture_queue_delay_->ClearFrames(); + last_capture_time_ = 0; + num_process_times_ = 0; +} + +void OveruseFrameDetector::FrameCaptured(int width, + int height, + int64_t capture_time_ms) { CriticalSectionScoped cs(crit_.get()); int64_t now = clock_->TimeInMilliseconds(); - int num_pixels = width * height; - if (num_pixels != num_pixels_ || DetectFrameTimeout(now)) { - // Frame size changed, reset statistics. - num_pixels_ = num_pixels; - capture_deltas_.Reset(); - last_capture_time_ = 0; - capture_queue_delay_->ClearFrames(); - num_process_times_ = 0; + if (FrameSizeChanged(width * height) || FrameTimeoutDetected(now)) { + ResetAll(width * height); } if (last_capture_time_ != 0) { capture_deltas_.AddSample(now - last_capture_time_); - encode_usage_->AddSample(now - last_capture_time_); + usage_->AddCaptureSample(now - last_capture_time_); } last_capture_time_ = now; capture_queue_delay_->FrameCaptured(now); + + if (options_.enable_extended_processing_usage) { + frame_queue_->Start(capture_time_ms, now); + } } void OveruseFrameDetector::FrameProcessingStarted() { @@ -332,18 +443,37 @@ void OveruseFrameDetector::FrameEncoded(int encode_time_ms) { CriticalSectionScoped cs(crit_.get()); - int64_t time = clock_->TimeInMilliseconds(); + int64_t now = clock_->TimeInMilliseconds(); if (last_encode_sample_ms_ != 0) { - int64_t diff_ms = time - last_encode_sample_ms_; - encode_time_->AddEncodeSample(encode_time_ms, diff_ms); - encode_usage_->AddEncodeSample(encode_time_ms, diff_ms); + int64_t diff_ms = now - last_encode_sample_ms_; + encode_time_->AddSample(encode_time_ms, diff_ms); + } + last_encode_sample_ms_ = now; + + if (!options_.enable_extended_processing_usage) { + AddProcessingTime(encode_time_ms); } - last_encode_sample_ms_ = time; } -int OveruseFrameDetector::last_capture_jitter_ms() const { +void OveruseFrameDetector::FrameSent(int64_t capture_time_ms) { CriticalSectionScoped cs(crit_.get()); - return last_capture_jitter_ms_; + if (!options_.enable_extended_processing_usage) { + return; + } + int delay_ms = frame_queue_->End(capture_time_ms, + clock_->TimeInMilliseconds()); + if (delay_ms > 0) { + AddProcessingTime(delay_ms); + } +} + +void OveruseFrameDetector::AddProcessingTime(int elapsed_ms) { + int64_t now = clock_->TimeInMilliseconds(); + if (last_sample_time_ms_ != 0) { + int64_t diff_ms = now - last_sample_time_ms_; + usage_->AddSample(elapsed_ms, diff_ms); + } + last_sample_time_ms_ = now; } int32_t OveruseFrameDetector::Process() { @@ -359,13 +489,9 @@ next_process_time_ = now + kProcessIntervalMs; ++num_process_times_; - // Don't trigger overuse unless we've seen a certain number of frames. - if (capture_deltas_.Count() < kMinFrameSampleCount) - return 0; - capture_queue_delay_->CalculateDelayChange(diff_ms); - if (num_process_times_ <= min_process_count_before_reporting_) { + if (num_process_times_ <= options_.min_process_count) { return 0; } @@ -375,7 +501,8 @@ // back and forth between this load, the system doesn't seem to handle it. bool check_for_backoff = last_rampup_time_ > last_overuse_time_; if (check_for_backoff) { - if (now - last_rampup_time_ < kStandardRampUpDelayMs) { + if (now - last_rampup_time_ < kStandardRampUpDelayMs || + num_overuse_detections_ > kMaxOverusesBeforeApplyRampupDelay) { // Going up was not ok for very long, back off. current_rampup_delay_ms_ *= kRampUpBackoffFactor; if (current_rampup_delay_ms_ > kMaxRampUpDelayMs) @@ -389,6 +516,7 @@ last_overuse_time_ = now; in_quick_rampup_ = false; checks_above_threshold_ = 0; + ++num_overuse_detections_; if (observer_ != NULL) observer_->OveruseDetected(); @@ -400,31 +528,31 @@ observer_->NormalUsage(); } - WEBRTC_TRACE( - webrtc::kTraceInfo, - webrtc::kTraceVideo, - -1, - "Capture input stats: avg: %.2fms, std_dev: %.2fms (rampup delay: " - "%dms, overuse: >=%.2fms, " - "underuse: <%.2fms)", - capture_deltas_.Mean(), - capture_deltas_.StdDev(), - in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_, - overuse_stddev_ms_, - normaluse_stddev_ms_); - - last_capture_jitter_ms_ = static_cast(capture_deltas_.StdDev() + 0.5); + int rampup_delay = + in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; + LOG(LS_VERBOSE) << " Frame stats: capture avg: " << capture_deltas_.Mean() + << " capture stddev " << capture_deltas_.StdDev() + << " encode usage " << usage_->Value() + << " overuse detections " << num_overuse_detections_ + << " rampup delay " << rampup_delay; return 0; } bool OveruseFrameDetector::IsOverusing() { - if (capture_deltas_.StdDev() >= overuse_stddev_ms_) { + bool overusing = false; + if (options_.enable_capture_jitter_method) { + overusing = capture_deltas_.StdDev() >= + options_.high_capture_jitter_threshold_ms; + } else if (options_.enable_encode_usage_method) { + overusing = usage_->Value() >= options_.high_encode_usage_threshold_percent; + } + + if (overusing) { ++checks_above_threshold_; } else { checks_above_threshold_ = 0; } - - return checks_above_threshold_ >= kConsecutiveChecksAboveThreshold; + return checks_above_threshold_ >= options_.high_threshold_consecutive_count; } bool OveruseFrameDetector::IsUnderusing(int64_t time_now) { @@ -432,6 +560,13 @@ if (time_now < last_rampup_time_ + delay) return false; - return capture_deltas_.StdDev() < normaluse_stddev_ms_; + bool underusing = false; + if (options_.enable_capture_jitter_method) { + underusing = capture_deltas_.StdDev() < + options_.low_capture_jitter_threshold_ms; + } else if (options_.enable_encode_usage_method) { + underusing = usage_->Value() < options_.low_encode_usage_threshold_percent; + } + return underusing; } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/overuse_frame_detector.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/overuse_frame_detector.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/overuse_frame_detector.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/overuse_frame_detector.h 2015-02-03 14:33:38.000000000 +0000 @@ -11,29 +11,17 @@ #ifndef WEBRTC_VIDEO_ENGINE_OVERUSE_FRAME_DETECTOR_H_ #define WEBRTC_VIDEO_ENGINE_OVERUSE_FRAME_DETECTOR_H_ +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/exp_filter.h" #include "webrtc/modules/interface/module.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/test/testsupport/gtest_prod_util.h" +#include "webrtc/video_engine/include/vie_base.h" namespace webrtc { class Clock; class CpuOveruseObserver; class CriticalSectionWrapper; -class VCMExpFilter; - -// Limits on standard deviation for under/overuse. -#ifdef WEBRTC_LINUX -const float kOveruseStdDevMs = 15.0f; -const float kNormalUseStdDevMs = 7.0f; -#elif WEBRTC_MAC -const float kOveruseStdDevMs = 24.0f; -const float kNormalUseStdDevMs = 14.0f; -#else -const float kOveruseStdDevMs = 17.0f; -const float kNormalUseStdDevMs = 10.0f; -#endif // TODO(pbos): Move this somewhere appropriate. class Statistics { @@ -42,6 +30,7 @@ void AddSample(float sample_ms); void Reset(); + void SetOptions(const CpuOveruseOptions& options); float Mean() const; float StdDev() const; @@ -53,24 +42,26 @@ float sum_; uint64_t count_; - scoped_ptr filtered_samples_; - scoped_ptr filtered_variance_; + CpuOveruseOptions options_; + scoped_ptr filtered_samples_; + scoped_ptr filtered_variance_; }; // Use to detect system overuse based on jitter in incoming frames. class OveruseFrameDetector : public Module { public: - explicit OveruseFrameDetector(Clock* clock, - float normaluse_stddev_ms, - float overuse_stddev_ms); + explicit OveruseFrameDetector(Clock* clock); ~OveruseFrameDetector(); // Registers an observer receiving overuse and underuse callbacks. Set // 'observer' to NULL to disable callbacks. void SetObserver(CpuOveruseObserver* observer); + // Sets options for overuse detection. + void SetOptions(const CpuOveruseOptions& options); + // Called for each captured frame. - void FrameCaptured(int width, int height); + void FrameCaptured(int width, int height, int64_t capture_time_ms); // Called when the processing of a captured frame is started. void FrameProcessingStarted(); @@ -78,67 +69,62 @@ // Called for each encoded frame. void FrameEncoded(int encode_time_ms); + // Called for each sent frame. + void FrameSent(int64_t capture_time_ms); + // Accessors. - // The last estimated jitter based on the incoming captured frames. - int last_capture_jitter_ms() const; - // Running average of reported encode time (FrameEncoded()). - // Only used for stats. - int AvgEncodeTimeMs() const; - - // The average encode time divided by the average time difference between - // incoming captured frames. - // This variable is currently only used for statistics. - int EncodeUsagePercent() const; - - // The current time delay between an incoming captured frame (FrameCaptured()) - // until the frame is being processed (FrameProcessingStarted()). - // (Note: if a new frame is received before an old frame has been processed, - // the old frame is skipped). - // The delay is returned as the delay in ms per second. - // This variable is currently only used for statistics. - int AvgCaptureQueueDelayMsPerS() const; + // Returns CpuOveruseMetrics where + // capture_jitter_ms: The estimated jitter based on incoming captured frames. + // avg_encode_time_ms: Running average of reported encode time + // (FrameEncoded()). Only used for stats. + // TODO(asapersson): Rename metric. + // encode_usage_percent: The average processing time of a frame on the + // send-side divided by the average time difference + // between incoming captured frames. + // capture_queue_delay_ms_per_s: The current time delay between an incoming + // captured frame (FrameCaptured()) until the + // frame is being processed + // (FrameProcessingStarted()). (Note: if a new + // frame is received before an old frame has + // been processed, the old frame is skipped). + // The delay is expressed in ms delay per sec. + // Only used for stats. + void GetCpuOveruseMetrics(CpuOveruseMetrics* metrics) const; + + // Only public for testing. int CaptureQueueDelayMsPerS() const; + int LastProcessingTimeMs() const; + int FramesInQueue() const; // Implements Module. virtual int32_t TimeUntilNextProcess() OVERRIDE; virtual int32_t Process() OVERRIDE; private: - FRIEND_TEST_ALL_PREFIXES(OveruseFrameDetectorTest, TriggerOveruse); - FRIEND_TEST_ALL_PREFIXES(OveruseFrameDetectorTest, OveruseAndRecover); - FRIEND_TEST_ALL_PREFIXES(OveruseFrameDetectorTest, DoubleOveruseAndRecover); - FRIEND_TEST_ALL_PREFIXES( - OveruseFrameDetectorTest, TriggerNormalUsageWithMinProcessCount); - FRIEND_TEST_ALL_PREFIXES( - OveruseFrameDetectorTest, ConstantOveruseGivesNoNormalUsage); - FRIEND_TEST_ALL_PREFIXES(OveruseFrameDetectorTest, LastCaptureJitter); - - void set_min_process_count_before_reporting(int64_t count) { - min_process_count_before_reporting_ = count; - } - class EncodeTimeAvg; - class EncodeUsage; + class SendProcessingUsage; class CaptureQueueDelay; + class FrameQueue; + + void AddProcessingTime(int elapsed_ms); bool IsOverusing(); bool IsUnderusing(int64_t time_now); - bool DetectFrameTimeout(int64_t now) const; + bool FrameTimeoutDetected(int64_t now) const; + bool FrameSizeChanged(int num_pixels) const; + + void ResetAll(int num_pixels); // Protecting all members. scoped_ptr crit_; - // Limits on standard deviation for under/overuse. - const float normaluse_stddev_ms_; - const float overuse_stddev_ms_; - - int64_t min_process_count_before_reporting_; - // Observer getting overuse reports. CpuOveruseObserver* observer_; + CpuOveruseOptions options_; + Clock* clock_; int64_t next_process_time_; int64_t num_process_times_; @@ -148,6 +134,7 @@ int64_t last_overuse_time_; int checks_above_threshold_; + int num_overuse_detections_; int64_t last_rampup_time_; bool in_quick_rampup_; @@ -156,11 +143,11 @@ // Number of pixels of last captured frame. int num_pixels_; - int last_capture_jitter_ms_; - int64_t last_encode_sample_ms_; scoped_ptr encode_time_; - scoped_ptr encode_usage_; + scoped_ptr usage_; + scoped_ptr frame_queue_; + int64_t last_sample_time_ms_; scoped_ptr capture_queue_delay_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/overuse_frame_detector_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/overuse_frame_detector_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/overuse_frame_detector_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/overuse_frame_detector_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -17,6 +17,12 @@ #include "webrtc/video_engine/overuse_frame_detector.h" namespace webrtc { +namespace { + const int kWidth = 640; + const int kHeight = 480; + const int kFrameInterval33ms = 33; + const int kProcessIntervalMs = 5000; +} // namespace class MockCpuOveruseObserver : public CpuOveruseObserver { public: @@ -27,113 +33,277 @@ MOCK_METHOD0(NormalUsage, void()); }; +class CpuOveruseObserverImpl : public CpuOveruseObserver { + public: + CpuOveruseObserverImpl() : + overuse_(0), + normaluse_(0) {} + virtual ~CpuOveruseObserverImpl() {} + + void OveruseDetected() { ++overuse_; } + void NormalUsage() { ++normaluse_; } + + int overuse_; + int normaluse_; +}; + class OveruseFrameDetectorTest : public ::testing::Test { protected: virtual void SetUp() { clock_.reset(new SimulatedClock(1234)); observer_.reset(new MockCpuOveruseObserver()); - overuse_detector_.reset(new OveruseFrameDetector(clock_.get(), - 10.0f, - 15.0f)); + overuse_detector_.reset(new OveruseFrameDetector(clock_.get())); + + options_.low_capture_jitter_threshold_ms = 10.0f; + options_.high_capture_jitter_threshold_ms = 15.0f; + options_.min_process_count = 0; + overuse_detector_->SetOptions(options_); overuse_detector_->SetObserver(observer_.get()); } - void InsertFramesWithInterval(size_t num_frames, int interval_ms) { + int InitialJitter() { + return ((options_.low_capture_jitter_threshold_ms + + options_.high_capture_jitter_threshold_ms) / 2.0f) + 0.5; + } + + int InitialUsage() { + return ((options_.low_encode_usage_threshold_percent + + options_.high_encode_usage_threshold_percent) / 2.0f) + 0.5; + } + + void InsertFramesWithInterval( + size_t num_frames, int interval_ms, int width, int height) { while (num_frames-- > 0) { clock_->AdvanceTimeMilliseconds(interval_ms); - overuse_detector_->FrameCaptured(640, 480); + overuse_detector_->FrameCaptured(width, height, + clock_->TimeInMilliseconds()); } } - void TriggerOveruse() { - int regular_frame_interval_ms = 33; + void InsertAndSendFramesWithInterval( + int num_frames, int interval_ms, int width, int height, int delay_ms) { + while (num_frames-- > 0) { + int64_t capture_time_ms = clock_->TimeInMilliseconds(); + overuse_detector_->FrameCaptured(width, height, capture_time_ms); + clock_->AdvanceTimeMilliseconds(delay_ms); + overuse_detector_->FrameEncoded(delay_ms); + overuse_detector_->FrameSent(capture_time_ms); + clock_->AdvanceTimeMilliseconds(interval_ms - delay_ms); + } + } - EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1); + void TriggerOveruse(int num_times) { + for (int i = 0; i < num_times; ++i) { + InsertFramesWithInterval(200, kFrameInterval33ms, kWidth, kHeight); + InsertFramesWithInterval(50, 110, kWidth, kHeight); + overuse_detector_->Process(); + } + } - InsertFramesWithInterval(200, regular_frame_interval_ms); - InsertFramesWithInterval(50, 110); + void TriggerUnderuse() { + InsertFramesWithInterval(900, kFrameInterval33ms, kWidth, kHeight); overuse_detector_->Process(); + } - InsertFramesWithInterval(200, regular_frame_interval_ms); - InsertFramesWithInterval(50, 110); + void TriggerOveruseWithProcessingUsage(int num_times) { + const int kDelayMs = 32; + for (int i = 0; i < num_times; ++i) { + InsertAndSendFramesWithInterval( + 1000, kFrameInterval33ms, kWidth, kHeight, kDelayMs); + overuse_detector_->Process(); + } + } + + void TriggerUnderuseWithProcessingUsage() { + const int kDelayMs1 = 5; + const int kDelayMs2 = 6; + InsertAndSendFramesWithInterval( + 1300, kFrameInterval33ms, kWidth, kHeight, kDelayMs1); + InsertAndSendFramesWithInterval( + 1, kFrameInterval33ms, kWidth, kHeight, kDelayMs2); overuse_detector_->Process(); } - void TriggerNormalUsage() { - int regular_frame_interval_ms = 33; + int CaptureJitterMs() { + CpuOveruseMetrics metrics; + overuse_detector_->GetCpuOveruseMetrics(&metrics); + return metrics.capture_jitter_ms; + } - EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(testing::AtLeast(1)); + int AvgEncodeTimeMs() { + CpuOveruseMetrics metrics; + overuse_detector_->GetCpuOveruseMetrics(&metrics); + return metrics.avg_encode_time_ms; + } - InsertFramesWithInterval(900, regular_frame_interval_ms); - overuse_detector_->Process(); + int UsagePercent() { + CpuOveruseMetrics metrics; + overuse_detector_->GetCpuOveruseMetrics(&metrics); + return metrics.encode_usage_percent; } + CpuOveruseOptions options_; scoped_ptr clock_; scoped_ptr observer_; scoped_ptr overuse_detector_; }; +// enable_capture_jitter_method = true; +// CaptureJitterMs() > high_capture_jitter_threshold_ms => overuse. +// CaptureJitterMs() < low_capture_jitter_threshold_ms => underuse. TEST_F(OveruseFrameDetectorTest, TriggerOveruse) { - overuse_detector_->set_min_process_count_before_reporting(0); - TriggerOveruse(); + // capture_jitter > high => overuse + EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1); + TriggerOveruse(options_.high_threshold_consecutive_count); } TEST_F(OveruseFrameDetectorTest, OveruseAndRecover) { - overuse_detector_->set_min_process_count_before_reporting(0); - TriggerOveruse(); - TriggerNormalUsage(); + // capture_jitter > high => overuse + EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1); + TriggerOveruse(options_.high_threshold_consecutive_count); + // capture_jitter < low => underuse + EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(testing::AtLeast(1)); + TriggerUnderuse(); +} + +TEST_F(OveruseFrameDetectorTest, OveruseAndRecoverWithNoObserver) { + overuse_detector_->SetObserver(NULL); + EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(0); + TriggerOveruse(options_.high_threshold_consecutive_count); + EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(0); + TriggerUnderuse(); +} + +TEST_F(OveruseFrameDetectorTest, OveruseAndRecoverWithMethodDisabled) { + options_.enable_capture_jitter_method = false; + options_.enable_encode_usage_method = false; + overuse_detector_->SetOptions(options_); + EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(0); + TriggerOveruse(options_.high_threshold_consecutive_count); + EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(0); + TriggerUnderuse(); } TEST_F(OveruseFrameDetectorTest, DoubleOveruseAndRecover) { - overuse_detector_->set_min_process_count_before_reporting(0); - TriggerOveruse(); - TriggerOveruse(); - TriggerNormalUsage(); + EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(2); + TriggerOveruse(options_.high_threshold_consecutive_count); + TriggerOveruse(options_.high_threshold_consecutive_count); + EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(testing::AtLeast(1)); + TriggerUnderuse(); } -TEST_F(OveruseFrameDetectorTest, TriggerNormalUsageWithMinProcessCount) { - overuse_detector_->set_min_process_count_before_reporting(1); - InsertFramesWithInterval(900, 33); +TEST_F(OveruseFrameDetectorTest, TriggerUnderuseWithMinProcessCount) { + CpuOveruseObserverImpl overuse_observer_; + overuse_detector_->SetObserver(&overuse_observer_); + options_.min_process_count = 1; + overuse_detector_->SetOptions(options_); + InsertFramesWithInterval(1200, kFrameInterval33ms, kWidth, kHeight); overuse_detector_->Process(); - EXPECT_EQ(-1, overuse_detector_->last_capture_jitter_ms()); - clock_->AdvanceTimeMilliseconds(5000); + EXPECT_EQ(0, overuse_observer_.normaluse_); + clock_->AdvanceTimeMilliseconds(kProcessIntervalMs); overuse_detector_->Process(); - EXPECT_GT(overuse_detector_->last_capture_jitter_ms(), 0); + EXPECT_EQ(1, overuse_observer_.normaluse_); } TEST_F(OveruseFrameDetectorTest, ConstantOveruseGivesNoNormalUsage) { - overuse_detector_->set_min_process_count_before_reporting(0); EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(0); - - for(size_t i = 0; i < 64; ++i) - TriggerOveruse(); + EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(64); + for(size_t i = 0; i < 64; ++i) { + TriggerOveruse(options_.high_threshold_consecutive_count); + } } -TEST_F(OveruseFrameDetectorTest, LastCaptureJitter) { - overuse_detector_->set_min_process_count_before_reporting(0); - EXPECT_EQ(-1, overuse_detector_->last_capture_jitter_ms()); - TriggerOveruse(); - EXPECT_GT(overuse_detector_->last_capture_jitter_ms(), 0); +TEST_F(OveruseFrameDetectorTest, ConsecutiveCountTriggersOveruse) { + EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1); + options_.high_threshold_consecutive_count = 2; + overuse_detector_->SetOptions(options_); + TriggerOveruse(2); +} + +TEST_F(OveruseFrameDetectorTest, IncorrectConsecutiveCountTriggersNoOveruse) { + EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(0); + options_.high_threshold_consecutive_count = 2; + overuse_detector_->SetOptions(options_); + TriggerOveruse(1); +} + +TEST_F(OveruseFrameDetectorTest, GetCpuOveruseMetrics) { + CpuOveruseMetrics metrics; + overuse_detector_->GetCpuOveruseMetrics(&metrics); + EXPECT_GT(metrics.capture_jitter_ms, 0); + EXPECT_GT(metrics.avg_encode_time_ms, 0); + EXPECT_GT(metrics.encode_usage_percent, 0); + EXPECT_GE(metrics.capture_queue_delay_ms_per_s, 0); + EXPECT_GE(metrics.encode_rsd, 0); +} + +TEST_F(OveruseFrameDetectorTest, CaptureJitter) { + EXPECT_EQ(InitialJitter(), CaptureJitterMs()); + InsertFramesWithInterval(1000, kFrameInterval33ms, kWidth, kHeight); + EXPECT_NE(InitialJitter(), CaptureJitterMs()); +} + +TEST_F(OveruseFrameDetectorTest, CaptureJitterResetAfterResolutionChange) { + EXPECT_EQ(InitialJitter(), CaptureJitterMs()); + InsertFramesWithInterval(1000, kFrameInterval33ms, kWidth, kHeight); + EXPECT_NE(InitialJitter(), CaptureJitterMs()); + // Verify reset. + InsertFramesWithInterval(1, kFrameInterval33ms, kWidth, kHeight + 1); + EXPECT_EQ(InitialJitter(), CaptureJitterMs()); +} + +TEST_F(OveruseFrameDetectorTest, CaptureJitterResetAfterFrameTimeout) { + EXPECT_EQ(InitialJitter(), CaptureJitterMs()); + InsertFramesWithInterval(1000, kFrameInterval33ms, kWidth, kHeight); + EXPECT_NE(InitialJitter(), CaptureJitterMs()); + InsertFramesWithInterval( + 1, options_.frame_timeout_interval_ms, kWidth, kHeight); + EXPECT_NE(InitialJitter(), CaptureJitterMs()); + // Verify reset. + InsertFramesWithInterval( + 1, options_.frame_timeout_interval_ms + 1, kWidth, kHeight); + EXPECT_EQ(InitialJitter(), CaptureJitterMs()); +} + +TEST_F(OveruseFrameDetectorTest, CaptureJitterResetAfterChangingThreshold) { + EXPECT_EQ(InitialJitter(), CaptureJitterMs()); + options_.high_capture_jitter_threshold_ms = 90.0f; + overuse_detector_->SetOptions(options_); + EXPECT_EQ(InitialJitter(), CaptureJitterMs()); + options_.low_capture_jitter_threshold_ms = 30.0f; + overuse_detector_->SetOptions(options_); + EXPECT_EQ(InitialJitter(), CaptureJitterMs()); +} + +TEST_F(OveruseFrameDetectorTest, MinFrameSamplesBeforeUpdatingCaptureJitter) { + options_.min_frame_samples = 40; + overuse_detector_->SetOptions(options_); + InsertFramesWithInterval(40, kFrameInterval33ms, kWidth, kHeight); + EXPECT_EQ(InitialJitter(), CaptureJitterMs()); } TEST_F(OveruseFrameDetectorTest, NoCaptureQueueDelay) { EXPECT_EQ(overuse_detector_->CaptureQueueDelayMsPerS(), 0); - overuse_detector_->FrameCaptured(320, 180); + overuse_detector_->FrameCaptured( + kWidth, kHeight, clock_->TimeInMilliseconds()); overuse_detector_->FrameProcessingStarted(); EXPECT_EQ(overuse_detector_->CaptureQueueDelayMsPerS(), 0); } TEST_F(OveruseFrameDetectorTest, CaptureQueueDelay) { - overuse_detector_->FrameCaptured(320, 180); + overuse_detector_->FrameCaptured( + kWidth, kHeight, clock_->TimeInMilliseconds()); clock_->AdvanceTimeMilliseconds(100); overuse_detector_->FrameProcessingStarted(); EXPECT_EQ(overuse_detector_->CaptureQueueDelayMsPerS(), 100); } TEST_F(OveruseFrameDetectorTest, CaptureQueueDelayMultipleFrames) { - overuse_detector_->FrameCaptured(320, 180); + overuse_detector_->FrameCaptured( + kWidth, kHeight, clock_->TimeInMilliseconds()); clock_->AdvanceTimeMilliseconds(10); - overuse_detector_->FrameCaptured(320, 180); + overuse_detector_->FrameCaptured( + kWidth, kHeight, clock_->TimeInMilliseconds()); clock_->AdvanceTimeMilliseconds(20); overuse_detector_->FrameProcessingStarted(); @@ -143,9 +313,11 @@ } TEST_F(OveruseFrameDetectorTest, CaptureQueueDelayResetAtResolutionSwitch) { - overuse_detector_->FrameCaptured(320, 180); + overuse_detector_->FrameCaptured( + kWidth, kHeight, clock_->TimeInMilliseconds()); clock_->AdvanceTimeMilliseconds(10); - overuse_detector_->FrameCaptured(321, 180); + overuse_detector_->FrameCaptured( + kWidth, kHeight + 1, clock_->TimeInMilliseconds()); clock_->AdvanceTimeMilliseconds(20); overuse_detector_->FrameProcessingStarted(); @@ -153,7 +325,8 @@ } TEST_F(OveruseFrameDetectorTest, CaptureQueueDelayNoMatchingCapturedFrame) { - overuse_detector_->FrameCaptured(320, 180); + overuse_detector_->FrameCaptured( + kWidth, kHeight, clock_->TimeInMilliseconds()); clock_->AdvanceTimeMilliseconds(100); overuse_detector_->FrameProcessingStarted(); EXPECT_EQ(overuse_detector_->CaptureQueueDelayMsPerS(), 100); @@ -162,24 +335,216 @@ EXPECT_EQ(overuse_detector_->CaptureQueueDelayMsPerS(), 100); } +TEST_F(OveruseFrameDetectorTest, FrameDelay_OneFrameDisabled) { + options_.enable_extended_processing_usage = false; + overuse_detector_->SetOptions(options_); + const int kProcessingTimeMs = 100; + overuse_detector_->FrameCaptured(kWidth, kHeight, 33); + clock_->AdvanceTimeMilliseconds(kProcessingTimeMs); + overuse_detector_->FrameSent(33); + EXPECT_EQ(-1, overuse_detector_->LastProcessingTimeMs()); +} + +TEST_F(OveruseFrameDetectorTest, FrameDelay_OneFrame) { + options_.enable_extended_processing_usage = true; + overuse_detector_->SetOptions(options_); + const int kProcessingTimeMs = 100; + overuse_detector_->FrameCaptured(kWidth, kHeight, 33); + clock_->AdvanceTimeMilliseconds(kProcessingTimeMs); + EXPECT_EQ(-1, overuse_detector_->LastProcessingTimeMs()); + overuse_detector_->FrameSent(33); + EXPECT_EQ(kProcessingTimeMs, overuse_detector_->LastProcessingTimeMs()); + EXPECT_EQ(0, overuse_detector_->FramesInQueue()); +} + +TEST_F(OveruseFrameDetectorTest, FrameDelay_TwoFrames) { + options_.enable_extended_processing_usage = true; + overuse_detector_->SetOptions(options_); + const int kProcessingTimeMs1 = 100; + const int kProcessingTimeMs2 = 50; + const int kTimeBetweenFramesMs = 200; + overuse_detector_->FrameCaptured(kWidth, kHeight, 33); + clock_->AdvanceTimeMilliseconds(kProcessingTimeMs1); + overuse_detector_->FrameSent(33); + EXPECT_EQ(kProcessingTimeMs1, overuse_detector_->LastProcessingTimeMs()); + clock_->AdvanceTimeMilliseconds(kTimeBetweenFramesMs); + overuse_detector_->FrameCaptured(kWidth, kHeight, 66); + clock_->AdvanceTimeMilliseconds(kProcessingTimeMs2); + overuse_detector_->FrameSent(66); + EXPECT_EQ(kProcessingTimeMs2, overuse_detector_->LastProcessingTimeMs()); +} + +TEST_F(OveruseFrameDetectorTest, FrameDelay_MaxQueueSize) { + options_.enable_extended_processing_usage = true; + overuse_detector_->SetOptions(options_); + const int kMaxQueueSize = 91; + for (int i = 0; i < kMaxQueueSize * 2; ++i) { + overuse_detector_->FrameCaptured(kWidth, kHeight, i); + } + EXPECT_EQ(kMaxQueueSize, overuse_detector_->FramesInQueue()); +} + +TEST_F(OveruseFrameDetectorTest, FrameDelay_NonProcessedFramesRemoved) { + options_.enable_extended_processing_usage = true; + overuse_detector_->SetOptions(options_); + const int kProcessingTimeMs = 100; + overuse_detector_->FrameCaptured(kWidth, kHeight, 33); + clock_->AdvanceTimeMilliseconds(kProcessingTimeMs); + overuse_detector_->FrameCaptured(kWidth, kHeight, 35); + clock_->AdvanceTimeMilliseconds(kProcessingTimeMs); + overuse_detector_->FrameCaptured(kWidth, kHeight, 66); + clock_->AdvanceTimeMilliseconds(kProcessingTimeMs); + overuse_detector_->FrameCaptured(kWidth, kHeight, 99); + clock_->AdvanceTimeMilliseconds(kProcessingTimeMs); + EXPECT_EQ(-1, overuse_detector_->LastProcessingTimeMs()); + EXPECT_EQ(4, overuse_detector_->FramesInQueue()); + overuse_detector_->FrameSent(66); + // Frame 33, 35 removed, 66 processed, 99 not processed. + EXPECT_EQ(2 * kProcessingTimeMs, overuse_detector_->LastProcessingTimeMs()); + EXPECT_EQ(1, overuse_detector_->FramesInQueue()); + overuse_detector_->FrameSent(99); + EXPECT_EQ(kProcessingTimeMs, overuse_detector_->LastProcessingTimeMs()); + EXPECT_EQ(0, overuse_detector_->FramesInQueue()); +} + +TEST_F(OveruseFrameDetectorTest, FrameDelay_ResetClearsFrames) { + options_.enable_extended_processing_usage = true; + overuse_detector_->SetOptions(options_); + const int kProcessingTimeMs = 100; + overuse_detector_->FrameCaptured(kWidth, kHeight, 33); + EXPECT_EQ(1, overuse_detector_->FramesInQueue()); + clock_->AdvanceTimeMilliseconds(kProcessingTimeMs); + // Verify reset (resolution changed). + overuse_detector_->FrameCaptured(kWidth, kHeight + 1, 66); + EXPECT_EQ(1, overuse_detector_->FramesInQueue()); + clock_->AdvanceTimeMilliseconds(kProcessingTimeMs); + overuse_detector_->FrameSent(66); + EXPECT_EQ(kProcessingTimeMs, overuse_detector_->LastProcessingTimeMs()); + EXPECT_EQ(0, overuse_detector_->FramesInQueue()); +} + +TEST_F(OveruseFrameDetectorTest, FrameDelay_NonMatchingSendFrameIgnored) { + options_.enable_extended_processing_usage = true; + overuse_detector_->SetOptions(options_); + const int kProcessingTimeMs = 100; + overuse_detector_->FrameCaptured(kWidth, kHeight, 33); + clock_->AdvanceTimeMilliseconds(kProcessingTimeMs); + overuse_detector_->FrameSent(34); + EXPECT_EQ(-1, overuse_detector_->LastProcessingTimeMs()); + overuse_detector_->FrameSent(33); + EXPECT_EQ(kProcessingTimeMs, overuse_detector_->LastProcessingTimeMs()); +} + TEST_F(OveruseFrameDetectorTest, EncodedFrame) { const int kInitialAvgEncodeTimeInMs = 5; - EXPECT_EQ(kInitialAvgEncodeTimeInMs, overuse_detector_->AvgEncodeTimeMs()); + EXPECT_EQ(kInitialAvgEncodeTimeInMs, AvgEncodeTimeMs()); for (int i = 0; i < 30; i++) { clock_->AdvanceTimeMilliseconds(33); overuse_detector_->FrameEncoded(2); } - EXPECT_EQ(2, overuse_detector_->AvgEncodeTimeMs()); + EXPECT_EQ(2, AvgEncodeTimeMs()); } -TEST_F(OveruseFrameDetectorTest, EncodedUsage) { - for (int i = 0; i < 30; i++) { - overuse_detector_->FrameCaptured(320, 180); - clock_->AdvanceTimeMilliseconds(5); - overuse_detector_->FrameEncoded(5); - clock_->AdvanceTimeMilliseconds(33-5); - } - EXPECT_EQ(15, overuse_detector_->EncodeUsagePercent()); +TEST_F(OveruseFrameDetectorTest, InitialProcessingUsage) { + EXPECT_EQ(InitialUsage(), UsagePercent()); +} + +TEST_F(OveruseFrameDetectorTest, ProcessingUsage) { + const int kProcessingTimeMs = 5; + InsertAndSendFramesWithInterval( + 1000, kFrameInterval33ms, kWidth, kHeight, kProcessingTimeMs); + EXPECT_EQ(kProcessingTimeMs * 100 / kFrameInterval33ms, UsagePercent()); +} + +TEST_F(OveruseFrameDetectorTest, ProcessingUsageResetAfterChangingThreshold) { + EXPECT_EQ(InitialUsage(), UsagePercent()); + options_.high_encode_usage_threshold_percent = 100; + overuse_detector_->SetOptions(options_); + EXPECT_EQ(InitialUsage(), UsagePercent()); + options_.low_encode_usage_threshold_percent = 20; + overuse_detector_->SetOptions(options_); + EXPECT_EQ(InitialUsage(), UsagePercent()); +} + +// enable_encode_usage_method = true; +// UsagePercent() > high_encode_usage_threshold_percent => overuse. +// UsagePercent() < low_encode_usage_threshold_percent => underuse. +TEST_F(OveruseFrameDetectorTest, TriggerOveruseWithProcessingUsage) { + options_.enable_capture_jitter_method = false; + options_.enable_encode_usage_method = true; + options_.enable_extended_processing_usage = false; + overuse_detector_->SetOptions(options_); + // usage > high => overuse + EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1); + TriggerOveruseWithProcessingUsage(options_.high_threshold_consecutive_count); +} + +TEST_F(OveruseFrameDetectorTest, OveruseAndRecoverWithProcessingUsage) { + options_.enable_capture_jitter_method = false; + options_.enable_encode_usage_method = true; + options_.enable_extended_processing_usage = false; + overuse_detector_->SetOptions(options_); + // usage > high => overuse + EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1); + TriggerOveruseWithProcessingUsage(options_.high_threshold_consecutive_count); + // usage < low => underuse + EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(testing::AtLeast(1)); + TriggerUnderuseWithProcessingUsage(); +} + +TEST_F(OveruseFrameDetectorTest, + OveruseAndRecoverWithProcessingUsageMethodDisabled) { + options_.enable_capture_jitter_method = false; + options_.enable_encode_usage_method = false; + options_.enable_extended_processing_usage = false; + overuse_detector_->SetOptions(options_); + // usage > high => overuse + EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(0); + TriggerOveruseWithProcessingUsage(options_.high_threshold_consecutive_count); + // usage < low => underuse + EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(0); + TriggerUnderuseWithProcessingUsage(); +} + +// enable_extended_processing_usage = true; +// enable_encode_usage_method = true; +// UsagePercent() > high_encode_usage_threshold_percent => overuse. +// UsagePercent() < low_encode_usage_threshold_percent => underuse. +TEST_F(OveruseFrameDetectorTest, TriggerOveruseWithExtendedProcessingUsage) { + options_.enable_capture_jitter_method = false; + options_.enable_encode_usage_method = true; + options_.enable_extended_processing_usage = true; + overuse_detector_->SetOptions(options_); + // usage > high => overuse + EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1); + TriggerOveruseWithProcessingUsage(options_.high_threshold_consecutive_count); +} + +TEST_F(OveruseFrameDetectorTest, OveruseAndRecoverWithExtendedProcessingUsage) { + options_.enable_capture_jitter_method = false; + options_.enable_encode_usage_method = true; + options_.enable_extended_processing_usage = true; + overuse_detector_->SetOptions(options_); + // usage > high => overuse + EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1); + TriggerOveruseWithProcessingUsage(options_.high_threshold_consecutive_count); + // usage < low => underuse + EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(testing::AtLeast(1)); + TriggerUnderuseWithProcessingUsage(); +} + +TEST_F(OveruseFrameDetectorTest, + OveruseAndRecoverWithExtendedProcessingUsageMethodDisabled) { + options_.enable_capture_jitter_method = false; + options_.enable_encode_usage_method = false; + options_.enable_extended_processing_usage = true; + overuse_detector_->SetOptions(options_); + // usage > high => overuse + EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(0); + TriggerOveruseWithProcessingUsage(options_.high_threshold_consecutive_count); + // usage < low => underuse + EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(0); + TriggerUnderuseWithProcessingUsage(); } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/OWNERS 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/OWNERS 2015-02-03 14:33:38.000000000 +0000 @@ -1,4 +1,13 @@ mflodman@webrtc.org stefan@webrtc.org -wu@webrtc.org mallinath@webrtc.org + +per-file *.isolate=kjellander@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* + +per-file BUILD.gn=kjellander@webrtc.org + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/stream_synchronization.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/stream_synchronization.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/stream_synchronization.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/stream_synchronization.cc 2015-02-03 14:33:38.000000000 +0000 @@ -16,7 +16,7 @@ #include -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { @@ -64,15 +64,15 @@ return false; } int64_t audio_last_capture_time_ms; - if (!synchronization::RtpToNtpMs(audio_measurement.latest_timestamp, - audio_measurement.rtcp, - &audio_last_capture_time_ms)) { + if (!RtpToNtpMs(audio_measurement.latest_timestamp, + audio_measurement.rtcp, + &audio_last_capture_time_ms)) { return false; } int64_t video_last_capture_time_ms; - if (!synchronization::RtpToNtpMs(video_measurement.latest_timestamp, - video_measurement.rtcp, - &video_last_capture_time_ms)) { + if (!RtpToNtpMs(video_measurement.latest_timestamp, + video_measurement.rtcp, + &video_last_capture_time_ms)) { return false; } if (video_last_capture_time_ms < 0) { @@ -96,18 +96,12 @@ assert(total_audio_delay_target_ms && total_video_delay_target_ms); int current_video_delay_ms = *total_video_delay_target_ms; - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, video_channel_id_, - "Audio delay is: %d for voice channel: %d", - current_audio_delay_ms, audio_channel_id_); - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, video_channel_id_, - "Network delay diff is: %d for voice channel: %d", - channel_delay_->network_delay, audio_channel_id_); + LOG(LS_VERBOSE) << "Audio delay: " << current_audio_delay_ms + << ", network delay diff: " << channel_delay_->network_delay + << " current diff: " << relative_delay_ms + << " for channel " << audio_channel_id_; // Calculate the difference between the lowest possible video delay and // the current audio delay. - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, video_channel_id_, - "Current diff is: %d for audio channel: %d", - relative_delay_ms, audio_channel_id_); - int current_diff_ms = current_video_delay_ms - current_audio_delay_ms + relative_delay_ms; @@ -198,11 +192,10 @@ channel_delay_->last_video_delay_ms = new_video_delay_ms; channel_delay_->last_audio_delay_ms = new_audio_delay_ms; - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, video_channel_id_, - "Sync video delay %d ms for video channel and audio delay %d for audio " - "channel %d", - new_video_delay_ms, channel_delay_->extra_audio_delay_ms, - audio_channel_id_); + LOG(LS_VERBOSE) << "Sync video delay " << new_video_delay_ms + << " and audio delay " << channel_delay_->extra_audio_delay_ms + << " for video channel " << video_channel_id_ + << " for audio channel " << audio_channel_id_; // Return values. *total_video_delay_target_ms = new_video_delay_ms; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/stream_synchronization.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/stream_synchronization.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/stream_synchronization.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/stream_synchronization.h 2015-02-03 14:33:38.000000000 +0000 @@ -13,7 +13,7 @@ #include -#include "webrtc/modules/remote_bitrate_estimator/include/rtp_to_ntp.h" +#include "webrtc/system_wrappers/interface/rtp_to_ntp.h" #include "webrtc/typedefs.h" namespace webrtc { @@ -24,7 +24,7 @@ public: struct Measurements { Measurements() : rtcp(), latest_receive_time_ms(0), latest_timestamp(0) {} - synchronization::RtcpList rtcp; + RtcpList rtcp; int64_t latest_receive_time_ms; uint32_t latest_timestamp; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/stream_synchronization_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/stream_synchronization_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/stream_synchronization_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/stream_synchronization_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -33,9 +33,8 @@ : kNtpJan1970(2208988800UL), time_now_ms_(offset) {} - synchronization::RtcpMeasurement GenerateRtcp(int frequency, - uint32_t offset) const { - synchronization::RtcpMeasurement rtcp; + RtcpMeasurement GenerateRtcp(int frequency, uint32_t offset) const { + RtcpMeasurement rtcp; NowNtp(&rtcp.ntp_secs, &rtcp.ntp_frac); rtcp.rtp_timestamp = NowRtp(frequency, offset); return rtcp; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/android/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/android/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/android/Android.mk 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/android/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -MY_CAPTURE_FOLDER := ../../../../modules/video_capture/main/source -MY_CAPTURE_JAVA_FOLDER := Android/java/org/webrtc/videoengine -MY_CAPTURE_PATH := $(MY_CAPTURE_FOLDER)/$(MY_CAPTURE_JAVA_FOLDER) - -MY_RENDER_FOLDER := ../../../../modules/video_render/main/source -MY_RENDER_JAVA_FOLDER := Android/java/org/webrtc/videoengine -MY_RENDER_PATH := $(MY_RENDER_FOLDER)/$(MY_RENDER_JAVA_FOLDER) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SRC_FILES := \ - src/org/webrtc/vieautotest/ViEAutotest.java \ - $(MY_CAPTURE_PATH)/VideoCaptureAndroid.java \ - $(MY_CAPTURE_PATH)/VideoCaptureDeviceInfoAndroid.java \ - $(MY_RENDER_PATH)/ViEAndroidGLES20.java \ - $(MY_RENDER_PATH)/ViERenderer.java \ - $(MY_RENDER_PATH)/ViESurfaceRenderer.java - -LOCAL_PACKAGE_NAME := webrtc-video-autotest -LOCAL_CERTIFICATE := platform - -LOCAL_JNI_SHARED_LIBRARIES := libwebrtc-video-autotest-jni - -include $(BUILD_PACKAGE) - -include $(call all-makefiles-under,$(LOCAL_PATH)) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/android/jni/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/android/jni/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/android/jni/Android.mk 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/android/jni/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -# the follow two lines are for NDK build -INTERFACES_PATH := $(LOCAL_PATH)/../../../../../../build/interface -LIBS_PATH := $(LOCAL_PATH)/../../../../../../build/libraries - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests -LOCAL_MODULE := libwebrtc-video-autotest-jni -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := \ - vie_autotest_jni.cc \ - ../../source/vie_autotest_android.cc \ - ../../source/vie_autotest.cc \ - ../../source/vie_autotest_base.cc \ - ../../source/vie_autotest_capture.cc \ - ../../source/vie_autotest_codec.cc \ - ../../source/vie_autotest_file.cc \ - ../../source/vie_autotest_image_process.cc \ - ../../source/vie_autotest_loopback.cc \ - ../../source/vie_autotest_network.cc \ - ../../source/vie_autotest_render.cc \ - ../../source/vie_autotest_rtp_rtcp.cc \ - ../../source/tb_I420_codec.cc \ - ../../source/tb_capture_device.cc \ - ../../source/tb_external_transport.cc \ - ../../source/tb_interfaces.cc \ - ../../source/tb_video_channel.cc - -LOCAL_CFLAGS := \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_ANDROID' \ - '-DWEBRTC_ANDROID_OPENSLES' - -LOCAL_C_INCLUDES := \ - external/gtest/include \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../interface \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../../../../.. \ - $(LOCAL_PATH)/../../../../../common_video/interface \ - $(LOCAL_PATH)/../../../../../common_video/vplib/main/interface \ - $(LOCAL_PATH)/../../../../../modules/interface \ - $(LOCAL_PATH)/../../../../../modules/video_capture/main/interface \ - $(LOCAL_PATH)/../../../../../modules/video_capture/main/source \ - $(LOCAL_PATH)/../../../../../modules/video_coding/codecs/interface \ - $(LOCAL_PATH)/../../../../../modules/video_render/main/interface \ - $(LOCAL_PATH)/../../../../../voice_engine/include \ - $(LOCAL_PATH)/../../../../../system_wrappers/interface - -LOCAL_PRELINK_MODULE := false - -LOCAL_SHARED_LIBRARIES := \ - libutils \ - libstlport \ - libandroid \ - libwebrtc \ - libGLESv2 - -# the following line is for NDK build -LOCAL_LDLIBS := $(LIBS_PATH)/VideoEngine_android_gcc.a -llog -lgcc - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_SHARED_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/android/res/values/strings.xml thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/android/res/values/strings.xml --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/android/res/values/strings.xml 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/android/res/values/strings.xml 2015-02-03 14:33:38.000000000 +0000 @@ -19,7 +19,6 @@ Capture Codec Mix - Encryption External Codec File Image Process diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/automated/vie_extended_integration_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/automated/vie_extended_integration_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/automated/vie_extended_integration_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/automated/vie_extended_integration_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -53,7 +53,7 @@ } TEST_F(DISABLED_ON_MAC(ViEExtendedIntegrationTest), - RunsRtpRtcpTestWithoutErrors) { + DISABLED_RunsRtpRtcpTestWithoutErrors) { tests_->ViERtpRtcpExtendedTest(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/automated/vie_network_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/automated/vie_network_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/automated/vie_network_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/automated/vie_network_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "gflags/gflags.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" +#include "webrtc/video_engine/test/auto_test/interface/vie_autotest.h" +#include "webrtc/video_engine/test/libvietest/include/tb_interfaces.h" +#include "webrtc/system_wrappers/interface/sleep.h" +#include "webrtc/system_wrappers/interface/tick_util.h" + +namespace { + +class RtcpCollectorTransport : public webrtc::Transport { + public: + RtcpCollectorTransport() : packets_() {} + virtual ~RtcpCollectorTransport() {} + + virtual int SendPacket(int /*channel*/, + const void* /*data*/, + int /*len*/) OVERRIDE { + EXPECT_TRUE(false); + return 0; + } + virtual int SendRTCPPacket(int channel, const void* data, int len) OVERRIDE { + const uint8_t* buf = static_cast(data); + webrtc::RtpUtility::RtpHeaderParser parser(buf, len); + if (parser.RTCP()) { + Packet p; + p.channel = channel; + p.length = len; + if (parser.ParseRtcp(&p.header)) { + if (p.header.payloadType == 201) { + buf += 20; + len -= 20; + } else { + return 0; + } + if (TryParseREMB(buf, len, &p)) { + packets_.push_back(p); + } + } + } + return 0; + } + + bool FindREMBFor(uint32_t ssrc, double min_rate) const { + for (std::vector::const_iterator it = packets_.begin(); + it != packets_.end(); ++it) { + if (it->remb_bitrate >= min_rate && it->remb_ssrc.end() != + std::find(it->remb_ssrc.begin(), it->remb_ssrc.end(), ssrc)) { + return true; + } + } + return false; + } + + private: + struct Packet { + Packet() : channel(-1), length(0), header(), remb_bitrate(0), remb_ssrc() {} + int channel; + int length; + webrtc::RTPHeader header; + double remb_bitrate; + std::vector remb_ssrc; + }; + + bool TryParseREMB(const uint8_t* buf, int length, Packet* p) { + if (length < 8) { + return false; + } + if (buf[0] != 'R' || buf[1] != 'E' || buf[2] != 'M' || buf[3] != 'B') { + return false; + } + uint8_t ssrcs = buf[4]; + uint8_t exp = buf[5] >> 2; + uint32_t mantissa = ((buf[5] & 0x03) << 16) + (buf[6] << 8) + buf[7]; + double bitrate = mantissa * static_cast(1 << exp); + p->remb_bitrate = bitrate; + + if (length < (8 + 4 * ssrcs)) { + return false; + } + buf += 8; + for (uint8_t i = 0; i < ssrcs; ++i) { + uint32_t ssrc = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]; + p->remb_ssrc.push_back(ssrc); + buf += 4; + } + return true; + } + + std::vector packets_; +}; + +class ViENetworkTest : public testing::Test { + protected: + ViENetworkTest() : vie_("ViENetworkTest"), channel_(-1), transport() {} + virtual ~ViENetworkTest() {} + + virtual void SetUp() OVERRIDE { + EXPECT_EQ(0, vie_.base->CreateChannel(channel_)); + EXPECT_EQ(0, vie_.rtp_rtcp->SetRembStatus(channel_, false, true)); + EXPECT_EQ(0, vie_.network->RegisterSendTransport(channel_, transport)); + } + + virtual void TearDown() OVERRIDE { + EXPECT_EQ(0, vie_.network->DeregisterSendTransport(channel_)); + } + + void ReceiveASTPacketsForBWE() { + for (int i = 0; i < kPacketCount; ++i) { + int64_t time = webrtc::TickTime::MillisecondTimestamp(); + webrtc::RTPHeader header; + header.ssrc = kSsrc1; + header.timestamp = i * 45000; + header.extension.hasAbsoluteSendTime = true; + header.extension.absoluteSendTime = i << (18 - 6); + EXPECT_EQ(0, vie_.network->ReceivedBWEPacket(channel_, time, kPacketSize, + header)); + webrtc::SleepMs(kIntervalMs); + } + } + + enum { + kSsrc1 = 667, + kSsrc2 = 668, + kPacketCount = 100, + kPacketSize = 1000, + kIntervalMs = 22 + }; + TbInterfaces vie_; + int channel_; + RtcpCollectorTransport transport; +}; + +TEST_F(ViENetworkTest, ReceiveBWEPacket_NoExtension) { + for (int i = 0; i < kPacketCount; ++i) { + int64_t time = webrtc::TickTime::MillisecondTimestamp(); + webrtc::RTPHeader header; + header.ssrc = kSsrc1; + header.timestamp = i * 45000; + EXPECT_EQ(0, vie_.network->ReceivedBWEPacket(channel_, time, kPacketSize, + header)); + webrtc::SleepMs(kIntervalMs); + } + EXPECT_FALSE(transport.FindREMBFor(kSsrc1, 0.0)); + unsigned int bandwidth = 0; + EXPECT_EQ(0, vie_.rtp_rtcp->GetEstimatedReceiveBandwidth(channel_, + &bandwidth)); +} + +TEST_F(ViENetworkTest, ReceiveBWEPacket_TOF) { + EXPECT_EQ(0, vie_.rtp_rtcp->SetReceiveTimestampOffsetStatus(channel_, true, + 1)); + for (int i = 0; i < kPacketCount; ++i) { + int64_t time = webrtc::TickTime::MillisecondTimestamp(); + webrtc::RTPHeader header; + header.ssrc = kSsrc1; + header.timestamp = i * 45000; + header.extension.hasTransmissionTimeOffset = true; + header.extension.transmissionTimeOffset = 17; + EXPECT_EQ(0, vie_.network->ReceivedBWEPacket(channel_, time, kPacketSize, + header)); + webrtc::SleepMs(kIntervalMs); + } + EXPECT_FALSE(transport.FindREMBFor(kSsrc1, 0.0)); + unsigned int bandwidth = 0; + EXPECT_EQ(0, vie_.rtp_rtcp->GetEstimatedReceiveBandwidth(channel_, + &bandwidth)); +} + +TEST_F(ViENetworkTest, ReceiveBWEPacket_AST) { + EXPECT_EQ(0, vie_.rtp_rtcp->SetReceiveAbsoluteSendTimeStatus(channel_, true, + 1)); + ReceiveASTPacketsForBWE(); + EXPECT_TRUE(transport.FindREMBFor(kSsrc1, 100000.0)); + unsigned int bandwidth = 0; + EXPECT_EQ(0, vie_.rtp_rtcp->GetEstimatedReceiveBandwidth(channel_, + &bandwidth)); + EXPECT_GT(bandwidth, 0u); +} + +TEST_F(ViENetworkTest, ReceiveBWEPacket_ASTx2) { + EXPECT_EQ(0, vie_.rtp_rtcp->SetReceiveAbsoluteSendTimeStatus(channel_, true, + 1)); + for (int i = 0; i < kPacketCount; ++i) { + int64_t time = webrtc::TickTime::MillisecondTimestamp(); + webrtc::RTPHeader header; + header.ssrc = kSsrc1; + header.timestamp = i * 45000; + header.extension.hasAbsoluteSendTime = true; + header.extension.absoluteSendTime = i << (18 - 6); + EXPECT_EQ(0, vie_.network->ReceivedBWEPacket(channel_, time, kPacketSize, + header)); + header.ssrc = kSsrc2; + header.timestamp += 171717; + EXPECT_EQ(0, vie_.network->ReceivedBWEPacket(channel_, time, kPacketSize, + header)); + webrtc::SleepMs(kIntervalMs); + } + EXPECT_TRUE(transport.FindREMBFor(kSsrc1, 200000.0)); + EXPECT_TRUE(transport.FindREMBFor(kSsrc2, 200000.0)); + unsigned int bandwidth = 0; + EXPECT_EQ(0, vie_.rtp_rtcp->GetEstimatedReceiveBandwidth(channel_, + &bandwidth)); + EXPECT_GT(bandwidth, 0u); +} + +TEST_F(ViENetworkTest, ReceiveBWEPacket_AST_DisabledReceive) { + EXPECT_EQ(0, vie_.rtp_rtcp->SetReceiveAbsoluteSendTimeStatus(channel_, false, + 1)); + ReceiveASTPacketsForBWE(); + EXPECT_FALSE(transport.FindREMBFor(kSsrc1, 0.0)); + unsigned int bandwidth = 0; + EXPECT_EQ(0, vie_.rtp_rtcp->GetEstimatedReceiveBandwidth(channel_, + &bandwidth)); +} +} // namespace diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/automated/vie_standard_integration_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/automated/vie_standard_integration_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/automated/vie_standard_integration_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/automated/vie_standard_integration_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -51,13 +51,8 @@ tests_->ViERenderStandardTest(); } -// Fails on Mac, see https://code.google.com/p/webrtc/issues/detail?id=1790 -#if defined(WEBRTC_MAC) -#define MAYBE_RunsRtpRtcpTestWithoutErrors DISABLED_RunsRtpRtcpTestWithoutErrors -#else -#define MAYBE_RunsRtpRtcpTestWithoutErrors RunsRtpRtcpTestWithoutErrors -#endif -TEST_F(ViEStandardIntegrationTest, MAYBE_RunsRtpRtcpTestWithoutErrors) { +// Flaky, see webrtc:1790. +TEST_F(ViEStandardIntegrationTest, DISABLED_RunsRtpRtcpTestWithoutErrors) { tests_->ViERtpRtcpStandardTest(); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/automated/vie_video_verification_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/automated/vie_video_verification_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/automated/vie_video_verification_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/automated/vie_video_verification_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -115,105 +115,6 @@ } }; -class ParameterizedFullStackTest : public ViEVideoVerificationTest, - public ::testing::WithParamInterface { - public: - static const int kNumFullStackInstances = 4; - - protected: - struct TestParameters { - NetworkParameters network; - std::string file_name; - int width; - int height; - int bitrate; - double avg_psnr_threshold; - double avg_ssim_threshold; - ProtectionMethod protection_method; - std::string test_label; - }; - - void SetUp() { - for (int i = 0; i < kNumFullStackInstances; ++i) { - parameter_table_[i].file_name = webrtc::test::ResourcePath("foreman_cif", - "yuv"); - parameter_table_[i].width = 352; - parameter_table_[i].height = 288; - } - int i = 0; - parameter_table_[i].protection_method = kNack; - // Uniform loss => Setting burst length to -1. - parameter_table_[i].network.loss_model = kUniformLoss; - parameter_table_[i].network.packet_loss_rate = 0; - parameter_table_[i].network.burst_length = -1; - parameter_table_[i].network.mean_one_way_delay = 0; - parameter_table_[i].network.std_dev_one_way_delay = 0; - parameter_table_[i].bitrate = 300; - // TODO(holmer): Enable for Win and Mac when the file rendering has been - // moved to a separate thread. -#ifdef WEBRTC_LINUX - parameter_table_[i].avg_psnr_threshold = 35; - parameter_table_[i].avg_ssim_threshold = 0.96; -#else - parameter_table_[i].avg_psnr_threshold = 0; - parameter_table_[i].avg_ssim_threshold = 0.0; -#endif - parameter_table_[i].test_label = "net_delay_0_0_plr_0"; - ++i; - parameter_table_[i].protection_method = kNack; - parameter_table_[i].network.loss_model = kUniformLoss; - parameter_table_[i].network.packet_loss_rate = 5; - parameter_table_[i].network.burst_length = -1; - parameter_table_[i].network.mean_one_way_delay = 50; - parameter_table_[i].network.std_dev_one_way_delay = 5; - parameter_table_[i].bitrate = 300; - // TODO(holmer): Enable for Win and Mac when the file rendering has been - // moved to a separate thread. -#ifdef WEBRTC_LINUX - parameter_table_[i].avg_psnr_threshold = 35; - parameter_table_[i].avg_ssim_threshold = 0.96; -#else - parameter_table_[i].avg_psnr_threshold = 0; - parameter_table_[i].avg_ssim_threshold = 0.0; -#endif - parameter_table_[i].test_label = "net_delay_50_5_plr_5"; - ++i; - parameter_table_[i].protection_method = kNack; - parameter_table_[i].network.loss_model = kUniformLoss; - parameter_table_[i].network.packet_loss_rate = 0; - parameter_table_[i].network.burst_length = -1; - parameter_table_[i].network.mean_one_way_delay = 100; - parameter_table_[i].network.std_dev_one_way_delay = 10; - parameter_table_[i].bitrate = 300; - // TODO(holmer): Enable for Win and Mac when the file rendering has been - // moved to a separate thread. -#ifdef WEBRTC_LINUX - parameter_table_[i].avg_psnr_threshold = 35; - parameter_table_[i].avg_ssim_threshold = 0.96; -#else - parameter_table_[i].avg_psnr_threshold = 0; - parameter_table_[i].avg_ssim_threshold = 0.0; -#endif - parameter_table_[i].test_label = "net_delay_100_10_plr_0"; - ++i; - parameter_table_[i].protection_method = kNack; - parameter_table_[i].network.loss_model = kGilbertElliotLoss; - parameter_table_[i].network.packet_loss_rate = 5; - parameter_table_[i].network.burst_length = 3; - parameter_table_[i].network.mean_one_way_delay = 100; - parameter_table_[i].network.std_dev_one_way_delay = 10; - parameter_table_[i].bitrate = 300; - // Thresholds disabled for now. This is being run mainly to get a graph. - parameter_table_[i].avg_psnr_threshold = 0; - parameter_table_[i].avg_ssim_threshold = 0.0; - parameter_table_[i].test_label = "net_delay_100_10_plr_5_gilbert_elliot"; - - ASSERT_EQ(kNumFullStackInstances - 1, i); - } - - TestParameters parameter_table_[kNumFullStackInstances]; -}; - TEST_F(ViEVideoVerificationTest, RunsBaseStandardTestWithoutErrors) { // I420 is lossless, so the I420 test should obviously get perfect results - // the local preview and remote output files should be bit-exact. This test @@ -251,99 +152,4 @@ kNumAttempts << " attempts."; } -// Runs a whole stack processing with tracking of which frames are dropped -// in the encoder. Tests show that they start at the same frame, which is -// the important thing when doing frame-to-frame comparison with PSNR/SSIM. -TEST_P(ParameterizedFullStackTest, RunsFullStackWithoutErrors) { - // Using CIF here since it's a more common resolution than QCIF, and higher - // resolutions shouldn't be a problem for a test using VP8. - input_file_ = parameter_table_[GetParam()].file_name; - FrameDropDetector detector; - local_file_renderer_ = new ViEToFileRenderer(); - remote_file_renderer_ = new FrameDropMonitoringRemoteFileRenderer(&detector); - SetUpLocalFileRenderer(local_file_renderer_); - SetUpRemoteFileRenderer(remote_file_renderer_); - - // Set a low bit rate so the encoder budget will be tight, causing it to drop - // frames every now and then. - const int kBitRateKbps = parameter_table_[GetParam()].bitrate; - const NetworkParameters network = parameter_table_[GetParam()].network; - int width = parameter_table_[GetParam()].width; - int height = parameter_table_[GetParam()].height; - ProtectionMethod protection_method = - parameter_table_[GetParam()].protection_method; - ViETest::Log("Bit rate : %5d kbps", kBitRateKbps); - ViETest::Log("Packet loss : %5d %%", network.packet_loss_rate); - ViETest::Log("Network delay: mean=%dms std dev=%d ms", - network.mean_one_way_delay, network.std_dev_one_way_delay); - tests_.TestFullStack(input_file_, width, height, kBitRateKbps, - protection_method, network, local_file_renderer_, - remote_file_renderer_, &detector); - const std::string reference_file = local_file_renderer_->GetFullOutputPath(); - const std::string output_file = remote_file_renderer_->GetFullOutputPath(); - StopRenderers(); - - detector.CalculateResults(); - detector.PrintReport(parameter_table_[GetParam()].test_label); - - if (detector.GetNumberOfFramesDroppedAt(FrameDropDetector::kRendered) > - detector.GetNumberOfFramesDroppedAt(FrameDropDetector::kDecoded)) { - detector.PrintDebugDump(); - } - - ASSERT_GE(detector.GetNumberOfFramesDroppedAt(FrameDropDetector::kRendered), - detector.GetNumberOfFramesDroppedAt(FrameDropDetector::kDecoded)) - << "The number of dropped frames on the decode and render steps are not " - "equal. This may be because we have a major problem in the buffers of " - "the ViEToFileRenderer?"; - - // We may have dropped frames during the processing, which means the output - // file does not contain all the frames that are present in the input file. - // To make the quality measurement correct, we must adjust the output file to - // that by copying the last successful frame into the place where the dropped - // frame would be, for all dropped frames. - const int frame_length_in_bytes = 3 * width * height / 2; - ViETest::Log("Frame length: %d bytes", frame_length_in_bytes); - std::vector all_frames = detector.GetAllFrames(); - FixOutputFileForComparison(output_file, frame_length_in_bytes, all_frames); - - // Verify all sent frames are present in the output file. - size_t output_file_size = webrtc::test::GetFileSize(output_file); - EXPECT_EQ(all_frames.size(), output_file_size / frame_length_in_bytes) - << "The output file size is incorrect. It should be equal to the number " - "of frames multiplied by the frame size. This will likely affect " - "PSNR/SSIM calculations in a bad way."; - - TearDownFileRenderers(); - - // We are running on a lower bitrate here so we need to settle for somewhat - // lower PSNR and SSIM values. - double actual_psnr = 0; - double actual_ssim = 0; - CompareFiles(reference_file, output_file, &actual_psnr, &actual_ssim); - - const double kExpectedMinimumPSNR = - parameter_table_[GetParam()].avg_psnr_threshold; - const double kExpectedMinimumSSIM = - parameter_table_[GetParam()].avg_ssim_threshold; - - EXPECT_GE(actual_psnr, kExpectedMinimumPSNR); - EXPECT_GE(actual_ssim, kExpectedMinimumSSIM); - - std::stringstream ss; - ss << std::setprecision(3) << std::fixed << actual_psnr; - webrtc::test::PrintResult( - "psnr", "", parameter_table_[GetParam()].test_label, - ss.str(), "dB", false); - - ss.str(""); - ss << std::setprecision(3) << std::fixed << actual_ssim; - webrtc::test::PrintResult( - "ssim", "", parameter_table_[GetParam()].test_label, - ss.str(), "", false); -} - -INSTANTIATE_TEST_CASE_P(FullStackTests, ParameterizedFullStackTest, - ::testing::Range(0, ParameterizedFullStackTest::kNumFullStackInstances)); - } // namespace diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/OWNERS 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,6 @@ +per-file *.isolate=kjellander@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/primitives/base_primitives.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/primitives/base_primitives.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/primitives/base_primitives.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/primitives/base_primitives.cc 2015-02-03 14:33:38.000000000 +0000 @@ -43,10 +43,13 @@ webrtc::VideoEngine* video_engine, webrtc::ViEBase* base_interface, webrtc::ViENetwork* network_interface, + webrtc::ViERTP_RTCP* rtp_rtcp_interface, int video_channel, const char* device_name) { webrtc::VideoCodec video_codec; memset(&video_codec, 0, sizeof(webrtc::VideoCodec)); + EXPECT_EQ(0, rtp_rtcp_interface->SetTransmissionSmoothingStatus(video_channel, + false)); ConfigureCodecsToI420(video_channel, video_codec, codec_interface); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/primitives/base_primitives.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/primitives/base_primitives.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/primitives/base_primitives.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/primitives/base_primitives.h 2015-02-03 14:33:38.000000000 +0000 @@ -16,6 +16,7 @@ class ViEBase; class ViECodec; class ViENetwork; +class ViERTP_RTCP; } // Tests a I420-to-I420 call. This test exercises the most basic WebRTC @@ -26,6 +27,7 @@ webrtc::VideoEngine* video_engine, webrtc::ViEBase* base_interface, webrtc::ViENetwork* network_interface, + webrtc::ViERTP_RTCP* rtp_rtcp_interface, int video_channel, const char* device_name); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/primitives/framedrop_primitives.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/primitives/framedrop_primitives.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/primitives/framedrop_primitives.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/primitives/framedrop_primitives.cc 2015-02-03 14:33:38.000000000 +0000 @@ -40,15 +40,18 @@ FrameDropDetector* frame_drop_detector) : ExternalRendererEffectFilter(renderer), frame_drop_detector_(frame_drop_detector) {} - int Transform(int size, unsigned char* frameBuffer, - unsigned int timeStamp90KHz, unsigned int width, + int Transform(int size, + unsigned char* frame_buffer, + int64_t ntp_time_ms, + unsigned int timestamp, + unsigned int width, unsigned int height) { frame_drop_detector_->ReportFrameState( FrameDropDetector::kCreated, - timeStamp90KHz, + timestamp, webrtc::TickTime::MicrosecondTimestamp()); return webrtc::ExternalRendererEffectFilter::Transform( - size, frameBuffer, timeStamp90KHz, width, height); + size, frame_buffer, ntp_time_ms, timestamp, width, height); } private: FrameDropDetector* frame_drop_detector_; @@ -97,12 +100,15 @@ explicit DecodedTimestampEffectFilter(FrameDropDetector* frame_drop_detector) : frame_drop_detector_(frame_drop_detector) {} virtual ~DecodedTimestampEffectFilter() {} - virtual int Transform(int size, unsigned char* frameBuffer, - unsigned int timeStamp90KHz, unsigned int width, + virtual int Transform(int size, + unsigned char* frame_buffer, + int64_t ntp_time_ms, + unsigned int timestamp, + unsigned int width, unsigned int height) { frame_drop_detector_->ReportFrameState( FrameDropDetector::kDecoded, - timeStamp90KHz, + timestamp, webrtc::TickTime::MicrosecondTimestamp()); return 0; } @@ -588,7 +594,7 @@ int FrameDropMonitoringRemoteFileRenderer::DeliverFrame( unsigned char *buffer, int buffer_size, uint32_t time_stamp, - int64_t render_time, void* /*handle*/) { + int64_t ntp_time_ms, int64_t render_time, void* /*handle*/) { // |render_time| provides the ideal render time for this frame. If that time // has already passed we will render it immediately. int64_t report_render_time_us = render_time * 1000; @@ -600,7 +606,8 @@ frame_drop_detector_->ReportFrameState(FrameDropDetector::kRendered, time_stamp, report_render_time_us); return ViEToFileRenderer::DeliverFrame(buffer, buffer_size, - time_stamp, render_time, NULL); + time_stamp, ntp_time_ms, + render_time, NULL); } int FrameDropMonitoringRemoteFileRenderer::FrameSizeChange( diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/primitives/framedrop_primitives.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/primitives/framedrop_primitives.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/primitives/framedrop_primitives.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/primitives/framedrop_primitives.h 2015-02-03 14:33:38.000000000 +0000 @@ -226,6 +226,7 @@ unsigned int number_of_streams) OVERRIDE; int DeliverFrame(unsigned char* buffer, int buffer_size, uint32_t time_stamp, + int64_t ntp_time_ms, int64_t render_time, void* handle) OVERRIDE; private: diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/Android.mk 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,64 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH:= $(call my-dir) - -# voice engine test app - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../android-webrtc.mk - -LOCAL_MODULE_TAGS := tests -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES:= \ - vie_autotest.cc \ - vie_autotest_android.cc \ - vie_autotest_base.cc \ - vie_autotest_capture.cc \ - vie_autotest_codec.cc \ - vie_autotest_file.cc \ - vie_autotest_image_process.cc \ - vie_autotest_loopback.cc \ - vie_autotest_network.cc \ - vie_autotest_render.cc \ - vie_autotest_rtp_rtcp.cc \ - vie_comparison_tests.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_ANDROID' \ - '-DWEBRTC_ANDROID_OPENSLES' - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../helpers \ - $(LOCAL_PATH)/../primitives \ - $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/../../../include \ - $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../../../../modules/video_coding/codecs/interface \ - $(LOCAL_PATH)/../../../../system_wrappers/interface \ - $(LOCAL_PATH)/../../../../modules/video_render/main/interface \ - $(LOCAL_PATH)/../../../../modules/interface \ - $(LOCAL_PATH)/../../../../modules/video_capture/main/interface \ - $(LOCAL_PATH)/../../../../common_video/vplib/main/interface \ - $(LOCAL_PATH)/../../../../voice_engine/include - -LOCAL_SHARED_LIBRARIES := \ - libutils \ - libstlport \ - libwebrtc - -LOCAL_MODULE:= webrtc_video_test - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_EXECUTABLE) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_android.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_android.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_android.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_android.cc 2015-02-03 14:33:38.000000000 +0000 @@ -13,15 +13,18 @@ #include #include -#include "webrtc/video_engine/test/auto_test/interface/vie_autotest.h" +#include "webrtc/modules/video_capture/video_capture_internal.h" +#include "webrtc/modules/video_render/video_render_internal.h" #include "webrtc/video_engine/test/auto_test/interface/vie_autotest_defines.h" +#include "webrtc/video_engine/test/auto_test/interface/vie_autotest.h" int ViEAutoTestAndroid::RunAutotest(int testSelection, int subTestSelection, void* window1, void* window2, JavaVM* javaVM, void* env, void* context) { ViEAutoTest vieAutoTest(window1, window2); ViETest::Log("RunAutoTest(%d, %d)", testSelection, subTestSelection); - webrtc::VideoEngine::SetAndroidObjects(javaVM); + webrtc::SetCaptureAndroidVM(javaVM, static_cast(context)); + webrtc::SetRenderAndroidVM(javaVM); #ifndef WEBRTC_ANDROID_OPENSLES // voice engine calls into ADM directly webrtc::VoiceEngine::SetAndroidObjects(javaVM, env, context); @@ -66,9 +69,11 @@ vieAutoTest.ViEImageProcessStandardTest(); break; +#if 0 // vie_autotest_network.cc isn't actually pulled into the build at all! case 7: // network vieAutoTest.ViENetworkStandardTest(); break; +#endif case 8: // Render vieAutoTest.ViERenderStandardTest(); @@ -101,9 +106,11 @@ vieAutoTest.ViEImageProcessAPITest(); break; +#if 0 // vie_autotest_network.cc isn't actually pulled into the build at all! case 7: // network vieAutoTest.ViENetworkAPITest(); break; +#endif case 8: // Render vieAutoTest.ViERenderAPITest(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_base.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_base.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_base.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_base.cc 2015-02-03 14:33:38.000000000 +0000 @@ -67,8 +67,8 @@ ViETest::Log("You should shortly see a local preview from camera %s" " in window 1 and the remote video in window 2.", device_name); ::TestI420CallSetup(interfaces.codec, interfaces.video_engine, - base_interface, interfaces.network, video_channel, - device_name); + base_interface, interfaces.network, interfaces.rtp_rtcp, + video_channel, device_name); // *************************************************************** // Testing finished. Tear down Video Engine diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_capture.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_capture.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_capture.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_capture.cc 2015-02-03 14:33:38.000000000 +0000 @@ -83,8 +83,11 @@ } // Implements video_engineEffectFilter. - virtual int Transform(int size, unsigned char* frame_buffer, - unsigned int timeStamp90KHz, unsigned int width, + virtual int Transform(int size, + unsigned char* frame_buffer, + int64_t ntp_time_ms, + unsigned int timestamp, + unsigned int width, unsigned int height) { EXPECT_TRUE(frame_buffer != NULL); EXPECT_EQ(expected_width_, width); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -98,6 +98,9 @@ case webrtc::kVideoCodecVP8: ViETest::Log("\tcodecType: VP8"); break; + case webrtc::kVideoCodecVP9: + ViETest::Log("\tcodecType: VP9"); + break; case webrtc::kVideoCodecI420: ViETest::Log("\tcodecType: I420"); break; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_codec.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_codec.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_codec.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_codec.cc 2015-02-03 14:33:38.000000000 +0000 @@ -124,7 +124,8 @@ } virtual int Transform(int size, unsigned char* frame_buffer, - unsigned int time_stamp90KHz, + int64_t ntp_time_ms, + unsigned int timestamp, unsigned int width, unsigned int height) { num_frames_++; @@ -509,6 +510,13 @@ break; } } + const unsigned int kMinBitrate = 123; + video_codec.minBitrate = kMinBitrate; + video_codec.startBitrate = 50; + EXPECT_EQ(0, codec->SetSendCodec(video_channel, video_codec)); + EXPECT_EQ(0, codec->GetSendCodec(video_channel, video_codec)); + EXPECT_EQ(kMinBitrate, video_codec.startBitrate); + memset(&video_codec, 0, sizeof(video_codec)); EXPECT_EQ(0, codec->GetSendCodec(video_channel, video_codec)); EXPECT_EQ(webrtc::kVideoCodecVP8, video_codec.codecType); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_custom_call.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_custom_call.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_custom_call.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_custom_call.cc 2015-02-03 14:33:38.000000000 +0000 @@ -1651,7 +1651,7 @@ void PrintGetDiscardedPackets(webrtc::ViECodec* vie_codec, int video_channel) { std::cout << "Discarded Packets" << std::endl; int discarded_packets = 0; - discarded_packets = vie_codec->GetDiscardedPackets(video_channel); + discarded_packets = vie_codec->GetNumDiscardedPackets(video_channel); std::cout << "\tNumber of discarded packets: " << discarded_packets << std::endl; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_image_process.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_image_process.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_image_process.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_image_process.cc 2015-02-03 14:33:38.000000000 +0000 @@ -28,12 +28,15 @@ ~MyEffectFilter() {} - virtual int Transform(int size, unsigned char* frameBuffer, - unsigned int timeStamp90KHz, unsigned int width, + virtual int Transform(int size, + unsigned char* frame_buffer, + int64_t ntp_time_ms, + unsigned int timestamp, + unsigned int width, unsigned int height) { // Black and white - memset(frameBuffer + (2 * size) / 3, 0x7f, size / 3); + memset(frame_buffer + (2 * size) / 3, 0x7f, size / 3); return 0; } }; @@ -157,10 +160,9 @@ tbCapture.captureId, effectFilter)); EXPECT_EQ(0, ViE.image_process->DeregisterCaptureEffectFilter( tbCapture.captureId)); - - // Double deregister - EXPECT_NE(0, ViE.image_process->DeregisterCaptureEffectFilter( + EXPECT_EQ(0, ViE.image_process->DeregisterCaptureEffectFilter( tbCapture.captureId)); + // Non-existing capture device EXPECT_NE(0, ViE.image_process->RegisterCaptureEffectFilter( tbChannel.videoChannel, effectFilter)); @@ -174,7 +176,7 @@ tbChannel.videoChannel, effectFilter)); EXPECT_EQ(0, ViE.image_process->DeregisterRenderEffectFilter( tbChannel.videoChannel)); - EXPECT_NE(0, ViE.image_process->DeregisterRenderEffectFilter( + EXPECT_EQ(0, ViE.image_process->DeregisterRenderEffectFilter( tbChannel.videoChannel)); // Non-existing channel id @@ -190,26 +192,12 @@ tbChannel.videoChannel, effectFilter)); EXPECT_EQ(0, ViE.image_process->DeregisterSendEffectFilter( tbChannel.videoChannel)); - EXPECT_NE(0, ViE.image_process->DeregisterSendEffectFilter( + EXPECT_EQ(0, ViE.image_process->DeregisterSendEffectFilter( tbChannel.videoChannel)); EXPECT_NE(0, ViE.image_process->RegisterSendEffectFilter( tbCapture.captureId, effectFilter)); // - // Denoising - // - EXPECT_EQ(0, ViE.image_process->EnableDenoising(tbCapture.captureId, true)); - // If the denoising is already enabled, it will just reuturn 0. - EXPECT_EQ(0, ViE.image_process->EnableDenoising(tbCapture.captureId, true)); - EXPECT_EQ(0, ViE.image_process->EnableDenoising( - tbCapture.captureId, false)); - // If the denoising is already disabled, it will just reuturn 0. - EXPECT_EQ(0, ViE.image_process->EnableDenoising( - tbCapture.captureId, false)); - EXPECT_NE(0, ViE.image_process->EnableDenoising( - tbChannel.videoChannel, true)); - - // // Deflickering // EXPECT_EQ(0, ViE.image_process->EnableDeflickering( diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_loopback.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_loopback.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_loopback.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_loopback.cc 2015-02-03 14:33:38.000000000 +0000 @@ -158,7 +158,7 @@ printf("Error in scanf()\n"); return -1; } - getchar(); + getc(stdin); captureIdx = captureIdx - 1; // Compensate for idx start at 1. #endif error = ptrViECapture->GetCaptureDevice(captureIdx, deviceName, @@ -350,7 +350,7 @@ printf("Error in scanf()\n"); return -1; } - getchar(); + getc(stdin); codecIdx = codecIdx - 1; // Compensate for idx start at 1. #endif // VP8 over generic transport gets this special one. @@ -624,7 +624,7 @@ // Call started printf("\nLoopback call started\n\n"); printf("Press enter to stop..."); - while ((getchar()) != '\n') + while ((getc(stdin)) != '\n') ; //******************************************************** diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_main.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_main.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_main.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_main.cc 2015-02-03 14:33:38.000000000 +0000 @@ -13,6 +13,7 @@ #include "gflags/gflags.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/test/field_trial.h" #include "webrtc/test/testsupport/fileutils.h" #include "webrtc/video_engine/test/auto_test/interface/vie_autotest.h" #include "webrtc/video_engine/test/auto_test/interface/vie_autotest_window_manager_interface.h" @@ -20,6 +21,10 @@ DEFINE_bool(automated, false, "Run Video engine tests in noninteractive mode."); DEFINE_bool(auto_custom_call, false, "Run custom call directly."); +DEFINE_string(force_fieldtrials, "", + "Field trials control experimental feature code which can be forced. " + "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enable/" + " will assign the group Enable to field trial WebRTC-FooFeature."); static const std::string kStandardTest = "ViEStandardIntegrationTest"; static const std::string kExtendedTest = "ViEExtendedIntegrationTest"; @@ -43,8 +48,13 @@ webrtc::test::SetExecutablePath(argv[0]); // Initialize the testing framework. testing::InitGoogleTest(&argc, argv); + // AllowCommandLineParsing allows us to ignore flags passed on to us by + // Chromium build bots without having to explicitly disable them. + google::AllowCommandLineReparsing(); // Parse remaining flags: google::ParseCommandLineFlags(&argc, &argv, true); + // Initialize field trial + webrtc::test::InitFieldTrialsFromString(FLAGS_force_fieldtrials); int result; if (FLAGS_automated) { @@ -96,10 +106,10 @@ int result; if (scanf("%d", &result) <= 0) { ViETest::Log("\nPlease enter a number instead, then hit enter."); - getchar(); + getc(stdin); return kInvalidChoice; } - getchar(); // Consume enter key. + getc(stdin); // Consume enter key. if (result < min_allowed || result > max_allowed) { ViETest::Log("%d-%d are valid choices. Please try again.", min_allowed, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_network.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_network.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_network.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_network.cc 2015-02-03 14:33:38.000000000 +0000 @@ -150,7 +150,7 @@ ViETest::Log("On Win7 and late Vista, you need to right click the " "exe and choose"); ViETest::Log("\"Run as administrator\"\n"); - getchar(); + getc(stdin); } EXPECT_EQ(0, ViE.network->GetSendToS( tbChannel.videoChannel, DSCP, useSetSockOpt)); // No ToS set @@ -390,7 +390,7 @@ ViETest::Log("On Win7 and late Vista, you need to right click the " "exe and choose"); ViETest::Log("\"Run as administrator\"\n"); - getchar(); + getc(stdin); } EXPECT_EQ(0, ViE.network->GetSendToS( tbChannel.videoChannel, DSCP, useSetSockOpt)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_record.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_record.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_record.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_record.cc 2015-02-03 14:33:38.000000000 +0000 @@ -216,7 +216,7 @@ printf("Error in scanf()\n"); return -1; } - getchar(); + getc(stdin); captureIdx = captureIdx - 1; // Compensate for idx start at 1. #endif error = ptrViECapture->GetCaptureDevice(captureIdx, deviceName, @@ -441,14 +441,14 @@ clock_time = webrtc::TickTime::MillisecondTimestamp(); timing << clock_time << std::endl; } - char c = getchar(); + char c = getc(stdin); fflush(stdin); while (c != 's') { if (c == '\n' && enable_labeling == 1) { clock_time = webrtc::TickTime::MillisecondTimestamp(); timing << clock_time << std::endl; } - c = getchar(); + c = getc(stdin); } if (enable_labeling == 1) { clock_time = webrtc::TickTime::MillisecondTimestamp(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_render.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_render.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_render.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_render.cc 2015-02-03 14:33:38.000000000 +0000 @@ -56,8 +56,10 @@ return 0; } - virtual int DeliverFrame(unsigned char* buffer, int bufferSize, + virtual int DeliverFrame(unsigned char* buffer, + int bufferSize, uint32_t time_stamp, + int64_t ntp_time_ms, int64_t render_time, void* /*handle*/) { if (bufferSize != CalcBufferSize(webrtc::kI420, _width, _height)) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc 2015-02-03 14:33:38.000000000 +0000 @@ -52,7 +52,7 @@ ViERtcpObserver() : _channel(-1), _subType(0), - _name(-1), + _name(0), _data(NULL), _dataLength(0) { @@ -179,8 +179,6 @@ myTransport.ClearStats(); EXPECT_EQ(0, ViE.rtp_rtcp->SetNACKStatus(tbChannel.videoChannel, true)); - EXPECT_EQ(0, ViE.rtp_rtcp->SetTransmissionSmoothingStatus( - tbChannel.videoChannel, true)); EXPECT_EQ(0, ViE.base->StartReceive(tbChannel.videoChannel)); EXPECT_EQ(0, ViE.base->StartSend(tbChannel.videoChannel)); @@ -220,6 +218,7 @@ myTransport.ClearStats(); const uint8_t kRtxPayloadType = 96; + // Temporarily disable pacing. EXPECT_EQ(0, ViE.rtp_rtcp->SetTransmissionSmoothingStatus( tbChannel.videoChannel, false)); EXPECT_EQ(0, ViE.rtp_rtcp->SetNACKStatus(tbChannel.videoChannel, true)); @@ -364,6 +363,33 @@ EXPECT_GE(received.extended_max_sequence_number, sent.extended_max_sequence_number); EXPECT_EQ(0, ViE.base->StopSend(tbChannel.videoChannel)); + EXPECT_EQ(0, ViE.base->StopReceive(tbChannel.videoChannel)); + + // + // Test bandwidth statistics with reserved bitrate + // + + myTransport.ClearStats(); + network.packet_loss_rate = 0; + network.loss_model = kUniformLoss; + myTransport.SetNetworkParameters(network); + + ViE.rtp_rtcp->SetReservedTransmitBitrate(tbChannel.videoChannel, 2000000); + + EXPECT_EQ(0, ViE.base->StartReceive(tbChannel.videoChannel)); + EXPECT_EQ(0, ViE.base->StartSend(tbChannel.videoChannel)); + + AutoTestSleep(kAutoTestSleepTimeMs); + + estimated_bandwidth = 0; + EXPECT_EQ(0, ViE.rtp_rtcp->GetEstimatedSendBandwidth(tbChannel.videoChannel, + &estimated_bandwidth)); + if (FLAGS_include_timing_dependent_tests) { + EXPECT_EQ(0u, estimated_bandwidth); + } + + EXPECT_EQ(0, ViE.base->StopReceive(tbChannel.videoChannel)); + EXPECT_EQ(0, ViE.base->StopSend(tbChannel.videoChannel)); // // Test bandwidth statistics with NACK and FEC separately @@ -492,9 +518,11 @@ EXPECT_EQ(0, ViE.base->StopReceive(tbChannel.videoChannel)); + ViETest::Log("Testing Network Down...\n"); EXPECT_EQ(0, ViE.rtp_rtcp->SetNACKStatus(tbChannel.videoChannel, true)); + // Reenable pacing. EXPECT_EQ(0, ViE.rtp_rtcp->SetTransmissionSmoothingStatus( tbChannel.videoChannel, true)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_file_based_comparison_tests.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_file_based_comparison_tests.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_file_based_comparison_tests.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_file_based_comparison_tests.cc 2015-02-03 14:33:38.000000000 +0000 @@ -66,8 +66,8 @@ const char* device_name = "Fake Capture Device"; ::TestI420CallSetup(interfaces.codec, interfaces.video_engine, - interfaces.base, interfaces.network, video_channel, - device_name); + interfaces.base, interfaces.network, interfaces.rtp_rtcp, + video_channel, device_name); EXPECT_EQ(0, render_interface->StopRender(video_channel)); EXPECT_EQ(0, render_interface->RemoveRenderer(video_channel)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/vie_auto_test.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/vie_auto_test.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/vie_auto_test.gypi 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/vie_auto_test.gypi 2015-02-03 14:33:38.000000000 +0000 @@ -13,15 +13,16 @@ 'type': 'executable', 'dependencies': [ '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', - '<(webrtc_root)/modules/modules.gyp:video_render_module', - '<(webrtc_root)/modules/modules.gyp:video_capture_module', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:metrics_default', + '<(webrtc_root)/modules/modules.gyp:video_capture_module_internal_impl', + '<(webrtc_root)/modules/modules.gyp:video_render_module_internal_impl', '<(webrtc_root)/voice_engine/voice_engine.gyp:voice_engine', '<(DEPTH)/testing/gtest.gyp:gtest', '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', '<(webrtc_root)/test/metrics.gyp:metrics', '<(webrtc_root)/test/test.gyp:channel_transport', '<(webrtc_root)/test/test.gyp:test_support', - '<(webrtc_root)/test/libtest/libtest.gyp:libtest', + '<(webrtc_root)/test/test.gyp:field_trial', 'video_engine_core', 'libvietest', ], @@ -42,6 +43,7 @@ 'automated/two_windows_fixture.cc', 'automated/vie_api_integration_test.cc', 'automated/vie_extended_integration_test.cc', + 'automated/vie_network_test.cc', 'automated/vie_standard_integration_test.cc', 'automated/vie_video_verification_test.cc', @@ -135,7 +137,6 @@ ], 'includes': [ '../../../build/isolate.gypi', - 'vie_auto_test.isolate', ], 'sources': [ 'vie_auto_test.isolate', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/vie_auto_test.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/vie_auto_test.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/vie_auto_test.isolate 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/auto_test/vie_auto_test.isolate 2015-02-03 14:33:38.000000000 +0000 @@ -8,31 +8,24 @@ { 'conditions': [ ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. 'variables': { - 'isolate_dependency_untracked': [ - '../../../../../data/', - '../../../../../resources/', + 'files': [ + '<(DEPTH)/data/', + '<(DEPTH)/resources/', ], }, }], ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../../../../testing/test_env.py', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/vie_auto_test<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_touched': [ - '../../../../DEPS', - ], - 'isolate_dependency_tracked': [ - '../../../../testing/test_env.py', + 'files': [ + '<(DEPTH)/DEPS', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/vie_auto_test<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '../../../../tools/swarming_client/', - ], }, }], ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/helpers/vie_to_file_renderer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/helpers/vie_to_file_renderer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/helpers/vie_to_file_renderer.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/helpers/vie_to_file_renderer.cc 2015-02-03 14:33:38.000000000 +0000 @@ -30,7 +30,7 @@ memcpy(this->buffer.get(), buffer, buffer_size); } - webrtc::scoped_array buffer; + webrtc::scoped_ptr buffer; int buffer_size; uint32_t timestamp; int64_t render_time; @@ -123,6 +123,7 @@ int ViEToFileRenderer::DeliverFrame(unsigned char *buffer, int buffer_size, uint32_t time_stamp, + int64_t ntp_time_ms, int64_t render_time, void* /*handle*/) { webrtc::CriticalSectionScoped lock(frame_queue_cs_.get()); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/tb_external_transport.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/tb_external_transport.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/tb_external_transport.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/tb_external_transport.h 2015-02-03 14:33:38.000000000 +0000 @@ -85,8 +85,8 @@ TbExternalTransport::SsrcChannelMap* receive_channels); ~TbExternalTransport(void); - virtual int SendPacket(int channel, const void *data, int len); - virtual int SendRTCPPacket(int channel, const void *data, int len); + virtual int SendPacket(int channel, const void *data, int len) OVERRIDE; + virtual int SendRTCPPacket(int channel, const void *data, int len) OVERRIDE; // Should only be called before/after traffic is being processed. // Only one observer can be set (multiple calls will overwrite each other). diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/tb_I420_codec.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/tb_I420_codec.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/tb_I420_codec.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/tb_I420_codec.h 2015-02-03 14:33:38.000000000 +0000 @@ -24,33 +24,28 @@ TbI420Encoder(); virtual ~TbI420Encoder(); - static int32_t VersionStatic(char* version, int32_t length); - virtual int32_t Version(char* version, int32_t length) const; - virtual int32_t InitEncode(const webrtc::VideoCodec* codecSettings, int32_t numberOfCores, - uint32_t maxPayloadSize); + uint32_t maxPayloadSize) OVERRIDE; virtual int32_t Encode( const webrtc::I420VideoFrame& inputImage, const webrtc::CodecSpecificInfo* codecSpecificInfo, - const std::vector* frameTypes); + const std::vector* frameTypes) OVERRIDE; virtual int32_t RegisterEncodeCompleteCallback( - webrtc::EncodedImageCallback* callback); - - virtual int32_t Release(); + webrtc::EncodedImageCallback* callback) OVERRIDE; - virtual int32_t Reset(); + virtual int32_t Release() OVERRIDE; - virtual int32_t SetChannelParameters(uint32_t packetLoss, int rtt); + virtual int32_t SetChannelParameters(uint32_t packetLoss, int rtt) OVERRIDE; - virtual int32_t SetRates(uint32_t newBitRate, uint32_t frameRate); + virtual int32_t SetRates(uint32_t newBitRate, uint32_t frameRate) OVERRIDE; - virtual int32_t SetPeriodicKeyFrames(bool enable); + virtual int32_t SetPeriodicKeyFrames(bool enable) OVERRIDE; virtual int32_t CodecConfigParameters(uint8_t* /*buffer*/, - int32_t /*size*/); + int32_t /*size*/) OVERRIDE; struct FunctionCalls { @@ -87,18 +82,18 @@ virtual ~TbI420Decoder(); virtual int32_t InitDecode(const webrtc::VideoCodec* inst, - int32_t numberOfCores); + int32_t numberOfCores) OVERRIDE; virtual int32_t Decode( const webrtc::EncodedImage& inputImage, bool missingFrames, const webrtc::RTPFragmentationHeader* fragmentation, const webrtc::CodecSpecificInfo* codecSpecificInfo = NULL, - int64_t renderTimeMs = -1); + int64_t renderTimeMs = -1) OVERRIDE; - virtual int32_t - RegisterDecodeCompleteCallback(webrtc::DecodedImageCallback* callback); - virtual int32_t Release(); - virtual int32_t Reset(); + virtual int32_t RegisterDecodeCompleteCallback( + webrtc::DecodedImageCallback* callback) OVERRIDE; + virtual int32_t Release() OVERRIDE; + virtual int32_t Reset() OVERRIDE; struct FunctionCalls { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/tb_interfaces.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/tb_interfaces.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/tb_interfaces.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/tb_interfaces.h 2015-02-03 14:33:38.000000000 +0000 @@ -13,8 +13,8 @@ #include +#include "webrtc/base/constructormagic.h" #include "webrtc/common_types.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/video_engine/include/vie_base.h" #include "webrtc/video_engine/include/vie_capture.h" #include "webrtc/video_engine/include/vie_codec.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/vie_external_render_filter.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/vie_external_render_filter.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/vie_external_render_filter.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/vie_external_render_filter.h 2015-02-03 14:33:38.000000000 +0000 @@ -24,8 +24,11 @@ explicit ExternalRendererEffectFilter(webrtc::ExternalRenderer* renderer) : width_(0), height_(0), renderer_(renderer) {} virtual ~ExternalRendererEffectFilter() {} - virtual int Transform(int size, unsigned char* frame_buffer, - unsigned int time_stamp90KHz, unsigned int width, + virtual int Transform(int size, + unsigned char* frame_buffer, + int64_t ntp_time_ms, + unsigned int timestamp, + unsigned int width, unsigned int height) { if (width != width_ || height_ != height) { renderer_->FrameSizeChange(width, height, 1); @@ -34,7 +37,8 @@ } return renderer_->DeliverFrame(frame_buffer, size, - time_stamp90KHz, + ntp_time_ms, + timestamp, webrtc::TickTime::MillisecondTimestamp(), NULL); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/vie_to_file_renderer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/vie_to_file_renderer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/vie_to_file_renderer.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/include/vie_to_file_renderer.h 2015-02-03 14:33:38.000000000 +0000 @@ -17,7 +17,7 @@ #include #include -#include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/base/constructormagic.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/video_engine/include/vie_render.h" @@ -60,6 +60,7 @@ int DeliverFrame(unsigned char* buffer, int buffer_size, uint32_t time_stamp, + int64_t ntp_time_ms, int64_t render_time, void* handle) OVERRIDE; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/OWNERS 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/OWNERS 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/testbed/tb_I420_codec.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/testbed/tb_I420_codec.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/testbed/tb_I420_codec.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/test/libvietest/testbed/tb_I420_codec.cc 2015-02-03 14:33:38.000000000 +0000 @@ -12,7 +12,6 @@ #include #include -#include #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" @@ -33,23 +32,6 @@ } } -int32_t TbI420Encoder::VersionStatic(char* version, int32_t length) -{ - const char* str = "I420 version 1.0.0\n"; - int32_t verLen = (int32_t) strlen(str); - if (verLen > length) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - strncpy(version, str, length); - return verLen; -} - -int32_t TbI420Encoder::Version(char* version, int32_t length) const -{ - return VersionStatic(version, length); -} - int32_t TbI420Encoder::Release() { _functionCalls.Release++; @@ -64,17 +46,6 @@ return WEBRTC_VIDEO_CODEC_OK; } -int32_t TbI420Encoder::Reset() -{ - _functionCalls.Reset++; - if (!_inited) - { - return WEBRTC_VIDEO_CODEC_UNINITIALIZED; - } - return WEBRTC_VIDEO_CODEC_OK; - -} - int32_t TbI420Encoder::SetChannelParameters(uint32_t packetLoss, int rtt) { _functionCalls.SetChannelParameters++; return WEBRTC_VIDEO_CODEC_OK; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/video_engine_core.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/video_engine_core.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/video_engine_core.gypi 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/video_engine_core.gypi 2015-02-03 14:33:38.000000000 +0000 @@ -122,6 +122,8 @@ 'type': '<(gtest_target_type)', 'dependencies': [ 'video_engine_core', + '<(webrtc_root)/modules/modules.gyp:video_capture_module_internal_impl', + '<(webrtc_root)/modules/modules.gyp:video_render_module_internal_impl', '<(DEPTH)/testing/gtest.gyp:gtest', '<(DEPTH)/testing/gmock.gyp:gmock', '<(webrtc_root)/test/test.gyp:test_support_main', @@ -131,12 +133,12 @@ 'encoder_state_feedback_unittest.cc', 'overuse_frame_detector_unittest.cc', 'stream_synchronization_unittest.cc', + 'vie_capturer_unittest.cc', + 'vie_codec_unittest.cc', 'vie_remb_unittest.cc', ], 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are - # using Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['OS=="android"', { 'dependencies': [ '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code', ], @@ -145,9 +147,7 @@ }, ], # targets 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are using - # Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['OS=="android"', { 'targets': [ { 'target_name': 'video_engine_core_unittests_apk_target', @@ -168,7 +168,6 @@ ], 'includes': [ '../build/isolate.gypi', - 'video_engine_core_unittests.isolate', ], 'sources': [ 'video_engine_core_unittests.isolate', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/video_engine_core_unittests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/video_engine_core_unittests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/video_engine_core_unittests.isolate 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/video_engine_core_unittests.isolate 2015-02-03 14:33:38.000000000 +0000 @@ -8,28 +8,23 @@ { 'conditions': [ ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. 'variables': { - 'isolate_dependency_untracked': [ - '../../../data/', - '../../../resources/', + 'files': [ + '<(DEPTH)/data/', + '<(DEPTH)/resources/', ], }, }], ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../../testing/test_env.py', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/video_engine_core_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_tracked': [ - '../../testing/test_env.py', + 'files': [ + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/video_engine_core_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '../../tools/swarming_client/', - ], }, }], ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_base_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_base_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_base_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_base_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -10,7 +10,6 @@ #include "webrtc/video_engine/vie_base_impl.h" -#include #include #include @@ -20,7 +19,7 @@ #include "webrtc/modules/video_processing/main/interface/video_processing.h" #include "webrtc/modules/video_render/include/video_render.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/video_engine/include/vie_errors.h" #include "webrtc/video_engine/vie_capturer.h" #include "webrtc/video_engine/vie_channel.h" @@ -45,42 +44,27 @@ } int ViEBaseImpl::Release() { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, shared_data_.instance_id(), - "ViEBase::Release()"); (*this)--; // Decrease ref count. int32_t ref_count = GetCount(); if (ref_count < 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, shared_data_.instance_id(), - "ViEBase release too many times"); - shared_data_.SetLastError(kViEAPIDoesNotExist); + LOG(LS_WARNING) << "ViEBase released too many times."; return -1; } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, shared_data_.instance_id(), - "ViEBase reference count: %d", ref_count); return ref_count; } ViEBaseImpl::ViEBaseImpl(const Config& config) - : shared_data_(config) { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_.instance_id(), - "ViEBaseImpl::ViEBaseImpl() Ctor"); -} + : shared_data_(config) {} -ViEBaseImpl::~ViEBaseImpl() { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_.instance_id(), - "ViEBaseImpl::ViEBaseImpl() Dtor"); -} +ViEBaseImpl::~ViEBaseImpl() {} int ViEBaseImpl::Init() { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, shared_data_.instance_id(), - "Init"); return 0; } int ViEBaseImpl::SetVoiceEngine(VoiceEngine* voice_engine) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s", __FUNCTION__); + LOG_F(LS_INFO) << "SetVoiceEngine"; if (shared_data_.channel_manager()->SetVoiceEngine(voice_engine) != 0) { shared_data_.SetLastError(kViEBaseVoEFailure); return -1; @@ -94,15 +78,10 @@ int ViEBaseImpl::RegisterCpuOveruseObserver(int video_channel, CpuOveruseObserver* observer) { + LOG_F(LS_INFO) << "RegisterCpuOveruseObserver on channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_.channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, - kTraceVideo, - ViEId(shared_data_.instance_id()), - "%s: channel %d doesn't exist", - __FUNCTION__, - video_channel); shared_data_.SetLastError(kViEBaseInvalidChannelId); return -1; } @@ -122,20 +101,34 @@ return 0; } -int ViEBaseImpl::CpuOveruseMeasures(int video_channel, - int* capture_jitter_ms, - int* avg_encode_time_ms, - int* encode_usage_percent, - int* capture_queue_delay_ms_per_s) { +int ViEBaseImpl::SetCpuOveruseOptions(int video_channel, + const CpuOveruseOptions& options) { + ViEChannelManagerScoped cs(*(shared_data_.channel_manager())); + ViEChannel* vie_channel = cs.Channel(video_channel); + if (!vie_channel) { + shared_data_.SetLastError(kViEBaseInvalidChannelId); + return -1; + } + ViEEncoder* vie_encoder = cs.Encoder(video_channel); + assert(vie_encoder); + + ViEInputManagerScoped is(*(shared_data_.input_manager())); + ViEFrameProviderBase* provider = is.FrameProvider(vie_encoder); + if (provider) { + ViECapturer* capturer = is.Capture(provider->Id()); + if (capturer) { + capturer->SetCpuOveruseOptions(options); + return 0; + } + } + return -1; +} + +int ViEBaseImpl::GetCpuOveruseMetrics(int video_channel, + CpuOveruseMetrics* metrics) { ViEChannelManagerScoped cs(*(shared_data_.channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, - kTraceVideo, - ViEId(shared_data_.instance_id()), - "%s: channel %d doesn't exist", - __FUNCTION__, - video_channel); shared_data_.SetLastError(kViEBaseInvalidChannelId); return -1; } @@ -147,34 +140,34 @@ if (provider) { ViECapturer* capturer = is.Capture(provider->Id()); if (capturer) { - capturer->CpuOveruseMeasures(capture_jitter_ms, - avg_encode_time_ms, - encode_usage_percent, - capture_queue_delay_ms_per_s); + capturer->GetCpuOveruseMetrics(metrics); return 0; } } return -1; } +void ViEBaseImpl::RegisterSendSideDelayObserver( + int channel, SendSideDelayObserver* observer) { + ViEChannelManagerScoped cs(*(shared_data_.channel_manager())); + ViEChannel* vie_channel = cs.Channel(channel); + assert(vie_channel); + vie_channel->RegisterSendSideDelayObserver(observer); +} + int ViEBaseImpl::CreateChannel(int& video_channel) { // NOLINT return CreateChannel(video_channel, static_cast(NULL)); } int ViEBaseImpl::CreateChannel(int& video_channel, // NOLINT const Config* config) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s", __FUNCTION__); if (shared_data_.channel_manager()->CreateChannel(&video_channel, config) == -1) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s: Could not create channel", __FUNCTION__); video_channel = -1; shared_data_.SetLastError(kViEBaseChannelCreationFailed); return -1; } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s: channel created: %d", __FUNCTION__, video_channel); + LOG(LS_INFO) << "Video channel created: " << video_channel; return 0; } @@ -189,15 +182,10 @@ } int ViEBaseImpl::DeleteChannel(const int video_channel) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s(%d)", __FUNCTION__, video_channel); { ViEChannelManagerScoped cs(*(shared_data_.channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_.instance_id()), - "%s: channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_.SetLastError(kViEBaseInvalidChannelId); return -1; } @@ -214,25 +202,19 @@ } if (shared_data_.channel_manager()->DeleteChannel(video_channel) == -1) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s: Could not delete channel %d", __FUNCTION__, - video_channel); shared_data_.SetLastError(kViEBaseUnknownError); return -1; } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s: channel deleted: %d", __FUNCTION__, video_channel); + LOG(LS_INFO) << "Channel deleted " << video_channel; return 0; } int ViEBaseImpl::ConnectAudioChannel(const int video_channel, const int audio_channel) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s(%d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "ConnectAudioChannel, video channel " << video_channel + << ", audio channel " << audio_channel; ViEChannelManagerScoped cs(*(shared_data_.channel_manager())); if (!cs.Channel(video_channel)) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s: channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_.SetLastError(kViEBaseInvalidChannelId); return -1; } @@ -246,12 +228,9 @@ } int ViEBaseImpl::DisconnectAudioChannel(const int video_channel) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s(%d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "DisconnectAudioChannel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_.channel_manager())); if (!cs.Channel(video_channel)) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s: channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_.SetLastError(kViEBaseInvalidChannelId); return -1; } @@ -265,16 +244,10 @@ } int ViEBaseImpl::StartSend(const int video_channel) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_.instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); - + LOG_F(LS_INFO) << "StartSend: " << video_channel; ViEChannelManagerScoped cs(*(shared_data_.channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_.instance_id(), video_channel), - "%s: Channel %d does not exist", __FUNCTION__, video_channel); shared_data_.SetLastError(kViEBaseInvalidChannelId); return -1; } @@ -282,9 +255,7 @@ ViEEncoder* vie_encoder = cs.Encoder(video_channel); assert(vie_encoder != NULL); if (vie_encoder->Owner() != video_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_.instance_id(), video_channel), - "Can't start ssend on a receive only channel."); + LOG_F(LS_ERROR) << "Can't start send on a receive only channel."; shared_data_.SetLastError(kViEBaseReceiveOnlyChannel); return -1; } @@ -294,13 +265,10 @@ int32_t error = vie_channel->StartSend(); if (error != 0) { vie_encoder->Restart(); - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_.instance_id(), video_channel), - "%s: Could not start sending on channel %d", __FUNCTION__, - video_channel); if (error == kViEBaseAlreadySending) { shared_data_.SetLastError(kViEBaseAlreadySending); } + LOG_F(LS_ERROR) << "Could not start sending " << video_channel; shared_data_.SetLastError(kViEBaseUnknownError); return -1; } @@ -310,29 +278,21 @@ } int ViEBaseImpl::StopSend(const int video_channel) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_.instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "StopSend " << video_channel; ViEChannelManagerScoped cs(*(shared_data_.channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_.instance_id(), video_channel), - "%s: Channel %d does not exist", __FUNCTION__, video_channel); shared_data_.SetLastError(kViEBaseInvalidChannelId); return -1; } int32_t error = vie_channel->StopSend(); if (error != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_.instance_id(), video_channel), - "%s: Could not stop sending on channel %d", __FUNCTION__, - video_channel); if (error == kViEBaseNotSending) { shared_data_.SetLastError(kViEBaseNotSending); } else { + LOG_F(LS_ERROR) << "Could not stop sending " << video_channel; shared_data_.SetLastError(kViEBaseUnknownError); } return -1; @@ -341,16 +301,11 @@ } int ViEBaseImpl::StartReceive(const int video_channel) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_.instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "StartReceive " << video_channel; ViEChannelManagerScoped cs(*(shared_data_.channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_.instance_id(), video_channel), - "%s: Channel %d does not exist", __FUNCTION__, video_channel); shared_data_.SetLastError(kViEBaseInvalidChannelId); return -1; } @@ -362,16 +317,10 @@ } int ViEBaseImpl::StopReceive(const int video_channel) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_.instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); - + LOG_F(LS_INFO) << "StopReceive " << video_channel; ViEChannelManagerScoped cs(*(shared_data_.channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_.instance_id(), video_channel), - "%s: Channel %d does not exist", __FUNCTION__, video_channel); shared_data_.SetLastError(kViEBaseInvalidChannelId); return -1; } @@ -383,32 +332,8 @@ } int ViEBaseImpl::GetVersion(char version[1024]) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_.instance_id()), - "GetVersion(version=?)"); - assert(kViEVersionMaxMessageSize == 1024); - if (!version) { - shared_data_.SetLastError(kViEBaseInvalidArgument); - return -1; - } - - // Add WebRTC Version. - std::stringstream version_stream; - version_stream << "VideoEngine 3.49.0" << std::endl; - - // Add build info. - version_stream << "Build: " << BUILDINFO << std::endl; - -#ifdef WEBRTC_EXTERNAL_TRANSPORT - version_stream << "External transport build" << std::endl; -#endif - int version_length = version_stream.tellp(); - assert(version_length < 1024); - memcpy(version, version_stream.str().c_str(), version_length); - version[version_length] = '\0'; - - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, - ViEId(shared_data_.instance_id()), "GetVersion() => %s", - version); + assert(version != NULL); + strcpy(version, "VideoEngine 39"); return 0; } @@ -420,9 +345,6 @@ int original_channel, bool sender) { ViEChannelManagerScoped cs(*(shared_data_.channel_manager())); if (!cs.Channel(original_channel)) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s - original_channel does not exist.", __FUNCTION__, - shared_data_.instance_id()); shared_data_.SetLastError(kViEBaseInvalidChannelId); return -1; } @@ -430,14 +352,13 @@ if (shared_data_.channel_manager()->CreateChannel(&video_channel, original_channel, sender) == -1) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s: Could not create channel", __FUNCTION__); video_channel = -1; shared_data_.SetLastError(kViEBaseChannelCreationFailed); return -1; } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(shared_data_.instance_id()), - "%s: channel created: %d", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "VideoChannel created: " << video_channel + << ", base channel " << original_channel + << ", is send channel : " << sender; return 0; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_base_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_base_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_base_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_base_impl.h 2015-02-03 14:33:38.000000000 +0000 @@ -33,11 +33,12 @@ virtual int SetVoiceEngine(VoiceEngine* voice_engine); virtual int RegisterCpuOveruseObserver(int channel, CpuOveruseObserver* observer); - virtual int CpuOveruseMeasures(int channel, - int* capture_jitter_ms, - int* avg_encode_time_ms, - int* encode_usage_percent, - int* capture_queue_delay_ms_per_s); + virtual int SetCpuOveruseOptions(int channel, + const CpuOveruseOptions& options); + virtual int GetCpuOveruseMetrics(int channel, + CpuOveruseMetrics* metrics); + virtual void RegisterSendSideDelayObserver(int channel, + SendSideDelayObserver* observer) OVERRIDE; virtual void SetLoadManager(CPULoadStateCallbackInvoker* aLoadManager); virtual int CreateChannel(int& video_channel); // NOLINT virtual int CreateChannel(int& video_channel, // NOLINT @@ -64,11 +65,6 @@ ViESharedData* shared_data() { return &shared_data_; } private: - // Version functions. - int32_t AddViEVersion(char* str) const; - int32_t AddBuildInfo(char* str) const; - int32_t AddExternalTransportBuild(char* str) const; - int CreateChannel(int& video_channel, int original_channel, // NOLINT bool sender); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_capture_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_capture_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_capture_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_capture_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -12,7 +12,7 @@ #include -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/video_engine/include/vie_errors.h" #include "webrtc/video_engine/vie_capturer.h" #include "webrtc/video_engine/vie_channel.h" @@ -43,37 +43,24 @@ } int ViECaptureImpl::Release() { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, shared_data_->instance_id(), - "ViECapture::Release()"); // Decrease ref count (*this)--; int32_t ref_count = GetCount(); if (ref_count < 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, shared_data_->instance_id(), - "ViECapture release too many times"); + LOG(LS_WARNING) << "ViECapture released too many times."; shared_data_->SetLastError(kViEAPIDoesNotExist); return -1; } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, shared_data_->instance_id(), - "ViECapture reference count: %d", ref_count); return ref_count; } ViECaptureImpl::ViECaptureImpl(ViESharedData* shared_data) - : shared_data_(shared_data) { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_->instance_id(), - "ViECaptureImpl::ViECaptureImpl() Ctor"); -} + : shared_data_(shared_data) {} -ViECaptureImpl::~ViECaptureImpl() { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_->instance_id(), - "ViECaptureImpl::~ViECaptureImpl() Dtor"); -} +ViECaptureImpl::~ViECaptureImpl() {} int ViECaptureImpl::NumberOfCaptureDevices() { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s", __FUNCTION__); return shared_data_->input_manager()->NumberOfCaptureDevices(); } @@ -83,8 +70,6 @@ unsigned int device_nameUTF8Length, char* unique_idUTF8, unsigned int unique_idUTF8Length) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(list_number: %d)", __FUNCTION__, list_number); return shared_data_->input_manager()->GetDeviceName( list_number, device_nameUTF8, device_nameUTF8Length, @@ -95,8 +80,7 @@ const char* unique_idUTF8, const unsigned int unique_idUTF8Length, int& capture_id) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(unique_idUTF8: %s)", __FUNCTION__, unique_idUTF8); + LOG(LS_INFO) << "AllocateCaptureDevice " << unique_idUTF8; const int32_t result = shared_data_->input_manager()->CreateCaptureDevice( unique_idUTF8, @@ -111,8 +95,6 @@ int ViECaptureImpl::AllocateExternalCaptureDevice( int& capture_id, ViEExternalCapture*& external_capture) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s", __FUNCTION__); const int32_t result = shared_data_->input_manager()->CreateExternalCaptureDevice( external_capture, capture_id); @@ -121,56 +103,45 @@ shared_data_->SetLastError(result); return -1; } + LOG(LS_INFO) << "External capture device allocated: " << capture_id; return 0; } int ViECaptureImpl::AllocateCaptureDevice( VideoCaptureModule& capture_module, int& capture_id) { // NOLINT - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s", __FUNCTION__); int32_t result = shared_data_->input_manager()->CreateCaptureDevice( &capture_module, capture_id); if (result != 0) { shared_data_->SetLastError(result); return -1; } + LOG(LS_INFO) << "External capture device, by module, allocated: " + << capture_id; return 0; } int ViECaptureImpl::ReleaseCaptureDevice(const int capture_id) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(capture_id: %d)", __FUNCTION__, capture_id); + LOG(LS_INFO) << "ReleaseCaptureDevice " << capture_id; { ViEInputManagerScoped is((*(shared_data_->input_manager()))); ViECapturer* vie_capture = is.Capture(capture_id); if (!vie_capture) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s: Capture device %d doesn't exist", __FUNCTION__, - capture_id); shared_data_->SetLastError(kViECaptureDeviceDoesNotExist); return -1; } } - - // Destroy the capture device. return shared_data_->input_manager()->DestroyCaptureDevice(capture_id); } int ViECaptureImpl::ConnectCaptureDevice(const int capture_id, const int video_channel) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(capture_id: %d, video_channel: %d)", __FUNCTION__, - capture_id, video_channel); + LOG(LS_INFO) << "Connect capture id " << capture_id + << " to channel " << video_channel; ViEInputManagerScoped is(*(shared_data_->input_manager())); ViECapturer* vie_capture = is.Capture(capture_id); if (!vie_capture) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Capture device %d doesn't exist", __FUNCTION__, - capture_id); shared_data_->SetLastError(kViECaptureDeviceDoesNotExist); return -1; } @@ -178,26 +149,18 @@ ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, - video_channel); + LOG(LS_ERROR) << "Channel doesn't exist."; shared_data_->SetLastError(kViECaptureDeviceInvalidChannelId); return -1; } if (vie_encoder->Owner() != video_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "Can't connect capture device to a receive only channel."); + LOG(LS_ERROR) << "Can't connect capture device to a receive device."; shared_data_->SetLastError(kViECaptureDeviceInvalidChannelId); return -1; } // Check if the encoder already has a connected frame provider if (is.FrameProvider(vie_encoder) != NULL) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d already connected to a capture device.", - __FUNCTION__, video_channel); + LOG(LS_ERROR) << "Channel already connected to capture device."; shared_data_->SetLastError(kViECaptureDeviceAlreadyConnected); return -1; } @@ -215,17 +178,12 @@ int ViECaptureImpl::DisconnectCaptureDevice(const int video_channel) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(video_channel: %d)", __FUNCTION__, video_channel); + LOG(LS_INFO) << "DisconnectCaptureDevice " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id()), - "%s: Channel %d doesn't exist", __FUNCTION__, - video_channel); + LOG(LS_ERROR) << "Channel doesn't exist."; shared_data_->SetLastError(kViECaptureDeviceInvalidChannelId); return -1; } @@ -233,19 +191,11 @@ ViEInputManagerScoped is(*(shared_data_->input_manager())); ViEFrameProviderBase* frame_provider = is.FrameProvider(vie_encoder); if (!frame_provider) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, - ViEId(shared_data_->instance_id()), - "%s: No capture device connected to channel %d", - __FUNCTION__, video_channel); shared_data_->SetLastError(kViECaptureDeviceNotConnected); return -1; } if (frame_provider->Id() < kViECaptureIdBase || frame_provider->Id() > kViECaptureIdMax) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, - ViEId(shared_data_->instance_id()), - "%s: No capture device connected to channel %d", - __FUNCTION__, video_channel); shared_data_->SetLastError(kViECaptureDeviceNotConnected); return -1; } @@ -263,16 +213,11 @@ int ViECaptureImpl::StartCapture(const int capture_id, const CaptureCapability& capture_capability) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(capture_id: %d)", __FUNCTION__, capture_id); + LOG(LS_INFO) << "StartCapture " << capture_id; ViEInputManagerScoped is(*(shared_data_->input_manager())); ViECapturer* vie_capture = is.Capture(capture_id); if (!vie_capture) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), capture_id), - "%s: Capture device %d doesn't exist", __FUNCTION__, - capture_id); shared_data_->SetLastError(kViECaptureDeviceDoesNotExist); return -1; } @@ -288,28 +233,22 @@ } int ViECaptureImpl::StopCapture(const int capture_id) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(capture_id: %d)", __FUNCTION__, capture_id); + LOG(LS_INFO) << "StopCapture " << capture_id; ViEInputManagerScoped is(*(shared_data_->input_manager())); ViECapturer* vie_capture = is.Capture(capture_id); if (!vie_capture) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), capture_id), - "%s: Capture device %d doesn't exist", __FUNCTION__, - capture_id); shared_data_->SetLastError(kViECaptureDeviceDoesNotExist); return -1; } if (!vie_capture->Started()) { shared_data_->SetLastError(kViECaptureDeviceNotStarted); - return -1; + return 0; } if (vie_capture->Stop() != 0) { shared_data_->SetLastError(kViECaptureDeviceUnknownError); return -1; } - return 0; } @@ -331,16 +270,12 @@ i_rotation = 270; break; } - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(rotation: %d)", __FUNCTION__, i_rotation); + LOG(LS_INFO) << "SetRotateCaptureFrames for " << capture_id + << ", rotation " << i_rotation; ViEInputManagerScoped is(*(shared_data_->input_manager())); ViECapturer* vie_capture = is.Capture(capture_id); if (!vie_capture) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), capture_id), - "%s: Capture device %d doesn't exist", __FUNCTION__, - capture_id); shared_data_->SetLastError(kViECaptureDeviceDoesNotExist); return -1; } @@ -353,17 +288,12 @@ int ViECaptureImpl::SetCaptureDelay(const int capture_id, const unsigned int capture_delay_ms) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(capture_id: %d, capture_delay_ms %u)", __FUNCTION__, - capture_id, capture_delay_ms); + LOG(LS_INFO) << "SetCaptureDelay " << capture_delay_ms + << ", for device " << capture_id; ViEInputManagerScoped is(*(shared_data_->input_manager())); ViECapturer* vie_capture = is.Capture(capture_id); if (!vie_capture) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), capture_id), - "%s: Capture device %d doesn't exist", __FUNCTION__, - capture_id); shared_data_->SetLastError(kViECaptureDeviceDoesNotExist); return -1; } @@ -378,8 +308,6 @@ int ViECaptureImpl::NumberOfCapabilities( const char* unique_idUTF8, const unsigned int unique_idUTF8Length) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(capture_device_name: %s)", __FUNCTION__, unique_idUTF8); #if defined(WEBRTC_MAC) // TODO(mflodman) Move to capture module! @@ -387,9 +315,7 @@ // automatically (mandatory). // Thus this function cannot be supported on the Mac platform. shared_data_->SetLastError(kViECaptureDeviceMacQtkitNotSupported); - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s This API is not supported on Mac OS", __FUNCTION__, - shared_data_->instance_id()); + LOG_F(LS_ERROR) << "API not supported on Mac OS X."; return -1; #endif return shared_data_->input_manager()->NumberOfCaptureCapabilities( @@ -401,18 +327,14 @@ const unsigned int unique_idUTF8Length, const unsigned int capability_number, CaptureCapability& capability) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(capture_device_name: %s)", __FUNCTION__, unique_idUTF8); #if defined(WEBRTC_MAC) // TODO(mflodman) Move to capture module! // QTKit framework handles all capabilities and capture settings // automatically (mandatory). // Thus this function cannot be supported on the Mac platform. + LOG_F(LS_ERROR) << "API not supported on Mac OS X."; shared_data_->SetLastError(kViECaptureDeviceMacQtkitNotSupported); - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s This API is not supported on Mac OS", __FUNCTION__, - shared_data_->instance_id()); return -1; #endif if (shared_data_->input_manager()->GetCaptureCapability( @@ -436,15 +358,9 @@ // automatically (mandatory). // Thus this function cannot be supported on the Mac platform. shared_data_->SetLastError(kViECaptureDeviceMacQtkitNotSupported); - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s This API is not supported on Mac OS", __FUNCTION__, - shared_data_->instance_id()); + LOG_F(LS_ERROR) << "API not supported on Mac OS X."; return -1; #endif - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s capture_id (capture_device_name: %s)", __FUNCTION__, - unique_idUTF8); - return shared_data_->input_manager()->DisplayCaptureSettingsDialogBox( unique_idUTF8, dialog_title, parent_window, x, y); @@ -452,8 +368,6 @@ int ViECaptureImpl::GetOrientation(const char* unique_idUTF8, RotateCapturedFrame& orientation) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s (capture_device_name: %s)", __FUNCTION__, unique_idUTF8); if (shared_data_->input_manager()->GetOrientation( unique_idUTF8, orientation) != 0) { @@ -466,13 +380,11 @@ int ViECaptureImpl::EnableBrightnessAlarm(const int capture_id, const bool enable) { + LOG(LS_INFO) << "EnableBrightnessAlarm for device " << capture_id + << ", status " << enable; ViEInputManagerScoped is(*(shared_data_->input_manager())); ViECapturer* vie_capture = is.Capture(capture_id); if (!vie_capture) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), capture_id), - "%s: Capture device %d doesn't exist", __FUNCTION__, - capture_id); shared_data_->SetLastError(kViECaptureDeviceDoesNotExist); return -1; } @@ -485,20 +397,15 @@ int ViECaptureImpl::RegisterObserver(const int capture_id, ViECaptureObserver& observer) { + LOG(LS_INFO) << "Register capture observer " << capture_id; ViEInputManagerScoped is(*(shared_data_->input_manager())); ViECapturer* vie_capture = is.Capture(capture_id); if (!vie_capture) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), capture_id), - "%s: Capture device %d doesn't exist", __FUNCTION__, - capture_id); shared_data_->SetLastError(kViECaptureDeviceDoesNotExist); return -1; } if (vie_capture->IsObserverRegistered()) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), capture_id), - "%s: Observer already registered", __FUNCTION__); + LOG_F(LS_ERROR) << "Observer already registered."; shared_data_->SetLastError(kViECaptureObserverAlreadyRegistered); return -1; } @@ -513,10 +420,6 @@ ViEInputManagerScoped is(*(shared_data_->input_manager())); ViECapturer* vie_capture = is.Capture(capture_id); if (!vie_capture) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), capture_id), - "%s: Capture device %d doesn't exist", __FUNCTION__, - capture_id); shared_data_->SetLastError(kViECaptureDeviceDoesNotExist); return -1; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_capturer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_capturer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_capturer.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_capturer.cc 2015-02-03 14:33:38.000000000 +0000 @@ -10,6 +10,7 @@ #include "webrtc/video_engine/vie_capturer.h" +#include "webrtc/common_video/interface/texture_video_frame.h" #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" #include "webrtc/modules/interface/module_common_types.h" #include "webrtc/modules/utility/interface/process_thread.h" @@ -19,10 +20,9 @@ #include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/event_wrapper.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/thread_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" #include "webrtc/system_wrappers/interface/trace_event.h" -#include "webrtc/video_engine/include/vie_base.h" #include "webrtc/video_engine/include/vie_image_process.h" #include "webrtc/video_engine/overuse_frame_detector.h" #include "webrtc/video_engine/vie_defines.h" @@ -57,30 +57,18 @@ brightness_frame_stats_(NULL), current_brightness_level_(Normal), reported_brightness_level_(Normal), - denoising_enabled_(false), observer_cs_(CriticalSectionWrapper::CreateCriticalSection()), observer_(NULL), - overuse_detector_(new OveruseFrameDetector(Clock::GetRealTimeClock(), - kNormalUseStdDevMs, - kOveruseStdDevMs)), + overuse_detector_(new OveruseFrameDetector(Clock::GetRealTimeClock())), config_(config) { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id, capture_id), - "ViECapturer::ViECapturer(capture_id: %d, engine_id: %d)", - capture_id, engine_id); unsigned int t_id = 0; - if (capture_thread_.Start(t_id)) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id, capture_id), - "%s: thread started: %u", __FUNCTION__, t_id); - } else { + if (!capture_thread_.Start(t_id)) { assert(false); } module_process_thread_.RegisterModule(overuse_detector_.get()); } ViECapturer::~ViECapturer() { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id_, capture_id_), - "ViECapturer::~ViECapturer() - capture_id: %d, engine_id: %d", - capture_id_, engine_id_); module_process_thread_.DeRegisterModule(overuse_detector_.get()); // Stop the thread. @@ -105,10 +93,6 @@ delete &deliver_event_; } else { assert(false); - WEBRTC_TRACE(kTraceMemory, kTraceVideoRenderer, - ViEId(engine_id_, capture_id_), - "%s: Not able to stop capture thread for device %d, leaking", - __FUNCTION__, capture_id_); } if (image_proc_module_) { @@ -218,8 +202,6 @@ } int32_t ViECapturer::Start(const CaptureCapability& capture_capability) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_), "%s", - __FUNCTION__); int width; int height; int frame_rate; @@ -256,15 +238,11 @@ } int32_t ViECapturer::Stop() { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_), "%s", - __FUNCTION__); requested_capability_ = CaptureCapability(); return capture_module_->StopCapture(); } bool ViECapturer::Started() { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_), "%s", - __FUNCTION__); return capture_module_->CaptureStarted(); } @@ -276,15 +254,12 @@ overuse_detector_->SetObserver(observer); } -void ViECapturer::CpuOveruseMeasures(int* capture_jitter_ms, - int* avg_encode_time_ms, - int* encode_usage_percent, - int* capture_queue_delay_ms_per_s) const { - *capture_jitter_ms = overuse_detector_->last_capture_jitter_ms(); - *avg_encode_time_ms = overuse_detector_->AvgEncodeTimeMs(); - *encode_usage_percent = overuse_detector_->EncodeUsagePercent(); - *capture_queue_delay_ms_per_s = - overuse_detector_->AvgCaptureQueueDelayMsPerS(); +void ViECapturer::SetCpuOveruseOptions(const CpuOveruseOptions& options) { + overuse_detector_->SetOptions(options); +} + +void ViECapturer::GetCpuOveruseMetrics(CpuOveruseMetrics* metrics) const { + overuse_detector_->GetCpuOveruseMetrics(metrics); } int32_t ViECapturer::SetCaptureDelay(int32_t delay_ms) { @@ -318,10 +293,6 @@ uint16_t height, RawVideoType video_type, unsigned long long capture_time) { // NOLINT - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_), - "ExternalCapture::IncomingFrame width %d, height %d, " - "capture_time %u", width, height, capture_time); - if (!external_capture_module_) { return -1; } @@ -336,11 +307,6 @@ int ViECapturer::IncomingFrameI420(const ViEVideoFrameI420& video_frame, unsigned long long capture_time) { // NOLINT - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_), - "ExternalCapture::IncomingFrame width %d, height %d, " - " capture_time %u", video_frame.width, video_frame.height, - capture_time); - if (!external_capture_module_) { return -1; } @@ -362,10 +328,7 @@ video_frame.v_pitch); if (ret < 0) { - WEBRTC_TRACE(kTraceError, - kTraceVideo, - ViEId(engine_id_, capture_id_), - "Failed to create I420VideoFrame"); + LOG_F(LS_ERROR) << "Could not create I420Frame."; return -1; } @@ -376,33 +339,40 @@ void ViECapturer::SwapFrame(I420VideoFrame* frame) { external_capture_module_->IncomingI420VideoFrame(frame, frame->render_time_ms()); + frame->set_timestamp(0); + frame->set_ntp_time_ms(0); + frame->set_render_time_ms(0); } void ViECapturer::OnIncomingCapturedFrame(const int32_t capture_id, I420VideoFrame& video_frame) { - WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, capture_id_), - "%s(capture_id: %d)", __FUNCTION__, capture_id); CriticalSectionScoped cs(capture_cs_.get()); // Make sure we render this frame earlier since we know the render time set // is slightly off since it's being set when the frame has been received from // the camera, and not when the camera actually captured the frame. video_frame.set_render_time_ms(video_frame.render_time_ms() - FrameDelay()); + overuse_detector_->FrameCaptured(video_frame.width(), + video_frame.height(), + video_frame.render_time_ms()); + TRACE_EVENT_ASYNC_BEGIN1("webrtc", "Video", video_frame.render_time_ms(), "render_time", video_frame.render_time_ms()); - captured_frame_.SwapFrame(&video_frame); + if (video_frame.native_handle() != NULL) { + captured_frame_.reset(video_frame.CloneFrame()); + } else { + if (captured_frame_ == NULL || captured_frame_->native_handle() != NULL) + captured_frame_.reset(new I420VideoFrame()); + captured_frame_->SwapFrame(&video_frame); + } capture_event_.Set(); - overuse_detector_->FrameCaptured(captured_frame_.width(), - captured_frame_.height()); - return; } void ViECapturer::OnCaptureDelayChanged(const int32_t id, const int32_t delay) { - WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, capture_id_), - "%s(capture_id: %d) delay %d", __FUNCTION__, capture_id_, - delay); + LOG(LS_INFO) << "Capture delayed change to " << delay + << " for device " << id; // Deliver the network delay to all registered callbacks. ViEFrameProviderBase::SetFrameDelay(delay); @@ -412,26 +382,9 @@ ViEEffectFilter* effect_filter) { CriticalSectionScoped cs(deliver_cs_.get()); - if (!effect_filter) { - if (!effect_filter_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_), - "%s: no effect filter added for capture device %d", - __FUNCTION__, capture_id_); - return -1; - } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_), - "%s: deregister effect filter for device %d", __FUNCTION__, - capture_id_); - } else { - if (effect_filter_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_), - "%s: effect filter already added for capture device %d", - __FUNCTION__, capture_id_); - return -1; - } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_), - "%s: register effect filter for device %d", __FUNCTION__, - capture_id_); + if (effect_filter != NULL && effect_filter_ != NULL) { + LOG_F(LS_ERROR) << "Effect filter already registered."; + return -1; } effect_filter_ = effect_filter; return 0; @@ -443,9 +396,7 @@ image_proc_module_ = VideoProcessingModule::Create( ViEModuleId(engine_id_, capture_id_)); if (!image_proc_module_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_), - "%s: could not create video processing module", - __FUNCTION__); + LOG_F(LS_ERROR) << "Could not create video processing module."; return -1; } } @@ -463,43 +414,10 @@ return 0; } -int32_t ViECapturer::EnableDenoising(bool enable) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_), - "%s(capture_device_id: %d, enable: %d)", __FUNCTION__, - capture_id_, enable); - - CriticalSectionScoped cs(deliver_cs_.get()); - if (enable) { - if (denoising_enabled_) { - // Already enabled, nothing need to be done. - return 0; - } - denoising_enabled_ = true; - if (IncImageProcRefCount() != 0) { - return -1; - } - } else { - if (denoising_enabled_ == false) { - // Already disabled, nothing need to be done. - return 0; - } - denoising_enabled_ = false; - DecImageProcRefCount(); - } - - return 0; -} - int32_t ViECapturer::EnableDeflickering(bool enable) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_), - "%s(capture_device_id: %d, enable: %d)", __FUNCTION__, - capture_id_, enable); - CriticalSectionScoped cs(deliver_cs_.get()); if (enable) { if (deflicker_frame_stats_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_), - "%s: deflickering already enabled", __FUNCTION__); return -1; } if (IncImageProcRefCount() != 0) { @@ -508,8 +426,6 @@ deflicker_frame_stats_ = new VideoProcessingModule::FrameStats(); } else { if (deflicker_frame_stats_ == NULL) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_), - "%s: deflickering not enabled", __FUNCTION__); return -1; } DecImageProcRefCount(); @@ -520,15 +436,9 @@ } int32_t ViECapturer::EnableBrightnessAlarm(bool enable) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_), - "%s(capture_device_id: %d, enable: %d)", __FUNCTION__, - capture_id_, enable); - CriticalSectionScoped cs(deliver_cs_.get()); if (enable) { if (brightness_frame_stats_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_), - "%s: BrightnessAlarm already enabled", __FUNCTION__); return -1; } if (IncImageProcRefCount() != 0) { @@ -538,8 +448,6 @@ } else { DecImageProcRefCount(); if (brightness_frame_stats_ == NULL) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, capture_id_), - "%s: deflickering not enabled", __FUNCTION__); return -1; } delete brightness_frame_stats_; @@ -553,13 +461,17 @@ } bool ViECapturer::ViECaptureProcess() { + int64_t capture_time = -1; if (capture_event_.Wait(kThreadWaitTimeMs) == kEventSignaled) { overuse_detector_->FrameProcessingStarted(); int64_t encode_start_time = -1; deliver_cs_->Enter(); if (SwapCapturedAndDeliverFrameIfAvailable()) { + capture_time = deliver_frame_->render_time_ms(); encode_start_time = Clock::GetRealTimeClock()->TimeInMilliseconds(); - DeliverI420Frame(&deliver_frame_); + DeliverI420Frame(deliver_frame_.get()); + if (deliver_frame_->native_handle() != NULL) + deliver_frame_.reset(); // Release the texture so it can be reused. } deliver_cs_->Leave(); if (current_brightness_level_ != reported_brightness_level_) { @@ -576,24 +488,27 @@ } } // We're done! + if (capture_time != -1) { + overuse_detector_->FrameSent(capture_time); + } return true; } void ViECapturer::DeliverI420Frame(I420VideoFrame* video_frame) { + if (video_frame->native_handle() != NULL) { + ViEFrameProviderBase::DeliverFrame(video_frame); + return; + } + // Apply image enhancement and effect filter. if (deflicker_frame_stats_) { if (image_proc_module_->GetFrameStats(deflicker_frame_stats_, *video_frame) == 0) { image_proc_module_->Deflickering(video_frame, deflicker_frame_stats_); } else { - WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, capture_id_), - "%s: could not get frame stats for captured frame", - __FUNCTION__); + LOG_F(LS_ERROR) << "Could not get frame stats."; } } - if (denoising_enabled_) { - image_proc_module_->Denoising(video_frame); - } if (brightness_frame_stats_) { if (image_proc_module_->GetFrameStats(brightness_frame_stats_, *video_frame) == 0) { @@ -611,8 +526,7 @@ current_brightness_level_ = Bright; break; default: - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, capture_id_), - "%s: Brightness detection failed", __FUNCTION__); + break; } } } @@ -620,10 +534,13 @@ unsigned int length = CalcBufferSize(kI420, video_frame->width(), video_frame->height()); - scoped_array video_buffer(new uint8_t[length]); + scoped_ptr video_buffer(new uint8_t[length]); ExtractBuffer(*video_frame, length, video_buffer.get()); - effect_filter_->Transform(length, video_buffer.get(), - video_frame->timestamp(), video_frame->width(), + effect_filter_->Transform(length, + video_buffer.get(), + video_frame->ntp_time_ms(), + video_frame->timestamp(), + video_frame->width(), video_frame->height()); } // Deliver the captured frame to all observers (channels, renderer or file). @@ -651,12 +568,7 @@ { CriticalSectionScoped cs(observer_cs_.get()); if (observer_) { - WEBRTC_TRACE(kTraceError, - kTraceVideo, - ViEId(engine_id_, capture_id_), - "%s Observer already registered", - __FUNCTION__, - capture_id_); + LOG_F(LS_ERROR) << "Observer already registered."; return -1; } observer_ = observer; @@ -684,17 +596,13 @@ void ViECapturer::OnCaptureFrameRate(const int32_t id, const uint32_t frame_rate) { - WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, capture_id_), - "OnCaptureFrameRate %d", frame_rate); - CriticalSectionScoped cs(observer_cs_.get()); observer_->CapturedFrameRate(id_, static_cast(frame_rate)); } void ViECapturer::OnNoPictureAlarm(const int32_t id, const VideoCaptureAlarm alarm) { - WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, capture_id_), - "OnNoPictureAlarm %d", alarm); + LOG(LS_WARNING) << "OnNoPictureAlarm " << id; CriticalSectionScoped cs(observer_cs_.get()); CaptureAlarm vie_alarm = (alarm == Raised) ? AlarmRaised : AlarmCleared; @@ -703,11 +611,21 @@ bool ViECapturer::SwapCapturedAndDeliverFrameIfAvailable() { CriticalSectionScoped cs(capture_cs_.get()); - if (captured_frame_.IsZeroSize()) + if (captured_frame_ == NULL) + return false; + + if (captured_frame_->native_handle() != NULL) { + deliver_frame_.reset(captured_frame_.release()); + return true; + } + + if (captured_frame_->IsZeroSize()) return false; - deliver_frame_.SwapFrame(&captured_frame_); - captured_frame_.ResetSize(); + if (deliver_frame_ == NULL) + deliver_frame_.reset(new I420VideoFrame()); + deliver_frame_->SwapFrame(captured_frame_.get()); + captured_frame_->ResetSize(); return true; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_capturer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_capturer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_capturer.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_capturer.h 2015-02-03 14:33:38.000000000 +0000 @@ -13,6 +13,7 @@ #include +#include "webrtc/base/thread_annotations.h" #include "webrtc/common_types.h" #include "webrtc/engine_configurations.h" #include "webrtc/modules/video_capture/include/video_capture.h" @@ -20,8 +21,8 @@ #include "webrtc/modules/video_coding/main/interface/video_coding.h" #include "webrtc/modules/video_processing/main/interface/video_processing.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/system_wrappers/interface/thread_annotations.h" #include "webrtc/typedefs.h" +#include "webrtc/video_engine/include/vie_base.h" #include "webrtc/video_engine/include/vie_capture.h" #include "webrtc/video_engine/vie_defines.h" #include "webrtc/video_engine/vie_frame_provider_base.h" @@ -94,7 +95,6 @@ // Effect filter. int32_t RegisterEffectFilter(ViEEffectFilter* effect_filter); - int32_t EnableDenoising(bool enable); int32_t EnableDeflickering(bool enable); int32_t EnableBrightnessAlarm(bool enable); @@ -107,11 +107,8 @@ const char* CurrentDeviceName() const; void RegisterCpuOveruseObserver(CpuOveruseObserver* observer); - - void CpuOveruseMeasures(int* capture_jitter_ms, - int* avg_encode_time_ms, - int* encode_usage_percent, - int* capture_queue_delay_ms_per_s) const; + void SetCpuOveruseOptions(const CpuOveruseOptions& options); + void GetCpuOveruseMetrics(CpuOveruseMetrics* metrics) const; protected: ViECapturer(int capture_id, @@ -172,8 +169,8 @@ EventWrapper& capture_event_; EventWrapper& deliver_event_; - I420VideoFrame captured_frame_; - I420VideoFrame deliver_frame_; + scoped_ptr captured_frame_; + scoped_ptr deliver_frame_; // Image processing. ViEEffectFilter* effect_filter_; @@ -183,7 +180,6 @@ VideoProcessingModule::FrameStats* brightness_frame_stats_; Brightness current_brightness_level_; Brightness reported_brightness_level_; - bool denoising_enabled_; // Statistics observer. scoped_ptr observer_cs_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_capturer_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_capturer_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_capturer_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_capturer_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file includes unit tests for ViECapturer. + +#include "webrtc/video_engine/vie_capturer.h" + +#include + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/common.h" +#include "webrtc/common_video/interface/native_handle.h" +#include "webrtc/common_video/interface/texture_video_frame.h" +#include "webrtc/modules/utility/interface/mock/mock_process_thread.h" +#include "webrtc/modules/video_capture/include/mock/mock_video_capture.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/event_wrapper.h" +#include "webrtc/system_wrappers/interface/ref_count.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/system_wrappers/interface/scoped_vector.h" +#include "webrtc/video_engine/mock/mock_vie_frame_provider_base.h" + +using ::testing::_; +using ::testing::Invoke; +using ::testing::NiceMock; +using ::testing::Return; +using ::testing::WithArg; + +// If an output frame does not arrive in 500ms, the test will fail. +#define FRAME_TIMEOUT_MS 500 + +namespace webrtc { + +bool EqualFrames(const I420VideoFrame& frame1, + const I420VideoFrame& frame2); +bool EqualTextureFrames(const I420VideoFrame& frame1, + const I420VideoFrame& frame2); +bool EqualBufferFrames(const I420VideoFrame& frame1, + const I420VideoFrame& frame2); +bool EqualFramesVector(const ScopedVector& frames1, + const ScopedVector& frames2); +I420VideoFrame* CreateI420VideoFrame(uint8_t length); + +class FakeNativeHandle : public NativeHandle { + public: + FakeNativeHandle() {} + virtual ~FakeNativeHandle() {} + virtual void* GetHandle() { return NULL; } +}; + +class ViECapturerTest : public ::testing::Test { + protected: + ViECapturerTest() + : mock_capture_module_(new NiceMock()), + mock_process_thread_(new NiceMock), + mock_frame_callback_(new NiceMock), + data_callback_(NULL), + output_frame_event_(EventWrapper::Create()) { + } + + virtual void SetUp() { + EXPECT_CALL(*mock_capture_module_, RegisterCaptureDataCallback(_)) + .WillRepeatedly(Invoke(this, &ViECapturerTest::SetCaptureDataCallback)); + EXPECT_CALL(*mock_frame_callback_, DeliverFrame(_, _, _, _)) + .WillRepeatedly( + WithArg<1>(Invoke(this, &ViECapturerTest::AddOutputFrame))); + + Config config; + vie_capturer_.reset( + ViECapturer::CreateViECapture( + 0, 0, config, mock_capture_module_.get(), *mock_process_thread_)); + vie_capturer_->RegisterFrameCallback(0, mock_frame_callback_.get()); + } + + virtual void TearDown() { + // ViECapturer accesses |mock_process_thread_| in destructor and should + // be deleted first. + vie_capturer_.reset(); + } + + void SetCaptureDataCallback(VideoCaptureDataCallback& data_callback) { + data_callback_ = &data_callback; + } + + void AddInputFrame(I420VideoFrame* frame) { + data_callback_->OnIncomingCapturedFrame(0, *frame); + } + + void AddOutputFrame(I420VideoFrame* frame) { + if (frame->native_handle() == NULL) + output_frame_ybuffers_.push_back(frame->buffer(kYPlane)); + // Clone the frames because ViECapturer owns the frames. + output_frames_.push_back(frame->CloneFrame()); + output_frame_event_->Set(); + } + + void WaitOutputFrame() { + EXPECT_EQ(kEventSignaled, output_frame_event_->Wait(FRAME_TIMEOUT_MS)); + } + + scoped_ptr mock_capture_module_; + scoped_ptr mock_process_thread_; + scoped_ptr mock_frame_callback_; + + // Used to send input capture frames to ViECapturer. + VideoCaptureDataCallback* data_callback_; + + scoped_ptr vie_capturer_; + + // Input capture frames of ViECapturer. + ScopedVector input_frames_; + + // Indicate an output frame has arrived. + scoped_ptr output_frame_event_; + + // Output delivered frames of ViECaptuer. + ScopedVector output_frames_; + + // The pointers of Y plane buffers of output frames. This is used to verify + // the frame are swapped and not copied. + std::vector output_frame_ybuffers_; +}; + +TEST_F(ViECapturerTest, TestTextureFrames) { + const int kNumFrame = 3; + for (int i = 0 ; i < kNumFrame; ++i) { + webrtc::RefCountImpl* handle = + new webrtc::RefCountImpl(); + input_frames_.push_back(new TextureVideoFrame(handle, i, i, i, i)); + AddInputFrame(input_frames_[i]); + WaitOutputFrame(); + } + + EXPECT_TRUE(EqualFramesVector(input_frames_, output_frames_)); +} + +TEST_F(ViECapturerTest, TestI420Frames) { + const int kNumFrame = 4; + ScopedVector copied_input_frames; + std::vector ybuffer_pointers; + for (int i = 0; i < kNumFrame; ++i) { + input_frames_.push_back(CreateI420VideoFrame(static_cast(i + 1))); + ybuffer_pointers.push_back(input_frames_[i]->buffer(kYPlane)); + // Copy input frames because the buffer data will be swapped. + copied_input_frames.push_back(input_frames_[i]->CloneFrame()); + AddInputFrame(input_frames_[i]); + WaitOutputFrame(); + } + + EXPECT_TRUE(EqualFramesVector(copied_input_frames, output_frames_)); + // Make sure the buffer is swapped and not copied. + for (int i = 0; i < kNumFrame; ++i) + EXPECT_EQ(ybuffer_pointers[i], output_frame_ybuffers_[i]); + // The pipeline should be filled with frames with allocated buffers. Check + // the last input frame has the same allocated size after swapping. + EXPECT_EQ(input_frames_.back()->allocated_size(kYPlane), + copied_input_frames.back()->allocated_size(kYPlane)); +} + +TEST_F(ViECapturerTest, TestI420FrameAfterTextureFrame) { + webrtc::RefCountImpl* handle = + new webrtc::RefCountImpl(); + input_frames_.push_back(new TextureVideoFrame(handle, 1, 1, 1, 1)); + AddInputFrame(input_frames_[0]); + WaitOutputFrame(); + + input_frames_.push_back(CreateI420VideoFrame(1)); + scoped_ptr copied_input_frame(input_frames_[1]->CloneFrame()); + AddInputFrame(copied_input_frame.get()); + WaitOutputFrame(); + + EXPECT_TRUE(EqualFramesVector(input_frames_, output_frames_)); +} + +TEST_F(ViECapturerTest, TestTextureFrameAfterI420Frame) { + input_frames_.push_back(CreateI420VideoFrame(1)); + scoped_ptr copied_input_frame(input_frames_[0]->CloneFrame()); + AddInputFrame(copied_input_frame.get()); + WaitOutputFrame(); + + webrtc::RefCountImpl* handle = + new webrtc::RefCountImpl(); + input_frames_.push_back(new TextureVideoFrame(handle, 1, 1, 1, 1)); + AddInputFrame(input_frames_[1]); + WaitOutputFrame(); + + EXPECT_TRUE(EqualFramesVector(input_frames_, output_frames_)); +} + +bool EqualFrames(const I420VideoFrame& frame1, + const I420VideoFrame& frame2) { + if (frame1.native_handle() != NULL || frame2.native_handle() != NULL) + return EqualTextureFrames(frame1, frame2); + return EqualBufferFrames(frame1, frame2); +} + +bool EqualTextureFrames(const I420VideoFrame& frame1, + const I420VideoFrame& frame2) { + return ((frame1.native_handle() == frame2.native_handle()) && + (frame1.width() == frame2.width()) && + (frame1.height() == frame2.height()) && + (frame1.timestamp() == frame2.timestamp()) && + (frame1.render_time_ms() == frame2.render_time_ms())); +} + +bool EqualBufferFrames(const I420VideoFrame& frame1, + const I420VideoFrame& frame2) { + return ((frame1.width() == frame2.width()) && + (frame1.height() == frame2.height()) && + (frame1.stride(kYPlane) == frame2.stride(kYPlane)) && + (frame1.stride(kUPlane) == frame2.stride(kUPlane)) && + (frame1.stride(kVPlane) == frame2.stride(kVPlane)) && + (frame1.timestamp() == frame2.timestamp()) && + (frame1.ntp_time_ms() == frame2.ntp_time_ms()) && + (frame1.render_time_ms() == frame2.render_time_ms()) && + (frame1.allocated_size(kYPlane) == frame2.allocated_size(kYPlane)) && + (frame1.allocated_size(kUPlane) == frame2.allocated_size(kUPlane)) && + (frame1.allocated_size(kVPlane) == frame2.allocated_size(kVPlane)) && + (memcmp(frame1.buffer(kYPlane), frame2.buffer(kYPlane), + frame1.allocated_size(kYPlane)) == 0) && + (memcmp(frame1.buffer(kUPlane), frame2.buffer(kUPlane), + frame1.allocated_size(kUPlane)) == 0) && + (memcmp(frame1.buffer(kVPlane), frame2.buffer(kVPlane), + frame1.allocated_size(kVPlane)) == 0)); +} + +bool EqualFramesVector(const ScopedVector& frames1, + const ScopedVector& frames2) { + if (frames1.size() != frames2.size()) + return false; + for (size_t i = 0; i < frames1.size(); ++i) { + if (!EqualFrames(*frames1[i], *frames2[i])) + return false; + } + return true; +} + +I420VideoFrame* CreateI420VideoFrame(uint8_t data) { + I420VideoFrame* frame = new I420VideoFrame(); + const int width = 36; + const int height = 24; + const int kSizeY = width * height * 2; + const int kSizeUV = width * height; + uint8_t buffer[kSizeY]; + memset(buffer, data, kSizeY); + frame->CreateFrame( + kSizeY, buffer, kSizeUV, buffer, kSizeUV, buffer, width, height, width, + width / 2, width / 2); + frame->set_timestamp(data); + frame->set_ntp_time_ms(data); + frame->set_render_time_ms(data); + return frame; +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel.cc 2015-02-03 14:33:38.000000000 +0000 @@ -24,8 +24,9 @@ #include "webrtc/modules/video_processing/main/interface/video_processing.h" #include "webrtc/modules/video_render/include/video_render_defines.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/system_wrappers/interface/metrics.h" #include "webrtc/system_wrappers/interface/thread_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" #include "webrtc/video_engine/call_stats.h" #include "webrtc/video_engine/include/vie_codec.h" #include "webrtc/video_engine/include/vie_errors.h" @@ -42,6 +43,50 @@ static const int kMaxTargetDelayMs = 10000; static const float kMaxIncompleteTimeMultiplier = 3.5f; +namespace { + +RTCPReportBlock AggregateReportBlocks( + const std::vector& report_blocks, + std::map* prev_report_blocks) { + int fraction_lost_sum = 0; + int fl_seq_num_sum = 0; + int jitter_sum = 0; + int number_of_report_blocks = 0; + RTCPReportBlock aggregate; + std::vector::const_iterator report_block = + report_blocks.begin(); + for (; report_block != report_blocks.end(); ++report_block) { + aggregate.cumulativeLost += report_block->cumulativeLost; + std::map::iterator prev_report_block = + prev_report_blocks->find(report_block->sourceSSRC); + if (prev_report_block != prev_report_blocks->end()) { + // Skip the first report block since we won't be able to get a correct + // weight for it. + int seq_num_diff = report_block->extendedHighSeqNum - + prev_report_block->second.extendedHighSeqNum; + if (seq_num_diff > 0) { + fraction_lost_sum += report_block->fractionLost * seq_num_diff; + fl_seq_num_sum += seq_num_diff; + } + } + jitter_sum += report_block->jitter; + ++number_of_report_blocks; + (*prev_report_blocks)[report_block->sourceSSRC] = *report_block; + } + if (fl_seq_num_sum > 0) { + aggregate.fractionLost = + (fraction_lost_sum + fl_seq_num_sum / 2) / fl_seq_num_sum; + } + if (number_of_report_blocks > 0) { + aggregate.jitter = + (jitter_sum + number_of_report_blocks / 2) / number_of_report_blocks; + } + // Not well defined for aggregated report blocks. + aggregate.extendedHighSeqNum = 0; + return aggregate; +} +} // namespace + // Helper class receiving statistics callbacks. class ChannelStatsObserver : public CallStatsObserver { public: @@ -77,10 +122,10 @@ callback_cs_(CriticalSectionWrapper::CreateCriticalSection()), rtp_rtcp_cs_(CriticalSectionWrapper::CreateCriticalSection()), default_rtp_rtcp_(default_rtp_rtcp), - vcm_(*VideoCodingModule::Create(ViEModuleId(engine_id, channel_id))), - vie_receiver_(channel_id, &vcm_, remote_bitrate_estimator, this), + vcm_(VideoCodingModule::Create()), + vie_receiver_(channel_id, vcm_, remote_bitrate_estimator, this), vie_sender_(channel_id), - vie_sync_(&vcm_, this), + vie_sync_(vcm_, this), stats_observer_(new ChannelStatsObserver(this)), module_process_thread_(module_process_thread), codec_observer_(NULL), @@ -90,6 +135,7 @@ intra_frame_observer_(intra_frame_observer), rtt_stats_(rtt_stats), paced_sender_(paced_sender), + pad_with_redundant_payloads_(false), bandwidth_observer_(bandwidth_observer), send_timestamp_extension_id_(kInvalidRtpExtensionId), absolute_send_time_extension_id_(kInvalidRtpExtensionId), @@ -104,11 +150,7 @@ nack_history_size_sender_(kSendSidePacketHistorySize), max_nack_reordering_threshold_(kMaxPacketAgeToNack), pre_render_callback_(NULL), - config_(config) { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id, channel_id), - "ViEChannel::ViEChannel(channel_id: %d, engine_id: %d)", - channel_id, engine_id); - + start_ms_(Clock::GetRealTimeClock()->TimeInMilliseconds()) { RtpRtcp::Configuration configuration; configuration.id = ViEModuleId(engine_id, channel_id); configuration.audio = false; @@ -121,104 +163,59 @@ configuration.remote_bitrate_estimator = remote_bitrate_estimator; configuration.paced_sender = paced_sender; configuration.receive_statistics = vie_receiver_.GetReceiveStatistics(); + configuration.send_bitrate_observer = &send_bitrate_observer_; + configuration.send_frame_count_observer = &send_frame_count_observer_; + configuration.send_side_delay_observer = &send_side_delay_observer_; rtp_rtcp_.reset(RtpRtcp::CreateRtpRtcp(configuration)); vie_receiver_.SetRtpRtcpModule(rtp_rtcp_.get()); - vcm_.SetNackSettings(kMaxNackListSize, max_nack_reordering_threshold_, 0); + vcm_->SetNackSettings(kMaxNackListSize, max_nack_reordering_threshold_, 0); } int32_t ViEChannel::Init() { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: channel_id: %d, engine_id: %d)", __FUNCTION__, channel_id_, - engine_id_); - if (module_process_thread_.RegisterModule( vie_receiver_.GetReceiveStatistics()) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Failed to register receive-statistics to process thread", - __FUNCTION__); return -1; } // RTP/RTCP initialization. if (rtp_rtcp_->SetSendingMediaStatus(false) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: RTP::SetSendingMediaStatus failure", __FUNCTION__); return -1; } if (module_process_thread_.RegisterModule(rtp_rtcp_.get()) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: RTP::RegisterModule failure", __FUNCTION__); return -1; } - if (rtp_rtcp_->SetKeyFrameRequestMethod(kKeyFrameReqFirRtp) != 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: RTP::SetKeyFrameRequestMethod failure", __FUNCTION__); - } - if (rtp_rtcp_->SetRTCPStatus(kRtcpCompound) != 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: RTP::SetRTCPStatus failure", __FUNCTION__); - } + rtp_rtcp_->SetKeyFrameRequestMethod(kKeyFrameReqFirRtp); + rtp_rtcp_->SetRTCPStatus(kRtcpCompound); if (paced_sender_) { - if (rtp_rtcp_->SetStorePacketsStatus(true, nack_history_size_sender_) != - 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s:SetStorePacketsStatus failure", __FUNCTION__); - } + rtp_rtcp_->SetStorePacketsStatus(true, nack_history_size_sender_); } - // VCM initialization - if (vcm_.InitializeReceiver() != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s: VCM::InitializeReceiver failure", __FUNCTION__); + if (vcm_->InitializeReceiver() != 0) { return -1; } - if (vcm_.SetVideoProtection(kProtectionKeyOnLoss, true)) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: VCM::SetVideoProtection failure", __FUNCTION__); + if (vcm_->SetVideoProtection(kProtectionKeyOnLoss, true)) { return -1; } - if (vcm_.RegisterReceiveCallback(this) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: VCM::RegisterReceiveCallback failure", __FUNCTION__); + if (vcm_->RegisterReceiveCallback(this) != 0) { return -1; } - if (vcm_.RegisterFrameTypeCallback(this) != 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: VCM::RegisterFrameTypeCallback failure", __FUNCTION__); - } - if (vcm_.RegisterReceiveStateCallback(this) != 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: VCM::RegisterReceiveStateCallback failure", __FUNCTION__); - } - if (vcm_.RegisterReceiveStatisticsCallback(this) != 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: VCM::RegisterReceiveStatisticsCallback failure", - __FUNCTION__); - } - if (vcm_.RegisterDecoderTimingCallback(this) != 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: VCM::RegisterDecoderTimingCallback failure", - __FUNCTION__); - } - if (vcm_.SetRenderDelay(kViEDefaultRenderDelayMs) != 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: VCM::SetRenderDelay failure", __FUNCTION__); - } - if (module_process_thread_.RegisterModule(&vcm_) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: VCM::RegisterModule(vcm) failure", __FUNCTION__); + vcm_->RegisterFrameTypeCallback(this); + vcm_->RegisterReceiveStateCallback(this); + vcm_->RegisterReceiveStatisticsCallback(this); + vcm_->RegisterDecoderTimingCallback(this); + vcm_->SetRenderDelay(kViEDefaultRenderDelayMs); + if (module_process_thread_.RegisterModule(vcm_) != 0) { return -1; } #ifdef VIDEOCODEC_VP8 VideoCodec video_codec; - if (vcm_.Codec(kVideoCodecVP8, &video_codec) == VCM_OK) { + if (vcm_->Codec(kVideoCodecVP8, &video_codec) == VCM_OK) { rtp_rtcp_->RegisterSendPayload(video_codec); // TODO(holmer): Can we call SetReceiveCodec() here instead? if (!vie_receiver_.RegisterPayload(video_codec)) { return -1; } - vcm_.RegisterReceiveCodec(&video_codec, number_of_cores_); - vcm_.RegisterSendCodec(&video_codec, number_of_cores_, + vcm_->RegisterReceiveCodec(&video_codec, number_of_cores_); + vcm_->RegisterSendCodec(&video_codec, number_of_cores_, rtp_rtcp_->MaxDataPayloadLength()); } else { assert(false); @@ -229,14 +226,11 @@ } ViEChannel::~ViEChannel() { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id_, channel_id_), - "ViEChannel Destructor, channel_id: %d, engine_id: %d", - channel_id_, engine_id_); - + UpdateHistograms(); // Make sure we don't get more callbacks from the RTP module. module_process_thread_.DeRegisterModule(vie_receiver_.GetReceiveStatistics()); module_process_thread_.DeRegisterModule(rtp_rtcp_.get()); - module_process_thread_.DeRegisterModule(&vcm_); + module_process_thread_.DeRegisterModule(vcm_); module_process_thread_.DeRegisterModule(&vie_sync_); while (simulcast_rtp_rtcp_.size() > 0) { std::list::iterator it = simulcast_rtp_rtcp_.begin(); @@ -254,27 +248,71 @@ StopDecodeThread(); } // Release modules. - VideoCodingModule::Destroy(&vcm_); + VideoCodingModule::Destroy(vcm_); +} + +void ViEChannel::UpdateHistograms() { + const float kMinCallLengthInMinutes = 0.5f; + float elapsed_minutes = + (Clock::GetRealTimeClock()->TimeInMilliseconds() - start_ms_) / 60000.0f; + if (elapsed_minutes < kMinCallLengthInMinutes) { + return; + } + RtcpPacketTypeCounter rtcp_sent; + RtcpPacketTypeCounter rtcp_received; + GetRtcpPacketTypeCounters(&rtcp_sent, &rtcp_received); + + if (sender_) { + if (rtcp_received.nack_requests > 0) { + RTC_HISTOGRAM_PERCENTAGE( + "WebRTC.Video.UniqueNackRequestsReceivedInPercent", + rtcp_received.UniqueNackRequestsInPercent()); + } + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.NackPacketsReceivedPerMinute", + rtcp_received.nack_packets / elapsed_minutes); + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.FirPacketsReceivedPerMinute", + rtcp_received.fir_packets / elapsed_minutes); + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.PliPacketsReceivedPerMinute", + rtcp_received.pli_packets / elapsed_minutes); + } else if (vie_receiver_.GetRemoteSsrc() > 0) { + // Get receive stats if we are receiving packets, i.e. there is a remote + // ssrc. + if (rtcp_sent.nack_requests > 0) { + RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.UniqueNackRequestsSentInPercent", + rtcp_sent.UniqueNackRequestsInPercent()); + } + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.NackPacketsSentPerMinute", + rtcp_sent.nack_packets / elapsed_minutes); + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.FirPacketsSentPerMinute", + rtcp_sent.fir_packets / elapsed_minutes); + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.PliPacketsSentPerMinute", + rtcp_sent.pli_packets / elapsed_minutes); + + webrtc::VCMFrameCount frames; + if (vcm_->ReceivedFrameCount(frames) == VCM_OK) { + uint32_t total_frames = frames.numKeyFrames + frames.numDeltaFrames; + if (total_frames > 0) { + RTC_HISTOGRAM_COUNTS_1000("WebRTC.Video.KeyFramesReceivedInPermille", + static_cast((frames.numKeyFrames * 1000.0f / total_frames) + + 0.5f)); + } + } + } } int32_t ViEChannel::SetSendCodec(const VideoCodec& video_codec, bool new_stream) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: codec_type: %d", __FUNCTION__, video_codec.codecType); - if (!sender_) { return 0; } if (video_codec.codecType == kVideoCodecRED || video_codec.codecType == kVideoCodecULPFEC) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: codec_type: %d is not a valid send codec.", __FUNCTION__, - video_codec.codecType); + LOG_F(LS_ERROR) << "Not a valid send codec " << video_codec.codecType; return -1; } if (kMaxSimulcastStreams < video_codec.numberOfSimulcastStreams) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Too many simulcast streams", __FUNCTION__); + LOG_F(LS_ERROR) << "Incorrect config " + << video_codec.numberOfSimulcastStreams; return -1; } // Update the RTP module with the settings. @@ -307,35 +345,26 @@ num_modules_to_add = 0; } - while (removed_rtp_rtcp_.size() > 0 && num_modules_to_add > 0) { - RtpRtcp* rtp_rtcp = removed_rtp_rtcp_.front(); + // Add back removed rtp modules. Order is important (allocate from front of + // removed modules) to preserve RTP settings such as SSRCs for simulcast + // streams. + std::list new_rtp_modules; + for (; removed_rtp_rtcp_.size() > 0 && num_modules_to_add > 0; + --num_modules_to_add) { + new_rtp_modules.push_back(removed_rtp_rtcp_.front()); removed_rtp_rtcp_.pop_front(); - simulcast_rtp_rtcp_.push_back(rtp_rtcp); - rtp_rtcp->SetSendingStatus(rtp_rtcp_->Sending()); - rtp_rtcp->SetSendingMediaStatus(rtp_rtcp_->SendingMedia()); - module_process_thread_.RegisterModule(rtp_rtcp); - --num_modules_to_add; } - for (int i = 0; i < num_modules_to_add; ++i) { - RtpRtcp::Configuration configuration; - configuration.id = ViEModuleId(engine_id_, channel_id_); - configuration.audio = false; // Video. - configuration.default_module = default_rtp_rtcp_; - configuration.outgoing_transport = &vie_sender_; - configuration.intra_frame_callback = intra_frame_observer_; - configuration.bandwidth_callback = bandwidth_observer_.get(); - configuration.rtt_stats = rtt_stats_; - configuration.paced_sender = paced_sender_; + for (int i = 0; i < num_modules_to_add; ++i) + new_rtp_modules.push_back(CreateRtpRtcpModule()); - RtpRtcp* rtp_rtcp = RtpRtcp::CreateRtpRtcp(configuration); + // Initialize newly added modules. + for (std::list::iterator it = new_rtp_modules.begin(); + it != new_rtp_modules.end(); + ++it) { + RtpRtcp* rtp_rtcp = *it; - // Silently ignore error. - module_process_thread_.RegisterModule(rtp_rtcp); - if (rtp_rtcp->SetRTCPStatus(rtp_rtcp_->RTCP()) != 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: RTP::SetRTCPStatus failure", __FUNCTION__); - } + rtp_rtcp->SetRTCPStatus(rtp_rtcp_->RTCP()); if (rtp_rtcp_->StorePackets()) { rtp_rtcp->SetStorePacketsStatus(true, nack_history_size_sender_); @@ -344,13 +373,23 @@ } if (fec_enabled) { - rtp_rtcp->SetGenericFECStatus(fec_enabled, payload_type_red, - payload_type_fec); + rtp_rtcp->SetGenericFECStatus( + fec_enabled, payload_type_red, payload_type_fec); } rtp_rtcp->SetSendingStatus(rtp_rtcp_->Sending()); rtp_rtcp->SetSendingMediaStatus(rtp_rtcp_->SendingMedia()); + + int mode; + uint32_t ssrc; + int payload_type; + rtp_rtcp_->RTXSendStatus(&mode, &ssrc, &payload_type); + rtp_rtcp->SetRTXSendStatus(mode); simulcast_rtp_rtcp_.push_back(rtp_rtcp); + + // Silently ignore error. + module_process_thread_.RegisterModule(rtp_rtcp); } + // Remove last in list if we have too many. for (int j = simulcast_rtp_rtcp_.size(); j > (video_codec.numberOfSimulcastStreams - 1); @@ -359,10 +398,8 @@ module_process_thread_.DeRegisterModule(rtp_rtcp); rtp_rtcp->SetSendingStatus(false); rtp_rtcp->SetSendingMediaStatus(false); - rtp_rtcp->RegisterSendFrameCountObserver(NULL); rtp_rtcp->RegisterSendChannelRtcpStatisticsCallback(NULL); rtp_rtcp->RegisterSendChannelRtpStatisticsCallback(NULL); - rtp_rtcp->RegisterVideoBitrateObserver(NULL); simulcast_rtp_rtcp_.pop_back(); removed_rtp_rtcp_.push_front(rtp_rtcp); } @@ -375,8 +412,6 @@ RtpRtcp* rtp_rtcp = *it; rtp_rtcp->DeRegisterSendPayload(video_codec.plType); if (rtp_rtcp->RegisterSendPayload(video_codec) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: could not register payload type", __FUNCTION__); return -1; } if (mtu_ != 0) { @@ -393,9 +428,6 @@ if (rtp_rtcp->RegisterSendRtpHeaderExtension( kRtpExtensionTransmissionTimeOffset, send_timestamp_extension_id_) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: could not register transmission time extension", - __FUNCTION__); } } else { rtp_rtcp->DeregisterSendRtpHeaderExtension( @@ -408,22 +440,15 @@ if (rtp_rtcp->RegisterSendRtpHeaderExtension( kRtpExtensionAbsoluteSendTime, absolute_send_time_extension_id_) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: could not register absolute send time extension", - __FUNCTION__); } } else { rtp_rtcp->DeregisterSendRtpHeaderExtension( kRtpExtensionAbsoluteSendTime); } - rtp_rtcp->RegisterSendFrameCountObserver( - rtp_rtcp_->GetSendFrameCountObserver()); rtp_rtcp->RegisterSendChannelRtcpStatisticsCallback( rtp_rtcp_->GetSendChannelRtcpStatisticsCallback()); rtp_rtcp->RegisterSendChannelRtpStatisticsCallback( rtp_rtcp_->GetSendChannelRtpStatisticsCallback()); - rtp_rtcp->RegisterVideoBitrateObserver( - rtp_rtcp_->GetVideoBitrateObserver()); } // |RegisterSimulcastRtpRtcpModules| resets all old weak pointers and old // modules can be deleted after this step. @@ -434,35 +459,19 @@ module_process_thread_.DeRegisterModule(rtp_rtcp); rtp_rtcp->SetSendingStatus(false); rtp_rtcp->SetSendingMediaStatus(false); - rtp_rtcp->RegisterSendFrameCountObserver(NULL); rtp_rtcp->RegisterSendChannelRtcpStatisticsCallback(NULL); rtp_rtcp->RegisterSendChannelRtpStatisticsCallback(NULL); - rtp_rtcp->RegisterVideoBitrateObserver(NULL); simulcast_rtp_rtcp_.pop_back(); removed_rtp_rtcp_.push_front(rtp_rtcp); } // Clear any previous modules. vie_receiver_.RegisterSimulcastRtpRtcpModules(simulcast_rtp_rtcp_); } - // Enable this if H264 is available. - // This sets the wanted packetization mode. - // if (video_codec.plType == kVideoCodecH264) { - // if (video_codec.codecSpecific.H264.packetization == kH264SingleMode) { - // rtp_rtcp_->SetH264PacketizationMode(H264_SINGLE_NAL_MODE); - // } else { - // rtp_rtcp_->SetH264PacketizationMode(H264_NON_INTERLEAVED_MODE); - // } - // if (video_codec.codecSpecific.H264.configParametersSize > 0) { - // rtp_rtcp_->SetH264SendModeNALU_PPS_SPS(true); - // } - // } // Don't log this error, no way to check in advance if this pl_type is // registered or not... rtp_rtcp_->DeRegisterSendPayload(video_codec.plType); if (rtp_rtcp_->RegisterSendPayload(video_codec) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: could not register payload type", __FUNCTION__); return -1; } if (restart_rtp) { @@ -477,23 +486,15 @@ } int32_t ViEChannel::SetReceiveCodec(const VideoCodec& video_codec) { - // We will not receive simulcast streams, so no need to handle that use case. - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s", __FUNCTION__); - if (!vie_receiver_.SetReceiveCodec(video_codec)) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not register receive payload type", __FUNCTION__); return -1; } if (video_codec.codecType != kVideoCodecRED && video_codec.codecType != kVideoCodecULPFEC) { // Register codec type with VCM, but do not register RED or ULPFEC. - if (vcm_.RegisterReceiveCodec(&video_codec, number_of_cores_, + if (vcm_->RegisterReceiveCodec(&video_codec, number_of_cores_, wait_for_key_frame_) != VCM_OK) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not register decoder", __FUNCTION__); return -1; } } @@ -501,12 +502,7 @@ } int32_t ViEChannel::GetReceiveCodec(VideoCodec* video_codec) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s", __FUNCTION__); - - if (vcm_.ReceiveCodec(video_codec) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not get receive codec", __FUNCTION__); + if (vcm_->ReceiveCodec(video_codec) != 0) { return -1; } return 0; @@ -516,21 +512,11 @@ CriticalSectionScoped cs(callback_cs_.get()); if (observer) { if (codec_observer_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: already added", __FUNCTION__); + LOG_F(LS_ERROR) << "Observer already registered."; return -1; } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: observer added", __FUNCTION__); codec_observer_ = observer; } else { - if (!codec_observer_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: no observer added", __FUNCTION__); - return -1; - } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: observer removed", __FUNCTION__); codec_observer_ = NULL; } return 0; @@ -540,48 +526,33 @@ VideoDecoder* decoder, bool buffered_rendering, int32_t render_delay) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s", __FUNCTION__); - int32_t result; - result = vcm_.RegisterExternalDecoder(decoder, pl_type, buffered_rendering); + result = vcm_->RegisterExternalDecoder(decoder, pl_type, buffered_rendering); if (result != VCM_OK) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not register external decoder with VCM.", - __FUNCTION__); return result; } - return vcm_.SetRenderDelay(render_delay); + return vcm_->SetRenderDelay(render_delay); } -int32_t ViEChannel::DeRegisterExternalDecoder( - const uint8_t pl_type) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s pl_type", __FUNCTION__, pl_type); - +int32_t ViEChannel::DeRegisterExternalDecoder(const uint8_t pl_type) { VideoCodec current_receive_codec; int32_t result = 0; - result = vcm_.ReceiveCodec(¤t_receive_codec); - if (vcm_.RegisterExternalDecoder(NULL, pl_type, false) != VCM_OK) { + result = vcm_->ReceiveCodec(¤t_receive_codec); + if (vcm_->RegisterExternalDecoder(NULL, pl_type, false) != VCM_OK) { return -1; } if (result == 0 && current_receive_codec.plType == pl_type) { - result = vcm_.RegisterReceiveCodec(¤t_receive_codec, number_of_cores_, - wait_for_key_frame_); + result = vcm_->RegisterReceiveCodec( + ¤t_receive_codec, number_of_cores_, wait_for_key_frame_); } return result; } -int32_t ViEChannel::ReceiveCodecStatistics( - uint32_t* num_key_frames, uint32_t* num_delta_frames) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s", __FUNCTION__); - +int32_t ViEChannel::ReceiveCodecStatistics(uint32_t* num_key_frames, + uint32_t* num_delta_frames) { VCMFrameCount received_frames; - if (vcm_.ReceivedFrameCount(received_frames) != VCM_OK) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not get received frame information", __FUNCTION__); + if (vcm_->ReceivedFrameCount(received_frames) != VCM_OK) { return -1; } *num_key_frames = received_frames.numKeyFrames; @@ -590,53 +561,40 @@ } uint32_t ViEChannel::DiscardedPackets() const { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); - return vcm_.DiscardedPackets(); + return vcm_->DiscardedPackets(); } int ViEChannel::ReceiveDelay() const { - return vcm_.Delay(); + return vcm_->Delay(); } int32_t ViEChannel::WaitForKeyFrame(bool wait) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s(wait: %d)", __FUNCTION__, wait); wait_for_key_frame_ = wait; return 0; } int32_t ViEChannel::SetSignalPacketLossStatus(bool enable, bool only_key_frames) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s(enable: %d)", __FUNCTION__, enable); if (enable) { if (only_key_frames) { - vcm_.SetVideoProtection(kProtectionKeyOnLoss, false); - if (vcm_.SetVideoProtection(kProtectionKeyOnKeyLoss, true) != VCM_OK) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s failed %d", __FUNCTION__, enable); + vcm_->SetVideoProtection(kProtectionKeyOnLoss, false); + if (vcm_->SetVideoProtection(kProtectionKeyOnKeyLoss, true) != VCM_OK) { return -1; } } else { - vcm_.SetVideoProtection(kProtectionKeyOnKeyLoss, false); - if (vcm_.SetVideoProtection(kProtectionKeyOnLoss, true) != VCM_OK) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s failed %d", __FUNCTION__, enable); + vcm_->SetVideoProtection(kProtectionKeyOnKeyLoss, false); + if (vcm_->SetVideoProtection(kProtectionKeyOnLoss, true) != VCM_OK) { return -1; } } } else { - vcm_.SetVideoProtection(kProtectionKeyOnLoss, false); - vcm_.SetVideoProtection(kProtectionKeyOnKeyLoss, false); + vcm_->SetVideoProtection(kProtectionKeyOnLoss, false); + vcm_->SetVideoProtection(kProtectionKeyOnKeyLoss, false); } return 0; } int32_t ViEChannel::SetRTCPMode(const RTCPMethod rtcp_mode) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: %d", __FUNCTION__, rtcp_mode); - CriticalSectionScoped cs(rtp_rtcp_cs_.get()); for (std::list::iterator it = simulcast_rtp_rtcp_.begin(); it != simulcast_rtp_rtcp_.end(); @@ -648,21 +606,13 @@ } int32_t ViEChannel::GetRTCPMode(RTCPMethod* rtcp_mode) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s", __FUNCTION__); *rtcp_mode = rtp_rtcp_->RTCP(); return 0; } int32_t ViEChannel::SetNACKStatus(const bool enable) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s(enable: %d)", __FUNCTION__, enable); - // Update the decoding VCM. - if (vcm_.SetVideoProtection(kProtectionNack, enable) != VCM_OK) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not set VCM NACK protection: %d", __FUNCTION__, - enable); + if (vcm_->SetVideoProtection(kProtectionNack, enable) != VCM_OK) { return -1; } if (enable) { @@ -670,33 +620,21 @@ SetFECStatus(false, 0, 0); } // Update the decoding VCM. - if (vcm_.SetVideoProtection(kProtectionNack, enable) != VCM_OK) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not set VCM NACK protection: %d", __FUNCTION__, - enable); + if (vcm_->SetVideoProtection(kProtectionNack, enable) != VCM_OK) { return -1; } return ProcessNACKRequest(enable); } int32_t ViEChannel::ProcessNACKRequest(const bool enable) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s(enable: %d)", __FUNCTION__, enable); - if (enable) { // Turn on NACK. - NACKMethod nackMethod = kNackRtcp; if (rtp_rtcp_->RTCP() == kRtcpOff) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not enable NACK, RTPC not on ", __FUNCTION__); return -1; } vie_receiver_.SetNackStatus(true, max_nack_reordering_threshold_); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Using NACK method %d", __FUNCTION__, nackMethod); rtp_rtcp_->SetStorePacketsStatus(true, nack_history_size_sender_); - - vcm_.RegisterPacketRequestCallback(this); + vcm_->RegisterPacketRequestCallback(this); CriticalSectionScoped cs(rtp_rtcp_cs_.get()); @@ -707,7 +645,7 @@ rtp_rtcp->SetStorePacketsStatus(true, nack_history_size_sender_); } // Don't introduce errors when NACK is enabled. - vcm_.SetDecodeErrorMode(kNoErrors); + vcm_->SetDecodeErrorMode(kNoErrors); } else { CriticalSectionScoped cs(rtp_rtcp_cs_.get()); for (std::list::iterator it = simulcast_rtp_rtcp_.begin(); @@ -718,14 +656,14 @@ rtp_rtcp->SetStorePacketsStatus(false, 0); } } - vcm_.RegisterPacketRequestCallback(NULL); + vcm_->RegisterPacketRequestCallback(NULL); if (paced_sender_ == NULL) { rtp_rtcp_->SetStorePacketsStatus(false, 0); } vie_receiver_.SetNackStatus(false, max_nack_reordering_threshold_); // When NACK is off, allow decoding with errors. Otherwise, the video // will freeze, and will only recover with a complete key frame. - vcm_.SetDecodeErrorMode(kWithErrors); + vcm_->SetDecodeErrorMode(kWithErrors); } return 0; } @@ -745,15 +683,8 @@ const bool enable, const unsigned char payload_typeRED, const unsigned char payload_typeFEC) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s(enable: %d, payload_typeRED: %u, payload_typeFEC: %u)", - __FUNCTION__, enable, payload_typeRED, payload_typeFEC); - if (rtp_rtcp_->SetGenericFECStatus(enable, payload_typeRED, payload_typeFEC) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not change FEC status to %d", __FUNCTION__, - enable); return -1; } CriticalSectionScoped cs(rtp_rtcp_cs_.get()); @@ -770,11 +701,7 @@ const bool enable, const unsigned char payload_typeRED, const unsigned char payload_typeFEC) { - // Update the decoding VCM with hybrid mode. - if (vcm_.SetVideoProtection(kProtectionNackFEC, enable) != VCM_OK) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not set VCM NACK protection: %d", __FUNCTION__, - enable); + if (vcm_->SetVideoProtection(kProtectionNackFEC, enable) != VCM_OK) { return -1; } @@ -788,9 +715,7 @@ int ViEChannel::SetSenderBufferingMode(int target_delay_ms) { if ((target_delay_ms < 0) || (target_delay_ms > kMaxTargetDelayMs)) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Target sender buffering delay out of bounds: %d", - __FUNCTION__, target_delay_ms); + LOG(LS_ERROR) << "Invalid send buffer value."; return -1; } if (target_delay_ms == 0) { @@ -803,16 +728,7 @@ nack_history_size_sender_ = kSendSidePacketHistorySize; } } - // Setting nack_history_size_. - // First disabling (forcing free) and then resetting to desired value. - if (rtp_rtcp_->SetStorePacketsStatus(false, 0) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s:SetStorePacketsStatus failure", __FUNCTION__); - return -1; - } if (rtp_rtcp_->SetStorePacketsStatus(true, nack_history_size_sender_) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s:SetStorePacketsStatus failure", __FUNCTION__); return -1; } return 0; @@ -820,9 +736,7 @@ int ViEChannel::SetReceiverBufferingMode(int target_delay_ms) { if ((target_delay_ms < 0) || (target_delay_ms > kMaxTargetDelayMs)) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Target receiver buffering delay out of bounds: %d", - __FUNCTION__, target_delay_ms); + LOG(LS_ERROR) << "Invalid receive buffer delay value."; return -1; } int max_nack_list_size; @@ -839,9 +753,9 @@ max_incomplete_time_ms = static_cast(kMaxIncompleteTimeMultiplier * target_delay_ms + 0.5f); } - vcm_.SetNackSettings(max_nack_list_size, max_nack_reordering_threshold_, + vcm_->SetNackSettings(max_nack_list_size, max_nack_reordering_threshold_, max_incomplete_time_ms); - vcm_.SetMinReceiverDelay(target_delay_ms); + vcm_->SetMinReceiverDelay(target_delay_ms); if (vie_sync_.SetTargetBufferingDelay(target_delay_ms) < 0) return -1; return 0; @@ -856,14 +770,10 @@ int32_t ViEChannel::SetKeyFrameRequestMethod( const KeyFrameRequestMethod method) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: %d", __FUNCTION__, method); return rtp_rtcp_->SetKeyFrameRequestMethod(method); } bool ViEChannel::EnableRemb(bool enable) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "ViEChannel::EnableRemb: %d", enable); if (rtp_rtcp_->SetREMBStatus(enable) != 0) return false; return true; @@ -950,19 +860,14 @@ } int32_t ViEChannel::EnableTMMBR(const bool enable) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: %d", __FUNCTION__, enable); return rtp_rtcp_->SetTMMBRStatus(enable); } int32_t ViEChannel::EnableKeyFrameRequestCallback(const bool enable) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: %d", __FUNCTION__, enable); CriticalSectionScoped cs(callback_cs_.get()); if (enable && !codec_observer_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: No ViECodecObserver set", __FUNCTION__, enable); + LOG(LS_ERROR) << "No ViECodecObserver set."; return -1; } do_key_frame_callbackRequest_ = enable; @@ -972,153 +877,126 @@ int32_t ViEChannel::SetSSRC(const uint32_t SSRC, const StreamType usage, const uint8_t simulcast_idx) { - WEBRTC_TRACE(webrtc::kTraceInfo, - webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s(usage:%d, SSRC: 0x%x, idx:%u)", - __FUNCTION__, usage, SSRC, simulcast_idx); - int rtx_settings = kRtxRetransmitted; - if (config_.Get().redundant_payloads) - rtx_settings |= kRtxRedundantPayloads; - if (simulcast_idx == 0) { - if (usage == kViEStreamTypeRtx) { - return rtp_rtcp_->SetRTXSendStatus(rtx_settings, true, SSRC); - } - return rtp_rtcp_->SetSSRC(SSRC); - } CriticalSectionScoped cs(rtp_rtcp_cs_.get()); - if (simulcast_idx > simulcast_rtp_rtcp_.size()) { - return -1; - } - std::list::const_iterator it = simulcast_rtp_rtcp_.begin(); - for (int i = 1; i < simulcast_idx; ++i, ++it) { - if (it == simulcast_rtp_rtcp_.end()) { - return -1; - } - } - RtpRtcp* rtp_rtcp_module = *it; + ReserveRtpRtcpModules(simulcast_idx + 1); + RtpRtcp* rtp_rtcp = GetRtpRtcpModule(simulcast_idx); + if (rtp_rtcp == NULL) + return -1; if (usage == kViEStreamTypeRtx) { - return rtp_rtcp_module->SetRTXSendStatus(rtx_settings, true, SSRC); + rtp_rtcp->SetRtxSsrc(SSRC); + } else { + rtp_rtcp->SetSSRC(SSRC); } - return rtp_rtcp_module->SetSSRC(SSRC); + return 0; } int32_t ViEChannel::SetRemoteSSRCType(const StreamType usage, const uint32_t SSRC) { - WEBRTC_TRACE(webrtc::kTraceInfo, - webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s(usage:%d, SSRC: 0x%x)", - __FUNCTION__, usage, SSRC); - - vie_receiver_.SetRtxStatus(true, SSRC); + vie_receiver_.SetRtxSsrc(SSRC); return 0; } -// TODO(mflodman) Add kViEStreamTypeRtx. int32_t ViEChannel::GetLocalSSRC(uint8_t idx, unsigned int* ssrc) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s", __FUNCTION__); - - if (idx == 0) { - *ssrc = rtp_rtcp_->SSRC(); - return 0; - } CriticalSectionScoped cs(rtp_rtcp_cs_.get()); - if (idx > simulcast_rtp_rtcp_.size()) { + RtpRtcp* rtp_rtcp = GetRtpRtcpModule(idx); + if (rtp_rtcp == NULL) return -1; - } - std::list::const_iterator it = simulcast_rtp_rtcp_.begin(); - for (int i = 1; i < idx; ++i, ++it) { - if (it == simulcast_rtp_rtcp_.end()) { - return -1; - } - } - *ssrc = (*it)->SSRC(); + *ssrc = rtp_rtcp->SSRC(); return 0; } int32_t ViEChannel::GetRemoteSSRC(uint32_t* ssrc) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); - *ssrc = vie_receiver_.GetRemoteSsrc(); return 0; } int32_t ViEChannel::GetRemoteCSRC(uint32_t CSRCs[kRtpCsrcSize]) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); - uint32_t arrayCSRC[kRtpCsrcSize]; memset(arrayCSRC, 0, sizeof(arrayCSRC)); int num_csrcs = vie_receiver_.GetCsrcs(arrayCSRC); if (num_csrcs > 0) { memcpy(CSRCs, arrayCSRC, num_csrcs * sizeof(uint32_t)); - for (int idx = 0; idx < num_csrcs; idx++) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "\tCSRC[%d] = %lu", idx, CSRCs[idx]); - } - } else { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: CSRC list is empty", __FUNCTION__); } return 0; } -int ViEChannel::SetRtxSendPayloadType(int payload_type) { - if (rtp_rtcp_->Sending()) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: already sending", __FUNCTION__); - return -1; +void ViEChannel::SetPadWithRedundantPayloads(bool enable) { + { + CriticalSectionScoped cs(callback_cs_.get()); + pad_with_redundant_payloads_ = enable; + } + int mode; + uint32_t ssrc; + int payload_type; + rtp_rtcp_->RTXSendStatus(&mode, &ssrc, &payload_type); + if (mode != kRtxOff) { + // Since RTX was already enabled we have to reset it with payload-based + // padding on. + SetRtxSendStatus(true); } +} + +int ViEChannel::SetRtxSendPayloadType(int payload_type) { rtp_rtcp_->SetRtxSendPayloadType(payload_type); - CriticalSectionScoped cs(rtp_rtcp_cs_.get()); for (std::list::iterator it = simulcast_rtp_rtcp_.begin(); it != simulcast_rtp_rtcp_.end(); it++) { (*it)->SetRtxSendPayloadType(payload_type); } + SetRtxSendStatus(true); return 0; } +void ViEChannel::SetRtxSendStatus(bool enable) { + int rtx_settings = kRtxOff; + if (enable) { + CriticalSectionScoped cs(callback_cs_.get()); + rtx_settings = kRtxRetransmitted; + if (pad_with_redundant_payloads_) + rtx_settings |= kRtxRedundantPayloads; + } + rtp_rtcp_->SetRTXSendStatus(rtx_settings); + CriticalSectionScoped cs(rtp_rtcp_cs_.get()); + for (std::list::iterator it = simulcast_rtp_rtcp_.begin(); + it != simulcast_rtp_rtcp_.end(); it++) { + (*it)->SetRTXSendStatus(rtx_settings); + } +} + void ViEChannel::SetRtxReceivePayloadType(int payload_type) { vie_receiver_.SetRtxPayloadType(payload_type); } int32_t ViEChannel::SetStartSequenceNumber(uint16_t sequence_number) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); - if (rtp_rtcp_->Sending()) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: already sending", __FUNCTION__); return -1; } return rtp_rtcp_->SetSequenceNumber(sequence_number); } +void ViEChannel::SetRtpStateForSsrc(uint32_t ssrc, const RtpState& rtp_state) { + assert(!rtp_rtcp_->Sending()); + default_rtp_rtcp_->SetRtpStateForSsrc(ssrc, rtp_state); +} + +RtpState ViEChannel::GetRtpStateForSsrc(uint32_t ssrc) { + assert(!rtp_rtcp_->Sending()); + + RtpState rtp_state; + if (!default_rtp_rtcp_->GetRtpStateForSsrc(ssrc, &rtp_state)) { + LOG(LS_ERROR) << "Couldn't get RTP state for ssrc: " << ssrc; + } + return rtp_state; +} + int32_t ViEChannel::SetRTCPCName(const char rtcp_cname[]) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s", __FUNCTION__); if (rtp_rtcp_->Sending()) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: already sending", __FUNCTION__); return -1; } return rtp_rtcp_->SetCNAME(rtcp_cname); } -int32_t ViEChannel::GetRTCPCName(char rtcp_cname[]) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s", __FUNCTION__); - return rtp_rtcp_->CNAME(rtcp_cname); -} - int32_t ViEChannel::GetRemoteRTCPCName(char rtcp_cname[]) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); - uint32_t remoteSSRC = vie_receiver_.GetRemoteSsrc(); return rtp_rtcp_->RemoteCNAME(remoteSSRC, rtcp_cname); } @@ -1127,21 +1005,11 @@ CriticalSectionScoped cs(callback_cs_.get()); if (observer) { if (rtp_observer_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: observer alread added", __FUNCTION__); + LOG_F(LS_ERROR) << "Observer already registered."; return -1; } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: observer added", __FUNCTION__); rtp_observer_ = observer; } else { - if (!rtp_observer_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: no observer added", __FUNCTION__); - return -1; - } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: observer removed", __FUNCTION__); rtp_observer_ = NULL; } return 0; @@ -1151,21 +1019,11 @@ CriticalSectionScoped cs(callback_cs_.get()); if (observer) { if (rtcp_observer_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: observer alread added", __FUNCTION__); + LOG_F(LS_ERROR) << "Observer already registered."; return -1; } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: observer added", __FUNCTION__); rtcp_observer_ = observer; } else { - if (!rtcp_observer_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: no observer added", __FUNCTION__); - return -1; - } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: observer removed", __FUNCTION__); rtcp_observer_ = NULL; } return 0; @@ -1176,34 +1034,25 @@ uint32_t name, const uint8_t* data, uint16_t data_length_in_bytes) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); if (!rtp_rtcp_->Sending()) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: not sending", __FUNCTION__); return -1; } if (!data) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: no input argument", __FUNCTION__); + LOG_F(LS_ERROR) << "Invalid input."; return -1; } if (data_length_in_bytes % 4 != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: input length error", __FUNCTION__); + LOG(LS_ERROR) << "Invalid input length."; return -1; } RTCPMethod rtcp_method = rtp_rtcp_->RTCP(); if (rtcp_method == kRtcpOff) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: RTCP not enabled", __FUNCTION__); + LOG_F(LS_ERROR) << "RTCP not enable."; return -1; } // Create and send packet. if (rtp_rtcp_->SetRTCPApplicationSpecificData(sub_type, name, data, data_length_in_bytes) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not send RTCP application data", __FUNCTION__); return -1; } return 0; @@ -1217,9 +1066,6 @@ uint16_t* fractionLost, uint32_t* cumulativeLost, int32_t* rttMs) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); - // TODO: how do we do this for simulcast ? average for all // except cumulative_lost that is the sum ? // CriticalSectionScoped cs(rtp_rtcp_cs_.get()); @@ -1237,8 +1083,7 @@ // Otherwise use the first report block. std::vector remote_stats; if (rtp_rtcp_->RemoteRTCPStat(&remote_stats) != 0 || remote_stats.empty()) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not get remote stats", __FUNCTION__); + LOG_F(LS_ERROR) << "Could not get remote stats"; return -1; } std::vector::const_iterator statistics = @@ -1261,8 +1106,7 @@ &NTPLow, &receivedPacketCount, &receivedOctetCount) != 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: failed to retrieve RTT", __FUNCTION__); + LOG_F(LS_ERROR) << "failed to retrieve RTT"; NTPHigh = 0; NTPLow = 0; receivedPacketCount = 0; @@ -1276,8 +1120,7 @@ uint16_t dummy; uint16_t rtt = 0; if (rtp_rtcp_->RTT(remote_ssrc, &rtt, &dummy, &dummy, &dummy) != 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not get RTT", __FUNCTION__); + LOG_F(LS_ERROR) << "failed to get RTT"; return -1; } *rttMs = rtt; @@ -1289,55 +1132,50 @@ uint32_t* extended_max, uint32_t* jitter_samples, int32_t* rtt_ms) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); + // Aggregate the report blocks associated with streams sent on this channel. + std::vector report_blocks; + rtp_rtcp_->RemoteRTCPStat(&report_blocks); + { + CriticalSectionScoped lock(rtp_rtcp_cs_.get()); + for (std::list::iterator it = simulcast_rtp_rtcp_.begin(); + it != simulcast_rtp_rtcp_.end(); + ++it) { + (*it)->RemoteRTCPStat(&report_blocks); + } + } - // TODO(pwestin) how do we do this for simulcast ? average for all - // except cumulative_lost that is the sum ? - // CriticalSectionScoped cs(rtp_rtcp_cs_.get()); + if (report_blocks.empty()) + return -1; - // for (std::list::const_iterator it = simulcast_rtp_rtcp_.begin(); - // it != simulcast_rtp_rtcp_.end(); - // it++) { - // RtpRtcp* rtp_rtcp = *it; - // } uint32_t remote_ssrc = vie_receiver_.GetRemoteSsrc(); - - // Get all RTCP receiver report blocks that have been received on this - // channel. If we receive RTP packets from a remote source we know the - // remote SSRC and use the report block from him. - // Otherwise use the first report block. - std::vector remote_stats; - if (rtp_rtcp_->RemoteRTCPStat(&remote_stats) != 0 || remote_stats.empty()) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not get remote stats", __FUNCTION__); - return -1; - } - std::vector::const_iterator statistics = - remote_stats.begin(); - for (; statistics != remote_stats.end(); ++statistics) { - if (statistics->remoteSSRC == remote_ssrc) + std::vector::const_iterator it = report_blocks.begin(); + for (; it != report_blocks.end(); ++it) { + if (it->remoteSSRC == remote_ssrc) break; } - - if (statistics == remote_stats.end()) { - // If we have not received any RTCP packets from this SSRC it probably means - // we have not received any RTP packets. - // Use the first received report block instead. - statistics = remote_stats.begin(); - remote_ssrc = statistics->remoteSSRC; - } - - *fraction_lost = statistics->fractionLost; - *cumulative_lost = statistics->cumulativeLost; - *extended_max = statistics->extendedHighSeqNum; - *jitter_samples = statistics->jitter; + if (it == report_blocks.end()) { + // We have not received packets with an SSRC matching the report blocks. To + // have a chance of calculating an RTT we will try with the SSRC of the + // first report block received. + // This is very important for send-only channels where we don't know the + // SSRC of the other end. + remote_ssrc = report_blocks[0].remoteSSRC; + } + + RTCPReportBlock report; + if (report_blocks.size() > 1) + report = AggregateReportBlocks(report_blocks, &prev_report_blocks_); + else + report = report_blocks[0]; + + *fraction_lost = report.fractionLost; + *cumulative_lost = report.cumulativeLost; + *extended_max = report.extendedHighSeqNum; + *jitter_samples = report.jitter; uint16_t dummy; uint16_t rtt = 0; if (rtp_rtcp_->RTT(remote_ssrc, &rtt, &dummy, &dummy, &dummy) != 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not get RTT", __FUNCTION__); return -1; } *rtt_ms = rtt; @@ -1346,8 +1184,6 @@ void ViEChannel::RegisterSendChannelRtcpStatisticsCallback( RtcpStatisticsCallback* callback) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); rtp_rtcp_->RegisterSendChannelRtcpStatisticsCallback(callback); CriticalSectionScoped cs(rtp_rtcp_cs_.get()); for (std::list::const_iterator it = simulcast_rtp_rtcp_.begin(); @@ -1365,17 +1201,12 @@ uint32_t* extended_max, uint32_t* jitter_samples, int32_t* rtt_ms) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s", __FUNCTION__); - uint32_t remote_ssrc = vie_receiver_.GetRemoteSsrc(); StreamStatistician* statistician = vie_receiver_.GetReceiveStatistics()->GetStatistician(remote_ssrc); RtcpStatistics receive_stats; if (!statistician || !statistician->GetStatistics( &receive_stats, rtp_rtcp_->RTCP() == kRtcpOff)) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not get received RTP statistics", __FUNCTION__); return -1; } *fraction_lost = receive_stats.fraction_lost; @@ -1385,21 +1216,13 @@ uint16_t dummy = 0; uint16_t rtt = 0; - if (rtp_rtcp_->RTT(remote_ssrc, &rtt, &dummy, &dummy, &dummy) != 0) { - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not get RTT", __FUNCTION__); - } + rtp_rtcp_->RTT(remote_ssrc, &rtt, &dummy, &dummy, &dummy); *rtt_ms = rtt; return 0; } void ViEChannel::RegisterReceiveChannelRtcpStatisticsCallback( RtcpStatisticsCallback* callback) { - WEBRTC_TRACE(kTraceInfo, - kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s", - __FUNCTION__); vie_receiver_.GetReceiveStatistics()->RegisterRtcpStatisticsCallback( callback); } @@ -1408,9 +1231,6 @@ uint32_t* packets_sent, uint32_t* bytes_received, uint32_t* packets_received) const { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); - StreamStatistician* statistician = vie_receiver_.GetReceiveStatistics()-> GetStatistician(vie_receiver_.GetRemoteSsrc()); *bytes_received = 0; @@ -1418,8 +1238,6 @@ if (statistician) statistician->GetDataCounters(bytes_received, packets_received); if (rtp_rtcp_->DataCountersRTP(bytes_sent, packets_sent) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not get counters", __FUNCTION__); return -1; } CriticalSectionScoped cs(rtp_rtcp_cs_.get()); @@ -1430,16 +1248,23 @@ uint32_t packets_sent_temp = 0; RtpRtcp* rtp_rtcp = *it; rtp_rtcp->DataCountersRTP(&bytes_sent_temp, &packets_sent_temp); - bytes_sent += bytes_sent_temp; - packets_sent += packets_sent_temp; + *bytes_sent += bytes_sent_temp; + *packets_sent += packets_sent_temp; + } + for (std::list::const_iterator it = removed_rtp_rtcp_.begin(); + it != removed_rtp_rtcp_.end(); ++it) { + uint32_t bytes_sent_temp = 0; + uint32_t packets_sent_temp = 0; + RtpRtcp* rtp_rtcp = *it; + rtp_rtcp->DataCountersRTP(&bytes_sent_temp, &packets_sent_temp); + *bytes_sent += bytes_sent_temp; + *packets_sent += packets_sent_temp; } return 0; } void ViEChannel::RegisterSendChannelRtpStatisticsCallback( StreamDataCountersCallback* callback) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); rtp_rtcp_->RegisterSendChannelRtpStatisticsCallback(callback); { CriticalSectionScoped cs(rtp_rtcp_cs_.get()); @@ -1453,23 +1278,38 @@ void ViEChannel::RegisterReceiveChannelRtpStatisticsCallback( StreamDataCountersCallback* callback) { - WEBRTC_TRACE(kTraceInfo, - kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s", - __FUNCTION__); vie_receiver_.GetReceiveStatistics()->RegisterRtpStatisticsCallback(callback); } -int32_t ViEChannel::GetRemoteRTCPSenderInfo(SenderInfo* sender_info) const { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); +void ViEChannel::GetRtcpPacketTypeCounters( + RtcpPacketTypeCounter* packets_sent, + RtcpPacketTypeCounter* packets_received) const { + rtp_rtcp_->GetRtcpPacketTypeCounters(packets_sent, packets_received); + CriticalSectionScoped cs(rtp_rtcp_cs_.get()); + for (std::list::const_iterator it = simulcast_rtp_rtcp_.begin(); + it != simulcast_rtp_rtcp_.end(); ++it) { + RtcpPacketTypeCounter sent; + RtcpPacketTypeCounter received; + (*it)->GetRtcpPacketTypeCounters(&sent, &received); + packets_sent->Add(sent); + packets_received->Add(received); + } + for (std::list::const_iterator it = removed_rtp_rtcp_.begin(); + it != removed_rtp_rtcp_.end(); ++it) { + RtcpPacketTypeCounter sent; + RtcpPacketTypeCounter received; + (*it)->GetRtcpPacketTypeCounters(&sent, &received); + packets_sent->Add(sent); + packets_received->Add(received); + } +} + +int32_t ViEChannel::GetRemoteRTCPSenderInfo(SenderInfo* sender_info) const { // Get the sender info from the latest received RTCP Sender Report. RTCPSenderInfo rtcp_sender_info; if (rtp_rtcp_->RemoteRTCPStat(&rtcp_sender_info) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: failed to read RTCP SR sender info", __FUNCTION__); + LOG_F(LS_ERROR) << "failed to read RTCP SR sender info"; return -1; } @@ -1485,9 +1325,6 @@ uint32_t* video_bitrate_sent, uint32_t* fec_bitrate_sent, uint32_t* nackBitrateSent) const { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); - rtp_rtcp_->BitrateSent(total_bitrate_sent, video_bitrate_sent, fec_bitrate_sent, nackBitrateSent); CriticalSectionScoped cs(rtp_rtcp_cs_.get()); @@ -1508,9 +1345,6 @@ bool ViEChannel::GetSendSideDelay(int* avg_send_delay, int* max_send_delay) const { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); - *avg_send_delay = 0; *max_send_delay = 0; bool valid_estimate = false; @@ -1540,20 +1374,14 @@ return valid_estimate; } -void ViEChannel::RegisterSendBitrateObserver( - BitrateStatisticsObserver* observer) { - rtp_rtcp_->RegisterVideoBitrateObserver(observer); - CriticalSectionScoped cs(rtp_rtcp_cs_.get()); - for (std::list::const_iterator it = simulcast_rtp_rtcp_.begin(); - it != simulcast_rtp_rtcp_.end(); - it++) { - (*it)->RegisterVideoBitrateObserver(observer); - } +void ViEChannel::RegisterSendSideDelayObserver( + SendSideDelayObserver* observer) { + send_side_delay_observer_.Set(observer); } -void ViEChannel::GetEstimatedReceiveBandwidth( - uint32_t* estimated_bandwidth) const { - vie_receiver_.EstimatedReceiveBandwidth(estimated_bandwidth); +void ViEChannel::RegisterSendBitrateObserver( + BitrateStatisticsObserver* observer) { + send_bitrate_observer_.Set(observer); } void ViEChannel::GetReceiveBandwidthEstimatorStats( @@ -1562,16 +1390,7 @@ } int32_t ViEChannel::StartRTPDump(const char file_nameUTF8[1024], - RTPDirections direction) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); - - if (direction != kRtpIncoming && direction != kRtpOutgoing) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: invalid input", __FUNCTION__); - return -1; - } - + RTPDirections direction) { if (direction == kRtpIncoming) { return vie_receiver_.StartRTPDump(file_nameUTF8); } else { @@ -1580,15 +1399,6 @@ } int32_t ViEChannel::StopRTPDump(RTPDirections direction) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s", __FUNCTION__); - - if (direction != kRtpIncoming && direction != kRtpOutgoing) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: invalid input", __FUNCTION__); - return -1; - } - if (direction == kRtpIncoming) { return vie_receiver_.StopRTPDump(); } else { @@ -1598,25 +1408,16 @@ int32_t ViEChannel::StartSend() { CriticalSectionScoped cs(callback_cs_.get()); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s", __FUNCTION__); - if (!external_transport_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: send sockets not initialized", __FUNCTION__); + LOG(LS_ERROR) << "No transport set."; return -1; } rtp_rtcp_->SetSendingMediaStatus(true); if (rtp_rtcp_->Sending()) { - // Already sending. - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Already sending", __FUNCTION__); return kViEBaseAlreadySending; } if (rtp_rtcp_->SetSendingStatus(true) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not start sending RTP", __FUNCTION__); return -1; } CriticalSectionScoped cs_rtp(rtp_rtcp_cs_.get()); @@ -1632,9 +1433,6 @@ } int32_t ViEChannel::StopSend() { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); - CriticalSectionScoped cs(rtp_rtcp_cs_.get()); rtp_rtcp_->SetSendingMediaStatus(false); for (std::list::iterator it = simulcast_rtp_rtcp_.begin(); @@ -1644,16 +1442,12 @@ rtp_rtcp->SetSendingMediaStatus(false); } if (!rtp_rtcp_->Sending()) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Not sending", __FUNCTION__); return kViEBaseNotSending; } // Reset. rtp_rtcp_->ResetSendDataCountersRTP(); if (rtp_rtcp_->SetSendingStatus(false) != 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: could not stop RTP sending", __FUNCTION__); return -1; } for (std::list::iterator it = simulcast_rtp_rtcp_.begin(); @@ -1673,13 +1467,7 @@ int32_t ViEChannel::StartReceive() { CriticalSectionScoped cs(callback_cs_.get()); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); - if (StartDecodeThread() != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: could not start decoder thread", __FUNCTION__); - vie_receiver_.StopReceive(); return -1; } @@ -1689,54 +1477,35 @@ } int32_t ViEChannel::StopReceive() { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); - vie_receiver_.StopReceive(); vie_receiver_.StopRTCPReceive(); StopDecodeThread(); - vcm_.ResetDecoder(); + vcm_->ResetDecoder(); return 0; } int32_t ViEChannel::RegisterSendTransport(Transport* transport) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); - if (rtp_rtcp_->Sending()) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Sending", __FUNCTION__); return -1; } CriticalSectionScoped cs(callback_cs_.get()); if (external_transport_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: transport already registered", __FUNCTION__); + LOG_F(LS_ERROR) << "Transport already registered."; return -1; } external_transport_ = transport; vie_sender_.RegisterSendTransport(transport); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Transport registered: 0x%p", __FUNCTION__, - &external_transport_); - return 0; } int32_t ViEChannel::DeregisterSendTransport() { CriticalSectionScoped cs(callback_cs_.get()); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); - if (!external_transport_) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: no transport registered", __FUNCTION__); - return -1; + return 0; } if (rtp_rtcp_->Sending()) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Sending", __FUNCTION__); + LOG_F(LS_ERROR) << "Can't deregister transport when sending."; return -1; } external_transport_ = NULL; @@ -1769,10 +1538,7 @@ } int32_t ViEChannel::SetMTU(uint16_t mtu) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); if (rtp_rtcp_->SetMaxTransferUnit(mtu) != 0) { - // Logging done. return -1; } CriticalSectionScoped cs(rtp_rtcp_cs_.get()); @@ -1787,15 +1553,10 @@ } uint16_t ViEChannel::MaxDataPayloadLength() const { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s", __FUNCTION__); return rtp_rtcp_->MaxDataPayloadLength(); } int32_t ViEChannel::EnableColorEnhancement(bool enable) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s(enable: %d)", __FUNCTION__, enable); - CriticalSectionScoped cs(callback_cs_.get()); color_enhancement_ = enable; return 0; @@ -1836,10 +1597,13 @@ unsigned int length = CalcBufferSize(kI420, video_frame.width(), video_frame.height()); - scoped_array video_buffer(new uint8_t[length]); + scoped_ptr video_buffer(new uint8_t[length]); ExtractBuffer(video_frame, length, video_buffer.get()); - effect_filter_->Transform(length, video_buffer.get(), - video_frame.timestamp(), video_frame.width(), + effect_filter_->Transform(length, + video_buffer.get(), + video_frame.ntp_time_ms(), + video_frame.timestamp(), + video_frame.width(), video_frame.height()); } if (color_enhancement_) { @@ -1853,8 +1617,6 @@ arr_ofCSRC[0] = vie_receiver_.GetRemoteSsrc(); no_of_csrcs = 1; } - WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s(timestamp:%u)", __FUNCTION__, video_frame.timestamp()); DeliverFrame(&video_frame, no_of_csrcs, arr_ofCSRC); return 0; } @@ -1873,9 +1635,6 @@ const uint32_t frame_rate) { CriticalSectionScoped cs(callback_cs_.get()); if (codec_observer_) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: bitrate %u, framerate %u", __FUNCTION__, bit_rate, - frame_rate); codec_observer_->IncomingRate(channel_id_, frame_rate, bit_rate); } return 0; @@ -1901,8 +1660,6 @@ } int32_t ViEChannel::RequestKeyFrame() { - WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s", __FUNCTION__); { CriticalSectionScoped cs(callback_cs_.get()); if (codec_observer_ && do_key_frame_callbackRequest_) { @@ -1919,14 +1676,11 @@ int32_t ViEChannel::ResendPackets(const uint16_t* sequence_numbers, uint16_t length) { - WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s(length: %d)", __FUNCTION__, length); return rtp_rtcp_->SendNACK(sequence_numbers, length); } void ViEChannel::ReceiveStateChange(VideoReceiveState state) { - WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s", __FUNCTION__); + LOG_F(LS_INFO); { CriticalSectionScoped cs(callback_cs_.get()); if (codec_observer_) { @@ -1940,12 +1694,66 @@ } bool ViEChannel::ChannelDecodeProcess() { - vcm_.Decode(kMaxDecodeWaitTimeMs); + vcm_->Decode(kMaxDecodeWaitTimeMs); return true; } void ViEChannel::OnRttUpdate(uint32_t rtt) { - vcm_.SetReceiveChannelParameters(rtt); + vcm_->SetReceiveChannelParameters(rtt); +} + +void ViEChannel::ReserveRtpRtcpModules(size_t num_modules) { + for (size_t total_modules = + 1 + simulcast_rtp_rtcp_.size() + removed_rtp_rtcp_.size(); + total_modules < num_modules; + ++total_modules) { + RtpRtcp* rtp_rtcp = CreateRtpRtcpModule(); + rtp_rtcp->SetSendingStatus(false); + rtp_rtcp->SetSendingMediaStatus(false); + rtp_rtcp->RegisterSendChannelRtcpStatisticsCallback(NULL); + rtp_rtcp->RegisterSendChannelRtpStatisticsCallback(NULL); + removed_rtp_rtcp_.push_back(rtp_rtcp); + } +} + +RtpRtcp* ViEChannel::GetRtpRtcpModule(size_t index) const { + if (index == 0) + return rtp_rtcp_.get(); + if (index <= simulcast_rtp_rtcp_.size()) { + std::list::const_iterator it = simulcast_rtp_rtcp_.begin(); + for (size_t i = 1; i < index; ++i) { + ++it; + } + return *it; + } + + // If the requested module exists it must be in the removed list. Index + // translation to this list must remove the default module as well as all + // active simulcast modules. + size_t removed_idx = index - simulcast_rtp_rtcp_.size() - 1; + if (removed_idx >= removed_rtp_rtcp_.size()) + return NULL; + + std::list::const_iterator it = removed_rtp_rtcp_.begin(); + while (removed_idx-- > 0) + ++it; + + return *it; +} + +RtpRtcp* ViEChannel::CreateRtpRtcpModule() { + RtpRtcp::Configuration configuration; + configuration.id = ViEModuleId(engine_id_, channel_id_); + configuration.audio = false; // Video. + configuration.default_module = default_rtp_rtcp_; + configuration.outgoing_transport = &vie_sender_; + configuration.intra_frame_callback = intra_frame_observer_; + configuration.bandwidth_callback = bandwidth_observer_.get(); + configuration.rtt_stats = rtt_stats_; + configuration.paced_sender = paced_sender_; + configuration.send_side_delay_observer = &send_side_delay_observer_; + + return RtpRtcp::CreateRtpRtcp(configuration); } int32_t ViEChannel::StartDecodeThread() { @@ -1958,8 +1766,6 @@ this, kHighestPriority, "DecodingThread"); if (!decode_thread_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: could not create decode thread", __FUNCTION__); return -1; } @@ -1967,20 +1773,14 @@ if (decode_thread_->Start(thread_id) == false) { delete decode_thread_; decode_thread_ = NULL; - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: could not start decode thread", __FUNCTION__); + LOG(LS_ERROR) << "Could not start decode thread."; return -1; } - - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: decode thread with id %u started", __FUNCTION__); return 0; } int32_t ViEChannel::StopDecodeThread() { if (!decode_thread_) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: decode thread not running", __FUNCTION__); return 0; } @@ -1988,9 +1788,6 @@ if (decode_thread_->Stop()) { delete decode_thread_; } else { - // Couldn't stop the thread, leak instead of crash. - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: could not stop decode thread", __FUNCTION__); assert(false && "could not stop decode thread"); } decode_thread_ = NULL; @@ -1999,10 +1796,6 @@ int32_t ViEChannel::SetVoiceChannel(int32_t ve_channel_id, VoEVideoSync* ve_sync_interface) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s, audio channel %d, video channel %d", __FUNCTION__, - ve_channel_id, channel_id_); - if (ve_sync_interface) { // Register lip sync module_process_thread_.RegisterModule(&vie_sync_); @@ -2021,26 +1814,9 @@ int32_t ViEChannel::RegisterEffectFilter(ViEEffectFilter* effect_filter) { CriticalSectionScoped cs(callback_cs_.get()); - if (!effect_filter) { - if (!effect_filter_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: no effect filter added for channel %d", - __FUNCTION__, channel_id_); - return -1; - } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: deregister effect filter for device %d", __FUNCTION__, - channel_id_); - } else { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: register effect filter for device %d", __FUNCTION__, - channel_id_); - if (effect_filter_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: effect filter already added for channel %d", - __FUNCTION__, channel_id_); - return -1; - } + if (effect_filter && effect_filter_) { + LOG(LS_ERROR) << "Effect filter already registered."; + return -1; } effect_filter_ = effect_filter; return 0; @@ -2054,8 +1830,7 @@ void ViEChannel::RegisterPreDecodeImageCallback( EncodedImageCallback* pre_decode_callback) { - CriticalSectionScoped cs(callback_cs_.get()); - vcm_.RegisterPreDecodeImageCallback(pre_decode_callback); + vcm_->RegisterPreDecodeImageCallback(pre_decode_callback); } void ViEChannel::OnApplicationDataReceived(const int32_t id, @@ -2064,8 +1839,6 @@ const uint16_t length, const uint8_t* data) { if (channel_id_ != ChannelId(id)) { - WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s, incorrect id", __FUNCTION__, id); return; } CriticalSectionScoped cs(callback_cs_.get()); @@ -2085,10 +1858,9 @@ const int frequency, const uint8_t channels, const uint32_t rate) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: payload_type %d, payload_name %s", __FUNCTION__, - payload_type, payload_name); - vcm_.ResetDecoder(); + LOG(LS_INFO) << "OnInitializeDecoder " << payload_type << " " + << payload_name; + vcm_->ResetDecoder(); CriticalSectionScoped cs(callback_cs_.get()); decoder_reset_ = true; @@ -2096,16 +1868,7 @@ } void ViEChannel::OnIncomingSSRCChanged(const int32_t id, const uint32_t ssrc) { - if (channel_id_ != ChannelId(id)) { - assert(false); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s, incorrect id", __FUNCTION__, id); - return; - } - - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: %u", __FUNCTION__, ssrc); - + assert(channel_id_ == ChannelId(id)); rtp_rtcp_->SetRemoteSSRC(ssrc); CriticalSectionScoped cs(callback_cs_.get()); @@ -2119,19 +1882,7 @@ void ViEChannel::OnIncomingCSRCChanged(const int32_t id, const uint32_t CSRC, const bool added) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: %u added: %d", __FUNCTION__, CSRC, added); - - if (channel_id_ != ChannelId(id)) { - assert(false); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s, incorrect id", __FUNCTION__, id); - return; - } - - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: %u", __FUNCTION__, CSRC); - + assert(channel_id_ == ChannelId(id)); CriticalSectionScoped cs(callback_cs_.get()); { if (rtp_observer_) { @@ -2149,13 +1900,11 @@ void ViEChannel::RegisterSendFrameCountObserver( FrameCountObserver* observer) { - rtp_rtcp_->RegisterSendFrameCountObserver(observer); - CriticalSectionScoped cs(rtp_rtcp_cs_.get()); - for (std::list::iterator it = simulcast_rtp_rtcp_.begin(); - it != simulcast_rtp_rtcp_.end(); - it++) { - (*it)->RegisterSendFrameCountObserver(observer); - } + send_frame_count_observer_.Set(observer); } +void ViEChannel::ReceivedBWEPacket(int64_t arrival_time_ms, + int payload_size, const RTPHeader& header) { + vie_receiver_.ReceivedBWEPacket(arrival_time_ms, payload_size, header); +} } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel_group.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel_group.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel_group.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel_group.cc 2015-02-03 14:33:38.000000000 +0000 @@ -10,6 +10,7 @@ #include "webrtc/video_engine/vie_channel_group.h" +#include "webrtc/base/thread_annotations.h" #include "webrtc/common.h" #include "webrtc/experiments.h" #include "webrtc/modules/bitrate_controller/include/bitrate_controller.h" @@ -17,7 +18,7 @@ #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" #include "webrtc/modules/utility/interface/process_thread.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/video_engine/call_stats.h" #include "webrtc/video_engine/encoder_state_feedback.h" #include "webrtc/video_engine/vie_channel.h" @@ -31,78 +32,86 @@ class WrappingBitrateEstimator : public RemoteBitrateEstimator { public: - WrappingBitrateEstimator(int engine_id, RemoteBitrateObserver* observer, - Clock* clock, ProcessThread* process_thread, + WrappingBitrateEstimator(int engine_id, + RemoteBitrateObserver* observer, + Clock* clock, const Config& config) : observer_(observer), clock_(clock), - process_thread_(process_thread), crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), engine_id_(engine_id), min_bitrate_bps_(config.Get().min_rate), - rbe_(RemoteBitrateEstimatorFactory().Create(observer_, clock_, + rate_control_type_(kAimdControl), + rbe_(RemoteBitrateEstimatorFactory().Create(observer_, + clock_, + rate_control_type_, min_bitrate_bps_)), using_absolute_send_time_(false), packets_since_absolute_send_time_(0) { - assert(process_thread_ != NULL); - process_thread_->RegisterModule(rbe_.get()); - } - virtual ~WrappingBitrateEstimator() { - process_thread_->DeRegisterModule(rbe_.get()); } + virtual ~WrappingBitrateEstimator() {} + virtual void IncomingPacket(int64_t arrival_time_ms, int payload_size, - const RTPHeader& header) { + const RTPHeader& header) OVERRIDE { CriticalSectionScoped cs(crit_sect_.get()); - PickEstimator(header); + PickEstimatorFromHeader(header); rbe_->IncomingPacket(arrival_time_ms, payload_size, header); } - virtual int32_t Process() { - assert(false && "Not supposed to register the WrappingBitrateEstimator!"); - return 0; + virtual int32_t Process() OVERRIDE { + CriticalSectionScoped cs(crit_sect_.get()); + return rbe_->Process(); } - virtual int32_t TimeUntilNextProcess() { - assert(false && "Not supposed to register the WrappingBitrateEstimator!"); - return 0; + virtual int32_t TimeUntilNextProcess() OVERRIDE { + CriticalSectionScoped cs(crit_sect_.get()); + return rbe_->TimeUntilNextProcess(); } - virtual void OnRttUpdate(uint32_t rtt) { + virtual void OnRttUpdate(uint32_t rtt) OVERRIDE { CriticalSectionScoped cs(crit_sect_.get()); rbe_->OnRttUpdate(rtt); } - virtual void RemoveStream(unsigned int ssrc) { + virtual void RemoveStream(unsigned int ssrc) OVERRIDE { CriticalSectionScoped cs(crit_sect_.get()); rbe_->RemoveStream(ssrc); } virtual bool LatestEstimate(std::vector* ssrcs, - unsigned int* bitrate_bps) const { + unsigned int* bitrate_bps) const OVERRIDE { CriticalSectionScoped cs(crit_sect_.get()); return rbe_->LatestEstimate(ssrcs, bitrate_bps); } - virtual bool GetStats(ReceiveBandwidthEstimatorStats* output) const { + virtual bool GetStats(ReceiveBandwidthEstimatorStats* output) const OVERRIDE { CriticalSectionScoped cs(crit_sect_.get()); return rbe_->GetStats(output); } + void SetConfig(const webrtc::Config& config) { + CriticalSectionScoped cs(crit_sect_.get()); + RateControlType new_control_type = + config.Get().enabled ? kAimdControl : + kMimdControl; + if (new_control_type != rate_control_type_) { + rate_control_type_ = new_control_type; + PickEstimator(); + } + } + private: - // Instantiate RBE for Time Offset or Absolute Send Time extensions. - void PickEstimator(const RTPHeader& header) { + void PickEstimatorFromHeader(const RTPHeader& header) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_.get()) { if (header.extension.hasAbsoluteSendTime) { // If we see AST in header, switch RBE strategy immediately. if (!using_absolute_send_time_) { - process_thread_->DeRegisterModule(rbe_.get()); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, ViEId(engine_id_), - "WrappingBitrateEstimator: Switching to absolute send time RBE."); - rbe_.reset(AbsoluteSendTimeRemoteBitrateEstimatorFactory().Create( - observer_, clock_, min_bitrate_bps_)); - process_thread_->RegisterModule(rbe_.get()); + LOG(LS_INFO) << + "WrappingBitrateEstimator: Switching to absolute send time RBE."; using_absolute_send_time_ = true; + PickEstimator(); } packets_since_absolute_send_time_ = 0; } else { @@ -110,25 +119,32 @@ if (using_absolute_send_time_) { ++packets_since_absolute_send_time_; if (packets_since_absolute_send_time_ >= kTimeOffsetSwitchThreshold) { - process_thread_->DeRegisterModule(rbe_.get()); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, ViEId(engine_id_), - "WrappingBitrateEstimator: Switching to transmission time offset " - "RBE."); - rbe_.reset(RemoteBitrateEstimatorFactory().Create(observer_, clock_, - min_bitrate_bps_)); - process_thread_->RegisterModule(rbe_.get()); + LOG(LS_INFO) << "WrappingBitrateEstimator: Switching to transmission " + << "time offset RBE."; using_absolute_send_time_ = false; + PickEstimator(); } } } } + // Instantiate RBE for Time Offset or Absolute Send Time extensions. + void PickEstimator() EXCLUSIVE_LOCKS_REQUIRED(crit_sect_.get()) { + if (using_absolute_send_time_) { + rbe_.reset(AbsoluteSendTimeRemoteBitrateEstimatorFactory().Create( + observer_, clock_, rate_control_type_, min_bitrate_bps_)); + } else { + rbe_.reset(RemoteBitrateEstimatorFactory().Create( + observer_, clock_, rate_control_type_, min_bitrate_bps_)); + } + } + RemoteBitrateObserver* observer_; Clock* clock_; - ProcessThread* process_thread_; scoped_ptr crit_sect_; const int engine_id_; const uint32_t min_bitrate_bps_; + RateControlType rate_control_type_; scoped_ptr rbe_; bool using_absolute_send_time_; uint32_t packets_since_absolute_send_time_; @@ -137,10 +153,13 @@ }; } // namespace -ChannelGroup::ChannelGroup(int engine_id, ProcessThread* process_thread, +ChannelGroup::ChannelGroup(int engine_id, + ProcessThread* process_thread, const Config* config) : remb_(new VieRemb()), - bitrate_controller_(BitrateController::CreateBitrateController(true)), + bitrate_controller_( + BitrateController::CreateBitrateController(Clock::GetRealTimeClock(), + true)), call_stats_(new CallStats()), encoder_state_feedback_(new EncoderStateFeedback()), config_(config), @@ -151,17 +170,25 @@ config_ = own_config_.get(); } assert(config_); // Must have a valid config pointer here. + remote_bitrate_estimator_.reset( - new WrappingBitrateEstimator(engine_id, remb_.get(), - Clock::GetRealTimeClock(), process_thread, - *config_)), + new WrappingBitrateEstimator(engine_id, + remb_.get(), + Clock::GetRealTimeClock(), + *config_)); + call_stats_->RegisterStatsObserver(remote_bitrate_estimator_.get()); + + process_thread->RegisterModule(remote_bitrate_estimator_.get()); process_thread->RegisterModule(call_stats_.get()); + process_thread->RegisterModule(bitrate_controller_.get()); } ChannelGroup::~ChannelGroup() { - call_stats_->DeregisterStatsObserver(remote_bitrate_estimator_.get()); + process_thread_->DeRegisterModule(bitrate_controller_.get()); process_thread_->DeRegisterModule(call_stats_.get()); + process_thread_->DeRegisterModule(remote_bitrate_estimator_.get()); + call_stats_->DeregisterStatsObserver(remote_bitrate_estimator_.get()); assert(channels_.empty()); assert(!remb_->InUse()); } @@ -223,4 +250,10 @@ } return true; } + +void ChannelGroup::SetBandwidthEstimationConfig(const webrtc::Config& config) { + WrappingBitrateEstimator* estimator = + static_cast(remote_bitrate_estimator_.get()); + estimator->SetConfig(config); +} } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel_group.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel_group.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel_group.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel_group.h 2015-02-03 14:33:38.000000000 +0000 @@ -42,6 +42,7 @@ bool SetChannelRembStatus(int channel_id, bool sender, bool receiver, ViEChannel* channel); + void SetBandwidthEstimationConfig(const webrtc::Config& config); BitrateController* GetBitrateController(); CallStats* GetCallStats(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel.h 2015-02-03 14:33:38.000000000 +0000 @@ -16,6 +16,7 @@ #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" #include "webrtc/modules/video_coding/main/interface/video_coding_defines.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/system_wrappers/interface/tick_util.h" #include "webrtc/typedefs.h" @@ -148,20 +149,21 @@ int32_t GetRemoteCSRC(uint32_t CSRCs[kRtpCsrcSize]); int SetRtxSendPayloadType(int payload_type); + // Only has an effect once RTX is enabled. + void SetPadWithRedundantPayloads(bool enable); void SetRtxReceivePayloadType(int payload_type); // Sets the starting sequence number, must be called before StartSend. int32_t SetStartSequenceNumber(uint16_t sequence_number); + void SetRtpStateForSsrc(uint32_t ssrc, const RtpState& rtp_state); + RtpState GetRtpStateForSsrc(uint32_t ssrc); + // Sets the CName for the outgoing stream on the channel. int32_t SetRTCPCName(const char rtcp_cname[]); - // Gets the CName for the outgoing stream on the channel. - int32_t GetRTCPCName(char rtcp_cname[]); - // Gets the CName of the incoming stream. int32_t GetRemoteRTCPCName(char rtcp_cname[]); - int32_t RegisterRtpObserver(ViERTPObserver* observer); int32_t RegisterRtcpObserver(ViERTCPObserver* observer); int32_t SendApplicationDefinedRTCPPacket( @@ -216,6 +218,9 @@ void RegisterReceiveChannelRtpStatisticsCallback( StreamDataCountersCallback* callback); + void GetRtcpPacketTypeCounters(RtcpPacketTypeCounter* packets_sent, + RtcpPacketTypeCounter* packets_received) const; + int32_t GetRemoteRTCPSenderInfo(SenderInfo* sender_info) const; @@ -223,8 +228,10 @@ uint32_t* video_bitrate_sent, uint32_t* fec_bitrate_sent, uint32_t* nackBitrateSent) const; + // TODO(holmer): Deprecated. We should use the SendSideDelayObserver instead + // to avoid deadlocks. bool GetSendSideDelay(int* avg_send_delay, int* max_send_delay) const; - void GetEstimatedReceiveBandwidth(uint32_t* estimated_bandwidth) const; + void RegisterSendSideDelayObserver(SendSideDelayObserver* observer); void GetReceiveBandwidthEstimatorStats( ReceiveBandwidthEstimatorStats* output) const; @@ -368,6 +375,9 @@ void RegisterSendFrameCountObserver(FrameCountObserver* observer); + void ReceivedBWEPacket(int64_t arrival_time_ms, int payload_size, + const RTPHeader& header); + protected: static bool ChannelDecodeThreadFunction(void* obj); bool ChannelDecodeProcess(); @@ -375,6 +385,11 @@ void OnRttUpdate(uint32_t rtt); private: + void ReserveRtpRtcpModules(size_t total_modules) + EXCLUSIVE_LOCKS_REQUIRED(rtp_rtcp_cs_); + RtpRtcp* GetRtpRtcpModule(size_t simulcast_idx) const + EXCLUSIVE_LOCKS_REQUIRED(rtp_rtcp_cs_); + RtpRtcp* CreateRtpRtcpModule(); // Assumed to be protected. int32_t StartDecodeThread(); int32_t StopDecodeThread(); @@ -385,6 +400,71 @@ const unsigned char payload_typeFEC); // Compute NACK list parameters for the buffering mode. int GetRequiredNackListSize(int target_delay_ms); + void SetRtxSendStatus(bool enable); + + void UpdateHistograms(); + + // ViEChannel exposes methods that allow to modify observers and callbacks + // to be modified. Such an API-style is cumbersome to implement and maintain + // at all the levels when comparing to only setting them at construction. As + // so this class instantiates its children with a wrapper that can be modified + // at a later time. + template + class RegisterableCallback : public T { + public: + RegisterableCallback() + : critsect_(CriticalSectionWrapper::CreateCriticalSection()), + callback_(NULL) {} + + void Set(T* callback) { + CriticalSectionScoped cs(critsect_.get()); + callback_ = callback; + } + + protected: + // Note: this should be implemented with a RW-lock to allow simultaneous + // calls into the callback. However that doesn't seem to be needed for the + // current type of callbacks covered by this class. + scoped_ptr critsect_; + T* callback_ GUARDED_BY(critsect_); + + private: + DISALLOW_COPY_AND_ASSIGN(RegisterableCallback); + }; + + class RegisterableBitrateStatisticsObserver: + public RegisterableCallback { + virtual void Notify(const BitrateStatistics& total_stats, + const BitrateStatistics& retransmit_stats, + uint32_t ssrc) { + CriticalSectionScoped cs(critsect_.get()); + if (callback_) + callback_->Notify(total_stats, retransmit_stats, ssrc); + } + } + send_bitrate_observer_; + + class RegisterableFrameCountObserver + : public RegisterableCallback { + virtual void FrameCountUpdated(FrameType frame_type, + uint32_t frame_count, + const unsigned int ssrc) { + CriticalSectionScoped cs(critsect_.get()); + if (callback_) + callback_->FrameCountUpdated(frame_type, frame_count, ssrc); + } + } send_frame_count_observer_; + + class RegisterableSendSideDelayObserver : + public RegisterableCallback { + virtual void SendSideDelayUpdated(int avg_delay_ms, + int max_delay_ms, + uint32_t ssrc) OVERRIDE { + CriticalSectionScoped cs(critsect_.get()); + if (callback_) + callback_->SendSideDelayUpdated(avg_delay_ms, max_delay_ms, ssrc); + } + } send_side_delay_observer_; int32_t channel_id_; int32_t engine_id_; @@ -401,7 +481,7 @@ scoped_ptr rtp_rtcp_; std::list simulcast_rtp_rtcp_; std::list removed_rtp_rtcp_; - VideoCodingModule& vcm_; + VideoCodingModule* const vcm_; ViEReceiver vie_receiver_; ViESender vie_sender_; ViESyncModule vie_sync_; @@ -418,6 +498,7 @@ RtcpIntraFrameObserver* intra_frame_observer_; RtcpRttStats* rtt_stats_; PacedSender* paced_sender_; + bool pad_with_redundant_payloads_; scoped_ptr bandwidth_observer_; int send_timestamp_extension_id_; @@ -441,7 +522,9 @@ int nack_history_size_sender_; int max_nack_reordering_threshold_; I420FrameCallback* pre_render_callback_; - const Config& config_; + const int64_t start_ms_; + + std::map prev_report_blocks_; }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel_manager.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel_manager.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel_manager.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel_manager.cc 2015-02-03 14:33:38.000000000 +0000 @@ -15,7 +15,7 @@ #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" #include "webrtc/modules/utility/interface/process_thread.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/video_engine/call_stats.h" #include "webrtc/video_engine/encoder_state_feedback.h" #include "webrtc/video_engine/vie_channel.h" @@ -41,18 +41,12 @@ engine_config_(config), load_manager_(NULL) { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id), - "ViEChannelManager::ViEChannelManager(engine_id: %d)", - engine_id); for (int idx = 0; idx < free_channel_ids_size_; idx++) { free_channel_ids_[idx] = true; } } ViEChannelManager::~ViEChannelManager() { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id_), - "ViEChannelManager Destructor, engine_id: %d", engine_id_); - while (channel_map_.size() > 0) { ChannelMap::iterator it = channel_map_.begin(); // DeleteChannel will erase this channel from the map and invalidate |it|. @@ -236,8 +230,6 @@ ChannelMap::iterator c_it = channel_map_.find(channel_id); if (c_it == channel_map_.end()) { // No such channel. - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), - "%s Channel doesn't exist: %d", __FUNCTION__, channel_id); return -1; } vie_channel = c_it->second; @@ -287,22 +279,17 @@ // deleted, which might take time. // If statment just to show that this object is not always deleted. if (vie_encoder) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_), - "%s ViEEncoder deleted for channel %d", __FUNCTION__, - channel_id); + LOG(LS_VERBOSE) << "ViEEncoder deleted for channel " << channel_id; delete vie_encoder; } // If statment just to show that this object is not always deleted. if (group) { // Delete the group if empty last since the encoder holds a pointer to the // BitrateController object that the group owns. - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_), - "%s ChannelGroup deleted for channel %d", __FUNCTION__, - channel_id); + LOG(LS_VERBOSE) << "Channel group deleted for channel " << channel_id; delete group; } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_), - "%s Channel %d deleted", __FUNCTION__, channel_id); + LOG(LS_VERBOSE) << "Channel deleted " << channel_id; return 0; } @@ -317,9 +304,6 @@ // Get new sync interface. sync_interface = VoEVideoSync::GetInterface(voice_engine); if (!sync_interface) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), - "%s Can't get audio sync interface from VoiceEngine.", - __FUNCTION__); return -1; } } @@ -340,8 +324,7 @@ int audio_channel_id) { CriticalSectionScoped cs(channel_id_critsect_); if (!voice_sync_interface_) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id), - "No VoE set"); + LOG_F(LS_ERROR) << "No VoE set."; return -1; } ViEChannel* channel = ViEChannelPtr(channel_id); @@ -379,6 +362,19 @@ return group->SetChannelRembStatus(channel_id, sender, receiver, channel); } +bool ViEChannelManager::SetReservedTransmitBitrate( + int channel_id, uint32_t reserved_transmit_bitrate_bps) { + CriticalSectionScoped cs(channel_id_critsect_); + ChannelGroup* group = FindGroup(channel_id); + if (!group) { + return false; + } + + BitrateController* bitrate_controller = group->GetBitrateController(); + bitrate_controller->SetReservedBitrate(reserved_transmit_bitrate_bps); + return true; +} + void ViEChannelManager::UpdateSsrcs(int channel_id, const std::list& ssrcs) { CriticalSectionScoped cs(channel_id_critsect_); @@ -400,6 +396,43 @@ } } +bool ViEChannelManager::SetBandwidthEstimationConfig( + int channel_id, const webrtc::Config& config) { + CriticalSectionScoped cs(channel_id_critsect_); + ChannelGroup* group = FindGroup(channel_id); + if (!group) { + return false; + } + group->SetBandwidthEstimationConfig(config); + return true; +} + +bool ViEChannelManager::GetEstimatedSendBandwidth( + int channel_id, uint32_t* estimated_bandwidth) const { + CriticalSectionScoped cs(channel_id_critsect_); + ChannelGroup* group = FindGroup(channel_id); + if (!group) { + return false; + } + group->GetBitrateController()->AvailableBandwidth(estimated_bandwidth); + return true; +} + +bool ViEChannelManager::GetEstimatedReceiveBandwidth( + int channel_id, uint32_t* estimated_bandwidth) const { + CriticalSectionScoped cs(channel_id_critsect_); + ChannelGroup* group = FindGroup(channel_id); + if (!group) { + return false; + } + std::vector ssrcs; + if (!group->GetRemoteBitrateEstimator()->LatestEstimate( + &ssrcs, estimated_bandwidth) || ssrcs.empty()) { + *estimated_bandwidth = 0; + } + return true; +} + bool ViEChannelManager::CreateChannelObject( int channel_id, ViEEncoder* vie_encoder, @@ -425,21 +458,15 @@ send_rtp_rtcp_module, sender); if (vie_channel->Init() != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), - "%s could not init channel", __FUNCTION__, channel_id); delete vie_channel; return false; } VideoCodec encoder; if (vie_encoder->GetEncoder(&encoder) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id), - "%s: Could not GetEncoder.", __FUNCTION__); delete vie_channel; return false; } if (sender && vie_channel->SetSendCodec(encoder) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id), - "%s: Could not SetSendCodec.", __FUNCTION__); delete vie_channel; return false; } @@ -453,8 +480,7 @@ CriticalSectionScoped cs(channel_id_critsect_); ChannelMap::const_iterator it = channel_map_.find(channel_id); if (it == channel_map_.end()) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), - "%s Channel doesn't exist: %d", __FUNCTION__, channel_id); + LOG(LS_ERROR) << "Channel doesn't exist " << channel_id; return NULL; } return it->second; @@ -479,8 +505,7 @@ } idx++; } - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), - "Max number of channels reached: %d", channel_map_.size()); + LOG(LS_ERROR) << "Max number of channels reached."; return -1; } @@ -491,8 +516,8 @@ free_channel_ids_[channel_id - kViEChannelIdBase] = true; } -ChannelGroup* ViEChannelManager::FindGroup(int channel_id) { - for (ChannelGroups::iterator it = channel_groups_.begin(); +ChannelGroup* ViEChannelManager::FindGroup(int channel_id) const { + for (ChannelGroups::const_iterator it = channel_groups_.begin(); it != channel_groups_.end(); ++it) { if ((*it)->HasChannel(channel_id)) { return *it; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel_manager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel_manager.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel_manager.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_channel_manager.h 2015-02-03 14:33:38.000000000 +0000 @@ -77,10 +77,22 @@ // Adds a channel to include when sending REMB. bool SetRembStatus(int channel_id, bool sender, bool receiver); + bool SetReservedTransmitBitrate(int channel_id, + uint32_t reserved_transmit_bitrate_bps); + // Updates the SSRCs for a channel. If one of the SSRCs already is registered, // it will simply be ignored and no error is returned. void UpdateSsrcs(int channel_id, const std::list& ssrcs); + // Sets bandwidth estimation related configurations. + bool SetBandwidthEstimationConfig(int channel_id, + const webrtc::Config& config); + + bool GetEstimatedSendBandwidth(int channel_id, + uint32_t* estimated_bandwidth) const; + bool GetEstimatedReceiveBandwidth(int channel_id, + uint32_t* estimated_bandwidth) const; + private: // Creates a channel object connected to |vie_encoder|. Assumed to be called // protected. @@ -107,7 +119,7 @@ void ReturnChannelId(int channel_id); // Returns the iterator to the ChannelGroup containing |channel_id|. - ChannelGroup* FindGroup(int channel_id); + ChannelGroup* FindGroup(int channel_id) const; // Returns true if at least one other channels uses the same ViEEncoder as // channel_id. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_codec_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_codec_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_codec_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_codec_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -15,7 +15,6 @@ #include "webrtc/engine_configurations.h" #include "webrtc/modules/video_coding/main/interface/video_coding.h" #include "webrtc/system_wrappers/interface/logging.h" -#include "webrtc/system_wrappers/interface/trace.h" #include "webrtc/video_engine/include/vie_errors.h" #include "webrtc/video_engine/vie_capturer.h" #include "webrtc/video_engine/vie_channel.h" @@ -28,6 +27,70 @@ namespace webrtc { +static void LogCodec(const VideoCodec& codec) { + LOG(LS_INFO) << "CodecType " << codec.codecType + << ", pl_type " << static_cast(codec.plType) + << ", resolution " << codec.width + << " x " << codec.height + << ", start br " << codec.startBitrate + << ", min br " << codec.minBitrate + << ", max br " << codec.maxBitrate + << ", max fps " << static_cast(codec.maxFramerate) + << ", max qp " << codec.qpMax + << ", number of streams " + << static_cast(codec.numberOfSimulcastStreams); + if (codec.codecType == kVideoCodecVP8) { + LOG(LS_INFO) << "VP8 specific settings"; + LOG(LS_INFO) << "pictureLossIndicationOn " + << codec.codecSpecific.VP8.pictureLossIndicationOn + << ", feedbackModeOn " + << codec.codecSpecific.VP8.feedbackModeOn + << ", complexity " + << codec.codecSpecific.VP8.complexity + << ", resilience " + << codec.codecSpecific.VP8.resilience + << ", numberOfTemporalLayers " + << static_cast( + codec.codecSpecific.VP8.numberOfTemporalLayers) + << ", keyFrameinterval " + << codec.codecSpecific.VP8.keyFrameInterval; + for (int idx = 0; idx < codec.numberOfSimulcastStreams; ++idx) { + LOG(LS_INFO) << "Stream " << codec.simulcastStream[idx].width + << " x " << codec.simulcastStream[idx].height; + LOG(LS_INFO) << "Temporal layers " + << static_cast( + codec.simulcastStream[idx].numberOfTemporalLayers) + << ", min br " + << codec.simulcastStream[idx].minBitrate + << ", target br " + << codec.simulcastStream[idx].targetBitrate + << ", max br " + << codec.simulcastStream[idx].maxBitrate + << ", qp max " + << codec.simulcastStream[idx].qpMax; + } + } else if (codec.codecType == kVideoCodecH264) { + LOG(LS_INFO) << "H264 specific settings"; + LOG(LS_INFO) << "profile: " + << codec.codecSpecific.H264.profile + << ", constraints: " + << codec.codecSpecific.H264.constraints + << ", level: " + << codec.codecSpecific.H264.level/10.0 + << ", packetizationMode: " + << codec.codecSpecific.H264.packetizationMode + << ", framedropping: " + << codec.codecSpecific.H264.frameDroppingOn + << ", keyFrameInterval: " + << codec.codecSpecific.H264.keyFrameInterval + << ", spslen: " + << codec.codecSpecific.H264.spsLen + << ", ppslen: " + << codec.codecSpecific.H264.ppsLen; + } +} + + ViECodec* ViECodec::GetInterface(VideoEngine* video_engine) { #ifdef WEBRTC_VIDEO_ENGINE_CODEC_API if (!video_engine) { @@ -44,45 +107,33 @@ } int ViECodecImpl::Release() { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, shared_data_->instance_id(), - "ViECodecImpl::Release()"); + LOG(LS_INFO) << "ViECodec::Release."; // Decrease ref count. (*this)--; int32_t ref_count = GetCount(); if (ref_count < 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, shared_data_->instance_id(), - "ViECodec released too many times"); + LOG(LS_WARNING) << "ViECodec released too many times."; shared_data_->SetLastError(kViEAPIDoesNotExist); return -1; } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, shared_data_->instance_id(), - "ViECodec reference count: %d", ref_count); return ref_count; } ViECodecImpl::ViECodecImpl(ViESharedData* shared_data) : shared_data_(shared_data) { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_->instance_id(), - "ViECodecImpl::ViECodecImpl() Ctor"); } ViECodecImpl::~ViECodecImpl() { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_->instance_id(), - "ViECodecImpl::~ViECodecImpl() Dtor"); } int ViECodecImpl::NumberOfCodecs() const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s", __FUNCTION__); // +2 because of FEC(RED and ULPFEC) return static_cast((VideoCodingModule::NumberOfCodecs() + 2)); } int ViECodecImpl::GetCodec(const unsigned char list_number, VideoCodec& video_codec) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(list_number: %d)", __FUNCTION__, list_number); if (list_number == VideoCodingModule::NumberOfCodecs()) { memset(&video_codec, 0, sizeof(VideoCodec)); strcpy(video_codec.plName, "red"); @@ -94,9 +145,6 @@ video_codec.codecType = kVideoCodecULPFEC; video_codec.plType = VCM_ULPFEC_PAYLOAD_TYPE; } else if (VideoCodingModule::Codec(list_number, &video_codec) != VCM_OK) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s: Could not get codec for list_number: %u", __FUNCTION__, - list_number); shared_data_->SetLastError(kViECodecInvalidArgument); return -1; } @@ -105,49 +153,8 @@ int ViECodecImpl::SetSendCodec(const int video_channel, const VideoCodec& video_codec) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(video_channel: %d, codec_type: %d)", __FUNCTION__, - video_channel, video_codec.codecType); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: codec: %d, pl_type: %d, width: %d, height: %d, bitrate: %d" - "maxBr: %d, min_br: %d, frame_rate: %d, qpMax: %u," - "numberOfSimulcastStreams: %u )", __FUNCTION__, - video_codec.codecType, video_codec.plType, video_codec.width, - video_codec.height, video_codec.startBitrate, - video_codec.maxBitrate, video_codec.minBitrate, - video_codec.maxFramerate, video_codec.qpMax, - video_codec.numberOfSimulcastStreams); - if (video_codec.codecType == kVideoCodecVP8) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "pictureLossIndicationOn: %d, feedbackModeOn: %d, " - "complexity: %d, resilience: %d, numberOfTemporalLayers: %u" - "keyFrameInterval %d", - video_codec.codecSpecific.VP8.pictureLossIndicationOn, - video_codec.codecSpecific.VP8.feedbackModeOn, - video_codec.codecSpecific.VP8.complexity, - video_codec.codecSpecific.VP8.resilience, - video_codec.codecSpecific.VP8.numberOfTemporalLayers, - video_codec.codecSpecific.VP8.keyFrameInterval); - } - if (video_codec.codecType == kVideoCodecH264) { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "profile: 0x%02x, constraints: 0x%02x, level 0x%02x (%1.1f), " - "packetizationMode: %u, frameDropping %d, " - "keyFrameInterval %d, SPS len %d, PPS len %d", - video_codec.codecSpecific.H264.profile, - video_codec.codecSpecific.H264.constraints, - video_codec.codecSpecific.H264.level, - video_codec.codecSpecific.H264.level/10.0, - video_codec.codecSpecific.H264.packetizationMode, - video_codec.codecSpecific.H264.frameDroppingOn, - video_codec.codecSpecific.H264.keyFrameInterval, - video_codec.codecSpecific.H264.spsLen, - video_codec.codecSpecific.H264.ppsLen); - } + LOG(LS_INFO) << "SetSendCodec for channel " << video_channel; + LogCodec(video_codec); if (!CodecValid(video_codec)) { // Error logged. shared_data_->SetLastError(kViECodecInvalidCodec); @@ -157,9 +164,6 @@ ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } @@ -167,9 +171,7 @@ ViEEncoder* vie_encoder = cs.Encoder(video_channel); assert(vie_encoder); if (vie_encoder->Owner() != video_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Receive only channel %d", __FUNCTION__, video_channel); + LOG_F(LS_ERROR) << "Receive only channel."; shared_data_->SetLastError(kViECodecReceiveOnlyChannel); return -1; } @@ -182,12 +184,12 @@ video_codec_internal.height * video_codec_internal.maxFramerate) / 1000; - WEBRTC_TRACE(kTraceInfo, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: New max bitrate set to %d kbps", __FUNCTION__, - video_codec_internal.maxBitrate); + LOG(LS_INFO) << "New max bitrate set " << video_codec_internal.maxBitrate; } + if (video_codec_internal.startBitrate < video_codec_internal.minBitrate) { + video_codec_internal.startBitrate = video_codec_internal.minBitrate; + } if (video_codec_internal.startBitrate > video_codec_internal.maxBitrate) { video_codec_internal.startBitrate = video_codec_internal.maxBitrate; } @@ -208,10 +210,6 @@ vie_encoder->Pause(); if (vie_encoder->SetEncoder(video_codec_internal) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Could not change encoder for channel %d", __FUNCTION__, - video_channel); shared_data_->SetLastError(kViECodecUnknownError); return -1; } @@ -223,10 +221,6 @@ ++it) { bool ret = true; if ((*it)->SetSendCodec(video_codec_internal, new_rtp_stream) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Could not set send codec for channel %d", __FUNCTION__, - video_channel); ret = false; } if (!ret) { @@ -241,9 +235,7 @@ if (video_codec_internal.numberOfSimulcastStreams == 0) { unsigned int ssrc = 0; if (vie_channel->GetLocalSSRC(0, &ssrc) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Could not get ssrc", __FUNCTION__); + LOG_F(LS_ERROR) << "Could not get ssrc."; } ssrcs.push_back(ssrc); } else { @@ -251,9 +243,7 @@ ++idx) { unsigned int ssrc = 0; if (vie_channel->GetLocalSSRC(idx, &ssrc) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Could not get ssrc for idx %d", __FUNCTION__, idx); + LOG_F(LS_ERROR) << "Could not get ssrc for stream " << idx; } ssrcs.push_back(ssrc); } @@ -280,16 +270,9 @@ int ViECodecImpl::GetSendCodec(const int video_channel, VideoCodec& video_codec) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(video_channel: %d)", __FUNCTION__, video_channel); - ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No encoder for channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } @@ -298,21 +281,11 @@ int ViECodecImpl::SetReceiveCodec(const int video_channel, const VideoCodec& video_codec) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(video_channel: %d, codec_type: %d)", __FUNCTION__, - video_channel, video_codec.codecType); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: codec: %d, pl_type: %d, width: %d, height: %d, bitrate: %d," - "maxBr: %d, min_br: %d, frame_rate: %d", __FUNCTION__, - video_codec.codecType, video_codec.plType, video_codec.width, - video_codec.height, video_codec.startBitrate, - video_codec.maxBitrate, video_codec.minBitrate, - video_codec.maxFramerate); + LOG(LS_INFO) << "SetReceiveCodec for channel " << video_channel; + LOG(LS_INFO) << "Codec type " << video_codec.codecType + << ", payload type " << video_codec.plType; if (CodecValid(video_codec) == false) { - // Error logged. shared_data_->SetLastError(kViECodecInvalidCodec); return -1; } @@ -320,18 +293,11 @@ ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } if (vie_channel->SetReceiveCodec(video_codec) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Could not set receive codec for channel %d", - __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecUnknownError); return -1; } @@ -340,17 +306,9 @@ int ViECodecImpl::GetReceiveCodec(const int video_channel, VideoCodec& video_codec) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(video_channel: %d, codec_type: %d)", __FUNCTION__, - video_channel, video_codec.codecType); - ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } @@ -366,16 +324,11 @@ const int video_channel, unsigned char config_parameters[kConfigParameterSize], unsigned char& config_parameters_size) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(video_channel: %d)", __FUNCTION__, video_channel); + LOG(LS_INFO) << "GetCodecConfigParameters " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No encoder for channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } @@ -390,17 +343,12 @@ int ViECodecImpl::SetImageScaleStatus(const int video_channel, const bool enable) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(video_channel: %d, enable: %d)", __FUNCTION__, video_channel, - enable); + LOG(LS_INFO) << "SetImageScaleStates for channel " << video_channel + << ", enable: " << enable; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } @@ -415,17 +363,9 @@ int ViECodecImpl::GetSendCodecStatistics(const int video_channel, unsigned int& key_frames, unsigned int& delta_frames) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(video_channel %d)", __FUNCTION__, video_channel); - ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No send codec for channel %d", __FUNCTION__, - video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } @@ -440,17 +380,9 @@ int ViECodecImpl::GetReceiveCodecStatistics(const int video_channel, unsigned int& key_frames, unsigned int& delta_frames) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(video_channel: %d)", __FUNCTION__, - video_channel); - ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } @@ -463,20 +395,11 @@ int ViECodecImpl::GetReceiveSideDelay(const int video_channel, int* delay_ms) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(video_channel: %d)", __FUNCTION__, video_channel); - if (delay_ms == NULL) { - LOG_F(LS_ERROR) << "NULL pointer argument."; - return -1; - } + assert(delay_ms != NULL); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } @@ -490,54 +413,33 @@ int ViECodecImpl::GetCodecTargetBitrate(const int video_channel, unsigned int* bitrate) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(video_channel: %d, codec_type: %d)", __FUNCTION__, - video_channel); - ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No send codec for channel %d", __FUNCTION__, - video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } return vie_encoder->CodecTargetBitrate(static_cast(bitrate)); } -unsigned int ViECodecImpl::GetDiscardedPackets(const int video_channel) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(video_channel: %d, codec_type: %d)", __FUNCTION__, - video_channel); - +int ViECodecImpl::GetNumDiscardedPackets(int video_channel) const { ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } - return vie_channel->DiscardedPackets(); + return static_cast(vie_channel->DiscardedPackets()); } int ViECodecImpl::SetKeyFrameRequestCallbackStatus(const int video_channel, const bool enable) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(video_channel: %d)", __FUNCTION__, video_channel); + LOG(LS_INFO) << "SetKeyFrameRequestCallbackStatus for " << video_channel + << ", enable " << enable; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } @@ -551,17 +453,13 @@ int ViECodecImpl::SetSignalKeyPacketLossStatus(const int video_channel, const bool enable, const bool only_key_frames) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(video_channel: %d, enable: %d, only_key_frames: %d)", - __FUNCTION__, video_channel, enable); + LOG(LS_INFO) << "SetSignalKeyPacketLossStatus for " << video_channel + << "enable, " << enable + << ", only key frames " << only_key_frames; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } @@ -574,23 +472,15 @@ int ViECodecImpl::RegisterEncoderObserver(const int video_channel, ViEEncoderObserver& observer) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s", __FUNCTION__); + LOG(LS_INFO) << "RegisterEncoderObserver for channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No encoder for channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } if (vie_encoder->RegisterCodecObserver(&observer) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Could not register codec observer at channel", - __FUNCTION__); shared_data_->SetLastError(kViECodecObserverAlreadyRegistered); return -1; } @@ -598,15 +488,11 @@ } int ViECodecImpl::DeregisterEncoderObserver(const int video_channel) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s", __FUNCTION__); + LOG(LS_INFO) << "DeregisterEncoderObserver for channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No encoder for channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } @@ -619,23 +505,15 @@ int ViECodecImpl::RegisterDecoderObserver(const int video_channel, ViEDecoderObserver& observer) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s", __FUNCTION__); + LOG(LS_INFO) << "RegisterDecoderObserver for channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } if (vie_channel->RegisterCodecObserver(&observer) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Could not register codec observer at channel", - __FUNCTION__); shared_data_->SetLastError(kViECodecObserverAlreadyRegistered); return -1; } @@ -643,16 +521,11 @@ } int ViECodecImpl::DeregisterDecoderObserver(const int video_channel) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id()), "%s", - __FUNCTION__); + LOG(LS_INFO) << "DeregisterDecodeObserver for channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } @@ -664,15 +537,11 @@ } int ViECodecImpl::SendKeyFrame(const int video_channel) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(video_channel: %d)", __FUNCTION__, video_channel); + LOG(LS_INFO) << "SendKeyFrame on channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } @@ -685,17 +554,12 @@ int ViECodecImpl::WaitForFirstKeyFrame(const int video_channel, const bool wait) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id()), - "%s(video_channel: %d, wait: %d)", __FUNCTION__, video_channel, - wait); + LOG(LS_INFO) << "WaitForFirstKeyFrame for channel " << video_channel + << ", wait " << wait; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return -1; } @@ -708,44 +572,35 @@ int ViECodecImpl::StartDebugRecording(int video_channel, const char* file_name_utf8) { + LOG(LS_INFO) << "StartDebugRecording for channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No encoder %d", __FUNCTION__, video_channel); return -1; } return vie_encoder->StartDebugRecording(file_name_utf8); } int ViECodecImpl::StopDebugRecording(int video_channel) { + LOG(LS_INFO) << "StopDebugRecording for channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No encoder %d", __FUNCTION__, video_channel); return -1; } return vie_encoder->StopDebugRecording(); } void ViECodecImpl::SuspendBelowMinBitrate(int video_channel) { + LOG(LS_INFO) << "SuspendBelowMinBitrate for channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No encoder %d", __FUNCTION__, video_channel); return; } vie_encoder->SuspendBelowMinBitrate(); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No channel %d", __FUNCTION__, video_channel); return; } // Must enable pacing when enabling SuspendBelowMinBitrate. Otherwise, no @@ -759,9 +614,6 @@ ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: No channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidChannelId); return false; } @@ -779,8 +631,7 @@ // We only care about the type and name for red. return true; } - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Codec type doesn't match pl_name", video_codec.plType); + LOG_F(LS_ERROR) << "Invalid RED configuration."; return false; } else if (video_codec.codecType == kVideoCodecULPFEC) { #if defined(WIN32) @@ -791,43 +642,40 @@ // We only care about the type and name for ULPFEC. return true; } - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Codec type doesn't match pl_name", video_codec.plType); + LOG_F(LS_ERROR) << "Invalid ULPFEC configuration."; return false; } else if ((video_codec.codecType == kVideoCodecVP8 && strncmp(video_codec.plName, "VP8", 4) == 0) || - (video_codec.codecType == kVideoCodecH264 && - strncmp(video_codec.plName, "H264", 4) == 0) || + (video_codec.codecType == kVideoCodecVP9 && + strncmp(video_codec.plName, "VP9", 4) == 0) || (video_codec.codecType == kVideoCodecI420 && - strncmp(video_codec.plName, "I420", 4) == 0)) { + strncmp(video_codec.plName, "I420", 4) == 0) || + (video_codec.codecType == kVideoCodecH264 && + strncmp(video_codec.plName, "H264", 4) == 0)) { // OK. } else if (video_codec.codecType != kVideoCodecGeneric) { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Codec type doesn't match pl_name", video_codec.plType); + LOG(LS_ERROR) << "Codec type and name mismatch."; return false; } if (video_codec.plType == 0 || video_codec.plType > 127) { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Invalid codec payload type: %d", video_codec.plType); + LOG(LS_ERROR) << "Invalif payload type: " << video_codec.plType; return false; } if (video_codec.width > kViEMaxCodecWidth || video_codec.height > kViEMaxCodecHeight) { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Invalid codec size: %u x %u", - video_codec.width, video_codec.height); + LOG(LS_ERROR) << "Invalid codec resolution " << video_codec.width + << " x " << video_codec.height; return false; } if (video_codec.startBitrate < kViEMinCodecBitrate) { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Invalid start_bitrate: %u", - video_codec.startBitrate); + LOG(LS_ERROR) << "Invalid start bitrate."; return false; } if (video_codec.minBitrate < kViEMinCodecBitrate) { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Invalid min_bitrate: %u", - video_codec.minBitrate); + LOG(LS_ERROR) << "Invalid min bitrate."; return false; } return true; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_codec_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_codec_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_codec_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_codec_impl.h 2015-02-03 14:33:38.000000000 +0000 @@ -53,7 +53,7 @@ int* delay_ms) const; virtual int GetCodecTargetBitrate(const int video_channel, unsigned int* bitrate) const; - virtual unsigned int GetDiscardedPackets(const int video_channel) const; + virtual int GetNumDiscardedPackets(int video_channel) const; virtual int SetKeyFrameRequestCallbackStatus(const int video_channel, const bool enable); virtual int SetSignalKeyPacketLossStatus(const int video_channel, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_codec_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_codec_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_codec_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_codec_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/common_types.h" + +namespace webrtc { + +// Builds VP8 codec with 0 simulcast streams. +void BuildVP8Codec(webrtc::VideoCodec* video_codec) { + video_codec->codecType = kVideoCodecVP8; + strncpy(video_codec->plName, "VP8", 4); + video_codec->plType = 100; + video_codec->width = 1280; + video_codec->height = 720; + + video_codec->startBitrate = 1000; // kbps + video_codec->maxBitrate = 2000; // kbps + video_codec->minBitrate = 1000; // kbps + video_codec->maxFramerate = 30; + + video_codec->qpMax = 50; + video_codec->numberOfSimulcastStreams = 0; + video_codec->mode = kRealtimeVideo; + + // Set VP8 codec specific info. + video_codec->codecSpecific.VP8.pictureLossIndicationOn = true; + video_codec->codecSpecific.VP8.feedbackModeOn = true; + video_codec->codecSpecific.VP8.complexity = kComplexityNormal; + video_codec->codecSpecific.VP8.resilience = kResilienceOff; + video_codec->codecSpecific.VP8.numberOfTemporalLayers = 0; + video_codec->codecSpecific.VP8.denoisingOn = true; + video_codec->codecSpecific.VP8.errorConcealmentOn = true; + video_codec->codecSpecific.VP8.automaticResizeOn = true; + video_codec->codecSpecific.VP8.frameDroppingOn = true; + video_codec->codecSpecific.VP8.keyFrameInterval = 200; +} + + +void SetSimulcastSettings(webrtc::VideoCodec* video_codec) { + // Simulcast settings. + video_codec->numberOfSimulcastStreams = 1; + video_codec->simulcastStream[0].width = 320; + video_codec->simulcastStream[0].height = 180; + video_codec->simulcastStream[0].numberOfTemporalLayers = 0; + video_codec->simulcastStream[0].maxBitrate = 100; + video_codec->simulcastStream[0].targetBitrate = 100; + video_codec->simulcastStream[0].minBitrate = 0; + video_codec->simulcastStream[0].qpMax = video_codec->qpMax; +} + + +// This test compares two VideoCodecInst objects except codec specific and +// simulcast streams. +TEST(ViECodecTest, TestCompareCodecs) { + VideoCodec codec1, codec2; + memset(&codec1, 0, sizeof(VideoCodec)); + memset(&codec2, 0, sizeof(VideoCodec)); + + BuildVP8Codec(&codec1); + BuildVP8Codec(&codec2); + + EXPECT_TRUE(codec1 == codec2); + EXPECT_FALSE(codec1 != codec2); + + // plname is case insensitive. + strncpy(codec2.plName, "vp8", 4); + EXPECT_TRUE(codec1 == codec2); + + codec2.codecType = kVideoCodecUnknown; + EXPECT_FALSE(codec1 == codec2); + + // Modify pltype. + BuildVP8Codec(&codec2); + codec2.plType = 101; + EXPECT_FALSE(codec1 == codec2); + + // Modifing height and width. + BuildVP8Codec(&codec2); + codec2.width = 640; + codec2.height = 480; + EXPECT_FALSE(codec1 == codec2); + + // Modify framerate, default value is 30. + BuildVP8Codec(&codec2); + codec2.maxFramerate = 15; + EXPECT_FALSE(codec1 == codec2); + + // Modifying startBitrate, default value is 1000 kbps. + BuildVP8Codec(&codec2); + codec2.startBitrate = 2000; + EXPECT_FALSE(codec1 == codec2); + // maxBitrate + BuildVP8Codec(&codec2); + codec2.startBitrate = 3000; + EXPECT_FALSE(codec1 == codec2); + // minBirate + BuildVP8Codec(&codec2); + codec2.startBitrate = 500; + EXPECT_FALSE(codec1 == codec2); + + // Modify qpMax. + BuildVP8Codec(&codec2); + codec2.qpMax = 100; + EXPECT_FALSE(codec1 == codec2); + + // Modify mode + BuildVP8Codec(&codec2); + codec2.mode = kScreensharing; + EXPECT_FALSE(codec1 == codec2); +} + +// Test VP8 specific comparision. +TEST(ViECodecTest, TestCompareVP8CodecSpecific) { + VideoCodec codec1, codec2; + memset(&codec1, 0, sizeof(VideoCodec)); + memset(&codec2, 0, sizeof(VideoCodec)); + + BuildVP8Codec(&codec1); + BuildVP8Codec(&codec2); + EXPECT_TRUE(codec1 == codec2); + + // pictureLossIndicationOn + codec2.codecSpecific.VP8.pictureLossIndicationOn = false; + EXPECT_FALSE(codec1 == codec2); + + // feedbackModeOn + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.feedbackModeOn = false; + EXPECT_FALSE(codec1 == codec2); + + // complexity + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.complexity = kComplexityHigh; + EXPECT_FALSE(codec1 == codec2); + + // resilience + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.resilience = kResilientStream; + EXPECT_FALSE(codec1 == codec2); + + // numberOfTemporalLayers + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.numberOfTemporalLayers = 2; + EXPECT_FALSE(codec1 == codec2); + + // denoisingOn + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.denoisingOn = false; + EXPECT_FALSE(codec1 == codec2); + + // errorConcealmentOn + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.errorConcealmentOn = false; + EXPECT_FALSE(codec1 == codec2); + + // pictureLossIndicationOn + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.automaticResizeOn = false; + EXPECT_FALSE(codec1 == codec2); + + // frameDroppingOn + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.frameDroppingOn = false; + EXPECT_FALSE(codec1 == codec2); + + // keyFrameInterval + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.keyFrameInterval = 100; + EXPECT_FALSE(codec1 == codec2); +} + +// This test compares simulcast stream information in VideoCodec. +TEST(ViECodecTest, TestCompareSimulcastStreams) { + VideoCodec codec1, codec2; + memset(&codec1, 0, sizeof(VideoCodec)); + memset(&codec2, 0, sizeof(VideoCodec)); + + BuildVP8Codec(&codec1); + BuildVP8Codec(&codec2); + // Set simulacast settings. + SetSimulcastSettings(&codec1); + SetSimulcastSettings(&codec2); + EXPECT_TRUE(codec1 == codec2); + + // Modify number of streams. + codec2.numberOfSimulcastStreams = 2; + EXPECT_FALSE(codec1 == codec2); + + // Resetting steram count. + codec2.numberOfSimulcastStreams = 1; + // Modify height and width in codec2. + codec2.simulcastStream[0].width = 640; + codec2.simulcastStream[0].height = 480; + EXPECT_FALSE(codec1 == codec2); + + // numberOfTemporalLayers + SetSimulcastSettings(&codec2); + codec2.simulcastStream[0].numberOfTemporalLayers = 2; + EXPECT_FALSE(codec1 == codec2); + + // maxBitrate + SetSimulcastSettings(&codec2); + codec2.simulcastStream[0].maxBitrate = 1000; + EXPECT_FALSE(codec1 == codec2); + + // targetBitrate + SetSimulcastSettings(&codec2); + codec2.simulcastStream[0].targetBitrate = 1000; + EXPECT_FALSE(codec1 == codec2); + + // minBitrate + SetSimulcastSettings(&codec2); + codec2.simulcastStream[0].minBitrate = 50; + EXPECT_FALSE(codec1 == codec2); + + // qpMax + SetSimulcastSettings(&codec2); + codec2.simulcastStream[0].qpMax = 100; + EXPECT_FALSE(codec1 == codec2); +} + +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_defines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_defines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_defines.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_defines.h 2015-02-03 14:33:38.000000000 +0000 @@ -34,9 +34,7 @@ enum { kViEMinKeyRequestIntervalMs = 300 }; // ViEBase -enum { kViEMaxNumberOfChannels = 32 }; -enum { kViEVersionMaxMessageSize = 1024 }; -enum { kViEMaxModuleVersionSize = 960 }; +enum { kViEMaxNumberOfChannels = 64 }; // ViECapture enum { kViEMaxCaptureDevices = 256 }; @@ -101,33 +99,10 @@ return static_cast(moduleId & 0xffff); } -// Build information macros -#if defined(_DEBUG) || defined(DEBUG) -#define BUILDMODE "d" -#elif defined(NDEBUG) -#define BUILDMODE "r" -#else -#define BUILDMODE "?" -#endif - -#define BUILDTIME __TIME__ -#define BUILDDATE __DATE__ - -// Example: "Oct 10 2002 12:05:30 r". -#define BUILDINFO BUILDDATE " " BUILDTIME " " BUILDMODE - // Windows specific. #if defined(_WIN32) #define RENDER_MODULE_TYPE kRenderWindows - // Warning pragmas. - // new behavior: elements of array 'XXX' will be default initialized. - #pragma warning(disable: 4351) - // 'this' : used in base member initializer list. - #pragma warning(disable: 4355) - // Frame pointer register 'ebp' modified by inline assembly code. - #pragma warning(disable: 4731) - // Include libraries. #pragma comment(lib, "winmm.lib") diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_encoder.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_encoder.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_encoder.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_encoder.cc 2015-02-03 14:33:38.000000000 +0000 @@ -23,10 +23,11 @@ #include "webrtc/modules/video_coding/main/interface/video_coding.h" #include "webrtc/modules/video_coding/main/interface/video_coding_defines.h" #include "webrtc/modules/video_coding/main/source/encoded_frame.h" +#include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/system_wrappers/interface/metrics.h" #include "webrtc/system_wrappers/interface/tick_util.h" -#include "webrtc/system_wrappers/interface/trace.h" #include "webrtc/system_wrappers/interface/trace_event.h" #include "webrtc/video_engine/include/vie_codec.h" #include "webrtc/video_engine/include/vie_image_process.h" @@ -35,16 +36,6 @@ namespace webrtc { -// Pace in kbits/s until we receive first estimate. -static const int kInitialPace = 2000; - -// Pacing-rate relative to our target send rate. -// Multiplicative factor that is applied to the target bitrate to calculate the -// number of bytes that can be transmitted per interval. -// Increasing this factor will result in lower delays in cases of bitrate -// overshoots from the encoder. -static const float kPaceMultiplier = 2.5f; - // Margin on when we pause the encoder when the pacing buffer overflows relative // to the configured buffer delay. static const float kEncoderPausePacerMargin = 2.0f; @@ -119,8 +110,10 @@ : owner_(owner) { } virtual ~ViEPacedSenderCallback() {} - virtual bool TimeToSendPacket(uint32_t ssrc, uint16_t sequence_number, - int64_t capture_time_ms, bool retransmission) { + virtual bool TimeToSendPacket(uint32_t ssrc, + uint16_t sequence_number, + int64_t capture_time_ms, + bool retransmission) { return owner_->TimeToSendPacket(ssrc, sequence_number, capture_time_ms, retransmission); } @@ -154,8 +147,7 @@ : engine_id_(engine_id), channel_id_(channel_id), number_of_cores_(number_of_cores), - vcm_(*webrtc::VideoCodingModule::Create(ViEModuleId(engine_id, - channel_id))), + vcm_(*webrtc::VideoCodingModule::Create()), vpm_(*webrtc::VideoProcessingModule::Create(ViEModuleId(engine_id, channel_id))), callback_cs_(CriticalSectionWrapper::CreateCriticalSection()), @@ -164,6 +156,7 @@ time_of_last_incoming_frame_ms_(0), load_manager_(NULL), send_padding_(false), + min_transmit_bitrate_kbps_(0), target_delay_ms_(0), network_is_transmitting_(true), encoder_paused_(false), @@ -179,12 +172,8 @@ picture_id_rpsi_(0), qm_callback_(NULL), video_suspended_(false), - pre_encode_callback_(NULL) { - WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo, - ViEId(engine_id, channel_id), - "%s(engine_id: %d) 0x%p - Constructor", __FUNCTION__, engine_id, - this); - + pre_encode_callback_(NULL), + start_ms_(Clock::GetRealTimeClock()->TimeInMilliseconds()) { RtpRtcp::Configuration configuration; configuration.id = ViEModuleId(engine_id_, channel_id_); configuration.audio = false; // Video. @@ -193,15 +182,16 @@ bitrate_observer_.reset(new ViEBitrateObserver(this)); pacing_callback_.reset(new ViEPacedSenderCallback(this)); loadstate_observer_.reset(new ViECPULoadStateObserver(this)); - paced_sender_.reset( - new PacedSender(pacing_callback_.get(), kInitialPace, kPaceMultiplier)); + paced_sender_.reset(new PacedSender( + Clock::GetRealTimeClock(), + pacing_callback_.get(), + kDefaultStartBitrateKbps, + PacedSender::kDefaultPaceMultiplier * kDefaultStartBitrateKbps, + 0)); } bool ViEEncoder::Init() { if (vcm_.InitializeSender() != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s InitializeSender failure", __FUNCTION__); return false; } vpm_.EnableTemporalDecimation(true); @@ -212,9 +202,6 @@ if (module_process_thread_.RegisterModule(&vcm_) != 0 || module_process_thread_.RegisterModule(default_rtp_rtcp_.get()) != 0 || module_process_thread_.RegisterModule(paced_sender_.get()) != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s RegisterModule failure", __FUNCTION__); return false; } if (qm_callback_) { @@ -223,11 +210,13 @@ qm_callback_ = new QMVideoSettingsCallback(&vpm_); #ifdef VIDEOCODEC_VP8 + VideoCodecType codec_type = webrtc::kVideoCodecVP8; +#else + VideoCodecType codec_type = webrtc::kVideoCodecI420; +#endif + VideoCodec video_codec; - if (vcm_.Codec(webrtc::kVideoCodecVP8, &video_codec) != VCM_OK) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s Codec failure", __FUNCTION__); + if (vcm_.Codec(codec_type, &video_codec) != VCM_OK) { return false; } { @@ -236,48 +225,18 @@ } if (vcm_.RegisterSendCodec(&video_codec, number_of_cores_, default_rtp_rtcp_->MaxDataPayloadLength()) != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s RegisterSendCodec failure", __FUNCTION__); return false; } if (default_rtp_rtcp_->RegisterSendPayload(video_codec) != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s RegisterSendPayload failure", __FUNCTION__); - return false; - } -#else - VideoCodec video_codec; - if (vcm_.Codec(webrtc::kVideoCodecI420, &video_codec) == VCM_OK) { - { - CriticalSectionScoped cs(data_cs_.get()); - send_padding_ = video_codec.numberOfSimulcastStreams > 1; - } - vcm_.RegisterSendCodec(&video_codec, number_of_cores_, - default_rtp_rtcp_->MaxDataPayloadLength()); - default_rtp_rtcp_->RegisterSendPayload(video_codec); - } else { return false; } -#endif - if (vcm_.RegisterTransportCallback(this) != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "ViEEncoder: VCM::RegisterTransportCallback failure"); return false; } if (vcm_.RegisterSendStatisticsCallback(this) != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "ViEEncoder: VCM::RegisterSendStatisticsCallback failure"); return false; } if (vcm_.RegisterVideoQMCallback(qm_callback_) != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "VCM::RegisterQMCallback failure"); return false; } return true; @@ -292,9 +251,7 @@ } ViEEncoder::~ViEEncoder() { - WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "ViEEncoder Destructor 0x%p, engine_id: %d", this, engine_id_); + UpdateHistograms(); if (bitrate_controller_) { bitrate_controller_->RemoveBitrateObserver(bitrate_observer_.get()); } @@ -310,15 +267,30 @@ delete qm_callback_; } +void ViEEncoder::UpdateHistograms() { + const float kMinCallLengthInMinutes = 0.5f; + float elapsed_minutes = + (Clock::GetRealTimeClock()->TimeInMilliseconds() - start_ms_) / 60000.0f; + if (elapsed_minutes < kMinCallLengthInMinutes) { + return; + } + webrtc::VCMFrameCount frames; + if (vcm_.SentFrameCount(frames) != VCM_OK) { + return; + } + uint32_t total_frames = frames.numKeyFrames + frames.numDeltaFrames; + if (total_frames > 0) { + RTC_HISTOGRAM_COUNTS_1000("WebRTC.Video.KeyFramesSentInPermille", + static_cast( + (frames.numKeyFrames * 1000.0f / total_frames) + 0.5f)); + } +} + int ViEEncoder::Owner() const { return channel_id_; } void ViEEncoder::SetNetworkTransmissionState(bool is_transmitting) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s(%s)", __FUNCTION__, - is_transmitting ? "transmitting" : "not transmitting"); { CriticalSectionScoped cs(data_cs_.get()); network_is_transmitting_ = is_transmitting; @@ -331,17 +303,11 @@ } void ViEEncoder::Pause() { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s", __FUNCTION__); CriticalSectionScoped cs(data_cs_.get()); encoder_paused_ = true; } void ViEEncoder::Restart() { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s", __FUNCTION__); CriticalSectionScoped cs(data_cs_.get()); encoder_paused_ = false; } @@ -352,9 +318,6 @@ int32_t ViEEncoder::GetCodec(uint8_t list_index, VideoCodec* video_codec) { if (vcm_.Codec(list_index, video_codec) != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s: Could not get codec", - __FUNCTION__); return -1; } return 0; @@ -363,43 +326,27 @@ int32_t ViEEncoder::RegisterExternalEncoder(webrtc::VideoEncoder* encoder, uint8_t pl_type, bool internal_source) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s: pltype %u", __FUNCTION__, - pl_type); - if (encoder == NULL) return -1; if (vcm_.RegisterExternalEncoder(encoder, pl_type, internal_source) != - VCM_OK) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "Could not register external encoder"); + VCM_OK) { return -1; } return 0; } int32_t ViEEncoder::DeRegisterExternalEncoder(uint8_t pl_type) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s: pltype %u", __FUNCTION__, pl_type); - webrtc::VideoCodec current_send_codec; if (vcm_.SendCodec(¤t_send_codec) == VCM_OK) { uint32_t current_bitrate_bps = 0; if (vcm_.Bitrate(¤t_bitrate_bps) != 0) { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "Failed to get the current encoder target bitrate."); + LOG(LS_WARNING) << "Failed to get the current encoder target bitrate."; } current_send_codec.startBitrate = (current_bitrate_bps + 500) / 1000; } if (vcm_.RegisterExternalEncoder(NULL, pl_type) != VCM_OK) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "Could not deregister external encoder"); return -1; } @@ -419,33 +366,23 @@ current_send_codec.extra_options = NULL; if (vcm_.RegisterSendCodec(¤t_send_codec, number_of_cores_, max_data_payload_length) != VCM_OK) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "Could not use internal encoder"); - return -1; + LOG(LS_INFO) << "De-registered the currently used external encoder (" + << static_cast(pl_type) << ") and therefore tried to " + << "register the corresponding internal encoder, but none " + << "was supported."; } } return 0; } int32_t ViEEncoder::SetEncoder(const webrtc::VideoCodec& video_codec) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s: CodecType: %d, width: %u, height: %u", __FUNCTION__, - video_codec.codecType, video_codec.width, video_codec.height); // Setting target width and height for VPM. if (vpm_.SetTargetResolution(video_codec.width, video_codec.height, video_codec.maxFramerate) != VPM_OK) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "Could not set VPM target dimensions"); return -1; } if (default_rtp_rtcp_->RegisterSendPayload(video_codec) != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "Could register RTP module video payload"); return -1; } // Convert from kbps to bps. @@ -464,42 +401,36 @@ } if (vcm_.RegisterSendCodec(&video_codec, number_of_cores_, max_data_payload_length) != VCM_OK) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "Could not register send codec"); return -1; } // Set this module as sending right away, let the slave module in the channel // start and stop sending. - if (default_rtp_rtcp_->Sending() == false) { - if (default_rtp_rtcp_->SetSendingStatus(true) != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "Could start RTP module sending"); - return -1; - } + if (default_rtp_rtcp_->SetSendingStatus(true) != 0) { + return -1; } + bitrate_controller_->SetBitrateObserver(bitrate_observer_.get(), video_codec.startBitrate * 1000, video_codec.minBitrate * 1000, kTransmissionMaxBitrateMultiplier * video_codec.maxBitrate * 1000); - paced_sender_->UpdateBitrate(video_codec.startBitrate, - video_codec.startBitrate, - video_codec.startBitrate); + CriticalSectionScoped crit(data_cs_.get()); + int pad_up_to_bitrate_kbps = video_codec.startBitrate; + if (pad_up_to_bitrate_kbps < min_transmit_bitrate_kbps_) + pad_up_to_bitrate_kbps = min_transmit_bitrate_kbps_; + + paced_sender_->UpdateBitrate( + video_codec.startBitrate, + PacedSender::kDefaultPaceMultiplier * video_codec.startBitrate, + pad_up_to_bitrate_kbps); + return 0; } int32_t ViEEncoder::GetEncoder(VideoCodec* video_codec) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s", __FUNCTION__); - if (vcm_.SendCodec(video_codec) != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "Could not get VCM send codec"); return -1; } return 0; @@ -508,16 +439,10 @@ int32_t ViEEncoder::GetCodecConfigParameters( unsigned char config_parameters[kConfigParameterSize], unsigned char& config_parameters_size) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s", __FUNCTION__); - int32_t num_parameters = vcm_.CodecConfigParameters(config_parameters, kConfigParameterSize); if (num_parameters <= 0) { config_parameters_size = 0; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "Could not get config parameters"); return -1; } config_parameters_size = static_cast(num_parameters); @@ -525,16 +450,11 @@ } int32_t ViEEncoder::ScaleInputImage(bool enable) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s(enable %d)", __FUNCTION__, - enable); - VideoFrameResampling resampling_mode = kFastRescaling; - if (enable == true) { + // TODO(mflodman) What? + if (enable) { // kInterpolation is currently not supported. - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s not supported", - __FUNCTION__, enable); + LOG_F(LS_ERROR) << "Not supported."; return -1; } vpm_.SetInputFrameResampleMode(resampling_mode); @@ -554,7 +474,8 @@ bool send_padding; { CriticalSectionScoped cs(data_cs_.get()); - send_padding = send_padding_ || video_suspended_; + send_padding = + send_padding_ || video_suspended_ || min_transmit_bitrate_kbps_ > 0; } if (send_padding) { return default_rtp_rtcp_->TimeToSendPadding(bytes); @@ -576,13 +497,32 @@ std::max(static_cast(target_delay_ms_ * kEncoderPausePacerMargin), kMinPacingDelayMs); } + if (paced_sender_->ExpectedQueueTimeMs() > + PacedSender::kDefaultMaxQueueLengthMs) { + // Too much data in pacer queue, drop frame. + return true; + } return !network_is_transmitting_; } -RtpRtcp* ViEEncoder::SendRtpRtcpModule() { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s", __FUNCTION__); +void ViEEncoder::TraceFrameDropStart() { + // Start trace event only on the first frame after encoder is paused. + if (!encoder_paused_and_dropped_frame_) { + TRACE_EVENT_ASYNC_BEGIN0("webrtc", "EncoderPaused", this); + } + encoder_paused_and_dropped_frame_ = true; + return; +} + +void ViEEncoder::TraceFrameDropEnd() { + // End trace event on first frame after encoder resumes, if frame was dropped. + if (encoder_paused_and_dropped_frame_) { + TRACE_EVENT_ASYNC_END0("webrtc", "EncoderPaused", this); + } + encoder_paused_and_dropped_frame_ = false; +} +RtpRtcp* ViEEncoder::SendRtpRtcpModule() { return default_rtp_rtcp_.get(); } @@ -590,29 +530,18 @@ I420VideoFrame* video_frame, int num_csrcs, const uint32_t CSRC[kRtpCsrcSize]) { - WEBRTC_TRACE(webrtc::kTraceStream, - webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s: %llu", __FUNCTION__, - video_frame->timestamp()); + if (default_rtp_rtcp_->SendingMedia() == false) { + // We've paused or we have no channels attached, don't encode. + return; + } { CriticalSectionScoped cs(data_cs_.get()); time_of_last_incoming_frame_ms_ = TickTime::MillisecondTimestamp(); - if (default_rtp_rtcp_->SendingMedia() == false) { - // We've paused or we have no channels attached, don't encode. - return; - } if (EncoderPaused()) { - if (!encoder_paused_and_dropped_frame_) { - TRACE_EVENT_ASYNC_BEGIN0("webrtc", "EncoderPaused", this); - } - encoder_paused_and_dropped_frame_ = true; + TraceFrameDropStart(); return; } - if (encoder_paused_and_dropped_frame_) { - TRACE_EVENT_ASYNC_END0("webrtc", "EncoderPaused", this); - } - encoder_paused_and_dropped_frame_ = false; + TraceFrameDropEnd(); } // Convert render time, in ms, to RTP timestamp. @@ -624,21 +553,6 @@ TRACE_EVENT_ASYNC_STEP0("webrtc", "Video", video_frame->render_time_ms(), "Encode"); video_frame->set_timestamp(time_stamp); - { - CriticalSectionScoped cs(callback_cs_.get()); - if (effect_filter_) { - unsigned int length = CalcBufferSize(kI420, - video_frame->width(), - video_frame->height()); - scoped_array video_buffer(new uint8_t[length]); - ExtractBuffer(*video_frame, length, video_buffer.get()); - effect_filter_->Transform(length, - video_buffer.get(), - video_frame->timestamp(), - video_frame->width(), - video_frame->height()); - } - } // Make sure the CSRC list is correct. if (num_csrcs > 0) { @@ -652,22 +566,37 @@ } default_rtp_rtcp_->SetCSRCs(tempCSRC, (uint8_t) num_csrcs); } - // Pass frame via preprocessor. + I420VideoFrame* decimated_frame = NULL; - const int ret = vpm_.PreprocessFrame(*video_frame, &decimated_frame); - if (ret == 1) { - // Drop this frame. - return; - } - if (ret != VPM_OK) { - WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s: Error preprocessing frame %u", __FUNCTION__, - video_frame->timestamp()); - return; + // TODO(wuchengli): support texture frames. + if (video_frame->native_handle() == NULL) { + { + CriticalSectionScoped cs(callback_cs_.get()); + if (effect_filter_) { + unsigned int length = + CalcBufferSize(kI420, video_frame->width(), video_frame->height()); + scoped_ptr video_buffer(new uint8_t[length]); + ExtractBuffer(*video_frame, length, video_buffer.get()); + effect_filter_->Transform(length, + video_buffer.get(), + video_frame->ntp_time_ms(), + video_frame->timestamp(), + video_frame->width(), + video_frame->height()); + } + } + + // Pass frame via preprocessor. + const int ret = vpm_.PreprocessFrame(*video_frame, &decimated_frame); + if (ret == 1) { + // Drop this frame. + return; + } + if (ret != VPM_OK) { + return; + } } - // Frame was not sampled => use original. + // If the frame was not resampled or scaled => use original. if (decimated_frame == NULL) { decimated_frame = video_frame; } @@ -678,40 +607,40 @@ pre_encode_callback_->FrameCallback(decimated_frame); } + if (video_frame->native_handle() != NULL) { + // TODO(wuchengli): add texture support. http://crbug.com/362437 + return; + } + #ifdef VIDEOCODEC_VP8 if (vcm_.SendCodec() == webrtc::kVideoCodecVP8) { webrtc::CodecSpecificInfo codec_specific_info; codec_specific_info.codecType = webrtc::kVideoCodecVP8; - codec_specific_info.codecSpecific.VP8.hasReceivedRPSI = - has_received_rpsi_; - codec_specific_info.codecSpecific.VP8.hasReceivedSLI = - has_received_sli_; - codec_specific_info.codecSpecific.VP8.pictureIdRPSI = - picture_id_rpsi_; - codec_specific_info.codecSpecific.VP8.pictureIdSLI = - picture_id_sli_; - has_received_sli_ = false; - has_received_rpsi_ = false; - - if (vcm_.AddVideoFrame(*decimated_frame, - vpm_.ContentMetrics(), - &codec_specific_info) != VCM_OK) { - WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s: Error encoding frame %u", __FUNCTION__, - video_frame->timestamp()); + { + CriticalSectionScoped cs(data_cs_.get()); + codec_specific_info.codecSpecific.VP8.hasReceivedRPSI = + has_received_rpsi_; + codec_specific_info.codecSpecific.VP8.hasReceivedSLI = + has_received_sli_; + codec_specific_info.codecSpecific.VP8.pictureIdRPSI = + picture_id_rpsi_; + codec_specific_info.codecSpecific.VP8.pictureIdSLI = + picture_id_sli_; + has_received_sli_ = false; + has_received_rpsi_ = false; } + + vcm_.AddVideoFrame(*decimated_frame, vpm_.ContentMetrics(), + &codec_specific_info); return; } #endif + #ifdef MOZ_WEBRTC_OMX // XXX effectively disable resolution changes until Bug 1067437 is resolved with new DSP code if (qm_callback_ && vcm_.SendCodec() == webrtc::kVideoCodecH264) { if (vcm_.RegisterVideoQMCallback(NULL) != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "VCM::RegisterQMCallback(NULL) failure"); + LOG_F(LS_ERROR) << "VCM::RegisterQMCallback(NULL) failure"; return; } delete qm_callback_; @@ -719,36 +648,19 @@ } #endif - if (vcm_.AddVideoFrame(*decimated_frame, - vpm_.ContentMetrics()) != VCM_OK) { - WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s: Error encoding frame %u", __FUNCTION__, - video_frame->timestamp()); - } + vcm_.AddVideoFrame(*decimated_frame, vpm_.ContentMetrics()); } void ViEEncoder::DelayChanged(int id, int frame_delay) { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s: %u", __FUNCTION__, - frame_delay); - default_rtp_rtcp_->SetCameraDelay(frame_delay); } int ViEEncoder::GetPreferedFrameSettings(int* width, int* height, int* frame_rate) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s", __FUNCTION__); - webrtc::VideoCodec video_codec; memset(&video_codec, 0, sizeof(video_codec)); if (vcm_.SendCodec(&video_codec) != VCM_OK) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "Could not get VCM send codec"); return -1; } @@ -759,21 +671,13 @@ } int ViEEncoder::SendKeyFrame() { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s", __FUNCTION__); return vcm_.IntraFrameRequest(0); } int32_t ViEEncoder::SendCodecStatistics( uint32_t* num_key_frames, uint32_t* num_delta_frames) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s", __FUNCTION__); - webrtc::VCMFrameCount sent_frames; if (vcm_.SentFrameCount(sent_frames) != VCM_OK) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s: Could not get sent frame information", __FUNCTION__); return -1; } *num_key_frames = sent_frames.numKeyFrames; @@ -781,20 +685,11 @@ return 0; } -int32_t ViEEncoder::EstimatedSendBandwidth( - uint32_t* available_bandwidth) const { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); - - if (!bitrate_controller_->AvailableBandwidth(available_bandwidth)) { - return -1; - } - return 0; +int32_t ViEEncoder::PacerQueuingDelayMs() const { + return paced_sender_->QueueInMs(); } int ViEEncoder::CodecTargetBitrate(uint32_t* bitrate) const { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", - __FUNCTION__); if (vcm_.Bitrate(bitrate) != 0) return -1; return 0; @@ -830,9 +725,6 @@ } if (fec_enabled_ || nack_enabled_) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s: FEC status ", - __FUNCTION__, fec_enabled); vcm_.RegisterProtectionCallback(this); // The send codec must be registered to set correct MTU. webrtc::VideoCodec codec; @@ -840,17 +732,12 @@ uint16_t max_pay_load = default_rtp_rtcp_->MaxDataPayloadLength(); uint32_t current_bitrate_bps = 0; if (vcm_.Bitrate(¤t_bitrate_bps) != 0) { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "Failed to get the current encoder target bitrate."); + LOG_F(LS_WARNING) << + "Failed to get the current encoder target bitrate."; } // Convert to start bitrate in kbps. codec.startBitrate = (current_bitrate_bps + 500) / 1000; if (vcm_.RegisterSendCodec(&codec, number_of_cores_, max_pay_load) != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s: Failed to update Sendcodec when enabling FEC", - __FUNCTION__, fec_enabled); return -1; } } @@ -871,15 +758,10 @@ // Disable external frame-droppers. vcm_.EnableFrameDropper(false); vpm_.EnableTemporalDecimation(false); - // We don't put any limits on the pacer queue when running in buffered mode - // since the encoder will be paused if the queue grow too large. - paced_sender_->set_max_queue_length_ms(-1); } else { // Real-time mode - enable frame droppers. vpm_.EnableTemporalDecimation(true); vcm_.EnableFrameDropper(true); - paced_sender_->set_max_queue_length_ms( - PacedSender::kDefaultMaxQueueLengthMs); } } @@ -909,30 +791,8 @@ uint32_t* sent_video_rate_bps, uint32_t* sent_nack_rate_bps, uint32_t* sent_fec_rate_bps) { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s, deltaFECRate: %u, key_fecrate: %u, " - "delta_use_uep_protection: %d, key_use_uep_protection: %d, " - "delta_max_fec_frames: %d, key_max_fec_frames: %d, " - "delta_mask_type: %d, key_mask_type: %d, ", - __FUNCTION__, - delta_fec_params->fec_rate, - key_fec_params->fec_rate, - delta_fec_params->use_uep_protection, - key_fec_params->use_uep_protection, - delta_fec_params->max_fec_frames, - key_fec_params->max_fec_frames, - delta_fec_params->fec_mask_type, - key_fec_params->fec_mask_type); - if (default_rtp_rtcp_->SetFecParameters(delta_fec_params, - key_fec_params) != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s: Could not update FEC parameters", __FUNCTION__); - } - default_rtp_rtcp_->BitrateSent(NULL, - sent_video_rate_bps, - sent_fec_rate_bps, + default_rtp_rtcp_->SetFecParameters(delta_fec_params, key_fec_params); + default_rtp_rtcp_->BitrateSent(NULL, sent_video_rate_bps, sent_fec_rate_bps, sent_nack_rate_bps); return 0; } @@ -941,9 +801,6 @@ const uint32_t frame_rate) { CriticalSectionScoped cs(callback_cs_.get()); if (codec_observer_) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s: bitrate %u, framerate %u", - __FUNCTION__, bit_rate, frame_rate); codec_observer_->OutgoingRate(channel_id_, frame_rate, bit_rate); } return 0; @@ -951,48 +808,30 @@ int32_t ViEEncoder::RegisterCodecObserver(ViEEncoderObserver* observer) { CriticalSectionScoped cs(callback_cs_.get()); - if (observer) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s: observer added", - __FUNCTION__); - if (codec_observer_) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s: observer already set.", - __FUNCTION__); - return -1; - } - codec_observer_ = observer; - } else { - if (codec_observer_ == NULL) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s: observer does not exist.", __FUNCTION__); - return -1; - } - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s: observer removed", - __FUNCTION__); - codec_observer_ = NULL; + if (observer && codec_observer_) { + LOG_F(LS_ERROR) << "Observer already set."; + return -1; } + codec_observer_ = observer; return 0; } void ViEEncoder::OnReceivedSLI(uint32_t /*ssrc*/, uint8_t picture_id) { + CriticalSectionScoped cs(data_cs_.get()); picture_id_sli_ = picture_id; has_received_sli_ = true; } void ViEEncoder::OnReceivedRPSI(uint32_t /*ssrc*/, uint64_t picture_id) { + CriticalSectionScoped cs(data_cs_.get()); picture_id_rpsi_ = picture_id; has_received_rpsi_ = true; } void ViEEncoder::OnReceivedIntraFrameRequest(uint32_t ssrc) { // Key frame request from remote side, signal to VCM. - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s", __FUNCTION__); TRACE_EVENT0("webrtc", "OnKeyFrameRequest"); int idx = 0; @@ -1012,9 +851,6 @@ int64_t now = TickTime::MillisecondTimestamp(); if (time_last_intra_request_ms_[ssrc] + kViEMinKeyRequestIntervalMs > now) { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s: Not encoding new intra due to timing", __FUNCTION__); return; } time_last_intra_request_ms_[ssrc] = now; @@ -1066,15 +902,19 @@ return true; } +void ViEEncoder::SetMinTransmitBitrate(int min_transmit_bitrate_kbps) { + assert(min_transmit_bitrate_kbps >= 0); + CriticalSectionScoped crit(data_cs_.get()); + min_transmit_bitrate_kbps_ = min_transmit_bitrate_kbps; +} + // Called from ViEBitrateObserver. void ViEEncoder::OnNetworkChanged(const uint32_t bitrate_bps, const uint8_t fraction_lost, const uint32_t round_trip_time_ms) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s(bitrate_bps: %u, fraction_lost: %u, rtt_ms: %u", - __FUNCTION__, bitrate_bps, fraction_lost, round_trip_time_ms); - + LOG(LS_VERBOSE) << "OnNetworkChanged, bitrate" << bitrate_bps + << " packet loss " << fraction_lost + << " rtt " << round_trip_time_ms; vcm_.SetChannelParameters(bitrate_bps, fraction_lost, round_trip_time_ms); bool video_is_suspended = vcm_.VideoSuspended(); int bitrate_kbps = bitrate_bps / 1000; @@ -1091,70 +931,60 @@ // Find the max amount of padding we can allow ourselves to send at this // point, based on which streams are currently active and what our current // available bandwidth is. - int max_padding_bitrate_kbps = 0; int pad_up_to_bitrate_kbps = 0; if (send_codec.numberOfSimulcastStreams == 0) { - max_padding_bitrate_kbps = send_codec.minBitrate; pad_up_to_bitrate_kbps = send_codec.minBitrate; } else { - int i = send_codec.numberOfSimulcastStreams - 1; - for (std::vector::reverse_iterator it = stream_bitrates.rbegin(); - it != stream_bitrates.rend(); ++it) { - if (*it > 0) { - max_padding_bitrate_kbps = std::min((*it + 500) / 1000, - stream_configs[i].minBitrate); - break; - } - --i; - } pad_up_to_bitrate_kbps = stream_configs[send_codec.numberOfSimulcastStreams - 1].minBitrate; for (int i = 0; i < send_codec.numberOfSimulcastStreams - 1; ++i) { pad_up_to_bitrate_kbps += stream_configs[i].targetBitrate; } } - if (video_is_suspended || send_codec.numberOfSimulcastStreams > 1) { - pad_up_to_bitrate_kbps = std::min(bitrate_kbps, pad_up_to_bitrate_kbps); - } else { - // Disable padding if only sending one stream and video isn't suspended. + + // Disable padding if only sending one stream and video isn't suspended and + // min-transmit bitrate isn't used (applied later). + if (!video_is_suspended && send_codec.numberOfSimulcastStreams <= 1) pad_up_to_bitrate_kbps = 0; - } { - // The amount of padding should decay to zero if no frames are being - // captured. CriticalSectionScoped cs(data_cs_.get()); + // The amount of padding should decay to zero if no frames are being + // captured unless a min-transmit bitrate is used. int64_t now_ms = TickTime::MillisecondTimestamp(); if (now_ms - time_of_last_incoming_frame_ms_ > kStopPaddingThresholdMs) - max_padding_bitrate_kbps = 0; - } + pad_up_to_bitrate_kbps = 0; - paced_sender_->UpdateBitrate(bitrate_kbps, - max_padding_bitrate_kbps, - pad_up_to_bitrate_kbps); - default_rtp_rtcp_->SetTargetSendBitrate(stream_bitrates); - { - CriticalSectionScoped cs(data_cs_.get()); + // Pad up to min bitrate. + if (pad_up_to_bitrate_kbps < min_transmit_bitrate_kbps_) + pad_up_to_bitrate_kbps = min_transmit_bitrate_kbps_; + + // Padding may never exceed bitrate estimate. + if (pad_up_to_bitrate_kbps > bitrate_kbps) + pad_up_to_bitrate_kbps = bitrate_kbps; + + paced_sender_->UpdateBitrate( + bitrate_kbps, + PacedSender::kDefaultPaceMultiplier * bitrate_kbps, + pad_up_to_bitrate_kbps); + default_rtp_rtcp_->SetTargetSendBitrate(stream_bitrates); if (video_suspended_ == video_is_suspended) return; video_suspended_ = video_is_suspended; } - // State changed, inform codec observer. + + // Video suspend-state changed, inform codec observer. + CriticalSectionScoped crit(callback_cs_.get()); if (codec_observer_) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s: video_suspended_ changed to %i", - __FUNCTION__, video_is_suspended); + LOG(LS_INFO) << "Video suspended " << video_is_suspended + << " for channel " << channel_id_; codec_observer_->SuspendChange(channel_id_, video_is_suspended); } } void ViEEncoder::onLoadStateChanged(CPULoadState load_state) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s: load state changed to %d", - __FUNCTION__, (int)load_state); - vcm_.SetCPULoadState(load_state); + LOG(LS_INFO) << "load state changed to " << load_state; + vcm_.SetCPULoadState(load_state); } PacedSender* ViEEncoder::GetPacedSender() { @@ -1163,26 +993,9 @@ int32_t ViEEncoder::RegisterEffectFilter(ViEEffectFilter* effect_filter) { CriticalSectionScoped cs(callback_cs_.get()); - if (effect_filter == NULL) { - if (effect_filter_ == NULL) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s: no effect filter added", - __FUNCTION__); - return -1; - } - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s: deregister effect filter", - __FUNCTION__); - } else { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), "%s: register effect", - __FUNCTION__); - if (effect_filter_) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, channel_id_), - "%s: effect filter already added ", __FUNCTION__); - return -1; - } + if (effect_filter != NULL && effect_filter_ != NULL) { + LOG_F(LS_ERROR) << "Filter already set."; + return -1; } effect_filter_ = effect_filter; return 0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_encoder.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_encoder.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_encoder.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_encoder.h 2015-02-03 14:33:38.000000000 +0000 @@ -14,6 +14,7 @@ #include #include +#include "webrtc/base/thread_annotations.h" #include "webrtc/common_types.h" #include "webrtc/modules/bitrate_controller/include/bitrate_controller.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" @@ -97,13 +98,13 @@ virtual void DeliverFrame(int id, I420VideoFrame* video_frame, int num_csrcs = 0, - const uint32_t CSRC[kRtpCsrcSize] = NULL); - virtual void DelayChanged(int id, int frame_delay); + const uint32_t CSRC[kRtpCsrcSize] = NULL) OVERRIDE; + virtual void DelayChanged(int id, int frame_delay) OVERRIDE; virtual int GetPreferedFrameSettings(int* width, int* height, - int* frame_rate); + int* frame_rate) OVERRIDE; - virtual void ProviderDestroyed(int id) { + virtual void ProviderDestroyed(int id) OVERRIDE { return; } @@ -111,8 +112,7 @@ int32_t SendCodecStatistics(uint32_t* num_key_frames, uint32_t* num_delta_frames); - int32_t EstimatedSendBandwidth( - uint32_t* available_bandwidth) const; + int PacerQueuingDelayMs() const; int CodecTargetBitrate(uint32_t* bitrate) const; // Loss protection. @@ -131,7 +131,7 @@ const uint8_t* payload_data, uint32_t payload_size, const RTPFragmentationHeader& fragmentation_header, - const RTPVideoHeader* rtp_video_hdr); + const RTPVideoHeader* rtp_video_hdr) OVERRIDE; // Implements VideoProtectionCallback. virtual int ProtectionRequest( @@ -139,22 +139,26 @@ const FecProtectionParams* key_fec_params, uint32_t* sent_video_rate_bps, uint32_t* sent_nack_rate_bps, - uint32_t* sent_fec_rate_bps); + uint32_t* sent_fec_rate_bps) OVERRIDE; // Implements VideoSendStatisticsCallback. virtual int32_t SendStatistics(const uint32_t bit_rate, - const uint32_t frame_rate); + const uint32_t frame_rate) OVERRIDE; + int32_t RegisterCodecObserver(ViEEncoderObserver* observer); // Implements RtcpIntraFrameObserver. - virtual void OnReceivedIntraFrameRequest(uint32_t ssrc); - virtual void OnReceivedSLI(uint32_t ssrc, uint8_t picture_id); - virtual void OnReceivedRPSI(uint32_t ssrc, uint64_t picture_id); - virtual void OnLocalSsrcChanged(uint32_t old_ssrc, uint32_t new_ssrc); + virtual void OnReceivedIntraFrameRequest(uint32_t ssrc) OVERRIDE; + virtual void OnReceivedSLI(uint32_t ssrc, uint8_t picture_id) OVERRIDE; + virtual void OnReceivedRPSI(uint32_t ssrc, uint64_t picture_id) OVERRIDE; + virtual void OnLocalSsrcChanged(uint32_t old_ssrc, + uint32_t new_ssrc) OVERRIDE; // Sets SSRCs for all streams. bool SetSsrcs(const std::list& ssrcs); + void SetMinTransmitBitrate(int min_transmit_bitrate_kbps); + // Effect filter. int32_t RegisterEffectFilter(ViEEffectFilter* effect_filter); @@ -162,15 +166,15 @@ void SetLoadManager(CPULoadStateCallbackInvoker* load_manager); // Enables recording of debugging information. - virtual int StartDebugRecording(const char* fileNameUTF8); + int StartDebugRecording(const char* fileNameUTF8); // Disables recording of debugging information. - virtual int StopDebugRecording(); + int StopDebugRecording(); // Lets the sender suspend video when the rate drops below // |threshold_bps|, and turns back on when the rate goes back up above // |threshold_bps| + |window_bps|. - virtual void SuspendBelowMinBitrate(); + void SuspendBelowMinBitrate(); // New-style callbacks, used by VideoSendStream. void RegisterPreEncodeCallback(I420FrameCallback* pre_encode_callback); @@ -195,7 +199,11 @@ int64_t capture_time_ms, bool retransmission); int TimeToSendPadding(int bytes); private: - bool EncoderPaused() const; + bool EncoderPaused() const EXCLUSIVE_LOCKS_REQUIRED(data_cs_); + void TraceFrameDropStart() EXCLUSIVE_LOCKS_REQUIRED(data_cs_); + void TraceFrameDropEnd() EXCLUSIVE_LOCKS_REQUIRED(data_cs_); + + void UpdateHistograms(); int32_t engine_id_; const int channel_id_; @@ -215,31 +223,34 @@ // Owned by PeerConnection, not ViEEncoder CPULoadStateCallbackInvoker* load_manager_; - int64_t time_of_last_incoming_frame_ms_; - bool send_padding_; - int target_delay_ms_; - bool network_is_transmitting_; - bool encoder_paused_; - bool encoder_paused_and_dropped_frame_; - std::map time_last_intra_request_ms_; + int64_t time_of_last_incoming_frame_ms_ GUARDED_BY(data_cs_); + bool send_padding_ GUARDED_BY(data_cs_); + int min_transmit_bitrate_kbps_ GUARDED_BY(data_cs_); + int target_delay_ms_ GUARDED_BY(data_cs_); + bool network_is_transmitting_ GUARDED_BY(data_cs_); + bool encoder_paused_ GUARDED_BY(data_cs_); + bool encoder_paused_and_dropped_frame_ GUARDED_BY(data_cs_); + std::map time_last_intra_request_ms_ + GUARDED_BY(data_cs_); bool fec_enabled_; bool nack_enabled_; - ViEEncoderObserver* codec_observer_; - ViEEffectFilter* effect_filter_; + ViEEncoderObserver* codec_observer_ GUARDED_BY(callback_cs_); + ViEEffectFilter* effect_filter_ GUARDED_BY(callback_cs_); ProcessThread& module_process_thread_; - bool has_received_sli_; - uint8_t picture_id_sli_; - bool has_received_rpsi_; - uint64_t picture_id_rpsi_; - std::map ssrc_streams_; + bool has_received_sli_ GUARDED_BY(data_cs_); + uint8_t picture_id_sli_ GUARDED_BY(data_cs_); + bool has_received_rpsi_ GUARDED_BY(data_cs_); + uint64_t picture_id_rpsi_ GUARDED_BY(data_cs_); + std::map ssrc_streams_ GUARDED_BY(data_cs_); // Quality modes callback QMVideoSettingsCallback* qm_callback_; - bool video_suspended_; - I420FrameCallback* pre_encode_callback_; + bool video_suspended_ GUARDED_BY(data_cs_); + I420FrameCallback* pre_encode_callback_ GUARDED_BY(callback_cs_); + const int64_t start_ms_; }; } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_external_codec_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_external_codec_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_external_codec_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_external_codec_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -11,7 +11,7 @@ #include "webrtc/video_engine/vie_external_codec_impl.h" #include "webrtc/engine_configurations.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/video_engine/include/vie_errors.h" #include "webrtc/video_engine/vie_channel.h" #include "webrtc/video_engine/vie_channel_manager.h" @@ -37,62 +37,42 @@ } int ViEExternalCodecImpl::Release() { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, shared_data_->instance_id(), - "ViEExternalCodec::Release()"); // Decrease ref count. (*this)--; int32_t ref_count = GetCount(); if (ref_count < 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, shared_data_->instance_id(), - "ViEExternalCodec release too many times"); + LOG(LS_WARNING) << "ViEExternalCodec released too many times."; shared_data_->SetLastError(kViEAPIDoesNotExist); return -1; } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, shared_data_->instance_id(), - "ViEExternalCodec reference count: %d", ref_count); return ref_count; } ViEExternalCodecImpl::ViEExternalCodecImpl(ViESharedData* shared_data) : shared_data_(shared_data) { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_->instance_id(), - "ViEExternalCodecImpl::ViEExternalCodecImpl() Ctor"); } ViEExternalCodecImpl::~ViEExternalCodecImpl() { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_->instance_id(), - "ViEExternalCodecImpl::~ViEExternalCodecImpl() Dtor"); } int ViEExternalCodecImpl::RegisterExternalSendCodec(const int video_channel, const unsigned char pl_type, VideoEncoder* encoder, bool internal_source) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s channel %d pl_type %d encoder 0x%x", __FUNCTION__, - video_channel, pl_type, encoder); + assert(encoder != NULL); + LOG(LS_INFO) << "Register external encoder for channel " << video_channel + << ", pl_type " << static_cast(pl_type) + << ", internal_source " << internal_source; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Invalid argument video_channel %u. Does it exist?", - __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidArgument); return -1; } - if (!encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Invalid argument Encoder 0x%x.", __FUNCTION__, encoder); - shared_data_->SetLastError(kViECodecInvalidArgument); - return -1; - } - - if (vie_encoder->RegisterExternalEncoder(encoder, pl_type, internal_source) - != 0) { + if (vie_encoder->RegisterExternalEncoder(encoder, pl_type, + internal_source) != 0) { shared_data_->SetLastError(kViECodecUnknownError); return -1; } @@ -101,17 +81,11 @@ int ViEExternalCodecImpl::DeRegisterExternalSendCodec( const int video_channel, const unsigned char pl_type) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s channel %d pl_type %d", __FUNCTION__, video_channel, - pl_type); + LOG(LS_INFO) << "Deregister external encoder for channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Invalid argument video_channel %u. Does it exist?", - __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidArgument); return -1; } @@ -129,25 +103,15 @@ VideoDecoder* decoder, bool decoder_render, int render_delay) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s channel %d pl_type %d decoder 0x%x, decoder_render %d, " - "renderDelay %d", __FUNCTION__, video_channel, pl_type, decoder, - decoder_render, render_delay); + LOG(LS_INFO) << "Register exrernal decoder for channel " << video_channel + << ", pl_type " << pl_type + << ", decoder_render " << decoder_render + << ", render_delay " << render_delay; + assert(decoder != NULL); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Invalid argument video_channel %u. Does it exist?", - __FUNCTION__, video_channel); - shared_data_->SetLastError(kViECodecInvalidArgument); - return -1; - } - if (!decoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Invalid argument decoder 0x%x.", __FUNCTION__, decoder); shared_data_->SetLastError(kViECodecInvalidArgument); return -1; } @@ -161,18 +125,13 @@ } int ViEExternalCodecImpl::DeRegisterExternalReceiveCodec( -const int video_channel, const unsigned char pl_type) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s channel %d pl_type %u", __FUNCTION__, video_channel, - pl_type); + const int video_channel, const unsigned char pl_type) { + LOG(LS_INFO) << "DeRegisterExternalReceiveCodec for channel " << video_channel + << ", pl_type " << pl_type; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Invalid argument video_channel %u. Does it exist?", - __FUNCTION__, video_channel); shared_data_->SetLastError(kViECodecInvalidArgument); return -1; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_file_image.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_file_image.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_file_image.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_file_image.cc 2015-02-03 14:33:38.000000000 +0000 @@ -20,7 +20,6 @@ #include "webrtc/common_video/interface/video_image.h" #include "webrtc/common_video/jpeg/include/jpeg.h" #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" -#include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { @@ -32,38 +31,25 @@ FILE* image_file = fopen(file_nameUTF8, "rb"); if (!image_file) { - WEBRTC_TRACE(kTraceError, kTraceVideo, engine_id, - "%s could not open file %s", __FUNCTION__, file_nameUTF8); return -1; } if (fseek(image_file, 0, SEEK_END) != 0) { fclose(image_file); - WEBRTC_TRACE(kTraceError, kTraceVideo, engine_id, - "ConvertJPEGToVideoFrame fseek SEEK_END error for file %s", - file_nameUTF8); return -1; } int buffer_size = ftell(image_file); if (buffer_size == -1) { fclose(image_file); - WEBRTC_TRACE(kTraceError, kTraceVideo, engine_id, - "ConvertJPEGToVideoFrame could tell file size for file %s", - file_nameUTF8); return -1; } image_buffer._size = buffer_size; if (fseek(image_file, 0, SEEK_SET) != 0) { fclose(image_file); - WEBRTC_TRACE(kTraceError, kTraceVideo, engine_id, - "ConvertJPEGToVideoFrame fseek SEEK_SET error for file %s", - file_nameUTF8); return -1; } image_buffer._buffer = new uint8_t[ image_buffer._size + 1]; if (image_buffer._size != fread(image_buffer._buffer, sizeof(uint8_t), image_buffer._size, image_file)) { - WEBRTC_TRACE(kTraceError, kTraceVideo, engine_id, - "%s could not read file %s", __FUNCTION__, file_nameUTF8); fclose(image_file); delete [] image_buffer._buffer; return -1; @@ -76,14 +62,8 @@ image_buffer._buffer = NULL; if (ret == -1) { - WEBRTC_TRACE(kTraceError, kTraceVideo, engine_id, - "%s could decode file %s from jpeg format", __FUNCTION__, - file_nameUTF8); return -1; } else if (ret == -3) { - WEBRTC_TRACE(kTraceError, kTraceVideo, engine_id, - "%s could not convert jpeg's data to i420 format", - __FUNCTION__, file_nameUTF8); } return 0; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_frame_provider_base.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_frame_provider_base.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_frame_provider_base.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_frame_provider_base.cc 2015-02-03 14:33:38.000000000 +0000 @@ -14,8 +14,8 @@ #include "webrtc/common_video/interface/i420_video_frame.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/tick_util.h" -#include "webrtc/system_wrappers/interface/trace.h" #include "webrtc/video_engine/vie_defines.h" namespace webrtc { @@ -29,9 +29,8 @@ ViEFrameProviderBase::~ViEFrameProviderBase() { if (frame_callbacks_.size() > 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, id_), - "FrameCallbacks still exist when Provider deleted %d", - frame_callbacks_.size()); + LOG_F(LS_WARNING) << "FrameCallbacks still exist when Provider deleted: " + << frame_callbacks_.size(); } for (FrameCallbacks::iterator it = frame_callbacks_.begin(); @@ -56,18 +55,22 @@ // Deliver the frame to all registered callbacks. if (frame_callbacks_.size() > 0) { - if (frame_callbacks_.size() == 1 || video_frame->native_handle() != NULL) { + if (frame_callbacks_.size() == 1) { // We don't have to copy the frame. frame_callbacks_.front()->DeliverFrame(id_, video_frame, num_csrcs, CSRC); } else { - // Make a copy of the frame for all callbacks.callback for (FrameCallbacks::iterator it = frame_callbacks_.begin(); it != frame_callbacks_.end(); ++it) { - if (!extra_frame_.get()) { - extra_frame_.reset(new I420VideoFrame()); + if (video_frame->native_handle() != NULL) { + (*it)->DeliverFrame(id_, video_frame, num_csrcs, CSRC); + } else { + // Make a copy of the frame for all callbacks. + if (!extra_frame_.get()) { + extra_frame_.reset(new I420VideoFrame()); + } + extra_frame_->CopyFrame(*video_frame); + (*it)->DeliverFrame(id_, extra_frame_.get(), num_csrcs, CSRC); } - extra_frame_->CopyFrame(*video_frame); - (*it)->DeliverFrame(id_, extra_frame_.get(), num_csrcs, CSRC); } } } @@ -76,8 +79,7 @@ static_cast((TickTime::Now() - start_process_time).Milliseconds()); if (process_time > 25) { // Warn if the delivery time is too long. - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, id_), - "%s Too long time: %ums", __FUNCTION__, process_time); + LOG(LS_WARNING) << "Too long time delivering frame " << process_time; } #endif } @@ -131,16 +133,10 @@ int ViEFrameProviderBase::RegisterFrameCallback( int observer_id, ViEFrameCallback* callback_object) { assert(callback_object); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, id_), "%s(0x%p)", - __FUNCTION__, callback_object); { CriticalSectionScoped cs(provider_cs_.get()); if (std::find(frame_callbacks_.begin(), frame_callbacks_.end(), callback_object) != frame_callbacks_.end()) { - // This object is already registered. - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, id_), - "%s 0x%p already registered", __FUNCTION__, - callback_object); assert(false && "frameObserver already registered"); return -1; } @@ -157,21 +153,15 @@ int ViEFrameProviderBase::DeregisterFrameCallback( const ViEFrameCallback* callback_object) { assert(callback_object); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, id_), "%s(0x%p)", - __FUNCTION__, callback_object); CriticalSectionScoped cs(provider_cs_.get()); FrameCallbacks::iterator it = std::find(frame_callbacks_.begin(), frame_callbacks_.end(), callback_object); if (it == frame_callbacks_.end()) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, id_), - "%s 0x%p not found", __FUNCTION__, callback_object); return -1; } frame_callbacks_.erase(it); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, id_), - "%s 0x%p deregistered", __FUNCTION__, callback_object); // Notify implementer of this class that the callback list have changed. FrameCallbackChanged(); @@ -181,8 +171,6 @@ bool ViEFrameProviderBase::IsFrameCallbackRegistered( const ViEFrameCallback* callback_object) { assert(callback_object); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, id_), - "%s(0x%p)", __FUNCTION__, callback_object); CriticalSectionScoped cs(provider_cs_.get()); return std::find(frame_callbacks_.begin(), frame_callbacks_.end(), diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_image_process_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_image_process_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_image_process_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_image_process_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -10,7 +10,7 @@ #include "webrtc/video_engine/vie_image_process_impl.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/video_engine/include/vie_errors.h" #include "webrtc/video_engine/vie_capturer.h" #include "webrtc/video_engine/vie_channel.h" @@ -39,45 +39,30 @@ } int ViEImageProcessImpl::Release() { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, shared_data_->instance_id(), - "ViEImageProcess::Release()"); // Decrease ref count. (*this)--; int32_t ref_count = GetCount(); if (ref_count < 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, shared_data_->instance_id(), - "ViEImageProcess release too many times"); + LOG(LS_ERROR) << "ViEImageProcess release too many times"; shared_data_->SetLastError(kViEAPIDoesNotExist); return -1; } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, shared_data_->instance_id(), - "ViEImageProcess reference count: %d", ref_count); return ref_count; } ViEImageProcessImpl::ViEImageProcessImpl(ViESharedData* shared_data) - : shared_data_(shared_data) { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_->instance_id(), - "ViEImageProcessImpl::ViEImageProcessImpl() Ctor"); -} + : shared_data_(shared_data) {} -ViEImageProcessImpl::~ViEImageProcessImpl() { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_->instance_id(), - "ViEImageProcessImpl::~ViEImageProcessImpl() Dtor"); -} +ViEImageProcessImpl::~ViEImageProcessImpl() {} int ViEImageProcessImpl::RegisterCaptureEffectFilter( const int capture_id, ViEEffectFilter& capture_filter) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(capture_id: %d)", __FUNCTION__, capture_id); + LOG_F(LS_INFO) << "capture_id: " << capture_id; ViEInputManagerScoped is(*(shared_data_->input_manager())); ViECapturer* vie_capture = is.Capture(capture_id); if (!vie_capture) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s: Capture device %d doesn't exist", __FUNCTION__, - capture_id); shared_data_->SetLastError(kViEImageProcessInvalidCaptureId); return -1; } @@ -89,15 +74,11 @@ } int ViEImageProcessImpl::DeregisterCaptureEffectFilter(const int capture_id) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(capture_id: %d)", __FUNCTION__, capture_id); + LOG_F(LS_INFO) << "capture_id: " << capture_id; ViEInputManagerScoped is(*(shared_data_->input_manager())); ViECapturer* vie_capture = is.Capture(capture_id); if (!vie_capture) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s: Capture device %d doesn't exist", __FUNCTION__, - capture_id); shared_data_->SetLastError(kViEImageProcessInvalidCaptureId); return -1; } @@ -111,14 +92,11 @@ int ViEImageProcessImpl::RegisterSendEffectFilter( const int video_channel, ViEEffectFilter& send_filter) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(video_channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "video_channel: " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (vie_encoder == NULL) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViEImageProcessInvalidChannelId); return -1; } @@ -131,14 +109,11 @@ } int ViEImageProcessImpl::DeregisterSendEffectFilter(const int video_channel) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(video_channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "video_channel: " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (vie_encoder == NULL) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViEImageProcessInvalidChannelId); return -1; } @@ -152,14 +127,11 @@ int ViEImageProcessImpl::RegisterRenderEffectFilter( const int video_channel, ViEEffectFilter& render_filter) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(video_channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "video_channel: " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViEImageProcessInvalidChannelId); return -1; } @@ -171,14 +143,11 @@ } int ViEImageProcessImpl::DeregisterRenderEffectFilter(const int video_channel) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(video_channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "video_channel: " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViEImageProcessInvalidChannelId); return -1; } @@ -192,16 +161,12 @@ int ViEImageProcessImpl::EnableDeflickering(const int capture_id, const bool enable) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(capture_id: %d, enable: %d)", __FUNCTION__, capture_id, - enable); + LOG_F(LS_INFO) << "capture_id: " << capture_id + << " enable: " << (enable ? "on" : "off"); ViEInputManagerScoped is(*(shared_data_->input_manager())); ViECapturer* vie_capture = is.Capture(capture_id); if (!vie_capture) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s: Capture device %d doesn't exist", __FUNCTION__, - capture_id); shared_data_->SetLastError(kViEImageProcessInvalidChannelId); return -1; } @@ -217,44 +182,14 @@ return 0; } -int ViEImageProcessImpl::EnableDenoising(const int capture_id, - const bool enable) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(capture_id: %d, enable: %d)", __FUNCTION__, capture_id, - enable); - - ViEInputManagerScoped is(*(shared_data_->input_manager())); - ViECapturer* vie_capture = is.Capture(capture_id); - if (!vie_capture) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s: Capture device %d doesn't exist", __FUNCTION__, - capture_id); - shared_data_->SetLastError(kViEImageProcessInvalidCaptureId); - return -1; - } - - if (vie_capture->EnableDenoising(enable) != 0) { - if (enable) { - shared_data_->SetLastError(kViEImageProcessAlreadyEnabled); - } else { - shared_data_->SetLastError(kViEImageProcessAlreadyDisabled); - } - return -1; - } - return 0; -} - int ViEImageProcessImpl::EnableColorEnhancement(const int video_channel, const bool enable) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(video_channel: %d, enable: %d)", __FUNCTION__, video_channel, - enable); + LOG_F(LS_INFO) << "video_channel: " << video_channel + << " enable: " << (enable ? "on" : "off"); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViEImageProcessInvalidChannelId); return -1; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_image_process_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_image_process_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_image_process_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_image_process_impl.h 2015-02-03 14:33:38.000000000 +0000 @@ -35,7 +35,6 @@ ViEEffectFilter& render_filter); virtual int DeregisterRenderEffectFilter(const int video_channel); virtual int EnableDeflickering(const int capture_id, const bool enable); - virtual int EnableDenoising(const int capture_id, const bool enable); virtual int EnableColorEnhancement(const int video_channel, const bool enable); virtual void RegisterPreEncodeCallback( diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -11,6 +11,7 @@ #include "webrtc/video_engine/vie_impl.h" #include "webrtc/common.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/trace.h" #ifdef WEBRTC_ANDROID @@ -31,82 +32,71 @@ } bool VideoEngine::Delete(VideoEngine*& video_engine) { - if (!video_engine) { - WEBRTC_TRACE(kTraceError, kTraceVideo, kModuleId, - "VideoEngine::Delete - No argument"); + if (!video_engine) return false; - } - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, kModuleId, - "VideoEngine::Delete(vie = 0x%p)", video_engine); + + LOG_F(LS_INFO); VideoEngineImpl* vie_impl = static_cast(video_engine); // Check all reference counters. ViEBaseImpl* vie_base = vie_impl; if (vie_base->GetCount() > 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, kModuleId, - "ViEBase ref count: %d", vie_base->GetCount()); + LOG(LS_ERROR) << "ViEBase ref count > 0: " << vie_base->GetCount(); return false; } #ifdef WEBRTC_VIDEO_ENGINE_CAPTURE_API ViECaptureImpl* vie_capture = vie_impl; if (vie_capture->GetCount() > 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, kModuleId, - "ViECapture ref count: %d", vie_capture->GetCount()); + LOG(LS_ERROR) << "ViECapture ref count > 0: " << vie_capture->GetCount(); return false; } #endif #ifdef WEBRTC_VIDEO_ENGINE_CODEC_API ViECodecImpl* vie_codec = vie_impl; if (vie_codec->GetCount() > 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, kModuleId, - "ViECodec ref count: %d", vie_codec->GetCount()); + LOG(LS_ERROR) << "ViECodec ref count > 0: " << vie_codec->GetCount(); return false; } #endif #ifdef WEBRTC_VIDEO_ENGINE_EXTERNAL_CODEC_API ViEExternalCodecImpl* vie_external_codec = vie_impl; if (vie_external_codec->GetCount() > 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, kModuleId, - "ViEExternalCodec ref count: %d", vie_external_codec->GetCount()); + LOG(LS_ERROR) << "ViEExternalCodec ref count > 0: " + << vie_external_codec->GetCount(); return false; } #endif #ifdef WEBRTC_VIDEO_ENGINE_FILE_API ViEFileImpl* vie_file = vie_impl; if (vie_file->GetCount() > 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, kModuleId, - "ViEFile ref count: %d", vie_file->GetCount()); + LOG(LS_ERROR) << "ViEFile ref count > 0: " << vie_file->GetCount(); return false; } #endif #ifdef WEBRTC_VIDEO_ENGINE_IMAGE_PROCESS_API ViEImageProcessImpl* vie_image_process = vie_impl; if (vie_image_process->GetCount() > 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, kModuleId, - "ViEImageProcess ref count: %d", - vie_image_process->GetCount()); + LOG(LS_ERROR) << "ViEImageProcess ref count > 0: " + << vie_image_process->GetCount(); return false; } #endif ViENetworkImpl* vie_network = vie_impl; if (vie_network->GetCount() > 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, kModuleId, - "ViENetwork ref count: %d", vie_network->GetCount()); + LOG(LS_ERROR) << "ViENetwork ref count > 0: " << vie_network->GetCount(); return false; } #ifdef WEBRTC_VIDEO_ENGINE_RENDER_API ViERenderImpl* vie_render = vie_impl; if (vie_render->GetCount() > 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, kModuleId, - "ViERender ref count: %d", vie_render->GetCount()); + LOG(LS_ERROR) << "ViERender ref count > 0: " << vie_render->GetCount(); return false; } #endif #ifdef WEBRTC_VIDEO_ENGINE_RTP_RTCP_API ViERTP_RTCPImpl* vie_rtp_rtcp = vie_impl; if (vie_rtp_rtcp->GetCount() > 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, kModuleId, - "ViERTP_RTCP ref count: %d", vie_rtp_rtcp->GetCount()); + LOG(LS_ERROR) << "ViERTP_RTCP ref count > 0: " << vie_rtp_rtcp->GetCount(); return false; } #endif @@ -115,8 +105,6 @@ vie_impl = NULL; video_engine = NULL; - WEBRTC_TRACE(kTraceInfo, kTraceVideo, kModuleId, - "%s: instance deleted.", __FUNCTION__); return true; } @@ -128,9 +116,8 @@ if (Trace::SetTraceFile(file_nameUTF8, add_file_counter) == -1) { return -1; } - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, kModuleId, - "SetTraceFileName(file_nameUTF8 = %s, add_file_counter = %d", - file_nameUTF8, add_file_counter); + LOG_F(LS_INFO) << "filename: " << file_nameUTF8 + << " add_file_counter: " << (add_file_counter ? "yes" : "no"); return 0; } @@ -139,19 +126,16 @@ if (filter == kTraceNone && old_filter != kTraceNone) { // Do the logging before turning it off. - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, kModuleId, - "SetTraceFilter(filter = 0x%x)", filter); + LOG_F(LS_INFO) << "filter: " << filter; } Trace::set_level_filter(filter); - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, kModuleId, - "SetTraceFilter(filter = 0x%x)", filter); + LOG_F(LS_INFO) << "filter: " << filter; return 0; } int VideoEngine::SetTraceCallback(TraceCallback* callback) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, kModuleId, - "SetTraceCallback(TraceCallback = 0x%p)", callback); + LOG_F(LS_INFO); return Trace::SetTraceCallback(callback); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_input_manager.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_input_manager.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_input_manager.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_input_manager.cc 2015-02-03 14:33:38.000000000 +0000 @@ -17,8 +17,8 @@ #include "webrtc/modules/video_coding/main/interface/video_coding.h" #include "webrtc/modules/video_coding/main/interface/video_coding_defines.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" #include "webrtc/video_engine/include/vie_errors.h" #include "webrtc/video_engine/vie_capturer.h" #include "webrtc/video_engine/vie_defines.h" @@ -35,17 +35,12 @@ vie_frame_provider_map_(), capture_device_info_(NULL), module_process_thread_(NULL) { - WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo, ViEId(engine_id_), - "%s", __FUNCTION__); - for (int idx = 0; idx < kViEMaxCaptureDevices; idx++) { free_capture_device_id_[idx] = true; } } ViEInputManager::~ViEInputManager() { - WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo, ViEId(engine_id_), - "%s", __FUNCTION__); for (FrameProviderMap::iterator it = vie_frame_provider_map_.begin(); it != vie_frame_provider_map_.end(); ++it) { @@ -61,8 +56,6 @@ } int ViEInputManager::NumberOfCaptureDevices() { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s", - __FUNCTION__); CriticalSectionScoped cs(device_info_cs_.get()); if (!GetDeviceInfo()) return 0; @@ -76,8 +69,6 @@ uint32_t device_name_length, char* device_unique_idUTF8, uint32_t device_unique_idUTF8Length) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), - "%s(device_number: %d)", __FUNCTION__, device_number); CriticalSectionScoped cs(device_info_cs_.get()); GetDeviceInfo(); assert(capture_device_info_); @@ -89,8 +80,6 @@ int ViEInputManager::NumberOfCaptureCapabilities( const char* device_unique_idUTF8) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s", - __FUNCTION__); CriticalSectionScoped cs(device_info_cs_.get()); if (!GetDeviceInfo()) return 0; @@ -102,9 +91,6 @@ const char* device_unique_idUTF8, const uint32_t device_capability_number, CaptureCapability& capability) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), - "%s(device_unique_idUTF8: %s, device_capability_number: %d)", - __FUNCTION__, device_unique_idUTF8, device_capability_number); CriticalSectionScoped cs(device_info_cs_.get()); GetDeviceInfo(); assert(capture_device_info_); @@ -128,9 +114,6 @@ int ViEInputManager::GetOrientation(const char* device_unique_idUTF8, RotateCapturedFrame& orientation) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), - "%s(device_unique_idUTF8: %s,)", __FUNCTION__, - device_unique_idUTF8); CriticalSectionScoped cs(device_info_cs_.get()); GetDeviceInfo(); assert(capture_device_info_); @@ -173,8 +156,6 @@ const char* device_unique_idUTF8, const uint32_t device_unique_idUTF8Length, int& capture_id) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), - "%s(device_unique_id: %s)", __FUNCTION__, device_unique_idUTF8); CriticalSectionScoped cs(map_cs_.get()); // Make sure the device is not already allocated. @@ -218,57 +199,39 @@ const char* cast_id = reinterpret_cast(device_unique_idUTF8); if (strncmp(cast_id, reinterpret_cast(found_unique_name), strlen(cast_id)) == 0) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, ViEId(engine_id_), - "%s:%d Capture device was found by unique ID: %s. Returning", - __FUNCTION__, __LINE__, device_unique_idUTF8); found_device = true; break; } } if (!found_device) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, ViEId(engine_id_), - "%s:%d Capture device NOT found by unique ID: %s. Returning", - __FUNCTION__, __LINE__, device_unique_idUTF8); + LOG(LS_ERROR) << "Capture device not found: " << device_unique_idUTF8; return kViECaptureDeviceDoesNotExist; } int newcapture_id = 0; - if (GetFreeCaptureId(&newcapture_id) == false) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), - "%s: Maximum supported number of capture devices already in " - "use", __FUNCTION__); + if (!GetFreeCaptureId(&newcapture_id)) { + LOG(LS_ERROR) << "All capture devices already allocated."; return kViECaptureDeviceMaxNoDevicesAllocated; } ViECapturer* vie_capture = ViECapturer::CreateViECapture( newcapture_id, engine_id_, config_, device_unique_idUTF8, device_unique_idUTF8Length, *module_process_thread_); if (!vie_capture) { - ReturnCaptureId(newcapture_id); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), - "%s: Could not create capture module for %s", __FUNCTION__, - device_unique_idUTF8); + ReturnCaptureId(newcapture_id); return kViECaptureDeviceUnknownError; } vie_frame_provider_map_[newcapture_id] = vie_capture; capture_id = newcapture_id; - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), - "%s(device_unique_id: %s, capture_id: %d)", __FUNCTION__, - device_unique_idUTF8, capture_id); return 0; } int ViEInputManager::CreateCaptureDevice(VideoCaptureModule* capture_module, int& capture_id) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s", - __FUNCTION__); - CriticalSectionScoped cs(map_cs_.get()); int newcapture_id = 0; if (!GetFreeCaptureId(&newcapture_id)) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), - "%s: Maximum supported number of capture devices already in " - "use", __FUNCTION__); + LOG(LS_ERROR) << "All capture devices already allocated."; return kViECaptureDeviceMaxNoDevicesAllocated; } @@ -276,21 +239,15 @@ newcapture_id, engine_id_, config_, capture_module, *module_process_thread_); if (!vie_capture) { - ReturnCaptureId(newcapture_id); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), - "%s: Could attach capture module.", __FUNCTION__); + ReturnCaptureId(newcapture_id); return kViECaptureDeviceUnknownError; } vie_frame_provider_map_[newcapture_id] = vie_capture; capture_id = newcapture_id; - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), - "%s, capture_id: %d", __FUNCTION__, capture_id); return 0; } int ViEInputManager::DestroyCaptureDevice(const int capture_id) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), - "%s(capture_id: %d)", __FUNCTION__, capture_id); ViECapturer* vie_capture = NULL; { // We need exclusive access to the object to delete it. @@ -300,18 +257,14 @@ vie_capture = ViECapturePtr(capture_id); if (!vie_capture) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), - "%s(capture_id: %d) - No such capture device id", - __FUNCTION__, capture_id); + LOG(LS_ERROR) << "No such capture device id: " << capture_id; return -1; } uint32_t num_callbacks = vie_capture->NumberOfRegisteredFrameCallbacks(); if (num_callbacks > 0) { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideo, - ViEId(engine_id_), "%s(capture_id: %d) - %u registered " - "callbacks when destroying capture device", - __FUNCTION__, capture_id, num_callbacks); + LOG(LS_WARNING) << num_callbacks << " still registered to capture id " + << capture_id << " when destroying capture device."; } vie_frame_provider_map_.erase(capture_id); ReturnCaptureId(capture_id); @@ -326,15 +279,11 @@ int ViEInputManager::CreateExternalCaptureDevice( ViEExternalCapture*& external_capture, int& capture_id) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s", - __FUNCTION__); CriticalSectionScoped cs(map_cs_.get()); int newcapture_id = 0; if (GetFreeCaptureId(&newcapture_id) == false) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), - "%s: Maximum supported number of capture devices already in " - "use", __FUNCTION__); + LOG(LS_ERROR) << "All capture devices already allocated."; return kViECaptureDeviceMaxNoDevicesAllocated; } @@ -342,30 +291,21 @@ newcapture_id, engine_id_, config_, NULL, 0, *module_process_thread_); if (!vie_capture) { ReturnCaptureId(newcapture_id); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), - "%s: Could not create capture module for external capture.", - __FUNCTION__); return kViECaptureDeviceUnknownError; } vie_frame_provider_map_[newcapture_id] = vie_capture; capture_id = newcapture_id; external_capture = vie_capture; - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), - "%s, capture_id: %d)", __FUNCTION__, capture_id); return 0; } bool ViEInputManager::GetFreeCaptureId(int* freecapture_id) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s", - __FUNCTION__); for (int id = 0; id < kViEMaxCaptureDevices; id++) { if (free_capture_device_id_[id]) { // We found a free capture device id. free_capture_device_id_[id] = false; *freecapture_id = id + kViECaptureIdBase; - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), - "%s: new id: %d", __FUNCTION__, *freecapture_id); return true; } } @@ -373,8 +313,6 @@ } void ViEInputManager::ReturnCaptureId(int capture_id) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), - "%s(%d)", __FUNCTION__, capture_id); CriticalSectionScoped cs(map_cs_.get()); if (capture_id >= kViECaptureIdBase && capture_id < kViEMaxCaptureDevices + kViECaptureIdBase) { @@ -411,8 +349,10 @@ ViECapturer* ViEInputManager::ViECapturePtr(int capture_id) const { if (!(capture_id >= kViECaptureIdBase && - capture_id <= kViECaptureIdBase + kViEMaxCaptureDevices)) + capture_id <= kViECaptureIdBase + kViEMaxCaptureDevices)) { + LOG(LS_ERROR) << "Capture device doesn't exist " << capture_id << "."; return NULL; + } return static_cast(ViEFrameProvider(capture_id)); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_manager_base.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_manager_base.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_manager_base.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_manager_base.h 2015-02-03 14:33:38.000000000 +0000 @@ -11,7 +11,7 @@ #ifndef WEBRTC_VIDEO_ENGINE_VIE_MANAGER_BASE_H_ #define WEBRTC_VIDEO_ENGINE_VIE_MANAGER_BASE_H_ -#include "webrtc/system_wrappers/interface/thread_annotations.h" +#include "webrtc/base/thread_annotations.h" namespace webrtc { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_network_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_network_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_network_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_network_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -16,7 +16,7 @@ #endif #include "webrtc/engine_configurations.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/video_engine/include/vie_errors.h" #include "webrtc/video_engine/vie_channel.h" #include "webrtc/video_engine/vie_channel_manager.h" @@ -39,35 +39,25 @@ } int ViENetworkImpl::Release() { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, shared_data_->instance_id(), - "ViENetwork::Release()"); // Decrease ref count. (*this)--; int32_t ref_count = GetCount(); if (ref_count < 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, shared_data_->instance_id(), - "ViENetwork release too many times"); + LOG(LS_ERROR) << "ViENetwork release too many times"; shared_data_->SetLastError(kViEAPIDoesNotExist); return -1; } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, shared_data_->instance_id(), - "ViENetwork reference count: %d", ref_count); return ref_count; } void ViENetworkImpl::SetNetworkTransmissionState(const int video_channel, const bool is_transmitting) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(event: Network %s)", __FUNCTION__, - is_transmitting ? "transmitting" : "not transmitting"); + LOG_F(LS_INFO) << "channel: " << video_channel + << " transmitting: " << (is_transmitting ? "yes" : "no"); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "An encoder doesn't exist for this channel"); shared_data_->SetLastError(kViENetworkInvalidChannelId); return; } @@ -75,34 +65,21 @@ } ViENetworkImpl::ViENetworkImpl(ViESharedData* shared_data) - : shared_data_(shared_data) { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_->instance_id(), - "ViENetworkImpl::ViENetworkImpl() Ctor"); -} + : shared_data_(shared_data) {} -ViENetworkImpl::~ViENetworkImpl() { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_->instance_id(), - "ViENetworkImpl::~ViENetworkImpl() Dtor"); -} +ViENetworkImpl::~ViENetworkImpl() {} int ViENetworkImpl::RegisterSendTransport(const int video_channel, Transport& transport) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "channel: " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s Channel doesn't exist", __FUNCTION__); shared_data_->SetLastError(kViENetworkInvalidChannelId); return -1; } if (vie_channel->Sending()) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s Channel already sending.", __FUNCTION__); + LOG_F(LS_ERROR) << "Already sending on channel: " << video_channel; shared_data_->SetLastError(kViENetworkAlreadySending); return -1; } @@ -114,22 +91,15 @@ } int ViENetworkImpl::DeregisterSendTransport(const int video_channel) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "channel: " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s Channel doesn't exist", __FUNCTION__); shared_data_->SetLastError(kViENetworkInvalidChannelId); return -1; } if (vie_channel->Sending()) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s Channel already sending", __FUNCTION__); + LOG_F(LS_ERROR) << "Actively sending on channel: " << video_channel; shared_data_->SetLastError(kViENetworkAlreadySending); return -1; } @@ -143,17 +113,9 @@ int ViENetworkImpl::ReceivedRTPPacket(const int video_channel, const void* data, const int length, const PacketTime& packet_time) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, data: -, length: %d)", __FUNCTION__, - video_channel, length); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - // The channel doesn't exists - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "Channel doesn't exist"); shared_data_->SetLastError(kViENetworkInvalidChannelId); return -1; } @@ -162,16 +124,9 @@ int ViENetworkImpl::ReceivedRTCPPacket(const int video_channel, const void* data, const int length) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, data: -, length: %d)", __FUNCTION__, - video_channel, length); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "Channel doesn't exist"); shared_data_->SetLastError(kViENetworkInvalidChannelId); return -1; } @@ -179,15 +134,10 @@ } int ViENetworkImpl::SetMTU(int video_channel, unsigned int mtu) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, mtu: %u)", __FUNCTION__, video_channel, mtu); + LOG_F(LS_INFO) << "channel: " << video_channel << " mtu: " << mtu; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "Channel doesn't exist"); shared_data_->SetLastError(kViENetworkInvalidChannelId); return -1; } @@ -197,4 +147,24 @@ } return 0; } + +int ViENetworkImpl::ReceivedBWEPacket(const int video_channel, + int64_t arrival_time_ms, int payload_size, const RTPHeader& header) { + ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); + ViEChannel* vie_channel = cs.Channel(video_channel); + if (!vie_channel) { + shared_data_->SetLastError(kViENetworkInvalidChannelId); + return -1; + } + + vie_channel->ReceivedBWEPacket(arrival_time_ms, payload_size, header); + return 0; +} + +bool ViENetworkImpl::SetBandwidthEstimationConfig( + int video_channel, const webrtc::Config& config) { + LOG_F(LS_INFO) << "channel: " << video_channel; + return shared_data_->channel_manager()->SetBandwidthEstimationConfig( + video_channel, config); +} } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_network_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_network_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_network_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_network_impl.h 2015-02-03 14:33:38.000000000 +0000 @@ -24,20 +24,29 @@ public ViERefCount { public: // Implements ViENetwork. - virtual int Release(); + virtual int Release() OVERRIDE; virtual void SetNetworkTransmissionState(const int video_channel, - const bool is_transmitting); + const bool is_transmitting) OVERRIDE; virtual int RegisterSendTransport(const int video_channel, - Transport& transport); - virtual int DeregisterSendTransport(const int video_channel); + Transport& transport) OVERRIDE; + virtual int DeregisterSendTransport(const int video_channel) OVERRIDE; virtual int ReceivedRTPPacket(const int video_channel, const void* data, const int length, - const PacketTime& packet_time); + const PacketTime& packet_time) OVERRIDE; virtual int ReceivedRTCPPacket(const int video_channel, const void* data, - const int length); - virtual int SetMTU(int video_channel, unsigned int mtu); + const int length) OVERRIDE; + virtual int SetMTU(int video_channel, unsigned int mtu) OVERRIDE; + + virtual int ReceivedBWEPacket(const int video_channel, + int64_t arrival_time_ms, + int payload_size, + const RTPHeader& header) OVERRIDE; + + virtual bool SetBandwidthEstimationConfig( + int video_channel, + const webrtc::Config& config) OVERRIDE; protected: explicit ViENetworkImpl(ViESharedData* shared_data); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_receiver.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_receiver.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_receiver.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_receiver.cc 2015-02-03 14:33:38.000000000 +0000 @@ -15,6 +15,7 @@ #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" #include "webrtc/modules/rtp_rtcp/interface/fec_receiver.h" #include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h" +#include "webrtc/modules/rtp_rtcp/interface/remote_ntp_time_estimator.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_receiver.h" @@ -22,33 +23,42 @@ #include "webrtc/modules/utility/interface/rtp_dump.h" #include "webrtc/modules/video_coding/main/interface/video_coding.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/tick_util.h" +#include "webrtc/system_wrappers/interface/timestamp_extrapolator.h" #include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { +static const int kPacketLogIntervalMs = 10000; + ViEReceiver::ViEReceiver(const int32_t channel_id, VideoCodingModule* module_vcm, RemoteBitrateEstimator* remote_bitrate_estimator, RtpFeedback* rtp_feedback) : receive_cs_(CriticalSectionWrapper::CreateCriticalSection()), - channel_id_(channel_id), + clock_(Clock::GetRealTimeClock()), rtp_header_parser_(RtpHeaderParser::Create()), - rtp_payload_registry_(new RTPPayloadRegistry( - channel_id, RTPPayloadStrategy::CreateStrategy(false))), - rtp_receiver_(RtpReceiver::CreateVideoReceiver( - channel_id, Clock::GetRealTimeClock(), this, rtp_feedback, - rtp_payload_registry_.get())), - rtp_receive_statistics_(ReceiveStatistics::Create( - Clock::GetRealTimeClock())), - fec_receiver_(FecReceiver::Create(channel_id, this)), + rtp_payload_registry_( + new RTPPayloadRegistry(RTPPayloadStrategy::CreateStrategy(false))), + rtp_receiver_( + RtpReceiver::CreateVideoReceiver(channel_id, + clock_, + this, + rtp_feedback, + rtp_payload_registry_.get())), + rtp_receive_statistics_(ReceiveStatistics::Create(clock_)), + fec_receiver_(FecReceiver::Create(this)), rtp_rtcp_(NULL), vcm_(module_vcm), remote_bitrate_estimator_(remote_bitrate_estimator), + ntp_estimator_(new RemoteNtpTimeEstimator(clock_)), rtp_dump_(NULL), receiving_(false), receiving_rtcp_(false), - restored_packet_in_use_(false) { + restored_packet_in_use_(false), + receiving_ast_enabled_(false), + last_packet_log_ms_(-1) { assert(remote_bitrate_estimator); } @@ -93,12 +103,12 @@ rtp_receiver_->SetNACKStatus(enable ? kNackRtcp : kNackOff); } -void ViEReceiver::SetRtxStatus(bool enable, uint32_t ssrc) { - rtp_payload_registry_->SetRtxStatus(enable, ssrc); +void ViEReceiver::SetRtxPayloadType(int payload_type) { + rtp_payload_registry_->SetRtxPayloadType(payload_type); } -void ViEReceiver::SetRtxPayloadType(uint32_t payload_type) { - rtp_payload_registry_->SetRtxPayloadType(payload_type); +void ViEReceiver::SetRtxSsrc(uint32_t ssrc) { + rtp_payload_registry_->SetRtxSsrc(ssrc); } uint32_t ViEReceiver::GetRemoteSsrc() const { @@ -141,9 +151,15 @@ bool ViEReceiver::SetReceiveAbsoluteSendTimeStatus(bool enable, int id) { if (enable) { - return rtp_header_parser_->RegisterRtpHeaderExtension( - kRtpExtensionAbsoluteSendTime, id); + if (rtp_header_parser_->RegisterRtpHeaderExtension( + kRtpExtensionAbsoluteSendTime, id)) { + receiving_ast_enabled_ = true; + return true; + } else { + return false; + } } else { + receiving_ast_enabled_ = false; return rtp_header_parser_->DeregisterRtpHeaderExtension( kRtpExtensionAbsoluteSendTime); } @@ -165,7 +181,12 @@ int32_t ViEReceiver::OnReceivedPayloadData( const uint8_t* payload_data, const uint16_t payload_size, const WebRtcRTPHeader* rtp_header) { - if (vcm_->IncomingPacket(payload_data, payload_size, *rtp_header) != 0) { + WebRtcRTPHeader rtp_header_with_ntp = *rtp_header; + rtp_header_with_ntp.ntp_time_ms = + ntp_estimator_->Estimate(rtp_header->header.timestamp); + if (vcm_->IncomingPacket(payload_data, + payload_size, + rtp_header_with_ntp) != 0) { // Check this... return -1; } @@ -176,12 +197,22 @@ int rtp_packet_length) { RTPHeader header; if (!rtp_header_parser_->Parse(rtp_packet, rtp_packet_length, &header)) { - WEBRTC_TRACE(kTraceDebug, webrtc::kTraceVideo, channel_id_, - "IncomingPacket invalid RTP header"); return false; } header.payload_type_frequency = kVideoPayloadTypeFrequency; - return ReceivePacket(rtp_packet, rtp_packet_length, header, false); + bool in_order = IsPacketInOrder(header); + return ReceivePacket(rtp_packet, rtp_packet_length, header, in_order); +} + +void ViEReceiver::ReceivedBWEPacket( + int64_t arrival_time_ms, int payload_size, const RTPHeader& header) { + // Only forward if the incoming packet *and* the channel are both configured + // to receive absolute sender time. RTP time stamps may have different rates + // for audio and video and shouldn't be mixed. + if (header.extension.hasAbsoluteSendTime && receiving_ast_enabled_) { + remote_bitrate_estimator_->IncomingPacket(arrival_time_ms, payload_size, + header); + } } int ViEReceiver::InsertRTPPacket(const uint8_t* rtp_packet, @@ -201,28 +232,49 @@ RTPHeader header; if (!rtp_header_parser_->Parse(rtp_packet, rtp_packet_length, &header)) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_, - "Incoming packet: Invalid RTP header"); return -1; } int payload_length = rtp_packet_length - header.headerLength; int64_t arrival_time_ms; + int64_t now_ms = clock_->TimeInMilliseconds(); if (packet_time.timestamp != -1) arrival_time_ms = (packet_time.timestamp + 500) / 1000; else - arrival_time_ms = TickTime::MillisecondTimestamp(); + arrival_time_ms = now_ms; + + { + // Periodically log the RTP header of incoming packets. + CriticalSectionScoped cs(receive_cs_.get()); + if (now_ms - last_packet_log_ms_ > kPacketLogIntervalMs) { + std::stringstream ss; + ss << "Packet received on SSRC: " << header.ssrc << " with payload type: " + << static_cast(header.payloadType) << ", timestamp: " + << header.timestamp << ", sequence number: " << header.sequenceNumber + << ", arrival time: " << arrival_time_ms; + if (header.extension.hasTransmissionTimeOffset) + ss << ", toffset: " << header.extension.transmissionTimeOffset; + if (header.extension.hasAbsoluteSendTime) + ss << ", abs send time: " << header.extension.absoluteSendTime; + LOG(LS_INFO) << ss.str(); + last_packet_log_ms_ = now_ms; + } + } remote_bitrate_estimator_->IncomingPacket(arrival_time_ms, payload_length, header); header.payload_type_frequency = kVideoPayloadTypeFrequency; bool in_order = IsPacketInOrder(header); - rtp_receive_statistics_->IncomingPacket( - header, rtp_packet_length, IsPacketRetransmitted(header, in_order)); rtp_payload_registry_->SetIncomingPayloadType(header); - return ReceivePacket(rtp_packet, rtp_packet_length, header, in_order) + int ret = ReceivePacket(rtp_packet, rtp_packet_length, header, in_order) ? 0 : -1; + // Update receive statistics after ReceivePacket. + // Receive statistics will be reset if the payload type changes (make sure + // that the first packet is included in the stats). + rtp_receive_statistics_->IncomingPacket( + header, rtp_packet_length, IsPacketRetransmitted(header, in_order)); + return ret; } bool ViEReceiver::ReceivePacket(const uint8_t* packet, @@ -253,12 +305,15 @@ rtp_receive_statistics_->FecPacketReceived(header.ssrc); if (fec_receiver_->AddReceivedRedPacket( header, packet, packet_length, ulpfec_pt) != 0) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_, - "Incoming RED packet error"); return false; } return fec_receiver_->ProcessReceivedFec() == 0; } else if (rtp_payload_registry_->IsRtx(header)) { + if (header.headerLength + header.paddingLength == packet_length) { + // This is an empty packet and should be silently dropped before trying to + // parse the RTX header. + return true; + } // Remove the RTX header and parse the original RTP header. if (packet_length < header.headerLength) return false; @@ -266,16 +321,14 @@ return false; CriticalSectionScoped cs(receive_cs_.get()); if (restored_packet_in_use_) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_, - "Multiple RTX headers detected, dropping packet"); + LOG(LS_WARNING) << "Multiple RTX headers detected, dropping packet."; return false; } uint8_t* restored_packet_ptr = restored_packet_; if (!rtp_payload_registry_->RestoreOriginalPacket( &restored_packet_ptr, packet, &packet_length, rtp_receiver_->SSRC(), header)) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_, - "Incoming RTX packet: invalid RTP header"); + LOG(LS_WARNING) << "Incoming RTX packet: Invalid RTP header"; return false; } restored_packet_in_use_ = true; @@ -306,7 +359,28 @@ } } assert(rtp_rtcp_); // Should be set by owner at construction time. - return rtp_rtcp_->IncomingRtcpPacket(rtcp_packet, rtcp_packet_length); + int ret = rtp_rtcp_->IncomingRtcpPacket(rtcp_packet, rtcp_packet_length); + if (ret != 0) { + return ret; + } + + uint16_t rtt = 0; + rtp_rtcp_->RTT(rtp_receiver_->SSRC(), &rtt, NULL, NULL, NULL); + if (rtt == 0) { + // Waiting for valid rtt. + return 0; + } + uint32_t ntp_secs = 0; + uint32_t ntp_frac = 0; + uint32_t rtp_timestamp = 0; + if (0 != rtp_rtcp_->RemoteNTP(&ntp_secs, &ntp_frac, NULL, NULL, + &rtp_timestamp)) { + // Waiting for RTCP. + return 0; + } + ntp_estimator_->UpdateRtcpTimestamp(rtt, ntp_secs, ntp_frac, rtp_timestamp); + + return 0; } void ViEReceiver::StartReceive() { @@ -337,16 +411,12 @@ } else { rtp_dump_ = RtpDump::CreateRtpDump(); if (rtp_dump_ == NULL) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, channel_id_, - "StartRTPDump: Failed to create RTP dump"); return -1; } } if (rtp_dump_->Start(file_nameUTF8) != 0) { RtpDump::DestroyRtpDump(rtp_dump_); rtp_dump_ = NULL; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, channel_id_, - "StartRTPDump: Failed to start RTP dump"); return -1; } return 0; @@ -357,36 +427,15 @@ if (rtp_dump_) { if (rtp_dump_->IsActive()) { rtp_dump_->Stop(); - } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, channel_id_, - "StopRTPDump: Dump not active"); } RtpDump::DestroyRtpDump(rtp_dump_); rtp_dump_ = NULL; } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, channel_id_, - "StopRTPDump: RTP dump not started"); return -1; } return 0; } -// TODO(holmer): To be moved to ViEChannelGroup. -void ViEReceiver::EstimatedReceiveBandwidth( - unsigned int* available_bandwidth) const { - std::vector ssrcs; - - // LatestEstimate returns an error if there is no valid bitrate estimate, but - // ViEReceiver instead returns a zero estimate. - remote_bitrate_estimator_->LatestEstimate(&ssrcs, available_bandwidth); - if (std::find(ssrcs.begin(), ssrcs.end(), rtp_receiver_->SSRC()) != - ssrcs.end()) { - *available_bandwidth /= ssrcs.size(); - } else { - *available_bandwidth = 0; - } -} - void ViEReceiver::GetReceiveBandwidthEstimatorStats( ReceiveBandwidthEstimatorStats* output) const { remote_bitrate_estimator_->GetStats(output); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_receiver.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_receiver.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_receiver.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_receiver.h 2015-02-03 14:33:38.000000000 +0000 @@ -25,6 +25,7 @@ class CriticalSectionWrapper; class FecReceiver; +class RemoteNtpTimeEstimator; class ReceiveStatistics; class RemoteBitrateEstimator; class RtpDump; @@ -46,8 +47,8 @@ bool RegisterPayload(const VideoCodec& video_codec); void SetNackStatus(bool enable, int max_nack_reordering_threshold); - void SetRtxStatus(bool enable, uint32_t ssrc); - void SetRtxPayloadType(uint32_t payload_type); + void SetRtxPayloadType(int payload_type); + void SetRtxSsrc(uint32_t ssrc); uint32_t GetRemoteSsrc() const; int GetCsrcs(uint32_t* csrcs) const; @@ -74,22 +75,22 @@ int ReceivedRTPPacket(const void* rtp_packet, int rtp_packet_length, const PacketTime& packet_time); int ReceivedRTCPPacket(const void* rtcp_packet, int rtcp_packet_length); - virtual bool OnRecoveredPacket(const uint8_t* packet, - int packet_length) OVERRIDE; // Implements RtpData. virtual int32_t OnReceivedPayloadData( const uint8_t* payload_data, const uint16_t payload_size, - const WebRtcRTPHeader* rtp_header); - - void EstimatedReceiveBandwidth(unsigned int* available_bandwidth) const; + const WebRtcRTPHeader* rtp_header) OVERRIDE; + virtual bool OnRecoveredPacket(const uint8_t* packet, + int packet_length) OVERRIDE; void GetReceiveBandwidthEstimatorStats( ReceiveBandwidthEstimatorStats* output) const; ReceiveStatistics* GetReceiveStatistics() const; + void ReceivedBWEPacket(int64_t arrival_time_ms, int payload_size, + const RTPHeader& header); private: int InsertRTPPacket(const uint8_t* rtp_packet, int rtp_packet_length, const PacketTime& packet_time); @@ -107,7 +108,7 @@ bool IsPacketRetransmitted(const RTPHeader& header, bool in_order) const; scoped_ptr receive_cs_; - const int32_t channel_id_; + Clock* clock_; scoped_ptr rtp_header_parser_; scoped_ptr rtp_payload_registry_; scoped_ptr rtp_receiver_; @@ -118,11 +119,15 @@ VideoCodingModule* vcm_; RemoteBitrateEstimator* remote_bitrate_estimator_; + scoped_ptr ntp_estimator_; + RtpDump* rtp_dump_; bool receiving_; bool receiving_rtcp_; uint8_t restored_packet_[kViEMaxMtu]; bool restored_packet_in_use_; + bool receiving_ast_enabled_; + int64_t last_packet_log_ms_; }; } // namespace webrt diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_remb.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_remb.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_remb.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_remb.cc 2015-02-03 14:33:38.000000000 +0000 @@ -22,8 +22,7 @@ namespace webrtc { -const int kRembSendIntervallMs = 1000; -const unsigned int kRembMinimumBitrateKbps = 50; +const int kRembSendIntervalMs = 200; // % threshold for if we should send a new REMB asap. const unsigned int kSendThresholdPercent = 97; @@ -38,15 +37,12 @@ void VieRemb::AddReceiveChannel(RtpRtcp* rtp_rtcp) { assert(rtp_rtcp); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "VieRemb::AddReceiveChannel(%p)", rtp_rtcp); CriticalSectionScoped cs(list_crit_.get()); if (std::find(receive_modules_.begin(), receive_modules_.end(), rtp_rtcp) != receive_modules_.end()) return; - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, "AddRembChannel"); // The module probably doesn't have a remote SSRC yet, so don't add it to the // map. receive_modules_.push_back(rtp_rtcp); @@ -54,8 +50,6 @@ void VieRemb::RemoveReceiveChannel(RtpRtcp* rtp_rtcp) { assert(rtp_rtcp); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "VieRemb::RemoveReceiveChannel(%p)", rtp_rtcp); CriticalSectionScoped cs(list_crit_.get()); for (RtpModules::iterator it = receive_modules_.begin(); @@ -69,8 +63,6 @@ void VieRemb::AddRembSender(RtpRtcp* rtp_rtcp) { assert(rtp_rtcp); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "VieRemb::AddRembSender(%p)", rtp_rtcp); CriticalSectionScoped cs(list_crit_.get()); @@ -83,8 +75,6 @@ void VieRemb::RemoveRembSender(RtpRtcp* rtp_rtcp) { assert(rtp_rtcp); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "VieRemb::RemoveRembSender(%p)", rtp_rtcp); CriticalSectionScoped cs(list_crit_.get()); for (RtpModules::iterator it = rtcp_sender_.begin(); @@ -106,8 +96,6 @@ void VieRemb::OnReceiveBitrateChanged(const std::vector& ssrcs, unsigned int bitrate) { - WEBRTC_TRACE(kTraceStream, kTraceVideo, -1, - "VieRemb::UpdateBitrateEstimate(bitrate: %u)", bitrate); list_crit_->Enter(); // If we already have an estimate, check if the new total estimate is below // kSendThresholdPercent of the previous estimate. @@ -117,7 +105,7 @@ if (new_remb_bitrate < kSendThresholdPercent * last_send_bitrate_ / 100) { // The new bitrate estimate is less than kSendThresholdPercent % of the // last report. Send a REMB asap. - last_remb_time_ = TickTime::MillisecondTimestamp() - kRembSendIntervallMs; + last_remb_time_ = TickTime::MillisecondTimestamp() - kRembSendIntervalMs; } } bitrate_ = bitrate; @@ -125,7 +113,7 @@ // Calculate total receive bitrate estimate. int64_t now = TickTime::MillisecondTimestamp(); - if (now - last_remb_time_ < kRembSendIntervallMs) { + if (now - last_remb_time_ < kRembSendIntervalMs) { list_crit_->Leave(); return; } @@ -145,11 +133,6 @@ } last_send_bitrate_ = bitrate_; - // Never send a REMB lower than last_send_bitrate_. - if (last_send_bitrate_ < kRembMinimumBitrateKbps) { - last_send_bitrate_ = kRembMinimumBitrateKbps; - } - list_crit_->Leave(); if (sender) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_remb_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_remb_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_remb_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_remb_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -18,35 +18,26 @@ #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" #include "webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h" -#include "webrtc/modules/utility/interface/process_thread.h" +#include "webrtc/modules/utility/interface/mock/mock_process_thread.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/system_wrappers/interface/tick_util.h" #include "webrtc/video_engine/vie_remb.h" using ::testing::_; using ::testing::AnyNumber; +using ::testing::NiceMock; using ::testing::Return; namespace webrtc { -class TestProcessThread : public ProcessThread { - public: - explicit TestProcessThread() {} - ~TestProcessThread() {} - virtual int32_t Start() { return 0; } - virtual int32_t Stop() { return 0; } - virtual int32_t RegisterModule(Module* module) { return 0; } - virtual int32_t DeRegisterModule(const Module* module) { return 0; } -}; - class ViERembTest : public ::testing::Test { protected: virtual void SetUp() { TickTime::UseFakeClock(12345); - process_thread_.reset(new TestProcessThread); + process_thread_.reset(new NiceMock); vie_remb_.reset(new VieRemb()); } - scoped_ptr process_thread_; + scoped_ptr process_thread_; scoped_ptr vie_remb_; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_renderer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_renderer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_renderer.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_renderer.cc 2015-02-03 14:33:38.000000000 +0000 @@ -181,6 +181,7 @@ external_renderer_->DeliverFrame(NULL, 0, video_frame.timestamp(), + video_frame.ntp_time_ms(), video_frame.render_time_ms(), video_frame.native_handle()); } else { @@ -244,6 +245,7 @@ external_renderer_->DeliverFrame(out_frame->Buffer(), out_frame->Length(), video_frame.timestamp(), + video_frame.ntp_time_ms(), video_frame.render_time_ms(), NULL); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_render_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_render_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_render_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_render_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -13,7 +13,7 @@ #include "webrtc/engine_configurations.h" #include "webrtc/modules/video_render/include/video_render.h" #include "webrtc/modules/video_render/include/video_render_defines.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/video_engine/include/vie_errors.h" #include "webrtc/video_engine/vie_capturer.h" #include "webrtc/video_engine/vie_channel.h" @@ -44,36 +44,24 @@ } int ViERenderImpl::Release() { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, shared_data_->instance_id(), - "ViERender::Release()"); // Decrease ref count (*this)--; int32_t ref_count = GetCount(); if (ref_count < 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, shared_data_->instance_id(), - "ViERender release too many times"); + LOG(LS_ERROR) << "ViERender release too many times"; return -1; } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, shared_data_->instance_id(), - "ViERender reference count: %d", ref_count); return ref_count; } ViERenderImpl::ViERenderImpl(ViESharedData* shared_data) - : shared_data_(shared_data) { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_->instance_id(), - "ViERenderImpl::ViERenderImpl() Ctor"); -} + : shared_data_(shared_data) {} -ViERenderImpl::~ViERenderImpl() { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_->instance_id(), - "ViERenderImpl::~ViERenderImpl() Dtor"); -} +ViERenderImpl::~ViERenderImpl() {} int ViERenderImpl::RegisterVideoRenderModule( VideoRender& render_module) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s (&render_module: %p)", __FUNCTION__, &render_module); + LOG_F(LS_INFO); if (shared_data_->render_manager()->RegisterVideoRenderModule( &render_module) != 0) { shared_data_->SetLastError(kViERenderUnknownError); @@ -84,8 +72,7 @@ int ViERenderImpl::DeRegisterVideoRenderModule( VideoRender& render_module) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s (&render_module: %p)", __FUNCTION__, &render_module); + LOG_F(LS_INFO); if (shared_data_->render_manager()->DeRegisterVideoRenderModule( &render_module) != 0) { // Error logging is done in ViERenderManager::DeRegisterVideoRenderModule. @@ -99,17 +86,14 @@ const unsigned int z_order, const float left, const float top, const float right, const float bottom) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s (render_id: %d, window: 0x%p, z_order: %u, left: %f, " - "top: %f, right: %f, bottom: %f)", - __FUNCTION__, render_id, window, z_order, left, top, right, - bottom); + LOG_F(LS_INFO) << "render_id: " << render_id << " z_order: " << z_order + << " left: " << left << " top: " << top << " right: " << right + << " bottom: " << bottom; { ViERenderManagerScoped rs(*(shared_data_->render_manager())); if (rs.Renderer(render_id)) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s - Renderer already exist %d.", __FUNCTION__, - render_id); + LOG(LS_ERROR) << "Renderer for render_id: " << render_id + << " already exists."; shared_data_->SetLastError(kViERenderAlreadyExists); return -1; } @@ -119,9 +103,6 @@ ViEChannelManagerScoped cm(*(shared_data_->channel_manager())); ViEFrameProviderBase* frame_provider = cm.Channel(render_id); if (!frame_provider) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s: FrameProvider id %d doesn't exist", __FUNCTION__, - render_id); shared_data_->SetLastError(kViERenderInvalidRenderId); return -1; } @@ -137,9 +118,6 @@ ViEInputManagerScoped is(*(shared_data_->input_manager())); ViEFrameProviderBase* frame_provider = is.FrameProvider(render_id); if (!frame_provider) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s: FrameProvider id %d doesn't exist", __FUNCTION__, - render_id); shared_data_->SetLastError(kViERenderInvalidRenderId); return -1; } @@ -154,17 +132,12 @@ } int ViERenderImpl::RemoveRenderer(const int render_id) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s(render_id: %d)", __FUNCTION__, render_id); + LOG_F(LS_INFO) << "render_id: " << render_id; ViERenderer* renderer = NULL; { ViERenderManagerScoped rs(*(shared_data_->render_manager())); renderer = rs.Renderer(render_id); if (!renderer) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, - ViEId(shared_data_->instance_id()), - "%s No render exist with render_id: %d", __FUNCTION__, - render_id); shared_data_->SetLastError(kViERenderInvalidRenderId); return -1; } @@ -176,10 +149,6 @@ ViEChannelManagerScoped cm(*(shared_data_->channel_manager())); ViEChannel* channel = cm.Channel(render_id); if (!channel) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, - ViEId(shared_data_->instance_id()), - "%s: no channel with id %d exists ", __FUNCTION__, - render_id); shared_data_->SetLastError(kViERenderInvalidRenderId); return -1; } @@ -189,10 +158,6 @@ ViEInputManagerScoped is(*(shared_data_->input_manager())); ViEFrameProviderBase* provider = is.FrameProvider(render_id); if (!provider) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, - ViEId(shared_data_->instance_id()), - "%s: no provider with id %d exists ", __FUNCTION__, - render_id); shared_data_->SetLastError(kViERenderInvalidRenderId); return -1; } @@ -206,16 +171,10 @@ } int ViERenderImpl::StartRender(const int render_id) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), render_id), - "%s(channel: %d)", __FUNCTION__, render_id); + LOG_F(LS_INFO) << "render_id: " << render_id; ViERenderManagerScoped rs(*(shared_data_->render_manager())); ViERenderer* renderer = rs.Renderer(render_id); if (!renderer) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), render_id), - "%s: No renderer with render Id %d exist.", __FUNCTION__, - render_id); shared_data_->SetLastError(kViERenderInvalidRenderId); return -1; } @@ -227,16 +186,10 @@ } int ViERenderImpl::StopRender(const int render_id) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), render_id), - "%s(channel: %d)", __FUNCTION__, render_id); + LOG_F(LS_INFO) << "render_id: " << render_id; ViERenderManagerScoped rs(*(shared_data_->render_manager())); ViERenderer* renderer = rs.Renderer(render_id); if (!renderer) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), render_id), - "%s: No renderer with render_id %d exist.", __FUNCTION__, - render_id); shared_data_->SetLastError(kViERenderInvalidRenderId); return -1; } @@ -248,16 +201,11 @@ } int ViERenderImpl::SetExpectedRenderDelay(int render_id, int render_delay) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), render_id), - "%s(channel: %d)", __FUNCTION__, render_id); + LOG_F(LS_INFO) << "render_id: " << render_id + << " render_delay: " << render_delay; ViERenderManagerScoped rs(*(shared_data_->render_manager())); ViERenderer* renderer = rs.Renderer(render_id); if (!renderer) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), render_id), - "%s: No renderer with render_id %d exist.", __FUNCTION__, - render_id); shared_data_->SetLastError(kViERenderInvalidRenderId); return -1; } @@ -271,16 +219,12 @@ int ViERenderImpl::ConfigureRender(int render_id, const unsigned int z_order, const float left, const float top, const float right, const float bottom) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), render_id), - "%s(channel: %d)", __FUNCTION__, render_id); + LOG_F(LS_INFO) << "render_id: " << render_id << " z_order: " << z_order + << " left: " << left << " top: " << top << " right: " << right + << " bottom: " << bottom; ViERenderManagerScoped rs(*(shared_data_->render_manager())); ViERenderer* renderer = rs.Renderer(render_id); if (!renderer) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), render_id), - "%s: No renderer with render_id %d exist.", __FUNCTION__, - render_id); shared_data_->SetLastError(kViERenderInvalidRenderId); return -1; } @@ -298,10 +242,6 @@ ViERenderManagerScoped rs(*(shared_data_->render_manager())); ViERenderer* renderer = rs.Renderer(render_id); if (!renderer) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), render_id), - "%s: No renderer with render_id %d exist.", __FUNCTION__, - render_id); shared_data_->SetLastError(kViERenderInvalidRenderId); return -1; } @@ -326,10 +266,7 @@ video_input_format != kVideoRGB565 && video_input_format != kVideoARGB4444 && video_input_format != kVideoARGB1555) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), render_id), - "%s: Unsupported video frame format requested", - __FUNCTION__, render_id); + LOG(LS_ERROR) << "Unsupported video frame format requested."; shared_data_->SetLastError(kViERenderInvalidFrameFormat); return -1; } @@ -337,9 +274,7 @@ // Verify the renderer doesn't exist. ViERenderManagerScoped rs(*(shared_data_->render_manager())); if (rs.Renderer(render_id)) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s - Renderer already exist %d.", __FUNCTION__, - render_id); + LOG_F(LS_ERROR) << "Renderer already exists for render_id: " << render_id; shared_data_->SetLastError(kViERenderAlreadyExists); return -1; } @@ -349,9 +284,6 @@ ViEChannelManagerScoped cm(*(shared_data_->channel_manager())); ViEFrameProviderBase* frame_provider = cm.Channel(render_id); if (!frame_provider) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s: FrameProvider id %d doesn't exist", __FUNCTION__, - render_id); shared_data_->SetLastError(kViERenderInvalidRenderId); return -1; } @@ -373,9 +305,6 @@ ViEInputManagerScoped is(*(shared_data_->input_manager())); ViEFrameProviderBase* frame_provider = is.FrameProvider(render_id); if (!frame_provider) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(shared_data_->instance_id()), - "%s: FrameProvider id %d doesn't exist", __FUNCTION__, - render_id); shared_data_->SetLastError(kViERenderInvalidRenderId); return -1; } @@ -402,12 +331,6 @@ ViEChannelManagerScoped cm(*(shared_data_->channel_manager())); ViEFrameProviderBase* frame_provider = cm.Channel(render_id); if (!frame_provider) { - WEBRTC_TRACE(kTraceError, - kTraceVideo, - ViEId(shared_data_->instance_id()), - "%s: FrameProvider id %d doesn't exist", - __FUNCTION__, - render_id); shared_data_->SetLastError(kViERenderInvalidRenderId); return -1; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_render_manager.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_render_manager.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_render_manager.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_render_manager.cc 2015-02-03 14:33:38.000000000 +0000 @@ -15,7 +15,7 @@ #include "webrtc/modules/video_render/include/video_render_defines.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/video_engine/vie_defines.h" #include "webrtc/video_engine/vie_renderer.h" @@ -35,14 +35,9 @@ : list_cs_(CriticalSectionWrapper::CreateCriticalSection()), engine_id_(engine_id), use_external_render_module_(false) { - WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo, ViEId(engine_id), - "ViERenderManager::ViERenderManager(engine_id: %d) - " - "Constructor", engine_id); } ViERenderManager::~ViERenderManager() { - WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo, ViEId(engine_id_), - "ViERenderManager Destructor, engine_id: %d", engine_id_); for (RendererMap::iterator it = stream_to_vie_renderer_.begin(); it != stream_to_vie_renderer_.end(); ++it) { @@ -57,10 +52,7 @@ // the registrant render module is associated with. VideoRender* current_module = FindRenderModule(render_module->Window()); if (current_module) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), - "A module is already registered for this window (window=%p, " - "current module=%p, registrant module=%p.", - render_module->Window(), current_module, render_module); + LOG_F(LS_ERROR) << "A render module is already registered for this window."; return -1; } @@ -75,9 +67,8 @@ // Check if there are streams in the module. uint32_t n_streams = render_module->GetNumIncomingRenderStreams(); if (n_streams != 0) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), - "There are still %d streams in this module, cannot " - "de-register", n_streams); + LOG(LS_ERROR) << "There are still " << n_streams + << "in this module, cannot de-register."; return -1; } @@ -89,8 +80,8 @@ return 0; } } - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), - "Module not registered"); + + LOG(LS_ERROR) << "Module not registered."; return -1; } @@ -105,9 +96,7 @@ if (stream_to_vie_renderer_.find(render_id) != stream_to_vie_renderer_.end()) { - // This stream is already added to a renderer, not allowed! - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), - "Render stream already exists"); + LOG(LS_ERROR) << "Render stream already exists"; return NULL; } @@ -117,11 +106,9 @@ // No render module for this window, create a new one. render_module = VideoRender::CreateVideoRender(ViEModuleId(engine_id_, -1), window, false); - if (!render_module) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), - "Could not create new render module"); + if (!render_module) return NULL; - } + render_list_.push_back(render_module); } @@ -131,12 +118,9 @@ *this, z_order, left, top, right, bottom); - if (!vie_renderer) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(engine_id_, render_id), - "Could not create new render stream"); + if (!vie_renderer) return NULL; - } + stream_to_vie_renderer_[render_id] = vie_renderer; return vie_renderer; } @@ -149,9 +133,7 @@ CriticalSectionScoped cs(list_cs_.get()); RendererMap::iterator it = stream_to_vie_renderer_.find(render_id); if (it == stream_to_vie_renderer_.end()) { - // No such stream - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideo, ViEId(engine_id_), - "No renderer for this stream found, channel_id"); + LOG(LS_ERROR) << "No renderer found for render_id: " << render_id; return 0; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_rtp_rtcp_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_rtp_rtcp_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_rtp_rtcp_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_rtp_rtcp_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -12,7 +12,7 @@ #include "webrtc/engine_configurations.h" #include "webrtc/system_wrappers/interface/file_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/video_engine/include/vie_errors.h" #include "webrtc/video_engine/vie_channel.h" #include "webrtc/video_engine/vie_channel_manager.h" @@ -90,48 +90,31 @@ } int ViERTP_RTCPImpl::Release() { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, shared_data_->instance_id(), - "ViERTP_RTCP::Release()"); // Decrease ref count. (*this)--; int32_t ref_count = GetCount(); if (ref_count < 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, shared_data_->instance_id(), - "ViERTP_RTCP release too many times"); + LOG(LS_ERROR) << "ViERTP_RTCP released too many times."; shared_data_->SetLastError(kViEAPIDoesNotExist); return -1; } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, shared_data_->instance_id(), - "ViERTP_RTCP reference count: %d", ref_count); return ref_count; } ViERTP_RTCPImpl::ViERTP_RTCPImpl(ViESharedData* shared_data) - : shared_data_(shared_data) { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_->instance_id(), - "ViERTP_RTCPImpl::ViERTP_RTCPImpl() Ctor"); -} + : shared_data_(shared_data) {} -ViERTP_RTCPImpl::~ViERTP_RTCPImpl() { - WEBRTC_TRACE(kTraceMemory, kTraceVideo, shared_data_->instance_id(), - "ViERTP_RTCPImpl::~ViERTP_RTCPImpl() Dtor"); -} +ViERTP_RTCPImpl::~ViERTP_RTCPImpl() {} int ViERTP_RTCPImpl::SetLocalSSRC(const int video_channel, const unsigned int SSRC, const StreamType usage, const unsigned char simulcast_idx) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, SSRC: %d)", __FUNCTION__, video_channel, SSRC); + LOG_F(LS_INFO) << "channel: " << video_channel << " ssrc: " << SSRC << ""; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - // The channel doesn't exists - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -145,20 +128,13 @@ int ViERTP_RTCPImpl::SetRemoteSSRCType(const int videoChannel, const StreamType usage, const unsigned int SSRC) const { - WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo, - ViEId(shared_data_->instance_id(), videoChannel), - "%s(channel: %d, usage:%d SSRC: 0x%x)", - __FUNCTION__, usage, videoChannel, SSRC); + LOG_F(LS_INFO) << "channel: " << videoChannel + << " usage: " << static_cast(usage) << " ssrc: " << SSRC; // Get the channel ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* ptrViEChannel = cs.Channel(videoChannel); if (ptrViEChannel == NULL) { - // The channel doesn't exists - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, - ViEId(shared_data_->instance_id(), videoChannel), - "%s: Channel %d doesn't exist", - __FUNCTION__, videoChannel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -171,15 +147,9 @@ int ViERTP_RTCPImpl::GetLocalSSRC(const int video_channel, unsigned int& SSRC) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, SSRC: %d)", __FUNCTION__, video_channel, SSRC); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -193,15 +163,9 @@ int ViERTP_RTCPImpl::GetRemoteSSRC(const int video_channel, unsigned int& SSRC) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel, SSRC); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -214,15 +178,9 @@ int ViERTP_RTCPImpl::GetRemoteCSRCs(const int video_channel, unsigned int CSRCs[kRtpCsrcSize]) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -235,15 +193,11 @@ int ViERTP_RTCPImpl::SetRtxSendPayloadType(const int video_channel, const uint8_t payload_type) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "channel: " << video_channel + << " payload_type: " << static_cast(payload_type); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -253,17 +207,28 @@ return 0; } +int ViERTP_RTCPImpl::SetPadWithRedundantPayloads(int video_channel, + bool enable) { + LOG_F(LS_INFO) << "channel: " << video_channel + << " pad with redundant payloads: " << (enable ? "enable" : + "disable"); + ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); + ViEChannel* vie_channel = cs.Channel(video_channel); + if (!vie_channel) { + shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); + return -1; + } + vie_channel->SetPadWithRedundantPayloads(enable); + return 0; +} + int ViERTP_RTCPImpl::SetRtxReceivePayloadType(const int video_channel, const uint8_t payload_type) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "channel: " << video_channel + << " payload_type: " << static_cast(payload_type); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -273,24 +238,14 @@ int ViERTP_RTCPImpl::SetStartSequenceNumber(const int video_channel, uint16_t sequence_number) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, sequence_number: %u)", __FUNCTION__, - video_channel, sequence_number); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } if (vie_channel->Sending()) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d already sending.", __FUNCTION__, - video_channel); + LOG_F(LS_ERROR) << "channel " << video_channel << " is already sending."; shared_data_->SetLastError(kViERtpRtcpAlreadySending); return -1; } @@ -301,18 +256,37 @@ return 0; } +void ViERTP_RTCPImpl::SetRtpStateForSsrc(int video_channel, + uint32_t ssrc, + const RtpState& rtp_state) { + ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); + ViEChannel* vie_channel = cs.Channel(video_channel); + if (!vie_channel) + return; + + if (vie_channel->Sending()) { + LOG_F(LS_ERROR) << "channel " << video_channel << " is already sending."; + return; + } + vie_channel->SetRtpStateForSsrc(ssrc, rtp_state); +} + +RtpState ViERTP_RTCPImpl::GetRtpStateForSsrc(int video_channel, uint32_t ssrc) { + ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); + ViEChannel* vie_channel = cs.Channel(video_channel); + if (!vie_channel) + return RtpState(); + + return vie_channel->GetRtpStateForSsrc(ssrc); +} + int ViERTP_RTCPImpl::SetRTCPStatus(const int video_channel, const ViERTCPMode rtcp_mode) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, mode: %d)", __FUNCTION__, video_channel, - rtcp_mode); + LOG_F(LS_INFO) << "channel: " << video_channel + << " mode: " << static_cast(rtcp_mode); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -327,23 +301,14 @@ int ViERTP_RTCPImpl::GetRTCPStatus(const int video_channel, ViERTCPMode& rtcp_mode) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel, rtcp_mode); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } RTCPMethod module_mode = kRtcpOff; if (vie_channel->GetRTCPMode(&module_mode) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: could not get current RTCP mode", __FUNCTION__); shared_data_->SetLastError(kViERtpRtcpUnknownError); return -1; } @@ -353,24 +318,16 @@ int ViERTP_RTCPImpl::SetRTCPCName(const int video_channel, const char rtcp_cname[KMaxRTCPCNameLength]) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, name: %s)", __FUNCTION__, video_channel, - rtcp_cname); + LOG_F(LS_INFO) << "channel: " << video_channel + << " rtcp_cname: " << rtcp_cname; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } if (vie_channel->Sending()) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d already sending.", __FUNCTION__, - video_channel); + LOG_F(LS_ERROR) << "channel " << video_channel << " is already sending."; shared_data_->SetLastError(kViERtpRtcpAlreadySending); return -1; } @@ -381,39 +338,12 @@ return 0; } -int ViERTP_RTCPImpl::GetRTCPCName(const int video_channel, - char rtcp_cname[KMaxRTCPCNameLength]) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); - ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); - ViEChannel* vie_channel = cs.Channel(video_channel); - if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); - shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); - return -1; - } - if (vie_channel->GetRTCPCName(rtcp_cname) != 0) { - shared_data_->SetLastError(kViERtpRtcpUnknownError); - return -1; - } - return 0; -} - int ViERTP_RTCPImpl::GetRemoteRTCPCName( const int video_channel, char rtcp_cname[KMaxRTCPCNameLength]) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -433,15 +363,11 @@ uint16_t* fractionLost, uint32_t* cumulativeLost, int32_t* rttMs) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "channel:" << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); + LOG(LS_ERROR) << "Channel " << video_channel << " doesn't exist"; shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -465,33 +391,18 @@ unsigned int name, const char* data, uint16_t data_length_in_bytes) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, sub_type: %c, name: %d, data: x, length: %u)", - __FUNCTION__, video_channel, sub_type, name, - data_length_in_bytes); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } if (!vie_channel->Sending()) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d not sending", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpNotSending); return -1; } RTCPMethod method; if (vie_channel->GetRTCPMode(&method) != 0 || method == kRtcpOff) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: RTCP disabled on channel %d.", __FUNCTION__, - video_channel); shared_data_->SetLastError(kViERtpRtcpRtcpDisabled); return -1; } @@ -505,23 +416,15 @@ } int ViERTP_RTCPImpl::SetNACKStatus(const int video_channel, const bool enable) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, enable: %d)", __FUNCTION__, video_channel, - enable); + LOG_F(LS_INFO) << "channel: " << video_channel << " " + << (enable ? "on" : "off"); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } if (vie_channel->SetNACKStatus(enable) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: failed for channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpUnknownError); return -1; } @@ -529,10 +432,6 @@ // Update the encoder ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Could not get encoder for channel %d", __FUNCTION__, - video_channel); shared_data_->SetLastError(kViERtpRtcpUnknownError); return -1; } @@ -543,37 +442,24 @@ int ViERTP_RTCPImpl::SetFECStatus(const int video_channel, const bool enable, const unsigned char payload_typeRED, const unsigned char payload_typeFEC) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, enable: %d, payload_typeRED: %u, " - "payloadTypeFEC: %u)", - __FUNCTION__, video_channel, enable, payload_typeRED, - payload_typeFEC); + LOG_F(LS_INFO) << "channel: " << video_channel + << " enable: " << (enable ? "on" : "off") + << " payload_typeRED: " << static_cast(payload_typeRED) + << " payload_typeFEC: " << static_cast(payload_typeFEC); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, - video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } if (vie_channel->SetFECStatus(enable, payload_typeRED, payload_typeFEC) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: failed for channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpUnknownError); return -1; } // Update the encoder. ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Could not get encoder for channel %d", __FUNCTION__, - video_channel); shared_data_->SetLastError(kViERtpRtcpUnknownError); return -1; } @@ -586,18 +472,13 @@ const bool enable, const unsigned char payload_typeRED, const unsigned char payload_typeFEC) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, enable: %d, payload_typeRED: %u, " - "payloadTypeFEC: %u)", - __FUNCTION__, video_channel, enable, payload_typeRED, - payload_typeFEC); + LOG_F(LS_INFO) << "channel: " << video_channel + << " enable: " << (enable ? "on" : "off") + << " payload_typeRED: " << static_cast(payload_typeRED) + << " payload_typeFEC: " << static_cast(payload_typeFEC); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -605,9 +486,6 @@ // Update the channel status with hybrid NACK FEC mode. if (vie_channel->SetHybridNACKFECStatus(enable, payload_typeRED, payload_typeFEC) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: failed for channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpUnknownError); return -1; } @@ -615,10 +493,6 @@ // Update the encoder. ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Could not get encoder for channel %d", __FUNCTION__, - video_channel); shared_data_->SetLastError(kViERtpRtcpUnknownError); return -1; } @@ -627,35 +501,23 @@ } int ViERTP_RTCPImpl::SetSenderBufferingMode(int video_channel, - int target_delay_ms) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, sender target_delay: %d)", - __FUNCTION__, video_channel, target_delay_ms); + int target_delay_ms) { + LOG_F(LS_INFO) << "channel: " << video_channel + << " target_delay_ms: " << target_delay_ms; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } ViEEncoder* vie_encoder = cs.Encoder(video_channel); if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Could not get encoder for channel %d", __FUNCTION__, - video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } // Update the channel with buffering mode settings. if (vie_channel->SetSenderBufferingMode(target_delay_ms) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: failed for channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpUnknownError); return -1; } @@ -666,26 +528,18 @@ } int ViERTP_RTCPImpl::SetReceiverBufferingMode(int video_channel, - int target_delay_ms) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, receiver target_delay: %d)", - __FUNCTION__, video_channel, target_delay_ms); + int target_delay_ms) { + LOG_F(LS_INFO) << "channel: " << video_channel + << " target_delay_ms: " << target_delay_ms; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } // Update the channel with buffering mode settings. if (vie_channel->SetReceiverBufferingMode(target_delay_ms) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: failed for channel %d", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpUnknownError); return -1; } @@ -695,18 +549,12 @@ int ViERTP_RTCPImpl::SetKeyFrameRequestMethod( const int video_channel, const ViEKeyFrameRequestMethod method) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, method: %d)", __FUNCTION__, video_channel, - method); + LOG_F(LS_INFO) << "channel: " << video_channel + << " method: " << static_cast(method); - // Get the channel. ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -720,16 +568,11 @@ int ViERTP_RTCPImpl::SetTMMBRStatus(const int video_channel, const bool enable) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, enable: %d)", __FUNCTION__, video_channel, - enable); + LOG_F(LS_INFO) << "channel: " << video_channel + << "enable: " << (enable ? "on" : "off"); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -740,12 +583,12 @@ return 0; } -int ViERTP_RTCPImpl::SetRembStatus(int video_channel, bool sender, +int ViERTP_RTCPImpl::SetRembStatus(int video_channel, + bool sender, bool receiver) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "ViERTP_RTCPImpl::SetRembStatus(%d, %d, %d)", video_channel, - sender, receiver); + LOG_F(LS_INFO) << "channel: " << video_channel + << " sender: " << (sender ? "on" : "off") + << " receiver: " << (receiver ? "on" : "off"); if (!shared_data_->channel_manager()->SetRembStatus(video_channel, sender, receiver)) { return -1; @@ -756,17 +599,12 @@ int ViERTP_RTCPImpl::SetSendTimestampOffsetStatus(int video_channel, bool enable, int id) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "ViERTP_RTCPImpl::SetSendTimestampOffsetStatus(%d, %d, %d)", - video_channel, enable, id); + LOG_F(LS_INFO) << "channel: " << video_channel + << "enable: " << (enable ? "on" : "off") << " id: " << id; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -780,16 +618,11 @@ int ViERTP_RTCPImpl::SetReceiveTimestampOffsetStatus(int video_channel, bool enable, int id) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "ViERTP_RTCPImpl::SetReceiveTimestampOffsetStatus(%d, %d, %d)", - video_channel, enable, id); + LOG_F(LS_INFO) << "channel: " << video_channel + << "enable: " << (enable ? "on" : "off") << " id: " << id; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -803,17 +636,12 @@ int ViERTP_RTCPImpl::SetSendAbsoluteSendTimeStatus(int video_channel, bool enable, int id) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "ViERTP_RTCPImpl::SetSendAbsoluteSendTimeStatus(%d, %d, %d)", - video_channel, enable, id); + LOG_F(LS_INFO) << "channel: " << video_channel + << "enable: " << (enable ? "on" : "off") << " id: " << id; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -827,16 +655,11 @@ int ViERTP_RTCPImpl::SetReceiveAbsoluteSendTimeStatus(int video_channel, bool enable, int id) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "ViERTP_RTCPImpl::SetReceiveAbsoluteSendTimeStatus(%d, %d, %d)", - video_channel, enable, id); + LOG_F(LS_INFO) << "channel: " << video_channel + << "enable: " << (enable ? "on" : "off") << " id: " << id; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -848,17 +671,12 @@ } int ViERTP_RTCPImpl::SetRtcpXrRrtrStatus(int video_channel, bool enable) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "ViERTP_RTCPImpl::SetRtcpXrRrtrStatus(%d, %d)", - video_channel, enable); + LOG_F(LS_INFO) << "channel: " << video_channel + << " enable: " << (enable ? "on" : "off"); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -868,16 +686,11 @@ int ViERTP_RTCPImpl::SetTransmissionSmoothingStatus(int video_channel, bool enable) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, enble: %d)", __FUNCTION__, video_channel, - enable); + LOG_F(LS_INFO) << "channel: " << video_channel + << " enable: " << (enable ? "on" : "off"); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -885,19 +698,37 @@ return 0; } +int ViERTP_RTCPImpl::SetMinTransmitBitrate(int video_channel, + int min_transmit_bitrate_kbps) { + LOG_F(LS_INFO) << "channel: " << video_channel + << " min_transmit_bitrate_kbps: " << min_transmit_bitrate_kbps; + ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); + ViEEncoder* vie_encoder = cs.Encoder(video_channel); + if (vie_encoder == NULL) + return -1; + vie_encoder->SetMinTransmitBitrate(min_transmit_bitrate_kbps); + return 0; +} + +int ViERTP_RTCPImpl::SetReservedTransmitBitrate( + int video_channel, unsigned int reserved_transmit_bitrate_bps) { + LOG_F(LS_INFO) << "channel: " << video_channel + << " reserved_transmit_bitrate_bps: " + << reserved_transmit_bitrate_bps; + if (!shared_data_->channel_manager()->SetReservedTransmitBitrate( + video_channel, reserved_transmit_bitrate_bps)) { + return -1; + } + return 0; +} + int ViERTP_RTCPImpl::GetReceiveChannelRtcpStatistics( const int video_channel, RtcpStatistics& basic_stats, int& rtt_ms) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -920,15 +751,9 @@ int ViERTP_RTCPImpl::GetSendChannelRtcpStatistics(const int video_channel, RtcpStatistics& basic_stats, int& rtt_ms) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -951,15 +776,9 @@ int ViERTP_RTCPImpl::GetRtpStatistics(const int video_channel, StreamDataCounters& sent, StreamDataCounters& received) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -973,17 +792,27 @@ return 0; } +int ViERTP_RTCPImpl::GetRtcpPacketTypeCounters( + int video_channel, + RtcpPacketTypeCounter* packets_sent, + RtcpPacketTypeCounter* packets_received) const { + ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); + ViEChannel* vie_channel = cs.Channel(video_channel); + if (!vie_channel) { + shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); + return -1; + } + vie_channel->GetRtcpPacketTypeCounters(packets_sent, packets_received); + return 0; +} + int ViERTP_RTCPImpl::GetRemoteRTCPSenderInfo(const int video_channel, SenderInfo* sender_info) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "channel:" << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); + LOG(LS_ERROR) << "Channel " << video_channel << " doesn't exist"; shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -999,15 +828,9 @@ unsigned int& video_bitrate_sent, unsigned int& fec_bitrate_sent, unsigned int& nackBitrateSent) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -1021,57 +844,29 @@ int ViERTP_RTCPImpl::GetEstimatedSendBandwidth( const int video_channel, unsigned int* estimated_bandwidth) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); - ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); - ViEEncoder* vie_encoder = cs.Encoder(video_channel); - if (!vie_encoder) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Could not get encoder for channel %d", __FUNCTION__, - video_channel); - shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); + if (!shared_data_->channel_manager()->GetEstimatedSendBandwidth( + video_channel, estimated_bandwidth)) { return -1; } - return vie_encoder->EstimatedSendBandwidth( - static_cast(estimated_bandwidth)); + return 0; } int ViERTP_RTCPImpl::GetEstimatedReceiveBandwidth( const int video_channel, unsigned int* estimated_bandwidth) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); - ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); - ViEChannel* vie_channel = cs.Channel(video_channel); - if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Could not get channel %d", __FUNCTION__, - video_channel); - shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); + if (!shared_data_->channel_manager()->GetEstimatedReceiveBandwidth( + video_channel, estimated_bandwidth)) { return -1; } - vie_channel->GetEstimatedReceiveBandwidth( - static_cast(estimated_bandwidth)); return 0; } int ViERTP_RTCPImpl::GetReceiveBandwidthEstimatorStats( const int video_channel, ReceiveBandwidthEstimatorStats* output) const { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Could not get channel %d", __FUNCTION__, - video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -1079,20 +874,28 @@ return 0; } +int ViERTP_RTCPImpl::GetPacerQueuingDelayMs( + const int video_channel, int* delay_ms) const { + ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); + ViEEncoder* vie_encoder = cs.Encoder(video_channel); + if (!vie_encoder) { + shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); + return -1; + } + *delay_ms = vie_encoder->PacerQueuingDelayMs(); + return 0; +} + int ViERTP_RTCPImpl::StartRTPDump(const int video_channel, const char file_nameUTF8[1024], RTPDirections direction) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, file_name: %s, direction: %d)", __FUNCTION__, - video_channel, file_nameUTF8, direction); + LOG_F(LS_INFO) << "channel: " << video_channel + << " filename: " << file_nameUTF8 + << " direction: " << static_cast(direction); assert(FileWrapper::kMaxFileNameSize == 1024); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -1105,16 +908,11 @@ int ViERTP_RTCPImpl::StopRTPDump(const int video_channel, RTPDirections direction) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d, direction: %d)", __FUNCTION__, video_channel, - direction); + LOG_F(LS_INFO) << "channel: " << video_channel + << " direction: " << static_cast(direction); ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -1127,15 +925,10 @@ int ViERTP_RTCPImpl::RegisterRTPObserver(const int video_channel, ViERTPObserver& observer) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -1147,15 +940,10 @@ } int ViERTP_RTCPImpl::DeregisterRTPObserver(const int video_channel) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -1168,15 +956,10 @@ int ViERTP_RTCPImpl::RegisterRTCPObserver(const int video_channel, ViERTCPObserver& observer) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -1188,15 +971,10 @@ } int ViERTP_RTCPImpl::DeregisterRTCPObserver(const int video_channel) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -1209,15 +987,10 @@ int ViERTP_RTCPImpl::RegisterSendChannelRtcpStatisticsCallback( int video_channel, RtcpStatisticsCallback* callback) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -1227,15 +1000,10 @@ int ViERTP_RTCPImpl::DeregisterSendChannelRtcpStatisticsCallback( int video_channel, RtcpStatisticsCallback* callback) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -1246,12 +1014,7 @@ int ViERTP_RTCPImpl::RegisterReceiveChannelRtcpStatisticsCallback( const int video_channel, RtcpStatisticsCallback* callback) { - WEBRTC_TRACE(kTraceApiCall, - kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", - __FUNCTION__, - video_channel); + LOG_F(LS_INFO) << "channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); assert(vie_channel != NULL); @@ -1262,12 +1025,7 @@ int ViERTP_RTCPImpl::DeregisterReceiveChannelRtcpStatisticsCallback( const int video_channel, RtcpStatisticsCallback* callback) { - WEBRTC_TRACE(kTraceApiCall, - kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", - __FUNCTION__, - video_channel); + LOG_F(LS_INFO) << "channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); assert(vie_channel != NULL); @@ -1277,9 +1035,7 @@ int ViERTP_RTCPImpl::RegisterSendChannelRtpStatisticsCallback( int video_channel, StreamDataCountersCallback* callback) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); assert(vie_channel != NULL); @@ -1289,9 +1045,7 @@ int ViERTP_RTCPImpl::DeregisterSendChannelRtpStatisticsCallback( int video_channel, StreamDataCountersCallback* callback) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); assert(vie_channel != NULL); @@ -1302,12 +1056,7 @@ int ViERTP_RTCPImpl::RegisterReceiveChannelRtpStatisticsCallback( const int video_channel, StreamDataCountersCallback* callback) { - WEBRTC_TRACE(kTraceApiCall, - kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", - __FUNCTION__, - video_channel); + LOG_F(LS_INFO) << "channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); assert(vie_channel != NULL); @@ -1318,12 +1067,7 @@ int ViERTP_RTCPImpl::DeregisterReceiveChannelRtpStatisticsCallback( const int video_channel, StreamDataCountersCallback* callback) { - WEBRTC_TRACE(kTraceApiCall, - kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", - __FUNCTION__, - video_channel); + LOG_F(LS_INFO) << "channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); assert(vie_channel != NULL); @@ -1335,12 +1079,7 @@ int ViERTP_RTCPImpl::RegisterSendBitrateObserver( const int video_channel, BitrateStatisticsObserver* observer) { - WEBRTC_TRACE(kTraceApiCall, - kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", - __FUNCTION__, - video_channel); + LOG_F(LS_INFO) << "channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); assert(vie_channel != NULL); @@ -1351,12 +1090,7 @@ int ViERTP_RTCPImpl::DeregisterSendBitrateObserver( const int video_channel, BitrateStatisticsObserver* observer) { - WEBRTC_TRACE(kTraceApiCall, - kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", - __FUNCTION__, - video_channel); + LOG_F(LS_INFO) << "channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); assert(vie_channel != NULL); @@ -1366,15 +1100,10 @@ int ViERTP_RTCPImpl::RegisterSendFrameCountObserver( int video_channel, FrameCountObserver* callback) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } @@ -1384,20 +1113,14 @@ int ViERTP_RTCPImpl::DeregisterSendFrameCountObserver( int video_channel, FrameCountObserver* callback) { - WEBRTC_TRACE(kTraceApiCall, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s(channel: %d)", __FUNCTION__, video_channel); + LOG_F(LS_INFO) << "channel " << video_channel; ViEChannelManagerScoped cs(*(shared_data_->channel_manager())); ViEChannel* vie_channel = cs.Channel(video_channel); if (!vie_channel) { - WEBRTC_TRACE(kTraceError, kTraceVideo, - ViEId(shared_data_->instance_id(), video_channel), - "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); return -1; } vie_channel->RegisterSendFrameCountObserver(NULL); return 0; } - } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_rtp_rtcp_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_rtp_rtcp_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_rtp_rtcp_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_rtp_rtcp_impl.h 2015-02-03 14:33:38.000000000 +0000 @@ -41,18 +41,22 @@ unsigned int CSRCs[kRtpCsrcSize]) const; virtual int SetRtxSendPayloadType(const int video_channel, const uint8_t payload_type); + virtual int SetPadWithRedundantPayloads(int video_channel, bool enable); virtual int SetRtxReceivePayloadType(const int video_channel, const uint8_t payload_type); virtual int SetStartSequenceNumber(const int video_channel, uint16_t sequence_number); + virtual void SetRtpStateForSsrc(int video_channel, + uint32_t ssrc, + const RtpState& rtp_state) OVERRIDE; + virtual RtpState GetRtpStateForSsrc(int video_channel, + uint32_t ssrc) OVERRIDE; virtual int SetRTCPStatus(const int video_channel, const ViERTCPMode rtcp_mode); virtual int GetRTCPStatus(const int video_channel, ViERTCPMode& rtcp_mode) const; virtual int SetRTCPCName(const int video_channel, const char rtcp_cname[KMaxRTCPCNameLength]); - virtual int GetRTCPCName(const int video_channel, - char rtcp_cname[KMaxRTCPCNameLength]) const; virtual int GetRemoteRTCPCName(const int video_channel, char rtcp_cname[KMaxRTCPCNameLength]) const; virtual int GetRemoteRTCPReceiverInfo(const int video_channel, @@ -99,6 +103,10 @@ int id); virtual int SetRtcpXrRrtrStatus(int video_channel, bool enable); virtual int SetTransmissionSmoothingStatus(int video_channel, bool enable); + virtual int SetMinTransmitBitrate(int video_channel, + int min_transmit_bitrate_kbps); + virtual int SetReservedTransmitBitrate( + int video_channel, unsigned int reserved_transmit_bitrate_bps); virtual int GetReceiveChannelRtcpStatistics(const int video_channel, RtcpStatistics& basic_stats, int& rtt_ms) const; @@ -108,6 +116,10 @@ virtual int GetRtpStatistics(const int video_channel, StreamDataCounters& sent, StreamDataCounters& received) const; + virtual int GetRtcpPacketTypeCounters( + int video_channel, + RtcpPacketTypeCounter* packets_sent, + RtcpPacketTypeCounter* packets_received) const; virtual int GetRemoteRTCPSenderInfo(const int video_channel, SenderInfo* sender_info) const; virtual int GetBandwidthUsage(const int video_channel, @@ -123,6 +135,8 @@ unsigned int* estimated_bandwidth) const; virtual int GetReceiveBandwidthEstimatorStats( const int video_channel, ReceiveBandwidthEstimatorStats* output) const; + virtual int GetPacerQueuingDelayMs(const int video_channel, + int* delay_ms) const; virtual int StartRTPDump(const int video_channel, const char file_nameUTF8[1024], RTPDirections direction); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_sender.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_sender.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_sender.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_sender.cc 2015-02-03 14:33:38.000000000 +0000 @@ -11,6 +11,7 @@ #include "webrtc/video_engine/vie_sender.h" #include +#include "webrtc/modules/rtp_rtcp/source/rtp_sender.h" #include "webrtc/modules/utility/interface/rtp_dump.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" @@ -59,16 +60,12 @@ } else { rtp_dump_ = RtpDump::CreateRtpDump(); if (rtp_dump_ == NULL) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, channel_id_, - "StartSRTPDump: Failed to create RTP dump"); return -1; } } if (rtp_dump_->Start(file_nameUTF8) != 0) { RtpDump::DestroyRtpDump(rtp_dump_); rtp_dump_ = NULL; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, channel_id_, - "StartRTPDump: Failed to start RTP dump"); return -1; } return 0; @@ -79,15 +76,10 @@ if (rtp_dump_) { if (rtp_dump_->IsActive()) { rtp_dump_->Stop(); - } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, channel_id_, - "StopRTPDump: Dump not active"); } RtpDump::DestroyRtpDump(rtp_dump_); rtp_dump_ = NULL; } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, channel_id_, - "StopRTPDump: RTP dump not started"); return -1; } return 0; @@ -106,12 +98,7 @@ static_cast(len)); } - const int bytes_sent = transport_->SendPacket(channel_id_, data, len); - if (bytes_sent != len) { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideo, channel_id_, - "ViESender::SendPacket - Transport failed to send RTP packet"); - } - return bytes_sent; + return transport_->SendPacket(channel_id_, data, len); } int ViESender::SendRTCPPacket(int vie_id, const void* data, int len) { @@ -126,14 +113,7 @@ static_cast(len)); } - const int bytes_sent = transport_->SendRTCPPacket(channel_id_, data, len); - if (bytes_sent != len) { - WEBRTC_TRACE( - webrtc::kTraceWarning, webrtc::kTraceVideo, channel_id_, - "ViESender::SendRTCPPacket - Transport failed to send RTCP packet" - " (%d vs %d)", bytes_sent, len); - } - return bytes_sent; + return transport_->SendRTCPPacket(channel_id_, data, len); } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_sender.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_sender.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_sender.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_sender.h 2015-02-03 14:33:38.000000000 +0000 @@ -40,8 +40,8 @@ int StopRTPDump(); // Implements Transport. - virtual int SendPacket(int vie_id, const void* data, int len); - virtual int SendRTCPPacket(int vie_id, const void* data, int len); + virtual int SendPacket(int vie_id, const void* data, int len) OVERRIDE; + virtual int SendRTCPPacket(int vie_id, const void* data, int len) OVERRIDE; private: const int32_t channel_id_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_sync_module.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_sync_module.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_sync_module.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_sync_module.cc 2015-02-03 14:33:38.000000000 +0000 @@ -14,7 +14,7 @@ #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" #include "webrtc/modules/video_coding/main/interface/video_coding.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/trace_event.h" #include "webrtc/video_engine/stream_synchronization.h" #include "webrtc/video_engine/vie_channel.h" @@ -30,31 +30,24 @@ return -1; if (!receiver.LastReceivedTimeMs(&stream->latest_receive_time_ms)) return -1; - synchronization::RtcpMeasurement measurement; - if (0 != rtp_rtcp.RemoteNTP(&measurement.ntp_secs, - &measurement.ntp_frac, + + uint32_t ntp_secs = 0; + uint32_t ntp_frac = 0; + uint32_t rtp_timestamp = 0; + if (0 != rtp_rtcp.RemoteNTP(&ntp_secs, + &ntp_frac, NULL, NULL, - &measurement.rtp_timestamp)) { + &rtp_timestamp)) { return -1; } - if (measurement.ntp_secs == 0 && measurement.ntp_frac == 0) { + + bool new_rtcp_sr = false; + if (!UpdateRtcpList( + ntp_secs, ntp_frac, rtp_timestamp, &stream->rtcp, &new_rtcp_sr)) { return -1; } - for (synchronization::RtcpList::iterator it = stream->rtcp.begin(); - it != stream->rtcp.end(); ++it) { - if (measurement.ntp_secs == (*it).ntp_secs && - measurement.ntp_frac == (*it).ntp_frac) { - // This RTCP has already been added to the list. - return 0; - } - } - // We need two RTCP SR reports to map between RTP and NTP. More than two will - // not improve the mapping. - if (stream->rtcp.size() == 2) { - stream->rtcp.pop_back(); - } - stream->rtcp.push_front(measurement); + return 0; } @@ -110,8 +103,6 @@ last_sync_time_ = TickTime::Now(); const int current_video_delay_ms = vcm_->Delay(); - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, vie_channel_->Id(), - "Video delay (JB + decoder) is %d ms", current_video_delay_ms); if (voe_channel_id_ == -1) { return 0; @@ -126,11 +117,6 @@ &audio_jitter_buffer_delay_ms, &playout_buffer_delay_ms, &avsync_offset_ms) != 0) { - // Could not get VoE delay value, probably not a valid channel Id or - // the channel have not received enough packets. - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo, vie_channel_->Id(), - "%s: VE_GetDelayEstimate error for voice_channel %d", - __FUNCTION__, voe_channel_id_); return 0; } const int current_audio_delay_ms = audio_jitter_buffer_delay_ms + @@ -180,15 +166,9 @@ return 0; } - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, vie_channel_->Id(), - "Set delay current(a=%d v=%d rel=%d) target(a=%d v=%d)", - current_audio_delay_ms, current_video_delay_ms, - relative_delay_ms, - target_audio_delay_ms, target_video_delay_ms); if (voe_sync_interface_->SetMinimumPlayoutDelay( voe_channel_id_, target_audio_delay_ms) == -1) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, vie_channel_->Id(), - "Error setting voice delay"); + LOG(LS_ERROR) << "Error setting voice delay."; } vcm_->SetMinimumPlayoutDelay(target_video_delay_ms); return 0; @@ -196,9 +176,8 @@ int ViESyncModule::SetTargetBufferingDelay(int target_delay_ms) { CriticalSectionScoped cs(data_cs_.get()); - if (!voe_sync_interface_) { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, vie_channel_->Id(), - "voe_sync_interface_ NULL, can't set playout delay."); + if (!voe_sync_interface_) { + LOG(LS_ERROR) << "voe_sync_interface_ NULL, can't set playout delay."; return -1; } sync_->SetTargetBufferingDelay(target_delay_ms); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_sync_module.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_sync_module.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_sync_module.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine/vie_sync_module.h 2015-02-03 14:33:38.000000000 +0000 @@ -45,8 +45,8 @@ int SetTargetBufferingDelay(int target_delay_ms); // Implements Module. - virtual int32_t TimeUntilNextProcess(); - virtual int32_t Process(); + virtual int32_t TimeUntilNextProcess() OVERRIDE; + virtual int32_t Process() OVERRIDE; private: scoped_ptr data_cs_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine_tests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine_tests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_engine_tests.isolate 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_engine_tests.isolate 2015-02-03 14:33:38.000000000 +0000 @@ -8,30 +8,23 @@ { 'conditions': [ ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. 'variables': { - 'isolate_dependency_untracked': [ - '../../data/', - '../../resources/', + 'files': [ + '<(DEPTH)/data/', + '<(DEPTH)/resources/', ], }, }], ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../testing/test_env.py', '<(PRODUCT_DIR)/video_engine_tests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_tracked': [ - '../DEPS', - '../resources/foreman_cif_short.yuv', - '../testing/test_env.py', + 'files': [ + '<(DEPTH)/DEPS', + '<(DEPTH)/resources/foreman_cif_short.yuv', '<(PRODUCT_DIR)/video_engine_tests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '../tools/swarming_client/', - ], }, }], ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_frame.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_frame.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_frame.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_frame.h 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_VIDEO_FRAME_H_ +#define WEBRTC_VIDEO_FRAME_H_ + +#include + +#include "webrtc/common_video/plane.h" +// TODO(pbos): Remove scoped_refptr include (and AddRef/Release if they're not +// used). +#include "webrtc/system_wrappers/interface/scoped_refptr.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +enum PlaneType { + kYPlane = 0, + kUPlane = 1, + kVPlane = 2, + kNumOfPlanes = 3 +}; + +class I420VideoFrame { + public: + I420VideoFrame(); + virtual ~I420VideoFrame(); + // Infrastructure for refCount implementation. + // Implements dummy functions for reference counting so that non reference + // counted instantiation can be done. These functions should not be called + // when creating the frame with new I420VideoFrame(). + // Note: do not pass a I420VideoFrame created with new I420VideoFrame() or + // equivalent to a scoped_refptr or memory leak will occur. + virtual int32_t AddRef() { + assert(false); + return -1; + } + virtual int32_t Release() { + assert(false); + return -1; + } + + // 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. + // Return value: 0 on success, -1 on error. + virtual int CreateEmptyFrame(int width, + int height, + int stride_y, + int stride_u, + int stride_v); + + // CreateFrame: Sets the frame's members and buffers. If required size is + // bigger than allocated one, new buffers of adequate size will be allocated. + // Return value: 0 on success, -1 on error. + virtual int CreateFrame(int size_y, + const uint8_t* buffer_y, + int size_u, + const uint8_t* buffer_u, + int size_v, + const uint8_t* buffer_v, + int width, + int height, + int stride_y, + int stride_u, + int stride_v); + + // Copy frame: If required size is bigger than allocated one, new buffers of + // adequate size will be allocated. + // Return value: 0 on success, -1 on error. + virtual int CopyFrame(const I420VideoFrame& videoFrame); + + // Make a copy of |this|. The caller owns the returned frame. + // Return value: a new frame on success, NULL on error. + virtual I420VideoFrame* CloneFrame() const; + + // Swap Frame. + virtual void SwapFrame(I420VideoFrame* videoFrame); + + // Get pointer to buffer per plane. + virtual uint8_t* buffer(PlaneType type); + // Overloading with const. + virtual const uint8_t* buffer(PlaneType type) const; + + // Get allocated size per plane. + virtual int allocated_size(PlaneType type) const; + + // Get allocated stride per plane. + virtual int stride(PlaneType type) const; + + // Set frame width. + virtual int set_width(int width); + + // Set frame height. + virtual int set_height(int height); + + // Get frame width. + virtual int width() const { return width_; } + + // Get frame height. + virtual int height() const { return height_; } + + // Set frame timestamp (90kHz). + virtual void set_timestamp(uint32_t timestamp) { timestamp_ = timestamp; } + + // Get frame timestamp (90kHz). + virtual uint32_t timestamp() const { return timestamp_; } + + // Set capture ntp time in miliseconds. + virtual void set_ntp_time_ms(int64_t ntp_time_ms) { + ntp_time_ms_ = ntp_time_ms; + } + + // Get capture ntp time in miliseconds. + virtual int64_t ntp_time_ms() const { return ntp_time_ms_; } + + // Set render time in miliseconds. + virtual void set_render_time_ms(int64_t render_time_ms) { + render_time_ms_ = render_time_ms; + } + + // Get render time in miliseconds. + virtual int64_t render_time_ms() const { return render_time_ms_; } + + // Return true if underlying plane buffers are of zero size, false if not. + virtual bool IsZeroSize() const; + + // Reset underlying plane buffers sizes to 0. This function doesn't + // clear memory. + virtual void ResetSize(); + + // Return the handle of the underlying video frame. This is used when the + // frame is backed by a texture. The object should be destroyed when it is no + // longer in use, so the underlying resource can be freed. + virtual void* native_handle() const; + + protected: + // Verifies legality of parameters. + // Return value: 0 on success, -1 on error. + virtual int CheckDimensions(int width, + int height, + int stride_y, + int stride_u, + int stride_v); + + private: + // Get the pointer to a specific plane. + const Plane* GetPlane(PlaneType type) const; + // Overloading with non-const. + Plane* GetPlane(PlaneType type); + + Plane y_plane_; + Plane u_plane_; + Plane v_plane_; + int width_; + int height_; + uint32_t timestamp_; + int64_t ntp_time_ms_; + int64_t render_time_ms_; +}; + +enum VideoFrameType { + kKeyFrame = 0, + kDeltaFrame = 1, + kGoldenFrame = 2, + kAltRefFrame = 3, + kSkipFrame = 4 +}; + +// TODO(pbos): Rename EncodedFrame and reformat this class' members. +class EncodedImage { + public: + EncodedImage() + : _encodedWidth(0), + _encodedHeight(0), + _timeStamp(0), + capture_time_ms_(0), + _frameType(kDeltaFrame), + _buffer(NULL), + _length(0), + _size(0), + _completeFrame(false) {} + + EncodedImage(uint8_t* buffer, uint32_t length, uint32_t size) + : _encodedWidth(0), + _encodedHeight(0), + _timeStamp(0), + ntp_time_ms_(0), + capture_time_ms_(0), + _frameType(kDeltaFrame), + _buffer(buffer), + _length(length), + _size(size), + _completeFrame(false) {} + + uint32_t _encodedWidth; + uint32_t _encodedHeight; + uint32_t _timeStamp; + // NTP time of the capture time in local timebase in milliseconds. + int64_t ntp_time_ms_; + int64_t capture_time_ms_; + VideoFrameType _frameType; + uint8_t* _buffer; + uint32_t _length; + uint32_t _size; + bool _completeFrame; +}; + +} // namespace webrtc +#endif // WEBRTC_VIDEO_FRAME_H_ + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_receive_stream.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_receive_stream.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_receive_stream.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_receive_stream.h 2015-02-03 14:33:38.000000000 +0000 @@ -31,31 +31,39 @@ class VideoDecoder; -// TODO(mflodman) Move all these settings to VideoDecoder and move the -// declaration to common_types.h. -struct ExternalVideoDecoder { - ExternalVideoDecoder() - : decoder(NULL), payload_type(0), renderer(false), expected_delay_ms(0) {} - // The actual decoder. - VideoDecoder* decoder; - - // Received RTP packets with this payload type will be sent to this decoder - // instance. - int payload_type; - - // 'true' if the decoder handles rendering as well. - bool renderer; - - // The expected delay for decoding and rendering, i.e. the frame will be - // delivered this many milliseconds, if possible, earlier than the ideal - // render time. - // Note: Ignored if 'renderer' is false. - int expected_delay_ms; -}; - class VideoReceiveStream { public: - struct Stats : public StreamStats { + // TODO(mflodman) Move all these settings to VideoDecoder and move the + // declaration to common_types.h. + struct Decoder { + Decoder() + : decoder(NULL), + payload_type(0), + renderer(false), + expected_delay_ms(0) {} + + // The actual decoder instance. + VideoDecoder* decoder; + + // Received RTP packets with this payload type will be sent to this decoder + // instance. + int payload_type; + + // Name of the decoded payload (such as VP8). Maps back to the depacketizer + // used to unpack incoming packets. + std::string payload_name; + + // 'true' if the decoder handles rendering as well. + bool renderer; + + // The expected delay for decoding and rendering, i.e. the frame will be + // delivered this many milliseconds, if possible, earlier than the ideal + // render time. + // Note: Ignored if 'renderer' is false. + int expected_delay_ms; + }; + + struct Stats : public SsrcStats { Stats() : network_frame_rate(0), decode_frame_rate(0), @@ -68,7 +76,7 @@ int decode_frame_rate; int render_frame_rate; int avg_delay_ms; - uint32_t discarded_packets; + int discarded_packets; uint32_t ssrc; std::string c_name; }; @@ -77,12 +85,13 @@ Config() : renderer(NULL), render_delay_ms(0), - audio_channel_id(0), + audio_channel_id(-1), pre_decode_callback(NULL), pre_render_callback(NULL), target_delay_ms(0) {} - // Codecs the receive stream can receive. - std::vector codecs; + + // Decoders for every payload that we can receive. + std::vector decoders; // Receive-stream specific RTP settings. struct Rtp { @@ -90,7 +99,7 @@ : remote_ssrc(0), local_ssrc(0), rtcp_mode(newapi::kRtcpReducedSize), - remb(false) {} + remb(true) {} // Synchronization source (stream identifier) to be received. uint32_t remote_ssrc; @@ -162,21 +171,16 @@ // stream. 'NULL' disables the callback. I420FrameCallback* pre_render_callback; - // External video decoders to be used if incoming payload type matches the - // registered type for an external decoder. - std::vector external_decoders; - // Target delay in milliseconds. A positive value indicates this stream is // used for streaming instead of a real-time call. int target_delay_ms; }; - virtual void StartReceiving() = 0; - virtual void StopReceiving() = 0; - virtual Stats GetStats() const = 0; + virtual void Start() = 0; + virtual void Stop() = 0; - // TODO(mflodman) Replace this with callback. - virtual void GetCurrentReceiveCodec(VideoCodec* receive_codec) = 0; + // TODO(pbos): Add info on currently-received codec to Stats. + virtual Stats GetStats() const = 0; protected: virtual ~VideoReceiveStream() {} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_send_stream.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_send_stream.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/video_send_stream.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/video_send_stream.h 2015-02-03 14:33:38.000000000 +0000 @@ -29,7 +29,6 @@ // These methods do not lock internally and must be called sequentially. // If your application switches input sources synchronization must be done // externally to make sure that any old frames are not delivered concurrently. - virtual void PutFrame(const I420VideoFrame& video_frame) = 0; virtual void SwapFrame(I420VideoFrame* video_frame) = 0; protected: @@ -42,15 +41,13 @@ Stats() : input_frame_rate(0), encode_frame_rate(0), - avg_delay_ms(0), - max_delay_ms(0) {} - + media_bitrate_bps(0), + suspended(false) {} int input_frame_rate; int encode_frame_rate; - int avg_delay_ms; - int max_delay_ms; - std::string c_name; - std::map substreams; + int media_bitrate_bps; + bool suspended; + std::map substreams; }; struct Config { @@ -59,16 +56,27 @@ post_encode_callback(NULL), local_renderer(NULL), render_delay_ms(0), - encoder(NULL), - internal_source(false), target_delay_ms(0), - pacing(false), suspend_below_min_bitrate(false) {} - VideoCodec codec; + std::string ToString() const; + + struct EncoderSettings { + EncoderSettings() : payload_type(-1), encoder(NULL) {} + + std::string ToString() const; + + std::string payload_name; + int payload_type; + + // Uninitialized VideoEncoder instance to be used for encoding. Will be + // initialized from inside the VideoSendStream. + webrtc::VideoEncoder* encoder; + } encoder_settings; static const size_t kDefaultMaxPacketSize = 1500 - 40; // TCP over IPv4. struct Rtp { Rtp() : max_packet_size(kDefaultMaxPacketSize) {} + std::string ToString() const; std::vector ssrcs; @@ -87,12 +95,17 @@ // Settings for RTP retransmission payload format, see RFC 4588 for // details. struct Rtx { - Rtx() : payload_type(0) {} + Rtx() : payload_type(-1), pad_with_redundant_payloads(false) {} + std::string ToString() const; // SSRCs to use for the RTX streams. std::vector ssrcs; // Payload type to use for the RTX stream. int payload_type; + // Use redundant payloads to pad the bitrate. Instead of padding with + // randomized packets, we will preemptively retransmit media packets on + // the RTX stream. + bool pad_with_redundant_payloads; } rtx; // RTCP CNAME, see RFC 3550. @@ -113,29 +126,16 @@ // Expected delay needed by the renderer, i.e. the frame will be delivered // this many milliseconds, if possible, earlier than expected render time. - // Only valid if |renderer| is set. + // Only valid if |local_renderer| is set. int render_delay_ms; - // TODO(mflodman) Move VideoEncoder to common_types.h and redefine. - // External encoding. 'encoder' is the external encoder instance and - // 'internal_source' is set to true if the encoder also captures the video - // frames. - VideoEncoder* encoder; - bool internal_source; - // Target delay in milliseconds. A positive value indicates this stream is // used for streaming instead of a real-time call. int target_delay_ms; - // True if network a send-side packet buffer should be used to pace out - // packets onto the network. - bool pacing; - // True if the stream should be suspended when the available bitrate fall // below the minimum configured bitrate. If this variable is false, the // stream may send at a rate higher than the estimated available bitrate. - // Enabling suspend_below_min_bitrate will also enable pacing and padding, - // otherwise, the video will be unable to recover from suspension. bool suspend_below_min_bitrate; }; @@ -143,11 +143,13 @@ // VideoSendStream is valid. virtual VideoSendStreamInput* Input() = 0; - virtual void StartSending() = 0; - virtual void StopSending() = 0; + virtual void Start() = 0; + virtual void Stop() = 0; - virtual bool SetCodec(const VideoCodec& codec) = 0; - virtual VideoCodec GetCodec() = 0; + // Set which streams to send. Must have at least as many SSRCs as configured + // in the config. Encoder settings are passed on to the encoder instance along + // with the VideoStream settings. + virtual bool ReconfigureVideoEncoder(const VideoEncoderConfig& config) = 0; virtual Stats GetStats() const = 0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/Android.mk 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../android-webrtc.mk - -LOCAL_ARM_MODE := arm -LOCAL_MODULE := libwebrtc_voe_core -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := \ - audio_frame_operations.cc \ - channel.cc \ - channel_manager.cc \ - dtmf_inband.cc \ - dtmf_inband_queue.cc \ - level_indicator.cc \ - monitor_module.cc \ - output_mixer.cc \ - ref_count.cc \ - shared_data.cc \ - statistics.cc \ - transmit_mixer.cc \ - utility.cc \ - voe_audio_processing_impl.cc \ - voe_base_impl.cc \ - voe_call_report_impl.cc \ - voe_codec_impl.cc \ - voe_dtmf_impl.cc \ - voe_encryption_impl.cc \ - voe_external_media_impl.cc \ - voe_file_impl.cc \ - voe_hardware_impl.cc \ - voe_neteq_stats_impl.cc \ - voe_network_impl.cc \ - voe_rtp_rtcp_impl.cc \ - voe_video_sync_impl.cc \ - voe_volume_control_impl.cc \ - voice_engine_impl.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - $(MY_WEBRTC_COMMON_DEFS) \ - '-DWEBRTC_ANDROID_OPENSLES' - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../../../common_audio/resampler/include \ - $(LOCAL_PATH)/../../../common_audio/signal_processing/include \ - $(LOCAL_PATH)/../../../modules/interface \ - $(LOCAL_PATH)/../../../modules/audio_coding/main/interface \ - $(LOCAL_PATH)/../../../modules/audio_conference_mixer/interface \ - $(LOCAL_PATH)/../../../modules/audio_device/main/interface \ - $(LOCAL_PATH)/../../../modules/audio_device/main/source \ - $(LOCAL_PATH)/../../../modules/audio_processing/include \ - $(LOCAL_PATH)/../../../modules/media_file/interface \ - $(LOCAL_PATH)/../../../modules/rtp_rtcp/interface \ - $(LOCAL_PATH)/../../../modules/udp_transport/interface \ - $(LOCAL_PATH)/../../../modules/utility/interface \ - $(LOCAL_PATH)/../../../system_wrappers/interface - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl \ - libstlport - -ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) -LOCAL_LDLIBS += -ldl -lpthread -endif - -ifneq ($(TARGET_SIMULATOR),true) -LOCAL_SHARED_LIBRARIES += libdl -endif - -ifndef NDK_ROOT -include external/stlport/libstlport.mk -endif -include $(BUILD_STATIC_LIBRARY) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/BUILD.gn thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/BUILD.gn --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/BUILD.gn 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/BUILD.gn 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,108 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../build/webrtc.gni") + +source_set("voice_engine") { + + sources = [ + "../common_types.h", + "../engine_configurations.h", + "../typedefs.h", + "include/voe_audio_processing.h", + "include/voe_base.h", + "include/voe_codec.h", + "include/voe_dtmf.h", + "include/voe_errors.h", + "include/voe_external_media.h", + "include/voe_file.h", + "include/voe_hardware.h", + "include/voe_neteq_stats.h", + "include/voe_network.h", + "include/voe_rtp_rtcp.h", + "include/voe_video_sync.h", + "include/voe_volume_control.h", + "channel.cc", + "channel.h", + "channel_manager.cc", + "channel_manager.h", + "dtmf_inband.cc", + "dtmf_inband.h", + "dtmf_inband_queue.cc", + "dtmf_inband_queue.h", + "level_indicator.cc", + "level_indicator.h", + "monitor_module.cc", + "monitor_module.h", + "network_predictor.cc", + "network_predictor.h", + "output_mixer.cc", + "output_mixer.h", + "shared_data.cc", + "shared_data.h", + "statistics.cc", + "statistics.h", + "transmit_mixer.cc", + "transmit_mixer.h", + "utility.cc", + "utility.h", + "voe_audio_processing_impl.cc", + "voe_audio_processing_impl.h", + "voe_base_impl.cc", + "voe_base_impl.h", + "voe_codec_impl.cc", + "voe_codec_impl.h", + "voe_dtmf_impl.cc", + "voe_dtmf_impl.h", + "voe_external_media_impl.cc", + "voe_external_media_impl.h", + "voe_file_impl.cc", + "voe_file_impl.h", + "voe_hardware_impl.cc", + "voe_hardware_impl.h", + "voe_neteq_stats_impl.cc", + "voe_neteq_stats_impl.h", + "voe_network_impl.cc", + "voe_network_impl.h", + "voe_rtp_rtcp_impl.cc", + "voe_rtp_rtcp_impl.h", + "voe_video_sync_impl.cc", + "voe_video_sync_impl.h", + "voe_volume_control_impl.cc", + "voe_volume_control_impl.h", + "voice_engine_defines.h", + "voice_engine_impl.cc", + "voice_engine_impl.h", + ] + + if (is_win) { + defines = [ "WEBRTC_DRIFT_COMPENSATION_SUPPORTED" ] + } + + configs += [ "..:common_config" ] + public_configs = [ "..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ + "../common_audio", + "../modules/audio_coding", + "../modules/audio_conference_mixer", + "../modules/audio_device", + "../modules/audio_processing", + "../modules/bitrate_controller", + "../modules/media_file", + "../modules/rtp_rtcp", + "../modules/utility", + "../system_wrappers", + ] +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/channel.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/channel.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/channel.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/channel.cc 2015-02-03 14:33:38.000000000 +0000 @@ -10,9 +10,11 @@ #include "webrtc/voice_engine/channel.h" +#include "webrtc/base/timeutils.h" #include "webrtc/common.h" #include "webrtc/modules/audio_device/include/audio_device.h" #include "webrtc/modules/audio_processing/include/audio_processing.h" +#include "webrtc/modules/interface/module_common_types.h" #include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_receiver.h" @@ -23,6 +25,7 @@ #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/video_engine/include/vie_network.h" #include "webrtc/voice_engine/include/voe_base.h" #include "webrtc/voice_engine/include/voe_external_media.h" #include "webrtc/voice_engine/include/voe_rtp_rtcp.h" @@ -86,6 +89,24 @@ ChannelStatistics stats_; }; +class VoEBitrateObserver : public BitrateObserver { + public: + explicit VoEBitrateObserver(Channel* owner) + : owner_(owner) {} + virtual ~VoEBitrateObserver() {} + + // Implements BitrateObserver. + virtual void OnNetworkChanged(const uint32_t bitrate_bps, + const uint8_t fraction_lost, + const uint32_t rtt) OVERRIDE { + // |fraction_lost| has a scale of 0 - 255. + owner_->OnNetworkChanged(bitrate_bps, fraction_lost, rtt); + } + + private: + Channel* owner_; +}; + int32_t Channel::SendData(FrameType frameType, uint8_t payloadType, @@ -104,7 +125,7 @@ // Store current audio level in the RTP/RTCP module. // The level will be used in combination with voice-activity state // (frameType) to add an RTP header extension - _rtpRtcpModule->SetAudioLevel(rtp_audioproc_->level_estimator()->RMS()); + _rtpRtcpModule->SetAudioLevel(rms_level_.RMS()); } // Push data from ACM to RTP/RTCP-module to deliver audio frame for @@ -179,21 +200,6 @@ return -1; } - // Insert extra RTP packet using if user has called the InsertExtraRTPPacket - // API - if (_insertExtraRTPPacket) - { - uint8_t* rtpHdr = (uint8_t*)data; - uint8_t M_PT(0); - if (_extraMarkerBit) - { - M_PT = 0x80; // set the M-bit - } - M_PT += _extraPayloadType; // set the payload type - *(++rtpHdr) = M_PT; // modify the M|PT-byte within the RTP header - _insertExtraRTPPacket = false; // insert one packet only - } - uint8_t* bufferToSendPtr = (uint8_t*)data; int32_t bufferLength = len; @@ -205,41 +211,6 @@ "Channel::SendPacket() RTP dump to output file failed"); } - // SRTP or External encryption - if (_encrypting) - { - if (_encryptionPtr) - { - if (!_encryptionRTPBufferPtr) - { - // Allocate memory for encryption buffer one time only - _encryptionRTPBufferPtr = - new uint8_t[kVoiceEngineMaxIpPacketSizeBytes]; - memset(_encryptionRTPBufferPtr, 0, - kVoiceEngineMaxIpPacketSizeBytes); - } - - // Perform encryption (SRTP or external) - int32_t encryptedBufferLength = 0; - _encryptionPtr->encrypt(_channelId, - bufferToSendPtr, - _encryptionRTPBufferPtr, - bufferLength, - (int*)&encryptedBufferLength); - if (encryptedBufferLength <= 0) - { - _engineStatisticsPtr->SetLastError( - VE_ENCRYPTION_FAILED, - kTraceError, "Channel::SendPacket() encryption failed"); - return -1; - } - - // Replace default data buffer with encrypted buffer - bufferToSendPtr = _encryptionRTPBufferPtr; - bufferLength = encryptedBufferLength; - } - } - int n = _transportPtr->SendPacket(channel, bufferToSendPtr, bufferLength); if (n < 0) { @@ -284,39 +255,6 @@ "Channel::SendPacket() RTCP dump to output file failed"); } - // SRTP or External encryption - if (_encrypting) - { - if (_encryptionPtr) - { - if (!_encryptionRTCPBufferPtr) - { - // Allocate memory for encryption buffer one time only - _encryptionRTCPBufferPtr = - new uint8_t[kVoiceEngineMaxIpPacketSizeBytes]; - } - - // Perform encryption (SRTP or external). - int32_t encryptedBufferLength = 0; - _encryptionPtr->encrypt_rtcp(_channelId, - bufferToSendPtr, - _encryptionRTCPBufferPtr, - bufferLength, - (int*)&encryptedBufferLength); - if (encryptedBufferLength <= 0) - { - _engineStatisticsPtr->SetLastError( - VE_ENCRYPTION_FAILED, kTraceError, - "Channel::SendRTCPPacket() encryption failed"); - return -1; - } - - // Replace default data buffer with encrypted buffer - bufferToSendPtr = _encryptionRTCPBufferPtr; - bufferLength = encryptedBufferLength; - } - } - int n = _transportPtr->SendRTCPPacket(channel, bufferToSendPtr, bufferLength); @@ -363,22 +301,8 @@ "Channel::OnIncomingSSRCChanged(id=%d, SSRC=%d)", id, ssrc); - int32_t channel = VoEChannelId(id); - assert(channel == _channelId); - // Update ssrc so that NTP for AV sync can be updated. _rtpRtcpModule->SetRemoteSSRC(ssrc); - - if (_rtpObserver) - { - CriticalSectionScoped cs(&_callbackCritSect); - - if (_rtpObserverPtr) - { - // Send new SSRC to registered observer using callback - _rtpObserverPtr->OnIncomingSSRCChanged(channel, ssrc); - } - } } void Channel::OnIncomingCSRCChanged(int32_t id, @@ -388,19 +312,6 @@ WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::OnIncomingCSRCChanged(id=%d, CSRC=%d, added=%d)", id, CSRC, added); - - int32_t channel = VoEChannelId(id); - assert(channel == _channelId); - - if (_rtpObserver) - { - CriticalSectionScoped cs(&_callbackCritSect); - - if (_rtpObserverPtr) - { - _rtpObserverPtr->OnIncomingCSRCChanged(channel, CSRC, added); - } - } } void Channel::ResetStatistics(uint32_t ssrc) { @@ -484,118 +395,6 @@ return 0; } -void -Channel::OnPacketTimeout(int32_t id) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::OnPacketTimeout(id=%d)", id); - - CriticalSectionScoped cs(_callbackCritSectPtr); - if (_voiceEngineObserverPtr) - { - if (_receiving || _externalTransport) - { - int32_t channel = VoEChannelId(id); - assert(channel == _channelId); - // Ensure that next OnReceivedPacket() callback will trigger - // a VE_PACKET_RECEIPT_RESTARTED callback. - _rtpPacketTimedOut = true; - // Deliver callback to the observer - WEBRTC_TRACE(kTraceInfo, kTraceVoice, - VoEId(_instanceId,_channelId), - "Channel::OnPacketTimeout() => " - "CallbackOnError(VE_RECEIVE_PACKET_TIMEOUT)"); - _voiceEngineObserverPtr->CallbackOnError(channel, - VE_RECEIVE_PACKET_TIMEOUT); - } - } -} - -void -Channel::OnReceivedPacket(int32_t id, - RtpRtcpPacketType packetType) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::OnReceivedPacket(id=%d, packetType=%d)", - id, packetType); - - assert(VoEChannelId(id) == _channelId); - - // Notify only for the case when we have restarted an RTP session. - if (_rtpPacketTimedOut && (kPacketRtp == packetType)) - { - CriticalSectionScoped cs(_callbackCritSectPtr); - if (_voiceEngineObserverPtr) - { - int32_t channel = VoEChannelId(id); - assert(channel == _channelId); - // Reset timeout mechanism - _rtpPacketTimedOut = false; - // Deliver callback to the observer - WEBRTC_TRACE(kTraceInfo, kTraceVoice, - VoEId(_instanceId,_channelId), - "Channel::OnPacketTimeout() =>" - " CallbackOnError(VE_PACKET_RECEIPT_RESTARTED)"); - _voiceEngineObserverPtr->CallbackOnError( - channel, - VE_PACKET_RECEIPT_RESTARTED); - } - } -} - -void -Channel::OnPeriodicDeadOrAlive(int32_t id, - RTPAliveType alive) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::OnPeriodicDeadOrAlive(id=%d, alive=%d)", id, alive); - - { - CriticalSectionScoped cs(&_callbackCritSect); - if (!_connectionObserver) - return; - } - - int32_t channel = VoEChannelId(id); - assert(channel == _channelId); - - // Use Alive as default to limit risk of false Dead detections - bool isAlive(true); - - // Always mark the connection as Dead when the module reports kRtpDead - if (kRtpDead == alive) - { - isAlive = false; - } - - // It is possible that the connection is alive even if no RTP packet has - // been received for a long time since the other side might use VAD/DTX - // and a low SID-packet update rate. - if ((kRtpNoRtp == alive) && _playing) - { - // Detect Alive for all NetEQ states except for the case when we are - // in PLC_CNG state. - // PLC_CNG <=> background noise only due to long expand or error. - // Note that, the case where the other side stops sending during CNG - // state will be detected as Alive. Dead is is not set until after - // missing RTCP packets for at least twelve seconds (handled - // internally by the RTP/RTCP module). - isAlive = (_outputSpeechType != AudioFrame::kPLCCNG); - } - - UpdateDeadOrAliveCounters(isAlive); - - // Send callback to the registered observer - if (_connectionObserver) - { - CriticalSectionScoped cs(&_callbackCritSect); - if (_connectionObserverPtr) - { - _connectionObserverPtr->OnPeriodicDeadOrAlive(channel, isAlive); - } - } -} - int32_t Channel::OnReceivedPayloadData(const uint8_t* payloadData, uint16_t payloadSize, @@ -608,9 +407,7 @@ rtpHeader->header.payloadType, rtpHeader->type.Audio.channel); - _lastRemoteTimeStamp = rtpHeader->header.timestamp; - - if (!_playing) + if (!channel_state_.Get().playing) { // Avoid inserting into NetEQ when we are not playing. Count the // packet as discarded. @@ -695,7 +492,9 @@ // Store speech type for dead-or-alive detection _outputSpeechType = audioFrame.speech_type_; - if (_rxApmIsEnabled) { + ChannelState::State state = channel_state_.Get(); + + if (state.rx_apm_is_enabled) { int err = rx_audioproc_->ProcessStream(&audioFrame); if (err) { LOG(LS_ERROR) << "ProcessStream() error: " << err; @@ -739,17 +538,11 @@ } // Mix decoded PCM output with file if file mixing is enabled - if (_outputFilePlaying) + if (state.output_file_playing) { MixAudioWithFile(audioFrame, audioFrame.sample_rate_hz_); } - // Place channel in on-hold state (~muted) if on-hold is activated - if (_outputIsOnHold) - { - AudioFrameOperations::Mute(audioFrame); - } - // External media if (_outputExternalMedia) { @@ -780,6 +573,36 @@ // Measure audio level (0-9) _outputAudioLevel.ComputeLevel(audioFrame); + if (capture_start_rtp_time_stamp_ < 0 && audioFrame.timestamp_ != 0) { + // The first frame with a valid rtp timestamp. + capture_start_rtp_time_stamp_ = audioFrame.timestamp_; + } + + if (capture_start_rtp_time_stamp_ >= 0) { + // audioFrame.timestamp_ should be valid from now on. + + // Compute elapsed time. + int64_t unwrap_timestamp = + rtp_ts_wraparound_handler_->Unwrap(audioFrame.timestamp_); + audioFrame.elapsed_time_ms_ = + (unwrap_timestamp - capture_start_rtp_time_stamp_) / + (GetPlayoutFrequency() / 1000); + + { + CriticalSectionScoped lock(ts_stats_lock_.get()); + // Compute ntp time. + audioFrame.ntp_time_ms_ = ntp_estimator_.Estimate( + audioFrame.timestamp_); + // |ntp_time_ms_| won't be valid until at least 2 RTCP SRs are received. + if (audioFrame.ntp_time_ms_ > 0) { + // Compute |capture_start_ntp_time_ms_| so that + // |capture_start_ntp_time_ms_| + |elapsed_time_ms_| == |ntp_time_ms_| + capture_start_ntp_time_ms_ = + audioFrame.ntp_time_ms_ - audioFrame.elapsed_time_ms_; + } + } + } + return 0; } @@ -808,10 +631,10 @@ // we take that frequency into consideration as well // This is not needed on sending side, since the codec will // limit the spectrum anyway. - if (_outputFilePlaying) + if (channel_state_.Get().output_file_playing) { CriticalSectionScoped cs(&_fileCritSect); - if (_outputFilePlayerPtr && _outputFilePlaying) + if (_outputFilePlayerPtr) { if(_outputFilePlayerPtr->Frequency()>highestNeeded) { @@ -873,9 +696,7 @@ if (id == _inputFilePlayerId) { - CriticalSectionScoped cs(&_fileCritSect); - - _inputFilePlaying = false; + channel_state_.SetInputFilePlaying(false); WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::PlayFileEnded() => input file player module is" @@ -883,9 +704,7 @@ } else if (id == _outputFilePlayerId) { - CriticalSectionScoped cs(&_fileCritSect); - - _outputFilePlaying = false; + channel_state_.SetOutputFilePlaying(false); WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::PlayFileEnded() => output file player module is" @@ -920,21 +739,19 @@ _channelId(channelId), rtp_header_parser_(RtpHeaderParser::Create()), rtp_payload_registry_( - new RTPPayloadRegistry(channelId, - RTPPayloadStrategy::CreateStrategy(true))), + new RTPPayloadRegistry(RTPPayloadStrategy::CreateStrategy(true))), rtp_receive_statistics_(ReceiveStatistics::Create( Clock::GetRealTimeClock())), rtp_receiver_(RtpReceiver::CreateAudioReceiver( VoEModuleId(instanceId, channelId), Clock::GetRealTimeClock(), this, this, this, rtp_payload_registry_.get())), telephone_event_handler_(rtp_receiver_->GetTelephoneEventHandler()), - audio_coding_(config.Get().Create( + audio_coding_(AudioCodingModule::Create( VoEModuleId(instanceId, channelId))), _rtpDumpIn(*RtpDump::CreateRtpDump()), _rtpDumpOut(*RtpDump::CreateRtpDump()), _outputAudioLevel(), _externalTransport(false), - _audioLevel_dBov(0), _inputFilePlayerPtr(NULL), _outputFilePlayerPtr(NULL), _outputFileRecorderPtr(NULL), @@ -943,27 +760,25 @@ _inputFilePlayerId(VoEModuleId(instanceId, channelId) + 1024), _outputFilePlayerId(VoEModuleId(instanceId, channelId) + 1025), _outputFileRecorderId(VoEModuleId(instanceId, channelId) + 1026), - _inputFilePlaying(false), - _outputFilePlaying(false), _outputFileRecording(false), _inbandDtmfQueue(VoEModuleId(instanceId, channelId)), _inbandDtmfGenerator(VoEModuleId(instanceId, channelId)), - _inputExternalMedia(false), _outputExternalMedia(false), _inputExternalMediaCallbackPtr(NULL), _outputExternalMediaCallbackPtr(NULL), - _encryptionRTPBufferPtr(NULL), - _decryptionRTPBufferPtr(NULL), - _encryptionRTCPBufferPtr(NULL), - _decryptionRTCPBufferPtr(NULL), _timeStamp(0), // This is just an offset, RTP module will add it's own random offset _sendTelephoneEventPayloadType(106), + ntp_estimator_(Clock::GetRealTimeClock()), jitter_buffer_playout_timestamp_(0), playout_timestamp_rtp_(0), playout_timestamp_rtcp_(0), playout_delay_ms_(0), _numberOfDiscardedPackets(0), send_sequence_number_(0), + ts_stats_lock_(CriticalSectionWrapper::CreateCriticalSection()), + rtp_ts_wraparound_handler_(new rtc::TimestampWrapAroundHandler()), + capture_start_rtp_time_stamp_(-1), + capture_start_ntp_time_ms_(-1), _engineStatisticsPtr(NULL), _outputMixerPtr(NULL), _transmitMixerPtr(NULL), @@ -972,56 +787,41 @@ _voiceEngineObserverPtr(NULL), _callbackCritSectPtr(NULL), _transportPtr(NULL), - _encryptionPtr(NULL), - rx_audioproc_(AudioProcessing::Create(VoEModuleId(instanceId, channelId))), _rxVadObserverPtr(NULL), _oldVadDecision(-1), _sendFrameType(0), - _rtpObserverPtr(NULL), _rtcpObserverPtr(NULL), - _outputIsOnHold(false), - _externalPlayout(false), _externalMixing(false), - _inputIsOnHold(false), - _playing(false), - _sending(false), - _receiving(false), _mixFileWithMicrophone(false), - _rtpObserver(false), _rtcpObserver(false), _mute(false), _panLeft(1.0f), _panRight(1.0f), _outputGain(1.0f), - _encrypting(false), - _decrypting(false), _playOutbandDtmfEvent(false), _playInbandDtmfEvent(false), - _extraPayloadType(0), - _insertExtraRTPPacket(false), - _extraMarkerBit(false), _lastLocalTimeStamp(0), - _lastRemoteTimeStamp(0), _lastPayloadType(0), _includeAudioLevelIndication(false), - _rtpPacketTimedOut(false), - _rtpPacketTimeOutIsEnabled(false), - _rtpTimeOutSeconds(0), - _connectionObserver(false), - _connectionObserverPtr(NULL), - _countAliveDetections(0), - _countDeadDetections(0), _outputSpeechType(AudioFrame::kNormalSpeech), + vie_network_(NULL), + video_channel_(-1), _average_jitter_buffer_delay_us(0), least_required_delay_ms_(0), _previousTimestamp(0), _recPacketDelayMs(20), _current_sync_offset(0), _RxVadDetection(false), - _rxApmIsEnabled(false), _rxAgcIsEnabled(false), _rxNsIsEnabled(false), - restored_packet_in_use_(false) + restored_packet_in_use_(false), + bitrate_controller_( + BitrateController::CreateBitrateController(Clock::GetRealTimeClock(), + true)), + rtcp_bandwidth_observer_( + bitrate_controller_->CreateRtcpBandwidthObserver()), + send_bitrate_observer_(new VoEBitrateObserver(this)), + network_predictor_(new NetworkPredictor(Clock::GetRealTimeClock())) { WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::Channel() - ctor"); @@ -1036,12 +836,17 @@ configuration.rtcp_feedback = this; configuration.audio_messages = this; configuration.receive_statistics = rtp_receive_statistics_.get(); + configuration.bandwidth_callback = rtcp_bandwidth_observer_.get(); _rtpRtcpModule.reset(RtpRtcp::CreateRtpRtcp(configuration)); statistics_proxy_.reset(new StatisticsProxy(_rtpRtcpModule->SSRC())); rtp_receive_statistics_->RegisterRtcpStatisticsCallback( statistics_proxy_.get()); + + Config audioproc_config; + audioproc_config.Set(new ExperimentalAgc(false)); + rx_audioproc_.reset(AudioProcessing::Create(audioproc_config)); } Channel::~Channel() @@ -1054,7 +859,7 @@ { DeRegisterExternalMediaProcessing(kPlaybackPerChannel); } - if (_inputExternalMedia) + if (channel_state_.Get().input_external_media) { DeRegisterExternalMediaProcessing(kRecordingPerChannel); } @@ -1114,12 +919,12 @@ // End of modules shutdown // Delete other objects + if (vie_network_) { + vie_network_->Release(); + vie_network_ = NULL; + } RtpDump::DestroyRtpDump(&_rtpDumpIn); RtpDump::DestroyRtpDump(&_rtpDumpOut); - delete [] _encryptionRTPBufferPtr; - delete [] _decryptionRTPBufferPtr; - delete [] _encryptionRTCPBufferPtr; - delete [] _decryptionRTCPBufferPtr; delete &_callbackCritSect; delete &_fileCritSect; delete &volume_settings_critsect_; @@ -1131,6 +936,8 @@ WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::Init()"); + channel_state_.Reset(); + // --- Initial sanity if ((_engineStatisticsPtr == NULL) || @@ -1329,7 +1136,7 @@ { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::StartPlayout()"); - if (_playing) + if (channel_state_.Get().playing) { return 0; } @@ -1345,8 +1152,7 @@ } } - _playing = true; - + channel_state_.SetPlaying(true); if (RegisterFilePlayingToMixer() != 0) return -1; @@ -1358,7 +1164,7 @@ { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::StopPlayout()"); - if (!_playing) + if (!channel_state_.Get().playing) { return 0; } @@ -1374,7 +1180,7 @@ } } - _playing = false; + channel_state_.SetPlaying(false); _outputAudioLevel.Clear(); return 0; @@ -1386,21 +1192,15 @@ WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::StartSend()"); // Resume the previous sequence number which was reset by StopSend(). - // This needs to be done before |_sending| is set to true. + // This needs to be done before |sending| is set to true. if (send_sequence_number_) SetInitSequenceNumber(send_sequence_number_); + if (channel_state_.Get().sending) { - // A lock is needed because |_sending| can be accessed or modified by - // another thread at the same time. - CriticalSectionScoped cs(&_callbackCritSect); - - if (_sending) - { - return 0; - } - _sending = true; + return 0; } + channel_state_.SetSending(true); if (_rtpRtcpModule->SetSendingStatus(true) != 0) { @@ -1408,7 +1208,7 @@ VE_RTP_RTCP_MODULE_ERROR, kTraceError, "StartSend() RTP/RTCP failed to start sending"); CriticalSectionScoped cs(&_callbackCritSect); - _sending = false; + channel_state_.SetSending(false); return -1; } @@ -1420,17 +1220,11 @@ { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::StopSend()"); + if (!channel_state_.Get().sending) { - // A lock is needed because |_sending| can be accessed or modified by - // another thread at the same time. - CriticalSectionScoped cs(&_callbackCritSect); - - if (!_sending) - { - return 0; - } - _sending = false; + return 0; } + channel_state_.SetSending(false); // Store the sequence number to be able to pick up the same sequence for // the next StartSend(). This is needed for restarting device, otherwise @@ -1458,11 +1252,11 @@ { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::StartReceiving()"); - if (_receiving) + if (channel_state_.Get().receiving) { return 0; } - _receiving = true; + channel_state_.SetReceiving(true); _numberOfDiscardedPackets = 0; return 0; } @@ -1472,115 +1266,12 @@ { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::StopReceiving()"); - if (!_receiving) + if (!channel_state_.Get().receiving) { return 0; } - // Recover DTMF detection status. - telephone_event_handler_->SetTelephoneEventForwardToDecoder(true); - RegisterReceiveCodecsToRTPModule(); - _receiving = false; - return 0; -} - -int32_t -Channel::SetNetEQPlayoutMode(NetEqModes mode) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::SetNetEQPlayoutMode()"); - AudioPlayoutMode playoutMode(voice); - switch (mode) - { - case kNetEqDefault: - playoutMode = voice; - break; - case kNetEqStreaming: - playoutMode = streaming; - break; - case kNetEqFax: - playoutMode = fax; - break; - case kNetEqOff: - playoutMode = off; - break; - } - if (audio_coding_->SetPlayoutMode(playoutMode) != 0) - { - _engineStatisticsPtr->SetLastError( - VE_AUDIO_CODING_MODULE_ERROR, kTraceError, - "SetNetEQPlayoutMode() failed to set playout mode"); - return -1; - } - return 0; -} - -int32_t -Channel::GetNetEQPlayoutMode(NetEqModes& mode) -{ - const AudioPlayoutMode playoutMode = audio_coding_->PlayoutMode(); - switch (playoutMode) - { - case voice: - mode = kNetEqDefault; - break; - case streaming: - mode = kNetEqStreaming; - break; - case fax: - mode = kNetEqFax; - break; - case off: - mode = kNetEqOff; - } - WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, - VoEId(_instanceId,_channelId), - "Channel::GetNetEQPlayoutMode() => mode=%u", mode); - return 0; -} - -int32_t -Channel::SetOnHoldStatus(bool enable, OnHoldModes mode) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::SetOnHoldStatus()"); - if (mode == kHoldSendAndPlay) - { - _outputIsOnHold = enable; - _inputIsOnHold = enable; - } - else if (mode == kHoldPlayOnly) - { - _outputIsOnHold = enable; - } - if (mode == kHoldSendOnly) - { - _inputIsOnHold = enable; - } - return 0; -} - -int32_t -Channel::GetOnHoldStatus(bool& enabled, OnHoldModes& mode) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::GetOnHoldStatus()"); - enabled = (_outputIsOnHold || _inputIsOnHold); - if (_outputIsOnHold && _inputIsOnHold) - { - mode = kHoldSendAndPlay; - } - else if (_outputIsOnHold && !_inputIsOnHold) - { - mode = kHoldPlayOnly; - } - else if (!_outputIsOnHold && _inputIsOnHold) - { - mode = kHoldSendOnly; - } - WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::GetOnHoldStatus() => enabled=%d, mode=%d", - enabled, mode); + channel_state_.SetReceiving(false); return 0; } @@ -1665,9 +1356,32 @@ return -1; } + bitrate_controller_->SetBitrateObserver(send_bitrate_observer_.get(), + codec.rate, 0, 0); + return 0; } +void +Channel::OnNetworkChanged(const uint32_t bitrate_bps, + const uint8_t fraction_lost, // 0 - 255. + const uint32_t rtt) { + WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), + "Channel::OnNetworkChanged(bitrate_bps=%d, fration_lost=%d, rtt=%d)", + bitrate_bps, fraction_lost, rtt); + // |fraction_lost| from BitrateObserver is short time observation of packet + // loss rate from past. We use network predictor to make a more reasonable + // loss rate estimation. + network_predictor_->UpdatePacketLossRate(fraction_lost); + uint8_t loss_rate = network_predictor_->GetLossRate(); + // Normalizes rate to 0 - 100. + if (audio_coding_->SetPacketLossRate(100 * loss_rate / 255) != 0) { + _engineStatisticsPtr->SetLastError(VE_AUDIO_CODING_MODULE_ERROR, + kTraceError, "OnNetworkChanged() failed to set packet loss rate"); + assert(false); // This should not happen. + } +} + int32_t Channel::SetVADStatus(bool enableVAD, ACMVADMode mode, bool disableDTX) { @@ -1707,14 +1421,14 @@ WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::SetRecPayloadType()"); - if (_playing) + if (channel_state_.Get().playing) { _engineStatisticsPtr->SetLastError( VE_ALREADY_PLAYING, kTraceError, "SetRecPayloadType() unable to set PT while playing"); return -1; } - if (_receiving) + if (channel_state_.Get().receiving) { _engineStatisticsPtr->SetLastError( VE_ALREADY_LISTENING, kTraceError, @@ -1818,47 +1532,6 @@ } int32_t -Channel::SetAMREncFormat(AmrMode mode) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::SetAMREncFormat()"); - - // ACM doesn't support AMR - return -1; -} - -int32_t -Channel::SetAMRDecFormat(AmrMode mode) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::SetAMRDecFormat()"); - - // ACM doesn't support AMR - return -1; -} - -int32_t -Channel::SetAMRWbEncFormat(AmrMode mode) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::SetAMRWbEncFormat()"); - - // ACM doesn't support AMR - return -1; - -} - -int32_t -Channel::SetAMRWbDecFormat(AmrMode mode) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::SetAMRWbDecFormat()"); - - // ACM doesn't support AMR - return -1; -} - -int32_t Channel::SetSendCNPayloadType(int type, PayloadFrequencies frequency) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), @@ -1907,207 +1580,27 @@ return 0; } -int32_t -Channel::SetISACInitTargetRate(int rateBps, bool useFixedFrameSize) +int Channel::SetOpusMaxPlaybackRate(int frequency_hz) { + WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), + "Channel::SetOpusMaxPlaybackRate()"); + + if (audio_coding_->SetOpusMaxPlaybackRate(frequency_hz) != 0) { + _engineStatisticsPtr->SetLastError( + VE_AUDIO_CODING_MODULE_ERROR, kTraceError, + "SetOpusMaxPlaybackRate() failed to set maximum playback rate"); + return -1; + } + return 0; +} + +int32_t Channel::RegisterExternalTransport(Transport& transport) { - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::SetISACInitTargetRate()"); + WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), + "Channel::RegisterExternalTransport()"); - CodecInst sendCodec; - if (audio_coding_->SendCodec(&sendCodec) == -1) - { - _engineStatisticsPtr->SetLastError( - VE_CODEC_ERROR, kTraceError, - "SetISACInitTargetRate() failed to retrieve send codec"); - return -1; - } - if (STR_CASE_CMP(sendCodec.plname, "ISAC") != 0) - { - // This API is only valid if iSAC is setup to run in channel-adaptive - // mode. - // We do not validate the adaptive mode here. It is done later in the - // ConfigISACBandwidthEstimator() API. - _engineStatisticsPtr->SetLastError( - VE_CODEC_ERROR, kTraceError, - "SetISACInitTargetRate() send codec is not iSAC"); - return -1; - } + CriticalSectionScoped cs(&_callbackCritSect); - uint8_t initFrameSizeMsec(0); - if (16000 == sendCodec.plfreq) - { - // Note that 0 is a valid and corresponds to "use default - if ((rateBps != 0 && - rateBps < kVoiceEngineMinIsacInitTargetRateBpsWb) || - (rateBps > kVoiceEngineMaxIsacInitTargetRateBpsWb)) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_ARGUMENT, kTraceError, - "SetISACInitTargetRate() invalid target rate - 1"); - return -1; - } - // 30 or 60ms - initFrameSizeMsec = (uint8_t)(sendCodec.pacsize / 16); - } - else if (32000 == sendCodec.plfreq) - { - if ((rateBps != 0 && - rateBps < kVoiceEngineMinIsacInitTargetRateBpsSwb) || - (rateBps > kVoiceEngineMaxIsacInitTargetRateBpsSwb)) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_ARGUMENT, kTraceError, - "SetISACInitTargetRate() invalid target rate - 2"); - return -1; - } - initFrameSizeMsec = (uint8_t)(sendCodec.pacsize / 32); // 30ms - } - - if (audio_coding_->ConfigISACBandwidthEstimator( - initFrameSizeMsec, rateBps, useFixedFrameSize) == -1) - { - _engineStatisticsPtr->SetLastError( - VE_AUDIO_CODING_MODULE_ERROR, kTraceError, - "SetISACInitTargetRate() iSAC BWE config failed"); - return -1; - } - - return 0; -} - -int32_t -Channel::SetISACMaxRate(int rateBps) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::SetISACMaxRate()"); - - CodecInst sendCodec; - if (audio_coding_->SendCodec(&sendCodec) == -1) - { - _engineStatisticsPtr->SetLastError( - VE_CODEC_ERROR, kTraceError, - "SetISACMaxRate() failed to retrieve send codec"); - return -1; - } - if (STR_CASE_CMP(sendCodec.plname, "ISAC") != 0) - { - // This API is only valid if iSAC is selected as sending codec. - _engineStatisticsPtr->SetLastError( - VE_CODEC_ERROR, kTraceError, - "SetISACMaxRate() send codec is not iSAC"); - return -1; - } - if (16000 == sendCodec.plfreq) - { - if ((rateBps < kVoiceEngineMinIsacMaxRateBpsWb) || - (rateBps > kVoiceEngineMaxIsacMaxRateBpsWb)) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_ARGUMENT, kTraceError, - "SetISACMaxRate() invalid max rate - 1"); - return -1; - } - } - else if (32000 == sendCodec.plfreq) - { - if ((rateBps < kVoiceEngineMinIsacMaxRateBpsSwb) || - (rateBps > kVoiceEngineMaxIsacMaxRateBpsSwb)) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_ARGUMENT, kTraceError, - "SetISACMaxRate() invalid max rate - 2"); - return -1; - } - } - if (_sending) - { - _engineStatisticsPtr->SetLastError( - VE_SENDING, kTraceError, - "SetISACMaxRate() unable to set max rate while sending"); - return -1; - } - - // Set the maximum instantaneous rate of iSAC (works for both adaptive - // and non-adaptive mode) - if (audio_coding_->SetISACMaxRate(rateBps) == -1) - { - _engineStatisticsPtr->SetLastError( - VE_AUDIO_CODING_MODULE_ERROR, kTraceError, - "SetISACMaxRate() failed to set max rate"); - return -1; - } - - return 0; -} - -int32_t -Channel::SetISACMaxPayloadSize(int sizeBytes) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::SetISACMaxPayloadSize()"); - CodecInst sendCodec; - if (audio_coding_->SendCodec(&sendCodec) == -1) - { - _engineStatisticsPtr->SetLastError( - VE_CODEC_ERROR, kTraceError, - "SetISACMaxPayloadSize() failed to retrieve send codec"); - return -1; - } - if (STR_CASE_CMP(sendCodec.plname, "ISAC") != 0) - { - _engineStatisticsPtr->SetLastError( - VE_CODEC_ERROR, kTraceError, - "SetISACMaxPayloadSize() send codec is not iSAC"); - return -1; - } - if (16000 == sendCodec.plfreq) - { - if ((sizeBytes < kVoiceEngineMinIsacMaxPayloadSizeBytesWb) || - (sizeBytes > kVoiceEngineMaxIsacMaxPayloadSizeBytesWb)) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_ARGUMENT, kTraceError, - "SetISACMaxPayloadSize() invalid max payload - 1"); - return -1; - } - } - else if (32000 == sendCodec.plfreq) - { - if ((sizeBytes < kVoiceEngineMinIsacMaxPayloadSizeBytesSwb) || - (sizeBytes > kVoiceEngineMaxIsacMaxPayloadSizeBytesSwb)) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_ARGUMENT, kTraceError, - "SetISACMaxPayloadSize() invalid max payload - 2"); - return -1; - } - } - if (_sending) - { - _engineStatisticsPtr->SetLastError( - VE_SENDING, kTraceError, - "SetISACMaxPayloadSize() unable to set max rate while sending"); - return -1; - } - - if (audio_coding_->SetISACMaxPayloadSize(sizeBytes) == -1) - { - _engineStatisticsPtr->SetLastError( - VE_AUDIO_CODING_MODULE_ERROR, kTraceError, - "SetISACMaxPayloadSize() failed to set max payload size"); - return -1; - } - return 0; -} - -int32_t Channel::RegisterExternalTransport(Transport& transport) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), - "Channel::RegisterExternalTransport()"); - - CriticalSectionScoped cs(&_callbackCritSect); - - if (_externalTransport) + if (_externalTransport) { _engineStatisticsPtr->SetLastError(VE_INVALID_OPERATION, kTraceError, @@ -2142,7 +1635,8 @@ return 0; } -int32_t Channel::ReceivedRTPPacket(const int8_t* data, int32_t length) { +int32_t Channel::ReceivedRTPPacket(const int8_t* data, int32_t length, + const PacketTime& packet_time) { WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::ReceivedRTPPacket()"); @@ -2171,6 +1665,23 @@ rtp_receive_statistics_->IncomingPacket(header, length, IsPacketRetransmitted(header, in_order)); rtp_payload_registry_->SetIncomingPayloadType(header); + + // Forward any packets to ViE bandwidth estimator, if enabled. + { + CriticalSectionScoped cs(&_callbackCritSect); + if (vie_network_) { + int64_t arrival_time_ms; + if (packet_time.timestamp != -1) { + arrival_time_ms = (packet_time.timestamp + 500) / 1000; + } else { + arrival_time_ms = TickTime::MillisecondTimestamp(); + } + int payload_length = length - header.headerLength; + vie_network_->ReceivedBWEPacket(video_channel_, arrival_time_ms, + payload_length, header); + } + } + return ReceivePacket(received_packet, length, header, in_order) ? 0 : -1; } @@ -2268,6 +1779,24 @@ VE_SOCKET_TRANSPORT_MODULE_ERROR, kTraceWarning, "Channel::IncomingRTPPacket() RTCP packet is invalid"); } + + { + CriticalSectionScoped lock(ts_stats_lock_.get()); + uint16_t rtt = GetRTT(); + if (rtt == 0) { + // Waiting for valid RTT. + return 0; + } + uint32_t ntp_secs = 0; + uint32_t ntp_frac = 0; + uint32_t rtp_timestamp = 0; + if (0 != _rtpRtcpModule->RemoteNTP(&ntp_secs, &ntp_frac, NULL, NULL, + &rtp_timestamp)) { + // Waiting for RTCP. + return 0; + } + ntp_estimator_.UpdateRtcpTimestamp(rtt, ntp_secs, ntp_frac, rtp_timestamp); + } return 0; } @@ -2285,7 +1814,7 @@ "stopPosition=%d)", fileName, loop, format, volumeScaling, startPosition, stopPosition); - if (_outputFilePlaying) + if (channel_state_.Get().output_file_playing) { _engineStatisticsPtr->SetLastError( VE_ALREADY_PLAYING, kTraceError, @@ -2334,7 +1863,7 @@ return -1; } _outputFilePlayerPtr->RegisterModuleFileCallback(this); - _outputFilePlaying = true; + channel_state_.SetOutputFilePlaying(true); } if (RegisterFilePlayingToMixer() != 0) @@ -2364,7 +1893,7 @@ } - if (_outputFilePlaying) + if (channel_state_.Get().output_file_playing) { _engineStatisticsPtr->SetLastError( VE_ALREADY_PLAYING, kTraceError, @@ -2412,7 +1941,7 @@ return -1; } _outputFilePlayerPtr->RegisterModuleFileCallback(this); - _outputFilePlaying = true; + channel_state_.SetOutputFilePlaying(true); } if (RegisterFilePlayingToMixer() != 0) @@ -2426,7 +1955,7 @@ WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::StopPlayingFileLocally()"); - if (!_outputFilePlaying) + if (!channel_state_.Get().output_file_playing) { _engineStatisticsPtr->SetLastError( VE_INVALID_OPERATION, kTraceWarning, @@ -2447,7 +1976,7 @@ _outputFilePlayerPtr->RegisterModuleFileCallback(NULL); FilePlayer::DestroyFilePlayer(_outputFilePlayerPtr); _outputFilePlayerPtr = NULL; - _outputFilePlaying = false; + channel_state_.SetOutputFilePlaying(false); } // _fileCritSect cannot be taken while calling // SetAnonymousMixibilityStatus. Refer to comments in @@ -2469,7 +1998,7 @@ WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::IsPlayingFileLocally()"); - return (int32_t)_outputFilePlaying; + return channel_state_.Get().output_file_playing; } int Channel::RegisterFilePlayingToMixer() @@ -2477,7 +2006,8 @@ // Return success for not registering for file playing to mixer if: // 1. playing file before playout is started on that channel. // 2. starting playout without file playing on that channel. - if (!_playing || !_outputFilePlaying) + if (!channel_state_.Get().playing || + !channel_state_.Get().output_file_playing) { return 0; } @@ -2488,8 +2018,8 @@ // the file, _fileCritSect will be taken. This would result in a deadlock. if (_outputMixerPtr->SetAnonymousMixabilityStatus(*this, true) != 0) { + channel_state_.SetOutputFilePlaying(false); CriticalSectionScoped cs(&_fileCritSect); - _outputFilePlaying = false; _engineStatisticsPtr->SetLastError( VE_AUDIO_CONF_MIX_MODULE_ERROR, kTraceError, "StartPlayingFile() failed to add participant as file to mixer"); @@ -2502,61 +2032,6 @@ return 0; } -int Channel::ScaleLocalFilePlayout(float scale) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::ScaleLocalFilePlayout(scale=%5.3f)", scale); - - CriticalSectionScoped cs(&_fileCritSect); - - if (!_outputFilePlaying) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_OPERATION, kTraceError, - "ScaleLocalFilePlayout() isnot playing"); - return -1; - } - if ((_outputFilePlayerPtr == NULL) || - (_outputFilePlayerPtr->SetAudioScaling(scale) != 0)) - { - _engineStatisticsPtr->SetLastError( - VE_BAD_ARGUMENT, kTraceError, - "SetAudioScaling() failed to scale the playout"); - return -1; - } - - return 0; -} - -int Channel::GetLocalPlayoutPosition(int& positionMs) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::GetLocalPlayoutPosition(position=?)"); - - uint32_t position; - - CriticalSectionScoped cs(&_fileCritSect); - - if (_outputFilePlayerPtr == NULL) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_OPERATION, kTraceError, - "GetLocalPlayoutPosition() filePlayer instance doesnot exist"); - return -1; - } - - if (_outputFilePlayerPtr->GetPlayoutPosition(position) != 0) - { - _engineStatisticsPtr->SetLastError( - VE_BAD_FILE, kTraceError, - "GetLocalPlayoutPosition() failed"); - return -1; - } - positionMs = position; - - return 0; -} - int Channel::StartPlayingFileAsMicrophone(const char* fileName, bool loop, FileFormats format, @@ -2571,7 +2046,9 @@ "stopPosition=%d)", fileName, loop, format, volumeScaling, startPosition, stopPosition); - if (_inputFilePlaying) + CriticalSectionScoped cs(&_fileCritSect); + + if (channel_state_.Get().input_file_playing) { _engineStatisticsPtr->SetLastError( VE_ALREADY_PLAYING, kTraceWarning, @@ -2579,8 +2056,6 @@ return 0; } - CriticalSectionScoped cs(&_fileCritSect); - // Destroy the old instance if (_inputFilePlayerPtr) { @@ -2621,7 +2096,7 @@ return -1; } _inputFilePlayerPtr->RegisterModuleFileCallback(this); - _inputFilePlaying = true; + channel_state_.SetInputFilePlaying(true); return 0; } @@ -2646,7 +2121,9 @@ return -1; } - if (_inputFilePlaying) + CriticalSectionScoped cs(&_fileCritSect); + + if (channel_state_.Get().input_file_playing) { _engineStatisticsPtr->SetLastError( VE_ALREADY_PLAYING, kTraceWarning, @@ -2654,8 +2131,6 @@ return 0; } - CriticalSectionScoped cs(&_fileCritSect); - // Destroy the old instance if (_inputFilePlayerPtr) { @@ -2692,7 +2167,7 @@ } _inputFilePlayerPtr->RegisterModuleFileCallback(this); - _inputFilePlaying = true; + channel_state_.SetInputFilePlaying(true); return 0; } @@ -2702,7 +2177,9 @@ WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::StopPlayingFileAsMicrophone()"); - if (!_inputFilePlaying) + CriticalSectionScoped cs(&_fileCritSect); + + if (!channel_state_.Get().input_file_playing) { _engineStatisticsPtr->SetLastError( VE_INVALID_OPERATION, kTraceWarning, @@ -2710,7 +2187,6 @@ return 0; } - CriticalSectionScoped cs(&_fileCritSect); if (_inputFilePlayerPtr->StopPlayingFile() != 0) { _engineStatisticsPtr->SetLastError( @@ -2721,7 +2197,7 @@ _inputFilePlayerPtr->RegisterModuleFileCallback(NULL); FilePlayer::DestroyFilePlayer(_inputFilePlayerPtr); _inputFilePlayerPtr = NULL; - _inputFilePlaying = false; + channel_state_.SetInputFilePlaying(false); return 0; } @@ -2730,35 +2206,7 @@ { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::IsPlayingFileAsMicrophone()"); - - return _inputFilePlaying; -} - -int Channel::ScaleFileAsMicrophonePlayout(float scale) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::ScaleFileAsMicrophonePlayout(scale=%5.3f)", scale); - - CriticalSectionScoped cs(&_fileCritSect); - - if (!_inputFilePlaying) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_OPERATION, kTraceError, - "ScaleFileAsMicrophonePlayout() isnot playing"); - return -1; - } - - if ((_inputFilePlayerPtr == NULL) || - (_inputFilePlayerPtr->SetAudioScaling(scale) != 0)) - { - _engineStatisticsPtr->SetLastError( - VE_BAD_ARGUMENT, kTraceError, - "SetAudioScaling() failed to scale playout"); - return -1; - } - - return 0; + return channel_state_.Get().input_file_playing; } int Channel::StartRecordingPlayout(const char* fileName, @@ -2950,6 +2398,7 @@ void Channel::SetMixWithMicStatus(bool mix) { + CriticalSectionScoped cs(&_fileCritSect); _mixFileWithMicrophone=mix; } @@ -3036,54 +2485,6 @@ return 0; } -int -Channel::RegisterExternalEncryption(Encryption& encryption) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::RegisterExternalEncryption()"); - - CriticalSectionScoped cs(&_callbackCritSect); - - if (_encryptionPtr) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_OPERATION, kTraceError, - "RegisterExternalEncryption() encryption already enabled"); - return -1; - } - - _encryptionPtr = &encryption; - - _decrypting = true; - _encrypting = true; - - return 0; -} - -int -Channel::DeRegisterExternalEncryption() -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::DeRegisterExternalEncryption()"); - - CriticalSectionScoped cs(&_callbackCritSect); - - if (!_encryptionPtr) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_OPERATION, kTraceWarning, - "DeRegisterExternalEncryption() encryption already disabled"); - return 0; - } - - _decrypting = false; - _encrypting = false; - - _encryptionPtr = NULL; - - return 0; -} - int Channel::SendTelephoneEventOutband(unsigned char eventCode, int lengthMs, int attenuationDb, bool playDtmfEvent) @@ -3122,27 +2523,6 @@ } int -Channel::SetDtmfPlayoutStatus(bool enable) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::SetDtmfPlayoutStatus()"); - if (audio_coding_->SetDtmfPlayoutStatus(enable) != 0) - { - _engineStatisticsPtr->SetLastError( - VE_AUDIO_CODING_MODULE_ERROR, kTraceWarning, - "SetDtmfPlayoutStatus() failed to set Dtmf playout"); - return -1; - } - return 0; -} - -bool -Channel::DtmfPlayoutStatus() const -{ - return audio_coding_->DtmfPlayoutStatus(); -} - -int Channel::SetSendTelephoneEventPayloadType(unsigned char type) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), @@ -3301,7 +2681,7 @@ } _rxAgcIsEnabled = enable; - _rxApmIsEnabled = ((_rxAgcIsEnabled == true) || (_rxNsIsEnabled == true)); + channel_state_.SetRxApmIsEnabled(_rxAgcIsEnabled || _rxNsIsEnabled); return 0; } @@ -3450,7 +2830,7 @@ } _rxNsIsEnabled = enable; - _rxApmIsEnabled = ((_rxAgcIsEnabled == true) || (_rxNsIsEnabled == true)); + channel_state_.SetRxApmIsEnabled(_rxAgcIsEnabled || _rxNsIsEnabled); return 0; } @@ -3477,63 +2857,21 @@ mode = kNsModerateSuppression; break; case NoiseSuppression::kHigh: - mode = kNsHighSuppression; - break; - case NoiseSuppression::kVeryHigh: - mode = kNsVeryHighSuppression; - break; - } - - WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, - VoEId(_instanceId,_channelId), - "GetRxNsStatus() => enabled=%d, mode=%d", enabled, mode); - return 0; -} - -#endif // #ifdef WEBRTC_VOICE_ENGINE_NR - -int -Channel::RegisterRTPObserver(VoERTPObserver& observer) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), - "Channel::RegisterRTPObserver()"); - CriticalSectionScoped cs(&_callbackCritSect); - - if (_rtpObserverPtr) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_OPERATION, kTraceError, - "RegisterRTPObserver() observer already enabled"); - return -1; - } - - _rtpObserverPtr = &observer; - _rtpObserver = true; - - return 0; -} - -int -Channel::DeRegisterRTPObserver() -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::DeRegisterRTPObserver()"); - CriticalSectionScoped cs(&_callbackCritSect); - - if (!_rtpObserverPtr) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_OPERATION, kTraceWarning, - "DeRegisterRTPObserver() observer already disabled"); - return 0; + mode = kNsHighSuppression; + break; + case NoiseSuppression::kVeryHigh: + mode = kNsVeryHighSuppression; + break; } - _rtpObserver = false; - _rtpObserverPtr = NULL; - + WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, + VoEId(_instanceId,_channelId), + "GetRxNsStatus() => enabled=%d, mode=%d", enabled, mode); return 0; } +#endif // #ifdef WEBRTC_VOICE_ENGINE_NR + int Channel::RegisterRTCPObserver(VoERTCPObserver& observer) { @@ -3581,20 +2919,14 @@ { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SetLocalSSRC()"); - if (_sending) + if (channel_state_.Get().sending) { _engineStatisticsPtr->SetLastError( VE_ALREADY_SENDING, kTraceError, "SetLocalSSRC() already sending"); return -1; } - if (_rtpRtcpModule->SetSSRC(ssrc) != 0) - { - _engineStatisticsPtr->SetLastError( - VE_RTP_RTCP_MODULE_ERROR, kTraceError, - "SetLocalSSRC() failed to set SSRC"); - return -1; - } + _rtpRtcpModule->SetSSRC(ssrc); return 0; } @@ -3618,70 +2950,34 @@ return 0; } -int -Channel::GetRemoteCSRCs(unsigned int arrCSRC[15]) -{ - if (arrCSRC == NULL) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_ARGUMENT, kTraceError, - "GetRemoteCSRCs() invalid array argument"); - return -1; - } - uint32_t arrOfCSRC[kRtpCsrcSize]; - int32_t CSRCs(0); - CSRCs = _rtpRtcpModule->CSRCs(arrOfCSRC); - if (CSRCs > 0) - { - memcpy(arrCSRC, arrOfCSRC, CSRCs * sizeof(uint32_t)); - for (int i = 0; i < (int) CSRCs; i++) - { - WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, - VoEId(_instanceId, _channelId), - "GetRemoteCSRCs() => arrCSRC[%d]=%lu", i, arrCSRC[i]); - } - } else - { - WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, - VoEId(_instanceId, _channelId), - "GetRemoteCSRCs() => list is empty!"); - } - return CSRCs; +int Channel::SetSendAudioLevelIndicationStatus(bool enable, unsigned char id) { + _includeAudioLevelIndication = enable; + return SetSendRtpHeaderExtension(enable, kRtpExtensionAudioLevel, id); } -int -Channel::SetRTPAudioLevelIndicationStatus(bool enable, unsigned char ID) -{ - if (rtp_audioproc_.get() == NULL) { - rtp_audioproc_.reset(AudioProcessing::Create(VoEModuleId(_instanceId, - _channelId))); - } - - if (rtp_audioproc_->level_estimator()->Enable(enable) != - AudioProcessing::kNoError) { - _engineStatisticsPtr->SetLastError(VE_APM_ERROR, kTraceError, - "Failed to enable AudioProcessing::level_estimator()"); +int Channel::SetReceiveAudioLevelIndicationStatus(bool enable, + unsigned char id) { + rtp_header_parser_->DeregisterRtpHeaderExtension( + kRtpExtensionAudioLevel); + if (enable && !rtp_header_parser_->RegisterRtpHeaderExtension( + kRtpExtensionAudioLevel, id)) { return -1; } + return 0; +} - _includeAudioLevelIndication = enable; - if (enable) { - rtp_header_parser_->RegisterRtpHeaderExtension(kRtpExtensionAudioLevel, - ID); - } else { - rtp_header_parser_->DeregisterRtpHeaderExtension(kRtpExtensionAudioLevel); - } - return _rtpRtcpModule->SetRTPAudioLevelIndicationStatus(enable, ID); +int Channel::SetSendAbsoluteSenderTimeStatus(bool enable, unsigned char id) { + return SetSendRtpHeaderExtension(enable, kRtpExtensionAbsoluteSendTime, id); } -int -Channel::GetRTPAudioLevelIndicationStatus(bool& enabled, unsigned char& ID) -{ - WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, - VoEId(_instanceId,_channelId), - "GetRTPAudioLevelIndicationStatus() => enabled=%d, ID=%u", - enabled, ID); - return _rtpRtcpModule->GetRTPAudioLevelIndicationStatus(enabled, ID); +int Channel::SetReceiveAbsoluteSenderTimeStatus(bool enable, unsigned char id) { + rtp_header_parser_->DeregisterRtpHeaderExtension( + kRtpExtensionAbsoluteSendTime); + if (enable && !rtp_header_parser_->RegisterRtpHeaderExtension( + kRtpExtensionAbsoluteSendTime, id)) { + return -1; + } + return 0; } int @@ -3727,22 +3023,6 @@ } int -Channel::GetRTCP_CNAME(char cName[256]) -{ - if (_rtpRtcpModule->CNAME(cName) != 0) - { - _engineStatisticsPtr->SetLastError( - VE_RTP_RTCP_MODULE_ERROR, kTraceError, - "GetRTCP_CNAME() failed to retrieve RTCP CNAME"); - return -1; - } - WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, - VoEId(_instanceId, _channelId), - "GetRTCP_CNAME() => cName=%s", cName); - return 0; -} - -int Channel::GetRemoteRTCP_CNAME(char cName[256]) { if (cName == NULL) @@ -3854,7 +3134,7 @@ { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SendApplicationDefinedRTCPPacket()"); - if (!_sending) + if (!channel_state_.Get().sending) { _engineStatisticsPtr->SetLastError( VE_NOT_SENDING, kTraceError, @@ -3940,29 +3220,6 @@ return 0; } -int Channel::GetRemoteRTCPSenderInfo(SenderInfo* sender_info) { - if (sender_info == NULL) { - _engineStatisticsPtr->SetLastError(VE_INVALID_ARGUMENT, kTraceError, - "GetRemoteRTCPSenderInfo() invalid sender_info."); - return -1; - } - - // Get the sender info from the latest received RTCP Sender Report. - RTCPSenderInfo rtcp_sender_info; - if (_rtpRtcpModule->RemoteRTCPStat(&rtcp_sender_info) != 0) { - _engineStatisticsPtr->SetLastError(VE_RTP_RTCP_MODULE_ERROR, kTraceError, - "GetRemoteRTCPSenderInfo() failed to read RTCP SR sender info."); - return -1; - } - - sender_info->NTP_timestamp_high = rtcp_sender_info.NTPseconds; - sender_info->NTP_timestamp_low = rtcp_sender_info.NTPfraction; - sender_info->RTP_timestamp = rtcp_sender_info.RTPtimeStamp; - sender_info->sender_packet_count = rtcp_sender_info.sendPacketCount; - sender_info->sender_octet_count = rtcp_sender_info.sendOctetCount; - return 0; -} - int Channel::GetRemoteRTCPReportBlocks( std::vector* report_blocks) { if (report_blocks == NULL) { @@ -4003,7 +3260,7 @@ int Channel::GetRTPStatistics(CallStatistics& stats) { - // --- Part one of the final structure (four values) + // --- RtcpStatistics // The jitter statistics is updated for each received RTP packet and is // based on received packets. @@ -4030,50 +3287,14 @@ stats.fractionLost, stats.cumulativeLost, stats.extendedMax, stats.jitterSamples); - // --- Part two of the final structure (one value) - - uint16_t RTT(0); - RTCPMethod method = _rtpRtcpModule->RTCP(); - if (method == kRtcpOff) - { - WEBRTC_TRACE(kTraceWarning, kTraceVoice, - VoEId(_instanceId, _channelId), - "GetRTPStatistics() RTCP is disabled => valid RTT " - "measurements cannot be retrieved"); - } else - { - // The remote SSRC will be zero if no RTP packet has been received. - uint32_t remoteSSRC = rtp_receiver_->SSRC(); - if (remoteSSRC > 0) - { - uint16_t avgRTT(0); - uint16_t maxRTT(0); - uint16_t minRTT(0); - - if (_rtpRtcpModule->RTT(remoteSSRC, &RTT, &avgRTT, &minRTT, &maxRTT) - != 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceVoice, - VoEId(_instanceId, _channelId), - "GetRTPStatistics() failed to retrieve RTT from " - "the RTP/RTCP module"); - } - } else - { - WEBRTC_TRACE(kTraceWarning, kTraceVoice, - VoEId(_instanceId, _channelId), - "GetRTPStatistics() failed to measure RTT since no " - "RTP packets have been received yet"); - } - } - - stats.rttMs = static_cast (RTT); + // --- RTT + stats.rttMs = GetRTT(); WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, _channelId), "GetRTPStatistics() => rttMs=%d", stats.rttMs); - // --- Part three of the final structure (four values) + // --- Data counters uint32_t bytesSent(0); uint32_t packetsSent(0); @@ -4105,18 +3326,23 @@ stats.bytesSent, stats.packetsSent, stats.bytesReceived, stats.packetsReceived); + // --- Timestamps + { + CriticalSectionScoped lock(ts_stats_lock_.get()); + stats.capture_start_ntp_time_ms_ = capture_start_ntp_time_ms_; + } return 0; } -int Channel::SetFECStatus(bool enable, int redPayloadtype) { +int Channel::SetREDStatus(bool enable, int redPayloadtype) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), - "Channel::SetFECStatus()"); + "Channel::SetREDStatus()"); if (enable) { if (redPayloadtype < 0 || redPayloadtype > 127) { _engineStatisticsPtr->SetLastError( VE_PLTYPE_ERROR, kTraceError, - "SetFECStatus() invalid RED payload type"); + "SetREDStatus() invalid RED payload type"); return -1; } @@ -4128,19 +3354,19 @@ } } - if (audio_coding_->SetFECStatus(enable) != 0) { + if (audio_coding_->SetREDStatus(enable) != 0) { _engineStatisticsPtr->SetLastError( VE_AUDIO_CODING_MODULE_ERROR, kTraceError, - "SetFECStatus() failed to set FEC state in the ACM"); + "SetREDStatus() failed to set RED state in the ACM"); return -1; } return 0; } int -Channel::GetFECStatus(bool& enabled, int& redPayloadtype) +Channel::GetREDStatus(bool& enabled, int& redPayloadtype) { - enabled = audio_coding_->FECStatus(); + enabled = audio_coding_->REDStatus(); if (enabled) { int8_t payloadType(0); @@ -4148,22 +3374,43 @@ { _engineStatisticsPtr->SetLastError( VE_RTP_RTCP_MODULE_ERROR, kTraceError, - "GetFECStatus() failed to retrieve RED PT from RTP/RTCP " + "GetREDStatus() failed to retrieve RED PT from RTP/RTCP " "module"); return -1; } WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, _channelId), - "GetFECStatus() => enabled=%d, redPayloadtype=%d", + "GetREDStatus() => enabled=%d, redPayloadtype=%d", enabled, redPayloadtype); return 0; } WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, _channelId), - "GetFECStatus() => enabled=%d", enabled); + "GetREDStatus() => enabled=%d", enabled); return 0; } +int Channel::SetCodecFECStatus(bool enable) { + WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), + "Channel::SetCodecFECStatus()"); + + if (audio_coding_->SetCodecFEC(enable) != 0) { + _engineStatisticsPtr->SetLastError( + VE_AUDIO_CODING_MODULE_ERROR, kTraceError, + "SetCodecFECStatus() failed to set FEC state"); + return -1; + } + return 0; +} + +bool Channel::GetCodecFECStatus() { + bool enabled = audio_coding_->CodecFEC(); + WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, + VoEId(_instanceId, _channelId), + "GetCodecFECStatus() => enabled=%d", enabled); + return enabled; +} + void Channel::SetNACKStatus(bool enable, int maxNumberOfPackets) { // None of these functions can fail. _rtpRtcpModule->SetStorePacketsStatus(enable, maxNumberOfPackets); @@ -4256,76 +3503,19 @@ return rtpDumpPtr->IsActive(); } -int -Channel::InsertExtraRTPPacket(unsigned char payloadType, - bool markerBit, - const char* payloadData, - unsigned short payloadSize) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), - "Channel::InsertExtraRTPPacket()"); - if (payloadType > 127) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_PLTYPE, kTraceError, - "InsertExtraRTPPacket() invalid payload type"); - return -1; - } - if (payloadData == NULL) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_ARGUMENT, kTraceError, - "InsertExtraRTPPacket() invalid payload data"); - return -1; - } - if (payloadSize > _rtpRtcpModule->MaxDataPayloadLength()) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_ARGUMENT, kTraceError, - "InsertExtraRTPPacket() invalid payload size"); - return -1; - } - if (!_sending) - { - _engineStatisticsPtr->SetLastError( - VE_NOT_SENDING, kTraceError, - "InsertExtraRTPPacket() not sending"); - return -1; - } - - // Create extra RTP packet by calling RtpRtcp::SendOutgoingData(). - // Transport::SendPacket() will be called by the module when the RTP packet - // is created. - // The call to SendOutgoingData() does *not* modify the timestamp and - // payloadtype to ensure that the RTP module generates a valid RTP packet - // (user might utilize a non-registered payload type). - // The marker bit and payload type will be replaced just before the actual - // transmission, i.e., the actual modification is done *after* the RTP - // module has delivered its RTP packet back to the VoE. - // We will use the stored values above when the packet is modified - // (see Channel::SendPacket()). - - _extraPayloadType = payloadType; - _extraMarkerBit = markerBit; - _insertExtraRTPPacket = true; - - if (_rtpRtcpModule->SendOutgoingData(kAudioFrameSpeech, - _lastPayloadType, - _lastLocalTimeStamp, - // Leaving the time when this frame was - // received from the capture device as - // undefined for voice for now. - -1, - (const uint8_t*) payloadData, - payloadSize) != 0) - { - _engineStatisticsPtr->SetLastError( - VE_RTP_RTCP_MODULE_ERROR, kTraceError, - "InsertExtraRTPPacket() failed to send extra RTP packet"); - return -1; - } - - return 0; +void Channel::SetVideoEngineBWETarget(ViENetwork* vie_network, + int video_channel) { + CriticalSectionScoped cs(&_callbackCritSect); + if (vie_network_) { + vie_network_->Release(); + vie_network_ = NULL; + } + video_channel_ = -1; + + if (vie_network != NULL && video_channel != -1) { + vie_network_ = vie_network; + video_channel_ = video_channel; + } } uint32_t @@ -4338,61 +3528,26 @@ return 0; } -// TODO(xians): This method borrows quite some code from -// TransmitMixer::GenerateAudioFrame(), refactor these two methods and reduce -// code duplication. void Channel::Demultiplex(const int16_t* audio_data, int sample_rate, int number_of_frames, int number_of_channels) { - // The highest sample rate that WebRTC supports for mono audio is 96kHz. - static const int kMaxNumberOfFrames = 960; - assert(number_of_frames <= kMaxNumberOfFrames); - - // Get the send codec information for doing resampling or downmixing later on. CodecInst codec; GetSendCodec(codec); - assert(codec.channels == 1 || codec.channels == 2); - int support_sample_rate = std::min(32000, - std::min(sample_rate, codec.plfreq)); - - // Downmix the data to mono if needed. - const int16_t* audio_ptr = audio_data; - if (number_of_channels == 2 && codec.channels == 1) { - if (!mono_recording_audio_.get()) - mono_recording_audio_.reset(new int16_t[kMaxNumberOfFrames]); - - AudioFrameOperations::StereoToMono(audio_data, number_of_frames, - mono_recording_audio_.get()); - audio_ptr = mono_recording_audio_.get(); - } - - // Resample the data to the sample rate that the codec is using. - if (input_resampler_.InitializeIfNeeded(sample_rate, - support_sample_rate, - codec.channels)) { - WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1), - "Channel::Demultiplex() unable to resample"); - return; - } - - int out_length = input_resampler_.Resample(audio_ptr, - number_of_frames * codec.channels, - _audioFrame.data_, - AudioFrame::kMaxDataSizeSamples); - if (out_length == -1) { - WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1), - "Channel::Demultiplex() resampling failed"); - return; - } - _audioFrame.samples_per_channel_ = out_length / codec.channels; - _audioFrame.timestamp_ = -1; - _audioFrame.sample_rate_hz_ = support_sample_rate; - _audioFrame.speech_type_ = AudioFrame::kNormalSpeech; - _audioFrame.vad_activity_ = AudioFrame::kVadUnknown; - _audioFrame.num_channels_ = codec.channels; - _audioFrame.id_ = _channelId; + if (!mono_recording_audio_.get()) { + // Temporary space for DownConvertToCodecFormat. + mono_recording_audio_.reset(new int16_t[kMaxMonoDataSizeSamples]); + } + DownConvertToCodecFormat(audio_data, + number_of_frames, + number_of_channels, + sample_rate, + codec.channels, + codec.plfreq, + mono_recording_audio_.get(), + &input_resampler_, + &_audioFrame); } uint32_t @@ -4405,20 +3560,20 @@ { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::PrepareEncodeAndSend() invalid audio frame"); - return -1; + return 0xFFFFFFFF; } - if (_inputFilePlaying) + if (channel_state_.Get().input_file_playing) { MixOrReplaceAudioWithFile(mixingFrequency); } - if (Mute()) - { - AudioFrameOperations::Mute(_audioFrame); + bool is_muted = Mute(); // Cache locally as Mute() takes a lock. + if (is_muted) { + AudioFrameOperations::Mute(_audioFrame); } - if (_inputExternalMedia) + if (channel_state_.Get().input_external_media) { CriticalSectionScoped cs(&_callbackCritSect); const bool isStereo = (_audioFrame.num_channels_ == 2); @@ -4437,11 +3592,11 @@ InsertInbandDtmfTone(); if (_includeAudioLevelIndication) { - // Performs level analysis only; does not affect the signal. - int err = rtp_audioproc_->ProcessStream(&_audioFrame); - if (err) { - LOG(LS_ERROR) << "ProcessStream() error: " << err; - assert(false); + int length = _audioFrame.samples_per_channel_ * _audioFrame.num_channels_; + if (is_muted) { + rms_level_.ProcessMuted(length); + } else { + rms_level_.Process(_audioFrame.data_, length); } } @@ -4459,7 +3614,7 @@ { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::EncodeAndSend() invalid audio frame"); - return -1; + return 0xFFFFFFFF; } _audioFrame.id_ = _channelId; @@ -4472,7 +3627,7 @@ { WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::EncodeAndSend() ACM encoding failed"); - return -1; + return 0xFFFFFFFF; } _timeStamp += _audioFrame.samples_per_channel_; @@ -4517,7 +3672,7 @@ return -1; } _inputExternalMediaCallbackPtr = &processObject; - _inputExternalMedia = true; + channel_state_.SetInputExternalMedia(true); } return 0; } @@ -4552,7 +3707,7 @@ "input external media already disabled"); return 0; } - _inputExternalMedia = false; + channel_state_.SetInputExternalMedia(false); _inputExternalMediaCallbackPtr = NULL; } @@ -4563,7 +3718,7 @@ WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::SetExternalMixing(enabled=%d)", enabled); - if (_playing) + if (channel_state_.Get().playing) { _engineStatisticsPtr->SetLastError( VE_INVALID_OPERATION, kTraceError, @@ -4578,68 +3733,6 @@ } int -Channel::ResetRTCPStatistics() -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::ResetRTCPStatistics()"); - uint32_t remoteSSRC(0); - remoteSSRC = rtp_receiver_->SSRC(); - return _rtpRtcpModule->ResetRTT(remoteSSRC); -} - -int -Channel::GetRoundTripTimeSummary(StatVal& delaysMs) const -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::GetRoundTripTimeSummary()"); - // Override default module outputs for the case when RTCP is disabled. - // This is done to ensure that we are backward compatible with the - // VoiceEngine where we did not use RTP/RTCP module. - if (!_rtpRtcpModule->RTCP()) - { - delaysMs.min = -1; - delaysMs.max = -1; - delaysMs.average = -1; - WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::GetRoundTripTimeSummary() RTCP is disabled =>" - " valid RTT measurements cannot be retrieved"); - return 0; - } - - uint32_t remoteSSRC; - uint16_t RTT; - uint16_t avgRTT; - uint16_t maxRTT; - uint16_t minRTT; - // The remote SSRC will be zero if no RTP packet has been received. - remoteSSRC = rtp_receiver_->SSRC(); - if (remoteSSRC == 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::GetRoundTripTimeSummary() unable to measure RTT" - " since no RTP packet has been received yet"); - } - - // Retrieve RTT statistics from the RTP/RTCP module for the specified - // channel and SSRC. The SSRC is required to parse out the correct source - // in conference scenarios. - if (_rtpRtcpModule->RTT(remoteSSRC, &RTT, &avgRTT, &minRTT,&maxRTT) != 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,_channelId), - "GetRoundTripTimeSummary unable to retrieve RTT values" - " from the RTCP layer"); - delaysMs.min = -1; delaysMs.max = -1; delaysMs.average = -1; - } - else - { - delaysMs.min = minRTT; - delaysMs.max = maxRTT; - delaysMs.average = avgRTT; - } - return 0; -} - -int Channel::GetNetworkStatistics(NetworkStatistics& stats) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), @@ -4723,12 +3816,8 @@ uint32_t playout_timestamp = 0; if (audio_coding_->PlayoutTimestamp(&playout_timestamp) == -1) { - WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,_channelId), - "Channel::UpdatePlayoutTimestamp() failed to read playout" - " timestamp from the ACM"); - _engineStatisticsPtr->SetLastError( - VE_CANNOT_RETRIEVE_VALUE, kTraceError, - "UpdatePlayoutTimestamp() failed to retrieve timestamp"); + // This can happen if this channel has not been received any RTP packet. In + // this case, NetEq is not capable of computing playout timestamp. return; } @@ -4743,20 +3832,10 @@ return; } - int32_t playout_frequency = audio_coding_->PlayoutFrequency(); - CodecInst current_recive_codec; - if (audio_coding_->ReceiveCodec(¤t_recive_codec) == 0) { - if (STR_CASE_CMP("G722", current_recive_codec.plname) == 0) { - playout_frequency = 8000; - } else if (STR_CASE_CMP("opus", current_recive_codec.plname) == 0) { - playout_frequency = 48000; - } - } - jitter_buffer_playout_timestamp_ = playout_timestamp; // Remove the playout delay. - playout_timestamp -= (delay_ms * (playout_frequency / 1000)); + playout_timestamp -= (delay_ms * (GetPlayoutFrequency() / 1000)); WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::UpdatePlayoutTimestamp() => playoutTimestamp = %lu", @@ -4791,7 +3870,7 @@ { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::SetInitTimestamp()"); - if (_sending) + if (channel_state_.Get().sending) { _engineStatisticsPtr->SetLastError( VE_SENDING, kTraceError, "SetInitTimestamp() already sending"); @@ -4812,7 +3891,7 @@ { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::SetInitSequenceNumber()"); - if (_sending) + if (channel_state_.Get().sending) { _engineStatisticsPtr->SetLastError( VE_SENDING, kTraceError, @@ -4844,7 +3923,7 @@ int32_t Channel::MixOrReplaceAudioWithFile(int mixingFrequency) { - scoped_array fileBuffer(new int16_t[640]); + scoped_ptr fileBuffer(new int16_t[640]); int fileSamples(0); { @@ -4884,11 +3963,11 @@ { // Currently file stream is always mono. // TODO(xians): Change the code when FilePlayer supports real stereo. - Utility::MixWithSat(_audioFrame.data_, - _audioFrame.num_channels_, - fileBuffer.get(), - 1, - fileSamples); + MixWithSat(_audioFrame.data_, + _audioFrame.num_channels_, + fileBuffer.get(), + 1, + fileSamples); } else { @@ -4896,7 +3975,7 @@ // Currently file stream is always mono. // TODO(xians): Change the code when FilePlayer supports real stereo. _audioFrame.UpdateFrame(_channelId, - -1, + 0xFFFFFFFF, fileBuffer.get(), fileSamples, mixingFrequency, @@ -4912,9 +3991,9 @@ Channel::MixAudioWithFile(AudioFrame& audioFrame, int mixingFrequency) { - assert(mixingFrequency <= 32000); + assert(mixingFrequency <= 48000); - scoped_array fileBuffer(new int16_t[640]); + scoped_ptr fileBuffer(new int16_t[960]); int fileSamples(0); { @@ -4944,11 +4023,11 @@ { // Currently file stream is always mono. // TODO(xians): Change the code when FilePlayer supports real stereo. - Utility::MixWithSat(audioFrame.data_, - audioFrame.num_channels_, - fileBuffer.get(), - 1, - fileSamples); + MixWithSat(audioFrame.data_, + audioFrame.num_channels_, + fileBuffer.get(), + 1, + fileSamples); } else { @@ -5036,28 +4115,6 @@ return 0; } -void -Channel::ResetDeadOrAliveCounters() -{ - _countDeadDetections = 0; - _countAliveDetections = 0; -} - -void -Channel::UpdateDeadOrAliveCounters(bool alive) -{ - if (alive) - _countAliveDetections++; - else - _countDeadDetections++; -} - -int -Channel::GetDeadOrAliveCounters(int& countDead, int& countAlive) const -{ - return 0; -} - int32_t Channel::SendPacketRaw(const void *data, int len, bool RTCP) { @@ -5084,44 +4141,29 @@ rtp_timestamp, sequence_number); // Get frequency of last received payload - int rtp_receive_frequency = audio_coding_->ReceiveFrequency(); - - CodecInst current_receive_codec; - if (audio_coding_->ReceiveCodec(¤t_receive_codec) != 0) { - return; - } + int rtp_receive_frequency = GetPlayoutFrequency(); // Update the least required delay. least_required_delay_ms_ = audio_coding_->LeastRequiredDelayMs(); - if (STR_CASE_CMP("G722", current_receive_codec.plname) == 0) { - // Even though the actual sampling rate for G.722 audio is - // 16,000 Hz, the RTP clock rate for the G722 payload format is - // 8,000 Hz because that value was erroneously assigned in - // RFC 1890 and must remain unchanged for backward compatibility. - rtp_receive_frequency = 8000; - } else if (STR_CASE_CMP("opus", current_receive_codec.plname) == 0) { - // We are resampling Opus internally to 32,000 Hz until all our - // DSP routines can operate at 48,000 Hz, but the RTP clock - // rate for the Opus payload format is standardized to 48,000 Hz, - // because that is the maximum supported decoding sampling rate. - rtp_receive_frequency = 48000; - } - // |jitter_buffer_playout_timestamp_| updated in UpdatePlayoutTimestamp for // every incoming packet. uint32_t timestamp_diff_ms = (rtp_timestamp - jitter_buffer_playout_timestamp_) / (rtp_receive_frequency / 1000); + if (!IsNewerTimestamp(rtp_timestamp, jitter_buffer_playout_timestamp_) || + timestamp_diff_ms > (2 * kVoiceEngineMaxMinPlayoutDelayMs)) { + // If |jitter_buffer_playout_timestamp_| is newer than the incoming RTP + // timestamp, the resulting difference is negative, but is set to zero. + // This can happen when a network glitch causes a packet to arrive late, + // and during long comfort noise periods with clock drift. + timestamp_diff_ms = 0; + } uint16_t packet_delay_ms = (rtp_timestamp - _previousTimestamp) / (rtp_receive_frequency / 1000); _previousTimestamp = rtp_timestamp; - if (timestamp_diff_ms > (2 * kVoiceEngineMaxMinPlayoutDelayMs)) { - timestamp_diff_ms = 0; - } - if (timestamp_diff_ms == 0) return; if (packet_delay_ms >= 10 && packet_delay_ms <= 60) { @@ -5266,5 +4308,83 @@ return 0; } +int Channel::SetSendRtpHeaderExtension(bool enable, RTPExtensionType type, + unsigned char id) { + int error = 0; + _rtpRtcpModule->DeregisterSendRtpHeaderExtension(type); + if (enable) { + error = _rtpRtcpModule->RegisterSendRtpHeaderExtension(type, id); + } + return error; +} + +int32_t Channel::GetPlayoutFrequency() { + int32_t playout_frequency = audio_coding_->PlayoutFrequency(); + CodecInst current_recive_codec; + if (audio_coding_->ReceiveCodec(¤t_recive_codec) == 0) { + if (STR_CASE_CMP("G722", current_recive_codec.plname) == 0) { + // Even though the actual sampling rate for G.722 audio is + // 16,000 Hz, the RTP clock rate for the G722 payload format is + // 8,000 Hz because that value was erroneously assigned in + // RFC 1890 and must remain unchanged for backward compatibility. + playout_frequency = 8000; + } else if (STR_CASE_CMP("opus", current_recive_codec.plname) == 0) { + // We are resampling Opus internally to 32,000 Hz until all our + // DSP routines can operate at 48,000 Hz, but the RTP clock + // rate for the Opus payload format is standardized to 48,000 Hz, + // because that is the maximum supported decoding sampling rate. + playout_frequency = 48000; + } + } + return playout_frequency; +} + +int Channel::GetRTT() const { + RTCPMethod method = _rtpRtcpModule->RTCP(); + if (method == kRtcpOff) { + WEBRTC_TRACE(kTraceWarning, kTraceVoice, + VoEId(_instanceId, _channelId), + "GetRTPStatistics() RTCP is disabled => valid RTT " + "measurements cannot be retrieved"); + return 0; + } + std::vector report_blocks; + _rtpRtcpModule->RemoteRTCPStat(&report_blocks); + if (report_blocks.empty()) { + WEBRTC_TRACE(kTraceWarning, kTraceVoice, + VoEId(_instanceId, _channelId), + "GetRTPStatistics() failed to measure RTT since no " + "RTCP packets have been received yet"); + return 0; + } + + uint32_t remoteSSRC = rtp_receiver_->SSRC(); + std::vector::const_iterator it = report_blocks.begin(); + for (; it != report_blocks.end(); ++it) { + if (it->remoteSSRC == remoteSSRC) + break; + } + if (it == report_blocks.end()) { + // We have not received packets with SSRC matching the report blocks. + // To calculate RTT we try with the SSRC of the first report block. + // This is very important for send-only channels where we don't know + // the SSRC of the other end. + remoteSSRC = report_blocks[0].remoteSSRC; + } + uint16_t rtt = 0; + uint16_t avg_rtt = 0; + uint16_t max_rtt= 0; + uint16_t min_rtt = 0; + if (_rtpRtcpModule->RTT(remoteSSRC, &rtt, &avg_rtt, &min_rtt, &max_rtt) + != 0) { + WEBRTC_TRACE(kTraceWarning, kTraceVoice, + VoEId(_instanceId, _channelId), + "GetRTPStatistics() failed to retrieve RTT from " + "the RTP/RTCP module"); + return 0; + } + return static_cast(rtt); +} + } // namespace voe } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/channel.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/channel.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/channel.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/channel.h 2015-02-03 14:33:38.000000000 +0000 @@ -8,13 +8,16 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_VOICE_ENGINE_CHANNEL_H -#define WEBRTC_VOICE_ENGINE_CHANNEL_H +#ifndef WEBRTC_VOICE_ENGINE_CHANNEL_H_ +#define WEBRTC_VOICE_ENGINE_CHANNEL_H_ #include "webrtc/common_audio/resampler/include/push_resampler.h" #include "webrtc/common_types.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" #include "webrtc/modules/audio_conference_mixer/interface/audio_conference_mixer_defines.h" +#include "webrtc/modules/audio_processing/rms_level.h" +#include "webrtc/modules/bitrate_controller/include/bitrate_controller.h" +#include "webrtc/modules/rtp_rtcp/interface/remote_ntp_time_estimator.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" #include "webrtc/modules/utility/interface/file_player.h" @@ -25,6 +28,7 @@ #include "webrtc/voice_engine/include/voe_audio_processing.h" #include "webrtc/voice_engine/include/voe_network.h" #include "webrtc/voice_engine/level_indicator.h" +#include "webrtc/voice_engine/network_predictor.h" #include "webrtc/voice_engine/shared_data.h" #include "webrtc/voice_engine/voice_engine_defines.h" @@ -33,6 +37,11 @@ #include "webrtc/voice_engine/include/voe_dtmf.h" #endif +namespace rtc { + +class TimestampWrapAroundHandler; +} + namespace webrtc { class AudioDeviceModule; @@ -41,12 +50,14 @@ class FileWrapper; class ProcessThread; class ReceiveStatistics; +class RemoteNtpTimeEstimator; class RtpDump; class RTPPayloadRegistry; class RtpReceiver; class RTPReceiverAudio; class RtpRtcp; class TelephoneEventHandler; +class ViENetwork; class VoEMediaProcess; class VoERTCPObserver; class VoERTPObserver; @@ -63,6 +74,84 @@ class TransmitMixer; class OutputMixer; +// Helper class to simplify locking scheme for members that are accessed from +// multiple threads. +// Example: a member can be set on thread T1 and read by an internal audio +// thread T2. Accessing the member via this class ensures that we are +// safe and also avoid TSan v2 warnings. +class ChannelState { + public: + struct State { + State() : rx_apm_is_enabled(false), + input_external_media(false), + output_file_playing(false), + input_file_playing(false), + playing(false), + sending(false), + receiving(false) {} + + bool rx_apm_is_enabled; + bool input_external_media; + bool output_file_playing; + bool input_file_playing; + bool playing; + bool sending; + bool receiving; + }; + + ChannelState() : lock_(CriticalSectionWrapper::CreateCriticalSection()) { + } + virtual ~ChannelState() {} + + void Reset() { + CriticalSectionScoped lock(lock_.get()); + state_ = State(); + } + + State Get() const { + CriticalSectionScoped lock(lock_.get()); + return state_; + } + + void SetRxApmIsEnabled(bool enable) { + CriticalSectionScoped lock(lock_.get()); + state_.rx_apm_is_enabled = enable; + } + + void SetInputExternalMedia(bool enable) { + CriticalSectionScoped lock(lock_.get()); + state_.input_external_media = enable; + } + + void SetOutputFilePlaying(bool enable) { + CriticalSectionScoped lock(lock_.get()); + state_.output_file_playing = enable; + } + + void SetInputFilePlaying(bool enable) { + CriticalSectionScoped lock(lock_.get()); + state_.input_file_playing = enable; + } + + void SetPlaying(bool enable) { + CriticalSectionScoped lock(lock_.get()); + state_.playing = enable; + } + + void SetSending(bool enable) { + CriticalSectionScoped lock(lock_.get()); + state_.sending = enable; + } + + void SetReceiving(bool enable) { + CriticalSectionScoped lock(lock_.get()); + state_.receiving = enable; + } + +private: + scoped_ptr lock_; + State state_; +}; class Channel: public RtpData, @@ -105,10 +194,6 @@ int32_t StartReceiving(); int32_t StopReceiving(); - int32_t SetNetEQPlayoutMode(NetEqModes mode); - int32_t GetNetEQPlayoutMode(NetEqModes& mode); - int32_t SetOnHoldStatus(bool enable, OnHoldModes mode); - int32_t GetOnHoldStatus(bool& enabled, OnHoldModes& mode); int32_t RegisterVoiceEngineObserver(VoiceEngineObserver& observer); int32_t DeRegisterVoiceEngineObserver(); @@ -120,14 +205,8 @@ int32_t GetVADStatus(bool& enabledVAD, ACMVADMode& mode, bool& disabledDTX); int32_t SetRecPayloadType(const CodecInst& codec); int32_t GetRecPayloadType(CodecInst& codec); - int32_t SetAMREncFormat(AmrMode mode); - int32_t SetAMRDecFormat(AmrMode mode); - int32_t SetAMRWbEncFormat(AmrMode mode); - int32_t SetAMRWbDecFormat(AmrMode mode); int32_t SetSendCNPayloadType(int type, PayloadFrequencies frequency); - int32_t SetISACInitTargetRate(int rateBps, bool useFixedFrameSize); - int32_t SetISACMaxRate(int rateBps); - int32_t SetISACMaxPayloadSize(int sizeBytes); + int SetOpusMaxPlaybackRate(int frequency_hz); // VoE dual-streaming. int SetSecondarySendCodec(const CodecInst& codec, int red_payload_type); @@ -137,7 +216,8 @@ // VoENetwork int32_t RegisterExternalTransport(Transport& transport); int32_t DeRegisterExternalTransport(); - int32_t ReceivedRTPPacket(const int8_t* data, int32_t length); + int32_t ReceivedRTPPacket(const int8_t* data, int32_t length, + const PacketTime& packet_time); int32_t ReceivedRTCPPacket(const int8_t* data, int32_t length); // VoEFile @@ -155,8 +235,6 @@ int StopPlayingFileLocally(); int IsPlayingFileLocally() const; int RegisterFilePlayingToMixer(); - int ScaleLocalFilePlayout(float scale); - int GetLocalPlayoutPosition(int& positionMs); int StartPlayingFileAsMicrophone(const char* fileName, bool loop, FileFormats format, int startPosition, @@ -171,7 +249,6 @@ const CodecInst* codecInst); int StopPlayingFileAsMicrophone(); int IsPlayingFileAsMicrophone() const; - int ScaleFileAsMicrophonePlayout(float scale); int StartRecordingPlayout(const char* fileName, const CodecInst* codecInst); int StartRecordingPlayout(OutStream* stream, const CodecInst* codecInst); int StopRecordingPlayout(); @@ -194,12 +271,6 @@ int SetChannelOutputVolumeScaling(float scaling); int GetChannelOutputVolumeScaling(float& scaling) const; - // VoECallReport - void ResetDeadOrAliveCounters(); - int ResetRTCPStatistics(); - int GetRoundTripTimeSummary(StatVal& delaysMs) const; - int GetDeadOrAliveCounters(int& countDead, int& countAlive) const; - // VoENetEqStats int GetNetworkStatistics(NetworkStatistics& stats); void GetDecodingCallStatistics(AudioDecodingCallStats* stats) const; @@ -220,17 +291,11 @@ // VoEVideoSyncExtended int GetRtpRtcp(RtpRtcp** rtpRtcpModule, RtpReceiver** rtp_receiver) const; - // VoEEncryption - int RegisterExternalEncryption(Encryption& encryption); - int DeRegisterExternalEncryption(); - // VoEDtmf int SendTelephoneEventOutband(unsigned char eventCode, int lengthMs, int attenuationDb, bool playDtmfEvent); int SendTelephoneEventInband(unsigned char eventCode, int lengthMs, int attenuationDb, bool playDtmfEvent); - int SetDtmfPlayoutStatus(bool enable); - bool DtmfPlayoutStatus() const; int SetSendTelephoneEventPayloadType(unsigned char type); int GetSendTelephoneEventPayloadType(unsigned char& type); @@ -251,20 +316,18 @@ #endif // VoERTP_RTCP - int RegisterRTPObserver(VoERTPObserver& observer); - int DeRegisterRTPObserver(); int RegisterRTCPObserver(VoERTCPObserver& observer); int DeRegisterRTCPObserver(); int SetLocalSSRC(unsigned int ssrc); int GetLocalSSRC(unsigned int& ssrc); int GetRemoteSSRC(unsigned int& ssrc); - int GetRemoteCSRCs(unsigned int arrCSRC[15]); - int SetRTPAudioLevelIndicationStatus(bool enable, unsigned char ID); - int GetRTPAudioLevelIndicationStatus(bool& enable, unsigned char& ID); + int SetSendAudioLevelIndicationStatus(bool enable, unsigned char id); + int SetReceiveAudioLevelIndicationStatus(bool enable, unsigned char id); + int SetSendAbsoluteSenderTimeStatus(bool enable, unsigned char id); + int SetReceiveAbsoluteSenderTimeStatus(bool enable, unsigned char id); int SetRTCPStatus(bool enable); int GetRTCPStatus(bool& enabled); int SetRTCP_CNAME(const char cName[256]); - int GetRTCP_CNAME(char cName[256]); int GetRemoteRTCP_CNAME(char cName[256]); int GetRemoteRTCPReceiverInfo(uint32_t& NTPHigh, uint32_t& NTPLow, uint32_t& receivedPacketCount, @@ -280,98 +343,83 @@ unsigned int& maxJitterMs, unsigned int& discardedPackets, unsigned int& cumulativeLost); - int GetRemoteRTCPSenderInfo(SenderInfo* sender_info); int GetRemoteRTCPReportBlocks(std::vector* report_blocks); int GetRTPStatistics(CallStatistics& stats); - int SetFECStatus(bool enable, int redPayloadtype); - int GetFECStatus(bool& enabled, int& redPayloadtype); + int SetREDStatus(bool enable, int redPayloadtype); + int GetREDStatus(bool& enabled, int& redPayloadtype); + int SetCodecFECStatus(bool enable); + bool GetCodecFECStatus(); void SetNACKStatus(bool enable, int maxNumberOfPackets); int StartRTPDump(const char fileNameUTF8[1024], RTPDirections direction); int StopRTPDump(RTPDirections direction); bool RTPDumpIsActive(RTPDirections direction); - int InsertExtraRTPPacket(unsigned char payloadType, bool markerBit, - const char* payloadData, - unsigned short payloadSize); - uint32_t LastRemoteTimeStamp() { return _lastRemoteTimeStamp; } + // Takes ownership of the ViENetwork. + void SetVideoEngineBWETarget(ViENetwork* vie_network, int video_channel); // From AudioPacketizationCallback in the ACM - int32_t SendData(FrameType frameType, - uint8_t payloadType, - uint32_t timeStamp, - const uint8_t* payloadData, - uint16_t payloadSize, - const RTPFragmentationHeader* fragmentation); + virtual int32_t SendData( + FrameType frameType, + uint8_t payloadType, + uint32_t timeStamp, + const uint8_t* payloadData, + uint16_t payloadSize, + const RTPFragmentationHeader* fragmentation) OVERRIDE; + // From ACMVADCallback in the ACM - int32_t InFrameType(int16_t frameType); + virtual int32_t InFrameType(int16_t frameType) OVERRIDE; int32_t OnRxVadDetected(int vadDecision); // From RtpData in the RTP/RTCP module - int32_t OnReceivedPayloadData(const uint8_t* payloadData, - uint16_t payloadSize, - const WebRtcRTPHeader* rtpHeader); - - bool OnRecoveredPacket(const uint8_t* packet, int packet_length); + virtual int32_t OnReceivedPayloadData( + const uint8_t* payloadData, + uint16_t payloadSize, + const WebRtcRTPHeader* rtpHeader) OVERRIDE; + virtual bool OnRecoveredPacket(const uint8_t* packet, + int packet_length) OVERRIDE; // From RtpFeedback in the RTP/RTCP module - int32_t OnInitializeDecoder( - int32_t id, - int8_t payloadType, - const char payloadName[RTP_PAYLOAD_NAME_SIZE], - int frequency, - uint8_t channels, - uint32_t rate); - - void OnPacketTimeout(int32_t id); - - void OnReceivedPacket(int32_t id, RtpRtcpPacketType packetType); - - void OnPeriodicDeadOrAlive(int32_t id, - RTPAliveType alive); - - void OnIncomingSSRCChanged(int32_t id, - uint32_t ssrc); - - void OnIncomingCSRCChanged(int32_t id, - uint32_t CSRC, bool added); - - void ResetStatistics(uint32_t ssrc); + virtual int32_t OnInitializeDecoder( + int32_t id, + int8_t payloadType, + const char payloadName[RTP_PAYLOAD_NAME_SIZE], + int frequency, + uint8_t channels, + uint32_t rate) OVERRIDE; + virtual void OnIncomingSSRCChanged(int32_t id, + uint32_t ssrc) OVERRIDE; + virtual void OnIncomingCSRCChanged(int32_t id, + uint32_t CSRC, bool added) OVERRIDE; + virtual void ResetStatistics(uint32_t ssrc) OVERRIDE; // From RtcpFeedback in the RTP/RTCP module - void OnApplicationDataReceived(int32_t id, - uint8_t subType, - uint32_t name, - uint16_t length, - const uint8_t* data); + virtual void OnApplicationDataReceived(int32_t id, + uint8_t subType, + uint32_t name, + uint16_t length, + const uint8_t* data) OVERRIDE; // From RtpAudioFeedback in the RTP/RTCP module - void OnReceivedTelephoneEvent(int32_t id, - uint8_t event, - bool endOfEvent); - - void OnPlayTelephoneEvent(int32_t id, - uint8_t event, - uint16_t lengthMs, - uint8_t volume); + virtual void OnPlayTelephoneEvent(int32_t id, + uint8_t event, + uint16_t lengthMs, + uint8_t volume) OVERRIDE; // From Transport (called by the RTP/RTCP module) - int SendPacket(int /*channel*/, const void *data, int len); - int SendRTCPPacket(int /*channel*/, const void *data, int len); + virtual int SendPacket(int /*channel*/, const void *data, int len) OVERRIDE; + virtual int SendRTCPPacket(int /*channel*/, + const void *data, + int len) OVERRIDE; // From MixerParticipant - int32_t GetAudioFrame(int32_t id, AudioFrame& audioFrame); - int32_t NeededFrequency(int32_t id); - - // From MonitorObserver - void OnPeriodicProcess(); + virtual int32_t GetAudioFrame(int32_t id, AudioFrame& audioFrame) OVERRIDE; + virtual int32_t NeededFrequency(int32_t id) OVERRIDE; // From FileCallback - void PlayNotification(int32_t id, - uint32_t durationMs); - void RecordNotification(int32_t id, - uint32_t durationMs); - void PlayFileEnded(int32_t id); - void RecordFileEnded(int32_t id); + virtual void PlayNotification(int32_t id, uint32_t durationMs) OVERRIDE; + virtual void RecordNotification(int32_t id, uint32_t durationMs) OVERRIDE; + virtual void PlayFileEnded(int32_t id) OVERRIDE; + virtual void RecordFileEnded(int32_t id) OVERRIDE; uint32_t InstanceId() const { @@ -383,36 +431,25 @@ } bool Playing() const { - return _playing; + return channel_state_.Get().playing; } bool Sending() const { - // A lock is needed because |_sending| is accessed by both - // TransmitMixer::PrepareDemux() and StartSend()/StopSend(), which - // are called by different threads. - CriticalSectionScoped cs(&_callbackCritSect); - return _sending; + return channel_state_.Get().sending; } bool Receiving() const { - return _receiving; + return channel_state_.Get().receiving; } bool ExternalTransport() const { + CriticalSectionScoped cs(&_callbackCritSect); return _externalTransport; } bool ExternalMixing() const { return _externalMixing; } - bool OutputIsOnHold() const - { - return _outputIsOnHold; - } - bool InputIsOnHold() const - { - return _inputIsOnHold; - } RtpRtcp* RtpRtcpModulePtr() const { return _rtpRtcpModule.get(); @@ -432,6 +469,11 @@ uint32_t PrepareEncodeAndSend(int mixingFrequency); uint32_t EncodeAndSend(); + // From BitrateObserver (called by the RTP/RTCP module). + void OnNetworkChanged(const uint32_t bitrate_bps, + const uint8_t fraction_lost, // 0 - 255. + const uint32_t rtt); + private: bool ReceivePacket(const uint8_t* packet, int packet_length, const RTPHeader& header, bool in_order); @@ -444,13 +486,17 @@ int InsertInbandDtmfTone(); int32_t MixOrReplaceAudioWithFile(int mixingFrequency); int32_t MixAudioWithFile(AudioFrame& audioFrame, int mixingFrequency); - void UpdateDeadOrAliveCounters(bool alive); int32_t SendPacketRaw(const void *data, int len, bool RTCP); void UpdatePacketDelay(uint32_t timestamp, uint16_t sequenceNumber); void RegisterReceiveCodecsToRTPModule(); int SetRedPayloadType(int red_payload_type); + int SetSendRtpHeaderExtension(bool enable, RTPExtensionType type, + unsigned char id); + + int32_t GetPlayoutFrequency(); + int GetRTT() const; CriticalSectionWrapper& _fileCritSect; CriticalSectionWrapper& _callbackCritSect; @@ -458,6 +504,8 @@ uint32_t _instanceId; int32_t _channelId; + ChannelState channel_state_; + scoped_ptr rtp_header_parser_; scoped_ptr rtp_payload_registry_; scoped_ptr rtp_receive_statistics_; @@ -471,32 +519,26 @@ AudioLevel _outputAudioLevel; bool _externalTransport; AudioFrame _audioFrame; - scoped_array mono_recording_audio_; - // Resampler is used when input data is stereo while codec is mono. - PushResampler input_resampler_; - uint8_t _audioLevel_dBov; + scoped_ptr mono_recording_audio_; + // Downsamples to the codec rate if necessary. + PushResampler input_resampler_; FilePlayer* _inputFilePlayerPtr; FilePlayer* _outputFilePlayerPtr; FileRecorder* _outputFileRecorderPtr; int _inputFilePlayerId; int _outputFilePlayerId; int _outputFileRecorderId; - bool _inputFilePlaying; - bool _outputFilePlaying; bool _outputFileRecording; DtmfInbandQueue _inbandDtmfQueue; DtmfInband _inbandDtmfGenerator; - bool _inputExternalMedia; bool _outputExternalMedia; VoEMediaProcess* _inputExternalMediaCallbackPtr; VoEMediaProcess* _outputExternalMediaCallbackPtr; - uint8_t* _encryptionRTPBufferPtr; - uint8_t* _decryptionRTPBufferPtr; - uint8_t* _encryptionRTCPBufferPtr; - uint8_t* _decryptionRTCPBufferPtr; uint32_t _timeStamp; uint8_t _sendTelephoneEventPayloadType; + RemoteNtpTimeEstimator ntp_estimator_ GUARDED_BY(ts_stats_lock_); + // Timestamp of the audio pulled from NetEq. uint32_t jitter_buffer_playout_timestamp_; uint32_t playout_timestamp_rtp_; @@ -506,6 +548,15 @@ uint16_t send_sequence_number_; uint8_t restored_packet_[kVoiceEngineMaxIpPacketSizeBytes]; + scoped_ptr ts_stats_lock_; + + scoped_ptr rtp_ts_wraparound_handler_; + // The rtp timestamp of the first played out audio frame. + int64_t capture_start_rtp_time_stamp_; + // The capture ntp time (in local timebase) of the first played out audio + // frame. + int64_t capture_start_ntp_time_ms_ GUARDED_BY(ts_stats_lock_); + // uses Statistics* _engineStatisticsPtr; OutputMixer* _outputMixerPtr; @@ -515,53 +566,32 @@ VoiceEngineObserver* _voiceEngineObserverPtr; // owned by base CriticalSectionWrapper* _callbackCritSectPtr; // owned by base Transport* _transportPtr; // WebRtc socket or external transport - Encryption* _encryptionPtr; // WebRtc SRTP or external encryption - scoped_ptr rtp_audioproc_; + RMSLevel rms_level_; scoped_ptr rx_audioproc_; // far end AudioProcessing VoERxVadCallback* _rxVadObserverPtr; int32_t _oldVadDecision; int32_t _sendFrameType; // Send data is voice, 1-voice, 0-otherwise - VoERTPObserver* _rtpObserverPtr; VoERTCPObserver* _rtcpObserverPtr; // VoEBase - bool _outputIsOnHold; - bool _externalPlayout; bool _externalMixing; - bool _inputIsOnHold; - bool _playing; - bool _sending; - bool _receiving; bool _mixFileWithMicrophone; - bool _rtpObserver; bool _rtcpObserver; // VoEVolumeControl bool _mute; float _panLeft; float _panRight; float _outputGain; - // VoEEncryption - bool _encrypting; - bool _decrypting; // VoEDtmf bool _playOutbandDtmfEvent; bool _playInbandDtmfEvent; // VoeRTP_RTCP - uint8_t _extraPayloadType; - bool _insertExtraRTPPacket; - bool _extraMarkerBit; uint32_t _lastLocalTimeStamp; - uint32_t _lastRemoteTimeStamp; int8_t _lastPayloadType; bool _includeAudioLevelIndication; // VoENetwork - bool _rtpPacketTimedOut; - bool _rtpPacketTimeOutIsEnabled; - uint32_t _rtpTimeOutSeconds; - bool _connectionObserver; - VoEConnectionObserver* _connectionObserverPtr; - uint32_t _countAliveDetections; - uint32_t _countDeadDetections; AudioFrame::SpeechType _outputSpeechType; + ViENetwork* vie_network_; + int video_channel_; // VoEVideoSync uint32_t _average_jitter_buffer_delay_us; int least_required_delay_ms_; @@ -570,13 +600,17 @@ int _current_sync_offset; // VoEAudioProcessing bool _RxVadDetection; - bool _rxApmIsEnabled; bool _rxAgcIsEnabled; bool _rxNsIsEnabled; bool restored_packet_in_use_; + // RtcpBandwidthObserver + scoped_ptr bitrate_controller_; + scoped_ptr rtcp_bandwidth_observer_; + scoped_ptr send_bitrate_observer_; + scoped_ptr network_predictor_; }; } // namespace voe } // namespace webrtc -#endif // WEBRTC_VOICE_ENGINE_CHANNEL_H +#endif // WEBRTC_VOICE_ENGINE_CHANNEL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/channel_manager.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/channel_manager.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/channel_manager.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/channel_manager.h 2015-02-03 14:33:38.000000000 +0000 @@ -13,8 +13,8 @@ #include +#include "webrtc/base/constructormagic.h" #include "webrtc/system_wrappers/interface/atomic32.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/typedefs.h" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/dtmf_inband_queue.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/dtmf_inband_queue.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/dtmf_inband_queue.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/dtmf_inband_queue.cc 2015-02-03 14:33:38.000000000 +0000 @@ -71,15 +71,17 @@ return nextDtmf; } -bool +bool DtmfInbandQueue::PendingDtmf() { - return(_nextEmptyIndex>0); + CriticalSectionScoped lock(&_DtmfCritsect); + return _nextEmptyIndex > 0; } -void +void DtmfInbandQueue::ResetDtmf() { + CriticalSectionScoped lock(&_DtmfCritsect); _nextEmptyIndex = 0; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/mock/fake_voe_external_media.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/mock/fake_voe_external_media.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/mock/fake_voe_external_media.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/mock/fake_voe_external_media.h 2015-02-03 14:33:38.000000000 +0000 @@ -56,7 +56,7 @@ int samples_per_channel, int sample_rate_hz, int num_channels) { const int length = samples_per_channel * num_channels; - scoped_array data; + scoped_ptr data; if (!audio) { data.reset(new int16_t[length]); memset(data.get(), 0, length * sizeof(data[0])); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/mock/mock_voe_connection_observer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/mock/mock_voe_connection_observer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/mock/mock_voe_connection_observer.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/mock/mock_voe_connection_observer.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MOCK_VOE_CONNECTION_OBSERVER_H_ -#define MOCK_VOE_CONNECTION_OBSERVER_H_ - -#include "webrtc/voice_engine/include/voe_network.h" - -#include "testing/gmock/include/gmock/gmock.h" - -namespace webrtc { - -class MockVoeConnectionObserver : public VoEConnectionObserver { - public: - MOCK_METHOD2(OnPeriodicDeadOrAlive, void(int channel, - bool alive)); -}; - -} - -#endif // MOCK_VOE_CONNECTION_OBSERVER_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_base.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_base.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_base.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_base.h 2015-02-03 14:33:38.000000000 +0000 @@ -85,8 +85,10 @@ // receives callbacks for generated trace messages. static int SetTraceCallback(TraceCallback* callback); +#if !defined(WEBRTC_CHROMIUM_BUILD) static int SetAndroidObjects(void* javaVM, void* context); static int SetAndroidObjects(void* javaVM, void* env, void* context); +#endif protected: VoiceEngine() {} @@ -171,24 +173,16 @@ // Gets the last VoiceEngine error code. virtual int LastError() = 0; - // Stops or resumes playout and transmission on a temporary basis. - virtual int SetOnHoldStatus(int channel, bool enable, - OnHoldModes mode = kHoldSendAndPlay) = 0; - - // Gets the current playout and transmission status. - virtual int GetOnHoldStatus(int channel, bool& enabled, - OnHoldModes& mode) = 0; - - // Sets the NetEQ playout mode for a specified |channel| number. - virtual int SetNetEQPlayoutMode(int channel, NetEqModes mode) = 0; - - // Gets the NetEQ playout mode for a specified |channel| number. - virtual int GetNetEQPlayoutMode(int channel, NetEqModes& mode) = 0; - // TODO(xians): Make the interface pure virtual after libjingle // implements the interface in its FakeWebRtcVoiceEngine. virtual AudioTransport* audio_transport() { return NULL; } + // To be removed. Don't use. + virtual int SetOnHoldStatus(int channel, bool enable, + OnHoldModes mode = kHoldSendAndPlay) { return -1; } + virtual int GetOnHoldStatus(int channel, bool& enabled, + OnHoldModes& mode) { return -1; } + protected: VoEBase() {} virtual ~VoEBase() {} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_call_report.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_call_report.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_call_report.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_call_report.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// This sub-API supports the following functionalities: -// -// - Long-term speech and noise level metrics. -// - Long-term echo metric statistics. -// - Round Trip Time (RTT) statistics. -// - Dead-or-Alive connection summary. -// - Generation of call reports to text files. -// -// Usage example, omitting error checking: -// -// using namespace webrtc; -// VoiceEngine* voe = VoiceEngine::Create(); -// VoEBase* base = VoEBase::GetInterface(voe); -// VoECallReport report = VoECallReport::GetInterface(voe); -// base->Init(); -// LevelStatistics stats; -// report->GetSpeechAndNoiseSummary(stats); -// ... -// base->Terminate(); -// base->Release(); -// report->Release(); -// VoiceEngine::Delete(voe); -// -#ifndef WEBRTC_VOICE_ENGINE_VOE_CALL_REPORT_H -#define WEBRTC_VOICE_ENGINE_VOE_CALL_REPORT_H - -#include "webrtc/common_types.h" - -namespace webrtc { - -class VoiceEngine; - -// VoECallReport -class WEBRTC_DLLEXPORT VoECallReport -{ -public: - // Factory for the VoECallReport sub-API. Increases an internal - // reference counter if successful. Returns NULL if the API is not - // supported or if construction fails. - static VoECallReport* GetInterface(VoiceEngine* voiceEngine); - - // Releases the VoECallReport sub-API and decreases an internal - // reference counter. Returns the new reference count. This value should - // be zero for all sub-API:s before the VoiceEngine object can be safely - // deleted. - virtual int Release() = 0; - - // Performs a combined reset of all components involved in generating - // the call report for a specified |channel|. Pass in -1 to reset - // all channels. - virtual int ResetCallReportStatistics(int channel) = 0; - - // Gets minimum, maximum and average levels for long-term echo metrics. - virtual int GetEchoMetricSummary(EchoStatistics& stats) = 0; - - // Gets minimum, maximum and average levels for Round Trip Time (RTT) - // measurements. - virtual int GetRoundTripTimeSummary(int channel, - StatVal& delaysMs) = 0; - - // Gets the total amount of dead and alive connection detections - // during a VoIP session. - virtual int GetDeadOrAliveSummary(int channel, int& numOfDeadDetections, - int& numOfAliveDetections) = 0; - - // Creates a text file in ASCII format, which contains a summary - // of all the statistics that can be obtained by the call report sub-API. - virtual int WriteReportToFile(const char* fileNameUTF8) = 0; - -protected: - VoECallReport() { } - virtual ~VoECallReport() { } -}; - -} // namespace webrtc - -#endif // WEBRTC_VOICE_ENGINE_VOE_CALL_REPORT_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_codec.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_codec.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_codec.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_codec.h 2015-02-03 14:33:38.000000000 +0000 @@ -84,22 +84,6 @@ // Gets the currently received |codec| for a specific |channel|. virtual int GetRecCodec(int channel, CodecInst& codec) = 0; - // Sets the initial values of target rate and frame size for iSAC - // for a specified |channel|. This API is only valid if iSAC is setup - // to run in channel-adaptive mode - virtual int SetISACInitTargetRate(int channel, int rateBps, - bool useFixedFrameSize = false) = 0; - - // Sets the maximum allowed iSAC rate which the codec may not exceed - // for a single packet for the specified |channel|. The maximum rate is - // defined as payload size per frame size in bits per second. - virtual int SetISACMaxRate(int channel, int rateBps) = 0; - - // Sets the maximum allowed iSAC payload size for a specified |channel|. - // The maximum value is set independently of the frame size, i.e. - // 30 ms and 60 ms packets have the same limit. - virtual int SetISACMaxPayloadSize(int channel, int sizeBytes) = 0; - // Sets the dynamic payload type number for a particular |codec| or // disables (ignores) a codec for receiving. For instance, when receiving // an invite from a SIP-based client, this function can be used to change @@ -118,6 +102,18 @@ virtual int SetSendCNPayloadType( int channel, int type, PayloadFrequencies frequency = kFreq16000Hz) = 0; + // Sets the codec internal FEC (forward error correction) status for a + // specified |channel|. Returns 0 if success, and -1 if failed. + // TODO(minyue): Make SetFECStatus() pure virtual when fakewebrtcvoiceengine + // in talk is ready. + virtual int SetFECStatus(int channel, bool enable) { return -1; } + + // Gets the codec internal FEC status for a specified |channel|. Returns 0 + // with the status stored in |enabled| if success, and -1 if encountered + // error. + // TODO(minyue): Make GetFECStatus() pure virtual when fakewebrtcvoiceengine + // in talk is ready. + virtual int GetFECStatus(int channel, bool& enabled) { return -1; } // Sets the VAD/DTX (silence suppression) status and |mode| for a // specified |channel|. Disabling VAD (through |enable|) will also disable @@ -130,17 +126,23 @@ virtual int GetVADStatus(int channel, bool& enabled, VadModes& mode, bool& disabledDTX) = 0; - // Not supported - virtual int SetAMREncFormat(int channel, AmrMode mode) = 0; - - // Not supported - virtual int SetAMRDecFormat(int channel, AmrMode mode) = 0; - - // Not supported - virtual int SetAMRWbEncFormat(int channel, AmrMode mode) = 0; - - // Not supported - virtual int SetAMRWbDecFormat(int channel, AmrMode mode) = 0; + // If send codec is Opus on a specified |channel|, sets the maximum playback + // rate the receiver will render: |frequency_hz| (in Hz). + // TODO(minyue): Make SetOpusMaxPlaybackRate() pure virtual when + // fakewebrtcvoiceengine in talk is ready. + virtual int SetOpusMaxPlaybackRate(int channel, int frequency_hz) { + return -1; + } + + // Don't use. To be removed. + virtual int SetAMREncFormat(int channel, AmrMode mode) { return -1; } + virtual int SetAMRDecFormat(int channel, AmrMode mode) { return -1; } + virtual int SetAMRWbEncFormat(int channel, AmrMode mode) { return -1; } + virtual int SetAMRWbDecFormat(int channel, AmrMode mode) { return -1; } + virtual int SetISACInitTargetRate(int channel, int rateBps, + bool useFixedFrameSize = false) { return -1; } + virtual int SetISACMaxRate(int channel, int rateBps) { return -1; } + virtual int SetISACMaxPayloadSize(int channel, int sizeBytes) { return -1; } protected: VoECodec() {} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_dtmf.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_dtmf.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_dtmf.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_dtmf.h 2015-02-03 14:33:38.000000000 +0000 @@ -43,7 +43,7 @@ class WEBRTC_DLLEXPORT VoEDtmf { public: - + // Factory for the VoEDtmf sub-API. Increases an internal // reference counter if successful. Returns NULL if the API is not // supported or if construction fails. @@ -60,24 +60,17 @@ bool outOfBand = true, int lengthMs = 160, int attenuationDb = 10) = 0; - + // Sets the dynamic payload |type| that should be used for telephone // events. virtual int SetSendTelephoneEventPayloadType(int channel, unsigned char type) = 0; - + // Gets the currently set dynamic payload |type| for telephone events. virtual int GetSendTelephoneEventPayloadType(int channel, unsigned char& type) = 0; - // Enables or disables local tone playout for received DTMF events - // out-of-band. - virtual int SetDtmfPlayoutStatus(int channel, bool enable) = 0; - - // Gets the DTMF playout status. - virtual int GetDtmfPlayoutStatus(int channel, bool& enabled) = 0; - // Toogles DTMF feedback state: when a DTMF tone is sent, the same tone // is played out on the speaker. virtual int SetDtmfFeedbackStatus(bool enable, @@ -90,14 +83,10 @@ virtual int PlayDtmfTone(int eventCode, int lengthMs = 200, int attenuationDb = 10) = 0; - // Starts playing out a DTMF feedback tone locally. - // The tone will be played out until the corresponding stop function - // is called. + // To be removed. Don't use. virtual int StartPlayingDtmfTone(int eventCode, - int attenuationDb = 10) = 0; - - // Stops playing out a DTMF feedback tone locally. - virtual int StopPlayingDtmfTone() = 0; + int attenuationDb = 10) { return -1; } + virtual int StopPlayingDtmfTone() { return -1; } protected: VoEDtmf() {} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_encryption.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_encryption.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_encryption.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_encryption.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// This sub-API supports the following functionalities: -// -// - External encryption and decryption. -// -// Usage example, omitting error checking: -// -// using namespace webrtc; -// VoiceEngine* voe = VoiceEngine::Create(); -// VoEEncryption* encrypt = VoEEncryption::GetInterface(voe); -// ... -// encrypt->Release(); -// VoiceEngine::Delete(voe); -// -#ifndef WEBRTC_VOICE_ENGINE_VOE_ENCRYPTION_H -#define WEBRTC_VOICE_ENGINE_VOE_ENCRYPTION_H - -#include "webrtc/common_types.h" - -namespace webrtc { - -class VoiceEngine; - -class WEBRTC_DLLEXPORT VoEEncryption -{ -public: - // Factory for the VoEEncryption sub-API. Increases an internal - // reference counter if successful. Returns NULL if the API is not - // supported or if construction fails. - static VoEEncryption* GetInterface(VoiceEngine* voiceEngine); - - // Releases the VoEEncryption sub-API and decreases an internal - // reference counter. Returns the new reference count. This value should - // be zero for all sub-API:s before the VoiceEngine object can be safely - // deleted. - virtual int Release() = 0; - - // Installs an Encryption instance and enables external encryption - // for the selected |channel|. - virtual int RegisterExternalEncryption( - int channel, Encryption& encryption) = 0; - - // Removes an Encryption instance and disables external encryption - // for the selected |channel|. - virtual int DeRegisterExternalEncryption(int channel) = 0; - -protected: - VoEEncryption() {} - virtual ~VoEEncryption() {} -}; - -} // namespace webrtc - -#endif // WEBRTC_VOICE_ENGINE_VOE_ENCRYPTION_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_errors.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_errors.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_errors.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_errors.h 2015-02-03 14:33:38.000000000 +0000 @@ -36,7 +36,7 @@ #define VE_DTMF_OUTOF_RANGE 8022 #define VE_INVALID_CHANNELS 8023 #define VE_SET_PLTYPE_FAILED 8024 -#define VE_ENCRYPT_NOT_INITED 8025 +// 8025 is not used #define VE_NOT_INITED 8026 #define VE_NOT_SENDING 8027 #define VE_EXT_TRANSPORT_NOT_SUPPORTED 8028 @@ -67,7 +67,7 @@ #define VE_SEND_ERROR 8092 #define VE_CANNOT_REMOVE_CONF_CHANNEL 8093 #define VE_PLTYPE_ERROR 8094 -#define VE_SET_FEC_FAILED 8095 +#define VE_SET_RED_FAILED 8095 #define VE_CANNOT_GET_PLAY_DATA 8096 #define VE_APM_ERROR 8097 #define VE_RUNTIME_PLAY_WARNING 8098 @@ -114,8 +114,8 @@ #define VE_RTP_KEEPALIVE_FAILED 9023 #define VE_SEND_DTMF_FAILED 9024 #define VE_CANNOT_RETRIEVE_CNAME 9025 -#define VE_DECRYPTION_FAILED 9026 -#define VE_ENCRYPTION_FAILED 9027 +// 9026 is not used +// 9027 is not used #define VE_CANNOT_RETRIEVE_RTP_STAT 9028 #define VE_GQOS_ERROR 9029 #define VE_BINDING_SOCKET_TO_LOCAL_ADDRESS_FAILED 9030 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_external_media.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_external_media.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_external_media.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_external_media.h 2015-02-03 14:33:38.000000000 +0000 @@ -108,8 +108,8 @@ // This function gets audio for an external playout sink. // During transmission, this function should be called every ~10 ms // to obtain a new 10 ms frame of audio. The length of the block will - // be 160, 320, 441 or 480 samples (for 16000, 32000, 44100 or 48000 - // kHz sampling rates respectively). + // be 160, 320, 441 or 480 samples (for 16000, 32000, 44100 or + // 48000 kHz sampling rates respectively). virtual int ExternalPlayoutGetData( int16_t speechData10ms[], int samplingFreqHz, int current_delay_ms, int& lengthSamples) = 0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_file.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_file.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_file.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_file.h 2015-02-03 14:33:38.000000000 +0000 @@ -84,9 +84,6 @@ // Returns the current file playing state for a specific |channel|. virtual int IsPlayingFileLocally(int channel) = 0; - // Sets the volume scaling for a speaker file that is already playing. - virtual int ScaleLocalFilePlayout(int channel, float scale) = 0; - // Starts reading data from a file and transmits the data either // mixed with or instead of the microphone signal. virtual int StartPlayingFileAsMicrophone( @@ -112,9 +109,6 @@ // Returns whether the |channel| is currently playing a file as microphone. virtual int IsPlayingFileAsMicrophone(int channel) = 0; - // Sets the volume scaling for a microphone file that is already playing. - virtual int ScaleFileAsMicrophonePlayout(int channel, float scale) = 0; - // Starts recording the mixed playout audio. virtual int StartRecordingPlayout(int channel, const char* fileNameUTF8, @@ -140,40 +134,31 @@ // Stops recording the microphone signal. virtual int StopRecordingMicrophone() = 0; - - // Gets the duration of a file. + // Don't use. To be removed. + virtual int ScaleLocalFilePlayout(int channel, float scale) { return -1; } + virtual int ScaleFileAsMicrophonePlayout( + int channel, float scale) { return -1; } virtual int GetFileDuration(const char* fileNameUTF8, int& durationMs, - FileFormats format = kFileFormatPcm16kHzFile) = 0; - - // Gets the current played position of a file on a specific |channel|. - virtual int GetPlaybackPosition(int channel, int& positionMs) = 0; - + FileFormats format = kFileFormatPcm16kHzFile) { return -1; } + virtual int GetPlaybackPosition(int channel, int& positionMs) { return -1; } virtual int ConvertPCMToWAV(const char* fileNameInUTF8, - const char* fileNameOutUTF8) = 0; - + const char* fileNameOutUTF8) { return -1; } virtual int ConvertPCMToWAV(InStream* streamIn, - OutStream* streamOut) = 0; - + OutStream* streamOut) { return -1; } virtual int ConvertWAVToPCM(const char* fileNameInUTF8, - const char* fileNameOutUTF8) = 0; - + const char* fileNameOutUTF8) { return -1; } virtual int ConvertWAVToPCM(InStream* streamIn, - OutStream* streamOut) = 0; - + OutStream* streamOut) { return -1; } virtual int ConvertPCMToCompressed(const char* fileNameInUTF8, const char* fileNameOutUTF8, - CodecInst* compression) = 0; - + CodecInst* compression) { return -1; } virtual int ConvertPCMToCompressed(InStream* streamIn, OutStream* streamOut, - CodecInst* compression) = 0; - + CodecInst* compression) { return -1; } virtual int ConvertCompressedToPCM(const char* fileNameInUTF8, - const char* fileNameOutUTF8) = 0; - + const char* fileNameOutUTF8) { return -1; } virtual int ConvertCompressedToPCM(InStream* streamIn, - OutStream* streamOut) = 0; - + OutStream* streamOut) { return -1; } protected: VoEFile() {} virtual ~VoEFile() {} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_hardware.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_hardware.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_hardware.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_hardware.h 2015-02-03 14:33:38.000000000 +0000 @@ -131,6 +131,7 @@ virtual int EnableBuiltInAEC(bool enable) = 0; virtual bool BuiltInAECIsEnabled() const = 0; + protected: VoEHardware() {} virtual ~VoEHardware() {} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_network.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_network.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_network.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_network.h 2015-02-03 14:33:38.000000000 +0000 @@ -40,19 +40,6 @@ class VoiceEngine; -// VoEConnectionObserver -class WEBRTC_DLLEXPORT VoEConnectionObserver -{ -public: - // This method will be called peridically and deliver dead-or-alive - // notifications for a specified |channel| when the observer interface - // has been installed and activated. - virtual void OnPeriodicDeadOrAlive(int channel, bool alive) = 0; - -protected: - virtual ~VoEConnectionObserver() {} -}; - // VoENetwork class WEBRTC_DLLEXPORT VoENetwork { @@ -80,8 +67,15 @@ // The packets received from the network should be passed to this // function when external transport is enabled. Note that the data // including the RTP-header must also be given to the VoiceEngine. - virtual int ReceivedRTPPacket( - int channel, const void* data, unsigned int length) = 0; + virtual int ReceivedRTPPacket(int channel, + const void* data, + unsigned int length) = 0; + virtual int ReceivedRTPPacket(int channel, + const void* data, + unsigned int length, + const PacketTime& packet_time) { + return 0; + } // The packets received from the network should be passed to this // function when external transport is enabled. Note that the data diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_rtp_rtcp.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_rtp_rtcp.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_rtp_rtcp.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_rtp_rtcp.h 2015-02-03 14:33:38.000000000 +0000 @@ -15,10 +15,9 @@ // - Transmission of RTCP sender reports. // - Obtaining RTCP data from incoming RTCP sender reports. // - RTP and RTCP statistics (jitter, packet loss, RTT etc.). -// - Forward Error Correction (FEC). +// - Redundant Coding (RED) // - Writing RTP and RTCP packets to binary files for off-line analysis of // the call quality. -// - Inserting extra RTP packets into active audio stream. // // Usage example, omitting error checking: // @@ -45,6 +44,7 @@ namespace webrtc { +class ViENetwork; class VoiceEngine; // VoERTPObserver @@ -86,6 +86,9 @@ int packetsSent; int bytesReceived; int packetsReceived; + // The capture ntp time (in local timebase) of the first played out audio + // frame. + int64_t capture_start_ntp_time_ms_; }; // See section 6.4.1 in http://www.ietf.org/rfc/rfc3550.txt for details. @@ -125,24 +128,6 @@ // deleted. virtual int Release() = 0; - // Registers an instance of a VoERTPObserver derived class for a specified - // |channel|. It will allow the user to observe callbacks related to the - // RTP protocol such as changes in the incoming SSRC. - virtual int RegisterRTPObserver(int channel, VoERTPObserver& observer) = 0; - - // Deregisters an instance of a VoERTPObserver derived class for a - // specified |channel|. - virtual int DeRegisterRTPObserver(int channel) = 0; - - // Registers an instance of a VoERTCPObserver derived class for a specified - // |channel|. - virtual int RegisterRTCPObserver( - int channel, VoERTCPObserver& observer) = 0; - - // Deregisters an instance of a VoERTCPObserver derived class for a - // specified |channel|. - virtual int DeRegisterRTCPObserver(int channel) = 0; - // Sets the local RTP synchronization source identifier (SSRC) explicitly. virtual int SetLocalSSRC(int channel, unsigned int ssrc) = 0; @@ -153,15 +138,28 @@ virtual int GetRemoteSSRC(int channel, unsigned int& ssrc) = 0; // Sets the status of rtp-audio-level-indication on a specific |channel|. - virtual int SetRTPAudioLevelIndicationStatus( - int channel, bool enable, unsigned char ID = 1) = 0; - - // Sets the status of rtp-audio-level-indication on a specific |channel|. - virtual int GetRTPAudioLevelIndicationStatus( - int channel, bool& enabled, unsigned char& ID) = 0; + virtual int SetSendAudioLevelIndicationStatus(int channel, + bool enable, + unsigned char id = 1) = 0; - // Gets the CSRCs of the incoming RTP packets. - virtual int GetRemoteCSRCs(int channel, unsigned int arrCSRC[15]) = 0; + // Sets the status of receiving rtp-audio-level-indication on a specific + // |channel|. + virtual int SetReceiveAudioLevelIndicationStatus(int channel, + bool enable, + unsigned char id = 1) { + // TODO(wu): Remove default implementation once talk is updated. + return 0; + } + + // Sets the status of sending absolute sender time on a specific |channel|. + virtual int SetSendAbsoluteSenderTimeStatus(int channel, + bool enable, + unsigned char id) = 0; + + // Sets status of receiving absolute sender time on a specific |channel|. + virtual int SetReceiveAbsoluteSenderTimeStatus(int channel, + bool enable, + unsigned char id) = 0; // Sets the RTCP status on a specific |channel|. virtual int SetRTCPStatus(int channel, bool enable) = 0; @@ -173,9 +171,11 @@ // specific |channel|. virtual int SetRTCP_CNAME(int channel, const char cName[256]) = 0; - // Gets the canonical name (CNAME) parameter for RTCP reports on a - // specific |channel|. - virtual int GetRTCP_CNAME(int channel, char cName[256]) = 0; + // TODO(holmer): Remove this API once it has been removed from + // fakewebrtcvoiceengine.h. + virtual int GetRTCP_CNAME(int channel, char cName[256]) { + return -1; + } // Gets the canonical name (CNAME) parameter for incoming RTCP reports // on a specific channel. @@ -197,11 +197,6 @@ // Gets RTCP statistics for a specific |channel|. virtual int GetRTCPStatistics(int channel, CallStatistics& stats) = 0; - // Gets the sender info part of the last received RTCP Sender Report (SR) - // on a specified |channel|. - virtual int GetRemoteRTCPSenderInfo( - int channel, SenderInfo* sender_info) = 0; - // Gets the report block parts of the last received RTCP Sender Report (SR), // or RTCP Receiver Report (RR) on a specified |channel|. Each vector // element also contains the SSRC of the sender in addition to a report @@ -209,18 +204,33 @@ virtual int GetRemoteRTCPReportBlocks( int channel, std::vector* receive_blocks) = 0; - // Sends an RTCP APP packet on a specific |channel|. - virtual int SendApplicationDefinedRTCPPacket( - int channel, unsigned char subType, unsigned int name, - const char* data, unsigned short dataLengthInBytes) = 0; + // Sets the Redundant Coding (RED) status on a specific |channel|. + // TODO(minyue): Make SetREDStatus() pure virtual when fakewebrtcvoiceengine + // in talk is ready. + virtual int SetREDStatus( + int channel, bool enable, int redPayloadtype = -1) { return -1; } + + // Gets the RED status on a specific |channel|. + // TODO(minyue): Make GetREDStatus() pure virtual when fakewebrtcvoiceengine + // in talk is ready. + virtual int GetREDStatus( + int channel, bool& enabled, int& redPayloadtype) { return -1; } // Sets the Forward Error Correction (FEC) status on a specific |channel|. + // TODO(minyue): Remove SetFECStatus() when SetFECStatus() is replaced by + // SetREDStatus() in fakewebrtcvoiceengine. virtual int SetFECStatus( - int channel, bool enable, int redPayloadtype = -1) = 0; + int channel, bool enable, int redPayloadtype = -1) { + return SetREDStatus(channel, enable, redPayloadtype); + }; // Gets the FEC status on a specific |channel|. + // TODO(minyue): Remove GetFECStatus() when GetFECStatus() is replaced by + // GetREDStatus() in fakewebrtcvoiceengine. virtual int GetFECStatus( - int channel, bool& enabled, int& redPayloadtype) = 0; + int channel, bool& enabled, int& redPayloadtype) { + return SetREDStatus(channel, enabled, redPayloadtype); + } // This function enables Negative Acknowledgment (NACK) using RTCP, // implemented based on RFC 4585. NACK retransmits RTP packets if lost on @@ -248,16 +258,33 @@ virtual int RTPDumpIsActive( int channel, RTPDirections direction = kRtpIncoming) = 0; - // Sends an extra RTP packet using an existing/active RTP session. - // It is possible to set the payload type, marker bit and payload - // of the extra RTP + // Sets video engine channel to receive incoming audio packets for + // aggregated bandwidth estimation. Takes ownership of the ViENetwork + // interface. + virtual int SetVideoEngineBWETarget(int channel, ViENetwork* vie_network, + int video_channel) { + return 0; + } + + // Will be removed. Don't use. + virtual int RegisterRTPObserver(int channel, + VoERTPObserver& observer) { return -1; }; + virtual int DeRegisterRTPObserver(int channel) { return -1; }; + virtual int RegisterRTCPObserver( + int channel, VoERTCPObserver& observer) { return -1; }; + virtual int DeRegisterRTCPObserver(int channel) { return -1; }; + virtual int GetRemoteCSRCs(int channel, + unsigned int arrCSRC[15]) { return -1; }; virtual int InsertExtraRTPPacket( - int channel, unsigned char payloadType, bool markerBit, - const char* payloadData, unsigned short payloadSize) = 0; - - // Gets the timestamp of the last RTP packet received by |channel|. + int channel, unsigned char payloadType, bool markerBit, + const char* payloadData, unsigned short payloadSize) { return -1; }; + virtual int GetRemoteRTCPSenderInfo( + int channel, SenderInfo* sender_info) { return -1; }; + virtual int SendApplicationDefinedRTCPPacket( + int channel, unsigned char subType, unsigned int name, + const char* data, unsigned short dataLengthInBytes) { return -1; }; virtual int GetLastRemoteTimeStamp(int channel, - uint32_t* lastRemoteTimeStamp) = 0; + uint32_t* lastRemoteTimeStamp) { return -1; }; protected: VoERTP_RTCP() {} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_volume_control.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_volume_control.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_volume_control.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/include/voe_volume_control.h 2015-02-03 14:33:38.000000000 +0000 @@ -62,12 +62,6 @@ // Gets the speaker |volume| level. virtual int GetSpeakerVolume(unsigned int& volume) = 0; - // Mutes the speaker device completely in the operating system. - virtual int SetSystemOutputMute(bool enable) = 0; - - // Gets the output device mute state in the operating system. - virtual int GetSystemOutputMute(bool &enabled) = 0; - // Sets the microphone volume level. Valid range is [0,255]. virtual int SetMicVolume(unsigned int volume) = 0; @@ -81,12 +75,6 @@ // Gets the current microphone input mute state. virtual int GetInputMute(int channel, bool& enabled) = 0; - // Mutes the microphone device completely in the operating system. - virtual int SetSystemInputMute(bool enable) = 0; - - // Gets the mute state of the input device in the operating system. - virtual int GetSystemInputMute(bool& enabled) = 0; - // Gets the microphone speech |level|, mapped non-linearly to the range // [0,9]. virtual int GetSpeechInputLevel(unsigned int& level) = 0; @@ -117,6 +105,12 @@ // Gets the current left and right scaling factors. virtual int GetOutputVolumePan(int channel, float& left, float& right) = 0; + // Don't use. Will be removed. + virtual int SetSystemOutputMute(bool enable) { return -1; } + virtual int GetSystemOutputMute(bool &enabled) { return -1; } + virtual int SetSystemInputMute(bool enable) { return -1; } + virtual int GetSystemInputMute(bool& enabled) { return -1; } + protected: VoEVolumeControl() {} virtual ~VoEVolumeControl() {} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/monitor_module.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/monitor_module.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/monitor_module.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/monitor_module.cc 2015-02-03 14:33:38.000000000 +0000 @@ -53,14 +53,6 @@ } int32_t -MonitorModule::Version(char* version, - uint32_t& remainingBufferInBytes, - uint32_t& position) const -{ - return 0; -} - -int32_t MonitorModule::ChangeUniqueId(int32_t id) { return 0; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/monitor_module.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/monitor_module.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/monitor_module.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/monitor_module.h 2015-02-03 14:33:38.000000000 +0000 @@ -40,15 +40,11 @@ virtual ~MonitorModule(); public: // module - int32_t Version(char* version, - uint32_t& remainingBufferInBytes, - uint32_t& position) const; + virtual int32_t ChangeUniqueId(int32_t id) OVERRIDE; - int32_t ChangeUniqueId(int32_t id); + virtual int32_t TimeUntilNextProcess() OVERRIDE; - int32_t TimeUntilNextProcess(); - - int32_t Process(); + virtual int32_t Process() OVERRIDE; private: enum { kAverageProcessUpdateTimeMs = 1000 }; MonitorObserver* _observerPtr; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/network_predictor.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/network_predictor.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/network_predictor.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/network_predictor.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/voice_engine/network_predictor.h" + +namespace webrtc { +namespace voe { + +NetworkPredictor::NetworkPredictor(Clock* clock) + : clock_(clock), + last_loss_rate_update_time_ms_(clock_->TimeInMilliseconds()), + loss_rate_filter_(new rtc::ExpFilter(0.9999f)) { +} + +void NetworkPredictor::UpdatePacketLossRate(uint8_t loss_rate) { + int64_t now_ms = clock_->TimeInMilliseconds(); + // Update the recursive average filter. + loss_rate_filter_->Apply( + static_cast(now_ms - last_loss_rate_update_time_ms_), + static_cast(loss_rate)); + last_loss_rate_update_time_ms_ = now_ms; +} + +uint8_t NetworkPredictor::GetLossRate() { + float value = loss_rate_filter_->filtered(); + return (value == rtc::ExpFilter::kValueUndefined) ? 0 : + static_cast(value + 0.5); +} +} // namespace voe +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/network_predictor.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/network_predictor.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/network_predictor.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/network_predictor.h 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_VOICE_ENGINE_NETWORK_PREDICTOR_H_ +#define WEBRTC_VOICE_ENGINE_NETWORK_PREDICTOR_H_ + +#include "webrtc/base/exp_filter.h" +#include "webrtc/system_wrappers/interface/clock.h" + +namespace webrtc { + +namespace voe { + +// NetworkPredictor is to predict network conditions e.g., packet loss rate, for +// sender and/or receiver to cope with changes in the network condition. +class NetworkPredictor { + public: + explicit NetworkPredictor(Clock* clock); + ~NetworkPredictor() {} + + // Gets the predicted packet loss rate. + uint8_t GetLossRate(); + + // Updates the packet loss rate predictor, on receiving a new observation of + // packet loss rate from past. Input packet loss rate should be in the + // interval [0, 255]. + void UpdatePacketLossRate(uint8_t loss_rate); + + private: + Clock* clock_; + int64_t last_loss_rate_update_time_ms_; + + // An exponential filter is used to predict packet loss rate. + scoped_ptr loss_rate_filter_; +}; + +} // namespace voe +} // namespace webrtc +#endif // WEBRTC_VOICE_ENGINE_NETWORK_PREDICTOR_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/network_predictor_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/network_predictor_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/network_predictor_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/network_predictor_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/voice_engine/network_predictor.h" +#include "webrtc/system_wrappers/interface/clock.h" + +namespace webrtc { +namespace voe { + +class TestNetworkPredictor : public ::testing::Test { + protected: + TestNetworkPredictor() + : clock_(0), + network_predictor_(new NetworkPredictor(&clock_)) {} + SimulatedClock clock_; + scoped_ptr network_predictor_; +}; + +TEST_F(TestNetworkPredictor, TestPacketLossRateFilter) { + // Test initial packet loss rate estimate is 0. + EXPECT_EQ(0, network_predictor_->GetLossRate()); + network_predictor_->UpdatePacketLossRate(32); + // First time, no filtering. + EXPECT_EQ(32, network_predictor_->GetLossRate()); + clock_.AdvanceTimeMilliseconds(1000); + network_predictor_->UpdatePacketLossRate(40); + float exp = pow(0.9999f, 1000); + float value = 32.0f * exp + (1 - exp) * 40.0f; + EXPECT_EQ(static_cast(value + 0.5f), + network_predictor_->GetLossRate()); +} +} // namespace voe +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer.cc 2015-02-03 14:33:38.000000000 +0000 @@ -16,11 +16,10 @@ #include "webrtc/system_wrappers/interface/file_wrapper.h" #include "webrtc/system_wrappers/interface/trace.h" #include "webrtc/voice_engine/include/voe_external_media.h" -#include "webrtc/voice_engine/output_mixer_internal.h" #include "webrtc/voice_engine/statistics.h" +#include "webrtc/voice_engine/utility.h" namespace webrtc { - namespace voe { void @@ -237,29 +236,6 @@ return 0; } -int OutputMixer::StartPlayingDtmfTone(uint8_t eventCode, - int attenuationDb) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1), - "OutputMixer::StartPlayingDtmfTone()"); - if (_dtmfGenerator.StartTone(eventCode, attenuationDb) != 0) - { - _engineStatisticsPtr->SetLastError( - VE_STILL_PLAYING_PREV_DTMF, - kTraceError, - "OutputMixer::StartPlayingDtmfTone())"); - return -1; - } - return 0; -} - -int OutputMixer::StopPlayingDtmfTone() -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1), - "OutputMixer::StopPlayingDtmfTone()"); - return (_dtmfGenerator.StopTone()); -} - int32_t OutputMixer::SetMixabilityStatus(MixerParticipant& participant, bool mixable) @@ -528,11 +504,12 @@ frame->sample_rate_hz_ = sample_rate_hz; // TODO(andrew): Ideally the downmixing would occur much earlier, in // AudioCodingModule. - return RemixAndResample(_audioFrame, &resampler_, frame); + RemixAndResample(_audioFrame, &resampler_, frame); + return 0; } int32_t -OutputMixer::DoOperationsOnCombinedSignal() +OutputMixer::DoOperationsOnCombinedSignal(bool feed_data_to_apm) { if (_audioFrame.sample_rate_hz_ != _mixingFrequencyHz) { @@ -565,24 +542,26 @@ } // --- Far-end Voice Quality Enhancement (AudioProcessing Module) - - APMAnalyzeReverseStream(_audioFrame); + if (feed_data_to_apm) { + APMAnalyzeReverseStream(_audioFrame); + } // --- External media processing - - if (_externalMedia) { CriticalSectionScoped cs(&_callbackCritSect); - const bool isStereo = (_audioFrame.num_channels_ == 2); - if (_externalMediaCallbackPtr) + if (_externalMedia) { - _externalMediaCallbackPtr->Process( - -1, - kPlaybackAllChannelsMixed, - (int16_t*)_audioFrame.data_, - _audioFrame.samples_per_channel_, - _audioFrame.sample_rate_hz_, - isStereo); + const bool is_stereo = (_audioFrame.num_channels_ == 2); + if (_externalMediaCallbackPtr) + { + _externalMediaCallbackPtr->Process( + -1, + kPlaybackAllChannelsMixed, + (int16_t*)_audioFrame.data_, + _audioFrame.samples_per_channel_, + _audioFrame.sample_rate_hz_, + is_stereo); + } } } @@ -597,9 +576,8 @@ // side. Downmix to mono. AudioFrame frame; frame.num_channels_ = 1; - frame.sample_rate_hz_ = _audioProcessingModulePtr->sample_rate_hz(); - if (RemixAndResample(audioFrame, &audioproc_resampler_, &frame) == -1) - return; + frame.sample_rate_hz_ = _audioProcessingModulePtr->input_sample_rate_hz(); + RemixAndResample(audioFrame, &audioproc_resampler_, &frame); if (_audioProcessingModulePtr->AnalyzeReverseStream(&frame) == -1) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1), @@ -656,5 +634,4 @@ } } // namespace voe - } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer.h 2015-02-03 14:33:38.000000000 +0000 @@ -54,13 +54,9 @@ // VoEDtmf int PlayDtmfTone(uint8_t eventCode, int lengthMs, int attenuationDb); - int StartPlayingDtmfTone(uint8_t eventCode, int attenuationDb); - - int StopPlayingDtmfTone(); - int32_t MixActiveChannels(); - int32_t DoOperationsOnCombinedSignal(); + int32_t DoOperationsOnCombinedSignal(bool feed_data_to_apm); int32_t SetMixabilityStatus(MixerParticipant& participant, bool mixable); @@ -135,8 +131,10 @@ CriticalSectionWrapper& _fileCritSect; AudioConferenceMixer& _mixerModule; AudioFrame _audioFrame; - PushResampler resampler_; // converts mixed audio to fit ADM format - PushResampler audioproc_resampler_; // converts mixed audio to fit APM rate + // Converts mixed audio to the audio device output rate. + PushResampler resampler_; + // Converts mixed audio to the audio processing rate. + PushResampler audioproc_resampler_; AudioLevel _audioLevel; // measures audio level for the combined signal DtmfInband _dtmfGenerator; int _instanceId; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer_internal.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer_internal.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer_internal.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer_internal.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/voice_engine/output_mixer_internal.h" - -#include "webrtc/common_audio/resampler/include/push_resampler.h" -#include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/modules/utility/interface/audio_frame_operations.h" -#include "webrtc/system_wrappers/interface/logging.h" -#include "webrtc/system_wrappers/interface/trace.h" - -namespace webrtc { -namespace voe { - -int RemixAndResample(const AudioFrame& src_frame, - PushResampler* resampler, - AudioFrame* dst_frame) { - const int16_t* audio_ptr = src_frame.data_; - int audio_ptr_num_channels = src_frame.num_channels_; - int16_t mono_audio[AudioFrame::kMaxDataSizeSamples]; - - // Downmix before resampling. - if (src_frame.num_channels_ == 2 && dst_frame->num_channels_ == 1) { - AudioFrameOperations::StereoToMono(src_frame.data_, - src_frame.samples_per_channel_, - mono_audio); - audio_ptr = mono_audio; - audio_ptr_num_channels = 1; - } - - if (resampler->InitializeIfNeeded(src_frame.sample_rate_hz_, - dst_frame->sample_rate_hz_, - audio_ptr_num_channels) == -1) { - dst_frame->CopyFrom(src_frame); - LOG_FERR3(LS_ERROR, InitializeIfNeeded, src_frame.sample_rate_hz_, - dst_frame->sample_rate_hz_, audio_ptr_num_channels); - return -1; - } - - const int src_length = src_frame.samples_per_channel_ * - audio_ptr_num_channels; - int out_length = resampler->Resample(audio_ptr, src_length, dst_frame->data_, - AudioFrame::kMaxDataSizeSamples); - if (out_length == -1) { - dst_frame->CopyFrom(src_frame); - LOG_FERR3(LS_ERROR, Resample, src_length, dst_frame->data_, - AudioFrame::kMaxDataSizeSamples); - return -1; - } - dst_frame->samples_per_channel_ = out_length / audio_ptr_num_channels; - - // Upmix after resampling. - if (src_frame.num_channels_ == 1 && dst_frame->num_channels_ == 2) { - // The audio in dst_frame really is mono at this point; MonoToStereo will - // set this back to stereo. - dst_frame->num_channels_ = 1; - AudioFrameOperations::MonoToStereo(dst_frame); - } - return 0; -} - -} // namespace voe -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer_internal.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer_internal.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer_internal.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer_internal.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_VOICE_ENGINE_OUTPUT_MIXER_INTERNAL_H_ -#define WEBRTC_VOICE_ENGINE_OUTPUT_MIXER_INTERNAL_H_ - -namespace webrtc { - -class AudioFrame; -class PushResampler; - -namespace voe { - -// Upmix or downmix and resample the audio in |src_frame| to |dst_frame|. -// Expects |dst_frame| to have its |num_channels_| and |sample_rate_hz_| set to -// the desired values. Updates |samples_per_channel_| accordingly. -// -// On failure, returns -1 and copies |src_frame| to |dst_frame|. -int RemixAndResample(const AudioFrame& src_frame, - PushResampler* resampler, - AudioFrame* dst_frame); - -} // namespace voe -} // namespace webrtc - -#endif // VOICE_ENGINE_OUTPUT_MIXER_INTERNAL_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/output_mixer_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "testing/gtest/include/gtest/gtest.h" -#include "webrtc/voice_engine/output_mixer.h" -#include "webrtc/voice_engine/output_mixer_internal.h" - -namespace webrtc { -namespace voe { -namespace { - -class OutputMixerTest : public ::testing::Test { - protected: - OutputMixerTest() { - src_frame_.sample_rate_hz_ = 16000; - src_frame_.samples_per_channel_ = src_frame_.sample_rate_hz_ / 100; - src_frame_.num_channels_ = 1; - dst_frame_.CopyFrom(src_frame_); - golden_frame_.CopyFrom(src_frame_); - } - - void RunResampleTest(int src_channels, int src_sample_rate_hz, - int dst_channels, int dst_sample_rate_hz); - - PushResampler resampler_; - AudioFrame src_frame_; - AudioFrame dst_frame_; - AudioFrame golden_frame_; -}; - -// Sets the signal value to increase by |data| with every sample. Floats are -// used so non-integer values result in rounding error, but not an accumulating -// error. -void SetMonoFrame(AudioFrame* frame, float data, int sample_rate_hz) { - memset(frame->data_, 0, sizeof(frame->data_)); - frame->num_channels_ = 1; - frame->sample_rate_hz_ = sample_rate_hz; - frame->samples_per_channel_ = sample_rate_hz / 100; - for (int i = 0; i < frame->samples_per_channel_; i++) { - frame->data_[i] = data * i; - } -} - -// Keep the existing sample rate. -void SetMonoFrame(AudioFrame* frame, float data) { - SetMonoFrame(frame, data, frame->sample_rate_hz_); -} - -// Sets the signal value to increase by |left| and |right| with every sample in -// each channel respectively. -void SetStereoFrame(AudioFrame* frame, float left, float right, - int sample_rate_hz) { - memset(frame->data_, 0, sizeof(frame->data_)); - frame->num_channels_ = 2; - frame->sample_rate_hz_ = sample_rate_hz; - frame->samples_per_channel_ = sample_rate_hz / 100; - for (int i = 0; i < frame->samples_per_channel_; i++) { - frame->data_[i * 2] = left * i; - frame->data_[i * 2 + 1] = right * i; - } -} - -// Keep the existing sample rate. -void SetStereoFrame(AudioFrame* frame, float left, float right) { - SetStereoFrame(frame, left, right, frame->sample_rate_hz_); -} - -void VerifyParams(const AudioFrame& ref_frame, const AudioFrame& test_frame) { - EXPECT_EQ(ref_frame.num_channels_, test_frame.num_channels_); - EXPECT_EQ(ref_frame.samples_per_channel_, test_frame.samples_per_channel_); - EXPECT_EQ(ref_frame.sample_rate_hz_, test_frame.sample_rate_hz_); -} - -// Computes the best SNR based on the error between |ref_frame| and -// |test_frame|. It allows for up to a |max_delay| in samples between the -// signals to compensate for the resampling delay. -float ComputeSNR(const AudioFrame& ref_frame, const AudioFrame& test_frame, - int max_delay) { - VerifyParams(ref_frame, test_frame); - float best_snr = 0; - int best_delay = 0; - for (int delay = 0; delay <= max_delay; delay++) { - float mse = 0; - float variance = 0; - for (int i = 0; i < ref_frame.samples_per_channel_ * - ref_frame.num_channels_ - delay; i++) { - int error = ref_frame.data_[i] - test_frame.data_[i + delay]; - mse += error * error; - variance += ref_frame.data_[i] * ref_frame.data_[i]; - } - float snr = 100; // We assign 100 dB to the zero-error case. - if (mse > 0) - snr = 10 * log10(variance / mse); - if (snr > best_snr) { - best_snr = snr; - best_delay = delay; - } - } - printf("SNR=%.1f dB at delay=%d\n", best_snr, best_delay); - return best_snr; -} - -void VerifyFramesAreEqual(const AudioFrame& ref_frame, - const AudioFrame& test_frame) { - VerifyParams(ref_frame, test_frame); - for (int i = 0; i < ref_frame.samples_per_channel_ * ref_frame.num_channels_; - i++) { - EXPECT_EQ(ref_frame.data_[i], test_frame.data_[i]); - } -} - -void OutputMixerTest::RunResampleTest(int src_channels, - int src_sample_rate_hz, - int dst_channels, - int dst_sample_rate_hz) { - PushResampler resampler; // Create a new one with every test. - const int16_t kSrcLeft = 30; // Shouldn't overflow for any used sample rate. - const int16_t kSrcRight = 15; - const float resampling_factor = (1.0 * src_sample_rate_hz) / - dst_sample_rate_hz; - const float dst_left = resampling_factor * kSrcLeft; - const float dst_right = resampling_factor * kSrcRight; - const float dst_mono = (dst_left + dst_right) / 2; - if (src_channels == 1) - SetMonoFrame(&src_frame_, kSrcLeft, src_sample_rate_hz); - else - SetStereoFrame(&src_frame_, kSrcLeft, kSrcRight, src_sample_rate_hz); - - if (dst_channels == 1) { - SetMonoFrame(&dst_frame_, 0, dst_sample_rate_hz); - if (src_channels == 1) - SetMonoFrame(&golden_frame_, dst_left, dst_sample_rate_hz); - else - SetMonoFrame(&golden_frame_, dst_mono, dst_sample_rate_hz); - } else { - SetStereoFrame(&dst_frame_, 0, 0, dst_sample_rate_hz); - if (src_channels == 1) - SetStereoFrame(&golden_frame_, dst_left, dst_left, dst_sample_rate_hz); - else - SetStereoFrame(&golden_frame_, dst_left, dst_right, dst_sample_rate_hz); - } - - // The speex resampler has a known delay dependent on quality and rates, - // which we approximate here. Multiplying by two gives us a crude maximum - // for any resampling, as the old resampler typically (but not always) - // has lower delay. The actual delay is calculated internally based on the - // filter length in the QualityMap. - static const int kInputKernelDelaySamples = 16*3; - const int max_delay = std::min(1.0f, 1/kResamplingFactor) * - kInputKernelDelaySamples * dst_channels * 2; - printf("(%d, %d Hz) -> (%d, %d Hz) ", // SNR reported on the same line later. - src_channels, src_sample_rate_hz, dst_channels, dst_sample_rate_hz); - EXPECT_EQ(0, RemixAndResample(src_frame_, &resampler, &dst_frame_)); - if (src_sample_rate_hz == 96000 && dst_sample_rate_hz == 8000) { - // The sinc resampler gives poor SNR at this extreme conversion, but we - // expect to see this rarely in practice. - EXPECT_GT(ComputeSNR(golden_frame_, dst_frame_, max_delay), 14.0f); - } else { - EXPECT_GT(ComputeSNR(golden_frame_, dst_frame_, max_delay), 46.0f); - } -} - -// These two tests assume memcpy() (no delay and no filtering) for input -// freq == output freq && same channels. RemixAndResample uses 'Fixed' -// resamplers to enable this behavior -TEST_F(OutputMixerTest, RemixAndResampleCopyFrameSucceeds) { - // Stereo -> stereo. - SetStereoFrame(&src_frame_, 10, 10); - SetStereoFrame(&dst_frame_, 0, 0); - EXPECT_EQ(0, RemixAndResample(src_frame_, &resampler_, &dst_frame_)); - VerifyFramesAreEqual(src_frame_, dst_frame_); - - // Mono -> mono. - SetMonoFrame(&src_frame_, 20); - SetMonoFrame(&dst_frame_, 0); - EXPECT_EQ(0, RemixAndResample(src_frame_, &resampler_, &dst_frame_)); - VerifyFramesAreEqual(src_frame_, dst_frame_); -} - -TEST_F(OutputMixerTest, RemixAndResampleMixingOnlySucceeds) { - // Stereo -> mono. - SetStereoFrame(&dst_frame_, 0, 0); - SetMonoFrame(&src_frame_, 10); - SetStereoFrame(&golden_frame_, 10, 10); - EXPECT_EQ(0, RemixAndResample(src_frame_, &resampler_, &dst_frame_)); - VerifyFramesAreEqual(dst_frame_, golden_frame_); - - // Mono -> stereo. - SetMonoFrame(&dst_frame_, 0); - SetStereoFrame(&src_frame_, 10, 20); - SetMonoFrame(&golden_frame_, 15); - EXPECT_EQ(0, RemixAndResample(src_frame_, &resampler_, &dst_frame_)); - VerifyFramesAreEqual(golden_frame_, dst_frame_); -} - -TEST_F(OutputMixerTest, RemixAndResampleSucceeds) { - // TODO(ajm): convert this to the parameterized TEST_P style used in - // sinc_resampler_unittest.cc. We can then easily add tighter SNR thresholds. - const int kSampleRates[] = {8000, 16000, 32000, 44100, 48000, 96000}; - const int kSampleRatesSize = sizeof(kSampleRates) / sizeof(*kSampleRates); - const int kChannels[] = {1, 2}; - const int kChannelsSize = sizeof(kChannels) / sizeof(*kChannels); - for (int src_rate = 0; src_rate < kSampleRatesSize; src_rate++) { - for (int dst_rate = 0; dst_rate < kSampleRatesSize; dst_rate++) { - for (int src_channel = 0; src_channel < kChannelsSize; src_channel++) { - for (int dst_channel = 0; dst_channel < kChannelsSize; dst_channel++) { - RunResampleTest(kChannels[src_channel], kSampleRates[src_rate], - kChannels[dst_channel], kSampleRates[dst_rate]); - } - } - } - } -} - -} // namespace -} // namespace voe -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/OWNERS 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/OWNERS 2015-02-03 14:33:38.000000000 +0000 @@ -2,3 +2,12 @@ henrika@webrtc.org niklas.enbom@webrtc.org xians@webrtc.org + +per-file *.isolate=kjellander@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* + +per-file BUILD.gn=kjellander@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/android/android_test/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/android/android_test/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/android/android_test/Android.mk 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/android/android_test/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SRC_FILES := \ - src/org/webrtc/voiceengine/test/AndroidTest.java - -LOCAL_PACKAGE_NAME := webrtc-voice-demo -LOCAL_CERTIFICATE := platform - -LOCAL_JNI_SHARED_LIBRARIES := libwebrtc-voice-demo-jni - -include $(BUILD_PACKAGE) - -include $(call all-makefiles-under,$(LOCAL_PATH)) diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/android/android_test/jni/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/android/android_test/jni/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/android/android_test/jni/Android.mk 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/android/android_test/jni/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,152 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -ifdef NDK_ROOT - -MY_WEBRTC_ROOT_PATH := $(call my-dir) - -MY_WEBRTC_SRC_PATH := ../../../../../../.. - -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/common_audio/resampler/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/common_audio/signal_processing/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/common_audio/vad/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/audio_coding/neteq/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/audio_coding/codecs/cng/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/audio_coding/codecs/g711/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/audio_coding/codecs/g722/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/audio_coding/codecs/pcm16b/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/audio_coding/codecs/ilbc/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/audio_coding/codecs/iSAC/fix/source/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/audio_coding/codecs/iSAC/main/source/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/audio_coding/main/source/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/audio_conference_mixer/source/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/audio_device/main/source/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/audio_processing/aec/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/audio_processing/aecm/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/audio_processing/agc/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/audio_processing/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/audio_processing/ns/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/audio_processing/utility/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/media_file/source/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/rtp_rtcp/source/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/udp_transport/source/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/modules/utility/source/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/system_wrappers/source/Android.mk -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/voice_engine/Android.mk - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE := libwebrtc_audio_preprocessing -LOCAL_MODULE_TAGS := optional - -LOCAL_WHOLE_STATIC_LIBRARIES := \ - libwebrtc_spl \ - libwebrtc_resampler \ - libwebrtc_apm \ - libwebrtc_apm_utility \ - libwebrtc_vad \ - libwebrtc_ns \ - libwebrtc_agc \ - libwebrtc_aec \ - libwebrtc_aecm \ - libwebrtc_system_wrappers \ - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libstlport_shared - -LOCAL_LDLIBS := \ - -lgcc \ - -llog - -LOCAL_PRELINK_MODULE := false - -include $(BUILD_SHARED_LIBRARY) - -### - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE := libwebrtc-voice-jni -LOCAL_MODULE_TAGS := optional - -LOCAL_WHOLE_STATIC_LIBRARIES := \ - libwebrtc_system_wrappers \ - libwebrtc_audio_device \ - libwebrtc_pcm16b \ - libwebrtc_cng \ - libwebrtc_audio_coding \ - libwebrtc_rtp_rtcp \ - libwebrtc_media_file \ - libwebrtc_udp_transport \ - libwebrtc_utility \ - libwebrtc_neteq \ - libwebrtc_audio_conference_mixer \ - libwebrtc_isac \ - libwebrtc_ilbc \ - libwebrtc_isacfix \ - libwebrtc_g722 \ - libwebrtc_g711 \ - libwebrtc_voe_core - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libstlport_shared \ - libwebrtc_audio_preprocessing - -LOCAL_LDLIBS := \ - -lgcc \ - -llog \ - -lOpenSLES - -LOCAL_PRELINK_MODULE := false - -include $(BUILD_SHARED_LIBRARY) - -### - -include $(MY_WEBRTC_ROOT_PATH)/$(MY_WEBRTC_SRC_PATH)/src/voice_engine/test/cmd_test/Android.mk - -else - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests -LOCAL_MODULE := libwebrtc-voice-demo-jni -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := android_test.cc -LOCAL_CFLAGS := \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_ANDROID' - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../../../auto_test \ - $(LOCAL_PATH)/../../../../interface \ - $(LOCAL_PATH)/../../../../../.. \ - $(LOCAL_PATH)/../../../../../../system_wrappers/interface - -LOCAL_PRELINK_MODULE := false - -LOCAL_SHARED_LIBRARIES := \ - libutils \ - libstlport \ - libandroid \ - libwebrtc \ - libGLESv2 - -include $(BUILD_SHARED_LIBRARY) - -endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/android/android_test/jni/android_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/android/android_test/jni/android_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/android/android_test/jni/android_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/android/android_test/jni/android_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -20,7 +20,6 @@ #include "webrtc/voice_engine/include/voe_audio_processing.h" #include "webrtc/voice_engine/include/voe_base.h" #include "webrtc/voice_engine/include/voe_codec.h" -#include "webrtc/voice_engine/include/voe_encryption.h" #include "webrtc/voice_engine/include/voe_file.h" #include "webrtc/voice_engine/include/voe_hardware.h" #include "webrtc/voice_engine/include/voe_network.h" @@ -89,13 +88,6 @@ "RTP / RTCP pointer doesn't exist"); \ return -1; \ } -#define VALIDATE_ENCRYPT_POINTER \ - if (!veData1.encrypt) \ - { \ - __android_log_write(ANDROID_LOG_ERROR, WEBRTC_LOG_TAG, \ - "Encrypt pointer doesn't exist"); \ - return -1; \ - } // Register functions in JNI_OnLoad() // How do we ensure that VoE is deleted? JNI_OnUnload? @@ -116,32 +108,6 @@ CPU = 4 }; -// ExtendedSelection enumerator -enum ExtendedSelection -{ - XSEL_Invalid = -1, - XSEL_None = 0, - XSEL_All, - XSEL_Base, - XSEL_CallReport, - XSEL_Codec, - XSEL_DTMF, - XSEL_Encryption, - XSEL_ExternalMedia, - XSEL_File, - XSEL_Hardware, - XSEL_NetEqStats, - XSEL_Network, - XSEL_PTT, - XSEL_RTP_RTCP, - XSEL_VideoSync, - XSEL_VideoSyncExtended, - XSEL_VolumeControl, - XSEL_VQE, - XSEL_APM, - XSEL_VQMon -}; - using namespace webrtc; class my_transportation; @@ -160,7 +126,6 @@ VoEVolumeControl* volume; VoEHardware* hardware; VoERTP_RTCP* rtp_rtcp; - VoEEncryption* encrypt; // Other my_transportation* extTrans; JavaVM* jvm; @@ -174,8 +139,8 @@ netw(network) { } - int SendPacket(int channel,const void *data,int len); - int SendRTCPPacket(int channel, const void *data, int len); + virtual int SendPacket(int channel,const void *data,int len) OVERRIDE; + virtual int SendRTCPPacket(int channel, const void *data, int len) OVERRIDE; private: VoENetwork * netw; }; @@ -447,80 +412,12 @@ return -1; } - ExtendedSelection xsel(XSEL_Invalid); - - switch (extendedSel) - { - case 0: - xsel = XSEL_None; - break; - case 1: - xsel = XSEL_All; - break; - case 2: - xsel = XSEL_Base; - break; - case 3: - xsel = XSEL_CallReport; - break; - case 4: - xsel = XSEL_Codec; - break; - case 5: - xsel = XSEL_DTMF; - break; - case 6: - xsel = XSEL_Encryption; - break; - case 7: - xsel = XSEL_ExternalMedia; - break; - case 8: - xsel = XSEL_File; - break; - case 9: - xsel = XSEL_Hardware; - break; - case 10: - xsel = XSEL_NetEqStats; - break; - case 11: - xsel = XSEL_Network; - break; - case 12: - xsel = XSEL_PTT; - break; - case 13: - xsel = XSEL_RTP_RTCP; - break; - case 14: - xsel = XSEL_VideoSync; - break; - case 15: - xsel = XSEL_VideoSyncExtended; - break; - case 16: - xsel = XSEL_VolumeControl; - break; - case 17: - xsel = XSEL_APM; - break; - case 18: - xsel = XSEL_VQMon; - break; - default: - xsel = XSEL_Invalid; - __android_log_write(ANDROID_LOG_ERROR, WEBRTC_LOG_TAG, - "RunAutoTest - Invalid extendedType"); - return -1; - } - // Set instance independent Java objects VoiceEngine::SetAndroidObjects(veData1.jvm, env, context); // Call voe test interface function // TODO(leozwang) add autotest setAndroidObjects(veData1.jvm, context); - // jint retVal = runAutoTest(tType, xsel); + // jint retVal = runAutoTest(tType); // Clear instance independent Java objects VoiceEngine::SetAndroidObjects(NULL, NULL, NULL); @@ -813,10 +710,10 @@ return -1; } */ - /* if (veData1.rtp_rtcp->SetFECStatus(channel, 1) != 0) + /* if (veData1.rtp_rtcp->SetREDStatus(channel, 1) != 0) { __android_log_write(ANDROID_LOG_ERROR, WEBRTC_LOG_TAG, - "Failed to enable FEC"); + "Failed to enable RED"); return -1; } */ @@ -858,10 +755,10 @@ jobject, jint channel) { - /* if (veData1.rtp_rtcp->SetFECStatus(channel, 0) != 0) + /* if (veData1.rtp_rtcp->SetREDStatus(channel, 0) != 0) { __android_log_write(ANDROID_LOG_ERROR, WEBRTC_LOG_TAG, - "Failed to disable FEC"); + "Failed to disable RED"); return -1; } */ @@ -1245,21 +1142,21 @@ /*VALIDATE_RTP_RTCP_POINTER; - if (veData1.rtp_rtcp->SetFECStatus(0, enable, -1) != 0) + if (veData1.rtp_rtcp->SetREDStatus(0, enable, -1) != 0) { __android_log_write(ANDROID_LOG_ERROR, WEBRTC_LOG_TAG, - "Could not set FEC"); + "Could not set RED"); return -1; } else if(enable) { __android_log_write(ANDROID_LOG_ERROR, WEBRTC_LOG_TAG, - "Could enable FEC"); + "Could enable RED"); } else { __android_log_write(ANDROID_LOG_ERROR, WEBRTC_LOG_TAG, - "Could disable FEC"); + "Could disable RED"); }*/ return 0; @@ -1348,15 +1245,6 @@ getOK = false; } - // Encrypt - veData.encrypt = VoEEncryption::GetInterface(veData.ve); - if (!veData.encrypt) - { - __android_log_write(ANDROID_LOG_ERROR, WEBRTC_LOG_TAG, - "Get encrypt sub-API failed"); - getOK = false; - } - return getOK; } @@ -1487,20 +1375,5 @@ } } - // Encrypt - if (veData.encrypt) - { - if (0 != veData.encrypt->Release()) - { - __android_log_write(ANDROID_LOG_ERROR, WEBRTC_LOG_TAG, - "Release encrypt sub-API failed"); - releaseOK = false; - } - else - { - veData.encrypt = NULL; - } - } - return releaseOK; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/android/OWNERS thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/android/OWNERS --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/android/OWNERS 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/android/OWNERS 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -leozwang@webrtc.org diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/Android.mk 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH:= $(call my-dir) - -# voice engine test app - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../android-webrtc.mk - -LOCAL_MODULE_TAGS := tests -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES:= \ - automated_mode.cc \ - voe_cpu_test.cc \ - voe_standard_test.cc \ - voe_stress_test.cc \ - voe_unit_test.cc \ - voe_extended_test.cc \ - voe_standard_integration_test.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_ANDROID' - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../../interface \ - $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/../../../../modules/audio_device/main/interface \ - $(LOCAL_PATH)/../../../../modules/interface \ - $(LOCAL_PATH)/../../../../system_wrappers/interface \ - $(LOCAL_PATH)/../../../../../test \ - external/gtest/include \ - -LOCAL_SHARED_LIBRARIES := \ - libutils \ - libstlport \ - libwebrtc - -LOCAL_MODULE:= webrtc_voe_autotest - -ifdef NDK_ROOT -include $(BUILD_EXECUTABLE) -else -include external/stlport/libstlport.mk -include $(BUILD_NATIVE_TEST) -endif - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/extended/agc_config_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/extended/agc_config_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/extended/agc_config_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/extended/agc_config_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -22,9 +22,7 @@ webrtc::AgcConfig default_agc_config_; }; -// TODO(aluebs): Re-enable tests in agc_config_test and audio_processing_test -// when possible. Bug: issue 2784. -TEST_F(AgcConfigTest, DISABLED_HasCorrectDefaultConfiguration) { +TEST_F(AgcConfigTest, HasCorrectDefaultConfiguration) { webrtc::AgcConfig agc_config; EXPECT_EQ(0, voe_apm_->GetAgcConfig(agc_config)); @@ -35,7 +33,7 @@ EXPECT_EQ(default_agc_config_.limiterEnable, agc_config.limiterEnable); } -TEST_F(AgcConfigTest, DISABLED_DealsWithInvalidParameters) { +TEST_F(AgcConfigTest, DealsWithInvalidParameters) { webrtc::AgcConfig agc_config = default_agc_config_; agc_config.digitalCompressionGaindB = 91; EXPECT_EQ(-1, voe_apm_->SetAgcConfig(agc_config)) << "Should not be able " @@ -49,7 +47,7 @@ EXPECT_EQ(VE_APM_ERROR, voe_base_->LastError()); } -TEST_F(AgcConfigTest, DISABLED_CanGetAndSetAgcStatus) { +TEST_F(AgcConfigTest, CanGetAndSetAgcStatus) { webrtc::AgcConfig agc_config; agc_config.digitalCompressionGaindB = 17; agc_config.targetLeveldBOv = 11; @@ -67,7 +65,7 @@ actual_config.targetLeveldBOv); } -TEST_F(AgcConfigTest, DISABLED_HasCorrectDefaultRxConfiguration) { +TEST_F(AgcConfigTest, HasCorrectDefaultRxConfiguration) { webrtc::AgcConfig agc_config; EXPECT_EQ(0, voe_apm_->GetRxAgcConfig(channel_, agc_config)); @@ -78,7 +76,7 @@ EXPECT_EQ(default_agc_config_.limiterEnable, agc_config.limiterEnable); } -TEST_F(AgcConfigTest, DISABLED_DealsWithInvalidRxParameters) { +TEST_F(AgcConfigTest, DealsWithInvalidRxParameters) { webrtc::AgcConfig agc_config = default_agc_config_; agc_config.digitalCompressionGaindB = 91; EXPECT_EQ(-1, voe_apm_->SetRxAgcConfig(channel_, agc_config)) << @@ -92,7 +90,7 @@ EXPECT_EQ(VE_APM_ERROR, voe_base_->LastError()); } -TEST_F(AgcConfigTest, DISABLED_CanGetAndSetRxAgcStatus) { +TEST_F(AgcConfigTest, CanGetAndSetRxAgcStatus) { webrtc::AgcConfig agc_config; agc_config.digitalCompressionGaindB = 17; agc_config.targetLeveldBOv = 11; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fakes/fake_external_transport.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fakes/fake_external_transport.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fakes/fake_external_transport.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fakes/fake_external_transport.cc 2015-02-03 14:33:38.000000000 +0000 @@ -59,7 +59,8 @@ switch (event_->Wait(500)) { case webrtc::kEventSignaled: lock_->Enter(); - my_network_->ReceivedRTPPacket(channel_, packet_buffer_, length_); + my_network_->ReceivedRTPPacket(channel_, packet_buffer_, length_, + webrtc::PacketTime()); lock_->Leave(); return true; case webrtc::kEventTimeout: diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fakes/fake_external_transport.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fakes/fake_external_transport.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fakes/fake_external_transport.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fakes/fake_external_transport.h 2015-02-03 14:33:38.000000000 +0000 @@ -23,8 +23,10 @@ public: explicit FakeExternalTransport(webrtc::VoENetwork* ptr); virtual ~FakeExternalTransport(); - int SendPacket(int channel, const void *data, int len); - int SendRTCPPacket(int channel, const void *data, int len); + + virtual int SendPacket(int channel, const void *data, int len) OVERRIDE; + virtual int SendRTCPPacket(int channel, const void *data, int len) OVERRIDE; + void SetDelayStatus(bool enabled, unsigned int delayInMs = 100); webrtc::VoENetwork* my_network_; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/after_initialization_fixture.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/after_initialization_fixture.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/after_initialization_fixture.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/after_initialization_fixture.cc 2015-02-03 14:33:38.000000000 +0000 @@ -8,6 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/voice_engine/test/auto_test/fixtures/after_initialization_fixture.h" class TestErrorObserver : public webrtc::VoiceEngineObserver { @@ -22,7 +23,11 @@ AfterInitializationFixture::AfterInitializationFixture() : error_observer_(new TestErrorObserver()) { - EXPECT_EQ(0, voe_base_->Init()); + webrtc::Config config; + config.Set(new webrtc::ExperimentalAgc(false)); + webrtc::AudioProcessing* audioproc = webrtc::AudioProcessing::Create(config); + + EXPECT_EQ(0, voe_base_->Init(NULL, audioproc)); #if defined(WEBRTC_ANDROID) EXPECT_EQ(0, voe_hardware_->SetLoudspeakerStatus(false)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/after_initialization_fixture.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/after_initialization_fixture.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/after_initialization_fixture.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/after_initialization_fixture.h 2015-02-03 14:33:38.000000000 +0000 @@ -35,12 +35,12 @@ ~LoopBackTransport() { thread_->Stop(); } - virtual int SendPacket(int channel, const void* data, int len) { + virtual int SendPacket(int channel, const void* data, int len) OVERRIDE { StorePacket(Packet::Rtp, channel, data, len); return len; } - virtual int SendRTCPPacket(int channel, const void* data, int len) { + virtual int SendRTCPPacket(int channel, const void* data, int len) OVERRIDE { StorePacket(Packet::Rtcp, channel, data, len); return len; } @@ -95,7 +95,8 @@ switch (p.type) { case Packet::Rtp: - voe_network_->ReceivedRTPPacket(p.channel, p.data, p.len); + voe_network_->ReceivedRTPPacket(p.channel, p.data, p.len, + webrtc::PacketTime()); break; case Packet::Rtcp: voe_network_->ReceivedRTCPPacket(p.channel, p.data, p.len); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.cc 2015-02-03 14:33:38.000000000 +0000 @@ -10,65 +10,7 @@ #include "webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.h" -#include - AfterStreamingFixture::AfterStreamingFixture() - : channel_(voe_base_->CreateChannel()) { - EXPECT_GE(channel_, 0); - - fake_microphone_input_file_ = resource_manager_.long_audio_file_path(); - EXPECT_FALSE(fake_microphone_input_file_.empty()); - - SetUpLocalPlayback(); + : BeforeStreamingFixture() { ResumePlaying(); - RestartFakeMicrophone(); -} - -AfterStreamingFixture::~AfterStreamingFixture() { - voe_file_->StopPlayingFileAsMicrophone(channel_); - PausePlaying(); - - EXPECT_EQ(0, voe_network_->DeRegisterExternalTransport(channel_)); - voe_base_->DeleteChannel(channel_); - delete transport_; -} - -void AfterStreamingFixture::SwitchToManualMicrophone() { - EXPECT_EQ(0, voe_file_->StopPlayingFileAsMicrophone(channel_)); - - TEST_LOG("You need to speak manually into the microphone for this test.\n"); - TEST_LOG("Please start speaking now.\n"); - Sleep(1000); -} - -void AfterStreamingFixture::RestartFakeMicrophone() { - EXPECT_EQ(0, voe_file_->StartPlayingFileAsMicrophone( - channel_, fake_microphone_input_file_.c_str(), true, true)); -} - -void AfterStreamingFixture::PausePlaying() { - EXPECT_EQ(0, voe_base_->StopSend(channel_)); - EXPECT_EQ(0, voe_base_->StopPlayout(channel_)); - EXPECT_EQ(0, voe_base_->StopReceive(channel_)); -} - -void AfterStreamingFixture::ResumePlaying() { - EXPECT_EQ(0, voe_base_->StartReceive(channel_)); - EXPECT_EQ(0, voe_base_->StartPlayout(channel_)); - EXPECT_EQ(0, voe_base_->StartSend(channel_)); -} - -void AfterStreamingFixture::SetUpLocalPlayback() { - transport_ = new LoopBackTransport(voe_network_); - EXPECT_EQ(0, voe_network_->RegisterExternalTransport(channel_, *transport_)); - - webrtc::CodecInst codec; - codec.channels = 1; - codec.pacsize = 160; - codec.plfreq = 8000; - codec.pltype = 0; - codec.rate = 64000; - strcpy(codec.plname, "PCMU"); - - voe_codec_->SetSendCodec(channel_, codec); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.h 2015-02-03 14:33:38.000000000 +0000 @@ -11,40 +11,14 @@ #ifndef SRC_VOICE_ENGINE_MAIN_TEST_AUTO_TEST_STANDARD_AFTER_STREAMING_H_ #define SRC_VOICE_ENGINE_MAIN_TEST_AUTO_TEST_STANDARD_AFTER_STREAMING_H_ -#include "webrtc/voice_engine/test/auto_test/fixtures/after_initialization_fixture.h" -#include "webrtc/voice_engine/test/auto_test/resource_manager.h" +#include "webrtc/voice_engine/test/auto_test/fixtures/before_streaming_fixture.h" // This fixture will, in addition to the work done by its superclasses, -// create a channel and start playing a file through the fake microphone -// to simulate microphone input. The purpose is to make it convenient -// to write tests that require microphone input. -class AfterStreamingFixture : public AfterInitializationFixture { +// start play back on construction. +class AfterStreamingFixture : public BeforeStreamingFixture { public: AfterStreamingFixture(); - virtual ~AfterStreamingFixture(); - - protected: - int channel_; - ResourceManager resource_manager_; - std::string fake_microphone_input_file_; - - // Shuts off the fake microphone for this test. - void SwitchToManualMicrophone(); - - // Restarts the fake microphone if it's been shut off earlier. - void RestartFakeMicrophone(); - - // Stops all sending and playout. - void PausePlaying(); - - // Resumes all sending and playout. - void ResumePlaying(); - - private: - void SetUpLocalPlayback(); - - LoopBackTransport* transport_; + virtual ~AfterStreamingFixture() {} }; - #endif // SRC_VOICE_ENGINE_MAIN_TEST_AUTO_TEST_STANDARD_AFTER_STREAMING_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/before_initialization_fixture.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/before_initialization_fixture.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/before_initialization_fixture.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/before_initialization_fixture.cc 2015-02-03 14:33:38.000000000 +0000 @@ -10,19 +10,10 @@ #include "webrtc/voice_engine/test/auto_test/fixtures/before_initialization_fixture.h" -#include "gflags/gflags.h" -#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" #include "webrtc/system_wrappers/interface/sleep.h" -DECLARE_bool(use_acm_version_2); - -BeforeInitializationFixture::BeforeInitializationFixture() { - // TODO(minyue): Remove when the old ACM is removed (latest 2014-04-01). - config_.Set(FLAGS_use_acm_version_2 ? - new webrtc::NewAudioCodingModuleFactory() : - new webrtc::AudioCodingModuleFactory()); - voice_engine_ = webrtc::VoiceEngine::Create(config_); - +BeforeInitializationFixture::BeforeInitializationFixture() + : voice_engine_(webrtc::VoiceEngine::Create()) { EXPECT_TRUE(voice_engine_ != NULL); voe_base_ = webrtc::VoEBase::GetInterface(voice_engine_); @@ -34,10 +25,8 @@ voe_network_ = webrtc::VoENetwork::GetInterface(voice_engine_); voe_file_ = webrtc::VoEFile::GetInterface(voice_engine_); voe_vsync_ = webrtc::VoEVideoSync::GetInterface(voice_engine_); - voe_encrypt_ = webrtc::VoEEncryption::GetInterface(voice_engine_); voe_hardware_ = webrtc::VoEHardware::GetInterface(voice_engine_); voe_xmedia_ = webrtc::VoEExternalMedia::GetInterface(voice_engine_); - voe_call_report_ = webrtc::VoECallReport::GetInterface(voice_engine_); voe_neteq_stats_ = webrtc::VoENetEqStats::GetInterface(voice_engine_); } @@ -51,10 +40,8 @@ voe_network_->Release(); voe_file_->Release(); voe_vsync_->Release(); - voe_encrypt_->Release(); voe_hardware_->Release(); voe_xmedia_->Release(); - voe_call_report_->Release(); voe_neteq_stats_->Release(); EXPECT_TRUE(webrtc::VoiceEngine::Delete(voice_engine_)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/before_initialization_fixture.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/before_initialization_fixture.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/before_initialization_fixture.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/before_initialization_fixture.h 2015-02-03 14:33:38.000000000 +0000 @@ -19,10 +19,8 @@ #include "webrtc/test/testsupport/gtest_disable.h" #include "webrtc/voice_engine/include/voe_audio_processing.h" #include "webrtc/voice_engine/include/voe_base.h" -#include "webrtc/voice_engine/include/voe_call_report.h" #include "webrtc/voice_engine/include/voe_codec.h" #include "webrtc/voice_engine/include/voe_dtmf.h" -#include "webrtc/voice_engine/include/voe_encryption.h" #include "webrtc/voice_engine/include/voe_errors.h" #include "webrtc/voice_engine/include/voe_external_media.h" #include "webrtc/voice_engine/include/voe_file.h" @@ -67,10 +65,8 @@ webrtc::VoENetwork* voe_network_; webrtc::VoEFile* voe_file_; webrtc::VoEVideoSync* voe_vsync_; - webrtc::VoEEncryption* voe_encrypt_; webrtc::VoEHardware* voe_hardware_; webrtc::VoEExternalMedia* voe_xmedia_; - webrtc::VoECallReport* voe_call_report_; webrtc::VoENetEqStats* voe_neteq_stats_; webrtc::Config config_; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/before_streaming_fixture.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/before_streaming_fixture.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/before_streaming_fixture.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/before_streaming_fixture.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/voice_engine/test/auto_test/fixtures/before_streaming_fixture.h" + +BeforeStreamingFixture::BeforeStreamingFixture() + : channel_(voe_base_->CreateChannel()), + transport_(NULL) { + EXPECT_GE(channel_, 0); + + fake_microphone_input_file_ = resource_manager_.long_audio_file_path(); + EXPECT_FALSE(fake_microphone_input_file_.empty()); + + SetUpLocalPlayback(); + RestartFakeMicrophone(); +} + +BeforeStreamingFixture::~BeforeStreamingFixture() { + voe_file_->StopPlayingFileAsMicrophone(channel_); + PausePlaying(); + + EXPECT_EQ(0, voe_network_->DeRegisterExternalTransport(channel_)); + voe_base_->DeleteChannel(channel_); + delete transport_; +} + +void BeforeStreamingFixture::SwitchToManualMicrophone() { + EXPECT_EQ(0, voe_file_->StopPlayingFileAsMicrophone(channel_)); + + TEST_LOG("You need to speak manually into the microphone for this test.\n"); + TEST_LOG("Please start speaking now.\n"); + Sleep(1000); +} + +void BeforeStreamingFixture::RestartFakeMicrophone() { + EXPECT_EQ(0, voe_file_->StartPlayingFileAsMicrophone( + channel_, fake_microphone_input_file_.c_str(), true, true)); +} + +void BeforeStreamingFixture::PausePlaying() { + EXPECT_EQ(0, voe_base_->StopSend(channel_)); + EXPECT_EQ(0, voe_base_->StopPlayout(channel_)); + EXPECT_EQ(0, voe_base_->StopReceive(channel_)); +} + +void BeforeStreamingFixture::ResumePlaying() { + EXPECT_EQ(0, voe_base_->StartReceive(channel_)); + EXPECT_EQ(0, voe_base_->StartPlayout(channel_)); + EXPECT_EQ(0, voe_base_->StartSend(channel_)); +} + +void BeforeStreamingFixture::SetUpLocalPlayback() { + transport_ = new LoopBackTransport(voe_network_); + EXPECT_EQ(0, voe_network_->RegisterExternalTransport(channel_, *transport_)); + + webrtc::CodecInst codec; + codec.channels = 1; + codec.pacsize = 160; + codec.plfreq = 8000; + codec.pltype = 0; + codec.rate = 64000; +#if defined(_MSC_VER) && defined(_WIN32) + _snprintf(codec.plname, RTP_PAYLOAD_NAME_SIZE - 1, "PCMU"); +#else + snprintf(codec.plname, RTP_PAYLOAD_NAME_SIZE, "PCMU"); +#endif + voe_codec_->SetSendCodec(channel_, codec); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/before_streaming_fixture.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/before_streaming_fixture.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/before_streaming_fixture.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fixtures/before_streaming_fixture.h 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_VOICE_ENGINE_MAIN_TEST_AUTO_TEST_STANDARD_BEFORE_STREAMING_H_ +#define SRC_VOICE_ENGINE_MAIN_TEST_AUTO_TEST_STANDARD_BEFORE_STREAMING_H_ + +#include +#include "webrtc/voice_engine/test/auto_test/fixtures/after_initialization_fixture.h" +#include "webrtc/voice_engine/test/auto_test/resource_manager.h" + +// This fixture will, in addition to the work done by its superclasses, +// create a channel and prepare playing a file through the fake microphone +// to simulate microphone input. The purpose is to make it convenient +// to write tests that require microphone input. +class BeforeStreamingFixture : public AfterInitializationFixture { + public: + BeforeStreamingFixture(); + virtual ~BeforeStreamingFixture(); + + protected: + int channel_; + ResourceManager resource_manager_; + std::string fake_microphone_input_file_; + + // Shuts off the fake microphone for this test. + void SwitchToManualMicrophone(); + + // Restarts the fake microphone if it's been shut off earlier. + void RestartFakeMicrophone(); + + // Stops all sending and playout. + void PausePlaying(); + + // Resumes all sending and playout. + void ResumePlaying(); + + private: + void SetUpLocalPlayback(); + + LoopBackTransport* transport_; +}; + + +#endif // SRC_VOICE_ENGINE_MAIN_TEST_AUTO_TEST_STANDARD_BEFORE_STREAMING_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fuzz/rtp_fuzz_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fuzz/rtp_fuzz_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fuzz/rtp_fuzz_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/fuzz/rtp_fuzz_test.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "webrtc/test/libtest/include/bit_flip_encryption.h" -#include "webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.h" - -class RtpFuzzTest : public AfterStreamingFixture { - protected: - void BitFlipFuzzTest(float flip_probability) { - BitFlipEncryption bit_flip_encryption(time(NULL), flip_probability); - - TEST_LOG("Starting to flip bits in RTP/RTCP packets.\n"); - voe_encrypt_->RegisterExternalEncryption(channel_, bit_flip_encryption); - - Sleep(5000); - - voe_encrypt_->DeRegisterExternalEncryption(channel_); - - TEST_LOG("Flipped %d bits. Back to normal.\n", - static_cast(bit_flip_encryption.flip_count())); - Sleep(2000); - } -}; - -TEST_F(RtpFuzzTest, VoiceEngineDealsWithASmallNumberOfTamperedRtpPackets) { - BitFlipFuzzTest(0.00005f); -} - -TEST_F(RtpFuzzTest, VoiceEngineDealsWithAMediumNumberOfTamperedRtpPackets) { - BitFlipFuzzTest(0.0005f); -} - -TEST_F(RtpFuzzTest, VoiceEngineDealsWithALargeNumberOfTamperedRtpPackets) { - BitFlipFuzzTest(0.05f); -} - -TEST_F(RtpFuzzTest, VoiceEngineDealsWithAHugeNumberOfTamperedRtpPackets) { - BitFlipFuzzTest(0.5f); -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/audio_processing_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/audio_processing_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/audio_processing_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/audio_processing_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -140,7 +140,7 @@ #if !defined(WEBRTC_IOS) && !defined(WEBRTC_ANDROID) -TEST_F(AudioProcessingTest, DISABLED_AgcIsOnByDefault) { +TEST_F(AudioProcessingTest, AgcIsOnByDefault) { bool agc_enabled = false; webrtc::AgcModes agc_mode = webrtc::kAgcAdaptiveAnalog; @@ -149,7 +149,7 @@ EXPECT_EQ(webrtc::kAgcAdaptiveAnalog, agc_mode); } -TEST_F(AudioProcessingTest, DISABLED_CanEnableAgcWithAllModes) { +TEST_F(AudioProcessingTest, CanEnableAgcWithAllModes) { TryEnablingAgcWithMode(webrtc::kAgcAdaptiveDigital); TryEnablingAgcWithMode(webrtc::kAgcAdaptiveAnalog); TryEnablingAgcWithMode(webrtc::kAgcFixedDigital); @@ -248,7 +248,7 @@ TryEnablingAecmWithMode(webrtc::kAecmSpeakerphone, false); } -TEST_F(AudioProcessingTest, DISABLED_RxAgcShouldBeOffByDefault) { +TEST_F(AudioProcessingTest, RxAgcShouldBeOffByDefault) { bool rx_agc_enabled = true; webrtc::AgcModes agc_mode = webrtc::kAgcDefault; @@ -257,12 +257,12 @@ EXPECT_EQ(webrtc::kAgcAdaptiveDigital, agc_mode); } -TEST_F(AudioProcessingTest, DISABLED_CanTurnOnDigitalRxAcg) { +TEST_F(AudioProcessingTest, CanTurnOnDigitalRxAcg) { TryEnablingRxAgcWithMode(webrtc::kAgcAdaptiveDigital); TryEnablingRxAgcWithMode(webrtc::kAgcFixedDigital); } -TEST_F(AudioProcessingTest, DISABLED_CannotTurnOnAdaptiveAnalogRxAgc) { +TEST_F(AudioProcessingTest, CannotTurnOnAdaptiveAnalogRxAgc) { EXPECT_EQ(-1, voe_apm_->SetRxAgcStatus( channel_, true, webrtc::kAgcAdaptiveAnalog)); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/call_report_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/call_report_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/call_report_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/call_report_test.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/test/testsupport/fileutils.h" -#include "webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.h" - -class CallReportTest : public AfterStreamingFixture { -}; - -TEST_F(CallReportTest, ResetCallReportStatisticsFailsForBadInput) { - EXPECT_EQ(-1, voe_call_report_->ResetCallReportStatistics(-2)); - EXPECT_EQ(-1, voe_call_report_->ResetCallReportStatistics(1)); -} - -TEST_F(CallReportTest, ResetCallReportStatisticsSucceedsWithCorrectInput) { - EXPECT_EQ(0, voe_call_report_->ResetCallReportStatistics(channel_)); - EXPECT_EQ(0, voe_call_report_->ResetCallReportStatistics(-1)); -} - -TEST_F(CallReportTest, EchoMetricSummarySucceeds) { - EXPECT_EQ(0, voe_apm_->SetEcMetricsStatus(true)); - Sleep(1000); - - webrtc::EchoStatistics echo_statistics; - EXPECT_EQ(0, voe_call_report_->GetEchoMetricSummary(echo_statistics)); -} - -TEST_F(CallReportTest, GetRoundTripTimeSummaryReturnsAllMinusOnesIfRtcpIsOff) { - voe_rtp_rtcp_->SetRTCPStatus(channel_, false); - - webrtc::StatVal delays; - EXPECT_EQ(0, voe_call_report_->GetRoundTripTimeSummary(channel_, delays)); - EXPECT_EQ(-1, delays.average); - EXPECT_EQ(-1, delays.min); - EXPECT_EQ(-1, delays.max); -} - -// Flaky: https://code.google.com/p/webrtc/issues/detail?id=1719 -TEST_F(CallReportTest, DISABLED_GetRoundTripTimesReturnsValuesIfRtcpIsOn) { - voe_rtp_rtcp_->SetRTCPStatus(channel_, true); - Sleep(1000); - - webrtc::StatVal delays; - EXPECT_EQ(0, voe_call_report_->GetRoundTripTimeSummary(channel_, delays)); - EXPECT_NE(-1, delays.average); - EXPECT_NE(-1, delays.min); - EXPECT_NE(-1, delays.max); -} - -TEST_F(CallReportTest, WriteReportToFileFailsOnBadInput) { - EXPECT_EQ(-1, voe_call_report_->WriteReportToFile(NULL)); -} - -TEST_F(CallReportTest, WriteReportToFileSucceedsWithCorrectFilename) { - std::string output_path = webrtc::test::OutputPath(); - std::string report_filename = output_path + "call_report.txt"; - - EXPECT_EQ(0, voe_call_report_->WriteReportToFile(report_filename.c_str())); -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/codec_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/codec_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/codec_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/codec_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -130,66 +130,31 @@ EXPECT_EQ(webrtc::kVadConventional, vad_mode); } -// Tests requiring manual verification (although they do have some value -// without the manual verification): -TEST_F(CodecTest, ManualExtendedISACApisBehaveAsExpected) { - strcpy(codec_instance_.plname, "isac"); - codec_instance_.pltype = 103; - codec_instance_.plfreq = 16000; - codec_instance_.channels = 1; - // -1 here means "adaptive rate". - codec_instance_.rate = -1; - codec_instance_.pacsize = 480; - - EXPECT_EQ(0, voe_codec_->SetSendCodec(channel_, codec_instance_)); - - EXPECT_NE(0, voe_codec_->SetISACInitTargetRate(channel_, 5000)) << - "iSAC should reject rate 5000."; - EXPECT_NE(0, voe_codec_->SetISACInitTargetRate(channel_, 33000)) << - "iSAC should reject rate 33000."; - EXPECT_EQ(0, voe_codec_->SetISACInitTargetRate(channel_, 32000)); - - TEST_LOG("Ensure that the sound is good (iSAC, target = 32kbps)...\n"); - Sleep(3000); - - EXPECT_EQ(0, voe_codec_->SetISACInitTargetRate(channel_, 10000)); - TEST_LOG("Ensure that the sound is good (iSAC, target = 10kbps)...\n"); - Sleep(3000); - - EXPECT_EQ(0, voe_codec_->SetISACInitTargetRate(channel_, 10000, true)); - EXPECT_EQ(0, voe_codec_->SetISACInitTargetRate(channel_, 10000, false)); - EXPECT_EQ(0, voe_codec_->SetISACInitTargetRate(channel_, 0)); - TEST_LOG("Ensure that the sound is good (iSAC, target = default)...\n"); - Sleep(3000); - - TEST_LOG(" Testing SetISACMaxPayloadSize:\n"); - EXPECT_EQ(0, voe_base_->StopSend(channel_)); - EXPECT_NE(0, voe_codec_->SetISACMaxPayloadSize(channel_, 50)); - EXPECT_NE(0, voe_codec_->SetISACMaxPayloadSize(channel_, 650)); - EXPECT_EQ(0, voe_codec_->SetISACMaxPayloadSize(channel_, 120)); - EXPECT_EQ(0, voe_base_->StartSend(channel_)); - TEST_LOG("Ensure that the sound is good (iSAC, " - "max payload size = 100 bytes)...\n"); - Sleep(3000); - - TEST_LOG(" Testing SetISACMaxRate:\n"); - EXPECT_EQ(0, voe_base_->StopSend(channel_)); - EXPECT_EQ(0, voe_codec_->SetISACMaxPayloadSize(channel_, 400)); - EXPECT_EQ(0, voe_base_->StartSend(channel_)); - - EXPECT_EQ(0, voe_base_->StopSend(channel_)); - EXPECT_NE(0, voe_codec_->SetISACMaxRate(channel_, 31900)); - EXPECT_NE(0, voe_codec_->SetISACMaxRate(channel_, 53500)); - EXPECT_EQ(0, voe_codec_->SetISACMaxRate(channel_, 32000)); - EXPECT_EQ(0, voe_base_->StartSend(channel_)); - TEST_LOG("Ensure that the sound is good (iSAC, max rate = 32 kbps)...\n"); - Sleep(3000); - - EXPECT_EQ(0, voe_base_->StopSend(channel_)); +TEST_F(CodecTest, OpusMaxPlaybackRateCanBeSet) { + for (int i = 0; i < voe_codec_->NumOfCodecs(); ++i) { + voe_codec_->GetCodec(i, codec_instance_); + if (_stricmp("opus", codec_instance_.plname)) { + continue; + } + voe_codec_->SetSendCodec(channel_, codec_instance_); + // SetOpusMaxPlaybackRate can handle any integer as the bandwidth. Following + // tests some most commonly used numbers. + EXPECT_EQ(0, voe_codec_->SetOpusMaxPlaybackRate(channel_, 48000)); + EXPECT_EQ(0, voe_codec_->SetOpusMaxPlaybackRate(channel_, 32000)); + EXPECT_EQ(0, voe_codec_->SetOpusMaxPlaybackRate(channel_, 16000)); + EXPECT_EQ(0, voe_codec_->SetOpusMaxPlaybackRate(channel_, 8000)); + } +} - // Restore "no limitation". No, no limit, we reach for the sky. - EXPECT_EQ(0, voe_codec_->SetISACMaxRate(channel_, 53400)); - EXPECT_EQ(0, voe_base_->StartSend(channel_)); +TEST_F(CodecTest, OpusMaxPlaybackRateCannotBeSetForNonOpus) { + for (int i = 0; i < voe_codec_->NumOfCodecs(); ++i) { + voe_codec_->GetCodec(i, codec_instance_); + if (!_stricmp("opus", codec_instance_.plname)) { + continue; + } + voe_codec_->SetSendCodec(channel_, codec_instance_); + EXPECT_EQ(-1, voe_codec_->SetOpusMaxPlaybackRate(channel_, 16000)); + } } // TODO(xians, phoglund): Re-enable when issue 372 is resolved. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/dtmf_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/dtmf_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/dtmf_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/dtmf_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -52,24 +52,10 @@ EXPECT_EQ(0, voe_dtmf_->SendTelephoneEvent(channel_, 110, true)); } -#ifndef WEBRTC_IOS -TEST_F(DtmfTest, ManualCanDisableDtmfPlayoutExceptOnIphone) { - TEST_LOG("Disabling DTMF playout (no tone should be heard) \n"); - EXPECT_EQ(0, voe_dtmf_->SetDtmfPlayoutStatus(channel_, false)); - EXPECT_EQ(0, voe_dtmf_->SendTelephoneEvent(channel_, 0, true)); - Sleep(500); - - TEST_LOG("Enabling DTMF playout (tone should be heard) \n"); - EXPECT_EQ(0, voe_dtmf_->SetDtmfPlayoutStatus(channel_, true)); - EXPECT_EQ(0, voe_dtmf_->SendTelephoneEvent(channel_, 0, true)); - Sleep(500); -} -#endif - // This test modifies the DTMF payload type from the default 106 to 88 // and then runs through 16 DTMF out.of-band events. TEST_F(DtmfTest, ManualCanChangeDtmfPayloadType) { - webrtc::CodecInst codec_instance; + webrtc::CodecInst codec_instance = webrtc::CodecInst(); TEST_LOG("Changing DTMF payload type.\n"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/encryption_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/encryption_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/encryption_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/encryption_test.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/voice_engine/include/voe_encryption.h" -#include "webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.h" - -class BasicBitInverseEncryption : public webrtc::Encryption { - void encrypt(int channel_no, unsigned char* in_data, - unsigned char* out_data, int bytes_in, int* bytes_out); - void decrypt(int channel_no, unsigned char* in_data, - unsigned char* out_data, int bytes_in, int* bytes_out); - void encrypt_rtcp(int channel_no, unsigned char* in_data, - unsigned char* out_data, int bytes_in, int* bytes_out); - void decrypt_rtcp(int channel_no, unsigned char* in_data, - unsigned char* out_data, int bytes_in, int* bytes_out); -}; - -void BasicBitInverseEncryption::encrypt(int, unsigned char* in_data, - unsigned char* out_data, - int bytes_in, int* bytes_out) { - int i; - for (i = 0; i < bytes_in; i++) - out_data[i] = ~in_data[i]; - out_data[bytes_in] = 0; - out_data[bytes_in + 1] = 0; - *bytes_out = bytes_in + 2; -} - -void BasicBitInverseEncryption::decrypt(int, unsigned char* in_data, - unsigned char* out_data, - int bytes_in, int* bytes_out) { - int i; - for (i = 0; i < bytes_in; i++) - out_data[i] = ~in_data[i]; - *bytes_out = bytes_in - 2; -} - -void BasicBitInverseEncryption::encrypt_rtcp(int, unsigned char* in_data, - unsigned char* out_data, - int bytes_in, int* bytes_out) { - int i; - for (i = 0; i < bytes_in; i++) - out_data[i] = ~in_data[i]; - out_data[bytes_in] = 0; - out_data[bytes_in + 1] = 0; - *bytes_out = bytes_in + 2; -} - -void BasicBitInverseEncryption::decrypt_rtcp(int, unsigned char* in_data, - unsigned char* out_data, - int bytes_in, int* bytes_out) { - int i; - for (i = 0; i < bytes_in; i++) - out_data[i] = ~in_data[i]; - out_data[bytes_in] = 0; - out_data[bytes_in + 1] = 0; - *bytes_out = bytes_in + 2; -} - - -class EncryptionTest : public AfterStreamingFixture { -}; - -TEST_F(EncryptionTest, ManualBasicCorrectExternalEncryptionHasNoEffectOnVoice) { - BasicBitInverseEncryption basic_encryption; - - voe_encrypt_->RegisterExternalEncryption(channel_, basic_encryption); - - TEST_LOG("Registered external encryption, should still hear good audio.\n"); - Sleep(3000); - - voe_encrypt_->DeRegisterExternalEncryption(channel_); -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/file_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/file_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/file_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/file_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -16,6 +16,9 @@ class FileTest : public AfterStreamingFixture { protected: // Creates the string åäö.pcm. +// TODO(henrika): enable this test once CreateTrickyFilenameInUtf8 no longer +// prevents compilation on Windows. Likely webrtc/base can be used here. +#if 0 std::string CreateTrickyFilenameInUtf8() { char filename[16] = { (char)0xc3, (char)0xa5, (char)0xc3, (char)0xa4, @@ -23,8 +26,12 @@ static_cast(0) }; return std::string(filename) + ".pcm"; } +#endif // 0 }; +// TODO(henrika): enable this test once CreateTrickyFilenameInUtf8 no longer +// prevents compilation on Windows. Likely webrtc/base can be used here. +#if 0 TEST_F(FileTest, ManualRecordToFileForThreeSecondsAndPlayback) { if (!FLAGS_include_timing_dependent_tests) { TEST_LOG("Skipping test - running in slow execution environment...\n"); @@ -45,16 +52,13 @@ EXPECT_EQ(0, voe_file_->StartPlayingFileLocally( channel_, recording_filename.c_str())); - // Play the file to the user and ensure the is-playing-locally - // and scaling methods also work. The clip is 3 seconds long. + // Play the file to the user and ensure the is-playing-locally. + // The clip is 3 seconds long. Sleep(250); EXPECT_EQ(1, voe_file_->IsPlayingFileLocally(channel_)); Sleep(1500); - TEST_LOG("Decreasing level by 50%%.\n"); - EXPECT_EQ(0, voe_file_->ScaleLocalFilePlayout(channel_, 0.5f)); - Sleep(1500); - EXPECT_EQ(0, voe_file_->IsPlayingFileLocally(channel_)); } +#endif // 0 TEST_F(FileTest, ManualRecordPlayoutToWavFileForThreeSecondsAndPlayback) { webrtc::CodecInst send_codec; @@ -79,11 +83,4 @@ Sleep(2000); // We should still be playing since we're looping. EXPECT_EQ(1, voe_file_->IsPlayingFileAsMicrophone(channel_)); - - // Try scaling as well. - TEST_LOG("Decreasing level by 50%%.\n"); - EXPECT_EQ(0, voe_file_->ScaleFileAsMicrophonePlayout(channel_, 0.5f)); - Sleep(1000); - - EXPECT_EQ(0, voe_file_->StopPlayingFileAsMicrophone(channel_)); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/hardware_before_streaming_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/hardware_before_streaming_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/hardware_before_streaming_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/hardware_before_streaming_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -14,10 +14,6 @@ using namespace webrtc; -static const char* kNoDevicesErrorMessage = - "Either you have no recording / playout device " - "on your system, or the method failed."; - class HardwareBeforeStreamingTest : public AfterInitializationFixture { }; @@ -29,17 +25,6 @@ EXPECT_EQ(VE_ALREADY_INITED, voe_base_->LastError()); } -TEST_F(HardwareBeforeStreamingTest, - GetCPULoadSucceedsOnWindowsButNotOtherPlatforms) { - int load_percent; -#if defined(_WIN32) - EXPECT_EQ(0, voe_hardware_->GetCPULoad(load_percent)); -#else - EXPECT_NE(0, voe_hardware_->GetCPULoad(load_percent)) << - "Should fail on non-Windows platforms."; -#endif -} - // Tests that only apply to mobile: #ifdef WEBRTC_IOS @@ -51,6 +36,10 @@ // Tests that only apply to desktop: #if !defined(WEBRTC_IOS) & !defined(WEBRTC_ANDROID) +static const char* kNoDevicesErrorMessage = + "Either you have no recording / playout device " + "on your system, or the method failed."; + TEST_F(HardwareBeforeStreamingTest, GetPlayoutDeviceStatusReturnsTrue) { bool play_available = false; EXPECT_EQ(0, voe_hardware_->GetPlayoutDeviceStatus(play_available)); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/hardware_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/hardware_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/hardware_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/hardware_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -54,20 +54,6 @@ } #endif -#ifdef _WIN32 -TEST_F(HardwareTest, GetCpuLoadWorksOnWindows) { - int load = -1; - EXPECT_EQ(0, voe_hardware_->GetCPULoad(load)); - EXPECT_GE(0, load); - TEST_LOG("Voice engine CPU load = %d%%\n", load); -} -#else -TEST_F(HardwareTest, GetCpuLoadReturnsErrorOnNonWindowsPlatform) { - int load = -1; - EXPECT_EQ(-1, voe_hardware_->GetCPULoad(load)); -} -#endif - // Flakily hangs on Windows: code.google.com/p/webrtc/issues/detail?id=2179. TEST_F(HardwareTest, DISABLED_ON_WIN(BuiltInWasapiAECWorksForAudioWindowsCoreAudioLayer)) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/manual_hold_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/manual_hold_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/manual_hold_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/manual_hold_test.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.h" - -// Note: This class includes sleeps and requires manual verification. -class ManualHoldTest : public AfterStreamingFixture { -}; - -TEST_F(ManualHoldTest, SetOnHoldStatusBlockAudio) { - TEST_LOG("Channel not on hold => should hear audio.\n"); - Sleep(2000); - TEST_LOG("Put channel on hold => should *not* hear audio.\n"); - EXPECT_EQ(0, voe_base_->SetOnHoldStatus(channel_, true)); - Sleep(2000); - TEST_LOG("Remove on hold => should hear audio again.\n"); - EXPECT_EQ(0, voe_base_->SetOnHoldStatus(channel_, false)); - Sleep(2000); - TEST_LOG("Put sending on hold => should *not* hear audio.\n"); - EXPECT_EQ(0, voe_base_->SetOnHoldStatus(channel_, true, webrtc::kHoldSendOnly)); - Sleep(2000); -} - -TEST_F(ManualHoldTest, SetOnHoldStatusBlocksLocalFileAudio) { - TEST_LOG("Start playing a file locally => " - "you should now hear this file being played out.\n"); - voe_file_->StopPlayingFileAsMicrophone(channel_); - EXPECT_EQ(0, voe_file_->StartPlayingFileLocally( - channel_, resource_manager_.long_audio_file_path().c_str(), true)); - Sleep(2000); - - TEST_LOG("Put playing on hold => should *not* hear audio.\n"); - EXPECT_EQ(0, voe_base_->SetOnHoldStatus( - channel_, true, webrtc::kHoldPlayOnly)); - Sleep(2000); -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/mixing_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/mixing_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/mixing_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/mixing_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -20,8 +20,12 @@ const int16_t kLimiterHeadroom = 29204; // == -1 dbFS const int16_t kInt16Max = 0x7fff; -const int kSampleRateHz = 16000; +const int kPayloadType = 105; +const int kInSampleRateHz = 16000; // Input file taken as 16 kHz by default. +const int kRecSampleRateHz = 16000; // Recorded with 16 kHz L16. const int kTestDurationMs = 3000; +const CodecInst kCodecL16 = {kPayloadType, "L16", 16000, 160, 1, 256000}; +const CodecInst kCodecOpus = {kPayloadType, "opus", 48000, 960, 1, 32000}; } // namespace @@ -54,7 +58,8 @@ bool real_audio, int16_t input_value, int16_t max_output_value, - int16_t min_output_value) { + int16_t min_output_value, + const CodecInst& codec_inst) { ASSERT_LE(num_remote_streams_using_mono, num_remote_streams); if (real_audio) { @@ -77,7 +82,8 @@ remote_streams[i] = voe_base_->CreateChannel(); EXPECT_NE(-1, remote_streams[i]); } - StartRemoteStreams(remote_streams, num_remote_streams_using_mono); + StartRemoteStreams(remote_streams, num_remote_streams_using_mono, + codec_inst); TEST_LOG("Playing %d remote streams.\n", num_remote_streams); // Give it plenty of time to get started. @@ -87,6 +93,9 @@ EXPECT_EQ(0, voe_file_->StartRecordingPlayout(-1 /* record meeting */, output_filename_.c_str())); SleepMs(kTestDurationMs); + while (GetFileDurationMs(output_filename_.c_str()) < kTestDurationMs) { + SleepMs(200); + } EXPECT_EQ(0, voe_file_->StopRecordingPlayout(-1)); StopLocalStreams(local_streams); @@ -103,7 +112,7 @@ void GenerateInputFile(int16_t input_value) { FILE* input_file = fopen(input_filename_.c_str(), "wb"); ASSERT_TRUE(input_file != NULL); - for (int i = 0; i < kSampleRateHz / 1000 * (kTestDurationMs * 2); i++) { + for (int i = 0; i < kInSampleRateHz / 1000 * (kTestDurationMs * 2); i++) { ASSERT_EQ(1u, fwrite(&input_value, sizeof(input_value), 1, input_file)); } ASSERT_EQ(0, fclose(input_file)); @@ -126,7 +135,7 @@ // Ensure we've at least recorded half as much file as the duration of the // test. We have to use a relaxed tolerance here due to filesystem flakiness // on the bots. - ASSERT_GE((samples_read * 1000.0) / kSampleRateHz, 0.5 * kTestDurationMs); + ASSERT_GE((samples_read * 1000.0) / kRecSampleRateHz, kTestDurationMs); // Ensure we read the entire file. ASSERT_NE(0, feof(output_file)); ASSERT_EQ(0, fclose(output_file)); @@ -150,17 +159,8 @@ // Start up remote streams ("normal" participants). void StartRemoteStreams(const std::vector& streams, - int num_remote_streams_using_mono) { - // Use L16 at 16kHz to minimize distortion (file recording is 16kHz and - // resampling will cause distortion). - CodecInst codec_inst; - strcpy(codec_inst.plname, "L16"); - codec_inst.channels = 1; - codec_inst.plfreq = kSampleRateHz; - codec_inst.pltype = 105; - codec_inst.pacsize = codec_inst.plfreq / 100; - codec_inst.rate = codec_inst.plfreq * sizeof(int16_t) * 8; // 8 bits/byte. - + int num_remote_streams_using_mono, + const CodecInst& codec_inst) { for (int i = 0; i < num_remote_streams_using_mono; ++i) { // Add some delay between starting up the channels in order to give them // different energies in the "real audio" test and hopefully exercise @@ -170,10 +170,11 @@ } // The remainder of the streams will use stereo. - codec_inst.channels = 2; - codec_inst.pltype++; + CodecInst codec_inst_stereo = codec_inst; + codec_inst_stereo.channels = 2; + codec_inst_stereo.pltype++; for (size_t i = num_remote_streams_using_mono; i < streams.size(); ++i) { - StartRemoteStream(streams[i], codec_inst, 1234 + 2 * i); + StartRemoteStream(streams[i], codec_inst_stereo, 1234 + 2 * i); } } @@ -199,6 +200,17 @@ } } + int GetFileDurationMs(const char* file_name) { + FILE* fid = fopen(file_name, "rb"); + EXPECT_FALSE(fid == NULL); + fseek(fid, 0, SEEK_END); + int size = ftell(fid); + EXPECT_NE(-1, size); + fclose(fid); + // Divided by 2 due to 2 bytes/sample. + return size * 1000 / kRecSampleRateHz / 2; + } + std::string input_filename_; const std::string output_filename_; LoopBackTransport* transport_; @@ -208,7 +220,11 @@ // somewhat more realistic scenario using real audio. It can at least hunt for // asserts and crashes. TEST_F(MixingTest, MixManyChannelsForStress) { - RunMixingTest(10, 0, 10, true, 0, 0, 0); + RunMixingTest(10, 0, 10, true, 0, 0, 0, kCodecL16); +} + +TEST_F(MixingTest, MixManyChannelsForStressOpus) { + RunMixingTest(10, 0, 10, true, 0, 0, 0, kCodecOpus); } // These tests assume a maximum of three mixed participants. We typically allow @@ -218,7 +234,7 @@ const int16_t kInputValue = 1000; const int16_t kExpectedOutput = kInputValue * 3; RunMixingTest(4, 0, 4, false, kInputValue, 1.1 * kExpectedOutput, - 0.9 * kExpectedOutput); + 0.9 * kExpectedOutput, kCodecL16); } // Ensure the mixing saturation protection is working. We can do this because @@ -231,7 +247,7 @@ ASSERT_GT(kInputValue * 3, kInt16Max); ASSERT_LT(1.1 * kExpectedOutput, kInt16Max); RunMixingTest(3, 0, 3, false, kInputValue, 1.1 * kExpectedOutput, - 0.9 * kExpectedOutput); + 0.9 * kExpectedOutput, kCodecL16); } TEST_F(MixingTest, SaturationProtectionHasNoEffectOnOneChannel) { @@ -241,21 +257,21 @@ ASSERT_GT(0.95 * kExpectedOutput, kLimiterHeadroom); // Tighter constraints are required here to properly test this. RunMixingTest(1, 0, 1, false, kInputValue, kExpectedOutput, - 0.95 * kExpectedOutput); + 0.95 * kExpectedOutput, kCodecL16); } TEST_F(MixingTest, VerifyAnonymousAndNormalParticipantMixing) { const int16_t kInputValue = 1000; const int16_t kExpectedOutput = kInputValue * 2; RunMixingTest(1, 1, 1, false, kInputValue, 1.1 * kExpectedOutput, - 0.9 * kExpectedOutput); + 0.9 * kExpectedOutput, kCodecL16); } TEST_F(MixingTest, AnonymousParticipantsAreAlwaysMixed) { const int16_t kInputValue = 1000; const int16_t kExpectedOutput = kInputValue * 4; RunMixingTest(3, 1, 3, false, kInputValue, 1.1 * kExpectedOutput, - 0.9 * kExpectedOutput); + 0.9 * kExpectedOutput, kCodecL16); } TEST_F(MixingTest, VerifyStereoAndMonoMixing) { @@ -263,7 +279,7 @@ const int16_t kExpectedOutput = kInputValue * 2; RunMixingTest(2, 0, 1, false, kInputValue, 1.1 * kExpectedOutput, // Lower than 0.9 due to observed flakiness on bots. - 0.8 * kExpectedOutput); + 0.8 * kExpectedOutput, kCodecL16); } } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/neteq_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/neteq_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/neteq_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/neteq_test.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.h" - -class NetEQTest : public AfterStreamingFixture { - protected: - void SetUp() { - additional_channel_[0] = voe_base_->CreateChannel(); - additional_channel_[1] = voe_base_->CreateChannel(); - } - - void TearDown() { - voe_base_->DeleteChannel(additional_channel_[0]); - voe_base_->DeleteChannel(additional_channel_[1]); - } - - int additional_channel_[2]; -}; - -TEST_F(NetEQTest, GetNetEQPlayoutModeReturnsDefaultModeByDefault) { - webrtc::NetEqModes mode; - EXPECT_EQ(0, voe_base_->GetNetEQPlayoutMode(channel_, mode)); - EXPECT_EQ(webrtc::kNetEqDefault, mode); -} - -TEST_F(NetEQTest, SetNetEQPlayoutModeActuallySetsTheModeForTheChannel) { - webrtc::NetEqModes mode; - // Set for the first channel but leave the others. - EXPECT_EQ(0, voe_base_->SetNetEQPlayoutMode(channel_, webrtc::kNetEqFax)); - EXPECT_EQ(0, voe_base_->GetNetEQPlayoutMode(channel_, mode)); - EXPECT_EQ(webrtc::kNetEqFax, mode); - - EXPECT_EQ(0, voe_base_->GetNetEQPlayoutMode(additional_channel_[0], mode)); - EXPECT_EQ(webrtc::kNetEqDefault, mode); - EXPECT_EQ(0, voe_base_->GetNetEQPlayoutMode(additional_channel_[1], mode)); - EXPECT_EQ(webrtc::kNetEqDefault, mode); - - // Set the second channel, leave the others. - EXPECT_EQ(0, voe_base_->SetNetEQPlayoutMode( - additional_channel_[0], webrtc::kNetEqStreaming)); - EXPECT_EQ(0, voe_base_->GetNetEQPlayoutMode(additional_channel_[0], mode)); - EXPECT_EQ(webrtc::kNetEqStreaming, mode); - - EXPECT_EQ(0, voe_base_->GetNetEQPlayoutMode(channel_, mode)); - EXPECT_EQ(webrtc::kNetEqFax, mode); - EXPECT_EQ(0, voe_base_->GetNetEQPlayoutMode(additional_channel_[1], mode)); - EXPECT_EQ(webrtc::kNetEqDefault, mode); - - // Set the third channel, leave the others. - EXPECT_EQ(0, voe_base_->SetNetEQPlayoutMode( - additional_channel_[1], webrtc::kNetEqOff)); - EXPECT_EQ(0, voe_base_->GetNetEQPlayoutMode(additional_channel_[1], mode)); - EXPECT_EQ(webrtc::kNetEqOff, mode); - - EXPECT_EQ(0, voe_base_->GetNetEQPlayoutMode(channel_, mode)); - EXPECT_EQ(webrtc::kNetEqFax, mode); - EXPECT_EQ(0, voe_base_->GetNetEQPlayoutMode(additional_channel_[0], mode)); - EXPECT_EQ(webrtc::kNetEqStreaming, mode); -} - -TEST_F(NetEQTest, ManualSetEQPlayoutModeStillProducesOkAudio) { - EXPECT_EQ(0, voe_base_->SetNetEQPlayoutMode(channel_, webrtc::kNetEqDefault)); - TEST_LOG("NetEQ default playout mode enabled => should hear OK audio.\n"); - Sleep(2000); - - EXPECT_EQ(0, voe_base_->SetNetEQPlayoutMode( - channel_, webrtc::kNetEqStreaming)); - TEST_LOG("NetEQ streaming playout mode enabled => should hear OK audio.\n"); - Sleep(2000); - - EXPECT_EQ(0, voe_base_->SetNetEQPlayoutMode(channel_, webrtc::kNetEqFax)); - TEST_LOG("NetEQ fax playout mode enabled => should hear OK audio.\n"); - Sleep(2000); - - EXPECT_EQ(0, voe_base_->SetNetEQPlayoutMode(channel_, webrtc::kNetEqOff)); - TEST_LOG("NetEQ off playout mode enabled => should hear OK audio.\n"); - Sleep(2000); -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/network_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/network_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/network_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/network_test.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/voice_engine/include/mock/mock_voe_connection_observer.h" -#include "webrtc/voice_engine/include/mock/mock_voe_observer.h" -#include "webrtc/voice_engine/test/auto_test/fakes/fake_external_transport.h" -#include "webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.h" -#include "webrtc/voice_engine/test/auto_test/voe_standard_test.h" -#include "webrtc/voice_engine/test/auto_test/voe_test_interface.h" - -class NetworkTest : public AfterStreamingFixture { -}; - -using ::testing::Between; - -TEST_F(NetworkTest, CanSwitchToExternalTransport) { - EXPECT_EQ(0, voe_base_->StopReceive(channel_)); - EXPECT_EQ(0, voe_base_->DeleteChannel(channel_)); - channel_ = voe_base_->CreateChannel(); - - FakeExternalTransport external_transport(voe_network_); - EXPECT_EQ(0, voe_network_->RegisterExternalTransport( - channel_, external_transport)); - - EXPECT_EQ(0, voe_base_->StartReceive(channel_)); - EXPECT_EQ(0, voe_base_->StartSend(channel_)); - EXPECT_EQ(0, voe_base_->StartPlayout(channel_)); - - Sleep(1000); - - EXPECT_EQ(0, voe_base_->StopSend(channel_)); - EXPECT_EQ(0, voe_base_->StopPlayout(channel_)); - EXPECT_EQ(0, voe_base_->StopReceive(channel_)); - - EXPECT_EQ(0, voe_network_->DeRegisterExternalTransport(channel_)); -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_before_streaming_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_before_streaming_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_before_streaming_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_before_streaming_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -48,9 +48,3 @@ EXPECT_EQ(0, voe_rtp_rtcp_->GetLocalSSRC(channel_, result)); EXPECT_EQ(1234u, result); } - -TEST_F(RtpRtcpBeforeStreamingTest, GetLastRemoteTimeStamp) { - uint32_t timestamp; - EXPECT_EQ(0, voe_rtp_rtcp_->GetLastRemoteTimeStamp(channel_, ×tamp)); - EXPECT_EQ(0u, timestamp); -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_extensions.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_extensions.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_extensions.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_extensions.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" +#include "webrtc/system_wrappers/interface/atomic32.h" +#include "webrtc/system_wrappers/interface/sleep.h" +#include "webrtc/video_engine/include/vie_network.h" +#include "webrtc/voice_engine/test/auto_test/fixtures/before_streaming_fixture.h" + +using ::testing::_; +using ::testing::AtLeast; +using ::testing::Eq; +using ::testing::Field; + +class ExtensionVerifyTransport : public webrtc::Transport { + public: + ExtensionVerifyTransport() + : parser_(webrtc::RtpHeaderParser::Create()), + received_packets_(0), + bad_packets_(0), + audio_level_id_(-1), + absolute_sender_time_id_(-1) {} + + virtual int SendPacket(int channel, const void* data, int len) OVERRIDE { + webrtc::RTPHeader header; + if (parser_->Parse(reinterpret_cast(data), + static_cast(len), + &header)) { + bool ok = true; + if (audio_level_id_ >= 0 && + !header.extension.hasAudioLevel) { + ok = false; + } + if (absolute_sender_time_id_ >= 0 && + !header.extension.hasAbsoluteSendTime) { + ok = false; + } + if (!ok) { + // bad_packets_ count packets we expected to have an extension but + // didn't have one. + ++bad_packets_; + } + } + // received_packets_ count all packets we receive. + ++received_packets_; + return len; + } + + virtual int SendRTCPPacket(int channel, const void* data, int len) OVERRIDE { + return len; + } + + void SetAudioLevelId(int id) { + audio_level_id_ = id; + parser_->RegisterRtpHeaderExtension(webrtc::kRtpExtensionAudioLevel, id); + } + + void SetAbsoluteSenderTimeId(int id) { + absolute_sender_time_id_ = id; + parser_->RegisterRtpHeaderExtension(webrtc::kRtpExtensionAbsoluteSendTime, + id); + } + + bool Wait() { + // Wait until we've received to specified number of packets. + while (received_packets_.Value() < kPacketsExpected) { + webrtc::SleepMs(kSleepIntervalMs); + } + // Check whether any where 'bad' (didn't contain an extension when they + // where supposed to). + return bad_packets_.Value() == 0; + } + + private: + enum { + kPacketsExpected = 10, + kSleepIntervalMs = 10 + }; + webrtc::scoped_ptr parser_; + webrtc::Atomic32 received_packets_; + webrtc::Atomic32 bad_packets_; + int audio_level_id_; + int absolute_sender_time_id_; +}; + +class SendRtpRtcpHeaderExtensionsTest : public BeforeStreamingFixture { + protected: + virtual void SetUp() OVERRIDE { + EXPECT_EQ(0, voe_network_->DeRegisterExternalTransport(channel_)); + EXPECT_EQ(0, voe_network_->RegisterExternalTransport(channel_, + verifying_transport_)); + } + virtual void TearDown() OVERRIDE { + PausePlaying(); + } + + ExtensionVerifyTransport verifying_transport_; +}; + +TEST_F(SendRtpRtcpHeaderExtensionsTest, SentPacketsIncludeNoAudioLevel) { + verifying_transport_.SetAudioLevelId(0); + ResumePlaying(); + EXPECT_FALSE(verifying_transport_.Wait()); +} + +TEST_F(SendRtpRtcpHeaderExtensionsTest, SentPacketsIncludeAudioLevel) { + EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAudioLevelIndicationStatus(channel_, true, + 9)); + verifying_transport_.SetAudioLevelId(9); + ResumePlaying(); + EXPECT_TRUE(verifying_transport_.Wait()); +} + +TEST_F(SendRtpRtcpHeaderExtensionsTest, SentPacketsIncludeNoAbsoluteSenderTime) +{ + verifying_transport_.SetAbsoluteSenderTimeId(0); + ResumePlaying(); + EXPECT_FALSE(verifying_transport_.Wait()); +} + +TEST_F(SendRtpRtcpHeaderExtensionsTest, SentPacketsIncludeAbsoluteSenderTime) { + EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAbsoluteSenderTimeStatus(channel_, true, + 11)); + verifying_transport_.SetAbsoluteSenderTimeId(11); + ResumePlaying(); + EXPECT_TRUE(verifying_transport_.Wait()); +} + +TEST_F(SendRtpRtcpHeaderExtensionsTest, SentPacketsIncludeAllExtensions1) { + EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAudioLevelIndicationStatus(channel_, true, + 9)); + EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAbsoluteSenderTimeStatus(channel_, true, + 11)); + verifying_transport_.SetAudioLevelId(9); + verifying_transport_.SetAbsoluteSenderTimeId(11); + ResumePlaying(); + EXPECT_TRUE(verifying_transport_.Wait()); +} + +TEST_F(SendRtpRtcpHeaderExtensionsTest, SentPacketsIncludeAllExtensions2) { + EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAbsoluteSenderTimeStatus(channel_, true, + 3)); + EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAudioLevelIndicationStatus(channel_, true, + 9)); + verifying_transport_.SetAbsoluteSenderTimeId(3); + // Don't register audio level with header parser - unknown extensions should + // be ignored when parsing. + ResumePlaying(); + EXPECT_TRUE(verifying_transport_.Wait()); +} + +class MockViENetwork : public webrtc::ViENetwork { + public: + MockViENetwork() {} + virtual ~MockViENetwork() {} + + MOCK_METHOD0(Release, int()); + MOCK_METHOD2(SetNetworkTransmissionState, void(const int, const bool)); + MOCK_METHOD2(RegisterSendTransport, int(const int, webrtc::Transport&)); + MOCK_METHOD1(DeregisterSendTransport, int(const int)); + MOCK_METHOD4(ReceivedRTPPacket, int(const int, const void*, const int, + const webrtc::PacketTime&)); + MOCK_METHOD3(ReceivedRTCPPacket, int(const int, const void*, const int)); + MOCK_METHOD2(SetMTU, int(int, unsigned int)); + MOCK_METHOD4(ReceivedBWEPacket, int(const int, int64_t, int, + const webrtc::RTPHeader&)); +}; + +class ReceiveRtpRtcpHeaderExtensionsTest : public BeforeStreamingFixture { + protected: + virtual void SetUp() OVERRIDE { + EXPECT_EQ(0, + voe_rtp_rtcp_->SetSendAbsoluteSenderTimeStatus(channel_, true, 11)); + EXPECT_EQ(0, + voe_rtp_rtcp_->SetReceiveAbsoluteSenderTimeStatus(channel_, true, 11)); + } + + enum { + kVideoChannelId1 = 667, + kVideoChannelId2 = 668 + }; + MockViENetwork mock_network_; +}; + +TEST_F(ReceiveRtpRtcpHeaderExtensionsTest, ReceiveASTDisabled) { + ResumePlaying(); + Sleep(500); +} + +TEST_F(ReceiveRtpRtcpHeaderExtensionsTest, ReceiveASTFailSetTarget) { + EXPECT_CALL(mock_network_, Release()).Times(1); + EXPECT_EQ(-1, voe_rtp_rtcp_->SetVideoEngineBWETarget(-1, &mock_network_, + kVideoChannelId1)); + ResumePlaying(); +} + +TEST_F(ReceiveRtpRtcpHeaderExtensionsTest, ReceiveASTEnabled) { + EXPECT_CALL(mock_network_, Release()).Times(1); + EXPECT_CALL(mock_network_, ReceivedBWEPacket(kVideoChannelId1, _, _, + Field(&webrtc::RTPHeader::extension, + Field(&webrtc::RTPHeaderExtension::hasAbsoluteSendTime, Eq(true))))) + .Times(AtLeast(1)); + EXPECT_EQ(0, voe_rtp_rtcp_->SetVideoEngineBWETarget(channel_, &mock_network_, + kVideoChannelId1)); + ResumePlaying(); + Sleep(500); + EXPECT_EQ(0, voe_rtp_rtcp_->SetVideoEngineBWETarget(channel_, NULL, -1)); +} + +TEST_F(ReceiveRtpRtcpHeaderExtensionsTest, ReceiveASTEnabledBadExtensionId) { + EXPECT_CALL(mock_network_, Release()).Times(1); + EXPECT_CALL(mock_network_, ReceivedBWEPacket(kVideoChannelId1, _, _, + Field(&webrtc::RTPHeader::extension, + Field(&webrtc::RTPHeaderExtension::hasAbsoluteSendTime, Eq(false))))) + .Times(AtLeast(1)); + EXPECT_EQ(0, voe_rtp_rtcp_->SetReceiveAbsoluteSenderTimeStatus(channel_, true, + 1)); + EXPECT_EQ(0, voe_rtp_rtcp_->SetVideoEngineBWETarget(channel_, &mock_network_, + kVideoChannelId1)); + ResumePlaying(); + Sleep(500); + EXPECT_EQ(0, voe_rtp_rtcp_->SetVideoEngineBWETarget(channel_, NULL, -1)); +} + +TEST_F(ReceiveRtpRtcpHeaderExtensionsTest, ReceiveASTEnabledNotSending) { + EXPECT_CALL(mock_network_, Release()).Times(1); + EXPECT_CALL(mock_network_, ReceivedBWEPacket(kVideoChannelId1, _, _, + Field(&webrtc::RTPHeader::extension, + Field(&webrtc::RTPHeaderExtension::hasAbsoluteSendTime, Eq(false))))) + .Times(AtLeast(1)); + EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAbsoluteSenderTimeStatus(channel_, false, + 11)); + EXPECT_EQ(0, voe_rtp_rtcp_->SetVideoEngineBWETarget(channel_, &mock_network_, + kVideoChannelId1)); + ResumePlaying(); + Sleep(500); + EXPECT_EQ(0, voe_rtp_rtcp_->SetVideoEngineBWETarget(channel_, NULL, -1)); +} + +TEST_F(ReceiveRtpRtcpHeaderExtensionsTest, ReceiveASTEnabledNotReceiving) { + EXPECT_CALL(mock_network_, Release()).Times(1); + EXPECT_CALL(mock_network_, ReceivedBWEPacket(kVideoChannelId1, _, _, + Field(&webrtc::RTPHeader::extension, + Field(&webrtc::RTPHeaderExtension::hasAbsoluteSendTime, Eq(false))))) + .Times(AtLeast(1)); + EXPECT_EQ(0, voe_rtp_rtcp_->SetReceiveAbsoluteSenderTimeStatus(channel_, + false, 11)); + EXPECT_EQ(0, voe_rtp_rtcp_->SetVideoEngineBWETarget(channel_, &mock_network_, + kVideoChannelId1)); + ResumePlaying(); + Sleep(500); + EXPECT_EQ(0, voe_rtp_rtcp_->SetVideoEngineBWETarget(channel_, NULL, -1)); +} + +TEST_F(ReceiveRtpRtcpHeaderExtensionsTest, ReceiveASTSwitchViENetwork) { + MockViENetwork mock_network_2; + EXPECT_CALL(mock_network_2, Release()).Times(1); + EXPECT_CALL(mock_network_2, ReceivedBWEPacket(kVideoChannelId1, _, _, + Field(&webrtc::RTPHeader::extension, + Field(&webrtc::RTPHeaderExtension::hasAbsoluteSendTime, Eq(true))))) + .Times(AtLeast(1)); + EXPECT_CALL(mock_network_, Release()).Times(1); + EXPECT_CALL(mock_network_, ReceivedBWEPacket(kVideoChannelId1, _, _, + Field(&webrtc::RTPHeader::extension, + Field(&webrtc::RTPHeaderExtension::hasAbsoluteSendTime, Eq(true))))) + .Times(AtLeast(1)); + EXPECT_EQ(0, voe_rtp_rtcp_->SetVideoEngineBWETarget(channel_, &mock_network_2, + kVideoChannelId1)); + ResumePlaying(); + Sleep(500); + EXPECT_EQ(0, voe_rtp_rtcp_->SetVideoEngineBWETarget(channel_, &mock_network_, + kVideoChannelId1)); + Sleep(500); + EXPECT_EQ(0, voe_rtp_rtcp_->SetVideoEngineBWETarget(channel_, NULL, -1)); +} + +TEST_F(ReceiveRtpRtcpHeaderExtensionsTest, ReceiveASTSwitchVideoChannel) { + EXPECT_CALL(mock_network_, Release()).Times(2); + EXPECT_CALL(mock_network_, ReceivedBWEPacket(kVideoChannelId1, _, _, + Field(&webrtc::RTPHeader::extension, + Field(&webrtc::RTPHeaderExtension::hasAbsoluteSendTime, Eq(true))))) + .Times(AtLeast(1)); + EXPECT_CALL(mock_network_, ReceivedBWEPacket(kVideoChannelId2, _, _, + Field(&webrtc::RTPHeader::extension, + Field(&webrtc::RTPHeaderExtension::hasAbsoluteSendTime, Eq(true))))) + .Times(AtLeast(1)); + EXPECT_EQ(0, voe_rtp_rtcp_->SetVideoEngineBWETarget(channel_, &mock_network_, + kVideoChannelId1)); + ResumePlaying(); + Sleep(500); + EXPECT_EQ(0, voe_rtp_rtcp_->SetVideoEngineBWETarget(channel_, &mock_network_, + kVideoChannelId2)); + Sleep(500); + EXPECT_EQ(0, voe_rtp_rtcp_->SetVideoEngineBWETarget(channel_, NULL, -1)); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -153,86 +153,6 @@ EXPECT_EQ(local_ssrc, ssrc); } -TEST_F(RtpRtcpTest, RtcpApplicationDefinedPacketsCanBeSentAndReceived) { - RtcpAppHandler rtcp_app_handler; - EXPECT_EQ(0, voe_rtp_rtcp_->RegisterRTCPObserver( - channel_, rtcp_app_handler)); - - // Send data aligned to 32 bytes. - const char* data = "application-dependent data------"; - unsigned short data_length = strlen(data); - unsigned int data_name = 0x41424344; // 'ABCD' in ascii - unsigned char data_subtype = 1; - - EXPECT_EQ(0, voe_rtp_rtcp_->SendApplicationDefinedRTCPPacket( - channel_, data_subtype, data_name, data, data_length)); - - // Ensure the RTP-RTCP process gets scheduled. - Sleep(1000); - - // Ensure we received the data in the callback. - ASSERT_EQ(data_length, rtcp_app_handler.length_in_bytes_); - EXPECT_EQ(0, memcmp(data, rtcp_app_handler.data_, data_length)); - EXPECT_EQ(data_name, rtcp_app_handler.name_); - EXPECT_EQ(data_subtype, rtcp_app_handler.sub_type_); - - EXPECT_EQ(0, voe_rtp_rtcp_->DeRegisterRTCPObserver(channel_)); -} - -TEST_F(RtpRtcpTest, DisabledRtcpObserverDoesNotReceiveData) { - RtcpAppHandler rtcp_app_handler; - EXPECT_EQ(0, voe_rtp_rtcp_->RegisterRTCPObserver( - channel_, rtcp_app_handler)); - - // Put observer in a known state before de-registering. - rtcp_app_handler.Reset(); - - EXPECT_EQ(0, voe_rtp_rtcp_->DeRegisterRTCPObserver(channel_)); - - const char* data = "whatever"; - EXPECT_EQ(0, voe_rtp_rtcp_->SendApplicationDefinedRTCPPacket( - channel_, 1, 0x41424344, data, strlen(data))); - - // Ensure the RTP-RTCP process gets scheduled. - Sleep(1000); - - // Ensure we received no data. - EXPECT_EQ(0u, rtcp_app_handler.name_); - EXPECT_EQ(0u, rtcp_app_handler.sub_type_); -} - -TEST_F(RtpRtcpTest, InsertExtraRTPPacketDealsWithInvalidArguments) { - const char payload_data[8] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' }; - - EXPECT_EQ(-1, voe_rtp_rtcp_->InsertExtraRTPPacket( - -1, 0, false, payload_data, 8)) << - "Should reject: invalid channel."; - EXPECT_EQ(-1, voe_rtp_rtcp_->InsertExtraRTPPacket( - channel_, -1, false, payload_data, 8)) << - "Should reject: invalid payload type."; - EXPECT_EQ(-1, voe_rtp_rtcp_->InsertExtraRTPPacket( - channel_, 128, false, payload_data, 8)) << - "Should reject: invalid payload type."; - EXPECT_EQ(-1, voe_rtp_rtcp_->InsertExtraRTPPacket( - channel_, 99, false, NULL, 8)) << - "Should reject: bad pointer."; - EXPECT_EQ(-1, voe_rtp_rtcp_->InsertExtraRTPPacket( - channel_, 99, false, payload_data, 1500 - 28 + 1)) << - "Should reject: invalid size."; -} - -TEST_F(RtpRtcpTest, DISABLED_CanTransmitExtraRtpPacketsWithoutError) { - const char payload_data[8] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' }; - - for (int i = 0; i < 128; ++i) { - // Try both with and without the marker bit set - EXPECT_EQ(0, voe_rtp_rtcp_->InsertExtraRTPPacket( - channel_, i, false, payload_data, 8)); - EXPECT_EQ(0, voe_rtp_rtcp_->InsertExtraRTPPacket( - channel_, i, true, payload_data, 8)); - } -} - // TODO(xians, phoglund): Re-enable when issue 372 is resolved. TEST_F(RtpRtcpTest, DISABLED_CanCreateRtpDumpFilesWithoutError) { // Create two RTP dump files (3 seconds long). You can verify these after @@ -252,27 +172,3 @@ EXPECT_EQ(0, voe_rtp_rtcp_->StopRTPDump(channel_, webrtc::kRtpOutgoing)); } -TEST_F(RtpRtcpTest, ObserverGetsNotifiedOnSsrcChange) { - TestRtpObserver rtcp_observer; - EXPECT_EQ(0, voe_rtp_rtcp_->RegisterRTPObserver( - channel_, rtcp_observer)); - - unsigned int new_ssrc = 7777; - EXPECT_EQ(0, voe_base_->StopSend(channel_)); - rtcp_observer.SetIncomingSsrc(new_ssrc); - EXPECT_EQ(0, voe_rtp_rtcp_->SetLocalSSRC(channel_, new_ssrc)); - EXPECT_EQ(0, voe_base_->StartSend(channel_)); - - rtcp_observer.WaitForChangedSsrc(); - - // Now try another SSRC. - unsigned int newer_ssrc = 1717; - EXPECT_EQ(0, voe_base_->StopSend(channel_)); - rtcp_observer.SetIncomingSsrc(newer_ssrc); - EXPECT_EQ(0, voe_rtp_rtcp_->SetLocalSSRC(channel_, newer_ssrc)); - EXPECT_EQ(0, voe_base_->StartSend(channel_)); - - rtcp_observer.WaitForChangedSsrc(); - - EXPECT_EQ(0, voe_rtp_rtcp_->DeRegisterRTPObserver(channel_)); -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/video_sync_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/video_sync_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/video_sync_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/video_sync_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -125,9 +125,7 @@ CheckEstimatesConvergeReasonablyWell(kMinimumReasonableDelayEstimateMs); } -#if !defined(WEBRTC_ANDROID) TEST_F(VideoSyncTest, CanGetPlayoutBufferSize) { int ignored; EXPECT_EQ(0, voe_vsync_->GetPlayoutBufferSize(ignored)); } -#endif // !ANDROID diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/volume_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/volume_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/volume_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/volume_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -25,11 +25,75 @@ } // namespace class VolumeTest : public AfterStreamingFixture { + public: + void SetAndVerifyMicVolume(unsigned int volume) { + bool success = voe_volume_control_->SetMicVolume(volume) == 0; +#if !defined(WEBRTC_LINUX) + EXPECT_TRUE(success); +#endif + if (!success) { + TEST_LOG("Failed to set microphone volume to %u.\n", volume); + return; + } + + unsigned int test_volume = 1000; + success = voe_volume_control_->GetMicVolume(test_volume) == 0; +#if !defined(WEBRTC_LINUX) + EXPECT_TRUE(success); +#endif + if (success) { + EXPECT_EQ(volume, test_volume); + } else { + TEST_LOG("Failed to get the microphone volume."); + EXPECT_EQ(1000u, test_volume); + } + } + + void SetAndVerifyInputMute(bool enable) { + bool success = voe_volume_control_->SetInputMute(channel_, enable) == 0; +#if !defined(WEBRTC_LINUX) + EXPECT_TRUE(success); +#endif + if (!success) { + TEST_LOG("Failed to %smute input.\n", enable ? "" : "un"); + return; + } + + bool is_muted = !enable; + success = voe_volume_control_->GetInputMute(channel_, is_muted) == 0; +#if !defined(WEBRTC_LINUX) + EXPECT_TRUE(success); +#endif + if (success) { + EXPECT_EQ(enable, is_muted); + } else { + TEST_LOG("Failed to mute the input."); + EXPECT_NE(enable, is_muted); + } + } }; -// TODO(phoglund): a number of tests are disabled here on Linux, all pending -// investigation in -// http://code.google.com/p/webrtc/issues/detail?id=367 +// Some tests are flaky on Linux (Pulse Audio), which boils down to some system +// values not being acquired in time. In Pulse Audio we make one retry if +// needed, but if we fail then, a -1 is returned propagating up through VoE. +// To avoid possible bugs slipping through on other platforms we make adequate +// changes on Linux only. +TEST_F(VolumeTest, VerifyCorrectErrorReturns) { + // All tests run on correct initialization which eliminates one possible error + // return. In addition, we assume the audio_device returning values without + // error, which eliminates another potential error. + // Left to verify are sanity checks of set parameters. + + // Valid volume range: [0, 255] + EXPECT_EQ(-1, voe_volume_control_->SetSpeakerVolume(256)); + EXPECT_EQ(-1, voe_volume_control_->SetMicVolume(256)); + + // Valid panning rage: [0, 1] + EXPECT_EQ(-1, voe_volume_control_->SetOutputVolumePan(channel_, -0.1f, 0.5f)); + EXPECT_EQ(-1, voe_volume_control_->SetOutputVolumePan(channel_, 1.1f, 0.5f)); + EXPECT_EQ(-1, voe_volume_control_->SetOutputVolumePan(channel_, 0.5f, -0.1f)); + EXPECT_EQ(-1, voe_volume_control_->SetOutputVolumePan(channel_, 0.5f, 1.1f)); +} TEST_F(VolumeTest, DefaultSpeakerVolumeIsAtMost255) { unsigned int volume = 1000; @@ -95,31 +159,46 @@ Sleep(1000); } -#if !defined(WEBRTC_IOS) - -TEST_F(VolumeTest, DISABLED_ON_LINUX(DefaultMicrophoneVolumeIsAtMost255)) { +TEST_F(VolumeTest, DefaultMicrophoneVolumeIsAtMost255) { unsigned int volume = 1000; - EXPECT_EQ(0, voe_volume_control_->GetMicVolume(volume)); - EXPECT_LE(volume, 255u); + bool could_get_mic_volume = voe_volume_control_->GetMicVolume(volume) == 0; +#if !defined(WEBRTC_LINUX) + EXPECT_TRUE(could_get_mic_volume); +#endif + if (could_get_mic_volume) { + EXPECT_LE(volume, 255u); + } else { + TEST_LOG("Failed to get the microphone volume."); + EXPECT_EQ(1000u, volume); + } } -TEST_F(VolumeTest, DISABLED_ON_LINUX( - ManualRequiresMicrophoneCanSetMicrophoneVolumeWithAcgOff)) { +TEST_F(VolumeTest, ManualRequiresMicrophoneCanSetMicrophoneVolumeWithAgcOff) { SwitchToManualMicrophone(); EXPECT_EQ(0, voe_apm_->SetAgcStatus(false)); unsigned int original_volume = 0; - EXPECT_EQ(0, voe_volume_control_->GetMicVolume(original_volume)); + bool could_get_mic_volume = + (voe_volume_control_->GetMicVolume(original_volume) == 0); +#if !defined(WEBRTC_LINUX) + EXPECT_TRUE(could_get_mic_volume); +#endif + if (could_get_mic_volume) + TEST_LOG("Current microphone volume is %u.\n", original_volume); + else + TEST_LOG("Failed to fetch current microphone volume.\n"); TEST_LOG("Setting microphone volume to 0.\n"); - EXPECT_EQ(0, voe_volume_control_->SetMicVolume(channel_)); + SetAndVerifyMicVolume(0); Sleep(1000); TEST_LOG("Setting microphone volume to 255.\n"); - EXPECT_EQ(0, voe_volume_control_->SetMicVolume(255)); - Sleep(1000); - TEST_LOG("Setting microphone volume back to saved value.\n"); - EXPECT_EQ(0, voe_volume_control_->SetMicVolume(original_volume)); + SetAndVerifyMicVolume(255); Sleep(1000); + if (could_get_mic_volume) { + TEST_LOG("Setting microphone volume back to %u.\n", original_volume); + SetAndVerifyMicVolume(original_volume); + Sleep(1000); + } } TEST_F(VolumeTest, ChannelScalingIsOneByDefault) { @@ -144,89 +223,25 @@ Sleep(2000); } -#endif // !WEBRTC_IOS - -#if !defined(WEBRTC_ANDROID) && !defined(WEBRTC_IOS) - TEST_F(VolumeTest, InputMutingIsNotEnabledByDefault) { bool is_muted = true; EXPECT_EQ(0, voe_volume_control_->GetInputMute(channel_, is_muted)); EXPECT_FALSE(is_muted); } -TEST_F(VolumeTest, DISABLED_ON_LINUX(ManualInputMutingMutesMicrophone)) { +TEST_F(VolumeTest, ManualInputMutingMutesMicrophone) { SwitchToManualMicrophone(); - // Enable muting. - EXPECT_EQ(0, voe_volume_control_->SetInputMute(channel_, true)); - bool is_muted = false; - EXPECT_EQ(0, voe_volume_control_->GetInputMute(channel_, is_muted)); - EXPECT_TRUE(is_muted); - + SetAndVerifyInputMute(true); TEST_LOG("Muted: talk into microphone and verify you can't hear yourself.\n"); Sleep(2000); // Test that we can disable muting. - EXPECT_EQ(0, voe_volume_control_->SetInputMute(channel_, false)); - EXPECT_EQ(0, voe_volume_control_->GetInputMute(channel_, is_muted)); - EXPECT_FALSE(is_muted); - + SetAndVerifyInputMute(false); TEST_LOG("Unmuted: talk into microphone and verify you can hear yourself.\n"); Sleep(2000); } -TEST_F(VolumeTest, DISABLED_ON_LINUX(SystemInputMutingIsNotEnabledByDefault)) { - bool is_muted = true; - EXPECT_EQ(0, voe_volume_control_->GetSystemInputMute(is_muted)); - EXPECT_FALSE(is_muted); -} - -TEST_F(VolumeTest, DISABLED_ON_LINUX(ManualSystemInputMutingMutesMicrophone)) { - SwitchToManualMicrophone(); - - // Enable system input muting. - EXPECT_EQ(0, voe_volume_control_->SetSystemInputMute(true)); - bool is_muted = false; - EXPECT_EQ(0, voe_volume_control_->GetSystemInputMute(is_muted)); - EXPECT_TRUE(is_muted); - - TEST_LOG("Muted: talk into microphone and verify you can't hear yourself.\n"); - Sleep(2000); - - // Test that we can disable system input muting. - EXPECT_EQ(0, voe_volume_control_->SetSystemInputMute(false)); - EXPECT_EQ(0, voe_volume_control_->GetSystemInputMute(is_muted)); - EXPECT_FALSE(is_muted); - - TEST_LOG("Unmuted: talk into microphone and verify you can hear yourself.\n"); - Sleep(2000); -} - -TEST_F(VolumeTest, DISABLED_ON_LINUX(SystemOutputMutingIsNotEnabledByDefault)) { - bool is_muted = true; - EXPECT_EQ(0, voe_volume_control_->GetSystemOutputMute(is_muted)); - EXPECT_FALSE(is_muted); -} - -TEST_F(VolumeTest, ManualSystemOutputMutingMutesOutput) { - // Enable muting. - EXPECT_EQ(0, voe_volume_control_->SetSystemOutputMute(true)); - bool is_muted = false; - EXPECT_EQ(0, voe_volume_control_->GetSystemOutputMute(is_muted)); - EXPECT_TRUE(is_muted); - - TEST_LOG("Muted: you should hear no audio.\n"); - Sleep(2000); - - // Test that we can disable muting. - EXPECT_EQ(0, voe_volume_control_->SetSystemOutputMute(false)); - EXPECT_EQ(0, voe_volume_control_->GetSystemOutputMute(is_muted)); - EXPECT_FALSE(is_muted); - - TEST_LOG("Unmuted: you should hear audio.\n"); - Sleep(2000); -} - TEST_F(VolumeTest, ManualTestInputAndOutputLevels) { SwitchToManualMicrophone(); @@ -284,5 +299,3 @@ EXPECT_FLOAT_EQ(0.1f, left); EXPECT_FLOAT_EQ(0.8f, right); } - -#endif // !WEBRTC_ANDROID && !WEBRTC_IOS diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_standard_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_standard_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_standard_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_standard_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -10,18 +10,17 @@ #include "webrtc/voice_engine/test/auto_test/voe_standard_test.h" +#include #include #include #include "webrtc/engine_configurations.h" -#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" #include "webrtc/system_wrappers/interface/event_wrapper.h" #include "webrtc/voice_engine/include/voe_neteq_stats.h" #include "webrtc/voice_engine/test/auto_test/automated_mode.h" #include "webrtc/voice_engine/test/auto_test/voe_cpu_test.h" #include "webrtc/voice_engine/test/auto_test/voe_stress_test.h" #include "webrtc/voice_engine/test/auto_test/voe_test_defines.h" -#include "webrtc/voice_engine/test/auto_test/voe_unit_test.h" #include "webrtc/voice_engine/voice_engine_defines.h" DEFINE_bool(include_timing_dependent_tests, true, @@ -30,8 +29,6 @@ DEFINE_bool(automated, false, "If true, we'll run the automated tests we have in noninteractive " "mode."); -DEFINE_bool(use_acm_version_2, false, - "If true, we'll run the tests with Audio Coding Module version 2."); using namespace webrtc; @@ -43,14 +40,10 @@ TEST_LOG("Supported sub APIs:\n\n"); if (_base) TEST_LOG(" Base\n"); - if (_callReport) - TEST_LOG(" CallReport\n"); if (_codec) TEST_LOG(" Codec\n"); if (_dtmf) TEST_LOG(" Dtmf\n"); - if (_encryption) - TEST_LOG(" Encryption\n"); if (_externalMedia) TEST_LOG(" ExternalMedia\n"); if (_file) @@ -73,14 +66,10 @@ TEST_LOG("Excluded sub APIs:\n\n"); if (!_base) TEST_LOG(" Base\n"); - if (!_callReport) - TEST_LOG(" CallReport\n"); if (!_codec) TEST_LOG(" Codec\n"); if (!_dtmf) TEST_LOG(" Dtmf\n"); - if (!_encryption) - TEST_LOG(" Encryption\n"); if (!_externalMedia) TEST_LOG(" ExternamMedia\n"); if (!_file) @@ -106,10 +95,8 @@ : initialized_(false), voice_engine_(NULL), voe_base_(0), - voe_call_report_(0), voe_codec_(0), voe_dtmf_(0), - voe_encrypt_(0), voe_xmedia_(0), voe_file_(0), voe_hardware_(0), @@ -138,12 +125,7 @@ return false; } - // TODO(minyue): Remove when the old ACM is removed (latest 2014-04-01). - config_.Set(FLAGS_use_acm_version_2 ? - new NewAudioCodingModuleFactory() : - new AudioCodingModuleFactory()); - voice_engine_ = VoiceEngine::Create(config_); - + voice_engine_ = VoiceEngine::Create(); if (!voice_engine_) { TEST_LOG("Failed to create VoiceEngine\n"); return false; @@ -165,7 +147,6 @@ #ifdef _TEST_VIDEO_SYNC_ voe_vsync_ = VoEVideoSync::GetInterface(voice_engine_); #endif - voe_encrypt_ = VoEEncryption::GetInterface(voice_engine_); voe_hardware_ = VoEHardware::GetInterface(voice_engine_); // Set the audio layer to use in all tests if (voe_hardware_) { @@ -181,9 +162,6 @@ #ifdef _TEST_XMEDIA_ voe_xmedia_ = VoEExternalMedia::GetInterface(voice_engine_); #endif -#ifdef _TEST_CALL_REPORT_ - voe_call_report_ = VoECallReport::GetInterface(voice_engine_); -#endif #ifdef WEBRTC_VOICE_ENGINE_NETEQ_STATS_API voe_neteq_stats_ = VoENetEqStats::GetInterface(voice_engine_); #endif @@ -231,10 +209,6 @@ voe_vsync_ = NULL; } #endif - if (voe_encrypt_) { - voe_encrypt_->Release(); - voe_encrypt_ = NULL; - } if (voe_hardware_) { voe_hardware_->Release(); voe_hardware_ = NULL; @@ -245,12 +219,6 @@ voe_xmedia_ = NULL; } #endif -#ifdef _TEST_CALL_REPORT_ - if (voe_call_report_) { - voe_call_report_->Release(); - voe_call_report_ = NULL; - } -#endif #ifdef WEBRTC_VOICE_ENGINE_NETEQ_STATS_API if (voe_neteq_stats_) { voe_neteq_stats_->Release(); @@ -289,9 +257,6 @@ if (test_type == Stress) { VoEStressTest stressTest(test_manager); result = stressTest.DoTest(); - } else if (test_type == Unit) { - VoEUnitTest unitTest(test_manager); - result = unitTest.DoTest(); } else if (test_type == CPU) { VoECpuTest cpuTest(test_manager); result = cpuTest.DoTest(); @@ -327,7 +292,7 @@ printf(" (1) Standard test\n"); printf(" (2) [OBSOLETE: Extended test(s)...]\n"); printf(" (3) Stress test(s)...\n"); - printf(" (4) Unit test(s)...\n"); + printf(" (4) [OBSOLETE: Unit test(s)...]\n"); printf(" (5) CPU & memory reference test [Windows]...\n"); printf("\n: "); @@ -347,7 +312,6 @@ test_type = Stress; break; case 4: - test_type = Unit; break; case 5: test_type = CPU; @@ -377,6 +341,9 @@ // This function and RunInAutomatedMode is defined in automated_mode.cc // to avoid macro clashes with googletest (for instance ASSERT_TRUE). InitializeGoogleTest(&argc, argv); + // AllowCommandLineParsing allows us to ignore flags passed on to us by + // Chromium build bots without having to explicitly disable them. + google::AllowCommandLineReparsing(); google::ParseCommandLineFlags(&argc, &argv, true); if (FLAGS_automated) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_standard_test.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_standard_test.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_standard_test.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_standard_test.h 2015-02-03 14:33:38.000000000 +0000 @@ -15,7 +15,6 @@ #include #include "gflags/gflags.h" -#include "webrtc/common.h" #include "webrtc/voice_engine/include/voe_audio_processing.h" #include "webrtc/voice_engine/include/voe_base.h" #include "webrtc/voice_engine/include/voe_dtmf.h" @@ -25,15 +24,9 @@ #include "webrtc/voice_engine/test/auto_test/resource_manager.h" #include "webrtc/voice_engine/test/auto_test/voe_test_common.h" #include "webrtc/voice_engine/test/auto_test/voe_test_interface.h" -#ifdef WEBRTC_VOICE_ENGINE_CALL_REPORT_API -#include "webrtc/voice_engine/include/voe_call_report.h" -#endif #ifdef WEBRTC_VOICE_ENGINE_CODEC_API #include "webrtc/voice_engine/include/voe_codec.h" #endif -#ifdef WEBRTC_VOICE_ENGINE_ENCRYPTION_API -#include "webrtc/voice_engine/include/voe_encryption.h" -#endif #ifdef WEBRTC_VOICE_ENGINE_EXTERNAL_MEDIA_API #include "webrtc/voice_engine/include/voe_external_media.h" #endif @@ -68,10 +61,8 @@ public: SubAPIManager() : _base(true), - _callReport(false), _codec(false), _dtmf(false), - _encryption(false), _externalMedia(false), _file(false), _hardware(false), @@ -81,18 +72,12 @@ _videoSync(false), _volumeControl(false), _apm(false) { -#ifdef WEBRTC_VOICE_ENGINE_CALL_REPORT_API - _callReport = true; -#endif #ifdef WEBRTC_VOICE_ENGINE_CODEC_API _codec = true; #endif #ifdef WEBRTC_VOICE_ENGINE_DTMF_API _dtmf = true; #endif -#ifdef WEBRTC_VOICE_ENGINE_ENCRYPTION_API - _encryption = true; -#endif #ifdef WEBRTC_VOICE_ENGINE_EXTERNAL_MEDIA_API _externalMedia = true; #endif @@ -123,7 +108,7 @@ void DisplayStatus() const; private: - bool _base, _callReport, _codec, _dtmf, _encryption; + bool _base, _codec, _dtmf; bool _externalMedia, _file, _hardware; bool _netEqStats, _network, _rtp_rtcp, _videoSync, _volumeControl, _apm; }; @@ -185,18 +170,10 @@ return voe_vsync_; } - VoEEncryption* EncryptionPtr() const { - return voe_encrypt_; - } - VoEExternalMedia* ExternalMediaPtr() const { return voe_xmedia_; } - VoECallReport* CallReportPtr() const { - return voe_call_report_; - } - #ifdef WEBRTC_VOICE_ENGINE_NETEQ_STATS_API VoENetEqStats* NetEqStatsPtr() const { return voe_neteq_stats_; @@ -208,10 +185,8 @@ VoiceEngine* voice_engine_; VoEBase* voe_base_; - VoECallReport* voe_call_report_; VoECodec* voe_codec_; VoEDtmf* voe_dtmf_; - VoEEncryption* voe_encrypt_; VoEExternalMedia* voe_xmedia_; VoEFile* voe_file_; VoEHardware* voe_hardware_; @@ -225,8 +200,6 @@ VoEAudioProcessing* voe_apm_; ResourceManager resource_manager_; - - Config config_; }; } // namespace voetest diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_test_defines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_test_defines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_test_defines.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_test_defines.h 2015-02-03 14:33:38.000000000 +0000 @@ -28,9 +28,7 @@ #define _TEST_AUDIO_PROCESSING_ #define _TEST_FILE_ #define _TEST_NETWORK_ -#define _TEST_CALL_REPORT_ #define _TEST_VIDEO_SYNC_ -#define _TEST_ENCRYPT_ #define _TEST_NETEQ_STATS_ #define _TEST_XMEDIA_ @@ -69,9 +67,6 @@ #ifndef WEBRTC_VOICE_ENGINE_VIDEO_SYNC_API #undef _TEST_VIDEO_SYNC_ #endif -#ifndef WEBRTC_VOICE_ENGINE_ENCRYPTION_API -#undef _TEST_ENCRYPT_ -#endif #ifndef WEBRTC_VOICE_ENGINE_HARDWARE_API #undef _TEST_HARDWARE_ #endif @@ -81,9 +76,6 @@ #ifndef WEBRTC_VOICE_ENGINE_NETEQ_STATS_API #undef _TEST_NETEQ_STATS_ #endif -#ifndef WEBRTC_VOICE_ENGINE_CALL_REPORT_API -#undef _TEST_CALL_REPORT_ -#endif // Some parts can cause problems while running Insure #ifdef __INSURE__ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_test_interface.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_test_interface.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_test_interface.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_test_interface.h 2015-02-03 14:33:38.000000000 +0000 @@ -27,7 +27,7 @@ Standard = 0, Stress = 2, - Unit = 3, + CPU = 4 }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_unit_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_unit_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_unit_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_unit_test.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,1086 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/voice_engine/test/auto_test/voe_unit_test.h" - -#include -#include -#include -#include -#if defined(_WIN32) -#include -#endif - -#include "webrtc/system_wrappers/interface/sleep.h" -#include "webrtc/system_wrappers/interface/thread_wrapper.h" -#include "webrtc/test/testsupport/fileutils.h" -#include "webrtc/voice_engine/test/auto_test/fakes/fake_media_process.h" -#include "webrtc/voice_engine/test/auto_test/voe_test_defines.h" -#include "webrtc/voice_engine/voice_engine_defines.h" - -using namespace webrtc; -using namespace test; - -namespace voetest { - -#define CHECK(expr) \ - if (expr) \ - { \ - printf("Error at line: %i, %s \n", __LINE__, #expr); \ - printf("Error code: %i \n", base->LastError()); \ - PAUSE \ - return -1; \ - } - -// ---------------------------------------------------------------------------- -// >>> R E A D M E F I R S T <<< -// ---------------------------------------------------------------------------- - -// 1) The user must ensure that the following codecs are included in VoE: -// -// - L16 -// - G.729 -// - G.722.1C - -// 2) It is also possible to modify the simulation time for each individual test -// -const int dTBetweenEachTest = 4000; - -// ---------------------------------------------------------------------------- -// Encrypt -// ---------------------------------------------------------------------------- - -void VoEUnitTest::encrypt(int channel_no, unsigned char * in_data, - unsigned char * out_data, int bytes_in, - int * bytes_out) { - int i; - - if (!_extOnOff) { - // no stereo emulation <=> pure bypass - for (i = 0; i < bytes_in; i++) - out_data[i] = in_data[i]; - *bytes_out = bytes_in; - } else if (_extOnOff && (_extBitsPerSample == 16)) { - // stereo emulation (sample based, 2 bytes per sample) - - const int nBytesPayload = bytes_in - 12; - - // RTP header (first 12 bytes) - memcpy(out_data, in_data, 12); - - // skip RTP header - short* ptrIn = (short*) &in_data[12]; - short* ptrOut = (short*) &out_data[12]; - - // network byte order - for (i = 0; i < nBytesPayload / 2; i++) { - // produce two output samples for each input sample - *ptrOut++ = *ptrIn; // left sample - *ptrOut++ = *ptrIn; // right sample - ptrIn++; - } - - *bytes_out = 12 + 2 * nBytesPayload; - } else if (_extOnOff && (_extBitsPerSample == 8)) { - // stereo emulation (sample based, 1 bytes per sample) - - const int nBytesPayload = bytes_in - 12; - - // RTP header (first 12 bytes) - memcpy(out_data, in_data, 12); - - // skip RTP header - unsigned char* ptrIn = (unsigned char*) &in_data[12]; - unsigned char* ptrOut = (unsigned char*) &out_data[12]; - - // network byte order - for (i = 0; i < nBytesPayload; i++) { - // produce two output samples for each input sample - *ptrOut++ = *ptrIn; // left sample - *ptrOut++ = *ptrIn; // right sample - ptrIn++; - } - - *bytes_out = 12 + 2 * nBytesPayload; - } else if (_extOnOff && (_extBitsPerSample == -1)) { - // stereo emulation (frame based) - - const int nBytesPayload = bytes_in - 12; - - // RTP header (first 12 bytes) - memcpy(out_data, in_data, 12); - - // skip RTP header - unsigned char* ptrIn = (unsigned char*) &in_data[12]; - unsigned char* ptrOut = (unsigned char*) &out_data[12]; - - // left channel - for (i = 0; i < nBytesPayload; i++) { - *ptrOut++ = *ptrIn++; - } - - ptrIn = (unsigned char*) &in_data[12]; - - // right channel - for (i = 0; i < nBytesPayload; i++) { - *ptrOut++ = *ptrIn++; - } - - *bytes_out = 12 + 2 * nBytesPayload; - } -} - -void VoEUnitTest::decrypt(int channel_no, unsigned char * in_data, - unsigned char * out_data, int bytes_in, - int * bytes_out) { - int i; - for (i = 0; i < bytes_in; i++) - out_data[i] = in_data[i]; - *bytes_out = bytes_in; -} - -void VoEUnitTest::encrypt_rtcp(int channel_no, unsigned char * in_data, - unsigned char * out_data, int bytes_in, - int * bytes_out) { - int i; - for (i = 0; i < bytes_in; i++) - out_data[i] = in_data[i]; - *bytes_out = bytes_in; -} - -void VoEUnitTest::decrypt_rtcp(int channel_no, unsigned char * in_data, - unsigned char * out_data, int bytes_in, - int * bytes_out) { - int i; - for (i = 0; i < bytes_in; i++) - out_data[i] = in_data[i]; - *bytes_out = bytes_in; -} - -void VoEUnitTest::SetStereoExternalEncryption(int channel, bool onOff, - int bitsPerSample) { - _extOnOff = onOff; - _extChannel = channel; - _extBitsPerSample = bitsPerSample; -} - -// VoEVEMediaProcess -FakeMediaProcess mpobj; - -// ---------------------------------------------------------------------------- -// VoEUnitTest -// ---------------------------------------------------------------------------- - -VoEUnitTest::VoEUnitTest(VoETestManager& mgr) : - _mgr(mgr), _extOnOff(false), _extBitsPerSample(-1), _extChannel(0) { - for (int i = 0; i < 32; i++) { - _listening[i] = false; - _playing[i] = false; - _sending[i] = false; - } -} - -// ---------------------------------------------------------------------------- -// DoTest -// ---------------------------------------------------------------------------- - -int VoEUnitTest::DoTest() { - int test(-1); - int ret(0); - while ((test != 0) && (ret != -1)) { - test = MenuSelection(); - switch (test) { - case 0: - // Quit stress test - break; - case 1: - ret = MixerTest(); - break; - case 2: - ret = MixerTest(); - break; - default: - // Should not be possible - printf("Invalid selection! (Test code error)\n"); - assert(false); - } // switch - } // while - - return ret; -} - -// ---------------------------------------------------------------------------- -// MenuSelection -// ---------------------------------------------------------------------------- - -int VoEUnitTest::MenuSelection() { - printf("------------------------------------------------\n"); - printf("Select unit test\n\n"); - printf(" (0) Quit\n"); - printf(" (1) All\n"); - printf("- - - - - - - - - - - - - - - - - - - - - - - - \n"); - printf(" (2) Mixer\n"); - - const int maxMenuSelection = 2; - int selection(-1); - - while ((selection < 0) || (selection > maxMenuSelection)) { - printf("\n: "); - int retval = scanf("%d", &selection); - if ((retval != 1) || (selection < 0) || (selection > maxMenuSelection)) { - printf("Invalid selection!\n"); - } - } - - return selection; -} - -// ---------------------------------------------------------------------------- -// StartMedia -// ---------------------------------------------------------------------------- - -int VoEUnitTest::StartMedia(int channel, int rtpPort, bool listen, bool playout, - bool send, bool fileAsMic, bool localFile) { - VoEBase* base = _mgr.BasePtr(); - VoEFile* file = _mgr.FilePtr(); - VoENetwork* voe_network = _mgr.NetworkPtr(); - - _listening[channel] = false; - _playing[channel] = false; - _sending[channel] = false; - voice_channel_transports_[channel].reset( - new VoiceChannelTransport(voe_network, channel)); - - CHECK(voice_channel_transports_[channel]->SetLocalReceiver(rtpPort)); - CHECK(voice_channel_transports_[channel]->SetSendDestination("127.0.0.1", - rtpPort)); - if (listen) { - _listening[channel] = true; - CHECK(base->StartReceive(channel)); - } - if (playout) { - _playing[channel] = true; - CHECK(base->StartPlayout(channel)); - } - if (send) { - _sending[channel] = true; - CHECK(base->StartSend(channel)); - } - if (fileAsMic) { - // play mic as file, mix with microphone to ensure that SWB can be - //tested as well - const bool mixWithMic(true); - CHECK(file->StartPlayingFileAsMicrophone(channel, _mgr.AudioFilename(), - true, mixWithMic)); - } - if (localFile) { - std::string inputFile = webrtc::test::OutputPath() + "audio_short16.pcm"; - CHECK(file->StartPlayingFileLocally(channel, - inputFile.c_str(), - false, - kFileFormatPcm16kHzFile)); - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// StopMedia -// ---------------------------------------------------------------------------- - -int VoEUnitTest::StopMedia(int channel) { - VoEBase* base = _mgr.BasePtr(); - VoEFile* file = _mgr.FilePtr(); - - if (file->IsPlayingFileAsMicrophone(channel)) { - CHECK(file->StopPlayingFileAsMicrophone(channel)); - } - if (file->IsPlayingFileLocally(channel)) { - CHECK(file->StopPlayingFileLocally(channel)); - } - if (_listening[channel]) { - _listening[channel] = false; - CHECK(base->StopReceive(channel)); - } - if (_playing[channel]) { - _playing[channel] = false; - CHECK(base->StopPlayout(channel)); - } - if (_sending[channel]) { - _sending[channel] = false; - CHECK(base->StopSend(channel)); - } - - return 0; -} - -void VoEUnitTest::Sleep(unsigned int timeMillisec, bool addMarker) { - if (addMarker) { - float dtSec = (float) ((float) timeMillisec / 1000.0); - printf("[dT=%.1f]", dtSec); - fflush(NULL); - } - webrtc::SleepMs(timeMillisec); -} - -void VoEUnitTest::Wait() { -#if defined(_WIN32) - printf("\npress any key..."); fflush(NULL); - _getch(); -#endif -} - -void VoEUnitTest::Test(const char* msg) { - printf("%s", msg); - fflush(NULL); - printf("\n"); - fflush(NULL); -} - -int VoEUnitTest::MixerTest() { - // Set up test parameters first - // - const int testTime(dTBetweenEachTest); - - printf("\n\n================================================\n"); - printf(" Mixer Unit Test\n"); - printf("================================================\n\n"); - - // Get sub-API pointers - // - VoEBase* base = _mgr.BasePtr(); - VoECodec* codec = _mgr.CodecPtr(); - VoEFile* file = _mgr.FilePtr(); - VoEVolumeControl* volume = _mgr.VolumeControlPtr(); - VoEEncryption* encrypt = _mgr.EncryptionPtr(); - VoEDtmf* dtmf = _mgr.DtmfPtr(); - VoEExternalMedia* xmedia = _mgr.ExternalMediaPtr(); - - // Set trace - // - std::string outputDir = webrtc::test::OutputPath(); - std::string traceFile = outputDir + "UnitTest_Mixer_trace.txt"; - VoiceEngine::SetTraceFile(outputDir.c_str()); - VoiceEngine::SetTraceFilter(kTraceStateInfo | kTraceWarning | kTraceError | - kTraceCritical | kTraceApiCall | kTraceMemory | - kTraceInfo); - - // Init - // - CHECK(base->Init()); - - // 8 kHz - // CodecInst l16_8 = { 123, "L16", 8000, 160, 1, 128000 }; - CodecInst pcmu_8 = { 0, "pcmu", 8000, 160, 1, 64000 }; - // CodecInst g729_8 = { 18, "g729", 8000, 160, 1, 8000 }; - - // 16 kHz - CodecInst ipcmwb_16 = { 97, "ipcmwb", 16000, 320, 1, 80000 }; - CodecInst l16_16 = { 124, "L16", 16000, 320, 1, 256000 }; - - // 32 kHz - CodecInst l16_32 = { 125, "L16", 32000, 320, 1, 512000 }; - CodecInst g722_1c_32 = { 126, "G7221", 32000, 640, 1, 32000 };// 20ms@32kHz - - // ------------------------ - // Verify mixing frequency - // ------------------------ - - base->CreateChannel(); - - Test(">> Verify correct mixing frequency:\n"); - - Test("(ch 0) Sending file at 8kHz <=> mixing at 8kHz..."); - CHECK(StartMedia(0, 12345, true, true, true, true, false)); - Sleep(testTime); - - Test("(ch 0) Sending file at 16kHz <=> mixing at 16kHz..."); - CHECK(codec->SetSendCodec(0, ipcmwb_16)); - Sleep(testTime); - - Test("(ch 0) Sending speech at 32kHz <=> mixing at 32Hz..."); - CHECK(codec->SetSendCodec(0, l16_32)); - Sleep(testTime); - - Test("(ch 0) Sending file at 8kHz <=> mixing at 8kHz..."); - CHECK(codec->SetSendCodec(0, pcmu_8)); - Sleep(testTime); - - Test("(ch 0) Playing 16kHz file locally <=> mixing at 16kHz..."); - std::string inputFile = outputDir + "audio_long16.pcm"; - CHECK(file->StartPlayingFileLocally(0, inputFile.c_str(), - false, kFileFormatPcm16kHzFile)); - Sleep(testTime); - CHECK(file->StopPlayingFileLocally(0)); - - base->CreateChannel(); - - Test("(ch 0) Sending file at 8kHz <=> mixing at 8kHz..."); - CHECK(codec->SetSendCodec(0, pcmu_8)); - Sleep(testTime); - - Test("(ch 0) Sending speech at 32kHz <=> mixing at 32Hz..."); - CHECK(codec->SetSendCodec(0, l16_32)); - Sleep(testTime); - - Test("(ch 1) Playing 16kHz file locally <=> mixing at 32kHz..."); - CHECK(StartMedia(1, 54321, false, true, false, false, true)); - Sleep(testTime); - - CHECK(StopMedia(1)); - CHECK(StopMedia(0)); - - base->DeleteChannel(1); - base->DeleteChannel(0); - ANL(); - - // ------------------------- - // Verify stereo mode mixing - // ------------------------- - - base->CreateChannel(); - base->CreateChannel(); - - // SetOutputVolumePan - // - // Ensure that all cases sound OK and that the mixer changes state between - // mono and stereo as it should. A debugger is required to trace the state - // transitions. - - Test(">> Verify correct mixing in stereo using SetOutputVolumePan():\n"); - - Test("(ch 0) Playing 16kHz file locally <=> mixing in mono @ 16kHz..."); - CHECK(StartMedia(0, 12345, false, true, false, false, true)); - Sleep(testTime); - Test("Panning volume to the left <=> mixing in stereo @ 16kHz..."); - CHECK(volume->SetOutputVolumePan(-1, 1.0, 0.0)); - Sleep(testTime); - Test("Panning volume to the right <=> mixing in stereo @ 16kHz..."); - CHECK(volume->SetOutputVolumePan(-1, 0.0, 1.0)); - Sleep(testTime); - Test("Back to center volume again <=> mixing in mono @ 16kHz..."); - CHECK(volume->SetOutputVolumePan(-1, 1.0, 1.0)); - Sleep(testTime); - Test("(ch 1) Playing 16kHz file locally <=> mixing in mono @ 16kHz..."); - CHECK(StartMedia(1, 54321, false, true, false, false, true)); - Sleep(testTime); - Test("Panning volume to the left <=> mixing in stereo @ 16kHz..."); - CHECK(volume->SetOutputVolumePan(-1, 1.0, 0.0)); - Sleep(testTime); - Test("Back to center volume again <=> mixing in mono @ 16kHz..."); - CHECK(volume->SetOutputVolumePan(-1, 1.0, 1.0)); - Sleep(testTime); - Test("(ch 1) Stopped playing file <=> mixing in mono @ 16kHz..."); - CHECK(StopMedia(1)); - Sleep(testTime); - CHECK(StopMedia(0)); - Test("(ch 0) Sending file at 8kHz <=> mixing at 8kHz..."); - CHECK(StartMedia(0, 12345, true, true, true, true, false)); - Sleep(testTime); - Test("(ch 0) Sending speech at 32kHz <=> mixing at 32kHz..."); - CHECK(codec->SetSendCodec(0, l16_32)); - Sleep(testTime); - Test("Panning volume to the right <=> mixing in stereo @ 32kHz..."); - CHECK(volume->SetOutputVolumePan(-1, 0.0, 1.0)); - Sleep(testTime); - Test("Back to center volume again <=> mixing in mono @ 32kHz..."); - CHECK(volume->SetOutputVolumePan(-1, 1.0, 1.0)); - Sleep(testTime); - CHECK(StopMedia(0)); - ANL(); - - base->DeleteChannel(0); - base->DeleteChannel(1); - - // SetChannelOutputVolumePan - // - // Ensure that all cases sound OK and that the mixer changes state between - // mono and stereo as it should. A debugger is required to trace the state - // transitions. - - base->CreateChannel(); - base->CreateChannel(); - - Test(">> Verify correct mixing in stereo using" - " SetChannelOutputVolumePan():\n"); - - Test("(ch 0) Playing 16kHz file locally <=> mixing in mono @ 16kHz..."); - CHECK(StartMedia(0, 12345, false, true, false, false, true)); - Sleep(testTime); - Test("(ch 0) Panning channel volume to the left <=> mixing in stereo @ " - "16kHz..."); - CHECK(volume->SetOutputVolumePan(0, 1.0, 0.0)); - Sleep(testTime); - Test("(ch 0) Panning channel volume to the right <=> mixing in stereo" - " @ 16kHz..."); - CHECK(volume->SetOutputVolumePan(0, 0.0, 1.0)); - Sleep(testTime); - Test("(ch 0) Back to center volume again <=> mixing in mono @" - " 16kHz..."); - CHECK(volume->SetOutputVolumePan(0, 1.0, 1.0)); - Sleep(testTime); - Test("(ch 1) Playing 16kHz file locally <=> mixing in mono @ 16kHz..."); - CHECK(StartMedia(1, 54321, false, true, false, false, true)); - Sleep(testTime); - Test("(ch 1) Panning channel volume to the left <=> mixing in stereo " - "@ 16kHz..."); - CHECK(volume->SetOutputVolumePan(1, 1.0, 0.0)); - Sleep(testTime); - Test("(ch 1) Back to center volume again <=> mixing in mono @ 16kHz..."); - CHECK(volume->SetOutputVolumePan(1, 1.0, 1.0)); - Sleep(testTime); - Test("(ch 1) Stopped playing file <=> mixing in mono @ 16kHz..."); - CHECK(StopMedia(1)); - Sleep(testTime); - CHECK(StopMedia(0)); - ANL(); - - base->DeleteChannel(0); - base->DeleteChannel(1); - - // Emulate stereo-encoding using Encryption - // - // Modify the transmitted RTP stream by using external encryption. - // Supports frame based and sample based "stereo-encoding schemes". - - base->CreateChannel(); - - Test(">> Verify correct mixing in stereo using emulated stereo input:\n"); - - // enable external encryption - CHECK(encrypt->RegisterExternalEncryption(0, *this)); - Test("(ch 0) External Encryption is now enabled:"); - - Test("(ch 0) Sending file at 8kHz <=> mixing in mono @ 8kHz..."); - CHECK(StartMedia(0, 12345, true, true, true, true, false)); - Sleep(testTime); - - // switch to 16kHz (L16) sending codec - CHECK(codec->SetSendCodec(0, l16_16)); - Test("(ch 0) Sending file at 16kHz (L16) <=> mixing in mono @ 16kHz..."); - Sleep(testTime); - - // register L16 as 2-channel codec on receiving side => - // should sound bad since RTP module splits all received packets in half - // (sample based) - CHECK(base->StopPlayout(0)); - CHECK(base->StopReceive(0)); - l16_16.channels = 2; - CHECK(codec->SetRecPayloadType(0, l16_16)); - CHECK(base->StartReceive(0)); - CHECK(base->StartPlayout(0)); - Test("(ch 0) 16kHz L16 is now registered as 2-channel codec on RX side => " - "should sound bad..."); - Sleep(testTime); - - // emulate sample-based stereo encoding - Test("(ch 0) Emulate sample-based stereo encoding on sending side => " - "should sound OK..."); - SetStereoExternalEncryption(0, true, 16); - Sleep(testTime); - Test("(ch 0) Stop emulating sample-based stereo encoding on sending side =>" - " should sound bad..."); - SetStereoExternalEncryption(0, false, 16); - Sleep(testTime); - Test("(ch 0) Emulate sample-based stereo encoding on sending side => " - "should sound OK..."); - SetStereoExternalEncryption(0, true, 16); - Sleep(testTime); - - // switch to 32kHz (L16) sending codec and disable stereo encoding - CHECK(codec->SetSendCodec(0, l16_32)); - SetStereoExternalEncryption(0, false, 16); - Test("(ch 0) Sending file and spech at 32kHz (L16) <=> mixing in mono @ " - "32kHz..."); - Sleep(testTime); - - // register L16 32kHz as 2-channel codec on receiving side - CHECK(base->StopPlayout(0)); - CHECK(base->StopReceive(0)); - l16_32.channels = 2; - CHECK(codec->SetRecPayloadType(0, l16_32)); - CHECK(base->StartReceive(0)); - CHECK(base->StartPlayout(0)); - Test("(ch 0) 32kHz L16 is now registered as 2-channel codec on RX side =>" - " should sound bad..."); - Sleep(testTime); - - // emulate sample-based stereo encoding - Test("(ch 0) Emulate sample-based stereo encoding on sending side =>" - " should sound OK..."); - SetStereoExternalEncryption(0, true, 16); - Sleep(testTime); - - StopMedia(0); - l16_32.channels = 1; - - // disable external encryption - CHECK(encrypt->DeRegisterExternalEncryption(0)); - ANL(); - - base->DeleteChannel(0); - - // ------------------ - // Verify put-on-hold - // ------------------ - - base->CreateChannel(); - base->CreateChannel(); - - Test(">> Verify put-on-hold functionality:\n"); - - Test("(ch 0) Sending at 8kHz..."); - CHECK(StartMedia(0, 12345, true, true, true, true, false)); - Sleep(testTime); - - CHECK(base->SetOnHoldStatus(0, true, kHoldPlayOnly)); - Test("(ch 0) Playout is now on hold..."); - Sleep(testTime); - CHECK(base->SetOnHoldStatus(0, false, kHoldPlayOnly)); - Test("(ch 0) Playout is now enabled again..."); - Sleep(testTime); - - Test("(ch 0) Sending at 16kHz..."); - l16_16.channels = 1; - CHECK(codec->SetSendCodec(0, l16_16)); - Sleep(testTime); - - CHECK(base->SetOnHoldStatus(0, true, kHoldPlayOnly)); - Test("(ch 0) Playout is now on hold..."); - Sleep(testTime); - CHECK(base->SetOnHoldStatus(0, false, kHoldPlayOnly)); - Test("(ch 0) Playout is now enabled again..."); - Sleep(testTime); - - Test("(ch 0) Perform minor panning to the left to force mixing in" - " stereo..."); - CHECK(volume->SetOutputVolumePan(0, (float)1.0, (float)0.7)); - Sleep(testTime); - - CHECK(base->SetOnHoldStatus(0, true, kHoldPlayOnly)); - Test("(ch 0) Playout is now on hold..."); - Sleep(testTime); - CHECK(base->SetOnHoldStatus(0, false, kHoldPlayOnly)); - Test("(ch 0) Playout is now enabled again..."); - Sleep(testTime); - - Test("(ch 0) Back to center volume again..."); - CHECK(volume->SetOutputVolumePan(0, 1.0, 1.0)); - Sleep(testTime); - - Test("(ch 1) Add 16kHz local file to the mixer..."); - CHECK(StartMedia(1, 54321, false, true, false, false, true)); - Sleep(testTime); - - CHECK(base->SetOnHoldStatus(0, true, kHoldPlayOnly)); - Test("(ch 0) Playout is now on hold..."); - Sleep(testTime); - CHECK(base->SetOnHoldStatus(1, true, kHoldPlayOnly)); - Test("(ch 1) Playout is now on hold => should be silent..."); - Sleep(testTime); - CHECK(base->SetOnHoldStatus(0, false, kHoldPlayOnly)); - Test("(ch 0) Playout is now enabled again..."); - CHECK(base->SetOnHoldStatus(1, false, kHoldPlayOnly)); - Test("(ch 1) Playout is now enabled again..."); - Sleep(testTime); - StopMedia(1); - Test("(ch 1) Stopped playing file..."); - Sleep(testTime); - StopMedia(0); - ANL(); - - base->DeleteChannel(0); - base->DeleteChannel(1); - - // ----------------------------------- - // Verify recording of playout to file - // ----------------------------------- - - // StartRecordingPlayout - // - // Verify that the correct set of signals is recorded in the mixer. - // Record each channel and all channels (-1) to ensure that post and pre - // mixing recording works. - - base->CreateChannel(); - base->CreateChannel(); - - Test(">> Verify file-recording functionality:\n"); - - Test("(ch 0) Sending at 8kHz..."); - CHECK(StartMedia(0, 12345, true, true, true, true, false)); - Sleep(testTime); - - Test("(ch 0) Recording of playout to 16kHz PCM file..."); - - std::string recordedPlayoutFile = webrtc::test::OutputPath() + - "RecordedPlayout16kHz.pcm"; - CHECK(file->StartRecordingPlayout( - 0, recordedPlayoutFile.c_str(), NULL)); - Sleep(testTime); - CHECK(file->StopRecordingPlayout(0)); - - Test("(ch 0) Playing out the recorded file..."); - CHECK(volume->SetInputMute(0, true)); - CHECK(file->StartPlayingFileLocally( - 0, recordedPlayoutFile.c_str())); - Sleep(testTime); - CHECK(file->StopPlayingFileLocally(0)); - CHECK(volume->SetInputMute(0, false)); - - CHECK(codec->SetSendCodec(0, l16_16)); - Test("(ch 0) Sending at 16kHz (L16)..."); - Sleep(testTime); - - Test("(ch 0) Recording of playout to 16kHz PCM file..."); - CHECK(file->StartRecordingPlayout( - 0, recordedPlayoutFile.c_str(), NULL)); - Sleep(testTime); - CHECK(file->StopRecordingPlayout(0)); - - Test("(ch 0) Playing out the recorded file..."); - CHECK(volume->SetInputMute(0, true)); - CHECK(file->StartPlayingFileLocally( - 0, recordedPlayoutFile.c_str())); - Sleep(testTime); - CHECK(file->StopPlayingFileLocally(0)); - CHECK(volume->SetInputMute(0, false)); - - CHECK(codec->SetSendCodec(0, l16_32)); - Test("(ch 0) Sending at 32kHz (L16)..."); - Sleep(testTime); - - Test("(ch 0) Recording of playout to 16kHz PCM file..."); - CHECK(file->StartRecordingPlayout( - 0, recordedPlayoutFile.c_str(), NULL)); - Sleep(testTime); - CHECK(file->StopRecordingPlayout(0)); - - Test("(ch 0) Playing out the recorded file..."); - CHECK(volume->SetInputMute(0, true)); - CHECK(file->StartPlayingFileLocally( - 0, recordedPlayoutFile.c_str())); - Sleep(testTime); - CHECK(file->StopPlayingFileLocally(0)); - CHECK(volume->SetInputMute(0, false)); - - Test("(ch 0) Sending at 16kHz without file as mic but file added on the" - " playout side instead..."); - CHECK(StopMedia(0)); - CHECK(StartMedia(0, 12345, false, true, false, false, true)); - CHECK(codec->SetSendCodec(0, l16_16)); - Sleep(testTime); - - Test("(ch 0) Recording of playout to 16kHz PCM file..."); - CHECK(file->StartRecordingPlayout( - 0, recordedPlayoutFile.c_str(), NULL)); - Sleep(testTime); - CHECK(file->StopRecordingPlayout(0)); - CHECK(file->StopPlayingFileLocally(0)); - - Test("(ch 0) Playing out the recorded file..."); - CHECK(file->StartPlayingFileLocally( - 0, recordedPlayoutFile.c_str())); - Sleep(testTime); - CHECK(file->StopPlayingFileLocally(0)); - - CHECK(StopMedia(0)); - CHECK(StopMedia(1)); - - Test("(ch 0) Sending at 16kHz..."); - CHECK(StartMedia(0, 12345, true, true, true, false, false)); - CHECK(codec->SetSendCodec(0, l16_16)); - Test("(ch 1) Adding playout file..."); - CHECK(StartMedia(1, 33333, false, true, false, false, true)); - Sleep(testTime); - - Test("(ch -1) Speak while recording all channels to add mixer input on " - "channel 0..."); - CHECK(file->StartRecordingPlayout( - -1, recordedPlayoutFile.c_str(), NULL)); - Sleep(testTime); - CHECK(file->StopRecordingPlayout(-1)); - CHECK(file->StopPlayingFileLocally(1)); - - Test("(ch 0) Playing out the recorded file..."); - CHECK(volume->SetInputMute(0, true)); - CHECK(file->StartPlayingFileLocally( - 0, recordedPlayoutFile.c_str())); - Sleep(testTime); - CHECK(file->StopPlayingFileLocally(0)); - CHECK(volume->SetInputMute(0, false)); - - CHECK(StopMedia(0)); - CHECK(StopMedia(1)); - ANL(); - - // StartRecordingPlayoutStereo - - Test(">> Verify recording of playout in stereo:\n"); - - Test("(ch 0) Sending at 32kHz..."); - CHECK(codec->SetSendCodec(0, l16_16)); - CHECK(StartMedia(0, 12345, true, true, true, true, false)); - Sleep(testTime); - - Test("Modified master balance (L=10%%, R=100%%) to force stereo mixing..."); - CHECK(volume->SetOutputVolumePan(-1, (float)0.1, (float)1.0)); - Sleep(testTime); - - /* - Test("Recording of left and right channel playout to two 16kHz PCM " - "files..."); - file->StartRecordingPlayoutStereo( - GetFilename("RecordedPlayout_Left_16kHz.pcm"), - GetFilename("RecordedPlayout_Right_16kHz.pcm"), StereoBoth); - Sleep(testTime); - Test("Back to center volume again..."); - CHECK(volume->SetOutputVolumePan(-1, (float)1.0, (float)1.0)); - */ - - Test("(ch 0) Playing out the recorded file for the left channel (10%%)..."); - CHECK(volume->SetInputMute(0, true)); - std::string leftFilename = outputDir + "RecordedPlayout_Left_16kHz.pcm"; - CHECK(file->StartPlayingFileLocally(0, leftFilename.c_str())); - Sleep(testTime); - CHECK(file->StopPlayingFileLocally(0)); - - Test("(ch 0) Playing out the recorded file for the right channel (100%%) =>" - " should sound louder than the left channel..."); - std::string rightFilename = outputDir + "RecordedPlayout_Right_16kHz.pcm"; - CHECK(file->StartPlayingFileLocally(0, rightFilename.c_str())); - Sleep(testTime); - CHECK(file->StopPlayingFileLocally(0)); - CHECK(volume->SetInputMute(0, false)); - - base->DeleteChannel(0); - base->DeleteChannel(1); - ANL(); - - // --------------------------- - // Verify inserted Dtmf tones - // --------------------------- - - Test(">> Verify Dtmf feedback functionality:\n"); - - base->CreateChannel(); - - for (int i = 0; i < 2; i++) { - if (i == 0) - Test("Dtmf direct feedback is now enabled..."); - else - Test("Dtmf direct feedback is now disabled..."); - - CHECK(dtmf->SetDtmfFeedbackStatus(true, (i==0))); - - Test("(ch 0) Sending at 32kHz using G.722.1C..."); - CHECK(codec->SetRecPayloadType(0, g722_1c_32)); - CHECK(codec->SetSendCodec(0, g722_1c_32)); - CHECK(StartMedia(0, 12345, true, true, true, false, false)); - Sleep(500); - - Test("(ch 0) Sending outband Dtmf events => ensure that they are added" - " to the mixer..."); - // ensure that receiver will not play out outband Dtmf - CHECK(dtmf->SetSendTelephoneEventPayloadType(0, 118)); - CHECK(dtmf->SendTelephoneEvent(0, 9, true, 390)); - Sleep(500); - CHECK(dtmf->SendTelephoneEvent(0, 1, true, 390)); - Sleep(500); - CHECK(dtmf->SendTelephoneEvent(0, 5, true, 390)); - Sleep(500); - Sleep(testTime - 1500); - - Test("(ch 0) Changing codec to 8kHz PCMU..."); - CHECK(codec->SetSendCodec(0, pcmu_8)); - Sleep(500); - - Test("(ch 0) Sending outband Dtmf events => ensure that they are added" - " to the mixer..."); - CHECK(dtmf->SendTelephoneEvent(0, 9, true, 390)); - Sleep(500); - CHECK(dtmf->SendTelephoneEvent(0, 1, true, 390)); - Sleep(500); - CHECK(dtmf->SendTelephoneEvent(0, 5, true, 390)); - Sleep(500); - Sleep(testTime - 1500); - - Test("(ch 0) Changing codec to 16kHz L16..."); - CHECK(codec->SetSendCodec(0, l16_16)); - Sleep(500); - - Test("(ch 0) Sending outband Dtmf events => ensure that they are added" - " to the mixer..."); - CHECK(dtmf->SendTelephoneEvent(0, 9, true, 390)); - Sleep(500); - CHECK(dtmf->SendTelephoneEvent(0, 1, true, 390)); - Sleep(500); - CHECK(dtmf->SendTelephoneEvent(0, 5, true, 390)); - Sleep(500); - Sleep(testTime - 1500); - - StopMedia(0); - ANL(); - } - - base->DeleteChannel(0); - - // --------------------------- - // Verify external processing - // -------------------------- - - base->CreateChannel(); - - Test(">> Verify external media processing:\n"); - - Test("(ch 0) Playing 16kHz file locally <=> mixing in mono @ 16kHz..."); - CHECK(StartMedia(0, 12345, false, true, false, false, true)); - Sleep(testTime); - Test("Enabling playout external media processing => played audio should " - "now be affected"); - CHECK(xmedia->RegisterExternalMediaProcessing( - 0, kPlaybackAllChannelsMixed, mpobj)); - Sleep(testTime); - Test("(ch 0) Sending speech at 32kHz <=> mixing at 32kHz..."); - CHECK(codec->SetSendCodec(0, l16_32)); - Sleep(testTime); - printf("Back to normal again\n"); - CHECK(xmedia->DeRegisterExternalMediaProcessing(0, - kPlaybackAllChannelsMixed)); - Sleep(testTime); - printf("Enabling playout external media processing on ch 0 => " - "played audio should now be affected\n"); - CHECK(xmedia->RegisterExternalMediaProcessing(0, kPlaybackPerChannel, - mpobj)); - Sleep(testTime); - Test("Panning volume to the right <=> mixing in stereo @ 32kHz..."); - CHECK(volume->SetOutputVolumePan(-1, 0.0, 1.0)); - Sleep(testTime); - Test("Back to center volume again <=> mixing in mono @ 32kHz..."); - CHECK(volume->SetOutputVolumePan(-1, 1.0, 1.0)); - Sleep(testTime); - printf("Back to normal again\n"); - CHECK(xmedia->DeRegisterExternalMediaProcessing(0, kPlaybackPerChannel)); - Sleep(testTime); - CHECK(StopMedia(0)); - ANL(); - - base->DeleteChannel(0); - - // -------------------------------------------------- - // Extended tests of emulated stereo encoding schemes - // -------------------------------------------------- - - CodecInst PCMU; - CodecInst G729; - CodecInst L16_8; - CodecInst L16_16; - CodecInst L16_32; - - base->CreateChannel(); - - Test(">> Verify emulated stereo encoding for differenct codecs:\n"); - - // enable external encryption - CHECK(encrypt->RegisterExternalEncryption(0, *this)); - Test("(ch 0) External Encryption is now enabled:"); - - // register all codecs on the receiving side - strcpy(PCMU.plname, "PCMU"); - PCMU.channels = 2; - PCMU.pacsize = 160; - PCMU.plfreq = 8000; - PCMU.pltype = 125; - PCMU.rate = 64000; - CHECK(codec->SetRecPayloadType(0, PCMU)); - - strcpy(G729.plname, "G729"); - G729.channels = 2; - G729.pacsize = 160; - G729.plfreq = 8000; - G729.pltype = 18; - G729.rate = 8000; - CHECK(codec->SetRecPayloadType(0, G729)); - - strcpy(L16_8.plname, "L16"); - L16_8.channels = 2; - L16_8.pacsize = 160; - L16_8.plfreq = 8000; - L16_8.pltype = 120; - L16_8.rate = 128000; - CHECK(codec->SetRecPayloadType(0, L16_8)); - - strcpy(L16_16.plname, "L16"); - L16_16.channels = 2; - L16_16.pacsize = 320; - L16_16.plfreq = 16000; - L16_16.pltype = 121; - L16_16.rate = 256000; - CHECK(codec->SetRecPayloadType(0, L16_16)); - - // NOTE - we cannot send larger than 1500 bytes per RTP packet - strcpy(L16_32.plname, "L16"); - L16_32.channels = 2; - L16_32.pacsize = 320; - L16_32.plfreq = 32000; - L16_32.pltype = 122; - L16_32.rate = 512000; - CHECK(codec->SetRecPayloadType(0, L16_32)); - - // sample-based, 8-bits per sample - - Test("(ch 0) Sending using G.711 (sample based, 8 bits/sample)..."); - PCMU.channels = 1; - CHECK(codec->SetSendCodec(0, PCMU)); - SetStereoExternalEncryption(0, true, 8); - CHECK(StartMedia(0, 12345, true, true, true, true, false)); - Sleep(testTime); - - // sample-based, 16-bits per sample - - Test("(ch 0) Sending using L16 8kHz (sample based, 16 bits/sample)..."); - L16_8.channels = 1; - CHECK(codec->SetSendCodec(0, L16_8)); - SetStereoExternalEncryption(0, true, 16); - Sleep(testTime); - - Test("(ch 0) Sending using L16 16kHz (sample based, 16 bits/sample)..."); - L16_16.channels = 1; - CHECK(codec->SetSendCodec(0, L16_16)); - Sleep(testTime); - - Test("(ch 0) Sending using L16 32kHz (sample based, 16 bits/sample)..."); - L16_32.channels = 1; - CHECK(codec->SetSendCodec(0, L16_32)); - Sleep(testTime); - - Test("(ch 0) Sending using G.729 (frame based)..."); - G729.channels = 1; - CHECK(codec->SetSendCodec(0, G729)); - Sleep(testTime); - - StopMedia(0); - - // disable external encryption - CHECK(encrypt->DeRegisterExternalEncryption(0)); - - base->DeleteChannel(0); - - // ------------------------------------------------------------------------ - CHECK(base->Terminate()); - - printf("\n\n------------------------------------------------\n"); - printf(" Test passed!\n"); - printf("------------------------------------------------\n\n"); - - return 0; -} - -} // namespace voetest diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_unit_test.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_unit_test.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_unit_test.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/voe_unit_test.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_VOICE_ENGINE_VOE_UNIT_TEST_H -#define WEBRTC_VOICE_ENGINE_VOE_UNIT_TEST_H - -#include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/test/channel_transport/include/channel_transport.h" -#include "webrtc/voice_engine/test/auto_test/voe_standard_test.h" - -namespace voetest { - -class VoETestManager; - -class VoEUnitTest : public Encryption { - public: - VoEUnitTest(VoETestManager& mgr); - ~VoEUnitTest() {} - int DoTest(); - - protected: - // Encryption - void encrypt(int channel_no, unsigned char * in_data, - unsigned char * out_data, int bytes_in, int * bytes_out); - void decrypt(int channel_no, unsigned char * in_data, - unsigned char * out_data, int bytes_in, int * bytes_out); - void encrypt_rtcp(int channel_no, unsigned char * in_data, - unsigned char * out_data, int bytes_in, int * bytes_out); - void decrypt_rtcp(int channel_no, unsigned char * in_data, - unsigned char * out_data, int bytes_in, int * bytes_out); - - private: - int MenuSelection(); - int MixerTest(); - void Sleep(unsigned int timeMillisec, bool addMarker = false); - void Wait(); - int StartMedia(int channel, - int rtpPort, - bool listen, - bool playout, - bool send, - bool fileAsMic, - bool localFile); - int StopMedia(int channel); - void Test(const char* msg); - void SetStereoExternalEncryption(int channel, bool onOff, int bitsPerSample); - - private: - VoETestManager& _mgr; - - private: - bool _listening[32]; - bool _playing[32]; - bool _sending[32]; - scoped_ptr voice_channel_transports_[32]; - - private: - bool _extOnOff; - int _extBitsPerSample; - int _extChannel; -}; - -} // namespace voetest -#endif // WEBRTC_VOICE_ENGINE_VOE_UNIT_TEST_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/cmd_test/Android.mk thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/cmd_test/Android.mk --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/cmd_test/Android.mk 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/cmd_test/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH:= $(call my-dir) - -# voice engine test app - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/../../../../../android-webrtc.mk - -LOCAL_MODULE_TAGS := tests -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES:= \ - voe_cmd_test.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_ANDROID' \ - '-DDEBUG' - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../../interface \ - $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/../../../../.. \ - external/gtest/include \ - frameworks/base/include - -LOCAL_SHARED_LIBRARIES := \ - libutils \ - libmedia \ - libcamera_client \ - libgui \ - libhardware \ - libandroid_runtime \ - libbinder - -#libwilhelm.so libDunDef-Android.so libbinder.so libsystem_server.so - -LOCAL_MODULE:= webrtc_voe_cmd - -ifdef NDK_ROOT -LOCAL_SHARED_LIBRARIES += \ - libstlport_shared \ - libwebrtc-voice-jni \ - libwebrtc_audio_preprocessing -include $(BUILD_EXECUTABLE) -else -LOCAL_SHARED_LIBRARIES += \ - libstlport \ - libwebrtc -include external/stlport/libstlport.mk -include $(BUILD_NATIVE_TEST) -endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/cmd_test/voe_cmd_test.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/cmd_test/voe_cmd_test.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/cmd_test/voe_cmd_test.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/cmd_test/voe_cmd_test.cc 2015-02-03 14:33:38.000000000 +0000 @@ -19,10 +19,7 @@ #include "gflags/gflags.h" #include "testing/gtest/include/gtest/gtest.h" -#include "webrtc/common.h" -#include "webrtc/common_types.h" #include "webrtc/engine_configurations.h" -#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" #include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/test/channel_transport/include/channel_transport.h" @@ -32,7 +29,6 @@ #include "webrtc/voice_engine/include/voe_base.h" #include "webrtc/voice_engine/include/voe_codec.h" #include "webrtc/voice_engine/include/voe_dtmf.h" -#include "webrtc/voice_engine/include/voe_encryption.h" #include "webrtc/voice_engine/include/voe_errors.h" #include "webrtc/voice_engine/include/voe_external_media.h" #include "webrtc/voice_engine/include/voe_file.h" @@ -43,8 +39,6 @@ #include "webrtc/voice_engine/include/voe_video_sync.h" #include "webrtc/voice_engine/include/voe_volume_control.h" -DEFINE_bool(use_acm_version_2, false, - "If true, we'll run the tests with Audio Coding Module version 2."); DEFINE_bool(use_log_file, false, "Output logs to a file; by default they will be printed to stderr."); @@ -67,7 +61,6 @@ VoENetwork* netw = NULL; VoEFile* file = NULL; VoEVideoSync* vsync = NULL; -VoEEncryption* encr = NULL; VoEHardware* hardware = NULL; VoEExternalMedia* xmedia = NULL; VoENetEqStats* neteqst = NULL; @@ -131,13 +124,7 @@ printf("Test started \n"); - // TODO(minyue): Remove when the old ACM is removed (latest 2014-04-01). - Config config; - config.Set(FLAGS_use_acm_version_2 ? - new NewAudioCodingModuleFactory() : - new AudioCodingModuleFactory()); - m_voe = VoiceEngine::Create(config); - + m_voe = VoiceEngine::Create(); base1 = VoEBase::GetInterface(m_voe); codec = VoECodec::GetInterface(m_voe); apm = VoEAudioProcessing::GetInterface(m_voe); @@ -147,7 +134,6 @@ netw = VoENetwork::GetInterface(m_voe); file = VoEFile::GetInterface(m_voe); vsync = VoEVideoSync::GetInterface(m_voe); - encr = VoEEncryption::GetInterface(m_voe); hardware = VoEHardware::GetInterface(m_voe); xmedia = VoEExternalMedia::GetInterface(m_voe); neteqst = VoENetEqStats::GetInterface(m_voe); @@ -220,9 +206,6 @@ if (vsync) vsync->Release(); - if (encr) - encr->Release(); - if (hardware) hardware->Release(); @@ -248,7 +231,6 @@ bool enable_rx_ns = false; bool typing_detection = false; bool muted = false; - bool on_hold = false; bool opus_stereo = false; bool experimental_ns_enabled = false; @@ -276,6 +258,9 @@ fflush(NULL); } + scoped_ptr voice_channel_transport( + new VoiceChannelTransport(netw, chan)); + char ip[64]; printf("1. 127.0.0.1 \n"); printf("2. Specify IP \n"); @@ -296,9 +281,6 @@ rPort = 1234; printf("Set Send port \n"); - scoped_ptr voice_channel_transport( - new VoiceChannelTransport(netw, chan)); - printf("Set Send IP \n"); res = voice_channel_transport->SetSendDestination(ip, rPort); VALIDATE; @@ -347,8 +329,6 @@ // Call loop bool newcall = true; while (newcall) { - -#ifndef WEBRTC_ANDROID int rd(-1), pd(-1); res = hardware->GetNumOfRecordingDevices(rd); VALIDATE; @@ -380,7 +360,6 @@ printf("Setting sound devices \n"); res = hardware->SetRecordingDevice(rd); VALIDATE; -#endif // WEBRTC_ANDROID res = codec->SetVADStatus(0, enable_cng); VALIDATE; @@ -421,7 +400,6 @@ VALIDATE; } -#ifndef WEBRTC_ANDROID printf("Getting mic volume \n"); unsigned int vol = 999; res = volume->GetMicVolume(vol); @@ -429,7 +407,6 @@ if ((vol > 255) || (vol < 1)) { printf("\n****ERROR in GetMicVolume"); } -#endif int forever = 1; while (forever) { @@ -456,7 +433,6 @@ printf("%i. Toggle receive-side NS \n", option_index++); printf("%i. AGC status \n", option_index++); printf("%i. Toggle microphone mute \n", option_index++); - printf("%i. Toggle on hold status \n", option_index++); printf("%i. Get last error code \n", option_index++); printf("%i. Toggle typing detection \n", option_index++); @@ -469,6 +445,9 @@ printf("%i. Remove a file-playing channel \n", option_index++); printf("%i. Toggle Opus stereo (Opus must be selected again to apply " "the setting) \n", option_index++); + printf("%i. Set Opus maximum playback rate \n", option_index++); + printf("%i. Set bit rate (only take effect on codecs that allow the " + "change) \n", option_index++); printf("Select action or %i to stop the call: ", option_index); int option_selection; @@ -508,9 +487,9 @@ printf("\n NS is now off! \n"); } else if (option_selection == option_index++) { experimental_ns_enabled = !experimental_ns_enabled; - res = base1->audio_processing()->EnableExperimentalNs( - experimental_ns_enabled); - VALIDATE; + Config config; + config.Set(new ExperimentalNs(experimental_ns_enabled)); + base1->audio_processing()->SetExtraOptions(config); if (experimental_ns_enabled) { printf("\n Experimental NS is now on!\n"); } else { @@ -644,19 +623,6 @@ else printf("\n Microphone is no longer on mute! \n"); } else if (option_selection == option_index++) { - // Toggle the call on hold - OnHoldModes mode; - res = base1->GetOnHoldStatus(chan, on_hold, mode); - VALIDATE; - on_hold = !on_hold; - mode = kHoldSendAndPlay; - res = base1->SetOnHoldStatus(chan, on_hold, mode); - VALIDATE; - if (on_hold) - printf("\n Call now on hold! \n"); - else - printf("\n Call now not on hold! \n"); - } else if (option_selection == option_index++) { // Get the last error code and print to screen int err_code = 0; err_code = base1->LastError(); @@ -794,6 +760,19 @@ else printf("\n Opus mono enabled (select Opus again to apply the " "setting). \n"); + } else if (option_selection == option_index++) { + printf("\n Input maxium playback rate in Hz: "); + int max_playback_rate; + ASSERT_EQ(1, scanf("%i", &max_playback_rate)); + res = codec->SetOpusMaxPlaybackRate(chan, max_playback_rate); + VALIDATE; + } else if (option_selection == option_index++) { + res = codec->GetSendCodec(chan, cinst); + VALIDATE; + printf("Current bit rate is %i bps, set to: ", cinst.rate); + ASSERT_EQ(1, scanf("%i", &cinst.rate)); + res = codec->SetSendCodec(chan, cinst); + VALIDATE; } else { break; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/win_test/Resource.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/win_test/Resource.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/win_test/Resource.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/win_test/Resource.h 2015-02-03 14:33:38.000000000 +0000 @@ -173,7 +173,7 @@ #define IDC_CHECK_TYPING_DETECTION 1101 #define IDC_CHECK_START_STOP_MODE2 1102 #define IDC_CHECK_DIRECT_FEEDBACK 1102 -#define IDC_CHECK_FEC 1102 +#define IDC_CHECK_RED 1102 #define IDC_BUTTON_SET_RX_TELEPHONE_PT_TYPE 1103 #define IDC_BUTTON_SET_RX_TELEPHONE_PT 1103 #define IDC_BUTTON_CLEAR_ERROR_CALLBACK 1103 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/win_test/WinTestDlg.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/win_test/WinTestDlg.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/win_test/WinTestDlg.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/win_test/WinTestDlg.cc 2015-02-03 14:33:38.000000000 +0000 @@ -54,28 +54,6 @@ } // ---------------------------------------------------------------------------- -// VoEConnectionObserver -// ---------------------------------------------------------------------------- - -class ConnectionObserver : public VoEConnectionObserver -{ -public: - ConnectionObserver(); - virtual void OnPeriodicDeadOrAlive(int channel, bool alive); -}; - -ConnectionObserver::ConnectionObserver() -{ -} - -void ConnectionObserver::OnPeriodicDeadOrAlive(int channel, bool alive) -{ - CString str; - str.Format(_T("OnPeriodicDeadOrAlive(channel=%d) => alive=%d"), channel, alive); - OutputDebugString(str); -} - -// ---------------------------------------------------------------------------- // VoiceEngineObserver // ---------------------------------------------------------------------------- @@ -151,8 +129,8 @@ { public: MyTransport(VoENetwork* veNetwork); - virtual int SendPacket(int channel, const void *data, int len); - virtual int SendRTCPPacket(int channel, const void *data, int len); + virtual int SendPacket(int channel, const void *data, int len) OVERRIDE; + virtual int SendRTCPPacket(int channel, const void *data, int len) OVERRIDE; private: VoENetwork* _veNetworkPtr; }; @@ -275,72 +253,6 @@ } // ---------------------------------------------------------------------------- -// Encryptionen -// ---------------------------------------------------------------------------- - -class MyEncryption : public Encryption -{ -public: - void encrypt(int channel_no, unsigned char * in_data, unsigned char * out_data, int bytes_in, int* bytes_out); - void decrypt(int channel_no, unsigned char * in_data, unsigned char * out_data, int bytes_in, int* bytes_out); - void encrypt_rtcp(int channel_no, unsigned char * in_data, unsigned char * out_data, int bytes_in, int* bytes_out); - void decrypt_rtcp(int channel_no, unsigned char * in_data, unsigned char * out_data, int bytes_in, int* bytes_out); -}; - -void MyEncryption::encrypt(int channel_no, unsigned char * in_data, unsigned char * out_data, int bytes_in, int* bytes_out) -{ - // --- Stereo emulation (sample based, 2 bytes per sample) - - const int nBytesPayload = bytes_in-12; - - // RTP header (first 12 bytes) - memcpy(out_data, in_data, 12); - - // skip RTP header - short* ptrIn = (short*) &in_data[12]; - short* ptrOut = (short*) &out_data[12]; - - // network byte order - for (int i = 0; i < nBytesPayload/2; i++) - { - // produce two output samples for each input sample - *ptrOut++ = *ptrIn; // left sample - *ptrOut++ = *ptrIn; // right sample - ptrIn++; - } - - *bytes_out = 12 + 2*nBytesPayload; - - /* - for(int i = 0; i < bytes_in; i++) - out_data[i] =~ in_data[i]; - *bytes_out = bytes_in; - */ -} - -void MyEncryption::decrypt(int channel_no, unsigned char * in_data, unsigned char * out_data, int bytes_in, int* bytes_out) -{ - // Do nothing (<=> memcpy) - for(int i = 0; i < bytes_in; i++) - out_data[i] = in_data[i]; - *bytes_out = bytes_in; -} - -void MyEncryption::encrypt_rtcp(int channel_no, unsigned char * in_data, unsigned char * out_data, int bytes_in, int* bytes_out) -{ - for(int i = 0; i < bytes_in; i++) - out_data[i] =~ in_data[i]; - *bytes_out = bytes_in; -} - -void MyEncryption::decrypt_rtcp(int channel_no, unsigned char * in_data, unsigned char * out_data, int bytes_in, int* bytes_out) -{ - for(int i = 0; i < bytes_in; i++) - out_data[i] =~ in_data[i]; - *bytes_out = bytes_in; -} - -// ---------------------------------------------------------------------------- // TelephoneEventObserver // ---------------------------------------------------------------------------- @@ -1121,10 +1033,8 @@ _veHardwarePtr(NULL), _veExternalMediaPtr(NULL), _veApmPtr(NULL), - _veEncryptionPtr(NULL), _veRtpRtcpPtr(NULL), _transportPtr(NULL), - _encryptionPtr(NULL), _externalMediaPtr(NULL), _externalTransport(false), _externalTransportBuild(false), @@ -1172,7 +1082,6 @@ { _veExternalMediaPtr = VoEExternalMedia::GetInterface(_vePtr); _veVolumeControlPtr = VoEVolumeControl::GetInterface(_vePtr); - _veEncryptionPtr = VoEEncryption::GetInterface(_vePtr); _veVideoSyncPtr = VoEVideoSync::GetInterface(_vePtr); _veNetworkPtr = VoENetwork::GetInterface(_vePtr); _veFilePtr = VoEFile::GetInterface(_vePtr); @@ -1183,9 +1092,7 @@ _veHardwarePtr = VoEHardware::GetInterface(_vePtr); _veRtpRtcpPtr = VoERTP_RTCP::GetInterface(_vePtr); _transportPtr = new MyTransport(_veNetworkPtr); - _encryptionPtr = new MyEncryption(); _externalMediaPtr = new MediaProcessImpl(); - _connectionObserverPtr = new ConnectionObserver(); _rxVadObserverPtr = new RxCallback(); } @@ -1201,14 +1108,11 @@ CWinTestDlg::~CWinTestDlg() { - if (_connectionObserverPtr) delete _connectionObserverPtr; if (_externalMediaPtr) delete _externalMediaPtr; if (_transportPtr) delete _transportPtr; - if (_encryptionPtr) delete _encryptionPtr; if (_rxVadObserverPtr) delete _rxVadObserverPtr; if (_veExternalMediaPtr) _veExternalMediaPtr->Release(); - if (_veEncryptionPtr) _veEncryptionPtr->Release(); if (_veVideoSyncPtr) _veVideoSyncPtr->Release(); if (_veVolumeControlPtr) _veVolumeControlPtr->Release(); @@ -1310,7 +1214,7 @@ ON_BN_CLICKED(IDC_CHECK_NS_1, &CWinTestDlg::OnBnClickedCheckNs1) ON_BN_CLICKED(IDC_CHECK_REC_CALL, &CWinTestDlg::OnBnClickedCheckRecCall) ON_BN_CLICKED(IDC_CHECK_TYPING_DETECTION, &CWinTestDlg::OnBnClickedCheckTypingDetection) - ON_BN_CLICKED(IDC_CHECK_FEC, &CWinTestDlg::OnBnClickedCheckFEC) + ON_BN_CLICKED(IDC_CHECK_RED, &CWinTestDlg::OnBnClickedCheckRED) ON_BN_CLICKED(IDC_BUTTON_CLEAR_ERROR_CALLBACK, &CWinTestDlg::OnBnClickedButtonClearErrorCallback) END_MESSAGE_MAP() @@ -1512,7 +1416,7 @@ GetDlgItem(IDC_CHECK_RXVAD)->EnableWindow(FALSE); GetDlgItem(IDC_CHECK_AGC_1)->EnableWindow(FALSE); GetDlgItem(IDC_CHECK_NS_1)->EnableWindow(FALSE); - GetDlgItem(IDC_CHECK_FEC)->EnableWindow(FALSE); + GetDlgItem(IDC_CHECK_RED)->EnableWindow(FALSE); CComboBox* comboIP(NULL); comboIP = (CComboBox*)GetDlgItem(IDC_COMBO_IP_1); @@ -1781,7 +1685,7 @@ GetDlgItem(IDC_CHECK_RXVAD)->EnableWindow(TRUE); GetDlgItem(IDC_CHECK_AGC_1)->EnableWindow(TRUE); GetDlgItem(IDC_CHECK_NS_1)->EnableWindow(TRUE); - GetDlgItem(IDC_CHECK_FEC)->EnableWindow(TRUE); + GetDlgItem(IDC_CHECK_RED)->EnableWindow(TRUE); // Always set send codec to default codec <=> index 0. CodecInst codec; @@ -1871,7 +1775,7 @@ GetDlgItem(IDC_CHECK_AGC_1)->EnableWindow(FALSE); GetDlgItem(IDC_CHECK_NS_1)->EnableWindow(FALSE); GetDlgItem(IDC_CHECK_RXVAD)->EnableWindow(FALSE); - GetDlgItem(IDC_CHECK_FEC)->EnableWindow(FALSE); + GetDlgItem(IDC_CHECK_RED)->EnableWindow(FALSE); SetDlgItemText(IDC_EDIT_RXVAD, _T("")); GetDlgItem(IDC_EDIT_RXVAD)->EnableWindow(FALSE); CButton* button = (CButton*)GetDlgItem(IDC_CHECK_EXT_TRANS_1); @@ -1906,7 +1810,7 @@ button->SetCheck(BST_UNCHECKED); button = (CButton*)GetDlgItem(IDC_CHECK_RXVAD); button->SetCheck(BST_UNCHECKED); - button = (CButton*)GetDlgItem(IDC_CHECK_FEC); + button = (CButton*)GetDlgItem(IDC_CHECK_RED); button->SetCheck(BST_UNCHECKED); } } @@ -2675,162 +2579,32 @@ void CWinTestDlg::OnBnClickedCheckSrtpTx1() { - int ret(0); - int channel = GetDlgItemInt(IDC_EDIT_1); - CButton* button = (CButton*)GetDlgItem(IDC_CHECK_SRTP_TX_1); - int check = button->GetCheck(); - const bool enable = (check == BST_CHECKED); - bool useForRTCP = false; - if (enable) - { - (_checkSrtpTx1++ %2 == 0) ? useForRTCP = false : useForRTCP = true; - // TODO(solenberg): Install SRTP encryption policy. - TEST(true, "Built-in SRTP support is deprecated. Enable it again by " - "setting an external encryption policy, i.e.:\n\r" - "_veEncryptionPtr->RegisterExternalEncryption(channel, myPolicy)"); - } - else - { - // TODO(solenberg): Uninstall SRTP encryption policy, i.e.: - // _veEncryptionPtr->DeRegisterExternalEncryption(channel); - } - if (ret == -1) - { - // restore inital state since API call failed - button->SetCheck((check == BST_CHECKED) ? BST_UNCHECKED : BST_CHECKED); - } + TEST(true, "Built-in SRTP support is deprecated."); } void CWinTestDlg::OnBnClickedCheckSrtpTx2() { - int ret(0); - int channel = GetDlgItemInt(IDC_EDIT_2); - CButton* button = (CButton*)GetDlgItem(IDC_CHECK_SRTP_TX_2); - int check = button->GetCheck(); - const bool enable = (check == BST_CHECKED); - bool useForRTCP = false; - if (enable) - { - (_checkSrtpTx2++ %2 == 0) ? useForRTCP = false : useForRTCP = true; - // TODO(solenberg): Install SRTP encryption policy. - TEST(true, "Built-in SRTP support is deprecated. Enable it again by " - "setting an external encryption policy, i.e.:\n\r" - "_veEncryptionPtr->RegisterExternalEncryption(channel, myPolicy)"); - } - else - { - // TODO(solenberg): Uninstall SRTP encryption policy, i.e.: - // _veEncryptionPtr->DeRegisterExternalEncryption(channel); - } - if (ret == -1) - { - // restore inital state since API call failed - button->SetCheck((check == BST_CHECKED) ? BST_UNCHECKED : BST_CHECKED); - } + TEST(true, "Built-in SRTP support is deprecated."); } void CWinTestDlg::OnBnClickedCheckSrtpRx1() { - int ret(0); - int channel = GetDlgItemInt(IDC_EDIT_1); - CButton* button = (CButton*)GetDlgItem(IDC_CHECK_SRTP_RX_1); - int check = button->GetCheck(); - const bool enable = (check == BST_CHECKED); - bool useForRTCP(false); - if (enable) - { - (_checkSrtpRx1++ %2 == 0) ? useForRTCP = false : useForRTCP = true; - // TODO(solenberg): Install SRTP encryption policy. - TEST(true, "Built-in SRTP support is deprecated. Enable it again by " - "setting an external encryption policy, i.e.:\n\r" - "_veEncryptionPtr->RegisterExternalEncryption(channel, myPolicy)"); - } - else - { - // TODO(solenberg): Uninstall SRTP encryption policy, i.e.: - // _veEncryptionPtr->DeRegisterExternalEncryption(channel); - } - if (ret == -1) - { - // restore inital state since API call failed - button->SetCheck((check == BST_CHECKED) ? BST_UNCHECKED : BST_CHECKED); - } + TEST(true, "Built-in SRTP support is deprecated."); } void CWinTestDlg::OnBnClickedCheckSrtpRx2() { - int ret(0); - int channel = GetDlgItemInt(IDC_EDIT_2); - CButton* button = (CButton*)GetDlgItem(IDC_CHECK_SRTP_RX_2); - int check = button->GetCheck(); - const bool enable = (check == BST_CHECKED); - bool useForRTCP(false); - if (enable) - { - (_checkSrtpRx2++ %2 == 0) ? useForRTCP = false : useForRTCP = true; - // TODO(solenberg): Install SRTP encryption policy. - TEST(true, "Built-in SRTP support is deprecated. Enable it again by " - "setting an external encryption policy, i.e.:\n\r" - "_veEncryptionPtr->RegisterExternalEncryption(channel, myPolicy)"); - } - else - { - // TODO(solenberg): Uninstall SRTP encryption policy, i.e.: - // _veEncryptionPtr->DeRegisterExternalEncryption(channel); - } - if (ret == -1) - { - // restore inital state since API call failed - button->SetCheck((check == BST_CHECKED) ? BST_UNCHECKED : BST_CHECKED); - } + TEST(true, "Built-in SRTP support is deprecated."); } void CWinTestDlg::OnBnClickedCheckExtEncryption1() { - int ret(0); - int channel = GetDlgItemInt(IDC_EDIT_1); - CButton* button = (CButton*)GetDlgItem(IDC_CHECK_EXT_ENCRYPTION_1); - int check = button->GetCheck(); - const bool enable = (check == BST_CHECKED); - if (enable) - { - TEST((ret = _veEncryptionPtr->RegisterExternalEncryption(channel, *_encryptionPtr)) == 0, - _T("RegisterExternalEncryption(channel=%d, encryption=0x%x)"), channel, _encryptionPtr); - } - else - { - TEST((ret = _veEncryptionPtr->DeRegisterExternalEncryption(channel)) == 0, - _T("DeRegisterExternalEncryption(channel=%d)"), channel); - } - if (ret == -1) - { - // restore inital state since API call failed - button->SetCheck((check == BST_CHECKED) ? BST_UNCHECKED : BST_CHECKED); - } + TEST(true, "External Encryption has been removed from the API!"); } void CWinTestDlg::OnBnClickedCheckExtEncryption2() { - int ret(0); - int channel = GetDlgItemInt(IDC_EDIT_2); - CButton* button = (CButton*)GetDlgItem(IDC_CHECK_EXT_ENCRYPTION_2); - int check = button->GetCheck(); - const bool enable = (check == BST_CHECKED); - if (enable) - { - TEST((ret = _veEncryptionPtr->RegisterExternalEncryption(channel, *_encryptionPtr)) == 0, - _T("RegisterExternalEncryption(channel=%d, encryption=0x%x)"), channel, _encryptionPtr); - } - else - { - TEST((ret = _veEncryptionPtr->DeRegisterExternalEncryption(channel)) == 0, - _T("DeRegisterExternalEncryption(channel=%d)"), channel); - } - if (ret == -1) - { - // restore inital state since API call failed - button->SetCheck((check == BST_CHECKED) ? BST_UNCHECKED : BST_CHECKED); - } + TEST(true, "External Encryption has been removed from the API!"); } void CWinTestDlg::OnBnClickedButtonDtmf1() @@ -3452,13 +3226,14 @@ TEST(_veApmPtr->SetTypingDetectionStatus(enable) == 0, _T("SetTypingDetectionStatus(enable=%d)"), enable); } -void CWinTestDlg::OnBnClickedCheckFEC() +void CWinTestDlg::OnBnClickedCheckRED() { - CButton* button = (CButton*)GetDlgItem(IDC_CHECK_FEC); + CButton* button = (CButton*)GetDlgItem(IDC_CHECK_RED); int channel = GetDlgItemInt(IDC_EDIT_1); int check = button->GetCheck(); const bool enable = (check == BST_CHECKED); - TEST(_veRtpRtcpPtr->SetFECStatus(channel, enable) == 0, _T("SetFECStatus(enable=%d)"), enable); + TEST(_veRtpRtcpPtr->SetREDStatus(channel, enable) == 0, + _T("SetREDStatus(enable=%d)"), enable); } // ---------------------------------------------------------------------------- diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/win_test/WinTestDlg.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/win_test/WinTestDlg.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/win_test/WinTestDlg.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/win_test/WinTestDlg.h 2015-02-03 14:33:38.000000000 +0000 @@ -83,7 +83,6 @@ #include "webrtc/voice_engine/include/voe_base.h" #include "webrtc/voice_engine/include/voe_codec.h" #include "webrtc/voice_engine/include/voe_dtmf.h" -#include "webrtc/voice_engine/include/voe_encryption.h" #include "webrtc/voice_engine/include/voe_external_media.h" #include "webrtc/voice_engine/include/voe_file.h" #include "webrtc/voice_engine/include/voe_hardware.h" @@ -97,8 +96,6 @@ #include "webrtc/voice_engine/include/voe_rtp_rtcp.h" class MediaProcessImpl; -class ConnectionObserver; -class MyEncryption; class RxCallback; class MyTransport; @@ -153,7 +150,6 @@ VoECodec* _veCodecPtr; VoEExternalMedia* _veExternalMediaPtr; VoEVolumeControl* _veVolumeControlPtr; - VoEEncryption* _veEncryptionPtr; VoEHardware* _veHardwarePtr; VoEVideoSync* _veVideoSyncPtr; VoENetwork* _veNetworkPtr; @@ -164,8 +160,6 @@ MyTransport* _transportPtr; MediaProcessImpl* _externalMediaPtr; - ConnectionObserver* _connectionObserverPtr; - MyEncryption* _encryptionPtr; RxCallback* _rxVadObserverPtr; private: @@ -252,8 +246,6 @@ afx_msg void OnBnClickedCheckSrtpRx1(); afx_msg void OnBnClickedCheckSrtpTx2(); afx_msg void OnBnClickedCheckSrtpRx2(); - afx_msg void OnBnClickedCheckExtEncryption1(); - afx_msg void OnBnClickedCheckExtEncryption2(); afx_msg void OnBnClickedButtonDtmf1(); afx_msg void OnBnClickedCheckRecMic(); afx_msg void OnBnClickedButtonDtmf2(); @@ -274,7 +266,7 @@ afx_msg void OnBnClickedCheckNs1(); afx_msg void OnBnClickedCheckRecCall(); afx_msg void OnBnClickedCheckTypingDetection(); - afx_msg void OnBnClickedCheckFEC(); + afx_msg void OnBnClickedCheckRED(); afx_msg void OnBnClickedButtonClearErrorCallback(); afx_msg void OnBnClickedCheckBwe1(); }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/win_test/WinTest.rc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/win_test/WinTest.rc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/win_test/WinTest.rc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/test/win_test/WinTest.rc 2015-02-03 14:33:38.000000000 +0000 @@ -207,7 +207,7 @@ CONTROL "TypingDetect",IDC_CHECK_TYPING_DETECTION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,572,156,60,14,WS_EX_DLGMODALFRAME EDITTEXT IDC_EDIT_AUDIO_LAYER,28,224,116,14,ES_AUTOHSCROLL | ES_READONLY EDITTEXT IDC_EDIT_CPU_LOAD,152,224,116,14,ES_AUTOHSCROLL | ES_READONLY - CONTROL "FEC",IDC_CHECK_FEC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,176,55,28,14,WS_EX_DLGMODALFRAME + CONTROL "RED",IDC_CHECK_RED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,176,55,28,14,WS_EX_DLGMODALFRAME LTEXT "=> Callbacks",IDC_STATIC_ERROR_CALLBACK,283,226,43,8 EDITTEXT IDC_EDIT_ERROR_CALLBACK,328,224,312,14,ES_AUTOHSCROLL PUSHBUTTON "Clear",IDC_BUTTON_CLEAR_ERROR_CALLBACK,644,224,24,14 diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/transmit_mixer.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/transmit_mixer.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/transmit_mixer.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/transmit_mixer.cc 2015-02-03 14:33:38.000000000 +0000 @@ -25,14 +25,8 @@ #define WEBRTC_ABS(a) (((a) < 0) ? -(a) : (a)) namespace webrtc { - namespace voe { -// Used for downmixing before resampling. -// TODO(ajm): audio_device should advertise the maximum sample rate it can -// provide. -static const int kMaxMonoDeviceDataSizeSamples = 960; // 10 ms, 96 kHz, mono. - // TODO(ajm): The thread safety of this is dubious... void TransmitMixer::OnPeriodicProcess() @@ -316,10 +310,7 @@ if (channel->Sending()) { CodecInst codec; channel->GetSendCodec(codec); - // TODO(tlegrand): Remove the 32 kHz restriction once we have full 48 kHz - // support in Audio Coding Module. - *max_sample_rate = std::min(32000, - std::max(*max_sample_rate, codec.plfreq)); + *max_sample_rate = std::max(*max_sample_rate, codec.plfreq); *max_channels = std::max(*max_channels, codec.channels); } } @@ -342,13 +333,10 @@ totalDelayMS, clockDrift, currentMicLevel); // --- Resample input audio and create/store the initial audio frame - if (GenerateAudioFrame(static_cast(audioSamples), - nSamples, - nChannels, - samplesPerSec) == -1) - { - return -1; - } + GenerateAudioFrame(static_cast(audioSamples), + nSamples, + nChannels, + samplesPerSec); { CriticalSectionScoped cs(&_callbackCritSect); @@ -397,7 +385,12 @@ } // --- Record to file - if (_fileRecording) + bool file_recording = false; + { + CriticalSectionScoped cs(&_critSect); + file_recording = _fileRecording; + } + if (file_recording) { RecordAudioToFile(_audioFrame.sample_rate_hz_); } @@ -428,10 +421,7 @@ it.Increment()) { Channel* channelPtr = it.GetChannel(); - if (channelPtr->InputIsOnHold()) - { - channelPtr->UpdateLocalTimeStamp(); - } else if (channelPtr->Sending()) + if (channelPtr->Sending()) { // Demultiplex makes a copy of its input. channelPtr->Demultiplex(_audioFrame); @@ -447,9 +437,7 @@ voe::ChannelOwner ch = _channelManagerPtr->GetChannel(voe_channels[i]); voe::Channel* channel_ptr = ch.channel(); if (channel_ptr) { - if (channel_ptr->InputIsOnHold()) { - channel_ptr->UpdateLocalTimeStamp(); - } else if (channel_ptr->Sending()) { + if (channel_ptr->Sending()) { // Demultiplex makes a copy of its input. channel_ptr->Demultiplex(_audioFrame); channel_ptr->PrepareEncodeAndSend(_audioFrame.sample_rate_hz_); @@ -468,7 +456,7 @@ it.Increment()) { Channel* channelPtr = it.GetChannel(); - if (channelPtr->Sending() && !channelPtr->InputIsOnHold()) + if (channelPtr->Sending()) { channelPtr->EncodeAndSend(); } @@ -481,7 +469,7 @@ for (int i = 0; i < number_of_voe_channels; ++i) { voe::ChannelOwner ch = _channelManagerPtr->GetChannel(voe_channels[i]); voe::Channel* channel_ptr = ch.channel(); - if (channel_ptr && channel_ptr->Sending() && !channel_ptr->InputIsOnHold()) + if (channel_ptr && channel_ptr->Sending()) channel_ptr->EncodeAndSend(); } } @@ -693,34 +681,6 @@ return _filePlaying; } -int TransmitMixer::ScaleFileAsMicrophonePlayout(float scale) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1), - "TransmitMixer::ScaleFileAsMicrophonePlayout(scale=%5.3f)", - scale); - - CriticalSectionScoped cs(&_critSect); - - if (!_filePlaying) - { - _engineStatisticsPtr->SetLastError( - VE_INVALID_OPERATION, kTraceError, - "ScaleFileAsMicrophonePlayout() isnot playing file"); - return -1; - } - - if ((_filePlayerPtr == NULL) || - (_filePlayerPtr->SetAudioScaling(scale) != 0)) - { - _engineStatisticsPtr->SetLastError( - VE_BAD_ARGUMENT, kTraceError, - "SetAudioScaling() failed to scale playout"); - return -1; - } - - return 0; -} - int TransmitMixer::StartRecordingMicrophone(const char* fileName, const CodecInst* codecInst) { @@ -728,6 +688,8 @@ "TransmitMixer::StartRecordingMicrophone(fileName=%s)", fileName); + CriticalSectionScoped cs(&_critSect); + if (_fileRecording) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1), @@ -761,8 +723,6 @@ format = kFileFormatCompressedFile; } - CriticalSectionScoped cs(&_critSect); - // Destroy the old instance if (_fileRecorderPtr) { @@ -807,6 +767,8 @@ WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1), "TransmitMixer::StartRecordingMicrophone()"); + CriticalSectionScoped cs(&_critSect); + if (_fileRecording) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1), @@ -839,8 +801,6 @@ format = kFileFormatCompressedFile; } - CriticalSectionScoped cs(&_critSect); - // Destroy the old instance if (_fileRecorderPtr) { @@ -884,6 +844,8 @@ WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1), "TransmitMixer::StopRecordingMicrophone()"); + CriticalSectionScoped cs(&_critSect); + if (!_fileRecording) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1), @@ -891,8 +853,6 @@ return 0; } - CriticalSectionScoped cs(&_critSect); - if (_fileRecorderPtr->StopRecording() != 0) { _engineStatisticsPtr->SetLastError( @@ -1170,65 +1130,44 @@ bool TransmitMixer::IsRecordingMic() { - + CriticalSectionScoped cs(&_critSect); return _fileRecording; } -// TODO(andrew): use RemixAndResample for this. // Note that if drift compensation is done here, a buffering stage will be // needed and this will need to switch to non-fixed resamples. -int TransmitMixer::GenerateAudioFrame(const int16_t audio[], - int samples_per_channel, - int num_channels, - int sample_rate_hz) { - int destination_rate; +void TransmitMixer::GenerateAudioFrame(const int16_t* audio, + int samples_per_channel, + int num_channels, + int sample_rate_hz) { + int codec_rate; int num_codec_channels; - GetSendCodecInfo(&destination_rate, &num_codec_channels); - - // Never upsample the capture signal here. This should be done at the - // end of the send chain. - destination_rate = std::min(destination_rate, sample_rate_hz); - stereo_codec_ = num_codec_channels == 2; - - const int16_t* audio_ptr = audio; - int16_t mono_audio[kMaxMonoDeviceDataSizeSamples]; - assert(samples_per_channel <= kMaxMonoDeviceDataSizeSamples); - // If no stereo codecs are in use, we downmix a stereo stream from the - // device early in the chain, before resampling. - if (num_channels == 2 && !stereo_codec_) { - AudioFrameOperations::StereoToMono(audio, samples_per_channel, - mono_audio); - audio_ptr = mono_audio; - num_channels = 1; - } - - if (resampler_.InitializeIfNeeded(sample_rate_hz, - destination_rate, - num_channels) != 0) { - WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1), - "TransmitMixer::GenerateAudioFrame() unable to resample"); - return -1; + GetSendCodecInfo(&codec_rate, &num_codec_channels); + // TODO(ajm): This currently restricts the sample rate to 32 kHz. + // See: https://code.google.com/p/webrtc/issues/detail?id=3146 + // When 48 kHz is supported natively by AudioProcessing, this will have + // to be changed to handle 44.1 kHz. + int max_sample_rate_hz = kAudioProcMaxNativeSampleRateHz; + if (audioproc_->echo_control_mobile()->is_enabled()) { + // AECM only supports 8 and 16 kHz. + max_sample_rate_hz = 16000; } + codec_rate = std::min(codec_rate, max_sample_rate_hz); + stereo_codec_ = num_codec_channels == 2; - int out_length = resampler_.Resample(audio_ptr, - samples_per_channel * num_channels, - _audioFrame.data_, - AudioFrame::kMaxDataSizeSamples); - if (out_length == -1) { - WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1), - "TransmitMixer::GenerateAudioFrame() resampling failed"); - return -1; + if (!mono_buffer_.get()) { + // Temporary space for DownConvertToCodecFormat. + mono_buffer_.reset(new int16_t[kMaxMonoDataSizeSamples]); } - - _audioFrame.samples_per_channel_ = out_length / num_channels; - _audioFrame.id_ = _instanceId; - _audioFrame.timestamp_ = -1; - _audioFrame.sample_rate_hz_ = destination_rate; - _audioFrame.speech_type_ = AudioFrame::kNormalSpeech; - _audioFrame.vad_activity_ = AudioFrame::kVadUnknown; - _audioFrame.num_channels_ = num_channels; - - return 0; + DownConvertToCodecFormat(audio, + samples_per_channel, + num_channels, + sample_rate_hz, + num_codec_channels, + codec_rate, + mono_buffer_.get(), + &resampler_, + &_audioFrame); } int32_t TransmitMixer::RecordAudioToFile( @@ -1257,7 +1196,7 @@ int32_t TransmitMixer::MixOrReplaceAudioWithFile( int mixingFrequency) { - scoped_array fileBuffer(new int16_t[640]); + scoped_ptr fileBuffer(new int16_t[640]); int fileSamples(0); { @@ -1288,18 +1227,18 @@ { // Currently file stream is always mono. // TODO(xians): Change the code when FilePlayer supports real stereo. - Utility::MixWithSat(_audioFrame.data_, - _audioFrame.num_channels_, - fileBuffer.get(), - 1, - fileSamples); + MixWithSat(_audioFrame.data_, + _audioFrame.num_channels_, + fileBuffer.get(), + 1, + fileSamples); } else { // Replace ACM audio with file. // Currently file stream is always mono. // TODO(xians): Change the code when FilePlayer supports real stereo. _audioFrame.UpdateFrame(-1, - -1, + 0xFFFFFFFF, fileBuffer.get(), fileSamples, mixingFrequency, @@ -1411,5 +1350,4 @@ } } // namespace voe - } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/transmit_mixer.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/transmit_mixer.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/transmit_mixer.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/transmit_mixer.h 2015-02-03 14:33:38.000000000 +0000 @@ -17,6 +17,7 @@ #include "webrtc/modules/interface/module_common_types.h" #include "webrtc/modules/utility/interface/file_player.h" #include "webrtc/modules/utility/interface/file_recorder.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/voice_engine/include/voe_base.h" #include "webrtc/voice_engine/level_indicator.h" #include "webrtc/voice_engine/monitor_module.h" @@ -36,9 +37,7 @@ class Statistics; class TransmitMixer : public MonitorObserver, - public FileCallback - -{ + public FileCallback { public: static int32_t Create(TransmitMixer*& mixer, uint32_t instanceId); @@ -118,8 +117,6 @@ int IsPlayingFileAsMicrophone() const; - int ScaleFileAsMicrophonePlayout(float scale); - int StartRecordingMicrophone(const char* fileName, const CodecInst* codecInst); @@ -175,10 +172,10 @@ // sending codecs. void GetSendCodecInfo(int* max_sample_rate, int* max_channels); - int GenerateAudioFrame(const int16_t audioSamples[], - int nSamples, - int nChannels, - int samplesPerSec); + void GenerateAudioFrame(const int16_t audioSamples[], + int nSamples, + int nChannels, + int samplesPerSec); int32_t RecordAudioToFile(uint32_t mixingFrequency); int32_t MixOrReplaceAudioWithFile( @@ -201,7 +198,7 @@ // owns MonitorModule _monitorModule; AudioFrame _audioFrame; - PushResampler resampler_; // ADM sample rate -> mixing rate + PushResampler resampler_; // ADM sample rate -> mixing rate FilePlayer* _filePlayerPtr; FileRecorder* _fileRecorderPtr; FileRecorder* _fileCallRecorderPtr; @@ -232,6 +229,7 @@ int32_t _remainingMuteMicTimeMs; bool stereo_codec_; bool swap_stereo_channels_; + scoped_ptr mono_buffer_; }; } // namespace voe diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/utility.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/utility.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/utility.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/utility.cc 2015-02-03 14:33:38.000000000 +0000 @@ -10,116 +10,149 @@ #include "webrtc/voice_engine/utility.h" +#include "webrtc/common_audio/resampler/include/push_resampler.h" #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/interface/module.h" -#include "webrtc/system_wrappers/interface/trace.h" - -namespace webrtc -{ - -namespace voe -{ -enum{kMaxTargetLen = 2*32*10}; // stereo 32KHz 10ms - -void Utility::MixWithSat(int16_t target[], - int target_channel, - const int16_t source[], - int source_channel, - int source_len) -{ - assert((target_channel == 1) || (target_channel == 2)); - assert((source_channel == 1) || (source_channel == 2)); - assert(source_len <= kMaxTargetLen); - - if ((target_channel == 2) && (source_channel == 1)) - { - // Convert source from mono to stereo. - int32_t left = 0; - int32_t right = 0; - for (int i = 0; i < source_len; ++i) { - left = source[i] + target[i*2]; - right = source[i] + target[i*2 + 1]; - target[i*2] = WebRtcSpl_SatW32ToW16(left); - target[i*2 + 1] = WebRtcSpl_SatW32ToW16(right); - } - } - else if ((target_channel == 1) && (source_channel == 2)) - { - // Convert source from stereo to mono. - int32_t temp = 0; - for (int i = 0; i < source_len/2; ++i) { - temp = ((source[i*2] + source[i*2 + 1])>>1) + target[i]; - target[i] = WebRtcSpl_SatW32ToW16(temp); - } - } - else - { - int32_t temp = 0; - for (int i = 0; i < source_len; ++i) { - temp = source[i] + target[i]; - target[i] = WebRtcSpl_SatW32ToW16(temp); - } - } +#include "webrtc/common_types.h" +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/modules/utility/interface/audio_frame_operations.h" +#include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/voice_engine/voice_engine_defines.h" + +namespace webrtc { +namespace voe { + +// TODO(ajm): There is significant overlap between RemixAndResample and +// ConvertToCodecFormat. Consolidate using AudioConverter. +void RemixAndResample(const AudioFrame& src_frame, + PushResampler* resampler, + AudioFrame* dst_frame) { + const int16_t* audio_ptr = src_frame.data_; + int audio_ptr_num_channels = src_frame.num_channels_; + int16_t mono_audio[AudioFrame::kMaxDataSizeSamples]; + + // Downmix before resampling. + if (src_frame.num_channels_ == 2 && dst_frame->num_channels_ == 1) { + AudioFrameOperations::StereoToMono(src_frame.data_, + src_frame.samples_per_channel_, + mono_audio); + audio_ptr = mono_audio; + audio_ptr_num_channels = 1; + } + + if (resampler->InitializeIfNeeded(src_frame.sample_rate_hz_, + dst_frame->sample_rate_hz_, + audio_ptr_num_channels) == -1) { + LOG_FERR3(LS_ERROR, InitializeIfNeeded, src_frame.sample_rate_hz_, + dst_frame->sample_rate_hz_, audio_ptr_num_channels); + assert(false); + } + + const int src_length = src_frame.samples_per_channel_ * + audio_ptr_num_channels; + int out_length = resampler->Resample(audio_ptr, src_length, dst_frame->data_, + AudioFrame::kMaxDataSizeSamples); + if (out_length == -1) { + LOG_FERR3(LS_ERROR, Resample, audio_ptr, src_length, dst_frame->data_); + assert(false); + } + dst_frame->samples_per_channel_ = out_length / audio_ptr_num_channels; + + // Upmix after resampling. + if (src_frame.num_channels_ == 1 && dst_frame->num_channels_ == 2) { + // The audio in dst_frame really is mono at this point; MonoToStereo will + // set this back to stereo. + dst_frame->num_channels_ = 1; + AudioFrameOperations::MonoToStereo(dst_frame); + } + + dst_frame->timestamp_ = src_frame.timestamp_; + dst_frame->elapsed_time_ms_ = src_frame.elapsed_time_ms_; + dst_frame->ntp_time_ms_ = src_frame.ntp_time_ms_; } -void Utility::MixSubtractWithSat(int16_t target[], - const int16_t source[], - uint16_t len) -{ - int32_t temp(0); - for (int i = 0; i < len; i++) - { - temp = target[i] - source[i]; - if (temp > 32767) - target[i] = 32767; - else if (temp < -32768) - target[i] = -32768; - else - target[i] = (int16_t) temp; - } +void DownConvertToCodecFormat(const int16_t* src_data, + int samples_per_channel, + int num_channels, + int sample_rate_hz, + int codec_num_channels, + int codec_rate_hz, + int16_t* mono_buffer, + PushResampler* resampler, + AudioFrame* dst_af) { + assert(samples_per_channel <= kMaxMonoDataSizeSamples); + assert(num_channels == 1 || num_channels == 2); + assert(codec_num_channels == 1 || codec_num_channels == 2); + dst_af->Reset(); + + // Never upsample the capture signal here. This should be done at the + // end of the send chain. + int destination_rate = std::min(codec_rate_hz, sample_rate_hz); + + // If no stereo codecs are in use, we downmix a stereo stream from the + // device early in the chain, before resampling. + if (num_channels == 2 && codec_num_channels == 1) { + AudioFrameOperations::StereoToMono(src_data, samples_per_channel, + mono_buffer); + src_data = mono_buffer; + num_channels = 1; + } + + if (resampler->InitializeIfNeeded( + sample_rate_hz, destination_rate, num_channels) != 0) { + LOG_FERR3(LS_ERROR, + InitializeIfNeeded, + sample_rate_hz, + destination_rate, + num_channels); + assert(false); + } + + const int in_length = samples_per_channel * num_channels; + int out_length = resampler->Resample( + src_data, in_length, dst_af->data_, AudioFrame::kMaxDataSizeSamples); + if (out_length == -1) { + LOG_FERR3(LS_ERROR, Resample, src_data, in_length, dst_af->data_); + assert(false); + } + + dst_af->samples_per_channel_ = out_length / num_channels; + dst_af->sample_rate_hz_ = destination_rate; + dst_af->num_channels_ = num_channels; } -void Utility::MixAndScaleWithSat(int16_t target[], - const int16_t source[], float scale, - uint16_t len) -{ - int32_t temp(0); - for (int i = 0; i < len; i++) - { - temp = (int32_t) (target[i] + scale * source[i]); - if (temp > 32767) - target[i] = 32767; - else if (temp < -32768) - target[i] = -32768; - else - target[i] = (int16_t) temp; - } -} - -void Utility::Scale(int16_t vector[], float scale, uint16_t len) -{ - for (int i = 0; i < len; i++) - { - vector[i] = (int16_t) (scale * vector[i]); - } -} - -void Utility::ScaleWithSat(int16_t vector[], float scale, - uint16_t len) -{ - int32_t temp(0); - for (int i = 0; i < len; i++) - { - temp = (int32_t) (scale * vector[i]); - if (temp > 32767) - vector[i] = 32767; - else if (temp < -32768) - vector[i] = -32768; - else - vector[i] = (int16_t) temp; +void MixWithSat(int16_t target[], + int target_channel, + const int16_t source[], + int source_channel, + int source_len) { + assert(target_channel == 1 || target_channel == 2); + assert(source_channel == 1 || source_channel == 2); + + if (target_channel == 2 && source_channel == 1) { + // Convert source from mono to stereo. + int32_t left = 0; + int32_t right = 0; + for (int i = 0; i < source_len; ++i) { + left = source[i] + target[i * 2]; + right = source[i] + target[i * 2 + 1]; + target[i * 2] = WebRtcSpl_SatW32ToW16(left); + target[i * 2 + 1] = WebRtcSpl_SatW32ToW16(right); + } + } else if (target_channel == 1 && source_channel == 2) { + // Convert source from stereo to mono. + int32_t temp = 0; + for (int i = 0; i < source_len / 2; ++i) { + temp = ((source[i * 2] + source[i * 2 + 1]) >> 1) + target[i]; + target[i] = WebRtcSpl_SatW32ToW16(temp); + } + } else { + int32_t temp = 0; + for (int i = 0; i < source_len; ++i) { + temp = source[i] + target[i]; + target[i] = WebRtcSpl_SatW32ToW16(temp); } + } } } // namespace voe - } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/utility.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/utility.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/utility.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/utility.h 2015-02-03 14:33:38.000000000 +0000 @@ -12,47 +12,52 @@ * Contains functions often used by different parts of VoiceEngine. */ -#ifndef WEBRTC_VOICE_ENGINE_UTILITY_H -#define WEBRTC_VOICE_ENGINE_UTILITY_H +#ifndef WEBRTC_VOICE_ENGINE_UTILITY_H_ +#define WEBRTC_VOICE_ENGINE_UTILITY_H_ +#include "webrtc/common_audio/resampler/include/push_resampler.h" #include "webrtc/typedefs.h" -#include "webrtc/voice_engine/voice_engine_defines.h" -namespace webrtc -{ +namespace webrtc { -class Module; +class AudioFrame; -namespace voe -{ - -class Utility -{ -public: - static void MixWithSat(int16_t target[], - int target_channel, - const int16_t source[], - int source_channel, - int source_len); - - static void MixSubtractWithSat(int16_t target[], - const int16_t source[], - uint16_t len); - - static void MixAndScaleWithSat(int16_t target[], - const int16_t source[], - float scale, - uint16_t len); - - static void Scale(int16_t vector[], float scale, uint16_t len); - - static void ScaleWithSat(int16_t vector[], - float scale, - uint16_t len); -}; +namespace voe { -} // namespace voe +// Upmix or downmix and resample the audio in |src_frame| to |dst_frame|. +// Expects |dst_frame| to have its sample rate and channels members set to the +// desired values. Updates the samples per channel member accordingly. No other +// members will be changed. +void RemixAndResample(const AudioFrame& src_frame, + PushResampler* resampler, + AudioFrame* dst_frame); + +// Downmix and downsample the audio in |src_data| to |dst_af| as necessary, +// specified by |codec_num_channels| and |codec_rate_hz|. |mono_buffer| is +// temporary space and must be of sufficient size to hold the downmixed source +// audio (recommend using a size of kMaxMonoDataSizeSamples). +// +// |dst_af| will have its data and format members (sample rate, channels and +// samples per channel) set appropriately. No other members will be changed. +// TODO(ajm): For now, this still calls Reset() on |dst_af|. Remove this, as +// it shouldn't be needed. +void DownConvertToCodecFormat(const int16_t* src_data, + int samples_per_channel, + int num_channels, + int sample_rate_hz, + int codec_num_channels, + int codec_rate_hz, + int16_t* mono_buffer, + PushResampler* resampler, + AudioFrame* dst_af); + +void MixWithSat(int16_t target[], + int target_channel, + const int16_t source[], + int source_channel, + int source_len); +} // namespace voe } // namespace webrtc -#endif // WEBRTC_VOICE_ENGINE_UTILITY_H +#endif // WEBRTC_VOICE_ENGINE_UTILITY_H_ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/utility_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/utility_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/utility_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/utility_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/common_audio/resampler/include/push_resampler.h" +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/voice_engine/utility.h" +#include "webrtc/voice_engine/voice_engine_defines.h" + +namespace webrtc { +namespace voe { +namespace { + +enum FunctionToTest { + TestRemixAndResample, + TestDownConvertToCodecFormat +}; + +class UtilityTest : public ::testing::Test { + protected: + UtilityTest() { + src_frame_.sample_rate_hz_ = 16000; + src_frame_.samples_per_channel_ = src_frame_.sample_rate_hz_ / 100; + src_frame_.num_channels_ = 1; + dst_frame_.CopyFrom(src_frame_); + golden_frame_.CopyFrom(src_frame_); + } + + void RunResampleTest(int src_channels, int src_sample_rate_hz, + int dst_channels, int dst_sample_rate_hz, + FunctionToTest function); + + PushResampler resampler_; + AudioFrame src_frame_; + AudioFrame dst_frame_; + AudioFrame golden_frame_; +}; + +// Sets the signal value to increase by |data| with every sample. Floats are +// used so non-integer values result in rounding error, but not an accumulating +// error. +void SetMonoFrame(AudioFrame* frame, float data, int sample_rate_hz) { + memset(frame->data_, 0, sizeof(frame->data_)); + frame->num_channels_ = 1; + frame->sample_rate_hz_ = sample_rate_hz; + frame->samples_per_channel_ = sample_rate_hz / 100; + for (int i = 0; i < frame->samples_per_channel_; i++) { + frame->data_[i] = data * i; + } +} + +// Keep the existing sample rate. +void SetMonoFrame(AudioFrame* frame, float data) { + SetMonoFrame(frame, data, frame->sample_rate_hz_); +} + +// Sets the signal value to increase by |left| and |right| with every sample in +// each channel respectively. +void SetStereoFrame(AudioFrame* frame, float left, float right, + int sample_rate_hz) { + memset(frame->data_, 0, sizeof(frame->data_)); + frame->num_channels_ = 2; + frame->sample_rate_hz_ = sample_rate_hz; + frame->samples_per_channel_ = sample_rate_hz / 100; + for (int i = 0; i < frame->samples_per_channel_; i++) { + frame->data_[i * 2] = left * i; + frame->data_[i * 2 + 1] = right * i; + } +} + +// Keep the existing sample rate. +void SetStereoFrame(AudioFrame* frame, float left, float right) { + SetStereoFrame(frame, left, right, frame->sample_rate_hz_); +} + +void VerifyParams(const AudioFrame& ref_frame, const AudioFrame& test_frame) { + EXPECT_EQ(ref_frame.num_channels_, test_frame.num_channels_); + EXPECT_EQ(ref_frame.samples_per_channel_, test_frame.samples_per_channel_); + EXPECT_EQ(ref_frame.sample_rate_hz_, test_frame.sample_rate_hz_); +} + +// Computes the best SNR based on the error between |ref_frame| and +// |test_frame|. It allows for up to a |max_delay| in samples between the +// signals to compensate for the resampling delay. +float ComputeSNR(const AudioFrame& ref_frame, const AudioFrame& test_frame, + int max_delay) { + VerifyParams(ref_frame, test_frame); + float best_snr = 0; + int best_delay = 0; + for (int delay = 0; delay <= max_delay; delay++) { + float mse = 0; + float variance = 0; + for (int i = 0; i < ref_frame.samples_per_channel_ * + ref_frame.num_channels_ - delay; i++) { + int error = ref_frame.data_[i] - test_frame.data_[i + delay]; + mse += error * error; + variance += ref_frame.data_[i] * ref_frame.data_[i]; + } + float snr = 100; // We assign 100 dB to the zero-error case. + if (mse > 0) + snr = 10 * log10(variance / mse); + if (snr > best_snr) { + best_snr = snr; + best_delay = delay; + } + } + printf("SNR=%.1f dB at delay=%d\n", best_snr, best_delay); + return best_snr; +} + +void VerifyFramesAreEqual(const AudioFrame& ref_frame, + const AudioFrame& test_frame) { + VerifyParams(ref_frame, test_frame); + for (int i = 0; i < ref_frame.samples_per_channel_ * ref_frame.num_channels_; + i++) { + EXPECT_EQ(ref_frame.data_[i], test_frame.data_[i]); + } +} + +void UtilityTest::RunResampleTest(int src_channels, + int src_sample_rate_hz, + int dst_channels, + int dst_sample_rate_hz, + FunctionToTest function) { + PushResampler resampler; // Create a new one with every test. + const int16_t kSrcLeft = 30; // Shouldn't overflow for any used sample rate. + const int16_t kSrcRight = 15; + const float resampling_factor = (1.0 * src_sample_rate_hz) / + dst_sample_rate_hz; + const float dst_left = resampling_factor * kSrcLeft; + const float dst_right = resampling_factor * kSrcRight; + const float dst_mono = (dst_left + dst_right) / 2; + if (src_channels == 1) + SetMonoFrame(&src_frame_, kSrcLeft, src_sample_rate_hz); + else + SetStereoFrame(&src_frame_, kSrcLeft, kSrcRight, src_sample_rate_hz); + + if (dst_channels == 1) { + SetMonoFrame(&dst_frame_, 0, dst_sample_rate_hz); + if (src_channels == 1) + SetMonoFrame(&golden_frame_, dst_left, dst_sample_rate_hz); + else + SetMonoFrame(&golden_frame_, dst_mono, dst_sample_rate_hz); + } else { + SetStereoFrame(&dst_frame_, 0, 0, dst_sample_rate_hz); + if (src_channels == 1) + SetStereoFrame(&golden_frame_, dst_left, dst_left, dst_sample_rate_hz); + else + SetStereoFrame(&golden_frame_, dst_left, dst_right, dst_sample_rate_hz); + } + + // The speex resampler has a known delay dependent on quality and rates, + // which we approximate here. Multiplying by two gives us a crude maximum + // for any resampling, as the old resampler typically (but not always) + // has lower delay. The actual delay is calculated internally based on the + // filter length in the QualityMap. + static const int kInputKernelDelaySamples = 16*3; + const int max_delay = std::min(1.0f, 1/kResamplingFactor) * + kInputKernelDelaySamples * dst_channels * 2; + printf("(%d, %d Hz) -> (%d, %d Hz) ", // SNR reported on the same line later. + src_channels, src_sample_rate_hz, dst_channels, dst_sample_rate_hz); + if (function == TestRemixAndResample) { + RemixAndResample(src_frame_, &resampler, &dst_frame_); + } else { + int16_t mono_buffer[kMaxMonoDataSizeSamples]; + DownConvertToCodecFormat(src_frame_.data_, + src_frame_.samples_per_channel_, + src_frame_.num_channels_, + src_frame_.sample_rate_hz_, + dst_frame_.num_channels_, + dst_frame_.sample_rate_hz_, + mono_buffer, + &resampler, + &dst_frame_); + } + + if (src_sample_rate_hz == 96000 && dst_sample_rate_hz == 8000) { + // The sinc resampler gives poor SNR at this extreme conversion, but we + // expect to see this rarely in practice. + EXPECT_GT(ComputeSNR(golden_frame_, dst_frame_, max_delay), 14.0f); + } else { + EXPECT_GT(ComputeSNR(golden_frame_, dst_frame_, max_delay), 46.0f); + } +} + +// These two tests assume memcpy() (no delay and no filtering) for input +// freq == output freq && same channels. RemixAndResample uses 'Fixed' +// resamplers to enable this behavior +TEST_F(UtilityTest, RemixAndResampleCopyFrameSucceeds) { + // Stereo -> stereo. + SetStereoFrame(&src_frame_, 10, 10); + SetStereoFrame(&dst_frame_, 0, 0); + RemixAndResample(src_frame_, &resampler_, &dst_frame_); + VerifyFramesAreEqual(src_frame_, dst_frame_); + + // Mono -> mono. + SetMonoFrame(&src_frame_, 20); + SetMonoFrame(&dst_frame_, 0); + RemixAndResample(src_frame_, &resampler_, &dst_frame_); + VerifyFramesAreEqual(src_frame_, dst_frame_); +} + +TEST_F(UtilityTest, RemixAndResampleMixingOnlySucceeds) { + // Stereo -> mono. + SetStereoFrame(&dst_frame_, 0, 0); + SetMonoFrame(&src_frame_, 10); + SetStereoFrame(&golden_frame_, 10, 10); + RemixAndResample(src_frame_, &resampler_, &dst_frame_); + VerifyFramesAreEqual(dst_frame_, golden_frame_); + + // Mono -> stereo. + SetMonoFrame(&dst_frame_, 0); + SetStereoFrame(&src_frame_, 10, 20); + SetMonoFrame(&golden_frame_, 15); + RemixAndResample(src_frame_, &resampler_, &dst_frame_); + VerifyFramesAreEqual(golden_frame_, dst_frame_); +} + +TEST_F(UtilityTest, RemixAndResampleSucceeds) { + const int kSampleRates[] = {8000, 16000, 32000, 44100, 48000, 96000}; + const int kSampleRatesSize = sizeof(kSampleRates) / sizeof(*kSampleRates); + const int kChannels[] = {1, 2}; + const int kChannelsSize = sizeof(kChannels) / sizeof(*kChannels); + for (int src_rate = 0; src_rate < kSampleRatesSize; src_rate++) { + for (int dst_rate = 0; dst_rate < kSampleRatesSize; dst_rate++) { + for (int src_channel = 0; src_channel < kChannelsSize; src_channel++) { + for (int dst_channel = 0; dst_channel < kChannelsSize; dst_channel++) { + RunResampleTest(kChannels[src_channel], kSampleRates[src_rate], + kChannels[dst_channel], kSampleRates[dst_rate], + TestRemixAndResample); + } + } + } + } +} + +TEST_F(UtilityTest, ConvertToCodecFormatSucceeds) { + const int kSampleRates[] = {8000, 16000, 32000, 44100, 48000, 96000}; + const int kSampleRatesSize = sizeof(kSampleRates) / sizeof(*kSampleRates); + const int kChannels[] = {1, 2}; + const int kChannelsSize = sizeof(kChannels) / sizeof(*kChannels); + for (int src_rate = 0; src_rate < kSampleRatesSize; src_rate++) { + for (int dst_rate = 0; dst_rate < kSampleRatesSize; dst_rate++) { + for (int src_channel = 0; src_channel < kChannelsSize; src_channel++) { + for (int dst_channel = 0; dst_channel < kChannelsSize; dst_channel++) { + if (dst_rate <= src_rate && dst_channel <= src_channel) { + RunResampleTest(kChannels[src_channel], kSampleRates[src_rate], + kChannels[src_channel], kSampleRates[dst_rate], + TestDownConvertToCodecFormat); + } + } + } + } + } +} + +} // namespace +} // namespace voe +} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_auto_test.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_auto_test.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_auto_test.isolate 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_auto_test.isolate 2015-02-03 14:33:38.000000000 +0000 @@ -8,28 +8,23 @@ { 'conditions': [ ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. 'variables': { - 'isolate_dependency_untracked': [ - '../../../data/', - '../../../resources/', + 'files': [ + '<(DEPTH)/data/', + '<(DEPTH)/resources/', ], }, }], ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../../testing/test_env.py', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/voe_auto_test<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_tracked': [ - '../../testing/test_env.py', + 'files': [ + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/voe_auto_test<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '../../tools/swarming_client/', - ], }, }], ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_base_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_base_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_base_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_base_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -25,13 +25,6 @@ #include "webrtc/voice_engine/utility.h" #include "webrtc/voice_engine/voice_engine_impl.h" -#if (defined(_WIN32) && defined(_DLL) && (_MSC_VER == 1400)) -// Fix for VS 2005 MD/MDd link problem -#include -extern "C" - { FILE _iob[3] = { __iob_func()[0], __iob_func()[1], __iob_func()[2]}; } -#endif - namespace webrtc { @@ -49,8 +42,7 @@ VoEBaseImpl::VoEBaseImpl(voe::SharedData* shared) : _voiceEngineObserverPtr(NULL), _callbackCritSect(*CriticalSectionWrapper::CreateCriticalSection()), - _voiceEngineObserver(false), _oldVoEMicLevel(0), _oldMicLevel(0), - _shared(shared) + _voiceEngineObserver(false), _shared(shared) { WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1), "VoEBaseImpl() - ctor"); @@ -133,19 +125,19 @@ uint32_t samplesPerSec, uint32_t totalDelayMS, int32_t clockDrift, - uint32_t currentMicLevel, + uint32_t micLevel, bool keyPressed, uint32_t& newMicLevel) { WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_shared->instance_id(), -1), "VoEBaseImpl::RecordedDataIsAvailable(nSamples=%u, " "nBytesPerSample=%u, nChannels=%u, samplesPerSec=%u, " - "totalDelayMS=%u, clockDrift=%d, currentMicLevel=%u)", + "totalDelayMS=%u, clockDrift=%d, micLevel=%u)", nSamples, nBytesPerSample, nChannels, samplesPerSec, - totalDelayMS, clockDrift, currentMicLevel); + totalDelayMS, clockDrift, micLevel); newMicLevel = static_cast(ProcessRecordedDataWithAPM( NULL, 0, audioSamples, samplesPerSec, nChannels, nSamples, - totalDelayMS, clockDrift, currentMicLevel, keyPressed)); + totalDelayMS, clockDrift, micLevel, keyPressed)); return 0; } @@ -156,41 +148,23 @@ uint8_t nChannels, uint32_t samplesPerSec, void* audioSamples, - uint32_t& nSamplesOut) + uint32_t& nSamplesOut, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) { - WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_shared->instance_id(), -1), - "VoEBaseImpl::NeedMorePlayData(nSamples=%u, " - "nBytesPerSample=%d, nChannels=%d, samplesPerSec=%u)", - nSamples, nBytesPerSample, nChannels, samplesPerSec); - - assert(_shared->output_mixer() != NULL); - - // TODO(andrew): if the device is running in mono, we should tell the mixer - // here so that it will only request mono from AudioCodingModule. - // Perform mixing of all active participants (channel-based mixing) - _shared->output_mixer()->MixActiveChannels(); - - // Additional operations on the combined signal - _shared->output_mixer()->DoOperationsOnCombinedSignal(); - - // Retrieve the final output mix (resampled to match the ADM) - _shared->output_mixer()->GetMixedAudio(samplesPerSec, nChannels, - &_audioFrame); - - assert(static_cast(nSamples) == _audioFrame.samples_per_channel_); - assert(samplesPerSec == - static_cast(_audioFrame.sample_rate_hz_)); - - // Deliver audio (PCM) samples to the ADM - memcpy( - (int16_t*) audioSamples, - (const int16_t*) _audioFrame.data_, - sizeof(int16_t) * (_audioFrame.samples_per_channel_ - * _audioFrame.num_channels_)); + WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_shared->instance_id(), -1), + "VoEBaseImpl::NeedMorePlayData(nSamples=%u, " + "nBytesPerSample=%d, nChannels=%d, samplesPerSec=%u)", + nSamples, nBytesPerSample, nChannels, samplesPerSec); + + GetPlayoutData(static_cast(samplesPerSec), + static_cast(nChannels), + static_cast(nSamples), true, audioSamples, + elapsed_time_ms, ntp_time_ms); - nSamplesOut = _audioFrame.samples_per_channel_; + nSamplesOut = _audioFrame.samples_per_channel_; - return 0; + return 0; } int VoEBaseImpl::OnDataAvailable(const int voe_channels[], @@ -200,16 +174,16 @@ int number_of_channels, int number_of_frames, int audio_delay_milliseconds, - int current_volume, + int volume, bool key_pressed, bool need_audio_processing) { WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_shared->instance_id(), -1), "VoEBaseImpl::OnDataAvailable(number_of_voe_channels=%d, " "sample_rate=%d, number_of_channels=%d, number_of_frames=%d, " - "audio_delay_milliseconds=%d, current_volume=%d, " + "audio_delay_milliseconds=%d, volume=%d, " "key_pressed=%d, need_audio_processing=%d)", number_of_voe_channels, sample_rate, number_of_channels, - number_of_frames, audio_delay_milliseconds, current_volume, + number_of_frames, audio_delay_milliseconds, volume, key_pressed, need_audio_processing); if (number_of_voe_channels == 0) return 0; @@ -218,14 +192,17 @@ return ProcessRecordedDataWithAPM( voe_channels, number_of_voe_channels, audio_data, sample_rate, number_of_channels, number_of_frames, audio_delay_milliseconds, - 0, current_volume, key_pressed); + 0, volume, key_pressed); } // No need to go through the APM, demultiplex the data to each VoE channel, // encode and send to the network. for (int i = 0; i < number_of_voe_channels; ++i) { - OnData(voe_channels[i], audio_data, 16, sample_rate, - number_of_channels, number_of_frames); + // TODO(ajm): In the case where multiple channels are using the same codec + // rate, this path needlessly does extra conversions. We should convert once + // and share between channels. + PushCaptureData(voe_channels[i], audio_data, 16, sample_rate, + number_of_channels, number_of_frames); } // Return 0 to indicate no need to change the volume. @@ -236,14 +213,20 @@ int bits_per_sample, int sample_rate, int number_of_channels, int number_of_frames) { + PushCaptureData(voe_channel, audio_data, bits_per_sample, sample_rate, + number_of_channels, number_of_frames); +} + +void VoEBaseImpl::PushCaptureData(int voe_channel, const void* audio_data, + int bits_per_sample, int sample_rate, + int number_of_channels, + int number_of_frames) { voe::ChannelOwner ch = _shared->channel_manager().GetChannel(voe_channel); voe::Channel* channel_ptr = ch.channel(); if (!channel_ptr) return; - if (channel_ptr->InputIsOnHold()) { - channel_ptr->UpdateLocalTimeStamp(); - } else if (channel_ptr->Sending()) { + if (channel_ptr->Sending()) { channel_ptr->Demultiplex(static_cast(audio_data), sample_rate, number_of_frames, number_of_channels); channel_ptr->PrepareEncodeAndSend(sample_rate); @@ -251,6 +234,18 @@ } } +void VoEBaseImpl::PullRenderData(int bits_per_sample, int sample_rate, + int number_of_channels, int number_of_frames, + void* audio_data, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) { + assert(bits_per_sample == 16); + assert(number_of_frames == static_cast(sample_rate / 100)); + + GetPlayoutData(sample_rate, number_of_channels, number_of_frames, false, + audio_data, elapsed_time_ms, ntp_time_ms); +} + int VoEBaseImpl::RegisterVoiceEngineObserver(VoiceEngineObserver& observer) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), @@ -392,18 +387,6 @@ _shared->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceInfo, "Init() failed to set the default output device"); } - if (_shared->audio_device()->SpeakerIsAvailable(&available) != 0) - { - _shared->SetLastError(VE_CANNOT_ACCESS_SPEAKER_VOL, kTraceInfo, - "Init() failed to check speaker availability, trying to " - "initialize speaker anyway"); - } - else if (!available) - { - _shared->SetLastError(VE_CANNOT_ACCESS_SPEAKER_VOL, kTraceInfo, - "Init() speaker not available, trying to initialize speaker " - "anyway"); - } if (_shared->audio_device()->InitSpeaker() != 0) { _shared->SetLastError(VE_CANNOT_ACCESS_SPEAKER_VOL, kTraceInfo, @@ -417,18 +400,6 @@ _shared->SetLastError(VE_SOUNDCARD_ERROR, kTraceInfo, "Init() failed to set the default input device"); } - if (_shared->audio_device()->MicrophoneIsAvailable(&available) != 0) - { - _shared->SetLastError(VE_CANNOT_ACCESS_MIC_VOL, kTraceInfo, - "Init() failed to check microphone availability, trying to " - "initialize microphone anyway"); - } - else if (!available) - { - _shared->SetLastError(VE_CANNOT_ACCESS_MIC_VOL, kTraceInfo, - "Init() microphone not available, trying to initialize " - "microphone anyway"); - } if (_shared->audio_device()->InitMicrophone() != 0) { _shared->SetLastError(VE_CANNOT_ACCESS_MIC_VOL, kTraceInfo, @@ -472,11 +443,6 @@ // Set the error state for any failures in this block. _shared->SetLastError(VE_APM_ERROR); - if (audioproc->echo_cancellation()->set_device_sample_rate_hz(48000)) { - LOG_FERR1(LS_ERROR, set_device_sample_rate_hz, 48000); - return -1; - } - // Configure AudioProcessing components. if (audioproc->high_pass_filter()->Enable(true) != 0) { LOG_FERR1(LS_ERROR, high_pass_filter()->Enable, true); @@ -811,15 +777,6 @@ accLen += len; assert(accLen < kVoiceEngineVersionMaxMessageSize); - len = AddBuildInfo(versionPtr); - if (len == -1) - { - return -1; - } - versionPtr += len; - accLen += len; - assert(accLen < kVoiceEngineVersionMaxMessageSize); - #ifdef WEBRTC_EXTERNAL_TRANSPORT len = AddExternalTransportBuild(versionPtr); if (len == -1) @@ -872,11 +829,6 @@ return 0; } -int32_t VoEBaseImpl::AddBuildInfo(char* str) const -{ - return sprintf(str, "Build: %s\n", BUILDINFO); -} - int32_t VoEBaseImpl::AddVoEVersion(char* str) const { return sprintf(str, "VoiceEngine 4.1.0\n"); @@ -903,88 +855,6 @@ return (_shared->statistics().LastError()); } - -int VoEBaseImpl::SetNetEQPlayoutMode(int channel, NetEqModes mode) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "SetNetEQPlayoutMode(channel=%i, mode=%i)", channel, mode); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "SetNetEQPlayoutMode() failed to locate channel"); - return -1; - } - return channelPtr->SetNetEQPlayoutMode(mode); -} - -int VoEBaseImpl::GetNetEQPlayoutMode(int channel, NetEqModes& mode) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetNetEQPlayoutMode(channel=%i, mode=?)", channel); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "GetNetEQPlayoutMode() failed to locate channel"); - return -1; - } - return channelPtr->GetNetEQPlayoutMode(mode); -} - -int VoEBaseImpl::SetOnHoldStatus(int channel, bool enable, OnHoldModes mode) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "SetOnHoldStatus(channel=%d, enable=%d, mode=%d)", channel, - enable, mode); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "SetOnHoldStatus() failed to locate channel"); - return -1; - } - return channelPtr->SetOnHoldStatus(enable, mode); -} - -int VoEBaseImpl::GetOnHoldStatus(int channel, bool& enabled, OnHoldModes& mode) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetOnHoldStatus(channel=%d, enabled=?, mode=?)", channel); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "GetOnHoldStatus() failed to locate channel"); - return -1; - } - return channelPtr->GetOnHoldStatus(enabled, mode); -} - int32_t VoEBaseImpl::StartPlayout() { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1), @@ -1152,48 +1022,41 @@ uint32_t number_of_frames, uint32_t audio_delay_milliseconds, int32_t clock_drift, - uint32_t current_volume, + uint32_t volume, bool key_pressed) { assert(_shared->transmit_mixer() != NULL); assert(_shared->audio_device() != NULL); uint32_t max_volume = 0; - uint16_t current_voe_mic_level = 0; + uint16_t voe_mic_level = 0; // Check for zero to skip this calculation; the consumer may use this to // indicate no volume is available. - if (current_volume != 0) { + if (volume != 0) { // Scale from ADM to VoE level range if (_shared->audio_device()->MaxMicrophoneVolume(&max_volume) == 0) { if (max_volume) { - current_voe_mic_level = static_cast( - (current_volume * kMaxVolumeLevel + + voe_mic_level = static_cast( + (volume * kMaxVolumeLevel + static_cast(max_volume / 2)) / max_volume); } } - // We learned that on certain systems (e.g Linux) the current_voe_mic_level + // We learned that on certain systems (e.g Linux) the voe_mic_level // can be greater than the maxVolumeLevel therefore - // we are going to cap the current_voe_mic_level to the maxVolumeLevel - // and change the maxVolume to current_volume if it turns out that - // the current_voe_mic_level is indeed greater than the maxVolumeLevel. - if (current_voe_mic_level > kMaxVolumeLevel) { - current_voe_mic_level = kMaxVolumeLevel; - max_volume = current_volume; + // we are going to cap the voe_mic_level to the maxVolumeLevel + // and change the maxVolume to volume if it turns out that + // the voe_mic_level is indeed greater than the maxVolumeLevel. + if (voe_mic_level > kMaxVolumeLevel) { + voe_mic_level = kMaxVolumeLevel; + max_volume = volume; } } - // Keep track if the MicLevel has been changed by the AGC, if not, - // use the old value AGC returns to let AGC continue its trend, - // so eventually the AGC is able to change the mic level. This handles - // issues with truncation introduced by the scaling. - if (_oldMicLevel == current_volume) - current_voe_mic_level = static_cast(_oldVoEMicLevel); - // Perform channel-independent operations // (APM, mix with file, record to file, mute, etc.) _shared->transmit_mixer()->PrepareDemux( audio_data, number_of_frames, number_of_channels, sample_rate, static_cast(audio_delay_milliseconds), clock_drift, - current_voe_mic_level, key_pressed); + voe_mic_level, key_pressed); // Copy the audio frame to each sending channel and perform // channel-dependent operations (file mixing, mute, etc.), encode and @@ -1213,11 +1076,7 @@ // Scale from VoE to ADM level range. uint32_t new_voe_mic_level = _shared->transmit_mixer()->CaptureLevel(); - // Keep track of the value AGC returns. - _oldVoEMicLevel = new_voe_mic_level; - _oldMicLevel = current_volume; - - if (new_voe_mic_level != current_voe_mic_level) { + if (new_voe_mic_level != voe_mic_level) { // Return the new volume if AGC has changed the volume. return static_cast( (new_voe_mic_level * max_volume + @@ -1228,4 +1087,34 @@ return 0; } +void VoEBaseImpl::GetPlayoutData(int sample_rate, int number_of_channels, + int number_of_frames, bool feed_data_to_apm, + void* audio_data, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) { + assert(_shared->output_mixer() != NULL); + + // TODO(andrew): if the device is running in mono, we should tell the mixer + // here so that it will only request mono from AudioCodingModule. + // Perform mixing of all active participants (channel-based mixing) + _shared->output_mixer()->MixActiveChannels(); + + // Additional operations on the combined signal + _shared->output_mixer()->DoOperationsOnCombinedSignal(feed_data_to_apm); + + // Retrieve the final output mix (resampled to match the ADM) + _shared->output_mixer()->GetMixedAudio(sample_rate, number_of_channels, + &_audioFrame); + + assert(number_of_frames == _audioFrame.samples_per_channel_); + assert(sample_rate == _audioFrame.sample_rate_hz_); + + // Deliver audio (PCM) samples to the ADM + memcpy(audio_data, _audioFrame.data_, + sizeof(int16_t) * number_of_frames * number_of_channels); + + *elapsed_time_ms = _audioFrame.elapsed_time_ms_; + *ntp_time_ms = _audioFrame.ntp_time_ms_; +} + } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_base_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_base_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_base_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_base_impl.h 2015-02-03 14:33:38.000000000 +0000 @@ -55,16 +55,6 @@ virtual int StopSend(int channel); - virtual int SetNetEQPlayoutMode(int channel, NetEqModes mode); - - virtual int GetNetEQPlayoutMode(int channel, NetEqModes& mode); - - virtual int SetOnHoldStatus(int channel, - bool enable, - OnHoldModes mode = kHoldSendAndPlay); - - virtual int GetOnHoldStatus(int channel, bool& enabled, OnHoldModes& mode); - virtual int GetVersion(char version[1024]); virtual int LastError(); @@ -80,7 +70,7 @@ uint32_t samplesPerSec, uint32_t totalDelayMS, int32_t clockDrift, - uint32_t currentMicLevel, + uint32_t micLevel, bool keyPressed, uint32_t& newMicLevel); @@ -89,7 +79,9 @@ uint8_t nChannels, uint32_t samplesPerSec, void* audioSamples, - uint32_t& nSamplesOut); + uint32_t& nSamplesOut, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms); virtual int OnDataAvailable(const int voe_channels[], int number_of_voe_channels, @@ -98,7 +90,7 @@ int number_of_channels, int number_of_frames, int audio_delay_milliseconds, - int current_volume, + int volume, bool key_pressed, bool need_audio_processing); @@ -106,6 +98,16 @@ int bits_per_sample, int sample_rate, int number_of_channels, int number_of_frames); + virtual void PushCaptureData(int voe_channel, const void* audio_data, + int bits_per_sample, int sample_rate, + int number_of_channels, int number_of_frames); + + virtual void PullRenderData(int bits_per_sample, int sample_rate, + int number_of_channels, int number_of_frames, + void* audio_data, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms); + // AudioDeviceObserver virtual void OnErrorIsReported(ErrorCode error); virtual void OnWarningIsReported(WarningCode warning); @@ -135,10 +137,15 @@ uint32_t number_of_frames, uint32_t audio_delay_milliseconds, int32_t clock_drift, - uint32_t current_volume, + uint32_t volume, bool key_pressed); - int32_t AddBuildInfo(char* str) const; + void GetPlayoutData(int sample_rate, int number_of_channels, + int number_of_frames, bool feed_data_to_apm, + void* audio_data, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms); + int32_t AddVoEVersion(char* str) const; // Initialize channel by setting Engine Information then initializing @@ -154,8 +161,6 @@ CriticalSectionWrapper& _callbackCritSect; bool _voiceEngineObserver; - uint32_t _oldVoEMicLevel; - uint32_t _oldMicLevel; AudioFrame _audioFrame; voe::SharedData* _shared; }; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_call_report_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_call_report_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_call_report_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_call_report_impl.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,383 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/voice_engine/voe_call_report_impl.h" - -#include "webrtc/modules/audio_processing/include/audio_processing.h" -#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/file_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" -#include "webrtc/voice_engine/channel.h" -#include "webrtc/voice_engine/include/voe_errors.h" -#include "webrtc/voice_engine/voice_engine_impl.h" - -namespace webrtc -{ - -VoECallReport* VoECallReport::GetInterface(VoiceEngine* voiceEngine) -{ -#ifndef WEBRTC_VOICE_ENGINE_CALL_REPORT_API - return NULL; -#else - if (NULL == voiceEngine) - { - return NULL; - } - VoiceEngineImpl* s = static_cast(voiceEngine); - s->AddRef(); - return s; -#endif -} - -#ifdef WEBRTC_VOICE_ENGINE_CALL_REPORT_API - -VoECallReportImpl::VoECallReportImpl(voe::SharedData* shared) : - _file(*FileWrapper::Create()), _shared(shared) -{ - WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1), - "VoECallReportImpl() - ctor"); -} - -VoECallReportImpl::~VoECallReportImpl() -{ - WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1), - "~VoECallReportImpl() - dtor"); - delete &_file; -} - -int VoECallReportImpl::ResetCallReportStatistics(int channel) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "ResetCallReportStatistics(channel=%d)", channel); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); - - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - - assert(_shared->audio_processing() != NULL); - - bool echoMode = - _shared->audio_processing()->echo_cancellation()->are_metrics_enabled(); - - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1), - " current AudioProcessingModule echo metric state %d)", - echoMode); - // Reset the APM statistics - if (_shared->audio_processing()->echo_cancellation()->enable_metrics(true) - != 0) - { - _shared->SetLastError(VE_APM_ERROR, kTraceError, - "ResetCallReportStatistics() unable to " - "set the AudioProcessingModule echo metrics state"); - return -1; - } - // Restore metric states - _shared->audio_processing()->echo_cancellation()->enable_metrics(echoMode); - - // Reset channel dependent statistics - if (channel != -1) - { - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "ResetCallReportStatistics() failed to locate channel"); - return -1; - } - channelPtr->ResetDeadOrAliveCounters(); - channelPtr->ResetRTCPStatistics(); - } else { - for (voe::ChannelManager::Iterator it(&_shared->channel_manager()); - it.IsValid(); - it.Increment()) { - it.GetChannel()->ResetDeadOrAliveCounters(); - it.GetChannel()->ResetRTCPStatistics(); - } - } - - return 0; -} - -int VoECallReportImpl::GetEchoMetricSummary(EchoStatistics& stats) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetEchoMetricSummary()"); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); - - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - assert(_shared->audio_processing() != NULL); - - return (GetEchoMetricSummaryInternal(stats)); -} - -int VoECallReportImpl::GetEchoMetricSummaryInternal(EchoStatistics& stats) -{ - // Retrieve echo metrics from the AudioProcessingModule - int ret(0); - bool mode(false); - EchoCancellation::Metrics metrics; - - // Ensure that echo metrics is enabled - - mode = - _shared->audio_processing()->echo_cancellation()->are_metrics_enabled(); - if (mode != false) - { - ret = _shared->audio_processing()->echo_cancellation()-> - GetMetrics(&metrics); - if (ret != 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceVoice, - VoEId(_shared->instance_id(), -1), - " AudioProcessingModule GetMetrics() => error"); - } - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceVoice, - VoEId(_shared->instance_id(), -1), - " AudioProcessingModule echo metrics is not enabled"); - } - - if ((ret != 0) || (mode == false)) - { - // Mark complete struct as invalid (-100 dB) - WEBRTC_TRACE(kTraceWarning, kTraceVoice, - VoEId(_shared->instance_id(), -1), - " unable to retrieve echo metrics from the AudioProcessingModule"); - stats.erl.min = -100; - stats.erl.max = -100; - stats.erl.average = -100; - stats.erle.min = -100; - stats.erle.max = -100; - stats.erle.average = -100; - stats.rerl.min = -100; - stats.rerl.max = -100; - stats.rerl.average = -100; - stats.a_nlp.min = -100; - stats.a_nlp.max = -100; - stats.a_nlp.average = -100; - } - else - { - - // Deliver output results to user - stats.erl.min = metrics.echo_return_loss.minimum; - stats.erl.max = metrics.echo_return_loss.maximum; - stats.erl.average = metrics.echo_return_loss.average; - WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, - VoEId(_shared->instance_id(), -1), " erl: min=%d, max=%d, avg=%d", - stats.erl.min, stats.erl.max, stats.erl.average); - - stats.erle.min = metrics.echo_return_loss_enhancement.minimum; - stats.erle.max = metrics.echo_return_loss_enhancement.maximum; - stats.erle.average = metrics.echo_return_loss_enhancement.average; - WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, - VoEId(_shared->instance_id(), -1), " erle: min=%d, max=%d, avg=%d", - stats.erle.min, stats.erle.max, stats.erle.average); - - stats.rerl.min = metrics.residual_echo_return_loss.minimum; - stats.rerl.max = metrics.residual_echo_return_loss.maximum; - stats.rerl.average = metrics.residual_echo_return_loss.average; - WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, - VoEId(_shared->instance_id(), -1), " rerl: min=%d, max=%d, avg=%d", - stats.rerl.min, stats.rerl.max, stats.rerl.average); - - stats.a_nlp.min = metrics.a_nlp.minimum; - stats.a_nlp.max = metrics.a_nlp.maximum; - stats.a_nlp.average = metrics.a_nlp.average; - WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, - VoEId(_shared->instance_id(), -1), - " a_nlp: min=%d, max=%d, avg=%d", - stats.a_nlp.min, stats.a_nlp.max, stats.a_nlp.average); - } - return 0; -} - -int VoECallReportImpl::GetRoundTripTimeSummary(int channel, StatVal& delaysMs) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetRoundTripTimeSummary()"); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); - - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "GetRoundTripTimeSummary() failed to locate channel"); - return -1; - } - - return channelPtr->GetRoundTripTimeSummary(delaysMs); -} - -int VoECallReportImpl::GetDeadOrAliveSummary(int channel, - int& numOfDeadDetections, - int& numOfAliveDetections) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetDeadOrAliveSummary(channel=%d)", channel); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); - - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - - return (GetDeadOrAliveSummaryInternal(channel, numOfDeadDetections, - numOfAliveDetections)); -} - -int VoECallReportImpl::GetDeadOrAliveSummaryInternal(int channel, - int& numOfDeadDetections, - int& numOfAliveDetections) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetDeadOrAliveSummary(channel=%d)", channel); - - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "GetRoundTripTimeSummary() failed to locate channel"); - return -1; - } - - return channelPtr->GetDeadOrAliveCounters(numOfDeadDetections, - numOfAliveDetections); -} - -int VoECallReportImpl::WriteReportToFile(const char* fileNameUTF8) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "WriteReportToFile(fileNameUTF8=%s)", fileNameUTF8); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); - - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - - if (NULL == fileNameUTF8) - { - _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, - "WriteReportToFile() invalid filename"); - return -1; - } - - if (_file.Open()) - { - _file.CloseFile(); - } - - // Open text file in write mode - if (_file.OpenFile(fileNameUTF8, false, false, true) != 0) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "WriteReportToFile() unable to open the file"); - return -1; - } - - // Summarize information and add it to the open file - // - _file.WriteText("WebRtc VoiceEngine Call Report\n"); - _file.WriteText("==============================\n"); - _file.WriteText("\nNetwork Packet Round Trip Time (RTT)\n"); - _file.WriteText("------------------------------------\n\n"); - - if (_shared->channel_manager().NumOfChannels() == 0) - return 0; - - for (voe::ChannelManager::Iterator it(&_shared->channel_manager()); - it.IsValid(); - it.Increment()) { - StatVal delaysMs; - _file.WriteText("channel %d:\n", it.GetChannel()->ChannelId()); - it.GetChannel()->GetRoundTripTimeSummary(delaysMs); - _file.WriteText(" min:%5d [ms]\n", delaysMs.min); - _file.WriteText(" max:%5d [ms]\n", delaysMs.max); - _file.WriteText(" avg:%5d [ms]\n", delaysMs.average); - } - - _file.WriteText("\nDead-or-Alive Connection Detections\n"); - _file.WriteText("------------------------------------\n\n"); - - for (voe::ChannelManager::Iterator it(&_shared->channel_manager()); - it.IsValid(); - it.Increment()) { - int dead = 0; - int alive = 0; - _file.WriteText("channel %d:\n", it.GetChannel()->ChannelId()); - GetDeadOrAliveSummary(it.GetChannel()->ChannelId(), dead, alive); - _file.WriteText(" #dead :%6d\n", dead); - _file.WriteText(" #alive:%6d\n", alive); - } - - EchoStatistics echo; - GetEchoMetricSummary(echo); - - _file.WriteText("\nEcho Metrics\n"); - _file.WriteText("------------\n\n"); - - _file.WriteText("erl:\n"); - _file.WriteText(" min:%5d [dB]\n", echo.erl.min); - _file.WriteText(" max:%5d [dB]\n", echo.erl.max); - _file.WriteText(" avg:%5d [dB]\n", echo.erl.average); - _file.WriteText("\nerle:\n"); - _file.WriteText(" min:%5d [dB]\n", echo.erle.min); - _file.WriteText(" max:%5d [dB]\n", echo.erle.max); - _file.WriteText(" avg:%5d [dB]\n", echo.erle.average); - _file.WriteText("rerl:\n"); - _file.WriteText(" min:%5d [dB]\n", echo.rerl.min); - _file.WriteText(" max:%5d [dB]\n", echo.rerl.max); - _file.WriteText(" avg:%5d [dB]\n", echo.rerl.average); - _file.WriteText("a_nlp:\n"); - _file.WriteText(" min:%5d [dB]\n", echo.a_nlp.min); - _file.WriteText(" max:%5d [dB]\n", echo.a_nlp.max); - _file.WriteText(" avg:%5d [dB]\n", echo.a_nlp.average); - - _file.WriteText("\n"); - - _file.Flush(); - _file.CloseFile(); - - return 0; -} - -#endif // WEBRTC_VOICE_ENGINE_CALL_REPORT_API - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_call_report_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_call_report_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_call_report_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_call_report_impl.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_VOICE_ENGINE_VOE_CALL_REPORT_IMPL_H -#define WEBRTC_VOICE_ENGINE_VOE_CALL_REPORT_IMPL_H - -#include "webrtc/voice_engine/include/voe_call_report.h" - -#include "webrtc/voice_engine/shared_data.h" - - -namespace webrtc -{ -class FileWrapper; - -class VoECallReportImpl: public VoECallReport -{ -public: - virtual int ResetCallReportStatistics(int channel); - - virtual int GetEchoMetricSummary(EchoStatistics& stats); - - virtual int GetRoundTripTimeSummary(int channel, - StatVal& delaysMs); - - virtual int GetDeadOrAliveSummary(int channel, int& numOfDeadDetections, - int& numOfAliveDetections); - - virtual int WriteReportToFile(const char* fileNameUTF8); - -protected: - VoECallReportImpl(voe::SharedData* shared); - virtual ~VoECallReportImpl(); - -private: - int GetDeadOrAliveSummaryInternal(int channel, - int& numOfDeadDetections, - int& numOfAliveDetections); - - int GetEchoMetricSummaryInternal(EchoStatistics& stats); - - int GetSpeechAndNoiseSummaryInternal(LevelStatistics& stats); - - FileWrapper& _file; - voe::SharedData* _shared; -}; - -} // namespace webrtc - -#endif // WEBRTC_VOICE_ENGINE_VOE_CALL_REPORT_IMPL_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_codec_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_codec_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_codec_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_codec_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -213,114 +213,6 @@ return 0; } -int VoECodecImpl::SetAMREncFormat(int channel, AmrMode mode) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "SetAMREncFormat(channel=%d, mode=%d)", channel, mode); -#ifdef WEBRTC_CODEC_AMR - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "SetAMREncFormat() failed to locate channel"); - return -1; - } - return channelPtr->SetAMREncFormat(mode); -#else - _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, - "SetAMREncFormat() AMR codec is not supported"); - return -1; -#endif -} - -int VoECodecImpl::SetAMRDecFormat(int channel, AmrMode mode) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "SetAMRDecFormat(channel=%i, mode=%i)", channel, mode); -#ifdef WEBRTC_CODEC_AMR - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "SetAMRDecFormat() failed to locate channel"); - return -1; - } - return channelPtr->SetAMRDecFormat(mode); -#else - _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, - "SetAMRDecFormat() AMR codec is not supported"); - return -1; -#endif -} - -int VoECodecImpl::SetAMRWbEncFormat(int channel, AmrMode mode) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "SetAMRWbEncFormat(channel=%d, mode=%d)", channel, mode); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); -#ifdef WEBRTC_CODEC_AMRWB - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "SetAMRWbEncFormat() failed to locate channel"); - return -1; - } - return channelPtr->SetAMRWbEncFormat(mode); -#else - _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, - "SetAMRWbEncFormat() AMR-wb codec is not supported"); - return -1; -#endif -} - -int VoECodecImpl::SetAMRWbDecFormat(int channel, AmrMode mode) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "SetAMRWbDecFormat(channel=%i, mode=%i)", channel, mode); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); -#ifdef WEBRTC_CODEC_AMRWB - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "SetAMRWbDecFormat() failed to locate channel"); - return -1; - } - return channelPtr->SetAMRWbDecFormat(mode); -#else - _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, - "SetAMRWbDecFormat() AMR-wb codec is not supported"); - return -1; -#endif -} - int VoECodecImpl::SetRecPayloadType(int channel, const CodecInst& codec) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), @@ -403,92 +295,39 @@ return channelPtr->SetSendCNPayloadType(type, frequency); } -int VoECodecImpl::SetISACInitTargetRate(int channel, int rateBps, - bool useFixedFrameSize) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "SetISACInitTargetRate(channel=%d, rateBps=%d, " - "useFixedFrameSize=%d)", channel, rateBps, useFixedFrameSize); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); -#ifdef WEBRTC_CODEC_ISAC - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "SetISACInitTargetRate() failed to locate channel"); - return -1; - } - return channelPtr->SetISACInitTargetRate(rateBps, useFixedFrameSize); -#else - _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, - "SetISACInitTargetRate() iSAC codec is not supported"); - return -1; -#endif -} - -int VoECodecImpl::SetISACMaxRate(int channel, int rateBps) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "SetISACMaxRate(channel=%d, rateBps=%d)", channel, rateBps); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); -#ifdef WEBRTC_CODEC_ISAC - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "SetISACMaxRate() failed to locate channel"); - return -1; - } - return channelPtr->SetISACMaxRate(rateBps); -#else - _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, - "SetISACMaxRate() iSAC codec is not supported"); - return -1; -#endif -} - -int VoECodecImpl::SetISACMaxPayloadSize(int channel, int sizeBytes) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "SetISACMaxPayloadSize(channel=%d, sizeBytes=%d)", channel, - sizeBytes); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); -#ifdef WEBRTC_CODEC_ISAC - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "SetISACMaxPayloadSize() failed to locate channel"); - return -1; - } - return channelPtr->SetISACMaxPayloadSize(sizeBytes); -#else - _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, - "SetISACMaxPayloadSize() iSAC codec is not supported"); - return -1; -#endif - return 0; +int VoECodecImpl::SetFECStatus(int channel, bool enable) { + WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), + "SetCodecFECStatus(channel=%d, enable=%d)", channel, enable); + if (!_shared->statistics().Initialized()) { + _shared->SetLastError(VE_NOT_INITED, kTraceError); + return -1; + } + voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); + voe::Channel* channelPtr = ch.channel(); + if (channelPtr == NULL) { + _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, + "SetCodecFECStatus() failed to locate channel"); + return -1; + } + return channelPtr->SetCodecFECStatus(enable); +} + +int VoECodecImpl::GetFECStatus(int channel, bool& enabled) { + WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), + "GetCodecFECStatus(channel=%d)", channel); + if (!_shared->statistics().Initialized()) { + _shared->SetLastError(VE_NOT_INITED, kTraceError); + return -1; + } + voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); + voe::Channel* channelPtr = ch.channel(); + if (channelPtr == NULL) { + _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, + "GetFECStatus() failed to locate channel"); + return -1; + } + enabled = channelPtr->GetCodecFECStatus(); + return 0; } int VoECodecImpl::SetVADStatus(int channel, bool enable, VadModes mode, @@ -579,6 +418,24 @@ return 0; } +int VoECodecImpl::SetOpusMaxPlaybackRate(int channel, int frequency_hz) { + WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), + "SetOpusMaxPlaybackRate(channel=%d, frequency_hz=%d)", channel, + frequency_hz); + if (!_shared->statistics().Initialized()) { + _shared->SetLastError(VE_NOT_INITED, kTraceError); + return -1; + } + voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); + voe::Channel* channelPtr = ch.channel(); + if (channelPtr == NULL) { + _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, + "SetOpusMaxPlaybackRate failed to locate channel"); + return -1; + } + return channelPtr->SetOpusMaxPlaybackRate(frequency_hz); +} + void VoECodecImpl::ACMToExternalCodecRepresentation(CodecInst& toInst, const CodecInst& fromInst) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_codec_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_codec_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_codec_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_codec_impl.h 2015-02-03 14:33:38.000000000 +0000 @@ -31,18 +31,6 @@ virtual int GetRecCodec(int channel, CodecInst& codec); - virtual int SetAMREncFormat(int channel, - AmrMode mode = kRfc3267BwEfficient); - - virtual int SetAMRDecFormat(int channel, - AmrMode mode = kRfc3267BwEfficient); - - virtual int SetAMRWbEncFormat(int channel, - AmrMode mode = kRfc3267BwEfficient); - - virtual int SetAMRWbDecFormat(int channel, - AmrMode mode = kRfc3267BwEfficient); - virtual int SetSendCNPayloadType( int channel, int type, PayloadFrequencies frequency = kFreq16000Hz); @@ -52,13 +40,9 @@ virtual int GetRecPayloadType(int channel, CodecInst& codec); - virtual int SetISACInitTargetRate(int channel, - int rateBps, - bool useFixedFrameSize = false); - - virtual int SetISACMaxRate(int channel, int rateBps); + virtual int SetFECStatus(int channel, bool enable); - virtual int SetISACMaxPayloadSize(int channel, int sizeBytes); + virtual int GetFECStatus(int channel, bool& enabled); virtual int SetVADStatus(int channel, bool enable, @@ -70,6 +54,8 @@ VadModes& mode, bool& disabledDTX); + virtual int SetOpusMaxPlaybackRate(int channel, int frequency_hz); + // Dual-streaming virtual int SetSecondarySendCodec(int channel, const CodecInst& codec, int red_payload_type); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_codec_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_codec_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_codec_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_codec_unittest.cc 2015-02-03 14:33:38.000000000 +0000 @@ -169,6 +169,56 @@ EXPECT_EQ(-1, voe_codec_->GetSecondarySendCodec(channel_, my_codec)); } +TEST(VoECodecInst, TestCompareCodecInstances) { + CodecInst codec1, codec2; + memset(&codec1, 0, sizeof(CodecInst)); + memset(&codec2, 0, sizeof(CodecInst)); + + codec1.pltype = 101; + strncpy(codec1.plname, "isac", 4); + codec1.plfreq = 8000; + codec1.pacsize = 110; + codec1.channels = 1; + codec1.rate = 8000; + memcpy(&codec2, &codec1, sizeof(CodecInst)); + // Compare two codecs now. + EXPECT_TRUE(codec1 == codec2); + EXPECT_FALSE(codec1 != codec2); + + // Changing pltype. + codec2.pltype = 102; + EXPECT_FALSE(codec1 == codec2); + EXPECT_TRUE(codec1 != codec2); + + // Reset to codec2 to codec1 state. + memcpy(&codec2, &codec1, sizeof(CodecInst)); + // payload name should be case insensitive. + strncpy(codec2.plname, "ISAC", 4); + EXPECT_TRUE(codec1 == codec2); + + // Test modifying the |plfreq| + codec2.plfreq = 16000; + EXPECT_FALSE(codec1 == codec2); + + // Reset to codec2 to codec1 state. + memcpy(&codec2, &codec1, sizeof(CodecInst)); + // Test modifying the |pacsize|. + codec2.pacsize = 440; + EXPECT_FALSE(codec1 == codec2); + + // Reset to codec2 to codec1 state. + memcpy(&codec2, &codec1, sizeof(CodecInst)); + // Test modifying the |channels|. + codec2.channels = 2; + EXPECT_FALSE(codec1 == codec2); + + // Reset to codec2 to codec1 state. + memcpy(&codec2, &codec1, sizeof(CodecInst)); + // Test modifying the |rate|. + codec2.rate = 0; + EXPECT_FALSE(codec1 == codec2); +} + } // namespace } // namespace voe } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_dtmf_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_dtmf_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_dtmf_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_dtmf_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -227,50 +227,6 @@ attenuationDb); } -int VoEDtmfImpl::StartPlayingDtmfTone(int eventCode, - int attenuationDb) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "StartPlayingDtmfTone(eventCode=%d, attenuationDb=%d)", - eventCode, attenuationDb); - - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - if (!_shared->audio_device()->Playing()) - { - _shared->SetLastError(VE_NOT_PLAYING, kTraceError, - "StartPlayingDtmfTone() no channel is playing out"); - return -1; - } - if ((eventCode < kMinDtmfEventCode) || - (eventCode > kMaxDtmfEventCode) || - (attenuationDb < kMinTelephoneEventAttenuation) || - (attenuationDb > kMaxTelephoneEventAttenuation)) - { - _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, - "StartPlayingDtmfTone() invalid tone parameter(s)"); - return -1; - } - return _shared->output_mixer()->StartPlayingDtmfTone(eventCode, - attenuationDb); -} - -int VoEDtmfImpl::StopPlayingDtmfTone() -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "StopPlayingDtmfTone()"); - - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - return _shared->output_mixer()->StopPlayingDtmfTone(); -} - int VoEDtmfImpl::SetDtmfFeedbackStatus(bool enable, bool directFeedback) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), @@ -301,55 +257,6 @@ enabled, directFeedback); return 0; } - -int VoEDtmfImpl::SetDtmfPlayoutStatus(int channel, bool enable) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "SetDtmfPlayoutStatus(channel=%d, enable=%d)", - channel, enable); - IPHONE_NOT_SUPPORTED(_shared->statistics()); - - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "SetDtmfPlayoutStatus() failed to locate channel"); - return -1; - } - return channelPtr->SetDtmfPlayoutStatus(enable); -} - -int VoEDtmfImpl::GetDtmfPlayoutStatus(int channel, bool& enabled) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetDtmfPlayoutStatus(channel=%d, enabled=?)", channel); - IPHONE_NOT_SUPPORTED(_shared->statistics()); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "GetDtmfPlayoutStatus() failed to locate channel"); - return -1; - } - enabled = channelPtr->DtmfPlayoutStatus(); - WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, - VoEId(_shared->instance_id(), -1), - "GetDtmfPlayoutStatus() => enabled=%d", enabled); - return 0; -} - #endif // #ifdef WEBRTC_VOICE_ENGINE_DTMF_API } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_dtmf_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_dtmf_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_dtmf_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_dtmf_impl.h 2015-02-03 14:33:38.000000000 +0000 @@ -42,15 +42,6 @@ int lengthMs = 200, int attenuationDb = 10); - virtual int StartPlayingDtmfTone(int eventCode, - int attenuationDb = 10); - - virtual int StopPlayingDtmfTone(); - - virtual int SetDtmfPlayoutStatus(int channel, bool enable); - - virtual int GetDtmfPlayoutStatus(int channel, bool& enabled); - protected: VoEDtmfImpl(voe::SharedData* shared); virtual ~VoEDtmfImpl(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_encryption_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_encryption_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_encryption_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_encryption_impl.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/voice_engine/voe_encryption_impl.h" - - -#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/trace.h" -#include "webrtc/voice_engine/channel.h" -#include "webrtc/voice_engine/include/voe_errors.h" -#include "webrtc/voice_engine/voice_engine_impl.h" - -namespace webrtc { - -VoEEncryption* VoEEncryption::GetInterface(VoiceEngine* voiceEngine) -{ -#ifndef WEBRTC_VOICE_ENGINE_ENCRYPTION_API - return NULL; -#else - if (NULL == voiceEngine) - { - return NULL; - } - VoiceEngineImpl* s = static_cast(voiceEngine); - s->AddRef(); - return s; -#endif -} - -#ifdef WEBRTC_VOICE_ENGINE_ENCRYPTION_API - -VoEEncryptionImpl::VoEEncryptionImpl(voe::SharedData* shared) : _shared(shared) -{ - WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1), - "VoEEncryptionImpl::VoEEncryptionImpl() - ctor"); -} - -VoEEncryptionImpl::~VoEEncryptionImpl() -{ - WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1), - "VoEEncryptionImpl::~VoEEncryptionImpl() - dtor"); -} - -int VoEEncryptionImpl::RegisterExternalEncryption(int channel, - Encryption& encryption) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "RegisterExternalEncryption(channel=%d, encryption=0x%x)", - channel, &encryption); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "RegisterExternalEncryption() failed to locate channel"); - return -1; - } - return channelPtr->RegisterExternalEncryption(encryption); -} - -int VoEEncryptionImpl::DeRegisterExternalEncryption(int channel) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "DeRegisterExternalEncryption(channel=%d)", channel); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "DeRegisterExternalEncryption() failed to locate channel"); - return -1; - } - return channelPtr->DeRegisterExternalEncryption(); -} - -#endif // #ifdef WEBRTC_VOICE_ENGINE_ENCRYPTION_API - -// EOF -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_encryption_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_encryption_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_encryption_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_encryption_impl.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_VOICE_ENGINE_VOE_ENCRYPTION_IMPL_H -#define WEBRTC_VOICE_ENGINE_VOE_ENCRYPTION_IMPL_H - -#include "webrtc/voice_engine/include/voe_encryption.h" - -#include "webrtc/voice_engine/shared_data.h" - -namespace webrtc { - -class VoEEncryptionImpl : public VoEEncryption -{ -public: - // External encryption - virtual int RegisterExternalEncryption( - int channel, - Encryption& encryption); - - virtual int DeRegisterExternalEncryption(int channel); - -protected: - VoEEncryptionImpl(voe::SharedData* shared); - virtual ~VoEEncryptionImpl(); - -private: - voe::SharedData* _shared; -}; - -} // namespace webrtc - -#endif // #ifndef WEBRTC_VOICE_ENGINE_VOE_ENCRYPTION_IMPL_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_external_media_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_external_media_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_external_media_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_external_media_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -382,7 +382,7 @@ // Retrieve mixed output at the specified rate shared_->output_mixer()->MixActiveChannels(); - shared_->output_mixer()->DoOperationsOnCombinedSignal(); + shared_->output_mixer()->DoOperationsOnCombinedSignal(true); shared_->output_mixer()->GetMixedAudio(samplingFreqHz, 1, &audioFrame); // Deliver audio (PCM) samples to the external sink diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_file_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_file_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_file_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_file_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -164,27 +164,6 @@ return channelPtr->IsPlayingFileLocally(); } -int VoEFileImpl::ScaleLocalFilePlayout(int channel, float scale) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "ScaleLocalFilePlayout(channel=%d, scale=%5.3f)", - channel, scale); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "StopPlayingFileLocally() failed to locate channel"); - return -1; - } - return channelPtr->ScaleLocalFilePlayout(scale); -} - int VoEFileImpl::StartPlayingFileAsMicrophone(int channel, const char fileNameUTF8[1024], bool loop, @@ -395,36 +374,6 @@ } } -int VoEFileImpl::ScaleFileAsMicrophonePlayout(int channel, float scale) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "ScaleFileAsMicrophonePlayout(channel=%d, scale=%5.3f)", - channel, scale); - - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - if (channel == -1) - { - return _shared->transmit_mixer()->ScaleFileAsMicrophonePlayout(scale); - } - else - { - // Stop adding file after demultiplexing <=> affects one channel only - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "IsPlayingFileAsMicrophone() failed to locate channel"); - return -1; - } - return channelPtr->ScaleFileAsMicrophonePlayout(scale); - } -} - int VoEFileImpl::StartRecordingPlayout( int channel, const char* fileNameUTF8, CodecInst* compression, int maxSizeBytes) @@ -642,717 +591,6 @@ return err; } -// TODO(andrew): a cursory inspection suggests there's a large amount of -// overlap in these convert functions which could be refactored to a helper. -int VoEFileImpl::ConvertPCMToWAV(const char* fileNameInUTF8, - const char* fileNameOutUTF8) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "ConvertPCMToWAV(fileNameInUTF8=%s, fileNameOutUTF8=%s)", - fileNameInUTF8, fileNameOutUTF8); - - // Create file player object - FilePlayer& playerObj(*FilePlayer::CreateFilePlayer( - -1, - kFileFormatPcm16kHzFile)); - - int res=playerObj.StartPlayingFile(fileNameInUTF8,false,0,1.0,0,0, NULL); - if (res) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "ConvertPCMToWAV failed to create player object"); - playerObj.StopPlayingFile(); - FilePlayer::DestroyFilePlayer(&playerObj); - return -1; - } - - // Create file recorder object - FileRecorder& recObj(*FileRecorder::CreateFileRecorder( - -1, kFileFormatWavFile)); - - CodecInst codecInst; - strncpy(codecInst.plname,"L16",32); - codecInst.channels = 1; - codecInst.rate = 256000; - codecInst.plfreq = 16000; - codecInst.pltype = 94; - codecInst.pacsize = 160; - - res = recObj.StartRecordingAudioFile(fileNameOutUTF8,codecInst,0); - if (res) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "ConvertPCMToWAV failed to create recorder object"); - playerObj.StopPlayingFile(); - FilePlayer::DestroyFilePlayer(&playerObj); - recObj.StopRecording(); - FileRecorder::DestroyFileRecorder(&recObj); - return -1; - } - - // Run throught the file - AudioFrame audioFrame; - int16_t decodedData[160]; - int decLength=0; - const uint32_t frequency = 16000; - - while(!playerObj.Get10msAudioFromFile(decodedData,decLength,frequency)) - { - if(decLength!=frequency/100) - { - // This is an OK way to end - break; - } - - audioFrame.UpdateFrame(-1, 0, decodedData, - (uint16_t)decLength, - frequency, AudioFrame::kNormalSpeech, - AudioFrame::kVadActive); - - res=recObj.RecordAudioToFile(audioFrame); - if(res) - { - WEBRTC_TRACE(kTraceError, kTraceVoice, - VoEId(_shared->instance_id(), -1), - "ConvertPCMToWAV failed during conversion (write frame)"); - } - } - - playerObj.StopPlayingFile(); - recObj.StopRecording(); - FilePlayer::DestroyFilePlayer(&playerObj); - FileRecorder::DestroyFileRecorder(&recObj); - - return res; -} - -int VoEFileImpl::ConvertPCMToWAV(InStream* streamIn, OutStream* streamOut) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "ConvertPCMToWAV(streamIn, streamOut)"); - - if ((streamIn == NULL) || (streamOut == NULL)) - { - WEBRTC_TRACE(kTraceError, kTraceVoice, - VoEId(_shared->instance_id(), -1), "invalid stream handles"); - return (-1); - } - - // Create file player object - FilePlayer& playerObj(*FilePlayer::CreateFilePlayer(-1, - kFileFormatPcm16kHzFile)); - int res = playerObj.StartPlayingFile(*streamIn,0,1.0,0,0,NULL); - if (res) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "ConvertPCMToWAV failed to create player object"); - playerObj.StopPlayingFile(); - FilePlayer::DestroyFilePlayer(&playerObj); - return -1; - } - - // Create file recorder object - FileRecorder& recObj(*FileRecorder::CreateFileRecorder(-1, - kFileFormatWavFile)); - CodecInst codecInst; - strncpy(codecInst.plname, "L16", 32); - codecInst.channels = 1; - codecInst.rate = 256000; - codecInst.plfreq = 16000; - codecInst.pltype = 94; - codecInst.pacsize = 160; - res = recObj.StartRecordingAudioFile(*streamOut,codecInst,0); - if (res) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "ConvertPCMToWAV failed to create recorder object"); - playerObj.StopPlayingFile(); - FilePlayer::DestroyFilePlayer(&playerObj); - recObj.StopRecording(); - FileRecorder::DestroyFileRecorder(&recObj); - return -1; - } - - // Run throught the file - AudioFrame audioFrame; - int16_t decodedData[160]; - int decLength=0; - const uint32_t frequency = 16000; - - while(!playerObj.Get10msAudioFromFile(decodedData,decLength,frequency)) - { - if(decLength!=frequency/100) - { - // This is an OK way to end - break; - } - - audioFrame.UpdateFrame(-1, 0, decodedData, - (uint16_t)decLength, frequency, - AudioFrame::kNormalSpeech, - AudioFrame::kVadActive); - - res=recObj.RecordAudioToFile(audioFrame); - if(res) - { - WEBRTC_TRACE(kTraceError, kTraceVoice, - VoEId(_shared->instance_id(), -1), - "ConvertPCMToWAV failed during conversion (write frame)"); - } - } - - playerObj.StopPlayingFile(); - recObj.StopRecording(); - FilePlayer::DestroyFilePlayer(&playerObj); - FileRecorder::DestroyFileRecorder(&recObj); - - return res; -} - -int VoEFileImpl::ConvertWAVToPCM(const char* fileNameInUTF8, - const char* fileNameOutUTF8) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "ConvertWAVToPCM(fileNameInUTF8=%s, fileNameOutUTF8=%s)", - fileNameInUTF8, fileNameOutUTF8); - - // Create file player object - FilePlayer& playerObj(*FilePlayer::CreateFilePlayer(-1, - kFileFormatWavFile)); - int res = playerObj.StartPlayingFile(fileNameInUTF8,false,0,1.0,0,0,NULL); - if (res) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "ConvertWAVToPCM failed to create player object"); - playerObj.StopPlayingFile(); - FilePlayer::DestroyFilePlayer(&playerObj); - return -1; - } - - // Create file recorder object - FileRecorder& recObj(*FileRecorder::CreateFileRecorder( - -1, kFileFormatPcm16kHzFile)); - - CodecInst codecInst; - strncpy(codecInst.plname,"L16",32); - codecInst.channels = 1; - codecInst.rate = 256000; - codecInst.plfreq = 16000; - codecInst.pltype = 94; - codecInst.pacsize = 160; - - res = recObj.StartRecordingAudioFile(fileNameOutUTF8,codecInst,0); - if (res) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "ConvertWAVToPCM failed to create recorder object"); - playerObj.StopPlayingFile(); - FilePlayer::DestroyFilePlayer(&playerObj); - recObj.StopRecording(); - FileRecorder::DestroyFileRecorder(&recObj); - return -1; - } - - // Run throught the file - AudioFrame audioFrame; - int16_t decodedData[160]; - int decLength=0; - const uint32_t frequency = 16000; - - while(!playerObj.Get10msAudioFromFile(decodedData,decLength,frequency)) - { - if(decLength!=frequency/100) - { - // This is an OK way to end - break; - } - - audioFrame.UpdateFrame(-1, 0, decodedData, - (uint16_t)decLength, - frequency, AudioFrame::kNormalSpeech, - AudioFrame::kVadActive); - - res=recObj.RecordAudioToFile(audioFrame); - if(res) - { - WEBRTC_TRACE(kTraceError, kTraceVoice, - VoEId(_shared->instance_id(), -1), - "ConvertWAVToPCM failed during conversion (write frame)"); - } - } - - playerObj.StopPlayingFile(); - recObj.StopRecording(); - FilePlayer::DestroyFilePlayer(&playerObj); - FileRecorder::DestroyFileRecorder(&recObj); - - return res; -} - -int VoEFileImpl::ConvertWAVToPCM(InStream* streamIn, OutStream* streamOut) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "ConvertWAVToPCM(streamIn, streamOut)"); - - if ((streamIn == NULL) || (streamOut == NULL)) - { - WEBRTC_TRACE(kTraceError, kTraceVoice, - VoEId(_shared->instance_id(), -1), "invalid stream handles"); - return (-1); - } - - // Create file player object - FilePlayer& playerObj(*FilePlayer::CreateFilePlayer(-1, - kFileFormatWavFile)); - int res = playerObj.StartPlayingFile(*streamIn,0,1.0,0,0,NULL); - if (res) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "ConvertWAVToPCM failed to create player object"); - playerObj.StopPlayingFile(); - FilePlayer::DestroyFilePlayer(&playerObj); - return -1; - } - - // Create file recorder object - FileRecorder& recObj(*FileRecorder::CreateFileRecorder( - -1, kFileFormatPcm16kHzFile)); - - CodecInst codecInst; - strncpy(codecInst.plname,"L16",32); - codecInst.channels = 1; - codecInst.rate = 256000; - codecInst.plfreq = 16000; - codecInst.pltype = 94; - codecInst.pacsize = 160; - - res = recObj.StartRecordingAudioFile(*streamOut,codecInst,0); - if (res) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "ConvertWAVToPCM failed to create recorder object"); - playerObj.StopPlayingFile(); - FilePlayer::DestroyFilePlayer(&playerObj); - recObj.StopRecording(); - FileRecorder::DestroyFileRecorder(&recObj); - return -1; - } - - // Run throught the file - AudioFrame audioFrame; - int16_t decodedData[160]; - int decLength=0; - const uint32_t frequency = 16000; - - while(!playerObj.Get10msAudioFromFile(decodedData,decLength,frequency)) - { - if(decLength!=frequency/100) - { - // This is an OK way to end - break; - } - - audioFrame.UpdateFrame(-1, 0, decodedData, - (uint16_t)decLength, frequency, - AudioFrame::kNormalSpeech, - AudioFrame::kVadActive); - - res=recObj.RecordAudioToFile(audioFrame); - if(res) - { - WEBRTC_TRACE(kTraceError, kTraceVoice, - VoEId(_shared->instance_id(), -1), - "ConvertWAVToPCM failed during conversion (write frame)"); - } - } - - playerObj.StopPlayingFile(); - recObj.StopRecording(); - FilePlayer::DestroyFilePlayer(&playerObj); - FileRecorder::DestroyFileRecorder(&recObj); - - return res; -} - -int VoEFileImpl::ConvertPCMToCompressed(const char* fileNameInUTF8, - const char* fileNameOutUTF8, - CodecInst* compression) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "ConvertPCMToCompressed(fileNameInUTF8=%s, fileNameOutUTF8=%s" - ", compression)", fileNameInUTF8, fileNameOutUTF8); - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1), - " compression: plname=%s, plfreq=%d, pacsize=%d", - compression->plname, compression->plfreq, - compression->pacsize); - - // Create file player object - FilePlayer& playerObj(*FilePlayer::CreateFilePlayer( - -1, - kFileFormatPcm16kHzFile)); - int res = playerObj.StartPlayingFile(fileNameInUTF8,false,0,1.0,0,0, NULL); - if (res) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "ConvertPCMToCompressed failed to create player object"); - // Clean up and shutdown the file player - playerObj.StopPlayingFile(); - FilePlayer::DestroyFilePlayer(&playerObj); - return -1; - } - - // Create file recorder object - FileRecorder& recObj(*FileRecorder::CreateFileRecorder( - -1, - kFileFormatCompressedFile)); - res = recObj.StartRecordingAudioFile(fileNameOutUTF8, *compression,0); - if (res) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "ConvertPCMToCompressed failed to create recorder object"); - playerObj.StopPlayingFile(); - FilePlayer::DestroyFilePlayer(&playerObj); - recObj.StopRecording(); - FileRecorder::DestroyFileRecorder(&recObj); - return -1; - } - - // Run throught the file - AudioFrame audioFrame; - int16_t decodedData[160]; - int decLength=0; - const uint32_t frequency = 16000; - - while(!playerObj.Get10msAudioFromFile(decodedData,decLength,frequency)) - { - if(decLength!=frequency/100) - { - // This is an OK way to end - break; - } - audioFrame.UpdateFrame(-1, 0, decodedData, - (uint16_t)decLength, - frequency, AudioFrame::kNormalSpeech, - AudioFrame::kVadActive); - - res=recObj.RecordAudioToFile(audioFrame); - if(res) - { - WEBRTC_TRACE(kTraceError, kTraceVoice, - VoEId(_shared->instance_id(), -1), - "ConvertPCMToCompressed failed during conversion " - "(write frame)"); - } - } - - playerObj.StopPlayingFile(); - recObj.StopRecording(); - FilePlayer::DestroyFilePlayer(&playerObj); - FileRecorder::DestroyFileRecorder(&recObj); - - return res; -} - -int VoEFileImpl::ConvertPCMToCompressed(InStream* streamIn, - OutStream* streamOut, - CodecInst* compression) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "ConvertPCMToCompressed(streamIn, streamOut, compression)"); - - if ((streamIn == NULL) || (streamOut == NULL)) - { - WEBRTC_TRACE(kTraceError, kTraceVoice, - VoEId(_shared->instance_id(), -1), "invalid stream handles"); - return (-1); - } - - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1), - " compression: plname=%s, plfreq=%d, pacsize=%d", - compression->plname, compression->plfreq, - compression->pacsize); - - // Create file player object - FilePlayer& playerObj(*FilePlayer::CreateFilePlayer( - -1, kFileFormatPcm16kHzFile)); - - int res = playerObj.StartPlayingFile(*streamIn,0,1.0,0,0,NULL); - if (res) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "ConvertPCMToCompressed failed to create player object"); - playerObj.StopPlayingFile(); - FilePlayer::DestroyFilePlayer(&playerObj); - return -1; - } - - // Create file recorder object - FileRecorder& recObj(*FileRecorder::CreateFileRecorder( - -1, kFileFormatCompressedFile)); - res = recObj.StartRecordingAudioFile(*streamOut,*compression,0); - if (res) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "ConvertPCMToCompressed failed to create recorder object"); - playerObj.StopPlayingFile(); - FilePlayer::DestroyFilePlayer(&playerObj); - recObj.StopRecording(); - FileRecorder::DestroyFileRecorder(&recObj); - return -1; - } - - // Run throught the file - AudioFrame audioFrame; - int16_t decodedData[160]; - int decLength=0; - const uint32_t frequency = 16000; - - while(!playerObj.Get10msAudioFromFile(decodedData,decLength,frequency)) - { - if(decLength!=frequency/100) - { - // This is an OK way to end - break; - } - audioFrame.UpdateFrame(-1, 0, decodedData, - (uint16_t)decLength, - frequency, AudioFrame::kNormalSpeech, - AudioFrame::kVadActive); - - res=recObj.RecordAudioToFile(audioFrame); - if(res) - { - WEBRTC_TRACE(kTraceError, kTraceVoice, - VoEId(_shared->instance_id(), -1), - "ConvertPCMToCompressed failed during conversion " - "(write frame)"); - } - } - - playerObj.StopPlayingFile(); - recObj.StopRecording(); - FilePlayer::DestroyFilePlayer(&playerObj); - FileRecorder::DestroyFileRecorder(&recObj); - - return res; -} - -int VoEFileImpl::ConvertCompressedToPCM(const char* fileNameInUTF8, - const char* fileNameOutUTF8) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "ConvertCompressedToPCM(fileNameInUTF8=%s," - " fileNameOutUTF8=%s)", - fileNameInUTF8, fileNameOutUTF8); - - // Create file player object - FilePlayer& playerObj(*FilePlayer::CreateFilePlayer( - -1, kFileFormatCompressedFile)); - - int res = playerObj.StartPlayingFile(fileNameInUTF8,false,0,1.0,0,0,NULL); - if (res) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "ConvertCompressedToPCM failed to create player object"); - playerObj.StopPlayingFile(); - FilePlayer::DestroyFilePlayer(&playerObj); - return -1; - } - - // Create file recorder object - FileRecorder& recObj(*FileRecorder::CreateFileRecorder( - -1, kFileFormatPcm16kHzFile)); - - CodecInst codecInst; - strncpy(codecInst.plname,"L16",32); - codecInst.channels = 1; - codecInst.rate = 256000; - codecInst.plfreq = 16000; - codecInst.pltype = 94; - codecInst.pacsize = 160; - - res = recObj.StartRecordingAudioFile(fileNameOutUTF8,codecInst,0); - if (res) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "ConvertCompressedToPCM failed to create recorder object"); - playerObj.StopPlayingFile(); - FilePlayer::DestroyFilePlayer(&playerObj); - recObj.StopRecording(); - FileRecorder::DestroyFileRecorder(&recObj); - return -1; - } - - // Run throught the file - AudioFrame audioFrame; - int16_t decodedData[160]; - int decLength=0; - const uint32_t frequency = 16000; - - while(!playerObj.Get10msAudioFromFile(decodedData,decLength,frequency)) - { - if(decLength!=frequency/100) - { - // This is an OK way to end - break; - } - audioFrame.UpdateFrame(-1, 0, decodedData, - (uint16_t)decLength, - frequency, - AudioFrame::kNormalSpeech, - AudioFrame::kVadActive); - - res=recObj.RecordAudioToFile(audioFrame); - if(res) - { - WEBRTC_TRACE(kTraceError, kTraceVoice, - VoEId(_shared->instance_id(), -1), - "ConvertCompressedToPCM failed during conversion " - "(write frame)"); - } - } - - playerObj.StopPlayingFile(); - recObj.StopRecording(); - FilePlayer::DestroyFilePlayer(&playerObj); - FileRecorder::DestroyFileRecorder(&recObj); - - return res; -} - -int VoEFileImpl::ConvertCompressedToPCM(InStream* streamIn, - OutStream* streamOut) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "ConvertCompressedToPCM(file, file);"); - - if ((streamIn == NULL) || (streamOut == NULL)) - { - WEBRTC_TRACE(kTraceError, kTraceVoice, - VoEId(_shared->instance_id(), -1), "invalid stream handles"); - return (-1); - } - - // Create file player object - FilePlayer& playerObj(*FilePlayer::CreateFilePlayer( - -1, kFileFormatCompressedFile)); - int res; - - res = playerObj.StartPlayingFile(*streamIn,0,1.0,0,0,NULL); - if (res) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "ConvertCompressedToPCM failed to create player object"); - playerObj.StopPlayingFile(); - FilePlayer::DestroyFilePlayer(&playerObj); - return -1; - } - - // Create file recorder object - FileRecorder& recObj(*FileRecorder::CreateFileRecorder( - -1, kFileFormatPcm16kHzFile)); - - CodecInst codecInst; - strncpy(codecInst.plname,"L16",32); - codecInst.channels = 1; - codecInst.rate = 256000; - codecInst.plfreq = 16000; - codecInst.pltype = 94; - codecInst.pacsize = 160; - - res = recObj.StartRecordingAudioFile(*streamOut,codecInst,0); - if (res) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "ConvertCompressedToPCM failed to create recorder object"); - playerObj.StopPlayingFile(); - FilePlayer::DestroyFilePlayer(&playerObj); - recObj.StopRecording(); - FileRecorder::DestroyFileRecorder(&recObj); - return -1; - } - - // Run throught the file - AudioFrame audioFrame; - int16_t decodedData[160]; - int decLength=0; - const uint32_t frequency = 16000; - - while(!playerObj.Get10msAudioFromFile(decodedData,decLength,frequency)) - { - if(decLength!=frequency/100) - { - // This is an OK way to end - break; - } - audioFrame.UpdateFrame(-1, 0, decodedData, - (uint16_t)decLength, - frequency, - AudioFrame::kNormalSpeech, - AudioFrame::kVadActive); - - res=recObj.RecordAudioToFile(audioFrame); - if(res) - { - WEBRTC_TRACE(kTraceError, kTraceVoice, - VoEId(_shared->instance_id(), -1), - "ConvertCompressedToPCM failed during conversion " - "(write frame)"); - } - } - - playerObj.StopPlayingFile(); - recObj.StopRecording(); - FilePlayer::DestroyFilePlayer(&playerObj); - FileRecorder::DestroyFileRecorder(&recObj); - - return res; -} - - -int VoEFileImpl::GetFileDuration(const char* fileNameUTF8, - int& durationMs, - FileFormats format) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetFileDuration(fileNameUTF8=%s, format=%d)", - fileNameUTF8, format); - - // Create a dummy file module for this - MediaFile * fileModule=MediaFile::CreateMediaFile(-1); - - // Temp container of the right format - uint32_t duration; - int res=fileModule->FileDurationMs(fileNameUTF8,duration,format); - if (res) - { - _shared->SetLastError(VE_BAD_FILE, kTraceError, - "GetFileDuration() failed measure file duration"); - return -1; - } - durationMs = duration; - MediaFile::DestroyMediaFile(fileModule); - fileModule = NULL; - - return(res); -} - -int VoEFileImpl::GetPlaybackPosition(int channel, int& positionMs) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetPlaybackPosition(channel=%d)", channel); - - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "GetPlaybackPosition() failed to locate channel"); - return -1; - } - return channelPtr->GetLocalPlayoutPosition(positionMs); -} - #endif // #ifdef WEBRTC_VOICE_ENGINE_FILE_API } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_file_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_file_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_file_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_file_impl.h 2015-02-03 14:33:38.000000000 +0000 @@ -41,8 +41,6 @@ virtual int IsPlayingFileLocally(int channel); - virtual int ScaleLocalFilePlayout(int channel, float scale); - // Use file as microphone input virtual int StartPlayingFileAsMicrophone( @@ -64,8 +62,6 @@ virtual int IsPlayingFileAsMicrophone(int channel); - virtual int ScaleFileAsMicrophonePlayout(int channel, float scale); - // Record speaker signal to file virtual int StartRecordingPlayout(int channel, @@ -90,43 +86,6 @@ virtual int StopRecordingMicrophone(); - // Conversion between different file formats - - virtual int ConvertPCMToWAV(const char* fileNameInUTF8, - const char* fileNameOutUTF8); - - virtual int ConvertPCMToWAV(InStream* streamIn, - OutStream* streamOut); - - virtual int ConvertWAVToPCM(const char* fileNameInUTF8, - const char* fileNameOutUTF8); - - virtual int ConvertWAVToPCM(InStream* streamIn, - OutStream* streamOut); - - virtual int ConvertPCMToCompressed(const char* fileNameInUTF8, - const char* fileNameOutUTF8, - CodecInst* compression); - - virtual int ConvertPCMToCompressed(InStream* streamIn, - OutStream* streamOut, - CodecInst* compression); - - virtual int ConvertCompressedToPCM(const char* fileNameInUTF8, - const char* fileNameOutUTF8); - - virtual int ConvertCompressedToPCM(InStream* streamIn, - OutStream* streamOut); - - // Misc file functions - - virtual int GetFileDuration( - const char* fileNameUTF8, - int& durationMs, - FileFormats format = kFileFormatPcm16kHzFile); - - virtual int GetPlaybackPosition(int channel, int& positionMs); - protected: VoEFileImpl(voe::SharedData* shared); virtual ~VoEFileImpl(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_hardware_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_hardware_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_hardware_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_hardware_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -148,8 +148,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "GetNumOfRecordingDevices(devices=?)"); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -169,8 +167,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "GetNumOfPlayoutDevices(devices=?)"); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -193,8 +189,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "GetRecordingDeviceName(index=%d)", index); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -251,8 +245,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "GetPlayoutDeviceName(index=%d)", index); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -310,8 +302,6 @@ "SetRecordingDevice(index=%d, recordingChannel=%d)", index, (int) recordingChannel); CriticalSectionScoped cs(_shared->crit_sec()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); - // TODO(leozwang): Add this api to Android OpenSL ES implementation. if (!_shared->statistics().Initialized()) { @@ -440,8 +430,6 @@ WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "SetPlayoutDevice(index=%d)", index); CriticalSectionScoped cs(_shared->crit_sec()); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -544,8 +532,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "GetRecordingDeviceStatus()"); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -578,8 +564,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "GetPlayoutDeviceStatus()"); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -612,7 +596,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "ResetAudioDevice()"); - ANDROID_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -627,13 +610,12 @@ " Failed to reset sound device"); return -1; } + return 0; #else _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, " no support for resetting sound device"); return -1; #endif - - return 0; } int VoEHardwareImpl::AudioDeviceControl(unsigned int par1, unsigned int par2, @@ -641,8 +623,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "AudioDeviceControl(%i, %i, %i)", par1, par2, par3); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { _shared->SetLastError(VE_NOT_INITED, kTraceError); @@ -657,7 +637,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "SetLoudspeakerStatus(enable=%i)", (int) enable); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -684,7 +663,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "GetLoudspeakerStatus()"); - IPHONE_NOT_SUPPORTED(_shared->statistics()); #if defined(WEBRTC_ANDROID) if (!_shared->statistics().Initialized()) @@ -712,8 +690,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "GetCPULoad()"); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_neteq_stats_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_neteq_stats_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_neteq_stats_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_neteq_stats_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -53,7 +53,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "GetNetworkStatistics(channel=%d, stats=?)", channel); - ANDROID_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -74,7 +73,6 @@ int VoENetEqStatsImpl::GetDecodingCallStatistics( int channel, AudioDecodingCallStats* stats) const { - ANDROID_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { _shared->SetLastError(VE_NOT_INITED, kTraceError); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_neteq_stats_unittest.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_neteq_stats_unittest.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_neteq_stats_unittest.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_neteq_stats_unittest.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,285 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/voice_engine/include/voe_neteq_stats.h" - -#include "testing/gtest/include/gtest/gtest.h" -#include "webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h" -#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" -#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" -#include "webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h" -#include "webrtc/modules/audio_device/include/fake_audio_device.h" -#include "webrtc/system_wrappers/interface/clock.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/test/testsupport/gtest_disable.h" -#include "webrtc/voice_engine/include/voe_base.h" -#include "webrtc/voice_engine/include/voe_hardware.h" -#include "webrtc/voice_engine/voice_engine_defines.h" - -namespace webrtc { -namespace voe { -namespace { - -const int kSampleRateHz = 16000; -const int kNumSamples10ms = kSampleRateHz / 100; -const int kFrameSizeMs = 10; // Multiple of 10. -const int kFrameSizeSamples = kFrameSizeMs / 10 * kNumSamples10ms; -const int kPayloadSizeBytes = kFrameSizeSamples * sizeof(int16_t); -const uint8_t kPayloadType = 111; - -class RtpUtility { - public: - RtpUtility(int samples_per_packet, uint8_t payload_type) - : samples_per_packet_(samples_per_packet), payload_type_(payload_type) {} - - virtual ~RtpUtility() {} - - void Populate(WebRtcRTPHeader* rtp_header) { - rtp_header->header.sequenceNumber = 0xABCD; - rtp_header->header.timestamp = 0xABCDEF01; - rtp_header->header.payloadType = payload_type_; - rtp_header->header.markerBit = false; - rtp_header->header.ssrc = 0x1234; - rtp_header->header.numCSRCs = 0; - rtp_header->frameType = kAudioFrameSpeech; - - rtp_header->header.payload_type_frequency = kSampleRateHz; - rtp_header->type.Audio.channel = 1; - rtp_header->type.Audio.isCNG = false; - } - - void Forward(WebRtcRTPHeader* rtp_header) { - ++rtp_header->header.sequenceNumber; - rtp_header->header.timestamp += samples_per_packet_; - } - - private: - int samples_per_packet_; - uint8_t payload_type_; -}; - -// This factory method allows access to ACM of a channel, facilitating insertion -// of packets to and pulling audio of ACM. -struct InsertAcm : AudioCodingModuleFactory { - explicit InsertAcm(AudioCodingModule* acm) : acm_(acm) {} - ~InsertAcm() {} - virtual AudioCodingModule* Create(int /*id*/) const { return acm_; } - - AudioCodingModule* acm_; -}; - -class VoENetEqStatsTest : public ::testing::Test { - protected: - VoENetEqStatsTest() - : acm1_(new acm1::AudioCodingModuleImpl(1, Clock::GetRealTimeClock())), - acm2_(new acm2::AudioCodingModuleImpl(2)), - voe_(VoiceEngine::Create()), - base_(VoEBase::GetInterface(voe_)), - voe_neteq_stats_(VoENetEqStats::GetInterface(voe_)), - channel_acm1_(-1), - channel_acm2_(-1), - adm_(new FakeAudioDeviceModule), - rtp_utility_(new RtpUtility(kFrameSizeSamples, kPayloadType)) {} - - ~VoENetEqStatsTest() {} - - void TearDown() { - voe_neteq_stats_->Release(); - base_->DeleteChannel(channel_acm1_); - base_->DeleteChannel(channel_acm2_); - base_->Terminate(); - base_->Release(); - VoiceEngine::Delete(voe_); - } - - void SetUp() { - // Check if all components are valid. - ASSERT_TRUE(voe_ != NULL); - ASSERT_TRUE(base_ != NULL); - ASSERT_TRUE(adm_.get() != NULL); - ASSERT_EQ(0, base_->Init(adm_.get())); - - // Set configs. - config_acm1_.Set(new InsertAcm(acm1_)); - config_acm2_.Set(new InsertAcm(acm2_)); - - // Create channe1s; - channel_acm1_ = base_->CreateChannel(config_acm1_); - ASSERT_NE(-1, channel_acm1_); - - channel_acm2_ = base_->CreateChannel(config_acm2_); - ASSERT_NE(-1, channel_acm2_); - - CodecInst codec; - AudioCodingModule::Codec("L16", &codec, kSampleRateHz, 1); - codec.pltype = kPayloadType; - - // Register L16 codec in ACMs. - ASSERT_EQ(0, acm1_->RegisterReceiveCodec(codec)); - ASSERT_EQ(0, acm2_->RegisterReceiveCodec(codec)); - - rtp_utility_->Populate(&rtp_header_); - } - - void InsertPacketAndPullAudio() { - AudioFrame audio_frame; - const uint8_t kPayload[kPayloadSizeBytes] = {0}; - - ASSERT_EQ(0, - acm1_->IncomingPacket(kPayload, kPayloadSizeBytes, rtp_header_)); - ASSERT_EQ(0, - acm2_->IncomingPacket(kPayload, kPayloadSizeBytes, rtp_header_)); - - ASSERT_EQ(0, acm1_->PlayoutData10Ms(-1, &audio_frame)); - ASSERT_EQ(0, acm2_->PlayoutData10Ms(-1, &audio_frame)); - rtp_utility_->Forward(&rtp_header_); - } - - void JustPullAudio() { - AudioFrame audio_frame; - ASSERT_EQ(0, acm1_->PlayoutData10Ms(-1, &audio_frame)); - ASSERT_EQ(0, acm2_->PlayoutData10Ms(-1, &audio_frame)); - } - - Config config_acm1_; - Config config_acm2_; - - // ACMs are inserted into VoE channels, and this class is not the owner of - // them. Therefore, they should not be deleted, not even in destructor. - AudioCodingModule* acm1_; - AudioCodingModule* acm2_; - - VoiceEngine* voe_; - VoEBase* base_; - VoENetEqStats* voe_neteq_stats_; - int channel_acm1_; - int channel_acm2_; - scoped_ptr adm_; - scoped_ptr rtp_utility_; - WebRtcRTPHeader rtp_header_; -}; - -// Check if the statistics are initialized correctly. Before any call to ACM -// all fields have to be zero. -TEST_F(VoENetEqStatsTest, DISABLED_ON_ANDROID(InitializedToZero)) { - AudioDecodingCallStats stats; - ASSERT_EQ(0, - voe_neteq_stats_->GetDecodingCallStatistics(channel_acm1_, &stats)); - EXPECT_EQ(0, stats.calls_to_neteq); - EXPECT_EQ(0, stats.calls_to_silence_generator); - EXPECT_EQ(0, stats.decoded_normal); - EXPECT_EQ(0, stats.decoded_cng); - EXPECT_EQ(0, stats.decoded_plc); - EXPECT_EQ(0, stats.decoded_plc_cng); - - ASSERT_EQ(0, - voe_neteq_stats_->GetDecodingCallStatistics(channel_acm2_, &stats)); - EXPECT_EQ(0, stats.calls_to_neteq); - EXPECT_EQ(0, stats.calls_to_silence_generator); - EXPECT_EQ(0, stats.decoded_normal); - EXPECT_EQ(0, stats.decoded_cng); - EXPECT_EQ(0, stats.decoded_plc); - EXPECT_EQ(0, stats.decoded_plc_cng); -} - -// Apply an initial playout delay. Calls to AudioCodingModule::PlayoutData10ms() -// should result in generating silence, check the associated field. -TEST_F(VoENetEqStatsTest, DISABLED_ON_ANDROID(SilenceGeneratorCalled)) { - AudioDecodingCallStats stats; - const int kInitialDelay = 100; - - acm1_->SetInitialPlayoutDelay(kInitialDelay); - acm2_->SetInitialPlayoutDelay(kInitialDelay); - - AudioFrame audio_frame; - int num_calls = 0; - for (int time_ms = 0; time_ms < kInitialDelay; - time_ms += kFrameSizeMs, ++num_calls) { - InsertPacketAndPullAudio(); - } - ASSERT_EQ(0, - voe_neteq_stats_->GetDecodingCallStatistics(channel_acm1_, &stats)); - EXPECT_EQ(0, stats.calls_to_neteq); - EXPECT_EQ(num_calls, stats.calls_to_silence_generator); - EXPECT_EQ(0, stats.decoded_normal); - EXPECT_EQ(0, stats.decoded_cng); - EXPECT_EQ(0, stats.decoded_plc); - EXPECT_EQ(0, stats.decoded_plc_cng); - - ASSERT_EQ(0, - voe_neteq_stats_->GetDecodingCallStatistics(channel_acm2_, &stats)); - EXPECT_EQ(0, stats.calls_to_neteq); - EXPECT_EQ(num_calls, stats.calls_to_silence_generator); - EXPECT_EQ(0, stats.decoded_normal); - EXPECT_EQ(0, stats.decoded_cng); - EXPECT_EQ(0, stats.decoded_plc); - EXPECT_EQ(0, stats.decoded_plc_cng); -} - -// Insert some packets and pull audio. Check statistics are valid. Then, -// simulate packet loss and check if PLC and PLC-to-CNG statistics are -// correctly updated. -TEST_F(VoENetEqStatsTest, DISABLED_ON_ANDROID(NetEqCalls)) { - AudioDecodingCallStats stats; - const int kNumNormalCalls = 10; - - AudioFrame audio_frame; - for (int num_calls = 0; num_calls < kNumNormalCalls; ++num_calls) { - InsertPacketAndPullAudio(); - } - ASSERT_EQ(0, - voe_neteq_stats_->GetDecodingCallStatistics(channel_acm1_, &stats)); - EXPECT_EQ(kNumNormalCalls, stats.calls_to_neteq); - EXPECT_EQ(0, stats.calls_to_silence_generator); - EXPECT_EQ(kNumNormalCalls, stats.decoded_normal); - EXPECT_EQ(0, stats.decoded_cng); - EXPECT_EQ(0, stats.decoded_plc); - EXPECT_EQ(0, stats.decoded_plc_cng); - - ASSERT_EQ(0, - voe_neteq_stats_->GetDecodingCallStatistics(channel_acm2_, &stats)); - EXPECT_EQ(kNumNormalCalls, stats.calls_to_neteq); - EXPECT_EQ(0, stats.calls_to_silence_generator); - EXPECT_EQ(kNumNormalCalls, stats.decoded_normal); - EXPECT_EQ(0, stats.decoded_cng); - EXPECT_EQ(0, stats.decoded_plc); - EXPECT_EQ(0, stats.decoded_plc_cng); - - const int kNumPlc = 3; - const int kNumPlcCng = 5; - - // Simulate packet-loss. NetEq first performs PLC then PLC fades to CNG. - for (int n = 0; n < kNumPlc + kNumPlcCng; ++n) { - JustPullAudio(); - } - ASSERT_EQ(0, - voe_neteq_stats_->GetDecodingCallStatistics(channel_acm1_, &stats)); - EXPECT_EQ(kNumNormalCalls + kNumPlc + kNumPlcCng, stats.calls_to_neteq); - EXPECT_EQ(0, stats.calls_to_silence_generator); - EXPECT_EQ(kNumNormalCalls, stats.decoded_normal); - EXPECT_EQ(0, stats.decoded_cng); - EXPECT_EQ(kNumPlc, stats.decoded_plc); - EXPECT_EQ(kNumPlcCng, stats.decoded_plc_cng); - - ASSERT_EQ(0, - voe_neteq_stats_->GetDecodingCallStatistics(channel_acm2_, &stats)); - EXPECT_EQ(kNumNormalCalls + kNumPlc + kNumPlcCng, stats.calls_to_neteq); - EXPECT_EQ(0, stats.calls_to_silence_generator); - EXPECT_EQ(kNumNormalCalls, stats.decoded_normal); - EXPECT_EQ(0, stats.decoded_cng); - EXPECT_EQ(kNumPlc, stats.decoded_plc); - EXPECT_EQ(kNumPlcCng, stats.decoded_plc_cng); -} - -} // namespace - -} // namespace voe - -} // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_network_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_network_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_network_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_network_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -88,7 +88,14 @@ int VoENetworkImpl::ReceivedRTPPacket(int channel, const void* data, - unsigned int length) + unsigned int length) { + return ReceivedRTPPacket(channel, data, length, webrtc::PacketTime()); +} + +int VoENetworkImpl::ReceivedRTPPacket(int channel, + const void* data, + unsigned int length, + const PacketTime& packet_time) { WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_shared->instance_id(), -1), "ReceivedRTPPacket(channel=%d, length=%u)", channel, length); @@ -125,7 +132,8 @@ "ReceivedRTPPacket() external transport is not enabled"); return -1; } - return channelPtr->ReceivedRTPPacket((const int8_t*) data, length); + return channelPtr->ReceivedRTPPacket((const int8_t*) data, length, + packet_time); } int VoENetworkImpl::ReceivedRTCPPacket(int channel, const void* data, diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_network_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_network_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_network_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_network_impl.h 2015-02-03 14:33:38.000000000 +0000 @@ -22,17 +22,22 @@ class VoENetworkImpl: public VoENetwork { public: - virtual int RegisterExternalTransport(int channel, Transport& transport); + virtual int RegisterExternalTransport(int channel, + Transport& transport) OVERRIDE; - virtual int DeRegisterExternalTransport(int channel); + virtual int DeRegisterExternalTransport(int channel) OVERRIDE; virtual int ReceivedRTPPacket(int channel, const void* data, - unsigned int length); + unsigned int length) OVERRIDE; + virtual int ReceivedRTPPacket(int channel, + const void* data, + unsigned int length, + const PacketTime& packet_time) OVERRIDE; virtual int ReceivedRTCPPacket(int channel, const void* data, - unsigned int length); + unsigned int length) OVERRIDE; protected: VoENetworkImpl(voe::SharedData* shared); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_rtp_rtcp_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_rtp_rtcp_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_rtp_rtcp_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_rtp_rtcp_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -11,6 +11,7 @@ #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/file_wrapper.h" #include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/video_engine/include/vie_network.h" #include "webrtc/voice_engine/include/voe_errors.h" #include "webrtc/voice_engine/voe_rtp_rtcp_impl.h" #include "webrtc/voice_engine/voice_engine_impl.h" @@ -49,88 +50,6 @@ "VoERTP_RTCPImpl::~VoERTP_RTCPImpl() - dtor"); } -int VoERTP_RTCPImpl::RegisterRTPObserver(int channel, VoERTPObserver& observer) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "RegisterRTPObserver(channel=%d observer=0x%x)", - channel, &observer); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "RegisterRTPObserver() failed to locate channel"); - return -1; - } - return channelPtr->RegisterRTPObserver(observer); -} - -int VoERTP_RTCPImpl::DeRegisterRTPObserver(int channel) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "DeRegisterRTPObserver(channel=%d)", channel); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "DeRegisterRTPObserver() failed to locate channel"); - return -1; - } - return channelPtr->DeRegisterRTPObserver(); -} - -int VoERTP_RTCPImpl::RegisterRTCPObserver(int channel, VoERTCPObserver& observer) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "RegisterRTCPObserver(channel=%d observer=0x%x)", - channel, &observer); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "RegisterRTPObserver() failed to locate channel"); - return -1; - } - return channelPtr->RegisterRTCPObserver(observer); -} - -int VoERTP_RTCPImpl::DeRegisterRTCPObserver(int channel) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "DeRegisterRTCPObserver(channel=%d)", channel); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "DeRegisterRTCPObserver() failed to locate channel"); - return -1; - } - return channelPtr->DeRegisterRTCPObserver(); -} - int VoERTP_RTCPImpl::SetLocalSSRC(int channel, unsigned int ssrc) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), @@ -191,82 +110,126 @@ return channelPtr->GetRemoteSSRC(ssrc); } -int VoERTP_RTCPImpl::GetRemoteCSRCs(int channel, unsigned int arrCSRC[15]) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetRemoteCSRCs(channel=%d, arrCSRC=?)", channel); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "GetRemoteCSRCs() failed to locate channel"); - return -1; - } - return channelPtr->GetRemoteCSRCs(arrCSRC); -} - - -int VoERTP_RTCPImpl::SetRTPAudioLevelIndicationStatus(int channel, - bool enable, - unsigned char ID) +int VoERTP_RTCPImpl::SetSendAudioLevelIndicationStatus(int channel, + bool enable, + unsigned char id) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "SetRTPAudioLevelIndicationStatus(channel=%d, enable=%d," - " ID=%u)", channel, enable, ID); + "SetSendAudioLevelIndicationStatus(channel=%d, enable=%d," + " ID=%u)", channel, enable, id); if (!_shared->statistics().Initialized()) { _shared->SetLastError(VE_NOT_INITED, kTraceError); return -1; } - if (enable && (ID < kVoiceEngineMinRtpExtensionId || - ID > kVoiceEngineMaxRtpExtensionId)) + if (enable && (id < kVoiceEngineMinRtpExtensionId || + id > kVoiceEngineMaxRtpExtensionId)) { - // [RFC5285] The 4-bit ID is the local identifier of this element in + // [RFC5285] The 4-bit id is the local identifier of this element in // the range 1-14 inclusive. _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, - "SetRTPAudioLevelIndicationStatus() invalid ID parameter"); + "SetSendAudioLevelIndicationStatus() invalid ID parameter"); return -1; } - // Set state and ID for the specified channel. + // Set state and id for the specified channel. voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); voe::Channel* channelPtr = ch.channel(); if (channelPtr == NULL) { _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "SetRTPAudioLevelIndicationStatus() failed to locate channel"); + "SetSendAudioLevelIndicationStatus() failed to locate channel"); return -1; } - return channelPtr->SetRTPAudioLevelIndicationStatus(enable, ID); + return channelPtr->SetSendAudioLevelIndicationStatus(enable, id); } -int VoERTP_RTCPImpl::GetRTPAudioLevelIndicationStatus(int channel, - bool& enabled, - unsigned char& ID) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetRTPAudioLevelIndicationStatus(channel=%d, enable=?, ID=?)", - channel); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "GetRTPAudioLevelIndicationStatus() failed to locate channel"); - return -1; - } - return channelPtr->GetRTPAudioLevelIndicationStatus(enabled, ID); +int VoERTP_RTCPImpl::SetReceiveAudioLevelIndicationStatus(int channel, + bool enable, + unsigned char id) { + WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), + "SetReceiveAudioLevelIndicationStatus(channel=%d, enable=%d, id=%u)", + channel, enable, id); + if (!_shared->statistics().Initialized()) { + _shared->SetLastError(VE_NOT_INITED, kTraceError); + return -1; + } + if (enable && + (id < kVoiceEngineMinRtpExtensionId || + id > kVoiceEngineMaxRtpExtensionId)) { + // [RFC5285] The 4-bit id is the local identifier of this element in + // the range 1-14 inclusive. + _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, + "SetReceiveAbsoluteSenderTimeStatus() invalid id parameter"); + return -1; + } + // Set state and id for the specified channel. + voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); + voe::Channel* channel_ptr = ch.channel(); + if (channel_ptr == NULL) { + _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, + "SetReceiveAudioLevelIndicationStatus() failed to locate channel"); + return -1; + } + return channel_ptr->SetReceiveAudioLevelIndicationStatus(enable, id); +} + +int VoERTP_RTCPImpl::SetSendAbsoluteSenderTimeStatus(int channel, + bool enable, + unsigned char id) { + WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), + "SetSendAbsoluteSenderTimeStatus(channel=%d, enable=%d, id=%u)", + channel, enable, id); + if (!_shared->statistics().Initialized()) { + _shared->SetLastError(VE_NOT_INITED, kTraceError); + return -1; + } + if (enable && (id < kVoiceEngineMinRtpExtensionId || + id > kVoiceEngineMaxRtpExtensionId)) { + // [RFC5285] The 4-bit id is the local identifier of this element in + // the range 1-14 inclusive. + _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, + "SetSendAbsoluteSenderTimeStatus() invalid id parameter"); + return -1; + } + // Set state and id for the specified channel. + voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); + voe::Channel* channelPtr = ch.channel(); + if (channelPtr == NULL) { + _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, + "SetSendAbsoluteSenderTimeStatus() failed to locate channel"); + return -1; + } + return channelPtr->SetSendAbsoluteSenderTimeStatus(enable, id); +} + +int VoERTP_RTCPImpl::SetReceiveAbsoluteSenderTimeStatus(int channel, + bool enable, + unsigned char id) { + WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), + "SetReceiveAbsoluteSenderTimeStatus(channel=%d, enable=%d, id=%u)", + channel, enable, id); + if (!_shared->statistics().Initialized()) { + _shared->SetLastError(VE_NOT_INITED, kTraceError); + return -1; + } + if (enable && (id < kVoiceEngineMinRtpExtensionId || + id > kVoiceEngineMaxRtpExtensionId)) { + // [RFC5285] The 4-bit id is the local identifier of this element in + // the range 1-14 inclusive. + _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, + "SetReceiveAbsoluteSenderTimeStatus() invalid id parameter"); + return -1; + } + // Set state and id for the specified channel. + voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); + voe::Channel* channelPtr = ch.channel(); + if (channelPtr == NULL) { + _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, + "SetReceiveAbsoluteSenderTimeStatus() failed to locate channel"); + return -1; + } + return channelPtr->SetReceiveAbsoluteSenderTimeStatus(enable, id); } int VoERTP_RTCPImpl::SetRTCPStatus(int channel, bool enable) @@ -329,26 +292,6 @@ return channelPtr->SetRTCP_CNAME(cName); } -int VoERTP_RTCPImpl::GetRTCP_CNAME(int channel, char cName[256]) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetRTCP_CNAME(channel=%d, cName=?)", channel); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "GetRTCP_CNAME() failed to locate channel"); - return -1; - } - return channelPtr->GetRTCP_CNAME(cName); -} - int VoERTP_RTCPImpl::GetRemoteRTCP_CNAME(int channel, char cName[256]) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), @@ -405,36 +348,6 @@ rttMs); } -int VoERTP_RTCPImpl::SendApplicationDefinedRTCPPacket( - int channel, - unsigned char subType, - unsigned int name, - const char* data, - unsigned short dataLengthInBytes) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "SendApplicationDefinedRTCPPacket(channel=%d, subType=%u," - "name=%u, data=?, dataLengthInBytes=%u)", - channel, subType, name, dataLengthInBytes); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "SendApplicationDefinedRTCPPacket() failed to locate channel"); - return -1; - } - return channelPtr->SendApplicationDefinedRTCPPacket(subType, - name, - data, - dataLengthInBytes); -} - int VoERTP_RTCPImpl::GetRTPStatistics(int channel, unsigned int& averageJitterMs, unsigned int& maxJitterMs, @@ -482,24 +395,6 @@ return channelPtr->GetRTPStatistics(stats); } -int VoERTP_RTCPImpl::GetRemoteRTCPSenderInfo(int channel, - SenderInfo* sender_info) { - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetRemoteRTCPSenderInfo(channel=%d)", channel); - if (!_shared->statistics().Initialized()) { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channel_ptr = ch.channel(); - if (channel_ptr == NULL) { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "GetRemoteRTCPSenderInfo() failed to locate channel"); - return -1; - } - return channel_ptr->GetRemoteRTCPSenderInfo(sender_info); -} - int VoERTP_RTCPImpl::GetRemoteRTCPReportBlocks( int channel, std::vector* report_blocks) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), @@ -518,10 +413,10 @@ return channel_ptr->GetRemoteRTCPReportBlocks(report_blocks); } -int VoERTP_RTCPImpl::SetFECStatus(int channel, bool enable, int redPayloadtype) +int VoERTP_RTCPImpl::SetREDStatus(int channel, bool enable, int redPayloadtype) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "SetFECStatus(channel=%d, enable=%d, redPayloadtype=%d)", + "SetREDStatus(channel=%d, enable=%d, redPayloadtype=%d)", channel, enable, redPayloadtype); #ifdef WEBRTC_CODEC_RED if (!_shared->statistics().Initialized()) @@ -534,23 +429,23 @@ if (channelPtr == NULL) { _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "SetFECStatus() failed to locate channel"); + "SetREDStatus() failed to locate channel"); return -1; } - return channelPtr->SetFECStatus(enable, redPayloadtype); + return channelPtr->SetREDStatus(enable, redPayloadtype); #else _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, - "SetFECStatus() RED is not supported"); + "SetREDStatus() RED is not supported"); return -1; #endif } -int VoERTP_RTCPImpl::GetFECStatus(int channel, +int VoERTP_RTCPImpl::GetREDStatus(int channel, bool& enabled, int& redPayloadtype) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetFECStatus(channel=%d, enabled=?, redPayloadtype=?)", + "GetREDStatus(channel=%d, enabled=?, redPayloadtype=?)", channel); #ifdef WEBRTC_CODEC_RED if (!_shared->statistics().Initialized()) @@ -563,18 +458,17 @@ if (channelPtr == NULL) { _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "GetFECStatus() failed to locate channel"); + "GetREDStatus() failed to locate channel"); return -1; } - return channelPtr->GetFECStatus(enabled, redPayloadtype); + return channelPtr->GetREDStatus(enabled, redPayloadtype); #else _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, - "GetFECStatus() RED is not supported"); + "GetREDStatus() RED is not supported"); return -1; #endif } - int VoERTP_RTCPImpl::SetNACKStatus(int channel, bool enable, int maxNoPackets) @@ -661,55 +555,25 @@ return channelPtr->RTPDumpIsActive(direction); } -int VoERTP_RTCPImpl::InsertExtraRTPPacket(int channel, - unsigned char payloadType, - bool markerBit, - const char* payloadData, - unsigned short payloadSize) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "InsertExtraRTPPacket(channel=%d, payloadType=%u," - " markerBit=%u, payloadSize=%u)", - channel, payloadType, markerBit, payloadSize); - - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "InsertExtraRTPPacket() failed to locate channel"); - return -1; - } - return channelPtr->InsertExtraRTPPacket(payloadType, - markerBit, - payloadData, - payloadSize); -} +int VoERTP_RTCPImpl::SetVideoEngineBWETarget(int channel, + ViENetwork* vie_network, + int video_channel) { + WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), + "SetVideoEngineBWETarget(channel=%d, vie_network=?, video_channel=%d)", + channel, vie_network, video_channel); -int VoERTP_RTCPImpl::GetLastRemoteTimeStamp(int channel, - uint32_t* timestamp) { - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetLastRemoteTimeStamp(channel=%d, timestamp=?)", channel); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "GetLastRemoteTimeStamp() failed to locate channel"); - return -1; + voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); + voe::Channel* channelPtr = ch.channel(); + if (channelPtr == NULL) { + _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, + "SetVideoEngineBWETarget() failed to locate channel"); + if (vie_network) { + vie_network->Release(); } - *timestamp = channelPtr->LastRemoteTimeStamp(); - return 0; + return -1; + } + channelPtr->SetVideoEngineBWETarget(vie_network, video_channel); + return 0; } #endif // #ifdef WEBRTC_VOICE_ENGINE_RTP_RTCP_API diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_rtp_rtcp_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_rtp_rtcp_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_rtp_rtcp_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_rtp_rtcp_impl.h 2015-02-03 14:33:38.000000000 +0000 @@ -20,15 +20,6 @@ class VoERTP_RTCPImpl : public VoERTP_RTCP { public: - // Registration of observers for RTP and RTCP callbacks - virtual int RegisterRTPObserver(int channel, VoERTPObserver& observer); - - virtual int DeRegisterRTPObserver(int channel); - - virtual int RegisterRTCPObserver(int channel, VoERTCPObserver& observer); - - virtual int DeRegisterRTCPObserver(int channel); - // RTCP virtual int SetRTCPStatus(int channel, bool enable); @@ -36,8 +27,6 @@ virtual int SetRTCP_CNAME(int channel, const char cName[256]); - virtual int GetRTCP_CNAME(int channel, char cName[256]); - virtual int GetRemoteRTCP_CNAME(int channel, char cName[256]); virtual int GetRemoteRTCPReceiverInfo(int channel, @@ -50,13 +39,6 @@ uint32_t& cumulativeLost, int32_t& rttMs); - virtual int SendApplicationDefinedRTCPPacket( - int channel, - unsigned char subType, - unsigned int name, - const char* data, - unsigned short dataLengthInBytes); - // SSRC virtual int SetLocalSSRC(int channel, unsigned int ssrc); @@ -65,16 +47,20 @@ virtual int GetRemoteSSRC(int channel, unsigned int& ssrc); // RTP Header Extension for Client-to-Mixer Audio Level Indication - virtual int SetRTPAudioLevelIndicationStatus(int channel, - bool enable, - unsigned char ID); - - virtual int GetRTPAudioLevelIndicationStatus(int channel, - bool& enabled, - unsigned char& ID); - - // CSRC - virtual int GetRemoteCSRCs(int channel, unsigned int arrCSRC[15]); + virtual int SetSendAudioLevelIndicationStatus(int channel, + bool enable, + unsigned char id); + virtual int SetReceiveAudioLevelIndicationStatus(int channel, + bool enable, + unsigned char id); + + // RTP Header Extension for Absolute Sender Time + virtual int SetSendAbsoluteSenderTimeStatus(int channel, + bool enable, + unsigned char id); + virtual int SetReceiveAbsoluteSenderTimeStatus(int channel, + bool enable, + unsigned char id); // Statistics virtual int GetRTPStatistics(int channel, @@ -85,17 +71,15 @@ virtual int GetRTCPStatistics(int channel, CallStatistics& stats); - virtual int GetRemoteRTCPSenderInfo(int channel, SenderInfo* sender_info); - virtual int GetRemoteRTCPReportBlocks( int channel, std::vector* report_blocks); - // FEC - virtual int SetFECStatus(int channel, + // RED + virtual int SetREDStatus(int channel, bool enable, int redPayloadtype = -1); - virtual int GetFECStatus(int channel, bool& enabled, int& redPayloadtype); + virtual int GetREDStatus(int channel, bool& enabled, int& redPayloadtype); //NACK virtual int SetNACKStatus(int channel, @@ -113,14 +97,8 @@ virtual int RTPDumpIsActive(int channel, RTPDirections direction = kRtpIncoming); - // Insert (and transmits) extra RTP packet into active RTP audio stream - virtual int InsertExtraRTPPacket(int channel, - unsigned char payloadType, - bool markerBit, - const char* payloadData, - unsigned short payloadSize); - virtual int GetLastRemoteTimeStamp(int channel, - uint32_t* lastRemoteTimeStamp); + virtual int SetVideoEngineBWETarget(int channel, ViENetwork* vie_network, + int video_channel); protected: VoERTP_RTCPImpl(voe::SharedData* shared); virtual ~VoERTP_RTCPImpl(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_video_sync_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_video_sync_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_video_sync_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_video_sync_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -51,7 +51,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "GetPlayoutTimestamp(channel=%d, timestamp=?)", channel); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -75,7 +74,6 @@ WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "SetInitTimestamp(channel=%d, timestamp=%lu)", channel, timestamp); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -99,7 +97,6 @@ WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "SetInitSequenceNumber(channel=%d, sequenceNumber=%hd)", channel, sequenceNumber); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -122,7 +119,6 @@ WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "SetMinimumPlayoutDelay(channel=%d, delayMs=%d)", channel, delayMs); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -162,7 +158,6 @@ WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "SetInitialPlayoutDelay(channel=%d, delay_ms=%d)", channel, delay_ms); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -186,7 +181,6 @@ int* avsync_offset_ms) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "GetDelayEstimate(channel=%d, delayMs=?)", channel); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { _shared->SetLastError(VE_NOT_INITED, kTraceError); @@ -211,7 +205,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "GetPlayoutBufferSize(bufferMs=?)"); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -259,7 +252,6 @@ int VoEVideoSyncImpl::GetLeastRequiredDelayMs(int channel) const { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "GetLeastRequiredDelayMS(channel=%d)", channel); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { _shared->SetLastError(VE_NOT_INITED, kTraceError); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_volume_control_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_volume_control_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_volume_control_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_volume_control_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -54,7 +54,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "SetSpeakerVolume(volume=%u)", volume); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -96,7 +95,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "GetSpeakerVolume()"); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -131,56 +129,10 @@ return 0; } -int VoEVolumeControlImpl::SetSystemOutputMute(bool enable) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetSystemOutputMute(enabled=%d)", enable); - - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - - if (_shared->audio_device()->SetSpeakerMute(enable) != 0) - { - _shared->SetLastError(VE_GET_MIC_VOL_ERROR, kTraceError, - "SpeakerMute() unable to Set speaker mute"); - return -1; - } - - return 0; -} - -int VoEVolumeControlImpl::GetSystemOutputMute(bool& enabled) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetSystemOutputMute(enabled=?)"); - - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - - if (_shared->audio_device()->SpeakerMute(&enabled) != 0) - { - _shared->SetLastError(VE_GET_MIC_VOL_ERROR, kTraceError, - "SpeakerMute() unable to get speaker mute state"); - return -1; - } - WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, - VoEId(_shared->instance_id(), -1), - "GetSystemOutputMute() => %d", enabled); - return 0; -} - int VoEVolumeControlImpl::SetMicVolume(unsigned int volume) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "SetMicVolume(volume=%u)", volume); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -238,8 +190,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "GetMicVolume()"); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -294,20 +244,16 @@ // Mute before demultiplexing <=> affects all channels return _shared->transmit_mixer()->SetMute(enable); } - else + // Mute after demultiplexing <=> affects one channel only + voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); + voe::Channel* channelPtr = ch.channel(); + if (channelPtr == NULL) { - // Mute after demultiplexing <=> affects one channel only - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "SetInputMute() failed to locate channel"); - return -1; - } - return channelPtr->SetMute(enable); + _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, + "SetInputMute() failed to locate channel"); + return -1; } - return 0; + return channelPtr->SetMute(enable); } int VoEVolumeControlImpl::GetInputMute(int channel, bool& enabled) @@ -342,50 +288,6 @@ return 0; } -int VoEVolumeControlImpl::SetSystemInputMute(bool enable) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "SetSystemInputMute(enabled=%d)", enable); - - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - - if (_shared->audio_device()->SetMicrophoneMute(enable) != 0) - { - _shared->SetLastError(VE_GET_MIC_VOL_ERROR, kTraceError, - "MicrophoneMute() unable to set microphone mute state"); - return -1; - } - - return 0; -} - -int VoEVolumeControlImpl::GetSystemInputMute(bool& enabled) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetSystemInputMute(enabled=?)"); - - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - - if (_shared->audio_device()->MicrophoneMute(&enabled) != 0) - { - _shared->SetLastError(VE_GET_MIC_VOL_ERROR, kTraceError, - "MicrophoneMute() unable to get microphone mute state"); - return -1; - } - WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, - VoEId(_shared->instance_id(), -1), - "GetSystemInputMute() => %d", enabled); - return 0; -} - int VoEVolumeControlImpl::GetSpeechInputLevel(unsigned int& level) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), @@ -409,7 +311,7 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "GetSpeechOutputLevel(channel=%d, level=?)", channel); - + if (!_shared->statistics().Initialized()) { _shared->SetLastError(VE_NOT_INITED, kTraceError); @@ -542,8 +444,6 @@ WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "SetOutputVolumePan(channel=%d, left=%2.1f, right=%2.1f)", channel, left, right); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -574,20 +474,16 @@ // Master balance (affectes the signal after output mixing) return _shared->output_mixer()->SetOutputVolumePan(left, right); } - else + // Per-channel balance (affects the signal before output mixing) + voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); + voe::Channel* channelPtr = ch.channel(); + if (channelPtr == NULL) { - // Per-channel balance (affects the signal before output mixing) - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "SetOutputVolumePan() failed to locate channel"); - return -1; - } - return channelPtr->SetOutputVolumePan(left, right); + _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, + "SetOutputVolumePan() failed to locate channel"); + return -1; } - return 0; + return channelPtr->SetOutputVolumePan(left, right); } int VoEVolumeControlImpl::GetOutputVolumePan(int channel, @@ -596,8 +492,6 @@ { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "GetOutputVolumePan(channel=%d, left=?, right=?)", channel); - ANDROID_NOT_SUPPORTED(_shared->statistics()); - IPHONE_NOT_SUPPORTED(_shared->statistics()); if (!_shared->statistics().Initialized()) { @@ -618,19 +512,15 @@ { return _shared->output_mixer()->GetOutputVolumePan(left, right); } - else + voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); + voe::Channel* channelPtr = ch.channel(); + if (channelPtr == NULL) { - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "GetOutputVolumePan() failed to locate channel"); - return -1; - } - return channelPtr->GetOutputVolumePan(left, right); + _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, + "GetOutputVolumePan() failed to locate channel"); + return -1; } - return 0; + return channelPtr->GetOutputVolumePan(left, right); } #endif // #ifdef WEBRTC_VOICE_ENGINE_VOLUME_CONTROL_API diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_volume_control_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_volume_control_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_volume_control_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voe_volume_control_impl.h 2015-02-03 14:33:38.000000000 +0000 @@ -24,10 +24,6 @@ virtual int GetSpeakerVolume(unsigned int& volume); - virtual int SetSystemOutputMute(bool enable); - - virtual int GetSystemOutputMute(bool& enabled); - virtual int SetMicVolume(unsigned int volume); virtual int GetMicVolume(unsigned int& volume); @@ -36,10 +32,6 @@ virtual int GetInputMute(int channel, bool& enabled); - virtual int SetSystemInputMute(bool enable); - - virtual int GetSystemInputMute(bool& enabled); - virtual int GetSpeechInputLevel(unsigned int& level); virtual int GetSpeechOutputLevel(int channel, unsigned int& level); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine_defines.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine_defines.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine_defines.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine_defines.h 2015-02-03 14:33:38.000000000 +0000 @@ -27,6 +27,10 @@ namespace webrtc { +// Internal buffer size required for mono audio, based on the highest sample +// rate voice engine supports (10 ms of audio at 192 kHz). +static const int kMaxMonoDataSizeSamples = 1920; + // VolumeControl enum { kMinVolumeLevel = 0 }; enum { kMaxVolumeLevel = 255 }; @@ -60,13 +64,13 @@ // Audio processing const NoiseSuppression::Level kDefaultNsMode = NoiseSuppression::kModerate; const GainControl::Mode kDefaultAgcMode = -#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) +#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) || defined(WEBRTC_GONK) GainControl::kAdaptiveDigital; #else GainControl::kAdaptiveAnalog; #endif const bool kDefaultAgcState = -#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) +#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) || defined(WEBRTC_GONK) false; #else true; @@ -124,26 +128,6 @@ } // namespace webrtc // ---------------------------------------------------------------------------- -// Build information macros -// ---------------------------------------------------------------------------- - -#if defined(_DEBUG) -#define BUILDMODE "d" -#elif defined(DEBUG) -#define BUILDMODE "d" -#elif defined(NDEBUG) -#define BUILDMODE "r" -#else -#define BUILDMODE "?" -#endif - -#define BUILDTIME __TIME__ -#define BUILDDATE __DATE__ - -// Example: "Oct 10 2002 12:05:30 r" -#define BUILDINFO BUILDDATE " " BUILDTIME " " BUILDMODE - -// ---------------------------------------------------------------------------- // Macros // ---------------------------------------------------------------------------- @@ -279,37 +263,6 @@ // Default device for Linux and Android #define WEBRTC_VOICE_ENGINE_DEFAULT_DEVICE 0 -#ifdef ANDROID - -// ---------------------------------------------------------------------------- -// Defines -// ---------------------------------------------------------------------------- - - // Always excluded for Android builds - #undef WEBRTC_CODEC_ISAC - // We need WEBRTC_VOE_EXTERNAL_REC_AND_PLAYOUT to make things work on Android. - // Motivation for the commented-out undef below is unclear. - // - // #undef WEBRTC_VOE_EXTERNAL_REC_AND_PLAYOUT - - // This macro used to cause the calling function to set an error code and return. - // However, not doing that seems to cause the unit tests to pass / behave reasonably, - // so it's disabled for now; see bug 819856. - #define ANDROID_NOT_SUPPORTED(stat) - //#define ANDROID_NOT_SUPPORTED(stat) NOT_SUPPORTED(stat) - -#else // LINUX PC - -// ---------------------------------------------------------------------------- -// Defines -// ---------------------------------------------------------------------------- - - #define ANDROID_NOT_SUPPORTED(stat) - -#endif // ANDROID - LINUX PC - -#else -#define ANDROID_NOT_SUPPORTED(stat) #endif // #ifdef WEBRTC_LINUX // *** WEBRTC_MAC *** @@ -343,7 +296,6 @@ #include #endif - #define DWORD unsigned long int #define WINAPI #define LPVOID void * @@ -369,35 +321,6 @@ // Default device for Mac and iPhone #define WEBRTC_VOICE_ENGINE_DEFAULT_DEVICE 0 - -// iPhone specific -#if defined(WEBRTC_IOS) - -// ---------------------------------------------------------------------------- -// Defines -// ---------------------------------------------------------------------------- - - // Always excluded for iPhone builds - #undef WEBRTC_CODEC_ISAC - #undef WEBRTC_VOE_EXTERNAL_REC_AND_PLAYOUT - - #define IPHONE_NOT_SUPPORTED(stat) NOT_SUPPORTED(stat) - -#else // Non-iPhone - -// ---------------------------------------------------------------------------- -// Enumerators -// ---------------------------------------------------------------------------- - -// ---------------------------------------------------------------------------- -// Defines -// ---------------------------------------------------------------------------- - - #define IPHONE_NOT_SUPPORTED(stat) -#endif - -#else -#define IPHONE_NOT_SUPPORTED(stat) #endif // #if defined(WEBRTC_BSD) || defined(WEBRTC_MAC) #endif // WEBRTC_VOICE_ENGINE_VOICE_ENGINE_DEFINES_H diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine.gyp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine.gyp 2015-02-03 14:33:38.000000000 +0000 @@ -20,6 +20,7 @@ '<(webrtc_root)/modules/modules.gyp:audio_conference_mixer', '<(webrtc_root)/modules/modules.gyp:audio_device', '<(webrtc_root)/modules/modules.gyp:audio_processing', + '<(webrtc_root)/modules/modules.gyp:bitrate_controller', '<(webrtc_root)/modules/modules.gyp:media_file', '<(webrtc_root)/modules/modules.gyp:rtp_rtcp', '<(webrtc_root)/modules/modules.gyp:webrtc_utility', @@ -34,10 +35,8 @@ '../typedefs.h', 'include/voe_audio_processing.h', 'include/voe_base.h', - 'include/voe_call_report.h', 'include/voe_codec.h', 'include/voe_dtmf.h', - 'include/voe_encryption.h', 'include/voe_errors.h', 'include/voe_external_media.h', 'include/voe_file.h', @@ -59,10 +58,10 @@ 'level_indicator.h', 'monitor_module.cc', 'monitor_module.h', + 'network_predictor.cc', + 'network_predictor.h', 'output_mixer.cc', 'output_mixer.h', - 'output_mixer_internal.cc', - 'output_mixer_internal.h', 'shared_data.cc', 'shared_data.h', 'statistics.cc', @@ -75,14 +74,10 @@ 'voe_audio_processing_impl.h', 'voe_base_impl.cc', 'voe_base_impl.h', - 'voe_call_report_impl.cc', - 'voe_call_report_impl.h', 'voe_codec_impl.cc', 'voe_codec_impl.h', 'voe_dtmf_impl.cc', 'voe_dtmf_impl.h', - 'voe_encryption_impl.cc', - 'voe_encryption_impl.h', 'voe_external_media_impl.cc', 'voe_external_media_impl.h', 'voe_file_impl.cc', @@ -132,17 +127,15 @@ ], 'sources': [ 'channel_unittest.cc', - 'output_mixer_unittest.cc', + 'network_predictor_unittest.cc', 'transmit_mixer_unittest.cc', + 'utility_unittest.cc', 'voe_audio_processing_unittest.cc', 'voe_base_unittest.cc', 'voe_codec_unittest.cc', - 'voe_neteq_stats_unittest.cc', ], 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are - # using Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['OS=="android"', { 'dependencies': [ '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code', ], @@ -158,7 +151,7 @@ '<(DEPTH)/testing/gtest.gyp:gtest', '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', - '<(webrtc_root)/test/libtest/libtest.gyp:libtest', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers_default', '<(webrtc_root)/test/test.gyp:channel_transport', '<(webrtc_root)/test/test.gyp:test_support', ], @@ -174,25 +167,22 @@ 'test/auto_test/fixtures/after_streaming_fixture.h', 'test/auto_test/fixtures/before_initialization_fixture.cc', 'test/auto_test/fixtures/before_initialization_fixture.h', - 'test/auto_test/fuzz/rtp_fuzz_test.cc', + 'test/auto_test/fixtures/before_streaming_fixture.cc', + 'test/auto_test/fixtures/before_streaming_fixture.h', 'test/auto_test/standard/audio_processing_test.cc', - 'test/auto_test/standard/call_report_test.cc', 'test/auto_test/standard/codec_before_streaming_test.cc', 'test/auto_test/standard/codec_test.cc', 'test/auto_test/standard/dtmf_test.cc', - 'test/auto_test/standard/encryption_test.cc', 'test/auto_test/standard/external_media_test.cc', 'test/auto_test/standard/file_before_streaming_test.cc', 'test/auto_test/standard/file_test.cc', 'test/auto_test/standard/hardware_before_initializing_test.cc', 'test/auto_test/standard/hardware_before_streaming_test.cc', 'test/auto_test/standard/hardware_test.cc', - 'test/auto_test/standard/manual_hold_test.cc', 'test/auto_test/standard/mixing_test.cc', 'test/auto_test/standard/neteq_stats_test.cc', - 'test/auto_test/standard/neteq_test.cc', - 'test/auto_test/standard/network_test.cc', 'test/auto_test/standard/rtp_rtcp_before_streaming_test.cc', + 'test/auto_test/standard/rtp_rtcp_extensions.cc', 'test/auto_test/standard/rtp_rtcp_test.cc', 'test/auto_test/standard/voe_base_misc_test.cc', 'test/auto_test/standard/video_sync_test.cc', @@ -206,8 +196,6 @@ 'test/auto_test/voe_stress_test.h', 'test/auto_test/voe_test_defines.h', 'test/auto_test/voe_test_interface.h', - 'test/auto_test/voe_unit_test.cc', - 'test/auto_test/voe_unit_test.h', ], 'conditions': [ ['OS=="android"', { @@ -231,6 +219,7 @@ '<(DEPTH)/testing/gtest.gyp:gtest', '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers_default', '<(webrtc_root)/test/test.gyp:channel_transport', '<(webrtc_root)/test/test.gyp:test_support', ], @@ -286,9 +275,7 @@ }, ], # targets }], - # TODO(henrike): remove build_with_chromium==1 when the bots are using - # Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['OS=="android"', { 'targets': [ { 'target_name': 'voice_engine_unittests_apk_target', @@ -309,7 +296,6 @@ ], 'includes': [ '../build/isolate.gypi', - 'voice_engine_unittests.isolate', ], 'sources': [ 'voice_engine_unittests.isolate', @@ -323,7 +309,6 @@ ], 'includes': [ '../build/isolate.gypi', - 'voe_auto_test.isolate', ], 'sources': [ 'voe_auto_test.isolate', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine_impl.cc thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine_impl.cc --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine_impl.cc 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine_impl.cc 2015-02-03 14:33:38.000000000 +0000 @@ -14,9 +14,11 @@ #include "webrtc/modules/audio_device/android/audio_record_jni.h" #include "webrtc/modules/audio_device/android/audio_track_jni.h" #endif +#if !defined(WEBRTC_CHROMIUM_BUILD) #include "webrtc/modules/audio_device/android/opensles_input.h" #include "webrtc/modules/audio_device/android/opensles_output.h" #endif +#endif #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" #include "webrtc/system_wrappers/interface/trace.h" @@ -72,6 +74,12 @@ "VoiceEngineImpl self deleting (voiceEngine=0x%p)", this); + // Clear any pointers before starting destruction. Otherwise worker- + // threads will still have pointers to a partially destructed object. + // Example: AudioDeviceBuffer::RequestPlayoutData() can access a + // partially deconstructed |_ptrCbAudioTransport| during destruction + // if we don't call Terminate here. + Terminate(); delete this; } @@ -80,8 +88,6 @@ VoiceEngine* VoiceEngine::Create() { Config* config = new Config(); - config->Set(new AudioCodingModuleFactory()); - return GetVoiceEngine(config, true); } @@ -147,6 +153,7 @@ return true; } +#if !defined(WEBRTC_CHROMIUM_BUILD) int VoiceEngine::SetAndroidObjects(void* javaVM, void* env, void* context) { #ifdef WEBRTC_ANDROID @@ -174,5 +181,6 @@ return -1; #endif } +#endif } // namespace webrtc diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine_impl.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine_impl.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine_impl.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine_impl.h 2015-02-03 14:33:38.000000000 +0000 @@ -18,18 +18,12 @@ #ifdef WEBRTC_VOICE_ENGINE_AUDIO_PROCESSING_API #include "webrtc/voice_engine/voe_audio_processing_impl.h" #endif -#ifdef WEBRTC_VOICE_ENGINE_CALL_REPORT_API -#include "webrtc/voice_engine/voe_call_report_impl.h" -#endif #ifdef WEBRTC_VOICE_ENGINE_CODEC_API #include "webrtc/voice_engine/voe_codec_impl.h" #endif #ifdef WEBRTC_VOICE_ENGINE_DTMF_API #include "webrtc/voice_engine/voe_dtmf_impl.h" #endif -#ifdef WEBRTC_VOICE_ENGINE_ENCRYPTION_API -#include "webrtc/voice_engine/voe_encryption_impl.h" -#endif #ifdef WEBRTC_VOICE_ENGINE_EXTERNAL_MEDIA_API #include "webrtc/voice_engine/voe_external_media_impl.h" #endif @@ -61,18 +55,12 @@ #ifdef WEBRTC_VOICE_ENGINE_AUDIO_PROCESSING_API public VoEAudioProcessingImpl, #endif -#ifdef WEBRTC_VOICE_ENGINE_CALL_REPORT_API - public VoECallReportImpl, -#endif #ifdef WEBRTC_VOICE_ENGINE_CODEC_API public VoECodecImpl, #endif #ifdef WEBRTC_VOICE_ENGINE_DTMF_API public VoEDtmfImpl, #endif -#ifdef WEBRTC_VOICE_ENGINE_ENCRYPTION_API - public VoEEncryptionImpl, -#endif #ifdef WEBRTC_VOICE_ENGINE_EXTERNAL_MEDIA_API public VoEExternalMediaImpl, #endif @@ -103,18 +91,12 @@ #ifdef WEBRTC_VOICE_ENGINE_AUDIO_PROCESSING_API VoEAudioProcessingImpl(this), #endif -#ifdef WEBRTC_VOICE_ENGINE_CALL_REPORT_API - VoECallReportImpl(this), -#endif #ifdef WEBRTC_VOICE_ENGINE_CODEC_API VoECodecImpl(this), #endif #ifdef WEBRTC_VOICE_ENGINE_DTMF_API VoEDtmfImpl(this), #endif -#ifdef WEBRTC_VOICE_ENGINE_ENCRYPTION_API - VoEEncryptionImpl(this), -#endif #ifdef WEBRTC_VOICE_ENGINE_EXTERNAL_MEDIA_API VoEExternalMediaImpl(this), #endif diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine_unittests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine_unittests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine_unittests.isolate 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/voice_engine/voice_engine_unittests.isolate 2015-02-03 14:33:38.000000000 +0000 @@ -8,28 +8,23 @@ { 'conditions': [ ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. 'variables': { - 'isolate_dependency_untracked': [ - '../../../data/', - '../../../resources/', + 'files': [ + '<(DEPTH)/data/', + '<(DEPTH)/resources/', ], }, }], ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../../testing/test_env.py', + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/voice_engine_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_tracked': [ - '../../testing/test_env.py', + 'files': [ + '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/voice_engine_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '../../tools/swarming_client/', - ], }, }], ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/webrtc_examples.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/webrtc_examples.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/webrtc_examples.gyp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/webrtc_examples.gyp 2015-02-03 14:33:38.000000000 +0000 @@ -7,6 +7,7 @@ # be found in the AUTHORS file in the root of the source tree. { 'includes': ['build/common.gypi'], + 'targets': [], 'conditions': [ ['OS=="android"', { 'targets': [ @@ -15,7 +16,9 @@ 'type': 'loadable_module', 'dependencies': [ '<(DEPTH)/third_party/icu/icu.gyp:icuuc', - '<(webrtc_root)/modules/modules.gyp:*', + '<(webrtc_root)/modules/modules.gyp:video_capture_module_internal_impl', + '<(webrtc_root)/modules/modules.gyp:video_render_module_internal_impl', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers_default', '<(webrtc_root)/test/test.gyp:channel_transport', '<(webrtc_root)/video_engine/video_engine.gyp:video_engine_core', '<(webrtc_root)/voice_engine/voice_engine.gyp:voice_engine', @@ -74,7 +77,8 @@ 'cp <(PRODUCT_DIR)/lib.java/video_render_module_java.jar <(android_webrtc_demo_root)/libs/ &&' '<(android_strip) -o <(android_webrtc_demo_root)/libs/<(android_app_abi)/libwebrtcdemo-jni.so <(PRODUCT_DIR)/libwebrtcdemo-jni.so && ' 'cd <(android_webrtc_demo_root) && ' - '{ ant debug > <(ant_log) 2>&1 || ' + '{ ANDROID_SDK_ROOT=<(android_sdk_root) ' + 'ant debug > <(ant_log) 2>&1 || ' ' { cat <(ant_log) ; exit 1; } } && ' 'cd - > /dev/null && ' 'cp <(android_webrtc_demo_root)/bin/WebRTCDemo-debug.apk <(_outputs)' @@ -129,13 +133,14 @@ 'outputs': ['<(PRODUCT_DIR)/OpenSlDemo-debug.apk'], 'action': [ 'bash', '-ec', - 'rm -f <(_outputs) && ' + 'rm -fr <(_outputs) <(android_opensl_demo_root)/{bin,libs} && ' 'mkdir -p <(android_opensl_demo_root)/libs/<(android_app_abi) && ' 'mkdir -p <(INTERMEDIATE_DIR) && ' # Must happen _before_ the cd below '<(android_strip) -o <(android_opensl_demo_root)/libs/<(android_app_abi)/libopensl-demo-jni.so <(PRODUCT_DIR)/libopensl-demo-jni.so && ' 'cp <(PRODUCT_DIR)/lib.java/audio_device_module_java.jar <(android_opensl_demo_root)/libs/ &&' 'cd <(android_opensl_demo_root) && ' - '{ ant debug > <(ant_log) 2>&1 || ' + '{ ANDROID_SDK_ROOT=<(android_sdk_root) ' + 'ant debug > <(ant_log) 2>&1 || ' ' { cat <(ant_log) ; exit 1; } } && ' 'cd - > /dev/null && ' 'cp <(android_opensl_demo_root)/bin/OpenSlDemo-debug.apk <(_outputs)' diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/webrtc.gyp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/webrtc.gyp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/webrtc.gyp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/webrtc.gyp 2015-02-03 14:33:38.000000000 +0000 @@ -9,6 +9,10 @@ 'conditions': [ ['include_tests==1', { 'includes': [ + 'libjingle/xmllite/xmllite_tests.gypi', + 'libjingle/xmpp/xmpp_tests.gypi', + 'p2p/p2p_tests.gypi', + 'sound/sound_tests.gypi', 'webrtc_tests.gypi', ], }], @@ -19,13 +23,20 @@ ], 'variables': { 'webrtc_all_dependencies': [ + 'base/base.gyp:*', + 'sound/sound.gyp:*', + 'common.gyp:*', 'common_audio/common_audio.gyp:*', 'common_video/common_video.gyp:*', + 'libjingle/xmllite/xmllite.gyp:*', + 'libjingle/xmpp/xmpp.gyp:*', 'modules/modules.gyp:*', + 'p2p/p2p.gyp:*', 'system_wrappers/source/system_wrappers.gyp:*', 'video_engine/video_engine.gyp:*', 'voice_engine/voice_engine.gyp:*', '<(webrtc_vp8_dir)/vp8.gyp:*', + '<(webrtc_vp9_dir)/vp9.gyp:*', ], }, 'targets': [ @@ -46,11 +57,7 @@ 'test/webrtc_test_common.gyp:webrtc_test_common_unittests', 'tools/tools.gyp:*', 'webrtc_tests', - ], - }], - ['build_with_chromium==0 and OS=="android"', { - 'dependencies': [ - '../tools/android/android_tools_precompiled.gyp:*', + 'rtc_unittests', ], }], ], @@ -73,8 +80,19 @@ '<@(webrtc_video_sources)', ], 'dependencies': [ + 'common.gyp:*', '<@(webrtc_video_dependencies)', ], + 'conditions': [ + # TODO(andresp): Chromium libpeerconnection should link directly with + # this and no if conditions should be needed on webrtc build files. + ['build_with_chromium==1', { + 'dependencies': [ + '<(webrtc_root)/modules/modules.gyp:video_capture_module_impl', + '<(webrtc_root)/modules/modules.gyp:video_render_module_impl', + ], + }], + ], }, ], } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/webrtc_perf_tests.isolate thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/webrtc_perf_tests.isolate --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/webrtc_perf_tests.isolate 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/webrtc_perf_tests.isolate 2015-02-03 14:33:38.000000000 +0000 @@ -8,32 +8,25 @@ { 'conditions': [ ['OS=="android"', { - # When doing Android builds, the WebRTC code is put in third_party/webrtc - # of a Chromium checkout, this is one level above the standalone build. 'variables': { - 'isolate_dependency_untracked': [ - '../../data/', - '../../resources/', + 'files': [ + '<(DEPTH)/data/', + '<(DEPTH)/resources/', ], }, }], ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../testing/test_env.py', '<(PRODUCT_DIR)/webrtc_perf_tests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_tracked': [ - '../DEPS', - '../resources/foreman_cif.yuv', - '../resources/paris_qcif.yuv', - '../resources/voice_engine/audio_long16.pcm', - '../testing/test_env.py', + 'files': [ + '<(DEPTH)/DEPS', + '<(DEPTH)/resources/foreman_cif.yuv', + '<(DEPTH)/resources/paris_qcif.yuv', + '<(DEPTH)/resources/voice_engine/audio_long16.pcm', '<(PRODUCT_DIR)/webrtc_perf_tests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '../tools/swarming_client/', - ], }, }], ], diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/webrtc_tests.gypi thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/webrtc_tests.gypi --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/trunk/webrtc/webrtc_tests.gypi 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/trunk/webrtc/webrtc_tests.gypi 2015-02-03 14:33:38.000000000 +0000 @@ -8,11 +8,30 @@ { 'targets': [ { + 'target_name': 'rtc_unittests', + 'type': 'executable', + 'dependencies': [ + 'base/base.gyp:rtc_base', + 'base/base_tests.gyp:rtc_base_tests_utils', + 'base/base_tests.gyp:rtc_base_tests', + 'libjingle/xmllite/xmllite.gyp:rtc_xmllite', + 'libjingle/xmpp/xmpp.gyp:rtc_xmpp', + 'p2p/p2p.gyp:rtc_p2p', + 'rtc_p2p_unittest', + 'rtc_sound_tests', + 'rtc_xmllite_unittest', + 'rtc_xmpp_unittest', + 'sound/sound.gyp:rtc_sound', + '<(DEPTH)/testing/gtest.gyp:gtest', + ], + }, + { 'target_name': 'webrtc_tests', 'type': 'none', 'dependencies': [ 'video_engine_tests', 'video_loopback', + 'video_replay', 'webrtc_perf_tests', ], }, @@ -20,12 +39,54 @@ 'target_name': 'video_loopback', 'type': 'executable', 'sources': [ + 'test/mac/run_test.mm', + 'test/run_test.cc', + 'test/run_test.h', 'video/loopback.cc', - 'test/test_main.cc', + ], + 'conditions': [ + ['OS=="mac"', { + 'sources!': [ + 'test/run_test.cc', + ], + }], ], 'dependencies': [ '<(DEPTH)/testing/gtest.gyp:gtest', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + 'test/webrtc_test_common.gyp:webrtc_test_common', + 'test/webrtc_test_common.gyp:webrtc_test_renderer', + '<(webrtc_root)/modules/modules.gyp:video_capture_module_internal_impl', + '<(webrtc_root)/modules/modules.gyp:video_render_module_impl', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers_default', + 'test/test.gyp:test_main', + 'webrtc', + ], + }, + { + 'target_name': 'video_replay', + 'type': 'executable', + 'sources': [ + 'test/mac/run_test.mm', + 'test/run_test.cc', + 'test/run_test.h', + 'video/replay.cc', + ], + 'conditions': [ + ['OS=="mac"', { + 'sources!': [ + 'test/run_test.cc', + ], + }], + ], + 'dependencies': [ + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', 'test/webrtc_test_common.gyp:webrtc_test_common', + 'test/webrtc_test_common.gyp:webrtc_test_renderer', + '<(webrtc_root)/modules/modules.gyp:video_capture_module_impl', + '<(webrtc_root)/modules/modules.gyp:video_render_module_impl', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers_default', 'webrtc', ], }, @@ -34,46 +95,60 @@ 'type': '<(gtest_target_type)', 'sources': [ 'video/bitrate_estimator_tests.cc', - 'video/call_tests.cc', + 'video/end_to_end_tests.cc', 'video/send_statistics_proxy_unittest.cc', 'video/video_send_stream_tests.cc', 'test/common_unittest.cc', 'test/testsupport/metrics/video_metrics_unittest.cc', - 'test/test_main.cc', ], 'dependencies': [ '<(DEPTH)/testing/gtest.gyp:gtest', - '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', - 'modules/modules.gyp:rtp_rtcp', + '<(webrtc_root)/modules/modules.gyp:rtp_rtcp', + '<(webrtc_root)/modules/modules.gyp:video_capture_module_impl', + '<(webrtc_root)/modules/modules.gyp:video_render_module_impl', 'test/metrics.gyp:metrics', 'test/webrtc_test_common.gyp:webrtc_test_common', + 'test/test.gyp:test_main', 'webrtc', ], + 'conditions': [ + ['OS=="android"', { + 'dependencies': [ + '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code', + ], + }], + ], }, { 'target_name': 'webrtc_perf_tests', 'type': '<(gtest_target_type)', 'sources': [ - 'modules/audio_coding/neteq4/test/neteq_performance_unittest.cc', - 'test/test_main.cc', + 'modules/audio_coding/neteq/test/neteq_performance_unittest.cc', 'video/call_perf_tests.cc', 'video/full_stack.cc', 'video/rampup_tests.cc', + 'video/rampup_tests.h', ], 'dependencies': [ '<(DEPTH)/testing/gtest.gyp:gtest', - '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', - 'modules/modules.gyp:neteq_unittest_tools', # Needed by neteq_performance_unittest. + 'modules/modules.gyp:neteq_test_support', # Needed by neteq_performance_unittest. 'modules/modules.gyp:rtp_rtcp', + '<(webrtc_root)/modules/modules.gyp:video_capture_module_impl', 'test/webrtc_test_common.gyp:webrtc_test_common', + 'test/test.gyp:test_main', 'webrtc', ], + 'conditions': [ + ['OS=="android"', { + 'dependencies': [ + '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code', + ], + }], + ], }, ], 'conditions': [ - # TODO(henrike): remove build_with_chromium==1 when the bots are using - # Chromium's buildbots. - ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', { + ['OS=="android"', { 'targets': [ { 'target_name': 'video_engine_tests_apk_target', @@ -94,6 +169,19 @@ ['test_isolation_mode != "noop"', { 'targets': [ { + 'target_name': 'rtc_unittests_run', + 'type': 'none', + 'dependencies': [ + 'rtc_unittests', + ], + 'includes': [ + 'build/isolate.gypi', + ], + 'sources': [ + 'rtc_unittests.isolate', + ], + }, + { 'target_name': 'video_engine_tests_run', 'type': 'none', 'dependencies': [ @@ -101,7 +189,6 @@ ], 'includes': [ 'build/isolate.gypi', - 'video_engine_tests.isolate', ], 'sources': [ 'video_engine_tests.isolate', @@ -115,7 +202,6 @@ ], 'includes': [ 'build/isolate.gypi', - 'webrtc_perf_tests.isolate', ], 'sources': [ 'webrtc_perf_tests.isolate', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/webrtc_version.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/webrtc_version.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/media/webrtc/webrtc_version.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/media/webrtc/webrtc_version.h 2015-02-03 14:33:38.000000000 +0000 @@ -1,3 +1,3 @@ -#define WEBRTC_SVNVERSION 4180 -#define WEBRTC_PULL_DATE "Wed Jan 05 04:11:00 EDT 2013" +#define WEBRTC_SVNVERSION 7864 +#define WEBRTC_PULL_DATE "Wed Dec 10 12:23:33 EST 2014" diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/memory/fallible/fallible.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/memory/fallible/fallible.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/memory/fallible/fallible.cpp 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/memory/fallible/fallible.cpp 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,11 @@ +/* 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 "fallible.h" + +namespace mozilla { + +const fallible_t fallible = {}; + +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/memory/fallible/fallible.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/memory/fallible/fallible.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/memory/fallible/fallible.h 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/memory/fallible/fallible.h 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,67 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_fallible_h +#define mozilla_fallible_h + +#if defined(__cplusplus) + +/* Explicit fallible allocation + * + * Memory allocation (normally) defaults to abort in case of failed + * allocation. That is, it never returns NULL, and crashes instead. + * + * Code can explicitely request for fallible memory allocation thanks + * to the declarations below. + * + * The typical use of the mozilla::fallible const is with placement new, + * like the following: + * + * foo = new (mozilla::fallible) Foo(); + * + * The following forms, or derivatives, are also possible but deprecated: + * + * foo = new ((mozilla::fallible_t())) Foo(); + * + * const mozilla::fallible_t fallible = mozilla::fallible_t(); + * bar = new (f) Bar(); + * + * It is also possible to declare method overloads with fallible allocation + * alternatives, like so: + * + * class Foo { + * public: + * void Method(void *); + * void Method(void *, const mozilla::fallible_t&); + * }; + * + * Foo foo; + * foo.Method(nullptr, mozilla::fallible); + * + * If that last method call is in a method that itself takes a const + * fallible_t& argument, it is recommended to propagate that argument + * instead of using mozilla::fallible: + * + * void Func(Foo &foo, const mozilla::fallible_t& aFallible) { + * foo.Method(nullptr, aFallible); + * } + * + */ +namespace mozilla { + +struct fallible_t { }; + +/* This symbol is kept unexported, such that in corner cases where the + * compiler can't remove its use (essentially, cross compilation-unit + * calls), the smallest machine code is used. + * Depending how the linker packs symbols, it will consume between 1 and + * 8 bytes of read-only data in each executable or shared library, but + * only in those where it's actually not optimized out by the compiler. + */ +extern const fallible_t fallible; + +} // namespace mozilla +#endif + +#endif // mozilla_fallible_h diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/memory/fallible/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/memory/fallible/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/memory/fallible/moz.build 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/memory/fallible/moz.build 2015-02-03 14:33:38.000000000 +0000 @@ -0,0 +1,30 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS.mozilla += [ + 'fallible.h', +] + +Library('fallible') + +SOURCES += [ + 'fallible.cpp', +] + +if CONFIG['_MSC_VER']: + # MSVC normally adds linker directives relative to the CRT in a .drectve + # section in .obj files. Then, when linking objects, it adds those + # directives as if they were given as command line arguments. This can + # lead to trying to include link CRTs because different objects are + # compiled with different CRT options (i.e. -MT vs. -MD), and failing. + # The only source in this directory doesn't expose anything that depends + # on a CRT, so it doesn't need to be bound to a specific one. + # Adding the -Zl option makes MSVC not store linker directives in the + # object. This allows to link fallible.obj to binaries independently of + # the CRT they use. + CXXFLAGS += [ + '-Zl', + ] diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/memory/mozalloc/fallible.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/memory/mozalloc/fallible.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/memory/mozalloc/fallible.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/memory/mozalloc/fallible.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_fallible_h -#define mozilla_fallible_h - -namespace mozilla { - -struct fallible_t { }; - -} // namespace mozilla - -#endif // mozilla_fallible_h diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/memory/mozalloc/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/memory/mozalloc/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/memory/mozalloc/moz.build 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/memory/mozalloc/moz.build 2015-02-03 14:33:38.000000000 +0000 @@ -6,7 +6,6 @@ NO_VISIBILITY_FLAGS = True EXPORTS.mozilla += [ - 'fallible.h', 'mozalloc.h', 'mozalloc_abort.h', 'mozalloc_oom.h', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mfbt/Assertions.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mfbt/Assertions.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mfbt/Assertions.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mfbt/Assertions.h 2015-02-03 14:33:38.000000000 +0000 @@ -131,7 +131,7 @@ * method is primarily for internal use in this header, and only secondarily * for use in implementing release-build assertions. */ -static MOZ_ALWAYS_INLINE void +static MOZ_COLD MOZ_ALWAYS_INLINE void MOZ_ReportAssertionFailure(const char* aStr, const char* aFilename, int aLine) MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS { @@ -148,7 +148,7 @@ #endif } -static MOZ_ALWAYS_INLINE void +static MOZ_COLD MOZ_ALWAYS_INLINE void MOZ_ReportCrash(const char* aStr, const char* aFilename, int aLine) MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mfbt/Attributes.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mfbt/Attributes.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mfbt/Attributes.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mfbt/Attributes.h 2015-02-03 14:33:38.000000000 +0000 @@ -191,6 +191,27 @@ # define MOZ_NORETURN /* no support */ #endif +/** + * MOZ_COLD tells the compiler that a function is "cold", meaning infrequently + * executed. This may lead it to optimize for size more aggressively than speed, + * or to allocate the body of the function in a distant part of the text segment + * to help keep it from taking up unnecessary icache when it isn't in use. + * + * Place this attribute at the very beginning of a function definition. For + * example, write + * + * MOZ_COLD int foo(); + * + * or + * + * MOZ_COLD int foo() { return 42; } + */ +#if defined(__GNUC__) || defined(__clang__) +# define MOZ_COLD __attribute__ ((cold)) +#else +# define MOZ_COLD +#endif + /* * MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS, specified at the end of a function * declaration, indicates that for the purposes of static analysis, this diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mfbt/EnumeratedArray.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mfbt/EnumeratedArray.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mfbt/EnumeratedArray.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mfbt/EnumeratedArray.h 2015-02-03 14:33:38.000000000 +0000 @@ -10,14 +10,12 @@ #define mozilla_EnumeratedArray_h #include "mozilla/Array.h" -#include "mozilla/TypedEnum.h" namespace mozilla { /** * EnumeratedArray is a fixed-size array container for use when an - * array is indexed by a specific enum class, as currently implemented - * by MOZ_BEGIN_ENUM_CLASS. + * array is indexed by a specific enum class. * * This provides type safety by guarding at compile time against accidentally * indexing such arrays with unrelated values. This also removes the need @@ -27,11 +25,11 @@ * * Example: * - * MOZ_BEGIN_ENUM_CLASS(AnimalSpecies) + * enum class AnimalSpecies { * Cow, * Sheep, * Count - * MOZ_END_ENUM_CLASS(AnimalSpecies) + * }; * * EnumeratedArray headCount; * @@ -40,7 +38,7 @@ * */ template class EnumeratedArray { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mfbt/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mfbt/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mfbt/moz.build 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mfbt/moz.build 2015-02-03 14:33:39.000000000 +0000 @@ -69,9 +69,7 @@ 'TemplateLib.h', 'ThreadLocal.h', 'ToString.h', - 'TypedEnum.h', 'TypedEnumBits.h', - 'TypedEnumInternal.h', 'Types.h', 'TypeTraits.h', 'UniquePtr.h', diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mfbt/tests/TestTypedEnum.cpp thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mfbt/tests/TestTypedEnum.cpp --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mfbt/tests/TestTypedEnum.cpp 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mfbt/tests/TestTypedEnum.cpp 2015-02-03 14:33:39.000000000 +0000 @@ -6,7 +6,6 @@ #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" -#include "mozilla/TypedEnum.h" #include "mozilla/TypedEnumBits.h" #include @@ -55,55 +54,55 @@ RequireLiteralType(); } -MOZ_BEGIN_ENUM_CLASS(AutoEnum) +enum class AutoEnum { A, B = -3, C -MOZ_END_ENUM_CLASS(AutoEnum) +}; -MOZ_BEGIN_ENUM_CLASS(CharEnum, char) +enum class CharEnum : char { A, B = 3, C -MOZ_END_ENUM_CLASS(CharEnum) +}; -MOZ_BEGIN_ENUM_CLASS(AutoEnumBitField) +enum class AutoEnumBitField { A = 0x10, B = 0x20, C -MOZ_END_ENUM_CLASS(AutoEnumBitField) +}; -MOZ_BEGIN_ENUM_CLASS(CharEnumBitField, char) +enum class CharEnumBitField : char { A = 0x10, B, C = 0x40 -MOZ_END_ENUM_CLASS(CharEnumBitField) +}; struct Nested { - MOZ_BEGIN_NESTED_ENUM_CLASS(AutoEnum) + enum class AutoEnum { A, B, C = -1 - MOZ_END_NESTED_ENUM_CLASS(AutoEnum) + }; - MOZ_BEGIN_NESTED_ENUM_CLASS(CharEnum, char) + enum class CharEnum : char { A = 4, B, C = 1 - MOZ_END_NESTED_ENUM_CLASS(CharEnum) + }; - MOZ_BEGIN_NESTED_ENUM_CLASS(AutoEnumBitField) + enum class AutoEnumBitField { A, B = 0x20, C - MOZ_END_NESTED_ENUM_CLASS(AutoEnumBitField) + }; - MOZ_BEGIN_NESTED_ENUM_CLASS(CharEnumBitField, char) + enum class CharEnumBitField : char { A = 1, B = 1, C = 1 - MOZ_END_NESTED_ENUM_CLASS(CharEnumBitField) + }; }; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AutoEnumBitField) @@ -112,11 +111,11 @@ MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Nested::CharEnumBitField) #define MAKE_STANDARD_BITFIELD_FOR_TYPE(IntType) \ - MOZ_BEGIN_ENUM_CLASS(BitFieldFor_##IntType, IntType) \ + enum class BitFieldFor_##IntType : IntType { \ A = 1, \ B = 2, \ C = 4, \ - MOZ_END_ENUM_CLASS(BitFieldFor_##IntType) \ + }; \ MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(BitFieldFor_##IntType) MAKE_STANDARD_BITFIELD_FOR_TYPE(int8_t) @@ -154,7 +153,7 @@ { using mozilla::IsConvertible; -#if defined(MOZ_HAVE_CXX11_STRONG_ENUMS) && defined(MOZ_HAVE_EXPLICIT_CONVERSION) +#if defined(MOZ_HAVE_EXPLICIT_CONVERSION) static_assert(!IsConvertible::value, "should not be convertible"); static_assert(!IsConvertible::value, "should not be convertible"); static_assert(!IsConvertible::value, "should not be convertible"); @@ -443,52 +442,52 @@ #endif } -MOZ_BEGIN_ENUM_CLASS(Int8EnumWithHighBits, int8_t) +enum class Int8EnumWithHighBits : int8_t { A = 0x20, B = 0x40 -MOZ_END_ENUM_CLASS(Int8EnumWithHighBits) +}; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int8EnumWithHighBits) -MOZ_BEGIN_ENUM_CLASS(Uint8EnumWithHighBits, uint8_t) +enum class Uint8EnumWithHighBits : uint8_t { A = 0x40, B = 0x80 -MOZ_END_ENUM_CLASS(Uint8EnumWithHighBits) +}; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint8EnumWithHighBits) -MOZ_BEGIN_ENUM_CLASS(Int16EnumWithHighBits, int16_t) +enum class Int16EnumWithHighBits : int16_t { A = 0x2000, B = 0x4000 -MOZ_END_ENUM_CLASS(Int16EnumWithHighBits) +}; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int16EnumWithHighBits) -MOZ_BEGIN_ENUM_CLASS(Uint16EnumWithHighBits, uint16_t) +enum class Uint16EnumWithHighBits : uint16_t { A = 0x4000, B = 0x8000 -MOZ_END_ENUM_CLASS(Uint16EnumWithHighBits) +}; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint16EnumWithHighBits) -MOZ_BEGIN_ENUM_CLASS(Int32EnumWithHighBits, int32_t) +enum class Int32EnumWithHighBits : int32_t { A = 0x20000000, B = 0x40000000 -MOZ_END_ENUM_CLASS(Int32EnumWithHighBits) +}; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int32EnumWithHighBits) -MOZ_BEGIN_ENUM_CLASS(Uint32EnumWithHighBits, uint32_t) +enum class Uint32EnumWithHighBits : uint32_t { A = 0x40000000u, B = 0x80000000u -MOZ_END_ENUM_CLASS(Uint32EnumWithHighBits) +}; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint32EnumWithHighBits) -MOZ_BEGIN_ENUM_CLASS(Int64EnumWithHighBits, int64_t) +enum class Int64EnumWithHighBits : int64_t { A = 0x2000000000000000ll, B = 0x4000000000000000ll -MOZ_END_ENUM_CLASS(Int64EnumWithHighBits) +}; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int64EnumWithHighBits) -MOZ_BEGIN_ENUM_CLASS(Uint64EnumWithHighBits, uint64_t) +enum class Uint64EnumWithHighBits : uint64_t { A = 0x4000000000000000ull, B = 0x8000000000000000ull -MOZ_END_ENUM_CLASS(Uint64EnumWithHighBits) +}; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint64EnumWithHighBits) // Checks that we don't accidentally truncate high bits by coercing to the wrong diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mfbt/TypedEnumBits.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mfbt/TypedEnumBits.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mfbt/TypedEnumBits.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mfbt/TypedEnumBits.h 2015-02-03 14:33:39.000000000 +0000 @@ -11,11 +11,55 @@ #ifndef mozilla_TypedEnumBits_h #define mozilla_TypedEnumBits_h +#include "mozilla/Attributes.h" #include "mozilla/IntegerTypeTraits.h" -#include "mozilla/TypedEnumInternal.h" namespace mozilla { +/* + * The problem that CastableTypedEnumResult aims to solve is that + * typed enums are not convertible to bool, and there is no way to make them + * be, yet user code wants to be able to write + * + * if (myFlags & Flags::SOME_PARTICULAR_FLAG) (1) + * + * There are different approaches to solving this. Most of them require + * adapting user code. For example, we could implement operator! and have + * the user write + * + * if (!!(myFlags & Flags::SOME_PARTICULAR_FLAG)) (2) + * + * Or we could supply a IsNonZero() or Any() function returning whether + * an enum value is nonzero, and have the user write + * + * if (Any(Flags & Flags::SOME_PARTICULAR_FLAG)) (3) + * + * But instead, we choose to preserve the original user syntax (1) as it + * is inherently more readable, and to ease porting existing code to typed + * enums. We achieve this by having operator& and other binary bitwise + * operators have as return type a class, CastableTypedEnumResult, + * that wraps a typed enum but adds bool convertibility. + */ +template +class CastableTypedEnumResult +{ +private: + const E mValue; + +public: + explicit MOZ_CONSTEXPR CastableTypedEnumResult(E aValue) + : mValue(aValue) + {} + + MOZ_CONSTEXPR operator E() const { return mValue; } + + template + MOZ_EXPLICIT_CONVERSION MOZ_CONSTEXPR + operator DestinationType() const { return DestinationType(mValue); } + + MOZ_CONSTEXPR bool operator !() const { return !bool(mValue); } +}; + #define MOZ_CASTABLETYPEDENUMRESULT_BINOP(Op, OtherType, ReturnType) \ template \ MOZ_CONSTEXPR ReturnType \ @@ -69,32 +113,6 @@ #undef MOZ_CASTABLETYPEDENUMRESULT_BINOP -#ifndef MOZ_HAVE_CXX11_STRONG_ENUMS - -#define MOZ_CASTABLETYPEDENUMRESULT_BINOP_EXTRA_NON_CXX11(Op, ReturnType) \ -template \ -MOZ_CONSTEXPR ReturnType \ -operator Op(typename E::Enum aE, const CastableTypedEnumResult& aR) \ -{ \ - return ReturnType(aE Op E(aR)); \ -} \ -template \ -MOZ_CONSTEXPR ReturnType \ -operator Op(const CastableTypedEnumResult& aR, typename E::Enum aE) \ -{ \ - return ReturnType(E(aR) Op aE); \ -} - -MOZ_CASTABLETYPEDENUMRESULT_BINOP_EXTRA_NON_CXX11(|, CastableTypedEnumResult) -MOZ_CASTABLETYPEDENUMRESULT_BINOP_EXTRA_NON_CXX11(&, CastableTypedEnumResult) -MOZ_CASTABLETYPEDENUMRESULT_BINOP_EXTRA_NON_CXX11(^, CastableTypedEnumResult) -MOZ_CASTABLETYPEDENUMRESULT_BINOP_EXTRA_NON_CXX11(==, bool) -MOZ_CASTABLETYPEDENUMRESULT_BINOP_EXTRA_NON_CXX11(!=, bool) - -#undef MOZ_CASTABLETYPEDENUMRESULT_BINOP_EXTRA_NON_CXX11 - -#endif // not MOZ_HAVE_CXX11_STRONG_ENUMS - namespace detail { template struct UnsignedIntegerTypeForEnum @@ -119,7 +137,11 @@ return a = a Op b; \ } -#define MOZ_MAKE_ENUM_CLASS_OPS_IMPL(Name) \ +/** + * MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS generates standard bitwise operators + * for the given enum type. Use this to enable using an enum type as bit-field. + */ +#define MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Name) \ MOZ_MAKE_ENUM_CLASS_BINOP_IMPL(Name, |) \ MOZ_MAKE_ENUM_CLASS_BINOP_IMPL(Name, &) \ MOZ_MAKE_ENUM_CLASS_BINOP_IMPL(Name, ^) \ @@ -131,54 +153,4 @@ return Result(Name(~(U(a)))); \ } -#ifndef MOZ_HAVE_CXX11_STRONG_ENUMS -# define MOZ_MAKE_ENUM_CLASS_BITWISE_BINOP_EXTRA_NON_CXX11(Name, Op) \ - inline MOZ_CONSTEXPR mozilla::CastableTypedEnumResult \ - operator Op(Name a, Name::Enum b) \ - { \ - return a Op Name(b); \ - } \ - \ - inline MOZ_CONSTEXPR mozilla::CastableTypedEnumResult \ - operator Op(Name::Enum a, Name b) \ - { \ - return Name(a) Op b; \ - } \ - \ - inline MOZ_CONSTEXPR mozilla::CastableTypedEnumResult \ - operator Op(Name::Enum a, Name::Enum b) \ - { \ - return Name(a) Op Name(b); \ - } \ - \ - inline Name& \ - operator Op##=(Name& a, Name::Enum b) \ - { \ - return a = a Op Name(b); \ - } - -# define MOZ_MAKE_ENUM_CLASS_OPS_EXTRA_NON_CXX11(Name) \ - MOZ_MAKE_ENUM_CLASS_BITWISE_BINOP_EXTRA_NON_CXX11(Name, |) \ - MOZ_MAKE_ENUM_CLASS_BITWISE_BINOP_EXTRA_NON_CXX11(Name, &) \ - MOZ_MAKE_ENUM_CLASS_BITWISE_BINOP_EXTRA_NON_CXX11(Name, ^) \ - inline MOZ_CONSTEXPR mozilla::CastableTypedEnumResult \ - operator~(Name::Enum a) \ - { \ - return ~(Name(a)); \ - } -#endif - -/** - * MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS generates standard bitwise operators - * for the given enum type. Use this to enable using an enum type as bit-field. - */ -#ifdef MOZ_HAVE_CXX11_STRONG_ENUMS -# define MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Name) \ - MOZ_MAKE_ENUM_CLASS_OPS_IMPL(Name) -#else -# define MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Name) \ - MOZ_MAKE_ENUM_CLASS_OPS_IMPL(Name) \ - MOZ_MAKE_ENUM_CLASS_OPS_EXTRA_NON_CXX11(Name) -#endif - #endif // mozilla_TypedEnumBits_h diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mfbt/TypedEnum.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mfbt/TypedEnum.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mfbt/TypedEnum.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mfbt/TypedEnum.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,256 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* Macros to emulate C++11 typed enums and enum classes. */ - -#ifndef mozilla_TypedEnum_h -#define mozilla_TypedEnum_h - -#include "mozilla/TypedEnumInternal.h" -#include "mozilla/MacroArgs.h" - -#if defined(__cplusplus) - -/** - * MOZ_BEGIN_ENUM_CLASS and MOZ_END_ENUM_CLASS provide access to the - * strongly-typed enumeration feature of C++11 ("enum class"). If supported - * by the compiler, an enum defined using these macros will not be implicitly - * converted to any other type, and its enumerators will be scoped using the - * enumeration name. Place MOZ_BEGIN_ENUM_CLASS(EnumName [, type]) in place of - * "enum EnumName {", and MOZ_END_ENUM_CLASS(EnumName) in place of the closing - * "};". For example, - * - * MOZ_BEGIN_ENUM_CLASS(Enum, int32_t) - * A, - * B = 6 - * MOZ_END_ENUM_CLASS(Enum) - * - * This will make "Enum::A" and "Enum::B" appear in the global scope, but "A" - * and "B" will not. In compilers that support C++11 strongly-typed - * enumerations, implicit conversions of Enum values to numeric types will - * fail. In other compilers, Enum itself will actually be defined as a class, - * and some implicit conversions will fail while others will succeed. - * - * The optional type argument specifies the underlying type for the enum where - * supported, as with MOZ_ENUM_TYPE(). As with MOZ_ENUM_TYPE(), it will do - * nothing on compilers that do not support it. - * - * MOZ_{BEGIN,END}_ENUM_CLASS doesn't work for defining enum classes nested - * inside classes. To define an enum class nested inside another class, use - * MOZ_{BEGIN,END}_NESTED_ENUM_CLASS, and place a MOZ_FINISH_NESTED_ENUM_CLASS - * in namespace scope to handle bits that can only be implemented with - * namespace-scoped code. For example: - * - * class FooBar - * { - * MOZ_BEGIN_NESTED_ENUM_CLASS(Enum, int32_t) - * A, - * B = 6 - * MOZ_END_NESTED_ENUM_CLASS(Enum) - * }; - * - * MOZ_FINISH_NESTED_ENUM_CLASS(FooBar::Enum) - */ -#if defined(MOZ_HAVE_CXX11_STRONG_ENUMS) - /* - * All compilers that support strong enums also support an explicit - * underlying type, so no extra check is needed. - */ - - /* Single-argument form. */ -# define MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER1(Name) \ - enum class Name { - /* Two-argument form. */ -# define MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER2(Name, type) \ - enum class Name : type { -# define MOZ_END_NESTED_ENUM_CLASS(Name) \ - }; -# define MOZ_FINISH_NESTED_ENUM_CLASS(Name) /* nothing */ - - /* - * MOZ_ENUM_CLASS_ENUM_TYPE allows using enum classes - * as template parameter types. For that, we need integer types. - * In the present case where the compiler supports strong enums, - * these are already integer types so there is nothing more to do. - */ -# define MOZ_ENUM_CLASS_ENUM_TYPE(Name) Name - /* - * See the comment below about MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE. - */ -# define MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(Name) Name -#else - /** - * We need Name to both name a type, and scope the provided enumerator - * names. Namespaces and classes both provide scoping, but namespaces - * aren't types, so we need to use a class that wraps the enum values. We - * have an implicit conversion from the inner enum type to the class, so - * statements like - * - * Enum x = Enum::A; - * - * will still work. We need to define an implicit conversion from the class - * to the inner enum as well, so that (for instance) switch statements will - * work. This means that the class can be implicitly converted to a numeric - * value as well via the enum type, since C++ allows an implicit - * user-defined conversion followed by a standard conversion to still be - * implicit. - * - * We have an explicit constructor from int defined, so that casts like - * (Enum)7 will still work. We also have a zero-argument constructor with - * no arguments, so declaration without initialization (like "Enum foo;") - * will work. - * - * Additionally, we'll delete as many operators as possible for the inner - * enum type, so statements like this will still fail: - * - * f(5 + Enum::B); // deleted operator+ - * - * But we can't prevent things like this, because C++ doesn't allow - * overriding conversions or assignment operators for enums: - * - * int x = Enum::A; - * int f() - * { - * return Enum::A; - * } - */ - - /* Single-argument form. */ -# define MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER1(Name) \ - class Name \ - { \ - public: \ - enum Enum \ - { - /* Two-argument form. */ -# define MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER2(Name, type) \ - class Name \ - { \ - public: \ - enum Enum : type \ - { -# define MOZ_END_NESTED_ENUM_CLASS(Name) \ - }; \ - Name() {} \ - MOZ_CONSTEXPR Name(Enum aEnum) : mEnum(aEnum) {} \ - template \ - explicit MOZ_CONSTEXPR Name(Other num) : mEnum((Enum)num) {} \ - MOZ_CONSTEXPR operator Enum() const { return mEnum; } \ - explicit MOZ_CONSTEXPR Name(const mozilla::CastableTypedEnumResult& aOther) \ - : mEnum(aOther.get()) \ - {} \ - private: \ - Enum mEnum; \ - }; -# define MOZ_FINISH_NESTED_ENUM_CLASS(Name) \ - inline int operator+(const int&, const Name::Enum&) = delete; \ - inline int operator+(const Name::Enum&, const int&) = delete; \ - inline int operator-(const int&, const Name::Enum&) = delete; \ - inline int operator-(const Name::Enum&, const int&) = delete; \ - inline int operator*(const int&, const Name::Enum&) = delete; \ - inline int operator*(const Name::Enum&, const int&) = delete; \ - inline int operator/(const int&, const Name::Enum&) = delete; \ - inline int operator/(const Name::Enum&, const int&) = delete; \ - inline int operator%(const int&, const Name::Enum&) = delete; \ - inline int operator%(const Name::Enum&, const int&) = delete; \ - inline int operator+(const Name::Enum&) = delete; \ - inline int operator-(const Name::Enum&) = delete; \ - inline int& operator++(Name::Enum&) = delete; \ - inline int operator++(Name::Enum&, int) = delete; \ - inline int& operator--(Name::Enum&) = delete; \ - inline int operator--(Name::Enum&, int) = delete; \ - inline bool operator==(const int&, const Name::Enum&) = delete; \ - inline bool operator==(const Name::Enum&, const int&) = delete; \ - inline bool operator!=(const int&, const Name::Enum&) = delete; \ - inline bool operator!=(const Name::Enum&, const int&) = delete; \ - inline bool operator>(const int&, const Name::Enum&) = delete; \ - inline bool operator>(const Name::Enum&, const int&) = delete; \ - inline bool operator<(const int&, const Name::Enum&) = delete; \ - inline bool operator<(const Name::Enum&, const int&) = delete; \ - inline bool operator>=(const int&, const Name::Enum&) = delete; \ - inline bool operator>=(const Name::Enum&, const int&) = delete; \ - inline bool operator<=(const int&, const Name::Enum&) = delete; \ - inline bool operator<=(const Name::Enum&, const int&) = delete; \ - inline bool operator!(const Name::Enum&) = delete; \ - inline bool operator&&(const bool&, const Name::Enum&) = delete; \ - inline bool operator&&(const Name::Enum&, const bool&) = delete; \ - inline bool operator||(const bool&, const Name::Enum&) = delete; \ - inline bool operator||(const Name::Enum&, const bool&) = delete; \ - inline int operator&(const int&, const Name::Enum&) = delete; \ - inline int operator&(const Name::Enum&, const int&) = delete; \ - inline int operator|(const int&, const Name::Enum&) = delete; \ - inline int operator|(const Name::Enum&, const int&) = delete; \ - inline int operator^(const int&, const Name::Enum&) = delete; \ - inline int operator^(const Name::Enum&, const int&) = delete; \ - inline int operator<<(const int&, const Name::Enum&) = delete; \ - inline int operator<<(const Name::Enum&, const int&) = delete; \ - inline int operator>>(const int&, const Name::Enum&) = delete; \ - inline int operator>>(const Name::Enum&, const int&) = delete; \ - inline int& operator+=(int&, const Name::Enum&) = delete; \ - inline int& operator-=(int&, const Name::Enum&) = delete; \ - inline int& operator*=(int&, const Name::Enum&) = delete; \ - inline int& operator/=(int&, const Name::Enum&) = delete; \ - inline int& operator%=(int&, const Name::Enum&) = delete; \ - inline int& operator&=(int&, const Name::Enum&) = delete; \ - inline int& operator|=(int&, const Name::Enum&) = delete; \ - inline int& operator^=(int&, const Name::Enum&) = delete; \ - inline int& operator<<=(int&, const Name::Enum&) = delete; \ - inline int& operator>>=(int&, const Name::Enum&) = delete; - - /* - * MOZ_ENUM_CLASS_ENUM_TYPE allows using enum classes - * as template parameter types. For that, we need integer types. - * In the present case, the integer type is the Enum nested type. - */ -# define MOZ_ENUM_CLASS_ENUM_TYPE(Name) Name::Enum - /* - * MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE is a variant of MOZ_ENUM_CLASS_ENUM_TYPE - * to be used when the enum class at hand depends on template parameters. - * - * Indeed, if T depends on template parameters, in order to name a nested type - * in T, C++ does not allow to just write "T::NestedType". Instead, we have - * to write "typename T::NestedType". The role of this macro is to add - * this "typename" keywords where needed. - * - * Example: - * - * template - * struct S {}; - * - * MOZ_BEGIN_ENUM_CLASS(E) - * Foo, - * Bar - * MOZ_END_ENUM_CLASS(E) - * - * S s; - * - * In this example, the second template parameter to S is meant to be of type - * T, but on non-C++11 compilers, type T is a class type, not an integer - * type, so it is not accepted as the type of a constant template parameter. - * One would then want to use MOZ_ENUM_CLASS_ENUM_TYPE(T), but that doesn't - * work either as T depends on template parameters (more specifically here, T - * _is_ a template parameter) so as MOZ_ENUM_CLASS_ENUM_TYPE(T) expands to - * T::Enum, we are missing the required "typename" keyword. So here, - * MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE is needed. - */ -# define MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(Name) typename Name::Enum -#endif - -# define MOZ_BEGIN_NESTED_ENUM_CLASS_GLUE(a, b) a b -# define MOZ_BEGIN_NESTED_ENUM_CLASS(...) \ - MOZ_BEGIN_NESTED_ENUM_CLASS_GLUE( \ - MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER, \ - __VA_ARGS__), \ - (__VA_ARGS__)) - -# define MOZ_BEGIN_ENUM_CLASS(...) MOZ_BEGIN_NESTED_ENUM_CLASS(__VA_ARGS__) -# define MOZ_END_ENUM_CLASS(Name) \ - MOZ_END_NESTED_ENUM_CLASS(Name) \ - MOZ_FINISH_NESTED_ENUM_CLASS(Name) - -#endif /* __cplusplus */ - -#endif /* mozilla_TypedEnum_h */ diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mfbt/TypedEnumInternal.h thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mfbt/TypedEnumInternal.h --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mfbt/TypedEnumInternal.h 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mfbt/TypedEnumInternal.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,103 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* Internal stuff needed by TypedEnum.h and TypedEnumBits.h. */ - -// NOTE: When we can assume C++11 enum class support and TypedEnum.h goes away, -// we should then consider folding TypedEnumInternal.h into TypedEnumBits.h. - -#ifndef mozilla_TypedEnumInternal_h -#define mozilla_TypedEnumInternal_h - -#include "mozilla/Attributes.h" - -#if defined(__cplusplus) - -#if defined(__clang__) - /* - * Per Clang documentation, "Note that marketing version numbers should not - * be used to check for language features, as different vendors use different - * numbering schemes. Instead, use the feature checking macros." - */ -# ifndef __has_extension -# define __has_extension __has_feature /* compatibility, for older versions of clang */ -# endif -# if __has_extension(cxx_strong_enums) -# define MOZ_HAVE_CXX11_STRONG_ENUMS -# endif -#elif defined(__GNUC__) -# if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L -# if MOZ_GCC_VERSION_AT_LEAST(4, 6, 3) -# define MOZ_HAVE_CXX11_STRONG_ENUMS -# endif -# endif -#elif defined(_MSC_VER) -# define MOZ_HAVE_CXX11_STRONG_ENUMS -#endif - -namespace mozilla { - -/* - * The problem that CastableTypedEnumResult aims to solve is that - * typed enums are not convertible to bool, and there is no way to make them - * be, yet user code wants to be able to write - * - * if (myFlags & Flags::SOME_PARTICULAR_FLAG) (1) - * - * There are different approaches to solving this. Most of them require - * adapting user code. For example, we could implement operator! and have - * the user write - * - * if (!!(myFlags & Flags::SOME_PARTICULAR_FLAG)) (2) - * - * Or we could supply a IsNonZero() or Any() function returning whether - * an enum value is nonzero, and have the user write - * - * if (Any(Flags & Flags::SOME_PARTICULAR_FLAG)) (3) - * - * But instead, we choose to preserve the original user syntax (1) as it - * is inherently more readable, and to ease porting existing code to typed - * enums. We achieve this by having operator& and other binary bitwise - * operators have as return type a class, CastableTypedEnumResult, - * that wraps a typed enum but adds bool convertibility. - */ -template -class CastableTypedEnumResult -{ -private: - const E mValue; - -public: - explicit MOZ_CONSTEXPR CastableTypedEnumResult(E aValue) - : mValue(aValue) - {} - - MOZ_CONSTEXPR operator E() const { return mValue; } - - template - MOZ_EXPLICIT_CONVERSION MOZ_CONSTEXPR - operator DestinationType() const { return DestinationType(mValue); } - - MOZ_CONSTEXPR bool operator !() const { return !bool(mValue); } - -#ifndef MOZ_HAVE_CXX11_STRONG_ENUMS - // This get() method is used to implement a constructor in the - // non-c++11 fallback path for MOZ_BEGIN_ENUM_CLASS, taking a - // CastableTypedEnumResult. If we try to implement it using the - // above conversion operator E(), then at least clang 3.3 - // (when forced to take the non-c++11 fallback path) compiles - // this constructor to an infinite recursion. So we introduce this - // get() method, that does exactly the same as the conversion operator, - // to work around this. - MOZ_CONSTEXPR E get() const { return mValue; } -#endif -}; - -} // namespace mozilla - -#endif // __cplusplus - -#endif // mozilla_TypedEnumInternal_h diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/app/Makefile.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/app/Makefile.in --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/app/Makefile.in 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/app/Makefile.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -PREF_JS_EXPORTS = $(srcdir)/mobile.js diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/app/mobile.js thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/app/mobile.js --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/app/mobile.js 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/app/mobile.js 2015-02-03 14:33:39.000000000 +0000 @@ -257,6 +257,13 @@ pref("browser.search.order.2", "chrome://browser/locale/region.properties"); pref("browser.search.order.3", "chrome://browser/locale/region.properties"); +// Market-specific search defaults (US market only) +pref("browser.search.geoSpecificDefaults", true); +pref("browser.search.defaultenginename.US", "chrome://browser/locale/region.properties"); +pref("browser.search.order.US.1", "chrome://browser/locale/region.properties"); +pref("browser.search.order.US.2", "chrome://browser/locale/region.properties"); +pref("browser.search.order.US.3", "chrome://browser/locale/region.properties"); + // disable updating pref("browser.search.update", false); @@ -271,9 +278,6 @@ // tell the search service that we don't really expose the "current engine" pref("browser.search.noCurrentEngine", true); -// disable the search service geoIP check -pref("browser.search.geoip.url", ""); - // Control media casting & mirroring features pref("browser.casting.enabled", true); pref("browser.mirroring.enabled", true); @@ -395,6 +399,8 @@ // When true, zooming will be enabled on all sites, even ones that declare user-scalable=no. pref("browser.ui.zoom.force-user-scalable", false); +pref("ui.zoomedview.enabled", false); + pref("ui.touch.radius.enabled", false); pref("ui.touch.radius.leftmm", 3); pref("ui.touch.radius.topmm", 5); @@ -844,3 +850,8 @@ // Enable the OpenH264 plugin support in the addon manager. pref("media.gmp-gmpopenh264.provider.enabled", true); + +// The default color scheme in reader mode (light, dark, print, auto) +// auto = color automatically adjusts according to ambient light level +// (auto only works on platforms where the 'devicelight' event is enabled) +pref("reader.color_scheme", "auto"); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/app/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/app/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/app/moz.build 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/app/moz.build 2015-02-03 14:33:39.000000000 +0000 @@ -12,3 +12,8 @@ if CONFIG['MOZ_PKG_SPECIAL']: DEFINES['MOZ_PKG_SPECIAL'] = CONFIG['MOZ_PKG_SPECIAL'] + +JS_PREFERENCE_FILES += [ + 'mobile.js', +] + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/AndroidManifest.xml.in thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/AndroidManifest.xml.in --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/AndroidManifest.xml.in 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/AndroidManifest.xml.in 2015-02-03 14:33:39.000000000 +0000 @@ -428,6 +428,8 @@ yearOfBirth is - * definitely old enough to create an account. + * Return true if the given year is the magic year. *

    - * This errs towards locking out users who might be old enough, but are not - * definitely old enough. + * The magic year is the calendar year when the user is the minimum age + * older. That is, for part of the magic year the user is younger than the age + * limit and for part of the magic year the user is older than the age limit. * * @param yearOfBirth - * @return true if somebody born in yearOfBirth is definitely old - * enough. + * @return true if yearOfBirth is the magic year. */ - public static boolean passesAgeCheck(int yearOfBirth) { - int thisYear = Calendar.getInstance().get(Calendar.YEAR); - int approximateAge = thisYear - yearOfBirth; - boolean oldEnough = approximateAge >= FxAccountConstants.MINIMUM_AGE_TO_CREATE_AN_ACCOUNT; + public static boolean isMagicYear(int yearOfBirth) { + final Calendar cal = Calendar.getInstance(); + final int thisYear = cal.get(Calendar.YEAR); + return (thisYear - yearOfBirth) == FxAccountConstants.MINIMUM_AGE_TO_CREATE_AN_ACCOUNT; + } + + /** + * Return true if the age of somebody born in + * dayOfBirth/zeroBasedMonthOfBirth/yearOfBirth is old enough to + * create an account. + * + * @param dayOfBirth + * @param zeroBasedMonthOfBirth + * @param yearOfBirth + * @return true if somebody born in + * dayOfBirth/zeroBasedMonthOfBirth/yearOfBirth is old enough. + */ + public static boolean passesAgeCheck(final int dayOfBirth, final int zeroBasedMonthOfBirth, final int yearOfBirth) { + final Calendar latestBirthday = Calendar.getInstance(); + final int y = latestBirthday.get(Calendar.YEAR); + final int m = latestBirthday.get(Calendar.MONTH); + final int d = latestBirthday.get(Calendar.DAY_OF_MONTH); + latestBirthday.clear(); + latestBirthday.set(y - FxAccountConstants.MINIMUM_AGE_TO_CREATE_AN_ACCOUNT, m, d); + + // Go back one second, so that the exact same birthday and latestBirthday satisfy birthday <= latestBirthday. + latestBirthday.add(Calendar.SECOND, 1); + + final Calendar birthday = Calendar.getInstance(); + birthday.clear(); + birthday.set(yearOfBirth, zeroBasedMonthOfBirth, dayOfBirth); + + boolean oldEnough = birthday.before(latestBirthday); + if (FxAccountUtils.LOG_PERSONAL_INFORMATION) { - FxAccountUtils.pii(LOG_TAG, "Age check " + (oldEnough ? "passes" : "fails") + - ": age is " + approximateAge + " = " + thisYear + " - " + yearOfBirth); + final StringBuilder message = new StringBuilder(); + final SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()); + message.append("Age check "); + message.append(oldEnough ? "passes" : "fails"); + message.append(": birthday is "); + message.append(sdf.format(birthday.getTime())); + message.append("; latest birthday is "); + message.append(sdf.format(latestBirthday.getTime())); + message.append(" (Y/M/D)."); + FxAccountUtils.pii(LOG_TAG, message.toString()); } + return oldEnough; } /** * Custom function for UI use only. */ - public static boolean passesAgeCheck(String yearText, String[] yearItems) { + public static boolean passesAgeCheck(int dayOfBirth, int zeroBaseMonthOfBirth, String yearText, String[] yearItems) { if (yearText == null) { throw new IllegalArgumentException("yearText must not be null"); } @@ -91,6 +131,7 @@ FxAccountUtils.pii(LOG_TAG, "Passed age check: year text was found in item list but was not a number."); return true; } - return passesAgeCheck(yearOfBirth); + + return passesAgeCheck(dayOfBirth, zeroBaseMonthOfBirth, yearOfBirth); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/BrowserApp.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/BrowserApp.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/BrowserApp.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/BrowserApp.java 2015-02-03 14:33:39.000000000 +0000 @@ -27,7 +27,6 @@ import org.mozilla.gecko.animation.ViewHelper; import org.mozilla.gecko.db.BrowserContract.Combined; import org.mozilla.gecko.db.BrowserDB; -import org.mozilla.gecko.db.LocalBrowserDB; import org.mozilla.gecko.db.SuggestedSites; import org.mozilla.gecko.distribution.Distribution; import org.mozilla.gecko.favicons.Favicons; @@ -50,13 +49,13 @@ import org.mozilla.gecko.home.HomePager.OnUrlOpenInBackgroundListener; import org.mozilla.gecko.home.HomePager.OnUrlOpenListener; import org.mozilla.gecko.home.HomePanelsManager; -import org.mozilla.gecko.home.TopSitesPanel; import org.mozilla.gecko.home.SearchEngine; import org.mozilla.gecko.menu.GeckoMenu; import org.mozilla.gecko.menu.GeckoMenuItem; import org.mozilla.gecko.mozglue.ContextUtils; import org.mozilla.gecko.mozglue.ContextUtils.SafeIntent; import org.mozilla.gecko.mozglue.RobocopTarget; +import org.mozilla.gecko.firstrun.FirstrunPane; import org.mozilla.gecko.preferences.ClearOnShutdownPref; import org.mozilla.gecko.preferences.GeckoPreferences; import org.mozilla.gecko.prompts.Prompt; @@ -67,7 +66,6 @@ import org.mozilla.gecko.tabs.TabHistoryPage; import org.mozilla.gecko.tabs.TabsPanel; import org.mozilla.gecko.tabs.TabHistoryController.OnShowTabHistory; -import org.mozilla.gecko.tiles.TilesRecorder; import org.mozilla.gecko.toolbar.AutocompleteHandler; import org.mozilla.gecko.toolbar.BrowserToolbar; import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState; @@ -90,7 +88,6 @@ import android.app.Activity; import android.app.AlertDialog; -import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; @@ -113,10 +110,8 @@ import android.os.Build; import android.os.Bundle; import android.os.StrictMode; -import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; -import android.support.v4.content.LocalBroadcastManager; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; @@ -163,7 +158,6 @@ private static final String STATE_ABOUT_HOME_TOP_PADDING = "abouthome_top_padding"; private static final String BROWSER_SEARCH_TAG = "browser_search"; - private static final String ONBOARD_STARTPANE_TAG = "startpane_dialog"; // Request ID for startActivityForResult. private static final int ACTIVITY_REQUEST_PREFERENCES = 1001; @@ -171,8 +165,6 @@ @RobocopTarget public static final String EXTRA_SKIP_STARTPANE = "skipstartpane"; - public static final String PREF_STARTPANE_ENABLED = "startpane_enabled"; - private BrowserSearch mBrowserSearch; private View mBrowserSearchContainer; @@ -181,6 +173,7 @@ public ActionModeCompatView mActionBar; private BrowserToolbar mBrowserToolbar; private ToolbarProgressView mProgressView; + private FirstrunPane mFirstrunPane; private HomePager mHomePager; private TabsPanel mTabsPanel; private ViewGroup mHomePagerContainer; @@ -238,8 +231,6 @@ private OrderedBroadcastHelper mOrderedBroadcastHelper; - private BroadcastReceiver mOnboardingReceiver; - private BrowserHealthReporter mBrowserHealthReporter; private ReadingListHelper mReadingListHelper; @@ -259,6 +250,175 @@ private final DynamicToolbar mDynamicToolbar = new DynamicToolbar(); + private DragHelper mDragHelper; + + private class DragHelper implements OuterLayout.DragCallback { + private int[] mToolbarLocation = new int[2]; // to avoid creation every time we need to check for toolbar location. + // When dragging horizontally, the area of mainlayout between left drag bound and right drag bound can + // be dragged. A touch on the right of that area will automatically close the view. + private int mStatusBarHeight; + + public DragHelper() { + // If a layout round happens from the root, the offset placed by viewdraghelper gets forgotten and + // main layout gets replaced to offset 0. + ((MainLayout) mMainLayout).setLayoutInterceptor(new LayoutInterceptor() { + @Override + public void onLayout() { + if (mRootLayout.isMoving()) { + mRootLayout.restoreTargetViewPosition(); + } + } + }); + } + + @Override + public void onDragProgress(float progress) { + mBrowserToolbar.setToolBarButtonsAlpha(1.0f - progress); + mTabsPanel.translateInRange(progress); + } + + @Override + public View getViewToDrag() { + return mMainLayout; + } + + /** + * Since pressing the tabs button slides the main layout, whereas draghelper changes its offset, here we + * restore the position of mainlayout as if it was opened by pressing the button. This allows the closing + * mechanism to work. + */ + @Override + public void startDrag(boolean wasOpen) { + if (wasOpen) { + mTabsPanel.setHWLayerEnabled(true); + mMainLayout.offsetTopAndBottom(getDragRange()); + mMainLayout.scrollTo(0, 0); + } else { + prepareTabsToShow(); + mBrowserToolbar.hideVirtualKeyboard(); + } + mBrowserToolbar.setContextMenuEnabled(false); + } + + @Override + public void stopDrag(boolean stoppingToOpen) { + if (stoppingToOpen) { + mTabsPanel.setHWLayerEnabled(false); + mMainLayout.offsetTopAndBottom(-getDragRange()); + mMainLayout.scrollTo(0, -getDragRange()); + } else { + mTabsPanel.hideImmediately(); + mTabsPanel.setHWLayerEnabled(false); + } + // Re-enabling context menu only while stopping to close. + if (stoppingToOpen) { + mBrowserToolbar.setContextMenuEnabled(false); + } else { + mBrowserToolbar.setContextMenuEnabled(true); + } + } + + @Override + public int getDragRange() { + return mTabsPanel.getVerticalPanelHeight(); + } + + @Override + public int getOrderedChildIndex(int index) { + // See ViewDragHelper's findTopChildUnder method. ViewDragHelper looks for the topmost view in z order + // to understand what needs to be dragged. Here we are tampering Toast's index in case it's hidden, + // otherwise draghelper would try to drag it. + int mainLayoutIndex = mRootLayout.indexOfChild(mMainLayout); + if (index > mainLayoutIndex && (mToast == null || !mToast.isVisible())) { + return mainLayoutIndex; + } else { + return index; + } + } + + @Override + public boolean canDrag(MotionEvent event) { + if (!AppConstants.MOZ_DRAGGABLE_URLBAR) { + return false; + } + + // if no current tab is active. + if (Tabs.getInstance().getSelectedTab() == null) { + return false; + } + + // currently disabled for tablets. + if (HardwareUtils.isTablet()) { + return false; + } + + // not enabled in editing mode. + if (mBrowserToolbar.isEditing()) { + return false; + } + + return isInToolbarBounds((int) event.getRawY()); + } + + @Override + public boolean canInterceptEventWhileOpen(MotionEvent event) { + if (event.getActionMasked() != MotionEvent.ACTION_DOWN) { + return false; + } + + // Need to check if are intercepting a touch on main layout since we might hit a visible toast. + if (mRootLayout.findTopChildUnder(event) == mMainLayout && + isInToolbarBounds((int) event.getRawY())) { + return true; + } + return false; + } + + private boolean isInToolbarBounds(int y) { + mBrowserToolbar.getLocationOnScreen(mToolbarLocation); + final int upperLimit = mToolbarLocation[1] + mBrowserToolbar.getMeasuredHeight(); + final int lowerLimit = mToolbarLocation[1]; + return (y > lowerLimit && y < upperLimit); + } + + public void prepareTabsToShow() { + if (ensureTabsPanelExists()) { + // If we've just inflated the tabs panel, only show it once the current + // layout pass is done to avoid displayed temporary UI states during + // relayout. + final ViewTreeObserver vto = mTabsPanel.getViewTreeObserver(); + if (vto.isAlive()) { + vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + mTabsPanel.getViewTreeObserver().removeGlobalOnLayoutListener(this); + prepareTabsToShow(); + } + }); + } + } else { + mTabsPanel.prepareToDrag(); + } + } + + public int getLowerLimit() { + return getStatusBarHeight(); + } + + private int getStatusBarHeight() { + if (mStatusBarHeight != 0) { + return mStatusBarHeight; + } + final int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + mStatusBarHeight = getResources().getDimensionPixelSize(resourceId); + return mStatusBarHeight; + } + Log.e(LOGTAG, "Unable to find statusbar height"); + return 0; + } + } + @Override public View onCreateView(final String name, final Context context, final AttributeSet attrs) { final View view; @@ -658,6 +818,9 @@ } }); + mDragHelper = new DragHelper(); + mRootLayout.setDraggableCallback(mDragHelper); + // Set the maximum bits-per-pixel the favicon system cares about. IconDirectoryEntry.setMaxBPP(GeckoAppShell.getScreenDepth()); } @@ -688,31 +851,29 @@ } /** - * Check and show onboarding start pane if the browser has never been launched and + * Check and show the firstrun pane if the browser has never been launched and * is not opening an external link from another application. * - * @param context Context of application; used to show Start Pane if appropriate + * @param context Context of application; used to show firstrun pane if appropriate * @param intent Intent that launched this activity */ - private void checkStartPane(Context context, SafeIntent intent) { + private void checkFirstrun(Context context, SafeIntent intent) { if (intent.getBooleanExtra(EXTRA_SKIP_STARTPANE, false)) { // Note that we don't set the pref, so subsequent launches can result - // in the start pane being shown. + // in the firstrun pane being shown. return; } - final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); try { final SharedPreferences prefs = GeckoSharedPrefs.forProfile(this); - if (prefs.getBoolean(PREF_STARTPANE_ENABLED, false)) { + if (prefs.getBoolean(FirstrunPane.PREF_FIRSTRUN_ENABLED, false)) { if (!Intent.ACTION_VIEW.equals(intent.getAction())) { - final DialogFragment dialog = new StartPane(); - dialog.show(getSupportFragmentManager(), ONBOARD_STARTPANE_TAG); + showFirstrunPager(); } // Don't bother trying again to show the v1 minimal first run. - prefs.edit().putBoolean(PREF_STARTPANE_ENABLED, false).apply(); + prefs.edit().putBoolean(FirstrunPane.PREF_FIRSTRUN_ENABLED, false).apply(); } } finally { StrictMode.setThreadPolicy(savedPolicy); @@ -748,13 +909,18 @@ return; } + if (hideFirstrunPager()) { + Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, TelemetryContract.Method.BACK, "firstrun-pane"); + return; + } + super.onBackPressed(); } @Override public void onAttachedToWindow() { // We can't show the first run experience until Gecko has finished initialization (bug 1077583). - checkStartPane(this, new SafeIntent(getIntent())); + checkFirstrun(this, new SafeIntent(getIntent())); } @Override @@ -783,9 +949,6 @@ // Register for Prompt:ShowTop so we can foreground this activity even if it's hidden. EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener)this, "Prompt:ShowTop"); - - final LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this); - lbm.unregisterReceiver(mOnboardingReceiver); } @Override @@ -955,7 +1118,7 @@ if (enabled) { if (mLayerView != null) { - mLayerView.setOnMetricsChangedListener(this); + mLayerView.setOnMetricsChangedDynamicToolbarViewportListener(this); } setToolbarMargin(0); mHomePagerContainer.setPadding(0, mBrowserChrome.getHeight(), 0, 0); @@ -963,7 +1126,7 @@ // Immediately show the toolbar when disabling the dynamic // toolbar. if (mLayerView != null) { - mLayerView.setOnMetricsChangedListener(null); + mLayerView.setOnMetricsChangedDynamicToolbarViewportListener(null); } mHomePagerContainer.setPadding(0, 0, 0, 0); if (mBrowserChrome != null) { @@ -1358,6 +1521,7 @@ invalidateOptionsMenu(); if (mTabsPanel != null) { + mRootLayout.reset(); updateSideBarState(); mTabsPanel.refresh(); } @@ -1381,6 +1545,10 @@ }); } + private boolean isSideBar() { + return (HardwareUtils.isTablet() && getOrientation() == Configuration.ORIENTATION_LANDSCAPE); + } + private void updateSideBarState() { if (NewTabletUI.isEnabled(this)) { return; @@ -1389,7 +1557,7 @@ if (mMainLayoutAnimator != null) mMainLayoutAnimator.stop(); - boolean isSideBar = (HardwareUtils.isTablet() && getOrientation() == Configuration.ORIENTATION_LANDSCAPE); + boolean isSideBar = isSideBar(); final int sidebarWidth = getResources().getDimensionPixelSize(R.dimen.tabs_sidebar_width); ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mTabsPanel.getLayoutParams(); @@ -1401,6 +1569,7 @@ mMainLayout.scrollTo(mainLayoutScrollX, 0); mTabsPanel.setIsSideBar(isSideBar); + mRootLayout.updateDragHelperParameters(); } @Override @@ -1732,7 +1901,7 @@ @Override public void onGlobalLayout() { mTabsPanel.getViewTreeObserver().removeGlobalOnLayoutListener(this); - mTabsPanel.show(panel); + showTabs(panel); } }); } @@ -1821,10 +1990,13 @@ if (!areTabsShown()) { mTabsPanel.setVisibility(View.INVISIBLE); mTabsPanel.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); + mRootLayout.setClosed(); + mBrowserToolbar.setContextMenuEnabled(true); } else { // Cancel editing mode to return to page content when the TabsPanel closes. We cancel // it here because there are graphical glitches if it's canceled while it's visible. mBrowserToolbar.cancelEdit(); + mRootLayout.setOpen(); } mTabsPanel.finishTabsAnimation(); @@ -1934,12 +2106,21 @@ && mHomePagerContainer != null && mHomePagerContainer.getVisibility() == View.VISIBLE); } + private boolean isFirstrunVisible() { + return (mFirstrunPane != null && mFirstrunPane.isVisible() + && mHomePagerContainer != null && mHomePagerContainer.getVisibility() == View.VISIBLE); + } + /** * Enters editing mode with the current tab's URL. There might be no * tabs loaded by the time the user enters editing mode e.g. just after * the app starts. In this case, we simply fallback to an empty URL. */ private void enterEditingMode() { + if (hideFirstrunPager()) { + Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, TelemetryContract.Method.ACTIONBAR, "firstrun-pane"); + } + String url = ""; final Tab tab = Tabs.getInstance().getSelectedTab(); @@ -2212,6 +2393,22 @@ } } + private void showFirstrunPager() { + if (mFirstrunPane == null) { + final ViewStub firstrunPagerStub = (ViewStub) findViewById(R.id.firstrun_pager_stub); + mFirstrunPane = (FirstrunPane) firstrunPagerStub.inflate(); + mFirstrunPane.load(getSupportFragmentManager()); + mFirstrunPane.registerOnFinishListener(new FirstrunPane.OnFinishListener() { + @Override + public void onFinish() { + BrowserApp.this.mFirstrunPane = null; + } + }); + } + + mHomePagerContainer.setVisibility(View.VISIBLE); + } + private void showHomePager(String panelId) { showHomePagerWithAnimator(panelId, null); } @@ -2299,6 +2496,15 @@ mLayerView.setVisibility(View.INVISIBLE); } + public boolean hideFirstrunPager() { + if (!isFirstrunVisible()) { + return false; + } + + mFirstrunPane.hide(); + return true; + } + /** * Hides the HomePager, using the url of the currently selected tab as the url to be * loaded. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/db/PasswordsProvider.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/db/PasswordsProvider.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/db/PasswordsProvider.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/db/PasswordsProvider.java 2015-02-03 14:33:40.000000000 +0000 @@ -6,6 +6,7 @@ import java.util.HashMap; +import org.mozilla.gecko.CrashHandler; import org.mozilla.gecko.GeckoApp; import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.GeckoEvent; @@ -50,6 +51,8 @@ private static final String LOG_TAG = "GeckPasswordsProvider"; + private CrashHandler mCrashHandler; + static { URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); @@ -82,10 +85,26 @@ public PasswordsProvider() { super(LOG_TAG); + } + + @Override + public boolean onCreate() { + mCrashHandler = CrashHandler.createDefaultCrashHandler(getContext()); // We don't use .loadMozGlue because we're in a different process, // and we just want to reuse code rather than use the loader lock etc. GeckoLoader.doLoadLibrary(getContext(), "mozglue"); + return super.onCreate(); + } + + @Override + public void shutdown() { + super.shutdown(); + + if (mCrashHandler != null) { + mCrashHandler.unregister(); + mCrashHandler = null; + } } @Override diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/db/SQLiteBridgeContentProvider.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/db/SQLiteBridgeContentProvider.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/db/SQLiteBridgeContentProvider.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/db/SQLiteBridgeContentProvider.java 2015-02-03 14:33:40.000000000 +0000 @@ -7,6 +7,7 @@ import java.io.File; import java.util.HashMap; +import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.GeckoProfile; import org.mozilla.gecko.GeckoThread; import org.mozilla.gecko.Telemetry; @@ -97,6 +98,10 @@ } mDatabasePerProfile = null; } + + if (AppConstants.Versions.feature11Plus) { + super.shutdown(); + } } @Override diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/db/SuggestedSites.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/db/SuggestedSites.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/db/SuggestedSites.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/db/SuggestedSites.java 2015-02-03 14:33:40.000000000 +0000 @@ -288,17 +288,16 @@ return; } - distribution.addOnDistributionReadyCallback(new Runnable() { + distribution.addOnDistributionReadyCallback(new Distribution.ReadyCallback() { @Override - public void run() { - Log.d(LOGTAG, "Running post-distribution task: suggested sites."); - + public void distributionNotFound() { // If distribution doesn't exist, simply continue to load // suggested sites directly from resources. See refresh(). - if (!distribution.exists()) { - return; - } + } + @Override + public void distributionFound(Distribution distribution) { + Log.d(LOGTAG, "Running post-distribution task: suggested sites."); // Merge suggested sites from distribution with the // default ones. Distribution takes precedence. Map sites = loadFromDistribution(distribution); @@ -320,6 +319,11 @@ final ContentResolver cr = context.getContentResolver(); cr.notifyChange(BrowserContract.SuggestedSites.CONTENT_URI, null); } + + @Override + public void distributionArrivedLate(Distribution distribution) { + distributionFound(distribution); + } }); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/distribution/Distribution.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/distribution/Distribution.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/distribution/Distribution.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/distribution/Distribution.java 2015-02-03 14:33:40.000000000 +0000 @@ -36,12 +36,14 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.GeckoEvent; import org.mozilla.gecko.GeckoSharedPrefs; import org.mozilla.gecko.Telemetry; import org.mozilla.gecko.mozglue.RobocopTarget; import org.mozilla.gecko.util.FileUtils; +import org.mozilla.gecko.util.HardwareUtils; import org.mozilla.gecko.util.ThreadUtils; import android.app.Activity; @@ -104,9 +106,29 @@ // Corresponds to the high value in Histograms.json. private static final long MAX_DOWNLOAD_TIME_MSEC = 40000; // 40 seconds. - // Wait just a little while for the system to send a referrer intent after install. - private static final long DELAY_WAIT_FOR_REFERRER_MSEC = 400; - + // If this is true, ready callbacks that arrive after our state is initially determined + // will be queued for delayed running. + // This should only be the case on first run, when we're in STATE_NONE. + // Implicitly accessed from any non-UI threads via Distribution.doInit, but in practice only one + // will actually perform initialization, and "non-UI thread" really means "background thread". + private volatile boolean shouldDelayLateCallbacks = false; + + /** + * These tasks can be queued to run when a distribution is available. + * + * If distributionFound is called, it will be the only call. + * If distributionNotFound is called, it might be followed by + * a call to distributionArrivedLate. + * + * When distributionNotFound is called, + * {@link org.mozilla.gecko.distribution.Distribution#exists()} will return + * false. In the other two callbacks, it will return true. + */ + public interface ReadyCallback { + void distributionNotFound(); + void distributionFound(Distribution distribution); + void distributionArrivedLate(Distribution distribution); + } /** * Used as a drop-off point for ReferrerReceiver. Checked when we process @@ -123,10 +145,14 @@ private final String packagePath; private final String prefsBranch; - private volatile int state = STATE_UNKNOWN; + volatile int state = STATE_UNKNOWN; private File distributionDir; - private final Queue onDistributionReady = new ConcurrentLinkedQueue(); + private final Queue onDistributionReady = new ConcurrentLinkedQueue<>(); + + // Callbacks in this queue have been invoked once as distributionNotFound. + // If they're invoked again, it'll be with distributionArrivedLate. + private final Queue onLateReady = new ConcurrentLinkedQueue<>(); /** * This is a little bit of a bad singleton, because in principle a Distribution @@ -246,9 +272,51 @@ * * @param ref a parsed referrer value from the store-supplied intent. */ - public static void onReceivedReferrer(ReferrerDescriptor ref) { + public static void onReceivedReferrer(final Context context, final ReferrerDescriptor ref) { // Track the referrer object for distribution handling. referrer = ref; + + ThreadUtils.postToBackgroundThread(new Runnable() { + @Override + public void run() { + final Distribution distribution = Distribution.getInstance(context); + + // This will bail if we aren't delayed, or we already have a distribution. + distribution.processDelayedReferrer(ref); + } + }); + } + + /** + * Handle a referrer intent that arrives after first use of the distribution. + */ + private void processDelayedReferrer(final ReferrerDescriptor ref) { + ThreadUtils.assertOnBackgroundThread(); + if (state != STATE_NONE) { + return; + } + + Log.i(LOGTAG, "Processing delayed referrer."); + + if (!checkIntentDistribution(ref)) { + // Oh well. No sense keeping these tasks around. + this.onLateReady.clear(); + return; + } + + // Persist our new state. + this.state = STATE_SET; + getSharedPreferences().edit().putInt(getKeyName(), this.state).apply(); + + // Just in case this isn't empty but doInit has finished. + runReadyQueue(); + + // Now process any tasks that already ran while we were in STATE_NONE + // to tell them of our good news. + runLateReadyQueue(); + + // Make sure that changes to search defaults are applied immediately. + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Distribution:Changed", "")); } /** @@ -344,15 +412,11 @@ // Bail if we've already tried to initialize the distribution, and // there wasn't one. - final SharedPreferences settings; - if (prefsBranch == null) { - settings = GeckoSharedPrefs.forApp(context); - } else { - settings = context.getSharedPreferences(prefsBranch, Activity.MODE_PRIVATE); - } + final SharedPreferences settings = getSharedPreferences(); - String keyName = context.getPackageName() + ".distribution_state"; + final String keyName = getKeyName(); this.state = settings.getInt(keyName, STATE_UNKNOWN); + if (this.state == STATE_NONE) { runReadyQueue(); return false; @@ -368,10 +432,14 @@ // We try the install intent, then the APK, then the system directory. final boolean distributionSet = - checkIntentDistribution() || + checkIntentDistribution(referrer) || checkAPKDistribution() || checkSystemDistribution(); + // If this is our first run -- and thus we weren't already in STATE_NONE or STATE_SET above -- + // and we didn't find a distribution already, then we should hold on to callbacks in case we + // get a late distribution. + this.shouldDelayLateCallbacks = !distributionSet; this.state = distributionSet ? STATE_SET : STATE_NONE; settings.edit().putInt(keyName, this.state).apply(); @@ -385,18 +453,9 @@ * * @return true if a referrer-supplied distribution was selected. */ - private boolean checkIntentDistribution() { + private boolean checkIntentDistribution(final ReferrerDescriptor referrer) { if (referrer == null) { - // Wait a predetermined time and try again. - // Just block the thread, because it's the simplest solution. - try { - Thread.sleep(DELAY_WAIT_FOR_REFERRER_MSEC); - } catch (InterruptedException e) { - // Good enough. - } - if (referrer == null) { - return false; - } + return false; } URI uri = getReferredDistribution(referrer); @@ -408,9 +467,21 @@ Log.v(LOGTAG, "Downloading referred distribution: " + uri); try { - HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection(); + final HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection(); + + // If the Search Activity starts, and we handle the referrer intent, this'll return + // null. Recover gracefully in this case. + final GeckoAppShell.GeckoInterface geckoInterface = GeckoAppShell.getGeckoInterface(); + final String ua; + if (geckoInterface == null) { + // Fall back to GeckoApp's default implementation. + ua = HardwareUtils.isTablet() ? AppConstants.USER_AGENT_FENNEC_TABLET : + AppConstants.USER_AGENT_FENNEC_MOBILE; + } else { + ua = geckoInterface.getDefaultUAString(); + } - connection.setRequestProperty(HTTP.USER_AGENT, GeckoAppShell.getGeckoInterface().getDefaultUAString()); + connection.setRequestProperty(HTTP.USER_AGENT, ua); connection.setRequestProperty("Accept", EXPECTED_CONTENT_TYPE); try { @@ -536,18 +607,6 @@ } /** - * Execute tasks that wanted to run when we were done loading - * the distribution. These tasks are expected to call {@link #exists()} - * to find out whether there's a distribution or not. - */ - private void runReadyQueue() { - Runnable task; - while ((task = onDistributionReady.poll()) != null) { - ThreadUtils.postToBackgroundThread(task); - } - } - - /** * @return true if we copied files out of the APK. Sets distributionDir in that case. */ private boolean checkAPKDistribution() { @@ -759,26 +818,96 @@ } /** - * The provided Runnable will be queued for execution after + * The provided ReadyCallback will be queued for execution after * the distribution is ready, or queued for immediate execution if the * distribution has already been processed. * - * Each Runnable will be executed on the background thread. + * Each ReadyCallback will be executed on the background thread. */ - public void addOnDistributionReadyCallback(Runnable runnable) { + public void addOnDistributionReadyCallback(final ReadyCallback callback) { if (state == STATE_UNKNOWN) { - this.onDistributionReady.add(runnable); + // Queue for later. + onDistributionReady.add(callback); } else { - // If we're already initialized, just queue up the runnable. - ThreadUtils.postToBackgroundThread(runnable); + invokeCallbackDelayed(callback); } } /** + * Run our delayed queue, after a delayed distribution arrives. + */ + private void runLateReadyQueue() { + ReadyCallback task; + while ((task = onLateReady.poll()) != null) { + invokeLateCallbackDelayed(task); + } + } + + /** + * Execute tasks that wanted to run when we were done loading + * the distribution. + */ + private void runReadyQueue() { + ReadyCallback task; + while ((task = onDistributionReady.poll()) != null) { + invokeCallbackDelayed(task); + } + } + + private void invokeLateCallbackDelayed(final ReadyCallback callback) { + ThreadUtils.postToBackgroundThread(new Runnable() { + @Override + public void run() { + // Sanity. + if (state != STATE_SET) { + Log.w(LOGTAG, "Refusing to invoke late distro callback in state " + state); + return; + } + callback.distributionArrivedLate(Distribution.this); + } + }); + } + + private void invokeCallbackDelayed(final ReadyCallback callback) { + ThreadUtils.postToBackgroundThread(new Runnable() { + @Override + public void run() { + switch (state) { + case STATE_SET: + callback.distributionFound(Distribution.this); + break; + case STATE_NONE: + callback.distributionNotFound(); + if (shouldDelayLateCallbacks) { + onLateReady.add(callback); + } + break; + default: + throw new IllegalStateException("Expected STATE_NONE or STATE_SET, got " + state); + } + } + }); + } + + /** * A safe way for callers to determine if this Distribution instance * represents a real live distribution. */ public boolean exists() { return state == STATE_SET; } + + private String getKeyName() { + return context.getPackageName() + ".distribution_state"; + } + + private SharedPreferences getSharedPreferences() { + final SharedPreferences settings; + if (prefsBranch == null) { + settings = GeckoSharedPrefs.forApp(context); + } else { + settings = context.getSharedPreferences(prefsBranch, Activity.MODE_PRIVATE); + } + return settings; + } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/distribution/ReferrerReceiver.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/distribution/ReferrerReceiver.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/distribution/ReferrerReceiver.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/distribution/ReferrerReceiver.java 2015-02-03 14:33:40.000000000 +0000 @@ -49,7 +49,7 @@ // Track the referrer object for distribution handling. if (TextUtils.equals(referrer.campaign, DISTRIBUTION_UTM_CAMPAIGN)) { - Distribution.onReceivedReferrer(referrer); + Distribution.onReceivedReferrer(context, referrer); } else { Log.d(LOGTAG, "Not downloading distribution: non-matching campaign."); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/firstrun/FirstrunPagerConfig.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/firstrun/FirstrunPagerConfig.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/firstrun/FirstrunPagerConfig.java 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/firstrun/FirstrunPagerConfig.java 2015-02-03 14:33:40.000000000 +0000 @@ -0,0 +1,36 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * 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/. */ + +package org.mozilla.gecko.firstrun; + +import java.util.LinkedList; +import java.util.List; + +public class FirstrunPagerConfig { + public static List getDefault() { + final List panels = new LinkedList<>(); + panels.add(new FirstrunPanel(WelcomePanel.class.getName(), "Welcome")); + return panels; + } + + public static class FirstrunPanel { + private String resource; + private String title; + + public FirstrunPanel(String resource, String title) { + this.resource = resource; + this.title = title; + } + + public String getResource() { + return this.resource; + } + + public String getTitle() { + return this.title; + } + + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/firstrun/FirstrunPager.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/firstrun/FirstrunPager.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/firstrun/FirstrunPager.java 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/firstrun/FirstrunPager.java 2015-02-03 14:33:40.000000000 +0000 @@ -0,0 +1,90 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * 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/. */ + +package org.mozilla.gecko.firstrun; + +import android.content.Context; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import com.nineoldandroids.animation.Animator; +import com.nineoldandroids.animation.AnimatorSet; +import com.nineoldandroids.animation.ObjectAnimator; +import com.nineoldandroids.view.ViewHelper; +import org.mozilla.gecko.animation.TransitionsTracker; + +import java.util.List; + +public class FirstrunPager extends ViewPager { + private Context context; + protected FirstrunPane.OnFinishListener listener; + + public FirstrunPager(Context context) { + this(context, null); + } + + public FirstrunPager(Context context, AttributeSet attrs) { + super(context, attrs); + this.context = context; + } + + public void load(FragmentManager fm, FirstrunPane.OnFinishListener listener) { + setAdapter(new ViewPagerAdapter(fm, FirstrunPagerConfig.getDefault())); + this.listener = listener; + + animateLoad(); + } + + public void hide() { + setAdapter(null); + } + + private void animateLoad() { + ViewHelper.setTranslationY(this, 500); + ViewHelper.setAlpha(this, 0); + + final Animator translateAnimator = ObjectAnimator.ofFloat(this, "translationY", 0); + translateAnimator.setDuration(400); + + final Animator alphaAnimator = ObjectAnimator.ofFloat(this, "alpha", 1); + alphaAnimator.setStartDelay(200); + alphaAnimator.setDuration(600); + + final AnimatorSet set = new AnimatorSet(); + set.playTogether(alphaAnimator, translateAnimator); + set.setStartDelay(400); + TransitionsTracker.track(set); + + set.start(); + } + + private class ViewPagerAdapter extends FragmentPagerAdapter { + private List panels; + + public ViewPagerAdapter(FragmentManager fm, List panels) { + super(fm); + this.panels = panels; + } + + @Override + public Fragment getItem(int i) { + final Fragment fragment = Fragment.instantiate(context, panels.get(i).getResource()); + ((FirstrunPanel) fragment).setOnFinishListener(listener); + return fragment; + } + + @Override + public int getCount() { + return panels.size(); + } + + @Override + public CharSequence getPageTitle(int i) { + return panels.get(i).getTitle().toUpperCase(); + } + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/firstrun/FirstrunPane.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/firstrun/FirstrunPane.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/firstrun/FirstrunPane.java 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/firstrun/FirstrunPane.java 2015-02-03 14:33:40.000000000 +0000 @@ -0,0 +1,80 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * 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/. */ + +package org.mozilla.gecko.firstrun; + +import android.content.Context; +import android.support.v4.app.FragmentManager; +import android.util.AttributeSet; + +import android.view.View; +import android.widget.LinearLayout; +import com.nineoldandroids.animation.Animator; +import com.nineoldandroids.animation.AnimatorListenerAdapter; +import com.nineoldandroids.animation.ObjectAnimator; +import org.mozilla.gecko.R; +import org.mozilla.gecko.animation.TransitionsTracker; + +public class FirstrunPane extends LinearLayout { + public static final String PREF_FIRSTRUN_ENABLED = "startpane_enabled"; + + public static interface OnFinishListener { + public void onFinish(); + } + + private FirstrunPager pager; + private boolean visible; + private OnFinishListener onFinishListener; + + public FirstrunPane(Context context) { + this(context, null); + } + public FirstrunPane(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void load(FragmentManager fm) { + visible = true; + pager = (FirstrunPager) findViewById(R.id.firstrun_pager); + pager.load(fm, new OnFinishListener() { + @Override + public void onFinish() { + hide(); + } + }); + } + + public boolean isVisible() { + return visible; + } + + public void hide() { + visible = false; + pager.hide(); + if (onFinishListener != null) { + onFinishListener.onFinish(); + } + animateHide(); + } + + private void animateHide() { + final Animator alphaAnimator = ObjectAnimator.ofFloat(this, "alpha", 0); + alphaAnimator.setDuration(150); + alphaAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + FirstrunPane.this.setVisibility(View.GONE); + } + }); + + TransitionsTracker.track(alphaAnimator); + + alphaAnimator.start(); + } + + public void registerOnFinishListener(OnFinishListener listener) { + this.onFinishListener = listener; + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/firstrun/FirstrunPanel.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/firstrun/FirstrunPanel.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/firstrun/FirstrunPanel.java 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/firstrun/FirstrunPanel.java 2015-02-03 14:33:40.000000000 +0000 @@ -0,0 +1,23 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * 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/. */ + +package org.mozilla.gecko.firstrun; + +import android.support.v4.app.Fragment; + +public class FirstrunPanel extends Fragment { + + protected FirstrunPane.OnFinishListener onFinishListener; + + public void setOnFinishListener(FirstrunPane.OnFinishListener listener) { + this.onFinishListener = listener; + } + + protected void close() { + if (onFinishListener != null) { + onFinishListener.onFinish(); + } + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/firstrun/WelcomePanel.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/firstrun/WelcomePanel.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/firstrun/WelcomePanel.java 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/firstrun/WelcomePanel.java 2015-02-03 14:33:40.000000000 +0000 @@ -0,0 +1,44 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * 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/. */ + +package org.mozilla.gecko.firstrun; + +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import org.mozilla.gecko.R; +import org.mozilla.gecko.Telemetry; +import org.mozilla.gecko.TelemetryContract; +import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity; + +public class WelcomePanel extends FirstrunPanel { + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) { + final ViewGroup root = (ViewGroup) inflater.inflate(R.layout.firstrun_welcome_fragment, container, false); + root.findViewById(R.id.welcome_account).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.BUTTON, "firstrun-sync"); + + final Intent accountIntent = new Intent(getActivity(), FxAccountGetStartedActivity.class); + startActivity(accountIntent); + + close(); + } + }); + + root.findViewById(R.id.welcome_browse).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.BUTTON, "firstrun-browser"); + close(); + } + }); + + return root; + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/fxa/activities/FxAccountAbstractActivity.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/fxa/activities/FxAccountAbstractActivity.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/fxa/activities/FxAccountAbstractActivity.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/fxa/activities/FxAccountAbstractActivity.java 2015-02-03 14:33:40.000000000 +0000 @@ -4,12 +4,12 @@ package org.mozilla.gecko.fxa.activities; +import org.mozilla.gecko.Locales.LocaleAwareActivity; import org.mozilla.gecko.background.common.log.Logger; import org.mozilla.gecko.background.fxa.FxAccountAgeLockoutHelper; import org.mozilla.gecko.fxa.FirefoxAccounts; import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount; import org.mozilla.gecko.sync.setup.activities.ActivityUtils; -import org.mozilla.gecko.Locales.LocaleAwareActivity; import android.accounts.Account; import android.app.Activity; @@ -71,6 +71,12 @@ redirectIfAppropriate(); } + @Override + public void onBackPressed() { + super.onBackPressed(); + overridePendingTransition(0, 0); + } + protected void launchActivity(Class activityClass) { Intent intent = new Intent(this, activityClass); // Per http://stackoverflow.com/a/8992365, this triggers a known bug with diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/fxa/activities/FxAccountAbstractSetupActivity.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/fxa/activities/FxAccountAbstractSetupActivity.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/fxa/activities/FxAccountAbstractSetupActivity.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/fxa/activities/FxAccountAbstractSetupActivity.java 2015-02-03 14:33:40.000000000 +0000 @@ -12,6 +12,7 @@ import java.util.Map; import java.util.Set; +import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.R; import org.mozilla.gecko.background.common.log.Logger; import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate; @@ -34,6 +35,8 @@ import android.accounts.Account; import android.accounts.AccountManager; +import android.animation.LayoutTransition; +import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.os.AsyncTask; @@ -50,6 +53,7 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; +import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; import android.widget.Button; @@ -63,6 +67,8 @@ public static final String EXTRA_PASSWORD = "password"; public static final String EXTRA_PASSWORD_SHOWN = "password_shown"; public static final String EXTRA_YEAR = "year"; + public static final String EXTRA_MONTH = "month"; + public static final String EXTRA_DAY = "day"; public static final String EXTRA_EXTRAS = "extras"; public static final String JSON_KEY_AUTH = "auth"; @@ -145,7 +151,14 @@ } protected void hideRemoteError() { - remoteErrorTextView.setVisibility(View.INVISIBLE); + if (AppConstants.Versions.feature11Plus) { + // On v11+, we remove the view entirely, which triggers a smooth + // animation. + remoteErrorTextView.setVisibility(View.GONE); + } else { + // On earlier versions, we just hide the error. + remoteErrorTextView.setVisibility(View.INVISIBLE); + } } protected void showRemoteError(Exception e, int defaultResourceId) { @@ -426,6 +439,16 @@ super.onCreate(savedInstanceState); } + @SuppressLint("NewApi") + protected void maybeEnableAnimations() { + // On v11+, we animate the error display being added and removed. This saves + // us some vertical space when we start the activity. + if (AppConstants.Versions.feature11Plus) { + final ViewGroup container = (ViewGroup) remoteErrorTextView.getParent(); + container.setLayoutTransition(new LayoutTransition()); + } + } + protected void updateFromIntentExtras() { // Only set email/password in onCreate; we don't want to overwrite edited values onResume. if (getIntent() != null && getIntent().getExtras() != null) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/fxa/activities/FxAccountAbstractUpdateCredentialsActivity.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/fxa/activities/FxAccountAbstractUpdateCredentialsActivity.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/fxa/activities/FxAccountAbstractUpdateCredentialsActivity.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/fxa/activities/FxAccountAbstractUpdateCredentialsActivity.java 2015-02-03 14:33:40.000000000 +0000 @@ -83,6 +83,7 @@ ActivityUtils.linkTextView(view, R.string.fxaccount_sign_in_forgot_password, R.string.fxaccount_link_forgot_password); updateFromIntentExtras(); + maybeEnableAnimations(); } protected class UpdateCredentialsDelegate implements RequestDelegate { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/fxa/activities/FxAccountCreateAccountActivity.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/fxa/activities/FxAccountCreateAccountActivity.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/fxa/activities/FxAccountCreateAccountActivity.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/fxa/activities/FxAccountCreateAccountActivity.java 2015-02-03 14:33:40.000000000 +0000 @@ -7,6 +7,7 @@ import java.util.Calendar; import java.util.HashMap; import java.util.LinkedList; +import java.util.Locale; import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -52,8 +53,13 @@ private static final int CHILD_REQUEST_CODE = 2; protected String[] yearItems; + protected String[] monthItems; + protected String[] dayItems; protected EditText yearEdit; + protected EditText monthEdit; + protected EditText dayEdit; protected CheckBox chooseCheckBox; + protected View monthDaycombo; protected Map selectedEngines; @@ -71,6 +77,9 @@ passwordEdit = (EditText) ensureFindViewById(null, R.id.password, "password edit"); showPasswordButton = (Button) ensureFindViewById(null, R.id.show_password, "show password button"); yearEdit = (EditText) ensureFindViewById(null, R.id.year_edit, "year edit"); + monthEdit = (EditText) ensureFindViewById(null, R.id.month_edit, "month edit"); + dayEdit = (EditText) ensureFindViewById(null, R.id.day_edit, "day edit"); + monthDaycombo = ensureFindViewById(null, R.id.month_day_combo, "month day combo"); remoteErrorTextView = (TextView) ensureFindViewById(null, R.id.remote_error, "remote error text view"); button = (Button) ensureFindViewById(null, R.id.button, "create account button"); progressBar = (ProgressBar) ensureFindViewById(null, R.id.progress, "progress bar"); @@ -84,6 +93,7 @@ createShowPasswordButton(); linkifyPolicy(); createChooseCheckBox(); + initializeMonthAndDayValues(); View signInInsteadLink = ensureFindViewById(null, R.id.sign_in_instead_link, "sign in instead link"); signInInsteadLink.setOnClickListener(new OnClickListener() { @@ -95,13 +105,26 @@ }); updateFromIntentExtras(); + maybeEnableAnimations(); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + updateMonthAndDayFromBundle(savedInstanceState); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + updateBundleWithMonthAndDay(outState); } @Override protected Bundle makeExtrasBundle(String email, String password) { final Bundle extras = super.makeExtrasBundle(email, password); - final String year = yearEdit.getText().toString(); - extras.putString(EXTRA_YEAR, year); + extras.putString(EXTRA_YEAR, yearEdit.getText().toString()); + updateBundleWithMonthAndDay(extras); return extras; } @@ -111,7 +134,38 @@ if (getIntent() != null) { yearEdit.setText(getIntent().getStringExtra(EXTRA_YEAR)); + updateMonthAndDayFromBundle(getIntent().getExtras() != null ? getIntent().getExtras() : new Bundle()); + } + } + + private void updateBundleWithMonthAndDay(final Bundle bundle) { + if (monthEdit.getTag() != null) { + bundle.putInt(EXTRA_MONTH, (Integer) monthEdit.getTag()); + } + if (dayEdit.getTag() != null) { + bundle.putInt(EXTRA_DAY, (Integer) dayEdit.getTag()); + } + } + + private void updateMonthAndDayFromBundle(final Bundle extras) { + final Integer zeroBasedMonthIndex = (Integer) extras.get(EXTRA_MONTH); + final Integer oneBasedDayIndex = (Integer) extras.get(EXTRA_DAY); + maybeEnableMonthAndDayButtons(); + + if (zeroBasedMonthIndex != null) { + monthEdit.setText(monthItems[zeroBasedMonthIndex]); + monthEdit.setTag(Integer.valueOf(zeroBasedMonthIndex)); + createDayEdit(zeroBasedMonthIndex); + + if (oneBasedDayIndex != null && dayItems != null) { + dayEdit.setText(dayItems[oneBasedDayIndex - 1]); + dayEdit.setTag(Integer.valueOf(oneBasedDayIndex)); + } + } else { + monthEdit.setText(""); + dayEdit.setText(""); } + updateButtonState(); } @Override @@ -186,6 +240,7 @@ @Override public void onClick(DialogInterface dialog, int which) { yearEdit.setText(yearItems[which]); + maybeEnableMonthAndDayButtons(); updateButtonState(); } }; @@ -200,6 +255,114 @@ }); } + private void initializeMonthAndDayValues() { + // Hide Month and day pickers + monthDaycombo.setVisibility(View.GONE); + dayEdit.setEnabled(false); + + // Populate month names. + final Calendar calendar = Calendar.getInstance(); + final Map monthNamesMap = calendar.getDisplayNames(Calendar.MONTH, Calendar.LONG, Locale.getDefault()); + monthItems = new String[monthNamesMap.size()]; + for (Map.Entry entry : monthNamesMap.entrySet()) { + monthItems[entry.getValue()] = entry.getKey(); + } + createMonthEdit(); + } + + protected void createMonthEdit() { + monthEdit.setText(""); + monthEdit.setTag(null); + monthEdit.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + android.content.DialogInterface.OnClickListener listener = new Dialog.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + monthEdit.setText(monthItems[which]); + monthEdit.setTag(Integer.valueOf(which)); + createDayEdit(which); + updateButtonState(); + } + }; + final AlertDialog dialog = new AlertDialog.Builder(FxAccountCreateAccountActivity.this) + .setTitle(R.string.fxaccount_create_account_month_of_birth) + .setItems(monthItems, listener) + .setIcon(R.drawable.icon) + .create(); + dialog.show(); + } + }); + } + + protected void createDayEdit(final int monthIndex) { + dayEdit.setText(""); + dayEdit.setTag(null); + dayEdit.setEnabled(true); + + String yearText = yearEdit.getText().toString(); + Integer birthYear; + try { + birthYear = Integer.parseInt(yearText); + } catch (NumberFormatException e) { + // Ideal this should never happen. + Logger.debug(LOG_TAG, "Exception while parsing year value" + e); + return; + } + + Calendar c = Calendar.getInstance(); + c.set(birthYear, monthIndex, 1); + LinkedList days = new LinkedList(); + for (int i = c.getActualMinimum(Calendar.DAY_OF_MONTH); i <= c.getActualMaximum(Calendar.DAY_OF_MONTH); i++) { + days.add(Integer.toString(i)); + } + dayItems = days.toArray(new String[days.size()]); + + dayEdit.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + android.content.DialogInterface.OnClickListener listener = new Dialog.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dayEdit.setText(dayItems[which]); + dayEdit.setTag(Integer.valueOf(which + 1)); // Days are 1-based. + updateButtonState(); + } + }; + final AlertDialog dialog = new AlertDialog.Builder(FxAccountCreateAccountActivity.this) + .setTitle(R.string.fxaccount_create_account_day_of_birth) + .setItems(dayItems, listener) + .setIcon(R.drawable.icon) + .create(); + dialog.show(); + } + }); + } + + private void maybeEnableMonthAndDayButtons() { + Integer yearOfBirth = null; + try { + yearOfBirth = Integer.valueOf(yearEdit.getText().toString(), 10); + } catch (NumberFormatException e) { + Logger.debug(LOG_TAG, "Year text is not a number; assuming year is a range and that user is old enough."); + } + + // Check if the selected year is the magic year. + if (yearOfBirth == null || !FxAccountAgeLockoutHelper.isMagicYear(yearOfBirth)) { + // Year/Dec/31 is the latest birthday in the selected year, corresponding + // to the youngest person. + monthEdit.setTag(Integer.valueOf(11)); + dayEdit.setTag(Integer.valueOf(31)); + return; + } + + // Show month and date field. + yearEdit.setVisibility(View.GONE); + monthDaycombo.setVisibility(View.VISIBLE); + monthEdit.setTag(null); + dayEdit.setTag(null); + } + public void createAccount(String email, String password, Map engines) { String serverURI = getAuthServerEndpoint(); PasswordStretcher passwordStretcher = makePasswordStretcher(password); @@ -230,7 +393,9 @@ @Override protected boolean shouldButtonBeEnabled() { return super.shouldButtonBeEnabled() && - (yearEdit.length() > 0); + (yearEdit.length() > 0) && + (monthEdit.getTag() != null) && + (dayEdit.getTag() != null); } protected void createCreateAccountButton() { @@ -242,11 +407,13 @@ } final String email = emailEdit.getText().toString(); final String password = passwordEdit.getText().toString(); + final int dayOfBirth = (Integer) dayEdit.getTag(); + final int zeroBasedMonthOfBirth = (Integer) monthEdit.getTag(); // Only include selected engines if the user currently has the option checked. final Map engines = chooseCheckBox.isChecked() ? selectedEngines : null; - if (FxAccountAgeLockoutHelper.passesAgeCheck(yearEdit.getText().toString(), yearItems)) { + if (FxAccountAgeLockoutHelper.passesAgeCheck(dayOfBirth, zeroBasedMonthOfBirth, yearEdit.getText().toString(), yearItems)) { FxAccountUtils.pii(LOG_TAG, "Passed age check."); createAccount(email, password, engines); } else { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/fxa/activities/FxAccountGetStartedActivity.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/fxa/activities/FxAccountGetStartedActivity.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/fxa/activities/FxAccountGetStartedActivity.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/fxa/activities/FxAccountGetStartedActivity.java 2015-02-03 14:33:40.000000000 +0000 @@ -6,6 +6,7 @@ import java.util.Locale; +import org.mozilla.gecko.Locales; import org.mozilla.gecko.R; import org.mozilla.gecko.background.common.log.Logger; import org.mozilla.gecko.background.fxa.FxAccountAgeLockoutHelper; @@ -13,7 +14,6 @@ import org.mozilla.gecko.fxa.FirefoxAccounts; import org.mozilla.gecko.fxa.FxAccountConstants; import org.mozilla.gecko.sync.setup.activities.ActivityUtils; -import org.mozilla.gecko.Locales; import android.accounts.AccountAuthenticatorActivity; import android.content.Intent; @@ -21,6 +21,10 @@ import android.os.SystemClock; import android.view.View; import android.view.View.OnClickListener; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.AnimationSet; +import android.view.animation.TranslateAnimation; import android.widget.TextView; /** @@ -58,6 +62,33 @@ startFlow(extras); } }); + + animateIconIn(); + } + + /** + * Float the icon up, starting from below and moving up to its final layout + * position. Also, fade the icon in. + *

    + * We animate relative to the size of the icon rather than from the bottom of + * the containing view for two reasons: first, the distance from bottom could + * be large on tablets; two, animating with absolute values requires that + * measurement has happened first, which requires a (sometimes buggy) + * onPreDrawListener. + */ + protected void animateIconIn() { + final AlphaAnimation a = new AlphaAnimation(0.0f, 1.0f); + final TranslateAnimation t = new TranslateAnimation( + Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f, + Animation.RELATIVE_TO_SELF, 2.0f, Animation.RELATIVE_TO_SELF, 0.0f); + + final AnimationSet animationSet = new AnimationSet(true); + animationSet.setDuration(150 * 7); // Straight outta... fxa-content-server. + animationSet.addAnimation(a); + animationSet.addAnimation(t); + + final View iconView = findViewById(R.id.icon); + iconView.startAnimation(animationSet); } protected void startFlow(Bundle extras) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/fxa/activities/FxAccountSignInActivity.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/fxa/activities/FxAccountSignInActivity.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/fxa/activities/FxAccountSignInActivity.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/fxa/activities/FxAccountSignInActivity.java 2015-02-03 14:33:40.000000000 +0000 @@ -70,6 +70,7 @@ }); updateFromIntentExtras(); + maybeEnableAnimations(); TextView view = (TextView) findViewById(R.id.forgot_password_link); ActivityUtils.linkTextView(view, R.string.fxaccount_sign_in_forgot_password, R.string.fxaccount_link_forgot_password); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/fxa/FxAccountConstants.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/fxa/FxAccountConstants.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/fxa/FxAccountConstants.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/fxa/FxAccountConstants.java 2015-02-03 14:33:40.000000000 +0000 @@ -16,8 +16,8 @@ public static final String STAGE_AUTH_SERVER_ENDPOINT = "https://api-accounts.stage.mozaws.net/v1"; public static final String STAGE_TOKEN_SERVER_ENDPOINT = "https://token.stage.mozaws.net/1.0/sync/1.5"; - // You must be at least 14 years old to create a Firefox Account. - public static final int MINIMUM_AGE_TO_CREATE_AN_ACCOUNT = 14; + // You must be at least 13 years old, on the day of creation, to create a Firefox Account. + public static final int MINIMUM_AGE_TO_CREATE_AN_ACCOUNT = 13; // You must wait 15 minutes after failing an age check before trying to create a different account. public static final long MINIMUM_TIME_TO_WAIT_AFTER_AGE_CHECK_FAILED_IN_MILLISECONDS = 15 * 60 * 1000; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/GeckoApp.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/GeckoApp.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/GeckoApp.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/GeckoApp.java 2015-02-03 14:33:39.000000000 +0000 @@ -130,6 +130,8 @@ private static final String LOGTAG = "GeckoApp"; private static final int ONE_DAY_MS = 1000*60*60*24; + private static final boolean ZOOMED_VIEW_ENABLED = AppConstants.NIGHTLY_BUILD; + private static enum StartupAction { NORMAL, /* normal application start */ URL, /* launched with a passed URL */ @@ -159,8 +161,9 @@ // after a version upgrade. private static final int CLEANUP_DEFERRAL_SECONDS = 15; - protected RelativeLayout mRootLayout; + protected OuterLayout mRootLayout; protected RelativeLayout mMainLayout; + protected RelativeLayout mGeckoLayout; private View mCameraView; private OrientationEventListener mCameraOrientationEventListener; @@ -173,6 +176,7 @@ private ContactService mContactService; private PromptService mPromptService; private TextSelection mTextSelection; + private ZoomedView mZoomedView; protected DoorHangerPopup mDoorHangerPopup; protected FormAssistPopup mFormAssistPopup; @@ -1264,7 +1268,7 @@ setContentView(getLayout()); // Set up Gecko layout. - mRootLayout = (RelativeLayout) findViewById(R.id.root_layout); + mRootLayout = (OuterLayout) findViewById(R.id.root_layout); mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout); mMainLayout = (RelativeLayout) findViewById(R.id.main_layout); @@ -1578,6 +1582,11 @@ (TextSelectionHandle) findViewById(R.id.caret_handle), (TextSelectionHandle) findViewById(R.id.focus_handle)); + if (ZOOMED_VIEW_ENABLED) { + ViewStub stub = (ViewStub) findViewById(R.id.zoomed_view_stub); + mZoomedView = (ZoomedView) stub.inflate(); + } + PrefsHelper.getPref("app.update.autodownload", new PrefsHelper.PrefHandlerBase() { @Override public void prefValue(String pref, String value) { UpdateServiceHelper.registerForUpdates(GeckoApp.this, value); @@ -2048,6 +2057,9 @@ mPromptService.destroy(); if (mTextSelection != null) mTextSelection.destroy(); + if (mZoomedView != null) { + mZoomedView.destroy(); + } NotificationHelper.destroy(); IntentHelper.destroy(); GeckoNetworkManager.destroy(); @@ -2382,11 +2394,24 @@ public static class MainLayout extends RelativeLayout { private TouchEventInterceptor mTouchEventInterceptor; private MotionEventInterceptor mMotionEventInterceptor; + private LayoutInterceptor mLayoutInterceptor; public MainLayout(Context context, AttributeSet attrs) { super(context, attrs); } + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + if (mLayoutInterceptor != null) { + mLayoutInterceptor.onLayout(); + } + } + + public void setLayoutInterceptor(LayoutInterceptor interceptor) { + mLayoutInterceptor = interceptor; + } + public void setTouchEventInterceptor(TouchEventInterceptor interceptor) { mTouchEventInterceptor = interceptor; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/GeckoEvent.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/GeckoEvent.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/GeckoEvent.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/GeckoEvent.java 2015-02-03 14:33:39.000000000 +0000 @@ -105,7 +105,8 @@ TELEMETRY_UI_EVENT(44), GAMEPAD_ADDREMOVE(45), GAMEPAD_DATA(46), - LONG_PRESS(47); + LONG_PRESS(47), + ZOOMEDVIEW(48); public final int value; @@ -114,26 +115,6 @@ } } - /** - * The DomKeyLocation enum encapsulates the DOM KeyboardEvent's constants. - * @see https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent#Key_location_constants - */ - @JNITarget - public enum DomKeyLocation { - DOM_KEY_LOCATION_STANDARD(0), - DOM_KEY_LOCATION_LEFT(1), - DOM_KEY_LOCATION_RIGHT(2), - DOM_KEY_LOCATION_NUMPAD(3), - DOM_KEY_LOCATION_MOBILE(4), - DOM_KEY_LOCATION_JOYSTICK(5); - - public final int value; - - private DomKeyLocation(int value) { - this.value = value; - } - } - // Encapsulation of common IME actions. @JNITarget public enum ImeAction { @@ -222,7 +203,6 @@ private int mRangeLineColor; private Location mLocation; private Address mAddress; - private DomKeyLocation mDomKeyLocation; private int mConnectionType; private boolean mIsWifi; @@ -312,30 +292,6 @@ mDOMPrintableKeyValue = k.getUnicodeChar(unmodifiedMetaState); } } - mDomKeyLocation = isJoystickButton(mKeyCode) ? DomKeyLocation.DOM_KEY_LOCATION_JOYSTICK - : DomKeyLocation.DOM_KEY_LOCATION_MOBILE; - } - - /** - * This method tests if a key is one of the described in: - * https://bugzilla.mozilla.org/show_bug.cgi?id=756504#c0 - * @param keyCode int with the key code (Android key constant from KeyEvent) - * @return true if the key is one of the listed above, false otherwise. - */ - private static boolean isJoystickButton(int keyCode) { - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_CENTER: - case KeyEvent.KEYCODE_DPAD_LEFT: - case KeyEvent.KEYCODE_DPAD_RIGHT: - case KeyEvent.KEYCODE_DPAD_DOWN: - case KeyEvent.KEYCODE_DPAD_UP: - return true; - default: - if (Versions.feature12Plus) { - return KeyEvent.isGamepadButton(keyCode); - } - return GeckoEvent.isGamepadButton(keyCode); - } } /** @@ -747,6 +703,17 @@ event.mMetaState = tabId; event.mBuffer = buffer; return event; + } + + public static GeckoEvent createZoomedViewEvent(int tabId, int x, int y, int bufw, int bufh, float scaleFactor, ByteBuffer buffer) { + GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.ZOOMEDVIEW); + event.mPoints = new Point[2]; + event.mPoints[0] = new Point(x, y); + event.mPoints[1] = new Point(bufw, bufh); + event.mX = (double) scaleFactor; + event.mMetaState = tabId; + event.mBuffer = buffer; + return event; } public static GeckoEvent createScreenOrientationEvent(short aScreenOrientation) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/GeckoProfile.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/GeckoProfile.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/GeckoProfile.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/GeckoProfile.java 2015-02-03 14:33:39.000000000 +0000 @@ -24,6 +24,7 @@ import org.mozilla.gecko.db.StubBrowserDB; import org.mozilla.gecko.distribution.Distribution; import org.mozilla.gecko.mozglue.RobocopTarget; +import org.mozilla.gecko.firstrun.FirstrunPane; import org.mozilla.gecko.util.INIParser; import org.mozilla.gecko.util.INISection; @@ -809,7 +810,7 @@ // Initialize pref flag for displaying the start pane for a new non-webapp profile. if (!mIsWebAppProfile) { final SharedPreferences prefs = GeckoSharedPrefs.forProfile(mApplicationContext); - prefs.edit().putBoolean(BrowserApp.PREF_STARTPANE_ENABLED, true).apply(); + prefs.edit().putBoolean(FirstrunPane.PREF_FIRSTRUN_ENABLED, true).apply(); } return profileDir; @@ -831,9 +832,14 @@ // Add everything when we're done loading the distribution. final Distribution distribution = Distribution.getInstance(context); - distribution.addOnDistributionReadyCallback(new Runnable() { + distribution.addOnDistributionReadyCallback(new Distribution.ReadyCallback() { @Override - public void run() { + public void distributionNotFound() { + this.distributionFound(null); + } + + @Override + public void distributionFound(Distribution distribution) { Log.d(LOGTAG, "Running post-distribution task: bookmarks."); final ContentResolver cr = context.getContentResolver(); @@ -853,10 +859,29 @@ // bookmarks as there are favicons, we can also guarantee that // the favicon IDs won't overlap. final LocalBrowserDB db = new LocalBrowserDB(getName()); - final int offset = db.addDistributionBookmarks(cr, distribution, 0); + final int offset = distribution == null ? 0 : db.addDistributionBookmarks(cr, distribution, 0); db.addDefaultBookmarks(context, cr, offset); } } + + @Override + public void distributionArrivedLate(Distribution distribution) { + Log.d(LOGTAG, "Running late distribution task: bookmarks."); + // Recover as best we can. + synchronized (GeckoProfile.this) { + // Skip initialization if the profile directory has been removed. + if (!profileDir.exists()) { + return; + } + + final LocalBrowserDB db = new LocalBrowserDB(getName()); + // We assume we've been called very soon after startup, and so our offset + // into "Mobile Bookmarks" is the number of bookmarks in the DB. + final ContentResolver cr = context.getContentResolver(); + final int offset = db.getCount(cr, "bookmarks"); + db.addDistributionBookmarks(cr, distribution, offset); + } + } }); } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/BufferedCairoImage.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/BufferedCairoImage.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/BufferedCairoImage.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/BufferedCairoImage.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,69 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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/. */ - -package org.mozilla.gecko.gfx; - -import org.mozilla.gecko.mozglue.DirectBufferAllocator; - -import android.graphics.Bitmap; -import android.util.Log; - -import java.nio.ByteBuffer; - -/** A Cairo image that simply saves a buffer of pixel data. */ -public class BufferedCairoImage extends CairoImage { - private ByteBuffer mBuffer; - private IntSize mSize; - private int mFormat; - - private static final String LOGTAG = "GeckoBufferedCairoImage"; - - /** Creates a buffered Cairo image from a byte buffer. */ - public BufferedCairoImage(ByteBuffer inBuffer, int inWidth, int inHeight, int inFormat) { - setBuffer(inBuffer, inWidth, inHeight, inFormat); - } - - /** Creates a buffered Cairo image from an Android bitmap. */ - public BufferedCairoImage(Bitmap bitmap) { - setBitmap(bitmap); - } - - private synchronized void freeBuffer() { - mBuffer = DirectBufferAllocator.free(mBuffer); - } - - @Override - public void destroy() { - try { - freeBuffer(); - } catch (Exception ex) { - Log.e(LOGTAG, "error clearing buffer: ", ex); - } - } - - @Override - public ByteBuffer getBuffer() { return mBuffer; } - @Override - public IntSize getSize() { return mSize; } - @Override - public int getFormat() { return mFormat; } - - - public void setBuffer(ByteBuffer buffer, int width, int height, int format) { - freeBuffer(); - mBuffer = buffer; - mSize = new IntSize(width, height); - mFormat = format; - } - - public void setBitmap(Bitmap bitmap) { - mFormat = CairoUtils.bitmapConfigToCairoFormat(bitmap.getConfig()); - mSize = new IntSize(bitmap.getWidth(), bitmap.getHeight()); - - int bpp = CairoUtils.bitsPerPixelForCairoFormat(mFormat); - mBuffer = DirectBufferAllocator.allocate(mSize.getArea() * bpp); - bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer()); - } -} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/BufferedImageGLInfo.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/BufferedImageGLInfo.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/BufferedImageGLInfo.java 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/BufferedImageGLInfo.java 2015-02-03 14:33:40.000000000 +0000 @@ -0,0 +1,35 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * 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/. */ + +package org.mozilla.gecko.gfx; + +import javax.microedition.khronos.opengles.GL10; + +/** Information needed to render buffered bitmaps using OpenGL ES. */ +public class BufferedImageGLInfo { + public final int internalFormat; + public final int format; + public final int type; + + public BufferedImageGLInfo(int bufferedImageFormat) { + switch (bufferedImageFormat) { + case BufferedImage.FORMAT_ARGB32: + internalFormat = format = GL10.GL_RGBA; type = GL10.GL_UNSIGNED_BYTE; + break; + case BufferedImage.FORMAT_RGB24: + internalFormat = format = GL10.GL_RGB; type = GL10.GL_UNSIGNED_BYTE; + break; + case BufferedImage.FORMAT_RGB16_565: + internalFormat = format = GL10.GL_RGB; type = GL10.GL_UNSIGNED_SHORT_5_6_5; + break; + case BufferedImage.FORMAT_A8: + case BufferedImage.FORMAT_A1: + throw new RuntimeException("BufferedImage FORMAT_A1 and FORMAT_A8 unsupported"); + default: + throw new RuntimeException("Unknown BufferedImage format"); + } + } +} + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/BufferedImage.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/BufferedImage.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/BufferedImage.java 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/BufferedImage.java 2015-02-03 14:33:40.000000000 +0000 @@ -0,0 +1,85 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * 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/. */ + +package org.mozilla.gecko.gfx; + +import org.mozilla.gecko.mozglue.DirectBufferAllocator; + +import android.graphics.Bitmap; +import android.util.Log; + +import java.nio.ByteBuffer; + +/** A buffered image that simply saves a buffer of pixel data. */ +public class BufferedImage { + private ByteBuffer mBuffer; + private IntSize mSize; + private int mFormat; + + private static final String LOGTAG = "GeckoBufferedImage"; + + /** Creates an empty buffered image */ + public BufferedImage() { + mSize = new IntSize(0, 0); + } + + /** Creates a buffered image from an Android bitmap. */ + public BufferedImage(Bitmap bitmap) { + mFormat = bitmapConfigToFormat(bitmap.getConfig()); + mSize = new IntSize(bitmap.getWidth(), bitmap.getHeight()); + + int bpp = bitsPerPixelForFormat(mFormat); + mBuffer = DirectBufferAllocator.allocate(mSize.getArea() * bpp); + bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer()); + } + + private synchronized void freeBuffer() { + mBuffer = DirectBufferAllocator.free(mBuffer); + } + + public void destroy() { + try { + freeBuffer(); + } catch (Exception ex) { + Log.e(LOGTAG, "error clearing buffer: ", ex); + } + } + + public ByteBuffer getBuffer() { return mBuffer; } + public IntSize getSize() { return mSize; } + public int getFormat() { return mFormat; } + + public static final int FORMAT_INVALID = -1; + public static final int FORMAT_ARGB32 = 0; + public static final int FORMAT_RGB24 = 1; + public static final int FORMAT_A8 = 2; + public static final int FORMAT_A1 = 3; + public static final int FORMAT_RGB16_565 = 4; + + private static int bitsPerPixelForFormat(int format) { + switch (format) { + case FORMAT_A1: return 1; + case FORMAT_A8: return 8; + case FORMAT_RGB16_565: return 16; + case FORMAT_RGB24: return 24; + case FORMAT_ARGB32: return 32; + default: + throw new RuntimeException("Unknown Cairo format"); + } + } + + private static int bitmapConfigToFormat(Bitmap.Config config) { + if (config == null) + return FORMAT_ARGB32; /* Droid Pro fix. */ + + switch (config) { + case ALPHA_8: return FORMAT_A8; + case ARGB_4444: throw new RuntimeException("ARGB_444 unsupported"); + case ARGB_8888: return FORMAT_ARGB32; + case RGB_565: return FORMAT_RGB16_565; + default: throw new RuntimeException("Unknown Skia bitmap config"); + } + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/CairoGLInfo.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/CairoGLInfo.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/CairoGLInfo.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/CairoGLInfo.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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/. */ - -package org.mozilla.gecko.gfx; - -import javax.microedition.khronos.opengles.GL10; - -/** Information needed to render Cairo bitmaps using OpenGL ES. */ -public class CairoGLInfo { - public final int internalFormat; - public final int format; - public final int type; - - public CairoGLInfo(int cairoFormat) { - switch (cairoFormat) { - case CairoImage.FORMAT_ARGB32: - internalFormat = format = GL10.GL_RGBA; type = GL10.GL_UNSIGNED_BYTE; - break; - case CairoImage.FORMAT_RGB24: - internalFormat = format = GL10.GL_RGB; type = GL10.GL_UNSIGNED_BYTE; - break; - case CairoImage.FORMAT_RGB16_565: - internalFormat = format = GL10.GL_RGB; type = GL10.GL_UNSIGNED_SHORT_5_6_5; - break; - case CairoImage.FORMAT_A8: - case CairoImage.FORMAT_A1: - throw new RuntimeException("Cairo FORMAT_A1 and FORMAT_A8 unsupported"); - default: - throw new RuntimeException("Unknown Cairo format"); - } - } -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/CairoImage.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/CairoImage.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/CairoImage.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/CairoImage.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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/. */ - -package org.mozilla.gecko.gfx; - -import java.nio.ByteBuffer; - -/* - * A bitmap with pixel data in one of the formats that Cairo understands. - */ -public abstract class CairoImage { - public abstract ByteBuffer getBuffer(); - - public abstract void destroy(); - - public abstract IntSize getSize(); - public abstract int getFormat(); - - public static final int FORMAT_INVALID = -1; - public static final int FORMAT_ARGB32 = 0; - public static final int FORMAT_RGB24 = 1; - public static final int FORMAT_A8 = 2; - public static final int FORMAT_A1 = 3; - public static final int FORMAT_RGB16_565 = 4; -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/CairoUtils.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/CairoUtils.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/CairoUtils.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/CairoUtils.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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/. */ - -package org.mozilla.gecko.gfx; - -import android.graphics.Bitmap; - -/** - * Utility methods useful when displaying Cairo bitmaps using OpenGL ES. - */ -public class CairoUtils { - private CairoUtils() { /* Don't call me. */ } - - public static int bitsPerPixelForCairoFormat(int cairoFormat) { - switch (cairoFormat) { - case CairoImage.FORMAT_A1: return 1; - case CairoImage.FORMAT_A8: return 8; - case CairoImage.FORMAT_RGB16_565: return 16; - case CairoImage.FORMAT_RGB24: return 24; - case CairoImage.FORMAT_ARGB32: return 32; - default: - throw new RuntimeException("Unknown Cairo format"); - } - } - - public static int bitmapConfigToCairoFormat(Bitmap.Config config) { - if (config == null) - return CairoImage.FORMAT_ARGB32; /* Droid Pro fix. */ - - switch (config) { - case ALPHA_8: return CairoImage.FORMAT_A8; - case ARGB_4444: throw new RuntimeException("ARGB_444 unsupported"); - case ARGB_8888: return CairoImage.FORMAT_ARGB32; - case RGB_565: return CairoImage.FORMAT_RGB16_565; - default: throw new RuntimeException("Unknown Skia bitmap config"); - } - } - - public static Bitmap.Config cairoFormatTobitmapConfig(int format) { - switch (format) { - case CairoImage.FORMAT_A8: return Bitmap.Config.ALPHA_8; - case CairoImage.FORMAT_ARGB32: return Bitmap.Config.ARGB_8888; - case CairoImage.FORMAT_RGB16_565: return Bitmap.Config.RGB_565; - default: - throw new RuntimeException("Unknown CairoImage format"); - } - } -} - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/GeckoLayerClient.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/GeckoLayerClient.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/GeckoLayerClient.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/GeckoLayerClient.java 2015-02-03 14:33:40.000000000 +0000 @@ -86,7 +86,8 @@ * that because mViewportMetrics might get reassigned in between reading the different * fields. */ private volatile ImmutableViewportMetrics mViewportMetrics; - private LayerView.OnMetricsChangedListener mViewportChangeListener; + private LayerView.OnMetricsChangedListener mDynamicToolbarViewportChangeListener; + private LayerView.OnMetricsChangedListener mZoomedViewViewportChangeListener; private ZoomConstraints mZoomConstraints; @@ -853,8 +854,11 @@ * You must hold the monitor while calling this. */ private void viewportMetricsChanged(boolean notifyGecko) { - if (mViewportChangeListener != null) { - mViewportChangeListener.onMetricsChanged(mViewportMetrics); + if (mDynamicToolbarViewportChangeListener != null) { + mDynamicToolbarViewportChangeListener.onMetricsChanged(mViewportMetrics); + } + if (mZoomedViewViewportChangeListener != null) { + mZoomedViewViewportChangeListener.onMetricsChanged(mViewportMetrics); } mView.requestRender(); @@ -910,8 +914,11 @@ /** Implementation of PanZoomTarget */ @Override public void panZoomStopped() { - if (mViewportChangeListener != null) { - mViewportChangeListener.onPanZoomStopped(); + if (mDynamicToolbarViewportChangeListener != null) { + mDynamicToolbarViewportChangeListener.onPanZoomStopped(); + } + if (mZoomedViewViewportChangeListener != null) { + mZoomedViewViewportChangeListener.onPanZoomStopped(); } } @@ -982,8 +989,12 @@ return layerPoint; } - void setOnMetricsChangedListener(LayerView.OnMetricsChangedListener listener) { - mViewportChangeListener = listener; + void setOnMetricsChangedDynamicToolbarViewportListener(LayerView.OnMetricsChangedListener listener) { + mDynamicToolbarViewportChangeListener = listener; + } + + void setOnMetricsChangedZoomedViewportListener(LayerView.OnMetricsChangedListener listener) { + mZoomedViewViewportChangeListener = listener; } public void addDrawListener(DrawListener listener) { diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/Layer.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/Layer.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/Layer.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/Layer.java 2015-02-03 14:33:40.000000000 +0000 @@ -69,7 +69,7 @@ /** * Call this before modifying the layer. Note that, for TileLayers, "modifying the layer" - * includes altering the underlying CairoImage in any way. Thus you must call this function + * includes altering the underlying BufferedImage in any way. Thus you must call this function * before modifying the byte buffer associated with this layer. * * This function may block, so you should never call this on the main UI thread. diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/LayerRenderer.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/LayerRenderer.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/LayerRenderer.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/LayerRenderer.java 2015-02-03 14:33:40.000000000 +0000 @@ -26,13 +26,17 @@ import android.opengl.GLES20; import android.os.SystemClock; import android.util.Log; + import org.mozilla.gecko.mozglue.JNITarget; +import org.mozilla.gecko.util.ThreadUtils; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.ArrayList; +import java.util.List; import javax.microedition.khronos.egl.EGLConfig; @@ -55,6 +59,8 @@ private static final long NANOS_PER_MS = 1000000; private static final int NANOS_PER_SECOND = 1000000000; + private static final int MAX_SCROLL_SPEED_TO_REQUEST_ZOOM_RENDER = 5; + private final LayerView mView; private final ScrollbarLayer mHorizScrollLayer; private final ScrollbarLayer mVertScrollLayer; @@ -90,6 +96,10 @@ private int mSampleHandle; private int mTMatrixHandle; + private List mZoomedViewListeners; + private float mLastViewLeft; + private float mLastViewTop; + // column-major matrix applied to each vertex to shift the viewport from // one ranging from (-1, -1),(1,1) to (0,0),(1,1) and to scale all sizes by // a factor of 2 to fill up the screen @@ -158,6 +168,7 @@ mCoordBuffer = mCoordByteBuffer.asFloatBuffer(); Tabs.registerOnTabsChangedListener(this); + mZoomedViewListeners = new ArrayList(); } private Bitmap expandCanvasToPowerOfTwo(Bitmap image, IntSize size) { @@ -185,6 +196,7 @@ mHorizScrollLayer.destroy(); mVertScrollLayer.destroy(); Tabs.unregisterOnTabsChangedListener(this); + mZoomedViewListeners.clear(); } void onSurfaceCreated(EGLConfig config) { @@ -586,6 +598,42 @@ } + private void maybeRequestZoomedViewRender(RenderContext context) { + // Concurrently update of mZoomedViewListeners should not be an issue here + // because the following line is just a short-circuit + if (mZoomedViewListeners.size() == 0) { + return; + } + + // When scrolling fast, do not request zoomed view render to avoid to slow down + // the scroll in the main view. + // Speed is estimated using the offset changes between 2 display frame calls + final float viewLeft = context.viewport.left - context.offset.x; + final float viewTop = context.viewport.top - context.offset.y; + boolean shouldWaitToRender = false; + + if (Math.abs(mLastViewLeft - viewLeft) > MAX_SCROLL_SPEED_TO_REQUEST_ZOOM_RENDER || + Math.abs(mLastViewTop - viewTop) > MAX_SCROLL_SPEED_TO_REQUEST_ZOOM_RENDER) { + shouldWaitToRender = true; + } + + mLastViewLeft = viewLeft; + mLastViewTop = viewTop; + + if (shouldWaitToRender) { + return; + } + + ThreadUtils.postToUiThread(new Runnable() { + @Override + public void run() { + for (LayerView.ZoomedViewListener listener : mZoomedViewListeners) { + listener.requestZoomedViewRender(); + } + } + }); + } + /** This function is invoked via JNI; be careful when modifying signature. */ @JNITarget public void endDrawing() { @@ -595,6 +643,8 @@ PanningPerfAPI.recordFrameTime(); + maybeRequestZoomedViewRender(mPageContext); + /* Used by robocop for testing purposes */ IntBuffer pixelBuffer = mPixelBuffer; if (mUpdated && pixelBuffer != null) { @@ -642,4 +692,26 @@ } } } + + public void updateZoomedView(final ByteBuffer data) { + ThreadUtils.postToUiThread(new Runnable() { + @Override + public void run() { + for (LayerView.ZoomedViewListener listener : mZoomedViewListeners) { + data.position(0); + listener.updateView(data); + } + } + }); + } + + public void addZoomedViewListener(LayerView.ZoomedViewListener listener) { + ThreadUtils.assertOnUiThread(); + mZoomedViewListeners.add(listener); + } + + public void removeZoomedViewListener(LayerView.ZoomedViewListener listener) { + ThreadUtils.assertOnUiThread(); + mZoomedViewListeners.remove(listener); + } } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/LayerView.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/LayerView.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/LayerView.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/LayerView.java 2015-02-03 14:33:40.000000000 +0000 @@ -5,7 +5,9 @@ package org.mozilla.gecko.gfx; +import java.nio.ByteBuffer; import java.nio.IntBuffer; +import java.util.ArrayList; import org.mozilla.gecko.AndroidGamepadManager; import org.mozilla.gecko.AppConstants.Versions; @@ -530,6 +532,18 @@ } } + //This method is called on the Gecko main thread. + @WrapElementForJNI(allowMultithread = true, stubName = "updateZoomedView") + public static void updateZoomedView(ByteBuffer data) { + LayerView layerView = GeckoAppShell.getLayerView(); + if (layerView != null) { + LayerRenderer layerRenderer = layerView.getRenderer(); + if (layerRenderer != null) { + layerRenderer.updateZoomedView(data); + } + } + } + public interface Listener { void renderRequested(); void sizeChanged(int width, int height); @@ -662,7 +676,27 @@ public void onPanZoomStopped(); } - public void setOnMetricsChangedListener(OnMetricsChangedListener listener) { - mLayerClient.setOnMetricsChangedListener(listener); + public void setOnMetricsChangedDynamicToolbarViewportListener(OnMetricsChangedListener listener) { + mLayerClient.setOnMetricsChangedDynamicToolbarViewportListener(listener); + } + + public void setOnMetricsChangedZoomedViewportListener(OnMetricsChangedListener listener) { + mLayerClient.setOnMetricsChangedZoomedViewportListener(listener); + } + + // Public hooks for zoomed view + + public interface ZoomedViewListener { + public void requestZoomedViewRender(); + public void updateView(ByteBuffer data); + } + + public void addZoomedViewListener(ZoomedViewListener listener) { + mRenderer.addZoomedViewListener(listener); } + + public void removeZoomedViewListener(ZoomedViewListener listener) { + mRenderer.removeZoomedViewListener(listener); + } + } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/PluginLayer.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/PluginLayer.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/PluginLayer.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/PluginLayer.java 2015-02-03 14:33:40.000000000 +0000 @@ -37,7 +37,7 @@ }; public PluginLayer(View view, RectF rect, int maxDimension) { - super(new BufferedCairoImage(null, 0, 0, 0), TileLayer.PaintMode.NORMAL); + super(new BufferedImage(), TileLayer.PaintMode.NORMAL); mView = view; mContainer = GeckoAppShell.getGeckoInterface().getPluginContainer(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/ScrollbarLayer.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/ScrollbarLayer.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/ScrollbarLayer.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/ScrollbarLayer.java 2015-02-03 14:33:40.000000000 +0000 @@ -59,7 +59,7 @@ private final Rect mEndCapTexCoords; // bottom/right endcap coordinates ScrollbarLayer(LayerRenderer renderer, Bitmap scrollbarImage, IntSize imageSize, boolean vertical) { - super(new BufferedCairoImage(scrollbarImage), TileLayer.PaintMode.NORMAL); + super(new BufferedImage(scrollbarImage), TileLayer.PaintMode.NORMAL); mRenderer = renderer; mVertical = vertical; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/TileLayer.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/TileLayer.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/gfx/TileLayer.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/gfx/TileLayer.java 2015-02-03 14:33:40.000000000 +0000 @@ -22,12 +22,12 @@ private IntSize mSize; private int[] mTextureIDs; - protected final CairoImage mImage; + protected final BufferedImage mImage; public enum PaintMode { NORMAL, REPEAT, STRETCH }; private PaintMode mPaintMode; - public TileLayer(CairoImage image, PaintMode paintMode) { + public TileLayer(BufferedImage image, PaintMode paintMode) { super(image.getSize()); mPaintMode = paintMode; @@ -143,8 +143,8 @@ GLES20.glGenTextures(mTextureIDs.length, mTextureIDs, 0); } - int cairoFormat = mImage.getFormat(); - CairoGLInfo glInfo = new CairoGLInfo(cairoFormat); + int imageFormat = mImage.getFormat(); + BufferedImageGLInfo glInfo = new BufferedImageGLInfo(imageFormat); bindAndSetGLParameters(); diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/health/BrowserHealthRecorder.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/health/BrowserHealthRecorder.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/health/BrowserHealthRecorder.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/health/BrowserHealthRecorder.java 2015-02-03 14:33:40.000000000 +0000 @@ -531,17 +531,50 @@ // Because the distribution lookup can take some time, do it at the end of // our background startup work, along with the Gecko snapshot fetch. final Distribution distribution = Distribution.getInstance(context); - distribution.addOnDistributionReadyCallback(new Runnable() { + distribution.addOnDistributionReadyCallback(new Distribution.ReadyCallback() { + private void requestGeckoFields() { + Log.d(LOG_TAG, "Requesting all add-ons and FHR prefs from Gecko."); + dispatcher.registerGeckoThreadListener(BrowserHealthRecorder.this, EVENT_SNAPSHOT); + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HealthReport:RequestSnapshot", null)); + } + @Override - public void run() { + public void distributionNotFound() { + requestGeckoFields(); + } + + @Override + public void distributionFound(Distribution distribution) { Log.d(LOG_TAG, "Running post-distribution task: health recorder."); final DistributionDescriptor desc = distribution.getDescriptor(); if (desc != null && desc.valid) { profileCache.setDistributionString(desc.id, desc.version); } - Log.d(LOG_TAG, "Requesting all add-ons and FHR prefs from Gecko."); - dispatcher.registerGeckoThreadListener(BrowserHealthRecorder.this, EVENT_SNAPSHOT); - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HealthReport:RequestSnapshot", null)); + requestGeckoFields(); + } + + @Override + public void distributionArrivedLate(Distribution distribution) { + profileCache.beginInitialization(); + + final DistributionDescriptor desc = distribution.getDescriptor(); + if (desc != null && desc.valid) { + profileCache.setDistributionString(desc.id, desc.version); + } + + // Now rebuild. + try { + profileCache.completeInitialization(); + + if (state == State.INITIALIZING) { + initializeStorage(); + } else { + onEnvironmentChanged(); + } + } catch (Exception e) { + // Well, we tried. + Log.e(LOG_TAG, "Couldn't complete profile cache init.", e); + } } }); } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/home/HomePagerTabStrip.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/home/HomePagerTabStrip.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/home/HomePagerTabStrip.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/home/HomePagerTabStrip.java 2015-02-03 14:33:40.000000000 +0000 @@ -132,7 +132,10 @@ private class PreDrawListener implements ViewTreeObserver.OnPreDrawListener { @Override public boolean onPreDraw() { - animateTitles(); + if (!TransitionsTracker.areTransitionsRunning()) { + // Don't show the title bounce animation if other animations are running. + animateTitles(); + } getViewTreeObserver().removeOnPreDrawListener(this); return true; } diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/home/TopSitesPanel.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/home/TopSitesPanel.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/home/TopSitesPanel.java 2015-01-25 22:24:32.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/home/TopSitesPanel.java 2015-02-03 14:33:40.000000000 +0000 @@ -304,11 +304,12 @@ public void onDestroyView() { super.onDestroyView(); - // Discard any additional item clicks on the list - // as the panel is getting destroyed (see bug 930160). + // Discard any additional item clicks on the list as the + // panel is getting destroyed (see bugs 930160 & 1096958). mList.setOnItemClickListener(null); - mList = null; + mGrid.setOnItemClickListener(null); + mList = null; mGrid = null; mListAdapter = null; mGridAdapter = null; diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/LayoutInterceptor.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/LayoutInterceptor.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/LayoutInterceptor.java 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/LayoutInterceptor.java 2015-02-03 14:33:40.000000000 +0000 @@ -0,0 +1,11 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * 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/. */ + + +package org.mozilla.gecko; + +public interface LayoutInterceptor { + public void onLayout(); +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/locales/en-US/sync_strings.dtd thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/locales/en-US/sync_strings.dtd --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/locales/en-US/sync_strings.dtd 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/locales/en-US/sync_strings.dtd 2015-02-03 14:33:40.000000000 +0000 @@ -150,6 +150,8 @@ etc). The account remains a "Firefox Account". --> + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/moz.build thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/moz.build --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/moz.build 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/moz.build 2015-02-03 14:33:40.000000000 +0000 @@ -202,6 +202,11 @@ 'FilePicker.java', 'FilePickerResultHandler.java', 'FindInPageBar.java', + 'firstrun/FirstrunPager.java', + 'firstrun/FirstrunPagerConfig.java', + 'firstrun/FirstrunPane.java', + 'firstrun/FirstrunPanel.java', + 'firstrun/WelcomePanel.java', 'FormAssistPopup.java', 'GeckoAccessibility.java', 'GeckoActivity.java', @@ -233,10 +238,8 @@ 'GeckoViewContent.java', 'gfx/Axis.java', 'gfx/BitmapUtils.java', - 'gfx/BufferedCairoImage.java', - 'gfx/CairoGLInfo.java', - 'gfx/CairoImage.java', - 'gfx/CairoUtils.java', + 'gfx/BufferedImage.java', + 'gfx/BufferedImageGLInfo.java', 'gfx/DisplayPortCalculator.java', 'gfx/DisplayPortMetrics.java', 'gfx/DrawTimingQueue.java', @@ -335,6 +338,7 @@ 'InputMethods.java', 'IntentHelper.java', 'JavaAddonManager.java', + 'LayoutInterceptor.java', 'LocaleManager.java', 'Locales.java', 'lwt/LightweightTheme.java', @@ -358,6 +362,7 @@ 'NotificationService.java', 'NSSBridge.java', 'OrderedBroadcastHelper.java', + 'OuterLayout.java', 'preferences/AlignRightLinkPreference.java', 'preferences/AndroidImport.java', 'preferences/AndroidImportPreference.java', @@ -408,7 +413,6 @@ 'sqlite/MatrixBlobCursor.java', 'sqlite/SQLiteBridge.java', 'sqlite/SQLiteBridgeException.java', - 'StartPane.java', 'SuggestClient.java', 'SurfaceBits.java', 'Tab.java', @@ -500,6 +504,7 @@ 'widget/ThumbnailView.java', 'widget/TwoWayView.java', 'ZoomConstraints.java', + 'ZoomedView.java', ] # The following sources are checked in to version control but # generated by a script (widget/generate_themed_views.py). If you're diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/OuterLayout.java thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/OuterLayout.java --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/OuterLayout.java 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/OuterLayout.java 2015-02-03 14:33:40.000000000 +0000 @@ -0,0 +1,254 @@ +/* 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/. */ +package org.mozilla.gecko; + +import android.content.Context; +import android.support.v4.view.ViewCompat; +import android.support.v4.widget.ViewDragHelper; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.widget.RelativeLayout; + +/* Outerlayout is the container layout of all the main views. It allows mainlayout to be dragged while targeting + the toolbar and it's responsible for handling the dragprocess. It relies on ViewDragHelper to ease the drag process. + */ +public class OuterLayout extends RelativeLayout { + private final double AUTO_OPEN_SPEED_LIMIT = 800.0; + private ViewDragHelper mDragHelper; + private int mDraggingBorder; + private int mDragRange; + private boolean mIsOpen = false; + private int mDraggingState = ViewDragHelper.STATE_IDLE; + private DragCallback mDragCallback; + + public static interface DragCallback { + public void startDrag(boolean wasOpen); + public void stopDrag(boolean stoppingToOpen); + public int getDragRange(); + public int getOrderedChildIndex(int index); + public boolean canDrag(MotionEvent event); + public boolean canInterceptEventWhileOpen(MotionEvent event); + public void onDragProgress(float progress); + public View getViewToDrag(); + public int getLowerLimit(); + } + + private class DragHelperCallback extends ViewDragHelper.Callback { + @Override + public void onViewDragStateChanged(int newState) { + if (newState == mDraggingState) { // no change + return; + } + + // if the view stopped moving. + if ((mDraggingState == ViewDragHelper.STATE_DRAGGING || mDraggingState == ViewDragHelper.STATE_SETTLING) && + newState == ViewDragHelper.STATE_IDLE) { + + final float rangeToCheck = mDragRange; + final float lowerLimit = mDragCallback.getLowerLimit(); + if (mDraggingBorder == lowerLimit) { + mIsOpen = false; + mDragCallback.onDragProgress(0); + } else if (mDraggingBorder == rangeToCheck) { + mIsOpen = true; + mDragCallback.onDragProgress(1); + } + mDragCallback.stopDrag(mIsOpen); + } + + // The view was previuosly moving. + if (newState == ViewDragHelper.STATE_DRAGGING && !isMoving()) { + mDragCallback.startDrag(mIsOpen); + updateRanges(); + } + + mDraggingState = newState; + } + + @Override + public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { + mDraggingBorder = top; + final float progress = Math.min(1, ((float) top) / mDragRange); + mDragCallback.onDragProgress(progress); + } + + @Override + public int getViewVerticalDragRange(View child) { + return mDragRange; + } + + @Override + public int getOrderedChildIndex(int index) { + return mDragCallback.getOrderedChildIndex(index); + } + + @Override + public boolean tryCaptureView(View view, int i) { + return (view.getId() == mDragCallback.getViewToDrag().getId()); + } + + @Override + public int clampViewPositionVertical(View child, int top, int dy) { + return top; + } + + @Override + public void onViewReleased(View releasedChild, float xvel, float yvel) { + final float rangeToCheck = mDragRange; + final float speedToCheck = yvel; + + if (mDraggingBorder == mDragCallback.getLowerLimit()) { + return; + } + + if (mDraggingBorder == rangeToCheck) { + return; + } + + boolean settleToOpen = false; + // Speed has priority over position. + if (speedToCheck > AUTO_OPEN_SPEED_LIMIT) { + settleToOpen = true; + } else if (speedToCheck < -AUTO_OPEN_SPEED_LIMIT) { + settleToOpen = false; + } else if (mDraggingBorder > rangeToCheck / 2) { + settleToOpen = true; + } else if (mDraggingBorder < rangeToCheck / 2) { + settleToOpen = false; + } + + final int settleDestX; + final int settleDestY; + if (settleToOpen) { + settleDestX = 0; + settleDestY = mDragRange; + } else { + settleDestX = 0; + settleDestY = mDragCallback.getLowerLimit(); + } + + if(mDragHelper.settleCapturedViewAt(settleDestX, settleDestY)) { + ViewCompat.postInvalidateOnAnimation(OuterLayout.this); + } + } + } + + public OuterLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + private void updateRanges() { + // Need to wait for the tabs to show in order to fetch the right sizes. + mDragRange = mDragCallback.getDragRange() + mDragCallback.getLowerLimit(); + } + + private void updateOrientation() { + mDragHelper.setEdgeTrackingEnabled(0); + } + + @Override + protected void onFinishInflate() { + mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback()); + mIsOpen = false; + super.onFinishInflate(); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + if (mDragCallback.canDrag(event)) { + if (mDragHelper.shouldInterceptTouchEvent(event)) { + return true; + } + } + + // Because while open the target layout is translated and draghelper does not catch it. + if (mIsOpen && mDragCallback.canInterceptEventWhileOpen(event)) { + return true; + } + return false; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + // touch events can be passed to the helper if we target the toolbar or we are already dragging. + if (mDragCallback.canDrag(event) || mDraggingState == ViewDragHelper.STATE_DRAGGING) { + mDragHelper.processTouchEvent(event); + } + return true; + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + // The first time fennec is started, tabs might not have been created while we drag. In that case we need + // an arbitrary range to start dragging that will be updated as soon as the tabs are created. + + if (mDragRange == 0) { + mDragRange = h / 2; + } + super.onSizeChanged(w, h, oldw, oldh); + } + + @Override + public void computeScroll() { // needed for automatic settling. + if (mDragHelper.continueSettling(true)) { + ViewCompat.postInvalidateOnAnimation(this); + } + } + + /** + * To be called when closing the tabs from outside (i.e. when touching the main layout). + */ + public void setClosed() { + mIsOpen = false; + mDragHelper.abort(); + } + + /** + * To be called when opening the tabs from outside (i.e. when clicking on the tabs button). + */ + public void setOpen() { + mIsOpen = true; + mDragHelper.abort(); + } + + public void setDraggableCallback(DragCallback dragCallback) { + mDragCallback = dragCallback; + updateOrientation(); + } + + // If a change happens while we are dragging, we abort the dragging and set to open state. + public void reset() { + updateOrientation(); + if (isMoving()) { + mDragHelper.abort(); + if (mDragCallback != null) { + mDragCallback.stopDrag(false); + mDragCallback.onDragProgress(0f); + } + } + } + + public void updateDragHelperParameters() { + mDragRange = mDragCallback.getDragRange() + mDragCallback.getLowerLimit(); + updateOrientation(); + } + + public boolean isMoving() { + return (mDraggingState == ViewDragHelper.STATE_DRAGGING || + mDraggingState == ViewDragHelper.STATE_SETTLING); + } + + public boolean isOpen() { + return mIsOpen; + } + + public View findTopChildUnder(MotionEvent event) { + return mDragHelper.findTopChildUnder((int) event.getX(), (int) event.getY()); + } + + public void restoreTargetViewPosition() { + mDragCallback.getViewToDrag().offsetTopAndBottom(mDraggingBorder); + } +} diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/resources/drawable/firstrun_button_background.xml thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/resources/drawable/firstrun_button_background.xml --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/resources/drawable/firstrun_button_background.xml 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/resources/drawable/firstrun_button_background.xml 2015-02-03 14:33:42.000000000 +0000 @@ -0,0 +1,11 @@ + + + + + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/resources/drawable/firstrun_button_enabled.xml thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/resources/drawable/firstrun_button_enabled.xml --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/resources/drawable/firstrun_button_enabled.xml 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/resources/drawable/firstrun_button_enabled.xml 2015-02-03 14:33:42.000000000 +0000 @@ -0,0 +1,11 @@ + + + + + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/resources/drawable/firstrun_button_pressed.xml thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/resources/drawable/firstrun_button_pressed.xml --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/resources/drawable/firstrun_button_pressed.xml 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/resources/drawable/firstrun_button_pressed.xml 2015-02-03 14:33:42.000000000 +0000 @@ -0,0 +1,11 @@ + + + + + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/resources/drawable/onboard_start_button.xml thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/resources/drawable/onboard_start_button.xml --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/resources/drawable/onboard_start_button.xml 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/resources/drawable/onboard_start_button.xml 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/resources/layout/browser_toolbar.xml thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/resources/layout/browser_toolbar.xml --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/resources/layout/browser_toolbar.xml 2015-01-25 22:24:33.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/resources/layout/browser_toolbar.xml 2015-02-03 14:33:42.000000000 +0000 @@ -6,12 +6,7 @@ + this View should be matched in the url_bar_translating_edge. --> + + + + + + + + + + diff -Nru thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/resources/layout/firstrun_welcome_fragment.xml thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/resources/layout/firstrun_welcome_fragment.xml --- thunderbird-trunk-38.0~a1~hg20150125r17395.225611/mozilla/mobile/android/base/resources/layout/firstrun_welcome_fragment.xml 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-trunk-38.0~a1~hg20150202r17443.227207/mozilla/mobile/android/base/resources/layout/firstrun_welcome_fragment.xml 2015-02-03 14:33:42.000000000 +0000 @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + +